]> git.rm.cloudns.org Git - xonotic/xonotic-data.pk3dir.git/commitdiff
Remove the g_ prefix from some server code files and rename sv_main to main
authorMario <mario.mario@y7mail.com>
Sun, 2 Aug 2020 12:22:45 +0000 (22:22 +1000)
committerMario <mario.mario@y7mail.com>
Sun, 2 Aug 2020 12:22:45 +0000 (22:22 +1000)
78 files changed:
qcsrc/common/gamemodes/gamemode/assault/sv_assault.qc
qcsrc/common/gamemodes/gamemode/ctf/sv_ctf.qc
qcsrc/common/gamemodes/gamemode/cts/sv_cts.qc
qcsrc/common/gamemodes/gamemode/domination/sv_domination.qc
qcsrc/common/gamemodes/gamemode/invasion/sv_invasion.qc
qcsrc/common/gamemodes/gamemode/keepaway/sv_keepaway.qc
qcsrc/common/gamemodes/gamemode/keyhunt/sv_keyhunt.qc
qcsrc/common/gamemodes/gamemode/lms/sv_lms.qc
qcsrc/common/gamemodes/gamemode/onslaught/sv_onslaught.qc
qcsrc/common/gamemodes/gamemode/race/sv_race.qc
qcsrc/common/mapobjects/func/breakable.qc
qcsrc/common/mapobjects/target/spawn.qc
qcsrc/common/mapobjects/teleporters.qc
qcsrc/common/mapobjects/trigger/jumppads.qc
qcsrc/common/monsters/monster.qh
qcsrc/common/monsters/sv_monsters.qc
qcsrc/common/mutators/mutator/breakablehook/sv_breakablehook.qc
qcsrc/common/mutators/mutator/buffs/sv_buffs.qc
qcsrc/common/notifications/all.qc
qcsrc/common/physics/player.qc
qcsrc/common/playerstats.qc
qcsrc/common/turrets/sv_turrets.qc
qcsrc/common/vehicles/sv_vehicles.qc
qcsrc/common/weapons/all.qc
qcsrc/common/weapons/weapon/shockwave.qc
qcsrc/server/_mod.inc
qcsrc/server/_mod.qh
qcsrc/server/anticheat.qc
qcsrc/server/bot/default/bot.qc
qcsrc/server/bot/default/havocbot/havocbot.qc
qcsrc/server/campaign.qc
qcsrc/server/cheats.qc
qcsrc/server/client.qc
qcsrc/server/clientkill.qc
qcsrc/server/command/cmd.qc
qcsrc/server/command/common.qc
qcsrc/server/command/common.qh
qcsrc/server/command/getreplies.qc
qcsrc/server/command/getreplies.qh
qcsrc/server/command/radarmap.qc
qcsrc/server/command/sv_cmd.qc
qcsrc/server/command/vote.qc
qcsrc/server/command/vote.qh
qcsrc/server/damage.qc [new file with mode: 0644]
qcsrc/server/damage.qh [new file with mode: 0644]
qcsrc/server/g_damage.qc [deleted file]
qcsrc/server/g_damage.qh [deleted file]
qcsrc/server/g_hook.qc [deleted file]
qcsrc/server/g_hook.qh [deleted file]
qcsrc/server/g_world.qc [deleted file]
qcsrc/server/g_world.qh [deleted file]
qcsrc/server/hook.qc [new file with mode: 0644]
qcsrc/server/hook.qh [new file with mode: 0644]
qcsrc/server/impulse.qc
qcsrc/server/items/items.qc
qcsrc/server/main.qc [new file with mode: 0644]
qcsrc/server/main.qh [new file with mode: 0644]
qcsrc/server/mapvoting.qc
qcsrc/server/miscfunctions.qc
qcsrc/server/miscfunctions.qh
qcsrc/server/player.qc
qcsrc/server/portals.qc
qcsrc/server/race.qc
qcsrc/server/round_handler.qc
qcsrc/server/scores.qc
qcsrc/server/spawnpoints.qc
qcsrc/server/sv_main.qc [deleted file]
qcsrc/server/sv_main.qh [deleted file]
qcsrc/server/teamplay.qc
qcsrc/server/weapons/accuracy.qc
qcsrc/server/weapons/common.qc
qcsrc/server/weapons/hitplot.qc
qcsrc/server/weapons/throwing.qc
qcsrc/server/weapons/tracing.qc
qcsrc/server/weapons/weaponstats.qc
qcsrc/server/weapons/weaponsystem.qc
qcsrc/server/world.qc [new file with mode: 0644]
qcsrc/server/world.qh [new file with mode: 0644]

index 3915429d63f1eaab0f47357384641febd91943e1..fcfcf32ff38cc082fd36e18f26f06f59fe19aefd 100644 (file)
@@ -4,8 +4,8 @@
 #include <common/mapobjects/func/breakable.qh>
 #include <common/mapobjects/triggers.qh>
 #include <common/turrets/sv_turrets.qh>
-#include <server/g_damage.qh>
-#include <server/g_world.qh>
+#include <server/damage.qh>
+#include <server/world.qh>
 #include <server/spawnpoints.qh>
 
 .entity sprite;
index ec296a9c9cec51484a6c66e97c4e091928ca36f9..7e436665953388366df74999cbcb7f2bdc5dbe3b 100644 (file)
@@ -7,8 +7,8 @@
 #include <server/command/vote.qh>
 #include <server/client.qh>
 #include <server/gamelog.qh>
-#include <server/g_damage.qh>
-#include <server/g_world.qh>
+#include <server/damage.qh>
+#include <server/world.qh>
 #include <server/items/items.qh>
 #include <server/race.qh>
 #include <server/teamplay.qh>
@@ -2205,7 +2205,7 @@ MUTATOR_HOOKFUNCTION(ctf, PlayerPreThink)
                WaypointSprite_UpdateHealth(player.wps_flagcarrier, healtharmor_maxdamage(GetResource(player, RES_HEALTH), GetResource(player, RES_ARMOR), autocvar_g_balance_armor_blockpercent, DEATH_WEAPON.m_id).x);
 }
 
-MUTATOR_HOOKFUNCTION(ctf, Damage_Calculate) // for changing damage and force values that are applied to players in g_damage.qc
+MUTATOR_HOOKFUNCTION(ctf, Damage_Calculate) // for changing damage and force values that are applied to players in damage.qc
 {
        entity frag_attacker = M_ARGV(1, entity);
        entity frag_target = M_ARGV(2, entity);
@@ -2358,7 +2358,7 @@ MUTATOR_HOOKFUNCTION(ctf, PlayerUseKey)
                                if(head != player && SAME_TEAM(head, player))
                                if(!head.speedrunning && !head.vehicle)
                                {
-                                       // if it's a player, use the view origin as reference (stolen from RadiusDamage functions in g_damage.qc)
+                                       // if it's a player, use the view origin as reference (stolen from RadiusDamage functions in damage.qc)
                                        vector head_center = WarpZone_UnTransformOrigin(head, CENTER_OR_VIEWOFS(head));
                                        vector passer_center = CENTER_OR_VIEWOFS(player);
 
index 22d021cf546d5be390c43c60e598bbfb5c2edaf5..1550c8e8927878d1320270d8ee3e7470217a4a3d 100644 (file)
@@ -2,7 +2,7 @@
 
 #include <server/client.qh>
 #include <server/race.qh>
-#include <server/g_world.qh>
+#include <server/world.qh>
 #include <server/gamelog.qh>
 #include <server/items/spawning.qh>
 #include <server/weapons/common.qh>
index 410d07e8f1164fc1fc3933241eb7554cf7d338ad..0a6ee791a6d91073353a1fca81e2dcb1d96c9816 100644 (file)
@@ -2,7 +2,7 @@
 
 #include <server/client.qh>
 #include <server/command/vote.qh>
-#include <server/g_damage.qh>
+#include <server/damage.qh>
 #include <server/gamelog.qh>
 #include <server/items/items.qh>
 #include <server/teamplay.qh>
index 09be394fb14ebed6751320f46ea3f067add7fc82..d74c2dfc2d12e474b84efd4b15bb86dcfc4d7319 100644 (file)
@@ -6,7 +6,7 @@
 #include <common/monsters/sv_monsters.qh>
 
 #include <server/bot/api.qh>
-#include <server/g_world.qh>
+#include <server/world.qh>
 #include <server/teamplay.qh>
 
 IntrusiveList g_invasion_roundends;
index 6ce13f2e99563e73ceb973a99e180e032d456f25..dda6185cf4366f5657e760cad907932c0a2732d6 100644 (file)
@@ -3,7 +3,7 @@
 #include <common/effects/all.qh>
 #include <server/client.qh>
 #include <server/gamelog.qh>
-#include <server/g_damage.qh>
+#include <server/damage.qh>
 #include <server/items/items.qh>
 
 .entity ballcarried;
@@ -384,7 +384,7 @@ MUTATOR_HOOKFUNCTION(ka, PlayerUseKey)
        }
 }
 
-MUTATOR_HOOKFUNCTION(ka, Damage_Calculate) // for changing damage and force values that are applied to players in g_damage.qc
+MUTATOR_HOOKFUNCTION(ka, Damage_Calculate) // for changing damage and force values that are applied to players in damage.qc
 {
        entity frag_attacker = M_ARGV(1, entity);
        entity frag_target = M_ARGV(2, entity);
index 5e098c67cc3012d41f9dc6f7cb4f155f2a307dc0..d48c76f3943313648aa1a4f39743a3b42845efff 100644 (file)
@@ -2,7 +2,7 @@
 
 #include <server/command/vote.qh>
 #include <server/gamelog.qh>
-#include <server/g_damage.qh>
+#include <server/damage.qh>
 #include <server/items/items.qh>
 #include <common/mapobjects/triggers.qh>
 
index a206fcbde36aefd27fa92305b834d357a0ea770c..1b7b527d8633dbde2a25f65004b245426b6042e6 100644 (file)
@@ -3,7 +3,7 @@
 #include <common/mutators/mutator/instagib/items.qh>
 #include <server/campaign.qh>
 #include <server/command/_mod.qh>
-#include <server/g_world.qh>
+#include <server/world.qh>
 #include <server/items/items.qh>
 
 int autocvar_g_lms_extra_lives;
index 6bb2387dd3362d653620a6288790d979786fca14..375edafb45d1d6074435c2a6d5e3a880aca0fd23 100644 (file)
@@ -4,7 +4,7 @@
 
 #include <server/bot/api.qh>
 #include <server/command/vote.qh>
-#include <server/g_damage.qh>
+#include <server/damage.qh>
 #include <server/items/items.qh>
 #include <common/mapobjects/defs.qh>
 #include <common/mapobjects/triggers.qh>
index f56353d0136aa5391c818b015ac8f0c4edd5be36..5c6e52f397389e8b4e6f03b4b08d029d1a6819a6 100644 (file)
@@ -1,7 +1,7 @@
 #include "sv_race.qh"
 
 #include <server/client.qh>
-#include <server/g_world.qh>
+#include <server/world.qh>
 #include <server/gamelog.qh>
 #include <server/race.qh>
 #include <common/ent_cs.qh>
index 24d7139435dc9bc7dcb3c5e1bd1ece102611ea71..4db6516580a6212880148ae30f574505dc115c3d 100644 (file)
@@ -1,7 +1,7 @@
 #include "breakable.qh"
 #ifdef SVQC
 
-#include <server/g_damage.qh>
+#include <server/damage.qh>
 #include <server/bot/api.qh>
 #include <common/csqcmodel_settings.qh>
 #include <lib/csqcmodel/sv_model.qh>
index 0c607934cbc95a07bae22cbe7109350d9175955e..b96370d18e75839838a6c1a6900170dbc230b386 100644 (file)
@@ -5,7 +5,7 @@
     #include <common/util.qh>
     #include <common/weapons/_all.qh>
     #include <common/stats.qh>
-    #include <server/g_world.qh>
+    #include <server/world.qh>
 #endif
 
 #ifdef SVQC
index 24b7fb99cb4222c144419d515a74d7fb1376432d..93bfcbb2683e89de0a4da2cfdb96b589e2b374fc 100644 (file)
@@ -15,7 +15,7 @@
     #include <common/weapons/_all.qh>
     #include <common/stats.qh>
     #include "../deathtypes/all.qh"
-    #include <server/sv_main.qh>
+    #include <server/main.qh>
     #include "../turrets/sv_turrets.qh"
     #include "../vehicles/all.qh"
     #include <common/gamemodes/_mod.qh>
index 66a9499d4bb13bf4fd392a5c3d1fd219e2f66ba5..5273179adfeb047faeea7f8d7c7ab518cebc9d60 100644 (file)
@@ -2,7 +2,7 @@
 // TODO: split target_push and put it in the target folder
 #ifdef SVQC
 #include <common/physics/movetypes/movetypes.qh>
-#include <server/sv_main.qh>
+#include <server/main.qh>
 
 void trigger_push_use(entity this, entity actor, entity trigger)
 {
index 146f64d405533ca59aac5636f81720f16d922157..5d79c4a838a0e05e1227655c89b6d1c015ef07e6 100644 (file)
@@ -59,7 +59,7 @@ ENDCLASS(Monster)
 
 #ifdef SVQC
 #include "sv_monsters.qh"
-#include <server/g_damage.qh>
+#include <server/damage.qh>
 #include <server/bot/api.qh>
 #include <server/weapons/common.qh>
 #include <server/weapons/tracing.qh>
index 46bb1593a050b1d937936e78c9412fd31fb10a53..d5bff8f0239f682727e8e09eac47c221329b3037 100644 (file)
 #include <server/autocvars.qh>
 #include <common/weapons/_all.qh>
 #include <common/stats.qh>
-#include <server/g_damage.qh>
+#include <server/damage.qh>
 #include "../deathtypes/all.qh"
 #include <server/items/items.qh>
 #include <server/mutators/_mod.qh>
 #include <server/steerlib.qh>
-#include <server/sv_main.qh>
+#include <server/main.qh>
 #include "../turrets/sv_turrets.qh"
 #include "../turrets/util.qh"
 #include "../vehicles/all.qh"
@@ -135,7 +135,7 @@ entity Monster_FindTarget(entity this)
        {
                if(Monster_ValidTarget(this, it))
                {
-                       // if it's a player, use the view origin as reference (stolen from RadiusDamage functions in g_damage.qc)
+                       // if it's a player, use the view origin as reference (stolen from RadiusDamage functions in damage.qc)
                        vector targ_center = CENTER_OR_VIEWOFS(it);
 
                        if(closest_target)
index dc1bfefc264b20d7cd9f6606723facb78bee9980..c80b97d36b1d711dd575c94bf9123f2d1bd1a234 100644 (file)
@@ -1,7 +1,7 @@
 #include "sv_breakablehook.qh"
 
 #include <common/deathtypes/all.qh>
-#include <server/g_hook.qh>
+#include <server/hook.qh>
 
 REGISTER_MUTATOR(breakablehook, cvar("g_breakablehook"));
 
index 29828a094c9473b354532b27a41bd17d9e370759..30cef339a69a4249785e6f04344b451f5427691b 100644 (file)
@@ -3,7 +3,7 @@
 #include <common/mapobjects/target/music.qh>
 #include <common/gamemodes/_mod.qh>
 #include <server/items/items.qh>
-#include <server/sv_main.qh>
+#include <server/main.qh>
 
 void buffs_DelayedInit(entity this);
 
index c8fb372d6bbbe74fb826fe67bcfa08f88b4d5412..c46c21722b69e201802b2ade22a3b81fb8de49d9 100644 (file)
@@ -8,7 +8,7 @@
        #include <common/teams.qh>
        #include <server/autocvars.qh>
        #include <server/constants.qh>
-       #include <server/g_world.qh>
+       #include <server/world.qh>
        #include <server/mutators/_mod.qh>
 #endif
 
index af1ae0ad181ad83d2ade2a7afc85c1024652ac66..ade9c4d6f8acc5a90f0411b680ab4ab34467dcf5 100644 (file)
@@ -8,7 +8,7 @@
 #include <server/miscfunctions.qh>
 #include <common/mapobjects/defs.qh>
 #include "../mapobjects/trigger/viewloc.qh"
-#include <server/sv_main.qh>
+#include <server/main.qh>
 
 // client side physics
 bool Physics_Valid(string thecvar)
index d91e16f4700a1f7b887b7115b2beda58476b310a..3b8c97c4b81626f6d292155e1831269540559b36 100644 (file)
@@ -9,7 +9,7 @@
     #include "../server/anticheat.qh"
     #include <common/stats.qh>
     #include "../server/scores.qh"
-       #include <server/g_world.qh>
+       #include <server/world.qh>
     #include "../server/weapons/accuracy.qh"
 #endif
 
@@ -296,7 +296,7 @@ void PlayerStats_GameReport_Handler(entity fh, entity pass, float status)
                 * G: game type
                 * O: mod name (icon request) as in server browser
                 * M: map name
-                * I: match ID (see "matchid" in g_world.qc)
+                * I: match ID (see "matchid" in world.qc)
                 * S: "hostname" of the server
                 * C: number of "unpure" cvar changes
                 * U: UDP port number of the server
index 84b0c824908da1c96a49b618495b34fcbcb748f2..e2a9ab2e4131bee26615108eb55873baef2c27a1 100644 (file)
@@ -2,7 +2,7 @@
 #ifdef SVQC
 #include <server/autocvars.qh>
 #include <server/bot/api.qh>
-#include <server/g_damage.qh>
+#include <server/damage.qh>
 #include <server/weapons/common.qh>
 #include <server/weapons/weaponsystem.qh>
 #include <common/mapobjects/defs.qh>
index 590aba88a74e3db161e43de38b34bc3fbf1dfba0..beff57489779392c07f7e1ccc876d56eb10bed4c 100644 (file)
@@ -1,14 +1,14 @@
 #include "sv_vehicles.qh"
 
 #include <server/bot/api.qh>
-#include <server/g_damage.qh>
-#include <server/g_world.qh>
+#include <server/damage.qh>
+#include <server/world.qh>
 #include <server/items/items.qh>
 #include <common/items/_mod.qh>
 #include <common/mapobjects/defs.qh>
 #include <common/mapobjects/teleporters.qh>
 #include <common/mapobjects/triggers.qh>
-#include <server/sv_main.qh>
+#include <server/main.qh>
 #include <server/weapons/common.qh>
 
 bool SendAuxiliaryXhair(entity this, entity to, int sf)
index 62e4ff0e014d9f48fdfcfebfdb5724ca5c17b809..dc87d6b6276f74cf20c5a74f8df655af49ab1492 100644 (file)
@@ -41,7 +41,7 @@
     #include <server/command/_mod.qh>
     #include <lib/csqcmodel/sv_model.qh>
     #include <server/portals.qh>
-    #include <server/g_hook.qh>
+    #include <server/hook.qh>
 #endif
 #ifdef GAMEQC
        #include "calculations.qc"
index f38c34b19791182d9127b58a6459d12d8cc332db..accb917f687e460c05108fd8fd91f71e756f3ecb 100644 (file)
@@ -479,7 +479,7 @@ void W_Shockwave_Attack(Weapon thiswep, entity actor, .entity weaponentity)
                        //  BLAST CONE CALCULATION
                        // ========================
 
-                       // if it's a player, use the view origin as reference (stolen from RadiusDamage functions in g_damage.qc)
+                       // if it's a player, use the view origin as reference (stolen from RadiusDamage functions in damage.qc)
                        center = CENTER_OR_VIEWOFS(head);
 
                        // find the closest point on the enemy to the center of the attack
index 7634e76357e6e1eade967a9bfed90eb4b52853b6..0da2e2be4cc5f09c8d3452f798ab10801619b675 100644 (file)
@@ -5,13 +5,14 @@
 #include <server/cheats.qc>
 #include <server/client.qc>
 #include <server/clientkill.qc>
-#include <server/g_damage.qc>
-#include <server/g_hook.qc>
-#include <server/g_world.qc>
+#include <server/damage.qc>
+#include <server/hook.qc>
+#include <server/world.qc>
 #include <server/gamelog.qc>
 #include <server/handicap.qc>
 #include <server/impulse.qc>
 #include <server/ipban.qc>
+#include <server/main.qc>
 #include <server/mapvoting.qc>
 #include <server/matrix.qc>
 #include <server/miscfunctions.qc>
@@ -24,9 +25,6 @@
 #include <server/scores_rules.qc>
 #include <server/spawnpoints.qc>
 #include <server/steerlib.qc>
-#ifdef SVQC
-    #include <server/sv_main.qc>
-#endif
 #include <server/teamplay.qc>
 #include <server/tests.qc>
 
index 331fdcd854672a67ffee5a49ae8736165cdbdef7..67080b3687838d623afcfe4e11e8eaa602730df8 100644 (file)
@@ -5,13 +5,14 @@
 #include <server/cheats.qh>
 #include <server/client.qh>
 #include <server/clientkill.qh>
-#include <server/g_damage.qh>
-#include <server/g_hook.qh>
-#include <server/g_world.qh>
+#include <server/damage.qh>
+#include <server/hook.qh>
+#include <server/world.qh>
 #include <server/gamelog.qh>
 #include <server/handicap.qh>
 #include <server/impulse.qh>
 #include <server/ipban.qh>
+#include <server/main.qh>
 #include <server/mapvoting.qh>
 #include <server/matrix.qh>
 #include <server/miscfunctions.qh>
@@ -24,9 +25,6 @@
 #include <server/scores_rules.qh>
 #include <server/spawnpoints.qh>
 #include <server/steerlib.qh>
-#ifdef SVQC
-    #include <server/sv_main.qh>
-#endif
 #include <server/teamplay.qh>
 #include <server/tests.qh>
 
index c0d1cc10e783e0f7d9581061dc56aabdcb9fcd34..7e8e5066020e19df88acccd737838c93184a6b63 100644 (file)
@@ -6,7 +6,7 @@
 #include <common/stats.qh>
 #include <server/client.qh>
 #include <server/gamelog.qh>
-#include <server/sv_main.qh>
+#include <server/main.qh>
 #include "miscfunctions.qh"
 
 #include "command/common.qh"
index 9d526b74db23220274985dace5b55ec009ded3c9..e8f810231baf45744c1186e9aaf45a03130a5d65 100644 (file)
@@ -18,8 +18,8 @@
 #include "../../client.qh"
 #include "../../constants.qh"
 #include <common/stats.qh>
-#include <server/g_world.qh>
-#include <server/g_damage.qh>
+#include <server/world.qh>
+#include <server/damage.qh>
 #include "../../race.qh"
 #include <server/items/items.qh>
 
index f6917bdb4d41e2fa9e5c52b9b6dbef24df3336f2..40a07a44c43c1d23422b88edcfd438bf95ec00d5 100644 (file)
@@ -6,7 +6,7 @@
 #include <server/client.qh>
 #include <common/weapons/_all.qh>
 #include <common/stats.qh>
-#include <server/g_damage.qh>
+#include <server/damage.qh>
 #include <server/items/items.qh>
 #include <server/miscfunctions.qh>
 #include <server/weapons/selection.qh>
index 92d183a4515094da1f4626cc3ff84dfeb73bfe6a..de46e57d508f12fcdb37a95f38da8ebee27da7ac 100644 (file)
@@ -5,7 +5,7 @@
 
 #include "cheats.qh"
 #include "miscfunctions.qh"
-#include "g_world.qh"
+#include "world.qh"
 
 #include "../common/campaign_common.qh"
 
index dc41fac73ad11aa701b7d51518f7aa9f8cf8db03..9d5cc1d42e3a051760ad5c528382d6448b465a2a 100644 (file)
@@ -5,9 +5,9 @@
 #include <server/miscfunctions.qh>
 #include <common/effects/all.qh>
 #include <server/resources.qh>
-#include <server/sv_main.qh>
+#include <server/main.qh>
 
-#include "g_damage.qh"
+#include "damage.qh"
 #include "clientkill.qh"
 #include "player.qh"
 #include "race.qh"
index 99aa86d24f82d7d233fc12f6fff56e2661252376..0a9a38fafe3c6b68666345b36b46a000c08a5246 100644 (file)
 #include "teamplay.qh"
 #include "spawnpoints.qh"
 #include "resources.qh"
-#include "g_damage.qh"
+#include "damage.qh"
 #include "handicap.qh"
-#include "g_hook.qh"
+#include "hook.qh"
 #include "command/common.qh"
 #include "command/vote.qh"
 #include "clientkill.qh"
 #include "cheats.qh"
-#include "g_world.qh"
+#include "world.qh"
 #include <server/gamelog.qh>
 #include "race.qh"
-#include <server/sv_main.qh>
+#include <server/main.qh>
 #include "antilag.qh"
 #include "campaign.qh"
 #include "command/common.qh"
index 600faed5a3776d9f9891f7358b495f306e91e963..73ccb383d40fe3f2c355b325c3eb3846f15e8541 100644 (file)
@@ -5,7 +5,7 @@
 #include <server/client.qh>
 #include <server/player.qh>
 
-#include "g_damage.qh"
+#include "damage.qh"
 #include "teamplay.qh"
 
 #include <common/vehicles/sv_vehicles.qh>
index 815c5e23f282b8d203e64fde9649d85199ecca8d..fe92d36673552852495602cd46b6d798450b752f 100644 (file)
@@ -1,6 +1,6 @@
 #include "cmd.qh"
 
-#include <server/g_world.qh>
+#include <server/world.qh>
 #include <server/miscfunctions.qh>
 
 #include <common/command/_mod.qh>
index 48f1cd16825103b4e48dcef3ebe48e53e8307a59..0f59802dd5960e3bdfb72230a7d2d877584ee00a 100644 (file)
@@ -3,7 +3,7 @@
 #include <server/client.qh>
 #include <common/weapons/_all.qh>
 #include <common/stats.qh>
-#include <server/g_world.qh>
+#include <server/world.qh>
 #include <server/miscfunctions.qh>
 
 #include <common/command/_mod.qh>
index ea3af546935bb3903ee290a3e2e504d62be058ef..c9aad4b7971c18634935bdb0d6b4659349c2f4d3 100644 (file)
@@ -54,7 +54,7 @@ float timeout_status;    // (values: 0, 1, 2) contains whether a timeout is not
 .float allowed_timeouts; // contains the number of allowed timeouts for each player
 .vector lastV_angle;     // used when pausing the game in order to force the player to keep his old view angle fixed
 
-// allow functions to be used in other code like g_world.qc and teamplay.qc
+// allow functions to be used in other code like world.qc and teamplay.qc
 void timeout_handler_think(entity this);
 
 // used by common/command/generic.qc:GenericCommand_dumpcommands to list all commands into a .txt file
index c5a26e8c92b7d598032409ff4942fcc75b240a15..3262974898a1a284858dcf3ef325c6eb393569ec 100644 (file)
@@ -2,7 +2,7 @@
 
 #include <common/weapons/_all.qh>
 #include <common/stats.qh>
-#include <server/g_world.qh>
+#include <server/world.qh>
 #include <server/miscfunctions.qh>
 
 #include <common/command/_mod.qh>
@@ -22,7 +22,7 @@
 //  Last updated: December 30th, 2011
 // =========================================================
 
-// These strings are set usually during init in g_world.qc,
+// These strings are set usually during init in world.qc,
 // or also by some game modes or other functions manually,
 // and their purpose is to output information to clients
 // without using any extra processing time.
index 7b436079227f47c1d1621cb891b5bd87400122c0..3ababeec576028bb07b69722714e9ee16a2b2746 100644 (file)
@@ -13,7 +13,7 @@ const int LADDER_SIZE = 30; // ladder shows the top X players
 string top_uids[LADDER_SIZE];
 float top_scores[LADDER_SIZE];
 
-// allow functions to be used in other code like g_world.qc and race.qc
+// allow functions to be used in other code like world.qc and race.qc
 string getrecords(float page);
 string getrankings();
 string getladder();
index 7ec8eb6ea157bf0a1706251c2b26da5b754393b6..6e73777a0f0321e55bd02ae54749e6f13623d914 100644 (file)
@@ -4,7 +4,7 @@
 #include <common/command/_mod.qh>
 #include <common/mapobjects/triggers.qh>
 
-#include "../g_world.qh"
+#include "../world.qh"
 
 #include <common/util.qh>
 
index 034bd6cb92cc37004d364ff67334f355855c287d..7de740bdde1049fc7b1f8c7c8e4d797ef91df4ae 100644 (file)
@@ -13,7 +13,7 @@
 #include "../campaign.qh"
 #include "../client.qh"
 #include "../player.qh"
-#include "../g_world.qh"
+#include "../world.qh"
 #include "../ipban.qh"
 #include "../teamplay.qh"
 
index 2ad5c05106a691053b0b3e346cba21c56c556aef..ee8a8e8db9a1550ec56cd407d3d2d0e05d6ccede 100644 (file)
@@ -11,8 +11,8 @@
 
 #include "common.qh"
 
-#include "../g_damage.qh"
-#include "../g_world.qh"
+#include "../damage.qh"
+#include "../world.qh"
 #include "../teamplay.qh"
 #include "../race.qh"
 #include "../round_handler.qh"
index 55068ab462cc804e05017141e98a0eff606d3833..7085ca0e1bc457d992e593e9485a7e516b1e0f77 100644 (file)
@@ -38,7 +38,7 @@ string vote_called_display; // visual string of command sent by client
 string vote_parsed_command; // command which is fixed after being parsed
 string vote_parsed_display; // visual string which is fixed after being parsed
 
-// allow functions to be used in other code like g_world.qc and teamplay.qc
+// allow functions to be used in other code like world.qc and teamplay.qc
 void VoteThink();
 void VoteReset();
 void VoteCommand(int request, entity caller, int argc, string vote_command);
diff --git a/qcsrc/server/damage.qc b/qcsrc/server/damage.qc
new file mode 100644 (file)
index 0000000..f799e96
--- /dev/null
@@ -0,0 +1,1301 @@
+#include "damage.qh"
+
+#include <common/effects/all.qh>
+#include "bot/api.qh"
+#include "hook.qh"
+#include <server/client.qh>
+#include <server/gamelog.qh>
+#include <server/items/items.qh>
+#include <server/mutators/_mod.qh>
+#include <server/main.qh>
+#include "teamplay.qh"
+#include "scores.qh"
+#include "spawnpoints.qh"
+#include "../common/state.qh"
+#include "../common/physics/player.qh"
+#include "resources.qh"
+#include "../common/vehicles/all.qh"
+#include "../common/items/_mod.qh"
+#include "../common/mutators/mutator/waypoints/waypointsprites.qh"
+#include "../common/mutators/mutator/instagib/sv_instagib.qh"
+#include "../common/mutators/mutator/buffs/buffs.qh"
+#include "weapons/accuracy.qh"
+#include "weapons/csqcprojectile.qh"
+#include "weapons/selection.qh"
+#include "../common/constants.qh"
+#include "../common/deathtypes/all.qh"
+#include <common/mapobjects/defs.qh>
+#include <common/mapobjects/triggers.qh>
+#include "../common/notifications/all.qh"
+#include "../common/physics/movetypes/movetypes.qh"
+#include "../common/playerstats.qh"
+#include "../common/teams.qh"
+#include "../common/util.qh"
+#include <common/gamemodes/_mod.qh>
+#include <common/gamemodes/rules.qh>
+#include <common/weapons/_all.qh>
+#include "../lib/csqcmodel/sv_model.qh"
+#include "../lib/warpzone/common.qh"
+
+void UpdateFrags(entity player, int f)
+{
+       GameRules_scoring_add_team(player, SCORE, f);
+}
+
+void GiveFrags(entity attacker, entity targ, float f, int deathtype, .entity weaponentity)
+{
+       // TODO route through PlayerScores instead
+       if(game_stopped) return;
+
+       if(f < 0)
+       {
+               if(targ == attacker)
+               {
+                       // suicide
+                       GameRules_scoring_add(attacker, SUICIDES, 1);
+               }
+               else
+               {
+                       // teamkill
+                       GameRules_scoring_add(attacker, TEAMKILLS, 1);
+               }
+       }
+       else
+       {
+               // regular frag
+               GameRules_scoring_add(attacker, KILLS, 1);
+               if(!warmup_stage && targ.playerid)
+                       PlayerStats_GameReport_Event_Player(attacker, sprintf("kills-%d", targ.playerid), 1);
+       }
+
+       GameRules_scoring_add(targ, DEATHS, 1);
+
+       // FIXME fix the mess this is (we have REAL points now!)
+       if(MUTATOR_CALLHOOK(GiveFragsForKill, attacker, targ, f, deathtype, attacker.(weaponentity)))
+               f = M_ARGV(2, float);
+
+       attacker.totalfrags += f;
+
+       if(f)
+               UpdateFrags(attacker, f);
+}
+
+string AppendItemcodes(string s, entity player)
+{
+       for(int slot = 0; slot < MAX_WEAPONSLOTS; ++slot)
+       {
+               .entity weaponentity = weaponentities[slot];
+               int w = player.(weaponentity).m_weapon.m_id;
+               if(w == 0)
+                       w = player.(weaponentity).cnt; // previous weapon
+               if(w != 0 || slot == 0)
+                       s = strcat(s, ftos(w));
+       }
+       if(time < STAT(STRENGTH_FINISHED, player))
+               s = strcat(s, "S");
+       if(time < STAT(INVINCIBLE_FINISHED, player))
+               s = strcat(s, "I");
+       if(PHYS_INPUT_BUTTON_CHAT(player))
+               s = strcat(s, "T");
+       // TODO: include these codes as a flag on the item itself
+       MUTATOR_CALLHOOK(LogDeath_AppendItemCodes, player, s);
+       s = M_ARGV(1, string);
+       return s;
+}
+
+void LogDeath(string mode, int deathtype, entity killer, entity killed)
+{
+       string s;
+       if(!autocvar_sv_eventlog)
+               return;
+       s = strcat(":kill:", mode);
+       s = strcat(s, ":", ftos(killer.playerid));
+       s = strcat(s, ":", ftos(killed.playerid));
+       s = strcat(s, ":type=", Deathtype_Name(deathtype));
+       s = strcat(s, ":items=");
+       s = AppendItemcodes(s, killer);
+       if(killed != killer)
+       {
+               s = strcat(s, ":victimitems=");
+               s = AppendItemcodes(s, killed);
+       }
+       GameLogEcho(s);
+}
+
+void Obituary_SpecialDeath(
+       entity notif_target,
+       float murder,
+       int deathtype,
+       string s1, string s2, string s3,
+       float f1, float f2, float f3)
+{
+       if(!DEATH_ISSPECIAL(deathtype))
+       {
+               backtrace("Obituary_SpecialDeath called without a special deathtype?\n");
+               return;
+       }
+
+       entity deathent = REGISTRY_GET(Deathtypes, deathtype - DT_FIRST);
+       if (!deathent)
+       {
+               backtrace("Obituary_SpecialDeath: Could not find deathtype entity!\n");
+               return;
+       }
+
+       if(g_cts && deathtype == DEATH_KILL.m_id)
+               return; // TODO: somehow put this in CTS gamemode file!
+
+       Notification death_message = (murder) ? deathent.death_msgmurder : deathent.death_msgself;
+       if(death_message)
+       {
+               Send_Notification_WOCOVA(
+                       NOTIF_ONE,
+                       notif_target,
+                       MSG_MULTI,
+                       death_message,
+                       s1, s2, s3, "",
+                       f1, f2, f3, 0
+               );
+               Send_Notification_WOCOVA(
+                       NOTIF_ALL_EXCEPT,
+                       notif_target,
+                       MSG_INFO,
+                       death_message.nent_msginfo,
+                       s1, s2, s3, "",
+                       f1, f2, f3, 0
+               );
+       }
+}
+
+float Obituary_WeaponDeath(
+       entity notif_target,
+       float murder,
+       int deathtype,
+       string s1, string s2, string s3,
+       float f1, float f2)
+{
+       Weapon death_weapon = DEATH_WEAPONOF(deathtype);
+       if (death_weapon == WEP_Null)
+               return false;
+
+       w_deathtype = deathtype;
+       Notification death_message = ((murder) ? death_weapon.wr_killmessage(death_weapon) : death_weapon.wr_suicidemessage(death_weapon));
+       w_deathtype = false;
+
+       if (death_message)
+       {
+               Send_Notification_WOCOVA(
+                       NOTIF_ONE,
+                       notif_target,
+                       MSG_MULTI,
+                       death_message,
+                       s1, s2, s3, "",
+                       f1, f2, 0, 0
+               );
+               // send the info part to everyone
+               Send_Notification_WOCOVA(
+                       NOTIF_ALL_EXCEPT,
+                       notif_target,
+                       MSG_INFO,
+                       death_message.nent_msginfo,
+                       s1, s2, s3, "",
+                       f1, f2, 0, 0
+               );
+       }
+       else
+       {
+               LOG_TRACEF(
+                       "Obituary_WeaponDeath(): ^1Deathtype ^7(%d)^1 has no notification for weapon %s!\n",
+                       deathtype,
+                       death_weapon.netname
+               );
+       }
+
+       return true;
+}
+
+bool frag_centermessage_override(entity attacker, entity targ, int deathtype, int kill_count_to_attacker, int kill_count_to_target, string attacker_name)
+{
+       if(deathtype == DEATH_FIRE.m_id)
+       {
+               Send_Notification(NOTIF_ONE, attacker, MSG_CHOICE, CHOICE_FRAG_FIRE, targ.netname, kill_count_to_attacker, (IS_BOT_CLIENT(targ) ? -1 : CS(targ).ping));
+               Send_Notification(NOTIF_ONE, targ, MSG_CHOICE, CHOICE_FRAGGED_FIRE, attacker_name, kill_count_to_target, GetResource(attacker, RES_HEALTH), GetResource(attacker, RES_ARMOR), (IS_BOT_CLIENT(attacker) ? -1 : CS(attacker).ping));
+               return true;
+       }
+
+       return MUTATOR_CALLHOOK(FragCenterMessage, attacker, targ, deathtype, kill_count_to_attacker, kill_count_to_target);
+}
+
+void Obituary(entity attacker, entity inflictor, entity targ, int deathtype, .entity weaponentity)
+{
+       // Sanity check
+       if (!IS_PLAYER(targ)) { backtrace("Obituary called on non-player?!\n"); return; }
+
+       // Declarations
+       float notif_firstblood = false;
+       float kill_count_to_attacker, kill_count_to_target;
+       bool notif_anonymous = false;
+       string attacker_name = attacker.netname;
+
+       // Set final information for the death
+       targ.death_origin = targ.origin;
+       string deathlocation = (autocvar_notification_server_allows_location ? NearestLocation(targ.death_origin) : "");
+
+       // Abort now if a mutator requests it
+       if (MUTATOR_CALLHOOK(ClientObituary, inflictor, attacker, targ, deathtype, attacker.(weaponentity))) { CS(targ).killcount = 0; return; }
+       notif_anonymous = M_ARGV(5, bool);
+
+       if(notif_anonymous)
+               attacker_name = "Anonymous player";
+
+       #ifdef NOTIFICATIONS_DEBUG
+       Debug_Notification(
+               sprintf(
+                       "Obituary(%s, %s, %s, %s = %d);\n",
+                       attacker_name,
+                       inflictor.netname,
+                       targ.netname,
+                       Deathtype_Name(deathtype),
+                       deathtype
+               )
+       );
+       #endif
+
+       // =======
+       // SUICIDE
+       // =======
+       if(targ == attacker)
+       {
+               if(DEATH_ISSPECIAL(deathtype))
+               {
+                       if(deathtype == DEATH_TEAMCHANGE.m_id || deathtype == DEATH_AUTOTEAMCHANGE.m_id)
+                       {
+                               Obituary_SpecialDeath(targ, false, deathtype, targ.netname, deathlocation, "", targ.team, 0, 0);
+                       }
+                       else
+                       {
+                               switch(DEATH_ENT(deathtype))
+                               {
+                                       case DEATH_MIRRORDAMAGE:
+                                       {
+                                               Obituary_SpecialDeath(targ, false, deathtype, targ.netname, deathlocation, "", CS(targ).killcount, 0, 0);
+                                               break;
+                                       }
+
+                                       default:
+                                       {
+                                               Obituary_SpecialDeath(targ, false, deathtype, targ.netname, deathlocation, "", CS(targ).killcount, 0, 0);
+                                               break;
+                                       }
+                               }
+                       }
+               }
+               else if (!Obituary_WeaponDeath(targ, false, deathtype, targ.netname, deathlocation, "", CS(targ).killcount, 0))
+               {
+                       backtrace("SUICIDE: what the hell happened here?\n");
+                       return;
+               }
+               LogDeath("suicide", deathtype, targ, targ);
+               if(deathtype != DEATH_AUTOTEAMCHANGE.m_id) // special case: don't negate frags if auto switched
+                       GiveFrags(attacker, targ, -1, deathtype, weaponentity);
+       }
+
+       // ======
+       // MURDER
+       // ======
+       else if(IS_PLAYER(attacker))
+       {
+               if(SAME_TEAM(attacker, targ))
+               {
+                       LogDeath("tk", deathtype, attacker, targ);
+                       GiveFrags(attacker, targ, -1, deathtype, weaponentity);
+
+                       CS(attacker).killcount = 0;
+
+                       Send_Notification(NOTIF_ONE, attacker, MSG_CENTER, CENTER_DEATH_TEAMKILL_FRAG, targ.netname);
+                       Send_Notification(NOTIF_ONE, targ, MSG_CENTER, CENTER_DEATH_TEAMKILL_FRAGGED, attacker_name);
+                       Send_Notification(NOTIF_ALL, NULL, MSG_INFO, APP_TEAM_NUM(targ.team, INFO_DEATH_TEAMKILL), targ.netname, attacker_name, deathlocation, CS(targ).killcount);
+
+                       // In this case, the death message will ALWAYS be "foo was betrayed by bar"
+                       // No need for specific death/weapon messages...
+               }
+               else
+               {
+                       LogDeath("frag", deathtype, attacker, targ);
+                       GiveFrags(attacker, targ, 1, deathtype, weaponentity);
+
+                       CS(attacker).taunt_soundtime = time + 1;
+                       CS(attacker).killcount = CS(attacker).killcount + 1;
+
+                       attacker.killsound += 1;
+
+                       // TODO: improve SPREE_ITEM and KILL_SPREE_LIST
+                       // these 2 macros are spread over multiple files
+                       #define SPREE_ITEM(counta,countb,center,normal,gentle) \
+                               case counta: \
+                                       Send_Notification(NOTIF_ONE, attacker, MSG_ANNCE, ANNCE_KILLSTREAK_##countb); \
+                                       if (!warmup_stage) \
+                                               PlayerStats_GameReport_Event_Player(attacker, PLAYERSTATS_ACHIEVEMENT_KILL_SPREE_##counta, 1); \
+                                       break;
+
+                       switch(CS(attacker).killcount)
+                       {
+                               KILL_SPREE_LIST
+                               default: break;
+                       }
+                       #undef SPREE_ITEM
+
+                       if(!warmup_stage && !checkrules_firstblood)
+                       {
+                               checkrules_firstblood = true;
+                               notif_firstblood = true; // modify the current messages so that they too show firstblood information
+                               PlayerStats_GameReport_Event_Player(attacker, PLAYERSTATS_ACHIEVEMENT_FIRSTBLOOD, 1);
+                               PlayerStats_GameReport_Event_Player(targ, PLAYERSTATS_ACHIEVEMENT_FIRSTVICTIM, 1);
+
+                               // tell spree_inf and spree_cen that this is a first-blood and first-victim event
+                               kill_count_to_attacker = -1;
+                               kill_count_to_target = -2;
+                       }
+                       else
+                       {
+                               kill_count_to_attacker = CS(attacker).killcount;
+                               kill_count_to_target = 0;
+                       }
+
+                       if(targ.istypefrag)
+                       {
+                               Send_Notification(
+                                       NOTIF_ONE,
+                                       attacker,
+                                       MSG_CHOICE,
+                                       CHOICE_TYPEFRAG,
+                                       targ.netname,
+                                       kill_count_to_attacker,
+                                       (IS_BOT_CLIENT(targ) ? -1 : CS(targ).ping)
+                               );
+                               Send_Notification(
+                                       NOTIF_ONE,
+                                       targ,
+                                       MSG_CHOICE,
+                                       CHOICE_TYPEFRAGGED,
+                                       attacker_name,
+                                       kill_count_to_target,
+                                       GetResource(attacker, RES_HEALTH),
+                                       GetResource(attacker, RES_ARMOR),
+                                       (IS_BOT_CLIENT(attacker) ? -1 : CS(attacker).ping)
+                               );
+                       }
+                       else if(!frag_centermessage_override(attacker, targ, deathtype, kill_count_to_attacker, kill_count_to_target, attacker_name))
+                       {
+                               Send_Notification(
+                                       NOTIF_ONE,
+                                       attacker,
+                                       MSG_CHOICE,
+                                       CHOICE_FRAG,
+                                       targ.netname,
+                                       kill_count_to_attacker,
+                                       (IS_BOT_CLIENT(targ) ? -1 : CS(targ).ping)
+                               );
+                               Send_Notification(
+                                       NOTIF_ONE,
+                                       targ,
+                                       MSG_CHOICE,
+                                       CHOICE_FRAGGED,
+                                       attacker_name,
+                                       kill_count_to_target,
+                                       GetResource(attacker, RES_HEALTH),
+                                       GetResource(attacker, RES_ARMOR),
+                                       (IS_BOT_CLIENT(attacker) ? -1 : CS(attacker).ping)
+                               );
+                       }
+
+                       int f3 = 0;
+                       if(deathtype == DEATH_BUFF.m_id)
+                               f3 = buff_FirstFromFlags(STAT(BUFFS, attacker)).m_id;
+
+                       if (!Obituary_WeaponDeath(targ, true, deathtype, targ.netname, attacker_name, deathlocation, CS(targ).killcount, kill_count_to_attacker))
+                               Obituary_SpecialDeath(targ, true, deathtype, targ.netname, attacker_name, deathlocation, CS(targ).killcount, kill_count_to_attacker, f3);
+               }
+       }
+
+       // =============
+       // ACCIDENT/TRAP
+       // =============
+       else
+       {
+               switch(DEATH_ENT(deathtype))
+               {
+                       // For now, we're just forcing HURTTRIGGER to behave as "DEATH_VOID" and giving it no special options...
+                       // Later on you will only be able to make custom messages using DEATH_CUSTOM,
+                       // and there will be a REAL DEATH_VOID implementation which mappers will use.
+                       case DEATH_HURTTRIGGER:
+                       {
+                               Obituary_SpecialDeath(targ, false, deathtype,
+                                       targ.netname,
+                                       inflictor.message,
+                                       deathlocation,
+                                       CS(targ).killcount,
+                                       0,
+                                       0);
+                               break;
+                       }
+
+                       case DEATH_CUSTOM:
+                       {
+                               Obituary_SpecialDeath(targ, false, deathtype,
+                                       targ.netname,
+                                       ((strstrofs(deathmessage, "%", 0) < 0) ? strcat("%s ", deathmessage) : deathmessage),
+                                       deathlocation,
+                                       CS(targ).killcount,
+                                       0,
+                                       0);
+                               break;
+                       }
+
+                       default:
+                       {
+                               Obituary_SpecialDeath(targ, false, deathtype, targ.netname, deathlocation, "", CS(targ).killcount, 0, 0);
+                               break;
+                       }
+               }
+
+               LogDeath("accident", deathtype, targ, targ);
+               GiveFrags(targ, targ, -1, deathtype, weaponentity);
+
+               if(GameRules_scoring_add(targ, SCORE, 0) == -5)
+               {
+                       Send_Notification(NOTIF_ONE, targ, MSG_ANNCE, ANNCE_ACHIEVEMENT_BOTLIKE);
+                       if (!warmup_stage)
+                       {
+                               PlayerStats_GameReport_Event_Player(attacker, PLAYERSTATS_ACHIEVEMENT_BOTLIKE, 1);
+                       }
+               }
+       }
+
+       // reset target kill count
+       CS(targ).killcount = 0;
+}
+
+void Ice_Think(entity this)
+{
+       if(!STAT(FROZEN, this.owner) || this.owner.iceblock != this)
+       {
+               delete(this);
+               return;
+       }
+       vector ice_org = this.owner.origin - '0 0 16';
+       if (this.origin != ice_org)
+               setorigin(this, ice_org);
+       this.nextthink = time;
+}
+
+void Freeze(entity targ, float revivespeed, int frozen_type, bool show_waypoint)
+{
+       if(!IS_PLAYER(targ) && !IS_MONSTER(targ)) // TODO: only specified entities can be freezed
+               return;
+
+       if(STAT(FROZEN, targ))
+               return;
+
+       float targ_maxhealth = ((IS_MONSTER(targ)) ? targ.max_health : start_health);
+
+       STAT(FROZEN, targ) = frozen_type;
+       STAT(REVIVE_PROGRESS, targ) = ((frozen_type == FROZEN_TEMP_DYING) ? 1 : 0);
+       SetResource(targ, RES_HEALTH, ((frozen_type == FROZEN_TEMP_DYING) ? targ_maxhealth : 1));
+       targ.revive_speed = revivespeed;
+       if(targ.bot_attack)
+               IL_REMOVE(g_bot_targets, targ);
+       targ.bot_attack = false;
+       targ.freeze_time = time;
+
+       entity ice = new(ice);
+       ice.owner = targ;
+       ice.scale = targ.scale;
+       // set_movetype(ice, MOVETYPE_FOLLOW) would rotate the ice model with the player
+       setthink(ice, Ice_Think);
+       ice.nextthink = time;
+       ice.frame = floor(random() * 21); // ice model has 20 different looking frames
+       setmodel(ice, MDL_ICE);
+       ice.alpha = 1;
+       ice.colormod = Team_ColorRGB(targ.team);
+       ice.glowmod = ice.colormod;
+       targ.iceblock = ice;
+       targ.revival_time = 0;
+
+       Ice_Think(ice);
+
+       RemoveGrapplingHooks(targ);
+
+       FOREACH_CLIENT(IS_PLAYER(it),
+       {
+               for(int slot = 0; slot < MAX_WEAPONSLOTS; ++slot)
+           {
+               .entity weaponentity = weaponentities[slot];
+               if(it.(weaponentity).hook.aiment == targ)
+                       RemoveHook(it.(weaponentity).hook);
+           }
+       });
+
+       // add waypoint
+       if(MUTATOR_CALLHOOK(Freeze, targ, revivespeed, frozen_type) || show_waypoint)
+               WaypointSprite_Spawn(WP_Frozen, 0, 0, targ, '0 0 64', NULL, targ.team, targ, waypointsprite_attached, true, RADARICON_WAYPOINT);
+}
+
+void Unfreeze(entity targ, bool reset_health)
+{
+       if(!STAT(FROZEN, targ))
+               return;
+
+       if (reset_health && STAT(FROZEN, targ) != FROZEN_TEMP_DYING)
+               SetResource(targ, RES_HEALTH, ((IS_PLAYER(targ)) ? start_health : targ.max_health));
+
+       targ.pauseregen_finished = time + autocvar_g_balance_pause_health_regen;
+
+       STAT(FROZEN, targ) = 0;
+       STAT(REVIVE_PROGRESS, targ) = 0;
+       targ.revival_time = time;
+       if(!targ.bot_attack)
+               IL_PUSH(g_bot_targets, targ);
+       targ.bot_attack = true;
+
+       WaypointSprite_Kill(targ.waypointsprite_attached);
+
+       FOREACH_CLIENT(IS_PLAYER(it),
+       {
+               for(int slot = 0; slot < MAX_WEAPONSLOTS; ++slot)
+           {
+               .entity weaponentity = weaponentities[slot];
+               if(it.(weaponentity).hook.aiment == targ)
+                       RemoveHook(it.(weaponentity).hook);
+           }
+       });
+
+       // remove the ice block
+       if(targ.iceblock)
+               delete(targ.iceblock);
+       targ.iceblock = NULL;
+
+       MUTATOR_CALLHOOK(Unfreeze, targ);
+}
+
+void Damage(entity targ, entity inflictor, entity attacker, float damage, int deathtype, .entity weaponentity, vector hitloc, vector force)
+{
+       float complainteamdamage = 0;
+       float mirrordamage = 0;
+       float mirrorforce = 0;
+
+       if (game_stopped || (IS_CLIENT(targ) && CS(targ).killcount == FRAGS_SPECTATOR))
+               return;
+
+       entity attacker_save = attacker;
+
+       // special rule: gravity bombs and sound-based attacks do not affect team mates (other than for disconnecting the hook)
+       if(DEATH_ISWEAPON(deathtype, WEP_HOOK) || (deathtype & HITTYPE_SOUND))
+       {
+               if(IS_PLAYER(targ) && SAME_TEAM(targ, attacker))
+               {
+                       return;
+               }
+       }
+
+       if(deathtype == DEATH_KILL.m_id || deathtype == DEATH_TEAMCHANGE.m_id || deathtype == DEATH_AUTOTEAMCHANGE.m_id)
+       {
+               // exit the vehicle before killing (fixes a crash)
+               if(IS_PLAYER(targ) && targ.vehicle)
+                       vehicles_exit(targ.vehicle, VHEF_RELEASE);
+
+               // These are ALWAYS lethal
+               // No damage modification here
+               // Instead, prepare the victim for his death...
+               SetResourceExplicit(targ, RES_ARMOR, 0);
+               targ.spawnshieldtime = 0;
+               SetResourceExplicit(targ, RES_HEALTH, 0.9); // this is < 1
+               targ.flags -= targ.flags & FL_GODMODE;
+               damage = 100000;
+       }
+       else if(deathtype == DEATH_MIRRORDAMAGE.m_id || deathtype == DEATH_NOAMMO.m_id)
+       {
+               // no processing
+       }
+       else
+       {
+               // nullify damage if teamplay is on
+               if(deathtype != DEATH_TELEFRAG.m_id)
+               if(IS_PLAYER(attacker))
+               {
+                       if(IS_PLAYER(targ) && targ != attacker && (IS_INDEPENDENT_PLAYER(attacker) || IS_INDEPENDENT_PLAYER(targ)))
+                       {
+                               damage = 0;
+                               force = '0 0 0';
+                       }
+                       else if(SAME_TEAM(attacker, targ))
+                       {
+                               if(autocvar_teamplay_mode == 1)
+                                       damage = 0;
+                               else if(attacker != targ)
+                               {
+                                       if(autocvar_teamplay_mode == 2)
+                                       {
+                                               if(IS_PLAYER(targ) && !IS_DEAD(targ))
+                                               {
+                                                       attacker.dmg_team = attacker.dmg_team + damage;
+                                                       complainteamdamage = attacker.dmg_team - autocvar_g_teamdamage_threshold;
+                                               }
+                                       }
+                                       else if(autocvar_teamplay_mode == 3)
+                                               damage = 0;
+                                       else if(autocvar_teamplay_mode == 4)
+                                       {
+                                               if(IS_PLAYER(targ) && !IS_DEAD(targ))
+                                               {
+                                                       attacker.dmg_team = attacker.dmg_team + damage;
+                                                       complainteamdamage = attacker.dmg_team - autocvar_g_teamdamage_threshold;
+                                                       if(complainteamdamage > 0)
+                                                               mirrordamage = autocvar_g_mirrordamage * complainteamdamage;
+                                                       mirrorforce = autocvar_g_mirrordamage * vlen(force);
+                                                       damage = autocvar_g_friendlyfire * damage;
+                                                       // mirrordamage will be used LATER
+
+                                                       if(autocvar_g_mirrordamage_virtual)
+                                                       {
+                                                               vector v  = healtharmor_applydamage(GetResource(attacker, RES_ARMOR), autocvar_g_balance_armor_blockpercent, deathtype, mirrordamage);
+                                                               attacker.dmg_take += v.x;
+                                                               attacker.dmg_save += v.y;
+                                                               attacker.dmg_inflictor = inflictor;
+                                                               mirrordamage = v.z;
+                                                               mirrorforce = 0;
+                                                       }
+
+                                                       if(autocvar_g_friendlyfire_virtual)
+                                                       {
+                                                               vector v = healtharmor_applydamage(GetResource(targ, RES_ARMOR), autocvar_g_balance_armor_blockpercent, deathtype, damage);
+                                                               targ.dmg_take += v.x;
+                                                               targ.dmg_save += v.y;
+                                                               targ.dmg_inflictor = inflictor;
+                                                               damage = 0;
+                                                               if(!autocvar_g_friendlyfire_virtual_force)
+                                                                       force = '0 0 0';
+                                                       }
+                                               }
+                                               else if(!targ.canteamdamage)
+                                                       damage = 0;
+                                       }
+                               }
+                       }
+               }
+
+               if (!DEATH_ISSPECIAL(deathtype))
+               {
+                       damage *= g_weapondamagefactor;
+                       mirrordamage *= g_weapondamagefactor;
+                       complainteamdamage *= g_weapondamagefactor;
+                       force = force * g_weaponforcefactor;
+                       mirrorforce *= g_weaponforcefactor;
+               }
+
+               // should this be changed at all? If so, in what way?
+               MUTATOR_CALLHOOK(Damage_Calculate, inflictor, attacker, targ, deathtype, damage, mirrordamage, force, attacker.(weaponentity));
+               damage = M_ARGV(4, float);
+               mirrordamage = M_ARGV(5, float);
+               force = M_ARGV(6, vector);
+
+               if(IS_PLAYER(targ) && damage > 0 && attacker)
+               {
+                       for(int slot = 0; slot < MAX_WEAPONSLOTS; ++slot)
+                   {
+                       .entity went = weaponentities[slot];
+                       if(targ.(went).hook && targ.(went).hook.aiment == attacker)
+                               RemoveHook(targ.(went).hook);
+                   }
+               }
+
+               if(STAT(FROZEN, targ) && !ITEM_DAMAGE_NEEDKILL(deathtype)
+                       && deathtype != DEATH_TEAMCHANGE.m_id && deathtype != DEATH_AUTOTEAMCHANGE.m_id)
+               {
+                       if(autocvar_g_frozen_revive_falldamage > 0 && deathtype == DEATH_FALL.m_id && damage >= autocvar_g_frozen_revive_falldamage)
+                       {
+                               Unfreeze(targ, false);
+                               SetResource(targ, RES_HEALTH, autocvar_g_frozen_revive_falldamage_health);
+                               Send_Effect(EFFECT_ICEORGLASS, targ.origin, '0 0 0', 3);
+                               Send_Notification(NOTIF_ALL, NULL, MSG_INFO, INFO_FREEZETAG_REVIVED_FALL, targ.netname);
+                               Send_Notification(NOTIF_ONE, targ, MSG_CENTER, CENTER_FREEZETAG_REVIVE_SELF);
+                       }
+
+                       damage = 0;
+                       force *= autocvar_g_frozen_force;
+               }
+
+               if(IS_PLAYER(targ) && STAT(FROZEN, targ)
+                       && ITEM_DAMAGE_NEEDKILL(deathtype) && !autocvar_g_frozen_damage_trigger)
+               {
+                       Send_Effect(EFFECT_TELEPORT, targ.origin, '0 0 0', 1);
+
+                       entity spot = SelectSpawnPoint(targ, false);
+                       if(spot)
+                       {
+                               damage = 0;
+                               targ.deadflag = DEAD_NO;
+
+                               targ.angles = spot.angles;
+
+                               targ.effects = 0;
+                               targ.effects |= EF_TELEPORT_BIT;
+
+                               targ.angles_z = 0; // never spawn tilted even if the spot says to
+                               targ.fixangle = true; // turn this way immediately
+                               targ.velocity = '0 0 0';
+                               targ.avelocity = '0 0 0';
+                               targ.punchangle = '0 0 0';
+                               targ.punchvector = '0 0 0';
+                               targ.oldvelocity = targ.velocity;
+
+                               targ.spawnorigin = spot.origin;
+                               setorigin(targ, spot.origin + '0 0 1' * (1 - targ.mins.z - 24));
+                               // don't reset back to last position, even if new position is stuck in solid
+                               targ.oldorigin = targ.origin;
+
+                               Send_Effect(EFFECT_TELEPORT, targ.origin, '0 0 0', 1);
+                       }
+               }
+
+               if(!MUTATOR_IS_ENABLED(mutator_instagib))
+               {
+                       // apply strength multiplier
+                       if (attacker.items & ITEM_Strength.m_itemid)
+                       {
+                               if(targ == attacker)
+                               {
+                                       damage = damage * autocvar_g_balance_powerup_strength_selfdamage;
+                                       force = force * autocvar_g_balance_powerup_strength_selfforce;
+                               }
+                               else
+                               {
+                                       damage = damage * autocvar_g_balance_powerup_strength_damage;
+                                       force = force * autocvar_g_balance_powerup_strength_force;
+                               }
+                       }
+
+                       // apply invincibility multiplier
+                       if (targ.items & ITEM_Shield.m_itemid)
+                       {
+                               damage = damage * autocvar_g_balance_powerup_invincible_takedamage;
+                               if (targ != attacker)
+                               {
+                                       force = force * autocvar_g_balance_powerup_invincible_takeforce;
+                               }
+                       }
+               }
+
+               if (targ == attacker)
+                       damage = damage * autocvar_g_balance_selfdamagepercent; // Partial damage if the attacker hits himself
+
+               // count the damage
+               if(attacker)
+               if(!IS_DEAD(targ))
+               if(deathtype != DEATH_BUFF.m_id)
+               if(targ.takedamage == DAMAGE_AIM)
+               if(targ != attacker)
+               {
+                       entity victim;
+                       if(IS_VEHICLE(targ) && targ.owner)
+                               victim = targ.owner;
+                       else
+                               victim = targ;
+
+                       if(IS_PLAYER(victim) || (IS_TURRET(victim) && victim.active == ACTIVE_ACTIVE) || IS_MONSTER(victim) || MUTATOR_CALLHOOK(PlayHitsound, victim, attacker))
+                       {
+                               if(DIFF_TEAM(victim, attacker) && !STAT(FROZEN, victim))
+                               {
+                                       if(damage > 0)
+                                       {
+                                               if(deathtype != DEATH_FIRE.m_id)
+                                               {
+                                                       if(PHYS_INPUT_BUTTON_CHAT(victim))
+                                                               attacker.typehitsound += 1;
+                                                       else
+                                                               attacker.damage_dealt += damage;
+                                               }
+
+                                               damage_goodhits += 1;
+                                               damage_gooddamage += damage;
+
+                                               if (!DEATH_ISSPECIAL(deathtype))
+                                               {
+                                                       if(IS_PLAYER(targ)) // don't do this for vehicles
+                                                       if(IsFlying(victim))
+                                                               yoda = 1;
+                                               }
+                                       }
+                               }
+                               else if(IS_PLAYER(attacker))
+                               {
+                                       // if enemy gets frozen in this frame and receives other damage don't
+                                       // play the typehitsound e.g. when hit by multiple bullets of the shotgun
+                                       if (deathtype != DEATH_FIRE.m_id && (!STAT(FROZEN, victim) || time > victim.freeze_time))
+                                       {
+                                               attacker.typehitsound += 1;
+                                       }
+                                       if(complainteamdamage > 0)
+                                               if(time > CS(attacker).teamkill_complain)
+                                               {
+                                                       CS(attacker).teamkill_complain = time + 5;
+                                                       CS(attacker).teamkill_soundtime = time + 0.4;
+                                                       CS(attacker).teamkill_soundsource = targ;
+                                               }
+                               }
+                       }
+               }
+       }
+
+       // apply push
+       if (targ.damageforcescale)
+       if (force)
+       if (!IS_PLAYER(targ) || time >= targ.spawnshieldtime || targ == attacker)
+       {
+               vector farce = damage_explosion_calcpush(targ.damageforcescale * force, targ.velocity, autocvar_g_balance_damagepush_speedfactor);
+               if(targ.move_movetype == MOVETYPE_PHYSICS)
+               {
+                       entity farcent = new(farce);
+                       farcent.enemy = targ;
+                       farcent.movedir = farce * 10;
+                       if(targ.mass)
+                               farcent.movedir = farcent.movedir * targ.mass;
+                       farcent.origin = hitloc;
+                       farcent.forcetype = FORCETYPE_FORCEATPOS;
+                       farcent.nextthink = time + 0.1;
+                       setthink(farcent, SUB_Remove);
+               }
+               else if(targ.move_movetype != MOVETYPE_NOCLIP)
+               {
+                       targ.velocity = targ.velocity + farce;
+               }
+               UNSET_ONGROUND(targ);
+               UpdateCSQCProjectile(targ);
+       }
+       // apply damage
+       if (damage != 0 || (targ.damageforcescale && force))
+       if (targ.event_damage)
+               targ.event_damage (targ, inflictor, attacker, damage, deathtype, weaponentity, hitloc, force);
+
+       // apply mirror damage if any
+       if(!autocvar_g_mirrordamage_onlyweapons || DEATH_WEAPONOF(deathtype) != WEP_Null)
+       if(mirrordamage > 0 || mirrorforce > 0)
+       {
+               attacker = attacker_save;
+
+               force = normalize(attacker.origin + attacker.view_ofs - hitloc) * mirrorforce;
+               Damage(attacker, inflictor, attacker, mirrordamage, DEATH_MIRRORDAMAGE.m_id, weaponentity, attacker.origin, force);
+       }
+}
+
+float RadiusDamageForSource (entity inflictor, vector inflictororigin, vector inflictorvelocity, entity attacker, float coredamage, float edgedamage, float rad, entity cantbe, entity mustbe,
+                                                               float inflictorselfdamage, float forceintensity, float forcezscale, int deathtype, .entity weaponentity, entity directhitentity)
+       // Returns total damage applies to creatures
+{
+       entity  targ;
+       vector  force;
+       float   total_damage_to_creatures;
+       entity  next;
+       float   tfloordmg;
+       float   tfloorforce;
+
+       float stat_damagedone;
+
+       if(RadiusDamage_running)
+       {
+               backtrace("RadiusDamage called recursively! Expect stuff to go HORRIBLY wrong.");
+               return 0;
+       }
+
+       RadiusDamage_running = 1;
+
+       tfloordmg = autocvar_g_throughfloor_damage;
+       tfloorforce = autocvar_g_throughfloor_force;
+
+       total_damage_to_creatures = 0;
+
+       if(deathtype != (WEP_HOOK.m_id | HITTYPE_SECONDARY | HITTYPE_BOUNCE)) // only send gravity bomb damage once
+               if(!(deathtype & HITTYPE_SOUND)) // do not send radial sound damage (bandwidth hog)
+               {
+                       force = inflictorvelocity;
+                       if(force == '0 0 0')
+                               force = '0 0 -1';
+                       else
+                               force = normalize(force);
+                       if(forceintensity >= 0)
+                               Damage_DamageInfo(inflictororigin, coredamage, edgedamage, rad, forceintensity * force, deathtype, 0, attacker);
+                       else
+                               Damage_DamageInfo(inflictororigin, coredamage, edgedamage, -rad, (-forceintensity) * force, deathtype, 0, attacker);
+               }
+
+       stat_damagedone = 0;
+
+       targ = WarpZone_FindRadius (inflictororigin, rad + MAX_DAMAGEEXTRARADIUS, false);
+       while (targ)
+       {
+               next = targ.chain;
+               if ((targ != inflictor) || inflictorselfdamage)
+               if (((cantbe != targ) && !mustbe) || (mustbe == targ))
+               if (targ.takedamage)
+               {
+                       vector nearest;
+                       vector diff;
+                       float power;
+
+                       // LordHavoc: measure distance to nearest point on target (not origin)
+                       // (this guarentees 100% damage on a touch impact)
+                       nearest = targ.WarpZone_findradius_nearest;
+                       diff = targ.WarpZone_findradius_dist;
+                       // round up a little on the damage to ensure full damage on impacts
+                       // and turn the distance into a fraction of the radius
+                       power = 1 - ((vlen (diff) - bound(MIN_DAMAGEEXTRARADIUS, targ.damageextraradius, MAX_DAMAGEEXTRARADIUS)) / rad);
+                       //bprint(" ");
+                       //bprint(ftos(power));
+                       //if (targ == attacker)
+                       //      print(ftos(power), "\n");
+                       if (power > 0)
+                       {
+                               float finaldmg;
+                               if (power > 1)
+                                       power = 1;
+                               finaldmg = coredamage * power + edgedamage * (1 - power);
+                               if (finaldmg > 0)
+                               {
+                                       float a;
+                                       float c;
+                                       vector hitloc;
+                                       vector myblastorigin;
+                                       vector center;
+
+                                       myblastorigin = WarpZone_TransformOrigin(targ, inflictororigin);
+
+                                       // if it's a player, use the view origin as reference
+                                       center = CENTER_OR_VIEWOFS(targ);
+
+                                       force = normalize(center - myblastorigin);
+                                       force = force * (finaldmg / coredamage) * forceintensity;
+                                       hitloc = nearest;
+
+                                       // apply special scaling along the z axis if set
+                                       // NOTE: 0 value is not allowed for compatibility, in the case of weapon cvars not being set
+                                       if(forcezscale)
+                                               force.z *= forcezscale;
+
+                                       if(targ != directhitentity)
+                                       {
+                                               float hits;
+                                               float total;
+                                               float hitratio;
+                                               float mininv_f, mininv_d;
+
+                                               // test line of sight to multiple positions on box,
+                                               // and do damage if any of them hit
+                                               hits = 0;
+
+                                               // we know: max stddev of hitratio = 1 / (2 * sqrt(n))
+                                               // so for a given max stddev:
+                                               // n = (1 / (2 * max stddev of hitratio))^2
+
+                                               mininv_d = (finaldmg * (1-tfloordmg)) / autocvar_g_throughfloor_damage_max_stddev;
+                                               mininv_f = (vlen(force) * (1-tfloorforce)) / autocvar_g_throughfloor_force_max_stddev;
+
+                                               if(autocvar_g_throughfloor_debug)
+                                                       LOG_INFOF("THROUGHFLOOR: D=%f F=%f max(dD)=1/%f max(dF)=1/%f", finaldmg, vlen(force), mininv_d, mininv_f);
+
+
+                                               total = 0.25 * (max(mininv_f, mininv_d) ** 2);
+
+                                               if(autocvar_g_throughfloor_debug)
+                                                       LOG_INFOF(" steps=%f", total);
+
+
+                                               if (IS_PLAYER(targ))
+                                                       total = ceil(bound(autocvar_g_throughfloor_min_steps_player, total, autocvar_g_throughfloor_max_steps_player));
+                                               else
+                                                       total = ceil(bound(autocvar_g_throughfloor_min_steps_other, total, autocvar_g_throughfloor_max_steps_other));
+
+                                               if(autocvar_g_throughfloor_debug)
+                                                       LOG_INFOF(" steps=%f dD=%f dF=%f", total, finaldmg * (1-tfloordmg) / (2 * sqrt(total)), vlen(force) * (1-tfloorforce) / (2 * sqrt(total)));
+
+                                               for(c = 0; c < total; ++c)
+                                               {
+                                                       //traceline(targ.WarpZone_findradius_findorigin, nearest, MOVE_NOMONSTERS, inflictor);
+                                                       WarpZone_TraceLine(inflictororigin, WarpZone_UnTransformOrigin(targ, nearest), MOVE_NOMONSTERS, inflictor);
+                                                       if (trace_fraction == 1 || trace_ent == targ)
+                                                       {
+                                                               ++hits;
+                                                               if (hits > 1)
+                                                                       hitloc = hitloc + nearest;
+                                                               else
+                                                                       hitloc = nearest;
+                                                       }
+                                                       nearest.x = targ.origin.x + targ.mins.x + random() * targ.size.x;
+                                                       nearest.y = targ.origin.y + targ.mins.y + random() * targ.size.y;
+                                                       nearest.z = targ.origin.z + targ.mins.z + random() * targ.size.z;
+                                               }
+
+                                               nearest = hitloc * (1 / max(1, hits));
+                                               hitratio = (hits / total);
+                                               a = bound(0, tfloordmg + (1-tfloordmg) * hitratio, 1);
+                                               finaldmg = finaldmg * a;
+                                               a = bound(0, tfloorforce + (1-tfloorforce) * hitratio, 1);
+                                               force = force * a;
+
+                                               if(autocvar_g_throughfloor_debug)
+                                                       LOG_INFOF(" D=%f F=%f", finaldmg, vlen(force));
+                                       }
+
+                                       //if (targ == attacker)
+                                       //{
+                                       //      print("hits ", ftos(hits), " / ", ftos(total));
+                                       //      print(" finaldmg ", ftos(finaldmg), " force ", vtos(force));
+                                       //      print(" (", ftos(a), ")\n");
+                                       //}
+                                       if(finaldmg || force)
+                                       {
+                                               if(targ.iscreature)
+                                               {
+                                                       total_damage_to_creatures += finaldmg;
+
+                                                       if(accuracy_isgooddamage(attacker, targ))
+                                                               stat_damagedone += finaldmg;
+                                               }
+
+                                               if(targ == directhitentity || DEATH_ISSPECIAL(deathtype))
+                                                       Damage(targ, inflictor, attacker, finaldmg, deathtype, weaponentity, nearest, force);
+                                               else
+                                                       Damage(targ, inflictor, attacker, finaldmg, deathtype | HITTYPE_SPLASH, weaponentity, nearest, force);
+                                       }
+                               }
+                       }
+               }
+               targ = next;
+       }
+
+       RadiusDamage_running = 0;
+
+       if(!DEATH_ISSPECIAL(deathtype))
+               accuracy_add(attacker, DEATH_WEAPONOF(deathtype), 0, min(coredamage, stat_damagedone));
+
+       return total_damage_to_creatures;
+}
+
+float RadiusDamage(entity inflictor, entity attacker, float coredamage, float edgedamage, float rad, entity cantbe, entity mustbe, float forceintensity, int deathtype, .entity weaponentity, entity directhitentity)
+{
+       return RadiusDamageForSource(inflictor, (inflictor.origin + (inflictor.mins + inflictor.maxs) * 0.5), inflictor.velocity, attacker, coredamage, edgedamage, rad, 
+                                                                       cantbe, mustbe, false, forceintensity, 1, deathtype, weaponentity, directhitentity);
+}
+
+bool Heal(entity targ, entity inflictor, float amount, float limit)
+{
+       if(game_stopped || (IS_CLIENT(targ) && CS(targ).killcount == FRAGS_SPECTATOR) || STAT(FROZEN, targ) || IS_DEAD(targ))
+               return false;
+
+       bool healed = false;
+       if(targ.event_heal)
+               healed = targ.event_heal(targ, inflictor, amount, limit);
+       // TODO: additional handling? what if the healing kills them? should this abort if healing would do so etc
+       // TODO: healing fx!
+       // TODO: armor healing?
+       return healed;
+}
+
+float Fire_IsBurning(entity e)
+{
+       return (time < e.fire_endtime);
+}
+
+float Fire_AddDamage(entity e, entity o, float d, float t, float dt)
+{
+       float dps;
+       float maxtime, mintime, maxdamage, mindamage, maxdps, mindps, totaldamage, totaltime;
+
+       if(IS_PLAYER(e))
+       {
+               if(IS_DEAD(e))
+                       return -1;
+       }
+       else
+       {
+               if(!e.fire_burner)
+               {
+                       // print("adding a fire burner to ", e.classname, "\n");
+                       e.fire_burner = new(fireburner);
+                       setthink(e.fire_burner, fireburner_think);
+                       e.fire_burner.nextthink = time;
+                       e.fire_burner.owner = e;
+               }
+       }
+
+       t = max(t, 0.1);
+       dps = d / t;
+       if(Fire_IsBurning(e))
+       {
+               mintime = e.fire_endtime - time;
+               maxtime = max(mintime, t);
+
+               mindps = e.fire_damagepersec;
+               maxdps = max(mindps, dps);
+
+               if(maxtime > mintime || maxdps > mindps)
+               {
+                       // Constraints:
+
+                       // damage we have right now
+                       mindamage = mindps * mintime;
+
+                       // damage we want to get
+                       maxdamage = mindamage + d;
+
+                       // but we can't exceed maxtime * maxdps!
+                       totaldamage = min(maxdamage, maxtime * maxdps);
+
+                       // LEMMA:
+                       // Look at:
+                       // totaldamage = min(mindamage + d, maxtime * maxdps)
+                       // We see:
+                       // totaldamage <= maxtime * maxdps
+                       // ==> totaldamage / maxdps <= maxtime.
+                       // We also see:
+                       // totaldamage / mindps = min(mindamage / mindps + d, maxtime * maxdps / mindps)
+                       //                     >= min(mintime, maxtime)
+                       // ==> totaldamage / maxdps >= mintime.
+
+                       /*
+                       // how long do we damage then?
+                       // at least as long as before
+                       // but, never exceed maxdps
+                       totaltime = max(mintime, totaldamage / maxdps); // always <= maxtime due to lemma
+                       */
+
+                       // alternate:
+                       // at most as long as maximum allowed
+                       // but, never below mindps
+                       totaltime = min(maxtime, totaldamage / mindps); // always >= mintime due to lemma
+
+                       // assuming t > mintime, dps > mindps:
+                       // we get d = t * dps = maxtime * maxdps
+                       // totaldamage = min(maxdamage, maxtime * maxdps) = min(... + d, maxtime * maxdps) = maxtime * maxdps
+                       // totaldamage / maxdps = maxtime
+                       // totaldamage / mindps > totaldamage / maxdps = maxtime
+                       // FROM THIS:
+                       // a) totaltime = max(mintime, maxtime) = maxtime
+                       // b) totaltime = min(maxtime, totaldamage / maxdps) = maxtime
+
+                       // assuming t <= mintime:
+                       // we get maxtime = mintime
+                       // a) totaltime = max(mintime, ...) >= mintime, also totaltime <= maxtime by the lemma, therefore totaltime = mintime = maxtime
+                       // b) totaltime = min(maxtime, ...) <= maxtime, also totaltime >= mintime by the lemma, therefore totaltime = mintime = maxtime
+
+                       // assuming dps <= mindps:
+                       // we get mindps = maxdps.
+                       // With this, the lemma says that mintime <= totaldamage / mindps = totaldamage / maxdps <= maxtime.
+                       // a) totaltime = max(mintime, totaldamage / maxdps) = totaldamage / maxdps
+                       // b) totaltime = min(maxtime, totaldamage / mindps) = totaldamage / maxdps
+
+                       e.fire_damagepersec = totaldamage / totaltime;
+                       e.fire_endtime = time + totaltime;
+                       if(totaldamage > 1.2 * mindamage)
+                       {
+                               e.fire_deathtype = dt;
+                               if(e.fire_owner != o)
+                               {
+                                       e.fire_owner = o;
+                                       e.fire_hitsound = false;
+                               }
+                       }
+                       if(accuracy_isgooddamage(o, e))
+                               accuracy_add(o, DEATH_WEAPONOF(dt), 0, max(0, totaldamage - mindamage));
+                       return max(0, totaldamage - mindamage); // can never be negative, but to make sure
+               }
+               else
+                       return 0;
+       }
+       else
+       {
+               e.fire_damagepersec = dps;
+               e.fire_endtime = time + t;
+               e.fire_deathtype = dt;
+               e.fire_owner = o;
+               e.fire_hitsound = false;
+               if(accuracy_isgooddamage(o, e))
+                       accuracy_add(o, DEATH_WEAPONOF(dt), 0, d);
+               return d;
+       }
+}
+
+void Fire_ApplyDamage(entity e)
+{
+       float t, d, hi, ty;
+       entity o;
+
+       if (!Fire_IsBurning(e))
+               return;
+
+       for(t = 0, o = e.owner; o.owner && t < 16; o = o.owner, ++t);
+       if(IS_NOT_A_CLIENT(o))
+               o = e.fire_owner;
+
+       // water and slime stop fire
+       if(e.waterlevel)
+       if(e.watertype != CONTENT_LAVA)
+               e.fire_endtime = 0;
+
+       // ice stops fire
+       if(STAT(FROZEN, e))
+               e.fire_endtime = 0;
+
+       t = min(frametime, e.fire_endtime - time);
+       d = e.fire_damagepersec * t;
+
+       hi = e.fire_owner.damage_dealt;
+       ty = e.fire_owner.typehitsound;
+       Damage(e, e, e.fire_owner, d, e.fire_deathtype, DMG_NOWEP, e.origin, '0 0 0');
+       if(e.fire_hitsound && e.fire_owner)
+       {
+               e.fire_owner.damage_dealt = hi;
+               e.fire_owner.typehitsound = ty;
+       }
+       e.fire_hitsound = true;
+
+       if(!IS_INDEPENDENT_PLAYER(e) && !STAT(FROZEN, e))
+       {
+               IL_EACH(g_damagedbycontents, it.damagedbycontents && it != e,
+               {
+                       if(!IS_DEAD(it) && it.takedamage && !IS_INDEPENDENT_PLAYER(it))
+                       if(boxesoverlap(e.absmin, e.absmax, it.absmin, it.absmax))
+                       {
+                               t = autocvar_g_balance_firetransfer_time * (e.fire_endtime - time);
+                               d = autocvar_g_balance_firetransfer_damage * e.fire_damagepersec * t;
+                               Fire_AddDamage(it, o, d, t, DEATH_FIRE.m_id);
+                       }
+               });
+       }
+}
+
+void Fire_ApplyEffect(entity e)
+{
+       if(Fire_IsBurning(e))
+               e.effects |= EF_FLAME;
+       else
+               e.effects &= ~EF_FLAME;
+}
+
+void fireburner_think(entity this)
+{
+       // for players, this is done in the regular loop
+       if(wasfreed(this.owner))
+       {
+               delete(this);
+               return;
+       }
+       Fire_ApplyEffect(this.owner);
+       if(!Fire_IsBurning(this.owner))
+       {
+               this.owner.fire_burner = NULL;
+               delete(this);
+               return;
+       }
+       Fire_ApplyDamage(this.owner);
+       this.nextthink = time;
+}
diff --git a/qcsrc/server/damage.qh b/qcsrc/server/damage.qh
new file mode 100644 (file)
index 0000000..14dbaf0
--- /dev/null
@@ -0,0 +1,163 @@
+#pragma once
+
+#if defined(CSQC)
+#elif defined(MENUQC)
+#elif defined(SVQC)
+    #include <common/weapons/_all.qh>
+    #include <common/stats.qh>
+    #include <server/items/items.qh>
+    #include <server/miscfunctions.qh>
+    #include <lib/warpzone/common.qh>
+    #include <common/constants.qh>
+    #include <common/teams.qh>
+    #include <common/util.qh>
+    #include <common/weapons/_all.qh>
+    #include "weapons/accuracy.qh"
+    #include "weapons/csqcprojectile.qh"
+    #include "weapons/selection.qh"
+    #include "autocvars.qh"
+    #include "constants.qh"
+    #include <common/notifications/all.qh>
+    #include <common/deathtypes/all.qh>
+    #include <server/mutators/_mod.qh>
+    #include <common/turrets/sv_turrets.qh>
+    #include <common/vehicles/all.qh>
+    #include <lib/csqcmodel/sv_model.qh>
+    #include <common/playerstats.qh>
+    #include "hook.qh"
+    #include "scores.qh"
+    #include "spawnpoints.qh"
+#endif
+
+.void(entity this, entity inflictor, entity attacker, float damage, int deathtype, .entity weaponentity, vector hitloc, vector force) event_damage;
+
+.bool(entity targ, entity inflictor, float amount, float limit) event_heal;
+
+.float dmg;
+.float dmg_edge;
+.float dmg_force;
+.float dmg_radius;
+
+bool Damage_DamageInfo_SendEntity(entity this, entity to, int sf);
+
+void Damage_DamageInfo(vector org, float coredamage, float edgedamage, float rad, vector force, int deathtype, float bloodtype, entity dmgowner);
+
+float checkrules_firstblood;
+
+.float damagedbycontents;
+.float damagedbytriggers;
+
+float yoda;
+float damage_goodhits;
+float damage_gooddamage;
+
+.float pain_finished; // Added by Supajoe
+
+.float dmg_team;
+.float teamkill_complain;
+.float teamkill_soundtime;
+.entity teamkill_soundsource;
+.entity pusher;
+.bool istypefrag;
+.float taunt_soundtime;
+
+.float spawnshieldtime;
+
+.int totalfrags;
+
+.bool canteamdamage;
+
+.vector death_origin;
+
+.float damage_dealt, typehitsound, killsound;
+
+// used for custom deathtype
+string deathmessage;
+
+float IsFlying(entity a);
+
+void UpdateFrags(entity player, int f);
+
+// NOTE: f=0 means still count as a (positive) kill, but count no frags for it
+void W_SwitchWeapon_Force(Player this, Weapon w, .entity weaponentity);
+void GiveFrags (entity attacker, entity targ, float f, int deathtype, .entity weaponentity);
+
+string AppendItemcodes(string s, entity player);
+
+void LogDeath(string mode, int deathtype, entity killer, entity killed);
+
+void Obituary_SpecialDeath(
+       entity notif_target,
+       float murder,
+       int deathtype,
+       string s1, string s2, string s3,
+       float f1, float f2, float f3);
+
+float w_deathtype;
+float Obituary_WeaponDeath(
+       entity notif_target,
+       float murder,
+       int deathtype,
+       string s1, string s2, string s3,
+       float f1, float f2);
+
+void Obituary(entity attacker, entity inflictor, entity targ, int deathtype, .entity weaponentity);
+
+// Frozen status effect
+//const int FROZEN_NOT              = 0;
+const int FROZEN_NORMAL             = 1;
+const int FROZEN_TEMP_REVIVING      = 2;
+const int FROZEN_TEMP_DYING         = 3;
+
+.float revival_time; // time at which player was last revived
+.float revive_speed; // NOTE: multiplier (anything above 1 is instaheal)
+.float freeze_time;
+.entity iceblock;
+.entity frozen_by; // for ice fields
+
+void Ice_Think(entity this);
+
+void Freeze(entity targ, float revivespeed, int frozen_type, bool show_waypoint);
+
+void Unfreeze(entity targ, bool reset_health);
+
+// WEAPONTODO
+#define DMG_NOWEP (weaponentities[0])
+
+// NOTE: the .weaponentity parameter can be set to DMG_NOWEP if the attack wasn't caused by a weapon or player
+void Damage (entity targ, entity inflictor, entity attacker, float damage, int deathtype, .entity weaponentity, vector hitloc, vector force);
+
+float RadiusDamage_running;
+float RadiusDamageForSource (entity inflictor, vector inflictororigin, vector inflictorvelocity, entity attacker, float coredamage, float edgedamage, float rad, entity cantbe, entity mustbe, float inflictorselfdamage, float forceintensity, float forcezscale, int deathtype, .entity weaponentity, entity directhitentity);
+       // Returns total damage applies to creatures
+
+float RadiusDamage (entity inflictor, entity attacker, float coredamage, float edgedamage, float rad, entity cantbe, entity mustbe, float forceintensity, int deathtype, .entity weaponentity, entity directhitentity);
+
+.float damageforcescale;
+const float MIN_DAMAGEEXTRARADIUS = 2;
+const float MAX_DAMAGEEXTRARADIUS = 16;
+.float damageextraradius;
+
+// Calls .event_heal on the target so that they can handle healing themselves
+// a limit of RES_LIMIT_NONE should be handled by the entity as its max health (if applicable)
+bool Heal(entity targ, entity inflictor, float amount, float limit);
+
+.float fire_damagepersec;
+.float fire_endtime;
+.float fire_deathtype;
+.entity fire_owner;
+.float fire_hitsound;
+.entity fire_burner;
+
+void fireburner_think(entity this);
+
+float Fire_IsBurning(entity e);
+
+float Fire_AddDamage(entity e, entity o, float d, float t, float dt);
+
+void Fire_ApplyDamage(entity e);
+
+void Fire_ApplyEffect(entity e);
+
+IntrusiveList g_damagedbycontents;
+STATIC_INIT(g_damagedbycontents) { g_damagedbycontents = IL_NEW(); }
diff --git a/qcsrc/server/g_damage.qc b/qcsrc/server/g_damage.qc
deleted file mode 100644 (file)
index f5d5f91..0000000
+++ /dev/null
@@ -1,1301 +0,0 @@
-#include "g_damage.qh"
-
-#include <common/effects/all.qh>
-#include "bot/api.qh"
-#include "g_hook.qh"
-#include <server/client.qh>
-#include <server/gamelog.qh>
-#include <server/items/items.qh>
-#include <server/mutators/_mod.qh>
-#include <server/sv_main.qh>
-#include "teamplay.qh"
-#include "scores.qh"
-#include "spawnpoints.qh"
-#include "../common/state.qh"
-#include "../common/physics/player.qh"
-#include "resources.qh"
-#include "../common/vehicles/all.qh"
-#include "../common/items/_mod.qh"
-#include "../common/mutators/mutator/waypoints/waypointsprites.qh"
-#include "../common/mutators/mutator/instagib/sv_instagib.qh"
-#include "../common/mutators/mutator/buffs/buffs.qh"
-#include "weapons/accuracy.qh"
-#include "weapons/csqcprojectile.qh"
-#include "weapons/selection.qh"
-#include "../common/constants.qh"
-#include "../common/deathtypes/all.qh"
-#include <common/mapobjects/defs.qh>
-#include <common/mapobjects/triggers.qh>
-#include "../common/notifications/all.qh"
-#include "../common/physics/movetypes/movetypes.qh"
-#include "../common/playerstats.qh"
-#include "../common/teams.qh"
-#include "../common/util.qh"
-#include <common/gamemodes/_mod.qh>
-#include <common/gamemodes/rules.qh>
-#include <common/weapons/_all.qh>
-#include "../lib/csqcmodel/sv_model.qh"
-#include "../lib/warpzone/common.qh"
-
-void UpdateFrags(entity player, int f)
-{
-       GameRules_scoring_add_team(player, SCORE, f);
-}
-
-void GiveFrags(entity attacker, entity targ, float f, int deathtype, .entity weaponentity)
-{
-       // TODO route through PlayerScores instead
-       if(game_stopped) return;
-
-       if(f < 0)
-       {
-               if(targ == attacker)
-               {
-                       // suicide
-                       GameRules_scoring_add(attacker, SUICIDES, 1);
-               }
-               else
-               {
-                       // teamkill
-                       GameRules_scoring_add(attacker, TEAMKILLS, 1);
-               }
-       }
-       else
-       {
-               // regular frag
-               GameRules_scoring_add(attacker, KILLS, 1);
-               if(!warmup_stage && targ.playerid)
-                       PlayerStats_GameReport_Event_Player(attacker, sprintf("kills-%d", targ.playerid), 1);
-       }
-
-       GameRules_scoring_add(targ, DEATHS, 1);
-
-       // FIXME fix the mess this is (we have REAL points now!)
-       if(MUTATOR_CALLHOOK(GiveFragsForKill, attacker, targ, f, deathtype, attacker.(weaponentity)))
-               f = M_ARGV(2, float);
-
-       attacker.totalfrags += f;
-
-       if(f)
-               UpdateFrags(attacker, f);
-}
-
-string AppendItemcodes(string s, entity player)
-{
-       for(int slot = 0; slot < MAX_WEAPONSLOTS; ++slot)
-       {
-               .entity weaponentity = weaponentities[slot];
-               int w = player.(weaponentity).m_weapon.m_id;
-               if(w == 0)
-                       w = player.(weaponentity).cnt; // previous weapon
-               if(w != 0 || slot == 0)
-                       s = strcat(s, ftos(w));
-       }
-       if(time < STAT(STRENGTH_FINISHED, player))
-               s = strcat(s, "S");
-       if(time < STAT(INVINCIBLE_FINISHED, player))
-               s = strcat(s, "I");
-       if(PHYS_INPUT_BUTTON_CHAT(player))
-               s = strcat(s, "T");
-       // TODO: include these codes as a flag on the item itself
-       MUTATOR_CALLHOOK(LogDeath_AppendItemCodes, player, s);
-       s = M_ARGV(1, string);
-       return s;
-}
-
-void LogDeath(string mode, int deathtype, entity killer, entity killed)
-{
-       string s;
-       if(!autocvar_sv_eventlog)
-               return;
-       s = strcat(":kill:", mode);
-       s = strcat(s, ":", ftos(killer.playerid));
-       s = strcat(s, ":", ftos(killed.playerid));
-       s = strcat(s, ":type=", Deathtype_Name(deathtype));
-       s = strcat(s, ":items=");
-       s = AppendItemcodes(s, killer);
-       if(killed != killer)
-       {
-               s = strcat(s, ":victimitems=");
-               s = AppendItemcodes(s, killed);
-       }
-       GameLogEcho(s);
-}
-
-void Obituary_SpecialDeath(
-       entity notif_target,
-       float murder,
-       int deathtype,
-       string s1, string s2, string s3,
-       float f1, float f2, float f3)
-{
-       if(!DEATH_ISSPECIAL(deathtype))
-       {
-               backtrace("Obituary_SpecialDeath called without a special deathtype?\n");
-               return;
-       }
-
-       entity deathent = REGISTRY_GET(Deathtypes, deathtype - DT_FIRST);
-       if (!deathent)
-       {
-               backtrace("Obituary_SpecialDeath: Could not find deathtype entity!\n");
-               return;
-       }
-
-       if(g_cts && deathtype == DEATH_KILL.m_id)
-               return; // TODO: somehow put this in CTS gamemode file!
-
-       Notification death_message = (murder) ? deathent.death_msgmurder : deathent.death_msgself;
-       if(death_message)
-       {
-               Send_Notification_WOCOVA(
-                       NOTIF_ONE,
-                       notif_target,
-                       MSG_MULTI,
-                       death_message,
-                       s1, s2, s3, "",
-                       f1, f2, f3, 0
-               );
-               Send_Notification_WOCOVA(
-                       NOTIF_ALL_EXCEPT,
-                       notif_target,
-                       MSG_INFO,
-                       death_message.nent_msginfo,
-                       s1, s2, s3, "",
-                       f1, f2, f3, 0
-               );
-       }
-}
-
-float Obituary_WeaponDeath(
-       entity notif_target,
-       float murder,
-       int deathtype,
-       string s1, string s2, string s3,
-       float f1, float f2)
-{
-       Weapon death_weapon = DEATH_WEAPONOF(deathtype);
-       if (death_weapon == WEP_Null)
-               return false;
-
-       w_deathtype = deathtype;
-       Notification death_message = ((murder) ? death_weapon.wr_killmessage(death_weapon) : death_weapon.wr_suicidemessage(death_weapon));
-       w_deathtype = false;
-
-       if (death_message)
-       {
-               Send_Notification_WOCOVA(
-                       NOTIF_ONE,
-                       notif_target,
-                       MSG_MULTI,
-                       death_message,
-                       s1, s2, s3, "",
-                       f1, f2, 0, 0
-               );
-               // send the info part to everyone
-               Send_Notification_WOCOVA(
-                       NOTIF_ALL_EXCEPT,
-                       notif_target,
-                       MSG_INFO,
-                       death_message.nent_msginfo,
-                       s1, s2, s3, "",
-                       f1, f2, 0, 0
-               );
-       }
-       else
-       {
-               LOG_TRACEF(
-                       "Obituary_WeaponDeath(): ^1Deathtype ^7(%d)^1 has no notification for weapon %s!\n",
-                       deathtype,
-                       death_weapon.netname
-               );
-       }
-
-       return true;
-}
-
-bool frag_centermessage_override(entity attacker, entity targ, int deathtype, int kill_count_to_attacker, int kill_count_to_target, string attacker_name)
-{
-       if(deathtype == DEATH_FIRE.m_id)
-       {
-               Send_Notification(NOTIF_ONE, attacker, MSG_CHOICE, CHOICE_FRAG_FIRE, targ.netname, kill_count_to_attacker, (IS_BOT_CLIENT(targ) ? -1 : CS(targ).ping));
-               Send_Notification(NOTIF_ONE, targ, MSG_CHOICE, CHOICE_FRAGGED_FIRE, attacker_name, kill_count_to_target, GetResource(attacker, RES_HEALTH), GetResource(attacker, RES_ARMOR), (IS_BOT_CLIENT(attacker) ? -1 : CS(attacker).ping));
-               return true;
-       }
-
-       return MUTATOR_CALLHOOK(FragCenterMessage, attacker, targ, deathtype, kill_count_to_attacker, kill_count_to_target);
-}
-
-void Obituary(entity attacker, entity inflictor, entity targ, int deathtype, .entity weaponentity)
-{
-       // Sanity check
-       if (!IS_PLAYER(targ)) { backtrace("Obituary called on non-player?!\n"); return; }
-
-       // Declarations
-       float notif_firstblood = false;
-       float kill_count_to_attacker, kill_count_to_target;
-       bool notif_anonymous = false;
-       string attacker_name = attacker.netname;
-
-       // Set final information for the death
-       targ.death_origin = targ.origin;
-       string deathlocation = (autocvar_notification_server_allows_location ? NearestLocation(targ.death_origin) : "");
-
-       // Abort now if a mutator requests it
-       if (MUTATOR_CALLHOOK(ClientObituary, inflictor, attacker, targ, deathtype, attacker.(weaponentity))) { CS(targ).killcount = 0; return; }
-       notif_anonymous = M_ARGV(5, bool);
-
-       if(notif_anonymous)
-               attacker_name = "Anonymous player";
-
-       #ifdef NOTIFICATIONS_DEBUG
-       Debug_Notification(
-               sprintf(
-                       "Obituary(%s, %s, %s, %s = %d);\n",
-                       attacker_name,
-                       inflictor.netname,
-                       targ.netname,
-                       Deathtype_Name(deathtype),
-                       deathtype
-               )
-       );
-       #endif
-
-       // =======
-       // SUICIDE
-       // =======
-       if(targ == attacker)
-       {
-               if(DEATH_ISSPECIAL(deathtype))
-               {
-                       if(deathtype == DEATH_TEAMCHANGE.m_id || deathtype == DEATH_AUTOTEAMCHANGE.m_id)
-                       {
-                               Obituary_SpecialDeath(targ, false, deathtype, targ.netname, deathlocation, "", targ.team, 0, 0);
-                       }
-                       else
-                       {
-                               switch(DEATH_ENT(deathtype))
-                               {
-                                       case DEATH_MIRRORDAMAGE:
-                                       {
-                                               Obituary_SpecialDeath(targ, false, deathtype, targ.netname, deathlocation, "", CS(targ).killcount, 0, 0);
-                                               break;
-                                       }
-
-                                       default:
-                                       {
-                                               Obituary_SpecialDeath(targ, false, deathtype, targ.netname, deathlocation, "", CS(targ).killcount, 0, 0);
-                                               break;
-                                       }
-                               }
-                       }
-               }
-               else if (!Obituary_WeaponDeath(targ, false, deathtype, targ.netname, deathlocation, "", CS(targ).killcount, 0))
-               {
-                       backtrace("SUICIDE: what the hell happened here?\n");
-                       return;
-               }
-               LogDeath("suicide", deathtype, targ, targ);
-               if(deathtype != DEATH_AUTOTEAMCHANGE.m_id) // special case: don't negate frags if auto switched
-                       GiveFrags(attacker, targ, -1, deathtype, weaponentity);
-       }
-
-       // ======
-       // MURDER
-       // ======
-       else if(IS_PLAYER(attacker))
-       {
-               if(SAME_TEAM(attacker, targ))
-               {
-                       LogDeath("tk", deathtype, attacker, targ);
-                       GiveFrags(attacker, targ, -1, deathtype, weaponentity);
-
-                       CS(attacker).killcount = 0;
-
-                       Send_Notification(NOTIF_ONE, attacker, MSG_CENTER, CENTER_DEATH_TEAMKILL_FRAG, targ.netname);
-                       Send_Notification(NOTIF_ONE, targ, MSG_CENTER, CENTER_DEATH_TEAMKILL_FRAGGED, attacker_name);
-                       Send_Notification(NOTIF_ALL, NULL, MSG_INFO, APP_TEAM_NUM(targ.team, INFO_DEATH_TEAMKILL), targ.netname, attacker_name, deathlocation, CS(targ).killcount);
-
-                       // In this case, the death message will ALWAYS be "foo was betrayed by bar"
-                       // No need for specific death/weapon messages...
-               }
-               else
-               {
-                       LogDeath("frag", deathtype, attacker, targ);
-                       GiveFrags(attacker, targ, 1, deathtype, weaponentity);
-
-                       CS(attacker).taunt_soundtime = time + 1;
-                       CS(attacker).killcount = CS(attacker).killcount + 1;
-
-                       attacker.killsound += 1;
-
-                       // TODO: improve SPREE_ITEM and KILL_SPREE_LIST
-                       // these 2 macros are spread over multiple files
-                       #define SPREE_ITEM(counta,countb,center,normal,gentle) \
-                               case counta: \
-                                       Send_Notification(NOTIF_ONE, attacker, MSG_ANNCE, ANNCE_KILLSTREAK_##countb); \
-                                       if (!warmup_stage) \
-                                               PlayerStats_GameReport_Event_Player(attacker, PLAYERSTATS_ACHIEVEMENT_KILL_SPREE_##counta, 1); \
-                                       break;
-
-                       switch(CS(attacker).killcount)
-                       {
-                               KILL_SPREE_LIST
-                               default: break;
-                       }
-                       #undef SPREE_ITEM
-
-                       if(!warmup_stage && !checkrules_firstblood)
-                       {
-                               checkrules_firstblood = true;
-                               notif_firstblood = true; // modify the current messages so that they too show firstblood information
-                               PlayerStats_GameReport_Event_Player(attacker, PLAYERSTATS_ACHIEVEMENT_FIRSTBLOOD, 1);
-                               PlayerStats_GameReport_Event_Player(targ, PLAYERSTATS_ACHIEVEMENT_FIRSTVICTIM, 1);
-
-                               // tell spree_inf and spree_cen that this is a first-blood and first-victim event
-                               kill_count_to_attacker = -1;
-                               kill_count_to_target = -2;
-                       }
-                       else
-                       {
-                               kill_count_to_attacker = CS(attacker).killcount;
-                               kill_count_to_target = 0;
-                       }
-
-                       if(targ.istypefrag)
-                       {
-                               Send_Notification(
-                                       NOTIF_ONE,
-                                       attacker,
-                                       MSG_CHOICE,
-                                       CHOICE_TYPEFRAG,
-                                       targ.netname,
-                                       kill_count_to_attacker,
-                                       (IS_BOT_CLIENT(targ) ? -1 : CS(targ).ping)
-                               );
-                               Send_Notification(
-                                       NOTIF_ONE,
-                                       targ,
-                                       MSG_CHOICE,
-                                       CHOICE_TYPEFRAGGED,
-                                       attacker_name,
-                                       kill_count_to_target,
-                                       GetResource(attacker, RES_HEALTH),
-                                       GetResource(attacker, RES_ARMOR),
-                                       (IS_BOT_CLIENT(attacker) ? -1 : CS(attacker).ping)
-                               );
-                       }
-                       else if(!frag_centermessage_override(attacker, targ, deathtype, kill_count_to_attacker, kill_count_to_target, attacker_name))
-                       {
-                               Send_Notification(
-                                       NOTIF_ONE,
-                                       attacker,
-                                       MSG_CHOICE,
-                                       CHOICE_FRAG,
-                                       targ.netname,
-                                       kill_count_to_attacker,
-                                       (IS_BOT_CLIENT(targ) ? -1 : CS(targ).ping)
-                               );
-                               Send_Notification(
-                                       NOTIF_ONE,
-                                       targ,
-                                       MSG_CHOICE,
-                                       CHOICE_FRAGGED,
-                                       attacker_name,
-                                       kill_count_to_target,
-                                       GetResource(attacker, RES_HEALTH),
-                                       GetResource(attacker, RES_ARMOR),
-                                       (IS_BOT_CLIENT(attacker) ? -1 : CS(attacker).ping)
-                               );
-                       }
-
-                       int f3 = 0;
-                       if(deathtype == DEATH_BUFF.m_id)
-                               f3 = buff_FirstFromFlags(STAT(BUFFS, attacker)).m_id;
-
-                       if (!Obituary_WeaponDeath(targ, true, deathtype, targ.netname, attacker_name, deathlocation, CS(targ).killcount, kill_count_to_attacker))
-                               Obituary_SpecialDeath(targ, true, deathtype, targ.netname, attacker_name, deathlocation, CS(targ).killcount, kill_count_to_attacker, f3);
-               }
-       }
-
-       // =============
-       // ACCIDENT/TRAP
-       // =============
-       else
-       {
-               switch(DEATH_ENT(deathtype))
-               {
-                       // For now, we're just forcing HURTTRIGGER to behave as "DEATH_VOID" and giving it no special options...
-                       // Later on you will only be able to make custom messages using DEATH_CUSTOM,
-                       // and there will be a REAL DEATH_VOID implementation which mappers will use.
-                       case DEATH_HURTTRIGGER:
-                       {
-                               Obituary_SpecialDeath(targ, false, deathtype,
-                                       targ.netname,
-                                       inflictor.message,
-                                       deathlocation,
-                                       CS(targ).killcount,
-                                       0,
-                                       0);
-                               break;
-                       }
-
-                       case DEATH_CUSTOM:
-                       {
-                               Obituary_SpecialDeath(targ, false, deathtype,
-                                       targ.netname,
-                                       ((strstrofs(deathmessage, "%", 0) < 0) ? strcat("%s ", deathmessage) : deathmessage),
-                                       deathlocation,
-                                       CS(targ).killcount,
-                                       0,
-                                       0);
-                               break;
-                       }
-
-                       default:
-                       {
-                               Obituary_SpecialDeath(targ, false, deathtype, targ.netname, deathlocation, "", CS(targ).killcount, 0, 0);
-                               break;
-                       }
-               }
-
-               LogDeath("accident", deathtype, targ, targ);
-               GiveFrags(targ, targ, -1, deathtype, weaponentity);
-
-               if(GameRules_scoring_add(targ, SCORE, 0) == -5)
-               {
-                       Send_Notification(NOTIF_ONE, targ, MSG_ANNCE, ANNCE_ACHIEVEMENT_BOTLIKE);
-                       if (!warmup_stage)
-                       {
-                               PlayerStats_GameReport_Event_Player(attacker, PLAYERSTATS_ACHIEVEMENT_BOTLIKE, 1);
-                       }
-               }
-       }
-
-       // reset target kill count
-       CS(targ).killcount = 0;
-}
-
-void Ice_Think(entity this)
-{
-       if(!STAT(FROZEN, this.owner) || this.owner.iceblock != this)
-       {
-               delete(this);
-               return;
-       }
-       vector ice_org = this.owner.origin - '0 0 16';
-       if (this.origin != ice_org)
-               setorigin(this, ice_org);
-       this.nextthink = time;
-}
-
-void Freeze(entity targ, float revivespeed, int frozen_type, bool show_waypoint)
-{
-       if(!IS_PLAYER(targ) && !IS_MONSTER(targ)) // TODO: only specified entities can be freezed
-               return;
-
-       if(STAT(FROZEN, targ))
-               return;
-
-       float targ_maxhealth = ((IS_MONSTER(targ)) ? targ.max_health : start_health);
-
-       STAT(FROZEN, targ) = frozen_type;
-       STAT(REVIVE_PROGRESS, targ) = ((frozen_type == FROZEN_TEMP_DYING) ? 1 : 0);
-       SetResource(targ, RES_HEALTH, ((frozen_type == FROZEN_TEMP_DYING) ? targ_maxhealth : 1));
-       targ.revive_speed = revivespeed;
-       if(targ.bot_attack)
-               IL_REMOVE(g_bot_targets, targ);
-       targ.bot_attack = false;
-       targ.freeze_time = time;
-
-       entity ice = new(ice);
-       ice.owner = targ;
-       ice.scale = targ.scale;
-       // set_movetype(ice, MOVETYPE_FOLLOW) would rotate the ice model with the player
-       setthink(ice, Ice_Think);
-       ice.nextthink = time;
-       ice.frame = floor(random() * 21); // ice model has 20 different looking frames
-       setmodel(ice, MDL_ICE);
-       ice.alpha = 1;
-       ice.colormod = Team_ColorRGB(targ.team);
-       ice.glowmod = ice.colormod;
-       targ.iceblock = ice;
-       targ.revival_time = 0;
-
-       Ice_Think(ice);
-
-       RemoveGrapplingHooks(targ);
-
-       FOREACH_CLIENT(IS_PLAYER(it),
-       {
-               for(int slot = 0; slot < MAX_WEAPONSLOTS; ++slot)
-           {
-               .entity weaponentity = weaponentities[slot];
-               if(it.(weaponentity).hook.aiment == targ)
-                       RemoveHook(it.(weaponentity).hook);
-           }
-       });
-
-       // add waypoint
-       if(MUTATOR_CALLHOOK(Freeze, targ, revivespeed, frozen_type) || show_waypoint)
-               WaypointSprite_Spawn(WP_Frozen, 0, 0, targ, '0 0 64', NULL, targ.team, targ, waypointsprite_attached, true, RADARICON_WAYPOINT);
-}
-
-void Unfreeze(entity targ, bool reset_health)
-{
-       if(!STAT(FROZEN, targ))
-               return;
-
-       if (reset_health && STAT(FROZEN, targ) != FROZEN_TEMP_DYING)
-               SetResource(targ, RES_HEALTH, ((IS_PLAYER(targ)) ? start_health : targ.max_health));
-
-       targ.pauseregen_finished = time + autocvar_g_balance_pause_health_regen;
-
-       STAT(FROZEN, targ) = 0;
-       STAT(REVIVE_PROGRESS, targ) = 0;
-       targ.revival_time = time;
-       if(!targ.bot_attack)
-               IL_PUSH(g_bot_targets, targ);
-       targ.bot_attack = true;
-
-       WaypointSprite_Kill(targ.waypointsprite_attached);
-
-       FOREACH_CLIENT(IS_PLAYER(it),
-       {
-               for(int slot = 0; slot < MAX_WEAPONSLOTS; ++slot)
-           {
-               .entity weaponentity = weaponentities[slot];
-               if(it.(weaponentity).hook.aiment == targ)
-                       RemoveHook(it.(weaponentity).hook);
-           }
-       });
-
-       // remove the ice block
-       if(targ.iceblock)
-               delete(targ.iceblock);
-       targ.iceblock = NULL;
-
-       MUTATOR_CALLHOOK(Unfreeze, targ);
-}
-
-void Damage(entity targ, entity inflictor, entity attacker, float damage, int deathtype, .entity weaponentity, vector hitloc, vector force)
-{
-       float complainteamdamage = 0;
-       float mirrordamage = 0;
-       float mirrorforce = 0;
-
-       if (game_stopped || (IS_CLIENT(targ) && CS(targ).killcount == FRAGS_SPECTATOR))
-               return;
-
-       entity attacker_save = attacker;
-
-       // special rule: gravity bombs and sound-based attacks do not affect team mates (other than for disconnecting the hook)
-       if(DEATH_ISWEAPON(deathtype, WEP_HOOK) || (deathtype & HITTYPE_SOUND))
-       {
-               if(IS_PLAYER(targ) && SAME_TEAM(targ, attacker))
-               {
-                       return;
-               }
-       }
-
-       if(deathtype == DEATH_KILL.m_id || deathtype == DEATH_TEAMCHANGE.m_id || deathtype == DEATH_AUTOTEAMCHANGE.m_id)
-       {
-               // exit the vehicle before killing (fixes a crash)
-               if(IS_PLAYER(targ) && targ.vehicle)
-                       vehicles_exit(targ.vehicle, VHEF_RELEASE);
-
-               // These are ALWAYS lethal
-               // No damage modification here
-               // Instead, prepare the victim for his death...
-               SetResourceExplicit(targ, RES_ARMOR, 0);
-               targ.spawnshieldtime = 0;
-               SetResourceExplicit(targ, RES_HEALTH, 0.9); // this is < 1
-               targ.flags -= targ.flags & FL_GODMODE;
-               damage = 100000;
-       }
-       else if(deathtype == DEATH_MIRRORDAMAGE.m_id || deathtype == DEATH_NOAMMO.m_id)
-       {
-               // no processing
-       }
-       else
-       {
-               // nullify damage if teamplay is on
-               if(deathtype != DEATH_TELEFRAG.m_id)
-               if(IS_PLAYER(attacker))
-               {
-                       if(IS_PLAYER(targ) && targ != attacker && (IS_INDEPENDENT_PLAYER(attacker) || IS_INDEPENDENT_PLAYER(targ)))
-                       {
-                               damage = 0;
-                               force = '0 0 0';
-                       }
-                       else if(SAME_TEAM(attacker, targ))
-                       {
-                               if(autocvar_teamplay_mode == 1)
-                                       damage = 0;
-                               else if(attacker != targ)
-                               {
-                                       if(autocvar_teamplay_mode == 2)
-                                       {
-                                               if(IS_PLAYER(targ) && !IS_DEAD(targ))
-                                               {
-                                                       attacker.dmg_team = attacker.dmg_team + damage;
-                                                       complainteamdamage = attacker.dmg_team - autocvar_g_teamdamage_threshold;
-                                               }
-                                       }
-                                       else if(autocvar_teamplay_mode == 3)
-                                               damage = 0;
-                                       else if(autocvar_teamplay_mode == 4)
-                                       {
-                                               if(IS_PLAYER(targ) && !IS_DEAD(targ))
-                                               {
-                                                       attacker.dmg_team = attacker.dmg_team + damage;
-                                                       complainteamdamage = attacker.dmg_team - autocvar_g_teamdamage_threshold;
-                                                       if(complainteamdamage > 0)
-                                                               mirrordamage = autocvar_g_mirrordamage * complainteamdamage;
-                                                       mirrorforce = autocvar_g_mirrordamage * vlen(force);
-                                                       damage = autocvar_g_friendlyfire * damage;
-                                                       // mirrordamage will be used LATER
-
-                                                       if(autocvar_g_mirrordamage_virtual)
-                                                       {
-                                                               vector v  = healtharmor_applydamage(GetResource(attacker, RES_ARMOR), autocvar_g_balance_armor_blockpercent, deathtype, mirrordamage);
-                                                               attacker.dmg_take += v.x;
-                                                               attacker.dmg_save += v.y;
-                                                               attacker.dmg_inflictor = inflictor;
-                                                               mirrordamage = v.z;
-                                                               mirrorforce = 0;
-                                                       }
-
-                                                       if(autocvar_g_friendlyfire_virtual)
-                                                       {
-                                                               vector v = healtharmor_applydamage(GetResource(targ, RES_ARMOR), autocvar_g_balance_armor_blockpercent, deathtype, damage);
-                                                               targ.dmg_take += v.x;
-                                                               targ.dmg_save += v.y;
-                                                               targ.dmg_inflictor = inflictor;
-                                                               damage = 0;
-                                                               if(!autocvar_g_friendlyfire_virtual_force)
-                                                                       force = '0 0 0';
-                                                       }
-                                               }
-                                               else if(!targ.canteamdamage)
-                                                       damage = 0;
-                                       }
-                               }
-                       }
-               }
-
-               if (!DEATH_ISSPECIAL(deathtype))
-               {
-                       damage *= g_weapondamagefactor;
-                       mirrordamage *= g_weapondamagefactor;
-                       complainteamdamage *= g_weapondamagefactor;
-                       force = force * g_weaponforcefactor;
-                       mirrorforce *= g_weaponforcefactor;
-               }
-
-               // should this be changed at all? If so, in what way?
-               MUTATOR_CALLHOOK(Damage_Calculate, inflictor, attacker, targ, deathtype, damage, mirrordamage, force, attacker.(weaponentity));
-               damage = M_ARGV(4, float);
-               mirrordamage = M_ARGV(5, float);
-               force = M_ARGV(6, vector);
-
-               if(IS_PLAYER(targ) && damage > 0 && attacker)
-               {
-                       for(int slot = 0; slot < MAX_WEAPONSLOTS; ++slot)
-                   {
-                       .entity went = weaponentities[slot];
-                       if(targ.(went).hook && targ.(went).hook.aiment == attacker)
-                               RemoveHook(targ.(went).hook);
-                   }
-               }
-
-               if(STAT(FROZEN, targ) && !ITEM_DAMAGE_NEEDKILL(deathtype)
-                       && deathtype != DEATH_TEAMCHANGE.m_id && deathtype != DEATH_AUTOTEAMCHANGE.m_id)
-               {
-                       if(autocvar_g_frozen_revive_falldamage > 0 && deathtype == DEATH_FALL.m_id && damage >= autocvar_g_frozen_revive_falldamage)
-                       {
-                               Unfreeze(targ, false);
-                               SetResource(targ, RES_HEALTH, autocvar_g_frozen_revive_falldamage_health);
-                               Send_Effect(EFFECT_ICEORGLASS, targ.origin, '0 0 0', 3);
-                               Send_Notification(NOTIF_ALL, NULL, MSG_INFO, INFO_FREEZETAG_REVIVED_FALL, targ.netname);
-                               Send_Notification(NOTIF_ONE, targ, MSG_CENTER, CENTER_FREEZETAG_REVIVE_SELF);
-                       }
-
-                       damage = 0;
-                       force *= autocvar_g_frozen_force;
-               }
-
-               if(IS_PLAYER(targ) && STAT(FROZEN, targ)
-                       && ITEM_DAMAGE_NEEDKILL(deathtype) && !autocvar_g_frozen_damage_trigger)
-               {
-                       Send_Effect(EFFECT_TELEPORT, targ.origin, '0 0 0', 1);
-
-                       entity spot = SelectSpawnPoint(targ, false);
-                       if(spot)
-                       {
-                               damage = 0;
-                               targ.deadflag = DEAD_NO;
-
-                               targ.angles = spot.angles;
-
-                               targ.effects = 0;
-                               targ.effects |= EF_TELEPORT_BIT;
-
-                               targ.angles_z = 0; // never spawn tilted even if the spot says to
-                               targ.fixangle = true; // turn this way immediately
-                               targ.velocity = '0 0 0';
-                               targ.avelocity = '0 0 0';
-                               targ.punchangle = '0 0 0';
-                               targ.punchvector = '0 0 0';
-                               targ.oldvelocity = targ.velocity;
-
-                               targ.spawnorigin = spot.origin;
-                               setorigin(targ, spot.origin + '0 0 1' * (1 - targ.mins.z - 24));
-                               // don't reset back to last position, even if new position is stuck in solid
-                               targ.oldorigin = targ.origin;
-
-                               Send_Effect(EFFECT_TELEPORT, targ.origin, '0 0 0', 1);
-                       }
-               }
-
-               if(!MUTATOR_IS_ENABLED(mutator_instagib))
-               {
-                       // apply strength multiplier
-                       if (attacker.items & ITEM_Strength.m_itemid)
-                       {
-                               if(targ == attacker)
-                               {
-                                       damage = damage * autocvar_g_balance_powerup_strength_selfdamage;
-                                       force = force * autocvar_g_balance_powerup_strength_selfforce;
-                               }
-                               else
-                               {
-                                       damage = damage * autocvar_g_balance_powerup_strength_damage;
-                                       force = force * autocvar_g_balance_powerup_strength_force;
-                               }
-                       }
-
-                       // apply invincibility multiplier
-                       if (targ.items & ITEM_Shield.m_itemid)
-                       {
-                               damage = damage * autocvar_g_balance_powerup_invincible_takedamage;
-                               if (targ != attacker)
-                               {
-                                       force = force * autocvar_g_balance_powerup_invincible_takeforce;
-                               }
-                       }
-               }
-
-               if (targ == attacker)
-                       damage = damage * autocvar_g_balance_selfdamagepercent; // Partial damage if the attacker hits himself
-
-               // count the damage
-               if(attacker)
-               if(!IS_DEAD(targ))
-               if(deathtype != DEATH_BUFF.m_id)
-               if(targ.takedamage == DAMAGE_AIM)
-               if(targ != attacker)
-               {
-                       entity victim;
-                       if(IS_VEHICLE(targ) && targ.owner)
-                               victim = targ.owner;
-                       else
-                               victim = targ;
-
-                       if(IS_PLAYER(victim) || (IS_TURRET(victim) && victim.active == ACTIVE_ACTIVE) || IS_MONSTER(victim) || MUTATOR_CALLHOOK(PlayHitsound, victim, attacker))
-                       {
-                               if(DIFF_TEAM(victim, attacker) && !STAT(FROZEN, victim))
-                               {
-                                       if(damage > 0)
-                                       {
-                                               if(deathtype != DEATH_FIRE.m_id)
-                                               {
-                                                       if(PHYS_INPUT_BUTTON_CHAT(victim))
-                                                               attacker.typehitsound += 1;
-                                                       else
-                                                               attacker.damage_dealt += damage;
-                                               }
-
-                                               damage_goodhits += 1;
-                                               damage_gooddamage += damage;
-
-                                               if (!DEATH_ISSPECIAL(deathtype))
-                                               {
-                                                       if(IS_PLAYER(targ)) // don't do this for vehicles
-                                                       if(IsFlying(victim))
-                                                               yoda = 1;
-                                               }
-                                       }
-                               }
-                               else if(IS_PLAYER(attacker))
-                               {
-                                       // if enemy gets frozen in this frame and receives other damage don't
-                                       // play the typehitsound e.g. when hit by multiple bullets of the shotgun
-                                       if (deathtype != DEATH_FIRE.m_id && (!STAT(FROZEN, victim) || time > victim.freeze_time))
-                                       {
-                                               attacker.typehitsound += 1;
-                                       }
-                                       if(complainteamdamage > 0)
-                                               if(time > CS(attacker).teamkill_complain)
-                                               {
-                                                       CS(attacker).teamkill_complain = time + 5;
-                                                       CS(attacker).teamkill_soundtime = time + 0.4;
-                                                       CS(attacker).teamkill_soundsource = targ;
-                                               }
-                               }
-                       }
-               }
-       }
-
-       // apply push
-       if (targ.damageforcescale)
-       if (force)
-       if (!IS_PLAYER(targ) || time >= targ.spawnshieldtime || targ == attacker)
-       {
-               vector farce = damage_explosion_calcpush(targ.damageforcescale * force, targ.velocity, autocvar_g_balance_damagepush_speedfactor);
-               if(targ.move_movetype == MOVETYPE_PHYSICS)
-               {
-                       entity farcent = new(farce);
-                       farcent.enemy = targ;
-                       farcent.movedir = farce * 10;
-                       if(targ.mass)
-                               farcent.movedir = farcent.movedir * targ.mass;
-                       farcent.origin = hitloc;
-                       farcent.forcetype = FORCETYPE_FORCEATPOS;
-                       farcent.nextthink = time + 0.1;
-                       setthink(farcent, SUB_Remove);
-               }
-               else if(targ.move_movetype != MOVETYPE_NOCLIP)
-               {
-                       targ.velocity = targ.velocity + farce;
-               }
-               UNSET_ONGROUND(targ);
-               UpdateCSQCProjectile(targ);
-       }
-       // apply damage
-       if (damage != 0 || (targ.damageforcescale && force))
-       if (targ.event_damage)
-               targ.event_damage (targ, inflictor, attacker, damage, deathtype, weaponentity, hitloc, force);
-
-       // apply mirror damage if any
-       if(!autocvar_g_mirrordamage_onlyweapons || DEATH_WEAPONOF(deathtype) != WEP_Null)
-       if(mirrordamage > 0 || mirrorforce > 0)
-       {
-               attacker = attacker_save;
-
-               force = normalize(attacker.origin + attacker.view_ofs - hitloc) * mirrorforce;
-               Damage(attacker, inflictor, attacker, mirrordamage, DEATH_MIRRORDAMAGE.m_id, weaponentity, attacker.origin, force);
-       }
-}
-
-float RadiusDamageForSource (entity inflictor, vector inflictororigin, vector inflictorvelocity, entity attacker, float coredamage, float edgedamage, float rad, entity cantbe, entity mustbe,
-                                                               float inflictorselfdamage, float forceintensity, float forcezscale, int deathtype, .entity weaponentity, entity directhitentity)
-       // Returns total damage applies to creatures
-{
-       entity  targ;
-       vector  force;
-       float   total_damage_to_creatures;
-       entity  next;
-       float   tfloordmg;
-       float   tfloorforce;
-
-       float stat_damagedone;
-
-       if(RadiusDamage_running)
-       {
-               backtrace("RadiusDamage called recursively! Expect stuff to go HORRIBLY wrong.");
-               return 0;
-       }
-
-       RadiusDamage_running = 1;
-
-       tfloordmg = autocvar_g_throughfloor_damage;
-       tfloorforce = autocvar_g_throughfloor_force;
-
-       total_damage_to_creatures = 0;
-
-       if(deathtype != (WEP_HOOK.m_id | HITTYPE_SECONDARY | HITTYPE_BOUNCE)) // only send gravity bomb damage once
-               if(!(deathtype & HITTYPE_SOUND)) // do not send radial sound damage (bandwidth hog)
-               {
-                       force = inflictorvelocity;
-                       if(force == '0 0 0')
-                               force = '0 0 -1';
-                       else
-                               force = normalize(force);
-                       if(forceintensity >= 0)
-                               Damage_DamageInfo(inflictororigin, coredamage, edgedamage, rad, forceintensity * force, deathtype, 0, attacker);
-                       else
-                               Damage_DamageInfo(inflictororigin, coredamage, edgedamage, -rad, (-forceintensity) * force, deathtype, 0, attacker);
-               }
-
-       stat_damagedone = 0;
-
-       targ = WarpZone_FindRadius (inflictororigin, rad + MAX_DAMAGEEXTRARADIUS, false);
-       while (targ)
-       {
-               next = targ.chain;
-               if ((targ != inflictor) || inflictorselfdamage)
-               if (((cantbe != targ) && !mustbe) || (mustbe == targ))
-               if (targ.takedamage)
-               {
-                       vector nearest;
-                       vector diff;
-                       float power;
-
-                       // LordHavoc: measure distance to nearest point on target (not origin)
-                       // (this guarentees 100% damage on a touch impact)
-                       nearest = targ.WarpZone_findradius_nearest;
-                       diff = targ.WarpZone_findradius_dist;
-                       // round up a little on the damage to ensure full damage on impacts
-                       // and turn the distance into a fraction of the radius
-                       power = 1 - ((vlen (diff) - bound(MIN_DAMAGEEXTRARADIUS, targ.damageextraradius, MAX_DAMAGEEXTRARADIUS)) / rad);
-                       //bprint(" ");
-                       //bprint(ftos(power));
-                       //if (targ == attacker)
-                       //      print(ftos(power), "\n");
-                       if (power > 0)
-                       {
-                               float finaldmg;
-                               if (power > 1)
-                                       power = 1;
-                               finaldmg = coredamage * power + edgedamage * (1 - power);
-                               if (finaldmg > 0)
-                               {
-                                       float a;
-                                       float c;
-                                       vector hitloc;
-                                       vector myblastorigin;
-                                       vector center;
-
-                                       myblastorigin = WarpZone_TransformOrigin(targ, inflictororigin);
-
-                                       // if it's a player, use the view origin as reference
-                                       center = CENTER_OR_VIEWOFS(targ);
-
-                                       force = normalize(center - myblastorigin);
-                                       force = force * (finaldmg / coredamage) * forceintensity;
-                                       hitloc = nearest;
-
-                                       // apply special scaling along the z axis if set
-                                       // NOTE: 0 value is not allowed for compatibility, in the case of weapon cvars not being set
-                                       if(forcezscale)
-                                               force.z *= forcezscale;
-
-                                       if(targ != directhitentity)
-                                       {
-                                               float hits;
-                                               float total;
-                                               float hitratio;
-                                               float mininv_f, mininv_d;
-
-                                               // test line of sight to multiple positions on box,
-                                               // and do damage if any of them hit
-                                               hits = 0;
-
-                                               // we know: max stddev of hitratio = 1 / (2 * sqrt(n))
-                                               // so for a given max stddev:
-                                               // n = (1 / (2 * max stddev of hitratio))^2
-
-                                               mininv_d = (finaldmg * (1-tfloordmg)) / autocvar_g_throughfloor_damage_max_stddev;
-                                               mininv_f = (vlen(force) * (1-tfloorforce)) / autocvar_g_throughfloor_force_max_stddev;
-
-                                               if(autocvar_g_throughfloor_debug)
-                                                       LOG_INFOF("THROUGHFLOOR: D=%f F=%f max(dD)=1/%f max(dF)=1/%f", finaldmg, vlen(force), mininv_d, mininv_f);
-
-
-                                               total = 0.25 * (max(mininv_f, mininv_d) ** 2);
-
-                                               if(autocvar_g_throughfloor_debug)
-                                                       LOG_INFOF(" steps=%f", total);
-
-
-                                               if (IS_PLAYER(targ))
-                                                       total = ceil(bound(autocvar_g_throughfloor_min_steps_player, total, autocvar_g_throughfloor_max_steps_player));
-                                               else
-                                                       total = ceil(bound(autocvar_g_throughfloor_min_steps_other, total, autocvar_g_throughfloor_max_steps_other));
-
-                                               if(autocvar_g_throughfloor_debug)
-                                                       LOG_INFOF(" steps=%f dD=%f dF=%f", total, finaldmg * (1-tfloordmg) / (2 * sqrt(total)), vlen(force) * (1-tfloorforce) / (2 * sqrt(total)));
-
-                                               for(c = 0; c < total; ++c)
-                                               {
-                                                       //traceline(targ.WarpZone_findradius_findorigin, nearest, MOVE_NOMONSTERS, inflictor);
-                                                       WarpZone_TraceLine(inflictororigin, WarpZone_UnTransformOrigin(targ, nearest), MOVE_NOMONSTERS, inflictor);
-                                                       if (trace_fraction == 1 || trace_ent == targ)
-                                                       {
-                                                               ++hits;
-                                                               if (hits > 1)
-                                                                       hitloc = hitloc + nearest;
-                                                               else
-                                                                       hitloc = nearest;
-                                                       }
-                                                       nearest.x = targ.origin.x + targ.mins.x + random() * targ.size.x;
-                                                       nearest.y = targ.origin.y + targ.mins.y + random() * targ.size.y;
-                                                       nearest.z = targ.origin.z + targ.mins.z + random() * targ.size.z;
-                                               }
-
-                                               nearest = hitloc * (1 / max(1, hits));
-                                               hitratio = (hits / total);
-                                               a = bound(0, tfloordmg + (1-tfloordmg) * hitratio, 1);
-                                               finaldmg = finaldmg * a;
-                                               a = bound(0, tfloorforce + (1-tfloorforce) * hitratio, 1);
-                                               force = force * a;
-
-                                               if(autocvar_g_throughfloor_debug)
-                                                       LOG_INFOF(" D=%f F=%f", finaldmg, vlen(force));
-                                       }
-
-                                       //if (targ == attacker)
-                                       //{
-                                       //      print("hits ", ftos(hits), " / ", ftos(total));
-                                       //      print(" finaldmg ", ftos(finaldmg), " force ", vtos(force));
-                                       //      print(" (", ftos(a), ")\n");
-                                       //}
-                                       if(finaldmg || force)
-                                       {
-                                               if(targ.iscreature)
-                                               {
-                                                       total_damage_to_creatures += finaldmg;
-
-                                                       if(accuracy_isgooddamage(attacker, targ))
-                                                               stat_damagedone += finaldmg;
-                                               }
-
-                                               if(targ == directhitentity || DEATH_ISSPECIAL(deathtype))
-                                                       Damage(targ, inflictor, attacker, finaldmg, deathtype, weaponentity, nearest, force);
-                                               else
-                                                       Damage(targ, inflictor, attacker, finaldmg, deathtype | HITTYPE_SPLASH, weaponentity, nearest, force);
-                                       }
-                               }
-                       }
-               }
-               targ = next;
-       }
-
-       RadiusDamage_running = 0;
-
-       if(!DEATH_ISSPECIAL(deathtype))
-               accuracy_add(attacker, DEATH_WEAPONOF(deathtype), 0, min(coredamage, stat_damagedone));
-
-       return total_damage_to_creatures;
-}
-
-float RadiusDamage(entity inflictor, entity attacker, float coredamage, float edgedamage, float rad, entity cantbe, entity mustbe, float forceintensity, int deathtype, .entity weaponentity, entity directhitentity)
-{
-       return RadiusDamageForSource(inflictor, (inflictor.origin + (inflictor.mins + inflictor.maxs) * 0.5), inflictor.velocity, attacker, coredamage, edgedamage, rad, 
-                                                                       cantbe, mustbe, false, forceintensity, 1, deathtype, weaponentity, directhitentity);
-}
-
-bool Heal(entity targ, entity inflictor, float amount, float limit)
-{
-       if(game_stopped || (IS_CLIENT(targ) && CS(targ).killcount == FRAGS_SPECTATOR) || STAT(FROZEN, targ) || IS_DEAD(targ))
-               return false;
-
-       bool healed = false;
-       if(targ.event_heal)
-               healed = targ.event_heal(targ, inflictor, amount, limit);
-       // TODO: additional handling? what if the healing kills them? should this abort if healing would do so etc
-       // TODO: healing fx!
-       // TODO: armor healing?
-       return healed;
-}
-
-float Fire_IsBurning(entity e)
-{
-       return (time < e.fire_endtime);
-}
-
-float Fire_AddDamage(entity e, entity o, float d, float t, float dt)
-{
-       float dps;
-       float maxtime, mintime, maxdamage, mindamage, maxdps, mindps, totaldamage, totaltime;
-
-       if(IS_PLAYER(e))
-       {
-               if(IS_DEAD(e))
-                       return -1;
-       }
-       else
-       {
-               if(!e.fire_burner)
-               {
-                       // print("adding a fire burner to ", e.classname, "\n");
-                       e.fire_burner = new(fireburner);
-                       setthink(e.fire_burner, fireburner_think);
-                       e.fire_burner.nextthink = time;
-                       e.fire_burner.owner = e;
-               }
-       }
-
-       t = max(t, 0.1);
-       dps = d / t;
-       if(Fire_IsBurning(e))
-       {
-               mintime = e.fire_endtime - time;
-               maxtime = max(mintime, t);
-
-               mindps = e.fire_damagepersec;
-               maxdps = max(mindps, dps);
-
-               if(maxtime > mintime || maxdps > mindps)
-               {
-                       // Constraints:
-
-                       // damage we have right now
-                       mindamage = mindps * mintime;
-
-                       // damage we want to get
-                       maxdamage = mindamage + d;
-
-                       // but we can't exceed maxtime * maxdps!
-                       totaldamage = min(maxdamage, maxtime * maxdps);
-
-                       // LEMMA:
-                       // Look at:
-                       // totaldamage = min(mindamage + d, maxtime * maxdps)
-                       // We see:
-                       // totaldamage <= maxtime * maxdps
-                       // ==> totaldamage / maxdps <= maxtime.
-                       // We also see:
-                       // totaldamage / mindps = min(mindamage / mindps + d, maxtime * maxdps / mindps)
-                       //                     >= min(mintime, maxtime)
-                       // ==> totaldamage / maxdps >= mintime.
-
-                       /*
-                       // how long do we damage then?
-                       // at least as long as before
-                       // but, never exceed maxdps
-                       totaltime = max(mintime, totaldamage / maxdps); // always <= maxtime due to lemma
-                       */
-
-                       // alternate:
-                       // at most as long as maximum allowed
-                       // but, never below mindps
-                       totaltime = min(maxtime, totaldamage / mindps); // always >= mintime due to lemma
-
-                       // assuming t > mintime, dps > mindps:
-                       // we get d = t * dps = maxtime * maxdps
-                       // totaldamage = min(maxdamage, maxtime * maxdps) = min(... + d, maxtime * maxdps) = maxtime * maxdps
-                       // totaldamage / maxdps = maxtime
-                       // totaldamage / mindps > totaldamage / maxdps = maxtime
-                       // FROM THIS:
-                       // a) totaltime = max(mintime, maxtime) = maxtime
-                       // b) totaltime = min(maxtime, totaldamage / maxdps) = maxtime
-
-                       // assuming t <= mintime:
-                       // we get maxtime = mintime
-                       // a) totaltime = max(mintime, ...) >= mintime, also totaltime <= maxtime by the lemma, therefore totaltime = mintime = maxtime
-                       // b) totaltime = min(maxtime, ...) <= maxtime, also totaltime >= mintime by the lemma, therefore totaltime = mintime = maxtime
-
-                       // assuming dps <= mindps:
-                       // we get mindps = maxdps.
-                       // With this, the lemma says that mintime <= totaldamage / mindps = totaldamage / maxdps <= maxtime.
-                       // a) totaltime = max(mintime, totaldamage / maxdps) = totaldamage / maxdps
-                       // b) totaltime = min(maxtime, totaldamage / mindps) = totaldamage / maxdps
-
-                       e.fire_damagepersec = totaldamage / totaltime;
-                       e.fire_endtime = time + totaltime;
-                       if(totaldamage > 1.2 * mindamage)
-                       {
-                               e.fire_deathtype = dt;
-                               if(e.fire_owner != o)
-                               {
-                                       e.fire_owner = o;
-                                       e.fire_hitsound = false;
-                               }
-                       }
-                       if(accuracy_isgooddamage(o, e))
-                               accuracy_add(o, DEATH_WEAPONOF(dt), 0, max(0, totaldamage - mindamage));
-                       return max(0, totaldamage - mindamage); // can never be negative, but to make sure
-               }
-               else
-                       return 0;
-       }
-       else
-       {
-               e.fire_damagepersec = dps;
-               e.fire_endtime = time + t;
-               e.fire_deathtype = dt;
-               e.fire_owner = o;
-               e.fire_hitsound = false;
-               if(accuracy_isgooddamage(o, e))
-                       accuracy_add(o, DEATH_WEAPONOF(dt), 0, d);
-               return d;
-       }
-}
-
-void Fire_ApplyDamage(entity e)
-{
-       float t, d, hi, ty;
-       entity o;
-
-       if (!Fire_IsBurning(e))
-               return;
-
-       for(t = 0, o = e.owner; o.owner && t < 16; o = o.owner, ++t);
-       if(IS_NOT_A_CLIENT(o))
-               o = e.fire_owner;
-
-       // water and slime stop fire
-       if(e.waterlevel)
-       if(e.watertype != CONTENT_LAVA)
-               e.fire_endtime = 0;
-
-       // ice stops fire
-       if(STAT(FROZEN, e))
-               e.fire_endtime = 0;
-
-       t = min(frametime, e.fire_endtime - time);
-       d = e.fire_damagepersec * t;
-
-       hi = e.fire_owner.damage_dealt;
-       ty = e.fire_owner.typehitsound;
-       Damage(e, e, e.fire_owner, d, e.fire_deathtype, DMG_NOWEP, e.origin, '0 0 0');
-       if(e.fire_hitsound && e.fire_owner)
-       {
-               e.fire_owner.damage_dealt = hi;
-               e.fire_owner.typehitsound = ty;
-       }
-       e.fire_hitsound = true;
-
-       if(!IS_INDEPENDENT_PLAYER(e) && !STAT(FROZEN, e))
-       {
-               IL_EACH(g_damagedbycontents, it.damagedbycontents && it != e,
-               {
-                       if(!IS_DEAD(it) && it.takedamage && !IS_INDEPENDENT_PLAYER(it))
-                       if(boxesoverlap(e.absmin, e.absmax, it.absmin, it.absmax))
-                       {
-                               t = autocvar_g_balance_firetransfer_time * (e.fire_endtime - time);
-                               d = autocvar_g_balance_firetransfer_damage * e.fire_damagepersec * t;
-                               Fire_AddDamage(it, o, d, t, DEATH_FIRE.m_id);
-                       }
-               });
-       }
-}
-
-void Fire_ApplyEffect(entity e)
-{
-       if(Fire_IsBurning(e))
-               e.effects |= EF_FLAME;
-       else
-               e.effects &= ~EF_FLAME;
-}
-
-void fireburner_think(entity this)
-{
-       // for players, this is done in the regular loop
-       if(wasfreed(this.owner))
-       {
-               delete(this);
-               return;
-       }
-       Fire_ApplyEffect(this.owner);
-       if(!Fire_IsBurning(this.owner))
-       {
-               this.owner.fire_burner = NULL;
-               delete(this);
-               return;
-       }
-       Fire_ApplyDamage(this.owner);
-       this.nextthink = time;
-}
diff --git a/qcsrc/server/g_damage.qh b/qcsrc/server/g_damage.qh
deleted file mode 100644 (file)
index 2348c7a..0000000
+++ /dev/null
@@ -1,163 +0,0 @@
-#pragma once
-
-#if defined(CSQC)
-#elif defined(MENUQC)
-#elif defined(SVQC)
-    #include <common/weapons/_all.qh>
-    #include <common/stats.qh>
-    #include <server/items/items.qh>
-    #include <server/miscfunctions.qh>
-    #include <lib/warpzone/common.qh>
-    #include <common/constants.qh>
-    #include <common/teams.qh>
-    #include <common/util.qh>
-    #include <common/weapons/_all.qh>
-    #include "weapons/accuracy.qh"
-    #include "weapons/csqcprojectile.qh"
-    #include "weapons/selection.qh"
-    #include "autocvars.qh"
-    #include "constants.qh"
-    #include <common/notifications/all.qh>
-    #include <common/deathtypes/all.qh>
-    #include <server/mutators/_mod.qh>
-    #include <common/turrets/sv_turrets.qh>
-    #include <common/vehicles/all.qh>
-    #include <lib/csqcmodel/sv_model.qh>
-    #include <common/playerstats.qh>
-    #include "g_hook.qh"
-    #include "scores.qh"
-    #include "spawnpoints.qh"
-#endif
-
-.void(entity this, entity inflictor, entity attacker, float damage, int deathtype, .entity weaponentity, vector hitloc, vector force) event_damage;
-
-.bool(entity targ, entity inflictor, float amount, float limit) event_heal;
-
-.float dmg;
-.float dmg_edge;
-.float dmg_force;
-.float dmg_radius;
-
-bool Damage_DamageInfo_SendEntity(entity this, entity to, int sf);
-
-void Damage_DamageInfo(vector org, float coredamage, float edgedamage, float rad, vector force, int deathtype, float bloodtype, entity dmgowner);
-
-float checkrules_firstblood;
-
-.float damagedbycontents;
-.float damagedbytriggers;
-
-float yoda;
-float damage_goodhits;
-float damage_gooddamage;
-
-.float pain_finished; // Added by Supajoe
-
-.float dmg_team;
-.float teamkill_complain;
-.float teamkill_soundtime;
-.entity teamkill_soundsource;
-.entity pusher;
-.bool istypefrag;
-.float taunt_soundtime;
-
-.float spawnshieldtime;
-
-.int totalfrags;
-
-.bool canteamdamage;
-
-.vector death_origin;
-
-.float damage_dealt, typehitsound, killsound;
-
-// used for custom deathtype
-string deathmessage;
-
-float IsFlying(entity a);
-
-void UpdateFrags(entity player, int f);
-
-// NOTE: f=0 means still count as a (positive) kill, but count no frags for it
-void W_SwitchWeapon_Force(Player this, Weapon w, .entity weaponentity);
-void GiveFrags (entity attacker, entity targ, float f, int deathtype, .entity weaponentity);
-
-string AppendItemcodes(string s, entity player);
-
-void LogDeath(string mode, int deathtype, entity killer, entity killed);
-
-void Obituary_SpecialDeath(
-       entity notif_target,
-       float murder,
-       int deathtype,
-       string s1, string s2, string s3,
-       float f1, float f2, float f3);
-
-float w_deathtype;
-float Obituary_WeaponDeath(
-       entity notif_target,
-       float murder,
-       int deathtype,
-       string s1, string s2, string s3,
-       float f1, float f2);
-
-void Obituary(entity attacker, entity inflictor, entity targ, int deathtype, .entity weaponentity);
-
-// Frozen status effect
-//const int FROZEN_NOT              = 0;
-const int FROZEN_NORMAL             = 1;
-const int FROZEN_TEMP_REVIVING      = 2;
-const int FROZEN_TEMP_DYING         = 3;
-
-.float revival_time; // time at which player was last revived
-.float revive_speed; // NOTE: multiplier (anything above 1 is instaheal)
-.float freeze_time;
-.entity iceblock;
-.entity frozen_by; // for ice fields
-
-void Ice_Think(entity this);
-
-void Freeze(entity targ, float revivespeed, int frozen_type, bool show_waypoint);
-
-void Unfreeze(entity targ, bool reset_health);
-
-// WEAPONTODO
-#define DMG_NOWEP (weaponentities[0])
-
-// NOTE: the .weaponentity parameter can be set to DMG_NOWEP if the attack wasn't caused by a weapon or player
-void Damage (entity targ, entity inflictor, entity attacker, float damage, int deathtype, .entity weaponentity, vector hitloc, vector force);
-
-float RadiusDamage_running;
-float RadiusDamageForSource (entity inflictor, vector inflictororigin, vector inflictorvelocity, entity attacker, float coredamage, float edgedamage, float rad, entity cantbe, entity mustbe, float inflictorselfdamage, float forceintensity, float forcezscale, int deathtype, .entity weaponentity, entity directhitentity);
-       // Returns total damage applies to creatures
-
-float RadiusDamage (entity inflictor, entity attacker, float coredamage, float edgedamage, float rad, entity cantbe, entity mustbe, float forceintensity, int deathtype, .entity weaponentity, entity directhitentity);
-
-.float damageforcescale;
-const float MIN_DAMAGEEXTRARADIUS = 2;
-const float MAX_DAMAGEEXTRARADIUS = 16;
-.float damageextraradius;
-
-// Calls .event_heal on the target so that they can handle healing themselves
-// a limit of RES_LIMIT_NONE should be handled by the entity as its max health (if applicable)
-bool Heal(entity targ, entity inflictor, float amount, float limit);
-
-.float fire_damagepersec;
-.float fire_endtime;
-.float fire_deathtype;
-.entity fire_owner;
-.float fire_hitsound;
-.entity fire_burner;
-
-void fireburner_think(entity this);
-
-float Fire_IsBurning(entity e);
-
-float Fire_AddDamage(entity e, entity o, float d, float t, float dt);
-
-void Fire_ApplyDamage(entity e);
-
-void Fire_ApplyEffect(entity e);
-
-IntrusiveList g_damagedbycontents;
-STATIC_INIT(g_damagedbycontents) { g_damagedbycontents = IL_NEW(); }
diff --git a/qcsrc/server/g_hook.qc b/qcsrc/server/g_hook.qc
deleted file mode 100644 (file)
index 5794720..0000000
+++ /dev/null
@@ -1,435 +0,0 @@
-#include "g_hook.qh"
-
-#include <server/bot/api.qh>
-#include <common/weapons/_all.qh>
-#include <common/stats.qh>
-#include <server/g_damage.qh>
-#include <server/miscfunctions.qh>
-#include <common/effects/all.qh>
-#include "weapons/common.qh"
-#include "weapons/csqcprojectile.qh"
-#include "weapons/weaponsystem.qh"
-#include "weapons/selection.qh"
-#include "weapons/tracing.qh"
-#include "player.qh"
-#include "command/common.qh"
-#include "command/vote.qh"
-#include "round_handler.qh"
-#include "../common/state.qh"
-#include "../common/physics/player.qh"
-#include "../common/vehicles/all.qh"
-#include "../common/constants.qh"
-#include "../common/util.qh"
-#include <common/net_linked.qh>
-#include <common/weapons/_all.qh>
-#include "../lib/warpzone/common.qh"
-#include "../lib/warpzone/server.qh"
-
-/*============================================
-
-      Wazat's Xonotic Grappling Hook
-
-        Contact: Wazat1@gmail.com
-
-
-Installation instructions:
---------------------------
-
-1. Place hook.c in your gamec source directory with the other source files.
-
-2. Add this line to the bottom of progs.src:
-
-gamec/hook.c
-
-3. Open defs.h and add these lines to the very bottom:
-
-// Wazat's grappling hook
-.entity                hook;
-void GrapplingHookFrame();
-void RemoveGrapplingHook(entity pl);
-void SetGrappleHookBindings();
-// hook impulses
-const float GRAPHOOK_FIRE              = 20;
-const float GRAPHOOK_RELEASE           = 21;
-// (note: you can change the hook impulse #'s to whatever you please)
-
-4. Open client.c and add this to the top of PutClientInServer():
-
-       RemoveGrapplingHook(this); // Wazat's Grappling Hook
-
-5. Find ClientConnect() (in client.c) and add these lines to the bottom:
-
-       // Wazat's grappling hook
-       SetGrappleHookBindings();
-
-6. Still in client.c, find PlayerPreThink and add this line just above the call to W_WeaponFrame:
-
-       GrapplingHookFrame();
-
-7. Build and test the mod.  You'll want to bind a key to "+hook" like this:
-bind ctrl "+hook"
-
-And you should be done!
-
-
-============================================*/
-
-void RemoveGrapplingHooks(entity pl)
-{
-       if(pl.move_movetype == MOVETYPE_FLY)
-               set_movetype(pl, MOVETYPE_WALK);
-
-       for(int slot = 0; slot < MAX_WEAPONSLOTS; ++slot)
-    {
-       .entity weaponentity = weaponentities[slot];
-       if(!pl.(weaponentity))
-               continue; // continue incase other slots exist?
-       if(pl.(weaponentity).hook)
-               delete(pl.(weaponentity).hook);
-       pl.(weaponentity).hook = NULL;
-    }
-
-       //pl.disableclientprediction = false;
-}
-
-void RemoveHook(entity this)
-{
-       entity player = this.realowner;
-    .entity weaponentity = this.weaponentity_fld;
-
-    if(player.(weaponentity).hook == this)
-       player.(weaponentity).hook = NULL;
-
-    if(player.move_movetype == MOVETYPE_FLY)
-       set_movetype(player, MOVETYPE_WALK);
-    delete(this);
-}
-
-void GrapplingHookReset(entity this)
-{
-       RemoveHook(this);
-}
-
-void GrapplingHook_Stop(entity this)
-{
-       Send_Effect(EFFECT_HOOK_IMPACT, this.origin, '0 0 0', 1);
-       sound (this, CH_SHOTS, SND_HOOK_IMPACT, VOL_BASE, ATTEN_NORM);
-
-       this.state = 1;
-       setthink(this, GrapplingHookThink);
-       this.nextthink = time;
-       settouch(this, func_null);
-       this.velocity = '0 0 0';
-       set_movetype(this, MOVETYPE_NONE);
-       this.hook_length = -1;
-}
-
-.vector hook_start, hook_end;
-bool GrapplingHookSend(entity this, entity to, int sf)
-{
-       WriteHeader(MSG_ENTITY, ENT_CLIENT_HOOK);
-       sf = sf & 0x7F;
-       if(sound_allowed(MSG_BROADCAST, this.realowner))
-               sf |= 0x80;
-       WriteByte(MSG_ENTITY, sf);
-       if(sf & 1)
-       {
-               WriteByte(MSG_ENTITY, etof(this.realowner));
-               WriteByte(MSG_ENTITY, weaponslot(this.weaponentity_fld));
-       }
-       if(sf & 2)
-       {
-               WriteVector(MSG_ENTITY, this.hook_start);
-       }
-       if(sf & 4)
-       {
-               WriteVector(MSG_ENTITY, this.hook_end);
-       }
-       return true;
-}
-
-int autocvar_g_grappling_hook_tarzan;
-
-void GrapplingHookThink(entity this)
-{
-       float spd, dist, minlength, pullspeed, ropestretch, ropeairfriction, rubberforce, newlength, rubberforce_overstretch;
-       vector dir, org, end, v0, dv, v, myorg, vs;
-       .entity weaponentity = this.weaponentity_fld;
-       if(this.realowner.(weaponentity).hook != this)  // how did that happen?
-       {
-               error("Owner lost the hook!\n");
-               return;
-       }
-       if(LostMovetypeFollow(this) || game_stopped || (round_handler_IsActive() && !round_handler_IsRoundStarted()) || ((this.aiment.flags & FL_PROJECTILE) && this.aiment.classname != "nade"))
-       {
-               RemoveHook(this);
-               return;
-       }
-       if(this.aiment)
-               WarpZone_RefSys_AddIncrementally(this, this.aiment);
-
-       this.nextthink = time;
-
-       int s = W_GunAlign(this.realowner.(weaponentity), STAT(GUNALIGN, this.realowner)) - 1;
-       vs = hook_shotorigin[s];
-
-       makevectors(this.realowner.v_angle);
-       org = this.realowner.origin + this.realowner.view_ofs + v_forward * vs.x + v_right * -vs.y + v_up * vs.z;
-       myorg = WarpZone_RefSys_TransformOrigin(this.realowner, this, org);
-
-       if(this.hook_length < 0)
-               this.hook_length = vlen(myorg - this.origin);
-
-       int tarzan = autocvar_g_grappling_hook_tarzan;
-       entity pull_entity = this.realowner;
-       float velocity_multiplier = 1;
-       MUTATOR_CALLHOOK(GrappleHookThink, this, tarzan, pull_entity, velocity_multiplier);
-       tarzan = M_ARGV(1, int);
-       pull_entity = M_ARGV(2, entity);
-       velocity_multiplier = M_ARGV(3, float);
-
-       if(this.state == 1)
-       {
-               pullspeed = autocvar_g_balance_grapplehook_speed_pull;//2000;
-               // speed the rope is pulled with
-
-               rubberforce = autocvar_g_balance_grapplehook_force_rubber;//2000;
-               // force the rope will use if it is stretched
-
-               rubberforce_overstretch = autocvar_g_balance_grapplehook_force_rubber_overstretch;//1000;
-               // force the rope will use if it is stretched
-
-               minlength = autocvar_g_balance_grapplehook_length_min;//100;
-               // minimal rope length
-               // if the rope goes below this length, it isn't pulled any more
-
-               ropestretch = autocvar_g_balance_grapplehook_stretch;//400;
-               // if the rope is stretched by more than this amount, more rope is
-               // given to you again
-
-               ropeairfriction = autocvar_g_balance_grapplehook_airfriction;//0.2
-               // while hanging on the rope, this friction component will help you a
-               // bit to control the rope
-
-               bool frozen_pulling = (autocvar_g_grappling_hook_tarzan >= 2 && autocvar_g_balance_grapplehook_pull_frozen);
-
-               dir = this.origin - myorg;
-               dist = vlen(dir);
-               dir = normalize(dir);
-
-               if(tarzan)
-               {
-                       v = v0 = WarpZone_RefSys_TransformVelocity(pull_entity, this, pull_entity.velocity);
-
-                       // first pull the rope...
-                       if(this.realowner.(weaponentity).hook_state & HOOK_PULLING)
-                       {
-                               newlength = this.hook_length;
-                               newlength = max(newlength - pullspeed * frametime, minlength);
-
-                               if(newlength < dist - ropestretch) // overstretched?
-                               {
-                                       newlength = dist - ropestretch;
-                                       if(v * dir < 0) // only if not already moving in hook direction
-                                               v = v + frametime * dir * rubberforce_overstretch;
-                               }
-
-                               this.hook_length = newlength;
-                       }
-
-                       if(pull_entity.move_movetype == MOVETYPE_FLY)
-                               set_movetype(pull_entity, MOVETYPE_WALK);
-
-                       if(this.realowner.(weaponentity).hook_state & HOOK_RELEASING)
-                       {
-                               newlength = dist;
-                               this.hook_length = newlength;
-                       }
-                       else
-                       {
-                               // then pull the player
-                               spd = bound(0, (dist - this.hook_length) / ropestretch, 1);
-                               v = v * (1 - frametime * ropeairfriction);
-                               v = v + frametime * dir * spd * rubberforce;
-
-                               dv = ((v - v0) * dir) * dir;
-                               if(tarzan >= 2)
-                               {
-                                       if(this.aiment.move_movetype == MOVETYPE_WALK || this.aiment.classname == "nade")
-                                       {
-                                               entity aim_ent = ((IS_VEHICLE(this.aiment) && this.aiment.owner) ? this.aiment.owner : this.aiment);
-                                               v = v - dv * 0.5;
-                                               if((frozen_pulling && STAT(FROZEN, this.aiment)) || !frozen_pulling)
-                                               {
-                                                       this.aiment.velocity = this.aiment.velocity - dv * 0.5;
-                                                       UNSET_ONGROUND(this.aiment);
-                                                       if(this.aiment.flags & FL_PROJECTILE)
-                                                               UpdateCSQCProjectile(this.aiment);
-                                               }
-                                               if(this.aiment.classname == "nade")
-                                                       this.aiment.nextthink = time + autocvar_g_balance_grapplehook_nade_time; // set time after letting go?
-                                               aim_ent.pusher = this.realowner;
-                                               aim_ent.pushltime = time + autocvar_g_maxpushtime;
-                                               aim_ent.istypefrag = PHYS_INPUT_BUTTON_CHAT(aim_ent);
-                                       }
-                               }
-
-                               UNSET_ONGROUND(pull_entity);
-                       }
-
-                       if(!frozen_pulling && !(this.aiment.flags & FL_PROJECTILE))
-                               pull_entity.velocity = WarpZone_RefSys_TransformVelocity(this, pull_entity, v * velocity_multiplier);
-
-                       if(frozen_pulling && autocvar_g_balance_grapplehook_pull_frozen == 2 && !STAT(FROZEN, this.aiment))
-                       {
-                               RemoveHook(this);
-                               return;
-                       }
-               }
-               else
-               {
-                       end = this.origin - dir*50;
-                       dist = vlen(end - myorg);
-                       if(dist < 200)
-                               spd = dist * (pullspeed / 200);
-                       else
-                               spd = pullspeed;
-                       if(spd < 50)
-                               spd = 0;
-                       this.realowner.velocity = dir*spd;
-                       set_movetype(this.realowner, MOVETYPE_FLY);
-
-                       UNSET_ONGROUND(this.realowner);
-               }
-       }
-
-       makevectors(this.angles.x * '-1 0 0' + this.angles.y * '0 1 0');
-       myorg = WarpZone_RefSys_TransformOrigin(this, this.realowner, this.origin); // + v_forward * (-9);
-
-       if(myorg != this.hook_start)
-       {
-               this.SendFlags |= 2;
-               this.hook_start = myorg;
-       }
-       if(org != this.hook_end)
-       {
-               this.SendFlags |= 4;
-               this.hook_end = org;
-       }
-}
-
-void GrapplingHookTouch(entity this, entity toucher)
-{
-       if(toucher.move_movetype == MOVETYPE_FOLLOW)
-               return;
-       PROJECTILE_TOUCH(this, toucher);
-
-       GrapplingHook_Stop(this);
-
-       if(toucher)
-               //if(toucher.move_movetype != MOVETYPE_NONE)
-               {
-                       SetMovetypeFollow(this, toucher);
-                       WarpZone_RefSys_BeginAddingIncrementally(this, this.aiment);
-               }
-
-       //this.realowner.disableclientprediction = true;
-}
-
-void GrapplingHook_Damage(entity this, entity inflictor, entity attacker, float damage, int deathtype, .entity weaponentity, vector hitloc, vector force)
-{
-       if(GetResource(this, RES_HEALTH) <= 0)
-               return;
-
-       if (!W_CheckProjectileDamage(inflictor.realowner, this.realowner, deathtype, -1)) // no exceptions
-               return; // g_balance_projectiledamage says to halt
-
-       TakeResource(this, RES_HEALTH, damage);
-
-       if (GetResource(this, RES_HEALTH) <= 0)
-       {
-               if(attacker != this.realowner)
-               {
-                       this.realowner.pusher = attacker;
-                       this.realowner.pushltime = time + autocvar_g_maxpushtime;
-                       this.realowner.istypefrag = PHYS_INPUT_BUTTON_CHAT(this.realowner);
-               }
-               RemoveHook(this);
-       }
-}
-
-void FireGrapplingHook(entity actor, .entity weaponentity)
-{
-       if(weaponLocked(actor)) return;
-       if(actor.vehicle) return;
-
-       int s = W_GunAlign(actor.(weaponentity), STAT(GUNALIGN, actor)) - 1;
-       vector vs = hook_shotorigin[s];
-       vector oldmovedir = actor.(weaponentity).movedir;
-       actor.(weaponentity).movedir = vs;
-       W_SetupShot_ProjectileSize(actor, weaponentity, '-3 -3 -3', '3 3 3', true, 0, SND_HOOK_FIRE, CH_WEAPON_B, 0, WEP_HOOK.m_id);
-       W_MuzzleFlash(WEP_HOOK, actor, weaponentity, w_shotorg, '0 0 0');
-       actor.(weaponentity).movedir = oldmovedir;
-
-       entity missile = WarpZone_RefSys_SpawnSameRefSys(actor);
-       missile.owner = missile.realowner = actor;
-       actor.(weaponentity).hook = missile;
-       missile.weaponentity_fld = weaponentity;
-       missile.reset = GrapplingHookReset;
-       missile.classname = "grapplinghook";
-       missile.flags = FL_PROJECTILE;
-       IL_PUSH(g_projectiles, missile);
-       IL_PUSH(g_bot_dodge, missile);
-
-       set_movetype(missile, ((autocvar_g_balance_grapplehook_gravity) ? MOVETYPE_TOSS : MOVETYPE_FLY));
-       PROJECTILE_MAKETRIGGER(missile);
-
-       //setmodel (missile, MDL_HOOK); // precision set below
-       setsize (missile, '-3 -3 -3', '3 3 3');
-       setorigin(missile, w_shotorg);
-
-       missile.state = 0; // not latched onto anything
-
-       W_SetupProjVelocity_Explicit(missile, w_shotdir, v_up, autocvar_g_balance_grapplehook_speed_fly, 0, 0, 0, false);
-
-       missile.angles = vectoangles (missile.velocity);
-       //missile.glow_color = 250; // 244, 250
-       //missile.glow_size = 120;
-       settouch(missile, GrapplingHookTouch);
-       setthink(missile, GrapplingHookThink);
-       missile.nextthink = time;
-
-       missile.effects = /*EF_FULLBRIGHT | EF_ADDITIVE |*/ EF_LOWPRECISION;
-
-       SetResourceExplicit(missile, RES_HEALTH, autocvar_g_balance_grapplehook_health);
-       missile.event_damage = GrapplingHook_Damage;
-       missile.takedamage = DAMAGE_AIM;
-       missile.damageforcescale = 0;
-       missile.damagedbycontents = (autocvar_g_balance_grapplehook_damagedbycontents);
-       if(missile.damagedbycontents)
-               IL_PUSH(g_damagedbycontents, missile);
-
-       missile.hook_start = missile.hook_end = missile.origin;
-
-       Net_LinkEntity(missile, false, 0, GrapplingHookSend);
-}
-
-void GrappleHookInit()
-{
-       if(g_grappling_hook)
-       {
-               hook_shotorigin[0] = '8 8 -12';
-               hook_shotorigin[1] = '8 8 -12';
-               hook_shotorigin[2] = '8 8 -12';
-               hook_shotorigin[3] = '8 8 -12';
-       }
-       else
-       {
-               Weapon w = WEP_HOOK;
-               w.wr_init(w);
-               hook_shotorigin[0] = shotorg_adjust_values(CL_Weapon_GetShotOrg(WEP_HOOK.m_id), false, false, 1);
-               hook_shotorigin[1] = shotorg_adjust_values(CL_Weapon_GetShotOrg(WEP_HOOK.m_id), false, false, 2);
-               hook_shotorigin[2] = shotorg_adjust_values(CL_Weapon_GetShotOrg(WEP_HOOK.m_id), false, false, 3);
-               hook_shotorigin[3] = shotorg_adjust_values(CL_Weapon_GetShotOrg(WEP_HOOK.m_id), false, false, 4);
-       }
-}
diff --git a/qcsrc/server/g_hook.qh b/qcsrc/server/g_hook.qh
deleted file mode 100644 (file)
index 1ed78e2..0000000
+++ /dev/null
@@ -1,23 +0,0 @@
-#pragma once
-
-// Wazat's grappling hook
-.entity                hook;
-void GrapplingHookThink(entity this);
-void RemoveGrapplingHooks(entity pl);
-void RemoveHook(entity this);
-// (note: you can change the hook impulse #'s to whatever you please)
-.float hook_time;
-
-.float hook_length;
-
-const float HOOK_FIRING = BIT(0);
-const float HOOK_REMOVING = BIT(1);
-const float HOOK_PULLING = BIT(2);
-const float HOOK_RELEASING = BIT(3);
-const float HOOK_WAITING_FOR_RELEASE = BIT(4);
-.float hook_state;
-.int state;
-
-void GrappleHookInit();
-vector hook_shotorigin[4];
-
diff --git a/qcsrc/server/g_world.qc b/qcsrc/server/g_world.qc
deleted file mode 100644 (file)
index e258e35..0000000
+++ /dev/null
@@ -1,2266 +0,0 @@
-#include "g_world.qh"
-
-#include "anticheat.qh"
-#include "antilag.qh"
-#include "bot/api.qh"
-#include "campaign.qh"
-#include "cheats.qh"
-#include "client.qh"
-#include "command/common.qh"
-#include "command/getreplies.qh"
-#include "command/sv_cmd.qh"
-#include "command/vote.qh"
-#include "g_hook.qh"
-#include <server/gamelog.qh>
-#include <server/g_damage.qh>
-#include "ipban.qh"
-#include "mapvoting.qh"
-#include <server/mutators/_mod.qh>
-#include "race.qh"
-#include "scores.qh"
-#include "scores_rules.qh"
-#include "spawnpoints.qh"
-#include "teamplay.qh"
-#include "weapons/weaponstats.qh"
-#include <server/weapons/common.qh>
-#include "../common/constants.qh"
-#include <common/net_linked.qh>
-#include "../common/deathtypes/all.qh"
-#include <common/gamemodes/_mod.qh>
-#include "../common/gamemodes/sv_rules.qh"
-#include "../common/mapinfo.qh"
-#include "../common/monsters/_mod.qh"
-#include "../common/monsters/sv_monsters.qh"
-#include "../common/vehicles/all.qh"
-#include "../common/notifications/all.qh"
-#include "../common/physics/player.qh"
-#include "../common/playerstats.qh"
-#include "../common/stats.qh"
-#include "../common/teams.qh"
-#include <common/mapobjects/triggers.qh>
-#include "../common/mapobjects/trigger/secret.qh"
-#include "../common/mapobjects/target/music.qh"
-#include "../common/util.qh"
-#include "../common/items/_mod.qh"
-#include <common/weapons/_all.qh>
-#include "../common/state.qh"
-
-const float LATENCY_THINKRATE = 10;
-.float latency_sum;
-.float latency_cnt;
-.float latency_time;
-entity pingplreport;
-void PingPLReport_Think(entity this)
-{
-       float delta;
-       entity e;
-
-       delta = 3 / maxclients;
-       if(delta < sys_frametime)
-               delta = 0;
-       this.nextthink = time + delta;
-
-       e = edict_num(this.cnt + 1);
-       if(IS_CLIENT(e) && IS_REAL_CLIENT(e))
-       {
-               WriteHeader(MSG_BROADCAST, TE_CSQC_PINGPLREPORT);
-               WriteByte(MSG_BROADCAST, this.cnt);
-               WriteShort(MSG_BROADCAST, bound(1, CS(e).ping, 65535));
-               WriteByte(MSG_BROADCAST, min(ceil(CS(e).ping_packetloss * 255), 255));
-               WriteByte(MSG_BROADCAST, min(ceil(CS(e).ping_movementloss * 255), 255));
-
-               // record latency times for clients throughout the match so we can report it to playerstats
-               if(time > (CS(e).latency_time + LATENCY_THINKRATE))
-               {
-                       CS(e).latency_sum += CS(e).ping;
-                       CS(e).latency_cnt += 1;
-                       CS(e).latency_time = time;
-                       //print("sum: ", ftos(CS(e).latency_sum), ", cnt: ", ftos(CS(e).latency_cnt), ", avg: ", ftos(CS(e).latency_sum / CS(e).latency_cnt), ".\n");
-               }
-       }
-       else
-       {
-               WriteHeader(MSG_BROADCAST, TE_CSQC_PINGPLREPORT);
-               WriteByte(MSG_BROADCAST, this.cnt);
-               WriteShort(MSG_BROADCAST, 0);
-               WriteByte(MSG_BROADCAST, 0);
-               WriteByte(MSG_BROADCAST, 0);
-       }
-       this.cnt = (this.cnt + 1) % maxclients;
-}
-void PingPLReport_Spawn()
-{
-       pingplreport = new_pure(pingplreport);
-       setthink(pingplreport, PingPLReport_Think);
-       pingplreport.nextthink = time;
-}
-
-const float SPAWNFLAG_NO_WAYPOINTS_FOR_ITEMS = 1;
-string redirection_target;
-float world_initialized;
-
-void SetDefaultAlpha()
-{
-       if (!MUTATOR_CALLHOOK(SetDefaultAlpha))
-       {
-               default_player_alpha = autocvar_g_player_alpha;
-               if(default_player_alpha == 0)
-                       default_player_alpha = 1;
-               default_weapon_alpha = default_player_alpha;
-       }
-}
-
-void GotoFirstMap(entity this)
-{
-       float n;
-       if(autocvar__sv_init)
-       {
-               // cvar_set("_sv_init", "0");
-               // we do NOT set this to 0 any more, so someone "accidentally" changing
-               // to this "init" map on a dedicated server will cause no permanent
-               // harm
-               if(autocvar_g_maplist_shuffle)
-                       ShuffleMaplist();
-               n = tokenizebyseparator(autocvar_g_maplist, " ");
-               cvar_set("g_maplist_index", ftos(n - 1)); // jump to map 0 in GotoNextMap
-
-               MapInfo_Enumerate();
-               MapInfo_FilterGametype(MapInfo_CurrentGametype(), MapInfo_CurrentFeatures(), MapInfo_RequiredFlags(), MapInfo_ForbiddenFlags(), 0);
-
-               if(!DoNextMapOverride(1))
-                       GotoNextMap(1);
-
-               return;
-       }
-
-       if(time < 5)
-       {
-               this.nextthink = time;
-       }
-       else
-       {
-               this.nextthink = time + 1;
-               LOG_INFO("Waiting for _sv_init being set to 1 by initialization scripts...");
-       }
-}
-
-void cvar_changes_init()
-{
-       float h;
-       string k, v, d;
-       float n, i, adding, pureadding;
-
-       strfree(cvar_changes);
-       strfree(cvar_purechanges);
-       cvar_purechanges_count = 0;
-
-       h = buf_create();
-       buf_cvarlist(h, "", "_"); // exclude all _ cvars as they are temporary
-       n = buf_getsize(h);
-
-       adding = true;
-       pureadding = true;
-
-       for(i = 0; i < n; ++i)
-       {
-               k = bufstr_get(h, i);
-
-#define BADPREFIX(p) if(substring(k, 0, strlen(p)) == p) continue
-#define BADPRESUFFIX(p,s) if(substring(k, 0, strlen(p)) == p && substring(k, -strlen(s), -1) == s) continue
-#define BADCVAR(p) if(k == p) continue
-
-               // general excludes and namespaces for server admin used cvars
-               BADPREFIX("help_"); // PN's server has this listed as changed, let's not rat him out for THAT
-
-               // internal
-               BADPREFIX("csqc_");
-               BADPREFIX("cvar_check_");
-               BADCVAR("gamecfg");
-               BADCVAR("g_configversion");
-               BADCVAR("halflifebsp");
-               BADCVAR("sv_mapformat_is_quake2");
-               BADCVAR("sv_mapformat_is_quake3");
-               BADPREFIX("sv_world");
-
-               // client
-               BADPREFIX("chase_");
-               BADPREFIX("cl_");
-               BADPREFIX("con_");
-               BADPREFIX("scoreboard_");
-               BADPREFIX("g_campaign");
-               BADPREFIX("g_waypointsprite_");
-               BADPREFIX("gl_");
-               BADPREFIX("joy");
-               BADPREFIX("hud_");
-               BADPREFIX("m_");
-               BADPREFIX("menu_");
-               BADPREFIX("net_slist_");
-               BADPREFIX("r_");
-               BADPREFIX("sbar_");
-               BADPREFIX("scr_");
-               BADPREFIX("snd_");
-               BADPREFIX("show");
-               BADPREFIX("sensitivity");
-               BADPREFIX("userbind");
-               BADPREFIX("v_");
-               BADPREFIX("vid_");
-               BADPREFIX("crosshair");
-               BADCVAR("mod_q3bsp_lightmapmergepower");
-               BADCVAR("mod_q3bsp_nolightmaps");
-               BADCVAR("fov");
-               BADCVAR("mastervolume");
-               BADCVAR("volume");
-               BADCVAR("bgmvolume");
-               BADCVAR("in_pitch_min");
-               BADCVAR("in_pitch_max");
-
-               // private
-               BADCVAR("developer");
-               BADCVAR("log_dest_udp");
-               BADCVAR("net_address");
-               BADCVAR("net_address_ipv6");
-               BADCVAR("port");
-               BADCVAR("savedgamecfg");
-               BADCVAR("serverconfig");
-               BADCVAR("sv_autoscreenshot");
-               BADCVAR("sv_heartbeatperiod");
-               BADCVAR("sv_vote_master_password");
-               BADCVAR("sys_colortranslation");
-               BADCVAR("sys_specialcharactertranslation");
-               BADCVAR("timeformat");
-               BADCVAR("timestamps");
-               BADCVAR("g_require_stats");
-               BADPREFIX("developer_");
-               BADPREFIX("g_ban_");
-               BADPREFIX("g_banned_list");
-               BADPREFIX("g_require_stats_");
-               BADPREFIX("g_chat_flood_");
-               BADPREFIX("g_ghost_items");
-               BADPREFIX("g_playerstats_");
-               BADPREFIX("g_voice_flood_");
-               BADPREFIX("log_file");
-               BADPREFIX("quit_");
-               BADPREFIX("rcon_");
-               BADPREFIX("sv_allowdownloads");
-               BADPREFIX("sv_autodemo");
-               BADPREFIX("sv_curl_");
-               BADPREFIX("sv_eventlog");
-               BADPREFIX("sv_logscores_");
-               BADPREFIX("sv_master");
-               BADPREFIX("sv_weaponstats_");
-               BADPREFIX("sv_waypointsprite_");
-               BADCVAR("rescan_pending");
-
-               // these can contain player IDs, so better hide
-               BADPREFIX("g_forced_team_");
-               BADCVAR("sv_muteban_list");
-               BADCVAR("sv_voteban_list");
-               BADCVAR("sv_allow_customplayermodels_idlist");
-               BADCVAR("sv_allow_customplayermodels_speciallist");
-
-               // mapinfo
-               BADCVAR("fraglimit");
-               BADCVAR("g_arena");
-               BADCVAR("g_assault");
-               BADCVAR("g_ca");
-               BADCVAR("g_ca_teams");
-               BADCVAR("g_conquest");
-               BADCVAR("g_conquest_teams");
-               BADCVAR("g_ctf");
-               BADCVAR("g_cts");
-               BADCVAR("g_dotc");
-               BADCVAR("g_dm");
-               BADCVAR("g_domination");
-               BADCVAR("g_domination_default_teams");
-               BADCVAR("g_duel");
-               BADCVAR("g_duel_not_dm_maps");
-               BADCVAR("g_freezetag");
-               BADCVAR("g_freezetag_teams");
-               BADCVAR("g_invasion_teams");
-               BADCVAR("g_invasion_type");
-               BADCVAR("g_jailbreak");
-               BADCVAR("g_jailbreak_teams");
-               BADCVAR("g_keepaway");
-               BADCVAR("g_keyhunt");
-               BADCVAR("g_keyhunt_teams");
-               BADCVAR("g_lms");
-               BADCVAR("g_nexball");
-               BADCVAR("g_onslaught");
-               BADCVAR("g_race");
-               BADCVAR("g_race_laps_limit");
-               BADCVAR("g_race_qualifying_timelimit");
-               BADCVAR("g_race_qualifying_timelimit_override");
-               BADCVAR("g_runematch");
-               BADCVAR("g_shootfromeye");
-               BADCVAR("g_snafu");
-               BADCVAR("g_survival");
-               BADCVAR("g_survival_not_dm_maps");
-               BADCVAR("g_tdm");
-               BADCVAR("g_tdm_on_dm_maps");
-               BADCVAR("g_tdm_teams");
-               BADCVAR("g_vip");
-               BADCVAR("leadlimit");
-               BADCVAR("nextmap");
-               BADCVAR("teamplay");
-               BADCVAR("timelimit");
-               BADCVAR("g_mapinfo_settemp_acl");
-               BADCVAR("g_mapinfo_ignore_warnings");
-               BADCVAR("g_maplist_ignore_sizes");
-               BADCVAR("g_maplist_sizes_count_bots");
-
-               // long
-               BADCVAR("hostname");
-               BADCVAR("g_maplist");
-               BADCVAR("g_maplist_mostrecent");
-               BADCVAR("sv_motd");
-
-               v = cvar_string(k);
-               d = cvar_defstring(k);
-               if(v == d)
-                       continue;
-
-               if(adding)
-               {
-                       cvar_changes = strcat(cvar_changes, k, " \"", v, "\" // \"", d, "\"\n");
-                       if(strlen(cvar_changes) > 16384)
-                       {
-                               cvar_changes = "// too many settings have been changed to show them here\n";
-                               adding = 0;
-                       }
-               }
-
-               // now check if the changes are actually gameplay relevant
-
-               // does nothing gameplay relevant
-               BADCVAR("captureleadlimit_override");
-               BADCVAR("condump_stripcolors");
-               BADCVAR("gameversion");
-               BADCVAR("fs_gamedir");
-               BADCVAR("g_allow_oldvortexbeam");
-               BADCVAR("g_balance_kill_delay");
-               BADCVAR("g_buffs_pickup_anyway");
-               BADCVAR("g_buffs_randomize");
-               BADCVAR("g_buffs_randomize_teamplay");
-               BADCVAR("g_campcheck_distance");
-               BADCVAR("g_chatsounds");
-               BADCVAR("g_ca_point_leadlimit");
-               BADCVAR("g_ca_point_limit");
-               BADCVAR("g_ctf_captimerecord_always");
-               BADCVAR("g_ctf_flag_glowtrails");
-               BADCVAR("g_ctf_dynamiclights");
-               BADCVAR("g_ctf_flag_pickup_verbosename");
-               BADPRESUFFIX("g_ctf_flag_", "_model");
-               BADPRESUFFIX("g_ctf_flag_", "_skin");
-               BADCVAR("g_domination_point_leadlimit");
-               BADCVAR("g_forced_respawn");
-               BADCVAR("g_freezetag_point_leadlimit");
-               BADCVAR("g_freezetag_point_limit");
-               BADCVAR("g_glowtrails");
-               BADCVAR("g_hats");
-               BADCVAR("g_casings");
-               BADCVAR("g_invasion_point_limit");
-               BADCVAR("g_jump_grunt");
-               BADCVAR("g_keepaway_ballcarrier_effects");
-               BADCVAR("g_keepawayball_effects");
-               BADCVAR("g_keyhunt_point_leadlimit");
-               BADCVAR("g_nexball_goalleadlimit");
-               BADCVAR("g_new_toys_autoreplace");
-               BADCVAR("g_new_toys_use_pickupsound");
-               BADCVAR("g_physics_predictall");
-               BADCVAR("g_piggyback");
-               BADCVAR("g_playerclip_collisions");
-               BADCVAR("g_spawn_alloweffects");
-               BADCVAR("g_tdm_point_leadlimit");
-               BADCVAR("g_tdm_point_limit");
-               BADCVAR("leadlimit_and_fraglimit");
-               BADCVAR("leadlimit_override");
-               BADCVAR("pausable");
-               BADCVAR("sv_announcer");
-               BADCVAR("sv_checkforpacketsduringsleep");
-               BADCVAR("sv_damagetext");
-               BADCVAR("sv_db_saveasdump");
-               BADCVAR("sv_intermission_cdtrack");
-               BADCVAR("sv_mapchange_delay");
-               BADCVAR("sv_minigames");
-               BADCVAR("sv_namechangetimer");
-               BADCVAR("sv_precacheplayermodels");
-               BADCVAR("sv_radio");
-               BADCVAR("sv_stepheight");
-               BADCVAR("sv_timeout");
-               BADCVAR("sv_weapons_modeloverride");
-               BADCVAR("w_prop_interval");
-               BADPREFIX("chat_");
-               BADPREFIX("crypto_");
-               BADPREFIX("gameversion_");
-               BADPREFIX("g_chat_");
-               BADPREFIX("g_ctf_captimerecord_");
-               BADPREFIX("g_hats_");
-               BADPREFIX("g_maplist_");
-               BADPREFIX("g_mod_");
-               BADPREFIX("g_respawn_");
-               BADPREFIX("net_");
-               BADPREFIX("notification_");
-               BADPREFIX("prvm_");
-               BADPREFIX("skill_");
-               BADPREFIX("sv_allow_");
-               BADPREFIX("sv_cullentities_");
-               BADPREFIX("sv_maxidle_");
-               BADPREFIX("sv_minigames_");
-               BADPREFIX("sv_radio_");
-               BADPREFIX("sv_timeout_");
-               BADPREFIX("sv_vote_");
-               BADPREFIX("timelimit_");
-
-               // allowed changes to server admins (please sync this to server.cfg)
-               // vi commands:
-               //   :/"impure"/,$d
-               //   :g!,^\/\/[^ /],d
-               //   :%s,//\([^ ]*\).*,BADCVAR("\1");,
-               //   :%!sort
-               // yes, this does contain some redundant stuff, don't really care
-               BADPREFIX("bot_ai_");
-               BADCVAR("bot_config_file");
-               BADCVAR("bot_number");
-               BADCVAR("bot_prefix");
-               BADCVAR("bot_suffix");
-               BADCVAR("capturelimit_override");
-               BADCVAR("fraglimit_override");
-               BADCVAR("gametype");
-               BADCVAR("g_antilag");
-               BADCVAR("g_balance_teams");
-               BADCVAR("g_balance_teams_prevent_imbalance");
-               BADCVAR("g_balance_teams_scorefactor");
-               BADCVAR("g_ban_sync_trusted_servers");
-               BADCVAR("g_ban_sync_uri");
-               BADCVAR("g_buffs");
-               BADCVAR("g_ca_teams_override");
-               BADCVAR("g_ctf_fullbrightflags");
-               BADCVAR("g_ctf_ignore_frags");
-               BADCVAR("g_ctf_leaderboard");
-               BADCVAR("g_domination_point_limit");
-               BADCVAR("g_domination_teams_override");
-               BADCVAR("g_freezetag_teams_override");
-               BADCVAR("g_friendlyfire");
-               BADCVAR("g_fullbrightitems");
-               BADCVAR("g_fullbrightplayers");
-               BADCVAR("g_keyhunt_point_limit");
-               BADCVAR("g_keyhunt_teams_override");
-               BADCVAR("g_lms_lives_override");
-               BADCVAR("g_maplist");
-               BADCVAR("g_maxplayers");
-               BADCVAR("g_mirrordamage");
-               BADCVAR("g_nexball_goallimit");
-               BADCVAR("g_norecoil");
-               BADCVAR("g_physics_clientselect");
-               BADCVAR("g_pinata");
-               BADCVAR("g_powerups");
-               BADCVAR("g_player_brightness");
-               BADCVAR("g_rocket_flying");
-               BADCVAR("g_rocket_flying_disabledelays");
-               BADCVAR("g_spawnshieldtime");
-               BADCVAR("g_start_delay");
-               BADCVAR("g_superspectate");
-               BADCVAR("g_tdm_teams_override");
-               BADCVAR("g_warmup");
-               BADCVAR("g_weapon_stay"); BADPRESUFFIX("g_", "_weapon_stay");
-               BADCVAR("hostname");
-               BADCVAR("log_file");
-               BADCVAR("maxplayers");
-               BADCVAR("minplayers");
-               BADCVAR("minplayers_per_team");
-               BADCVAR("net_address");
-               BADCVAR("port");
-               BADCVAR("rcon_password");
-               BADCVAR("rcon_restricted_commands");
-               BADCVAR("rcon_restricted_password");
-               BADCVAR("skill");
-               BADCVAR("sv_adminnick");
-               BADCVAR("sv_autoscreenshot");
-               BADCVAR("sv_autotaunt");
-               BADCVAR("sv_curl_defaulturl");
-               BADCVAR("sv_defaultcharacter");
-               BADCVAR("sv_defaultcharacterskin");
-               BADCVAR("sv_defaultplayercolors");
-               BADCVAR("sv_defaultplayermodel");
-               BADCVAR("sv_defaultplayerskin");
-               BADCVAR("sv_maxidle");
-               BADCVAR("sv_maxrate");
-               BADCVAR("sv_motd");
-               BADCVAR("sv_public");
-               BADCVAR("sv_ready_restart");
-               BADCVAR("sv_status_privacy");
-               BADCVAR("sv_taunt");
-               BADCVAR("sv_vote_call");
-               BADCVAR("sv_vote_commands");
-               BADCVAR("sv_vote_majority_factor");
-               BADCVAR("sv_vote_master");
-               BADCVAR("sv_vote_master_commands");
-               BADCVAR("sv_vote_master_password");
-               BADCVAR("sv_vote_simple_majority_factor");
-               BADCVAR("teamplay_mode");
-               BADCVAR("timelimit_override");
-               BADPREFIX("g_warmup_");
-               BADPREFIX("sv_info_");
-               BADPREFIX("sv_ready_restart_");
-
-               // mutators that announce themselves properly to the server browser
-               BADCVAR("g_instagib");
-               BADCVAR("g_new_toys");
-               BADCVAR("g_nix");
-               BADCVAR("g_grappling_hook");
-               BADCVAR("g_jetpack");
-
-               // temporary for testing
-               // TODO remove before 0.8.3 release
-               BADCVAR("g_ca_weaponarena");
-               BADCVAR("g_freezetag_weaponarena");
-               BADCVAR("g_lms_weaponarena");
-               BADCVAR("g_ctf_stalemate_time");
-
-               if(cvar_string("g_mod_balance") == "Testing")
-               {
-                       // (temporary) while using the Testing balance, any weapon balance cvars are allowed to be changed
-                       BADPREFIX("g_balance_");
-               }
-
-#undef BADPRESUFFIX
-#undef BADPREFIX
-#undef BADCVAR
-
-               if(pureadding)
-               {
-                       cvar_purechanges = strcat(cvar_purechanges, k, " \"", v, "\" // \"", d, "\"\n");
-                       if(strlen(cvar_purechanges) > 16384)
-                       {
-                               cvar_purechanges = "// too many settings have been changed to show them here\n";
-                               pureadding = 0;
-                       }
-               }
-               ++cvar_purechanges_count;
-               // WARNING: this variable is used for the server list
-               // NEVER dare to skip this code!
-               // Hacks to intentionally appearing as "pure server" even though you DO have
-               // modified settings may be punished by removal from the server list.
-               // You can do to the variables cvar_changes and cvar_purechanges all you want,
-               // though.
-       }
-       buf_del(h);
-       if(cvar_changes == "")
-               cvar_changes = "// this server runs at default server settings\n";
-       else
-               cvar_changes = strcat("// this server runs at modified server settings:\n", cvar_changes);
-       cvar_changes = strzone(cvar_changes);
-       if(cvar_purechanges == "")
-               cvar_purechanges = "// this server runs at default gameplay settings\n";
-       else
-               cvar_purechanges = strcat("// this server runs at modified gameplay settings:\n", cvar_purechanges);
-       cvar_purechanges = strzone(cvar_purechanges);
-}
-
-entity randomseed;
-bool RandomSeed_Send(entity this, entity to, int sf)
-{
-       WriteHeader(MSG_ENTITY, ENT_CLIENT_RANDOMSEED);
-       WriteShort(MSG_ENTITY, this.cnt);
-       return true;
-}
-void RandomSeed_Think(entity this)
-{
-       this.cnt = bound(0, floor(random() * 65536), 65535);
-       this.nextthink = time + 5;
-
-       this.SendFlags |= 1;
-}
-void RandomSeed_Spawn()
-{
-       randomseed = new_pure(randomseed);
-       setthink(randomseed, RandomSeed_Think);
-       Net_LinkEntity(randomseed, false, 0, RandomSeed_Send);
-
-       getthink(randomseed)(randomseed); // sets random seed and nextthink
-}
-
-spawnfunc(__init_dedicated_server)
-{
-       // handler for _init/_init map (only for dedicated server initialization)
-
-       world_initialized = -1; // don't complain
-
-       delete_fn = remove_unsafely;
-
-       entity e = spawn();
-       setthink(e, GotoFirstMap);
-       e.nextthink = time; // this is usually 1 at this point
-
-       e = new(info_player_deathmatch);  // safeguard against player joining
-
-    // assign reflectively to avoid "assignment to world" warning
-    for (int i = 0, n = numentityfields(); i < n; ++i) {
-        string k = entityfieldname(i);
-        if (k == "classname") {
-            // safeguard against various stuff ;)
-            putentityfieldstring(i, this, "worldspawn");
-            break;
-        }
-    }
-
-       // needs to be done so early because of the constants they create
-       static_init();
-       static_init_late();
-       static_init_precache();
-
-       IL_PUSH(g_spawnpoints, e); // just incase
-
-       MapInfo_Enumerate();
-       MapInfo_FilterGametype(MapInfo_CurrentGametype(), MapInfo_CurrentFeatures(), MapInfo_RequiredFlags(), MapInfo_ForbiddenFlags(), 0);
-}
-
-void __init_dedicated_server_shutdown() {
-       MapInfo_Shutdown();
-}
-
-STATIC_INIT_EARLY(maxclients)
-{
-       maxclients = 0;
-       for (entity head = nextent(NULL); head; head = nextent(head)) {
-               ++maxclients;
-       }
-}
-
-void default_delayedinit(entity this)
-{
-       if(!scores_initialized)
-               ScoreRules_generic();
-}
-
-void InitGameplayMode()
-{
-       VoteReset();
-
-       // find out good world mins/maxs bounds, either the static bounds found by looking for solid, or the mapinfo specified bounds
-       get_mi_min_max(1);
-       // assign reflectively to avoid "assignment to world" warning
-       int done = 0; for (int i = 0, n = numentityfields(); i < n; ++i) {
-           string k = entityfieldname(i); vector v = (k == "mins") ? mi_min : (k == "maxs") ? mi_max : '0 0 0';
-           if (v) {
-            putentityfieldstring(i, world, sprintf("%v", v));
-            if (++done == 2) break;
-        }
-       }
-       // currently, NetRadiant's limit is 131072 qu for each side
-       // distance from one corner of a 131072qu cube to the opposite corner is approx. 227023 qu
-       // set the distance according to map size but don't go over the limit to avoid issues with float precision
-       // in case somebody makes extremely large maps
-       max_shot_distance = min(230000, vlen(world.maxs - world.mins));
-
-       MapInfo_LoadMapSettings(mapname);
-       GameRules_teams(false);
-
-       if (!cvar_value_issafe(world.fog))
-       {
-               LOG_INFO("The current map contains a potentially harmful fog setting, ignored");
-               world.fog = string_null;
-       }
-       if(MapInfo_Map_fog != "")
-       {
-               if(MapInfo_Map_fog == "none")
-                       world.fog = string_null;
-               else
-                       world.fog = strzone(MapInfo_Map_fog);
-       }
-       clientstuff = strzone(MapInfo_Map_clientstuff);
-
-       MapInfo_ClearTemps();
-
-       gamemode_name = MapInfo_Type_ToText(MapInfo_LoadedGametype);
-
-       cache_mutatormsg = strzone("");
-       cache_lastmutatormsg = strzone("");
-
-       InitializeEntity(NULL, default_delayedinit, INITPRIO_GAMETYPE_FALLBACK);
-}
-
-void Map_MarkAsRecent(string m);
-float world_already_spawned;
-spawnfunc(worldspawn)
-{
-       server_is_dedicated = boolean(stof(cvar_defstring("is_dedicated")));
-
-       bool wantrestart = false;
-       {
-               if (!server_is_dedicated)
-               {
-                       // force unloading of server pk3 files when starting a listen server
-                       // localcmd("\nfs_rescan\n"); // FIXME: does more harm than good, has unintended side effects. What we really want is to unload temporary pk3s only
-                       // restore csqc_progname too
-                       string expect = "csprogs.dat";
-                       wantrestart = cvar_string("csqc_progname") != expect;
-                       cvar_set("csqc_progname", expect);
-               }
-               else
-               {
-                       // Try to use versioned csprogs from pk3
-                       // Only ever use versioned csprogs.dat files on dedicated servers;
-                       // we need to reset csqc_progname on clients ourselves, and it's easier if the client's release name is constant
-                       string pk3csprogs = "csprogs-" WATERMARK ".dat";
-                       // This always works; fall back to it if a versioned csprogs.dat is suddenly missing
-                       string select = "csprogs.dat";
-                       if (fexists(pk3csprogs)) select = pk3csprogs;
-                       if (cvar_string("csqc_progname") != select)
-                       {
-                               cvar_set("csqc_progname", select);
-                               wantrestart = true;
-                       }
-                       // Check for updates on startup
-                       // We do it this way for atomicity so that connecting clients still match the server progs and don't disconnect
-                       int sentinel = fopen("progs.txt", FILE_READ);
-                       if (sentinel >= 0)
-                       {
-                               string switchversion = fgets(sentinel);
-                               fclose(sentinel);
-                               if (switchversion != "" && switchversion != WATERMARK)
-                               {
-                                       LOG_INFOF("Switching progs: " WATERMARK " -> %s", switchversion);
-                                       // if it doesn't exist, assume either:
-                                       //   a) the current program was overwritten
-                                       //   b) this is a client only update
-                                       string newprogs = sprintf("progs-%s.dat", switchversion);
-                                       if (fexists(newprogs))
-                                       {
-                                               cvar_set("sv_progs", newprogs);
-                                               wantrestart = true;
-                                       }
-                                       string newcsprogs = sprintf("csprogs-%s.dat", switchversion);
-                                       if (fexists(newcsprogs))
-                                       {
-                                               cvar_set("csqc_progname", newcsprogs);
-                                               wantrestart = true;
-                                       }
-                               }
-                       }
-               }
-               if (wantrestart)
-               {
-                       LOG_INFO("Restart requested");
-                       changelevel(mapname);
-                       // let initialization continue, shutdown depends on it
-               }
-       }
-
-       if(world_already_spawned)
-               error("world already spawned - you may have EXACTLY ONE worldspawn!");
-       world_already_spawned = true;
-
-       delete_fn = remove_safely; // during spawning, watch what you remove!
-
-       cvar_changes_init(); // do this very early now so it REALLY matches the server config
-
-       // needs to be done so early because of the constants they create
-       static_init();
-
-       ServerProgsDB = db_load(strcat("server.db", autocvar_sessionid));
-
-       TemporaryDB = db_create();
-
-       // 0 normal
-       lightstyle(0, "m");
-
-       // 1 FLICKER (first variety)
-       lightstyle(1, "mmnmmommommnonmmonqnmmo");
-
-       // 2 SLOW STRONG PULSE
-       lightstyle(2, "abcdefghijklmnopqrstuvwxyzyxwvutsrqponmlkjihgfedcba");
-
-       // 3 CANDLE (first variety)
-       lightstyle(3, "mmmmmaaaaammmmmaaaaaabcdefgabcdefg");
-
-       // 4 FAST STROBE
-       lightstyle(4, "mamamamamama");
-
-       // 5 GENTLE PULSE 1
-       lightstyle(5,"jklmnopqrstuvwxyzyxwvutsrqponmlkj");
-
-       // 6 FLICKER (second variety)
-       lightstyle(6, "nmonqnmomnmomomno");
-
-       // 7 CANDLE (second variety)
-       lightstyle(7, "mmmaaaabcdefgmmmmaaaammmaamm");
-
-       // 8 CANDLE (third variety)
-       lightstyle(8, "mmmaaammmaaammmabcdefaaaammmmabcdefmmmaaaa");
-
-       // 9 SLOW STROBE (fourth variety)
-       lightstyle(9, "aaaaaaaazzzzzzzz");
-
-       // 10 FLUORESCENT FLICKER
-       lightstyle(10, "mmamammmmammamamaaamammma");
-
-       // 11 SLOW PULSE NOT FADE TO BLACK
-       lightstyle(11, "abcdefghijklmnopqrrqponmlkjihgfedcba");
-
-       // styles 32-62 are assigned by the spawnfunc_light program for switchable lights
-
-       // 63 testing
-       lightstyle(63, "a");
-
-       if(autocvar_g_campaign)
-               CampaignPreInit();
-
-       Map_MarkAsRecent(mapname);
-
-       PlayerStats_GameReport_Init(); // we need this to be initiated before InitGameplayMode
-
-       InitGameplayMode();
-       static_init_late();
-       static_init_precache();
-       readlevelcvars();
-       GrappleHookInit();
-
-       GameRules_limit_fallbacks();
-
-       if(warmup_limit == 0)
-               warmup_limit = (autocvar_timelimit > 0) ? autocvar_timelimit * 60 : autocvar_timelimit;
-
-       player_count = 0;
-       bot_waypoints_for_items = autocvar_g_waypoints_for_items;
-       if(bot_waypoints_for_items == 1)
-               if(this.spawnflags & SPAWNFLAG_NO_WAYPOINTS_FOR_ITEMS)
-                       bot_waypoints_for_items = 0;
-
-       WaypointSprite_Init();
-
-       GameLogInit(); // prepare everything
-       // NOTE for matchid:
-       // changing the logic generating it is okay. But:
-       // it HAS to stay <= 64 chars
-       // character set: ASCII 33-126 without the following characters: : ; ' " \ $
-       if(autocvar_sv_eventlog)
-       {
-               string s = sprintf("%s.%s.%06d", itos(autocvar_sv_eventlog_files_counter), strftime(false, "%s"), floor(random() * 1000000));
-               matchid = strzone(s);
-
-               GameLogEcho(strcat(":gamestart:", GetGametype(), "_", GetMapname(), ":", s));
-               s = ":gameinfo:mutators:LIST";
-
-               MUTATOR_CALLHOOK(BuildMutatorsString, s);
-               s = M_ARGV(0, string);
-
-               // initialiation stuff, not good in the mutator system
-               if(!autocvar_g_use_ammunition)
-                       s = strcat(s, ":no_use_ammunition");
-
-               // initialiation stuff, not good in the mutator system
-               if(autocvar_g_pickup_items == 0)
-                       s = strcat(s, ":no_pickup_items");
-               if(autocvar_g_pickup_items > 0)
-                       s = strcat(s, ":pickup_items");
-
-               // initialiation stuff, not good in the mutator system
-               if(autocvar_g_weaponarena != "0")
-                       s = strcat(s, ":", autocvar_g_weaponarena, " arena");
-
-               // TODO to mutator system
-               if(autocvar_g_norecoil)
-                       s = strcat(s, ":norecoil");
-
-               // TODO to mutator system
-               if(autocvar_g_powerups == 0)
-                       s = strcat(s, ":no_powerups");
-               if(autocvar_g_powerups > 0)
-                       s = strcat(s, ":powerups");
-
-               GameLogEcho(s);
-               GameLogEcho(":gameinfo:end");
-       }
-       else
-               matchid = strzone(ftos(random()));
-
-       cvar_set("nextmap", "");
-
-       SetDefaultAlpha();
-
-       if(autocvar_g_campaign)
-               CampaignPostInit();
-
-       Ban_LoadBans();
-
-       MapInfo_Enumerate();
-       MapInfo_FilterGametype(MapInfo_CurrentGametype(), MapInfo_CurrentFeatures(), MapInfo_RequiredFlags(), MapInfo_ForbiddenFlags(), 1);
-
-       if(fexists(strcat("scripts/", mapname, ".arena")))
-               cvar_settemp("sv_q3acompat_machineshotgunswap", "1");
-
-       if(fexists(strcat("scripts/", mapname, ".defi")))
-               cvar_settemp("sv_q3defragcompat", "1");
-
-       if(whichpack(strcat("maps/", mapname, ".cfg")) != "")
-       {
-               int fd = fopen(strcat("maps/", mapname, ".cfg"), FILE_READ);
-               if(fd != -1)
-               {
-                       string s;
-                       while((s = fgets(fd)))
-                       {
-                               int l = tokenize_console(s);
-                               if(l < 2)
-                                       continue;
-                               if(argv(0) == "cd")
-                               {
-                                       string trackname = argv(2);
-                                       LOG_INFO("Found ^1UNSUPPORTED^7 cd loop command in .cfg file; put this line in mapinfo instead:");
-                                       LOG_INFO("  cdtrack ", trackname);
-                                       if (cvar_value_issafe(trackname))
-                                       {
-                                               string newstuff = strcat(clientstuff, "cd loop \"", trackname, "\"\n");
-                                               strcpy(clientstuff, newstuff);
-                                       }
-                               }
-                               else if(argv(0) == "fog")
-                               {
-                                       LOG_INFO("Found ^1UNSUPPORTED^7 fog command in .cfg file; put this line in worldspawn in the .map/.bsp/.ent file instead:");
-                                       LOG_INFO("  \"fog\" \"", s, "\"");
-                               }
-                               else if(argv(0) == "set")
-                               {
-                                       LOG_INFO("Found ^1UNSUPPORTED^7 set command in .cfg file; put this line in mapinfo instead:");
-                                       LOG_INFO("  clientsettemp_for_type all ", argv(1), " ", argv(2));
-                               }
-                               else if(argv(0) != "//")
-                               {
-                                       LOG_INFO("Found ^1UNSUPPORTED^7 set command in .cfg file; put this line in mapinfo instead:");
-                                       LOG_INFO("  clientsettemp_for_type all ", argv(0), " ", argv(1));
-                               }
-                       }
-                       fclose(fd);
-               }
-       }
-
-       WeaponStats_Init();
-
-       Nagger_Init();
-
-       // set up information replies for clients and server to use
-       maplist_reply = strzone(getmaplist());
-       lsmaps_reply = strzone(getlsmaps());
-       monsterlist_reply = strzone(getmonsterlist());
-       for(int i = 0; i < 10; ++i)
-       {
-               string s = getrecords(i);
-               if (s)
-                       records_reply[i] = strzone(s);
-       }
-       ladder_reply = strzone(getladder());
-       rankings_reply = strzone(getrankings());
-
-       // begin other init
-       ClientInit_Spawn();
-       RandomSeed_Spawn();
-       PingPLReport_Spawn();
-
-       CheatInit();
-
-       if (!wantrestart) localcmd("\n_sv_hook_gamestart ", GetGametype(), "\n");
-
-       // fill sv_curl_serverpackages from .serverpackage files
-       if (autocvar_sv_curl_serverpackages_auto)
-       {
-               string s = "csprogs-" WATERMARK ".txt";
-               // remove automatically managed files from the list to prevent duplicates
-               for (int i = 0, n = tokenize_console(cvar_string("sv_curl_serverpackages")); i < n; ++i)
-               {
-                       string pkg = argv(i);
-                       if (startsWith(pkg, "csprogs-")) continue;
-                       if (endsWith(pkg, "-serverpackage.txt")) continue;
-                       if (endsWith(pkg, ".serverpackage")) continue;  // OLD legacy
-                       s = cons(s, pkg);
-               }
-               // add automatically managed files to the list
-               #define X(match) MACRO_BEGIN \
-                       int fd = search_begin(match, true, false); \
-                       if (fd >= 0) \
-                       { \
-                               for (int i = 0, j = search_getsize(fd); i < j; ++i) \
-                               { \
-                                       s = cons(s, search_getfilename(fd, i)); \
-                               } \
-                               search_end(fd); \
-                       } \
-               MACRO_END
-               X("*-serverpackage.txt");
-               X("*.serverpackage");
-               #undef X
-               cvar_set("sv_curl_serverpackages", s);
-       }
-
-       // MOD AUTHORS: change this, and possibly remove a few of the blocks below to ignore certain changes
-       modname = "Xonotic";
-       // physics/balance/config changes that count as mod
-       if(cvar_string("g_mod_physics") != cvar_defstring("g_mod_physics"))
-               modname = cvar_string("g_mod_physics");
-       if(cvar_string("g_mod_balance") != cvar_defstring("g_mod_balance") && cvar_string("g_mod_balance") != "Testing")
-               modname = cvar_string("g_mod_balance");
-       if(cvar_string("g_mod_config") != cvar_defstring("g_mod_config"))
-               modname = cvar_string("g_mod_config");
-       // extra mutators that deserve to count as mod
-       MUTATOR_CALLHOOK(SetModname, modname);
-       modname = M_ARGV(0, string);
-
-       // save it for later
-       modname = strzone(modname);
-
-       WinningConditionHelper(this); // set worldstatus
-
-       world_initialized = 1;
-       __spawnfunc_spawn_all();
-}
-
-spawnfunc(light)
-{
-       //makestatic (this); // Who the f___ did that?
-       delete(this);
-}
-
-string GetGametype()
-{
-       return MapInfo_Type_ToString(MapInfo_LoadedGametype);
-}
-
-string GetMapname()
-{
-       return mapname;
-}
-
-float Map_Count, Map_Current;
-string Map_Current_Name;
-
-// NOTE: this now expects the map list to be already tokenized and the count in Map_Count
-int GetMaplistPosition()
-{
-       string map = GetMapname();
-       int idx = autocvar_g_maplist_index;
-
-       if(idx >= 0)
-       {
-               if(idx < Map_Count)
-               {
-                       if(map == argv(idx))
-                       {
-                               return idx;
-                       }
-               }
-       }
-
-       for(int pos = 0; pos < Map_Count; ++pos)
-       {
-               if(map == argv(pos))
-                       return pos;
-       }
-
-       // resume normal maplist rotation if current map is not in g_maplist
-       return idx;
-}
-
-bool MapHasRightSize(string map)
-{
-       int minplayers = max(0, floor(autocvar_minplayers));
-       if (teamplay)
-               minplayers = max(0, floor(autocvar_minplayers_per_team) * AvailableTeams());
-       if (autocvar_g_maplist_check_waypoints
-               && (currentbots || autocvar_bot_number || player_count < minplayers))
-       {
-               string checkwp_msg = strcat("checkwp ", map);
-               if(!fexists(strcat("maps/", map, ".waypoints")))
-               {
-                       LOG_TRACE(checkwp_msg, ": no waypoints");
-                       return false;
-               }
-               LOG_TRACE(checkwp_msg, ": has waypoints");
-       }
-
-       if(autocvar_g_maplist_ignore_sizes)
-               return true;
-
-       // open map size restriction file
-       string opensize_msg = strcat("opensize ", map);
-       float fh = fopen(strcat("maps/", map, ".sizes"), FILE_READ);
-       int player_limit = ((autocvar_g_maplist_sizes_count_maxplayers) ? GetPlayerLimit() : 0);
-       int pcount = ((player_limit > 0) ? min(player_count, player_limit) : player_count); // bind it to the player limit so that forced spectators don't influence the limits
-       if(!autocvar_g_maplist_sizes_count_bots)
-               pcount -= currentbots;
-       if(fh >= 0)
-       {
-               opensize_msg = strcat(opensize_msg, ": ok, ");
-               int mapmin = stoi(fgets(fh));
-               int mapmax = stoi(fgets(fh));
-               fclose(fh);
-               if(pcount < mapmin)
-               {
-                       LOG_TRACE(opensize_msg, "not enough");
-                       return false;
-               }
-               if(mapmax && pcount > mapmax)
-               {
-                       LOG_TRACE(opensize_msg, "too many");
-                       return false;
-               }
-               LOG_TRACE(opensize_msg, "right size");
-               return true;
-       }
-       LOG_TRACE(opensize_msg, ": not found");
-       return true;
-}
-
-string Map_Filename(float position)
-{
-       return strcat("maps/", argv(position), ".bsp");
-}
-
-void Map_MarkAsRecent(string m)
-{
-       cvar_set("g_maplist_mostrecent", strwords(cons(m, autocvar_g_maplist_mostrecent), max(0, autocvar_g_maplist_mostrecent_count)));
-}
-
-float Map_IsRecent(string m)
-{
-       return strhasword(autocvar_g_maplist_mostrecent, m);
-}
-
-float Map_Check(float position, float pass)
-{
-       string filename;
-       string map_next;
-       map_next = argv(position);
-       if(pass <= 1)
-       {
-               if(Map_IsRecent(map_next))
-                       return 0;
-       }
-       filename = Map_Filename(position);
-       if(MapInfo_CheckMap(map_next))
-       {
-               if(pass == 2)
-                       return 1;
-               if(MapHasRightSize(map_next))
-                       return 1;
-               return 0;
-       }
-       else
-               LOG_DEBUG( "Couldn't select '", filename, "'..." );
-
-       return 0;
-}
-
-void Map_Goto_SetStr(string nextmapname)
-{
-       if(getmapname_stored != "")
-               strunzone(getmapname_stored);
-       if(nextmapname == "")
-               getmapname_stored = "";
-       else
-               getmapname_stored = strzone(nextmapname);
-}
-
-void Map_Goto_SetFloat(float position)
-{
-       cvar_set("g_maplist_index", ftos(position));
-       Map_Goto_SetStr(argv(position));
-}
-
-void Map_Goto(float reinit)
-{
-       MapInfo_LoadMap(getmapname_stored, reinit);
-}
-
-// return codes of map selectors:
-//   -1 = temporary failure (that is, try some method that is guaranteed to succeed)
-//   -2 = permanent failure
-float MaplistMethod_Iterate() // usual method
-{
-       float pass, i;
-
-       LOG_TRACE("Trying MaplistMethod_Iterate");
-
-       for(pass = 1; pass <= 2; ++pass)
-       {
-               for(i = 1; i < Map_Count; ++i)
-               {
-                       float mapindex;
-                       mapindex = (i + Map_Current) % Map_Count;
-                       if(Map_Check(mapindex, pass))
-                               return mapindex;
-               }
-       }
-       return -1;
-}
-
-float MaplistMethod_Repeat() // fallback method
-{
-       LOG_TRACE("Trying MaplistMethod_Repeat");
-
-       if(Map_Check(Map_Current, 2))
-               return Map_Current;
-       return -2;
-}
-
-float MaplistMethod_Random() // random map selection
-{
-       float i, imax;
-
-       LOG_TRACE("Trying MaplistMethod_Random");
-
-       imax = 42;
-
-       for(i = 0; i <= imax; ++i)
-       {
-               float mapindex;
-               mapindex = (Map_Current + floor(random() * (Map_Count - 1) + 1)) % Map_Count; // any OTHER map
-               if(Map_Check(mapindex, 1))
-                       return mapindex;
-       }
-       return -1;
-}
-
-float MaplistMethod_Shuffle(float exponent) // more clever shuffling
-// the exponent sets a bias on the map selection:
-// the higher the exponent, the less likely "shortly repeated" same maps are
-{
-       float i, j, imax, insertpos;
-
-       LOG_TRACE("Trying MaplistMethod_Shuffle");
-
-       imax = 42;
-
-       for(i = 0; i <= imax; ++i)
-       {
-               string newlist;
-
-               // now reinsert this at another position
-               insertpos = (random() ** (1 / exponent));       // ]0, 1]
-               insertpos = insertpos * (Map_Count - 1);       // ]0, Map_Count - 1]
-               insertpos = ceil(insertpos) + 1;               // {2, 3, 4, ..., Map_Count}
-               LOG_TRACE("SHUFFLE: insert pos = ", ftos(insertpos));
-
-               // insert the current map there
-               newlist = "";
-               for(j = 1; j < insertpos; ++j)                 // i == 1: no loop, will be inserted as first; however, i == 1 has been excluded above
-                       newlist = strcat(newlist, " ", argv(j));
-               newlist = strcat(newlist, " ", argv(0));       // now insert the just selected map
-               for(j = insertpos; j < Map_Count; ++j)         // i == Map_Count: no loop, has just been inserted as last
-                       newlist = strcat(newlist, " ", argv(j));
-               newlist = substring(newlist, 1, strlen(newlist) - 1);
-               cvar_set("g_maplist", newlist);
-               Map_Count = tokenizebyseparator(autocvar_g_maplist, " ");
-
-               // NOTE: the selected map has just been inserted at (insertpos-1)th position
-               Map_Current = insertpos - 1; // this is not really valid, but this way the fallback has a chance of working
-               if(Map_Check(Map_Current, 1))
-                       return Map_Current;
-       }
-       return -1;
-}
-
-void Maplist_Init()
-{
-       float i = Map_Count = 0;
-       if(autocvar_g_maplist != "")
-       {
-               Map_Count = tokenizebyseparator(autocvar_g_maplist, " ");
-               for (i = 0; i < Map_Count; ++i)
-               {
-                       if (Map_Check(i, 2))
-                               break;
-               }
-       }
-
-       if (i == Map_Count)
-       {
-               bprint( "Maplist contains no usable maps!  Resetting it to default map list.\n" );
-               cvar_set("g_maplist", MapInfo_ListAllAllowedMaps(MapInfo_RequiredFlags(), MapInfo_ForbiddenFlags() | MAPINFO_FLAG_NOAUTOMAPLIST));
-               if(autocvar_g_maplist_shuffle)
-                       ShuffleMaplist();
-               if(!server_is_dedicated)
-                       localcmd("\nmenu_cmd sync\n");
-               Map_Count = tokenizebyseparator(autocvar_g_maplist, " ");
-       }
-       if(Map_Count == 0)
-               error("empty maplist, cannot select a new map");
-       Map_Current = bound(0, GetMaplistPosition(), Map_Count - 1);
-
-       strcpy(Map_Current_Name, argv(Map_Current)); // will be automatically freed on exit thanks to DP
-       // this may or may not be correct, but who cares, in the worst case a map
-       // isn't chosen in the first pass that should have been
-}
-
-string GetNextMap()
-{
-       Maplist_Init();
-       float nextMap = -1;
-
-       if(nextMap == -1)
-               if(autocvar_g_maplist_shuffle > 0)
-                       nextMap = MaplistMethod_Shuffle(autocvar_g_maplist_shuffle + 1);
-
-       if(nextMap == -1)
-               if(autocvar_g_maplist_selectrandom)
-                       nextMap = MaplistMethod_Random();
-
-       if(nextMap == -1)
-               nextMap = MaplistMethod_Iterate();
-
-       if(nextMap == -1)
-               nextMap = MaplistMethod_Repeat();
-
-       if(nextMap >= 0)
-       {
-               Map_Goto_SetFloat(nextMap);
-               return getmapname_stored;
-       }
-
-       return "";
-}
-
-float DoNextMapOverride(float reinit)
-{
-       if(autocvar_g_campaign)
-       {
-               CampaignPostIntermission();
-               alreadychangedlevel = true;
-               return true;
-       }
-       if(autocvar_quit_when_empty)
-       {
-               if(player_count <= currentbots)
-               {
-                       localcmd("quit\n");
-                       alreadychangedlevel = true;
-                       return true;
-               }
-       }
-       if(autocvar_quit_and_redirect != "")
-       {
-               redirection_target = strzone(autocvar_quit_and_redirect);
-               alreadychangedlevel = true;
-               return true;
-       }
-       if (!reinit && autocvar_samelevel) // if samelevel is set, stay on same level
-       {
-               localcmd("restart\n");
-               alreadychangedlevel = true;
-               return true;
-       }
-       if(autocvar_nextmap != "")
-       {
-               string m;
-               m = GameTypeVote_MapInfo_FixName(autocvar_nextmap);
-               cvar_set("nextmap",m);
-
-               if(!m || gametypevote)
-                       return false;
-               if(autocvar_sv_vote_gametype)
-               {
-                       Map_Goto_SetStr(m);
-                       return false;
-               }
-
-               if(MapInfo_CheckMap(m))
-               {
-                       Map_Goto_SetStr(m);
-                       Map_Goto(reinit);
-                       alreadychangedlevel = true;
-                       return true;
-               }
-       }
-       if(!reinit && autocvar_lastlevel)
-       {
-               cvar_settemp_restore();
-               localcmd("set lastlevel 0\ntogglemenu 1\n");
-               alreadychangedlevel = true;
-               return true;
-       }
-       return false;
-}
-
-void GotoNextMap(float reinit)
-{
-       //string nextmap;
-       //float n, nummaps;
-       //string s;
-       if (alreadychangedlevel)
-               return;
-       alreadychangedlevel = true;
-
-       string nextMap = GetNextMap();
-       if(nextMap == "")
-               error("Everything is broken - cannot find a next map. Please report this to the developers.");
-       Map_Goto(reinit);
-}
-
-
-/*
-============
-IntermissionThink
-
-When the player presses attack or jump, change to the next level
-============
-*/
-.float autoscreenshot;
-void IntermissionThink(entity this)
-{
-       FixIntermissionClient(this);
-
-       float server_screenshot = (autocvar_sv_autoscreenshot && CS(this).cvar_cl_autoscreenshot);
-       float client_screenshot = (CS(this).cvar_cl_autoscreenshot == 2);
-
-       if( (server_screenshot || client_screenshot)
-               && ((this.autoscreenshot > 0) && (time > this.autoscreenshot)) )
-       {
-               this.autoscreenshot = -1;
-               if(IS_REAL_CLIENT(this)) { stuffcmd(this, sprintf("\nscreenshot screenshots/autoscreenshot/%s-%s.jpg; echo \"^5A screenshot has been taken at request of the server.\"\n", GetMapname(), strftime(false, "%s"))); }
-               return;
-       }
-
-       if (time < intermission_exittime)
-               return;
-
-       if(!mapvote_initialized)
-               if (time < intermission_exittime + 10 && !(PHYS_INPUT_BUTTON_ATCK(this) || PHYS_INPUT_BUTTON_JUMP(this) || PHYS_INPUT_BUTTON_ATCK2(this) || PHYS_INPUT_BUTTON_HOOK(this) || PHYS_INPUT_BUTTON_USE(this)))
-                       return;
-
-       MapVote_Start();
-}
-
-/*
-===============================================================================
-
-RULES
-
-===============================================================================
-*/
-
-void DumpStats(float final)
-{
-       float file;
-       string s;
-       float to_console;
-       float to_eventlog;
-       float to_file;
-       float i;
-
-       to_console = autocvar_sv_logscores_console;
-       to_eventlog = autocvar_sv_eventlog;
-       to_file = autocvar_sv_logscores_file;
-
-       if(!final)
-       {
-               to_console = true; // always print printstats replies
-               to_eventlog = false; // but never print them to the event log
-       }
-
-       if(to_eventlog)
-               if(autocvar_sv_eventlog_console)
-                       to_console = false; // otherwise we get the output twice
-
-       if(final)
-               s = ":scores:";
-       else
-               s = ":status:";
-       s = strcat(s, GetGametype(), "_", GetMapname(), ":", ftos(rint(time)));
-
-       if(to_console)
-               LOG_INFO(s);
-       if(to_eventlog)
-               GameLogEcho(s);
-
-       file = -1;
-       if(to_file)
-       {
-               file = fopen(autocvar_sv_logscores_filename, FILE_APPEND);
-               if(file == -1)
-                       to_file = false;
-               else
-                       fputs(file, strcat(s, "\n"));
-       }
-
-       s = strcat(":labels:player:", GetPlayerScoreString(NULL, 0));
-       if(to_console)
-               LOG_INFO(s);
-       if(to_eventlog)
-               GameLogEcho(s);
-       if(to_file)
-               fputs(file, strcat(s, "\n"));
-
-       FOREACH_CLIENT(IS_REAL_CLIENT(it) || (IS_BOT_CLIENT(it) && autocvar_sv_logscores_bots), {
-               s = strcat(":player:see-labels:", GetPlayerScoreString(it, 0), ":");
-               s = strcat(s, ftos(rint(time - CS(it).jointime)), ":");
-               if(IS_PLAYER(it) || MUTATOR_CALLHOOK(GetPlayerStatus, it))
-                       s = strcat(s, ftos(it.team), ":");
-               else
-                       s = strcat(s, "spectator:");
-
-               if(to_console)
-                       LOG_INFO(s, playername(it, false));
-               if(to_eventlog)
-                       GameLogEcho(strcat(s, ftos(it.playerid), ":", playername(it, false)));
-               if(to_file)
-                       fputs(file, strcat(s, playername(it, false), "\n"));
-       });
-
-       if(teamplay)
-       {
-               s = strcat(":labels:teamscores:", GetTeamScoreString(0, 0));
-               if(to_console)
-                       LOG_INFO(s);
-               if(to_eventlog)
-                       GameLogEcho(s);
-               if(to_file)
-                       fputs(file, strcat(s, "\n"));
-
-               for(i = 1; i < 16; ++i)
-               {
-                       s = strcat(":teamscores:see-labels:", GetTeamScoreString(i, 0));
-                       s = strcat(s, ":", ftos(i));
-                       if(to_console)
-                               LOG_INFO(s);
-                       if(to_eventlog)
-                               GameLogEcho(s);
-                       if(to_file)
-                               fputs(file, strcat(s, "\n"));
-               }
-       }
-
-       if(to_console)
-               LOG_INFO(":end");
-       if(to_eventlog)
-               GameLogEcho(":end");
-       if(to_file)
-       {
-               fputs(file, ":end\n");
-               fclose(file);
-       }
-}
-
-void FixIntermissionClient(entity e)
-{
-       if(!e.autoscreenshot) // initial call
-       {
-               e.autoscreenshot = time + 0.8;  // used for autoscreenshot
-               SetResourceExplicit(e, RES_HEALTH, -2342);
-               // first intermission phase; voting phase has positive health (used to decide whether to send SVC_FINALE or not)
-               for (int slot = 0; slot < MAX_WEAPONSLOTS; ++slot)
-               {
-                   .entity weaponentity = weaponentities[slot];
-                       if(e.(weaponentity))
-                       {
-                               e.(weaponentity).effects = EF_NODRAW;
-                               if (e.(weaponentity).weaponchild)
-                                       e.(weaponentity).weaponchild.effects = EF_NODRAW;
-                       }
-               }
-               if(IS_REAL_CLIENT(e))
-               {
-                       stuffcmd(e, "\nscr_printspeed 1000000\n");
-                       RandomSelection_Init();
-                       FOREACH_WORD(autocvar_sv_intermission_cdtrack, true, {
-                               RandomSelection_AddString(it, 1, 1);
-                       });
-                       if (RandomSelection_chosen_string != "")
-                       {
-                               stuffcmd(e, sprintf("\ncd loop %s\n", RandomSelection_chosen_string));
-                       }
-                       msg_entity = e;
-                       WriteByte(MSG_ONE, SVC_INTERMISSION);
-               }
-       }
-}
-
-/*
-go to the next level for deathmatch
-only called if a time or frag limit has expired
-*/
-void NextLevel()
-{
-       game_stopped = true;
-       intermission_running = 1; // game over
-
-       // enforce a wait time before allowing changelevel
-       if(player_count > 0)
-               intermission_exittime = time + autocvar_sv_mapchange_delay;
-       else
-               intermission_exittime = -1;
-
-       /*
-       WriteByte (MSG_ALL, SVC_CDTRACK);
-       WriteByte (MSG_ALL, 3);
-       WriteByte (MSG_ALL, 3);
-       // done in FixIntermission
-       */
-
-       //pos = FindIntermission ();
-
-       VoteReset();
-
-       DumpStats(true);
-
-       // send statistics
-       PlayerStats_GameReport(true);
-       WeaponStats_Shutdown();
-
-       Kill_Notification(NOTIF_ALL, NULL, MSG_CENTER, CPID_Null); // kill all centerprints now
-
-       if(autocvar_sv_eventlog)
-               GameLogEcho(":gameover");
-
-       GameLogClose();
-
-       FOREACH_CLIENT(IS_PLAYER(it), {
-               FixIntermissionClient(it);
-               if(it.winning)
-                       bprint(playername(it, false), " ^7wins.\n");
-       });
-
-       target_music_kill();
-
-       if(autocvar_g_campaign)
-               CampaignPreIntermission();
-
-       MUTATOR_CALLHOOK(MatchEnd);
-
-       localcmd("\nsv_hook_gameend\n");
-}
-
-
-float InitiateSuddenDeath()
-{
-       // Check first whether normal overtimes could be added before initiating suddendeath mode
-       // - for this timelimit_overtime needs to be >0 of course
-       // - also check the winning condition calculated in the previous frame and only add normal overtime
-       //   again, if at the point at which timelimit would be extended again, still no winner was found
-       if (!autocvar_g_campaign && checkrules_overtimesadded >= 0
-               && (checkrules_overtimesadded < autocvar_timelimit_overtimes || autocvar_timelimit_overtimes < 0)
-               && autocvar_timelimit_overtime && !(g_race && !g_race_qualifying))
-       {
-               return 1; // need to call InitiateOvertime later
-       }
-       else
-       {
-               if(!checkrules_suddendeathend)
-               {
-                       if(autocvar_g_campaign)
-                               checkrules_suddendeathend = time; // no suddendeath in campaign
-                       else
-                               checkrules_suddendeathend = time + 60 * autocvar_timelimit_suddendeath;
-                       if(g_race && !g_race_qualifying)
-                               race_StartCompleting();
-               }
-               return 0;
-       }
-}
-
-void InitiateOvertime() // ONLY call this if InitiateSuddenDeath returned true
-{
-       ++checkrules_overtimesadded;
-       //add one more overtime by simply extending the timelimit
-       cvar_set("timelimit", ftos(autocvar_timelimit + autocvar_timelimit_overtime));
-       Send_Notification(NOTIF_ALL, NULL, MSG_CENTER, CENTER_OVERTIME_TIME, autocvar_timelimit_overtime * 60);
-}
-
-float GetWinningCode(float fraglimitreached, float equality)
-{
-       if(autocvar_g_campaign == 1)
-       {
-               if(fraglimitreached)
-                       return WINNING_YES;
-               else
-                       return WINNING_NO;
-       }
-       else
-       {
-               if(equality)
-               {
-                       if(fraglimitreached)
-                               return WINNING_STARTSUDDENDEATHOVERTIME;
-                       else
-                               return WINNING_NEVER;
-               }
-               else
-               {
-                       if(fraglimitreached)
-                               return WINNING_YES;
-                       else
-                               return WINNING_NO;
-               }
-       }
-}
-
-// set the .winning flag for exactly those players with a given field value
-void SetWinners(.float field, float value)
-{
-       FOREACH_CLIENT(IS_PLAYER(it), { it.winning = (it.(field) == value); });
-}
-
-// set the .winning flag for those players with a given field value
-void AddWinners(.float field, float value)
-{
-       FOREACH_CLIENT(IS_PLAYER(it), {
-               if(it.(field) == value)
-                       it.winning = 1;
-       });
-}
-
-// clear the .winning flags
-void ClearWinners()
-{
-       FOREACH_CLIENT(IS_PLAYER(it), { it.winning = 0; });
-}
-
-void ShuffleMaplist()
-{
-       cvar_set("g_maplist", shufflewords(autocvar_g_maplist));
-}
-
-int fragsleft_last;
-float WinningCondition_Scores(float limit, float leadlimit)
-{
-       // TODO make everything use THIS winning condition (except LMS)
-       WinningConditionHelper(NULL);
-
-       if(teamplay)
-       {
-               for (int i = 1; i < 5; ++i)
-               {
-                       Team_SetTeamScore(Team_GetTeamFromIndex(i),
-                               TeamScore_GetCompareValue(Team_IndexToTeam(i)));
-               }
-       }
-
-       ClearWinners();
-       if(WinningConditionHelper_winner)
-               WinningConditionHelper_winner.winning = 1;
-       if(WinningConditionHelper_winnerteam >= 0)
-               SetWinners(team, WinningConditionHelper_winnerteam);
-
-       if(WinningConditionHelper_lowerisbetter)
-       {
-               WinningConditionHelper_topscore = -WinningConditionHelper_topscore;
-               WinningConditionHelper_secondscore = -WinningConditionHelper_secondscore;
-               limit = -limit;
-       }
-
-       if(WinningConditionHelper_zeroisworst)
-               leadlimit = 0; // not supported in this mode
-
-       if(MUTATOR_CALLHOOK(Scores_CountFragsRemaining))
-       {
-               float fragsleft;
-               if (checkrules_suddendeathend && time >= checkrules_suddendeathend)
-               {
-                       fragsleft = 1;
-               }
-               else
-               {
-                       fragsleft = FLOAT_MAX;
-                       float leadingfragsleft = FLOAT_MAX;
-                       if (limit)
-                               fragsleft = limit - WinningConditionHelper_topscore;
-                       if (leadlimit)
-                               leadingfragsleft = WinningConditionHelper_secondscore + leadlimit - WinningConditionHelper_topscore;
-
-                       if (limit && leadlimit && autocvar_leadlimit_and_fraglimit)
-                               fragsleft = max(fragsleft, leadingfragsleft);
-                       else
-                               fragsleft = min(fragsleft, leadingfragsleft);
-               }
-
-               if (fragsleft_last != fragsleft) // do not announce same remaining frags multiple times
-               {
-                       if (fragsleft == 1)
-                               Send_Notification(NOTIF_ALL, NULL, MSG_ANNCE, ANNCE_REMAINING_FRAG_1);
-                       else if (fragsleft == 2)
-                               Send_Notification(NOTIF_ALL, NULL, MSG_ANNCE, ANNCE_REMAINING_FRAG_2);
-                       else if (fragsleft == 3)
-                               Send_Notification(NOTIF_ALL, NULL, MSG_ANNCE, ANNCE_REMAINING_FRAG_3);
-
-                       fragsleft_last = fragsleft;
-               }
-       }
-
-       bool fraglimit_reached = (limit && WinningConditionHelper_topscore >= limit);
-       bool leadlimit_reached = (leadlimit && WinningConditionHelper_topscore - WinningConditionHelper_secondscore >= leadlimit);
-
-       bool limit_reached;
-       // only respect leadlimit_and_fraglimit when both limits are set or the game will never end
-       if (limit && leadlimit && autocvar_leadlimit_and_fraglimit)
-               limit_reached = (fraglimit_reached && leadlimit_reached);
-       else
-               limit_reached = (fraglimit_reached || leadlimit_reached);
-
-       return GetWinningCode(
-               WinningConditionHelper_topscore && limit_reached,
-               WinningConditionHelper_equality
-       );
-}
-
-float WinningCondition_RanOutOfSpawns()
-{
-       if(have_team_spawns <= 0)
-               return WINNING_NO;
-
-       if(!autocvar_g_spawn_useallspawns)
-               return WINNING_NO;
-
-       if(!some_spawn_has_been_used)
-               return WINNING_NO;
-
-       for (int i = 1; i < 5; ++i)
-       {
-               Team_SetTeamScore(Team_GetTeamFromIndex(i), 0);
-       }
-
-       FOREACH_CLIENT(IS_PLAYER(it) && !IS_DEAD(it),
-       {
-               if (Team_IsValidTeam(it.team))
-               {
-                       Team_SetTeamScore(Team_GetTeam(it.team), 1);
-               }
-       });
-
-       IL_EACH(g_spawnpoints, true,
-       {
-               if (Team_IsValidTeam(it.team))
-               {
-                       Team_SetTeamScore(Team_GetTeam(it.team), 1);
-               }
-       });
-
-       ClearWinners();
-       float team1_score = Team_GetTeamScore(Team_GetTeamFromIndex(1));
-       float team2_score = Team_GetTeamScore(Team_GetTeamFromIndex(2));
-       float team3_score = Team_GetTeamScore(Team_GetTeamFromIndex(3));
-       float team4_score = Team_GetTeamScore(Team_GetTeamFromIndex(4));
-       if(team1_score + team2_score + team3_score + team4_score == 0)
-       {
-               checkrules_equality = true;
-               return WINNING_YES;
-       }
-       else if(team1_score + team2_score + team3_score + team4_score == 1)
-       {
-               float t, i;
-               if(team1_score)
-                       t = 1;
-               else if(team2_score)
-                       t = 2;
-               else if(team3_score)
-                       t = 3;
-               else // if(team4_score)
-                       t = 4;
-               entity balance = TeamBalance_CheckAllowedTeams(NULL);
-               for(i = 0; i < MAX_TEAMSCORE; ++i)
-               {
-                       for (int j = 1; j <= NUM_TEAMS; ++j)
-                       {
-                               if (t == j)
-                               {
-                                       continue;
-                               }
-                               if (!TeamBalance_IsTeamAllowed(balance, j))
-                               {
-                                       continue;
-                               }
-                               TeamScore_AddToTeam(Team_IndexToTeam(j), i, -1000);
-                       }
-               }
-
-               AddWinners(team, t);
-               return WINNING_YES;
-       }
-       else
-               return WINNING_NO;
-}
-
-/*
-============
-CheckRules_World
-
-Exit deathmatch games upon conditions
-============
-*/
-void CheckRules_World()
-{
-       VoteThink();
-       MapVote_Think();
-
-       SetDefaultAlpha();
-
-       if (intermission_running) // someone else quit the game already
-       {
-               if(player_count == 0) // Nobody there? Then let's go to the next map
-                       MapVote_Start();
-                       // this will actually check the player count in the next frame
-                       // again, but this shouldn't hurt
-               return;
-       }
-
-       float timelimit = autocvar_timelimit * 60;
-       float fraglimit = autocvar_fraglimit;
-       float leadlimit = autocvar_leadlimit;
-       if (leadlimit < 0) leadlimit = 0;
-
-       if(warmup_stage || time <= game_starttime) // NOTE: this is <= to prevent problems in the very tic where the game starts
-       {
-               if(timelimit > 0)
-                       timelimit = 0; // timelimit is not made for warmup
-               if(fraglimit > 0)
-                       fraglimit = 0; // no fraglimit for now
-               leadlimit = 0; // no leadlimit for now
-       }
-
-       if(timelimit > 0)
-       {
-               timelimit += game_starttime;
-       }
-       else if (timelimit < 0)
-       {
-               // endmatch
-               NextLevel();
-               return;
-       }
-
-       float wantovertime;
-       wantovertime = 0;
-
-       if(checkrules_suddendeathend)
-       {
-               if(!checkrules_suddendeathwarning)
-               {
-                       checkrules_suddendeathwarning = true;
-                       if(g_race && !g_race_qualifying)
-                               Send_Notification(NOTIF_ALL, NULL, MSG_CENTER, CENTER_RACE_FINISHLAP);
-                       else
-                               Send_Notification(NOTIF_ALL, NULL, MSG_CENTER, CENTER_OVERTIME_FRAG);
-               }
-       }
-       else
-       {
-               if (timelimit && time >= timelimit)
-               {
-                       if(g_race && (g_race_qualifying == 2) && timelimit > 0)
-                       {
-                               float totalplayers;
-                               float playerswithlaps;
-                               float readyplayers;
-                               totalplayers = playerswithlaps = readyplayers = 0;
-                               FOREACH_CLIENT(IS_PLAYER(it), {
-                                       ++totalplayers;
-                                       if(GameRules_scoring_add(it, RACE_FASTEST, 0))
-                                               ++playerswithlaps;
-                                       if(it.ready)
-                                               ++readyplayers;
-                               });
-
-                               // at least 2 of the players have completed a lap: start the RACE
-                               // otherwise, the players should end the qualifying on their own
-                               if(readyplayers || playerswithlaps >= 2)
-                               {
-                                       checkrules_suddendeathend = 0;
-                                       ReadyRestart(); // go to race
-                                       return;
-                               }
-                               else
-                                       wantovertime |= InitiateSuddenDeath();
-                       }
-                       else
-                               wantovertime |= InitiateSuddenDeath();
-               }
-       }
-
-       if (checkrules_suddendeathend && time >= checkrules_suddendeathend)
-       {
-               NextLevel();
-               return;
-       }
-
-       int checkrules_status = WinningCondition_RanOutOfSpawns();
-       if(checkrules_status == WINNING_YES)
-               bprint("Hey! Someone ran out of spawns!\n");
-       else if(MUTATOR_CALLHOOK(CheckRules_World, checkrules_status, timelimit, fraglimit))
-               checkrules_status = M_ARGV(0, float);
-       else
-               checkrules_status = WinningCondition_Scores(fraglimit, leadlimit);
-
-       if(checkrules_status == WINNING_STARTSUDDENDEATHOVERTIME)
-       {
-               checkrules_status = WINNING_NEVER;
-               checkrules_overtimesadded = -1;
-               wantovertime |= InitiateSuddenDeath();
-       }
-
-       if(checkrules_status == WINNING_NEVER)
-               // equality cases! Nobody wins if the overtime ends in a draw.
-               ClearWinners();
-
-       if(wantovertime)
-       {
-               if(checkrules_status == WINNING_NEVER)
-                       InitiateOvertime();
-               else
-                       checkrules_status = WINNING_YES;
-       }
-
-       if(checkrules_suddendeathend)
-               if(checkrules_status != WINNING_NEVER || time >= checkrules_suddendeathend)
-                       checkrules_status = WINNING_YES;
-
-       if(checkrules_status == WINNING_YES)
-       {
-               //print("WINNING\n");
-               NextLevel();
-       }
-}
-
-string GotoMap(string m)
-{
-       m = GameTypeVote_MapInfo_FixName(m);
-       if (!m)
-               return "The map you suggested is not available on this server.";
-       if (!autocvar_sv_vote_gametype)
-       if(!MapInfo_CheckMap(m))
-               return "The map you suggested does not support the current game mode.";
-       cvar_set("nextmap", m);
-       cvar_set("timelimit", "-1");
-       if(mapvote_initialized || alreadychangedlevel)
-       {
-               if(DoNextMapOverride(0))
-                       return "Map switch initiated.";
-               else
-                       return "Hm... no. For some reason I like THIS map more.";
-       }
-       else
-               return "Map switch will happen after scoreboard.";
-}
-
-bool autocvar_sv_gameplayfix_multiplethinksperframe = true;
-void RunThink(entity this)
-{
-       // don't let things stay in the past.
-       // it is possible to start that way by a trigger with a local time.
-       if(this.nextthink <= 0 || this.nextthink > time + frametime)
-               return;
-
-       float oldtime = time; // do we need to save this?
-
-       for (int iterations = 0; iterations < 128 && !wasfreed(this); iterations++)
-       {
-               time = max(oldtime, this.nextthink);
-               this.nextthink = 0;
-
-               if(getthink(this))
-                       getthink(this)(this);
-               // mods often set nextthink to time to cause a think every frame,
-               // we don't want to loop in that case, so exit if the new nextthink is
-               // <= the time the qc was told, also exit if it is past the end of the
-               // frame
-               if(this.nextthink <= time || this.nextthink > oldtime + frametime || !autocvar_sv_gameplayfix_multiplethinksperframe)
-                       break;
-       }
-
-       time = oldtime;
-}
-
-bool autocvar_sv_freezenonclients;
-bool autocvar_sv_gameplayfix_delayprojectiles = false;
-void Physics_Frame()
-{
-       if(autocvar_sv_freezenonclients)
-               return;
-
-       IL_EACH(g_moveables, true,
-       {
-               if(IS_CLIENT(it) || it.classname == "" || it.move_movetype == MOVETYPE_PHYSICS)
-                       continue;
-
-               //set_movetype(it, it.move_movetype);
-               // inline the set_movetype function, since this is called a lot
-               it.movetype = (it.move_qcphysics) ? MOVETYPE_QCENTITY : it.move_movetype;
-
-               if(it.move_qcphysics && it.move_movetype != MOVETYPE_NONE)
-                       Movetype_Physics_NoMatchTicrate(it, PHYS_INPUT_TIMELENGTH, false);
-
-               if(it.movetype >= MOVETYPE_USER_FIRST && it.movetype <= MOVETYPE_USER_LAST) // these cases have no think handling
-               {
-                       if(it.move_movetype == MOVETYPE_PUSH || it.move_movetype == MOVETYPE_FAKEPUSH)
-                               continue; // these movetypes have no regular think function
-                       // handle thinking here
-                       if (getthink(it) && it.nextthink > 0 && it.nextthink <= time + frametime)
-                               RunThink(it);
-               }
-       });
-
-       if(autocvar_sv_gameplayfix_delayprojectiles >= 0)
-               return;
-
-       IL_EACH(g_moveables, it.move_qcphysics,
-       {
-               if(IS_CLIENT(it) || it.classname == "" || it.move_movetype == MOVETYPE_NONE)
-                       continue;
-               Movetype_Physics_NoMatchTicrate(it, PHYS_INPUT_TIMELENGTH, false);
-       });
-}
-
-void systems_update();
-void EndFrame()
-{
-       anticheat_endframe();
-
-       Physics_Frame();
-
-       FOREACH_CLIENT(IS_REAL_CLIENT(it), {
-               entity e = IS_SPEC(it) ? it.enemy : it;
-               if (e.typehitsound) {
-                       STAT(TYPEHIT_TIME, it) = time;
-               } else if (e.killsound) {
-                       STAT(KILL_TIME, it) = time;
-               } else if (e.damage_dealt) {
-                       STAT(HIT_TIME, it) = time;
-                       STAT(DAMAGE_DEALT_TOTAL, it) += ceil(e.damage_dealt);
-               }
-       });
-       // add 1 frametime because after this, engine SV_Physics
-       // increases time by a frametime and then networks the frame
-       // add another frametime because client shows everything with
-       // 1 frame of lag (cl_nolerp 0). The last +1 however should not be
-       // needed!
-       float altime = time + frametime * (1 + autocvar_g_antilag_nudge);
-       FOREACH_CLIENT(true, {
-               it.typehitsound = false;
-               it.damage_dealt = 0;
-               it.killsound = false;
-               antilag_record(it, CS(it), altime);
-       });
-       IL_EACH(g_monsters, true,
-       {
-               antilag_record(it, it, altime);
-       });
-       IL_EACH(g_projectiles, it.classname == "nade",
-       {
-               antilag_record(it, it, altime);
-       });
-       systems_update();
-       IL_ENDFRAME();
-}
-
-
-/*
- * RedirectionThink:
- * returns true if redirecting
- */
-float redirection_timeout;
-float redirection_nextthink;
-float RedirectionThink()
-{
-       float clients_found;
-
-       if(redirection_target == "")
-               return false;
-
-       if(!redirection_timeout)
-       {
-               cvar_set("sv_public", "-2");
-               redirection_timeout = time + 0.6; // this will only try twice... should be able to keep more clients
-               if(redirection_target == "self")
-                       bprint("^3SERVER NOTICE:^7 restarting the server\n");
-               else
-                       bprint("^3SERVER NOTICE:^7 redirecting everyone to ", redirection_target, "\n");
-       }
-
-       if(time < redirection_nextthink)
-               return true;
-
-       redirection_nextthink = time + 1;
-
-       clients_found = 0;
-       FOREACH_CLIENT(IS_REAL_CLIENT(it), {
-               // TODO add timer
-               LOG_INFO("Redirecting: sending connect command to ", it.netname);
-               if(redirection_target == "self")
-                       stuffcmd(it, "\ndisconnect; defer ", ftos(autocvar_quit_and_redirect_timer), " reconnect\n");
-               else
-                       stuffcmd(it, strcat("\ndisconnect; defer ", ftos(autocvar_quit_and_redirect_timer), " \"connect ", redirection_target, "\"\n"));
-               ++clients_found;
-       });
-
-       LOG_INFO("Redirecting: ", ftos(clients_found), " clients left.");
-
-       if(time > redirection_timeout || clients_found == 0)
-               localcmd("\nwait; wait; wait; quit\n");
-
-       return true;
-}
-
-void RestoreGame()
-{
-       // Loaded from a save game
-       // some things then break, so let's work around them...
-
-       // Progs DB (capture records)
-       ServerProgsDB = db_load(strcat("server.db", autocvar_sessionid));
-
-       // Mapinfo
-       MapInfo_Shutdown();
-       MapInfo_Enumerate();
-       MapInfo_FilterGametype(MapInfo_CurrentGametype(), MapInfo_CurrentFeatures(), MapInfo_RequiredFlags(), MapInfo_ForbiddenFlags(), 1);
-       WeaponStats_Init();
-
-       TargetMusic_RestoreGame();
-}
-
-void Shutdown()
-{
-       game_stopped = 2;
-
-       if(world_initialized > 0)
-       {
-               world_initialized = 0;
-
-               // if a timeout is active, reset the slowmo value to normal
-               if(timeout_status == TIMEOUT_ACTIVE)
-                       cvar_set("slowmo", ftos(orig_slowmo));
-
-               LOG_TRACE("Saving persistent data...");
-               Ban_SaveBans();
-
-               // playerstats with unfinished match
-               PlayerStats_GameReport(false);
-
-               if(!cheatcount_total)
-               {
-                       if(autocvar_sv_db_saveasdump)
-                               db_dump(ServerProgsDB, strcat("server.db", autocvar_sessionid));
-                       else
-                               db_save(ServerProgsDB, strcat("server.db", autocvar_sessionid));
-               }
-               if(autocvar_developer > 0)
-               {
-                       if(autocvar_sv_db_saveasdump)
-                               db_dump(TemporaryDB, "server-temp.db");
-                       else
-                               db_save(TemporaryDB, "server-temp.db");
-               }
-               CheatShutdown(); // must be after cheatcount check
-               db_close(ServerProgsDB);
-               db_close(TemporaryDB);
-               LOG_TRACE("Saving persistent data... done!");
-               // tell the bot system the game is ending now
-               bot_endgame();
-
-               WeaponStats_Shutdown();
-               MapInfo_Shutdown();
-       }
-       else if(world_initialized == 0)
-       {
-               LOG_INFO("NOTE: crashed before even initializing the world, not saving persistent data");
-       }
-       else
-       {
-               __init_dedicated_server_shutdown();
-       }
-}
diff --git a/qcsrc/server/g_world.qh b/qcsrc/server/g_world.qh
deleted file mode 100644 (file)
index 3bbaad6..0000000
+++ /dev/null
@@ -1,65 +0,0 @@
-#pragma once
-
-float checkrules_equality;
-float checkrules_suddendeathwarning;
-float checkrules_suddendeathend;
-float checkrules_overtimesadded; //how many overtimes have been already added
-
-// flag set on worldspawn so that the code knows if it is dedicated or not
-bool server_is_dedicated;
-
-string cvar_changes;
-string cvar_purechanges;
-float cvar_purechanges_count;
-
-string modname;
-
-string gamemode_name;
-
-string clientstuff;
-
-string matchid;
-
-.string fog;
-
-float intermission_running;
-float intermission_exittime;
-float alreadychangedlevel;
-
-string cache_mutatormsg;
-string cache_lastmutatormsg;
-
-float default_player_alpha;
-float default_weapon_alpha;
-
-// database
-float ServerProgsDB;
-float TemporaryDB;
-
-.float winning;
-const int WINNING_NO = 0; // no winner, but time limits may terminate the game
-const int WINNING_YES = 1; // winner found
-const int WINNING_NEVER = 2; // no winner, enter overtime if time limit is reached
-const int WINNING_STARTSUDDENDEATHOVERTIME = 3; // no winner, enter suddendeath overtime NOW
-
-float WinningCondition_Scores(float limit, float leadlimit);
-void SetWinners(.float field, float value);
-void IntermissionThink(entity this);
-void GotoNextMap(float reinit);
-void ReadyRestart();
-
-string GetGametype();
-
-void DumpStats(float final);
-float Map_IsRecent(string m);
-string GetNextMap();
-void ShuffleMaplist();
-void Map_Goto_SetStr(string nextmapname);
-void Map_Goto(float reinit);
-void Map_MarkAsRecent(string m);
-float DoNextMapOverride(float reinit);
-void CheckRules_World();
-float RedirectionThink();
-
-IntrusiveList g_moveables;
-STATIC_INIT(g_moveables) { g_moveables = IL_NEW(); }
diff --git a/qcsrc/server/hook.qc b/qcsrc/server/hook.qc
new file mode 100644 (file)
index 0000000..6042a48
--- /dev/null
@@ -0,0 +1,435 @@
+#include "hook.qh"
+
+#include <server/bot/api.qh>
+#include <common/weapons/_all.qh>
+#include <common/stats.qh>
+#include <server/damage.qh>
+#include <server/miscfunctions.qh>
+#include <common/effects/all.qh>
+#include "weapons/common.qh"
+#include "weapons/csqcprojectile.qh"
+#include "weapons/weaponsystem.qh"
+#include "weapons/selection.qh"
+#include "weapons/tracing.qh"
+#include "player.qh"
+#include "command/common.qh"
+#include "command/vote.qh"
+#include "round_handler.qh"
+#include "../common/state.qh"
+#include "../common/physics/player.qh"
+#include "../common/vehicles/all.qh"
+#include "../common/constants.qh"
+#include "../common/util.qh"
+#include <common/net_linked.qh>
+#include <common/weapons/_all.qh>
+#include "../lib/warpzone/common.qh"
+#include "../lib/warpzone/server.qh"
+
+/*============================================
+
+      Wazat's Xonotic Grappling Hook
+
+        Contact: Wazat1@gmail.com
+
+
+Installation instructions:
+--------------------------
+
+1. Place hook.c in your gamec source directory with the other source files.
+
+2. Add this line to the bottom of progs.src:
+
+gamec/hook.c
+
+3. Open defs.h and add these lines to the very bottom:
+
+// Wazat's grappling hook
+.entity                hook;
+void GrapplingHookFrame();
+void RemoveGrapplingHook(entity pl);
+void SetGrappleHookBindings();
+// hook impulses
+const float GRAPHOOK_FIRE              = 20;
+const float GRAPHOOK_RELEASE           = 21;
+// (note: you can change the hook impulse #'s to whatever you please)
+
+4. Open client.c and add this to the top of PutClientInServer():
+
+       RemoveGrapplingHook(this); // Wazat's Grappling Hook
+
+5. Find ClientConnect() (in client.c) and add these lines to the bottom:
+
+       // Wazat's grappling hook
+       SetGrappleHookBindings();
+
+6. Still in client.c, find PlayerPreThink and add this line just above the call to W_WeaponFrame:
+
+       GrapplingHookFrame();
+
+7. Build and test the mod.  You'll want to bind a key to "+hook" like this:
+bind ctrl "+hook"
+
+And you should be done!
+
+
+============================================*/
+
+void RemoveGrapplingHooks(entity pl)
+{
+       if(pl.move_movetype == MOVETYPE_FLY)
+               set_movetype(pl, MOVETYPE_WALK);
+
+       for(int slot = 0; slot < MAX_WEAPONSLOTS; ++slot)
+    {
+       .entity weaponentity = weaponentities[slot];
+       if(!pl.(weaponentity))
+               continue; // continue incase other slots exist?
+       if(pl.(weaponentity).hook)
+               delete(pl.(weaponentity).hook);
+       pl.(weaponentity).hook = NULL;
+    }
+
+       //pl.disableclientprediction = false;
+}
+
+void RemoveHook(entity this)
+{
+       entity player = this.realowner;
+    .entity weaponentity = this.weaponentity_fld;
+
+    if(player.(weaponentity).hook == this)
+       player.(weaponentity).hook = NULL;
+
+    if(player.move_movetype == MOVETYPE_FLY)
+       set_movetype(player, MOVETYPE_WALK);
+    delete(this);
+}
+
+void GrapplingHookReset(entity this)
+{
+       RemoveHook(this);
+}
+
+void GrapplingHook_Stop(entity this)
+{
+       Send_Effect(EFFECT_HOOK_IMPACT, this.origin, '0 0 0', 1);
+       sound (this, CH_SHOTS, SND_HOOK_IMPACT, VOL_BASE, ATTEN_NORM);
+
+       this.state = 1;
+       setthink(this, GrapplingHookThink);
+       this.nextthink = time;
+       settouch(this, func_null);
+       this.velocity = '0 0 0';
+       set_movetype(this, MOVETYPE_NONE);
+       this.hook_length = -1;
+}
+
+.vector hook_start, hook_end;
+bool GrapplingHookSend(entity this, entity to, int sf)
+{
+       WriteHeader(MSG_ENTITY, ENT_CLIENT_HOOK);
+       sf = sf & 0x7F;
+       if(sound_allowed(MSG_BROADCAST, this.realowner))
+               sf |= 0x80;
+       WriteByte(MSG_ENTITY, sf);
+       if(sf & 1)
+       {
+               WriteByte(MSG_ENTITY, etof(this.realowner));
+               WriteByte(MSG_ENTITY, weaponslot(this.weaponentity_fld));
+       }
+       if(sf & 2)
+       {
+               WriteVector(MSG_ENTITY, this.hook_start);
+       }
+       if(sf & 4)
+       {
+               WriteVector(MSG_ENTITY, this.hook_end);
+       }
+       return true;
+}
+
+int autocvar_g_grappling_hook_tarzan;
+
+void GrapplingHookThink(entity this)
+{
+       float spd, dist, minlength, pullspeed, ropestretch, ropeairfriction, rubberforce, newlength, rubberforce_overstretch;
+       vector dir, org, end, v0, dv, v, myorg, vs;
+       .entity weaponentity = this.weaponentity_fld;
+       if(this.realowner.(weaponentity).hook != this)  // how did that happen?
+       {
+               error("Owner lost the hook!\n");
+               return;
+       }
+       if(LostMovetypeFollow(this) || game_stopped || (round_handler_IsActive() && !round_handler_IsRoundStarted()) || ((this.aiment.flags & FL_PROJECTILE) && this.aiment.classname != "nade"))
+       {
+               RemoveHook(this);
+               return;
+       }
+       if(this.aiment)
+               WarpZone_RefSys_AddIncrementally(this, this.aiment);
+
+       this.nextthink = time;
+
+       int s = W_GunAlign(this.realowner.(weaponentity), STAT(GUNALIGN, this.realowner)) - 1;
+       vs = hook_shotorigin[s];
+
+       makevectors(this.realowner.v_angle);
+       org = this.realowner.origin + this.realowner.view_ofs + v_forward * vs.x + v_right * -vs.y + v_up * vs.z;
+       myorg = WarpZone_RefSys_TransformOrigin(this.realowner, this, org);
+
+       if(this.hook_length < 0)
+               this.hook_length = vlen(myorg - this.origin);
+
+       int tarzan = autocvar_g_grappling_hook_tarzan;
+       entity pull_entity = this.realowner;
+       float velocity_multiplier = 1;
+       MUTATOR_CALLHOOK(GrappleHookThink, this, tarzan, pull_entity, velocity_multiplier);
+       tarzan = M_ARGV(1, int);
+       pull_entity = M_ARGV(2, entity);
+       velocity_multiplier = M_ARGV(3, float);
+
+       if(this.state == 1)
+       {
+               pullspeed = autocvar_g_balance_grapplehook_speed_pull;//2000;
+               // speed the rope is pulled with
+
+               rubberforce = autocvar_g_balance_grapplehook_force_rubber;//2000;
+               // force the rope will use if it is stretched
+
+               rubberforce_overstretch = autocvar_g_balance_grapplehook_force_rubber_overstretch;//1000;
+               // force the rope will use if it is stretched
+
+               minlength = autocvar_g_balance_grapplehook_length_min;//100;
+               // minimal rope length
+               // if the rope goes below this length, it isn't pulled any more
+
+               ropestretch = autocvar_g_balance_grapplehook_stretch;//400;
+               // if the rope is stretched by more than this amount, more rope is
+               // given to you again
+
+               ropeairfriction = autocvar_g_balance_grapplehook_airfriction;//0.2
+               // while hanging on the rope, this friction component will help you a
+               // bit to control the rope
+
+               bool frozen_pulling = (autocvar_g_grappling_hook_tarzan >= 2 && autocvar_g_balance_grapplehook_pull_frozen);
+
+               dir = this.origin - myorg;
+               dist = vlen(dir);
+               dir = normalize(dir);
+
+               if(tarzan)
+               {
+                       v = v0 = WarpZone_RefSys_TransformVelocity(pull_entity, this, pull_entity.velocity);
+
+                       // first pull the rope...
+                       if(this.realowner.(weaponentity).hook_state & HOOK_PULLING)
+                       {
+                               newlength = this.hook_length;
+                               newlength = max(newlength - pullspeed * frametime, minlength);
+
+                               if(newlength < dist - ropestretch) // overstretched?
+                               {
+                                       newlength = dist - ropestretch;
+                                       if(v * dir < 0) // only if not already moving in hook direction
+                                               v = v + frametime * dir * rubberforce_overstretch;
+                               }
+
+                               this.hook_length = newlength;
+                       }
+
+                       if(pull_entity.move_movetype == MOVETYPE_FLY)
+                               set_movetype(pull_entity, MOVETYPE_WALK);
+
+                       if(this.realowner.(weaponentity).hook_state & HOOK_RELEASING)
+                       {
+                               newlength = dist;
+                               this.hook_length = newlength;
+                       }
+                       else
+                       {
+                               // then pull the player
+                               spd = bound(0, (dist - this.hook_length) / ropestretch, 1);
+                               v = v * (1 - frametime * ropeairfriction);
+                               v = v + frametime * dir * spd * rubberforce;
+
+                               dv = ((v - v0) * dir) * dir;
+                               if(tarzan >= 2)
+                               {
+                                       if(this.aiment.move_movetype == MOVETYPE_WALK || this.aiment.classname == "nade")
+                                       {
+                                               entity aim_ent = ((IS_VEHICLE(this.aiment) && this.aiment.owner) ? this.aiment.owner : this.aiment);
+                                               v = v - dv * 0.5;
+                                               if((frozen_pulling && STAT(FROZEN, this.aiment)) || !frozen_pulling)
+                                               {
+                                                       this.aiment.velocity = this.aiment.velocity - dv * 0.5;
+                                                       UNSET_ONGROUND(this.aiment);
+                                                       if(this.aiment.flags & FL_PROJECTILE)
+                                                               UpdateCSQCProjectile(this.aiment);
+                                               }
+                                               if(this.aiment.classname == "nade")
+                                                       this.aiment.nextthink = time + autocvar_g_balance_grapplehook_nade_time; // set time after letting go?
+                                               aim_ent.pusher = this.realowner;
+                                               aim_ent.pushltime = time + autocvar_g_maxpushtime;
+                                               aim_ent.istypefrag = PHYS_INPUT_BUTTON_CHAT(aim_ent);
+                                       }
+                               }
+
+                               UNSET_ONGROUND(pull_entity);
+                       }
+
+                       if(!frozen_pulling && !(this.aiment.flags & FL_PROJECTILE))
+                               pull_entity.velocity = WarpZone_RefSys_TransformVelocity(this, pull_entity, v * velocity_multiplier);
+
+                       if(frozen_pulling && autocvar_g_balance_grapplehook_pull_frozen == 2 && !STAT(FROZEN, this.aiment))
+                       {
+                               RemoveHook(this);
+                               return;
+                       }
+               }
+               else
+               {
+                       end = this.origin - dir*50;
+                       dist = vlen(end - myorg);
+                       if(dist < 200)
+                               spd = dist * (pullspeed / 200);
+                       else
+                               spd = pullspeed;
+                       if(spd < 50)
+                               spd = 0;
+                       this.realowner.velocity = dir*spd;
+                       set_movetype(this.realowner, MOVETYPE_FLY);
+
+                       UNSET_ONGROUND(this.realowner);
+               }
+       }
+
+       makevectors(this.angles.x * '-1 0 0' + this.angles.y * '0 1 0');
+       myorg = WarpZone_RefSys_TransformOrigin(this, this.realowner, this.origin); // + v_forward * (-9);
+
+       if(myorg != this.hook_start)
+       {
+               this.SendFlags |= 2;
+               this.hook_start = myorg;
+       }
+       if(org != this.hook_end)
+       {
+               this.SendFlags |= 4;
+               this.hook_end = org;
+       }
+}
+
+void GrapplingHookTouch(entity this, entity toucher)
+{
+       if(toucher.move_movetype == MOVETYPE_FOLLOW)
+               return;
+       PROJECTILE_TOUCH(this, toucher);
+
+       GrapplingHook_Stop(this);
+
+       if(toucher)
+               //if(toucher.move_movetype != MOVETYPE_NONE)
+               {
+                       SetMovetypeFollow(this, toucher);
+                       WarpZone_RefSys_BeginAddingIncrementally(this, this.aiment);
+               }
+
+       //this.realowner.disableclientprediction = true;
+}
+
+void GrapplingHook_Damage(entity this, entity inflictor, entity attacker, float damage, int deathtype, .entity weaponentity, vector hitloc, vector force)
+{
+       if(GetResource(this, RES_HEALTH) <= 0)
+               return;
+
+       if (!W_CheckProjectileDamage(inflictor.realowner, this.realowner, deathtype, -1)) // no exceptions
+               return; // g_balance_projectiledamage says to halt
+
+       TakeResource(this, RES_HEALTH, damage);
+
+       if (GetResource(this, RES_HEALTH) <= 0)
+       {
+               if(attacker != this.realowner)
+               {
+                       this.realowner.pusher = attacker;
+                       this.realowner.pushltime = time + autocvar_g_maxpushtime;
+                       this.realowner.istypefrag = PHYS_INPUT_BUTTON_CHAT(this.realowner);
+               }
+               RemoveHook(this);
+       }
+}
+
+void FireGrapplingHook(entity actor, .entity weaponentity)
+{
+       if(weaponLocked(actor)) return;
+       if(actor.vehicle) return;
+
+       int s = W_GunAlign(actor.(weaponentity), STAT(GUNALIGN, actor)) - 1;
+       vector vs = hook_shotorigin[s];
+       vector oldmovedir = actor.(weaponentity).movedir;
+       actor.(weaponentity).movedir = vs;
+       W_SetupShot_ProjectileSize(actor, weaponentity, '-3 -3 -3', '3 3 3', true, 0, SND_HOOK_FIRE, CH_WEAPON_B, 0, WEP_HOOK.m_id);
+       W_MuzzleFlash(WEP_HOOK, actor, weaponentity, w_shotorg, '0 0 0');
+       actor.(weaponentity).movedir = oldmovedir;
+
+       entity missile = WarpZone_RefSys_SpawnSameRefSys(actor);
+       missile.owner = missile.realowner = actor;
+       actor.(weaponentity).hook = missile;
+       missile.weaponentity_fld = weaponentity;
+       missile.reset = GrapplingHookReset;
+       missile.classname = "grapplinghook";
+       missile.flags = FL_PROJECTILE;
+       IL_PUSH(g_projectiles, missile);
+       IL_PUSH(g_bot_dodge, missile);
+
+       set_movetype(missile, ((autocvar_g_balance_grapplehook_gravity) ? MOVETYPE_TOSS : MOVETYPE_FLY));
+       PROJECTILE_MAKETRIGGER(missile);
+
+       //setmodel (missile, MDL_HOOK); // precision set below
+       setsize (missile, '-3 -3 -3', '3 3 3');
+       setorigin(missile, w_shotorg);
+
+       missile.state = 0; // not latched onto anything
+
+       W_SetupProjVelocity_Explicit(missile, w_shotdir, v_up, autocvar_g_balance_grapplehook_speed_fly, 0, 0, 0, false);
+
+       missile.angles = vectoangles (missile.velocity);
+       //missile.glow_color = 250; // 244, 250
+       //missile.glow_size = 120;
+       settouch(missile, GrapplingHookTouch);
+       setthink(missile, GrapplingHookThink);
+       missile.nextthink = time;
+
+       missile.effects = /*EF_FULLBRIGHT | EF_ADDITIVE |*/ EF_LOWPRECISION;
+
+       SetResourceExplicit(missile, RES_HEALTH, autocvar_g_balance_grapplehook_health);
+       missile.event_damage = GrapplingHook_Damage;
+       missile.takedamage = DAMAGE_AIM;
+       missile.damageforcescale = 0;
+       missile.damagedbycontents = (autocvar_g_balance_grapplehook_damagedbycontents);
+       if(missile.damagedbycontents)
+               IL_PUSH(g_damagedbycontents, missile);
+
+       missile.hook_start = missile.hook_end = missile.origin;
+
+       Net_LinkEntity(missile, false, 0, GrapplingHookSend);
+}
+
+void GrappleHookInit()
+{
+       if(g_grappling_hook)
+       {
+               hook_shotorigin[0] = '8 8 -12';
+               hook_shotorigin[1] = '8 8 -12';
+               hook_shotorigin[2] = '8 8 -12';
+               hook_shotorigin[3] = '8 8 -12';
+       }
+       else
+       {
+               Weapon w = WEP_HOOK;
+               w.wr_init(w);
+               hook_shotorigin[0] = shotorg_adjust_values(CL_Weapon_GetShotOrg(WEP_HOOK.m_id), false, false, 1);
+               hook_shotorigin[1] = shotorg_adjust_values(CL_Weapon_GetShotOrg(WEP_HOOK.m_id), false, false, 2);
+               hook_shotorigin[2] = shotorg_adjust_values(CL_Weapon_GetShotOrg(WEP_HOOK.m_id), false, false, 3);
+               hook_shotorigin[3] = shotorg_adjust_values(CL_Weapon_GetShotOrg(WEP_HOOK.m_id), false, false, 4);
+       }
+}
diff --git a/qcsrc/server/hook.qh b/qcsrc/server/hook.qh
new file mode 100644 (file)
index 0000000..1ed78e2
--- /dev/null
@@ -0,0 +1,23 @@
+#pragma once
+
+// Wazat's grappling hook
+.entity                hook;
+void GrapplingHookThink(entity this);
+void RemoveGrapplingHooks(entity pl);
+void RemoveHook(entity this);
+// (note: you can change the hook impulse #'s to whatever you please)
+.float hook_time;
+
+.float hook_length;
+
+const float HOOK_FIRING = BIT(0);
+const float HOOK_REMOVING = BIT(1);
+const float HOOK_PULLING = BIT(2);
+const float HOOK_RELEASING = BIT(3);
+const float HOOK_WAITING_FOR_RELEASE = BIT(4);
+.float hook_state;
+.int state;
+
+void GrappleHookInit();
+vector hook_shotorigin[4];
+
index 521c2223f7eab73582b4326fc2ba33e6cf73d66c..779384d70c08747fe6e29c13fa6c4ccec2644721 100644 (file)
@@ -6,7 +6,7 @@
 #include "cheats.qh"
 #include "client.qh"
 #include "clientkill.qh"
-#include "g_damage.qh"
+#include "damage.qh"
 #include "weapons/selection.qh"
 #include "weapons/tracing.qh"
 #include "weapons/weaponsystem.qh"
index 44cd6ba2859fa4948ff68c0f0b6fbd94d480d5d0..861b21eed2b566df931eef6a382f4b848139ec7c 100644 (file)
@@ -6,7 +6,7 @@
 
 #include <server/command/vote.qh>
 
-#include <server/g_damage.qh>
+#include <server/damage.qh>
 
 #include <server/mutators/_mod.qh>
 
diff --git a/qcsrc/server/main.qc b/qcsrc/server/main.qc
new file mode 100644 (file)
index 0000000..bbafd02
--- /dev/null
@@ -0,0 +1,406 @@
+#include "main.qh"
+
+#include "anticheat.qh"
+#include "hook.qh"
+#include "damage.qh"
+#include "world.qh"
+#include "spawnpoints.qh"
+#include <server/gamelog.qh>
+
+#include "bot/api.qh"
+
+#include "command/common.qh"
+
+#include <server/mutators/_mod.qh>
+#include "weapons/csqcprojectile.qh"
+#include <server/weapons/common.qh>
+#include <server/compat/quake3.qh>
+
+#include "../common/constants.qh"
+#include "../common/deathtypes/all.qh"
+#include "../common/debug.qh"
+#include "../common/mapinfo.qh"
+#include "../common/util.qh"
+
+#include "../common/vehicles/all.qh"
+#include <common/monsters/sv_monsters.qh>
+#include <common/weapons/_all.qh>
+
+#include "../lib/csqcmodel/sv_model.qh"
+
+#include "../lib/warpzone/common.qh"
+#include "../lib/warpzone/server.qh"
+
+void CreatureFrame_hotliquids(entity this)
+{
+       if (this.contents_damagetime >= time)
+       {
+               return;
+       }
+
+       this.contents_damagetime = time + autocvar_g_balance_contents_damagerate;
+
+       if (this.flags & FL_PROJECTILE)
+       {
+               if (this.watertype == CONTENT_LAVA)
+                       Damage (this, NULL, NULL, autocvar_g_balance_contents_projectiledamage * autocvar_g_balance_contents_damagerate * this.waterlevel, DEATH_LAVA.m_id, DMG_NOWEP, this.origin, '0 0 0');
+               else if (this.watertype == CONTENT_SLIME)
+                       Damage (this, NULL, NULL, autocvar_g_balance_contents_projectiledamage * autocvar_g_balance_contents_damagerate * this.waterlevel, DEATH_SLIME.m_id, DMG_NOWEP, this.origin, '0 0 0');
+       }
+       else
+       {
+               if (STAT(FROZEN, this))
+               {
+                       if (this.watertype == CONTENT_LAVA)
+                               Damage(this, NULL, NULL, 10000, DEATH_LAVA.m_id, DMG_NOWEP, this.origin, '0 0 0');
+                       else if (this.watertype == CONTENT_SLIME)
+                               Damage(this, NULL, NULL, 10000, DEATH_SLIME.m_id, DMG_NOWEP, this.origin, '0 0 0');
+               }
+               else if (this.watertype == CONTENT_LAVA)
+               {
+                       if (this.watersound_finished < time)
+                       {
+                               this.watersound_finished = time + 0.5;
+                               sound (this, CH_PLAYER_SINGLE, SND_LAVA, VOL_BASE, ATTEN_NORM);
+                       }
+                       Damage (this, NULL, NULL, autocvar_g_balance_contents_playerdamage_lava * autocvar_g_balance_contents_damagerate * this.waterlevel, DEATH_LAVA.m_id, DMG_NOWEP, this.origin, '0 0 0');
+                       if(autocvar_g_balance_contents_playerdamage_lava_burn)
+                               Fire_AddDamage(this, NULL, autocvar_g_balance_contents_playerdamage_lava_burn * this.waterlevel, autocvar_g_balance_contents_playerdamage_lava_burn_time * this.waterlevel, DEATH_LAVA.m_id);
+               }
+               else if (this.watertype == CONTENT_SLIME)
+               {
+                       if (this.watersound_finished < time)
+                       {
+                               this.watersound_finished = time + 0.5;
+                               sound (this, CH_PLAYER_SINGLE, SND_SLIME, VOL_BASE, ATTEN_NORM);
+                       }
+                       Damage (this, NULL, NULL, autocvar_g_balance_contents_playerdamage_slime * autocvar_g_balance_contents_damagerate * this.waterlevel, DEATH_SLIME.m_id, DMG_NOWEP, this.origin, '0 0 0');
+               }
+       }
+}
+
+void CreatureFrame_Liquids(entity this)
+{
+       if (this.watertype <= CONTENT_WATER && this.waterlevel > 0) // workaround a retarded bug made by id software :P (yes, it's that old of a bug)
+       {
+               if (!(this.flags & FL_INWATER))
+               {
+                       this.flags |= FL_INWATER;
+                       this.contents_damagetime = 0;
+               }
+
+               CreatureFrame_hotliquids(this);
+       }
+       else
+       {
+               if (this.flags & FL_INWATER)
+               {
+                       // play leave water sound
+                       this.flags &= ~FL_INWATER;
+                       this.contents_damagetime = 0;
+               }
+       }
+}
+
+void CreatureFrame_FallDamage(entity this)
+{
+       if(IS_VEHICLE(this) || (this.flags & FL_PROJECTILE))
+               return; // vehicles and projectiles don't receive fall damage
+       if(!(this.velocity || this.oldvelocity))
+               return; // if the entity hasn't moved and isn't moving, then don't do anything
+
+       // check for falling damage
+       bool have_hook = false;
+       for(int slot = 0; slot < MAX_WEAPONSLOTS; ++slot)
+       {
+           .entity weaponentity = weaponentities[slot];
+           if(this.(weaponentity).hook && this.(weaponentity).hook.state)
+           {
+               have_hook = true;
+               break;
+           }
+       }
+       if(!have_hook)
+       {
+               float dm; // dm is the velocity DECREASE. Velocity INCREASE should never cause a sound or any damage.
+               if(autocvar_g_balance_falldamage_onlyvertical)
+                       dm = fabs(this.oldvelocity.z) - vlen(this.velocity);
+               else
+                       dm = vlen(this.oldvelocity) - vlen(this.velocity);
+               if (IS_DEAD(this))
+                       dm = (dm - autocvar_g_balance_falldamage_deadminspeed) * autocvar_g_balance_falldamage_factor;
+               else
+                       dm = min((dm - autocvar_g_balance_falldamage_minspeed) * autocvar_g_balance_falldamage_factor, autocvar_g_balance_falldamage_maxdamage);
+               if (dm > 0)
+               {
+                       tracebox(this.origin, this.mins, this.maxs, this.origin - '0 0 1', MOVE_NOMONSTERS, this);
+                       if (!(trace_dphitq3surfaceflags & Q3SURFACEFLAG_NODAMAGE))
+                               Damage (this, NULL, NULL, dm, DEATH_FALL.m_id, DMG_NOWEP, this.origin, '0 0 0');
+               }
+       }
+
+       if(autocvar_g_maxspeed > 0 && vdist(this.velocity, >, autocvar_g_maxspeed))
+               Damage (this, NULL, NULL, 100000, DEATH_SHOOTING_STAR.m_id, DMG_NOWEP, this.origin, '0 0 0');
+}
+
+void CreatureFrame_All()
+{
+       if(game_stopped || time < game_starttime)
+               return;
+
+       IL_EACH(g_damagedbycontents, it.damagedbycontents,
+       {
+               if (it.move_movetype == MOVETYPE_NOCLIP) continue;
+               CreatureFrame_Liquids(it);
+               CreatureFrame_FallDamage(it);
+               it.oldvelocity = it.velocity;
+       });
+}
+
+void Pause_TryPause(bool ispaused)
+{
+       int n = 0;
+       FOREACH_CLIENT(IS_PLAYER(it) && IS_REAL_CLIENT(it), {
+               if (PHYS_INPUT_BUTTON_CHAT(it) != ispaused) return;
+               ++n;
+       });
+       if (!n) return;
+       setpause(ispaused);
+}
+
+void SV_PausedTic(float elapsedtime)
+{
+       if (!server_is_dedicated) Pause_TryPause(false);
+}
+
+/*
+=============
+StartFrame
+
+Called before each frame by the server
+=============
+*/
+
+bool game_delay_last;
+
+bool autocvar_sv_autopause = false;
+void systems_update();
+void sys_phys_update(entity this, float dt);
+void StartFrame()
+{
+    // TODO: if move is more than 50ms, split it into two moves (this matches QWSV behavior and the client prediction)
+    IL_EACH(g_players, IS_FAKE_CLIENT(it), sys_phys_update(it, frametime));
+    IL_EACH(g_players, IS_FAKE_CLIENT(it), PlayerPreThink(it));
+
+       execute_next_frame();
+       if (autocvar_sv_autopause && !server_is_dedicated) Pause_TryPause(true);
+
+       delete_fn = remove_unsafely; // not during spawning!
+       serverprevtime = servertime;
+       servertime = time;
+       serverframetime = frametime;
+
+#ifdef PROFILING
+       if(time > client_cefc_accumulatortime + 1)
+       {
+               float t = client_cefc_accumulator / (time - client_cefc_accumulatortime);
+               int c_seeing = 0;
+               int c_seen = 0;
+               FOREACH_CLIENT(true, {
+                       if(IS_REAL_CLIENT(it))
+                               ++c_seeing;
+                       if(IS_PLAYER(it))
+                               ++c_seen;
+               });
+               LOG_INFO(
+                   "CEFC time: ", ftos(t * 1000), "ms; ",
+            "CEFC calls per second: ", ftos(c_seeing * (c_seen - 1) / t), "; ",
+            "CEFC 100% load at: ", ftos(solve_quadratic(t, -t, -1) * '0 1 0')
+        );
+               client_cefc_accumulatortime = time;
+               client_cefc_accumulator = 0;
+       }
+#endif
+
+       IL_EACH(g_projectiles, it.csqcprojectile_clientanimate, CSQCProjectile_Check(it));
+
+       if (RedirectionThink()) return;
+
+       UncustomizeEntitiesRun();
+       InitializeEntitiesRun();
+
+       WarpZone_StartFrame();
+
+       sys_frametime = autocvar_sys_ticrate * autocvar_slowmo;
+       if (sys_frametime <= 0) sys_frametime = 1.0 / 60.0; // somewhat safe fallback
+
+       if (timeout_status == TIMEOUT_LEADTIME) // just before the timeout (when timeout_status will be TIMEOUT_ACTIVE)
+               orig_slowmo = autocvar_slowmo; // slowmo will be restored after the timeout
+
+       // detect when the pre-game countdown (if any) has ended and the game has started
+       bool game_delay = (time < game_starttime);
+       if (autocvar_sv_eventlog && game_delay_last && !game_delay)
+               GameLogEcho(":startdelay_ended");
+       game_delay_last = game_delay;
+
+       CreatureFrame_All();
+       CheckRules_World();
+
+       if (warmup_stage && !game_stopped && warmup_limit > 0 && time >= warmup_limit) {
+               ReadyRestart();
+               return;
+       }
+
+       bot_serverframe();
+       anticheat_startframe();
+       MUTATOR_CALLHOOK(SV_StartFrame);
+
+       GlobalStats_updateglobal();
+    FOREACH_CLIENT(true, GlobalStats_update(it));
+    IL_EACH(g_players, IS_FAKE_CLIENT(it), PlayerPostThink(it));
+}
+
+.vector originjitter;
+.vector anglesjitter;
+.float anglejitter;
+.string gametypefilter;
+.string cvarfilter;
+
+/**
+ * Evaluate an expression of the form: [+ | -]? [var[op]val | [op]var | val | var] ...
+ * +: all must match. this is the default
+ * -: one must NOT match
+ *
+ * var>x
+ * var<x
+ * var>=x
+ * var<=x
+ * var==x
+ * var!=x
+ * var===x
+ * var!==x
+ */
+bool expr_evaluate(string s)
+{
+    bool ret = false;
+    if (str2chr(s, 0) == '+') {
+        s = substring(s, 1, -1);
+    } else if (str2chr(s, 0) == '-') {
+        ret = true;
+        s = substring(s, 1, -1);
+    }
+    bool expr_fail = false;
+    for (int i = 0, n = tokenize_console(s); i < n; ++i) {
+        int o;
+        string k, v;
+        s = argv(i);
+        #define X(expr) \
+            if (expr) \
+                continue; \
+            expr_fail = true; \
+            break;
+
+        #define BINOP(op, len, expr) \
+            if ((o = strstrofs(s, op, 0)) >= 0) { \
+                k = substring(s, 0, o); \
+                v = substring(s, o + len, -1); \
+                X(expr); \
+            }
+        BINOP(">=", 2, cvar(k) >= stof(v));
+        BINOP("<=", 2, cvar(k) <= stof(v));
+        BINOP(">",  1, cvar(k) >  stof(v));
+        BINOP("<",  1, cvar(k) <  stof(v));
+        BINOP("==", 2, cvar(k) == stof(v));
+        BINOP("!=", 2, cvar(k) != stof(v));
+        BINOP("===", 3, cvar_string(k) == v);
+        BINOP("!==", 3, cvar_string(k) != v);
+        {
+            k = s;
+            bool b = true;
+            if (str2chr(k, 0) == '!') {
+                k = substring(s, 1, -1);
+                b = false;
+            }
+            float f = stof(k);
+            bool isnum = ftos(f) == k;
+            X(boolean(isnum ? f : cvar(k)) == b);
+        }
+        #undef BINOP
+        #undef X
+    }
+    if (!expr_fail) {
+        ret = !ret;
+    }
+    // now ret is true if we want to keep the item, and false if we want to get rid of it
+    return ret;
+}
+
+void SV_OnEntityPreSpawnFunction(entity this)
+{
+       if (this)
+       if (this.gametypefilter != "")
+       if (!isGametypeInFilter(MapInfo_LoadedGametype, teamplay, have_team_spawns, this.gametypefilter))
+       {
+               delete(this);
+               return;
+       }
+       if (this.cvarfilter != "" && !expr_evaluate(this.cvarfilter)) {
+               delete(this);
+               return;
+       }
+
+       if (DoesQ3ARemoveThisEntity(this)) {
+               delete(this);
+               return;
+       }
+
+       set_movetype(this, this.movetype);
+
+       if (this.monster_attack) {
+               IL_PUSH(g_monster_targets, this);
+    }
+
+       // support special -1 and -2 angle from radiant
+       if (this.angles == '0 -1 0') {
+               this.angles = '-90 0 0';
+       } else if (this.angles == '0 -2 0') {
+               this.angles = '+90 0 0';
+    }
+
+    #define X(out, in) MACRO_BEGIN \
+        if (in != 0) { out = out + (random() * 2 - 1) * in; } \
+    MACRO_END
+    X(this.origin.x, this.originjitter.x); X(this.origin.y, this.originjitter.y); X(this.origin.z, this.originjitter.z);
+    X(this.angles.x, this.anglesjitter.x); X(this.angles.y, this.anglesjitter.y); X(this.angles.z, this.anglesjitter.z);
+    X(this.angles.y, this.anglejitter);
+    #undef X
+
+       if (MUTATOR_CALLHOOK(OnEntityPreSpawn, this)) {
+               delete(this);
+               return;
+       }
+}
+
+void WarpZone_PostInitialize_Callback()
+{
+       // create waypoint links for warpzones
+       entity tracetest_ent = spawn();
+       setsize(tracetest_ent, PL_MIN_CONST, PL_MAX_CONST);
+       tracetest_ent.dphitcontentsmask = DPCONTENTS_SOLID | DPCONTENTS_BODY | DPCONTENTS_PLAYERCLIP | DPCONTENTS_BOTCLIP;
+       //for(entity e = warpzone_first; e; e = e.warpzone_next)
+       for(entity e = NULL; (e = find(e, classname, "trigger_warpzone")); )
+               waypoint_spawnforteleporter_wz(e, tracetest_ent);
+       delete(tracetest_ent);
+}
+
+/*
+==================
+main
+
+unused but required by the engine
+==================
+*/
+void main ()
+{
+
+}
diff --git a/qcsrc/server/main.qh b/qcsrc/server/main.qh
new file mode 100644 (file)
index 0000000..dc3d80d
--- /dev/null
@@ -0,0 +1,28 @@
+#pragma once
+
+bool expr_evaluate(string s);
+
+#ifdef PROFILING
+float client_cefc_accumulator;
+float client_cefc_accumulatortime;
+#endif
+
+float servertime, serverprevtime, serverframetime;
+
+.vector oldvelocity; // for fall damage
+
+.float watersound_finished;
+
+.bool iscreature;
+.float species;
+
+.float contents_damagetime;
+
+/*
+==================
+main
+
+unused but required by the engine
+==================
+*/
+void main ();
index 2286b60a48ff4bed4f09cbc429584dbfa8540674..ac64f630ff1d5181372a33c21fb4e67bde03588a 100644 (file)
@@ -5,7 +5,7 @@
 #include <common/stats.qh>
 #include <server/gamelog.qh>
 #include <server/miscfunctions.qh>
-#include "g_world.qh"
+#include "world.qh"
 #include "command/cmd.qh"
 #include "command/getreplies.qh"
 #include "../common/constants.qh"
index 76dcf849130a36d22e72a0b7087ee7d9d347357d..1a751984ddb07942236986aa5996f33f3e46513b 100644 (file)
@@ -4,15 +4,15 @@
 #include "command/common.qh"
 #include "client.qh"
 #include "constants.qh"
-#include "g_damage.qh"
-#include "g_hook.qh"
-#include "g_world.qh"
+#include "damage.qh"
+#include "hook.qh"
+#include "world.qh"
 #include <server/gamelog.qh>
 #include "ipban.qh"
 #include <server/items/items.qh>
 #include <server/mutators/_mod.qh>
 #include <server/spawnpoints.qh>
-#include <server/sv_main.qh>
+#include <server/main.qh>
 #include "mapvoting.qh"
 #include "resources.qh"
 #include <server/items/spawning.qh>
index 6d317a71c03af6c1c3602175b96941da32756e32..8a2406316cc9038549fc15f38fa303a40c349245 100644 (file)
@@ -3,7 +3,7 @@
 #include <common/weapons/_all.qh>
 #include <common/stats.qh>
 #include <server/client.qh>
-#include <server/g_world.qh>
+#include <server/world.qh>
 
 #include <server/items/items.qh>
 
index ba19f16a74c3d8b6c83d8e86797e295bc233e323..ef60454033748a5c25acbdb3a74cefc8f4cf3447 100644 (file)
@@ -5,13 +5,13 @@
 #include "cheats.qh"
 #include "client.qh"
 #include "clientkill.qh"
-#include "g_damage.qh"
-#include "g_world.qh"
+#include "damage.qh"
+#include "world.qh"
 #include "handicap.qh"
 #include "miscfunctions.qh"
 #include "portals.qh"
 #include "teamplay.qh"
-#include <server/sv_main.qh>
+#include <server/main.qh>
 #include "weapons/common.qh"
 #include "weapons/throwing.qh"
 #include "command/common.qh"
index 7edbc6f996b39d185806bb53c449bfe3d5734265..7abe6ae9786bd75941f6b927e214dacf80b6508e 100644 (file)
@@ -1,7 +1,7 @@
 #include "portals.qh"
 
 #include <common/effects/all.qh>
-#include "g_hook.qh"
+#include "hook.qh"
 #include "mutators/_mod.qh"
 #include <server/client.qh>
 #include <server/weapons/common.qh>
@@ -21,7 +21,7 @@
 #include "../common/vehicles/sv_vehicles.qh"
 #include <common/weapons/weapon/porto.qh>
 #include <server/player.qh>
-#include <server/g_damage.qh>
+#include <server/damage.qh>
 
 #define PORTALS_ARE_NOT_SOLID
 
index 7b99d916a5a6c443711f7196187f2678a86f0bfb..c2de3c9cc63e830a1859f7a16989a3e66d08c55e 100644 (file)
@@ -2,8 +2,8 @@
 
 #include <common/weapons/_all.qh>
 #include <common/stats.qh>
-#include <server/g_damage.qh>
-#include <server/g_world.qh>
+#include <server/damage.qh>
+#include <server/world.qh>
 #include <server/miscfunctions.qh>
 #include <server/weapons/common.qh>
 #include "client.qh"
index aab069cba9eda86431705111967ace4391eec20e..66da6c37c1fb154ec39cac5381e57be04e9978a3 100644 (file)
@@ -1,6 +1,6 @@
 #include "round_handler.qh"
 
-#include <server/g_world.qh>
+#include <server/world.qh>
 #include <server/miscfunctions.qh>
 #include "campaign.qh"
 #include "command/vote.qh"
index 18bb5f76cb50d6f91ecd67bcec11bb16445e0ff2..e4e1d236aba4c11690c06c50dc99a03bd9dcb0c1 100644 (file)
@@ -3,7 +3,7 @@
 #include "command/common.qh"
 #include <common/weapons/_all.qh>
 #include "client.qh"
-#include <server/g_world.qh>
+#include <server/world.qh>
 #include <server/miscfunctions.qh>
 #include <server/mutators/_mod.qh>
 #include <server/round_handler.qh>
index 9884ec75ea019eb0b651dd84a45c028826daee18..b629ef168449af6f5280a1fd5469ddacc2bc645b 100644 (file)
@@ -1,7 +1,7 @@
 #include "spawnpoints.qh"
 
 #include <server/mutators/_mod.qh>
-#include "g_world.qh"
+#include "world.qh"
 #include "miscfunctions.qh"
 #include "race.qh"
 #include <common/weapons/_all.qh>
diff --git a/qcsrc/server/sv_main.qc b/qcsrc/server/sv_main.qc
deleted file mode 100644 (file)
index 4969c51..0000000
+++ /dev/null
@@ -1,406 +0,0 @@
-#include "sv_main.qh"
-
-#include "anticheat.qh"
-#include "g_hook.qh"
-#include "g_damage.qh"
-#include "g_world.qh"
-#include "spawnpoints.qh"
-#include <server/gamelog.qh>
-
-#include "bot/api.qh"
-
-#include "command/common.qh"
-
-#include <server/mutators/_mod.qh>
-#include "weapons/csqcprojectile.qh"
-#include <server/weapons/common.qh>
-#include <server/compat/quake3.qh>
-
-#include "../common/constants.qh"
-#include "../common/deathtypes/all.qh"
-#include "../common/debug.qh"
-#include "../common/mapinfo.qh"
-#include "../common/util.qh"
-
-#include "../common/vehicles/all.qh"
-#include <common/monsters/sv_monsters.qh>
-#include <common/weapons/_all.qh>
-
-#include "../lib/csqcmodel/sv_model.qh"
-
-#include "../lib/warpzone/common.qh"
-#include "../lib/warpzone/server.qh"
-
-void CreatureFrame_hotliquids(entity this)
-{
-       if (this.contents_damagetime >= time)
-       {
-               return;
-       }
-
-       this.contents_damagetime = time + autocvar_g_balance_contents_damagerate;
-
-       if (this.flags & FL_PROJECTILE)
-       {
-               if (this.watertype == CONTENT_LAVA)
-                       Damage (this, NULL, NULL, autocvar_g_balance_contents_projectiledamage * autocvar_g_balance_contents_damagerate * this.waterlevel, DEATH_LAVA.m_id, DMG_NOWEP, this.origin, '0 0 0');
-               else if (this.watertype == CONTENT_SLIME)
-                       Damage (this, NULL, NULL, autocvar_g_balance_contents_projectiledamage * autocvar_g_balance_contents_damagerate * this.waterlevel, DEATH_SLIME.m_id, DMG_NOWEP, this.origin, '0 0 0');
-       }
-       else
-       {
-               if (STAT(FROZEN, this))
-               {
-                       if (this.watertype == CONTENT_LAVA)
-                               Damage(this, NULL, NULL, 10000, DEATH_LAVA.m_id, DMG_NOWEP, this.origin, '0 0 0');
-                       else if (this.watertype == CONTENT_SLIME)
-                               Damage(this, NULL, NULL, 10000, DEATH_SLIME.m_id, DMG_NOWEP, this.origin, '0 0 0');
-               }
-               else if (this.watertype == CONTENT_LAVA)
-               {
-                       if (this.watersound_finished < time)
-                       {
-                               this.watersound_finished = time + 0.5;
-                               sound (this, CH_PLAYER_SINGLE, SND_LAVA, VOL_BASE, ATTEN_NORM);
-                       }
-                       Damage (this, NULL, NULL, autocvar_g_balance_contents_playerdamage_lava * autocvar_g_balance_contents_damagerate * this.waterlevel, DEATH_LAVA.m_id, DMG_NOWEP, this.origin, '0 0 0');
-                       if(autocvar_g_balance_contents_playerdamage_lava_burn)
-                               Fire_AddDamage(this, NULL, autocvar_g_balance_contents_playerdamage_lava_burn * this.waterlevel, autocvar_g_balance_contents_playerdamage_lava_burn_time * this.waterlevel, DEATH_LAVA.m_id);
-               }
-               else if (this.watertype == CONTENT_SLIME)
-               {
-                       if (this.watersound_finished < time)
-                       {
-                               this.watersound_finished = time + 0.5;
-                               sound (this, CH_PLAYER_SINGLE, SND_SLIME, VOL_BASE, ATTEN_NORM);
-                       }
-                       Damage (this, NULL, NULL, autocvar_g_balance_contents_playerdamage_slime * autocvar_g_balance_contents_damagerate * this.waterlevel, DEATH_SLIME.m_id, DMG_NOWEP, this.origin, '0 0 0');
-               }
-       }
-}
-
-void CreatureFrame_Liquids(entity this)
-{
-       if (this.watertype <= CONTENT_WATER && this.waterlevel > 0) // workaround a retarded bug made by id software :P (yes, it's that old of a bug)
-       {
-               if (!(this.flags & FL_INWATER))
-               {
-                       this.flags |= FL_INWATER;
-                       this.contents_damagetime = 0;
-               }
-
-               CreatureFrame_hotliquids(this);
-       }
-       else
-       {
-               if (this.flags & FL_INWATER)
-               {
-                       // play leave water sound
-                       this.flags &= ~FL_INWATER;
-                       this.contents_damagetime = 0;
-               }
-       }
-}
-
-void CreatureFrame_FallDamage(entity this)
-{
-       if(IS_VEHICLE(this) || (this.flags & FL_PROJECTILE))
-               return; // vehicles and projectiles don't receive fall damage
-       if(!(this.velocity || this.oldvelocity))
-               return; // if the entity hasn't moved and isn't moving, then don't do anything
-
-       // check for falling damage
-       bool have_hook = false;
-       for(int slot = 0; slot < MAX_WEAPONSLOTS; ++slot)
-       {
-           .entity weaponentity = weaponentities[slot];
-           if(this.(weaponentity).hook && this.(weaponentity).hook.state)
-           {
-               have_hook = true;
-               break;
-           }
-       }
-       if(!have_hook)
-       {
-               float dm; // dm is the velocity DECREASE. Velocity INCREASE should never cause a sound or any damage.
-               if(autocvar_g_balance_falldamage_onlyvertical)
-                       dm = fabs(this.oldvelocity.z) - vlen(this.velocity);
-               else
-                       dm = vlen(this.oldvelocity) - vlen(this.velocity);
-               if (IS_DEAD(this))
-                       dm = (dm - autocvar_g_balance_falldamage_deadminspeed) * autocvar_g_balance_falldamage_factor;
-               else
-                       dm = min((dm - autocvar_g_balance_falldamage_minspeed) * autocvar_g_balance_falldamage_factor, autocvar_g_balance_falldamage_maxdamage);
-               if (dm > 0)
-               {
-                       tracebox(this.origin, this.mins, this.maxs, this.origin - '0 0 1', MOVE_NOMONSTERS, this);
-                       if (!(trace_dphitq3surfaceflags & Q3SURFACEFLAG_NODAMAGE))
-                               Damage (this, NULL, NULL, dm, DEATH_FALL.m_id, DMG_NOWEP, this.origin, '0 0 0');
-               }
-       }
-
-       if(autocvar_g_maxspeed > 0 && vdist(this.velocity, >, autocvar_g_maxspeed))
-               Damage (this, NULL, NULL, 100000, DEATH_SHOOTING_STAR.m_id, DMG_NOWEP, this.origin, '0 0 0');
-}
-
-void CreatureFrame_All()
-{
-       if(game_stopped || time < game_starttime)
-               return;
-
-       IL_EACH(g_damagedbycontents, it.damagedbycontents,
-       {
-               if (it.move_movetype == MOVETYPE_NOCLIP) continue;
-               CreatureFrame_Liquids(it);
-               CreatureFrame_FallDamage(it);
-               it.oldvelocity = it.velocity;
-       });
-}
-
-void Pause_TryPause(bool ispaused)
-{
-       int n = 0;
-       FOREACH_CLIENT(IS_PLAYER(it) && IS_REAL_CLIENT(it), {
-               if (PHYS_INPUT_BUTTON_CHAT(it) != ispaused) return;
-               ++n;
-       });
-       if (!n) return;
-       setpause(ispaused);
-}
-
-void SV_PausedTic(float elapsedtime)
-{
-       if (!server_is_dedicated) Pause_TryPause(false);
-}
-
-/*
-=============
-StartFrame
-
-Called before each frame by the server
-=============
-*/
-
-bool game_delay_last;
-
-bool autocvar_sv_autopause = false;
-void systems_update();
-void sys_phys_update(entity this, float dt);
-void StartFrame()
-{
-    // TODO: if move is more than 50ms, split it into two moves (this matches QWSV behavior and the client prediction)
-    IL_EACH(g_players, IS_FAKE_CLIENT(it), sys_phys_update(it, frametime));
-    IL_EACH(g_players, IS_FAKE_CLIENT(it), PlayerPreThink(it));
-
-       execute_next_frame();
-       if (autocvar_sv_autopause && !server_is_dedicated) Pause_TryPause(true);
-
-       delete_fn = remove_unsafely; // not during spawning!
-       serverprevtime = servertime;
-       servertime = time;
-       serverframetime = frametime;
-
-#ifdef PROFILING
-       if(time > client_cefc_accumulatortime + 1)
-       {
-               float t = client_cefc_accumulator / (time - client_cefc_accumulatortime);
-               int c_seeing = 0;
-               int c_seen = 0;
-               FOREACH_CLIENT(true, {
-                       if(IS_REAL_CLIENT(it))
-                               ++c_seeing;
-                       if(IS_PLAYER(it))
-                               ++c_seen;
-               });
-               LOG_INFO(
-                   "CEFC time: ", ftos(t * 1000), "ms; ",
-            "CEFC calls per second: ", ftos(c_seeing * (c_seen - 1) / t), "; ",
-            "CEFC 100% load at: ", ftos(solve_quadratic(t, -t, -1) * '0 1 0')
-        );
-               client_cefc_accumulatortime = time;
-               client_cefc_accumulator = 0;
-       }
-#endif
-
-       IL_EACH(g_projectiles, it.csqcprojectile_clientanimate, CSQCProjectile_Check(it));
-
-       if (RedirectionThink()) return;
-
-       UncustomizeEntitiesRun();
-       InitializeEntitiesRun();
-
-       WarpZone_StartFrame();
-
-       sys_frametime = autocvar_sys_ticrate * autocvar_slowmo;
-       if (sys_frametime <= 0) sys_frametime = 1.0 / 60.0; // somewhat safe fallback
-
-       if (timeout_status == TIMEOUT_LEADTIME) // just before the timeout (when timeout_status will be TIMEOUT_ACTIVE)
-               orig_slowmo = autocvar_slowmo; // slowmo will be restored after the timeout
-
-       // detect when the pre-game countdown (if any) has ended and the game has started
-       bool game_delay = (time < game_starttime);
-       if (autocvar_sv_eventlog && game_delay_last && !game_delay)
-               GameLogEcho(":startdelay_ended");
-       game_delay_last = game_delay;
-
-       CreatureFrame_All();
-       CheckRules_World();
-
-       if (warmup_stage && !game_stopped && warmup_limit > 0 && time >= warmup_limit) {
-               ReadyRestart();
-               return;
-       }
-
-       bot_serverframe();
-       anticheat_startframe();
-       MUTATOR_CALLHOOK(SV_StartFrame);
-
-       GlobalStats_updateglobal();
-    FOREACH_CLIENT(true, GlobalStats_update(it));
-    IL_EACH(g_players, IS_FAKE_CLIENT(it), PlayerPostThink(it));
-}
-
-.vector originjitter;
-.vector anglesjitter;
-.float anglejitter;
-.string gametypefilter;
-.string cvarfilter;
-
-/**
- * Evaluate an expression of the form: [+ | -]? [var[op]val | [op]var | val | var] ...
- * +: all must match. this is the default
- * -: one must NOT match
- *
- * var>x
- * var<x
- * var>=x
- * var<=x
- * var==x
- * var!=x
- * var===x
- * var!==x
- */
-bool expr_evaluate(string s)
-{
-    bool ret = false;
-    if (str2chr(s, 0) == '+') {
-        s = substring(s, 1, -1);
-    } else if (str2chr(s, 0) == '-') {
-        ret = true;
-        s = substring(s, 1, -1);
-    }
-    bool expr_fail = false;
-    for (int i = 0, n = tokenize_console(s); i < n; ++i) {
-        int o;
-        string k, v;
-        s = argv(i);
-        #define X(expr) \
-            if (expr) \
-                continue; \
-            expr_fail = true; \
-            break;
-
-        #define BINOP(op, len, expr) \
-            if ((o = strstrofs(s, op, 0)) >= 0) { \
-                k = substring(s, 0, o); \
-                v = substring(s, o + len, -1); \
-                X(expr); \
-            }
-        BINOP(">=", 2, cvar(k) >= stof(v));
-        BINOP("<=", 2, cvar(k) <= stof(v));
-        BINOP(">",  1, cvar(k) >  stof(v));
-        BINOP("<",  1, cvar(k) <  stof(v));
-        BINOP("==", 2, cvar(k) == stof(v));
-        BINOP("!=", 2, cvar(k) != stof(v));
-        BINOP("===", 3, cvar_string(k) == v);
-        BINOP("!==", 3, cvar_string(k) != v);
-        {
-            k = s;
-            bool b = true;
-            if (str2chr(k, 0) == '!') {
-                k = substring(s, 1, -1);
-                b = false;
-            }
-            float f = stof(k);
-            bool isnum = ftos(f) == k;
-            X(boolean(isnum ? f : cvar(k)) == b);
-        }
-        #undef BINOP
-        #undef X
-    }
-    if (!expr_fail) {
-        ret = !ret;
-    }
-    // now ret is true if we want to keep the item, and false if we want to get rid of it
-    return ret;
-}
-
-void SV_OnEntityPreSpawnFunction(entity this)
-{
-       if (this)
-       if (this.gametypefilter != "")
-       if (!isGametypeInFilter(MapInfo_LoadedGametype, teamplay, have_team_spawns, this.gametypefilter))
-       {
-               delete(this);
-               return;
-       }
-       if (this.cvarfilter != "" && !expr_evaluate(this.cvarfilter)) {
-               delete(this);
-               return;
-       }
-
-       if (DoesQ3ARemoveThisEntity(this)) {
-               delete(this);
-               return;
-       }
-
-       set_movetype(this, this.movetype);
-
-       if (this.monster_attack) {
-               IL_PUSH(g_monster_targets, this);
-    }
-
-       // support special -1 and -2 angle from radiant
-       if (this.angles == '0 -1 0') {
-               this.angles = '-90 0 0';
-       } else if (this.angles == '0 -2 0') {
-               this.angles = '+90 0 0';
-    }
-
-    #define X(out, in) MACRO_BEGIN \
-        if (in != 0) { out = out + (random() * 2 - 1) * in; } \
-    MACRO_END
-    X(this.origin.x, this.originjitter.x); X(this.origin.y, this.originjitter.y); X(this.origin.z, this.originjitter.z);
-    X(this.angles.x, this.anglesjitter.x); X(this.angles.y, this.anglesjitter.y); X(this.angles.z, this.anglesjitter.z);
-    X(this.angles.y, this.anglejitter);
-    #undef X
-
-       if (MUTATOR_CALLHOOK(OnEntityPreSpawn, this)) {
-               delete(this);
-               return;
-       }
-}
-
-void WarpZone_PostInitialize_Callback()
-{
-       // create waypoint links for warpzones
-       entity tracetest_ent = spawn();
-       setsize(tracetest_ent, PL_MIN_CONST, PL_MAX_CONST);
-       tracetest_ent.dphitcontentsmask = DPCONTENTS_SOLID | DPCONTENTS_BODY | DPCONTENTS_PLAYERCLIP | DPCONTENTS_BOTCLIP;
-       //for(entity e = warpzone_first; e; e = e.warpzone_next)
-       for(entity e = NULL; (e = find(e, classname, "trigger_warpzone")); )
-               waypoint_spawnforteleporter_wz(e, tracetest_ent);
-       delete(tracetest_ent);
-}
-
-/*
-==================
-main
-
-unused but required by the engine
-==================
-*/
-void main ()
-{
-
-}
diff --git a/qcsrc/server/sv_main.qh b/qcsrc/server/sv_main.qh
deleted file mode 100644 (file)
index dc3d80d..0000000
+++ /dev/null
@@ -1,28 +0,0 @@
-#pragma once
-
-bool expr_evaluate(string s);
-
-#ifdef PROFILING
-float client_cefc_accumulator;
-float client_cefc_accumulatortime;
-#endif
-
-float servertime, serverprevtime, serverframetime;
-
-.vector oldvelocity; // for fall damage
-
-.float watersound_finished;
-
-.bool iscreature;
-.float species;
-
-.float contents_damagetime;
-
-/*
-==================
-main
-
-unused but required by the engine
-==================
-*/
-void main ();
index ce41941c530e89e68605ad57ae5ca40887d0ee1c..1c436da9691b0d555225ef9453286dfab73b9212 100644 (file)
@@ -2,7 +2,7 @@
 
 #include "client.qh"
 #include <server/gamelog.qh>
-#include <server/g_damage.qh>
+#include <server/damage.qh>
 #include "race.qh"
 #include "scores.qh"
 #include "scores_rules.qh"
index 0b895ea37bc9778f2532f613734dc36f03831415..2ec4fc710433bfbd3785dc698b9785af8ba8013f 100644 (file)
@@ -2,7 +2,7 @@
 
 #include <server/client.qh>
 #include <server/mutators/_mod.qh>
-#include <server/g_damage.qh>
+#include <server/damage.qh>
 #include <common/constants.qh>
 #include <common/net_linked.qh>
 #include <server/player.qh>
index 4c9f78422e0872d21c0c1ae0867728fec354aee6..7cd56faa758925ee1c37ddce9459361aa3184409 100644 (file)
@@ -2,7 +2,7 @@
 
 #include <common/weapons/_all.qh>
 #include <common/stats.qh>
-#include <server/g_damage.qh>
+#include <server/damage.qh>
 #include <server/items/items.qh>
 #include <server/miscfunctions.qh>
 #include <common/constants.qh>
index eca09ebaa0d0a1255046d4cc62306e995b5ea680..0407ad79923966caaa7a5ea9acbc87e35d3f2e54 100644 (file)
@@ -3,7 +3,7 @@
 #include <server/client.qh>
 #include <common/weapons/_all.qh>
 #include <common/stats.qh>
-#include <server/g_world.qh>
+#include <server/world.qh>
 #include <server/miscfunctions.qh>
 #include "../antilag.qh"
 #include <common/weapons/_all.qh>
index 395bfb9b1c2ecc7d2cdebfdb5a06ba57c7b749ad..2408adbf140ef1f75458129a9c72441725fcdad5 100644 (file)
@@ -5,7 +5,7 @@
 #include <server/items/spawning.qh>
 #include <server/mutators/_mod.qh>
 #include <server/items/items.qh>
-#include "../g_damage.qh"
+#include "../damage.qh"
 #include <common/items/item.qh>
 #include <common/mapinfo.qh>
 #include <common/notifications/all.qh>
index 2d9972551d7832385cb2512f0b2fe9af988b03d0..f4de0e1bf15996ac12aed1a31e64652493d96822 100644 (file)
@@ -7,8 +7,8 @@
 #include "hitplot.qh"
 #include "weaponsystem.qh"
 
-#include "../g_damage.qh"
-#include <server/sv_main.qh>
+#include "../damage.qh"
+#include <server/main.qh>
 #include "../antilag.qh"
 
 #include <common/constants.qh>
index eadd6f4bc83017e6a675b78301ed59d3731468f9..552ab67310b929bb27606b76378519341c042741 100644 (file)
@@ -3,7 +3,7 @@
 #include <common/weapons/_all.qh>
 #include <common/stats.qh>
 #include <server/miscfunctions.qh>
-#include "../g_world.qh"
+#include "../world.qh"
 
 #include <common/weapons/_all.qh>
 
index 81b25e845f81879b46a9fac4118e9fac5ae5e3b7..7638813882d306ca3e67544c10290ed80231d866 100644 (file)
@@ -4,8 +4,8 @@
 
 #include "../command/common.qh"
 #include <server/client.qh>
-#include <server/g_damage.qh>
-#include <server/g_world.qh>
+#include <server/damage.qh>
+#include <server/world.qh>
 #include <server/items/items.qh>
 #include <server/mutators/_mod.qh>
 #include "../round_handler.qh"
diff --git a/qcsrc/server/world.qc b/qcsrc/server/world.qc
new file mode 100644 (file)
index 0000000..c9fec23
--- /dev/null
@@ -0,0 +1,2266 @@
+#include "world.qh"
+
+#include "anticheat.qh"
+#include "antilag.qh"
+#include "bot/api.qh"
+#include "campaign.qh"
+#include "cheats.qh"
+#include "client.qh"
+#include "command/common.qh"
+#include "command/getreplies.qh"
+#include "command/sv_cmd.qh"
+#include "command/vote.qh"
+#include "hook.qh"
+#include <server/gamelog.qh>
+#include <server/damage.qh>
+#include "ipban.qh"
+#include "mapvoting.qh"
+#include <server/mutators/_mod.qh>
+#include "race.qh"
+#include "scores.qh"
+#include "scores_rules.qh"
+#include "spawnpoints.qh"
+#include "teamplay.qh"
+#include "weapons/weaponstats.qh"
+#include <server/weapons/common.qh>
+#include "../common/constants.qh"
+#include <common/net_linked.qh>
+#include "../common/deathtypes/all.qh"
+#include <common/gamemodes/_mod.qh>
+#include "../common/gamemodes/sv_rules.qh"
+#include "../common/mapinfo.qh"
+#include "../common/monsters/_mod.qh"
+#include "../common/monsters/sv_monsters.qh"
+#include "../common/vehicles/all.qh"
+#include "../common/notifications/all.qh"
+#include "../common/physics/player.qh"
+#include "../common/playerstats.qh"
+#include "../common/stats.qh"
+#include "../common/teams.qh"
+#include <common/mapobjects/triggers.qh>
+#include "../common/mapobjects/trigger/secret.qh"
+#include "../common/mapobjects/target/music.qh"
+#include "../common/util.qh"
+#include "../common/items/_mod.qh"
+#include <common/weapons/_all.qh>
+#include "../common/state.qh"
+
+const float LATENCY_THINKRATE = 10;
+.float latency_sum;
+.float latency_cnt;
+.float latency_time;
+entity pingplreport;
+void PingPLReport_Think(entity this)
+{
+       float delta;
+       entity e;
+
+       delta = 3 / maxclients;
+       if(delta < sys_frametime)
+               delta = 0;
+       this.nextthink = time + delta;
+
+       e = edict_num(this.cnt + 1);
+       if(IS_CLIENT(e) && IS_REAL_CLIENT(e))
+       {
+               WriteHeader(MSG_BROADCAST, TE_CSQC_PINGPLREPORT);
+               WriteByte(MSG_BROADCAST, this.cnt);
+               WriteShort(MSG_BROADCAST, bound(1, CS(e).ping, 65535));
+               WriteByte(MSG_BROADCAST, min(ceil(CS(e).ping_packetloss * 255), 255));
+               WriteByte(MSG_BROADCAST, min(ceil(CS(e).ping_movementloss * 255), 255));
+
+               // record latency times for clients throughout the match so we can report it to playerstats
+               if(time > (CS(e).latency_time + LATENCY_THINKRATE))
+               {
+                       CS(e).latency_sum += CS(e).ping;
+                       CS(e).latency_cnt += 1;
+                       CS(e).latency_time = time;
+                       //print("sum: ", ftos(CS(e).latency_sum), ", cnt: ", ftos(CS(e).latency_cnt), ", avg: ", ftos(CS(e).latency_sum / CS(e).latency_cnt), ".\n");
+               }
+       }
+       else
+       {
+               WriteHeader(MSG_BROADCAST, TE_CSQC_PINGPLREPORT);
+               WriteByte(MSG_BROADCAST, this.cnt);
+               WriteShort(MSG_BROADCAST, 0);
+               WriteByte(MSG_BROADCAST, 0);
+               WriteByte(MSG_BROADCAST, 0);
+       }
+       this.cnt = (this.cnt + 1) % maxclients;
+}
+void PingPLReport_Spawn()
+{
+       pingplreport = new_pure(pingplreport);
+       setthink(pingplreport, PingPLReport_Think);
+       pingplreport.nextthink = time;
+}
+
+const float SPAWNFLAG_NO_WAYPOINTS_FOR_ITEMS = 1;
+string redirection_target;
+float world_initialized;
+
+void SetDefaultAlpha()
+{
+       if (!MUTATOR_CALLHOOK(SetDefaultAlpha))
+       {
+               default_player_alpha = autocvar_g_player_alpha;
+               if(default_player_alpha == 0)
+                       default_player_alpha = 1;
+               default_weapon_alpha = default_player_alpha;
+       }
+}
+
+void GotoFirstMap(entity this)
+{
+       float n;
+       if(autocvar__sv_init)
+       {
+               // cvar_set("_sv_init", "0");
+               // we do NOT set this to 0 any more, so someone "accidentally" changing
+               // to this "init" map on a dedicated server will cause no permanent
+               // harm
+               if(autocvar_g_maplist_shuffle)
+                       ShuffleMaplist();
+               n = tokenizebyseparator(autocvar_g_maplist, " ");
+               cvar_set("g_maplist_index", ftos(n - 1)); // jump to map 0 in GotoNextMap
+
+               MapInfo_Enumerate();
+               MapInfo_FilterGametype(MapInfo_CurrentGametype(), MapInfo_CurrentFeatures(), MapInfo_RequiredFlags(), MapInfo_ForbiddenFlags(), 0);
+
+               if(!DoNextMapOverride(1))
+                       GotoNextMap(1);
+
+               return;
+       }
+
+       if(time < 5)
+       {
+               this.nextthink = time;
+       }
+       else
+       {
+               this.nextthink = time + 1;
+               LOG_INFO("Waiting for _sv_init being set to 1 by initialization scripts...");
+       }
+}
+
+void cvar_changes_init()
+{
+       float h;
+       string k, v, d;
+       float n, i, adding, pureadding;
+
+       strfree(cvar_changes);
+       strfree(cvar_purechanges);
+       cvar_purechanges_count = 0;
+
+       h = buf_create();
+       buf_cvarlist(h, "", "_"); // exclude all _ cvars as they are temporary
+       n = buf_getsize(h);
+
+       adding = true;
+       pureadding = true;
+
+       for(i = 0; i < n; ++i)
+       {
+               k = bufstr_get(h, i);
+
+#define BADPREFIX(p) if(substring(k, 0, strlen(p)) == p) continue
+#define BADPRESUFFIX(p,s) if(substring(k, 0, strlen(p)) == p && substring(k, -strlen(s), -1) == s) continue
+#define BADCVAR(p) if(k == p) continue
+
+               // general excludes and namespaces for server admin used cvars
+               BADPREFIX("help_"); // PN's server has this listed as changed, let's not rat him out for THAT
+
+               // internal
+               BADPREFIX("csqc_");
+               BADPREFIX("cvar_check_");
+               BADCVAR("gamecfg");
+               BADCVAR("g_configversion");
+               BADCVAR("halflifebsp");
+               BADCVAR("sv_mapformat_is_quake2");
+               BADCVAR("sv_mapformat_is_quake3");
+               BADPREFIX("sv_world");
+
+               // client
+               BADPREFIX("chase_");
+               BADPREFIX("cl_");
+               BADPREFIX("con_");
+               BADPREFIX("scoreboard_");
+               BADPREFIX("g_campaign");
+               BADPREFIX("g_waypointsprite_");
+               BADPREFIX("gl_");
+               BADPREFIX("joy");
+               BADPREFIX("hud_");
+               BADPREFIX("m_");
+               BADPREFIX("menu_");
+               BADPREFIX("net_slist_");
+               BADPREFIX("r_");
+               BADPREFIX("sbar_");
+               BADPREFIX("scr_");
+               BADPREFIX("snd_");
+               BADPREFIX("show");
+               BADPREFIX("sensitivity");
+               BADPREFIX("userbind");
+               BADPREFIX("v_");
+               BADPREFIX("vid_");
+               BADPREFIX("crosshair");
+               BADCVAR("mod_q3bsp_lightmapmergepower");
+               BADCVAR("mod_q3bsp_nolightmaps");
+               BADCVAR("fov");
+               BADCVAR("mastervolume");
+               BADCVAR("volume");
+               BADCVAR("bgmvolume");
+               BADCVAR("in_pitch_min");
+               BADCVAR("in_pitch_max");
+
+               // private
+               BADCVAR("developer");
+               BADCVAR("log_dest_udp");
+               BADCVAR("net_address");
+               BADCVAR("net_address_ipv6");
+               BADCVAR("port");
+               BADCVAR("savedgamecfg");
+               BADCVAR("serverconfig");
+               BADCVAR("sv_autoscreenshot");
+               BADCVAR("sv_heartbeatperiod");
+               BADCVAR("sv_vote_master_password");
+               BADCVAR("sys_colortranslation");
+               BADCVAR("sys_specialcharactertranslation");
+               BADCVAR("timeformat");
+               BADCVAR("timestamps");
+               BADCVAR("g_require_stats");
+               BADPREFIX("developer_");
+               BADPREFIX("g_ban_");
+               BADPREFIX("g_banned_list");
+               BADPREFIX("g_require_stats_");
+               BADPREFIX("g_chat_flood_");
+               BADPREFIX("g_ghost_items");
+               BADPREFIX("g_playerstats_");
+               BADPREFIX("g_voice_flood_");
+               BADPREFIX("log_file");
+               BADPREFIX("quit_");
+               BADPREFIX("rcon_");
+               BADPREFIX("sv_allowdownloads");
+               BADPREFIX("sv_autodemo");
+               BADPREFIX("sv_curl_");
+               BADPREFIX("sv_eventlog");
+               BADPREFIX("sv_logscores_");
+               BADPREFIX("sv_master");
+               BADPREFIX("sv_weaponstats_");
+               BADPREFIX("sv_waypointsprite_");
+               BADCVAR("rescan_pending");
+
+               // these can contain player IDs, so better hide
+               BADPREFIX("g_forced_team_");
+               BADCVAR("sv_muteban_list");
+               BADCVAR("sv_voteban_list");
+               BADCVAR("sv_allow_customplayermodels_idlist");
+               BADCVAR("sv_allow_customplayermodels_speciallist");
+
+               // mapinfo
+               BADCVAR("fraglimit");
+               BADCVAR("g_arena");
+               BADCVAR("g_assault");
+               BADCVAR("g_ca");
+               BADCVAR("g_ca_teams");
+               BADCVAR("g_conquest");
+               BADCVAR("g_conquest_teams");
+               BADCVAR("g_ctf");
+               BADCVAR("g_cts");
+               BADCVAR("g_dotc");
+               BADCVAR("g_dm");
+               BADCVAR("g_domination");
+               BADCVAR("g_domination_default_teams");
+               BADCVAR("g_duel");
+               BADCVAR("g_duel_not_dm_maps");
+               BADCVAR("g_freezetag");
+               BADCVAR("g_freezetag_teams");
+               BADCVAR("g_invasion_teams");
+               BADCVAR("g_invasion_type");
+               BADCVAR("g_jailbreak");
+               BADCVAR("g_jailbreak_teams");
+               BADCVAR("g_keepaway");
+               BADCVAR("g_keyhunt");
+               BADCVAR("g_keyhunt_teams");
+               BADCVAR("g_lms");
+               BADCVAR("g_nexball");
+               BADCVAR("g_onslaught");
+               BADCVAR("g_race");
+               BADCVAR("g_race_laps_limit");
+               BADCVAR("g_race_qualifying_timelimit");
+               BADCVAR("g_race_qualifying_timelimit_override");
+               BADCVAR("g_runematch");
+               BADCVAR("g_shootfromeye");
+               BADCVAR("g_snafu");
+               BADCVAR("g_survival");
+               BADCVAR("g_survival_not_dm_maps");
+               BADCVAR("g_tdm");
+               BADCVAR("g_tdm_on_dm_maps");
+               BADCVAR("g_tdm_teams");
+               BADCVAR("g_vip");
+               BADCVAR("leadlimit");
+               BADCVAR("nextmap");
+               BADCVAR("teamplay");
+               BADCVAR("timelimit");
+               BADCVAR("g_mapinfo_settemp_acl");
+               BADCVAR("g_mapinfo_ignore_warnings");
+               BADCVAR("g_maplist_ignore_sizes");
+               BADCVAR("g_maplist_sizes_count_bots");
+
+               // long
+               BADCVAR("hostname");
+               BADCVAR("g_maplist");
+               BADCVAR("g_maplist_mostrecent");
+               BADCVAR("sv_motd");
+
+               v = cvar_string(k);
+               d = cvar_defstring(k);
+               if(v == d)
+                       continue;
+
+               if(adding)
+               {
+                       cvar_changes = strcat(cvar_changes, k, " \"", v, "\" // \"", d, "\"\n");
+                       if(strlen(cvar_changes) > 16384)
+                       {
+                               cvar_changes = "// too many settings have been changed to show them here\n";
+                               adding = 0;
+                       }
+               }
+
+               // now check if the changes are actually gameplay relevant
+
+               // does nothing gameplay relevant
+               BADCVAR("captureleadlimit_override");
+               BADCVAR("condump_stripcolors");
+               BADCVAR("gameversion");
+               BADCVAR("fs_gamedir");
+               BADCVAR("g_allow_oldvortexbeam");
+               BADCVAR("g_balance_kill_delay");
+               BADCVAR("g_buffs_pickup_anyway");
+               BADCVAR("g_buffs_randomize");
+               BADCVAR("g_buffs_randomize_teamplay");
+               BADCVAR("g_campcheck_distance");
+               BADCVAR("g_chatsounds");
+               BADCVAR("g_ca_point_leadlimit");
+               BADCVAR("g_ca_point_limit");
+               BADCVAR("g_ctf_captimerecord_always");
+               BADCVAR("g_ctf_flag_glowtrails");
+               BADCVAR("g_ctf_dynamiclights");
+               BADCVAR("g_ctf_flag_pickup_verbosename");
+               BADPRESUFFIX("g_ctf_flag_", "_model");
+               BADPRESUFFIX("g_ctf_flag_", "_skin");
+               BADCVAR("g_domination_point_leadlimit");
+               BADCVAR("g_forced_respawn");
+               BADCVAR("g_freezetag_point_leadlimit");
+               BADCVAR("g_freezetag_point_limit");
+               BADCVAR("g_glowtrails");
+               BADCVAR("g_hats");
+               BADCVAR("g_casings");
+               BADCVAR("g_invasion_point_limit");
+               BADCVAR("g_jump_grunt");
+               BADCVAR("g_keepaway_ballcarrier_effects");
+               BADCVAR("g_keepawayball_effects");
+               BADCVAR("g_keyhunt_point_leadlimit");
+               BADCVAR("g_nexball_goalleadlimit");
+               BADCVAR("g_new_toys_autoreplace");
+               BADCVAR("g_new_toys_use_pickupsound");
+               BADCVAR("g_physics_predictall");
+               BADCVAR("g_piggyback");
+               BADCVAR("g_playerclip_collisions");
+               BADCVAR("g_spawn_alloweffects");
+               BADCVAR("g_tdm_point_leadlimit");
+               BADCVAR("g_tdm_point_limit");
+               BADCVAR("leadlimit_and_fraglimit");
+               BADCVAR("leadlimit_override");
+               BADCVAR("pausable");
+               BADCVAR("sv_announcer");
+               BADCVAR("sv_checkforpacketsduringsleep");
+               BADCVAR("sv_damagetext");
+               BADCVAR("sv_db_saveasdump");
+               BADCVAR("sv_intermission_cdtrack");
+               BADCVAR("sv_mapchange_delay");
+               BADCVAR("sv_minigames");
+               BADCVAR("sv_namechangetimer");
+               BADCVAR("sv_precacheplayermodels");
+               BADCVAR("sv_radio");
+               BADCVAR("sv_stepheight");
+               BADCVAR("sv_timeout");
+               BADCVAR("sv_weapons_modeloverride");
+               BADCVAR("w_prop_interval");
+               BADPREFIX("chat_");
+               BADPREFIX("crypto_");
+               BADPREFIX("gameversion_");
+               BADPREFIX("g_chat_");
+               BADPREFIX("g_ctf_captimerecord_");
+               BADPREFIX("g_hats_");
+               BADPREFIX("g_maplist_");
+               BADPREFIX("g_mod_");
+               BADPREFIX("g_respawn_");
+               BADPREFIX("net_");
+               BADPREFIX("notification_");
+               BADPREFIX("prvm_");
+               BADPREFIX("skill_");
+               BADPREFIX("sv_allow_");
+               BADPREFIX("sv_cullentities_");
+               BADPREFIX("sv_maxidle_");
+               BADPREFIX("sv_minigames_");
+               BADPREFIX("sv_radio_");
+               BADPREFIX("sv_timeout_");
+               BADPREFIX("sv_vote_");
+               BADPREFIX("timelimit_");
+
+               // allowed changes to server admins (please sync this to server.cfg)
+               // vi commands:
+               //   :/"impure"/,$d
+               //   :g!,^\/\/[^ /],d
+               //   :%s,//\([^ ]*\).*,BADCVAR("\1");,
+               //   :%!sort
+               // yes, this does contain some redundant stuff, don't really care
+               BADPREFIX("bot_ai_");
+               BADCVAR("bot_config_file");
+               BADCVAR("bot_number");
+               BADCVAR("bot_prefix");
+               BADCVAR("bot_suffix");
+               BADCVAR("capturelimit_override");
+               BADCVAR("fraglimit_override");
+               BADCVAR("gametype");
+               BADCVAR("g_antilag");
+               BADCVAR("g_balance_teams");
+               BADCVAR("g_balance_teams_prevent_imbalance");
+               BADCVAR("g_balance_teams_scorefactor");
+               BADCVAR("g_ban_sync_trusted_servers");
+               BADCVAR("g_ban_sync_uri");
+               BADCVAR("g_buffs");
+               BADCVAR("g_ca_teams_override");
+               BADCVAR("g_ctf_fullbrightflags");
+               BADCVAR("g_ctf_ignore_frags");
+               BADCVAR("g_ctf_leaderboard");
+               BADCVAR("g_domination_point_limit");
+               BADCVAR("g_domination_teams_override");
+               BADCVAR("g_freezetag_teams_override");
+               BADCVAR("g_friendlyfire");
+               BADCVAR("g_fullbrightitems");
+               BADCVAR("g_fullbrightplayers");
+               BADCVAR("g_keyhunt_point_limit");
+               BADCVAR("g_keyhunt_teams_override");
+               BADCVAR("g_lms_lives_override");
+               BADCVAR("g_maplist");
+               BADCVAR("g_maxplayers");
+               BADCVAR("g_mirrordamage");
+               BADCVAR("g_nexball_goallimit");
+               BADCVAR("g_norecoil");
+               BADCVAR("g_physics_clientselect");
+               BADCVAR("g_pinata");
+               BADCVAR("g_powerups");
+               BADCVAR("g_player_brightness");
+               BADCVAR("g_rocket_flying");
+               BADCVAR("g_rocket_flying_disabledelays");
+               BADCVAR("g_spawnshieldtime");
+               BADCVAR("g_start_delay");
+               BADCVAR("g_superspectate");
+               BADCVAR("g_tdm_teams_override");
+               BADCVAR("g_warmup");
+               BADCVAR("g_weapon_stay"); BADPRESUFFIX("g_", "_weapon_stay");
+               BADCVAR("hostname");
+               BADCVAR("log_file");
+               BADCVAR("maxplayers");
+               BADCVAR("minplayers");
+               BADCVAR("minplayers_per_team");
+               BADCVAR("net_address");
+               BADCVAR("port");
+               BADCVAR("rcon_password");
+               BADCVAR("rcon_restricted_commands");
+               BADCVAR("rcon_restricted_password");
+               BADCVAR("skill");
+               BADCVAR("sv_adminnick");
+               BADCVAR("sv_autoscreenshot");
+               BADCVAR("sv_autotaunt");
+               BADCVAR("sv_curl_defaulturl");
+               BADCVAR("sv_defaultcharacter");
+               BADCVAR("sv_defaultcharacterskin");
+               BADCVAR("sv_defaultplayercolors");
+               BADCVAR("sv_defaultplayermodel");
+               BADCVAR("sv_defaultplayerskin");
+               BADCVAR("sv_maxidle");
+               BADCVAR("sv_maxrate");
+               BADCVAR("sv_motd");
+               BADCVAR("sv_public");
+               BADCVAR("sv_ready_restart");
+               BADCVAR("sv_status_privacy");
+               BADCVAR("sv_taunt");
+               BADCVAR("sv_vote_call");
+               BADCVAR("sv_vote_commands");
+               BADCVAR("sv_vote_majority_factor");
+               BADCVAR("sv_vote_master");
+               BADCVAR("sv_vote_master_commands");
+               BADCVAR("sv_vote_master_password");
+               BADCVAR("sv_vote_simple_majority_factor");
+               BADCVAR("teamplay_mode");
+               BADCVAR("timelimit_override");
+               BADPREFIX("g_warmup_");
+               BADPREFIX("sv_info_");
+               BADPREFIX("sv_ready_restart_");
+
+               // mutators that announce themselves properly to the server browser
+               BADCVAR("g_instagib");
+               BADCVAR("g_new_toys");
+               BADCVAR("g_nix");
+               BADCVAR("g_grappling_hook");
+               BADCVAR("g_jetpack");
+
+               // temporary for testing
+               // TODO remove before 0.8.3 release
+               BADCVAR("g_ca_weaponarena");
+               BADCVAR("g_freezetag_weaponarena");
+               BADCVAR("g_lms_weaponarena");
+               BADCVAR("g_ctf_stalemate_time");
+
+               if(cvar_string("g_mod_balance") == "Testing")
+               {
+                       // (temporary) while using the Testing balance, any weapon balance cvars are allowed to be changed
+                       BADPREFIX("g_balance_");
+               }
+
+#undef BADPRESUFFIX
+#undef BADPREFIX
+#undef BADCVAR
+
+               if(pureadding)
+               {
+                       cvar_purechanges = strcat(cvar_purechanges, k, " \"", v, "\" // \"", d, "\"\n");
+                       if(strlen(cvar_purechanges) > 16384)
+                       {
+                               cvar_purechanges = "// too many settings have been changed to show them here\n";
+                               pureadding = 0;
+                       }
+               }
+               ++cvar_purechanges_count;
+               // WARNING: this variable is used for the server list
+               // NEVER dare to skip this code!
+               // Hacks to intentionally appearing as "pure server" even though you DO have
+               // modified settings may be punished by removal from the server list.
+               // You can do to the variables cvar_changes and cvar_purechanges all you want,
+               // though.
+       }
+       buf_del(h);
+       if(cvar_changes == "")
+               cvar_changes = "// this server runs at default server settings\n";
+       else
+               cvar_changes = strcat("// this server runs at modified server settings:\n", cvar_changes);
+       cvar_changes = strzone(cvar_changes);
+       if(cvar_purechanges == "")
+               cvar_purechanges = "// this server runs at default gameplay settings\n";
+       else
+               cvar_purechanges = strcat("// this server runs at modified gameplay settings:\n", cvar_purechanges);
+       cvar_purechanges = strzone(cvar_purechanges);
+}
+
+entity randomseed;
+bool RandomSeed_Send(entity this, entity to, int sf)
+{
+       WriteHeader(MSG_ENTITY, ENT_CLIENT_RANDOMSEED);
+       WriteShort(MSG_ENTITY, this.cnt);
+       return true;
+}
+void RandomSeed_Think(entity this)
+{
+       this.cnt = bound(0, floor(random() * 65536), 65535);
+       this.nextthink = time + 5;
+
+       this.SendFlags |= 1;
+}
+void RandomSeed_Spawn()
+{
+       randomseed = new_pure(randomseed);
+       setthink(randomseed, RandomSeed_Think);
+       Net_LinkEntity(randomseed, false, 0, RandomSeed_Send);
+
+       getthink(randomseed)(randomseed); // sets random seed and nextthink
+}
+
+spawnfunc(__init_dedicated_server)
+{
+       // handler for _init/_init map (only for dedicated server initialization)
+
+       world_initialized = -1; // don't complain
+
+       delete_fn = remove_unsafely;
+
+       entity e = spawn();
+       setthink(e, GotoFirstMap);
+       e.nextthink = time; // this is usually 1 at this point
+
+       e = new(info_player_deathmatch);  // safeguard against player joining
+
+    // assign reflectively to avoid "assignment to world" warning
+    for (int i = 0, n = numentityfields(); i < n; ++i) {
+        string k = entityfieldname(i);
+        if (k == "classname") {
+            // safeguard against various stuff ;)
+            putentityfieldstring(i, this, "worldspawn");
+            break;
+        }
+    }
+
+       // needs to be done so early because of the constants they create
+       static_init();
+       static_init_late();
+       static_init_precache();
+
+       IL_PUSH(g_spawnpoints, e); // just incase
+
+       MapInfo_Enumerate();
+       MapInfo_FilterGametype(MapInfo_CurrentGametype(), MapInfo_CurrentFeatures(), MapInfo_RequiredFlags(), MapInfo_ForbiddenFlags(), 0);
+}
+
+void __init_dedicated_server_shutdown() {
+       MapInfo_Shutdown();
+}
+
+STATIC_INIT_EARLY(maxclients)
+{
+       maxclients = 0;
+       for (entity head = nextent(NULL); head; head = nextent(head)) {
+               ++maxclients;
+       }
+}
+
+void default_delayedinit(entity this)
+{
+       if(!scores_initialized)
+               ScoreRules_generic();
+}
+
+void InitGameplayMode()
+{
+       VoteReset();
+
+       // find out good world mins/maxs bounds, either the static bounds found by looking for solid, or the mapinfo specified bounds
+       get_mi_min_max(1);
+       // assign reflectively to avoid "assignment to world" warning
+       int done = 0; for (int i = 0, n = numentityfields(); i < n; ++i) {
+           string k = entityfieldname(i); vector v = (k == "mins") ? mi_min : (k == "maxs") ? mi_max : '0 0 0';
+           if (v) {
+            putentityfieldstring(i, world, sprintf("%v", v));
+            if (++done == 2) break;
+        }
+       }
+       // currently, NetRadiant's limit is 131072 qu for each side
+       // distance from one corner of a 131072qu cube to the opposite corner is approx. 227023 qu
+       // set the distance according to map size but don't go over the limit to avoid issues with float precision
+       // in case somebody makes extremely large maps
+       max_shot_distance = min(230000, vlen(world.maxs - world.mins));
+
+       MapInfo_LoadMapSettings(mapname);
+       GameRules_teams(false);
+
+       if (!cvar_value_issafe(world.fog))
+       {
+               LOG_INFO("The current map contains a potentially harmful fog setting, ignored");
+               world.fog = string_null;
+       }
+       if(MapInfo_Map_fog != "")
+       {
+               if(MapInfo_Map_fog == "none")
+                       world.fog = string_null;
+               else
+                       world.fog = strzone(MapInfo_Map_fog);
+       }
+       clientstuff = strzone(MapInfo_Map_clientstuff);
+
+       MapInfo_ClearTemps();
+
+       gamemode_name = MapInfo_Type_ToText(MapInfo_LoadedGametype);
+
+       cache_mutatormsg = strzone("");
+       cache_lastmutatormsg = strzone("");
+
+       InitializeEntity(NULL, default_delayedinit, INITPRIO_GAMETYPE_FALLBACK);
+}
+
+void Map_MarkAsRecent(string m);
+float world_already_spawned;
+spawnfunc(worldspawn)
+{
+       server_is_dedicated = boolean(stof(cvar_defstring("is_dedicated")));
+
+       bool wantrestart = false;
+       {
+               if (!server_is_dedicated)
+               {
+                       // force unloading of server pk3 files when starting a listen server
+                       // localcmd("\nfs_rescan\n"); // FIXME: does more harm than good, has unintended side effects. What we really want is to unload temporary pk3s only
+                       // restore csqc_progname too
+                       string expect = "csprogs.dat";
+                       wantrestart = cvar_string("csqc_progname") != expect;
+                       cvar_set("csqc_progname", expect);
+               }
+               else
+               {
+                       // Try to use versioned csprogs from pk3
+                       // Only ever use versioned csprogs.dat files on dedicated servers;
+                       // we need to reset csqc_progname on clients ourselves, and it's easier if the client's release name is constant
+                       string pk3csprogs = "csprogs-" WATERMARK ".dat";
+                       // This always works; fall back to it if a versioned csprogs.dat is suddenly missing
+                       string select = "csprogs.dat";
+                       if (fexists(pk3csprogs)) select = pk3csprogs;
+                       if (cvar_string("csqc_progname") != select)
+                       {
+                               cvar_set("csqc_progname", select);
+                               wantrestart = true;
+                       }
+                       // Check for updates on startup
+                       // We do it this way for atomicity so that connecting clients still match the server progs and don't disconnect
+                       int sentinel = fopen("progs.txt", FILE_READ);
+                       if (sentinel >= 0)
+                       {
+                               string switchversion = fgets(sentinel);
+                               fclose(sentinel);
+                               if (switchversion != "" && switchversion != WATERMARK)
+                               {
+                                       LOG_INFOF("Switching progs: " WATERMARK " -> %s", switchversion);
+                                       // if it doesn't exist, assume either:
+                                       //   a) the current program was overwritten
+                                       //   b) this is a client only update
+                                       string newprogs = sprintf("progs-%s.dat", switchversion);
+                                       if (fexists(newprogs))
+                                       {
+                                               cvar_set("sv_progs", newprogs);
+                                               wantrestart = true;
+                                       }
+                                       string newcsprogs = sprintf("csprogs-%s.dat", switchversion);
+                                       if (fexists(newcsprogs))
+                                       {
+                                               cvar_set("csqc_progname", newcsprogs);
+                                               wantrestart = true;
+                                       }
+                               }
+                       }
+               }
+               if (wantrestart)
+               {
+                       LOG_INFO("Restart requested");
+                       changelevel(mapname);
+                       // let initialization continue, shutdown depends on it
+               }
+       }
+
+       if(world_already_spawned)
+               error("world already spawned - you may have EXACTLY ONE worldspawn!");
+       world_already_spawned = true;
+
+       delete_fn = remove_safely; // during spawning, watch what you remove!
+
+       cvar_changes_init(); // do this very early now so it REALLY matches the server config
+
+       // needs to be done so early because of the constants they create
+       static_init();
+
+       ServerProgsDB = db_load(strcat("server.db", autocvar_sessionid));
+
+       TemporaryDB = db_create();
+
+       // 0 normal
+       lightstyle(0, "m");
+
+       // 1 FLICKER (first variety)
+       lightstyle(1, "mmnmmommommnonmmonqnmmo");
+
+       // 2 SLOW STRONG PULSE
+       lightstyle(2, "abcdefghijklmnopqrstuvwxyzyxwvutsrqponmlkjihgfedcba");
+
+       // 3 CANDLE (first variety)
+       lightstyle(3, "mmmmmaaaaammmmmaaaaaabcdefgabcdefg");
+
+       // 4 FAST STROBE
+       lightstyle(4, "mamamamamama");
+
+       // 5 GENTLE PULSE 1
+       lightstyle(5,"jklmnopqrstuvwxyzyxwvutsrqponmlkj");
+
+       // 6 FLICKER (second variety)
+       lightstyle(6, "nmonqnmomnmomomno");
+
+       // 7 CANDLE (second variety)
+       lightstyle(7, "mmmaaaabcdefgmmmmaaaammmaamm");
+
+       // 8 CANDLE (third variety)
+       lightstyle(8, "mmmaaammmaaammmabcdefaaaammmmabcdefmmmaaaa");
+
+       // 9 SLOW STROBE (fourth variety)
+       lightstyle(9, "aaaaaaaazzzzzzzz");
+
+       // 10 FLUORESCENT FLICKER
+       lightstyle(10, "mmamammmmammamamaaamammma");
+
+       // 11 SLOW PULSE NOT FADE TO BLACK
+       lightstyle(11, "abcdefghijklmnopqrrqponmlkjihgfedcba");
+
+       // styles 32-62 are assigned by the spawnfunc_light program for switchable lights
+
+       // 63 testing
+       lightstyle(63, "a");
+
+       if(autocvar_g_campaign)
+               CampaignPreInit();
+
+       Map_MarkAsRecent(mapname);
+
+       PlayerStats_GameReport_Init(); // we need this to be initiated before InitGameplayMode
+
+       InitGameplayMode();
+       static_init_late();
+       static_init_precache();
+       readlevelcvars();
+       GrappleHookInit();
+
+       GameRules_limit_fallbacks();
+
+       if(warmup_limit == 0)
+               warmup_limit = (autocvar_timelimit > 0) ? autocvar_timelimit * 60 : autocvar_timelimit;
+
+       player_count = 0;
+       bot_waypoints_for_items = autocvar_g_waypoints_for_items;
+       if(bot_waypoints_for_items == 1)
+               if(this.spawnflags & SPAWNFLAG_NO_WAYPOINTS_FOR_ITEMS)
+                       bot_waypoints_for_items = 0;
+
+       WaypointSprite_Init();
+
+       GameLogInit(); // prepare everything
+       // NOTE for matchid:
+       // changing the logic generating it is okay. But:
+       // it HAS to stay <= 64 chars
+       // character set: ASCII 33-126 without the following characters: : ; ' " \ $
+       if(autocvar_sv_eventlog)
+       {
+               string s = sprintf("%s.%s.%06d", itos(autocvar_sv_eventlog_files_counter), strftime(false, "%s"), floor(random() * 1000000));
+               matchid = strzone(s);
+
+               GameLogEcho(strcat(":gamestart:", GetGametype(), "_", GetMapname(), ":", s));
+               s = ":gameinfo:mutators:LIST";
+
+               MUTATOR_CALLHOOK(BuildMutatorsString, s);
+               s = M_ARGV(0, string);
+
+               // initialiation stuff, not good in the mutator system
+               if(!autocvar_g_use_ammunition)
+                       s = strcat(s, ":no_use_ammunition");
+
+               // initialiation stuff, not good in the mutator system
+               if(autocvar_g_pickup_items == 0)
+                       s = strcat(s, ":no_pickup_items");
+               if(autocvar_g_pickup_items > 0)
+                       s = strcat(s, ":pickup_items");
+
+               // initialiation stuff, not good in the mutator system
+               if(autocvar_g_weaponarena != "0")
+                       s = strcat(s, ":", autocvar_g_weaponarena, " arena");
+
+               // TODO to mutator system
+               if(autocvar_g_norecoil)
+                       s = strcat(s, ":norecoil");
+
+               // TODO to mutator system
+               if(autocvar_g_powerups == 0)
+                       s = strcat(s, ":no_powerups");
+               if(autocvar_g_powerups > 0)
+                       s = strcat(s, ":powerups");
+
+               GameLogEcho(s);
+               GameLogEcho(":gameinfo:end");
+       }
+       else
+               matchid = strzone(ftos(random()));
+
+       cvar_set("nextmap", "");
+
+       SetDefaultAlpha();
+
+       if(autocvar_g_campaign)
+               CampaignPostInit();
+
+       Ban_LoadBans();
+
+       MapInfo_Enumerate();
+       MapInfo_FilterGametype(MapInfo_CurrentGametype(), MapInfo_CurrentFeatures(), MapInfo_RequiredFlags(), MapInfo_ForbiddenFlags(), 1);
+
+       if(fexists(strcat("scripts/", mapname, ".arena")))
+               cvar_settemp("sv_q3acompat_machineshotgunswap", "1");
+
+       if(fexists(strcat("scripts/", mapname, ".defi")))
+               cvar_settemp("sv_q3defragcompat", "1");
+
+       if(whichpack(strcat("maps/", mapname, ".cfg")) != "")
+       {
+               int fd = fopen(strcat("maps/", mapname, ".cfg"), FILE_READ);
+               if(fd != -1)
+               {
+                       string s;
+                       while((s = fgets(fd)))
+                       {
+                               int l = tokenize_console(s);
+                               if(l < 2)
+                                       continue;
+                               if(argv(0) == "cd")
+                               {
+                                       string trackname = argv(2);
+                                       LOG_INFO("Found ^1UNSUPPORTED^7 cd loop command in .cfg file; put this line in mapinfo instead:");
+                                       LOG_INFO("  cdtrack ", trackname);
+                                       if (cvar_value_issafe(trackname))
+                                       {
+                                               string newstuff = strcat(clientstuff, "cd loop \"", trackname, "\"\n");
+                                               strcpy(clientstuff, newstuff);
+                                       }
+                               }
+                               else if(argv(0) == "fog")
+                               {
+                                       LOG_INFO("Found ^1UNSUPPORTED^7 fog command in .cfg file; put this line in worldspawn in the .map/.bsp/.ent file instead:");
+                                       LOG_INFO("  \"fog\" \"", s, "\"");
+                               }
+                               else if(argv(0) == "set")
+                               {
+                                       LOG_INFO("Found ^1UNSUPPORTED^7 set command in .cfg file; put this line in mapinfo instead:");
+                                       LOG_INFO("  clientsettemp_for_type all ", argv(1), " ", argv(2));
+                               }
+                               else if(argv(0) != "//")
+                               {
+                                       LOG_INFO("Found ^1UNSUPPORTED^7 set command in .cfg file; put this line in mapinfo instead:");
+                                       LOG_INFO("  clientsettemp_for_type all ", argv(0), " ", argv(1));
+                               }
+                       }
+                       fclose(fd);
+               }
+       }
+
+       WeaponStats_Init();
+
+       Nagger_Init();
+
+       // set up information replies for clients and server to use
+       maplist_reply = strzone(getmaplist());
+       lsmaps_reply = strzone(getlsmaps());
+       monsterlist_reply = strzone(getmonsterlist());
+       for(int i = 0; i < 10; ++i)
+       {
+               string s = getrecords(i);
+               if (s)
+                       records_reply[i] = strzone(s);
+       }
+       ladder_reply = strzone(getladder());
+       rankings_reply = strzone(getrankings());
+
+       // begin other init
+       ClientInit_Spawn();
+       RandomSeed_Spawn();
+       PingPLReport_Spawn();
+
+       CheatInit();
+
+       if (!wantrestart) localcmd("\n_sv_hook_gamestart ", GetGametype(), "\n");
+
+       // fill sv_curl_serverpackages from .serverpackage files
+       if (autocvar_sv_curl_serverpackages_auto)
+       {
+               string s = "csprogs-" WATERMARK ".txt";
+               // remove automatically managed files from the list to prevent duplicates
+               for (int i = 0, n = tokenize_console(cvar_string("sv_curl_serverpackages")); i < n; ++i)
+               {
+                       string pkg = argv(i);
+                       if (startsWith(pkg, "csprogs-")) continue;
+                       if (endsWith(pkg, "-serverpackage.txt")) continue;
+                       if (endsWith(pkg, ".serverpackage")) continue;  // OLD legacy
+                       s = cons(s, pkg);
+               }
+               // add automatically managed files to the list
+               #define X(match) MACRO_BEGIN \
+                       int fd = search_begin(match, true, false); \
+                       if (fd >= 0) \
+                       { \
+                               for (int i = 0, j = search_getsize(fd); i < j; ++i) \
+                               { \
+                                       s = cons(s, search_getfilename(fd, i)); \
+                               } \
+                               search_end(fd); \
+                       } \
+               MACRO_END
+               X("*-serverpackage.txt");
+               X("*.serverpackage");
+               #undef X
+               cvar_set("sv_curl_serverpackages", s);
+       }
+
+       // MOD AUTHORS: change this, and possibly remove a few of the blocks below to ignore certain changes
+       modname = "Xonotic";
+       // physics/balance/config changes that count as mod
+       if(cvar_string("g_mod_physics") != cvar_defstring("g_mod_physics"))
+               modname = cvar_string("g_mod_physics");
+       if(cvar_string("g_mod_balance") != cvar_defstring("g_mod_balance") && cvar_string("g_mod_balance") != "Testing")
+               modname = cvar_string("g_mod_balance");
+       if(cvar_string("g_mod_config") != cvar_defstring("g_mod_config"))
+               modname = cvar_string("g_mod_config");
+       // extra mutators that deserve to count as mod
+       MUTATOR_CALLHOOK(SetModname, modname);
+       modname = M_ARGV(0, string);
+
+       // save it for later
+       modname = strzone(modname);
+
+       WinningConditionHelper(this); // set worldstatus
+
+       world_initialized = 1;
+       __spawnfunc_spawn_all();
+}
+
+spawnfunc(light)
+{
+       //makestatic (this); // Who the f___ did that?
+       delete(this);
+}
+
+string GetGametype()
+{
+       return MapInfo_Type_ToString(MapInfo_LoadedGametype);
+}
+
+string GetMapname()
+{
+       return mapname;
+}
+
+float Map_Count, Map_Current;
+string Map_Current_Name;
+
+// NOTE: this now expects the map list to be already tokenized and the count in Map_Count
+int GetMaplistPosition()
+{
+       string map = GetMapname();
+       int idx = autocvar_g_maplist_index;
+
+       if(idx >= 0)
+       {
+               if(idx < Map_Count)
+               {
+                       if(map == argv(idx))
+                       {
+                               return idx;
+                       }
+               }
+       }
+
+       for(int pos = 0; pos < Map_Count; ++pos)
+       {
+               if(map == argv(pos))
+                       return pos;
+       }
+
+       // resume normal maplist rotation if current map is not in g_maplist
+       return idx;
+}
+
+bool MapHasRightSize(string map)
+{
+       int minplayers = max(0, floor(autocvar_minplayers));
+       if (teamplay)
+               minplayers = max(0, floor(autocvar_minplayers_per_team) * AvailableTeams());
+       if (autocvar_g_maplist_check_waypoints
+               && (currentbots || autocvar_bot_number || player_count < minplayers))
+       {
+               string checkwp_msg = strcat("checkwp ", map);
+               if(!fexists(strcat("maps/", map, ".waypoints")))
+               {
+                       LOG_TRACE(checkwp_msg, ": no waypoints");
+                       return false;
+               }
+               LOG_TRACE(checkwp_msg, ": has waypoints");
+       }
+
+       if(autocvar_g_maplist_ignore_sizes)
+               return true;
+
+       // open map size restriction file
+       string opensize_msg = strcat("opensize ", map);
+       float fh = fopen(strcat("maps/", map, ".sizes"), FILE_READ);
+       int player_limit = ((autocvar_g_maplist_sizes_count_maxplayers) ? GetPlayerLimit() : 0);
+       int pcount = ((player_limit > 0) ? min(player_count, player_limit) : player_count); // bind it to the player limit so that forced spectators don't influence the limits
+       if(!autocvar_g_maplist_sizes_count_bots)
+               pcount -= currentbots;
+       if(fh >= 0)
+       {
+               opensize_msg = strcat(opensize_msg, ": ok, ");
+               int mapmin = stoi(fgets(fh));
+               int mapmax = stoi(fgets(fh));
+               fclose(fh);
+               if(pcount < mapmin)
+               {
+                       LOG_TRACE(opensize_msg, "not enough");
+                       return false;
+               }
+               if(mapmax && pcount > mapmax)
+               {
+                       LOG_TRACE(opensize_msg, "too many");
+                       return false;
+               }
+               LOG_TRACE(opensize_msg, "right size");
+               return true;
+       }
+       LOG_TRACE(opensize_msg, ": not found");
+       return true;
+}
+
+string Map_Filename(float position)
+{
+       return strcat("maps/", argv(position), ".bsp");
+}
+
+void Map_MarkAsRecent(string m)
+{
+       cvar_set("g_maplist_mostrecent", strwords(cons(m, autocvar_g_maplist_mostrecent), max(0, autocvar_g_maplist_mostrecent_count)));
+}
+
+float Map_IsRecent(string m)
+{
+       return strhasword(autocvar_g_maplist_mostrecent, m);
+}
+
+float Map_Check(float position, float pass)
+{
+       string filename;
+       string map_next;
+       map_next = argv(position);
+       if(pass <= 1)
+       {
+               if(Map_IsRecent(map_next))
+                       return 0;
+       }
+       filename = Map_Filename(position);
+       if(MapInfo_CheckMap(map_next))
+       {
+               if(pass == 2)
+                       return 1;
+               if(MapHasRightSize(map_next))
+                       return 1;
+               return 0;
+       }
+       else
+               LOG_DEBUG( "Couldn't select '", filename, "'..." );
+
+       return 0;
+}
+
+void Map_Goto_SetStr(string nextmapname)
+{
+       if(getmapname_stored != "")
+               strunzone(getmapname_stored);
+       if(nextmapname == "")
+               getmapname_stored = "";
+       else
+               getmapname_stored = strzone(nextmapname);
+}
+
+void Map_Goto_SetFloat(float position)
+{
+       cvar_set("g_maplist_index", ftos(position));
+       Map_Goto_SetStr(argv(position));
+}
+
+void Map_Goto(float reinit)
+{
+       MapInfo_LoadMap(getmapname_stored, reinit);
+}
+
+// return codes of map selectors:
+//   -1 = temporary failure (that is, try some method that is guaranteed to succeed)
+//   -2 = permanent failure
+float MaplistMethod_Iterate() // usual method
+{
+       float pass, i;
+
+       LOG_TRACE("Trying MaplistMethod_Iterate");
+
+       for(pass = 1; pass <= 2; ++pass)
+       {
+               for(i = 1; i < Map_Count; ++i)
+               {
+                       float mapindex;
+                       mapindex = (i + Map_Current) % Map_Count;
+                       if(Map_Check(mapindex, pass))
+                               return mapindex;
+               }
+       }
+       return -1;
+}
+
+float MaplistMethod_Repeat() // fallback method
+{
+       LOG_TRACE("Trying MaplistMethod_Repeat");
+
+       if(Map_Check(Map_Current, 2))
+               return Map_Current;
+       return -2;
+}
+
+float MaplistMethod_Random() // random map selection
+{
+       float i, imax;
+
+       LOG_TRACE("Trying MaplistMethod_Random");
+
+       imax = 42;
+
+       for(i = 0; i <= imax; ++i)
+       {
+               float mapindex;
+               mapindex = (Map_Current + floor(random() * (Map_Count - 1) + 1)) % Map_Count; // any OTHER map
+               if(Map_Check(mapindex, 1))
+                       return mapindex;
+       }
+       return -1;
+}
+
+float MaplistMethod_Shuffle(float exponent) // more clever shuffling
+// the exponent sets a bias on the map selection:
+// the higher the exponent, the less likely "shortly repeated" same maps are
+{
+       float i, j, imax, insertpos;
+
+       LOG_TRACE("Trying MaplistMethod_Shuffle");
+
+       imax = 42;
+
+       for(i = 0; i <= imax; ++i)
+       {
+               string newlist;
+
+               // now reinsert this at another position
+               insertpos = (random() ** (1 / exponent));       // ]0, 1]
+               insertpos = insertpos * (Map_Count - 1);       // ]0, Map_Count - 1]
+               insertpos = ceil(insertpos) + 1;               // {2, 3, 4, ..., Map_Count}
+               LOG_TRACE("SHUFFLE: insert pos = ", ftos(insertpos));
+
+               // insert the current map there
+               newlist = "";
+               for(j = 1; j < insertpos; ++j)                 // i == 1: no loop, will be inserted as first; however, i == 1 has been excluded above
+                       newlist = strcat(newlist, " ", argv(j));
+               newlist = strcat(newlist, " ", argv(0));       // now insert the just selected map
+               for(j = insertpos; j < Map_Count; ++j)         // i == Map_Count: no loop, has just been inserted as last
+                       newlist = strcat(newlist, " ", argv(j));
+               newlist = substring(newlist, 1, strlen(newlist) - 1);
+               cvar_set("g_maplist", newlist);
+               Map_Count = tokenizebyseparator(autocvar_g_maplist, " ");
+
+               // NOTE: the selected map has just been inserted at (insertpos-1)th position
+               Map_Current = insertpos - 1; // this is not really valid, but this way the fallback has a chance of working
+               if(Map_Check(Map_Current, 1))
+                       return Map_Current;
+       }
+       return -1;
+}
+
+void Maplist_Init()
+{
+       float i = Map_Count = 0;
+       if(autocvar_g_maplist != "")
+       {
+               Map_Count = tokenizebyseparator(autocvar_g_maplist, " ");
+               for (i = 0; i < Map_Count; ++i)
+               {
+                       if (Map_Check(i, 2))
+                               break;
+               }
+       }
+
+       if (i == Map_Count)
+       {
+               bprint( "Maplist contains no usable maps!  Resetting it to default map list.\n" );
+               cvar_set("g_maplist", MapInfo_ListAllAllowedMaps(MapInfo_RequiredFlags(), MapInfo_ForbiddenFlags() | MAPINFO_FLAG_NOAUTOMAPLIST));
+               if(autocvar_g_maplist_shuffle)
+                       ShuffleMaplist();
+               if(!server_is_dedicated)
+                       localcmd("\nmenu_cmd sync\n");
+               Map_Count = tokenizebyseparator(autocvar_g_maplist, " ");
+       }
+       if(Map_Count == 0)
+               error("empty maplist, cannot select a new map");
+       Map_Current = bound(0, GetMaplistPosition(), Map_Count - 1);
+
+       strcpy(Map_Current_Name, argv(Map_Current)); // will be automatically freed on exit thanks to DP
+       // this may or may not be correct, but who cares, in the worst case a map
+       // isn't chosen in the first pass that should have been
+}
+
+string GetNextMap()
+{
+       Maplist_Init();
+       float nextMap = -1;
+
+       if(nextMap == -1)
+               if(autocvar_g_maplist_shuffle > 0)
+                       nextMap = MaplistMethod_Shuffle(autocvar_g_maplist_shuffle + 1);
+
+       if(nextMap == -1)
+               if(autocvar_g_maplist_selectrandom)
+                       nextMap = MaplistMethod_Random();
+
+       if(nextMap == -1)
+               nextMap = MaplistMethod_Iterate();
+
+       if(nextMap == -1)
+               nextMap = MaplistMethod_Repeat();
+
+       if(nextMap >= 0)
+       {
+               Map_Goto_SetFloat(nextMap);
+               return getmapname_stored;
+       }
+
+       return "";
+}
+
+float DoNextMapOverride(float reinit)
+{
+       if(autocvar_g_campaign)
+       {
+               CampaignPostIntermission();
+               alreadychangedlevel = true;
+               return true;
+       }
+       if(autocvar_quit_when_empty)
+       {
+               if(player_count <= currentbots)
+               {
+                       localcmd("quit\n");
+                       alreadychangedlevel = true;
+                       return true;
+               }
+       }
+       if(autocvar_quit_and_redirect != "")
+       {
+               redirection_target = strzone(autocvar_quit_and_redirect);
+               alreadychangedlevel = true;
+               return true;
+       }
+       if (!reinit && autocvar_samelevel) // if samelevel is set, stay on same level
+       {
+               localcmd("restart\n");
+               alreadychangedlevel = true;
+               return true;
+       }
+       if(autocvar_nextmap != "")
+       {
+               string m;
+               m = GameTypeVote_MapInfo_FixName(autocvar_nextmap);
+               cvar_set("nextmap",m);
+
+               if(!m || gametypevote)
+                       return false;
+               if(autocvar_sv_vote_gametype)
+               {
+                       Map_Goto_SetStr(m);
+                       return false;
+               }
+
+               if(MapInfo_CheckMap(m))
+               {
+                       Map_Goto_SetStr(m);
+                       Map_Goto(reinit);
+                       alreadychangedlevel = true;
+                       return true;
+               }
+       }
+       if(!reinit && autocvar_lastlevel)
+       {
+               cvar_settemp_restore();
+               localcmd("set lastlevel 0\ntogglemenu 1\n");
+               alreadychangedlevel = true;
+               return true;
+       }
+       return false;
+}
+
+void GotoNextMap(float reinit)
+{
+       //string nextmap;
+       //float n, nummaps;
+       //string s;
+       if (alreadychangedlevel)
+               return;
+       alreadychangedlevel = true;
+
+       string nextMap = GetNextMap();
+       if(nextMap == "")
+               error("Everything is broken - cannot find a next map. Please report this to the developers.");
+       Map_Goto(reinit);
+}
+
+
+/*
+============
+IntermissionThink
+
+When the player presses attack or jump, change to the next level
+============
+*/
+.float autoscreenshot;
+void IntermissionThink(entity this)
+{
+       FixIntermissionClient(this);
+
+       float server_screenshot = (autocvar_sv_autoscreenshot && CS(this).cvar_cl_autoscreenshot);
+       float client_screenshot = (CS(this).cvar_cl_autoscreenshot == 2);
+
+       if( (server_screenshot || client_screenshot)
+               && ((this.autoscreenshot > 0) && (time > this.autoscreenshot)) )
+       {
+               this.autoscreenshot = -1;
+               if(IS_REAL_CLIENT(this)) { stuffcmd(this, sprintf("\nscreenshot screenshots/autoscreenshot/%s-%s.jpg; echo \"^5A screenshot has been taken at request of the server.\"\n", GetMapname(), strftime(false, "%s"))); }
+               return;
+       }
+
+       if (time < intermission_exittime)
+               return;
+
+       if(!mapvote_initialized)
+               if (time < intermission_exittime + 10 && !(PHYS_INPUT_BUTTON_ATCK(this) || PHYS_INPUT_BUTTON_JUMP(this) || PHYS_INPUT_BUTTON_ATCK2(this) || PHYS_INPUT_BUTTON_HOOK(this) || PHYS_INPUT_BUTTON_USE(this)))
+                       return;
+
+       MapVote_Start();
+}
+
+/*
+===============================================================================
+
+RULES
+
+===============================================================================
+*/
+
+void DumpStats(float final)
+{
+       float file;
+       string s;
+       float to_console;
+       float to_eventlog;
+       float to_file;
+       float i;
+
+       to_console = autocvar_sv_logscores_console;
+       to_eventlog = autocvar_sv_eventlog;
+       to_file = autocvar_sv_logscores_file;
+
+       if(!final)
+       {
+               to_console = true; // always print printstats replies
+               to_eventlog = false; // but never print them to the event log
+       }
+
+       if(to_eventlog)
+               if(autocvar_sv_eventlog_console)
+                       to_console = false; // otherwise we get the output twice
+
+       if(final)
+               s = ":scores:";
+       else
+               s = ":status:";
+       s = strcat(s, GetGametype(), "_", GetMapname(), ":", ftos(rint(time)));
+
+       if(to_console)
+               LOG_INFO(s);
+       if(to_eventlog)
+               GameLogEcho(s);
+
+       file = -1;
+       if(to_file)
+       {
+               file = fopen(autocvar_sv_logscores_filename, FILE_APPEND);
+               if(file == -1)
+                       to_file = false;
+               else
+                       fputs(file, strcat(s, "\n"));
+       }
+
+       s = strcat(":labels:player:", GetPlayerScoreString(NULL, 0));
+       if(to_console)
+               LOG_INFO(s);
+       if(to_eventlog)
+               GameLogEcho(s);
+       if(to_file)
+               fputs(file, strcat(s, "\n"));
+
+       FOREACH_CLIENT(IS_REAL_CLIENT(it) || (IS_BOT_CLIENT(it) && autocvar_sv_logscores_bots), {
+               s = strcat(":player:see-labels:", GetPlayerScoreString(it, 0), ":");
+               s = strcat(s, ftos(rint(time - CS(it).jointime)), ":");
+               if(IS_PLAYER(it) || MUTATOR_CALLHOOK(GetPlayerStatus, it))
+                       s = strcat(s, ftos(it.team), ":");
+               else
+                       s = strcat(s, "spectator:");
+
+               if(to_console)
+                       LOG_INFO(s, playername(it, false));
+               if(to_eventlog)
+                       GameLogEcho(strcat(s, ftos(it.playerid), ":", playername(it, false)));
+               if(to_file)
+                       fputs(file, strcat(s, playername(it, false), "\n"));
+       });
+
+       if(teamplay)
+       {
+               s = strcat(":labels:teamscores:", GetTeamScoreString(0, 0));
+               if(to_console)
+                       LOG_INFO(s);
+               if(to_eventlog)
+                       GameLogEcho(s);
+               if(to_file)
+                       fputs(file, strcat(s, "\n"));
+
+               for(i = 1; i < 16; ++i)
+               {
+                       s = strcat(":teamscores:see-labels:", GetTeamScoreString(i, 0));
+                       s = strcat(s, ":", ftos(i));
+                       if(to_console)
+                               LOG_INFO(s);
+                       if(to_eventlog)
+                               GameLogEcho(s);
+                       if(to_file)
+                               fputs(file, strcat(s, "\n"));
+               }
+       }
+
+       if(to_console)
+               LOG_INFO(":end");
+       if(to_eventlog)
+               GameLogEcho(":end");
+       if(to_file)
+       {
+               fputs(file, ":end\n");
+               fclose(file);
+       }
+}
+
+void FixIntermissionClient(entity e)
+{
+       if(!e.autoscreenshot) // initial call
+       {
+               e.autoscreenshot = time + 0.8;  // used for autoscreenshot
+               SetResourceExplicit(e, RES_HEALTH, -2342);
+               // first intermission phase; voting phase has positive health (used to decide whether to send SVC_FINALE or not)
+               for (int slot = 0; slot < MAX_WEAPONSLOTS; ++slot)
+               {
+                   .entity weaponentity = weaponentities[slot];
+                       if(e.(weaponentity))
+                       {
+                               e.(weaponentity).effects = EF_NODRAW;
+                               if (e.(weaponentity).weaponchild)
+                                       e.(weaponentity).weaponchild.effects = EF_NODRAW;
+                       }
+               }
+               if(IS_REAL_CLIENT(e))
+               {
+                       stuffcmd(e, "\nscr_printspeed 1000000\n");
+                       RandomSelection_Init();
+                       FOREACH_WORD(autocvar_sv_intermission_cdtrack, true, {
+                               RandomSelection_AddString(it, 1, 1);
+                       });
+                       if (RandomSelection_chosen_string != "")
+                       {
+                               stuffcmd(e, sprintf("\ncd loop %s\n", RandomSelection_chosen_string));
+                       }
+                       msg_entity = e;
+                       WriteByte(MSG_ONE, SVC_INTERMISSION);
+               }
+       }
+}
+
+/*
+go to the next level for deathmatch
+only called if a time or frag limit has expired
+*/
+void NextLevel()
+{
+       game_stopped = true;
+       intermission_running = 1; // game over
+
+       // enforce a wait time before allowing changelevel
+       if(player_count > 0)
+               intermission_exittime = time + autocvar_sv_mapchange_delay;
+       else
+               intermission_exittime = -1;
+
+       /*
+       WriteByte (MSG_ALL, SVC_CDTRACK);
+       WriteByte (MSG_ALL, 3);
+       WriteByte (MSG_ALL, 3);
+       // done in FixIntermission
+       */
+
+       //pos = FindIntermission ();
+
+       VoteReset();
+
+       DumpStats(true);
+
+       // send statistics
+       PlayerStats_GameReport(true);
+       WeaponStats_Shutdown();
+
+       Kill_Notification(NOTIF_ALL, NULL, MSG_CENTER, CPID_Null); // kill all centerprints now
+
+       if(autocvar_sv_eventlog)
+               GameLogEcho(":gameover");
+
+       GameLogClose();
+
+       FOREACH_CLIENT(IS_PLAYER(it), {
+               FixIntermissionClient(it);
+               if(it.winning)
+                       bprint(playername(it, false), " ^7wins.\n");
+       });
+
+       target_music_kill();
+
+       if(autocvar_g_campaign)
+               CampaignPreIntermission();
+
+       MUTATOR_CALLHOOK(MatchEnd);
+
+       localcmd("\nsv_hook_gameend\n");
+}
+
+
+float InitiateSuddenDeath()
+{
+       // Check first whether normal overtimes could be added before initiating suddendeath mode
+       // - for this timelimit_overtime needs to be >0 of course
+       // - also check the winning condition calculated in the previous frame and only add normal overtime
+       //   again, if at the point at which timelimit would be extended again, still no winner was found
+       if (!autocvar_g_campaign && checkrules_overtimesadded >= 0
+               && (checkrules_overtimesadded < autocvar_timelimit_overtimes || autocvar_timelimit_overtimes < 0)
+               && autocvar_timelimit_overtime && !(g_race && !g_race_qualifying))
+       {
+               return 1; // need to call InitiateOvertime later
+       }
+       else
+       {
+               if(!checkrules_suddendeathend)
+               {
+                       if(autocvar_g_campaign)
+                               checkrules_suddendeathend = time; // no suddendeath in campaign
+                       else
+                               checkrules_suddendeathend = time + 60 * autocvar_timelimit_suddendeath;
+                       if(g_race && !g_race_qualifying)
+                               race_StartCompleting();
+               }
+               return 0;
+       }
+}
+
+void InitiateOvertime() // ONLY call this if InitiateSuddenDeath returned true
+{
+       ++checkrules_overtimesadded;
+       //add one more overtime by simply extending the timelimit
+       cvar_set("timelimit", ftos(autocvar_timelimit + autocvar_timelimit_overtime));
+       Send_Notification(NOTIF_ALL, NULL, MSG_CENTER, CENTER_OVERTIME_TIME, autocvar_timelimit_overtime * 60);
+}
+
+float GetWinningCode(float fraglimitreached, float equality)
+{
+       if(autocvar_g_campaign == 1)
+       {
+               if(fraglimitreached)
+                       return WINNING_YES;
+               else
+                       return WINNING_NO;
+       }
+       else
+       {
+               if(equality)
+               {
+                       if(fraglimitreached)
+                               return WINNING_STARTSUDDENDEATHOVERTIME;
+                       else
+                               return WINNING_NEVER;
+               }
+               else
+               {
+                       if(fraglimitreached)
+                               return WINNING_YES;
+                       else
+                               return WINNING_NO;
+               }
+       }
+}
+
+// set the .winning flag for exactly those players with a given field value
+void SetWinners(.float field, float value)
+{
+       FOREACH_CLIENT(IS_PLAYER(it), { it.winning = (it.(field) == value); });
+}
+
+// set the .winning flag for those players with a given field value
+void AddWinners(.float field, float value)
+{
+       FOREACH_CLIENT(IS_PLAYER(it), {
+               if(it.(field) == value)
+                       it.winning = 1;
+       });
+}
+
+// clear the .winning flags
+void ClearWinners()
+{
+       FOREACH_CLIENT(IS_PLAYER(it), { it.winning = 0; });
+}
+
+void ShuffleMaplist()
+{
+       cvar_set("g_maplist", shufflewords(autocvar_g_maplist));
+}
+
+int fragsleft_last;
+float WinningCondition_Scores(float limit, float leadlimit)
+{
+       // TODO make everything use THIS winning condition (except LMS)
+       WinningConditionHelper(NULL);
+
+       if(teamplay)
+       {
+               for (int i = 1; i < 5; ++i)
+               {
+                       Team_SetTeamScore(Team_GetTeamFromIndex(i),
+                               TeamScore_GetCompareValue(Team_IndexToTeam(i)));
+               }
+       }
+
+       ClearWinners();
+       if(WinningConditionHelper_winner)
+               WinningConditionHelper_winner.winning = 1;
+       if(WinningConditionHelper_winnerteam >= 0)
+               SetWinners(team, WinningConditionHelper_winnerteam);
+
+       if(WinningConditionHelper_lowerisbetter)
+       {
+               WinningConditionHelper_topscore = -WinningConditionHelper_topscore;
+               WinningConditionHelper_secondscore = -WinningConditionHelper_secondscore;
+               limit = -limit;
+       }
+
+       if(WinningConditionHelper_zeroisworst)
+               leadlimit = 0; // not supported in this mode
+
+       if(MUTATOR_CALLHOOK(Scores_CountFragsRemaining))
+       {
+               float fragsleft;
+               if (checkrules_suddendeathend && time >= checkrules_suddendeathend)
+               {
+                       fragsleft = 1;
+               }
+               else
+               {
+                       fragsleft = FLOAT_MAX;
+                       float leadingfragsleft = FLOAT_MAX;
+                       if (limit)
+                               fragsleft = limit - WinningConditionHelper_topscore;
+                       if (leadlimit)
+                               leadingfragsleft = WinningConditionHelper_secondscore + leadlimit - WinningConditionHelper_topscore;
+
+                       if (limit && leadlimit && autocvar_leadlimit_and_fraglimit)
+                               fragsleft = max(fragsleft, leadingfragsleft);
+                       else
+                               fragsleft = min(fragsleft, leadingfragsleft);
+               }
+
+               if (fragsleft_last != fragsleft) // do not announce same remaining frags multiple times
+               {
+                       if (fragsleft == 1)
+                               Send_Notification(NOTIF_ALL, NULL, MSG_ANNCE, ANNCE_REMAINING_FRAG_1);
+                       else if (fragsleft == 2)
+                               Send_Notification(NOTIF_ALL, NULL, MSG_ANNCE, ANNCE_REMAINING_FRAG_2);
+                       else if (fragsleft == 3)
+                               Send_Notification(NOTIF_ALL, NULL, MSG_ANNCE, ANNCE_REMAINING_FRAG_3);
+
+                       fragsleft_last = fragsleft;
+               }
+       }
+
+       bool fraglimit_reached = (limit && WinningConditionHelper_topscore >= limit);
+       bool leadlimit_reached = (leadlimit && WinningConditionHelper_topscore - WinningConditionHelper_secondscore >= leadlimit);
+
+       bool limit_reached;
+       // only respect leadlimit_and_fraglimit when both limits are set or the game will never end
+       if (limit && leadlimit && autocvar_leadlimit_and_fraglimit)
+               limit_reached = (fraglimit_reached && leadlimit_reached);
+       else
+               limit_reached = (fraglimit_reached || leadlimit_reached);
+
+       return GetWinningCode(
+               WinningConditionHelper_topscore && limit_reached,
+               WinningConditionHelper_equality
+       );
+}
+
+float WinningCondition_RanOutOfSpawns()
+{
+       if(have_team_spawns <= 0)
+               return WINNING_NO;
+
+       if(!autocvar_g_spawn_useallspawns)
+               return WINNING_NO;
+
+       if(!some_spawn_has_been_used)
+               return WINNING_NO;
+
+       for (int i = 1; i < 5; ++i)
+       {
+               Team_SetTeamScore(Team_GetTeamFromIndex(i), 0);
+       }
+
+       FOREACH_CLIENT(IS_PLAYER(it) && !IS_DEAD(it),
+       {
+               if (Team_IsValidTeam(it.team))
+               {
+                       Team_SetTeamScore(Team_GetTeam(it.team), 1);
+               }
+       });
+
+       IL_EACH(g_spawnpoints, true,
+       {
+               if (Team_IsValidTeam(it.team))
+               {
+                       Team_SetTeamScore(Team_GetTeam(it.team), 1);
+               }
+       });
+
+       ClearWinners();
+       float team1_score = Team_GetTeamScore(Team_GetTeamFromIndex(1));
+       float team2_score = Team_GetTeamScore(Team_GetTeamFromIndex(2));
+       float team3_score = Team_GetTeamScore(Team_GetTeamFromIndex(3));
+       float team4_score = Team_GetTeamScore(Team_GetTeamFromIndex(4));
+       if(team1_score + team2_score + team3_score + team4_score == 0)
+       {
+               checkrules_equality = true;
+               return WINNING_YES;
+       }
+       else if(team1_score + team2_score + team3_score + team4_score == 1)
+       {
+               float t, i;
+               if(team1_score)
+                       t = 1;
+               else if(team2_score)
+                       t = 2;
+               else if(team3_score)
+                       t = 3;
+               else // if(team4_score)
+                       t = 4;
+               entity balance = TeamBalance_CheckAllowedTeams(NULL);
+               for(i = 0; i < MAX_TEAMSCORE; ++i)
+               {
+                       for (int j = 1; j <= NUM_TEAMS; ++j)
+                       {
+                               if (t == j)
+                               {
+                                       continue;
+                               }
+                               if (!TeamBalance_IsTeamAllowed(balance, j))
+                               {
+                                       continue;
+                               }
+                               TeamScore_AddToTeam(Team_IndexToTeam(j), i, -1000);
+                       }
+               }
+
+               AddWinners(team, t);
+               return WINNING_YES;
+       }
+       else
+               return WINNING_NO;
+}
+
+/*
+============
+CheckRules_World
+
+Exit deathmatch games upon conditions
+============
+*/
+void CheckRules_World()
+{
+       VoteThink();
+       MapVote_Think();
+
+       SetDefaultAlpha();
+
+       if (intermission_running) // someone else quit the game already
+       {
+               if(player_count == 0) // Nobody there? Then let's go to the next map
+                       MapVote_Start();
+                       // this will actually check the player count in the next frame
+                       // again, but this shouldn't hurt
+               return;
+       }
+
+       float timelimit = autocvar_timelimit * 60;
+       float fraglimit = autocvar_fraglimit;
+       float leadlimit = autocvar_leadlimit;
+       if (leadlimit < 0) leadlimit = 0;
+
+       if(warmup_stage || time <= game_starttime) // NOTE: this is <= to prevent problems in the very tic where the game starts
+       {
+               if(timelimit > 0)
+                       timelimit = 0; // timelimit is not made for warmup
+               if(fraglimit > 0)
+                       fraglimit = 0; // no fraglimit for now
+               leadlimit = 0; // no leadlimit for now
+       }
+
+       if(timelimit > 0)
+       {
+               timelimit += game_starttime;
+       }
+       else if (timelimit < 0)
+       {
+               // endmatch
+               NextLevel();
+               return;
+       }
+
+       float wantovertime;
+       wantovertime = 0;
+
+       if(checkrules_suddendeathend)
+       {
+               if(!checkrules_suddendeathwarning)
+               {
+                       checkrules_suddendeathwarning = true;
+                       if(g_race && !g_race_qualifying)
+                               Send_Notification(NOTIF_ALL, NULL, MSG_CENTER, CENTER_RACE_FINISHLAP);
+                       else
+                               Send_Notification(NOTIF_ALL, NULL, MSG_CENTER, CENTER_OVERTIME_FRAG);
+               }
+       }
+       else
+       {
+               if (timelimit && time >= timelimit)
+               {
+                       if(g_race && (g_race_qualifying == 2) && timelimit > 0)
+                       {
+                               float totalplayers;
+                               float playerswithlaps;
+                               float readyplayers;
+                               totalplayers = playerswithlaps = readyplayers = 0;
+                               FOREACH_CLIENT(IS_PLAYER(it), {
+                                       ++totalplayers;
+                                       if(GameRules_scoring_add(it, RACE_FASTEST, 0))
+                                               ++playerswithlaps;
+                                       if(it.ready)
+                                               ++readyplayers;
+                               });
+
+                               // at least 2 of the players have completed a lap: start the RACE
+                               // otherwise, the players should end the qualifying on their own
+                               if(readyplayers || playerswithlaps >= 2)
+                               {
+                                       checkrules_suddendeathend = 0;
+                                       ReadyRestart(); // go to race
+                                       return;
+                               }
+                               else
+                                       wantovertime |= InitiateSuddenDeath();
+                       }
+                       else
+                               wantovertime |= InitiateSuddenDeath();
+               }
+       }
+
+       if (checkrules_suddendeathend && time >= checkrules_suddendeathend)
+       {
+               NextLevel();
+               return;
+       }
+
+       int checkrules_status = WinningCondition_RanOutOfSpawns();
+       if(checkrules_status == WINNING_YES)
+               bprint("Hey! Someone ran out of spawns!\n");
+       else if(MUTATOR_CALLHOOK(CheckRules_World, checkrules_status, timelimit, fraglimit))
+               checkrules_status = M_ARGV(0, float);
+       else
+               checkrules_status = WinningCondition_Scores(fraglimit, leadlimit);
+
+       if(checkrules_status == WINNING_STARTSUDDENDEATHOVERTIME)
+       {
+               checkrules_status = WINNING_NEVER;
+               checkrules_overtimesadded = -1;
+               wantovertime |= InitiateSuddenDeath();
+       }
+
+       if(checkrules_status == WINNING_NEVER)
+               // equality cases! Nobody wins if the overtime ends in a draw.
+               ClearWinners();
+
+       if(wantovertime)
+       {
+               if(checkrules_status == WINNING_NEVER)
+                       InitiateOvertime();
+               else
+                       checkrules_status = WINNING_YES;
+       }
+
+       if(checkrules_suddendeathend)
+               if(checkrules_status != WINNING_NEVER || time >= checkrules_suddendeathend)
+                       checkrules_status = WINNING_YES;
+
+       if(checkrules_status == WINNING_YES)
+       {
+               //print("WINNING\n");
+               NextLevel();
+       }
+}
+
+string GotoMap(string m)
+{
+       m = GameTypeVote_MapInfo_FixName(m);
+       if (!m)
+               return "The map you suggested is not available on this server.";
+       if (!autocvar_sv_vote_gametype)
+       if(!MapInfo_CheckMap(m))
+               return "The map you suggested does not support the current game mode.";
+       cvar_set("nextmap", m);
+       cvar_set("timelimit", "-1");
+       if(mapvote_initialized || alreadychangedlevel)
+       {
+               if(DoNextMapOverride(0))
+                       return "Map switch initiated.";
+               else
+                       return "Hm... no. For some reason I like THIS map more.";
+       }
+       else
+               return "Map switch will happen after scoreboard.";
+}
+
+bool autocvar_sv_gameplayfix_multiplethinksperframe = true;
+void RunThink(entity this)
+{
+       // don't let things stay in the past.
+       // it is possible to start that way by a trigger with a local time.
+       if(this.nextthink <= 0 || this.nextthink > time + frametime)
+               return;
+
+       float oldtime = time; // do we need to save this?
+
+       for (int iterations = 0; iterations < 128 && !wasfreed(this); iterations++)
+       {
+               time = max(oldtime, this.nextthink);
+               this.nextthink = 0;
+
+               if(getthink(this))
+                       getthink(this)(this);
+               // mods often set nextthink to time to cause a think every frame,
+               // we don't want to loop in that case, so exit if the new nextthink is
+               // <= the time the qc was told, also exit if it is past the end of the
+               // frame
+               if(this.nextthink <= time || this.nextthink > oldtime + frametime || !autocvar_sv_gameplayfix_multiplethinksperframe)
+                       break;
+       }
+
+       time = oldtime;
+}
+
+bool autocvar_sv_freezenonclients;
+bool autocvar_sv_gameplayfix_delayprojectiles = false;
+void Physics_Frame()
+{
+       if(autocvar_sv_freezenonclients)
+               return;
+
+       IL_EACH(g_moveables, true,
+       {
+               if(IS_CLIENT(it) || it.classname == "" || it.move_movetype == MOVETYPE_PHYSICS)
+                       continue;
+
+               //set_movetype(it, it.move_movetype);
+               // inline the set_movetype function, since this is called a lot
+               it.movetype = (it.move_qcphysics) ? MOVETYPE_QCENTITY : it.move_movetype;
+
+               if(it.move_qcphysics && it.move_movetype != MOVETYPE_NONE)
+                       Movetype_Physics_NoMatchTicrate(it, PHYS_INPUT_TIMELENGTH, false);
+
+               if(it.movetype >= MOVETYPE_USER_FIRST && it.movetype <= MOVETYPE_USER_LAST) // these cases have no think handling
+               {
+                       if(it.move_movetype == MOVETYPE_PUSH || it.move_movetype == MOVETYPE_FAKEPUSH)
+                               continue; // these movetypes have no regular think function
+                       // handle thinking here
+                       if (getthink(it) && it.nextthink > 0 && it.nextthink <= time + frametime)
+                               RunThink(it);
+               }
+       });
+
+       if(autocvar_sv_gameplayfix_delayprojectiles >= 0)
+               return;
+
+       IL_EACH(g_moveables, it.move_qcphysics,
+       {
+               if(IS_CLIENT(it) || it.classname == "" || it.move_movetype == MOVETYPE_NONE)
+                       continue;
+               Movetype_Physics_NoMatchTicrate(it, PHYS_INPUT_TIMELENGTH, false);
+       });
+}
+
+void systems_update();
+void EndFrame()
+{
+       anticheat_endframe();
+
+       Physics_Frame();
+
+       FOREACH_CLIENT(IS_REAL_CLIENT(it), {
+               entity e = IS_SPEC(it) ? it.enemy : it;
+               if (e.typehitsound) {
+                       STAT(TYPEHIT_TIME, it) = time;
+               } else if (e.killsound) {
+                       STAT(KILL_TIME, it) = time;
+               } else if (e.damage_dealt) {
+                       STAT(HIT_TIME, it) = time;
+                       STAT(DAMAGE_DEALT_TOTAL, it) += ceil(e.damage_dealt);
+               }
+       });
+       // add 1 frametime because after this, engine SV_Physics
+       // increases time by a frametime and then networks the frame
+       // add another frametime because client shows everything with
+       // 1 frame of lag (cl_nolerp 0). The last +1 however should not be
+       // needed!
+       float altime = time + frametime * (1 + autocvar_g_antilag_nudge);
+       FOREACH_CLIENT(true, {
+               it.typehitsound = false;
+               it.damage_dealt = 0;
+               it.killsound = false;
+               antilag_record(it, CS(it), altime);
+       });
+       IL_EACH(g_monsters, true,
+       {
+               antilag_record(it, it, altime);
+       });
+       IL_EACH(g_projectiles, it.classname == "nade",
+       {
+               antilag_record(it, it, altime);
+       });
+       systems_update();
+       IL_ENDFRAME();
+}
+
+
+/*
+ * RedirectionThink:
+ * returns true if redirecting
+ */
+float redirection_timeout;
+float redirection_nextthink;
+float RedirectionThink()
+{
+       float clients_found;
+
+       if(redirection_target == "")
+               return false;
+
+       if(!redirection_timeout)
+       {
+               cvar_set("sv_public", "-2");
+               redirection_timeout = time + 0.6; // this will only try twice... should be able to keep more clients
+               if(redirection_target == "self")
+                       bprint("^3SERVER NOTICE:^7 restarting the server\n");
+               else
+                       bprint("^3SERVER NOTICE:^7 redirecting everyone to ", redirection_target, "\n");
+       }
+
+       if(time < redirection_nextthink)
+               return true;
+
+       redirection_nextthink = time + 1;
+
+       clients_found = 0;
+       FOREACH_CLIENT(IS_REAL_CLIENT(it), {
+               // TODO add timer
+               LOG_INFO("Redirecting: sending connect command to ", it.netname);
+               if(redirection_target == "self")
+                       stuffcmd(it, "\ndisconnect; defer ", ftos(autocvar_quit_and_redirect_timer), " reconnect\n");
+               else
+                       stuffcmd(it, strcat("\ndisconnect; defer ", ftos(autocvar_quit_and_redirect_timer), " \"connect ", redirection_target, "\"\n"));
+               ++clients_found;
+       });
+
+       LOG_INFO("Redirecting: ", ftos(clients_found), " clients left.");
+
+       if(time > redirection_timeout || clients_found == 0)
+               localcmd("\nwait; wait; wait; quit\n");
+
+       return true;
+}
+
+void RestoreGame()
+{
+       // Loaded from a save game
+       // some things then break, so let's work around them...
+
+       // Progs DB (capture records)
+       ServerProgsDB = db_load(strcat("server.db", autocvar_sessionid));
+
+       // Mapinfo
+       MapInfo_Shutdown();
+       MapInfo_Enumerate();
+       MapInfo_FilterGametype(MapInfo_CurrentGametype(), MapInfo_CurrentFeatures(), MapInfo_RequiredFlags(), MapInfo_ForbiddenFlags(), 1);
+       WeaponStats_Init();
+
+       TargetMusic_RestoreGame();
+}
+
+void Shutdown()
+{
+       game_stopped = 2;
+
+       if(world_initialized > 0)
+       {
+               world_initialized = 0;
+
+               // if a timeout is active, reset the slowmo value to normal
+               if(timeout_status == TIMEOUT_ACTIVE)
+                       cvar_set("slowmo", ftos(orig_slowmo));
+
+               LOG_TRACE("Saving persistent data...");
+               Ban_SaveBans();
+
+               // playerstats with unfinished match
+               PlayerStats_GameReport(false);
+
+               if(!cheatcount_total)
+               {
+                       if(autocvar_sv_db_saveasdump)
+                               db_dump(ServerProgsDB, strcat("server.db", autocvar_sessionid));
+                       else
+                               db_save(ServerProgsDB, strcat("server.db", autocvar_sessionid));
+               }
+               if(autocvar_developer > 0)
+               {
+                       if(autocvar_sv_db_saveasdump)
+                               db_dump(TemporaryDB, "server-temp.db");
+                       else
+                               db_save(TemporaryDB, "server-temp.db");
+               }
+               CheatShutdown(); // must be after cheatcount check
+               db_close(ServerProgsDB);
+               db_close(TemporaryDB);
+               LOG_TRACE("Saving persistent data... done!");
+               // tell the bot system the game is ending now
+               bot_endgame();
+
+               WeaponStats_Shutdown();
+               MapInfo_Shutdown();
+       }
+       else if(world_initialized == 0)
+       {
+               LOG_INFO("NOTE: crashed before even initializing the world, not saving persistent data");
+       }
+       else
+       {
+               __init_dedicated_server_shutdown();
+       }
+}
diff --git a/qcsrc/server/world.qh b/qcsrc/server/world.qh
new file mode 100644 (file)
index 0000000..3bbaad6
--- /dev/null
@@ -0,0 +1,65 @@
+#pragma once
+
+float checkrules_equality;
+float checkrules_suddendeathwarning;
+float checkrules_suddendeathend;
+float checkrules_overtimesadded; //how many overtimes have been already added
+
+// flag set on worldspawn so that the code knows if it is dedicated or not
+bool server_is_dedicated;
+
+string cvar_changes;
+string cvar_purechanges;
+float cvar_purechanges_count;
+
+string modname;
+
+string gamemode_name;
+
+string clientstuff;
+
+string matchid;
+
+.string fog;
+
+float intermission_running;
+float intermission_exittime;
+float alreadychangedlevel;
+
+string cache_mutatormsg;
+string cache_lastmutatormsg;
+
+float default_player_alpha;
+float default_weapon_alpha;
+
+// database
+float ServerProgsDB;
+float TemporaryDB;
+
+.float winning;
+const int WINNING_NO = 0; // no winner, but time limits may terminate the game
+const int WINNING_YES = 1; // winner found
+const int WINNING_NEVER = 2; // no winner, enter overtime if time limit is reached
+const int WINNING_STARTSUDDENDEATHOVERTIME = 3; // no winner, enter suddendeath overtime NOW
+
+float WinningCondition_Scores(float limit, float leadlimit);
+void SetWinners(.float field, float value);
+void IntermissionThink(entity this);
+void GotoNextMap(float reinit);
+void ReadyRestart();
+
+string GetGametype();
+
+void DumpStats(float final);
+float Map_IsRecent(string m);
+string GetNextMap();
+void ShuffleMaplist();
+void Map_Goto_SetStr(string nextmapname);
+void Map_Goto(float reinit);
+void Map_MarkAsRecent(string m);
+float DoNextMapOverride(float reinit);
+void CheckRules_World();
+float RedirectionThink();
+
+IntrusiveList g_moveables;
+STATIC_INIT(g_moveables) { g_moveables = IL_NEW(); }