Merge remote-tracking branch 'origin/master' into terencehill/ca_arena_mutators
authorSamual Lenks <samual@xonotic.org>
Tue, 7 May 2013 05:13:01 +0000 (01:13 -0400)
committerSamual Lenks <samual@xonotic.org>
Tue, 7 May 2013 05:13:01 +0000 (01:13 -0400)
52 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/View.qc
qcsrc/client/announcer.qc
qcsrc/client/autocvars.qh
qcsrc/client/hud.qc
qcsrc/client/hud_config.qc
qcsrc/common/constants.qh
qcsrc/common/mapinfo.qc
qcsrc/common/notifications.qh
qcsrc/menu/xonotic/dialog_multiplayer_create_advanced.c
qcsrc/server/arena.qc [deleted file]
qcsrc/server/autocvars.qh
qcsrc/server/bot/aim.qc
qcsrc/server/bot/bot.qc
qcsrc/server/cl_client.qc
qcsrc/server/cl_impulse.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/constants.qh
qcsrc/server/defs.qh
qcsrc/server/g_damage.qc
qcsrc/server/g_hook.qc
qcsrc/server/g_world.qc
qcsrc/server/miscfunctions.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.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 [new file with mode: 0644]
qcsrc/server/round_handler.qh [new file with mode: 0644]
qcsrc/server/scores.qc
qcsrc/server/sv_main.qc
qcsrc/server/teamplay.qc
qcsrc/server/w_minstanex.qc

index ea7552a..9c7201d 100644 (file)
@@ -199,7 +199,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 af8d3e8..6913ac6 100644 (file)
@@ -142,6 +142,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"
 
 
@@ -161,6 +162,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
+
 
 
 // ==================
@@ -268,13 +272,17 @@ set g_domination_point_glow               0 "domination point glow (warning, slow)"
 //  freezetag
 // ===========
 set g_freezetag 0 "Freeze Tag: Freeze the opposing team(s) to win, unfreeze teammates by standing next to them"
-seta g_freezetag_warmup 5 "Time players get to run around before the round starts"
+set g_freezetag_warmup 5 "Time players get to run around before the round starts"
 seta g_freezetag_point_limit -1        "Freeze Tag point limit overriding the mapinfo specified one (use 0 to play without limit, and -1 to use the mapinfo's limit)"
 seta g_freezetag_point_leadlimit -1    "Freeze Tag point lead limit overriding the mapinfo specified one (use 0 to play without limit, and -1 to use the mapinfo's limit)"
-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"
-seta g_freezetag_frozen_force 0.6 "How much to multiply the force on a frozen player with"
+set g_freezetag_revive_speed 0.4 "Speed for reviving a frozen teammate"
+set g_freezetag_revive_clearspeed 1.6 "Speed at which reviving progress gets lost when out of range"
+set 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
+set g_freezetag_frozen_force 0.6 "How much to multiply the force on a frozen player with"
+set g_freezetag_frozen_maxtime 60 "frozen players will be automatically unfrozen after this time in seconds"
+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 bb0c97e..f99494a 100644 (file)
@@ -197,7 +197,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 775ddaf..bfa0c0b 100644 (file)
@@ -197,7 +197,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 b7a208d..27ca9ab 100644 (file)
@@ -197,7 +197,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 2e718b2..3a489a0 100644 (file)
@@ -197,7 +197,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 d4e71d8..5705341 100644 (file)
@@ -197,7 +197,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 547f902..d27fd4d 100644 (file)
@@ -895,8 +895,8 @@ void CSQC_UpdateView(float w, float h)
                        }
                }
        }
-       
-       if(autocvar_hud_damage)
+
+       if(autocvar_hud_damage && !getstati(STAT_FROZEN))
        {
                splash_size_x = max(vid_conwidth, vid_conheight);
                splash_size_y = max(vid_conwidth, vid_conheight);
@@ -1080,7 +1080,7 @@ void CSQC_UpdateView(float w, float h)
                        if(getstatf(STAT_REVIVE_PROGRESS))
                        {
                                DrawCircleClippedPic(eX * 0.5 * vid_conwidth + eY * 0.6 * vid_conheight, 0.1 * vid_conheight, "gfx/crosshair_ring.tga", getstatf(STAT_REVIVE_PROGRESS), '0.25 0.90 1', autocvar_hud_colorflash_alpha, DRAWFLAG_ADDITIVE);
-                               drawstring_aspect(eY * 0.64 * vid_conheight, "Revival progress", eX * vid_conwidth + eY * 0.025 * vid_conheight, '1 1 1', 1, DRAWFLAG_NORMAL);
+                               drawstring_aspect(eY * 0.64 * vid_conheight, _("Revival progress"), eX * vid_conwidth + eY * 0.025 * vid_conheight, '1 1 1', 1, DRAWFLAG_NORMAL);
                        }
                }
 
index 0409aea..7a4ed92 100644 (file)
@@ -16,19 +16,34 @@ float announcer_5min;
 void Announcer_Countdown() 
 {
        float starttime = getstatf(STAT_GAMESTARTTIME);
+       float roundstarttime = getstatf(STAT_ROUNDSTARTTIME);
+       if(roundstarttime == -1)
+       {
+               Local_Notification(MSG_CENTER, CENTER_COUNTDOWN_ROUNDSTOP);
+               remove(self);
+               return;
+       }
+       if(roundstarttime >= starttime)
+               starttime = roundstarttime;
+       if(starttime <= time && roundstarttime != starttime) // game start time has passed
+               announcer_5min = announcer_1min = FALSE; // reset maptime announcers now as well
+
        float countdown = (starttime - time);
        float countdown_rounded = floor(0.5 + countdown);
-       
+
        if(countdown <= 0) // countdown has finished, starttime is now
        {
+               Local_Notification(MSG_CENTER, CENTER_COUNTDOWN_BEGIN); 
                Local_Notification(MSG_MULTI, MULTI_COUNTDOWN_BEGIN); 
-               announcer_5min = announcer_1min = FALSE; // reset maptime announcers now as well
                remove(self);
                return;
        }
        else // countdown is still going
        {
-               Local_Notification(MSG_CENTER, CENTER_COUNTDOWN_GAMESTART, countdown_rounded);
+               if(roundstarttime == starttime)
+                       Local_Notification(MSG_CENTER, CENTER_COUNTDOWN_ROUNDSTART, countdown_rounded);
+               else
+                       Local_Notification(MSG_CENTER, CENTER_COUNTDOWN_GAMESTART, countdown_rounded);
 
                switch(countdown_rounded)
                {
@@ -52,17 +67,24 @@ void Announcer_Countdown()
 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
                        Local_Notification(MSG_ANNCE, ANNCE_PREPARE);
-               
+
                if(time < startTime) 
                {
-                       entity e;
-                       e = spawn();
-                       e.think = Announcer_Countdown;
+                       entity e = find(world, classname, "announcer_countdown");
+                       if not(e)
+                       {
+                               e = spawn();
+                               e.classname = "announcer_countdown";
+                               e.think = Announcer_Countdown;
+                       }
                        e.nextthink = startTime - floor(startTime - time); //synchronize nextthink to startTime
                }
        }
index 6243a00..c13e2bf 100644 (file)
@@ -253,7 +253,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 e49ac60..ee44db9 100644 (file)
@@ -2633,32 +2633,100 @@ void HUD_Vote(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;
+#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)
        {
-               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 = 0;
+       for(tm = teams.sort_next; tm; tm = tm.sort_next)
+               if(tm.team != NUM_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;
+       vector pos, itemSize;
+       itemSize = eX * mySize_x*(1/columns) + eY * mySize_y*(1/rows);
+       for(i=0; i<teams_count; ++i)
+       {
+               pos = myPos + eX * column * itemSize_x + eY * row * itemSize_y;
+
+               DrawCAItem(pos, itemSize, aspect_ratio, layout, i);
+
+               ++row;
+               if(row >= rows)
+               {
+                       row = 0;
+                       ++column;
+               }
        }
 }
 
@@ -3272,11 +3340,11 @@ void HUD_Mod_Dom(vector myPos, vector mySize)
 
        int i;
        float row = 0, column = 0;
+       vector pos, itemSize;
+       itemSize = eX * mySize_x*(1/columns) + eY * mySize_y*(1/rows);
        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);
+               pos = myPos + eX * column * itemSize_x + eY * row * itemSize_y;
 
                DrawDomItem(pos, itemSize, aspect_ratio, layout, i);
 
index d478f73..ca8ec19 100644 (file)
@@ -140,7 +140,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("_aspect");
index ccf2725..cbda9dc 100644 (file)
@@ -177,6 +177,7 @@ const float STAT_SECRETS_TOTAL = 70;
 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;
index b4dbda2..82c5673 100644 (file)
@@ -476,6 +476,22 @@ void _MapInfo_Map_ApplyGametype(string s, float pWantedType, float pThisType, fl
                s = cdr(s);
        }
 
+       if(pWantedType == MAPINFO_TYPE_CA)
+       {
+               sa = car(s);
+               if(sa != "")
+                       cvar_set("g_ca_teams", sa);
+               s = cdr(s);
+       }
+
+       if(pWantedType == MAPINFO_TYPE_FREEZETAG)
+       {
+               sa = car(s);
+               if(sa != "")
+                       cvar_set("g_freezetag_teams", sa);
+               s = cdr(s);
+       }
+
        if(pWantedType == MAPINFO_TYPE_CTF)
        {
                sa = car(s);
@@ -566,6 +582,8 @@ void _MapInfo_Map_ApplyGametypeEx(string s, float pWantedType, float pThisType)
        cvar_set("leadlimit", cvar_defstring("leadlimit"));
        cvar_set("fraglimit", cvar_defstring("fraglimit"));
        cvar_set("g_tdm_teams", cvar_defstring("g_tdm_teams"));
+       cvar_set("g_ca_teams", cvar_defstring("g_ca_teams"));
+       cvar_set("g_freezetag_teams", cvar_defstring("g_freezetag_teams"));
        cvar_set("g_keyhunt_teams", cvar_defstring("g_keyhunt_teams"));
        cvar_set("g_domination_default_teams", cvar_defstring("g_domination_default_teams"));
        cvar_set("g_race_qualifying_timelimit", cvar_defstring("g_race_qualifying_timelimit"));
@@ -613,6 +631,8 @@ void _MapInfo_Map_ApplyGametypeEx(string s, float pWantedType, float pThisType)
                else if(k == "teams")
                {
                        cvar_set("g_tdm_teams", v);
+                       cvar_set("g_ca_teams", v);
+                       cvar_set("g_freezetag_teams", v);
                        cvar_set("g_keyhunt_teams", v);
                        cvar_set("g_domination_default_teams", v);
                }
index 8a9c54f..171be6a 100644 (file)
@@ -323,8 +323,12 @@ void Send_Notification_WOVA(
        MSG_INFO_NOTIF(1, INFO_DEATH_SELF_VOID,                2, 1, "s1 s2loc spree_lost", "s1",       "notify_void",          _("^BG%s^K1 was in the wrong place%s%s\n"), "") \
        MULTITEAM_INFO(1, INFO_DEATH_TEAMKILL_, 4,             3, 1, "s1 s2 s3loc spree_end", "s2 s1",  "notify_teamkill_%s",   _("^BG%s^K1 was betrayed by ^BG%s^K1%s%s\n"), "") \
        MSG_INFO_NOTIF(1, INFO_FREEZETAG_FREEZE,               2, 0, "s1 s2", "",                       "",                     _("^BG%s^K1 was frozen by ^BG%s\n"), "") \
-       MSG_INFO_NOTIF(1, INFO_FREEZETAG_REVIVE,               2, 0, "s1 s2", "",                       "",                     _("^BG%s^K3 was revived by ^BG%s\n"), "") \
-       MULTITEAM_INFO(1, INFO_FREEZETAG_ROUND_WIN_, 4,        0, 0, "", "",                            "",                     _("^TC^TT^BG team wins the round, all other teams were frozen\n"), "") \
+       MSG_INFO_NOTIF(1, INFO_FREEZETAG_REVIVED,              2, 0, "s1 s2", "",                       "",                     _("^BG%s^K3 was revived by ^BG%s\n"), "") \
+       MSG_INFO_NOTIF(1, INFO_FREEZETAG_AUTO_REVIVED,         1, 1, "s1 f1", "",                       "",                     _("^BG%s^K3 was automatically revived after %s second(s)\n"), "") \
+       MULTITEAM_INFO(1, INFO_ROUND_TEAM_WIN_, 4,             0, 0, "", "",                            "",                     _("^TC^TT^BG team wins the round\n"), "") \
+       MSG_INFO_NOTIF(1, INFO_ROUND_PLAYER_WIN,               1, 0, "s1", "",                          "",                     _("^BG%s^BG wins the round\n"), "") \
+       MSG_INFO_NOTIF(1, INFO_ROUND_TIED,                     0, 0, "", "",                            "",                     _("^BGRound tied\n"), "") \
+       MSG_INFO_NOTIF(1, INFO_ROUND_OVER,                     0, 0, "", "",                            "",                     _("^BGRound over, there's no winner\n"), "") \
        MSG_INFO_NOTIF(1, INFO_FREEZETAG_SELF,                 1, 0, "s1", "",                          "",                     _("^BG%s^K1 froze themself\n"), "") \
        MSG_INFO_NOTIF(1, INFO_GODMODE_OFF,                    0, 1, "f1", "",                          "",                     _("^BGGodmode saved you %s units of damage, cheater!\n"), "") \
        MSG_INFO_NOTIF(0, INFO_ITEM_WEAPON_DONTHAVE,           0, 1, "item_wepname", "",                      "",               _("^BGYou do not have the ^F1%s\n"), "") \
@@ -426,13 +430,14 @@ void Send_Notification_WOVA(
                MSG_CENTER_NOTIF(default, prefix##PINK, strnum, flnum, args, cpid, durcnt, TCR(normal, COL_TEAM_4, strtoupper(STATIC_NAME_TEAM_4)), TCR(gentle, COL_TEAM_4, strtoupper(STATIC_NAME_TEAM_4))) \
        #endif
 #define MSG_CENTER_NOTIFICATIONS \
-       MSG_CENTER_NOTIF(1, CENTER_ARENA_BEGIN,                 0, 0, "",             CPID_ARENA,          "2 0", _("^F4Begin!"), "") \
-       MSG_CENTER_NOTIF(1, CENTER_ARENA_NEEDPLAYER,            0, 0, "",             CPID_ARENA,          "2 0", _("^BGNeed at least 1 player in each team to play Clan Arena!"), "") \
-       MSG_CENTER_NOTIF(1, CENTER_ARENA_ROUNDSTART,            0, 1, "",             CPID_ARENA,          "1 f1", _("^F4Round will start in ^COUNT"), "") \
        MSG_CENTER_NOTIF(1, CENTER_ASSAULT_ATTACKING,           0, 0, "",             CPID_ASSAULT_ROLE,   "0 0", _("^BGYou are attacking!"), "") \
        MSG_CENTER_NOTIF(1, CENTER_ASSAULT_DEFENDING,           0, 0, "",             CPID_ASSAULT_ROLE,   "0 0", _("^BGYou are defending!"), "") \
-       MSG_CENTER_NOTIF(1, CENTER_COUNTDOWN_BEGIN,             0, 0, "",             CPID_GAMESTART,      "2 0", _("^F4Begin!"), "") \
-       MSG_CENTER_NOTIF(1, CENTER_COUNTDOWN_GAMESTART,         0, 1, "",             CPID_GAMESTART,      "1 f1", _("^F4Game starts in ^COUNT"), "") \
+       MSG_CENTER_NOTIF(1, CENTER_COUNTDOWN_BEGIN,             0, 0, "",             CPID_ROUND,          "2 0", _("^F4Begin!"), "") \
+       MSG_CENTER_NOTIF(1, CENTER_COUNTDOWN_GAMESTART,         0, 1, "",             CPID_ROUND,          "1 f1", _("^F4Game starts in ^COUNT"), "") \
+       MSG_CENTER_NOTIF(1, CENTER_COUNTDOWN_ROUNDSTART,        0, 1, "",             CPID_ROUND,          "1 f1", _("^F4Round starts in ^COUNT"), "") \
+       MSG_CENTER_NOTIF(1, CENTER_COUNTDOWN_ROUNDSTOP,         0, 0, "",             CPID_ROUND,          "2 0", _("^F4Round cannot start"), "") \
+       MSG_CENTER_NOTIF(1, CENTER_ROUND_TIED,                  0, 0, "",             CPID_ROUND,          "0 0", _("^BGRound tied"), "") \
+       MSG_CENTER_NOTIF(1, CENTER_ROUND_OVER,                  0, 0, "",             CPID_ROUND,          "0 0", _("^BGRound over, there's no winner"), "") \
        MSG_CENTER_NOTIF(1, CENTER_CTF_CAPTURESHIELD_FREE,      0, 0, "",             CPID_CTF_CAPSHIELD,  "0 0", _("^BGYou are now free.\n^BGFeel free to ^F2try to capture^BG the flag again\n^BGif you think you will succeed."), "") \
        MSG_CENTER_NOTIF(1, CENTER_CTF_CAPTURESHIELD_SHIELDED,  0, 0, "",             CPID_CTF_CAPSHIELD,  "0 0", _("^BGYou are now ^F1shielded^BG from the flag\n^BGfor ^F2too many unsuccessful attempts^BG to capture.\n^BGMake some defensive scores before trying again."), "") \
        MULTITEAM_CENTER(1, CENTER_CTF_CAPTURE_, 2,             0, 0, "",             CPID_CTF_LOWPRIO,    "0 0", _("^BGYou captured the ^TC^TT^BG flag!"), "") \
@@ -495,9 +500,11 @@ void Send_Notification_WOVA(
        MSG_CENTER_NOTIF(1, CENTER_FREEZETAG_FROZEN,            1, 0, "s1",           NO_CPID,             "0 0", _("^K1You were frozen by ^BG%s"), "") \
        MSG_CENTER_NOTIF(1, CENTER_FREEZETAG_REVIVE,            1, 0, "s1",           NO_CPID,             "0 0", _("^K3You revived ^BG%s"), "") \
        MSG_CENTER_NOTIF(1, CENTER_FREEZETAG_REVIVED,           1, 0, "s1",           NO_CPID,             "0 0", _("^K3You were revived by ^BG%s"), "") \
-       MULTITEAM_CENTER(1, CENTER_FREEZETAG_ROUND_WIN_, 4,     0, 0, "",             NO_CPID,             "0 0", _("^TC^TT^BG team wins the round, all other teams were frozen"), "") \
+       MSG_CENTER_NOTIF(1, CENTER_FREEZETAG_AUTO_REVIVED,      0, 1, "f1",           NO_CPID,             "0 0", _("^K3You were automatically revived after %s second(s)"), "") \
+       MULTITEAM_CENTER(1, CENTER_ROUND_TEAM_WIN_, 4,          0, 0, "",             CPID_ROUND,          "0 0", _("^TC^TT^BG team wins the round"), "") \
+       MSG_CENTER_NOTIF(1, CENTER_ROUND_PLAYER_WIN,            1, 0, "s1",           CPID_ROUND,          "0 0", _("^BG%s^BG wins the round"), "") \
        MSG_CENTER_NOTIF(1, CENTER_FREEZETAG_SELF,              0, 0, "",             NO_CPID,             "0 0", _("^K1You froze yourself"), "") \
-       MSG_CENTER_NOTIF(1, CENTER_FREEZETAG_SPAWN_LATE,        0, 0, "",             NO_CPID,             "0 0", _("^K1You spawned after the round started, you'll spawn as frozen"), "") \
+       MSG_CENTER_NOTIF(1, CENTER_FREEZETAG_SPAWN_LATE,        0, 0, "",             NO_CPID,             "0 0", _("^K1Round already started, you spawn as frozen"), "") \
        MSG_CENTER_NOTIF(1, CENTER_ITEM_WEAPON_DONTHAVE,        0, 1, "item_wepname",                      CPID_ITEM, "item_centime 0", _("^BGYou do not have the ^F1%s"), "") \
        MSG_CENTER_NOTIF(1, CENTER_ITEM_WEAPON_DROP,            1, 1, "item_wepname item_wepammo",         CPID_ITEM, "item_centime 0", _("^BGYou dropped the ^F1%s^BG%s"), "") \
        MSG_CENTER_NOTIF(1, CENTER_ITEM_WEAPON_GOT,             0, 1, "item_wepname",                      CPID_ITEM, "item_centime 0", _("^BGYou got the ^F1%s"), "") \
@@ -512,9 +519,12 @@ void Send_Notification_WOVA(
        MSG_CENTER_NOTIF(1, CENTER_KEYHUNT_HELP,                0, 0, "",              CPID_KEYHUNT,          "0 0", _("^BGAll keys are in your team's hands!\nHelp the key carriers to meet!"), "") \
        MULTITEAM_CENTER(1, CENTER_KEYHUNT_INTERFERE_, 4,       0, 0, "",              CPID_KEYHUNT,          "0 0", _("^BGAll keys are in ^TC^TT team^BG's hands!\nInterfere ^F4NOW^BG!"), "") \
        MSG_CENTER_NOTIF(1, CENTER_KEYHUNT_MEET,                0, 0, "",              CPID_KEYHUNT,          "0 0", _("^BGAll keys are in your team's hands!\nMeet the other key carriers ^F4NOW^BG!"), "") \
+       MSG_CENTER_NOTIF(1, CENTER_KEYHUNT_ROUNDSTART,          0, 1, "",              CPID_KEYHUNT_OTHER,    "1 f1", _("^F4Round will start in ^COUNT"), "") \
        MSG_CENTER_NOTIF(1, CENTER_KEYHUNT_SCAN,                0, 1, "",              CPID_KEYHUNT_OTHER,    "f1 0", _("^BGScanning frequency range..."), "") \
        MULTITEAM_CENTER(1, CENTER_KEYHUNT_START_, 4,           0, 0, "",              CPID_KEYHUNT,          "0 0", _("^BGYou are starting with the ^TC^TT Key"), "") \
-       MSG_CENTER_NOTIF(1, CENTER_KEYHUNT_WAIT,                0, 4, "kh_teams",      CPID_KEYHUNT_OTHER,    "0 0", _("^BGWaiting for players to join...\nNeed active players for: %s"), "") \
+       MSG_CENTER_NOTIF(1, CENTER_KEYHUNT_WAIT,                0, 4, "missing_teams", CPID_KEYHUNT_OTHER,    "0 0", _("^BGWaiting for players to join...\nNeed active players for: %s"), "") \
+       MSG_CENTER_NOTIF(1, CENTER_MISSING_TEAMS,               0, 4, "missing_teams", CPID_MISSING_TEAMS,    "-1 0", _("^BGWaiting for players to join...\nNeed active players for: %s"), "") \
+       MSG_CENTER_NOTIF(1, CENTER_MISSING_PLAYERS,             0, 1, "f1",            CPID_MISSING_PLAYERS,  "-1 0", _("^BGWaiting for %s player(s) to join..."), "") \
        MSG_CENTER_NOTIF(1, CENTER_LMS_CAMPCHECK,               0, 0, "",              CPID_LMS_CAMP,         "0 0", _("^F2Don't camp!"), "") \
        MSG_CENTER_NOTIF(1, CENTER_MINSTA_FINDAMMO,             0, 0, "",              CPID_MINSTA_FINDAMMO,  "1 9", _("^F4^COUNT^BG left to find some ammo!"), "") \
        MSG_CENTER_NOTIF(1, CENTER_MINSTA_FINDAMMO_FIRST,       0, 0, "",              CPID_MINSTA_FINDAMMO,  "1 10", _("^BGGet some ammo or you'll be dead in ^F4^COUNT^BG!"), _("^BGGet some ammo! ^F4^COUNT^BG left!")) \
@@ -612,7 +622,6 @@ void Send_Notification_WOVA(
        MSG_MULTI_NOTIF(1, ITEM_WEAPON_NOAMMO,                   NO_MSG,        INFO_ITEM_WEAPON_NOAMMO,                   CENTER_ITEM_WEAPON_NOAMMO) \
        MSG_MULTI_NOTIF(1, ITEM_WEAPON_PRIMORSEC,                NO_MSG,        INFO_ITEM_WEAPON_PRIMORSEC,                CENTER_ITEM_WEAPON_PRIMORSEC) \
        MSG_MULTI_NOTIF(1, ITEM_WEAPON_UNAVAILABLE,              NO_MSG,        INFO_ITEM_WEAPON_UNAVAILABLE,              CENTER_ITEM_WEAPON_UNAVAILABLE) \
-       MSG_MULTI_NOTIF(1, MULTI_ARENA_BEGIN,                    ANNCE_BEGIN,   NO_MSG,                                    CENTER_ARENA_BEGIN) \
        MSG_MULTI_NOTIF(1, MULTI_COUNTDOWN_BEGIN,                ANNCE_BEGIN,   NO_MSG,                                    CENTER_COUNTDOWN_BEGIN) \
        MSG_MULTI_NOTIF(1, MULTI_MINSTA_FINDAMMO,                ANNCE_NUM_10,  NO_MSG,                                    CENTER_MINSTA_FINDAMMO_FIRST) \
        MSG_MULTI_NOTIF(1, WEAPON_ACCORDEON_MURDER,              NO_MSG,        INFO_WEAPON_ACCORDEON_MURDER,              NO_MSG) \
@@ -737,7 +746,7 @@ var float autocvar_notification_frag_verbose = TRUE;
     f2race_time: mmssss of f2
     race_col: color of race time/position (i.e. good or bad)
     race_diff: show time difference between f2 and f3
-    kh_teams: show which teams still need players in keyhunt centerprint
+    missing_teams: show which teams still need players
     pass_key: find the keybind for "passing" or "dropping" in CTF game mode
     frag_ping: show the ping of a player
     frag_stats: show health/armor/ping of a player
@@ -791,7 +800,7 @@ string arg_slot[NOTIF_MAX_ARGS];
     ARG_CASE(ARG_CS_SV,     "f3race_time",   mmssss(f3)) \
     ARG_CASE(ARG_CS_SV,     "race_col",      CCR(((f1 == 1) ? "^F1" : "^F2"))) \
     ARG_CASE(ARG_CS_SV,     "race_diff",     ((f2 > f3) ? sprintf(CCR("^1[+%s]"), mmssss(f2 - f3)) : sprintf(CCR("^2[-%s]"), mmssss(f3 - f2)))) \
-    ARG_CASE(ARG_CS,        "kh_teams",      notif_arg_kh_teams(f1, f2, f3, f4)) \
+    ARG_CASE(ARG_CS,        "missing_teams", notif_arg_missing_teams(f1, f2, f3, f4)) \
     ARG_CASE(ARG_CS,        "pass_key",      ((((tmp_s = getcommandkey("pass", "+use")) != "pass") && !(strstrofs(tmp_s, "not bound", 0) >= 0)) ? sprintf(CCR(_(" ^F1(Press %s)")), tmp_s) : "")) \
     ARG_CASE(ARG_CS,        "frag_ping",     notif_arg_frag_ping(TRUE, f2)) \
     ARG_CASE(ARG_CS,        "frag_stats",    notif_arg_frag_stats(f2, f3, f4)) \
@@ -835,7 +844,7 @@ string notif_arg_frag_stats(float fhealth, float farmor, float fping)
                return sprintf(CCR(_("\n(^F4Dead^BG)%s")), notif_arg_frag_ping(FALSE, fping));
 }
 
-string notif_arg_kh_teams(float f1, float f2, float f3, float f4)
+string notif_arg_missing_teams(float f1, float f2, float f3, float f4)
 {
        return sprintf("%s%s%s%s",
                (f1 ?
index 3fcabaa..e6fffe0 100644 (file)
@@ -55,7 +55,7 @@ void XonoticAdvancedDialog_fill(entity me)
        me.TR(me);
                me.TDempty(me, 0.2);
                me.TD(me, 1, 1.2, makeXonoticTextLabel(0, _("Teams:")));
-               me.TD(me, 1, 1.6, e = makeXonoticTextSlider("g_tdm_teams_override g_domination_teams_override g_keyhunt_teams_override"));
+               me.TD(me, 1, 1.6, e = makeXonoticTextSlider("g_tdm_teams_override g_domination_teams_override g_ca_teams_override g_freezetag_teams_override g_keyhunt_teams_override"));
                        e.addValue(e, "Default", "0");
                        e.addValue(e, "2 teams", "2");
                        e.addValue(e, "3 teams", "3");
diff --git a/qcsrc/server/arena.qc b/qcsrc/server/arena.qc
deleted file mode 100644 (file)
index 34b81bb..0000000
+++ /dev/null
@@ -1,462 +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 totalalive;
-.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, keys, weapons, waypoints, ... of the map.
- * Sets the 'warmup' global variable.
- */
-void reset_map(float dorespawn)
-{
-       entity oldself;
-       oldself = self;
-
-       if(g_arena && autocvar_g_arena_warmup)
-               warmup = time + autocvar_g_arena_warmup;
-       else if(g_ca) {
-               warmup = time + autocvar_g_ca_warmup;
-               allowed_to_spawn = 1;
-       }
-       else if(g_freezetag)
-       {
-               warmup = time + autocvar_g_freezetag_warmup;
-       }
-
-       lms_lowest_lives = 999;
-       lms_next_place = player_count;
-
-       race_ReadyRestart();
-
-       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)
-       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 if(g_freezetag)
-                       {
-                               if(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(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)
-               {
-                       Kill_Notification(NOTIF_ALL, world, MSG_CENTER_CPID, CPID_ARENA);
-                       warmup = 0;
-               }
-               if(champion && g_arena)
-               {
-                       FOR_EACH_REALCLIENT(e)
-                               centerprint(e, strcat("The Champion is ", champion.netname));
-                       champion = world;
-               }
-               return;
-       }
-       if((!g_arena && !g_ca && !g_freezetag) || (g_arena && !arena_roundbased) || (time < game_starttime))
-               return;
-
-       f = ceil(warmup - time);
-
-       if(inWarmupStage)
-               allowed_to_spawn = 1;
-       else if(!g_ca)
-               allowed_to_spawn = 0;
-
-       if(time < warmup && !inWarmupStage)
-       {
-               if (g_ca)
-                       allowed_to_spawn = 1;
-               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)) {
-                               Send_Notification(NOTIF_ALL, world, MSG_CENTER, CENTER_ARENA_NEEDPLAYER);
-                               warmup = time + autocvar_g_ca_warmup;
-                       } else {
-                               if(f == 5)
-                                       Send_Notification(NOTIF_ALL, world, MSG_ANNCE, ANNCE_PREPARE);
-                               else if(f == 3)
-                                       Send_Notification(NOTIF_ALL, world, MSG_ANNCE, ANNCE_NUM_3);
-                               else if(f == 2)
-                                       Send_Notification(NOTIF_ALL, world, MSG_ANNCE, ANNCE_NUM_2);
-                               else if(f == 1)
-                                       Send_Notification(NOTIF_ALL, world, MSG_ANNCE, ANNCE_NUM_1);
-
-                               Send_Notification(NOTIF_ALL, world, MSG_CENTER, CENTER_ARENA_ROUNDSTART, 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)
-       {
-               roundStartTime_prev = f;
-               if(g_ca) {
-                       if(red_players && blue_players)
-                               allowed_to_spawn = 0;
-                       else
-                               reset_map(TRUE);
-               } else {
-                       Send_Notification(NOTIF_ALL, world, MSG_MULTI, MULTI_ARENA_BEGIN);
-               }
-
-               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 == NUM_TEAM_1)
-               {
-                       red_players += 1;
-                       total_players += 1;
-               }
-               else if (self.team == NUM_TEAM_2)
-               {
-                       blue_players += 1;
-                       total_players += 1;
-               }
-               else if (self.team == NUM_TEAM_3)
-               {
-                       yellow_players += 1;
-                       total_players += 1;
-               }
-               else if (self.team == NUM_TEAM_4)
-               {
-                       pink_players += 1;
-                       total_players += 1;
-               }
-       }
-}
-
-void count_alive_players()
-{
-       totalalive = redalive = bluealive = yellowalive = pinkalive = 0;
-       if(g_ca)
-       {
-               FOR_EACH_PLAYER(self) {
-                       if (self.team == NUM_TEAM_1 && self.health >= 1)
-                       {
-                               redalive += 1;
-                               totalalive += 1;
-                       }
-                       else if (self.team == NUM_TEAM_2 && self.health >= 1)
-                       {
-                               bluealive += 1;
-                               totalalive += 1;
-                       }
-               }
-               FOR_EACH_REALCLIENT(self) {
-                       self.redalive_stat = redalive;
-                       self.bluealive_stat = bluealive;
-               }
-       }
-       else if(g_freezetag)
-       {
-               // count amount of alive players in each team
-               FOR_EACH_PLAYER(self) {
-                       if (self.team == NUM_TEAM_1 && self.freezetag_frozen == 0 && self.health >= 1)
-                       {
-                               redalive += 1;
-                               totalalive += 1;
-                       }
-                       else if (self.team == NUM_TEAM_2 && self.freezetag_frozen == 0 && self.health >= 1)
-                       {
-                               bluealive += 1;
-                               totalalive += 1;
-                       }
-                       else if (self.team == NUM_TEAM_3 && self.freezetag_frozen == 0 && self.health >= 1)
-                       {
-                               yellowalive += 1;
-                               totalalive += 1;
-                       }
-                       else if (self.team == NUM_TEAM_4 && self.freezetag_frozen == 0 && self.health >= 1)
-                       {
-                               pinkalive += 1;
-                               totalalive += 1;
-                       }
-               }
-               FOR_EACH_REALCLIENT(self) {
-                       self.redalive_stat = redalive;
-                       self.bluealive_stat = bluealive;
-                       self.yellowalive_stat = yellowalive;
-                       self.pinkalive_stat = pinkalive;
-               }
-       }
-
-}
-
-/**
- * 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(warmup == 0 && g_ca && !inWarmupStage)
-       {
-               if(red_players || blue_players)
-                       reset_map(TRUE);
-               return;
-       }
-       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(NUM_TEAM_1, ST_SCORE, +1);
-                               }
-                               else if(bluealive) {
-                                       play2all("ctf/blue_capture.wav");
-                                       FOR_EACH_CLIENT(self) centerprint(self, "^4BLUE ^7team wins the round");
-                                       TeamScore_AddToTeam(NUM_TEAM_2, 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 if(g_freezetag) {
-               if((next_round && 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();
-                       }
-               }
-       }
-}
index 15ad76c..ad8157a 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;
@@ -709,6 +710,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")
@@ -814,11 +817,15 @@ string autocvar_g_forced_team_pink;
 string autocvar_g_forced_team_red;
 string autocvar_g_forced_team_yellow;
 float autocvar_g_freezetag_frozen_force;
+float autocvar_g_freezetag_frozen_maxtime;
 float autocvar_g_freezetag_point_leadlimit;
 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")
@@ -967,7 +974,6 @@ float autocvar_g_spawn_useallspawns;
 float autocvar_g_spawnpoints_auto_move_out_of_solid;
 #define autocvar_g_spawnshieldtime cvar("g_spawnshieldtime")
 float autocvar_g_spawnsound;
-float autocvar_g_start_delay;
 #define autocvar_g_start_weapon_laser cvar("g_start_weapon_laser")
 float autocvar_g_tdm_team_spawns;
 float autocvar_g_tdm_teams;
index 3bff21e..cb42aa5 100644 (file)
@@ -111,9 +111,8 @@ float bot_shouldattack(entity e)
                        return FALSE;
        }
 
-       if(g_freezetag)
-               if(e.freezetag_frozen)
-                       return FALSE;
+       if(e.freezetag_frozen)
+               return FALSE;
 
        // If neither player has ball then don't attack unless the ball is on the
        // ground.
index d87c726..f6e7f6f 100644 (file)
@@ -551,7 +551,7 @@ float bot_fixcount()
 
        FOR_EACH_REALCLIENT(head)
        {
-               if(head.classname == "player" || g_lms || g_arena || g_ca)
+               if(head.classname == "player" || g_lms || g_arena || head.caplayer == 1)
                        ++activerealplayers;
                ++realplayers;
        }
index 7f16dfd..5d29bf7 100644 (file)
@@ -277,7 +277,7 @@ entity SelectSpawnPoint (float anypoint)
        else
        {
                float mindist;
-               if (arena_roundbased && !g_ca)
+               if (g_arena && arena_roundbased)
                        mindist = 800;
                else
                        mindist = 100;
@@ -385,6 +385,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;
+
        MUTATOR_CALLHOOK(MakePlayerObserver);
 
        minstagib_stop_countdown(self);
@@ -443,7 +461,9 @@ void PutObserverInServer (void)
        self.pauseregen_finished = 0;
        self.damageforcescale = 0;
        self.death_time = 0;
+       self.respawn_flags = 0;
        self.respawn_time = 0;
+       self.stat_respawn_time = 0;
        self.alpha = 0;
        self.scale = 0;
        self.fade_time = 0;
@@ -488,45 +508,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;
@@ -622,11 +603,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;
@@ -645,13 +622,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;
 
@@ -757,7 +733,9 @@ void PutClientInServer (void)
                }
                self.damageforcescale = 2;
                self.death_time = 0;
+               self.respawn_flags = 0;
                self.respawn_time = 0;
+               self.stat_respawn_time = 0;
                self.scale = 0;
                self.fade_time = 0;
                self.pain_frame = 0;
@@ -809,14 +787,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;
@@ -1047,14 +1017,13 @@ void ClientKill_Now_TeamChange()
        }
        else if(self.killindicator_teamchange == -2)
        {
-               if(g_ca)
-                       self.caplayer = 0;
                if(blockSpectators)
                        Send_Notification(NOTIF_ONE_ONLY, self, MSG_INFO, INFO_SPECTATE_WARNING, autocvar_g_maxplayers_spectator_blocktime);
                PutObserverInServer();
        }
        else
                SV_ChangeTeam(self.killindicator_teamchange - 1);
+       self.killindicator_teamchange = 0;
 }
 
 void ClientKill_Now()
@@ -1230,19 +1199,11 @@ void ClientKill_TeamChange (float targetteam) // 0 = don't change, -1 = auto, -2
 
 void ClientKill (void)
 {
-       if (gameover)
-               return;
+       if(gameover) 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
@@ -1472,13 +1433,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();
@@ -1552,13 +1506,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);
 }
 /*
 =============
@@ -1625,12 +1577,6 @@ void ClientDisconnect (void)
 
        bot_relinkplayerlist();
 
-       if(g_arena)
-       {
-               Spawnqueue_Unmark(self);
-               Spawnqueue_Remove(self);
-       }
-
        accuracy_free(self);
        ClientData_Detach();
        PlayerScore_Detach(self);
@@ -1988,7 +1934,7 @@ void player_regen (void)
        limita = limita * limit_mod;
        //limitf = limitf * limit_mod;
 
-       if(g_lms && g_ca)
+       if(g_ca)
                rot_mod = 0;
 
        if (!g_minstagib && !g_ca && (!g_lms || autocvar_g_lms_regenerate))
@@ -2113,7 +2059,6 @@ void SpectateCopy(entity spectatee) {
        self.dmg_inflictor = spectatee.dmg_inflictor;
        self.v_angle = spectatee.v_angle;
        self.angles = spectatee.v_angle;
-       self.stat_respawn_time = spectatee.stat_respawn_time;
        if(!self.BUTTON_USE)
                self.fixangle = TRUE;
        setorigin(self, spectatee.origin);
@@ -2264,6 +2209,8 @@ void ShowRespawnCountdown()
 
 void LeaveSpectatorMode()
 {
+       if(self.caplayer)
+               return;
        if(nJoinAllowed(self))
        {
                if(!teamplay || autocvar_g_campaign || autocvar_g_balance_teams || (self.wasplayer && autocvar_g_changeteam_banned) || self.team_forced > 0)
@@ -2282,7 +2229,8 @@ void LeaveSpectatorMode()
 
                        if(IS_PLAYER(self)) { Send_Notification(NOTIF_ALL, world, MSG_INFO, INFO_JOIN_PLAY, self.netname); }
                }
-               else if not(g_ca && self.caplayer) { stuffcmd(self, "menu_showteamselect\n"); }
+               else
+                       stuffcmd(self, "menu_showteamselect\n");
        }
        else
        {
@@ -2322,8 +2270,9 @@ float nJoinAllowed(entity ignore) {
                return maxclients - totalClients;
 
        float currentlyPlaying = 0;
-       FOR_EACH_REALPLAYER(e)
-               currentlyPlaying += 1;
+       FOR_EACH_REALCLIENT(e)
+               if(e.classname == "player" || e.caplayer == 1)
+                       currentlyPlaying += 1;
 
        if(currentlyPlaying < autocvar_g_maxplayers)
                return min(maxclients - totalClients, autocvar_g_maxplayers - currentlyPlaying);
@@ -2490,14 +2439,10 @@ void PlayerPreThink (void)
        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;
-
        if(frametime)
        {
                // physics frames: update anticheat stuff
@@ -2622,25 +2567,26 @@ void PlayerPreThink (void)
 
                if (self.deadflag != DEAD_NO)
                {
-                       float button_pressed, force_respawn;
                        if(self.personal && g_race_qualifying)
                        {
                                if(time > self.respawn_time)
                                {
                                        self.respawn_time = time + 1; // only retry once a second
+                                       self.stat_respawn_time = self.respawn_time;
                                        respawn();
                                        self.impulse = 141;
                                }
                        }
                        else
                        {
+                               float button_pressed;
                                if(frametime)
                                        player_anim();
                                button_pressed = (self.BUTTON_ATCK || self.BUTTON_JUMP || self.BUTTON_ATCK2 || self.BUTTON_HOOK || self.BUTTON_USE);
-                               force_respawn = (g_lms || g_ca || g_cts || autocvar_g_forced_respawn);
+
                                if (self.deadflag == DEAD_DYING)
                                {
-                                       if(force_respawn)
+                                       if(self.respawn_flags & RESPAWN_FORCE)
                                                self.deadflag = DEAD_RESPAWNING;
                                        else if(!button_pressed)
                                                self.deadflag = DEAD_DEAD;
@@ -2663,7 +2609,13 @@ void PlayerPreThink (void)
                                                respawn();
                                        }
                                }
+
                                ShowRespawnCountdown();
+
+                               if(self.respawn_flags & RESPAWN_SILENT)
+                                       self.stat_respawn_time = 0;
+                               else
+                                       self.stat_respawn_time = self.respawn_time;
                        }
 
                        // if respawning, invert stat_respawn_time to indicate this, the client translates it
index 529567a..d09c070 100644 (file)
@@ -46,6 +46,10 @@ void ImpulseCommands (void)
                return;
        self.impulse = 0;
 
+       // forbid impulses when not in round time
+       if(round_handler_IsActive() && !round_handler_IsRoundStarted())
+               return;
+
        if (timeout_status == TIMEOUT_ACTIVE) //don't allow any impulses while the game is paused
                return;
     
index 944db7e..95aeced 100644 (file)
@@ -338,7 +338,6 @@ void PlayerCorpseDamage (entity inflictor, entity attacker, float damage, float
 }
 
 void ClientKill_Now_TeamChange();
-void freezetag_CheckWinner();
 
 void PlayerDamage (entity inflictor, entity attacker, float damage, float deathtype, vector hitloc, vector force)
 {
@@ -347,9 +346,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);
 
@@ -579,14 +575,6 @@ void PlayerDamage (entity inflictor, entity attacker, float damage, float deatht
                        }
                }
 
-               if(!g_freezetag)
-               {
-                       // become fully visible
-                       self.alpha = default_player_alpha;
-                       // throw a weapon
-                       SpawnThrownWeapon (self.origin + (self.mins + self.maxs) * 0.5, self.switchweapon);
-               }
-
                // print an obituary message
                Obituary (attacker, inflictor, self, deathtype);
                race_PreDie();
@@ -598,18 +586,12 @@ void PlayerDamage (entity inflictor, entity attacker, float damage, float deatht
        if(accuracy_isgooddamage(attacker, self))
         attacker.accuracy.(accuracy_frags[w-1]) += 1;
 
-               if(deathtype == DEATH_HURTTRIGGER && g_freezetag)
-               {
-                       PutClientInServer();
-                       count_alive_players(); // re-count players
-                       freezetag_CheckWinner();
-                       return;
-               }
-
                frag_attacker = attacker;
                frag_inflictor = inflictor;
                frag_target = self;
+               frag_deathtype = deathtype;
                MUTATOR_CALLHOOK(PlayerDies);
+
                weapon_action(self.weapon, WR_PLAYERDEATH);
 
                RemoveGrapplingHook(self);
@@ -626,19 +608,23 @@ void PlayerDamage (entity inflictor, entity attacker, float damage, float deatht
                        //WriteAngle (MSG_ONE, 80);
                }
 
-               if(defer_ClientKill_Now_TeamChange) // TODO does this work with FreezeTag?
-                       ClientKill_Now_TeamChange();
-
-               if(g_arena)
-                       Spawnqueue_Unmark(self);
+               if(defer_ClientKill_Now_TeamChange)
+                       ClientKill_Now_TeamChange(); // can turn player into spectator
 
-               if(g_freezetag)
+               // 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;
 
                // when we get here, player actually dies
-               // clear waypoints (do this AFTER FreezeTag)
+
+               // clear waypoints
                WaypointSprite_PlayerDead();
+               // throw a weapon
+               SpawnThrownWeapon (self.origin + (self.mins + self.maxs) * 0.5, self.switchweapon);
 
+               // become fully visible
+               self.alpha = default_player_alpha;
                // make the corpse upright (not tilted)
                self.angles_x = 0;
                self.angles_z = 0;
@@ -677,6 +663,10 @@ void PlayerDamage (entity inflictor, entity attacker, float damage, float deatht
                        self.respawn_countdown = 10; // first number to count down from is 10
                else
                        self.respawn_countdown = -1; // do not count down
+
+               if(g_lms || g_cts || autocvar_g_forced_respawn)
+                       self.respawn_flags = self.respawn_flags | RESPAWN_FORCE;
+
                self.death_time = time;
                if (random() < 0.5)
                        animdecide_setstate(self, self.anim_state | ANIMSTATE_DEAD1, TRUE);
index 2a1297d..57b1cf4 100644 (file)
@@ -303,8 +303,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)
@@ -354,7 +352,19 @@ void W_ThrowWeapon(vector velo, vector delta, float doreduce)
        Send_Notification(NOTIF_ONE, self, MSG_MULTI, ITEM_WEAPON_DROP, a, w);
 }
 
-// Bringed back weapon frame
+float forbidWeaponUse()
+{
+       if(time < game_starttime && !autocvar_sv_ready_restart_after_countdown)
+               return 1;
+       if(round_handler_IsActive() && !round_handler_IsRoundStarted())
+               return 1;
+       if(self.player_blocked)
+               return 1;
+       if(self.freezetag_frozen)
+               return 1;
+       return 0;
+}
+
 void W_WeaponFrame()
 {
        vector fo, ri, up;
@@ -362,15 +372,16 @@ void W_WeaponFrame()
        if (frametime)
                self.weapon_frametime = frametime;
 
-       if(((arena_roundbased || g_ca || g_freezetag) && time < warmup) || ((time < game_starttime) && !autocvar_sv_ready_restart_after_countdown))
-               return;
-
-       if(self.freezetag_frozen == 1)
-               return;
-
        if (!self.weaponentity || self.health < 1)
                return; // Dead player can't use weapons and injure impulse commands
 
+       if(forbidWeaponUse())
+       if(self.weaponentity.state != WS_CLEAR)
+       {
+               w_ready();
+               return;
+       }
+
        if(!self.switchweapon)
        {
                self.weapon = 0;
index d363de5..e22399f 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);
                                                Kill_Notification(NOTIF_ONE_ONLY, self, MSG_CENTER_CPID, CPID_PREVENT_JOIN);
@@ -195,6 +194,8 @@ void ClientCommand_ready(float request) // todo: anti-spam for toggling readynes
                                {
                                        if(!readyrestart_happened || autocvar_sv_ready_restart_repeatable)
                                        {
+                                               if(time < game_starttime) // game is already restarting
+                                                       return;
                                                if (self.ready) // toggle
                                                {
                                                        self.ready = FALSE;
@@ -413,10 +414,10 @@ void ClientCommand_spectate(float request)
                                
                                if(self.classname == "player" && autocvar_sv_spectate == 1) 
                                        ClientKill_TeamChange(-2); // observe
-                               
+
                                // in CA, allow a dead player to move to spectators (without that, caplayer!=0 will be moved back to the player list)
                                // note: if arena game mode is ever done properly, this needs to be removed.
-                               if(g_ca && self.caplayer && (self.classname == "spectator" || self.classname == "observer"))
+                               if(self.caplayer && (self.classname == "spectator" || self.classname == "observer"))
                                {
                                        sprint(self, "WARNING: you will spectate in the next round.\n");
                                        self.caplayer = 0;
index 5e7810a..3e564e3 100644 (file)
@@ -319,6 +319,84 @@ void VoteThink()
 //  Game logic for warmup
 // =======================
 
+// Resets the state of all clients, items, 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);
+
+       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 (IS_PLAYER(self)) {
+                               //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(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() 
 {
@@ -345,8 +423,7 @@ void ReadyRestart_force()
        checkrules_suddendeathend = checkrules_overtimesadded = checkrules_suddendeathwarning = 0;
 
        readyrestart_happened = 1;
-       game_starttime = time;
-       if(!g_ca && !g_arena) { game_starttime += RESTART_COUNTDOWN; }
+       game_starttime = time + RESTART_COUNTDOWN;
 
        // clear player attributes
        FOR_EACH_CLIENT(tmp_player)
@@ -362,19 +439,19 @@ void ReadyRestart_force()
        inWarmupStage = 0; // once the game is restarted the game is in match stage
 
        // reset the .ready status of all players (also spectators)
-       FOR_EACH_CLIENTSLOT(tmp_player) { tmp_player.ready = 0; }
+       FOR_EACH_REALCLIENT(tmp_player) { tmp_player.ready = 0; }
        readycount = 0;
        Nagger_ReadyCounted(); // NOTE: this causes a resend of that entity, and will also turn off warmup state on the client
 
        // lock teams with lockonrestart
-       if(autocvar_teamplay_lockonrestart && teamplay) 
+       if(autocvar_teamplay_lockonrestart && teamplay)
        {
                lockteams = 1;
                bprint("^1The teams are now locked.\n");
        }
 
        //initiate the restart-countdown-announcer entity
-       if(autocvar_sv_ready_restart_after_countdown && !g_ca && !g_arena)
+       if(autocvar_sv_ready_restart_after_countdown)
        {
                restart_timer = spawn();
                restart_timer.think = ReadyRestart_think;
@@ -414,10 +491,13 @@ void ReadyCount()
        float ready_needed_factor, ready_needed_count;
        float t_ready = 0, t_players = 0;
 
-       FOR_EACH_REALPLAYER(tmp_player)
+       FOR_EACH_REALCLIENT(tmp_player)
        {
-               ++t_players;
-               if(tmp_player.ready) { ++t_ready; }
+               if(IS_PLAYER(tmp_player) || tmp_player.caplayer == 1)
+               {
+                       ++t_players;
+                       if(tmp_player.ready) { ++t_ready; }
+               }
        }
 
        readycount = t_ready;
index 748b7ce..1225b6b 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 522bb2f..cb3c34f 100644 (file)
@@ -47,6 +47,9 @@ float DEAD_DEAD                               = 2;
 float  DEAD_RESPAWNABLE                        = 3;
 float  DEAD_RESPAWNING                 = 4;
 
+float  RESPAWN_FORCE                   = 1;
+float  RESPAWN_SILENT                  = 2;
+
 float  DAMAGE_NO                               = 0;
 float  DAMAGE_YES                              = 1;
 float  DAMAGE_AIM                              = 2;
index a7cd801..c5720da 100644 (file)
@@ -104,6 +104,7 @@ float server_is_dedicated;
 //.float cnt2;
 
 .float play_time;
+.float respawn_flags;
 .float respawn_time;
 .float death_time;
 .float fade_time;
@@ -446,8 +447,10 @@ string cvar_changes;
 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;
@@ -579,8 +582,6 @@ string deathmessage;
 .void (float act_state) setactive;
 .entity realowner;
 
-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
@@ -588,7 +589,6 @@ float serverflags;
 .float player_blocked;
 
 .float freezetag_frozen;
-.float freezetag_revive_progress;
 
 .entity muzzle_flash;
 .float misc_bulletcounter;     // replaces uzi & hlac bullet counter.
index 9648bcc..16a6a04 100644 (file)
@@ -120,10 +120,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 f7af47d..dec6e16 100644 (file)
@@ -121,7 +121,7 @@ void GrapplingHookThink()
                error("Owner lost the hook!\n");
                return;
        }
-       if(LostMovetypeFollow(self) || intermission_running)
+       if(LostMovetypeFollow(self) || intermission_running || (round_handler_IsActive() && !round_handler_IsRoundStarted()))
        {
                RemoveGrapplingHook(self.realowner);
                return;
@@ -299,14 +299,8 @@ 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(forbidWeaponUse()) return;
+       if(self.vehicle) return;
 
        makevectors(self.v_angle);
 
index ed194ef..d8ce88c 100644 (file)
@@ -256,16 +256,17 @@ void cvar_changes_init()
                BADCVAR("g_arena");
                BADCVAR("g_assault");
                BADCVAR("g_ca");
+               BADCVAR("g_ca_teams");
                BADCVAR("g_ctf");
                BADCVAR("g_cts");
                BADCVAR("g_dm");
                BADCVAR("g_domination");
                BADCVAR("g_domination_default_teams");
                BADCVAR("g_freezetag");
+               BADCVAR("g_freezetag_teams");
                BADCVAR("g_keepaway");
                BADCVAR("g_keyhunt");
                BADCVAR("g_keyhunt_teams");
-               BADCVAR("g_keyhunt_teams");
                BADCVAR("g_lms");
                BADCVAR("g_nexball");
                BADCVAR("g_onslaught");
@@ -359,8 +360,10 @@ void cvar_changes_init()
                BADCVAR("g_balance_teams_scorefactor");
                BADCVAR("g_ban_sync_trusted_servers");
                BADCVAR("g_ban_sync_uri");
+               BADCVAR("g_ca_teams_override");
                BADCVAR("g_ctf_ignore_frags");
                BADCVAR("g_domination_point_limit");
+               BADCVAR("g_freezetag_teams_override");
                BADCVAR("g_friendlyfire");
                BADCVAR("g_fullbrightitems");
                BADCVAR("g_fullbrightplayers");
@@ -581,8 +584,6 @@ void spawnfunc_worldspawn (void)
 
        compressShortVector_init();
 
-       allowed_to_spawn = TRUE;
-
        entity head;
        head = nextent(world);
        maxclients = 0;
@@ -783,6 +784,7 @@ void spawnfunc_worldspawn (void)
        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();
 
@@ -805,19 +807,6 @@ void spawnfunc_worldspawn (void)
 
        addstat(STAT_HAGAR_LOAD, AS_INT, hagar_load);
 
-       if(g_ca || g_freezetag)
-       {
-               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);
-       }
-       if(g_freezetag)
-       {
-               addstat(STAT_FROZEN, AS_INT, freezetag_frozen);
-               addstat(STAT_REVIVE_PROGRESS, AS_FLOAT, freezetag_revive_progress);
-       }
-
        // g_movementspeed hack
        addstat(STAT_MOVEVARS_AIRSPEEDLIMIT_NONQW, AS_FLOAT, stat_sv_airspeedlimit_nonqw);
        addstat(STAT_MOVEVARS_MAXSPEED, AS_FLOAT, stat_sv_maxspeed);
@@ -1478,7 +1467,7 @@ void DumpStats(float final)
                {
                        s = strcat(":player:see-labels:", GetPlayerScoreString(other, 0), ":");
                        s = strcat(s, ftos(rint(time - other.jointime)), ":");
-                       if(other.classname == "player" || g_arena || g_ca || g_lms)
+                       if(other.classname == "player" || g_arena || other.caplayer == 1 || g_lms)
                                s = strcat(s, ftos(other.team), ":");
                        else
                                s = strcat(s, "spectator:");
index 545aa31..fa8be67 100644 (file)
@@ -1140,8 +1140,8 @@ void readlevelcvars(void)
     if(!g_weapon_stay)
         g_weapon_stay = cvar("g_weapon_stay");
 
-       if not(inWarmupStage && !g_ca)
-               game_starttime = cvar("g_start_delay");
+       if not(inWarmupStage)
+               game_starttime = time + cvar("g_start_delay");
 
        readplayerstartcvars();
 }
index 35b2e65..d705f5d 100644 (file)
@@ -46,10 +46,22 @@ 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)
 
+MUTATOR_HOOKABLE(reset_map_global);
+       // called in reset_map
+
+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
 
@@ -59,6 +71,7 @@ MUTATOR_HOOKABLE(PlayerDies);
                entity frag_inflictor;
                entity frag_attacker;
                entity frag_target; // same as self
+               float frag_deathtype;
 
 MUTATOR_HOOKABLE(GiveFragsForKill);
        // called when someone was fragged by "self", and is expected to change frag_score to adjust scoring for the kill
diff --git a/qcsrc/server/mutators/gamemode_arena.qc b/qcsrc/server/mutators/gamemode_arena.qc
new file mode 100644 (file)
index 0000000..46b8fac
--- /dev/null
@@ -0,0 +1,278 @@
+.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_GetEndTime() > 0 && round_handler_GetEndTime() - time <= 0)
+       {
+               Send_Notification(NOTIF_ALL, world, MSG_CENTER, CENTER_ROUND_OVER);
+               Send_Notification(NOTIF_ALL, world, MSG_INFO, INFO_ROUND_OVER);
+               round_handler_Init(5, autocvar_g_arena_warmup, autocvar_g_arena_round_timelimit);
+               return 1;
+       }
+
+       if(numspawned > 1)
+               return 0;
+
+       entity champion;
+       champion = world;
+       FOR_EACH_CLIENT(e)
+       {
+               if(e.spawned && IS_PLAYER(e))
+                       champion = e;
+       }
+
+       if(champion)
+       {
+               Send_Notification(NOTIF_ALL, world, MSG_CENTER, CENTER_ROUND_PLAYER_WIN, champion.netname);
+               Send_Notification(NOTIF_ALL, world, MSG_INFO, INFO_ROUND_PLAYER_WIN, champion.netname);
+               UpdateFrags(champion, +1);
+       }
+       else
+       {
+               Send_Notification(NOTIF_ALL, world, MSG_CENTER, CENTER_ROUND_TIED);
+               Send_Notification(NOTIF_ALL, world, MSG_INFO, INFO_ROUND_TIED);
+       }
+       round_handler_Init(5, autocvar_g_arena_warmup, autocvar_g_arena_round_timelimit);
+       return 1;
+}
+
+void Arena_AddChallengers()
+{
+       entity e;
+       if(time < 2) // don't force players to spawn so early
+               return;
+       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()
+{
+       Arena_AddChallengers();
+
+       if(numspawned >= 2)
+       {
+               if(prev_numspawned > 0)
+                       Kill_Notification(NOTIF_ALL, world, MSG_CENTER_CPID, CPID_MISSING_PLAYERS);
+               prev_numspawned = -1;
+               return 1;
+       }
+
+       if(prev_numspawned != numspawned && numspawned == 1)
+       {
+               if(maxspawned - numspawned > 0)
+                       Send_Notification(NOTIF_ALL, world, MSG_CENTER, CENTER_MISSING_PLAYERS, maxspawned - numspawned);
+               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_ForbidPlayerScore_Clear)
+{
+       return 1;
+}
+
+MUTATOR_HOOKFUNCTION(arena_GiveFragsForKill)
+{
+       if(arena_roundbased)
+               frag_score = 0; // score will be given to the champion when the round ends
+       return 1;
+}
+
+MUTATOR_HOOKFUNCTION(arena_PlayerDies)
+{
+       // put dead players in the spawn queue
+       if(arena_roundbased)
+               self.respawn_flags = (RESPAWN_FORCE | RESPAWN_SILENT);
+       else
+               self.respawn_flags = RESPAWN_SILENT;
+       Spawnqueue_Unmark(self);
+       return 1;
+}
+
+MUTATOR_HOOKFUNCTION(arena_SV_StartFrame)
+{
+       if(gameover) return 1;
+       if(time <= game_starttime || !arena_roundbased)
+               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);
+               round_handler_Init(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(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;
+}
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..11330fb
--- /dev/null
@@ -0,0 +1,266 @@
+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 == NUM_TEAM_1)
+               {
+                       ++total_players;
+                       if (e.health >= 1) ++redalive;
+               }
+               else if(e.team == NUM_TEAM_2)
+               {
+                       ++total_players;
+                       if (e.health >= 1) ++bluealive;
+               }
+               else if(e.team == NUM_TEAM_3)
+               {
+                       ++total_players;
+                       if (e.health >= 1) ++yellowalive;
+               }
+               else if(e.team == NUM_TEAM_4)
+               {
+                       ++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 = NUM_TEAM_1;
+       if(bluealive >= 1)
+       {
+               if(winner_team) return 0;
+               winner_team = NUM_TEAM_2;
+       }
+       if(yellowalive >= 1)
+       {
+               if(winner_team) return 0;
+               winner_team = NUM_TEAM_3;
+       }
+       if(pinkalive >= 1)
+       {
+               if(winner_team) return 0;
+               winner_team = NUM_TEAM_4;
+       }
+       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()
+{
+       if(round_handler_GetEndTime() > 0 && round_handler_GetEndTime() - time <= 0)
+       {
+               Send_Notification(NOTIF_ALL, world, MSG_CENTER, CENTER_ROUND_OVER);
+               Send_Notification(NOTIF_ALL, world, MSG_INFO, INFO_ROUND_OVER);
+               allowed_to_spawn = FALSE;
+               round_handler_Init(5, autocvar_g_ca_warmup, autocvar_g_ca_round_timelimit);
+               return 1;
+       }
+
+       CA_count_alive_players();
+       if(CA_ALIVE_TEAMS() > 1)
+               return 0;
+
+       float winner_team = CA_GetWinnerTeam();
+       if(winner_team > 0)
+       {
+               Send_Notification(NOTIF_ALL, world, MSG_CENTER, APP_TEAM_NUM_4(winner_team, CENTER_ROUND_TEAM_WIN_));
+               Send_Notification(NOTIF_ALL, world, MSG_INFO, APP_TEAM_NUM_4(winner_team, INFO_ROUND_TEAM_WIN_));
+               TeamScore_AddToTeam(winner_team, ST_SCORE, +1);
+       }
+       else if(winner_team == -1)
+       {
+               Send_Notification(NOTIF_ALL, world, MSG_CENTER, CENTER_ROUND_TIED);
+               Send_Notification(NOTIF_ALL, world, MSG_INFO, INFO_ROUND_TIED);
+       }
+
+       allowed_to_spawn = FALSE;
+       round_handler_Init(5, autocvar_g_ca_warmup, autocvar_g_ca_round_timelimit);
+       return 1;
+}
+
+void CA_RoundStart()
+{
+       if(inWarmupStage)
+               allowed_to_spawn = TRUE;
+       else
+               allowed_to_spawn = FALSE;
+}
+
+float prev_total_players;
+float CA_CheckTeams()
+{
+       allowed_to_spawn = TRUE;
+       CA_count_alive_players();
+       if(CA_ALIVE_TEAMS_OK())
+       {
+               if(prev_total_players > 0)
+                       Kill_Notification(NOTIF_ALL, world, MSG_CENTER_CPID, CPID_MISSING_TEAMS);
+               prev_total_players = -1;
+               return 1;
+       }
+       if(prev_total_players != total_players)
+       {
+               float p1 = 0, p2 = 0, p3 = 0, p4 = 0;
+               if(!redalive) p1 = NUM_TEAM_1;
+               if(!bluealive) p2 = NUM_TEAM_2;
+               if(ca_teams >= 3)
+               if(!yellowalive) p3 = NUM_TEAM_3;
+               if(ca_teams >= 4)
+               if(!pinkalive) p4 = NUM_TEAM_4;
+               Send_Notification(NOTIF_ALL, world, MSG_CENTER, CENTER_MISSING_TEAMS, p1, p2, p3, p4);
+               prev_total_players = total_players;
+       }
+       return 0;
+}
+
+MUTATOR_HOOKFUNCTION(ca_PlayerSpawn)
+{
+       self.caplayer = 1;
+       return 1;
+}
+
+MUTATOR_HOOKFUNCTION(ca_PutClientInServer)
+{
+       if(!allowed_to_spawn)
+       {
+               self.classname = "observer";
+               if(!self.caplayer)
+               {
+                       self.caplayer = 0.5;
+                       if(clienttype(self) == CLIENTTYPE_REAL)
+                               sprint(self, "You will join the game in the next round.\n");
+               }
+       }
+       return 1;
+}
+
+MUTATOR_HOOKFUNCTION(ca_reset_map_players)
+{
+       FOR_EACH_CLIENT(self)
+       {
+               if(self.caplayer)
+               {
+                       self.classname = "player";
+                       self.caplayer = 1;
+                       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_PlayerDies)
+{
+       if(!allowed_to_spawn)
+               self.respawn_flags =  RESPAWN_SILENT;
+       return 1;
+}
+
+MUTATOR_HOOKFUNCTION(ca_ForbidPlayerScore_Clear)
+{
+       return 1;
+}
+
+MUTATOR_HOOKFUNCTION(ca_MakePlayerObserver)
+{
+       if(self.killindicator_teamchange == -2)
+               self.caplayer = 0;
+       if(self.caplayer)
+               self.frags = FRAGS_LMS_LOSER;
+       return 1;
+}
+
+MUTATOR_HOOKFUNCTION(ca_ForbidThrowCurrentWeapon)
+{
+       return 1;
+}
+
+MUTATOR_HOOKFUNCTION(ca_GiveFragsForKill)
+{
+       frag_score = 0; // score will be given to the winner team when the round ends
+       return 1;
+}
+
+void ca_Initialize()
+{
+       allowed_to_spawn = TRUE;
+
+       round_handler_Spawn(CA_CheckTeams, CA_CheckWinner, CA_RoundStart);
+       round_handler_Init(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(PlayerDies, ca_PlayerDies, CBC_ORDER_ANY);
+       MUTATOR_HOOK(ForbidPlayerScore_Clear, ca_ForbidPlayerScore_Clear, CBC_ORDER_ANY);
+       MUTATOR_HOOK(ForbidThrowCurrentWeapon, ca_ForbidThrowCurrentWeapon, CBC_ORDER_ANY);
+       MUTATOR_HOOK(GiveFragsForKill, ca_GiveFragsForKill, CBC_ORDER_FIRST);
+
+       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;
+}
diff --git a/qcsrc/server/mutators/gamemode_ca.qh b/qcsrc/server/mutators/gamemode_ca.qh
new file mode 100644 (file)
index 0000000..ab0a9d1
--- /dev/null
@@ -0,0 +1,3 @@
+// should be removed in the future, as other code should not have to care
+.float caplayer; // 0.5 if scheduled to join the next round
+
index c43a398..980a9b2 100644 (file)
-void freezetag_Initialize()
+.float freezetag_frozen_time;
+.float freezetag_frozen_timeout;
+.float freezetag_revive_progress;
+.entity freezetag_ice;
+#define ICE_MAX_ALPHA 1
+#define ICE_MIN_ALPHA 0.1
+float freezetag_teams;
+
+void freezetag_count_alive_players()
 {
-       precache_model("models/ice/ice.md3");
-       warmup = time + autocvar_g_start_delay + autocvar_g_freezetag_warmup;
-       ScoreRules_freezetag();
+       entity e;
+       total_players = redalive = bluealive = yellowalive = pinkalive = 0;
+       FOR_EACH_PLAYER(e) {
+               if(e.team == NUM_TEAM_1 && e.health >= 1)
+               {
+                       ++total_players;
+                       if (!e.freezetag_frozen) ++redalive;
+               }
+               else if(e.team == NUM_TEAM_2 && e.health >= 1)
+               {
+                       ++total_players;
+                       if (!e.freezetag_frozen) ++bluealive;
+               }
+               else if(e.team == NUM_TEAM_3 && e.health >= 1)
+               {
+                       ++total_players;
+                       if (!e.freezetag_frozen) ++yellowalive;
+               }
+               else if(e.team == NUM_TEAM_4 && e.health >= 1)
+               {
+                       ++total_players;
+                       if (!e.freezetag_frozen) ++pinkalive;
+               }
+       }
+       FOR_EACH_REALCLIENT(e) {
+               e.redalive_stat = redalive;
+               e.bluealive_stat = bluealive;
+               e.yellowalive_stat = yellowalive;
+               e.pinkalive_stat = pinkalive;
+       }
 }
+#define FREEZETAG_ALIVE_TEAMS() ((redalive > 0) + (bluealive > 0) + (yellowalive > 0) + (pinkalive > 0))
+#define FREEZETAG_ALIVE_TEAMS_OK() (FREEZETAG_ALIVE_TEAMS() == freezetag_teams)
 
-void freezetag_CheckWinner()
+float prev_total_players;
+float freezetag_CheckTeams()
 {
-       if(time <= game_starttime) // game didn't even start yet! nobody can win in that case.
-               return;
+       if(FREEZETAG_ALIVE_TEAMS_OK())
+       {
+               if(prev_total_players > 0)
+                       Kill_Notification(NOTIF_ALL, world, MSG_CENTER_CPID, CPID_MISSING_TEAMS);
+               prev_total_players = -1;
+               return 1;
+       }
+       if(prev_total_players != total_players)
+       {
+               float p1 = 0, p2 = 0, p3 = 0, p4 = 0;
+               if(!redalive) p1 = NUM_TEAM_1;
+               if(!bluealive) p2 = NUM_TEAM_2;
+               if(freezetag_teams >= 3)
+               if(!yellowalive) p3 = NUM_TEAM_3;
+               if(freezetag_teams >= 4)
+               if(!pinkalive) p4 = NUM_TEAM_4;
+               Send_Notification(NOTIF_ALL, world, MSG_CENTER, CENTER_MISSING_TEAMS, p1, p2, p3, p4);
+               prev_total_players = total_players;
+       }
+       return 0;
+}
 
-       if(next_round || (time > warmup - autocvar_g_freezetag_warmup && time < warmup))
-               return; // already waiting for next round to start
+float freezetag_getWinnerTeam()
+{
+       float winner_team = 0;
+       if(redalive >= 1)
+               winner_team = NUM_TEAM_1;
+       if(bluealive >= 1)
+       {
+               if(winner_team) return 0;
+               winner_team = NUM_TEAM_2;
+       }
+       if(yellowalive >= 1)
+       {
+               if(winner_team) return 0;
+               winner_team = NUM_TEAM_3;
+       }
+       if(pinkalive >= 1)
+       {
+               if(winner_team) return 0;
+               winner_team = NUM_TEAM_4;
+       }
+       if(winner_team)
+               return winner_team;
+       return -1; // no player left
+}
 
-       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; // we still have active players on two or more teams, nobody won yet
+float freezetag_CheckWinner()
+{
+       entity e;
+       if(round_handler_GetEndTime() > 0 && round_handler_GetEndTime() - time <= 0)
+       {
+               Send_Notification(NOTIF_ALL, world, MSG_CENTER, CENTER_ROUND_OVER);
+               Send_Notification(NOTIF_ALL, world, MSG_INFO, INFO_ROUND_OVER);
+               FOR_EACH_PLAYER(e)
+                       e.freezetag_frozen_timeout = 0;
+               round_handler_Init(5, autocvar_g_freezetag_warmup, autocvar_g_freezetag_round_timelimit);
+               return 1;
+       }
 
-       entity e, winner;
-       winner = world;
+       if(FREEZETAG_ALIVE_TEAMS() > 1)
+               return 0;
 
-       FOR_EACH_PLAYER(e)
+       float winner_team;
+       winner_team = freezetag_getWinnerTeam();
+       if(winner_team > 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
-               }
+               Send_Notification(NOTIF_ALL, world, MSG_CENTER, APP_TEAM_NUM_4(winner_team, CENTER_ROUND_TEAM_WIN_));
+               Send_Notification(NOTIF_ALL, world, MSG_INFO, APP_TEAM_NUM_4(winner_team, INFO_ROUND_TEAM_WIN_));
+               TeamScore_AddToTeam(winner_team, ST_SCORE, +1);
        }
-
-       if(winner != world) // just in case a winner wasn't found
+       else if(winner_team == -1)
        {
-               Send_Notification(NOTIF_ALL, world, MSG_CENTER, APP_TEAM_NUM_4(winner.team, CENTER_FREEZETAG_ROUND_WIN_));
-               Send_Notification(NOTIF_ALL, world, MSG_INFO, APP_TEAM_NUM_4(winner.team, INFO_FREEZETAG_ROUND_WIN_));
-               TeamScore_AddToTeam(winner.team, ST_SCORE, +1);
+               Send_Notification(NOTIF_ALL, world, MSG_CENTER, CENTER_ROUND_TIED);
+               Send_Notification(NOTIF_ALL, world, MSG_INFO, INFO_ROUND_TIED);
        }
 
-       next_round = time + 5;
+       FOR_EACH_PLAYER(e)
+               e.freezetag_frozen_timeout = 0;
+       round_handler_Init(5, autocvar_g_freezetag_warmup, autocvar_g_freezetag_round_timelimit);
+       return 1;
 }
 
 // this is needed to allow the player to turn his view around (fixangle can't
@@ -52,13 +137,36 @@ void freezetag_Ice_Think()
        self.nextthink = time;
 }
 
+void freezetag_Add_Score(entity attacker)
+{
+       if(attacker == self)
+       {
+               // you froze your own dumb self
+               // counted as "suicide" already
+               PlayerScore_Add(self, SP_SCORE, -1);
+       }
+       else if(IS_PLAYER(attacker))
+       {
+               // got frozen by an enemy
+               // counted as "kill" and "death" already
+               PlayerScore_Add(self, SP_SCORE, -1);
+               PlayerScore_Add(attacker, SP_SCORE, +1);
+       }
+       // else nothing - got frozen by the game type rules themselves
+}
+
 void freezetag_Freeze(entity attacker)
 {
        if(self.freezetag_frozen)
                return;
        self.freezetag_frozen = 1;
+       self.freezetag_frozen_time = time;
        self.freezetag_revive_progress = 0;
        self.health = 1;
+       if(autocvar_g_freezetag_frozen_maxtime > 0)
+               self.freezetag_frozen_timeout = time + autocvar_g_freezetag_frozen_maxtime;
+
+       freezetag_count_alive_players();
 
        entity ice;
        ice = spawn();
@@ -67,53 +175,31 @@ void freezetag_Freeze(entity attacker)
        ice.think = freezetag_Ice_Think;
        ice.nextthink = time;
        ice.frame = floor(random() * 21); // ice model has 20 different looking frames
+       ice.alpha = ICE_MAX_ALPHA;
+       ice.colormod = Team_ColorRGB(self.team);
+       ice.glowmod = ice.colormod;
        setmodel(ice, "models/ice/ice.md3");
 
-       entity oldself;
-       oldself = self;
-       self = ice;
-       freezetag_Ice_Think();
-       self = oldself;
+       self.freezetag_ice = ice;
 
        RemoveGrapplingHook(self);
 
        // add waypoint
        WaypointSprite_Spawn("freezetag_frozen", 0, 0, self, '0 0 64', world, self.team, self, waypointsprite_attached, TRUE, RADARICON_WAYPOINT, '0.25 0.90 1');
 
-       if(attacker == self)
-       {
-               // you froze your own dumb self
-               // counted as "suicide" already
-               PlayerScore_Add(self, SP_SCORE, -1);
-       }
-       else if(attacker.classname == "player")
-       {
-               // got frozen by an enemy
-               // counted as "kill" and "death" already
-               PlayerScore_Add(self, SP_SCORE, -1);
-               PlayerScore_Add(attacker, SP_SCORE, +1);
-       }
-       else
-       {
-               // nothing - got frozen by the game type rules themselves
-       }
+       freezetag_Add_Score(attacker);
 }
 
 void freezetag_Unfreeze(entity attacker)
 {
        self.freezetag_frozen = 0;
+       self.freezetag_frozen_time = 0;
+       self.freezetag_frozen_timeout = 0;
        self.freezetag_revive_progress = 0;
-       self.health = autocvar_g_balance_health_start;
 
-       // remove the ice block
-       entity ice;
-       for(ice = world; (ice = find(ice, classname, "freezetag_ice")); ) if(ice.owner == self)
-       {
-               remove(ice);
-               break;
-       }
+       remove(self.freezetag_ice);
+       self.freezetag_ice = world;
 
-       // remove waypoint
        if(self.waypointsprite_attached)
                WaypointSprite_Kill(self.waypointsprite_attached);
 }
@@ -229,44 +315,45 @@ void havocbot_role_ft_freeing()
 
 MUTATOR_HOOKFUNCTION(freezetag_RemovePlayer)
 {
-       if(self.freezetag_frozen == 0 && self.health >= 1)
-       {
-               if(self.team == NUM_TEAM_1)
-                       --redalive;
-               else if(self.team == NUM_TEAM_2)
-                       --bluealive;
-               else if(self.team == NUM_TEAM_3)
-                       --yellowalive;
-               else if(self.team == NUM_TEAM_4)
-                       --pinkalive;
-               --totalalive;
-       }
-
-       if(total_players > 2) // only check for winners if we had more than two players (one of them left, don't let the other player win just because of that)
-               freezetag_CheckWinner();
-
+       self.health = 0; // neccessary to update correctly alive stats
        freezetag_Unfreeze(world);
-
+       freezetag_count_alive_players();
        return 1;
 }
 
 MUTATOR_HOOKFUNCTION(freezetag_PlayerDies)
 {
-       if(self.freezetag_frozen == 0)
+       if(round_handler_IsActive())
+       if(round_handler_CountdownRunning())
        {
-               if(self.team == NUM_TEAM_1)
-                       --redalive;
-               else if(self.team == NUM_TEAM_2)
-                       --bluealive;
-               else if(self.team == NUM_TEAM_3)
-                       --yellowalive;
-               else if(self.team == NUM_TEAM_4)
-                       --pinkalive;
-               --totalalive;
+               if(self.freezetag_frozen)
+                       freezetag_Unfreeze(world);
+               freezetag_count_alive_players();
+               return 1; // let the player die so that he can respawn whenever he wants
+       }
 
-               freezetag_Freeze(frag_attacker);
+       // Cases DEATH_TEAMCHANGE and DEATH_AUTOTEAMCHANGE are needed to fix a bug whe
+       // you succeed changing team through the menu: you both really die (gibbing) and get frozen
+       if(ITEM_DAMAGE_NEEDKILL(frag_deathtype)
+               || frag_deathtype == DEATH_TEAMCHANGE || frag_deathtype == DEATH_AUTOTEAMCHANGE)
+       {
+               // let the player die, he will be automatically frozen when he respawns
+               if(!self.freezetag_frozen)
+               {
+                       freezetag_Add_Score(frag_attacker);
+                       freezetag_count_alive_players();
+               }
+               else
+                       freezetag_Unfreeze(world); // remove ice
+               self.freezetag_frozen_timeout = -2; // freeze on respawn
+               return 1;
        }
 
+       if(self.freezetag_frozen)
+               return 1;
+
+       freezetag_Freeze(frag_attacker);
+
        if(frag_attacker == frag_target || frag_attacker == world)
        {
                if(frag_target.classname == STR_PLAYER)
@@ -284,22 +371,24 @@ MUTATOR_HOOKFUNCTION(freezetag_PlayerDies)
 
        frag_target.health = 1; // "respawn" the player :P
 
-       freezetag_CheckWinner();
-
        return 1;
 }
 
 MUTATOR_HOOKFUNCTION(freezetag_PlayerSpawn)
 {
-       freezetag_Unfreeze(world); // start by making sure that all ice blocks are removed
+       if(self.freezetag_frozen_timeout == -1) // if PlayerSpawn is called by reset_map_players
+               return 1; // do nothing, round is starting right now
 
-       if(total_players == 1 && time > game_starttime) // only one player active on server, start a new match immediately
-       if(!next_round && warmup && (time < warmup - autocvar_g_freezetag_warmup || time > warmup)) // not awaiting next round
+       if(self.freezetag_frozen_timeout == -2) // player was dead
        {
-               next_round = time;
+               freezetag_Freeze(world);
                return 1;
        }
-       if(warmup && time > warmup) // spawn too late, freeze player
+
+       freezetag_count_alive_players();
+
+       if(round_handler_IsActive())
+       if(round_handler_IsRoundStarted())
        {
                Send_Notification(NOTIF_ONE, self, MSG_CENTER, CENTER_FREEZETAG_SPAWN_LATE);
                freezetag_Freeze(world);
@@ -308,33 +397,69 @@ MUTATOR_HOOKFUNCTION(freezetag_PlayerSpawn)
        return 1;
 }
 
+MUTATOR_HOOKFUNCTION(freezetag_reset_map_players)
+{
+       FOR_EACH_PLAYER(self)
+       {
+               if (self.freezetag_frozen)
+                       freezetag_Unfreeze(world);
+               self.freezetag_frozen_timeout = -1;
+               PutClientInServer();
+               self.freezetag_frozen_timeout = 0;
+       }
+       freezetag_count_alive_players();
+       return 1;
+}
+
 MUTATOR_HOOKFUNCTION(freezetag_GiveFragsForKill)
 {
        frag_score = 0; // no frags counted in Freeze Tag
        return 1;
 }
 
+.float reviving; // temp var
 MUTATOR_HOOKFUNCTION(freezetag_PlayerPreThink)
 {
        float n;
-       vector revive_extra_size;
 
-       revive_extra_size = '1 1 1' * autocvar_g_freezetag_revive_extra_size;
+       if(gameover)
+               return 1;
+
+       if(self.freezetag_frozen)
+       {
+               // keep health = 1
+               self.pauseregen_finished = time + autocvar_g_balance_pause_health_regen;
+       }
+
+       if(round_handler_IsActive())
+       if(!round_handler_IsRoundStarted())
+               return 1;
 
        entity o;
        o = world;
-       n = 0;
-       FOR_EACH_PLAYER(other) if(self != other)
+       if(self.freezetag_frozen_timeout > 0 && time < self.freezetag_frozen_timeout)
+               self.freezetag_ice.alpha = ICE_MIN_ALPHA + (ICE_MAX_ALPHA - ICE_MIN_ALPHA) * (self.freezetag_frozen_timeout - time) / (self.freezetag_frozen_timeout - self.freezetag_frozen_time);
+
+       if(self.freezetag_frozen_timeout > 0 && time >= self.freezetag_frozen_timeout)
+               n = -1;
+       else
        {
-               if(other.freezetag_frozen == 0)
+               vector revive_extra_size = '1 1 1' * autocvar_g_freezetag_revive_extra_size;
+               n = 0;
+               FOR_EACH_PLAYER(other) if(self != other)
                {
-                       if(other.team == self.team)
+                       if(other.freezetag_frozen == 0)
                        {
-                               if(boxesoverlap(self.absmin - revive_extra_size, self.absmax + revive_extra_size, other.absmin, other.absmax))
+                               if(other.team == self.team)
                                {
-                                       if(!o)
-                                               o = other;
-                                       ++n;
+                                       if(boxesoverlap(self.absmin - revive_extra_size, self.absmax + revive_extra_size, other.absmin, other.absmax))
+                                       {
+                                               if(!o)
+                                                       o = other;
+                                               if(self.freezetag_frozen)
+                                                       other.reviving = TRUE;
+                                               ++n;
+                                       }
                                }
                        }
                }
@@ -342,44 +467,42 @@ MUTATOR_HOOKFUNCTION(freezetag_PlayerPreThink)
 
        if(n && self.freezetag_frozen) // OK, there is at least one teammate reviving us
        {
-               self.freezetag_revive_progress = bound(0, self.freezetag_revive_progress + frametime * autocvar_g_freezetag_revive_speed, 1);
+               self.freezetag_revive_progress = bound(0, self.freezetag_revive_progress + frametime * max(1/60, autocvar_g_freezetag_revive_speed), 1);
                self.health = max(1, self.freezetag_revive_progress * autocvar_g_balance_health_start);
 
                if(self.freezetag_revive_progress >= 1)
                {
                        freezetag_Unfreeze(self);
+                       freezetag_count_alive_players();
+
+                       if(n == -1)
+                       {
+                               Send_Notification(NOTIF_ONE, self, MSG_CENTER, CENTER_FREEZETAG_AUTO_REVIVED, autocvar_g_freezetag_frozen_maxtime);
+                               Send_Notification(NOTIF_ALL, world, MSG_INFO, INFO_FREEZETAG_AUTO_REVIVED, self.netname, autocvar_g_freezetag_frozen_maxtime);
+                               return 1;
+                       }
 
                        // EVERY team mate nearby gets a point (even if multiple!)
-                       FOR_EACH_PLAYER(other) if(self != other)
+                       FOR_EACH_PLAYER(other)
                        {
-                               if(other.freezetag_frozen == 0)
+                               if(other.reviving)
                                {
-                                       if(other.team == self.team)
-                                       {
-                                               if(boxesoverlap(self.absmin - revive_extra_size, self.absmax + revive_extra_size, other.absmin, other.absmax))
-                                               {
-                                                       PlayerScore_Add(other, SP_FREEZETAG_REVIVALS, +1);
-                                                       PlayerScore_Add(other, SP_SCORE, +1);
-                                               }
-                                       }
+                                       PlayerScore_Add(other, SP_FREEZETAG_REVIVALS, +1);
+                                       PlayerScore_Add(other, SP_SCORE, +1);
                                }
                        }
 
                        Send_Notification(NOTIF_ONE, self, MSG_CENTER, CENTER_FREEZETAG_REVIVED, o.netname);
                        Send_Notification(NOTIF_ONE, o, MSG_CENTER, CENTER_FREEZETAG_REVIVE, self.netname);
-                       Send_Notification(NOTIF_ALL, world, MSG_INFO, INFO_FREEZETAG_REVIVE, self.netname, o.netname);
+                       Send_Notification(NOTIF_ALL, world, MSG_INFO, INFO_FREEZETAG_REVIVED, self.netname, o.netname);
                }
 
-               // now find EVERY teammate within reviving radius, set their revive_progress values correct
-               FOR_EACH_PLAYER(other) if(self != other)
+               FOR_EACH_PLAYER(other)
                {
-                       if(other.freezetag_frozen == 0)
+                       if(other.reviving)
                        {
-                               if(other.team == self.team)
-                               {
-                                       if(boxesoverlap(self.absmin - revive_extra_size, self.absmax + revive_extra_size, other.absmin, other.absmax))
-                                               other.freezetag_revive_progress = self.freezetag_revive_progress;
-                               }
+                               other.freezetag_revive_progress = self.freezetag_revive_progress;
+                               other.reviving = FALSE;
                        }
                }
        }
@@ -408,15 +531,12 @@ MUTATOR_HOOKFUNCTION(freezetag_PlayerPhysics)
 
 MUTATOR_HOOKFUNCTION(freezetag_PlayerDamage_Calculate)
 {
-    if(g_freezetag)
-    {
-        if(frag_target.freezetag_frozen == 1 && frag_deathtype != DEATH_HURTTRIGGER)
-        {
-            frag_damage = 0;
-            frag_force = frag_force * autocvar_g_freezetag_frozen_force;
-        }
-    }
-    return 1;
+       if(frag_target.freezetag_frozen && frag_deathtype != DEATH_HURTTRIGGER)
+       {
+               frag_damage = 0;
+               frag_force = frag_force * autocvar_g_freezetag_frozen_force;
+       }
+       return 1;
 }
 
 MUTATOR_HOOKFUNCTION(freezetag_ForbidThrowCurrentWeapon)
@@ -426,6 +546,13 @@ MUTATOR_HOOKFUNCTION(freezetag_ForbidThrowCurrentWeapon)
        return 0;
 }
 
+MUTATOR_HOOKFUNCTION(freezetag_ItemTouch)
+{
+       if (other.freezetag_frozen)
+               return 1;
+       return 0;
+}
+
 MUTATOR_HOOKFUNCTION(freezetag_BotRoles)
 {
        if not(self.deadflag)
@@ -435,22 +562,60 @@ MUTATOR_HOOKFUNCTION(freezetag_BotRoles)
                else
                        self.havocbot_role = havocbot_role_ft_offense;
        }
-       
+
        return TRUE;
 }
 
+MUTATOR_HOOKFUNCTION(freezetag_SpectateCopy)
+{
+       self.freezetag_frozen = other.freezetag_frozen;
+       self.freezetag_revive_progress = other.freezetag_revive_progress;
+       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;
+}
+
+void freezetag_Initialize()
+{
+       precache_model("models/ice/ice.md3");
+       ScoreRules_freezetag();
+
+       round_handler_Spawn(freezetag_CheckTeams, freezetag_CheckWinner, func_null);
+       round_handler_Init(5, autocvar_g_freezetag_warmup, autocvar_g_freezetag_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);
+
+       addstat(STAT_FROZEN, AS_INT, freezetag_frozen);
+       addstat(STAT_REVIVE_PROGRESS, AS_FLOAT, freezetag_revive_progress);
+}
+
 MUTATOR_DEFINITION(gamemode_freezetag)
 {
        MUTATOR_HOOK(MakePlayerObserver, freezetag_RemovePlayer, CBC_ORDER_ANY);
        MUTATOR_HOOK(ClientDisconnect, freezetag_RemovePlayer, CBC_ORDER_ANY);
        MUTATOR_HOOK(PlayerDies, freezetag_PlayerDies, CBC_ORDER_ANY);
        MUTATOR_HOOK(PlayerSpawn, freezetag_PlayerSpawn, CBC_ORDER_ANY);
+       MUTATOR_HOOK(reset_map_players, freezetag_reset_map_players, CBC_ORDER_ANY);
        MUTATOR_HOOK(GiveFragsForKill, freezetag_GiveFragsForKill, CBC_ORDER_FIRST);
        MUTATOR_HOOK(PlayerPreThink, freezetag_PlayerPreThink, CBC_ORDER_FIRST);
        MUTATOR_HOOK(PlayerPhysics, freezetag_PlayerPhysics, CBC_ORDER_FIRST);
        MUTATOR_HOOK(PlayerDamage_Calculate, freezetag_PlayerDamage_Calculate, CBC_ORDER_ANY);
-       MUTATOR_HOOK(ForbidThrowCurrentWeapon, freezetag_ForbidThrowCurrentWeapon, CBC_ORDER_FIRST); //first, last or any? dunno.
+       MUTATOR_HOOK(ForbidThrowCurrentWeapon, freezetag_ForbidThrowCurrentWeapon, CBC_ORDER_ANY);
+       MUTATOR_HOOK(ItemTouch, freezetag_ItemTouch, 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 97d2dcd..13a06c3 100644 (file)
@@ -479,7 +479,7 @@ void kh_FinishRound()  // runs when a team captures the keys
                kh_Key_Remove(key);
        kh_no_radar_circles = FALSE;
 
-       Send_Notification(NOTIF_ALL, world, MSG_CENTER, CENTER_ARENA_ROUNDSTART, autocvar_g_balance_keyhunt_delay_round);
+       Send_Notification(NOTIF_ALL, world, MSG_CENTER, CENTER_KEYHUNT_ROUNDSTART, autocvar_g_balance_keyhunt_delay_round);
        kh_Controller_SetThink(autocvar_g_balance_keyhunt_delay_round, kh_StartRound);
 }
 
@@ -857,7 +857,7 @@ void kh_WaitForPlayers()  // delay start of the round until enough players are p
        float p1 = kh_CheckPlayers(0), p2 = kh_CheckPlayers(1), p3 = kh_CheckPlayers(2), p4 = kh_CheckPlayers(3);
        if not(p1 || p2 || p3 || p4)
        {
-               Send_Notification(NOTIF_ALL, world, MSG_CENTER, CENTER_ARENA_ROUNDSTART, autocvar_g_balance_keyhunt_delay_round);
+               Send_Notification(NOTIF_ALL, world, MSG_CENTER, CENTER_KEYHUNT_ROUNDSTART, autocvar_g_balance_keyhunt_delay_round);
                kh_Controller_SetThink(autocvar_g_balance_keyhunt_delay_round, kh_StartRound);
        }
        else
index bda701f..611f7f0 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(float t, kh_Think_t func);
index a351f93..5a05035 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 cf90b95..7665cc8 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 157aa38..edef590 100644 (file)
@@ -35,6 +35,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_domination.qh
 mutators/gamemode_keyhunt.qh // TODO fix this
@@ -85,6 +87,8 @@ antilag.qh
 
 playerdemo.qh
 
+round_handler.qh
+
 // singleplayer stuff
 item_key.qh
 secret.qh
@@ -102,7 +106,6 @@ g_subs.qc
 g_tetris.qc
 
 //runematch.qc
-arena.qc
 
 g_violence.qc
 g_damage.qc
@@ -211,9 +214,13 @@ anticheat.qc
 cheats.qc
 playerstats.qc
 
+round_handler.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
diff --git a/qcsrc/server/round_handler.qc b/qcsrc/server/round_handler.qc
new file mode 100644 (file)
index 0000000..af69e8e
--- /dev/null
@@ -0,0 +1,116 @@
+void round_handler_Think()
+{
+       float f;
+
+       if(time < game_starttime)
+       {
+               round_handler_Reset(game_starttime);
+               return;
+       }
+
+       if(gameover)
+       {
+               round_handler_Reset(0);
+               round_handler_Remove();
+               return;
+       }
+
+       if(self.wait)
+       {
+               self.wait = FALSE;
+               self.cnt = self.count + 1; // init countdown
+               round_starttime = time + self.count;
+               reset_map(TRUE);
+       }
+
+       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 == 0)
+                       {
+                               self.cnt = 0;
+                               self.round_endtime = (self.round_timelimit) ? time + self.round_timelimit : 0;
+                               self.nextthink = time;
+                               if(self.roundStart)
+                                       self.roundStart();
+                               return;
+                       }
+                       self.cnt = self.cnt - 1;
+               }
+               else
+               {
+                       round_handler_Reset(0);
+               }
+               self.nextthink = time + 1; // canRoundStart every second
+       }
+       else
+       {
+               if(self.canRoundEnd())
+               {
+                       // schedule a new round
+                       self.wait = TRUE;
+                       self.nextthink = time + self.delay;
+               }
+               else
+               {
+                       self.nextthink = time; // canRoundEnd every frame
+               }
+       }
+}
+
+void round_handler_Init(float the_delay, float the_count, float the_round_timelimit)
+{
+       round_handler.delay = (the_delay > 0) ? the_delay : 0;
+       round_handler.count = fabs(floor(the_count));
+       round_handler.cnt = round_handler.count + 1;
+       round_handler.round_timelimit = (the_round_timelimit > 0) ? the_round_timelimit : 0;
+}
+
+// NOTE: this is only needed because if round_handler spawns at time 1
+// gamestarttime isn't initialized yet
+void round_handler_FirstThink()
+{
+       round_starttime = max(time, game_starttime) + round_handler.count;
+       round_handler.think = round_handler_Think;
+       round_handler.nextthink = max(time, game_starttime);
+}
+
+void round_handler_Spawn(float() canRoundStart_func, float() canRoundEnd_func, void() roundStart_func)
+{
+       if(round_handler)
+       {
+               backtrace("Can't spawn round_handler again!");
+               return;
+       }
+       round_handler = spawn();
+       round_handler.classname = "round_handler";
+
+       round_handler.think = round_handler_FirstThink;
+       round_handler.canRoundStart = canRoundStart_func;
+       round_handler.canRoundEnd = canRoundEnd_func;
+       round_handler.roundStart = roundStart_func;
+       round_handler.wait = FALSE;
+       round_handler_Init(5, 5, 180);
+       round_handler.nextthink = time;
+}
+
+void round_handler_Reset(float next_think)
+{
+       round_handler.wait = FALSE;
+       if(round_handler.count)
+       if(round_handler.cnt < round_handler.count + 1)
+               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()
+{
+       remove(round_handler);
+       round_handler = world;
+}
+
diff --git a/qcsrc/server/round_handler.qh b/qcsrc/server/round_handler.qh
new file mode 100644 (file)
index 0000000..22a91dc
--- /dev/null
@@ -0,0 +1,23 @@
+entity round_handler;
+.float delay; // stores delay from round end to countdown start
+.float count; // stores initial number of the countdown
+.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_Init(float the_delay, float the_count, float the_round_timelimit);
+void round_handler_Spawn(float() canRoundStart_func, float() canRoundEnd_func, void() roundStart_func);
+void round_handler_Reset(float next_think);
+void round_handler_Remove();
+
+#define round_handler_IsActive() (round_handler != world)
+#define round_handler_AwaitingNextRound() (round_handler.wait)
+#define round_handler_CountdownRunning() (!round_handler.wait && round_handler.cnt)
+#define round_handler_IsRoundStarted() (!round_handler.wait && !round_handler.cnt)
+#define round_handler_GetEndTime() (round_handler.round_endtime)
+
index e8d5590..4c75c98 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
 
@@ -527,12 +527,12 @@ void WinningConditionHelper()
                                s = strcat(s, ":human");
                        else
                                s = strcat(s, ":bot");
-                       if(p.classname != "player" && !g_arena && !g_ca && !g_lms)
+                       if(p.classname != "player" && !g_arena && p.caplayer != 1 && !g_lms)
                                s = strcat(s, ":spectator");
                }
                else
                {
-                       if(p.classname == "player" || g_arena || g_ca || g_lms)
+                       if(p.classname == "player" || g_arena || p.caplayer == 1 || g_lms)
                                s = GetPlayerScoreString(p, 2);
                        else
                                s = "-666";
index d80b777..8d81e03 100644 (file)
@@ -209,19 +209,13 @@ void StartFrame (void)
 
        skill = autocvar_skill;
 
-       count_players();
-       if(g_ca || g_freezetag)
-               count_alive_players();
-       Arena_Warmup();
-       Spawnqueue_Check();
-
        // detect when the pre-game countdown (if any) has ended and the game has started
        game_delay = (time < game_starttime) ? TRUE : FALSE;
 
        if(game_delay_last == TRUE)
        if(game_delay == FALSE)
        if(autocvar_sv_eventlog)
-                       GameLogEcho(":startdelay_ended");
+               GameLogEcho(":startdelay_ended");
 
        game_delay_last = game_delay;
 
index 6608fc9..e307489 100644 (file)
@@ -121,10 +121,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)
@@ -132,9 +129,9 @@ void InitGameplayMode()
                ActivateTeamplay();
                fraglimit_override = autocvar_g_ca_point_limit;
                leadlimit_override = autocvar_g_ca_point_leadlimit;
-               precache_sound("ctf/red_capture.wav");
-               precache_sound("ctf/blue_capture.wav");
+               MUTATOR_ADD(gamemode_ca);
        }
+
        if(g_keyhunt)
        {
                ActivateTeamplay();
index b34d66b..611319a 100644 (file)
@@ -97,8 +97,9 @@ void minstagib_ammocheck(void)
                minstagib_stop_countdown(self);
        else if (self.ammo_cells > 0 || (self.items & IT_UNLIMITED_WEAPON_AMMO))
        {
+               if (self.minstagib_needammo)
+                       self.health = 100;
                minstagib_stop_countdown(self);
-               self.health = 100;
        }
        else
        {