]> git.xonotic.org Git - xonotic/xonotic-data.pk3dir.git/commitdiff
Merge branch 'terencehill/playerlist_layout' into 'master'
authorterencehill <piuntn@gmail.com>
Fri, 15 Mar 2024 11:13:52 +0000 (11:13 +0000)
committerterencehill <piuntn@gmail.com>
Fri, 15 Mar 2024 11:13:52 +0000 (11:13 +0000)
Menu: make player list in the Server Info dialog more readable

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

117 files changed:
.gitattributes
.gitlab-ci.yml
.tx/merge-base
common.ja_JP.po
gamemodes-server.cfg
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/clanarena/sv_clanarena.qc
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/survival/survival.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/mapinfo.qc
qcsrc/common/mapinfo.qh
qcsrc/common/mapobjects/func/conveyor.qc
qcsrc/common/mapobjects/func/door.qc
qcsrc/common/mapobjects/func/door.qh
qcsrc/common/mapobjects/func/ladder.qc
qcsrc/common/mapobjects/func/plat.qc
qcsrc/common/mapobjects/platforms.qc
qcsrc/common/mapobjects/subs.qc
qcsrc/common/mapobjects/teleporters.qc
qcsrc/common/mapobjects/trigger/swamp.qc
qcsrc/common/mapobjects/trigger/viewloc.qc
qcsrc/common/mutators/mutator/buffs/buffs.qh
qcsrc/common/mutators/mutator/buffs/sv_buffs.qc
qcsrc/common/mutators/mutator/buffs/sv_buffs.qh
qcsrc/common/mutators/mutator/damagetext/damagetext.qc
qcsrc/common/mutators/mutator/itemstime/itemstime.qc
qcsrc/common/mutators/mutator/nades/nades.qc
qcsrc/common/mutators/mutator/powerups/powerup/invisibility.qh
qcsrc/common/mutators/mutator/powerups/powerup/speed.qh
qcsrc/common/mutators/mutator/powerups/powerups.qh
qcsrc/common/mutators/mutator/waypoints/all.inc
qcsrc/common/mutators/mutator/waypoints/all.qh
qcsrc/common/mutators/mutator/waypoints/waypointsprites.qc
qcsrc/common/notifications/all.qh
qcsrc/common/physics/movetypes/movetypes.qc
qcsrc/common/physics/movetypes/toss.qc
qcsrc/common/playerstats.qc
qcsrc/common/resources/resources.qh
qcsrc/common/scores.qh
qcsrc/common/stats.qh
qcsrc/common/weapons/weapon/porto.qc
qcsrc/common/weapons/weapon/shockwave.qc
qcsrc/common/weapons/weapon/vaporizer.qc
qcsrc/common/weapons/weapon/vortex.qc
qcsrc/lib/log.qh
qcsrc/lib/net.qh
qcsrc/lib/registry.qh
qcsrc/lib/registry_net.qh
qcsrc/lib/string.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/command/sv_cmd.qc
qcsrc/server/intermission.qc
qcsrc/server/intermission.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 aa2c105570ad6a953c9d5212219a4631373aa5f8..7647566dfa204f82b38887843028af9c9c782574 100644 (file)
@@ -36,7 +36,7 @@ test_compilation_units:
 test_sv_game:
   stage: test
   script:
-    - export EXPECT=210a5126bffa3cd2acdb8d62bcec9e11
+    - export EXPECT=ded5d54d7a8326069f5f3d015cee1bf2
     - qcsrc/tools/sv_game-hashtest.sh
     - exit $?
 
index 4f849218d30ab3a04f2231023964949cc64f6504..0514b49646ee49c95abcb0e445a576eea50306ae 100644 (file)
@@ -1 +1 @@
-Sat Jan 20 07:23:48 AM CET 2024
+Fri Jan 26 07:22:59 AM CET 2024
index 4fc22b06a36803eacf74646d17515b040c3e3a7b..e83368ceb43c85d519c92eac54baca08ca74c56c 100644 (file)
@@ -7,7 +7,7 @@
 # Antoni Das <Antonidas159@gmail.com>, 2017
 # Antonio <piuntn@gmail.com>, 2023
 # LegendGuard, 2020
-# LegendGuard, 2020-2023
+# LegendGuard, 2020-2024
 # Lento <securemailfor28-xonotic@yahoo.co.jp>, 2015
 # RYU N. <ryusho2523@yahoo.co.jp>, 2021
 # z 411 <winrar.hurr@gmail.com>, 2021-2022
@@ -17,7 +17,7 @@ msgstr ""
 "Report-Msgid-Bugs-To: \n"
 "POT-Creation-Date: 2023-07-11 07:22+0200\n"
 "PO-Revision-Date: 2013-09-12 16:53+0000\n"
-"Last-Translator: RYU N. <ryusho2523@yahoo.co.jp>, 2021\n"
+"Last-Translator: LegendGuard, 2020-2024\n"
 "Language-Team: Japanese (Japan) (http://app.transifex.com/team-xonotic/"
 "xonotic/language/ja_JP/)\n"
 "Language: ja_JP\n"
@@ -351,7 +351,7 @@ msgstr "QMCMD^旗が見えました、アイコン"
 
 #: qcsrc/client/hud/panel/quickmenu.qc:838
 msgid "QMCMD^defending (l:%l^7) (h:%h^7 a:%a^7 w:%w^7)"
-msgstr "QMCMD^守り中 (l:%l^7) (h:%h^7 a:%a^7 w:%w^7)"
+msgstr "QMCMD^防衛中 (l:%l^7) (h:%h^7 a:%a^7 w:%w^7)"
 
 #: qcsrc/client/hud/panel/quickmenu.qc:838
 msgid "QMCMD^defending, icon"
index 01d297e084a27827809150fc6aa2cfd705b9bc7b..a859acbded174ebaf334b8725ea30439b342c45a 100644 (file)
@@ -265,7 +265,7 @@ set g_ca_round_timelimit 180 "round time limit in seconds"
 set g_ca_teams_override 0
 set g_ca_team_spawns 0 "when 1, players spawn from the team spawnpoints of the map, if any"
 set g_ca_teams 0
-set g_ca_prevent_stalemate 0 "when round time ends instead of instant stalemate give round win to the team with most survivors or with the most total health"
+set g_ca_prevent_stalemate 0 "When round time ends instead of instant stalemate give round win to the team with 1: most survivors. 2: most total health. 3: most survivors or if equal then most total health"
 set g_ca_weaponarena "most" "starting weapons - takes the same options as g_weaponarena"
 
 
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 f0a793afd50f4d39aa2d28fe1e3bfc313e438e43..8ee0a676569b8364f931e52b7a976c87d2f858ef 100644 (file)
@@ -1,7 +1,7 @@
 #include "sv_clanarena.qh"
 
 float autocvar_g_ca_damage2score = 100;
-bool autocvar_g_ca_prevent_stalemate;
+int autocvar_g_ca_prevent_stalemate;
 
 float autocvar_g_ca_start_health = 200;
 float autocvar_g_ca_start_armor = 200;
@@ -46,80 +46,95 @@ void nades_Clear(entity player);
 
 int CA_PreventStalemate()
 {
-       //LOG_INFO("PreventStalemate running");
-       int winnerTeam = 0;
-       int secondTeam = 0;
+       //bprint("PreventStalemate running\n");
 
-       for(int i = 1; i <= AVAILABLE_TEAMS; i++)
+       // g_ca_prevent_stalemate:
+       // Run survivor count check with 1 aka bit 0b0001
+       // Run total health check with 2 aka bit 0b0010
+       // With a value like 3 which has both bits both are ran
+
+       bool prevent_stalemate_by_survivors = (autocvar_g_ca_prevent_stalemate & BIT(0));
+       bool prevent_stalemate_by_health    = (autocvar_g_ca_prevent_stalemate & BIT(1));
+
+       // Check which team has more alive players
+       if (prevent_stalemate_by_survivors)
        {
-               if(!winnerTeam || Team_GetNumberOfAlivePlayers(Team_GetTeamFromIndex(i)) > Team_GetNumberOfAlivePlayers(Team_GetTeam(winnerTeam)))
+               int winnerTeam = 0;
+               int secondTeam = 0;
+
+               for(int i = 1; i <= AVAILABLE_TEAMS; ++i)
                {
-                       secondTeam = winnerTeam;
-                       winnerTeam = Team_IndexToTeam(i);
+                       if(!winnerTeam || Team_GetNumberOfAlivePlayers(Team_GetTeamFromIndex(i)) > Team_GetNumberOfAlivePlayers(Team_GetTeam(winnerTeam)))
+                       {
+                               secondTeam = winnerTeam;
+                               winnerTeam = Team_IndexToTeam(i);
+                       }
+                       else
+                       {
+                               if(!secondTeam || Team_GetNumberOfAlivePlayers(Team_GetTeamFromIndex(i)) > Team_GetNumberOfAlivePlayers(Team_GetTeam(secondTeam)))
+                                       secondTeam = Team_IndexToTeam(i);
+                       }
                }
-               else
+
+               if(Team_GetNumberOfAlivePlayers(Team_GetTeam(winnerTeam)) != Team_GetNumberOfAlivePlayers(Team_GetTeam(secondTeam)))
                {
-                       if(!secondTeam || Team_GetNumberOfAlivePlayers(Team_GetTeamFromIndex(i)) > Team_GetNumberOfAlivePlayers(Team_GetTeam(secondTeam)))
-                               secondTeam = Team_IndexToTeam(i);
+                       bprint(sprintf("Stalemate broken by alive players. Best team: %s%s (%d)^7 - Trailing team: %s%s (%d)\n",
+                               Team_ColorCode(winnerTeam), Team_ColorName(winnerTeam), Team_GetNumberOfAlivePlayers(Team_GetTeam(winnerTeam)),
+                               Team_ColorCode(secondTeam), Team_ColorName(secondTeam), Team_GetNumberOfAlivePlayers(Team_GetTeam(secondTeam))));
+                       return winnerTeam;
                }
        }
 
-       if(Team_GetNumberOfAlivePlayers(Team_GetTeam(winnerTeam)) != Team_GetNumberOfAlivePlayers(Team_GetTeam(secondTeam)))
+       // Check which team has more health
+       if (prevent_stalemate_by_health)
        {
-               LOG_INFOF("Stalemate broken by alive players. Best team: %s%s (%d)^7 - Trailing team: %s%s (%d)",
-                       Team_ColorCode(winnerTeam), Team_ColorName(winnerTeam), Team_GetNumberOfAlivePlayers(Team_GetTeam(winnerTeam)),
-                       Team_ColorCode(secondTeam), Team_ColorName(secondTeam), Team_GetNumberOfAlivePlayers(Team_GetTeam(secondTeam)));
-               return winnerTeam;
-       }
-
-       // Equality. Let's check which team has more health now
-       //LOG_INFO("Equality. Checking health now.");
-       winnerTeam = 0;
-       secondTeam = 0;
-       int winnerTeamHealth = 0;
-       int secondTeamHealth = 0;
-       int teamIndex, teamHealth;
+               int winnerTeam = 0;
+               int secondTeam = 0;
+               int winnerTeamHealth = 0;
+               int secondTeamHealth = 0;
+               int teamIndex, teamHealth;
 
-       for(int i = 1; i <= AVAILABLE_TEAMS; i++)
-       {
-               teamIndex = i;
-               teamHealth = 0;
-
-               // Add up health for the players in this team
-               FOREACH_CLIENT(IS_PLAYER(it) && Entity_HasValidTeam(it) && it.team == Team_IndexToTeam(teamIndex),
+               for(int i = 1; i <= AVAILABLE_TEAMS; ++i)
                {
-                       if (IS_DEAD(it))
-                               continue;
-                       teamHealth += GetResource(it, RES_HEALTH) + GetResource(it, RES_ARMOR);
-               });
+                       teamIndex = i;
+                       teamHealth = 0;
 
-               // Set the winner teams
-               if(!winnerTeam || teamHealth > winnerTeamHealth)
-               {
-                       secondTeam = winnerTeam;
-                       secondTeamHealth = winnerTeamHealth;
-                       winnerTeam = Team_IndexToTeam(i);
-                       winnerTeamHealth = teamHealth;
-               }
-               else
-               {
-                       if(!secondTeam || teamHealth > secondTeamHealth)
+                       // Add up health for the players in this team
+                       FOREACH_CLIENT(IS_PLAYER(it) && Entity_HasValidTeam(it) && it.team == Team_IndexToTeam(teamIndex),
+                       {
+                               if (IS_DEAD(it))
+                                       continue;
+                               teamHealth += GetResource(it, RES_HEALTH) + GetResource(it, RES_ARMOR);
+                       });
+
+                       // Set the winner teams
+                       if(!winnerTeam || teamHealth > winnerTeamHealth)
+                       {
+                               secondTeam = winnerTeam;
+                               secondTeamHealth = winnerTeamHealth;
+                               winnerTeam = Team_IndexToTeam(i);
+                               winnerTeamHealth = teamHealth;
+                       }
+                       else
                        {
-                               secondTeam = Team_IndexToTeam(i);
-                               secondTeamHealth = teamHealth;
+                               if(!secondTeam || teamHealth > secondTeamHealth)
+                               {
+                                       secondTeam = Team_IndexToTeam(i);
+                                       secondTeamHealth = teamHealth;
+                               }
                        }
                }
-       }
 
-       if(winnerTeamHealth != secondTeamHealth)
-       {
-               LOG_INFOF("Stalemate broken by team health. Best team: %s%s (%d)^7 - Trailing team: %s%s (%d)",
-                       Team_ColorCode(winnerTeam), Team_ColorName(winnerTeam), winnerTeamHealth,
-                       Team_ColorCode(secondTeam), Team_ColorName(secondTeam), secondTeamHealth);
-               return winnerTeam;
+               if(winnerTeamHealth != secondTeamHealth)
+               {
+                       bprint(sprintf("Stalemate broken by team health. Best team: %s%s (%d)^7 - Trailing team: %s%s (%d)\n",
+                               Team_ColorCode(winnerTeam), Team_ColorName(winnerTeam), winnerTeamHealth,
+                               Team_ColorCode(secondTeam), Team_ColorName(secondTeam), secondTeamHealth));
+                       return winnerTeam;
+               }
        }
-       else
-               return -2; // Equality. Can't avoid the stalemate.
+
+       return -2; // Equality. Can't avoid the stalemate.
 }
 
 float CA_CheckWinner()
@@ -128,7 +143,11 @@ float CA_CheckWinner()
 
        if(round_handler_GetEndTime() > 0 && round_handler_GetEndTime() - time <= 0)
        {
-               if(autocvar_g_ca_prevent_stalemate)
+               // attempt to prevent stalemate by survivor count AND/OR total team health?
+               bool prevent_stalemate_by_survivors = (autocvar_g_ca_prevent_stalemate & BIT(0));
+               bool prevent_stalemate_by_health    = (autocvar_g_ca_prevent_stalemate & BIT(1));
+
+               if(prevent_stalemate_by_survivors || prevent_stalemate_by_health)
                        winner_team = CA_PreventStalemate();
                else
                        winner_team = -2;
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 a8a67ad44efe5e76395c1587fe1b13c4ea2ff201..26191ad9c893855b4954ff0da60eec1915d98dd3 100644 (file)
@@ -1,2 +1,5 @@
 #include "survival.qh"
+
+#ifdef GAMEQC
 REGISTER_NET_LINKED(ENT_CLIENT_SURVIVALSTATUSES)
+#endif
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 82a7e758413de31ffd73eae094b93717c67fb678..e66ebb7671d4b85e9dc832d690d9e11e67392b44 100644 (file)
@@ -1167,14 +1167,14 @@ float MapInfo_Get_ByName_NoFallbacks(string pFilename, int pAllowGenerate, Gamet
                {
                        MapInfo_Map_flags |= MAPINFO_FLAG_FRUSTRATING;
                }
-               else if(t == "noautomaplist")
+               else if(t == "donotwant" || t == "noautomaplist")
                {
-                       MapInfo_Map_flags |= MAPINFO_FLAG_NOAUTOMAPLIST;
+                       MapInfo_Map_flags |= MAPINFO_FLAG_DONOTWANT;
                }
                else if(t == "gameversion_min")
                {
                        if (cvar("gameversion") < stof(s))
-                               MapInfo_Map_flags |= MAPINFO_FLAG_NOAUTOMAPLIST;
+                               MapInfo_Map_flags |= MAPINFO_FLAG_DONOTWANT;
                }
                else if(t == "type")
                {
index 952274a73aaf0fe584a47cf2f2583478345abe8c..4d65695ae41a9391d19b1f145440c1942bfc5e70 100644 (file)
@@ -123,7 +123,7 @@ ENDCLASS(Gametype)
 
 REGISTRY(Gametypes, 32)
 REGISTER_REGISTRY(Gametypes)
-REGISTRY_SORT(Gametypes);
+REGISTRY_SORT(Gametypes)
 REGISTRY_CHECK(Gametypes)
 
 REGISTRY_DEFINE_GET(Gametypes, NULL)
@@ -143,7 +143,7 @@ const int MAPINFO_FEATURE_MONSTERS      = 8;
 const int MAPINFO_FLAG_HIDDEN           = 1; // not in lsmaps/menu/vcall/etc., can just be changed to manually
 const int MAPINFO_FLAG_FORBIDDEN        = 2; // don't even allow the map by a cvar setting that allows hidden maps
 const int MAPINFO_FLAG_FRUSTRATING      = 4; // this map is near impossible to play, enable at your own risk
-const int MAPINFO_FLAG_NOAUTOMAPLIST    = 8; // do not include when automatically building maplist (counts as hidden for maplist building purposes)
+const int MAPINFO_FLAG_DONOTWANT        = 8; // do not include in GUI voting screen or select in GotoNextMap()/GetNextMap(), unless added with `suggestmap` or required as a fallback
 
 float MapInfo_count;
 
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 1316c3283003e6ce5b5a573e47960eebdb9c2020..ba2e53d27d027c2a01bbcd38736fec993665b60b 100644 (file)
@@ -29,6 +29,7 @@ void door_blocked(entity this, entity blocker)
 {
        bool reverse = false;
        if((this.spawnflags & DOOR_CRUSH)
+               && !Q3COMPAT_COMMON
 #ifdef SVQC
                && (blocker.takedamage != DAMAGE_NO)
 #elif defined(CSQC)
@@ -43,46 +44,46 @@ void door_blocked(entity this, entity blocker)
        else
        {
 #ifdef SVQC
-               if((this.dmg) && (blocker.takedamage == DAMAGE_YES))    // Shall we bite?
+               if (this.dmg && blocker.takedamage != DAMAGE_NO)    // Shall we bite?
                        Damage (blocker, this, this, this.dmg, DEATH_HURTTRIGGER.m_id, DMG_NOWEP, blocker.origin, '0 0 0');
 #endif
 
-                // don't change direction for dead or dying stuff
-               if(IS_DEAD(blocker)
+               // don't change direction for dead or dying stuff
+               if (!IS_DEAD(blocker)
 #ifdef SVQC
-                       && (blocker.takedamage == DAMAGE_NO)
+                       && blocker.takedamage != DAMAGE_NO
 #endif
+                       && this.wait >= 0
+                       && !(Q3COMPAT_COMMON && (this.spawnflags & Q3_DOOR_CRUSHER))
                )
                {
-                       if (this.wait >= 0)
+                       if (this.state == STATE_DOWN)
                        {
-                               if (this.state == STATE_DOWN)
-                               {
-                                       if (this.classname == "door")
-                                               door_go_up(this, NULL, NULL);
-                                       else
-                                               door_rotating_go_up(this, blocker);
-                               }
+                               if (this.classname == "door")
+                                       door_go_up(this, NULL, NULL);
                                else
-                               {
-                                       if (this.classname == "door")
-                                               door_go_down(this);
-                                       else
-                                               door_rotating_go_down(this);
-                               }
-                               reverse = true;
+                                       door_rotating_go_up(this, blocker);
                        }
+                       else
+                       {
+                               if (this.classname == "door")
+                                       door_go_down(this);
+                               else
+                                       door_rotating_go_down(this);
+                       }
+                       reverse = true;
                }
 #ifdef SVQC
                else
                {
                        //gib dying stuff just to make sure
-                       if((this.dmg) && (blocker.takedamage != DAMAGE_NO))    // Shall we bite?
+                       if (this.dmg && blocker.takedamage != DAMAGE_NO && IS_DEAD(blocker))    // Shall we bite?
                                Damage (blocker, this, this, 10000, DEATH_HURTTRIGGER.m_id, DMG_NOWEP, blocker.origin, '0 0 0');
                }
 #endif
        }
-       if (!reverse && this.classname == "door")
+       // if we didn't change direction and are using a non-linear movement controller, we must pause it
+       if (!reverse && this.classname == "door" && this.move_controller)
                SUB_CalcMovePause(this);
 }
 
@@ -212,10 +213,13 @@ bool door_check_keys(entity door, entity player)
        return false;
 }
 
-void door_fire(entity this, entity actor, entity trigger)
+void door_use(entity this, entity actor, entity trigger)
 {
-       if (this.owner != this)
-               objerror (this, "door_fire: this.owner != this");
+       //dprint("door_use (model: ");dprint(this.model);dprint(")\n");
+
+       if (!this.owner)
+               return;
+       this = this.owner;
 
        if (this.spawnflags & DOOR_TOGGLE)
        {
@@ -256,14 +260,6 @@ void door_fire(entity this, entity actor, entity trigger)
        } while ((e != this) && (e != NULL));
 }
 
-void door_use(entity this, entity actor, entity trigger)
-{
-       //dprint("door_use (model: ");dprint(this.model);dprint(")\n");
-
-       if (this.owner)
-               door_fire(this.owner, actor, trigger);
-}
-
 void door_damage(entity this, entity inflictor, entity attacker, float damage, int deathtype, .entity weaponentity, vector hitloc, vector force)
 {
        if(this.spawnflags & NOSPLASH)
@@ -369,14 +365,25 @@ void door_trigger_touch(entity this, entity toucher)
 #endif
                        return;
 
-       if (time < this.door_finished)
+       if (this.owner.state == STATE_UP)
                return;
 
        // check if door is locked
        if (!door_check_keys(this, toucher))
                return;
 
-       this.door_finished = time + 1;
+       if (this.owner.state == STATE_TOP)
+       {
+               if (this.owner.nextthink < this.owner.ltime + this.owner.wait)
+               {
+                       entity e = this.owner;
+                       do {
+                               e.nextthink = e.ltime + e.wait;
+                               e = e.enemy;
+                       } while (e != this.owner);
+               }
+               return;
+       }
 
        door_use(this.owner, toucher, NULL);
 }
@@ -408,7 +415,8 @@ LinkDoors
 
 entity LinkDoors_nextent(entity cur, entity near, entity pass)
 {
-       while((cur = find(cur, classname, pass.classname)) && ((cur.spawnflags & DOOR_DONT_LINK) || cur.enemy))
+       while ((cur = find(cur, classname, pass.classname))
+       && ((!Q3COMPAT_COMMON && (cur.spawnflags & DOOR_DONT_LINK)) || cur.enemy))
        {
        }
        return cur;
@@ -416,6 +424,9 @@ entity LinkDoors_nextent(entity cur, entity near, entity pass)
 
 bool LinkDoors_isconnected(entity e1, entity e2, entity pass)
 {
+       if(Q3COMPAT_COMMON)
+               return e1.team == e2.team;
+
        float DELTA = 4;
        if((e1.absmin_x > e2.absmax_x + DELTA)
        || (e1.absmin_y > e2.absmax_y + DELTA)
@@ -441,7 +452,9 @@ void LinkDoors(entity this)
 
        if (this.enemy)
                return;         // already linked by another door
-       if (this.spawnflags & DOOR_DONT_LINK)
+
+       // Q3 door linking is done for teamed doors only and is not affected by spawnflags or bmodel proximity
+       if ((!Q3COMPAT_COMMON && (this.spawnflags & DOOR_DONT_LINK)) || (Q3COMPAT_COMMON && !this.team))
        {
                this.owner = this.enemy = this;
 
@@ -544,7 +557,7 @@ SILVER_KEY causes the door to open only if the activator holds a silver key.
 "speed"                movement speed (100 default)
 "wait"         wait before returning (3 default, -1 = never return)
 "lip"          lip remaining at end of move (8 default)
-"dmg"          damage to inflict when blocked (2 default)
+"dmg"          damage to inflict when blocked (0 default)
 "sounds"
 0)     no sound
 1)     stone
@@ -666,14 +679,28 @@ void door_init_shared(entity this)
 
        if (q3compat)
        {
-               // CPMA adds these fields for overriding the engine sounds
+               // CPMA adds these fields for overriding the Q3 default sounds
                string s = GetField_fullspawndata(this, "sound_start", true);
                string e = GetField_fullspawndata(this, "sound_end", true);
 
                if (s)
                        this.noise2 = strzone(s);
+               else
+               {
+                       // PK3s supporting Q3A sometimes include custom sounds at Q3 default paths
+                       s = "sound/movers/doors/dr1_strt.wav";
+                       if (FindFileInMapPack(s))
+                               this.noise2 = s;
+               }
+
                if (e)
                        this.noise1 = strzone(e);
+               else
+               {
+                       e = "sound/movers/doors/dr1_end.wav";
+                       if (FindFileInMapPack(e))
+                               this.noise1 = e;
+               }
        }
 
        // sound when door stops moving
@@ -693,7 +720,7 @@ void door_init_shared(entity this)
        }
        else if (!this.wait)
        {
-               this.wait = 3;
+               this.wait = q3compat ? 2 : 3;
        }
 
        if (!this.lip)
@@ -738,15 +765,20 @@ spawnfunc(func_door)
        if(this.spawnflags & DOOR_NONSOLID)
                this.solid = SOLID_NOT;
 
-// DOOR_START_OPEN is to allow an entity to be lighted in the closed position
-// but spawn in the open position
+       // DOOR_START_OPEN is to allow an entity to be lighted in the closed position
+       // but spawn in the open position
+       // the tuba door on xoylent requires the delayed init
        if (this.spawnflags & DOOR_START_OPEN)
                InitializeEntity(this, door_init_startopen, INITPRIO_SETLOCATION);
 
        door_init_shared(this);
 
        this.pos1 = this.origin;
-       this.pos2 = this.pos1 + this.movedir*(fabs(this.movedir*this.size) - this.lip);
+       vector absmovedir;
+       absmovedir.x = fabs(this.movedir.x);
+       absmovedir.y = fabs(this.movedir.y);
+       absmovedir.z = fabs(this.movedir.z);
+       this.pos2 = this.pos1 + this.movedir * (absmovedir * this.size - this.lip);
 
        if(autocvar_sv_doors_always_open)
        {
@@ -760,10 +792,23 @@ spawnfunc(func_door)
                        this.speed = 100;
        }
 
+       if (q3compat)
+       {
+               if (!this.dmg)
+                       this.dmg = 2;
+
+               if (!this.team)
+               {
+                       string t = GetField_fullspawndata(this, "team");
+                       // bones_was_here: same hack as used to support teamed items on Q3 maps
+                       if (t) this.team = crc16(false, t);
+               }
+       }
+
        settouch(this, door_touch);
 
-// LinkDoors can't be done until all of the doors have been spawned, so
-// the sizes can be detected properly.
+       // LinkDoors can't be done until all of the doors have been spawned, so
+       // the sizes can be detected properly.
        InitializeEntity(this, LinkDoors, INITPRIO_LINKDOORS);
 
        this.reset = door_reset;
index f185f4be8fc96e307ae05ee89078b3174396fb44..ce7025dd5488854e741bb166c9c3e2db8b1a8f2a 100644 (file)
@@ -4,7 +4,7 @@
 bool autocvar_sv_doors_always_open;
 #endif
 
-const int DOOR_START_OPEN = BIT(0);
+const int DOOR_START_OPEN = BIT(0); // has same meaning in Q3: reverse position 1 and 2
 const int DOOR_DONT_LINK = BIT(2);
 const int SPAWNFLAGS_GOLD_KEY = BIT(3); // Quake 1 compat, can only be used with func_door!
 const int SPAWNFLAGS_SILVER_KEY = BIT(4); // Quake 1 compat, can only be used with func_door!
@@ -13,6 +13,7 @@ const int DOOR_TOGGLE = BIT(5);
 const int DOOR_NONSOLID = BIT(10);
 const int DOOR_CRUSH = BIT(11); // can't use CRUSH cause that is the same as DOOR_DONT_LINK
 
+#define Q3_DOOR_CRUSHER BIT(2) // in Q3 this disables the auto reverse so the blocking player takes damage every frame
 
 #ifdef CSQC
 // stuff for preload
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 2376c5956de1adbdc227654e30b819c8d5137b0f..23b9eb5b1725eee170cfd1ab7ab5c020bca102b3 100644 (file)
@@ -7,7 +7,9 @@ void plat_link(entity this);
 void plat_delayedinit(entity this)
 {
        plat_link(this);
-       plat_spawn_inside_trigger(this); // the "start moving" trigger
+       // Q3 uses only a truth check of .targetname to decide whether to spawn a trigger
+       if (!Q3COMPAT_COMMON || this.targetname == "")
+               plat_spawn_inside_trigger(this); // the "start moving" trigger
 }
 
 float plat_send(entity this, entity to, float sf)
@@ -56,7 +58,12 @@ void plat_link(entity this)
 
 spawnfunc(func_plat)
 {
-       if (this.spawnflags & CRUSH)
+       if (q3compat)
+       {
+               this.spawnflags = 0; // Q3 plats have no spawnflags
+               if (!this.dmg) this.dmg = 2;
+       }
+       else if (this.spawnflags & CRUSH)
        {
                this.dmg = 10000;
        }
@@ -91,14 +98,28 @@ spawnfunc(func_plat)
 
        if (q3compat)
        {
-               // CPMA adds these fields for overriding the engine sounds
+               // CPMA adds these fields for overriding the Q3 default sounds
                string s = GetField_fullspawndata(this, "sound_start", true);
                string e = GetField_fullspawndata(this, "sound_end", true);
 
                if (s)
                        this.noise = strzone(s);
+               else
+               {
+                       // PK3s supporting Q3A sometimes include custom sounds at Q3 default paths
+                       s = "sound/movers/plats/pt1_strt.wav";
+                       if (FindFileInMapPack(s))
+                               this.noise = s;
+               }
+
                if (e)
                        this.noise1 = strzone(e);
+               else
+               {
+                       e = "sound/movers/plats/pt1_end.wav";
+                       if (FindFileInMapPack(e))
+                               this.noise1 = e;
+               }
        }
 
        if(this.noise && this.noise != "")
@@ -122,8 +143,8 @@ spawnfunc(func_plat)
 
        setblocked(this, plat_crush);
 
-       if (!this.speed) this.speed = 150;
-       if (!this.lip) this.lip = 16;
+       if (!this.speed) this.speed = q3compat ? 200 : 150;
+       if (!this.lip) this.lip = q3compat ? 8 : 16;
        if (!this.height) this.height = this.size.z - this.lip;
 
        this.pos1 = this.origin;
@@ -186,7 +207,8 @@ NET_HANDLE(ENT_CLIENT_PLAT, bool isnew)
                set_movetype(this, MOVETYPE_PUSH);
                this.move_time = time;
 
-               plat_spawn_inside_trigger(this);
+               if (!Q3COMPAT_COMMON || this.targetname == "")
+                       plat_spawn_inside_trigger(this);
        }
 
        if(sf & SF_TRIGGER_RESET)
index 28b420b20ab4f2ed6c49bde68e5383d365de1b6b..08458a630b3aa1187e330039111104a4ae7406c4 100644 (file)
@@ -173,13 +173,21 @@ void plat_use(entity this, entity actor, entity trigger)
        plat_go_down(this);
 }
 
+void plat_target_use(entity this, entity actor, entity trigger)
+{
+       if (this.state == STATE_TOP)
+               this.nextthink = this.ltime + 1;
+       else if (this.state != STATE_UP)
+               plat_go_up(this);
+}
+
 // WARNING: backwards compatibility because people don't use already existing fields :(
 // TODO: Check if any maps use these fields and remove these fields if it doesn't break maps
 .string sound1, sound2;
 
 void plat_reset(entity this)
 {
-       if(this.targetname && this.targetname != "")
+       if (this.targetname && this.targetname != "" && !Q3COMPAT_COMMON)
        {
                setorigin(this, this.pos1);
                this.state = STATE_UP;
@@ -189,7 +197,7 @@ void plat_reset(entity this)
        {
                setorigin(this, this.pos2);
                this.state = STATE_BOTTOM;
-               this.use = plat_trigger_use;
+               this.use = (this.targetname != "" && Q3COMPAT_COMMON) ? plat_target_use : plat_trigger_use;
        }
 
 #ifdef SVQC
index f89ab21e2f79ab1903dabc12c10da0cbcefac9ef..09ceba21f0bb4caee58bc093bfb1ea6a11b5e8a6 100644 (file)
@@ -298,6 +298,11 @@ void SUB_CalcMove (entity this, vector tdest, float tspeedtype, float tspeed, vo
                        break;
        }
 
+       // Q3 implements this fallback for all movers at the end of its InitMover()
+       // If .speed is negative this applies, instead of the mover-specific default speed.
+       if (traveltime <= 0)
+               traveltime = 0.001;
+
        // Very short animations don't really show off the effect
        // of controlled animation, so let's just use linear movement.
        // Alternatively entities can choose to specify non-controlled movement.
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 ae465dfdb3a5e8bfd0049894e403bb74a718dab6..a14ad6bb8cdb4e6062cbae72c7244b527bee6177 100644 (file)
@@ -8,11 +8,6 @@
 #include <common/mutators/mutator/waypoints/all.qh>
 #endif
 
-#ifdef GAMEQC
-REGISTER_WAYPOINT(Buff, _("Buff"), "", '1 0.5 0', 1);
-REGISTER_RADARICON(Buff, 1);
-#endif
-
 #define REGISTER_BUFF(id, inst) \
     REGISTER(StatusEffect, BUFF_##id, m_id, inst)
 
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 7cdd567f1a59d7057c1c36348cf38b54fa1d72d8..0ed133530888e8791e4269bfa93c47f62265adf0 100644 (file)
@@ -1,3 +1,5 @@
 #include "damagetext.qh"
 
+#ifdef GAMEQC
 REGISTER_NET_LINKED(damagetext)
+#endif
index 19045727b6cc8e8a0ae603d0315713c89b148924..5aa59bffc38eb6f29f2da638dfb25a9527d54e44 100644 (file)
@@ -2,7 +2,9 @@
 
 REGISTER_MUTATOR(itemstime, true);
 
+#ifdef GAMEQC
 REGISTER_NET_TEMP(itemstime)
+#endif
 
 #ifdef SVQC
 void IT_Write(entity e, int i, float f) {
index 01f78c05b3f938d000ee1fd568391283388d815b..3f9e54cdbd97b40d9c153198c9eeccad918ab9e1 100644 (file)
@@ -37,9 +37,11 @@ entity Nade_TrailEffect(int proj, int nade_team)
 
     return EFFECT_Null;
 }
-#endif
 
 REGISTER_NET_TEMP(TE_CSQC_DARKBLINKING);
+
+#endif
+
 #ifdef CSQC
 #include <client/draw.qh>
 #include <client/hud/hud.qh>
index bf8d8fb10a903820e9ad9e546c444c05b9274c19..690abb5ca2fe207b7fb41bd7c6ac7af82bcc7786 100644 (file)
@@ -51,6 +51,7 @@ REGISTER_ITEM(Invisibility, Powerup) {
 }
 
 SPAWNFUNC_ITEM(item_invisibility, ITEM_Invisibility)
+SPAWNFUNC_ITEM(item_buff_invisibility, ITEM_Invisibility)
 
 CLASS(Invisibility, Powerups)
     ATTRIB(Invisibility, netname, string, "invisibility");
index c816fec53a7ffc8f3fc6d48c2dad5bbe853c1f11..38010e480b1c723b5d0449ef0616ad0629cd3c4b 100644 (file)
@@ -52,6 +52,7 @@ REGISTER_ITEM(Speed, Powerup) {
 }
 
 SPAWNFUNC_ITEM(item_speed, ITEM_Speed)
+SPAWNFUNC_ITEM(item_buff_speed, ITEM_Speed)
 
 CLASS(Speed, Powerups)
     ATTRIB(Speed, netname, string, "speed");
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 98b1f4d3ce8b5f78bee76adc87a0e057690ef0cd..50664831af3cf073c02b9ffbb27b8272981a63e0 100644 (file)
@@ -71,3 +71,5 @@ REGISTER_WAYPOINT(Vehicle, _("Vehicle"), "", '1 1 1', 1);
 REGISTER_WAYPOINT(VehicleIntruder, _("Intruder!"), "", '1 1 1', 1);
 
 REGISTER_WAYPOINT(Seeker, _("Tagged"), "", '0.5 1 0', 2);
+
+REGISTER_WAYPOINT(Buff, _("Buff"), "", '1 0.5 0', 1);
index fa1cbd31a1548d45a15770902c71fe858ccaa805..b5f5f161d3c72c52aed841ed2082b839799c431f 100644 (file)
@@ -58,5 +58,6 @@ REGISTER_RADARICON(TAGGED,          1);
 REGISTER_RADARICON(Item,            1);
 REGISTER_RADARICON(Vehicle,         1);
 REGISTER_RADARICON(Weapon,          1);
+REGISTER_RADARICON(Buff,            1);
 
 #include "all.inc"
index f19fc059e393661839737c680b3089491e7bd4db..0625f412ee18cc37c8ffbe6edab3f735bef2402a 100644 (file)
@@ -2,7 +2,9 @@
 
 REGISTER_MUTATOR(waypointsprites, true);
 
+#ifdef GAMEQC
 REGISTER_NET_LINKED(waypointsprites)
+#endif
 
 #ifdef SVQC
 bool WaypointSprite_SendEntity(entity this, entity to, float sendflags)
index 00bd94bb740066bc03810210ece3d6238d895da8..f4c25968cc2bf3154b7bd2643c4403ad946b0055 100644 (file)
@@ -714,7 +714,7 @@ string notif_arg_item_wepammo(float f1, float f2)
 
 REGISTRY(Notifications, BITS(11))
 REGISTER_REGISTRY(Notifications)
-REGISTRY_SORT(Notifications);
+REGISTRY_SORT(Notifications)
 
 REGISTRY_DEFINE_GET(Notifications, NULL)
 STATIC_INIT(Notifications) { FOREACH(Notifications, true, it.m_id = i); }
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 fa7ce8b73bf448e928a254fb1f62e687d581e0fc..28ad1f82adb628e128887661a1ddf191f346a034 100644 (file)
@@ -49,8 +49,8 @@ STATIC_INIT(default_order_resources)
 }
 #endif
 
-REGISTRY_SORT(Resources);
-REGISTRY_CHECK(Resources);
+REGISTRY_SORT(Resources)
+REGISTRY_CHECK(Resources)
 
 REGISTRY_DEFINE_GET(Resources, NULL)
 STATIC_INIT(Resources_renumber) { FOREACH(Resources, true, it.m_id = i); }
index 8a01893f1019c423927029d57b004baa8f66b88c..8f22b5c7525bcea5c80f01d594c9fc38a11660a7 100644 (file)
@@ -3,11 +3,11 @@
 #define MAX_SCORE 64
 
 #define REGISTER_SP(id) REGISTER(Scores, SP, id, m_id, new_pure(PlayerScoreField))
-REGISTRY(Scores, MAX_SCORE);
+REGISTRY(Scores, MAX_SCORE)
 REGISTER_REGISTRY(Scores)
 // do not sort alphabetically, player sort priority is based on score registration order
-//REGISTRY_SORT(Scores);
-REGISTRY_CHECK(Scores);
+//REGISTRY_SORT(Scores)
+REGISTRY_CHECK(Scores)
 
 REGISTRY_DEFINE_GET(Scores, NULL)
 STATIC_INIT(Scores_renumber) { FOREACH(Scores, true, it.m_id = i); }
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 bd4e5975a21cb89cf725316b75db15f5a2208608..b41a14f3452196e6b2d12cb5a5c1d0a90f6499a9 100644 (file)
@@ -1,6 +1,8 @@
 #include "shockwave.qh"
 
+#ifdef GAMEQC
 REGISTER_NET_TEMP(TE_CSQC_SHOCKWAVEPARTICLE)
+#endif
 
 #ifdef SVQC
 
index 4be54b6b220378c28746b2f864cc1bc730681e46..9ca0b3192d71f5394879ed6c0e64f7eb38953eb1 100644 (file)
@@ -1,6 +1,8 @@
 #include "vaporizer.qh"
 
+#ifdef GAMEQC
 REGISTER_NET_TEMP(TE_CSQC_VAPORBEAMPARTICLE)
+#endif
 
 #if defined(SVQC)
 void SendCSQCVaporizerBeamParticle(entity player, int hit) {
index 4d4e43ec6cb7ea0356eb8317da2fe5833457d4e0..d9cacac00de1e5092b13d52f747fc741dadfd2dc 100644 (file)
@@ -24,7 +24,9 @@ METHOD(Vortex, wr_glow, vector(Vortex this, int actor_colors, entity wepent))
 }
 #endif
 
+#ifdef GAMEQC
 REGISTER_NET_TEMP(TE_CSQC_VORTEXBEAMPARTICLE)
+#endif
 
 #if defined(SVQC)
 void SendCSQCVortexBeamParticle(float charge) {
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 c9cdf6981b8533cf6eb85f88a478c8ee4150c8ea..7c490fd157e477c70d723b3663b77116c2f6f833 100644 (file)
@@ -7,6 +7,9 @@
 // netcode mismatch and not sure what caused it? developer_csqcentities 1
 
 .string netname;
+
+#ifdef GAMEQC
+
 .int m_id;
 .bool(entity this, entity sender, bool isNew) m_read;
 #define NET_HANDLE(id, param) bool Net_Handle_##id(entity this, entity sender, param)
@@ -37,7 +40,6 @@ string _net_prevmsgstr;
                        this.netname = #id; \
                }
 #endif
-#define REGISTER_NET_S2C(id) REGISTER_NET_TEMP(id)
 
 REGISTRY(TempEntities, BITS(8) - 80)
 REGISTER_REGISTRY(TempEntities)
@@ -107,6 +109,8 @@ REGISTRY_CHECK(C2S_Protocol)
 REGISTRY_DEFINE_GET(C2S_Protocol, NULL)
 STATIC_INIT(C2S_Protocol_renumber) { FOREACH(C2S_Protocol, true, it.m_id = i); }
 
+#endif // GAMEQC
+
 #ifdef SVQC
        const int MSG_ENTITY = 5;
 
index fdcc730a068527b73b4a1e2291fc910616e9b53f..a610df2044d6745b7ffde1dc6412ce7516cd18b5 100644 (file)
@@ -177,13 +177,30 @@ void Registry_send(string id, string hash);
        STATIC_INIT(Registry_check_##id) \
        { \
                /* Note: SHA256 isn't always available, use MD4 instead */ \
-               string s = ""; \
-               FOREACH(id, true, s = strcat(s, ":", it.registered_id)); \
+               string s = "", group = ""; \
+               int str_len = 0, digests_len = 0, group_idx = 0; \
+               FOREACH(id, true, { \
+                       group = strcat(group, ":", it.registered_id); \
+                       if (++group_idx < 50) /* this is to reduce strlen calls */ \
+                               continue; \
+                       int group_len = strlen(group); \
+                       if (str_len + 1 + group_len >= VM_TEMPSTRING_MAXSIZE) \
+                       { \
+                               /* keep previous digests and replace current string with its digest */ \
+                               s = strcat(substring(s, 0, digests_len), ":", digest_hex("MD4", s)); \
+                               digests_len = str_len = strlen(s); \
+                       } \
+                       s = strcat(s, group); \
+                       str_len += group_len; \
+                       group = ""; \
+                       group_idx = 0; \
+               }); \
+               s = strcat(s, group); \
                s = substring(s, 1, -1); /* remove initial ":" */ \
                string h = REGISTRY_HASH(id) = strzone(digest_hex("MD4", s)); \
                LOG_DEBUGF(#id ": %s\n[%s]", h, s); \
        } \
-       void Registry_check(string r, string sv) \
+       void Registry_check(string r, string sv) /* called by CSQC */ \
        { \
                if (r == #id) \
                { \
@@ -194,7 +211,7 @@ void Registry_send(string id, string hash);
                        } \
                } \
        } \
-       void Registry_send_all() { Registry_send(#id, REGISTRY_HASH(id)); } \
+       void Registry_send_all() { Registry_send(#id, REGISTRY_HASH(id)); } /* called by SVQC */ \
 
 #define _REGISTER_REGISTRY(id, str) \
        ACCUMULATE_FUNCTION(__static_init_1, Register##id) \
index 6c947d7c5db192d390eab5d9b3616e190af37f56..1c5b04342a27eb0fcadb5955b7cf237ec52f3a29 100644 (file)
@@ -2,7 +2,9 @@
 
 #include "net.qh"
 
+#ifdef GAMEQC
 REGISTER_NET_TEMP(registry)
+#endif
 
 #ifdef CSQC
 NET_HANDLE(registry, bool isnew)
index 3c39b7c0bb8e93a0f94ec95cbd081a41e44ba3e2..72673b2354517e28c5abd96c1cc1eb8f160b5b70 100644 (file)
@@ -4,6 +4,9 @@
 #include "sort.qh"
 #include "oo.qh"
 
+// this is not exactly 16KiB (16384 bytes) because one byte is reserved for the \0 terminator
+#define VM_TEMPSTRING_MAXSIZE 16383
+
 // string logic
 //
 // true: is truthy
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..8bff8c4c67eec298b31f2ea589872a23575e6069 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)
        {
@@ -917,6 +920,10 @@ void m_toggle(int mode)
        if (Menu_Active)
        {
                if (mode == 1) return;
+               // when togglemenu is called without arguments (mode is -1)
+               // the menu is closed only when connected
+               if (mode == -1 && !(gamestatus & GAME_CONNECTED)) return;
+               // togglemenu 0 always closes the menu
                m_hide();
        }
        else
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 f05edfcb5a670225986eb18ae6f679645d540c9b..3fdb1f968c5b75b96363643c1286837850d67ab4 100644 (file)
@@ -21,6 +21,7 @@
 #include <server/command/radarmap.qh>
 #include <server/intermission.qh>
 #include <server/ipban.qh>
+#include <server/mapvoting.qh>
 #include <server/mutators/_mod.qh>
 #include <server/player.qh>
 #include <server/scores_rules.qh>
@@ -774,30 +775,12 @@ void GameCommand_gametype(int request, int argc)
                        if (argv(1) != "")
                        {
                                string s = argv(1);
-                               Gametype t = MapInfo_Type_FromString(s, false, false), tsave = MapInfo_CurrentGametype();
+                               Gametype t = MapInfo_Type_FromString(s, false, false);
 
                                if (t)
-                               {
-                                       MapInfo_SwitchGameType(t);
-                                       MapInfo_FilterGametype(MapInfo_CurrentGametype(), MapInfo_CurrentFeatures(), MapInfo_RequiredFlags(), MapInfo_ForbiddenFlags(), 0);
-                                       if (MapInfo_count > 0)
-                                       {
-                                               // 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 ", s, "\n");
-                                       }
-                                       else
-                                       {
-                                               bprint("Cannot use this game type: no map for it found\n");
-                                               MapInfo_SwitchGameType(tsave);
-                                               MapInfo_FilterGametype(MapInfo_CurrentGametype(), MapInfo_CurrentFeatures(), MapInfo_RequiredFlags(), MapInfo_ForbiddenFlags(), 0);
-                                       }
-                               }
+                                       GameTypeVote_SetGametype(t);
                                else
-                               {
                                        bprint("Failed to switch to ", s, ": this game type does not exist!\n");
-                               }
 
                                return;
                        }
index 9392c2399f3942aac3a2598568b4e7111be6971e..917eb8ce44b18901b3704e7226cf3bfaf99ea1dd 100644 (file)
@@ -20,8 +20,7 @@ string GetMapname()
        return mapname;
 }
 
-float Map_Count, Map_Current;
-string Map_Current_Name;
+int Map_Count, Map_Current;
 
 // NOTE: this now expects the map list to be already tokenized and the count in Map_Count
 int GetMaplistPosition()
@@ -130,6 +129,9 @@ bool Map_Check(int position, float pass)
        {
                if(pass == 2)
                        return true;
+               // MapInfo_Map_flags was set by MapInfo_CheckMap()
+               if (MapInfo_Map_flags & MAPINFO_FLAG_DONOTWANT)
+                       return false;
                if(MapHasRightSize(map_next))
                        return true;
                return false;
@@ -150,8 +152,9 @@ void Map_Goto_SetStr(string nextmapname)
                getmapname_stored = strzone(nextmapname);
 }
 
-void Map_Goto_SetFloat(float position)
+void Map_Goto_SetIndex(int position)
 {
+       Map_Current = position;
        cvar_set("g_maplist_index", ftos(position));
        Map_Goto_SetStr(argv(position));
 }
@@ -164,9 +167,9 @@ void Map_Goto(float reinit)
 // return codes of map selectors:
 //   -1 = temporary failure (that is, try some method that is guaranteed to succeed)
 //   -2 = permanent failure
-float MaplistMethod_Iterate() // usual method
+int MaplistMethod_Iterate(void) // usual method
 {
-       float pass, i;
+       int pass, i;
 
        LOG_TRACE("Trying MaplistMethod_Iterate");
 
@@ -174,7 +177,7 @@ float MaplistMethod_Iterate() // usual method
        {
                for(i = 1; i < Map_Count; ++i)
                {
-                       float mapindex;
+                       int mapindex;
                        mapindex = (i + Map_Current) % Map_Count;
                        if(Map_Check(mapindex, pass))
                                return mapindex;
@@ -183,7 +186,7 @@ float MaplistMethod_Iterate() // usual method
        return -1;
 }
 
-float MaplistMethod_Repeat() // fallback method
+int MaplistMethod_Repeat(void) // fallback method
 {
        LOG_TRACE("Trying MaplistMethod_Repeat");
 
@@ -192,9 +195,9 @@ float MaplistMethod_Repeat() // fallback method
        return -2;
 }
 
-float MaplistMethod_Random() // random map selection
+int MaplistMethod_Random(void) // random map selection
 {
-       float i, imax;
+       int i, imax;
 
        LOG_TRACE("Trying MaplistMethod_Random");
 
@@ -202,7 +205,7 @@ float MaplistMethod_Random() // random map selection
 
        for(i = 0; i <= imax; ++i)
        {
-               float mapindex;
+               int mapindex;
                mapindex = (Map_Current + floor(random() * (Map_Count - 1) + 1)) % Map_Count; // any OTHER map
                if(Map_Check(mapindex, 1))
                        return mapindex;
@@ -212,7 +215,7 @@ float MaplistMethod_Random() // random map selection
 
 // the exponent sets a bias on the map selection:
 // the higher the exponent, the less likely "shortly repeated" same maps are
-float MaplistMethod_Shuffle(float exponent) // more clever shuffling
+int MaplistMethod_Shuffle(float exponent) // more clever shuffling
 {
        float i, j, imax, insertpos;
 
@@ -252,57 +255,57 @@ float MaplistMethod_Shuffle(float exponent) // more clever shuffling
                Map_Count = tokenizebyseparator(autocvar_g_maplist, " ");
 
                // NOTE: the selected map has just been inserted at (insertpos-1)th position
-               Map_Current = insertpos - 1; // this is not really valid, but this way the fallback has a chance of working
-               if(Map_Check(Map_Current, 1))
-                       return Map_Current;
+               if (Map_Check(insertpos - 1, 1))
+                       return insertpos - 1;
        }
        return -1;
 }
 
-void Maplist_Init()
+int Maplist_Init(void)
 {
-       float i = Map_Count = 0;
+       int i, available_maps = 0;
+       Map_Count = 0;
        if(autocvar_g_maplist != "")
        {
                Map_Count = tokenizebyseparator(autocvar_g_maplist, " ");
                for (i = 0; i < Map_Count; ++i)
-               {
                        if (Map_Check(i, 2))
-                               break;
-               }
+                               ++available_maps;
        }
 
-       if (i == Map_Count)
+       if (!available_maps)
        {
                bprint( "Maplist contains no usable maps!  Resetting it to default map list.\n" );
-               cvar_set("g_maplist", MapInfo_ListAllAllowedMaps(MapInfo_RequiredFlags(), MapInfo_ForbiddenFlags() | MAPINFO_FLAG_NOAUTOMAPLIST));
-               if(autocvar_g_maplist_shuffle)
-                       ShuffleMaplist();
+               cvar_set("g_maplist", MapInfo_ListAllowedMaps(MapInfo_CurrentGametype(), MapInfo_RequiredFlags(), MapInfo_ForbiddenFlags()));
                if(!server_is_dedicated)
                        localcmd("\nmenu_cmd sync\n");
                Map_Count = tokenizebyseparator(autocvar_g_maplist, " ");
+               for (i = 0; i < Map_Count; ++i)
+                       if (Map_Check(i, 2))
+                               ++available_maps;
        }
+
        if(Map_Count == 0)
                error("empty maplist, cannot select a new map");
+
        Map_Current = bound(0, GetMaplistPosition(), Map_Count - 1);
 
-       strcpy(Map_Current_Name, argv(Map_Current)); // will be automatically freed on exit thanks to DP
-       // this may or may not be correct, but who cares, in the worst case a map
-       // isn't chosen in the first pass that should have been
+       if(autocvar_g_maplist_shuffle)
+               cvar_set("g_maplist", shufflewords(autocvar_g_maplist));
+
+       return available_maps;
 }
 
-string GetNextMap()
+// NOTE: call Maplist_Init() before making GetNextMap() call(s)
+string GetNextMap(void)
 {
-       Maplist_Init();
-       float nextMap = -1;
+       int nextMap = -1;
 
-       if(nextMap == -1)
-               if(autocvar_g_maplist_shuffle > 0)
-                       nextMap = MaplistMethod_Shuffle(autocvar_g_maplist_shuffle + 1);
+       if(nextMap == -1 && autocvar_g_maplist_shuffle > 0)
+               nextMap = MaplistMethod_Shuffle(autocvar_g_maplist_shuffle + 1);
 
-       if(nextMap == -1)
-               if(autocvar_g_maplist_selectrandom)
-                       nextMap = MaplistMethod_Random();
+       if(nextMap == -1 && autocvar_g_maplist_selectrandom)
+               nextMap = MaplistMethod_Random();
 
        if(nextMap == -1)
                nextMap = MaplistMethod_Iterate();
@@ -312,7 +315,7 @@ string GetNextMap()
 
        if(nextMap >= 0)
        {
-               Map_Goto_SetFloat(nextMap);
+               Map_Goto_SetIndex(nextMap);
                return getmapname_stored;
        }
 
@@ -389,17 +392,13 @@ void GotoNextMap(float reinit)
                return;
        alreadychangedlevel = true;
 
+       Maplist_Init();
        string nextMap = GetNextMap();
        if(nextMap == "")
                error("Everything is broken - cannot find a next map. Please report this to the developers.");
        Map_Goto(reinit);
 }
 
-void ShuffleMaplist()
-{
-       cvar_set("g_maplist", shufflewords(autocvar_g_maplist));
-}
-
 string GotoMap(string m)
 {
        m = GameTypeVote_MapInfo_FixName(m);
index e3504829b57cdb27b11049c590432b336a240dbe..67413147c18bc3e231e57619dced90864cc1521e 100644 (file)
@@ -24,9 +24,8 @@ bool Map_IsRecent(string m);
 
 bool Map_Check(int position, float pass);
 
-string GetNextMap();
-
-void ShuffleMaplist();
+int Maplist_Init(void);
+string GetNextMap(void);
 
 void Map_Goto_SetStr(string nextmapname);
 
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 f22347e8411632de37f40794976e87ad8b66ffbb..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;
@@ -477,6 +485,32 @@ string GetField_fullspawndata(entity e, string f, ...)
        return v;
 }
 
+/*
+=============
+FindFileInMapPack
+
+Returns the first matching VFS file path that exists in the current map's pack.
+Returns string_null if no files match or the map isn't packaged.
+=============
+*/
+string FindFileInMapPack(string pattern)
+{
+       if (!checkextension("DP_QC_FS_SEARCH_PACKFILE"))
+               return string_null;
+
+       string base_pack = whichpack(strcat("maps/", mapname, ".bsp"));
+       if (base_pack == "" || !base_pack) // this map isn't packaged or there was an error
+               return string_null;
+
+       int glob = search_packfile_begin(pattern, true, true, base_pack);
+       if (glob < 0)
+               return string_null;
+
+       string file = search_getfilename(glob, 0);
+       search_end(glob);
+       return file;
+}
+
 void WarpZone_PostInitialize_Callback()
 {
        // create waypoint links for warpzones
index 051968eb00c488cbef6ebc9d53f2dcca818a9fca..d8ba13dcc2633ae0e0925143b9c145eea2d8530e 100644 (file)
@@ -12,7 +12,6 @@
 #include <server/command/cmd.qh>
 #include <server/command/getreplies.qh>
 #include <server/gamelog.qh>
-#include <server/intermission.qh>
 #include <server/world.qh>
 
 // definitions
@@ -203,16 +202,7 @@ void MapVote_AddVotable(string nextMap, bool isSuggestion)
 
 void MapVote_AddVotableMaps(int nmax, int smax)
 {
-       int available_maps = 0;
-       if (autocvar_g_maplist != "")
-       {
-               int c = tokenizebyseparator(autocvar_g_maplist, " ");
-               for (int i = 0; i < c; ++i)
-               {
-                       if (Map_Check(i, 1) || Map_Check(i, 2))
-                               ++available_maps;
-               }
-       }
+       int available_maps = Maplist_Init();
        int max_attempts = available_maps;
        if (available_maps >= 2)
                max_attempts = min(available_maps * 5, 100);
@@ -225,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;
@@ -252,16 +254,6 @@ void MapVote_Init()
 
        MapVote_AddVotableMaps(nmax, smax);
 
-       if(mapvote_count == 0)
-       {
-               bprint( "Maplist contains no single playable map!  Resetting it to default map list.\n" );
-               cvar_set("g_maplist", MapInfo_ListAllowedMaps(MapInfo_CurrentGametype(), MapInfo_RequiredFlags(), MapInfo_ForbiddenFlags()));
-               if(autocvar_g_maplist_shuffle)
-                       ShuffleMaplist();
-               localcmd("\nmenu_cmd sync\n");
-               MapVote_AddVotableMaps(nmax, 0);
-       }
-
        mapvote_count_real = mapvote_count;
        if(mapvote_abstain)
                MapVote_AddVotable("don't care", false);
@@ -274,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)
@@ -394,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
@@ -691,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);
                }
@@ -762,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
        {
@@ -772,11 +781,7 @@ bool GameTypeVote_SetGametype(Gametype type)
                return false;
        }
 
-       //localcmd("gametype ", MapInfo_Type_ToString(type), "\n");
-
        cvar_set("g_maplist", MapInfo_ListAllowedMaps(type, MapInfo_RequiredFlags(), MapInfo_ForbiddenFlags()) );
-       if(autocvar_g_maplist_shuffle)
-               ShuffleMaplist();
 
        return true;
 }
@@ -787,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 ed8ef6ca1e206f85a9c0648045098b3b0ad74d53..36578740c07fff735e11041ce9811d1284727484 100644 (file)
@@ -30,8 +30,9 @@ void MapVote_Start();
 void MapVote_Spawn();
 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 6e79c73ef1c014d1de388729a8de6f15fdbffd1f..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>
@@ -118,12 +119,7 @@ void GotoFirstMap(entity this)
        {
                // cvar_set("_sv_init", "0");
                // we do NOT set this to 0 any more, so someone "accidentally" changing
-               // to this "init" map on a dedicated server will cause no permanent
-               // harm
-               if(autocvar_g_maplist_shuffle)
-                       ShuffleMaplist();
-               n = tokenizebyseparator(autocvar_g_maplist, " ");
-               cvar_set("g_maplist_index", ftos(n - 1)); // jump to map 0 in GotoNextMap
+               // to this "init" map on a dedicated server will cause no permanent harm
 
                MapInfo_Enumerate();
                MapInfo_FilterGametype(MapInfo_CurrentGametype(), MapInfo_CurrentFeatures(), MapInfo_RequiredFlags(), MapInfo_ForbiddenFlags(), 0);
@@ -337,8 +333,11 @@ void cvar_changes_init()
 
                if(adding)
                {
+                       if (cvar_changes == "")
+                               cvar_changes = "// this server runs at modified server settings:\n";
+
                        cvar_changes = strcat(cvar_changes, k, " \"", v, "\" // \"", d, "\"\n");
-                       if(strlen(cvar_changes) > 16384)
+                       if(strlen(cvar_changes) >= VM_TEMPSTRING_MAXSIZE)
                        {
                                cvar_changes = "// too many settings have been changed to show them here\n";
                                adding = 0;
@@ -551,8 +550,11 @@ void cvar_changes_init()
 
                if(pureadding)
                {
+                       if (cvar_purechanges == "")
+                               cvar_purechanges = "// this server runs at modified gameplay settings:\n";
+
                        cvar_purechanges = strcat(cvar_purechanges, k, " \"", v, "\" // \"", d, "\"\n");
-                       if(strlen(cvar_purechanges) > 16384)
+                       if(strlen(cvar_purechanges) >= VM_TEMPSTRING_MAXSIZE)
                        {
                                cvar_purechanges = "// too many settings have been changed to show them here\n";
                                pureadding = 0;
@@ -567,15 +569,13 @@ void cvar_changes_init()
                // though.
        }
        buf_del(h);
+
        if(cvar_changes == "")
                cvar_changes = "// this server runs at default server settings\n";
-       else
-               cvar_changes = strcat("// this server runs at modified server settings:\n", cvar_changes);
        cvar_changes = strzone(cvar_changes);
+
        if(cvar_purechanges == "")
                cvar_purechanges = "// this server runs at default gameplay settings\n";
-       else
-               cvar_purechanges = strcat("// this server runs at modified gameplay settings:\n", cvar_purechanges);
        cvar_purechanges = strzone(cvar_purechanges);
 }
 
@@ -747,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")));
 
@@ -2301,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;
@@ -2318,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 a72143db3849a387a10fad28f8440f6816600081..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"
@@ -226,7 +227,7 @@ set g_maplist_mostrecent "" "contains the name of the maps that were most recent
 set g_maplist_mostrecent_count 3 "number of most recent maps that are blocked from being played again"
 set g_maplist_index 0 "this is used internally for saving position in maplist cycle"
 set g_maplist_selectrandom 0 "if 1, a random map will be chosen as next map - DEPRECATED in favor of g_maplist_shuffle"
-set g_maplist_shuffle 1 "new randomization method: like selectrandom, but avoid playing the same maps in short succession. This works by taking out the first element and inserting it into g_maplist with a bias to the end of the list"
+set g_maplist_shuffle 1 "1: shuffling method which avoids playing the same maps in short succession by taking out the first element and inserting it into g_maplist with a bias to the end of the list. -1: a simpler shuffling method which should be adequate if g_maplist_mostrecent_count is large enough."
 set g_maplist_check_waypoints 0 "when 1, maps are skipped if there currently are bots, but the map has no waypoints"
 set g_maplist_ignore_sizes 0 "when 1, all maps are shown in the map list regardless of player count"
 set g_maplist_sizes_count_maxplayers 1 "check the player limit when getting the player count so forced spectators don't affect the size restrictions"
@@ -243,7 +244,7 @@ set g_player_brightness 0 "set to 2 for brighter players"
 
 set g_player_damageforcescale 2 "push multiplier of attacks against players"
 
-set g_player_damageplayercenter 0 "0: always calculate knockback force direction from player's eyes instead of bbox center. 1: use bbox center point for others, shot origin for attacker's self-damage"
+set g_player_damageplayercenter 1 "0: always calculate knockback force direction from player's eyes instead of bbox center. 1: use bbox center point for others, shot origin for attacker's self-damage"
 
 set g_playerclip_collisions 1 "0 = disable collision testing against playerclips, might be useful on some defrag maps"
 set g_botclip_collisions 1 "0 = disable collision testing against botclips, might be useful on some defrag maps"
@@ -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)"