]> de.git.xonotic.org Git - xonotic/xonotic-data.pk3dir.git/commitdiff
Merge branch 'master' into terencehill/lms_updates 808/head
authorterencehill <piuntn@gmail.com>
Wed, 6 Jul 2022 13:28:34 +0000 (15:28 +0200)
committerterencehill <piuntn@gmail.com>
Wed, 6 Jul 2022 13:28:34 +0000 (15:28 +0200)
# Conflicts:
# gamemodes-server.cfg
# notifications.cfg
# qcsrc/common/gamemodes/gamemode/lms/sv_lms.qc
# qcsrc/common/notifications/all.inc

1  2 
gamemodes-server.cfg
notifications.cfg
qcsrc/common/gamemodes/gamemode/lms/cl_lms.qc
qcsrc/common/gamemodes/gamemode/lms/sv_lms.qc
qcsrc/common/notifications/all.inc

diff --combined gamemodes-server.cfg
index 41744478cec0b66f9eb8c094dcfd01372e031585,318749021fe8d329ae55e6cbb58de4eafb3486ef..a57c8f135c70aa561cf68ee2a0e52bb02aa80947
@@@ -224,7 -224,7 +224,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"
@@@ -322,7 -322,7 +322,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"
  
@@@ -445,24 -445,13 +445,27 @@@ 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_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_leader_lives_diff 2 "players leading by at least this number of lives are considered leaders and are more visible"
 +set g_lms_leader_minpercent 0.5 "leading players are not considered leaders only if they are more than this percentage of total players"
 +set g_lms_leader_wp_time 5 "show waypoints for leaders only for this amount of time"
 +set g_lms_leader_wp_interval 25 "periodically show again waypoints for leaders after this amount of time"
 +set g_lms_leader_wp_interval_jitter 10 "jitter waypoint interval by this amount of time"
 +set g_lms_dynamic_respawn_delay 1 "increase player respawn delay based on the number of lives less than the leader (NOTE: delay doesn't increase when only 2 players are left alive)"
 +set g_lms_dynamic_respawn_delay_base 2 "base player respawn delay"
 +set g_lms_dynamic_respawn_delay_increase 3 "increase base player respawn delay by this amount of time for each life less than the leader"
 +set g_lms_dynamic_respawn_delay_max 20 "max player respawn delay"
 +set g_lms_dynamic_vampire 1 "attackers receive a fraction of health removed from enemies based on the number of lives less than the enemy"
 +set g_lms_dynamic_vampire_factor_base 0.1 "base vampire factor"
 +set g_lms_dynamic_vampire_factor_increase 0.1 "increase vampire factor by this fraction for each life less than the enemy"
 +set g_lms_dynamic_vampire_factor_max 0.5 "max vampire factor"
 +set g_lms_dynamic_vampire_min_lives_diff 2 "number of lives the attacker must have less than the enemy to receive the base fraction of health"
+ 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"
  
  
  // =========
@@@ -562,8 -551,6 +565,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)"
  
  // ======
diff --combined notifications.cfg
index 131b3060cccd6ad49179734cde3c98d32a439496,ecd6c0b24004e455cb9fd608aecb764defa4b506..732ed5faec28f19209e3f12cb1fdd1d9270df378
@@@ -93,7 -93,7 +93,7 @@@ seta notification_ANNCE_VOTE_ACCEPT "2
  seta notification_ANNCE_VOTE_CALL "2" "0 = disabled, 1 = enabled if gentle mode is off, 2 = always enabled"
  seta notification_ANNCE_VOTE_FAIL "2" "0 = disabled, 1 = enabled if gentle mode is off, 2 = always enabled"
  
- // MSG_INFO notifications (count = 334):
+ // MSG_INFO notifications (count = 335):
  seta notification_INFO_CA_JOIN_LATE "1" "0 = off, 1 = print to console, 2 = print to console and chatbox (if notification_allow_chatboxprint is enabled)"
  seta notification_INFO_CA_LEAVE "1" "0 = off, 1 = print to console, 2 = print to console and chatbox (if notification_allow_chatboxprint is enabled)"
  seta notification_INFO_CHAT_NOSPECTATORS "2" "0 = off, 1 = print to console, 2 = print to console and chatbox (if notification_allow_chatboxprint is enabled)"
@@@ -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)"
@@@ -283,6 -283,7 +283,7 @@@ seta notification_INFO_WEAPON_ACCORDEON
  seta notification_INFO_WEAPON_ACCORDEON_SUICIDE "1" "0 = off, 1 = print to console, 2 = print to console and chatbox (if notification_allow_chatboxprint is enabled)"
  seta notification_INFO_WEAPON_ARC_MURDER "1" "0 = off, 1 = print to console, 2 = print to console and chatbox (if notification_allow_chatboxprint is enabled)"
  seta notification_INFO_WEAPON_ARC_MURDER_SPRAY "1" "0 = off, 1 = print to console, 2 = print to console and chatbox (if notification_allow_chatboxprint is enabled)"
+ seta notification_INFO_WEAPON_ARC_SUICIDE_BOLT "1" "0 = off, 1 = print to console, 2 = print to console and chatbox (if notification_allow_chatboxprint is enabled)"
  seta notification_INFO_WEAPON_BLASTER_MURDER "1" "0 = off, 1 = print to console, 2 = print to console and chatbox (if notification_allow_chatboxprint is enabled)"
  seta notification_INFO_WEAPON_BLASTER_SUICIDE "1" "0 = off, 1 = print to console, 2 = print to console and chatbox (if notification_allow_chatboxprint is enabled)"
  seta notification_INFO_WEAPON_CRYLINK_MURDER "1" "0 = off, 1 = print to console, 2 = print to console and chatbox (if notification_allow_chatboxprint is enabled)"
@@@ -342,12 -343,11 +343,11 @@@ seta notification_INFO_WEAPON_TUBA_SUIC
  seta notification_INFO_WEAPON_VAPORIZER_MURDER "1" "0 = off, 1 = print to console, 2 = print to console and chatbox (if notification_allow_chatboxprint is enabled)"
  seta notification_INFO_WEAPON_VORTEX_MURDER "1" "0 = off, 1 = print to console, 2 = print to console and chatbox (if notification_allow_chatboxprint is enabled)"
  
 -// MSG_CENTER notifications (count = 241):
 +// MSG_CENTER notifications (count = 243):
  seta notification_CENTER_ALONE "1" "0 = off, 1 = centerprint"
  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"
@@@ -480,8 -480,7 +480,9 @@@ 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_LMS_VISIBLE_LEADER "1" "0 = off, 1 = centerprint"
 +seta notification_CENTER_LMS_VISIBLE_OTHER "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"
@@@ -539,7 -538,7 +540,7 @@@ seta notification_CENTER_VEHICLE_STEAL 
  seta notification_CENTER_VEHICLE_STEAL_SELF "1" "0 = off, 1 = centerprint"
  seta notification_CENTER_WEAPON_MINELAYER_LIMIT "1" "0 = off, 1 = centerprint"
  
- // MSG_MULTI notifications (count = 156):
+ // MSG_MULTI notifications (count = 157):
  seta notification_DEATH_MURDER_BUFF "1" "Enable this multiple notification"
  seta notification_DEATH_MURDER_CHEAT "1" "Enable this multiple notification"
  seta notification_DEATH_MURDER_DROWN "1" "Enable this multiple notification"
@@@ -580,10 -579,10 +581,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"
@@@ -638,6 -637,7 +639,7 @@@ seta notification_WEAPON_ACCORDEON_MURD
  seta notification_WEAPON_ACCORDEON_SUICIDE "1" "Enable this multiple notification"
  seta notification_WEAPON_ARC_MURDER "1" "Enable this multiple notification"
  seta notification_WEAPON_ARC_MURDER_SPRAY "1" "Enable this multiple notification"
+ seta notification_WEAPON_ARC_SUICIDE_BOLT "1" "Enable this multiple notification"
  seta notification_WEAPON_BLASTER_MURDER "1" "Enable this multiple notification"
  seta notification_WEAPON_BLASTER_SUICIDE "1" "Enable this multiple notification"
  seta notification_WEAPON_CRYLINK_MURDER "1" "Enable this multiple notification"
@@@ -748,4 -748,4 +750,4 @@@ seta notification_show_sprees_info "3" 
  seta notification_show_sprees_info_newline "1" "Show attacker spree information for MSG_INFO messages on a separate line than the death notification itself"
  seta notification_show_sprees_info_specialonly "1" "Don't show attacker spree information in MSG_INFO messages if it isn't an achievement"
  
- // Notification counts (total = 841): MSG_ANNCE = 80, MSG_INFO = 334, MSG_CENTER = 243, MSG_MULTI = 156, MSG_CHOICE = 28
 -// Notification counts (total = 841): MSG_ANNCE = 80, MSG_INFO = 335, MSG_CENTER = 241, MSG_MULTI = 157, MSG_CHOICE = 28
++// Notification counts (total = 843): MSG_ANNCE = 80, MSG_INFO = 335, MSG_CENTER = 243, MSG_MULTI = 157, MSG_CHOICE = 28
index ea622ec824ed7c4852936fe40105bc14d31ad500,05e335597751f8f9ecca6eab12c87fa11b1998e6..53b56c56c658db2c8159b7c94aab9dfcf9621cc4
@@@ -13,14 -13,7 +13,7 @@@ MUTATOR_HOOKFUNCTION(cl_lms, DrawInfoMe
                vector mySize = M_ARGV(1, vector);
                vector fontsize = '0.2 0.2 0' * mySize.y;
                int img_curr_group = M_ARGV(2, int);
-               if(sk.(scores(ps_primary)) >= 666)
-               {
-                       InfoMessage(_("^1Match has already begun"));
-                       M_ARGV(0, vector) = pos;
-                       M_ARGV(2, int) = img_curr_group;
-                       return true;
-               }
-               else if(sk.(scores(ps_primary)) > 0)
+               if(sk.(scores(ps_primary)) > 0)
                {
                        InfoMessage(_("^1You have no more lives left"));
                        M_ARGV(0, vector) = pos;
        }
        return false;
  }
 +
 +void HUD_Mod_LMS_Draw(vector myPos, vector mySize)
 +{
 +      int leaders_count = STAT(REDALIVE); // recycled stat
 +      if(!leaders_count)
 +      {
 +              mod_active = 0;
 +              return;
 +      }
 +
 +      int rows, columns;
 +      float aspect_ratio = 2;
 +      rows = HUD_GetRowCount(2, mySize, aspect_ratio);
 +      columns = ceil(2 / rows);
 +
 +      vector pos = myPos;
 +      vector itemSize = vec2(mySize.x / columns, mySize.y / rows);
 +
 +      bool visible_leaders = STAT(OBJECTIVE_STATUS);
 +
 +      if (visible_leaders)
 +              drawpic_aspect_skin(pos, "flag_stalemate", vec2(0.5 * itemSize.x, itemSize.y), '1 1 1', panel_fg_alpha, DRAWFLAG_NORMAL);
 +      drawpic_aspect_skin(pos, "player_neutral", vec2(0.5 * itemSize.x, itemSize.y), '1 1 1', panel_fg_alpha, DRAWFLAG_NORMAL);
 +      drawstring_aspect(pos + eX * 0.5 * itemSize.x, ftos(leaders_count), vec2(0.5 * itemSize.x, itemSize.y), '1 1 1', panel_fg_alpha, DRAWFLAG_NORMAL);
 +
 +      if (rows > 1)
 +              pos.y = myPos.y + itemSize.y;
 +      else
 +              pos.x = myPos.x + itemSize.x;
 +
 +      int lives_diff = STAT(BLUEALIVE); // recycled stat
 +      vector color = '1 1 0';
 +      if (lives_diff >= 4)
 +              color = '1 0 0';
 +      else if (lives_diff == 3)
 +              color = '1 0.5 0';
 +      float scale = 0.75;
 +      drawstring_aspect(pos + itemSize * (1 - scale) * 0.5, strcat("+", ftos(lives_diff)), itemSize * scale, color, panel_fg_alpha, DRAWFLAG_NORMAL);
 +}
 +
 +void HUD_Mod_LMS(vector myPos, vector mySize)
 +{
 +      mod_active = 1;
 +
 +      HUD_Mod_LMS_Draw(myPos, mySize);
 +}
index 571528c069475d37736086cbe57d84d528b5125c,9a210262b68e80b8b1c8b323a81739f8f8111f94..e3ebfefd31fc9a6562590a1650ffc9b506b1e5d9
@@@ -7,29 -7,12 +7,32 @@@
  #include <server/items/items.qh>
  
  int autocvar_g_lms_extra_lives;
+ float autocvar_g_lms_forfeit_min_match_time;
  bool autocvar_g_lms_join_anytime;
  int autocvar_g_lms_last_join;
+ bool autocvar_g_lms_items;
  bool autocvar_g_lms_regenerate;
 +int autocvar_g_lms_leader_lives_diff = 2;
 +float autocvar_g_lms_leader_minpercent = 0.5;
 +float autocvar_g_lms_leader_wp_interval = 25;
 +float autocvar_g_lms_leader_wp_interval_jitter = 10;
 +float autocvar_g_lms_leader_wp_time = 5;
 +float autocvar_g_lms_dynamic_respawn_delay = 1;
 +float autocvar_g_lms_dynamic_respawn_delay_base = 2;
 +float autocvar_g_lms_dynamic_respawn_delay_increase = 3;
 +float autocvar_g_lms_dynamic_respawn_delay_max = 20;
 +bool autocvar_g_lms_dynamic_vampire = 1;
 +float autocvar_g_lms_dynamic_vampire_factor_base = 0.1;
 +float autocvar_g_lms_dynamic_vampire_factor_increase = 0.1;
 +float autocvar_g_lms_dynamic_vampire_factor_max = 0.5;
 +int autocvar_g_lms_dynamic_vampire_min_lives_diff = 2;
 +
 +.float lms_leader;
 +int lms_leaders;
 +float lms_visible_leaders_time;
 +bool lms_visible_leaders = true; // triggers lms_visible_leaders_time update in the first frame
 +bool lms_visible_leaders_prev;
+ bool autocvar_g_lms_rot;
  
  // main functions
  int LMS_NewPlayerLives()
@@@ -61,10 -44,16 +64,16 @@@ int WinningCondition_LMS(
  
        entity first_player = NULL;
        int totalplayers = 0;
-       FOREACH_CLIENT(IS_PLAYER(it) && it.frags == FRAGS_PLAYER, {
-               if (!totalplayers)
-                       first_player = it;
-               ++totalplayers;
+       int totalplayed = 0;
+       FOREACH_CLIENT(true, {
+               if (IS_PLAYER(it) && it.frags == FRAGS_PLAYER)
+               {
+                       if (!totalplayers)
+                               first_player = it;
+                       ++totalplayers;
+               }
+               else if (GameRules_scoring_add(it, LMS_RANK, 0))
+                       ++totalplayed;
        });
  
        if (totalplayers)
@@@ -73,7 -62,7 +82,7 @@@
                {
                        // two or more active players - continue with the game
  
-                       if (autocvar_g_campaign)
+                       if (autocvar_g_campaign && campaign_bots_may_start)
                        {
                                FOREACH_CLIENT(IS_REAL_CLIENT(it), {
                                        float pl_lives = GameRules_scoring_add(it, LMS_LIVES, 0);
                        // exactly one player?
  
                        ClearWinners();
-                       SetWinners(winning, 0); // NOTE: exactly one player is still "player", so this works out
  
                        if (LMS_NewPlayerLives())
                        {
+                               if (totalplayed && game_starttime > 0 && time > game_starttime + autocvar_g_lms_forfeit_min_match_time) // give players time to join
+                               {
+                                       GameRules_scoring_add(first_player, LMS_RANK, 1);
+                                       first_player.winning = 1;
+                                       return WINNING_YES;
+                               }
                                // game still running (that is, nobody got removed from the game by a frag yet)? then continue
                                return WINNING_NO;
                        }
                                // a winner!
                                // and assign him his first place
                                GameRules_scoring_add(first_player, LMS_RANK, 1);
+                               first_player.winning = 1;
                                return WINNING_YES;
                        }
                }
                // nobody is playing at all...
                if (LMS_NewPlayerLives())
                {
+                       if (totalplayed && game_starttime > 0 && time > game_starttime + autocvar_g_lms_forfeit_min_match_time) // give players time to join
+                       {
+                               ClearWinners();
+                               return WINNING_YES;
+                       }
                        // wait for players...
                }
                else
        return WINNING_NO;
  }
  
 +// runs on waypoints which are attached to leaders, updates once per frame
 +bool lms_waypointsprite_visible_for_player(entity this, entity player, entity view)
 +{
 +      if(view.lms_leader)
 +              if(IS_SPEC(player))
 +                      return false; // we don't want spectators of leaders to see the attached waypoint on the top of their screen
 +
 +      if (!lms_visible_leaders)
 +              return false;
 +
 +      return true;
 +}
 +
 +int lms_leaders_lives_diff;
 +void lms_UpdateLeaders()
 +{
 +      int max_lives = 0;
 +      int pl_cnt = 0;
 +      FOREACH_CLIENT(IS_PLAYER(it) && it.frags != FRAGS_PLAYER_OUT_OF_GAME, {
 +              int lives = GameRules_scoring_add(it, LMS_LIVES, 0);
 +              if (lives > max_lives)
 +                      max_lives = lives;
 +              pl_cnt++;
 +      });
 +
 +      int second_max_lives = 0;
 +      int pl_cnt_with_max_lives = 0;
 +      FOREACH_CLIENT(IS_PLAYER(it) && it.frags != FRAGS_PLAYER_OUT_OF_GAME, {
 +              int lives = GameRules_scoring_add(it, LMS_LIVES, 0);
 +              if (lives == max_lives)
 +                      pl_cnt_with_max_lives++;
 +              else if (lives > second_max_lives)
 +                      second_max_lives = lives;
 +      });
 +
 +      lms_leaders_lives_diff = max_lives - second_max_lives;
 +
 +      int lives_diff = autocvar_g_lms_leader_lives_diff;
 +      if (lms_leaders_lives_diff >= lives_diff && pl_cnt_with_max_lives <= pl_cnt * autocvar_g_lms_leader_minpercent)
 +              FOREACH_CLIENT(IS_PLAYER(it) && it.frags != FRAGS_PLAYER_OUT_OF_GAME, {
 +                      int lives = GameRules_scoring_add(it, LMS_LIVES, 0);
 +                      if (lives == max_lives)
 +                      {
 +                              if (!it.lms_leader)
 +                                      it.lms_leader = true;
 +                      }
 +                      else
 +                      {
 +                              it.lms_leader = false;
 +                      }
 +              });
 +      else
 +              FOREACH_CLIENT(IS_PLAYER(it) && it.frags != FRAGS_PLAYER_OUT_OF_GAME, {
 +                      if (it.waypointsprite_attachedforcarrier)
 +                              WaypointSprite_Kill(it.waypointsprite_attachedforcarrier);
 +                      it.lms_leader = false;
 +              });
 +}
 +
  // mutator hooks
  MUTATOR_HOOKFUNCTION(lms, reset_map_global)
  {
@@@ -212,7 -153,7 +232,7 @@@ MUTATOR_HOOKFUNCTION(lms, reset_map_pla
                }
  
                CS(it).killcount = 0;
-               it.lmsplayer = 0;
+               INGAME_STATUS_CLEAR(it);
                it.lms_spectate_warning = 0;
                GameRules_scoring_add(it, LMS_RANK, -GameRules_scoring_add(it, LMS_RANK, 0));
                GameRules_scoring_add(it, LMS_LIVES, -GameRules_scoring_add(it, LMS_LIVES, 0));
  
                TRANSMUTE(Player, it);
                PutClientInServer(it);
 +              it.lms_leader = false;
 +              if (it.waypointsprite_attachedforcarrier)
 +                      WaypointSprite_Kill(it.waypointsprite_attachedforcarrier);
        });
  }
  
@@@ -239,12 -177,15 +259,15 @@@ MUTATOR_HOOKFUNCTION(lms, ReadLevelCvar
  // returns true if player is added to the game
  bool lms_AddPlayer(entity player)
  {
-       if (!player.lmsplayer)
+       if (!INGAME(player))
        {
                int lives = GameRules_scoring_add(player, LMS_LIVES, LMS_NewPlayerLives());
                if(lives <= 0)
                        return false;
-               player.lmsplayer = 2; // temp value indicating player has just joined the game (but not spawned yet)
+               if (time < game_starttime)
+                       INGAME_STATUS_SET(player, INGAME_STATUS_JOINED);
+               else
+                       INGAME_STATUS_SET(player, INGAME_STATUS_JOINING); // this is just to delay setting health and armor that can't be done here
        }
        if (warmup_stage || time <= game_starttime)
        {
@@@ -285,7 -226,7 +308,7 @@@ MUTATOR_HOOKFUNCTION(lms, PlayerSpawn
        if (warmup_stage || time < game_starttime)
                return true;
  
-       if (player.lmsplayer == 2) // just joined the game
+       if (INGAME_JOINING(player))
        {
                // spawn player with the same amount of health / armor
                // as the least healthy player with the least number of lives
                        SetResource(player, RES_HEALTH, max(1, min_health));
                if (min_armorvalue != start_armorvalue)
                        SetResource(player, RES_ARMOR, min_armorvalue);
-               player.lmsplayer = 1;
+               INGAME_STATUS_SET(player, INGAME_STATUS_JOINED);
        }
  }
  
@@@ -316,6 -257,20 +339,6 @@@ MUTATOR_HOOKFUNCTION(lms, ForbidSpawn
        return true;
  }
  
 -MUTATOR_HOOKFUNCTION(lms, PlayerDies)
 -{
 -      entity frag_target = M_ARGV(2, entity);
 -
 -      float tl = GameRules_scoring_add(frag_target, LMS_LIVES, 0);
 -      if (tl <= 0)
 -      {
 -              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;
 -}
 -
  void lms_RemovePlayer(entity player)
  {
        if (warmup_stage || time < game_starttime)
                        });
                        GameRules_scoring_add(player, LMS_RANK, pl_cnt + 1);
                }
-               else
+               else if (INGAME(player))
                {
                        int min_forfeiter_rank = 665; // different from 666
-                       FOREACH_CLIENT(true, {
+                       FOREACH_CLIENT(it != player, {
                                // update rank of other players that were eliminated
                                if (it.frags == FRAGS_PLAYER_OUT_OF_GAME)
                                {
                        player.frags = FRAGS_PLAYER_OUT_OF_GAME;
                        TRANSMUTE(Observer, player);
                }
 +              if (autocvar_g_lms_leader_lives_diff > 0)
 +                      lms_UpdateLeaders();
        }
  
        if (CS(player).killcount != FRAGS_SPECTATOR && player.lms_spectate_warning < 3)
@@@ -380,7 -333,7 +403,7 @@@ MUTATOR_HOOKFUNCTION(lms, ClientDisconn
        player.lms_spectate_warning = 3;
  
        lms_RemovePlayer(player);
-       player.lmsplayer = 0;
+       INGAME_STATUS_CLEAR(player);
  }
  
  MUTATOR_HOOKFUNCTION(lms, MakePlayerObserver)
                GameRules_scoring_add(player, LMS_LIVES, -GameRules_scoring_add(player, LMS_LIVES, 0));
                player.frags = FRAGS_SPECTATOR;
                TRANSMUTE(Observer, player);
-               player.lmsplayer = 0;
+               INGAME_STATUS_CLEAR(player);
        }
        else
        {
@@@ -418,150 -371,25 +441,152 @@@ MUTATOR_HOOKFUNCTION(lms, PlayerPreThin
  {
        entity player = M_ARGV(0, entity);
  
 +      // recycled REDALIVE and BLUEALIVE to avoid adding a dedicated stat
 +      STAT(REDALIVE, player) = lms_leaders;
 +      STAT(BLUEALIVE, player) = lms_leaders_lives_diff;
 +
        if(player.deadflag == DEAD_DYING)
                player.deadflag = DEAD_RESPAWNING;
  }
  
 +MUTATOR_HOOKFUNCTION(lms, SV_StartFrame)
 +{
 +      float leader_time = autocvar_g_lms_leader_wp_time;
 +      float leader_interval = leader_time + autocvar_g_lms_leader_wp_interval;
 +      lms_visible_leaders_prev = lms_visible_leaders;
 +      lms_visible_leaders = (time > lms_visible_leaders_time && time < lms_visible_leaders_time + leader_time);
 +      if (lms_visible_leaders_prev && !lms_visible_leaders)
 +              lms_visible_leaders_time = time + leader_interval + random() * autocvar_g_lms_leader_wp_interval_jitter;
 +
 +      lms_leaders = 0;
 +      FOREACH_CLIENT(true, {
 +              STAT(OBJECTIVE_STATUS, it) = lms_visible_leaders;
 +              if (IS_PLAYER(it) && it.frags != FRAGS_PLAYER_OUT_OF_GAME)
 +              {
 +                      if (it.lms_leader)
 +                      {
 +                              if (!it.waypointsprite_attachedforcarrier)
 +                              {
 +                                      WaypointSprite_AttachCarrier(WP_LmsLeader, it, RADARICON_FLAGCARRIER);
 +                                      it.waypointsprite_attachedforcarrier.waypointsprite_visible_for_player = lms_waypointsprite_visible_for_player;
 +                                      WaypointSprite_UpdateRule(it.waypointsprite_attachedforcarrier, 0, SPRITERULE_DEFAULT);
 +                                      vector pl_color = colormapPaletteColor(it.clientcolors & 0x0F, false);
 +                                      WaypointSprite_UpdateTeamRadar(it.waypointsprite_attachedforcarrier, RADARICON_FLAGCARRIER, pl_color);
 +                                      WaypointSprite_Ping(it.waypointsprite_attachedforcarrier);
 +                              }
 +                              if (!lms_visible_leaders_prev && lms_visible_leaders && IS_REAL_CLIENT(it))
 +                                      Send_Notification(NOTIF_ONE, it, MSG_CENTER, CENTER_LMS_VISIBLE_LEADER);
 +                              lms_leaders++;
 +                      }
 +                      else // if (!it.lms_leader)
 +                      {
 +                              if (IS_PLAYER(it) && it.frags != FRAGS_PLAYER_OUT_OF_GAME)
 +                              {
 +                                      if (!lms_visible_leaders_prev && lms_visible_leaders && IS_REAL_CLIENT(it))
 +                                              Send_Notification(NOTIF_ONE, it, MSG_CENTER, CENTER_LMS_VISIBLE_OTHER);
 +                              }
 +                              if (it.waypointsprite_attachedforcarrier)
 +                                      WaypointSprite_Kill(it.waypointsprite_attachedforcarrier);
 +                      }
 +              }
 +      });
 +}
 +
  MUTATOR_HOOKFUNCTION(lms, PlayerRegen)
  {
-       if(autocvar_g_lms_regenerate)
-               return false;
-       return true;
+       if(!autocvar_g_lms_regenerate)
+               M_ARGV(2, float) = 0;
+       if(!autocvar_g_lms_rot)
+               M_ARGV(3, float) = 0;
+       return (!autocvar_g_lms_regenerate && !autocvar_g_lms_rot);
  }
  
 +MUTATOR_HOOKFUNCTION(lms, PlayerPowerups)
 +{
 +      entity player = M_ARGV(0, entity);
 +      if (player.waypointsprite_attachedforcarrier)
 +              player.effects |= (EF_ADDITIVE | EF_FULLBRIGHT);
 +      else
 +              player.effects &= ~(EF_ADDITIVE | EF_FULLBRIGHT);
 +}
 +
  MUTATOR_HOOKFUNCTION(lms, ForbidThrowCurrentWeapon)
  {
        // forbode!
        return true;
  }
  
 +MUTATOR_HOOKFUNCTION(lms, Damage_Calculate)
 +{
 +      if (!autocvar_g_lms_dynamic_vampire)
 +              return;
 +
 +      entity frag_attacker = M_ARGV(1, entity);
 +      entity frag_target = M_ARGV(2, entity);
 +      float frag_damage = M_ARGV(4, float);
 +
 +      if (IS_PLAYER(frag_attacker) && !IS_DEAD(frag_attacker)
 +              && IS_PLAYER(frag_target) && !IS_DEAD(frag_target) && frag_attacker != frag_target)
 +      {
 +              float vampire_factor = 0;
 +
 +              int frag_attacker_lives = GameRules_scoring_add(frag_attacker, LMS_LIVES, 0);
 +              int frag_target_lives = GameRules_scoring_add(frag_target, LMS_LIVES, 0);
 +              int diff = frag_target_lives - frag_attacker_lives - autocvar_g_lms_dynamic_vampire_min_lives_diff;
 +
 +              if (diff >= 0)
 +                      vampire_factor = autocvar_g_lms_dynamic_vampire_factor_base + diff * autocvar_g_lms_dynamic_vampire_factor_increase;
 +              if (vampire_factor > 0)
 +              {
 +                      vampire_factor = min(vampire_factor, autocvar_g_lms_dynamic_vampire_factor_max);
 +                      SetResourceExplicit(frag_attacker, RES_HEALTH,
 +                              min(GetResource(frag_attacker, RES_HEALTH) + frag_damage * vampire_factor, start_health));
 +              }
 +      }
 +}
 +
 +MUTATOR_HOOKFUNCTION(lms, PlayerDied)
 +{
 +      if (!warmup_stage && autocvar_g_lms_leader_lives_diff > 0)
 +              lms_UpdateLeaders();
 +}
 +
 +MUTATOR_HOOKFUNCTION(lms, CalculateRespawnTime)
 +{
 +      entity player = M_ARGV(0, entity);
 +      player.respawn_flags |= RESPAWN_FORCE;
 +
 +      int pl_lives = GameRules_scoring_add(player, LMS_LIVES, 0);
 +      if (pl_lives <= 0)
 +      {
 +              player.respawn_flags = RESPAWN_SILENT;
 +              // prevent unwanted sudden rejoin as spectator and movement of spectator camera
 +              player.respawn_time = time + 2;
 +              return true;
 +      }
 +
 +      if (autocvar_g_lms_dynamic_respawn_delay <= 0)
 +              return false;
 +
 +      int max_lives = 0;
 +      int pl_cnt = 0;
 +      FOREACH_CLIENT(it != player && IS_PLAYER(it) && it.frags != FRAGS_PLAYER_OUT_OF_GAME, {
 +              int lives = GameRules_scoring_add(it, LMS_LIVES, 0);
 +              if (lives > max_lives)
 +                      max_lives = lives;
 +              pl_cnt++;
 +      });
 +
 +      // min delay with only 2 players
 +      if (pl_cnt == 1) // player wasn't counted
 +              max_lives = 0;
 +
 +      float dlay = autocvar_g_lms_dynamic_respawn_delay_base +
 +              autocvar_g_lms_dynamic_respawn_delay_increase * max(0, max_lives - pl_lives);
 +      player.respawn_time = time + min(autocvar_g_lms_dynamic_respawn_delay_max, dlay);
 +      return true;
 +}
 +
  MUTATOR_HOOKFUNCTION(lms, GiveFragsForKill)
  {
        entity frag_target = M_ARGV(1, entity);
@@@ -611,13 -439,16 +636,16 @@@ MUTATOR_HOOKFUNCTION(lms, ForbidPlayerS
  
  MUTATOR_HOOKFUNCTION(lms, FilterItemDefinition)
  {
+       if (autocvar_g_lms_items)
+               return false;
        entity definition = M_ARGV(0, entity);
  
        if (autocvar_g_lms_extra_lives && definition == ITEM_ExtraLife)
        {
                return false;
        }
-       return true;
+       return (autocvar_g_pickup_items <= 0); // only allow items if explicitly enabled
  }
  
  void lms_extralife(entity this)
@@@ -667,7 -498,7 +695,7 @@@ MUTATOR_HOOKFUNCTION(lms, ItemTouch
  MUTATOR_HOOKFUNCTION(lms, Bot_FixCount, CBC_ORDER_EXCLUSIVE)
  {
        FOREACH_CLIENT(IS_REAL_CLIENT(it), {
-               if (it.lmsplayer && it.lms_spectate_warning < 2)
+               if (INGAME(it) && it.lms_spectate_warning < 2)
                        ++M_ARGV(0, int); // activerealplayers
                ++M_ARGV(1, int); // realplayers
        });
@@@ -689,7 -520,8 +717,8 @@@ MUTATOR_HOOKFUNCTION(lms, ClientCommand
                if(player.frags != FRAGS_SPECTATOR && player.frags != FRAGS_PLAYER_OUT_OF_GAME)
                {
                        player.lms_spectate_warning = 1;
-                       sprint(player, "WARNING: you won't be able to enter the game again after spectating in LMS. Use the same command again to spectate anyway.\n");
+                       sprint(player, "^1WARNING:^7 you can't rejoin this match after spectating. Use the same command again to spectate anyway.\n");
+                       Send_Notification(NOTIF_ONE_ONLY, player, MSG_CENTER, CENTER_LMS_SPECWARN);
                }
                return MUT_SPECCMD_RETURN;
        }
@@@ -708,13 -540,6 +737,6 @@@ MUTATOR_HOOKFUNCTION(lms, SetWeaponAren
                M_ARGV(0, string) = autocvar_g_lms_weaponarena;
  }
  
- MUTATOR_HOOKFUNCTION(lms, GetPlayerStatus)
- {
-       entity player = M_ARGV(0, entity);
-       return boolean(player.lmsplayer);
- }
  MUTATOR_HOOKFUNCTION(lms, AddPlayerScore)
  {
        if(game_stopped)
index 382fd367dee48a1e70ffed26ca7aae14d8217db5,92b4c8f16f8358ab64947820cf73596cce5fff26..69483bfc3c075b19427b39d1eb808c7b4d857075
@@@ -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(WEAPON_ACCORDEON_SUICIDE,                N_CONSOLE,  2, 1, "s1 s2loc spree_lost", "s1",                      "weapontuba",               _("^BG%s^K1 hurt their own ears with the @!#%%'n Accordeon%s%s"), "")
      MSG_INFO_NOTIF(WEAPON_ARC_MURDER,                       N_CONSOLE,  3, 2, "spree_inf s1 s2 s3loc spree_end", "s2 s1",       "weaponarc",                _("^BG%s%s^K1 was electrocuted by ^BG%s^K1's Arc%s%s"), "")
      MSG_INFO_NOTIF(WEAPON_ARC_MURDER_SPRAY,                 N_CONSOLE,  3, 2, "spree_inf s1 s2 s3loc spree_end", "s2 s1",       "weaponarc",                _("^BG%s%s^K1 was blasted by ^BG%s^K1's Arc bolts%s%s"), "")
+     MSG_INFO_NOTIF(WEAPON_ARC_SUICIDE_BOLT,                 N_CONSOLE,  2, 1, "s1 s2loc spree_lost", "s1",                      "weaponarc",                _("^BG%s^K1 played with Arc bolts%s%s"), "")
      MSG_INFO_NOTIF(WEAPON_BLASTER_MURDER,                   N_CONSOLE,  3, 2, "spree_inf s1 s2 s3loc spree_end", "s2 s1",       "weaponlaser",              _("^BG%s%s^K1 was shot to death by ^BG%s^K1's Blaster%s%s"), "")
      MSG_INFO_NOTIF(WEAPON_BLASTER_SUICIDE,                  N_CONSOLE,  2, 1, "s1 s2loc spree_lost", "s1",                      "weaponlaser",              _("^BG%s^K1 shot themself to hell with their Blaster%s%s"), "")
      MSG_INFO_NOTIF(WEAPON_CRYLINK_MURDER,                   N_CONSOLE,  3, 2, "spree_inf s1 s2 s3loc spree_end", "s2 s1",       "weaponcrylink",            _("^BG%s%s^K1 felt the strong pull of ^BG%s^K1's Crylink%s%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"), "")
      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(LMS_VISIBLE_LEADER,                N_ENABLE,    0, 0, "",               CPID_LMS,               "0 0",  _("^BGEnemies can now see you on radar!"), "")
 +    MSG_CENTER_NOTIF(LMS_VISIBLE_OTHER,                 N_ENABLE,    0, 0, "",               CPID_LMS,               "0 0",  _("^BGLeaders can now be seen by enemies on radar!"), "")
  
      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_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)
      MSG_MULTI_NOTIF(WEAPON_ACCORDEON_SUICIDE,           N_ENABLE,  NULL,           INFO_WEAPON_ACCORDEON_SUICIDE,          CENTER_DEATH_SELF_GENERIC)
      MSG_MULTI_NOTIF(WEAPON_ARC_MURDER,                  N_ENABLE,  NULL,           INFO_WEAPON_ARC_MURDER,                 NULL)
      MSG_MULTI_NOTIF(WEAPON_ARC_MURDER_SPRAY,            N_ENABLE,  NULL,           INFO_WEAPON_ARC_MURDER_SPRAY,           NULL)
+     MSG_MULTI_NOTIF(WEAPON_ARC_SUICIDE_BOLT,            N_ENABLE,  NULL,           INFO_WEAPON_ARC_SUICIDE_BOLT,           CENTER_DEATH_SELF_GENERIC)
      MSG_MULTI_NOTIF(WEAPON_BLASTER_MURDER,              N_ENABLE,  NULL,           INFO_WEAPON_BLASTER_MURDER,             NULL)
      MSG_MULTI_NOTIF(WEAPON_BLASTER_SUICIDE,             N_ENABLE,  NULL,           INFO_WEAPON_BLASTER_SUICIDE,            CENTER_DEATH_SELF_GENERIC)
      MSG_MULTI_NOTIF(WEAPON_CRYLINK_MURDER,              N_ENABLE,  NULL,           INFO_WEAPON_CRYLINK_MURDER,             NULL)