]> git.xonotic.org Git - xonotic/xonotic-data.pk3dir.git/commitdiff
Merge branch 'master' into Mario/survival
authorMario <mario.mario@y7mail.com>
Fri, 1 Jul 2022 05:46:31 +0000 (15:46 +1000)
committerMario <mario.mario@y7mail.com>
Fri, 1 Jul 2022 05:46:31 +0000 (15:46 +1000)
1  2 
gamemodes-client.cfg
gamemodes-server.cfg
notifications.cfg
qcsrc/common/ent_cs.qc
qcsrc/common/gamemodes/gamemode/survival/sv_survival.qc
qcsrc/common/notifications/all.inc
qcsrc/common/notifications/all.qh
qcsrc/common/scores.qh
qcsrc/common/stats.qh
qcsrc/menu/xonotic/util.qc
qcsrc/server/mutators/events.qh

diff --combined gamemodes-client.cfg
index 45d0b7a3f3e0107b51bd41bb6fa223af772ed2d9,71d272a174e7c50dcb868fa34e472d043668a250..1bc0853c690e18ddd0b5ced19f54977eb2a2531e
@@@ -32,7 -32,6 +32,7 @@@ alias cl_hook_gamestart_k
  alias cl_hook_gamestart_ft
  alias cl_hook_gamestart_inv
  alias cl_hook_gamestart_duel
- alias cl_hook_gameend "rpn /cl_matchcount dup load 1 + =" // increase match count every time a game ends
 +alias cl_hook_gamestart_sv
+ alias cl_hook_gameend
  alias cl_hook_shutdown
  alias cl_hook_activeweapon
diff --combined gamemodes-server.cfg
index da7108e80d23367e232005d57541272d430dfc35,318749021fe8d329ae55e6cbb58de4eafb3486ef..2976590906a1a9372a6d6f4e7a3956895b5e9633
@@@ -29,7 -29,6 +29,7 @@@ alias sv_hook_gamestart_k
  alias sv_hook_gamestart_ft
  alias sv_hook_gamestart_inv
  alias sv_hook_gamestart_duel
 +alias sv_hook_gamestart_sv
  // 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
@@@ -59,7 -58,6 +59,7 @@@ 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_sv
  
  // 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.
@@@ -210,13 -208,6 +210,13 @@@ 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_sv_respawn_delay_small 0
 +set g_sv_respawn_delay_small_count 0
 +set g_sv_respawn_delay_large 0
 +set g_sv_respawn_delay_large_count 0
 +set g_sv_respawn_delay_max 0
 +set g_sv_respawn_waves 0
 +set g_sv_weapon_stay 0
  
  
  // =========
@@@ -233,7 -224,7 +233,7 @@@ set g_ca_point_limit -1 "Clan Arena poi
  set g_ca_point_leadlimit -1 "Clan Arena point lead limit overriding the mapinfo specified one (use 0 to play without limit, and -1 to use the mapinfo's limit)"
  set g_ca_spectate_enemies 0 "allow eliminated players to spectate enemy players during Clan Arena games"
  set g_ca_warmup 10 "time players get to run around before the round starts"
- set g_ca_damage2score_multiplier 0.01
+ set g_ca_damage2score 100  "every this amount of damage done give players 1 point"
  set g_ca_round_timelimit 180 "round time limit in seconds"
  set g_ca_teams_override 0
  set g_ca_team_spawns 0 "when 1, players spawn from the team spawnpoints of the map, if any"
@@@ -331,7 -322,7 +331,7 @@@ exec ctfscoring-samual.cf
  // ====================
  set g_cts 0 "CTS: complete the stage"
  set g_cts_selfdamage 1 "0 = disable all selfdamage and falldamage in cts"
- set g_cts_finish_kill_delay 10 "prevent cheating by running back to the start line, and starting out with more speed than otherwise possible"
+ set g_cts_finish_kill_delay 2 "kill player this many seconds after stage completion to prevent cheating by starting out with more speed than otherwise possible; set it to 0 to not kill or to -1 to kill instantly"
  set g_cts_send_rankings_cnt 15 "send this number of map records to clients"
  set g_cts_removeprojectiles 0 "remove projectiles when the player dies, to prevent using weapons earlier in the stage than intended"
  
@@@ -454,10 -445,13 +454,13 @@@ set g_keyhunt_team_spawns 0 "when 1, pl
  set g_lms 0 "Last Man Standing: everyone starts with a certain amount of lives, and the survivor wins"
  set g_lms_lives_override -1
  set g_lms_extra_lives 0
- set g_lms_regenerate 0
- set g_lms_last_join 3 "if g_lms_join_anytime is false, new players can only join if the worst active player has more than (fraglimit - g_lms_last_join) lives"
- set g_lms_join_anytime 1      "if true, new players can join, but get same amount of lives as the worst player"
+ set g_lms_regenerate 0 "health and/or armor regeneration, according to g_balance_health_regen and g_balance_armor_regen"
+ set g_lms_rot 0 "health and/or armor rotting, according to g_balance_health_rot and g_balance_armor_rot"
+ set g_lms_last_join 3 "if g_lms_join_anytime is 0, new players can only join if the worst active player has (fraglimit - g_lms_last_join) or more lives; in other words, new players can no longer join once the worst player loses more than g_lms_last_join lives"
+ 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"
  
  
  // =========
@@@ -557,8 -551,6 +560,6 @@@ set g_invasion_monster_count 10 "numbe
  set g_invasion_zombies_only 0 "only spawn zombies"
  set g_invasion_spawn_delay 0.25
  set g_invasion_spawnpoint_spawn_delay 0.5
- set g_invasion_teams 0 "number of teams in invasion (note: use mapinfo to set this)"
- set g_invasion_team_spawns 1 "use team spawns in teamplay invasion mode"
  set g_invasion_type 0 "type of invasion mode - 0: round-based, 1: hunting, 2: complete the stage (note: use mapinfo to set this)"
  
  // ======
@@@ -568,14 -560,3 +569,14 @@@ 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"
 +
 +// ==========
 +//  survival
 +// ==========
 +set g_survival 0 "Survival: identify and eliminate all the hunters before all your allies are gone"
 +set g_survival_not_lms_maps 0 "when this is set, LMS maps will NOT be listed in survival"
 +set g_survival_hunter_count 0.25 "number of players who will become hunters, 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_survival_punish_teamkill 1 "kill the player when they kill an ally"
 +set g_survival_reward_survival 1 "give a point to all surviving players if the round timelimit is reached, in addition to the points given for kills"
 +set g_survival_warmup 10 "how long the players will have time to run around the map before the round starts"
 +set g_survival_round_timelimit 180 "round time limit in seconds"
diff --combined notifications.cfg
index 88a6828d3d54f4205f694af0f5c072ed949bbedc,afae1593f61610c61d4e0e4d3a6eb04249854cb1..46d976e991a83d9abb6b7a26fce58d64daa59bd7
@@@ -162,10 -162,10 +162,10 @@@ seta notification_INFO_DEATH_SELF_FALL 
  seta notification_INFO_DEATH_SELF_FIRE "1" "0 = off, 1 = print to console, 2 = print to console and chatbox (if notification_allow_chatboxprint is enabled)"
  seta notification_INFO_DEATH_SELF_GENERIC "1" "0 = off, 1 = print to console, 2 = print to console and chatbox (if notification_allow_chatboxprint is enabled)"
  seta notification_INFO_DEATH_SELF_LAVA "1" "0 = off, 1 = print to console, 2 = print to console and chatbox (if notification_allow_chatboxprint is enabled)"
+ seta notification_INFO_DEATH_SELF_MON_GOLEM_CLAW "1" "0 = off, 1 = print to console, 2 = print to console and chatbox (if notification_allow_chatboxprint is enabled)"
+ seta notification_INFO_DEATH_SELF_MON_GOLEM_SMASH "1" "0 = off, 1 = print to console, 2 = print to console and chatbox (if notification_allow_chatboxprint is enabled)"
+ seta notification_INFO_DEATH_SELF_MON_GOLEM_ZAP "1" "0 = off, 1 = print to console, 2 = print to console and chatbox (if notification_allow_chatboxprint is enabled)"
  seta notification_INFO_DEATH_SELF_MON_MAGE "1" "0 = off, 1 = print to console, 2 = print to console and chatbox (if notification_allow_chatboxprint is enabled)"
- seta notification_INFO_DEATH_SELF_MON_SHAMBLER_CLAW "1" "0 = off, 1 = print to console, 2 = print to console and chatbox (if notification_allow_chatboxprint is enabled)"
- seta notification_INFO_DEATH_SELF_MON_SHAMBLER_SMASH "1" "0 = off, 1 = print to console, 2 = print to console and chatbox (if notification_allow_chatboxprint is enabled)"
- seta notification_INFO_DEATH_SELF_MON_SHAMBLER_ZAP "1" "0 = off, 1 = print to console, 2 = print to console and chatbox (if notification_allow_chatboxprint is enabled)"
  seta notification_INFO_DEATH_SELF_MON_SPIDER "1" "0 = off, 1 = print to console, 2 = print to console and chatbox (if notification_allow_chatboxprint is enabled)"
  seta notification_INFO_DEATH_SELF_MON_WYVERN "1" "0 = off, 1 = print to console, 2 = print to console and chatbox (if notification_allow_chatboxprint is enabled)"
  seta notification_INFO_DEATH_SELF_MON_ZOMBIE_JUMP "1" "0 = off, 1 = print to console, 2 = print to console and chatbox (if notification_allow_chatboxprint is enabled)"
@@@ -274,8 -274,6 +274,8 @@@ seta notification_INFO_SCORES "1" "0 = 
  seta notification_INFO_SPECTATE_WARNING "1" "0 = off, 1 = print to console, 2 = print to console and chatbox (if notification_allow_chatboxprint is enabled)"
  seta notification_INFO_SUPERSPEC_MISSING_UID "2" "0 = off, 1 = print to console, 2 = print to console and chatbox (if notification_allow_chatboxprint is enabled)"
  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_SURVIVAL_HUNTER_WIN "1" "0 = off, 1 = print to console, 2 = print to console and chatbox (if notification_allow_chatboxprint is enabled)"
 +seta notification_INFO_SURVIVAL_SUVIVOR_WIN "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)"
  seta notification_INFO_VERSION_BETA "1" "0 = off, 1 = print to console, 2 = print to console and chatbox (if notification_allow_chatboxprint is enabled)"
@@@ -349,7 -347,6 +349,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"
@@@ -482,6 -479,7 +481,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"
@@@ -526,10 -524,6 +526,10 @@@ seta notification_CENTER_SEQUENCE_COUNT
  seta notification_CENTER_SUPERWEAPON_BROKEN "1" "0 = off, 1 = centerprint"
  seta notification_CENTER_SUPERWEAPON_LOST "1" "0 = off, 1 = centerprint"
  seta notification_CENTER_SUPERWEAPON_PICKUP "1" "0 = off, 1 = centerprint"
 +seta notification_CENTER_SURVIVAL_HUNTER "1" "0 = off, 1 = centerprint"
 +seta notification_CENTER_SURVIVAL_HUNTER_WIN "1" "0 = off, 1 = centerprint"
 +seta notification_CENTER_SURVIVAL_SURVIVOR "1" "0 = off, 1 = centerprint"
 +seta notification_CENTER_SURVIVAL_SURVIVOR_WIN "1" "0 = off, 1 = centerprint"
  seta notification_CENTER_TEAMCHANGE_AUTO "1" "0 = off, 1 = centerprint"
  seta notification_CENTER_TEAMCHANGE "1" "0 = off, 1 = centerprint"
  seta notification_CENTER_TEAMCHANGE_SPECTATE "1" "0 = off, 1 = centerprint"
@@@ -584,10 -578,10 +584,10 @@@ seta notification_DEATH_SELF_FALL "1" "
  seta notification_DEATH_SELF_FIRE "1" "Enable this multiple notification"
  seta notification_DEATH_SELF_GENERIC "1" "Enable this multiple notification"
  seta notification_DEATH_SELF_LAVA "1" "Enable this multiple notification"
+ seta notification_DEATH_SELF_MON_GOLEM_CLAW "1" "Enable this multiple notification"
+ seta notification_DEATH_SELF_MON_GOLEM_SMASH "1" "Enable this multiple notification"
+ seta notification_DEATH_SELF_MON_GOLEM_ZAP "1" "Enable this multiple notification"
  seta notification_DEATH_SELF_MON_MAGE "1" "Enable this multiple notification"
- seta notification_DEATH_SELF_MON_SHAMBLER_CLAW "1" "Enable this multiple notification"
- seta notification_DEATH_SELF_MON_SHAMBLER_SMASH "1" "Enable this multiple notification"
- seta notification_DEATH_SELF_MON_SHAMBLER_ZAP "1" "Enable this multiple notification"
  seta notification_DEATH_SELF_MON_SPIDER "1" "Enable this multiple notification"
  seta notification_DEATH_SELF_MON_WYVERN "1" "Enable this multiple notification"
  seta notification_DEATH_SELF_MON_ZOMBIE_JUMP "1" "Enable this multiple notification"
diff --combined qcsrc/common/ent_cs.qc
index 7bc6aa52587370c606101d80ea3ae9ef0e4636ef,5366371458fe52fadb86e3c2d7fc3dd8b2ca44db..9ec68acb8155053aba99d89db8fe155ff71737de
@@@ -2,12 -2,12 +2,12 @@@
  
  #if defined(CSQC)
        #include <common/gamemodes/_mod.qh>
-       #include <common/resources.qh>
+       #include <common/resources/resources.qh>
  #elif defined(MENUQC)
  #elif defined(SVQC)
        #include <common/gamemodes/_mod.qh>
-       #include <common/resources.qh>
-       #include <server/resources.qh>
+       #include <common/resources/resources.qh>
+       #include <common/resources/sv_resources.qh>
  #endif
  
  REGISTRY(EntCSProps, BITS(16) - 1)
@@@ -157,11 -157,6 +157,11 @@@ ENTCS_PROP(SOLID, true, sv_solid, solid
        { WriteByte(chan, ent.sv_solid); },
        { ent.sv_solid = ReadByte(); })
  
 +// gamemode specific player survival status (independent of score and frags)
 +ENTCS_PROP(SURVIVAL_STATUS, true, survival_status, survival_status, ENTCS_SET_NORMAL,
 +      { WriteShort(chan, ent.survival_status); },
 +      { ent.survival_status = ReadShort(); })
 +
  #ifdef SVQC
  
        int ENTCS_PUBLICMASK = 0, ENTCS_PRIVATEMASK = 0;
                        {
                                if (radar_showenemies) break;
                                if (SAME_TEAM(to, player)) break;
-                               if (!(IS_PLAYER(to) || to.caplayer)) break;
+                               if (!(IS_PLAYER(to) || INGAME(to))) break;
                        }
                        sf &= ENTCS_PUBLICMASK; // no private updates
                } while (0);
index ec7c40f8de7e6b33cb53987158d022cde889c3ff,0000000000000000000000000000000000000000..aa3631af9ed992ad8b9a5861be86ff31b668e6f8
mode 100644,000000..100644
--- /dev/null
@@@ -1,459 -1,0 +1,453 @@@
-       if(e.caplayer == 1 && (IS_DEAD(e) || e.frags == FRAGS_PLAYER_OUT_OF_GAME))
 +#include "sv_survival.qh"
 +
 +float autocvar_g_survival_hunter_count = 0.25;
 +float autocvar_g_survival_round_timelimit = 180;
 +float autocvar_g_survival_warmup = 10;
 +bool autocvar_g_survival_punish_teamkill = true;
 +bool autocvar_g_survival_reward_survival = true;
 +
 +void surv_FakeTimeLimit(entity e, float t)
 +{
 +      if(!IS_REAL_CLIENT(e))
 +              return;
 +#if 0
 +      msg_entity = e;
 +      WriteByte(MSG_ONE, 3); // svc_updatestat
 +      WriteByte(MSG_ONE, 236); // STAT_TIMELIMIT
 +      if(t < 0)
 +              WriteCoord(MSG_ONE, autocvar_timelimit);
 +      else
 +              WriteCoord(MSG_ONE, (t + 1) / 60);
 +#else
 +      STAT(SURVIVAL_ROUNDTIMER, e) = t;
 +#endif
 +}
 +
 +void nades_Clear(entity player);
 +
 +void Surv_UpdateScores(bool timed_out)
 +{
 +      // give players their hard-earned kills now that the round is over
 +      FOREACH_CLIENT(true,
 +      {
 +              it.totalfrags += it.survival_validkills;
 +              if(it.survival_validkills)
 +                      GameRules_scoring_add(it, SCORE, it.survival_validkills);
 +              it.survival_validkills = 0;
 +              // player survived the round
 +              if(IS_PLAYER(it) && !IS_DEAD(it))
 +              {
 +                      if(autocvar_g_survival_reward_survival && timed_out && it.survival_status == SV_STATUS_PREY)
 +                              GameRules_scoring_add(it, SCORE, 1); // reward survivors who make it to the end of the round time limit
 +                      if(it.survival_status == SV_STATUS_PREY)
 +                              GameRules_scoring_add(it, SV_SURVIVALS, 1);
 +                      else if(it.survival_status == SV_STATUS_HUNTER)
 +                              GameRules_scoring_add(it, SV_HUNTS, 1);
 +              }
 +      });
 +}
 +
 +float Surv_CheckWinner()
 +{
 +      if(round_handler_GetEndTime() > 0 && round_handler_GetEndTime() - time <= 0)
 +      {
 +              // if the match times out, survivors win too!
 +              Send_Notification(NOTIF_ALL, NULL, MSG_CENTER, CENTER_SURVIVAL_SURVIVOR_WIN);
 +              Send_Notification(NOTIF_ALL, NULL, MSG_INFO, INFO_SURVIVAL_SURVIVOR_WIN);
 +              FOREACH_CLIENT(true,
 +              {
 +                      if(IS_PLAYER(it))
 +                              nades_Clear(it);
 +                      surv_FakeTimeLimit(it, -1);
 +              });
 +
 +              Surv_UpdateScores(true);
 +
 +              allowed_to_spawn = false;
 +              game_stopped = true;
 +              round_handler_Init(5, autocvar_g_survival_warmup, autocvar_g_survival_round_timelimit);
 +              return 1;
 +      }
 +
 +      int survivor_count = 0, hunter_count = 0;
 +      FOREACH_CLIENT(IS_PLAYER(it) && !IS_DEAD(it),
 +      {
 +              if(it.survival_status == SV_STATUS_PREY)
 +                      survivor_count++;
 +              else if(it.survival_status == SV_STATUS_HUNTER)
 +                      hunter_count++;
 +      });
 +      if(survivor_count > 0 && hunter_count > 0)
 +      {
 +              return 0;
 +      }
 +
 +      if(hunter_count > 0) // hunters win
 +      {
 +              Send_Notification(NOTIF_ALL, NULL, MSG_CENTER, CENTER_SURVIVAL_HUNTER_WIN);
 +              Send_Notification(NOTIF_ALL, NULL, MSG_INFO, INFO_SURVIVAL_HUNTER_WIN);
 +      }
 +      else if(survivor_count > 0) // survivors win
 +      {
 +              Send_Notification(NOTIF_ALL, NULL, MSG_CENTER, CENTER_SURVIVAL_SURVIVOR_WIN);
 +              Send_Notification(NOTIF_ALL, NULL, MSG_INFO, INFO_SURVIVAL_SURVIVOR_WIN);
 +      }
 +      else
 +      {
 +              Send_Notification(NOTIF_ALL, NULL, MSG_CENTER, CENTER_ROUND_TIED);
 +              Send_Notification(NOTIF_ALL, NULL, MSG_INFO, INFO_ROUND_TIED);
 +      }
 +
 +      Surv_UpdateScores(false);
 +
 +      allowed_to_spawn = false;
 +      game_stopped = true;
 +      round_handler_Init(5, autocvar_g_survival_warmup, autocvar_g_survival_round_timelimit);
 +
 +      FOREACH_CLIENT(true,
 +      {
 +              if(IS_PLAYER(it))
 +                      nades_Clear(it);
 +              surv_FakeTimeLimit(it, -1);
 +      });
 +
 +      return 1;
 +}
 +
 +void Surv_RoundStart()
 +{
 +      allowed_to_spawn = boolean(warmup_stage);
 +      int playercount = 0;
 +      FOREACH_CLIENT(true,
 +      {
 +              if(IS_PLAYER(it) && !IS_DEAD(it))
 +              {
 +                      ++playercount;
 +                      it.survival_status = SV_STATUS_PREY;
 +              }
 +              else
 +                      it.survival_status = 0; // this is mostly a safety check; if a client manages to somehow maintain a survival status, clear it before the round starts!
 +              it.survival_validkills = 0;
 +      });
 +      int hunter_count = bound(1, ((autocvar_g_survival_hunter_count >= 1) ? autocvar_g_survival_hunter_count : floor(playercount * autocvar_g_survival_hunter_count)), playercount - 1); // 20%, but ensure at least 1 and less than total
 +      int total_hunters = 0;
 +      FOREACH_CLIENT_RANDOM(IS_PLAYER(it) && !IS_DEAD(it),
 +      {
 +              if(total_hunters >= hunter_count)
 +                      break;
 +              total_hunters++;
 +              it.survival_status = SV_STATUS_HUNTER;
 +      });
 +
 +      FOREACH_CLIENT(IS_PLAYER(it) && !IS_DEAD(it),
 +      {
 +              if(it.survival_status == SV_STATUS_PREY)
 +                      Send_Notification(NOTIF_ONE_ONLY, it, MSG_CENTER, CENTER_SURVIVAL_SURVIVOR);
 +              else if(it.survival_status == SV_STATUS_HUNTER)
 +                      Send_Notification(NOTIF_ONE_ONLY, it, MSG_CENTER, CENTER_SURVIVAL_HUNTER);
 +
 +              surv_FakeTimeLimit(it, round_handler_GetEndTime());
 +      });
 +}
 +
 +bool Surv_CheckPlayers()
 +{
 +      static int prev_missing_players;
 +      allowed_to_spawn = true;
 +      int playercount = 0;
 +      FOREACH_CLIENT(IS_PLAYER(it) && !IS_DEAD(it),
 +      {
 +              ++playercount;
 +      });
 +      if (playercount >= 2)
 +      {
 +              if(prev_missing_players > 0)
 +                      Kill_Notification(NOTIF_ALL, NULL, MSG_CENTER, CPID_MISSING_PLAYERS);
 +              prev_missing_players = -1;
 +              return true;
 +      }
 +      if(playercount == 0)
 +      {
 +              if(prev_missing_players > 0)
 +                      Kill_Notification(NOTIF_ALL, NULL, MSG_CENTER, CPID_MISSING_PLAYERS);
 +              prev_missing_players = -1;
 +              return false;
 +      }
 +      // if we get here, only 1 player is missing
 +      if(prev_missing_players != 1)
 +      {
 +              Send_Notification(NOTIF_ALL, NULL, MSG_CENTER, CENTER_MISSING_PLAYERS, 1);
 +              prev_missing_players = 1;
 +      }
 +      return false;
 +}
 +
 +bool surv_isEliminated(entity e)
 +{
-       if(e.caplayer == 0.5)
++      if(INGAME_JOINED(e) && (IS_DEAD(e) || e.frags == FRAGS_PLAYER_OUT_OF_GAME))
 +              return true;
-       if(IS_PLAYER(player) || player.caplayer)
++      if(INGAME_JOINING(e))
 +              return true;
 +      return false;
 +}
 +
 +void surv_Initialize() // run at the start of a match, initiates game mode
 +{
 +      GameRules_scoring(0, SFL_SORT_PRIO_PRIMARY, 0, {
 +              field(SP_SV_SURVIVALS, "survivals", 0);
 +              field(SP_SV_HUNTS, "hunts", SFL_SORT_PRIO_SECONDARY);
 +      });
 +
 +      allowed_to_spawn = true;
 +      round_handler_Spawn(Surv_CheckPlayers, Surv_CheckWinner, Surv_RoundStart);
 +      round_handler_Init(5, autocvar_g_survival_warmup, autocvar_g_survival_round_timelimit);
 +      EliminatedPlayers_Init(surv_isEliminated);
 +}
 +
 +
 +// ==============
 +// Hook Functions
 +// ==============
 +
 +MUTATOR_HOOKFUNCTION(sv, ClientObituary)
 +{
 +      // in survival, announcing a frag would tell everyone who the hunter is
 +      entity frag_attacker = M_ARGV(1, entity);
 +      entity frag_target = M_ARGV(2, entity);
 +      if(IS_PLAYER(frag_attacker) && frag_attacker != frag_target)
 +      {
 +              float frag_deathtype = M_ARGV(3, float);
 +              entity wep_ent = M_ARGV(4, entity);
 +              // "team" kill, a point is awarded to the player by default so we must take it away plus an extra one
 +              // unless the player is going to be punished for suicide, in which case just remove one
 +              if(frag_attacker.survival_status == frag_target.survival_status)
 +                      GiveFrags(frag_attacker, frag_target, ((autocvar_g_survival_punish_teamkill) ? -1 : -2), frag_deathtype, wep_ent.weaponentity_fld);
 +      }
 +
 +      M_ARGV(5, bool) = true; // anonymous attacker
 +}
 +
 +MUTATOR_HOOKFUNCTION(sv, PlayerPreThink)
 +{
 +      entity player = M_ARGV(0, entity);
 +
-       player.caplayer = 1;
++      if(IS_PLAYER(player) || INGAME_JOINED(player))
 +      {
 +              // update the scoreboard colour display to out the real killer at the end of the round
 +              // running this every frame to avoid cheats
 +              int plcolor = SV_COLOR_PREY;
 +              if(player.survival_status == SV_STATUS_HUNTER && game_stopped)
 +                      plcolor = SV_COLOR_HUNTER;
 +              setcolor(player, plcolor);
 +      }
 +}
 +
 +MUTATOR_HOOKFUNCTION(sv, PlayerSpawn)
 +{
 +      entity player = M_ARGV(0, entity);
 +
 +      player.survival_status = 0;
 +      player.survival_validkills = 0;
-       if (!allowed_to_spawn && player.caplayer)
++      INGAME_STATUS_SET(player, INGAME_STATUS_JOINED);
 +      if (!warmup_stage)
 +              eliminatedPlayers.SendFlags |= 1;
 +}
 +
 +MUTATOR_HOOKFUNCTION(sv, ForbidSpawn)
 +{
 +      entity player = M_ARGV(0, entity);
 +
 +      // spectators / observers that weren't playing can join; they are
 +      // immediately forced to observe in the PutClientInServer hook
 +      // this way they are put in a team and can play in the next round
-               if (CS(player).jointime != time && !player.caplayer) // not when connecting
++      if (!allowed_to_spawn && INGAME(player))
 +              return true;
 +      return false;
 +}
 +
 +MUTATOR_HOOKFUNCTION(sv, PutClientInServer)
 +{
 +      entity player = M_ARGV(0, entity);
 +
 +      if (!allowed_to_spawn && IS_PLAYER(player)) // this is true even when player is trying to join
 +      {
 +              TRANSMUTE(Observer, player);
-                       player.caplayer = 0.5;
++              if (CS(player).jointime != time && !INGAME(player)) // not when connecting
 +              {
-               if (!it.caplayer && IS_BOT_CLIENT(it))
-                       it.caplayer = 1;
-               if (it.caplayer)
++                      INGAME_STATUS_SET(player, INGAME_STATUS_JOINING);
 +                      Send_Notification(NOTIF_ONE_ONLY, player, MSG_INFO, INFO_CA_JOIN_LATE);
 +              }
 +      }
 +}
 +
 +MUTATOR_HOOKFUNCTION(sv, reset_map_players)
 +{
 +      FOREACH_CLIENT(true, {
 +              CS(it).killcount = 0;
 +              it.survival_status = 0;
 +              surv_FakeTimeLimit(it, -1); // restore original timelimit
-                       it.caplayer = 1;
++              if (INGAME(it) || IS_BOT_CLIENT(it))
 +              {
 +                      TRANSMUTE(Player, it);
-               player.caplayer = 0;
-       if (player.caplayer)
++                      INGAME_STATUS_SET(it, INGAME_STATUS_JOINED);
 +                      PutClientInServer(it);
 +              }
 +      });
 +      bot_relinkplayerlist();
 +      return true;
 +}
 +
 +MUTATOR_HOOKFUNCTION(sv, reset_map_global)
 +{
 +      allowed_to_spawn = true;
 +      return true;
 +}
 +
 +entity surv_LastPlayerForTeam(entity this)
 +{
 +      entity last_pl = NULL;
 +      FOREACH_CLIENT(IS_PLAYER(it) && it != this, {
 +              if (!IS_DEAD(it) && this.survival_status == it.survival_status)
 +              {
 +                      if (!last_pl)
 +                              last_pl = it;
 +                      else
 +                              return NULL;
 +              }
 +      });
 +      return last_pl;
 +}
 +
 +void surv_LastPlayerForTeam_Notify(entity this)
 +{
 +      if (!warmup_stage && round_handler_IsActive() && round_handler_IsRoundStarted())
 +      {
 +              entity pl = surv_LastPlayerForTeam(this);
 +              if (pl)
 +                      Send_Notification(NOTIF_ONE_ONLY, pl, MSG_CENTER, CENTER_ALONE);
 +      }
 +}
 +
 +MUTATOR_HOOKFUNCTION(sv, PlayerDies)
 +{
 +      entity frag_attacker = M_ARGV(1, entity);
 +      entity frag_target = M_ARGV(2, entity);
 +      float frag_deathtype = M_ARGV(3, float);
 +
 +      surv_LastPlayerForTeam_Notify(frag_target);
 +      if (!allowed_to_spawn)
 +      {
 +              frag_target.respawn_flags = RESPAWN_SILENT;
 +              // prevent unwanted sudden rejoin as spectator and movement of spectator camera
 +              frag_target.respawn_time = time + 2;
 +      }
 +      frag_target.respawn_flags |= RESPAWN_FORCE;
 +      if (!warmup_stage)
 +      {
 +              eliminatedPlayers.SendFlags |= 1;
 +              if (IS_BOT_CLIENT(frag_target))
 +                      bot_clearqueue(frag_target);
 +      }
 +
 +      // killed an ally! punishment is death
 +      if(autocvar_g_survival_punish_teamkill && frag_attacker != frag_target && IS_PLAYER(frag_attacker) && IS_PLAYER(frag_target) && frag_attacker.survival_status == frag_target.survival_status && !ITEM_DAMAGE_NEEDKILL(frag_deathtype))
 +      if(!warmup_stage && round_handler_IsActive() && round_handler_IsRoundStarted()) // don't autokill if the round hasn't
 +              Damage(frag_attacker, frag_attacker, frag_attacker, 100000, DEATH_MIRRORDAMAGE.m_id, DMG_NOWEP, frag_attacker.origin, '0 0 0');
 +      return true;
 +}
 +
 +MUTATOR_HOOKFUNCTION(sv, ClientDisconnect)
 +{
 +      entity player = M_ARGV(0, entity);
 +
 +      if (IS_PLAYER(player) && !IS_DEAD(player))
 +              surv_LastPlayerForTeam_Notify(player);
 +      return true;
 +}
 +
 +MUTATOR_HOOKFUNCTION(sv, MakePlayerObserver)
 +{
 +      entity player = M_ARGV(0, entity);
++      bool is_forced = M_ARGV(1, bool);
++      if (is_forced && INGAME(player))
++              INGAME_STATUS_CLEAR(player);
 +
 +      if (IS_PLAYER(player) && !IS_DEAD(player))
 +              surv_LastPlayerForTeam_Notify(player);
 +      if (player.killindicator_teamchange == -2) // player wants to spectate
-       if (!player.caplayer)
++              INGAME_STATUS_CLEAR(player);
++      if (INGAME(player))
 +              player.frags = FRAGS_PLAYER_OUT_OF_GAME;
 +      if (!warmup_stage)
 +              eliminatedPlayers.SendFlags |= 1;
-               if (IS_PLAYER(it) || it.caplayer == 1)
++      if (!INGAME(player))
 +      {
 +              player.survival_validkills = 0;
 +              player.survival_status = 0;
 +              surv_FakeTimeLimit(player, -1); // restore original timelimit
 +              return false;  // allow team reset
 +      }
 +      return true;  // prevent team reset
 +}
 +
 +MUTATOR_HOOKFUNCTION(sv, Scores_CountFragsRemaining)
 +{
 +      // announce remaining frags?
 +      return true;
 +}
 +
 +MUTATOR_HOOKFUNCTION(sv, GiveFragsForKill, CBC_ORDER_FIRST)
 +{
 +      entity frag_attacker = M_ARGV(0, entity);
 +      if(!warmup_stage && round_handler_IsActive() && round_handler_IsRoundStarted())
 +              frag_attacker.survival_validkills += M_ARGV(2, float);
 +      M_ARGV(2, float) = 0; // score will be given to the winner when the round ends
 +      return true;
 +}
 +
 +MUTATOR_HOOKFUNCTION(sv, AddPlayerScore)
 +{
 +      entity scorefield = M_ARGV(0, entity);
 +      if(scorefield == SP_KILLS || scorefield == SP_DEATHS || scorefield == SP_SUICIDES || scorefield == SP_DMG || scorefield == SP_DMGTAKEN)
 +              M_ARGV(1, float) = 0; // don't report that the player has killed or been killed, that would out them as a hunter!
 +}
 +
 +MUTATOR_HOOKFUNCTION(sv, CalculateRespawnTime)
 +{
 +      // no respawn calculations needed, player is forced to spectate anyway
 +      return true;
 +}
 +
 +MUTATOR_HOOKFUNCTION(sv, Bot_FixCount, CBC_ORDER_EXCLUSIVE)
 +{
 +      FOREACH_CLIENT(IS_REAL_CLIENT(it), {
-       if (player.caplayer)
++              if (IS_PLAYER(it) || INGAME_JOINED(it))
 +                      ++M_ARGV(0, int);
 +              ++M_ARGV(1, int);
 +      });
 +      return true;
 +}
 +
 +MUTATOR_HOOKFUNCTION(sv, ClientCommand_Spectate)
 +{
 +      entity player = M_ARGV(0, entity);
 +
- MUTATOR_HOOKFUNCTION(sv, GetPlayerStatus)
- {
-       entity player = M_ARGV(0, entity);
-       return player.caplayer == 1;
- }
++      if (INGAME(player))
 +      {
 +              // they're going to spec, we can do other checks
 +              if (autocvar_sv_spectate && (IS_SPEC(player) || IS_OBSERVER(player)))
 +                      Send_Notification(NOTIF_ONE_ONLY, player, MSG_INFO, INFO_CA_LEAVE);
 +              return MUT_SPECCMD_FORCE;
 +      }
 +
 +      return MUT_SPECCMD_CONTINUE;
 +}
 +
 +MUTATOR_HOOKFUNCTION(sv, BotShouldAttack)
 +{
 +      entity bot = M_ARGV(0, entity);
 +      entity targ = M_ARGV(1, entity);
 +
 +      if(targ.survival_status == bot.survival_status)
 +              return true;
 +}
index 8204e79300a309473cd9ab12ab2ad5136035ab79,87398b11d222367c1aeafa752eecab84b02e3f0d..2f9bc75571e0125bce1e2fdf8d5bc91ba7668f4b
@@@ -300,9 -300,9 +300,9 @@@ string multiteam_info_sprintf(string in
      MSG_INFO_NOTIF(DEATH_SELF_GENERIC,                      N_CONSOLE,  2, 1, "s1 s2loc spree_lost", "s1",      "notify_selfkill",      _("^BG%s^K1 died%s%s"), "")
      MSG_INFO_NOTIF(DEATH_SELF_LAVA,                         N_CONSOLE,  2, 1, "s1 s2loc spree_lost", "s1",      "notify_lava",          _("^BG%s^K1 turned into hot slag%s%s"), _("^BG%s^K1 found a hot place%s%s"))
      MSG_INFO_NOTIF(DEATH_SELF_MON_MAGE,                     N_CONSOLE,  2, 1, "s1 s2loc spree_lost", "s1",      "notify_death",         _("^BG%s^K1 was exploded by a Mage%s%s"), "")
-     MSG_INFO_NOTIF(DEATH_SELF_MON_SHAMBLER_CLAW,            N_CONSOLE,  2, 1, "s1 s2loc spree_lost", "s1",      "notify_death",         _("^BG%s^K1's innards became outwards by a Shambler%s%s"), "")
-     MSG_INFO_NOTIF(DEATH_SELF_MON_SHAMBLER_SMASH,           N_CONSOLE,  2, 1, "s1 s2loc spree_lost", "s1",      "notify_death",         _("^BG%s^K1 was smashed by a Shambler%s%s"), "")
-     MSG_INFO_NOTIF(DEATH_SELF_MON_SHAMBLER_ZAP,             N_CONSOLE,  2, 1, "s1 s2loc spree_lost", "s1",      "notify_death",         _("^BG%s^K1 was zapped to death by a Shambler%s%s"), "")
+     MSG_INFO_NOTIF(DEATH_SELF_MON_GOLEM_CLAW,               N_CONSOLE,  2, 1, "s1 s2loc spree_lost", "s1",      "notify_death",         _("^BG%s^K1's innards became outwards by a Golem%s%s"), "")
+     MSG_INFO_NOTIF(DEATH_SELF_MON_GOLEM_SMASH,              N_CONSOLE,  2, 1, "s1 s2loc spree_lost", "s1",      "notify_death",         _("^BG%s^K1 was smashed by a Golem%s%s"), "")
+     MSG_INFO_NOTIF(DEATH_SELF_MON_GOLEM_ZAP,                N_CONSOLE,  2, 1, "s1 s2loc spree_lost", "s1",      "notify_death",         _("^BG%s^K1 was zapped to death by a Golem%s%s"), "")
      MSG_INFO_NOTIF(DEATH_SELF_MON_SPIDER,                   N_CONSOLE,  2, 1, "s1 s2loc spree_lost", "s1",      "notify_death",         _("^BG%s^K1 was bitten by a Spider%s%s"), "")
      MSG_INFO_NOTIF(DEATH_SELF_MON_WYVERN,                   N_CONSOLE,  2, 1, "s1 s2loc spree_lost", "s1",      "notify_death",         _("^BG%s^K1 was fireballed by a Wyvern%s%s"), "")
      MSG_INFO_NOTIF(DEATH_SELF_MON_ZOMBIE_JUMP,              N_CONSOLE,  2, 1, "s1 s2loc spree_lost", "s1",      "notify_death",         _("^BG%s^K1 joins the Zombies%s%s"), "")
  
      MSG_INFO_NOTIF(SUPERWEAPON_PICKUP,                      N_CONSOLE,  1, 0, "s1", "s1",       "superweapons",         _("^BG%s^K1 picked up a Superweapon"), "")
  
 +    MSG_INFO_NOTIF(SURVIVAL_HUNTER_WIN,                     N_CONSOLE,  0, 0, "", "",           "",                     _("^K1Hunters^BG win the round"), "")
 +    MSG_INFO_NOTIF(SURVIVAL_SURVIVOR_WIN,                   N_CONSOLE,  0, 0, "", "",           "",                     _("^F1Survivors^BG win the round"), "")
 +
      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"), "")
  
      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"), "")
      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(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(SUPERWEAPON_LOST,                  N_ENABLE,    0, 0, "",               CPID_POWERUP,           "0 0",  _("^F2Superweapons have been lost"), "")
      MSG_CENTER_NOTIF(SUPERWEAPON_PICKUP,                N_ENABLE,    0, 0, "",               CPID_POWERUP,           "0 0",  _("^F2You now have a superweapon"), "")
  
 +    MSG_CENTER_NOTIF(SURVIVAL_HUNTER,                   N_ENABLE,    0, 0, "",               CPID_SURVIVAL,          "5 0",  strcat(BOLD_OPERATOR, _("^BGYou are a ^K1hunter^BG! Eliminate the survivor(s) without raising suspicion!")), "")
 +    MSG_CENTER_NOTIF(SURVIVAL_HUNTER_WIN,               N_ENABLE,    0, 0, "",               CPID_ROUND,             "0 0",  _("^K1Hunters^BG win the round"), "")
 +    MSG_CENTER_NOTIF(SURVIVAL_SURVIVOR,                 N_ENABLE,    0, 0, "",               CPID_SURVIVAL,          "5 0",  strcat(BOLD_OPERATOR, _("^BGYou are a ^F1survivor^BG! Identify and eliminate the hunter(s)!")), "")
 +    MSG_CENTER_NOTIF(SURVIVAL_SURVIVOR_WIN,             N_ENABLE,    0, 0, "",               CPID_ROUND,             "0 0",  _("^F1Survivors^BG win the round"), "")
 +
      MULTITEAM_CENTER(TEAMCHANGE,                        N_ENABLE,    0, 1, "",               CPID_TEAMCHANGE,        "1 f1", _("^K1Changing to ^TC^TT^K1 in ^COUNT"), "", NAME)
      MSG_CENTER_NOTIF(TEAMCHANGE_AUTO,                   N_ENABLE,    0, 1, "",               CPID_TEAMCHANGE,        "1 f1", _("^K1Changing team in ^COUNT"), "")
      MSG_CENTER_NOTIF(TEAMCHANGE_SPECTATE,               N_ENABLE,    0, 1, "",               CPID_TEAMCHANGE,        "1 f1", _("^K1Spectating in ^COUNT"), "")
      MSG_MULTI_NOTIF(DEATH_SELF_GENERIC,                 N_ENABLE,  NULL,           INFO_DEATH_SELF_GENERIC,                CENTER_DEATH_SELF_GENERIC)
      MSG_MULTI_NOTIF(DEATH_SELF_LAVA,                    N_ENABLE,  NULL,           INFO_DEATH_SELF_LAVA,                   CENTER_DEATH_SELF_LAVA)
      MSG_MULTI_NOTIF(DEATH_SELF_MON_MAGE,                N_ENABLE,  NULL,           INFO_DEATH_SELF_MON_MAGE,               CENTER_DEATH_SELF_MONSTER)
-     MSG_MULTI_NOTIF(DEATH_SELF_MON_SHAMBLER_CLAW,       N_ENABLE,  NULL,           INFO_DEATH_SELF_MON_SHAMBLER_CLAW,      CENTER_DEATH_SELF_MONSTER)
-     MSG_MULTI_NOTIF(DEATH_SELF_MON_SHAMBLER_SMASH,      N_ENABLE,  NULL,           INFO_DEATH_SELF_MON_SHAMBLER_SMASH,     CENTER_DEATH_SELF_MONSTER)
-     MSG_MULTI_NOTIF(DEATH_SELF_MON_SHAMBLER_ZAP,        N_ENABLE,  NULL,           INFO_DEATH_SELF_MON_SHAMBLER_ZAP,       CENTER_DEATH_SELF_MONSTER)
+     MSG_MULTI_NOTIF(DEATH_SELF_MON_GOLEM_CLAW,          N_ENABLE,  NULL,           INFO_DEATH_SELF_MON_GOLEM_CLAW,         CENTER_DEATH_SELF_MONSTER)
+     MSG_MULTI_NOTIF(DEATH_SELF_MON_GOLEM_SMASH,         N_ENABLE,  NULL,           INFO_DEATH_SELF_MON_GOLEM_SMASH,        CENTER_DEATH_SELF_MONSTER)
+     MSG_MULTI_NOTIF(DEATH_SELF_MON_GOLEM_ZAP,           N_ENABLE,  NULL,           INFO_DEATH_SELF_MON_GOLEM_ZAP,          CENTER_DEATH_SELF_MONSTER)
      MSG_MULTI_NOTIF(DEATH_SELF_MON_SPIDER,              N_ENABLE,  NULL,           INFO_DEATH_SELF_MON_SPIDER,             CENTER_DEATH_SELF_MONSTER)
      MSG_MULTI_NOTIF(DEATH_SELF_MON_WYVERN,              N_ENABLE,  NULL,           INFO_DEATH_SELF_MON_WYVERN,             CENTER_DEATH_SELF_MONSTER)
      MSG_MULTI_NOTIF(DEATH_SELF_MON_ZOMBIE_JUMP,         N_ENABLE,  NULL,           INFO_DEATH_SELF_MON_ZOMBIE_JUMP,        CENTER_DEATH_SELF_MONSTER)
index e1969ef5cdc93a33aaf145b04dd484052e6fae5c,fb06d264af250212cea590a3e0b9b51224281898..e165125138a9b47a7deaa2680b05bcac5ca8f415
@@@ -62,7 -62,6 +62,6 @@@ ENUMCLASS(CPID
        CASE(CPID, MISSING_TEAMS)
        CASE(CPID, MISSING_PLAYERS)
        CASE(CPID, INSTAGIB_FINDAMMO)
-       CASE(CPID, CAMPAIGN_MESSAGE)
        CASE(CPID, MOTD)
        CASE(CPID, NIX)
        CASE(CPID, ONSLAUGHT)
@@@ -70,7 -69,6 +69,7 @@@
        CASE(CPID, OVERTIME)
        CASE(CPID, POWERUP)
        CASE(CPID, RACE_FINISHLAP)
 +      CASE(CPID, SURVIVAL)
        CASE(CPID, TEAMCHANGE)
        CASE(CPID, TIMEOUT)
        CASE(CPID, TIMEIN)
@@@ -360,8 -358,8 +359,8 @@@ float autocvar_notification_show_sprees
        f1points: point or points depending on f1
        f1ord: count_ordinal of f1
        f1time: process_time of f1
-       f1race_time: mmssss of f1
-       f2race_time: mmssss of f2
+       f1race_time: TIME_ENCODED_TOSTRING of f1
+       f2race_time: TIME_ENCODED_TOSTRING of f2
        race_col: color of race time/position (i.e. good or bad)
        race_diff: show time difference between f2 and f3
        missing_teams: show which teams still need players
@@@ -420,11 -418,11 +419,11 @@@ string BUFF_NAME(int i)
        ARG_CASE(ARG_CS,        "f1points",      (f1 == 1 ? _("point") : _("points"))) \
        ARG_CASE(ARG_CS_SV,     "f1ord",         count_ordinal(f1)) \
        ARG_CASE(ARG_CS_SV,     "f1time",        process_time(2, f1)) \
-       ARG_CASE(ARG_CS_SV_HA,  "f1race_time",   mmssss(f1)) \
-       ARG_CASE(ARG_CS_SV_HA,  "f2race_time",   mmssss(f2)) \
-       ARG_CASE(ARG_CS_SV_HA,  "f3race_time",   mmssss(f3)) \
+       ARG_CASE(ARG_CS_SV_HA,  "f1race_time",   TIME_ENCODED_TOSTRING(f1, true)) \
+       ARG_CASE(ARG_CS_SV_HA,  "f2race_time",   TIME_ENCODED_TOSTRING(f2, true)) \
+       ARG_CASE(ARG_CS_SV_HA,  "f3race_time",   TIME_ENCODED_TOSTRING(f3, true)) \
        ARG_CASE(ARG_CS_SV,     "race_col",      CCR(((f1 == 1) ? "^F1" : "^F2"))) \
-       ARG_CASE(ARG_CS_SV,     "race_diff",     ((f2 > f3) ? sprintf(CCR("^1[+%s]"), mmssss(f2 - f3)) : sprintf(CCR("^2[-%s]"), mmssss(f3 - f2)))) \
+       ARG_CASE(ARG_CS_SV,     "race_diff",     ((f2 > f3) ? sprintf(CCR("^1[+%s]"), TIME_ENCODED_TOSTRING(f2 - f3, true)) : sprintf(CCR("^2[-%s]"), TIME_ENCODED_TOSTRING(f3 - f2, true)))) \
        ARG_CASE(ARG_CS,        "missing_teams", notif_arg_missing_teams(f1)) \
        ARG_CASE(ARG_CS,        "pass_key",      getcommandkey(_("drop flag"), "+use")) \
        ARG_CASE(ARG_CS,        "nade_key",      getcommandkey(_("throw nade"), "dropweapon")) \
@@@ -635,6 -633,7 +634,7 @@@ string notif_arg_item_wepammo(float f1
  {
        string ammoitems = "";
        Weapon wep = REGISTRY_GET(Weapons, f1);
+       // TODO: registry handles
        switch (wep.ammo_type)
        {
                case RES_SHELLS:  ammoitems = ITEM_Shells.m_name;      break;
diff --combined qcsrc/common/scores.qh
index 002a6a3c0aaaef67f66c0e4bca76e7eee810a387,45af93992aacdf33f7d8338867fe73a43807b2a3..9e570e30ebd1e44e233b70b18e8d97f8855def91
@@@ -15,7 -15,6 +15,6 @@@ STATIC_INIT(Scores_renumber) { FOREACH(
   * Score indices
   */
  
- // game mode specific indices are not in common/, but in server/scores_rules.qc!
  #ifdef GAMEQC
  // fields not networked via the score system
  REGISTER_SP(END);
@@@ -88,9 -87,6 +87,9 @@@ REGISTER_SP(NEXBALL_FAULTS)
  
  REGISTER_SP(ONS_TAKES);
  REGISTER_SP(ONS_CAPS);
 +
 +REGISTER_SP(SV_SURVIVALS);
 +REGISTER_SP(SV_HUNTS);
  #endif
  
  
diff --combined qcsrc/common/stats.qh
index 91e5631be8b020605c43a9ec50e7827ec2cffef9,128f090c4fd9ec94ff1269b3336bda705be60c8e..077767f2d4bc8acdbea3a390b041818d77a9c14c
@@@ -79,6 -79,9 +79,9 @@@ 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
  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")
@@@ -91,7 -94,6 +94,6 @@@ REGISTER_STAT(GAMESTARTTIME, float, gam
  /** arc heat in [0,1] */
  REGISTER_STAT(PRESSED_KEYS, int)
  REGISTER_STAT(FUEL, int)
- REGISTER_STAT(NB_METERSTART, float)
  /** compressShotOrigin */
  REGISTER_STAT(SHOTORG, int)
  REGISTER_STAT(LEADLIMIT, float, autocvar_leadlimit)
@@@ -100,7 -102,7 +102,7 @@@ REGISTER_STAT(LEADLIMIT_AND_FRAGLIMIT, 
  REGISTER_STAT(LAST_PICKUP, float)
  REGISTER_STAT(HUD, int)
  REGISTER_STAT(HIT_TIME, float)
- REGISTER_STAT(DAMAGE_DEALT_TOTAL, int)
+ REGISTER_STAT(HITSOUND_DAMAGE_DEALT_TOTAL, int)
  REGISTER_STAT(TYPEHIT_TIME, float)
  REGISTER_STAT(AIR_FINISHED, float)
  REGISTER_STAT(VEHICLESTAT_HEALTH, int)
@@@ -116,6 -118,8 +118,8 @@@ REGISTER_STAT(SECRETS_TOTAL, int, secre
  REGISTER_STAT(SECRETS_FOUND, int, secrets_found)
  REGISTER_STAT(RESPAWN_TIME, float)
  REGISTER_STAT(ROUNDSTARTTIME, float, round_starttime)
+ 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)
@@@ -338,6 -342,9 +342,9 @@@ REGISTER_STAT(DOM_PPS_BLUE, float
  REGISTER_STAT(DOM_PPS_YELLOW, float)
  REGISTER_STAT(DOM_PPS_PINK, float)
  
+ // nexball
+ REGISTER_STAT(NB_METERSTART, float)
  #ifdef SVQC
  float autocvar_g_teleport_maxspeed;
  #endif
@@@ -358,6 -365,8 +365,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
@@@ -397,6 -406,8 +406,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")
@@@ -428,5 -439,3 +439,5 @@@ REGISTER_STAT(GUNALIGN, int
  #ifdef SVQC
  SPECTATE_COPYFIELD(_STAT(GUNALIGN))
  #endif
 +
 +REGISTER_STAT(SURVIVAL_ROUNDTIMER, float)
index 8781061348b9ac281ed0beb66f418247509fe801,f98a8de8876e18ea5258fb5e0b6847234fc87de3..f013263b632a3fc29bbedf4586a56faab269b2fa
@@@ -313,11 -313,6 +313,6 @@@ void URI_Get_Callback(float id, float s
        }
  }
  
- void DisableServerBackwardsCompatibility()
- {
-       cvar_set("gameversion_min", ftos(100 * floor(cvar("gameversion") / 100)));
- }
  void UpdateNotification_URI_Get_Callback(float id, float status, string data)
  {
        float n;
        string s;
  
        string un_version = "";
+       string un_tosversion = "";
        string un_download = "";
        string un_url = "";
        string un_bannedservers = "";
                                un_version = s;
                                break;
                        }
+                       case "T":
+                       {
+                               un_tosversion = s;
+                               break;
+                       }
                        case "C":
                        {
                                un_compatexpire = s;
                }
        }
  
-       if(un_version != "")
+       if(un_version != "" && vercmp(cvar_string("g_xonoticversion"), un_version) < 0)
        {
-               if(vercmp(cvar_string("g_xonoticversion"), un_version) < 0)
-               {
-                       // update needed
-                       _Nex_ExtResponseSystem_UpdateTo = strzone(un_version);
-                       if(un_download) { LOG_INFO(_("Update can be downloaded at:"), "\n", un_download); }
-                       if(un_url) { _Nex_ExtResponseSystem_UpdateToURL = strzone(un_url); }
-                       DisableServerBackwardsCompatibility();
-               }
-               else if(cvar_string("g_xonoticversion") == un_version)
-               {
-                       if(un_compatexpire != "")
-                       {
-                               string curdate = strftime(false, "%Y%m%d%H%M%S");
-                               if (strcmp(curdate, un_compatexpire) >= 0)
-                                       DisableServerBackwardsCompatibility();
-                       }
-               }
+               // update needed
+               _Nex_ExtResponseSystem_UpdateTo = strzone(un_version);
+               if(un_download) { LOG_INFO(_("Update can be downloaded at:"), "\n", un_download); }
+               if(un_url) { _Nex_ExtResponseSystem_UpdateToURL = strzone(un_url); }
+       }
+       if(un_tosversion != "")
+       {
+               _Nex_ExtResponseSystem_NewToS = stof(un_tosversion);
        }
  
        if(un_bannedservers != "")
@@@ -464,15 -457,8 +457,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)
@@@ -582,6 -568,7 +568,7 @@@ void preMenuDraw(
                draw_CenterText(mid - 1 * line, l1, fs, '1 0 0', 1, 0);
                draw_CenterText(mid - 0 * line, l2, fs, '0 0 1', 1, 0);
        }
        if (!campaign_name_previous)
                campaign_name_previous = strzone(strcat(campaign_name, "x")); // force unequal
        if(campaign_name == campaign_name_previous)
@@@ -681,15 -668,14 +668,16 @@@ float updateCompression(
        GAMETYPE(MAPINFO_TYPE_NEXBALL) \
        GAMETYPE(MAPINFO_TYPE_ONSLAUGHT) \
        GAMETYPE(MAPINFO_TYPE_ASSAULT) \
++      GAMETYPE(MAPINFO_TYPE_SURVIVAL) \
        /* GAMETYPE(MAPINFO_TYPE_DUEL) */ \
-       /* GAMETYPE(MAPINFO_TYPE_SURVIVAL) */ \
 +      /* GAMETYPE(MAPINFO_TYPE_INVASION) */ \
        /**/
  
  // hidden gametypes come last so indexing always works correctly
  #define HIDDEN_GAMETYPES \
        GAMETYPE(MAPINFO_TYPE_RACE) \
        GAMETYPE(MAPINFO_TYPE_CTS) \
+       GAMETYPE(MAPINFO_TYPE_INVASION) \
        /**/
  
  Gametype GameType_GetID(int cnt)
@@@ -818,6 -804,18 +806,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)
index f33fc4598e0f785f04a5138b27ebcab5b835125b,e7f9f897b0fdce206ccae04312aad6bbe17092b1..87b3aa16d3bc267f3a0d5ae589d25f9a7d156363
@@@ -10,6 -10,7 +10,7 @@@
  /** called when a player becomes observer, after shared setup */
  #define EV_MakePlayerObserver(i, o) \
      /** player */ i(entity, MUTATOR_ARGV_0_entity) \
+     /** is_forced */ i(bool, MUTATOR_ARGV_1_bool) \
      /**/
  MUTATOR_HOOKABLE(MakePlayerObserver, EV_MakePlayerObserver)
  
@@@ -316,8 -317,17 +317,17 @@@ MUTATOR_HOOKABLE(PlayerPreThink, EV_Pla
      /**/
  MUTATOR_HOOKABLE(GetPressedKeys, EV_GetPressedKeys);
  
- /** is meant to call GetCvars_handle*(get_cvars_s, get_cvars_f, cvarfield, "cvarname") for cvars this mutator needs from the client */
- // NOTE: requesting cvar values (get_cvars_f 0) is deprecated
+ /**
+  * is meant to call GetCvars_handle* for cvars this mutator needs from the client, e.g.:
+       MUTATOR_HOOKFUNCTION(mymutator, GetCvars)
+       {
+               GetCvars_handleFloat(this, store, s, f, cvar_mycvar, "mycvar");
+               return false;
+       }
+  * Usually you can just use REPLICATE instead of this hook, e.g.:
+       REPLICATE(cvar_mycvar, int, "mycvar");
+  * NOTE: requesting cvar values (get_cvars_f 0) is deprecated
+  */
  #define EV_GetCvars(i, o) \
      /**/ i(float, get_cvars_f) \
      /**/ i(string, get_cvars_s) \
@@@ -704,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(int, MUTATOR_ARGV_1_int) \
-       /** 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(int, MUTATOR_ARGV_1_int) \
-       /**/                  o(int, MUTATOR_ARGV_1_int) \
-       /** 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(int, MUTATOR_ARGV_1_int) \
-       /** 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(int, MUTATOR_ARGV_1_int) \
-       /** 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(int, MUTATOR_ARGV_1_int) \
-       /**/                 o(int, MUTATOR_ARGV_1_int) \
-       /** 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(int, MUTATOR_ARGV_1_int) \
-       /**/                 o(int, MUTATOR_ARGV_1_int) \
-       /** 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(int, MUTATOR_ARGV_1_int) \
-     /**/                 o(int, MUTATOR_ARGV_1_int) \
-     /** 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(int, MUTATOR_ARGV_1_int) \
-     /**/                 o(int, MUTATOR_ARGV_1_int) \
-     /** 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) \
@@@ -996,11 -1011,11 +1011,6 @@@ MUTATOR_HOOKABLE(AddPlayerScore, EV_Add
      /**/
  MUTATOR_HOOKABLE(AddedPlayerScore, EV_AddPlayerScore);
  
--#define EV_GetPlayerStatus(i, o) \
--    /** player */    i(entity, MUTATOR_ARGV_0_entity) \
--    /**/
--MUTATOR_HOOKABLE(GetPlayerStatus, EV_GetPlayerStatus);
--
  #define EV_SetWeaponArena(i, o) \
      /** arena */     i(string, MUTATOR_ARGV_0_string) \
      /**/             o(string, MUTATOR_ARGV_0_string) \
@@@ -1138,7 -1153,10 +1148,10 @@@ MUTATOR_HOOKABLE(ChatMessage, EV_ChatMe
      /**/
  MUTATOR_HOOKABLE(ChatMessageTo, EV_ChatMessageTo);
  
- /** return true to just restart the match, for modes that don't support readyrestart */
+ /**
+  * return true to restart the server instead of restarting the match, for modes that don't support readyrestart.
+  * NOTE: ReadyRestart support is mandatory in campaign
+  */
  MUTATOR_HOOKABLE(ReadyRestart_Deny, EV_NO_ARGS);
  
  /** called when a fusion reactor is validating its target */