From: Samual Lenks Date: Wed, 8 May 2013 01:10:59 +0000 (-0400) Subject: Merge remote-tracking branch 'origin/master' into terencehill/spectate_prev X-Git-Tag: xonotic-v0.7.0~55^2~11^2~1 X-Git-Url: http://de.git.xonotic.org/?p=xonotic%2Fxonotic-data.pk3dir.git;a=commitdiff_plain;h=1633673cc6c65bdca0120f2a4fc33e8c2449f06d;hp=b0e4ebf4cf698adc8d6322fb366d0e6045d555e6 Merge remote-tracking branch 'origin/master' into terencehill/spectate_prev --- diff --git a/_hud_common.cfg b/_hud_common.cfg index ca90bc9da2..15453b7dcc 100644 --- a/_hud_common.cfg +++ b/_hud_common.cfg @@ -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" diff --git a/_hud_descriptions.cfg b/_hud_descriptions.cfg index ea7552a2a6..9c7201d0ce 100644 --- a/_hud_descriptions.cfg +++ b/_hud_descriptions.cfg @@ -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" diff --git a/balanceXPM.cfg b/balanceXPM.cfg index fc02dc9cb2..8012967ad7 100644 --- a/balanceXPM.cfg +++ b/balanceXPM.cfg @@ -475,10 +475,10 @@ set g_balance_crylink_reload_ammo 0 //default: 10 set g_balance_crylink_reload_time 2 // }}} // {{{ nex -set g_balance_nex_primary_damage 90 +set g_balance_nex_primary_damage 80 set g_balance_nex_primary_force 400 set g_balance_nex_primary_refire 1.5 -set g_balance_nex_primary_animtime 0.4 +set g_balance_nex_primary_animtime 0.5 set g_balance_nex_primary_ammo 6 set g_balance_nex_primary_damagefalloff_mindist 0 // 1000 For tZork ;3 set g_balance_nex_primary_damagefalloff_maxdist 0 // 3000 diff --git a/balanceXonotic.cfg b/balanceXonotic.cfg index a0252cb48b..c96ea21870 100644 --- a/balanceXonotic.cfg +++ b/balanceXonotic.cfg @@ -475,10 +475,10 @@ set g_balance_crylink_reload_ammo 0 //default: 10 set g_balance_crylink_reload_time 2 // }}} // {{{ nex -set g_balance_nex_primary_damage 90 +set g_balance_nex_primary_damage 80 set g_balance_nex_primary_force 400 set g_balance_nex_primary_refire 1.5 -set g_balance_nex_primary_animtime 0.4 +set g_balance_nex_primary_animtime 0.5 set g_balance_nex_primary_ammo 6 set g_balance_nex_primary_damagefalloff_mindist 0 // 1000 For tZork ;3 set g_balance_nex_primary_damagefalloff_maxdist 0 // 3000 diff --git a/defaultXonotic.cfg b/defaultXonotic.cfg index 2af290f9ab..de83c9ae57 100644 --- a/defaultXonotic.cfg +++ b/defaultXonotic.cfg @@ -900,7 +900,7 @@ seta "userbind8_press" "say_team flag seen (l:%y^7); g_waypointsprite_team_here_ seta "userbind9_press" "say_team defending (l:%l^7) (h:%h^7 a:%a^7 w:%w^7); g_waypointsprite_team_here"; seta "userbind9_release" ""; seta "userbind9_description" "team: defending, icon" seta "userbind10_press" "say_team roaming (l:%l^7) (h:%h^7 a:%a^7 w:%w^7); g_waypointsprite_team_here"; seta "userbind10_release" ""; seta "userbind10_description" "team: roaming, icon" seta "userbind11_press" "say_team attacking (l:%l^7) (h:%h^7 a:%a^7 w:%w^7); g_waypointsprite_team_here"; seta "userbind11_release" ""; seta "userbind11_description" "team: attacking, icon" -seta "userbind12_press" "say_team killed flagcarrier (l:%y^7); g_waypointsprite_team_p"; seta "userbind12_release" ""; seta "userbind12_description" "team: killed flag, icon" +seta "userbind12_press" "say_team killed flagcarrier (l:%y^7); g_waypointsprite_team_here_p"; seta "userbind12_release" ""; seta "userbind12_description" "team: killed flag, icon" seta "userbind13_press" "say_team dropped flag (l:%d^7); g_waypointsprite_team_here_d"; seta "userbind13_release" ""; seta "userbind13_description" "team: dropped flag, icon" seta "userbind14_press" "say_team dropped gun %w^7 (l:%l^7); g_waypointsprite_team_here; wait; dropweapon"; seta "userbind14_release" ""; seta "userbind14_description" "team: drop gun, icon" // TODO change this to "use" once we can @@ -989,8 +989,7 @@ seta menu_slist_showfull 1 "show servers even if they are full and have no slots seta menu_slist_showempty 1 "show servers even if they are no empty and have no opponents to play against" seta menu_slist_modfilter "" // set to either: !modname or modname. modname of = means "same as we are running now". -// for menu weapon arena -set menu_weaponarena_with_laser 0 "also enable the Laser in this weapon arena" +seta menu_weaponarena "" seta menu_maxplayers 16 "maxplayers value when the menu starts a game" diff --git a/gamemodes.cfg b/gamemodes.cfg index af8d3e83d8..b7e65c8eb8 100644 --- a/gamemodes.cfg +++ b/gamemodes.cfg @@ -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 index 0000000000..89a7659218 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 index 0000000000..8717d8f76d 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 index 0000000000..3de1ccb804 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 index 0000000000..be0939c067 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 index 0000000000..57d76c47e1 Binary files /dev/null and b/gfx/menu/luminos/clearbutton_n.tga differ diff --git a/gfx/menu/luminos/skinvalues.txt b/gfx/menu/luminos/skinvalues.txt index 316ae273f8..e79dba0e09 100755 --- a/gfx/menu/luminos/skinvalues.txt +++ b/gfx/menu/luminos/skinvalues.txt @@ -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 index 0000000000..26bc0effef 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 index 0000000000..e857381452 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 index 0000000000..26bc0effef Binary files /dev/null and b/gfx/menu/wickedx/clearbutton_n.tga differ diff --git a/gfx/menu/wickedx/skinvalues.txt b/gfx/menu/wickedx/skinvalues.txt index 3259d7122a..1560512858 100755 --- a/gfx/menu/wickedx/skinvalues.txt +++ b/gfx/menu/wickedx/skinvalues.txt @@ -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 index 0000000000..d8bfea5adb 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 index 0000000000..338ac1f586 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 index 0000000000..60ee8c8d10 Binary files /dev/null and b/gfx/menu/xaw/clearbutton_n.tga differ diff --git a/gfx/menu/xaw/skinvalues.txt b/gfx/menu/xaw/skinvalues.txt index 36db25d5cd..3bd555f031 100644 --- a/gfx/menu/xaw/skinvalues.txt +++ b/gfx/menu/xaw/skinvalues.txt @@ -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 diff --git a/hud_luminos.cfg b/hud_luminos.cfg index bb0c97e099..f99494a1cc 100644 --- a/hud_luminos.cfg +++ b/hud_luminos.cfg @@ -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" diff --git a/hud_luminos_minimal.cfg b/hud_luminos_minimal.cfg index 775ddaf8f1..bfa0c0b3e4 100644 --- a/hud_luminos_minimal.cfg +++ b/hud_luminos_minimal.cfg @@ -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" diff --git a/hud_luminos_minimal_xhair.cfg b/hud_luminos_minimal_xhair.cfg index b7a208dbdb..27ca9ab80d 100644 --- a/hud_luminos_minimal_xhair.cfg +++ b/hud_luminos_minimal_xhair.cfg @@ -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" diff --git a/hud_luminos_old.cfg b/hud_luminos_old.cfg index 2e718b288b..3a489a04e4 100644 --- a/hud_luminos_old.cfg +++ b/hud_luminos_old.cfg @@ -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" diff --git a/hud_nexuiz.cfg b/hud_nexuiz.cfg index d4e71d8762..57053411d7 100644 --- a/hud_nexuiz.cfg +++ b/hud_nexuiz.cfg @@ -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" diff --git a/notifications.cfg b/notifications.cfg index 0006f6a8ea..1463d19b8f 100644 --- a/notifications.cfg +++ b/notifications.cfg @@ -11,7 +11,48 @@ // ** ** // // ********************************************** // -// MSG_INFO notifications (count = 208): +// MSG_ANNCE notifications (count = 39): +seta notification_ANNCE_ACHIEVEMENT_AIRSHOT "1" "Notification control cvar: 0 = disabled, 1 = enabled if gentle mode is off, 2 = always enabled)" +seta notification_ANNCE_ACHIEVEMENT_AMAZING "1" "Notification control cvar: 0 = disabled, 1 = enabled if gentle mode is off, 2 = always enabled)" +seta notification_ANNCE_ACHIEVEMENT_AWESOME "1" "Notification control cvar: 0 = disabled, 1 = enabled if gentle mode is off, 2 = always enabled)" +seta notification_ANNCE_ACHIEVEMENT_BOTLIKE "1" "Notification control cvar: 0 = disabled, 1 = enabled if gentle mode is off, 2 = always enabled)" +seta notification_ANNCE_ACHIEVEMENT_ELECTROBITCH "2" "Notification control cvar: 0 = disabled, 1 = enabled if gentle mode is off, 2 = always enabled)" +seta notification_ANNCE_ACHIEVEMENT_IMPRESSIVE "1" "Notification control cvar: 0 = disabled, 1 = enabled if gentle mode is off, 2 = always enabled)" +seta notification_ANNCE_ACHIEVEMENT_YODA "1" "Notification control cvar: 0 = disabled, 1 = enabled if gentle mode is off, 2 = always enabled)" +seta notification_ANNCE_BEGIN "2" "Notification control cvar: 0 = disabled, 1 = enabled if gentle mode is off, 2 = always enabled)" +seta notification_ANNCE_KILLSTREAK_03 "1" "Notification control cvar: 0 = disabled, 1 = enabled if gentle mode is off, 2 = always enabled)" +seta notification_ANNCE_KILLSTREAK_05 "1" "Notification control cvar: 0 = disabled, 1 = enabled if gentle mode is off, 2 = always enabled)" +seta notification_ANNCE_KILLSTREAK_10 "1" "Notification control cvar: 0 = disabled, 1 = enabled if gentle mode is off, 2 = always enabled)" +seta notification_ANNCE_KILLSTREAK_15 "1" "Notification control cvar: 0 = disabled, 1 = enabled if gentle mode is off, 2 = always enabled)" +seta notification_ANNCE_KILLSTREAK_20 "1" "Notification control cvar: 0 = disabled, 1 = enabled if gentle mode is off, 2 = always enabled)" +seta notification_ANNCE_KILLSTREAK_25 "1" "Notification control cvar: 0 = disabled, 1 = enabled if gentle mode is off, 2 = always enabled)" +seta notification_ANNCE_KILLSTREAK_30 "1" "Notification control cvar: 0 = disabled, 1 = enabled if gentle mode is off, 2 = always enabled)" +seta notification_ANNCE_MINSTAGIB_LASTSECOND "1" "Notification control cvar: 0 = disabled, 1 = enabled if gentle mode is off, 2 = always enabled)" +seta notification_ANNCE_MINSTAGIB_NARROWLY "1" "Notification control cvar: 0 = disabled, 1 = enabled if gentle mode is off, 2 = always enabled)" +seta notification_ANNCE_MINSTAGIB_TERMINATED "1" "Notification control cvar: 0 = disabled, 1 = enabled if gentle mode is off, 2 = always enabled)" +seta notification_ANNCE_MULTIFRAG "0" "Notification control cvar: 0 = disabled, 1 = enabled if gentle mode is off, 2 = always enabled)" +seta notification_ANNCE_NUM_1 "2" "Notification control cvar: 0 = disabled, 1 = enabled if gentle mode is off, 2 = always enabled)" +seta notification_ANNCE_NUM_2 "2" "Notification control cvar: 0 = disabled, 1 = enabled if gentle mode is off, 2 = always enabled)" +seta notification_ANNCE_NUM_3 "2" "Notification control cvar: 0 = disabled, 1 = enabled if gentle mode is off, 2 = always enabled)" +seta notification_ANNCE_NUM_4 "2" "Notification control cvar: 0 = disabled, 1 = enabled if gentle mode is off, 2 = always enabled)" +seta notification_ANNCE_NUM_5 "2" "Notification control cvar: 0 = disabled, 1 = enabled if gentle mode is off, 2 = always enabled)" +seta notification_ANNCE_NUM_6 "2" "Notification control cvar: 0 = disabled, 1 = enabled if gentle mode is off, 2 = always enabled)" +seta notification_ANNCE_NUM_7 "2" "Notification control cvar: 0 = disabled, 1 = enabled if gentle mode is off, 2 = always enabled)" +seta notification_ANNCE_NUM_8 "2" "Notification control cvar: 0 = disabled, 1 = enabled if gentle mode is off, 2 = always enabled)" +seta notification_ANNCE_NUM_9 "2" "Notification control cvar: 0 = disabled, 1 = enabled if gentle mode is off, 2 = always enabled)" +seta notification_ANNCE_NUM_10 "2" "Notification control cvar: 0 = disabled, 1 = enabled if gentle mode is off, 2 = always enabled)" +seta notification_ANNCE_PREPARE "2" "Notification control cvar: 0 = disabled, 1 = enabled if gentle mode is off, 2 = always enabled)" +seta notification_ANNCE_REMAINING_FRAG_1 "1" "Notification control cvar: 0 = disabled, 1 = enabled if gentle mode is off, 2 = always enabled)" +seta notification_ANNCE_REMAINING_FRAG_2 "1" "Notification control cvar: 0 = disabled, 1 = enabled if gentle mode is off, 2 = always enabled)" +seta notification_ANNCE_REMAINING_FRAG_3 "1" "Notification control cvar: 0 = disabled, 1 = enabled if gentle mode is off, 2 = always enabled)" +seta notification_ANNCE_REMAINING_MIN_1 "2" "Notification control cvar: 0 = disabled, 1 = enabled if gentle mode is off, 2 = always enabled)" +seta notification_ANNCE_REMAINING_MIN_5 "2" "Notification control cvar: 0 = disabled, 1 = enabled if gentle mode is off, 2 = always enabled)" +seta notification_ANNCE_TIMEOUT "2" "Notification control cvar: 0 = disabled, 1 = enabled if gentle mode is off, 2 = always enabled)" +seta notification_ANNCE_VOTE_ACCEPT "2" "Notification control cvar: 0 = disabled, 1 = enabled if gentle mode is off, 2 = always enabled)" +seta notification_ANNCE_VOTE_CALL "2" "Notification control cvar: 0 = disabled, 1 = enabled if gentle mode is off, 2 = always enabled)" +seta notification_ANNCE_VOTE_FAIL "2" "Notification control cvar: 0 = disabled, 1 = enabled if gentle mode is off, 2 = always enabled)" + +// MSG_INFO notifications (count = 207): seta notification_INFO_CTF_CAPTURE_RED "1" "Notification control cvar: 0 = off, 1 = print to console, 2 = print to console and chatbox (if notification_allow_chatboxprint is enabled)" seta notification_INFO_CTF_CAPTURE_BLUE "1" "Notification control cvar: 0 = off, 1 = print to console, 2 = print to console and chatbox (if notification_allow_chatboxprint is enabled)" seta notification_INFO_CTF_CAPTURE_BROKEN_RED "1" "Notification control cvar: 0 = off, 1 = print to console, 2 = print to console and chatbox (if notification_allow_chatboxprint is enabled)" @@ -124,7 +165,7 @@ seta notification_INFO_JOIN_CONNECT_TEAM_RED "2" "Notification control cvar: 0 = seta notification_INFO_JOIN_CONNECT_TEAM_BLUE "2" "Notification control cvar: 0 = off, 1 = print to console, 2 = print to console and chatbox (if notification_allow_chatboxprint is enabled)" seta notification_INFO_JOIN_CONNECT_TEAM_YELLOW "2" "Notification control cvar: 0 = off, 1 = print to console, 2 = print to console and chatbox (if notification_allow_chatboxprint is enabled)" seta notification_INFO_JOIN_CONNECT_TEAM_PINK "2" "Notification control cvar: 0 = off, 1 = print to console, 2 = print to console and chatbox (if notification_allow_chatboxprint is enabled)" -seta notification_INFO_JOIN_PLAY "2" "Notification control cvar: 0 = off, 1 = print to console, 2 = print to console and chatbox (if notification_allow_chatboxprint is enabled)" +seta notification_INFO_JOIN_PLAY "1" "Notification control cvar: 0 = off, 1 = print to console, 2 = print to console and chatbox (if notification_allow_chatboxprint is enabled)" seta notification_INFO_KEEPAWAY_DROPPED "1" "Notification control cvar: 0 = off, 1 = print to console, 2 = print to console and chatbox (if notification_allow_chatboxprint is enabled)" seta notification_INFO_KEEPAWAY_PICKUP "1" "Notification control cvar: 0 = off, 1 = print to console, 2 = print to console and chatbox (if notification_allow_chatboxprint is enabled)" seta notification_INFO_KEYHUNT_CAPTURE_RED "1" "Notification control cvar: 0 = off, 1 = print to console, 2 = print to console and chatbox (if notification_allow_chatboxprint is enabled)" @@ -152,7 +193,7 @@ seta notification_INFO_POWERUP_STRENGTH "1" "Notification control cvar: 0 = off, seta notification_INFO_QUIT_DISCONNECT "2" "Notification control cvar: 0 = off, 1 = print to console, 2 = print to console and chatbox (if notification_allow_chatboxprint is enabled)" seta notification_INFO_QUIT_KICK_IDLING "2" "Notification control cvar: 0 = off, 1 = print to console, 2 = print to console and chatbox (if notification_allow_chatboxprint is enabled)" seta notification_INFO_QUIT_KICK_SPECTATING "1" "Notification control cvar: 0 = off, 1 = print to console, 2 = print to console and chatbox (if notification_allow_chatboxprint is enabled)" -seta notification_INFO_QUIT_SPECTATE "2" "Notification control cvar: 0 = off, 1 = print to console, 2 = print to console and chatbox (if notification_allow_chatboxprint is enabled)" +seta notification_INFO_QUIT_SPECTATE "1" "Notification control cvar: 0 = off, 1 = print to console, 2 = print to console and chatbox (if notification_allow_chatboxprint is enabled)" seta notification_INFO_RACE_ABANDONED "1" "Notification control cvar: 0 = off, 1 = print to console, 2 = print to console and chatbox (if notification_allow_chatboxprint is enabled)" seta notification_INFO_RACE_FAIL_RANKED "1" "Notification control cvar: 0 = off, 1 = print to console, 2 = print to console and chatbox (if notification_allow_chatboxprint is enabled)" seta notification_INFO_RACE_FAIL_UNRANKED "1" "Notification control cvar: 0 = off, 1 = print to console, 2 = print to console and chatbox (if notification_allow_chatboxprint is enabled)" @@ -205,7 +246,6 @@ seta notification_INFO_WEAPON_NEX_MURDER "1" "Notification control cvar: 0 = off seta notification_INFO_WEAPON_RIFLE_MURDER "1" "Notification control cvar: 0 = off, 1 = print to console, 2 = print to console and chatbox (if notification_allow_chatboxprint is enabled)" seta notification_INFO_WEAPON_RIFLE_MURDER_HAIL "1" "Notification control cvar: 0 = off, 1 = print to console, 2 = print to console and chatbox (if notification_allow_chatboxprint is enabled)" seta notification_INFO_WEAPON_RIFLE_MURDER_HAIL_PIERCING "1" "Notification control cvar: 0 = off, 1 = print to console, 2 = print to console and chatbox (if notification_allow_chatboxprint is enabled)" -seta notification_INFO_WEAPON_RIFLE_MURDER_HEADSHOT "1" "Notification control cvar: 0 = off, 1 = print to console, 2 = print to console and chatbox (if notification_allow_chatboxprint is enabled)" seta notification_INFO_WEAPON_RIFLE_MURDER_PIERCING "1" "Notification control cvar: 0 = off, 1 = print to console, 2 = print to console and chatbox (if notification_allow_chatboxprint is enabled)" seta notification_INFO_WEAPON_ROCKETLAUNCHER_MURDER_DIRECT "1" "Notification control cvar: 0 = off, 1 = print to console, 2 = print to console and chatbox (if notification_allow_chatboxprint is enabled)" seta notification_INFO_WEAPON_ROCKETLAUNCHER_MURDER_SPLASH "1" "Notification control cvar: 0 = off, 1 = print to console, 2 = print to console and chatbox (if notification_allow_chatboxprint is enabled)" @@ -356,7 +396,7 @@ seta notification_CENTER_TEAMCHANGE_SUICIDE "1" "Notification control cvar: 0 = seta notification_CENTER_TIMEOUT_BEGINNING "1" "Notification control cvar: 0 = off, 1 = centerprint" seta notification_CENTER_TIMEOUT_ENDING "1" "Notification control cvar: 0 = off, 1 = centerprint" -// MSG_MULTI notifications (count = 118): +// MSG_MULTI notifications (count = 120): seta notification_DEATH_MURDER_CHEAT "1" "Notification control cvar: 0 = off, 1 = trigger subcalls" seta notification_DEATH_MURDER_DROWN "1" "Notification control cvar: 0 = off, 1 = trigger subcalls" seta notification_DEATH_MURDER_FALL "1" "Notification control cvar: 0 = off, 1 = trigger subcalls" @@ -426,6 +466,9 @@ seta notification_ITEM_WEAPON_GOT "1" "Notification control cvar: 0 = off, 1 = t seta notification_ITEM_WEAPON_NOAMMO "1" "Notification control cvar: 0 = off, 1 = trigger subcalls" seta notification_ITEM_WEAPON_PRIMORSEC "1" "Notification control cvar: 0 = off, 1 = trigger subcalls" seta notification_ITEM_WEAPON_UNAVAILABLE "1" "Notification control cvar: 0 = off, 1 = trigger subcalls" +seta notification_MULTI_ARENA_BEGIN "1" "Notification control cvar: 0 = off, 1 = trigger subcalls" +seta notification_MULTI_COUNTDOWN_BEGIN "1" "Notification control cvar: 0 = off, 1 = trigger subcalls" +seta notification_MULTI_MINSTA_FINDAMMO "1" "Notification control cvar: 0 = off, 1 = trigger subcalls" seta notification_WEAPON_ACCORDEON_MURDER "1" "Notification control cvar: 0 = off, 1 = trigger subcalls" seta notification_WEAPON_ACCORDEON_SUICIDE "1" "Notification control cvar: 0 = off, 1 = trigger subcalls" seta notification_WEAPON_CRYLINK_MURDER "1" "Notification control cvar: 0 = off, 1 = trigger subcalls" @@ -460,7 +503,6 @@ seta notification_WEAPON_NEX_MURDER "1" "Notification control cvar: 0 = off, 1 = seta notification_WEAPON_RIFLE_MURDER "1" "Notification control cvar: 0 = off, 1 = trigger subcalls" seta notification_WEAPON_RIFLE_MURDER_HAIL "1" "Notification control cvar: 0 = off, 1 = trigger subcalls" seta notification_WEAPON_RIFLE_MURDER_HAIL_PIERCING "1" "Notification control cvar: 0 = off, 1 = trigger subcalls" -seta notification_WEAPON_RIFLE_MURDER_HEADSHOT "1" "Notification control cvar: 0 = off, 1 = trigger subcalls" seta notification_WEAPON_RIFLE_MURDER_PIERCING "1" "Notification control cvar: 0 = off, 1 = trigger subcalls" seta notification_WEAPON_ROCKETLAUNCHER_MURDER_DIRECT "1" "Notification control cvar: 0 = off, 1 = trigger subcalls" seta notification_WEAPON_ROCKETLAUNCHER_MURDER_SPLASH "1" "Notification control cvar: 0 = off, 1 = trigger subcalls" @@ -481,6 +523,7 @@ seta notification_allow_chatboxprint "1" "Allow notifications to be printed to c seta notification_ctf_capture_verbose "0" "Show extra information when someone captures a flag" seta notification_ctf_pickup_enemy_verbose "0" "Show extra information if an enemy picks up a flag" seta notification_ctf_pickup_team_verbose "0" "Show extra information if a team mate picks up a flag" +seta notification_debug "0" "Print extra debug information on all notification function calls (Requires -DNOTIFICATIONS_DEBUG flag to be enabled on QCSRC compilation)... 0 = disabled, 1 = dprint, 2 = print" seta notification_errors_are_fatal "1" "If a notification fails upon initialization, cause a Host_Error to stop the program" seta notification_frag_verbose "1" "Show extra information when you frag someone (or when you are fragged" seta notification_item_centerprinttime "1.5" "How long to show item information centerprint messages (like 'You got the Electro' or such)" @@ -497,4 +540,4 @@ seta notification_show_sprees_info "3" "Show spree information in MSG_INFO messa seta notification_show_sprees_info_newline "1" "Show attacker spree information for MSG_INFO messages on a separate line than the death notification itself" seta notification_show_sprees_info_specialonly "1" "Don't show attacker spree information in MSG_INFO messages if it isn't an achievement" -// Notification counts (total = 459): MSG_INFO = 208, MSG_CENTER = 133, MSG_MULTI = 118 +// Notification counts (total = 499): MSG_ANNCE = 39, MSG_INFO = 207, MSG_CENTER = 133, MSG_MULTI = 120 diff --git a/qcsrc/Makefile b/qcsrc/Makefile index 3e98265a95..4f772589ba 100644 --- a/qcsrc/Makefile +++ b/qcsrc/Makefile @@ -34,6 +34,7 @@ QCCFLAGS ?= \ -funtyped-nil \ -fno-permissive \ -fvariadic-args \ + -DNOTIFICATIONS_DEBUG \ $(QCCFLAGS_EXTRA) $(QCCFLAGS_WATERMARK) else # this. is. fteqccccccccccccccccccc! diff --git a/qcsrc/client/Main.qc b/qcsrc/client/Main.qc index 173a93194b..7a11300ea9 100644 --- a/qcsrc/client/Main.qc +++ b/qcsrc/client/Main.qc @@ -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 @@ -179,7 +180,6 @@ void CSQC_Init(void) DamageInfo_Precache(); Vehicles_Precache(); turrets_precache(); - Announcer_Precache(); Tuba_Precache(); CSQCPlayer_Precache(); @@ -211,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"))); } @@ -1207,10 +1206,6 @@ float CSQC_Parse_TempEntity() Net_ReadPingPLReport(); bHandled = true; break; - case TE_CSQC_ANNOUNCE: - Announcer_Play(ReadString()); - bHandled = true; - break; case TE_CSQC_WEAPONCOMPLAIN: Net_WeaponComplain(); bHandled = true; diff --git a/qcsrc/client/View.qc b/qcsrc/client/View.qc index 4f00c55341..d27fd4d98e 100644 --- a/qcsrc/client/View.qc +++ b/qcsrc/client/View.qc @@ -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); } } diff --git a/qcsrc/client/announcer.qc b/qcsrc/client/announcer.qc index 240d424c6a..7a4ed9223a 100644 --- a/qcsrc/client/announcer.qc +++ b/qcsrc/client/announcer.qc @@ -1,14 +1,6 @@ -float previous_announcement_time; -float previous_game_starttime; -string previous_announcement; - -// remaining maptime announcer sounds, true when sound was already played -float announcer_1min; -float announcer_5min; - void Announcer_Play(string announcement) { - if((announcement != previous_announcement) || (time >= (previous_announcement_time + autocvar_cl_announcer_antispam))) + /*if((announcement != previous_announcement) || (time >= (previous_announcement_time + autocvar_cl_announcer_antispam))) { sound(world, CH_INFO, strcat("announcer/", autocvar_cl_announcer, "/", announcement, ".wav"), VOL_BASEVOICE, ATTN_NONE); @@ -16,32 +8,49 @@ void Announcer_Play(string announcement) previous_announcement = strzone(announcement); previous_announcement_time = time; - } + }*/ } +float announcer_1min; +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 { - if (!spectatee_status) - Local_Notification(MSG_CENTER, CENTER_COUNTDOWN_BEGIN); - - Announcer_Play("begin"); - announcer_5min = announcer_1min = FALSE; // reset maptime announcers now as well + Local_Notification(MSG_CENTER, CENTER_COUNTDOWN_BEGIN); + Local_Notification(MSG_MULTI, MULTI_COUNTDOWN_BEGIN); remove(self); return; } else // countdown is still going { - if (!spectatee_status) + if(roundstarttime == starttime) + Local_Notification(MSG_CENTER, CENTER_COUNTDOWN_ROUNDSTART, countdown_rounded); + else Local_Notification(MSG_CENTER, CENTER_COUNTDOWN_GAMESTART, countdown_rounded); - if(countdown_rounded <= 3 && countdown_rounded >= 1) - Announcer_Play(ftos(countdown_rounded)); + switch(countdown_rounded) + { + case 1: Local_Notification(MSG_ANNCE, ANNCE_NUM_1); break; + case 2: Local_Notification(MSG_ANNCE, ANNCE_NUM_2); break; + case 3: Local_Notification(MSG_ANNCE, ANNCE_NUM_3); break; + } self.nextthink = (starttime - (countdown - 1)); } @@ -54,20 +63,28 @@ void Announcer_Countdown() * timelimit, fraglimit and game_starttime! Requires engine changes (remove STAT_TIMELIMIT * and STAT_FRAGLIMIT to be auto-sent) */ + float previous_game_starttime; void Announcer_Gamestart() { float startTime = getstatf(STAT_GAMESTARTTIME); - + float roundstarttime = getstatf(STAT_ROUNDSTARTTIME); + if(roundstarttime > startTime) + startTime = roundstarttime; + if(previous_game_starttime != startTime) { if((time + 5.0) < startTime) // if connecting to server while restart was active don't always play prepareforbattle - Announcer_Play("prepareforbattle"); - + 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 } } @@ -106,7 +123,7 @@ void Announcer_Time() if not(autocvar_g_warmup_limit == -1 && warmup_stage) { announcer_5min = TRUE; - Announcer_Play("5minutesremain"); + Local_Notification(MSG_ANNCE, ANNCE_REMAINING_MIN_5); } } } @@ -128,7 +145,7 @@ void Announcer_Time() if not(autocvar_g_warmup_limit == -1 && warmup_stage) { announcer_1min = TRUE; - Announcer_Play("1minuteremains"); + Local_Notification(MSG_ANNCE, ANNCE_REMAINING_MIN_1); } } } @@ -139,50 +156,3 @@ void Announcer() Announcer_Gamestart(); Announcer_Time(); } - -void Announcer_Precache () -{ - precache_sound (strcat("announcer/", autocvar_cl_announcer, "/1minuteremains.wav")); - precache_sound (strcat("announcer/", autocvar_cl_announcer, "/5minutesremain.wav")); - - precache_sound (strcat("announcer/", autocvar_cl_announcer, "/electrobitch.wav")); - precache_sound (strcat("announcer/", autocvar_cl_announcer, "/airshot.wav")); - precache_sound (strcat("announcer/", autocvar_cl_announcer, "/03kills.wav")); - precache_sound (strcat("announcer/", autocvar_cl_announcer, "/05kills.wav")); - precache_sound (strcat("announcer/", autocvar_cl_announcer, "/10kills.wav")); - precache_sound (strcat("announcer/", autocvar_cl_announcer, "/15kills.wav")); - precache_sound (strcat("announcer/", autocvar_cl_announcer, "/20kills.wav")); - precache_sound (strcat("announcer/", autocvar_cl_announcer, "/25kills.wav")); - precache_sound (strcat("announcer/", autocvar_cl_announcer, "/30kills.wav")); - precache_sound (strcat("announcer/", autocvar_cl_announcer, "/botlike.wav")); - precache_sound (strcat("announcer/", autocvar_cl_announcer, "/yoda.wav")); - precache_sound (strcat("announcer/", autocvar_cl_announcer, "/amazing.wav")); - precache_sound (strcat("announcer/", autocvar_cl_announcer, "/awesome.wav")); - precache_sound (strcat("announcer/", autocvar_cl_announcer, "/impressive.wav")); - - precache_sound (strcat("announcer/", autocvar_cl_announcer, "/prepareforbattle.wav")); - precache_sound (strcat("announcer/", autocvar_cl_announcer, "/begin.wav")); - precache_sound (strcat("announcer/", autocvar_cl_announcer, "/timeoutcalled.wav")); - precache_sound (strcat("announcer/", autocvar_cl_announcer, "/1fragleft.wav")); - precache_sound (strcat("announcer/", autocvar_cl_announcer, "/2fragsleft.wav")); - precache_sound (strcat("announcer/", autocvar_cl_announcer, "/3fragsleft.wav")); - precache_sound (strcat("announcer/", autocvar_cl_announcer, "/terminated.wav")); - - precache_sound (strcat("announcer/", autocvar_cl_announcer, "/1.wav")); - precache_sound (strcat("announcer/", autocvar_cl_announcer, "/2.wav")); - precache_sound (strcat("announcer/", autocvar_cl_announcer, "/3.wav")); - precache_sound (strcat("announcer/", autocvar_cl_announcer, "/4.wav")); - precache_sound (strcat("announcer/", autocvar_cl_announcer, "/5.wav")); - precache_sound (strcat("announcer/", autocvar_cl_announcer, "/6.wav")); - precache_sound (strcat("announcer/", autocvar_cl_announcer, "/7.wav")); - precache_sound (strcat("announcer/", autocvar_cl_announcer, "/8.wav")); - precache_sound (strcat("announcer/", autocvar_cl_announcer, "/9.wav")); - precache_sound (strcat("announcer/", autocvar_cl_announcer, "/10.wav")); - - precache_sound (strcat("announcer/", autocvar_cl_announcer, "/lastsecond.wav")); - precache_sound (strcat("announcer/", autocvar_cl_announcer, "/narrowly.wav")); - - precache_sound (strcat("announcer/", autocvar_cl_announcer, "/voteaccept.wav")); - precache_sound (strcat("announcer/", autocvar_cl_announcer, "/votecall.wav")); - precache_sound (strcat("announcer/", autocvar_cl_announcer, "/votefail.wav")); -} diff --git a/qcsrc/client/autocvars.qh b/qcsrc/client/autocvars.qh index 150efb1708..c13e2bf6e8 100644 --- a/qcsrc/client/autocvars.qh +++ b/qcsrc/client/autocvars.qh @@ -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; diff --git a/qcsrc/client/csqcmodel_hooks.qc b/qcsrc/client/csqcmodel_hooks.qc index a9f8410b41..109b83d166 100644 --- a/qcsrc/client/csqcmodel_hooks.qc +++ b/qcsrc/client/csqcmodel_hooks.qc @@ -313,6 +313,10 @@ void CSQCPlayer_FallbackFrame_PostUpdate(float isnew) } self.csqcmodel_isdead = IS_DEAD_FRAME(self.frame); } +void CSQCPlayer_AnimDecide_PostUpdate(float isnew) +{ + self.csqcmodel_isdead = !!(self.anim_state & (ANIMSTATE_DEAD1 | ANIMSTATE_DEAD2)); +} float CSQCPlayer_FallbackFrame(float f) { if(frameduration(self.modelindex, f) > 0) @@ -692,7 +696,9 @@ void CSQCModel_Hook_PostUpdate(float isnew, float isplayer, float islocalplayer) if(self.isplayermodel) { CSQCPlayer_ForceModel_PostUpdate(); - if(!isplayer) + if(isplayer) + CSQCPlayer_AnimDecide_PostUpdate(isnew); + else CSQCPlayer_FallbackFrame_PostUpdate(isnew); } CSQCModel_Effects_PostUpdate(); diff --git a/qcsrc/client/hud.qc b/qcsrc/client/hud.qc index 90dac2e096..469fe67fa2 100644 --- a/qcsrc/client/hud.qc +++ b/qcsrc/client/hud.qc @@ -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= 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 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) diff --git a/qcsrc/client/hud.qh b/qcsrc/client/hud.qh index bef2079b31..19cd36ddb0 100644 --- a/qcsrc/client/hud.qh +++ b/qcsrc/client/hud.qh @@ -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 diff --git a/qcsrc/client/hud_config.qc b/qcsrc/client/hud_config.qc index 21ab08fa70..ca8ec19c1a 100644 --- a/qcsrc/client/hud_config.qc +++ b/qcsrc/client/hud_config.qc @@ -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 diff --git a/qcsrc/common/command/generic.qc b/qcsrc/common/command/generic.qc index 6643387f88..3059916c61 100644 --- a/qcsrc/common/command/generic.qc +++ b/qcsrc/common/command/generic.qc @@ -436,13 +436,15 @@ void GenericCommand_restartnotifs(float request) print(sprintf( strcat( "Restart_Notifications(): Restarting %d notifications... ", - "Counts: MSG_INFO = %d, MSG_CENTER = %d, MSG_MULTI = %d\n" + "Counts: MSG_ANNCE = %d, MSG_INFO = %d, MSG_CENTER = %d, MSG_MULTI = %d\n" ), ( + NOTIF_ANNCE_COUNT + NOTIF_INFO_COUNT + NOTIF_CENTER_COUNT + NOTIF_MULTI_COUNT - ), + ), + NOTIF_ANNCE_COUNT, NOTIF_INFO_COUNT, NOTIF_CENTER_COUNT, NOTIF_MULTI_COUNT diff --git a/qcsrc/common/constants.qh b/qcsrc/common/constants.qh index c25fb059f6..cbda9dcdd5 100644 --- a/qcsrc/common/constants.qh +++ b/qcsrc/common/constants.qh @@ -36,14 +36,13 @@ const float TE_CSQC_NEXGUNBEAMPARTICLE = 104; const float TE_CSQC_LIGHTNINGARC = 105; const float TE_CSQC_TEAMNAGGER = 106; const float TE_CSQC_PINGPLREPORT = 107; -const float TE_CSQC_ANNOUNCE = 108; -const float TE_CSQC_TARGET_MUSIC = 109; -const float TE_CSQC_WEAPONCOMPLAIN = 110; -const float TE_CSQC_NEX_SCOPE = 111; -const float TE_CSQC_MINELAYER_MAXMINES = 112; -const float TE_CSQC_HAGAR_MAXROCKETS = 113; -const float TE_CSQC_VEHICLESETUP = 114; -const float TE_CSQC_SVNOTICE = 115; +const float TE_CSQC_TARGET_MUSIC = 108; +const float TE_CSQC_WEAPONCOMPLAIN = 109; +const float TE_CSQC_NEX_SCOPE = 110; +const float TE_CSQC_MINELAYER_MAXMINES = 111; +const float TE_CSQC_HAGAR_MAXROCKETS = 112; +const float TE_CSQC_VEHICLESETUP = 113; +const float TE_CSQC_SVNOTICE = 114; const float RACE_NET_CHECKPOINT_HIT_QUALIFYING = 0; // byte checkpoint, short time, short recordtime, string recordholder const float RACE_NET_CHECKPOINT_CLEAR = 1; @@ -178,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; @@ -392,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 diff --git a/qcsrc/common/mapinfo.qc b/qcsrc/common/mapinfo.qc index b4dbda25f2..82c5673d8b 100644 --- a/qcsrc/common/mapinfo.qc +++ b/qcsrc/common/mapinfo.qc @@ -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); } diff --git a/qcsrc/common/notifications.qc b/qcsrc/common/notifications.qc index cf495153ef..b72380ccee 100644 --- a/qcsrc/common/notifications.qc +++ b/qcsrc/common/notifications.qc @@ -7,6 +7,7 @@ string Get_Notif_TypeName(float net_type) { switch(net_type) { + case MSG_ANNCE: return "MSG_ANNCE"; case MSG_INFO: return "MSG_INFO"; case MSG_CENTER: return "MSG_CENTER"; case MSG_CENTER_CPID: return "MSG_CENTER_CPID"; @@ -20,6 +21,7 @@ entity Get_Notif_Ent(float net_type, float net_name) { switch(net_type) { + case MSG_ANNCE: return msg_annce_notifs[net_name - 1]; case MSG_INFO: return msg_info_notifs[net_name - 1]; case MSG_CENTER: return msg_center_notifs[net_name - 1]; case MSG_MULTI: return msg_multi_notifs[net_name - 1]; @@ -37,6 +39,7 @@ string Notification_CheckArgs_TypeName(float net_type, float net_name) { checkargs = sprintf("Improper name: %d!", net_name); } break; } switch(net_type) { + CHECKARG_TYPENAME(ANNCE) CHECKARG_TYPENAME(INFO) CHECKARG_TYPENAME(CENTER) CHECKARG_TYPENAME(MULTI) @@ -104,6 +107,7 @@ string Notification_CheckArgs( void Destroy_Notification_Entity(entity notif) { if(notif.nent_name != "") { strunzone(notif.nent_name); } + if(notif.nent_snd != "") { strunzone(notif.nent_snd); } if(notif.nent_args != "") { strunzone(notif.nent_args); } if(notif.nent_hudargs != "") { strunzone(notif.nent_hudargs); } if(notif.nent_icon != "") { strunzone(notif.nent_icon); } @@ -133,6 +137,7 @@ void Destroy_All_Notifications(void) #endif // kill all real notification entities + DESTROY_LOOP(MSG_ANNCE, NOTIF_ANNCE_COUNT) DESTROY_LOOP(MSG_INFO, NOTIF_INFO_COUNT) DESTROY_LOOP(MSG_CENTER, NOTIF_CENTER_COUNT) DESTROY_LOOP(MSG_MULTI, NOTIF_MULTI_COUNT) @@ -339,8 +344,13 @@ void Create_Notification_Entity( float typeid, float nameid, string namestring, + float anncename, float infoname, float centername, + float channel, + string snd, + float vol, + float position, float strnum, float flnum, string args, @@ -360,6 +370,13 @@ void Create_Notification_Entity( string typestring = ""; switch(typeid) { + case MSG_ANNCE: + { + typestring = "MSG_ANNCE"; + msg_annce_notifs[nameid - 1] = notif; + notif.classname = "msg_annce_notification"; + break; + } case MSG_INFO: { typestring = "MSG_INFO"; @@ -409,7 +426,7 @@ void Create_Notification_Entity( if(msg_is_multi) { // Set MSG_MULTI string/float counts - if((infoname == NO_MSG) && (centername == NO_MSG)) + if((anncename == NO_MSG) && (infoname == NO_MSG) && (centername == NO_MSG)) { print(sprintf( strcat( @@ -423,6 +440,9 @@ void Create_Notification_Entity( } else { + // announcements don't actually need any arguments, so lets not even count them. + if(anncename != NO_MSG) { notif.nent_msgannce = msg_annce_notifs[anncename - 1]; } + float infoname_stringcount = 0, infoname_floatcount = 0; float centername_stringcount = 0, centername_floatcount = 0; @@ -445,6 +465,41 @@ void Create_Notification_Entity( notif.nent_floatcount = max(infoname_floatcount, centername_floatcount); } } + else if(typeid == MSG_ANNCE) + { + // Set MSG_ANNCE information and handle precaching + #ifdef CSQC + if not(GENTLE && (var_cvar == 1)) + { + if(snd != "") + { + if(notif.nent_enabled) + { + precache_sound(sprintf("announcer/%s/%s.wav", autocvar_cl_announcer, snd)); + notif.nent_channel = channel; + notif.nent_snd = strzone(snd); + notif.nent_vol = vol; + notif.nent_position = position; + } + } + else + { + print(sprintf( + strcat( + "^1NOTIFICATION WITH NO SOUND: ", + "^7net_type = %s, net_name = %s.\n" + ), + typestring, + namestring + )); + notif_error = TRUE; + } + } + else { notif.nent_enabled = FALSE; } + #else + notif.nent_enabled = FALSE; + #endif + } else { // Set MSG_INFO and MSG_CENTER string/float counts @@ -653,6 +708,14 @@ void Dump_Notifications(float fh, float alsoprint) // This is not necessary, and does not matter if they vary between config versions, // it is just a semi-helpful tool for those who want to manually change their user settings. + NOTIF_WRITE(sprintf("\n// MSG_ANNCE notifications (count = %d):\n", NOTIF_ANNCE_COUNT)); + for(i = 1; i <= NOTIF_ANNCE_COUNT; ++i) + { + e = Get_Notif_Ent(MSG_ANNCE, i); + if not(e) { backtrace("Dump_Notifications(): Missing notification entity!\n"); return; } + NOTIF_WRITE_ENTITY(e.nent_name, e.nent_default, "Notification control cvar: 0 = disabled, 1 = enabled if gentle mode is off, 2 = always enabled)"); + } + NOTIF_WRITE(sprintf("\n// MSG_INFO notifications (count = %d):\n", NOTIF_INFO_COUNT)); for(i = 1; i <= NOTIF_INFO_COUNT; ++i) { @@ -683,6 +746,7 @@ void Dump_Notifications(float fh, float alsoprint) NOTIF_WRITE_HARDCODED("ctf_capture_verbose", "0", "Show extra information when someone captures a flag"); NOTIF_WRITE_HARDCODED("ctf_pickup_enemy_verbose", "0", "Show extra information if an enemy picks up a flag"); NOTIF_WRITE_HARDCODED("ctf_pickup_team_verbose", "0", "Show extra information if a team mate picks up a flag"); + NOTIF_WRITE_HARDCODED("debug", "0", "Print extra debug information on all notification function calls (Requires -DNOTIFICATIONS_DEBUG flag to be enabled on QCSRC compilation)... 0 = disabled, 1 = dprint, 2 = print"); NOTIF_WRITE_HARDCODED("errors_are_fatal", "1", "If a notification fails upon initialization, cause a Host_Error to stop the program"); NOTIF_WRITE_HARDCODED("frag_verbose", "1", "Show extra information when you frag someone (or when you are fragged"); NOTIF_WRITE_HARDCODED("item_centerprinttime", "1.5", "How long to show item information centerprint messages (like 'You got the Electro' or such)"); @@ -702,13 +766,15 @@ void Dump_Notifications(float fh, float alsoprint) NOTIF_WRITE(sprintf( strcat( "\n// Notification counts (total = %d): ", - "MSG_INFO = %d, MSG_CENTER = %d, MSG_MULTI = %d\n" + "MSG_ANNCE = %d, MSG_INFO = %d, MSG_CENTER = %d, MSG_MULTI = %d\n" ), ( + NOTIF_ANNCE_COUNT + NOTIF_INFO_COUNT + NOTIF_CENTER_COUNT + NOTIF_MULTI_COUNT - ), + ), + NOTIF_ANNCE_COUNT, NOTIF_INFO_COUNT, NOTIF_CENTER_COUNT, NOTIF_MULTI_COUNT @@ -732,13 +798,24 @@ void Notification_GetCvars() // Frontend Notification Pushing // =============================== +#ifdef NOTIFICATIONS_DEBUG +void Debug_Notification(string input) +{ + switch(autocvar_notification_debug) + { + case 1: { dprint(input); break; } + case 2: { print(input); break; } + } +} +#endif + string Local_Notification_sprintf( string input, string args, string s1, string s2, string s3, string s4, float f1, float f2, float f3, float f4) { #ifdef NOTIFICATIONS_DEBUG - dprint(sprintf( + Debug_Notification(sprintf( "Local_Notification_sprintf('%s^7', '%s', %s, %s);\n", MakeConsoleSafe(input), args, @@ -778,6 +855,62 @@ string Local_Notification_sprintf( } #ifdef CSQC +void Local_Notification_sound( + float soundchannel, string soundfile, + float soundvolume, float soundposition) +{ + if((soundfile != prev_soundfile) || (time >= (prev_soundtime + autocvar_cl_announcer_antispam))) + { + #ifdef NOTIFICATIONS_DEBUG + Debug_Notification(sprintf( + "Local_Notification_sound(world, %f, '%s', %f, %f);\n", + soundchannel, + sprintf( + "announcer/%s/%s.wav", + autocvar_cl_announcer, + soundfile + ), + soundvolume, + soundposition + )); + #endif + + sound( + world, + soundchannel, + sprintf( + "announcer/%s/%s.wav", + autocvar_cl_announcer, + soundfile + ), + soundvolume, + soundposition + ); + + if(prev_soundfile) { strunzone(prev_soundfile); } + prev_soundfile = strzone(soundfile); + prev_soundtime = time; + } + else + { + #ifdef NOTIFICATIONS_DEBUG + Debug_Notification(sprintf( + "Local_Notification_sound(world, %f, '%s', %f, %f) ^1BLOCKED BY ANTISPAM:^7 prevsnd: '%s', time/prevtime: %f, limit: %f\n", + soundchannel, + sprintf( + "announcer/%s/%s.wav", + autocvar_cl_announcer, + soundfile + ), + soundvolume, + soundposition, + prev_soundfile, + (time - prev_soundtime), + autocvar_cl_announcer_antispam + )); + #endif + } +} void Local_Notification_HUD_Notify_Push( string icon, string hudargs, string s1, string s2, string s3, string s4) @@ -802,7 +935,7 @@ void Local_Notification_HUD_Notify_Push( } } #ifdef NOTIFICATIONS_DEBUG - dprint(sprintf( + Debug_Notification(sprintf( "Local_Notification_HUD_Notify_Push('%s^7', '%s', %s, %s);\n", icon, hudargs, @@ -842,7 +975,7 @@ void Local_Notification_centerprint_generic( } } #ifdef NOTIFICATIONS_DEBUG - dprint(sprintf( + Debug_Notification(sprintf( "Local_Notification_centerprint_generic('%s^7', '%s', %d, %d, %d, %d);\n", MakeConsoleSafe(input), durcnt, @@ -865,7 +998,7 @@ void Local_Notification(float net_type, float net_name, ...count) if not(notif.nent_enabled) { #ifdef NOTIFICATIONS_DEBUG - dprint(sprintf( + Debug_Notification(sprintf( "Local_Notification(%s, %s): Entity was disabled...\n", Get_Notif_TypeName(net_type), notif.nent_name @@ -911,7 +1044,7 @@ void Local_Notification(float net_type, float net_name, ...count) float f4 = ((3 < notif.nent_floatcount) ? ...((notif.nent_stringcount + 3), float) : 0); #ifdef NOTIFICATIONS_DEBUG - dprint(sprintf( + Debug_Notification(sprintf( "Local_Notification(%s, %s, %s, %s);\n", Get_Notif_TypeName(net_type), notif.nent_name, @@ -922,6 +1055,21 @@ void Local_Notification(float net_type, float net_name, ...count) switch(net_type) { + case MSG_ANNCE: + { + #ifdef CSQC + Local_Notification_sound( + notif.nent_channel, + notif.nent_snd, + notif.nent_vol, + notif.nent_position + ); + #else + backtrace("MSG_ANNCE on server?... Please notify Samual immediately!\n"); + #endif + break; + } + case MSG_INFO: { print( @@ -973,6 +1121,16 @@ void Local_Notification(float net_type, float net_name, ...count) f1, f2, f3, f4); } #ifdef CSQC + if(notif.nent_msgannce) + if(notif.nent_msgannce.nent_enabled) + { + Local_Notification_WOVA( + MSG_ANNCE, + notif.nent_msgannce.nent_id, + 0, 0, + "", "", "", "", + 0, 0, 0, 0); + } if(notif.nent_msgcenter) if(notif.nent_msgcenter.nent_enabled) { @@ -1021,7 +1179,7 @@ void Read_Notification(float is_new) if(net_type == MSG_CENTER_CPID) { #ifdef NOTIFICATIONS_DEBUG - dprint(sprintf( + Debug_Notification(sprintf( "Read_Notification(%d) at %f: net_type = %s, net_name = %d\n", is_new, time, @@ -1054,7 +1212,7 @@ void Read_Notification(float is_new) if not(notif) { backtrace("Read_Notification: Could not find notification entity!\n"); return; } #ifdef NOTIFICATIONS_DEBUG - dprint(sprintf( + Debug_Notification(sprintf( "Read_Notification(%d) at %f: net_type = %s, net_name = %s\n", is_new, time, @@ -1091,7 +1249,7 @@ void Net_Notification_Remove() if not(self) { backtrace(sprintf("Net_Notification_Remove() at %f: Missing self!?\n", time)); return; } #ifdef NOTIFICATIONS_DEBUG - dprint(sprintf( + Debug_Notification(sprintf( "Net_Notification_Remove() at %f: %s '%s - %s' notification\n", time, ((self.nent_net_name == -1) ? "Killed" : "Removed"), @@ -1204,11 +1362,11 @@ void Kill_Notification( if(checkargs != "") { backtrace(sprintf("Incorrect usage of Kill_Notification: %s\n", checkargs)); return; } #ifdef NOTIFICATIONS_DEBUG - dprint(sprintf( + Debug_Notification(sprintf( "Kill_Notification(%d, '%s', %s, %d);\n", broadcast, client.netname, - Get_Notif_TypeName(net_type), + (net_type ? Get_Notif_TypeName(net_type) : "0"), net_name )); #endif @@ -1265,7 +1423,7 @@ void Kill_Notification( { if(net_type) { - if(killed_cpid != NO_CPID) + if((killed_cpid != NO_CPID) && (notif.nent_net_type == MSG_CENTER)) { if(notif.owner.nent_cpid == killed_cpid) { @@ -1338,7 +1496,7 @@ void Send_Notification( float f2 = ((1 < notif.nent_floatcount) ? ...((notif.nent_stringcount + 1), float) : 0); float f3 = ((2 < notif.nent_floatcount) ? ...((notif.nent_stringcount + 2), float) : 0); float f4 = ((3 < notif.nent_floatcount) ? ...((notif.nent_stringcount + 3), float) : 0); - dprint(sprintf( + Debug_Notification(sprintf( "Send_Notification(%d, %s, %s, %s, %s);\n", broadcast, Get_Notif_TypeName(net_type), @@ -1373,7 +1531,7 @@ void Send_Notification( Net_LinkEntity(net_notif, FALSE, 0, Net_Write_Notification); - if(server_is_dedicated && (broadcast == NOTIF_ALL || broadcast == NOTIF_ALL_EXCEPT) && (net_type != MSG_CENTER)) + if(server_is_dedicated && (broadcast == NOTIF_ALL || broadcast == NOTIF_ALL_EXCEPT) && (net_type != MSG_ANNCE) && (net_type != MSG_CENTER)) { Local_Notification_WOVA( net_type, net_name, @@ -1394,7 +1552,7 @@ void Send_Notification_WOVA( entity notif = Get_Notif_Ent(net_type, net_name); #ifdef NOTIFICATIONS_DEBUG - dprint(sprintf( + Debug_Notification(sprintf( "Send_Notification_WOVA(%d, %s, %s, %s, %s);\n", broadcast, Get_Notif_TypeName(net_type), diff --git a/qcsrc/common/notifications.qh b/qcsrc/common/notifications.qh index 04a1cec222..d1aeba001e 100644 --- a/qcsrc/common/notifications.qh +++ b/qcsrc/common/notifications.qh @@ -4,10 +4,11 @@ // ================================================ // main types/groups of notifications -#define MSG_INFO 1 // "Global" information messages (sent to console, and notify panel if it has an icon) -#define MSG_CENTER 2 // "Personal" centerprint messages -#define MSG_CENTER_CPID 3 // Kill centerprint message -#define MSG_MULTI 4 // Subcall MSG_INFO and/or MSG_CENTER notifications +#define MSG_ANNCE 1 // "Global" AND "personal" announcer messages +#define MSG_INFO 2 // "Global" information messages +#define MSG_CENTER 3 // "Personal" centerprint messages +#define MSG_CENTER_CPID 4 // Kill centerprint message +#define MSG_MULTI 5 // Subcall MSG_INFO and/or MSG_CENTER notifications #define NO_MSG -12345 @@ -44,8 +45,13 @@ void Create_Notification_Entity( float typeid, float nameid, string namestring, + float anncename, float infoname, float centername, + float channel, + string snd, + float vol, + float position, float strnum, float flnum, string args, @@ -60,6 +66,10 @@ void Create_Notification_Entity( void Dump_Notifications(float fh, float alsoprint); +#ifdef NOTIFICATIONS_DEBUG +void Debug_Notification(string input); +#endif + void Local_Notification(float net_type, float net_name, ...count); void Local_Notification_WOVA( float net_type, float net_name, @@ -69,6 +79,8 @@ void Local_Notification_WOVA( #ifdef CSQC // CLIENT ONLY void Read_Notification(float is_new); +string prev_soundfile; +float prev_soundtime; #endif #ifdef SVQC // SERVER ONLY @@ -102,8 +114,19 @@ void Send_Notification_WOVA( // ==================================== /* List of all notifications (including identifiers and display information) - Possible Tokens: default, name, infoname, centername, strnum, flnum, args, hudargs, icon, cpid, durcnt, normal, gentle + Possible Tokens: + default, name, channel, sound, volume, position, + anncename, infoname, centername, strnum, flnum, args, + hudargs, icon, cpid, durcnt, normal, gentle Format Specifications: + MSG_ANNCE: + default: FLOAT: Default setting for whether the notification is enabled or not + ^-> 0 = disabled, 1 = enabled if gentle is disabled, 2 = always enabled + name: VAR: Name of notification + channel: FLOAT: Sound channel to broadcast on to + sound: STRING: Filename for the announcement sound + volume: FLOAT: Volume setting for the announcement sound + position: FLOAT: Attenuation/positioning value MSG_INFO: default: FLOAT: Default setting for whether the notification is enabled or not ^-> 0 = disabled, 1 = enabled, 2 = also print to chat box @@ -130,6 +153,7 @@ void Send_Notification_WOVA( default: FLOAT: Default setting for whether the notification is enabled or not ^-> 0 = disabled, 1 = enabled name: VAR: Name of chaining notification + anncename: VAR: Name of announcer notification for reference infoname: VAR: Name of info notification for reference centername: VAR: Name of centerprint notification for reference @@ -170,14 +194,55 @@ void Send_Notification_WOVA( If you send a notification with mismatching arguments, Send_Notification() will error. */ +#define MSG_ANNCE_NOTIFICATIONS \ + MSG_ANNCE_NOTIF(1, ANNCE_ACHIEVEMENT_AIRSHOT, CH_INFO, "airshot", VOL_BASEVOICE, ATTN_NONE) \ + MSG_ANNCE_NOTIF(1, ANNCE_ACHIEVEMENT_AMAZING, CH_INFO, "amazing", VOL_BASEVOICE, ATTN_NONE) \ + MSG_ANNCE_NOTIF(1, ANNCE_ACHIEVEMENT_AWESOME, CH_INFO, "awesome", VOL_BASEVOICE, ATTN_NONE) \ + MSG_ANNCE_NOTIF(1, ANNCE_ACHIEVEMENT_BOTLIKE, CH_INFO, "botlike", VOL_BASEVOICE, ATTN_NONE) \ + MSG_ANNCE_NOTIF(2, ANNCE_ACHIEVEMENT_ELECTROBITCH, CH_INFO, "electrobitch", VOL_BASEVOICE, ATTN_NONE) \ + MSG_ANNCE_NOTIF(1, ANNCE_ACHIEVEMENT_IMPRESSIVE, CH_INFO, "impressive", VOL_BASEVOICE, ATTN_NONE) \ + MSG_ANNCE_NOTIF(1, ANNCE_ACHIEVEMENT_YODA, CH_INFO, "yoda", VOL_BASEVOICE, ATTN_NONE) \ + MSG_ANNCE_NOTIF(2, ANNCE_BEGIN, CH_INFO, "begin", VOL_BASEVOICE, ATTN_NONE) \ + MSG_ANNCE_NOTIF(1, ANNCE_KILLSTREAK_03, CH_INFO, "03kills", VOL_BASEVOICE, ATTN_NONE) \ + MSG_ANNCE_NOTIF(1, ANNCE_KILLSTREAK_05, CH_INFO, "05kills", VOL_BASEVOICE, ATTN_NONE) \ + MSG_ANNCE_NOTIF(1, ANNCE_KILLSTREAK_10, CH_INFO, "10kills", VOL_BASEVOICE, ATTN_NONE) \ + MSG_ANNCE_NOTIF(1, ANNCE_KILLSTREAK_15, CH_INFO, "15kills", VOL_BASEVOICE, ATTN_NONE) \ + MSG_ANNCE_NOTIF(1, ANNCE_KILLSTREAK_20, CH_INFO, "20kills", VOL_BASEVOICE, ATTN_NONE) \ + MSG_ANNCE_NOTIF(1, ANNCE_KILLSTREAK_25, CH_INFO, "25kills", VOL_BASEVOICE, ATTN_NONE) \ + MSG_ANNCE_NOTIF(1, ANNCE_KILLSTREAK_30, CH_INFO, "30kills", VOL_BASEVOICE, ATTN_NONE) \ + MSG_ANNCE_NOTIF(1, ANNCE_MINSTAGIB_LASTSECOND, CH_INFO, "lastsecond", VOL_BASEVOICE, ATTN_NONE) \ + MSG_ANNCE_NOTIF(1, ANNCE_MINSTAGIB_NARROWLY, CH_INFO, "narrowly", VOL_BASEVOICE, ATTN_NONE) \ + MSG_ANNCE_NOTIF(1, ANNCE_MINSTAGIB_TERMINATED, CH_INFO, "terminated", VOL_BASEVOICE, ATTN_NONE) \ + MSG_ANNCE_NOTIF(0, ANNCE_MULTIFRAG, CH_INFO, "multifrag", VOL_BASEVOICE, ATTN_NONE) \ + MSG_ANNCE_NOTIF(2, ANNCE_NUM_1, CH_INFO, "1", VOL_BASEVOICE, ATTN_NONE) \ + MSG_ANNCE_NOTIF(2, ANNCE_NUM_2, CH_INFO, "2", VOL_BASEVOICE, ATTN_NONE) \ + MSG_ANNCE_NOTIF(2, ANNCE_NUM_3, CH_INFO, "3", VOL_BASEVOICE, ATTN_NONE) \ + MSG_ANNCE_NOTIF(2, ANNCE_NUM_4, CH_INFO, "4", VOL_BASEVOICE, ATTN_NONE) \ + MSG_ANNCE_NOTIF(2, ANNCE_NUM_5, CH_INFO, "5", VOL_BASEVOICE, ATTN_NONE) \ + MSG_ANNCE_NOTIF(2, ANNCE_NUM_6, CH_INFO, "6", VOL_BASEVOICE, ATTN_NONE) \ + MSG_ANNCE_NOTIF(2, ANNCE_NUM_7, CH_INFO, "7", VOL_BASEVOICE, ATTN_NONE) \ + MSG_ANNCE_NOTIF(2, ANNCE_NUM_8, CH_INFO, "8", VOL_BASEVOICE, ATTN_NONE) \ + MSG_ANNCE_NOTIF(2, ANNCE_NUM_9, CH_INFO, "9", VOL_BASEVOICE, ATTN_NONE) \ + MSG_ANNCE_NOTIF(2, ANNCE_NUM_10, CH_INFO, "10", VOL_BASEVOICE, ATTN_NONE) \ + MSG_ANNCE_NOTIF(2, ANNCE_PREPARE, CH_INFO, "prepareforbattle", VOL_BASEVOICE, ATTN_NONE) \ + MSG_ANNCE_NOTIF(1, ANNCE_REMAINING_FRAG_1, CH_INFO, "1fragleft", VOL_BASEVOICE, ATTN_NONE) \ + MSG_ANNCE_NOTIF(1, ANNCE_REMAINING_FRAG_2, CH_INFO, "2fragsleft", VOL_BASEVOICE, ATTN_NONE) \ + MSG_ANNCE_NOTIF(1, ANNCE_REMAINING_FRAG_3, CH_INFO, "3fragsleft", VOL_BASEVOICE, ATTN_NONE) \ + MSG_ANNCE_NOTIF(2, ANNCE_REMAINING_MIN_1, CH_INFO, "1minuteremains", VOL_BASEVOICE, ATTN_NONE) \ + MSG_ANNCE_NOTIF(2, ANNCE_REMAINING_MIN_5, CH_INFO, "5minutesremain", VOL_BASEVOICE, ATTN_NONE) \ + MSG_ANNCE_NOTIF(2, ANNCE_TIMEOUT, CH_INFO, "timeoutcalled", VOL_BASEVOICE, ATTN_NONE) \ + MSG_ANNCE_NOTIF(2, ANNCE_VOTE_ACCEPT, CH_INFO, "voteaccept", VOL_BASEVOICE, ATTN_NONE) \ + MSG_ANNCE_NOTIF(2, ANNCE_VOTE_CALL, CH_INFO, "votecall", VOL_BASEVOICE, ATTN_NONE) \ + MSG_ANNCE_NOTIF(2, ANNCE_VOTE_FAIL, CH_INFO, "votefail", VOL_BASEVOICE, ATTN_NONE) + #define MULTITEAM_INFO(default,prefix,teams,strnum,flnum,args,hudargs,icon,normal,gentle) \ - MSG_INFO_NOTIF(default, prefix##RED, strnum, flnum, args, hudargs, sprintf(icon, strtolower(NAME_TEAM_1)), TCR(normal, COL_TEAM_1, strtoupper(NAME_TEAM_1)), TCR(gentle, COL_TEAM_1, strtoupper(NAME_TEAM_1))) \ - MSG_INFO_NOTIF(default, prefix##BLUE, strnum, flnum, args, hudargs, sprintf(icon, strtolower(NAME_TEAM_2)), TCR(normal, COL_TEAM_2, strtoupper(NAME_TEAM_2)), TCR(gentle, COL_TEAM_2, strtoupper(NAME_TEAM_2))) \ + MSG_INFO_NOTIF(default, prefix##RED, strnum, flnum, args, hudargs, sprintf(icon, strtolower(STATIC_NAME_TEAM_1)), TCR(normal, COL_TEAM_1, strtoupper(STATIC_NAME_TEAM_1)), TCR(gentle, COL_TEAM_1, strtoupper(STATIC_NAME_TEAM_1))) \ + MSG_INFO_NOTIF(default, prefix##BLUE, strnum, flnum, args, hudargs, sprintf(icon, strtolower(STATIC_NAME_TEAM_2)), TCR(normal, COL_TEAM_2, strtoupper(STATIC_NAME_TEAM_2)), TCR(gentle, COL_TEAM_2, strtoupper(STATIC_NAME_TEAM_2))) \ #if teams >= 3 \ - MSG_INFO_NOTIF(default, prefix##YELLOW, strnum, flnum, args, hudargs, sprintf(icon, strtolower(NAME_TEAM_3)), TCR(normal, COL_TEAM_3, strtoupper(NAME_TEAM_3)), TCR(gentle, COL_TEAM_3, strtoupper(NAME_TEAM_3))) \ + MSG_INFO_NOTIF(default, prefix##YELLOW, strnum, flnum, args, hudargs, sprintf(icon, strtolower(STATIC_NAME_TEAM_3)), TCR(normal, COL_TEAM_3, strtoupper(STATIC_NAME_TEAM_3)), TCR(gentle, COL_TEAM_3, strtoupper(STATIC_NAME_TEAM_3))) \ #endif \ #if teams >= 4 \ - MSG_INFO_NOTIF(default, prefix##PINK, strnum, flnum, args, hudargs, sprintf(icon, strtolower(NAME_TEAM_4)), TCR(normal, COL_TEAM_4, strtoupper(NAME_TEAM_4)), TCR(gentle, COL_TEAM_4, strtoupper(NAME_TEAM_4))) \ + MSG_INFO_NOTIF(default, prefix##PINK, strnum, flnum, args, hudargs, sprintf(icon, strtolower(STATIC_NAME_TEAM_4)), TCR(normal, COL_TEAM_4, strtoupper(STATIC_NAME_TEAM_4)), TCR(gentle, COL_TEAM_4, strtoupper(STATIC_NAME_TEAM_4))) \ #endif #define MSG_INFO_NOTIFICATIONS \ MULTITEAM_INFO(1, INFO_CTF_CAPTURE_, 2, 1, 0, "s1", "s1", "notify_%s_captured", _("^BG%s^BG captured the ^TC^TT^BG flag\n"), "") \ @@ -256,10 +321,14 @@ void Send_Notification_WOVA( MSG_INFO_NOTIF(1, INFO_DEATH_SELF_VH_WAKI_DEATH, 2, 1, "s1 s2loc spree_lost", "s1", "notify_death", _("^BG%s^K1 got caught in the blast of a Racer explosion%s%s\n"), "") \ MSG_INFO_NOTIF(1, INFO_DEATH_SELF_VH_WAKI_ROCKET, 2, 1, "s1 s2loc spree_lost", "s1", "notify_death", _("^BG%s^K1 couldn't find shelter from a Racer rocket%s%s\n"), "") \ 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\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"), "") \ @@ -288,8 +357,8 @@ void Send_Notification_WOVA( MSG_INFO_NOTIF(1, INFO_QUIT_KICK_SPECTATING, 0, 0, "", "", "", _("^F2You were kicked from the server because you are a spectator and spectators aren't allowed at the moment.\n"), "") \ MSG_INFO_NOTIF(1, INFO_QUIT_SPECTATE, 1, 0, "s1", "", "", _("^BG%s^F3 is now spectating\n"), "") \ MSG_INFO_NOTIF(1, INFO_RACE_ABANDONED, 1, 0, "s1", "", "", _("^BG%s^BG has abandoned the race\n"), "") \ - MSG_INFO_NOTIF(1, INFO_RACE_FAIL_RANKED, 1, 3, "s1 race_col f1ord race_col f2race_time race_diff", "s1", "race_newfail", _("^BG%s^BG couldn't break their %s%s^BG place record of %s%s %s\n"), "") \ - MSG_INFO_NOTIF(1, INFO_RACE_FAIL_UNRANKED, 1, 3, "s1 race_col f1ord race_col f2race_time race_diff", "s1", "race_newfail", _("^BG%s^BG couldn't break the %s%s^BG place record of %s%s %s\n"), "") \ + MSG_INFO_NOTIF(1, INFO_RACE_FAIL_RANKED, 1, 3, "s1 race_col f1ord race_col f3race_time race_diff", "s1", "race_newfail", _("^BG%s^BG couldn't break their %s%s^BG place record of %s%s %s\n"), "") \ + MSG_INFO_NOTIF(1, INFO_RACE_FAIL_UNRANKED, 1, 3, "s1 race_col f1ord race_col f3race_time race_diff", "s1", "race_newfail", _("^BG%s^BG couldn't break the %s%s^BG place record of %s%s %s\n"), "") \ MSG_INFO_NOTIF(1, INFO_RACE_FINISHED, 1, 0, "s1", "", "", _("^BG%s^BG has finished the race\n"), "") \ MSG_INFO_NOTIF(1, INFO_RACE_NEW_BROKEN, 2, 3, "s1 s2 race_col f1ord race_col f2race_time race_diff", "s1 s2", "race_newrankyellow", _("^BG%s^BG broke %s^BG's %s%s^BG place record with %s%s %s\n"), "") \ MSG_INFO_NOTIF(1, INFO_RACE_NEW_IMPROVED, 1, 3, "s1 race_col f1ord race_col f2race_time race_diff", "s1", "race_newtime", _("^BG%s^BG improved their %s%s^BG place record with %s%s %s\n"), "") \ @@ -336,7 +405,6 @@ void Send_Notification_WOVA( MSG_INFO_NOTIF(1, INFO_WEAPON_RIFLE_MURDER, 3, 2, "spree_inf s1 s2 s3loc spree_end", "s2 s1", "weaponrifle", _("^BG%s%s^K1 was sniped with a Rifle by ^BG%s^K1%s%s\n"), "") \ MSG_INFO_NOTIF(1, INFO_WEAPON_RIFLE_MURDER_HAIL, 3, 2, "spree_inf s1 s2 s3loc spree_end", "s2 s1", "weaponrifle", _("^BG%s%s^K1 died in ^BG%s^K1's Rifle bullet hail%s%s\n"), "") \ MSG_INFO_NOTIF(1, INFO_WEAPON_RIFLE_MURDER_HAIL_PIERCING, 3, 2, "spree_inf s1 s2 s3loc spree_end", "s2 s1", "weaponrifle", _("^BG%s%s^K1 failed to hide from ^BG%s^K1's Rifle bullet hail%s%s\n"), "") \ - MSG_INFO_NOTIF(1, INFO_WEAPON_RIFLE_MURDER_HEADSHOT, 3, 2, "spree_inf s1 s2 s3loc spree_end", "s2 s1", "notify_headshot", _("^BG%s%s^K1 was shot in the head with a Rifle by ^BG%s^K1%s%s\n"), "") \ MSG_INFO_NOTIF(1, INFO_WEAPON_RIFLE_MURDER_PIERCING, 3, 2, "spree_inf s1 s2 s3loc spree_end", "s2 s1", "weaponrifle", _("^BG%s%s^K1 failed to hide from ^BG%s^K1's Rifle%s%s\n"), "") \ MSG_INFO_NOTIF(1, INFO_WEAPON_ROCKETLAUNCHER_MURDER_DIRECT, 3, 2, "spree_inf s1 s2 s3loc spree_end", "s2 s1", "weaponrocketlauncher", _("^BG%s%s^K1 ate ^BG%s^K1's rocket%s%s\n"), "") \ MSG_INFO_NOTIF(1, INFO_WEAPON_ROCKETLAUNCHER_MURDER_SPLASH, 3, 2, "spree_inf s1 s2 s3loc spree_end", "s2 s1", "weaponrocketlauncher", _("^BG%s%s^K1 got too close ^BG%s^K1's rocket%s%s\n"), "") \ @@ -353,22 +421,23 @@ void Send_Notification_WOVA( MSG_INFO_NOTIF(1, INFO_WEAPON_UZI_MURDER_SPRAY, 3, 2, "spree_inf s1 s2 s3loc spree_end", "s2 s1", "weaponuzi", _("^BG%s%s^K1 was riddled full of holes by ^BG%s^K1's Machine Gun%s%s\n"), "") #define MULTITEAM_CENTER(default,prefix,teams,strnum,flnum,args,cpid,durcnt,normal,gentle) \ - MSG_CENTER_NOTIF(default, prefix##RED, strnum, flnum, args, cpid, durcnt, TCR(normal, COL_TEAM_1, strtoupper(NAME_TEAM_1)), TCR(gentle, COL_TEAM_1, strtoupper(NAME_TEAM_1))) \ - MSG_CENTER_NOTIF(default, prefix##BLUE, strnum, flnum, args, cpid, durcnt, TCR(normal, COL_TEAM_2, strtoupper(NAME_TEAM_2)), TCR(gentle, COL_TEAM_2, strtoupper(NAME_TEAM_2))) \ + MSG_CENTER_NOTIF(default, prefix##RED, strnum, flnum, args, cpid, durcnt, TCR(normal, COL_TEAM_1, strtoupper(STATIC_NAME_TEAM_1)), TCR(gentle, COL_TEAM_1, strtoupper(STATIC_NAME_TEAM_1))) \ + MSG_CENTER_NOTIF(default, prefix##BLUE, strnum, flnum, args, cpid, durcnt, TCR(normal, COL_TEAM_2, strtoupper(STATIC_NAME_TEAM_2)), TCR(gentle, COL_TEAM_2, strtoupper(STATIC_NAME_TEAM_2))) \ #if teams >= 3 \ - MSG_CENTER_NOTIF(default, prefix##YELLOW, strnum, flnum, args, cpid, durcnt, TCR(normal, COL_TEAM_3, strtoupper(NAME_TEAM_3)), TCR(gentle, COL_TEAM_3, strtoupper(NAME_TEAM_3))) \ + MSG_CENTER_NOTIF(default, prefix##YELLOW, strnum, flnum, args, cpid, durcnt, TCR(normal, COL_TEAM_3, strtoupper(STATIC_NAME_TEAM_3)), TCR(gentle, COL_TEAM_3, strtoupper(STATIC_NAME_TEAM_3))) \ #endif \ #if teams >= 4 \ - MSG_CENTER_NOTIF(default, prefix##PINK, strnum, flnum, args, cpid, durcnt, TCR(normal, COL_TEAM_4, strtoupper(NAME_TEAM_4)), TCR(gentle, COL_TEAM_4, strtoupper(NAME_TEAM_4))) \ + 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!"), "") \ @@ -427,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"), "") \ @@ -448,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!")) \ @@ -458,7 +533,7 @@ void Send_Notification_WOVA( MSG_CENTER_NOTIF(1, CENTER_NIX_COUNTDOWN, 0, 2, "item_wepname", CPID_NIX, "1 f2", _("^F2^COUNT^BG until weapon change...\nNext weapon: ^F1%s"), "") \ MSG_CENTER_NOTIF(1, CENTER_NIX_NEWWEAPON, 0, 1, "item_wepname", CPID_NIX, "0 0", _("^F2Active weapon: ^F1%s"), "") \ MSG_CENTER_NOTIF(1, CENTER_OVERTIME_FRAG, 0, 0, "", CPID_OVERTIME, "0 0", _("^F2Now playing ^F4OVERTIME^F2!\nKeep fragging until we have a winner!"), _("^F2Now playing ^F4OVERTIME^F2!\nKeep scoring until we have a winner!")) \ - MSG_CENTER_NOTIF(1, CENTER_OVERTIME_TIME, 0, 1, "f1time", CPID_OVERTIME, "0 0", _("^F2Now playing ^F4OVERTIME^F2!\nAdded ^F4%s^F2 to the game!"), "") \ + MSG_CENTER_NOTIF(1, CENTER_OVERTIME_TIME, 0, 1, "f1time", CPID_OVERTIME, "0 0", _("^F2Now playing ^F4OVERTIME^F2!\n^BGAdded ^F4%s^BG to the game!"), "") \ MSG_CENTER_NOTIF(1, CENTER_POWERDOWN_INVISIBILITY, 0, 0, "", CPID_POWERUP, "0 0", _("^F2Invisibility has worn off"), "") \ MSG_CENTER_NOTIF(1, CENTER_POWERDOWN_SHIELD, 0, 0, "", CPID_POWERUP, "0 0", _("^F2Shield has worn off"), "") \ MSG_CENTER_NOTIF(1, CENTER_POWERDOWN_SPEED, 0, 0, "", CPID_POWERUP, "0 0", _("^F2Speed has worn off"), "") \ @@ -479,124 +554,125 @@ void Send_Notification_WOVA( MSG_CENTER_NOTIF(1, CENTER_TIMEOUT_ENDING, 0, 1, "", CPID_TIMEOUT, "1 f1", _("^F4Timeout ends in ^COUNT"), "") #define MSG_MULTI_NOTIFICATIONS \ - MSG_MULTI_NOTIF(1, DEATH_MURDER_CHEAT, INFO_DEATH_MURDER_CHEAT, NO_MSG) \ - MSG_MULTI_NOTIF(1, DEATH_MURDER_DROWN, INFO_DEATH_MURDER_DROWN, NO_MSG) \ - MSG_MULTI_NOTIF(1, DEATH_MURDER_FALL, INFO_DEATH_MURDER_FALL, NO_MSG) \ - MSG_MULTI_NOTIF(1, DEATH_MURDER_FIRE, INFO_DEATH_MURDER_FIRE, NO_MSG) \ - MSG_MULTI_NOTIF(1, DEATH_MURDER_LAVA, INFO_DEATH_MURDER_LAVA, NO_MSG) \ - MSG_MULTI_NOTIF(1, DEATH_MURDER_SHOOTING_STAR, INFO_DEATH_MURDER_SHOOTING_STAR, NO_MSG) \ - MSG_MULTI_NOTIF(1, DEATH_MURDER_SLIME, INFO_DEATH_MURDER_SLIME, NO_MSG) \ - MSG_MULTI_NOTIF(1, DEATH_MURDER_SWAMP, INFO_DEATH_MURDER_SWAMP, NO_MSG) \ - MSG_MULTI_NOTIF(1, DEATH_MURDER_TELEFRAG, INFO_DEATH_MURDER_TELEFRAG, NO_MSG) \ - MSG_MULTI_NOTIF(1, DEATH_MURDER_TOUCHEXPLODE, INFO_DEATH_MURDER_TOUCHEXPLODE, NO_MSG) \ - MSG_MULTI_NOTIF(1, DEATH_MURDER_VH_BUMB_DEATH, INFO_DEATH_MURDER_VH_BUMB_DEATH, NO_MSG) \ - MSG_MULTI_NOTIF(1, DEATH_MURDER_VH_BUMB_GUN, INFO_DEATH_MURDER_VH_BUMB_GUN, NO_MSG) \ - MSG_MULTI_NOTIF(1, DEATH_MURDER_VH_CRUSH, INFO_DEATH_MURDER_VH_CRUSH, NO_MSG) \ - MSG_MULTI_NOTIF(1, DEATH_MURDER_VH_RAPT_BOMB, INFO_DEATH_MURDER_VH_RAPT_BOMB, NO_MSG) \ - MSG_MULTI_NOTIF(1, DEATH_MURDER_VH_RAPT_CANNON, INFO_DEATH_MURDER_VH_RAPT_CANNON, NO_MSG) \ - MSG_MULTI_NOTIF(1, DEATH_MURDER_VH_RAPT_DEATH, INFO_DEATH_MURDER_VH_RAPT_DEATH, NO_MSG) \ - MSG_MULTI_NOTIF(1, DEATH_MURDER_VH_SPID_DEATH, INFO_DEATH_MURDER_VH_SPID_DEATH, NO_MSG) \ - MSG_MULTI_NOTIF(1, DEATH_MURDER_VH_SPID_MINIGUN, INFO_DEATH_MURDER_VH_SPID_MINIGUN, NO_MSG) \ - MSG_MULTI_NOTIF(1, DEATH_MURDER_VH_SPID_ROCKET, INFO_DEATH_MURDER_VH_SPID_ROCKET, NO_MSG) \ - MSG_MULTI_NOTIF(1, DEATH_MURDER_VH_WAKI_DEATH, INFO_DEATH_MURDER_VH_WAKI_DEATH, NO_MSG) \ - MSG_MULTI_NOTIF(1, DEATH_MURDER_VH_WAKI_GUN, INFO_DEATH_MURDER_VH_WAKI_GUN, NO_MSG) \ - MSG_MULTI_NOTIF(1, DEATH_MURDER_VH_WAKI_ROCKET, INFO_DEATH_MURDER_VH_WAKI_ROCKET, NO_MSG) \ - MSG_MULTI_NOTIF(1, DEATH_MURDER_VOID, INFO_DEATH_MURDER_VOID, NO_MSG) \ - MSG_MULTI_NOTIF(1, DEATH_SELF_AUTOTEAMCHANGE, INFO_DEATH_SELF_AUTOTEAMCHANGE, CENTER_DEATH_SELF_AUTOTEAMCHANGE) \ - MSG_MULTI_NOTIF(1, DEATH_SELF_BETRAYAL, INFO_DEATH_SELF_BETRAYAL, CENTER_DEATH_SELF_BETRAYAL) \ - MSG_MULTI_NOTIF(1, DEATH_SELF_CAMP, INFO_DEATH_SELF_CAMP, CENTER_DEATH_SELF_CAMP) \ - MSG_MULTI_NOTIF(1, DEATH_SELF_CHEAT, INFO_DEATH_SELF_CHEAT, CENTER_DEATH_SELF_CHEAT) \ - MSG_MULTI_NOTIF(1, DEATH_SELF_CUSTOM, INFO_DEATH_SELF_GENERIC, CENTER_DEATH_SELF_CUSTOM) \ - MSG_MULTI_NOTIF(1, DEATH_SELF_DROWN, INFO_DEATH_SELF_DROWN, CENTER_DEATH_SELF_DROWN) \ - MSG_MULTI_NOTIF(1, DEATH_SELF_FALL, INFO_DEATH_SELF_FALL, CENTER_DEATH_SELF_FALL) \ - MSG_MULTI_NOTIF(1, DEATH_SELF_FIRE, INFO_DEATH_SELF_FIRE, CENTER_DEATH_SELF_FIRE) \ - MSG_MULTI_NOTIF(1, DEATH_SELF_GENERIC, INFO_DEATH_SELF_GENERIC, CENTER_DEATH_SELF_GENERIC) \ - MSG_MULTI_NOTIF(1, DEATH_SELF_LAVA, INFO_DEATH_SELF_LAVA, CENTER_DEATH_SELF_LAVA) \ - MSG_MULTI_NOTIF(1, DEATH_SELF_NOAMMO, INFO_DEATH_SELF_NOAMMO, CENTER_DEATH_SELF_NOAMMO) \ - MSG_MULTI_NOTIF(1, DEATH_SELF_ROT, INFO_DEATH_SELF_ROT, CENTER_DEATH_SELF_ROT) \ - MSG_MULTI_NOTIF(1, DEATH_SELF_SHOOTING_STAR, INFO_DEATH_SELF_SHOOTING_STAR, CENTER_DEATH_SELF_SHOOTING_STAR) \ - MSG_MULTI_NOTIF(1, DEATH_SELF_SLIME, INFO_DEATH_SELF_SLIME, CENTER_DEATH_SELF_SLIME) \ - MSG_MULTI_NOTIF(1, DEATH_SELF_SUICIDE, INFO_DEATH_SELF_SUICIDE, CENTER_DEATH_SELF_SUICIDE) \ - MSG_MULTI_NOTIF(1, DEATH_SELF_SWAMP, INFO_DEATH_SELF_SWAMP, CENTER_DEATH_SELF_SWAMP) \ - MSG_MULTI_NOTIF(1, DEATH_SELF_TEAMCHANGE, INFO_DEATH_SELF_TEAMCHANGE, CENTER_DEATH_SELF_TEAMCHANGE) \ - MSG_MULTI_NOTIF(1, DEATH_SELF_TOUCHEXPLODE, INFO_DEATH_SELF_TOUCHEXPLODE, CENTER_DEATH_SELF_TOUCHEXPLODE) \ - MSG_MULTI_NOTIF(1, DEATH_SELF_TURRET, INFO_DEATH_SELF_TURRET, CENTER_DEATH_SELF_TURRET) \ - MSG_MULTI_NOTIF(1, DEATH_SELF_TURRET_EWHEEL, INFO_DEATH_SELF_TURRET_EWHEEL, CENTER_DEATH_SELF_TURRET_EWHEEL) \ - MSG_MULTI_NOTIF(1, DEATH_SELF_TURRET_FLAC, INFO_DEATH_SELF_TURRET_FLAC, CENTER_DEATH_SELF_TURRET) \ - MSG_MULTI_NOTIF(1, DEATH_SELF_TURRET_HELLION, INFO_DEATH_SELF_TURRET_HELLION, CENTER_DEATH_SELF_TURRET) \ - MSG_MULTI_NOTIF(1, DEATH_SELF_TURRET_HK, INFO_DEATH_SELF_TURRET_HK, CENTER_DEATH_SELF_TURRET) \ - MSG_MULTI_NOTIF(1, DEATH_SELF_TURRET_MACHINEGUN, INFO_DEATH_SELF_TURRET_MACHINEGUN, CENTER_DEATH_SELF_TURRET) \ - MSG_MULTI_NOTIF(1, DEATH_SELF_TURRET_MLRS, INFO_DEATH_SELF_TURRET_MLRS, CENTER_DEATH_SELF_TURRET) \ - MSG_MULTI_NOTIF(1, DEATH_SELF_TURRET_PHASER, INFO_DEATH_SELF_TURRET_PHASER, CENTER_DEATH_SELF_TURRET) \ - MSG_MULTI_NOTIF(1, DEATH_SELF_TURRET_PLASMA, INFO_DEATH_SELF_TURRET_PLASMA, CENTER_DEATH_SELF_TURRET) \ - MSG_MULTI_NOTIF(1, DEATH_SELF_TURRET_TESLA, INFO_DEATH_SELF_TURRET_TESLA, CENTER_DEATH_SELF_TURRET) \ - MSG_MULTI_NOTIF(1, DEATH_SELF_TURRET_WALK_GUN, INFO_DEATH_SELF_TURRET_WALK_GUN, CENTER_DEATH_SELF_TURRET_WALK) \ - MSG_MULTI_NOTIF(1, DEATH_SELF_TURRET_WALK_MEELE, INFO_DEATH_SELF_TURRET_WALK_MEELE, CENTER_DEATH_SELF_TURRET_WALK) \ - MSG_MULTI_NOTIF(1, DEATH_SELF_TURRET_WALK_ROCKET, INFO_DEATH_SELF_TURRET_WALK_ROCKET, CENTER_DEATH_SELF_TURRET_WALK) \ - MSG_MULTI_NOTIF(1, DEATH_SELF_VH_BUMB_DEATH, INFO_DEATH_SELF_VH_BUMB_DEATH, CENTER_DEATH_SELF_VH_BUMB_DEATH) \ - MSG_MULTI_NOTIF(1, DEATH_SELF_VH_CRUSH, INFO_DEATH_SELF_VH_CRUSH, CENTER_DEATH_SELF_VH_CRUSH) \ - MSG_MULTI_NOTIF(1, DEATH_SELF_VH_RAPT_BOMB, INFO_DEATH_SELF_VH_RAPT_BOMB, CENTER_DEATH_SELF_VH_RAPT_BOMB) \ - MSG_MULTI_NOTIF(1, DEATH_SELF_VH_RAPT_DEATH, INFO_DEATH_SELF_VH_RAPT_DEATH, CENTER_DEATH_SELF_VH_RAPT_DEATH) \ - MSG_MULTI_NOTIF(1, DEATH_SELF_VH_SPID_DEATH, INFO_DEATH_SELF_VH_SPID_DEATH, CENTER_DEATH_SELF_VH_SPID_DEATH) \ - MSG_MULTI_NOTIF(1, DEATH_SELF_VH_SPID_ROCKET, INFO_DEATH_SELF_VH_SPID_ROCKET, CENTER_DEATH_SELF_VH_SPID_ROCKET) \ - MSG_MULTI_NOTIF(1, DEATH_SELF_VH_WAKI_DEATH, INFO_DEATH_SELF_VH_WAKI_DEATH, CENTER_DEATH_SELF_VH_WAKI_DEATH) \ - MSG_MULTI_NOTIF(1, DEATH_SELF_VH_WAKI_ROCKET, INFO_DEATH_SELF_VH_WAKI_ROCKET, CENTER_DEATH_SELF_VH_WAKI_ROCKET) \ - MSG_MULTI_NOTIF(1, DEATH_SELF_VOID, INFO_DEATH_SELF_VOID, CENTER_DEATH_SELF_VOID) \ - MSG_MULTI_NOTIF(1, ITEM_WEAPON_DONTHAVE, INFO_ITEM_WEAPON_DONTHAVE, CENTER_ITEM_WEAPON_DONTHAVE) \ - MSG_MULTI_NOTIF(1, ITEM_WEAPON_DROP, INFO_ITEM_WEAPON_DROP, CENTER_ITEM_WEAPON_DROP) \ - MSG_MULTI_NOTIF(1, ITEM_WEAPON_GOT, INFO_ITEM_WEAPON_GOT, CENTER_ITEM_WEAPON_GOT) \ - MSG_MULTI_NOTIF(1, ITEM_WEAPON_NOAMMO, INFO_ITEM_WEAPON_NOAMMO, CENTER_ITEM_WEAPON_NOAMMO) \ - MSG_MULTI_NOTIF(1, ITEM_WEAPON_PRIMORSEC, INFO_ITEM_WEAPON_PRIMORSEC, CENTER_ITEM_WEAPON_PRIMORSEC) \ - MSG_MULTI_NOTIF(1, ITEM_WEAPON_UNAVAILABLE, INFO_ITEM_WEAPON_UNAVAILABLE, CENTER_ITEM_WEAPON_UNAVAILABLE) \ - MSG_MULTI_NOTIF(1, WEAPON_ACCORDEON_MURDER, INFO_WEAPON_ACCORDEON_MURDER, NO_MSG) \ - MSG_MULTI_NOTIF(1, WEAPON_ACCORDEON_SUICIDE, INFO_WEAPON_ACCORDEON_SUICIDE, CENTER_DEATH_SELF_GENERIC) \ - MSG_MULTI_NOTIF(1, WEAPON_CRYLINK_MURDER, INFO_WEAPON_CRYLINK_MURDER, NO_MSG) \ - MSG_MULTI_NOTIF(1, WEAPON_CRYLINK_SUICIDE, INFO_WEAPON_CRYLINK_SUICIDE, CENTER_DEATH_SELF_GENERIC) \ - MSG_MULTI_NOTIF(1, WEAPON_ELECTRO_MURDER_BOLT, INFO_WEAPON_ELECTRO_MURDER_BOLT, NO_MSG) \ - MSG_MULTI_NOTIF(1, WEAPON_ELECTRO_MURDER_COMBO, INFO_WEAPON_ELECTRO_MURDER_COMBO, NO_MSG) \ - MSG_MULTI_NOTIF(1, WEAPON_ELECTRO_MURDER_ORBS, INFO_WEAPON_ELECTRO_MURDER_ORBS, NO_MSG) \ - MSG_MULTI_NOTIF(1, WEAPON_ELECTRO_SUICIDE_BOLT, INFO_WEAPON_ELECTRO_SUICIDE_BOLT, CENTER_DEATH_SELF_GENERIC) \ - MSG_MULTI_NOTIF(1, WEAPON_ELECTRO_SUICIDE_ORBS, INFO_WEAPON_ELECTRO_SUICIDE_ORBS, CENTER_DEATH_SELF_GENERIC) \ - MSG_MULTI_NOTIF(1, WEAPON_FIREBALL_MURDER_BLAST, INFO_WEAPON_FIREBALL_MURDER_BLAST, NO_MSG) \ - MSG_MULTI_NOTIF(1, WEAPON_FIREBALL_MURDER_FIREMINE, INFO_WEAPON_FIREBALL_MURDER_FIREMINE, NO_MSG) \ - MSG_MULTI_NOTIF(1, WEAPON_FIREBALL_SUICIDE_BLAST, INFO_WEAPON_FIREBALL_SUICIDE_BLAST, CENTER_DEATH_SELF_GENERIC) \ - MSG_MULTI_NOTIF(1, WEAPON_FIREBALL_SUICIDE_FIREMINE, INFO_WEAPON_FIREBALL_SUICIDE_FIREMINE, CENTER_DEATH_SELF_GENERIC) \ - MSG_MULTI_NOTIF(1, WEAPON_HAGAR_MURDER_BURST, INFO_WEAPON_HAGAR_MURDER_BURST, NO_MSG) \ - MSG_MULTI_NOTIF(1, WEAPON_HAGAR_MURDER_SPRAY, INFO_WEAPON_HAGAR_MURDER_SPRAY, NO_MSG) \ - MSG_MULTI_NOTIF(1, WEAPON_HAGAR_SUICIDE, INFO_WEAPON_HAGAR_SUICIDE, CENTER_DEATH_SELF_GENERIC) \ - MSG_MULTI_NOTIF(1, WEAPON_HLAC_MURDER, INFO_WEAPON_HLAC_MURDER, NO_MSG) \ - MSG_MULTI_NOTIF(1, WEAPON_HLAC_SUICIDE, INFO_WEAPON_HLAC_SUICIDE, CENTER_DEATH_SELF_GENERIC) \ - MSG_MULTI_NOTIF(1, WEAPON_HOOK_MURDER, INFO_WEAPON_HOOK_MURDER, NO_MSG) \ - MSG_MULTI_NOTIF(1, WEAPON_KLEINBOTTLE_MURDER, INFO_WEAPON_KLEINBOTTLE_MURDER, NO_MSG) \ - MSG_MULTI_NOTIF(1, WEAPON_KLEINBOTTLE_SUICIDE, INFO_WEAPON_KLEINBOTTLE_SUICIDE, CENTER_DEATH_SELF_GENERIC) \ - MSG_MULTI_NOTIF(1, WEAPON_LASER_MURDER, INFO_WEAPON_LASER_MURDER, NO_MSG) \ - MSG_MULTI_NOTIF(1, WEAPON_LASER_SUICIDE, INFO_WEAPON_LASER_SUICIDE, CENTER_DEATH_SELF_GENERIC) \ - MSG_MULTI_NOTIF(1, WEAPON_MINELAYER_MURDER, INFO_WEAPON_MINELAYER_MURDER, NO_MSG) \ - MSG_MULTI_NOTIF(1, WEAPON_MINELAYER_SUICIDE, INFO_WEAPON_MINELAYER_SUICIDE, CENTER_DEATH_SELF_GENERIC) \ - MSG_MULTI_NOTIF(1, WEAPON_MINSTANEX_MURDER, INFO_WEAPON_MINSTANEX_MURDER, NO_MSG) \ - MSG_MULTI_NOTIF(1, WEAPON_MORTAR_MURDER_BOUNCE, INFO_WEAPON_MORTAR_MURDER_BOUNCE, NO_MSG) \ - MSG_MULTI_NOTIF(1, WEAPON_MORTAR_MURDER_EXPLODE, INFO_WEAPON_MORTAR_MURDER_EXPLODE, NO_MSG) \ - MSG_MULTI_NOTIF(1, WEAPON_MORTAR_SUICIDE_BOUNCE, INFO_WEAPON_MORTAR_SUICIDE_BOUNCE, CENTER_DEATH_SELF_GENERIC) \ - MSG_MULTI_NOTIF(1, WEAPON_MORTAR_SUICIDE_EXPLODE, INFO_WEAPON_MORTAR_SUICIDE_EXPLODE, CENTER_DEATH_SELF_GENERIC) \ - MSG_MULTI_NOTIF(1, WEAPON_NEX_MURDER, INFO_WEAPON_NEX_MURDER, NO_MSG) \ - MSG_MULTI_NOTIF(1, WEAPON_RIFLE_MURDER, INFO_WEAPON_RIFLE_MURDER, NO_MSG) \ - MSG_MULTI_NOTIF(1, WEAPON_RIFLE_MURDER_HAIL, INFO_WEAPON_RIFLE_MURDER_HAIL, NO_MSG) \ - MSG_MULTI_NOTIF(1, WEAPON_RIFLE_MURDER_HAIL_PIERCING, INFO_WEAPON_RIFLE_MURDER_HAIL_PIERCING, NO_MSG) \ - MSG_MULTI_NOTIF(1, WEAPON_RIFLE_MURDER_HEADSHOT, INFO_WEAPON_RIFLE_MURDER_HEADSHOT, NO_MSG) \ - MSG_MULTI_NOTIF(1, WEAPON_RIFLE_MURDER_PIERCING, INFO_WEAPON_RIFLE_MURDER_PIERCING, NO_MSG) \ - MSG_MULTI_NOTIF(1, WEAPON_ROCKETLAUNCHER_MURDER_DIRECT, INFO_WEAPON_ROCKETLAUNCHER_MURDER_DIRECT, NO_MSG) \ - MSG_MULTI_NOTIF(1, WEAPON_ROCKETLAUNCHER_MURDER_SPLASH, INFO_WEAPON_ROCKETLAUNCHER_MURDER_SPLASH, NO_MSG) \ - MSG_MULTI_NOTIF(1, WEAPON_ROCKETLAUNCHER_SUICIDE, INFO_WEAPON_ROCKETLAUNCHER_SUICIDE, CENTER_DEATH_SELF_GENERIC) \ - MSG_MULTI_NOTIF(1, WEAPON_SEEKER_MURDER_SPRAY, INFO_WEAPON_SEEKER_MURDER_SPRAY, NO_MSG) \ - MSG_MULTI_NOTIF(1, WEAPON_SEEKER_MURDER_TAG, INFO_WEAPON_SEEKER_MURDER_TAG, NO_MSG) \ - MSG_MULTI_NOTIF(1, WEAPON_SEEKER_SUICIDE, INFO_WEAPON_SEEKER_SUICIDE, CENTER_DEATH_SELF_GENERIC) \ - MSG_MULTI_NOTIF(1, WEAPON_SHOTGUN_MURDER, INFO_WEAPON_SHOTGUN_MURDER, NO_MSG) \ - MSG_MULTI_NOTIF(1, WEAPON_SHOTGUN_MURDER_SLAP, INFO_WEAPON_SHOTGUN_MURDER_SLAP, NO_MSG) \ - MSG_MULTI_NOTIF(1, WEAPON_THINKING_WITH_PORTALS, INFO_WEAPON_THINKING_WITH_PORTALS, CENTER_DEATH_SELF_GENERIC) \ - MSG_MULTI_NOTIF(1, WEAPON_TUBA_MURDER, INFO_WEAPON_TUBA_MURDER, NO_MSG) \ - MSG_MULTI_NOTIF(1, WEAPON_TUBA_SUICIDE, INFO_WEAPON_TUBA_SUICIDE, CENTER_DEATH_SELF_GENERIC) \ - MSG_MULTI_NOTIF(1, WEAPON_UZI_MURDER_SNIPE, INFO_WEAPON_UZI_MURDER_SNIPE, NO_MSG) \ - MSG_MULTI_NOTIF(1, WEAPON_UZI_MURDER_SPRAY, INFO_WEAPON_UZI_MURDER_SPRAY, NO_MSG) + MSG_MULTI_NOTIF(1, DEATH_MURDER_CHEAT, NO_MSG, INFO_DEATH_MURDER_CHEAT, NO_MSG) \ + MSG_MULTI_NOTIF(1, DEATH_MURDER_DROWN, NO_MSG, INFO_DEATH_MURDER_DROWN, NO_MSG) \ + MSG_MULTI_NOTIF(1, DEATH_MURDER_FALL, NO_MSG, INFO_DEATH_MURDER_FALL, NO_MSG) \ + MSG_MULTI_NOTIF(1, DEATH_MURDER_FIRE, NO_MSG, INFO_DEATH_MURDER_FIRE, NO_MSG) \ + MSG_MULTI_NOTIF(1, DEATH_MURDER_LAVA, NO_MSG, INFO_DEATH_MURDER_LAVA, NO_MSG) \ + MSG_MULTI_NOTIF(1, DEATH_MURDER_SHOOTING_STAR, NO_MSG, INFO_DEATH_MURDER_SHOOTING_STAR, NO_MSG) \ + MSG_MULTI_NOTIF(1, DEATH_MURDER_SLIME, NO_MSG, INFO_DEATH_MURDER_SLIME, NO_MSG) \ + MSG_MULTI_NOTIF(1, DEATH_MURDER_SWAMP, NO_MSG, INFO_DEATH_MURDER_SWAMP, NO_MSG) \ + MSG_MULTI_NOTIF(1, DEATH_MURDER_TELEFRAG, NO_MSG, INFO_DEATH_MURDER_TELEFRAG, NO_MSG) \ + MSG_MULTI_NOTIF(1, DEATH_MURDER_TOUCHEXPLODE, NO_MSG, INFO_DEATH_MURDER_TOUCHEXPLODE, NO_MSG) \ + MSG_MULTI_NOTIF(1, DEATH_MURDER_VH_BUMB_DEATH, NO_MSG, INFO_DEATH_MURDER_VH_BUMB_DEATH, NO_MSG) \ + MSG_MULTI_NOTIF(1, DEATH_MURDER_VH_BUMB_GUN, NO_MSG, INFO_DEATH_MURDER_VH_BUMB_GUN, NO_MSG) \ + MSG_MULTI_NOTIF(1, DEATH_MURDER_VH_CRUSH, NO_MSG, INFO_DEATH_MURDER_VH_CRUSH, NO_MSG) \ + MSG_MULTI_NOTIF(1, DEATH_MURDER_VH_RAPT_BOMB, NO_MSG, INFO_DEATH_MURDER_VH_RAPT_BOMB, NO_MSG) \ + MSG_MULTI_NOTIF(1, DEATH_MURDER_VH_RAPT_CANNON, NO_MSG, INFO_DEATH_MURDER_VH_RAPT_CANNON, NO_MSG) \ + MSG_MULTI_NOTIF(1, DEATH_MURDER_VH_RAPT_DEATH, NO_MSG, INFO_DEATH_MURDER_VH_RAPT_DEATH, NO_MSG) \ + MSG_MULTI_NOTIF(1, DEATH_MURDER_VH_SPID_DEATH, NO_MSG, INFO_DEATH_MURDER_VH_SPID_DEATH, NO_MSG) \ + MSG_MULTI_NOTIF(1, DEATH_MURDER_VH_SPID_MINIGUN, NO_MSG, INFO_DEATH_MURDER_VH_SPID_MINIGUN, NO_MSG) \ + MSG_MULTI_NOTIF(1, DEATH_MURDER_VH_SPID_ROCKET, NO_MSG, INFO_DEATH_MURDER_VH_SPID_ROCKET, NO_MSG) \ + MSG_MULTI_NOTIF(1, DEATH_MURDER_VH_WAKI_DEATH, NO_MSG, INFO_DEATH_MURDER_VH_WAKI_DEATH, NO_MSG) \ + MSG_MULTI_NOTIF(1, DEATH_MURDER_VH_WAKI_GUN, NO_MSG, INFO_DEATH_MURDER_VH_WAKI_GUN, NO_MSG) \ + MSG_MULTI_NOTIF(1, DEATH_MURDER_VH_WAKI_ROCKET, NO_MSG, INFO_DEATH_MURDER_VH_WAKI_ROCKET, NO_MSG) \ + MSG_MULTI_NOTIF(1, DEATH_MURDER_VOID, NO_MSG, INFO_DEATH_MURDER_VOID, NO_MSG) \ + MSG_MULTI_NOTIF(1, DEATH_SELF_AUTOTEAMCHANGE, NO_MSG, INFO_DEATH_SELF_AUTOTEAMCHANGE, CENTER_DEATH_SELF_AUTOTEAMCHANGE) \ + MSG_MULTI_NOTIF(1, DEATH_SELF_BETRAYAL, NO_MSG, INFO_DEATH_SELF_BETRAYAL, CENTER_DEATH_SELF_BETRAYAL) \ + MSG_MULTI_NOTIF(1, DEATH_SELF_CAMP, NO_MSG, INFO_DEATH_SELF_CAMP, CENTER_DEATH_SELF_CAMP) \ + MSG_MULTI_NOTIF(1, DEATH_SELF_CHEAT, NO_MSG, INFO_DEATH_SELF_CHEAT, CENTER_DEATH_SELF_CHEAT) \ + MSG_MULTI_NOTIF(1, DEATH_SELF_CUSTOM, NO_MSG, INFO_DEATH_SELF_GENERIC, CENTER_DEATH_SELF_CUSTOM) \ + MSG_MULTI_NOTIF(1, DEATH_SELF_DROWN, NO_MSG, INFO_DEATH_SELF_DROWN, CENTER_DEATH_SELF_DROWN) \ + MSG_MULTI_NOTIF(1, DEATH_SELF_FALL, NO_MSG, INFO_DEATH_SELF_FALL, CENTER_DEATH_SELF_FALL) \ + MSG_MULTI_NOTIF(1, DEATH_SELF_FIRE, NO_MSG, INFO_DEATH_SELF_FIRE, CENTER_DEATH_SELF_FIRE) \ + MSG_MULTI_NOTIF(1, DEATH_SELF_GENERIC, NO_MSG, INFO_DEATH_SELF_GENERIC, CENTER_DEATH_SELF_GENERIC) \ + MSG_MULTI_NOTIF(1, DEATH_SELF_LAVA, NO_MSG, INFO_DEATH_SELF_LAVA, CENTER_DEATH_SELF_LAVA) \ + MSG_MULTI_NOTIF(1, DEATH_SELF_NOAMMO, NO_MSG, INFO_DEATH_SELF_NOAMMO, CENTER_DEATH_SELF_NOAMMO) \ + MSG_MULTI_NOTIF(1, DEATH_SELF_ROT, NO_MSG, INFO_DEATH_SELF_ROT, CENTER_DEATH_SELF_ROT) \ + MSG_MULTI_NOTIF(1, DEATH_SELF_SHOOTING_STAR, NO_MSG, INFO_DEATH_SELF_SHOOTING_STAR, CENTER_DEATH_SELF_SHOOTING_STAR) \ + MSG_MULTI_NOTIF(1, DEATH_SELF_SLIME, NO_MSG, INFO_DEATH_SELF_SLIME, CENTER_DEATH_SELF_SLIME) \ + MSG_MULTI_NOTIF(1, DEATH_SELF_SUICIDE, NO_MSG, INFO_DEATH_SELF_SUICIDE, CENTER_DEATH_SELF_SUICIDE) \ + MSG_MULTI_NOTIF(1, DEATH_SELF_SWAMP, NO_MSG, INFO_DEATH_SELF_SWAMP, CENTER_DEATH_SELF_SWAMP) \ + MSG_MULTI_NOTIF(1, DEATH_SELF_TEAMCHANGE, NO_MSG, INFO_DEATH_SELF_TEAMCHANGE, CENTER_DEATH_SELF_TEAMCHANGE) \ + MSG_MULTI_NOTIF(1, DEATH_SELF_TOUCHEXPLODE, NO_MSG, INFO_DEATH_SELF_TOUCHEXPLODE, CENTER_DEATH_SELF_TOUCHEXPLODE) \ + MSG_MULTI_NOTIF(1, DEATH_SELF_TURRET, NO_MSG, INFO_DEATH_SELF_TURRET, CENTER_DEATH_SELF_TURRET) \ + MSG_MULTI_NOTIF(1, DEATH_SELF_TURRET_EWHEEL, NO_MSG, INFO_DEATH_SELF_TURRET_EWHEEL, CENTER_DEATH_SELF_TURRET_EWHEEL) \ + MSG_MULTI_NOTIF(1, DEATH_SELF_TURRET_FLAC, NO_MSG, INFO_DEATH_SELF_TURRET_FLAC, CENTER_DEATH_SELF_TURRET) \ + MSG_MULTI_NOTIF(1, DEATH_SELF_TURRET_HELLION, NO_MSG, INFO_DEATH_SELF_TURRET_HELLION, CENTER_DEATH_SELF_TURRET) \ + MSG_MULTI_NOTIF(1, DEATH_SELF_TURRET_HK, NO_MSG, INFO_DEATH_SELF_TURRET_HK, CENTER_DEATH_SELF_TURRET) \ + MSG_MULTI_NOTIF(1, DEATH_SELF_TURRET_MACHINEGUN, NO_MSG, INFO_DEATH_SELF_TURRET_MACHINEGUN, CENTER_DEATH_SELF_TURRET) \ + MSG_MULTI_NOTIF(1, DEATH_SELF_TURRET_MLRS, NO_MSG, INFO_DEATH_SELF_TURRET_MLRS, CENTER_DEATH_SELF_TURRET) \ + MSG_MULTI_NOTIF(1, DEATH_SELF_TURRET_PHASER, NO_MSG, INFO_DEATH_SELF_TURRET_PHASER, CENTER_DEATH_SELF_TURRET) \ + MSG_MULTI_NOTIF(1, DEATH_SELF_TURRET_PLASMA, NO_MSG, INFO_DEATH_SELF_TURRET_PLASMA, CENTER_DEATH_SELF_TURRET) \ + MSG_MULTI_NOTIF(1, DEATH_SELF_TURRET_TESLA, NO_MSG, INFO_DEATH_SELF_TURRET_TESLA, CENTER_DEATH_SELF_TURRET) \ + MSG_MULTI_NOTIF(1, DEATH_SELF_TURRET_WALK_GUN, NO_MSG, INFO_DEATH_SELF_TURRET_WALK_GUN, CENTER_DEATH_SELF_TURRET_WALK) \ + MSG_MULTI_NOTIF(1, DEATH_SELF_TURRET_WALK_MEELE, NO_MSG, INFO_DEATH_SELF_TURRET_WALK_MEELE, CENTER_DEATH_SELF_TURRET_WALK) \ + MSG_MULTI_NOTIF(1, DEATH_SELF_TURRET_WALK_ROCKET, NO_MSG, INFO_DEATH_SELF_TURRET_WALK_ROCKET, CENTER_DEATH_SELF_TURRET_WALK) \ + MSG_MULTI_NOTIF(1, DEATH_SELF_VH_BUMB_DEATH, NO_MSG, INFO_DEATH_SELF_VH_BUMB_DEATH, CENTER_DEATH_SELF_VH_BUMB_DEATH) \ + MSG_MULTI_NOTIF(1, DEATH_SELF_VH_CRUSH, NO_MSG, INFO_DEATH_SELF_VH_CRUSH, CENTER_DEATH_SELF_VH_CRUSH) \ + MSG_MULTI_NOTIF(1, DEATH_SELF_VH_RAPT_BOMB, NO_MSG, INFO_DEATH_SELF_VH_RAPT_BOMB, CENTER_DEATH_SELF_VH_RAPT_BOMB) \ + MSG_MULTI_NOTIF(1, DEATH_SELF_VH_RAPT_DEATH, NO_MSG, INFO_DEATH_SELF_VH_RAPT_DEATH, CENTER_DEATH_SELF_VH_RAPT_DEATH) \ + MSG_MULTI_NOTIF(1, DEATH_SELF_VH_SPID_DEATH, NO_MSG, INFO_DEATH_SELF_VH_SPID_DEATH, CENTER_DEATH_SELF_VH_SPID_DEATH) \ + MSG_MULTI_NOTIF(1, DEATH_SELF_VH_SPID_ROCKET, NO_MSG, INFO_DEATH_SELF_VH_SPID_ROCKET, CENTER_DEATH_SELF_VH_SPID_ROCKET) \ + MSG_MULTI_NOTIF(1, DEATH_SELF_VH_WAKI_DEATH, NO_MSG, INFO_DEATH_SELF_VH_WAKI_DEATH, CENTER_DEATH_SELF_VH_WAKI_DEATH) \ + MSG_MULTI_NOTIF(1, DEATH_SELF_VH_WAKI_ROCKET, NO_MSG, INFO_DEATH_SELF_VH_WAKI_ROCKET, CENTER_DEATH_SELF_VH_WAKI_ROCKET) \ + MSG_MULTI_NOTIF(1, DEATH_SELF_VOID, NO_MSG, INFO_DEATH_SELF_VOID, CENTER_DEATH_SELF_VOID) \ + MSG_MULTI_NOTIF(1, ITEM_WEAPON_DONTHAVE, NO_MSG, INFO_ITEM_WEAPON_DONTHAVE, CENTER_ITEM_WEAPON_DONTHAVE) \ + MSG_MULTI_NOTIF(1, ITEM_WEAPON_DROP, NO_MSG, INFO_ITEM_WEAPON_DROP, CENTER_ITEM_WEAPON_DROP) \ + MSG_MULTI_NOTIF(1, ITEM_WEAPON_GOT, NO_MSG, INFO_ITEM_WEAPON_GOT, CENTER_ITEM_WEAPON_GOT) \ + 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_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) \ + MSG_MULTI_NOTIF(1, WEAPON_ACCORDEON_SUICIDE, NO_MSG, INFO_WEAPON_ACCORDEON_SUICIDE, CENTER_DEATH_SELF_GENERIC) \ + MSG_MULTI_NOTIF(1, WEAPON_CRYLINK_MURDER, NO_MSG, INFO_WEAPON_CRYLINK_MURDER, NO_MSG) \ + MSG_MULTI_NOTIF(1, WEAPON_CRYLINK_SUICIDE, NO_MSG, INFO_WEAPON_CRYLINK_SUICIDE, CENTER_DEATH_SELF_GENERIC) \ + MSG_MULTI_NOTIF(1, WEAPON_ELECTRO_MURDER_BOLT, NO_MSG, INFO_WEAPON_ELECTRO_MURDER_BOLT, NO_MSG) \ + MSG_MULTI_NOTIF(1, WEAPON_ELECTRO_MURDER_COMBO, NO_MSG, INFO_WEAPON_ELECTRO_MURDER_COMBO, NO_MSG) \ + MSG_MULTI_NOTIF(1, WEAPON_ELECTRO_MURDER_ORBS, NO_MSG, INFO_WEAPON_ELECTRO_MURDER_ORBS, NO_MSG) \ + MSG_MULTI_NOTIF(1, WEAPON_ELECTRO_SUICIDE_BOLT, NO_MSG, INFO_WEAPON_ELECTRO_SUICIDE_BOLT, CENTER_DEATH_SELF_GENERIC) \ + MSG_MULTI_NOTIF(1, WEAPON_ELECTRO_SUICIDE_ORBS, NO_MSG, INFO_WEAPON_ELECTRO_SUICIDE_ORBS, CENTER_DEATH_SELF_GENERIC) \ + MSG_MULTI_NOTIF(1, WEAPON_FIREBALL_MURDER_BLAST, NO_MSG, INFO_WEAPON_FIREBALL_MURDER_BLAST, NO_MSG) \ + MSG_MULTI_NOTIF(1, WEAPON_FIREBALL_MURDER_FIREMINE, NO_MSG, INFO_WEAPON_FIREBALL_MURDER_FIREMINE, NO_MSG) \ + MSG_MULTI_NOTIF(1, WEAPON_FIREBALL_SUICIDE_BLAST, NO_MSG, INFO_WEAPON_FIREBALL_SUICIDE_BLAST, CENTER_DEATH_SELF_GENERIC) \ + MSG_MULTI_NOTIF(1, WEAPON_FIREBALL_SUICIDE_FIREMINE, NO_MSG, INFO_WEAPON_FIREBALL_SUICIDE_FIREMINE, CENTER_DEATH_SELF_GENERIC) \ + MSG_MULTI_NOTIF(1, WEAPON_HAGAR_MURDER_BURST, NO_MSG, INFO_WEAPON_HAGAR_MURDER_BURST, NO_MSG) \ + MSG_MULTI_NOTIF(1, WEAPON_HAGAR_MURDER_SPRAY, NO_MSG, INFO_WEAPON_HAGAR_MURDER_SPRAY, NO_MSG) \ + MSG_MULTI_NOTIF(1, WEAPON_HAGAR_SUICIDE, NO_MSG, INFO_WEAPON_HAGAR_SUICIDE, CENTER_DEATH_SELF_GENERIC) \ + MSG_MULTI_NOTIF(1, WEAPON_HLAC_MURDER, NO_MSG, INFO_WEAPON_HLAC_MURDER, NO_MSG) \ + MSG_MULTI_NOTIF(1, WEAPON_HLAC_SUICIDE, NO_MSG, INFO_WEAPON_HLAC_SUICIDE, CENTER_DEATH_SELF_GENERIC) \ + MSG_MULTI_NOTIF(1, WEAPON_HOOK_MURDER, NO_MSG, INFO_WEAPON_HOOK_MURDER, NO_MSG) \ + MSG_MULTI_NOTIF(1, WEAPON_KLEINBOTTLE_MURDER, NO_MSG, INFO_WEAPON_KLEINBOTTLE_MURDER, NO_MSG) \ + MSG_MULTI_NOTIF(1, WEAPON_KLEINBOTTLE_SUICIDE, NO_MSG, INFO_WEAPON_KLEINBOTTLE_SUICIDE, CENTER_DEATH_SELF_GENERIC) \ + MSG_MULTI_NOTIF(1, WEAPON_LASER_MURDER, NO_MSG, INFO_WEAPON_LASER_MURDER, NO_MSG) \ + MSG_MULTI_NOTIF(1, WEAPON_LASER_SUICIDE, NO_MSG, INFO_WEAPON_LASER_SUICIDE, CENTER_DEATH_SELF_GENERIC) \ + MSG_MULTI_NOTIF(1, WEAPON_MINELAYER_MURDER, NO_MSG, INFO_WEAPON_MINELAYER_MURDER, NO_MSG) \ + MSG_MULTI_NOTIF(1, WEAPON_MINELAYER_SUICIDE, NO_MSG, INFO_WEAPON_MINELAYER_SUICIDE, CENTER_DEATH_SELF_GENERIC) \ + MSG_MULTI_NOTIF(1, WEAPON_MINSTANEX_MURDER, NO_MSG, INFO_WEAPON_MINSTANEX_MURDER, NO_MSG) \ + MSG_MULTI_NOTIF(1, WEAPON_MORTAR_MURDER_BOUNCE, NO_MSG, INFO_WEAPON_MORTAR_MURDER_BOUNCE, NO_MSG) \ + MSG_MULTI_NOTIF(1, WEAPON_MORTAR_MURDER_EXPLODE, NO_MSG, INFO_WEAPON_MORTAR_MURDER_EXPLODE, NO_MSG) \ + MSG_MULTI_NOTIF(1, WEAPON_MORTAR_SUICIDE_BOUNCE, NO_MSG, INFO_WEAPON_MORTAR_SUICIDE_BOUNCE, CENTER_DEATH_SELF_GENERIC) \ + MSG_MULTI_NOTIF(1, WEAPON_MORTAR_SUICIDE_EXPLODE, NO_MSG, INFO_WEAPON_MORTAR_SUICIDE_EXPLODE, CENTER_DEATH_SELF_GENERIC) \ + MSG_MULTI_NOTIF(1, WEAPON_NEX_MURDER, NO_MSG, INFO_WEAPON_NEX_MURDER, NO_MSG) \ + MSG_MULTI_NOTIF(1, WEAPON_RIFLE_MURDER, NO_MSG, INFO_WEAPON_RIFLE_MURDER, NO_MSG) \ + MSG_MULTI_NOTIF(1, WEAPON_RIFLE_MURDER_HAIL, NO_MSG, INFO_WEAPON_RIFLE_MURDER_HAIL, NO_MSG) \ + MSG_MULTI_NOTIF(1, WEAPON_RIFLE_MURDER_HAIL_PIERCING, NO_MSG, INFO_WEAPON_RIFLE_MURDER_HAIL_PIERCING, NO_MSG) \ + MSG_MULTI_NOTIF(1, WEAPON_RIFLE_MURDER_PIERCING, NO_MSG, INFO_WEAPON_RIFLE_MURDER_PIERCING, NO_MSG) \ + MSG_MULTI_NOTIF(1, WEAPON_ROCKETLAUNCHER_MURDER_DIRECT, NO_MSG, INFO_WEAPON_ROCKETLAUNCHER_MURDER_DIRECT, NO_MSG) \ + MSG_MULTI_NOTIF(1, WEAPON_ROCKETLAUNCHER_MURDER_SPLASH, NO_MSG, INFO_WEAPON_ROCKETLAUNCHER_MURDER_SPLASH, NO_MSG) \ + MSG_MULTI_NOTIF(1, WEAPON_ROCKETLAUNCHER_SUICIDE, NO_MSG, INFO_WEAPON_ROCKETLAUNCHER_SUICIDE, CENTER_DEATH_SELF_GENERIC) \ + MSG_MULTI_NOTIF(1, WEAPON_SEEKER_MURDER_SPRAY, NO_MSG, INFO_WEAPON_SEEKER_MURDER_SPRAY, NO_MSG) \ + MSG_MULTI_NOTIF(1, WEAPON_SEEKER_MURDER_TAG, NO_MSG, INFO_WEAPON_SEEKER_MURDER_TAG, NO_MSG) \ + MSG_MULTI_NOTIF(1, WEAPON_SEEKER_SUICIDE, NO_MSG, INFO_WEAPON_SEEKER_SUICIDE, CENTER_DEATH_SELF_GENERIC) \ + MSG_MULTI_NOTIF(1, WEAPON_SHOTGUN_MURDER, NO_MSG, INFO_WEAPON_SHOTGUN_MURDER, NO_MSG) \ + MSG_MULTI_NOTIF(1, WEAPON_SHOTGUN_MURDER_SLAP, NO_MSG, INFO_WEAPON_SHOTGUN_MURDER_SLAP, NO_MSG) \ + MSG_MULTI_NOTIF(1, WEAPON_THINKING_WITH_PORTALS, NO_MSG, INFO_WEAPON_THINKING_WITH_PORTALS, CENTER_DEATH_SELF_GENERIC) \ + MSG_MULTI_NOTIF(1, WEAPON_TUBA_MURDER, NO_MSG, INFO_WEAPON_TUBA_MURDER, NO_MSG) \ + MSG_MULTI_NOTIF(1, WEAPON_TUBA_SUICIDE, NO_MSG, INFO_WEAPON_TUBA_SUICIDE, CENTER_DEATH_SELF_GENERIC) \ + MSG_MULTI_NOTIF(1, WEAPON_UZI_MURDER_SNIPE, NO_MSG, INFO_WEAPON_UZI_MURDER_SNIPE, NO_MSG) \ + MSG_MULTI_NOTIF(1, WEAPON_UZI_MURDER_SPRAY, NO_MSG, INFO_WEAPON_UZI_MURDER_SPRAY, NO_MSG) // =========================== @@ -617,6 +693,7 @@ var float autocvar_notification_show_sprees_info_specialonly = TRUE; var float autocvar_notification_errors_are_fatal = TRUE; var float autocvar_notification_lifetime_runtime = 0.5; var float autocvar_notification_lifetime_mapload = 10; +var float autocvar_notification_debug = FALSE; #ifdef SVQC .float FRAG_VERBOSE; @@ -670,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 @@ -721,9 +798,10 @@ string arg_slot[NOTIF_MAX_ARGS]; ARG_CASE(ARG_CS, "f1time", process_time(2, f1)) \ ARG_CASE(ARG_CS_SV, "f1race_time", mmssss(f1)) \ ARG_CASE(ARG_CS_SV, "f2race_time", mmssss(f2)) \ + 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)) \ @@ -767,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 ? @@ -947,11 +1025,13 @@ float notif_error; float notif_global_error; // notification entities +entity msg_annce_notifs[NOTIF_MAX]; entity msg_info_notifs[NOTIF_MAX]; entity msg_center_notifs[NOTIF_MAX]; entity msg_multi_notifs[NOTIF_MAX]; // notification counts +float NOTIF_ANNCE_COUNT; float NOTIF_INFO_COUNT; float NOTIF_CENTER_COUNT; float NOTIF_MULTI_COUNT; @@ -962,8 +1042,13 @@ float NOTIF_CPID_COUNT; .string nent_name; .float nent_id; .float nent_enabled; +.entity nent_msgannce; .entity nent_msginfo; .entity nent_msgcenter; +.float nent_channel; +.string nent_snd; +.float nent_vol; +.float nent_position; .float nent_stringcount; .float nent_floatcount; .string nent_args; @@ -981,6 +1066,40 @@ float NOTIF_CPID_COUNT; .string nent_strings[4]; .float nent_floats[4]; +#define MSG_ANNCE_NOTIF(default,name,channel,sound,volume,position) \ + NOTIF_ADD_AUTOCVAR(name, default) \ + float name; \ + void RegisterNotification_##name() \ + { \ + SET_FIELD_COUNT(name, NOTIF_FIRST, NOTIF_ANNCE_COUNT) \ + CHECK_MAX_COUNT(name, NOTIF_MAX, NOTIF_ANNCE_COUNT, "notifications") \ + Create_Notification_Entity( \ + default, /* var_default */ \ + autocvar_notification_##name, /* var_cvar */ \ + MSG_ANNCE, /* typeid */ \ + name, /* nameid */ \ + strtoupper(#name), /* namestring */ \ + NO_MSG, /* anncename */ \ + NO_MSG, /* infoname */ \ + NO_MSG, /* centername */ \ + channel, /* channel */ \ + sound, /* snd */ \ + volume, /* vol */ \ + position, /* position */ \ + NO_MSG, /* strnum */ \ + NO_MSG, /* flnum */ \ + "", /* args */ \ + "", /* hudargs */ \ + "", /* icon */ \ + NO_MSG, /* cpid */ \ + "", /* durcnt */ \ + "", /* normal */ \ + "", /* gentle */ \ + FALSE, /* msg_is_info */ \ + FALSE); /* msg_is_multi */ \ + } \ + ACCUMULATE_FUNCTION(RegisterNotifications, RegisterNotification_##name) + #define MSG_INFO_NOTIF(default,name,strnum,flnum,args,hudargs,icon,normal,gentle) \ NOTIF_ADD_AUTOCVAR(name, default) \ float name; \ @@ -994,8 +1113,13 @@ float NOTIF_CPID_COUNT; MSG_INFO, /* typeid */ \ name, /* nameid */ \ strtoupper(#name), /* namestring */ \ + NO_MSG, /* anncename */ \ NO_MSG, /* infoname */ \ NO_MSG, /* centername */ \ + NO_MSG, /* channel */ \ + "", /* snd */ \ + NO_MSG, /* vol */ \ + NO_MSG, /* position */ \ strnum, /* strnum */ \ flnum, /* flnum */ \ args, /* args */ \ @@ -1025,8 +1149,13 @@ float NOTIF_CPID_COUNT; MSG_CENTER, /* typeid */ \ name, /* nameid */ \ strtoupper(#name), /* namestring */ \ + NO_MSG, /* anncename */ \ NO_MSG, /* infoname */ \ NO_MSG, /* centername */ \ + NO_MSG, /* channel */ \ + "", /* snd */ \ + NO_MSG, /* vol */ \ + NO_MSG, /* position */ \ strnum, /* strnum */ \ flnum, /* flnum */ \ args, /* args */ \ @@ -1041,7 +1170,7 @@ float NOTIF_CPID_COUNT; } \ ACCUMULATE_FUNCTION(RegisterNotifications, RegisterNotification_##name) -#define MSG_MULTI_NOTIF(default,name,infoname,centername) \ +#define MSG_MULTI_NOTIF(default,name,anncename,infoname,centername) \ NOTIF_ADD_AUTOCVAR(name, default) \ float name; \ void RegisterNotification_##name() \ @@ -1054,8 +1183,13 @@ float NOTIF_CPID_COUNT; MSG_MULTI, /* typeid */ \ name, /* nameid */ \ strtoupper(#name), /* namestring */ \ + anncename, /* anncename */ \ infoname, /* infoname */ \ centername, /* centername */ \ + NO_MSG, /* channel */ \ + "", /* snd */ \ + NO_MSG, /* vol */ \ + NO_MSG, /* position */ \ NO_MSG, /* strnum */ \ NO_MSG, /* flnum */ \ "", /* args */ \ @@ -1112,10 +1246,12 @@ void RegisterNotifications_Done() // NOW we actually activate the declarations ACCUMULATE_FUNCTION(RegisterNotifications, RegisterNotifications_First) +MSG_ANNCE_NOTIFICATIONS MSG_INFO_NOTIFICATIONS MSG_CENTER_NOTIFICATIONS MSG_MULTI_NOTIFICATIONS ACCUMULATE_FUNCTION(RegisterNotifications, RegisterNotifications_Done) +#undef MSG_ANNCE_NOTIF #undef MSG_INFO_NOTIF #undef MSG_CENTER_NOTIF #undef MSG_MULTI_NOTIF diff --git a/qcsrc/common/teams.qh b/qcsrc/common/teams.qh index c9bed09a62..db3a39bcd5 100644 --- a/qcsrc/common/teams.qh +++ b/qcsrc/common/teams.qh @@ -30,6 +30,12 @@ const string NAME_TEAM_4 = _("Pink"); const string NAME_TEAM = _("Team"); const string NAME_NEUTRAL = _("Neutral"); +// used for replacement in filenames or such where the name CANNOT be allowed to be translated +const string STATIC_NAME_TEAM_1 = "Red"; +const string STATIC_NAME_TEAM_2 = "Blue"; +const string STATIC_NAME_TEAM_3 = "Yellow"; +const string STATIC_NAME_TEAM_4 = "Pink"; + #define APP_TEAM_NUM_2(num,prefix) ((num == NUM_TEAM_1) ? prefix##RED : prefix##BLUE) #define APP_TEAM_NUM_4(num,prefix) ((num == NUM_TEAM_1) ? prefix##RED : ((num == NUM_TEAM_2) ? prefix##BLUE : ((num == NUM_TEAM_3) ? prefix##YELLOW : prefix##PINK))) #define APP_TEAM_ENT_2(ent,prefix) ((ent.team == NUM_TEAM_1) ? prefix##RED : prefix##BLUE) @@ -57,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'; @@ -86,6 +85,20 @@ string Team_ColorName(float teamid) return NAME_NEUTRAL; } +// used for replacement in filenames or such where the name CANNOT be allowed to be translated +string Static_Team_ColorName(float teamid) +{ + switch(teamid) + { + case NUM_TEAM_1: return STATIC_NAME_TEAM_1; + case NUM_TEAM_2: return STATIC_NAME_TEAM_2; + case NUM_TEAM_3: return STATIC_NAME_TEAM_3; + case NUM_TEAM_4: return STATIC_NAME_TEAM_4; + } + + return NAME_NEUTRAL; +} + float Team_ColorToTeam(string team_color) { switch(strtolower(team_color)) @@ -135,6 +148,10 @@ float Team_TeamToNumber(float teamid) #define Team_ColorName_Lower(teamid) strtolower(Team_ColorName(teamid)) #define Team_ColorName_Upper(teamid) strtoupper(Team_ColorName(teamid)) +// used for replacement in filenames or such where the name CANNOT be allowed to be translated +#define Static_Team_ColorName_Lower(teamid) strtolower(Static_Team_ColorName(teamid)) +#define Static_Team_ColorName_Upper(teamid) strtoupper(Static_Team_ColorName(teamid)) + #define Team_FullName(teamid) strcat(Team_ColorName(teamid), " ", NAME_TEAM, "^7") #define Team_ColoredFullName(teamid) strcat(Team_ColorCode(teamid), Team_ColorName(teamid), " ", NAME_TEAM, "^7") diff --git a/qcsrc/common/util-pre.qh b/qcsrc/common/util-pre.qh index dffce2f2bb..1c2c1cc9f2 100644 --- a/qcsrc/common/util-pre.qh +++ b/qcsrc/common/util-pre.qh @@ -9,7 +9,7 @@ #ifndef NOCOMPAT //# define WORKAROUND_XON010 //# define COMPAT_XON010_CHANNELS -# define COMPAT_XON050_ENGINE +//# define COMPAT_XON050_ENGINE # define COMPAT_NO_MOD_IS_XONOTIC # define COMPAT_XON060_DONTCRASH_CHECKPVS #endif diff --git a/qcsrc/common/util.qc b/qcsrc/common/util.qc index 9db09a2434..7fa2f576a6 100644 --- a/qcsrc/common/util.qc +++ b/qcsrc/common/util.qc @@ -2586,3 +2586,23 @@ void dedicated_print(string input) // print(), but only print if the server is n if(server_is_dedicated) { print(input); } } #endif + +#ifndef MENUQC +float Announcer_PickNumber(float num) +{ + switch(num) + { + case 10: num = ANNCE_NUM_10; break; + case 9: num = ANNCE_NUM_9; break; + case 8: num = ANNCE_NUM_8; break; + case 7: num = ANNCE_NUM_7; break; + case 6: num = ANNCE_NUM_6; break; + case 5: num = ANNCE_NUM_5; break; + case 4: num = ANNCE_NUM_4; break; + case 3: num = ANNCE_NUM_3; break; + case 2: num = ANNCE_NUM_2; break; + case 1: num = ANNCE_NUM_1; break; + } + return num; +} +#endif diff --git a/qcsrc/common/util.qh b/qcsrc/common/util.qh index bd603b74c2..e9bd01cfa4 100644 --- a/qcsrc/common/util.qh +++ b/qcsrc/common/util.qh @@ -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 @@ -430,3 +402,7 @@ void dedicated_print(string input); #define PROGNAME "CSQC" #endif #endif + +#ifndef MENUQC +float Announcer_PickNumber(float num); +#endif diff --git a/qcsrc/menu/classes.c b/qcsrc/menu/classes.c index 0a3a55c5f1..f00971674e 100644 --- a/qcsrc/menu/classes.c +++ b/qcsrc/menu/classes.c @@ -43,6 +43,7 @@ #include "xonotic/slider_resolution.c" #include "xonotic/checkbox.c" #include "xonotic/checkbox_string.c" +#include "xonotic/weaponarenacheckbox.c" #include "xonotic/radiobutton.c" #include "xonotic/nexposee.c" #include "xonotic/rootdialog.c" diff --git a/qcsrc/menu/item/inputbox.c b/qcsrc/menu/item/inputbox.c index 572d3a0710..614f6d5576 100644 --- a/qcsrc/menu/item/inputbox.c +++ b/qcsrc/menu/item/inputbox.c @@ -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); } diff --git a/qcsrc/menu/item/inputcontainer.c b/qcsrc/menu/item/inputcontainer.c index 65129b294d..4531a1f4e1 100644 --- a/qcsrc/menu/item/inputcontainer.c +++ b/qcsrc/menu/item/inputcontainer.c @@ -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; diff --git a/qcsrc/menu/item/nexposee.c b/qcsrc/menu/item/nexposee.c index 64da6bcd6d..97eabd7d66 100644 --- a/qcsrc/menu/item/nexposee.c +++ b/qcsrc/menu/item/nexposee.c @@ -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; } diff --git a/qcsrc/menu/menu.qc b/qcsrc/menu/menu.qc index ddf9ff686a..2dc73a8d22 100644 --- a/qcsrc/menu/menu.qc +++ b/qcsrc/menu/menu.qc @@ -687,7 +687,7 @@ void m_draw() if(Menu_Active) if(!cvar("menu_video_played")) { - localcmd("cd loop $menu_cdtrack; play sound/announcer/default/welcome.ogg\n"); + localcmd("cd loop $menu_cdtrack; play sound/announcer/default/welcome.wav\n"); menuLogoAlpha = -0.8; // no idea why, but when I start this at zero, it jumps instead of fading FIXME } // ALWAYS set this cvar; if we start but menu is not active, this means we want no background music! diff --git a/qcsrc/menu/skin-customizables.inc b/qcsrc/menu/skin-customizables.inc index d30ac7248d..6b5cac042c 100644 --- a/qcsrc/menu/skin-customizables.inc +++ b/qcsrc/menu/skin-customizables.inc @@ -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); diff --git a/qcsrc/menu/xonotic/charmap.c b/qcsrc/menu/xonotic/charmap.c index 0e375c9cc4..6966aaf481 100644 --- a/qcsrc/menu/xonotic/charmap.c +++ b/qcsrc/menu/xonotic/charmap.c @@ -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; } diff --git a/qcsrc/menu/xonotic/dialog_multiplayer_create_advanced.c b/qcsrc/menu/xonotic/dialog_multiplayer_create_advanced.c index 3fcabaaab6..e6fffe0936 100644 --- a/qcsrc/menu/xonotic/dialog_multiplayer_create_advanced.c +++ b/qcsrc/menu/xonotic/dialog_multiplayer_create_advanced.c @@ -55,7 +55,7 @@ void XonoticAdvancedDialog_fill(entity me) me.TR(me); me.TDempty(me, 0.2); me.TD(me, 1, 1.2, makeXonoticTextLabel(0, _("Teams:"))); - me.TD(me, 1, 1.6, e = makeXonoticTextSlider("g_tdm_teams_override g_domination_teams_override g_keyhunt_teams_override")); + me.TD(me, 1, 1.6, e = makeXonoticTextSlider("g_tdm_teams_override g_domination_teams_override g_ca_teams_override g_freezetag_teams_override g_keyhunt_teams_override")); e.addValue(e, "Default", "0"); e.addValue(e, "2 teams", "2"); e.addValue(e, "3 teams", "3"); diff --git a/qcsrc/menu/xonotic/dialog_multiplayer_create_mutators.c b/qcsrc/menu/xonotic/dialog_multiplayer_create_mutators.c index e4128235f2..40fb4cca5e 100644 --- a/qcsrc/menu/xonotic/dialog_multiplayer_create_mutators.c +++ b/qcsrc/menu/xonotic/dialog_multiplayer_create_mutators.c @@ -102,65 +102,14 @@ string XonoticMutatorsDialog_toString(entity me) s = strcat(s, ", ", _("No powerups")); if(cvar("g_powerups") > 0) s = strcat(s, ", ", _("Powerups")); + if(cvar("g_touchexplode") > 0) + s = strcat(s, ", ", _("Touch explode")); if(s == "") return ZCTX(_("MUT^None")); else return substring(s, 2, strlen(s) - 2); } - - -// WARNING: dirty hack. TODO clean this up by putting this behaviour in extra classes. -void loadCvarsLaserWeaponArenaWeaponButton(entity me) -{ - tokenize_console(cvar_string("g_weaponarena")); - me.checked = (argv(0) == me.cvarValue); -} - -void saveCvarsLaserWeaponArenaWeaponButton(entity me) -{ - string suffix; - - suffix = ""; - if(me.cvarValue != "laser" && me.cvarValue != "most") - if(cvar("menu_weaponarena_with_laser")) - suffix = " laser"; - if(me.checked) - cvar_set("g_weaponarena", strcat(me.cvarValue, suffix)); - else - cvar_set("g_weaponarena", me.cvarOffValue); -} - -.void(entity) draw_weaponarena; -.void(entity) saveCvars_weaponarena; -void saveCvarsLaserWeaponArenaLaserButton(entity me) -{ - // run the old function - me.saveCvars_weaponarena(me); - - me.disabled = ((cvar_string("g_weaponarena") == "0") || (cvar_string("g_weaponarena") == "laser") || (cvar_string("g_weaponarena") == "most")); - - if not(me.disabled) - { - // check for the laser suffix - string s; - s = cvar_string("g_weaponarena"); - if(me.checked && substring(s, strlen(s) - 6, 6) != " laser") - s = strcat(s, " laser"); - else if(!me.checked && substring(s, strlen(s) - 6, 6) == " laser") - s = substring(s, 0, strlen(s) - 6); - cvar_set("g_weaponarena", s); - } -} - -void preDrawLaserWeaponArenaLaserButton(entity me) -{ - me.disabled = ((cvar_string("g_weaponarena") == "0") || (cvar_string("g_weaponarena") == "laser") || (cvar_string("g_weaponarena") == "most")); - // run the old function - me.draw_weaponarena(me); -} -// WARNING: end of dirty hack. Do not try this at home. - float checkCompatibility_pinata(entity me) { if(cvar("g_minstagib")) @@ -187,17 +136,33 @@ float checkCompatibility_newtoys(entity me) return 0; return 1; } +float checkCompatibility_weaponarena_weapon(entity me) +{ + if(cvar("g_minstagib")) + return 0; + if(cvar_string("g_weaponarena") == "most") + return 0; + if(cvar_string("g_weaponarena") == "all") + return 0; + if(cvar_string("g_weaponarena") == "0") + return 0; + if(cvar_string("g_start_weapon_laser") == "0") + return 0; + return 1; +} void XonoticMutatorsDialog_fill(entity me) { entity e, s, w; float i, j; - string str, hstr; me.TR(me); me.TD(me, 1, 2, makeXonoticTextLabel(0, _("Gameplay mutators:"))); me.TR(me); me.TDempty(me, 0.2); me.TD(me, 1, 1.8, e = makeXonoticCheckBox(0, "g_dodging", _("Dodging"))); + me.TR(me); + me.TDempty(me, 0.2); + me.TD(me, 1, 1.8, e = makeXonoticCheckBox(0, "g_touchexplode", _("Touch explode"))); me.TR(me); me.TDempty(me, 0.2); me.TD(me, 1, 1.8, e = makeXonoticCheckBox(0, "g_cloaked", _("Cloaked"))); @@ -254,10 +219,11 @@ void XonoticMutatorsDialog_fill(entity me) me.TR(me); me.gotoRC(me, 0, 2); me.setFirstColumn(me, me.currentColumn); - me.TD(me, 1, 4, makeXonoticTextLabel(0, _("Weapon arenas:"))); + me.TD(me, 1, 2, e = makeXonoticRadioButton(1, string_null, string_null, _("Regular (no arena)"))); me.TR(me); - me.TDempty(me, 0.2); - me.TD(me, 1, 1.8, e = makeXonoticRadioButton(1, string_null, string_null, _("Regular (no arena)"))); + me.TD(me, 1, 2, e = makeXonoticRadioButton(1, "g_weaponarena", "menu_weaponarena", _("Weapon arenas:"))); + e.getCvarValueFromCvar = TRUE; + e.cvarOffValue = "0"; for(i = WEP_FIRST, j = 0; i <= WEP_LAST; ++i) { w = get_weaponinfo(i); @@ -265,47 +231,38 @@ void XonoticMutatorsDialog_fill(entity me) continue; if(j & 1 == 0) me.TR(me); - str = w.netname; - hstr = w.message; me.TDempty(me, 0.2); - me.TD(me, 1, 1.8, e = makeXonoticRadioButton(1, "g_weaponarena", strzone(str), strzone(hstr))); - e.cvarOffValue = "0"; - // custom load/save logic that ignores a " laser" suffix, or adds it - e.loadCvars = loadCvarsLaserWeaponArenaWeaponButton; - e.saveCvars = saveCvarsLaserWeaponArenaWeaponButton; - e.loadCvars(e); + me.TD(me, 1, 1.8, e = makeXonoticWeaponarenaCheckBox(strzone(w.netname), strzone(w.message))); + setDependentWeird(e, checkCompatibility_weaponarena_weapon); ++j; } me.TR(me); me.TDempty(me, 0.2); - me.TD(me, 1, 1.8, e = makeXonoticCheckBox(0, "menu_weaponarena_with_laser", _("with laser"))); - // hook the draw function to gray it out - e.draw_weaponarena = e.draw; - e.draw = preDrawLaserWeaponArenaLaserButton; - // hook the save function to notify about the cvar - e.saveCvars_weaponarena = e.saveCvars; - e.saveCvars = saveCvarsLaserWeaponArenaLaserButton; + me.TD(me, 1, 1.8, e = makeXonoticRadioButton(1, "g_weaponarena", "most", _("Most weapons"))); + e.cvarOffValue = "0"; + me.TR(me); + me.TDempty(me, 0.2); + me.TD(me, 1, 1.8, e = makeXonoticRadioButton(1, "g_weaponarena", "all", _("All weapons"))); + e.cvarOffValue = "0"; me.TR(me); me.TD(me, 1, 4, makeXonoticTextLabel(0, _("Special arenas:"))); me.TR(me); me.TDempty(me, 0.2); - me.TD(me, 1, 1.8, e = makeXonoticRadioButton(1, "g_minstagib", string_null, _("MinstaGib"))); + me.TD(me, 1, 1.8, e = makeXonoticRadioButton(1, "g_minstagib", "1", _("MinstaGib"))); + e.cvarOffValue = "0"; me.TR(me); me.TDempty(me, 0.2); - me.TD(me, 1, 1.8, e = makeXonoticRadioButton(1, "g_nix", string_null, _("NIX"))); + me.TD(me, 1, 1.8, e = makeXonoticRadioButton(1, "g_nix", "1", _("NIX"))); + e.cvarOffValue = "0"; me.TR(me); me.TDempty(me, 0.4); me.TD(me, 1, 1.6, e = makeXonoticCheckBox(0, "g_nix_with_laser", _("with laser"))); setDependent(e, "g_nix", 1, 1); - me.TR(me); - me.TDempty(me, 0.2); - me.TD(me, 1, 1.8, e = makeXonoticRadioButton(1, "g_weaponarena", "most", _("Most weapons"))); - e.cvarOffValue = "0"; me.TR(me); me.TDempty(me, 0.2); me.TD(me, 1, 1.8, e = makeXonoticRadioButton(1, "g_start_weapon_laser", "0", _("No start weapons"))); e.cvarOffValue = "-1"; - makeMulti(e, "g_start_weapon_shotgun g_start_weapon_uzi g_start_weapon_grenadelauncher g_start_weapon_minelayer g_start_weapon_electro g_start_weapon_crylink g_start_weapon_nex g_start_weapon_hagar g_start_weapon_rocketlauncher g_start_weapon_rifle g_start_weapon_hlac g_start_weapon_seeker g_start_weapon_minstanex g_start_weapon_hook g_start_weapon_porto g_start_weapon_tuba g_start_weapon_minelayer"); + makeMulti(e, "g_start_weapon_shotgun g_start_weapon_uzi g_start_weapon_grenadelauncher g_start_weapon_minelayer g_start_weapon_electro g_start_weapon_crylink g_start_weapon_nex g_start_weapon_hagar g_start_weapon_rocketlauncher g_start_weapon_porto g_start_weapon_minstanex g_start_weapon_hook g_start_weapon_hlac g_start_weapon_rifle g_start_weapon_fireball g_start_weapon_seeker g_start_weapon_tuba"); me.gotoRC(me, me.rows - 1, 0); me.TD(me, 1, me.columns, e = makeXonoticButton(_("OK"), '0 0 0')); diff --git a/qcsrc/menu/xonotic/dialog_multiplayer_demo.c b/qcsrc/menu/xonotic/dialog_multiplayer_demo.c index bb4b969c4c..84115f1ed6 100644 --- a/qcsrc/menu/xonotic/dialog_multiplayer_demo.c +++ b/qcsrc/menu/xonotic/dialog_multiplayer_demo.c @@ -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); diff --git a/qcsrc/menu/xonotic/dialog_multiplayer_join.c b/qcsrc/menu/xonotic/dialog_multiplayer_join.c index ee451744fb..02d3b41020 100644 --- a/qcsrc/menu/xonotic/dialog_multiplayer_join.c +++ b/qcsrc/menu/xonotic/dialog_multiplayer_join.c @@ -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; diff --git a/qcsrc/menu/xonotic/dialog_multiplayer_playersetup.c b/qcsrc/menu/xonotic/dialog_multiplayer_playersetup.c index ef930b4e14..21beeca6f4 100644 --- a/qcsrc/menu/xonotic/dialog_multiplayer_playersetup.c +++ b/qcsrc/menu/xonotic/dialog_multiplayer_playersetup.c @@ -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)); diff --git a/qcsrc/menu/xonotic/dialog_settings_misc.c b/qcsrc/menu/xonotic/dialog_settings_misc.c index b9aab55ab7..37effe9325 100644 --- a/qcsrc/menu/xonotic/dialog_settings_misc.c +++ b/qcsrc/menu/xonotic/dialog_settings_misc.c @@ -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); diff --git a/qcsrc/menu/xonotic/dialog_settings_misc_cvars.c b/qcsrc/menu/xonotic/dialog_settings_misc_cvars.c index 7ebca4162c..5db6d35c62 100644 --- a/qcsrc/menu/xonotic/dialog_settings_misc_cvars.c +++ b/qcsrc/menu/xonotic/dialog_settings_misc_cvars.c @@ -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; diff --git a/qcsrc/menu/xonotic/inputbox.c b/qcsrc/menu/xonotic/inputbox.c index dbbcc7b0c4..56aa9702c6 100644 --- a/qcsrc/menu/xonotic/inputbox.c +++ b/qcsrc/menu/xonotic/inputbox.c @@ -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/menu/xonotic/radiobutton.c b/qcsrc/menu/xonotic/radiobutton.c index 8c353eae67..4ac5aa7b18 100644 --- a/qcsrc/menu/xonotic/radiobutton.c +++ b/qcsrc/menu/xonotic/radiobutton.c @@ -13,6 +13,7 @@ CLASS(XonoticRadioButton) EXTENDS(RadioButton) ATTRIB(XonoticRadioButton, cvarName, string, string_null) ATTRIB(XonoticRadioButton, cvarValue, string, string_null) ATTRIB(XonoticRadioButton, cvarOffValue, string, string_null) + ATTRIB(XonoticRadioButton, getCvarValueFromCvar, float, 0) METHOD(XonoticRadioButton, loadCvars, void(entity)) METHOD(XonoticRadioButton, saveCvars, void(entity)) @@ -98,7 +99,12 @@ void XonoticRadioButton_saveCvars(entity me) if(me.cvarName) { if(me.checked) - cvar_set(me.cvarName, me.cvarValue); + { + if(me.getCvarValueFromCvar) + cvar_set(me.cvarName, cvar_string(me.cvarValue)); + else + cvar_set(me.cvarName, me.cvarValue); + } else if(me.cvarOffValue) cvar_set(me.cvarName, me.cvarOffValue); } diff --git a/qcsrc/menu/xonotic/weaponarenacheckbox.c b/qcsrc/menu/xonotic/weaponarenacheckbox.c new file mode 100644 index 0000000000..163f9c63b4 --- /dev/null +++ b/qcsrc/menu/xonotic/weaponarenacheckbox.c @@ -0,0 +1,57 @@ +#ifdef INTERFACE +CLASS(XonoticWeaponarenaCheckBox) EXTENDS(CheckBox) + METHOD(XonoticWeaponarenaCheckBox, configureXonoticWeaponarenaCheckBox, void(entity, string, string)) + METHOD(XonoticWeaponarenaCheckBox, setChecked, void(entity, float)) + ATTRIB(XonoticWeaponarenaCheckBox, fontSize, float, SKINFONTSIZE_NORMAL) + ATTRIB(XonoticWeaponarenaCheckBox, image, string, SKINGFX_CHECKBOX) + ATTRIB(XonoticWeaponarenaCheckBox, netname, string, string_null) + + METHOD(XonoticWeaponarenaCheckBox, loadCvars, void(entity)) + METHOD(XonoticWeaponarenaCheckBox, saveCvars, void(entity)) +ENDCLASS(XonoticWeaponarenaCheckBox) +entity makeXonoticWeaponarenaCheckBox(string, string); +#endif + +#ifdef IMPLEMENTATION +entity makeXonoticWeaponarenaCheckBox(string theWeapon, string theText) +{ + entity me; + me = spawnXonoticWeaponarenaCheckBox(); + me.configureXonoticWeaponarenaCheckBox(me, theWeapon, theText); + return me; +} +void XonoticWeaponarenaCheckBox_configureXonoticWeaponarenaCheckBox(entity me, string theWeapon, string theText) +{ + me.netname = theWeapon; + me.checked = FALSE; + me.loadCvars(me); + me.configureCheckBox(me, theText, me.fontSize, me.image); +} +void XonoticWeaponarenaCheckBox_setChecked(entity me, float foo) +{ + me.checked = !me.checked; + me.saveCvars(me); +} +void XonoticWeaponarenaCheckBox_loadCvars(entity me) +{ + float n = tokenize_console(cvar_string("menu_weaponarena")); + float i; + for(i=0; i 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) - Announce("prepareforbattle"); - else if(f == 3) - Announce("3"); - else if(f == 2) - Announce("2"); - else if(f == 1) - Announce("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 { - Announce("begin"); - Send_Notification(NOTIF_ALL, world, MSG_CENTER, CENTER_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 index 7a5662c979..0000000000 --- a/qcsrc/server/assault.qc +++ /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("", 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 index 0000000000..7a5662c979 --- /dev/null +++ b/qcsrc/server/attic/assault.qc @@ -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("", 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 index 0000000000..4456802d5c --- /dev/null +++ b/qcsrc/server/attic/bot/havocbot/role_assault.qc @@ -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 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); +} diff --git a/qcsrc/server/autocvars.qh b/qcsrc/server/autocvars.qh index 1febc23d26..efb9f8c17a 100644 --- a/qcsrc/server/autocvars.qh +++ b/qcsrc/server/autocvars.qh @@ -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; @@ -1222,3 +1228,7 @@ float autocvar_physics_ode; float autocvar_g_physical_items; float autocvar_g_physical_items_damageforcescale; float autocvar_g_physical_items_reset; +float autocvar_g_touchexplode_radius; +float autocvar_g_touchexplode_damage; +float autocvar_g_touchexplode_edgedamage; +float autocvar_g_touchexplode_force; diff --git a/qcsrc/server/bot/aim.qc b/qcsrc/server/bot/aim.qc index 3bff21ecf0..cb42aa5c2c 100644 --- a/qcsrc/server/bot/aim.qc +++ b/qcsrc/server/bot/aim.qc @@ -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. diff --git a/qcsrc/server/bot/bot.qc b/qcsrc/server/bot/bot.qc index d87c72695f..f6e7f6f1b6 100644 --- a/qcsrc/server/bot/bot.qc +++ b/qcsrc/server/bot/bot.qc @@ -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; } diff --git a/qcsrc/server/bot/havocbot/havocbot.qc b/qcsrc/server/bot/havocbot/havocbot.qc index 1b9178b177..0b8bac7333 100644 --- a/qcsrc/server/bot/havocbot/havocbot.qc +++ b/qcsrc/server/bot/havocbot/havocbot.qc @@ -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 index 4456802d5c..0000000000 --- a/qcsrc/server/bot/havocbot/role_assault.qc +++ /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 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); -} diff --git a/qcsrc/server/bot/havocbot/roles.qc b/qcsrc/server/bot/havocbot/roles.qc index a3078c4e43..a54874786c 100644 --- a/qcsrc/server/bot/havocbot/roles.qc +++ b/qcsrc/server/bot/havocbot/roles.qc @@ -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(); } diff --git a/qcsrc/server/cl_client.qc b/qcsrc/server/cl_client.qc index 3f6006838e..ca19ed7464 100644 --- a/qcsrc/server/cl_client.qc +++ b/qcsrc/server/cl_client.qc @@ -6,22 +6,6 @@ void send_CSQC_teamnagger() { WriteByte(MSG_BROADCAST, TE_CSQC_TEAMNAGGER); } -void Announce(string snd) { - WriteByte(MSG_BROADCAST, SVC_TEMPENTITY); - WriteByte(MSG_BROADCAST, TE_CSQC_ANNOUNCE); - WriteString(MSG_BROADCAST, snd); -} - -void AnnounceTo(entity e, string snd) { - if (clienttype(e) == CLIENTTYPE_REAL) - { - msg_entity = e; - WriteByte(MSG_ONE, SVC_TEMPENTITY); - WriteByte(MSG_ONE, TE_CSQC_ANNOUNCE); - WriteString(MSG_ONE, snd); - } -} - float ClientData_Send(entity to, float sf) { if(to != self.owner) @@ -293,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; @@ -401,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); @@ -422,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); @@ -459,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; @@ -504,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; @@ -627,21 +588,6 @@ void FixPlayermodel() setcolor(self, stof(autocvar_sv_defaultplayercolors)); } -void PlayerTouchExplode(entity p1, entity p2) -{ - vector org; - org = (p1.origin + p2.origin) * 0.5; - org_z += (p1.mins_z + p2.mins_z) * 0.5; - - te_explosion(org); - - entity e; - e = spawn(); - setorigin(e, org); - RadiusDamage(e, world, g_touchexplode_damage, g_touchexplode_edgedamage, g_touchexplode_radius, world, g_touchexplode_force, DEATH_TOUCHEXPLODE, world); - remove(e); -} - /* ============= PutClientInServer @@ -653,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; @@ -676,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; @@ -788,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; @@ -840,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; @@ -873,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 @@ -1078,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() @@ -1147,7 +1074,7 @@ void KillIndicator_Think() if(clienttype(self.owner) == CLIENTTYPE_REAL) { if(self.cnt <= 10) - AnnounceTo(self.owner, strcat(ftos(self.cnt), "")); + { Send_Notification(NOTIF_ONE, self.owner, MSG_ANNCE, Announcer_PickNumber(self.cnt)); } } self.nextthink = time + 1; self.cnt -= 1; @@ -1261,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 @@ -1433,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) @@ -1503,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(); @@ -1525,17 +1437,14 @@ void ClientConnect (void) if(clienttype(self) == CLIENTTYPE_REAL) { - if(autocvar_g_bugrigs || WEPSET_EQ_AW(g_weaponarena_weapons, WEP_TUBA)) - stuffcmd(self, "cl_cmd settemp chase_active 1\n"); - } - - if(g_lms) - { - if(PlayerScore_Add(self, SP_LMS_LIVES, LMS_NewPlayerLives()) <= 0) + if(!autocvar_g_campaign) { - PlayerScore_Add(self, SP_LMS_RANK, 666); - self.frags = FRAGS_SPECTATOR; + self.motd_actived_time = -1; + Send_Notification(NOTIF_ONE_ONLY, self, MSG_CENTER, CENTER_MOTD, getwelcomemessage()); } + + if(autocvar_g_bugrigs || WEPSET_EQ_AW(g_weaponarena_weapons, WEP_TUBA)) + stuffcmd(self, "cl_cmd settemp chase_active 1\n"); } if(!sv_foginterval && world.fog != "") @@ -1574,19 +1483,14 @@ void ClientConnect (void) CheatInitClient(); - if(!autocvar_g_campaign) - Send_Notification(NOTIF_ONE_ONLY, self, MSG_CENTER, CENTER_MOTD, getwelcomemessage()); - 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); } /* ============= @@ -1653,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); @@ -2016,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)) @@ -2141,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); @@ -2333,13 +2230,15 @@ void ShowRespawnCountdown() { self.respawn_countdown = number - 1; if(ceil(self.respawn_time - (time + 0.5)) == number) // only say it if it is the same number even in 0.5s; to prevent overlapping sounds - AnnounceTo(self, strcat(ftos(number), "")); + Send_Notification(NOTIF_ONE, self, MSG_ANNCE, Announcer_PickNumber(number)); } } } 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) @@ -2351,21 +2250,20 @@ void LeaveSpectatorMode() if(autocvar_g_campaign) { campaign_bots_may_start = 1; } - else - { Kill_Notification(NOTIF_ONE_ONLY, self, MSG_CENTER_CPID, CPID_MOTD); } Kill_Notification(NOTIF_ONE_ONLY, self, MSG_CENTER_CPID, CPID_PREVENT_JOIN); - + PutClientInServer(); 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 { // Player may not join because g_maxplayers is set - Send_Notification(NOTIF_ONE_ONLY, self, MSG_CENTER_CPID, CPID_PREVENT_JOIN); + Send_Notification(NOTIF_ONE_ONLY, self, MSG_CENTER, CENTER_JOIN_PREVENT); } } @@ -2400,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); @@ -2422,22 +2321,24 @@ void checkSpectatorBlock() { } } -.float motd_actived_time; // used for both motd and campaign_message void PrintWelcomeMessage() { - if (self.motd_actived_time == 0) { // is there already a message showing? + if(self.motd_actived_time == 0) + { if (autocvar_g_campaign) { if ((self.classname == "player" && self.BUTTON_INFO) || (self.classname != "player")) { self.motd_actived_time = time; Send_Notification(NOTIF_ONE_ONLY, self, MSG_CENTER, CENTER_MOTD, campaign_message); } } else { - if ((time - self.jointime > autocvar_welcome_message_time) && self.BUTTON_INFO) { + if (self.BUTTON_INFO) { self.motd_actived_time = time; Send_Notification(NOTIF_ONE_ONLY, self, MSG_CENTER, CENTER_MOTD, getwelcomemessage()); } } - } else { // showing MOTD or campaign message + } + else if(self.motd_actived_time > 0) // showing MOTD or campaign message + { if (autocvar_g_campaign) { if (self.BUTTON_INFO) self.motd_actived_time = time; @@ -2446,16 +2347,25 @@ void PrintWelcomeMessage() Kill_Notification(NOTIF_ONE_ONLY, self, MSG_CENTER_CPID, CPID_MOTD); } } else { - if ((time - self.jointime) > autocvar_welcome_message_time) { - if (self.BUTTON_INFO) - self.motd_actived_time = time; - else if (time - self.motd_actived_time > 2) { // hide it some seconds after BUTTON_INFO has been released - self.motd_actived_time = 0; - Kill_Notification(NOTIF_ONE_ONLY, self, MSG_CENTER_CPID, CPID_MOTD); - } + if (self.BUTTON_INFO) + self.motd_actived_time = time; + else if (time - self.motd_actived_time > 2) { // hide it some seconds after BUTTON_INFO has been released + self.motd_actived_time = 0; + Kill_Notification(NOTIF_ONE_ONLY, self, MSG_CENTER_CPID, CPID_MOTD); } } } + else //if(self.motd_actived_time < 0) // just connected, motd is active + { + if(self.BUTTON_INFO) // BUTTON_INFO hides initial MOTD + self.motd_actived_time = -2; // wait until BUTTON_INFO gets released + else if(self.motd_actived_time == -2 || IS_PLAYER(self) || time - self.jointime > autocvar_welcome_message_time) + { + // instanctly hide MOTD + self.motd_actived_time = 0; + Kill_Notification(NOTIF_ONE_ONLY, self, MSG_CENTER_CPID, CPID_MOTD); + } + } } void ObserverThink() @@ -2486,8 +2396,6 @@ void ObserverThink() } } } - - PrintWelcomeMessage(); } void SpectatorThink() @@ -2536,7 +2444,6 @@ void SpectatorThink() PutObserverInServer(); } - PrintWelcomeMessage(); self.flags |= FL_CLIENT | FL_NOTARGET; } @@ -2555,8 +2462,6 @@ void PlayerUseKey() MUTATOR_CALLHOOK(PlayerUseKey); } -.float touchexplode_time; - /* ============= PlayerPreThink @@ -2572,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 @@ -2657,11 +2558,10 @@ void PlayerPreThink (void) self.usekeypressed = self.BUTTON_USE; } - PrintWelcomeMessage(); + if(clienttype(self) == CLIENTTYPE_REAL) + PrintWelcomeMessage(); if(self.classname == "player") { -// if(self.netname == "Wazat") -// bprint(self.classname, "\n"); CheckRules_Player(); @@ -2705,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; @@ -2746,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 @@ -2755,55 +2662,6 @@ void PlayerPreThink (void) return; } - // FIXME from now on self.deadflag is always 0 (and self.health is never < 1) - // so (self.deadflag == DEAD_NO) is always true in the code below - - if(g_touchexplode) - if(time > self.touchexplode_time) - if(self.classname == "player") - if(self.deadflag == DEAD_NO) - if not(IS_INDEPENDENT_PLAYER(self)) - FOR_EACH_PLAYER(other) if(self != other) - { - if(time > other.touchexplode_time) - if(other.deadflag == DEAD_NO) - if not(IS_INDEPENDENT_PLAYER(other)) - if(boxesoverlap(self.absmin, self.absmax, other.absmin, other.absmax)) - { - PlayerTouchExplode(self, other); - self.touchexplode_time = other.touchexplode_time = time + 0.2; - } - } - - 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 :( - 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; @@ -3014,7 +2872,7 @@ void PlayerPostThink (void) else if(timeleft <= 10) { if(timeleft != self.idlekick_lasttimeleft) - AnnounceTo(self, ftos(timeleft)); + Send_Notification(NOTIF_ONE, self, MSG_ANNCE, Announcer_PickNumber(timeleft)); self.idlekick_lasttimeleft = timeleft; } } diff --git a/qcsrc/server/cl_impulse.qc b/qcsrc/server/cl_impulse.qc index 529567a3b4..d09c0704fe 100644 --- a/qcsrc/server/cl_impulse.qc +++ b/qcsrc/server/cl_impulse.qc @@ -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; diff --git a/qcsrc/server/cl_physics.qc b/qcsrc/server/cl_physics.qc index 6ddf7b69ed..a318b8e3a4 100644 --- a/qcsrc/server/cl_physics.qc +++ b/qcsrc/server/cl_physics.qc @@ -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; } @@ -358,7 +352,7 @@ void RaceCarPhysics() rigvel_z -= frametime * autocvar_sv_gravity; // 4x gravity plays better rigvel_xy = vec2(rigvel); - if(g_bugrigs_planar_movement_car_jumping && !g_touchexplode) // touchexplode is a better way to handle collisions + if(g_bugrigs_planar_movement_car_jumping) mt = MOVE_NORMAL; else mt = MOVE_NOMONSTERS; diff --git a/qcsrc/server/cl_player.qc b/qcsrc/server/cl_player.qc index 944db7e5b8..95aeced31b 100644 --- a/qcsrc/server/cl_player.qc +++ b/qcsrc/server/cl_player.qc @@ -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); diff --git a/qcsrc/server/cl_weapons.qc b/qcsrc/server/cl_weapons.qc index 2a1297dc2b..0014e9182d 100644 --- a/qcsrc/server/cl_weapons.qc +++ b/qcsrc/server/cl_weapons.qc @@ -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; diff --git a/qcsrc/server/command/cmd.qc b/qcsrc/server/command/cmd.qc index 6c1e0ed56f..e22399f4ac 100644 --- a/qcsrc/server/command/cmd.qc +++ b/qcsrc/server/command/cmd.qc @@ -154,12 +154,10 @@ 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_MOTD); Kill_Notification(NOTIF_ONE_ONLY, self, MSG_CENTER_CPID, CPID_PREVENT_JOIN); Send_Notification(NOTIF_ALL, world, MSG_INFO, INFO_JOIN_PLAY, self.netname); PutClientInServer(); @@ -167,7 +165,7 @@ void ClientCommand_join(float request) else { //player may not join because of g_maxplayers is set - Send_Notification(NOTIF_ONE_ONLY, self, MSG_CENTER_CPID, CPID_PREVENT_JOIN); + Send_Notification(NOTIF_ONE_ONLY, self, MSG_CENTER, CENTER_JOIN_PREVENT); } } } @@ -196,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; @@ -414,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; diff --git a/qcsrc/server/command/common.qc b/qcsrc/server/command/common.qc index e95cf4c658..132a0af70e 100644 --- a/qcsrc/server/command/common.qc +++ b/qcsrc/server/command/common.qc @@ -182,7 +182,7 @@ void timeout_handler_think() Send_Notification(NOTIF_ALL, world, MSG_CENTER, CENTER_TIMEOUT_ENDING, timeout_time); if(timeout_time == autocvar_sv_timeout_resumetime) // play a warning sound when only seconds are left - Announce("prepareforbattle"); + Send_Notification(NOTIF_ALL, world, MSG_ANNCE, ANNCE_PREPARE); self.nextthink = time + TIMEOUT_SLOWMO_VALUE; // think again in one second timeout_time -= 1; // decrease the time counter @@ -575,7 +575,7 @@ void CommonCommand_timeout(float request, entity caller) // DEAR GOD THIS COMMAN timeout_handler.think = timeout_handler_think; timeout_handler.nextthink = time; // always let the entity think asap - Announce("timeoutcalled"); + Send_Notification(NOTIF_ALL, world, MSG_ANNCE, ANNCE_TIMEOUT); } } else { print_to(caller, "^1Timeouts are not allowed to be called, enable them with sv_timeout 1.\n"); } diff --git a/qcsrc/server/command/vote.qc b/qcsrc/server/command/vote.qc index 329fb9d4ef..3e564e39ae 100644 --- a/qcsrc/server/command/vote.qc +++ b/qcsrc/server/command/vote.qc @@ -148,21 +148,21 @@ void VoteAccept() if(vote_caller) { vote_caller.vote_waittime = 0; } // people like your votes, you don't need to wait to vote again VoteReset(); - Announce("voteaccept"); + Send_Notification(NOTIF_ALL, world, MSG_ANNCE, ANNCE_VOTE_ACCEPT); } void VoteReject() { bprint("\{1}^2* ^3", GetCallerName(vote_caller), "^2's vote for ", vote_called_display, "^2 was rejected\n"); VoteReset(); - Announce("votefail"); + Send_Notification(NOTIF_ALL, world, MSG_ANNCE, ANNCE_VOTE_FAIL); } void VoteTimeout() { bprint("\{1}^2* ^3", GetCallerName(vote_caller), "^2's vote for ", vote_called_display, "^2 timed out\n"); VoteReset(); - Announce("votefail"); + Send_Notification(NOTIF_ALL, world, MSG_ANNCE, ANNCE_VOTE_FAIL); } void VoteSpam(float notvoters, float mincount, string result) @@ -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,13 +423,13 @@ 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 alivetime + // clear player attributes FOR_EACH_CLIENT(tmp_player) { tmp_player.alivetime = 0; + tmp_player.killcount = 0; PlayerStats_Event(tmp_player, PLAYERSTATS_ALIVETIME, -PlayerStats_Event(tmp_player, PLAYERSTATS_ALIVETIME, 0)); } @@ -361,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; @@ -413,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; @@ -610,7 +691,7 @@ float VoteCommand_parse(entity caller, string vote_command, string vote_list, fl if(accepted > 0) { - string reason = ((argc > next_token) ? substring(vote_command, argv_start_index(next_token), argv_end_index(-1) - argv_start_index(next_token)) : "No reason provided"); + string reason = ((argc > next_token) ? substring(vote_command, argv_start_index(next_token), strlen(vote_command) - argv_start_index(next_token)) : "No reason provided"); string command_arguments; if(first_command == "kickban") @@ -725,7 +806,7 @@ void VoteCommand_call(float request, entity caller, float argc, string vote_comm } FOR_EACH_REALCLIENT(tmp_player) { ++tmp_playercount; } - if(tmp_playercount > 1) { Announce("votecall"); } // don't announce a "vote now" sound if player is alone + if(tmp_playercount > 1) { Send_Notification(NOTIF_ALL, world, MSG_ANNCE, ANNCE_VOTE_CALL); } // don't announce a "vote now" sound if player is alone bprint("\{1}^2* ^3", GetCallerName(vote_caller), "^2 calls a vote for ", vote_called_display, "\n"); if(autocvar_sv_eventlog) { GameLogEcho(strcat(":vote:vcall:", ftos(vote_caller.playerid), ":", vote_called_display)); } diff --git a/qcsrc/server/command/vote.qh b/qcsrc/server/command/vote.qh index 748b7ce6d9..1225b6be58 100644 --- a/qcsrc/server/command/vote.qh +++ b/qcsrc/server/command/vote.qh @@ -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 diff --git a/qcsrc/server/constants.qh b/qcsrc/server/constants.qh index 522bb2ffca..cb3c34fdc4 100644 --- a/qcsrc/server/constants.qh +++ b/qcsrc/server/constants.qh @@ -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; diff --git a/qcsrc/server/defs.qh b/qcsrc/server/defs.qh index 668433ce00..fb93b7c787 100644 --- a/qcsrc/server/defs.qh +++ b/qcsrc/server/defs.qh @@ -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; @@ -239,9 +237,9 @@ float game_completion_ratio; // 0 at start, 1 near end .float winning; .float jointime; // time of joining .float alivetime; // time of being alive +.float motd_actived_time; // used for both motd and campaign_message float nJoinAllowed(entity ignore); -#define PREVENT_JOIN_TEXT "^1You may not join the game at this time.\n\nThe player limit reached maximum capacity." .float spawnshieldtime; @@ -281,9 +279,6 @@ float default_weapon_alpha; .float cvar_cl_allow_uidtracking; .string stored_netname; -void Announce(string snd); -void AnnounceTo(entity e, string snd); - .float version_nagtime; #define NUM_JUMPPADSUSED 3 @@ -449,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; @@ -582,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 @@ -591,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. diff --git a/qcsrc/server/g_damage.qc b/qcsrc/server/g_damage.qc index 29eed9af8c..6687f18ebc 100644 --- a/qcsrc/server/g_damage.qc +++ b/qcsrc/server/g_damage.qc @@ -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; @@ -376,7 +354,7 @@ void Obituary(entity attacker, entity inflictor, entity targ, float deathtype) string deathlocation = (autocvar_notification_server_allows_location ? NearestLocation(targ.death_origin) : ""); #ifdef NOTIFICATIONS_DEBUG - dprint( + Debug_Notification( sprintf( "Obituary(%s, %s, %s, %s = %d);\n", attacker.netname, @@ -440,7 +418,7 @@ void Obituary(entity attacker, entity inflictor, entity targ, float deathtype) Send_Notification(NOTIF_ONE, attacker, MSG_CENTER, CENTER_DEATH_TEAMKILL_FRAG, targ.netname); Send_Notification(NOTIF_ONE, targ, MSG_CENTER, CENTER_DEATH_TEAMKILL_FRAGGED, attacker.netname); - Send_Notification(NOTIF_ALL, world, MSG_INFO, APP_TEAM_NUM_4(targ.team, INFO_DEATH_TEAMKILL_), targ.netname, attacker.netname, targ.killcount); + Send_Notification(NOTIF_ALL, world, MSG_INFO, APP_TEAM_NUM_4(targ.team, INFO_DEATH_TEAMKILL_), targ.netname, attacker.netname, deathlocation, targ.killcount); // In this case, the death message will ALWAYS be "foo was betrayed by bar" // No need for specific death/weapon messages... @@ -456,7 +434,7 @@ void Obituary(entity attacker, entity inflictor, entity targ, float deathtype) #define SPREE_ITEM(counta,countb,center,normal,gentle) \ case counta: \ { \ - AnnounceTo(attacker, strcat(#countb, "kills")); \ + Send_Notification(NOTIF_ONE, attacker, MSG_ANNCE, ANNCE_KILLSTREAK_##countb); \ PlayerStats_Event(attacker, PLAYERSTATS_ACHIEVEMENT_KILL_SPREE_##counta, 1); \ break; \ } @@ -557,7 +535,7 @@ void Obituary(entity attacker, entity inflictor, entity targ, float deathtype) if(PlayerScore_Add(targ, SP_SCORE, 0) == -5) { - AnnounceTo(targ, "botlike"); + Send_Notification(NOTIF_ONE, targ, MSG_ANNCE, ANNCE_ACHIEVEMENT_BOTLIKE); PlayerStats_Event(attacker, PLAYERSTATS_ACHIEVEMENT_BOTLIKE, 1); } } @@ -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) { diff --git a/qcsrc/server/g_hook.qc b/qcsrc/server/g_hook.qc index f7af47d924..dec6e167d1 100644 --- a/qcsrc/server/g_hook.qc +++ b/qcsrc/server/g_hook.qc @@ -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); diff --git a/qcsrc/server/g_world.qc b/qcsrc/server/g_world.qc index a9b9585689..1af52b1320 100644 --- a/qcsrc/server/g_world.qc +++ b/qcsrc/server/g_world.qc @@ -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); @@ -1350,7 +1339,7 @@ void IntermissionThink() && ((self.autoscreenshot > 0) && (time > self.autoscreenshot)) ) { self.autoscreenshot = -1; - if(clienttype(self) == CLIENTTYPE_REAL) { stuffcmd(self, sprintf("\nscreenshot screenshots/autoscreenshot/%s-%s.jpg; echo \"^5A screenshot has been taken at request of the server.\"", GetMapname(), strftime(FALSE, "%s"))); } + if(clienttype(self) == CLIENTTYPE_REAL) { stuffcmd(self, sprintf("\nscreenshot screenshots/autoscreenshot/%s-%s.jpg; echo \"^5A screenshot has been taken at request of the server.\"\n", GetMapname(), strftime(FALSE, "%s"))); } return; } @@ -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:"); @@ -1592,7 +1581,7 @@ void NextLevel() PlayerStats_AddGlobalInfo(e); PlayerStats_Shutdown(); WeaponStats_Shutdown(); - + Kill_Notification(NOTIF_ALL, world, MSG_CENTER, 0); // kill all centerprints now if(autocvar_sv_eventlog) @@ -1678,7 +1667,7 @@ void InitiateOvertime() // ONLY call this if InitiateSuddenDeath returned true tl += autocvar_timelimit_overtime; cvar_set("timelimit", ftos(tl)); - Send_Notification(NOTIF_ALL, world, MSG_CENTER, CENTER_OVERTIME_TIME, autocvar_timelimit_overtime); + Send_Notification(NOTIF_ALL, world, MSG_CENTER, CENTER_OVERTIME_TIME, autocvar_timelimit_overtime * 60); } float GetWinningCode(float fraglimitreached, float equality) @@ -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(); @@ -1961,11 +1932,11 @@ float WinningCondition_Scores(float limit, float leadlimit) if (limit) if (leaderfrags == limit - 1) - Announce("1fragleft"); + Send_Notification(NOTIF_ALL, world, MSG_ANNCE, ANNCE_REMAINING_FRAG_1); else if (leaderfrags == limit - 2) - Announce("2fragsleft"); + Send_Notification(NOTIF_ALL, world, MSG_ANNCE, ANNCE_REMAINING_FRAG_2); else if (leaderfrags == limit - 3) - Announce("3fragsleft"); + Send_Notification(NOTIF_ALL, world, MSG_ANNCE, ANNCE_REMAINING_FRAG_3); } } diff --git a/qcsrc/server/miscfunctions.qc b/qcsrc/server/miscfunctions.qc index 17a26821d9..13eb647dbd 100644 --- a/qcsrc/server/miscfunctions.qc +++ b/qcsrc/server/miscfunctions.qc @@ -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"); @@ -993,12 +993,6 @@ float g_bugrigs_speed_ref; float g_bugrigs_speed_pow; float g_bugrigs_steer; -float g_touchexplode; -float g_touchexplode_radius; -float g_touchexplode_damage; -float g_touchexplode_edgedamage; -float g_touchexplode_force; - float sv_autotaunt; float sv_taunt; @@ -1014,6 +1008,8 @@ void readlevelcvars(void) MUTATOR_ADD(mutator_spawn_near_teammate); if(cvar("g_physical_items")) MUTATOR_ADD(mutator_physical_items); + if(cvar("g_touchexplode")) + MUTATOR_ADD(mutator_touchexplode); if(!g_minstagib) { if(cvar("g_invincible_projectiles")) @@ -1053,12 +1049,6 @@ void readlevelcvars(void) g_bugrigs_speed_pow = cvar("g_bugrigs_speed_pow"); g_bugrigs_steer = cvar("g_bugrigs_steer"); - g_touchexplode = cvar("g_touchexplode"); - g_touchexplode_radius = cvar("g_touchexplode_radius"); - g_touchexplode_damage = cvar("g_touchexplode_damage"); - g_touchexplode_edgedamage = cvar("g_touchexplode_edgedamage"); - g_touchexplode_force = cvar("g_touchexplode_force"); - sv_clones = cvar("sv_clones"); sv_foginterval = cvar("sv_foginterval"); g_cloaked = cvar("g_cloaked"); @@ -1150,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(); } diff --git a/qcsrc/server/mutators/base.qh b/qcsrc/server/mutators/base.qh index 35b2e65234..311d156304 100644 --- a/qcsrc/server/mutators/base.qh +++ b/qcsrc/server/mutators/base.qh @@ -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 index 0000000000..46b8faccc1 --- /dev/null +++ b/qcsrc/server/mutators/gamemode_arena.qc @@ -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 index 0000000000..a2f623a000 --- /dev/null +++ b/qcsrc/server/mutators/gamemode_arena.qh @@ -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 index 0000000000..94e104728f --- /dev/null +++ b/qcsrc/server/mutators/gamemode_assault.qc @@ -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("", 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 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 index 0000000000..9aecf87f29 --- /dev/null +++ b/qcsrc/server/mutators/gamemode_assault.qh @@ -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 index 0000000000..11330fbd7d --- /dev/null +++ b/qcsrc/server/mutators/gamemode_ca.qc @@ -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 index 0000000000..ab0a9d1954 --- /dev/null +++ b/qcsrc/server/mutators/gamemode_ca.qh @@ -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 + diff --git a/qcsrc/server/mutators/gamemode_domination.qc b/qcsrc/server/mutators/gamemode_domination.qc index 051eaa333f..98b9b40dd8 100644 --- a/qcsrc/server/mutators/gamemode_domination.qc +++ b/qcsrc/server/mutators/gamemode_domination.qc @@ -19,6 +19,7 @@ void dompoint_captured () { entity head; float old_delay, old_team, real_team; + string msg = "dom-neut"; // now that the delay has expired, switch to the latest team to lay claim to this point head = self.owner; @@ -74,8 +75,16 @@ void dompoint_captured () SUB_UseTargets (); self.delay = old_delay; self.team = old_team; + + switch(self.team) + { + case NUM_TEAM_1: msg = "dom-red"; break; + case NUM_TEAM_2: msg = "dom-blue"; break; + case NUM_TEAM_3: msg = "dom-yellow"; break; + case NUM_TEAM_4: msg = "dom-pink"; break; + } - WaypointSprite_UpdateSprites(self.sprite, strcat("dom-", Team_ColorName_Lower(self.goalentity.team)), "", ""); + WaypointSprite_UpdateSprites(self.sprite, msg, "", ""); total_pps = 0, pps_red = 0, pps_blue = 0, pps_yellow = 0, pps_pink = 0; for(head = world; (head = find(head, classname, "dom_controlpoint")) != world; ) diff --git a/qcsrc/server/mutators/gamemode_freezetag.qc b/qcsrc/server/mutators/gamemode_freezetag.qc index c43a398754..980a9b20d9 100644 --- a/qcsrc/server/mutators/gamemode_freezetag.qc +++ b/qcsrc/server/mutators/gamemode_freezetag.qc @@ -1,46 +1,131 @@ -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 { diff --git a/qcsrc/server/mutators/gamemode_keyhunt.qc b/qcsrc/server/mutators/gamemode_keyhunt.qc index 97d2dcd479..13a06c367c 100644 --- a/qcsrc/server/mutators/gamemode_keyhunt.qc +++ b/qcsrc/server/mutators/gamemode_keyhunt.qc @@ -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 diff --git a/qcsrc/server/mutators/gamemode_keyhunt.qh b/qcsrc/server/mutators/gamemode_keyhunt.qh index bda701f96a..611f7f065e 100644 --- a/qcsrc/server/mutators/gamemode_keyhunt.qh +++ b/qcsrc/server/mutators/gamemode_keyhunt.qh @@ -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 index 0000000000..681a1d433a --- /dev/null +++ b/qcsrc/server/mutators/gamemode_lms.qc @@ -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 index 0000000000..16fda61550 --- /dev/null +++ b/qcsrc/server/mutators/gamemode_lms.qh @@ -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 diff --git a/qcsrc/server/mutators/mutator_new_toys.qc b/qcsrc/server/mutators/mutator_new_toys.qc index 6ee3e87b3c..89ad13c538 100644 --- a/qcsrc/server/mutators/mutator_new_toys.qc +++ b/qcsrc/server/mutators/mutator_new_toys.qc @@ -184,7 +184,7 @@ MUTATOR_HOOKFUNCTION(nt_SetWeaponreplace) MUTATOR_HOOKFUNCTION(nt_FilterItem) { if(nt_IsNewToy(self.weapon)) - self.item_pickupsound = "weapons/weaponpickup_new_toys.ogg"; + self.item_pickupsound = "weapons/weaponpickup_new_toys.wav"; return 0; } @@ -200,7 +200,7 @@ MUTATOR_DEFINITION(mutator_new_toys) if(time > 1) // game loads at time 1 error("This cannot be added at runtime\n"); - precache_sound("weapons/weaponpickup_new_toys.ogg"); + precache_sound("weapons/weaponpickup_new_toys.wav"); // mark the guns as ok to use by e.g. impulse 99 float i; diff --git a/qcsrc/server/mutators/mutator_superspec.qc b/qcsrc/server/mutators/mutator_superspec.qc index 16aa80cabc..8f289e5c3b 100644 --- a/qcsrc/server/mutators/mutator_superspec.qc +++ b/qcsrc/server/mutators/mutator_superspec.qc @@ -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; diff --git a/qcsrc/server/mutators/mutator_touchexplode.qc b/qcsrc/server/mutators/mutator_touchexplode.qc new file mode 100644 index 0000000000..f51ed653a0 --- /dev/null +++ b/qcsrc/server/mutators/mutator_touchexplode.qc @@ -0,0 +1,46 @@ +.float touchexplode_time; + +void PlayerTouchExplode(entity p1, entity p2) +{ + vector org; + org = (p1.origin + p2.origin) * 0.5; + org_z += (p1.mins_z + p2.mins_z) * 0.5; + + sound(self, CH_TRIGGER, "weapons/grenade_impact.wav", VOL_BASE, ATTN_NORM); + pointparticles(particleeffectnum("explosion_small"), org, '0 0 0', 1); + + entity e; + e = spawn(); + setorigin(e, org); + RadiusDamage(e, world, autocvar_g_touchexplode_damage, autocvar_g_touchexplode_edgedamage, autocvar_g_touchexplode_radius, world, autocvar_g_touchexplode_force, DEATH_TOUCHEXPLODE, world); + remove(e); +} + +MUTATOR_HOOKFUNCTION(touchexplode_PlayerThink) +{ + if(time > self.touchexplode_time) + if not(gameover) + if(IS_PLAYER(self)) + if(self.deadflag == DEAD_NO) + if not(IS_INDEPENDENT_PLAYER(self)) + FOR_EACH_PLAYER(other) if(self != other) + { + if(time > other.touchexplode_time) + if(other.deadflag == DEAD_NO) + if not(IS_INDEPENDENT_PLAYER(other)) + if(boxesoverlap(self.absmin, self.absmax, other.absmin, other.absmax)) + { + PlayerTouchExplode(self, other); + self.touchexplode_time = other.touchexplode_time = time + 0.2; + } + } + + return FALSE; +} + +MUTATOR_DEFINITION(mutator_touchexplode) +{ + MUTATOR_HOOK(PlayerPreThink, touchexplode_PlayerThink, CBC_ORDER_ANY); + + return FALSE; +} diff --git a/qcsrc/server/mutators/mutators.qh b/qcsrc/server/mutators/mutators.qh index 4bdcbb2823..dab42bd65d 100644 --- a/qcsrc/server/mutators/mutators.qh +++ b/qcsrc/server/mutators/mutators.qh @@ -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); @@ -15,5 +19,6 @@ MUTATOR_DECLARATION(mutator_spawn_near_teammate); MUTATOR_DECLARATION(mutator_physical_items); MUTATOR_DECLARATION(mutator_vampire); MUTATOR_DECLARATION(mutator_superspec); +MUTATOR_DECLARATION(mutator_touchexplode); MUTATOR_DECLARATION(sandbox); diff --git a/qcsrc/server/mutators/sandbox.qc b/qcsrc/server/mutators/sandbox.qc index 391e317423..cf5d82f87a 100644 --- a/qcsrc/server/mutators/sandbox.qc +++ b/qcsrc/server/mutators/sandbox.qc @@ -26,7 +26,7 @@ void sandbox_ObjectFunction_Touch() intensity -= autocvar_g_sandbox_object_material_velocity_min; // start from minimum velocity, not actual velocity intensity = bound(0, intensity * autocvar_g_sandbox_object_material_velocity_factor, 1); - sound(self, CH_TRIGGER, strcat("object/impact_", self.material, "_", ftos(ceil(random() * 5)) , ".ogg"), VOL_BASE * intensity, ATTN_NORM); + sound(self, CH_TRIGGER, strcat("object/impact_", self.material, "_", ftos(ceil(random() * 5)) , ".wav"), VOL_BASE * intensity, ATTN_NORM); pointparticles(particleeffectnum(strcat("impact_", self.material)), self.origin, '0 0 0', ceil(intensity * 10)); // allow a count from 1 to 10 } @@ -403,7 +403,7 @@ void sandbox_Database_Load() { // since objects are being loaded for the first time, precache material sounds for each for (i = 1; i <= 5; i++) // 5 sounds in total - precache_sound(strcat("object/impact_", e.material, "_", ftos(i), ".ogg")); + precache_sound(strcat("object/impact_", e.material, "_", ftos(i), ".wav")); } } if(autocvar_g_sandbox_info > 0) @@ -675,7 +675,7 @@ MUTATOR_HOOKFUNCTION(sandbox_PlayerCommand) if(argv(3)) { for (i = 1; i <= 5; i++) // precache material sounds, 5 in total - precache_sound(strcat("object/impact_", argv(3), "_", ftos(i), ".ogg")); + precache_sound(strcat("object/impact_", argv(3), "_", ftos(i), ".wav")); e.material = strzone(argv(3)); } else diff --git a/qcsrc/server/portals.qc b/qcsrc/server/portals.qc index 3e83c972ee..f0b9c5b218 100644 --- a/qcsrc/server/portals.qc +++ b/qcsrc/server/portals.qc @@ -172,7 +172,7 @@ float Portal_TeleportPlayer(entity teleporter, entity player) { // telefrag within 1 second of portal creation = amazing if(time < teleporter.teleport_time + 1) - AnnounceTo(player, "amazing"); + Send_Notification(NOTIF_ONE, player, MSG_ANNCE, ANNCE_ACHIEVEMENT_AMAZING); } if not(teleporter.enemy) diff --git a/qcsrc/server/progs.src b/qcsrc/server/progs.src index f33be85b9b..b3cc22ddaa 100644 --- a/qcsrc/server/progs.src +++ b/qcsrc/server/progs.src @@ -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 @@ -231,6 +242,7 @@ mutators/mutator_spawn_near_teammate.qc mutators/mutator_physical_items.qc mutators/sandbox.qc mutators/mutator_superspec.qc +mutators/mutator_touchexplode.qc ../warpzonelib/anglestransform.qc ../warpzonelib/mathlib.qc diff --git a/qcsrc/server/round_handler.qc b/qcsrc/server/round_handler.qc new file mode 100644 index 0000000000..af69e8e9e5 --- /dev/null +++ b/qcsrc/server/round_handler.qc @@ -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 index 0000000000..22a91dc2c6 --- /dev/null +++ b/qcsrc/server/round_handler.qh @@ -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) + diff --git a/qcsrc/server/scores.qc b/qcsrc/server/scores.qc index e8d5590238..ad10fa5d53 100644 --- a/qcsrc/server/scores.qc +++ b/qcsrc/server/scores.qc @@ -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"; diff --git a/qcsrc/server/scores_rules.qc b/qcsrc/server/scores_rules.qc index 13fd49f29b..d60f4563c1 100644 --- a/qcsrc/server/scores_rules.qc +++ b/qcsrc/server/scores_rules.qc @@ -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 diff --git a/qcsrc/server/sv_main.qc b/qcsrc/server/sv_main.qc index d80b777a6f..8d81e03806 100644 --- a/qcsrc/server/sv_main.qc +++ b/qcsrc/server/sv_main.qc @@ -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; diff --git a/qcsrc/server/t_items.qc b/qcsrc/server/t_items.qc index 2ff925ca53..40abd18b13 100644 --- a/qcsrc/server/t_items.qc +++ b/qcsrc/server/t_items.qc @@ -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) @@ -630,9 +626,9 @@ float Item_GiveTo(entity item, entity player) if (clienttype(player) == CLIENTTYPE_REAL) { if(player.health <= 5) - AnnounceTo(player, "lastsecond"); + Send_Notification(NOTIF_ONE, player, MSG_ANNCE, ANNCE_MINSTAGIB_LASTSECOND); else if(player.health < 50) - AnnounceTo(player, "narrowly"); + Send_Notification(NOTIF_ONE, player, MSG_ANNCE, ANNCE_MINSTAGIB_NARROWLY); } // sound not available // else if(item.items == IT_CELLS) @@ -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 diff --git a/qcsrc/server/teamplay.qc b/qcsrc/server/teamplay.qc index 6608fc9a31..16236dd16b 100644 --- a/qcsrc/server/teamplay.qc +++ b/qcsrc/server/teamplay.qc @@ -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 } diff --git a/qcsrc/server/tturrets/system/system_main.qc b/qcsrc/server/tturrets/system/system_main.qc index dd58b3cee2..726aa7be44 100644 --- a/qcsrc/server/tturrets/system/system_main.qc +++ b/qcsrc/server/tturrets/system/system_main.qc @@ -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) { diff --git a/qcsrc/server/tturrets/units/unit_machinegun.qc b/qcsrc/server/tturrets/units/unit_machinegun.qc index 9358cd7baf..8f18d845a9 100644 --- a/qcsrc/server/tturrets/units/unit_machinegun.qc +++ b/qcsrc/server/tturrets/units/unit_machinegun.qc @@ -31,6 +31,7 @@ void turret_machinegun_std_init() } self.damage_flags |= TFL_DMG_HEADSHAKE; + self.target_select_flags = TFL_TARGETSELECT_PLAYERS | TFL_TARGETSELECT_RANGELIMTS | TFL_TARGETSELECT_TEAMCHECK; // Our fire routine self.turret_firefunc = turret_machinegun_attack; diff --git a/qcsrc/server/vehicles/vehicles.qc b/qcsrc/server/vehicles/vehicles.qc index e23cc702cc..1b1ae3f647 100644 --- a/qcsrc/server/vehicles/vehicles.qc +++ b/qcsrc/server/vehicles/vehicles.qc @@ -1268,14 +1268,14 @@ float vehicle_initialize(string net_name, self.vehicle_exit = exitfunc; self.vehicle_enter = enterproc; self.PlayerPhysplug = physproc; - self.event_damage = vehicles_damage; + self.event_damage = func_null; self.touch = vehicles_touch; self.think = vehicles_spawn; self.nextthink = time; 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; } diff --git a/qcsrc/server/w_common.qc b/qcsrc/server/w_common.qc index e68a417e00..2e0c1e8f02 100644 --- a/qcsrc/server/w_common.qc +++ b/qcsrc/server/w_common.qc @@ -166,6 +166,7 @@ void FireRailgunBullet (vector start, vector end, float bdamage, float bforce, f .float dmg_force; .float dmg_radius; .float dmg_total; +//.float last_yoda; void W_BallisticBullet_Hit (void) { float f, q, g; @@ -186,8 +187,11 @@ void W_BallisticBullet_Hit (void) g = accuracy_isgooddamage(self.realowner, other); Damage(other, self, self.realowner, self.dmg * f, self.projectiledeathtype, self.origin, self.dmg_force * normalize(self.velocity) * f); - if(yoda) - AnnounceTo(self.realowner, "awesome"); + /*if(yoda && (time > (self.last_yoda + 5))) + { + Send_Notification(NOTIF_ONE, self.realowner, MSG_ANNCE, ANNCE_ACHIEVEMENT_YODA); + self.last_yoda = time; + }*/ // calculate hits for ballistic weapons if(g) diff --git a/qcsrc/server/w_electro.qc b/qcsrc/server/w_electro.qc index 20aac867e9..972642343b 100644 --- a/qcsrc/server/w_electro.qc +++ b/qcsrc/server/w_electro.qc @@ -33,7 +33,7 @@ void W_Plasma_Explode (void) if(IsDifferentTeam(self.realowner, other)) if(other.deadflag == DEAD_NO) if(IsFlying(other)) - AnnounceTo(self.realowner, "electrobitch"); + Send_Notification(NOTIF_ONE, self.realowner, MSG_ANNCE, ANNCE_ACHIEVEMENT_ELECTROBITCH); self.event_damage = func_null; self.takedamage = DAMAGE_NO; diff --git a/qcsrc/server/w_grenadelauncher.qc b/qcsrc/server/w_grenadelauncher.qc index b463a4e71a..cdcf841e01 100644 --- a/qcsrc/server/w_grenadelauncher.qc +++ b/qcsrc/server/w_grenadelauncher.qc @@ -12,7 +12,7 @@ void W_Grenade_Explode (void) if(IsDifferentTeam(self.realowner, other)) if(other.deadflag == DEAD_NO) if(IsFlying(other)) - AnnounceTo(self.realowner, "airshot"); + Send_Notification(NOTIF_ONE, self.realowner, MSG_ANNCE, ANNCE_ACHIEVEMENT_AIRSHOT); self.event_damage = func_null; self.takedamage = DAMAGE_NO; @@ -32,7 +32,7 @@ void W_Grenade_Explode2 (void) if(IsDifferentTeam(self.realowner, other)) if(other.deadflag == DEAD_NO) if(IsFlying(other)) - AnnounceTo(self.realowner, "airshot"); + Send_Notification(NOTIF_ONE, self.realowner, MSG_ANNCE, ANNCE_ACHIEVEMENT_AIRSHOT); self.event_damage = func_null; self.takedamage = DAMAGE_NO; diff --git a/qcsrc/server/w_hagar.qc b/qcsrc/server/w_hagar.qc index b4b79abb83..4d9a0fabe1 100644 --- a/qcsrc/server/w_hagar.qc +++ b/qcsrc/server/w_hagar.qc @@ -236,6 +236,9 @@ void W_Hagar_Attack2_Load_Release (void) void W_Hagar_Attack2_Load (void) { // loadable hagar secondary attack, must always run each frame + + if(time < game_starttime) + return; float loaded, enough_ammo; loaded = self.hagar_load >= autocvar_g_balance_hagar_secondary_load_max; diff --git a/qcsrc/server/w_minelayer.qc b/qcsrc/server/w_minelayer.qc index 6d3c331739..40c60e827e 100644 --- a/qcsrc/server/w_minelayer.qc +++ b/qcsrc/server/w_minelayer.qc @@ -66,7 +66,7 @@ void W_Mine_Explode () if(IsDifferentTeam(self.realowner, other)) if(other.deadflag == DEAD_NO) if(IsFlying(other)) - AnnounceTo(self.realowner, "airshot"); + Send_Notification(NOTIF_ONE, self.realowner, MSG_ANNCE, ANNCE_ACHIEVEMENT_AIRSHOT); self.event_damage = func_null; self.takedamage = DAMAGE_NO; diff --git a/qcsrc/server/w_minstanex.qc b/qcsrc/server/w_minstanex.qc index c464af93a2..611319a16c 100644 --- a/qcsrc/server/w_minstanex.qc +++ b/qcsrc/server/w_minstanex.qc @@ -19,15 +19,15 @@ void W_MinstaNex_Attack (void) if(g_minstagib) { if(yoda) - AnnounceTo(self, "yoda"); + Send_Notification(NOTIF_ONE, self, MSG_ANNCE, ANNCE_ACHIEVEMENT_YODA); } else { if(yoda && flying) - AnnounceTo(self, "yoda"); + Send_Notification(NOTIF_ONE, self, MSG_ANNCE, ANNCE_ACHIEVEMENT_YODA); if(damage_goodhits && self.minstanex_lasthit) { - AnnounceTo(self, "impressive"); + Send_Notification(NOTIF_ONE, self, MSG_ANNCE, ANNCE_ACHIEVEMENT_IMPRESSIVE); damage_goodhits = 0; // only every second time } } @@ -93,12 +93,13 @@ void minstagib_ammocheck(void) if (time < self.minstagib_nextthink) return; - if (self.deadflag || gameover) + if (self.deadflag || gameover || (self.flags & FL_GODMODE)) 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 { @@ -106,60 +107,58 @@ void minstagib_ammocheck(void) if (self.health == 5) { Damage(self, self, self, 5, DEATH_NOAMMO, self.origin, '0 0 0'); - AnnounceTo(self, "terminated"); + Send_Notification(NOTIF_ONE, self, MSG_ANNCE, ANNCE_MINSTAGIB_TERMINATED); } else if (self.health == 10) { Damage(self, self, self, 5, DEATH_NOAMMO, self.origin, '0 0 0'); - AnnounceTo(self, "1"); + Send_Notification(NOTIF_ONE, self, MSG_ANNCE, ANNCE_NUM_1); } else if (self.health == 20) { Damage(self, self, self, 10, DEATH_NOAMMO, self.origin, '0 0 0'); - AnnounceTo(self, "2"); + Send_Notification(NOTIF_ONE, self, MSG_ANNCE, ANNCE_NUM_2); } else if (self.health == 30) { Damage(self, self, self, 10, DEATH_NOAMMO, self.origin, '0 0 0'); - AnnounceTo(self, "3"); + Send_Notification(NOTIF_ONE, self, MSG_ANNCE, ANNCE_NUM_3); } else if (self.health == 40) { Damage(self, self, self, 10, DEATH_NOAMMO, self.origin, '0 0 0'); - AnnounceTo(self, "4"); + Send_Notification(NOTIF_ONE, self, MSG_ANNCE, ANNCE_NUM_4); } else if (self.health == 50) { Damage(self, self, self, 10, DEATH_NOAMMO, self.origin, '0 0 0'); - AnnounceTo(self, "5"); + Send_Notification(NOTIF_ONE, self, MSG_ANNCE, ANNCE_NUM_5); } else if (self.health == 60) { Damage(self, self, self, 10, DEATH_NOAMMO, self.origin, '0 0 0'); - AnnounceTo(self, "6"); + Send_Notification(NOTIF_ONE, self, MSG_ANNCE, ANNCE_NUM_6); } else if (self.health == 70) { Damage(self, self, self, 10, DEATH_NOAMMO, self.origin, '0 0 0'); - AnnounceTo(self, "7"); + Send_Notification(NOTIF_ONE, self, MSG_ANNCE, ANNCE_NUM_7); } else if (self.health == 80) { Damage(self, self, self, 10, DEATH_NOAMMO, self.origin, '0 0 0'); - AnnounceTo(self, "8"); + Send_Notification(NOTIF_ONE, self, MSG_ANNCE, ANNCE_NUM_8); } else if (self.health == 90) { Send_Notification(NOTIF_ONE_ONLY, self, MSG_CENTER, CENTER_MINSTA_FINDAMMO); Damage(self, self, self, 10, DEATH_NOAMMO, self.origin, '0 0 0'); - AnnounceTo(self, "9"); + Send_Notification(NOTIF_ONE, self, MSG_ANNCE, ANNCE_NUM_9); } else if (self.health == 100) { - Send_Notification(NOTIF_ONE_ONLY, self, MSG_CENTER, CENTER_MINSTA_FINDAMMO_FIRST); + Send_Notification(NOTIF_ONE_ONLY, self, MSG_MULTI, MULTI_MINSTA_FINDAMMO); Damage(self, self, self, 10, DEATH_NOAMMO, self.origin, '0 0 0'); - if not(self.flags & FL_GODMODE) - AnnounceTo(self, "10"); } } self.minstagib_nextthink = time + 1; diff --git a/qcsrc/server/w_nex.qc b/qcsrc/server/w_nex.qc index 9d66f407ef..23dcce96d8 100644 --- a/qcsrc/server/w_nex.qc +++ b/qcsrc/server/w_nex.qc @@ -66,7 +66,7 @@ void W_Nex_Attack (float issecondary) FireRailgunBullet (w_shotorg, w_shotorg + w_shotdir * MAX_SHOT_DISTANCE, mydmg, myforce, mymindist, mymaxdist, myhalflife, myforcehalflife, WEP_NEX); if(yoda && flying) - AnnounceTo(self, "yoda"); + Send_Notification(NOTIF_ONE, self, MSG_ANNCE, ANNCE_ACHIEVEMENT_YODA); //beam and muzzle flash done on client SendCSQCNexBeamParticle(charge); diff --git a/qcsrc/server/w_rocketlauncher.qc b/qcsrc/server/w_rocketlauncher.qc index 202dd68bdf..250fc0d3be 100644 --- a/qcsrc/server/w_rocketlauncher.qc +++ b/qcsrc/server/w_rocketlauncher.qc @@ -23,7 +23,7 @@ void W_Rocket_Explode () if(IsDifferentTeam(self.realowner, other)) if(other.deadflag == DEAD_NO) if(IsFlying(other)) - AnnounceTo(self.realowner, "airshot"); + Send_Notification(NOTIF_ONE, self.realowner, MSG_ANNCE, ANNCE_ACHIEVEMENT_AIRSHOT); self.event_damage = func_null; self.takedamage = DAMAGE_NO; diff --git a/tooltips.db b/tooltips.db index 1301bda978..0f80a81332 100644 --- a/tooltips.db +++ b/tooltips.db @@ -40,7 +40,6 @@ \g_pinata\Players will drop all weapons they possessed when they are killed \g_weapon_stay\Weapons stay after they are picked up \g_weaponarena\Selecting a weapon arena will give all players that weapon at spawn as well as unlimited ammo, and disable all other weapon pickups. -\menu_weaponarena_with_laser\Also enable the laser in the weapon arena \g_minstagib\Players will be given the Minstanex, which is a railgun with infinite damage. If the player runs out of ammo, he will have 10 seconds to find some or if he fails to do so, face death. The secondary fire mode is a laser which does not inflict any damage and is good for doing trickjumps. \g_nix\No items Xonotic - instead of pickup items, everyone plays with the same weapon. After some time, a countdown will start, after which everyone will switch to another weapon. \g_nix_with_laser\Always carry the laser as an additional weapon in Nix diff --git a/tooltips.db.it b/tooltips.db.it index 180076f771..e1d1825ddd 100644 --- a/tooltips.db.it +++ b/tooltips.db.it @@ -27,7 +27,7 @@ \g_maplist_votable\Numero di mappe che sono mostrate nel voto delle mappe alla fine di una partita \sv_vote_simple_majority_factor\La semplice maggioranza vince il voto \XonoticMultiplayerDialog/Impostazioni avanzate...\Impostazioni avanzate del server -\XonoticMultiplayerDialog/Mutatori...\Mutatori e arene dedicate ad armi +\XonoticMultiplayerDialog/Mutatori...\Mutatori e arene di armi \g_dodging\Abilita schivamento \g_cloaked\Tutti i giocatori sono quasi invisibili \g_footsteps\Abilita suoni dei passi @@ -40,7 +40,6 @@ \g_pinata\I giocatori rilasceranno tutte le armi che possedevano appena vengono uccisi \g_weapon_stay\Le armi rimangono dopo che vengono raccolte \g_weaponarena\Selezionando un'arena dedicata ad un'arma si darà a tutti i giocatori quell'arma con munizioni infinite, e disabiliterà tutti gli altri raccoglimenti delle armi. -\menu_weaponarena_with_laser\Abilita anche il laser nelle arene dedicate ad un'arma \g_minstagib\Ai giocatori sarà dato il Minstanex, che è un railgun con danni illimitati. Se il giocatore rimane senza munizioni, avrà 10 secondi per trovarne alcune, altrimenti morirà. Il fuoco secondario è un laser che non infligge nessun danno ed è buono per effettuare vari trickjump. \g_nix\"No items Xonotic" - invece di raccogliere oggetti, ognuno giocherà con la stessa arma. Dopo un pò di tempo, un conto alla rovescia inizierà, dopo il quale ognuno passerà ad un'altra arma. \g_nix_with_laser\Porta sempre il laser come arma aggiuntiva nella modalità "No items Xonotic" diff --git a/unit_fusreac.cfg b/unit_fusreac.cfg index c575f1bf58..c9373f68fe 100644 --- a/unit_fusreac.cfg +++ b/unit_fusreac.cfg @@ -11,3 +11,22 @@ set g_turrets_unit_fusreac_std_target_range_min 1 set g_turrets_unit_fusreac_std_ammo_max 100 set g_turrets_unit_fusreac_std_ammo 0 set g_turrets_unit_fusreac_std_ammo_recharge 100 + +set g_turrets_unit_fusreac_std_shot_radius 0 +set g_turrets_unit_fusreac_std_shot_spread 0 +set g_turrets_unit_fusreac_std_shot_force 0 +set g_turrets_unit_fusreac_std_shot_volly 0 +set g_turrets_unit_fusreac_std_shot_volly_refire 0 +set g_turrets_unit_fusreac_std_target_range_optimal 0 +set g_turrets_unit_fusreac_std_target_select_rangebias 0 +set g_turrets_unit_fusreac_std_target_select_samebias 0 +set g_turrets_unit_fusreac_std_target_select_anglebias 0 +set g_turrets_unit_fusreac_std_target_select_playerbias 0 +set g_turrets_unit_fusreac_std_aim_firetolerance_dist 0 +set g_turrets_unit_fusreac_std_aim_speed 0 +set g_turrets_unit_fusreac_std_aim_maxrot 0 +set g_turrets_unit_fusreac_std_aim_maxpitch 0 +set g_turrets_unit_fusreac_std_track_type 0 +set g_turrets_unit_fusreac_std_track_accel_pitch 0 +set g_turrets_unit_fusreac_std_track_accel_rot 0 +set g_turrets_unit_fusreac_std_track_blendrate 0 \ No newline at end of file