]> de.git.xonotic.org Git - xonotic/xonotic-data.pk3dir.git/commitdiff
Merge branch 'terencehill/ca_arena_freezetag_bugfixes' into terencehill/arena_gamemode
authorterencehill <piuntn@gmail.com>
Tue, 5 Feb 2013 22:55:07 +0000 (23:55 +0100)
committerterencehill <piuntn@gmail.com>
Tue, 5 Feb 2013 23:02:23 +0000 (00:02 +0100)
Conflicts:
qcsrc/server/cl_client.qc
qcsrc/server/scores.qc

39 files changed:
_hud_descriptions.cfg
gamemodes.cfg
gfx/hud/default/player_pink.tga [new file with mode: 0644]
gfx/hud/default/player_yellow.tga [new file with mode: 0644]
hud_luminos.cfg
hud_luminos_minimal.cfg
hud_luminos_minimal_xhair.cfg
hud_luminos_old.cfg
hud_nexuiz.cfg
qcsrc/client/autocvars.qh
qcsrc/client/hud.qc
qcsrc/client/hud_config.qc
qcsrc/server/arena.qc [deleted file]
qcsrc/server/autocvars.qh
qcsrc/server/cl_client.qc
qcsrc/server/cl_player.qc
qcsrc/server/cl_weapons.qc
qcsrc/server/command/cmd.qc
qcsrc/server/command/vote.qc
qcsrc/server/command/vote.qh
qcsrc/server/defs.qh
qcsrc/server/g_damage.qc
qcsrc/server/g_hook.qc
qcsrc/server/g_world.qc
qcsrc/server/mutators/base.qh
qcsrc/server/mutators/gamemode_arena.qc [new file with mode: 0644]
qcsrc/server/mutators/gamemode_arena.qh [new file with mode: 0644]
qcsrc/server/mutators/gamemode_ca.qc [new file with mode: 0644]
qcsrc/server/mutators/gamemode_ca.qh [new file with mode: 0644]
qcsrc/server/mutators/gamemode_freezetag.qc
qcsrc/server/mutators/gamemode_keyhunt.qh
qcsrc/server/mutators/mutator_superspec.qc
qcsrc/server/mutators/mutators.qh
qcsrc/server/progs.src
qcsrc/server/round_handler.qc
qcsrc/server/round_handler.qh
qcsrc/server/scores.qc
qcsrc/server/sv_main.qc
qcsrc/server/teamplay.qc

index 4c832ef1e9b9a6208be14c874f302a93528ff300..c77be4eed700bf1110182115e89710db74684b3b 100644 (file)
@@ -198,7 +198,9 @@ seta hud_panel_modicons_bg_color_team "" "override panel color with team color i
 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"
index 6cff7e5a42666ab32954c8ec8d5abb757b1a09f6..752787536e9010edc0251a2bd9c2ac26be054fbf 100644 (file)
@@ -149,6 +149,7 @@ set g_ft_weapon_stay 0
 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"
 
 
@@ -168,6 +169,9 @@ set g_ca_spectate_enemies 0 "Allow spectating enemy player by dead player during
 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
+
 
 
 // ==================
@@ -282,7 +286,10 @@ seta g_freezetag_point_leadlimit -1        "Freeze Tag point lead limit overriding the
 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
 
 
 // ==========
diff --git a/gfx/hud/default/player_pink.tga b/gfx/hud/default/player_pink.tga
new file mode 100644 (file)
index 0000000..89a7659
Binary files /dev/null and b/gfx/hud/default/player_pink.tga differ
diff --git a/gfx/hud/default/player_yellow.tga b/gfx/hud/default/player_yellow.tga
new file mode 100644 (file)
index 0000000..8717d8f
Binary files /dev/null and b/gfx/hud/default/player_yellow.tga differ
index c70e1b103a79bfe75504c9c8a86c4696b28f0387..862b8ccf982e958fa25bfec56d557a7ae6e01625 100644 (file)
@@ -196,7 +196,9 @@ seta hud_panel_modicons_bg_color_team ""
 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"
index 4c0a7f46d06ad9159dce70b715d982dfd8abb5b1..e344cbb27a8bbc2b52744ffa64fe868b2a393857 100644 (file)
@@ -196,7 +196,9 @@ seta hud_panel_modicons_bg_color_team ""
 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"
index 56ec31c8091c5566957fcb7f2112cd9f8ef7a398..df652104cf3aed52d45d189450adfbd7a08738bf 100644 (file)
@@ -196,7 +196,9 @@ seta hud_panel_modicons_bg_color_team ""
 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"
index 263412734311e3e73c1015f5b90596674257da7c..f7bdb61f99d41b02b7e245f0c14f760c6dfe300f 100644 (file)
@@ -196,7 +196,9 @@ seta hud_panel_modicons_bg_color_team ""
 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"
index 42a0571bb3d96f3d92e7c87ff965f60ff982177c..058f2749e10e5f85ff553252179667d097141512 100644 (file)
@@ -196,7 +196,9 @@ seta hud_panel_modicons_bg_color_team ""
 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"
index 94cd1a5c03bb4b03450aa61a3facea5e0e874145..ef9a3125c4ea1bcf11da5f0e9cf54130ffb6ece3 100644 (file)
@@ -246,7 +246,9 @@ float autocvar_hud_panel_healtharmor_text;
 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;
index cc870794b10653a3dea145058a83693ab5282e3b..095b0978b50c2ed09eb38fd34bd736d40f1a037e 100644 (file)
@@ -3297,32 +3297,93 @@ void HUD_VoteWindow(void)
 
 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;
+       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;
+               case 3:
+                       stat = getstati(STAT_PINKALIVE);
+                       pic = "player_pink.tga";
+                       color = '1 0 1';
+       }
 
-       vector redpos, bluepos;
-       if(mySize_x > mySize_y)
+       if(mySize_x/mySize_y > aspect_ratio)
        {
-               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);
+               i = aspect_ratio * mySize_y;
+               myPos_x = myPos_x + (mySize_x - i) / 2;
+               mySize_x = i;
        }
        else
        {
-               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);
+               i = 1/aspect_ratio * mySize_x;
+               myPos_y = myPos_y + (mySize_y - i) / 2;
+               mySize_y = i;
+       }
+
+       if(layout)
+       {
+               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;
+       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, column;
+       for(i=0; i<teams_count; ++i)
+       {
+               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;
+               }
        }
 }
 
index acff2892da506f53bdef49d0bb8f77baa73735ea..d21234ccecaf6bf868dd0ba786f0b2497dfe6b68 100644 (file)
@@ -139,7 +139,9 @@ void HUD_Panel_ExportCfg(string cfgname)
                                        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");
diff --git a/qcsrc/server/arena.qc b/qcsrc/server/arena.qc
deleted file mode 100644 (file)
index 14caab8..0000000
+++ /dev/null
@@ -1,448 +0,0 @@
-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();
-}
-
index cc0e48cc5512fb6ff96d7233e511d84a9fedf2a1..c40419b57a724d2b1dc0d68b895a3f384dabc254 100644 (file)
@@ -75,6 +75,7 @@ float autocvar_g_arena_maxspawned;
 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;
@@ -743,6 +744,8 @@ float autocvar_g_ca_point_leadlimit;
 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")
@@ -854,6 +857,9 @@ float autocvar_g_freezetag_point_limit;
 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")
index ae40c4e1ef2025139a9714db695065858dc89430..6493069fa9518b5000347135ca71998e814994b3 100644 (file)
@@ -293,7 +293,7 @@ entity SelectSpawnPoint (float anypoint)
        else
        {
                float mindist;
-               if (arena_roundbased && !g_ca)
+               if (g_arena && arena_roundbased)
                        mindist = 800;
                else
                        mindist = 100;
@@ -401,6 +401,24 @@ void PutObserverInServer (void)
                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);
 
@@ -507,45 +525,6 @@ void PutObserverInServer (void)
        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;
@@ -653,11 +632,7 @@ Called when a client spawns in the server
 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;
@@ -676,13 +651,12 @@ void PutClientInServer (void)
                        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;
 
@@ -842,14 +816,6 @@ void PutClientInServer (void)
                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;
@@ -1263,19 +1229,12 @@ void ClientKill_TeamChange (float targetteam) // 0 = don't change, -1 = auto, -2
 
 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
@@ -1511,13 +1470,6 @@ void ClientConnect (void)
        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();
@@ -1591,13 +1543,11 @@ void ClientConnect (void)
        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);
 }
 /*
 =============
@@ -1665,12 +1615,6 @@ void ClientDisconnect (void)
 
        bot_relinkplayerlist();
 
-       if(g_arena)
-       {
-               Spawnqueue_Unmark(self);
-               Spawnqueue_Remove(self);
-       }
-
        accuracy_free(self);
        ClientData_Detach();
        PlayerScore_Detach(self);
@@ -2560,10 +2504,7 @@ void PlayerPreThink (void)
        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)
        {
index be700c87a4545650bb6a08a6df67e0173e401de4..e564c3ac4f9e3f590be53b2d995d1dba807255d7 100644 (file)
@@ -439,9 +439,6 @@ void PlayerDamage (entity inflictor, entity attacker, float damage, float deatht
        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);
 
@@ -709,9 +706,6 @@ void PlayerDamage (entity inflictor, entity attacker, float damage, float deatht
                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")
index b78d22cec213532745cc66c66803e4c243c6fcc2..cdc6ea36d088fa90f23c5806a4b4bb9629af7ad6 100644 (file)
@@ -313,8 +313,6 @@ float W_IsWeaponThrowable(float w)
                return 0;
        if (g_lms)
                return 0;
-       if (g_ca)
-               return 0;
        if (g_cts)
                return 0;
        if (g_nexball && w == WEP_GRENADE_LAUNCHER)
@@ -375,12 +373,15 @@ void W_WeaponFrame()
        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;
 
index 530646afd2ec8fbb105dc8a080b41dadaaa25d0f..dea46bbcbebbeb26f68b7ffe1da311081bb4223d 100644 (file)
@@ -154,9 +154,8 @@ void ClientCommand_join(float request)
                                {
                                        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");
index 329fb9d4ef98c1a4d8d4ee0c30b8b15e9625f701..45e15c95f62dd3441189ed9e85cf78c2e9bf42c8 100644 (file)
@@ -319,6 +319,84 @@ void VoteThink()
 //  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() 
 {
index 748b7ce6d99bb7fb5a68a3888917898b0171a263..1225b6be582cecb5feb9ecb6a645d1fd161d0ab6 100644 (file)
@@ -47,4 +47,5 @@ float readycount; // amount of players who are ready
 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
index 401cc786661b0835d5f6c3686f9837218e472ce0..90701b588431b28b988345fcb4284a0f3b775cd9 100644 (file)
@@ -633,8 +633,6 @@ string deathmessage;
 
 .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
index a3eeefb73033ce71cfa2532ce575378af314904e..13cbab046785fd62d4eae33b97d0d26ad9f7fdb5 100644 (file)
@@ -131,10 +131,6 @@ void GiveFrags (entity attacker, entity targ, float f, float deathtype)
 
        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)
        {
index f7af47d924036f121a6b8340a8dc2df5a0edaee4..83366b6422adee05de501fbe89a8c7581cd07bcb 100644 (file)
@@ -299,14 +299,10 @@ void FireGrapplingHook (void)
        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);
 
index c70f7b84c569b7cbbb1a5cdc18890eb68509cf58..4a575e75ba9c39faa40ee9f21cd7e5c2df4411d3 100644 (file)
@@ -806,12 +806,6 @@ void spawnfunc_worldspawn (void)
 
        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);
index a9e180c5968008da2b5a713c1e001bc2c06f5539..d3d91232cf94c30fdacb9003eddea5167a816da4 100644 (file)
@@ -46,6 +46,9 @@ void Mutator_Remove(mutatorfunc_t func, string name); // calls error() on fail
 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)
@@ -56,6 +59,9 @@ MUTATOR_HOOKABLE(reset_map_global);
 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
 
diff --git a/qcsrc/server/mutators/gamemode_arena.qc b/qcsrc/server/mutators/gamemode_arena.qc
new file mode 100644 (file)
index 0000000..cc93dd3
--- /dev/null
@@ -0,0 +1,285 @@
+.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)
+{
+       self.frags = FRAGS_PLAYER;
+       if(self.version_mismatch)
+       {
+               Spawnqueue_Unmark(self);
+               Spawnqueue_Remove(self);
+       }
+       else
+               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
+       {
+               error("This is a game type and it cannot be removed at runtime.");
+       }
+
+       return 0;
+}
diff --git a/qcsrc/server/mutators/gamemode_arena.qh b/qcsrc/server/mutators/gamemode_arena.qh
new file mode 100644 (file)
index 0000000..a2f623a
--- /dev/null
@@ -0,0 +1,2 @@
+// should be removed in the future, as other code should not have to care
+float arena_roundbased;
diff --git a/qcsrc/server/mutators/gamemode_ca.qc b/qcsrc/server/mutators/gamemode_ca.qc
new file mode 100644 (file)
index 0000000..1a27b11
--- /dev/null
@@ -0,0 +1,260 @@
+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;
+       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
+       {
+               error("This is a game type and it cannot be removed at runtime.");
+       }
+
+       return 0;
+}
diff --git a/qcsrc/server/mutators/gamemode_ca.qh b/qcsrc/server/mutators/gamemode_ca.qh
new file mode 100644 (file)
index 0000000..54df1a4
--- /dev/null
@@ -0,0 +1,3 @@
+// should be removed in the future, as other code should not have to care
+.float caplayer;
+
index 3b385e0632979eed463e5ae4a0810769e8a400cc..e29079b62c38eec377917e22317e527923877ee1 100644 (file)
@@ -1,3 +1,4 @@
+float freezetag_teams;
 float freezetag_CheckTeams();
 float freezetag_CheckWinner();
 void freezetag_Initialize()
@@ -5,7 +6,7 @@ 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);
@@ -49,24 +50,14 @@ void freezetag_count_alive_players()
                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)
                {
@@ -81,6 +72,10 @@ float freezetag_CheckTeams()
                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)
@@ -89,31 +84,62 @@ float freezetag_CheckTeams()
        }
        return 0;
 }
-float freezetag_CheckWinner()
-{
-       if(freezetag_TeamsCanPlay())
-               return 0;
 
-       entity e, winner;
-       string teamname;
-       winner = world;
+float freezetag_getWinnerTeam()
+{
+       float winner_team;
+       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;
@@ -570,6 +596,16 @@ MUTATOR_HOOKFUNCTION(freezetag_SpectateCopy)
        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);
@@ -584,6 +620,7 @@ MUTATOR_DEFINITION(gamemode_freezetag)
        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
        {
index 8ee8434293669b00a15501c92612f7c1d3eedf51..55614ff8effba562ed5f9fa21120caed6fc9188a 100644 (file)
@@ -6,7 +6,6 @@ float kh_tracking_enabled;
 .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);
index 0645b4805446b17cd1baafe035225d4b4793fceb..708db15b631d6b993ad259f4da482708256f45d6 100644 (file)
@@ -443,6 +443,9 @@ void superspec_hello()
 
 MUTATOR_HOOKFUNCTION(superspec_ClientConnect)
 {
+       if(clienttype(self) != CLIENTTYPE_REAL)
+               return FALSE;
+
        string fn = "superspec-local.options";
        float fh;
 
index 7fcfd9883bb9195f24b7f513ffc1e8189d03c014..bca29eab8e327934d068371bb60c7c50081f3982 100644 (file)
@@ -1,3 +1,5 @@
+MUTATOR_DECLARATION(gamemode_arena);
+MUTATOR_DECLARATION(gamemode_ca);
 MUTATOR_DECLARATION(gamemode_keyhunt);
 MUTATOR_DECLARATION(gamemode_freezetag);
 MUTATOR_DECLARATION(gamemode_keepaway);
index 0a27d68b90eaacb701f998d6b460033ab6cf149c..7c3f23e3e7688efa2129a0bb31501aa1905ac722 100644 (file)
@@ -29,6 +29,8 @@ defs.qh               // Should rename this, it has fields and globals
 
 mutators/base.qh
 mutators/mutators.qh
+mutators/gamemode_arena.qh
+mutators/gamemode_ca.qh
 mutators/gamemode_ctf.qh
 mutators/gamemode_keyhunt.qh // TODO fix this
 mutators/gamemode_keepaway.qh
@@ -97,7 +99,6 @@ g_subs.qc
 g_tetris.qc
 
 runematch.qc
-arena.qc
 
 g_violence.qc
 g_damage.qc
@@ -211,6 +212,8 @@ round_handler.qc
 ../common/explosion_equation.qc
 
 mutators/base.qc
+mutators/gamemode_arena.qc
+mutators/gamemode_ca.qc
 mutators/gamemode_ctf.qc
 mutators/gamemode_freezetag.qc
 mutators/gamemode_keyhunt.qc
index 2bac357fba292ee5cc6f11538022c83ce893860c..d94d9b9cd4104a307c896cfdf1e91d953097f6f7 100644 (file)
@@ -38,7 +38,10 @@ void round_handler_Think()
                                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 != func_null)
+                                       self.roundStart();
                                return;
                        }
 
@@ -67,7 +70,7 @@ void round_handler_Think()
        }
 }
 
-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)
        {
@@ -80,10 +83,12 @@ void round_handler_Spawn(float() canRoundStart_func, float() canRoundEnd_func, f
        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.round_timelimit = the_round_timelimit;
        round_handler.nextthink = max(time, game_starttime + 1);
 }
 
@@ -107,6 +112,11 @@ float round_handler_IsRoundStarted()
        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;
index 55026fa84401565d9aca334ee75021c6d07b87d9..1d3ea773364fa5ffc4c8cefa02065ba79029b114 100644 (file)
@@ -4,14 +4,18 @@ entity round_handler;
 .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();
 
index 0df5b69af06fbc09c3809ddca9604a3d51a0f412..2a0a58a51d6a092a02e48a2c7f5a2618f508d664 100644 (file)
@@ -256,8 +256,8 @@ float PlayerScore_Clear(entity player)
        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
 
index 171a4ae9b7d9505e52f6faf3a77a880638a77663..a6c28a5c0988d2fda7ba18ae8efc8d071d95b863 100644 (file)
@@ -153,7 +153,6 @@ Called before each frame by the server
 float game_delay;
 float game_delay_last;
 
-void Arena_Main();
 void RuneMatchGivePoints();
 float RedirectionThink();
 entity SelectSpawnPoint (float anypoint);
@@ -230,8 +229,6 @@ void StartFrame (void)
                return;
        }
 
-       Arena_Main();
-
        CreatureFrame ();
        CheckRules_World ();
 
index 61b9f06fdc5064127c14e19d5c408ac3adcaad6b..48ae80951118f938d06b6b29543f5286a1bad1d7 100644 (file)
@@ -170,10 +170,7 @@ void InitGameplayMode()
        {
                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)
@@ -181,10 +178,9 @@ void InitGameplayMode()
                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();