]> git.xonotic.org Git - xonotic/xonotic-data.pk3dir.git/commitdiff
Merge branch 'terencehill/menu_registries' into 'master'
authorterencehill <piuntn@gmail.com>
Thu, 14 Mar 2024 23:51:19 +0000 (23:51 +0000)
committerterencehill <piuntn@gmail.com>
Thu, 14 Mar 2024 23:51:19 +0000 (23:51 +0000)
Menu: remove useless registries

See merge request xonotic/xonotic-data.pk3dir!1261

83 files changed:
.gitattributes
.gitlab-ci.yml
physicsCPMA.cfg
physicsFruit.cfg
physicsHavoc.cfg
physicsLeeStricklin-ModdedFruit.cfg
physicsLeeStricklin.cfg
physicsLeeStricklinOld.cfg
physicsLzd.cfg
physicsNexuiz10.cfg
physicsNexuiz11.cfg
physicsNexuiz151.cfg
physicsNexuiz151b.cfg
physicsNexuiz16rc1.cfg
physicsNexuiz20.cfg
physicsNexuiz25.cfg
physicsNexuiz26.cfg
physicsNoQWBunny-nexbased.cfg
physicsOverkill.cfg
physicsQ.cfg
physicsQ2.cfg
physicsQ2a.cfg
physicsQ3.cfg
physicsQBF.cfg
physicsQBFplus.cfg
physicsSamual.cfg
physicsWarsow.cfg
physicsWarsowClassicBunny.cfg
physicsWarsowDev.cfg
physicsX.cfg
physicsX010.cfg
physicsX07.cfg
physicsXDF.cfg
physicsXDFLight.cfg
qcsrc/client/csqcmodel_hooks.qc
qcsrc/client/items/items.qc
qcsrc/client/main.qc
qcsrc/common/_all.inc
qcsrc/common/checkextension.qc [new file with mode: 0644]
qcsrc/common/checkextension.qh [new file with mode: 0644]
qcsrc/common/constants.qh
qcsrc/common/csqcmodel_settings.qh
qcsrc/common/gamemodes/gamemode/ctf/sv_ctf.qc
qcsrc/common/gamemodes/gamemode/domination/sv_domination.qc
qcsrc/common/gamemodes/gamemode/keepaway/sv_keepaway.qc
qcsrc/common/gamemodes/gamemode/keyhunt/sv_keyhunt.qc
qcsrc/common/gamemodes/gamemode/tka/sv_tka.qc
qcsrc/common/items/item.qh
qcsrc/common/items/item/armor.qh
qcsrc/common/items/item/health.qh
qcsrc/common/items/item/pickup.qh
qcsrc/common/mapobjects/func/conveyor.qc
qcsrc/common/mapobjects/func/ladder.qc
qcsrc/common/mapobjects/teleporters.qc
qcsrc/common/mapobjects/trigger/swamp.qc
qcsrc/common/mapobjects/trigger/viewloc.qc
qcsrc/common/mutators/mutator/buffs/sv_buffs.qc
qcsrc/common/mutators/mutator/buffs/sv_buffs.qh
qcsrc/common/mutators/mutator/powerups/powerups.qh
qcsrc/common/physics/movetypes/movetypes.qc
qcsrc/common/physics/movetypes/toss.qc
qcsrc/common/playerstats.qc
qcsrc/common/stats.qh
qcsrc/common/weapons/weapon/porto.qc
qcsrc/lib/log.qh
qcsrc/lib/warpzone/common.qc
qcsrc/lib/warpzone/common.qh
qcsrc/lib/warpzone/util_server.qh
qcsrc/menu/menu.qc
qcsrc/server/bot/default/navigation.qc
qcsrc/server/bot/default/waypoints.qc
qcsrc/server/client.qc
qcsrc/server/client.qh
qcsrc/server/items/items.qc
qcsrc/server/main.qc
qcsrc/server/mapvoting.qc
qcsrc/server/mapvoting.qh
qcsrc/server/player.qc
qcsrc/server/world.qc
qcsrc/server/world.qh
qcsrc/tools/sv_game-hashtest.sh
xonotic-client.cfg
xonotic-server.cfg

index 14c10a42706a302437fa270fa570effabcf67fdd..454b3377c9863a4e96349a1a8bdba5af7d7ce9ac 100644 (file)
@@ -1,6 +1,3 @@
-*.q[ch] linguist-language=C
-*.inc linguist-language=C
-
 * -crlf
 *.0 -crlf
 *.1 crlf=input
@@ -116,8 +113,8 @@ GPL crlf=input
 *.ico -crlf
 *.idl crlf=input
 *.idsoftware crlf=input
-*.inc crlf=input
 *.in crlf=input
+*.inc crlf=input linguist-language=C gitlab-language=C
 *.info-1 -crlf
 *.info-2 -crlf
 *.info -crlf
@@ -184,9 +181,8 @@ POSITIONS -crlf
 *.psd -crlf
 *.py crlf=input
 *.q3map1 crlf=input
-*.qc crlf=input
+*.q[ch] crlf=input linguist-language=C gitlab-language=C
 *.qdt crlf=input
-*.qh crlf=input
 *.rar -crlf
 *.rb crlf=input
 *.rc2 crlf=input
index a36862c5628bfbc674b509ecaba278afa92a2e69..7647566dfa204f82b38887843028af9c9c782574 100644 (file)
@@ -36,7 +36,7 @@ test_compilation_units:
 test_sv_game:
   stage: test
   script:
-    - export EXPECT=9ee7ce44ffe60a5e1cf29a2b82f79ebf
+    - export EXPECT=ded5d54d7a8326069f5f3d015cee1bf2
     - qcsrc/tools/sv_game-hashtest.sh
     - exit $?
 
index 559ce3a37c78464f241c7f476e19595047b08c82..b6317debd19d54773dbb3efc201d82c4c0204101 100644 (file)
@@ -63,8 +63,8 @@ sv_jumpspeedcap_min 0
 sv_jumpspeedcap_max ""
 // CPMA has ramp jumping
 sv_jumpspeedcap_max_disable_on_ramps 1
-// FIXME: Q3 teleporters _set_ speed to 400, not cap it
 g_teleport_maxspeed 400
+g_teleport_minspeed 400
 sv_track_canjump 1
 sv_gameplayfix_stepdown_maxspeed 0
 g_movement_highspeed_q3_compat 1
index 7c0c5170e8216ea94536a2395e5881f8e7ce3d92..f1146b858654192c6ebc802807334a981ec4804a 100644 (file)
@@ -36,6 +36,7 @@ sv_jumpspeedcap_min 0 // need predicting? (it should already be in CSQC predicti
 sv_jumpspeedcap_max 0.35
 sv_jumpspeedcap_max_disable_on_ramps 1
 g_teleport_maxspeed 0
+g_teleport_minspeed 0
 sv_track_canjump 0
 sv_gameplayfix_stepdown_maxspeed 0
 g_movement_highspeed_q3_compat 0
index fdefb9ccaf724f55c4247e3203fcc2aca2569ddf..55f26de719382d069e7cf8d2c0a0c2ccf1dc1e6b 100644 (file)
@@ -37,6 +37,7 @@ sv_jumpspeedcap_min ""
 sv_jumpspeedcap_max ""
 sv_jumpspeedcap_max_disable_on_ramps 0
 g_teleport_maxspeed 0
+g_teleport_minspeed 0
 sv_track_canjump 0
 sv_gameplayfix_stepdown_maxspeed 0
 g_movement_highspeed_q3_compat 0
index 25f2b54d6f186a98b4078837dcca526586050699..e80bac1350db5d45125523bd70b1b39206fff389 100644 (file)
@@ -45,6 +45,7 @@ sv_jumpspeedcap_min 0 // need predicting? (it should already be in CSQC predicti
 sv_jumpspeedcap_max ""
 sv_jumpspeedcap_max_disable_on_ramps 1
 g_teleport_maxspeed 0
+g_teleport_minspeed 0
 sv_track_canjump 0
 sv_gameplayfix_stepdown_maxspeed 0
 g_movement_highspeed_q3_compat 0
index 5edcfbe27b48a306962295049c6e0cf05874e68d..5e4a81633b8d1f36e285786f8ffd47ee642aaf3b 100644 (file)
@@ -45,6 +45,7 @@ sv_jumpspeedcap_min ""
 sv_jumpspeedcap_max ""
 sv_jumpspeedcap_max_disable_on_ramps 1
 g_teleport_maxspeed 0
+g_teleport_minspeed 0
 sv_track_canjump 0
 sv_gameplayfix_stepdown_maxspeed 0
 g_movement_highspeed_q3_compat 0
index a4bdd08ca12be3c403daeba5707d584a48a4e883..13d059108cf6cdae15154a8b59eaf17bd01e7421 100644 (file)
@@ -40,6 +40,7 @@ sv_jumpspeedcap_min ""
 sv_jumpspeedcap_max 0.38
 sv_jumpspeedcap_max_disable_on_ramps 0
 g_teleport_maxspeed 0
+g_teleport_minspeed 0
 sv_track_canjump 0
 sv_gameplayfix_stepdown_maxspeed 0
 g_movement_highspeed_q3_compat 0
index 8d54437fb82751732490a9fa5c4f59b3ea14a6c7..6bcbffb93ba1dc80f6e820bd8891da1fd3a9159f 100644 (file)
@@ -38,6 +38,7 @@ sv_jumpspeedcap_min ""
 sv_jumpspeedcap_max ""
 sv_jumpspeedcap_max_disable_on_ramps 0
 g_teleport_maxspeed 0
+g_teleport_minspeed 0
 sv_track_canjump 0
 sv_gameplayfix_stepdown_maxspeed 0
 g_movement_highspeed_q3_compat 0
index 3ca1ded65ce1ebb2ce281a90dbeb87ee3f8a79e5..af10e6208be50395088e1cbc91293c6b5b082ae8 100644 (file)
@@ -38,6 +38,7 @@ sv_jumpspeedcap_min ""
 sv_jumpspeedcap_max ""
 sv_jumpspeedcap_max_disable_on_ramps 0
 g_teleport_maxspeed 0
+g_teleport_minspeed 0
 sv_track_canjump 0
 sv_gameplayfix_stepdown_maxspeed 0
 g_movement_highspeed_q3_compat 0
index 03056d184e8ed1fe824137c52f0b542826cadc32..22b743528fe81c1f333e74dcd07f00bb6e427021 100644 (file)
@@ -37,6 +37,7 @@ sv_jumpspeedcap_min ""
 sv_jumpspeedcap_max ""
 sv_jumpspeedcap_max_disable_on_ramps 0
 g_teleport_maxspeed 0
+g_teleport_minspeed 0
 sv_track_canjump 0
 sv_gameplayfix_stepdown_maxspeed 0
 g_movement_highspeed_q3_compat 0
index e2aa58971c1b50373ad16f37af9464ff489432e8..703a6a3c641740120555f22ebe90fafcd30b9d6c 100644 (file)
@@ -37,6 +37,7 @@ sv_jumpspeedcap_min ""
 sv_jumpspeedcap_max ""
 sv_jumpspeedcap_max_disable_on_ramps 0
 g_teleport_maxspeed 0
+g_teleport_minspeed 0
 sv_track_canjump 0
 sv_gameplayfix_stepdown_maxspeed 0
 g_movement_highspeed_q3_compat 0
index 26f746a7c0d9ae8e961a1afcebcd11f10d23f516..d5d377b0cd3f4b995c1279a5af2e05d28b83715a 100644 (file)
@@ -37,6 +37,7 @@ sv_jumpspeedcap_min ""
 sv_jumpspeedcap_max ""
 sv_jumpspeedcap_max_disable_on_ramps 0
 g_teleport_maxspeed 0
+g_teleport_minspeed 0
 sv_track_canjump 0
 sv_gameplayfix_stepdown_maxspeed 0
 g_movement_highspeed_q3_compat 0
index 40e7d1f3fca423b90d6c9dd9a0323032975a4f6a..32a4aaac534fee48a15a4bdb89a03c9134f899fd 100644 (file)
@@ -37,6 +37,7 @@ sv_jumpspeedcap_min ""
 sv_jumpspeedcap_max ""
 sv_jumpspeedcap_max_disable_on_ramps 0
 g_teleport_maxspeed 0
+g_teleport_minspeed 0
 sv_track_canjump 0
 sv_gameplayfix_stepdown_maxspeed 0
 g_movement_highspeed_q3_compat 0
index 24e27b01dd2fb07177d893c61fe69d5d07d24116..d0c5e196a86736a266ed0143dd3854c73d4361d1 100644 (file)
@@ -37,6 +37,7 @@ sv_jumpspeedcap_min ""
 sv_jumpspeedcap_max ""
 sv_jumpspeedcap_max_disable_on_ramps 0
 g_teleport_maxspeed 0
+g_teleport_minspeed 0
 sv_track_canjump 0
 sv_gameplayfix_stepdown_maxspeed 0
 g_movement_highspeed_q3_compat 0
index 167d2f4d453efb5a0b155c597915d2d3f3dffac0..a563f39b03af2677395bca10b92df57fb7f7a7cd 100644 (file)
@@ -37,6 +37,7 @@ sv_jumpspeedcap_min ""
 sv_jumpspeedcap_max ""
 sv_jumpspeedcap_max_disable_on_ramps 0
 g_teleport_maxspeed 0
+g_teleport_minspeed 0
 sv_track_canjump 0
 sv_gameplayfix_stepdown_maxspeed 0
 g_movement_highspeed_q3_compat 0
index 6c41d6bbc061e9106bcca2e96b7a9bf22990571a..7bd2891f95048206993f4d82a83fa17659e40fa6 100644 (file)
@@ -37,6 +37,7 @@ sv_jumpspeedcap_min ""
 sv_jumpspeedcap_max ""
 sv_jumpspeedcap_max_disable_on_ramps 0
 g_teleport_maxspeed 0
+g_teleport_minspeed 0
 sv_track_canjump 0
 sv_gameplayfix_stepdown_maxspeed 0
 g_movement_highspeed_q3_compat 0
index c1447c0f091aa748b084ce148ae01af65fc91e98..cf077e6aec2d9207c90d4f095a52c6c8b441a486 100644 (file)
@@ -46,6 +46,7 @@ sv_jumpspeedcap_min ""
 sv_jumpspeedcap_max ""
 sv_jumpspeedcap_max_disable_on_ramps 1
 g_teleport_maxspeed 0
+g_teleport_minspeed 0
 sv_track_canjump 0
 sv_gameplayfix_stepdown_maxspeed 0
 g_movement_highspeed_q3_compat 0
index aa97fb15b4882198f22757aea2be809eca3e5a08..794d1cc4428f7b532c719d18c7a83869a8bfe1ec 100644 (file)
@@ -52,6 +52,7 @@ sv_jumpspeedcap_min ""
 sv_jumpspeedcap_max ""
 sv_jumpspeedcap_max_disable_on_ramps 1
 g_teleport_maxspeed 0
+g_teleport_minspeed 0
 sv_track_canjump 0
 sv_gameplayfix_stepdown_maxspeed 400
 g_movement_highspeed_q3_compat 0
index b84883d88bc4ab4acc2b8ba9244212339068392d..9a5becaa3cb1c30a8024670a46a7296374b4e0c0 100644 (file)
@@ -38,6 +38,7 @@ sv_jumpspeedcap_min ""
 sv_jumpspeedcap_max ""
 sv_jumpspeedcap_max_disable_on_ramps 0
 g_teleport_maxspeed 0
+g_teleport_minspeed 0
 sv_track_canjump 0
 sv_gameplayfix_stepdown_maxspeed 0
 g_movement_highspeed_q3_compat 0
index 5e18a2f0491a46da8c336e884ba00d9d70427c18..3d3c14aeae77888bef6b4710d30eb0542af5e72a 100644 (file)
@@ -38,6 +38,7 @@ sv_jumpspeedcap_min ""
 sv_jumpspeedcap_max ""
 sv_jumpspeedcap_max_disable_on_ramps 0
 g_teleport_maxspeed 0
+g_teleport_minspeed 0
 sv_track_canjump 0
 sv_gameplayfix_stepdown_maxspeed 0
 g_movement_highspeed_q3_compat 0
index c614e817d350d64d1725b9f17da243ffd216379e..7fe21756f74015e5efe5f890c84aaf6cad44d6f7 100644 (file)
@@ -39,6 +39,7 @@ sv_jumpspeedcap_min ""
 sv_jumpspeedcap_max ""
 sv_jumpspeedcap_max_disable_on_ramps 0
 g_teleport_maxspeed 0
+g_teleport_minspeed 0
 sv_track_canjump 0
 sv_gameplayfix_stepdown_maxspeed 0
 g_movement_highspeed_q3_compat 0
index 08fdde547eda7a7f2f9b193f52f2ef3851992875..1e26efb33602e8e74c919ace39f2ed881a266f8b 100644 (file)
@@ -41,8 +41,8 @@ sv_jumpspeedcap_min ""
 // VQ3 has no ramp jumping
 sv_jumpspeedcap_max 270
 sv_jumpspeedcap_max_disable_on_ramps 0
-// FIXME: Q3 teleporters _set_ speed to 400, not cap it
 g_teleport_maxspeed 400
+g_teleport_minspeed 400
 sv_track_canjump 1
 sv_gameplayfix_stepdown_maxspeed 0
 g_movement_highspeed_q3_compat 1
index 4de2fbd88517603b606b3ca9136bffa5a891cac2..c12f3c3ba3998270da07fc7affd61b7538411a29 100644 (file)
@@ -37,6 +37,7 @@ sv_jumpspeedcap_min ""
 sv_jumpspeedcap_max ""
 sv_jumpspeedcap_max_disable_on_ramps 0
 g_teleport_maxspeed 0
+g_teleport_minspeed 0
 sv_track_canjump 0
 sv_gameplayfix_stepdown_maxspeed 0
 g_movement_highspeed_q3_compat 0
index 880504782b2d999ca253d8c1b9dce55d7c8cbbfe..dc2f3a186a3b4e67f6aa0646b0a95a09f6f014b2 100644 (file)
@@ -37,6 +37,7 @@ sv_jumpspeedcap_min ""
 sv_jumpspeedcap_max ""
 sv_jumpspeedcap_max_disable_on_ramps 0
 g_teleport_maxspeed 0
+g_teleport_minspeed 0
 sv_track_canjump 0
 sv_gameplayfix_stepdown_maxspeed 0
 g_movement_highspeed_q3_compat 0
index 88165e03df64da0c0afc56cf714f2898423d53c8..b960eb01b6fb87d9c4dcb12a17c3664379d16958 100644 (file)
@@ -37,6 +37,7 @@ sv_jumpspeedcap_min ""
 sv_jumpspeedcap_max ""
 sv_jumpspeedcap_max_disable_on_ramps 0
 g_teleport_maxspeed 0
+g_teleport_minspeed 0
 sv_track_canjump 0
 sv_gameplayfix_stepdown_maxspeed 0
 g_movement_highspeed_q3_compat 0
index f89927d5d3b18d259ff134cc7a47c637889362b3..189b3307834d090573414ced5d53e09b30a95ccd 100644 (file)
@@ -38,6 +38,7 @@ sv_jumpspeedcap_min ""
 sv_jumpspeedcap_max ""
 sv_jumpspeedcap_max_disable_on_ramps 0
 g_teleport_maxspeed 0
+g_teleport_minspeed 0
 sv_track_canjump 0
 sv_gameplayfix_stepdown_maxspeed 0
 g_movement_highspeed_q3_compat 0
index ac1a96b52a1f9b8306dd2c6d2fdeab1733890295..c891a2b74928704a36d7935794186c611c895771 100644 (file)
@@ -38,6 +38,7 @@ sv_jumpspeedcap_min ""
 sv_jumpspeedcap_max ""
 sv_jumpspeedcap_max_disable_on_ramps 0
 g_teleport_maxspeed 0
+g_teleport_minspeed 0
 sv_track_canjump 0
 sv_gameplayfix_stepdown_maxspeed 0
 g_movement_highspeed_q3_compat 0
index 7f7bcc8d6fbb1ec494ef141a0739b68783bea611..7c9db0fbaeb243566dded3c480ca250853d68944 100644 (file)
@@ -38,6 +38,7 @@ sv_jumpspeedcap_min ""
 sv_jumpspeedcap_max ""
 sv_jumpspeedcap_max_disable_on_ramps 0
 g_teleport_maxspeed 0
+g_teleport_minspeed 0
 sv_track_canjump 0
 sv_gameplayfix_stepdown_maxspeed 0
 g_movement_highspeed_q3_compat 0
index 21ded2a8c859376b438e9b49d7db39e7393bd3b3..f0f984bfa5f397a4a481c0139a145e1dd3883353 100644 (file)
@@ -53,6 +53,7 @@ sv_jumpspeedcap_min ""
 sv_jumpspeedcap_max ""
 sv_jumpspeedcap_max_disable_on_ramps 1
 g_teleport_maxspeed 0
+g_teleport_minspeed 0
 sv_track_canjump 0
 sv_gameplayfix_stepdown_maxspeed 400
 g_movement_highspeed_q3_compat 0
index e350037e60e2da0591270e83014e2c1fcfc6595e..4a6d8bb86051822d8a7a4d8bd0fb27efa667e2b2 100644 (file)
@@ -46,6 +46,7 @@ sv_jumpspeedcap_min ""
 sv_jumpspeedcap_max ""
 sv_jumpspeedcap_max_disable_on_ramps 1
 g_teleport_maxspeed 0
+g_teleport_minspeed 0
 sv_track_canjump 0
 sv_gameplayfix_stepdown_maxspeed 400
 g_movement_highspeed_q3_compat 0
index c7c2250e7c91a74f47cc64a8f6600f2a2f39464c..b3936901c42eb87da825eaed8ca1cb3de676e162 100644 (file)
@@ -52,6 +52,7 @@ sv_jumpspeedcap_min ""
 sv_jumpspeedcap_max ""
 sv_jumpspeedcap_max_disable_on_ramps 1
 g_teleport_maxspeed 0
+g_teleport_minspeed 0
 sv_track_canjump 0
 sv_gameplayfix_stepdown_maxspeed 400
 g_movement_highspeed_q3_compat 0
index 1436bbdcb85bd00952a04855674d21957a779820..8baf5be7ec41282d89a97cc33d1f1e82a6a0dff4 100644 (file)
@@ -44,6 +44,7 @@ sv_jumpspeedcap_min 0
 sv_jumpspeedcap_max 0.5
 sv_jumpspeedcap_max_disable_on_ramps 1
 g_teleport_maxspeed 600
+g_teleport_minspeed 0
 sv_track_canjump 0
 sv_gameplayfix_stepdown_maxspeed 400
 // needed for correct q3 haste simulation
index 1c86e786d1051d79643a7d0199ff4669421f7674..bab680d68605ac8465b8ae6478e2d326c11e6767 100644 (file)
@@ -44,6 +44,7 @@ sv_jumpspeedcap_min ""
 sv_jumpspeedcap_max ""
 sv_jumpspeedcap_max_disable_on_ramps 1
 g_teleport_maxspeed 0
+g_teleport_minspeed 0
 sv_track_canjump 0
 sv_gameplayfix_stepdown_maxspeed 400
 g_movement_highspeed_q3_compat 0
index 78b84fdea6f3da3801773ede0622c4bbd6207cb5..99368d39370e37aa31eea427c12d944616d1c3b3 100644 (file)
@@ -309,11 +309,15 @@ void CSQCPlayer_ModelAppearance_Apply(entity this, bool islocalplayer)
                        this.colormap = 1024 + autocvar_cl_forcemyplayercolors;
                else if (autocvar_cl_forceuniqueplayercolors && !islocalplayer && !gametype.m_1v1)
                {
-                       // Assign each enemy unique colors
+                       // Assign each enemy an unique color combination
                        // pick colors from 0 to 14 since 15 is the rainbow color
                        // pl01 0 1, pl02 1 2, ..., pl14 13 14, pl15 14 0
                        // pl16 0 2, pl17 1 3, ..., pl29 13  0, pl30 14 1
-                       int num = this.entnum - 1;
+                       int num;
+                       if (this.isplayermodel & ISPLAYER_CLIENT)
+                               num = this.entnum - 1;
+                       else
+                               num = this.sv_entnum - 1;
                        int c1 = num % 15;
                        int q = floor(num / 15);
                        int c2 = (c1 + 1 + q) % 15;
index dba2c8ffcd15fbcbabda8296e7646b0d47a9fd08..b72269a9e4b36c36c1b34ee4a49d36b82da47e66 100644 (file)
@@ -15,6 +15,7 @@
 .float anim_start_time; // reusing for bob waveform synchronisation
 .vector angles_held; // reusing for (re)storing original angles
 .float wait, delay, pointtime; // reusing for despawn effects
+.vector m_mins, m_maxs; // reusing for storing standard bbox (same purpose as in SVQC itemdef)
 
 HashMap ENT_CLIENT_ITEM_simple;
 STATIC_INIT(ENT_CLIENT_ITEM_simple)
@@ -69,8 +70,7 @@ void ItemSetModel(entity this, bool wantsimple)
                LOG_WARNF("this.model is unset for item %s", this.classname);
        precache_model(this.model);
        _setmodel(this, this.model);
-       setsize(this, '-16 -16 0', '16 16 48');
-       // bones_was_here TODO: network proper box size for sv_legacy_bbox_expand 0
+       setsize(this, this.m_mins, this.m_maxs);
 }
 
 void ItemDraw(entity this)
@@ -137,9 +137,8 @@ void ItemDraw(entity this)
        if (bobheight != this.origin_z - this.oldorigin_z)
        {
                this.origin_z = this.oldorigin_z + bobheight;
-               this.mins_z = 0 - bobheight; // don't want the absmin and absmax to bob
-               this.maxs_z = 48 - bobheight;
-               // bones_was_here TODO: network proper box size for sv_legacy_bbox_expand 0
+               this.mins_z = this.m_mins.z - bobheight; // don't want the absmin and absmax to bob
+               this.maxs_z = this.m_maxs.z - bobheight;
        }
 
        // set alpha based on distance
@@ -235,13 +234,6 @@ NET_HANDLE(ENT_CLIENT_ITEM, bool isnew)
                this.angles = this.angles_held = ReadAngleVector();
        }
 
-       /* bones_was_here TODO: network proper box size for sv_legacy_bbox_expand 0
-       if(sf & ISF_SIZE)
-       {
-               setsize(this, '-16 -16 0', '16 16 48');
-       }
-       */
-
        if(sf & ISF_STATUS) // need to read/write status first so model can handle simple, fb etc.
        {
                int prevItemStatus = this.ItemStatus;
@@ -280,7 +272,7 @@ NET_HANDLE(ENT_CLIENT_ITEM, bool isnew)
                }
        }
 
-       if(sf & ISF_MODEL)
+       if(sf & ISF_SIZE || sf & ISF_SIZE2) // always true when it's spawned (in CSQC's perspective)
        {
                if(isnew)
                {
@@ -290,6 +282,22 @@ NET_HANDLE(ENT_CLIENT_ITEM, bool isnew)
                        this.entremove = ItemRemove;
                }
 
+               if(sf & ISF_SIZE && !(sf & ISF_SIZE2)) // Small
+               {
+                       this.m_mins = ITEM_S_MINS;
+                       this.m_maxs = ITEM_S_MAXS;
+               }
+               else if(!(sf & ISF_SIZE) && sf & ISF_SIZE2) // Large
+               {
+                       this.m_mins = ITEM_D_MINS;
+                       this.m_maxs = ITEM_L_MAXS;
+               }
+               else // Default
+               {
+                       this.m_mins = ITEM_D_MINS;
+                       this.m_maxs = ITEM_D_MAXS;
+               }
+
                this.fade_end = ReadShort();
 
                strcpy(this.mdl, ReadString());
@@ -323,7 +331,7 @@ NET_HANDLE(ENT_CLIENT_ITEM, bool isnew)
                SET_ONGROUND(this); // extra overkill
        }
 
-       if(sf & ISF_REMOVEFX && !(sf & ISF_SIZE) && !(sf & ISF_MODEL)) // TODO !isnew isn't reliable for this... are we double sending initialisations?
+       if(sf & ISF_REMOVEFX && !(sf & ISF_SIZE) && !(sf & ISF_SIZE2)) // TODO !isnew isn't reliable for this... are we double sending initialisations?
        {
                // no longer available to pick up, about to be removed
                if (this.drawmask) // this.alpha > 0
index 9e214551097dd0dc60e5d3431cbfd4df32b43ecc..e30f9bbcb21d481cbbfe9cb156bf8332f1a3b3cf 100644 (file)
@@ -13,6 +13,7 @@
 #include <client/shownames.qh>
 #include <client/view.qh>
 #include <client/weapons/projectile.qh>
+#include <common/checkextension.qh>
 #include <common/deathtypes/all.qh>
 #include <common/effects/all.inc>
 #include <common/effects/all.qh>
@@ -50,6 +51,8 @@ void CSQC_Init()
        LOG_TRACEF("^4CSQC Build information: ^1%s", WATERMARK);
 #endif
 
+       CheckEngineExtensions();
+
        {
                int i = 0;
                for ( ; i < 255; ++i)
index 0a2cca157e65e54bd9bd3d9fb45b64a2dfdc4eba..b775b9686af21c166eaccff8b3da1688cba3d83c 100644 (file)
@@ -1,5 +1,7 @@
 noref float autocvar_net_connecttimeout = 30;
 
+#include "checkextension.qc"
+
 #ifdef GAMEQC
 #include "anim.qc"
 #include "animdecide.qc"
diff --git a/qcsrc/common/checkextension.qc b/qcsrc/common/checkextension.qc
new file mode 100644 (file)
index 0000000..3e1a69f
--- /dev/null
@@ -0,0 +1,60 @@
+#include "checkextension.qh"
+
+#ifdef GAMEQC
+entity findbox_tofield_Fallback(vector mins, vector maxs, .entity tofield)
+{
+       // 0.03125 minimum radius because findradius returns no results if radius is zero
+       // but findbox for a zero-sized box returns entities touching the specified point
+       entity chain = findradius_tofield(0.5 * (mins + maxs), max(0.03125, 0.5 * vlen(maxs - mins)), tofield);
+       entity prev = NULL;
+       for (entity e = chain; e; e = e.tofield)
+       {
+               if (boxesoverlap(e.absmin, e.absmax, mins, maxs))
+                       prev = e;
+               else // not in box so remove from chain
+               {
+                       if (prev)
+                               prev.tofield = e.tofield;
+                       else
+                               chain = chain.tofield;
+               }
+       }
+       return chain;
+}
+entity findbox_Fallback(vector mins, vector maxs)
+{
+       return findbox_tofield_Fallback(mins, maxs, chain);
+}
+#endif // GAMEQC
+
+void CheckEngineExtensions(void)
+{
+       if (!cvar("pr_checkextension"))
+               LOG_FATAL("Engine lacks the QC extension system.");
+
+       if (!checkextension("DP_QC_URI_GET") || !checkextension("DP_QC_URI_POST"))
+               LOG_WARN("Engine lacks HTTP support, XonStat and map downloads are unavailable.");
+
+       if (!checkextension("DP_CRYPTO"))
+               LOG_WARN("Engine lacks DP_CRYPTO, Player IDs (required for XonStat and CTS/CTF records) are unavailable.");
+
+#ifdef SVQC // change to GAMEQC if/when we use nudgeoutofsolid in CSQC
+       if (!checkextension("DP_QC_NUDGEOUTOFSOLID"))
+       {
+               LOG_WARN("Engine lacks DP_QC_NUDGEOUTOFSOLID, falling back to WarpZoneLib_MoveOutOfSolid().");
+               // DP_QC_NUDGEOUTOFSOLID fixes many cases WarpZoneLib_MoveOutOfSolid() can't, usually in less CPU time
+               nudgeoutofsolid = WarpZoneLib_MoveOutOfSolid;
+       }
+#endif
+
+#ifdef GAMEQC
+       if (!checkextension("DP_QC_FINDBOX"))
+       {
+               LOG_WARN("Engine lacks DP_QC_FINDBOX, performance will be suboptimal.");
+               findbox = findbox_Fallback;
+               findbox_tofield = findbox_tofield_Fallback;
+       }
+#endif
+
+       // TODO: add proper warns/errors for other extensions we depend on
+}
diff --git a/qcsrc/common/checkextension.qh b/qcsrc/common/checkextension.qh
new file mode 100644 (file)
index 0000000..c00cad8
--- /dev/null
@@ -0,0 +1,3 @@
+#pragma once
+
+void CheckEngineExtensions(void);
index 18e4e6100142b9c4ee73c3fb27ab94ec61481db3..e3be1e1d002b3c22fccdc053f39d23ee9f2045c9 100644 (file)
@@ -72,7 +72,7 @@ const int FL_INWATER                          = 16; /* BIT(3) */      // for enter / leave water splash
 const int FL_MONSTER                           = 32; /* BIT(4) */
 const int FL_GODMODE                           = 64; /* BIT(5) */      // player cheat
 const int FL_NOTARGET                          = 128; /* BIT(6) */     // player cheat
-const int FL_ITEM                                      = 256; /* BIT(7) */     // extra wide size for bonus items
+const int FL_ITEM                                      = 256; /* BIT(7) */     // extra wide size for bonus items IF sv_legacy_bbox_expand is 1
 const int FL_ONGROUND                          = 512; /* BIT(8) */     // standing on something
 const int FL_PARTIALGROUND                     = 1024; /* BIT(9) */    // not all corners are valid
 const int FL_WATERJUMP                         = 2048; /* BIT(10) */   // player jumping out of water
index adf9367ef7ee7baeae587d9a041269df482bb131..7155f1807e7f213ff4739c64569634a2de355abc 100644 (file)
@@ -44,6 +44,9 @@
 //   bits above 14 are defined in lib/csqcmodel/common.qh
 #define CSQCMODEL_EXTRAPROPERTIES \
        CSQCMODEL_PROPERTY(BIT(0), int, ReadShort, WriteShort, colormap) \
+       CSQCMODEL_IF(!isplayer) \
+               CSQCMODEL_PROPERTY(BIT(0), int, ReadByte, WriteByte, sv_entnum) \
+       CSQCMODEL_ENDIF \
        CSQCMODEL_PROPERTY(BIT(1), int, ReadInt24_t, WriteInt24_t, effects) \
        CSQCMODEL_PROPERTY(BIT(2), int, ReadByte, WriteByte, modelflags) \
        CSQCMODEL_PROPERTY(BIT(2), int, ReadByte, WriteByte, skin) \
index e16ed913829338802823dddce026e7be7f42d989..4bea0edf4e3bfe1d89d12c45904693245d80f5ac 100644 (file)
@@ -469,6 +469,13 @@ void ctf_Handle_Throw(entity player, entity receiver, int droptype)
        tracebox(player.origin - FLAG_DROP_OFFSET, flag.m_mins, flag.m_maxs, player.origin + FLAG_DROP_OFFSET, MOVE_NOMONSTERS, flag);
        flag.solid = SOLID_TRIGGER; // before setorigin to ensure area grid linking
        setorigin(flag, trace_endpos);
+       if (trace_startsolid && !nudgeoutofsolid(flag)) // TODO: trace_allsolid would perform better but isn't 100% reliable yet
+       {
+               // the flag's bbox doesn't fit but we can assume the player's current bbox does
+               tracebox(player.origin - FLAG_DROP_OFFSET, player.mins, player.maxs, player.origin + FLAG_DROP_OFFSET, MOVE_NOMONSTERS, flag);
+               flag.origin = trace_endpos;
+               setsize(flag, player.mins, player.maxs); // this allows physics to move the flag somewhere its think func can resize it
+       }
        flag.owner.flagcarried = NULL;
        GameRules_scoring_vip(flag.owner, false);
        flag.owner = NULL;
@@ -932,7 +939,6 @@ void ctf_FlagThink(entity this)
 
        // sanity checks
        if(this.mins != this.m_mins || this.maxs != this.m_maxs) { // reset the flag boundaries in case it got squished
-               LOG_TRACE("wtf the flag got squashed?");
                tracebox(this.origin, this.m_mins, this.m_maxs, this.origin, MOVE_NOMONSTERS, this);
                if(!trace_startsolid || this.noalign) // can we resize it without getting stuck?
                        setsize(this, this.m_mins, this.m_maxs);
@@ -1337,7 +1343,8 @@ void ctf_FlagSetup(int teamnum, entity flag) // called when spawning a flag enti
 
        // appearence
        _setmodel(flag, flag.model); // precision set below
-       setsize(flag, CTF_FLAG.m_mins * flag.scale, CTF_FLAG.m_maxs * flag.scale);
+       // 0.8.6 with sv_legacy_bbox_expand 1 did this FL_ITEM expansion in DP
+       setsize(flag, CTF_FLAG.m_mins * flag.scale - '15 15 1', CTF_FLAG.m_maxs * flag.scale + '15 15 1');
        flag.m_mins = flag.mins; // store these for squash checks
        flag.m_maxs = flag.maxs;
        setorigin(flag, (flag.origin + FLAG_SPAWN_OFFSET));
index a82be5a3c10df5df35c76ad37e7e152a4431c79c..3c270b3ad917b841b58dec7534093c8510c860db 100644 (file)
@@ -297,7 +297,7 @@ void dom_controlpoint_setup(entity this)
        if(!this.flags & FL_ITEM)
                IL_PUSH(g_items, this);
        this.flags = FL_ITEM;
-       setsize(this, '-32 -32 -32', '32 32 32');
+       setsize(this, '-48 -48 -32', '48 48 32'); // 0.8.6 used '-32 -32 -32', '32 32 32' with sv_legacy_bbox_expand 1 and FL_ITEM
        setorigin(this, this.origin + '0 0 20');
        droptofloor(this);
 
index c50d0d2fc1b964645738a6629f70c96684bd9f8a..4ba23b197955ca4efdecb6d9ad06a23a178486d1 100644 (file)
@@ -198,6 +198,7 @@ void ka_DropEvent(entity player) // runs any time that a player is supposed to l
        IL_PUSH(g_damagedbycontents, ball);
        ball.effects &= ~EF_NODRAW;
        setorigin(ball, player.origin + '0 0 10');
+       nudgeoutofsolid(ball); // a ball has a horizontally bigger bbox than a player
        ball.velocity = '0 0 200' + '0 100 0'*crandom() + '100 0 0'*crandom();
        ball.owner = NULL;
        navigation_dynamicgoal_set(ball, player);
@@ -240,7 +241,10 @@ void ka_SpawnBalls()
                entity e = new(keepawayball);
                setmodel(e, MDL_KA_BALL);
                e.solid = SOLID_TRIGGER; // before setsize to ensure area grid linking
-               setsize(e, '-16 -16 -20', '16 16 20'); // 20 20 20 was too big, player is only 16 16 24... gotta cheat with the Z (20) axis so that the particle isn't cut off
+               // 20 20 20 was too big, player is only 16 16 24... gotta cheat with the Z (20) axis so that the particle isn't cut off
+               // bones_was_here: that was WITH sv_legacy_bbox_expand 1 and FL_ITEM (mins -= '15 15 1'; maxs += '15 15 1')
+               // it's round so should have a symmetrical bbox, same height as pickup items so it can't be jumped over in any physics
+               setsize(e, '-24 -24 -24', '24 24 24');
                e.damageforcescale = autocvar_g_keepawayball_damageforcescale;
                e.takedamage = DAMAGE_YES;
                e.event_damage = ka_DamageEvent;
index 8396c1e619d0f80c1fca39db7197f224fbee26db..0e79b58ebf6b40b96f2b28b2f4bf793f47f89b18 100644 (file)
@@ -45,8 +45,8 @@ const float KH_KEY_XYSPEED = 45;
 #endif
 const float KH_KEY_WP_ZSHIFT = 20;
 
-const vector KH_KEY_MIN = '-10 -10 -46';
-const vector KH_KEY_MAX = '10 10 3';
+const vector KH_KEY_MIN = '-25 -25 -46'; // 0.8.6 used '-10 -10 -46' with sv_legacy_bbox_expand 1 and FL_ITEM
+const vector KH_KEY_MAX = '25 25 4';     // 0.8.6 used '10 10 3'     with sv_legacy_bbox_expand 1 and FL_ITEM
 const float KH_KEY_BRIGHTNESS = 2;
 
 bool kh_no_radar_circles;
@@ -291,6 +291,7 @@ void kh_Key_Detach(entity key) // runs every time a key is dropped or lost. Runs
        if(!IL_CONTAINS(g_items, key))
                IL_PUSH(g_items, key);
        set_movetype(key, MOVETYPE_TOSS);
+       nudgeoutofsolid(key); // a key has a bigger bbox than a player
        key.pain_finished = time + autocvar_g_balance_keyhunt_delay_return;
        key.damageforcescale = autocvar_g_balance_keyhunt_damageforcescale;
        key.takedamage = DAMAGE_YES;
index 0be1e46639754422310e2d4789bb4d9637d6ca38..507d31750599f1182ca7d14fbd9a2e72a23a6d08 100644 (file)
@@ -179,6 +179,7 @@ void tka_DropEvent(entity player) // runs any time that a player is supposed to
        ball.takedamage = DAMAGE_YES;
        ball.effects &= ~EF_NODRAW;
        setorigin(ball, player.origin + '0 0 10');
+       nudgeoutofsolid(ball); // a ball has a horizontally bigger bbox than a player
        ball.velocity = '0 0 200' + '0 100 0'*crandom() + '100 0 0'*crandom();
        ball.owner = NULL;
        navigation_dynamicgoal_set(ball, player);
@@ -220,7 +221,10 @@ void tka_SpawnBalls()
        {
                entity e = new(keepawayball);
                setmodel(e, MDL_TKA_BALL);
-               setsize(e, '-16 -16 -20', '16 16 20'); // 20 20 20 was too big, player is only 16 16 24... gotta cheat with the Z (20) axis so that the particle isn't cut off
+               // 20 20 20 was too big, player is only 16 16 24... gotta cheat with the Z (20) axis so that the particle isn't cut off
+               // bones_was_here: that was WITH sv_legacy_bbox_expand 1 and FL_ITEM (mins -= '15 15 1'; maxs += '15 15 1')
+               // it's round so should have a symmetrical bbox, same height as pickup items so it can't be jumped over in any physics
+               setsize(e, '-24 -24 -24', '24 24 24');
                e.damageforcescale = autocvar_g_tkaball_damageforcescale;
                e.takedamage = DAMAGE_YES;
                e.solid = SOLID_TRIGGER;
index 4beeda8498a52a4056f5ba101d8797b25c021acc..1b29d71bd526f773b2d0153e3e009dec3903ee39 100644 (file)
@@ -40,7 +40,7 @@ const int IT_PICKUPMASK                       = IT_UNLIMITED_AMMO | IT_UNLIMITED_SUPER
 // item networking
 const int ISF_REMOVEFX          = BIT(0); // technically unnecessary (after the kludge in Item_Think() is reverted), but cheaper and cleaner than using ITS_AVAILABLE
 const int ISF_LOCATION          = BIT(1);
-const int ISF_MODEL             = BIT(2);
+const int ISF_SIZE2             = BIT(2);
 const int ISF_STATUS            = BIT(3);
 const int ISF_COLORMAP          = BIT(4);
 const int ISF_DROP              = BIT(5);
@@ -67,6 +67,14 @@ const float IT_DESPAWNFX_TIME = 1.5;
 // FIXME but updating faster applies the kludge in Item_Think() sooner so it's less noticeable
 const float IT_UPDATE_INTERVAL = 0.0625;
 
+// item bboxes for sv_legacy_bbox_expand 0
+// Small, Default and Large (large maxs are used with default mins)
+const vector ITEM_S_MINS = '-24 -24 0';
+const vector ITEM_S_MAXS = '24 24 48';
+const vector ITEM_D_MINS = '-30 -30 0'; // 0.8.6 set '-16 -16 0' then DP subtracted '15 15 1' but NetRadiant used '-30 -30 0'
+const vector ITEM_D_MAXS = '30 30 48';  // 0.8.6 set '16 16 48' then DP added '15 15 1' but NetRadiant used '30 30 32'
+const vector ITEM_L_MAXS = '30 30 70';  // 0.8.6 set '16 16 80' for powerups, '16 16 70' for megas, '16 16 60' for buffs
+
 .float fade_start;
 .float fade_end;
 
index 341dfec7fa01ce6437df2155108b64193badfa7e..b8c3b408f942c6816e7d67c74680a712cc16a18c 100644 (file)
@@ -3,8 +3,6 @@
 #include "pickup.qh"
 CLASS(Armor, Pickup)
 #ifdef SVQC
-    ATTRIB(Armor, m_mins, vector, '-16 -16 0');
-    ATTRIB(Armor, m_maxs, vector, '16 16 48');
     ATTRIB(Armor, m_pickupevalfunc, float(entity player, entity item), healtharmor_pickupevalfunc);
     ATTRIB(Armor, m_botvalue, int, 5000);
 #endif
@@ -46,6 +44,8 @@ REGISTER_ITEM(ArmorSmall, Armor) {
         this.m_icon             =   "armor"; // compatible with Xonotic v0.8.2 or lower
 #endif
 #ifdef SVQC
+    this.m_mins                 =   ITEM_S_MINS;
+    this.m_maxs                 =   ITEM_S_MAXS;
     this.m_itemid               =   IT_RESOURCE;
     this.m_respawntime          =   GET(g_pickup_respawntime_armor_small);
     this.m_respawntimejitter    =   GET(g_pickup_respawntimejitter_armor_small);
@@ -88,6 +88,8 @@ REGISTER_ITEM(ArmorMedium, Armor) {
         this.m_icon             =   "armor"; // compatible with Xonotic v0.8.2 or lower
 #endif
 #ifdef SVQC
+    this.m_mins                 =   ITEM_S_MINS;
+    this.m_maxs                 =   ITEM_S_MAXS;
     this.m_itemid               =   IT_RESOURCE;
     this.m_respawntime          =   GET(g_pickup_respawntime_armor_medium);
     this.m_respawntimejitter    =   GET(g_pickup_respawntimejitter_armor_medium);
@@ -177,7 +179,7 @@ REGISTER_ITEM(ArmorMega, Armor) {
     this.m_waypoint             =   _("Mega armor");
     this.m_waypointblink        =   2;
 #ifdef SVQC
-    this.m_maxs                 =   '16 16 70';
+    this.m_maxs                 =   ITEM_L_MAXS;
     this.m_itemid               =   IT_RESOURCE;
     this.m_respawntime          =   GET(g_pickup_respawntime_armor_mega);
     this.m_respawntimejitter    =   GET(g_pickup_respawntimejitter_armor_mega);
index a2664cc516b5bf0c6d7aeee46ec67e01509e6499..a028f28c33142b7a10136b015868ce05c2800bc0 100644 (file)
@@ -3,8 +3,6 @@
 #include "pickup.qh"
 CLASS(Health, Pickup)
 #ifdef SVQC
-    ATTRIB(Health, m_mins, vector, '-16 -16 0');
-    ATTRIB(Health, m_maxs, vector, '16 16 48');
     ATTRIB(Health, m_pickupevalfunc, float(entity player, entity item), healtharmor_pickupevalfunc);
     ATTRIB(Health, m_botvalue, int, 5000);
 #endif
@@ -46,6 +44,8 @@ REGISTER_ITEM(HealthSmall, Health) {
         this.m_icon             =   "health"; // compatible with Xonotic v0.8.2 or lower
 #endif
 #ifdef SVQC
+    this.m_mins                 =   ITEM_S_MINS;
+    this.m_maxs                 =   ITEM_S_MAXS;
     this.m_itemid               =   IT_RESOURCE;
     this.m_respawntime          =   GET(g_pickup_respawntime_health_small);
     this.m_respawntimejitter    =   GET(g_pickup_respawntimejitter_health_small);
@@ -89,6 +89,8 @@ REGISTER_ITEM(HealthMedium, Health) {
         this.m_icon             =   "health"; // compatible with Xonotic v0.8.2 or lower
 #endif
 #ifdef SVQC
+    this.m_mins                 =   ITEM_S_MINS;
+    this.m_maxs                 =   ITEM_S_MAXS;
     this.m_itemid               =   IT_RESOURCE;
     this.m_respawntime          =   GET(g_pickup_respawntime_health_medium);
     this.m_respawntimejitter    =   GET(g_pickup_respawntimejitter_health_medium);
@@ -178,7 +180,7 @@ REGISTER_ITEM(HealthMega, Health) {
     this.m_waypoint             =   _("Mega health");
     this.m_waypointblink        =   2;
 #ifdef SVQC
-    this.m_maxs                 =   '16 16 70';
+    this.m_maxs                 =   ITEM_L_MAXS;
     this.m_itemid               =   IT_RESOURCE;
     this.m_respawntime          =   GET(g_pickup_respawntime_health_mega);
     this.m_respawntimejitter    =   GET(g_pickup_respawntimejitter_health_mega);
index 6df6baf83a90682dd37c3eecd92e9c9c8ca673ee..95c9d81a019eba844499bb714ad40d23ff326d21 100644 (file)
@@ -45,8 +45,8 @@ CLASS(Pickup, GameItem)
     }
     ATTRIB(Pickup, m_itemid, int, 0);
 #ifdef SVQC
-    ATTRIB(Pickup, m_mins, vector, '-16 -16 0');
-    ATTRIB(Pickup, m_maxs, vector, '16 16 48');
+    ATTRIB(Pickup, m_mins, vector, ITEM_D_MINS);
+    ATTRIB(Pickup, m_maxs, vector, ITEM_D_MAXS);
     ATTRIB(Pickup, m_botvalue, int, 0);
     ATTRIB(Pickup, m_itemflags, int, 0);
     float generic_pickupevalfunc(entity player, entity item);
index 3666dd337b2c56ffed1b03ca54d1339dfe98d4e8..724fb929072a11adb92d5eb7352a819a2ce61dbc 100644 (file)
@@ -21,20 +21,12 @@ void conveyor_think(entity this)
        {
                FOREACH_ENTITY_RADIUS((this.absmin + this.absmax) * 0.5, vlen(this.absmax - this.absmin) * 0.5 + 1, it.conveyor.active == ACTIVE_NOT && isPushable(it),
                {
-                       vector emin = it.absmin;
-                       vector emax = it.absmax;
-                       if(this.solid == SOLID_BSP)
+                       if (WarpZoneLib_ExactTrigger_Touch(this, it, false))
                        {
-                               emin -= '1 1 1';
-                               emax += '1 1 1';
+                               if(!it.conveyor)
+                                       IL_PUSH(g_conveyed, it);
+                               it.conveyor = this;
                        }
-                       if(boxesoverlap(emin, emax, this.absmin, this.absmax)) // quick
-                               if(WarpZoneLib_BoxTouchesBrush(emin, emax, this, it)) // accurate
-                               {
-                                       if(!it.conveyor)
-                                               IL_PUSH(g_conveyed, it);
-                                       it.conveyor = this;
-                               }
                });
 
                IL_EACH(g_conveyed, it.conveyor == this,
index 3d94c1a2d13ecf51e87d1a7b3f9d8d66635d2710..a1b374b868dfe2878481f2ca36ad2246b055f2c5 100644 (file)
@@ -19,21 +19,11 @@ void func_ladder_think(entity this)
 
        FOREACH_ENTITY_RADIUS((this.absmin + this.absmax) * 0.5, vlen(this.absmax - this.absmin) * 0.5 + 1, !it.ladder_entity && IS_PLAYER(it) && it.move_movetype != MOVETYPE_NOCLIP && !IS_DEAD(it),
        {
-               vector emin = it.absmin;
-               vector emax = it.absmax;
-               if(this.solid == SOLID_BSP || (IS_CSQC && this.solid == SOLID_TRIGGER)) // CSQC doesn't expand properly
+               if (WarpZoneLib_ExactTrigger_Touch(this, it, false))
                {
-                       emin -= '1 1 1';
-                       emax += '1 1 1';
-               }
-               if(boxesoverlap(emin, emax, this.absmin, this.absmax)) // quick
-               {
-                       if(WarpZoneLib_BoxTouchesBrush(emin, emax, this, it)) // accurate
-                       {
-                               if(!it.ladder_entity)
-                                       IL_PUSH(g_ladderents, it);
-                               it.ladder_entity = this;
-                       }
+                       if(!it.ladder_entity)
+                               IL_PUSH(g_ladderents, it);
+                       it.ladder_entity = this;
                }
        });
 
@@ -178,10 +168,6 @@ NET_HANDLE(ENT_CLIENT_LADDER, bool isnew)
        this.move_time = time;
        this.entremove = func_ladder_remove;
 
-       // NOTE: CSQC's version of setorigin doesn't expand
-       this.absmin -= '1 1 1';
-       this.absmax += '1 1 1';
-
        return true;
 }
 #endif
index 651566c71b6fbec9ff4fd7ef5052a14dea7919ce..98864129cf20daa9e12d8d562a31995b8bc2e7f3 100644 (file)
@@ -225,9 +225,16 @@ entity Simple_TeleportPlayer(entity teleporter, entity player)
 
        if(!(teleporter.classname == "trigger_teleport" && (teleporter.spawnflags & TELEPORT_KEEP_SPEED)) &&
           !(teleporter.classname == "target_teleporter" && (teleporter.spawnflags & TARGET_TELEPORTER_KEEP_SPEED)))
+       {
+               // test if player is slower than min
+               if(STAT(TELEPORT_MINSPEED, player))
+                       if(vdist(player.velocity, <, STAT(TELEPORT_MINSPEED, player)))
+                               player.velocity = normalize(player.velocity) * max(0, STAT(TELEPORT_MINSPEED, player));
+               // test if player is faster than max (or if min is above max)
                if(STAT(TELEPORT_MAXSPEED, player))
                        if(vdist(player.velocity, >, STAT(TELEPORT_MAXSPEED, player)))
                                player.velocity = normalize(player.velocity) * max(0, STAT(TELEPORT_MAXSPEED, player));
+       }
 
        locout = e.origin + '0 0 1' * (1 - player.mins.z - 24);
 
index a54665962c975ea494677ac83c231c36f266a6bf..061c621313494256c7b4666bbb64d792ff769e3c 100644 (file)
@@ -31,20 +31,12 @@ void swamp_think(entity this)
        {
                FOREACH_ENTITY_RADIUS((this.absmin + this.absmax) * 0.5, vlen(this.absmax - this.absmin) * 0.5 + 1, it.swampslug.active == ACTIVE_NOT && IS_PLAYER(it) && !IS_DEAD(it),
                {
-                       vector emin = it.absmin;
-                       vector emax = it.absmax;
-                       if(this.solid == SOLID_BSP)
+                       if (WarpZoneLib_ExactTrigger_Touch(this, it, false))
                        {
-                               emin -= '1 1 1';
-                               emax += '1 1 1';
+                               if(!it.swampslug)
+                                       IL_PUSH(g_swamped, it);
+                               it.swampslug = this;
                        }
-                       if(boxesoverlap(emin, emax, this.absmin, this.absmax)) // quick
-                               if(WarpZoneLib_BoxTouchesBrush(emin, emax, this, it)) // accurate
-                               {
-                                       if(!it.swampslug)
-                                               IL_PUSH(g_swamped, it);
-                                       it.swampslug = this;
-                               }
                });
 
                IL_EACH(g_swamped, it.swampslug == this,
index eb3ae7f4b67e6ba3a07ee3b1d1c9d6dc271e7c8b..9efaf9588d55a17d03e9d8ee549034c0ddff132e 100644 (file)
@@ -32,37 +32,16 @@ void viewloc_think(entity this)
 #if 1
        FOREACH_CLIENT(!it.viewloc && IS_PLAYER(it),
        {
-               vector emin = it.absmin;
-               vector emax = it.absmax;
-               if(this.solid == SOLID_BSP)
-               {
-                       emin -= '1 1 1';
-                       emax += '1 1 1';
-               }
-               if(boxesoverlap(emin, emax, this.absmin, this.absmax)) // quick
-               {
-                       if(WarpZoneLib_BoxTouchesBrush(emin, emax, this, it)) // accurate
-                               it.viewloc = this;
-               }
+               if (WarpZoneLib_ExactTrigger_Touch(this, it, false))
+                       it.viewloc = this;
        });
 #else
-
-               for(e = findradius((this.absmin + this.absmax) * 0.5, vlen(this.absmax - this.absmin) * 0.5 + 1); e; e = e.chain)
-                       if(!e.viewloc)
-                               if(IS_PLAYER(e)) // should we support non-player entities with this?
-                               //if(!IS_DEAD(e)) // death view is handled separately, we can't override this just yet
-                               {
-                                       vector emin = e.absmin;
-                                       vector emax = e.absmax;
-                                       if(this.solid == SOLID_BSP)
-                                       {
-                                               emin -= '1 1 1';
-                                               emax += '1 1 1';
-                                       }
-                                       if(boxesoverlap(emin, emax, this.absmin, this.absmax)) // quick
-                                               if(WarpZoneLib_BoxTouchesBrush(emin, emax, this, e)) // accurate
-                                                       e.viewloc = this;
-                               }
+       for(e = findradius((this.absmin + this.absmax) * 0.5, vlen(this.absmax - this.absmin) * 0.5 + 1); e; e = e.chain)
+               if(!e.viewloc)
+                       if(IS_PLAYER(e)) // should we support non-player entities with this?
+                       //if(!IS_DEAD(e)) // death view is handled separately, we can't override this just yet
+                               if (WarpZoneLib_ExactTrigger_Touch(this, it, false))
+                                       e.viewloc = this;
 #endif
 
        this.nextthink = time;
index 5014aaed55f6fa7e9027e7964b91631b33d9ab43..e51c7cc9d6d97696511c6df3323ddbc8365aa62a 100644 (file)
@@ -314,7 +314,7 @@ void buff_Think(entity this)
                this.skin = buff.m_skin;
 
                setmodel(this, MDL_BUFF);
-               setsize(this, BUFF_MIN, BUFF_MAX);
+               setsize(this, ITEM_D_MINS, ITEM_L_MAXS);
 
                if(this.buff_waypoint)
                {
@@ -440,7 +440,7 @@ void buff_Init(entity this)
        setthink(this, buff_Think);
        settouch(this, buff_Touch);
        setmodel(this, MDL_BUFF);
-       setsize(this, BUFF_MIN, BUFF_MAX);
+       setsize(this, ITEM_D_MINS, ITEM_L_MAXS);
        this.reset = buff_Reset;
        this.nextthink = time + 0.1;
        this.gravity = 1;
index c8b2b363d12e6e704b214036e6cf33b44cfc0c09..c8281a003372bf8f909df8632c8e3f0d9323e6ee 100644 (file)
@@ -74,9 +74,6 @@ float autocvar_g_buffs_luck_damagemultiplier = 3;
 .float buff_shield; // delay for players to keep them from spamming buff pickups
 .entity buff_model; // controls effects (TODO: make csqc)
 
-const vector BUFF_MIN = ('-16 -16 0');
-const vector BUFF_MAX = ('16 16 60');
-
 float buff_Available(entity buff);
 
 void buff_RemoveAll(entity actor, int removal_type);
index 3a614e38829485e838a2fa8c54b5135080424219..ecd754672d3bbed1e9fbe544a71b4065e8b2bd2c 100644 (file)
@@ -3,8 +3,7 @@
 #include <common/items/item/pickup.qh>
 CLASS(Powerup, Pickup)
 #ifdef SVQC
-    ATTRIB(Powerup, m_mins, vector, '-16 -16 0');
-    ATTRIB(Powerup, m_maxs, vector, '16 16 80');
+    ATTRIB(Powerup, m_maxs, vector, ITEM_L_MAXS);
     ATTRIB(Powerup, m_botvalue, int, 11000);
     ATTRIB(Powerup, m_itemflags, int, FL_POWERUP);
     ATTRIB(Powerup, m_respawntime, float(), GET(g_pickup_respawntime_powerup));
index 651b6f3d642e7f861fe1cb4adcb439b5a7d6a8e3..73ceb57b0621f85ce638add082b9ffb0b268a58f 100644 (file)
@@ -472,10 +472,18 @@ void _Movetype_LinkEdict_TouchAreaGrid(entity this)  // SV_LinkEdict_TouchAreaGr
        int save_trace_dphitq3surfaceflags = trace_dphitq3surfaceflags;
        string save_trace_dphittexturename = trace_dphittexturename;
 
-    FOREACH_ENTITY_RADIUS_ORDERED(0.5 * (this.absmin + this.absmax), 0.5 * vlen(this.absmax - this.absmin), true, {
+       vector emin = this.absmin, emax = this.absmax;
+       // Xonotic and Nexuiz maps assume triggers will be activated by adjacent players
+       // prior to sv_legacy_bbox_expand 0 DP always did this for SVQC and never for CSQC
+       // we also need this for zero-size bboxes because radius == 0 returns nothing
+       // see also: WarpZoneLib_ExactTrigger_Touch()
+       emin -= '1 1 1';
+       emax += '1 1 1';
+
+       FOREACH_ENTITY_RADIUS_ORDERED(0.5 * (this.absmin + this.absmax), 0.5 * vlen(emin - emax), true, {
                if (it.solid == SOLID_TRIGGER && it != this)
                if (it.move_nomonsters != MOVE_NOMONSTERS && it.move_nomonsters != MOVE_WORLDONLY)
-               if (gettouch(it) && boxesoverlap(it.absmin, it.absmax, this.absmin, this.absmax))
+               if (gettouch(it) && boxesoverlap(it.absmin, it.absmax, emin, emax))
                {
                        trace_allsolid = false;
                        trace_startsolid = false;
@@ -493,7 +501,7 @@ void _Movetype_LinkEdict_TouchAreaGrid(entity this)  // SV_LinkEdict_TouchAreaGr
 
                        gettouch(it)(it, this);
                }
-    });
+       });
 
        trace_allsolid = save_trace_allsolid;
        trace_startsolid = save_trace_startsolid;
@@ -515,44 +523,11 @@ void _Movetype_LinkEdict(entity this, bool touch_triggers)  // SV_LinkEdict
 {
        if(autocvar__movetype_debug)
        {
-               vector mi, ma;
-               if(this.solid == SOLID_BSP)
-               {
-                       // TODO set the absolute bbox
-                       mi = this.mins;
-                       ma = this.maxs;
-               }
-               else
-               {
-                       mi = this.mins;
-                       ma = this.maxs;
-               }
-               mi += this.origin;
-               ma += this.origin;
-
-               if(this.flags & FL_ITEM)
-               {
-                       mi -= '15 15 1';
-                       ma += '15 15 1';
-               }
-               else
-               {
-                       mi -= '1 1 1';
-                       ma += '1 1 1';
-               }
-
-               this.absmin = mi;
-               this.absmax = ma;
+               this.absmin = this.origin + this.mins;
+               this.absmax = this.origin + this.maxs;
        }
        else
-       {
                setorigin(this, this.origin); // calls SV_LinkEdict
-       #ifdef CSQC
-               // NOTE: CSQC's version of setorigin doesn't expand
-               this.absmin -= '1 1 1';
-               this.absmax += '1 1 1';
-       #endif
-       }
 
        if(touch_triggers)
                _Movetype_LinkEdict_TouchAreaGrid(this);
@@ -708,7 +683,10 @@ bool _Movetype_PushEntity(entity this, vector push, bool dolink)  // SV_PushEnti
                _Movetype_PushEntityTrace(this, push);
                this.move_nomonsters = oldtype;
                if(trace_startsolid)
+               {
+                       trace_fraction = 0;
                        return true;
+               }
        }
 
        this.origin = trace_endpos;
index c23ab4384b7651ffb0e284da0ce8b353d1272f85..5c254811d181c2cac9f5a5496490ae442241d403 100644 (file)
@@ -50,14 +50,17 @@ void _Movetype_Physics_Toss(entity this, float dt)  // SV_Physics_Toss
        float movetime = dt;
        for (int bump = 0; bump < MAX_CLIP_PLANES && movetime > 0; bump++)
        {
+               if(this.velocity == '0 0 0')
+                       break;
+
                vector move = this.velocity * movetime;
                if(!_Movetype_PushEntity(this, move, true))
-                       return;
+                       return; // teleported
                if (wasfreed(this))
                        return;
 
                // NOTE: this is bmodelstartsolid in the engine
-               if (trace_startsolid && trace_ent.solid == SOLID_BSP)
+               if (trace_allsolid && trace_fraction == 0 && trace_ent.solid == SOLID_BSP)
                {
                        // QC lacks pointers so we must save the old trace values
                        float oldtrace_fraction = trace_fraction;
@@ -68,9 +71,16 @@ void _Movetype_Physics_Toss(entity this, float dt)  // SV_Physics_Toss
                        trace_plane_normal = oldtrace_plane_normal;
                        trace_ent = oldtrace_ent;
                        if(!_Movetype_PushEntity(this, move, true))
-                               return;
+                               return; // teleported
                        if (wasfreed(this))
                                return;
+                       if (trace_allsolid && trace_fraction == 0)
+                       {
+                               // immovably stuck, don't waste CPU trying to move again
+                               this.velocity = '0 0 0';
+                               SET_ONGROUND(this);
+                               return;
+                       }
                }
 
                if (trace_fraction == 1)
index 548246f5fc45a75b82af918a1f9bd9aa70f2b145..9744eda19231c450315c6984af9a690bdae6b36f 100644 (file)
@@ -288,11 +288,11 @@ void PlayerStats_GameReport_Init() // initiated before InitGameplayMode so that
        {
                PlayerStats_GameReport_DelayMapVote = true;
 
-               serverflags |= SERVERFLAG_PLAYERSTATS;
                if(autocvar_g_playerstats_gamereport_uri != cvar_defstring("g_playerstats_gamereport_uri"))
-               {
-                       serverflags |= SERVERFLAG_PLAYERSTATS_CUSTOM;
-               }
+                       serverflags |= SERVERFLAG_PLAYERSTATS | SERVERFLAG_PLAYERSTATS_CUSTOM;
+               else if(checkextension("DP_CRYPTO") && checkextension("DP_QC_URI_POST"))
+                       // XonStat submission requires player and server IDs, and HTTPS POST
+                       serverflags |= SERVERFLAG_PLAYERSTATS;
 
                PlayerStats_GameReport_AddEvent(PLAYERSTATS_ALIVETIME);
                PlayerStats_GameReport_AddEvent(PLAYERSTATS_AVGLATENCY);
index b7be1425c02c535c3354e91a9edc9af2613c8177..9b4394e45a62f1d0a2851555945521740c7c8e71 100644 (file)
@@ -343,8 +343,10 @@ REGISTER_STAT(NB_METERSTART, float)
 
 #ifdef SVQC
 float autocvar_g_teleport_maxspeed;
+float autocvar_g_teleport_minspeed;
 #endif
 REGISTER_STAT(TELEPORT_MAXSPEED, float, autocvar_g_teleport_maxspeed)
+REGISTER_STAT(TELEPORT_MINSPEED, float, autocvar_g_teleport_minspeed)
 REGISTER_STAT(TELEPORT_TELEFRAG_AVOID, int, autocvar_g_telefrags_avoid)
 
 REGISTER_STAT(CAMERA_SPECTATOR, int)
index 9a1f7b26b5f4a023044d863bd1afea60a8f2fe7c..00999a03f7f1b575a90bfd1a18a485855ad2f834 100644 (file)
@@ -118,9 +118,10 @@ void W_Porto_Fail(entity this, float failhard)
 
        if(this.cnt < 0 && !failhard && this.realowner.playerid == this.playerid && !IS_DEAD(this.realowner) && !(STAT(WEAPONS, this.realowner) & WEPSET(PORTO)))
        {
-               setsize(this, '-16 -16 0', '16 16 48');
+               // FIXME: item properties should be obtained from the registry
+               setsize(this, ITEM_D_MINS, ITEM_D_MAXS);
                setorigin(this, this.origin + trace_plane_normal);
-               if(move_out_of_solid(this))
+               if(nudgeoutofsolid(this))
                {
                        this.flags = FL_ITEM;
                        IL_PUSH(g_items, this);
index f33202f939ddaf975aa785b3fdd924372d6e1d11..2a94fcf09739876f5273f4a70514e1a1662c1b6b 100644 (file)
@@ -34,65 +34,55 @@ void print_assertfailed_fatal(string expr);
 #define ASSERT_LESS(name, var, const) noref int name[(const - var + 1)];
 
 
-#if defined(MENUQC)
-string(string, string...) strcat1n = #53;
-#else
-string(string, string...) strcat1n = #115;
-#endif
-
 // would be nice if __FUNC__ could be concatenated at compile time
 #if 0
        // less work, bigger binary
-       #define __SOURCELOC__ (sprintf("^7%s^9(^9"__FILE__"^7:^9"STR(__LINE__)"^7)", __FUNC__))
+       #define __SOURCELOC__ (sprintf("^7%s^9(^9"__FILE__"^7:^9"STR(__LINE__)"^7)\n", __FUNC__))
 #else
-       #define __SOURCELOC__ (sprintf("^7%s^9(^9%s^7:^9%s^7)", __FUNC__, __FILE__, STR(__LINE__)))
+       #define __SOURCELOC__ (sprintf("^7%s^9(^9%s^7:^9%s^7)\n", __FUNC__, __FILE__, STR(__LINE__)))
 #endif
 
-#define _LOG_HEADER(level) "^9[::^7"PROGNAME"^9::"level"^9] ", __SOURCELOC__
-#define _LOG(f, level, s) \
+#define _LOG_HEADER(level, full) strcat("^9[::^7", PROGNAME, "^9::", level, "^9] ", ((full) ? __SOURCELOC__ : ""))
+
+#define _LOG(func_header, level, func_msg, s) \
        MACRO_BEGIN \
-               if (autocvar_developer > 0) f(strcat1n(_LOG_HEADER(level), "\n")); \
-               f(strcat1n("^7", s, "\n")); \
+               func_header(_LOG_HEADER(level, autocvar_developer > 0)); \
+               func_msg(strcat("^7", s, "\n")); \
        MACRO_END
 
-#define  LOG_FATAL(...) _LOG_FATAL(strcat1n(__VA_ARGS__))
+#define  LOG_FATAL(...) _LOG_FATAL(strcat(__VA_ARGS__))
 #define  LOG_FATALF(...) _LOG_FATAL(sprintf(__VA_ARGS__))
-#define _LOG_FATAL(s) _LOG(error, "^1FATAL", s)
+#define _LOG_FATAL(s) _LOG(print, "^1FATAL", error, s)
 
-#define  LOG_SEVERE(...) _LOG_SEVERE(strcat1n(__VA_ARGS__))
+#define  LOG_SEVERE(...) _LOG_SEVERE(strcat(__VA_ARGS__))
 #define  LOG_SEVEREF(...) _LOG_SEVERE(sprintf(__VA_ARGS__))
-#define _LOG_SEVERE(s) _LOG(backtrace, "^1SEVERE", s)
+#define _LOG_SEVERE(s) _LOG(print, "^1SEVERE", backtrace, s)
 
-#define  LOG_WARN(...) _LOG_WARN(strcat1n(__VA_ARGS__))
+#define  LOG_WARN(...) _LOG_WARN(strcat(__VA_ARGS__))
 #define  LOG_WARNF(...) _LOG_WARN(sprintf(__VA_ARGS__))
-#define _LOG_WARN(s) _LOG(print, "^3WARNING", s)
+#define _LOG_WARN(s) _LOG(print, "^3WARNING", print, s)
 
-#define  LOG_INFO(...) _LOG_INFO(strcat1n(__VA_ARGS__))
+#define  LOG_INFO(...) _LOG_INFO(strcat(__VA_ARGS__))
 #define  LOG_INFOF(...) _LOG_INFO(sprintf(__VA_ARGS__))
 #define _LOG_INFO(s) \
        MACRO_BEGIN \
-               if (autocvar_developer > 1) dprint(strcat1n(_LOG_HEADER("^5INFO"), "\n")); \
+               if (autocvar_developer > 0) print(_LOG_HEADER("^5INFO", autocvar_developer > 1)); \
                string __s = s; \
                print("^7", __s); \
                /* TODO: unconditionally add a newline when possible */ \
                if (str2chr(__s, strlen(__s) - 1) != '\n') { print("\n"); } \
        MACRO_END
 
-#define  LOG_TRACE(...) _LOG_TRACE(strcat1n(__VA_ARGS__))
+#define  LOG_TRACE(...) _LOG_TRACE(strcat(__VA_ARGS__))
 #define  LOG_TRACEF(...) _LOG_TRACE(sprintf(__VA_ARGS__))
-#define _LOG_TRACE(s) _LOG(dprint, "^6TRACE", s)
+#define _LOG_TRACE(s) _LOG(dprint, "^6TRACE", dprint, s)
 
-#define  LOG_DEBUG(...) _LOG_DEBUG(strcat1n(__VA_ARGS__))
+#define  LOG_DEBUG(...) _LOG_DEBUG(strcat(__VA_ARGS__))
 #define  LOG_DEBUGF(...) _LOG_DEBUG(sprintf(__VA_ARGS__))
-#define _LOG_DEBUG(s) _LOG(dprint2, "^2DEBUG", s)
-
-#define dprint2(msg) \
-       MACRO_BEGIN \
-               if (autocvar_developer > 1) dprint(msg); \
-       MACRO_END
+#define _LOG_DEBUG(s) if (autocvar_developer > 1) _LOG(dprint, "^2DEBUG", dprint, s)
 
 // same as LOG_INFO but without any debug information that bloats console output and compiled program files
-#define  LOG_HELP(...) _LOG_HELP(strcat1n(__VA_ARGS__))
+#define  LOG_HELP(...) _LOG_HELP(strcat(__VA_ARGS__))
 #define  LOG_HELPF(...) _LOG_HELP(sprintf(__VA_ARGS__))
 #define _LOG_HELP(s) \
        MACRO_BEGIN \
index d9a12517d1512efa663e3d5234eae467156edaf8..7858311bcecdb78404d6999fc56adc5e6969ba95 100644 (file)
@@ -811,12 +811,12 @@ entity WarpZone_RefSys_SpawnSameRefSys(entity me)
 bool WarpZoneLib_ExactTrigger_Touch(entity this, entity toucher, bool touchfunc)
 {
        vector emin = toucher.absmin, emax = toucher.absmax;
-       if(STAT(Q3COMPAT))
+       if (!Q3COMPAT_COMMON)
        {
-               // DP's tracebox enlarges absolute bounding boxes by a single quake unit
-               // we must undo that here to allow accurate touching
-               emin += '1 1 1';
-               emax -= '1 1 1';
+               // Xonotic and Nexuiz maps assume triggers will be activated by adjacent players
+               // prior to sv_legacy_bbox_expand 0 DP always did this for SVQC and never for CSQC
+               emin -= '1 1 1';
+               emax += '1 1 1';
        }
 
        // if called from a touch func, we can assume the boxes do overlap
@@ -841,16 +841,16 @@ void WarpZoneLib_MoveOutOfSolid_Expand(entity e, vector by)
        }
 }
 
-bool WarpZoneLib_MoveOutOfSolid(entity e)
+int WarpZoneLib_MoveOutOfSolid(entity e)
 {
        vector o = e.origin;
        traceline(o, o, MOVE_WORLDONLY, e);
        if (trace_startsolid)
-               return false;
+               return 0; // too stuck, giving up
 
        tracebox(o, e.mins, e.maxs, o, MOVE_WORLDONLY, e);
        if (!trace_startsolid)
-               return true;
+               return -1; // wasn't stuck
 
        vector m0 = e.mins;
        vector m1 = e.maxs;
@@ -868,8 +868,8 @@ bool WarpZoneLib_MoveOutOfSolid(entity e)
        if (trace_startsolid)
        {
                setorigin(e, o);
-               return false;
+               return 0; // can't fix
        }
 
-       return true;
+       return 1; // was stuck but is fixed now
 }
index f73d079792af9f663775cb4b5294718a16bba03d..f80b5a63873325d50fd67799ad8cb50634df92ea 100644 (file)
@@ -106,7 +106,7 @@ entity WarpZone_RefSys_SpawnSameRefSys(entity me); // spawn().R = me.R
 #ifndef BITXOR_ASSIGN
 # define BITXOR_ASSIGN(a,b) ((a) = ((a) | (b)) - ((a) & (b)))
 #endif
-bool WarpZoneLib_MoveOutOfSolid(entity e);
+int WarpZoneLib_MoveOutOfSolid(entity e);
 #define move_out_of_solid(e) WarpZoneLib_MoveOutOfSolid(e)
 
 bool WarpZoneLib_ExactTrigger_Touch(entity this, entity toucher, bool touchfunc);
index fe5bf07d43654e9f8867913696490dcfce75ca10..567d17941e45d9610102c45fac5e5bae04acabd5 100644 (file)
@@ -1,6 +1,5 @@
 #pragma once
 
-bool WarpZoneLib_MoveOutOfSolid(entity e);
 #ifdef SVQC
 void WarpZoneLib_ExactTrigger_Init(entity this, bool unsetmodel);
 #endif
index 7796cbccaa1de271031755ca8f1d7a6316aaa647..1edf6bfbaf261fbc59a3ba57dbe72d61bceba507 100644 (file)
@@ -17,6 +17,7 @@
 
 #include "xonotic/util.qh"
 
+#include <common/checkextension.qh>
 #include <common/items/_mod.qh>
 #include <common/weapons/_all.qh>
 #include <common/mapinfo.qh>
@@ -76,6 +77,8 @@ void m_init()
                LOG_TRACEF("^4MQC Build information: ^1%s", WATERMARK);
 #endif
 
+       CheckEngineExtensions();
+
        // list all game dirs (TEST)
        if (cvar("developer") > 0)
        {
index 98069e0abe527a2a4620f2be8e5c490d9cdc90df..8fc33448b9401cb5c1f5faf6f246c0c6b3429d28 100644 (file)
@@ -225,8 +225,8 @@ bool navigation_checkladders(entity e, vector org, vector m1, vector m2, vector
        IL_EACH(g_ladders, true,
        {
                if(it.bot_pickup)
-               if(boxesoverlap(org + m1 + '-1 -1 -1', org + m2 + '1 1 1', it.absmin, it.absmax))
-               if(boxesoverlap(end, end2, it.absmin + vec2(m1) + '-1 -1 0', it.absmax + vec2(m2) + '1 1 0'))
+               if(boxesoverlap(org + m1, org + m2, it.absmin, it.absmax))
+               if(boxesoverlap(end, end2, it.absmin + vec2(m1), it.absmax + vec2(m2)))
                {
                        vector top = org;
                        top.z = it.absmax.z + (PL_MAX_CONST.z - PL_MIN_CONST.z);
index 8618dad98859fb7576550a4eb2e9bfabd649e920..0d2ed77acfaf5ec76485cfc1b0e15e3946336802 100644 (file)
@@ -647,7 +647,7 @@ void waypoint_spawn_fromeditor(entity pl, bool at_crosshair, bool is_jump_wp, bo
                        e = it; break;
                });
                if (!e)
-                       e = waypoint_spawn(jp.absmin - PL_MAX_CONST + '1 1 1', jp.absmax - PL_MIN_CONST + '-1 -1 -1', WAYPOINTFLAG_TELEPORT);
+                       e = waypoint_spawn(jp.absmin - PL_MAX_CONST, jp.absmax - PL_MIN_CONST, WAYPOINTFLAG_TELEPORT);
                if (!pl.wp_locked)
                        pl.wp_locked = e;
        }
@@ -2062,7 +2062,7 @@ void waypoint_spawnforteleporter_wz(entity e, entity tracetest_ent)
 void waypoint_spawnforteleporter(entity e, vector destination, float timetaken, entity tracetest_ent)
 {
        destination = waypoint_fixorigin(destination, tracetest_ent);
-       waypoint_spawnforteleporter_boxes(e, WAYPOINTFLAG_TELEPORT, e.absmin - PL_MAX_CONST + '1 1 1', e.absmax - PL_MIN_CONST + '-1 -1 -1', destination, destination, timetaken);
+       waypoint_spawnforteleporter_boxes(e, WAYPOINTFLAG_TELEPORT, e.absmin - PL_MAX_CONST, e.absmax - PL_MIN_CONST, destination, destination, timetaken);
 }
 
 entity waypoint_spawnpersonal(entity this, vector position)
index 00063bba214fae3ba064a144ec18bf2983db2eae..58473889bb423d0d71334acd55f589922335cecb 100644 (file)
@@ -2423,135 +2423,23 @@ void PlayerUseKey(entity this)
 =============
 PlayerPreThink
 
-Called every frame for each client before the physics are run
+Called every frame for each real client by DP (and for each bot by StartFrame()),
+and when executing every asynchronous move, so only include things that MUST be done then.
+Use PlayerFrame() instead for code that only needs to run once per server frame.
+frametime == 0 in the asynchronous code path.
+
+TODO: move more stuff from here and PlayerThink() and ObserverOrSpectatorThink() to PlayerFrame() (frametime is always set there)
 =============
 */
 .float last_vehiclecheck;
 void PlayerPreThink (entity this)
 {
-       STAT(GUNALIGN, this) = CS_CVAR(this).cvar_cl_gunalign; // TODO
-       STAT(MOVEVARS_CL_TRACK_CANJUMP, this) = CS_CVAR(this).cvar_cl_movement_track_canjump;
-
        WarpZone_PlayerPhysics_FixVAngle(this);
 
-       if (frametime) {
-               // physics frames: update anticheat stuff
-               anticheat_prethink(this);
-
-               // WORKAROUND: only use dropclient in server frames (frametime set).
-               // Never use it in cl_movement frames (frametime zero).
-               if (blockSpectators && IS_REAL_CLIENT(this)
-                       && (IS_SPEC(this) || IS_OBSERVER(this)) && !INGAME(this)
-                       && time > (CS(this).spectatortime + autocvar_g_maxplayers_spectator_blocktime))
-               {
-                       if (dropclient_schedule(this))
-                               Send_Notification(NOTIF_ONE_ONLY, this, MSG_INFO, INFO_QUIT_KICK_SPECTATING);
-               }
-       }
-
        zoomstate_set = false;
 
-       // Check for nameless players
-       if (this.netname == "" || this.netname != CS(this).netname_previous)
-       {
-               bool assume_unchanged = (CS(this).netname_previous == "");
-               if (autocvar_sv_name_maxlength > 0 && strlennocol(this.netname) > autocvar_sv_name_maxlength)
-               {
-                       int new_length = textLengthUpToLength(this.netname, autocvar_sv_name_maxlength, strlennocol);
-                       this.netname = strzone(strcat(substring(this.netname, 0, new_length), "^7"));
-                       sprint(this, sprintf("Warning: your name is longer than %d characters, it has been truncated.\n", autocvar_sv_name_maxlength));
-                       assume_unchanged = false;
-                       // stuffcmd(this, strcat("name ", this.netname, "\n")); // maybe?
-               }
-               if (isInvisibleString(this.netname))
-               {
-                       this.netname = strzone(sprintf("Player#%d", this.playerid));
-                       sprint(this, "Warning: invisible names are not allowed.\n");
-                       assume_unchanged = false;
-                       // stuffcmd(this, strcat("name ", this.netname, "\n")); // maybe?
-               }
-               if (!assume_unchanged && autocvar_sv_eventlog)
-                       GameLogEcho(strcat(":name:", ftos(this.playerid), ":", playername(this.netname, this.team, false)));
-               strcpy(CS(this).netname_previous, this.netname);
-       }
-
-       // version nagging
-       if (CS(this).version_nagtime && CS_CVAR(this).cvar_g_xonoticversion && time > CS(this).version_nagtime) {
-        CS(this).version_nagtime = 0;
-        if (strstrofs(CS_CVAR(this).cvar_g_xonoticversion, "git", 0) >= 0 || strstrofs(CS_CVAR(this).cvar_g_xonoticversion, "autobuild", 0) >= 0) {
-            // git client
-        } else if (strstrofs(autocvar_g_xonoticversion, "git", 0) >= 0 || strstrofs(autocvar_g_xonoticversion, "autobuild", 0) >= 0) {
-            // git server
-            Send_Notification(NOTIF_ONE_ONLY, this, MSG_INFO, INFO_VERSION_BETA, autocvar_g_xonoticversion, CS_CVAR(this).cvar_g_xonoticversion);
-        } else {
-            int r = vercmp(CS_CVAR(this).cvar_g_xonoticversion, autocvar_g_xonoticversion);
-            if (r < 0) { // old client
-                Send_Notification(NOTIF_ONE_ONLY, this, MSG_INFO, INFO_VERSION_OUTDATED, autocvar_g_xonoticversion, CS_CVAR(this).cvar_g_xonoticversion);
-            } else if (r > 0) { // old server
-                Send_Notification(NOTIF_ONE_ONLY, this, MSG_INFO, INFO_VERSION_OLD, autocvar_g_xonoticversion, CS_CVAR(this).cvar_g_xonoticversion);
-            }
-        }
-    }
-
-       // GOD MODE info
-       if (!(this.flags & FL_GODMODE) && this.max_armorvalue)
-       {
-               Send_Notification(NOTIF_ONE_ONLY, this, MSG_INFO, INFO_GODMODE_OFF, this.max_armorvalue);
-               this.max_armorvalue = 0;
-       }
-
-       if (frametime && IS_PLAYER(this) && time >= game_starttime)
-       {
-               if (STAT(FROZEN, this) == FROZEN_TEMP_REVIVING)
-               {
-                       STAT(REVIVE_PROGRESS, this) = bound(0, STAT(REVIVE_PROGRESS, this) + frametime * this.revive_speed, 1);
-                       SetResourceExplicit(this, RES_HEALTH, max(1, STAT(REVIVE_PROGRESS, this) * start_health));
-                       if (this.iceblock)
-                               this.iceblock.alpha = bound(0.2, 1 - STAT(REVIVE_PROGRESS, this), 1);
-
-                       if (STAT(REVIVE_PROGRESS, this) >= 1)
-                               Unfreeze(this, false);
-               }
-               else if (STAT(FROZEN, this) == FROZEN_TEMP_DYING)
-               {
-                       STAT(REVIVE_PROGRESS, this) = bound(0, STAT(REVIVE_PROGRESS, this) - frametime * this.revive_speed, 1);
-                       SetResourceExplicit(this, RES_HEALTH, max(0, autocvar_g_nades_ice_health + (start_health-autocvar_g_nades_ice_health) * STAT(REVIVE_PROGRESS, this)));
-
-                       if (GetResource(this, RES_HEALTH) < 1)
-                       {
-                               if (this.vehicle)
-                                       vehicles_exit(this.vehicle, VHEF_RELEASE);
-                               if(this.event_damage)
-                                       this.event_damage(this, this, this.frozen_by, 1, DEATH_NADE_ICE_FREEZE.m_id, DMG_NOWEP, this.origin, '0 0 0');
-                       }
-                       else if (STAT(REVIVE_PROGRESS, this) <= 0)
-                               Unfreeze(this, false);
-               }
-       }
-
        MUTATOR_CALLHOOK(PlayerPreThink, this);
 
-       if(autocvar_g_vehicles_enter && (time > this.last_vehiclecheck) && !game_stopped && !this.vehicle)
-       if(IS_PLAYER(this) && !STAT(FROZEN, this) && !IS_DEAD(this) && !IS_INDEPENDENT_PLAYER(this))
-       {
-               FOREACH_ENTITY_RADIUS(this.origin, autocvar_g_vehicles_enter_radius, IS_VEHICLE(it) && !IS_DEAD(it) && it.takedamage != DAMAGE_NO,
-               {
-                       if(!it.owner)
-                       {
-                               if(!it.team || SAME_TEAM(this, it))
-                                       Send_Notification(NOTIF_ONE, this, MSG_CENTER, CENTER_VEHICLE_ENTER);
-                               else if(autocvar_g_vehicles_steal)
-                                       Send_Notification(NOTIF_ONE, this, MSG_CENTER, CENTER_VEHICLE_ENTER_STEAL);
-                       }
-                       else if((it.vehicle_flags & VHF_MULTISLOT) && SAME_TEAM(it.owner, this))
-                       {
-                               Send_Notification(NOTIF_ONE, this, MSG_CENTER, CENTER_VEHICLE_ENTER_GUNNER);
-                       }
-               });
-
-               this.last_vehiclecheck = time + 1;
-       }
-
        if(PHYS_INPUT_BUTTON_USE(this) && !CS(this).usekeypressed)
                PlayerUseKey(this);
        CS(this).usekeypressed = PHYS_INPUT_BUTTON_USE(this);
@@ -2599,6 +2487,7 @@ void PlayerPreThink (entity this)
                SetZoomState(this, PHYS_INPUT_BUTTON_ZOOM(this) || PHYS_INPUT_BUTTON_ZOOMSCRIPT(this) || wep_zoomed);
        }
 
+       // Voice sound effects
        if (CS(this).teamkill_soundtime && time > CS(this).teamkill_soundtime)
        {
                CS(this).teamkill_soundtime = 0;
@@ -2669,15 +2558,180 @@ void Player_Physics(entity this)
 =============
 PlayerPostThink
 
-Called every frame for each client after the physics are run
+Called every frame for each real client by DP (and for each bot by StartFrame()),
+and when executing every asynchronous move, so only include things that MUST be done then.
+Use PlayerFrame() instead for code that only needs to run once per server frame.
+frametime == 0 in the asynchronous code path.
 =============
 */
 void PlayerPostThink (entity this)
 {
        Player_Physics(this);
 
+       if (IS_PLAYER(this)) {
+               if(this.death_time == time && IS_DEAD(this))
+               {
+                       // player's bbox gets resized now, instead of in the damage event that killed the player,
+                       // once all the damage events of this frame have been processed with normal size
+                       this.maxs.z = 5;
+                       setsize(this, this.mins, this.maxs);
+               }
+               DrownPlayer(this);
+               UpdateChatBubble(this);
+               if (CS(this).impulse) ImpulseCommands(this);
+               GetPressedKeys(this);
+               if (game_stopped)
+               {
+                       CSQCMODEL_AUTOUPDATE(this);
+                       return;
+               }
+       }
+       else if (IS_OBSERVER(this) && STAT(PRESSED_KEYS, this))
+       {
+               CS(this).pressedkeys = 0;
+               STAT(PRESSED_KEYS, this) = 0;
+       }
+
+       CSQCMODEL_AUTOUPDATE(this);
+}
+
+/*
+=============
+PlayerFrame
+
+Called every frame for each client by StartFrame().
+Use this for code that only needs to run once per server frame.
+frametime is always set here.
+=============
+*/
+void PlayerFrame (entity this)
+{
+// formerly PreThink code
+       STAT(GUNALIGN, this) = CS_CVAR(this).cvar_cl_gunalign; // TODO
+       STAT(MOVEVARS_CL_TRACK_CANJUMP, this) = CS_CVAR(this).cvar_cl_movement_track_canjump;
+
+       // physics frames: update anticheat stuff
+       anticheat_prethink(this);
+
+       // Check if spectating is allowed
+       if (blockSpectators && IS_REAL_CLIENT(this)
+       && (IS_SPEC(this) || IS_OBSERVER(this)) && !INGAME(this)
+       && time > (CS(this).spectatortime + autocvar_g_maxplayers_spectator_blocktime))
+       {
+               if (dropclient_schedule(this))
+                       Send_Notification(NOTIF_ONE_ONLY, this, MSG_INFO, INFO_QUIT_KICK_SPECTATING);
+       }
+
+       // Check for nameless players
+       if (this.netname == "" || this.netname != CS(this).netname_previous)
+       {
+               bool assume_unchanged = (CS(this).netname_previous == "");
+               if (autocvar_sv_name_maxlength > 0 && strlennocol(this.netname) > autocvar_sv_name_maxlength)
+               {
+                       int new_length = textLengthUpToLength(this.netname, autocvar_sv_name_maxlength, strlennocol);
+                       this.netname = strzone(strcat(substring(this.netname, 0, new_length), "^7"));
+                       sprint(this, sprintf("Warning: your name is longer than %d characters, it has been truncated.\n", autocvar_sv_name_maxlength));
+                       assume_unchanged = false;
+                       // stuffcmd(this, strcat("name ", this.netname, "\n")); // maybe?
+               }
+               if (isInvisibleString(this.netname))
+               {
+                       this.netname = strzone(sprintf("Player#%d", this.playerid));
+                       sprint(this, "Warning: invisible names are not allowed.\n");
+                       assume_unchanged = false;
+                       // stuffcmd(this, strcat("name ", this.netname, "\n")); // maybe?
+               }
+               if (!assume_unchanged && autocvar_sv_eventlog)
+                       GameLogEcho(strcat(":name:", ftos(this.playerid), ":", playername(this.netname, this.team, false)));
+               strcpy(CS(this).netname_previous, this.netname);
+       }
+
+       // version nagging
+       if (CS(this).version_nagtime && CS_CVAR(this).cvar_g_xonoticversion && time > CS(this).version_nagtime)
+       {
+               CS(this).version_nagtime = 0;
+               if (strstrofs(CS_CVAR(this).cvar_g_xonoticversion, "git", 0) >= 0 || strstrofs(CS_CVAR(this).cvar_g_xonoticversion, "autobuild", 0) >= 0)
+               {
+                       // git client
+               }
+               else if (strstrofs(autocvar_g_xonoticversion, "git", 0) >= 0 || strstrofs(autocvar_g_xonoticversion, "autobuild", 0) >= 0)
+               {
+                       // git server
+                       Send_Notification(NOTIF_ONE_ONLY, this, MSG_INFO, INFO_VERSION_BETA, autocvar_g_xonoticversion, CS_CVAR(this).cvar_g_xonoticversion);
+               }
+               else
+               {
+                       int r = vercmp(CS_CVAR(this).cvar_g_xonoticversion, autocvar_g_xonoticversion);
+                       if (r < 0) // old client
+                               Send_Notification(NOTIF_ONE_ONLY, this, MSG_INFO, INFO_VERSION_OUTDATED, autocvar_g_xonoticversion, CS_CVAR(this).cvar_g_xonoticversion);
+                       else if (r > 0) // old server
+                               Send_Notification(NOTIF_ONE_ONLY, this, MSG_INFO, INFO_VERSION_OLD, autocvar_g_xonoticversion, CS_CVAR(this).cvar_g_xonoticversion);
+               }
+       }
+
+       // GOD MODE info
+       if (!(this.flags & FL_GODMODE) && this.max_armorvalue)
+       {
+               Send_Notification(NOTIF_ONE_ONLY, this, MSG_INFO, INFO_GODMODE_OFF, this.max_armorvalue);
+               this.max_armorvalue = 0;
+       }
+
+       // FreezeTag
+       if (IS_PLAYER(this) && time >= game_starttime)
+       {
+               if (STAT(FROZEN, this) == FROZEN_TEMP_REVIVING)
+               {
+                       STAT(REVIVE_PROGRESS, this) = bound(0, STAT(REVIVE_PROGRESS, this) + frametime * this.revive_speed, 1);
+                       SetResourceExplicit(this, RES_HEALTH, max(1, STAT(REVIVE_PROGRESS, this) * start_health));
+                       if (this.iceblock)
+                               this.iceblock.alpha = bound(0.2, 1 - STAT(REVIVE_PROGRESS, this), 1);
+
+                       if (STAT(REVIVE_PROGRESS, this) >= 1)
+                               Unfreeze(this, false);
+               }
+               else if (STAT(FROZEN, this) == FROZEN_TEMP_DYING)
+               {
+                       STAT(REVIVE_PROGRESS, this) = bound(0, STAT(REVIVE_PROGRESS, this) - frametime * this.revive_speed, 1);
+                       SetResourceExplicit(this, RES_HEALTH, max(0, autocvar_g_nades_ice_health + (start_health-autocvar_g_nades_ice_health) * STAT(REVIVE_PROGRESS, this)));
+
+                       if (GetResource(this, RES_HEALTH) < 1)
+                       {
+                               if (this.vehicle)
+                                       vehicles_exit(this.vehicle, VHEF_RELEASE);
+                               if(this.event_damage)
+                                       this.event_damage(this, this, this.frozen_by, 1, DEATH_NADE_ICE_FREEZE.m_id, DMG_NOWEP, this.origin, '0 0 0');
+                       }
+                       else if (STAT(REVIVE_PROGRESS, this) <= 0)
+                               Unfreeze(this, false);
+               }
+       }
+
+       // Vehicles
+       if(autocvar_g_vehicles_enter && (time > this.last_vehiclecheck) && !game_stopped && !this.vehicle)
+       if(IS_PLAYER(this) && !STAT(FROZEN, this) && !IS_DEAD(this) && !IS_INDEPENDENT_PLAYER(this))
+       {
+               FOREACH_ENTITY_RADIUS(this.origin, autocvar_g_vehicles_enter_radius, IS_VEHICLE(it) && !IS_DEAD(it) && it.takedamage != DAMAGE_NO,
+               {
+                       if(!it.owner)
+                       {
+                               if(!it.team || SAME_TEAM(this, it))
+                                       Send_Notification(NOTIF_ONE, this, MSG_CENTER, CENTER_VEHICLE_ENTER);
+                               else if(autocvar_g_vehicles_steal)
+                                       Send_Notification(NOTIF_ONE, this, MSG_CENTER, CENTER_VEHICLE_ENTER_STEAL);
+                       }
+                       else if((it.vehicle_flags & VHF_MULTISLOT) && SAME_TEAM(it.owner, this))
+                       {
+                               Send_Notification(NOTIF_ONE, this, MSG_CENTER, CENTER_VEHICLE_ENTER_GUNNER);
+                       }
+               });
+
+               this.last_vehiclecheck = time + 1;
+       }
+
+
+
+// formerly PostThink code
        if (autocvar_sv_maxidle > 0 || (IS_PLAYER(this) && autocvar_sv_maxidle_playertospectator > 0))
-       if (frametime) // WORKAROUND: only use dropclient in server frames (frametime set). Never use it in cl_movement frames (frametime zero).
        if (IS_REAL_CLIENT(this))
        if (IS_PLAYER(this) || autocvar_sv_maxidle_alsokickspectators)
        if (!intermission_running) // NextLevel() kills all centerprints after setting this true
@@ -2764,36 +2818,10 @@ void PlayerPostThink (entity this)
                CS(this).teamkill_soundsource = NULL;
        }
 
-       if (IS_PLAYER(this)) {
-               if(this.death_time == time && IS_DEAD(this))
-               {
-                       // player's bbox gets resized now, instead of in the damage event that killed the player,
-                       // once all the damage events of this frame have been processed with normal size
-                       this.maxs.z = 5;
-                       setsize(this, this.mins, this.maxs);
-               }
-               DrownPlayer(this);
-               UpdateChatBubble(this);
-               if (CS(this).impulse) ImpulseCommands(this);
-               GetPressedKeys(this);
-               if (game_stopped)
-               {
-                       CSQCMODEL_AUTOUPDATE(this);
-                       return;
-               }
-       }
-       else if (IS_OBSERVER(this) && STAT(PRESSED_KEYS, this))
-       {
-               CS(this).pressedkeys = 0;
-               STAT(PRESSED_KEYS, this) = 0;
-       }
-
        if (this.waypointsprite_attachedforcarrier) {
                float hp = healtharmor_maxdamage(GetResource(this, RES_HEALTH), GetResource(this, RES_ARMOR), autocvar_g_balance_armor_blockpercent, DEATH_WEAPON.m_id).x;
                WaypointSprite_UpdateHealth(this.waypointsprite_attachedforcarrier, hp);
        }
-
-       CSQCMODEL_AUTOUPDATE(this);
 }
 
 // hack to copy the button fields from the client entity to the Client State
index bd42503250027f29e0879607ae93296dcc5513f4..9611df4e87980f2fb639198e7f94fd1cae7f70ca 100644 (file)
@@ -83,9 +83,6 @@ float autocvar_sv_player_scale;
 
 void ClientState_attach(entity this);
 
-IntrusiveList g_players;
-STATIC_INIT(g_players) { g_players = IL_NEW(); }
-
 CLASS(Client, Object)
     /** Client name */
     ATTRIB(Client, netname, string, this.netname);
@@ -278,10 +275,8 @@ CLASS(Player, Client)
 
     INIT(Player) {
         this.classname = STR_PLAYER;
-        IL_PUSH(g_players, this);
     }
     DESTRUCTOR(Player) {
-        IL_REMOVE(g_players, this);
     }
 ENDCLASS(Player)
 
@@ -407,6 +402,8 @@ const int MIN_SPEC_TIME = 1;
 bool joinAllowed(entity this);
 void Join(entity this);
 
+void PlayerFrame (entity this);
+
 #define SPECTATE_COPY() ACCUMULATE void SpectateCopy(entity this, entity spectatee)
 #define SPECTATE_COPYFIELD(fld) SPECTATE_COPY() { this.(fld) = spectatee.(fld); }
 
index 23e11ec0098fd2187a863371593fed2f9de3c1c2..820f88ebfe6ee258efc592252ab4480b337d9203 100644 (file)
@@ -36,6 +36,24 @@ bool ItemSend(entity this, entity to, int sf)
        else
                sf &= ~ISF_DROP;
 
+       // if this item is being spawned (in CSQC's perspective)
+       // reuse ISF_SIZE and ISF_SIZE2 to also tell CSQC its bbox size
+       if(sf & ISF_SIZE)
+       {
+               if(this.maxs == ITEM_S_MAXS) // Small
+               {
+                       sf |= ISF_SIZE;
+                       sf &= ~ISF_SIZE2;
+               }
+               else if(this.maxs == ITEM_L_MAXS) // Large
+               {
+                       sf &= ~ISF_SIZE;
+                       sf |= ISF_SIZE2;
+               }
+               else // Default
+                       sf |= ISF_SIZE | ISF_SIZE2;
+       }
+
        WriteHeader(MSG_ENTITY, ENT_CLIENT_ITEM);
        WriteByte(MSG_ENTITY, sf);
 
@@ -50,13 +68,10 @@ bool ItemSend(entity this, entity to, int sf)
                WriteAngleVector(MSG_ENTITY, this.angles);
        }
 
-       // sets size on the client, unused on server
-       //if(sf & ISF_SIZE)
-
        if(sf & ISF_STATUS)
                WriteByte(MSG_ENTITY, this.ItemStatus);
 
-       if(sf & ISF_MODEL)
+       if(sf & ISF_SIZE || sf & ISF_SIZE2) // always true when it's spawned (in CSQC's perspective)
        {
                WriteShort(MSG_ENTITY, bound(0, this.fade_end, 32767));
 
@@ -1046,6 +1061,9 @@ void StartItem(entity this, entity def)
                        this.nextthink = max(this.strength_finished, this.invincible_finished, this.superweapons_finished);
                }
 
+               // most loot items have a bigger horizontal size than a player
+               nudgeoutofsolid(this);
+
                // don't drop if in a NODROP zone (such as lava)
                traceline(this.origin, this.origin, MOVE_NORMAL, this);
                if (trace_dpstartcontents & DPCONTENTS_NODROP)
@@ -1082,11 +1100,19 @@ void StartItem(entity this, entity def)
                if(this.angles != '0 0 0')
                        this.SendFlags |= ISF_ANGLES;
 
-               if(q3compat && !this.team)
+               if(q3compat)
                {
-                       string t = GetField_fullspawndata(this, "team");
-                       // bones_was_here: this hack is cheaper than changing to a .string strcmp()
-                       if(t) this.team = crc16(false, t);
+                       if (!this.team)
+                       {
+                               string t = GetField_fullspawndata(this, "team");
+                               // bones_was_here: this hack is cheaper than changing to a .string strcmp()
+                               if(t) this.team = crc16(false, t);
+                       }
+
+                       // In Q3 the origin is in the middle of the bbox ("radius" 15), in Xon it's at the bottom
+                       // so we need to offset vertically (only for items placed by the mapper).
+                       this.origin.z += -15 - this.mins.z;
+                       setorigin(this, this.origin);
                }
 
                // it's a level item
@@ -1099,8 +1125,6 @@ void StartItem(entity this, entity def)
                // do item filtering according to game mode and other things
                if (this.noalign <= 0)
                {
-                       // first nudge it off the floor a little bit to avoid math errors
-                       setorigin(this, this.origin + '0 0 1');
                        // note droptofloor returns false if stuck/or would fall too far
                        if (!this.noalign)
                                droptofloor(this);
index b5832d8939ae91b28b44f671007e9a619d5ec44f..40e14c906bf858e544c321cf475cada1526a9024 100644 (file)
@@ -294,9 +294,12 @@ 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));
+       FOREACH_CLIENT(IS_FAKE_CLIENT(it),
+       {
+               // DP calls these for real clients only
+               sys_phys_update(it, frametime); // called by SV_PlayerPhysics for players
+               PlayerPreThink(it);
+       });
 
        execute_next_frame();
 
@@ -365,8 +368,13 @@ void StartFrame()
        MUTATOR_CALLHOOK(SV_StartFrame);
 
        GlobalStats_updateglobal();
-       FOREACH_CLIENT(true, GlobalStats_update(it));
-       IL_EACH(g_players, IS_FAKE_CLIENT(it), PlayerPostThink(it));
+       FOREACH_CLIENT(true,
+       {
+               GlobalStats_update(it);
+               if (IS_FAKE_CLIENT(it))
+                       PlayerPostThink(it); // DP calls this for real clients only
+               PlayerFrame(it);
+       });
 }
 
 .vector originjitter;
index 5ac9b0472f7bc9844cf5eb5cd4e99672c5714461..d8ba13dcc2633ae0e0925143b9c145eea2d8530e 100644 (file)
@@ -215,6 +215,18 @@ void MapVote_AddVotableMaps(int nmax, int smax)
                MapVote_AddVotable(GetNextMap(), false);
 }
 
+bool GameTypeVote_SetGametype(Gametype type);
+void GameTypeVote_ApplyGameType(Gametype type)
+{
+       localcmd("sv_vote_gametype_hook_all\n");
+       localcmd("sv_vote_gametype_hook_", MapInfo_Type_ToString(type), "\n");
+
+       if (!GameTypeVote_SetGametype(type))
+               LOG_TRACE("Selected gametype is not supported by any map");
+}
+
+Gametype voted_gametype;
+Gametype match_gametype;
 void MapVote_Init()
 {
        int nmax, smax;
@@ -254,6 +266,13 @@ void MapVote_Init()
                mapvote_keeptwotime = 0;
 
        MapVote_Spawn();
+
+       // If match_gametype is set it means voted_gametype has just been applied (on game type vote end).
+       // In this case apply back match_gametype here so that the "restart" command, if called,
+       // properly restarts the map applying the current game type.
+       // Applying voted_gametype before map vote start is needed to properly initialize map vote.
+       if (match_gametype)
+               GameTypeVote_ApplyGameType(match_gametype);
 }
 
 void MapVote_SendPicture(entity to, int id)
@@ -374,7 +393,7 @@ bool MapVote_SendEntity(entity this, entity to, int sf)
                {
                        // map vote but gametype has been chosen via voting screen
                        WriteByte(MSG_ENTITY, BIT(1)); // gametypevote_flags
-                       WriteString(MSG_ENTITY, MapInfo_Type_ToText(MapInfo_CurrentGametype()));
+                       WriteString(MSG_ENTITY, MapInfo_Type_ToText(voted_gametype));
                }
                else
                        WriteByte(MSG_ENTITY, 0); // map vote
@@ -671,6 +690,14 @@ void MapVote_Think()
        {
                if (time > mapvote_winner_time + 1)
                {
+                       if (voted_gametype)
+                       {
+                               // clear match_gametype so that GameTypeVote_ApplyGameType
+                               // prints the game type switch message
+                               match_gametype = NULL;
+                               GameTypeVote_ApplyGameType(voted_gametype);
+                       }
+
                        Map_Goto_SetStr(mapvote_maps[mapvote_winner]);
                        Map_Goto(0);
                }
@@ -742,7 +769,9 @@ bool GameTypeVote_SetGametype(Gametype type)
                // update lsmaps in case the gametype changed, this way people can easily list maps for it
                if(lsmaps_reply != "") { strunzone(lsmaps_reply); }
                lsmaps_reply = strzone(getlsmaps());
-               bprint("Game type successfully switched to ", MapInfo_Type_ToString(type), "\n");
+
+               if (!match_gametype) // don't show this msg if we are temporarily switching game type
+                       bprint("Game type successfully switched to ", MapInfo_Type_ToString(type), "\n");
        }
        else
        {
@@ -763,13 +792,10 @@ bool GameTypeVote_Finished(int pos)
        if(!gametypevote || gametypevote_finished)
                return false;
 
-       localcmd("sv_vote_gametype_hook_all\n");
-       localcmd("sv_vote_gametype_hook_", mapvote_maps[pos], "\n");
+       match_gametype = MapInfo_CurrentGametype();
+       voted_gametype = MapInfo_Type_FromString(mapvote_maps[pos], false, false);
 
-       if ( !GameTypeVote_SetGametype(GameTypeVote_Type_FromString(mapvote_maps[pos])) )
-       {
-               LOG_TRACE("Selected gametype is not supported by any map");
-       }
+       GameTypeVote_ApplyGameType(voted_gametype);
 
        gametypevote_finished = true;
 
index c33dd5242f9614d515bfa71b8b287a262ae25164..36578740c07fff735e11041ce9811d1284727484 100644 (file)
@@ -32,7 +32,7 @@ void MapVote_Think();
 void MapVote_SendPicture(entity to, int id);
 bool GameTypeVote_SetGametype(entity type);
 float GameTypeVote_Start();
-float GameTypeVote_Finished(float pos);
+float GameTypeVote_Finished(int pos);
 string GameTypeVote_MapInfo_FixName(string m);
 
 bool gametypevote;
index ad3ba94e4a21d18c9aa7c293a1c68cfa9a04edeb..3ca1bbc1b1116c73c224f211d88f51074b720467 100644 (file)
@@ -127,6 +127,7 @@ void CopyBody(entity this, float keepvelocity)
 
        if(clone.colormap <= maxclients && clone.colormap > 0)
                clone.colormap = 1024 + this.clientcolors;
+       clone.sv_entnum = etof(this); // sent to CSQC for color mapping purposes
 
        CSQCMODEL_AUTOINIT(clone);
        clone.CopyBody_nextthink = this.nextthink;
index 7bbbfa0dd7d9152f8e096f3a778ee0639059bfde..926349a575665fb8d9c85499557d6be318487a61 100644 (file)
@@ -1,5 +1,6 @@
 #include "world.qh"
 
+#include <common/checkextension.qh>
 #include <common/constants.qh>
 #include <common/deathtypes/all.qh>
 #include <common/gamemodes/_mod.qh>
@@ -746,6 +747,8 @@ void InitGameplayMode()
 bool world_already_spawned;
 spawnfunc(worldspawn)
 {
+       CheckEngineExtensions();
+
        cvar_set("_endmatch", "0");
        server_is_dedicated = boolean(stof(cvar_defstring("is_dedicated")));
 
@@ -2300,16 +2303,55 @@ void InitializeEntitiesRun()
        delete_fn = remove_unsafely;
 }
 
-// deferred dropping
-// ported from VM_SV_droptofloor TODO: make a common function for the client-side?
-void DropToFloor_Handler(entity this)
+// originally ported from DP's droptofloor() builtin
+// TODO: make a common function for the client-side?
+// bones_was_here: when we have a use case for it, yes
+void DropToFloor_QC(entity this)
 {
+       int nudgeresult;
+
        if(!this || wasfreed(this))
        {
-               // no modifying free entities
+               LOG_WARN("DropToFloor_QC: can not modify free entity");
                return;
        }
 
+       /* Prior to sv_legacy_bbox_expand 0, both droptofloor and nudgeoutofsolid were done for items
+        * using box '-16 -16 0' '16 16 48' (without the FL_ITEM expansion applied),
+        * which often resulted in bboxes partially in solids once expansion was applied.
+        * We don't want bboxes in solids (bad for gameplay and culling),
+        * but we also don't want items to land on a "skirting board" or the base of a sloping wall.
+        * For initial nudgeoutofsolid and droptofloor stages we use a small box
+        * so they fall as far and in the same place as they traditionally would,
+        * then we nudge the full size box out of solid, in a direction appropriate for the plane(s).
+        */
+       vector saved_mins = this.mins; // gmqcc's used-uninitialized check doesn't handle
+       vector saved_maxs = this.maxs; // making these assignments FL_ITEM conditional.
+       if (this.flags & FL_ITEM)
+       {
+               // Using the Q3 bbox for best compatibility with all maps, except...
+               this.mins.x = -15;
+               this.mins.y = -15;
+               this.maxs.x = 15;
+               this.maxs.y = 15;
+               this.maxs.z = this.mins.z + 30; // ...Nex, Xon and Quake use a different vertical offset, see also: StartItem()
+       }
+
+       /* NOTE: sv_gameplayfix_droptofloorstartsolid_nudgetocorrect isn't checked, so it won't need to be networked to CSQC.
+        * It was enabled by default in all Xonotic releases and in Nexuiz, so now certain maps depend on it.
+        * Example: on erbium 0.8.6 the shards @ crylink are too low (in collision with the floor),
+        * so without this those fall through the floor.
+        * Q3, Q2 and Quake don't try to move items out of solid.
+        */
+       if(!Q3COMPAT_COMMON && autocvar_sv_mapformat_is_quake3) // Xonotic, Nexuiz
+       {
+               nudgeresult = nudgeoutofsolid(this);
+               if (!nudgeresult)
+                       LOG_WARNF("DropToFloor_QC at \"%v\": COULD NOT FIX badly placed entity \"%s\" before drop", this.origin, this.classname);
+               else if (nudgeresult > 0)
+                       LOG_WARNF("DropToFloor_QC at \"%v\": FIXED badly placed entity \"%s\" before drop", this.origin, this.classname);
+       }
+
        vector end = this.origin;
        if (autocvar_sv_mapformat_is_quake3)
                end.z -= 4096;
@@ -2317,69 +2359,68 @@ void DropToFloor_Handler(entity this)
                end.z -= 128;
        else
                end.z -= 256; // Quake, QuakeWorld
+       tracebox(this.origin, this.mins, this.maxs, end, MOVE_NOMONSTERS, this);
 
-       // NOTE: SV_NudgeOutOfSolid is used in the engine here
-       if(autocvar_sv_gameplayfix_droptofloorstartsolid_nudgetocorrect)
+       if (!autocvar_sv_mapformat_is_quake3 && !autocvar_sv_mapformat_is_quake2 && (trace_allsolid || trace_fraction == 1)) // Quake
+       {
+               // Quake games just delete badly placed entities...
+               LOG_WARNF("DropToFloor_QC at \"%v\" (Quake compat): DELETING badly placed entity \"%s\"", this.origin, this.classname);
+               delete(this);
+               return;
+       }
+       else if ((Q3COMPAT_COMMON || autocvar_sv_mapformat_is_quake2) && trace_startsolid) // Q3, Q2
        {
-               _Movetype_UnstickEntity(this);
-               move_out_of_solid(this);
+               // ...but we can't do that on Q3 maps like jamdm1
+               // because our tracebox hits things Q3's trace doesn't (patches?).
+               LOG_WARNF("DropToFloor_QC at \"%v\" (Quake 3 compat): badly placed entity \"%s\"", this.origin, this.classname);
        }
 
-       tracebox(this.origin, this.mins, this.maxs, end, MOVE_NORMAL, this);
+       /* NOTE: sv_gameplayfix_droptofloorstartsolid (fallback from tracebox to traceline) isn't implemented.
+        * It was disabled by default in all Xonotic releases and in Nexuiz.
+        * Q3 doesn't support it (always uses its '-15 -15 -15' '15 15 15' box when dropping items), neither does Quake or Q2.
+        */
 
-       if(trace_startsolid && autocvar_sv_gameplayfix_droptofloorstartsolid)
+       if (!autocvar_sv_mapformat_is_quake2) // Quake, Q3, Nexuiz, Xonotic
+               // allow to ride movers (or unset if in freefall)
+               this.groundentity = trace_ent;
+
+       if (!autocvar_sv_mapformat_is_quake3)
+               // if support is destroyed, keep suspended (gross hack for floating items in various maps)
+               // bones_was_here: is this for Q1BSP only? Which maps use it? Do we need it at all? Intentions unclear in DP...
+               this.move_suspendedinair = true;
+
+       if (trace_fraction)
+               this.origin = trace_endpos;
+
+       if (this.flags & FL_ITEM)
        {
-               vector offset, org;
-               offset = 0.5 * (this.mins + this.maxs);
-               offset.z = this.mins.z;
-               org = this.origin + offset;
-               traceline(org, end, MOVE_NORMAL, this);
-               trace_endpos = trace_endpos - offset;
-               if(trace_startsolid)
-               {
-                       LOG_DEBUGF("DropToFloor_Handler: %v could not fix badly placed entity", this.origin);
-                       _Movetype_LinkEdict(this, false);
-                       SET_ONGROUND(this);
-                       this.groundentity = NULL;
-               }
-               else if(trace_fraction < 1)
-               {
-                       LOG_DEBUGF("DropToFloor_Handler: %v fixed badly placed entity", this.origin);
-                       setorigin(this, trace_endpos);
-                       if(autocvar_sv_gameplayfix_droptofloorstartsolid_nudgetocorrect)
-                       {
-                               _Movetype_UnstickEntity(this);
-                               move_out_of_solid(this);
-                       }
-                       SET_ONGROUND(this);
-                       this.groundentity = trace_ent;
-                       // if support is destroyed, keep suspended (gross hack for floating items in various maps)
-                       this.move_suspendedinair = true;
-               }
+               this.mins = saved_mins;
+               this.maxs = saved_maxs;
+
+               // A side effect of using a small box to drop items (and do the initial nudge) is
+               // the full size box can end up in collision with a sloping floor or terrain model.
+               nudgeresult = nudgeoutofsolid(this);
+               // No warns for successful nudge because it would spam about items on slopes/terrain.
        }
-       else
+       else if (trace_allsolid && trace_fraction) // dropped using "proper" bbox but never left solid
        {
-               if(!trace_allsolid && trace_fraction < 1)
-               {
-                       setorigin(this, trace_endpos);
-                       SET_ONGROUND(this);
-                       this.groundentity = trace_ent;
-                       // if support is destroyed, keep suspended (gross hack for floating items in various maps)
-                       this.move_suspendedinair = true;
-               }
-               else
-               {
-                       // if we can't get the entity out of solid, mark it as on ground so physics doesn't attempt to drop it
-                       // hacky workaround for #2774
-                       SET_ONGROUND(this);
-               }
+               nudgeresult = nudgeoutofsolid(this);
+               if (nudgeresult > 0)
+                       LOG_WARNF("DropToFloor_QC at \"%v\": FIXED badly placed entity \"%s\" after drop", this.origin, this.classname);
        }
-       this.dropped_origin = this.origin;
+       else
+               nudgeresult = -1;
+
+       if (!nudgeresult)
+       if (!Q3COMPAT_COMMON) // to be expected on Q3 maps like gu3-pewter because we use bigger final bboxes
+               LOG_WARNF("DropToFloor_QC at \"%v\": COULD NOT FIX stuck entity \"%s\" after drop", this.origin, this.classname);
+
+       setorigin(this, this.dropped_origin = this.origin);
 }
 
 void droptofloor(entity this)
 {
-       InitializeEntity(this, DropToFloor_Handler, INITPRIO_DROPTOFLOOR);
+       InitializeEntity(this, DropToFloor_QC, INITPRIO_DROPTOFLOOR);
 }
 
 bool autocvar_sv_gameplayfix_multiplethinksperframe = true;
index ff799e64cc529d7d406aebd5d929d12cb25b4766..67adb0fab77201a230537eff216c9790175ff656 100644 (file)
@@ -28,8 +28,6 @@ float autocvar_timelimit_max;
 float autocvar_timelimit_overtime;
 int autocvar_timelimit_overtimes;
 float autocvar_timelimit_suddendeath;
-bool autocvar_sv_gameplayfix_droptofloorstartsolid;
-bool autocvar_sv_gameplayfix_droptofloorstartsolid_nudgetocorrect;
 
 bool autocvar_sv_mapformat_is_quake3;
 bool autocvar_sv_mapformat_is_quake2;
index f3f0eb233e4279fc4abcf0ba8de9c1428d3a8949..431a6f7c1636f80cd750cb7469d48193366b1c70 100755 (executable)
@@ -185,12 +185,13 @@ mkdir -p data/maps
 createdtoday "data/maps/_init.bsp" \
        || wget -nv -O data/maps/_init.bsp https://gitlab.com/xonotic/xonotic-maps.pk3dir/raw/master/maps/_init/_init.bsp
 
+PASS=0
 while read -r LINE
 do
        printf "%s\n" "$LINE"
-       [ "$LINE" = "All tests OK" ] && PASS=1
+       printf "%s\n" "$LINE" | grep -q ".*All tests OK$" && PASS=1
 done < <(${ENGINE} +developer 1 +map _init +sv_cmd runtest +wait +quit)
-test "$PASS" = "1" || { printf 'sv_cmd runtest failed!'; exit 1; }
+test "$PASS" = "1" || { printf "\033[1;31m%s\033[0m\n" "sv_cmd runtest failed!"; exit 1; }
 
 ${ENGINE} +map _init +sv_cmd dumpnotifs +wait +quit
 diff notifications.cfg data/data/notifications_dump.cfg ||
@@ -228,8 +229,8 @@ then # green ok print
        printf "\033[32m%s\033[0m\n" "hashes match"
        exit 0
 else # red error print
-       printf "\033[32m%s\033[0m\n" "expected: $EXPECT"
-       printf "\033[32m%s\033[0m\n" "  actual: $HASH"
-       printf "\033[31m%s\033[0m\n" "!!! ERROR: HASHES DO NOT MATCH !!!"
+       printf "\033[31m%s\033[0m\n" "expected: $EXPECT"
+       printf "\033[31m%s\033[0m\n" "  actual: $HASH"
+       printf "\033[1;31m%s\033[0m\n" "!!! ERROR: HASHES DO NOT MATCH !!!"
        exit 1
 fi
index d1b449f475d8ac5608a069854660ec72626e739e..9dee8744cf08c855a31707978673b92abd410e0c 100644 (file)
@@ -240,7 +240,7 @@ seta cl_damageeffect_lifetime_max 6 "maximum lifetime a damage effect may have"
 set cl_deathglow 2 "number of seconds during which dead bodies glow out"
 set cl_deathglow_min 0.5 "glow out up to this glow factor"
 
-set cl_respawn_ghosts_keepcolors 0 "if enabled respawn ghosts keep body colors"
+set cl_respawn_ghosts_keepcolors 1 "if enabled respawn ghosts keep body colors"
 
 set _teams_available 0 "internal cvar, value is synced from the server to reflect currently available teams to join"
 
index e38ce52f96db5f5b4ec87751a8998337a0895198..063425b2615ccb2e2484a722fc1a749bd9b00bee 100644 (file)
@@ -57,6 +57,7 @@ set g_telefrags 1 "telefragging, i.e. killing someone who stands in the way of s
 set g_telefrags_teamplay 1 "never telefrag team mates"
 set g_telefrags_avoid 1 "when teleporters have a random destination, avoid teleporting to locations where a telefrag would happen"
 set g_teleport_maxspeed 0 "maximum speed that a player can keep when going through a teleporter (if a misc_teleporter_dest also has a cap the smallest one of these will be used), 0 = don't limit, -1 = keep no speed"
+set g_teleport_minspeed 0 "minimum speed that a player can keep when going through a teleporter which affects speed"
 
 set g_respawn_ghosts 1 "if 1 dead bodies become ghosts and float away when the player respawns"
 set g_respawn_ghosts_speed 5 "the speed with which respawn ghosts float and rotate"
@@ -419,20 +420,10 @@ set timelimit_decrement 5 "number of minutes removed from the timer when voting
 set timelimit_min 5 "shortest match time achieveable with reducematchtime and timelimit votes"
 set timelimit_max 60 "maximum match time achieveable with extendmatchtime and timelimit votes"
 
-sv_gameplayfix_delayprojectiles 0
-sv_gameplayfix_q2airaccelerate 1
-sv_gameplayfix_stepmultipletimes 1
-sv_gameplayfix_stepdown 2
-// only available in qc physics
-set sv_gameplayfix_stepdown_maxspeed 0 "maximum speed walking entities can be moving for stepping down to apply"
-
 // delay for "kill" to prevent abuse
 set g_balance_kill_delay 2 "timer before death when using the kill command"
 set g_balance_kill_antispam 5 "additional time added to the kill delay if used repeatedly"
 
-// this feature is currently buggy in the engine (it appears to PREVENT any dropping in lots of maps, leading to weirdly aligned entities, and in some cases even CAUSES them to drop through solid, like in facing worlds nex)
-sv_gameplayfix_droptofloorstartsolid 0
-
 set sv_foginterval 1 "force enable fog in regular intervals"
 
 set sv_maxidle 0 "kick players idle for more than this amount of time in seconds"
@@ -545,6 +536,17 @@ alias pc_gw "prvm_globalwatchpoint client ${* ?}"
 alias pm_gw "prvm_globalwatchpoint menu ${* ?}"
 ///////// qc debugger shortcuts END /////////
 
+sv_gameplayfix_delayprojectiles 0
+sv_gameplayfix_q2airaccelerate 1
+sv_gameplayfix_stepmultipletimes 1
+sv_gameplayfix_stepdown 2
+// only available in qc physics
+set sv_gameplayfix_stepdown_maxspeed 0 "maximum speed walking entities can be moving for stepping down to apply"
+
+// this feature is currently buggy in the engine (it appears to PREVENT any dropping in lots of maps, leading to weirdly aligned entities, and in some cases even CAUSES them to drop through solid, like in facing worlds nex)
+// the bugs should be fixed in https://gitlab.com/xonotic/darkplaces/-/merge_requests/144 but we don't need this, didn't enable it in the past, and it's not implemented in DropToFloor_QC().
+sv_gameplayfix_droptofloorstartsolid 0
+
 // otherwise, antilag breaks
 sv_gameplayfix_consistentplayerprethink 1
 
@@ -555,6 +557,9 @@ sv_gameplayfix_unstickplayers 1
 sv_gameplayfix_gravityunaffectedbyticrate 1
 sv_gameplayfix_nogravityonground 1
 
+// avoid needing to work around Quake bbox expansion in many places
+sv_legacy_bbox_expand 0
+
 set sv_q3compat_changehitbox 0 "use Q3 player hitbox dimensions and camera height on Q3 maps (maps with an entry in a .arena or .defi file)"
 
 set g_movement_highspeed 1 "multiplier scale for movement speed (applies to sv_maxspeed and sv_maxairspeed, also applies to air acceleration when g_movement_highspeed_q3_compat is set to 0)"