]> de.git.xonotic.org Git - xonotic/xonotic-data.pk3dir.git/commitdiff
Merge branch 'master' into z411/bai-server
authorz411 <z411@omaera.org>
Fri, 8 Apr 2022 23:09:06 +0000 (19:09 -0400)
committerz411 <z411@omaera.org>
Fri, 8 Apr 2022 23:09:06 +0000 (19:09 -0400)
35 files changed:
1  2 
balance-xdf.cfg
commands.cfg
gamemodes-server.cfg
notifications.cfg
qcsrc/client/announcer.qc
qcsrc/client/hud/panel/centerprint.qc
qcsrc/client/hud/panel/centerprint.qh
qcsrc/client/hud/panel/timer.qc
qcsrc/client/main.qc
qcsrc/client/main.qh
qcsrc/client/view.qc
qcsrc/common/ent_cs.qc
qcsrc/common/gamemodes/gamemode/_mod.inc
qcsrc/common/gamemodes/gamemode/_mod.qh
qcsrc/common/mapinfo.qh
qcsrc/common/net_linked.qh
qcsrc/common/notifications/all.inc
qcsrc/common/notifications/all.qh
qcsrc/common/stats.qh
qcsrc/menu/xonotic/util.qc
qcsrc/server/client.qc
qcsrc/server/client.qh
qcsrc/server/command/cmd.qc
qcsrc/server/command/common.qc
qcsrc/server/command/sv_cmd.qc
qcsrc/server/command/vote.qc
qcsrc/server/command/vote.qh
qcsrc/server/damage.qc
qcsrc/server/damage.qh
qcsrc/server/items/items.qc
qcsrc/server/mutators/events.qh
qcsrc/server/round_handler.qc
qcsrc/server/scores.qc
qcsrc/server/world.qc
qcsrc/server/world.qh

diff --combined balance-xdf.cfg
index f248b7ac83a46d151e279f13da416924505a1c60,67e8810b5435f4c1954b59902336391083667002..d8d4df60d53e2a6c916d61a7785bb07df3efd8b8
@@@ -49,22 -49,6 +49,22 @@@ set g_lms_start_ammo_rockets 16
  set g_lms_start_ammo_cells 180
  set g_lms_start_ammo_plasma 180
  set g_lms_start_ammo_fuel 0
 +set g_mayhem_start_health 200
 +set g_mayhem_start_armor 200
 +set g_mayhem_start_ammo_shells 60
 +set g_mayhem_start_ammo_nails 320
 +set g_mayhem_start_ammo_rockets 160
 +set g_mayhem_start_ammo_cells 180
 +set g_mayhem_start_ammo_plasma 180
 +set g_mayhem_start_ammo_fuel 0
 +set g_tmayhem_start_health 200
 +set g_tmayhem_start_armor 200
 +set g_tmayhem_start_ammo_shells 60
 +set g_tmayhem_start_ammo_nails 320
 +set g_tmayhem_start_ammo_rockets 160
 +set g_tmayhem_start_ammo_cells 180
 +set g_tmayhem_start_ammo_plasma 180
 +set g_tmayhem_start_ammo_fuel 0
  set g_balance_nix_roundtime 25
  set g_balance_nix_incrtime 1.6
  set g_balance_nix_ammo_shells 60
@@@ -237,9 -221,9 +237,9 @@@ set g_balance_powerup_invincible_takefo
  set g_balance_powerup_invincible_time 30
  set g_balance_powerup_invisibility_alpha 0.15
  set g_balance_powerup_invisibility_time 30
- set g_balance_powerup_speed_attackrate 0.8
- set g_balance_powerup_speed_highspeed 1.5
- set g_balance_powerup_speed_time 30
+ set g_balance_powerup_speed_attackrate 0.7692307692   // 1/1.3
+ set g_balance_powerup_speed_highspeed 1.3
+ set g_balance_powerup_speed_time 30 // q3 haste lasts 30 seconds unless count field set otherwise
  set g_balance_powerup_strength_damage 3
  set g_balance_powerup_strength_force 3
  set g_balance_powerup_strength_time 30
diff --combined commands.cfg
index aa11197196af2065b30cd7dbee582fa755d4dd6a,1e26673d42055b639a03f40bbc12d9358e842c07..27c5a8bc17e620596ad207f7e567c9055ef7f4b9
@@@ -111,12 -111,13 +111,13 @@@ alias tdem "timedemo $1
  // ===============================================
  //  menu_cmd (menu command) - menu/command/menu_cmd.qc
  // ===============================================
+ alias menu_showwelcomedialog "menu_cmd directmenu Welcome"
  alias menu_showteamselect "menu_cmd directmenu TeamSelect"
  alias menu_showhudexit "menu_cmd directmenu HUDExit"
  alias menu_showhudoptions "menu_cmd directpanelhudmenu ${* ?}"
  alias menu_showsandboxtools "menu_cmd directmenu SandboxTools"
  alias menu_showquitdialog "menu_cmd directmenu Quit"
- alias menu_showdisconnectdialog "menu_cmd directmenu Disconnect"
+ alias menu_showgamemenudialog "menu_cmd directmenu GameMenu"
  alias menu_showmonstertools "menu_cmd directmenu MonsterTools"
  
  // command executed before loading a map by the menu
@@@ -184,7 -185,7 +185,7 @@@ alias team_red "cmd selectteam red; cm
  alias team_blue "cmd selectteam blue; cmd join"
  alias team_pink "cmd selectteam pink; cmd join"
  alias team_yellow "cmd selectteam yellow; cmd join"
- alias team_auto "cmd selectteam auto; cmd join"
+ alias team_auto "togglemenu 0; cmd selectteam auto; cmd join"
  
  alias spec "spectate ${* ?}"
  
@@@ -238,7 -239,8 +239,8 @@@ alias unlockteams          "qc_cmd_s
  alias warp                 "qc_cmd_sv     warp                 ${* ?}" // Choose different level in campaign
  
  // other aliases for server commands
- alias endmatch "timelimit -1"
+ set _endmatch 0 "if set to 1 ends the match immediately; use it instead of timelimit -1 (deprecated)"
+ alias endmatch "_endmatch 1"
  alias bots "minplayers 4; minplayers_per_team 2"
  alias nobots "minplayers 0; minplayers_per_team 0"
  
@@@ -267,7 -269,6 +269,7 @@@ alias settemp_restore "qc_cmd_svcl sett
  // ===================================
  alias ban                  "qc_cmd_sv     ban                  ${* ?}" // Ban an IP address or a range of addresses (like 1.2.3)
  alias banlist              "qc_cmd_sv     banlist              ${* ?}" // List all existing bans
 +alias kickkick             "qc_cmd_sv     kickkick             ${* ?}" // Disconnect a client
  alias kickban              "qc_cmd_sv     kickban              ${* ?}" // Disconnect a client and ban it at the same time
  alias mute                 "qc_cmd_sv     mute                 ${* ?}" // Disallow a client from talking by muting them
  alias unban                "qc_cmd_sv     unban                ${* ?}" // Remove an existing ban
diff --combined gamemodes-server.cfg
index 457883ad4c66ebca93b1fa632dc6fe3850ce8a90,ba4812b57afd8bdc4c5da48d31320788f122acb2..b57a6233acf7d80a3915d9ee717a22689d77ac02
@@@ -29,9 -29,6 +29,9 @@@ alias sv_hook_gamestart_k
  alias sv_hook_gamestart_ft
  alias sv_hook_gamestart_inv
  alias sv_hook_gamestart_duel
 +alias sv_hook_gamestart_mmm //LegendGuard adds mmm hook for MMM 20-02-2021
 +alias sv_hook_gamestart_mayhem
 +alias sv_hook_gamestart_tmayhem
  // there is currently no hook for when the match is restarted
  // see sv_hook_readyrestart for previous uses of this hook
  //alias sv_hook_gamerestart
@@@ -61,9 -58,6 +61,9 @@@ alias sv_vote_gametype_hook_on
  alias sv_vote_gametype_hook_rc
  alias sv_vote_gametype_hook_tdm
  alias sv_vote_gametype_hook_duel
 +alias sv_vote_gametype_hook_mmm //LegendGuard adds mmm hook for MMM 20-02-2021
 +alias sv_vote_gametype_hook_mayhem
 +alias sv_vote_gametype_hook_tmayhem
  
  // Example preset to allow 1v1ctf to be used for the gametype voting screen.
  // Aliases can have max 31 chars so the gametype can have max 9 chars.
@@@ -214,27 -208,6 +214,27 @@@ set g_duel_respawn_delay_large_count 
  set g_duel_respawn_delay_max 0
  set g_duel_respawn_waves 0
  set g_duel_weapon_stay 0
 +set g_mmm_respawn_delay_small 0 //LegendGuard adds mmm cvars for MMM 20-02-2021
 +set g_mmm_respawn_delay_small_count 0
 +set g_mmm_respawn_delay_large 0
 +set g_mmm_respawn_delay_large_count 0
 +set g_mmm_respawn_delay_max 0
 +set g_mmm_respawn_waves 0
 +set g_mmm_weapon_stay 0
 +set g_mayhem_respawn_delay_small 0
 +set g_mayhem_respawn_delay_small_count 0
 +set g_mayhem_respawn_delay_large 0
 +set g_mayhem_respawn_delay_large_count 0
 +set g_mayhem_respawn_delay_max 0
 +set g_mayhem_respawn_waves 0
 +set g_mayhem_weapon_stay 0
 +set g_tmayhem_respawn_delay_small 0
 +set g_tmayhem_respawn_delay_small_count 0
 +set g_tmayhem_respawn_delay_large 0
 +set g_tmayhem_respawn_delay_large_count 0
 +set g_tmayhem_respawn_delay_max 0
 +set g_tmayhem_respawn_waves 0
 +set g_tmayhem_weapon_stay 0
  
  
  // =========
@@@ -478,6 -451,7 +478,7 @@@ set g_lms_last_join 3      "if g_lms_join_an
  set g_lms_join_anytime 1      "1: new players can join, but get same amount of lives as the worst player; 0: new players can only join if the worst active player has (fraglimit - g_lms_last_join) or more lives"
  set g_lms_items 0 "enables items to spawn, weaponarena still disables weapons and ammo (to force all items to spawn, use g_pickup_items 1 instead)"
  set g_lms_weaponarena "most_available" "starting weapons - takes the same options as g_weaponarena"
+ set g_lms_forfeit_min_match_time 30 "end the match early if at least this many seconds have elapsed and less than 2 players are playing due to forfeits"
  
  
  // =========
@@@ -588,71 -562,3 +589,71 @@@ set g_duel 0 "Duel: frag the opponent m
  //set g_duel_warmup 180 "Have a short warmup period before beginning the actual duel"
  set g_duel_with_powerups 0 "Enable powerups to spawn in the duel gamemode"
  set g_duel_not_dm_maps 0 "when this is set, DM maps will NOT be listed in duel"
 +
 +//LegendGuard adds mmm cvars for MMM 20-02-2021
 +// ==========
 +//  murder in megaerebus manor
 +// ==========
 +set g_mmm 0 "Murder in Megaerebus Manor: A group of space civilians have murderers among them. Murderers must kill civilians, while the civilians have to try to find and kill the murderers"
 +set g_mmm_not_lms_maps 0 "when this is set, LMS maps will NOT be listed in mmm"
 +set g_mmm_not_dm_maps 0 "when this is set, DM maps will NOT be listed in mmm"
 +set g_mmm_murderer_count 0.25 "number of players who will become murderers, set between 0 and 0.9 to use a multiplier of the current players, or 1 and above to specify an exact number of players"
 +set g_mmm_detective_count 0.125 "number of players who will become detectives, set between 0 and 0.9 to use a multiplier of the current players, or 1 and above, to specify an exact number of players. 0 = no detectives"
 +set g_mmm_punish_teamkill 0 "enable option to kill the player when they kill an ally"
 +set g_mmm_reward_civilian 1 "give a point to all civilian players if the round timelimit is reached, in addition to the points given for kills"
 +set g_mmm_warmup 10 "how long the players will have time to run around the map before the round starts"
 +set g_mmm_round_timelimit 180 "round time limit in seconds"
 +set g_mmm_max_karma_points 1000 "limit of maximum number of karma points will have in the server"
 +set g_mmm_min_karma_points 550 "limit where number of karma points can be reached when are being decreased"
 +set g_mmm_karma_bankick_tool 1 "tool for strict rules when karma is low: '0' nothing does, '1' forces player to spec, '2' kicks player, '3' bans player"
 +set g_mmm_karma_bantime 1800 "number of seconds to ban someone with very low karma"
 +set g_mmm_karma_damageactive 1 "enable karma damage rule. If a player's karma is low, they will not do as much damage as a player who has high or full karma"
 +set g_mmm_karma_damagepunishmentdeal 20 "punishment damage points when player kills an ally"
 +set g_mmm_karma_severity 0.25 "how severe karma is to decrease karma points to the players [0.1 - 1.0]"
 +set g_mmm_reward_sleuth 1 "give a point to all sleuth players if investigated corpses"
 +set g_mmm_reward_detective 1 "give a point to all detective players if investigated corpses"
 +
 +// ==============================
 +//  free for all and team mayhem
 +// ==============================
 +set g_mayhem 0 "Mayhem: Compete for the most damage dealt and frags in this chaotic mayhem!"
 +set g_tmayhem 0 "Team Mayhem: Compete with your team for the most damage dealt and frags in this chaotic mayhem!"
 +
 +set g_mayhem_scoringmethod 1 "1: By default 25% of the score is based on kills and 75% of it is based on damage. 2: 100% frags. 3: 100% damage."
 +set g_tmayhem_scoringmethod 1 "1: By default 25% of the score is based on kills and 75% of it is based on damage. 2: 100% frags. 3: 100% damage."
 +set g_mayhem_scoringmethod_1_damage_weight 0.75 "for the first scoring method how much is damage equal to player's spawning health worth in score"
 +set g_tmayhem_scoringmethod_1_damage_weight 0.75 "for the first scoring method how much is damage equal to player's spawning health worth in score"
 +set g_mayhem_scoringmethod_1_disable_selfdamage2score 0 "disable reducing score with self damage at the cost of full penalty for suicides regardless of how much health was lost suiciding"
 +set g_tmayhem_scoringmethod_1_disable_selfdamage2score 0 "disable reducing score with self damage at the cost of full penalty for suicides regardless of how much health was lost suiciding"
 +set g_mayhem_scoringmethod_1_frag_weight 0.25 "for the first scoring method how much is a frag worth in score"
 +set g_tmayhem_scoringmethod_1_frag_weight 0.25 "for the first scoring method how much is a frag worth in score"
 +
 +set g_mayhem_fraglimit 30 "Team Mayhem basis for how many frags until the match ends, edit this over point_limit preferably"
 +set g_tmayhem_fraglimit 50 "Team Mayhem basis for how many frags until the match ends, edit this over point_limit preferably"
 +
 +set g_mayhem_visual_score_limit 1000 "Mayhem visual score limit overriding the mapinfo specified one"
 +set g_tmayhem_visual_score_limit 1000 "Team Mayhem visual score limit overriding the mapinfo specified one"
 +
 +set g_tmayhem_score_leadlimit -1 "Team Mayhem score lead limit(based on tmayhem_visual_score_limit, not tmayhem_fraglimit) overriding the mapinfo specified one (use 0 to play without limit, and -1 to use the mapinfo's limit)"
 +
 +set g_mayhem_weaponarena "most_available" "starting weapons - takes the same options as g_weaponarena"
 +set g_tmayhem_weaponarena "most_available" "starting weapons - takes the same options as g_weaponarena"
 +
 +set g_mayhem_powerups 1 "Allow powerups in mayhem. Only checked if g_powerups is -1 therefore this will be overridden by g_powerups 1 or 0"
 +set g_tmayhem_powerups 1 "Allow powerups in team mayhem. Only checked if g_powerups is -1 therefore this will be overridden by g_powerups 1 or 0"
 +set g_mayhem_pickup_items 0 "spawn pickup items in mayhem"
 +set g_tmayhem_pickup_items 0 "spawn pickup items in team mayhem"
 +set g_mayhem_pickup_items_remove_weapons_and_ammo 1 "when pickup items are enabled in mayhem still remove weapons and ammo pickups"
 +set g_tmayhem_pickup_items_remove_weapons_and_ammo 1 "when pickup items are enabled in team mayhem still remove weapons and ammo pickups"
 +
 +set g_mayhem_selfdamage 0 "0 = disable selfdamage in mayhem, 1 = enable selfdamage in mayhem"
 +set g_tmayhem_selfdamage 0 "0 = disable selfdamage in tmayhem, 1 = enable selfdamage in tmayhem"
 +
 +set g_mayhem_regenerate 0 "health and/or armor regeneration, according to g_balance_health_regen and g_balance_armor_regen"
 +set g_tmayhem_regenerate 0 "health and/or armor regeneration, according to g_balance_health_regen and g_balance_armor_regen"
 +set g_mayhem_rot 0 "health and/or armor rotting, according to g_balance_health_rot and g_balance_armor_rot"
 +set g_tmayhem_rot 0 "health and/or armor rotting, according to g_balance_health_rot and g_balance_armor_rot"
 +
 +set g_tmayhem_teams 2 "how many teams are in team mayhem (set by mapinfo)"
 +set g_tmayhem_team_spawns 0 "when 1, players spawn from the team spawnpoints of the map, if any"
 +set g_tmayhem_teams_override 0        "how many teams are in team mayhem"
diff --combined notifications.cfg
index ff5ec357d70ea517fcf4774a692d61349a2e91e4,83fbcaf849fb23be8a46febd075b598fcf3d28a5..307aabd16edef00b0ba54454a4550f68ec5dc4d8
@@@ -276,11 -276,6 +276,11 @@@ seta notification_INFO_SUPERSPEC_MISSIN
  seta notification_INFO_SUPERWEAPON_PICKUP "1" "0 = off, 1 = print to console, 2 = print to console and chatbox (if notification_allow_chatboxprint is enabled)"
  seta notification_INFO_TEAMCHANGE_LARGERTEAM "1" "0 = off, 1 = print to console, 2 = print to console and chatbox (if notification_allow_chatboxprint is enabled)"
  seta notification_INFO_TEAMCHANGE_NOTALLOWED "1" "0 = off, 1 = print to console, 2 = print to console and chatbox (if notification_allow_chatboxprint is enabled)"
 +
 +//LegendGuard adds mmm notifications for MMM 20-02-2021
 +seta notification_INFO_MMM_MURDERER_WIN "1" "0 = off, 1 = print to console, 2 = print to console and chatbox (if notification_allow_chatboxprint is enabled)"
 +seta notification_INFO_MMM_CIVILIAN_WIN "1" "0 = off, 1 = print to console, 2 = print to console and chatbox (if notification_allow_chatboxprint is enabled)"
 +
  seta notification_INFO_VERSION_BETA "1" "0 = off, 1 = print to console, 2 = print to console and chatbox (if notification_allow_chatboxprint is enabled)"
  seta notification_INFO_VERSION_OLD "2" "0 = off, 1 = print to console, 2 = print to console and chatbox (if notification_allow_chatboxprint is enabled)"
  seta notification_INFO_VERSION_OUTDATED "2" "0 = off, 1 = print to console, 2 = print to console and chatbox (if notification_allow_chatboxprint is enabled)"
@@@ -352,7 -347,6 +352,6 @@@ seta notification_CENTER_ALONE "1" "0 
  seta notification_CENTER_ASSAULT_ATTACKING "1" "0 = off, 1 = centerprint"
  seta notification_CENTER_ASSAULT_DEFENDING "1" "0 = off, 1 = centerprint"
  seta notification_CENTER_ASSAULT_OBJ_DESTROYED "1" "0 = off, 1 = centerprint"
- seta notification_CENTER_CAMPAIGN_MESSAGE "1" "0 = off, 1 = centerprint"
  seta notification_CENTER_CAMPCHECK "1" "0 = off, 1 = centerprint"
  seta notification_CENTER_COINTOSS "1" "0 = off, 1 = centerprint"
  seta notification_CENTER_COUNTDOWN_BEGIN "1" "0 = off, 1 = centerprint"
@@@ -485,6 -479,7 +484,7 @@@ seta notification_CENTER_KEYHUNT_ROUNDS
  seta notification_CENTER_KEYHUNT_SCAN "1" "0 = off, 1 = centerprint"
  seta notification_CENTER_KEYHUNT_START "1" "0 = off, 1 = centerprint"
  seta notification_CENTER_LMS_NOLIVES "1" "0 = off, 1 = centerprint"
+ seta notification_CENTER_LMS_SPECWARN "1" "0 = off, 1 = centerprint"
  seta notification_CENTER_MISSING_PLAYERS "1" "0 = off, 1 = centerprint"
  seta notification_CENTER_MISSING_TEAMS "1" "0 = off, 1 = centerprint"
  seta notification_CENTER_MOTD "1" "0 = off, 1 = centerprint"
@@@ -535,14 -530,6 +535,14 @@@ seta notification_CENTER_TEAMCHANGE_SPE
  seta notification_CENTER_TEAMCHANGE_SUICIDE "1" "0 = off, 1 = centerprint"
  seta notification_CENTER_TIMEOUT_BEGINNING "1" "0 = off, 1 = centerprint"
  seta notification_CENTER_TIMEOUT_ENDING "1" "0 = off, 1 = centerprint"
 +
 +//LegendGuard adds mmm notification for MMM 20-02-2021
 +seta notification_CENTER_MMM_MURDERER "1" "0 = off, 1 = centerprint"
 +seta notification_CENTER_MMM_MURDERER_WIN "1" "0 = off, 1 = centerprint"
 +seta notification_CENTER_MMM_CIVILIAN "1" "0 = off, 1 = centerprint"
 +seta notification_CENTER_MMM_CIVILIAN_WIN "1" "0 = off, 1 = centerprint"
 +seta notification_CENTER_MMM_DETECTIVE "1" "0 = off, 1 = centerprint"
 +
  seta notification_CENTER_VEHICLE_ENTER "1" "0 = off, 1 = centerprint"
  seta notification_CENTER_VEHICLE_ENTER_GUNNER "1" "0 = off, 1 = centerprint"
  seta notification_CENTER_VEHICLE_ENTER_STEAL "1" "0 = off, 1 = centerprint"
index 41c8ecf5b29b116e39ea906226ec8231683132f1,6a211246f3047bba3b7c49e5f394cebe571f706e..79f7adf01216519b477f6daff3ece069ba800b0d
@@@ -1,13 -1,12 +1,12 @@@
  #include "announcer.qh"
  
+ #include <client/draw.qh>
  #include <client/hud/panel/centerprint.qh>
  #include <client/mutators/_mod.qh>
  #include <common/notifications/all.qh>
  #include <common/stats.qh>
  #include <common/mapinfo.qh>
  #include <common/ent_cs.qh>
- #include <common/gamemodes/gamemode/duel/duel.qh>
  
  bool announcer_1min;
  bool announcer_5min;
@@@ -25,9 -24,7 +24,9 @@@ void Announcer_Countdown(entity this
  {
        float starttime = STAT(GAMESTARTTIME);
        float roundstarttime = STAT(ROUNDSTARTTIME);
 -      if(roundstarttime == -1)
 +      bool game_timeout = (STAT(TIMEOUT_LAST) > 0);
 +      
 +      if(roundstarttime == -1 || game_timeout)
        {
                Local_Notification(MSG_CENTER, CENTER_COUNTDOWN_ROUNDSTOP);
                delete(this);
                return;
        }
  
-       if(starttime <= time && roundstarttime != starttime) // game start time has passed
-               announcer_5min = announcer_1min = false; // reset maptime announcers now as well
        bool inround = (roundstarttime && time >= starttime);
        float countdown = (inround ? roundstarttime - time : starttime - time);
        float countdown_rounded = floor(0.5 + countdown);
  
 +      if(time >= starttime) centerprint_ClearTitle();
 +      
        if(countdown <= 0) // countdown has finished, starttime is now
        {
                Local_Notification(MSG_CENTER, CENTER_COUNTDOWN_BEGIN);
@@@ -58,7 -50,7 +54,7 @@@
        {
                if(inround)
                {
-                       Local_Notification(MSG_CENTER, CENTER_COUNTDOWN_ROUNDSTART, countdown_rounded);
+                       Local_Notification(MSG_CENTER, CENTER_COUNTDOWN_ROUNDSTART, STAT(ROUNDS_PLAYED) + 1, countdown_rounded);
                        Notification annce_num = Announcer_PickNumber(CNT_ROUNDSTART, countdown_rounded);
                        if(annce_num != NULL)
                                Local_Notification(MSG_ANNCE, annce_num);
@@@ -82,7 -74,7 +78,7 @@@
   * timelimit, fraglimit and game_starttime! Requires engine changes (remove STAT_TIMELIMIT
   * and STAT_FRAGLIMIT to be auto-sent)
   */
 float previous_game_starttime;
+ float previous_game_starttime;
  void Announcer_Gamestart()
  {
        float startTime = STAT(GAMESTARTTIME);
                                setthink(announcer_countdown, Announcer_Countdown);
                        }
  
-                       // z411 set title
-                       if(time < STAT(GAMESTARTTIME) && !warmup_stage) {
-                               if(gametype == MAPINFO_TYPE_DUEL) {
+                       if(!warmup_stage && time < STAT(GAMESTARTTIME))
+                       {
+                               if (gametype.m_1v1)
+                               {
                                        entity pl1 = players.sort_next;
                                        entity pl2 = pl1.sort_next;
-                                       
-                                       centerprint_SetTitle(strcat(entcs_GetName(pl1.sv_entnum), " vs ", entcs_GetName(pl2.sv_entnum)));
-                               } else {
-                                       centerprint_SetTitle(MapInfo_Type_ToText(gametype));
+                                       string pl1_name = (pl1 && pl1.team != NUM_SPECTATOR ? entcs_GetName(pl1.sv_entnum) : "???");
+                                       string pl2_name = (pl2 && pl2.team != NUM_SPECTATOR ? entcs_GetName(pl2.sv_entnum) : "???");
+                                       float offset = stringwidth(pl2_name, true, hud_fontsize) - stringwidth(pl1_name, true, hud_fontsize) - 1;
+                                       centerprint_SetTitle(sprintf("^BG%s^BG%s%s", pl1_name, _("  vs  "), pl2_name), offset / 2); // Show duelers in 1v1 game mode
                                }
-                       }
-                       
-                       if(time + 5.0 < startTime) // if connecting to server while restart was active don't always play prepareforbattle
-                       if(time > announcer_countdown.nextthink && !warmup_stage && time < STAT(GAMESTARTTIME)) { // don't play it again if countdown was already going
-                               if(teamplay)
-                                       Local_Notification(MSG_ANNCE, ANNCE_PREPARE_TEAM);
                                else
-                                       Local_Notification(MSG_ANNCE, ANNCE_PREPARE);
+                                       centerprint_SetTitle(strcat("^BG", MapInfo_Type_ToText(gametype)), 0); // Show game type as title
+                               if(time + 5.0 < startTime) // if connecting to server while restart was active don't always play prepareforbattle
 -                                      Local_Notification(MSG_ANNCE, ANNCE_PREPARE);
++                                      if(teamplay)
++                                              Local_Notification(MSG_ANNCE, ANNCE_PREPARE_TEAM);
++                                      else
++                                              Local_Notification(MSG_ANNCE, ANNCE_PREPARE);
                        }
-                       
                        announcer_countdown.nextthink = startTime - floor(startTime - time + 0.5); //synchronize nextthink to startTime
                }
        }
@@@ -154,9 -145,25 +152,25 @@@ MACRO_EN
  
  void Announcer_Time()
  {
+       static bool warmup_stage_prev;
        if(intermission)
                return;
  
+       if (warmup_stage != warmup_stage_prev)
+       {
+               announcer_5min = announcer_1min = false;
+               warmup_stage_prev = warmup_stage;
+               return;
+       }
+       float starttime = STAT(GAMESTARTTIME);
+       if(time < starttime)
+       {
+               announcer_5min = announcer_1min = false;
+               return;
+       }
        float timeleft;
        if(warmup_stage)
        {
                        timeleft = 0;
        }
        else
-               timeleft = max(0, STAT(TIMELIMIT) * 60 + STAT(GAMESTARTTIME) - time);
+               timeleft = max(0, STAT(TIMELIMIT) * 60 + starttime - time);
  
        if(autocvar_cl_announcer_maptime >= 2)
                ANNOUNCER_CHECKMINUTE(5);
index 16d8c8b0c43bd32cb4014d51b1329b2a663f51f4,5a18bf388a4422cc60439a6c1642330b3849ca31..25e2815125c0adec6f3dc752f1572eff9f51d72a
@@@ -33,53 -33,20 +33,24 @@@ void HUD_CenterPrint_Export(int fh
  
  const int CENTERPRINT_MAX_MSGS = 10;
  const int CENTERPRINT_MAX_ENTRIES = 50;
- const float CENTERPRINT_SPACING = 0.7;
+ const float CENTERPRINT_BASE_SIZE = 1.4;
+ const float CENTERPRINT_SPACING = 0.5;
+ const float CENTERPRINT_TITLE_SPACING = 0.35;
  int cpm_index;
  string centerprint_messages[CENTERPRINT_MAX_MSGS];
  int centerprint_msgID[CENTERPRINT_MAX_MSGS];
- bool centerprint_bold[CENTERPRINT_MAX_MSGS];
  float centerprint_time[CENTERPRINT_MAX_MSGS];
+ float centerprint_start_time[CENTERPRINT_MAX_MSGS];
  float centerprint_expire_time[CENTERPRINT_MAX_MSGS];
  int centerprint_countdown_num[CENTERPRINT_MAX_MSGS];
  bool centerprint_showing;
  
- bool centerprint_title_show;
 +float centerprint_medal_expire_time;
 +string centerprint_medal_icon;
 +float centerprint_medal_times;
 +
  string centerprint_title;
- void centerprint_ClearTitle()
- {
-       centerprint_title = string_null;
-       centerprint_title_show = false;
- }
- void centerprint_SetTitle(string title)
- {
-       if(title != centerprint_title) {
-               if(centerprint_title)
-                       strunzone(centerprint_title);
-               centerprint_title = strzone(title);
-               
-               centerprint_title_show = true;
-       }
- }
- void centerprint_Medal(string icon, float times)
- {
-       if(!autocvar_hud_panel_centerprint_medals) return;
-       
-       //LOG_INFOF("centerprint_Medal: icon: %s times: %d", icon, times);
-       //centerprint_medal_expire_time = time + autocvar_hud_panel_centerprint_time;
-       centerprint_medal_expire_time = time + MSG_MEDAL_TIME;
-       centerprint_medal_times = times;
-       if(centerprint_medal_icon)
-               strunzone(centerprint_medal_icon);
-       centerprint_medal_icon = strzone(strcat("gfx/medal/", icon));
-       
-       centerprint_showing = true;
- }
+ float centerprint_title_offset;
  
  void centerprint_Add(int new_id, string strMessage, float duration, int countdown_num)
  {
        if(strMessage == "" && new_id == 0)
                return;
  
-       // strip BOLD_OPERATOR
-       bool is_bold = (substring(strMessage, 0, 5) == BOLD_OPERATOR);
-       if (is_bold)
-               strMessage = substring(strMessage, 5, -1);
        // strip trailing newlines
        j = strlen(strMessage) - 1;
        while(substring(strMessage, j, 1) == "\n" && j >= 0)
                }
        }
  
+       if (strMessage == "")
+               return;
        if (i == CENTERPRINT_MAX_MSGS)
        {
                // a msg with the same id was not found, add the msg at the next position
                j = cpm_index;
        }
        strcpy(centerprint_messages[j], strMessage);
-       centerprint_bold[j] = is_bold;
+       centerprint_start_time[j] = time;
        centerprint_msgID[j] = new_id;
        if (duration < 0)
        {
@@@ -176,14 -141,31 +145,43 @@@ void centerprint_KillAll(
  {
        for (int i=0; i<CENTERPRINT_MAX_MSGS; ++i)
        {
+               centerprint_start_time[i] = 0;
                centerprint_expire_time[i] = 0;
                centerprint_time[i] = 1;
                centerprint_msgID[i] = 0;
-               centerprint_bold[i] = false;
                strfree(centerprint_messages[i]);
        }
  }
  
 -              if(centerprint_title)
 -                      strfree(centerprint_title);
 -
 -              centerprint_title = strzone(CCR(title));
++void centerprint_Medal(string icon, float times)
++{
++      if(!autocvar_hud_panel_centerprint_medals) return;
++
++      //LOG_INFOF("centerprint_Medal: icon: %s times: %d", icon, times);
++      //centerprint_medal_expire_time = time + autocvar_hud_panel_centerprint_time;
++      centerprint_medal_expire_time = time + MSG_MEDAL_TIME;
++      centerprint_medal_times = times;
++      if(centerprint_medal_icon)
++              strunzone(centerprint_medal_icon);
++      centerprint_medal_icon = strzone(strcat("gfx/medal/", icon));
++
++      centerprint_showing = true;
++}
++
+ void centerprint_ClearTitle()
+ {
+       strfree(centerprint_title);
+       centerprint_title_offset = 0;
+ }
+ void centerprint_SetTitle(string title, float offset)
+ {
+       if(title != centerprint_title) {
++              strcpy(centerprint_title, CCR(title));
+               centerprint_title_offset = offset;
+       }
+ }
  float hud_configure_cp_generation_time;
  void HUD_CenterPrint()
  {
        {
                if(!autocvar_hud_panel_centerprint) return;
  
-               if(hud_configure_prev)
+               if(hud_configure_prev) {
+                       centerprint_ClearTitle();
                        centerprint_KillAll();
+               }
        }
        else
        {
                {
                        if(highlightedPanel == HUD_PANEL(CENTERPRINT))
                        {
+                               centerprint_SetTitle(sprintf(_("Title at %s"), seconds_tostring(hud_configure_cp_generation_time)), 0);
                                float r;
                                r = random();
                                if (r > 0.8)
                                        centerprint_Add(floor(r*1000), sprintf(_("^3Countdown message at time %s, seconds left: ^COUNT"), seconds_tostring(time)), 1, 10);
                                else if (r > 0.55)
-                                       centerprint_Add(0, sprintf(_("^1Multiline message at time %s that\n^1lasts longer than normal"), seconds_tostring(time)), 20, 0);
+                                       centerprint_Add(0, sprintf(_("^1Multiline message at time %s that\n^BOLDlasts longer than normal"), seconds_tostring(time)), 20, 0);
                                else
                                        centerprint_AddStandard(sprintf(_("Message at time %s"), seconds_tostring(time)));
                                hud_configure_cp_generation_time = time + 1 + random()*4;
                panel_size -= '2 2 0' * panel_bg_padding;
        }
  
-       int entries;
-       float height;
-       vector fontsize;
        int i, j, k, n, g;
-       float a, sz, align, current_msg_posY = 0, msg_size;
-       vector pos, mysize, newsize;
-       string ts;
+       float a = 1, sz, align, current_msg_posY = 0, msg_size;
+       vector pos;
+       vector cp_fontsize = hud_fontsize * CENTERPRINT_BASE_SIZE;
+       string ts = "";
        bool all_messages_expired = true;
  
        pos = panel_pos;
-       height = vid_conheight/50 * 4;
-       pos.y -= height;
++
+       if (autocvar_hud_panel_centerprint_flip)
+               pos.y += panel_size.y;
+       align = bound(0, autocvar_hud_panel_centerprint_align, 1);
 +      
 +      // z411 draw medals first
 +      if (autocvar_hud_panel_centerprint_medals && time < centerprint_medal_expire_time) {
++              float height = vid_conheight/50 * 4;
++              pos.y -= height;
++
 +              if(time < centerprint_medal_expire_time - MSG_MEDAL_FADE_TIME)
 +                      a = 1;
 +              else
 +                      a = (centerprint_medal_expire_time - time) / MSG_MEDAL_FADE_TIME;
 +              
 +              vector tmp_in = pos;
 +                      
-               mysize = draw_getimagesize(centerprint_medal_icon);
-               newsize = vec2(height*(mysize.x/mysize.y), height);
-               fontsize = '1 1 0' * (newsize.y/2);
++              vector mysize = draw_getimagesize(centerprint_medal_icon);
++              vector newsize = vec2(height*(mysize.x/mysize.y), height);
++              vector fontsize = '1 1 0' * (newsize.y/2);
 +              
 +              tmp_in.x += (panel_size.x - newsize.x) / 2; // center medal icon
 +              
 +              if(centerprint_medal_times < autocvar_hud_panel_centerprint_medals_max) {
 +                      tmp_in.x -= ((newsize.x * 1.1) * (centerprint_medal_times - 1) / 2);
 +                      for(int t = 0; t < centerprint_medal_times; t++) {
 +                              drawpic(tmp_in, centerprint_medal_icon, newsize, '1 1 1', a, DRAWFLAG_NORMAL);
 +                              tmp_in.x += newsize.x * 1.1;
 +                      }
 +              } else {
 +                      drawpic(tmp_in, centerprint_medal_icon, newsize, '1 1 1', a, DRAWFLAG_NORMAL);
 +                      tmp_in.x += newsize.x + fontsize.x * 0.25; // draw times next to it
 +                      tmp_in.y += (newsize.y - fontsize.y) / 2;
 +                      drawstring(tmp_in, ftos(centerprint_medal_times), fontsize, '1 1 1', a, DRAWFLAG_NORMAL);
 +              }
++
++              pos.y += height;
 +              
 +              all_messages_expired = false;
 +      }
-       
-       // continue with normal procedure
-       pos.y += height;
-       
-       // z411 title
-       if(centerprint_title_show) {
-               fontsize = '1 1 0' * vid_conheight/50 * autocvar_hud_panel_centerprint_fontscale * 1.5;
-               drawcolorcodedstring(pos + '0.5 0 0' * (panel_size.x - stringwidth(centerprint_title, true, fontsize)), centerprint_title, fontsize, 1, DRAWFLAG_NORMAL);
-               pos.y += fontsize.y + (fontsize.y / 4);
-               
+       // Show title if available
+       if(centerprint_title) {
+               vector fontsize = cp_fontsize * autocvar_hud_panel_centerprint_fontscale_title;
+               float width = stringwidth(centerprint_title, true, fontsize);
+               pos.x = panel_pos.x + (panel_size.x - width) * align;
+               if (autocvar_hud_panel_centerprint_flip)
+                       pos.y -= fontsize.y;
+               if (centerprint_title_offset && align == 0.5)
+                       pos.x += centerprint_title_offset * CENTERPRINT_BASE_SIZE * autocvar_hud_panel_centerprint_fontscale_title;
+               drawcolorcodedstring(pos, centerprint_title, fontsize, 1, DRAWFLAG_NORMAL);
+               if (autocvar_hud_panel_centerprint_flip)
+                       pos.y -= cp_fontsize.y * CENTERPRINT_TITLE_SPACING;
+               else
+                       pos.y += fontsize.y + (hud_fontsize.y * CENTERPRINT_TITLE_SPACING);
+               drawfill(pos, vec2(width, 1), '1 1 1', 1, DRAWFLAG_NORMAL);
+               if (autocvar_hud_panel_centerprint_flip)
+                       pos.y -= cp_fontsize.y * CENTERPRINT_TITLE_SPACING;
+               else
+                       pos.y += cp_fontsize.y * CENTERPRINT_TITLE_SPACING;
                all_messages_expired = false;
        }
-       
-       // continue with normal procedure this time
-       
-       if (autocvar_hud_panel_centerprint_flip)
-               pos.y += panel_size.y;
-       align = bound(0, autocvar_hud_panel_centerprint_align, 1);
        for (g=0, i=0, j=cpm_index; i<CENTERPRINT_MAX_MSGS; ++i, ++j)
        {
-               bool is_bold = centerprint_bold[j];
-               // entries = bound(1, floor(CENTERPRINT_MAX_ENTRIES * 4 * panel_size_y/panel_size_x), CENTERPRINT_MAX_ENTRIES);
-               // height = panel_size_y/entries;
-               // fontsize = '1 1 0' * height;
-               height = (is_bold) ? vid_conheight/50 * autocvar_hud_panel_centerprint_fontscale_bold : vid_conheight/50 * autocvar_hud_panel_centerprint_fontscale;
-               fontsize = '1 1 0' * height;
-               entries = bound(1, floor(panel_size.y/height), CENTERPRINT_MAX_ENTRIES);
                if (j == CENTERPRINT_MAX_MSGS)
                        j = 0;
                if (centerprint_expire_time[j] == -1)
  
                all_messages_expired = false;
  
+               if (time < centerprint_start_time[j]) continue;
+               float fade_in_time = autocvar_hud_panel_centerprint_fade_in;
+               float fade_out_time = autocvar_hud_panel_centerprint_fade_out;
+               if (centerprint_countdown_num[j]) {
+                       fade_in_time = 0;
+                       fade_out_time = 0;
+               }
                // fade
-               //if(centerprint_time[j] < 0)  // Expired but forced. Expire time is the fade-in time.
-               //      a = (time - centerprint_expire_time[j]) / max(0.0001, autocvar_hud_panel_centerprint_fade_in);
-               if(centerprint_time[j] < 0 || centerprint_expire_time[j] - autocvar_hud_panel_centerprint_fade_out > time)  // Regularily printed. Not fading out yet.
+               if(fade_in_time && centerprint_start_time[j] && time < centerprint_start_time[j] + fade_in_time) // Fade in
+                       a = (time - centerprint_start_time[j]) / fade_in_time;
+               else if(time < centerprint_expire_time[j] - fade_out_time || centerprint_time[j] < 0) // Regularily printed or forced
                        a = 1;
-               else // Expiring soon, so fade it out.
-                       a = (centerprint_expire_time[j] - time) / max(0.0001, autocvar_hud_panel_centerprint_fade_out);
+               else if(fade_out_time) // Expiring soon, so fade it out.
+                       a = (centerprint_expire_time[j] - time) / fade_out_time;
  
                if(centerprint_msgID[j] == ORDINAL(CPID_TIMEIN))
                        a = 1;
                // while counting down show it anyway in order to hold the current message position
                if (a <= 0.5/255.0 && centerprint_countdown_num[j] == 0)  // Guaranteed invisible - don't show.
                        continue;
-               if (a > 1)
-                       a = 1;
-               // set the size from fading in/out before subsequent fading
-               //sz = autocvar_hud_panel_centerprint_fade_minfontsize + a * (1 - autocvar_hud_panel_centerprint_fade_minfontsize);
  
                // also fade it based on positioning
                if(autocvar_hud_panel_centerprint_fade_subsequent)
                }
                a *= panel_fg_alpha;
  
-               // finally set the size based on the new alpha from subsequent fading
-               //sz = sz * (autocvar_hud_panel_centerprint_fade_subsequent_minfontsize + a * (1 - autocvar_hud_panel_centerprint_fade_subsequent_minfontsize));
-               sz = 1; // z411 disable ugly zoom shit
+               // finally set the size based on the alpha
+               sz = autocvar_hud_panel_centerprint_fade_minfontsize + a * (1 - autocvar_hud_panel_centerprint_fade_minfontsize);
                drawfontscale = hud_scale * sz;
  
                if (centerprint_countdown_num[j])
-                       n = tokenizebyseparator(strreplace("^COUNT", count_seconds(centerprint_countdown_num[j]), centerprint_messages[j]), "\n");
+                       n = tokenizebyseparator(strreplace("^COUNT", ftos(centerprint_countdown_num[j]), centerprint_messages[j]), "\n");
                else
                        n = tokenizebyseparator(centerprint_messages[j], "\n");
  
                                getWrappedLine_remaining = argv(k);
                                while(getWrappedLine_remaining)
                                {
+                                       bool is_bold = (substring(getWrappedLine_remaining, 0, 5) == BOLD_OPERATOR);
+                                       vector fontsize = cp_fontsize * (is_bold ? autocvar_hud_panel_centerprint_fontscale_bold : autocvar_hud_panel_centerprint_fontscale);
                                        ts = getWrappedLine(panel_size.x * hud_scale.x * sz, fontsize, stringwidth_colors);
                                        if (ts != "")
                                                pos.y -= fontsize.y;
                for(k = 0; k < n; ++k)
                {
                        getWrappedLine_remaining = argv(k);
+                       bool is_bold = (substring(getWrappedLine_remaining, 0, 5) == BOLD_OPERATOR);
+                       vector fontsize = cp_fontsize * (is_bold ? autocvar_hud_panel_centerprint_fontscale_bold : autocvar_hud_panel_centerprint_fontscale);
+                       if (is_bold)
+                               getWrappedLine_remaining = substring(getWrappedLine_remaining, 5, -1);
                        while(getWrappedLine_remaining)
                        {
                                ts = getWrappedLine(panel_size.x * hud_scale.x * sz, fontsize, stringwidth_colors);
                ++g; // move next position number up
  
                msg_size = pos.y - msg_size;
                if (autocvar_hud_panel_centerprint_flip)
                {
-                       pos.y = current_msg_posY - CENTERPRINT_SPACING * fontsize.y;
+                       pos.y -= msg_size + CENTERPRINT_SPACING * cp_fontsize.y;
                        if (a < 1 && centerprint_msgID[j] == 0) // messages with id can be replaced just after they are faded out, so never move over them the next messages
-                               pos.y += (msg_size + CENTERPRINT_SPACING * fontsize.y) * (1 - sqrt(sz));
+                               pos.y += (1 - sqrt(a));
  
                        if (pos.y < panel_pos.y) // check if the next message can be shown
                        {
                }
                else
                {
-                       pos.y += CENTERPRINT_SPACING * fontsize.y;
+                       pos.y += CENTERPRINT_SPACING * cp_fontsize.y;
                        if (a < 1 && centerprint_msgID[j] == 0) // messages with id can be replaced just after they are faded out, so never move over them the next messages
-                               pos.y -= (msg_size + CENTERPRINT_SPACING * fontsize.y) * (1 - sqrt(sz));
+                               pos.y -= (1 - sqrt(a));
  
-                       if(pos.y > panel_pos.y + panel_size.y - fontsize.y) // check if the next message can be shown
+                       if(pos.y > panel_pos.y + panel_size.y - cp_fontsize.y) // check if the next message can be shown
                        {
                                drawfontscale = hud_scale;
                                return;
index 1a1ce172df7ed69ffe7f281b8c209dd2d5d83af3,b1d52cf0adc06d4e14be1e837ac4f1ecd7dd7a41..73a2808223a4526c98d4030651051a66763fee4c
@@@ -3,29 -3,25 +3,29 @@@
  
  bool autocvar_hud_panel_centerprint;
  float autocvar_hud_panel_centerprint_align;
- //float autocvar_hud_panel_centerprint_fade_in = 0.15;
 -float autocvar_hud_panel_centerprint_fade_in = 0.15;
++float autocvar_hud_panel_centerprint_fade_in = 0;
  float autocvar_hud_panel_centerprint_fade_out = 0.15;
  float autocvar_hud_panel_centerprint_fade_subsequent = 1;
  float autocvar_hud_panel_centerprint_fade_subsequent_passone = 3;
  float autocvar_hud_panel_centerprint_fade_subsequent_passone_minalpha = 0.5;
  float autocvar_hud_panel_centerprint_fade_subsequent_passtwo = 10;
  float autocvar_hud_panel_centerprint_fade_subsequent_passtwo_minalpha = 0.5;
- //float autocvar_hud_panel_centerprint_fade_subsequent_minfontsize = 0.75;
- //float autocvar_hud_panel_centerprint_fade_minfontsize = 0;
+ float autocvar_hud_panel_centerprint_fade_minfontsize = 1;
  bool autocvar_hud_panel_centerprint_flip;
- float autocvar_hud_panel_centerprint_fontscale;
+ float autocvar_hud_panel_centerprint_fontscale = 1;
  float autocvar_hud_panel_centerprint_fontscale_bold = 1.4;
- bool autocvar_hud_panel_centerprint_dynamichud  = true;
+ float autocvar_hud_panel_centerprint_fontscale_title = 1.8;
+ bool autocvar_hud_panel_centerprint_dynamichud = true;
  float autocvar_hud_panel_centerprint_time;
  
- void centerprint_ClearTitle();
- void centerprint_SetTitle(string title);
- void centerprint_Medal(string icon, int times);
 +bool autocvar_hud_panel_centerprint_medals = true;
 +int autocvar_hud_panel_centerprint_medals_max = 5;
 +
  void centerprint_Add(int new_id, string strMessage, float duration, int countdown_num);
  void centerprint_AddStandard(string strMessage);
  void centerprint_Kill(int id);
  void centerprint_KillAll();
+ void centerprint_ClearTitle();
+ void centerprint_SetTitle(string title, float offset);
++void centerprint_Medal(string icon, int times);
index 7613347b62e134e00b8a75e87872465af1eb57b1,68e7312c7a5ba881ee1f45a000e91f64219ea7fb..51095ae1a8ed4673c4c267fe3e141d8109f2a497
@@@ -4,14 -4,38 +4,40 @@@
  #include <client/view.qh>
  
  // Timer (#5)
 +float last_timeleft;
 +int autocvar_cl_timer_countdown = 3; // 0 = disabled, 1 = always on, 2 = only spec, 3 = as dictated by server
  
  void HUD_Timer_Export(int fh)
  {
        // allow saving cvars that aesthetically change the panel into hud skin files
  }
  
+ vector HUD_Timer_Color(float timeleft)
+ {
+       if(timeleft <= 60)
+               return '1 0 0'; // red
+       else if(timeleft <= 300)
+               return '1 1 0'; // yellow
+       else
+               return '1 1 1'; // white
+ }
+ float HUD_Timer_TimeElapsed(float curtime, float starttime)
+ {
+       float time_elapsed = curtime - starttime;
+       if (!autocvar_hud_panel_timer_unbound)
+               time_elapsed = max(0, time_elapsed);
+       return floor(time_elapsed);
+ }
+ float HUD_Timer_TimeLeft(float curtime, float starttime, float timelimit)
+ {
+       float timeleft = timelimit + starttime - curtime;
+       if (!autocvar_hud_panel_timer_unbound)
+               timeleft = bound(0, timeleft, timelimit);
+       return ceil(timeleft);
+ }
  void HUD_Timer()
  {
        if(!autocvar__hud_configure)
                HUD_Scale_Enable();
        else
                HUD_Scale_Disable();
-       HUD_Panel_DrawBg();
        if(panel_bg_padding)
        {
                pos += '1 1 0' * panel_bg_padding;
                mySize -= '2 2 0' * panel_bg_padding;
        }
  
-       string timer_sub = "";
-       float timelimit, timeleft, minutesLeft, overtimes, timeout_last;
-       timelimit = STAT(TIMELIMIT);
-       overtimes = STAT(OVERTIMESADDED);
-       timeout_last = STAT(TIMEOUT_LAST);
+       string timer;
+       string subtimer = string_null;
+       string subtext = string_null;
+       float curtime, timelimit, timeleft;
+       vector timer_size, subtext_size, subtimer_size;
+       vector timer_color = '1 1 1';
+       vector subtimer_color = '1 1 1';
+       bool swap = (autocvar_hud_panel_timer_secondary == 2 && STAT(ROUNDSTARTTIME));
++      float timeout_last = STAT(TIMEOUT_LAST);
+       // Use real or frozen time and get the time limit
 -      curtime = (intermission_time ? intermission_time : time);
++      if(intermission_time)
++              curtime = intermission_time;
++      else if(timeout_last)
++              curtime = timeout_last;
++      else
++              curtime = time;
 +
-       if (autocvar_hud_panel_timer_unbound){
-               timeleft = max(0, timelimit * 60 + STAT(GAMESTARTTIME) - time);
-       } else {
-               timeleft = bound(0, timelimit * 60 + STAT(GAMESTARTTIME) - time, timelimit * 60);
+       if(warmup_stage)
+       {
+               timelimit = STAT(WARMUP_TIMELIMIT);
+               if(timelimit == 0)
+                       timelimit = STAT(TIMELIMIT) * 60;
+       }
+       else
+       {
+               timelimit = STAT(TIMELIMIT) * 60;
        }
-       timeleft = ceil(timeleft);
+       // Calculate time left
+       timeleft = HUD_Timer_TimeLeft(curtime, STAT(GAMESTARTTIME), timelimit);
+       // Timer color
+       if(!intermission_time && !warmup_stage && timelimit > 0)
+               timer_color = HUD_Timer_Color(timeleft);
  
-       if(countdown_type && !warmup_stage && timeleft > 0 && timeleft != last_timeleft && timeleft <= 10)
 +      // countdown sound
 +      // if 3 use server dictated option, otherwise the client's
 +      int countdown_type;
 +      if(autocvar_cl_timer_countdown == 3)
 +              countdown_type = sv_timer_countdown;
 +      else
 +              countdown_type = autocvar_cl_timer_countdown;
 +      
-       minutesLeft = floor(timeleft / 60);
++      if(countdown_type && !warmup_stage && timeleft > 0 && timeleft != last_timeleft && timeleft <= 10 && !intermission_time)
 +      {
 +              if(countdown_type == 1 || (countdown_type == 2 && spectatee_status))
 +                      sound(NULL, CH_INFO, SND_ENDCOUNT, VOL_BASE, ATTN_NONE);
 +              
 +              last_timeleft = timeleft;
 +      }
 +
+       // Timer text
+       if (autocvar_hud_panel_timer_increment || timelimit <= 0)
+               timer = seconds_tostring(HUD_Timer_TimeElapsed(curtime, STAT(GAMESTARTTIME)));
+       else
+               timer = seconds_tostring(timeleft);
  
-       float warmup_timeleft = 0;
-       if(warmup_stage)
+       // Secondary timer for round-based game modes
+       if(STAT(ROUNDSTARTTIME) && autocvar_hud_panel_timer_secondary)
        {
-               float warmup_timelimit = STAT(WARMUP_TIMELIMIT);
-               if(warmup_timelimit > 0)
-                       warmup_timeleft = max(0, warmup_timelimit - time + STAT(GAMESTARTTIME));
-               else if(warmup_timelimit == 0)
-                       warmup_timeleft = timeleft;
-               warmup_timeleft = ceil(warmup_timeleft);
-       }
+               if(STAT(ROUNDSTARTTIME) == -1) {
+                       // Round can't start
+                       subtimer = "--:--";
+                       subtimer_color = '1 0 0';
+               } else {
+                       float round_curtime, round_timelimit, round_timeleft;
  
-       vector timer_color;
-       if(intermission_time || minutesLeft >= 5 || warmup_stage || timelimit == 0)
-               timer_color = '1 1 1'; //white
-       else if(minutesLeft >= 1)
-               timer_color = '1 1 0'; //yellow
-       else
-               timer_color = '1 0 0'; //red
-       if (intermission_time) {
-               timer = max(0, floor(intermission_time - STAT(GAMESTARTTIME)));
-               timer_sub = "Intermission";
-       } else if (timeout_last) {
-               if(autocvar_hud_panel_timer_increment)
-                       timer = max(0, floor(timeout_last - STAT(GAMESTARTTIME)));
-               else
-                       timer = ceil(max(0, timelimit * 60 + STAT(GAMESTARTTIME) - timeout_last));
-               timer_sub = "Timeout";
-       } else if (autocvar_hud_panel_timer_increment || timelimit == 0) {
-               // Time elapsed timer
-               if((warmup_stage && warmup_timeleft <= 0) || time < STAT(GAMESTARTTIME))
-                       timer = 0;
-               else
-                       timer = floor(time - STAT(GAMESTARTTIME));
-       } else {
-               // Time left timer
-               if(warmup_stage) {
-                       if(warmup_timeleft <= 0)
-                               timer = floor(timelimit * 60);
+                       // Use real or frozen time and get the time limit
+                       round_curtime = (game_stopped_time ? game_stopped_time : time);
+                       round_timelimit = STAT(ROUND_TIMELIMIT);
+                       // Calculate time left
+                       round_timeleft = HUD_Timer_TimeLeft(round_curtime, STAT(ROUNDSTARTTIME), round_timelimit);
+                       // Subtimer color
+                       if(!intermission_time && round_timelimit > 0)
+                               subtimer_color = HUD_Timer_Color(round_timeleft);
+                       // Subtimer text
+                       if (autocvar_hud_panel_timer_increment || round_timelimit <= 0)
+                               subtimer = seconds_tostring(HUD_Timer_TimeElapsed(round_curtime, STAT(ROUNDSTARTTIME)));
                        else
-                               timer = warmup_timeleft;
-               } else {
-                       timer = timeleft;
+                               subtimer = seconds_tostring(round_timeleft);
                }
        }
-       
-       if(warmup_stage)
-               timer_sub = "Warmup";
+       // Subtext
+       int overtimes = STAT(OVERTIMES);
+       if(warmup_stage || autocvar__hud_configure)
+               subtext = _("Warmup");
+       else if(STAT(TIMEOUT_STATUS) == 2)
+               subtext = _("Timeout");
+       else if (overtimes == -1)
+               subtext = _("Sudden Death");
        else if(overtimes == 1)
-               timer_sub = "Overtime";
-       else if (overtimes > 1)
-               timer_sub = sprintf("Overtime #%d", overtimes);
-       
-       drawstring_aspect(pos, seconds_tostring(timer), mySize, timer_color, panel_fg_alpha, DRAWFLAG_NORMAL);
-       
-       if(timer_sub != "") {
-               pos.y += mySize.y;
-               mySize.y = mySize.y / 2;
-               drawstring_aspect(pos, timer_sub, mySize, '1 0 0', panel_fg_alpha, DRAWFLAG_NORMAL);
+               subtext = _("Overtime");
+       else if (overtimes >= 2)
+               subtext = sprintf(_("Overtime #%d"), overtimes);
+       subtext_size  = vec2(mySize.x, mySize.y / 3);
+       timer_size    = vec2(mySize.x, mySize.y - subtext_size.y);
+       subtimer_size = vec2(mySize.x / 3, mySize.y - subtext_size.y);
+       panel_size.y -= subtext_size.y;
+       HUD_Panel_DrawBg();
+       if(subtimer) {
+               float subtimer_padding = subtimer_size.y / 5;
+               timer_size.x -= subtimer_size.x;
+               drawstring_aspect(pos + eX * timer_size.x + eY * subtimer_padding, (swap ? timer : subtimer), subtimer_size - eY * subtimer_padding * 2, (swap ? timer_color : subtimer_color), panel_fg_alpha, DRAWFLAG_NORMAL);
        }
  
+       drawstring_aspect(pos, (swap ? subtimer : timer), timer_size, (swap ? subtimer_color : timer_color), panel_fg_alpha, DRAWFLAG_NORMAL);
+       if(subtext)
+               drawstring_aspect(pos + eY * timer_size.y, subtext, subtext_size, '0 1 0', panel_fg_alpha, DRAWFLAG_NORMAL);
        draw_endBoldFont();
  }
diff --combined qcsrc/client/main.qc
index 4aa29e380a593cae960d21d6a64479a64c855643,8165f4cead3f76540654bef10b0eaf3bc713885c..e854e7a99980df730543d9ded3f9112345bfaee1
@@@ -98,9 -98,6 +98,9 @@@ void CSQC_Init(
        registercvar("cl_weapon_switch_fallback_to_impulse", "1");
  
        registercvar("cl_allow_uidranking", "1");
 +      
 +      // z411
 +      registercvar("cl_chat_sounds", "1");
  
        if(autocvar_cl_lockview)
                cvar_set("cl_lockview", "0");
@@@ -474,6 -471,32 +474,32 @@@ float CSQC_InputEvent(int bInputType, f
        if(override)
                return true;
  
+       if(bInputType == 3 || bInputType == 2)
+               return false;
+       // at this point bInputType can only be 0 or 1 (key pressed or released)
+       bool key_pressed = (bInputType == 0);
+       if(key_pressed) {
+               if(nPrimary == K_ALT) hudShiftState |= S_ALT;
+               if(nPrimary == K_CTRL) hudShiftState |= S_CTRL;
+               if(nPrimary == K_SHIFT) hudShiftState |= S_SHIFT;
+       }
+       else {
+               if(nPrimary == K_ALT) hudShiftState -= (hudShiftState & S_ALT);
+               if(nPrimary == K_CTRL) hudShiftState -= (hudShiftState & S_CTRL);
+               if(nPrimary == K_SHIFT) hudShiftState -= (hudShiftState & S_SHIFT);
+       }
+       if (nPrimary == K_ESCAPE && !(hudShiftState & S_SHIFT) && key_pressed)
+       {
+               if (!isdemo() && cvar("_menu_gamemenu_dialog_available"))
+               {
+                       localcmd("\nmenu_showgamemenudialog\n");
+                       return true;
+               }
+       }
        return false;
  }
  
@@@ -738,8 -761,6 +764,8 @@@ NET_HANDLE(ENT_CLIENT_RANDOMSEED, bool 
  NET_HANDLE(ENT_CLIENT_ACCURACY, bool isnew)
  {
        make_pure(this);
 +      float entnum = ReadByte();
 +      
        int sf = ReadInt24_t();
        if (sf == 0) {
                for (int w = 0; w <= WEP_LAST - WEP_FIRST; ++w)
        int f = 1;
        for (int w = 0; w <= WEP_LAST - WEP_FIRST; ++w) {
                if (sf & f) {
 -                      int b = ReadByte();
 -                      if (b == 0)
 -                              weapon_accuracy[w] = -1;
 -                      else if (b == 255)
 -                              weapon_accuracy[w] = 1.0; // no better error handling yet, sorry
 -                      else
 -                              weapon_accuracy[w] = (b - 1.0) / 100.0;
 +                      if(entnum > 0) {
 +                              playerslots[entnum-1].accuracy_frags[w] = ReadByte();
 +                              playerslots[entnum-1].accuracy_hit[w] = ReadShort();
 +                              playerslots[entnum-1].accuracy_cnt_hit[w] = ReadShort();
 +                              playerslots[entnum-1].accuracy_cnt_fired[w] = ReadShort();
 +                              
 +                              //LOG_INFOF("Duel stats ?/%d", playerslots[entnum-1].accuracy_cnt_fired[w]);
 +                      } else {
 +                              int b = ReadByte();
 +                              if (b == 0)
 +                                      weapon_accuracy[w] = -1;
 +                              else if (b == 255)
 +                                      weapon_accuracy[w] = 1.0; // no better error handling yet, sorry
 +                              else
 +                                      weapon_accuracy[w] = (b - 1.0) / 100.0;
 +                      }
                }
                f = (f == 0x800000) ? 1 : f * 2;
        }
@@@ -1025,6 -1037,7 +1051,7 @@@ void Fog_Force(
                localcmd(sprintf("\nfog %s\nr_fog_exp2 0\nr_drawfog 1\n", forcefog));
  }
  
+ bool net_handle_ServerWelcome();
  NET_HANDLE(ENT_CLIENT_SCORES_INFO, bool isnew)
  {
        make_pure(this);
                strcpy(teamscores_label(i), ReadString());
                teamscores_flags(i) = ReadByte();
        }
+       bool welcome_msg_too = ReadByte();
+       if (welcome_msg_too)
+               net_handle_ServerWelcome();
        return = true;
        Scoreboard_InitScores();
        Gamemode_Init();
@@@ -1066,11 -1082,6 +1096,11 @@@ NET_HANDLE(ENT_CLIENT_INIT, bool isnew
        serverflags = ReadByte();
  
        g_trueaim_minrange = ReadCoord();
 +      
 +      strcpy(hostname_full, ReadString());
 +      strcpy(motd_permanent, ReadString());
 +      
 +      sv_timer_countdown = ReadByte();
  
        return = true;
  
        if (!postinit) PostInit();
  }
  
 +NET_HANDLE(TE_CSQC_TEAMNAMES, bool isNew)
 +{
 +      teamname_red = strzone(ReadString());
 +      teamname_blue = strzone(ReadString());
 +      teamname_yellow = strzone(ReadString());
 +      teamname_pink = strzone(ReadString());
 +
 +      return = true;
 +}
 +
 +NET_HANDLE(TE_CSQC_CHATSOUND, bool isNew)
 +{
 +      string snd = ReadString();
 +      snd = strcat("chat/", snd, ".ogg");
 +      
 +      precache_sound(snd);
 +      _sound(NULL, CH_INFO, snd, VOL_BASE, ATTN_NONE);
 +
 +      return = true;
 +}
 +
  float GetSpeedUnitFactor(int speed_unit)
  {
        switch(speed_unit)
@@@ -1326,6 -1316,64 +1356,64 @@@ NET_HANDLE(TE_CSQC_WEAPONCOMPLAIN, boo
        }
  }
  
+ bool net_handle_ServerWelcome()
+ {
+       bool campaign = ReadByte();
+       if (campaign)
+       {
+               string campaign_title = ReadString();
+               int campaign_level = ReadByte();
+               string campaign_msg = ReadString();
+               string welcomedialog_args;
+               welcomedialog_args = strcat("HOSTNAME \"", campaign_title, "\"");
+               string key = getcommandkey(_("jump"), "+jump");
+               string msg = strcat(
+                       CCR("^F1"), sprintf(_("Level %d:"), campaign_level),
+                       sprintf(CCR(" ^BG%s\n^3\n"), campaign_msg),
+                       sprintf(CCR(_("^BGPress ^F2%s^BG to enter the game")), key));
+               msg = MakeConsoleSafe(strreplace("\n", "\\n", msg));
+               welcomedialog_args = strcat(welcomedialog_args, " WELCOME \"", msg, "\"");
+               localcmd("\nmenu_cmd directmenu Welcome ", welcomedialog_args, "\n");
+               return true;
+       }
+       bool force_centerprint = ReadByte();
+       string hostname = ReadString();
+       string ver = ReadString();
+       string modifications = ReadString();
+       string cache_mutatormsg = ReadString();
+       string mutator_msg = ReadString();
+       string motd = ReadString();
+       string msg = "";
+       msg = strcat(msg, ver);
+       msg = strcat(msg, "^8\n\n", strcat(_("Gametype:"), " ^1", MapInfo_Type_ToText(gametype)), "^8\n");
+       if(modifications != "")
+               msg = strcat(msg, "^8\n", _("Active modifications:"), " ^3", modifications, "^8\n");
+       if (cache_mutatormsg != "")
+               msg = strcat(msg, "\n\n^8", _("Special gameplay tips:"), " ^7", cache_mutatormsg);
+       msg = strcat(msg, mutator_msg); // trust that the mutator will do proper formatting
+       if (motd != "")
+               msg = strcat(msg, "\n\n^8", _("MOTD:"), " ^7", motd);
+       if (!force_centerprint && !isdemo() && cvar("_menu_welcome_dialog_available") && autocvar_cl_welcome_in_menu_dialog)
+       {
+               string welcomedialog_args;
+               welcomedialog_args = strcat("HOSTNAME \"", hostname, "\"");
+               msg = MakeConsoleSafe(strreplace("\n", "\\n", msg));
+               welcomedialog_args = strcat(welcomedialog_args, " WELCOME \"", msg, "\"");
+               localcmd("\nmenu_cmd directmenu Welcome ", welcomedialog_args, "\n");
+       }
+       else
+               centerprint_Add(ORDINAL(CPID_MOTD), strcat(hostname, "\n\n\n", msg), -1, 0);
+       return true;
+ }
+ NET_HANDLE(TE_CSQC_SERVERWELCOME, bool isNew)
+ {
+       return net_handle_ServerWelcome();
+ }
  string _getcommandkey(string cmd_name, string command, bool forcename)
  {
        string keys;
diff --combined qcsrc/client/main.qh
index 41fdb48e6ca778aeba0ed3b026a237924ef4a21e,56dae7f4f191d5216faf83e120c9d86447bd3444..d46db5fd059329aad625fde8adeb11cc26d562a4
@@@ -2,12 -2,6 +2,12 @@@
  
  #include <common/constants.qh>
  #include <common/weapons/_all.qh>
 +#include <common/items/inventory.qh>
 +
 +// z411
 +string hostname_full;
 +string motd_permanent;
 +int sv_timer_countdown;
  
  bool autocvar_cl_db_saveasdump;
  bool autocvar_cl_spawn_event_particles;
@@@ -25,6 -19,7 +25,7 @@@ bool autocvar__hud_showbinds_reload
  bool autocvar_developer_csqcentities;
  bool autocvar_cl_race_cptimes_onlyself; // TODO: move to race gamemode
  bool autocvar_cl_race_cptimes_showself = false;
+ bool autocvar_cl_welcome_in_menu_dialog = true;
  
  // Map coordinate base calculations need these
  vector mi_center;
@@@ -160,16 -155,8 +161,16 @@@ string GetSpeedUnit(int speed_unit)
  .int enttype; // entity type sent from server
  .int sv_entnum; // entity number sent from server
  
 +// z411 accuracy info
 +.float accuracy_frags[REGISTRY_MAX(Weapons)];
 +.float accuracy_hit[REGISTRY_MAX(Weapons)];
 +.float accuracy_cnt_hit[REGISTRY_MAX(Weapons)];
 +.float accuracy_cnt_fired[REGISTRY_MAX(Weapons)];
 +
  .int team;
  .int team_size;
 +.int countrycode;
 +.string rank;
  
  int binddb;
  
@@@ -196,4 -183,3 +197,4 @@@ float serverprevtime, serverdeltatime
  float ticrate;
  
  int serverflags;
 +
diff --combined qcsrc/client/view.qc
index 06fe74a2076c04e10051d3e7a9e2d256f801790c,0399ef87b1d9d7cf9531168c4ac9bf632c5c061d..26a290991bbfce7e98e6f2dd83707fdeb197c226
@@@ -851,7 -851,7 +851,7 @@@ void HitSound(
        float kill_time = STAT(KILL_TIME);
        if (COMPARE_INCREASING(kill_time, kill_time_prev) > autocvar_cl_hitsound_antispam_time)
        {
 -              sound(NULL, CH_INFO, SND_KILL, VOL_BASE, ATTN_NONE);
 +              sound(NULL, CH_INFO, SND_KILL, VOL_BASE * 1.15, ATTN_NONE);
                kill_time_prev = kill_time;
        }
  }
@@@ -916,7 -916,6 +916,7 @@@ void HUD_Draw(entity this
        UpdateDamage();
        HUD_Crosshair(this);
        HitSound();
 +      Local_Notification_Queue_Process();
  }
  
  void ViewLocation_Mouse()
@@@ -1155,12 -1154,6 +1155,12 @@@ void HUD_Damage(
                        myhealth_flash += autocvar_hud_damage_fade_rate * frametime; // dead
                }
        }
 +      
 +      if(myhealth_prev > 1 && myhealth <= 0 && !intermission)
 +      {
 +              // Just died
 +              sound(NULL, CH_INFO, SND_DEATH, VOL_BASE, ATTN_NONE);
 +      }
  
        if(spectatee_status == -1 || intermission)
        {
@@@ -1646,6 -1639,11 +1646,11 @@@ void CSQC_UpdateView(entity this, floa
        if(intermission && !intermission_time)
                intermission_time = time;
  
+       if(STAT(GAME_STOPPED) && !game_stopped_time)
+               game_stopped_time = time;
+       else if(game_stopped_time && !STAT(GAME_STOPPED))
+               game_stopped_time = 0;
        if(intermission && !isdemo() && !(calledhooks & HOOK_END))
        {
                if(calledhooks & HOOK_START)
diff --combined qcsrc/common/ent_cs.qc
index 06d9440ad548bcc0334cfd0b7f61f8f472b55079,5366371458fe52fadb86e3c2d7fc3dd8b2ca44db..a87c2764e5d75c850508d5e903d73bb583f0833e
@@@ -151,31 -151,12 +151,31 @@@ ENTCS_PROP(CLIENTCOLORS, true, clientco
  ENTCS_PROP(FRAGS, true, frags, frags, ENTCS_SET_NORMAL,
        { WriteShort(chan, ent.frags); },
        { ent.frags = ReadShort(); })
 +      
 +ENTCS_PROP(COUNTRYCODE, true, countrycode, countrycode, ENTCS_SET_NORMAL,
 +      { WriteByte(chan, ent.countrycode); },
 +      { ent.countrycode = ReadByte(); })
 +
 +ENTCS_PROP(RANK, true, rank, rank, ENTCS_SET_NORMAL,
 +      { WriteString(chan, ent.rank); },
 +      { strcpy(ent.rank, ReadString()); })
  
  // use sv_solid to avoid changing solidity state of entcs entities
  ENTCS_PROP(SOLID, true, sv_solid, solid, ENTCS_SET_NORMAL,
        { WriteByte(chan, ent.sv_solid); },
        { ent.sv_solid = ReadByte(); })
  
- ENTCS_PROP(MMM_STATUS, true, mmm_status, mmm_status, ENTCS_SET_NORMAL,
-       { WriteShort(chan, ent.mmm_status); },
-       { ent.mmm_status = ReadShort(); })
 +// z411 weapon
 +ENTCS_PROP(ACTIVEWEPID, false, activewepid, activewepid, ENTCS_SET_NORMAL,
 +      { WriteByte(chan, ent.activewepid); },
 +      { ent.activewepid = ReadByte(); })
 +
 +//LegendGuard adds ENTCS_PROP for MMM 20-02-2021
 +// gamemode specific player mmm status (independent of score and frags)
++//ENTCS_PROP(MMM_STATUS, true, mmm_status, mmm_status, ENTCS_SET_NORMAL,
++//    { WriteShort(chan, ent.mmm_status); },
++//    { ent.mmm_status = ReadShort(); })
 +
  #ifdef SVQC
  
        int ENTCS_PUBLICMASK = 0, ENTCS_PRIVATEMASK = 0;
index 96ba25f11f044a0260c8e1bef6114eabfd53df0c,a33ec87a01a34a5a8406f57aa3c3d52829cf3994..0d8f0f395db97f9ef57b298c25cc2cb7193b1333
  #include <common/gamemodes/gamemode/keepaway/_mod.inc>
  #include <common/gamemodes/gamemode/keyhunt/_mod.inc>
  #include <common/gamemodes/gamemode/lms/_mod.inc>
- #include <common/gamemodes/gamemode/mmm/_mod.inc> //LegendGuard adds _mod.inc for Murder in Megaerebus Manor 20-02-2021
++//#include <common/gamemodes/gamemode/mmm/_mod.inc>
 +#include <common/gamemodes/gamemode/mayhem/_mod.inc>
  #include <common/gamemodes/gamemode/nexball/_mod.inc>
  #include <common/gamemodes/gamemode/onslaught/_mod.inc>
  #include <common/gamemodes/gamemode/race/_mod.inc>
  #include <common/gamemodes/gamemode/tdm/_mod.inc>
 +#include <common/gamemodes/gamemode/tmayhem/_mod.inc>
index 25c9c119d02b761c0abbf6518beed5930a2381ca,ffd71d59d3f1092453b6d83f8048003693dfa531..9cffae953f6e9e84e175810afac69cc194fcac4d
  #include <common/gamemodes/gamemode/keepaway/_mod.qh>
  #include <common/gamemodes/gamemode/keyhunt/_mod.qh>
  #include <common/gamemodes/gamemode/lms/_mod.qh>
- #include <common/gamemodes/gamemode/mmm/_mod.qh> //LegendGuard adds _mod.qh for Murder in Megaerebus Manor 20-02-2021
++//#include <common/gamemodes/gamemode/mmm/_mod.qh>
 +#include <common/gamemodes/gamemode/mayhem/_mod.qh>
  #include <common/gamemodes/gamemode/nexball/_mod.qh>
  #include <common/gamemodes/gamemode/onslaught/_mod.qh>
  #include <common/gamemodes/gamemode/race/_mod.qh>
  #include <common/gamemodes/gamemode/tdm/_mod.qh>
 +#include <common/gamemodes/gamemode/tmayhem/_mod.qh>
diff --combined qcsrc/common/mapinfo.qh
index ab02af59bc55e768fd20a8e172415c8247641016,a7bf6ce0aab77fdcb4718c0f084998c30da9f517..6bfa910dd8b474c4324dd7d1af6a0fbc0da55348
@@@ -22,6 -22,7 +22,7 @@@ const int GAMETYPE_FLAG_PREFERRE
  const int GAMETYPE_FLAG_PRIORITY        = BIT(3); // priority selection when preferred gametype isn't available in random selections
  const int GAMETYPE_FLAG_HIDELIMITS      = BIT(4); // don't display a score limit needed for winning the match in the scoreboard
  const int GAMETYPE_FLAG_WEAPONARENA     = BIT(5); // gametype has a forced weapon arena, weapon arena mutators should disable themselves when this is set
+ const int GAMETYPE_FLAG_1V1             = BIT(6); // 1v1 gameplay
  
  int MAPINFO_TYPE_ALL;
  .int m_flags;
@@@ -44,6 -45,8 +45,8 @@@ CLASS(Gametype, Object
      ATTRIB(Gametype, m_hidelimits, bool, false);
      /** does this gametype enforce its own weapon arena? */
      ATTRIB(Gametype, m_weaponarena, bool, false);
+     /** 1v1 gameplay? */
+     ATTRIB(Gametype, m_1v1, bool, false);
      /** game type defaults */
      ATTRIB(Gametype, model2, string);
      /** game type description */
      /** game type priority in random selections */
      ATTRIB(Gametype, m_priority, int, 0);
  #ifdef CSQC
 -    ATTRIB(Gametype, m_modicons, void(vector pos, vector mySize));
 -    ATTRIB(Gametype, m_modicons_reset, void());
 -    ATTRIB(Gametype, m_modicons_export, void(int fh));
 +    //ATTRIB(Gametype, m_modicons, void(vector pos, vector mySize));
 +    //ATTRIB(Gametype, m_modicons_reset, void());
 +    //ATTRIB(Gametype, m_modicons_export, void(int fh));
 +      ATTRIB(Gametype, m_modscores, int(int team));
  #endif
  
      /** DO NOT USE, this is compatibility for legacy maps! */
          this.m_priority = ((gflags & GAMETYPE_FLAG_PREFERRED) ? 2 : ((gflags & GAMETYPE_FLAG_PRIORITY) ? 1 : 0));
          this.m_hidelimits = (gflags & GAMETYPE_FLAG_HIDELIMITS);
          this.m_weaponarena = (gflags & GAMETYPE_FLAG_WEAPONARENA);
+         this.m_1v1 = (gflags & GAMETYPE_FLAG_1V1);
  
          // same as `1 << m_id`
          MAPINFO_TYPE_ALL |= this.items = this.m_flags = (MAPINFO_TYPE_ALL + 1);
index e8e32cc06d50ad267f7d605ce82cd1b6b8febcc5,e1586c2034cd867b4a02d3e4fe995d8b3786d15a..a3429bf3291f83e5f4cfe37f2c2ccb65709f5e5d
@@@ -5,13 -5,9 +5,14 @@@ REGISTER_NET_TEMP(TE_CSQC_RACE
  REGISTER_NET_TEMP(TE_CSQC_TEAMNAGGER)
  REGISTER_NET_TEMP(TE_CSQC_PINGPLREPORT)
  REGISTER_NET_TEMP(TE_CSQC_WEAPONCOMPLAIN)
+ REGISTER_NET_TEMP(TE_CSQC_SERVERWELCOME)
  REGISTER_NET_TEMP(TE_CSQC_VEHICLESETUP)
  
 +REGISTER_NET_TEMP(TE_CSQC_TEAMNAMES)
 +REGISTER_NET_TEMP(TE_CSQC_CHATSOUND)
 +REGISTER_NET_TEMP(TE_CSQC_WEAPONPICKUP)
 +REGISTER_NET_TEMP(TE_CSQC_TOTALSHARDS) // for RJZ
 +
  const int RACE_NET_CHECKPOINT_HIT_QUALIFYING = 0; // byte checkpoint, short time, short recordtime, string recordholder
  const int RACE_NET_CHECKPOINT_CLEAR = 1;
  const int RACE_NET_CHECKPOINT_NEXT_QUALIFYING = 2; // byte nextcheckpoint, short recordtime, string recordholder
index 0b7b3e7c056c125d8a133080d036745cfe06dc15,d34e929cb1f1cdcd1ffd18093057a5505eaa27d7..128255010cbe53b887cfd3e36b5852026761ef90
  #define N___NEVER 0
  #define N_GNTLOFF 1
  #define N__ALWAYS 2
 +#define ANNCE_DEFTIME 2
  
 -#define MULTITEAM_ANNCE(prefix, defaultvalue, sound, channel, volume, position) \
 +#define MULTITEAM_ANNCE(prefix, defaultvalue, sound, channel, volume, position, queuetime) \
      NOTIF_ADD_AUTOCVAR(ANNCE_##prefix, defaultvalue) \
 -    MSG_ANNCE_NOTIF_TEAM(NUM_TEAM_1, prefix##_RED, prefix, defaultvalue, sprintf(sound, strtolower(STATIC_NAME_TEAM_1)), channel, volume, position) \
 -    MSG_ANNCE_NOTIF_TEAM(NUM_TEAM_2, prefix##_BLUE, prefix, defaultvalue, sprintf(sound, strtolower(STATIC_NAME_TEAM_2)), channel, volume, position) \
 -    MSG_ANNCE_NOTIF_TEAM(NUM_TEAM_3, prefix##_YELLOW, prefix, defaultvalue, sprintf(sound, strtolower(STATIC_NAME_TEAM_3)), channel, volume, position) \
 -    MSG_ANNCE_NOTIF_TEAM(NUM_TEAM_4, prefix##_PINK, prefix, defaultvalue, sprintf(sound, strtolower(STATIC_NAME_TEAM_4)), channel, volume, position)
 +    MSG_ANNCE_NOTIF_TEAM(NUM_TEAM_1, prefix##_RED, prefix, defaultvalue, sprintf(sound, strtolower(STATIC_NAME_TEAM_1)), channel, volume, position, queuetime) \
 +    MSG_ANNCE_NOTIF_TEAM(NUM_TEAM_2, prefix##_BLUE, prefix, defaultvalue, sprintf(sound, strtolower(STATIC_NAME_TEAM_2)), channel, volume, position, queuetime) \
 +    MSG_ANNCE_NOTIF_TEAM(NUM_TEAM_3, prefix##_YELLOW, prefix, defaultvalue, sprintf(sound, strtolower(STATIC_NAME_TEAM_3)), channel, volume, position, queuetime) \
 +    MSG_ANNCE_NOTIF_TEAM(NUM_TEAM_4, prefix##_PINK, prefix, defaultvalue, sprintf(sound, strtolower(STATIC_NAME_TEAM_4)), channel, volume, position, queuetime)
  
  // MSG_ANNCE_NOTIFICATIONS
 -    MSG_ANNCE_NOTIF(ACHIEVEMENT_AIRSHOT,        N_GNTLOFF, "airshot",           CH_INFO, VOL_BASEVOICE, ATTEN_NONE)
 -    MSG_ANNCE_NOTIF(ACHIEVEMENT_AMAZING,        N_GNTLOFF, "amazing",           CH_INFO, VOL_BASEVOICE, ATTEN_NONE)
 -    MSG_ANNCE_NOTIF(ACHIEVEMENT_AWESOME,        N_GNTLOFF, "awesome",           CH_INFO, VOL_BASEVOICE, ATTEN_NONE)
 -    MSG_ANNCE_NOTIF(ACHIEVEMENT_BOTLIKE,        N_GNTLOFF, "botlike",           CH_INFO, VOL_BASEVOICE, ATTEN_NONE)
 -    MSG_ANNCE_NOTIF(ACHIEVEMENT_ELECTROBITCH,   N__ALWAYS, "electrobitch",      CH_INFO, VOL_BASEVOICE, ATTEN_NONE)
 -    MSG_ANNCE_NOTIF(ACHIEVEMENT_IMPRESSIVE,     N_GNTLOFF, "impressive",        CH_INFO, VOL_BASEVOICE, ATTEN_NONE)
 -    MSG_ANNCE_NOTIF(ACHIEVEMENT_YODA,           N_GNTLOFF, "yoda",              CH_INFO, VOL_BASEVOICE, ATTEN_NONE)
 -
 -    MSG_ANNCE_NOTIF(BEGIN,                      N__ALWAYS, "begin",             CH_INFO, VOL_BASEVOICE, ATTEN_NONE)
 -
 -    MSG_ANNCE_NOTIF(HEADSHOT,                   N__ALWAYS, "headshot",          CH_INFO, VOL_BASEVOICE, ATTEN_NONE)
 -
 -    MSG_ANNCE_NOTIF(KILLSTREAK_03,              N_GNTLOFF, "03kills",           CH_INFO, VOL_BASEVOICE, ATTEN_NONE)
 -    MSG_ANNCE_NOTIF(KILLSTREAK_05,              N_GNTLOFF, "05kills",           CH_INFO, VOL_BASEVOICE, ATTEN_NONE)
 -    MSG_ANNCE_NOTIF(KILLSTREAK_10,              N_GNTLOFF, "10kills",           CH_INFO, VOL_BASEVOICE, ATTEN_NONE)
 -    MSG_ANNCE_NOTIF(KILLSTREAK_15,              N_GNTLOFF, "15kills",           CH_INFO, VOL_BASEVOICE, ATTEN_NONE)
 -    MSG_ANNCE_NOTIF(KILLSTREAK_20,              N_GNTLOFF, "20kills",           CH_INFO, VOL_BASEVOICE, ATTEN_NONE)
 -    MSG_ANNCE_NOTIF(KILLSTREAK_25,              N_GNTLOFF, "25kills",           CH_INFO, VOL_BASEVOICE, ATTEN_NONE)
 -    MSG_ANNCE_NOTIF(KILLSTREAK_30,              N_GNTLOFF, "30kills",           CH_INFO, VOL_BASEVOICE, ATTEN_NONE)
 -
 -    MSG_ANNCE_NOTIF(INSTAGIB_LASTSECOND,        N_GNTLOFF, "lastsecond",        CH_INFO, VOL_BASEVOICE, ATTEN_NONE)
 -    MSG_ANNCE_NOTIF(INSTAGIB_NARROWLY,          N_GNTLOFF, "narrowly",          CH_INFO, VOL_BASEVOICE, ATTEN_NONE)
 -    MSG_ANNCE_NOTIF(INSTAGIB_TERMINATED,        N_GNTLOFF, "terminated",        CH_INFO, VOL_BASEVOICE, ATTEN_NONE)
 -
 -    MSG_ANNCE_NOTIF(MULTIFRAG,                  N___NEVER, "multifrag",         CH_INFO, VOL_BASEVOICE, ATTEN_NONE)
 -
 -    MSG_ANNCE_NOTIF(NUM_1,                      N__ALWAYS, "1",                 CH_INFO, VOL_BASEVOICE, ATTEN_NONE)
 -    MSG_ANNCE_NOTIF(NUM_2,                      N__ALWAYS, "2",                 CH_INFO, VOL_BASEVOICE, ATTEN_NONE)
 -    MSG_ANNCE_NOTIF(NUM_3,                      N__ALWAYS, "3",                 CH_INFO, VOL_BASEVOICE, ATTEN_NONE)
 -    MSG_ANNCE_NOTIF(NUM_4,                      N__ALWAYS, "4",                 CH_INFO, VOL_BASEVOICE, ATTEN_NONE)
 -    MSG_ANNCE_NOTIF(NUM_5,                      N__ALWAYS, "5",                 CH_INFO, VOL_BASEVOICE, ATTEN_NONE)
 -    MSG_ANNCE_NOTIF(NUM_6,                      N__ALWAYS, "6",                 CH_INFO, VOL_BASEVOICE, ATTEN_NONE)
 -    MSG_ANNCE_NOTIF(NUM_7,                      N__ALWAYS, "7",                 CH_INFO, VOL_BASEVOICE, ATTEN_NONE)
 -    MSG_ANNCE_NOTIF(NUM_8,                      N__ALWAYS, "8",                 CH_INFO, VOL_BASEVOICE, ATTEN_NONE)
 -    MSG_ANNCE_NOTIF(NUM_9,                      N__ALWAYS, "9",                 CH_INFO, VOL_BASEVOICE, ATTEN_NONE)
 -    MSG_ANNCE_NOTIF(NUM_10,                     N__ALWAYS, "10",                CH_INFO, VOL_BASEVOICE, ATTEN_NONE)
 -
 -    MSG_ANNCE_NOTIF(NUM_GAMESTART_1,            N__ALWAYS, "1",                 CH_INFO, VOL_BASEVOICE, ATTEN_NONE)
 -    MSG_ANNCE_NOTIF(NUM_GAMESTART_2,            N__ALWAYS, "2",                 CH_INFO, VOL_BASEVOICE, ATTEN_NONE)
 -    MSG_ANNCE_NOTIF(NUM_GAMESTART_3,            N__ALWAYS, "3",                 CH_INFO, VOL_BASEVOICE, ATTEN_NONE)
 -    MSG_ANNCE_NOTIF(NUM_GAMESTART_4,            N__ALWAYS, "4",                 CH_INFO, VOL_BASEVOICE, ATTEN_NONE)
 -    MSG_ANNCE_NOTIF(NUM_GAMESTART_5,            N__ALWAYS, "5",                 CH_INFO, VOL_BASEVOICE, ATTEN_NONE)
 -    MSG_ANNCE_NOTIF(NUM_GAMESTART_6,            N___NEVER, "6",                 CH_INFO, VOL_BASEVOICE, ATTEN_NONE)
 -    MSG_ANNCE_NOTIF(NUM_GAMESTART_7,            N___NEVER, "7",                 CH_INFO, VOL_BASEVOICE, ATTEN_NONE)
 -    MSG_ANNCE_NOTIF(NUM_GAMESTART_8,            N___NEVER, "8",                 CH_INFO, VOL_BASEVOICE, ATTEN_NONE)
 -    MSG_ANNCE_NOTIF(NUM_GAMESTART_9,            N___NEVER, "9",                 CH_INFO, VOL_BASEVOICE, ATTEN_NONE)
 -    MSG_ANNCE_NOTIF(NUM_GAMESTART_10,           N___NEVER, "10",                CH_INFO, VOL_BASEVOICE, ATTEN_NONE)
 -
 -    MSG_ANNCE_NOTIF(NUM_KILL_1,                 N___NEVER, "1",                 CH_INFO, VOL_BASEVOICE, ATTEN_NONE)
 -    MSG_ANNCE_NOTIF(NUM_KILL_2,                 N___NEVER, "2",                 CH_INFO, VOL_BASEVOICE, ATTEN_NONE)
 -    MSG_ANNCE_NOTIF(NUM_KILL_3,                 N___NEVER, "3",                 CH_INFO, VOL_BASEVOICE, ATTEN_NONE)
 -    MSG_ANNCE_NOTIF(NUM_KILL_4,                 N___NEVER, "4",                 CH_INFO, VOL_BASEVOICE, ATTEN_NONE)
 -    MSG_ANNCE_NOTIF(NUM_KILL_5,                 N___NEVER, "5",                 CH_INFO, VOL_BASEVOICE, ATTEN_NONE)
 -    MSG_ANNCE_NOTIF(NUM_KILL_6,                 N___NEVER, "6",                 CH_INFO, VOL_BASEVOICE, ATTEN_NONE)
 -    MSG_ANNCE_NOTIF(NUM_KILL_7,                 N___NEVER, "7",                 CH_INFO, VOL_BASEVOICE, ATTEN_NONE)
 -    MSG_ANNCE_NOTIF(NUM_KILL_8,                 N___NEVER, "8",                 CH_INFO, VOL_BASEVOICE, ATTEN_NONE)
 -    MSG_ANNCE_NOTIF(NUM_KILL_9,                 N___NEVER, "9",                 CH_INFO, VOL_BASEVOICE, ATTEN_NONE)
 -    MSG_ANNCE_NOTIF(NUM_KILL_10,                N___NEVER, "10",                CH_INFO, VOL_BASEVOICE, ATTEN_NONE)
 -
 -    MSG_ANNCE_NOTIF(NUM_RESPAWN_1,              N___NEVER, "1",                 CH_INFO, VOL_BASEVOICE, ATTEN_NONE)
 -    MSG_ANNCE_NOTIF(NUM_RESPAWN_2,              N___NEVER, "2",                 CH_INFO, VOL_BASEVOICE, ATTEN_NONE)
 -    MSG_ANNCE_NOTIF(NUM_RESPAWN_3,              N___NEVER, "3",                 CH_INFO, VOL_BASEVOICE, ATTEN_NONE)
 -    MSG_ANNCE_NOTIF(NUM_RESPAWN_4,              N___NEVER, "4",                 CH_INFO, VOL_BASEVOICE, ATTEN_NONE)
 -    MSG_ANNCE_NOTIF(NUM_RESPAWN_5,              N___NEVER, "5",                 CH_INFO, VOL_BASEVOICE, ATTEN_NONE)
 -    MSG_ANNCE_NOTIF(NUM_RESPAWN_6,              N___NEVER, "6",                 CH_INFO, VOL_BASEVOICE, ATTEN_NONE)
 -    MSG_ANNCE_NOTIF(NUM_RESPAWN_7,              N___NEVER, "7",                 CH_INFO, VOL_BASEVOICE, ATTEN_NONE)
 -    MSG_ANNCE_NOTIF(NUM_RESPAWN_8,              N___NEVER, "8",                 CH_INFO, VOL_BASEVOICE, ATTEN_NONE)
 -    MSG_ANNCE_NOTIF(NUM_RESPAWN_9,              N___NEVER, "9",                 CH_INFO, VOL_BASEVOICE, ATTEN_NONE)
 -    MSG_ANNCE_NOTIF(NUM_RESPAWN_10,             N___NEVER, "10",                CH_INFO, VOL_BASEVOICE, ATTEN_NONE)
 -
 -    MSG_ANNCE_NOTIF(NUM_ROUNDSTART_1,           N__ALWAYS, "1",                 CH_INFO, VOL_BASEVOICE, ATTEN_NONE)
 -    MSG_ANNCE_NOTIF(NUM_ROUNDSTART_2,           N__ALWAYS, "2",                 CH_INFO, VOL_BASEVOICE, ATTEN_NONE)
 -    MSG_ANNCE_NOTIF(NUM_ROUNDSTART_3,           N__ALWAYS, "3",                 CH_INFO, VOL_BASEVOICE, ATTEN_NONE)
 -    MSG_ANNCE_NOTIF(NUM_ROUNDSTART_4,           N___NEVER, "4",                 CH_INFO, VOL_BASEVOICE, ATTEN_NONE)
 -    MSG_ANNCE_NOTIF(NUM_ROUNDSTART_5,           N___NEVER, "5",                 CH_INFO, VOL_BASEVOICE, ATTEN_NONE)
 -    MSG_ANNCE_NOTIF(NUM_ROUNDSTART_6,           N___NEVER, "6",                 CH_INFO, VOL_BASEVOICE, ATTEN_NONE)
 -    MSG_ANNCE_NOTIF(NUM_ROUNDSTART_7,           N___NEVER, "7",                 CH_INFO, VOL_BASEVOICE, ATTEN_NONE)
 -    MSG_ANNCE_NOTIF(NUM_ROUNDSTART_8,           N___NEVER, "8",                 CH_INFO, VOL_BASEVOICE, ATTEN_NONE)
 -    MSG_ANNCE_NOTIF(NUM_ROUNDSTART_9,           N___NEVER, "9",                 CH_INFO, VOL_BASEVOICE, ATTEN_NONE)
 -    MSG_ANNCE_NOTIF(NUM_ROUNDSTART_10,          N___NEVER, "10",                CH_INFO, VOL_BASEVOICE, ATTEN_NONE)
 -
 -    MSG_ANNCE_NOTIF(PREPARE,                    N__ALWAYS, "prepareforbattle",  CH_INFO, VOL_BASEVOICE, ATTEN_NONE)
 -
 -    MSG_ANNCE_NOTIF(REMAINING_FRAG_1,           N_GNTLOFF, "1fragleft",         CH_INFO, VOL_BASEVOICE, ATTEN_NONE)
 -    MSG_ANNCE_NOTIF(REMAINING_FRAG_2,           N_GNTLOFF, "2fragsleft",        CH_INFO, VOL_BASEVOICE, ATTEN_NONE)
 -    MSG_ANNCE_NOTIF(REMAINING_FRAG_3,           N_GNTLOFF, "3fragsleft",        CH_INFO, VOL_BASEVOICE, ATTEN_NONE)
 -
 -    MSG_ANNCE_NOTIF(REMAINING_MIN_1,            N__ALWAYS, "1minuteremains",    CH_INFO, VOL_BASEVOICE, ATTEN_NONE)
 -    MSG_ANNCE_NOTIF(REMAINING_MIN_5,            N__ALWAYS, "5minutesremain",    CH_INFO, VOL_BASEVOICE, ATTEN_NONE)
 -
 -    MSG_ANNCE_NOTIF(TIMEOUT,                    N__ALWAYS, "timeoutcalled",     CH_INFO, VOL_BASEVOICE, ATTEN_NONE)
 -
 -    MSG_ANNCE_NOTIF(VOTE_ACCEPT,                N__ALWAYS, "voteaccept",        CH_INFO, VOL_BASEVOICE, ATTEN_NONE)
 -    MSG_ANNCE_NOTIF(VOTE_CALL,                  N__ALWAYS, "votecall",          CH_INFO, VOL_BASEVOICE, ATTEN_NONE)
 -    MSG_ANNCE_NOTIF(VOTE_FAIL,                  N__ALWAYS, "votefail",          CH_INFO, VOL_BASEVOICE, ATTEN_NONE)
 +    MSG_ANNCE_NOTIF(ACHIEVEMENT_AIRSHOT,        N_GNTLOFF, "airshot",           CH_INFO, VOL_BASEVOICE, ATTEN_NONE, ANNCE_DEFTIME)
 +    MSG_ANNCE_NOTIF(ACHIEVEMENT_AMAZING,        N_GNTLOFF, "amazing",           CH_INFO, VOL_BASEVOICE, ATTEN_NONE, ANNCE_DEFTIME)
 +      MSG_ANNCE_NOTIF(ACHIEVEMENT_ASSIST,         N_GNTLOFF, "assist",            CH_INFO, VOL_BASEVOICE, ATTEN_NONE, ANNCE_DEFTIME)
 +      MSG_ANNCE_NOTIF(ACHIEVEMENT_AWESOME,        N_GNTLOFF, "awesome",           CH_INFO, VOL_BASEVOICE, ATTEN_NONE, ANNCE_DEFTIME)
 +      MSG_ANNCE_NOTIF(ACHIEVEMENT_DAMAGE,         N_GNTLOFF, "damage",            CH_INFO, VOL_BASEVOICE, ATTEN_NONE, ANNCE_DEFTIME)
 +      MSG_ANNCE_NOTIF(ACHIEVEMENT_DEFENSE,        N_GNTLOFF, "defense",           CH_INFO, VOL_BASEVOICE, ATTEN_NONE, ANNCE_DEFTIME)
 +      MSG_ANNCE_NOTIF(ACHIEVEMENT_EXCELLENT,      N_GNTLOFF, "excellent",         CH_INFO, VOL_BASEVOICE, ATTEN_NONE, ANNCE_DEFTIME)
 +    MSG_ANNCE_NOTIF(ACHIEVEMENT_BOTLIKE,        N_GNTLOFF, "botlike",           CH_INFO, VOL_BASEVOICE, ATTEN_NONE, ANNCE_DEFTIME)
 +    MSG_ANNCE_NOTIF(ACHIEVEMENT_ELECTROBITCH,   N__ALWAYS, "electrobitch",      CH_INFO, VOL_BASEVOICE, ATTEN_NONE, ANNCE_DEFTIME)
 +    MSG_ANNCE_NOTIF(ACHIEVEMENT_IMPRESSIVE,     N_GNTLOFF, "impressive",        CH_INFO, VOL_BASEVOICE, ATTEN_NONE, ANNCE_DEFTIME)
 +    MSG_ANNCE_NOTIF(ACHIEVEMENT_YODA,           N_GNTLOFF, "yoda",              CH_INFO, VOL_BASEVOICE, ATTEN_NONE, ANNCE_DEFTIME)
 +      MSG_ANNCE_NOTIF(ACHIEVEMENT_PERFECT,        N_GNTLOFF, "perfect",           CH_INFO, VOL_BASEVOICE, ATTEN_NONE, ANNCE_DEFTIME)
 +      MSG_ANNCE_NOTIF(ACHIEVEMENT_ACCURACY,       N_GNTLOFF, "accuracy",          CH_INFO, VOL_BASEVOICE, ATTEN_NONE, ANNCE_DEFTIME)
 +      MSG_ANNCE_NOTIF(ACHIEVEMENT_TELEFRAG,       N_GNTLOFF, "telefrag",          CH_INFO, VOL_BASEVOICE, ATTEN_NONE, ANNCE_DEFTIME)
 +
 +    MSG_ANNCE_NOTIF(BEGIN,                      N__ALWAYS, "begin",             CH_INFO, VOL_BASEVOICE, ATTEN_NONE, -1)
 +
 +    MSG_ANNCE_NOTIF(HEADSHOT,                   N__ALWAYS, "headshot",          CH_INFO, VOL_BASEVOICE, ATTEN_NONE, ANNCE_DEFTIME)
 +      MSG_ANNCE_NOTIF(HUMILIATION,                N__ALWAYS, "humiliation",       CH_INFO, VOL_BASEVOICE, ATTEN_NONE, ANNCE_DEFTIME)
 +
 +    MSG_ANNCE_NOTIF(KILLSTREAK_03,              N_GNTLOFF, "03kills",           CH_INFO, VOL_BASEVOICE, ATTEN_NONE, ANNCE_DEFTIME)
 +    MSG_ANNCE_NOTIF(KILLSTREAK_05,              N_GNTLOFF, "05kills",           CH_INFO, VOL_BASEVOICE, ATTEN_NONE, ANNCE_DEFTIME)
 +    MSG_ANNCE_NOTIF(KILLSTREAK_10,              N_GNTLOFF, "10kills",           CH_INFO, VOL_BASEVOICE, ATTEN_NONE, ANNCE_DEFTIME)
 +    MSG_ANNCE_NOTIF(KILLSTREAK_15,              N_GNTLOFF, "15kills",           CH_INFO, VOL_BASEVOICE, ATTEN_NONE, ANNCE_DEFTIME)
 +
 +    MSG_ANNCE_NOTIF(INSTAGIB_LASTSECOND,        N_GNTLOFF, "lastsecond",        CH_INFO, VOL_BASEVOICE, ATTEN_NONE, ANNCE_DEFTIME)
 +    MSG_ANNCE_NOTIF(INSTAGIB_NARROWLY,          N_GNTLOFF, "narrowly",          CH_INFO, VOL_BASEVOICE, ATTEN_NONE, ANNCE_DEFTIME)
 +    MSG_ANNCE_NOTIF(INSTAGIB_TERMINATED,        N_GNTLOFF, "terminated",        CH_INFO, VOL_BASEVOICE, ATTEN_NONE, ANNCE_DEFTIME)
 +
 +    MSG_ANNCE_NOTIF(MULTIFRAG,                  N_GNTLOFF, "multifrag",         CH_INFO, VOL_BASEVOICE, ATTEN_NONE, ANNCE_DEFTIME)
 +      MSG_ANNCE_NOTIF(FIRSTBLOOD,                 N_GNTLOFF, "firstblood",        CH_INFO, VOL_BASEVOICE, ATTEN_NONE, ANNCE_DEFTIME)
 +
 +    MSG_ANNCE_NOTIF(NUM_1,                      N__ALWAYS, "1",                 CH_INFO, VOL_BASEVOICE, ATTEN_NONE, -1)
 +    MSG_ANNCE_NOTIF(NUM_2,                      N__ALWAYS, "2",                 CH_INFO, VOL_BASEVOICE, ATTEN_NONE, -1)
 +    MSG_ANNCE_NOTIF(NUM_3,                      N__ALWAYS, "3",                 CH_INFO, VOL_BASEVOICE, ATTEN_NONE, -1)
 +    MSG_ANNCE_NOTIF(NUM_4,                      N__ALWAYS, "4",                 CH_INFO, VOL_BASEVOICE, ATTEN_NONE, -1)
 +    MSG_ANNCE_NOTIF(NUM_5,                      N__ALWAYS, "5",                 CH_INFO, VOL_BASEVOICE, ATTEN_NONE, -1)
 +    MSG_ANNCE_NOTIF(NUM_6,                      N__ALWAYS, "6",                 CH_INFO, VOL_BASEVOICE, ATTEN_NONE, -1)
 +    MSG_ANNCE_NOTIF(NUM_7,                      N__ALWAYS, "7",                 CH_INFO, VOL_BASEVOICE, ATTEN_NONE, -1)
 +    MSG_ANNCE_NOTIF(NUM_8,                      N__ALWAYS, "8",                 CH_INFO, VOL_BASEVOICE, ATTEN_NONE, -1)
 +    MSG_ANNCE_NOTIF(NUM_9,                      N__ALWAYS, "9",                 CH_INFO, VOL_BASEVOICE, ATTEN_NONE, -1)
 +    MSG_ANNCE_NOTIF(NUM_10,                     N__ALWAYS, "10",                CH_INFO, VOL_BASEVOICE, ATTEN_NONE, -1)
 +
 +    MSG_ANNCE_NOTIF(NUM_GAMESTART_1,            N__ALWAYS, "1",                 CH_INFO, VOL_BASEVOICE, ATTEN_NONE, -1)
 +    MSG_ANNCE_NOTIF(NUM_GAMESTART_2,            N__ALWAYS, "2",                 CH_INFO, VOL_BASEVOICE, ATTEN_NONE, -1)
 +    MSG_ANNCE_NOTIF(NUM_GAMESTART_3,            N__ALWAYS, "3",                 CH_INFO, VOL_BASEVOICE, ATTEN_NONE, -1)
 +    MSG_ANNCE_NOTIF(NUM_GAMESTART_4,            N__ALWAYS, "4",                 CH_INFO, VOL_BASEVOICE, ATTEN_NONE, -1)
 +    MSG_ANNCE_NOTIF(NUM_GAMESTART_5,            N__ALWAYS, "5",                 CH_INFO, VOL_BASEVOICE, ATTEN_NONE, -1)
 +    MSG_ANNCE_NOTIF(NUM_GAMESTART_6,            N___NEVER, "6",                 CH_INFO, VOL_BASEVOICE, ATTEN_NONE, -1)
 +    MSG_ANNCE_NOTIF(NUM_GAMESTART_7,            N___NEVER, "7",                 CH_INFO, VOL_BASEVOICE, ATTEN_NONE, -1)
 +    MSG_ANNCE_NOTIF(NUM_GAMESTART_8,            N___NEVER, "8",                 CH_INFO, VOL_BASEVOICE, ATTEN_NONE, -1)
 +    MSG_ANNCE_NOTIF(NUM_GAMESTART_9,            N___NEVER, "9",                 CH_INFO, VOL_BASEVOICE, ATTEN_NONE, -1)
 +    MSG_ANNCE_NOTIF(NUM_GAMESTART_10,           N___NEVER, "10",                CH_INFO, VOL_BASEVOICE, ATTEN_NONE, -1)
 +
 +    MSG_ANNCE_NOTIF(NUM_IDLE_1,                 N___NEVER, "1",                 CH_INFO, VOL_BASEVOICE, ATTEN_NONE, -1)
 +    MSG_ANNCE_NOTIF(NUM_IDLE_2,                 N___NEVER, "2",                 CH_INFO, VOL_BASEVOICE, ATTEN_NONE, -1)
 +    MSG_ANNCE_NOTIF(NUM_IDLE_3,                 N___NEVER, "3",                 CH_INFO, VOL_BASEVOICE, ATTEN_NONE, -1)
 +    MSG_ANNCE_NOTIF(NUM_IDLE_4,                 N___NEVER, "4",                 CH_INFO, VOL_BASEVOICE, ATTEN_NONE, -1)
 +    MSG_ANNCE_NOTIF(NUM_IDLE_5,                 N___NEVER, "5",                 CH_INFO, VOL_BASEVOICE, ATTEN_NONE, -1)
 +    MSG_ANNCE_NOTIF(NUM_IDLE_6,                 N___NEVER, "6",                 CH_INFO, VOL_BASEVOICE, ATTEN_NONE, -1)
 +    MSG_ANNCE_NOTIF(NUM_IDLE_7,                 N___NEVER, "7",                 CH_INFO, VOL_BASEVOICE, ATTEN_NONE, -1)
 +    MSG_ANNCE_NOTIF(NUM_IDLE_8,                 N___NEVER, "8",                 CH_INFO, VOL_BASEVOICE, ATTEN_NONE, -1)
 +    MSG_ANNCE_NOTIF(NUM_IDLE_9,                 N___NEVER, "9",                 CH_INFO, VOL_BASEVOICE, ATTEN_NONE, -1)
 +    MSG_ANNCE_NOTIF(NUM_IDLE_10,                N___NEVER, "10",                CH_INFO, VOL_BASEVOICE, ATTEN_NONE, -1)
 +
 +    MSG_ANNCE_NOTIF(NUM_KILL_1,                 N___NEVER, "1",                 CH_INFO, VOL_BASEVOICE, ATTEN_NONE, -1)
 +    MSG_ANNCE_NOTIF(NUM_KILL_2,                 N___NEVER, "2",                 CH_INFO, VOL_BASEVOICE, ATTEN_NONE, -1)
 +    MSG_ANNCE_NOTIF(NUM_KILL_3,                 N___NEVER, "3",                 CH_INFO, VOL_BASEVOICE, ATTEN_NONE, -1)
 +    MSG_ANNCE_NOTIF(NUM_KILL_4,                 N___NEVER, "4",                 CH_INFO, VOL_BASEVOICE, ATTEN_NONE, -1)
 +    MSG_ANNCE_NOTIF(NUM_KILL_5,                 N___NEVER, "5",                 CH_INFO, VOL_BASEVOICE, ATTEN_NONE, -1)
 +    MSG_ANNCE_NOTIF(NUM_KILL_6,                 N___NEVER, "6",                 CH_INFO, VOL_BASEVOICE, ATTEN_NONE, -1)
 +    MSG_ANNCE_NOTIF(NUM_KILL_7,                 N___NEVER, "7",                 CH_INFO, VOL_BASEVOICE, ATTEN_NONE, -1)
 +    MSG_ANNCE_NOTIF(NUM_KILL_8,                 N___NEVER, "8",                 CH_INFO, VOL_BASEVOICE, ATTEN_NONE, -1)
 +    MSG_ANNCE_NOTIF(NUM_KILL_9,                 N___NEVER, "9",                 CH_INFO, VOL_BASEVOICE, ATTEN_NONE, -1)
 +    MSG_ANNCE_NOTIF(NUM_KILL_10,                N___NEVER, "10",                CH_INFO, VOL_BASEVOICE, ATTEN_NONE, -1)
 +
 +    MSG_ANNCE_NOTIF(NUM_RESPAWN_1,              N___NEVER, "1",                 CH_INFO, VOL_BASEVOICE, ATTEN_NONE, -1)
 +    MSG_ANNCE_NOTIF(NUM_RESPAWN_2,              N___NEVER, "2",                 CH_INFO, VOL_BASEVOICE, ATTEN_NONE, -1)
 +    MSG_ANNCE_NOTIF(NUM_RESPAWN_3,              N___NEVER, "3",                 CH_INFO, VOL_BASEVOICE, ATTEN_NONE, -1)
 +    MSG_ANNCE_NOTIF(NUM_RESPAWN_4,              N___NEVER, "4",                 CH_INFO, VOL_BASEVOICE, ATTEN_NONE, -1)
 +    MSG_ANNCE_NOTIF(NUM_RESPAWN_5,              N___NEVER, "5",                 CH_INFO, VOL_BASEVOICE, ATTEN_NONE, -1)
 +    MSG_ANNCE_NOTIF(NUM_RESPAWN_6,              N___NEVER, "6",                 CH_INFO, VOL_BASEVOICE, ATTEN_NONE, -1)
 +    MSG_ANNCE_NOTIF(NUM_RESPAWN_7,              N___NEVER, "7",                 CH_INFO, VOL_BASEVOICE, ATTEN_NONE, -1)
 +    MSG_ANNCE_NOTIF(NUM_RESPAWN_8,              N___NEVER, "8",                 CH_INFO, VOL_BASEVOICE, ATTEN_NONE, -1)
 +    MSG_ANNCE_NOTIF(NUM_RESPAWN_9,              N___NEVER, "9",                 CH_INFO, VOL_BASEVOICE, ATTEN_NONE, -1)
 +    MSG_ANNCE_NOTIF(NUM_RESPAWN_10,             N___NEVER, "10",                CH_INFO, VOL_BASEVOICE, ATTEN_NONE, -1)
 +
 +    MSG_ANNCE_NOTIF(NUM_ROUNDSTART_1,           N__ALWAYS, "1",                 CH_INFO, VOL_BASEVOICE, ATTEN_NONE, -1)
 +    MSG_ANNCE_NOTIF(NUM_ROUNDSTART_2,           N__ALWAYS, "2",                 CH_INFO, VOL_BASEVOICE, ATTEN_NONE, -1)
 +    MSG_ANNCE_NOTIF(NUM_ROUNDSTART_3,           N__ALWAYS, "3",                 CH_INFO, VOL_BASEVOICE, ATTEN_NONE, -1)
 +    MSG_ANNCE_NOTIF(NUM_ROUNDSTART_4,           N___NEVER, "4",                 CH_INFO, VOL_BASEVOICE, ATTEN_NONE, -1)
 +    MSG_ANNCE_NOTIF(NUM_ROUNDSTART_5,           N___NEVER, "5",                 CH_INFO, VOL_BASEVOICE, ATTEN_NONE, -1)
 +    MSG_ANNCE_NOTIF(NUM_ROUNDSTART_6,           N___NEVER, "6",                 CH_INFO, VOL_BASEVOICE, ATTEN_NONE, -1)
 +    MSG_ANNCE_NOTIF(NUM_ROUNDSTART_7,           N___NEVER, "7",                 CH_INFO, VOL_BASEVOICE, ATTEN_NONE, -1)
 +    MSG_ANNCE_NOTIF(NUM_ROUNDSTART_8,           N___NEVER, "8",                 CH_INFO, VOL_BASEVOICE, ATTEN_NONE, -1)
 +    MSG_ANNCE_NOTIF(NUM_ROUNDSTART_9,           N___NEVER, "9",                 CH_INFO, VOL_BASEVOICE, ATTEN_NONE, -1)
 +    MSG_ANNCE_NOTIF(NUM_ROUNDSTART_10,          N___NEVER, "10",                CH_INFO, VOL_BASEVOICE, ATTEN_NONE, -1)
 +
 +    MSG_ANNCE_NOTIF(PREPARE,                    N__ALWAYS, "prepareforbattle",  CH_INFO, VOL_BASEVOICE, ATTEN_NONE, -1)
 +      MSG_ANNCE_NOTIF(PREPARE_TEAM,               N__ALWAYS, "prepareyourteam",   CH_INFO, VOL_BASEVOICE, ATTEN_NONE, -1)
 +
 +    MSG_ANNCE_NOTIF(REMAINING_FRAG_1,           N_GNTLOFF, "1fragleft",         CH_INFO, VOL_BASEVOICE, ATTEN_NONE, ANNCE_DEFTIME)
 +    MSG_ANNCE_NOTIF(REMAINING_FRAG_2,           N_GNTLOFF, "2fragsleft",        CH_INFO, VOL_BASEVOICE, ATTEN_NONE, ANNCE_DEFTIME)
 +    MSG_ANNCE_NOTIF(REMAINING_FRAG_3,           N_GNTLOFF, "3fragsleft",        CH_INFO, VOL_BASEVOICE, ATTEN_NONE, ANNCE_DEFTIME)
 +
 +    MSG_ANNCE_NOTIF(REMAINING_MIN_1,            N__ALWAYS, "1minuteremains",    CH_INFO, VOL_BASEVOICE, ATTEN_NONE, ANNCE_DEFTIME)
 +    MSG_ANNCE_NOTIF(REMAINING_MIN_5,            N__ALWAYS, "5minutesremain",    CH_INFO, VOL_BASEVOICE, ATTEN_NONE, ANNCE_DEFTIME)
 +
 +    MSG_ANNCE_NOTIF(TIMEOUT,                    N__ALWAYS, "timeoutcalled",     CH_INFO, VOL_BASEVOICE, ATTEN_NONE, -1)
 +      MSG_ANNCE_NOTIF(OVERTIME,                   N__ALWAYS, "overtime",          CH_INFO, VOL_BASEVOICE, ATTEN_NONE, ANNCE_DEFTIME)
 +      MSG_ANNCE_NOTIF(SUDDENDEATH,                N__ALWAYS, "suddendeath",       CH_INFO, VOL_BASEVOICE, ATTEN_NONE, ANNCE_DEFTIME)
 +      MSG_ANNCE_NOTIF(SUICIDE,                    N_GNTLOFF, "suicide",           CH_INFO, VOL_BASEVOICE, ATTEN_NONE, -1)
 +      MSG_ANNCE_NOTIF(ACCIDENT,                   N_GNTLOFF, "accident",          CH_INFO, VOL_BASEVOICE, ATTEN_NONE, -1)
 +
 +    MSG_ANNCE_NOTIF(VOTE_ACCEPT,                N__ALWAYS, "voteaccept",        CH_INFO, VOL_BASEVOICE, ATTEN_NONE, ANNCE_DEFTIME)
 +    MSG_ANNCE_NOTIF(VOTE_CALL,                  N__ALWAYS, "votecall",          CH_INFO, VOL_BASEVOICE, ATTEN_NONE, ANNCE_DEFTIME)
 +    MSG_ANNCE_NOTIF(VOTE_FAIL,                  N__ALWAYS, "votefail",          CH_INFO, VOL_BASEVOICE, ATTEN_NONE, ANNCE_DEFTIME)
 +      
 +      MSG_ANNCE_NOTIF(LEAD_GAINED,                N__ALWAYS, "leadgained",        CH_INFO, VOL_BASEVOICE, ATTEN_NONE, ANNCE_DEFTIME)
 +      MSG_ANNCE_NOTIF(LEAD_LOST,                  N__ALWAYS, "leadlost",          CH_INFO, VOL_BASEVOICE, ATTEN_NONE, ANNCE_DEFTIME)
 +      MSG_ANNCE_NOTIF(LEAD_TIED,                  N__ALWAYS, "leadtied",          CH_INFO, VOL_BASEVOICE, ATTEN_NONE, ANNCE_DEFTIME)
 +      
 +      MULTITEAM_ANNCE(ROUND_TEAM_WIN,                         N__ALWAYS, "round_win_%s",              CH_INFO, VOL_BASEVOICE, ATTEN_NONE, ANNCE_DEFTIME)
 +      
 +      MSG_ANNCE_NOTIF(TEAM_SCORES_TEAM,                       N__ALWAYS, "scores_team",               CH_INFO, VOL_BASEVOICE, ATTEN_NONE, ANNCE_DEFTIME)
 +      MSG_ANNCE_NOTIF(TEAM_SCORES_ENEMY,                      N__ALWAYS, "scores_enemy",              CH_INFO, VOL_BASEVOICE, ATTEN_NONE, ANNCE_DEFTIME)
 +      MULTITEAM_ANNCE(TEAM_SCORES,                            N__ALWAYS, "scores_%s",                 CH_INFO, VOL_BASEVOICE, ATTEN_NONE, ANNCE_DEFTIME)
 +      
 +      MSG_ANNCE_NOTIF(TEAM_LEADS_TEAM,                        N__ALWAYS, "leads_team",                CH_INFO, VOL_BASEVOICE, ATTEN_NONE, ANNCE_DEFTIME)
 +      MSG_ANNCE_NOTIF(TEAM_LEADS_ENEMY,                       N__ALWAYS, "leads_enemy",               CH_INFO, VOL_BASEVOICE, ATTEN_NONE, ANNCE_DEFTIME)
 +      MULTITEAM_ANNCE(TEAM_LEADS,                                     N__ALWAYS, "leads_%s",                  CH_INFO, VOL_BASEVOICE, ATTEN_NONE, ANNCE_DEFTIME)
 +      
 +      MULTITEAM_ANNCE(TEAM_WINS,                                      N__ALWAYS, "wins_%s",                   CH_INFO, VOL_BASEVOICE, ATTEN_NONE, ANNCE_DEFTIME)
 +      
 +      MSG_ANNCE_NOTIF(TEAM_LEADS_TIED,            N__ALWAYS, "teamstied",         CH_INFO, VOL_BASEVOICE, ATTEN_NONE, ANNCE_DEFTIME)
 +      MSG_ANNCE_NOTIF(ROUND_OVER,                 N__ALWAYS, "round_over",        CH_INFO, VOL_BASEVOICE, ATTEN_NONE, ANNCE_DEFTIME)
 +      MSG_ANNCE_NOTIF(ROUND_TIED,                 N__ALWAYS, "round_tied",        CH_INFO, VOL_BASEVOICE, ATTEN_NONE, ANNCE_DEFTIME)
 +      MSG_ANNCE_NOTIF(ALONE,                      N__ALWAYS, "alone",             CH_INFO, VOL_BASEVOICE, ATTEN_NONE, ANNCE_DEFTIME)
 +      
 +      MSG_ANNCE_NOTIF(CTF_PICKUP_YOU,             N__ALWAYS, "ctf_pickup_you",    CH_INFO, VOL_BASEVOICE, ATTEN_NONE, ANNCE_DEFTIME)
 +      MSG_ANNCE_NOTIF(CTF_PICKUP_TEAM,            N__ALWAYS, "ctf_pickup_team",   CH_INFO, VOL_BASEVOICE, ATTEN_NONE, ANNCE_DEFTIME)
 +      MSG_ANNCE_NOTIF(CTF_PICKUP_ENEMY,           N__ALWAYS, "ctf_pickup_enemy",  CH_INFO, VOL_BASEVOICE, ATTEN_NONE, ANNCE_DEFTIME)
 +      MULTITEAM_ANNCE(CTF_PICKUP,                                     N__ALWAYS, "ctf_pickup_%s",             CH_INFO, VOL_BASEVOICE, ATTEN_NONE, ANNCE_DEFTIME)
 +      
 +      MSG_ANNCE_NOTIF(CTF_RETURN_TEAM,            N__ALWAYS, "ctf_return_team",   CH_INFO, VOL_BASEVOICE, ATTEN_NONE, ANNCE_DEFTIME)
 +      MSG_ANNCE_NOTIF(CTF_RETURN_ENEMY,           N__ALWAYS, "ctf_return_enemy",  CH_INFO, VOL_BASEVOICE, ATTEN_NONE, ANNCE_DEFTIME)
 +      MULTITEAM_ANNCE(CTF_RETURN,                                     N__ALWAYS, "ctf_return_%s",             CH_INFO, VOL_BASEVOICE, ATTEN_NONE, ANNCE_DEFTIME)
 +
 +// MSG_MEDAL_NOTIFICATIONS
 +
 +#define MSG_MEDAL_TIME 2
 +#define MSG_MEDAL_FADE_TIME 0.5
 +
 +      MSG_MEDAL_NOTIF(AIRSHOT, N__ALWAYS,        "airshot",       ANNCE_ACHIEVEMENT_AIRSHOT)
 +      MSG_MEDAL_NOTIF(ASSIST, N__ALWAYS,         "assist",        ANNCE_ACHIEVEMENT_ASSIST)
 +      MSG_MEDAL_NOTIF(DAMAGE, N__ALWAYS,         "damage",        ANNCE_ACHIEVEMENT_DAMAGE)
 +      MSG_MEDAL_NOTIF(DEFENSE, N__ALWAYS,        "defense",       ANNCE_ACHIEVEMENT_DEFENSE)
 +      MSG_MEDAL_NOTIF(ELECTROBITCH, N__ALWAYS,   "electrobitch",  ANNCE_ACHIEVEMENT_ELECTROBITCH)
 +      MSG_MEDAL_NOTIF(EXCELLENT, N__ALWAYS,      "excellent",     ANNCE_ACHIEVEMENT_EXCELLENT)
 +      MSG_MEDAL_NOTIF(FIRSTBLOOD, N__ALWAYS,     "firstblood",    ANNCE_FIRSTBLOOD)
 +      MSG_MEDAL_NOTIF(HEADSHOT, N__ALWAYS,       "headshot",      ANNCE_HEADSHOT)
 +      MSG_MEDAL_NOTIF(HUMILIATION, N__ALWAYS,    "humiliation",   ANNCE_HUMILIATION)
 +      MSG_MEDAL_NOTIF(IMPRESSIVE, N__ALWAYS,     "impressive",    ANNCE_ACHIEVEMENT_IMPRESSIVE)
 +      MSG_MEDAL_NOTIF(YODA, N__ALWAYS,           "yoda",          ANNCE_ACHIEVEMENT_YODA)
 +      MSG_MEDAL_NOTIF(TELEFRAG, N__ALWAYS,       "telefrag",      ANNCE_ACHIEVEMENT_TELEFRAG)
 +      
 +      MSG_MEDAL_NOTIF(CAPTURE, N__ALWAYS,        "capture",       NULL)
 +      MSG_MEDAL_NOTIF(PERFECT, N__ALWAYS,        "perfect",       ANNCE_ACHIEVEMENT_PERFECT)
 +      MSG_MEDAL_NOTIF(ACCURACY, N__ALWAYS,       "accuracy",      ANNCE_ACHIEVEMENT_ACCURACY)
 +      
 +      MSG_MEDAL_NOTIF(KILLSTREAK_03, N__ALWAYS, "killstreak_03", ANNCE_KILLSTREAK_03)
 +      MSG_MEDAL_NOTIF(KILLSTREAK_05, N__ALWAYS, "killstreak_05", ANNCE_KILLSTREAK_05)
 +      MSG_MEDAL_NOTIF(KILLSTREAK_10, N__ALWAYS, "killstreak_10", ANNCE_KILLSTREAK_10)
 +      MSG_MEDAL_NOTIF(KILLSTREAK_15, N__ALWAYS, "killstreak_15", ANNCE_KILLSTREAK_15)
 +      
  
  #undef N___NEVER
  #undef N_GNTLOFF
@@@ -436,7 -355,6 +436,7 @@@ string multiteam_info_sprintf(string in
      MSG_INFO_NOTIF(FREEZETAG_SELF,                          N_CONSOLE,  1, 0, "s1", "",         "",     _("^BG%s^K1 froze themself"), "")
  
      MULTITEAM_INFO(ROUND_TEAM_WIN,                          N_CONSOLE,  0, 0, "", "",           "",     _("^TC^TT^BG team wins the round"), "", NAME)
 +      MULTITEAM_INFO(ROUND_TEAM_SCORES,                       N_CONSOLE,  0, 0, "", "",           "",     _("^TC^TT^BG scores"), "", NAME)
      MSG_INFO_NOTIF(ROUND_PLAYER_WIN,                        N_CONSOLE,  1, 0, "s1", "",         "",     _("^BG%s^BG wins the round"), "")
      MSG_INFO_NOTIF(ROUND_TIED,                              N_CONSOLE,  0, 0, "", "",           "",     _("^BGRound tied"), "")
      MSG_INFO_NOTIF(ROUND_OVER,                              N_CONSOLE,  0, 0, "", "",           "",     _("^BGRound over, there's no winner"), "")
      MSG_INFO_NOTIF(POWERUP_STRENGTH,                        N_CONSOLE,  1, 0, "s1", "s1",       "strength",       _("^BG%s^K1 picked up Strength"), "")
  
      MSG_INFO_NOTIF(QUIT_DISCONNECT,                         N_CHATCON,  1, 0, "s1", "",         "",             _("^BG%s^F3 disconnected"), "")
 -    MSG_INFO_NOTIF(QUIT_KICK_IDLING,                        N_CHATCON,  1, 1, "s1 f1", "",      "",             _("^BG%s^F3 was kicked after idling for %s seconds"), "")
 -    MSG_INFO_NOTIF(MOVETOSPEC_IDLING,                       N_CHATCON,  1, 1, "s1 f1", "",      "",             _("^BG%s^F3 was moved to^BG spectators^F3 after idling for %s seconds"), "")
 +      MSG_INFO_NOTIF(QUIT_KICK,                               N_CHATCON,  1, 0, "s1", "",         "",             _("^BG%s^F3 was kicked"), "")
 +    MSG_INFO_NOTIF(QUIT_KICK_IDLING,                        N_CHATCON,  1, 0, "s1", "",         "",             _("^BG%s^F3 was kicked for idling"), "")
 +    MSG_INFO_NOTIF(MOVETOSPEC_IDLING,                       N_CHATCON,  1, 0, "s1", "",         "",             _("^BG%s^F3 was moved to spectator for idling"), "")
      MSG_INFO_NOTIF(QUIT_KICK_SPECTATING,                    N_CONSOLE,  0, 0, "", "",           "",             _("^F2You were kicked from the server because you are a spectator and spectators aren't allowed at the moment."), "")
      MSG_INFO_NOTIF(QUIT_KICK_TEAMKILL,                      N_CHATCON,  1, 0, "s1", "",         "",             _("^BG%s^F3 was kicked for excessive teamkilling"), "")
      MSG_INFO_NOTIF(QUIT_SPECTATE,                           N_CHATCON,  1, 0, "s1", "",         "",             _("^BG%s^F3 is now^BG spectating"), "")
  
      MSG_INFO_NOTIF(TEAMCHANGE_LARGERTEAM,                   N_CONSOLE,  0, 0, "", "",           "",                     _("^BGYou cannot change to a larger team"), "")
      MSG_INFO_NOTIF(TEAMCHANGE_NOTALLOWED,                   N_CONSOLE,  0, 0, "", "",           "",                     _("^BGYou are not allowed to change teams"), "")
 +    //LegendGuard adds MSG_INFO_NOTIF for MMM 20-02-2021
 +    //LegendGuard adds N_CHATCON option 20-03-2021
 +    MSG_INFO_NOTIF(MMM_MURDERER,                            N_CHATCON,  0, 0, "", "",           "",                     _("^BGYou are ^K1Murderer^BG!"), "")
 +    MSG_INFO_NOTIF(MMM_MURDERER_WIN,                        N_CONSOLE,  0, 0, "", "",           "",                     _("^K1Murderers^BG win the round"), "")
 +    
 +    //LegendGuard adds N_CHATCON option 20-03-2021
 +    MSG_INFO_NOTIF(MMM_CIVILIAN,                            N_CHATCON,  0, 0, "", "",           "",                     _("^BGYou are ^F1Civilian^BG!"), "")
 +    MSG_INFO_NOTIF(MMM_CIVILIAN_WIN,                        N_CONSOLE,  0, 0, "", "",           "",                     _("^F1Civilians^BG win the round"), "")
 +    
 +    //LegendGuard adds N_CHATCON option 20-03-2021
 +    MSG_INFO_NOTIF(MMM_DETECTIVE,                           N_CHATCON,  0, 0, "", "",           "",                     _("^BGYou are ^4Detective^BG!"), "")
 +    MSG_INFO_NOTIF(MMM_WHOISDETECTIVE,                      N_CHATCON,  1, 0, "s1", "",         "",                     _("^BG%s is ^4Detective^BG!"), "")
 +    MSG_INFO_NOTIF(MMM_CORPSEDETECTION,                     N_CHATCON,  1, 0, "s1", "",         "",                     _("^BG%s"), "")
 +    MSG_INFO_NOTIF(MMM_KARMAWARNING,                        N_CHATCON,  0, 0, "", "",           "",                     _("^K1KARMA WARNING! ^3Here, have the Rifle!"), "")
  
      MSG_INFO_NOTIF(VERSION_BETA,                            N_CONSOLE,  2, 0, "s1 s2", "",      "",                     _("^F4NOTE: ^BGThe server is running ^F1Xonotic %s (beta)^BG, you have ^F2Xonotic %s"), "")
      MSG_INFO_NOTIF(VERSION_OLD,                             N_CHATCON,  2, 0, "s1 s2", "",      "",                     _("^F4NOTE: ^BGThe server is running ^F1Xonotic %s^BG, you have ^F2Xonotic %s"), "")
      MSG_CENTER_NOTIF(ASSAULT_DEFENDING,                 N_ENABLE,    0, 0, "",               CPID_ASSAULT_ROLE,      "0 0",  _("^BGYou are defending!"), "")
      MSG_CENTER_NOTIF(ASSAULT_OBJ_DESTROYED,             N_ENABLE,    0, 1, "f1time",         CPID_ASSAULT_ROLE,      "0 0",  _("^BGObjective destroyed in ^F4%s^BG!"), "")
  
-     MSG_CENTER_NOTIF(COUNTDOWN_BEGIN,                   N_ENABLE,    0, 0, "",               CPID_ROUND,             "2 0",  _("^F4Begin!"), "")
-     MSG_CENTER_NOTIF(COUNTDOWN_GAMESTART,               N_ENABLE,    0, 1, "",               CPID_ROUND,             "1 f1", _("^F4Game starts in ^COUNT"), "")
-     MSG_CENTER_NOTIF(COUNTDOWN_ROUNDSTART,              N_ENABLE,    0, 1, "",               CPID_ROUND,             "1 f1", _("^F4Round starts in ^COUNT"), "")
+     MSG_CENTER_NOTIF(COUNTDOWN_BEGIN,                   N_ENABLE,    0, 0, "",               CPID_ROUND,             "2 0",  BOLD(_("^BGBegin!")), "")
+     MSG_CENTER_NOTIF(COUNTDOWN_GAMESTART,               N_ENABLE,    0, 1, "",               CPID_ROUND,             "1 f1", strcat(_("^BGGame starts in"), "\n", BOLD("^COUNT")), "")
+     MSG_CENTER_NOTIF(COUNTDOWN_ROUNDSTART,              N_ENABLE,    0, 2, "f1",             CPID_ROUND,             "1 f2", strcat(_("^BGRound %s starts in"), "\n", BOLD("^COUNT")), "")
      MSG_CENTER_NOTIF(COUNTDOWN_ROUNDSTOP,               N_ENABLE,    0, 0, "",               CPID_ROUND,             "2 0",  _("^F4Round cannot start"), "")
  
      MSG_CENTER_NOTIF(ROUND_TIED,                        N_ENABLE,    0, 0, "",               CPID_ROUND,             "0 0",  _("^BGRound tied"), "")
  
      #define VERBOSE_MURDER(type) strcat(MURDER_##type, "^BG%s")
  
 -    #define MURDER_FRAG             BOLD(_("^K3%sYou fragged ^BG%s"))
 -    #define MURDER_FRAG2            BOLD(_("^K3%sYou scored against ^BG%s"))
 -    #define MURDER_FRAGGED          BOLD(_("^K1%sYou were fragged by ^BG%s"))
 -    #define MURDER_FRAGGED2         BOLD(_("^K1%sYou were scored against by ^BG%s"))
 +    #define MURDER_FRAG             strcat(BOLD_OPERATOR, _("^K3%sYou fragged ^BG%s"))
 +    #define MURDER_FRAG2            strcat(BOLD_OPERATOR, _("^K3%sYou scored against ^BG%s"))
 +      #define MURDER_FRAG3            strcat(BOLD_OPERATOR, _("^K3%sYou fragged ^BG%s"), "\n", "%s^BG place with %s")
 +      #define MURDER_FRAG4            strcat(BOLD_OPERATOR, _("^K3%sYou fragged ^BG%s"), "\n", "%s^BG with %s")
 +    #define MURDER_FRAGGED          _("^K1%sYou were fragged by ^BG%s")
 +    #define MURDER_FRAGGED2         _("^K1%sYou were scored against by ^BG%s")
      MSG_CENTER_NOTIF(DEATH_MURDER_FRAG,                   N_ENABLE,  1, 1, "spree_cen s1",               CPID_Null,  "0 0",  MURDER_FRAG,                    MURDER_FRAG2                   )
 +      MSG_CENTER_NOTIF(DEATH_MURDER_DM,                     N_ENABLE,  1, 2, "spree_cen s1 frag_pos f2",   CPID_Null,  "0 0",  MURDER_FRAG3, "")
 +      MSG_CENTER_NOTIF(DEATH_MURDER_DUEL,                   N_ENABLE,  2, 2, "spree_cen s1 s2 f2",         CPID_Null,  "0 0",  MURDER_FRAG4, "")
      MSG_CENTER_NOTIF(DEATH_MURDER_FRAGGED,                N_ENABLE,  1, 1, "spree_cen s1",               CPID_Null,  "0 0",  MURDER_FRAGGED,                 MURDER_FRAGGED2                )
      MSG_CENTER_NOTIF(DEATH_MURDER_FRAGGED_VERBOSE,        N_ENABLE,  1, 4, "spree_cen s1 frag_stats",    CPID_Null,  "0 0",  VERBOSE_MURDER(FRAGGED),        VERBOSE_MURDER(FRAGGED2)       )
      MSG_CENTER_NOTIF(DEATH_MURDER_FRAG_VERBOSE,           N_ENABLE,  1, 2, "spree_cen s1 frag_ping",     CPID_Null,  "0 0",  VERBOSE_MURDER(FRAG),           VERBOSE_MURDER(FRAG2)          )
  
      MULTITEAM_CENTER(ROUND_TEAM_LOSS,                   N_ENABLE,    0, 0, "",               CPID_ROUND,             "0 0",  _("^TC^TT^BG team loses the round"), "", NAME)
      MULTITEAM_CENTER(ROUND_TEAM_WIN,                    N_ENABLE,    0, 0, "",               CPID_ROUND,             "0 0",  _("^TC^TT^BG team wins the round"), "", NAME)
 +      MULTITEAM_CENTER(ROUND_TEAM_SCORES,                 N_ENABLE,    0, 0, "",               CPID_ROUND,             "0 0",  _("^TC^TT^BG scores"), "", NAME)
      MSG_CENTER_NOTIF(ROUND_PLAYER_WIN,                  N_ENABLE,    1, 0, "s1",             CPID_ROUND,             "0 0",  _("^BG%s^BG wins the round"), "")
  
      MSG_CENTER_NOTIF(FREEZETAG_SELF,                    N_ENABLE,    0, 0, "",               CPID_Null,              "0 0",  _("^K1You froze yourself"), "")
      MULTITEAM_CENTER(KEYHUNT_START,                     N_ENABLE,    0, 0, "",               CPID_KEYHUNT,           "0 0",  _("^BGYou are starting with the ^TC^TT Key"), "", KEY)
  
      MSG_CENTER_NOTIF(LMS_NOLIVES,                       N_ENABLE,    0, 0, "",               CPID_LMS,               "0 0",  _("^BGYou have no lives left, you must wait until the next match"), "")
+     MSG_CENTER_NOTIF(LMS_SPECWARN,                      N_ENABLE,    0, 0, "",               CPID_LMS,               "0 0",  _("^F4WARNING:^BG you can't rejoin this match after spectating.\nUse the same command again to spectate anyway."), "")
  
      MSG_CENTER_NOTIF(MISSING_TEAMS,                     N_ENABLE,    0, 1, "missing_teams",  CPID_MISSING_TEAMS,     "-1 0", _("^BGWaiting for players to join...\nNeed active players for: %s"), "")
      MSG_CENTER_NOTIF(MISSING_PLAYERS,                   N_ENABLE,    0, 1, "f1",             CPID_MISSING_PLAYERS,   "-1 0", _("^BGWaiting for %s player(s) to join..."), "")
 +      MSG_CENTER_NOTIF(MISSING_READY,                     N_ENABLE,    0, 0, "",               CPID_MISSING_READY,     "-1 0", _("^BGThe match will begin\nwhen more players are ready.\n\nPress ^F2F4^BG to get ready"), "")
  
      MSG_CENTER_NOTIF(INSTAGIB_DOWNGRADE,                N_ENABLE,    0, 0, "",               CPID_INSTAGIB_FINDAMMO, "5 0",  _("^BGYour weapon has been downgraded until you find some ammo!"), "")
      MSG_CENTER_NOTIF(INSTAGIB_FINDAMMO,                 N_ENABLE,    0, 0, "",               CPID_INSTAGIB_FINDAMMO, "1 9",  _("^F4^COUNT^BG left to find some ammo!"), "")
      MSG_CENTER_NOTIF(INSTAGIB_FINDAMMO_FIRST,           N_ENABLE,    0, 0, "",               CPID_INSTAGIB_FINDAMMO, "1 10", _("^BGGet some ammo or you'll be dead in ^F4^COUNT^BG!"), _("^BGGet some ammo! ^F4^COUNT^BG left!"))
      MSG_CENTER_NOTIF(INSTAGIB_LIVES_REMAINING,          N_ENABLE,    0, 1, "f1",             CPID_Null,              "0 0",  _("^F2Extra lives remaining: ^K1%s"), "")
  
-     MSG_CENTER_NOTIF(CAMPAIGN_MESSAGE,                  N_ENABLE,    1, 1, "f1 s1 join_key", CPID_CAMPAIGN_MESSAGE,  "-1 0", strcat(_("Level %s: "), "^BG%s\n^3\n", _("^BGPress ^F2%s^BG to enter the game")), "")
      MSG_CENTER_NOTIF(MOTD,                              N_ENABLE,    1, 0, "s1",             CPID_MOTD,              "-1 0", "^BG%s", "")
  
      MSG_CENTER_NOTIF(NIX_COUNTDOWN,                     N_ENABLE,    0, 2, "item_wepname",   CPID_NIX,               "1 f2", _("^F2^COUNT^BG until weapon change...\nNext weapon: ^F1%s"), "")
  
      MSG_CENTER_NOTIF(TIMEOUT_BEGINNING,                 N_ENABLE,    0, 1, "",               CPID_TIMEOUT,           "1 f1", _("^F4Timeout begins in ^COUNT"), "")
      MSG_CENTER_NOTIF(TIMEOUT_ENDING,                    N_ENABLE,    0, 1, "",               CPID_TIMEIN,            "1 f1", _("^F4Timeout ends in ^COUNT"), "")
 +      MSG_CENTER_NOTIF(TIMEOUT_ONGOING,                   N_ENABLE,    0, 0, "",               CPID_TIMEIN,            "1 f1", _("^F4Match paused"), "")
 +    //LegendGuard adds MSG_CENTER_NOTIF for MMM 20-02-2021
 +    MSG_CENTER_NOTIF(MMM_MURDERER,                      N_ENABLE,    0, 0, "",               CPID_MMM,               "5 0",  strcat(BOLD_OPERATOR, _("^BGYou are ^K1Murderer^BG! Kill all the civilians without raising suspicion!")), "")
 +    MSG_CENTER_NOTIF(MMM_MURDERER_WIN,                  N_ENABLE,    0, 0, "",               CPID_ROUND,             "0 0",  _("^K1Murderers^BG win the round"), "")
 +    
 +    MSG_CENTER_NOTIF(MMM_CIVILIAN,                      N_ENABLE,    0, 0, "",               CPID_MMM,               "5 0",  strcat(BOLD_OPERATOR, _("^BGYou are ^F1Civilian^BG! Try to find out who are murderers and survive until time is up!")), "")
 +    MSG_CENTER_NOTIF(MMM_CIVILIAN_WIN,                  N_ENABLE,    0, 0, "",               CPID_ROUND,             "0 0",  _("^F1Civilians^BG win the round"), "")
 +    
 +    MSG_CENTER_NOTIF(MMM_DETECTIVE,                        N_ENABLE,    0, 0, "",               CPID_MMM,               "5 0",  strcat(BOLD_OPERATOR, _("^BGYou are ^4Detective^BG! Find out who are murderers and protect the civilians!")), "")
 +    MSG_CENTER_NOTIF(MMM_CORPSEDETECTION,               N_ENABLE,    1, 0, "s1",             CPID_MMM,               "4 0",  strcat(BOLD_OPERATOR, _("^BG%s")), "")
  
      MSG_CENTER_NOTIF(JOIN_PREVENT_MINIGAME,             N_ENABLE,    0, 0, "",               CPID_Null,              "0 0",  _("^K1Cannot join given minigame session!"), "" )
  
index e6c0e23ded50a1229c072c4f8d73c4a52a1fc862,45259741b355aa7d703c0e4db40a276abb53be5e..78bbee97797c6247b2e8478d9320a1f2b376d067
@@@ -25,8 -25,6 +25,8 @@@ ENUMCLASS(MSG
        CASE(MSG, CHOICE)
        /** Kill centerprint message @deprecated */
        CASE(MSG, CENTER_KILL)
 +      /** Medal notification */
 +      CASE(MSG, MEDAL)
  ENUMCLASS_END(MSG)
  
  string Get_Notif_TypeName(MSG net_type)
                case MSG_CENTER: return "MSG_CENTER";
                case MSG_MULTI: return "MSG_MULTI";
                case MSG_CHOICE: return "MSG_CHOICE";
 +              case MSG_MEDAL: return "MSG_MEDAL";
                case MSG_CENTER_KILL: return "MSG_CENTER_KILL";
        }
        LOG_WARNF("Get_Notif_TypeName(%d): Improper net type!", ORDINAL(net_type));
        return "";
  }
 -
 +//LegendGuard adds CASE(CPID, MMM) after TIMEIN for MMM 20-02-2021
  ENUMCLASS(CPID)
        CASE(CPID, ASSAULT_ROLE)
        CASE(CPID, ROUND)
@@@ -64,9 -61,7 +64,8 @@@
        CASE(CPID, LMS)
        CASE(CPID, MISSING_TEAMS)
        CASE(CPID, MISSING_PLAYERS)
 +      CASE(CPID, MISSING_READY)
        CASE(CPID, INSTAGIB_FINDAMMO)
-       CASE(CPID, CAMPAIGN_MESSAGE)
        CASE(CPID, MOTD)
        CASE(CPID, NIX)
        CASE(CPID, ONSLAUGHT)
@@@ -77,7 -72,6 +76,7 @@@
        CASE(CPID, TEAMCHANGE)
        CASE(CPID, TIMEOUT)
        CASE(CPID, TIMEIN)
 +      CASE(CPID, MMM)
        CASE(CPID, VEHICLES)
        CASE(CPID, VEHICLES_OTHER)
        /** always last */
@@@ -130,8 -124,7 +129,8 @@@ void Create_Notification_Entity_Annce(e
                                                                                float channel,
                                                                                string snd,
                                                                                float vol,
 -                                                                              float position);
 +                                                                              float position,
 +                                                                              float queuetime);
  
  void Create_Notification_Entity_InfoCenter(entity notif,
                                                                                        float var_cvar,
@@@ -165,13 -158,6 +164,13 @@@ void Create_Notification_Entity_Choice(
                                                                                Notification optiona,
                                                                                Notification optionb);
  
 +void Create_Notification_Entity_Medal(entity notif,
 +                                                                              float var_cvar,
 +                                                                              string namestring,
 +                                                                              /* MSG_MEDAL */
 +                                                                              string icon,
 +                                                                              Notification anncename);
 +
  void Dump_Notifications(int fh, bool alsoprint);
  
  #define DEFAULT_FILENAME "notifications_dump.cfg"
@@@ -264,16 -250,10 +263,16 @@@ ENUMCLASS(NOTIF
        CASE(NOTIF, TEAM)
        /** send only to X team and their spectators, except for Y person and their spectators */
        CASE(NOTIF, TEAM_EXCEPT)
 +      /** send only to X team; don't include spectators */
 +      CASE(NOTIF, TEAM_ONLY)
 +      /** send to team X team except for Y person; don't include spectators */
 +      CASE(NOTIF, TEAM_ONLY_EXCEPT)
        /** send to everyone */
        CASE(NOTIF, ALL)
        /** send to everyone except X person and their spectators */
        CASE(NOTIF, ALL_EXCEPT)
 +      /** send to all spectators **/
 +      CASE(NOTIF, ALL_SPEC)
  ENUMCLASS_END(NOTIF)
  
  string Get_Notif_BroadcastName(NOTIF broadcast)
        {
                case NOTIF_ONE: return "NOTIF_ONE";
                case NOTIF_ONE_ONLY: return "NOTIF_ONE_ONLY";
 +              case NOTIF_ALL_SPEC: return "NOTIF_ALL_SPEC";
                case NOTIF_ALL_EXCEPT: return "NOTIF_ALL_EXCEPT";
                case NOTIF_ALL: return "NOTIF_ALL";
                case NOTIF_TEAM: return "NOTIF_TEAM";
                case NOTIF_TEAM_EXCEPT: return "NOTIF_TEAM_EXCEPT";
 +              case NOTIF_TEAM_ONLY: return "NOTIF_TEAM_ONLY";
 +              case NOTIF_TEAM_ONLY_EXCEPT: return "NOTIF_TEAM_ONLY_EXCEPT";
        }
        LOG_WARNF("Get_Notif_BroadcastName(%d): Improper broadcast!", broadcast);
        return "";
@@@ -408,19 -385,6 +407,19 @@@ const float NOTIF_MAX_ARGS = 7
  const float NOTIF_MAX_HUDARGS = 2;
  const float NOTIF_MAX_DURCNT = 2;
  
 +#ifdef CSQC
 +const int NOTIF_QUEUE_MAX = 10;
 +entity notif_queue_entity[NOTIF_QUEUE_MAX];
 +MSG notif_queue_type[NOTIF_QUEUE_MAX];
 +float notif_queue_time[NOTIF_QUEUE_MAX];
 +float notif_queue_f1[NOTIF_QUEUE_MAX];
 +
 +float notif_queue_next_time;
 +int notif_queue_length;
 +
 +void Local_Notification_Queue_Process();
 +#endif
 +
  string arg_slot[NOTIF_MAX_ARGS];
  
  const float ARG_CS_SV_HA = 1; // enabled on CSQC, SVQC, and Hudargs
@@@ -465,7 -429,7 +464,7 @@@ string BUFF_NAME(int i)
        ARG_CASE(ARG_CS,        "join_key",      getcommandkey(_("jump"), "+jump")) \
        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)) \
 -      /*ARG_CASE(ARG_CS,      "frag_pos",      ((Should_Print_Score_Pos(f1)) ? sprintf("\n^BG%s", Read_Score_Pos(f1)) : ""))*/ \
 +      ARG_CASE(ARG_CS,        "frag_pos",      notif_arg_frag_pos(f2)) \
        ARG_CASE(ARG_CS,        "spree_cen",     (autocvar_notification_show_sprees ? notif_arg_spree_cen(f1) : "")) \
        ARG_CASE(ARG_CS_SV,     "spree_inf",     (autocvar_notification_show_sprees ? notif_arg_spree_inf(1, input, s2, f2) : "")) \
        ARG_CASE(ARG_CS_SV,     "spree_end",     (autocvar_notification_show_sprees ? notif_arg_spree_inf(-1, "", "", f1) : "")) \
@@@ -491,73 -455,11 +490,73 @@@ MACRO_EN
        SPREE_ITEM(5, 05, _("RAGE! "), _("%s^K1 unlocked RAGE! %s^BG"), _("%s^K1 made FIVE SCORES IN A ROW! %s^BG")) \
        SPREE_ITEM(10, 10, _("MASSACRE! "), _("%s^K1 started a MASSACRE! %s^BG"), _("%s^K1 made TEN SCORES IN A ROW! %s^BG")) \
        SPREE_ITEM(15, 15, _("MAYHEM! "), _("%s^K1 executed MAYHEM! %s^BG"), _("%s^K1 made FIFTEEN SCORES IN A ROW! %s^BG")) \
 -      SPREE_ITEM(20, 20, _("BERSERKER! "), _("%s^K1 is a BERSERKER! %s^BG"), _("%s^K1 made TWENTY SCORES IN A ROW! %s^BG")) \
 -      SPREE_ITEM(25, 25, _("CARNAGE! "), _("%s^K1 inflicts CARNAGE! %s^BG"), _("%s^K1 made TWENTY FIVE SCORES IN A ROW! %s^BG")) \
 -      SPREE_ITEM(30, 30, _("ARMAGEDDON! "), _("%s^K1 unleashes ARMAGEDDON! %s^BG"), _("%s^K1 made THIRTY SCORES IN A ROW! %s^BG"))
  
  #ifdef CSQC
 +// z411 TODO : This actually doesn't work very well.
 +// This gets run before the client gets score updates so it works
 +// fine when you're playing (because frags get updated first)
 +// but it breaks a lot when you're spectating because
 +// we sometimes get the new frag info at different times
 +// (before or after we run this). A suggested fix would
 +// be to do this sorting and comparison in the server.
 +string notif_arg_frag_pos(int score)
 +{
 +      entity pl;
 +      int place = 1;
 +      string str, color, tail;
 +      bool tied = false;
 +      
 +      for(pl = players.sort_next; pl; pl = pl.sort_next) {
 +              if(pl.team == NUM_SPECTATOR) continue;
 +              if(pl.(scores(SP_SCORE)) == score) break;
 +              ++place;
 +      }
 +      
 +      entity prev = pl.sort_prev;
 +      entity next = pl.sort_next;
 +      if(prev && prev.(scores(SP_SCORE)) == score) {
 +              tied = true;
 +              --place; // We're tied always for the best place
 +      }
 +      if(next && next.(scores(SP_SCORE)) == score) {
 +              tied = true;
 +      }
 +      
 +      switch(place) {
 +              case 1:
 +                      color = "^4";
 +                      break;
 +              case 2:
 +                      color = "^1";
 +                      break;
 +              case 3:
 +                      color = "^3";
 +                      break;
 +              default:
 +                      color = "";
 +      }
 +      
 +      switch(place % 10) {
 +              case 1:
 +                      tail = "st";
 +                      break;
 +              case 2:
 +                      tail = "nd";
 +                      break;
 +              case 3:
 +                      tail = "rd";
 +                      break;
 +              default:
 +                      tail = "th";
 +      }
 +      
 +      str = strcat(color, ftos(place), tail);
 +      if(tied)
 +              return strcat("Tied for ", str);
 +      else
 +              return str;
 +}
 +
  string notif_arg_frag_ping(bool newline, float fping)
  {
        string s = newline ? "\n" : " ";
@@@ -764,7 -666,6 +763,7 @@@ string notif_arg_item_wepammo(float f1
  .string nent_snd;
  .float nent_vol;
  .float nent_position;
 +.float nent_queuetime;
  
  // MSG_INFO and MSG_CENTER entity values
  .string nent_args; // used by both
@@@ -843,22 -744,21 +842,22 @@@ Notification Get_Notif_Ent(MSG net_type
        return it;
  }
  
 -#define MSG_ANNCE_NOTIF_TEAM(teamnum, name, cvarname, defaultvalue, sound, channel, volume, position) \
 -      MSG_ANNCE_NOTIF_(teamnum, ANNCE_##name, ANNCE_##cvarname, defaultvalue, sound, channel, volume, position)
 +#define MSG_ANNCE_NOTIF_TEAM(teamnum, name, cvarname, defaultvalue, sound, channel, volume, position, queuetime) \
 +      MSG_ANNCE_NOTIF_(teamnum, ANNCE_##name, ANNCE_##cvarname, defaultvalue, sound, channel, volume, position, queuetime)
  
 -#define MSG_ANNCE_NOTIF(name, defaultvalue, sound, channel, volume, position) \
 +#define MSG_ANNCE_NOTIF(name, defaultvalue, sound, channel, volume, position, queuetime) \
        NOTIF_ADD_AUTOCVAR(ANNCE_##name, defaultvalue) \
 -      MSG_ANNCE_NOTIF_(0, ANNCE_##name, ANNCE_##name, defaultvalue, sound, channel, volume, position)
 +      MSG_ANNCE_NOTIF_(0, ANNCE_##name, ANNCE_##name, defaultvalue, sound, channel, volume, position, queuetime)
  
 -#define MSG_ANNCE_NOTIF_(teamnum, name, cvarname, defaultvalue, sound, channel, volume, position) \
 +#define MSG_ANNCE_NOTIF_(teamnum, name, cvarname, defaultvalue, sound, channel, volume, position, queuetime) \
        REGISTER(Notifications, name, m_id, new_pure(msg_annce_notification)) { \
                Create_Notification_Entity      (this, defaultvalue, ACVNN(cvarname), MSG_ANNCE, strtoupper(#name), teamnum); \
                Create_Notification_Entity_Annce(this, ACVNN(cvarname), strtoupper(#name), \
 -                      channel,   /* channel  */ \
 -                      sound,     /* snd      */ \
 -                      volume,    /* vol      */ \
 -                      position); /* position */ \
 +                      channel,    /* channel   */ \
 +                      sound,      /* snd       */ \
 +                      volume,     /* vol       */ \
 +                      position,   /* position  */ \
 +                      queuetime); /* queuetime */ \
        }
  
  #define MSG_INFO_NOTIF_TEAM(teamnum, name, cvarname, defaultvalue, strnum, flnum, args, hudargs, icon, normal, gentle) \
                        optiona,                                 /* optiona     */ \
                        optionb);                                /* optionb     */ \
        }
 +      
 +#define MSG_MEDAL_NOTIF(name, defaultvalue, icon, anncename) \
 +      NOTIF_ADD_AUTOCVAR(MEDAL_##name, defaultvalue) \
 +      MSG_MEDAL_NOTIF_(0, MEDAL_##name, MEDAL_##name, defaultvalue, icon, anncename)
 +
 +#define MSG_MEDAL_NOTIF_(teamnum, name, cvarname, defaultvalue, icon, anncename) \
 +      REGISTER(Notifications, name, m_id, new_pure(msg_medal_notification)) { \
 +              Create_Notification_Entity      (this, defaultvalue, ACVNN(cvarname), MSG_MEDAL, strtoupper(#name), teamnum); \
 +              Create_Notification_Entity_Medal(this, ACVNN(cvarname), strtoupper(#name), \
 +                      icon, \
 +                      anncename); \
 +      }
  
  REGISTRY_BEGIN(Notifications)
  {
diff --combined qcsrc/common/stats.qh
index 929992ab7fc6c7a74e5f40903ef0fcc73845656a,128f090c4fd9ec94ff1269b3336bda705be60c8e..4ed9b95f67c1ceb71df3122519c4a8563e9abdf4
@@@ -78,14 -78,10 +78,16 @@@ float W_WeaponRateFactor(entity this)
  float game_stopped;
  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 checkrules_overtimesadded; // z411 add
 +float overtime_starttime; // z411 point in time where first overtime started
 +
 +float timeout_last;
 +float timeout_total_time;
 +bool game_timeout;
 +
  int autocvar_leadlimit;
+ int overtimes; // overtimes added (-1 = sudden death)
+ int timeout_status; // (values: 0, 1, 2) contains whether a timeout is not active (0), was called but still at leadtime (1) or is active (2)
  // TODO: world.qh can't be included here due to circular includes!
  #define autocvar_fraglimit cvar("fraglimit")
  #define autocvar_fraglimit_override cvar("fraglimit_override")
@@@ -94,9 -90,6 +96,9 @@@
  #endif
  REGISTER_STAT(WEAPONRATEFACTOR, float, W_WeaponRateFactor(this))
  REGISTER_STAT(GAME_STOPPED, int, game_stopped)
 +
 +REGISTER_STAT(TIMEOUT_LAST, float, timeout_last)
 +
  REGISTER_STAT(GAMESTARTTIME, float, game_starttime)
  /** arc heat in [0,1] */
  REGISTER_STAT(PRESSED_KEYS, int)
@@@ -121,14 -114,14 +123,10 @@@ REGISTER_STAT(VEHICLESTAT_AMMO2, int
  REGISTER_STAT(VEHICLESTAT_RELOAD2, int)
  REGISTER_STAT(VEHICLESTAT_W2MODE, int)
  REGISTER_STAT(NADE_TIMER, float)
- //REGISTER_STAT(SECRETS_TOTAL, int, secrets_total)
- //REGISTER_STAT(SECRETS_FOUND, int, secrets_found)
 -REGISTER_STAT(SECRETS_TOTAL, int, secrets_total)
 -REGISTER_STAT(SECRETS_FOUND, int, secrets_found)
  REGISTER_STAT(RESPAWN_TIME, float)
  REGISTER_STAT(ROUNDSTARTTIME, float, round_starttime)
- REGISTER_STAT(OVERTIMESTARTTIME, float, overtime_starttime)
- REGISTER_STAT(OVERTIMESADDED, float, checkrules_overtimesadded)
- //REGISTER_STAT(MONSTERS_TOTAL, int)
- //REGISTER_STAT(MONSTERS_KILLED, int)
+ REGISTER_STAT(OVERTIMES, int, overtimes)
+ REGISTER_STAT(TIMEOUT_STATUS, int, timeout_status)
 -REGISTER_STAT(MONSTERS_TOTAL, int)
 -REGISTER_STAT(MONSTERS_KILLED, int)
  REGISTER_STAT(NADE_BONUS, float)
  REGISTER_STAT(NADE_BONUS_TYPE, int)
  REGISTER_STAT(NADE_BONUS_SCORE, float)
@@@ -145,6 -138,7 +143,6 @@@ REGISTER_STAT(ITEMSTIME, int, autocvar_
  REGISTER_STAT(KILL_TIME, float)
  REGISTER_STAT(VEIL_ORB, float)
  REGISTER_STAT(VEIL_ORB_ALPHA, float)
 -
  #ifdef SVQC
  float autocvar_sv_showfps = 0;
  #endif
@@@ -371,6 -365,8 +369,8 @@@ REGISTER_STAT(Q3COMPAT, int, q3compat
  #ifdef SVQC
  #include "physics/movetypes/movetypes.qh"
  float warmup_limit;
+ float round_limit;
+ int rounds_played;
  #endif
  
  #ifdef SVQC
@@@ -410,6 -406,8 +410,8 @@@ REGISTER_STAT(MOVEVARS_AIRCONTROL, floa
  REGISTER_STAT(FRAGLIMIT, float, autocvar_fraglimit)
  REGISTER_STAT(TIMELIMIT, float, autocvar_timelimit)
  REGISTER_STAT(WARMUP_TIMELIMIT, float, warmup_limit)
+ REGISTER_STAT(ROUNDS_PLAYED, int, rounds_played)
+ REGISTER_STAT(ROUND_TIMELIMIT, float, round_limit)
  #ifdef SVQC
  float autocvar_sv_wallfriction;
  #define autocvar_sv_gravity cvar("sv_gravity")
@@@ -441,5 -439,3 +443,5 @@@ REGISTER_STAT(GUNALIGN, int
  #ifdef SVQC
  SPECTATE_COPYFIELD(_STAT(GUNALIGN))
  #endif
 +
 +REGISTER_STAT(MMM_ROUNDTIMER, float) //LegendGuard adds MMM_ROUNDTIMER for MMM 20-02-2021
index 05e8a75b5e674b1778480bb857795e5eed38ef09,691404ac117d183c95162716a60c886d405017cc..6c3711f7478581a704358a057b80d1e48dafb1d3
@@@ -475,15 -475,8 +475,8 @@@ void updateCheck(
        if(!_Nex_ExtResponseSystem_Queried)
        {
                _Nex_ExtResponseSystem_Queried = 1;
-               float startcnt;
-               string uri;
-               cvar_set("cl_startcount", ftos(startcnt = cvar("cl_startcount") + 1));
-               // for privacy, munge the start count a little
-               startcnt = floor((floor(startcnt / 10) + random()) * 10);
-               uri = sprintf("http://update.xonotic.org/checkupdate.txt?version=%s&cnt=%d", uri_escape(cvar_string("g_xonoticversion")), startcnt);
-               uri_get(uri, URI_GET_UPDATENOTIFICATION);
+               cvar_set("cl_startcount", ftos(cvar("cl_startcount") + 1));
+               uri_get("https://update.xonotic.org/checkupdate.txt", URI_GET_UPDATENOTIFICATION);
        }
  
        if(_Nex_ExtResponseSystem_PacksStep > 0)
@@@ -686,8 -679,6 +679,8 @@@ float updateCompression(
        GAMETYPE(MAPINFO_TYPE_CTF) \
        GAMETYPE(MAPINFO_TYPE_CA) \
        GAMETYPE(MAPINFO_TYPE_FREEZETAG) \
 +      GAMETYPE(MAPINFO_TYPE_TEAM_MAYHEM) \
 +      GAMETYPE(MAPINFO_TYPE_MAYHEM) \
        GAMETYPE(MAPINFO_TYPE_KEEPAWAY) \
        GAMETYPE(MAPINFO_TYPE_KEYHUNT) \
        GAMETYPE(MAPINFO_TYPE_LMS) \
        GAMETYPE(MAPINFO_TYPE_ASSAULT) \
        /* GAMETYPE(MAPINFO_TYPE_DUEL) */ \
        /* GAMETYPE(MAPINFO_TYPE_INVASION) */ \
-       GAMETYPE(MAPINFO_TYPE_MMM) \
-       //LegendGuard adds GAMETYPE for menu for MMM 20-02-2021
++      /* GAMETYPE(MAPINFO_TYPE_MMM) */ \
        /**/
  
  // hidden gametypes come last so indexing always works correctly
@@@ -833,6 -822,18 +825,18 @@@ void dialog_hudpanel_main_settings(enti
                                e.configureXonoticTextSliderValues(e);
  }
  
+ bool isServerSingleplayer()
+ {
+       return (cvar_string("net_address") == "127.0.0.1" && cvar_string("net_address_ipv6") == "::1");
+ }
+ void makeServerSingleplayer()
+ {
+       // it doesn't allow clients to connect from different machines
+       localcmd("defer 0.1 \"sv_cmd settemp net_address 127.0.0.1\"\n");
+       localcmd("defer 0.1 \"sv_cmd settemp net_address_ipv6 ::1\"\n");
+ }
  float getFadedAlpha(float currentAlpha, float startAlpha, float targetAlpha)
  {
        if(startAlpha < targetAlpha)
diff --combined qcsrc/server/client.qc
index d09425f0d8697af82fc4282d21b81a3015f87c35,a5f508851b00876304e486f92f9fcb9634d6f438..68cdc2acfdb7484edc32ef25ef7df3e3886d22e9
@@@ -98,16 -98,6 +98,16 @@@ void send_CSQC_teamnagger() 
        WriteHeader(MSG_BROADCAST, TE_CSQC_TEAMNAGGER);
  }
  
 +void send_TeamNames(int channel, entity to) {
 +      msg_entity = to;
 +      
 +      WriteHeader(channel, TE_CSQC_TEAMNAMES);
 +      WriteString(channel, autocvar_g_teamnames_red);
 +      WriteString(channel, autocvar_g_teamnames_blue);
 +      WriteString(channel, autocvar_g_teamnames_yellow);
 +      WriteString(channel, autocvar_g_teamnames_pink);
 +}
 +
  int CountSpectators(entity player, entity to)
  {
        if(!player) { return 0; } // not sure how, but best to be safe
@@@ -248,6 -238,7 +248,7 @@@ void setplayermodel(entity e, string mo
  void PutObserverInServer(entity this, bool is_forced, bool use_spawnpoint)
  {
        bool mutator_returnvalue = MUTATOR_CALLHOOK(MakePlayerObserver, this, is_forced);
+       bool recount_ready = false;
        PlayerState_detach(this);
  
        if (IS_PLAYER(this))
                if(IS_REAL_CLIENT(this))
                {
                        if (vote_called) { VoteCount(false); }
-                       ReadyCount();
+                       this.ready = false;
+                       recount_ready = true;
                }
                entcs_update_players(this);
        }
  
        if (this.vehicle) vehicles_exit(this.vehicle, VHEF_RELEASE);
  
+       TRANSMUTE(Observer, this);
+       if(recount_ready) ReadyCount();
        WaypointSprite_PlayerDead(this);
+       accuracy_resend(this);
  
        if (CS(this).killcount != FRAGS_SPECTATOR && !game_stopped && CHAT_NOSPECTATORS())
                Send_Notification(NOTIF_ONE_ONLY, this, MSG_INFO, INFO_CHAT_NOSPECTATORS);
  
-       accuracy_resend(this);
        CS(this).spectatortime = time;
        if(this.bot_attack)
                IL_REMOVE(g_bot_targets, this);
                IL_REMOVE(g_monster_targets, this);
        this.monster_attack = false;
        STAT(HUD, this) = HUD_NORMAL;
-       TRANSMUTE(Observer, this);
        this.iscreature = false;
        this.teleportable = TELEPORT_SIMPLE;
        if(this.damagedbycontents)
        this.revival_time = 0;
        this.draggable = drag_undraggable;
  
+       player_powerups_remove_all(this);
        this.items = 0;
        STAT(WEAPONS, this) = '0 0 0';
        this.drawonlytoclient = this;
  
        if (CS(this).just_joined)
                CS(this).just_joined = false;
 +      
 +      // for RJZ
 +      if (autocvar_rjz_count_shards)
 +              send_TotalShards(this);
  }
  
  int player_getspecies(entity this)
@@@ -541,36 -532,6 +546,36 @@@ void FixPlayermodel(entity player
                                setcolor(player, stof(autocvar_sv_defaultplayercolors));
  }
  
 +void ResetPlayerResources(entity this)
 +{
 +      if (warmup_stage) {
 +              SetResource(this, RES_SHELLS, warmup_start_ammo_shells);
 +              SetResource(this, RES_BULLETS, warmup_start_ammo_nails);
 +              SetResource(this, RES_ROCKETS, warmup_start_ammo_rockets);
 +              SetResource(this, RES_CELLS, warmup_start_ammo_cells);
 +              SetResource(this, RES_PLASMA, warmup_start_ammo_plasma);
 +              SetResource(this, RES_FUEL, warmup_start_ammo_fuel);
 +              SetResource(this, RES_HEALTH, warmup_start_health);
 +              SetResource(this, RES_ARMOR, warmup_start_armorvalue);
 +              STAT(WEAPONS, this) = WARMUP_START_WEAPONS;
 +      } else {
 +              SetResource(this, RES_SHELLS, start_ammo_shells);
 +              SetResource(this, RES_BULLETS, start_ammo_nails);
 +              SetResource(this, RES_ROCKETS, start_ammo_rockets);
 +              SetResource(this, RES_CELLS, start_ammo_cells);
 +              SetResource(this, RES_PLASMA, start_ammo_plasma);
 +              SetResource(this, RES_FUEL, start_ammo_fuel);
 +              SetResource(this, RES_HEALTH, start_health);
 +              SetResource(this, RES_ARMOR, start_armorvalue);
 +              STAT(WEAPONS, this) = start_weapons;
 +              if (MUTATOR_CALLHOOK(ForbidRandomStartWeapons, this) == false)
 +              {
 +                      GiveRandomWeapons(this, random_start_weapons_count,
 +                              autocvar_g_random_start_weapons, random_start_ammo);
 +              }
 +      }
 +}
 +
  void PutPlayerInServer(entity this)
  {
        if (this.vehicle) vehicles_exit(this.vehicle, VHEF_RELEASE);
        this.takedamage = DAMAGE_AIM;
        this.effects = EF_TELEPORT_BIT | EF_RESTARTANIM_BIT;
  
 -      if (warmup_stage) {
 -              SetResource(this, RES_SHELLS, warmup_start_ammo_shells);
 -              SetResource(this, RES_BULLETS, warmup_start_ammo_nails);
 -              SetResource(this, RES_ROCKETS, warmup_start_ammo_rockets);
 -              SetResource(this, RES_CELLS, warmup_start_ammo_cells);
 -              SetResource(this, RES_PLASMA, warmup_start_ammo_plasma);
 -              SetResource(this, RES_FUEL, warmup_start_ammo_fuel);
 -              SetResource(this, RES_HEALTH, warmup_start_health);
 -              SetResource(this, RES_ARMOR, warmup_start_armorvalue);
 -              STAT(WEAPONS, this) = WARMUP_START_WEAPONS;
 -      } else {
 -              SetResource(this, RES_SHELLS, start_ammo_shells);
 -              SetResource(this, RES_BULLETS, start_ammo_nails);
 -              SetResource(this, RES_ROCKETS, start_ammo_rockets);
 -              SetResource(this, RES_CELLS, start_ammo_cells);
 -              SetResource(this, RES_PLASMA, start_ammo_plasma);
 -              SetResource(this, RES_FUEL, start_ammo_fuel);
 -              SetResource(this, RES_HEALTH, start_health);
 -              SetResource(this, RES_ARMOR, start_armorvalue);
 -              STAT(WEAPONS, this) = start_weapons;
 -              if (MUTATOR_CALLHOOK(ForbidRandomStartWeapons, this) == false)
 -              {
 -                      GiveRandomWeapons(this, random_start_weapons_count,
 -                              autocvar_g_random_start_weapons, random_start_ammo);
 -              }
 -      }
 +      ResetPlayerResources(this);
 +      
        SetSpectatee_status(this, 0);
  
        PS(this).dual_weapons = '0 0 0';
@@@ -849,9 -834,7 +854,9 @@@ void PutClientInServer(entity this
        } else if (IS_PLAYER(this)) {
                PutPlayerInServer(this);
        }
 -
 +      // send team names
 +      if(teamplay && IS_REAL_CLIENT(this))
 +              send_TeamNames(MSG_ONE, this);
        bot_relinkplayerlist();
  }
  
@@@ -891,13 -874,6 +896,13 @@@ void ClientInit_misc(entity this
        WriteByte(channel, this.cnt * 255.0); // g_balance_damagepush_speedfactor
        WriteByte(channel, serverflags);
        WriteCoord(channel, autocvar_g_trueaim_minrange);
 +      
 +      // z411 send full hostname
 +      WriteString(channel, (autocvar_hostname_full != "" ? autocvar_hostname_full : autocvar_hostname));
 +      WriteString(channel, autocvar_sv_motd_permanent);
 +      
 +      // z411 send client countdown type
 +      WriteByte(channel, autocvar_sv_timer_countdown);
  }
  
  void ClientInit_CheckUpdate(entity this)
@@@ -1057,8 -1033,27 +1062,27 @@@ string GetClientVersionMessage(entity t
        }
  }
  
- string getwelcomemessage(entity this)
+ void SendWelcomemessage(entity this, bool force_centerprint)
+ {
+       msg_entity = this;
+       WriteHeader(MSG_ONE, TE_CSQC_SERVERWELCOME);
+       SendWelcomemessage_msg_type(this, force_centerprint, MSG_ONE);
+ }
+ void SendWelcomemessage_msg_type(entity this, bool force_centerprint, int msg_type)
  {
+       WriteByte(msg_type, boolean(autocvar_g_campaign));
+       if (boolean(autocvar_g_campaign))
+       {
+               WriteString(msg_type, Campaign_GetTitle());
+               WriteByte(msg_type, Campaign_GetLevelNum());
+               WriteString(msg_type, Campaign_GetMessage());
+               return;
+       }
+       WriteByte(msg_type, force_centerprint);
+       WriteString(msg_type, autocvar_hostname);
+       WriteString(msg_type, GetClientVersionMessage(this));
        MUTATOR_CALLHOOK(BuildMutatorsPrettyString, "");
        string modifications = M_ARGV(0, string);
  
                modifications = strcat(modifications, ", Jet pack");
        modifications = substring(modifications, 2, strlen(modifications) - 2);
  
-       //string versionmessage = GetClientVersionMessage(this);
-       //string s = strcat(versionmessage, "^8\n^9", (autocvar_hostname_full ? autocvar_hostname_full : autocvar_hostname));
-       string s = (autocvar_hostname_full != "" ? autocvar_hostname_full : autocvar_hostname);
-       s = strcat(s, "^8\n^7", gamemode_name);
-       if(modifications != "")
-               s = strcat(s, "^7 | ^3", modifications);
+       WriteString(msg_type, modifications);
  
        if(cache_lastmutatormsg != autocvar_g_mutatormsg)
        {
                strcpy(cache_mutatormsg, cache_lastmutatormsg);
        }
  
-       if (cache_mutatormsg != "") {
-               s = strcat(s, "\n^8tips: ^7", cache_mutatormsg);
-       }
+       WriteString(msg_type, cache_mutatormsg);
  
        string mutator_msg = "";
        MUTATOR_CALLHOOK(BuildGameplayTipsString, mutator_msg);
        mutator_msg = M_ARGV(0, string);
  
-       s = strcat(s, mutator_msg); // trust that the mutator will do proper formatting
-       string motd = autocvar_sv_motd;
-       if (motd != "") {
-               s = strcat(s, "\n\n^7", strreplace("\\n", "\n", motd));
-       }
-       
-       // It's been a work, I want some credit for a while. Might remove later.
-       s = strcat(s, "\n\nUsing BaI mod by z411 - bienvenidoainternet.org");
-       return s;
+       WriteString(msg_type, mutator_msg); // trust that the mutator will do proper formatting
+       WriteString(msg_type, strreplace("\\n", "\n", autocvar_sv_motd));
  }
  
  /**
@@@ -1147,12 -1125,8 +1154,12 @@@ void ClientConnect(entity this
        else
                CS(this).allowed_timeouts = autocvar_sv_timeout_number;
  
 -      if (autocvar_sv_eventlog)
 +      if (autocvar_sv_eventlog) {
                GameLogEcho(strcat(":join:", ftos(this.playerid), ":", ftos(etof(this)), ":", ((IS_REAL_CLIENT(this)) ? GameLog_ProcessIP(this.netaddress) : "bot"), ":", playername(this.netname, this.team, false)));
 +              
 +              /* z411 for RJZ */
 +              if(autocvar_rjz_ranks) GameLogEcho(strcat(":idfp:", ftos(etof(this)), ":", this.crypto_idfp));
 +      }
  
        CS(this).just_joined = true;  // stop spamming the eventlog with additional lines when the client connects
  
  
        MUTATOR_CALLHOOK(ClientConnect, this);
  
-       if (IS_REAL_CLIENT(this))
-       {
-               if (!autocvar_g_campaign && !IS_PLAYER(this))
-               {
-                       CS(this).motd_actived_time = -1;
-                       Send_Notification(NOTIF_ONE_ONLY, this, MSG_CENTER, CENTER_MOTD, getwelcomemessage(this));
-               }
-       }
+       if (IS_REAL_CLIENT(this) && !IS_PLAYER(this) && !autocvar_g_campaign)
+               CS(this).motd_actived_time = -1; // the welcome message is shown by the client
  }
  /*
  =============
@@@ -1479,21 -1447,25 +1480,25 @@@ void respawn(entity this
  void play_countdown(entity this, float finished, Sound samp)
  {
        TC(Sound, samp);
-       if(IS_REAL_CLIENT(this))
-               if(floor(finished - time - frametime) != floor(finished - time))
-                       if(finished - time < 6)
-                               sound (this, CH_INFO, samp, VOL_BASE, ATTEN_NORM);
+       float time_left = finished - time;
+       if(IS_REAL_CLIENT(this) && time_left < 6 && floor(time_left - frametime) != floor(time_left))
+               sound(this, CH_INFO, samp, VOL_BASE, ATTEN_NORM);
  }
  
+ // it removes special powerups not handled by StatusEffects
  void player_powerups_remove_all(entity this)
  {
-       if (this.items & IT_SUPERWEAPON)
+       if (this.items & (IT_SUPERWEAPON | IT_UNLIMITED_AMMO | IT_UNLIMITED_SUPERWEAPONS))
        {
                // don't play the poweroff sound when the game restarts or the player disconnects
-               if (time > game_starttime + 1 && IS_CLIENT(this))
+               if (time > game_starttime + 1 && IS_CLIENT(this)
+                       && !(start_items & (IT_UNLIMITED_AMMO | IT_UNLIMITED_SUPERWEAPONS)))
+               {
                        sound(this, CH_INFO, SND_POWEROFF, VOL_BASE, ATTEN_NORM);
-               stopsound(this, CH_TRIGGER_SINGLE); // get rid of the pickup sound
-               this.items -= (this.items & IT_SUPERWEAPON);
+               }
+               if (this.items & (IT_UNLIMITED_AMMO | IT_UNLIMITED_SUPERWEAPONS))
+                       stopsound(this, CH_TRIGGER_SINGLE); // get rid of the pickup sound
+               this.items -= (this.items & (IT_SUPERWEAPON | IT_UNLIMITED_AMMO | IT_UNLIMITED_SUPERWEAPONS));
        }
  }
  
@@@ -2015,11 -1987,6 +2020,11 @@@ void Join(entity this
        else
                Send_Notification(NOTIF_ALL, NULL, MSG_INFO, INFO_JOIN_PLAY, this.netname);
        this.team_selected = false;
 +      
 +      // z411
 +      // send constant ready notification
 +      if(warmup_stage)
 +              Send_Notification(NOTIF_ONE_ONLY, this, MSG_CENTER, CENTER_MISSING_READY);
  }
  
  int GetPlayerLimit()
@@@ -2088,12 -2055,12 +2093,12 @@@ void PrintWelcomeMessage(entity this
                if (autocvar_g_campaign) {
                        if ((IS_PLAYER(this) && PHYS_INPUT_BUTTON_INFO(this)) || (!IS_PLAYER(this))) {
                                CS(this).motd_actived_time = time;
-                               Send_Notification(NOTIF_ONE_ONLY, this, MSG_CENTER, CENTER_CAMPAIGN_MESSAGE, Campaign_GetMessage(), Campaign_GetLevelNum());
+                               SendWelcomemessage(this, false);
                        }
                } else {
                        if (PHYS_INPUT_BUTTON_INFO(this)) {
                                CS(this).motd_actived_time = time;
-                               Send_Notification(NOTIF_ONE_ONLY, this, MSG_CENTER, CENTER_MOTD, getwelcomemessage(this));
+                               SendWelcomemessage(this, true);
                        }
                }
        }
                                CS(this).motd_actived_time = time;
                        else if ((time - CS(this).motd_actived_time > 2) && IS_PLAYER(this)) { // hide it some seconds after BUTTON_INFO has been released
                                CS(this).motd_actived_time = 0;
-                               Kill_Notification(NOTIF_ONE_ONLY, this, MSG_CENTER, CPID_CAMPAIGN_MESSAGE);
+                               Kill_Notification(NOTIF_ONE_ONLY, this, MSG_CENTER, CPID_MOTD);
                        }
                } else {
                        if (PHYS_INPUT_BUTTON_INFO(this))
                {
                        // instantly hide MOTD
                        CS(this).motd_actived_time = 0;
-                       if (autocvar_g_campaign)
-                               Kill_Notification(NOTIF_ONE_ONLY, this, MSG_CENTER, CPID_CAMPAIGN_MESSAGE);
-                       else
-                               Kill_Notification(NOTIF_ONE_ONLY, this, MSG_CENTER, CPID_MOTD);
+                       Kill_Notification(NOTIF_ONE_ONLY, this, MSG_CENTER, CPID_MOTD);
                }
                else if (IS_PLAYER(this) || IS_SPEC(this))
                {
@@@ -2162,13 -2126,12 +2164,13 @@@ bool PlayerThink(entity this
                return false;
        }
  
 -      if (timeout_status == TIMEOUT_ACTIVE) {
 -              // don't allow the player to turn around while game is paused
 +      if (game_timeout) {
 +        // don't allow the player to turn around while game is paused
                // FIXME turn this into CSQC stuff
                this.v_angle = this.lastV_angle;
                this.angles = this.lastV_angle;
                this.fixangle = true;
 +              return false;
        }
  
        if (frametime) player_powerups(this);
diff --combined qcsrc/server/client.qh
index 2ee5aa3cad7f5e50672099975f88af9d58d0a1d8,5fc5422c63f7e55e5d4316f1a91fad7f3b0bdce5..44ae9e03c09b6d53fd30a848a4d2072bcb6b6655
@@@ -49,7 -49,6 +49,7 @@@ float autocvar_gameversion_max
  string autocvar_hostname;
  int autocvar_spawn_debug;
  string autocvar_sv_motd;
 +string autocvar_sv_motd_permanent;
  int autocvar_sv_name_maxlength = 64;
  bool autocvar_sv_servermodelsonly;
  int autocvar_sv_spectate;
@@@ -57,21 -56,6 +57,21 @@@ bool autocvar_sv_teamnagger
  float autocvar_sv_player_scale;
  bool autocvar_sv_showspectators;
  
 +// z411
 +string autocvar_hostname_full;
 +string autocvar_g_teamnames_red;
 +string autocvar_g_teamnames_blue;
 +string autocvar_g_teamnames_yellow;
 +string autocvar_g_teamnames_pink;
 +
 +void send_TeamNames(int channel, entity to);
 +
 +// Medals
 +float autocvar_g_medals_excellent_time = 2;
 +
 +// Timer
 +int autocvar_sv_timer_countdown = 1; // 0 = disabled, 1 = enabled, 2 = spect only
 +
  // WEAPONTODO
  .string weaponorder_byimpulse;
  
@@@ -107,8 -91,6 +107,8 @@@ CLASS(Client, Object
      ATTRIB(Client, colormap, int, this.colormap);
      ATTRIB(Client, team, int, this.team);
      ATTRIB(Client, clientcolors, int, this.clientcolors);
 +      ATTRIB(Client, countrycode, int, this.countrycode);
 +      ATTRIB(Client, rank, string, this.rank);
      /** Client IP */
      ATTRIB(Client, netaddress, string, this.netaddress);
      ATTRIB(Client, playermodel, string, this.playermodel);
      ATTRIB(Client, cvar_cl_pokenade_type, string, this.cvar_cl_pokenade_type);
      ATTRIB(Client, cvar_cl_spawn_near_teammate, bool, this.cvar_cl_spawn_near_teammate);
      ATTRIB(Client, cvar_cl_gunalign, int, this.cvar_cl_gunalign);
 +      ATTRIB(Client, cvar_cl_chat_sounds, bool, this.cvar_cl_chat_sounds);
      ATTRIB(Client, cvar_cl_handicap, float, this.cvar_cl_handicap);
      ATTRIB(Client, cvar_cl_clippedspectating, bool, this.cvar_cl_clippedspectating);
      ATTRIB(Client, cvar_cl_autoscreenshot, int, this.cvar_cl_autoscreenshot);
@@@ -333,15 -314,14 +333,17 @@@ bool independent_players
  #define IS_INDEPENDENT_PLAYER(e) ((e).solid == SOLID_TRIGGER)
  #define MAKE_INDEPENDENT_PLAYER(e) (((e).solid = SOLID_TRIGGER), ((e).frags = FRAGS_PLAYER_OUT_OF_GAME))
  
 +.float lastkill;
 +.int countrycode;
  .int killcount;
 +.string rank; // RJZ
  
  //flood fields
  .float nickspamtime; // time of last nick change
  .float nickspamcount;
  
+ void SendWelcomemessage_msg_type(entity this, bool force_centerprint, int msg_type);
  // respawning
  .int respawn_flags;
  .float respawn_time;
@@@ -426,9 -406,3 +428,9 @@@ void Join(entity this)
  #define SPECTATE_COPYFIELD(fld) SPECTATE_COPY() { this.(fld) = spectatee.(fld); }
  
  const int MAX_SPECTATORS = 7;
 +
 +float _medal_times;
 +#define Give_Medal(entity,medalname) \
 +      _medal_times = GameRules_scoring_add(entity, MEDAL_##medalname, 1); \
 +      Send_Notification(NOTIF_ONE, entity, MSG_MEDAL, MEDAL_##medalname, _medal_times);
 +
index 680c7432c5e7f3581f7fdeb6c7a6a1ba0074d3b7,9feb068a812bdd377f7bf9a6822cdf0217a52104..0c1dcc79844919aaa709a828dfb90d1efddbe236
@@@ -275,7 -275,7 +275,7 @@@ void ClientCommand_join(entity caller, 
        {
                case CMD_REQUEST_COMMAND:
                {
 -                      if (!game_stopped && IS_CLIENT(caller) && !IS_PLAYER(caller))
 +                      if (!game_stopped && !game_timeout && IS_CLIENT(caller) && !IS_PLAYER(caller))
                        {
                                if (joinAllowed(caller))
                                        Join(caller);
@@@ -372,7 -372,7 +372,7 @@@ void ClientCommand_ready(entity caller
        {
                case CMD_REQUEST_COMMAND:
                {
-                       if (IS_CLIENT(caller) && caller.last_ready < time - 3) // anti-spam
+                       if (IS_CLIENT(caller) && caller.last_ready < time - 3)
                        {
                                if (warmup_stage || g_race_qualifying == 2)
                                {
                                        {
                                                caller.ready = false;
                                                if (IS_PLAYER(caller) || INGAME_JOINED(caller))
 -                                                      bprint(playername(caller.netname, caller.team, false), "^2 is ^1NOT^2 ready\n");
 +                                                      bprint("\{1}", playername(caller.netname, caller.team, false), "^2 is ^1NOT^2 ready\n");
                                        }
                                        else
                                        {
                                                caller.ready = true;
                                                if (IS_PLAYER(caller) || INGAME_JOINED(caller))
 -                                                      bprint(playername(caller.netname, caller.team, false), "^2 is ready\n");
 +                                                      bprint("\{1}", playername(caller.netname, caller.team, false), "^2 is ready\n");
                                        }
  
                                        caller.last_ready = time;
@@@ -595,7 -595,7 +595,7 @@@ void ClientCommand_spectate(entity call
        {
                case CMD_REQUEST_COMMAND:
                {
 -                      if (!intermission_running && IS_CLIENT(caller))
 +                      if (!intermission_running && IS_CLIENT(caller) && !game_timeout)
                        {
                                if(argv(1) != "")
                                {
        }
  }
  
 +void ClientCommand_sounds(entity caller, int request)
 +{
 +      switch (request)
 +      {
 +              case CMD_REQUEST_COMMAND:
 +              {
 +                      sprint(caller, strcat("Available sounds: \n\n", autocvar_sv_chat_sounds_list, "\n"));
 +                      return; // never fall through to usage
 +              }
 +
 +              default:
 +              case CMD_REQUEST_USAGE:
 +              {
 +                      sprint(caller, "\nUsage:^3 cmd sounds\n");
 +                      return;
 +              }
 +      }
 +}
 +
  void ClientCommand_suggestmap(entity caller, int request, int argc)
  {
        switch (request)
@@@ -831,7 -812,6 +831,7 @@@ void ClientCommand_(entity caller, int 
        CLIENT_COMMAND("selfstuff", ClientCommand_selfstuff(ent, request, command), "Stuffcmd a command to your own client") \
        CLIENT_COMMAND("sentcvar", ClientCommand_sentcvar(ent, request, arguments), "New system for sending a client cvar to the server") \
        CLIENT_COMMAND("spectate", ClientCommand_spectate(ent, request), "Become an observer") \
 +      CLIENT_COMMAND("sounds", ClientCommand_sounds(ent, request), "Get list of commsnds") \
        CLIENT_COMMAND("suggestmap", ClientCommand_suggestmap(ent, request, arguments), "Suggest a map to the mapvote at match end") \
        CLIENT_COMMAND("tell", ClientCommand_tell(ent, request, arguments, command), "Send a message directly to a player") \
        CLIENT_COMMAND("voice", ClientCommand_voice(ent, request, arguments, command), "Send voice message via sound") \
index 30fe59d3a3477dc327dfdec789f9cbccfca953b5,056a8e9e6ec7dae7efb4356cc4a2cf8b63f047d3..a6f3af3c61e3dc6bf76b6274ec7806fc394f4f01
@@@ -13,7 -13,6 +13,7 @@@
  #include <server/client.qh>
  #include <server/command/common.qh>
  #include <server/mutators/_mod.qh>
 +#include <server/round_handler.qh>
  #include <server/scores.qh>
  #include <server/world.qh>
  
@@@ -203,35 -202,16 +203,36 @@@ void timeout_handler_think(entity this
                                if (timeout_time == autocvar_sv_timeout_resumetime) // play a warning sound when only <sv_timeout_resumetime> seconds are left
                                        Send_Notification(NOTIF_ALL, NULL, MSG_ANNCE, ANNCE_PREPARE);
  
 -                              this.nextthink = time + TIMEOUT_SLOWMO_VALUE;       // think again in one second
 +                              //this.nextthink = time + TIMEOUT_SLOWMO_VALUE;       // think again in one second
 +                              this.nextthink = time + 1;
                                timeout_time -= 1;                                  // decrease the time counter
                        }
 +                      else if (timeout_time == -1)  // infinite timer
 +                      {
 +                              Send_Notification(NOTIF_ALL, NULL, MSG_CENTER, CENTER_TIMEOUT_ONGOING);
 +                              this.nextthink = time + TIMEOUT_SLOWMO_VALUE;
 +                      }
                        else  // time to end the timeout
                        {
                                Kill_Notification(NOTIF_ALL, NULL, MSG_CENTER, CPID_TIMEIN);
                                timeout_status = TIMEOUT_INACTIVE;
 +                              float total_time = time - timeout_last;
  
                                // reset the slowmo value back to normal
 -                              cvar_set("slowmo", ftos(orig_slowmo));
 +                              // z411 TODO
 +                              //cvar_set("slowmo", ftos(orig_slowmo));
 +                              
 +                              // Disable timeout and fix times
 +                              game_timeout = false;
 +                              timeout_total_time += total_time;
 +                              game_starttime += total_time;
-                               round_starttime += total_time;
++                              if(round_starttime)
++                                      round_starttime += total_time;
 +                              if(round_handler && round_handler_GetEndTime() > 0)
 +                                      round_handler.round_endtime += total_time;
 +
 +                              LOG_INFOF("Timeout lasted %d secs", total_time);
 +                              timeout_last = 0;
  
                                // unlock the view for players so they can move around again
                                FOREACH_CLIENT(IS_PLAYER(it) && IS_REAL_CLIENT(it), {
                                timeout_status = TIMEOUT_ACTIVE;
  
                                // set the slowmo value to the timeout default slowmo value
 -                              cvar_set("slowmo", ftos(TIMEOUT_SLOWMO_VALUE));
 +                              //cvar_set("slowmo", ftos(TIMEOUT_SLOWMO_VALUE));
 +                              game_timeout = true;
 +                              timeout_last = time;
 +                              
 +                              // play timeout sound
 +                              sound(NULL, CH_INFO, SND_TIMEOUT, VOL_BASE, ATTN_NONE);
  
                                // reset all the flood variables
                                FOREACH_CLIENT(true, {
@@@ -689,6 -664,7 +690,7 @@@ void CommonCommand_timein(int request, 
                                                        timeout_status = TIMEOUT_INACTIVE;
                                                        timeout_time = 0;
                                                        timeout_handler.nextthink = time;  // timeout_handler has to take care of it immediately
+                                                       Kill_Notification(NOTIF_ALL, NULL, MSG_CENTER, CPID_TIMEOUT);
                                                        bprint(strcat("^7The timeout was aborted by ", GetCallerName(caller), " !\n"));
                                                        return;
                                                }
                                                {
                                                        timeout_time = autocvar_sv_timeout_resumetime;
                                                        timeout_handler.nextthink = time;  // timeout_handler has to take care of it immediately
 -                                                      bprint(strcat("^1Attention: ^7", GetCallerName(caller), " resumed the game! Prepare for battle!\n"));
 +                                                      bprint(strcat("\{1}^1Attention: ^7", GetCallerName(caller), " resumed the game! Prepare for battle!\n"));
                                                        return;
                                                }
  
@@@ -761,7 -737,7 +763,7 @@@ void CommonCommand_timeout(int request
                                {
                                        if (caller)   CS(caller).allowed_timeouts -= 1;
                                        // write a bprint who started the timeout (and how many they have left)
 -                                      bprint(GetCallerName(caller), " ^7called a timeout", (caller ? strcat(" (", ftos(CS(caller).allowed_timeouts), " timeout(s) left)") : ""), "!\n");
 +                                      bprint("\{1}", GetCallerName(caller), " ^7called a timeout", (caller ? strcat(" (", ftos(CS(caller).allowed_timeouts), " timeout(s) left)") : ""), "!\n");
  
                                        timeout_status = TIMEOUT_LEADTIME;
                                        timeout_caller = caller;
                                        timeout_handler = new(timeout_handler);
                                        setthink(timeout_handler, timeout_handler_think);
                                        timeout_handler.nextthink = time;  // always let the entity think asap
 -
 +                                      
                                        Send_Notification(NOTIF_ALL, NULL, MSG_ANNCE, ANNCE_TIMEOUT);
                                }
                        }
index 38267247534ce7f708a9d26a5f6b0c80525c63ed,b35f4fb53ae4e6ad52687a964a8782a58889d701..95979c304e09046e5aeca3128346418d920a71b8
@@@ -156,61 -156,6 +156,61 @@@ void GameCommand_adminmsg(int request, 
        }
  }
  
 +void GameCommand_teamname(int request, int argc)
 +{
 +      switch (request)
 +      {
 +              case CMD_REQUEST_COMMAND:
 +              {
 +                      if (argv(1) == "")
 +                      {
 +                              return;
 +                      }
 +                      if (!teamplay)
 +                      {
 +                              LOG_INFO("selectteam can only be used in teamgames");
 +                              return;
 +                      }
 +                      
 +                      switch (argv(1))
 +                      {
 +                              case "red":
 +                              case "blue":
 +                              case "yellow":
 +                              case "pink":
 +                              {
 +                                      int tm = Team_ColorToTeam(argv(1));
 +                                      if(argv(2) != "") {
 +                                              cvar_set(strcat("g_teamnames_", argv(1)), argv(2));
 +                                              bprintf("\{1}%s%s^7 team is now known as %s^7\n", Team_ColorCode(tm), Team_ColorName(tm), argv(2));
 +                                      } else {
 +                                              cvar_set(strcat("g_teamnames_", argv(1)), "");
 +                                              bprintf("\{1}%s%s^7 team now doesn't have a team name\n", Team_ColorCode(tm), Team_ColorName(tm), argv(2));
 +                                      }
 +                                      
 +                                      break;
 +                              }
 +                              default:
 +                              {
 +                                      return;
 +                              }
 +                      }
 +                      
 +                      send_TeamNames(MSG_BROADCAST, NULL);
 +                      return;
 +              }
 +
 +              default:
 +                      LOG_INFOF("Incorrect parameters for ^2%s^7", argv(0));
 +              case CMD_REQUEST_USAGE:
 +              {
 +                      LOG_HELP("Usage:^3 sv_cmd sendteams");
 +                      LOG_HELP("  No arguments required.");
 +                      return;
 +              }
 +      }
 +}
 +
  void GameCommand_allready(int request)
  {
        switch (request)
@@@ -388,9 -333,9 +388,9 @@@ void GameCommand_bot_cmd(int request, i
                                cvar_settemp("minplayers", "0");
                                cvar_settemp("minplayers_per_team", "0");
                                cvar_settemp("bot_number", "0");
-                               bot_fixcount();
+                               bot_fixcount(false);  // Kill all bots.
                                cvar_settemp("bot_number", argv(2));
-                               if (!bot_fixcount()) LOG_INFO("Sorry, could not set requested bot count");
+                               if (!bot_fixcount(true)) LOG_INFO("Sorry, could not set requested bot count");
                                return;
                        }
                        else if (argv(1) == "load" && argc == 3)
                                                        cvar_settemp("minplayers", "0");
                                                        cvar_settemp("minplayers_per_team", "0");
                                                        cvar_settemp("bot_number", "0");
-                                                       bot_fixcount();
+                                                       bot_fixcount(false);  // Kill all bots.
                                                        cvar_settemp("bot_number", argv(3));
-                                                       if (!bot_fixcount()) LOG_INFO("Sorry, could not set requested bot count");
+                                                       if (!bot_fixcount(true)) LOG_INFO("Sorry, could not set requested bot count");
                                                }
                                                else
                                                {
@@@ -978,46 -923,6 +978,46 @@@ void GameCommand_gotomap(int request, i
        }
  }
  
 +void GameCommand_ircmsg(int request, int argc, string command)
 +{
 +      /* IRCSay from the SMB Modpack */
 +      switch (request)
 +      {
 +              case CMD_REQUEST_COMMAND:
 +              {
 +                      string msgstr = substring(command, strlen(argv(0))+1, strlen(command));
 +                      
 +                      if(msgstr == "")
 +                              return;
 +                      
 +                      string prefix;
 +                      if(substring(msgstr, 0, 3) == "^4*") // actions
 +                                      prefix = "\{3}";
 +                      else
 +                                      prefix = "\{1}";
 +
 +                      msgstr = strcat(prefix, strreplace("\n", " ", msgstr), "\n"); // newlines only are good for centerprint
 +
 +                      FOREACH_CLIENTSLOT(true,
 +                      {
 +                                      if(!intermission_running)
 +                                      if((autocvar_g_chat_nospectators == 1) || (autocvar_g_chat_nospectators == 2 && !(warmup_stage || game_stopped)))
 +                                      if(IS_PLAYER(it))
 +                                              continue;
 +                                      if(IS_REAL_CLIENT(it))
 +                                              sprint(it, msgstr);
 +                      });
 +              }
 +              
 +              default:
 +              case CMD_REQUEST_USAGE:
 +              {
 +                      LOG_HELP("Usage:^3 sv_cmd ircmsg message");
 +                      return;
 +              }
 +      }
 +}
 +
  void GameCommand_lockteams(int request)
  {
        switch (request)
                        if (teamplay)
                        {
                                lockteams = 1;
 -                              bprint("^1The teams are now locked.\n");
 +                              bprint("\{1}^1The teams are now locked.\n");
                        }
                        else
                        {
@@@ -1073,72 -978,6 +1073,72 @@@ void GameCommand_make_mapinfo(int reque
        }
  }
  
 +void GameCommand_setflag(int request, int argc)
 +{
 +      switch (request)
 +      {
 +              case CMD_REQUEST_COMMAND:
 +              {
 +                      entity client;
 +                      float accepted;
 +                      
 +                      client = GetFilteredEntity(argv(1));
 +                      accepted = VerifyClientEntity(client, false, false);
 +
 +                      if (accepted <= 0)
 +                      {
 +                              LOG_INFO("^1ERROR^7: Couldn't set country flag");
 +                              LOG_HELP("Usage:^3 sv_cmd setflag #client_id countrycode[0-249]");
 +                              return;
 +                      }
 +                      
 +                      client.countrycode = stof(argv(2));
 +                      LOG_INFO("^2SUCCESS^7: Country flag set!");
 +                      return;
 +              }
 +              default:
 +                      LOG_INFOF("Incorrect parameters for ^2%s^7", argv(0));
 +              case CMD_REQUEST_USAGE:
 +              {
 +                      LOG_HELP("Usage:^3 sv_cmd setflag #client_id countrycode[0-249]");
 +                      return;
 +              }
 +      }
 +}
 +
 +void GameCommand_setrank(int request, int argc)
 +{
 +      switch (request)
 +      {
 +              case CMD_REQUEST_COMMAND:
 +              {
 +                      entity client;
 +                      float accepted;
 +                      
 +                      client = GetFilteredEntity(argv(1));
 +                      accepted = VerifyClientEntity(client, false, false);
 +
 +                      if (accepted <= 0)
 +                      {
 +                              LOG_INFO("^1ERROR^7: Couldn't set player rank");
 +                              LOG_HELP("Usage:^3 sv_cmd setrank #client_id rank");
 +                              return;
 +                      }
 +                      
 +                      client.rank = strzone(argv(2));
 +                      LOG_INFO("^2SUCCESS^7: Player rank set!");
 +                      return;
 +              }
 +              default:
 +                      LOG_INFOF("Incorrect parameters for ^2%s^7", argv(0));
 +              case CMD_REQUEST_USAGE:
 +              {
 +                      LOG_HELP("Usage:^3 sv_cmd setrank #client_id #rank");
 +                      return;
 +              }
 +      }
 +}
 +
  void GameCommand_moveplayer(int request, int argc)
  {
        switch (request)
@@@ -1386,7 -1225,7 +1386,7 @@@ void GameCommand_setbots(int request, i
                                cvar_settemp("minplayers", "0");
                                cvar_settemp("minplayers_per_team", "0");
                                cvar_settemp("bot_number", argv(1));
-                               bot_fixcount();
+                               bot_fixcount(true);
                                return;
                        }
                }
@@@ -1726,7 -1565,7 +1726,7 @@@ void GameCommand_unlockteams(int reques
                        if (teamplay)
                        {
                                lockteams = 0;
 -                              bprint("^1The teams are now unlocked.\n");
 +                              bprint("\{1}^1The teams are now unlocked.\n");
                        }
                        else
                        {
@@@ -1829,7 -1668,6 +1829,7 @@@ SERVER_COMMAND(extendmatchtime, "Increa
  SERVER_COMMAND(gametype, "Simple command to change the active gametype") { GameCommand_gametype(request, arguments); }
  SERVER_COMMAND(gettaginfo, "Get specific information about a weapon model") { GameCommand_gettaginfo(request, arguments); }
  SERVER_COMMAND(gotomap, "Simple command to switch to another map") { GameCommand_gotomap(request, arguments); }
 +SERVER_COMMAND(ircmsg, "Chat message to be sent by IRC bots") { GameCommand_ircmsg(request, arguments, command); }
  SERVER_COMMAND(lockteams, "Disable the ability for players to switch or enter teams") { GameCommand_lockteams(request); }
  SERVER_COMMAND(make_mapinfo, "Automatically rebuild mapinfo files") { GameCommand_make_mapinfo(request); }
  SERVER_COMMAND(moveplayer, "Change the team/status of a player") { GameCommand_moveplayer(request, arguments); }
@@@ -1839,11 -1677,8 +1839,11 @@@ SERVER_COMMAND(radarmap, "Generate a ra
  SERVER_COMMAND(reducematchtime, "Decrease the timelimit value incrementally") { GameCommand_reducematchtime(request); }
  SERVER_COMMAND(resetmatch, "Soft restart the game without changing teams; goes back to warmup if enabled") { GameCommand_resetmatch(request); }
  SERVER_COMMAND(setbots, "Adjust how many bots are in the match") { GameCommand_setbots(request, arguments); }
 +SERVER_COMMAND(setflag, "Set client flag") { GameCommand_setflag(request, arguments); }
 +SERVER_COMMAND(setrank, "Set client rank") { GameCommand_setrank(request, arguments); }
  SERVER_COMMAND(shuffleteams, "Randomly move players to different teams") { GameCommand_shuffleteams(request); }
  SERVER_COMMAND(stuffto, "Send a command to be executed on a client") { GameCommand_stuffto(request, arguments); }
 +SERVER_COMMAND(teamname, "Set team name") { GameCommand_teamname(request, arguments); }
  SERVER_COMMAND(trace, "Various debugging tools with tracing") { GameCommand_trace(request, arguments); }
  SERVER_COMMAND(unlockteams, "Enable the ability for players to switch or enter teams") { GameCommand_unlockteams(request); }
  SERVER_COMMAND(warp, "Choose different level in campaign") { GameCommand_warp(request, arguments); }
index 8019d4a42db1b7a50d95254b74458a562a46a407,42d78dd0ac29dae5653e216bc227920eb4669f05..2a03697479f8d0fb1726c7a5a5671c41078b52fa
@@@ -219,10 -219,6 +219,10 @@@ void VoteCount(float first_count
  
        // add up all the votes from each connected client
        FOREACH_CLIENT(IS_REAL_CLIENT(it) && IS_CLIENT(it), {
 +              // z411
 +              if(vote_target_type == VOTE_TARGET_TEAM && it.team != vote_caller.team) continue;
 +              if(vote_target_type == VOTE_TARGET_SINGLE && it != vote_target) continue;
 +              
                ++vote_player_count;
                if (IS_PLAYER(it))   ++vote_real_player_count;
                switch (it.vote_selection)
@@@ -356,12 -352,6 +356,12 @@@ void reset_map(bool dorespawn, bool is_
                if (round_handler_IsActive())
                        round_handler_Reset(game_starttime);
        }
 +      
 +      // for RJZ
 +      if (autocvar_rjz_count_shards) {
 +              total_shards = 0;
 +              send_TotalShardsAll();
 +      }
  
        if (shuffleteams_on_reset_map)
        {
@@@ -445,7 -435,7 +445,7 @@@ void ReadyRestart_force(bool is_fake_ro
        // clear overtime, we have to decrease timelimit to its original value again.
        if (checkrules_overtimesadded > 0 && g_race_qualifying != 2)
                cvar_set("timelimit", ftos(autocvar_timelimit - (checkrules_overtimesadded * autocvar_timelimit_overtime)));
-       checkrules_suddendeathend = checkrules_overtimesadded = checkrules_suddendeathwarning = 0;
+       checkrules_suddendeathend = checkrules_overtimesadded = checkrules_suddendeathwarning = overtimes = 0;
  
        if(warmup_stage)
                game_starttime = time; // Warmup: No countdown in warmup
                localcmd("\nsv_hook_warmupend\n");
  
        // reset the .ready status of all players (also spectators)
 -      FOREACH_CLIENT(IS_REAL_CLIENT(it), { it.ready = false; });
 +      FOREACH_CLIENT(IS_REAL_CLIENT(it), {
 +              it.ready = false;
 +              Kill_Notification(NOTIF_ONE_ONLY, it, MSG_CENTER, CPID_MISSING_READY);
 +      });
        readycount = 0;
        Nagger_ReadyCounted();  // NOTE: this causes a resend of that entity, and will also turn off warmup state on the client
  
                FOREACH_CLIENT(IS_PLAYER(it) && IS_REAL_CLIENT(it), { CS(it).allowed_timeouts = autocvar_sv_timeout_number; });
        }
  
 +      round_handler_Activate(!warmup_stage);
        if (!sv_ready_restart_after_countdown || warmup_stage)
                reset_map(true, is_fake_round_start);
  
@@@ -706,8 -692,6 +706,8 @@@ int VoteCommand_parse(entity caller, st
                case MUT_VOTEPARSE_UNACCEPTABLE: { return 0; }
        }
  
 +      vote_target_type = VOTE_TARGET_ALL;
 +      
        switch (first_command) // now go through and parse the proper commands to adjust as needed.
        {
                case "kick":
                                if (first_command == "kickban")
                                        command_arguments = strcat(ftos(autocvar_g_ban_default_bantime), " ", ftos(autocvar_g_ban_default_masksize), " ~");
  
 -                              vote_parsed_command = strcat(first_command, " # ", ftos(etof(victim)), " ", command_arguments);
 +                              if (first_command == "kick") // z411 : Use our kick implementation - kind of hacky...
 +                                      vote_parsed_command = strcat("defer 2 \"sv_cmd kickkick # ", ftos(etof(victim)), " ", command_arguments, "\"");
 +                              else
 +                                      vote_parsed_command = strcat("defer 2 \"", first_command, " # ", ftos(etof(victim)), " ", command_arguments, "\"");
 +                              
                                vote_parsed_display = sprintf("^1%s #%d ^7%s^1 %s", first_command, etof(victim), victim.netname, reason);
                        }
                        else
                {
                        vote_command = ValidateMap(argv(startpos + 1), caller);
                        if (!vote_command)  return -1;
 -                      vote_parsed_command = strcat("gotomap ", vote_command);
 -                      vote_parsed_display = strzone(strcat("^1", vote_parsed_command));
 +                      vote_parsed_command = strcat("defer 2 \"gotomap ", vote_command, "\"");
 +                      vote_parsed_display = strzone(strcat("^1gotomap ", vote_command));
  
                        break;
                }
 +              
 +              // z411 team calls
 +              case "teamname":
 +              {
 +                      if (teamplay && Team_IsValidTeam(caller.team)) {
 +                              vote_target_type = VOTE_TARGET_TEAM;
 +                      
 +                              string tmname = strtolower(Static_Team_ColorName(caller.team));
 +                              string newname = argv(startpos + 1);
 +                              
 +                              vote_parsed_command = strcat(first_command, " ", tmname, " \"", newname, "\"");
 +                              vote_parsed_display = strzone(strcat("^3(Team) ^1", first_command, " ^2", newname));
 +                      } else { print_to(caller, "vcall: Not in a team\n"); return 0; }
 +                      
 +                      break;
 +              }
  
                // TODO: replicate the old behaviour of being able to vote for maps from different modes on multimode servers (possibly support it in gotomap too)
                // maybe fallback instead of aborting if map name is invalid?
                        break;
                }
  
 -              case "restart":
 +              case "gg":
 +              case "shuffleteams":
 +              case "endmatch":
                {
                        // add a delay so that vote result can be seen and announcer can be heard
                        // if the vote is accepted
 -                      vote_parsed_command = strcat("defer 1 ", vote_command);
 +                      vote_parsed_command = strcat("defer 2 ", vote_command);
 +                      vote_parsed_display = strzone(strcat("^1", vote_command));
 +                      
 +                      break;
 +              }
 +
 +              case "reset":
 +              case "restart": // re-direct all match restarting to resetmatch
 +                      vote_command = "resetmatch"; // fall-through
 +              case "resetmatch":
 +              {
 +                      vote_parsed_command = strcat("defer 2 ", vote_command);
                        vote_parsed_display = strzone(strcat("^1", vote_command));
  
                        break;
                                return -1;
                        }
  
 -                      vote_parsed_command = vote_command;
 +                      vote_parsed_command = strcat("defer 2 ", vote_command);
                        vote_parsed_display = strzone(strcat("^1", vote_command));
                        break;
                }
index 355f241ef1b680f1d71138c0d93127e8b1a723eb,7c96223a71318d736040fa6fa5d091275843c564..b3079a20732abe6298aa4b38e1a3fcda5c0ad0fe
@@@ -37,11 -37,6 +37,11 @@@ const float VOTE_NULL = 0
  const float VOTE_NORMAL = 1;
  const float VOTE_MASTER = 2;
  
 +// z411 vote targets
 +const float VOTE_TARGET_ALL = 0;
 +const float VOTE_TARGET_TEAM = 1;
 +const float VOTE_TARGET_SINGLE = 2;
 +
  // global vote information declarations
  entity vote_caller;         // original caller of the current vote
  string vote_caller_name;    // name of the vote caller
@@@ -51,8 -46,6 +51,8 @@@ float vote_accept_count;    // total am
  float vote_reject_count;    // same as above, but rejected
  float vote_abstain_count;   // same as above, but abstained
  float vote_needed_overall;  // total amount of players NEEDED for a vote to pass (based on sv_vote_majority_factor)
 +float vote_target_type; // z411
 +entity vote_target; // z411
  .float vote_master;         // flag for if the player has vote master privelages
  .float vote_waittime;       // flag for how long the player must wait before they can vote again
  .float vote_selection;      // flag for which vote selection the player has made (See VOTE_SELECT_*)
@@@ -71,7 -64,7 +71,7 @@@ const float RESTART_COUNTDOWN = 10
  entity nagger;
  float readycount;                  // amount of players who are ready
  .float ready;                      // flag for if a player is ready
- .float last_ready;                               // z411 time of the last readyup for anti-spam
+ .float last_ready;                 // last ready time for anti-spam
  .int team_saved;                   // team number to restore upon map reset
  .void(entity this) reset;            // if set, an entity is reset using this
  .void(entity this) reset2;         // if set, an entity is reset using this (after calling ALL the reset functions for other entities)
diff --combined qcsrc/server/damage.qc
index a2f2b722a7a753c6f3c14d2f85c8c885b268d126,d0b3a288fb3011d5472d1d5df49a2d58ea217399..8d2747bac5e68789c408504e03844f0c1bb2c68b
@@@ -124,7 -124,6 +124,7 @@@ void LogDeath(string mode, int deathtyp
  
  void Obituary_SpecialDeath(
        entity notif_target,
 +      entity attacker,
        float murder,
        int deathtype,
        string s1, string s2, string s3,
                        f1, f2, f3, 0
                );
        }
 +      
 +      if(deathtype == DEATH_TELEFRAG.m_id) {
 +              Give_Medal(attacker, TELEFRAG);
 +      }
  }
  
  float Obituary_WeaponDeath(
        entity notif_target,
 +      entity attacker,
        float murder,
        int deathtype,
        string s1, string s2, string s3,
                        s1, s2, s3, "",
                        f1, f2, 0, 0
                );
 +              
 +              // z411 special medals
 +              if(attacker) {
 +                      switch(death_message) {
 +                              case WEAPON_SHOTGUN_MURDER_SLAP:
 +                                      if(!cvar("g_melee_only")) { // don't spam humiliation if we're in melee_only mode
 +                                              Give_Medal(attacker, HUMILIATION);
 +                                      }
 +                                      break;
 +                              case WEAPON_ELECTRO_MURDER_COMBO:
 +                                      Give_Medal(attacker, ELECTROBITCH);
 +                                      break;
 +                      }
 +              }
        }
        else
        {
@@@ -266,7 -246,7 +266,7 @@@ void Obituary(entity attacker, entity i
        notif_anonymous = M_ARGV(5, bool);
  
        if(notif_anonymous)
 -              attacker_name = "Anonymous player";
 +              attacker_name = "???";
  
        #ifdef NOTIFICATIONS_DEBUG
        Debug_Notification(
                {
                        if(deathtype == DEATH_TEAMCHANGE.m_id || deathtype == DEATH_AUTOTEAMCHANGE.m_id)
                        {
 -                              Obituary_SpecialDeath(targ, false, deathtype, targ.netname, deathlocation, "", targ.team, 0, 0);
 +                              Obituary_SpecialDeath(targ, NULL, false, deathtype, targ.netname, deathlocation, "", targ.team, 0, 0);
                        }
                        else
                        {
                                {
                                        case DEATH_MIRRORDAMAGE:
                                        {
 -                                              Obituary_SpecialDeath(targ, false, deathtype, targ.netname, deathlocation, "", CS(targ).killcount, 0, 0);
 +                                              Obituary_SpecialDeath(targ, NULL, false, deathtype, targ.netname, deathlocation, "", CS(targ).killcount, 0, 0);
                                                break;
                                        }
                                        case DEATH_HURTTRIGGER:
 -                                              Obituary_SpecialDeath(targ, false, deathtype, targ.netname, inflictor.message, deathlocation, CS(targ).killcount, 0, 0);
 +                                              Obituary_SpecialDeath(targ, NULL, false, deathtype, targ.netname, inflictor.message, deathlocation, CS(targ).killcount, 0, 0);
                                                break;
                                        default:
                                        {
 -                                              Obituary_SpecialDeath(targ, false, deathtype, targ.netname, deathlocation, "", CS(targ).killcount, 0, 0);
 +                                              Obituary_SpecialDeath(targ, NULL, false, deathtype, targ.netname, deathlocation, "", CS(targ).killcount, 0, 0);
                                                break;
                                        }
                                }
                        }
                }
 -              else if (!Obituary_WeaponDeath(targ, false, deathtype, targ.netname, deathlocation, "", CS(targ).killcount, 0))
 +              else if (!Obituary_WeaponDeath(targ, NULL, false, deathtype, targ.netname, deathlocation, "", CS(targ).killcount, 0))
                {
                        backtrace("SUICIDE: what the hell happened here?\n");
                        return;
                }
                LogDeath("suicide", deathtype, targ, targ);
 +              Send_Notification(NOTIF_ONE, targ, MSG_ANNCE, ANNCE_SUICIDE);
                if(deathtype != DEATH_AUTOTEAMCHANGE.m_id) // special case: don't negate frags if auto switched
                        GiveFrags(attacker, targ, -1, deathtype, weaponentity);
        }
  
                        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_name);
 -                      Send_Notification(NOTIF_ALL, NULL, MSG_INFO, APP_TEAM_NUM(targ.team, INFO_DEATH_TEAMKILL), targ.netname, attacker_name, deathlocation, CS(targ).killcount);
 +                      Send_Notification(NOTIF_ALL, NULL, MSG_INFO, APP_TEAM_NUM(targ.team, INFO_DEATH_TEAMKILL),
 +                              playername(targ.netname, targ.team, true), playername(attacker_name, attacker.team, true),
 +                              deathlocation, CS(targ).killcount);
  
                        // In this case, the death message will ALWAYS be "foo was betrayed by bar"
                        // No need for specific death/weapon messages...
                        CS(attacker).killcount = CS(attacker).killcount + 1;
  
                        attacker.killsound += 1;
 -
 +                      
                        // TODO: improve SPREE_ITEM and KILL_SPREE_LIST
                        // these 2 macros are spread over multiple files
                        #define SPREE_ITEM(counta,countb,center,normal,gentle) \
                                case counta: \
 -                                      Send_Notification(NOTIF_ONE, attacker, MSG_ANNCE, ANNCE_KILLSTREAK_##countb); \
 +                                      Give_Medal(attacker, KILLSTREAK_##countb); \
                                        if (!warmup_stage) \
                                                PlayerStats_GameReport_Event_Player(attacker, PLAYERSTATS_ACHIEVEMENT_KILL_SPREE_##counta, 1); \
                                        break;
                        {
                                checkrules_firstblood = true;
                                notif_firstblood = true; // modify the current messages so that they too show firstblood information
 +                              Give_Medal(attacker, FIRSTBLOOD);
                                PlayerStats_GameReport_Event_Player(attacker, PLAYERSTATS_ACHIEVEMENT_FIRSTBLOOD, 1);
                                PlayerStats_GameReport_Event_Player(targ, PLAYERSTATS_ACHIEVEMENT_FIRSTVICTIM, 1);
  
                                kill_count_to_attacker = CS(attacker).killcount;
                                kill_count_to_target = 0;
                        }
 +                      
 +                      // Excellent check
 +                      if(attacker.lastkill && attacker.lastkill > time - autocvar_g_medals_excellent_time) {
 +                              Give_Medal(attacker, EXCELLENT);
 +                      }
 +                      attacker.lastkill = time;
  
                        if(targ.istypefrag)
                        {
                        if(deathtype == DEATH_BUFF.m_id)
                                f3 = buff_FirstFromFlags(attacker).m_id;
  
 -                      if (!Obituary_WeaponDeath(targ, true, deathtype, targ.netname, attacker_name, deathlocation, CS(targ).killcount, kill_count_to_attacker))
 -                              Obituary_SpecialDeath(targ, true, deathtype, targ.netname, attacker_name, deathlocation, CS(targ).killcount, kill_count_to_attacker, f3);
 +                      if (!Obituary_WeaponDeath(targ, attacker, true, deathtype, playername(targ.netname, targ.team, true), playername(attacker_name, attacker.team, true), deathlocation, CS(targ).killcount, kill_count_to_attacker))
 +                              Obituary_SpecialDeath(targ, attacker, true, deathtype, playername(targ.netname, targ.team, true), playername(attacker_name, attacker.team, true), deathlocation, CS(targ).killcount, kill_count_to_attacker, f3);
                }
        }
  
                        // and there will be a REAL DEATH_VOID implementation which mappers will use.
                        case DEATH_HURTTRIGGER:
                        {
 -                              Obituary_SpecialDeath(targ, false, deathtype,
 -                                      targ.netname,
 +                              Obituary_SpecialDeath(targ, NULL, false, deathtype,
 +                                      playername(targ.netname, targ.team, true),
                                        inflictor.message,
                                        deathlocation,
                                        CS(targ).killcount,
  
                        case DEATH_CUSTOM:
                        {
 -                              Obituary_SpecialDeath(targ, false, deathtype,
 -                                      targ.netname,
 +                              Obituary_SpecialDeath(targ, NULL, false, deathtype,
 +                                      playername(targ.netname, targ.team, true),
                                        ((strstrofs(deathmessage, "%", 0) < 0) ? strcat("%s ", deathmessage) : deathmessage),
                                        deathlocation,
                                        CS(targ).killcount,
  
                        default:
                        {
 -                              Obituary_SpecialDeath(targ, false, deathtype, targ.netname, deathlocation, "", CS(targ).killcount, 0, 0);
 +                              Obituary_SpecialDeath(targ, NULL, false, deathtype, playername(targ.netname, targ.team, true), deathlocation, "", CS(targ).killcount, 0, 0);
                                break;
                        }
                }
  
                LogDeath("accident", deathtype, targ, targ);
 +              Send_Notification(NOTIF_ONE, targ, MSG_ANNCE, ANNCE_ACCIDENT);
                GiveFrags(targ, targ, -1, deathtype, weaponentity);
  
                if(GameRules_scoring_add(targ, SCORE, 0) == -5)
@@@ -912,6 -881,8 +912,8 @@@ float RadiusDamageForSource (entity inf
                return 0;
        }
  
+       if (rad < 0) rad = 0;
        RadiusDamage_running = 1;
  
        tfloordmg = autocvar_g_throughfloor_damage;
                if (((cantbe != targ) && !mustbe) || (mustbe == targ))
                if (targ.takedamage)
                {
-                       vector nearest;
-                       vector diff;
-                       float power;
-                       // LordHavoc: measure distance to nearest point on target (not origin)
-                       // (this guarentees 100% damage on a touch impact)
-                       nearest = targ.WarpZone_findradius_nearest;
-                       diff = targ.WarpZone_findradius_dist;
+                       // measure distance from nearest point on target (not origin)
+                       // to nearest point on inflictor (not origin)
+                       vector nearest = targ.WarpZone_findradius_nearest;
+                       vector inflictornearest = NearestPointOnBoundingBox(
+                               inflictororigin - (inflictor.maxs - inflictor.mins) * 0.5,
+                               inflictororigin + (inflictor.maxs - inflictor.mins) * 0.5,
+                               nearest);
+                       vector diff = inflictornearest - nearest;
                        // round up a little on the damage to ensure full damage on impacts
                        // and turn the distance into a fraction of the radius
-                       power = 1 - ((vlen (diff) - bound(MIN_DAMAGEEXTRARADIUS, targ.damageextraradius, MAX_DAMAGEEXTRARADIUS)) / rad);
-                       //bprint(" ");
-                       //bprint(ftos(power));
-                       //if (targ == attacker)
-                       //      print(ftos(power), "\n");
-                       if (power > 0)
+                       float dist = max(0, vlen(diff) - bound(MIN_DAMAGEEXTRARADIUS, targ.damageextraradius, MAX_DAMAGEEXTRARADIUS));
+                       if (dist <= rad)
                        {
-                               float finaldmg;
-                               if (power > 1)
-                                       power = 1;
-                               finaldmg = coredamage * power + edgedamage * (1 - power);
+                               float power = 1;
+                               if (rad > 0)
+                                       power -= (dist / rad);
+                               // at this point power can't be < 0 or > 1
+                               float finaldmg = coredamage * power + edgedamage * (1 - power);
                                if (finaldmg > 0)
                                {
                                        float a;
diff --combined qcsrc/server/damage.qh
index a2b853428e862bf5fc79053eebe63636d44dcc91,a1dadc1a28f052017abbcbddd6252a3c7ec3cc65..bbd7f4154be827fdc3065cb7c37823b46465e9e5
@@@ -64,7 -64,6 +64,7 @@@ int impressive_hits
  .float spawnshieldtime;
  
  .int totalfrags;
 +.float lastkill;
  
  .bool canteamdamage;
  
@@@ -90,7 -89,6 +90,7 @@@ void LogDeath(string mode, int deathtyp
  
  void Obituary_SpecialDeath(
        entity notif_target,
 +      entity attacker,
        float murder,
        int deathtype,
        string s1, string s2, string s3,
@@@ -99,7 -97,6 +99,7 @@@
  float w_deathtype;
  float Obituary_WeaponDeath(
        entity notif_target,
 +      entity target,
        float murder,
        int deathtype,
        string s1, string s2, string s3,
@@@ -138,7 -135,7 +138,7 @@@ float RadiusDamageForSource (entity inf
  float RadiusDamage (entity inflictor, entity attacker, float coredamage, float edgedamage, float rad, entity cantbe, entity mustbe, float forceintensity, int deathtype, .entity weaponentity, entity directhitentity);
  
  .float damageforcescale;
- const float MIN_DAMAGEEXTRARADIUS = 2;
+ const float MIN_DAMAGEEXTRARADIUS = 0.1;
  const float MAX_DAMAGEEXTRARADIUS = 16;
  .float damageextraradius;
  
@@@ -150,7 -147,6 +150,6 @@@ bool Heal(entity targ, entity inflictor
  .float fire_deathtype;
  .entity fire_owner;
  .float fire_hitsound;
- .entity fire_burner;
  
  float Fire_AddDamage(entity e, entity o, float d, float t, float dt);
  
index bb0da8681e9c90c6fe10db0c399cb188a8956972,ed39c7bca91e11fcb3d11d32f590c737379e0355..faec01a4c7c4b852440fbc5aa355db6f92810164
@@@ -11,7 -11,6 +11,7 @@@
  #include <common/mutators/mutator/buffs/sv_buffs.qh>
  #include <common/mutators/mutator/powerups/_mod.qh>
  #include <common/mutators/mutator/status_effects/_mod.qh>
 +#include <common/net_linked.qh>
  #include <common/notifications/all.qh>
  #include <common/resources/resources.qh>
  #include <common/util.qh>
@@@ -218,8 -217,6 +218,8 @@@ void Item_Respawn(entity this
  
  void Item_RespawnCountdown(entity this)
  {
 +      if(game_timeout) { this.nextthink = time + 1; return; }
 +      
        if(this.item_respawncounter >= ITEM_RESPAWN_TICKS)
        {
                if(this.waypointsprite_attached)
        else
        {
                this.nextthink = time + 1;
 -              this.item_respawncounter += 1;
 +              this.item_respawncounter = floor((time - game_starttime) - (this.scheduledrespawntime - ITEM_RESPAWN_TICKS)) + 1;
 +              //this.item_respawncounter += 1;
 +              //LOG_INFOF("Respawncounter: %d", this.item_respawncounter);
 +              if(this.item_respawncounter < 1) return;
 +              
                if(this.item_respawncounter == 1)
                {
                        do {
  
  void Item_RespawnThink(entity this)
  {
 -      this.nextthink = time;
 +      this.nextthink = time + 1;
        if(this.origin != this.oldorigin)
                ItemUpdate(this);
 -
 -      if(time >= this.wait)
 +      
 +      if(!game_timeout && time - game_starttime >= this.wait)
                Item_Respawn(this);
 +      
 +      //LOG_INFOF("time until respawn %d", (this.wait) - (time - game_starttime));
  }
  
  void Item_ScheduleRespawnIn(entity e, float t)
        if ((Item_ItemsTime_Allow(e.itemdef) || (STAT(WEAPONS, e) & WEPSET_SUPERWEAPONS) || MUTATOR_CALLHOOK(Item_ScheduleRespawn, e, t)) && (t - ITEM_RESPAWN_TICKS) > 0)
        {
                setthink(e, Item_RespawnCountdown);
 -              e.nextthink = time + max(0, t - ITEM_RESPAWN_TICKS);
 -              e.scheduledrespawntime = e.nextthink + ITEM_RESPAWN_TICKS;
 +              //e.nextthink = time - timeout_total_time + max(0, t - ITEM_RESPAWN_TICKS);
 +              //e.scheduledrespawntime = e.nextthink + ITEM_RESPAWN_TICKS;
 +              e.nextthink = time;
 +              e.scheduledrespawntime = time - game_starttime + t;
                e.item_respawncounter = 0;
 +              
                if(Item_ItemsTime_Allow(e.itemdef) || (STAT(WEAPONS, e) & WEPSET_SUPERWEAPONS))
                {
                        t = Item_ItemsTime_UpdateTime(e, e.scheduledrespawntime);
        {
                setthink(e, Item_RespawnThink);
                e.nextthink = time;
 -              e.scheduledrespawntime = time + t;
 -              e.wait = time + t;
 +              e.scheduledrespawntime = time - game_starttime + t;
 +              e.wait = time - game_starttime + t;
  
                if(Item_ItemsTime_Allow(e.itemdef) || (STAT(WEAPONS, e) & WEPSET_SUPERWEAPONS))
                {
@@@ -488,15 -476,6 +488,15 @@@ bool Item_GiveAmmoTo(entity item, entit
        return true;
  }
  
 +void Item_NotifyWeapon(entity player, int wep)
 +{
 +      if(IS_REAL_CLIENT(player)) {
 +              msg_entity = player;
 +              WriteHeader(MSG_ONE, TE_CSQC_WEAPONPICKUP);
 +              WriteByte(MSG_ONE, wep);
 +      }
 +}
 +
  bool Item_GiveTo(entity item, entity player)
  {
        // if nothing happens to player, just return without taking the item
        pickedup |= Item_GiveAmmoTo(item, player, RES_CELLS, g_pickup_cells_max);
        pickedup |= Item_GiveAmmoTo(item, player, RES_PLASMA, g_pickup_plasma_max);
        pickedup |= Item_GiveAmmoTo(item, player, RES_FUEL, g_pickup_fuel_max);
 +      
 +      // for RJZ
 +      if (autocvar_rjz_count_shards && !warmup_stage && item.itemdef == ITEM_ArmorSmall) {
 +              total_shards++;
 +              send_TotalShardsAll();
 +      }
 +      
        if (item.itemdef.instanceOfWeaponPickup)
        {
 -              WepSet w;
 +              WepSet w, wp;
                w = STAT(WEAPONS, item);
 -              w &= ~STAT(WEAPONS, player);
 +              wp = w & ~STAT(WEAPONS, player);
  
                if (w || (item.spawnshieldtime && item.pickup_anyway > 0))
                {
                        pickedup = true;
                        FOREACH(Weapons, it != WEP_Null, {
 -                              if(w & (it.m_wepset))
 +                              Weapon wep = it;
 +                              
 +                              if(w & (wep.m_wepset)) {
 +                                      // z411 Seriously find a better way to do this
 +                                      Item_NotifyWeapon(player, wep.m_id);
 +                                      FOREACH_CLIENT(IS_SPEC(it) && it.enemy == player, { Item_NotifyWeapon(it, wep.m_id); });
 +                              }
 +                              
 +                              if(wp & (wep.m_wepset))
                                {
                                        for(int slot = 0; slot < MAX_WEAPONSLOTS; ++slot)
                                        {
                                                .entity weaponentity = weaponentities[slot];
                                                if(player.(weaponentity).m_weapon != WEP_Null || slot == 0)
 -                                                      W_DropEvent(wr_pickup, player, it.m_id, item, weaponentity);
 +                                                      W_DropEvent(wr_pickup, player, wep.m_id, item, weaponentity);
                                        }
 -                                      W_GiveWeapon(player, it.m_id);
 +                                      W_GiveWeapon(player, wep.m_id);
                                }
                        });
                }
@@@ -1471,12 -1435,12 +1471,12 @@@ void GiveSound(entity e, float v0, floa
        if(v1 <= v0 - t)
        {
                if(snd_decr != NULL)
-                       sound (e, CH_TRIGGER, snd_decr, VOL_BASE, ATTEN_NORM);
+                       sound(e, CH_TRIGGER, snd_decr, VOL_BASE, ATTEN_NORM);
        }
        else if(v0 >= v0 + t)
        {
                if(snd_incr != NULL)
-                       sound (e, CH_TRIGGER, snd_incr, VOL_BASE, ATTEN_NORM);
+                       sound(e, ((snd_incr == SND_POWERUP) ? CH_TRIGGER_SINGLE : CH_TRIGGER), snd_incr, VOL_BASE, ATTEN_NORM);
        }
  }
  
index a9c95a9c412c4c00a7b79af93a2181f18f9faa94,6feb8113233dd0280b964a3b7f7e0687a324b89a..d55cd977066fd4fc35aade3580ffe3afc9dce9e4
@@@ -714,24 -714,27 +714,27 @@@ enum 
      /**/
  MUTATOR_HOOKABLE(ItemTouched, EV_ItemTouched);
  
+ // The Resource hooks are often called by other hooks and to avoid conflicts
+ // as much as possible their args start from ARGV_7
  /** Called when the amount of entity resources changes. Can be used to override
  resource limit. */
  #define EV_GetResourceLimit(i, o) \
-       /** checked entity */ i(entity, MUTATOR_ARGV_0_entity) \
-       /** resource type */  i(entity, MUTATOR_ARGV_1_entity) \
-       /** limit */          i(float, MUTATOR_ARGV_2_float) \
-       /**/                  o(float, MUTATOR_ARGV_2_float) \
+       /** checked entity */ i(entity, MUTATOR_ARGV_7_entity) \
+       /** resource type */  i(entity, MUTATOR_ARGV_8_entity) \
+       /** limit */          i(float, MUTATOR_ARGV_9_float) \
+       /**/                  o(float, MUTATOR_ARGV_9_float) \
        /**/
  MUTATOR_HOOKABLE(GetResourceLimit, EV_GetResourceLimit);
  
  /** Called when the amount of resource of an entity changes. See RES_*
  constants for resource types. Return true to forbid the change. */
  #define EV_SetResource(i, o) \
-       /** checked entity */ i(entity, MUTATOR_ARGV_0_entity) \
-       /** resource type */  i(entity, MUTATOR_ARGV_1_entity) \
-       /**/                  o(entity, MUTATOR_ARGV_1_entity) \
-       /** amount */         i(float, MUTATOR_ARGV_2_float) \
-       /**/                  o(float, MUTATOR_ARGV_2_float) \
+       /** checked entity */ i(entity, MUTATOR_ARGV_7_entity) \
+       /** resource type */  i(entity, MUTATOR_ARGV_8_entity) \
+       /**/                  o(entity, MUTATOR_ARGV_8_entity) \
+       /** amount */         i(float, MUTATOR_ARGV_9_float) \
+       /**/                  o(float, MUTATOR_ARGV_9_float) \
        /**/
  MUTATOR_HOOKABLE(SetResource, EV_SetResource);
  
  constants for resource types. Amount wasted is the amount of resource that is
  above resource limit so it was not given. */
  #define EV_ResourceAmountChanged(i, o) \
-       /** checked entity */ i(entity, MUTATOR_ARGV_0_entity) \
-       /** resource type */  i(entity, MUTATOR_ARGV_1_entity) \
-       /** amount */         i(float, MUTATOR_ARGV_2_float) \
+       /** checked entity */ i(entity, MUTATOR_ARGV_7_entity) \
+       /** resource type */  i(entity, MUTATOR_ARGV_8_entity) \
+       /** amount */         i(float, MUTATOR_ARGV_9_float) \
        /**/
  MUTATOR_HOOKABLE(ResourceAmountChanged, EV_ResourceAmountChanged);
  
  limit. See RES_* constants for resource types. Amount wasted is the amount
  of resource that is above resource limit so it was not given. */
  #define EV_ResourceWasted(i, o) \
-       /** checked entity */ i(entity, MUTATOR_ARGV_0_entity) \
-       /** resource type */  i(entity, MUTATOR_ARGV_1_entity) \
-       /** amount wasted */  i(float, MUTATOR_ARGV_2_float) \
+       /** checked entity */ i(entity, MUTATOR_ARGV_7_entity) \
+       /** resource type */  i(entity, MUTATOR_ARGV_8_entity) \
+       /** amount wasted */  i(float, MUTATOR_ARGV_9_float) \
        /**/
  MUTATOR_HOOKABLE(ResourceWasted, EV_ResourceWasted);
  
  for resource types. Return true to forbid giving.
  NOTE: This hook is also called by GiveResourceWithLimit */
  #define EV_GiveResource(i, o) \
-       /** receiver */      i(entity, MUTATOR_ARGV_0_entity) \
-       /** resource type */ i(entity, MUTATOR_ARGV_1_entity) \
-       /**/                 o(entity, MUTATOR_ARGV_1_entity) \
-       /** amount */        i(float, MUTATOR_ARGV_2_float) \
-       /**/                 o(float, MUTATOR_ARGV_2_float) \
+       /** receiver */      i(entity, MUTATOR_ARGV_7_entity) \
+       /** resource type */ i(entity, MUTATOR_ARGV_8_entity) \
+       /**/                 o(entity, MUTATOR_ARGV_8_entity) \
+       /** amount */        i(float, MUTATOR_ARGV_9_float) \
+       /**/                 o(float, MUTATOR_ARGV_9_float) \
        /**/
  MUTATOR_HOOKABLE(GiveResource, EV_GiveResource);
  
  /** Called when entity is being given some resource with specified limit. See
  RES_* constants for resource types. Return true to forbid giving. */
  #define EV_GiveResourceWithLimit(i, o) \
-       /** receiver */      i(entity, MUTATOR_ARGV_0_entity) \
-       /** resource type */ i(entity, MUTATOR_ARGV_1_entity) \
-       /**/                 o(entity, MUTATOR_ARGV_1_entity) \
-       /** amount */        i(float, MUTATOR_ARGV_2_float) \
-       /**/                 o(float, MUTATOR_ARGV_2_float) \
-       /** limit */         i(float, MUTATOR_ARGV_3_float) \
-       /**/                 o(float, MUTATOR_ARGV_3_float) \
+       /** receiver */      i(entity, MUTATOR_ARGV_7_entity) \
+       /** resource type */ i(entity, MUTATOR_ARGV_8_entity) \
+       /**/                 o(entity, MUTATOR_ARGV_8_entity) \
+       /** amount */        i(float, MUTATOR_ARGV_9_float) \
+       /**/                 o(float, MUTATOR_ARGV_9_float) \
+       /** limit */         i(float, MUTATOR_ARGV_10_float) \
+       /**/                 o(float, MUTATOR_ARGV_10_float) \
        /**/
  MUTATOR_HOOKABLE(GiveResourceWithLimit, EV_GiveResourceWithLimit);
  
  for resource types. Return true to forbid giving.
  NOTE: This hook is also called by TakeResourceWithLimit */
  #define EV_TakeResource(i, o) \
-     /** receiver */      i(entity, MUTATOR_ARGV_0_entity) \
-     /** resource type */ i(entity, MUTATOR_ARGV_1_entity) \
-     /**/                 o(entity, MUTATOR_ARGV_1_entity) \
-     /** amount */        i(float, MUTATOR_ARGV_2_float) \
-     /**/                 o(float, MUTATOR_ARGV_2_float) \
+     /** receiver */      i(entity, MUTATOR_ARGV_7_entity) \
+     /** resource type */ i(entity, MUTATOR_ARGV_8_entity) \
+     /**/                 o(entity, MUTATOR_ARGV_8_entity) \
+     /** amount */        i(float, MUTATOR_ARGV_9_float) \
+     /**/                 o(float, MUTATOR_ARGV_9_float) \
      /**/
  MUTATOR_HOOKABLE(TakeResource, EV_TakeResource);
  
  /** Called when some resource is being taken from an entity, with a limit. See
  RES_* constants for resource types. Return true to forbid giving. */
  #define EV_TakeResourceWithLimit(i, o) \
-     /** receiver */      i(entity, MUTATOR_ARGV_0_entity) \
-     /** resource type */ i(entity, MUTATOR_ARGV_1_entity) \
-     /**/                 o(entity, MUTATOR_ARGV_1_entity) \
-     /** amount */        i(float, MUTATOR_ARGV_2_float) \
-     /**/                 o(float, MUTATOR_ARGV_2_float) \
-     /** limit */         i(float, MUTATOR_ARGV_3_float) \
-     /**/                 o(float, MUTATOR_ARGV_3_float) \
+     /** receiver */      i(entity, MUTATOR_ARGV_7_entity) \
+     /** resource type */ i(entity, MUTATOR_ARGV_8_entity) \
+     /**/                 o(entity, MUTATOR_ARGV_8_entity) \
+     /** amount */        i(float, MUTATOR_ARGV_9_float) \
+     /**/                 o(float, MUTATOR_ARGV_9_float) \
+     /** limit */         i(float, MUTATOR_ARGV_10_float) \
+     /**/                 o(float, MUTATOR_ARGV_10_float) \
      /**/
  MUTATOR_HOOKABLE(TakeResourceWithLimit, EV_TakeResourceWithLimit);
  
+ // END Resource hooks
  /** called at when a player connect */
  #define EV_ClientConnect(i, o) \
      /** player */ i(entity, MUTATOR_ARGV_0_entity) \
@@@ -860,7 -865,6 +865,7 @@@ MUTATOR_HOOKABLE(FireBullet_Hit, EV_Fir
  MUTATOR_HOOKABLE(FixPlayermodel, EV_FixPlayermodel);
  
  /** Return error to play frag remaining announcements */
 +MUTATOR_HOOKABLE(Scores_AnnounceLeads, EV_NO_ARGS);
  MUTATOR_HOOKABLE(Scores_CountFragsRemaining, EV_NO_ARGS);
  
  #define EV_GrappleHookThink(i, o) \
index 9c0431a5248ddef62f408e6c0c9e2ce6798837c3,6c9342b80e30bcb64ec2759cc4a67c245d3bc95f..44757cfd152b4c0bc6b30c53cd455b8d0b871de2
@@@ -4,14 -4,10 +4,14 @@@
  #include <common/util.qh>
  #include <server/campaign.qh>
  #include <server/command/vote.qh>
 +#include <server/weapons/accuracy.qh>
  #include <server/world.qh>
  
  void round_handler_Think(entity this)
  {
 +      if (!this.isactive) return;
 +      if (game_timeout) { this.nextthink = time + 1; return; }
 +
        if (intermission_running)
        {
                round_handler_Reset(0);
@@@ -46,7 -42,7 +46,8 @@@
                                this.cnt = 0;
                                this.round_endtime = (this.round_timelimit) ? time + this.round_timelimit : 0;
                                this.nextthink = time;
 +                              FOREACH_CLIENT(IS_PLAYER(it), { roundaccuracy_clear(it); });
+                               rounds_played++;
                                if (this.roundStart) this.roundStart();
                                return;
                        }
@@@ -55,6 -51,7 +56,7 @@@
                else
                {
                        round_handler_Reset(0);
+                       round_starttime = -1; // can't start
                }
                this.nextthink = time + 1;  // canRoundStart every second
        }
@@@ -80,6 -77,7 +82,7 @@@ void round_handler_Init(float the_delay
        this.count = fabs(floor(the_count));
        this.cnt = this.count + 1;
        this.round_timelimit = (the_round_timelimit > 0) ? the_round_timelimit : 0;
+       round_limit = the_round_timelimit;
  }
  
  // NOTE: this is only needed because if round_handler spawns at time 1
@@@ -99,26 -97,14 +102,26 @@@ void round_handler_Spawn(bool() canRoun
                return;
        }
        entity this = round_handler = new_pure(round_handler);
 -
 -      setthink(this, round_handler_FirstThink);
 +      
        this.canRoundStart = canRoundStart_func;
        this.canRoundEnd = canRoundEnd_func;
        this.roundStart = roundStart_func;
        this.wait = false;
        round_handler_Init(5, 5, 180);
 -      this.nextthink = time;
 +}
 +
 +void round_handler_Activate(bool active) {
 +      if (round_handler) {
 +              entity this = round_handler;
 +      
 +              this.isactive = active;
 +              if(active) {
 +                      setthink(this, round_handler_FirstThink);
 +                      this.nextthink = time;
 +              } else {
 +                      round_starttime = -1;
 +              }
 +      }
  }
  
  void round_handler_Reset(float next_think)
        if (this.count)
                if (this.cnt < this.count + 1) this.cnt = this.count + 1;
        this.nextthink = next_think;
-       round_starttime = (next_think) ? (next_think + this.count) : -1;
+       if (next_think)
+       {
+               if (next_think <= game_starttime) rounds_played = 0;
+               round_starttime = next_think + this.count;
+       }
  }
  
  void round_handler_Remove()
diff --combined qcsrc/server/scores.qc
index 6c82292d482f4e11241d24aaef389566a58790e5,38fd7f40e16ee2a2c76d3db8bd476f08fcc7ed55..612e37e56d89c7d53b87a7df0d49557ed3938ca3
@@@ -194,6 -194,7 +194,7 @@@ void ScoreInfo_SetLabel_TeamScore(floa
        }
  }
  
+ .bool welcome_msg_already_sent_on_connection;
  bool ScoreInfo_SendEntity(entity this, entity to, int sf)
  {
        float i;
                WriteString(MSG_ENTITY, teamscores_label(i));
                WriteByte(MSG_ENTITY, teamscores_flags(i));
        }
+       bool welcome_msg_too = (!to.welcome_msg_already_sent_on_connection);
+       WriteByte(MSG_ENTITY, welcome_msg_too);
+       // welcome message is sent here because it needs to know the gametype
+       if (welcome_msg_too)
+       {
+               SendWelcomemessage_msg_type(this, false, MSG_ENTITY);
+               to.welcome_msg_already_sent_on_connection = true;
+       }
        return true;
  }
  
@@@ -276,8 -285,8 +285,8 @@@ float PlayerScore_Clear(entity player
        sk = CS(player).scorekeeper;
        FOREACH(Scores, true, {
                if(sk.(scores(it)) != 0)
 -                      if(scores_label(it) != "")
 -                              sk.SendFlags |= BIT(i % 16);
 +                      //if(scores_label(it) != "")
 +                      sk.SendFlags |= (2 ** (i % 16));
                if(i != SP_ELO.m_id)
                        sk.(scores(it)) = 0;
        });
@@@ -294,8 -303,8 +303,8 @@@ void Score_ClearAll(
                if (!sk) continue;
                FOREACH(Scores, true, {
                        if(sk.(scores(it)) != 0)
 -                              if(scores_label(it) != "")
 -                                      sk.SendFlags |= BIT(i % 16);
 +                              //if(scores_label(it) != "")
 +                              sk.SendFlags |= (2 ** (i % 16));
                        if(i != SP_ELO.m_id)
                                sk.(scores(it)) = 0;
                });
                for(int j = 0; j < MAX_TEAMSCORE; ++j)
                {
                        if(sk.(teamscores(j)) != 0)
 -                              if(teamscores_label(j) != "")
 -                                      sk.SendFlags |= BIT(j);
 +                              //if(teamscores_label(j) != "")
 +                              sk.SendFlags |= (2 ** j);
                        sk.(teamscores(j)) = 0;
                }
        }
@@@ -356,8 -365,8 +365,8 @@@ float PlayerScore_Add(entity player, Pl
        {
                return s.(scores(scorefield));
        }
 -      if(scores_label(scorefield) != "")
 -              s.SendFlags |= BIT(scorefield.m_id % 16);
 +      //if(scores_label(scorefield) != "")
 +      s.SendFlags |= (2 ** (scorefield.m_id % 16));
        if(!warmup_stage)
                PlayerStats_GameReport_Event_Player(s.owner, strcat(PLAYERSTATS_TOTAL, scores_label(scorefield)), score);
        s.(scores(scorefield)) += score;
@@@ -412,24 -421,6 +421,24 @@@ float PlayerScore_Compare(entity t1, en
        return result.x;
  }
  
 +bool Score_NewLeader()
 +{
 +      if(teamplay) {
 +              if (WinningConditionHelper_winnerteam != WinningConditionHelper_winnerteam_last && (WinningConditionHelper_secondteam || WinningConditionHelper_equality))
 +              {
 +                      WinningConditionHelper_winnerteam_last = WinningConditionHelper_winnerteam;
 +                      return true;
 +              }
 +      } else {
 +              if (WinningConditionHelper_winner != WinningConditionHelper_winner_last && (WinningConditionHelper_second || WinningConditionHelper_equality))
 +              {
 +                      WinningConditionHelper_winner_last = WinningConditionHelper_winner;
 +                      return true;
 +              }
 +      }
 +      return false;
 +}
 +
  void WinningConditionHelper(entity this)
  {
        float c;
  
                WinningConditionHelper_equality = (PlayerScore_Compare(winnerscorekeeper, secondscorekeeper, false) == 0);
                if(WinningConditionHelper_equality)
 +              {
 +                      WinningConditionHelper_equality_one = WinningConditionHelper_winner;
 +                      WinningConditionHelper_equality_two = WinningConditionHelper_second;
                        WinningConditionHelper_winner = WinningConditionHelper_second = NULL;
 +              }
 +              else
 +              {
 +                      WinningConditionHelper_equality_one = WinningConditionHelper_equality_two = NULL;
 +              }
  
                WinningConditionHelper_topscore = winnerscorekeeper.scores_primary;
                WinningConditionHelper_secondscore = secondscorekeeper.scores_primary;
diff --combined qcsrc/server/world.qc
index 446b198d951ec426de7afb86d73559cd24c3f0e0,5801fae88a3eb2fbd06d091e81bed09b779c38d4..98df0c1a568dca3ffaa4662d8a33fc97a8262d6d
@@@ -101,27 -101,6 +101,27 @@@ void PingPLReport_Spawn(
  
  const float SPAWNFLAG_NO_WAYPOINTS_FOR_ITEMS = 1;
  
 +void send_TotalShards(entity to) {
 +      // for RJZ
 +      // Send total number of picked up shards
 +      if(!autocvar_rjz_count_shards) return;
 +      if(!IS_REAL_CLIENT(to)) return;
 +      
 +      msg_entity = to;
 +      WriteHeader(MSG_ONE, TE_CSQC_TOTALSHARDS);
 +      WriteInt24_t(MSG_ONE, total_shards);
 +}
 +
 +void send_TotalShardsAll() {
 +      // for RJZ
 +      // Send total number of picked up shards
 +      if(!autocvar_rjz_count_shards) return;
 +      
 +      FOREACH_CLIENT(IS_REAL_CLIENT(it) && (IS_SPEC(it) || IS_OBSERVER(it)), {
 +              send_TotalShards(it);
 +      });
 +}
 +
  void SetDefaultAlpha()
  {
        if (!MUTATOR_CALLHOOK(SetDefaultAlpha))
@@@ -308,7 -287,6 +308,7 @@@ void cvar_changes_init(
                BADCVAR("g_keyhunt");
                BADCVAR("g_keyhunt_teams");
                BADCVAR("g_lms");
 +              BADCVAR("g_mayhem");
                BADCVAR("g_nexball");
                BADCVAR("g_onslaught");
                BADCVAR("g_race");
                BADCVAR("g_tdm");
                BADCVAR("g_tdm_on_dm_maps");
                BADCVAR("g_tdm_teams");
 +              BADCVAR("g_mmm");
 +              BADCVAR("g_mmm_not_dm_maps");
 +              BADCVAR("g_tmayhem");
 +              BADCVAR("g_tmayhem_teams");
                BADCVAR("g_vip");
                BADCVAR("leadlimit");
                BADCVAR("nextmap");
                BADCVAR("g_mapinfo_ignore_warnings");
                BADCVAR("g_maplist_ignore_sizes");
                BADCVAR("g_maplist_sizes_count_bots");
 +              //LegendGuard adds BADCVAR(g_*) for MMM 20-02-2021
  
                // long
                BADCVAR("hostname");
 +              BADCVAR("hostname_full");
                BADCVAR("g_maplist");
                BADCVAR("g_maplist_mostrecent");
                BADCVAR("sv_motd");
 +              BADCVAR("sv_motd_permanent");
 +              
 +              BADPREFIX("g_teamnames_");
  
                v = cvar_string(k);
                d = cvar_defstring(k);
                BADCVAR("g_forced_respawn");
                BADCVAR("g_freezetag_point_leadlimit");
                BADCVAR("g_freezetag_point_limit");
 +              BADCVAR("g_freezetag_revive_respawn");
 +              BADCVAR("g_freezetag_round_stop");
 +              BADCVAR("g_freezetag_round_respawn");
                BADCVAR("g_glowtrails");
                BADCVAR("g_hats");
                BADCVAR("g_casings");
                BADCVAR("g_spawn_alloweffects");
                BADCVAR("g_tdm_point_leadlimit");
                BADCVAR("g_tdm_point_limit");
 +              BADCVAR("g_mayhem_fraglimit");
 +              BADCVAR("g_tmayhem_fraglimit");
 +              BADCVAR("g_mayhem_visual_score_limit");
 +              BADCVAR("g_tmayhem_visual_score_limit");
 +              BADCVAR("g_tmayhem_score_leadlimit");
                BADCVAR("leadlimit_and_fraglimit");
                BADCVAR("leadlimit_override");
 +              BADCVAR("g_mayhem_scoringmethod");
 +              BADCVAR("g_mayhem_scoringmethod_damage_weight");
 +              BADCVAR("g_mayhem_scoringmethod_frag_weight");
 +              BADCVAR("g_tmayhem_scoringmethod");
 +              BADCVAR("g_tmayhem_scoringmethod_damage_weight");
 +              BADCVAR("g_tmayhem_scoringmethod_frag_weight");
                BADCVAR("pausable");
                BADCVAR("sv_announcer");
                BADCVAR("sv_checkforpacketsduringsleep");
                BADPREFIX("sv_timeout_");
                BADPREFIX("sv_vote_");
                BADPREFIX("timelimit_");
 +              BADPREFIX("sv_chat_");
 +              BADPREFIX("sv_jingle_");
  
                // allowed changes to server admins (please sync this to server.cfg)
                // vi commands:
                BADCVAR("g_keyhunt_point_limit");
                BADCVAR("g_keyhunt_teams_override");
                BADCVAR("g_lms_lives_override");
 +              BADCVAR("g_mayhem_powerups");
                BADCVAR("g_maplist");
                BADCVAR("g_maxplayers");
                BADCVAR("g_mirrordamage");
                BADCVAR("g_start_delay");
                BADCVAR("g_superspectate");
                BADCVAR("g_tdm_teams_override");
 +              BADCVAR("g_tmayhem_teams_override");
 +              BADCVAR("g_tmayhem_powerups");
                BADCVAR("g_warmup");
                BADCVAR("g_weapon_stay"); BADPRESUFFIX("g_", "_weapon_stay");
                BADCVAR("hostname");
                BADCVAR("g_ca_weaponarena");
                BADCVAR("g_freezetag_weaponarena");
                BADCVAR("g_lms_weaponarena");
 +              BADCVAR("g_mayhem_weaponarena");
 +              BADCVAR("g_tmayhem_weaponarena");
                BADCVAR("g_ctf_stalemate_time");
  
  #undef BADPRESUFFIX
@@@ -1074,15 -1022,6 +1074,15 @@@ spawnfunc(worldspawn
  
        WinningConditionHelper(this); // set worldstatus
  
 +      if(!warmup_stage)
 +              round_handler_Activate(true);
 +      
 +      // for RJZ
 +      if (autocvar_rjz_count_shards && warmup_stage) {
 +              total_shards = -2;
 +              send_TotalShardsAll();
 +      }
 +      
        world_initialized = 1;
        __spawnfunc_spawn_all();
  }
@@@ -1336,6 -1275,7 +1336,7 @@@ only called if a time or frag limit ha
  */
  void NextLevel()
  {
+       cvar_set("_endmatch", "0");
        game_stopped = true;
        intermission_running = true; // game over
  
        */
  
        //pos = FindIntermission ();
 +      
 +      sound(NULL, CH_INFO, SND_ENDMATCH, VOL_BASE, ATTN_NONE);
  
        VoteReset();
  
        WeaponStats_Shutdown();
  
        Kill_Notification(NOTIF_ALL, NULL, MSG_CENTER, CPID_Null); // kill all centerprints now
 +      
 +      // send winner notification
 +      if(teamplay) {
 +              Send_Notification(NOTIF_ALL, NULL, MSG_ANNCE, APP_TEAM_NUM(WinningConditionHelper_winnerteam, ANNCE_TEAM_WINS));
 +      }
  
        if(autocvar_sv_eventlog)
                GameLogEcho(":gameover");
        });
  
        target_music_kill();
 +      
 +      // z411
 +      if(autocvar_sv_jingle_end) {
 +              int jingles_len = 0;
 +              string jingles[32];
 +              jingles[0] = "";
 +              
 +              FOREACH_WORD(autocvar_sv_jingle_end_list, it,
 +              {
 +                      jingles[jingles_len] = it;
 +                      jingles_len++;
 +              });
 +              
 +              if(jingles_len) {
 +                      int song_to_play = rint(random() * (jingles_len - 1));
 +              
 +                      FOREACH_CLIENT(IS_REAL_CLIENT(it),
 +                      {
 +                              stuffcmd(it, "cd stop\n");
 +                              _sound(it, CH_INFO, strcat("jingle/", jingles[song_to_play], ".ogg"), VOL_BASE * autocvar_sv_jingle_end_volume, ATTEN_NORM);
 +                      });
 +              }
 +      }
  
        if(autocvar_g_campaign)
                CampaignPreIntermission();
  }
  
  
float InitiateSuddenDeath()
int InitiateSuddenDeath()
  {
        // Check first whether normal overtimes could be added before initiating suddendeath mode
        // - for this timelimit_overtime needs to be >0 of course
                if(!checkrules_suddendeathend)
                {
                        if(autocvar_g_campaign)
+                       {
                                checkrules_suddendeathend = time; // no suddendeath in campaign
+                       }
                        else
+                       {
                                checkrules_suddendeathend = time + 60 * autocvar_timelimit_suddendeath;
+                               overtimes = -1;
+                       }
                        if(g_race && !g_race_qualifying)
                                race_StartCompleting();
                }
  void InitiateOvertime() // ONLY call this if InitiateSuddenDeath returned true
  {
        ++checkrules_overtimesadded;
+       overtimes = checkrules_overtimesadded;
        //add one more overtime by simply extending the timelimit
        cvar_set("timelimit", ftos(autocvar_timelimit + autocvar_timelimit_overtime));
        Send_Notification(NOTIF_ALL, NULL, MSG_CENTER, CENTER_OVERTIME_TIME, autocvar_timelimit_overtime * 60);
 +      
 +      sound(NULL, CH_INFO, SND_OVERTIME, VOL_BASE, ATTN_NONE);
 +      if(checkrules_overtimesadded == 1) {
 +              Send_Notification(NOTIF_ALL, NULL, MSG_ANNCE, ANNCE_OVERTIME);
 +              overtime_starttime = time;
 +      }
  }
  
  float GetWinningCode(float fraglimitreached, float equality)
@@@ -1514,49 -1424,7 +1521,49 @@@ void ClearWinners(
        FOREACH_CLIENT(IS_PLAYER(it) || INGAME(it), { it.winning = 0; });
  }
  
 -int fragsleft_last;
 +void AnnounceNewLeader()
 +{
 +      if(teamplay) {
 +              if (WinningConditionHelper_equality)
 +                      Send_Notification(NOTIF_ALL, NULL, MSG_ANNCE, ANNCE_TEAM_LEADS_TIED);
 +              else
 +                      FOREACH_CLIENT(IS_PLAYER(it), {
 +                              if(it.team == WinningConditionHelper_winnerteam)
 +                                      Send_Notification(NOTIF_ONE_ONLY, it, MSG_ANNCE, ANNCE_TEAM_LEADS_TEAM);
 +                              else
 +                                      Send_Notification(NOTIF_ONE_ONLY, it, MSG_ANNCE, ANNCE_TEAM_LEADS_ENEMY);
 +                      });
 +                      Send_Notification(NOTIF_ALL_SPEC, NULL, MSG_ANNCE, APP_TEAM_NUM(WinningConditionHelper_winnerteam, ANNCE_TEAM_LEADS));
 +      } else {
 +              if (WinningConditionHelper_equality)
 +              {
 +                      Send_Notification(NOTIF_ONE, WinningConditionHelper_equality_one, MSG_ANNCE, ANNCE_LEAD_TIED);
 +                      Send_Notification(NOTIF_ONE, WinningConditionHelper_equality_two, MSG_ANNCE, ANNCE_LEAD_TIED);
 +              }
 +              else
 +              {
 +                      Send_Notification(NOTIF_ONE, WinningConditionHelper_winner, MSG_ANNCE, ANNCE_LEAD_GAINED);
 +                      Send_Notification(NOTIF_ONE, WinningConditionHelper_second, MSG_ANNCE, ANNCE_LEAD_LOST);
 +              }
 +      }
 +}
 +
 +void AnnounceScores(float tm)
 +{
 +      WinningConditionHelper(NULL);
 +      if (Score_NewLeader()) {
 +              AnnounceNewLeader();
 +      } else {
 +              FOREACH_CLIENT(IS_PLAYER(it), {
 +                      if(it.team == tm)
 +                              Send_Notification(NOTIF_ONE_ONLY, it, MSG_ANNCE, ANNCE_TEAM_SCORES_TEAM);
 +                      else
 +                              Send_Notification(NOTIF_ONE_ONLY, it, MSG_ANNCE, ANNCE_TEAM_SCORES_ENEMY);
 +              });
 +              Send_Notification(NOTIF_ALL_SPEC, NULL, MSG_ANNCE, APP_TEAM_NUM(tm, ANNCE_TEAM_SCORES));
 +      }
 +}
 +
  float WinningCondition_Scores(float limit, float leadlimit)
  {
        // TODO make everything use THIS winning condition (except LMS)
  
        if(MUTATOR_CALLHOOK(Scores_CountFragsRemaining))
        {
 -              float fragsleft;
                if (checkrules_suddendeathend && time >= checkrules_suddendeathend)
                {
                        fragsleft = 1;
                        fragsleft_last = fragsleft;
                }
        }
 -
 +      
 +      // z411 - lead announcer
 +      if(MUTATOR_CALLHOOK(Scores_AnnounceLeads)) {
 +              if (Score_NewLeader())
 +                      AnnounceNewLeader();
 +      }
 +      
        bool fraglimit_reached = (limit && WinningConditionHelper_topscore >= limit);
        bool leadlimit_reached = (leadlimit && WinningConditionHelper_topscore - WinningConditionHelper_secondscore >= leadlimit);
  
@@@ -1742,9 -1605,6 +1749,9 @@@ void CheckRules_World(
                        // again, but this shouldn't hurt
                return;
        }
 +      
 +      // z411 don't check rules if we're in a timeout
 +      if (game_timeout) return;
  
        float timelimit = autocvar_timelimit * 60;
        float fraglimit = autocvar_fraglimit;
                leadlimit = 0; // no leadlimit for now
        }
  
-       if(timelimit > 0)
-       {
-               timelimit += game_starttime;
-       }
-       else if (timelimit < 0)
+       if (autocvar__endmatch || timelimit < 0)
        {
                // endmatch
                NextLevel();
                return;
        }
  
-       float wantovertime;
-       wantovertime = 0;
+       if(timelimit > 0)
+               timelimit += game_starttime;
+       int overtimes_prev = overtimes;
+       int wantovertime = 0;
  
        if(checkrules_suddendeathend)
        {
                                Send_Notification(NOTIF_ALL, NULL, MSG_CENTER, CENTER_RACE_FINISHLAP);
                        else
                                Send_Notification(NOTIF_ALL, NULL, MSG_CENTER, CENTER_OVERTIME_FRAG);
 +                      checkrules_overtimesadded = 1;
 +                      sound(NULL, CH_INFO, SND_OVERTIME, VOL_BASE, ATTN_NONE);
 +                      Send_Notification(NOTIF_ALL, NULL, MSG_ANNCE, ANNCE_OVERTIME);
                }
        }
        else
  
        if(checkrules_status == WINNING_YES)
        {
+               if (overtimes == -1 && overtimes != overtimes_prev)
+               {
+                       // if suddendeathend overtime has just begun, revert it
+                       checkrules_suddendeathend = 0;
+                       overtimes = overtimes_prev;
+               }
                //print("WINNING\n");
                NextLevel();
        }
@@@ -2208,8 -2070,6 +2220,8 @@@ void readlevelcvars(
  {
        if(cvar("sv_allow_fullbright"))
                serverflags |= SERVERFLAG_ALLOW_FULLBRIGHT;
 +      if(cvar("sv_forbid_pickuptimer"))
 +              serverflags |= SERVERFLAG_FORBID_PICKUPTIMER;
  
        sv_ready_restart_after_countdown = cvar("sv_ready_restart_after_countdown");
  
@@@ -2403,7 -2263,7 +2415,7 @@@ void RunThink(entity this, float dt
  bool autocvar_sv_freezenonclients;
  void Physics_Frame()
  {
 -      if(autocvar_sv_freezenonclients)
 +      if(autocvar_sv_freezenonclients || game_timeout)
                return;
  
        IL_EACH(g_moveables, true,
diff --combined qcsrc/server/world.qh
index a4eaa76f64afc6ae00b9d8a480860509ce789c6e,242b3369470546ff8c0a1c4b0169e14605237044..4400d486e972bb81a11caecff5658eae16026524
@@@ -3,6 -3,7 +3,7 @@@
  #include <common/weapons/_all.qh>
  
  bool autocvar__sv_init;
+ bool autocvar__endmatch;
  bool autocvar_g_use_ammunition;
  bool autocvar_g_jetpack;
  bool autocvar_g_warmup_allguns;
@@@ -27,18 -28,10 +28,18 @@@ float autocvar_timelimit_overtime
  int autocvar_timelimit_overtimes;
  float autocvar_timelimit_suddendeath;
  
 +// z411
 +bool autocvar_sv_jingle_end;
 +string autocvar_sv_jingle_end_list;
 +float autocvar_sv_jingle_end_volume;
 +
 +float fragsleft;
 +int fragsleft_last;
 +
  float checkrules_equality;
  float checkrules_suddendeathwarning;
  float checkrules_suddendeathend;
//float checkrules_overtimesadded; //how many overtimes have been already added
int checkrules_overtimesadded; //how many overtimes have been already added
  
  // flag set on worldspawn so that the code knows if it is dedicated or not
  bool server_is_dedicated;
@@@ -164,12 -157,5 +165,12 @@@ void readlevelcvars()
  .vector dropped_origin;
  void droptofloor(entity this);
  
 +/* z411 for RJZ */
 +bool autocvar_rjz_count_shards = false;
 +bool autocvar_rjz_ranks = false;
 +int  total_shards = 0;
 +void send_TotalShards(entity to);
 +void send_TotalShardsAll();
 +
  IntrusiveList g_moveables;
  STATIC_INIT(g_moveables) { g_moveables = IL_NEW(); }