seta hud_panel_modicons_bg_alpha "" "if set to something else than \"\" = override default panel background alpha"
seta hud_panel_modicons_bg_border "" "if set to something else than \"\" = override default size of border around the background"
seta hud_panel_modicons_bg_padding "" "if set to something else than \"\" = override default padding of contents from border"
+seta hud_panel_modicons_ca_layout "" "2 possible layouts: 0) number of alive players; 1) icons and number of alive players"
seta hud_panel_modicons_dom_layout "" "3 possible layouts: 0) only icons; 1) icons and percentage of average pps (points per second); 2) icons and average pps"
+seta hud_panel_modicons_freezetag_layout "" "2 possible layouts: 0) number of alive players; 1) icons and number of alive players"
seta hud_panel_pressedkeys "" "enable/disable this panel, 1 = show only when spectating other players, 2 = show always"
seta hud_panel_pressedkeys_pos "" "position of this base of the panel"
set g_arena 0 "Arena: many one-on-one rounds are played to find the winner"
set g_arena_maxspawned 2 "maximum number of players to spawn at once (the rest is spectating, waiting for their turn)"
set g_arena_roundbased 1 "if disabled, the next player will spawn as soon as someone dies"
+set g_arena_round_timelimit 180
set g_arena_warmup 5 "time, newly spawned players have to prepare themselves in round based matches"
set g_ca_warmup 10 "how long the players will have time to run around the map before the round starts"
set g_ca_damage2score_multiplier 0.01
set g_ca_round_timelimit 180
+seta g_ca_teams_override 0
+set g_ca_teams 0
+
// ==================
seta g_freezetag_revive_speed 0.4 "Speed for reviving a frozen teammate"
seta g_freezetag_revive_clearspeed 1.6 "Speed at which reviving progress gets lost when out of range"
seta g_freezetag_revive_extra_size 100 "Distance in qu that you can stand from a frozen teammate to keep reviving him"
+set g_freezetag_round_timelimit 180
seta g_freezetag_frozen_force 0.6 "How much to multiply the force on a frozen player with"
+seta g_freezetag_teams_override 0
+set g_freezetag_teams 0
// ==========
seta hud_panel_modicons_bg_alpha ""
seta hud_panel_modicons_bg_border ""
seta hud_panel_modicons_bg_padding "0"
+seta hud_panel_modicons_ca_layout "1"
seta hud_panel_modicons_dom_layout "1"
+seta hud_panel_modicons_freezetag_layout "1"
seta hud_panel_pressedkeys 1
seta hud_panel_pressedkeys_pos "0.450000 0.720000"
seta hud_panel_modicons_bg_alpha ""
seta hud_panel_modicons_bg_border ""
seta hud_panel_modicons_bg_padding ""
+seta hud_panel_modicons_ca_layout "1"
seta hud_panel_modicons_dom_layout "1"
+seta hud_panel_modicons_freezetag_layout "1"
seta hud_panel_pressedkeys 1
seta hud_panel_pressedkeys_pos "0.450000 0.650000"
seta hud_panel_modicons_bg_alpha ""
seta hud_panel_modicons_bg_border ""
seta hud_panel_modicons_bg_padding ""
+seta hud_panel_modicons_ca_layout "1"
seta hud_panel_modicons_dom_layout "1"
+seta hud_panel_modicons_freezetag_layout "1"
seta hud_panel_pressedkeys 1
seta hud_panel_pressedkeys_pos "0.450000 0.690000"
seta hud_panel_modicons_bg_alpha ""
seta hud_panel_modicons_bg_border ""
seta hud_panel_modicons_bg_padding ""
+seta hud_panel_modicons_ca_layout "1"
seta hud_panel_modicons_dom_layout "1"
+seta hud_panel_modicons_freezetag_layout "1"
seta hud_panel_pressedkeys 1
seta hud_panel_pressedkeys_pos "0.410000 0.710000"
seta hud_panel_modicons_bg_alpha ""
seta hud_panel_modicons_bg_border ""
seta hud_panel_modicons_bg_padding ""
+seta hud_panel_modicons_ca_layout "1"
seta hud_panel_modicons_dom_layout "1"
+seta hud_panel_modicons_freezetag_layout "1"
seta hud_panel_pressedkeys 1
seta hud_panel_pressedkeys_pos "0.440000 0.760000"
void Announcer_Countdown()
{
float starttime = getstatf(STAT_GAMESTARTTIME);
+ float roundstarttime = getstatf(STAT_ROUNDSTARTTIME);
+ if(starttime <= time) // game start time has passed
+ announcer_5min = announcer_1min = FALSE; // reset maptime announcers now as well
+ if(roundstarttime == -1)
+ {
+ // stop countdown immediately
+ centerprint_generic(CPID_GAME_STARTING, "", 1, 0);
+ remove(self);
+ return;
+ }
+ if(roundstarttime >= starttime)
+ starttime = roundstarttime;
+
float countdown = (starttime - time);
float countdown_rounded = floor(0.5 + countdown);
-
+
if(countdown <= 0) // countdown has finished, starttime is now
{
if (!spectatee_status)
centerprint_generic(CPID_GAME_STARTING, _("^1Begin!"), 1, 0);
Announcer_Play("begin");
- announcer_5min = announcer_1min = FALSE; // reset maptime announcers now as well
remove(self);
return;
}
else // countdown is still going
{
if (!spectatee_status)
- centerprint_generic(CPID_GAME_STARTING, _("^1Game starts in %d seconds"), 1, countdown_rounded);
+ {
+ if(roundstarttime == starttime)
+ centerprint_generic(CPID_GAME_STARTING, _("^1Round starts in %d seconds"), 1, countdown_rounded);
+ else
+ centerprint_generic(CPID_GAME_STARTING, _("^1Game starts in %d seconds"), 1, countdown_rounded);
+ }
if(countdown_rounded <= 3 && countdown_rounded >= 1)
Announcer_Play(ftos(countdown_rounded));
void Announcer_Gamestart()
{
float startTime = getstatf(STAT_GAMESTARTTIME);
-
+ float roundstarttime = getstatf(STAT_ROUNDSTARTTIME);
+ if(roundstarttime > startTime)
+ startTime = roundstarttime;
+
if(previous_game_starttime != startTime)
{
if((time + 5.0) < startTime) // if connecting to server while restart was active don't always play prepareforbattle
Announcer_Play("prepareforbattle");
-
+
if(time < startTime)
{
entity e;
float autocvar_hud_panel_infomessages;
float autocvar_hud_panel_infomessages_flip;
float autocvar_hud_panel_modicons;
+float autocvar_hud_panel_modicons_ca_layout;
float autocvar_hud_panel_modicons_dom_layout;
+float autocvar_hud_panel_modicons_freezetag_layout;
float autocvar_hud_panel_notify;
float autocvar_hud_panel_notify_fadetime;
float autocvar_hud_panel_notify_flip;
float mod_active; // is there any active mod icon?
-// Clan Arena HUD modicons
-void HUD_Mod_CA(vector pos, vector mySize)
+void DrawCAItem(vector myPos, vector mySize, float aspect_ratio, float layout, float i)
{
- mod_active = 1; // CA should never hide the mod icons panel
- float redalive, bluealive;
- redalive = getstati(STAT_REDALIVE);
- bluealive = getstati(STAT_BLUEALIVE);
+ float stat;
+ string pic;
+ vector color;
+#ifdef GMQCC
+ stat = -1;
+ pic = "";
+ color = '0 0 0';
+#endif
+ switch(i)
+ {
+ case 0:
+ stat = getstati(STAT_REDALIVE);
+ pic = "player_red.tga";
+ color = '1 0 0';
+ break;
+ case 1:
+ stat = getstati(STAT_BLUEALIVE);
+ pic = "player_blue.tga";
+ color = '0 0 1';
+ break;
+ case 2:
+ stat = getstati(STAT_YELLOWALIVE);
+ pic = "player_yellow.tga";
+ color = '1 1 0';
+ break;
+ default:
+ case 3:
+ stat = getstati(STAT_PINKALIVE);
+ pic = "player_pink.tga";
+ color = '1 0 1';
+ break;
+ }
- vector redpos, bluepos;
- if(mySize_x > mySize_y)
+ if(mySize_x/mySize_y > aspect_ratio)
+ {
+ i = aspect_ratio * mySize_y;
+ myPos_x = myPos_x + (mySize_x - i) / 2;
+ mySize_x = i;
+ }
+ else
+ {
+ i = 1/aspect_ratio * mySize_x;
+ myPos_y = myPos_y + (mySize_y - i) / 2;
+ mySize_y = i;
+ }
+
+ if(layout)
{
- redpos = pos;
- bluepos = pos + eY * 0.5 * mySize_y;
- drawpic_aspect_skin(redpos, "player_red.tga", 0.5 * mySize, '1 1 1', panel_fg_alpha, DRAWFLAG_NORMAL);
- drawstring_aspect(redpos + eX * 0.5 * mySize_x, ftos(redalive), 0.5 * mySize, '1 1 1', panel_fg_alpha, DRAWFLAG_NORMAL);
- drawpic_aspect_skin(bluepos, "player_blue.tga", 0.5 * mySize, '1 1 1', panel_fg_alpha, DRAWFLAG_NORMAL);
- drawstring_aspect(bluepos + eX * 0.5 * mySize_x, ftos(bluealive), 0.5 * mySize, '1 1 1', panel_fg_alpha, DRAWFLAG_NORMAL);
+ drawpic_aspect_skin(myPos, pic, eX * 0.7 * mySize_x + eY * mySize_y, '1 1 1', panel_fg_alpha, DRAWFLAG_NORMAL);
+ drawstring_aspect(myPos + eX * 0.7 * mySize_x, ftos(stat), eX * 0.3 * mySize_x + eY * mySize_y, color, panel_fg_alpha, DRAWFLAG_NORMAL);
}
else
+ drawstring_aspect(myPos, ftos(stat), mySize, color, panel_fg_alpha, DRAWFLAG_NORMAL);
+}
+
+// Clan Arena and Freeze Tag HUD modicons
+void HUD_Mod_CA(vector myPos, vector mySize)
+{
+ mod_active = 1; // required in each mod function that always shows something
+ entity tm;
+ float teams_count = 0;
+ for(tm = teams.sort_next; tm; tm = tm.sort_next)
+ if(tm.team != COLOR_SPECTATOR)
+ ++teams_count;
+
+ float layout;
+ if(gametype == MAPINFO_TYPE_CA)
+ layout = autocvar_hud_panel_modicons_ca_layout;
+ else //if(gametype == MAPINFO_TYPE_FREEZETAG)
+ layout = autocvar_hud_panel_modicons_freezetag_layout;
+ float rows, columns, aspect_ratio;
+ rows = mySize_y/mySize_x;
+ aspect_ratio = (layout) ? 2 : 1;
+ rows = bound(1, floor((sqrt((4 * aspect_ratio * teams_count + rows) * rows) + rows + 0.5) / 2), teams_count);
+ columns = ceil(teams_count/rows);
+
+ int i;
+ float row = 0, column = 0;
+ for(i=0; i<teams_count; ++i)
{
- redpos = pos;
- bluepos = pos + eY * 0.5 * mySize_y;
- drawpic_aspect_skin(redpos, "player_red.tga", eX * mySize_x + eY * 0.3 * mySize_y, '1 1 1', panel_fg_alpha, DRAWFLAG_NORMAL);
- drawstring_aspect(redpos + eY * 0.3 * mySize_y, ftos(redalive), eX * mySize_x + eY * 0.2 * mySize_y, '1 1 1', panel_fg_alpha, DRAWFLAG_NORMAL);
- drawpic_aspect_skin(bluepos, "player_blue.tga", eX * mySize_x + eY * 0.3 * mySize_y, '1 1 1', panel_fg_alpha, DRAWFLAG_NORMAL);
- drawstring_aspect(bluepos + eY * 0.3 * mySize_y, ftos(bluealive), eX * mySize_x + eY * 0.2 * mySize_y, '1 1 1', panel_fg_alpha, DRAWFLAG_NORMAL);
+ vector pos, itemSize;
+ pos = myPos + eX * column * mySize_x*(1/columns) + eY * row * mySize_y*(1/rows);
+ itemSize = eX * mySize_x*(1/columns) + eY * mySize_y*(1/rows);
+
+ DrawCAItem(pos, itemSize, aspect_ratio, layout, i);
+
+ ++row;
+ if(row >= rows)
+ {
+ row = 0;
+ ++column;
+ }
}
}
HUD_Write_PanelCvar_q("_alreadyvoted_alpha");
break;
case HUD_PANEL_MODICONS:
+ HUD_Write_PanelCvar_q("_ca_layout");
HUD_Write_PanelCvar_q("_dom_layout");
+ HUD_Write_PanelCvar_q("_freezetag_layout");
break;
case HUD_PANEL_PRESSEDKEYS:
HUD_Write_PanelCvar_q("_attack");
const float STAT_SECRETS_FOUND = 71;
const float STAT_RESPAWN_TIME = 72;
+const float STAT_ROUNDSTARTTIME = 73;
// mod stats (1xx)
const float STAT_REDALIVE = 100;
float CPID_MINSTA_FINDAMMO = 3;
float CPID_NIX_WPNCHANGE = 4;
float CPID_DISCONNECT_IDLING = 5;
-float CPID_ROUND_STARTING = 6;
+
float CPID_GAME_STARTING = 7;
float CPID_TIMEOUT_COUNTDOWN = 8;
float CPID_MOTD = 9;
+++ /dev/null
-float maxspawned;
-float numspawned;
-float arena_roundbased;
-.float spawned;
-.entity spawnqueue_next;
-.entity spawnqueue_prev;
-.float spawnqueue_in;
-entity spawnqueue_first;
-entity spawnqueue_last;
-entity champion;
-float warmup;
-.float caplayer;
-
-void PutObserverInServer();
-void PutClientInServer();
-
-float next_round;
-float redalive, bluealive, yellowalive, pinkalive;
-.float redalive_stat, bluealive_stat, yellowalive_stat, pinkalive_stat;
-float red_players, blue_players, yellow_players, pink_players;
-float total_players;
-
-/**
- * Resets the state of all clients, items, flags, runes, keys, weapons, waypoints, ... of the map.
- * Sets the 'warmup' global variable.
- */
-void reset_map(float dorespawn)
-{
- entity oldself;
- oldself = self;
-
- if(time <= game_starttime && round_handler_IsActive())
- round_handler_Reset(game_starttime + 1);
-
- if(g_arena)
- {
- warmup = max(time, game_starttime);
- if(autocvar_g_arena_warmup > 0)
- warmup += autocvar_g_arena_warmup;
- }
- else if(g_ca)
- {
- warmup = max(time, game_starttime);
- if(autocvar_g_ca_warmup > 0)
- warmup += autocvar_g_ca_warmup;
- allowed_to_spawn = 1;
- }
- else if(g_race || g_cts)
- race_ReadyRestart();
- else MUTATOR_CALLHOOK(reset_map_global);
-
- lms_lowest_lives = 999;
- lms_next_place = player_count;
-
- for(self = world; (self = nextent(self)); )
- if(clienttype(self) == CLIENTTYPE_NOTACLIENT)
- {
- if(self.reset)
- {
- self.reset();
- continue;
- }
-
- if(self.team_saved)
- self.team = self.team_saved;
-
- if(self.flags & FL_PROJECTILE) // remove any projectiles left
- remove(self);
- }
-
- // Waypoints and assault start come LAST
- for(self = world; (self = nextent(self)); )
- if(clienttype(self) == CLIENTTYPE_NOTACLIENT)
- {
- if(self.reset2)
- {
- self.reset2();
- continue;
- }
- }
-
- // Moving the player reset code here since the player-reset depends
- // on spawnpoint entities which have to be reset first --blub
- if(dorespawn)
- if(!MUTATOR_CALLHOOK(reset_map_players))
- FOR_EACH_CLIENT(self) {
- if(self.flags & FL_CLIENT) // reset all players
- {
- if(g_arena)
- {
- if(self.spawned)
- PutClientInServer();
- else
- PutObserverInServer();
- }
- else if(g_ca && self.caplayer) {
- self.classname = "player";
- PutClientInServer();
- }
- else
- {
- /*
- only reset players if a restart countdown is active
- this can either be due to cvar sv_ready_restart_after_countdown having set
- restart_mapalreadyrestarted to 1 after the countdown ended or when
- sv_ready_restart_after_countdown is not used and countdown is still running
- */
- if (restart_mapalreadyrestarted || (time < game_starttime))
- {
- //NEW: changed behaviour so that it prevents that previous spectators/observers suddenly spawn as players
- if (self.classname == "player") {
- //PlayerScore_Clear(self);
- if(g_lms)
- PlayerScore_Add(self, SP_LMS_LIVES, LMS_NewPlayerLives());
- self.killcount = 0;
- //stop the player from moving so that he stands still once he gets respawned
- self.velocity = '0 0 0';
- self.avelocity = '0 0 0';
- self.movement = '0 0 0';
- PutClientInServer();
- }
- }
- }
- }
- }
-
- if(g_keyhunt)
- kh_Controller_SetThink_NoMsg(autocvar_g_balance_keyhunt_delay_round+(game_starttime - time), kh_StartRound);
-
- if(g_arena)
- if(champion && champion.classname == "player" && player_count > 1)
- UpdateFrags(champion, +1);
-
- self = oldself;
-}
-
-void Spawnqueue_Insert(entity e)
-{
- if(e.spawnqueue_in)
- return;
- dprint(strcat("Into queue: ", e.netname, "\n"));
- e.spawnqueue_in = TRUE;
- e.spawnqueue_prev = spawnqueue_last;
- e.spawnqueue_next = world;
- if(spawnqueue_last)
- spawnqueue_last.spawnqueue_next = e;
- spawnqueue_last = e;
- if(!spawnqueue_first)
- spawnqueue_first = e;
-}
-
-void Spawnqueue_Remove(entity e)
-{
- if(!e.spawnqueue_in)
- return;
- dprint(strcat("Out of queue: ", e.netname, "\n"));
- e.spawnqueue_in = FALSE;
- if(e == spawnqueue_first)
- spawnqueue_first = e.spawnqueue_next;
- if(e == spawnqueue_last)
- spawnqueue_last = e.spawnqueue_prev;
- if(e.spawnqueue_prev)
- e.spawnqueue_prev.spawnqueue_next = e.spawnqueue_next;
- if(e.spawnqueue_next)
- e.spawnqueue_next.spawnqueue_prev = e.spawnqueue_prev;
- e.spawnqueue_next = world;
- e.spawnqueue_prev = world;
-}
-
-void Spawnqueue_Unmark(entity e)
-{
- if(!e.spawned)
- return;
- e.spawned = FALSE;
- numspawned = numspawned - 1;
-}
-
-void Spawnqueue_Mark(entity e)
-{
- if(e.spawned)
- return;
- e.spawned = TRUE;
- numspawned = numspawned + 1;
-}
-
-/**
- * If roundbased arena game mode is active, it centerprints the texts for the
- * player when player is waiting for the countdown to finish.
- * Blocks the players movement while countdown is active.
- * Unblocks the player once the countdown is over.
- *
- * Called in StartFrame()
- */
-float roundStartTime_prev; // prevent networkspam
-void Arena_Warmup()
-{
- float f;
- entity e;
-
- if(gameover)
- {
- if(warmup && time < warmup)
- {
- FOR_EACH_REALCLIENT(e)
- Send_CSQC_Centerprint_Generic_Expire(e, CPID_ROUND_STARTING);
- warmup = 0;
- }
- if(champion && g_arena)
- {
- FOR_EACH_REALCLIENT(e)
- centerprint(e, strcat("The Champion is ", champion.netname));
- champion = world;
- }
- return;
- }
- if(time < game_starttime)
- return;
-
- f = ceil(warmup - time);
-
- if(g_ca)
- {
- if(inWarmupStage)
- allowed_to_spawn = 1;
- else if (warmup == 0) //first warmup or warmup cleared
- {
- if (red_players && blue_players)
- reset_map(TRUE);
- else if(f != roundStartTime_prev)
- {
- if(roundStartTime_prev & 1) // msg every 2 seconds
- if(roundStartTime_prev - f == 1) // block sudden msg
- FOR_EACH_REALCLIENT(self)
- Send_CSQC_Centerprint_Generic(self, CPID_ROUND_STARTING, "^1Need at least 1 player in each team to play CA", 2, 0);
- roundStartTime_prev = f;
- }
- return;
- }
- }
-
- if(time < warmup && !inWarmupStage)
- {
- if (g_ca)
- allowed_to_spawn = 1;
- else if(champion && g_arena)
- {
- FOR_EACH_REALCLIENT(e)
- centerprint(e, strcat("The Champion is ", champion.netname));
- }
-
- if(f != roundStartTime_prev) {
- roundStartTime_prev = f;
-
- if(g_ca && !(red_players && blue_players)) {
- warmup = 0;
- return;
- }
-
- if(f == 5)
- Announce("prepareforbattle");
- else if(f == 3)
- Announce("3");
- else if(f == 2)
- Announce("2");
- else if(f == 1)
- Announce("1");
-
- FOR_EACH_REALCLIENT(e)
- Send_CSQC_Centerprint_Generic(e, CPID_ROUND_STARTING, "Round will start in %d", 1, f);
- }
-
- if (g_arena) {
- FOR_EACH_CLIENT(e)
- {
- if(e.spawned && e.classname == "player")
- e.player_blocked = 1;
- }
- }
- }
- else if(f > -1 && f != roundStartTime_prev && !inWarmupStage)
- {
- roundStartTime_prev = f;
- if(g_ca) {
- if(red_players && blue_players)
- allowed_to_spawn = 0;
- else
- {
- warmup = 0;
- return;
- }
- }
- Announce("begin");
- FOR_EACH_REALCLIENT(e)
- Send_CSQC_Centerprint_Generic(e, CPID_ROUND_STARTING, "^1Begin!", 1, 0);
-
- if(g_arena) {
- FOR_EACH_CLIENT(e)
- {
- if(e.player_blocked)
- e.player_blocked = 0;
- }
- }
- }
-
- // clear champion to avoid centerprinting again the champion msg
- if (champion)
- champion = world;
-}
-
-void count_players()
-{
- // count amount of players in each team
- total_players = red_players = blue_players = yellow_players = pink_players = 0;
- FOR_EACH_PLAYER(self) {
- if (self.team == COLOR_TEAM1)
- {
- red_players += 1;
- total_players += 1;
- }
- else if (self.team == COLOR_TEAM2)
- {
- blue_players += 1;
- total_players += 1;
- }
- else if (self.team == COLOR_TEAM3)
- {
- yellow_players += 1;
- total_players += 1;
- }
- else if (self.team == COLOR_TEAM4)
- {
- pink_players += 1;
- total_players += 1;
- }
- }
-}
-
-void count_alive_players()
-{
- redalive = bluealive = yellowalive = pinkalive = 0;
- FOR_EACH_PLAYER(self) {
- if (self.team == COLOR_TEAM1 && self.health >= 1)
- redalive += 1;
- else if (self.team == COLOR_TEAM2 && self.health >= 1)
- bluealive += 1;
- }
- FOR_EACH_REALCLIENT(self) {
- self.redalive_stat = redalive;
- self.bluealive_stat = bluealive;
- }
-}
-
-/**
- * This function finds out whether an arena round is over 1 player is left.
- * It determines the last player who's still alive and saves it's entity reference
- * in the global variable 'champion'. Then the new enemy/enemies are put into the server.
- *
- * Gets called in StartFrame()
- */
-void Spawnqueue_Check()
-{
- if(time < warmup + 1 || inWarmupStage || intermission_running)
- return;
-
- if(g_ca) {
- if(allowed_to_spawn) // round is not started yet
- return;
- if(!next_round) {
- if(!(redalive && bluealive)) {
- // every player of (at least) one team is dead, round ends here
- if(redalive) {
- play2all("ctf/red_capture.wav");
- FOR_EACH_CLIENT(self) centerprint(self, "^1RED ^7team wins the round");
- TeamScore_AddToTeam(COLOR_TEAM1, ST_SCORE, +1);
- }
- else if(bluealive) {
- play2all("ctf/blue_capture.wav");
- FOR_EACH_CLIENT(self) centerprint(self, "^4BLUE ^7team wins the round");
- TeamScore_AddToTeam(COLOR_TEAM2, ST_SCORE, +1);
- }
- else
- FOR_EACH_CLIENT(self) centerprint(self, "^7Round tied");
- next_round = -1;
- }
- else if(time - warmup > autocvar_g_ca_round_timelimit) {
- FOR_EACH_CLIENT(self) centerprint(self, "^7Round tied");
- next_round = time + 5;
- }
- }
- else if(next_round == -1) {
- // wait for killed players to be put as spectators
- if(!(red_players && blue_players))
- next_round = time + 5;
- }
- else if((next_round > 0 && next_round < time))
- {
- next_round = 0;
- reset_map(TRUE);
- }
- } else { // arena
- //extend next_round if it isn't set yet and only 1 player is spawned
- if(!next_round)
- if(numspawned < 2)
- next_round = time + 3;
-
- if(!arena_roundbased || (next_round && next_round < time && player_count > 1))
- {
- next_round = 0;
-
- if(arena_roundbased)
- {
- champion = find(world, classname, "player");
- while(champion && champion.deadflag)
- champion = find(champion, classname, "player");
- reset_map(TRUE);
- }
-
- while(numspawned < maxspawned && spawnqueue_first)
- {
- self = spawnqueue_first;
-
- bprint ("^4", self.netname, "^4 is the next challenger\n");
-
- Spawnqueue_Remove(self);
- Spawnqueue_Mark(self);
-
- self.classname = "player";
- PutClientInServer();
- }
- }
- }
-}
-
-void Arena_Main()
-{
- if(!(g_ca || g_arena))
- return;
-
- if(g_ca)
- {
- count_players();
- count_alive_players();
- }
- if(!g_arena || arena_roundbased)
- Arena_Warmup();
- Spawnqueue_Check();
-}
-
float autocvar_g_arena_point_leadlimit;
float autocvar_g_arena_point_limit;
float autocvar_g_arena_roundbased;
+float autocvar_g_arena_round_timelimit;
float autocvar_g_arena_warmup;
float autocvar_g_assault;
float autocvar_g_balance_armor_blockpercent;
float autocvar_g_ca_point_limit;
float autocvar_g_ca_round_timelimit;
float autocvar_g_ca_spectate_enemies;
+float autocvar_g_ca_teams;
+float autocvar_g_ca_teams_override;
float autocvar_g_ca_warmup;
float autocvar_g_campaign;
#define autocvar_g_campaign_forceteam cvar("g_campaign_forceteam")
float autocvar_g_freezetag_revive_extra_size;
float autocvar_g_freezetag_revive_speed;
float autocvar_g_freezetag_revive_clearspeed;
+float autocvar_g_freezetag_round_timelimit;
+float autocvar_g_freezetag_teams;
+float autocvar_g_freezetag_teams_override;
float autocvar_g_freezetag_warmup;
#define autocvar_g_friendlyfire cvar("g_friendlyfire")
#define autocvar_g_friendlyfire_virtual cvar("g_friendlyfire_virtual")
else
{
float mindist;
- if (arena_roundbased && !g_ca)
+ if (g_arena && arena_roundbased)
mindist = 800;
else
mindist = 100;
WriteEntity(MSG_ONE, self);
}
+ if(g_lms)
+ {
+ // Only if the player cannot play at all
+ if(PlayerScore_Add(self, SP_LMS_RANK, 0) == 666)
+ self.frags = FRAGS_SPECTATOR;
+ else
+ self.frags = FRAGS_LMS_LOSER;
+ }
+ else if((g_race && g_race_qualifying) || g_cts)
+ {
+ if(PlayerScore_Add(self, SP_RACE_FASTEST, 0))
+ self.frags = FRAGS_LMS_LOSER;
+ else
+ self.frags = FRAGS_SPECTATOR;
+ }
+ else
+ self.frags = FRAGS_SPECTATOR;
+
DropAllRunes(self);
MUTATOR_CALLHOOK(MakePlayerObserver);
self.punchvector = '0 0 0';
self.oldvelocity = self.velocity;
self.fire_endtime = -1;
-
- if(g_arena)
- {
- if(self.version_mismatch)
- {
- self.frags = FRAGS_SPECTATOR;
- Spawnqueue_Unmark(self);
- Spawnqueue_Remove(self);
- }
- else
- {
- self.frags = FRAGS_LMS_LOSER;
- Spawnqueue_Insert(self);
- }
- }
- else if(g_lms)
- {
- // Only if the player cannot play at all
- if(PlayerScore_Add(self, SP_LMS_RANK, 0) == 666)
- self.frags = FRAGS_SPECTATOR;
- else
- self.frags = FRAGS_LMS_LOSER;
- }
- else if(g_ca)
- {
- if(self.caplayer)
- self.frags = FRAGS_LMS_LOSER;
- else
- self.frags = FRAGS_SPECTATOR;
- }
- else if((g_race && g_race_qualifying) || g_cts)
- {
- if(PlayerScore_Add(self, SP_RACE_FASTEST, 0))
- self.frags = FRAGS_LMS_LOSER;
- else
- self.frags = FRAGS_SPECTATOR;
- }
- else
- self.frags = FRAGS_SPECTATOR;
}
.float model_randomizer;
void PutClientInServer (void)
{
if(clienttype(self) == CLIENTTYPE_BOT)
- {
self.classname = "player";
- if(g_ca)
- self.caplayer = 1;
- }
else if(clienttype(self) == CLIENTTYPE_REAL)
{
msg_entity = self;
self.classname = "observer";
}
- if((g_arena && !self.spawned) || (g_ca && !allowed_to_spawn))
- self.classname = "observer";
+ MUTATOR_CALLHOOK(PutClientInServer);
if(gameover)
self.classname = "observer";
- if(self.classname == "player" && (!g_ca || (g_ca && allowed_to_spawn))) {
+ if(self.classname == "player") {
entity spot, oldself;
float j;
self.lastteleporttime = time; // prevent insane speeds due to changing origin
self.hud = HUD_NORMAL;
- if(g_arena)
- {
- Spawnqueue_Remove(self);
- Spawnqueue_Mark(self);
- }
- else if(g_ca)
- self.caplayer = 1;
-
self.event_damage = PlayerDamage;
self.bot_attack = TRUE;
void ClientKill (void)
{
- if (gameover)
- return;
+ if(gameover) return;
+ if(g_ca && player_count == 1) return;
+ if(self.player_blocked) return;
+ if(self.freezetag_frozen) return;
- if((g_arena || g_ca) && ((champion && champion.classname == "player" && player_count > 1) || player_count == 1)) // don't allow a kill in this case either
- {
- // do nothing
- }
- else if(self.freezetag_frozen)
- {
- // do nothing
- }
- else
- ClientKill_TeamChange(0);
+ ClientKill_TeamChange(0);
}
void CTS_ClientKill (entity e) // silent version of ClientKill, used when player finishes a CTS run. Useful to prevent cheating by running back to the start line and starting out with more speed
else
stuffcmd(self, "set _teams_available 0\n");
- if(g_arena || g_ca)
- {
- self.classname = "observer";
- if(g_arena)
- Spawnqueue_Insert(self);
- }
-
attach_entcs();
bot_relinkplayerlist();
CSQCMODEL_AUTOINIT();
self.model_randomizer = random();
-
- if(clienttype(self) != CLIENTTYPE_REAL)
- return;
-
- sv_notice_join();
-
- MUTATOR_CALLHOOK(ClientConnect);
+
+ if(clienttype(self) == CLIENTTYPE_REAL)
+ sv_notice_join();
+
+ MUTATOR_CALLHOOK(ClientConnect);
}
/*
=============
bot_relinkplayerlist();
- if(g_arena)
- {
- Spawnqueue_Unmark(self);
- Spawnqueue_Remove(self);
- }
-
accuracy_free(self);
ClientData_Detach();
PlayerScore_Detach(self);
WarpZone_PlayerPhysics_FixVAngle();
self.stat_game_starttime = game_starttime;
+ self.stat_round_starttime = round_starttime;
self.stat_allow_oldnexbeam = autocvar_g_allow_oldnexbeam;
self.stat_leadlimit = autocvar_leadlimit;
- if(g_arena || (g_ca && !allowed_to_spawn))
- self.stat_respawn_time = 0;
- else
- self.stat_respawn_time = self.respawn_time;
+ self.stat_respawn_time = self.respawn_time;
if(frametime)
{
float valid_damage_for_weaponstats;
float excess;
- if((g_arena && numspawned < 2) || (g_ca && allowed_to_spawn) && !inWarmupStage)
- return;
-
dh = max(self.health, 0);
da = max(self.armorvalue, 0);
if(defer_ClientKill_Now_TeamChange)
ClientKill_Now_TeamChange(); // can turn player into spectator
- if(g_arena)
- Spawnqueue_Unmark(self);
-
// player could have been miraculously resuscitated ;)
// e.g. players in freezetag get frozen, they don't really die
if(self.health >= 1 || self.classname != "player")
return 0;
if (g_lms)
return 0;
- if (g_ca)
- return 0;
if (g_cts)
return 0;
if (g_nexball && w == WEP_GRENADE_LAUNCHER)
if (frametime)
self.weapon_frametime = frametime;
- if(((arena_roundbased || g_ca) && time < warmup) || ((time < game_starttime) && !autocvar_sv_ready_restart_after_countdown))
+ if(time < game_starttime && !autocvar_sv_ready_restart_after_countdown)
return;
if(round_handler_IsActive() && !round_handler_IsRoundStarted())
return;
+ if(self.player_blocked)
+ return;
+
if(self.freezetag_frozen == 1)
return;
{
if(nJoinAllowed(self))
{
- if(g_ca) { self.caplayer = 1; }
if(autocvar_g_campaign) { campaign_bots_may_start = 1; }
-
+
self.classname = "player";
PlayerScore_Clear(self);
bprint ("^4", self.netname, "^4 is playing now\n");
// Game logic for warmup
// =======================
+// Resets the state of all clients, items, flags, runes, keys, weapons, waypoints, ... of the map.
+void reset_map(float dorespawn)
+{
+ entity oldself;
+ oldself = self;
+
+ if(time <= game_starttime && round_handler_IsActive())
+ round_handler_Reset(game_starttime + 1);
+
+ if(g_race || g_cts)
+ race_ReadyRestart();
+ else MUTATOR_CALLHOOK(reset_map_global);
+
+ lms_lowest_lives = 999;
+ lms_next_place = player_count;
+
+ for(self = world; (self = nextent(self)); )
+ if(clienttype(self) == CLIENTTYPE_NOTACLIENT)
+ {
+ if(self.reset)
+ {
+ self.reset();
+ continue;
+ }
+
+ if(self.team_saved)
+ self.team = self.team_saved;
+
+ if(self.flags & FL_PROJECTILE) // remove any projectiles left
+ remove(self);
+ }
+
+ // Waypoints and assault start come LAST
+ for(self = world; (self = nextent(self)); )
+ if(clienttype(self) == CLIENTTYPE_NOTACLIENT)
+ {
+ if(self.reset2)
+ {
+ self.reset2();
+ continue;
+ }
+ }
+
+ // Moving the player reset code here since the player-reset depends
+ // on spawnpoint entities which have to be reset first --blub
+ if(dorespawn)
+ if(!MUTATOR_CALLHOOK(reset_map_players))
+ FOR_EACH_CLIENT(self) // reset all players
+ {
+ /*
+ only reset players if a restart countdown is active
+ this can either be due to cvar sv_ready_restart_after_countdown having set
+ restart_mapalreadyrestarted to 1 after the countdown ended or when
+ sv_ready_restart_after_countdown is not used and countdown is still running
+ */
+ if (restart_mapalreadyrestarted || (time < game_starttime))
+ {
+ //NEW: changed behaviour so that it prevents that previous spectators/observers suddenly spawn as players
+ if (self.classname == "player") {
+ //PlayerScore_Clear(self);
+ if(g_lms)
+ PlayerScore_Add(self, SP_LMS_LIVES, LMS_NewPlayerLives());
+ self.killcount = 0;
+ //stop the player from moving so that he stands still once he gets respawned
+ self.velocity = '0 0 0';
+ self.avelocity = '0 0 0';
+ self.movement = '0 0 0';
+ PutClientInServer();
+ }
+ }
+ }
+
+ if(g_keyhunt)
+ kh_Controller_SetThink_NoMsg(autocvar_g_balance_keyhunt_delay_round+(game_starttime - time), kh_StartRound);
+
+ self = oldself;
+}
+
// Restarts the map after the countdown is over (and cvar sv_ready_restart_after_countdown is set)
void ReadyRestart_think()
{
float readyrestart_happened; // keeps track of whether a restart has already happened
float restart_mapalreadyrestarted; // bool, indicates whether reset_map() was already executed
.float ready; // flag for if a player is ready
+void reset_map(float dorespawn);
void ReadyCount();
\ No newline at end of file
string cvar_purechanges;
float cvar_purechanges_count;
-float game_starttime; //point in time when the countdown is over
+float game_starttime; //point in time when the countdown to game start is over
+float round_starttime; //point in time when the countdown to round start is over
.float stat_game_starttime;
+.float stat_round_starttime;
.float stat_sv_airaccel_qw;
.float stat_sv_airstrafeaccel_qw;
.float hagar_load;
-float allowed_to_spawn; // boolean variable used by the clan arena code to determine if a player can spawn (after the round has ended)
-
float serverflags;
.float team_forced; // can be a team number to force a team, or 0 for default action, or -1 for forced spectator
PlayerScore_Add(targ, SP_DEATHS, 1);
- if(g_arena || g_ca)
- if(autocvar_g_arena_roundbased)
- return;
-
if(targ != attacker) // not for suicides
if(g_weaponarena_random)
{
float s;
vector vs;
- if((arena_roundbased && time < warmup) || (time < game_starttime))
- return;
-
- if(self.freezetag_frozen)
- return;
-
- if(self.vehicle)
- return;
+ if(time < game_starttime) return;
+ if(self.player_blocked) return;
+ if(self.freezetag_frozen) return;
+ if(self.vehicle) return;
makevectors(self.v_angle);
addstat(STAT_SWITCHWEAPON, AS_INT, switchweapon);
addstat(STAT_SWITCHINGWEAPON, AS_INT, switchingweapon);
addstat(STAT_GAMESTARTTIME, AS_FLOAT, stat_game_starttime);
+ addstat(STAT_ROUNDSTARTTIME, AS_FLOAT, stat_round_starttime);
addstat(STAT_ALLOW_OLDNEXBEAM, AS_INT, stat_allow_oldnexbeam);
Nagger_Init();
addstat(STAT_HAGAR_LOAD, AS_INT, hagar_load);
- if(g_ca)
- {
- addstat(STAT_REDALIVE, AS_INT, redalive_stat);
- addstat(STAT_BLUEALIVE, AS_INT, bluealive_stat);
- }
-
// g_movementspeed hack
addstat(STAT_MOVEVARS_AIRSPEEDLIMIT_NONQW, AS_FLOAT, stat_sv_airspeedlimit_nonqw);
addstat(STAT_MOVEVARS_MAXSPEED, AS_FLOAT, stat_sv_maxspeed);
MUTATOR_HOOKABLE(MakePlayerObserver);
// called when a player becomes observer, after shared setup
+MUTATOR_HOOKABLE(PutClientInServer);
+ entity self; // client wanting to spawn
+
MUTATOR_HOOKABLE(PlayerSpawn);
entity spawn_spot; // spot that was used, or world
// called when a player spawns as player, after shared setup, before his weapon is chosen (so items may be changed in here)
MUTATOR_HOOKABLE(reset_map_players);
// called in reset_map
+MUTATOR_HOOKABLE(ForbidPlayerScore_Clear);
+ // returns 1 if clearing player score shall not be allowed
+
MUTATOR_HOOKABLE(ClientDisconnect);
// called when a player disconnects
--- /dev/null
+.float spawned;
+float maxspawned;
+float numspawned;
+.entity spawnqueue_next;
+.entity spawnqueue_prev;
+.float spawnqueue_in;
+entity spawnqueue_first;
+entity spawnqueue_last;
+
+void Spawnqueue_Insert(entity e)
+{
+ if(e.spawnqueue_in)
+ return;
+ dprint(strcat("Into queue: ", e.netname, "\n"));
+ e.spawnqueue_in = TRUE;
+ e.spawnqueue_prev = spawnqueue_last;
+ e.spawnqueue_next = world;
+ if(spawnqueue_last)
+ spawnqueue_last.spawnqueue_next = e;
+ spawnqueue_last = e;
+ if(!spawnqueue_first)
+ spawnqueue_first = e;
+}
+
+void Spawnqueue_Remove(entity e)
+{
+ if(!e.spawnqueue_in)
+ return;
+ dprint(strcat("Out of queue: ", e.netname, "\n"));
+ e.spawnqueue_in = FALSE;
+ if(e == spawnqueue_first)
+ spawnqueue_first = e.spawnqueue_next;
+ if(e == spawnqueue_last)
+ spawnqueue_last = e.spawnqueue_prev;
+ if(e.spawnqueue_prev)
+ e.spawnqueue_prev.spawnqueue_next = e.spawnqueue_next;
+ if(e.spawnqueue_next)
+ e.spawnqueue_next.spawnqueue_prev = e.spawnqueue_prev;
+ e.spawnqueue_next = world;
+ e.spawnqueue_prev = world;
+}
+
+void Spawnqueue_Unmark(entity e)
+{
+ if(!e.spawned)
+ return;
+ e.spawned = FALSE;
+ numspawned = numspawned - 1;
+}
+
+void Spawnqueue_Mark(entity e)
+{
+ if(e.spawned)
+ return;
+ e.spawned = TRUE;
+ numspawned = numspawned + 1;
+}
+
+float Arena_CheckWinner()
+{
+ entity e;
+
+ if(round_handler_GetTimeLeft() <= 0)
+ {
+ FOR_EACH_REALCLIENT(e)
+ centerprint(e, "Round over, there's no winner");
+ bprint("Round over, there's no winner\n");
+ return 1;
+ }
+
+ if(numspawned > 1)
+ return 0;
+
+ entity champion;
+ champion = world;
+ FOR_EACH_CLIENT(e)
+ {
+ if(e.spawned && e.classname == "player")
+ champion = e;
+ }
+
+ if(champion)
+ {
+ FOR_EACH_REALCLIENT(e)
+ centerprint(e, strcat("The Champion is ", champion.netname));
+ bprint("The Champion is ", champion.netname, "\n");
+ UpdateFrags(champion, +1);
+ }
+ else
+ {
+ FOR_EACH_REALCLIENT(e)
+ centerprint(e, "Round tied");
+ bprint("Round tied\n");
+ }
+ return 1;
+}
+
+void Arena_AddChallengers()
+{
+ entity e;
+ e = self;
+ while(numspawned < maxspawned && spawnqueue_first)
+ {
+ self = spawnqueue_first;
+
+ bprint ("^4", self.netname, "^4 is the next challenger\n");
+
+ Spawnqueue_Remove(self);
+ Spawnqueue_Mark(self);
+
+ self.classname = "player";
+ PutClientInServer();
+ }
+ self = e;
+}
+
+float prev_numspawned;
+float Arena_CheckPlayers()
+{
+ entity e;
+
+ Arena_AddChallengers();
+
+ if(numspawned >= 2)
+ {
+ if(prev_numspawned != -1)
+ {
+ FOR_EACH_REALCLIENT(e)
+ Send_CSQC_Centerprint_Generic_Expire(e, CPID_WAITING_PLAYERS);
+ }
+ prev_numspawned = -1;
+ return 1;
+ }
+
+ if(prev_numspawned != numspawned && numspawned == 1)
+ {
+ FOR_EACH_REALCLIENT(e)
+ Send_CSQC_Centerprint_Generic(e, CPID_WAITING_PLAYERS, "Waiting for players to join...", -1, 0);
+ prev_numspawned = numspawned;
+ }
+
+ return 0;
+}
+
+void Arena_RoundStart()
+{
+ entity e;
+ FOR_EACH_PLAYER(e)
+ e.player_blocked = 0;
+}
+
+MUTATOR_HOOKFUNCTION(arena_ClientDisconnect)
+{
+ Spawnqueue_Unmark(self);
+ Spawnqueue_Remove(self);
+ return 1;
+}
+
+MUTATOR_HOOKFUNCTION(arena_reset_map_players)
+{
+ FOR_EACH_CLIENT(self)
+ {
+ if(self.spawned)
+ {
+ PutClientInServer();
+ self.player_blocked = 1;
+ }
+ else
+ PutObserverInServer();
+ }
+ return 1;
+}
+
+MUTATOR_HOOKFUNCTION(arena_MakePlayerObserver)
+{
+ if(self.version_mismatch)
+ {
+ self.frags = FRAGS_SPECTATOR;
+ Spawnqueue_Unmark(self);
+ Spawnqueue_Remove(self);
+ }
+ else
+ {
+ self.frags = FRAGS_LMS_LOSER;
+ Spawnqueue_Insert(self);
+ }
+ return 1;
+}
+
+MUTATOR_HOOKFUNCTION(arena_PutClientInServer)
+{
+ if(!self.spawned)
+ self.classname = "observer";
+ return 1;
+}
+
+MUTATOR_HOOKFUNCTION(arena_ClientConnect)
+{
+ self.classname = "observer";
+ Spawnqueue_Insert(self);
+ return 1;
+}
+
+MUTATOR_HOOKFUNCTION(arena_PlayerSpawn)
+{
+ Spawnqueue_Remove(self);
+ Spawnqueue_Mark(self);
+ if(arena_roundbased)
+ self.player_blocked = 1;
+ return 1;
+}
+
+MUTATOR_HOOKFUNCTION(arena_PlayerPreThink)
+{
+ self.stat_respawn_time = 0;
+
+ // put dead players in the spawn queue
+ if(arena_roundbased)
+ if(self.deadflag && time - self.death_time >= 1.5)
+ PutClientInServer();
+
+ return 1;
+}
+
+MUTATOR_HOOKFUNCTION(arena_ForbidPlayerScore_Clear)
+{
+ return 1;
+}
+
+MUTATOR_HOOKFUNCTION(arena_GiveFragsForKill)
+{
+ if(arena_roundbased)
+ frag_score = 0;
+ return 1;
+}
+
+MUTATOR_HOOKFUNCTION(arena_PlayerDies)
+{
+ Spawnqueue_Unmark(self);
+ return 1;
+}
+
+MUTATOR_HOOKFUNCTION(arena_SV_StartFrame)
+{
+ if(arena_roundbased) return 1;
+ if(time <= game_starttime) return 1;
+ if(gameover) return 1;
+
+ Arena_AddChallengers();
+ return 1;
+}
+
+void arena_Initialize()
+{
+ maxspawned = max(2, autocvar_g_arena_maxspawned);
+ arena_roundbased = autocvar_g_arena_roundbased;
+ if(arena_roundbased)
+ round_handler_Spawn(Arena_CheckPlayers, Arena_CheckWinner, Arena_RoundStart, 5, autocvar_g_arena_warmup, autocvar_g_arena_round_timelimit);
+}
+
+MUTATOR_DEFINITION(gamemode_arena)
+{
+ MUTATOR_HOOK(ClientDisconnect, arena_ClientDisconnect, CBC_ORDER_ANY);
+ MUTATOR_HOOK(reset_map_players, arena_reset_map_players, CBC_ORDER_ANY);
+ MUTATOR_HOOK(MakePlayerObserver, arena_MakePlayerObserver, CBC_ORDER_ANY);
+ MUTATOR_HOOK(PutClientInServer, arena_PutClientInServer, CBC_ORDER_ANY);
+ MUTATOR_HOOK(ClientConnect, arena_ClientConnect, CBC_ORDER_ANY);
+ MUTATOR_HOOK(PlayerSpawn, arena_PlayerSpawn, CBC_ORDER_ANY);
+ MUTATOR_HOOK(PlayerPreThink, arena_PlayerPreThink, CBC_ORDER_ANY);
+ MUTATOR_HOOK(ForbidPlayerScore_Clear, arena_ForbidPlayerScore_Clear, CBC_ORDER_ANY);
+ MUTATOR_HOOK(GiveFragsForKill, arena_GiveFragsForKill, CBC_ORDER_ANY);
+ MUTATOR_HOOK(PlayerDies, arena_PlayerDies, CBC_ORDER_ANY);
+ MUTATOR_HOOK(SV_StartFrame, arena_SV_StartFrame, CBC_ORDER_ANY);
+
+ MUTATOR_ONADD
+ {
+ if(time > 1) // game loads at time 1
+ error("This is a game type and it cannot be added at runtime.");
+ arena_Initialize();
+ }
+
+ MUTATOR_ONREMOVE
+ {
+ print("This is a game type and it cannot be removed at runtime.");
+ return -1;
+ }
+
+ return 0;
+}
--- /dev/null
+// should be removed in the future, as other code should not have to care
+float arena_roundbased;
--- /dev/null
+float total_players;
+float redalive, bluealive, yellowalive, pinkalive;
+.float redalive_stat, bluealive_stat, yellowalive_stat, pinkalive_stat;
+float ca_teams;
+float allowed_to_spawn;
+
+void CA_count_alive_players()
+{
+ entity e;
+ total_players = redalive = bluealive = yellowalive = pinkalive = 0;
+ FOR_EACH_PLAYER(e) {
+ if(e.team == COLOR_TEAM1)
+ {
+ ++total_players;
+ if (e.health >= 1) ++redalive;
+ }
+ else if(e.team == COLOR_TEAM2)
+ {
+ ++total_players;
+ if (e.health >= 1) ++bluealive;
+ }
+ else if(e.team == COLOR_TEAM3)
+ {
+ ++total_players;
+ if (e.health >= 1) ++yellowalive;
+ }
+ else if(e.team == COLOR_TEAM4)
+ {
+ ++total_players;
+ if (e.health >= 1) ++pinkalive;
+ }
+ }
+ FOR_EACH_REALCLIENT(e) {
+ e.redalive_stat = redalive;
+ e.bluealive_stat = bluealive;
+ e.yellowalive_stat = yellowalive;
+ e.pinkalive_stat = pinkalive;
+ }
+}
+
+float CA_GetWinnerTeam()
+{
+ float winner_team = 0;
+ if(redalive >= 1)
+ winner_team = COLOR_TEAM1;
+ if(bluealive >= 1)
+ {
+ if(winner_team) return 0;
+ winner_team = COLOR_TEAM2;
+ }
+ if(yellowalive >= 1)
+ {
+ if(winner_team) return 0;
+ winner_team = COLOR_TEAM3;
+ }
+ if(pinkalive >= 1)
+ {
+ if(winner_team) return 0;
+ winner_team = COLOR_TEAM4;
+ }
+ if(winner_team)
+ return winner_team;
+ return -1; // no player left
+}
+
+#define CA_ALIVE_TEAMS() ((redalive > 0) + (bluealive > 0) + (yellowalive > 0) + (pinkalive > 0))
+#define CA_ALIVE_TEAMS_OK() (CA_ALIVE_TEAMS() == ca_teams)
+float CA_CheckWinner()
+{
+ entity e;
+ if(round_handler_GetTimeLeft() <= 0)
+ {
+ FOR_EACH_REALCLIENT(e)
+ centerprint(e, "Round over, there's no winner");
+ bprint("Round over, there's no winner.\n");
+ allowed_to_spawn = TRUE;
+ return 1;
+ }
+
+ CA_count_alive_players();
+ if(CA_ALIVE_TEAMS() > 1)
+ return 0;
+
+ float winner_team;
+ string teamname;
+ winner_team = CA_GetWinnerTeam();
+ if(winner_team > 0)
+ {
+ teamname = ColoredTeamName(winner_team);
+ FOR_EACH_REALCLIENT(e)
+ centerprint(e, strcat(teamname, " wins the round"));
+ bprint(teamname, " wins the round.\n");
+ TeamScore_AddToTeam(winner_team, ST_SCORE, +1);
+ }
+ else if(winner_team == -1)
+ {
+ FOR_EACH_REALCLIENT(e)
+ centerprint(e, "Round tied");
+ bprint("Round tied.\n");
+ }
+
+ allowed_to_spawn = TRUE;
+ return 1;
+}
+
+void CA_RoundStart()
+{
+ if(inWarmupStage)
+ allowed_to_spawn = TRUE;
+ else
+ allowed_to_spawn = FALSE;
+}
+
+float prev_total_players;
+float CA_CheckTeams()
+{
+ entity e;
+ allowed_to_spawn = TRUE;
+ CA_count_alive_players();
+ if(CA_ALIVE_TEAMS_OK())
+ {
+ if(prev_total_players != -1)
+ {
+ FOR_EACH_REALCLIENT(e)
+ Send_CSQC_Centerprint_Generic_Expire(e, CPID_WAITING_PLAYERS);
+ }
+ prev_total_players = -1;
+ return 1;
+ }
+ if(prev_total_players != total_players)
+ {
+ string teams_missing = "";
+ if(!redalive) teams_missing = strcat(teams_missing, ColoredTeamName(COLOR_TEAM1), ", ");
+ if(!bluealive) teams_missing = strcat(teams_missing, ColoredTeamName(COLOR_TEAM2), ", ");
+ if(ca_teams >= 3)
+ if(!yellowalive) teams_missing = strcat(teams_missing, ColoredTeamName(COLOR_TEAM3), ", ");
+ if(ca_teams == 4)
+ if(!pinkalive) teams_missing = strcat(teams_missing, ColoredTeamName(COLOR_TEAM4), ", ");
+ teams_missing = substring(teams_missing, 0, strlen(teams_missing)-2);
+
+ FOR_EACH_REALCLIENT(e)
+ Send_CSQC_Centerprint_Generic(e, CPID_WAITING_PLAYERS, strcat("Waiting for players to join...\n\nNeed active players for: ", teams_missing), -1, 0);
+ prev_total_players = total_players;
+ }
+ return 0;
+}
+
+MUTATOR_HOOKFUNCTION(ca_PlayerSpawn)
+{
+ self.caplayer = TRUE;
+ return 1;
+}
+
+MUTATOR_HOOKFUNCTION(ca_PutClientInServer)
+{
+ if(clienttype(self) == CLIENTTYPE_BOT)
+ self.caplayer = TRUE;
+ if(!allowed_to_spawn)
+ self.classname = "observer";
+ return 1;
+}
+
+MUTATOR_HOOKFUNCTION(ca_reset_map_players)
+{
+ FOR_EACH_CLIENT(self)
+ {
+ if(self.caplayer)
+ {
+ self.classname = "player";
+ PutClientInServer();
+ }
+ }
+ return 1;
+}
+
+MUTATOR_HOOKFUNCTION(ca_ClientConnect)
+{
+ self.classname = "observer";
+ return 1;
+}
+
+MUTATOR_HOOKFUNCTION(ca_reset_map_global)
+{
+ allowed_to_spawn = TRUE;
+ return 1;
+}
+
+MUTATOR_HOOKFUNCTION(ca_GetTeamCount)
+{
+ ca_teams = autocvar_g_ca_teams_override;
+ if(ca_teams < 2)
+ ca_teams = autocvar_g_ca_teams;
+ ca_teams = bound(2, ca_teams, 4);
+ ret_float = ca_teams;
+ return 1;
+}
+
+MUTATOR_HOOKFUNCTION(ca_PlayerPreThink)
+{
+ if(!allowed_to_spawn)
+ self.stat_respawn_time = 0;
+ return 1;
+}
+
+MUTATOR_HOOKFUNCTION(ca_ForbidPlayerScore_Clear)
+{
+ return 1;
+}
+
+MUTATOR_HOOKFUNCTION(ca_MakePlayerObserver)
+{
+ if(self.caplayer)
+ self.frags = FRAGS_LMS_LOSER;
+ return 1;
+}
+
+MUTATOR_HOOKFUNCTION(ca_ForbidThrowCurrentWeapon)
+{
+ return 1;
+}
+
+void ca_Initialize()
+{
+ allowed_to_spawn = TRUE;
+
+ round_handler_Spawn(CA_CheckTeams, CA_CheckWinner, CA_RoundStart, 5, autocvar_g_ca_warmup, autocvar_g_ca_round_timelimit);
+
+ addstat(STAT_REDALIVE, AS_INT, redalive_stat);
+ addstat(STAT_BLUEALIVE, AS_INT, bluealive_stat);
+ addstat(STAT_YELLOWALIVE, AS_INT, yellowalive_stat);
+ addstat(STAT_PINKALIVE, AS_INT, pinkalive_stat);
+}
+
+MUTATOR_DEFINITION(gamemode_ca)
+{
+ MUTATOR_HOOK(PlayerSpawn, ca_PlayerSpawn, CBC_ORDER_ANY);
+ MUTATOR_HOOK(PutClientInServer, ca_PutClientInServer, CBC_ORDER_ANY);
+ MUTATOR_HOOK(MakePlayerObserver, ca_MakePlayerObserver, CBC_ORDER_ANY);
+ MUTATOR_HOOK(ClientConnect, ca_ClientConnect, CBC_ORDER_ANY);
+ MUTATOR_HOOK(reset_map_global, ca_reset_map_global, CBC_ORDER_ANY);
+ MUTATOR_HOOK(reset_map_players, ca_reset_map_players, CBC_ORDER_ANY);
+ MUTATOR_HOOK(GetTeamCount, ca_GetTeamCount, CBC_ORDER_EXCLUSIVE);
+ MUTATOR_HOOK(PlayerPreThink, ca_PlayerPreThink, CBC_ORDER_ANY);
+ MUTATOR_HOOK(ForbidPlayerScore_Clear, ca_ForbidPlayerScore_Clear, CBC_ORDER_ANY);
+ MUTATOR_HOOK(ForbidThrowCurrentWeapon, ca_ForbidThrowCurrentWeapon, CBC_ORDER_ANY);
+
+ MUTATOR_ONADD
+ {
+ if(time > 1) // game loads at time 1
+ error("This is a game type and it cannot be added at runtime.");
+ ca_Initialize();
+ }
+
+ MUTATOR_ONREMOVE
+ {
+ print("This is a game type and it cannot be removed at runtime.");
+ return -1;
+ }
+
+ return 0;
+}
--- /dev/null
+// should be removed in the future, as other code should not have to care
+.float caplayer;
+
+float freezetag_teams;
float freezetag_CheckTeams();
float freezetag_CheckWinner();
void freezetag_Initialize()
precache_model("models/ice/ice.md3");
ScoreRules_freezetag();
- round_handler_Spawn(freezetag_CheckTeams, freezetag_CheckWinner, 5, autocvar_g_freezetag_warmup);
+ round_handler_Spawn(freezetag_CheckTeams, freezetag_CheckWinner, func_null, 5, autocvar_g_freezetag_warmup, autocvar_g_freezetag_round_timelimit);
addstat(STAT_REDALIVE, AS_INT, redalive_stat);
addstat(STAT_BLUEALIVE, AS_INT, bluealive_stat);
e.pinkalive_stat = pinkalive;
}
}
-
-float freezetag_TeamsCanPlay()
-{
- if((redalive >= 1 && bluealive >= 1)
- || (redalive >= 1 && yellowalive >= 1)
- || (redalive >= 1 && pinkalive >= 1)
- || (bluealive >= 1 && yellowalive >= 1)
- || (bluealive >= 1 && pinkalive >= 1)
- || (yellowalive >= 1 && pinkalive >= 1))
- return 1; // we still have active players on two or more teams, nobody won yet
- return 0;
-}
+#define FREEZETAG_ALIVE_TEAMS() ((redalive > 0) + (bluealive > 0) + (yellowalive > 0) + (pinkalive > 0))
+#define FREEZETAG_ALIVE_TEAMS_OK() (FREEZETAG_ALIVE_TEAMS() == freezetag_teams)
float prev_total_players;
float freezetag_CheckTeams()
{
entity e;
- if(freezetag_TeamsCanPlay())
+ if(FREEZETAG_ALIVE_TEAMS_OK())
{
if(prev_total_players != -1)
{
string teams_missing = "";
if(!redalive) teams_missing = strcat(teams_missing, ColoredTeamName(COLOR_TEAM1), ", ");
if(!bluealive) teams_missing = strcat(teams_missing, ColoredTeamName(COLOR_TEAM2), ", ");
+ if(freezetag_teams >= 3)
+ if(!yellowalive) teams_missing = strcat(teams_missing, ColoredTeamName(COLOR_TEAM3), ", ");
+ if(freezetag_teams == 4)
+ if(!pinkalive) teams_missing = strcat(teams_missing, ColoredTeamName(COLOR_TEAM4), ", ");
teams_missing = substring(teams_missing, 0, strlen(teams_missing)-2);
FOR_EACH_REALCLIENT(e)
}
return 0;
}
-float freezetag_CheckWinner()
-{
- if(freezetag_TeamsCanPlay())
- return 0;
- entity e, winner;
- string teamname;
- winner = world;
+float freezetag_getWinnerTeam()
+{
+ float winner_team = 0;
+ if(redalive >= 1)
+ winner_team = COLOR_TEAM1;
+ if(bluealive >= 1)
+ {
+ if(winner_team) return 0;
+ winner_team = COLOR_TEAM2;
+ }
+ if(yellowalive >= 1)
+ {
+ if(winner_team) return 0;
+ winner_team = COLOR_TEAM3;
+ }
+ if(pinkalive >= 1)
+ {
+ if(winner_team) return 0;
+ winner_team = COLOR_TEAM4;
+ }
+ if(winner_team)
+ return winner_team;
+ return -1; // no player left
+}
- FOR_EACH_PLAYER(e)
+float freezetag_CheckWinner()
+{
+ entity e;
+ if(round_handler_GetTimeLeft() <= 0)
{
- if(e.freezetag_frozen == 0 && e.health >= 1) // here's one player from the winning team... good
- {
- winner = e;
- break; // break, we found the winner
- }
+ FOR_EACH_REALCLIENT(e)
+ centerprint(e, "Round over, there's no winner");
+ bprint("Round over, there's no winner.\n");
+ return 1;
}
- if(winner != world) // just in case a winner wasn't found
+ if(FREEZETAG_ALIVE_TEAMS() > 1)
+ return 0;
+
+ float winner_team;
+ string teamname;
+ winner_team = freezetag_getWinnerTeam();
+ if(winner_team > 0)
{
- teamname = ColoredTeamName(winner.team);
+ teamname = ColoredTeamName(winner_team);
FOR_EACH_REALCLIENT(e)
centerprint(e, strcat(teamname, "^5 wins the round, all other teams were frozen."));
bprint(teamname, "^5 wins the round since all the other teams were frozen.\n");
- TeamScore_AddToTeam(winner.team, ST_SCORE, +1);
+ TeamScore_AddToTeam(winner_team, ST_SCORE, +1);
+ }
+ else if(winner_team == -1)
+ {
+ FOR_EACH_REALCLIENT(e)
+ centerprint(e, "^5Round tied! All teams were frozen.");
+ bprint("^5Round tied! All teams were frozen.\n");
}
return 1;
return 0;
}
+MUTATOR_HOOKFUNCTION(freezetag_GetTeamCount)
+{
+ freezetag_teams = autocvar_g_freezetag_teams_override;
+ if(freezetag_teams < 2)
+ freezetag_teams = autocvar_g_freezetag_teams;
+ freezetag_teams = bound(2, freezetag_teams, 4);
+ ret_float = freezetag_teams;
+ return 0;
+}
+
MUTATOR_DEFINITION(gamemode_freezetag)
{
MUTATOR_HOOK(MakePlayerObserver, freezetag_RemovePlayer, CBC_ORDER_ANY);
MUTATOR_HOOK(ForbidThrowCurrentWeapon, freezetag_ForbidThrowCurrentWeapon, CBC_ORDER_ANY);
MUTATOR_HOOK(HavocBot_ChooseRule, freezetag_BotRoles, CBC_ORDER_ANY);
MUTATOR_HOOK(SpectateCopy, freezetag_SpectateCopy, CBC_ORDER_ANY);
+ MUTATOR_HOOK(GetTeamCount, freezetag_GetTeamCount, CBC_ORDER_EXCLUSIVE);
MUTATOR_ONADD
{
.entity kh_next;
float kh_Key_AllOwnedByWhichTeam();
-// used by arena.qc ready-restart:
typedef void(void) kh_Think_t;
void kh_StartRound();
void kh_Controller_SetThink_NoMsg(float t, kh_Think_t func);
MUTATOR_HOOKFUNCTION(superspec_ClientConnect)
{
+ if(clienttype(self) != CLIENTTYPE_REAL)
+ return FALSE;
+
string fn = "superspec-local.options";
float fh;
+MUTATOR_DECLARATION(gamemode_arena);
+MUTATOR_DECLARATION(gamemode_ca);
MUTATOR_DECLARATION(gamemode_keyhunt);
MUTATOR_DECLARATION(gamemode_freezetag);
MUTATOR_DECLARATION(gamemode_keepaway);
mutators/base.qh
mutators/mutators.qh
+mutators/gamemode_arena.qh
+mutators/gamemode_ca.qh
mutators/gamemode_ctf.qh
mutators/gamemode_domination.qh
mutators/gamemode_keyhunt.qh // TODO fix this
g_tetris.qc
runematch.qc
-arena.qc
g_violence.qc
g_damage.qc
../common/explosion_equation.qc
mutators/base.qc
+mutators/gamemode_arena.qc
+mutators/gamemode_ca.qc
mutators/gamemode_ctf.qc
mutators/gamemode_domination.qc
mutators/gamemode_freezetag.qc
void round_handler_Think()
{
- entity e;
float f;
- if(time <= game_starttime)
+ if(time < game_starttime)
{
- round_handler_Reset(game_starttime + 1);
+ round_handler_Reset(game_starttime);
return;
}
reset_map(TRUE);
self.wait = FALSE;
self.cnt = self.count + 1; // init countdown
+ round_starttime = time + self.count;
}
if(self.cnt > 0) // countdown running
{
if(self.canRoundStart())
{
+ if(self.cnt == self.count + 1)
+ round_starttime = time + self.count;
f = self.cnt - 1;
- if(f == 5) Announce("prepareforbattle");
- else if(f == 3) Announce("3");
- else if(f == 2) Announce("2");
- else if(f == 1) Announce("1");
- else if(f == 0)
+ if(f == 0)
{
- Announce("begin");
- FOR_EACH_REALCLIENT(e)
- Send_CSQC_Centerprint_Generic(e, CPID_ROUND_STARTING, "^1Begin!", 1, 0);
self.cnt = 0;
+ self.round_endtime = time + self.round_timelimit;
self.nextthink = time;
+ if(self.roundStart)
+ self.roundStart();
return;
}
-
- FOR_EACH_REALCLIENT(e)
- Send_CSQC_Centerprint_Generic(e, CPID_ROUND_STARTING, "Round will start in %d", 1, f);
self.cnt = self.cnt - 1;
}
else
}
}
-void round_handler_Spawn(float() canRoundStart_func, float() canRoundEnd_func, float the_delay, float the_count)
+void round_handler_Spawn(float() canRoundStart_func, float() canRoundEnd_func, void() roundStart_func, float the_delay, float the_count, float the_round_timelimit)
{
if(round_handler)
{
round_handler.think = round_handler_Think;
round_handler.canRoundStart = canRoundStart_func;
round_handler.canRoundEnd = canRoundEnd_func;
+ round_handler.roundStart = roundStart_func;
round_handler.delay = (the_delay > 0) ? the_delay : 0;
round_handler.count = fabs(floor(the_count));
round_handler.wait = FALSE;
round_handler.cnt = round_handler.count + 1;
- round_handler.nextthink = max(time, game_starttime + 1);
+ round_handler.round_timelimit = the_round_timelimit;
+ // if round_handler spawns at time 1 gamestarttime isn't initialized yet
+ //round_handler.nextthink = max(time, game_starttime + 1);
+ round_handler.nextthink = time;
+ round_starttime = time + round_handler.count;
}
float round_handler_IsActive()
return (!round_handler.wait && !round_handler.cnt);
}
+float round_handler_GetTimeLeft()
+{
+ return (round_handler.round_endtime - time);
+}
+
void round_handler_Reset(float next_think)
{
- entity e;
round_handler.wait = FALSE;
if(round_handler.count)
if(round_handler.cnt < round_handler.count + 1)
- {
- FOR_EACH_REALCLIENT(e)
- Send_CSQC_Centerprint_Generic_Expire(e, CPID_ROUND_STARTING);
round_handler.cnt = round_handler.count + 1;
- }
round_handler.nextthink = next_think;
+ round_starttime = (next_think) ? (next_think + round_handler.count) : -1;
}
void round_handler_Remove()
.float wait; // it's set to TRUE when round ends, to FALSE when countdown starts
.float cnt; // its initial value is .count + 1, then decreased while counting down
// reaches 0 when the round starts
+.float round_timelimit;
+.float round_endtime;
.float() canRoundStart;
.float() canRoundEnd;
+.void() roundStart;
-void round_handler_Spawn(float() canRoundStart_func, float() canRoundEnd_func, float the_delay, float the_count);
+void round_handler_Spawn(float() canRoundStart_func, float() canRoundEnd_func, void() roundStart_func, float the_delay, float the_count, float the_round_timelimit);
float round_handler_IsActive();
float round_handler_AwaitingNextRound();
float round_handler_CountdownRunning();
float round_handler_IsRoundStarted();
+float round_handler_GetTimeLeft();
void round_handler_Reset(float next_think);
void round_handler_Remove();
if(teamscores_entities_count)
return 0;
+ if(MUTATOR_CALLHOOK(ForbidPlayerScore_Clear)) return 0;
if(g_lms) return 0;
- if(g_arena || g_ca) return 0;
if(g_cts) return 0; // in CTS, you don't lose score by observing
if(g_race && g_race_qualifying) return 0; // in qualifying, you don't lose score by observing
float game_delay;
float game_delay_last;
-void Arena_Main();
void RuneMatchGivePoints();
float RedirectionThink();
entity SelectSpawnPoint (float anypoint);
return;
}
- Arena_Main();
-
CreatureFrame ();
CheckRules_World ();
{
fraglimit_override = autocvar_g_arena_point_limit;
leadlimit_override = autocvar_g_arena_point_leadlimit;
- maxspawned = autocvar_g_arena_maxspawned;
- if(maxspawned < 2)
- maxspawned = 2;
- arena_roundbased = autocvar_g_arena_roundbased;
+ MUTATOR_ADD(gamemode_arena);
}
if(g_ca)
ActivateTeamplay();
fraglimit_override = autocvar_g_ca_point_limit;
leadlimit_override = autocvar_g_ca_point_leadlimit;
- allowed_to_spawn = TRUE;
- precache_sound("ctf/red_capture.wav");
- precache_sound("ctf/blue_capture.wav");
+ MUTATOR_ADD(gamemode_ca);
}
+
if(g_keyhunt)
{
ActivateTeamplay();