]> de.git.xonotic.org Git - xonotic/xonotic-data.pk3dir.git/commitdiff
Merge branch 'master' into Mario/mute_bot_warnings
authorMario <mario.mario@y7mail.com>
Wed, 8 May 2013 02:11:00 +0000 (12:11 +1000)
committerMario <mario.mario@y7mail.com>
Wed, 8 May 2013 02:11:00 +0000 (12:11 +1000)
95 files changed:
_hud_common.cfg
_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]
gfx/menu/luminos/clearbutton_c.tga [new file with mode: 0644]
gfx/menu/luminos/clearbutton_f.tga [new file with mode: 0644]
gfx/menu/luminos/clearbutton_n.tga [new file with mode: 0644]
gfx/menu/luminos/skinvalues.txt
gfx/menu/wickedx/clearbutton_c.tga [new file with mode: 0644]
gfx/menu/wickedx/clearbutton_f.tga [new file with mode: 0644]
gfx/menu/wickedx/clearbutton_n.tga [new file with mode: 0644]
gfx/menu/wickedx/skinvalues.txt
gfx/menu/xaw/clearbutton_c.tga [new file with mode: 0644]
gfx/menu/xaw/clearbutton_f.tga [new file with mode: 0644]
gfx/menu/xaw/clearbutton_n.tga [new file with mode: 0644]
gfx/menu/xaw/skinvalues.txt
hud_luminos.cfg
hud_luminos_minimal.cfg
hud_luminos_minimal_xhair.cfg
hud_luminos_old.cfg
hud_nexuiz.cfg
qcsrc/client/Main.qc
qcsrc/client/View.qc
qcsrc/client/announcer.qc
qcsrc/client/autocvars.qh
qcsrc/client/hud.qc
qcsrc/client/hud.qh
qcsrc/client/hud_config.qc
qcsrc/common/constants.qh
qcsrc/common/mapinfo.qc
qcsrc/common/notifications.qh
qcsrc/common/teams.qh
qcsrc/common/util.qh
qcsrc/menu/item/inputbox.c
qcsrc/menu/item/inputcontainer.c
qcsrc/menu/item/nexposee.c
qcsrc/menu/skin-customizables.inc
qcsrc/menu/xonotic/charmap.c
qcsrc/menu/xonotic/dialog_multiplayer_create_advanced.c
qcsrc/menu/xonotic/dialog_multiplayer_demo.c
qcsrc/menu/xonotic/dialog_multiplayer_join.c
qcsrc/menu/xonotic/dialog_multiplayer_playersetup.c
qcsrc/menu/xonotic/dialog_settings_misc.c
qcsrc/menu/xonotic/dialog_settings_misc_cvars.c
qcsrc/menu/xonotic/inputbox.c
qcsrc/server/arena.qc [deleted file]
qcsrc/server/assault.qc [deleted file]
qcsrc/server/attic/assault.qc [new file with mode: 0644]
qcsrc/server/attic/bot/havocbot/role_assault.qc [new file with mode: 0644]
qcsrc/server/autocvars.qh
qcsrc/server/bot/aim.qc
qcsrc/server/bot/bot.qc
qcsrc/server/bot/havocbot/havocbot.qc
qcsrc/server/bot/havocbot/role_assault.qc [deleted file]
qcsrc/server/bot/havocbot/roles.qc
qcsrc/server/cl_client.qc
qcsrc/server/cl_impulse.qc
qcsrc/server/cl_physics.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_assault.qc [new file with mode: 0644]
qcsrc/server/mutators/gamemode_assault.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/gamemode_lms.qc [new file with mode: 0644]
qcsrc/server/mutators/gamemode_lms.qh [new file with mode: 0644]
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/scores_rules.qc
qcsrc/server/sv_main.qc
qcsrc/server/t_items.qc
qcsrc/server/teamplay.qc
qcsrc/server/tturrets/system/system_main.qc
qcsrc/server/vehicles/vehicles.qc
qcsrc/server/w_minstanex.qc

index ca90bc9da204f14dc8af7cf536336699cb230363..15453b7dcc018bbc8148b938a782d2717020cd27 100644 (file)
@@ -44,6 +44,8 @@ alias -hud_panel_radar_maximized "cl_cmd hud radar 0"
 alias hud_panel_radar_maximized "cl_cmd hud radar"
 
 // other hud cvars
+seta hud_panel_update_interval 2 "how often (in seconds) common panel cvars are reloaded"
+
 seta hud_showbinds 1   "what to show in the HUD to indicate certain keys to press: 0 display commands, 1 bound keys, 2 both"
 seta hud_showbinds_limit 2     "maximum number of bound keys to show for a command. 0 for unlimited"
 set _hud_showbinds_reload 0    "set it to 1 to reload binds if you changed any. It is reset to 0 automatically"
index ea7552a2a6566f7fb0631578327770a534ce4a12..9c7201d0ce78cf29cab9186b0189c8ab95c7c424 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 af8d3e83d825411b18c8b58356711a49a46be44e..b7e65c8eb8fb9aa734bf0a11f2d7b688915ca5b1 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
 
 
 // ==========
@@ -333,6 +341,7 @@ set g_keyhunt_teams 0
 // ===================
 set g_lms 0 "Last Man Standing: everyone starts with a certain amount of lives, and the survivor wins"
 set g_lms_lives_override -1
+set g_lms_extra_lives 0
 set g_lms_regenerate 0
 set g_lms_campcheck_interval 10
 set g_lms_campcheck_damage 100
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
diff --git a/gfx/menu/luminos/clearbutton_c.tga b/gfx/menu/luminos/clearbutton_c.tga
new file mode 100644 (file)
index 0000000..3de1ccb
Binary files /dev/null and b/gfx/menu/luminos/clearbutton_c.tga differ
diff --git a/gfx/menu/luminos/clearbutton_f.tga b/gfx/menu/luminos/clearbutton_f.tga
new file mode 100644 (file)
index 0000000..be0939c
Binary files /dev/null and b/gfx/menu/luminos/clearbutton_f.tga differ
diff --git a/gfx/menu/luminos/clearbutton_n.tga b/gfx/menu/luminos/clearbutton_n.tga
new file mode 100644 (file)
index 0000000..57d76c4
Binary files /dev/null and b/gfx/menu/luminos/clearbutton_n.tga differ
index 316ae273f8a88f29dab82696785e8e33a879eb85..e79dba0e099fd157d28f4c8f7ad7692b3568f7c1 100755 (executable)
@@ -189,6 +189,13 @@ COLOR_INPUTBOX_N                '1 1 1'
 COLOR_INPUTBOX_F                '1 1 1'
 MARGIN_INPUTBOX_CHARS           1
 
+// item: clear button
+//   uses "clearbutton" images
+OFFSET_CLEARBUTTON              -0.3
+COLOR_CLEARBUTTON_N                '1 1 1'
+COLOR_CLEARBUTTON_C                '1 1 1'
+COLOR_CLEARBUTTON_F                '1 1 1'
+
 // item: key grabber
 COLOR_KEYGRABBER_TITLES         '1 1 1'
 ALPHA_KEYGRABBER_TITLES         1
diff --git a/gfx/menu/wickedx/clearbutton_c.tga b/gfx/menu/wickedx/clearbutton_c.tga
new file mode 100644 (file)
index 0000000..26bc0ef
Binary files /dev/null and b/gfx/menu/wickedx/clearbutton_c.tga differ
diff --git a/gfx/menu/wickedx/clearbutton_f.tga b/gfx/menu/wickedx/clearbutton_f.tga
new file mode 100644 (file)
index 0000000..e857381
Binary files /dev/null and b/gfx/menu/wickedx/clearbutton_f.tga differ
diff --git a/gfx/menu/wickedx/clearbutton_n.tga b/gfx/menu/wickedx/clearbutton_n.tga
new file mode 100644 (file)
index 0000000..26bc0ef
Binary files /dev/null and b/gfx/menu/wickedx/clearbutton_n.tga differ
index 3259d7122afe644cccbbcfd775cef69a60c2bc73..15605128588e79c4e3c73c4f647a3e518837ea50 100755 (executable)
@@ -189,6 +189,13 @@ COLOR_INPUTBOX_N                '1 1 1'
 COLOR_INPUTBOX_F                '1 1 1'
 MARGIN_INPUTBOX_CHARS           1
 
+// item: clear button
+//   uses "clearbutton" images
+OFFSET_CLEARBUTTON              -0.5
+COLOR_CLEARBUTTON_N                '1 1 1'
+COLOR_CLEARBUTTON_C                '1 1 1'
+COLOR_CLEARBUTTON_F                '1 1 1'
+
 // item: key grabber
 COLOR_KEYGRABBER_TITLES         '1 1 1'
 ALPHA_KEYGRABBER_TITLES         1
diff --git a/gfx/menu/xaw/clearbutton_c.tga b/gfx/menu/xaw/clearbutton_c.tga
new file mode 100644 (file)
index 0000000..d8bfea5
Binary files /dev/null and b/gfx/menu/xaw/clearbutton_c.tga differ
diff --git a/gfx/menu/xaw/clearbutton_f.tga b/gfx/menu/xaw/clearbutton_f.tga
new file mode 100644 (file)
index 0000000..338ac1f
Binary files /dev/null and b/gfx/menu/xaw/clearbutton_f.tga differ
diff --git a/gfx/menu/xaw/clearbutton_n.tga b/gfx/menu/xaw/clearbutton_n.tga
new file mode 100644 (file)
index 0000000..60ee8c8
Binary files /dev/null and b/gfx/menu/xaw/clearbutton_n.tga differ
index 36db25d5cd7cf18f8a538437059360214194d49e..3bd555f031ed32255e35cec19596893bfc5597dc 100644 (file)
@@ -129,6 +129,13 @@ COLOR_INPUTBOX_N                '1 1 1'
 COLOR_INPUTBOX_F                '1 1 1'
 MARGIN_INPUTBOX_CHARS           1
 
+// item: clear button
+//   uses "clearbutton" images
+OFFSET_CLEARBUTTON              0
+COLOR_CLEARBUTTON_N                '1 1 1'
+COLOR_CLEARBUTTON_C                '1 1 1'
+COLOR_CLEARBUTTON_F                '1 1 1'
+
 // item: key grabber
 COLOR_KEYGRABBER_TITLES         '1 1 1'
 ALPHA_KEYGRABBER_TITLES         1
index bb0c97e0998af1f8a4d88cf7f3aef2e236f9e52b..f99494a1cc154d5c39115e1c347d73fc73de8d39 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 775ddaf8f16f3779f77ace00f834c7c4015a418b..bfa0c0b3e4c510536133ebd520010576001b14ea 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 b7a208dbdbccb0a924fe50bcd37f9b7e460685c7..27ca9ab80d0e0133937453d8314579a433b388de 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 2e718b288b240dacd582d711835b0ba3ed16e14c..3a489a04e44c95dd2b1e6aef6379bfba7c4a96c3 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 d4e71d876268cac6b7cccd8e60b9bb7573563ca0..57053411d74d9e91fabc67c42a294b6f6574ad39 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 ecc2000d7ccae32447fa4e78e75460a26e8f1f5a..7a11300ea9f1487a177a00192964bfda94413a34 100644 (file)
@@ -156,7 +156,8 @@ void CSQC_Init(void)
        CALL_ACCUMULATED_FUNCTION(RegisterGametypes);
        CALL_ACCUMULATED_FUNCTION(RegisterNotifications);
        CALL_ACCUMULATED_FUNCTION(RegisterDeathtypes);
-       
+       CALL_ACCUMULATED_FUNCTION(RegisterHUD_Panels);
+
        WaypointSprite_Load();
 
        // precaches
@@ -210,7 +211,6 @@ void CSQC_Init(void)
 
        hud_skin_path = strzone(strcat("gfx/hud/", autocvar_hud_skin));
        hud_configure_prev = -1;
-       tab_panel = -1;
 
        draw_currentSkin = strzone(strcat("gfx/menu/", cvar_string("menu_skin")));
 }
index 4f00c55341639fbce6fd13122c29384a1db910b6..d27fd4d98e8927cddc2fa255496a39124c2c27af 100644 (file)
@@ -384,6 +384,7 @@ float checkfail[16];
 #define BUTTON_3 4
 #define BUTTON_4 8
 float cl_notice_run();
+float prev_myteam;
 void CSQC_UpdateView(float w, float h)
 {
        entity e;
@@ -442,6 +443,14 @@ void CSQC_UpdateView(float w, float h)
 #endif
                myteam = GetPlayerColor(player_localentnum - 1);
 
+       if(myteam != prev_myteam)
+       {
+               myteamcolors = colormapPaletteColor(myteam, 1);
+               for(i = 0; i < HUD_PANEL_NUM; ++i)
+                       hud_panel[i].update_time = time;
+               prev_myteam = myteam;
+       }
+
        ticrate = getstatf(STAT_MOVEVARS_TICRATE) * getstatf(STAT_MOVEVARS_TIMESCALE);
 
        float is_dead = (getstati(STAT_HEALTH) <= 0);
@@ -886,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);
@@ -1071,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 0409aea8bcdee9ca80b59562ba10ef1ae742e2d0..7a4ed9223ad090d0549c5453e3e930819e9281c7 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 150efb1708ca705054fd00d2c30bbb20d6c18091..c13e2bf6e837ccb7a8b81817398470b5997d96c0 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;
@@ -302,6 +304,7 @@ float autocvar_hud_panel_score;
 float autocvar_hud_panel_score_rankings;
 float autocvar_hud_panel_timer;
 float autocvar_hud_panel_timer_increment;
+float autocvar_hud_panel_update_interval;
 float autocvar_hud_panel_vote;
 float autocvar_hud_panel_vote_alreadyvoted_alpha;
 string autocvar_hud_panel_vote_bg_alpha;
index bf72c2f467bd9b08551278e36b8eb799a47e7d4b..469fe67fa27d1524e8f541adaf86602b46f3be90 100644 (file)
@@ -487,11 +487,9 @@ void HUD_Weapons(void)
                        return;
                }
        }
-       else
-               hud_configure_active_panel = HUD_PANEL_WEAPONS;
 
        // update generic hud functions
-       HUD_Panel_UpdateCvars(weapons);
+       HUD_Panel_UpdateCvars();
        HUD_Panel_ApplyFadeAlpha();
 
        draw_beginBoldFont();
@@ -950,10 +948,8 @@ void HUD_Ammo(void)
                if(!autocvar_hud_panel_ammo) return;
                if(spectatee_status == -1) return;
        }
-       else
-               hud_configure_active_panel = HUD_PANEL_AMMO;
 
-       HUD_Panel_UpdateCvars(ammo);
+       HUD_Panel_UpdateCvars();
        HUD_Panel_ApplyFadeAlpha();
 
        draw_beginBoldFont();
@@ -1154,14 +1150,12 @@ void HUD_Powerups(void)
        }
        else
        {
-               hud_configure_active_panel = HUD_PANEL_POWERUPS;
-
                strength_time = 15;
                shield_time = 27;
                superweapons_time = 13;
        }
 
-       HUD_Panel_UpdateCvars(powerups);
+       HUD_Panel_UpdateCvars();
        HUD_Panel_ApplyFadeAlpha();
 
        draw_beginBoldFont();
@@ -1397,14 +1391,12 @@ void HUD_HealthArmor(void)
        }
        else
        {
-               hud_configure_active_panel = HUD_PANEL_HEALTHARMOR;
-
                health = 150;
                armor = 75;
                fuel = 20;
        }
 
-       HUD_Panel_UpdateCvars(healtharmor);
+       HUD_Panel_UpdateCvars();
        HUD_Panel_ApplyFadeAlpha();
        vector pos, mySize;
        pos = panel_pos;
@@ -1656,10 +1648,8 @@ void HUD_Notify(void)
        {
                if(!autocvar_hud_panel_notify) return;
        }
-       else
-               hud_configure_active_panel = HUD_PANEL_NOTIFY;
 
-       HUD_Panel_UpdateCvars(notify);
+       HUD_Panel_UpdateCvars();
        HUD_Panel_ApplyFadeAlpha();
        vector pos, mySize;
        pos = panel_pos;
@@ -1795,10 +1785,8 @@ void HUD_Timer(void)
        {
                if(!autocvar_hud_panel_timer) return;
        }
-       else
-               hud_configure_active_panel = HUD_PANEL_TIMER;
 
-       HUD_Panel_UpdateCvars(timer);
+       HUD_Panel_UpdateCvars();
        HUD_Panel_ApplyFadeAlpha();
 
        draw_beginBoldFont();
@@ -1865,12 +1853,10 @@ void HUD_Radar(void)
                        if (autocvar_hud_panel_radar != 2 && !teamplay) return;
                }
        }
-       else
-               hud_configure_active_panel = HUD_PANEL_RADAR;
 
-       HUD_Panel_UpdateCvars(radar);
+       HUD_Panel_UpdateCvars();
        HUD_Panel_ApplyFadeAlpha();
-       
+
        float f = 0;
 
        if (hud_panel_radar_maximized && !autocvar__hud_configure)
@@ -2159,10 +2145,8 @@ void HUD_Score(void)
                if(!autocvar_hud_panel_score) return;
                if(spectatee_status == -1 && (gametype == MAPINFO_TYPE_RACE || gametype == MAPINFO_TYPE_CTS)) return;
        }
-       else
-               hud_configure_active_panel = HUD_PANEL_SCORE;
 
-       HUD_Panel_UpdateCvars(score);
+       HUD_Panel_UpdateCvars();
        HUD_Panel_ApplyFadeAlpha();
        vector pos, mySize;
        pos = panel_pos;
@@ -2356,10 +2340,8 @@ void HUD_RaceTimer (void)
                if(!(gametype == MAPINFO_TYPE_RACE || gametype == MAPINFO_TYPE_CTS)) return;
                if(spectatee_status == -1) return;
        }
-       else
-               hud_configure_active_panel = HUD_PANEL_RACETIMER;
 
-       HUD_Panel_UpdateCvars(racetimer);
+       HUD_Panel_UpdateCvars();
        HUD_Panel_ApplyFadeAlpha();
 
        draw_beginBoldFont();
@@ -2508,7 +2490,7 @@ float vote_prev; // previous state of vote_active to check for a change
 float vote_alpha;
 float vote_change; // "time" when vote_active changed
 
-void HUD_VoteWindow(void) 
+void HUD_Vote(void)
 {
        if(autocvar_cl_allow_uid2name == -1 && (gametype == MAPINFO_TYPE_CTS || gametype == MAPINFO_TYPE_RACE || (serverflags & SERVERFLAG_PLAYERSTATS)))
        {
@@ -2540,8 +2522,6 @@ void HUD_VoteWindow(void)
        }
        else
        {
-               hud_configure_active_panel = HUD_PANEL_VOTE;
-
                vote_yescount = 3;
                vote_nocount = 2;
                vote_needed = 4;
@@ -2562,7 +2542,7 @@ void HUD_VoteWindow(void)
        if(!vote_alpha)
                return;
 
-       HUD_Panel_UpdateCvars(vote);
+       HUD_Panel_UpdateCvars();
        HUD_Panel_ApplyFadeAlpha();
 
        if(uid2name_dialog)
@@ -2653,32 +2633,100 @@ void HUD_VoteWindow(void)
 
 float mod_active; // is there any active mod icon?
 
-// Clan Arena HUD modicons
-void HUD_Mod_CA(vector pos, vector mySize)
+void DrawCAItem(vector myPos, vector mySize, float aspect_ratio, float layout, float i)
 {
-       mod_active = 1; // CA should never hide the mod icons panel
-       float redalive, bluealive;
-       redalive = getstati(STAT_REDALIVE);
-       bluealive = getstati(STAT_BLUEALIVE);
+       float stat;
+       string pic;
+       vector color;
+#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;
+               }
        }
 }
 
@@ -3292,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);
 
@@ -3320,10 +3368,8 @@ void HUD_ModIcons(void)
                if(!autocvar_hud_panel_modicons) return;
                if (gametype != MAPINFO_TYPE_CTF && gametype != MAPINFO_TYPE_KEYHUNT && gametype != MAPINFO_TYPE_NEXBALL && gametype != MAPINFO_TYPE_CTS && gametype != MAPINFO_TYPE_RACE && gametype != MAPINFO_TYPE_CA && gametype != MAPINFO_TYPE_FREEZETAG && gametype != MAPINFO_TYPE_KEEPAWAY && gametype != MAPINFO_TYPE_DOMINATION) return;
        }
-       else
-               hud_configure_active_panel = HUD_PANEL_MODICONS;
 
-       HUD_Panel_UpdateCvars(modicons);
+       HUD_Panel_UpdateCvars();
        HUD_Panel_ApplyFadeAlpha();
 
        draw_beginBoldFont();
@@ -3372,18 +3418,15 @@ void HUD_ModIcons(void)
 
 // Draw pressed keys (#11)
 //
-void HUD_DrawPressedKeys(void)
+void HUD_PressedKeys(void)
 {
        if(!autocvar__hud_configure)
        {
                if(!autocvar_hud_panel_pressedkeys) return;
                if(spectatee_status <= 0 && autocvar_hud_panel_pressedkeys < 2) return;
        }
-       else
-               hud_configure_active_panel = HUD_PANEL_PRESSEDKEYS;
-
 
-       HUD_Panel_UpdateCvars(pressedkeys);
+       HUD_Panel_UpdateCvars();
        HUD_Panel_ApplyFadeAlpha();
        vector pos, mySize;
        pos = panel_pos;
@@ -3454,10 +3497,8 @@ void HUD_Chat(void)
                if(autocvar__con_chat_maximized)
                        if(!hud_draw_maximized) return;
        }
-       else
-               hud_configure_active_panel = HUD_PANEL_CHAT;
 
-       HUD_Panel_UpdateCvars(chat);
+       HUD_Panel_UpdateCvars();
        HUD_Panel_ApplyFadeAlpha();
 
        if(autocvar__con_chat_maximized && !autocvar__hud_configure) // draw at full screen height if maximized
@@ -3530,10 +3571,8 @@ void HUD_EngineInfo(void)
        {
                if(!autocvar_hud_panel_engineinfo) return;
        }
-       else
-               hud_configure_active_panel = HUD_PANEL_ENGINEINFO;
 
-       HUD_Panel_UpdateCvars(engineinfo);
+       HUD_Panel_UpdateCvars();
        HUD_Panel_ApplyFadeAlpha();
        vector pos, mySize;
        pos = panel_pos;
@@ -3593,10 +3632,8 @@ void HUD_InfoMessages(void)
        {
                if(!autocvar_hud_panel_infomessages) return;
        }
-       else
-               hud_configure_active_panel = HUD_PANEL_INFOMESSAGES;
 
-       HUD_Panel_UpdateCvars(infomessages);
+       HUD_Panel_UpdateCvars();
        HUD_Panel_ApplyFadeAlpha();
        vector pos, mySize;
        pos = panel_pos;
@@ -3656,7 +3693,7 @@ void HUD_InfoMessages(void)
                        if(spectatee_status == -1)
                                s = sprintf(_("^1Press ^3%s^1 to spectate"), getcommandkey("primary fire", "+fire"));
                        else
-                               s = sprintf(_("^1Press ^3%s^1 for another player"), getcommandkey("primary fire", "+fire"));
+                               s = sprintf(_("^1Press ^3%s^1 or ^3%s^1 for next or previous player"), getcommandkey("next weapon", "weapnext"), getcommandkey("previous weapon", "weapprev"));
                        drawInfoMessage(s)
 
                        if(spectatee_status == -1)
@@ -3784,10 +3821,8 @@ void HUD_Physics(void)
                if(spectatee_status == -1 && (autocvar_hud_panel_physics == 1 || autocvar_hud_panel_physics == 3)) return;
                if(autocvar_hud_panel_physics == 3 && !(gametype == MAPINFO_TYPE_RACE || gametype == MAPINFO_TYPE_CTS)) return;
        }
-       else
-               hud_configure_active_panel = HUD_PANEL_PHYSICS;
 
-       HUD_Panel_UpdateCvars(physics);
+       HUD_Panel_UpdateCvars();
        HUD_Panel_ApplyFadeAlpha();
 
        draw_beginBoldFont();
@@ -4167,8 +4202,6 @@ void HUD_CenterPrint (void)
        }
        else
        {
-               hud_configure_active_panel = HUD_PANEL_CENTERPRINT;
-
                if (!hud_configure_prev)
                        reset_centerprint_messages();
                if (time > hud_configure_cp_generation_time)
@@ -4185,7 +4218,7 @@ void HUD_CenterPrint (void)
                }
        }
 
-       HUD_Panel_UpdateCvars(centerprint);
+       HUD_Panel_UpdateCvars();
 
        // this panel doesn't fade when showing the scoreboard
        if(autocvar__menu_alpha)
@@ -4373,44 +4406,6 @@ void HUD_Reset (void)
                HUD_Mod_CTF_Reset();
 }
 
-#define HUD_DrawPanel(id)\
-switch (id) {\
-       case (HUD_PANEL_RADAR):\
-               HUD_Radar(); break;\
-       case (HUD_PANEL_WEAPONS):\
-               HUD_Weapons(); break;\
-       case (HUD_PANEL_AMMO):\
-               HUD_Ammo(); break;\
-       case (HUD_PANEL_POWERUPS):\
-               HUD_Powerups(); break;\
-       case (HUD_PANEL_HEALTHARMOR):\
-               HUD_HealthArmor(); break;\
-       case (HUD_PANEL_NOTIFY):\
-               HUD_Notify(); break;\
-       case (HUD_PANEL_TIMER):\
-               HUD_Timer(); break;\
-       case (HUD_PANEL_SCORE):\
-               HUD_Score(); break;\
-       case (HUD_PANEL_RACETIMER):\
-               HUD_RaceTimer(); break;\
-       case (HUD_PANEL_VOTE):\
-               HUD_VoteWindow(); break;\
-       case (HUD_PANEL_MODICONS):\
-               HUD_ModIcons(); break;\
-       case (HUD_PANEL_PRESSEDKEYS):\
-               HUD_DrawPressedKeys(); break;\
-       case (HUD_PANEL_CHAT):\
-               HUD_Chat(); break;\
-       case (HUD_PANEL_ENGINEINFO):\
-               HUD_EngineInfo(); break;\
-       case (HUD_PANEL_INFOMESSAGES):\
-               HUD_InfoMessages(); break;\
-       case (HUD_PANEL_PHYSICS):\
-               HUD_Physics(); break;\
-       case (HUD_PANEL_CENTERPRINT):\
-               HUD_CenterPrint(); break;\
-} ENDS_WITH_CURLY_BRACE
-
 void HUD_Main (void)
 {
        float i;
@@ -4440,7 +4435,7 @@ void HUD_Main (void)
        // they must call HUD_Panel_ApplyFadeAlpha(); only when showing the menu
        if(scoreboard_fade_alpha == 1)
        {
-               HUD_CenterPrint();
+               (panel = HUD_PANEL(CENTERPRINT)).panel_draw();
                return;
        }
 
@@ -4489,7 +4484,10 @@ void HUD_Main (void)
                vector color;
                float hud_dock_color_team = autocvar_hud_dock_color_team;
                if((teamplay) && hud_dock_color_team) {
-                       color = colormapPaletteColor(myteam, 1) * hud_dock_color_team;
+                       if(autocvar__hud_configure && myteam == NUM_SPECTATOR)
+                               color = '1 0 0' * hud_dock_color_team;
+                       else
+                               color = myteamcolors * hud_dock_color_team;
                }
                else if(autocvar_hud_configure_teamcolorforced && autocvar__hud_configure && hud_dock_color_team) {
                        color = '1 0 0' * hud_dock_color_team;
@@ -4564,31 +4562,35 @@ void HUD_Main (void)
        hud_draw_maximized = 0;
        // draw panels in order specified by panel_order array
        for(i = HUD_PANEL_NUM - 1; i >= 0; --i)
-               HUD_DrawPanel(panel_order[i]);
+               (panel = hud_panel[panel_order[i]]).panel_draw();
 
        hud_draw_maximized = 1; // panels that may be maximized must check this var
        // draw maximized panels on top
        if(hud_panel_radar_maximized)
-               HUD_Radar();
+               (panel = HUD_PANEL(RADAR)).panel_draw();
        if(autocvar__con_chat_maximized)
-               HUD_Chat();
+               (panel = HUD_PANEL(CHAT)).panel_draw();
 
        if(autocvar__hud_configure)
        {
-               if(tab_panel != -1)
+               if(tab_panel)
                {
-                       HUD_Panel_UpdatePosSizeForId(tab_panel)
+                       panel = tab_panel;
+                       HUD_Panel_UpdatePosSize()
                        drawfill(panel_pos - '1 1 0' * panel_bg_border, panel_size + '2 2 0' * panel_bg_border, '1 1 1', .2, DRAWFLAG_NORMAL);
                }
-               if(highlightedPanel != -1)
+               if(highlightedPanel)
                {
-                       HUD_Panel_UpdatePosSizeForId(highlightedPanel);
+                       panel = highlightedPanel;
+                       HUD_Panel_UpdatePosSize()
                        HUD_Panel_HlBorder(panel_bg_border + 1.5 * hlBorderSize, '0 0.5 1', 0.25 * (1 - autocvar__menu_alpha));
                }
                if(!hud_configure_prev || hud_configure_prev == -1)
                {
                        if(autocvar_hud_cursormode) { setcursormode(1); }
                        hudShiftState = 0;
+                       for(i = HUD_PANEL_NUM - 1; i >= 0; --i)
+                               hud_panel[panel_order[i]].update_time = time;
                }
        }
        else if (hud_configure_prev && autocvar_hud_cursormode)
index bef2079b31920637c695ce579b4835d40281dab7..19cd36ddb0b0551bd4bc6833ef5b73f8be5da9d4 100644 (file)
@@ -1,4 +1,10 @@
-float panel_order[HUD_PANEL_NUM];
+#define HUD_PANEL_MAX 24
+entity hud_panel[HUD_PANEL_MAX];
+#define HUD_PANEL_FIRST 0
+float HUD_PANEL_NUM;
+float HUD_PANEL_LAST;
+
+float panel_order[HUD_PANEL_MAX];
 string hud_panelorder_prev;
 
 float hud_draw_maximized;
@@ -8,7 +14,7 @@ vector mousepos;
 vector panel_click_distance; // mouse cursor distance from the top left corner of the panel (saved only upon a click)
 vector panel_click_resizeorigin; // coordinates for opposite point when resizing
 float resizeCorner; // 1 = topleft, 2 = topright, 3 = bottomleft, 4 = bottomright
-var float highlightedPanel = -1;
+entity highlightedPanel;
 float highlightedAction; // 0 = nothing, 1 = move, 2 = resize
 
 const float BORDER_MULTIPLIER = 0.25;
@@ -48,17 +54,29 @@ float hud_fade_alpha;
 string hud_skin_path;
 string hud_skin_prev;
 
+vector myteamcolors;
+
 var vector progressbar_color;
 
-var float highlightedPanel_backup = -1;
+entity highlightedPanel_backup;
 var vector panel_pos_backup;
 var vector panel_size_backup;
 
-var float highlightedPanel_copied = -1; //this is good only to know if there is something copied
 var vector panel_size_copied;
 
-var float hud_configure_active_panel; // this panel has recently referred the UpdateCvars macro
-var string panel_name;
+entity panel;
+.string panel_name;
+.float panel_id;
+.vector current_panel_pos;
+.vector current_panel_size;
+.string current_panel_bg;
+.float current_panel_bg_alpha;
+.float current_panel_bg_border;
+.vector current_panel_bg_color;
+.float current_panel_bg_color_team;
+.float current_panel_bg_padding;
+.float current_panel_fg_alpha;
+.float update_time;
 var float panel_enabled;
 var vector panel_pos;
 var vector panel_size;
@@ -76,8 +94,52 @@ var string panel_bg_border_str;
 var float panel_bg_padding;
 var string panel_bg_padding_str;
 
+.void() panel_draw;
+
 float current_player;
 
+
+#define HUD_PANELS \
+       HUD_PANEL(WEAPONS      , HUD_Weapons      , weapons) \
+       HUD_PANEL(AMMO         , HUD_Ammo         , ammo) \
+       HUD_PANEL(POWERUPS     , HUD_Powerups     , powerups) \
+       HUD_PANEL(HEALTHARMOR  , HUD_HealthArmor  , healtharmor) \
+       HUD_PANEL(NOTIFY       , HUD_Notify       , notify) \
+       HUD_PANEL(TIMER        , HUD_Timer        , timer) \
+       HUD_PANEL(RADAR        , HUD_Radar        , radar) \
+       HUD_PANEL(SCORE        , HUD_Score        , score) \
+       HUD_PANEL(RACETIMER    , HUD_RaceTimer    , racetimer) \
+       HUD_PANEL(VOTE         , HUD_Vote         , vote) \
+       HUD_PANEL(MODICONS     , HUD_ModIcons     , modicons) \
+       HUD_PANEL(PRESSEDKEYS  , HUD_PressedKeys  , pressedkeys) \
+       HUD_PANEL(CHAT         , HUD_Chat         , chat) \
+       HUD_PANEL(ENGINEINFO   , HUD_EngineInfo   , engineinfo) \
+       HUD_PANEL(INFOMESSAGES , HUD_InfoMessages , infomessages) \
+       HUD_PANEL(PHYSICS      , HUD_Physics      , physics) \
+       HUD_PANEL(CENTERPRINT  , HUD_CenterPrint  , centerprint)
+
+#define HUD_PANEL(NAME,draw_func,name) \
+       float HUD_PANEL_##NAME; \
+       void ##draw_func(void); \
+       void RegisterHUD_Panel_##NAME() \
+       { \
+               HUD_PANEL_LAST = HUD_PANEL_##NAME = HUD_PANEL_NUM; \
+               entity hud_panelent = spawn(); \
+               hud_panel[HUD_PANEL_##NAME] = hud_panelent; \
+               hud_panelent.classname = "hud_panel"; \
+               hud_panelent.panel_name = #name; \
+               hud_panelent.panel_id = HUD_PANEL_##NAME; \
+               hud_panelent.panel_draw = ##draw_func; \
+               ++HUD_PANEL_NUM; \
+       } \
+       ACCUMULATE_FUNCTION(RegisterHUD_Panels, RegisterHUD_Panel_##NAME)
+
+HUD_PANELS
+#undef HUD_PANEL
+
+#define HUD_PANEL(NAME) hud_panel[HUD_PANEL_##NAME]
+
+
 // Because calling lots of functions in QC apparently cuts fps in half on many machines:
 // ----------------------
 // MACRO HELL STARTS HERE
@@ -126,7 +188,10 @@ if(!autocvar__hud_configure && panel_bg_str == "0") {\
 // Get value for panel_bg_color: if "" fetch default, else use panel_bg_color. Convert pants, shirt or teamcolor into a vector.
 #define HUD_Panel_GetColor()\
 if((teamplay) && panel_bg_color_team) {\
-       panel_bg_color = colormapPaletteColor(myteam, 1) * panel_bg_color_team;\
+       if(autocvar__hud_configure && myteam == NUM_SPECTATOR)\
+               panel_bg_color = '1 0 0' * panel_bg_color_team;\
+       else\
+               panel_bg_color = myteamcolors * panel_bg_color_team;\
 } else if (autocvar_hud_configure_teamcolorforced && autocvar__hud_configure && panel_bg_color_team) {\
        panel_bg_color = '1 0 0' * panel_bg_color_team;\
 } else {\
@@ -153,7 +218,7 @@ if(panel_bg_color_team_str == "") {\
 
 // the check doesn't allow to fade this panel when showing the panel-specific menu dialog
 #define HUD_Panel_ApplyFadeAlpha()\
-if(!(menu_enabled == 2 && highlightedPanel == hud_configure_active_panel))\
+if(!(menu_enabled == 2 && panel == highlightedPanel))\
 {\
        panel_bg_alpha *= hud_fade_alpha;\
        panel_fg_alpha *= hud_fade_alpha;\
@@ -169,7 +234,7 @@ panel_bg_alpha = stof(panel_bg_alpha_str);\
 if(autocvar__hud_configure) {\
        if(!panel_enabled)\
                panel_bg_alpha = 0.25;\
-       else if(menu_enabled == 2 && highlightedPanel == hud_configure_active_panel)\
+       else if(menu_enabled == 2 && panel == highlightedPanel)\
                panel_bg_alpha = (1 - autocvar__menu_alpha) * max(cvar("hud_configure_bg_minalpha"), panel_bg_alpha) + autocvar__menu_alpha * panel_bg_alpha;\
        else\
                panel_bg_alpha = max(cvar("hud_configure_bg_minalpha"), panel_bg_alpha);\
@@ -256,90 +321,60 @@ else\
        }\
 }
 
-// Update all common cvars of given panel name
-#define HUD_Panel_UpdateCvars(name) \
-panel_enabled = autocvar_hud_panel_##name; \
-panel_pos = stov(cvar_string("hud_panel_" #name "_pos")); \
-panel_size = stov(cvar_string("hud_panel_" #name "_size")); \
-panel_bg_str = cvar_string("hud_panel_" #name "_bg"); \
-panel_bg_color_str = cvar_string("hud_panel_" #name "_bg_color"); \
-panel_bg_color_team_str = cvar_string("hud_panel_" #name "_bg_color_team"); \
-panel_bg_alpha_str = cvar_string("hud_panel_" #name "_bg_alpha"); \
-panel_bg_border_str = cvar_string("hud_panel_" #name "_bg_border"); \
-panel_bg_padding_str = cvar_string("hud_panel_" #name "_bg_padding"); \
-HUD_Panel_GetStringVars()\
-if(menu_enabled == 2 && hud_configure_active_panel == highlightedPanel) {\
-       HUD_Panel_GetMenuSize()\
-       HUD_Panel_GetMenuPos()\
+// NOTE: in hud_configure mode cvars must be reloaded every frame
+#define HUD_Panel_UpdateCvars() \
+if(panel.update_time <= time) { \
+       if(autocvar__hud_configure) panel_enabled = cvar(strcat("hud_panel_", panel.panel_name)); \
+       panel_pos = stov(cvar_string(strcat("hud_panel_", panel.panel_name, "_pos"))); \
+       panel_size = stov(cvar_string(strcat("hud_panel_", panel.panel_name, "_size"))); \
+       panel_bg_str = cvar_string(strcat("hud_panel_", panel.panel_name, "_bg")); \
+       panel_bg_color_str = cvar_string(strcat("hud_panel_", panel.panel_name, "_bg_color")); \
+       panel_bg_color_team_str = cvar_string(strcat("hud_panel_", panel.panel_name, "_bg_color_team")); \
+       panel_bg_alpha_str = cvar_string(strcat("hud_panel_", panel.panel_name, "_bg_alpha")); \
+       panel_bg_border_str = cvar_string(strcat("hud_panel_", panel.panel_name, "_bg_border")); \
+       panel_bg_padding_str = cvar_string(strcat("hud_panel_", panel.panel_name, "_bg_padding")); \
+       HUD_Panel_GetStringVars()\
+       if(menu_enabled == 2 && panel == highlightedPanel) {\
+               HUD_Panel_GetMenuSize()\
+               HUD_Panel_GetMenuPos()\
+       } \
+       panel.current_panel_pos = panel_pos; \
+       panel.current_panel_size = panel_size; \
+       if(panel.current_panel_bg != "") \
+               strunzone(panel.current_panel_bg); \
+       panel.current_panel_bg = strzone(panel_bg); \
+       panel.current_panel_bg_alpha = panel_bg_alpha; \
+       panel.current_panel_bg_border = panel_bg_border; \
+       panel.current_panel_bg_color = panel_bg_color; \
+       panel.current_panel_bg_color_team = panel_bg_color_team; \
+       panel.current_panel_bg_padding = panel_bg_padding; \
+       panel.current_panel_fg_alpha = panel_fg_alpha; \
+       panel.update_time = (autocvar__hud_configure) ? time : time + autocvar_hud_panel_update_interval; \
+} else { \
+       panel_pos = panel.current_panel_pos; \
+       panel_size = panel.current_panel_size; \
+       panel_bg = panel.current_panel_bg; \
+       panel_bg_alpha = panel.current_panel_bg_alpha; \
+       panel_bg_border = panel.current_panel_bg_border; \
+       panel_bg_color = panel.current_panel_bg_color; \
+       panel_bg_color_team = panel.current_panel_bg_color_team; \
+       panel_bg_padding = panel.current_panel_bg_padding; \
+       panel_fg_alpha = panel.current_panel_fg_alpha; \
 } ENDS_WITH_CURLY_BRACE
 
-// FTEQCC I HATE YOU WHY DO YOU MAKE ME DO THIS??? :(
-// max macro length is 1024 characters, I must split it up :(
-
-// Update all common cvars of given panel id
-#define HUD_Panel_UpdateCvarsForId_Part2(id) \
-switch(id) { \
-       case HUD_PANEL_INFOMESSAGES: HUD_Panel_UpdateCvars(infomessages) break; \
-       case HUD_PANEL_PHYSICS: HUD_Panel_UpdateCvars(physics); break;\
-       case HUD_PANEL_CENTERPRINT: HUD_Panel_UpdateCvars(centerprint); break;\
-}
-
-#define HUD_Panel_UpdateCvarsForId(id) \
-switch(id) { \
-       case HUD_PANEL_WEAPONS: HUD_Panel_UpdateCvars(weapons) break; \
-       case HUD_PANEL_AMMO: HUD_Panel_UpdateCvars(ammo) break; \
-       case HUD_PANEL_POWERUPS: HUD_Panel_UpdateCvars(powerups) break; \
-       case HUD_PANEL_HEALTHARMOR: HUD_Panel_UpdateCvars(healtharmor) break; \
-       case HUD_PANEL_NOTIFY: HUD_Panel_UpdateCvars(notify) break; \
-       case HUD_PANEL_TIMER: HUD_Panel_UpdateCvars(timer) break; \
-       case HUD_PANEL_RADAR: HUD_Panel_UpdateCvars(radar) break; \
-       case HUD_PANEL_SCORE: HUD_Panel_UpdateCvars(score) break; \
-       case HUD_PANEL_RACETIMER: HUD_Panel_UpdateCvars(racetimer) break; \
-       case HUD_PANEL_VOTE: HUD_Panel_UpdateCvars(vote) break; \
-       case HUD_PANEL_MODICONS: HUD_Panel_UpdateCvars(modicons) break; \
-       case HUD_PANEL_PRESSEDKEYS: HUD_Panel_UpdateCvars(pressedkeys) break; \
-       case HUD_PANEL_CHAT: HUD_Panel_UpdateCvars(chat) break; \
-       case HUD_PANEL_ENGINEINFO: HUD_Panel_UpdateCvars(engineinfo) break; \
-       default: HUD_Panel_UpdateCvarsForId_Part2(id)\
-}
-
-#define HUD_Panel_UpdatePosSize(name) \
-panel_pos = stov(cvar_string("hud_panel_" #name "_pos")); \
-panel_size = stov(cvar_string("hud_panel_" #name "_size")); \
+#define HUD_Panel_UpdatePosSize() {\
+panel_enabled = cvar(strcat("hud_panel_", panel.panel_name)); \
+panel_pos = stov(cvar_string(strcat("hud_panel_", panel.panel_name, "_pos"))); \
+panel_size = stov(cvar_string(strcat("hud_panel_", panel.panel_name, "_size"))); \
 HUD_Panel_GetScaledVectors()\
-if(menu_enabled == 2 && hud_configure_active_panel == highlightedPanel) {\
+if(menu_enabled == 2 && panel == highlightedPanel) {\
        HUD_Panel_GetMenuSize()\
        HUD_Panel_GetMenuPos()\
 }\
-panel_bg_border_str = cvar_string("hud_panel_" #name "_bg_border"); \
-HUD_Panel_GetBorder()
-
-// Update pos and size of given panel id
-#define HUD_Panel_UpdatePosSizeForId_Part2(id) \
-switch(id) { \
-       case HUD_PANEL_INFOMESSAGES: HUD_Panel_UpdatePosSize(infomessages) break;\
-       case HUD_PANEL_PHYSICS: HUD_Panel_UpdatePosSize(physics); break;\
-       case HUD_PANEL_CENTERPRINT: HUD_Panel_UpdatePosSize(centerprint); break;\
-}
+panel_bg_border_str = cvar_string(strcat("hud_panel_", panel.panel_name, "_bg_border")); \
+HUD_Panel_GetBorder() \
+} ENDS_WITH_CURLY_BRACE
 
-#define HUD_Panel_UpdatePosSizeForId(id) \
-switch(id) { \
-       case HUD_PANEL_WEAPONS: HUD_Panel_UpdatePosSize(weapons) break;\
-       case HUD_PANEL_AMMO: HUD_Panel_UpdatePosSize(ammo) break;\
-       case HUD_PANEL_POWERUPS: HUD_Panel_UpdatePosSize(powerups) break;\
-       case HUD_PANEL_HEALTHARMOR: HUD_Panel_UpdatePosSize(healtharmor) break;\
-       case HUD_PANEL_NOTIFY: HUD_Panel_UpdatePosSize(notify) break;\
-       case HUD_PANEL_TIMER: HUD_Panel_UpdatePosSize(timer) break;\
-       case HUD_PANEL_RADAR: HUD_Panel_UpdatePosSize(radar) break;\
-       case HUD_PANEL_SCORE: HUD_Panel_UpdatePosSize(score) break;\
-       case HUD_PANEL_RACETIMER: HUD_Panel_UpdatePosSize(racetimer) break;\
-       case HUD_PANEL_VOTE: HUD_Panel_UpdatePosSize(vote) break;\
-       case HUD_PANEL_MODICONS: HUD_Panel_UpdatePosSize(modicons) break;\
-       case HUD_PANEL_PRESSEDKEYS: HUD_Panel_UpdatePosSize(pressedkeys) break;\
-       case HUD_PANEL_CHAT: HUD_Panel_UpdatePosSize(chat) break;\
-       case HUD_PANEL_ENGINEINFO: HUD_Panel_UpdatePosSize(engineinfo) break;\
-       default: HUD_Panel_UpdatePosSizeForId_Part2(id)\
-}
 
 #define KN_MAX_ENTRIES 10
 
index 21ab08fa709c51aceb213849a47b6058f413747c..ca8ec19c1ab51cbf33cba4eb27c119b3ae413016 100644 (file)
@@ -2,8 +2,8 @@
 // q: quoted, n: not quoted
 #define HUD_Write_Cvar_n(cvar) HUD_Write(strcat("seta ", cvar, " ", cvar_string(cvar), "\n"))
 #define HUD_Write_Cvar_q(cvar) HUD_Write(strcat("seta ", cvar, " \"", cvar_string(cvar), "\"\n"))
-#define HUD_Write_PanelCvar_n(cvar_suf) HUD_Write_Cvar_n(strcat("hud_panel_", panel_name, cvar_suf))
-#define HUD_Write_PanelCvar_q(cvar_suf) HUD_Write_Cvar_q(strcat("hud_panel_", panel_name, cvar_suf))
+#define HUD_Write_PanelCvar_n(cvar_suf) HUD_Write_Cvar_n(strcat("hud_panel_", panel.panel_name, cvar_suf))
+#define HUD_Write_PanelCvar_q(cvar_suf) HUD_Write_Cvar_q(strcat("hud_panel_", panel.panel_name, cvar_suf))
 // Save the config
 void HUD_Panel_ExportCfg(string cfgname)
 {
@@ -52,7 +52,7 @@ void HUD_Panel_ExportCfg(string cfgname)
                float i;
                for (i = 0; i < HUD_PANEL_NUM; ++i)
                {
-                       HUD_Panel_GetName(i);
+                       panel = hud_panel[i];
 
                        HUD_Write_PanelCvar_n("");
                        HUD_Write_PanelCvar_q("_pos");
@@ -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");
@@ -217,10 +219,10 @@ vector HUD_Panel_CheckMove(vector myPos, vector mySize)
        myTarget = myPos;
 
        for (i = 0; i < HUD_PANEL_NUM; ++i) {
-               if(i == highlightedPanel || !panel_enabled)
-                       continue;
-
-               HUD_Panel_UpdatePosSizeForId(i);
+               panel = hud_panel[i];
+               if(panel == highlightedPanel) continue;
+               HUD_Panel_UpdatePosSize()
+               if(!panel_enabled) continue;
 
                panel_pos -= '1 1 0' * panel_bg_border;
                panel_size += '2 2 0' * panel_bg_border;
@@ -280,7 +282,8 @@ vector HUD_Panel_CheckMove(vector myPos, vector mySize)
 
 void HUD_Panel_SetPos(vector pos)
 {
-       HUD_Panel_UpdatePosSizeForId(highlightedPanel);
+       panel = highlightedPanel;
+       HUD_Panel_UpdatePosSize()
        vector mySize;
        mySize = panel_size;
 
@@ -302,8 +305,7 @@ void HUD_Panel_SetPos(vector pos)
        string s;
        s = strcat(ftos(pos_x/vid_conwidth), " ", ftos(pos_y/vid_conheight));
 
-       HUD_Panel_GetName(highlightedPanel);
-       cvar_set(strcat("hud_panel_", panel_name, "_pos"), s);
+       cvar_set(strcat("hud_panel_", highlightedPanel.panel_name, "_pos"), s);
 }
 
 // check if resize will result in panel being moved into another panel. If so, return snapped vector, otherwise return the given vector
@@ -317,10 +319,10 @@ vector HUD_Panel_CheckResize(vector mySize, vector resizeorigin) {
        ratio = mySize_x/mySize_y;
 
        for (i = 0; i < HUD_PANEL_NUM; ++i) {
-               if(i == highlightedPanel || !panel_enabled)
-                       continue;
-
-               HUD_Panel_UpdatePosSizeForId(i);
+               panel = hud_panel[i];
+               if(panel == highlightedPanel) continue;
+               HUD_Panel_UpdatePosSize()
+               if(!panel_enabled) continue;
 
                panel_pos -= '1 1 0' * panel_bg_border;
                panel_size += '2 2 0' * panel_bg_border;
@@ -419,7 +421,8 @@ vector HUD_Panel_CheckResize(vector mySize, vector resizeorigin) {
 
 void HUD_Panel_SetPosSize(vector mySize)
 {
-       HUD_Panel_UpdatePosSizeForId(highlightedPanel);
+       panel = highlightedPanel;
+       HUD_Panel_UpdatePosSize()
        vector resizeorigin;
        resizeorigin = panel_click_resizeorigin;
        local noref vector myPos; // fteqcc sucks
@@ -428,7 +431,7 @@ void HUD_Panel_SetPosSize(vector mySize)
        mySize_x = max(0.025 * vid_conwidth, mySize_x);
        mySize_y = max(0.025 * vid_conheight, mySize_y);
 
-       if(highlightedPanel == HUD_PANEL_CHAT) // some panels have their own restrictions, like the chat panel (which actually only moves the engine chat print around). Looks bad if it's too small.
+       if(highlightedPanel == HUD_PANEL(CHAT)) // some panels have their own restrictions, like the chat panel (which actually only moves the engine chat print around). Looks bad if it's too small.
        {
                mySize_x = max(17 * autocvar_con_chatsize, mySize_x);
                mySize_y = max(2 * autocvar_con_chatsize + 2 * panel_bg_padding, mySize_y);
@@ -499,20 +502,19 @@ void HUD_Panel_SetPosSize(vector mySize)
        //if(cvar("hud_configure_checkcollisions_debug"))
                //drawfill(myPos, mySize, '0 1 0', .3, DRAWFLAG_NORMAL);
 
-       HUD_Panel_GetName(highlightedPanel);
        string s;
        s = strcat(ftos(mySize_x/vid_conwidth), " ", ftos(mySize_y/vid_conheight));
-       cvar_set(strcat("hud_panel_", panel_name, "_size"), s);
+       cvar_set(strcat("hud_panel_", highlightedPanel.panel_name, "_size"), s);
 
        s = strcat(ftos(myPos_x/vid_conwidth), " ", ftos(myPos_y/vid_conheight));
-       cvar_set(strcat("hud_panel_", panel_name, "_pos"), s);
+       cvar_set(strcat("hud_panel_", highlightedPanel.panel_name, "_pos"), s);
 }
 
 float pressed_key_time;
 vector highlightedPanel_initial_pos, highlightedPanel_initial_size;
 void HUD_Panel_Arrow_Action(float nPrimary)
 {
-       if (highlightedPanel == -1)
+       if(!highlightedPanel)
                return;
 
        hud_configure_checkcollisions = (!(hudShiftState & S_CTRL) && autocvar_hud_configure_checkcollisions);
@@ -547,7 +549,8 @@ void HUD_Panel_Arrow_Action(float nPrimary)
                        step = (step / 64) * (1 + 2 * (time - pressed_key_time));
        }
 
-       HUD_Panel_UpdatePosSizeForId(highlightedPanel);
+       panel = highlightedPanel;
+       HUD_Panel_UpdatePosSize()
 
        highlightedPanel_initial_pos = panel_pos;
        highlightedPanel_initial_size = panel_size;
@@ -606,7 +609,8 @@ void HUD_Panel_Arrow_Action(float nPrimary)
                HUD_Panel_SetPos(pos);
        }
 
-       HUD_Panel_UpdatePosSizeForId(highlightedPanel);
+       panel = highlightedPanel;
+       HUD_Panel_UpdatePosSize()
 
        if (highlightedPanel_initial_pos != panel_pos || highlightedPanel_initial_size != panel_size)
        {
@@ -626,15 +630,16 @@ float prevMouseClickedTime; // time during previous left mouse click, to check f
 vector prevMouseClickedPos; // pos during previous left mouse click, to check for doubleclicks
 
 void HUD_Panel_EnableMenu();
-float tab_panels[HUD_PANEL_NUM];
-float tab_panel, tab_backward;
+entity tab_panels[HUD_PANEL_MAX];
+entity tab_panel;
 vector tab_panel_pos;
+float tab_backward;
 void HUD_Panel_FirstInDrawQ(float id);
 void reset_tab_panels()
 {
        int i;
        for(i = 0; i < HUD_PANEL_NUM; ++i)
-               tab_panels[i] = -1;
+               tab_panels[i] = world;
 }
 float HUD_Panel_InputEvent(float bInputType, float nPrimary, float nSecondary)
 {
@@ -685,14 +690,14 @@ float HUD_Panel_InputEvent(float bInputType, float nPrimary, float nSecondary)
        {
                if (bInputType == 1) //ctrl has been released
                {
-                       if (tab_panel != -1)
+                       if (tab_panel)
                        {
                                //switch to selected panel
                                highlightedPanel = tab_panel;
                                highlightedAction = 0;
-                               HUD_Panel_FirstInDrawQ(highlightedPanel);
+                               HUD_Panel_FirstInDrawQ(highlightedPanel.panel_id);
                        }
-                       tab_panel = -1;
+                       tab_panel = world;
                        reset_tab_panels();
                }
        }
@@ -736,15 +741,18 @@ float HUD_Panel_InputEvent(float bInputType, float nPrimary, float nSecondary)
                //(it should only after every other panel of the hud)
                //It's a minor bug anyway, we can live with it
 
-               float starting_panel;
-               float old_tab_panel = tab_panel;
-               if (tab_panel == -1) //first press of TAB
+               entity starting_panel;
+               entity old_tab_panel = tab_panel;
+               if (!tab_panel) //first press of TAB
                {
-                       if (highlightedPanel != -1)
-                               HUD_Panel_UpdatePosSizeForId(highlightedPanel)
+                       if (highlightedPanel)
+                       {
+                               panel = highlightedPanel;
+                               HUD_Panel_UpdatePosSize()
+                       }
                        else
                                panel_pos = '0 0 0';
-                       starting_panel = highlightedPanel; //can be -1, it means no starting panel
+                       starting_panel = highlightedPanel;
                        tab_panel_pos = panel_pos; //to compute level
                }
                else
@@ -763,35 +771,36 @@ float HUD_Panel_InputEvent(float bInputType, float nPrimary, float nSecondary)
                level = floor(tab_panel_pos_y / level_height) * level_height; //starting level
                candidate_pos_x = (!tab_backward) ? vid_conwidth : 0;
                start_pos_x = tab_panel_pos_x;
-               tab_panel = -1;
+               tab_panel = world;
                k=0;
                while(++k)
                {
                        for(i = 0; i < HUD_PANEL_NUM; ++i)
                        {
-                               if (i == tab_panels[i] || i == starting_panel)
+                               panel = hud_panel[i];
+                               if (panel == tab_panels[i] || panel == starting_panel)
                                        continue;
-                               HUD_Panel_UpdatePosSizeForId(i)
+                               HUD_Panel_UpdatePosSize()
                                if (panel_pos_y >= level && (panel_pos_y - level) < level_height)
                                if (  ( !tab_backward && panel_pos_x >= start_pos_x && (panel_pos_x < candidate_pos_x || (panel_pos_x == candidate_pos_x && panel_pos_y <= candidate_pos_y)) )
                                        || ( tab_backward && panel_pos_x <= start_pos_x && (panel_pos_x > candidate_pos_x || (panel_pos_x == candidate_pos_x && panel_pos_y >= candidate_pos_y)) )  )
                                {
-                                       tab_panel = i;
+                                       tab_panel = panel;
                                        tab_panel_pos = candidate_pos = panel_pos;
                                }
                        }
-                       if (tab_panel != -1)
+                       if (tab_panel)
                                break;
                        if (k == LEVELS_NUM) //tab_panel not found
                        {
                                reset_tab_panels();
-                               if (old_tab_panel == -2) //this prevents an infinite loop (should not happen normally)
+                               if (!old_tab_panel)
                                {
-                                       tab_panel = -1;
+                                       tab_panel = world;
                                        return true;
                                }
                                starting_panel = old_tab_panel;
-                               old_tab_panel = -2;
+                               old_tab_panel = world;
                                goto find_tab_panel; //u must find tab_panel!
                        }
                        if (!tab_backward)
@@ -808,18 +817,15 @@ float HUD_Panel_InputEvent(float bInputType, float nPrimary, float nSecondary)
                        }
                }
 
-               tab_panels[tab_panel] = tab_panel;
+               tab_panels[tab_panel.panel_id] = tab_panel;
        }
        else if(nPrimary == K_SPACE && hudShiftState & S_CTRL) // enable/disable highlighted panel or dock
        {
                if (bInputType == 1 || mouseClicked)
                        return true;
 
-               if (highlightedPanel != -1)
-               {
-                       HUD_Panel_GetName(highlightedPanel);
-                       cvar_set(strcat("hud_panel_", panel_name), ftos(!(panel_enabled)));
-               }
+               if (highlightedPanel)
+                       cvar_set(strcat("hud_panel_", highlightedPanel.panel_name), ftos(!cvar(strcat("hud_panel_", highlightedPanel.panel_name))));
                else
                        cvar_set(strcat("hud_dock"), (autocvar_hud_dock == "") ? "dock" : "");
        }
@@ -828,11 +834,11 @@ float HUD_Panel_InputEvent(float bInputType, float nPrimary, float nSecondary)
                if (bInputType == 1 || mouseClicked)
                        return true;
 
-               if (highlightedPanel != -1)
+               if (highlightedPanel)
                {
-                       HUD_Panel_UpdatePosSizeForId(highlightedPanel);
+                       panel = highlightedPanel;
+                       HUD_Panel_UpdatePosSize()
                        panel_size_copied = panel_size;
-                       highlightedPanel_copied = highlightedPanel;
                }
        }
        else if(nPrimary == 'v' && hudShiftState & S_CTRL) // past copied size on the highlighted panel
@@ -840,10 +846,11 @@ float HUD_Panel_InputEvent(float bInputType, float nPrimary, float nSecondary)
                if (bInputType == 1 || mouseClicked)
                        return true;
 
-               if (highlightedPanel_copied == -1 || highlightedPanel == -1)
+               if (panel_size_copied == '0 0 0' || !highlightedPanel)
                        return true;
 
-               HUD_Panel_UpdatePosSizeForId(highlightedPanel);
+               panel = highlightedPanel;
+               HUD_Panel_UpdatePosSize()
 
                // reduce size if it'd go beyond screen boundaries
                vector tmp_size = panel_size_copied;
@@ -861,22 +868,20 @@ float HUD_Panel_InputEvent(float bInputType, float nPrimary, float nSecondary)
                highlightedPanel_backup = highlightedPanel;
 
                s = strcat(ftos(tmp_size_x/vid_conwidth), " ", ftos(tmp_size_y/vid_conheight));
-               HUD_Panel_GetName(highlightedPanel);
-               cvar_set(strcat("hud_panel_", panel_name, "_size"), s);
+               cvar_set(strcat("hud_panel_", highlightedPanel.panel_name, "_size"), s);
        }
        else if(nPrimary == 'z' && hudShiftState & S_CTRL) // undo last action
        {
                if (bInputType == 1 || mouseClicked)
                        return true;
                //restore previous values
-               if (highlightedPanel_backup != -1)
+               if (highlightedPanel_backup)
                {
-                       HUD_Panel_GetName(highlightedPanel_backup);
                        s = strcat(ftos(panel_pos_backup_x/vid_conwidth), " ", ftos(panel_pos_backup_y/vid_conheight));
-                       cvar_set(strcat("hud_panel_", panel_name, "_pos"), s);
+                       cvar_set(strcat("hud_panel_", highlightedPanel_backup.panel_name, "_pos"), s);
                        s = strcat(ftos(panel_size_backup_x/vid_conwidth), " ", ftos(panel_size_backup_y/vid_conheight));
-                       cvar_set(strcat("hud_panel_", panel_name, "_size"), s);
-                       highlightedPanel_backup = -1;
+                       cvar_set(strcat("hud_panel_", highlightedPanel_backup.panel_name, "_size"), s);
+                       highlightedPanel_backup = world;
                }
        }
        else if(nPrimary == K_UPARROW || nPrimary == K_DOWNARROW || nPrimary == K_LEFTARROW || nPrimary == K_RIGHTARROW)
@@ -896,7 +901,7 @@ float HUD_Panel_InputEvent(float bInputType, float nPrimary, float nSecondary)
        {
                if (bInputType == 1)
                        return true;
-               if (highlightedPanel != -1)
+               if (highlightedPanel)
                        HUD_Panel_EnableMenu();
        }
        else if(hit_con_bind)
@@ -914,7 +919,8 @@ float HUD_Panel_Check_Mouse_Pos(float allow_move)
                i = panel_order[j];
                j += 1;
 
-               HUD_Panel_UpdatePosSizeForId(i);
+               panel = hud_panel[i];
+               HUD_Panel_UpdatePosSize()
 
                border = max(8, panel_bg_border); // FORCED border so a small border size doesn't mean you can't resize
 
@@ -994,14 +1000,15 @@ void HUD_Panel_Highlight(float allow_move)
                i = panel_order[j];
                j += 1;
 
-               HUD_Panel_UpdatePosSizeForId(i);
+               panel = hud_panel[i];
+               HUD_Panel_UpdatePosSize()
 
                border = max(8, panel_bg_border); // FORCED border so a small border size doesn't mean you can't resize
 
                // move
                if(allow_move && mousepos_x > panel_pos_x && mousepos_y > panel_pos_y && mousepos_x < panel_pos_x + panel_size_x && mousepos_y < panel_pos_y + panel_size_y)
                {
-                       highlightedPanel = i;
+                       highlightedPanel = hud_panel[i];
                        HUD_Panel_FirstInDrawQ(i);
                        highlightedAction = 1;
                        panel_click_distance = mousepos - panel_pos;
@@ -1010,7 +1017,7 @@ void HUD_Panel_Highlight(float allow_move)
                // resize from topleft border
                else if(mousepos_x >= panel_pos_x - border && mousepos_y >= panel_pos_y - border && mousepos_x <= panel_pos_x + 0.5 * panel_size_x && mousepos_y <= panel_pos_y + 0.5 * panel_size_y)
                {
-                       highlightedPanel = i;
+                       highlightedPanel = hud_panel[i];
                        HUD_Panel_FirstInDrawQ(i);
                        highlightedAction = 2;
                        resizeCorner = 1;
@@ -1021,7 +1028,7 @@ void HUD_Panel_Highlight(float allow_move)
                // resize from topright border
                else if(mousepos_x >= panel_pos_x + 0.5 * panel_size_x && mousepos_y >= panel_pos_y - border && mousepos_x <= panel_pos_x + panel_size_x + border && mousepos_y <= panel_pos_y + 0.5 * panel_size_y)
                {
-                       highlightedPanel = i;
+                       highlightedPanel = hud_panel[i];
                        HUD_Panel_FirstInDrawQ(i);
                        highlightedAction = 2;
                        resizeCorner = 2;
@@ -1033,7 +1040,7 @@ void HUD_Panel_Highlight(float allow_move)
                // resize from bottomleft border
                else if(mousepos_x >= panel_pos_x - border && mousepos_y >= panel_pos_y + 0.5 * panel_size_y && mousepos_x <= panel_pos_x + 0.5 * panel_size_x && mousepos_y <= panel_pos_y + panel_size_y + border)
                {
-                       highlightedPanel = i;
+                       highlightedPanel = hud_panel[i];
                        HUD_Panel_FirstInDrawQ(i);
                        highlightedAction = 2;
                        resizeCorner = 3;
@@ -1045,7 +1052,7 @@ void HUD_Panel_Highlight(float allow_move)
                // resize from bottomright border
                else if(mousepos_x >= panel_pos_x + 0.5 * panel_size_x && mousepos_y >= panel_pos_y + 0.5 * panel_size_y && mousepos_x <= panel_pos_x + panel_size_x + border && mousepos_y <= panel_pos_y + panel_size_y + border)
                {
-                       highlightedPanel = i;
+                       highlightedPanel = hud_panel[i];
                        HUD_Panel_FirstInDrawQ(i);
                        highlightedAction = 2;
                        resizeCorner = 4;
@@ -1054,7 +1061,7 @@ void HUD_Panel_Highlight(float allow_move)
                        return;
                }
        }
-       highlightedPanel = -1;
+       highlightedPanel = world;
        highlightedAction = 0;
 }
 
@@ -1062,8 +1069,7 @@ void HUD_Panel_EnableMenu()
 {
        menu_enabled = 2;
        menu_enabled_time = time;
-       HUD_Panel_GetName(highlightedPanel);
-       localcmd("menu_showhudoptions ", panel_name, "\n");
+       localcmd("menu_showhudoptions ", highlightedPanel.panel_name, "\n");
 }
 float mouse_over_panel;
 void HUD_Panel_Mouse()
@@ -1093,21 +1099,21 @@ void HUD_Panel_Mouse()
        {
                if(prevMouseClicked == 0)
                {
-                       if (tab_panel != -1)
+                       if (tab_panel)
                        {
                                //stop ctrl-tab selection
-                               tab_panel = -1;
+                               tab_panel = world;
                                reset_tab_panels();
                        }
                        HUD_Panel_Highlight(mouseClicked & S_MOUSE1); // sets highlightedPanel, highlightedAction, panel_click_distance, panel_click_resizeorigin
-                                                                       // and calls HUD_Panel_UpdatePosSizeForId() for the highlighted panel
-                       if (highlightedPanel != -1)
+                                                                       // and calls HUD_Panel_UpdatePosSize() for the highlighted panel
+                       if (highlightedPanel)
                        {
                                highlightedPanel_initial_pos = panel_pos;
                                highlightedPanel_initial_size = panel_size;
                        }
                        // doubleclick check
-                       if ((mouseClicked & S_MOUSE1) && time - prevMouseClickedTime < 0.4 && highlightedPanel != -1 && prevMouseClickedPos == mousepos)
+                       if ((mouseClicked & S_MOUSE1) && time - prevMouseClickedTime < 0.4 && highlightedPanel && prevMouseClickedPos == mousepos)
                        {
                                mouseClicked = 0; // to prevent spam, I guess.
                                HUD_Panel_EnableMenu();
@@ -1123,9 +1129,12 @@ void HUD_Panel_Mouse()
                        }
                }
                else
-                       HUD_Panel_UpdatePosSizeForId(highlightedPanel);
+               {
+                       panel = highlightedPanel;
+                       HUD_Panel_UpdatePosSize()
+               }
 
-               if (highlightedPanel != -1)
+               if (highlightedPanel)
                {
                        drawfill(panel_pos - '1 1 0' * panel_bg_border, panel_size + '2 2 0' * panel_bg_border, '1 1 1', .1, DRAWFLAG_NORMAL);
                        if (highlightedPanel_initial_pos != panel_pos || highlightedPanel_initial_size != panel_size)
@@ -1170,7 +1179,7 @@ void HUD_Panel_Mouse()
                        mouse_over_panel = 0;
                else
                        mouse_over_panel = HUD_Panel_Check_Mouse_Pos(TRUE);
-               if (mouse_over_panel && tab_panel == -1)
+               if (mouse_over_panel && !tab_panel)
                        drawfill(panel_pos - '1 1 0' * panel_bg_border, panel_size + '2 2 0' * panel_bg_border, '1 1 1', .1, DRAWFLAG_NORMAL);
        }
        // draw cursor after performing move/resize to have the panel pos/size updated before mouse_over_panel
index ffec62095b9b9466ffcf5db66a75251f254a18f5..cbda9dcdd5232bfbb6d50753844cddcaf996c18a 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;
@@ -391,43 +392,6 @@ float WR_SWITCHABLE        = 12; // (CSQC) impact effect
 float WR_PLAYERDEATH    = 13; // (SVQC) does not need to do anything
 float WR_GONETHINK     = 14; // (SVQC) logic to run every frame, also if no longer having the weapon as long as the switch away has not been performed
 
-float HUD_PANEL_WEAPONS                = 0;
-float HUD_PANEL_AMMO           = 1;
-float HUD_PANEL_POWERUPS       = 2;
-float HUD_PANEL_HEALTHARMOR    = 3;
-float HUD_PANEL_NOTIFY         = 4;
-float HUD_PANEL_TIMER          = 5;
-float HUD_PANEL_RADAR          = 6;
-float HUD_PANEL_SCORE          = 7;
-float HUD_PANEL_RACETIMER      = 8;
-float HUD_PANEL_VOTE           = 9;
-float HUD_PANEL_MODICONS       = 10;
-float HUD_PANEL_PRESSEDKEYS    = 11;
-float HUD_PANEL_CHAT           = 12;
-float HUD_PANEL_ENGINEINFO     = 13;
-float HUD_PANEL_INFOMESSAGES   = 14;
-float HUD_PANEL_PHYSICS        = 15;
-float HUD_PANEL_CENTERPRINT    = 16;
-float HUD_PANEL_NUM            = 17; // always last panel id + 1, please increment when adding a new panel
-
-string HUD_PANELNAME_WEAPONS           = "weapons";
-string HUD_PANELNAME_AMMO              = "ammo";
-string HUD_PANELNAME_POWERUPS          = "powerups";
-string HUD_PANELNAME_HEALTHARMOR       = "healtharmor";
-string HUD_PANELNAME_NOTIFY            = "notify";
-string HUD_PANELNAME_TIMER             = "timer";
-string HUD_PANELNAME_RADAR             = "radar";
-string HUD_PANELNAME_SCORE             = "score";
-string HUD_PANELNAME_RACETIMER         = "racetimer";
-string HUD_PANELNAME_VOTE              = "vote";
-string HUD_PANELNAME_MODICONS          = "modicons";
-string HUD_PANELNAME_PRESSEDKEYS       = "pressedkeys";
-string HUD_PANELNAME_CHAT              = "chat";
-string HUD_PANELNAME_ENGINEINFO                = "engineinfo";
-string HUD_PANELNAME_INFOMESSAGES      = "infomessages";
-string HUD_PANELNAME_PHYSICS   = "physics";
-string HUD_PANELNAME_CENTERPRINT       = "centerprint";
-
 #define SERVERFLAG_ALLOW_FULLBRIGHT 1
 #define SERVERFLAG_TEAMPLAY 2
 #define SERVERFLAG_PLAYERSTATS 4
index b4dbda25f2bc2b2617c37342f460e2e2715e6a8f..82c5673d8b44ee22cb479844517c44e8e30cea73 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 8a9c54fa6a35985fb15da4b227e787c512816dae..d1aeba001e5e2f6a4c36c5d03a8c1f2f8117febd 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!"), "") \
@@ -491,13 +496,16 @@ void Send_Notification_WOVA(
        MSG_CENTER_NOTIF(1, CENTER_DEATH_TEAMKILL_FRAG,         1, 0, "s1",           NO_CPID,             "0 0", _("^K1Moron! You fragged ^BG%s^K1, a team mate!"), _("^K1Moron! You went against ^BG%s^K1, a team mate!")) \
        MSG_CENTER_NOTIF(1, CENTER_DEATH_TEAMKILL_FRAGGED,      1, 0, "s1",           NO_CPID,             "0 0", _("^K1You were fragged by ^BG%s^K1, a team mate"), _("^K1You were scored against by ^BG%s^K1, a team mate")) \
        MSG_CENTER_NOTIF(1, CENTER_DISCONNECT_IDLING,           0, 1, "",             CPID_IDLING,         "1 f1", _("^K1Stop idling!\n^BGDisconnecting in ^COUNT..."), "") \
+       MSG_CENTER_NOTIF(1, CENTER_EXTRALIVES,                          0, 0, "",             NO_CPID,             "0 0", _("^F2You picked up some extra lives"), "") \
        MSG_CENTER_NOTIF(1, CENTER_FREEZETAG_FREEZE,            1, 0, "s1",           NO_CPID,             "0 0", _("^K3You froze ^BG%s"), "") \
        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 +520,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 +623,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 +747,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 +801,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 +845,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 c1ef62d95079a669b82ac7b54a388ca3ed8743bf..db3a39bcd5a0b4b43215eeb8fe859fa18a49979a 100644 (file)
@@ -63,17 +63,10 @@ vector Team_ColorRGB(float teamid)
 {
        switch(teamid)
        {
-               #ifdef TEAMNUMBERS_THAT_ARENT_STUPID
-               case NUM_TEAM_1: return '1 0 0'; // red
-               case NUM_TEAM_2: return '0 0 1'; // blue
-               case NUM_TEAM_3: return '1 1 0'; // yellow
-               case NUM_TEAM_4: return '1 0 1'; // pink
-               #else
                case NUM_TEAM_1: return '1 0.0625 0.0625';
                case NUM_TEAM_2: return '0.0625 0.0625 1';
                case NUM_TEAM_3: return '1 1 0.0625';
                case NUM_TEAM_4: return '1 0.0625 1';
-               #endif
        }
 
     return '0 0 0';
index df910e353927a2593f56a7d5f245d0d29702f0ed..e9bd01cfa4d34fdacb55dc1c16cd1a92b94ef1cd 100644 (file)
@@ -264,34 +264,6 @@ float get_model_parameters_fixbone;
 string get_model_parameters_desc;
 float get_model_parameters(string mod, float skn); // call with string_null to clear; skin -1 means mod is the filename of the txt file and is to be split
 
-// stupid stupid stupid FTEQCC has a max limit on macro sizes, let's work around by splitting the macro into two macros! :(
-#define HUD_Panel_GetName_Part2(id) \
-switch(id) {\
-       case HUD_PANEL_ENGINEINFO: panel_name = HUD_PANELNAME_ENGINEINFO; break; \
-       case HUD_PANEL_INFOMESSAGES: panel_name = HUD_PANELNAME_INFOMESSAGES; break; \
-       case HUD_PANEL_PHYSICS: panel_name = HUD_PANELNAME_PHYSICS; break; \
-       case HUD_PANEL_CENTERPRINT: panel_name = HUD_PANELNAME_CENTERPRINT; break; \
-} ENDS_WITH_CURLY_BRACE
-
-// Get name of specified panel id
-#define HUD_Panel_GetName(id) \
-switch(id) { \
-       case HUD_PANEL_WEAPONS: panel_name = HUD_PANELNAME_WEAPONS; break; \
-       case HUD_PANEL_AMMO: panel_name = HUD_PANELNAME_AMMO; break; \
-       case HUD_PANEL_POWERUPS: panel_name = HUD_PANELNAME_POWERUPS; break; \
-       case HUD_PANEL_HEALTHARMOR: panel_name = HUD_PANELNAME_HEALTHARMOR; break; \
-       case HUD_PANEL_NOTIFY: panel_name = HUD_PANELNAME_NOTIFY; break; \
-       case HUD_PANEL_TIMER: panel_name = HUD_PANELNAME_TIMER; break; \
-       case HUD_PANEL_RADAR: panel_name = HUD_PANELNAME_RADAR; break; \
-       case HUD_PANEL_SCORE: panel_name = HUD_PANELNAME_SCORE; break; \
-       case HUD_PANEL_RACETIMER: panel_name = HUD_PANELNAME_RACETIMER; break; \
-       case HUD_PANEL_VOTE: panel_name = HUD_PANELNAME_VOTE; break; \
-       case HUD_PANEL_MODICONS: panel_name = HUD_PANELNAME_MODICONS; break; \
-       case HUD_PANEL_PRESSEDKEYS: panel_name = HUD_PANELNAME_PRESSEDKEYS; break; \
-       case HUD_PANEL_CHAT: panel_name = HUD_PANELNAME_CHAT; break; \
-    default: HUD_Panel_GetName_Part2(id)\
-}
-
 vector vec2(vector v);
 
 #ifndef MENUQC
index 572d3a0710dd866bef056667cb2862165d80cf0b..614f6d557679a465861e400a8e3cb9e1deebee1a 100644 (file)
@@ -5,10 +5,12 @@ CLASS(InputBox) EXTENDS(Label)
        METHOD(InputBox, setText, void(entity, string))
        METHOD(InputBox, enterText, void(entity, string))
        METHOD(InputBox, keyDown, float(entity, float, float, float))
+       METHOD(InputBox, mouseMove, float(entity, vector))
        METHOD(InputBox, mouseRelease, float(entity, vector))
        METHOD(InputBox, mousePress, float(entity, vector))
        METHOD(InputBox, mouseDrag, float(entity, vector))
        METHOD(InputBox, showNotify, void(entity))
+       METHOD(InputBox, resizeNotify, void(entity, vector, vector, vector, vector))
 
        ATTRIB(InputBox, src, string, string_null)
 
@@ -26,6 +28,15 @@ CLASS(InputBox) EXTENDS(Label)
        ATTRIB(InputBox, color, vector, '1 1 1')
        ATTRIB(InputBox, colorF, vector, '1 1 1')
        ATTRIB(InputBox, maxLength, float, 255) // if negative, it counts bytes, not chars
+
+       ATTRIB(InputBox, enableClearButton, float, 1)
+       ATTRIB(InputBox, clearButton, entity, NULL)
+       ATTRIB(InputBox, cb_width, float, 0)
+       ATTRIB(InputBox, cb_pressed, float, 0)
+       ATTRIB(InputBox, cb_focused, float, 0)
+       ATTRIB(InputBox, cb_color, vector, '1 1 1')
+       ATTRIB(InputBox, cb_colorF, vector, '1 1 1')
+       ATTRIB(InputBox, cb_colorC, vector, '1 1 1')
 ENDCLASS(InputBox)
 void InputBox_Clear_Click(entity btn, entity me);
 #endif
@@ -37,6 +48,16 @@ void InputBox_configureInputBox(entity me, string theText, float theCursorPos, f
        me.src = gfx;
        me.cursorPos = theCursorPos;
 }
+void InputBox_resizeNotify(entity me, vector relOrigin, vector relSize, vector absOrigin, vector absSize)
+{
+       SUPER(InputBox).resizeNotify(me, relOrigin, relSize, absOrigin, absSize);
+       if (me.enableClearButton)
+       {
+               me.cb_width = absSize_y / absSize_x;
+               me.cb_offset = bound(-1, me.cb_offset, 0) * me.cb_width; // bound to range -1, 0
+               me.keepspaceRight = me.keepspaceRight - me.cb_offset + me.cb_width;
+       }
+}
 
 void InputBox_setText(entity me, string txt)
 {
@@ -50,18 +71,60 @@ void InputBox_Clear_Click(entity btn, entity me)
        me.setText(me, "");
 }
 
+float over_ClearButton(entity me, vector pos)
+{
+       if (pos_x >= 1 + me.cb_offset - me.cb_width)
+       if (pos_x < 1 + me.cb_offset)
+       if (pos_y >= 0)
+       if (pos_y < 1)
+               return 1;
+       return 0;
+}
+
+float InputBox_mouseMove(entity me, vector pos)
+{
+       if (me.enableClearButton)
+       {
+               if (over_ClearButton(me, pos))
+               {
+                       me.cb_focused = 1;
+                       return 1;
+               }
+               me.cb_focused = 0;
+       }
+       return 1;
+}
+
 float InputBox_mouseDrag(entity me, vector pos)
 {
        float p;
-       me.dragScrollPos = pos;
-       p = me.scrollPos + pos_x - me.keepspaceLeft;
-       me.cursorPos = draw_TextLengthUpToWidth(me.text, p, 0, me.realFontSize);
-       me.lastChangeTime = time;
+       if(me.pressed)
+       {
+               me.dragScrollPos = pos;
+               p = me.scrollPos + pos_x - me.keepspaceLeft;
+               me.cursorPos = draw_TextLengthUpToWidth(me.text, p, 0, me.realFontSize);
+               me.lastChangeTime = time;
+       }
+       else if (me.enableClearButton)
+       {
+               if (over_ClearButton(me, pos))
+               {
+                       me.cb_pressed = 1;
+                       return 1;
+               }
+       }
+       me.cb_pressed = 0;
        return 1;
 }
 
 float InputBox_mousePress(entity me, vector pos)
 {
+       if (me.enableClearButton)
+       if (over_ClearButton(me, pos))
+       {
+               me.cb_pressed = 1;
+               return 1;
+       }
        me.dragScrollTimer = time;
        me.pressed = 1;
        return InputBox_mouseDrag(me, pos);
@@ -69,8 +132,19 @@ float InputBox_mousePress(entity me, vector pos)
 
 float InputBox_mouseRelease(entity me, vector pos)
 {
+       if(me.cb_pressed)
+       if (over_ClearButton(me, pos))
+       {
+               me.cb_pressed = 0;
+               InputBox_Clear_Click(world, me);
+               return 1;
+       }
+       float r = InputBox_mouseDrag(me, pos);
+       //reset cb_pressed after mouseDrag, mouseDrag could set cb_pressed in this case:
+       //mouse press out of the clear button, drag and then mouse release over the clear button
+       me.cb_pressed = 0;
        me.pressed = 0;
-       return InputBox_mouseDrag(me, pos);
+       return r;
 }
 
 void InputBox_enterText(entity me, string ch)
@@ -295,6 +369,17 @@ void InputBox_draw(entity me)
 
        draw_ClearClip();
 
+       if (me.enableClearButton)
+       if (me.text != "")
+       {
+               if(me.focused && me.cb_pressed)
+                       draw_Picture(eX * (1 + me.cb_offset - me.cb_width), strcat(me.cb_src, "_c"), eX * me.cb_width + eY, me.cb_colorC, 1);
+               else if(me.focused && me.cb_focused)
+                       draw_Picture(eX * (1 + me.cb_offset - me.cb_width), strcat(me.cb_src, "_f"), eX * me.cb_width + eY, me.cb_colorF, 1);
+               else
+                       draw_Picture(eX * (1 + me.cb_offset - me.cb_width), strcat(me.cb_src, "_n"), eX * me.cb_width + eY, me.cb_color, 1);
+       }
+
        // skipping SUPER(InputBox).draw(me);
        Item_draw(me);
 }
index 65129b294dd6c0229436da186734c244d30b6b6f..4531a1f4e11c2f4917baef0e9d03d4f5bb4ef6f1 100644 (file)
@@ -134,6 +134,8 @@ float InputContainer_mouseDrag(entity me, vector pos)
 }
 float InputContainer_mouseMove(entity me, vector pos)
 {
+       if(me.mouseFocusedChild != me.focusedChild) // if the keyboard moved the focus away
+               me.mouseFocusedChild = NULL; // force focusing
        if(me._changeFocusXY(me, pos))
                if(SUPER(InputContainer).mouseMove(me, pos))
                        return 1;
index 64da6bcd6de8a93f0919d080bc425f2f358b3762..97eabd7d66beffd07e0263f5474a63c572db5270 100644 (file)
@@ -263,7 +263,7 @@ float Nexposee_mouseMove(entity me, vector pos)
        if(me.animationState == 0)
        {
                if(me.mouseFocusedChild)
-                       if(me.mouseFocusedChild != e)
+                       if(me.mouseFocusedChild != e || me.mouseFocusedChild != me.selectedChild)
                                me.selectedChild = me.mouseFocusedChild;
                return 1;
        }
index d30ac7248d0294ac557d8fe4b38a9a96b1814ad1..6b5cac042ca970af7df0cc83c1c4e9f5e5e14585 100644 (file)
@@ -166,6 +166,13 @@ SKINBEGIN
        SKINVECTOR(COLOR_INPUTBOX_F, '1 1 1');
        SKINFLOAT(MARGIN_INPUTBOX_CHARS, 1);
 
+       // item: clear button
+       SKINSTRING(GFX_CLEARBUTTON, "clearbutton");
+       SKINFLOAT(OFFSET_CLEARBUTTON, 0);
+       SKINVECTOR(COLOR_CLEARBUTTON_N, '1 1 1');
+       SKINVECTOR(COLOR_CLEARBUTTON_F, '1 1 1');
+       SKINVECTOR(COLOR_CLEARBUTTON_C, '1 1 1');
+
        // item: key grabber
        SKINVECTOR(COLOR_KEYGRABBER_TITLES, '1 1 1');
        SKINFLOAT(ALPHA_KEYGRABBER_TITLES, 1);
index 0e375c9cc4654e10f81b938232f25ef36eca3c93..6966aaf481b7460b337387762e542ac171dc7a7f 100644 (file)
@@ -64,7 +64,7 @@ float XonoticCharmap_mouseMove(entity me, vector coords)
                return 0;
        }
        c = y * 16 + x;
-       if(c != me.mouseSelectedCharacterCell)
+       if(c != me.mouseSelectedCharacterCell || me.mouseSelectedCharacterCell != me.selectedCharacterCell)
                me.mouseSelectedCharacterCell = me.selectedCharacterCell = c;
        return 1;
 }
index 3fcabaaab6f8aa2e25030b7e420c45b52f308baf..e6fffe093648f52966028adb370e00607330107b 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");
index bb4b969c4c80e5e07f073ab972f57e719d30e3af..84115f1ed651fcaac7852871657b170b478eb9e4 100644 (file)
@@ -20,22 +20,17 @@ entity makeXonoticDemoBrowserTab()
 }
 void XonoticDemoBrowserTab_fill(entity me)
 {
-       entity e;
-       entity btn;
-       entity dlist;
+       entity e, dlist;
 
        me.TR(me);
                me.TD(me, 1, 4, e = makeXonoticCheckBox(0, "cl_autodemo", _("Automatically record demos while playing")));
        me.TR(me);
        me.TR(me);
                me.TD(me, 1, 0.5, e = makeXonoticTextLabel(0, _("Filter:")));
-               me.TD(me, 1, 0.5, btn = makeXonoticButton(_("Clear"), '0 0 0'));
-                       btn.onClick = InputBox_Clear_Click;     
-               me.TD(me, 1, 3, e = makeXonoticInputBox(0, string_null));
+               me.TD(me, 1, 3.5, e = makeXonoticInputBox(0, string_null));
                        dlist = makeXonoticDemoList();
                        e.onChange = DemoList_Filter_Change;
                        e.onChangeEntity = dlist;
-                       btn.onClickEntity = e;
                        dlist.controlledTextbox = e;
                        
        me.TR(me);
index ee451744fb8899942380f76248d7ff8bfa638f51..02d3b410209a0af2bdf371309a45ea7325f57906 100644 (file)
@@ -20,18 +20,15 @@ entity makeXonoticServerListTab()
 }
 void XonoticServerListTab_fill(entity me)
 {
-       entity e, slist, btn;
+       entity e, slist;
 
        slist  = makeXonoticServerList();
 
        me.TR(me);
                me.TD(me, 1, 0.4, e = makeXonoticTextLabel(0, _("Filter:")));
-               me.TD(me, 1, 0.6, btn = makeXonoticButton(_("Clear"), '0 0 0'));
-                       btn.onClick = InputBox_Clear_Click;
-               me.TD(me, 1, me.columns - 0.6 * 4 - 0.4, e = makeXonoticInputBox(0, string_null));
+               me.TD(me, 1, me.columns - 0.6 * 3 - 0.4, e = makeXonoticInputBox(0, string_null));
                        e.onChange = ServerList_Filter_Change;
                        e.onChangeEntity = slist;
-                       btn.onClickEntity = e;
                        slist.controlledTextbox = e;
                me.TD(me, 1, 0.6, e = makeXonoticCheckBox(0, "menu_slist_showempty", ZCTX(_("SRVS^Empty"))));
                        slist.filterShowEmpty = e.checked;
index ef930b4e1490fe879c28ca7a83a191f191610777..21beeca6f48ea94472869ba69d7ac98742cad660 100644 (file)
@@ -45,6 +45,7 @@ void XonoticPlayerSettingsTab_fill(entity me)
                        box.forbiddenCharacters = "\r\n\\\"$"; // don't care, isn't getting saved
                        box.maxLength = -127; // negative means encoded length in bytes
                        box.saveImmediately = 1;
+                       box.enableClearButton = 0;
                        label.textEntity = box;
        me.TR(me);
                me.TD(me, 5, 1, e = makeXonoticColorpicker(box));
index b9aab55ab74dda5cf007353946118592572a78f1..37effe93256e3415ac97140777ee7bda1062669e 100644 (file)
@@ -49,6 +49,7 @@ void XonoticMiscSettingsTab_fill(entity me)
                me.TDempty(me, 0.2);
                me.TD(me, 1, 1.8, e = makeXonoticTextLabel(0, _("Client UDP port:")));
                me.TD(me, 1, 1, e = makeXonoticInputBox(0, "cl_port"));
+                       e.enableClearButton = 0;
        me.TR(me);
        me.TR(me);
                me.TDempty(me, 0.2);
index 7ebca4162ce78174a5c9e6a93ffcbaeb8bb09b94..5db6d35c62c3d6522a9bd1c222947d4dcc16e37b 100644 (file)
@@ -17,26 +17,27 @@ void XonoticCvarsDialog_showNotify(entity me)
 }
 void XonoticCvarsDialog_fill(entity me) // in this dialog, use SKINCOLOR_CVARLIST_CONTROLS to color ALL controls 
 {
-       entity e, cvarlist, btn;
-       
+
+       entity e, cvarlist;
+
        cvarlist = makeXonoticCvarList();
-       
+
        cvarlist.color = 
                cvarlist.colorF = 
                cvarlist.color2 = 
                cvarlist.colorC = 
                SKINCOLOR_CVARLIST_CONTROLS;
-       
+
        me.TR(me);
                me.TD(me, 1, 1, e = makeXonoticTextLabel(0, _("Cvar filter:")));
-               me.TD(me, 1, 0.5, btn = makeXonoticButton(_("Clear"), SKINCOLOR_CVARLIST_CONTROLS));
-               me.TD(me, 1, me.columns - 1.5, e = makeXonoticInputBox(0, string_null));
+               me.TD(me, 1, me.columns - 1, e = makeXonoticInputBox(0, string_null));
                        e.color = SKINCOLOR_CVARLIST_CONTROLS;
                        e.colorF = SKINCOLOR_CVARLIST_CONTROLS;
+                       e.cb_color = SKINCOLOR_CVARLIST_CONTROLS;
+                       e.cb_colorC = SKINCOLOR_CVARLIST_CONTROLS;
+                       e.cb_colorF = SKINCOLOR_CVARLIST_CONTROLS;
                        e.onChange = CvarList_Filter_Change;
                        e.onChangeEntity = cvarlist;
-                       btn.onClick = InputBox_Clear_Click;
-                       btn.onClickEntity = e;
                        cvarlist.controlledTextbox = e; // this COULD also be the Value box, but this leads to accidentally editing stuff
        me.TR(me);
                me.TD(me, me.rows - me.currentRow - 7, me.columns, cvarlist);
@@ -54,6 +55,9 @@ void XonoticCvarsDialog_fill(entity me) // in this dialog, use SKINCOLOR_CVARLIS
                        cvarlist.cvarValueBox = e;
                        e.color = SKINCOLOR_CVARLIST_CONTROLS;
                        e.colorF = SKINCOLOR_CVARLIST_CONTROLS;
+                       e.cb_color = SKINCOLOR_CVARLIST_CONTROLS;
+                       e.cb_colorC = SKINCOLOR_CVARLIST_CONTROLS;
+                       e.cb_colorF = SKINCOLOR_CVARLIST_CONTROLS;
                        e.onChange = CvarList_Value_Change;
                        e.onChangeEntity = cvarlist;
                        e.onEnter = CvarList_End_Editing;
index dbbcc7b0c495af9609cae5dfac612b2ef4da9f74..56aa9702c67170c82e286d62eed7cec004221999 100644 (file)
@@ -16,6 +16,13 @@ CLASS(XonoticInputBox) EXTENDS(InputBox)
 
        ATTRIB(XonoticInputBox, alpha, float, SKINALPHA_TEXT)
 
+       // Clear button attributes
+       ATTRIB(XonoticInputBox, cb_offset, float, SKINOFFSET_CLEARBUTTON) // bound to range -1, 0
+       ATTRIB(XonoticInputBox, cb_src, string, SKINGFX_CLEARBUTTON)
+       ATTRIB(XonoticInputBox, cb_color, vector, SKINCOLOR_CLEARBUTTON_N)
+       ATTRIB(XonoticInputBox, cb_colorF, vector, SKINCOLOR_CLEARBUTTON_F)
+       ATTRIB(XonoticInputBox, cb_colorC, vector, SKINCOLOR_CLEARBUTTON_C)
+
        ATTRIB(XonoticInputBox, cvarName, string, string_null)
        METHOD(XonoticInputBox, loadCvars, void(entity))
        METHOD(XonoticInputBox, saveCvars, void(entity))
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();
-                       }
-               }
-       }
-}
diff --git a/qcsrc/server/assault.qc b/qcsrc/server/assault.qc
deleted file mode 100644 (file)
index 7a5662c..0000000
+++ /dev/null
@@ -1,376 +0,0 @@
-void spawnfunc_func_breakable();
-void target_objective_decrease_activate();
-.entity assault_decreaser;
-.entity assault_sprite;
-
-void spawnfunc_info_player_attacker() {
-       if(!g_assault)
-       {
-               remove(self);
-               return;
-       }
-       self.team = NUM_TEAM_1; // red, gets swapped every round
-       spawnfunc_info_player_deathmatch();
-}
-
-void spawnfunc_info_player_defender() {
-       if(!g_assault)
-       {
-               remove(self);
-               return;
-       }
-       self.team = NUM_TEAM_2; // blue, gets swapped every round
-       spawnfunc_info_player_deathmatch();
-}
-
-// reset this objective. Used when spawning an objective
-// and when a new round starts
-void assault_objective_reset() {
-       self.health = ASSAULT_VALUE_INACTIVE;
-}
-
-void assault_objective_use() {
-       // activate objective
-       self.health = 100;
-       //print("^2Activated objective ", self.targetname, "=", etos(self), "\n");
-       //print("Activator is ", activator.classname, "\n");
-
-       entity oldself;
-       oldself = self;
-
-       for(self = world; (self = find(self, target, oldself.targetname)); )
-       {
-               if(self.classname == "target_objective_decrease")
-                       target_objective_decrease_activate();
-       }
-
-       self = oldself;
-}
-
-vector target_objective_spawn_evalfunc(entity player, entity spot, vector current)
-{
-       if(self.health < 0 || self.health >= ASSAULT_VALUE_INACTIVE)
-               return '-1 0 0';
-       return current;
-}
-
-void spawnfunc_target_objective() {
-       if(!g_assault)
-       {
-               remove(self);
-               return;
-       }
-       self.classname = "target_objective";
-       self.use = assault_objective_use;
-       assault_objective_reset();
-       self.reset = assault_objective_reset;
-       self.spawn_evalfunc = target_objective_spawn_evalfunc;
-}
-
-
-// decrease the health of targeted objectives
-void assault_objective_decrease_use() {
-       if(activator.team != assault_attacker_team) {
-               // wrong team triggered decrease
-               return;
-       }
-
-       if(other.assault_sprite)
-       {
-               WaypointSprite_Disown(other.assault_sprite, waypointsprite_deadlifetime);
-               if(other.classname == "func_assault_destructible")
-                       other.sprite = world;
-       }
-       else
-               return; // already activated! cannot activate again!
-
-       if(self.enemy.health < ASSAULT_VALUE_INACTIVE)
-       {
-               if(self.enemy.health - self.dmg > 0.5)
-               {
-                       PlayerTeamScore_Add(activator, SP_SCORE, ST_SCORE, self.dmg);
-                       self.enemy.health = self.enemy.health - self.dmg;
-               }
-               else
-               {
-                       PlayerTeamScore_Add(activator, SP_SCORE, ST_SCORE, self.enemy.health);
-                       PlayerTeamScore_Add(activator, SP_ASSAULT_OBJECTIVES, ST_ASSAULT_OBJECTIVES, 1);
-                       self.enemy.health = -1;
-
-                       entity oldself, oldactivator;
-
-                       oldself = self;
-                       self = oldself.enemy;
-                               if(self.message)
-                               {
-                                       entity player;
-                                       string s;
-                                       FOR_EACH_PLAYER(player)
-                                       {
-                                               s = strcat(self.message, "\n");
-                                               centerprint(player, s);
-                                       }
-                               }
-
-                               oldactivator = activator;
-                               activator = oldself;
-                                       SUB_UseTargets();
-                               activator = oldactivator;
-                       self = oldself;
-               }
-       }
-}
-
-void assault_setenemytoobjective()
-{
-       entity objective;
-       for(objective = world; (objective = find(objective, targetname, self.target)); ) {
-               if(objective.classname == "target_objective") {
-                       if(self.enemy == world)
-                               self.enemy = objective;
-                       else
-                               objerror("more than one objective as target - fix the map!");
-                       break;
-               }
-       }
-
-       if(self.enemy == world)
-               objerror("no objective as target - fix the map!");
-}
-
-float assault_decreaser_sprite_visible(entity e)
-{
-       entity decreaser;
-
-       decreaser = self.assault_decreaser;
-
-       if(decreaser.enemy.health >= ASSAULT_VALUE_INACTIVE)
-               return FALSE;
-
-       return TRUE;
-}
-
-void target_objective_decrease_activate()
-{
-       entity ent, spr;
-       self.owner = world;
-       for(ent = world; (ent = find(ent, target, self.targetname)); )
-       {
-               if(ent.assault_sprite != world)
-               {
-                       WaypointSprite_Disown(ent.assault_sprite, waypointsprite_deadlifetime);
-                       if(ent.classname == "func_assault_destructible")
-                               ent.sprite = world;
-               }
-
-               spr = WaypointSprite_SpawnFixed("<placeholder>", 0.5 * (ent.absmin + ent.absmax), ent, assault_sprite, RADARICON_OBJECTIVE, '1 0.5 0');
-               spr.assault_decreaser = self;
-               spr.waypointsprite_visible_for_player = assault_decreaser_sprite_visible;
-               spr.classname = "sprite_waypoint";
-               WaypointSprite_UpdateRule(spr, assault_attacker_team, SPRITERULE_TEAMPLAY);
-               if(ent.classname == "func_assault_destructible")
-               {
-                       WaypointSprite_UpdateSprites(spr, "as-defend", "as-destroy", "as-destroy");
-                       WaypointSprite_UpdateMaxHealth(spr, ent.max_health);
-                       WaypointSprite_UpdateHealth(spr, ent.health);
-                       ent.sprite = spr;
-               }
-               else
-                       WaypointSprite_UpdateSprites(spr, "as-defend", "as-push", "as-push");
-       }
-}
-
-void target_objective_decrease_findtarget()
-{
-       assault_setenemytoobjective();
-}
-
-//=============================================================================
-
-void spawnfunc_target_objective_decrease() {
-       if(!g_assault)
-       {
-               remove(self);
-               return;
-       }
-
-       self.classname = "target_objective_decrease";
-
-       if(!self.dmg) {
-               self.dmg = 101;
-       }
-       self.use = assault_objective_decrease_use;
-       self.health = ASSAULT_VALUE_INACTIVE;
-       self.max_health = ASSAULT_VALUE_INACTIVE;
-       self.enemy = world;
-
-       InitializeEntity(self, target_objective_decrease_findtarget, INITPRIO_FINDTARGET);
-}
-
-// destructible walls that can be used to trigger target_objective_decrease
-void spawnfunc_func_assault_destructible() {
-       if(!g_assault)
-       {
-               remove(self);
-               return;
-       }
-       self.spawnflags = 3;
-       self.classname = "func_assault_destructible";
-       if(assault_attacker_team == NUM_TEAM_1) {
-               self.team = NUM_TEAM_2;
-       } else {
-               self.team = NUM_TEAM_1;
-       }
-       spawnfunc_func_breakable();
-}
-
-void assault_wall_think() {
-       if(self.enemy.health < 0) {
-               self.model = "";
-               self.solid = SOLID_NOT;
-       } else {
-               self.model = self.mdl;
-               self.solid = SOLID_BSP;
-       }
-
-       self.nextthink = time + 0.2;
-}
-
-void spawnfunc_func_assault_wall() {
-       if(!g_assault)
-       {
-               remove(self);
-               return;
-       }
-       self.classname = "func_assault_wall";
-       self.mdl = self.model;
-       setmodel(self, self.mdl);
-       self.solid = SOLID_BSP;
-       self.think = assault_wall_think;
-       self.nextthink = time;
-       InitializeEntity(self, assault_setenemytoobjective, INITPRIO_FINDTARGET);
-}
-
-
-void target_assault_roundend_reset() {
-       //print("round end reset\n");
-       self.cnt = self.cnt + 1; // up round counter
-       self.winning = 0; // up round
-}
-
-void target_assault_roundend_use() {
-       self.winning = 1; // round has been won by attackers
-}
-
-void spawnfunc_target_assault_roundend() {
-       if(!g_assault)
-       {
-               remove(self);
-               return;
-       }
-       self.winning = 0; // round not yet won by attackers
-       self.classname = "target_assault_roundend";
-       self.use = target_assault_roundend_use;
-       self.cnt = 0; // first round
-       self.reset = target_assault_roundend_reset;
-}
-
-void assault_roundstart_use() {
-
-       activator = self;
-       SUB_UseTargets();
-
-
-#ifdef TTURRETS_ENABLED
-       entity ent, oldself;
-
-       //(Re)spawn all turrets
-       oldself = self;
-       ent = find(world, classname, "turret_main");
-       while(ent) {
-               // Swap turret teams
-               if(ent.team == NUM_TEAM_1)
-                       ent.team = NUM_TEAM_2;
-               else
-                       ent.team = NUM_TEAM_1;
-
-               self = ent;
-
-               // Dubbles as teamchange
-               turret_stdproc_respawn();
-
-               ent = find(ent, classname, "turret_main");
-       }
-       self = oldself;
-#endif
-
-
-}
-
-void spawnfunc_target_assault_roundstart() {
-       if(!g_assault)
-       {
-               remove(self);
-               return;
-       }
-       assault_attacker_team = NUM_TEAM_1;
-       self.classname = "target_assault_roundstart";
-       self.use = assault_roundstart_use;
-       self.reset2 = assault_roundstart_use;
-       InitializeEntity(self, assault_roundstart_use, INITPRIO_FINDTARGET);
-}
-
-// trigger new round
-// reset objectives, toggle spawnpoints, reset triggers, ...
-void vehicles_clearrturn();
-void vehicles_spawn();
-void assault_new_round()
-{
-    entity oldself;
-       //bprint("ASSAULT: new round\n");
-
-       oldself = self;
-       // Eject players from vehicles
-    FOR_EACH_PLAYER(self)
-    {
-        if(self.vehicle)
-            vehicles_exit(VHEF_RELESE);
-    }
-
-    self = findchainflags(vehicle_flags, VHF_ISVEHICLE);
-    while(self)
-    {
-        vehicles_clearrturn();
-        vehicles_spawn();
-        self = self.chain;
-    }
-
-    self = oldself;
-
-       // up round counter
-       self.winning = self.winning + 1;
-
-       // swap attacker/defender roles
-       if(assault_attacker_team == NUM_TEAM_1) {
-               assault_attacker_team = NUM_TEAM_2;
-       } else {
-               assault_attacker_team = NUM_TEAM_1;
-       }
-
-
-       entity ent;
-       for(ent = world; (ent = nextent(ent)); )
-       {
-               if(clienttype(ent) == CLIENTTYPE_NOTACLIENT)
-               {
-                       if(ent.team_saved == NUM_TEAM_1)
-                               ent.team_saved = NUM_TEAM_2;
-                       else if(ent.team_saved == NUM_TEAM_2)
-                               ent.team_saved = NUM_TEAM_1;
-               }
-       }
-
-       // reset the level with a countdown
-       cvar_set("timelimit", ftos(ceil(time - game_starttime) / 60));
-       ReadyRestart_force(); // sets game_starttime
-}
diff --git a/qcsrc/server/attic/assault.qc b/qcsrc/server/attic/assault.qc
new file mode 100644 (file)
index 0000000..7a5662c
--- /dev/null
@@ -0,0 +1,376 @@
+void spawnfunc_func_breakable();
+void target_objective_decrease_activate();
+.entity assault_decreaser;
+.entity assault_sprite;
+
+void spawnfunc_info_player_attacker() {
+       if(!g_assault)
+       {
+               remove(self);
+               return;
+       }
+       self.team = NUM_TEAM_1; // red, gets swapped every round
+       spawnfunc_info_player_deathmatch();
+}
+
+void spawnfunc_info_player_defender() {
+       if(!g_assault)
+       {
+               remove(self);
+               return;
+       }
+       self.team = NUM_TEAM_2; // blue, gets swapped every round
+       spawnfunc_info_player_deathmatch();
+}
+
+// reset this objective. Used when spawning an objective
+// and when a new round starts
+void assault_objective_reset() {
+       self.health = ASSAULT_VALUE_INACTIVE;
+}
+
+void assault_objective_use() {
+       // activate objective
+       self.health = 100;
+       //print("^2Activated objective ", self.targetname, "=", etos(self), "\n");
+       //print("Activator is ", activator.classname, "\n");
+
+       entity oldself;
+       oldself = self;
+
+       for(self = world; (self = find(self, target, oldself.targetname)); )
+       {
+               if(self.classname == "target_objective_decrease")
+                       target_objective_decrease_activate();
+       }
+
+       self = oldself;
+}
+
+vector target_objective_spawn_evalfunc(entity player, entity spot, vector current)
+{
+       if(self.health < 0 || self.health >= ASSAULT_VALUE_INACTIVE)
+               return '-1 0 0';
+       return current;
+}
+
+void spawnfunc_target_objective() {
+       if(!g_assault)
+       {
+               remove(self);
+               return;
+       }
+       self.classname = "target_objective";
+       self.use = assault_objective_use;
+       assault_objective_reset();
+       self.reset = assault_objective_reset;
+       self.spawn_evalfunc = target_objective_spawn_evalfunc;
+}
+
+
+// decrease the health of targeted objectives
+void assault_objective_decrease_use() {
+       if(activator.team != assault_attacker_team) {
+               // wrong team triggered decrease
+               return;
+       }
+
+       if(other.assault_sprite)
+       {
+               WaypointSprite_Disown(other.assault_sprite, waypointsprite_deadlifetime);
+               if(other.classname == "func_assault_destructible")
+                       other.sprite = world;
+       }
+       else
+               return; // already activated! cannot activate again!
+
+       if(self.enemy.health < ASSAULT_VALUE_INACTIVE)
+       {
+               if(self.enemy.health - self.dmg > 0.5)
+               {
+                       PlayerTeamScore_Add(activator, SP_SCORE, ST_SCORE, self.dmg);
+                       self.enemy.health = self.enemy.health - self.dmg;
+               }
+               else
+               {
+                       PlayerTeamScore_Add(activator, SP_SCORE, ST_SCORE, self.enemy.health);
+                       PlayerTeamScore_Add(activator, SP_ASSAULT_OBJECTIVES, ST_ASSAULT_OBJECTIVES, 1);
+                       self.enemy.health = -1;
+
+                       entity oldself, oldactivator;
+
+                       oldself = self;
+                       self = oldself.enemy;
+                               if(self.message)
+                               {
+                                       entity player;
+                                       string s;
+                                       FOR_EACH_PLAYER(player)
+                                       {
+                                               s = strcat(self.message, "\n");
+                                               centerprint(player, s);
+                                       }
+                               }
+
+                               oldactivator = activator;
+                               activator = oldself;
+                                       SUB_UseTargets();
+                               activator = oldactivator;
+                       self = oldself;
+               }
+       }
+}
+
+void assault_setenemytoobjective()
+{
+       entity objective;
+       for(objective = world; (objective = find(objective, targetname, self.target)); ) {
+               if(objective.classname == "target_objective") {
+                       if(self.enemy == world)
+                               self.enemy = objective;
+                       else
+                               objerror("more than one objective as target - fix the map!");
+                       break;
+               }
+       }
+
+       if(self.enemy == world)
+               objerror("no objective as target - fix the map!");
+}
+
+float assault_decreaser_sprite_visible(entity e)
+{
+       entity decreaser;
+
+       decreaser = self.assault_decreaser;
+
+       if(decreaser.enemy.health >= ASSAULT_VALUE_INACTIVE)
+               return FALSE;
+
+       return TRUE;
+}
+
+void target_objective_decrease_activate()
+{
+       entity ent, spr;
+       self.owner = world;
+       for(ent = world; (ent = find(ent, target, self.targetname)); )
+       {
+               if(ent.assault_sprite != world)
+               {
+                       WaypointSprite_Disown(ent.assault_sprite, waypointsprite_deadlifetime);
+                       if(ent.classname == "func_assault_destructible")
+                               ent.sprite = world;
+               }
+
+               spr = WaypointSprite_SpawnFixed("<placeholder>", 0.5 * (ent.absmin + ent.absmax), ent, assault_sprite, RADARICON_OBJECTIVE, '1 0.5 0');
+               spr.assault_decreaser = self;
+               spr.waypointsprite_visible_for_player = assault_decreaser_sprite_visible;
+               spr.classname = "sprite_waypoint";
+               WaypointSprite_UpdateRule(spr, assault_attacker_team, SPRITERULE_TEAMPLAY);
+               if(ent.classname == "func_assault_destructible")
+               {
+                       WaypointSprite_UpdateSprites(spr, "as-defend", "as-destroy", "as-destroy");
+                       WaypointSprite_UpdateMaxHealth(spr, ent.max_health);
+                       WaypointSprite_UpdateHealth(spr, ent.health);
+                       ent.sprite = spr;
+               }
+               else
+                       WaypointSprite_UpdateSprites(spr, "as-defend", "as-push", "as-push");
+       }
+}
+
+void target_objective_decrease_findtarget()
+{
+       assault_setenemytoobjective();
+}
+
+//=============================================================================
+
+void spawnfunc_target_objective_decrease() {
+       if(!g_assault)
+       {
+               remove(self);
+               return;
+       }
+
+       self.classname = "target_objective_decrease";
+
+       if(!self.dmg) {
+               self.dmg = 101;
+       }
+       self.use = assault_objective_decrease_use;
+       self.health = ASSAULT_VALUE_INACTIVE;
+       self.max_health = ASSAULT_VALUE_INACTIVE;
+       self.enemy = world;
+
+       InitializeEntity(self, target_objective_decrease_findtarget, INITPRIO_FINDTARGET);
+}
+
+// destructible walls that can be used to trigger target_objective_decrease
+void spawnfunc_func_assault_destructible() {
+       if(!g_assault)
+       {
+               remove(self);
+               return;
+       }
+       self.spawnflags = 3;
+       self.classname = "func_assault_destructible";
+       if(assault_attacker_team == NUM_TEAM_1) {
+               self.team = NUM_TEAM_2;
+       } else {
+               self.team = NUM_TEAM_1;
+       }
+       spawnfunc_func_breakable();
+}
+
+void assault_wall_think() {
+       if(self.enemy.health < 0) {
+               self.model = "";
+               self.solid = SOLID_NOT;
+       } else {
+               self.model = self.mdl;
+               self.solid = SOLID_BSP;
+       }
+
+       self.nextthink = time + 0.2;
+}
+
+void spawnfunc_func_assault_wall() {
+       if(!g_assault)
+       {
+               remove(self);
+               return;
+       }
+       self.classname = "func_assault_wall";
+       self.mdl = self.model;
+       setmodel(self, self.mdl);
+       self.solid = SOLID_BSP;
+       self.think = assault_wall_think;
+       self.nextthink = time;
+       InitializeEntity(self, assault_setenemytoobjective, INITPRIO_FINDTARGET);
+}
+
+
+void target_assault_roundend_reset() {
+       //print("round end reset\n");
+       self.cnt = self.cnt + 1; // up round counter
+       self.winning = 0; // up round
+}
+
+void target_assault_roundend_use() {
+       self.winning = 1; // round has been won by attackers
+}
+
+void spawnfunc_target_assault_roundend() {
+       if(!g_assault)
+       {
+               remove(self);
+               return;
+       }
+       self.winning = 0; // round not yet won by attackers
+       self.classname = "target_assault_roundend";
+       self.use = target_assault_roundend_use;
+       self.cnt = 0; // first round
+       self.reset = target_assault_roundend_reset;
+}
+
+void assault_roundstart_use() {
+
+       activator = self;
+       SUB_UseTargets();
+
+
+#ifdef TTURRETS_ENABLED
+       entity ent, oldself;
+
+       //(Re)spawn all turrets
+       oldself = self;
+       ent = find(world, classname, "turret_main");
+       while(ent) {
+               // Swap turret teams
+               if(ent.team == NUM_TEAM_1)
+                       ent.team = NUM_TEAM_2;
+               else
+                       ent.team = NUM_TEAM_1;
+
+               self = ent;
+
+               // Dubbles as teamchange
+               turret_stdproc_respawn();
+
+               ent = find(ent, classname, "turret_main");
+       }
+       self = oldself;
+#endif
+
+
+}
+
+void spawnfunc_target_assault_roundstart() {
+       if(!g_assault)
+       {
+               remove(self);
+               return;
+       }
+       assault_attacker_team = NUM_TEAM_1;
+       self.classname = "target_assault_roundstart";
+       self.use = assault_roundstart_use;
+       self.reset2 = assault_roundstart_use;
+       InitializeEntity(self, assault_roundstart_use, INITPRIO_FINDTARGET);
+}
+
+// trigger new round
+// reset objectives, toggle spawnpoints, reset triggers, ...
+void vehicles_clearrturn();
+void vehicles_spawn();
+void assault_new_round()
+{
+    entity oldself;
+       //bprint("ASSAULT: new round\n");
+
+       oldself = self;
+       // Eject players from vehicles
+    FOR_EACH_PLAYER(self)
+    {
+        if(self.vehicle)
+            vehicles_exit(VHEF_RELESE);
+    }
+
+    self = findchainflags(vehicle_flags, VHF_ISVEHICLE);
+    while(self)
+    {
+        vehicles_clearrturn();
+        vehicles_spawn();
+        self = self.chain;
+    }
+
+    self = oldself;
+
+       // up round counter
+       self.winning = self.winning + 1;
+
+       // swap attacker/defender roles
+       if(assault_attacker_team == NUM_TEAM_1) {
+               assault_attacker_team = NUM_TEAM_2;
+       } else {
+               assault_attacker_team = NUM_TEAM_1;
+       }
+
+
+       entity ent;
+       for(ent = world; (ent = nextent(ent)); )
+       {
+               if(clienttype(ent) == CLIENTTYPE_NOTACLIENT)
+               {
+                       if(ent.team_saved == NUM_TEAM_1)
+                               ent.team_saved = NUM_TEAM_2;
+                       else if(ent.team_saved == NUM_TEAM_2)
+                               ent.team_saved = NUM_TEAM_1;
+               }
+       }
+
+       // reset the level with a countdown
+       cvar_set("timelimit", ftos(ceil(time - game_starttime) / 60));
+       ReadyRestart_force(); // sets game_starttime
+}
diff --git a/qcsrc/server/attic/bot/havocbot/role_assault.qc b/qcsrc/server/attic/bot/havocbot/role_assault.qc
new file mode 100644 (file)
index 0000000..4456802
--- /dev/null
@@ -0,0 +1,205 @@
+#define HAVOCBOT_AST_ROLE_NONE                 0
+#define HAVOCBOT_AST_ROLE_DEFENSE      2
+#define HAVOCBOT_AST_ROLE_OFFENSE      4
+
+.float havocbot_role_flags;
+.float havocbot_attack_time;
+
+.void() havocbot_role;
+.void() havocbot_previous_role;
+
+void() havocbot_role_ast_defense;
+void() havocbot_role_ast_offense;
+.entity havocbot_ast_target;
+
+void(entity bot) havocbot_ast_reset_role;
+
+void(float ratingscale, vector org, float sradius) havocbot_goalrating_items;
+void(float ratingscale, vector org, float sradius) havocbot_goalrating_enemyplayers;
+
+void havocbot_goalrating_ast_targets(float ratingscale)
+{
+       entity ad, best, wp, tod;
+       float radius, found, bestvalue;
+       vector p;
+
+       ad = findchain(classname, "func_assault_destructible");
+
+       for (; ad; ad = ad.chain)
+       {
+               if (ad.target == "")
+                       continue;
+
+               if not(ad.bot_attack)
+                       continue;
+
+               found = FALSE;
+               for(tod = world; (tod = find(tod, targetname, ad.target)); )
+               {
+                       if(tod.classname == "target_objective_decrease")
+                       {
+                               if(tod.enemy.health > 0 && tod.enemy.health < ASSAULT_VALUE_INACTIVE)
+                               {
+                               //      dprint(etos(ad),"\n");
+                                       found = TRUE;
+                                       break;
+                               }
+                       }
+               }
+
+               if(!found)
+               {
+               ///     dprint("target not found\n");
+                       continue;
+               }
+               /// dprint("target #", etos(ad), " found\n");
+
+
+               p = 0.5 * (ad.absmin + ad.absmax);
+       //      dprint(vtos(ad.origin), " ", vtos(ad.absmin), " ", vtos(ad.absmax),"\n");
+       //      te_knightspike(p);
+       //      te_lightning2(world, '0 0 0', p);
+
+               // Find and rate waypoints around it
+               found = FALSE;
+               best = world;
+               bestvalue = 99999999999;
+               for(radius=0; radius<1500 && !found; radius+=500)
+               {
+                       for(wp=findradius(p, radius); wp; wp=wp.chain)
+                       {
+                               if(!(wp.wpflags & WAYPOINTFLAG_GENERATED))
+                               if(wp.classname=="waypoint")
+                               if(checkpvs(wp.origin, ad))
+                               {
+                                       found = TRUE;
+                                       if(wp.cnt<bestvalue)
+                                       {
+                                               best = wp;
+                                               bestvalue = wp.cnt;
+                                       }
+                               }
+                       }
+               }
+
+               if(best)
+               {
+               ///     dprint("waypoints around target were found\n");
+               //      te_lightning2(world, '0 0 0', best.origin);
+               //      te_knightspike(best.origin);
+
+                       navigation_routerating(best, ratingscale, 4000);
+                       best.cnt += 1;
+
+                       self.havocbot_attack_time = 0;
+
+                       if(checkpvs(self.view_ofs,ad))
+                       if(checkpvs(self.view_ofs,best))
+                       {
+                       //      dprint("increasing attack time for this target\n");
+                               self.havocbot_attack_time = time + 2;
+                       }
+               }
+       }
+}
+
+void havocbot_role_ast_offense()
+{
+       if(self.deadflag != DEAD_NO)
+       {
+               self.havocbot_attack_time = 0;
+               havocbot_ast_reset_role(self);
+               return;
+       }
+
+       // Set the role timeout if necessary
+       if (!self.havocbot_role_timeout)
+               self.havocbot_role_timeout = time + 120;
+
+       if (time > self.havocbot_role_timeout)
+       {
+               havocbot_ast_reset_role(self);
+               return;
+       }
+
+       if(self.havocbot_attack_time>time)
+               return;
+
+       if (self.bot_strategytime < time)
+       {
+               navigation_goalrating_start();
+               havocbot_goalrating_enemyplayers(20000, self.origin, 650);
+               havocbot_goalrating_ast_targets(20000);
+               havocbot_goalrating_items(15000, self.origin, 10000);
+               navigation_goalrating_end();
+
+               self.bot_strategytime = time + autocvar_bot_ai_strategyinterval;
+       }
+}
+
+void havocbot_role_ast_defense()
+{
+       if(self.deadflag != DEAD_NO)
+       {
+               self.havocbot_attack_time = 0;
+               havocbot_ast_reset_role(self);
+               return;
+       }
+
+       // Set the role timeout if necessary
+       if (!self.havocbot_role_timeout)
+               self.havocbot_role_timeout = time + 120;
+
+       if (time > self.havocbot_role_timeout)
+       {
+               havocbot_ast_reset_role(self);
+               return;
+       }
+
+       if(self.havocbot_attack_time>time)
+               return;
+
+       if (self.bot_strategytime < time)
+       {
+               navigation_goalrating_start();
+               havocbot_goalrating_enemyplayers(20000, self.origin, 3000);
+               havocbot_goalrating_ast_targets(20000);
+               havocbot_goalrating_items(15000, self.origin, 10000);
+               navigation_goalrating_end();
+
+               self.bot_strategytime = time + autocvar_bot_ai_strategyinterval;
+       }
+}
+
+void havocbot_role_ast_setrole(entity bot, float role)
+{
+       switch(role)
+       {
+               case HAVOCBOT_AST_ROLE_DEFENSE:
+                       bot.havocbot_role = havocbot_role_ast_defense;
+                       bot.havocbot_role_flags = HAVOCBOT_AST_ROLE_DEFENSE;
+                       bot.havocbot_role_timeout = 0;
+                       break;
+               case HAVOCBOT_AST_ROLE_OFFENSE:
+                       bot.havocbot_role = havocbot_role_ast_offense;
+                       bot.havocbot_role_flags = HAVOCBOT_AST_ROLE_OFFENSE;
+                       bot.havocbot_role_timeout = 0;
+                       break;
+       }
+}
+
+void havocbot_ast_reset_role(entity bot)
+{
+       if(self.deadflag != DEAD_NO)
+               return;
+
+       if(bot.team==assault_attacker_team)
+               havocbot_role_ast_setrole(bot, HAVOCBOT_AST_ROLE_OFFENSE);
+       else
+               havocbot_role_ast_setrole(bot, HAVOCBOT_AST_ROLE_DEFENSE);
+}
+
+void havocbot_chooserole_ast()
+{
+       havocbot_ast_reset_role(self);
+}
index 15ad76c8745c700de4f0c2a2d464eb42459c15e3..efb9f8c17a240cfdd5869f6a074a7cb15fd2383b 100644 (file)
@@ -75,8 +75,8 @@ 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;
 float autocvar_g_balance_armor_limit;
 float autocvar_g_balance_armor_regen;
@@ -709,6 +709,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 +816,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")
@@ -861,6 +867,7 @@ float autocvar_g_keyhunt_teams;
 float autocvar_g_keyhunt_teams_override;
 float autocvar_g_lms_campcheck_damage;
 float autocvar_g_lms_campcheck_distance;
+float autocvar_g_lms_extra_lives;
 float autocvar_g_lms_campcheck_interval;
 float autocvar_g_lms_join_anytime;
 float autocvar_g_lms_last_join;
@@ -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 3bff21ecf00a4801bd083ba12315087312f28931..cb42aa5c2c4b0b158697b4122a51c27e88543d7d 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 d87c72695f32a6a5593def26636eeba93746267f..f6e7f6f1b6921f5745982030050f76e1f350cee4 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 1b9178b1774b7632c2359463a9fd777833771326..0b8bac7333ee49ce8807733056a810a888c6fdfd 100644 (file)
@@ -1,7 +1,6 @@
 #include "havocbot.qh"
 #include "role_onslaught.qc"
 #include "role_keyhunt.qc"
-#include "role_assault.qc"
 #include "roles.qc"
 
 void havocbot_ai()
diff --git a/qcsrc/server/bot/havocbot/role_assault.qc b/qcsrc/server/bot/havocbot/role_assault.qc
deleted file mode 100644 (file)
index 4456802..0000000
+++ /dev/null
@@ -1,205 +0,0 @@
-#define HAVOCBOT_AST_ROLE_NONE                 0
-#define HAVOCBOT_AST_ROLE_DEFENSE      2
-#define HAVOCBOT_AST_ROLE_OFFENSE      4
-
-.float havocbot_role_flags;
-.float havocbot_attack_time;
-
-.void() havocbot_role;
-.void() havocbot_previous_role;
-
-void() havocbot_role_ast_defense;
-void() havocbot_role_ast_offense;
-.entity havocbot_ast_target;
-
-void(entity bot) havocbot_ast_reset_role;
-
-void(float ratingscale, vector org, float sradius) havocbot_goalrating_items;
-void(float ratingscale, vector org, float sradius) havocbot_goalrating_enemyplayers;
-
-void havocbot_goalrating_ast_targets(float ratingscale)
-{
-       entity ad, best, wp, tod;
-       float radius, found, bestvalue;
-       vector p;
-
-       ad = findchain(classname, "func_assault_destructible");
-
-       for (; ad; ad = ad.chain)
-       {
-               if (ad.target == "")
-                       continue;
-
-               if not(ad.bot_attack)
-                       continue;
-
-               found = FALSE;
-               for(tod = world; (tod = find(tod, targetname, ad.target)); )
-               {
-                       if(tod.classname == "target_objective_decrease")
-                       {
-                               if(tod.enemy.health > 0 && tod.enemy.health < ASSAULT_VALUE_INACTIVE)
-                               {
-                               //      dprint(etos(ad),"\n");
-                                       found = TRUE;
-                                       break;
-                               }
-                       }
-               }
-
-               if(!found)
-               {
-               ///     dprint("target not found\n");
-                       continue;
-               }
-               /// dprint("target #", etos(ad), " found\n");
-
-
-               p = 0.5 * (ad.absmin + ad.absmax);
-       //      dprint(vtos(ad.origin), " ", vtos(ad.absmin), " ", vtos(ad.absmax),"\n");
-       //      te_knightspike(p);
-       //      te_lightning2(world, '0 0 0', p);
-
-               // Find and rate waypoints around it
-               found = FALSE;
-               best = world;
-               bestvalue = 99999999999;
-               for(radius=0; radius<1500 && !found; radius+=500)
-               {
-                       for(wp=findradius(p, radius); wp; wp=wp.chain)
-                       {
-                               if(!(wp.wpflags & WAYPOINTFLAG_GENERATED))
-                               if(wp.classname=="waypoint")
-                               if(checkpvs(wp.origin, ad))
-                               {
-                                       found = TRUE;
-                                       if(wp.cnt<bestvalue)
-                                       {
-                                               best = wp;
-                                               bestvalue = wp.cnt;
-                                       }
-                               }
-                       }
-               }
-
-               if(best)
-               {
-               ///     dprint("waypoints around target were found\n");
-               //      te_lightning2(world, '0 0 0', best.origin);
-               //      te_knightspike(best.origin);
-
-                       navigation_routerating(best, ratingscale, 4000);
-                       best.cnt += 1;
-
-                       self.havocbot_attack_time = 0;
-
-                       if(checkpvs(self.view_ofs,ad))
-                       if(checkpvs(self.view_ofs,best))
-                       {
-                       //      dprint("increasing attack time for this target\n");
-                               self.havocbot_attack_time = time + 2;
-                       }
-               }
-       }
-}
-
-void havocbot_role_ast_offense()
-{
-       if(self.deadflag != DEAD_NO)
-       {
-               self.havocbot_attack_time = 0;
-               havocbot_ast_reset_role(self);
-               return;
-       }
-
-       // Set the role timeout if necessary
-       if (!self.havocbot_role_timeout)
-               self.havocbot_role_timeout = time + 120;
-
-       if (time > self.havocbot_role_timeout)
-       {
-               havocbot_ast_reset_role(self);
-               return;
-       }
-
-       if(self.havocbot_attack_time>time)
-               return;
-
-       if (self.bot_strategytime < time)
-       {
-               navigation_goalrating_start();
-               havocbot_goalrating_enemyplayers(20000, self.origin, 650);
-               havocbot_goalrating_ast_targets(20000);
-               havocbot_goalrating_items(15000, self.origin, 10000);
-               navigation_goalrating_end();
-
-               self.bot_strategytime = time + autocvar_bot_ai_strategyinterval;
-       }
-}
-
-void havocbot_role_ast_defense()
-{
-       if(self.deadflag != DEAD_NO)
-       {
-               self.havocbot_attack_time = 0;
-               havocbot_ast_reset_role(self);
-               return;
-       }
-
-       // Set the role timeout if necessary
-       if (!self.havocbot_role_timeout)
-               self.havocbot_role_timeout = time + 120;
-
-       if (time > self.havocbot_role_timeout)
-       {
-               havocbot_ast_reset_role(self);
-               return;
-       }
-
-       if(self.havocbot_attack_time>time)
-               return;
-
-       if (self.bot_strategytime < time)
-       {
-               navigation_goalrating_start();
-               havocbot_goalrating_enemyplayers(20000, self.origin, 3000);
-               havocbot_goalrating_ast_targets(20000);
-               havocbot_goalrating_items(15000, self.origin, 10000);
-               navigation_goalrating_end();
-
-               self.bot_strategytime = time + autocvar_bot_ai_strategyinterval;
-       }
-}
-
-void havocbot_role_ast_setrole(entity bot, float role)
-{
-       switch(role)
-       {
-               case HAVOCBOT_AST_ROLE_DEFENSE:
-                       bot.havocbot_role = havocbot_role_ast_defense;
-                       bot.havocbot_role_flags = HAVOCBOT_AST_ROLE_DEFENSE;
-                       bot.havocbot_role_timeout = 0;
-                       break;
-               case HAVOCBOT_AST_ROLE_OFFENSE:
-                       bot.havocbot_role = havocbot_role_ast_offense;
-                       bot.havocbot_role_flags = HAVOCBOT_AST_ROLE_OFFENSE;
-                       bot.havocbot_role_timeout = 0;
-                       break;
-       }
-}
-
-void havocbot_ast_reset_role(entity bot)
-{
-       if(self.deadflag != DEAD_NO)
-               return;
-
-       if(bot.team==assault_attacker_team)
-               havocbot_role_ast_setrole(bot, HAVOCBOT_AST_ROLE_OFFENSE);
-       else
-               havocbot_role_ast_setrole(bot, HAVOCBOT_AST_ROLE_DEFENSE);
-}
-
-void havocbot_chooserole_ast()
-{
-       havocbot_ast_reset_role(self);
-}
index a3078c4e43588bc64bbbeef1c2a09e490a4af928..a54874786cf6df38f2f87cbf267ba88970d5fc79 100644 (file)
@@ -277,8 +277,6 @@ void havocbot_chooserole()
                havocbot_chooserole_race();
        else if (g_onslaught)
                havocbot_chooserole_ons();
-       else if (g_assault)
-               havocbot_chooserole_ast();
        else // assume anything else is deathmatch
                havocbot_chooserole_dm();
 }
index 7f16dfd74ba449302824326641e3d6cdd07da71e..3d89301844c248c20f59dd3003e2f3dd434a337e 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);
@@ -406,13 +424,9 @@ void PutObserverInServer (void)
        if not(g_ca)  // don't reset teams when moving a ca player to the spectators
                self.team = -1;  // move this as it is needed to log the player spectating in eventlog
 
-       if(self.killcount != -666) {
-               if(g_lms) {
-                       if(PlayerScore_Add(self, SP_LMS_RANK, 0) > 0 && self.lms_spectate_warning != 2)
-                               Send_Notification(NOTIF_ALL, world, MSG_INFO, INFO_LMS_NOLIVES, self.netname);
-                       else
-                               Send_Notification(NOTIF_ALL, world, MSG_INFO, INFO_LMS_FORFEIT, self.netname);
-               } else { Send_Notification(NOTIF_ALL, world, MSG_INFO, INFO_QUIT_SPECTATE, self.netname); }
+       if(self.killcount != -666)
+       {
+               Send_Notification(NOTIF_ALL, world, MSG_INFO, INFO_QUIT_SPECTATE, self.netname);
 
                if(self.just_joined == FALSE) {
                        LogTeamchange(self.playerid, -1, 4);
@@ -443,7 +457,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 +504,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 +599,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 +618,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 +729,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 +783,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;
@@ -842,14 +808,7 @@ void PutClientInServer (void)
 
                //stuffcmd(self, "chase_active 0");
                //stuffcmd(self, "set viewsize $tmpviewsize \n");
-
-               if(g_assault) {
-                       if(self.team == assault_attacker_team)
-                               Send_Notification(NOTIF_TEAM, self, MSG_CENTER, CENTER_ASSAULT_ATTACKING);
-                       else
-                               Send_Notification(NOTIF_TEAM, self, MSG_CENTER, CENTER_ASSAULT_DEFENDING);
-               }
-
+               
                target_voicescript_clear(self);
 
                // reset fields the weapons may use
@@ -1047,14 +1006,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 +1188,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
@@ -1402,7 +1352,7 @@ void ClientConnect (void)
 
        JoinBestTeam(self, FALSE, FALSE); // if the team number is valid, keep it
 
-       if((autocvar_sv_spectate == 1 && !g_lms) || autocvar_g_campaign || self.team_forced < 0) {
+       if((autocvar_sv_spectate == 1) || autocvar_g_campaign || self.team_forced < 0) {
                self.classname = "observer";
        } else {
                if(teamplay)
@@ -1472,13 +1422,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();
@@ -1504,15 +1447,6 @@ void ClientConnect (void)
                        stuffcmd(self, "cl_cmd settemp chase_active 1\n");
        }
 
-       if(g_lms)
-       {
-               if(PlayerScore_Add(self, SP_LMS_LIVES, LMS_NewPlayerLives()) <= 0)
-               {
-                       PlayerScore_Add(self, SP_LMS_RANK, 666);
-                       self.frags = FRAGS_SPECTATOR;
-               }
-       }
-
        if(!sv_foginterval && world.fog != "")
                stuffcmd(self, strcat("\nfog ", world.fog, "\nr_fog_exp2 0\nr_drawfog 1\n"));
 
@@ -1552,13 +1486,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 +1557,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 +1914,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 +2039,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);
@@ -2164,6 +2089,46 @@ float SpectateUpdate() {
 }
 
 
+float SpectateSet()
+{
+       if(self.enemy.classname != "player")
+               return FALSE;
+       /*if(self.enemy.vehicle)
+       {
+
+               msg_entity = self;
+               WriteByte(MSG_ONE, SVC_SETVIEW);
+               WriteEntity(MSG_ONE, self.enemy);
+               //stuffcmd(self, "set viewsize $tmpviewsize \n");
+
+               self.movetype = MOVETYPE_NONE;
+               accuracy_resend(self);
+       }
+       else
+       {*/
+               msg_entity = self;
+               WriteByte(MSG_ONE, SVC_SETVIEW);
+               WriteEntity(MSG_ONE, self.enemy);
+               //stuffcmd(self, "set viewsize $tmpviewsize \n");
+               self.movetype = MOVETYPE_NONE;
+               accuracy_resend(self);
+
+               if(!SpectateUpdate())
+                       PutObserverInServer();
+       //}
+       return TRUE;
+}
+
+float Spectate(entity pl)
+{
+       if(g_ca && !autocvar_g_ca_spectate_enemies && self.caplayer)
+       if(pl.team != self.team)
+               return 0;
+
+       self.enemy = pl;
+       return SpectateSet();
+}
+
 // Returns next available player to spectate if g_ca_spectate_enemies == 0
 entity CA_SpectateNext(entity start) {
        if (start.team == self.team) {
@@ -2187,13 +2152,10 @@ entity CA_SpectateNext(entity start) {
        return other;
 }
 
-float SpectateNext(entity _prefer) {
-       
-       if(_prefer)
-               other = _prefer;        
-       else
-               other = find(self.enemy, classname, "player");
-       
+float SpectateNext()
+{
+       other = find(self.enemy, classname, "player");
+
        if (g_ca && !autocvar_g_ca_spectate_enemies && self.caplayer) {
                // CA and ca players when spectating enemies is forbidden
                other = CA_SpectateNext(other);
@@ -2202,38 +2164,49 @@ float SpectateNext(entity _prefer) {
                if (!other)
                        other = find(other, classname, "player");
        }
-       
+
        if (other)
                self.enemy = other;
 
-       if(self.enemy.classname == "player") {
-           /*if(self.enemy.vehicle)
-           {      
-            
-            msg_entity = self;
-            WriteByte(MSG_ONE, SVC_SETVIEW);
-            WriteEntity(MSG_ONE, self.enemy);
-            //stuffcmd(self, "set viewsize $tmpviewsize \n");
-            
-            self.movetype = MOVETYPE_NONE;
-            accuracy_resend(self);
-           }
-           else 
-           {*/         
-            msg_entity = self;
-            WriteByte(MSG_ONE, SVC_SETVIEW);
-            WriteEntity(MSG_ONE, self.enemy);
-            //stuffcmd(self, "set viewsize $tmpviewsize \n");
-            self.movetype = MOVETYPE_NONE;
-            accuracy_resend(self);
-
-            if(!SpectateUpdate())
-                PutObserverInServer();
-        //}
-        return 1;
-       } else {
-               return 0;
+       return SpectateSet();
+}
+
+float SpectatePrev()
+{
+       // NOTE: chain order is from the highest to the lower entnum (unlike find)
+       other = findchain(classname, "player");
+       if not(other) // no player
+               return FALSE;
+
+       entity first = other;
+       // skip players until current spectated player
+       if(self.enemy)
+       while(other && other != self.enemy)
+               other = other.chain;
+
+       if (g_ca && !autocvar_g_ca_spectate_enemies && self.caplayer)
+       {
+               do { other = other.chain; }
+               while(other && other.team != self.team);
+
+               if not(other)
+               {
+                       other = first;
+                       while(other.team != self.team)
+                               other = other.chain;
+                       if(other == self.enemy)
+                               return TRUE;
+               }
        }
+       else
+       {
+               if(other.chain)
+                       other = other.chain;
+               else
+                       other = first;
+       }
+       self.enemy = other;
+       return SpectateSet();
 }
 
 /*
@@ -2264,6 +2237,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 +2257,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 +2298,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);
@@ -2400,7 +2377,7 @@ void ObserverThink()
                        self.flags |= FL_SPAWNING;
                } else if(self.BUTTON_ATCK && !self.version_mismatch) {
                        self.flags &~= FL_JUMPRELEASED;
-                       if(SpectateNext(world) == 1) {
+                       if(SpectateNext()) {
                                self.classname = "spectator";
                        }
                } else {
@@ -2427,14 +2404,24 @@ void SpectatorThink()
                if (self.BUTTON_JUMP && !self.version_mismatch) {
                        self.flags &~= FL_JUMPRELEASED;
                        self.flags |= FL_SPAWNING;
-               } else if(self.BUTTON_ATCK) {
+               } else if(self.BUTTON_ATCK || self.impulse == 10 || self.impulse == 15 || self.impulse == 18 || self.impulse >= 200 && self.impulse <= 209) {
                        self.flags &~= FL_JUMPRELEASED;
-                       if(SpectateNext(world) == 1) {
+                       if(SpectateNext()) {
                                self.classname = "spectator";
                        } else {
                                self.classname = "observer";
                                PutClientInServer();
                        }
+                       self.impulse = 0;
+               } else if(self.impulse == 12 || self.impulse == 16  || self.impulse == 19 || self.impulse >= 220 && self.impulse <= 229) {
+                       self.flags &~= FL_JUMPRELEASED;
+                       if(SpectatePrev()) {
+                               self.classname = "spectator";
+                       } else {
+                               self.classname = "observer";
+                               PutClientInServer();
+                       }
+                       self.impulse = 0;
                } else if (self.BUTTON_ATCK2) {
                        self.flags &~= FL_JUMPRELEASED;
                        self.classname = "observer";
@@ -2490,14 +2477,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 +2605,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 +2647,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
@@ -2673,39 +2663,6 @@ void PlayerPreThink (void)
                        return;
                }
 
-               if(g_lms && !self.deadflag && autocvar_g_lms_campcheck_interval)
-               {
-                       vector dist;
-
-                       // calculate player movement (in 2 dimensions only, so jumping on one spot doesn't count as movement)
-                       dist = self.prevorigin - self.origin;
-                       dist_z = 0;
-                       self.lms_traveled_distance += fabs(vlen(dist));
-
-                       if((autocvar_g_campaign && !campaign_bots_may_start) || (time < game_starttime))
-                       {
-                               self.lms_nextcheck = time + autocvar_g_lms_campcheck_interval*2;
-                               self.lms_traveled_distance = 0;
-                       }
-
-                       if(time > self.lms_nextcheck)
-                       {
-                               //sprint(self, "distance: ", ftos(self.lms_traveled_distance), "\n");
-                               if(self.lms_traveled_distance < autocvar_g_lms_campcheck_distance)
-                               {
-                                       Send_Notification(NOTIF_ONE, self, MSG_CENTER, CENTER_LMS_CAMPCHECK);
-                                       // FIXME KadaverJack: gibbing player here causes playermodel to bounce around, instead of eye.md3
-                                       // I wasn't able to find out WHY that happens, so I put a workaround in place that shall prevent players from being gibbed :(
-                                       if(self.vehicle)
-                                               Damage(self.vehicle, self, self, autocvar_g_lms_campcheck_damage * 2, DEATH_CAMP, self.vehicle.origin, '0 0 0');
-                                       else
-                                               Damage(self, self, self, bound(0, autocvar_g_lms_campcheck_damage, self.health + self.armorvalue * autocvar_g_balance_armor_blockpercent + 5), DEATH_CAMP, self.origin, '0 0 0');
-                               }
-                               self.lms_nextcheck = time + autocvar_g_lms_campcheck_interval;
-                               self.lms_traveled_distance = 0;
-                       }
-               }
-
                self.prevorigin = self.origin;
 
                float do_crouch = self.BUTTON_CROUCH;
index 529567a3b407cc7118590aa59c451fb3444798ac..d09c0704fe56dbc2536a52a74df8a93381318560 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 a929e368c184f3f1a0ea6431318ac5a96c772de8..a318b8e3a4ad10c950a1adacc807a5da8b15e241 100644 (file)
@@ -48,13 +48,7 @@ void PlayerJump (void)
        mjumpheight = autocvar_sv_jumpvelocity;
        if (self.waterlevel >= WATERLEVEL_SWIMMING)
        {
-               if (self.watertype == CONTENT_WATER)
-                       self.velocity_z = 200;
-               else if (self.watertype == CONTENT_SLIME)
-                       self.velocity_z = 80;
-               else
-                       self.velocity_z = 50;
-
+               self.velocity_z = self.stat_sv_maxspeed * 0.7;
                return;
        }
 
index 944db7e5b8b07dd7588c90679c5ad0ba1b872a12..95aeced31b308b5db10fa4ab9da332baab5151aa 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 2a1297dc2be398d5a204852f84c880e862c54820..0014e9182d4f4c864559af9f5982902dbc37efcb 100644 (file)
@@ -301,10 +301,6 @@ float W_IsWeaponThrowable(float w)
                return 0;
        if (g_weaponarena)
                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 +350,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 +370,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 d363de52759811d727c8bc232bbbaf536bfd9544..e22399f4acd14ed8b42b8901d1dc2f9c056050e2 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 5e7810a4a48633f2285e29d91c024ac4d25b6631..3e564e39ae93f055226d64ad8e3caf5b40e13fe0 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 748b7ce6d99bb7fb5a68a3888917898b0171a263..1225b6be582cecb5feb9ecb6a645d1fd161d0ab6 100644 (file)
@@ -47,4 +47,5 @@ float readycount; // amount of players who are ready
 float readyrestart_happened; // keeps track of whether a restart has already happened
 float restart_mapalreadyrestarted; // bool, indicates whether reset_map() was already executed
 .float ready; // flag for if a player is ready
+void reset_map(float dorespawn);
 void ReadyCount();
\ No newline at end of file
index 522bb2ffca0dc94174ef97e50321431a9c113e62..cb3c34fdc4de8a5fd6e4ef2f8c716a7d650381ad 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 a7cd80136676b4ad3a9362776c6065c8c7d6dfaf..fb93b7c7876b3d133d4f5b30b2daa872b56e4aa8 100644 (file)
@@ -46,9 +46,6 @@ entity        activator;
 float player_count;
 float currentbots;
 float bots_would_leave;
-float lms_lowest_lives;
-float lms_next_place;
-float LMS_NewPlayerLives();
 
 void UpdateFrags(entity player, float f);
 .float totalfrags;
@@ -104,6 +101,7 @@ float server_is_dedicated;
 //.float cnt2;
 
 .float play_time;
+.float respawn_flags;
 .float respawn_time;
 .float death_time;
 .float fade_time;
@@ -446,8 +444,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 +579,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 +586,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 9648bccf9dba6814d026a4078b787642352e367c..6687f18ebc24064cf023faaa1c9a6b766cdb9396 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)
        {
@@ -186,24 +182,6 @@ void GiveFrags (entity attacker, entity targ, float f, float deathtype)
        else
        {
                self = oldself;
-               if(g_lms)
-               {
-                       // remove a life
-                       float tl;
-                       tl = PlayerScore_Add(targ, SP_LMS_LIVES, -1);
-                       if(tl < lms_lowest_lives)
-                               lms_lowest_lives = tl;
-                       if(tl <= 0)
-                       {
-                               if(!lms_next_place)
-                                       lms_next_place = player_count;
-                               else
-                                       lms_next_place = min(lms_next_place, player_count);
-                               PlayerScore_Add(targ, SP_LMS_RANK, lms_next_place); // won't ever spawn again
-                               --lms_next_place;
-                       }
-                       f = 0;
-               }
        }
 
        attacker.totalfrags += f;
@@ -699,14 +677,6 @@ void Damage (entity targ, entity inflictor, entity attacker, float damage, float
                        }
                }
 
-               if(targ.classname == "player")
-               if(attacker.classname == "player")
-               if(attacker != targ)
-               {
-                       targ.lms_traveled_distance = autocvar_g_lms_campcheck_distance;
-                       attacker.lms_traveled_distance = autocvar_g_lms_campcheck_distance;
-               }
-
                if(targ.classname == "player")
                if (g_minstagib)
                {
index f7af47d924036f121a6b8340a8dc2df5a0edaee4..dec6e167d1b413c074374b0d067a440591d225f1 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 ed194ef0d154cf411b7628d1837a3a20ccb07f8a..1af52b13209eea54f3d915f1a1992a1c0b12cc6f 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:");
@@ -1769,24 +1758,6 @@ float WinningCondition_Onslaught()
        return WINNING_NO;
 }
 
-float LMS_NewPlayerLives()
-{
-       float fl;
-       fl = autocvar_fraglimit;
-       if(fl == 0)
-               fl = 999;
-
-       // first player has left the game for dying too much? Nobody else can get in.
-       if(lms_lowest_lives < 1)
-               return 0;
-
-       if(!autocvar_g_lms_join_anytime)
-               if(lms_lowest_lives < fl - autocvar_g_lms_last_join)
-                       return 0;
-
-       return bound(1, lms_lowest_lives, fl);
-}
-
 // Assault winning condition: If the attackers triggered a round end (by fulfilling all objectives)
 // they win. Otherwise the defending team wins once the timelimit passes.
 void assault_new_round();
index 545aa31b258f5dd64f888f834b3226c297ad1c5b..13eb647dbdeb36d83aeebb371d88a44e4bcef1f4 100644 (file)
@@ -750,7 +750,7 @@ void readplayerstartcvars()
        s = cvar_string("g_weaponarena");
        if (s == "0" || s == "")
        {
-               if(g_lms || g_ca)
+               if(g_ca)
                        s = "most";
        }
 
@@ -828,7 +828,7 @@ void readplayerstartcvars()
                g_pinata = 0; // incompatible
                g_weapon_stay = 0; // incompatible
                WEPSET_COPY_AA(start_weapons, g_weaponarena_weapons);
-               if(!(g_lms || g_ca))
+               if(!g_ca)
                        start_items |= IT_UNLIMITED_AMMO;
        }
        else if (g_minstagib)
@@ -881,7 +881,7 @@ void readplayerstartcvars()
        }
        else
        {
-               if(g_lms || g_ca)
+               if(g_ca)
                {
                        start_ammo_shells = cvar("g_lms_start_ammo_shells");
                        start_ammo_nails = cvar("g_lms_start_ammo_nails");
@@ -899,7 +899,7 @@ void readplayerstartcvars()
                }
        }
 
-       if (g_lms || g_ca)
+       if (g_ca)
        {
                start_health = cvar("g_lms_start_health");
                start_armorvalue = cvar("g_lms_start_armor");
@@ -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 35b2e652349a3f95ac238df57e083082537c62ab..311d156304c9dbd0c99835d8ab928515e8ddc6aa 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
@@ -235,6 +248,10 @@ MUTATOR_HOOKABLE(HelpMePing);
        // INPUT
        entity self; // the player who pressed impulse 33
        
+MUTATOR_HOOKABLE(VehicleSpawn);
+       // called when a vehicle initializes
+       // return TRUE to remove the vehicle
+       
 MUTATOR_HOOKABLE(VehicleEnter);
        // called when a player enters a vehicle
        // allows mutators to set special settings in this event
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_assault.qc b/qcsrc/server/mutators/gamemode_assault.qc
new file mode 100644 (file)
index 0000000..94e1047
--- /dev/null
@@ -0,0 +1,614 @@
+// random functions
+void assault_objective_use()
+{
+       // activate objective
+       self.health = 100;
+       //print("^2Activated objective ", self.targetname, "=", etos(self), "\n");
+       //print("Activator is ", activator.classname, "\n");
+
+       entity oldself;
+       oldself = self;
+
+       for(self = world; (self = find(self, target, oldself.targetname)); )
+       {
+               if(self.classname == "target_objective_decrease")
+                       target_objective_decrease_activate();
+       }
+
+       self = oldself;
+}
+
+vector target_objective_spawn_evalfunc(entity player, entity spot, vector current)
+{
+       if(self.health < 0 || self.health >= ASSAULT_VALUE_INACTIVE)
+               return '-1 0 0';
+       return current;
+}
+
+// reset this objective. Used when spawning an objective
+// and when a new round starts
+void assault_objective_reset()
+{
+       self.health = ASSAULT_VALUE_INACTIVE;
+}
+
+// decrease the health of targeted objectives
+void assault_objective_decrease_use()
+{
+       if(activator.team != assault_attacker_team)
+       {
+               // wrong team triggered decrease
+               return;
+       }
+
+       if(other.assault_sprite)
+       {
+               WaypointSprite_Disown(other.assault_sprite, waypointsprite_deadlifetime);
+               if(other.classname == "func_assault_destructible")
+                       other.sprite = world;
+       }
+       else
+               return; // already activated! cannot activate again!
+
+       if(self.enemy.health < ASSAULT_VALUE_INACTIVE)
+       {
+               if(self.enemy.health - self.dmg > 0.5)
+               {
+                       PlayerTeamScore_Add(activator, SP_SCORE, ST_SCORE, self.dmg);
+                       self.enemy.health = self.enemy.health - self.dmg;
+               }
+               else
+               {
+                       PlayerTeamScore_Add(activator, SP_SCORE, ST_SCORE, self.enemy.health);
+                       PlayerTeamScore_Add(activator, SP_ASSAULT_OBJECTIVES, ST_ASSAULT_OBJECTIVES, 1);
+                       self.enemy.health = -1;
+
+                       entity oldself, oldactivator;
+
+                       oldself = self;
+                       self = oldself.enemy;
+                       if(self.message)
+                       {
+                               entity player;
+                               string s;
+                               FOR_EACH_PLAYER(player)
+                               {
+                                       s = strcat(self.message, "\n");
+                                       centerprint(player, s);
+                               }
+                       }
+
+                       oldactivator = activator;
+                       activator = oldself;
+                       SUB_UseTargets();
+                       activator = oldactivator;
+                       self = oldself;
+               }
+       }
+}
+
+void assault_setenemytoobjective()
+{
+       entity objective;
+       for(objective = world; (objective = find(objective, targetname, self.target)); )
+       {
+               if(objective.classname == "target_objective")
+               {
+                       if(self.enemy == world)
+                               self.enemy = objective;
+                       else
+                               objerror("more than one objective as target - fix the map!");
+                       break;
+               }
+       }
+
+       if(self.enemy == world)
+               objerror("no objective as target - fix the map!");
+}
+
+float assault_decreaser_sprite_visible(entity e)
+{
+       entity decreaser;
+
+       decreaser = self.assault_decreaser;
+
+       if(decreaser.enemy.health >= ASSAULT_VALUE_INACTIVE)
+               return FALSE;
+
+       return TRUE;
+}
+
+void target_objective_decrease_activate()
+{
+       entity ent, spr;
+       self.owner = world;
+       for(ent = world; (ent = find(ent, target, self.targetname)); )
+       {
+               if(ent.assault_sprite != world)
+               {
+                       WaypointSprite_Disown(ent.assault_sprite, waypointsprite_deadlifetime);
+                       if(ent.classname == "func_assault_destructible")
+                               ent.sprite = world;
+               }
+
+               spr = WaypointSprite_SpawnFixed("<placeholder>", 0.5 * (ent.absmin + ent.absmax), ent, assault_sprite, RADARICON_OBJECTIVE, '1 0.5 0');
+               spr.assault_decreaser = self;
+               spr.waypointsprite_visible_for_player = assault_decreaser_sprite_visible;
+               spr.classname = "sprite_waypoint";
+               WaypointSprite_UpdateRule(spr, assault_attacker_team, SPRITERULE_TEAMPLAY);
+               if(ent.classname == "func_assault_destructible")
+               {
+                       WaypointSprite_UpdateSprites(spr, "as-defend", "as-destroy", "as-destroy");
+                       WaypointSprite_UpdateMaxHealth(spr, ent.max_health);
+                       WaypointSprite_UpdateHealth(spr, ent.health);
+                       ent.sprite = spr;
+               }
+               else
+                       WaypointSprite_UpdateSprites(spr, "as-defend", "as-push", "as-push");
+       }
+}
+
+void target_objective_decrease_findtarget()
+{
+       assault_setenemytoobjective();
+}
+
+void target_assault_roundend_reset()
+{
+       //print("round end reset\n");
+       self.cnt = self.cnt + 1; // up round counter
+       self.winning = 0; // up round
+}
+
+void target_assault_roundend_use()
+{
+       self.winning = 1; // round has been won by attackers
+}
+
+void assault_roundstart_use()
+{
+       activator = self;
+       SUB_UseTargets();
+
+#ifdef TTURRETS_ENABLED
+       entity ent, oldself;
+
+       //(Re)spawn all turrets
+       oldself = self;
+       ent = find(world, classname, "turret_main");
+       while(ent) {
+               // Swap turret teams
+               if(ent.team == NUM_TEAM_1)
+                       ent.team = NUM_TEAM_2;
+               else
+                       ent.team = NUM_TEAM_1;
+
+               self = ent;
+
+               // Dubbles as teamchange
+               turret_stdproc_respawn();
+
+               ent = find(ent, classname, "turret_main");
+       }
+       self = oldself;
+#endif
+}
+
+void assault_wall_think()
+{
+       if(self.enemy.health < 0)
+       {
+               self.model = "";
+               self.solid = SOLID_NOT;
+       }
+       else
+       {
+               self.model = self.mdl;
+               self.solid = SOLID_BSP;
+       }
+
+       self.nextthink = time + 0.2;
+}
+
+// trigger new round
+// reset objectives, toggle spawnpoints, reset triggers, ...
+void vehicles_clearrturn();
+void vehicles_spawn();
+void assault_new_round()
+{
+    entity oldself;
+       //bprint("ASSAULT: new round\n");
+
+       oldself = self;
+       // Eject players from vehicles
+    FOR_EACH_PLAYER(self)
+    {
+        if(self.vehicle)
+            vehicles_exit(VHEF_RELESE);
+    }
+
+    self = findchainflags(vehicle_flags, VHF_ISVEHICLE);
+    while(self)
+    {
+        vehicles_clearrturn();
+        vehicles_spawn();
+        self = self.chain;
+    }
+
+    self = oldself;
+
+       // up round counter
+       self.winning = self.winning + 1;
+
+       // swap attacker/defender roles
+       if(assault_attacker_team == NUM_TEAM_1)
+               assault_attacker_team = NUM_TEAM_2;
+       else
+               assault_attacker_team = NUM_TEAM_1;
+
+       entity ent;
+       for(ent = world; (ent = nextent(ent)); )
+       {
+               if(clienttype(ent) == CLIENTTYPE_NOTACLIENT)
+               {
+                       if(ent.team_saved == NUM_TEAM_1)
+                               ent.team_saved = NUM_TEAM_2;
+                       else if(ent.team_saved == NUM_TEAM_2)
+                               ent.team_saved = NUM_TEAM_1;
+               }
+       }
+
+       // reset the level with a countdown
+       cvar_set("timelimit", ftos(ceil(time - game_starttime) / 60));
+       ReadyRestart_force(); // sets game_starttime
+}
+
+// spawnfuncs
+void spawnfunc_info_player_attacker()
+{
+       if not(g_assault) { remove(self); return; }
+       
+       self.team = NUM_TEAM_1; // red, gets swapped every round
+       spawnfunc_info_player_deathmatch();
+}
+
+void spawnfunc_info_player_defender()
+{
+       if not(g_assault) { remove(self); return; }
+       
+       self.team = NUM_TEAM_2; // blue, gets swapped every round
+       spawnfunc_info_player_deathmatch();
+}
+
+void spawnfunc_target_objective()
+{
+       if not(g_assault) { remove(self); return; }
+       
+       self.classname = "target_objective";
+       self.use = assault_objective_use;
+       assault_objective_reset();
+       self.reset = assault_objective_reset;
+       self.spawn_evalfunc = target_objective_spawn_evalfunc;
+}
+
+void spawnfunc_target_objective_decrease()
+{
+       if not(g_assault) { remove(self); return; }
+
+       self.classname = "target_objective_decrease";
+
+       if(!self.dmg)
+               self.dmg = 101;
+
+       self.use = assault_objective_decrease_use;
+       self.health = ASSAULT_VALUE_INACTIVE;
+       self.max_health = ASSAULT_VALUE_INACTIVE;
+       self.enemy = world;
+
+       InitializeEntity(self, target_objective_decrease_findtarget, INITPRIO_FINDTARGET);
+}
+
+// destructible walls that can be used to trigger target_objective_decrease
+void spawnfunc_func_assault_destructible()
+{
+       if not(g_assault) { remove(self); return; }
+       
+       self.spawnflags = 3;
+       self.classname = "func_assault_destructible";
+       
+       if(assault_attacker_team == NUM_TEAM_1)
+               self.team = NUM_TEAM_2;
+       else
+               self.team = NUM_TEAM_1;
+
+       spawnfunc_func_breakable();
+}
+
+void spawnfunc_func_assault_wall()
+{
+       if not(g_assault) { remove(self); return; }
+       
+       self.classname = "func_assault_wall";
+       self.mdl = self.model;
+       setmodel(self, self.mdl);
+       self.solid = SOLID_BSP;
+       self.think = assault_wall_think;
+       self.nextthink = time;
+       InitializeEntity(self, assault_setenemytoobjective, INITPRIO_FINDTARGET);
+}
+
+void spawnfunc_target_assault_roundend()
+{
+       if not(g_assault) { remove(self); return; }
+
+       self.winning = 0; // round not yet won by attackers
+       self.classname = "target_assault_roundend";
+       self.use = target_assault_roundend_use;
+       self.cnt = 0; // first round
+       self.reset = target_assault_roundend_reset;
+}
+
+void spawnfunc_target_assault_roundstart()
+{
+       if not(g_assault) { remove(self); return; }
+       
+       assault_attacker_team = NUM_TEAM_1;
+       self.classname = "target_assault_roundstart";
+       self.use = assault_roundstart_use;
+       self.reset2 = assault_roundstart_use;
+       InitializeEntity(self, assault_roundstart_use, INITPRIO_FINDTARGET);
+}
+
+// legacy bot code
+void havocbot_goalrating_ast_targets(float ratingscale)
+{
+       entity ad, best, wp, tod;
+       float radius, found, bestvalue;
+       vector p;
+
+       ad = findchain(classname, "func_assault_destructible");
+
+       for (; ad; ad = ad.chain)
+       {
+               if (ad.target == "")
+                       continue;
+
+               if not(ad.bot_attack)
+                       continue;
+
+               found = FALSE;
+               for(tod = world; (tod = find(tod, targetname, ad.target)); )
+               {
+                       if(tod.classname == "target_objective_decrease")
+                       {
+                               if(tod.enemy.health > 0 && tod.enemy.health < ASSAULT_VALUE_INACTIVE)
+                               {
+                               //      dprint(etos(ad),"\n");
+                                       found = TRUE;
+                                       break;
+                               }
+                       }
+               }
+
+               if(!found)
+               {
+               ///     dprint("target not found\n");
+                       continue;
+               }
+               /// dprint("target #", etos(ad), " found\n");
+
+
+               p = 0.5 * (ad.absmin + ad.absmax);
+       //      dprint(vtos(ad.origin), " ", vtos(ad.absmin), " ", vtos(ad.absmax),"\n");
+       //      te_knightspike(p);
+       //      te_lightning2(world, '0 0 0', p);
+
+               // Find and rate waypoints around it
+               found = FALSE;
+               best = world;
+               bestvalue = 99999999999;
+               for(radius=0; radius<1500 && !found; radius+=500)
+               {
+                       for(wp=findradius(p, radius); wp; wp=wp.chain)
+                       {
+                               if(!(wp.wpflags & WAYPOINTFLAG_GENERATED))
+                               if(wp.classname=="waypoint")
+                               if(checkpvs(wp.origin, ad))
+                               {
+                                       found = TRUE;
+                                       if(wp.cnt<bestvalue)
+                                       {
+                                               best = wp;
+                                               bestvalue = wp.cnt;
+                                       }
+                               }
+                       }
+               }
+
+               if(best)
+               {
+               ///     dprint("waypoints around target were found\n");
+               //      te_lightning2(world, '0 0 0', best.origin);
+               //      te_knightspike(best.origin);
+
+                       navigation_routerating(best, ratingscale, 4000);
+                       best.cnt += 1;
+
+                       self.havocbot_attack_time = 0;
+
+                       if(checkpvs(self.view_ofs,ad))
+                       if(checkpvs(self.view_ofs,best))
+                       {
+                       //      dprint("increasing attack time for this target\n");
+                               self.havocbot_attack_time = time + 2;
+                       }
+               }
+       }
+}
+
+void havocbot_role_ast_offense()
+{
+       if(self.deadflag != DEAD_NO)
+       {
+               self.havocbot_attack_time = 0;
+               havocbot_ast_reset_role(self);
+               return;
+       }
+
+       // Set the role timeout if necessary
+       if (!self.havocbot_role_timeout)
+               self.havocbot_role_timeout = time + 120;
+
+       if (time > self.havocbot_role_timeout)
+       {
+               havocbot_ast_reset_role(self);
+               return;
+       }
+
+       if(self.havocbot_attack_time>time)
+               return;
+
+       if (self.bot_strategytime < time)
+       {
+               navigation_goalrating_start();
+               havocbot_goalrating_enemyplayers(20000, self.origin, 650);
+               havocbot_goalrating_ast_targets(20000);
+               havocbot_goalrating_items(15000, self.origin, 10000);
+               navigation_goalrating_end();
+
+               self.bot_strategytime = time + autocvar_bot_ai_strategyinterval;
+       }
+}
+
+void havocbot_role_ast_defense()
+{
+       if(self.deadflag != DEAD_NO)
+       {
+               self.havocbot_attack_time = 0;
+               havocbot_ast_reset_role(self);
+               return;
+       }
+
+       // Set the role timeout if necessary
+       if (!self.havocbot_role_timeout)
+               self.havocbot_role_timeout = time + 120;
+
+       if (time > self.havocbot_role_timeout)
+       {
+               havocbot_ast_reset_role(self);
+               return;
+       }
+
+       if(self.havocbot_attack_time>time)
+               return;
+
+       if (self.bot_strategytime < time)
+       {
+               navigation_goalrating_start();
+               havocbot_goalrating_enemyplayers(20000, self.origin, 3000);
+               havocbot_goalrating_ast_targets(20000);
+               havocbot_goalrating_items(15000, self.origin, 10000);
+               navigation_goalrating_end();
+
+               self.bot_strategytime = time + autocvar_bot_ai_strategyinterval;
+       }
+}
+
+void havocbot_role_ast_setrole(entity bot, float role)
+{
+       switch(role)
+       {
+               case HAVOCBOT_AST_ROLE_DEFENSE:
+                       bot.havocbot_role = havocbot_role_ast_defense;
+                       bot.havocbot_role_flags = HAVOCBOT_AST_ROLE_DEFENSE;
+                       bot.havocbot_role_timeout = 0;
+                       break;
+               case HAVOCBOT_AST_ROLE_OFFENSE:
+                       bot.havocbot_role = havocbot_role_ast_offense;
+                       bot.havocbot_role_flags = HAVOCBOT_AST_ROLE_OFFENSE;
+                       bot.havocbot_role_timeout = 0;
+                       break;
+       }
+}
+
+void havocbot_ast_reset_role(entity bot)
+{
+       if(self.deadflag != DEAD_NO)
+               return;
+
+       if(bot.team == assault_attacker_team)
+               havocbot_role_ast_setrole(bot, HAVOCBOT_AST_ROLE_OFFENSE);
+       else
+               havocbot_role_ast_setrole(bot, HAVOCBOT_AST_ROLE_DEFENSE);
+}
+
+// mutator hooks
+MUTATOR_HOOKFUNCTION(assault_PlayerSpawn)
+{
+       if(self.team == assault_attacker_team)
+               centerprint(self, "You are attacking!");
+       else
+               centerprint(self, "You are defending!");
+               
+       return FALSE;
+}
+
+MUTATOR_HOOKFUNCTION(assault_TurretSpawn)
+{
+       if not (self.team)
+               self.team = 14;
+
+       return FALSE;
+}
+
+MUTATOR_HOOKFUNCTION(assault_VehicleSpawn)
+{
+       self.nextthink = time + 0.5;
+
+       return FALSE;
+}
+
+MUTATOR_HOOKFUNCTION(assault_BotRoles)
+{
+       havocbot_ast_reset_role(self);
+       return TRUE;
+}
+
+// scoreboard setup
+void assault_ScoreRules()
+{
+       ScoreRules_basics(2, SFL_SORT_PRIO_SECONDARY, SFL_SORT_PRIO_SECONDARY, TRUE);
+       ScoreInfo_SetLabel_TeamScore(  ST_ASSAULT_OBJECTIVES,    "objectives",      SFL_SORT_PRIO_PRIMARY);
+       ScoreInfo_SetLabel_PlayerScore(SP_ASSAULT_OBJECTIVES,    "objectives",      SFL_SORT_PRIO_PRIMARY);
+       ScoreRules_basics_end();
+}
+
+MUTATOR_DEFINITION(gamemode_assault)
+{
+       MUTATOR_HOOK(PlayerSpawn, assault_PlayerSpawn, CBC_ORDER_ANY);
+       MUTATOR_HOOK(TurretSpawn, assault_TurretSpawn, CBC_ORDER_ANY);
+       MUTATOR_HOOK(VehicleSpawn, assault_VehicleSpawn, CBC_ORDER_ANY);
+       MUTATOR_HOOK(HavocBot_ChooseRule, assault_BotRoles, 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.");
+               assault_ScoreRules();
+       }
+
+       MUTATOR_ONROLLBACK_OR_REMOVE
+       {
+               // we actually cannot roll back assault_Initialize here
+               // BUT: we don't need to! If this gets called, adding always
+               // succeeds.
+       }
+
+       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_assault.qh b/qcsrc/server/mutators/gamemode_assault.qh
new file mode 100644 (file)
index 0000000..9aecf87
--- /dev/null
@@ -0,0 +1,31 @@
+// sprites
+.entity assault_decreaser;
+.entity assault_sprite;
+
+// legacy bot defs
+#define HAVOCBOT_AST_ROLE_NONE                 0
+#define HAVOCBOT_AST_ROLE_DEFENSE      2
+#define HAVOCBOT_AST_ROLE_OFFENSE      4
+
+.float havocbot_role_flags;
+.float havocbot_attack_time;
+
+.void() havocbot_role;
+.void() havocbot_previous_role;
+
+void() havocbot_role_ast_defense;
+void() havocbot_role_ast_offense;
+.entity havocbot_ast_target;
+
+void(entity bot) havocbot_ast_reset_role;
+
+void(float ratingscale, vector org, float sradius) havocbot_goalrating_items;
+void(float ratingscale, vector org, float sradius) havocbot_goalrating_enemyplayers;
+
+// scoreboard stuff
+#define ST_ASSAULT_OBJECTIVES 1
+#define SP_ASSAULT_OBJECTIVES 4
+
+// predefined spawnfuncs
+void spawnfunc_func_breakable();
+void target_objective_decrease_activate();
\ No newline at end of file
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 c43a398754c34b0e513e60f189423e2a12f78406..980a9b20d9aff6c37ac55d344404a4bf9c821b5e 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 97d2dcd47936d8706973f5d495cab87ea6e88adc..13a06c367c72ca7084bb4d5d7301fa8affe73f58 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 bda701f96a015dafd8440f35df82b22ae032e147..611f7f065e10be5eda8f984c10166c76ea99fdf6 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);
diff --git a/qcsrc/server/mutators/gamemode_lms.qc b/qcsrc/server/mutators/gamemode_lms.qc
new file mode 100644 (file)
index 0000000..681a1d4
--- /dev/null
@@ -0,0 +1,249 @@
+// main functions
+float LMS_NewPlayerLives()
+{
+       float fl;
+       fl = autocvar_fraglimit;
+       if(fl == 0)
+               fl = 999;
+
+       // first player has left the game for dying too much? Nobody else can get in.
+       if(lms_lowest_lives < 1)
+               return 0;
+
+       if(!autocvar_g_lms_join_anytime)
+               if(lms_lowest_lives < fl - autocvar_g_lms_last_join)
+                       return 0;
+
+       return bound(1, lms_lowest_lives, fl);
+}
+
+// mutator hooks
+MUTATOR_HOOKFUNCTION(lms_PlayerSpawn)
+{
+       if(IS_PLAYER(self))
+       if(restart_mapalreadyrestarted || (time < game_starttime))
+               PlayerScore_Add(self, SP_LMS_LIVES, LMS_NewPlayerLives());
+               
+       return FALSE;
+}
+
+MUTATOR_HOOKFUNCTION(lms_RemovePlayer)
+{
+       // 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;
+               
+       if(self.killcount != -666)
+               if(PlayerScore_Add(self, SP_LMS_RANK, 0) > 0 && self.lms_spectate_warning != 2)
+                       Send_Notification(NOTIF_ALL, world, MSG_INFO, INFO_LMS_NOLIVES, self.netname);
+               else
+                       Send_Notification(NOTIF_ALL, world, MSG_INFO, INFO_LMS_FORFEIT, self.netname);
+               
+       return FALSE;
+}
+
+MUTATOR_HOOKFUNCTION(lms_ClientConnect)
+{
+       self.classname = "player";
+       campaign_bots_may_start = 1;
+       
+       if(PlayerScore_Add(self, SP_LMS_LIVES, LMS_NewPlayerLives()) <= 0)
+       {
+               PlayerScore_Add(self, SP_LMS_RANK, 666);
+               self.frags = FRAGS_SPECTATOR;
+       }
+                       
+       return FALSE;
+}
+
+MUTATOR_HOOKFUNCTION(lms_PlayerThink)
+{
+       if(self.deadflag == DEAD_DYING)
+               self.deadflag = DEAD_RESPAWNING;
+               
+       if not(self.deadflag)
+       if(autocvar_g_lms_campcheck_interval)
+       {
+               vector dist;
+
+               // calculate player movement (in 2 dimensions only, so jumping on one spot doesn't count as movement)
+               dist = self.prevorigin - self.origin;
+               dist_z = 0;
+               self.lms_traveled_distance += fabs(vlen(dist));
+
+               if((autocvar_g_campaign && !campaign_bots_may_start) || (time < game_starttime))
+               {
+                       self.lms_nextcheck = time + autocvar_g_lms_campcheck_interval*2;
+                       self.lms_traveled_distance = 0;
+               }
+
+               if(time > self.lms_nextcheck)
+               {
+                       if(self.lms_traveled_distance < autocvar_g_lms_campcheck_distance)
+                       {
+                               Send_Notification(NOTIF_ONE, self, MSG_CENTER, CENTER_LMS_CAMPCHECK);
+                               if(self.vehicle)
+                                       Damage(self.vehicle, self, self, autocvar_g_lms_campcheck_damage * 2, DEATH_CAMP, self.vehicle.origin, '0 0 0');
+                               else
+                                       Damage(self, self, self, bound(0, autocvar_g_lms_campcheck_damage, self.health + self.armorvalue * autocvar_g_balance_armor_blockpercent + 5), DEATH_CAMP, self.origin, '0 0 0');
+                       }
+                       self.lms_nextcheck = time + autocvar_g_lms_campcheck_interval;
+                       self.lms_traveled_distance = 0;
+               }
+       }
+               
+       return FALSE;
+}
+
+MUTATOR_HOOKFUNCTION(lms_PlayerDamage)
+{
+       if(IS_PLAYER(frag_target))
+       if(IS_PLAYER(frag_attacker))
+       if(frag_attacker != frag_target)
+       {
+               frag_target.lms_traveled_distance = autocvar_g_lms_campcheck_distance;
+               frag_attacker.lms_traveled_distance = autocvar_g_lms_campcheck_distance;
+       }
+               
+       return FALSE;
+}
+
+MUTATOR_HOOKFUNCTION(lms_ForbidThrowing)
+{
+       // forbode!
+       return TRUE;
+}
+
+MUTATOR_HOOKFUNCTION(lms_GiveFragsForKill)
+{
+       // remove a life
+       float tl;
+       tl = PlayerScore_Add(frag_target, SP_LMS_LIVES, -1);
+       if(tl < lms_lowest_lives)
+               lms_lowest_lives = tl;
+       if(tl <= 0)
+       {
+               if(!lms_next_place)
+                       lms_next_place = player_count;
+               else
+                       lms_next_place = min(lms_next_place, player_count);
+               PlayerScore_Add(frag_target, SP_LMS_RANK, lms_next_place); // won't ever spawn again
+               --lms_next_place;
+       }
+       frag_score = 0;
+               
+       return TRUE;
+}
+
+MUTATOR_HOOKFUNCTION(lms_SetStartItems)
+{
+       start_items &~= IT_UNLIMITED_AMMO;
+       start_ammo_shells = cvar("g_lms_start_ammo_shells");
+       start_ammo_nails = cvar("g_lms_start_ammo_nails");
+       start_ammo_rockets = cvar("g_lms_start_ammo_rockets");
+       start_ammo_cells = cvar("g_lms_start_ammo_cells");
+       start_ammo_fuel = cvar("g_lms_start_ammo_fuel");
+       start_health = cvar("g_lms_start_health");
+       start_armorvalue = cvar("g_lms_start_armor");
+
+       return FALSE;
+}
+
+MUTATOR_HOOKFUNCTION(lms_KeepScore)
+{
+       // don't clear player score
+       return TRUE;
+}
+
+MUTATOR_HOOKFUNCTION(lms_FilterItem)
+{
+       if(autocvar_g_lms_extra_lives)
+       if(self.classname == "item_health_mega")
+       {
+               self.max_health = 1;
+               return FALSE;
+       }
+       
+       return TRUE;
+}
+
+MUTATOR_HOOKFUNCTION(lms_ItemTouch)
+{
+       // give extra lives for mega health
+       if(self.items & IT_HEALTH)
+       {
+               Send_Notification(NOTIF_ONE, other, MSG_CENTER, CENTER_EXTRALIVES);
+               PlayerScore_Add(other, SP_LMS_LIVES, autocvar_g_lms_extra_lives);
+       }
+       
+       return FALSE;
+}
+
+MUTATOR_HOOKFUNCTION(lms_BotSpawn)
+{
+       // temporary hack to give bots lives
+       if(PlayerScore_Add(self, SP_LMS_LIVES, LMS_NewPlayerLives()) <= 0)
+       {
+               PlayerScore_Add(self, SP_LMS_RANK, 666);
+               self.frags = FRAGS_SPECTATOR;
+       }
+       
+       return FALSE;
+}
+
+// scoreboard stuff
+void lms_ScoreRules()
+{
+       ScoreRules_basics(0, 0, 0, FALSE);
+       ScoreInfo_SetLabel_PlayerScore(SP_LMS_LIVES,    "lives",     SFL_SORT_PRIO_SECONDARY);
+       ScoreInfo_SetLabel_PlayerScore(SP_LMS_RANK,     "rank",      SFL_LOWER_IS_BETTER | SFL_RANK | SFL_SORT_PRIO_PRIMARY | SFL_ALLOW_HIDE);
+       ScoreRules_basics_end();
+}
+
+void lms_Initialize()
+{
+       lms_lowest_lives = 9999;
+       lms_next_place = 0;
+       
+       lms_ScoreRules();
+}
+
+MUTATOR_DEFINITION(gamemode_lms)
+{
+       MUTATOR_HOOK(PlayerSpawn, lms_PlayerSpawn, CBC_ORDER_ANY);
+       MUTATOR_HOOK(MakePlayerObserver, lms_RemovePlayer, CBC_ORDER_ANY);
+       MUTATOR_HOOK(ClientConnect, lms_ClientConnect, CBC_ORDER_ANY);
+       MUTATOR_HOOK(PlayerPreThink, lms_PlayerThink, CBC_ORDER_ANY);
+       MUTATOR_HOOK(PlayerDamage_Calculate, lms_PlayerDamage, CBC_ORDER_ANY);
+       MUTATOR_HOOK(ForbidThrowCurrentWeapon, lms_ForbidThrowing, CBC_ORDER_ANY);
+       MUTATOR_HOOK(GiveFragsForKill, lms_GiveFragsForKill, CBC_ORDER_ANY);
+       MUTATOR_HOOK(SetStartItems, lms_SetStartItems, CBC_ORDER_ANY);
+       MUTATOR_HOOK(ForbidPlayerScore_Clear, lms_KeepScore, CBC_ORDER_ANY);
+       MUTATOR_HOOK(FilterItem, lms_FilterItem, CBC_ORDER_ANY);
+       MUTATOR_HOOK(ItemTouch, lms_ItemTouch, CBC_ORDER_ANY);
+       MUTATOR_HOOK(HavocBot_ChooseRule, lms_BotSpawn, 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.");
+               lms_Initialize();
+       }
+
+       MUTATOR_ONROLLBACK_OR_REMOVE
+       {
+               // we actually cannot roll back lms_Initialize here
+               // BUT: we don't need to! If this gets called, adding always
+               // succeeds.
+       }
+
+       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_lms.qh b/qcsrc/server/mutators/gamemode_lms.qh
new file mode 100644 (file)
index 0000000..16fda61
--- /dev/null
@@ -0,0 +1,8 @@
+// scoreboard stuff
+#define SP_LMS_LIVES 4
+#define SP_LMS_RANK 5
+
+// lives related defs
+float lms_lowest_lives;
+float lms_next_place;
+float LMS_NewPlayerLives();
\ No newline at end of file
index a351f937c7c73df9d078bcf2fe0c4febb137cd79..8f289e5c3b5e0f5821b0df4bc5b99ecbb3c7da2e 100644 (file)
@@ -21,7 +21,7 @@
 
 float _spectate(entity _player)
 {
-       if(SpectateNext(_player) == 1)
+       if(Spectate(_player) == 1)
        {
                PutObserverInServer();
                self.classname = "spectator";
@@ -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 cf90b957ea5d96e259ef60f2b1558d5ed7654e43..dab42bd65d8c39287701a4c3cffb3daf808f9609 100644 (file)
@@ -1,3 +1,6 @@
+MUTATOR_DECLARATION(gamemode_assault);
+MUTATOR_DECLARATION(gamemode_arena);
+MUTATOR_DECLARATION(gamemode_ca);
 MUTATOR_DECLARATION(gamemode_keyhunt);
 MUTATOR_DECLARATION(gamemode_freezetag);
 MUTATOR_DECLARATION(gamemode_keepaway);
@@ -5,6 +8,7 @@ MUTATOR_DECLARATION(gamemode_ctf);
 MUTATOR_DECLARATION(gamemode_nexball);
 MUTATOR_DECLARATION(gamemode_onslaught);
 MUTATOR_DECLARATION(gamemode_domination);
+MUTATOR_DECLARATION(gamemode_lms);
 
 MUTATOR_DECLARATION(mutator_dodging);
 MUTATOR_DECLARATION(mutator_invincibleprojectiles);
index 157aa38a4377e2dfc74927f741c3e1cd577cd1f7..b3cc22ddaa7a785c9a44472025a74e1b0cde3cb3 100644 (file)
@@ -35,11 +35,15 @@ defs.qh             // Should rename this, it has fields and globals
 
 mutators/base.qh
 mutators/mutators.qh
+mutators/gamemode_assault.qh
+mutators/gamemode_arena.qh
+mutators/gamemode_ca.qh
 mutators/gamemode_ctf.qh
 mutators/gamemode_domination.qh
 mutators/gamemode_keyhunt.qh // TODO fix this
 mutators/gamemode_keepaway.qh
 mutators/gamemode_nexball.qh 
+mutators/gamemode_lms.qh
 mutators/mutator_dodging.qh
 
 //// tZork Turrets ////
@@ -85,6 +89,8 @@ antilag.qh
 
 playerdemo.qh
 
+round_handler.qh
+
 // singleplayer stuff
 item_key.qh
 secret.qh
@@ -102,7 +108,6 @@ g_subs.qc
 g_tetris.qc
 
 //runematch.qc
-arena.qc
 
 g_violence.qc
 g_damage.qc
@@ -173,7 +178,7 @@ command/getreplies.qc
 command/cmd.qc
 command/sv_cmd.qc
 
-assault.qc
+//assault.qc
 
 ipban.qc
 
@@ -211,9 +216,14 @@ anticheat.qc
 cheats.qc
 playerstats.qc
 
+round_handler.qc
+
 ../common/explosion_equation.qc
 
 mutators/base.qc
+mutators/gamemode_assault.qc
+mutators/gamemode_arena.qc
+mutators/gamemode_ca.qc
 mutators/gamemode_ctf.qc
 mutators/gamemode_domination.qc
 mutators/gamemode_freezetag.qc
@@ -221,6 +231,7 @@ mutators/gamemode_keyhunt.qc
 mutators/gamemode_keepaway.qc
 mutators/gamemode_nexball.qc
 mutators/gamemode_onslaught.qc
+mutators/gamemode_lms.qc
 mutators/mutator_invincibleproj.qc
 mutators/mutator_new_toys.qc
 mutators/mutator_nix.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 e8d5590238b877b79fdb53a74d1dc44f2f1c03a0..ad10fa5d53accf9baa0e4791a96215acc0bf691c 100644 (file)
@@ -256,8 +256,8 @@ float PlayerScore_Clear(entity player)
        if(teamscores_entities_count)
                return 0;
 
-       if(g_lms) return 0;
-       if(g_arena || g_ca) return 0;
+       if(MUTATOR_CALLHOOK(ForbidPlayerScore_Clear)) 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 13fd49f29bf58e72a57ca3514b2f19103fc0d0bf..d60f4563c1cfdbf937982c9046ed66cc2f264423 100644 (file)
@@ -44,17 +44,6 @@ void ScoreRules_generic()
        ScoreRules_basics_end();
 }
 
-// LMS stuff
-#define SP_LMS_LIVES 4
-#define SP_LMS_RANK 5
-void ScoreRules_lms()
-{
-       ScoreRules_basics(0, 0, 0, FALSE);
-       ScoreInfo_SetLabel_PlayerScore(SP_LMS_LIVES,    "lives",     SFL_SORT_PRIO_SECONDARY);
-       ScoreInfo_SetLabel_PlayerScore(SP_LMS_RANK,     "rank",      SFL_LOWER_IS_BETTER | SFL_RANK | SFL_SORT_PRIO_PRIMARY | SFL_ALLOW_HIDE);
-       ScoreRules_basics_end();
-}
-
 // Key hunt stuff
 #define ST_KH_CAPS 1
 #define SP_KH_CAPS 4
@@ -104,17 +93,6 @@ void ScoreRules_race()
        ScoreRules_basics_end();
 }
 
-// Assault stuff
-#define ST_ASSAULT_OBJECTIVES 1
-#define SP_ASSAULT_OBJECTIVES 4
-void ScoreRules_assault()
-{
-       ScoreRules_basics(2, SFL_SORT_PRIO_SECONDARY, SFL_SORT_PRIO_SECONDARY, TRUE);
-       ScoreInfo_SetLabel_TeamScore(  ST_ASSAULT_OBJECTIVES,    "objectives",      SFL_SORT_PRIO_PRIMARY);
-       ScoreInfo_SetLabel_PlayerScore(SP_ASSAULT_OBJECTIVES,    "objectives",      SFL_SORT_PRIO_PRIMARY);
-       ScoreRules_basics_end();
-}
-
 // Nexball stuff
 #define ST_NEXBALL_GOALS 1
 #define SP_NEXBALL_GOALS 4
index d80b777a6f129197428f16146e36af1b32fc186a..8d81e03806267b9c5b565aa495ade4744cffa619 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 392c05ce14f3e9b9ac0671b662e510adf34393fd..40abd18b135fe920265d197357927a458308f990 100644 (file)
@@ -282,8 +282,6 @@ float have_pickup_item(void)
                        return TRUE;
                if(autocvar_g_powerups == 0)
                        return FALSE;
-               if(g_lms)
-                       return FALSE;
                if(g_ca)
                        return FALSE;
                if(g_arena)
@@ -295,8 +293,6 @@ float have_pickup_item(void)
                        return TRUE;
                if(autocvar_g_pickup_items == 0)
                        return FALSE;
-               if(g_lms)
-                       return FALSE;
                if(g_ca)
                        return FALSE;
                if(g_weaponarena)
@@ -657,7 +653,7 @@ float Item_GiveTo(entity item, entity player)
                        // sound not available
                        // AnnounceTo(player, "_lives");
                        player.armorvalue = bound(player.armorvalue, 999, player.armorvalue + autocvar_g_minstagib_extralives);
-                       sprint(player, "^3You picked up some extra lives\n");
+                       Send_Notification(NOTIF_ONE, player, MSG_CENTER, CENTER_EXTRALIVES);
                }
 
                // invis powerup
index 6608fc9a317e38e1e73686179016a354ecdabe45..16236dd16ba7d93fb52210553efe8d0ca89a5078 100644 (file)
@@ -112,19 +112,14 @@ void InitGameplayMode()
                leadlimit_override = 0; // not supported by LMS
                if(fraglimit_override == 0)
                        fraglimit_override = -1;
-               lms_lowest_lives = 9999;
-               lms_next_place = 0;
-               ScoreRules_lms();
+               MUTATOR_ADD(gamemode_lms);
        }
 
        if(g_arena)
        {
                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 +127,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();
@@ -154,7 +149,7 @@ void InitGameplayMode()
        if(g_assault)
        {
                ActivateTeamplay();
-               ScoreRules_assault();
+               MUTATOR_ADD(gamemode_assault);
                have_team_spawns = -1; // request team spawns
        }
 
index dd58b3cee2860e0c031dc131a47eab165c846967..726aa7be44c9df5f6963e79a5af55ac820a038bc 100644 (file)
@@ -1060,12 +1060,7 @@ float turret_stdproc_init (string cvar_base_name, string base, string head, floa
     self.effects = EF_NODRAW;
     
     // Handle turret teams.
-    if (autocvar_g_assault != 0)
-    {
-        if not (self.team)
-            self.team = 14; // Assume turrets are on the defending side if not explicitly set otehrwize
-    }
-    else if not (teamplay)
+    if not (teamplay)
                self.team = MAX_SHOT_DISTANCE; // Group all turrets into the same team, so they dont kill eachother.
        else if(g_onslaught && self.targetname)
        {
index a79fcc62a4e2df25044e7d4a55e7647ecf4273f4..1b1ae3f647628626c24440d0b020ac8654909be1 100644 (file)
@@ -1275,7 +1275,7 @@ float vehicle_initialize(string  net_name,
     self.vehicle_respawntime = _respawntime;
     self.vehicle_spawn       = spawnproc;
     self.effects             = EF_NODRAW;
-    if(g_assault || !autocvar_g_vehicles_delayspawn)
+    if(!autocvar_g_vehicles_delayspawn)
         self.nextthink = time + 0.5;
     else
         self.nextthink = time + _respawntime + (random() * autocvar_g_vehicles_delayspawn_jitter);
@@ -1315,6 +1315,9 @@ float vehicle_initialize(string  net_name,
     self.pos1 = self.origin;
     self.pos2 = self.angles;
     self.tur_head.team = self.team;
+       
+       if(MUTATOR_CALLHOOK(VehicleSpawn))
+               return FALSE;
 
     return TRUE;
 }
index b34d66bf677184220955ef3b6b69304c1130ab3d..611319a16cd94f4794e5606ed55fb35205f4a54b 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
        {