Merge branch 'master' into Mario/notifications
authorMario <zacjardine@y7mail.com>
Sat, 20 Dec 2014 04:48:17 +0000 (15:48 +1100)
committerMario <zacjardine@y7mail.com>
Sat, 20 Dec 2014 04:48:17 +0000 (15:48 +1100)
Conflicts:
qcsrc/common/notifications.qh
qcsrc/server/command/sv_cmd.qc
qcsrc/server/mutators/gamemode_assault.qc
qcsrc/server/teamplay.qc

14 files changed:
1  2 
qcsrc/common/notifications.qh
qcsrc/common/weapons/w_minelayer.qc
qcsrc/common/weapons/w_porto.qc
qcsrc/server/cl_client.qc
qcsrc/server/cl_physics.qc
qcsrc/server/command/cmd.qc
qcsrc/server/command/sv_cmd.qc
qcsrc/server/g_triggers.qc
qcsrc/server/item_key.qc
qcsrc/server/mutators/gamemode_assault.qc
qcsrc/server/mutators/gamemode_onslaught.qc
qcsrc/server/mutators/mutator_superspec.qc
qcsrc/server/t_items.qc
qcsrc/server/t_plats.qc

@@@ -339,9 -339,7 +339,12 @@@ void Send_Notification_WOCOVA
      MULTITEAM_INFO##teams(default,prefix,strnum,flnum,args,hudargs,icon,normal,gentle)
  
  #define MSG_INFO_NOTIFICATIONS \
+     MSG_INFO_NOTIF(2, INFO_CHAT_NOSPECTATORS,              0, 0, "", "",                            "",                     _("^F4NOTE: ^BGSpectator chat is not sent to players during the match"), "") \
 +    MSG_INFO_NOTIF(2, INFO_COINTOSS,                       1, 0, "s1", "",                          "",                     _("^F2Throwing coin... Result: %s^F2!"), "") \
 +    MSG_INFO_NOTIF(1, INFO_JETPACK_NOFUEL,                 0, 0, "", "",                            "",                     _("^BGYou don't have any fuel for the ^F1Jetpack"), "") \
 +    MSG_INFO_NOTIF(2, INFO_SUPERSPEC_MISSING_UID,          0, 0, "", "",                            "",                     _("^F2You lack a UID, superspec options will not be saved/restored"), "") \
++    MSG_INFO_NOTIF(1, INFO_CA_JOIN_LATE,                   0, 0, "", "",                            "",                     _("^F1Round already started, you will join the game in the next round"), "") \
++    MSG_INFO_NOTIF(1, INFO_CA_LEAVE,                       0, 0, "", "",                            "",                     _("^F2You will spectate in the next round"), "") \
      MULTITEAM_INFO(1, INFO_CTF_CAPTURE_, 2,                1, 0, "s1", "s1",                        "notify_%s_captured",   _("^BG%s^BG captured the ^TC^TT^BG flag"), "") \
      MULTITEAM_INFO(1, INFO_CTF_CAPTURE_BROKEN_, 2,         2, 2, "s1 f1p2dec s2 f2p2dec", "s1",     "notify_%s_captured",   _("^BG%s^BG captured the ^TC^TT^BG flag in ^F1%s^BG seconds, breaking ^BG%s^BG's previous record of ^F2%s^BG seconds"), "") \
      MULTITEAM_INFO(1, INFO_CTF_CAPTURE_TIME_, 2,           1, 1, "s1 f1p2dec", "s1",                "notify_%s_captured",   _("^BG%s^BG captured the ^TC^TT^BG flag in ^F1%s^BG seconds"), "") \
      MSG_INFO_NOTIF(1, INFO_DEATH_SELF_VH_WAKI_ROCKET,      2, 1, "s1 s2loc spree_lost", "s1",       "notify_death",         _("^BG%s^K1 couldn't find shelter from a Racer rocket%s%s"), "") \
      MSG_INFO_NOTIF(1, INFO_DEATH_SELF_VOID,                2, 1, "s1 s2loc spree_lost", "s1",       "notify_void",          _("^BG%s^K1 was in the wrong place%s%s"), "") \
      MULTITEAM_INFO(1, INFO_DEATH_TEAMKILL_, 4,             3, 1, "s1 s2 s3loc spree_end", "s2 s1",  "notify_teamkill_%s",   _("^BG%s^K1 was betrayed by ^BG%s^K1%s%s"), "") \
 -    MSG_INFO_NOTIF(1, INFO_CA_JOIN_LATE,                   0, 0, "", "",                            "",                     _("^F1Round already started, you will join the game in the next round"), "") \
 -    MSG_INFO_NOTIF(1, INFO_CA_LEAVE,                       0, 0, "", "",                            "",                     _("^F2You will spectate in the next round"), "") \
+     MSG_INFO_NOTIF(1, INFO_DOMINATION_CAPTURE_TIME,        2, 2, "s1 s2 f1 f2", "",                 "",                     _("^BG%s^BG%s^BG (%s points every %s seconds)"), "") \
      MSG_INFO_NOTIF(1, INFO_FREEZETAG_FREEZE,               2, 0, "s1 s2", "",                       "",                     _("^BG%s^K1 was frozen by ^BG%s"), "") \
      MSG_INFO_NOTIF(1, INFO_FREEZETAG_REVIVED,              2, 0, "s1 s2", "",                       "",                     _("^BG%s^K3 was revived by ^BG%s"), "") \
      MSG_INFO_NOTIF(1, INFO_FREEZETAG_REVIVED_FALL,         1, 0, "s1", "",                          "",                     _("^BG%s^K3 was revived by falling"), "") \
      MSG_INFO_NOTIF(1, INFO_WEAPON_HOOK_MURDER,                   3, 2, "spree_inf s1 s2 s3loc spree_end", "s2 s1",  "weaponhook",             _("^BG%s%s^K1 was caught in ^BG%s^K1's Hook gravity bomb%s%s"), "") \
      MSG_INFO_NOTIF(1, INFO_WEAPON_KLEINBOTTLE_MURDER,            3, 2, "spree_inf s1 s2 s3loc spree_end", "s2 s1",  "weapontuba",             _("^BG%s%s^K1 died of ^BG%s^K1's great playing on the @!#%%'n Klein Bottle%s%s"), "") \
      MSG_INFO_NOTIF(1, INFO_WEAPON_KLEINBOTTLE_SUICIDE,           2, 1, "s1 s2loc spree_lost", "s1",                 "weapontuba",             _("^BG%s^K1 hurt their own ears with the @!#%%'n Klein Bottle%s%s"), "") \
-     MSG_INFO_NOTIF(1, INFO_WEAPON_LASER_MURDER,                  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 Laser%s%s"), "") \
-     MSG_INFO_NOTIF(1, INFO_WEAPON_LASER_SUICIDE,                 2, 1, "s1 s2loc spree_lost", "s1",                 "weaponlaser",            _("^BG%s^K1 shot themself to hell with their Laser%s%s"), "") \
+     MSG_INFO_NOTIF(1, INFO_WEAPON_MACHINEGUN_MURDER_SNIPE,       3, 2, "spree_inf s1 s2 s3loc spree_end", "s2 s1",  "weaponuzi",              _("^BG%s%s^K1 was sniped by ^BG%s^K1's Machine Gun%s%s"), "") \
+     MSG_INFO_NOTIF(1, INFO_WEAPON_MACHINEGUN_MURDER_SPRAY,       3, 2, "spree_inf s1 s2 s3loc spree_end", "s2 s1",  "weaponuzi",              _("^BG%s%s^K1 was riddled full of holes by ^BG%s^K1's Machine Gun%s%s"), "") \
++    MSG_INFO_NOTIF(1, INFO_WEAPON_MINELAYER_LIMIT,               0, 1, "f1", "",                                    "",                       _("^BGYou cannot place more than ^F2%s^BG mines at a time"), "") \
      MSG_INFO_NOTIF(1, INFO_WEAPON_MINELAYER_MURDER,              3, 2, "spree_inf s1 s2 s3loc spree_end", "s2 s1",  "weaponminelayer",        _("^BG%s%s^K1 got too close to ^BG%s^K1's mine%s%s"), "") \
      MSG_INFO_NOTIF(1, INFO_WEAPON_MINELAYER_SUICIDE,             2, 1, "s1 s2loc spree_lost", "s1",                 "weaponminelayer",        _("^BG%s^K1 forgot about their mine%s%s"), "") \
-     MSG_INFO_NOTIF(1, INFO_WEAPON_MINSTANEX_MURDER,              3, 2, "spree_inf s1 s2 s3loc spree_end", "s2 s1",  "weaponminstanex",        _("^BG%s%s^K1 has been vaporized by ^BG%s^K1's Minstanex%s%s"), "") \
      MSG_INFO_NOTIF(1, INFO_WEAPON_MORTAR_MURDER_BOUNCE,          3, 2, "spree_inf s1 s2 s3loc spree_end", "s2 s1",  "weapongrenadelauncher",  _("^BG%s%s^K1 got too close to ^BG%s^K1's Mortar grenade%s%s"), "") \
      MSG_INFO_NOTIF(1, INFO_WEAPON_MORTAR_MURDER_EXPLODE,         3, 2, "spree_inf s1 s2 s3loc spree_end", "s2 s1",  "weapongrenadelauncher",  _("^BG%s%s^K1 ate ^BG%s^K1's Mortar grenade%s%s"), "") \
      MSG_INFO_NOTIF(1, INFO_WEAPON_MORTAR_SUICIDE_BOUNCE,         2, 1, "s1 s2loc spree_lost", "s1",                 "weapongrenadelauncher",  _("^BG%s^K1 didn't see their own Mortar grenade%s%s"), "") \
      MSG_CENTER_NOTIF(1, CENTER_KEYHUNT_ROUNDSTART,          0, 1, "",              CPID_KEYHUNT_OTHER,    "1 f1", _("^F4Round will start in ^COUNT"), "") \
      MSG_CENTER_NOTIF(1, CENTER_KEYHUNT_SCAN,                0, 1, "",              CPID_KEYHUNT_OTHER,    "f1 0", _("^BGScanning frequency range..."), "") \
      MULTITEAM_CENTER(1, CENTER_KEYHUNT_START_, 4,           0, 0, "",              CPID_KEYHUNT,          "0 0", _("^BGYou are starting with the ^TC^TT Key"), "") \
-     MSG_CENTER_NOTIF(1, CENTER_KEYHUNT_WAIT,                0, 4, "missing_teams", CPID_KEYHUNT_OTHER,    "0 0", _("^BGWaiting for players to join...\nNeed active players for: %s"), "") \
-     MSG_CENTER_NOTIF(1, CENTER_MISSING_TEAMS,               0, 4, "missing_teams", CPID_MISSING_TEAMS,    "-1 0", _("^BGWaiting for players to join...\nNeed active players for: %s"), "") \
+     MSG_CENTER_NOTIF(1, CENTER_KEYHUNT_WAIT,                0, 1, "missing_teams", CPID_KEYHUNT_OTHER,    "0 0", _("^BGWaiting for players to join...\nNeed active players for: %s"), "") \
++    MSG_CENTER_NOTIF(1, CENTER_LMS_NOLIVES,                 0, 0, "",              CPID_LMS,              "0 0", _("^BGYou have no lives left, you must wait until the next match"), "") \
+     MSG_CENTER_NOTIF(1, CENTER_MISSING_TEAMS,               0, 1, "missing_teams", CPID_MISSING_TEAMS,    "-1 0", _("^BGWaiting for players to join...\nNeed active players for: %s"), "") \
      MSG_CENTER_NOTIF(1, CENTER_MISSING_PLAYERS,             0, 1, "f1",            CPID_MISSING_PLAYERS,  "-1 0", _("^BGWaiting for %s player(s) to join..."), "") \
-     MSG_CENTER_NOTIF(1, CENTER_MINSTA_FINDAMMO,             0, 0, "",              CPID_MINSTA_FINDAMMO,  "1 9", _("^F4^COUNT^BG left to find some ammo!"), "") \
-     MSG_CENTER_NOTIF(1, CENTER_MINSTA_FINDAMMO_FIRST,       0, 0, "",              CPID_MINSTA_FINDAMMO,  "1 10", _("^BGGet some ammo or you'll be dead in ^F4^COUNT^BG!"), _("^BGGet some ammo! ^F4^COUNT^BG left!")) \
-     MSG_CENTER_NOTIF(1, CENTER_MINSTA_LIVES_REMAINING,      0, 1, "f1",            NO_CPID,               "0 0", _("^F2Extra lives remaining: ^K1%s"), "") \
-     MSG_CENTER_NOTIF(1, CENTER_MINSTA_SECONDARY,            0, 0, "",              NO_CPID,               "0 0", _("^BGSecondary fire inflicts no damage!"), "") \
-     MSG_CENTER_NOTIF(1, CENTER_MOTD,                        1, 0, "s1",            CPID_MOTD,             "-1 0", _("^BG%s"), "") \
+     MSG_CENTER_NOTIF(1, CENTER_INSTAGIB_FINDAMMO,           0, 0, "",              CPID_INSTAGIB_FINDAMMO,"1 9", _("^F4^COUNT^BG left to find some ammo!"), "") \
+     MSG_CENTER_NOTIF(1, CENTER_INSTAGIB_FINDAMMO_FIRST,     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(1, CENTER_INSTAGIB_LIVES_REMAINING,    0, 1, "f1",            NO_CPID,               "0 0", _("^F2Extra lives remaining: ^K1%s"), "") \
+     MSG_CENTER_NOTIF(1, CENTER_MOTD,                        1, 0, "s1",            CPID_MOTD,             "-1 0", "^BG%s", "") \
      MSG_CENTER_NOTIF(1, CENTER_NIX_COUNTDOWN,               0, 2, "item_wepname",  CPID_NIX,              "1 f2", _("^F2^COUNT^BG until weapon change...\nNext weapon: ^F1%s"), "") \
      MSG_CENTER_NOTIF(1, CENTER_NIX_NEWWEAPON,               0, 1, "item_wepname",  CPID_NIX,              "0 0", _("^F2Active weapon: ^F1%s"), "") \
      MSG_CENTER_NOTIF(1, CENTER_NADE,                        0, 0, "",              NO_CPID,               "0 0", _("^BGPress ^F2DROPWEAPON^BG again to toss the grenade!"), "") \
 +    MSG_CENTER_NOTIF(1, CENTER_ONS_NOTSHIELDED,             0, 0, "",              CPID_ONSLAUGHT,        "0 0", _("^K1Your generator is NOT shielded!\n^BGRe-capture controlpoints to shield it!"), "") \
      MSG_CENTER_NOTIF(1, CENTER_OVERTIME_FRAG,               0, 0, "",              CPID_OVERTIME,         "0 0", _("^F2Now playing ^F4OVERTIME^F2!\nKeep fragging until we have a winner!"), _("^F2Now playing ^F4OVERTIME^F2!\nKeep scoring until we have a winner!")) \
-     MSG_CENTER_NOTIF(1, CENTER_OVERTIME_CONTROLPOINT,       0, 0, "",              CPID_OVERTIME,         "3 0", _("^F2Now playing ^F4OVERTIME^F2!\n\nGenerators start now to decay.\nThe more control points your team holds,\nthe faster the enemy generator decays"), "") \
++    MSG_CENTER_NOTIF(1, CENTER_OVERTIME_CONTROLPOINT,       0, 0, "",              CPID_OVERTIME,         "5 0", _("^F2Now playing ^F4OVERTIME^F2!\n\nGenerators are now decaying.\nThe more control points your team holds,\nthe faster the enemy generator decays"), "") \
      MSG_CENTER_NOTIF(1, CENTER_OVERTIME_TIME,               0, 1, "f1time",        CPID_OVERTIME,         "0 0", _("^F2Now playing ^F4OVERTIME^F2!\n^BGAdded ^F4%s^BG to the game!"), "") \
-     MSG_CENTER_NOTIF(1, CENTER_PORTO_FAILED,                0, 0, "",              NO_CPID,               "0 0", _("^K1Portal deployment failed.\n\n^F2Catch it to try again!"), "") \
 +    MSG_CENTER_NOTIF(1, CENTER_PORTO_CREATED_IN,            0, 0, "",              NO_CPID,               "0 0", _("^K1In^BG-portal created"), "") \
 +    MSG_CENTER_NOTIF(1, CENTER_PORTO_CREATED_OUT,           0, 0, "",              NO_CPID,               "0 0", _("^F3Out^BG-portal created"), "") \
++    MSG_CENTER_NOTIF(1, CENTER_PORTO_FAILED,                0, 0, "",              NO_CPID,               "0 0", _("^K1Portal deployment failed.\n\n^F2Catch it to try again!"), "") \
      MSG_CENTER_NOTIF(1, CENTER_POWERDOWN_INVISIBILITY,      0, 0, "",              CPID_POWERUP,          "0 0", _("^F2Invisibility has worn off"), "") \
      MSG_CENTER_NOTIF(1, CENTER_POWERDOWN_SHIELD,            0, 0, "",              CPID_POWERUP,          "0 0", _("^F2Shield has worn off"), "") \
      MSG_CENTER_NOTIF(1, CENTER_POWERDOWN_SPEED,             0, 0, "",              CPID_POWERUP,          "0 0", _("^F2Speed has worn off"), "") \
      MSG_CENTER_NOTIF(1, CENTER_TEAMCHANGE_SPECTATE,         0, 1, "",              CPID_TEAMCHANGE,       "1 f1", _("^K1Spectating in ^COUNT"), "") \
      MSG_CENTER_NOTIF(1, CENTER_TEAMCHANGE_SUICIDE,          0, 1, "",              CPID_TEAMCHANGE,       "1 f1", _("^K1Suicide in ^COUNT"), "") \
      MSG_CENTER_NOTIF(1, CENTER_TIMEOUT_BEGINNING,           0, 1, "",              CPID_TIMEOUT,          "1 f1", _("^F4Timeout begins in ^COUNT"), "") \
-     MSG_CENTER_NOTIF(1, CENTER_TIMEOUT_ENDING,              0, 1, "",              CPID_TIMEOUT,          "1 f1", _("^F4Timeout ends in ^COUNT"), "") 
 -    MSG_CENTER_NOTIF(1, CENTER_TIMEOUT_ENDING,              0, 1, "",              CPID_TIMEOUT,          "1 f1", _("^F4Timeout ends in ^COUNT"), "")
++    MSG_CENTER_NOTIF(1, CENTER_TIMEOUT_ENDING,              0, 1, "",              CPID_TIMEOUT,          "1 f1", _("^F4Timeout ends in ^COUNT"), "") \
++    MSG_CENTER_NOTIF(1, CENTER_WEAPON_MINELAYER_LIMIT,      0, 1, "f1",            NO_CPID,               "0 0",  _("^BGYou cannot place more than ^F2%s^BG mines at a time"), "") 
  
  #define MULTITEAM_MULTI2(default,prefix,anncepre,infopre,centerpre) \
      MSG_MULTI_NOTIF(default, prefix##RED, anncepre##RED, infopre##RED, centerpre##RED) \
      MSG_MULTI_NOTIF(1, ITEM_WEAPON_NOAMMO,                   NO_MSG,        INFO_ITEM_WEAPON_NOAMMO,                   CENTER_ITEM_WEAPON_NOAMMO) \
      MSG_MULTI_NOTIF(1, ITEM_WEAPON_PRIMORSEC,                NO_MSG,        INFO_ITEM_WEAPON_PRIMORSEC,                CENTER_ITEM_WEAPON_PRIMORSEC) \
      MSG_MULTI_NOTIF(1, ITEM_WEAPON_UNAVAILABLE,              NO_MSG,        INFO_ITEM_WEAPON_UNAVAILABLE,              CENTER_ITEM_WEAPON_UNAVAILABLE) \
 +    MSG_MULTI_NOTIF(1, MULTI_COINTOSS,                       NO_MSG,        INFO_COINTOSS,                             CENTER_COINTOSS) \
      MSG_MULTI_NOTIF(1, MULTI_COUNTDOWN_BEGIN,                ANNCE_BEGIN,   NO_MSG,                                    CENTER_COUNTDOWN_BEGIN) \
-     MSG_MULTI_NOTIF(1, MULTI_MINSTA_FINDAMMO,                ANNCE_NUM_10,  NO_MSG,                                    CENTER_MINSTA_FINDAMMO_FIRST) \
+     MSG_MULTI_NOTIF(1, MULTI_INSTAGIB_FINDAMMO,              ANNCE_NUM_10,  NO_MSG,                                    CENTER_INSTAGIB_FINDAMMO_FIRST) \
      MSG_MULTI_NOTIF(1, WEAPON_ACCORDEON_MURDER,              NO_MSG,        INFO_WEAPON_ACCORDEON_MURDER,              NO_MSG) \
      MSG_MULTI_NOTIF(1, WEAPON_ACCORDEON_SUICIDE,             NO_MSG,        INFO_WEAPON_ACCORDEON_SUICIDE,             CENTER_DEATH_SELF_GENERIC) \
+     MSG_MULTI_NOTIF(1, WEAPON_ARC_MURDER,                    NO_MSG,        INFO_WEAPON_ARC_MURDER,                    NO_MSG) \
+     MSG_MULTI_NOTIF(1, WEAPON_BLASTER_MURDER,                NO_MSG,        INFO_WEAPON_BLASTER_MURDER,                NO_MSG) \
+     MSG_MULTI_NOTIF(1, WEAPON_BLASTER_SUICIDE,               NO_MSG,        INFO_WEAPON_BLASTER_SUICIDE,               CENTER_DEATH_SELF_GENERIC) \
      MSG_MULTI_NOTIF(1, WEAPON_CRYLINK_MURDER,                NO_MSG,        INFO_WEAPON_CRYLINK_MURDER,                NO_MSG) \
      MSG_MULTI_NOTIF(1, WEAPON_CRYLINK_SUICIDE,               NO_MSG,        INFO_WEAPON_CRYLINK_SUICIDE,               CENTER_DEATH_SELF_GENERIC) \
+     MSG_MULTI_NOTIF(1, WEAPON_DEVASTATOR_MURDER_DIRECT,      NO_MSG,        INFO_WEAPON_DEVASTATOR_MURDER_DIRECT,      NO_MSG) \
+     MSG_MULTI_NOTIF(1, WEAPON_DEVASTATOR_MURDER_SPLASH,      NO_MSG,        INFO_WEAPON_DEVASTATOR_MURDER_SPLASH,      NO_MSG) \
+     MSG_MULTI_NOTIF(1, WEAPON_DEVASTATOR_SUICIDE,            NO_MSG,        INFO_WEAPON_DEVASTATOR_SUICIDE,            CENTER_DEATH_SELF_GENERIC) \
      MSG_MULTI_NOTIF(1, WEAPON_ELECTRO_MURDER_BOLT,           NO_MSG,        INFO_WEAPON_ELECTRO_MURDER_BOLT,           NO_MSG) \
      MSG_MULTI_NOTIF(1, WEAPON_ELECTRO_MURDER_COMBO,          NO_MSG,        INFO_WEAPON_ELECTRO_MURDER_COMBO,          NO_MSG) \
      MSG_MULTI_NOTIF(1, WEAPON_ELECTRO_MURDER_ORBS,           NO_MSG,        INFO_WEAPON_ELECTRO_MURDER_ORBS,           NO_MSG) \
      MSG_MULTI_NOTIF(1, WEAPON_HOOK_MURDER,                   NO_MSG,        INFO_WEAPON_HOOK_MURDER,                   NO_MSG) \
      MSG_MULTI_NOTIF(1, WEAPON_KLEINBOTTLE_MURDER,            NO_MSG,        INFO_WEAPON_KLEINBOTTLE_MURDER,            NO_MSG) \
      MSG_MULTI_NOTIF(1, WEAPON_KLEINBOTTLE_SUICIDE,           NO_MSG,        INFO_WEAPON_KLEINBOTTLE_SUICIDE,           CENTER_DEATH_SELF_GENERIC) \
-     MSG_MULTI_NOTIF(1, WEAPON_LASER_MURDER,                  NO_MSG,        INFO_WEAPON_LASER_MURDER,                  NO_MSG) \
-     MSG_MULTI_NOTIF(1, WEAPON_LASER_SUICIDE,                 NO_MSG,        INFO_WEAPON_LASER_SUICIDE,                 CENTER_DEATH_SELF_GENERIC) \
+     MSG_MULTI_NOTIF(1, WEAPON_MACHINEGUN_MURDER_SNIPE,       NO_MSG,        INFO_WEAPON_MACHINEGUN_MURDER_SNIPE,       NO_MSG) \
+     MSG_MULTI_NOTIF(1, WEAPON_MACHINEGUN_MURDER_SPRAY,       NO_MSG,        INFO_WEAPON_MACHINEGUN_MURDER_SPRAY,       NO_MSG) \
++    MSG_MULTI_NOTIF(1, WEAPON_MINELAYER_LIMIT,               NO_MSG,        INFO_WEAPON_MINELAYER_LIMIT,               CENTER_WEAPON_MINELAYER_LIMIT) \
      MSG_MULTI_NOTIF(1, WEAPON_MINELAYER_MURDER,              NO_MSG,        INFO_WEAPON_MINELAYER_MURDER,              NO_MSG) \
      MSG_MULTI_NOTIF(1, WEAPON_MINELAYER_SUICIDE,             NO_MSG,        INFO_WEAPON_MINELAYER_SUICIDE,             CENTER_DEATH_SELF_GENERIC) \
-     MSG_MULTI_NOTIF(1, WEAPON_MINSTANEX_MURDER,              NO_MSG,        INFO_WEAPON_MINSTANEX_MURDER,              NO_MSG) \
      MSG_MULTI_NOTIF(1, WEAPON_MORTAR_MURDER_BOUNCE,          NO_MSG,        INFO_WEAPON_MORTAR_MURDER_BOUNCE,          NO_MSG) \
      MSG_MULTI_NOTIF(1, WEAPON_MORTAR_MURDER_EXPLODE,         NO_MSG,        INFO_WEAPON_MORTAR_MURDER_EXPLODE,         NO_MSG) \
      MSG_MULTI_NOTIF(1, WEAPON_MORTAR_SUICIDE_BOUNCE,         NO_MSG,        INFO_WEAPON_MORTAR_SUICIDE_BOUNCE,         CENTER_DEATH_SELF_GENERIC) \
index 0000000,970192e..2dac41e
mode 000000,100644..100644
--- /dev/null
@@@ -1,0 -1,622 +1,622 @@@
 -                      sprint(self, strcat("minelayer: You cannot place more than ^2", ftos(WEP_CVAR(minelayer, limit)), " ^7mines at a time\n") );
+ #ifdef REGISTER_WEAPON
+ REGISTER_WEAPON(
+ /* WEP_##id  */ MINE_LAYER,
+ /* function  */ W_MineLayer,
+ /* ammotype  */ ammo_rockets,
+ /* impulse   */ 4,
+ /* flags     */ WEP_FLAG_MUTATORBLOCKED | WEP_FLAG_RELOADABLE | WEP_TYPE_SPLASH,
+ /* rating    */ BOT_PICKUP_RATING_HIGH,
+ /* color     */ '0.75 1 0',
+ /* modelname */ "minelayer",
+ /* simplemdl */ "foobar",
+ /* crosshair */ "gfx/crosshairminelayer 0.9",
+ /* wepimg    */ "weaponminelayer",
+ /* refname   */ "minelayer",
+ /* wepname   */ _("Mine Layer")
+ );
+ #define MINELAYER_SETTINGS(w_cvar,w_prop) MINELAYER_SETTINGS_LIST(w_cvar, w_prop, MINE_LAYER, minelayer)
+ #define MINELAYER_SETTINGS_LIST(w_cvar,w_prop,id,sn) \
+       w_cvar(id, sn, NONE, ammo) \
+       w_cvar(id, sn, NONE, animtime) \
+       w_cvar(id, sn, NONE, damage) \
+       w_cvar(id, sn, NONE, damageforcescale) \
+       w_cvar(id, sn, NONE, detonatedelay) \
+       w_cvar(id, sn, NONE, edgedamage) \
+       w_cvar(id, sn, NONE, force) \
+       w_cvar(id, sn, NONE, health) \
+       w_cvar(id, sn, NONE, lifetime) \
+       w_cvar(id, sn, NONE, lifetime_countdown) \
+       w_cvar(id, sn, NONE, limit) \
+       w_cvar(id, sn, NONE, protection) \
+       w_cvar(id, sn, NONE, proximityradius) \
+       w_cvar(id, sn, NONE, radius) \
+       w_cvar(id, sn, NONE, refire) \
+       w_cvar(id, sn, NONE, remote_damage) \
+       w_cvar(id, sn, NONE, remote_edgedamage) \
+       w_cvar(id, sn, NONE, remote_force) \
+       w_cvar(id, sn, NONE, remote_radius) \
+       w_cvar(id, sn, NONE, speed) \
+       w_cvar(id, sn, NONE, time) \
+       w_prop(id, sn, float,  reloading_ammo, reload_ammo) \
+       w_prop(id, sn, float,  reloading_time, reload_time) \
+       w_prop(id, sn, float,  switchdelay_raise, switchdelay_raise) \
+       w_prop(id, sn, float,  switchdelay_drop, switchdelay_drop) \
+       w_prop(id, sn, string, weaponreplace, weaponreplace) \
+       w_prop(id, sn, float,  weaponstart, weaponstart) \
+       w_prop(id, sn, float,  weaponstartoverride, weaponstartoverride) \
+       w_prop(id, sn, float,  weaponthrowable, weaponthrowable)
+ #ifdef SVQC
+ MINELAYER_SETTINGS(WEP_ADD_CVAR, WEP_ADD_PROP)
+ void W_MineLayer_Think(void);
+ .float minelayer_detonate, mine_explodeanyway;
+ .float mine_time;
+ .vector mine_orientation;
+ #endif
+ #else
+ #ifdef SVQC
+ void spawnfunc_weapon_minelayer(void) { weapon_defaultspawnfunc(WEP_MINE_LAYER); }
+ void W_MineLayer_Stick(entity to)
+ {
+       spamsound(self, CH_SHOTS, "weapons/mine_stick.wav", VOL_BASE, ATTN_NORM);
+       // in order for mines to face properly when sticking to the ground, they must be a server side entity rather than a csqc projectile
+       entity newmine;
+       newmine = spawn();
+       newmine.classname = self.classname;
+       newmine.bot_dodge = self.bot_dodge;
+       newmine.bot_dodgerating = self.bot_dodgerating;
+       newmine.owner = self.owner;
+       newmine.realowner = self.realowner;
+       setsize(newmine, '-4 -4 -4', '4 4 4');
+       setorigin(newmine, self.origin);
+       setmodel(newmine, "models/mine.md3");
+       newmine.angles = vectoangles(-trace_plane_normal); // face against the surface
+       newmine.mine_orientation = -trace_plane_normal;
+       newmine.takedamage = self.takedamage;
+       newmine.damageforcescale = self.damageforcescale;
+       newmine.health = self.health;
+       newmine.event_damage = self.event_damage;
+       newmine.spawnshieldtime = self.spawnshieldtime;
+       newmine.damagedbycontents = TRUE;
+       newmine.movetype = MOVETYPE_NONE; // lock the mine in place
+       newmine.projectiledeathtype = self.projectiledeathtype;
+       newmine.mine_time = self.mine_time;
+       newmine.touch = func_null;
+       newmine.think = W_MineLayer_Think;
+       newmine.nextthink = time;
+       newmine.cnt = self.cnt;
+       newmine.flags = self.flags;
+       remove(self);
+       self = newmine;
+       if(to)
+               SetMovetypeFollow(self, to);
+ }
+ void W_MineLayer_Explode(void)
+ {
+       if(other.takedamage == DAMAGE_AIM)
+               if(IS_PLAYER(other))
+                       if(DIFF_TEAM(self.realowner, other))
+                               if(other.deadflag == DEAD_NO)
+                                       if(IsFlying(other))
+                                               Send_Notification(NOTIF_ONE, self.realowner, MSG_ANNCE, ANNCE_ACHIEVEMENT_AIRSHOT);
+       self.event_damage = func_null;
+       self.takedamage = DAMAGE_NO;
+       RadiusDamage(self, self.realowner, WEP_CVAR(minelayer, damage), WEP_CVAR(minelayer, edgedamage), WEP_CVAR(minelayer, radius), world, world, WEP_CVAR(minelayer, force), self.projectiledeathtype, other);
+       if(self.realowner.weapon == WEP_MINE_LAYER)
+       {
+               entity oldself;
+               oldself = self;
+               self = self.realowner;
+               if(!WEP_ACTION(WEP_MINE_LAYER, WR_CHECKAMMO1))
+               {
+                       self.cnt = WEP_MINE_LAYER;
+                       ATTACK_FINISHED(self) = time;
+                       self.switchweapon = w_getbestweapon(self);
+               }
+               self = oldself;
+       }
+       self.realowner.minelayer_mines -= 1;
+       remove(self);
+ }
+ void W_MineLayer_DoRemoteExplode(void)
+ {
+       self.event_damage = func_null;
+       self.takedamage = DAMAGE_NO;
+       if(self.movetype == MOVETYPE_NONE || self.movetype == MOVETYPE_FOLLOW)
+               self.velocity = self.mine_orientation; // particle fx and decals need .velocity
+       RadiusDamage(self, self.realowner, WEP_CVAR(minelayer, remote_damage), WEP_CVAR(minelayer, remote_edgedamage), WEP_CVAR(minelayer, remote_radius), world, world, WEP_CVAR(minelayer, remote_force), self.projectiledeathtype | HITTYPE_BOUNCE, world);
+       if(self.realowner.weapon == WEP_MINE_LAYER)
+       {
+               entity oldself;
+               oldself = self;
+               self = self.realowner;
+               if(!WEP_ACTION(WEP_MINE_LAYER, WR_CHECKAMMO1))
+               {
+                       self.cnt = WEP_MINE_LAYER;
+                       ATTACK_FINISHED(self) = time;
+                       self.switchweapon = w_getbestweapon(self);
+               }
+               self = oldself;
+       }
+       self.realowner.minelayer_mines -= 1;
+       remove(self);
+ }
+ void W_MineLayer_RemoteExplode(void)
+ {
+       if(self.realowner.deadflag == DEAD_NO)
+               if((self.spawnshieldtime >= 0)
+                       ? (time >= self.spawnshieldtime) // timer
+                       : (vlen(NearestPointOnBox(self.realowner, self.origin) - self.origin) > WEP_CVAR(minelayer, remote_radius)) // safety device
+               )
+               {
+                       W_MineLayer_DoRemoteExplode();
+               }
+ }
+ void W_MineLayer_ProximityExplode(void)
+ {
+       // make sure no friend is in the mine's radius. If there is any, explosion is delayed until he's at a safe distance
+       if(WEP_CVAR(minelayer, protection) && self.mine_explodeanyway == 0)
+       {
+               entity head;
+               head = findradius(self.origin, WEP_CVAR(minelayer, radius));
+               while(head)
+               {
+                       if(head == self.realowner || SAME_TEAM(head, self.realowner))
+                               return;
+                       head = head.chain;
+               }
+       }
+       self.mine_time = 0;
+       W_MineLayer_Explode();
+ }
+ float W_MineLayer_Count(entity e)
+ {
+       float minecount = 0;
+       entity mine;
+       for(mine = world; (mine = find(mine, classname, "mine")); ) if(mine.realowner == e)
+               minecount += 1;
+       return minecount;
+ }
+ void W_MineLayer_Think(void)
+ {
+       entity head;
+       self.nextthink = time;
+       if(self.movetype == MOVETYPE_FOLLOW)
+       {
+               if(LostMovetypeFollow(self))
+               {
+                       UnsetMovetypeFollow(self);
+                       self.movetype = MOVETYPE_NONE;
+               }
+       }
+       
+       // our lifetime has expired, it's time to die - mine_time just allows us to play a sound for this
+       // TODO: replace this mine_trigger.wav sound with a real countdown
+       if((time > self.cnt) && (!self.mine_time) && (self.cnt > 0))
+       {
+               if(WEP_CVAR(minelayer, lifetime_countdown) > 0)
+                       spamsound(self, CH_SHOTS, "weapons/mine_trigger.wav", VOL_BASE, ATTN_NORM);
+               self.mine_time = time + WEP_CVAR(minelayer, lifetime_countdown);
+               self.mine_explodeanyway = 1; // make the mine super aggressive -- Samual: Rather, make it not care if a team mate is near.
+       }
+       // a player's mines shall explode if he disconnects or dies
+       // TODO: Do this on team change too -- Samual: But isn't a player killed when they switch teams?
+       if(!IS_PLAYER(self.realowner) || self.realowner.deadflag != DEAD_NO || self.realowner.frozen)
+       {
+               other = world;
+               self.projectiledeathtype |= HITTYPE_BOUNCE;
+               W_MineLayer_Explode();
+               return;
+       }
+       // set the mine for detonation when a foe gets close enough
+       head = findradius(self.origin, WEP_CVAR(minelayer, proximityradius));
+       while(head)
+       {
+               if(IS_PLAYER(head) && head.deadflag == DEAD_NO && !head.frozen)
+               if(head != self.realowner && DIFF_TEAM(head, self.realowner)) // don't trigger for team mates
+               if(!self.mine_time)
+               {
+                       spamsound(self, CH_SHOTS, "weapons/mine_trigger.wav", VOL_BASE, ATTN_NORM);
+                       self.mine_time = time + WEP_CVAR(minelayer, time);
+               }
+               head = head.chain;
+       }
+       // explode if it's time to
+       if(self.mine_time && time >= self.mine_time)
+       {
+               W_MineLayer_ProximityExplode();
+               return;
+       }
+       // remote detonation
+       if(self.realowner.weapon == WEP_MINE_LAYER)
+       if(self.realowner.deadflag == DEAD_NO)
+       if(self.minelayer_detonate)
+               W_MineLayer_RemoteExplode();
+ }
+ void W_MineLayer_Touch(void)
+ {
+       if(self.movetype == MOVETYPE_NONE || self.movetype == MOVETYPE_FOLLOW)
+               return; // we're already a stuck mine, why do we get called? TODO does this even happen?
+       if(WarpZone_Projectile_Touch())
+       {
+               if(wasfreed(self))
+                       self.realowner.minelayer_mines -= 1;
+               return;
+       }
+       if(other && IS_PLAYER(other) && other.deadflag == DEAD_NO)
+       {
+               // hit a player
+               // don't stick
+       }
+       else
+       {
+               W_MineLayer_Stick(other);
+       }
+ }
+ void W_MineLayer_Damage(entity inflictor, entity attacker, float damage, float deathtype, vector hitloc, vector force)
+ {
+       if(self.health <= 0)
+               return;
+               
+       float is_from_enemy = (inflictor.realowner != self.realowner);
+               
+       if(!W_CheckProjectileDamage(inflictor.realowner, self.realowner, deathtype, (is_from_enemy ? 1 : -1)))
+               return; // g_projectiles_damage says to halt
+               
+       self.health = self.health - damage;
+       self.angles = vectoangles(self.velocity);
+       
+       if(self.health <= 0)
+               W_PrepareExplosionByDamage(attacker, W_MineLayer_Explode);
+ }
+ void W_MineLayer_Attack(void)
+ {
+       entity mine;
+       entity flash;
+       // scan how many mines we placed, and return if we reached our limit
+       if(WEP_CVAR(minelayer, limit))
+       {
+               if(self.minelayer_mines >= WEP_CVAR(minelayer, limit))
+               {
+                       // the refire delay keeps this message from being spammed
++                      Send_Notification(NOTIF_ONE, self, MSG_MULTI, WEAPON_MINELAYER_LIMIT, WEP_CVAR(minelayer, limit));
+                       play2(self, "weapons/unavailable.wav");
+                       return;
+               }
+       }
+       W_DecreaseAmmo(WEP_CVAR(minelayer, ammo));
+       W_SetupShot_ProjectileSize(self, '-4 -4 -4', '4 4 4', FALSE, 5, "weapons/mine_fire.wav", CH_WEAPON_A, WEP_CVAR(minelayer, damage));
+       pointparticles(particleeffectnum("rocketlauncher_muzzleflash"), w_shotorg, w_shotdir * 1000, 1);
+       mine = WarpZone_RefSys_SpawnSameRefSys(self);
+       mine.owner = mine.realowner = self;
+       if(WEP_CVAR(minelayer, detonatedelay) >= 0)
+               mine.spawnshieldtime = time + WEP_CVAR(minelayer, detonatedelay);
+       else
+               mine.spawnshieldtime = -1;
+       mine.classname = "mine";
+       mine.bot_dodge = TRUE;
+       mine.bot_dodgerating = WEP_CVAR(minelayer, damage) * 2; // * 2 because it can detonate inflight which makes it even more dangerous
+       mine.takedamage = DAMAGE_YES;
+       mine.damageforcescale = WEP_CVAR(minelayer, damageforcescale);
+       mine.health = WEP_CVAR(minelayer, health);
+       mine.event_damage = W_MineLayer_Damage;
+       mine.damagedbycontents = TRUE;
+       mine.movetype = MOVETYPE_TOSS;
+       PROJECTILE_MAKETRIGGER(mine);
+       mine.projectiledeathtype = WEP_MINE_LAYER;
+       setsize(mine, '-4 -4 -4', '4 4 4'); // give it some size so it can be shot
+       setorigin(mine, w_shotorg - v_forward * 4); // move it back so it hits the wall at the right point
+       W_SetupProjVelocity_Basic(mine, WEP_CVAR(minelayer, speed), 0);
+       mine.angles = vectoangles(mine.velocity);
+       mine.touch = W_MineLayer_Touch;
+       mine.think = W_MineLayer_Think;
+       mine.nextthink = time;
+       mine.cnt = (WEP_CVAR(minelayer, lifetime) - WEP_CVAR(minelayer, lifetime_countdown));
+       mine.flags = FL_PROJECTILE;
+       mine.missile_flags = MIF_SPLASH | MIF_ARC | MIF_PROXY;
+       if(mine.cnt > 0) { mine.cnt += time; }
+       CSQCProjectile(mine, TRUE, PROJECTILE_MINE, TRUE);
+       // muzzle flash for 1st person view
+       flash = spawn();
+       setmodel(flash, "models/flash.md3"); // precision set below
+       SUB_SetFade(flash, time, 0.1);
+       flash.effects = EF_ADDITIVE | EF_FULLBRIGHT | EF_LOWPRECISION;
+       W_AttachToShotorg(flash, '5 0 0');
+       // common properties
+       other = mine; MUTATOR_CALLHOOK(EditProjectile);
+       
+       self.minelayer_mines = W_MineLayer_Count(self);
+ }
+ float W_MineLayer_PlacedMines(float detonate)
+ {
+       entity mine;
+       float minfound = 0;
+       for(mine = world; (mine = find(mine, classname, "mine")); ) if(mine.realowner == self)
+       {
+               if(detonate)
+               {
+                       if(!mine.minelayer_detonate)
+                       {
+                               mine.minelayer_detonate = TRUE;
+                               minfound = 1;
+                       }
+               }
+               else
+                       minfound = 1;
+       }
+       return minfound;
+ }
+ float W_MineLayer(float req)
+ {
+       entity mine;
+       float ammo_amount;
+       switch(req)
+       {
+               case WR_AIM:
+               {
+                       // aim and decide to fire if appropriate
+                       if(self.minelayer_mines >= WEP_CVAR(minelayer, limit))
+                               self.BUTTON_ATCK = FALSE;
+                       else
+                               self.BUTTON_ATCK = bot_aim(WEP_CVAR(minelayer, speed), 0, WEP_CVAR(minelayer, lifetime), FALSE);
+                       if(skill >= 2) // skill 0 and 1 bots won't detonate mines!
+                       {
+                               // decide whether to detonate mines
+                               entity targetlist, targ;
+                               float edgedamage, coredamage, edgeradius, recipricoledgeradius, d;
+                               float selfdamage, teamdamage, enemydamage;
+                               edgedamage = WEP_CVAR(minelayer, edgedamage);
+                               coredamage = WEP_CVAR(minelayer, damage);
+                               edgeradius = WEP_CVAR(minelayer, radius);
+                               recipricoledgeradius = 1 / edgeradius;
+                               selfdamage = 0;
+                               teamdamage = 0;
+                               enemydamage = 0;
+                               targetlist = findchainfloat(bot_attack, TRUE);
+                               mine = find(world, classname, "mine");
+                               while(mine)
+                               {
+                                       if(mine.realowner != self)
+                                       {
+                                               mine = find(mine, classname, "mine");
+                                               continue;
+                                       }
+                                       targ = targetlist;
+                                       while(targ)
+                                       {
+                                               d = vlen(targ.origin + (targ.mins + targ.maxs) * 0.5 - mine.origin);
+                                               d = bound(0, edgedamage + (coredamage - edgedamage) * sqrt(1 - d * recipricoledgeradius), 10000);
+                                               // count potential damage according to type of target
+                                               if(targ == self)
+                                                       selfdamage = selfdamage + d;
+                                               else if(targ.team == self.team && teamplay)
+                                                       teamdamage = teamdamage + d;
+                                               else if(bot_shouldattack(targ))
+                                                       enemydamage = enemydamage + d;
+                                               targ = targ.chain;
+                                       }
+                                       mine = find(mine, classname, "mine");
+                               }
+                               float desirabledamage;
+                               desirabledamage = enemydamage;
+                               if(time > self.invincible_finished && time > self.spawnshieldtime)
+                                       desirabledamage = desirabledamage - selfdamage * autocvar_g_balance_selfdamagepercent;
+                               if(teamplay && self.team)
+                                       desirabledamage = desirabledamage - teamdamage;
+                               mine = find(world, classname, "mine");
+                               while(mine)
+                               {
+                                       if(mine.realowner != self)
+                                       {
+                                               mine = find(mine, classname, "mine");
+                                               continue;
+                                       }
+                                       makevectors(mine.v_angle);
+                                       targ = targetlist;
+                                       if(skill > 9) // normal players only do this for the target they are tracking
+                                       {
+                                               targ = targetlist;
+                                               while(targ)
+                                               {
+                                                       if(
+                                                               (v_forward * normalize(mine.origin - targ.origin)< 0.1)
+                                                               && desirabledamage > 0.1*coredamage
+                                                       )self.BUTTON_ATCK2 = TRUE;
+                                                       targ = targ.chain;
+                                               }
+                                       }else{
+                                               float distance; distance= bound(300,vlen(self.origin-self.enemy.origin),30000);
+                                               //As the distance gets larger, a correct detonation gets near imposible
+                                               //Bots are assumed to use the mine spawnfunc_light to see if the mine gets near a player
+                                               if(v_forward * normalize(mine.origin - self.enemy.origin)< 0.1)
+                                                       if(IS_PLAYER(self.enemy))
+                                                               if(desirabledamage >= 0.1*coredamage)
+                                                                       if(random()/distance*300 > frametime*bound(0,(10-skill)*0.2,1))
+                                                                               self.BUTTON_ATCK2 = TRUE;
+                                       //      dprint(ftos(random()/distance*300),">");dprint(ftos(frametime*bound(0,(10-skill)*0.2,1)),"\n");
+                                       }
+                                       mine = find(mine, classname, "mine");
+                               }
+                               // if we would be doing at X percent of the core damage, detonate it
+                               // but don't fire a new shot at the same time!
+                               if(desirabledamage >= 0.75 * coredamage) //this should do group damage in rare fortunate events
+                                       self.BUTTON_ATCK2 = TRUE;
+                               if((skill > 6.5) && (selfdamage > self.health))
+                                       self.BUTTON_ATCK2 = FALSE;
+                               //if(self.BUTTON_ATCK2 == TRUE)
+                               //      dprint(ftos(desirabledamage),"\n");
+                               if(self.BUTTON_ATCK2 == TRUE) self.BUTTON_ATCK = FALSE;
+                       }
+                       
+                       return TRUE;
+               }
+               case WR_THINK:
+               {
+                       if(autocvar_g_balance_minelayer_reload_ammo && self.clip_load < WEP_CVAR(minelayer, ammo)) // forced reload
+                       {
+                               // not if we're holding the minelayer without enough ammo, but can detonate existing mines
+                               if(!(W_MineLayer_PlacedMines(FALSE) && self.WEP_AMMO(MINE_LAYER) < WEP_CVAR(minelayer, ammo)))
+                                       WEP_ACTION(self.weapon, WR_RELOAD);
+                       }
+                       else if(self.BUTTON_ATCK)
+                       {
+                               if(weapon_prepareattack(0, WEP_CVAR(minelayer, refire)))
+                               {
+                                       W_MineLayer_Attack();
+                                       weapon_thinkf(WFRAME_FIRE1, WEP_CVAR(minelayer, animtime), w_ready);
+                               }
+                       }
+                       if(self.BUTTON_ATCK2)
+                       {
+                               if(W_MineLayer_PlacedMines(TRUE))
+                                       sound(self, CH_WEAPON_B, "weapons/mine_det.wav", VOL_BASE, ATTN_NORM);
+                       }
+                       
+                       return TRUE;
+               }
+               case WR_INIT:
+               {
+                       precache_model("models/flash.md3");
+                       precache_model("models/mine.md3");
+                       precache_model("models/weapons/g_minelayer.md3");
+                       precache_model("models/weapons/v_minelayer.md3");
+                       precache_model("models/weapons/h_minelayer.iqm");
+                       precache_sound("weapons/mine_det.wav");
+                       precache_sound("weapons/mine_fire.wav");
+                       precache_sound("weapons/mine_stick.wav");
+                       precache_sound("weapons/mine_trigger.wav");
+                       MINELAYER_SETTINGS(WEP_SKIP_CVAR, WEP_SET_PROP)
+                       return TRUE;
+               }
+               case WR_CHECKAMMO1:
+               {
+                       // don't switch while placing a mine
+                       if(ATTACK_FINISHED(self) <= time || self.weapon != WEP_MINE_LAYER)
+                       {
+                               ammo_amount = self.WEP_AMMO(MINE_LAYER) >= WEP_CVAR(minelayer, ammo);
+                               ammo_amount += self.(weapon_load[WEP_MINE_LAYER]) >= WEP_CVAR(minelayer, ammo);
+                               return ammo_amount;
+                       }
+                       return TRUE;
+               }
+               case WR_CHECKAMMO2:
+               {
+                       if(W_MineLayer_PlacedMines(FALSE))
+                               return TRUE;
+                       else
+                               return FALSE;
+               }
+               case WR_CONFIG:
+               {
+                       MINELAYER_SETTINGS(WEP_CONFIG_WRITE_CVARS, WEP_CONFIG_WRITE_PROPS)
+                       return TRUE;
+               }
+               case WR_RESETPLAYER:
+               {
+                       self.minelayer_mines = 0;
+                       return TRUE;
+               }
+               case WR_RELOAD:
+               {
+                       W_Reload(WEP_CVAR(minelayer, ammo), "weapons/reload.wav");
+                       return TRUE;
+               }
+               case WR_SUICIDEMESSAGE:
+               {
+                       return WEAPON_MINELAYER_SUICIDE;
+               }
+               case WR_KILLMESSAGE:
+               {
+                       return WEAPON_MINELAYER_MURDER;
+               }
+       }
+       return FALSE;
+ }
+ #endif
+ #ifdef CSQC
+ float W_MineLayer(float req)
+ {
+       switch(req)
+       {
+               case WR_IMPACTEFFECT:
+               {
+                       vector org2;
+                       org2 = w_org + w_backoff * 12;
+                       pointparticles(particleeffectnum("rocket_explode"), org2, '0 0 0', 1);
+                       if(!w_issilent)
+                               sound(self, CH_SHOTS, "weapons/mine_exp.wav", VOL_BASE, ATTN_NORM);
+                       
+                       return TRUE;
+               }
+               case WR_INIT:
+               {
+                       precache_sound("weapons/mine_exp.wav");
+                       return TRUE;
+               }
+               case WR_ZOOMRETICLE:
+               {
+                       // no weapon specific image for this weapon
+                       return FALSE;
+               }
+       }
+       return FALSE;
+ }
+ #endif
+ #endif
index 0000000,49fb47e..e1fb82f
mode 000000,100644..100644
--- /dev/null
@@@ -1,0 -1,422 +1,422 @@@
 -                              centerprint(self.realowner, "^1Portal deployment failed.\n\n^2Catch it to try again!");
+ #ifdef REGISTER_WEAPON
+ REGISTER_WEAPON(
+ /* WEP_##id  */ PORTO,
+ /* function  */ W_Porto,
+ /* ammotype  */ ammo_none,
+ /* impulse   */ 0,
+ /* flags     */ WEP_TYPE_OTHER | WEP_FLAG_SUPERWEAPON,
+ /* rating    */ 0,
+ /* color     */ '0.5 0.5 0.5',
+ /* modelname */ "porto",
+ /* simplemdl */ "foobar",
+ /* crosshair */ "gfx/crosshairporto 0.6",
+ /* wepimg    */ "weaponporto",
+ /* refname   */ "porto",
+ /* wepname   */ _("Port-O-Launch")
+ );
+ #define PORTO_SETTINGS(w_cvar,w_prop) PORTO_SETTINGS_LIST(w_cvar, w_prop, PORTO, porto)
+ #define PORTO_SETTINGS_LIST(w_cvar,w_prop,id,sn) \
+       w_cvar(id, sn, BOTH, animtime) \
+       w_cvar(id, sn, BOTH, lifetime) \
+       w_cvar(id, sn, BOTH, refire) \
+       w_cvar(id, sn, BOTH, speed) \
+       w_cvar(id, sn, NONE, secondary) \
+       w_prop(id, sn, float,  switchdelay_raise, switchdelay_raise) \
+       w_prop(id, sn, float,  switchdelay_drop, switchdelay_drop) \
+       w_prop(id, sn, string, weaponreplace, weaponreplace) \
+       w_prop(id, sn, float,  weaponstart, weaponstart) \
+       w_prop(id, sn, float,  weaponstartoverride, weaponstartoverride) \
+       w_prop(id, sn, float,  weaponthrowable, weaponthrowable)
+ #ifdef SVQC
+ PORTO_SETTINGS(WEP_ADD_CVAR, WEP_ADD_PROP)
+ .entity porto_current;
+ .vector porto_v_angle; // holds "held" view angles
+ .float porto_v_angle_held;
+ .vector right_vector;
+ #endif
+ #else
+ #ifdef SVQC
+ void spawnfunc_weapon_porto(void) { weapon_defaultspawnfunc(WEP_PORTO); }
+ void W_Porto_Success(void)
+ {
+       if(self.realowner == world)
+       {
+               objerror("Cannot succeed successfully: no owner\n");
+               return;
+       }
+       self.realowner.porto_current = world;
+       remove(self);
+ }
+ string W_ThrowNewWeapon(entity own, float wpn, float doreduce, vector org, vector velo);
+ void W_Porto_Fail(float failhard)
+ {
+       if(self.realowner == world)
+       {
+               objerror("Cannot fail successfully: no owner\n");
+               return;
+       }
+       // no portals here!
+       if(self.cnt < 0)
+       {
+               Portal_ClearWithID(self.realowner, self.portal_id);
+       }
+       self.realowner.porto_current = world;
+       if(self.cnt < 0 && !failhard && self.realowner.playerid == self.playerid && self.realowner.deadflag == DEAD_NO && !(self.realowner.weapons & WEPSET_PORTO))
+       {
+               setsize(self, '-16 -16 0', '16 16 32');
+               setorigin(self, self.origin + trace_plane_normal);
+               if(move_out_of_solid(self))
+               {
+                       self.flags = FL_ITEM;
+                       self.velocity = trigger_push_calculatevelocity(self.origin, self.realowner, 128);
+                       tracetoss(self, self);
+                       if(vlen(trace_endpos - self.realowner.origin) < 128)
+                       {
+                               W_ThrowNewWeapon(self.realowner, WEP_PORTO, 0, self.origin, self.velocity);
 -                      centerprint(self.realowner, "^1In^7-portal created.");
++                              Send_Notification(NOTIF_ONE, self.realowner, MSG_CENTER, CENTER_PORTO_FAILED);
+                       }
+               }
+       }
+       remove(self);
+ }
+ void W_Porto_Remove(entity p)
+ {
+       if(p.porto_current.realowner == p && p.porto_current.classname == "porto")
+       {
+               entity oldself;
+               oldself = self;
+               self = p.porto_current;
+               W_Porto_Fail(1);
+               self = oldself;
+       }
+ }
+ void W_Porto_Think(void)
+ {
+       trace_plane_normal = '0 0 0';
+       if(self.realowner.playerid != self.playerid)
+               remove(self);
+       else
+               W_Porto_Fail(0);
+ }
+ void W_Porto_Touch(void)
+ {
+       vector norm;
+       // do not use PROJECTILE_TOUCH here
+       // FIXME but DO handle warpzones!
+       if(other.classname == "portal")
+               return; // handled by the portal
+       norm = trace_plane_normal;
+       if(trace_ent.iscreature)
+       {
+               traceline(trace_ent.origin, trace_ent.origin + '0 0 2' * PL_MIN_z, MOVE_WORLDONLY, self);
+               if(trace_fraction >= 1)
+                       return;
+               if(trace_dphitq3surfaceflags & Q3SURFACEFLAG_SLICK || trace_dphitcontents & DPCONTENTS_PLAYERCLIP)
+                       return;
+               if(trace_dphitq3surfaceflags & Q3SURFACEFLAG_NOIMPACT)
+                       return;
+       }
+       if(self.realowner.playerid != self.playerid)
+       {
+               sound(self, CH_SHOTS, "porto/unsupported.wav", VOL_BASE, ATTEN_NORM);
+               remove(self);
+       }
+       else if(trace_dphitq3surfaceflags & Q3SURFACEFLAG_SLICK || trace_dphitcontents & DPCONTENTS_PLAYERCLIP)
+       {
+               spamsound(self, CH_SHOTS, "porto/bounce.wav", VOL_BASE, ATTEN_NORM);
+               // just reflect
+               self.right_vector = self.right_vector - 2 * trace_plane_normal * (self.right_vector * trace_plane_normal);
+               self.angles = vectoangles(self.velocity - 2 * trace_plane_normal * (self.velocity * trace_plane_normal));
+       }
+       else if(trace_dphitq3surfaceflags & Q3SURFACEFLAG_NOIMPACT)
+       {
+               sound(self, CH_SHOTS, "porto/unsupported.wav", VOL_BASE, ATTEN_NORM);
+               W_Porto_Fail(0);
+               if(self.cnt < 0)
+                       Portal_ClearAll_PortalsOnly(self.realowner);
+       }
+       else if(self.cnt == 0)
+       {
+               // in-portal only
+               if(Portal_SpawnInPortalAtTrace(self.realowner, self.right_vector, self.portal_id))
+               {
+                       sound(self, CH_SHOTS, "porto/create.wav", VOL_BASE, ATTEN_NORM);
+                       trace_plane_normal = norm;
 -                      centerprint(self.realowner, "^4Out^7-portal created.");
++                      Send_Notification(NOTIF_ONE, self.realowner, MSG_CENTER, CENTER_PORTO_CREATED_IN);
+                       W_Porto_Success();
+               }
+               else
+               {
+                       sound(self, CH_SHOTS, "porto/unsupported.wav", VOL_BASE, ATTEN_NORM);
+                       trace_plane_normal = norm;
+                       W_Porto_Fail(0);
+               }
+       }
+       else if(self.cnt == 1)
+       {
+               // out-portal only
+               if(Portal_SpawnOutPortalAtTrace(self.realowner, self.right_vector, self.portal_id))
+               {
+                       sound(self, CH_SHOTS, "porto/create.wav", VOL_BASE, ATTEN_NORM);
+                       trace_plane_normal = norm;
 -                      centerprint(self.realowner, "^1In^7-portal created.");
++                      Send_Notification(NOTIF_ONE, self.realowner, MSG_CENTER, CENTER_PORTO_CREATED_OUT);
+                       W_Porto_Success();
+               }
+               else
+               {
+                       sound(self, CH_SHOTS, "porto/unsupported.wav", VOL_BASE, ATTEN_NORM);
+                       trace_plane_normal = norm;
+                       W_Porto_Fail(0);
+               }
+       }
+       else if(self.effects & EF_RED)
+       {
+               self.effects += EF_BLUE - EF_RED;
+               if(Portal_SpawnInPortalAtTrace(self.realowner, self.right_vector, self.portal_id))
+               {
+                       sound(self, CH_SHOTS, "porto/create.wav", VOL_BASE, ATTEN_NORM);
+                       trace_plane_normal = norm;
 -                              centerprint(self.realowner, "^4Out^7-portal created.");
++                      Send_Notification(NOTIF_ONE, self.realowner, MSG_CENTER, CENTER_PORTO_CREATED_IN);
+                       self.right_vector = self.right_vector - 2 * trace_plane_normal * (self.right_vector * norm);
+                       self.angles = vectoangles(self.velocity - 2 * trace_plane_normal * (self.velocity * norm));
+                       CSQCProjectile(self, TRUE, PROJECTILE_PORTO_BLUE, TRUE); // change type
+               }
+               else
+               {
+                       sound(self, CH_SHOTS, "porto/unsupported.wav", VOL_BASE, ATTEN_NORM);
+                       trace_plane_normal = norm;
+                       Portal_ClearAll_PortalsOnly(self.realowner);
+                       W_Porto_Fail(0);
+               }
+       }
+       else
+       {
+               if(self.realowner.portal_in.portal_id == self.portal_id)
+               {
+                       if(Portal_SpawnOutPortalAtTrace(self.realowner, self.right_vector, self.portal_id))
+                       {
+                               sound(self, CH_SHOTS, "porto/create.wav", VOL_BASE, ATTEN_NORM);
+                               trace_plane_normal = norm;
++                              Send_Notification(NOTIF_ONE, self.realowner, MSG_CENTER, CENTER_PORTO_CREATED_OUT);
+                               W_Porto_Success();
+                       }
+                       else
+                       {
+                               sound(self, CH_SHOTS, "porto/unsupported.wav", VOL_BASE, ATTEN_NORM);
+                               Portal_ClearAll_PortalsOnly(self.realowner);
+                               W_Porto_Fail(0);
+                       }
+               }
+               else
+               {
+                       sound(self, CH_SHOTS, "porto/unsupported.wav", VOL_BASE, ATTEN_NORM);
+                       Portal_ClearAll_PortalsOnly(self.realowner);
+                       W_Porto_Fail(0);
+               }
+       }
+ }
+ void W_Porto_Attack(float type)
+ {
+       entity gren;
+       W_SetupShot(self, FALSE, 4, "porto/fire.wav", CH_WEAPON_A, 0);
+       // always shoot from the eye
+       w_shotdir = v_forward;
+       w_shotorg = self.origin + self.view_ofs + ((w_shotorg - self.origin - self.view_ofs) * v_forward) * v_forward;
+       //pointparticles(particleeffectnum("grenadelauncher_muzzleflash"), w_shotorg, w_shotdir * 1000, 1);
+       gren = spawn();
+       gren.cnt = type;
+       gren.owner = gren.realowner = self;
+       gren.playerid = self.playerid;
+       gren.classname = "porto";
+       gren.bot_dodge = TRUE;
+       gren.bot_dodgerating = 200;
+       gren.movetype = MOVETYPE_BOUNCEMISSILE;
+       PROJECTILE_MAKETRIGGER(gren);
+       gren.effects = EF_RED;
+       gren.scale = 4;
+       setorigin(gren, w_shotorg);
+       setsize(gren, '0 0 0', '0 0 0');
+       
+       gren.nextthink = time + WEP_CVAR_BOTH(porto, (type <= 0), lifetime);
+       gren.think = W_Porto_Think;
+       gren.touch = W_Porto_Touch;
+       
+       if(self.items & IT_STRENGTH)
+               W_SetupProjVelocity_Basic(gren, WEP_CVAR_BOTH(porto, (type <= 0), speed) * autocvar_g_balance_powerup_strength_force, 0);
+       else
+               W_SetupProjVelocity_Basic(gren, WEP_CVAR_BOTH(porto, (type <= 0), speed), 0);
+       gren.angles = vectoangles(gren.velocity);
+       gren.flags = FL_PROJECTILE;
+       gren.portal_id = time;
+       self.porto_current = gren;
+       gren.playerid = self.playerid;
+       fixedmakevectors(fixedvectoangles(gren.velocity));
+       gren.right_vector = v_right;
+       gren.dphitcontentsmask = DPCONTENTS_SOLID | DPCONTENTS_BODY | DPCONTENTS_PLAYERCLIP;
+       if(type > 0)
+               CSQCProjectile(gren, TRUE, PROJECTILE_PORTO_BLUE, TRUE);
+       else
+               CSQCProjectile(gren, TRUE, PROJECTILE_PORTO_RED, TRUE);
+       other = gren; MUTATOR_CALLHOOK(EditProjectile);
+ }
+ float w_nexball_weapon(float req); // WEAPONTODO
+ float W_Porto(float req)
+ {
+       //vector v_angle_save;
+       if(g_nexball) { return w_nexball_weapon(req); }
+       
+       switch(req)
+       {
+               case WR_AIM:
+               {
+                       self.BUTTON_ATCK = FALSE;
+                       self.BUTTON_ATCK2 = FALSE;
+                       if(!WEP_CVAR(porto, secondary))
+                               if(bot_aim(WEP_CVAR_PRI(porto, speed), 0, WEP_CVAR_PRI(porto, lifetime), FALSE))
+                                       self.BUTTON_ATCK = TRUE;
+                                       
+                       return TRUE;
+               }
+               case WR_CONFIG:
+               {
+                       PORTO_SETTINGS(WEP_CONFIG_WRITE_CVARS, WEP_CONFIG_WRITE_PROPS)
+                       return TRUE;
+               }
+               case WR_THINK:
+               {
+                       if(WEP_CVAR(porto, secondary))
+                       {
+                               if(self.BUTTON_ATCK)
+                               if(!self.porto_current)
+                               if(!self.porto_forbidden)
+                               if(weapon_prepareattack(0, WEP_CVAR_PRI(porto, refire)))
+                               {
+                                       W_Porto_Attack(0);
+                                       weapon_thinkf(WFRAME_FIRE1, WEP_CVAR_PRI(porto, animtime), w_ready);
+                               }
+                               if(self.BUTTON_ATCK2)
+                               if(!self.porto_current)
+                               if(!self.porto_forbidden)
+                               if(weapon_prepareattack(1, WEP_CVAR_SEC(porto, refire)))
+                               {
+                                       W_Porto_Attack(1);
+                                       weapon_thinkf(WFRAME_FIRE2, WEP_CVAR_SEC(porto, animtime), w_ready);
+                               }
+                       }
+                       else
+                       {
+                               if(self.porto_v_angle_held)
+                               {
+                                       if(!self.BUTTON_ATCK2)
+                                       {
+                                               self.porto_v_angle_held = 0;
+                                               ClientData_Touch(self);
+                                       }
+                               }
+                               else
+                               {
+                                       if(self.BUTTON_ATCK2)
+                                       {
+                                               self.porto_v_angle = self.v_angle;
+                                               self.porto_v_angle_held = 1;
+                                               ClientData_Touch(self);
+                                       }
+                               }
+                               if(self.porto_v_angle_held)
+                                       makevectors(self.porto_v_angle); // override the previously set angles
+                               if(self.BUTTON_ATCK)
+                               if(!self.porto_current)
+                               if(!self.porto_forbidden)
+                               if(weapon_prepareattack(0, WEP_CVAR_PRI(porto, refire)))
+                               {
+                                       W_Porto_Attack(-1);
+                                       weapon_thinkf(WFRAME_FIRE1, WEP_CVAR_PRI(porto, animtime), w_ready);
+                               }
+                       }
+                       
+                       return TRUE;
+               }
+               case WR_INIT:
+               {
+                       precache_model("models/weapons/g_porto.md3");
+                       precache_model("models/weapons/v_porto.md3");
+                       precache_model("models/weapons/h_porto.iqm");
+                       precache_model("models/portal.md3");
+                       precache_sound("porto/bounce.wav");
+                       precache_sound("porto/create.wav");
+                       precache_sound("porto/expire.wav");
+                       precache_sound("porto/explode.wav");
+                       precache_sound("porto/fire.wav");
+                       precache_sound("porto/unsupported.wav");
+                       PORTO_SETTINGS(WEP_SKIP_CVAR, WEP_SET_PROP)
+                       return TRUE;
+               }
+               case WR_SETUP:
+               {
+                       self.ammo_field = ammo_none;
+                       return TRUE;
+               }
+               case WR_RESETPLAYER:
+               {
+                       self.porto_current = world;
+                       return TRUE;
+               }
+       }
+       return FALSE;
+ }
+ #endif
+ #ifdef CSQC
+ float W_Porto(float req)
+ {
+       switch(req)
+       {
+               case WR_IMPACTEFFECT:
+               {
+                       print("Since when does Porto send DamageInfo?\n");
+                       return TRUE;
+               }
+               case WR_INIT:
+               {
+                       // nothing to do
+                       return TRUE;
+               }
+               case WR_ZOOMRETICLE:
+               {
+                       // no weapon specific image for this weapon
+                       return FALSE;
+               }
+       }
+       return FALSE;
+ }
+ #endif
+ #endif
@@@ -1577,44 -1542,52 +1542,52 @@@ float CalcRotRegen(float current, floa
  
  void player_regen (void)
  {
-       float minh, mina, minf, maxh, maxa, maxf, limith, limita, limitf, max_mod, regen_mod, rot_mod, limit_mod;
-       maxh = autocvar_g_balance_health_rotstable;
-       maxa = autocvar_g_balance_armor_rotstable;
-       maxf = autocvar_g_balance_fuel_rotstable;
-       minh = autocvar_g_balance_health_regenstable;
-       mina = autocvar_g_balance_armor_regenstable;
-       minf = autocvar_g_balance_fuel_regenstable;
-       limith = autocvar_g_balance_health_limit;
-       limita = autocvar_g_balance_armor_limit;
-       limitf = autocvar_g_balance_fuel_limit;
+       float max_mod, regen_mod, rot_mod, limit_mod;
        max_mod = regen_mod = rot_mod = limit_mod = 1;
 -              
+       regen_mod_max = max_mod;
+       regen_mod_regen = regen_mod;
+       regen_mod_rot = rot_mod;
+       regen_mod_limit = limit_mod;
+       if(!MUTATOR_CALLHOOK(PlayerRegen))
+       if(!self.frozen)
+       {
+               float minh, mina, maxh, maxa, limith, limita;
+               maxh = autocvar_g_balance_health_rotstable;
+               maxa = autocvar_g_balance_armor_rotstable;
+               minh = autocvar_g_balance_health_regenstable;
+               mina = autocvar_g_balance_armor_regenstable;
+               limith = autocvar_g_balance_health_limit;
+               limita = autocvar_g_balance_armor_limit;
++
+               max_mod = regen_mod_max;
+               regen_mod = regen_mod_regen;
+               rot_mod = regen_mod_rot;
+               limit_mod = regen_mod_limit;
+               maxh = maxh * max_mod;
+               minh = minh * max_mod;
+               limith = limith * limit_mod;
+               limita = limita * limit_mod;
  
-       maxh = maxh * max_mod;
-       //maxa = maxa * max_mod;
-       //maxf = maxf * max_mod;
-       minh = minh * max_mod;
-       //mina = mina * max_mod;
-       //minf = minf * max_mod;
-       limith = limith * limit_mod;
-       limita = limita * limit_mod;
-       //limitf = limitf * limit_mod;
+               self.armorvalue = CalcRotRegen(self.armorvalue, mina, autocvar_g_balance_armor_regen, autocvar_g_balance_armor_regenlinear, regen_mod * frametime * (time > self.pauseregen_finished), maxa, autocvar_g_balance_armor_rot, autocvar_g_balance_armor_rotlinear, rot_mod * frametime * (time > self.pauserotarmor_finished), limita);
+               self.health = CalcRotRegen(self.health, minh, autocvar_g_balance_health_regen, autocvar_g_balance_health_regenlinear, regen_mod * frametime * (time > self.pauseregen_finished), maxh, autocvar_g_balance_health_rot, autocvar_g_balance_health_rotlinear, rot_mod * frametime * (time > self.pauserothealth_finished), limith);
+       }
  
-       if(g_ca)
-               rot_mod = 0;
+       // if player rotted to death...  die!
+       // check this outside above checks, as player may still be able to rot to death
+       if(self.health < 1)
+               self.event_damage(self, self, 1, DEATH_ROT, self.origin, '0 0 0');
  
-       if (!g_minstagib && !g_ca && (!g_lms || autocvar_g_lms_regenerate))
+       if (!(self.items & IT_UNLIMITED_WEAPON_AMMO))
        {
-               self.armorvalue = CalcRotRegen(self.armorvalue, mina, autocvar_g_balance_armor_regen, autocvar_g_balance_armor_regenlinear, regen_mod * frametime * (time > self.pauseregen_finished), maxa, autocvar_g_balance_armor_rot, autocvar_g_balance_armor_rotlinear, rot_mod * frametime * (time > self.pauserotarmor_finished), limita);
-               self.health = CalcRotRegen(self.health, minh, autocvar_g_balance_health_regen, autocvar_g_balance_health_regenlinear, regen_mod * frametime * (time > self.pauseregen_finished), maxh, autocvar_g_balance_health_rot, autocvar_g_balance_health_rotlinear, rot_mod * frametime * (time > self.pauserothealth_finished), limith);
+               float minf, maxf, limitf;
  
-               // if player rotted to death...  die!
-               if(self.health < 1)
-                       self.event_damage(self, self, 1, DEATH_ROT, self.origin, '0 0 0');
-       }
+               maxf = autocvar_g_balance_fuel_rotstable;
+               minf = autocvar_g_balance_fuel_regenstable;
+               limitf = autocvar_g_balance_fuel_limit;
  
-       if not(self.items & IT_UNLIMITED_WEAPON_AMMO)
-               self.ammo_fuel = CalcRotRegen(self.ammo_fuel, minf, autocvar_g_balance_fuel_regen, autocvar_g_balance_fuel_regenlinear, regen_mod * frametime * (time > self.pauseregen_finished) * ((self.items & IT_FUEL_REGEN) != 0), maxf, autocvar_g_balance_fuel_rot, autocvar_g_balance_fuel_rotlinear, rot_mod * frametime * (time > self.pauserotfuel_finished), limitf);
+               self.ammo_fuel = CalcRotRegen(self.ammo_fuel, minf, autocvar_g_balance_fuel_regen, autocvar_g_balance_fuel_regenlinear, frametime * (time > self.pauseregen_finished) * ((self.items & IT_FUEL_REGEN) != 0), maxf, autocvar_g_balance_fuel_rot, autocvar_g_balance_fuel_rotlinear, frametime * (time > self.pauserotfuel_finished), limitf);
+       }
  }
  
  float zoomstate_set;
@@@ -1774,7 -1752,7 +1752,6 @@@ float SpectateUpdate(
        return 1;
  }
  
--
  float SpectateSet()
  {
        if(self.enemy.classname != "player")
@@@ -2594,8 -2623,8 +2622,6 @@@ void PlayerPostThink (void
        }
        */
  
--      //pointparticles(particleeffectnum("machinegun_impact"), self.origin + self.view_ofs + '0 0 7', '0 0 0', 1);
--
        if(self.waypointsprite_attachedforcarrier)
                WaypointSprite_UpdateHealth(self.waypointsprite_attachedforcarrier, '1 0 0' * healtharmor_maxdamage(self.health, self.armorvalue, autocvar_g_balance_armor_blockpercent, DEATH_WEAPON));
  
Simple merge
@@@ -150,9 -150,11 +150,11 @@@ void ClientCommand_join(float request
                {
                        if(IS_CLIENT(self))
                        {
-                               if(!IS_PLAYER(self) && !lockteams && !g_arena)
 -                              if(!IS_PLAYER(self) && !lockteams)
++                              if(!IS_PLAYER(self) && !lockteams && !gameover)
                                {
-                                       if(nJoinAllowed(self)) 
+                                       if(self.caplayer)
+                                               return;
+                                       if(nJoinAllowed(self))
                                        {
                                                if(autocvar_g_campaign) { campaign_bots_may_start = 1; }
  
@@@ -302,7 -461,19 +461,19 @@@ void ClientCommand_selectteam(float req
                                                                        else if(self.wasplayer && autocvar_g_changeteam_banned)
                                                                                sprint(self, "^1You cannot change team, forbidden by the server.\n");
                                                                        else
 -                                                                                              sprint(self, "Cannot change to a larger/better/shinier team\n");
+                                                                       {
+                                                                               if(autocvar_g_balance_teams && autocvar_g_balance_teams_prevent_imbalance)
+                                                                               {
+                                                                                       CheckAllowedTeams(self);
+                                                                                       GetTeamCounts(self);
+                                                                                       if(!TeamSmallerEqThanTeam(Team_TeamToNumber(selection), Team_TeamToNumber(self.team), self))
+                                                                                       {
++                                                                                              Send_Notification(NOTIF_ONE, self, MSG_INFO, INFO_TEAMCHANGE_LARGERTEAM);
+                                                                                               return;
+                                                                                       }
+                                                                               }
                                                                                ClientKill_TeamChange(selection);
+                                                                       }
                                                                }
                                                        }
                                                        else
@@@ -435,15 -478,17 +478,14 @@@ void GameCommand_cointoss(float request
        {
                case CMD_REQUEST_COMMAND:
                {
 -                      entity client;
 -                      string result1 = (argv(2) ? strcat("^7", argv(1), "^3!\n") : "^1HEADS^3!\n");
 -                      string result2 = (argv(2) ? strcat("^7", argv(2), "^3!\n") : "^4TAILS^3!\n");
 +                      string result1 = (argv(2) ? strcat("^7", argv(1)) : "^1HEADS");
 +                      string result2 = (argv(2) ? strcat("^7", argv(2)) : "^4TAILS");
                        string choice = ((random() > 0.5) ? result1 : result2);
 -
 -                      FOR_EACH_CLIENT(client)
 -                              centerprint(client, strcat("^3Throwing coin... Result: ", choice));
 -                      bprint(strcat("^3Throwing coin... Result: ", choice));
 +                      
 +                      Send_Notification(NOTIF_ALL, world, MSG_MULTI, MULTI_COINTOSS, choice);
-                       
                        return;
                }
-               
                default:
                case CMD_REQUEST_USAGE:
                {
Simple merge
@@@ -338,10 -338,10 +338,10 @@@ void trigger_keylock_touch(void) 
                } else if (other.key_door_messagetime <= time) {
                        // no keys were given
                        play2(other, self.noise2);
 -                      centerprint(other, strcat("You need ", item_keys_keylist(self.itemkeys), "!"));
 +                      Send_Notification(NOTIF_ONE, other, MSG_CENTER, CENTER_DOOR_LOCKED_NEED, item_keys_keylist(self.itemkeys));
                        other.key_door_messagetime = time + 2;
                }
-               
                // trigger target2
                if (self.delay <= time || started_delay == TRUE)
                if (self.target2) {
@@@ -539,10 -523,10 +516,10 @@@ void havocbot_ast_reset_role(entity bot
  MUTATOR_HOOKFUNCTION(assault_PlayerSpawn)
  {
        if(self.team == assault_attacker_team)
 -              centerprint(self, "You are attacking!");
 +              Send_Notification(NOTIF_ONE, self, MSG_CENTER, CENTER_ASSAULT_ATTACKING);
        else
 -              centerprint(self, "You are defending!");
 +              Send_Notification(NOTIF_ONE, self, MSG_CENTER, CENTER_ASSAULT_DEFENDING);
-               
        return FALSE;
  }
  
Simple merge
Simple merge