]> de.git.xonotic.org Git - xonotic/xonotic-data.pk3dir.git/commitdiff
Merge branch 'master' into terencehill/menu_listbox_changes
authorterencehill <piuntn@gmail.com>
Sat, 1 Aug 2015 20:18:20 +0000 (22:18 +0200)
committerterencehill <piuntn@gmail.com>
Sat, 1 Aug 2015 20:18:20 +0000 (22:18 +0200)
242 files changed:
defaultXonotic.cfg
gfx/menu/luma/skinvalues.txt
gfx/menu/luminos/icon_ipv4.tga
gfx/menu/luminos/icon_ipv6.tga
gfx/menu/luminos/icon_mod_.tga
gfx/menu/luminos/icon_mod_MinstaGib.tga
gfx/menu/luminos/icon_mod_NewToys.tga
gfx/menu/luminos/icon_mod_Overkill.tga
gfx/menu/luminos/icon_mod_instagib.tga
gfx/menu/luminos/icon_pure1.tga
gfx/menu/luminos/skinvalues.txt
gfx/menu/wickedx/icon_ipv4.tga
gfx/menu/wickedx/icon_ipv6.tga
gfx/menu/wickedx/icon_mod_.tga
gfx/menu/wickedx/icon_mod_MinstaGib.tga
gfx/menu/wickedx/icon_mod_NewToys.tga
gfx/menu/wickedx/icon_mod_Overkill.tga
gfx/menu/wickedx/icon_mod_instagib.tga
gfx/menu/wickedx/icon_pure1.tga
gfx/menu/wickedx/skinvalues.txt
gfx/menu/xaw/icon_ipv4.tga
gfx/menu/xaw/icon_ipv6.tga
gfx/menu/xaw/icon_mod_.tga
gfx/menu/xaw/icon_mod_MinstaGib.tga
gfx/menu/xaw/icon_mod_NewToys.tga
gfx/menu/xaw/icon_mod_Overkill.tga
gfx/menu/xaw/icon_mod_instagib.tga
gfx/menu/xaw/icon_pure1.tga
gfx/menu/xaw/skinvalues.txt
physicsX.cfg
physicsX010.cfg
physicsX07.cfg
physicsXDF.cfg
physicsXDFLight.cfg
qcsrc/client/bgmscript.qh
qcsrc/client/casings.qc
qcsrc/client/command/cl_cmd.qc
qcsrc/client/csqcmodel_hooks.qc
qcsrc/client/damage.qc
qcsrc/client/effects.qc
qcsrc/client/gibs.qc
qcsrc/client/hud.qc
qcsrc/client/hud.qh
qcsrc/client/main.qc
qcsrc/client/miscfunctions.qc
qcsrc/client/miscfunctions.qh
qcsrc/client/movetypes.qc [deleted file]
qcsrc/client/movetypes.qh [deleted file]
qcsrc/client/particles.qc
qcsrc/client/particles.qh
qcsrc/client/player_skeleton.qc
qcsrc/client/progs.src
qcsrc/client/scoreboard.qc
qcsrc/client/t_items.qc
qcsrc/client/target_music.qc [deleted file]
qcsrc/client/target_music.qh [deleted file]
qcsrc/client/tturrets.qc
qcsrc/client/vehicles/all.qc
qcsrc/client/vehicles/bumblebee.qc
qcsrc/client/view.qc
qcsrc/client/wall.qh
qcsrc/client/waypointsprites.qc
qcsrc/client/weapons/projectile.qc
qcsrc/common/constants.qh
qcsrc/common/csqcmodel_settings.qh
qcsrc/common/monsters/sv_monsters.qc
qcsrc/common/movetypes/follow.qc [new file with mode: 0644]
qcsrc/common/movetypes/include.qc [new file with mode: 0644]
qcsrc/common/movetypes/include.qh [new file with mode: 0644]
qcsrc/common/movetypes/movetypes.qc [new file with mode: 0644]
qcsrc/common/movetypes/movetypes.qh [new file with mode: 0644]
qcsrc/common/movetypes/push.qc [new file with mode: 0644]
qcsrc/common/movetypes/push.qh [new file with mode: 0644]
qcsrc/common/movetypes/step.qc [new file with mode: 0644]
qcsrc/common/movetypes/toss.qc [new file with mode: 0644]
qcsrc/common/movetypes/toss.qh [new file with mode: 0644]
qcsrc/common/movetypes/walk.qc [new file with mode: 0644]
qcsrc/common/movetypes/walk.qh [new file with mode: 0644]
qcsrc/common/nades.qc
qcsrc/common/net_notice.qc
qcsrc/common/physics.qc [new file with mode: 0644]
qcsrc/common/physics.qh [new file with mode: 0644]
qcsrc/common/stats.qh
qcsrc/common/triggers/func/bobbing.qc [new file with mode: 0644]
qcsrc/common/triggers/func/breakable.qc [new file with mode: 0644]
qcsrc/common/triggers/func/breakable.qh [new file with mode: 0644]
qcsrc/common/triggers/func/button.qc [new file with mode: 0644]
qcsrc/common/triggers/func/conveyor.qc [new file with mode: 0644]
qcsrc/common/triggers/func/conveyor.qh [new file with mode: 0644]
qcsrc/common/triggers/func/door.qc [new file with mode: 0644]
qcsrc/common/triggers/func/door.qh [new file with mode: 0644]
qcsrc/common/triggers/func/door_rotating.qc [new file with mode: 0644]
qcsrc/common/triggers/func/door_secret.qc [new file with mode: 0644]
qcsrc/common/triggers/func/fourier.qc [new file with mode: 0644]
qcsrc/common/triggers/func/include.qc [new file with mode: 0644]
qcsrc/common/triggers/func/include.qh [new file with mode: 0644]
qcsrc/common/triggers/func/ladder.qc [new file with mode: 0644]
qcsrc/common/triggers/func/ladder.qh [new file with mode: 0644]
qcsrc/common/triggers/func/pendulum.qc [new file with mode: 0644]
qcsrc/common/triggers/func/plat.qc [new file with mode: 0644]
qcsrc/common/triggers/func/plat.qh [new file with mode: 0644]
qcsrc/common/triggers/func/pointparticles.qc [new file with mode: 0644]
qcsrc/common/triggers/func/pointparticles.qh [new file with mode: 0644]
qcsrc/common/triggers/func/rainsnow.qc [new file with mode: 0644]
qcsrc/common/triggers/func/rainsnow.qh [new file with mode: 0644]
qcsrc/common/triggers/func/rotating.qc [new file with mode: 0644]
qcsrc/common/triggers/func/stardust.qc [new file with mode: 0644]
qcsrc/common/triggers/func/train.qc [new file with mode: 0644]
qcsrc/common/triggers/func/train.qh [new file with mode: 0644]
qcsrc/common/triggers/func/vectormamamam.qc [new file with mode: 0644]
qcsrc/common/triggers/include.qc [new file with mode: 0644]
qcsrc/common/triggers/include.qh [new file with mode: 0644]
qcsrc/common/triggers/misc/corner.qc [new file with mode: 0644]
qcsrc/common/triggers/misc/corner.qh [new file with mode: 0644]
qcsrc/common/triggers/misc/follow.qc [new file with mode: 0644]
qcsrc/common/triggers/misc/include.qc [new file with mode: 0644]
qcsrc/common/triggers/misc/include.qh [new file with mode: 0644]
qcsrc/common/triggers/misc/laser.qc [new file with mode: 0644]
qcsrc/common/triggers/misc/laser.qh [new file with mode: 0644]
qcsrc/common/triggers/misc/teleport_dest.qc [new file with mode: 0644]
qcsrc/common/triggers/platforms.qc [new file with mode: 0644]
qcsrc/common/triggers/platforms.qh [new file with mode: 0644]
qcsrc/common/triggers/subs.qc [new file with mode: 0644]
qcsrc/common/triggers/subs.qh [new file with mode: 0644]
qcsrc/common/triggers/target/changelevel.qc [new file with mode: 0644]
qcsrc/common/triggers/target/include.qc [new file with mode: 0644]
qcsrc/common/triggers/target/include.qh [new file with mode: 0644]
qcsrc/common/triggers/target/location.qc [new file with mode: 0644]
qcsrc/common/triggers/target/music.qc [new file with mode: 0644]
qcsrc/common/triggers/target/music.qh [new file with mode: 0644]
qcsrc/common/triggers/target/spawn.qc [new file with mode: 0644]
qcsrc/common/triggers/target/speaker.qc [new file with mode: 0644]
qcsrc/common/triggers/target/voicescript.qc [new file with mode: 0644]
qcsrc/common/triggers/teleporters.qc [new file with mode: 0644]
qcsrc/common/triggers/teleporters.qh [new file with mode: 0644]
qcsrc/common/triggers/trigger/counter.qc [new file with mode: 0644]
qcsrc/common/triggers/trigger/delay.qc [new file with mode: 0644]
qcsrc/common/triggers/trigger/disablerelay.qc [new file with mode: 0644]
qcsrc/common/triggers/trigger/flipflop.qc [new file with mode: 0644]
qcsrc/common/triggers/trigger/gamestart.qc [new file with mode: 0644]
qcsrc/common/triggers/trigger/gravity.qc [new file with mode: 0644]
qcsrc/common/triggers/trigger/heal.qc [new file with mode: 0644]
qcsrc/common/triggers/trigger/hurt.qc [new file with mode: 0644]
qcsrc/common/triggers/trigger/impulse.qc [new file with mode: 0644]
qcsrc/common/triggers/trigger/impulse.qh [new file with mode: 0644]
qcsrc/common/triggers/trigger/include.qc [new file with mode: 0644]
qcsrc/common/triggers/trigger/include.qh [new file with mode: 0644]
qcsrc/common/triggers/trigger/jumppads.qc [new file with mode: 0644]
qcsrc/common/triggers/trigger/jumppads.qh [new file with mode: 0644]
qcsrc/common/triggers/trigger/keylock.qc [new file with mode: 0644]
qcsrc/common/triggers/trigger/keylock.qh [new file with mode: 0644]
qcsrc/common/triggers/trigger/magicear.qc [new file with mode: 0644]
qcsrc/common/triggers/trigger/monoflop.qc [new file with mode: 0644]
qcsrc/common/triggers/trigger/multi.qc [new file with mode: 0644]
qcsrc/common/triggers/trigger/multi.qh [new file with mode: 0644]
qcsrc/common/triggers/trigger/multivibrator.qc [new file with mode: 0644]
qcsrc/common/triggers/trigger/relay.qc [new file with mode: 0644]
qcsrc/common/triggers/trigger/relay_activators.qc [new file with mode: 0644]
qcsrc/common/triggers/trigger/relay_if.qc [new file with mode: 0644]
qcsrc/common/triggers/trigger/relay_teamcheck.qc [new file with mode: 0644]
qcsrc/common/triggers/trigger/secret.qc [new file with mode: 0644]
qcsrc/common/triggers/trigger/secret.qh [new file with mode: 0644]
qcsrc/common/triggers/trigger/swamp.qc [new file with mode: 0644]
qcsrc/common/triggers/trigger/swamp.qh [new file with mode: 0644]
qcsrc/common/triggers/trigger/teleport.qc [new file with mode: 0644]
qcsrc/common/triggers/triggers.qc [new file with mode: 0644]
qcsrc/common/triggers/triggers.qh [new file with mode: 0644]
qcsrc/common/util.qc
qcsrc/common/weapons/all.qc
qcsrc/common/weapons/w_porto.qc
qcsrc/csqcmodellib/cl_player.qc
qcsrc/csqcmodellib/cl_player.qh
qcsrc/dpdefs/upstream/csprogsdefs.qc
qcsrc/menu/skin-customizables.inc
qcsrc/menu/xonotic/gametypelist.qc
qcsrc/menu/xonotic/serverlist.qc
qcsrc/server/autocvars.qh
qcsrc/server/bot/havocbot/havocbot.qc
qcsrc/server/bot/navigation.qc
qcsrc/server/bot/waypoints.qc
qcsrc/server/campaign.qc
qcsrc/server/cheats.qc
qcsrc/server/cl_client.qc
qcsrc/server/cl_physics.qc [deleted file]
qcsrc/server/cl_player.qc
qcsrc/server/command/cmd.qc
qcsrc/server/command/radarmap.qc
qcsrc/server/constants.qh
qcsrc/server/defs.qh
qcsrc/server/func_breakable.qc [deleted file]
qcsrc/server/g_damage.qc
qcsrc/server/g_hook.qc
qcsrc/server/g_models.qc
qcsrc/server/g_subs.qc
qcsrc/server/g_subs.qh
qcsrc/server/g_triggers.qc [deleted file]
qcsrc/server/g_triggers.qh [deleted file]
qcsrc/server/g_world.qc
qcsrc/server/item_key.qc
qcsrc/server/item_key.qh
qcsrc/server/miscfunctions.qc
qcsrc/server/miscfunctions.qh
qcsrc/server/mutators/gamemode.qh
qcsrc/server/mutators/gamemode_assault.qh
qcsrc/server/mutators/gamemode_ctf.qc
qcsrc/server/mutators/gamemode_ctf.qh
qcsrc/server/mutators/gamemode_keepaway.qc
qcsrc/server/mutators/gamemode_keyhunt.qc
qcsrc/server/mutators/gamemode_nexball.qc
qcsrc/server/mutators/mutator.qh
qcsrc/server/mutators/mutator_dodging.qc
qcsrc/server/mutators/mutator_multijump.qc
qcsrc/server/mutators/mutators_include.qc
qcsrc/server/pathlib.qc
qcsrc/server/portals.qc
qcsrc/server/progs.src
qcsrc/server/secret.qc [deleted file]
qcsrc/server/secret.qh [deleted file]
qcsrc/server/spawnpoints.qc
qcsrc/server/sv_main.qc
qcsrc/server/t_halflife.qc
qcsrc/server/t_items.qc
qcsrc/server/t_jumppads.qc [deleted file]
qcsrc/server/t_jumppads.qh [deleted file]
qcsrc/server/t_plats.qc
qcsrc/server/t_swamp.qc [deleted file]
qcsrc/server/t_teleporters.qc [deleted file]
qcsrc/server/t_teleporters.qh [deleted file]
qcsrc/server/target_spawn.qc [deleted file]
qcsrc/server/tturrets/system/system_misc.qc
qcsrc/server/tturrets/units/unit_targettrigger.qc
qcsrc/server/tturrets/units/unit_walker.qc
qcsrc/server/vehicles/racer.qc
qcsrc/server/vehicles/vehicle.qh
qcsrc/server/weapons/hitplot.qc
qcsrc/server/weapons/throwing.qc
qcsrc/warpzonelib/common.qc
qcsrc/warpzonelib/common.qh
qcsrc/warpzonelib/server.qc
qcsrc/warpzonelib/util_server.qc
qcsrc/warpzonelib/util_server.qh
xonotic-credits.txt

index 756ab235571eec7f27c2a40529a55591204e81c3..c0d585e9fce4a00cc9a79e59461805f527631f14 100644 (file)
@@ -296,6 +296,7 @@ set sv_fraginfo_stats 1 "Enable statistics (health/armor) display information, 0
 
 // use default physics
 set sv_friction_on_land 0
+set sv_friction_slick 0.5
 
 set sv_player_viewoffset "0 0 35" "view offset of the player model"
 set sv_player_mins "-16 -16 -24" "playermodel mins"
index ed335aa1497156725ac413a9466389917556f0a4..c997fea25772cd797dc361c60304db04e7cc3b5b 100644 (file)
@@ -126,6 +126,9 @@ HEIGHT_NORMAL                 1.45
 HEIGHT_TITLE                  1.45
 HEIGHT_ZOOMEDTITLE            -1
 
+// gametypelist
+BOOL_GAMETYPELIST_ICON_BLUR   0
+
 // inputbox
 COLOR_INPUTBOX_F              '1 1 1'
 COLOR_INPUTBOX_N              '1 1 1'
index a0484f4e37eeb9aba750de55cf2e203b654cdb7e..74b44139950cf7e46d00997d4f0a34d7435aa7bb 100644 (file)
Binary files a/gfx/menu/luminos/icon_ipv4.tga and b/gfx/menu/luminos/icon_ipv4.tga differ
index d3fdc03ee3e1cd4e6951e1b44beebd0d34b2111a..fe1227cb03383059c108918537888312707080f6 100644 (file)
Binary files a/gfx/menu/luminos/icon_ipv6.tga and b/gfx/menu/luminos/icon_ipv6.tga differ
index 0b31a343dc10305cf3829733ef92106b9a679854..63738cbcfa16f218b111ecde8b05fa2ba4279408 100644 (file)
Binary files a/gfx/menu/luminos/icon_mod_.tga and b/gfx/menu/luminos/icon_mod_.tga differ
index 518fd79649f966c692ca4e3562c9d774a94817df..065b84424651647058a56cb04d780e583f670465 100644 (file)
Binary files a/gfx/menu/luminos/icon_mod_MinstaGib.tga and b/gfx/menu/luminos/icon_mod_MinstaGib.tga differ
index cf88f77032e403a405b64007125995079d8a2b34..00acb711750154ab87ec26b7b693592cd8bd8343 100644 (file)
Binary files a/gfx/menu/luminos/icon_mod_NewToys.tga and b/gfx/menu/luminos/icon_mod_NewToys.tga differ
index 743722ea1817a237201c552a13aae63772c3defd..42e92c68ce0407b23e3a9171668210bdc9a758e0 100644 (file)
Binary files a/gfx/menu/luminos/icon_mod_Overkill.tga and b/gfx/menu/luminos/icon_mod_Overkill.tga differ
index 518fd79649f966c692ca4e3562c9d774a94817df..065b84424651647058a56cb04d780e583f670465 100644 (file)
Binary files a/gfx/menu/luminos/icon_mod_instagib.tga and b/gfx/menu/luminos/icon_mod_instagib.tga differ
index 62980d257acf7c009589f7d256612dd106cf70a8..7f7a1302946720c94e4990b3e3b5e9d1a7d1b676 100644 (file)
Binary files a/gfx/menu/luminos/icon_pure1.tga and b/gfx/menu/luminos/icon_pure1.tga differ
index d465f13c3661bc36bcc173160328d2390c9bebfe..ca0384fb554135f88ed4bf13a41c3b3e849497f9 100755 (executable)
@@ -196,6 +196,9 @@ COLOR_DIALOG_SCREENSHOTVIEWER   '1 1 1'
 COLOR_DIALOG_CVARS              '1 0 0'
 COLOR_DIALOG_HUDCONFIRM         '1 0 0'
 
+// gametypelist
+BOOL_GAMETYPELIST_ICON_BLUR     1
+
 // item: input box
 //   uses "inputbox" images
 COLOR_INPUTBOX_N                '1 1 1'
index a0484f4e37eeb9aba750de55cf2e203b654cdb7e..74b44139950cf7e46d00997d4f0a34d7435aa7bb 100644 (file)
Binary files a/gfx/menu/wickedx/icon_ipv4.tga and b/gfx/menu/wickedx/icon_ipv4.tga differ
index d3fdc03ee3e1cd4e6951e1b44beebd0d34b2111a..fe1227cb03383059c108918537888312707080f6 100644 (file)
Binary files a/gfx/menu/wickedx/icon_ipv6.tga and b/gfx/menu/wickedx/icon_ipv6.tga differ
index 0b31a343dc10305cf3829733ef92106b9a679854..63738cbcfa16f218b111ecde8b05fa2ba4279408 100644 (file)
Binary files a/gfx/menu/wickedx/icon_mod_.tga and b/gfx/menu/wickedx/icon_mod_.tga differ
index 518fd79649f966c692ca4e3562c9d774a94817df..065b84424651647058a56cb04d780e583f670465 100644 (file)
Binary files a/gfx/menu/wickedx/icon_mod_MinstaGib.tga and b/gfx/menu/wickedx/icon_mod_MinstaGib.tga differ
index cf88f77032e403a405b64007125995079d8a2b34..00acb711750154ab87ec26b7b693592cd8bd8343 100644 (file)
Binary files a/gfx/menu/wickedx/icon_mod_NewToys.tga and b/gfx/menu/wickedx/icon_mod_NewToys.tga differ
index 743722ea1817a237201c552a13aae63772c3defd..42e92c68ce0407b23e3a9171668210bdc9a758e0 100644 (file)
Binary files a/gfx/menu/wickedx/icon_mod_Overkill.tga and b/gfx/menu/wickedx/icon_mod_Overkill.tga differ
index 518fd79649f966c692ca4e3562c9d774a94817df..065b84424651647058a56cb04d780e583f670465 100644 (file)
Binary files a/gfx/menu/wickedx/icon_mod_instagib.tga and b/gfx/menu/wickedx/icon_mod_instagib.tga differ
index 62980d257acf7c009589f7d256612dd106cf70a8..7f7a1302946720c94e4990b3e3b5e9d1a7d1b676 100644 (file)
Binary files a/gfx/menu/wickedx/icon_pure1.tga and b/gfx/menu/wickedx/icon_pure1.tga differ
index 09ded4b087c356d741a823ab25293da71bae7dcb..b7011a0b0e18369246a573e86f52dd0d22174bb4 100644 (file)
@@ -196,6 +196,9 @@ COLOR_DIALOG_SCREENSHOTVIEWER   '1 1 1'
 COLOR_DIALOG_CVARS              '1 0 0'
 COLOR_DIALOG_HUDCONFIRM         '1 0 0'
 
+// gametypelist
+BOOL_GAMETYPELIST_ICON_BLUR     1
+
 // item: input box
 //   uses "inputbox" images
 COLOR_INPUTBOX_N                '1 1 1'
index a0484f4e37eeb9aba750de55cf2e203b654cdb7e..74b44139950cf7e46d00997d4f0a34d7435aa7bb 100644 (file)
Binary files a/gfx/menu/xaw/icon_ipv4.tga and b/gfx/menu/xaw/icon_ipv4.tga differ
index d3fdc03ee3e1cd4e6951e1b44beebd0d34b2111a..fe1227cb03383059c108918537888312707080f6 100644 (file)
Binary files a/gfx/menu/xaw/icon_ipv6.tga and b/gfx/menu/xaw/icon_ipv6.tga differ
index 0b31a343dc10305cf3829733ef92106b9a679854..63738cbcfa16f218b111ecde8b05fa2ba4279408 100644 (file)
Binary files a/gfx/menu/xaw/icon_mod_.tga and b/gfx/menu/xaw/icon_mod_.tga differ
index 518fd79649f966c692ca4e3562c9d774a94817df..065b84424651647058a56cb04d780e583f670465 100644 (file)
Binary files a/gfx/menu/xaw/icon_mod_MinstaGib.tga and b/gfx/menu/xaw/icon_mod_MinstaGib.tga differ
index cf88f77032e403a405b64007125995079d8a2b34..00acb711750154ab87ec26b7b693592cd8bd8343 100644 (file)
Binary files a/gfx/menu/xaw/icon_mod_NewToys.tga and b/gfx/menu/xaw/icon_mod_NewToys.tga differ
index 743722ea1817a237201c552a13aae63772c3defd..42e92c68ce0407b23e3a9171668210bdc9a758e0 100644 (file)
Binary files a/gfx/menu/xaw/icon_mod_Overkill.tga and b/gfx/menu/xaw/icon_mod_Overkill.tga differ
index 518fd79649f966c692ca4e3562c9d774a94817df..065b84424651647058a56cb04d780e583f670465 100644 (file)
Binary files a/gfx/menu/xaw/icon_mod_instagib.tga and b/gfx/menu/xaw/icon_mod_instagib.tga differ
index 62980d257acf7c009589f7d256612dd106cf70a8..7f7a1302946720c94e4990b3e3b5e9d1a7d1b676 100644 (file)
Binary files a/gfx/menu/xaw/icon_pure1.tga and b/gfx/menu/xaw/icon_pure1.tga differ
index 00df32498ebdfdb623c9e42d3c9c0348db648a94..5f4bbaad40211135e157b03f89e82e99ef337475 100644 (file)
@@ -114,6 +114,9 @@ COLOR_CVARLIST_CHANGED          '1 0 0'
 COLOR_CVARLIST_UNCHANGED        '1 1 1'
 COLOR_CVARLIST_CONTROLS         '1 0 0'
 
+// gametypelist
+BOOL_GAMETYPELIST_ICON_BLUR     1
+
 // item: dialog
 //   uses "border" images
 //   uses "closebutton" images
index e03a6d4171b442f1f5297e3c2f9db5b676cd7c99..c109e1157636e5b6fb91f658a0c8ba37910b922f 100644 (file)
@@ -44,6 +44,7 @@ sv_warsowbunny_accel 0.1593
 sv_warsowbunny_topspeed 925
 sv_warsowbunny_backtosideratio 0.8
 sv_friction_on_land 0
+sv_friction_slick 0.5
 sv_doublejump 0
 sv_jumpspeedcap_min ""
 sv_jumpspeedcap_max ""
index 77926d05f7bf99a21aba09dee93c8ce864c974bb..6dadcf161bd1078d9b6b4227e84c6d9624b0f7e0 100644 (file)
@@ -37,6 +37,7 @@ sv_warsowbunny_accel 0.1593
 sv_warsowbunny_topspeed 925
 sv_warsowbunny_backtosideratio 0.8
 sv_friction_on_land 0
+sv_friction_slick 0.5
 sv_doublejump 0
 sv_jumpspeedcap_min ""
 sv_jumpspeedcap_max ""
index e2a3ef648087b64716ae7b5a7153a9663995ec67..a3e42a977eca1a69fc3ba438f7aa69f305466e34 100644 (file)
@@ -43,6 +43,7 @@ sv_warsowbunny_accel 0.1593
 sv_warsowbunny_topspeed 925
 sv_warsowbunny_backtosideratio 0.8
 sv_friction_on_land 0
+sv_friction_slick 0.5
 sv_doublejump 0
 sv_jumpspeedcap_min ""
 sv_jumpspeedcap_max ""
index 0acb6e9535955b03e79f281a2a502f95c6946f58..8597e9e005d77020ea96c2375efa78ba56887b8c 100644 (file)
@@ -35,6 +35,7 @@ sv_warsowbunny_accel 0.1593
 sv_warsowbunny_topspeed 925
 sv_warsowbunny_backtosideratio 0.8
 sv_friction_on_land 0
+sv_friction_slick 0.5
 sv_doublejump 1
 sv_jumpspeedcap_min 0
 sv_jumpspeedcap_max 0.5
index cac90b4ee748314e844c882df28d9edbbb8eb8c6..6cb0780dd988ee2f7ffafed71d16e8348d0a8b4b 100644 (file)
@@ -35,6 +35,7 @@ sv_warsowbunny_accel 0.1593
 sv_warsowbunny_topspeed 925
 sv_warsowbunny_backtosideratio 0.8
 sv_friction_on_land 0
+sv_friction_slick 0.5
 sv_doublejump 0
 sv_jumpspeedcap_min ""
 sv_jumpspeedcap_max ""
index cd70955cb428709ddb29a931189e6a2b536a9679..5921bb4bc7325126c5227353192a1aa1fe18c250 100644 (file)
@@ -10,6 +10,9 @@ class(BGMScript) .float bgmscriptrelease;
 
 class(BGMScript) .float just_toggled;
 
+#ifdef CSQC
 void BGMScript_InitEntity(entity e);
 float doBGMScript(entity e);
 #endif
+
+#endif
index f9c58957e546c33f65287233822a36bbf3f30cda..8961af2241a34dc489001a1f867a9b1c924da694 100644 (file)
@@ -1,7 +1,7 @@
 #include "casings.qh"
 #include "_all.qh"
 
-#include "movetypes.qh"
+#include "../common/movetypes/movetypes.qh"
 #include "prandom.qh"
 #include "rubble.qh"
 
index 18fcfd1271cfeb9c273349cadf47e1f7331bdb63..ed8cb190d26ee803a68df32662ff62f3a9e14b2a 100644 (file)
@@ -351,6 +351,31 @@ void LocalCommand_mv_download(int request, int argc)
        }
 }
 
+void LocalCommand_find(int request, int argc)
+{
+       switch(request)
+       {
+               case CMD_REQUEST_COMMAND:
+               {
+                       entity client;
+
+                       for(client = world; (client = find(client, classname, argv(1))); )
+                               print(etos(client), "\n");
+
+                       return;
+               }
+
+               default:
+                       print("Incorrect parameters for ^2find^7\n");
+               case CMD_REQUEST_USAGE:
+               {
+                       print("\nUsage:^3 cl_cmd find classname\n");
+                       print("  Where 'classname' is the classname to search for.\n");
+                       return;
+               }
+       }
+}
+
 void LocalCommand_sendcvar(int request, int argc)
 {
        switch(request)
@@ -422,6 +447,7 @@ void LocalCommand_(int request)
        CLIENT_COMMAND("handlevote", LocalCommand_handlevote(request, arguments), "System to handle selecting a vote or option") \
        CLIENT_COMMAND("hud", LocalCommand_hud(request, arguments), "Commands regarding/controlling the HUD system") \
        CLIENT_COMMAND("localprint", LocalCommand_localprint(request, arguments), "Create your own centerprint sent to yourself") \
+       CLIENT_COMMAND("find", LocalCommand_find(request, arguments), "Search through entities for matching classname") \
        CLIENT_COMMAND("mv_download", LocalCommand_mv_download(request, arguments), "Retrieve mapshot picture from the server") \
        CLIENT_COMMAND("sendcvar", LocalCommand_sendcvar(request, arguments), "Send a cvar to the server (like weaponpriority)") \
        /* nothing */
index dbe999f8600517220936d3e14470ac83d0cb5e5d..92cef15efff3316b3f936aa2dca50a95f359e575 100644 (file)
@@ -655,10 +655,10 @@ void CSQCModel_Hook_PreDraw(bool isplayer)
                        bool onground = 0;
                        if(self == csqcplayer)
                        {
-                               if(self.pmove_flags & PMF_ONGROUND)
+                               if(self.flags & FL_ONGROUND)
                                        onground = 1;
-                               self.anim_prev_pmove_flags = self.pmove_flags;
-                               if(self.pmove_flags & PMF_DUCKED)
+                               self.anim_prev_pmove_flags = self.flags;
+                               if(self.flags & FL_DUCKED)
                                        animdecide_setstate(self, self.anim_state | ANIMSTATE_DUCK, false);
                                else if(self.anim_state & ANIMSTATE_DUCK)
                                        animdecide_setstate(self, self.anim_state - ANIMSTATE_DUCK, false);
index 39c422e1cca700cabe13953c50af3c7234bcbc40..3626c44c617408555c5bb4c4eaede028fe6399db 100644 (file)
@@ -2,13 +2,13 @@
 #include "_all.qh"
 
 #include "gibs.qh"
-#include "movetypes.qh"
 #include "prandom.qh"
 
 #include "vehicles/all.qh"
 
 #include "../common/constants.qh"
 #include "../common/deathtypes.qh"
+#include "../common/movetypes/movetypes.qh"
 #include "../common/util.qh"
 
 #include "../common/weapons/all.qh"
@@ -52,7 +52,7 @@ void DamageEffect_Think()
        pointparticles(self.team, org, '0 0 0', 1);
 }
 
-void DamageEffect(vector hitorg, float dmg, int type, int specnum)
+void DamageEffect(vector hitorg, float thedamage, int type, int specnum)
 {
        // particle effects for players and objects damaged by weapons (eg: flames coming out of victims shot with rockets)
 
@@ -97,7 +97,7 @@ void DamageEffect(vector hitorg, float dmg, int type, int specnum)
                        return; // allow a single damage on non-skeletal models
        }
 
-       life = bound(autocvar_cl_damageeffect_lifetime_min, dmg * autocvar_cl_damageeffect_lifetime, autocvar_cl_damageeffect_lifetime_max);
+       life = bound(autocvar_cl_damageeffect_lifetime_min, thedamage * autocvar_cl_damageeffect_lifetime, autocvar_cl_damageeffect_lifetime_max);
 
        effectname = get_weaponinfo(DEATH_WEAPONOF(type)).netname;
 
@@ -126,7 +126,7 @@ void DamageEffect(vector hitorg, float dmg, int type, int specnum)
 
 void Ent_DamageInfo(float isNew)
 {
-       float dmg, rad, edge, thisdmg;
+       float thedamage, rad, edge, thisdmg;
        bool hitplayer = false;
        int species, forcemul;
        vector force, thisforce;
@@ -142,7 +142,7 @@ void Ent_DamageInfo(float isNew)
        w_org.y = ReadCoord();
        w_org.z = ReadCoord();
 
-       dmg = ReadByte();
+       thedamage = ReadByte();
        rad = ReadByte();
        edge = ReadByte();
        force = decompressShortVector(ReadShort());
@@ -173,10 +173,10 @@ void Ent_DamageInfo(float isNew)
                                continue;
                        if(thisdmg < 0)
                                thisdmg = 0;
-                       if(dmg)
+                       if(thedamage)
                        {
-                               thisdmg = dmg + (edge - dmg) * thisdmg;
-                               thisforce = forcemul * vlen(force) * (thisdmg / dmg) * normalize(self.origin - w_org);
+                               thisdmg = thedamage + (edge - thedamage) * thisdmg;
+                               thisforce = forcemul * vlen(force) * (thisdmg / thedamage) * normalize(self.origin - w_org);
                        }
                        else
                        {
@@ -189,7 +189,7 @@ void Ent_DamageInfo(float isNew)
                        if(vlen(nearest - w_org) > bound(MIN_DAMAGEEXTRARADIUS, self.damageextraradius, MAX_DAMAGEEXTRARADIUS))
                                continue;
 
-                       thisdmg = dmg;
+                       thisdmg = thedamage;
                        thisforce = forcemul * force;
                }
 
index 99a9944d8ed3b276eca38407efa6d6b81cbb51f8..c0c378fd1a42c4cb5fb697902e8bdca4c743539c 100644 (file)
@@ -8,9 +8,6 @@
 .string fx_texture;
 .float  fx_lifetime;
 
-void SUB_Remove()
-{ remove(self); }
-
 void b_draw()
 {
     //Draw_CylindricLine(self.fx_start, self.fx_end, self.fx_with, self.fx_texture, 0, time * 3, '1 1 1', 0.7, DRAWFLAG_ADDITIVE, view_origin);
index c617106019b0e0412881b00474ddbb86d8b89ee1..298e9d5e672d61566d314cfa286d128cd8f63ce2 100644 (file)
@@ -1,11 +1,11 @@
 #include "gibs.qh"
 #include "_all.qh"
 
-#include "movetypes.qh"
 #include "prandom.qh"
 #include "rubble.qh"
 
 #include "../common/constants.qh"
+#include "../common/movetypes/movetypes.qh"
 #include "../common/util.qh"
 
 .float scale;
index b61fe2a5da685f95d66b3a766fdaded8d63cf2ca..a3acb8332e32b7709c564ba0f3185e23e9386ce0 100644 (file)
@@ -521,8 +521,6 @@ void HUD_Weapons(void)
        // update generic hud functions
        HUD_Panel_UpdateCvars();
 
-       draw_beginBoldFont();
-
        // figure out weapon order (how the weapons are sorted) // TODO make this configurable
        if(weaponorder_bypriority != autocvar_cl_weaponpriority || !weaponorder[0])
        {
@@ -592,10 +590,7 @@ void HUD_Weapons(void)
 
                // might as well commit suicide now, no reason to live ;)
                if (weapon_count == 0)
-               {
-                       draw_endBoldFont();
                        return;
-               }
 
                vector old_panel_size = panel_size;
                vector padded_panel_size = panel_size - '2 2 0' * panel_bg_padding;
@@ -749,10 +744,7 @@ void HUD_Weapons(void)
        HUD_Panel_DrawBg(1);
 
        if(center.x == -1)
-       {
-               draw_endBoldFont();
                return;
-       }
 
        if(panel_bg_padding)
        {
@@ -955,8 +947,6 @@ void HUD_Weapons(void)
                        }
                }
        }
-
-       draw_endBoldFont();
 }
 
 // Ammo (#1)
@@ -1551,6 +1541,9 @@ void HUD_HealthArmor(void)
        }
 
        HUD_Panel_UpdateCvars();
+
+       draw_beginBoldFont();
+
        vector pos, mySize;
        pos = panel_pos;
        mySize = panel_size;
@@ -1756,6 +1749,8 @@ void HUD_HealthArmor(void)
                        HUD_Panel_DrawProgressBar(pos, mySize, "progressbar", fuel/100, is_vertical, fuel_baralign, autocvar_hud_progressbar_fuel_color, panel_fg_alpha * 0.8, DRAWFLAG_NORMAL);
                }
        }
+
+       draw_endBoldFont();
 }
 
 // Notification area (#4)
@@ -2321,7 +2316,7 @@ void HUD_Score(void)
        vector distribution_color;
        entity tm, pl, me;
 
-       me = playerslots[player_localentnum - 1];
+       me = playerslots[current_player];
 
        if((scores_flags[ps_primary] & SFL_TIME) && !teamplay) { // race/cts record display on HUD
                string timer, distrtimer;
@@ -2490,8 +2485,6 @@ void HUD_RaceTimer (void)
 
        HUD_Panel_UpdateCvars();
 
-       draw_beginBoldFont();
-
        vector pos, mySize;
        pos = panel_pos;
        mySize = panel_size;
@@ -2527,7 +2520,9 @@ void HUD_RaceTimer (void)
        if(autocvar__hud_configure)
        {
                s = "0:13:37";
+               draw_beginBoldFont();
                drawstring(pos + eX * 0.5 * mySize.x - '0.5 0 0' * stringwidth(s, false, '0.60 0.60 0' * mySize.y), s, '0.60 0.60 0' * mySize.y, '1 1 1', panel_fg_alpha, DRAWFLAG_NORMAL);
+               draw_endBoldFont();
                s = _("^1Intermediate 1 (+15.42)");
                drawcolorcodedstring(pos + eX * 0.5 * mySize.x - '0.5 0 0' * stringwidth(s, true, '1 1 0' * 0.20 * mySize.y) + eY * 0.60 * mySize.y, s, '1 1 0' * 0.20 * mySize.y, panel_fg_alpha, DRAWFLAG_NORMAL);
                s = sprintf(_("^1PENALTY: %.1f (%s)"), 2, "missing a checkpoint");
@@ -2577,6 +2572,8 @@ void HUD_RaceTimer (void)
                        }
                }
 
+               draw_beginBoldFont();
+
                if(forcetime != "")
                {
                        a = bound(0, (time - race_checkpointtime) / 0.5, 1);
@@ -2590,6 +2587,8 @@ void HUD_RaceTimer (void)
                        s = TIME_ENCODED_TOSTRING(TIME_ENCODE(time + TIME_DECODE(race_penaltyaccumulator) - race_laptime));
                        drawstring(pos + eX * 0.5 * mySize.x - '0.5 0 0' * stringwidth(s, false, '0.6 0.6 0' * mySize.y), s, '0.6 0.6 0' * mySize.y, '1 1 1', panel_fg_alpha * a, DRAWFLAG_NORMAL);
                }
+
+               draw_endBoldFont();
        }
        else
        {
@@ -2620,8 +2619,6 @@ void HUD_RaceTimer (void)
                        }
                }
        }
-
-       draw_endBoldFont();
 }
 
 // Vote window (#9)
@@ -3785,7 +3782,7 @@ void HUD_InfoMessages(void)
                        if(spectatee_status == -1)
                                s = _("^1Observing");
                        else
-                               s = sprintf(_("^1Spectating: ^7%s"), GetPlayerName(player_localentnum - 1));
+                               s = sprintf(_("^1Spectating: ^7%s"), GetPlayerName(current_player));
                        drawInfoMessage(s);
 
                        if(spectatee_status == -1)
@@ -4633,8 +4630,6 @@ void HUD_Main (void)
                hud_skin_prev = strzone(autocvar_hud_skin);
        }
 
-    current_player = player_localentnum;
-
        // draw the dock
        if(autocvar_hud_dock != "" && autocvar_hud_dock != "0")
        {
@@ -4654,11 +4649,11 @@ void HUD_Main (void)
                {
                        string hud_dock_color = autocvar_hud_dock_color;
                        if(hud_dock_color == "shirt") {
-                               f = stof(getplayerkeyvalue(current_player - 1, "colors"));
+                               f = stof(getplayerkeyvalue(current_player, "colors"));
                                color = colormapPaletteColor(floor(f / 16), 0);
                        }
                        else if(hud_dock_color == "pants") {
-                               f = stof(getplayerkeyvalue(current_player - 1, "colors"));
+                               f = stof(getplayerkeyvalue(current_player, "colors"));
                                color = colormapPaletteColor(f % 16, 1);
                        }
                        else
index 3b61cb4539643c06b1e66dcc89b9fccbee6546f0..5bc2ca4a736c1529b6b91a2bac7478a6291f5e51 100644 (file)
@@ -227,9 +227,9 @@ HUD_PANELS(HUD_PANEL)
                        panel_bg_color = autocvar_hud_panel_bg_color;                                                           \
                } else {                                                                                                    \
                        if (panel_bg_color_str == "shirt") {                                                                    \
-                               panel_bg_color = colormapPaletteColor(floor(stof(getplayerkeyvalue(current_player - 1, "colors")) / 16), 0);\
+                               panel_bg_color = colormapPaletteColor(floor(stof(getplayerkeyvalue(current_player, "colors")) / 16), 0); \
                        } else if (panel_bg_color_str == "pants") {                                                             \
-                               panel_bg_color = colormapPaletteColor(stof(getplayerkeyvalue(current_player - 1, "colors")) % 16, 1);\
+                               panel_bg_color = colormapPaletteColor(stof(getplayerkeyvalue(current_player, "colors")) % 16, 1); \
                        } else {                                                                                                \
                                panel_bg_color = stov(panel_bg_color_str);                                                          \
                        }                                                                                                       \
index 7648b129775afa875f38ff5e16de5339c62c3a80..982056fcbb13f363cac97a3dfc4bc00f787ac122 100644 (file)
@@ -17,7 +17,6 @@
 #include "scoreboard.qh"
 #include "shownames.qh"
 #include "sortlist.qh"
-#include "target_music.qh"
 #include "tturrets.qh"
 #include "tuba.qh"
 #include "t_items.qh"
@@ -46,6 +45,8 @@
 #include "../csqcmodellib/cl_model.qh"
 #include "../csqcmodellib/interpolate.qh"
 
+#include "../common/triggers/include.qh"
+
 #include "../warpzonelib/client.qh"
 
 // --------------------------------------------------------------------------
@@ -378,6 +379,7 @@ float CSQC_InputEvent(float bInputType, float nPrimary, float nSecondary)
 
 // --------------------------------------------------------------------------
 // BEGIN OPTIONAL CSQC FUNCTIONS
+
 void Ent_RemoveEntCS()
 {
        entcs_receiver[self.sv_entnum] = world;
@@ -695,7 +697,7 @@ void Ent_ReadSpawnPoint(float is_new) // entity for spawnpoint
        if(is_new)
        {
                self.origin = spn_origin;
-               setsize(self, PL_MIN, PL_MAX);
+               setsize(self, PL_MIN_CONST, PL_MAX_CONST);
                droptofloor();
 
                /*if(autocvar_cl_spawn_point_model) // needs a model first
@@ -871,6 +873,17 @@ void CSQC_Ent_Update(float bIsNewEntity)
                case ENT_CLIENT_SPAWNEVENT: Ent_ReadSpawnEvent(bIsNewEntity); break;
                case ENT_CLIENT_NOTIFICATION: Read_Notification(bIsNewEntity); break;
                case ENT_CLIENT_HEALING_ORB: ent_healer(); break;
+               case ENT_CLIENT_LADDER: ent_func_ladder(); break;
+               case ENT_CLIENT_TRIGGER_PUSH: ent_trigger_push(); break;
+               case ENT_CLIENT_TARGET_PUSH: ent_target_push(); break;
+               case ENT_CLIENT_CONVEYOR: ent_conveyor(); break;
+               case ENT_CLIENT_DOOR: ent_door(); break;
+               case ENT_CLIENT_PLAT: ent_plat(); break;
+               case ENT_CLIENT_SWAMP: ent_swamp(); break;
+               case ENT_CLIENT_CORNER: ent_corner(); break;
+               case ENT_CLIENT_KEYLOCK: ent_keylock(); break;
+               case ENT_CLIENT_TRAIN: ent_train(); break;
+               case ENT_CLIENT_TRIGGER_IMPULSE: ent_trigger_impulse(); break;
 
                default:
                        //error(strcat(_("unknown entity type in CSQC_Ent_Update: %d\n"), self.enttype));
index f9e9705921989b0a290f1e3d8dbbf0c28809d671..845a5641067df16b639e047090b8b03b98e452f9 100644 (file)
@@ -359,11 +359,11 @@ void PolyDrawModel(entity e)
                        break;
 }
 
-void DrawCircleClippedPic(vector centre, float radius, string pic, float f, vector rgb, float a, float drawflag)
+void DrawCircleClippedPic(vector centre, float radi, string pic, float f, vector rgb, float a, float drawflag)
 {
        float x, y, q, d;
        vector ringsize, v, t;
-       ringsize = radius * '1 1 0';
+       ringsize = radi * '1 1 0';
 
        x = cos(f * 2 * M_PI);
        y = sin(f * 2 * M_PI);
index 6c643962a65678ab19643cc48a2300536aab2ee0..0c2be7fc54afa6dbab8782acb592f2af11125ace 100644 (file)
@@ -5,6 +5,15 @@ entity players;
 entity teams;
 float team_count; // real teams
 
+const int INITPRIO_FIRST                               = 0;
+const int INITPRIO_GAMETYPE                    = 0;
+const int INITPRIO_GAMETYPE_FALLBACK   = 1;
+const int INITPRIO_FINDTARGET                  = 10;
+const int INITPRIO_DROPTOFLOOR                         = 20;
+const int INITPRIO_SETLOCATION                         = 90;
+const int INITPRIO_LINKDOORS                   = 91;
+const int INITPRIO_LAST                                = 99;
+
 void AuditLists();
 
 float RegisterPlayer(entity player);
@@ -132,7 +141,7 @@ void drawcolorcodedstring_aspect_expanding(vector pos, string text, vector sz, f
 float PolyDrawModelSurface(entity e, float i_s);
 void PolyDrawModel(entity e);
 
-void DrawCircleClippedPic(vector centre, float radius, string pic, float f, vector rgb, float a, float drawflag);
+void DrawCircleClippedPic(vector centre, float radi, string pic, float f, vector rgb, float a, float drawflag);
 
 const vector GETPLAYERORIGIN_ERROR = '1123581321 2357111317 3141592653'; // way out of bounds for anything on the map
 vector getplayerorigin(int pl);
diff --git a/qcsrc/client/movetypes.qc b/qcsrc/client/movetypes.qc
deleted file mode 100644 (file)
index beb6230..0000000
+++ /dev/null
@@ -1,557 +0,0 @@
-#include "movetypes.qh"
-#include "_all.qh"
-
-#include "t_items.qh"
-
-#include "../common/stats.qh"
-#include "../common/util.qh"
-
-#include "../csqcmodellib/common.qh"
-
-
-const int MOVEFLAG_GRAVITYUNAFFECTEDBYTICRATE = 4;
-#define GRAVITY_UNAFFECTED_BY_TICRATE (getstati(STAT_MOVEFLAGS) & MOVEFLAG_GRAVITYUNAFFECTEDBYTICRATE)
-
-.entity move_groundentity; // FIXME add move_groundnetworkentity?
-.float move_suspendedinair;
-.float move_didgravity;
-
-void _Movetype_CheckVelocity() // SV_CheckVelocity
-{
-}
-
-float _Movetype_CheckWater(entity ent) // SV_CheckWater
-{
-       vector point = ent.move_origin;
-       point.z += (ent.mins.z + 1);
-
-       int nativecontents = pointcontents(point);
-
-       if(ent.move_watertype)
-       if(ent.move_watertype != nativecontents)
-       {
-               //print(sprintf("_Movetype_CheckWater(): Original: '%d', New: '%d'\n", ent.move_watertype, nativecontents));
-               if(ent.contentstransition)
-                       ent.contentstransition(ent.move_watertype, nativecontents);
-       }
-
-       ent.move_waterlevel = 0;
-       ent.move_watertype = CONTENT_EMPTY;
-
-       int supercontents = Mod_Q1BSP_SuperContentsFromNativeContents(nativecontents);
-       if(supercontents & DPCONTENTS_LIQUIDSMASK)
-       {
-               ent.move_watertype = nativecontents;
-               ent.move_waterlevel = 1;
-               point.y = (ent.origin.y + ((ent.mins.z + ent.maxs.y) * 0.5));
-               if(Mod_Q1BSP_SuperContentsFromNativeContents(pointcontents(point)) & DPCONTENTS_LIQUIDSMASK)
-               {
-                       ent.move_waterlevel = 2;
-                       point.y = ent.origin.y + ent.view_ofs.y;
-                       if(Mod_Q1BSP_SuperContentsFromNativeContents(pointcontents(point)) & DPCONTENTS_LIQUIDSMASK)
-                               ent.move_waterlevel = 3;
-               }
-       }
-
-       return (ent.move_waterlevel > 1);
-}
-
-void _Movetype_CheckWaterTransition(entity ent) // SV_CheckWaterTransition
-{
-       float contents = pointcontents(ent.move_origin);
-
-       if(!ent.move_watertype)
-       {
-               // just spawned here
-               if(!autocvar_cl_gameplayfix_fixedcheckwatertransition)
-               {
-                       ent.move_watertype = contents;
-                       ent.move_waterlevel = 1;
-                       return;
-               }
-       }
-       else if(ent.move_watertype != contents)
-       {
-               //print(sprintf("_Movetype_CheckWaterTransition(): Origin: %s, Direct: '%d', Original: '%d', New: '%d'\n", vtos(ent.move_origin), pointcontents(ent.move_origin), ent.move_watertype, contents));
-               if(ent.contentstransition)
-                       ent.contentstransition(ent.move_watertype, contents);
-       }
-
-       if(contents <= CONTENT_WATER)
-       {
-               ent.move_watertype = contents;
-               ent.move_waterlevel = 1;
-       }
-       else
-       {
-               ent.move_watertype = CONTENT_EMPTY;
-               ent.move_waterlevel = (autocvar_cl_gameplayfix_fixedcheckwatertransition ? 0 : contents);
-       }
-}
-
-void _Movetype_Impact(entity oth) // SV_Impact
-{
-       entity oldother, oldself;
-
-       oldself = self;
-       oldother = other;
-
-       if(self.move_touch)
-       {
-               other = oth;
-
-               self.move_touch();
-
-               other = oldother;
-       }
-
-       if(oth.move_touch)
-       {
-               other = self;
-               self = oth;
-
-               self.move_touch();
-
-               self = oldself;
-               other = oldother;
-       }
-}
-
-void _Movetype_LinkEdict_TouchAreaGrid() // SV_LinkEdict_TouchAreaGrid
-{
-       entity e, oldself, oldother;
-
-       oldself = self;
-       oldother = other;
-
-       for(e = findradius(0.5 * (self.absmin + self.absmax), 0.5 * vlen(self.absmax - self.absmin)); e; e = e.chain)
-       {
-               if(e.move_touch)
-               if(boxesoverlap(e.absmin, e.absmax, oldself.absmin, oldself.absmax))
-               {
-                       self = e;
-                       other = oldself;
-
-                       trace_allsolid = false;
-                       trace_startsolid = false;
-                       trace_fraction = 1;
-                       trace_inwater = false;
-                       trace_inopen = true;
-                       trace_endpos = e.origin;
-                       trace_plane_normal = '0 0 1';
-                       trace_plane_dist = 0;
-                       trace_ent = oldself;
-
-                       e.move_touch();
-               }
-       }
-
-       other = oldother;
-       self = oldself;
-}
-
-void _Movetype_LinkEdict(float touch_triggers) // SV_LinkEdict
-{
-       vector mi, ma;
-       if(self.solid == SOLID_BSP)
-       {
-               // TODO set the absolute bbox
-               mi = self.mins;
-               ma = self.maxs;
-       }
-       else
-       {
-               mi = self.mins;
-               ma = self.maxs;
-       }
-       mi = mi + self.origin;
-       ma = ma + self.origin;
-
-       if(self.move_flags & FL_ITEM)
-       {
-               mi.x -= 15;
-               mi.y -= 15;
-               mi.z -= 1;
-               ma.x += 15;
-               ma.y += 15;
-               ma.z += 1;
-       }
-       else
-       {
-               mi.x -= 1;
-               mi.y -= 1;
-               mi.z -= 1;
-               ma.x += 1;
-               ma.y += 1;
-               ma.z += 1;
-       }
-
-       self.absmin = mi;
-       self.absmax = ma;
-
-       if(touch_triggers)
-               _Movetype_LinkEdict_TouchAreaGrid();
-}
-
-float _Movetype_TestEntityPosition(vector ofs) // SV_TestEntityPosition
-{
-       vector org;
-       org = self.move_origin + ofs;
-
-       int cont = self.dphitcontentsmask;
-       self.dphitcontentsmask = DPCONTENTS_SOLID;
-       tracebox(self.move_origin, self.mins, self.maxs, self.move_origin, MOVE_NOMONSTERS, self);
-       self.dphitcontentsmask = cont;
-
-       if(trace_startsolid)
-               return true;
-
-       if(vlen(trace_endpos - self.move_origin) > 0.0001)
-               self.move_origin = trace_endpos;
-       return false;
-}
-
-float _Movetype_UnstickEntity() // SV_UnstickEntity
-{
-       if(!_Movetype_TestEntityPosition('0 0 0'))
-               return true;
-       if(!_Movetype_TestEntityPosition('-1 0 0')) goto success;
-       if(!_Movetype_TestEntityPosition('1 0 0')) goto success;
-       if(!_Movetype_TestEntityPosition('0 -1 0')) goto success;
-       if(!_Movetype_TestEntityPosition('0 1 0')) goto success;
-       if(!_Movetype_TestEntityPosition('-1 -1 0')) goto success;
-       if(!_Movetype_TestEntityPosition('1 -1 0')) goto success;
-       if(!_Movetype_TestEntityPosition('-1 1 0')) goto success;
-       if(!_Movetype_TestEntityPosition('1 1 0')) goto success;
-       float i;
-       for(i = 1; i <= 17; ++i)
-       {
-               if(!_Movetype_TestEntityPosition('0 0 -1' * i)) goto success;
-               if(!_Movetype_TestEntityPosition('0 0 1' * i)) goto success;
-       }
-       dprintf("Can't unstick an entity (edict: %d, classname: %s, origin: %s)\n", num_for_edict(self), self.classname, vtos(self.move_origin));
-       return false;
-:success
-       dprintf("Sucessfully unstuck an entity (edict: %d, classname: %s, origin: %s)\n", num_for_edict(self), self.classname, vtos(self.move_origin));
-       _Movetype_LinkEdict(true);
-       return true;
-}
-
-vector _Movetype_ClipVelocity(vector vel, vector norm, float f) // SV_ClipVelocity
-{
-       vel = vel - ((vel * norm) * norm) * f;
-
-       if(vel.x > -0.1 && vel.x < 0.1) vel.x = 0;
-       if(vel.y > -0.1 && vel.y < 0.1) vel.y = 0;
-       if(vel.z > -0.1 && vel.z < 0.1) vel.z = 0;
-
-       return vel;
-}
-
-void _Movetype_PushEntityTrace(vector push)
-{
-       vector end;
-       float type;
-
-       end = self.move_origin + push;
-
-       if(self.move_nomonsters)
-               type = max(0, self.move_nomonsters);
-       else if(self.move_movetype == MOVETYPE_FLYMISSILE)
-               type = MOVE_MISSILE;
-       else if(self.solid == SOLID_TRIGGER || self.solid == SOLID_NOT)
-               type = MOVE_NOMONSTERS;
-       else
-               type = MOVE_NORMAL;
-
-       tracebox(self.move_origin, self.mins, self.maxs, end, type, self);
-}
-
-float _Movetype_PushEntity(vector push, float failonstartsolid) // SV_PushEntity
-{
-       _Movetype_PushEntityTrace(push);
-
-       if(trace_startsolid && failonstartsolid)
-               return trace_fraction;
-
-       self.move_origin = trace_endpos;
-
-       if(trace_fraction < 1)
-               if(self.solid >= SOLID_TRIGGER && (!(self.move_flags & FL_ONGROUND) || (self.move_groundentity != trace_ent)))
-                       _Movetype_Impact(trace_ent);
-
-       return trace_fraction;
-}
-
-const float MAX_CLIP_PLANES = 5;
-void _Movetype_Physics_Toss(float dt) // SV_Physics_Toss
-{
-       if(self.move_flags & FL_ONGROUND)
-       {
-               if(self.move_velocity.z >= 1/32)
-                       self.move_flags &= ~FL_ONGROUND;
-               else if(!self.move_groundentity)
-                       return;
-               else if(self.move_suspendedinair && wasfreed(self.move_groundentity))
-               {
-                       self.move_groundentity = world;
-                       return;
-               }
-       }
-
-       self.move_suspendedinair = false;
-
-       _Movetype_CheckVelocity();
-
-       if(self.move_movetype == MOVETYPE_BOUNCE || self.move_movetype == MOVETYPE_TOSS)
-       {
-               self.move_didgravity = 1;
-               if(GRAVITY_UNAFFECTED_BY_TICRATE)
-               {
-                       if(self.gravity)
-                               self.move_velocity_z -= 0.5 * dt * self.gravity * getstatf(STAT_MOVEVARS_GRAVITY);
-                       else
-                               self.move_velocity_z -= 0.5 * dt * getstatf(STAT_MOVEVARS_GRAVITY);
-               }
-               else
-               {
-                       if(self.gravity)
-                               self.move_velocity_z -= dt * self.gravity * getstatf(STAT_MOVEVARS_GRAVITY);
-                       else
-                               self.move_velocity_z -= dt * getstatf(STAT_MOVEVARS_GRAVITY);
-               }
-       }
-
-       self.move_angles = self.move_angles + self.move_avelocity * dt;
-
-       float movetime, bump;
-       movetime = dt;
-       for(bump = 0; bump < MAX_CLIP_PLANES && movetime > 0; ++bump)
-       {
-               vector move;
-               move = self.move_velocity * movetime;
-               _Movetype_PushEntity(move, true);
-               if(wasfreed(self))
-                       return;
-
-               if(trace_startsolid)
-               {
-                       _Movetype_UnstickEntity();
-                       _Movetype_PushEntity(move, false);
-                       if(wasfreed(self))
-                               return;
-               }
-
-               if(trace_fraction == 1)
-                       break;
-
-               movetime *= 1 - min(1, trace_fraction);
-
-               if(self.move_movetype == MOVETYPE_BOUNCEMISSILE)
-               {
-                       self.move_velocity = _Movetype_ClipVelocity(self.move_velocity, trace_plane_normal, 2.0);
-                       self.move_flags &= ~FL_ONGROUND;
-               }
-               else if(self.move_movetype == MOVETYPE_BOUNCE)
-               {
-                       float d, bouncefac, bouncestop;
-
-                       bouncefac = self.move_bounce_factor;     if(!bouncefac)  bouncefac = 0.5;
-                       bouncestop = self.move_bounce_stopspeed; if(!bouncestop) bouncestop = 60 / 800;
-                       if(self.gravity)
-                               bouncestop *= self.gravity * getstatf(STAT_MOVEVARS_GRAVITY);
-                       else
-                               bouncestop *= getstatf(STAT_MOVEVARS_GRAVITY);
-
-                       self.move_velocity = _Movetype_ClipVelocity(self.move_velocity, trace_plane_normal, 1 + bouncefac);
-
-                       d = trace_plane_normal * self.move_velocity;
-                       if(trace_plane_normal.z > 0.7 && d < bouncestop && d > -bouncestop)
-                       {
-                               self.move_flags |= FL_ONGROUND;
-                               self.move_groundentity = trace_ent;
-                               self.move_velocity = '0 0 0';
-                               self.move_avelocity = '0 0 0';
-                       }
-                       else
-                               self.move_flags &= ~FL_ONGROUND;
-               }
-               else
-               {
-                       self.move_velocity = _Movetype_ClipVelocity(self.move_velocity, trace_plane_normal, 1.0);
-                       if(trace_plane_normal.z > 0.7)
-                       {
-                               self.move_flags |= FL_ONGROUND;
-                               self.move_groundentity = trace_ent;
-                               if(trace_ent.solid == SOLID_BSP)
-                                       self.move_suspendedinair = true;
-                               self.move_velocity = '0 0 0';
-                               self.move_avelocity = '0 0 0';
-                       }
-                       else
-                               self.move_flags &= ~FL_ONGROUND;
-               }
-
-               // DP revision 8905 (just, WHY...)
-               if(self.move_movetype == MOVETYPE_BOUNCEMISSILE)
-                       break;
-
-               // DP revision 8918 (WHY...)
-               if(self.move_flags & FL_ONGROUND)
-                       break;
-       }
-
-       if(GRAVITY_UNAFFECTED_BY_TICRATE)
-       if(self.move_didgravity > 0)
-       if(!(self.move_flags & FL_ONGROUND))
-       {
-               if(self.gravity)
-                       self.move_velocity_z -= 0.5 * dt * self.gravity * getstatf(STAT_MOVEVARS_GRAVITY);
-               else
-                       self.move_velocity_z -= 0.5 * dt * getstatf(STAT_MOVEVARS_GRAVITY);
-       }
-
-       _Movetype_CheckWaterTransition(self);
-}
-
-void _Movetype_Physics_Frame(float movedt)
-{
-       self.move_didgravity = -1;
-       switch(self.move_movetype)
-       {
-               case MOVETYPE_PUSH:
-               case MOVETYPE_FAKEPUSH:
-                       error("SV_Physics_Pusher not implemented");
-                       break;
-               case MOVETYPE_NONE:
-                       break;
-               case MOVETYPE_FOLLOW:
-                       error("SV_Physics_Follow not implemented");
-                       break;
-               case MOVETYPE_NOCLIP:
-                       _Movetype_CheckWater(self);
-                       self.move_origin = self.move_origin + ticrate * self.move_velocity;
-                       self.move_angles = self.move_angles + ticrate * self.move_avelocity;
-                       _Movetype_LinkEdict(false);
-                       break;
-               case MOVETYPE_STEP:
-                       error("SV_Physics_Step not implemented");
-                       break;
-               case MOVETYPE_WALK:
-                       error("SV_Physics_Walk not implemented");
-                       break;
-               case MOVETYPE_TOSS:
-               case MOVETYPE_BOUNCE:
-               case MOVETYPE_BOUNCEMISSILE:
-               case MOVETYPE_FLYMISSILE:
-               case MOVETYPE_FLY:
-                       _Movetype_Physics_Toss(movedt);
-                       break;
-       }
-}
-
-void Movetype_Physics_NoMatchServer() // optimized
-{
-       float movedt;
-
-       movedt = time - self.move_time;
-       self.move_time = time;
-
-       _Movetype_Physics_Frame(movedt);
-       if(wasfreed(self))
-               return;
-
-       self.avelocity = self.move_avelocity;
-       self.velocity = self.move_velocity;
-       self.angles = self.move_angles;
-       setorigin(self, self.move_origin);
-}
-
-void Movetype_Physics_MatchServer(bool sloppy)
-{
-       Movetype_Physics_MatchTicrate(ticrate, sloppy);
-}
-
-void Movetype_Physics_MatchTicrate(float tr, bool sloppy) // SV_Physics_Entity
-{
-       float n, i, dt, movedt;
-
-       if(tr <= 0)
-       {
-               Movetype_Physics_NoMatchServer();
-               return;
-       }
-
-       dt = time - self.move_time;
-
-       movedt = tr;
-       n = max(0, floor(dt / tr));
-       dt -= n * tr;
-       self.move_time += n * tr;
-
-       if(!self.move_didgravity)
-               self.move_didgravity = ((self.move_movetype == MOVETYPE_BOUNCE || self.move_movetype == MOVETYPE_TOSS) && !(self.move_flags & FL_ONGROUND));
-
-       for(i = 0; i < n; ++i)
-       {
-               _Movetype_Physics_Frame(movedt);
-               if(wasfreed(self))
-                       return;
-       }
-
-       self.avelocity = self.move_avelocity;
-
-       if(dt > 0 && self.move_movetype != MOVETYPE_NONE && !(self.move_flags & FL_ONGROUND))
-       {
-               // now continue the move from move_time to time
-               self.velocity = self.move_velocity;
-
-               if(self.move_didgravity > 0)
-               {
-                       if(GRAVITY_UNAFFECTED_BY_TICRATE)
-                       {
-                               if(self.gravity)
-                                       self.velocity_z -= 0.5 * dt * self.gravity * getstatf(STAT_MOVEVARS_GRAVITY);
-                               else
-                                       self.velocity_z -= 0.5 * dt * getstatf(STAT_MOVEVARS_GRAVITY);
-                       }
-                       else
-                       {
-                               if(self.gravity)
-                                       self.velocity_z -= dt * self.gravity * getstatf(STAT_MOVEVARS_GRAVITY);
-                               else
-                                       self.velocity_z -= dt * getstatf(STAT_MOVEVARS_GRAVITY);
-                       }
-               }
-
-               self.angles = self.move_angles + dt * self.avelocity;
-
-               if(sloppy || self.movetype == MOVETYPE_NOCLIP)
-               {
-                       setorigin(self, self.move_origin + dt * self.velocity);
-               }
-               else
-               {
-                       _Movetype_PushEntityTrace(dt * self.velocity);
-                       if(!trace_startsolid)
-                               setorigin(self, trace_endpos);
-               }
-
-               if(self.move_didgravity > 0)
-               {
-                       if(GRAVITY_UNAFFECTED_BY_TICRATE)
-                       {
-                               if(self.gravity)
-                                       self.velocity_z -= 0.5 * dt * self.gravity * getstatf(STAT_MOVEVARS_GRAVITY);
-                               else
-                                       self.velocity_z -= 0.5 * dt * getstatf(STAT_MOVEVARS_GRAVITY);
-                       }
-               }
-       }
-       else
-       {
-               self.velocity = self.move_velocity;
-               self.angles = self.move_angles;
-               setorigin(self, self.move_origin);
-       }
-}
diff --git a/qcsrc/client/movetypes.qh b/qcsrc/client/movetypes.qh
deleted file mode 100644 (file)
index 3294ce4..0000000
+++ /dev/null
@@ -1,44 +0,0 @@
-#ifndef MOVETYPES_H
-#define MOVETYPES_H
-
-.float move_movetype;
-.float move_time;
-.vector move_origin;
-.vector move_angles;
-.vector move_velocity;
-.vector move_avelocity;
-.int move_flags;
-.int move_watertype;
-.int move_waterlevel;
-.void(void) move_touch;
-.void(float, float) contentstransition;
-.float move_bounce_factor;
-.float move_bounce_stopspeed;
-.float move_nomonsters; // -1 for MOVE_NORMAL, otherwise a MOVE_ constant
-
-// should match sv_gameplayfix_fixedcheckwatertransition
-float autocvar_cl_gameplayfix_fixedcheckwatertransition = 1;
-
-void Movetype_Physics_MatchTicrate(float tr, bool sloppy);
-void Movetype_Physics_MatchServer(bool sloppy);
-void Movetype_Physics_NoMatchServer();
-
-const int MOVETYPE_NONE                                = 0;
-const int MOVETYPE_ANGLENOCLIP     = 1;
-const int MOVETYPE_ANGLECLIP       = 2;
-const int MOVETYPE_WALK                                = 3;
-const int MOVETYPE_STEP                                = 4;
-const int MOVETYPE_FLY                         = 5;
-const int MOVETYPE_TOSS                                = 6;
-const int MOVETYPE_PUSH                                = 7;
-const int MOVETYPE_NOCLIP                  = 8;
-const int MOVETYPE_FLYMISSILE      = 9;
-const int MOVETYPE_BOUNCE                  = 10;
-const int MOVETYPE_BOUNCEMISSILE       = 11;   // Like bounce but doesn't lose speed on bouncing
-const int MOVETYPE_FOLLOW           = 12;
-const int MOVETYPE_FAKEPUSH         = 13;
-const int MOVETYPE_FLY_WORLDONLY    = 33;
-
-const int FL_ITEM                   = 256;
-const int FL_ONGROUND                          = 512;
-#endif
index e61071137dceb41a60888cf2c7b29d90f6887bd2..e997d15600f835a1b40de4fac68618374d301886 100644 (file)
 #include "particles.qh"
 #include "_all.qh"
 
-#include "bgmscript.qh"
-
 #include "../common/stats.qh"
 #include "../common/util.qh"
 
 #include "../warpzonelib/common.qh"
 
-void Draw_PointParticles()
-{
-       float n, i, fail;
-       vector p;
-       vector sz;
-       vector o;
-       o = self.origin;
-       sz = self.maxs - self.mins;
-       n = doBGMScript(self);
-       if(self.absolute == 2)
-       {
-               if(n >= 0)
-                       n = self.just_toggled ? self.impulse : 0;
-               else
-                       n = self.impulse * drawframetime;
-       }
-       else
-       {
-               n *= self.impulse * drawframetime;
-               if(self.just_toggled)
-                       if(n < 1)
-                               n = 1;
-       }
-       if(n == 0)
-               return;
-       fail = 0;
-       for(i = random(); i <= n && fail <= 64*n; ++i)
-       {
-               p = o + self.mins;
-               p.x += random() * sz.x;
-               p.y += random() * sz.y;
-               p.z += random() * sz.z;
-               if(WarpZoneLib_BoxTouchesBrush(p, p, self, world))
-               {
-                       if(self.movedir != '0 0 0')
-                       {
-                               traceline(p, p + normalize(self.movedir) * 4096, 0, world);
-                               p = trace_endpos;
-                               pointparticles(self.cnt, p, trace_plane_normal * vlen(self.movedir) + self.velocity + randomvec() * self.waterlevel, self.count);
-                       }
-                       else
-                       {
-                               pointparticles(self.cnt, p, self.velocity + randomvec() * self.waterlevel, self.count);
-                       }
-                       if(self.noise != "")
-                       {
-                               setorigin(self, p);
-                               sound(self, CH_AMBIENT, self.noise, VOL_BASE * self.volume, self.atten);
-                       }
-                       self.just_toggled = 0;
-               }
-               else if(self.absolute)
-               {
-                       ++fail;
-                       --i;
-               }
-       }
-       setorigin(self, o);
-}
-
-void Ent_PointParticles_Remove()
-{
-       if(self.noise)
-               strunzone(self.noise);
-       self.noise = string_null;
-       if(self.bgmscript)
-               strunzone(self.bgmscript);
-       self.bgmscript = string_null;
-}
-
-void Ent_PointParticles()
-{
-       float i;
-       vector v;
-       int f = ReadByte();
-       if(f & 2)
-       {
-               i = ReadCoord(); // density (<0: point, >0: volume)
-               if(i && !self.impulse && self.cnt) // self.cnt check is so it only happens if the ent already existed
-                       self.just_toggled = 1;
-               self.impulse = i;
-       }
-       if(f & 4)
-       {
-               self.origin_x = ReadCoord();
-               self.origin_y = ReadCoord();
-               self.origin_z = ReadCoord();
-       }
-       if(f & 1)
-       {
-               self.modelindex = ReadShort();
-               if(f & 0x80)
-               {
-                       if(self.modelindex)
-                       {
-                               self.mins_x = ReadCoord();
-                               self.mins_y = ReadCoord();
-                               self.mins_z = ReadCoord();
-                               self.maxs_x = ReadCoord();
-                               self.maxs_y = ReadCoord();
-                               self.maxs_z = ReadCoord();
-                       }
-                       else
-                       {
-                               self.mins    = '0 0 0';
-                               self.maxs_x = ReadCoord();
-                               self.maxs_y = ReadCoord();
-                               self.maxs_z = ReadCoord();
-                       }
-               }
-               else
-               {
-                       self.mins = self.maxs = '0 0 0';
-               }
-
-               self.cnt = ReadShort(); // effect number
-
-               if(f & 0x20)
-               {
-                       self.velocity = decompressShortVector(ReadShort());
-                       self.movedir = decompressShortVector(ReadShort());
-               }
-               else
-               {
-                       self.velocity = self.movedir = '0 0 0';
-               }
-               if(f & 0x40)
-               {
-                       self.waterlevel = ReadShort() / 16.0;
-                       self.count = ReadByte() / 16.0;
-               }
-               else
-               {
-                       self.waterlevel = 0;
-                       self.count = 1;
-               }
-               if(self.noise)
-                       strunzone(self.noise);
-               if(self.bgmscript)
-                       strunzone(self.bgmscript);
-               self.noise = strzone(ReadString());
-               if(self.noise != "")
-               {
-                       self.atten = ReadByte() / 64.0;
-                       self.volume = ReadByte() / 255.0;
-               }
-               self.bgmscript = strzone(ReadString());
-               if(self.bgmscript != "")
-               {
-                       self.bgmscriptattack = ReadByte() / 64.0;
-                       self.bgmscriptdecay = ReadByte() / 64.0;
-                       self.bgmscriptsustain = ReadByte() / 255.0;
-                       self.bgmscriptrelease = ReadByte() / 64.0;
-               }
-               BGMScript_InitEntity(self);
-       }
-
-       if(f & 2)
-       {
-               self.absolute = (self.impulse >= 0);
-               if(!self.absolute)
-               {
-                       v = self.maxs - self.mins;
-                       self.impulse *= -v.x * v.y * v.z / 262144; // relative: particles per 64^3 cube
-               }
-       }
-
-       if(f & 0x10)
-               self.absolute = 2;
-
-       setorigin(self, self.origin);
-       setsize(self, self.mins, self.maxs);
-       self.solid = SOLID_NOT;
-       self.draw = Draw_PointParticles;
-       self.entremove = Ent_PointParticles_Remove;
-}
-
-void Draw_Rain()
-{
-    te_particlerain(self.origin + self.mins, self.origin + self.maxs, self.velocity, floor(self.count * drawframetime + random()), self.glow_color);
-}
-
-void Draw_Snow()
-{
-    te_particlesnow(self.origin + self.mins, self.origin + self.maxs, self.velocity, floor(self.count * drawframetime + random()), self.glow_color);
-}
-
-void Ent_RainOrSnow()
-{
-       self.impulse = ReadByte(); // Rain, Snow, or Whatever
-       self.origin_x = ReadCoord();
-       self.origin_y = ReadCoord();
-       self.origin_z = ReadCoord();
-       self.maxs_x = ReadCoord();
-       self.maxs_y = ReadCoord();
-       self.maxs_z = ReadCoord();
-       self.velocity = decompressShortVector(ReadShort());
-       self.count = ReadShort() * 10;
-       self.glow_color = ReadByte(); // color
-
-       self.mins    = -0.5 * self.maxs;
-       self.maxs    =  0.5 * self.maxs;
-       self.origin  = self.origin - self.mins;
-
-       setorigin(self, self.origin);
-       setsize(self, self.mins, self.maxs);
-       self.solid = SOLID_NOT;
-       if(self.impulse)
-               self.draw = Draw_Rain;
-       else
-               self.draw = Draw_Snow;
-}
-
 void Net_ReadVortexBeamParticle()
 {
        vector shotorg, endpos;
index 586f3f00004e35dfe0e0d51b3387f3e3b6f26ac2..791313f50408de88ed712a9bd60d90b2a3c3cd3e 100644 (file)
@@ -1,6 +1,5 @@
 #ifndef PARTICLES_H
 #define PARTICLES_H
-
 .int dphitcontentsmask;
 
 entityclass(PointParticles);
@@ -23,11 +22,5 @@ void Ent_PointParticles();
 
 class(PointParticles) .float glow_color; // palette index
 
-void Draw_Rain();
-
-void Draw_Snow();
-
-void Ent_RainOrSnow();
-
 void Net_ReadVortexBeamParticle();
 #endif
index 9df837229692bace30366a8becff7470e79c9e32..1d67e171ca667fa225e83644c7660254a60055af 100644 (file)
@@ -172,6 +172,8 @@ void skeleton_from_frames(entity e, float is_dead)
 
        if(!is_dead)
        {
+               if(self == csqcplayer)
+                       self.v_angle_x = input_angles_x;
                int i;
                for(i = 0; i < MAX_AIM_BONES; ++i)
                {
index 8ba12b9e71d957c7260fcbd09dbf80b562c9b4ee..a9d0c565a7e1ffc3b627db8ca4b0b00abdc4f746 100644 (file)
@@ -13,13 +13,11 @@ gibs.qc
 hook.qc
 hud.qc
 hud_config.qc
-laser.qc
 main.qc
 mapvoting.qc
 miscfunctions.qc
 modeleffects.qc
 movelib.qc
-movetypes.qc
 noise.qc
 particles.qc
 player_skeleton.qc
@@ -28,7 +26,6 @@ rubble.qc
 scoreboard.qc
 shownames.qc
 sortlist.qc
-target_music.qc
 teamradar.qc
 tturrets.qc
 tuba.qc
@@ -47,9 +44,11 @@ weapons/projectile.qc // TODO
 ../common/animdecide.qc
 ../common/buffs.qc
 ../common/mapinfo.qc
+../common/movetypes/include.qc
 ../common/nades.qc
 ../common/net_notice.qc
 ../common/notifications.qc
+../common/physics.qc
 ../common/playerstats.qc
 ../common/test.qc
 ../common/urllib.qc
@@ -61,11 +60,16 @@ weapons/projectile.qc // TODO
 
 ../common/weapons/all.qc // TODO
 
+../common/triggers/include.qc
+
 ../csqcmodellib/cl_model.qc
 ../csqcmodellib/cl_player.qc
 ../csqcmodellib/interpolate.qc
 
+../server/mutators/mutator_multijump.qc
+
 ../warpzonelib/anglestransform.qc
 ../warpzonelib/client.qc
 ../warpzonelib/common.qc
 ../warpzonelib/mathlib.qc
+../warpzonelib/util_server.qc
index 905c66c3cc13895b3500915a8631560e747ff4aa..418e1db08b2697239e7d81194fe91e82279d8ff6 100644 (file)
@@ -360,7 +360,6 @@ void Cmd_HUD_SetFields(float argc)
 
        hud_fontsize = HUD_GetFontsize("hud_fontsize");
 
-       draw_beginBoldFont();
        for(i = 1; i < argc - 1; ++i)
        {
                float nocomplain;
@@ -499,7 +498,6 @@ void Cmd_HUD_SetFields(float argc)
        }
 
        hud_field[hud_num_fields] = SP_END;
-       draw_endBoldFont();
 }
 
 // MOVEUP::
@@ -879,7 +877,6 @@ vector HUD_Scoreboard_MakeTable(vector pos, entity tm, vector rgb, vector bg_siz
        }
 
        // print the strings of the columns headers and draw the columns
-       draw_beginBoldFont();
        int i;
        for(i = 0; i < hud_num_fields; ++i)
        {
@@ -923,7 +920,6 @@ vector HUD_Scoreboard_MakeTable(vector pos, entity tm, vector rgb, vector bg_siz
                        pos.x -= hud_fontsize.x;
                }
        }
-       draw_endBoldFont();
 
        pos.x = xmin;
        pos.y += 1.25 * hud_fontsize.y; // skip the header
index ca631fcbd518929531c402fd5ef1e303cdf0d176..3981b29eb85fdca9f6f9384cb321b2308a1917c2 100644 (file)
@@ -1,7 +1,7 @@
 #include "_all.qh"
 
-#include "movetypes.qh"
 #include "../common/buffs.qh"
+#include "../common/movetypes/movetypes.qh"
 #include "../common/util.qh"
 #include "../common/weapons/all.qh"
 #include "../csqcmodellib/cl_model.qh"
diff --git a/qcsrc/client/target_music.qc b/qcsrc/client/target_music.qc
deleted file mode 100644 (file)
index 7711c25..0000000
+++ /dev/null
@@ -1,197 +0,0 @@
-#include "target_music.qh"
-#include "_all.qh"
-
-#include "../common/constants.qh"
-#include "../common/util.qh"
-
-#include "../warpzonelib/common.qh"
-
-.string noise;
-.float cnt;
-.float count;
-.float fade_time;
-.float fade_rate;
-.float lifetime;
-.float volume;
-
-void TargetMusic_Advance()
-{
-       // run AFTER all the thinks!
-       entity best, e;
-       float vol, vol0;
-       best = music_default;
-       if(music_target && time < music_target.lifetime)
-               best = music_target;
-       if(music_trigger)
-               best = music_trigger;
-       for(e = world; (e = findfloat(e, enttype, ENT_CLIENT_TRIGGER_MUSIC)); ) if(e.noise)
-       {
-               vol0 = e.lastvol;
-               if(getsoundtime(e, CH_BGM_SINGLE) < 0)
-               {
-                       vol0 = -1;
-               }
-               if(e == best)
-               {
-                       // increase volume
-                       if(e.fade_time > 0)
-                               e.state = bound(0, e.state + frametime / e.fade_time, 1);
-                       else
-                               e.state = 1;
-               }
-               else
-               {
-                       // decrease volume
-                       if(e.fade_rate > 0)
-                               e.state = bound(0, e.state - frametime / e.fade_rate, 1);
-                       else
-                               e.state = 0;
-               }
-               vol = e.state * e.volume * autocvar_bgmvolume;
-               if(vol != vol0)
-               {
-                       if(vol0 < 0)
-                               sound(e, CH_BGM_SINGLE, e.noise, vol, ATTEN_NONE); // restart
-                       else
-                               sound(e, CH_BGM_SINGLE, "", vol, ATTEN_NONE);
-                       e.lastvol = vol;
-               }
-       }
-       music_trigger = world;
-
-       if(best)
-               bgmtime = getsoundtime(best, CH_BGM_SINGLE);
-       else
-               bgmtime = gettime(GETTIME_CDTRACK);
-}
-
-void Net_TargetMusic()
-{
-       int id = ReadShort();
-       float vol = ReadByte() / 255.0;
-       float fai = ReadByte() / 16.0;
-       float fao = ReadByte() / 16.0;
-       float tim = ReadByte();
-       string noi = ReadString();
-
-       entity e;
-       for(e = world; (e = findfloat(e, enttype, ENT_CLIENT_TRIGGER_MUSIC)); )
-       {
-               if(e.count == id)
-                       break;
-       }
-       if(!e)
-       {
-               e = spawn();
-               e.enttype = ENT_CLIENT_TRIGGER_MUSIC;
-               e.count = id;
-       }
-       if(e.noise != noi)
-       {
-               if(e.noise)
-                       strunzone(e.noise);
-               e.noise = strzone(noi);
-               precache_sound(e.noise);
-               sound(e, CH_BGM_SINGLE, e.noise, 0, ATTEN_NONE);
-               if(getsoundtime(e, CH_BGM_SINGLE) < 0)
-               {
-                       dprintf("Cannot initialize sound %s\n", e.noise);
-                       strunzone(e.noise);
-                       e.noise = string_null;
-               }
-       }
-       e.volume = vol;
-       e.fade_time = fai;
-       e.fade_rate = fao;
-       if(vol > 0)
-       {
-               if(tim == 0)
-               {
-                       music_default = e;
-                       if(!music_disabled)
-                       {
-                               e.state = 2;
-                               cvar_settemp("music_playlist_index", "-1"); // don't use playlists
-                               localcmd("cd stop\n"); // just in case
-                               music_disabled = 1;
-                       }
-               }
-               else
-               {
-                       music_target = e;
-                       e.lifetime = time + tim;
-               }
-       }
-}
-
-void Ent_TriggerMusic_Think()
-{
-       if(WarpZoneLib_BoxTouchesBrush(view_origin, view_origin, self, world))
-       {
-               music_trigger = self;
-       }
-       self.nextthink = time;
-}
-
-void Ent_TriggerMusic_Remove()
-{
-       if(self.noise)
-               strunzone(self.noise);
-       self.noise = string_null;
-}
-
-void Ent_ReadTriggerMusic()
-{
-       int f = ReadByte();
-       if(f & 4)
-       {
-               self.origin_x = ReadCoord();
-               self.origin_y = ReadCoord();
-               self.origin_z = ReadCoord();
-       }
-       if(f & 1)
-       {
-               self.modelindex = ReadShort();
-               if(self.modelindex)
-               {
-                       self.mins_x = ReadCoord();
-                       self.mins_y = ReadCoord();
-                       self.mins_z = ReadCoord();
-                       self.maxs_x = ReadCoord();
-                       self.maxs_y = ReadCoord();
-                       self.maxs_z = ReadCoord();
-               }
-               else
-               {
-                       self.mins    = '0 0 0';
-                       self.maxs_x = ReadCoord();
-                       self.maxs_y = ReadCoord();
-                       self.maxs_z = ReadCoord();
-               }
-
-               self.volume = ReadByte() / 255.0;
-               self.fade_time = ReadByte() / 16.0;
-               self.fade_rate = ReadByte() / 16.0;
-               string s = self.noise;
-               if(self.noise)
-                       strunzone(self.noise);
-               self.noise = strzone(ReadString());
-               if(self.noise != s)
-               {
-                       precache_sound(self.noise);
-                       sound(self, CH_BGM_SINGLE, self.noise, 0, ATTEN_NONE);
-                       if(getsoundtime(self, CH_BGM_SINGLE) < 0)
-                       {
-                               dprintf("Cannot initialize sound %s\n", self.noise);
-                               strunzone(self.noise);
-                               self.noise = string_null;
-                       }
-               }
-       }
-
-       setorigin(self, self.origin);
-       setsize(self, self.mins, self.maxs);
-       self.cnt = 1;
-       self.think = Ent_TriggerMusic_Think;
-       self.nextthink = time;
-}
diff --git a/qcsrc/client/target_music.qh b/qcsrc/client/target_music.qh
deleted file mode 100644 (file)
index 182e1f4..0000000
+++ /dev/null
@@ -1,23 +0,0 @@
-#ifndef TARGET_MUSIC_H
-#define TARGET_MUSIC_H
-
-float music_disabled;
-entity music_default;
-entity music_target;
-entity music_trigger;
-// FIXME also control bgmvolume here, to not require a target_music for the default track.
-
-entityclass(TargetMusic);
-class(TargetMusic) .int state;
-class(TargetMusic) .float lastvol;
-
-void TargetMusic_Advance();
-
-void Net_TargetMusic();
-
-void Ent_TriggerMusic_Think();
-
-void Ent_TriggerMusic_Remove();
-
-void Ent_ReadTriggerMusic();
-#endif
index b74024d508aa09b0bf5dab2b660447aa5f2d5f33..58c031e4d85027425e530aebf125a310882899d0 100644 (file)
@@ -3,13 +3,14 @@
 
 #include "hud.qh"
 #include "movelib.qh"
-#include "movetypes.qh"
 #include "prandom.qh"
 #include "teamradar.qh"
 #include "waypointsprites.qh"
 
 #include "../common/teams.qh"
 
+#include "../common/movetypes/movetypes.qh"
+
 #include "../server/tturrets/include/turrets_early.qh"
 
 #include "../warpzonelib/anglestransform.qh"
index 0af4d4b967f1a6b5e2a9b0e4443d3baa8763e393..ba92d7c3198383da21ace9e504c6dca9674ad0fd 100644 (file)
@@ -1,14 +1,14 @@
 #include "all.qh"
 #include "../_all.qh"
 
-#include "../movetypes.qh"
-#include "../movetypes.qh"
+#include "../../common/movetypes/movetypes.qh"
 #include "../prandom.qh"
 #include "../scoreboard.qh"
 #include "../t_items.qh"
 
 #include "../../common/buffs.qh"
 #include "../../common/constants.qh"
+#include "../../common/movetypes/movetypes.qh"
 #include "../../common/stats.qh"
 #include "../../common/util.qh"
 
index 8430668cda01c6308bdf628a111f67b3423ca3f8..1304d3a2e63f3492057e515be176d07540f49860 100644 (file)
@@ -3,9 +3,10 @@
 #include "../gibs.qh"
 #include "../hook.qh"
 #include "../main.qh"
-#include "../movetypes.qh"
 #include "../wall.qh"
 
 #include "../weapons/projectile.qh"
 
+#include "../../common/movetypes/movetypes.qh"
+
 #include "../../server/vehicles/bumblebee.qc"
index d3bcf92447bc928f8c26680f18a230cd60915eb9..35793364fafd3c60c9853065064f39a32f6bc840 100644 (file)
@@ -8,7 +8,6 @@
 #include "noise.qh"
 #include "scoreboard.qh"
 #include "shownames.qh"
-#include "target_music.qh"
 #include "vehicles/all.qh"
 #include "waypointsprites.qh"
 
@@ -16,6 +15,7 @@
 #include "../common/mapinfo.qh"
 #include "../common/nades.qh"
 #include "../common/stats.qh"
+#include "../common/triggers/target/music.qh"
 #include "../common/teams.qh"
 #include "../common/util.qh"
 
@@ -1054,7 +1054,11 @@ void CSQC_UpdateView(float w, float h)
 
        CSQCPlayer_SetCamera();
 
-       myteam = GetPlayerColor(player_localentnum - 1);
+       if(player_localentnum <= maxclients) // is it a client?
+               current_player = player_localentnum - 1;
+       else // then player_localentnum is the vehicle I'm driving
+               current_player = player_localnum;
+       myteam = GetPlayerColor(current_player);
 
        if(myteam != prev_myteam)
        {
index d84fa46b6cd8812ad6540c9844f385d8ff003ac6..04c0dce24f84326303bf3e57638da5e8329f2f67 100644 (file)
@@ -15,8 +15,6 @@ class(Wall) .vector saved;
 // fade_vertical_offset is a vertival offset for player position
 .float fade_start, fade_end, fade_vertical_offset;
 .float default_solid;
-// This variable will be set by trigger
-.float antiwall_flag;
 
 void Ent_Wall_Draw();
 
index aed0865dda1e0152b3cdc87c745a21ee9f31c0b9..0b32de7e920b5e598d1a3aaed844f8f1f22941cb 100644 (file)
@@ -54,7 +54,7 @@ void drawquad(vector o, vector ri, vector up, string pic, vector rgb, float a, f
        R_EndPolygon();
 }
 
-void drawhealthbar(vector org, float rot, float h, vector sz, vector hotspot, float width, float height, float margin, float border, float align, vector rgb, float a, vector hrgb, float ha, float f)
+void drawhealthbar(vector org, float rot, float h, vector sz, vector hotspot, float width, float theheight, float margin, float border, float align, vector rgb, float a, vector hrgb, float ha, float f)
 {
        vector o, ri, up;
        float owidth; // outer width
@@ -72,13 +72,13 @@ void drawhealthbar(vector org, float rot, float h, vector sz, vector hotspot, fl
        up = rotate(up, rot);
 
        owidth = width + 2 * border;
-       o = o - up * (margin + border + height) + ri * (sz.x - owidth) * 0.5;
+       o = o - up * (margin + border + theheight) + ri * (sz.x - owidth) * 0.5;
 
        drawquad(o - up * border,                               ri * owidth,    up * border, "", rgb,  a,  f);
-       drawquad(o + up * height,                               ri * owidth,    up * border, "", rgb,  a,  f);
-       drawquad(o,                                             ri * border,    up * height, "", rgb,  a,  f);
-       drawquad(o + ri * (owidth - border),                    ri * border,    up * height, "", rgb,  a,  f);
-       drawquad(o + ri * (border + align * ((1 - h) * width)), ri * width * h, up * height, "", hrgb, ha, f);
+       drawquad(o + up * theheight,                               ri * owidth,    up * border, "", rgb,  a,  f);
+       drawquad(o,                                             ri * border,    up * theheight, "", rgb,  a,  f);
+       drawquad(o + ri * (owidth - border),                    ri * border,    up * theheight, "", rgb,  a,  f);
+       drawquad(o + ri * (border + align * ((1 - h) * width)), ri * width * h, up * theheight, "", hrgb, ha, f);
 }
 
 // returns location of sprite text
index cd4f9b7441aee5483e0e9207591c8fb8cbe8912a..efa6509c7060e2085458923b33e668e0d2e4fc39 100644 (file)
@@ -3,10 +3,10 @@
 #include "../autocvars.qh"
 #include "../defs.qh"
 #include "../main.qh"
-#include "../movetypes.qh"
 
 #include "../../common/constants.qh"
 #include "../../common/nades.qh"
+#include "../../common/movetypes/movetypes.qh"
 #include "../../common/util.qh"
 
 #include "../../csqcmodellib/interpolate.qh"
@@ -486,6 +486,7 @@ void Ent_Projectile()
        if(!(self.count & 0x80))
                InterpolateOrigin_Note();
 
+       self.classname = "csqcprojectile";
        self.draw = Projectile_Draw;
        self.entremove = Ent_RemoveProjectile;
 }
index 9c998f39a85d6041a203f4915123294e3931d7d1..939c6d7c17ffaf882155ac46c63ffed740fd7e86 100644 (file)
@@ -1,6 +1,31 @@
 #ifndef CONSTANTS_H
 #define CONSTANTS_H
 
+// COMMIT-TODO: Update if necessary before committing
+// Revision 1: additional statistics sent (flag caps, returns, deaths)
+// Revision 2: Mapvote preview pictures
+// Revision 3: optimized map vote protocol
+// Revision 4: CSQC config var system
+// Revision 5: mapvote time fix
+// Revision 6: more robust against packet loss/delays, also show not yet connected clients
+// Revision 7: packet loss column
+// Revision 8: race
+// Revision 9: race delta
+// Revision 10: scoreboard force
+// Revision 11: scoreboard unforce; spectator support beginning
+// Revision 12: smaller scores updates (SERVER: requires new engine)
+// Revision 13: pointparticles
+// Revision 14: laser
+// Revision 15: zoom
+// Revision 16: multi-weapons
+// Revision 17: multi-weaponimpulses
+// Revision 18: warmup
+// Revision 19: fog
+// Revision 20: naggers
+// Revision 21: entcs for players optimized (position data down from 12 to 7 bytes); waypointsprites in csqc for team radar
+// Revision 22: hook shot origin
+#define CSQC_REVISION 22
+
 const int AS_STRING = 1;
 const int AS_INT = 2;
 const int AS_FLOAT_TRUNCATED = 2;
@@ -79,6 +104,17 @@ const int ENT_CLIENT_ELIMINATEDPLAYERS = 39;
 const int ENT_CLIENT_TURRET = 40;
 const int ENT_CLIENT_AUXILIARYXHAIR = 50;
 const int ENT_CLIENT_VEHICLE = 60;
+const int ENT_CLIENT_LADDER = 61;
+const int ENT_CLIENT_TRIGGER_PUSH = 62;
+const int ENT_CLIENT_TARGET_PUSH = 63;
+const int ENT_CLIENT_CONVEYOR = 64;
+const int ENT_CLIENT_DOOR = 65;
+const int ENT_CLIENT_TRAIN = 66;
+const int ENT_CLIENT_PLAT = 67;
+const int ENT_CLIENT_TRIGGER_IMPULSE = 68;
+const int ENT_CLIENT_SWAMP = 69;
+const int ENT_CLIENT_CORNER = 70;
+const int ENT_CLIENT_KEYLOCK = 71;
 
 const int ENT_CLIENT_HEALING_ORB = 80;
 
@@ -277,7 +313,7 @@ const int SERVERFLAG_ALLOW_FULLBRIGHT = 1;
 const int SERVERFLAG_TEAMPLAY = 2;
 const int SERVERFLAG_PLAYERSTATS = 4;
 
-// FIXME/EXPLAINME: why?
+// FIXME/EXPLAINME: why? Mario: because
 vector autocvar_sv_player_maxs = '16 16 45';
 vector autocvar_sv_player_mins = '-16 -16 -24';
 vector autocvar_sv_player_viewoffset = '0 0 20';
@@ -286,6 +322,9 @@ vector autocvar_sv_player_crouch_mins = '-16 -16 -24';
 vector autocvar_sv_player_crouch_viewoffset = '0 0 20';
 vector autocvar_sv_player_headsize = '24 24 12';
 
+
+// not so constant
+#ifdef SVQC
 #define PL_VIEW_OFS autocvar_sv_player_viewoffset
 #define PL_MIN autocvar_sv_player_mins
 #define PL_MAX autocvar_sv_player_maxs
@@ -293,16 +332,18 @@ vector autocvar_sv_player_headsize = '24 24 12';
 #define PL_CROUCH_MIN autocvar_sv_player_crouch_mins
 #define PL_CROUCH_MAX autocvar_sv_player_crouch_maxs
 #define PL_HEAD autocvar_sv_player_headsize
+#elif defined(CSQC)
+#define PL_VIEW_OFS vec3(getstatf(STAT_PL_VIEW_OFS1), getstatf(STAT_PL_VIEW_OFS2), getstatf(STAT_PL_VIEW_OFS3))
+#define PL_MIN vec3(getstatf(STAT_PL_MIN1), getstatf(STAT_PL_MIN2), getstatf(STAT_PL_MIN3))
+#define PL_MAX vec3(getstatf(STAT_PL_MAX1), getstatf(STAT_PL_MAX2), getstatf(STAT_PL_MAX3))
+#define PL_CROUCH_VIEW_OFS vec3(getstatf(STAT_PL_CROUCH_VIEW_OFS1), getstatf(STAT_PL_CROUCH_VIEW_OFS2), getstatf(STAT_PL_CROUCH_VIEW_OFS3))
+#define PL_CROUCH_MIN vec3(getstatf(STAT_PL_CROUCH_MIN1), getstatf(STAT_PL_CROUCH_MIN2), getstatf(STAT_PL_CROUCH_MIN3))
+#define PL_CROUCH_MAX vec3(getstatf(STAT_PL_CROUCH_MAX1), getstatf(STAT_PL_CROUCH_MAX2), getstatf(STAT_PL_CROUCH_MAX3))
+#endif
 
-// helpers
-#define PL_VIEW_OFS_z autocvar_sv_player_viewoffset.z
-#define PL_MIN_z autocvar_sv_player_mins.z
-#define PL_MAX_z autocvar_sv_player_maxs.z
-#define PL_CROUCH_VIEW_OFS_z autocvar_sv_player_crouch_viewoffset.z
-#define PL_CROUCH_MIN_z autocvar_sv_player_mins.z
-#define PL_HEAD_x autocvar_sv_player_headsize.x
-#define PL_HEAD_y autocvar_sv_player_headsize.y
-#define PL_HEAD_z autocvar_sv_player_headsize.z
+// a bit more constant
+const vector PL_MAX_CONST = '16 16 45';
+const vector PL_MIN_CONST = '-16 -16 -24';
 
 // spawnpoint prios
 const int SPAWN_PRIO_NEAR_TEAMMATE_FOUND = 200;
index fdc555e2460e69fd7f344de9f15dfcf65b2533f0..5e5ff42eb47e2893ca4699626fef5bfece9abe13 100644 (file)
@@ -8,7 +8,7 @@
 //#define CSQCMODEL_SUPPORT_GETTAGINFO_BEFORE_DRAW
 
 // server decides crouching, this lags, but so be it
-#define CSQCMODEL_SERVERSIDE_CROUCH
+//#define CSQCMODEL_SERVERSIDE_CROUCH
 
 // a hack for Xonotic
 #ifdef CSQC
@@ -52,7 +52,8 @@
                CSQCMODEL_PROPERTY(512, float, ReadApproxPastTime, WriteApproxPastTime, anim_upper_time) \
        CSQCMODEL_ENDIF \
        CSQCMODEL_PROPERTY(1024, float, ReadAngle, WriteAngle, v_angle_x) \
-       CSQCMODEL_PROPERTY_SCALED(4096, float, ReadByte, WriteByte, scale, 16, 0, 255)
+       CSQCMODEL_PROPERTY_SCALED(4096, float, ReadByte, WriteByte, scale, 16, 0, 255) \
+       CSQCMODEL_PROPERTY(8192, int, ReadInt24_t, WriteInt24_t, dphitcontentsmask)
 // TODO get rid of colormod/glowmod here, find good solution for vortex charge glowmod hack; also get rid of some useless properties on non-players that only exist for CopyBody
 
 // add hook function calls here
index ce254751bd8d83f9c519dbb229c11b15e16bcb44..239f8fe9a7f57bf06f2f861f0544683e0774bda7 100644 (file)
@@ -19,6 +19,7 @@
     #include "../../server/campaign.qh"
     #include "../../server/command/common.qh"
     #include "../../server/command/cmd.qh"
+       #include "../triggers/triggers.qh"
     #include "../../csqcmodellib/sv_model.qh"
     #include "../../server/round_handler.qh"
     #include "../../server/tturrets/include/turrets.qh"
diff --git a/qcsrc/common/movetypes/follow.qc b/qcsrc/common/movetypes/follow.qc
new file mode 100644 (file)
index 0000000..6cf28d6
--- /dev/null
@@ -0,0 +1,31 @@
+void _Movetype_Physics_Follow() // SV_Physics_Follow
+{
+       entity e = self.move_aiment; // TODO: networking?
+
+       // LordHavoc: implemented rotation on MOVETYPE_FOLLOW objects
+       if(self.move_angles == self.move_punchangle)
+       {
+               self.move_origin = e.move_origin + self.view_ofs;
+       }
+       else
+       {
+               vector ang, v;
+               ang_x = -self.move_punchangle_x;
+               ang_y = self.move_punchangle_y;
+               ang_z = self.move_punchangle_z;
+               makevectors(ang);
+               v_x = self.view_ofs_x * v_forward_x + self.view_ofs_y * v_right_x + self.view_ofs_z * v_up_x;
+               v_y = self.view_ofs_x * v_forward_y + self.view_ofs_y * v_right_y + self.view_ofs_z * v_up_y;
+               v_z = self.view_ofs_x * v_forward_z + self.view_ofs_y * v_right_z + self.view_ofs_z * v_up_z;
+               ang_x = -e.move_angles_x;
+               ang_y = e.move_angles_y;
+               ang_z = e.move_angles_z;
+               makevectors(ang);
+               self.move_origin_x = v_x * v_forward_x + v_y * v_forward_y + v_z * v_forward_z + e.move_origin_x;
+               self.move_origin_x = v_x * v_right_x + v_y * v_right_y + v_z * v_right_z + e.move_origin_y;
+               self.move_origin_x = v_x * v_up_x + v_y * v_up_y + v_z * v_up_z + e.move_origin_z;
+       }
+
+       self.move_angles = e.move_angles + self.v_angle;
+       _Movetype_LinkEdict(false);
+}
diff --git a/qcsrc/common/movetypes/include.qc b/qcsrc/common/movetypes/include.qc
new file mode 100644 (file)
index 0000000..322b3c4
--- /dev/null
@@ -0,0 +1,7 @@
+#include "push.qc"
+#include "toss.qc"
+#include "walk.qc"
+#include "step.qc"
+#include "follow.qc"
+
+#include "movetypes.qc"
diff --git a/qcsrc/common/movetypes/include.qh b/qcsrc/common/movetypes/include.qh
new file mode 100644 (file)
index 0000000..a96e595
--- /dev/null
@@ -0,0 +1,8 @@
+#ifndef MOVETYPES_INCLUDE_H
+#define MOVETYPES_INCLUDE_H
+
+#include "push.qh"
+#include "toss.qh"
+#include "walk.qh"
+
+#endif
diff --git a/qcsrc/common/movetypes/movetypes.qc b/qcsrc/common/movetypes/movetypes.qc
new file mode 100644 (file)
index 0000000..3dd286e
--- /dev/null
@@ -0,0 +1,683 @@
+#include "include.qh"
+#include "../physics.qh"
+
+#if defined(CSQC)
+       #include "../../dpdefs/csprogsdefs.qh"
+       #include "../../client/defs.qh"
+       #include "../stats.qh"
+       #include "../util.qh"
+       #include "movetypes.qh"
+       #include "../../csqcmodellib/common.qh"
+       #include "../../server/t_items.qh"
+#elif defined(MENUQC)
+#elif defined(SVQC)
+       #include "../../server/autocvars.qh"
+#endif
+
+void _Movetype_WallFriction(vector stepnormal)  // SV_WallFriction
+{
+       /*float d, i;
+       vector into, side;
+       makevectors(self.v_angle);
+       d = (stepnormal * v_forward) + 0.5;
+
+       if(d < 0)
+       {
+           i = (stepnormal * self.move_velocity);
+           into = i * stepnormal;
+           side = self.move_velocity - into;
+           self.move_velocity_x = side.x * (1 * d);
+           self.move_velocity_y = side.y * (1 * d);
+       }*/
+}
+
+vector planes[MAX_CLIP_PLANES];
+int _Movetype_FlyMove(float dt, bool applygravity, vector stepnormal, float stepheight) // SV_FlyMove
+{
+       int blocked = 0, bumpcount;
+       int i, j, numplanes = 0;
+       float time_left = dt, grav = 0;
+       vector push;
+       vector primal_velocity, original_velocity, new_velocity = '0 0 0', restore_velocity;
+
+       for(i = 0; i <= MAX_CLIP_PLANES; ++i)
+               planes[i] = '0 0 0';
+       
+       grav = 0;
+
+       restore_velocity = self.move_velocity;
+
+       if(applygravity)
+       {
+               self.move_didgravity = 1;
+               grav = dt * (PHYS_ENTGRAVITY(self) ? PHYS_ENTGRAVITY(self) : 1) * PHYS_GRAVITY;
+
+               if(!GAMEPLAYFIX_NOGRAVITYONGROUND || !(self.move_flags & FL_ONGROUND))
+               {
+                       if(GRAVITY_UNAFFECTED_BY_TICRATE)
+                               self.move_velocity_z -= grav * 0.5;
+                       else
+                               self.move_velocity_z -= grav;
+               }
+       }
+
+       original_velocity = primal_velocity = self.move_velocity;
+
+       for(bumpcount = 0;bumpcount < MAX_CLIP_PLANES;bumpcount++)
+       {
+               if(!self.move_velocity_x && !self.move_velocity_y && !self.move_velocity_z)
+                       break;
+
+               push = self.move_velocity * time_left;
+               if(!_Movetype_PushEntity(push, false))
+               {
+                       // we got teleported by a touch function
+                       // let's abort the move
+                       blocked |= 8;
+                       break;
+               }
+
+               // this code is used by MOVETYPE_WALK and MOVETYPE_STEP and SV_UnstickEntity
+               // abort move if we're stuck in the world (and didn't make it out)
+               if(trace_startsolid && trace_allsolid)
+               {
+                       self.move_velocity = restore_velocity;
+                       return 3;
+               }
+
+               if(trace_fraction == 1)
+                       break;
+               if(trace_plane_normal_z)
+               {
+                       if(trace_plane_normal_z > 0.7)
+                       {
+                               // floor
+                               blocked |= 1;
+
+                               if(!trace_ent)
+                               {
+                                       //dprint("_Movetype_FlyMove: !trace_ent\n");
+                                       trace_ent = world;
+                               }
+
+                               self.move_flags |= FL_ONGROUND;
+                               self.move_groundentity = trace_ent;
+                       }
+               }
+               else if(stepheight)
+               {
+                       // step - handle it immediately
+                       vector org;
+                       vector steppush;
+                       //Con_Printf("step %f %f %f : ", self.move_origin_x, PRVM_serveredictvector(ent, origin)[1], PRVM_serveredictvector(ent, origin)[2]);
+                       steppush = '0 0 1' * stepheight;
+                       org = self.move_origin;
+                       if(!_Movetype_PushEntity(steppush, false))
+                       {
+                               blocked |= 8;
+                               break;
+                       }
+                       //Con_Printf("%f %f %f : ", self.move_origin_x, PRVM_serveredictvector(ent, origin)[1], PRVM_serveredictvector(ent, origin)[2]);
+                       if(!_Movetype_PushEntity(push, false))
+                       {
+                               blocked |= 8;
+                               break;
+                       }
+                       float trace2_fraction = trace_fraction;
+                       //Con_Printf("%f %f %f : ", self.move_origin_x, PRVM_serveredictvector(ent, origin)[1], PRVM_serveredictvector(ent, origin)[2]);
+                       steppush = '0 0 1' * (org_z - self.move_origin_z);
+                       if(!_Movetype_PushEntity(steppush, false))
+                       {
+                               blocked |= 8;
+                               break;
+                       }
+                       //Con_Printf("%f %f %f : ", self.move_origin_x, PRVM_serveredictvector(ent, origin)[1], PRVM_serveredictvector(ent, origin)[2]);
+                       // accept the new position if it made some progress...
+                       if(fabs(self.move_origin_x - org_x) >= 0.03125 || fabs(self.move_origin_y - org_y) >= 0.03125)
+                       {
+                               //Con_Printf("accepted (delta %f %f %f)\n", self.move_origin_x - org_x, PRVM_serveredictvector(ent, origin)[1] - org[1], PRVM_serveredictvector(ent, origin)[2] - org[2]);
+                               trace_endpos = self.move_origin;
+                               time_left *= 1 - trace2_fraction;
+                               numplanes = 0;
+                               continue;
+                       }
+                       else
+                       {
+                               //Con_Printf("REJECTED (delta %f %f %f)\n", self.move_origin_x - org_x, PRVM_serveredictvector(ent, origin)[1] - org[1], PRVM_serveredictvector(ent, origin)[2] - org[2]);
+                               self.move_origin = org;
+                       }
+               }
+               else
+               {
+                       // step - return it to caller
+                       blocked |= 2;
+                       // save the trace for player extrafriction
+                       if(stepnormal)
+                               stepnormal = trace_plane_normal;
+               }
+               if(trace_fraction >= 0.001)
+               {
+                       // actually covered some distance
+                       original_velocity = self.move_velocity;
+                       numplanes = 0;
+               }
+
+               time_left *= 1 - trace_fraction;
+
+               // clipped to another plane
+               if(numplanes >= MAX_CLIP_PLANES)
+               {
+                       // this shouldn't really happen
+                       self.move_velocity = '0 0 0';
+                       blocked = 3;
+                       break;
+               }
+
+               planes[numplanes] = trace_plane_normal;
+               numplanes++;
+
+               // modify original_velocity so it parallels all of the clip planes
+               for (i = 0;i < numplanes;i++)
+               {
+                       new_velocity = _Movetype_ClipVelocity(original_velocity, planes[i], 1);
+                       for (j = 0;j < numplanes;j++)
+                       {
+                               if(j != i)
+                               {
+                                       // not ok
+                                       if((new_velocity * planes[j]) < 0)
+                                               break;
+                               }
+                       }
+                       if(j == numplanes)
+                               break;
+               }
+
+               if(i != numplanes)
+               {
+                       // go along this plane
+                       self.move_velocity = new_velocity;
+               }
+               else
+               {
+                       // go along the crease
+                       if(numplanes != 2)
+                       {
+                               self.move_velocity = '0 0 0';
+                               blocked = 7;
+                               break;
+                       }
+                       vector dir;
+                       dir.x = planes[0].y * planes[1].z - planes[0].z * planes[1].y;
+                       dir.y = planes[0].z * planes[1].x - planes[0].x * planes[1].z;
+                       dir.z = planes[0].x * planes[1].y - planes[0].y * planes[1].x;
+                       // LordHavoc: thanks to taniwha of QuakeForge for pointing out this fix for slowed falling in corners
+                       float ilength = sqrt((dir * dir));
+                       if(ilength)
+                               ilength = 1.0 / ilength;
+                       dir.x *= ilength;
+                       dir.y *= ilength;
+                       dir.z *= ilength;
+                       float d = (dir * self.move_velocity);
+                       self.move_velocity = dir * d;
+               }
+
+               // if current velocity is against the original velocity,
+               // stop dead to avoid tiny occilations in sloping corners
+               if((self.move_velocity * primal_velocity) <= 0)
+               {
+                       self.move_velocity = '0 0 0';
+                       break;
+               }
+       }
+
+       // LordHavoc: this came from QW and allows you to get out of water more easily
+       if(GAMEPLAYFIX_EASIERWATERJUMP && (self.move_flags & FL_WATERJUMP) && !(blocked & 8))
+               self.move_velocity = primal_velocity;
+
+       if(applygravity)
+       {
+               if(!GAMEPLAYFIX_NOGRAVITYONGROUND || !(self.move_flags & FL_ONGROUND))
+               {
+                       if(GRAVITY_UNAFFECTED_BY_TICRATE)
+                               self.move_velocity_z -= grav * 0.5f;
+               }
+       }
+
+       return blocked;
+}
+
+void _Movetype_CheckVelocity()  // SV_CheckVelocity
+{
+       // if(vlen(self.move_velocity) < 0.0001)
+       // self.move_velocity = '0 0 0';
+}
+
+bool _Movetype_CheckWater(entity ent)  // SV_CheckWater
+{
+       vector point = ent.move_origin;
+       point.z += (ent.mins.z + 1);
+
+       int nativecontents = pointcontents(point);
+       if(ent.move_watertype && ent.move_watertype != nativecontents)
+       {
+               // dprintf("_Movetype_CheckWater(): Original: '%d', New: '%d'\n", ent.move_watertype, nativecontents);
+               if(ent.contentstransition)
+                       ent.contentstransition(ent.move_watertype, nativecontents);
+       }
+
+       ent.move_waterlevel = 0;
+       ent.move_watertype = CONTENT_EMPTY;
+
+       int supercontents = Mod_Q1BSP_SuperContentsFromNativeContents(nativecontents);
+       if(supercontents & DPCONTENTS_LIQUIDSMASK)
+       {
+               ent.move_watertype = nativecontents;
+               ent.move_waterlevel = 1;
+               point.y = (ent.origin.y + ((ent.mins.z + ent.maxs.y) * 0.5));
+               if(Mod_Q1BSP_SuperContentsFromNativeContents(pointcontents(point)) & DPCONTENTS_LIQUIDSMASK)
+               {
+                       ent.move_waterlevel = 2;
+                       point.y = ent.origin.y + ent.view_ofs.y;
+                       if(Mod_Q1BSP_SuperContentsFromNativeContents(pointcontents(point)) & DPCONTENTS_LIQUIDSMASK)
+                               ent.move_waterlevel = 3;
+               }
+       }
+
+       return ent.move_waterlevel > 1;
+}
+
+void _Movetype_CheckWaterTransition(entity ent)  // SV_CheckWaterTransition
+{
+       int contents = pointcontents(ent.move_origin);
+
+       if(!ent.move_watertype)
+       {
+               // just spawned here
+               if(!autocvar_cl_gameplayfix_fixedcheckwatertransition)
+               {
+                       ent.move_watertype = contents;
+                       ent.move_waterlevel = 1;
+                       return;
+               }
+       }
+       else if(ent.move_watertype != contents)
+       {
+               // dprintf("_Movetype_CheckWaterTransition(): Origin: %s, Direct: '%d', Original: '%d', New: '%d'\n", vtos(ent.move_origin), pointcontents(ent.move_origin), ent.move_watertype, contents);
+               if(ent.contentstransition)
+                       ent.contentstransition(ent.move_watertype, contents);
+       }
+
+       if(contents <= CONTENT_WATER)
+       {
+               ent.move_watertype = contents;
+               ent.move_waterlevel = 1;
+       }
+       else
+       {
+               ent.move_watertype = CONTENT_EMPTY;
+               ent.move_waterlevel = (autocvar_cl_gameplayfix_fixedcheckwatertransition ? 0 : contents);
+       }
+}
+
+void _Movetype_Impact(entity oth)  // SV_Impact
+{
+       entity oldself = self;
+       entity oldother = other;
+
+       if(self.move_touch)
+       {
+               other = oth;
+
+               self.move_touch();
+
+               other = oldother;
+       }
+
+       if(oth.move_touch)
+       {
+               other = self;
+               self = oth;
+
+               self.move_touch();
+
+               self = oldself;
+               other = oldother;
+       }
+}
+
+void _Movetype_LinkEdict_TouchAreaGrid()  // SV_LinkEdict_TouchAreaGrid
+{
+       entity oldself = self;
+       entity oldother = other;
+
+       for (entity e = findradius(0.5 * (self.absmin + self.absmax), 0.5 * vlen(self.absmax - self.absmin)); e; e = e.chain)
+       {
+               if(e.move_touch && boxesoverlap(e.absmin, e.absmax, oldself.absmin, oldself.absmax))
+               {
+                       self = e;
+                       other = oldself;
+
+                       trace_allsolid = false;
+                       trace_startsolid = false;
+                       trace_fraction = 1;
+                       trace_inwater = false;
+                       trace_inopen = true;
+                       trace_endpos = e.origin;
+                       trace_plane_normal = '0 0 1';
+                       trace_plane_dist = 0;
+                       trace_ent = oldself;
+
+                       e.move_touch();
+               }
+       }
+
+       other = oldother;
+       self = oldself;
+}
+
+void _Movetype_LinkEdict(bool touch_triggers)  // SV_LinkEdict
+{
+       vector mi, ma;
+       if(self.solid == SOLID_BSP)
+       {
+               // TODO set the absolute bbox
+               mi = self.mins;
+               ma = self.maxs;
+       }
+       else
+       {
+               mi = self.mins;
+               ma = self.maxs;
+       }
+       mi += self.move_origin;
+       ma += self.move_origin;
+
+       if(self.move_flags & FL_ITEM)
+       {
+               mi.x -= 15;
+               mi.y -= 15;
+               mi.z -= 1;
+               ma.x += 15;
+               ma.y += 15;
+               ma.z += 1;
+       }
+       else
+       {
+               mi.x -= 1;
+               mi.y -= 1;
+               mi.z -= 1;
+               ma.x += 1;
+               ma.y += 1;
+               ma.z += 1;
+       }
+
+       self.absmin = mi;
+       self.absmax = ma;
+
+       if(touch_triggers)
+               _Movetype_LinkEdict_TouchAreaGrid();
+}
+
+bool _Movetype_TestEntityPosition(vector ofs)  // SV_TestEntityPosition
+{
+//     vector org = self.move_origin + ofs;
+
+       int cont = self.dphitcontentsmask;
+       self.dphitcontentsmask = DPCONTENTS_SOLID;
+       tracebox(self.move_origin, self.mins, self.maxs, self.move_origin, MOVE_NOMONSTERS, self);
+       self.dphitcontentsmask = cont;
+
+       if(trace_startsolid)
+               return true;
+
+       if(vlen(trace_endpos - self.move_origin) > 0.0001)
+               self.move_origin = trace_endpos;
+       return false;
+}
+
+bool _Movetype_UnstickEntity()  // SV_UnstickEntity
+{
+       if(!_Movetype_TestEntityPosition('0 0 0')) return true;
+       if(!_Movetype_TestEntityPosition('-1 0 0')) goto success;
+       if(!_Movetype_TestEntityPosition('1 0 0')) goto success;
+       if(!_Movetype_TestEntityPosition('0 -1 0')) goto success;
+       if(!_Movetype_TestEntityPosition('0 1 0')) goto success;
+       if(!_Movetype_TestEntityPosition('-1 -1 0')) goto success;
+       if(!_Movetype_TestEntityPosition('1 -1 0')) goto success;
+       if(!_Movetype_TestEntityPosition('-1 1 0')) goto success;
+       if(!_Movetype_TestEntityPosition('1 1 0')) goto success;
+       for (int i = 1; i <= 17; ++i)
+       {
+               if(!_Movetype_TestEntityPosition('0 0 -1' * i)) goto success;
+               if(!_Movetype_TestEntityPosition('0 0 1' * i)) goto success;
+       }
+       dprintf("Can't unstick an entity (edict: %d, classname: %s, origin: %s)\n",
+               num_for_edict(self), self.classname, vtos(self.move_origin));
+       return false;
+       : success;
+       dprintf("Sucessfully unstuck an entity (edict: %d, classname: %s, origin: %s)\n",
+               num_for_edict(self), self.classname, vtos(self.move_origin));
+       _Movetype_LinkEdict(true);
+       return true;
+}
+
+vector _Movetype_ClipVelocity(vector vel, vector norm, float f)  // SV_ClipVelocity
+{
+       vel -= ((vel * norm) * norm) * f;
+
+       if(vel.x > -0.1 && vel.x < 0.1) vel.x = 0;
+       if(vel.y > -0.1 && vel.y < 0.1) vel.y = 0;
+       if(vel.z > -0.1 && vel.z < 0.1) vel.z = 0;
+
+       return vel;
+}
+
+void _Movetype_PushEntityTrace(vector push)
+{
+       vector end = self.move_origin + push;
+       int type;
+       if(self.move_nomonsters)
+               type = max(0, self.move_nomonsters);
+       else if(self.move_movetype == MOVETYPE_FLYMISSILE)
+               type = MOVE_MISSILE;
+       else if(self.solid == SOLID_TRIGGER || self.solid == SOLID_NOT)
+               type = MOVE_NOMONSTERS;
+       else
+               type = MOVE_NORMAL;
+
+       tracebox(self.move_origin, self.mins, self.maxs, end, type, self);
+}
+
+float _Movetype_PushEntity(vector push, bool failonstartsolid)  // SV_PushEntity
+{
+       _Movetype_PushEntityTrace(push);
+
+       if(trace_startsolid && failonstartsolid)
+               return trace_fraction;
+
+       self.move_origin = trace_endpos;
+
+       if(trace_fraction < 1)
+               if(self.solid >= SOLID_TRIGGER && (!(self.move_flags & FL_ONGROUND) || (self.move_groundentity != trace_ent)))
+                       _Movetype_Impact(trace_ent);
+
+       return trace_fraction;
+}
+
+
+.float ltime;
+.void() blocked;
+// matrix version of makevectors, sets v_forward, v_right and v_up
+void makevectors_matrix(vector myangles)  // AngleVectorsFLU
+{
+       v_forward = v_right = v_up = '0 0 0';
+
+       float y = myangles.y * (M_PI * 2 / 360);
+       float sy = sin(y);
+       float cy = cos(y);
+       float p = myangles.x * (M_PI * 2 / 360);
+       float sp = sin(p);
+       float cp = cos(p);
+       if(v_forward)
+       {
+               v_forward.x = cp * cy;
+               v_forward.y = cp * sy;
+               v_forward.z = -sp;
+       }
+       if(v_right || v_up)
+       {
+               if(myangles.z)
+               {
+                       float r = myangles.z * (M_PI * 2 / 360);
+                       float sr = sin(r);
+                       float cr = cos(r);
+                       if(v_right)
+                       {
+                               v_right.x = sr * sp * cy + cr * -sy;
+                               v_right.y = sr * sp * sy + cr * cy;
+                               v_right.z = sr * cp;
+                       }
+                       if(v_up)
+                       {
+                               v_up.x = cr * sp * cy + -sr * -sy;
+                               v_up.y = cr * sp * sy + -sr * cy;
+                               v_up.z = cr * cp;
+                       }
+               }
+               else
+               {
+                       if(v_right)
+                       {
+                               v_right.x = -sy;
+                               v_right.y = cy;
+                               v_right.z = 0;
+                       }
+                       if(v_up)
+                       {
+                               v_up.x = sp * cy;
+                               v_up.y = sp * sy;
+                               v_up.z = cp;
+                       }
+               }
+       }
+}
+
+void _Movetype_Physics_Frame(float movedt)
+{
+       self.move_didgravity = -1;
+       switch (self.move_movetype)
+       {
+               case MOVETYPE_PUSH:
+               case MOVETYPE_FAKEPUSH:
+                       _Movetype_Physics_Pusher(movedt);
+                       break;
+               case MOVETYPE_NONE:
+                       break;
+               case MOVETYPE_FOLLOW:
+                       _Movetype_Physics_Follow();
+                       break;
+               case MOVETYPE_NOCLIP:
+                       _Movetype_CheckWater(self);
+                       self.move_origin = self.move_origin + TICRATE * self.move_velocity;
+                       self.move_angles = self.move_angles + TICRATE * self.move_avelocity;
+                       _Movetype_LinkEdict(false);
+                       break;
+               case MOVETYPE_STEP:
+                       _Movetype_Physics_Step(movedt);
+                       break;
+               case MOVETYPE_WALK:
+                       _Movetype_Physics_Walk(movedt);
+                       break;
+               case MOVETYPE_TOSS:
+               case MOVETYPE_BOUNCE:
+               case MOVETYPE_BOUNCEMISSILE:
+               case MOVETYPE_FLYMISSILE:
+               case MOVETYPE_FLY:
+                       _Movetype_Physics_Toss(movedt);
+                       break;
+       }
+}
+
+void Movetype_Physics_NoMatchServer()  // optimized
+{
+       float movedt = time - self.move_time;
+       self.move_time = time;
+
+       _Movetype_Physics_Frame(movedt);
+       if(wasfreed(self))
+               return;
+
+       self.avelocity = self.move_avelocity;
+       self.velocity = self.move_velocity;
+       self.angles = self.move_angles;
+       setorigin(self, self.move_origin);
+}
+
+void Movetype_Physics_MatchServer(bool sloppy)
+{
+       Movetype_Physics_MatchTicrate(TICRATE, sloppy);
+}
+
+void Movetype_Physics_MatchTicrate(float tr, bool sloppy)  // SV_Physics_Entity
+{
+       if(tr <= 0)
+       {
+               Movetype_Physics_NoMatchServer();
+               return;
+       }
+
+       float dt = time - self.move_time;
+
+       int n = max(0, floor(dt / tr));
+       dt -= n * tr;
+       self.move_time += n * tr;
+
+       if(!self.move_didgravity)
+               self.move_didgravity = ((self.move_movetype == MOVETYPE_BOUNCE || self.move_movetype == MOVETYPE_TOSS) && !(self.move_flags & FL_ONGROUND));
+
+       for (int i = 0; i < n; ++i)
+       {
+               _Movetype_Physics_Frame(tr);
+               if(wasfreed(self))
+                       return;
+       }
+
+       self.avelocity = self.move_avelocity;
+
+       if(dt > 0 && self.move_movetype != MOVETYPE_NONE && !(self.move_flags & FL_ONGROUND))
+       {
+               // now continue the move from move_time to time
+               self.velocity = self.move_velocity;
+
+               if(self.move_didgravity > 0)
+               {
+                       self.velocity_z -= (GRAVITY_UNAFFECTED_BY_TICRATE ? 0.5 : 1)
+                           * dt
+                           * (self.gravity ? self.gravity : 1)
+                           * PHYS_GRAVITY;
+               }
+
+               self.angles = self.move_angles + dt * self.avelocity;
+
+               if(sloppy || self.move_movetype == MOVETYPE_NOCLIP)
+               {
+                       setorigin(self, self.move_origin + dt * self.velocity);
+               }
+               else
+               {
+                       _Movetype_PushEntityTrace(dt * self.velocity);
+                       if(!trace_startsolid)
+                               setorigin(self, trace_endpos);
+               }
+
+               if(self.move_didgravity > 0 && GRAVITY_UNAFFECTED_BY_TICRATE)
+                       self.velocity_z -= 0.5 * dt * (self.gravity ? self.gravity : 1) * PHYS_GRAVITY;
+       }
+       else
+       {
+               self.velocity = self.move_velocity;
+               self.angles = self.move_angles;
+               setorigin(self, self.move_origin);
+       }
+}
diff --git a/qcsrc/common/movetypes/movetypes.qh b/qcsrc/common/movetypes/movetypes.qh
new file mode 100644 (file)
index 0000000..67cd51e
--- /dev/null
@@ -0,0 +1,103 @@
+#ifndef MOVETYPES_H
+#define MOVETYPES_H
+
+.float move_ltime;
+.void(void)move_think;
+.float move_nextthink;
+.void(void)move_blocked;
+
+.float move_movetype;
+.float move_time;
+.vector move_origin;
+.vector move_angles;
+.vector move_velocity;
+.vector move_avelocity;
+.int move_flags;
+.int move_watertype;
+.int move_waterlevel;
+.void(void)move_touch;
+.void(float, float)contentstransition;
+.float move_bounce_factor;
+.float move_bounce_stopspeed;
+.float move_nomonsters;  // -1 for MOVE_NORMAL, otherwise a MOVE_ constant
+
+.entity move_aiment;
+.vector move_punchangle;
+
+// should match sv_gameplayfix_fixedcheckwatertransition
+float autocvar_cl_gameplayfix_fixedcheckwatertransition = 1;
+
+#ifdef SVQC
+.int stat_gameplayfix_upvelocityclearsonground;
+
+#define GRAVITY_UNAFFECTED_BY_TICRATE autocvar_sv_gameplayfix_gravityunaffectedbyticrate
+#define UPWARD_VELOCITY_CLEARS_ONGROUND autocvar_sv_gameplayfix_upwardvelocityclearsongroundflag
+
+#define TICRATE sys_frametime
+#elif defined(CSQC)
+#define GRAVITY_UNAFFECTED_BY_TICRATE (getstati(STAT_MOVEFLAGS) & MOVEFLAG_GRAVITYUNAFFECTEDBYTICRATE)
+#define UPWARD_VELOCITY_CLEARS_ONGROUND getstati(STAT_GAMEPLAYFIX_UPVELOCITYCLEARSONGROUND)
+
+#define TICRATE ticrate
+#endif
+
+.entity move_groundentity;  // FIXME add move_groundnetworkentity?
+.float move_suspendedinair;
+.float move_didgravity;
+
+void _Movetype_WallFriction(vector stepnormal);
+int _Movetype_FlyMove(float dt, bool applygravity, vector stepnormal, float stepheight);
+void _Movetype_CheckVelocity();
+void _Movetype_CheckWaterTransition(entity ent);
+float _Movetype_CheckWater(entity ent);
+void _Movetype_LinkEdict_TouchAreaGrid();
+void _Movetype_LinkEdict(float touch_triggers);
+float _Movetype_TestEntityPosition(vector ofs);
+float _Movetype_UnstickEntity();
+vector _Movetype_ClipVelocity(vector vel, vector norm, float f);
+void _Movetype_PushEntityTrace(vector push);
+float _Movetype_PushEntity(vector push, float failonstartsolid);
+void makevectors_matrix(vector myangles);
+
+void Movetype_Physics_MatchTicrate(float tr, bool sloppy);
+void Movetype_Physics_MatchServer(bool sloppy);
+void Movetype_Physics_NoMatchServer();
+void _Movetype_LinkEdict(float touch_triggers);
+void _Movetype_LinkEdict_TouchAreaGrid();
+
+float _Movetype_UnstickEntity();
+
+const int MAX_CLIP_PLANES = 5;
+
+#ifdef CSQC
+const int MOVETYPE_NONE             = 0;
+const int MOVETYPE_ANGLENOCLIP      = 1;
+const int MOVETYPE_ANGLECLIP        = 2;
+const int MOVETYPE_WALK             = 3;
+const int MOVETYPE_STEP             = 4;
+const int MOVETYPE_FLY              = 5;
+const int MOVETYPE_TOSS             = 6;
+const int MOVETYPE_PUSH             = 7;
+const int MOVETYPE_NOCLIP           = 8;
+const int MOVETYPE_FLYMISSILE       = 9;
+const int MOVETYPE_BOUNCE           = 10;
+const int MOVETYPE_BOUNCEMISSILE    = 11;  // Like bounce but doesn't lose speed on bouncing
+const int MOVETYPE_FOLLOW           = 12;
+const int MOVETYPE_FLY_WORLDONLY    = 33;
+
+const int FL_ITEM                   = 256;
+const int FL_ONGROUND                          = 512;
+#endif
+
+const int MOVETYPE_FAKEPUSH         = 13;
+
+const float MOVEFLAG_Q2AIRACCELERATE            = 1;
+const float MOVEFLAG_NOGRAVITYONGROUND          = 2;
+const float MOVEFLAG_GRAVITYUNAFFECTEDBYTICRATE = 4;
+
+#ifdef CSQC
+// TODO: figure out server's version of this
+#define moveflags (getstati(STAT_MOVEFLAGS))
+#endif
+
+#endif
diff --git a/qcsrc/common/movetypes/push.qc b/qcsrc/common/movetypes/push.qc
new file mode 100644 (file)
index 0000000..1f563f5
--- /dev/null
@@ -0,0 +1,161 @@
+void _Movetype_PushMove(float dt)  // SV_PushMove
+{
+       if (self.move_velocity == '0 0 0' && self.move_avelocity == '0 0 0')
+       {
+               self.move_ltime += dt;
+               return;
+       }
+
+       switch (self.solid)
+       {
+               // LordHavoc: valid pusher types
+               case SOLID_BSP:
+               case SOLID_BBOX:
+               case SOLID_SLIDEBOX:
+               case SOLID_CORPSE:  // LordHavoc: this would be weird...
+                       break;
+               // LordHavoc: no collisions
+               case SOLID_NOT:
+               case SOLID_TRIGGER:
+                       self.move_origin = self.move_origin + dt * self.move_velocity;
+                       self.move_angles = self.move_angles + dt * self.move_avelocity;
+                       self.move_angles_x -= 360.0 * floor(self.move_angles.x * (1.0 / 360.0));
+                       self.move_angles_y -= 360.0 * floor(self.move_angles.y * (1.0 / 360.0));
+                       self.move_angles_z -= 360.0 * floor(self.move_angles.z * (1.0 / 360.0));
+                       self.move_ltime += dt;
+                       _Movetype_LinkEdict(true);
+                       return;
+               default:
+                       dprintf("_Movetype_PushMove: entity %e, unrecognized solid type %d\n", self, self.solid);
+                       return;
+       }
+
+       bool rotated = (self.move_angles * self.move_angles) + (self.move_avelocity * self.move_avelocity) > 0;
+
+       vector move1 = self.move_velocity * dt;
+       vector moveangle = self.move_avelocity * dt;
+
+       makevectors_matrix(-moveangle);
+
+//     vector pushorig = self.move_origin;
+//     vector pushang = self.move_angles;
+//     float pushltime = self.move_ltime;
+
+// move the pusher to its final position
+
+       self.move_origin = self.move_origin + dt * self.move_velocity;
+       self.move_angles = self.move_angles + dt * self.move_avelocity;
+
+       self.move_ltime += dt;
+       _Movetype_LinkEdict(true);
+
+       int savesolid = self.solid;
+
+       if (self.move_movetype != MOVETYPE_FAKEPUSH)
+       {
+               for (entity check = findradius(0.5 * (self.absmin + self.absmax), 0.5 * vlen(self.absmax - self.absmin)); check; check = check.chain)
+               {
+                       switch (check.move_movetype)
+                       {
+                               case MOVETYPE_NONE:
+                               case MOVETYPE_PUSH:
+                               case MOVETYPE_FOLLOW:
+                               case MOVETYPE_NOCLIP:
+                               case MOVETYPE_FLY_WORLDONLY:
+                                       continue;
+                               default:
+                                       break;
+                       }
+
+                       if (check.owner == self)
+                               continue;
+
+                       if (self.owner == check)
+                               continue;
+
+                       vector pivot = check.mins + 0.5 * (check.maxs - check.mins);
+                       vector move;
+                       if (rotated)
+                       {
+                               vector org = (check.move_origin - self.move_origin) + pivot;
+                               vector org2;
+                               org2.x = org * v_forward;
+                               org2.y = org * v_right;
+                               org2.z = org * v_up;
+                               move = (org2 - org) + move1;
+                       }
+                       else
+                       {
+                               move = move1;
+                       }
+
+                       // physics objects need better collisions than this code can do
+                       if (check.move_movetype == 32)  // MOVETYPE_PHYSICS
+                       {
+                               check.move_origin = check.move_origin + move;
+                               entity oldself = self;
+                               self = check;
+                               _Movetype_LinkEdict(true);
+                               self = oldself;
+                               continue;
+                       }
+
+                       // try moving the contacted entity
+                       self.solid = SOLID_NOT;
+                       entity oldself = self;
+                       self = check;
+                       if (!_Movetype_PushEntity(move, true))
+                       {
+                               self = oldself;
+                               // entity "check" got teleported
+                               check.move_angles_y += trace_fraction * moveangle.y;
+                               self.solid = savesolid;
+                               continue;  // pushed enough
+                       }
+                       self = oldself;
+                       // FIXME: turn players specially
+                       check.move_angles_y += trace_fraction * moveangle.y;
+                       self.solid = savesolid;
+
+                       // this trace.fraction < 1 check causes items to fall off of pushers
+                       // if they pass under or through a wall
+                       // the groundentity check causes items to fall off of ledges
+                       if (check.move_movetype != MOVETYPE_WALK && (trace_fraction < 1 || check.move_groundentity != self))
+                               check.move_flags &= ~FL_ONGROUND;
+               }
+       }
+
+       self.move_angles_x -= 360.0 * floor(self.move_angles.x * (1.0 / 360.0));
+       self.move_angles_y -= 360.0 * floor(self.move_angles.y * (1.0 / 360.0));
+       self.move_angles_z -= 360.0 * floor(self.move_angles.z * (1.0 / 360.0));
+}
+
+void _Movetype_Physics_Pusher(float dt)  // SV_Physics_Pusher
+{
+       float oldltime = self.move_ltime;
+       float thinktime = self.move_nextthink;
+       float movetime;
+       if (thinktime < self.move_ltime + dt)
+       {
+               movetime = thinktime - self.move_ltime;
+               if (movetime < 0)
+                       movetime = 0;
+       }
+       else
+       {
+               movetime = dt;
+       }
+
+       if (movetime)
+               // advances self.move_ltime if not blocked
+               _Movetype_PushMove(movetime);
+
+       if (thinktime > oldltime && thinktime <= self.move_ltime)
+       {
+               self.move_nextthink = 0;
+               self.move_time = time;
+               other = world;
+               if (self.move_think)
+                       self.move_think();
+       }
+}
diff --git a/qcsrc/common/movetypes/push.qh b/qcsrc/common/movetypes/push.qh
new file mode 100644 (file)
index 0000000..44d5442
--- /dev/null
@@ -0,0 +1,6 @@
+#ifndef MOVETYPE_PUSH_H
+#define MOVETYPE_PUSH_H
+
+void _Movetype_Physics_Pusher(float dt);
+
+#endif
\ No newline at end of file
diff --git a/qcsrc/common/movetypes/step.qc b/qcsrc/common/movetypes/step.qc
new file mode 100644 (file)
index 0000000..822bf09
--- /dev/null
@@ -0,0 +1,23 @@
+void _Movetype_Physics_Step(float dt) // SV_Physics_Step
+{
+       if(self.move_flags & FL_ONGROUND)
+       {
+               if(self.velocity_z >= (1.0 / 32.0) && UPWARD_VELOCITY_CLEARS_ONGROUND)
+               {
+                       self.move_flags &= ~FL_ONGROUND;
+                       _Movetype_CheckVelocity();
+                       _Movetype_FlyMove(dt, true, '0 0 0', 0);
+                       _Movetype_LinkEdict(true);
+               }
+       }
+       else
+       {
+               _Movetype_CheckVelocity();
+               _Movetype_FlyMove(dt, true, '0 0 0', 0);
+               _Movetype_LinkEdict(true);
+
+               // TODO? movetypesteplandevent
+       }
+
+       _Movetype_CheckWaterTransition(self);
+}
diff --git a/qcsrc/common/movetypes/toss.qc b/qcsrc/common/movetypes/toss.qc
new file mode 100644 (file)
index 0000000..6ed0407
--- /dev/null
@@ -0,0 +1,115 @@
+#include "../physics.qh"
+
+void _Movetype_Physics_Toss(float dt)  // SV_Physics_Toss
+{
+       if (self.move_flags & FL_ONGROUND)
+       {
+               if (self.move_velocity.z >= 1 / 32)
+               {
+                       self.move_flags &= ~FL_ONGROUND;
+               }
+               else if (!self.move_groundentity)
+               {
+                       return;
+               }
+               else if (self.move_suspendedinair && wasfreed(self.move_groundentity))
+               {
+                       self.move_groundentity = world;
+                       return;
+               }
+       }
+
+       self.move_suspendedinair = false;
+
+       _Movetype_CheckVelocity();
+
+       if (self.move_movetype == MOVETYPE_BOUNCE || self.move_movetype == MOVETYPE_TOSS)
+       {
+               self.move_didgravity = 1;
+               self.move_velocity_z -= (GRAVITY_UNAFFECTED_BY_TICRATE ? 0.5 : 1)
+                   * dt
+                   * (self.gravity ? self.gravity : 1)
+                   * PHYS_GRAVITY;
+       }
+
+       self.move_angles = self.move_angles + self.move_avelocity * dt;
+
+       float movetime = dt;
+       for (int bump = 0; bump < MAX_CLIP_PLANES && movetime > 0; ++bump)
+       {
+               vector move = self.move_velocity * movetime;
+               _Movetype_PushEntity(move, true);
+               if (wasfreed(self))
+                       return;
+
+               if (trace_startsolid)
+               {
+                       _Movetype_UnstickEntity();
+                       _Movetype_PushEntity(move, false);
+                       if (wasfreed(self))
+                               return;
+               }
+
+               if (trace_fraction == 1)
+                       break;
+
+               movetime *= 1 - min(1, trace_fraction);
+
+               if (self.move_movetype == MOVETYPE_BOUNCEMISSILE)
+               {
+                       self.move_velocity = _Movetype_ClipVelocity(self.move_velocity, trace_plane_normal, 2.0);
+                       self.move_flags &= ~FL_ONGROUND;
+               }
+               else if (self.move_movetype == MOVETYPE_BOUNCE)
+               {
+                       float bouncefac = self.move_bounce_factor;     if (!bouncefac)  bouncefac = 0.5;
+                       float bouncestop = self.move_bounce_stopspeed; if (!bouncestop) bouncestop = 60 / 800;
+                       bouncestop *= (self.gravity ? self.gravity : 1) * PHYS_GRAVITY;
+
+                       self.move_velocity = _Movetype_ClipVelocity(self.move_velocity, trace_plane_normal, 1 + bouncefac);
+
+                       float d = trace_plane_normal * self.move_velocity;
+                       if (trace_plane_normal.z > 0.7 && d < bouncestop && d > -bouncestop)
+                       {
+                               self.move_flags |= FL_ONGROUND;
+                               self.move_groundentity = trace_ent;
+                               self.move_velocity = '0 0 0';
+                               self.move_avelocity = '0 0 0';
+                       }
+                       else
+                       {
+                               self.move_flags &= ~FL_ONGROUND;
+                       }
+               }
+               else
+               {
+                       self.move_velocity = _Movetype_ClipVelocity(self.move_velocity, trace_plane_normal, 1.0);
+                       if (trace_plane_normal.z > 0.7)
+                       {
+                               self.move_flags |= FL_ONGROUND;
+                               self.move_groundentity = trace_ent;
+                               if (trace_ent.solid == SOLID_BSP)
+                                       self.move_suspendedinair = true;
+                               self.move_velocity = '0 0 0';
+                               self.move_avelocity = '0 0 0';
+                       }
+                       else
+                       {
+                               self.move_flags &= ~FL_ONGROUND;
+                       }
+               }
+
+               // DP revision 8905 (just, WHY...)
+               if (self.move_movetype == MOVETYPE_BOUNCEMISSILE)
+                       break;
+
+               // DP revision 8918 (WHY...)
+               if (self.move_flags & FL_ONGROUND)
+                       break;
+       }
+
+       if (GRAVITY_UNAFFECTED_BY_TICRATE && self.move_didgravity > 0 && !(self.move_flags & FL_ONGROUND))
+               self.move_velocity_z -= 0.5 * dt * (self.gravity ? self.gravity : 1) * PHYS_GRAVITY;
+
+       _Movetype_CheckWaterTransition(self);
+}
diff --git a/qcsrc/common/movetypes/toss.qh b/qcsrc/common/movetypes/toss.qh
new file mode 100644 (file)
index 0000000..9e11595
--- /dev/null
@@ -0,0 +1,6 @@
+#ifndef MOVETYPE_TOSS_H
+#define MOVETYPE_TOSS_H
+
+void _Movetype_Physics_Toss(float dt);
+
+#endif
\ No newline at end of file
diff --git a/qcsrc/common/movetypes/walk.qc b/qcsrc/common/movetypes/walk.qc
new file mode 100644 (file)
index 0000000..180dfcc
--- /dev/null
@@ -0,0 +1,169 @@
+void _Movetype_Physics_Walk(float dt)  // SV_WalkMove
+{
+       vector stepnormal = '0 0 0';
+
+       // if frametime is 0 (due to client sending the same timestamp twice), don't move
+       if (dt <= 0)
+               return;
+
+       if (GAMEPLAYFIX_UNSTICKPLAYERS)
+               _Movetype_UnstickEntity();
+
+       bool applygravity = (!_Movetype_CheckWater(self) && self.move_movetype == MOVETYPE_WALK && !(self.move_flags & FL_WATERJUMP));
+
+       _Movetype_CheckVelocity();
+
+       // do a regular slide move unless it looks like you ran into a step
+       bool oldonground = (self.move_flags & FL_ONGROUND);
+
+       vector start_origin = self.move_origin;
+       vector start_velocity = self.move_velocity;
+
+       int clip = _Movetype_FlyMove(dt, applygravity, stepnormal, GAMEPLAYFIX_STEPMULTIPLETIMES ? PHYS_STEPHEIGHT : 0);
+
+       if (GAMEPLAYFIX_DOWNTRACEONGROUND && !(clip & 1))
+       {
+               // only try this if there was no floor in the way in the trace (no,
+               // this check seems to be not REALLY necessary, because if clip & 1,
+               // our trace will hit that thing too)
+               vector upmove = self.move_origin + '0 0 1';
+               vector downmove = self.move_origin - '0 0 1';
+               int type;
+               if (self.move_movetype == MOVETYPE_FLYMISSILE)
+                       type = MOVE_MISSILE;
+               else if (self.move_movetype == MOVETYPE_FLY_WORLDONLY)
+                       type = MOVE_WORLDONLY;
+               else if (self.solid == SOLID_TRIGGER || self.solid == SOLID_NOT)
+                       type = MOVE_NOMONSTERS;
+               else type = MOVE_NORMAL;
+               tracebox(upmove, self.mins, self.maxs, downmove, type, self);
+               if (trace_fraction < 1 && trace_plane_normal.z > 0.7)
+                       clip |= 1;  // but we HAVE found a floor
+       }
+
+       // if the move did not hit the ground at any point, we're not on ground
+       if (!(clip & 1))
+               self.move_flags &= ~FL_ONGROUND;
+
+       _Movetype_CheckVelocity();
+       _Movetype_LinkEdict(true);
+
+       if (clip & 8)  // teleport
+               return;
+
+       if (self.move_flags & FL_WATERJUMP)
+               return;
+
+       if (PHYS_NOSTEP)
+               return;
+
+       vector originalmove_origin = self.move_origin;
+       vector originalmove_velocity = self.move_velocity;
+       // originalmove_clip = clip;
+       int originalmove_flags = self.move_flags;
+       entity originalmove_groundentity = self.move_groundentity;
+
+       // if move didn't block on a step, return
+       if (clip & 2)
+       {
+               // if move was not trying to move into the step, return
+               if (fabs(start_velocity.x) < 0.03125 && fabs(start_velocity.y) < 0.03125)
+                       return;
+
+               if (self.move_movetype != MOVETYPE_FLY)
+               {
+                       // return if gibbed by a trigger
+                       if (self.move_movetype != MOVETYPE_WALK)
+                               return;
+
+                       // return if attempting to jump while airborn (unless sv_jumpstep)
+                       if (!PHYS_JUMPSTEP)
+                               if (!oldonground && self.move_waterlevel == 0)
+                                       return;
+               }
+
+               // try moving up and forward to go up a step
+               // back to start pos
+               self.move_origin = start_origin;
+               self.move_velocity = start_velocity;
+
+               // move up
+               vector upmove = '0 0 1' * PHYS_STEPHEIGHT;
+               if (!_Movetype_PushEntity(upmove, true))
+               {
+                       // we got teleported when upstepping... must abort the move
+                       return;
+               }
+
+               // move forward
+               self.move_velocity_z = 0;
+               clip = _Movetype_FlyMove(dt, applygravity, stepnormal, 0);
+               self.move_velocity_z += start_velocity.z;
+               if (clip & 8)
+               {
+                       // we got teleported when upstepping... must abort the move
+                       // note that z velocity handling may not be what QC expects here, but we cannot help it
+                       return;
+               }
+
+               _Movetype_CheckVelocity();
+               _Movetype_LinkEdict(true);
+
+               // check for stuckness, possibly due to the limited precision of floats
+               // in the clipping hulls
+               if (clip
+                   && fabs(originalmove_origin.y - self.move_origin.y) < 0.03125
+                   && fabs(originalmove_origin.x - self.move_origin.x) < 0.03125)
+               {
+                       // Con_Printf("wall\n");
+                       // stepping up didn't make any progress, revert to original move
+                       self.move_origin = originalmove_origin;
+                       self.move_velocity = originalmove_velocity;
+                       // clip = originalmove_clip;
+                       self.move_flags = originalmove_flags;
+                       self.move_groundentity = originalmove_groundentity;
+                       // now try to unstick if needed
+                       // clip = SV_TryUnstick (ent, oldvel);
+                       return;
+               }
+
+               // Con_Printf("step - ");
+
+               // extra friction based on view angle
+               if (clip & 2 && PHYS_WALLFRICTION)
+                       _Movetype_WallFriction(stepnormal);
+       }
+       // don't do the down move if stepdown is disabled, moving upward, not in water, or the move started offground or ended onground
+       else if (!GAMEPLAYFIX_STEPDOWN || self.move_waterlevel >= 3 || start_velocity.z >= (1.0 / 32.0) || !oldonground || (self.move_flags & FL_ONGROUND))
+       {
+               return;
+       }
+
+       // move down
+       vector downmove = '0 0 1' * (-PHYS_STEPHEIGHT + start_velocity.z * dt);
+       if (!_Movetype_PushEntity(downmove, true))
+       {
+               // we got teleported when downstepping... must abort the move
+               return;
+       }
+
+       if (trace_fraction < 1 && trace_plane_normal.z > 0.7)
+       {
+               // this has been disabled so that you can't jump when you are stepping
+               // up while already jumping (also known as the Quake2 double jump bug)
+       }
+       else
+       {
+               // Con_Printf("slope\n");
+               // if the push down didn't end up on good ground, use the move without
+               // the step up.  This happens near wall / slope combinations, and can
+               // cause the player to hop up higher on a slope too steep to climb
+               self.move_origin = originalmove_origin;
+               self.move_velocity = originalmove_velocity;
+               self.move_flags = originalmove_flags;
+               self.move_groundentity = originalmove_groundentity;
+       }
+
+       _Movetype_CheckVelocity();
+       _Movetype_LinkEdict(true);
+}
diff --git a/qcsrc/common/movetypes/walk.qh b/qcsrc/common/movetypes/walk.qh
new file mode 100644 (file)
index 0000000..10493c9
--- /dev/null
@@ -0,0 +1,6 @@
+#ifndef MOVETYPE_WALK_H
+#define MOVETYPE_WALK_H
+
+void _Movetype_Physics_Walk(float dt);
+
+#endif
index 4225a19f03cb76e98b5afa198312d94f2c575cfc..fcdc5533e4c1be7b9f94902074c62761b119c4d1 100644 (file)
@@ -3,7 +3,7 @@
        #include "../client/defs.qh"
        #include "nades.qh"
        #include "buffs.qh"
-       #include "../client/movetypes.qh"
+       #include "../common/movetypes/movetypes.qh"
        #include "../server/tturrets/include/turrets_early.qh"
        #include "../client/main.qh"
        #include "../csqcmodellib/cl_model.qh"
index bc520e59d57005b7547d8ddbeba0a3cd6a5b9584..fb9958b08fcbd186fd151b65afb89462d442a357 100644 (file)
@@ -47,9 +47,6 @@ void sv_notice_toall(string _notice, float _howlong, float _modal)
 #endif // SVQC
 
 #ifdef CSQC
-void SUB_Remove()
-{ remove(self); }
-
 void cl_notice_read()
 {
     entity _notice;
diff --git a/qcsrc/common/physics.qc b/qcsrc/common/physics.qc
new file mode 100644 (file)
index 0000000..60ca3b1
--- /dev/null
@@ -0,0 +1,1933 @@
+#include "physics.qh"
+#include "triggers/trigger/swamp.qh"
+#include "triggers/trigger/jumppads.qh"
+
+#ifdef SVQC
+
+#include "../server/miscfunctions.qh"
+
+// client side physics
+bool Physics_Valid(string thecvar)
+{
+       if(!autocvar_g_physics_clientselect) { return false; }
+
+       string l = strcat(" ", autocvar_g_physics_clientselect_options, " ");
+
+       if(strstrofs(l, strcat(" ", thecvar, " "), 0) >= 0)
+               return true;
+
+       return false;
+}
+
+float Physics_ClientOption(entity pl, string option)
+{
+       if(Physics_Valid(pl.cvar_cl_physics))
+       {
+               string var = sprintf("g_physics_%s_%s", pl.cvar_cl_physics, option);
+               if(cvar_type(var) & CVAR_TYPEFLAG_EXISTS)
+                       return cvar(var);
+       }
+       if(autocvar_g_physics_clientselect && autocvar_g_physics_clientselect_default)
+       {
+               string var = sprintf("g_physics_%s_%s", autocvar_g_physics_clientselect_default, option);
+               if(cvar_type(var) & CVAR_TYPEFLAG_EXISTS)
+                       return cvar(var);
+       }
+       return cvar(strcat("sv_", option));
+}
+
+void Physics_AddStats()
+{
+       // static view offset and hitbox vectors
+       // networked for all you bandwidth pigs out there
+       addstat(STAT_PL_VIEW_OFS1, AS_FLOAT, stat_pl_view_ofs_x);
+       addstat(STAT_PL_VIEW_OFS2, AS_FLOAT, stat_pl_view_ofs_y);
+       addstat(STAT_PL_VIEW_OFS3, AS_FLOAT, stat_pl_view_ofs_z);
+       addstat(STAT_PL_CROUCH_VIEW_OFS1, AS_FLOAT, stat_pl_crouch_view_ofs_x);
+       addstat(STAT_PL_CROUCH_VIEW_OFS2, AS_FLOAT, stat_pl_crouch_view_ofs_y);
+       addstat(STAT_PL_CROUCH_VIEW_OFS3, AS_FLOAT, stat_pl_crouch_view_ofs_z);
+
+       addstat(STAT_PL_MIN1, AS_FLOAT, stat_pl_min_x);
+       addstat(STAT_PL_MIN2, AS_FLOAT, stat_pl_min_y);
+       addstat(STAT_PL_MIN3, AS_FLOAT, stat_pl_min_z);
+       addstat(STAT_PL_MAX1, AS_FLOAT, stat_pl_max_x);
+       addstat(STAT_PL_MAX2, AS_FLOAT, stat_pl_max_y);
+       addstat(STAT_PL_MAX3, AS_FLOAT, stat_pl_max_z);
+       addstat(STAT_PL_CROUCH_MIN1, AS_FLOAT, stat_pl_crouch_min_x);
+       addstat(STAT_PL_CROUCH_MIN2, AS_FLOAT, stat_pl_crouch_min_y);
+       addstat(STAT_PL_CROUCH_MIN3, AS_FLOAT, stat_pl_crouch_min_z);
+       addstat(STAT_PL_CROUCH_MAX1, AS_FLOAT, stat_pl_crouch_max_x);
+       addstat(STAT_PL_CROUCH_MAX2, AS_FLOAT, stat_pl_crouch_max_y);
+       addstat(STAT_PL_CROUCH_MAX3, AS_FLOAT, stat_pl_crouch_max_z);
+
+       // g_movementspeed hack
+       addstat(STAT_MOVEVARS_AIRSPEEDLIMIT_NONQW, AS_FLOAT, stat_sv_airspeedlimit_nonqw);
+       addstat(STAT_MOVEVARS_MAXSPEED, AS_FLOAT, stat_sv_maxspeed);
+       addstat(STAT_MOVEVARS_AIRACCEL_QW, AS_FLOAT, stat_sv_airaccel_qw);
+       addstat(STAT_MOVEVARS_AIRSTRAFEACCEL_QW, AS_FLOAT, stat_sv_airstrafeaccel_qw);
+       addstat(STAT_MOVEVARS_HIGHSPEED, AS_FLOAT, stat_movement_highspeed);
+
+       // jet pack
+       addstat(STAT_JETPACK_ACCEL_SIDE, AS_FLOAT, stat_jetpack_accel_side);
+       addstat(STAT_JETPACK_ACCEL_UP, AS_FLOAT, stat_jetpack_accel_up);
+       addstat(STAT_JETPACK_ANTIGRAVITY, AS_FLOAT, stat_jetpack_antigravity);
+       addstat(STAT_JETPACK_FUEL, AS_FLOAT, stat_jetpack_fuel);
+       addstat(STAT_JETPACK_MAXSPEED_UP, AS_FLOAT, stat_jetpack_maxspeed_up);
+       addstat(STAT_JETPACK_MAXSPEED_SIDE, AS_FLOAT, stat_jetpack_maxspeed_side);
+
+       // hack to fix track_canjump
+       addstat(STAT_MOVEVARS_TRACK_CANJUMP, AS_INT, cvar_cl_movement_track_canjump);
+
+       // double jump
+       addstat(STAT_DOUBLEJUMP, AS_INT, stat_doublejump);
+
+       // jump speed caps
+       addstat(STAT_MOVEVARS_JUMPSPEEDCAP_MIN, AS_FLOAT, stat_jumpspeedcap_min);
+       addstat(STAT_MOVEVARS_JUMPSPEEDCAP_MIN, AS_FLOAT, stat_jumpspeedcap_min);
+       addstat(STAT_MOVEVARS_JUMPSPEEDCAP_DISABLE_ONRAMPS, AS_INT, stat_jumpspeedcap_disable_onramps);
+
+       // hacks
+       addstat(STAT_MOVEVARS_FRICTION_ONLAND, AS_FLOAT, stat_sv_friction_on_land);
+       addstat(STAT_MOVEVARS_FRICTION_SLICK, AS_FLOAT, stat_sv_friction_slick);
+       addstat(STAT_GAMEPLAYFIX_EASIERWATERJUMP, AS_INT, stat_gameplayfix_easierwaterjump);
+
+       // new properties
+       addstat(STAT_MOVEVARS_JUMPVELOCITY, AS_FLOAT, stat_sv_jumpvelocity);
+       addstat(STAT_MOVEVARS_AIRACCEL_QW_STRETCHFACTOR, AS_FLOAT, stat_sv_airaccel_qw_stretchfactor);
+       addstat(STAT_MOVEVARS_MAXAIRSTRAFESPEED, AS_FLOAT, stat_sv_maxairstrafespeed);
+       addstat(STAT_MOVEVARS_MAXAIRSPEED, AS_FLOAT, stat_sv_maxairspeed);
+       addstat(STAT_MOVEVARS_AIRSTRAFEACCELERATE, AS_FLOAT, stat_sv_airstrafeaccelerate);
+       addstat(STAT_MOVEVARS_WARSOWBUNNY_TURNACCEL, AS_FLOAT, stat_sv_warsowbunny_turnaccel);
+       addstat(STAT_MOVEVARS_AIRACCEL_SIDEWAYS_FRICTION, AS_FLOAT, stat_sv_airaccel_sideways_friction);
+       addstat(STAT_MOVEVARS_AIRCONTROL, AS_FLOAT, stat_sv_aircontrol);
+       addstat(STAT_MOVEVARS_AIRCONTROL_POWER, AS_FLOAT, stat_sv_aircontrol_power);
+       addstat(STAT_MOVEVARS_AIRCONTROL_PENALTY, AS_FLOAT, stat_sv_aircontrol_penalty);
+       addstat(STAT_MOVEVARS_WARSOWBUNNY_AIRFORWARDACCEL, AS_FLOAT, stat_sv_warsowbunny_airforwardaccel);
+       addstat(STAT_MOVEVARS_WARSOWBUNNY_TOPSPEED, AS_FLOAT, stat_sv_warsowbunny_topspeed);
+       addstat(STAT_MOVEVARS_WARSOWBUNNY_ACCEL, AS_FLOAT, stat_sv_warsowbunny_accel);
+       addstat(STAT_MOVEVARS_WARSOWBUNNY_BACKTOSIDERATIO, AS_FLOAT, stat_sv_warsowbunny_backtosideratio);
+       addstat(STAT_MOVEVARS_FRICTION, AS_FLOAT, stat_sv_friction);
+       addstat(STAT_MOVEVARS_ACCELERATE, AS_FLOAT, stat_sv_accelerate);
+       addstat(STAT_MOVEVARS_STOPSPEED, AS_FLOAT, stat_sv_stopspeed);
+       addstat(STAT_MOVEVARS_AIRACCELERATE, AS_FLOAT, stat_sv_airaccelerate);
+       addstat(STAT_MOVEVARS_AIRSTOPACCELERATE, AS_FLOAT, stat_sv_airstopaccelerate);
+
+       addstat(STAT_GAMEPLAYFIX_UPVELOCITYCLEARSONGROUND, AS_INT, stat_gameplayfix_upvelocityclearsonground);
+}
+
+void Physics_UpdateStats(float maxspd_mod)
+{
+       // blah
+       self.stat_pl_view_ofs = PL_VIEW_OFS;
+       self.stat_pl_crouch_view_ofs = PL_CROUCH_VIEW_OFS;
+
+       self.stat_pl_min = PL_MIN;
+       self.stat_pl_max = PL_MAX;
+       self.stat_pl_crouch_min = PL_CROUCH_MIN;
+       self.stat_pl_crouch_max = PL_CROUCH_MAX;
+
+
+       self.stat_sv_airaccel_qw = AdjustAirAccelQW(Physics_ClientOption(self, "airaccel_qw"), maxspd_mod);
+       if(Physics_ClientOption(self, "airstrafeaccel_qw"))
+               self.stat_sv_airstrafeaccel_qw = AdjustAirAccelQW(Physics_ClientOption(self, "airstrafeaccel_qw"), maxspd_mod);
+       else
+               self.stat_sv_airstrafeaccel_qw = 0;
+       self.stat_sv_airspeedlimit_nonqw = Physics_ClientOption(self, "airspeedlimit_nonqw") * maxspd_mod;
+       self.stat_sv_maxspeed = Physics_ClientOption(self, "maxspeed") * maxspd_mod; // also slow walking
+       self.stat_movement_highspeed = PHYS_HIGHSPEED; // TODO: remove this!
+
+       self.stat_doublejump = PHYS_DOUBLEJUMP;
+
+       self.stat_jetpack_antigravity = PHYS_JETPACK_ANTIGRAVITY;
+       self.stat_jetpack_accel_up = PHYS_JETPACK_ACCEL_UP;
+       self.stat_jetpack_accel_side = PHYS_JETPACK_ACCEL_SIDE;
+       self.stat_jetpack_maxspeed_side = PHYS_JETPACK_MAXSPEED_SIDE;
+       self.stat_jetpack_maxspeed_up = PHYS_JETPACK_MAXSPEED_UP;
+       self.stat_jetpack_fuel = PHYS_JETPACK_FUEL;
+
+       self.stat_jumpspeedcap_min = PHYS_JUMPSPEEDCAP_MIN;
+       self.stat_jumpspeedcap_max = PHYS_JUMPSPEEDCAP_MAX;
+       self.stat_jumpspeedcap_disable_onramps = PHYS_JUMPSPEEDCAP_DISABLE_ONRAMPS;
+
+       self.stat_sv_friction_on_land = PHYS_FRICTION_ONLAND;
+       self.stat_sv_friction_slick = PHYS_FRICTION_SLICK;
+
+       self.stat_gameplayfix_easierwaterjump = GAMEPLAYFIX_EASIERWATERJUMP;
+
+
+       // old stats
+       // fix some new settings
+       self.stat_sv_airaccel_qw_stretchfactor = Physics_ClientOption(self, "airaccel_qw_stretchfactor");
+       self.stat_sv_maxairstrafespeed = Physics_ClientOption(self, "maxairstrafespeed");
+       self.stat_sv_maxairspeed = Physics_ClientOption(self, "maxairspeed");
+       self.stat_sv_airstrafeaccelerate = Physics_ClientOption(self, "airstrafeaccelerate");
+       self.stat_sv_warsowbunny_turnaccel = Physics_ClientOption(self, "warsowbunny_turnaccel");
+       self.stat_sv_airaccel_sideways_friction = Physics_ClientOption(self, "airaccel_sideways_friction");
+       self.stat_sv_aircontrol = Physics_ClientOption(self, "aircontrol");
+       self.stat_sv_aircontrol_power = Physics_ClientOption(self, "aircontrol_power");
+       self.stat_sv_aircontrol_penalty = Physics_ClientOption(self, "aircontrol_penalty");
+       self.stat_sv_warsowbunny_airforwardaccel = Physics_ClientOption(self, "warsowbunny_airforwardaccel");
+       self.stat_sv_warsowbunny_topspeed = Physics_ClientOption(self, "warsowbunny_topspeed");
+       self.stat_sv_warsowbunny_accel = Physics_ClientOption(self, "warsowbunny_accel");
+       self.stat_sv_warsowbunny_backtosideratio = Physics_ClientOption(self, "warsowbunny_backtosideratio");
+       self.stat_sv_friction = Physics_ClientOption(self, "friction");
+       self.stat_sv_accelerate = Physics_ClientOption(self, "accelerate");
+       self.stat_sv_stopspeed = Physics_ClientOption(self, "stopspeed");
+       self.stat_sv_airaccelerate = Physics_ClientOption(self, "airaccelerate");
+       self.stat_sv_airstopaccelerate = Physics_ClientOption(self, "airstopaccelerate");
+       self.stat_sv_jumpvelocity = Physics_ClientOption(self, "jumpvelocity");
+
+       self.stat_gameplayfix_upvelocityclearsonground = UPWARD_VELOCITY_CLEARS_ONGROUND;
+}
+#endif
+
+float IsMoveInDirection(vector mv, float ang) // key mix factor
+{
+       if (mv_x == 0 && mv_y == 0)
+               return 0; // avoid division by zero
+       ang -= RAD2DEG * atan2(mv_y, mv_x);
+       ang = remainder(ang, 360) / 45;
+       return ang > 1 ? 0 : ang < -1 ? 0 : 1 - fabs(ang);
+}
+
+float GeomLerp(float a, float lerp, float b)
+{
+       return a == 0 ? (lerp < 1 ? 0 : b)
+               : b == 0 ? (lerp > 0 ? 0 : a)
+               : a * pow(fabs(b / a), lerp);
+}
+
+noref float pmove_waterjumptime;
+
+const float unstick_count = 27;
+vector unstick_offsets[unstick_count] =
+{
+// 1 no nudge (just return the original if this test passes)
+       '0.000   0.000  0.000',
+// 6 simple nudges
+       ' 0.000  0.000  0.125', '0.000  0.000 -0.125',
+       '-0.125  0.000  0.000', '0.125  0.000  0.000',
+       ' 0.000 -0.125  0.000', '0.000  0.125  0.000',
+// 4 diagonal flat nudges
+       '-0.125 -0.125  0.000', '0.125 -0.125  0.000',
+       '-0.125  0.125  0.000', '0.125  0.125  0.000',
+// 8 diagonal upward nudges
+       '-0.125  0.000  0.125', '0.125  0.000  0.125',
+       ' 0.000 -0.125  0.125', '0.000  0.125  0.125',
+       '-0.125 -0.125  0.125', '0.125 -0.125  0.125',
+       '-0.125  0.125  0.125', '0.125  0.125  0.125',
+// 8 diagonal downward nudges
+       '-0.125  0.000 -0.125', '0.125  0.000 -0.125',
+       ' 0.000 -0.125 -0.125', '0.000  0.125 -0.125',
+       '-0.125 -0.125 -0.125', '0.125 -0.125 -0.125',
+       '-0.125  0.125 -0.125', '0.125  0.125 -0.125',
+};
+
+void PM_ClientMovement_Unstick()
+{
+       float i;
+       for (i = 0; i < unstick_count; i++)
+       {
+               vector neworigin = unstick_offsets[i] + self.origin;
+               tracebox(neworigin, PL_CROUCH_MIN, PL_CROUCH_MAX, neworigin, MOVE_NORMAL, self);
+               if (!trace_startsolid)
+               {
+                       setorigin(self, neworigin);
+                       return;// true;
+               }
+       }
+}
+
+void PM_ClientMovement_UpdateStatus(bool ground)
+{
+       // make sure player is not stuck
+       PM_ClientMovement_Unstick();
+
+       // set crouched
+       if (PHYS_INPUT_BUTTON_CROUCH(self))
+       {
+               // wants to crouch, this always works..
+               if (!IS_DUCKED(self))
+                       SET_DUCKED(self);
+       }
+       else
+       {
+               // wants to stand, if currently crouching we need to check for a
+               // low ceiling first
+               if (IS_DUCKED(self))
+               {
+                       tracebox(self.origin, PL_MIN, PL_MAX, self.origin, MOVE_NORMAL, self);
+                       if (!trace_startsolid)
+                               UNSET_DUCKED(self);
+               }
+       }
+
+       // set onground
+       vector origin1 = self.origin + '0 0 1';
+       vector origin2 = self.origin - '0 0 1';
+
+       if(ground)
+       {
+               tracebox(origin1, self.mins, self.maxs, origin2, MOVE_NORMAL, self);
+               if (trace_fraction < 1.0 && trace_plane_normal_z > 0.7)
+               {
+                       SET_ONGROUND(self);
+
+                       // this code actually "predicts" an impact; so let's clip velocity first
+                       float f = self.velocity * trace_plane_normal;
+                       self.velocity -= f * trace_plane_normal;
+               }
+               else
+                       UNSET_ONGROUND(self);
+       }
+
+       // set watertype/waterlevel
+       origin1 = self.origin;
+       origin1_z += self.mins_z + 1;
+       self.waterlevel = WATERLEVEL_NONE;
+
+       int thepoint = pointcontents(origin1);
+
+       self.watertype = (thepoint == CONTENT_WATER || thepoint == CONTENT_LAVA || thepoint == CONTENT_SLIME);
+
+       if(self.watertype)
+       {
+               self.waterlevel = WATERLEVEL_WETFEET;
+               origin1_z = self.origin_z + (self.mins_z + self.maxs_z) * 0.5;
+               thepoint = pointcontents(origin1);
+               if(thepoint == CONTENT_WATER || thepoint == CONTENT_LAVA || thepoint == CONTENT_SLIME)
+               {
+                       self.waterlevel = WATERLEVEL_SWIMMING;
+                       origin1_z = self.origin_z + 22;
+                       thepoint = pointcontents(origin1);
+                       if(thepoint == CONTENT_WATER || thepoint == CONTENT_LAVA || thepoint == CONTENT_SLIME)
+                               self.waterlevel = WATERLEVEL_SUBMERGED;
+               }
+       }
+
+       if(IS_ONGROUND(self) || self.velocity_z <= 0 || pmove_waterjumptime <= 0)
+               pmove_waterjumptime = 0;
+}
+
+void PM_ClientMovement_Move()
+{
+#ifdef CSQC
+       int bump;
+       float t;
+       float f;
+       vector neworigin;
+       vector currentorigin2;
+       vector neworigin2;
+       vector primalvelocity;
+
+       vector trace1_endpos = '0 0 0';
+       vector trace2_endpos = '0 0 0';
+       vector trace3_endpos = '0 0 0';
+       float trace1_fraction = 0;
+       float trace2_fraction = 0;
+       float trace3_fraction = 0;
+       vector trace1_plane_normal = '0 0 0';
+       vector trace2_plane_normal = '0 0 0';
+       vector trace3_plane_normal = '0 0 0';
+       
+
+       PM_ClientMovement_UpdateStatus(false);
+       primalvelocity = self.velocity;
+       for(bump = 0, t = PHYS_INPUT_TIMELENGTH; bump < 8 && (self.velocity * self.velocity) > 0; bump++)
+       {
+               neworigin = self.origin + t * self.velocity;
+               tracebox(self.origin, self.mins, self.maxs, neworigin, MOVE_NORMAL, self);
+               trace1_endpos = trace_endpos;
+               trace1_fraction = trace_fraction;
+               trace1_plane_normal = trace_plane_normal;
+               if(trace1_fraction < 1 && trace1_plane_normal_z == 0)
+               {
+                       // may be a step or wall, try stepping up
+                       // first move forward at a higher level
+                       currentorigin2 = self.origin;
+                       currentorigin2_z += PHYS_STEPHEIGHT;
+                       neworigin2 = neworigin;
+                       neworigin2_z += PHYS_STEPHEIGHT;
+                       tracebox(currentorigin2, self.mins, self.maxs, neworigin2, MOVE_NORMAL, self);
+                       trace2_endpos = trace_endpos;
+                       trace2_fraction = trace_fraction;
+                       trace2_plane_normal = trace_plane_normal;
+                       if(!trace_startsolid)
+                       {
+                               // then move down from there
+                               currentorigin2 = trace2_endpos;
+                               neworigin2 = trace2_endpos;
+                               neworigin2_z = self.origin_z;
+                               tracebox(currentorigin2, self.mins, self.maxs, neworigin2, MOVE_NORMAL, self);
+                               trace3_endpos = trace_endpos;
+                               trace3_fraction = trace_fraction;
+                               trace3_plane_normal = trace_plane_normal;
+                               // accept the new trace if it made some progress
+                               if(fabs(trace3_endpos_x - trace1_endpos_x) >= 0.03125 || fabs(trace3_endpos_y - trace1_endpos_y) >= 0.03125)
+                               {
+                                       trace1_endpos = trace2_endpos;
+                                       trace1_fraction = trace2_fraction;
+                                       trace1_plane_normal = trace2_plane_normal;
+                                       trace1_endpos = trace3_endpos;
+                               }
+                       }
+               }
+
+               // check if it moved at all
+               if(trace1_fraction >= 0.001)
+                       setorigin(self, trace1_endpos);
+
+               // check if it moved all the way
+               if(trace1_fraction == 1)
+                       break;
+
+               // this is only really needed for nogravityonground combined with gravityunaffectedbyticrate
+               // <LordHavoc> I'm pretty sure I commented it out solely because it seemed redundant
+               // this got commented out in a change that supposedly makes the code match QW better
+               // so if this is broken, maybe put it in an if(cls.protocol != PROTOCOL_QUAKEWORLD) block
+               if(trace1_plane_normal_z > 0.7)
+                       SET_ONGROUND(self);
+
+               t -= t * trace1_fraction;
+
+               f = (self.velocity * trace1_plane_normal);
+               self.velocity = self.velocity + -f * trace1_plane_normal;
+       }
+       if(pmove_waterjumptime > 0)
+               self.velocity = primalvelocity;
+#endif
+}
+
+void CPM_PM_Aircontrol(vector wishdir, float wishspeed)
+{
+       float k = 32 * (2 * IsMoveInDirection(self.movement, 0) - 1);
+       if (k <= 0)
+               return;
+
+       k *= bound(0, wishspeed / PHYS_MAXAIRSPEED(self), 1);
+
+       float zspeed = self.velocity_z;
+       self.velocity_z = 0;
+       float xyspeed = vlen(self.velocity);
+       self.velocity = normalize(self.velocity);
+
+       float dot = self.velocity * wishdir;
+
+       if (dot > 0) // we can't change direction while slowing down
+       {
+               k *= pow(dot, PHYS_AIRCONTROL_POWER) * PHYS_INPUT_TIMELENGTH;
+               xyspeed = max(0, xyspeed - PHYS_AIRCONTROL_PENALTY * sqrt(max(0, 1 - dot*dot)) * k/32);
+               k *= PHYS_AIRCONTROL;
+               self.velocity = normalize(self.velocity * xyspeed + wishdir * k);
+       }
+
+       self.velocity = self.velocity * xyspeed;
+       self.velocity_z = zspeed;
+}
+
+float AdjustAirAccelQW(float accelqw, float factor)
+{
+       return copysign(bound(0.000001, 1 - (1 - fabs(accelqw)) * factor, 1), accelqw);
+}
+
+// example config for alternate speed clamping:
+//   sv_airaccel_qw 0.8
+//   sv_airaccel_sideways_friction 0
+//   prvm_globalset server speedclamp_mode 1
+//     (or 2)
+void PM_Accelerate(vector wishdir, float wishspeed, float wishspeed0, float accel, float accelqw, float stretchfactor, float sidefric, float speedlimit)
+{
+       float speedclamp = stretchfactor > 0 ? stretchfactor
+       : accelqw < 0 ? 1 // full clamping, no stretch
+       : -1; // no clamping
+
+       accelqw = fabs(accelqw);
+
+       if (GAMEPLAYFIX_Q2AIRACCELERATE)
+               wishspeed0 = wishspeed; // don't need to emulate this Q1 bug
+
+       float vel_straight = self.velocity * wishdir;
+       float vel_z = self.velocity_z;
+       vector vel_xy = vec2(self.velocity);
+       vector vel_perpend = vel_xy - vel_straight * wishdir;
+
+       float step = accel * PHYS_INPUT_TIMELENGTH * wishspeed0;
+
+       float vel_xy_current  = vlen(vel_xy);
+       if (speedlimit)
+               accelqw = AdjustAirAccelQW(accelqw, (speedlimit - bound(wishspeed, vel_xy_current, speedlimit)) / max(1, speedlimit - wishspeed));
+       float vel_xy_forward =  vel_xy_current  + bound(0, wishspeed - vel_xy_current, step) * accelqw + step * (1 - accelqw);
+       float vel_xy_backward = vel_xy_current  - bound(0, wishspeed + vel_xy_current, step) * accelqw - step * (1 - accelqw);
+       vel_xy_backward = max(0, vel_xy_backward); // not that it REALLY occurs that this would cause wrong behaviour afterwards
+       vel_straight =          vel_straight    + bound(0, wishspeed - vel_straight,   step) * accelqw + step * (1 - accelqw);
+
+       if (sidefric < 0 && (vel_perpend*vel_perpend))
+               // negative: only apply so much sideways friction to stay below the speed you could get by "braking"
+       {
+               float f = max(0, 1 + PHYS_INPUT_TIMELENGTH * wishspeed * sidefric);
+               float fmin = (vel_xy_backward * vel_xy_backward - vel_straight * vel_straight) / (vel_perpend * vel_perpend);
+               // assume: fmin > 1
+               // vel_xy_backward*vel_xy_backward - vel_straight*vel_straight > vel_perpend*vel_perpend
+               // vel_xy_backward*vel_xy_backward > vel_straight*vel_straight + vel_perpend*vel_perpend
+               // vel_xy_backward*vel_xy_backward > vel_xy * vel_xy
+               // obviously, this cannot be
+               if (fmin <= 0)
+                       vel_perpend *= f;
+               else
+               {
+                       fmin = sqrt(fmin);
+                       vel_perpend *= max(fmin, f);
+               }
+       }
+       else
+               vel_perpend *= max(0, 1 - PHYS_INPUT_TIMELENGTH * wishspeed * sidefric);
+
+       vel_xy = vel_straight * wishdir + vel_perpend;
+
+       if (speedclamp >= 0)
+       {
+               float vel_xy_preclamp;
+               vel_xy_preclamp = vlen(vel_xy);
+               if (vel_xy_preclamp > 0) // prevent division by zero
+               {
+                       vel_xy_current += (vel_xy_forward - vel_xy_current) * speedclamp;
+                       if (vel_xy_current < vel_xy_preclamp)
+                               vel_xy *= (vel_xy_current / vel_xy_preclamp);
+               }
+       }
+
+       self.velocity = vel_xy + vel_z * '0 0 1';
+}
+
+void PM_AirAccelerate(vector wishdir, float wishspeed)
+{
+       if (wishspeed == 0)
+               return;
+
+       vector curvel = self.velocity;
+       curvel_z = 0;
+       float curspeed = vlen(curvel);
+
+       if (wishspeed > curspeed * 1.01)
+               wishspeed = min(wishspeed, curspeed + PHYS_WARSOWBUNNY_AIRFORWARDACCEL * PHYS_MAXSPEED(self) * PHYS_INPUT_TIMELENGTH);
+       else
+       {
+               float f = max(0, (PHYS_WARSOWBUNNY_TOPSPEED - curspeed) / (PHYS_WARSOWBUNNY_TOPSPEED - PHYS_MAXSPEED(self)));
+               wishspeed = max(curspeed, PHYS_MAXSPEED(self)) + PHYS_WARSOWBUNNY_ACCEL * f * PHYS_MAXSPEED(self) * PHYS_INPUT_TIMELENGTH;
+       }
+       vector wishvel = wishdir * wishspeed;
+       vector acceldir = wishvel - curvel;
+       float addspeed = vlen(acceldir);
+       acceldir = normalize(acceldir);
+
+       float accelspeed = min(addspeed, PHYS_WARSOWBUNNY_TURNACCEL * PHYS_MAXSPEED(self) * PHYS_INPUT_TIMELENGTH);
+
+       if (PHYS_WARSOWBUNNY_BACKTOSIDERATIO < 1)
+       {
+               vector curdir = normalize(curvel);
+               float dot = acceldir * curdir;
+               if (dot < 0)
+                       acceldir -= (1 - PHYS_WARSOWBUNNY_BACKTOSIDERATIO) * dot * curdir;
+       }
+
+       self.velocity += accelspeed * acceldir;
+}
+
+
+/*
+=============
+PlayerJump
+
+When you press the jump key
+returns true if handled
+=============
+*/
+bool PlayerJump (void)
+{
+       if (PHYS_FROZEN(self))
+               return true; // no jumping in freezetag when frozen
+
+#ifdef SVQC
+       if (self.player_blocked)
+               return true; // no jumping while blocked
+#endif
+
+       bool doublejump = false;
+       float mjumpheight = PHYS_JUMPVELOCITY;
+
+       player_multijump = doublejump;
+       player_jumpheight = mjumpheight;
+#ifdef SVQC
+       if (MUTATOR_CALLHOOK(PlayerJump))
+#elif defined(CSQC)
+       if(PM_multijump_checkjump())
+#endif
+               return true;
+
+       doublejump = player_multijump;
+       mjumpheight = player_jumpheight;
+
+       if (PHYS_DOUBLEJUMP)
+       {
+               tracebox(self.origin + '0 0 0.01', self.mins, self.maxs, self.origin - '0 0 0.01', MOVE_NORMAL, self);
+               if (trace_fraction < 1 && trace_plane_normal_z > 0.7)
+               {
+                       doublejump = true;
+
+                       // we MUST clip velocity here!
+                       float f;
+                       f = self.velocity * trace_plane_normal;
+                       if (f < 0)
+                               self.velocity -= f * trace_plane_normal;
+               }
+       }
+
+       if (self.waterlevel >= WATERLEVEL_SWIMMING)
+       {
+               self.velocity_z = PHYS_MAXSPEED(self) * 0.7;
+               return true;
+       }
+
+       if (!doublejump)
+               if (!IS_ONGROUND(self))
+                       return IS_JUMP_HELD(self);
+
+       if (PHYS_TRACK_CANJUMP(self))
+               if (IS_JUMP_HELD(self))
+                       return true;
+
+       // sv_jumpspeedcap_min/sv_jumpspeedcap_max act as baseline
+       // velocity bounds.  Final velocity is bound between (jumpheight *
+       // min + jumpheight) and (jumpheight * max + jumpheight);
+
+       if(PHYS_JUMPSPEEDCAP_MIN)
+       {
+               float minjumpspeed = mjumpheight * PHYS_JUMPSPEEDCAP_MIN;
+
+               if (self.velocity_z < minjumpspeed)
+                       mjumpheight += minjumpspeed - self.velocity_z;
+       }
+
+       if(PHYS_JUMPSPEEDCAP_MAX)
+       {
+               // don't do jump speedcaps on ramps to preserve old xonotic ramjump style
+               tracebox(self.origin + '0 0 0.01', self.mins, self.maxs, self.origin - '0 0 0.01', MOVE_NORMAL, self);
+
+               if (!(trace_fraction < 1 && trace_plane_normal_z < 0.98 && PHYS_JUMPSPEEDCAP_DISABLE_ONRAMPS))
+               {
+                       float maxjumpspeed = mjumpheight * PHYS_JUMPSPEEDCAP_MAX;
+
+                       if (self.velocity_z > maxjumpspeed)
+                               mjumpheight -= self.velocity_z - maxjumpspeed;
+               }
+       }
+
+       if (!WAS_ONGROUND(self))
+       {
+#ifdef SVQC
+               if(autocvar_speedmeter)
+                       dprint(strcat("landing velocity: ", vtos(self.velocity), " (abs: ", ftos(vlen(self.velocity)), ")\n"));
+#endif
+               if(self.lastground < time - 0.3)
+               {
+                       self.velocity_x *= (1 - PHYS_FRICTION_ONLAND);
+                       self.velocity_y *= (1 - PHYS_FRICTION_ONLAND);
+               }
+#ifdef SVQC
+               if(self.jumppadcount > 1)
+                       dprint(strcat(ftos(self.jumppadcount), "x jumppad combo\n"));
+               self.jumppadcount = 0;
+#endif
+       }
+
+       self.velocity_z += mjumpheight;
+
+       UNSET_ONGROUND(self);
+       SET_JUMP_HELD(self);
+
+#ifdef SVQC
+
+       self.oldvelocity_z = self.velocity_z;
+
+       animdecide_setaction(self, ANIMACTION_JUMP, true);
+
+       if (autocvar_g_jump_grunt)
+               PlayerSound(playersound_jump, CH_PLAYER, VOICETYPE_PLAYERSOUND);
+#endif
+       return true;
+}
+
+void CheckWaterJump()
+{
+// check for a jump-out-of-water
+       makevectors(self.v_angle);
+       vector start = self.origin;
+       start_z += 8;
+       v_forward_z = 0;
+       normalize(v_forward);
+       vector end = start + v_forward*24;
+       traceline (start, end, true, self);
+       if (trace_fraction < 1)
+       {       // solid at waist
+               start_z = start_z + self.maxs_z - 8;
+               end = start + v_forward*24;
+               self.movedir = trace_plane_normal * -50;
+               traceline(start, end, true, self);
+               if (trace_fraction == 1)
+               {       // open at eye level
+                       self.velocity_z = 225;
+                       self.flags |= FL_WATERJUMP;
+                       SET_JUMP_HELD(self);
+#ifdef SVQC
+                       self.teleport_time = time + 2;  // safety net
+#elif defined(CSQC)
+                       pmove_waterjumptime = time + 2;
+#endif
+               }
+       }
+}
+
+
+#ifdef SVQC
+       #define JETPACK_JUMP(s) s.cvar_cl_jetpack_jump
+#elif defined(CSQC)
+       float autocvar_cl_jetpack_jump;
+       #define JETPACK_JUMP(s) autocvar_cl_jetpack_jump
+#endif
+.float jetpack_stopped;
+// Hack: shouldn't need to know about this
+.float multijump_count;
+void CheckPlayerJump()
+{
+#ifdef SVQC
+       float was_flying = ITEMS_STAT(self) & IT_USING_JETPACK;
+#endif
+       if (JETPACK_JUMP(self) < 2)
+               ITEMS_STAT(self) &= ~IT_USING_JETPACK;
+
+       if(PHYS_INPUT_BUTTON_JUMP(self) || PHYS_INPUT_BUTTON_JETPACK(self))
+       {
+               float air_jump = !PlayerJump() || self.multijump_count > 0; // PlayerJump() has important side effects
+               float activate = JETPACK_JUMP(self) && air_jump && PHYS_INPUT_BUTTON_JUMP(self) || PHYS_INPUT_BUTTON_JETPACK(self);
+               float has_fuel = !PHYS_JETPACK_FUEL || PHYS_AMMO_FUEL(self) || ITEMS_STAT(self) & IT_UNLIMITED_WEAPON_AMMO;
+
+               if (!(ITEMS_STAT(self) & IT_JETPACK)) { }
+               else if (self.jetpack_stopped) { }
+               else if (!has_fuel)
+               {
+#ifdef SVQC
+                       if (was_flying) // TODO: ran out of fuel message
+                               Send_Notification(NOTIF_ONE, self, MSG_INFO, INFO_JETPACK_NOFUEL);
+                       else if (activate)
+                               Send_Notification(NOTIF_ONE, self, MSG_INFO, INFO_JETPACK_NOFUEL);
+#endif
+                       self.jetpack_stopped = true;
+                       ITEMS_STAT(self) &= ~IT_USING_JETPACK;
+               }
+               else if (activate && !PHYS_FROZEN(self))
+                       ITEMS_STAT(self) |= IT_USING_JETPACK;
+       }
+       else
+       {
+               self.jetpack_stopped = false;
+               ITEMS_STAT(self) &= ~IT_USING_JETPACK;
+       }
+       if (!PHYS_INPUT_BUTTON_JUMP(self))
+               UNSET_JUMP_HELD(self);
+
+       if (self.waterlevel == WATERLEVEL_SWIMMING)
+               CheckWaterJump();
+}
+
+float racecar_angle(float forward, float down)
+{
+       if (forward < 0)
+       {
+               forward = -forward;
+               down = -down;
+       }
+
+       float ret = vectoyaw('0 1 0' * down + '1 0 0' * forward);
+
+       float angle_mult = forward / (800 + forward);
+
+       if (ret > 180)
+               return ret * angle_mult + 360 * (1 - angle_mult);
+       else
+               return ret * angle_mult;
+}
+
+void RaceCarPhysics()
+{
+#ifdef SVQC
+       // using this move type for "big rigs"
+       // the engine does not push the entity!
+
+       vector rigvel;
+
+       vector angles_save = self.angles;
+       float accel = bound(-1, self.movement.x / PHYS_MAXSPEED(self), 1);
+       float steer = bound(-1, self.movement.y / PHYS_MAXSPEED(self), 1);
+
+       if (g_bugrigs_reverse_speeding)
+       {
+               if (accel < 0)
+               {
+                       // back accel is DIGITAL
+                       // to prevent speedhack
+                       if (accel < -0.5)
+                               accel = -1;
+                       else
+                               accel = 0;
+               }
+       }
+
+       self.angles_x = 0;
+       self.angles_z = 0;
+       makevectors(self.angles); // new forward direction!
+
+       if (IS_ONGROUND(self) || g_bugrigs_air_steering)
+       {
+               float myspeed = self.velocity * v_forward;
+               float upspeed = self.velocity * v_up;
+
+               // responsiveness factor for steering and acceleration
+               float f = 1 / (1 + pow(max(-myspeed, myspeed) / g_bugrigs_speed_ref, g_bugrigs_speed_pow));
+               //MAXIMA: f(v) := 1 / (1 + (v / g_bugrigs_speed_ref) ^ g_bugrigs_speed_pow);
+
+               float steerfactor;
+               if (myspeed < 0 && g_bugrigs_reverse_spinning)
+                       steerfactor = -myspeed * g_bugrigs_steer;
+               else
+                       steerfactor = -myspeed * f * g_bugrigs_steer;
+
+               float accelfactor;
+               if (myspeed < 0 && g_bugrigs_reverse_speeding)
+                       accelfactor = g_bugrigs_accel;
+               else
+                       accelfactor = f * g_bugrigs_accel;
+               //MAXIMA: accel(v) := f(v) * g_bugrigs_accel;
+
+               if (accel < 0)
+               {
+                       if (myspeed > 0)
+                       {
+                               myspeed = max(0, myspeed - PHYS_INPUT_TIMELENGTH * (g_bugrigs_friction_floor - g_bugrigs_friction_brake * accel));
+                       }
+                       else
+                       {
+                               if (!g_bugrigs_reverse_speeding)
+                                       myspeed = min(0, myspeed + PHYS_INPUT_TIMELENGTH * g_bugrigs_friction_floor);
+                       }
+               }
+               else
+               {
+                       if (myspeed >= 0)
+                       {
+                               myspeed = max(0, myspeed - PHYS_INPUT_TIMELENGTH * g_bugrigs_friction_floor);
+                       }
+                       else
+                       {
+                               if (g_bugrigs_reverse_stopping)
+                                       myspeed = 0;
+                               else
+                                       myspeed = min(0, myspeed + PHYS_INPUT_TIMELENGTH * (g_bugrigs_friction_floor + g_bugrigs_friction_brake * accel));
+                       }
+               }
+               // terminal velocity = velocity at which 50 == accelfactor, that is, 1549 units/sec
+               //MAXIMA: friction(v) := g_bugrigs_friction_floor;
+
+               self.angles_y += steer * PHYS_INPUT_TIMELENGTH * steerfactor; // apply steering
+               makevectors(self.angles); // new forward direction!
+
+               myspeed += accel * accelfactor * PHYS_INPUT_TIMELENGTH;
+
+               rigvel = myspeed * v_forward + '0 0 1' * upspeed;
+       }
+       else
+       {
+               float myspeed = vlen(self.velocity);
+
+               // responsiveness factor for steering and acceleration
+               float f = 1 / (1 + pow(max(0, myspeed / g_bugrigs_speed_ref), g_bugrigs_speed_pow));
+               float steerfactor = -myspeed * f;
+               self.angles_y += steer * PHYS_INPUT_TIMELENGTH * steerfactor; // apply steering
+
+               rigvel = self.velocity;
+               makevectors(self.angles); // new forward direction!
+       }
+
+       rigvel *= max(0, 1 - vlen(rigvel) * g_bugrigs_friction_air * PHYS_INPUT_TIMELENGTH);
+       //MAXIMA: airfriction(v) := v * v * g_bugrigs_friction_air;
+       //MAXIMA: total_acceleration(v) := accel(v) - friction(v) - airfriction(v);
+       //MAXIMA: solve(total_acceleration(v) = 0, v);
+
+       if (g_bugrigs_planar_movement)
+       {
+               vector rigvel_xy, neworigin, up;
+               float mt;
+
+               rigvel_z -= PHYS_INPUT_TIMELENGTH * PHYS_GRAVITY; // 4x gravity plays better
+               rigvel_xy = vec2(rigvel);
+
+               if (g_bugrigs_planar_movement_car_jumping)
+                       mt = MOVE_NORMAL;
+               else
+                       mt = MOVE_NOMONSTERS;
+
+               tracebox(self.origin, self.mins, self.maxs, self.origin + '0 0 1024', mt, self);
+               up = trace_endpos - self.origin;
+
+               // BUG RIGS: align the move to the surface instead of doing collision testing
+               // can we move?
+               tracebox(trace_endpos, self.mins, self.maxs, trace_endpos + rigvel_xy * PHYS_INPUT_TIMELENGTH, mt, self);
+
+               // align to surface
+               tracebox(trace_endpos, self.mins, self.maxs, trace_endpos - up + '0 0 1' * rigvel_z * PHYS_INPUT_TIMELENGTH, mt, self);
+
+               if (trace_fraction < 0.5)
+               {
+                       trace_fraction = 1;
+                       neworigin = self.origin;
+               }
+               else
+                       neworigin = trace_endpos;
+
+               if (trace_fraction < 1)
+               {
+                       // now set angles_x so that the car points parallel to the surface
+                       self.angles = vectoangles(
+                                       '1 0 0' * v_forward_x * trace_plane_normal_z
+                                       +
+                                       '0 1 0' * v_forward_y * trace_plane_normal_z
+                                       +
+                                       '0 0 1' * -(v_forward_x * trace_plane_normal_x + v_forward_y * trace_plane_normal_y)
+                                       );
+                       SET_ONGROUND(self);
+               }
+               else
+               {
+                       // now set angles_x so that the car points forward, but is tilted in velocity direction
+                       UNSET_ONGROUND(self);
+               }
+
+               self.velocity = (neworigin - self.origin) * (1.0 / PHYS_INPUT_TIMELENGTH);
+               self.movetype = MOVETYPE_NOCLIP;
+       }
+       else
+       {
+               rigvel_z -= PHYS_INPUT_TIMELENGTH * PHYS_GRAVITY; // 4x gravity plays better
+               self.velocity = rigvel;
+               self.movetype = MOVETYPE_FLY;
+       }
+
+       trace_fraction = 1;
+       tracebox(self.origin, self.mins, self.maxs, self.origin - '0 0 4', MOVE_NORMAL, self);
+       if (trace_fraction != 1)
+       {
+               self.angles = vectoangles2(
+                               '1 0 0' * v_forward_x * trace_plane_normal_z
+                               +
+                               '0 1 0' * v_forward_y * trace_plane_normal_z
+                               +
+                               '0 0 1' * -(v_forward_x * trace_plane_normal_x + v_forward_y * trace_plane_normal_y),
+                               trace_plane_normal
+                               );
+       }
+       else
+       {
+               vector vel_local;
+
+               vel_local_x = v_forward * self.velocity;
+               vel_local_y = v_right * self.velocity;
+               vel_local_z = v_up * self.velocity;
+
+               self.angles_x = racecar_angle(vel_local_x, vel_local_z);
+               self.angles_z = racecar_angle(-vel_local_y, vel_local_z);
+       }
+
+       // smooth the angles
+       vector vf1, vu1, smoothangles;
+       makevectors(self.angles);
+       float f = bound(0, PHYS_INPUT_TIMELENGTH * g_bugrigs_angle_smoothing, 1);
+       if (f == 0)
+               f = 1;
+       vf1 = v_forward * f;
+       vu1 = v_up * f;
+       makevectors(angles_save);
+       vf1 = vf1 + v_forward * (1 - f);
+       vu1 = vu1 + v_up * (1 - f);
+       smoothangles = vectoangles2(vf1, vu1);
+       self.angles_x = -smoothangles_x;
+       self.angles_z =  smoothangles_z;
+#endif
+}
+
+string specialcommand = "xwxwxsxsxaxdxaxdx1x ";
+.float specialcommand_pos;
+void SpecialCommand()
+{
+#ifdef SVQC
+#ifdef TETRIS
+       TetrisImpulse();
+#else
+       if (!CheatImpulse(99))
+               print("A hollow voice says \"Plugh\".\n");
+#endif
+#endif
+}
+
+float PM_check_keepaway(void)
+{
+#ifdef SVQC
+       return (self.ballcarried && g_keepaway) ? autocvar_g_keepaway_ballcarrier_highspeed : 1;
+#else
+       return 1;
+#endif
+}
+
+void PM_check_race_movetime(void)
+{
+#ifdef SVQC
+       self.race_movetime_frac += PHYS_INPUT_TIMELENGTH;
+       float f = floor(self.race_movetime_frac);
+       self.race_movetime_frac -= f;
+       self.race_movetime_count += f;
+       self.race_movetime = self.race_movetime_frac + self.race_movetime_count;
+#endif
+}
+
+float PM_check_specialcommand(float buttons)
+{
+#ifdef SVQC
+       string c;
+       if (!buttons)
+               c = "x";
+       else if (buttons == 1)
+               c = "1";
+       else if (buttons == 2)
+               c = " ";
+       else if (buttons == 128)
+               c = "s";
+       else if (buttons == 256)
+               c = "w";
+       else if (buttons == 512)
+               c = "a";
+       else if (buttons == 1024)
+               c = "d";
+       else
+               c = "?";
+
+       if (c == substring(specialcommand, self.specialcommand_pos, 1))
+       {
+               self.specialcommand_pos += 1;
+               if (self.specialcommand_pos >= strlen(specialcommand))
+               {
+                       self.specialcommand_pos = 0;
+                       SpecialCommand();
+                       return true;
+               }
+       }
+       else if (self.specialcommand_pos && (c != substring(specialcommand, self.specialcommand_pos - 1, 1)))
+               self.specialcommand_pos = 0;
+#endif
+       return false;
+}
+
+void PM_check_nickspam(void)
+{
+#ifdef SVQC
+       if (time >= self.nickspamtime)
+               return;
+       if (self.nickspamcount >= autocvar_g_nick_flood_penalty_yellow)
+       {
+               // slight annoyance for nick change scripts
+               self.movement = -1 * self.movement;
+               self.BUTTON_ATCK = self.BUTTON_JUMP = self.BUTTON_ATCK2 = self.BUTTON_ZOOM = self.BUTTON_CROUCH = self.BUTTON_HOOK = self.BUTTON_USE = 0;
+
+               if (self.nickspamcount >= autocvar_g_nick_flood_penalty_red) // if you are persistent and the slight annoyance above does not stop you, I'll show you!
+               {
+                       self.v_angle_x = random() * 360;
+                       self.v_angle_y = random() * 360;
+                       // at least I'm not forcing retardedview by also assigning to angles_z
+                       self.fixangle = true;
+               }
+       }
+#endif
+}
+
+void PM_check_punch()
+{
+#ifdef SVQC
+       if (self.punchangle != '0 0 0')
+       {
+               float f = vlen(self.punchangle) - 10 * PHYS_INPUT_TIMELENGTH;
+               if (f > 0)
+                       self.punchangle = normalize(self.punchangle) * f;
+               else
+                       self.punchangle = '0 0 0';
+       }
+
+       if (self.punchvector != '0 0 0')
+       {
+               float f = vlen(self.punchvector) - 30 * PHYS_INPUT_TIMELENGTH;
+               if (f > 0)
+                       self.punchvector = normalize(self.punchvector) * f;
+               else
+                       self.punchvector = '0 0 0';
+       }
+#endif
+}
+
+void PM_check_spider(void)
+{
+#ifdef SVQC
+       if (time >= self.spider_slowness)
+               return;
+       PHYS_MAXSPEED(self) *= 0.5; // half speed while slow from spider
+       PHYS_MAXAIRSPEED(self) *= 0.5;
+       PHYS_AIRSPEEDLIMIT_NONQW(self) *= 0.5;
+       PHYS_AIRSTRAFEACCELERATE(self) *= 0.5;
+#endif
+}
+
+// predict frozen movement, as frozen players CAN move in some cases
+void PM_check_frozen(void)
+{
+       if (!PHYS_FROZEN(self))
+               return;
+       if (PHYS_DODGING_FROZEN
+#ifdef SVQC
+       && IS_REAL_CLIENT(self)
+#endif
+       )
+       {
+               self.movement_x = bound(-5, self.movement.x, 5);
+               self.movement_y = bound(-5, self.movement.y, 5);
+               self.movement_z = bound(-5, self.movement.z, 5);
+       }
+       else
+               self.movement = '0 0 0';
+
+       vector midpoint = ((self.absmin + self.absmax) * 0.5);
+       if (pointcontents(midpoint) == CONTENT_WATER)
+       {
+               self.velocity = self.velocity * 0.5;
+
+               if (pointcontents(midpoint + '0 0 16') == CONTENT_WATER)
+                       self.velocity_z = 200;
+       }
+}
+
+void PM_check_hitground()
+{
+#ifdef SVQC
+       if (IS_ONGROUND(self))
+       if (IS_PLAYER(self)) // no fall sounds for observers thank you very much
+       if (self.wasFlying)
+       {
+               self.wasFlying = 0;
+               if (self.waterlevel < WATERLEVEL_SWIMMING)
+               if (time >= self.ladder_time)
+               if (!self.hook)
+               {
+                       self.nextstep = time + 0.3 + random() * 0.1;
+                       trace_dphitq3surfaceflags = 0;
+                       tracebox(self.origin, self.mins, self.maxs, self.origin - '0 0 1', MOVE_NOMONSTERS, self);
+                       if (!(trace_dphitq3surfaceflags & Q3SURFACEFLAG_NOSTEPS))
+                       {
+                               if (trace_dphitq3surfaceflags & Q3SURFACEFLAG_METALSTEPS)
+                                       GlobalSound(globalsound_metalfall, CH_PLAYER, VOICETYPE_PLAYERSOUND);
+                               else
+                                       GlobalSound(globalsound_fall, CH_PLAYER, VOICETYPE_PLAYERSOUND);
+                       }
+               }
+       }
+#endif
+}
+
+void PM_check_blocked(void)
+{
+#ifdef SVQC
+       if (!self.player_blocked)
+               return;
+       self.movement = '0 0 0';
+       self.disableclientprediction = 1;
+#endif
+}
+
+#ifdef SVQC
+float speedaward_lastsent;
+float speedaward_lastupdate;
+#endif
+void PM_check_race(void)
+{
+#ifdef SVQC
+       if(!(g_cts || g_race))
+               return;
+       if (vlen(self.velocity - self.velocity_z * '0 0 1') > speedaward_speed)
+       {
+               speedaward_speed = vlen(self.velocity - self.velocity_z * '0 0 1');
+               speedaward_holder = self.netname;
+               speedaward_uid = self.crypto_idfp;
+               speedaward_lastupdate = time;
+       }
+       if (speedaward_speed > speedaward_lastsent && time - speedaward_lastupdate > 1)
+       {
+               string rr = (g_cts) ? CTS_RECORD : RACE_RECORD;
+               race_send_speedaward(MSG_ALL);
+               speedaward_lastsent = speedaward_speed;
+               if (speedaward_speed > speedaward_alltimebest && speedaward_uid != "")
+               {
+                       speedaward_alltimebest = speedaward_speed;
+                       speedaward_alltimebest_holder = speedaward_holder;
+                       speedaward_alltimebest_uid = speedaward_uid;
+                       db_put(ServerProgsDB, strcat(GetMapname(), rr, "speed/speed"), ftos(speedaward_alltimebest));
+                       db_put(ServerProgsDB, strcat(GetMapname(), rr, "speed/crypto_idfp"), speedaward_alltimebest_uid);
+                       race_send_speedaward_alltimebest(MSG_ALL);
+               }
+       }
+#endif
+}
+
+void PM_check_vortex(void)
+{
+#ifdef SVQC
+       // WEAPONTODO
+       float xyspeed = vlen(vec2(self.velocity));
+       if (self.weapon == WEP_VORTEX && WEP_CVAR(vortex, charge) && WEP_CVAR(vortex, charge_velocity_rate) && xyspeed > WEP_CVAR(vortex, charge_minspeed))
+       {
+               // add a maximum of charge_velocity_rate when going fast (f = 1), gradually increasing from minspeed (f = 0) to maxspeed
+               xyspeed = min(xyspeed, WEP_CVAR(vortex, charge_maxspeed));
+               float f = (xyspeed - WEP_CVAR(vortex, charge_minspeed)) / (WEP_CVAR(vortex, charge_maxspeed) - WEP_CVAR(vortex, charge_minspeed));
+               // add the extra charge
+               self.vortex_charge = min(1, self.vortex_charge + WEP_CVAR(vortex, charge_velocity_rate) * f * PHYS_INPUT_TIMELENGTH);
+       }
+#endif
+}
+
+void PM_fly(float maxspd_mod)
+{
+       // noclipping or flying
+       UNSET_ONGROUND(self);
+
+       self.velocity = self.velocity * (1 - PHYS_INPUT_TIMELENGTH * PHYS_FRICTION);
+       makevectors(self.v_angle);
+       //wishvel = v_forward * self.movement.x + v_right * self.movement.y + v_up * self.movement.z;
+       vector wishvel = v_forward * self.movement.x
+                                       + v_right * self.movement.y
+                                       + '0 0 1' * self.movement.z;
+       // acceleration
+       vector wishdir = normalize(wishvel);
+       float wishspeed = min(vlen(wishvel), PHYS_MAXSPEED(self) * maxspd_mod);
+#ifdef SVQC
+       if (time >= self.teleport_time)
+#endif
+               PM_Accelerate(wishdir, wishspeed, wishspeed, PHYS_ACCELERATE * maxspd_mod, 1, 0, 0, 0);
+       PM_ClientMovement_Move();
+}
+
+void PM_swim(float maxspd_mod)
+{
+       // swimming
+       UNSET_ONGROUND(self);
+
+       float jump = PHYS_INPUT_BUTTON_JUMP(self);
+       // water jump only in certain situations
+       // this mimics quakeworld code
+       if (jump && self.waterlevel == WATERLEVEL_SWIMMING && self.velocity_z >= -180)
+       {
+               vector yawangles = '0 1 0' * self.v_angle.y;
+               makevectors(yawangles);
+               vector forward = v_forward;
+               vector spot = self.origin + 24 * forward;
+               spot_z += 8;
+               traceline(spot, spot, MOVE_NOMONSTERS, self);
+               if (trace_startsolid)
+               {
+                       spot_z += 24;
+                       traceline(spot, spot, MOVE_NOMONSTERS, self);
+                       if (!trace_startsolid)
+                       {
+                               self.velocity = forward * 50;
+                               self.velocity_z = 310;
+                               pmove_waterjumptime = 2;
+                               UNSET_ONGROUND(self);
+                               SET_JUMP_HELD(self);
+                       }
+               }
+       }
+       makevectors(self.v_angle);
+       //wishvel = v_forward * self.movement.x + v_right * self.movement.y + v_up * self.movement.z;
+       vector wishvel = v_forward * self.movement.x
+                                       + v_right * self.movement.y
+                                       + '0 0 1' * self.movement.z;
+       if (wishvel == '0 0 0')
+               wishvel = '0 0 -60'; // drift towards bottom
+
+       vector wishdir = normalize(wishvel);
+       float wishspeed = min(vlen(wishvel), PHYS_MAXSPEED(self) * maxspd_mod) * 0.7;
+
+       if (IS_DUCKED(self))
+       wishspeed *= 0.5;
+
+//     if (pmove_waterjumptime <= 0) // TODO: use
+    {
+               // water friction
+               float f = 1 - PHYS_INPUT_TIMELENGTH * PHYS_FRICTION;
+               f = min(max(0, f), 1);
+               self.velocity *= f;
+
+               f = wishspeed - self.velocity * wishdir;
+               if (f > 0)
+               {
+                       float accelspeed = min(PHYS_ACCELERATE * PHYS_INPUT_TIMELENGTH * wishspeed, f);
+                       self.velocity += accelspeed * wishdir;
+               }
+
+               // holding jump button swims upward slowly
+               if (jump)
+               {
+#if 0
+                       if (self.watertype & CONTENT_LAVA)
+                               self.velocity_z =  50;
+                       else if (self.watertype & CONTENT_SLIME)
+                               self.velocity_z =  80;
+                       else
+                       {
+                               if (IS_NEXUIZ_DERIVED(gamemode))
+#endif
+                                       self.velocity_z = 200;
+#if 0
+                               else
+                                       self.velocity_z = 100;
+                       }
+#endif
+               }
+       }
+       // water acceleration
+       PM_Accelerate(wishdir, wishspeed, wishspeed, PHYS_ACCELERATE * maxspd_mod, 1, 0, 0, 0);
+       PM_ClientMovement_Move();
+}
+
+void PM_ladder(float maxspd_mod)
+{
+       // on a spawnfunc_func_ladder or swimming in spawnfunc_func_water
+       UNSET_ONGROUND(self);
+
+       float g;
+       g = PHYS_GRAVITY * PHYS_INPUT_TIMELENGTH;
+       if (PHYS_ENTGRAVITY(self))
+               g *= PHYS_ENTGRAVITY(self);
+       if (GAMEPLAYFIX_GRAVITYUNAFFECTEDBYTICRATE)
+       {
+               g *= 0.5;
+               self.velocity_z += g;
+       }
+
+       self.velocity = self.velocity * (1 - PHYS_INPUT_TIMELENGTH * PHYS_FRICTION);
+       makevectors(self.v_angle);
+       //wishvel = v_forward * self.movement.x + v_right * self.movement.y + v_up * self.movement.z;
+       vector wishvel = v_forward * self.movement_x
+                                       + v_right * self.movement_y
+                                       + '0 0 1' * self.movement_z;
+       self.velocity_z += g;
+       if (self.ladder_entity.classname == "func_water")
+       {
+               float f = vlen(wishvel);
+               if (f > self.ladder_entity.speed)
+                       wishvel *= (self.ladder_entity.speed / f);
+
+               self.watertype = self.ladder_entity.skin;
+               f = self.ladder_entity.origin_z + self.ladder_entity.maxs_z;
+               if ((self.origin_z + self.view_ofs_z) < f)
+                       self.waterlevel = WATERLEVEL_SUBMERGED;
+               else if ((self.origin_z + (self.mins_z + self.maxs_z) * 0.5) < f)
+                       self.waterlevel = WATERLEVEL_SWIMMING;
+               else if ((self.origin_z + self.mins_z + 1) < f)
+                       self.waterlevel = WATERLEVEL_WETFEET;
+               else
+               {
+                       self.waterlevel = WATERLEVEL_NONE;
+                       self.watertype = CONTENT_EMPTY;
+               }
+       }
+       // acceleration
+       vector wishdir = normalize(wishvel);
+       float wishspeed = min(vlen(wishvel), PHYS_MAXSPEED(self) * maxspd_mod);
+#ifdef SVQC
+       if (time >= self.teleport_time)
+#endif
+               // water acceleration
+               PM_Accelerate(wishdir, wishspeed, wishspeed, PHYS_ACCELERATE*maxspd_mod, 1, 0, 0, 0);
+       PM_ClientMovement_Move();
+}
+
+void PM_jetpack(float maxspd_mod)
+{
+       //makevectors(self.v_angle.y * '0 1 0');
+       makevectors(self.v_angle);
+       vector wishvel = v_forward * self.movement_x
+                                       + v_right * self.movement_y;
+       // add remaining speed as Z component
+       float maxairspd = PHYS_MAXAIRSPEED(self) * max(1, maxspd_mod);
+       // fix speedhacks :P
+       wishvel = normalize(wishvel) * min(1, vlen(wishvel) / maxairspd);
+       // add the unused velocity as up component
+       wishvel_z = 0;
+
+       // if (self.BUTTON_JUMP)
+               wishvel_z = sqrt(max(0, 1 - wishvel * wishvel));
+
+       // it is now normalized, so...
+       float a_side = PHYS_JETPACK_ACCEL_SIDE;
+       float a_up = PHYS_JETPACK_ACCEL_UP;
+       float a_add = PHYS_JETPACK_ANTIGRAVITY * PHYS_GRAVITY;
+
+       wishvel_x *= a_side;
+       wishvel_y *= a_side;
+       wishvel_z *= a_up;
+       wishvel_z += a_add;
+
+       float best = 0;
+       //////////////////////////////////////////////////////////////////////////////////////
+       // finding the maximum over all vectors of above form
+       // with wishvel having an absolute value of 1
+       //////////////////////////////////////////////////////////////////////////////////////
+       // we're finding the maximum over
+       //   f(a_side, a_up, a_add, z) := a_side * (1 - z^2) + (a_add + a_up * z)^2;
+       // for z in the range from -1 to 1
+       //////////////////////////////////////////////////////////////////////////////////////
+       // maximum is EITHER attained at the single extreme point:
+       float a_diff = a_side * a_side - a_up * a_up;
+       float f;
+       if (a_diff != 0)
+       {
+               f = a_add * a_up / a_diff; // this is the zero of diff(f(a_side, a_up, a_add, z), z)
+               if (f > -1 && f < 1) // can it be attained?
+               {
+                       best = (a_diff + a_add * a_add) * (a_diff + a_up * a_up) / a_diff;
+                       //print("middle\n");
+               }
+       }
+       // OR attained at z = 1:
+       f = (a_up + a_add) * (a_up + a_add);
+       if (f > best)
+       {
+               best = f;
+               //print("top\n");
+       }
+       // OR attained at z = -1:
+       f = (a_up - a_add) * (a_up - a_add);
+       if (f > best)
+       {
+               best = f;
+               //print("bottom\n");
+       }
+       best = sqrt(best);
+       //////////////////////////////////////////////////////////////////////////////////////
+
+       //print("best possible acceleration: ", ftos(best), "\n");
+
+       float fxy, fz;
+       fxy = bound(0, 1 - (self.velocity * normalize(wishvel_x * '1 0 0' + wishvel_y * '0 1 0')) / PHYS_JETPACK_MAXSPEED_SIDE, 1);
+       if (wishvel_z - PHYS_GRAVITY > 0)
+               fz = bound(0, 1 - self.velocity_z / PHYS_JETPACK_MAXSPEED_UP, 1);
+       else
+               fz = bound(0, 1 + self.velocity_z / PHYS_JETPACK_MAXSPEED_UP, 1);
+
+       float fvel;
+       fvel = vlen(wishvel);
+       wishvel_x *= fxy;
+       wishvel_y *= fxy;
+       wishvel_z = (wishvel_z - PHYS_GRAVITY) * fz + PHYS_GRAVITY;
+
+       fvel = min(1, vlen(wishvel) / best);
+       if (PHYS_JETPACK_FUEL && !(ITEMS_STAT(self) & IT_UNLIMITED_WEAPON_AMMO))
+               f = min(1, PHYS_AMMO_FUEL(self) / (PHYS_JETPACK_FUEL * PHYS_INPUT_TIMELENGTH * fvel));
+       else
+               f = 1;
+
+       //print("this acceleration: ", ftos(vlen(wishvel) * f), "\n");
+
+       if (f > 0 && wishvel != '0 0 0')
+       {
+               self.velocity = self.velocity + wishvel * f * PHYS_INPUT_TIMELENGTH;
+               UNSET_ONGROUND(self);
+
+#ifdef SVQC
+               if (!(ITEMS_STAT(self) & IT_UNLIMITED_WEAPON_AMMO))
+                       self.ammo_fuel -= PHYS_JETPACK_FUEL * PHYS_INPUT_TIMELENGTH * fvel * f;
+
+               ITEMS_STAT(self) |= IT_USING_JETPACK;
+
+               // jetpack also inhibits health regeneration, but only for 1 second
+               self.pauseregen_finished = max(self.pauseregen_finished, time + autocvar_g_balance_pause_fuel_regen);
+#endif
+       }
+
+#ifdef CSQC
+       float g = PHYS_GRAVITY * PHYS_ENTGRAVITY(self) * PHYS_INPUT_TIMELENGTH;
+       if (GAMEPLAYFIX_GRAVITYUNAFFECTEDBYTICRATE)
+               self.velocity_z -= g * 0.5;
+       else
+               self.velocity_z -= g;
+       PM_ClientMovement_Move();
+       if (!IS_ONGROUND(self) || !(GAMEPLAYFIX_NOGRAVITYONGROUND))
+               if (GAMEPLAYFIX_GRAVITYUNAFFECTEDBYTICRATE)
+                       self.velocity_z -= g * 0.5;
+#endif
+}
+
+void PM_walk(float buttons_prev, float maxspd_mod)
+{
+       if (!WAS_ONGROUND(self))
+       {
+#ifdef SVQC
+               if (autocvar_speedmeter)
+                       dprint(strcat("landing velocity: ", vtos(self.velocity), " (abs: ", ftos(vlen(self.velocity)), ")\n"));
+#endif
+               if (self.lastground < time - 0.3)
+                       self.velocity *= (1 - PHYS_FRICTION_ONLAND);
+#ifdef SVQC
+               if (self.jumppadcount > 1)
+                       dprint(strcat(ftos(self.jumppadcount), "x jumppad combo\n"));
+               self.jumppadcount = 0;
+#endif
+       }
+
+       // walking
+       makevectors(self.v_angle.y * '0 1 0');
+       vector wishvel = v_forward * self.movement.x
+                                       + v_right * self.movement.y;
+       // acceleration
+       vector wishdir = normalize(wishvel);
+       float wishspeed = vlen(wishvel);
+
+       wishspeed = min(wishspeed, PHYS_MAXSPEED(self) * maxspd_mod);
+       if (IS_DUCKED(self))
+               wishspeed *= 0.5;
+
+       // apply edge friction
+       float f = vlen(vec2(self.velocity));
+       if (f > 0)
+       {
+               float realfriction;
+               trace_dphitq3surfaceflags = 0;
+               tracebox(self.origin, self.mins, self.maxs, self.origin - '0 0 1', MOVE_NOMONSTERS, self);
+               // TODO: apply edge friction
+               // apply ground friction
+               if(trace_dphitq3surfaceflags & Q3SURFACEFLAG_SLICK)
+                       realfriction = PHYS_FRICTION_SLICK;
+               else
+                       realfriction = PHYS_FRICTION;
+
+               f = 1 - PHYS_INPUT_TIMELENGTH * realfriction * ((f < PHYS_STOPSPEED) ? (PHYS_STOPSPEED / f) : 1);
+               f = max(0, f);
+               self.velocity *= f;
+               /*
+                  Mathematical analysis time!
+
+                  Our goal is to invert this mess.
+
+                  For the two cases we get:
+                       v = v0 * (1 - PHYS_INPUT_TIMELENGTH * (PHYS_STOPSPEED / v0) * PHYS_FRICTION)
+                         = v0 - PHYS_INPUT_TIMELENGTH * PHYS_STOPSPEED * PHYS_FRICTION
+                       v0 = v + PHYS_INPUT_TIMELENGTH * PHYS_STOPSPEED * PHYS_FRICTION
+                  and
+                       v = v0 * (1 - PHYS_INPUT_TIMELENGTH * PHYS_FRICTION)
+                       v0 = v / (1 - PHYS_INPUT_TIMELENGTH * PHYS_FRICTION)
+
+                  These cases would be chosen ONLY if:
+                       v0 < PHYS_STOPSPEED
+                       v + PHYS_INPUT_TIMELENGTH * PHYS_STOPSPEED * PHYS_FRICTION < PHYS_STOPSPEED
+                       v < PHYS_STOPSPEED * (1 - PHYS_INPUT_TIMELENGTH * PHYS_FRICTION)
+                  and, respectively:
+                       v0 >= PHYS_STOPSPEED
+                       v / (1 - PHYS_INPUT_TIMELENGTH * PHYS_FRICTION) >= PHYS_STOPSPEED
+                       v >= PHYS_STOPSPEED * (1 - PHYS_INPUT_TIMELENGTH * PHYS_FRICTION)
+                */
+       }
+       float addspeed = wishspeed - self.velocity * wishdir;
+       if (addspeed > 0)
+       {
+               float accelspeed = min(PHYS_ACCELERATE * PHYS_INPUT_TIMELENGTH * wishspeed, addspeed);
+               self.velocity += accelspeed * wishdir;
+       }
+       float g = PHYS_GRAVITY * PHYS_ENTGRAVITY(self) * PHYS_INPUT_TIMELENGTH;
+       if (!(GAMEPLAYFIX_NOGRAVITYONGROUND))
+               self.velocity_z -= g * (GAMEPLAYFIX_GRAVITYUNAFFECTEDBYTICRATE ? 0.5 : 1);
+       if (self.velocity * self.velocity)
+               PM_ClientMovement_Move();
+       if (GAMEPLAYFIX_GRAVITYUNAFFECTEDBYTICRATE)
+               if (!IS_ONGROUND(self) || !GAMEPLAYFIX_NOGRAVITYONGROUND)
+                       self.velocity_z -= g * 0.5;
+}
+
+void PM_air(float buttons_prev, float maxspd_mod)
+{
+       makevectors(self.v_angle.y * '0 1 0');
+       vector wishvel = v_forward * self.movement.x
+                                       + v_right * self.movement.y;
+       // acceleration
+       vector wishdir = normalize(wishvel);
+       float wishspeed = vlen(wishvel);
+
+#ifdef SVQC
+       if (time >= self.teleport_time)
+#else
+       if (pmove_waterjumptime <= 0)
+#endif
+       {
+               float maxairspd = PHYS_MAXAIRSPEED(self) * min(maxspd_mod, 1);
+
+               // apply air speed limit
+               float airaccelqw = PHYS_AIRACCEL_QW(self);
+               float wishspeed0 = wishspeed;
+               wishspeed = min(wishspeed, maxairspd);
+               if (IS_DUCKED(self))
+                       wishspeed *= 0.5;
+               float airaccel = PHYS_AIRACCELERATE * min(maxspd_mod, 1);
+
+               float accelerating = (self.velocity * wishdir > 0);
+               float wishspeed2 = wishspeed;
+
+               // CPM: air control
+               if (PHYS_AIRSTOPACCELERATE)
+               {
+                       vector curdir = normalize(vec2(self.velocity));
+                       airaccel += (PHYS_AIRSTOPACCELERATE*maxspd_mod - airaccel) * max(0, -(curdir * wishdir));
+               }
+               // note that for straight forward jumping:
+               // step = accel * PHYS_INPUT_TIMELENGTH * wishspeed0;
+               // accel  = bound(0, wishspeed - vel_xy_current, step) * accelqw + step * (1 - accelqw);
+               // -->
+               // dv/dt = accel * maxspeed (when slow)
+               // dv/dt = accel * maxspeed * (1 - accelqw) (when fast)
+               // log dv/dt = logaccel + logmaxspeed (when slow)
+               // log dv/dt = logaccel + logmaxspeed + log(1 - accelqw) (when fast)
+               float strafity = IsMoveInDirection(self.movement, -90) + IsMoveInDirection(self.movement, +90); // if one is nonzero, other is always zero
+               if (PHYS_MAXAIRSTRAFESPEED)
+                       wishspeed = min(wishspeed, GeomLerp(PHYS_MAXAIRSPEED(self)*maxspd_mod, strafity, PHYS_MAXAIRSTRAFESPEED*maxspd_mod));
+               if (PHYS_AIRSTRAFEACCELERATE(self))
+                       airaccel = GeomLerp(airaccel, strafity, PHYS_AIRSTRAFEACCELERATE(self)*maxspd_mod);
+               if (PHYS_AIRSTRAFEACCEL_QW(self))
+                       airaccelqw =
+               (((strafity > 0.5 ? PHYS_AIRSTRAFEACCEL_QW(self) : PHYS_AIRACCEL_QW(self)) >= 0) ? +1 : -1)
+               *
+               (1 - GeomLerp(1 - fabs(PHYS_AIRACCEL_QW(self)), strafity, 1 - fabs(PHYS_AIRSTRAFEACCEL_QW(self))));
+               // !CPM
+
+               if (PHYS_WARSOWBUNNY_TURNACCEL && accelerating && self.movement.y == 0 && self.movement.x != 0)
+                       PM_AirAccelerate(wishdir, wishspeed2);
+               else
+                       PM_Accelerate(wishdir, wishspeed, wishspeed0, airaccel, airaccelqw, PHYS_AIRACCEL_QW_STRETCHFACTOR(self), PHYS_AIRACCEL_SIDEWAYS_FRICTION / maxairspd, PHYS_AIRSPEEDLIMIT_NONQW(self));
+
+               if (PHYS_AIRCONTROL)
+                       CPM_PM_Aircontrol(wishdir, wishspeed2);
+       }
+       float g = PHYS_GRAVITY * PHYS_ENTGRAVITY(self) * PHYS_INPUT_TIMELENGTH;
+       if (GAMEPLAYFIX_GRAVITYUNAFFECTEDBYTICRATE)
+               self.velocity_z -= g * 0.5;
+       else
+               self.velocity_z -= g;
+       PM_ClientMovement_Move();
+       if (!IS_ONGROUND(self) || !(GAMEPLAYFIX_NOGRAVITYONGROUND))
+               if (GAMEPLAYFIX_GRAVITYUNAFFECTEDBYTICRATE)
+                       self.velocity_z -= g * 0.5;
+}
+
+// used for calculating airshots
+bool IsFlying(entity a)
+{
+       if(IS_ONGROUND(a))
+               return false;
+       if(a.waterlevel >= WATERLEVEL_SWIMMING)
+               return false;
+       traceline(a.origin, a.origin - '0 0 48', MOVE_NORMAL, a);
+       if(trace_fraction < 1)
+               return false;
+       return true;
+}
+
+void PM_Main()
+{
+       int buttons = PHYS_INPUT_BUTTON_MASK(self);
+#ifdef CSQC
+       self.items = getstati(STAT_ITEMS, 0, 24);
+
+       self.movement = PHYS_INPUT_MOVEVALUES(self);
+
+       vector oldv_angle = self.v_angle;
+       vector oldangles = self.angles; // we need to save these, as they're abused by other code
+       self.v_angle = PHYS_INPUT_ANGLES(self);
+       self.angles = PHYS_WORLD_ANGLES(self);
+
+       self.team = myteam + 1; // is this correct?
+       if (!(PHYS_INPUT_BUTTON_JUMP(self))) // !jump
+               UNSET_JUMP_HELD(self); // canjump = true
+       pmove_waterjumptime -= PHYS_INPUT_TIMELENGTH;
+
+       PM_ClientMovement_UpdateStatus(true);
+#endif
+       
+
+#ifdef SVQC
+       WarpZone_PlayerPhysics_FixVAngle();
+#endif
+       float maxspeed_mod = 1;
+       maxspeed_mod *= PM_check_keepaway();
+       maxspeed_mod *= PHYS_HIGHSPEED;
+
+#ifdef SVQC
+       Physics_UpdateStats(maxspeed_mod);
+
+       if (self.PlayerPhysplug)
+               if (self.PlayerPhysplug())
+                       return;
+#endif
+
+       PM_check_race_movetime();
+#ifdef SVQC
+       anticheat_physics();
+#endif
+
+       if (PM_check_specialcommand(buttons))
+               return;
+#ifdef SVQC
+       if (sv_maxidle > 0)
+       {
+               if (buttons != self.buttons_old || self.movement != self.movement_old || self.v_angle != self.v_angle_old)
+                       self.parm_idlesince = time;
+       }
+#endif
+       int buttons_prev = self.buttons_old;
+       self.buttons_old = buttons;
+       self.movement_old = self.movement;
+       self.v_angle_old = self.v_angle;
+
+       PM_check_nickspam();
+
+       PM_check_punch();
+#ifdef SVQC
+       if (IS_BOT_CLIENT(self))
+       {
+               if (playerdemo_read())
+                       return;
+               bot_think();
+       }
+
+       if (IS_PLAYER(self))
+#endif
+       {
+#ifdef SVQC
+               if (self.race_penalty)
+                       if (time > self.race_penalty)
+                               self.race_penalty = 0;
+#endif
+
+               bool not_allowed_to_move = false;
+#ifdef SVQC
+               if (self.race_penalty)
+                       not_allowed_to_move = true;
+#endif
+#ifdef SVQC
+               if (time < game_starttime)
+                       not_allowed_to_move = true;
+#endif
+
+               if (not_allowed_to_move)
+               {
+                       self.velocity = '0 0 0';
+                       self.movetype = MOVETYPE_NONE;
+#ifdef SVQC
+                       self.disableclientprediction = 2;
+#endif
+               }
+#ifdef SVQC
+               else if (self.disableclientprediction == 2)
+               {
+                       if (self.movetype == MOVETYPE_NONE)
+                               self.movetype = MOVETYPE_WALK;
+                       self.disableclientprediction = 0;
+               }
+#endif
+       }
+
+#ifdef SVQC
+       if (self.movetype == MOVETYPE_NONE)
+               return;
+
+       // when we get here, disableclientprediction cannot be 2
+       self.disableclientprediction = 0;
+#endif
+
+       PM_check_spider();
+
+       PM_check_frozen();
+
+       PM_check_blocked();
+
+       maxspeed_mod = 1;
+
+       if (self.in_swamp)
+               maxspeed_mod *= self.swamp_slowdown; //cvar("g_balance_swamp_moverate");
+
+       // conveyors: first fix velocity
+       if (self.conveyor.state)
+               self.velocity -= self.conveyor.movedir;
+
+#ifdef SVQC
+       MUTATOR_CALLHOOK(PlayerPhysics);
+#endif
+#ifdef CSQC
+       PM_multijump();
+#endif
+
+//     float forcedodge = 1;
+//     if(forcedodge) {
+//#ifdef CSQC
+//             PM_dodging_checkpressedkeys();
+//#endif
+//             PM_dodging();
+//             PM_ClientMovement_Move();
+//             return;
+//     }
+
+#ifdef SVQC
+       if (!IS_PLAYER(self))
+       {
+               maxspeed_mod = autocvar_sv_spectator_speed_multiplier;
+               if (!self.spectatorspeed)
+                       self.spectatorspeed = maxspeed_mod;
+               if (self.impulse && self.impulse <= 19 || (self.impulse >= 200 && self.impulse <= 209) || (self.impulse >= 220 && self.impulse <= 229))
+               {
+                       if (self.lastclassname != "player")
+                       {
+                               if (self.impulse == 10 || self.impulse == 15 || self.impulse == 18 || (self.impulse >= 200 && self.impulse <= 209))
+                                       self.spectatorspeed = bound(1, self.spectatorspeed + 0.5, 5);
+                               else if (self.impulse == 11)
+                                       self.spectatorspeed = maxspeed_mod;
+                               else if (self.impulse == 12 || self.impulse == 16  || self.impulse == 19 || (self.impulse >= 220 && self.impulse <= 229))
+                                       self.spectatorspeed = bound(1, self.spectatorspeed - 0.5, 5);
+                               else if (self.impulse >= 1 && self.impulse <= 9)
+                                       self.spectatorspeed = 1 + 0.5 * (self.impulse - 1);
+                       } // otherwise just clear
+                       self.impulse = 0;
+               }
+               maxspeed_mod = self.spectatorspeed;
+       }
+
+       float spd = max(PHYS_MAXSPEED(self), PHYS_MAXAIRSPEED(self)) * maxspeed_mod;
+       if(self.speed != spd)
+       {
+               self.speed = spd;
+               string temps = ftos(spd);
+               stuffcmd(self, strcat("cl_forwardspeed ", temps, "\n"));
+               stuffcmd(self, strcat("cl_backspeed ", temps, "\n"));
+               stuffcmd(self, strcat("cl_sidespeed ", temps, "\n"));
+               stuffcmd(self, strcat("cl_upspeed ", temps, "\n"));
+       }
+#endif
+
+       if(PHYS_DEAD(self))
+       {
+               // handle water here
+               vector midpoint = ((self.absmin + self.absmax) * 0.5);
+               if(pointcontents(midpoint) == CONTENT_WATER)
+               {
+                       self.velocity = self.velocity * 0.5;
+
+                       // do we want this?
+                       //if(pointcontents(midpoint + '0 0 2') == CONTENT_WATER)
+                               //{ self.velocity_z = 70; }
+               }
+               goto end;
+       }
+
+#ifdef SVQC
+       if (!self.fixangle && !g_bugrigs)
+               self.angles = '0 1 0' * self.v_angle.y;
+#endif
+
+       PM_check_hitground();
+
+       if(IsFlying(self))
+               self.wasFlying = 1;
+
+       if (IS_PLAYER(self))
+               CheckPlayerJump();
+
+       if (self.flags & FL_WATERJUMP)
+       {
+               self.velocity_x = self.movedir_x;
+               self.velocity_y = self.movedir_y;
+               if (time > self.teleport_time || self.waterlevel == WATERLEVEL_NONE)
+               {
+                       self.flags &= ~FL_WATERJUMP;
+                       self.teleport_time = 0;
+               }
+       }
+
+#ifdef SVQC
+       else if (g_bugrigs && IS_PLAYER(self))
+               RaceCarPhysics();
+#endif
+
+       else if (self.movetype == MOVETYPE_NOCLIP || self.movetype == MOVETYPE_FLY || self.movetype == MOVETYPE_FLY_WORLDONLY || (BUFFS(self) & BUFF_FLIGHT))
+               PM_fly(maxspeed_mod);
+
+       else if (self.waterlevel >= WATERLEVEL_SWIMMING)
+               PM_swim(maxspeed_mod);
+
+       else if (time < self.ladder_time)
+               PM_ladder(maxspeed_mod);
+
+       else if (ITEMS_STAT(self) & IT_USING_JETPACK)
+               PM_jetpack(maxspeed_mod);
+
+       else if (IS_ONGROUND(self))
+               PM_walk(buttons_prev, maxspeed_mod);
+
+       else
+               PM_air(buttons_prev, maxspeed_mod);
+
+#ifdef SVQC
+       if (!IS_OBSERVER(self))
+               PM_check_race();
+#endif
+       PM_check_vortex();
+
+:end
+       if (IS_ONGROUND(self))
+               self.lastground = time;
+
+       // conveyors: then break velocity again
+       if(self.conveyor.state)
+               self.velocity += self.conveyor.movedir;
+
+       self.lastflags = self.flags;
+
+       self.lastclassname = self.classname;
+
+#ifdef CSQC
+       self.v_angle = oldv_angle;
+       self.angles = oldangles;
+#endif
+}
+
+#ifdef SVQC
+void SV_PlayerPhysics(void)
+#elif defined(CSQC)
+void CSQC_ClientMovement_PlayerMove_Frame(void)
+#endif
+{
+       PM_Main();
+
+#ifdef CSQC
+       self.pmove_flags = 
+                       ((self.flags & FL_DUCKED) ? PMF_DUCKED : 0) |
+                       (!(self.flags & FL_JUMPRELEASED) ? 0 : PMF_JUMP_HELD) |
+                       ((self.flags & FL_ONGROUND) ? PMF_ONGROUND : 0);
+#endif
+}
diff --git a/qcsrc/common/physics.qh b/qcsrc/common/physics.qh
new file mode 100644 (file)
index 0000000..17de7b2
--- /dev/null
@@ -0,0 +1,372 @@
+#ifndef COMMON_PHYSICS_H
+#define COMMON_PHYSICS_H
+
+// Client/server mappings
+
+.entity conveyor;
+
+.float race_penalty;
+
+.float gravity;
+.float swamp_slowdown;
+.float lastflags;
+.float lastground;
+.float wasFlying;
+.float spectatorspeed;
+
+.vector movement_old;
+.float buttons_old;
+.vector v_angle_old;
+.string lastclassname;
+
+.float() PlayerPhysplug;
+float AdjustAirAccelQW(float accelqw, float factor);
+
+bool IsFlying(entity a);
+
+#ifdef CSQC
+
+       const int FL_WATERJUMP = 2048;  // player jumping out of water
+       const int FL_JUMPRELEASED = 4096;       // for jump debouncing
+
+       float PM_multijump_checkjump();
+       void PM_multijump();
+
+       .float watertype;
+       .int items;
+
+       .vector movement;
+       .vector v_angle;
+
+// TODO
+       #define IS_CLIENT(s)                                            (s).isplayermodel
+       #define IS_PLAYER(s)                                            (s).isplayermodel
+       #define isPushable(s)                                           (s).isplayermodel
+
+       float player_multijump;
+       float player_jumpheight;
+
+       #define PHYS_INPUT_ANGLES(s)                            input_angles
+// TODO
+       #define PHYS_WORLD_ANGLES(s)                            input_angles
+
+       #define PHYS_INPUT_TIMELENGTH                           input_timelength
+       #define PHYS_INPUT_FRAMETIME                            serverdeltatime
+
+       #define PHYS_INPUT_MOVEVALUES(s)                        input_movevalues
+
+       #define PHYS_INPUT_BUTTON_MASK(s)               (input_buttons | 128 * (input_movevalues_x < 0) | 256 * (input_movevalues_x > 0) | 512 * (input_movevalues_y < 0) | 1024 * (input_movevalues_y > 0))
+       #define PHYS_INPUT_BUTTON_ATCK(s)                       !!(input_buttons & 1)
+       #define PHYS_INPUT_BUTTON_JUMP(s)                       !!(input_buttons & 2)
+       #define PHYS_INPUT_BUTTON_ATCK2(s)                      !!(input_buttons & 4)
+       #define PHYS_INPUT_BUTTON_ZOOM(s)                       !!(input_buttons & 8)
+       #define PHYS_INPUT_BUTTON_CROUCH(s)                     !!(input_buttons & 16)
+       #define PHYS_INPUT_BUTTON_HOOK(s)                       !!(input_buttons & 32)
+       #define PHYS_INPUT_BUTTON_USE(s)                        !!(input_buttons & 64)
+       #define PHYS_INPUT_BUTTON_BACKWARD(s)           !!(input_buttons & 128)
+       #define PHYS_INPUT_BUTTON_FORWARD(s)            !!(input_buttons & 256)
+       #define PHYS_INPUT_BUTTON_LEFT(s)                       !!(input_buttons & 512)
+       #define PHYS_INPUT_BUTTON_RIGHT(s)                      !!(input_buttons & 1024)
+       #define PHYS_INPUT_BUTTON_JETPACK(s)            !!(input_buttons & 4096)
+
+       #define PHYS_DEAD(s)                                            s.csqcmodel_isdead
+
+       #define GAMEPLAYFIX_GRAVITYUNAFFECTEDBYTICRATE  !!(moveflags & MOVEFLAG_GRAVITYUNAFFECTEDBYTICRATE)
+       #define GAMEPLAYFIX_NOGRAVITYONGROUND                   cvar("sv_gameplayfix_nogravityonground")
+       #define GAMEPLAYFIX_Q2AIRACCELERATE                             cvar("sv_gameplayfix_q2airaccelerate")
+       #define GAMEPLAYFIX_EASIERWATERJUMP                     getstati(STAT_GAMEPLAYFIX_EASIERWATERJUMP)
+       #define GAMEPLAYFIX_DOWNTRACEONGROUND                   getstati(STAT_GAMEPLAYFIX_DOWNTRACEONGROUND)
+       #define GAMEPLAYFIX_STEPMULTIPLETIMES                   getstati(STAT_GAMEPLAYFIX_STEPMULTIPLETIMES)
+       #define GAMEPLAYFIX_UNSTICKPLAYERS                              getstati(STAT_GAMEPLAYFIX_UNSTICKPLAYERS)
+       #define GAMEPLAYFIX_STEPDOWN                                    getstati(STAT_GAMEPLAYFIX_STEPDOWN)
+
+       #define IS_DUCKED(s)                                            !!(s.flags & FL_DUCKED)
+       #define SET_DUCKED(s)                                           s.flags |= FL_DUCKED
+       #define UNSET_DUCKED(s)                                         s.flags &= ~FL_DUCKED
+
+       #define IS_JUMP_HELD(s)                                         !(s.flags & FL_JUMPRELEASED)
+       #define SET_JUMP_HELD(s)                                        s.flags &= ~FL_JUMPRELEASED
+       #define UNSET_JUMP_HELD(s)                                      s.flags |= FL_JUMPRELEASED
+
+       #define IS_ONGROUND(s)                                          !!(s.flags & FL_ONGROUND)
+       #define SET_ONGROUND(s)                                         s.flags |= FL_ONGROUND
+       #define UNSET_ONGROUND(s)                                       s.flags &= ~FL_ONGROUND
+
+       #define WAS_ONGROUND(s)                                         !!(s.lastflags & FL_ONGROUND)
+
+       #define ITEMS_STAT(s)                                           (s).items
+       #define BUFFS(s)                                                        getstati(STAT_BUFFS)
+
+       #define PHYS_AMMO_FUEL(s)                                       getstati(STAT_FUEL)
+
+       #define PHYS_FROZEN(s)                                          getstati(STAT_FROZEN)
+
+       #define PHYS_DOUBLEJUMP                                         getstati(STAT_DOUBLEJUMP)
+
+       #define PHYS_BUGRIGS                                            getstati(STAT_BUGRIGS)
+       #define PHYS_BUGRIGS_ANGLE_SMOOTHING            getstati(STAT_BUGRIGS_ANGLE_SMOOTHING)
+       #define PHYS_BUGRIGS_PLANAR_MOVEMENT            getstati(STAT_BUGRIGS_PLANAR_MOVEMENT)
+       #define PHYS_BUGRIGS_REVERSE_SPEEDING           getstati(STAT_BUGRIGS_REVERSE_SPEEDING)
+       #define PHYS_BUGRIGS_FRICTION_FLOOR             getstatf(STAT_BUGRIGS_FRICTION_FLOOR)
+       #define PHYS_BUGRIGS_AIR_STEERING                       getstati(STAT_BUGRIGS_AIR_STEERING)
+       #define PHYS_BUGRIGS_FRICTION_BRAKE             getstatf(STAT_BUGRIGS_FRICTION_BRAKE)
+       #define PHYS_BUGRIGS_ACCEL                                      getstatf(STAT_BUGRIGS_ACCEL)
+       #define PHYS_BUGRIGS_SPEED_REF                          getstatf(STAT_BUGRIGS_SPEED_REF)
+       #define PHYS_BUGRIGS_SPEED_POW                          getstatf(STAT_BUGRIGS_SPEED_POW)
+       #define PHYS_BUGRIGS_STEER                                      getstatf(STAT_BUGRIGS_STEER)
+       #define PHYS_BUGRIGS_FRICTION_AIR                       getstatf(STAT_BUGRIGS_FRICTION_AIR)
+       #define PHYS_BUGRIGS_CAR_JUMPING                        getstatf(STAT_BUGRIGS_CAR_JUMPING)
+       #define PHYS_BUGRIGS_REVERSE_SPINNING           getstatf(STAT_BUGRIGS_REVERSE_SPINNING)
+       #define PHYS_BUGRIGS_REVERSE_STOPPING           getstatf(STAT_BUGRIGS_REVERSE_STOPPING)
+
+       #define PHYS_JUMPSPEEDCAP_MIN                           getstatf(STAT_MOVEVARS_JUMPSPEEDCAP_MIN)
+       #define PHYS_JUMPSPEEDCAP_MAX                           getstatf(STAT_MOVEVARS_JUMPSPEEDCAP_MAX)
+       #define PHYS_JUMPSPEEDCAP_DISABLE_ONRAMPS       getstati(STAT_MOVEVARS_JUMPSPEEDCAP_DISABLE_ONRAMPS)
+
+       #define PHYS_TRACK_CANJUMP(s)                           getstati(STAT_MOVEVARS_TRACK_CANJUMP)
+       #define PHYS_ACCELERATE                                         getstatf(STAT_MOVEVARS_ACCELERATE)
+       #define PHYS_AIRACCEL_QW(s)                                     getstatf(STAT_MOVEVARS_AIRACCEL_QW)
+       #define PHYS_AIRACCEL_QW_STRETCHFACTOR(s)       getstatf(STAT_MOVEVARS_AIRACCEL_QW_STRETCHFACTOR)
+       #define PHYS_AIRACCEL_SIDEWAYS_FRICTION         getstatf(STAT_MOVEVARS_AIRACCEL_SIDEWAYS_FRICTION)
+       #define PHYS_AIRACCELERATE                                      getstatf(STAT_MOVEVARS_AIRACCELERATE)
+       #define PHYS_AIRCONTROL                                         getstatf(STAT_MOVEVARS_AIRCONTROL)
+       #define PHYS_AIRCONTROL_PENALTY                         getstatf(STAT_MOVEVARS_AIRCONTROL_PENALTY)
+       #define PHYS_AIRCONTROL_POWER                           getstatf(STAT_MOVEVARS_AIRCONTROL_POWER)
+       #define PHYS_AIRSPEEDLIMIT_NONQW(s)                     getstatf(STAT_MOVEVARS_AIRSPEEDLIMIT_NONQW)
+       #define PHYS_AIRSTOPACCELERATE                          getstatf(STAT_MOVEVARS_AIRSTOPACCELERATE)
+       #define PHYS_AIRSTRAFEACCEL_QW(s)                       getstatf(STAT_MOVEVARS_AIRSTRAFEACCEL_QW)
+       #define PHYS_AIRSTRAFEACCELERATE(s)                     getstatf(STAT_MOVEVARS_AIRSTRAFEACCELERATE)
+       #define PHYS_ENTGRAVITY(s)                                      getstatf(STAT_MOVEVARS_ENTGRAVITY)
+       #define PHYS_FRICTION                                           getstatf(STAT_MOVEVARS_FRICTION)
+       #define PHYS_FRICTION_SLICK                                     getstatf(STAT_MOVEVARS_FRICTION_SLICK)
+       #define PHYS_FRICTION_ONLAND                            getstatf(STAT_MOVEVARS_FRICTION_ONLAND)
+       #define PHYS_GRAVITY                                            getstatf(STAT_MOVEVARS_GRAVITY)
+       #define PHYS_HIGHSPEED                                          getstatf(STAT_MOVEVARS_HIGHSPEED)
+       #define PHYS_JUMPVELOCITY                                       getstatf(STAT_MOVEVARS_JUMPVELOCITY)
+       #define PHYS_MAXAIRSPEED(s)                                     getstatf(STAT_MOVEVARS_MAXAIRSPEED)
+       #define PHYS_MAXAIRSTRAFESPEED                          getstatf(STAT_MOVEVARS_MAXAIRSTRAFESPEED)
+       #define PHYS_MAXSPEED(s)                                        getstatf(STAT_MOVEVARS_MAXSPEED)
+       #define PHYS_STEPHEIGHT                                         getstatf(STAT_MOVEVARS_STEPHEIGHT)
+       #define PHYS_STOPSPEED                                          getstatf(STAT_MOVEVARS_STOPSPEED)
+       #define PHYS_WARSOWBUNNY_ACCEL                          getstatf(STAT_MOVEVARS_WARSOWBUNNY_ACCEL)
+       #define PHYS_WARSOWBUNNY_BACKTOSIDERATIO        getstatf(STAT_MOVEVARS_WARSOWBUNNY_BACKTOSIDERATIO)
+       #define PHYS_WARSOWBUNNY_AIRFORWARDACCEL        getstatf(STAT_MOVEVARS_WARSOWBUNNY_AIRFORWARDACCEL)
+       #define PHYS_WARSOWBUNNY_TOPSPEED                       getstatf(STAT_MOVEVARS_WARSOWBUNNY_TOPSPEED)
+       #define PHYS_WARSOWBUNNY_TURNACCEL                      getstatf(STAT_MOVEVARS_WARSOWBUNNY_TURNACCEL)
+
+       #define PHYS_WALLFRICTION                                       getstati(STAT_MOVEVARS_WALLFRICTION)
+
+       #define PHYS_JETPACK_ACCEL_UP                           getstatf(STAT_JETPACK_ACCEL_UP)
+       #define PHYS_JETPACK_ACCEL_SIDE                         getstatf(STAT_JETPACK_ACCEL_SIDE)
+       #define PHYS_JETPACK_ANTIGRAVITY                        getstatf(STAT_JETPACK_ANTIGRAVITY)
+       #define PHYS_JETPACK_FUEL                                       getstatf(STAT_JETPACK_FUEL)
+       #define PHYS_JETPACK_MAXSPEED_UP                        getstatf(STAT_JETPACK_MAXSPEED_UP)
+       #define PHYS_JETPACK_MAXSPEED_SIDE                      getstatf(STAT_JETPACK_MAXSPEED_SIDE)
+
+       #define PHYS_DODGING_FROZEN                                     getstati(STAT_DODGING_FROZEN)
+
+       #define PHYS_NOSTEP                                                     getstati(STAT_NOSTEP)
+       #define PHYS_JUMPSTEP                                           getstati(STAT_MOVEVARS_JUMPSTEP)
+
+#elif defined(SVQC)
+
+       bool Physics_Valid(string thecvar);
+
+       .vector stat_pl_view_ofs;
+       .vector stat_pl_crouch_view_ofs;
+
+       .vector stat_pl_min;
+       .vector stat_pl_max;
+       .vector stat_pl_crouch_min;
+       .vector stat_pl_crouch_max;
+
+       .float stat_sv_airaccel_qw;
+       .float stat_sv_airstrafeaccel_qw;
+       .float stat_sv_airspeedlimit_nonqw;
+       .float stat_sv_maxspeed;
+       .float stat_movement_highspeed;
+
+       .float stat_sv_friction_on_land;
+       .float stat_sv_friction_slick;
+
+       .float stat_doublejump;
+
+       .float stat_jumpspeedcap_min;
+       .float stat_jumpspeedcap_max;
+       .float stat_jumpspeedcap_disable_onramps;
+
+       .float stat_jetpack_accel_side;
+       .float stat_jetpack_accel_up;
+       .float stat_jetpack_antigravity;
+       .float stat_jetpack_fuel;
+       .float stat_jetpack_maxspeed_up;
+       .float stat_jetpack_maxspeed_side;
+       .float stat_gameplayfix_easierwaterjump;
+       .float stat_gameplayfix_downtracesupportsongroundflag;
+       .float stat_gameplayfix_stepmultipletimes;
+       .float stat_gameplayfix_unstickplayers;
+       .float stat_gameplayfix_stepdown;
+
+       .float stat_bugrigs;
+       .float stat_bugrigs_angle_smoothing;
+       .float stat_bugrigs_planar_movement;
+       .float stat_bugrigs_reverse_speeding;
+       .float stat_bugrigs_friction_floor;
+       .float stat_bugrigs_air_steering;
+       .float stat_bugrigs_friction_brake;
+       .float stat_bugrigs_accel;
+       .float stat_bugrigs_speed_ref;
+       .float stat_bugrigs_speed_pow;
+       .float stat_bugrigs_steer;
+       .float stat_bugrigs_friction_air;
+       .float stat_bugrigs_car_jumping;
+       .float stat_bugrigs_reverse_spinning;
+       .float stat_bugrigs_reverse_stopping;
+
+       // new properties
+       .float stat_sv_jumpvelocity;
+       .float stat_sv_airaccel_qw_stretchfactor;
+       .float stat_sv_maxairstrafespeed;
+       .float stat_sv_maxairspeed;
+       .float stat_sv_airstrafeaccelerate;
+       .float stat_sv_warsowbunny_turnaccel;
+       .float stat_sv_airaccel_sideways_friction;
+       .float stat_sv_aircontrol;
+       .float stat_sv_aircontrol_power;
+       .float stat_sv_aircontrol_penalty;
+       .float stat_sv_warsowbunny_airforwardaccel;
+       .float stat_sv_warsowbunny_topspeed;
+       .float stat_sv_warsowbunny_accel;
+       .float stat_sv_warsowbunny_backtosideratio;
+       .float stat_sv_friction;
+       .float stat_sv_accelerate;
+       .float stat_sv_stopspeed;
+       .float stat_sv_airaccelerate;
+       .float stat_sv_airstopaccelerate;
+
+       .float stat_nostep;
+       .float stat_jumpstep;
+
+       #define PHYS_INPUT_ANGLES(s)                            s.v_angle
+       #define PHYS_WORLD_ANGLES(s)                            s.angles
+
+       #define PHYS_INPUT_TIMELENGTH                           frametime
+       #define PHYS_INPUT_FRAMETIME                            sys_frametime
+
+       #define PHYS_INPUT_MOVEVALUES(s)                        s.movement
+       // TODO: cache
+       #define PHYS_INPUT_BUTTON_MASK(s)               (s.BUTTON_ATCK | 2 * s.BUTTON_JUMP | 4 * s.BUTTON_ATCK2 | 8 * s.BUTTON_ZOOM | 16 * s.BUTTON_CROUCH | 32 * s.BUTTON_HOOK | 64 * s.BUTTON_USE | 128 * (s.movement_x < 0) | 256 * (s.movement_x > 0) | 512 * (s.movement_y < 0) | 1024 * (s.movement_y > 0))
+       #define PHYS_INPUT_BUTTON_ATCK(s)                       s.BUTTON_ATCK
+       #define PHYS_INPUT_BUTTON_JUMP(s)                       s.BUTTON_JUMP
+       #define PHYS_INPUT_BUTTON_ATCK2(s)                      s.BUTTON_ATCK2
+       #define PHYS_INPUT_BUTTON_ZOOM(s)                       s.BUTTON_ZOOM
+       #define PHYS_INPUT_BUTTON_CROUCH(s)                     s.BUTTON_CROUCH
+       #define PHYS_INPUT_BUTTON_HOOK(s)                       s.BUTTON_HOOK
+       #define PHYS_INPUT_BUTTON_USE(s)                        s.BUTTON_USE
+       #define PHYS_INPUT_BUTTON_BACKWARD(s)           (s.movement_x < 0)
+       #define PHYS_INPUT_BUTTON_FORWARD(s)            (s.movement_x > 0)
+       #define PHYS_INPUT_BUTTON_LEFT(s)                       (s.movement_y < 0)
+       #define PHYS_INPUT_BUTTON_RIGHT(s)                      (s.movement_y > 0)
+       #define PHYS_INPUT_BUTTON_JETPACK(s)            s.BUTTON_JETPACK
+
+       #define PHYS_DEAD(s)                                            s.deadflag != DEAD_NO
+
+       #define GAMEPLAYFIX_GRAVITYUNAFFECTEDBYTICRATE  autocvar_sv_gameplayfix_gravityunaffectedbyticrate
+       #define GAMEPLAYFIX_NOGRAVITYONGROUND                   cvar("sv_gameplayfix_nogravityonground")
+       #define GAMEPLAYFIX_Q2AIRACCELERATE                             autocvar_sv_gameplayfix_q2airaccelerate
+       #define GAMEPLAYFIX_EASIERWATERJUMP                             cvar("sv_gameplayfix_easierwaterjump")
+       #define GAMEPLAYFIX_DOWNTRACEONGROUND                   cvar("sv_gameplayfix_downtracesupportsongroundflag")
+       #define GAMEPLAYFIX_STEPMULTIPLETIMES                   cvar("sv_gameplayfix_stepmultipletimes")
+       #define GAMEPLAYFIX_UNSTICKPLAYERS                              cvar("sv_gameplayfix_unstickplayers")
+       #define GAMEPLAYFIX_STEPDOWN                                    cvar("sv_gameplayfix_stepdown")
+
+       #define IS_DUCKED(s)                                            s.crouch
+       #define SET_DUCKED(s)                                           s.crouch = true
+       #define UNSET_DUCKED(s)                                         s.crouch = false
+
+       #define IS_JUMP_HELD(s)                                         !(s.flags & FL_JUMPRELEASED)
+       #define SET_JUMP_HELD(s)                                        s.flags &= ~FL_JUMPRELEASED
+       #define UNSET_JUMP_HELD(s)                                      s.flags |= FL_JUMPRELEASED
+
+       #define IS_ONGROUND(s)                                          !!(s.flags & FL_ONGROUND)
+       #define SET_ONGROUND(s)                                         s.flags |= FL_ONGROUND
+       #define UNSET_ONGROUND(s)                                       s.flags &= ~FL_ONGROUND
+
+       #define WAS_ONGROUND(s)                                         !!((s).lastflags & FL_ONGROUND)
+
+       #define ITEMS_STAT(s)                                           s.items
+       #define BUFFS(s)                                                        (s).buffs
+
+       #define PHYS_AMMO_FUEL(s)                                       s.ammo_fuel
+
+       #define PHYS_FROZEN(s)                                          s.frozen
+
+       #define PHYS_DOUBLEJUMP                                         autocvar_sv_doublejump
+
+       #define PHYS_BUGRIGS                                            g_bugrigs
+       #define PHYS_BUGRIGS_ANGLE_SMOOTHING            g_bugrigs_angle_smoothing
+       #define PHYS_BUGRIGS_PLANAR_MOVEMENT            g_bugrigs_planar_movement
+       #define PHYS_BUGRIGS_REVERSE_SPEEDING           g_bugrigs_reverse_speeding
+       #define PHYS_BUGRIGS_FRICTION_FLOOR                     g_bugrigs_friction_floor
+       #define PHYS_BUGRIGS_AIR_STEERING                       g_bugrigs_air_steering
+       #define PHYS_BUGRIGS_FRICTION_BRAKE                     g_bugrigs_friction_brake
+       #define PHYS_BUGRIGS_ACCEL                                      g_bugrigs_accel
+       #define PHYS_BUGRIGS_SPEED_REF                          g_bugrigs_speed_ref
+       #define PHYS_BUGRIGS_SPEED_POW                          g_bugrigs_speed_pow
+       #define PHYS_BUGRIGS_STEER                                      g_bugrigs_steer
+       #define PHYS_BUGRIGS_FRICTION_AIR                       g_bugrigs_friction_air
+       #define PHYS_BUGRIGS_CAR_JUMPING                        g_bugrigs_planar_movement_car_jumping
+       #define PHYS_BUGRIGS_REVERSE_SPINNING           g_bugrigs_reverse_spinning
+       #define PHYS_BUGRIGS_REVERSE_STOPPING           g_bugrigs_reverse_stopping
+
+       #define PHYS_JUMPSPEEDCAP_MIN                           autocvar_sv_jumpspeedcap_min
+       #define PHYS_JUMPSPEEDCAP_MAX                           autocvar_sv_jumpspeedcap_max
+       #define PHYS_JUMPSPEEDCAP_DISABLE_ONRAMPS       autocvar_sv_jumpspeedcap_max_disable_on_ramps
+
+       #define PHYS_TRACK_CANJUMP(s)                           s.cvar_cl_movement_track_canjump
+       #define PHYS_ACCELERATE                                         self.stat_sv_accelerate
+       #define PHYS_AIRACCEL_QW(s)                                     s.stat_sv_airaccel_qw
+       #define PHYS_AIRACCEL_QW_STRETCHFACTOR(s)       self.stat_sv_airaccel_qw_stretchfactor
+       #define PHYS_AIRACCEL_SIDEWAYS_FRICTION         self.stat_sv_airaccel_sideways_friction
+       #define PHYS_AIRACCELERATE                                      self.stat_sv_airaccelerate
+       #define PHYS_AIRCONTROL                                         self.stat_sv_aircontrol
+       #define PHYS_AIRCONTROL_PENALTY                         self.stat_sv_aircontrol_penalty
+       #define PHYS_AIRCONTROL_POWER                           self.stat_sv_aircontrol_power
+       #define PHYS_AIRSPEEDLIMIT_NONQW(s)                     s.stat_sv_airspeedlimit_nonqw
+       #define PHYS_AIRSTOPACCELERATE                          self.stat_sv_airstopaccelerate
+       #define PHYS_AIRSTRAFEACCEL_QW(s)                       s.stat_sv_airstrafeaccel_qw
+       #define PHYS_AIRSTRAFEACCELERATE(s)                     s.stat_sv_airstrafeaccelerate
+       #define PHYS_ENTGRAVITY(s)                                      s.gravity
+       #define PHYS_FRICTION                                           self.stat_sv_friction
+       #define PHYS_FRICTION_SLICK                                     autocvar_sv_friction_slick
+       #define PHYS_FRICTION_ONLAND                            autocvar_sv_friction_on_land
+       #define PHYS_GRAVITY                                            autocvar_sv_gravity
+       #define PHYS_HIGHSPEED                                          autocvar_g_movement_highspeed
+       #define PHYS_JUMPVELOCITY                                       self.stat_sv_jumpvelocity
+       #define PHYS_MAXAIRSPEED(s)                                     self.stat_sv_maxairspeed
+       #define PHYS_MAXAIRSTRAFESPEED                          self.stat_sv_maxairstrafespeed
+       #define PHYS_MAXSPEED(s)                                        s.stat_sv_maxspeed
+       #define PHYS_STEPHEIGHT                                         autocvar_sv_stepheight
+       #define PHYS_STOPSPEED                                          self.stat_sv_stopspeed
+       #define PHYS_WARSOWBUNNY_ACCEL                          self.stat_sv_warsowbunny_accel
+       #define PHYS_WARSOWBUNNY_BACKTOSIDERATIO        self.stat_sv_warsowbunny_backtosideratio
+       #define PHYS_WARSOWBUNNY_AIRFORWARDACCEL        self.stat_sv_warsowbunny_airforwardaccel
+       #define PHYS_WARSOWBUNNY_TOPSPEED                       self.stat_sv_warsowbunny_topspeed
+       #define PHYS_WARSOWBUNNY_TURNACCEL                      self.stat_sv_warsowbunny_turnaccel
+
+       #define PHYS_WALLFRICTION                                       cvar("sv_wallfriction")
+
+       #define PHYS_JETPACK_ACCEL_UP                           autocvar_g_jetpack_acceleration_up
+       #define PHYS_JETPACK_ACCEL_SIDE                         autocvar_g_jetpack_acceleration_side
+       #define PHYS_JETPACK_ANTIGRAVITY                        autocvar_g_jetpack_antigravity
+       #define PHYS_JETPACK_FUEL                                       autocvar_g_jetpack_fuel
+       #define PHYS_JETPACK_MAXSPEED_UP                        autocvar_g_jetpack_maxspeed_up
+       #define PHYS_JETPACK_MAXSPEED_SIDE                      autocvar_g_jetpack_maxspeed_side
+
+       #define PHYS_DODGING_FROZEN                                     autocvar_sv_dodging_frozen
+
+       #define PHYS_NOSTEP                                                     cvar("sv_nostep")
+       #define PHYS_JUMPSTEP                                           cvar("sv_jumpstep")
+
+#endif
+#endif
index f888e1b2c8a10e00668841e8048c2eb9119b8549..b488df1cfd709edacf36401d42990adbd4f4ed93 100644 (file)
@@ -107,8 +107,8 @@ const int STAT_HEALING_ORB_ALPHA      = 83;
 const int STAT_PLASMA                 = 84;
 const int STAT_OK_AMMO_CHARGE         = 85;
 const int STAT_OK_AMMO_CHARGEPOOL     = 86;
-// 87 empty?
-// 88 empty?
+const int STAT_FROZEN                 = 87;
+const int STAT_REVIVE_PROGRESS        = 88;
 // 89 empty?
 // 90 empty?
 // 91 empty?
@@ -153,22 +153,24 @@ const int STAT_KH_PINKKEY_TEAM        = 103;
 
 /* Gamemode-specific stats end here */
 
-
-const int STAT_FROZEN                 = 105;
-const int STAT_REVIVE_PROGRESS        = 106;
-// 107 empty?
-// 108 empty?
-// 109 empty?
-// 110 empty?
-// 111 empty?
-// 112 empty?
-// 113 empty?
-// 114 empty?
-// 115 empty?
-// 116 empty?
-// 117 empty?
-// 118 empty?
-// 119 empty?
+const int STAT_PL_VIEW_OFS1           = 105;
+const int STAT_PL_VIEW_OFS2           = 106;
+const int STAT_PL_VIEW_OFS3           = 107;
+const int STAT_PL_MIN1                = 108;
+const int STAT_PL_MIN2                = 109;
+const int STAT_PL_MIN3                = 110;
+const int STAT_PL_MAX1                = 111;
+const int STAT_PL_MAX2                = 112;
+const int STAT_PL_MAX3                = 113;
+const int STAT_PL_CROUCH_MIN1         = 114;
+const int STAT_PL_CROUCH_MIN2         = 115;
+const int STAT_PL_CROUCH_MIN3         = 116;
+const int STAT_PL_CROUCH_MAX1         = 117;
+const int STAT_PL_CROUCH_MAX2         = 118;
+const int STAT_PL_CROUCH_MAX3         = 119;
+const int STAT_PL_CROUCH_VIEW_OFS1    = 117;
+const int STAT_PL_CROUCH_VIEW_OFS2    = 118;
+const int STAT_PL_CROUCH_VIEW_OFS3    = 119;
 // 120 empty?
 // 121 empty?
 // 122 empty?
@@ -218,58 +220,58 @@ const int STAT_REVIVE_PROGRESS        = 106;
 // 165 empty?
 // 166 empty?
 // 167 empty?
-// 168 empty?
-// 169 empty?
-// 170 empty?
-// 171 empty?
-// 172 empty?
-// 173 empty?
-// 174 empty?
-// 175 empty?
-// 176 empty?
-// 177 empty?
-// 178 empty?
-// 179 empty?
-// 180 empty?
-// 181 empty?
-// 182 empty?
-// 183 empty?
-// 184 empty?
-// 185 empty?
-// 186 empty?
-// 187 empty?
-// 188 empty?
-// 189 empty?
-// 190 empty?
-// 191 empty?
-// 192 empty?
-// 193 empty?
-// 194 empty?
-// 195 empty?
-// 196 empty?
-// 197 empty?
-// 198 empty?
-// 199 empty?
-// 200 empty?
-// 201 empty?
-// 202 empty?
-// 203 empty?
-// 204 empty?
-// 205 empty?
-// 206 empty?
-// 207 empty?
-// 208 empty?
-// 209 empty?
-// 210 empty?
-// 211 empty?
-// 212 empty?
-// 213 empty?
-// 214 empty?
-// 215 empty?
-// 216 empty?
-// 217 empty?
-// 218 empty?
-// 219 empty?
+const int STAT_GAMEPLAYFIX_UPVELOCITYCLEARSONGROUND   = 168;
+const int STAT_BUGRIGS_REVERSE_STOPPING               = 169;
+const int STAT_BUGRIGS_REVERSE_SPINNING               = 170;
+const int STAT_BUGRIGS_CAR_JUMPING                    = 171;
+const int STAT_BUGRIGS_FRICTION_AIR                   = 172;
+const int STAT_BUGRIGS_STEER                          = 173;
+const int STAT_BUGRIGS_SPEED_POW                      = 174;
+const int STAT_BUGRIGS_SPEED_REF                      = 175;
+const int STAT_BUGRIGS_ACCEL                          = 176;
+const int STAT_BUGRIGS_FRICTION_BRAKE                 = 177;
+const int STAT_BUGRIGS_AIR_STEERING                   = 178;
+const int STAT_BUGRIGS_FRICTION_FLOOR                 = 179;
+const int STAT_BUGRIGS_REVERSE_SPEEDING               = 180;
+const int STAT_BUGRIGS_PLANAR_MOVEMENT                = 181;
+const int STAT_BUGRIGS_ANGLE_SMOOTHING                = 182;
+const int STAT_BUGRIGS                                = 183;
+const int STAT_GAMEPLAYFIX_STEPDOWN                   = 184;
+const int STAT_MOVEVARS_JUMPSTEP                      = 185;
+const int STAT_NOSTEP                                 = 186;
+const int STAT_GAMEPLAYFIX_UNSTICKPLAYERS             = 187;
+const int STAT_GAMEPLAYFIX_STEPMULTIPLETIMES          = 188;
+const int STAT_GAMEPLAYFIX_DOWNTRACEONGROUND          = 189;
+const int STAT_GAMEPLAYFIX_EASIERWATERJUMP            = 190;
+const int STAT_MOVEVARS_FRICTION_SLICK                = 191;
+const int STAT_MOVEVARS_FRICTION_ONLAND               = 192;
+const int STAT_MOVEVARS_JUMPSPEEDCAP_DISABLE_ONRAMPS  = 193;
+const int STAT_MOVEVARS_JUMPSPEEDCAP_MAX              = 194;
+const int STAT_MOVEVARS_JUMPSPEEDCAP_MIN              = 195;
+const int STAT_DOUBLEJUMP                             = 196;
+const int STAT_MOVEVARS_TRACK_CANJUMP                 = 197;
+const int STAT_MULTIJUMP_ADD                          = 198;
+const int STAT_MULTIJUMP_SPEED                        = 199;
+const int STAT_MULTIJUMP                              = 200;
+const int STAT_DODGING_TIMEOUT                        = 201;
+const int STAT_DODGING_WALL                           = 202;
+const int STAT_DODGING_UP_SPEED                       = 203;
+const int STAT_DODGING_RAMP_TIME                      = 204;
+const int STAT_DODGING_HEIGHT_THRESHOLD               = 205;
+const int STAT_DODGING_DISTANCE_THRESHOLD             = 206;
+const int STAT_DODGING_HORIZ_SPEED                    = 207;
+const int STAT_DODGING_DELAY                          = 208;
+const int STAT_DODGING_FROZEN_NO_DOUBLETAP            = 209;
+const int STAT_DODGING_HORIZ_SPEED_FROZEN             = 210;
+const int STAT_DODGING                                = 211;
+const int STAT_DODGING_FROZEN                         = 212;
+const int STAT_JETPACK_MAXSPEED_UP                    = 213;
+const int STAT_JETPACK_MAXSPEED_SIDE                  = 214;
+const int STAT_JETPACK_FUEL                           = 215;
+const int STAT_JETPACK_ANTIGRAVITY                    = 216;
+const int STAT_JETPACK_ACCEL_SIDE                     = 217;
+const int STAT_JETPACK_ACCEL_UP                       = 218;
+const int STAT_MOVEVARS_HIGHSPEED                     = 219;
 const int STAT_MOVEVARS_AIRACCEL_QW_STRETCHFACTOR     = 220;
 const int STAT_MOVEVARS_AIRCONTROL_PENALTY            = 221;
 const int STAT_MOVEVARS_AIRSPEEDLIMIT_NONQW           = 222;
diff --git a/qcsrc/common/triggers/func/bobbing.qc b/qcsrc/common/triggers/func/bobbing.qc
new file mode 100644 (file)
index 0000000..9fb2f56
--- /dev/null
@@ -0,0 +1,85 @@
+#ifdef SVQC
+.float height;
+void func_bobbing_controller_think()
+{
+       vector v;
+       self.nextthink = time + 0.1;
+
+       if(self.owner.active != ACTIVE_ACTIVE)
+       {
+               self.owner.velocity = '0 0 0';
+               return;
+       }
+
+       // calculate sinewave using makevectors
+       makevectors((self.nextthink * self.owner.cnt + self.owner.phase * 360) * '0 1 0');
+       v = self.owner.destvec + self.owner.movedir * v_forward_y;
+       if(self.owner.classname == "func_bobbing") // don't brake stuff if the func_bobbing was killtarget'ed
+               // * 10 so it will arrive in 0.1 sec
+               self.owner.velocity = (v - self.owner.SUB_ORIGIN) * 10;
+}
+
+/*QUAKED spawnfunc_func_bobbing (0 .5 .8) ? X_AXIS Y_AXIS
+Brush model that moves back and forth on one axis (default Z).
+speed : how long one cycle takes in seconds (default 4)
+height : how far the cycle moves (default 32)
+phase : cycle timing adjustment (0-1 as a fraction of the cycle, default 0)
+noise : path/name of looping .wav file to play.
+dmg : Do this mutch dmg every .dmgtime intervall when blocked
+dmgtime : See above.
+*/
+void spawnfunc_func_bobbing()
+{
+       entity controller;
+       if (self.noise != "")
+       {
+               precache_sound(self.noise);
+               soundto(MSG_INIT, self, CH_TRIGGER_SINGLE, self.noise, VOL_BASE, ATTEN_IDLE);
+       }
+       if (!self.speed)
+               self.speed = 4;
+       if (!self.height)
+               self.height = 32;
+       // center of bobbing motion
+       self.destvec = self.origin;
+       // time scale to get degrees
+       self.cnt = 360 / self.speed;
+
+       self.active = ACTIVE_ACTIVE;
+
+       // damage when blocked
+       self.blocked = generic_plat_blocked;
+       if(self.dmg && (self.message == ""))
+               self.message = " was squished";
+    if(self.dmg && (self.message2 == ""))
+               self.message2 = "was squished by";
+       if(self.dmg && (!self.dmgtime))
+               self.dmgtime = 0.25;
+       self.dmgtime2 = time;
+
+       // how far to bob
+       if (self.spawnflags & 1) // X
+               self.movedir = '1 0 0' * self.height;
+       else if (self.spawnflags & 2) // Y
+               self.movedir = '0 1 0' * self.height;
+       else // Z
+               self.movedir = '0 0 1' * self.height;
+
+       if (!InitMovingBrushTrigger())
+               return;
+
+       // wait for targets to spawn
+       controller = spawn();
+       controller.classname = "func_bobbing_controller";
+       controller.owner = self;
+       controller.nextthink = time + 1;
+       controller.think = func_bobbing_controller_think;
+       self.SUB_NEXTTHINK = self.SUB_LTIME + 999999999;
+       self.SUB_THINK = SUB_NullThink;
+
+       // Savage: Reduce bandwith, critical on e.g. nexdm02
+       self.effects |= EF_LOWPRECISION;
+
+       // TODO make a reset function for this one
+}
+#endif
diff --git a/qcsrc/common/triggers/func/breakable.qc b/qcsrc/common/triggers/func/breakable.qc
new file mode 100644 (file)
index 0000000..85120ef
--- /dev/null
@@ -0,0 +1,324 @@
+#ifdef SVQC
+
+#include "../../../server/_all.qh"
+
+#include "../../../server/g_subs.qh"
+#include "../../../server/waypointsprites.qh"
+#include "../../../server/g_damage.qh"
+#include "../../../server/bot/bot.qh"
+#include "../../common/csqcmodel_settings.qh"
+#include "../../../csqcmodellib/sv_model.qh"
+#include "../../../server/weapons/common.qh"
+
+.entity sprite;
+
+.float dmg;
+.float dmg_edge;
+.float dmg_radius;
+.float dmg_force;
+.float debrismovetype;
+.float debrissolid;
+.vector debrisvelocity;
+.vector debrisvelocityjitter;
+.vector debrisavelocityjitter;
+.float debristime;
+.float debristimejitter;
+.float debrisfadetime;
+.float debrisdamageforcescale;
+.float debrisskin;
+
+.string mdl_dead; // or "" to hide when broken
+.string debris; // space separated list of debris models
+// other fields:
+//   mdl = particle effect name
+//   count = particle effect multiplier
+//   targetname = target to trigger to unbreak the model
+//   target = targets to trigger when broken
+//   health = amount of damage it can take
+//   spawnflags:
+//     1 = start disabled (needs to be triggered to activate)
+//     2 = indicate damage
+// notes:
+//   for mdl_dead to work, origin must be set (using a common/origin brush).
+//   Otherwise mdl_dead will be displayed at the map origin, and nobody would
+//   want that!
+
+void func_breakable_damage(entity inflictor, entity attacker, float damage, int deathtype, vector hitloc, vector force);
+
+//
+// func_breakable
+// - basically func_assault_destructible for general gameplay use
+//
+void LaunchDebris (string debrisname, vector force)
+{
+       entity dbr = spawn();
+       setorigin(dbr, self.absmin
+                  + '1 0 0' * random() * (self.absmax.x - self.absmin.x)
+                  + '0 1 0' * random() * (self.absmax.y - self.absmin.y)
+                  + '0 0 1' * random() * (self.absmax.z - self.absmin.z));
+       setmodel (dbr, debrisname );
+       dbr.skin = self.debrisskin;
+       dbr.colormap = self.colormap; // inherit team colors
+       dbr.owner = self; // do not be affected by our own explosion
+       dbr.movetype = self.debrismovetype;
+       dbr.solid = self.debrissolid;
+       if(dbr.solid != SOLID_BSP) // SOLID_BSP has exact collision, MAYBE this works? TODO check this out
+               setsize(dbr, '0 0 0', '0 0 0'); // needed for performance, until engine can deal better with it
+       dbr.velocity_x = self.debrisvelocity.x + self.debrisvelocityjitter.x * crandom();
+       dbr.velocity_y = self.debrisvelocity.y + self.debrisvelocityjitter.y * crandom();
+       dbr.velocity_z = self.debrisvelocity.z + self.debrisvelocityjitter.z * crandom();
+       self.velocity = self.velocity + force * self.debrisdamageforcescale;
+       dbr.avelocity_x = random()*self.debrisavelocityjitter.x;
+       dbr.avelocity_y = random()*self.debrisavelocityjitter.y;
+       dbr.avelocity_z = random()*self.debrisavelocityjitter.z;
+       dbr.damageforcescale = self.debrisdamageforcescale;
+       if(dbr.damageforcescale)
+               dbr.takedamage = DAMAGE_YES;
+       SUB_SetFade(dbr, time + self.debristime + crandom() * self.debristimejitter, self.debrisfadetime);
+}
+
+void func_breakable_colormod()
+{
+       float h;
+       if (!(self.spawnflags & 2))
+               return;
+       h = self.health / self.max_health;
+       if(h < 0.25)
+               self.colormod = '1 0 0';
+       else if(h <= 0.75)
+               self.colormod = '1 0 0' + '0 1 0' * (2 * h - 0.5);
+       else
+               self.colormod = '1 1 1';
+
+       CSQCMODEL_AUTOUPDATE();
+}
+
+void func_breakable_look_destroyed()
+{
+       float floorZ;
+
+       if(self.solid == SOLID_BSP) // in case a misc_follow moved me, save the current origin first
+               self.dropped_origin = self.origin;
+
+       if(self.mdl_dead == "")
+               self.effects |= EF_NODRAW;
+       else {
+               if (self.origin == '0 0 0')     {       // probably no origin brush, so don't spawn in the middle of the map..
+                       floorZ = self.absmin.z;
+                       setorigin(self,((self.absmax+self.absmin)*.5));
+                       self.origin_z = floorZ;
+               }
+               setmodel(self, self.mdl_dead);
+               self.effects &= ~EF_NODRAW;
+       }
+
+       CSQCMODEL_AUTOUPDATE();
+
+       self.solid = SOLID_NOT;
+}
+
+void func_breakable_look_restore()
+{
+       setmodel(self, self.mdl);
+       self.effects &= ~EF_NODRAW;
+
+       if(self.mdl_dead != "") // only do this if we use mdl_dead, to behave better with misc_follow
+               setorigin(self, self.dropped_origin);
+
+       CSQCMODEL_AUTOUPDATE();
+
+       self.solid = SOLID_BSP;
+}
+
+void func_breakable_behave_destroyed()
+{
+       self.health = self.max_health;
+       self.takedamage = DAMAGE_NO;
+       self.bot_attack = false;
+       self.event_damage = func_null;
+       self.state = 1;
+       func_breakable_colormod();
+       if (self.noise1)
+               stopsound (self, CH_TRIGGER_SINGLE);
+}
+
+void func_breakable_behave_restore()
+{
+       self.health = self.max_health;
+       if(self.sprite)
+       {
+               WaypointSprite_UpdateMaxHealth(self.sprite, self.max_health);
+               WaypointSprite_UpdateHealth(self.sprite, self.health);
+       }
+       self.takedamage = DAMAGE_AIM;
+       self.bot_attack = true;
+       self.event_damage = func_breakable_damage;
+       self.state = 0;
+       self.nextthink = 0; // cancel auto respawn
+       func_breakable_colormod();
+       if (self.noise1)
+               sound (self, CH_TRIGGER_SINGLE, self.noise1, VOL_BASE, ATTEN_NORM);
+}
+
+void func_breakable_init_for_player(entity player)
+{
+       if (self.noise1 && self.state == 0 && clienttype(player) == CLIENTTYPE_REAL)
+       {
+               msg_entity = player;
+               soundto (MSG_ONE, self, CH_TRIGGER_SINGLE, self.noise1, VOL_BASE, ATTEN_NORM);
+       }
+}
+
+void func_breakable_destroyed()
+{
+       func_breakable_look_destroyed();
+       func_breakable_behave_destroyed();
+
+       CSQCMODEL_AUTOUPDATE();
+}
+
+void func_breakable_restore()
+{
+       func_breakable_look_restore();
+       func_breakable_behave_restore();
+
+       CSQCMODEL_AUTOUPDATE();
+}
+
+vector debrisforce; // global, set before calling this
+void func_breakable_destroy() {
+       float n, i;
+       string oldmsg;
+
+       activator = self.owner;
+       self.owner = world; // set by W_PrepareExplosionByDamage
+
+       // now throw around the debris
+       n = tokenize_console(self.debris);
+       for(i = 0; i < n; ++i)
+               LaunchDebris(argv(i), debrisforce);
+
+       func_breakable_destroyed();
+
+       if(self.noise)
+               sound (self, CH_TRIGGER, self.noise, VOL_BASE, ATTEN_NORM);
+
+       if(self.dmg)
+               RadiusDamage(self, activator, self.dmg, self.dmg_edge, self.dmg_radius, self, world, self.dmg_force, DEATH_HURTTRIGGER, world);
+
+       if(self.cnt)
+               pointparticles(self.cnt, self.absmin * 0.5 + self.absmax * 0.5, '0 0 0', self.count);
+
+       if(self.respawntime)
+       {
+               self.think = func_breakable_restore;
+               self.nextthink = time + self.respawntime + crandom() * self.respawntimejitter;
+       }
+
+       oldmsg = self.message;
+       self.message = "";
+       SUB_UseTargets();
+       self.message = oldmsg;
+}
+
+void func_breakable_damage(entity inflictor, entity attacker, float damage, int deathtype, vector hitloc, vector force)
+{
+       if(self.state == 1)
+               return;
+       if(self.spawnflags & DOOR_NOSPLASH)
+               if(!(DEATH_ISSPECIAL(deathtype)) && (deathtype & HITTYPE_SPLASH))
+                       return;
+       if(self.team)
+               if(attacker.team == self.team)
+                       return;
+       self.health = self.health - damage;
+       if(self.sprite)
+       {
+               WaypointSprite_Ping(self.sprite);
+               WaypointSprite_UpdateHealth(self.sprite, self.health);
+       }
+       func_breakable_colormod();
+
+       if(self.health <= 0)
+       {
+               debrisforce = force;
+               W_PrepareExplosionByDamage(attacker, func_breakable_destroy);
+       }
+}
+
+void func_breakable_reset()
+{
+       self.team = self.team_saved;
+       func_breakable_look_restore();
+       if(self.spawnflags & 1)
+               func_breakable_behave_destroyed();
+       else
+               func_breakable_behave_restore();
+
+       CSQCMODEL_AUTOUPDATE();
+}
+
+// destructible walls that can be used to trigger target_objective_decrease
+void spawnfunc_func_breakable()
+{
+       float n, i;
+       if(!self.health)
+               self.health = 100;
+       self.max_health = self.health;
+
+       // yes, I know, MOVETYPE_NONE is not available here, not that one would want it here anyway
+       if(!self.debrismovetype) self.debrismovetype = MOVETYPE_BOUNCE;
+       if(!self.debrissolid) self.debrissolid = SOLID_NOT;
+       if(self.debrisvelocity == '0 0 0') self.debrisvelocity = '0 0 140';
+       if(self.debrisvelocityjitter == '0 0 0') self.debrisvelocityjitter = '70 70 70';
+       if(self.debrisavelocityjitter == '0 0 0') self.debrisavelocityjitter = '600 600 600';
+       if(!self.debristime) self.debristime = 3.5;
+       if(!self.debristimejitter) self.debristime = 2.5;
+
+       if(self.mdl != "")
+               self.cnt = particleeffectnum(self.mdl);
+       if(self.count == 0)
+               self.count = 1;
+
+       if(self.message == "")
+               self.message = "got too close to an explosion";
+       if(self.message2 == "")
+               self.message2 = "was pushed into an explosion by";
+       if(!self.dmg_radius)
+               self.dmg_radius = 150;
+       if(!self.dmg_force)
+               self.dmg_force = 200;
+
+       self.mdl = self.model;
+       SetBrushEntityModel();
+
+       self.use = func_breakable_restore;
+
+       // precache all the models
+       if (self.mdl_dead)
+               precache_model(self.mdl_dead);
+       n = tokenize_console(self.debris);
+       for(i = 0; i < n; ++i)
+               precache_model(argv(i));
+       if(self.noise)
+               precache_sound(self.noise);
+       if(self.noise1)
+               precache_sound(self.noise1);
+
+       self.team_saved = self.team;
+       self.dropped_origin = self.origin;
+
+       self.reset = func_breakable_reset;
+       func_breakable_reset();
+
+       self.init_for_player_needed = 1;
+       self.init_for_player = func_breakable_init_for_player;
+
+       CSQCMODEL_AUTOINIT();
+}
+
+// for use in maps with a "model" key set
+void spawnfunc_misc_breakablemodel() {
+       spawnfunc_func_breakable();
+}
+#endif
diff --git a/qcsrc/common/triggers/func/breakable.qh b/qcsrc/common/triggers/func/breakable.qh
new file mode 100644 (file)
index 0000000..b641d94
--- /dev/null
@@ -0,0 +1,8 @@
+#ifndef TRIGGERS_FUNC_BREAKABLE_H
+#define TRIGGERS_FUNC_BREAKABLE_H
+
+#ifdef SVQC
+void spawnfunc_func_breakable();
+#endif
+
+#endif
diff --git a/qcsrc/common/triggers/func/button.qc b/qcsrc/common/triggers/func/button.qc
new file mode 100644 (file)
index 0000000..bb8875b
--- /dev/null
@@ -0,0 +1,155 @@
+#ifdef SVQC
+// button and multiple button
+
+void() button_wait;
+void() button_return;
+
+void button_wait()
+{
+       self.state = STATE_TOP;
+       self.SUB_NEXTTHINK = self.SUB_LTIME + self.wait;
+       self.SUB_THINK = button_return;
+       activator = self.enemy;
+       SUB_UseTargets();
+       self.frame = 1;                 // use alternate textures
+}
+
+void button_done()
+{
+       self.state = STATE_BOTTOM;
+}
+
+void button_return()
+{
+       self.state = STATE_DOWN;
+       SUB_CalcMove (self.pos1, TSPEED_LINEAR, self.speed, button_done);
+       self.frame = 0;                 // use normal textures
+       if (self.health)
+               self.takedamage = DAMAGE_YES;   // can be shot again
+}
+
+
+void button_blocked()
+{
+       // do nothing, just don't come all the way back out
+}
+
+
+void button_fire()
+{
+       self.health = self.max_health;
+       self.takedamage = DAMAGE_NO;    // will be reset upon return
+
+       if (self.state == STATE_UP || self.state == STATE_TOP)
+               return;
+
+       if (self.noise != "")
+               sound (self, CH_TRIGGER, self.noise, VOL_BASE, ATTEN_NORM);
+
+       self.state = STATE_UP;
+       SUB_CalcMove (self.pos2, TSPEED_LINEAR, self.speed, button_wait);
+}
+
+void button_reset()
+{
+       self.health = self.max_health;
+       setorigin(self, self.pos1);
+       self.frame = 0;                 // use normal textures
+       self.state = STATE_BOTTOM;
+       if (self.health)
+               self.takedamage = DAMAGE_YES;   // can be shot again
+}
+
+void button_use()
+{
+       if(self.active != ACTIVE_ACTIVE)
+               return;
+
+       self.enemy = activator;
+       button_fire ();
+}
+
+void button_touch()
+{
+       if (!other)
+               return;
+       if (!other.iscreature)
+               return;
+       if(other.velocity * self.movedir < 0)
+               return;
+       self.enemy = other;
+       if (other.owner)
+               self.enemy = other.owner;
+       button_fire ();
+}
+
+void button_damage(entity inflictor, entity attacker, float damage, int deathtype, vector hitloc, vector force)
+{
+       if(self.spawnflags & DOOR_NOSPLASH)
+               if(!(DEATH_ISSPECIAL(deathtype)) && (deathtype & HITTYPE_SPLASH))
+                       return;
+       self.health = self.health - damage;
+       if (self.health <= 0)
+       {
+               self.enemy = damage_attacker;
+               button_fire ();
+       }
+}
+
+
+/*QUAKED spawnfunc_func_button (0 .5 .8) ?
+When a button is touched, it moves some distance in the direction of it's angle, triggers all of it's targets, waits some time, then returns to it's original position where it can be triggered again.
+
+"angle"                determines the opening direction
+"target"       all entities with a matching targetname will be used
+"speed"                override the default 40 speed
+"wait"         override the default 1 second wait (-1 = never return)
+"lip"          override the default 4 pixel lip remaining at end of move
+"health"       if set, the button must be killed instead of touched. If set to -1, the button will fire on ANY attack, even damageless ones like the InstaGib laser
+"sounds"
+0) steam metal
+1) wooden clunk
+2) metallic click
+3) in-out
+*/
+void spawnfunc_func_button()
+{
+       SetMovedir ();
+
+       if (!InitMovingBrushTrigger())
+               return;
+       self.effects |= EF_LOWPRECISION;
+
+       self.blocked = button_blocked;
+       self.use = button_use;
+
+//     if (self.health == 0) // all buttons are now shootable
+//             self.health = 10;
+       if (self.health)
+       {
+               self.max_health = self.health;
+               self.event_damage = button_damage;
+               self.takedamage = DAMAGE_YES;
+       }
+       else
+               self.touch = button_touch;
+
+       if (!self.speed)
+               self.speed = 40;
+       if (!self.wait)
+               self.wait = 1;
+       if (!self.lip)
+               self.lip = 4;
+
+    if(self.noise != "")
+        precache_sound(self.noise);
+
+       self.active = ACTIVE_ACTIVE;
+
+       self.pos1 = self.origin;
+       self.pos2 = self.pos1 + self.movedir*(fabs(self.movedir*self.size) - self.lip);
+    self.flags |= FL_NOTARGET;
+
+       button_reset();
+}
+#endif
diff --git a/qcsrc/common/triggers/func/conveyor.qc b/qcsrc/common/triggers/func/conveyor.qc
new file mode 100644 (file)
index 0000000..6ed01b0
--- /dev/null
@@ -0,0 +1,197 @@
+void conveyor_think()
+{
+#ifdef CSQC
+       // TODO: check if this is what is causing the glitchiness when switching between them
+       float dt = time - self.move_time;
+       self.move_time = time;
+       if(dt <= 0) { return; }
+#endif
+       entity e;
+
+       // set myself as current conveyor where possible
+       for(e = world; (e = findentity(e, conveyor, self)); )
+               e.conveyor = world;
+
+       if(self.state)
+       {
+               for(e = findradius((self.absmin + self.absmax) * 0.5, vlen(self.absmax - self.absmin) * 0.5 + 1); e; e = e.chain)
+                       if(!e.conveyor.state)
+                               if(isPushable(e))
+                               {
+                                       vector emin = e.absmin;
+                                       vector emax = e.absmax;
+                                       if(self.solid == SOLID_BSP)
+                                       {
+                                               emin -= '1 1 1';
+                                               emax += '1 1 1';
+                                       }
+                                       if(boxesoverlap(emin, emax, self.absmin, self.absmax)) // quick
+                                               if(WarpZoneLib_BoxTouchesBrush(emin, emax, self, e)) // accurate
+                                                       e.conveyor = self;
+                               }
+
+               for(e = world; (e = findentity(e, conveyor, self)); )
+               {
+                       if(IS_CLIENT(e)) // doing it via velocity has quite some advantages
+                               continue; // done in SV_PlayerPhysics   continue;
+
+                       setorigin(e, e.origin + self.movedir * PHYS_INPUT_FRAMETIME);
+                       move_out_of_solid(e);
+#ifdef SVQC
+                       UpdateCSQCProjectile(e);
+#endif
+                       /*
+                       // stupid conveyor code
+                       tracebox(e.origin, e.mins, e.maxs, e.origin + self.movedir * sys_frametime, MOVE_NORMAL, e);
+                       if(trace_fraction > 0)
+                               setorigin(e, trace_endpos);
+                       */
+               }
+       }
+
+#ifdef SVQC
+       self.nextthink = time;
+#endif
+}
+
+#ifdef SVQC
+
+void conveyor_use()
+{
+       self.state = !self.state;
+
+       self.SendFlags |= 2;
+}
+
+void conveyor_reset()
+{
+       self.state = (self.spawnflags & 1);
+
+       self.SendFlags |= 2;
+}
+
+bool conveyor_send(entity to, int sf)
+{
+       WriteByte(MSG_ENTITY, ENT_CLIENT_CONVEYOR);
+       WriteByte(MSG_ENTITY, sf);
+
+       if(sf & 1)
+       {
+               WriteByte(MSG_ENTITY, self.warpzone_isboxy);
+               WriteCoord(MSG_ENTITY, self.origin_x);
+               WriteCoord(MSG_ENTITY, self.origin_y);
+               WriteCoord(MSG_ENTITY, self.origin_z);
+
+               WriteCoord(MSG_ENTITY, self.mins_x);
+               WriteCoord(MSG_ENTITY, self.mins_y);
+               WriteCoord(MSG_ENTITY, self.mins_z);
+               WriteCoord(MSG_ENTITY, self.maxs_x);
+               WriteCoord(MSG_ENTITY, self.maxs_y);
+               WriteCoord(MSG_ENTITY, self.maxs_z);
+
+               WriteCoord(MSG_ENTITY, self.movedir_x);
+               WriteCoord(MSG_ENTITY, self.movedir_y);
+               WriteCoord(MSG_ENTITY, self.movedir_z);
+
+               WriteByte(MSG_ENTITY, self.speed);
+               WriteByte(MSG_ENTITY, self.state);
+
+               WriteString(MSG_ENTITY, self.targetname);
+               WriteString(MSG_ENTITY, self.target);
+       }
+
+       if(sf & 2)
+               WriteByte(MSG_ENTITY, self.state);
+
+       return true;
+}
+
+void conveyor_init()
+{
+       if (!self.speed)
+               self.speed = 200;
+       self.movedir = self.movedir * self.speed;
+       self.think = conveyor_think;
+       self.nextthink = time;
+       IFTARGETED
+       {
+               self.use = conveyor_use;
+               self.reset = conveyor_reset;
+               conveyor_reset();
+       }
+       else
+               self.state = 1;
+
+       FixSize(self);
+
+       Net_LinkEntity(self, 0, false, conveyor_send);
+
+       self.SendFlags |= 1;
+}
+
+void spawnfunc_trigger_conveyor()
+{
+       SetMovedir();
+       EXACTTRIGGER_INIT;
+       conveyor_init();
+}
+
+void spawnfunc_func_conveyor()
+{
+       SetMovedir();
+       InitMovingBrushTrigger();
+       self.movetype = MOVETYPE_NONE;
+       conveyor_init();
+}
+
+#elif defined(CSQC)
+
+void conveyor_init()
+{
+       self.draw = conveyor_think;
+       self.drawmask = MASK_NORMAL;
+
+       self.movetype = MOVETYPE_NONE;
+       self.model = "";
+       self.solid = SOLID_TRIGGER;
+       self.move_origin = self.origin;
+       self.move_time = time;
+}
+
+void ent_conveyor()
+{
+       float sf = ReadByte();
+
+       if(sf & 1)
+       {
+               self.warpzone_isboxy = ReadByte();
+               self.origin_x = ReadCoord();
+               self.origin_y = ReadCoord();
+               self.origin_z = ReadCoord();
+               setorigin(self, self.origin);
+
+               self.mins_x = ReadCoord();
+               self.mins_y = ReadCoord();
+               self.mins_z = ReadCoord();
+               self.maxs_x = ReadCoord();
+               self.maxs_y = ReadCoord();
+               self.maxs_z = ReadCoord();
+               setsize(self, self.mins, self.maxs);
+
+               self.movedir_x = ReadCoord();
+               self.movedir_y = ReadCoord();
+               self.movedir_z = ReadCoord();
+
+               self.speed = ReadByte();
+               self.state = ReadByte();
+
+               self.targetname = strzone(ReadString());
+               self.target = strzone(ReadString());
+
+               conveyor_init();
+       }
+
+       if(sf & 2)
+               self.state = ReadByte();
+}
+#endif
diff --git a/qcsrc/common/triggers/func/conveyor.qh b/qcsrc/common/triggers/func/conveyor.qh
new file mode 100644 (file)
index 0000000..f272030
--- /dev/null
@@ -0,0 +1,3 @@
+#ifdef CSQC
+void ent_conveyor();
+#endif
diff --git a/qcsrc/common/triggers/func/door.qc b/qcsrc/common/triggers/func/door.qc
new file mode 100644 (file)
index 0000000..e3f82af
--- /dev/null
@@ -0,0 +1,886 @@
+/*
+
+Doors are similar to buttons, but can spawn a fat trigger field around them
+to open without a touch, and they link together to form simultanious
+double/quad doors.
+
+Door.owner is the master door.  If there is only one door, it points to itself.
+If multiple doors, all will point to a single one.
+
+Door.enemy chains from the master door through all doors linked in the chain.
+
+*/
+
+
+/*
+=============================================================================
+
+THINK FUNCTIONS
+
+=============================================================================
+*/
+
+void() door_go_down;
+void() door_go_up;
+void() door_rotating_go_down;
+void() door_rotating_go_up;
+
+void door_blocked()
+{
+       if((self.spawnflags & 8) 
+#ifdef SVQC
+               && (other.takedamage != DAMAGE_NO)
+#elif defined(CSQC)
+               && !PHYS_DEAD(other)
+#endif
+       )
+       { // KIll Kill Kill!!
+#ifdef SVQC
+               Damage (other, self, self, 10000, DEATH_HURTTRIGGER, other.origin, '0 0 0');
+#endif
+       }
+       else
+       {
+#ifdef SVQC
+               if((self.dmg) && (other.takedamage == DAMAGE_YES))    // Shall we bite?
+                       Damage (other, self, self, self.dmg, DEATH_HURTTRIGGER, other.origin, '0 0 0');
+#endif
+
+                // don't change direction for dead or dying stuff
+               if(PHYS_DEAD(other)
+#ifdef SVQC
+                       && (other.takedamage == DAMAGE_NO)
+#endif
+               )
+               {
+                       if (self.wait >= 0)
+                       {
+                               if (self.state == STATE_DOWN)
+                       if (self.classname == "door")
+                       {
+                               door_go_up ();
+                       } else
+                       {
+                               door_rotating_go_up ();
+                       }
+                               else
+                       if (self.classname == "door")
+                       {
+                               door_go_down ();
+                       } else
+                       {
+                               door_rotating_go_down ();
+                       }
+                       }
+               }
+#ifdef SVQC
+               else
+               {
+                       //gib dying stuff just to make sure
+                       if((self.dmg) && (other.takedamage != DAMAGE_NO))    // Shall we bite?
+                               Damage (other, self, self, 10000, DEATH_HURTTRIGGER, other.origin, '0 0 0');
+               }
+#endif
+       }
+}
+
+void door_hit_top()
+{
+       if (self.noise1 != "")
+               sound (self, CH_TRIGGER_SINGLE, self.noise1, VOL_BASE, ATTEN_NORM);
+       self.state = STATE_TOP;
+       if (self.spawnflags & DOOR_TOGGLE)
+               return;         // don't come down automatically
+       if (self.classname == "door")
+       {
+               self.SUB_THINK = door_go_down;
+       } else
+       {
+               self.SUB_THINK = door_rotating_go_down;
+       }
+       self.SUB_NEXTTHINK = self.SUB_LTIME + self.wait;
+}
+
+void door_hit_bottom()
+{
+       if (self.noise1 != "")
+               sound (self, CH_TRIGGER_SINGLE, self.noise1, VOL_BASE, ATTEN_NORM);
+       self.state = STATE_BOTTOM;
+}
+
+void door_go_down()
+{
+       if (self.noise2 != "")
+               sound (self, CH_TRIGGER_SINGLE, self.noise2, VOL_BASE, ATTEN_NORM);
+       if (self.max_health)
+       {
+               self.takedamage = DAMAGE_YES;
+               self.health = self.max_health;
+       }
+
+       self.state = STATE_DOWN;
+       SUB_CalcMove (self.pos1, TSPEED_LINEAR, self.speed, door_hit_bottom);
+}
+
+void door_go_up()
+{
+       if (self.state == STATE_UP)
+               return;         // already going up
+
+       if (self.state == STATE_TOP)
+       {       // reset top wait time
+               self.SUB_NEXTTHINK = self.SUB_LTIME + self.wait;
+               return;
+       }
+
+       if (self.noise2 != "")
+               sound (self, CH_TRIGGER_SINGLE, self.noise2, VOL_BASE, ATTEN_NORM);
+       self.state = STATE_UP;
+       SUB_CalcMove (self.pos2, TSPEED_LINEAR, self.speed, door_hit_top);
+
+       string oldmessage;
+       oldmessage = self.message;
+       self.message = "";
+       SUB_UseTargets();
+       self.message = oldmessage;
+}
+
+
+/*
+=============================================================================
+
+ACTIVATION FUNCTIONS
+
+=============================================================================
+*/
+
+float door_check_keys(void)
+{
+       local entity door;
+
+
+       if (self.owner)
+               door = self.owner;
+       else
+               door = self;
+
+       // no key needed
+       if (!door.itemkeys)
+               return true;
+
+       // this door require a key
+       // only a player can have a key
+       if (!IS_PLAYER(other))
+               return false;
+
+#ifdef SVQC
+       if (item_keys_usekey(door, other))
+       {
+               // some keys were used
+               if (other.key_door_messagetime <= time)
+               {
+
+                       play2(other, "misc/talk.wav");
+                       Send_Notification(NOTIF_ONE, other, MSG_CENTER, CENTER_DOOR_LOCKED_ALSONEED, item_keys_keylist(door.itemkeys));
+                       other.key_door_messagetime = time + 2;
+               }
+       }
+       else
+       {
+               // no keys were used
+               if (other.key_door_messagetime <= time)
+               {
+                       play2(other, "misc/talk.wav");
+                       Send_Notification(NOTIF_ONE, other, MSG_CENTER, CENTER_DOOR_LOCKED_NEED, item_keys_keylist(door.itemkeys));
+
+                       other.key_door_messagetime = time + 2;
+               }
+       }
+#endif
+
+       if (door.itemkeys)
+       {
+#ifdef SVQC
+               // door is now unlocked
+               play2(other, "misc/talk.wav");
+               Send_Notification(NOTIF_ONE, other, MSG_CENTER, CENTER_DOOR_UNLOCKED);
+#endif
+               return true;
+       }
+       else
+               return false;
+}
+
+void door_fire()
+{
+       entity  oself;
+       entity  starte;
+
+       if (self.owner != self)
+               objerror ("door_fire: self.owner != self");
+
+       oself = self;
+
+       if (self.spawnflags & DOOR_TOGGLE)
+       {
+               if (self.state == STATE_UP || self.state == STATE_TOP)
+               {
+                       starte = self;
+                       do
+                       {
+                               if (self.classname == "door")
+                               {
+                                       door_go_down ();
+                               }
+                               else
+                               {
+                                       door_rotating_go_down ();
+                               }
+                               self = self.enemy;
+                       } while ( (self != starte) && (self != world) );
+                       self = oself;
+                       return;
+               }
+       }
+
+// trigger all paired doors
+       starte = self;
+       do
+       {
+               if (self.classname == "door")
+               {
+                       door_go_up ();
+               } else
+               {
+                       // if the BIDIR spawnflag (==2) is set and the trigger has set trigger_reverse, reverse the opening direction
+                       if ((self.spawnflags & 2) && other.trigger_reverse!=0 && self.lip!=666 && self.state == STATE_BOTTOM)
+                       {
+                               self.lip = 666; // self.lip is used to remember reverse opening direction for door_rotating
+                               self.pos2 = '0 0 0' - self.pos2;
+                       }
+                       // if BIDIR_IN_DOWN (==8) is set, prevent the door from reoping during closing if it is triggered from the wrong side
+                       if (!((self.spawnflags & 2) &&  (self.spawnflags & 8) && self.state == STATE_DOWN
+                               && (((self.lip==666) && (other.trigger_reverse==0)) || ((self.lip!=666) && (other.trigger_reverse!=0)))))
+                       {
+                               door_rotating_go_up ();
+                       }
+               }
+               self = self.enemy;
+       } while ( (self != starte) && (self != world) );
+       self = oself;
+}
+
+void door_use()
+{
+       entity oself;
+
+       //dprint("door_use (model: ");dprint(self.model);dprint(")\n");
+
+       if (self.owner)
+       {
+               oself = self;
+               self = self.owner;
+               door_fire ();
+               self = oself;
+       }
+}
+
+void door_damage(entity inflictor, entity attacker, float damage, int deathtype, vector hitloc, vector force)
+{
+       entity oself;
+       if(self.spawnflags & DOOR_NOSPLASH)
+               if(!(DEATH_ISSPECIAL(deathtype)) && (deathtype & HITTYPE_SPLASH))
+                       return;
+       self.health = self.health - damage;
+
+       if (self.itemkeys)
+       {
+               // don't allow opening doors through damage if keys are required
+               return;
+       }
+
+       if (self.health <= 0)
+       {
+               oself = self;
+               self = self.owner;
+               self.health = self.max_health;
+               self.takedamage = DAMAGE_NO;    // wil be reset upon return
+               door_use ();
+               self = oself;
+       }
+}
+
+
+/*
+================
+door_touch
+
+Prints messages
+================
+*/
+
+void door_touch()
+{
+       if (!IS_PLAYER(other))
+               return;
+       if (self.owner.attack_finished_single > time)
+               return;
+
+       self.owner.attack_finished_single = time + 2;
+
+#ifdef SVQC
+       if (!(self.owner.dmg) && (self.owner.message != ""))
+       {
+               if (IS_CLIENT(other))
+                       centerprint(other, self.owner.message);
+               play2(other, "misc/talk.wav");
+       }
+#endif
+}
+
+void door_generic_plat_blocked()
+{
+
+       if((self.spawnflags & 8) && (other.takedamage != DAMAGE_NO)) { // KIll Kill Kill!!
+#ifdef SVQC
+               Damage (other, self, self, 10000, DEATH_HURTTRIGGER, other.origin, '0 0 0');
+#endif
+       }
+       else
+       {
+
+#ifdef SVQC
+               if((self.dmg) && (other.takedamage == DAMAGE_YES))    // Shall we bite?
+                       Damage (other, self, self, self.dmg, DEATH_HURTTRIGGER, other.origin, '0 0 0');
+#endif
+
+                //Dont chamge direction for dead or dying stuff
+               if(PHYS_DEAD(other) && (other.takedamage == DAMAGE_NO))
+               {
+                       if (self.wait >= 0)
+                       {
+                               if (self.state == STATE_DOWN)
+                                       door_rotating_go_up ();
+                               else
+                                       door_rotating_go_down ();
+                       }
+               }
+#ifdef SVQC
+               else
+               {
+                       //gib dying stuff just to make sure
+                       if((self.dmg) && (other.takedamage != DAMAGE_NO))    // Shall we bite?
+                               Damage (other, self, self, 10000, DEATH_HURTTRIGGER, other.origin, '0 0 0');
+               }
+#endif
+       }
+}
+
+void door_rotating_hit_top()
+{
+       if (self.noise1 != "")
+               sound (self, CH_TRIGGER_SINGLE, self.noise1, VOL_BASE, ATTEN_NORM);
+       self.state = STATE_TOP;
+       if (self.spawnflags & DOOR_TOGGLE)
+               return;         // don't come down automatically
+       self.SUB_THINK = door_rotating_go_down;
+       self.SUB_NEXTTHINK = self.SUB_LTIME + self.wait;
+}
+
+void door_rotating_hit_bottom()
+{
+       if (self.noise1 != "")
+               sound (self, CH_TRIGGER_SINGLE, self.noise1, VOL_BASE, ATTEN_NORM);
+       if (self.lip==666) // self.lip is used to remember reverse opening direction for door_rotating
+       {
+               self.pos2 = '0 0 0' - self.pos2;
+               self.lip = 0;
+       }
+       self.state = STATE_BOTTOM;
+}
+
+void door_rotating_go_down()
+{
+       if (self.noise2 != "")
+               sound (self, CH_TRIGGER_SINGLE, self.noise2, VOL_BASE, ATTEN_NORM);
+       if (self.max_health)
+       {
+               self.takedamage = DAMAGE_YES;
+               self.health = self.max_health;
+       }
+
+       self.state = STATE_DOWN;
+       SUB_CalcAngleMove (self.pos1, TSPEED_LINEAR, self.speed, door_rotating_hit_bottom);
+}
+
+void door_rotating_go_up()
+{
+       if (self.state == STATE_UP)
+               return;         // already going up
+
+       if (self.state == STATE_TOP)
+       {       // reset top wait time
+               self.SUB_NEXTTHINK = self.SUB_LTIME + self.wait;
+               return;
+       }
+       if (self.noise2 != "")
+               sound (self, CH_TRIGGER_SINGLE, self.noise2, VOL_BASE, ATTEN_NORM);
+       self.state = STATE_UP;
+       SUB_CalcAngleMove (self.pos2, TSPEED_LINEAR, self.speed, door_rotating_hit_top);
+
+       string oldmessage;
+       oldmessage = self.message;
+       self.message = "";
+       SUB_UseTargets();
+       self.message = oldmessage;
+}
+
+
+/*
+=========================================
+door trigger
+
+Spawned if a door lacks a real activator
+=========================================
+*/
+
+void door_trigger_touch()
+{
+       if (other.health < 1)
+#ifdef SVQC
+               if (!((other.iscreature || (other.flags & FL_PROJECTILE)) && !PHYS_DEAD(other)))
+#elif defined(CSQC)
+               if(!((IS_CLIENT(other) || other.classname == "csqcprojectile") && !PHYS_DEAD(other)))
+#endif
+                       return;
+
+       if (time < self.attack_finished_single)
+               return;
+
+       // check if door is locked
+       if (!door_check_keys())
+               return;
+
+       self.attack_finished_single = time + 1;
+
+       activator = other;
+
+       self = self.owner;
+       door_use ();
+}
+
+void spawn_field(vector fmins, vector fmaxs)
+{
+       entity  trigger;
+       vector  t1 = fmins, t2 = fmaxs;
+
+       trigger = spawn();
+       trigger.classname = "doortriggerfield";
+       trigger.movetype = MOVETYPE_NONE;
+       trigger.solid = SOLID_TRIGGER;
+       trigger.owner = self;
+#ifdef SVQC
+       trigger.touch = door_trigger_touch;
+#elif defined(CSQC)
+       trigger.trigger_touch = door_trigger_touch;
+       trigger.draw = trigger_draw_generic;
+       trigger.drawmask = MASK_NORMAL;
+#endif
+
+       setsize (trigger, t1 - '60 60 8', t2 + '60 60 8');
+}
+
+
+/*
+=============
+LinkDoors
+
+
+=============
+*/
+
+entity LinkDoors_nextent(entity cur, entity near, entity pass)
+{
+       while((cur = find(cur, classname, self.classname)) && ((cur.spawnflags & 4) || cur.enemy))
+       {
+       }
+       return cur;
+}
+
+bool LinkDoors_isconnected(entity e1, entity e2, entity pass)
+{
+       float DELTA = 4;
+       if((e1.absmin_x > e2.absmax_x + DELTA)
+       || (e1.absmin_y > e2.absmax_y + DELTA)
+       || (e1.absmin_z > e2.absmax_z + DELTA)
+       || (e2.absmin_x > e1.absmax_x + DELTA)
+       || (e2.absmin_y > e1.absmax_y + DELTA)
+       || (e2.absmin_z > e1.absmax_z + DELTA)
+       ) { return false; }
+       return true;
+}
+
+#ifdef SVQC
+void door_link();
+#endif
+void LinkDoors()
+{
+       entity  t;
+       vector  cmins, cmaxs;
+
+#ifdef SVQC
+       door_link();
+#endif
+
+       if (self.enemy)
+               return;         // already linked by another door
+       if (self.spawnflags & 4)
+       {
+               self.owner = self.enemy = self;
+
+               if (self.health)
+                       return;
+               IFTARGETED
+                       return;
+               if (self.items)
+                       return;
+
+               spawn_field(self.absmin, self.absmax);
+
+               return;         // don't want to link this door
+       }
+
+       FindConnectedComponent(self, enemy, LinkDoors_nextent, LinkDoors_isconnected, world);
+
+       // set owner, and make a loop of the chain
+       dprint("LinkDoors: linking doors:");
+       for(t = self; ; t = t.enemy)
+       {
+               dprint(" ", etos(t));
+               t.owner = self;
+               if(t.enemy == world)
+               {
+                       t.enemy = self;
+                       break;
+               }
+       }
+       dprint("\n");
+
+       // collect health, targetname, message, size
+       cmins = self.absmin;
+       cmaxs = self.absmax;
+       for(t = self; ; t = t.enemy)
+       {
+               if(t.health && !self.health)
+                       self.health = t.health;
+               if((t.targetname != "") && (self.targetname == ""))
+                       self.targetname = t.targetname;
+               if((t.message != "") && (self.message == ""))
+                       self.message = t.message;
+               if (t.absmin_x < cmins_x)
+                       cmins_x = t.absmin_x;
+               if (t.absmin_y < cmins_y)
+                       cmins_y = t.absmin_y;
+               if (t.absmin_z < cmins_z)
+                       cmins_z = t.absmin_z;
+               if (t.absmax_x > cmaxs_x)
+                       cmaxs_x = t.absmax_x;
+               if (t.absmax_y > cmaxs_y)
+                       cmaxs_y = t.absmax_y;
+               if (t.absmax_z > cmaxs_z)
+                       cmaxs_z = t.absmax_z;
+               if(t.enemy == self)
+                       break;
+       }
+
+       // distribute health, targetname, message
+       for(t = self; t; t = t.enemy)
+       {
+               t.health = self.health;
+               t.targetname = self.targetname;
+               t.message = self.message;
+               if(t.enemy == self)
+                       break;
+       }
+
+       // shootable, or triggered doors just needed the owner/enemy links,
+       // they don't spawn a field
+
+       if (self.health)
+               return;
+       IFTARGETED
+               return;
+       if (self.items)
+               return;
+
+       spawn_field(cmins, cmaxs);
+}
+
+#ifdef SVQC
+/*QUAKED spawnfunc_func_door (0 .5 .8) ? START_OPEN x DOOR_DONT_LINK GOLD_KEY SILVER_KEY TOGGLE
+if two doors touch, they are assumed to be connected and operate as a unit.
+
+TOGGLE causes the door to wait in both the start and end states for a trigger event.
+
+START_OPEN causes the door to move to its destination when spawned, and operate in reverse.  It is used to temporarily or permanently close off an area when triggered (not useful for touch or takedamage doors).
+
+GOLD_KEY causes the door to open only if the activator holds a gold key.
+
+SILVER_KEY causes the door to open only if the activator holds a silver key.
+
+"message"      is printed when the door is touched if it is a trigger door and it hasn't been fired yet
+"angle"                determines the opening direction
+"targetname" if set, no touch field will be spawned and a remote button or trigger field activates the door.
+"health"       if set, door must be shot open
+"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)
+"sounds"
+0)     no sound
+1)     stone
+2)     base
+3)     stone chain
+4)     screechy metal
+FIXME: only one sound set available at the time being
+
+*/
+
+float door_send(entity to, float sf)
+{
+       WriteByte(MSG_ENTITY, ENT_CLIENT_DOOR);
+       WriteByte(MSG_ENTITY, sf);
+
+       if(sf & SF_TRIGGER_INIT)
+       {
+               WriteString(MSG_ENTITY, self.classname);
+               WriteByte(MSG_ENTITY, self.spawnflags);
+
+               WriteString(MSG_ENTITY, self.model);
+
+               trigger_common_write(true);
+
+               WriteCoord(MSG_ENTITY, self.pos1_x);
+               WriteCoord(MSG_ENTITY, self.pos1_y);
+               WriteCoord(MSG_ENTITY, self.pos1_z);
+               WriteCoord(MSG_ENTITY, self.pos2_x);
+               WriteCoord(MSG_ENTITY, self.pos2_y);
+               WriteCoord(MSG_ENTITY, self.pos2_z);
+
+               WriteCoord(MSG_ENTITY, self.size_x);
+               WriteCoord(MSG_ENTITY, self.size_y);
+               WriteCoord(MSG_ENTITY, self.size_z);
+
+               WriteShort(MSG_ENTITY, self.wait);
+               WriteShort(MSG_ENTITY, self.speed);
+               WriteByte(MSG_ENTITY, self.lip);
+               WriteByte(MSG_ENTITY, self.state);
+               WriteCoord(MSG_ENTITY, self.SUB_LTIME);
+       }
+
+       if(sf & SF_TRIGGER_RESET)
+       {
+               // client makes use of this, we do not
+       }
+
+       if(sf & SF_TRIGGER_UPDATE)
+       {
+               WriteCoord(MSG_ENTITY, self.origin_x);
+               WriteCoord(MSG_ENTITY, self.origin_y);
+               WriteCoord(MSG_ENTITY, self.origin_z);
+
+               WriteCoord(MSG_ENTITY, self.pos1_x);
+               WriteCoord(MSG_ENTITY, self.pos1_y);
+               WriteCoord(MSG_ENTITY, self.pos1_z);
+               WriteCoord(MSG_ENTITY, self.pos2_x);
+               WriteCoord(MSG_ENTITY, self.pos2_y);
+               WriteCoord(MSG_ENTITY, self.pos2_z);
+       }
+
+       return true;
+}
+
+void door_link()
+{
+       // set size now, as everything is loaded
+       //FixSize(self);
+       //Net_LinkEntity(self, false, 0, door_send);
+}
+#endif
+
+void door_init_startopen()
+{
+       SUB_SETORIGIN(self, self.pos2);
+       self.pos2 = self.pos1;
+       self.pos1 = self.origin;
+
+#ifdef SVQC
+       self.SendFlags |= SF_TRIGGER_UPDATE;
+#endif
+}
+
+void door_reset()
+{
+       SUB_SETORIGIN(self, self.pos1);
+       self.SUB_VELOCITY = '0 0 0';
+       self.state = STATE_BOTTOM;
+       self.SUB_THINK = func_null;
+       self.SUB_NEXTTHINK = 0;
+
+#ifdef SVQC
+       self.SendFlags |= SF_TRIGGER_RESET;
+#endif
+}
+
+#ifdef SVQC
+
+// spawnflags require key (for now only func_door)
+void spawnfunc_func_door()
+{
+       // Quake 1 keys compatibility
+       if (self.spawnflags & SPAWNFLAGS_GOLD_KEY)
+               self.itemkeys |= ITEM_KEY_BIT(0);
+       if (self.spawnflags & SPAWNFLAGS_SILVER_KEY)
+               self.itemkeys |= ITEM_KEY_BIT(1);
+
+       SetMovedir ();
+
+       self.max_health = self.health;
+       if (!InitMovingBrushTrigger())
+               return;
+       self.effects |= EF_LOWPRECISION;
+       self.classname = "door";
+
+       self.blocked = door_blocked;
+       self.use = door_use;
+
+       if(self.dmg && (self.message == ""))
+               self.message = "was squished";
+       if(self.dmg && (self.message2 == ""))
+               self.message2 = "was squished by";
+
+       if (self.sounds > 0)
+       {
+               precache_sound ("plats/medplat1.wav");
+               precache_sound ("plats/medplat2.wav");
+               self.noise2 = "plats/medplat1.wav";
+               self.noise1 = "plats/medplat2.wav";
+       }
+
+       if (!self.speed)
+               self.speed = 100;
+       if (!self.wait)
+               self.wait = 3;
+       if (!self.lip)
+               self.lip = 8;
+
+       self.pos1 = self.SUB_ORIGIN;
+       self.pos2 = self.pos1 + self.movedir*(fabs(self.movedir*self.size) - self.lip);
+
+// DOOR_START_OPEN is to allow an entity to be lighted in the closed position
+// but spawn in the open position
+       if (self.spawnflags & DOOR_START_OPEN)
+               InitializeEntity(self, door_init_startopen, INITPRIO_SETLOCATION);
+
+       self.state = STATE_BOTTOM;
+
+       if (self.health)
+       {
+               self.takedamage = DAMAGE_YES;
+               self.event_damage = door_damage;
+       }
+
+       if (self.items)
+               self.wait = -1;
+
+       self.touch = door_touch;
+
+// LinkDoors can't be done until all of the doors have been spawned, so
+// the sizes can be detected properly.
+       InitializeEntity(self, LinkDoors, INITPRIO_LINKDOORS);
+
+       self.reset = door_reset;
+}
+
+#elif defined(CSQC)
+
+void door_draw()
+{
+       Movetype_Physics_NoMatchServer();
+
+       trigger_draw_generic();
+}
+
+void ent_door()
+{
+       float sf = ReadByte();
+
+       if(sf & SF_TRIGGER_INIT)
+       {
+               self.classname = strzone(ReadString());
+               self.spawnflags = ReadByte();
+
+               self.mdl = strzone(ReadString());
+               setmodel(self, self.mdl);
+
+               trigger_common_read(true);
+
+               self.pos1_x = ReadCoord();
+               self.pos1_y = ReadCoord();
+               self.pos1_z = ReadCoord();
+               self.pos2_x = ReadCoord();
+               self.pos2_y = ReadCoord();
+               self.pos2_z = ReadCoord();
+
+               self.size_x = ReadCoord();
+               self.size_y = ReadCoord();
+               self.size_z = ReadCoord();
+
+               self.wait = ReadShort();
+               self.speed = ReadShort();
+               self.lip = ReadByte();
+               self.state = ReadByte();
+               self.SUB_LTIME = ReadCoord();
+
+               self.solid = SOLID_BSP;
+               self.movetype = MOVETYPE_PUSH;
+               self.trigger_touch = door_touch;
+               self.draw = door_draw;
+               self.drawmask = MASK_NORMAL;
+               self.use = door_use;
+
+               LinkDoors();
+
+               if(self.spawnflags & DOOR_START_OPEN)
+                       door_init_startopen();
+
+               self.move_time = time;
+               self.move_origin = self.origin;
+               self.move_movetype = MOVETYPE_PUSH;
+               self.move_angles = self.angles;
+               self.move_blocked = door_blocked;
+       }
+
+       if(sf & SF_TRIGGER_RESET)
+       {
+               door_reset();
+       }
+
+       if(sf & SF_TRIGGER_UPDATE)
+       {
+               self.origin_x = ReadCoord();
+               self.origin_y = ReadCoord();
+               self.origin_z = ReadCoord();
+               setorigin(self, self.origin);
+               self.move_origin = self.origin;
+
+               self.pos1_x = ReadCoord();
+               self.pos1_y = ReadCoord();
+               self.pos1_z = ReadCoord();
+               self.pos2_x = ReadCoord();
+               self.pos2_y = ReadCoord();
+               self.pos2_z = ReadCoord();
+       }
+}
+
+#endif
diff --git a/qcsrc/common/triggers/func/door.qh b/qcsrc/common/triggers/func/door.qh
new file mode 100644 (file)
index 0000000..e91061f
--- /dev/null
@@ -0,0 +1,17 @@
+// door constants
+const int DOOR_START_OPEN = 1;
+const int DOOR_DONT_LINK = 4;
+const int DOOR_TOGGLE = 32;
+
+const int DOOR_NOSPLASH = 256; // generic anti-splashdamage spawnflag
+
+const int SPAWNFLAGS_GOLD_KEY = 8;
+const int SPAWNFLAGS_SILVER_KEY = 16;
+
+#ifdef CSQC
+// stuff for preload
+void ent_door();
+
+// abused
+.float attack_finished_single;
+#endif
diff --git a/qcsrc/common/triggers/func/door_rotating.qc b/qcsrc/common/triggers/func/door_rotating.qc
new file mode 100644 (file)
index 0000000..bdf05a0
--- /dev/null
@@ -0,0 +1,126 @@
+#ifdef SVQC
+/*QUAKED spawnfunc_func_door_rotating (0 .5 .8) ? START_OPEN BIDIR DOOR_DONT_LINK BIDIR_IN_DOWN x TOGGLE X_AXIS Y_AXIS
+if two doors touch, they are assumed to be connected and operate as a unit.
+
+TOGGLE causes the door to wait in both the start and end states for a trigger event.
+
+BIDIR makes the door work bidirectional, so that the opening direction is always away from the requestor.
+The usage of bidirectional doors requires two manually instantiated triggers (trigger_multiple), the one to open it in the other direction
+must have set trigger_reverse to 1.
+BIDIR_IN_DOWN will the door prevent from reopening while closing if it is triggered from the other side.
+
+START_OPEN causes the door to move to its destination when spawned, and operate in reverse.  It is used to temporarily or permanently close off an area when triggered (not usefull for touch or takedamage doors).
+
+"message"      is printed when the door is touched if it is a trigger door and it hasn't been fired yet
+"angle"                determines the destination angle for opening. negative values reverse the direction.
+"targetname"    if set, no touch field will be spawned and a remote button or trigger field activates the door.
+"health"       if set, door must be shot open
+"speed"                movement speed (100 default)
+"wait"         wait before returning (3 default, -1 = never return)
+"dmg"          damage to inflict when blocked (2 default)
+"sounds"
+0)     no sound
+1)     stone
+2)     base
+3)     stone chain
+4)     screechy metal
+FIXME: only one sound set available at the time being
+*/
+
+void door_rotating_reset()
+{
+       self.angles = self.pos1;
+       self.avelocity = '0 0 0';
+       self.state = STATE_BOTTOM;
+       self.think = func_null;
+       self.nextthink = 0;
+}
+
+void door_rotating_init_startopen()
+{
+       self.angles = self.movedir;
+       self.pos2 = '0 0 0';
+       self.pos1 = self.movedir;
+}
+
+
+void spawnfunc_func_door_rotating()
+{
+
+       //if (!self.deathtype) // map makers can override this
+       //      self.deathtype = " got in the way";
+
+       // I abuse "movedir" for denoting the axis for now
+       if (self.spawnflags & 64) // X (untested)
+               self.movedir = '0 0 1';
+       else if (self.spawnflags & 128) // Y (untested)
+               self.movedir = '1 0 0';
+       else // Z
+               self.movedir = '0 1 0';
+
+       if (self.angles_y==0) self.angles_y = 90;
+
+       self.movedir = self.movedir * self.angles_y;
+       self.angles = '0 0 0';
+
+       self.max_health = self.health;
+       self.avelocity = self.movedir;
+       if (!InitMovingBrushTrigger())
+               return;
+       self.velocity = '0 0 0';
+       //self.effects |= EF_LOWPRECISION;
+       self.classname = "door_rotating";
+
+       self.blocked = door_blocked;
+       self.use = door_use;
+
+    if(self.spawnflags & 8)
+        self.dmg = 10000;
+
+    if(self.dmg && (self.message == ""))
+               self.message = "was squished";
+    if(self.dmg && (self.message2 == ""))
+               self.message2 = "was squished by";
+
+    if (self.sounds > 0)
+       {
+               precache_sound ("plats/medplat1.wav");
+               precache_sound ("plats/medplat2.wav");
+               self.noise2 = "plats/medplat1.wav";
+               self.noise1 = "plats/medplat2.wav";
+       }
+
+       if (!self.speed)
+               self.speed = 50;
+       if (!self.wait)
+               self.wait = 1;
+       self.lip = 0; // self.lip is used to remember reverse opening direction for door_rotating
+
+       self.pos1 = '0 0 0';
+       self.pos2 = self.movedir;
+
+// DOOR_START_OPEN is to allow an entity to be lighted in the closed position
+// but spawn in the open position
+       if (self.spawnflags & DOOR_START_OPEN)
+               InitializeEntity(self, door_rotating_init_startopen, INITPRIO_SETLOCATION);
+
+       self.state = STATE_BOTTOM;
+
+       if (self.health)
+       {
+               self.takedamage = DAMAGE_YES;
+               self.event_damage = door_damage;
+       }
+
+       if (self.items)
+               self.wait = -1;
+
+       self.touch = door_touch;
+
+// LinkDoors can't be done until all of the doors have been spawned, so
+// the sizes can be detected properly.
+       InitializeEntity(self, LinkDoors, INITPRIO_LINKDOORS);
+
+       self.reset = door_rotating_reset;
+}
+#endif
diff --git a/qcsrc/common/triggers/func/door_secret.qc b/qcsrc/common/triggers/func/door_secret.qc
new file mode 100644 (file)
index 0000000..a0a44bb
--- /dev/null
@@ -0,0 +1,236 @@
+#ifdef SVQC
+void() fd_secret_move1;
+void() fd_secret_move2;
+void() fd_secret_move3;
+void() fd_secret_move4;
+void() fd_secret_move5;
+void() fd_secret_move6;
+void() fd_secret_done;
+
+const float SECRET_OPEN_ONCE = 1;              // stays open
+const float SECRET_1ST_LEFT = 2;               // 1st move is left of arrow
+const float SECRET_1ST_DOWN = 4;               // 1st move is down from arrow
+const float SECRET_NO_SHOOT = 8;               // only opened by trigger
+const float SECRET_YES_SHOOT = 16;     // shootable even if targeted
+
+void fd_secret_use()
+{
+       float temp;
+       string message_save;
+
+       self.health = 10000;
+       self.bot_attack = true;
+
+       // exit if still moving around...
+       if (self.origin != self.oldorigin)
+               return;
+
+       message_save = self.message;
+       self.message = ""; // no more message
+       SUB_UseTargets();                               // fire all targets / killtargets
+       self.message = message_save;
+
+       self.velocity = '0 0 0';
+
+       // Make a sound, wait a little...
+
+       if (self.noise1 != "")
+               sound(self, CH_TRIGGER_SINGLE, self.noise1, VOL_BASE, ATTEN_NORM);
+       self.SUB_NEXTTHINK = self.SUB_LTIME + 0.1;
+
+       temp = 1 - (self.spawnflags & SECRET_1ST_LEFT); // 1 or -1
+       makevectors(self.mangle);
+
+       if (!self.t_width)
+       {
+               if (self.spawnflags & SECRET_1ST_DOWN)
+                       self.t_width = fabs(v_up * self.size);
+               else
+                       self.t_width = fabs(v_right * self.size);
+       }
+
+       if (!self.t_length)
+               self.t_length = fabs(v_forward * self.size);
+
+       if (self.spawnflags & SECRET_1ST_DOWN)
+               self.dest1 = self.origin - v_up * self.t_width;
+       else
+               self.dest1 = self.origin + v_right * (self.t_width * temp);
+
+       self.dest2 = self.dest1 + v_forward * self.t_length;
+       SUB_CalcMove(self.dest1, TSPEED_LINEAR, self.speed, fd_secret_move1);
+       if (self.noise2 != "")
+               sound(self, CH_TRIGGER_SINGLE, self.noise2, VOL_BASE, ATTEN_NORM);
+}
+
+void fd_secret_damage(entity inflictor, entity attacker, float damage, int deathtype, vector hitloc, vector force)
+{
+       fd_secret_use();
+}
+
+// Wait after first movement...
+void fd_secret_move1()
+{
+       self.SUB_NEXTTHINK = self.SUB_LTIME + 1.0;
+       self.think = fd_secret_move2;
+       if (self.noise3 != "")
+               sound(self, CH_TRIGGER_SINGLE, self.noise3, VOL_BASE, ATTEN_NORM);
+}
+
+// Start moving sideways w/sound...
+void fd_secret_move2()
+{
+       if (self.noise2 != "")
+               sound(self, CH_TRIGGER_SINGLE, self.noise2, VOL_BASE, ATTEN_NORM);
+       SUB_CalcMove(self.dest2, TSPEED_LINEAR, self.speed, fd_secret_move3);
+}
+
+// Wait here until time to go back...
+void fd_secret_move3()
+{
+       if (self.noise3 != "")
+               sound(self, CH_TRIGGER_SINGLE, self.noise3, VOL_BASE, ATTEN_NORM);
+       if (!(self.spawnflags & SECRET_OPEN_ONCE))
+       {
+               self.SUB_NEXTTHINK = self.SUB_LTIME + self.wait;
+               self.think = fd_secret_move4;
+       }
+}
+
+// Move backward...
+void fd_secret_move4()
+{
+       if (self.noise2 != "")
+               sound(self, CH_TRIGGER_SINGLE, self.noise2, VOL_BASE, ATTEN_NORM);
+       SUB_CalcMove(self.dest1, TSPEED_LINEAR, self.speed, fd_secret_move5);
+}
+
+// Wait 1 second...
+void fd_secret_move5()
+{
+       self.SUB_NEXTTHINK = self.SUB_LTIME + 1.0;
+       self.think = fd_secret_move6;
+       if (self.noise3 != "")
+               sound(self, CH_TRIGGER_SINGLE, self.noise3, VOL_BASE, ATTEN_NORM);
+}
+
+void fd_secret_move6()
+{
+       if (self.noise2 != "")
+               sound(self, CH_TRIGGER_SINGLE, self.noise2, VOL_BASE, ATTEN_NORM);
+       SUB_CalcMove(self.oldorigin, TSPEED_LINEAR, self.speed, fd_secret_done);
+}
+
+void fd_secret_done()
+{
+       if (self.spawnflags&SECRET_YES_SHOOT)
+       {
+               self.health = 10000;
+               self.takedamage = DAMAGE_YES;
+               //self.th_pain = fd_secret_use;
+       }
+       if (self.noise3 != "")
+               sound(self, CH_TRIGGER_SINGLE, self.noise3, VOL_BASE, ATTEN_NORM);
+}
+
+void secret_blocked()
+{
+       if (time < self.attack_finished_single)
+               return;
+       self.attack_finished_single = time + 0.5;
+       //T_Damage (other, self, self, self.dmg, self.dmg, self.deathtype, DT_IMPACT, (self.absmin + self.absmax) * 0.5, '0 0 0', Obituary_Generic);
+}
+
+/*
+==============
+secret_touch
+
+Prints messages
+================
+*/
+void secret_touch()
+{
+       if (!other.iscreature)
+               return;
+       if (self.attack_finished_single > time)
+               return;
+
+       self.attack_finished_single = time + 2;
+
+       if (self.message)
+       {
+               if (IS_CLIENT(other))
+                       centerprint(other, self.message);
+               play2(other, "misc/talk.wav");
+       }
+}
+
+void secret_reset()
+{
+       if (self.spawnflags&SECRET_YES_SHOOT)
+       {
+               self.health = 10000;
+               self.takedamage = DAMAGE_YES;
+       }
+       setorigin(self, self.oldorigin);
+       self.think = func_null;
+       self.SUB_NEXTTHINK = 0;
+}
+
+/*QUAKED spawnfunc_func_door_secret (0 .5 .8) ? open_once 1st_left 1st_down no_shoot always_shoot
+Basic secret door. Slides back, then to the side. Angle determines direction.
+wait  = # of seconds before coming back
+1st_left = 1st move is left of arrow
+1st_down = 1st move is down from arrow
+always_shoot = even if targeted, keep shootable
+t_width = override WIDTH to move back (or height if going down)
+t_length = override LENGTH to move sideways
+"dmg"          damage to inflict when blocked (2 default)
+
+If a secret door has a targetname, it will only be opened by it's botton or trigger, not by damage.
+"sounds"
+1) medieval
+2) metal
+3) base
+*/
+
+void spawnfunc_func_door_secret()
+{
+       /*if (!self.deathtype) // map makers can override this
+               self.deathtype = " got in the way";*/
+
+       if (!self.dmg)
+               self.dmg = 2;
+
+       // Magic formula...
+       self.mangle = self.angles;
+       self.angles = '0 0 0';
+       self.classname = "door";
+       if (!InitMovingBrushTrigger())
+               return;
+       self.effects |= EF_LOWPRECISION;
+
+       self.touch = secret_touch;
+       self.blocked = secret_blocked;
+       self.speed = 50;
+       self.use = fd_secret_use;
+       IFTARGETED
+       {
+       }
+       else
+               self.spawnflags |= SECRET_YES_SHOOT;
+
+       if(self.spawnflags&SECRET_YES_SHOOT)
+       {
+               self.health = 10000;
+               self.takedamage = DAMAGE_YES;
+               self.event_damage = fd_secret_damage;
+       }
+       self.oldorigin = self.origin;
+       if (!self.wait)
+               self.wait = 5;          // 5 seconds before closing
+
+       self.reset = secret_reset;
+       secret_reset();
+}
+#endif
diff --git a/qcsrc/common/triggers/func/fourier.qc b/qcsrc/common/triggers/func/fourier.qc
new file mode 100644 (file)
index 0000000..19dceef
--- /dev/null
@@ -0,0 +1,89 @@
+#ifdef SVQC
+/*QUAKED spawnfunc_func_fourier (0 .5 .8) ?
+Brush model that moves in a pattern of added up sine waves, can be used e.g. for circular motions.
+netname: list of <frequencymultiplier> <phase> <x> <y> <z> quadruples, separated by spaces; note that phase 0 represents a sine wave, and phase 0.25 a cosine wave (by default, it uses 1 0 0 0 1, to match func_bobbing's defaults
+speed: how long one cycle of frequency multiplier 1 in seconds (default 4)
+height: amplitude modifier (default 32)
+phase: cycle timing adjustment (0-1 as a fraction of the cycle, default 0)
+noise: path/name of looping .wav file to play.
+dmg: Do this mutch dmg every .dmgtime intervall when blocked
+dmgtime: See above.
+*/
+
+void func_fourier_controller_think()
+{
+       vector v;
+       float n, i, t;
+
+       self.nextthink = time + 0.1;
+       if(self.owner.active != ACTIVE_ACTIVE)
+       {
+               self.owner.velocity = '0 0 0';
+               return;
+       }
+
+
+       n = floor((tokenize_console(self.owner.netname)) / 5);
+       t = self.nextthink * self.owner.cnt + self.owner.phase * 360;
+
+       v = self.owner.destvec;
+
+       for(i = 0; i < n; ++i)
+       {
+               makevectors((t * stof(argv(i*5)) + stof(argv(i*5+1)) * 360) * '0 1 0');
+               v = v + ('1 0 0' * stof(argv(i*5+2)) + '0 1 0' * stof(argv(i*5+3)) + '0 0 1' * stof(argv(i*5+4))) * self.owner.height * v_forward_y;
+       }
+
+       if(self.owner.classname == "func_fourier") // don't brake stuff if the func_fourier was killtarget'ed
+               // * 10 so it will arrive in 0.1 sec
+               self.owner.velocity = (v - self.owner.origin) * 10;
+}
+
+void spawnfunc_func_fourier()
+{
+       entity controller;
+       if (self.noise != "")
+       {
+               precache_sound(self.noise);
+               soundto(MSG_INIT, self, CH_TRIGGER_SINGLE, self.noise, VOL_BASE, ATTEN_IDLE);
+       }
+
+       if (!self.speed)
+               self.speed = 4;
+       if (!self.height)
+               self.height = 32;
+       self.destvec = self.origin;
+       self.cnt = 360 / self.speed;
+
+       self.blocked = generic_plat_blocked;
+       if(self.dmg && (self.message == ""))
+               self.message = " was squished";
+    if(self.dmg && (self.message2 == ""))
+               self.message2 = "was squished by";
+       if(self.dmg && (!self.dmgtime))
+               self.dmgtime = 0.25;
+       self.dmgtime2 = time;
+
+       if(self.netname == "")
+               self.netname = "1 0 0 0 1";
+
+       if (!InitMovingBrushTrigger())
+               return;
+
+       self.active = ACTIVE_ACTIVE;
+
+       // wait for targets to spawn
+       controller = spawn();
+       controller.classname = "func_fourier_controller";
+       controller.owner = self;
+       controller.nextthink = time + 1;
+       controller.think = func_fourier_controller_think;
+       self.SUB_NEXTTHINK = self.SUB_LTIME + 999999999;
+       self.SUB_THINK = SUB_NullThink; // for PushMove
+
+       // Savage: Reduce bandwith, critical on e.g. nexdm02
+       self.effects |= EF_LOWPRECISION;
+
+       // TODO make a reset function for this one
+}
+#endif
diff --git a/qcsrc/common/triggers/func/include.qc b/qcsrc/common/triggers/func/include.qc
new file mode 100644 (file)
index 0000000..290c2e9
--- /dev/null
@@ -0,0 +1,19 @@
+#include "include.qh"
+
+#include "bobbing.qc"
+#include "breakable.qc"
+#include "button.qc"
+#include "conveyor.qc"
+#include "door.qc"
+#include "door_rotating.qc"
+#include "door_secret.qc"
+#include "fourier.qc"
+#include "ladder.qc"
+#include "pendulum.qc"
+#include "plat.qc"
+#include "pointparticles.qc"
+#include "rainsnow.qc"
+#include "rotating.qc"
+#include "stardust.qc"
+#include "train.qc"
+#include "vectormamamam.qc"
diff --git a/qcsrc/common/triggers/func/include.qh b/qcsrc/common/triggers/func/include.qh
new file mode 100644 (file)
index 0000000..624c0b5
--- /dev/null
@@ -0,0 +1,12 @@
+#ifndef TRIGGERS_FUNC_INCLUDE_H
+#define TRIGGERS_FUNC_INCLUDE_H
+
+#include "conveyor.qh"
+#include "door.qh"
+#include "ladder.qh"
+#include "plat.qh"
+#include "rainsnow.qh"
+#include "pointparticles.qh"
+#include "train.qh"
+
+#endif
diff --git a/qcsrc/common/triggers/func/ladder.qc b/qcsrc/common/triggers/func/ladder.qc
new file mode 100644 (file)
index 0000000..f0216cc
--- /dev/null
@@ -0,0 +1,83 @@
+void func_ladder_touch()
+{
+#ifdef SVQC
+       if (!other.iscreature)
+               return;
+       if (other.vehicle_flags & VHF_ISVEHICLE)
+               return;
+#endif
+#ifdef CSQC
+       if(other.classname != "csqcmodel")
+               return;
+#endif
+
+       EXACTTRIGGER_TOUCH;
+
+       other.ladder_time = time + 0.1;
+       other.ladder_entity = self;
+}
+
+#ifdef SVQC
+float func_ladder_send(entity to, float sf)
+{
+       WriteByte(MSG_ENTITY, ENT_CLIENT_LADDER);
+
+       WriteString(MSG_ENTITY, self.classname);
+       WriteByte(MSG_ENTITY, self.skin);
+       WriteByte(MSG_ENTITY, self.speed);
+       WriteString(MSG_ENTITY, self.mdl);
+
+       trigger_common_write(false);
+
+       return true;
+}
+
+void func_ladder_link()
+{
+       //self.SendEntity = func_ladder_send;
+       //self.SendFlags = 0xFFFFFF;
+       //self.model = "null";
+}
+
+void spawnfunc_func_ladder()
+{
+       self.mdl = self.model;
+       EXACTTRIGGER_INIT;
+       self.touch = func_ladder_touch;
+
+       func_ladder_link();
+}
+
+void spawnfunc_func_water()
+{
+       self.mdl = self.model;
+       EXACTTRIGGER_INIT;
+       self.touch = func_ladder_touch;
+
+       func_ladder_link();
+}
+
+#elif defined(CSQC)
+.float speed;
+
+void ent_func_ladder()
+{
+       self.classname = strzone(ReadString());
+       self.skin = ReadByte();
+       self.speed = ReadByte();
+       self.model = strzone(ReadString());
+
+       trigger_common_read(false);
+       self.mins = self.maxs = '0 0 0';
+
+       self.solid = SOLID_TRIGGER;
+       self.draw = trigger_draw_generic;
+       self.trigger_touch = func_ladder_touch;
+       self.drawmask = MASK_NORMAL;
+       self.move_time = time;
+       self.entremove = trigger_remove_generic;
+
+       //precache_model(self.mdl);
+       EXACTTRIGGER_INIT;
+}
+#endif
diff --git a/qcsrc/common/triggers/func/ladder.qh b/qcsrc/common/triggers/func/ladder.qh
new file mode 100644 (file)
index 0000000..11eeeda
--- /dev/null
@@ -0,0 +1,6 @@
+.float ladder_time;
+.entity ladder_entity;
+
+#ifdef CSQC
+void ent_func_ladder();
+#endif
diff --git a/qcsrc/common/triggers/func/pendulum.qc b/qcsrc/common/triggers/func/pendulum.qc
new file mode 100644 (file)
index 0000000..7d3eff7
--- /dev/null
@@ -0,0 +1,77 @@
+#ifdef SVQC
+.float freq;
+void func_pendulum_controller_think()
+{
+       float v;
+       self.nextthink = time + 0.1;
+
+       if (!(self.owner.active == ACTIVE_ACTIVE))
+       {
+               self.owner.avelocity_x = 0;
+               return;
+       }
+
+       // calculate sinewave using makevectors
+       makevectors((self.nextthink * self.owner.freq + self.owner.phase) * '0 360 0');
+       v = self.owner.speed * v_forward_y + self.cnt;
+       if(self.owner.classname == "func_pendulum") // don't brake stuff if the func_bobbing was killtarget'ed
+       {
+               // * 10 so it will arrive in 0.1 sec
+               self.owner.avelocity_z = (remainder(v - self.owner.angles_z, 360)) * 10;
+       }
+}
+
+void spawnfunc_func_pendulum()
+{
+       entity controller;
+       if (self.noise != "")
+       {
+               precache_sound(self.noise);
+               soundto(MSG_INIT, self, CH_TRIGGER_SINGLE, self.noise, VOL_BASE, ATTEN_IDLE);
+       }
+
+       self.active = ACTIVE_ACTIVE;
+
+       // keys: angle, speed, phase, noise, freq
+
+       if(!self.speed)
+               self.speed = 30;
+       // not initializing self.dmg to 2, to allow damageless pendulum
+
+       if(self.dmg && (self.message == ""))
+               self.message = " was squished";
+       if(self.dmg && (self.message2 == ""))
+               self.message2 = "was squished by";
+       if(self.dmg && (!self.dmgtime))
+               self.dmgtime = 0.25;
+       self.dmgtime2 = time;
+
+       self.blocked = generic_plat_blocked;
+
+       self.avelocity_z = 0.0000001;
+       if (!InitMovingBrushTrigger())
+               return;
+
+       if(!self.freq)
+       {
+               // find pendulum length (same formula as Q3A)
+               self.freq = 1 / (M_PI * 2) * sqrt(autocvar_sv_gravity / (3 * max(8, fabs(self.mins_z))));
+       }
+
+       // copy initial angle
+       self.cnt = self.angles_z;
+
+       // wait for targets to spawn
+       controller = spawn();
+       controller.classname = "func_pendulum_controller";
+       controller.owner = self;
+       controller.nextthink = time + 1;
+       controller.think = func_pendulum_controller_think;
+       self.nextthink = self.SUB_LTIME + 999999999;
+       self.SUB_THINK = SUB_NullThink; // for PushMove
+
+       //self.effects |= EF_LOWPRECISION;
+
+       // TODO make a reset function for this one
+}
+#endif
diff --git a/qcsrc/common/triggers/func/plat.qc b/qcsrc/common/triggers/func/plat.qc
new file mode 100644 (file)
index 0000000..d36b1c6
--- /dev/null
@@ -0,0 +1,201 @@
+#ifdef SVQC
+void plat_link();
+
+void plat_delayedinit()
+{
+       plat_link();
+       plat_spawn_inside_trigger(); // the "start moving" trigger
+}
+
+float plat_send(entity to, float sf)
+{
+       WriteByte(MSG_ENTITY, ENT_CLIENT_PLAT);
+       WriteByte(MSG_ENTITY, sf);
+
+       if(sf & SF_TRIGGER_INIT)
+       {
+               WriteByte(MSG_ENTITY, self.platmovetype_start);
+               WriteByte(MSG_ENTITY, self.platmovetype_turn);
+               WriteByte(MSG_ENTITY, self.platmovetype_end);
+               WriteByte(MSG_ENTITY, self.spawnflags);
+
+               WriteString(MSG_ENTITY, self.model);
+
+               trigger_common_write(true);
+
+               WriteCoord(MSG_ENTITY, self.pos1_x);
+               WriteCoord(MSG_ENTITY, self.pos1_y);
+               WriteCoord(MSG_ENTITY, self.pos1_z);
+               WriteCoord(MSG_ENTITY, self.pos2_x);
+               WriteCoord(MSG_ENTITY, self.pos2_y);
+               WriteCoord(MSG_ENTITY, self.pos2_z);
+
+               WriteCoord(MSG_ENTITY, self.size_x);
+               WriteCoord(MSG_ENTITY, self.size_y);
+               WriteCoord(MSG_ENTITY, self.size_z);
+
+               WriteAngle(MSG_ENTITY, self.mangle_x);
+               WriteAngle(MSG_ENTITY, self.mangle_y);
+               WriteAngle(MSG_ENTITY, self.mangle_z);
+
+               WriteShort(MSG_ENTITY, self.speed);
+               WriteShort(MSG_ENTITY, self.height);
+               WriteByte(MSG_ENTITY, self.lip);
+               WriteByte(MSG_ENTITY, self.state);
+
+               WriteShort(MSG_ENTITY, self.dmg);
+       }
+
+       if(sf & SF_TRIGGER_RESET)
+       {
+               // used on client
+       }
+
+       return true;
+}
+
+void plat_link()
+{
+       //Net_LinkEntity(self, 0, false, plat_send);
+}
+
+void spawnfunc_func_plat()
+{
+       if (self.sounds == 0)
+               self.sounds = 2;
+
+    if(self.spawnflags & 4)
+        self.dmg = 10000;
+
+    if(self.dmg && (self.message == ""))
+               self.message = "was squished";
+    if(self.dmg && (self.message2 == ""))
+               self.message2 = "was squished by";
+
+       if (self.sounds == 1)
+       {
+               precache_sound ("plats/plat1.wav");
+               precache_sound ("plats/plat2.wav");
+               self.noise = "plats/plat1.wav";
+               self.noise1 = "plats/plat2.wav";
+       }
+
+       if (self.sounds == 2)
+       {
+               precache_sound ("plats/medplat1.wav");
+               precache_sound ("plats/medplat2.wav");
+               self.noise = "plats/medplat1.wav";
+               self.noise1 = "plats/medplat2.wav";
+       }
+
+       if (self.sound1)
+       {
+               precache_sound (self.sound1);
+               self.noise = self.sound1;
+       }
+       if (self.sound2)
+       {
+               precache_sound (self.sound2);
+               self.noise1 = self.sound2;
+       }
+
+       self.mangle = self.angles;
+       self.angles = '0 0 0';
+
+       self.classname = "plat";
+       if (!InitMovingBrushTrigger())
+               return;
+       self.effects |= EF_LOWPRECISION;
+       setsize (self, self.mins , self.maxs);
+
+       self.blocked = plat_crush;
+
+       if (!self.speed)
+               self.speed = 150;
+       if (!self.lip)
+               self.lip = 16;
+       if (!self.height)
+               self.height = self.size_z - self.lip;
+
+       self.pos1 = self.origin;
+       self.pos2 = self.origin;
+       self.pos2_z = self.origin_z - self.height;
+
+       self.reset = plat_reset;
+       plat_reset();
+
+       InitializeEntity(self, plat_delayedinit, INITPRIO_FINDTARGET);
+}
+#elif defined(CSQC)
+void plat_draw()
+{
+       Movetype_Physics_NoMatchServer();
+       //Movetype_Physics_MatchServer(autocvar_cl_projectiles_sloppy);
+}
+
+void ent_plat()
+{
+       float sf = ReadByte();
+
+       if(sf & SF_TRIGGER_INIT)
+       {
+               self.platmovetype_start = ReadByte();
+               self.platmovetype_turn = ReadByte();
+               self.platmovetype_end = ReadByte();
+               self.spawnflags = ReadByte();
+
+               self.model = strzone(ReadString());
+               setmodel(self, self.model);
+
+               trigger_common_read(true);
+
+               self.pos1_x = ReadCoord();
+               self.pos1_y = ReadCoord();
+               self.pos1_z = ReadCoord();
+               self.pos2_x = ReadCoord();
+               self.pos2_y = ReadCoord();
+               self.pos2_z = ReadCoord();
+
+               self.size_x = ReadCoord();
+               self.size_y = ReadCoord();
+               self.size_z = ReadCoord();
+
+               self.mangle_x = ReadAngle();
+               self.mangle_y = ReadAngle();
+               self.mangle_z = ReadAngle();
+
+               self.speed = ReadShort();
+               self.height = ReadShort();
+               self.lip = ReadByte();
+               self.state = ReadByte();
+
+               self.dmg = ReadShort();
+
+               self.classname = "plat";
+               self.solid = SOLID_BSP;
+               self.movetype = MOVETYPE_PUSH;
+               self.drawmask = MASK_NORMAL;
+               self.draw = plat_draw;
+               self.use = plat_use;
+               self.entremove = trigger_remove_generic;
+
+               plat_reset(); // also called here
+
+               self.move_movetype = MOVETYPE_PUSH;
+               self.move_origin = self.origin;
+               self.move_angles = self.angles;
+               self.move_time = time;
+
+               plat_spawn_inside_trigger();
+       }
+
+       if(sf & SF_TRIGGER_RESET)
+       {
+               plat_reset();
+
+               self.move_origin = self.origin;
+               self.move_angles = self.angles;
+               self.move_time = time;
+       }
+}
+#endif
diff --git a/qcsrc/common/triggers/func/plat.qh b/qcsrc/common/triggers/func/plat.qh
new file mode 100644 (file)
index 0000000..e3562f4
--- /dev/null
@@ -0,0 +1,3 @@
+#ifdef CSQC
+void ent_plat();
+#endif
diff --git a/qcsrc/common/triggers/func/pointparticles.qc b/qcsrc/common/triggers/func/pointparticles.qc
new file mode 100644 (file)
index 0000000..9b32371
--- /dev/null
@@ -0,0 +1,362 @@
+#ifdef CSQC
+       #include "../../../client/particles.qh"
+#endif
+
+#ifdef SVQC
+// NOTE: also contains func_sparks
+
+float pointparticles_SendEntity(entity to, float fl)
+{
+       WriteByte(MSG_ENTITY, ENT_CLIENT_POINTPARTICLES);
+
+       // optional features to save space
+       fl = fl & 0x0F;
+       if(self.spawnflags & 2)
+               fl |= 0x10; // absolute count on toggle-on
+       if(self.movedir != '0 0 0' || self.velocity != '0 0 0')
+               fl |= 0x20; // 4 bytes - saves CPU
+       if(self.waterlevel || self.count != 1)
+               fl |= 0x40; // 4 bytes - obscure features almost never used
+       if(self.mins != '0 0 0' || self.maxs != '0 0 0')
+               fl |= 0x80; // 14 bytes - saves lots of space
+
+       WriteByte(MSG_ENTITY, fl);
+       if(fl & 2)
+       {
+               if(self.state)
+                       WriteCoord(MSG_ENTITY, self.impulse);
+               else
+                       WriteCoord(MSG_ENTITY, 0); // off
+       }
+       if(fl & 4)
+       {
+               WriteCoord(MSG_ENTITY, self.origin_x);
+               WriteCoord(MSG_ENTITY, self.origin_y);
+               WriteCoord(MSG_ENTITY, self.origin_z);
+       }
+       if(fl & 1)
+       {
+               if(self.model != "null")
+               {
+                       WriteShort(MSG_ENTITY, self.modelindex);
+                       if(fl & 0x80)
+                       {
+                               WriteCoord(MSG_ENTITY, self.mins_x);
+                               WriteCoord(MSG_ENTITY, self.mins_y);
+                               WriteCoord(MSG_ENTITY, self.mins_z);
+                               WriteCoord(MSG_ENTITY, self.maxs_x);
+                               WriteCoord(MSG_ENTITY, self.maxs_y);
+                               WriteCoord(MSG_ENTITY, self.maxs_z);
+                       }
+               }
+               else
+               {
+                       WriteShort(MSG_ENTITY, 0);
+                       if(fl & 0x80)
+                       {
+                               WriteCoord(MSG_ENTITY, self.maxs_x);
+                               WriteCoord(MSG_ENTITY, self.maxs_y);
+                               WriteCoord(MSG_ENTITY, self.maxs_z);
+                       }
+               }
+               WriteShort(MSG_ENTITY, self.cnt);
+               if(fl & 0x20)
+               {
+                       WriteShort(MSG_ENTITY, compressShortVector(self.velocity));
+                       WriteShort(MSG_ENTITY, compressShortVector(self.movedir));
+               }
+               if(fl & 0x40)
+               {
+                       WriteShort(MSG_ENTITY, self.waterlevel * 16.0);
+                       WriteByte(MSG_ENTITY, self.count * 16.0);
+               }
+               WriteString(MSG_ENTITY, self.noise);
+               if(self.noise != "")
+               {
+                       WriteByte(MSG_ENTITY, floor(self.atten * 64));
+                       WriteByte(MSG_ENTITY, floor(self.volume * 255));
+               }
+               WriteString(MSG_ENTITY, self.bgmscript);
+               if(self.bgmscript != "")
+               {
+                       WriteByte(MSG_ENTITY, floor(self.bgmscriptattack * 64));
+                       WriteByte(MSG_ENTITY, floor(self.bgmscriptdecay * 64));
+                       WriteByte(MSG_ENTITY, floor(self.bgmscriptsustain * 255));
+                       WriteByte(MSG_ENTITY, floor(self.bgmscriptrelease * 64));
+               }
+       }
+       return 1;
+}
+
+void pointparticles_use()
+{
+       self.state = !self.state;
+       self.SendFlags |= 2;
+}
+
+void pointparticles_think()
+{
+       if(self.origin != self.oldorigin)
+       {
+               self.SendFlags |= 4;
+               self.oldorigin = self.origin;
+       }
+       self.nextthink = time;
+}
+
+void pointparticles_reset()
+{
+       if(self.spawnflags & 1)
+               self.state = 1;
+       else
+               self.state = 0;
+}
+
+void spawnfunc_func_pointparticles()
+{
+       if(self.model != "")
+               setmodel(self, self.model);
+       if(self.noise != "")
+               precache_sound (self.noise);
+
+       if(!self.bgmscriptsustain)
+               self.bgmscriptsustain = 1;
+       else if(self.bgmscriptsustain < 0)
+               self.bgmscriptsustain = 0;
+
+       if(!self.atten)
+               self.atten = ATTEN_NORM;
+       else if(self.atten < 0)
+               self.atten = 0;
+       if(!self.volume)
+               self.volume = 1;
+       if(!self.count)
+               self.count = 1;
+       if(!self.impulse)
+               self.impulse = 1;
+
+       if(!self.modelindex)
+       {
+               setorigin(self, self.origin + self.mins);
+               setsize(self, '0 0 0', self.maxs - self.mins);
+       }
+       if(!self.cnt)
+               self.cnt = particleeffectnum(self.mdl);
+
+       Net_LinkEntity(self, (self.spawnflags & 4), 0, pointparticles_SendEntity);
+
+       IFTARGETED
+       {
+               self.use = pointparticles_use;
+               self.reset = pointparticles_reset;
+               self.reset();
+       }
+       else
+               self.state = 1;
+       self.think = pointparticles_think;
+       self.nextthink = time;
+}
+
+void spawnfunc_func_sparks()
+{
+       // self.cnt is the amount of sparks that one burst will spawn
+       if(self.cnt < 1) {
+               self.cnt = 25.0; // nice default value
+       }
+
+       // self.wait is the probability that a sparkthink will spawn a spark shower
+       // range: 0 - 1, but 0 makes little sense, so...
+       if(self.wait < 0.05) {
+               self.wait = 0.25; // nice default value
+       }
+
+       self.count = self.cnt;
+       self.mins = '0 0 0';
+       self.maxs = '0 0 0';
+       self.velocity = '0 0 -1';
+       self.mdl = "TE_SPARK";
+       self.impulse = 10 * self.wait; // by default 2.5/sec
+       self.wait = 0;
+       self.cnt = 0; // use mdl
+
+       spawnfunc_func_pointparticles();
+}
+#elif defined(CSQC)
+
+void Draw_PointParticles()
+{
+       float n, i, fail;
+       vector p;
+       vector sz;
+       vector o;
+       o = self.origin;
+       sz = self.maxs - self.mins;
+       n = doBGMScript(self);
+       if(self.absolute == 2)
+       {
+               if(n >= 0)
+                       n = self.just_toggled ? self.impulse : 0;
+               else
+                       n = self.impulse * drawframetime;
+       }
+       else
+       {
+               n *= self.impulse * drawframetime;
+               if(self.just_toggled)
+                       if(n < 1)
+                               n = 1;
+       }
+       if(n == 0)
+               return;
+       fail = 0;
+       for(i = random(); i <= n && fail <= 64*n; ++i)
+       {
+               p = o + self.mins;
+               p.x += random() * sz.x;
+               p.y += random() * sz.y;
+               p.z += random() * sz.z;
+               if(WarpZoneLib_BoxTouchesBrush(p, p, self, world))
+               {
+                       if(self.movedir != '0 0 0')
+                       {
+                               traceline(p, p + normalize(self.movedir) * 4096, 0, world);
+                               p = trace_endpos;
+                               pointparticles(self.cnt, p, trace_plane_normal * vlen(self.movedir) + self.velocity + randomvec() * self.waterlevel, self.count);
+                       }
+                       else
+                       {
+                               pointparticles(self.cnt, p, self.velocity + randomvec() * self.waterlevel, self.count);
+                       }
+                       if(self.noise != "")
+                       {
+                               setorigin(self, p);
+                               sound(self, CH_AMBIENT, self.noise, VOL_BASE * self.volume, self.atten);
+                       }
+                       self.just_toggled = 0;
+               }
+               else if(self.absolute)
+               {
+                       ++fail;
+                       --i;
+               }
+       }
+       setorigin(self, o);
+}
+
+void Ent_PointParticles_Remove()
+{
+       if(self.noise)
+               strunzone(self.noise);
+       self.noise = string_null;
+       if(self.bgmscript)
+               strunzone(self.bgmscript);
+       self.bgmscript = string_null;
+}
+
+void Ent_PointParticles()
+{
+       float i;
+       vector v;
+       int f = ReadByte();
+       if(f & 2)
+       {
+               i = ReadCoord(); // density (<0: point, >0: volume)
+               if(i && !self.impulse && self.cnt) // self.cnt check is so it only happens if the ent already existed
+                       self.just_toggled = 1;
+               self.impulse = i;
+       }
+       if(f & 4)
+       {
+               self.origin_x = ReadCoord();
+               self.origin_y = ReadCoord();
+               self.origin_z = ReadCoord();
+       }
+       if(f & 1)
+       {
+               self.modelindex = ReadShort();
+               if(f & 0x80)
+               {
+                       if(self.modelindex)
+                       {
+                               self.mins_x = ReadCoord();
+                               self.mins_y = ReadCoord();
+                               self.mins_z = ReadCoord();
+                               self.maxs_x = ReadCoord();
+                               self.maxs_y = ReadCoord();
+                               self.maxs_z = ReadCoord();
+                       }
+                       else
+                       {
+                               self.mins    = '0 0 0';
+                               self.maxs_x = ReadCoord();
+                               self.maxs_y = ReadCoord();
+                               self.maxs_z = ReadCoord();
+                       }
+               }
+               else
+               {
+                       self.mins = self.maxs = '0 0 0';
+               }
+
+               self.cnt = ReadShort(); // effect number
+
+               if(f & 0x20)
+               {
+                       self.velocity = decompressShortVector(ReadShort());
+                       self.movedir = decompressShortVector(ReadShort());
+               }
+               else
+               {
+                       self.velocity = self.movedir = '0 0 0';
+               }
+               if(f & 0x40)
+               {
+                       self.waterlevel = ReadShort() / 16.0;
+                       self.count = ReadByte() / 16.0;
+               }
+               else
+               {
+                       self.waterlevel = 0;
+                       self.count = 1;
+               }
+               if(self.noise)
+                       strunzone(self.noise);
+               if(self.bgmscript)
+                       strunzone(self.bgmscript);
+               self.noise = strzone(ReadString());
+               if(self.noise != "")
+               {
+                       self.atten = ReadByte() / 64.0;
+                       self.volume = ReadByte() / 255.0;
+               }
+               self.bgmscript = strzone(ReadString());
+               if(self.bgmscript != "")
+               {
+                       self.bgmscriptattack = ReadByte() / 64.0;
+                       self.bgmscriptdecay = ReadByte() / 64.0;
+                       self.bgmscriptsustain = ReadByte() / 255.0;
+                       self.bgmscriptrelease = ReadByte() / 64.0;
+               }
+               BGMScript_InitEntity(self);
+       }
+
+       if(f & 2)
+       {
+               self.absolute = (self.impulse >= 0);
+               if(!self.absolute)
+               {
+                       v = self.maxs - self.mins;
+                       self.impulse *= -v.x * v.y * v.z / 262144; // relative: particles per 64^3 cube
+               }
+       }
+
+       if(f & 0x10)
+               self.absolute = 2;
+
+       setorigin(self, self.origin);
+       setsize(self, self.mins, self.maxs);
+       self.solid = SOLID_NOT;
+       self.draw = Draw_PointParticles;
+       self.entremove = Ent_PointParticles_Remove;
+}
+#endif
diff --git a/qcsrc/common/triggers/func/pointparticles.qh b/qcsrc/common/triggers/func/pointparticles.qh
new file mode 100644 (file)
index 0000000..d446e72
--- /dev/null
@@ -0,0 +1,5 @@
+#ifdef CSQC
+
+void Ent_PointParticles();
+
+#endif
\ No newline at end of file
diff --git a/qcsrc/common/triggers/func/rainsnow.qc b/qcsrc/common/triggers/func/rainsnow.qc
new file mode 100644 (file)
index 0000000..deb5ee5
--- /dev/null
@@ -0,0 +1,128 @@
+#ifdef SVQC
+float rainsnow_SendEntity(entity to, float sf)
+{
+       WriteByte(MSG_ENTITY, ENT_CLIENT_RAINSNOW);
+       WriteByte(MSG_ENTITY, self.state);
+       WriteCoord(MSG_ENTITY, self.origin_x + self.mins_x);
+       WriteCoord(MSG_ENTITY, self.origin_y + self.mins_y);
+       WriteCoord(MSG_ENTITY, self.origin_z + self.mins_z);
+       WriteCoord(MSG_ENTITY, self.maxs_x - self.mins_x);
+       WriteCoord(MSG_ENTITY, self.maxs_y - self.mins_y);
+       WriteCoord(MSG_ENTITY, self.maxs_z - self.mins_z);
+       WriteShort(MSG_ENTITY, compressShortVector(self.dest));
+       WriteShort(MSG_ENTITY, self.count);
+       WriteByte(MSG_ENTITY, self.cnt);
+       return 1;
+}
+
+/*QUAKED spawnfunc_func_rain (0 .5 .8) ?
+This is an invisible area like a trigger, which rain falls inside of.
+
+Keys:
+"velocity"
+ falling direction (should be something like '0 0 -700', use the X and Y velocity for wind)
+"cnt"
+ sets color of rain (default 12 - white)
+"count"
+ adjusts density, this many particles fall every second for a 1024x1024 area, default is 2000
+*/
+void spawnfunc_func_rain()
+{
+       self.dest = self.velocity;
+       self.velocity = '0 0 0';
+       if (!self.dest)
+               self.dest = '0 0 -700';
+       self.angles = '0 0 0';
+       self.movetype = MOVETYPE_NONE;
+       self.solid = SOLID_NOT;
+       SetBrushEntityModel();
+       if (!self.cnt)
+               self.cnt = 12;
+       if (!self.count)
+               self.count = 2000;
+       self.count = 0.01 * self.count * (self.size_x / 1024) * (self.size_y / 1024);
+       if (self.count < 1)
+               self.count = 1;
+       if(self.count > 65535)
+               self.count = 65535;
+
+       self.state = 1; // 1 is rain, 0 is snow
+       self.Version = 1;
+
+       Net_LinkEntity(self, false, 0, rainsnow_SendEntity);
+}
+
+
+/*QUAKED spawnfunc_func_snow (0 .5 .8) ?
+This is an invisible area like a trigger, which snow falls inside of.
+
+Keys:
+"velocity"
+ falling direction (should be something like '0 0 -300', use the X and Y velocity for wind)
+"cnt"
+ sets color of rain (default 12 - white)
+"count"
+ adjusts density, this many particles fall every second for a 1024x1024 area, default is 2000
+*/
+void spawnfunc_func_snow()
+{
+       self.dest = self.velocity;
+       self.velocity = '0 0 0';
+       if (!self.dest)
+               self.dest = '0 0 -300';
+       self.angles = '0 0 0';
+       self.movetype = MOVETYPE_NONE;
+       self.solid = SOLID_NOT;
+       SetBrushEntityModel();
+       if (!self.cnt)
+               self.cnt = 12;
+       if (!self.count)
+               self.count = 2000;
+       self.count = 0.01 * self.count * (self.size_x / 1024) * (self.size_y / 1024);
+       if (self.count < 1)
+               self.count = 1;
+       if(self.count > 65535)
+               self.count = 65535;
+
+       self.state = 0; // 1 is rain, 0 is snow
+       self.Version = 1;
+
+       Net_LinkEntity(self, false, 0, rainsnow_SendEntity);
+}
+#elif defined(CSQC)
+void Draw_Rain()
+{
+    te_particlerain(self.origin + self.mins, self.origin + self.maxs, self.velocity, floor(self.count * drawframetime + random()), self.glow_color);
+}
+
+void Draw_Snow()
+{
+    te_particlesnow(self.origin + self.mins, self.origin + self.maxs, self.velocity, floor(self.count * drawframetime + random()), self.glow_color);
+}
+
+void Ent_RainOrSnow()
+{
+       self.impulse = ReadByte(); // Rain, Snow, or Whatever
+       self.origin_x = ReadCoord();
+       self.origin_y = ReadCoord();
+       self.origin_z = ReadCoord();
+       self.maxs_x = ReadCoord();
+       self.maxs_y = ReadCoord();
+       self.maxs_z = ReadCoord();
+       self.velocity = decompressShortVector(ReadShort());
+       self.count = ReadShort() * 10;
+       self.glow_color = ReadByte(); // color
+
+       self.mins    = -0.5 * self.maxs;
+       self.maxs    =  0.5 * self.maxs;
+       self.origin  = self.origin - self.mins;
+
+       setorigin(self, self.origin);
+       setsize(self, self.mins, self.maxs);
+       self.solid = SOLID_NOT;
+       if(self.impulse)
+               self.draw = Draw_Rain;
+       else
+               self.draw = Draw_Snow;
+}
+#endif
diff --git a/qcsrc/common/triggers/func/rainsnow.qh b/qcsrc/common/triggers/func/rainsnow.qh
new file mode 100644 (file)
index 0000000..5d8d923
--- /dev/null
@@ -0,0 +1,3 @@
+#ifdef CSQC
+void Ent_RainOrSnow();
+#endif
diff --git a/qcsrc/common/triggers/func/rotating.qc b/qcsrc/common/triggers/func/rotating.qc
new file mode 100644 (file)
index 0000000..93d4a30
--- /dev/null
@@ -0,0 +1,77 @@
+#ifdef SVQC
+void func_rotating_setactive(float astate)
+{
+
+       if (astate == ACTIVE_TOGGLE)
+       {
+               if(self.active == ACTIVE_ACTIVE)
+                       self.active = ACTIVE_NOT;
+               else
+                       self.active = ACTIVE_ACTIVE;
+       }
+       else
+               self.active = astate;
+
+       if(self.active  == ACTIVE_NOT)
+               self.avelocity = '0 0 0';
+       else
+               self.avelocity = self.pos1;
+}
+
+/*QUAKED spawnfunc_func_rotating (0 .5 .8) ? - - X_AXIS Y_AXIS
+Brush model that spins in place on one axis (default Z).
+speed   : speed to rotate (in degrees per second)
+noise   : path/name of looping .wav file to play.
+dmg     : Do this mutch dmg every .dmgtime intervall when blocked
+dmgtime : See above.
+*/
+
+void spawnfunc_func_rotating()
+{
+       if (self.noise != "")
+       {
+               precache_sound(self.noise);
+               ambientsound(self.origin, self.noise, VOL_BASE, ATTEN_IDLE);
+       }
+
+       self.active = ACTIVE_ACTIVE;
+       self.setactive = func_rotating_setactive;
+
+       if (!self.speed)
+               self.speed = 100;
+       // FIXME: test if this turns the right way, then remove this comment (negate as needed)
+       if (self.spawnflags & 4) // X (untested)
+               self.avelocity = '0 0 1' * self.speed;
+       // FIXME: test if this turns the right way, then remove this comment (negate as needed)
+       else if (self.spawnflags & 8) // Y (untested)
+               self.avelocity = '1 0 0' * self.speed;
+       // FIXME: test if this turns the right way, then remove this comment (negate as needed)
+       else // Z
+               self.avelocity = '0 1 0' * self.speed;
+
+       self.pos1 = self.avelocity;
+
+    if(self.dmg && (self.message == ""))
+        self.message = " was squished";
+    if(self.dmg && (self.message2 == ""))
+               self.message2 = "was squished by";
+
+
+    if(self.dmg && (!self.dmgtime))
+        self.dmgtime = 0.25;
+
+    self.dmgtime2 = time;
+
+       if (!InitMovingBrushTrigger())
+               return;
+       // no EF_LOWPRECISION here, as rounding angles is bad
+
+    self.blocked = generic_plat_blocked;
+
+       // wait for targets to spawn
+       self.SUB_NEXTTHINK = self.SUB_LTIME + 999999999;
+       self.SUB_THINK = SUB_NullThink; // for PushMove
+
+       // TODO make a reset function for this one
+}
+#endif
diff --git a/qcsrc/common/triggers/func/stardust.qc b/qcsrc/common/triggers/func/stardust.qc
new file mode 100644 (file)
index 0000000..30c406f
--- /dev/null
@@ -0,0 +1,8 @@
+#ifdef SVQC
+void spawnfunc_func_stardust()
+{
+       self.effects = EF_STARDUST;
+
+       CSQCMODEL_AUTOINIT();
+}
+#endif
diff --git a/qcsrc/common/triggers/func/train.qc b/qcsrc/common/triggers/func/train.qc
new file mode 100644 (file)
index 0000000..001a759
--- /dev/null
@@ -0,0 +1,316 @@
+.float train_wait_turning;
+void() train_next;
+void train_wait()
+{
+       entity oldself;
+       oldself = self;
+       self = self.enemy;
+       SUB_UseTargets();
+       self = oldself;
+       self.enemy = world;
+
+       // if turning is enabled, the train will turn toward the next point while waiting
+       if(self.platmovetype_turn && !self.train_wait_turning)
+       {
+               entity targ, cp;
+               vector ang;
+               targ = find(world, targetname, self.target);
+               if((self.spawnflags & 1) && targ.curvetarget)
+                       cp = find(world, targetname, targ.curvetarget);
+               else
+                       cp = world;
+
+               if(cp) // bezier curves movement
+                       ang = cp.origin - (self.origin - self.view_ofs); // use the origin of the control point of the next path_corner
+               else // linear movement
+                       ang = targ.origin - (self.origin - self.view_ofs); // use the origin of the next path_corner
+               ang = vectoangles(ang);
+               ang_x = -ang_x; // flip up / down orientation
+
+               if(self.wait > 0) // slow turning
+                       SUB_CalcAngleMove(ang, TSPEED_TIME, self.SUB_LTIME - time + self.wait, train_wait);
+               else // instant turning
+                       SUB_CalcAngleMove(ang, TSPEED_TIME, 0.0000001, train_wait);
+               self.train_wait_turning = true;
+               return;
+       }
+
+#ifdef SVQC
+       if(self.noise != "")
+               stopsoundto(MSG_BROADCAST, self, CH_TRIGGER_SINGLE); // send this as unreliable only, as the train will resume operation shortly anyway
+#endif
+
+       if(self.wait < 0 || self.train_wait_turning) // no waiting or we already waited while turning
+       {
+               self.train_wait_turning = false;
+               train_next();
+       }
+       else
+       {
+               self.SUB_THINK = train_next;
+               self.SUB_NEXTTHINK = self.SUB_LTIME + self.wait;
+       }
+}
+
+void train_next()
+{
+       entity targ, cp = world;
+       vector cp_org = '0 0 0';
+
+       targ = find(world, targetname, self.target);
+       self.target = targ.target;
+       if (self.spawnflags & 1)
+       {
+               if(targ.curvetarget)
+               {
+                       cp = find(world, targetname, targ.curvetarget); // get its second target (the control point)
+                       cp_org = cp.origin - self.view_ofs; // no control point found, assume a straight line to the destination
+               }
+       }
+       if (self.target == "")
+               objerror("train_next: no next target");
+       self.wait = targ.wait;
+       if (!self.wait)
+               self.wait = 0.1;
+
+       if(targ.platmovetype)
+       {
+               // this path_corner contains a movetype overrider, apply it
+               self.platmovetype_start = targ.platmovetype_start;
+               self.platmovetype_end = targ.platmovetype_end;
+       }
+       else
+       {
+               // this path_corner doesn't contain a movetype overrider, use the train's defaults
+               self.platmovetype_start = self.platmovetype_start_default;
+               self.platmovetype_end = self.platmovetype_end_default;
+       }
+
+       if (targ.speed)
+       {
+               if (cp)
+                       SUB_CalcMove_Bezier(cp_org, targ.origin - self.view_ofs, TSPEED_LINEAR, targ.speed, train_wait);
+               else
+                       SUB_CalcMove(targ.origin - self.view_ofs, TSPEED_LINEAR, targ.speed, train_wait);
+       }
+       else
+       {
+               if (cp)
+                       SUB_CalcMove_Bezier(cp_org, targ.origin - self.view_ofs, TSPEED_LINEAR, self.speed, train_wait);
+               else
+                       SUB_CalcMove(targ.origin - self.view_ofs, TSPEED_LINEAR, self.speed, train_wait);
+       }
+
+       if(self.noise != "")
+               sound(self, CH_TRIGGER_SINGLE, self.noise, VOL_BASE, ATTEN_IDLE);
+}
+
+#ifdef SVQC
+float train_send(entity to, float sf)
+{
+       WriteByte(MSG_ENTITY, ENT_CLIENT_TRAIN);
+       WriteByte(MSG_ENTITY, sf);
+
+       if(sf & SF_TRIGGER_INIT)
+       {
+               WriteString(MSG_ENTITY, self.platmovetype);
+               WriteByte(MSG_ENTITY, self.platmovetype_turn);
+               WriteByte(MSG_ENTITY, self.spawnflags);
+
+               WriteString(MSG_ENTITY, self.model);
+
+               trigger_common_write(true);
+
+               WriteString(MSG_ENTITY, self.curvetarget);
+
+               WriteCoord(MSG_ENTITY, self.pos1_x);
+               WriteCoord(MSG_ENTITY, self.pos1_y);
+               WriteCoord(MSG_ENTITY, self.pos1_z);
+               WriteCoord(MSG_ENTITY, self.pos2_x);
+               WriteCoord(MSG_ENTITY, self.pos2_y);
+               WriteCoord(MSG_ENTITY, self.pos2_z);
+
+               WriteCoord(MSG_ENTITY, self.size_x);
+               WriteCoord(MSG_ENTITY, self.size_y);
+               WriteCoord(MSG_ENTITY, self.size_z);
+
+               WriteCoord(MSG_ENTITY, self.view_ofs_x);
+               WriteCoord(MSG_ENTITY, self.view_ofs_y);
+               WriteCoord(MSG_ENTITY, self.view_ofs_z);
+
+               WriteAngle(MSG_ENTITY, self.mangle_x);
+               WriteAngle(MSG_ENTITY, self.mangle_y);
+               WriteAngle(MSG_ENTITY, self.mangle_z);
+
+               WriteShort(MSG_ENTITY, self.speed);
+               WriteShort(MSG_ENTITY, self.height);
+               WriteByte(MSG_ENTITY, self.lip);
+               WriteByte(MSG_ENTITY, self.state);
+               WriteByte(MSG_ENTITY, self.wait);
+
+               WriteShort(MSG_ENTITY, self.dmg);
+               WriteByte(MSG_ENTITY, self.dmgtime);
+       }
+
+       if(sf & SF_TRIGGER_RESET)
+       {
+               // used on client
+       }
+
+       return true;
+}
+
+void train_link()
+{
+       //Net_LinkEntity(self, 0, false, train_send);
+}
+
+void func_train_find()
+{
+       entity targ;
+       targ = find(world, targetname, self.target);
+       self.target = targ.target;
+       if (self.target == "")
+               objerror("func_train_find: no next target");
+       SUB_SETORIGIN(self, targ.origin - self.view_ofs);
+       self.SUB_NEXTTHINK = self.SUB_LTIME + 1;
+       self.SUB_THINK = train_next;
+
+       train_link();
+}
+
+#endif
+
+/*QUAKED spawnfunc_func_train (0 .5 .8) ?
+Ridable platform, targets spawnfunc_path_corner path to follow.
+speed : speed the train moves (can be overridden by each spawnfunc_path_corner)
+target : targetname of first spawnfunc_path_corner (starts here)
+*/
+#ifdef SVQC
+void spawnfunc_func_train()
+{
+       if (self.noise != "")
+               precache_sound(self.noise);
+
+       if (self.target == "")
+               objerror("func_train without a target");
+       if (!self.speed)
+               self.speed = 100;
+
+       if (!InitMovingBrushTrigger())
+               return;
+       self.effects |= EF_LOWPRECISION;
+       
+       if (self.spawnflags & 2)
+       {
+               self.platmovetype_turn = true;
+               self.view_ofs = '0 0 0'; // don't offset a rotating train, origin works differently now
+       }
+       else
+               self.view_ofs = self.mins;
+
+       // wait for targets to spawn
+       InitializeEntity(self, func_train_find, INITPRIO_FINDTARGET);
+
+       self.blocked = generic_plat_blocked;
+       if(self.dmg && (self.message == ""))
+               self.message = " was squished";
+    if(self.dmg && (self.message2 == ""))
+               self.message2 = "was squished by";
+       if(self.dmg && (!self.dmgtime))
+               self.dmgtime = 0.25;
+       self.dmgtime2 = time;
+
+       if(!set_platmovetype(self, self.platmovetype))
+               return;
+       self.platmovetype_start_default = self.platmovetype_start;
+       self.platmovetype_end_default = self.platmovetype_end;
+
+       // TODO make a reset function for this one
+}
+#elif defined(CSQC)
+void train_draw()
+{
+       //Movetype_Physics_NoMatchServer();
+       Movetype_Physics_MatchServer(autocvar_cl_projectiles_sloppy);
+}
+
+void ent_train()
+{
+       float sf = ReadByte();
+
+       if(sf & SF_TRIGGER_INIT)
+       {
+               self.platmovetype = strzone(ReadString());
+               self.platmovetype_turn = ReadByte();
+               self.spawnflags = ReadByte();
+
+               self.model = strzone(ReadString());
+               setmodel(self, self.model);
+
+               trigger_common_read(true);
+
+               self.curvetarget = strzone(ReadString());
+
+               self.pos1_x = ReadCoord();
+               self.pos1_y = ReadCoord();
+               self.pos1_z = ReadCoord();
+               self.pos2_x = ReadCoord();
+               self.pos2_y = ReadCoord();
+               self.pos2_z = ReadCoord();
+
+               self.size_x = ReadCoord();
+               self.size_y = ReadCoord();
+               self.size_z = ReadCoord();
+
+               self.view_ofs_x = ReadCoord();
+               self.view_ofs_y = ReadCoord();
+               self.view_ofs_z = ReadCoord();
+
+               self.mangle_x = ReadAngle();
+               self.mangle_y = ReadAngle();
+               self.mangle_z = ReadAngle();
+
+               self.speed = ReadShort();
+               self.height = ReadShort();
+               self.lip = ReadByte();
+               self.state = ReadByte();
+               self.wait = ReadByte();
+
+               self.dmg = ReadShort();
+               self.dmgtime = ReadByte();
+
+               self.classname = "func_train";
+               self.solid = SOLID_BSP;
+               self.movetype = MOVETYPE_PUSH;
+               self.drawmask = MASK_NORMAL;
+               self.draw = train_draw;
+               self.entremove = trigger_remove_generic;
+
+               if(set_platmovetype(self, self.platmovetype))
+               {
+                       self.platmovetype_start_default = self.platmovetype_start;
+                       self.platmovetype_end_default = self.platmovetype_end;
+               }
+
+               // everything is set up by the time the train is linked, we shouldn't need this
+               //func_train_find();
+
+               // but we will need these
+               //self.move_nextthink = self.move_ltime + 0.1;
+               //self.move_think = train_next;
+               train_next();
+
+               self.move_movetype = MOVETYPE_PUSH;
+               self.move_origin = self.origin;
+               self.move_angles = self.angles;
+               self.move_time = time;
+       }
+
+       if(sf & SF_TRIGGER_RESET)
+       {
+               // TODO: make a reset function for trains
+       }
+}
+
+#endif
diff --git a/qcsrc/common/triggers/func/train.qh b/qcsrc/common/triggers/func/train.qh
new file mode 100644 (file)
index 0000000..f69515f
--- /dev/null
@@ -0,0 +1,4 @@
+#ifdef CSQC
+.float dmgtime;
+void ent_train();
+#endif
diff --git a/qcsrc/common/triggers/func/vectormamamam.qc b/qcsrc/common/triggers/func/vectormamamam.qc
new file mode 100644 (file)
index 0000000..521ac87
--- /dev/null
@@ -0,0 +1,159 @@
+#ifdef SVQC
+// reusing some fields havocbots declared
+.entity wp00, wp01, wp02, wp03;
+
+.float targetfactor, target2factor, target3factor, target4factor;
+.vector targetnormal, target2normal, target3normal, target4normal;
+
+vector func_vectormamamam_origin(entity o, float t)
+{
+       vector v, p;
+       float f;
+       entity e;
+
+       f = o.spawnflags;
+       v = '0 0 0';
+
+       e = o.wp00;
+       if(e)
+       {
+               p = e.origin + t * e.velocity;
+               if(f & 1)
+                       v = v + (p * o.targetnormal) * o.targetnormal * o.targetfactor;
+               else
+                       v = v + (p - (p * o.targetnormal) * o.targetnormal) * o.targetfactor;
+       }
+
+       e = o.wp01;
+       if(e)
+       {
+               p = e.origin + t * e.velocity;
+               if(f & 2)
+                       v = v + (p * o.target2normal) * o.target2normal * o.target2factor;
+               else
+                       v = v + (p - (p * o.target2normal) * o.target2normal) * o.target2factor;
+       }
+
+       e = o.wp02;
+       if(e)
+       {
+               p = e.origin + t * e.velocity;
+               if(f & 4)
+                       v = v + (p * o.target3normal) * o.target3normal * o.target3factor;
+               else
+                       v = v + (p - (p * o.target3normal) * o.target3normal) * o.target3factor;
+       }
+
+       e = o.wp03;
+       if(e)
+       {
+               p = e.origin + t * e.velocity;
+               if(f & 8)
+                       v = v + (p * o.target4normal) * o.target4normal * o.target4factor;
+               else
+                       v = v + (p - (p * o.target4normal) * o.target4normal) * o.target4factor;
+       }
+
+       return v;
+}
+
+void func_vectormamamam_controller_think()
+{
+       self.nextthink = time + 0.1;
+
+       if(self.owner.active != ACTIVE_ACTIVE)
+       {
+               self.owner.velocity = '0 0 0';
+               return;
+       }
+
+       if(self.owner.classname == "func_vectormamamam") // don't brake stuff if the func_vectormamamam was killtarget'ed
+               self.owner.velocity = (self.owner.destvec + func_vectormamamam_origin(self.owner, 0.1) - self.owner.origin) * 10;
+}
+
+void func_vectormamamam_findtarget()
+{
+       if(self.target != "")
+               self.wp00 = find(world, targetname, self.target);
+
+       if(self.target2 != "")
+               self.wp01 = find(world, targetname, self.target2);
+
+       if(self.target3 != "")
+               self.wp02 = find(world, targetname, self.target3);
+
+       if(self.target4 != "")
+               self.wp03 = find(world, targetname, self.target4);
+
+       if(!self.wp00 && !self.wp01 && !self.wp02 && !self.wp03)
+               objerror("No reference entity found, so there is nothing to move. Aborting.");
+
+       self.destvec = self.origin - func_vectormamamam_origin(self, 0);
+
+       entity controller;
+       controller = spawn();
+       controller.classname = "func_vectormamamam_controller";
+       controller.owner = self;
+       controller.nextthink = time + 1;
+       controller.think = func_vectormamamam_controller_think;
+}
+
+void spawnfunc_func_vectormamamam()
+{
+       if (self.noise != "")
+       {
+               precache_sound(self.noise);
+               soundto(MSG_INIT, self, CH_TRIGGER_SINGLE, self.noise, VOL_BASE, ATTEN_IDLE);
+       }
+
+       if(!self.targetfactor)
+               self.targetfactor = 1;
+
+       if(!self.target2factor)
+               self.target2factor = 1;
+
+       if(!self.target3factor)
+               self.target3factor = 1;
+
+       if(!self.target4factor)
+               self.target4factor = 1;
+
+       if(vlen(self.targetnormal))
+               self.targetnormal = normalize(self.targetnormal);
+
+       if(vlen(self.target2normal))
+               self.target2normal = normalize(self.target2normal);
+
+       if(vlen(self.target3normal))
+               self.target3normal = normalize(self.target3normal);
+
+       if(vlen(self.target4normal))
+               self.target4normal = normalize(self.target4normal);
+
+       self.blocked = generic_plat_blocked;
+       if(self.dmg && (self.message == ""))
+               self.message = " was squished";
+    if(self.dmg && (self.message == ""))
+               self.message2 = "was squished by";
+       if(self.dmg && (!self.dmgtime))
+               self.dmgtime = 0.25;
+       self.dmgtime2 = time;
+
+       if(self.netname == "")
+               self.netname = "1 0 0 0 1";
+
+       if (!InitMovingBrushTrigger())
+               return;
+
+       // wait for targets to spawn
+       self.SUB_NEXTTHINK = self.SUB_LTIME + 999999999;
+       self.SUB_THINK = SUB_NullThink; // for PushMove
+
+       // Savage: Reduce bandwith, critical on e.g. nexdm02
+       self.effects |= EF_LOWPRECISION;
+
+       self.active = ACTIVE_ACTIVE;
+
+       InitializeEntity(self, func_vectormamamam_findtarget, INITPRIO_FINDTARGET);
+}
+#endif
diff --git a/qcsrc/common/triggers/include.qc b/qcsrc/common/triggers/include.qc
new file mode 100644 (file)
index 0000000..59da1f2
--- /dev/null
@@ -0,0 +1,20 @@
+#include "include.qh"
+
+// some required common stuff
+#include "subs.qc"
+#include "triggers.qc"
+#include "platforms.qc"
+#include "teleporters.qc"
+
+// func
+#include "func/include.qc"
+
+// misc
+#include "misc/include.qc"
+
+// target
+#include "target/include.qc"
+
+// trigger
+#include "trigger/include.qc"
+
diff --git a/qcsrc/common/triggers/include.qh b/qcsrc/common/triggers/include.qh
new file mode 100644 (file)
index 0000000..138d267
--- /dev/null
@@ -0,0 +1,25 @@
+#ifndef TRIGGERS_INCLUDE_H
+#define TRIGGERS_INCLUDE_H
+
+// some required common stuff
+#ifdef CSQC
+       #include "../../server/item_key.qh"
+#endif
+#include "triggers.qh"
+#include "subs.qh"
+#include "platforms.qh"
+#include "teleporters.qh"
+
+// func
+#include "func/include.qh"
+
+// misc
+#include "misc/include.qh"
+
+// target
+#include "target/include.qh"
+
+// trigger
+#include "trigger/include.qh"
+
+#endif
diff --git a/qcsrc/common/triggers/misc/corner.qc b/qcsrc/common/triggers/misc/corner.qc
new file mode 100644 (file)
index 0000000..4294eac
--- /dev/null
@@ -0,0 +1,85 @@
+#ifdef SVQC
+bool corner_send(entity to, int sf)
+{
+       WriteByte(MSG_ENTITY, ENT_CLIENT_CORNER);
+
+       WriteString(MSG_ENTITY, self.platmovetype);
+
+       WriteCoord(MSG_ENTITY, self.origin_x);
+       WriteCoord(MSG_ENTITY, self.origin_y);
+       WriteCoord(MSG_ENTITY, self.origin_z);
+
+       WriteString(MSG_ENTITY, self.target);
+       WriteString(MSG_ENTITY, self.target2);
+       WriteString(MSG_ENTITY, self.target3);
+       WriteString(MSG_ENTITY, self.target4);
+       WriteString(MSG_ENTITY, self.targetname);
+       WriteByte(MSG_ENTITY, self.target_random);
+
+       WriteByte(MSG_ENTITY, self.wait);
+
+       return true;
+}
+
+void corner_link()
+{
+       //Net_LinkEntity(self, false, 0, corner_send);
+}
+
+void spawnfunc_path_corner()
+{
+       // setup values for overriding train movement
+       // if a second value does not exist, both start and end speeds are the single value specified
+       set_platmovetype(self, self.platmovetype);
+
+       corner_link();
+}
+#elif defined(CSQC)
+
+void corner_remove()
+{
+       if(self.target) { strunzone(self.target); }
+       self.target = string_null;
+
+       if(self.target2) { strunzone(self.target2); }
+       self.target2 = string_null;
+
+       if(self.target3) { strunzone(self.target3); }
+       self.target3 = string_null;
+
+       if(self.target4) { strunzone(self.target4); }
+       self.target4 = string_null;
+
+       if(self.targetname) { strunzone(self.targetname); }
+       self.targetname = string_null;
+
+       if(self.platmovetype) { strunzone(self.platmovetype); }
+       self.platmovetype = string_null;
+}
+
+void ent_corner()
+{
+       self.platmovetype = strzone(ReadString());
+
+       self.origin_x = ReadCoord();
+       self.origin_y = ReadCoord();
+       self.origin_z = ReadCoord();
+       setorigin(self, self.origin);
+
+       self.target = strzone(ReadString());
+       self.target2 = strzone(ReadString());
+       self.target3 = strzone(ReadString());
+       self.target4 = strzone(ReadString());
+       self.targetname = strzone(ReadString());
+       self.target_random = ReadByte();
+
+       self.wait = ReadByte();
+
+       self.classname = "path_corner";
+       self.drawmask = MASK_NORMAL;
+       self.entremove = corner_remove;
+
+       set_platmovetype(self, self.platmovetype);
+}
+
+#endif
diff --git a/qcsrc/common/triggers/misc/corner.qh b/qcsrc/common/triggers/misc/corner.qh
new file mode 100644 (file)
index 0000000..62b3aae
--- /dev/null
@@ -0,0 +1,3 @@
+#ifdef CSQC
+void ent_corner();
+#endif
diff --git a/qcsrc/common/triggers/misc/follow.qc b/qcsrc/common/triggers/misc/follow.qc
new file mode 100644 (file)
index 0000000..bfeb865
--- /dev/null
@@ -0,0 +1,69 @@
+// the way this entity works makes it no use to CSQC, as it removes itself instantly
+
+#ifdef SVQC
+void follow_init()
+{
+       entity src, dst;
+       src = world;
+       dst = world;
+       if(self.killtarget != "")
+               src = find(world, targetname, self.killtarget);
+       if(self.target != "")
+               dst = find(world, targetname, self.target);
+
+       if(!src && !dst)
+       {
+               objerror("follow: could not find target/killtarget");
+               return;
+       }
+
+       if(self.jointtype)
+       {
+               // already done :P entity must stay
+               self.aiment = src;
+               self.enemy = dst;
+       }
+       else if(!src || !dst)
+       {
+               objerror("follow: could not find target/killtarget");
+               return;
+       }
+       else if(self.spawnflags & 1)
+       {
+               // attach
+               if(self.spawnflags & 2)
+               {
+                       setattachment(dst, src, self.message);
+               }
+               else
+               {
+                       attach_sameorigin(dst, src, self.message);
+               }
+
+               dst.solid = SOLID_NOT; // solid doesn't work with attachment
+               remove(self);
+       }
+       else
+       {
+               if(self.spawnflags & 2)
+               {
+                       dst.movetype = MOVETYPE_FOLLOW;
+                       dst.aiment = src;
+                       // dst.punchangle = '0 0 0'; // keep unchanged
+                       dst.view_ofs = dst.origin;
+                       dst.v_angle = dst.angles;
+               }
+               else
+               {
+                       follow_sameorigin(dst, src);
+               }
+
+               remove(self);
+       }
+}
+
+void spawnfunc_misc_follow()
+{
+       InitializeEntity(self, follow_init, INITPRIO_FINDTARGET);
+}
+#endif
diff --git a/qcsrc/common/triggers/misc/include.qc b/qcsrc/common/triggers/misc/include.qc
new file mode 100644 (file)
index 0000000..c05f8c3
--- /dev/null
@@ -0,0 +1,6 @@
+#include "include.qh"
+
+#include "corner.qc"
+#include "follow.qc"
+#include "laser.qc"
+#include "teleport_dest.qc"
diff --git a/qcsrc/common/triggers/misc/include.qh b/qcsrc/common/triggers/misc/include.qh
new file mode 100644 (file)
index 0000000..71b2205
--- /dev/null
@@ -0,0 +1,7 @@
+#ifndef TRIGGERS_MISC_INCLUDE_H
+#define TRIGGERS_MISC_INCLUDE_H
+
+#include "corner.qh"
+#include "laser.qh"
+
+#endif
diff --git a/qcsrc/common/triggers/misc/laser.qc b/qcsrc/common/triggers/misc/laser.qc
new file mode 100644 (file)
index 0000000..2d9f095
--- /dev/null
@@ -0,0 +1,393 @@
+#if defined(CSQC)
+       #include "../../../client/_all.qh"
+       #include "../../buffs.qh"
+       #include "../../../csqcmodellib/interpolate.qh"
+       #include "../../../client/main.qh"
+       #include "../../../csqcmodellib/cl_model.qh"
+#elif defined(MENUQC)
+#elif defined(SVQC)
+#endif
+
+#ifdef SVQC
+.float modelscale;
+void misc_laser_aim()
+{
+       vector a;
+       if(self.enemy)
+       {
+               if(self.spawnflags & 2)
+               {
+                       if(self.enemy.origin != self.mangle)
+                       {
+                               self.mangle = self.enemy.origin;
+                               self.SendFlags |= 2;
+                       }
+               }
+               else
+               {
+                       a = vectoangles(self.enemy.origin - self.origin);
+                       a_x = -a_x;
+                       if(a != self.mangle)
+                       {
+                               self.mangle = a;
+                               self.SendFlags |= 2;
+                       }
+               }
+       }
+       else
+       {
+               if(self.angles != self.mangle)
+               {
+                       self.mangle = self.angles;
+                       self.SendFlags |= 2;
+               }
+       }
+       if(self.origin != self.oldorigin)
+       {
+               self.SendFlags |= 1;
+               self.oldorigin = self.origin;
+       }
+}
+
+void misc_laser_init()
+{
+       if(self.target != "")
+               self.enemy = find(world, targetname, self.target);
+}
+
+.entity pusher;
+void misc_laser_think()
+{
+       vector o;
+       entity oldself;
+       entity hitent;
+       vector hitloc;
+
+       self.nextthink = time;
+
+       if(!self.state)
+               return;
+
+       misc_laser_aim();
+
+       if(self.enemy)
+       {
+               o = self.enemy.origin;
+               if (!(self.spawnflags & 2))
+                       o = self.origin + normalize(o - self.origin) * 32768;
+       }
+       else
+       {
+               makevectors(self.mangle);
+               o = self.origin + v_forward * 32768;
+       }
+
+       if(self.dmg || self.enemy.target != "")
+       {
+               traceline(self.origin, o, MOVE_NORMAL, self);
+       }
+       hitent = trace_ent;
+       hitloc = trace_endpos;
+
+       if(self.enemy.target != "") // DETECTOR laser
+       {
+               if(trace_ent.iscreature)
+               {
+                       self.pusher = hitent;
+                       if(!self.count)
+                       {
+                               self.count = 1;
+
+                               oldself = self;
+                               self = self.enemy;
+                               activator = self.pusher;
+                               SUB_UseTargets();
+                               self = oldself;
+                       }
+               }
+               else
+               {
+                       if(self.count)
+                       {
+                               self.count = 0;
+
+                               oldself = self;
+                               self = self.enemy;
+                               activator = self.pusher;
+                               SUB_UseTargets();
+                               self = oldself;
+                       }
+               }
+       }
+
+       if(self.dmg)
+       {
+               if(self.team)
+                       if(((self.spawnflags & 8) == 0) == (self.team != hitent.team))
+                               return;
+               if(hitent.takedamage)
+                       Damage(hitent, self, self, ((self.dmg < 0) ? 100000 : (self.dmg * frametime)), DEATH_HURTTRIGGER, hitloc, '0 0 0');
+       }
+}
+
+float laser_SendEntity(entity to, float fl)
+{
+       WriteByte(MSG_ENTITY, ENT_CLIENT_LASER);
+       fl = fl - (fl & 0xF0); // use that bit to indicate finite length laser
+       if(self.spawnflags & 2)
+               fl |= 0x80;
+       if(self.alpha)
+               fl |= 0x40;
+       if(self.scale != 1 || self.modelscale != 1)
+               fl |= 0x20;
+       if(self.spawnflags & 4)
+               fl |= 0x10;
+       WriteByte(MSG_ENTITY, fl);
+       if(fl & 1)
+       {
+               WriteCoord(MSG_ENTITY, self.origin_x);
+               WriteCoord(MSG_ENTITY, self.origin_y);
+               WriteCoord(MSG_ENTITY, self.origin_z);
+       }
+       if(fl & 8)
+       {
+               WriteByte(MSG_ENTITY, self.colormod_x * 255.0);
+               WriteByte(MSG_ENTITY, self.colormod_y * 255.0);
+               WriteByte(MSG_ENTITY, self.colormod_z * 255.0);
+               if(fl & 0x40)
+                       WriteByte(MSG_ENTITY, self.alpha * 255.0);
+               if(fl & 0x20)
+               {
+                       WriteByte(MSG_ENTITY, bound(0, self.scale * 16.0, 255));
+                       WriteByte(MSG_ENTITY, bound(0, self.modelscale * 16.0, 255));
+               }
+               if((fl & 0x80) || !(fl & 0x10)) // effect doesn't need sending if the laser is infinite and has collision testing turned off
+                       WriteShort(MSG_ENTITY, self.cnt + 1);
+       }
+       if(fl & 2)
+       {
+               if(fl & 0x80)
+               {
+                       WriteCoord(MSG_ENTITY, self.enemy.origin_x);
+                       WriteCoord(MSG_ENTITY, self.enemy.origin_y);
+                       WriteCoord(MSG_ENTITY, self.enemy.origin_z);
+               }
+               else
+               {
+                       WriteAngle(MSG_ENTITY, self.mangle_x);
+                       WriteAngle(MSG_ENTITY, self.mangle_y);
+               }
+       }
+       if(fl & 4)
+               WriteByte(MSG_ENTITY, self.state);
+       return 1;
+}
+
+/*QUAKED spawnfunc_misc_laser (.5 .5 .5) ? START_ON DEST_IS_FIXED
+Any object touching the beam will be hurt
+Keys:
+"target"
+ spawnfunc_target_position where the laser ends
+"mdl"
+ name of beam end effect to use
+"colormod"
+ color of the beam (default: red)
+"dmg"
+ damage per second (-1 for a laser that kills immediately)
+*/
+void laser_use()
+{
+       self.state = !self.state;
+       self.SendFlags |= 4;
+       misc_laser_aim();
+}
+
+void laser_reset()
+{
+       if(self.spawnflags & 1)
+               self.state = 1;
+       else
+               self.state = 0;
+}
+
+void spawnfunc_misc_laser()
+{
+       if(self.mdl)
+       {
+               if(self.mdl == "none")
+                       self.cnt = -1;
+               else
+               {
+                       self.cnt = particleeffectnum(self.mdl);
+                       if(self.cnt < 0)
+                               if(self.dmg)
+                                       self.cnt = particleeffectnum("laser_deadly");
+               }
+       }
+       else if(!self.cnt)
+       {
+               if(self.dmg)
+                       self.cnt = particleeffectnum("laser_deadly");
+               else
+                       self.cnt = -1;
+       }
+       if(self.cnt < 0)
+               self.cnt = -1;
+
+       if(self.colormod == '0 0 0')
+               if(!self.alpha)
+                       self.colormod = '1 0 0';
+       if(self.message == "")
+               self.message = "saw the light";
+       if (self.message2 == "")
+               self.message2 = "was pushed into a laser by";
+       if(!self.scale)
+               self.scale = 1;
+       if(!self.modelscale)
+               self.modelscale = 1;
+       else if(self.modelscale < 0)
+               self.modelscale = 0;
+       self.think = misc_laser_think;
+       self.nextthink = time;
+       InitializeEntity(self, misc_laser_init, INITPRIO_FINDTARGET);
+
+       self.mangle = self.angles;
+
+       Net_LinkEntity(self, false, 0, laser_SendEntity);
+
+       IFTARGETED
+       {
+               self.reset = laser_reset;
+               laser_reset();
+               self.use = laser_use;
+       }
+       else
+               self.state = 1;
+}
+#elif defined(CSQC)
+
+// a laser goes from origin in direction angles
+// it has color 'colormod'
+// and stops when something is in the way
+entityclass(Laser);
+class(Laser) .int cnt; // end effect
+class(Laser) .vector colormod;
+class(Laser) .int state; // on-off
+class(Laser) .int count; // flags for the laser
+class(Laser) .vector velocity;
+class(Laser) .float alpha;
+class(Laser) .float scale; // scaling factor of the thickness
+class(Laser) .float modelscale; // scaling factor of the dlight
+
+void Draw_Laser()
+{
+       if(!self.state)
+               return;
+       InterpolateOrigin_Do();
+       if(self.count & 0x80)
+       {
+               if(self.count & 0x10)
+               {
+                       trace_endpos = self.velocity;
+                       trace_dphitq3surfaceflags = 0;
+               }
+               else
+                       traceline(self.origin, self.velocity, 0, self);
+       }
+       else
+       {
+               if(self.count & 0x10)
+               {
+                       makevectors(self.angles);
+                       trace_endpos = self.origin + v_forward * 1048576;
+                       trace_dphitq3surfaceflags = Q3SURFACEFLAG_SKY;
+               }
+               else
+               {
+                       makevectors(self.angles);
+                       traceline(self.origin, self.origin + v_forward * 32768, 0, self);
+                       if(trace_dphitq3surfaceflags & Q3SURFACEFLAG_SKY)
+                               trace_endpos = self.origin + v_forward * 1048576;
+               }
+       }
+       if(self.scale != 0)
+       {
+               if(self.alpha)
+               {
+                       Draw_CylindricLine(self.origin, trace_endpos, self.scale, "particles/laserbeam", 0, time * 3, self.colormod, self.alpha, DRAWFLAG_NORMAL, view_origin);
+               }
+               else
+               {
+                       Draw_CylindricLine(self.origin, trace_endpos, self.scale, "particles/laserbeam", 0, time * 3, self.colormod, 0.5, DRAWFLAG_ADDITIVE, view_origin);
+               }
+       }
+       if (!(trace_dphitq3surfaceflags & (Q3SURFACEFLAG_SKY | Q3SURFACEFLAG_NOIMPACT)))
+       {
+               if(self.cnt >= 0)
+                       pointparticles(self.cnt, trace_endpos, trace_plane_normal, drawframetime * 1000);
+               if(self.colormod != '0 0 0' && self.modelscale != 0)
+                       adddynamiclight(trace_endpos + trace_plane_normal * 1, self.modelscale, self.colormod * 5);
+       }
+}
+
+void Ent_Laser()
+{
+       InterpolateOrigin_Undo();
+
+       // 30 bytes, or 13 bytes for just moving
+       int f = ReadByte();
+       self.count = (f & 0xF0);
+
+       if(self.count & 0x80)
+               self.iflags = IFLAG_VELOCITY | IFLAG_ORIGIN;
+       else
+               self.iflags = IFLAG_ANGLES | IFLAG_ORIGIN;
+
+       if(f & 1)
+       {
+               self.origin_x = ReadCoord();
+               self.origin_y = ReadCoord();
+               self.origin_z = ReadCoord();
+               setorigin(self, self.origin);
+       }
+       if(f & 8)
+       {
+               self.colormod_x = ReadByte() / 255.0;
+               self.colormod_y = ReadByte() / 255.0;
+               self.colormod_z = ReadByte() / 255.0;
+               if(f & 0x40)
+                       self.alpha = ReadByte() / 255.0;
+               else
+                       self.alpha = 0;
+               self.scale = 2;
+               self.modelscale = 50;
+               if(f & 0x20)
+               {
+                       self.scale *= ReadByte() / 16.0; // beam radius
+                       self.modelscale *= ReadByte() / 16.0; // dlight radius
+               }
+               if((f & 0x80) || !(f & 0x10))
+                       self.cnt = ReadShort() - 1; // effect number
+               else
+                       self.cnt = 0;
+       }
+       if(f & 2)
+       {
+               if(f & 0x80)
+               {
+                       self.velocity_x = ReadCoord();
+                       self.velocity_y = ReadCoord();
+                       self.velocity_z = ReadCoord();
+               }
+               else
+               {
+                       self.angles_x = ReadAngle();
+                       self.angles_y = ReadAngle();
+               }
+       }
+       if(f & 4)
+               self.state = ReadByte();
+       InterpolateOrigin_Note();
+       self.draw = Draw_Laser;
+}
+#endif
diff --git a/qcsrc/common/triggers/misc/laser.qh b/qcsrc/common/triggers/misc/laser.qh
new file mode 100644 (file)
index 0000000..a77c4c5
--- /dev/null
@@ -0,0 +1,3 @@
+#ifdef CSQC
+void Ent_Laser();
+#endif
diff --git a/qcsrc/common/triggers/misc/teleport_dest.qc b/qcsrc/common/triggers/misc/teleport_dest.qc
new file mode 100644 (file)
index 0000000..b80ce82
--- /dev/null
@@ -0,0 +1,30 @@
+#ifdef SVQC
+
+void spawnfunc_info_teleport_destination (void)
+{
+       self.classname = "info_teleport_destination";
+
+       self.mangle = self.angles;
+       self.angles = '0 0 0';
+
+       //setorigin (self, self.origin + '0 0 27');     // To fix a mappers' habit as old as Quake
+       setorigin (self, self.origin);
+
+       IFTARGETED
+       {
+       }
+       else
+               objerror ("^3Teleport destination without a targetname");
+}
+
+void spawnfunc_misc_teleporter_dest (void)
+{
+       spawnfunc_info_teleport_destination();
+}
+
+void spawnfunc_target_teleporter (void)
+{
+       spawnfunc_info_teleport_destination();
+}
+
+#endif
diff --git a/qcsrc/common/triggers/platforms.qc b/qcsrc/common/triggers/platforms.qc
new file mode 100644 (file)
index 0000000..b837d7c
--- /dev/null
@@ -0,0 +1,235 @@
+void generic_plat_blocked()
+{
+#ifdef SVQC
+       if(self.dmg && other.takedamage != DAMAGE_NO)
+       {
+               if(self.dmgtime2 < time)
+               {
+                       Damage (other, self, self, self.dmg, DEATH_HURTTRIGGER, other.origin, '0 0 0');
+                       self.dmgtime2 = time + self.dmgtime;
+               }
+
+               // Gib dead/dying stuff
+               if(other.deadflag != DEAD_NO)
+                       Damage (other, self, self, 10000, DEATH_HURTTRIGGER, other.origin, '0 0 0');
+       }
+#endif
+}
+
+void plat_spawn_inside_trigger()
+{
+       entity trigger;
+       vector tmin, tmax;
+
+       trigger = spawn();
+       trigger.touch = plat_center_touch;
+       trigger.movetype = MOVETYPE_NONE;
+       trigger.solid = SOLID_TRIGGER;
+       trigger.enemy = self;
+
+#ifdef CSQC
+       trigger.drawmask = MASK_NORMAL;
+       trigger.trigger_touch = plat_center_touch;
+       trigger.draw = trigger_draw_generic;
+#endif
+
+       tmin = self.absmin + '25 25 0';
+       tmax = self.absmax - '25 25 -8';
+       tmin_z = tmax_z - (self.pos1_z - self.pos2_z + 8);
+       if (self.spawnflags & PLAT_LOW_TRIGGER)
+               tmax_z = tmin_z + 8;
+
+       if (self.size_x <= 50)
+       {
+               tmin_x = (self.mins_x + self.maxs_x) / 2;
+               tmax_x = tmin_x + 1;
+       }
+       if (self.size_y <= 50)
+       {
+               tmin_y = (self.mins_y + self.maxs_y) / 2;
+               tmax_y = tmin_y + 1;
+       }
+
+       if(tmin_x < tmax_x)
+               if(tmin_y < tmax_y)
+                       if(tmin_z < tmax_z)
+                       {
+                               setsize (trigger, tmin, tmax);
+                               return;
+                       }
+
+       // otherwise, something is fishy...
+       remove(trigger);
+       objerror("plat_spawn_inside_trigger: platform has odd size or lip, can't spawn");
+}
+
+void plat_hit_top()
+{
+       sound (self, CH_TRIGGER_SINGLE, self.noise1, VOL_BASE, ATTEN_NORM);
+       self.state = 1;
+
+       self.SUB_THINK = plat_go_down;
+       self.SUB_NEXTTHINK = self.SUB_LTIME + 3;
+}
+
+void plat_hit_bottom()
+{
+       sound (self, CH_TRIGGER_SINGLE, self.noise1, VOL_BASE, ATTEN_NORM);
+       self.state = 2;
+}
+
+void plat_go_down()
+{
+       sound (self, CH_TRIGGER_SINGLE, self.noise, VOL_BASE, ATTEN_NORM);
+       self.state = 3;
+       SUB_CalcMove (self.pos2, TSPEED_LINEAR, self.speed, plat_hit_bottom);
+}
+
+void plat_go_up()
+{
+       sound (self, CH_TRIGGER_SINGLE, self.noise, VOL_BASE, ATTEN_NORM);
+       self.state = 4;
+       SUB_CalcMove (self.pos1, TSPEED_LINEAR, self.speed, plat_hit_top);
+}
+
+void plat_center_touch()
+{
+#ifdef SVQC
+       if (!other.iscreature)
+               return;
+
+       if (other.health <= 0)
+               return;
+#elif defined(CSQC)
+       if (!IS_PLAYER(other))
+               return;
+       if(PHYS_DEAD(other))
+               return;
+#endif
+
+       self = self.enemy;
+       if (self.state == 2)
+               plat_go_up ();
+       else if (self.state == 1)
+               self.SUB_NEXTTHINK = self.SUB_LTIME + 1;
+}
+
+void plat_outside_touch()
+{
+#ifdef SVQC
+       if (!other.iscreature)
+               return;
+
+       if (other.health <= 0)
+               return;
+#elif defined(CSQC)
+       if (!IS_PLAYER(other))
+               return;
+#endif
+
+       self = self.enemy;
+       if (self.state == 1)
+               plat_go_down ();
+}
+
+void plat_trigger_use()
+{
+#ifdef SVQC
+       if (self.think)
+               return;         // already activated
+#elif defined(CSQC)
+       if(self.move_think)
+               return;
+#endif
+       plat_go_down();
+}
+
+
+void plat_crush()
+{
+       if((self.spawnflags & 4) && (other.takedamage != DAMAGE_NO))
+       { // KIll Kill Kill!!
+#ifdef SVQC
+               Damage (other, self, self, 10000, DEATH_HURTTRIGGER, other.origin, '0 0 0');
+#endif
+       }
+       else
+       {
+#ifdef SVQC
+               if((self.dmg) && (other.takedamage != DAMAGE_NO))
+               {   // Shall we bite?
+                       Damage (other, self, self, self.dmg, DEATH_HURTTRIGGER, other.origin, '0 0 0');
+                       // Gib dead/dying stuff
+                       if(other.deadflag != DEAD_NO)
+                               Damage (other, self, self, 10000, DEATH_HURTTRIGGER, other.origin, '0 0 0');
+               }
+#endif
+
+               if (self.state == 4)
+                       plat_go_down ();
+               else if (self.state == 3)
+                       plat_go_up ();
+       // when in other states, then the plat_crush event came delayed after
+       // plat state already had changed
+       // this isn't a bug per se!
+       }
+}
+
+void plat_use()
+{
+       self.use = func_null;
+       if (self.state != 4)
+               objerror ("plat_use: not in up state");
+       plat_go_down();
+}
+
+.string sound1, sound2;
+
+void plat_reset()
+{
+       IFTARGETED
+       {
+               setorigin (self, self.pos1);
+               self.state = 4;
+               self.use = plat_use;
+       }
+       else
+       {
+               setorigin (self, self.pos2);
+               self.state = 2;
+               self.use = plat_trigger_use;
+       }
+
+#ifdef SVQC
+       self.SendFlags |= SF_TRIGGER_RESET;
+#endif
+}
+
+.float platmovetype_start_default, platmovetype_end_default;
+bool set_platmovetype(entity e, string s)
+{
+       // sets platmovetype_start and platmovetype_end based on a string consisting of two values
+
+       int n = tokenize_console(s);
+       if(n > 0)
+               e.platmovetype_start = stof(argv(0));
+       else
+               e.platmovetype_start = 0;
+
+       if(n > 1)
+               e.platmovetype_end = stof(argv(1));
+       else
+               e.platmovetype_end = e.platmovetype_start;
+
+       if(n > 2)
+               if(argv(2) == "force")
+                       return true; // no checking, return immediately
+
+       if(!cubic_speedfunc_is_sane(e.platmovetype_start, e.platmovetype_end))
+       {
+               objerror("Invalid platform move type; platform would go in reverse, which is not allowed.");
+               return false;
+       }
+
+       return true;
+}
diff --git a/qcsrc/common/triggers/platforms.qh b/qcsrc/common/triggers/platforms.qh
new file mode 100644 (file)
index 0000000..e0a581b
--- /dev/null
@@ -0,0 +1,16 @@
+#ifndef PLATFORMS_H
+#define PLATFORMS_H
+
+.float dmgtime2;
+
+void() plat_center_touch;
+void() plat_outside_touch;
+void() plat_trigger_use;
+void() plat_go_up;
+void() plat_go_down;
+void() plat_crush;
+const float PLAT_LOW_TRIGGER = 1;
+
+.float dmg;
+
+#endif
diff --git a/qcsrc/common/triggers/subs.qc b/qcsrc/common/triggers/subs.qc
new file mode 100644 (file)
index 0000000..f21455d
--- /dev/null
@@ -0,0 +1,392 @@
+void SUB_NullThink(void) { }
+
+void()  SUB_CalcMoveDone;
+void() SUB_CalcAngleMoveDone;
+//void() SUB_UseTargets;
+
+/*
+==================
+SUB_Remove
+
+Remove self
+==================
+*/
+void SUB_Remove()
+{
+       remove (self);
+}
+
+/*
+==================
+SUB_Friction
+
+Applies some friction to self
+==================
+*/
+.float friction;
+void SUB_Friction (void)
+{
+       self.SUB_NEXTTHINK = time;
+       if(self.SUB_FLAGS & FL_ONGROUND)
+               self.SUB_VELOCITY = self.SUB_VELOCITY * (1 - frametime * self.friction);
+}
+
+/*
+==================
+SUB_VanishOrRemove
+
+Makes client invisible or removes non-client
+==================
+*/
+void SUB_VanishOrRemove (entity ent)
+{
+       if (IS_CLIENT(ent))
+       {
+               // vanish
+               ent.alpha = -1;
+               ent.effects = 0;
+#ifdef SVQC
+               ent.glow_size = 0;
+               ent.pflags = 0;
+#endif
+       }
+       else
+       {
+               // remove
+               remove (ent);
+       }
+}
+
+void SUB_SetFade_Think (void)
+{
+       if(self.alpha == 0)
+               self.alpha = 1;
+       self.SUB_THINK = SUB_SetFade_Think;
+       self.SUB_NEXTTHINK = time;
+       self.alpha -= frametime * self.fade_rate;
+       if (self.alpha < 0.01)
+               SUB_VanishOrRemove(self);
+       else
+               self.SUB_NEXTTHINK = time;
+}
+
+/*
+==================
+SUB_SetFade
+
+Fade 'ent' out when time >= 'when'
+==================
+*/
+void SUB_SetFade (entity ent, float when, float fading_time)
+{
+       ent.fade_rate = 1/fading_time;
+       ent.SUB_THINK = SUB_SetFade_Think;
+       ent.SUB_NEXTTHINK = when;
+}
+
+/*
+=============
+SUB_CalcMove
+
+calculate self.SUB_VELOCITY and self.SUB_NEXTTHINK to reach dest from
+self.SUB_ORIGIN traveling at speed
+===============
+*/
+void SUB_CalcMoveDone (void)
+{
+       // After moving, set origin to exact final destination
+
+       SUB_SETORIGIN (self, self.finaldest);
+       self.SUB_VELOCITY = '0 0 0';
+       self.SUB_NEXTTHINK = -1;
+       if (self.think1)
+               self.think1 ();
+}
+
+.float platmovetype_turn;
+void SUB_CalcMove_controller_think (void)
+{
+       entity oldself;
+       float traveltime;
+       float phasepos;
+       float nexttick;
+       vector delta;
+       vector delta2;
+       vector veloc;
+       vector angloc;
+       vector nextpos;
+       delta = self.destvec;
+       delta2 = self.destvec2;
+       if(time < self.animstate_endtime)
+       {
+               nexttick = time + PHYS_INPUT_FRAMETIME;
+
+               traveltime = self.animstate_endtime - self.animstate_starttime;
+               phasepos = (nexttick - self.animstate_starttime) / traveltime; // range: [0, 1]
+               phasepos = cubic_speedfunc(self.platmovetype_start, self.platmovetype_end, phasepos);
+               nextpos = self.origin + (delta * phasepos) + (delta2 * phasepos * phasepos);
+               // derivative: delta + 2 * delta2 * phasepos (e.g. for angle positioning)
+
+               if(self.owner.platmovetype_turn)
+               {
+                       vector destangle;
+                       destangle = delta + 2 * delta2 * phasepos;
+                       destangle = vectoangles(destangle);
+                       destangle_x = -destangle_x; // flip up / down orientation
+
+                       // take the shortest distance for the angles
+                       SUB_ANGLES(self.owner)_x -= 360 * floor((SUB_ANGLES(self.owner)_x - destangle_x) / 360 + 0.5);
+                       SUB_ANGLES(self.owner)_y -= 360 * floor((SUB_ANGLES(self.owner)_y - destangle_y) / 360 + 0.5);
+                       SUB_ANGLES(self.owner)_z -= 360 * floor((SUB_ANGLES(self.owner)_z - destangle_z) / 360 + 0.5);
+                       angloc = destangle - SUB_ANGLES(self.owner);
+                       angloc = angloc * (1 / PHYS_INPUT_FRAMETIME); // so it arrives for the next frame
+                       self.owner.SUB_AVELOCITY = angloc;
+               }
+               if(nexttick < self.animstate_endtime)
+                       veloc = nextpos - self.owner.SUB_ORIGIN;
+               else
+                       veloc = self.finaldest - self.owner.SUB_ORIGIN;
+               veloc = veloc * (1 / PHYS_INPUT_FRAMETIME); // so it arrives for the next frame
+
+               self.owner.SUB_VELOCITY = veloc;
+               self.nextthink = nexttick;
+       }
+       else
+       {
+               // derivative: delta + 2 * delta2 (e.g. for angle positioning)
+               oldself = self;
+               self.owner.SUB_THINK = self.think1;
+               self = self.owner;
+               remove(oldself);
+               self.SUB_THINK();
+       }
+}
+
+void SUB_CalcMove_controller_setbezier (entity controller, vector org, vector control, vector destin)
+{
+       // 0 * (1-t) * (1-t) + 2 * control * t * (1-t) + destin * t * t
+       // 2 * control * t - 2 * control * t * t + destin * t * t
+       // 2 * control * t + (destin - 2 * control) * t * t
+
+       setorigin(controller, org);
+       control -= org;
+       destin -= org;
+
+       controller.destvec = 2 * control; // control point
+       controller.destvec2 = destin - 2 * control; // quadratic part required to reach end point
+       // also: initial d/dphasepos origin = 2 * control, final speed = 2 * (destin - control)
+}
+
+void SUB_CalcMove_controller_setlinear (entity controller, vector org, vector destin)
+{
+       // 0 * (1-t) * (1-t) + 2 * control * t * (1-t) + destin * t * t
+       // 2 * control * t - 2 * control * t * t + destin * t * t
+       // 2 * control * t + (destin - 2 * control) * t * t
+
+       setorigin(controller, org);
+       destin -= org;
+
+       controller.destvec = destin; // end point
+       controller.destvec2 = '0 0 0';
+}
+
+float TSPEED_TIME = -1;
+float TSPEED_LINEAR = 0;
+float TSPEED_START = 1;
+float TSPEED_END = 2;
+// TODO average too?
+
+void SUB_CalcMove_Bezier (vector tcontrol, vector tdest, float tspeedtype, float tspeed, void() func)
+{
+       float   traveltime;
+       entity controller;
+
+       if (!tspeed)
+               objerror ("No speed is defined!");
+
+       self.think1 = func;
+       self.finaldest = tdest;
+       self.SUB_THINK = SUB_CalcMoveDone;
+
+       switch(tspeedtype)
+       {
+               default:
+               case TSPEED_START:
+                       traveltime = 2 * vlen(tcontrol - self.SUB_ORIGIN) / tspeed;
+                       break;
+               case TSPEED_END:
+                       traveltime = 2 * vlen(tcontrol - tdest)       / tspeed;
+                       break;
+               case TSPEED_LINEAR:
+                       traveltime = vlen(tdest - self.SUB_ORIGIN)        / tspeed;
+                       break;
+               case TSPEED_TIME:
+                       traveltime = tspeed;
+                       break;
+       }
+
+       if (traveltime < 0.1) // useless anim
+       {
+               self.SUB_VELOCITY = '0 0 0';
+               self.SUB_NEXTTHINK = self.SUB_LTIME + 0.1;
+               return;
+       }
+
+       controller = spawn();
+       controller.classname = "SUB_CalcMove_controller";
+       controller.owner = self;
+       controller.platmovetype = self.platmovetype;
+       controller.platmovetype_start = self.platmovetype_start;
+       controller.platmovetype_end = self.platmovetype_end;
+       SUB_CalcMove_controller_setbezier(controller, self.SUB_ORIGIN, tcontrol, tdest);
+       controller.finaldest = (tdest + '0 0 0.125'); // where do we want to end? Offset to overshoot a bit.
+       controller.animstate_starttime = time;
+       controller.animstate_endtime = time + traveltime;
+       controller.think = SUB_CalcMove_controller_think;
+       controller.think1 = self.SUB_THINK;
+
+       // the thinking is now done by the controller
+       self.SUB_THINK = SUB_NullThink; // for PushMove
+       self.SUB_NEXTTHINK = self.SUB_LTIME + traveltime;
+
+       // invoke controller
+       self = controller;
+       self.think();
+       self = self.owner;
+}
+
+void SUB_CalcMove (vector tdest, float tspeedtype, float tspeed, void() func)
+{
+       vector  delta;
+       float   traveltime;
+
+       if (!tspeed)
+               objerror ("No speed is defined!");
+
+       self.think1 = func;
+       self.finaldest = tdest;
+       self.SUB_THINK = SUB_CalcMoveDone;
+
+       if (tdest == self.SUB_ORIGIN)
+       {
+               self.SUB_VELOCITY = '0 0 0';
+               self.SUB_NEXTTHINK = self.SUB_LTIME + 0.1;
+               return;
+       }
+
+       delta = tdest - self.SUB_ORIGIN;
+
+       switch(tspeedtype)
+       {
+               default:
+               case TSPEED_START:
+               case TSPEED_END:
+               case TSPEED_LINEAR:
+                       traveltime = vlen (delta) / tspeed;
+                       break;
+               case TSPEED_TIME:
+                       traveltime = tspeed;
+                       break;
+       }
+
+       // 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.
+        // The only currently implemented alternative movement is linear (value 1)
+       if (traveltime < 0.15 || (self.platmovetype_start == 1 && self.platmovetype_end == 1)) // is this correct?
+       {
+               self.SUB_VELOCITY = delta * (1/traveltime);     // QuakeC doesn't allow vector/float division
+               self.SUB_NEXTTHINK = self.SUB_LTIME + traveltime;
+               return;
+       }
+
+       // now just run like a bezier curve...
+       SUB_CalcMove_Bezier((self.SUB_ORIGIN + tdest) * 0.5, tdest, tspeedtype, tspeed, func);
+}
+
+void SUB_CalcMoveEnt (entity ent, vector tdest, float tspeedtype, float tspeed, void() func)
+{
+       entity  oldself;
+
+       oldself = self;
+       self = ent;
+
+       SUB_CalcMove (tdest, tspeedtype, tspeed, func);
+
+       self = oldself;
+}
+
+/*
+=============
+SUB_CalcAngleMove
+
+calculate self.SUB_AVELOCITY and self.SUB_NEXTTHINK to reach destangle from
+self.angles rotating
+
+The calling function should make sure self.SUB_THINK is valid
+===============
+*/
+void SUB_CalcAngleMoveDone (void)
+{
+       // After rotating, set angle to exact final angle
+       self.angles = self.finalangle;
+       self.SUB_AVELOCITY = '0 0 0';
+       self.SUB_NEXTTHINK = -1;
+       if (self.think1)
+               self.think1 ();
+}
+
+// FIXME: I fixed this function only for rotation around the main axes
+void SUB_CalcAngleMove (vector destangle, float tspeedtype, float tspeed, void() func)
+{
+       vector  delta;
+       float   traveltime;
+
+       if (!tspeed)
+               objerror ("No speed is defined!");
+
+       // take the shortest distance for the angles
+       self.angles_x -= 360 * floor((self.angles_x - destangle_x) / 360 + 0.5);
+       self.angles_y -= 360 * floor((self.angles_y - destangle_y) / 360 + 0.5);
+       self.angles_z -= 360 * floor((self.angles_z - destangle_z) / 360 + 0.5);
+       delta = destangle - self.angles;
+
+       switch(tspeedtype)
+       {
+               default:
+               case TSPEED_START:
+               case TSPEED_END:
+               case TSPEED_LINEAR:
+                       traveltime = vlen (delta) / tspeed;
+                       break;
+               case TSPEED_TIME:
+                       traveltime = tspeed;
+                       break;
+       }
+
+       self.think1 = func;
+       self.finalangle = destangle;
+       self.SUB_THINK = SUB_CalcAngleMoveDone;
+
+       if (traveltime < 0.1)
+       {
+               self.SUB_AVELOCITY = '0 0 0';
+               self.SUB_NEXTTHINK = self.SUB_LTIME + 0.1;
+               return;
+       }
+
+       self.SUB_AVELOCITY = delta * (1 / traveltime);
+       self.SUB_NEXTTHINK = self.SUB_LTIME + traveltime;
+}
+
+void SUB_CalcAngleMoveEnt (entity ent, vector destangle, float tspeedtype, float tspeed, void() func)
+{
+       entity  oldself;
+
+       oldself = self;
+       self = ent;
+
+       SUB_CalcAngleMove (destangle, tspeedtype, tspeed, func);
+
+       self = oldself;
+}
diff --git a/qcsrc/common/triggers/subs.qh b/qcsrc/common/triggers/subs.qh
new file mode 100644 (file)
index 0000000..ea8ef30
--- /dev/null
@@ -0,0 +1,108 @@
+#ifndef SUBS_H
+#define SUBS_H
+
+#ifdef SVQC
+
+       #define SUB_ANGLES(s)           (s).angles
+       #define SUB_VELOCITY            velocity
+       #define SUB_AVELOCITY           avelocity
+       #define SUB_ORIGIN                      origin
+       #define SUB_SETORIGIN(s,v)      setorigin((s), (v))
+       #define SUB_NEXTTHINK           nextthink
+       #define SUB_THINK                       think
+       #define SUB_LTIME                       ltime
+       #define SUB_FLAGS                       flags
+
+#elif defined(CSQC)
+
+       void _Movetype_LinkEdict(float touch_triggers);
+
+       #define SUB_ANGLES(s)   (s).move_angles
+       #define SUB_VELOCITY    move_velocity
+       #define SUB_AVELOCITY   move_avelocity
+       #define SUB_ORIGIN              move_origin
+       #define SUB_NEXTTHINK   move_nextthink
+       #define SUB_THINK               move_think
+       #define SUB_LTIME               move_ltime
+       #define SUB_FLAGS               move_flags
+
+       void SUB_SETORIGIN(entity s, vector v)
+       {
+               s.move_origin = v;
+               entity oldself = self;
+               self = s;
+               _Movetype_LinkEdict(true);
+               self = oldself;
+       }
+
+#endif
+
+void SUB_Remove();
+void SUB_SetFade (entity ent, float when, float fading_time);
+void SUB_VanishOrRemove (entity ent);
+
+.vector                finaldest, finalangle;          //plat.qc stuff
+.void()                think1;
+.float state;
+.float         t_length, t_width;
+
+.vector destvec;
+.vector destvec2;
+
+// player animation state
+.float animstate_startframe;
+.float animstate_numframes;
+.float animstate_framerate;
+.float animstate_starttime;
+.float animstate_endtime;
+.float animstate_override;
+.float animstate_looping;
+
+.float delay;
+.float wait;
+.float lip;
+.float speed;
+.float sounds;
+.string  platmovetype;
+.float platmovetype_start, platmovetype_end;
+
+entity activator;
+
+.string killtarget;
+
+.vector        pos1, pos2;
+.vector        mangle;
+
+.string target2;
+.string target3;
+.string target4;
+.string curvetarget;
+.float target_random;
+.float trigger_reverse;
+
+// Keys player is holding
+.float itemkeys;
+// message delay for func_door locked by keys and key locks
+// this field is used on player entities
+.float key_door_messagetime;
+
+.vector dest1, dest2;
+
+#ifdef CSQC
+// this stuff is defined in the server side engine VM, so we must define it separately here
+.float takedamage;
+const float DAMAGE_NO  = 0;
+const float DAMAGE_YES = 1;
+const float DAMAGE_AIM = 2;
+
+float  STATE_TOP               = 0;
+float  STATE_BOTTOM    = 1;
+float  STATE_UP                = 2;
+float  STATE_DOWN              = 3;
+
+.string                noise, noise1, noise2, noise3;  // contains names of wavs to play
+
+.float         max_health;             // players maximum health is stored here
+#endif
+
+#endif
diff --git a/qcsrc/common/triggers/target/changelevel.qc b/qcsrc/common/triggers/target/changelevel.qc
new file mode 100644 (file)
index 0000000..1ec8cc9
--- /dev/null
@@ -0,0 +1,18 @@
+#ifdef SVQC
+.string chmap, gametype;
+void spawnfunc_target_changelevel_use()
+{
+       if(self.gametype != "")
+               MapInfo_SwitchGameType(MapInfo_Type_FromString(self.gametype));
+
+       if (self.chmap == "")
+               localcmd("endmatch\n");
+       else
+               localcmd(strcat("changelevel ", self.chmap, "\n"));
+}
+
+void spawnfunc_target_changelevel()
+{
+       self.use = spawnfunc_target_changelevel_use;
+}
+#endif
diff --git a/qcsrc/common/triggers/target/include.qc b/qcsrc/common/triggers/target/include.qc
new file mode 100644 (file)
index 0000000..c53ea6d
--- /dev/null
@@ -0,0 +1,8 @@
+#include "include.qh"
+
+#include "changelevel.qc"
+#include "location.qc"
+#include "music.qc"
+#include "spawn.qc"
+#include "speaker.qc"
+#include "voicescript.qc"
diff --git a/qcsrc/common/triggers/target/include.qh b/qcsrc/common/triggers/target/include.qh
new file mode 100644 (file)
index 0000000..4e44b97
--- /dev/null
@@ -0,0 +1,6 @@
+#ifndef TRIGGERS_TARGET_INCLUDE_H
+#define TRIGGERS_TARGET_INCLUDE_H
+
+#include "music.qh"
+
+#endif
diff --git a/qcsrc/common/triggers/target/location.qc b/qcsrc/common/triggers/target/location.qc
new file mode 100644 (file)
index 0000000..1430cab
--- /dev/null
@@ -0,0 +1,14 @@
+#ifdef SVQC
+void spawnfunc_target_location()
+{
+    self.classname = "target_location";
+    // location name in netname
+    // eventually support: count, teamgame selectors, line of sight?
+}
+
+void spawnfunc_info_location()
+{
+    self.classname = "target_location";
+    self.message = self.netname;
+}
+#endif
diff --git a/qcsrc/common/triggers/target/music.qc b/qcsrc/common/triggers/target/music.qc
new file mode 100644 (file)
index 0000000..4594c90
--- /dev/null
@@ -0,0 +1,329 @@
+#if defined(CSQC)
+#elif defined(MENUQC)
+#elif defined(SVQC)
+    #include "../../../server/_all.qh"
+    #include "../../constants.qh"
+    #include "../../../server/constants.qh"
+    #include "../../../server/defs.qh"
+#endif
+
+#ifdef SVQC
+
+// values:
+//   volume
+//   noise
+//   targetname
+//   lifetime
+//   fade_time
+//   fade_rate
+// when triggered, the music is overridden for activator until lifetime (or forever, if lifetime is 0)
+// when targetname is not set, THIS ONE is default
+void target_music_sendto(float to, float is)
+{
+       WriteByte(to, SVC_TEMPENTITY);
+       WriteByte(to, TE_CSQC_TARGET_MUSIC);
+       WriteShort(to, num_for_edict(self));
+       WriteByte(to, self.volume * 255.0 * is);
+       WriteByte(to, self.fade_time * 16.0);
+       WriteByte(to, self.fade_rate * 16.0);
+       WriteByte(to, self.lifetime);
+       WriteString(to, self.noise);
+}
+void target_music_reset()
+{
+       if(self.targetname == "")
+               target_music_sendto(MSG_ALL, 1);
+}
+void target_music_use()
+{
+       if(!activator)
+               return;
+       if(IS_REAL_CLIENT(activator))
+       {
+               msg_entity = activator;
+               target_music_sendto(MSG_ONE, 1);
+       }
+       entity head;
+       FOR_EACH_SPEC(head) if(head.enemy == activator) { msg_entity = head; target_music_sendto(MSG_ONE, 1); }
+}
+void spawnfunc_target_music()
+{
+       self.use = target_music_use;
+       self.reset = target_music_reset;
+       if(!self.volume)
+               self.volume = 1;
+       if(self.targetname == "")
+               target_music_sendto(MSG_INIT, 1);
+       else
+               target_music_sendto(MSG_INIT, 0);
+}
+void TargetMusic_RestoreGame()
+{
+       for(self = world; (self = find(self, classname, "target_music")); )
+       {
+               if(self.targetname == "")
+                       target_music_sendto(MSG_INIT, 1);
+               else
+                       target_music_sendto(MSG_INIT, 0);
+       }
+}
+// values:
+//   volume
+//   noise
+//   targetname
+//   fade_time
+// spawnflags:
+//   1 = START_OFF
+// when triggered, it is disabled/enabled for everyone
+float trigger_music_SendEntity(entity to, float sf)
+{
+       WriteByte(MSG_ENTITY, ENT_CLIENT_TRIGGER_MUSIC);
+       sf &= ~0x80;
+       if(self.cnt)
+               sf |= 0x80;
+       WriteByte(MSG_ENTITY, sf);
+       if(sf & 4)
+       {
+               WriteCoord(MSG_ENTITY, self.origin.x);
+               WriteCoord(MSG_ENTITY, self.origin.y);
+               WriteCoord(MSG_ENTITY, self.origin.z);
+       }
+       if(sf & 1)
+       {
+               if(self.model != "null")
+               {
+                       WriteShort(MSG_ENTITY, self.modelindex);
+                       WriteCoord(MSG_ENTITY, self.mins.x);
+                       WriteCoord(MSG_ENTITY, self.mins.y);
+                       WriteCoord(MSG_ENTITY, self.mins.z);
+                       WriteCoord(MSG_ENTITY, self.maxs.x);
+                       WriteCoord(MSG_ENTITY, self.maxs.y);
+                       WriteCoord(MSG_ENTITY, self.maxs.z);
+               }
+               else
+               {
+                       WriteShort(MSG_ENTITY, 0);
+                       WriteCoord(MSG_ENTITY, self.maxs.x);
+                       WriteCoord(MSG_ENTITY, self.maxs.y);
+                       WriteCoord(MSG_ENTITY, self.maxs.z);
+               }
+               WriteByte(MSG_ENTITY, self.volume * 255.0);
+               WriteByte(MSG_ENTITY, self.fade_time * 16.0);
+               WriteByte(MSG_ENTITY, self.fade_rate * 16.0);
+               WriteString(MSG_ENTITY, self.noise);
+       }
+       return 1;
+}
+void trigger_music_reset()
+{
+       self.cnt = !(self.spawnflags & 1);
+       self.SendFlags |= 0x80;
+}
+void trigger_music_use()
+{
+       self.cnt = !self.cnt;
+       self.SendFlags |= 0x80;
+}
+void spawnfunc_trigger_music()
+{
+       if(self.model != "")
+               setmodel(self, self.model);
+       if(!self.volume)
+               self.volume = 1;
+       if(!self.modelindex)
+       {
+               setorigin(self, self.origin + self.mins);
+               setsize(self, '0 0 0', self.maxs - self.mins);
+       }
+       trigger_music_reset();
+
+       self.use = trigger_music_use;
+       self.reset = trigger_music_reset;
+
+       Net_LinkEntity(self, false, 0, trigger_music_SendEntity);
+}
+#elif defined(CSQC)
+
+void TargetMusic_Advance()
+{
+       // run AFTER all the thinks!
+       entity best, e;
+       float vol, vol0;
+       best = music_default;
+       if(music_target && time < music_target.lifetime)
+               best = music_target;
+       if(music_trigger)
+               best = music_trigger;
+       for(e = world; (e = findfloat(e, enttype, ENT_CLIENT_TRIGGER_MUSIC)); ) if(e.noise)
+       {
+               vol0 = e.lastvol;
+               if(getsoundtime(e, CH_BGM_SINGLE) < 0)
+               {
+                       vol0 = -1;
+               }
+               if(e == best)
+               {
+                       // increase volume
+                       if(e.fade_time > 0)
+                               e.state = bound(0, e.state + frametime / e.fade_time, 1);
+                       else
+                               e.state = 1;
+               }
+               else
+               {
+                       // decrease volume
+                       if(e.fade_rate > 0)
+                               e.state = bound(0, e.state - frametime / e.fade_rate, 1);
+                       else
+                               e.state = 0;
+               }
+               vol = e.state * e.volume * autocvar_bgmvolume;
+               if(vol != vol0)
+               {
+                       if(vol0 < 0)
+                               sound(e, CH_BGM_SINGLE, e.noise, vol, ATTEN_NONE); // restart
+                       else
+                               sound(e, CH_BGM_SINGLE, "", vol, ATTEN_NONE);
+                       e.lastvol = vol;
+               }
+       }
+       music_trigger = world;
+
+       if(best)
+               bgmtime = getsoundtime(best, CH_BGM_SINGLE);
+       else
+               bgmtime = gettime(GETTIME_CDTRACK);
+}
+
+void Net_TargetMusic()
+{
+       int id = ReadShort();
+       float vol = ReadByte() / 255.0;
+       float fai = ReadByte() / 16.0;
+       float fao = ReadByte() / 16.0;
+       float tim = ReadByte();
+       string noi = ReadString();
+
+       entity e;
+       for(e = world; (e = findfloat(e, enttype, ENT_CLIENT_TRIGGER_MUSIC)); )
+       {
+               if(e.count == id)
+                       break;
+       }
+       if(!e)
+       {
+               e = spawn();
+               e.enttype = ENT_CLIENT_TRIGGER_MUSIC;
+               e.count = id;
+       }
+       if(e.noise != noi)
+       {
+               if(e.noise)
+                       strunzone(e.noise);
+               e.noise = strzone(noi);
+               precache_sound(e.noise);
+               sound(e, CH_BGM_SINGLE, e.noise, 0, ATTEN_NONE);
+               if(getsoundtime(e, CH_BGM_SINGLE) < 0)
+               {
+                       dprintf("Cannot initialize sound %s\n", e.noise);
+                       strunzone(e.noise);
+                       e.noise = string_null;
+               }
+       }
+       e.volume = vol;
+       e.fade_time = fai;
+       e.fade_rate = fao;
+       if(vol > 0)
+       {
+               if(tim == 0)
+               {
+                       music_default = e;
+                       if(!music_disabled)
+                       {
+                               e.state = 2;
+                               cvar_settemp("music_playlist_index", "-1"); // don't use playlists
+                               localcmd("cd stop\n"); // just in case
+                               music_disabled = 1;
+                       }
+               }
+               else
+               {
+                       music_target = e;
+                       e.lifetime = time + tim;
+               }
+       }
+}
+
+void Ent_TriggerMusic_Think()
+{
+       if(WarpZoneLib_BoxTouchesBrush(view_origin, view_origin, self, world))
+       {
+               music_trigger = self;
+       }
+       self.nextthink = time;
+}
+
+void Ent_TriggerMusic_Remove()
+{
+       if(self.noise)
+               strunzone(self.noise);
+       self.noise = string_null;
+}
+
+void Ent_ReadTriggerMusic()
+{
+       int f = ReadByte();
+       if(f & 4)
+       {
+               self.origin_x = ReadCoord();
+               self.origin_y = ReadCoord();
+               self.origin_z = ReadCoord();
+       }
+       if(f & 1)
+       {
+               self.modelindex = ReadShort();
+               if(self.modelindex)
+               {
+                       self.mins_x = ReadCoord();
+                       self.mins_y = ReadCoord();
+                       self.mins_z = ReadCoord();
+                       self.maxs_x = ReadCoord();
+                       self.maxs_y = ReadCoord();
+                       self.maxs_z = ReadCoord();
+               }
+               else
+               {
+                       self.mins    = '0 0 0';
+                       self.maxs_x = ReadCoord();
+                       self.maxs_y = ReadCoord();
+                       self.maxs_z = ReadCoord();
+               }
+
+               self.volume = ReadByte() / 255.0;
+               self.fade_time = ReadByte() / 16.0;
+               self.fade_rate = ReadByte() / 16.0;
+               string s = self.noise;
+               if(self.noise)
+                       strunzone(self.noise);
+               self.noise = strzone(ReadString());
+               if(self.noise != s)
+               {
+                       precache_sound(self.noise);
+                       sound(self, CH_BGM_SINGLE, self.noise, 0, ATTEN_NONE);
+                       if(getsoundtime(self, CH_BGM_SINGLE) < 0)
+                       {
+                               dprintf("Cannot initialize sound %s\n", self.noise);
+                               strunzone(self.noise);
+                               self.noise = string_null;
+                       }
+               }
+       }
+
+       setorigin(self, self.origin);
+       setsize(self, self.mins, self.maxs);
+       self.cnt = 1;
+       self.think = Ent_TriggerMusic_Think;
+       self.nextthink = time;
+}
+
+#endif
diff --git a/qcsrc/common/triggers/target/music.qh b/qcsrc/common/triggers/target/music.qh
new file mode 100644 (file)
index 0000000..712d412
--- /dev/null
@@ -0,0 +1,28 @@
+#ifndef TARGET_MUSIC_H
+#define TARGET_MUSIC_H
+
+.float lifetime;
+
+#ifdef CSQC
+float music_disabled;
+entity music_default;
+entity music_target;
+entity music_trigger;
+// FIXME also control bgmvolume here, to not require a target_music for the default track.
+
+entityclass(TargetMusic);
+class(TargetMusic) .int state;
+class(TargetMusic) .float lastvol;
+
+void TargetMusic_Advance();
+
+void Net_TargetMusic();
+
+void Ent_TriggerMusic_Think();
+
+void Ent_TriggerMusic_Remove();
+
+void Ent_ReadTriggerMusic();
+#endif
+
+#endif
diff --git a/qcsrc/common/triggers/target/spawn.qc b/qcsrc/common/triggers/target/spawn.qc
new file mode 100644 (file)
index 0000000..f5c54b5
--- /dev/null
@@ -0,0 +1,349 @@
+#if defined(CSQC)
+#elif defined(MENUQC)
+#elif defined(SVQC)
+       #include "../../../server/_all.qh"
+    #include "../../util.qh"
+    #include "../../../server/defs.qh"
+#endif
+
+#ifdef SVQC
+
+// spawner entity
+// "classname" "target_spawn"
+// "message" "fieldname value fieldname value ..."
+// "spawnflags"
+//   1 = call the spawn function
+//   2 = trigger on map load
+
+float target_spawn_initialized;
+.void() target_spawn_spawnfunc;
+float target_spawn_spawnfunc_field;
+.entity target_spawn_activator;
+.float target_spawn_id;
+float target_spawn_count;
+
+void target_spawn_helper_setmodel()
+{
+       setmodel(self, self.model);
+}
+
+void target_spawn_helper_setsize()
+{
+       setsize(self, self.mins, self.maxs);
+}
+
+void target_spawn_edit_entity(entity e, string msg, entity kt, entity t2, entity t3, entity t4, entity act)
+{
+       float i, n, valuefieldpos;
+       string key, value, valuefield, valueoffset, valueoffsetrandom;
+       entity valueent;
+       vector data, data2;
+       entity oldself;
+       entity oldactivator;
+
+       n = tokenize_console(msg);
+
+       for(i = 0; i < n-1; i += 2)
+       {
+               key = argv(i);
+               value = argv(i+1);
+               if(key == "$")
+               {
+                       data.x = -1;
+                       data.y = FIELD_STRING;
+               }
+               else
+               {
+                       data = stov(db_get(TemporaryDB, strcat("/target_spawn/field/", key)));
+                       if(data.y == 0) // undefined field, i.e., invalid type
+                       {
+                               print("target_spawn: invalid/unknown entity key ", key, " specified, ignored!\n");
+                               continue;
+                       }
+               }
+               if(substring(value, 0, 1) == "$")
+               {
+                       value = substring(value, 1, strlen(value) - 1);
+                       if(substring(value, 0, 1) == "$")
+                       {
+                               // deferred replacement
+                               // do nothing
+                               // useful for creating target_spawns with this!
+                       }
+                       else
+                       {
+                               // replace me!
+                               valuefieldpos = strstrofs(value, "+", 0);
+                               valueoffset = "";
+                               if(valuefieldpos != -1)
+                               {
+                                       valueoffset = substring(value, valuefieldpos + 1, strlen(value) - valuefieldpos - 1);
+                                       value = substring(value, 0, valuefieldpos);
+                               }
+
+                               valuefieldpos = strstrofs(valueoffset, "+", 0);
+                               valueoffsetrandom = "";
+                               if(valuefieldpos != -1)
+                               {
+                                       valueoffsetrandom = substring(valueoffset, valuefieldpos + 1, strlen(valueoffset) - valuefieldpos - 1);
+                                       valueoffset = substring(valueoffset, 0, valuefieldpos);
+                               }
+
+                               valuefieldpos = strstrofs(value, ".", 0);
+                               valuefield = "";
+                               if(valuefieldpos != -1)
+                               {
+                                       valuefield = substring(value, valuefieldpos + 1, strlen(value) - valuefieldpos - 1);
+                                       value = substring(value, 0, valuefieldpos);
+                               }
+
+                               if(value == "self")
+                               {
+                                       valueent = self;
+                                       value = "";
+                               }
+                               else if(value == "activator")
+                               {
+                                       valueent = act;
+                                       value = "";
+                               }
+                               else if(value == "other")
+                               {
+                                       valueent = other;
+                                       value = "";
+                               }
+                               else if(value == "pusher")
+                               {
+                                       if(time < act.pushltime)
+                                               valueent = act.pusher;
+                                       else
+                                               valueent = world;
+                                       value = "";
+                               }
+                               else if(value == "target")
+                               {
+                                       valueent = e;
+                                       value = "";
+                               }
+                               else if(value == "killtarget")
+                               {
+                                       valueent = kt;
+                                       value = "";
+                               }
+                               else if(value == "target2")
+                               {
+                                       valueent = t2;
+                                       value = "";
+                               }
+                               else if(value == "target3")
+                               {
+                                       valueent = t3;
+                                       value = "";
+                               }
+                               else if(value == "target4")
+                               {
+                                       valueent = t4;
+                                       value = "";
+                               }
+                               else if(value == "time")
+                               {
+                                       valueent = world;
+                                       value = ftos(time);
+                               }
+                               else
+                               {
+                                       print("target_spawn: invalid/unknown variable replacement ", value, " specified, ignored!\n");
+                                       continue;
+                               }
+
+                               if(valuefield == "")
+                               {
+                                       if(value == "")
+                                               value = ftos(num_for_edict(valueent));
+                               }
+                               else
+                               {
+                                       if(value != "")
+                                       {
+                                               print("target_spawn: try to get a field of a non-entity, ignored!\n");
+                                               continue;
+                                       }
+                                       data2 = stov(db_get(TemporaryDB, strcat("/target_spawn/field/", valuefield)));
+                                       if(data2_y == 0) // undefined field, i.e., invalid type
+                                       {
+                                               print("target_spawn: invalid/unknown entity key replacement ", valuefield, " specified, ignored!\n");
+                                               continue;
+                                       }
+                                       value = getentityfieldstring(data2_x, valueent);
+                               }
+
+                               if(valueoffset != "")
+                               {
+                                       switch(data.y)
+                                       {
+                                               case FIELD_STRING:
+                                                       value = strcat(value, valueoffset);
+                                                       break;
+                                               case FIELD_FLOAT:
+                                                       value = ftos(stof(value) + stof(valueoffset));
+                                                       break;
+                                               case FIELD_VECTOR:
+                                                       value = vtos(stov(value) + stov(valueoffset));
+                                                       break;
+                                               default:
+                                                       print("target_spawn: only string, float and vector fields can do calculations, calculation ignored!\n");
+                                                       break;
+                                       }
+                               }
+
+                               if(valueoffsetrandom != "")
+                               {
+                                       switch(data.y)
+                                       {
+                                               case FIELD_FLOAT:
+                                                       value = ftos(stof(value) + random() * stof(valueoffsetrandom));
+                                                       break;
+                                               case FIELD_VECTOR:
+                                                       data2 = stov(valueoffsetrandom);
+                                                       value = vtos(stov(value) + random() * data2_x * '1 0 0' + random() * data2_y * '0 1 0' + random() * data2_z * '0 0 1');
+                                                       break;
+                                               default:
+                                                       print("target_spawn: only float and vector fields can do random calculations, calculation ignored!\n");
+                                                       break;
+                                       }
+                               }
+                       }
+               }
+               if(key == "$")
+               {
+                       if(substring(value, 0, 1) == "_")
+                               value = strcat("target_spawn_helper", value);
+                       putentityfieldstring(target_spawn_spawnfunc_field, e, value);
+
+                       oldself = self;
+                       oldactivator = activator;
+
+                       self = e;
+                       activator = act;
+
+                       self.target_spawn_spawnfunc();
+
+                       self = oldself;
+                       activator = oldactivator;
+
+                       // We called an external function, so we have to re-tokenize msg.
+                       n = tokenize_console(msg);
+               }
+               else
+               {
+                       if(data.y == FIELD_VECTOR)
+                               value = strreplace("'", "", value); // why?!?
+                       putentityfieldstring(data.x, e, value);
+               }
+       }
+}
+
+void target_spawn_useon(entity e)
+{
+       self.target_spawn_activator = activator;
+       target_spawn_edit_entity(
+               e,
+               self.message,
+               find(world, targetname, self.killtarget),
+               find(world, targetname, self.target2),
+               find(world, targetname, self.target3),
+               find(world, targetname, self.target4),
+               activator
+       );
+}
+
+float target_spawn_cancreate()
+{
+       float c;
+       entity e;
+
+       c = self.count;
+       if(c == 0) // no limit?
+               return 1;
+
+       ++c; // increase count to not include MYSELF
+       for(e = world; (e = findfloat(e, target_spawn_id, self.target_spawn_id)); --c)
+               ;
+
+       // if c now is 0, we have AT LEAST the given count (maybe more), so don't spawn any more
+       if(c == 0)
+               return 0;
+       return 1;
+}
+
+void target_spawn_use()
+{
+       entity e;
+
+       if(self.target == "")
+       {
+               // spawn new entity
+               if(!target_spawn_cancreate())
+                       return;
+               e = spawn();
+               target_spawn_useon(e);
+               e.target_spawn_id = self.target_spawn_id;
+       }
+       else if(self.target == "*activator")
+       {
+               // edit entity
+               if(activator)
+                       target_spawn_useon(activator);
+       }
+       else
+       {
+               // edit entity
+               for(e = world; (e = find(e, targetname, self.target)); )
+                       target_spawn_useon(e);
+       }
+}
+
+void target_spawn_spawnfirst()
+{
+       activator = self.target_spawn_activator;
+       if(self.spawnflags & 2)
+               target_spawn_use();
+}
+
+void initialize_field_db()
+{
+       if(!target_spawn_initialized)
+       {
+               float n, i;
+               string fn;
+               vector prev, next;
+               float ft;
+
+               n = numentityfields();
+               for(i = 0; i < n; ++i)
+               {
+                       fn = entityfieldname(i);
+                       ft = entityfieldtype(i);
+                       next = i * '1 0 0' + ft * '0 1 0' + '0 0 1';
+                       prev = stov(db_get(TemporaryDB, strcat("/target_spawn/field/", fn)));
+                       if(prev.y == 0)
+                       {
+                               db_put(TemporaryDB, strcat("/target_spawn/field/", fn), vtos(next));
+                               if(fn == "target_spawn_spawnfunc")
+                                       target_spawn_spawnfunc_field = i;
+                       }
+               }
+
+               target_spawn_initialized = 1;
+       }
+}
+
+void spawnfunc_target_spawn()
+{
+       initialize_field_db();
+       self.use = target_spawn_use;
+       self.message = strzone(strreplace("'", "\"", self.message));
+       self.target_spawn_id = ++target_spawn_count;
+       InitializeEntity(self, target_spawn_spawnfirst, INITPRIO_LAST);
+}
+#endif
diff --git a/qcsrc/common/triggers/target/speaker.qc b/qcsrc/common/triggers/target/speaker.qc
new file mode 100644 (file)
index 0000000..7be8b91
--- /dev/null
@@ -0,0 +1,133 @@
+#ifdef SVQC
+// TODO add a way to do looped sounds with sound(); then complete this entity
+void target_speaker_use_off();
+void target_speaker_use_activator()
+{
+       if (!IS_REAL_CLIENT(activator))
+               return;
+       string snd;
+       if(substring(self.noise, 0, 1) == "*")
+       {
+               var .string sample;
+               sample = GetVoiceMessageSampleField(substring(self.noise, 1, -1));
+               if(GetPlayerSoundSampleField_notFound)
+                       snd = "misc/null.wav";
+               else if(activator.sample == "")
+                       snd = "misc/null.wav";
+               else
+               {
+                       tokenize_console(activator.sample);
+                       float n;
+                       n = stof(argv(1));
+                       if(n > 0)
+                               snd = strcat(argv(0), ftos(floor(random() * n + 1)), ".wav"); // randomization
+                       else
+                               snd = strcat(argv(0), ".wav"); // randomization
+               }
+       }
+       else
+               snd = self.noise;
+       msg_entity = activator;
+       soundto(MSG_ONE, self, CH_TRIGGER, snd, VOL_BASE * self.volume, self.atten);
+}
+void target_speaker_use_on()
+{
+       string snd;
+       if(substring(self.noise, 0, 1) == "*")
+       {
+               var .string sample;
+               sample = GetVoiceMessageSampleField(substring(self.noise, 1, -1));
+               if(GetPlayerSoundSampleField_notFound)
+                       snd = "misc/null.wav";
+               else if(activator.sample == "")
+                       snd = "misc/null.wav";
+               else
+               {
+                       tokenize_console(activator.sample);
+                       float n;
+                       n = stof(argv(1));
+                       if(n > 0)
+                               snd = strcat(argv(0), ftos(floor(random() * n + 1)), ".wav"); // randomization
+                       else
+                               snd = strcat(argv(0), ".wav"); // randomization
+               }
+       }
+       else
+               snd = self.noise;
+       sound(self, CH_TRIGGER_SINGLE, snd, VOL_BASE * self.volume, self.atten);
+       if(self.spawnflags & 3)
+               self.use = target_speaker_use_off;
+}
+void target_speaker_use_off()
+{
+       sound(self, CH_TRIGGER_SINGLE, "misc/null.wav", VOL_BASE * self.volume, self.atten);
+       self.use = target_speaker_use_on;
+}
+void target_speaker_reset()
+{
+       if(self.spawnflags & 1) // LOOPED_ON
+       {
+               if(self.use == target_speaker_use_on)
+                       target_speaker_use_on();
+       }
+       else if(self.spawnflags & 2)
+       {
+               if(self.use == target_speaker_use_off)
+                       target_speaker_use_off();
+       }
+}
+
+void spawnfunc_target_speaker()
+{
+       // TODO: "*" prefix to sound file name
+       // TODO: wait and random (just, HOW? random is not a field)
+       if(self.noise)
+               precache_sound (self.noise);
+
+       if(!self.atten && !(self.spawnflags & 4))
+       {
+               IFTARGETED
+                       self.atten = ATTEN_NORM;
+               else
+                       self.atten = ATTEN_STATIC;
+       }
+       else if(self.atten < 0)
+               self.atten = 0;
+
+       if(!self.volume)
+               self.volume = 1;
+
+       IFTARGETED
+       {
+               if(self.spawnflags & 8) // ACTIVATOR
+                       self.use = target_speaker_use_activator;
+               else if(self.spawnflags & 1) // LOOPED_ON
+               {
+                       target_speaker_use_on();
+                       self.reset = target_speaker_reset;
+               }
+               else if(self.spawnflags & 2) // LOOPED_OFF
+               {
+                       self.use = target_speaker_use_on;
+                       self.reset = target_speaker_reset;
+               }
+               else
+                       self.use = target_speaker_use_on;
+       }
+       else if(self.spawnflags & 1) // LOOPED_ON
+       {
+               ambientsound (self.origin, self.noise, VOL_BASE * self.volume, self.atten);
+               remove(self);
+       }
+       else if(self.spawnflags & 2) // LOOPED_OFF
+       {
+               objerror("This sound entity can never be activated");
+       }
+       else
+       {
+               // Quake/Nexuiz fallback
+               ambientsound (self.origin, self.noise, VOL_BASE * self.volume, self.atten);
+               remove(self);
+       }
+}
+#endif
diff --git a/qcsrc/common/triggers/target/voicescript.qc b/qcsrc/common/triggers/target/voicescript.qc
new file mode 100644 (file)
index 0000000..c173d80
--- /dev/null
@@ -0,0 +1,101 @@
+#ifdef SVQC
+.entity voicescript; // attached voice script
+.float voicescript_index; // index of next voice, or -1 to use the randomized ones
+.float voicescript_nextthink; // time to play next voice
+.float voicescript_voiceend; // time when this voice ends
+
+void target_voicescript_clear(entity pl)
+{
+       pl.voicescript = world;
+}
+
+void target_voicescript_use()
+{
+       if(activator.voicescript != self)
+       {
+               activator.voicescript = self;
+               activator.voicescript_index = 0;
+               activator.voicescript_nextthink = time + self.delay;
+       }
+}
+
+void target_voicescript_next(entity pl)
+{
+       entity vs;
+       float i, n, dt;
+
+       vs = pl.voicescript;
+       if(!vs)
+               return;
+       if(vs.message == "")
+               return;
+       if (!IS_PLAYER(pl))
+               return;
+       if(gameover)
+               return;
+
+       if(time >= pl.voicescript_voiceend)
+       {
+               if(time >= pl.voicescript_nextthink)
+               {
+                       // get the next voice...
+                       n = tokenize_console(vs.message);
+
+                       if(pl.voicescript_index < vs.cnt)
+                               i = pl.voicescript_index * 2;
+                       else if(n > vs.cnt * 2)
+                               i = ((pl.voicescript_index - vs.cnt) % ((n - vs.cnt * 2 - 1) / 2)) * 2 + vs.cnt * 2 + 1;
+                       else
+                               i = -1;
+
+                       if(i >= 0)
+                       {
+                               play2(pl, strcat(vs.netname, "/", argv(i), ".wav"));
+                               dt = stof(argv(i + 1));
+                               if(dt >= 0)
+                               {
+                                       pl.voicescript_voiceend = time + dt;
+                                       pl.voicescript_nextthink = pl.voicescript_voiceend + vs.wait * (0.5 + random());
+                               }
+                               else
+                               {
+                                       pl.voicescript_voiceend = time - dt;
+                                       pl.voicescript_nextthink = pl.voicescript_voiceend;
+                               }
+
+                               pl.voicescript_index += 1;
+                       }
+                       else
+                       {
+                               pl.voicescript = world; // stop trying then
+                       }
+               }
+       }
+}
+
+void spawnfunc_target_voicescript()
+{
+       // netname: directory of the sound files
+       // message: list of "sound file" duration "sound file" duration, a *, and again a list
+       //          foo1 4.1 foo2 4.0 foo3 -3.1 * fool1 1.1 fool2 7.1 fool3 9.1 fool4 3.7
+       //          Here, a - in front of the duration means that no delay is to be
+       //          added after this message
+       // wait: average time between messages
+       // delay: initial delay before the first message
+
+       float i, n;
+       self.use = target_voicescript_use;
+
+       n = tokenize_console(self.message);
+       self.cnt = n / 2;
+       for(i = 0; i+1 < n; i += 2)
+       {
+               if(argv(i) == "*")
+               {
+                       self.cnt = i / 2;
+                       ++i;
+               }
+               precache_sound(strcat(self.netname, "/", argv(i), ".wav"));
+       }
+}
+#endif
diff --git a/qcsrc/common/triggers/teleporters.qc b/qcsrc/common/triggers/teleporters.qc
new file mode 100644 (file)
index 0000000..a5d89f5
--- /dev/null
@@ -0,0 +1,253 @@
+#include "teleporters.qh"
+
+#if defined(CSQC)
+#elif defined(MENUQC)
+#elif defined(SVQC)
+       #include "../../server/_all.qh"
+    #include "../../warpzonelib/common.qh"
+    #include "../../warpzonelib/util_server.qh"
+    #include "../../warpzonelib/server.qh"
+    #include "../constants.qh"
+       #include "../triggers/subs.qh"
+    #include "../util.qh"
+    #include "../../server/weapons/csqcprojectile.qh"
+    #include "../../server/autocvars.qh"
+    #include "../../server/constants.qh"
+    #include "../../server/defs.qh"
+    #include "../deathtypes.qh"
+    #include "../../server/tturrets/include/turrets_early.qh"
+    #include "../../server/vehicles/all.qh"
+    #include "../mapinfo.qh"
+    #include "../../server/anticheat.qh"
+#endif
+
+#ifdef SVQC
+
+float check_tdeath(entity player, vector org, vector telefragmin, vector telefragmax)
+{
+       if (IS_PLAYER(player) && player.health >= 1)
+       {
+               TDEATHLOOP(org)
+               {
+                       if (!(teamplay && autocvar_g_telefrags_teamplay && head.team == player.team))
+                               if(IS_PLAYER(head))
+                                       if(head.health >= 1)
+                                               return 1;
+               }
+       }
+       return 0;
+}
+
+void tdeath(entity player, entity teleporter, entity telefragger, vector telefragmin, vector telefragmax)
+{
+       TDEATHLOOP(player.origin)
+       {
+               if (IS_PLAYER(player) && player.health >= 1)
+               {
+                       if (!(teamplay && autocvar_g_telefrags_teamplay && head.team == player.team))
+                       {
+                               if(IS_PLAYER(head))
+                                       if(head.health >= 1)
+                                               ++tdeath_hit;
+                               Damage (head, teleporter, telefragger, 10000, DEATH_TELEFRAG, head.origin, '0 0 0');
+                       }
+               }
+               else // dead bodies and monsters gib themselves instead of telefragging
+                       Damage (telefragger, teleporter, telefragger, 10000, DEATH_TELEFRAG, telefragger.origin, '0 0 0');
+       }
+}
+
+void spawn_tdeath(vector v0, entity e, vector v)
+{
+       tdeath(e, e, e, '0 0 0', '0 0 0');
+}
+
+void TeleportPlayer(entity teleporter, entity player, vector to, vector to_angles, vector to_velocity, vector telefragmin, vector telefragmax, float tflags)
+{
+       entity telefragger;
+       vector from;
+
+       if(teleporter.owner)
+               telefragger = teleporter.owner;
+       else
+               telefragger = player;
+
+       makevectors (to_angles);
+
+       if(player.teleportable == TELEPORT_NORMAL) // don't play sounds or show particles for anything that isn't a player, maybe change later to block only observers
+       {
+               if(self.pushltime < time) // only show one teleport effect per teleporter per 0.2 seconds, for better fps
+               {
+                       if(tflags & TELEPORT_FLAG_SOUND)
+                               sound (player, CH_TRIGGER, "misc/teleport.wav", VOL_BASE, ATTEN_NORM);
+                       if(tflags & TELEPORT_FLAG_PARTICLES)
+                       {
+                               pointparticles(particleeffectnum("teleport"), player.origin, '0 0 0', 1);
+                               pointparticles(particleeffectnum("teleport"), to + v_forward * 32, '0 0 0', 1);
+                       }
+                       self.pushltime = time + 0.2;
+               }
+       }
+
+       // Relocate the player
+       // assuming to allows PL_MIN to PL_MAX box and some more
+       from = player.origin;
+       setorigin (player, to);
+       player.oldorigin = to; // don't undo the teleport by unsticking
+       player.angles = to_angles;
+       player.fixangle = true;
+       player.velocity = to_velocity;
+       BITXOR_ASSIGN(player.effects, EF_TELEPORT_BIT);
+
+       makevectors(player.angles);
+       Reset_ArcBeam(player, v_forward);
+       UpdateCSQCProjectileAfterTeleport(player);
+
+       if(IS_PLAYER(player))
+       {
+               if(tflags & TELEPORT_FLAG_TDEATH)
+                       if(player.takedamage && player.deadflag == DEAD_NO && !g_race && !g_cts && (autocvar_g_telefrags || (tflags & TELEPORT_FLAG_FORCE_TDEATH)))
+                               tdeath(player, teleporter, telefragger, telefragmin, telefragmax);
+
+               // player no longer is on ground
+               player.flags &= ~FL_ONGROUND;
+
+               // reset tracking of oldvelocity for impact damage (sudden velocity changes)
+               player.oldvelocity = player.velocity;
+
+               // reset tracking of who pushed you into a hazard (for kill credit)
+               if(teleporter.owner)
+               {
+                       player.pusher = teleporter.owner;
+                       player.pushltime = time + autocvar_g_maxpushtime;
+                       player.istypefrag = player.BUTTON_CHAT;
+               }
+               else
+               {
+                       player.pushltime = 0;
+                       player.istypefrag = 0;
+               }
+
+               player.lastteleporttime = time;
+       }
+}
+
+entity Simple_TeleportPlayer(entity teleporter, entity player)
+{
+       vector locout;
+       entity e;
+       float p;
+
+       // Find the output teleporter
+       if(teleporter.enemy)
+       {
+               e = teleporter.enemy;
+       }
+       else
+       {
+               RandomSelection_Init();
+               for(e = world; (e = find(e, targetname, teleporter.target)); )
+               {
+                       p = 1;
+                       if(autocvar_g_telefrags_avoid)
+                       {
+                               locout = e.origin + '0 0 1' * (1 - player.mins.z - 24);
+                               if(check_tdeath(player, locout, '0 0 0', '0 0 0'))
+                                       p = 0;
+                       }
+                       RandomSelection_Add(e, 0, string_null, (e.cnt ? e.cnt : 1), p);
+               }
+               e = RandomSelection_chosen_ent;
+       }
+
+       if(!e) { sprint(player, "Teleport destination vanished. Sorry... please complain to the mapper.\n"); }
+
+       makevectors(e.mangle);
+
+       if(e.speed)
+               if(vlen(player.velocity) > e.speed)
+                       player.velocity = normalize(player.velocity) * max(0, e.speed);
+
+       if(autocvar_g_teleport_maxspeed)
+               if(vlen(player.velocity) > autocvar_g_teleport_maxspeed)
+                       player.velocity = normalize(player.velocity) * max(0, autocvar_g_teleport_maxspeed);
+
+       locout = e.origin + '0 0 1' * (1 - player.mins.z - 24);
+       TeleportPlayer(teleporter, player, locout, e.mangle, v_forward * vlen(player.velocity), '0 0 0', '0 0 0', TELEPORT_FLAGS_TELEPORTER);
+
+       return e;
+}
+
+void teleport_findtarget (void)
+{
+       entity e;
+       float n;
+
+       n = 0;
+       for(e = world; (e = find(e, targetname, self.target)); )
+       {
+               ++n;
+               if(e.movetype == MOVETYPE_NONE)
+                       waypoint_spawnforteleporter(self, e.origin, 0);
+               if(e.classname != "info_teleport_destination")
+                       print("^3MAPPER ERROR: teleporter does target an invalid teleport destination entity. Angles will not work.\n");
+       }
+
+       if(n == 0)
+       {
+               // no dest!
+               objerror ("Teleporter with nonexistant target");
+               return;
+       }
+       else if(n == 1)
+       {
+               // exactly one dest - bots love that
+               self.enemy = find(e, targetname, self.target);
+       }
+       else
+       {
+               // have to use random selection every single time
+               self.enemy = world;
+       }
+
+       // now enable touch
+       self.touch = Teleport_Touch;
+}
+
+entity Teleport_Find(vector mi, vector ma)
+{
+       entity e;
+       for(e = world; (e = find(e, classname, "trigger_teleport")); )
+               if(WarpZoneLib_BoxTouchesBrush(mi, ma, e, world))
+                       return e;
+       return world;
+}
+
+void WarpZone_PostTeleportPlayer_Callback(entity pl)
+{
+       makevectors(pl.angles);
+       Reset_ArcBeam(pl, v_forward);
+       UpdateCSQCProjectileAfterTeleport(pl);
+       {
+               entity oldself = self;
+               self = pl;
+               anticheat_fixangle();
+               self = oldself;
+       }
+       // "disown" projectiles after teleport
+       if(pl.owner)
+       if(pl.owner == pl.realowner)
+       {
+               if(!(pl.flags & FL_PROJECTILE))
+                       print("A non-projectile got through a warpzone and its owner cleared. It's a ", pl.classname, ".\n");
+               pl.owner = world;
+       }
+       if(IS_PLAYER(pl))
+       {
+               // reset tracking of oldvelocity for impact damage (sudden velocity changes)
+               pl.oldvelocity = pl.velocity;
+               // reset teleport time tracking too (or multijump can cause insane speeds)
+               pl.lastteleporttime = time;
+       }
+}
+#endif
diff --git a/qcsrc/common/triggers/teleporters.qh b/qcsrc/common/triggers/teleporters.qh
new file mode 100644 (file)
index 0000000..bdd24dd
--- /dev/null
@@ -0,0 +1,68 @@
+#ifndef T_TELEPORTERS_H
+#define T_TELEPORTERS_H
+
+#ifdef SVQC
+
+void trigger_teleport_use();
+
+#define TDEATHLOOP(o) \
+       entity head; \
+       vector deathmin; \
+       vector deathmax; \
+       float deathradius; \
+       deathmin = (o) + player.mins; \
+       deathmax = (o) + player.maxs; \
+       if(telefragmin != telefragmax) \
+       { \
+               if(deathmin.x > telefragmin.x) deathmin.x = telefragmin.x; \
+               if(deathmin.y > telefragmin.y) deathmin.y = telefragmin.y; \
+               if(deathmin.z > telefragmin.z) deathmin.z = telefragmin.z; \
+               if(deathmax.x < telefragmax.x) deathmax.x = telefragmax.x; \
+               if(deathmax.y < telefragmax.y) deathmax.y = telefragmax.y; \
+               if(deathmax.z < telefragmax.z) deathmax.z = telefragmax.z; \
+       } \
+       deathradius = max(vlen(deathmin), vlen(deathmax)); \
+       for(head = findradius(o, deathradius); head; head = head.chain) \
+               if(head != player) \
+                       if(head.takedamage) \
+                               if(boxesoverlap(deathmin, deathmax, head.absmin, head.absmax))
+
+
+float check_tdeath(entity player, vector org, vector telefragmin, vector telefragmax);
+float tdeath_hit;
+void tdeath(entity player, entity teleporter, entity telefragger, vector telefragmin, vector telefragmax);
+
+void spawn_tdeath(vector v0, entity e, vector v);
+
+.entity pusher;
+const float TELEPORT_FLAG_SOUND = 1;
+const float TELEPORT_FLAG_PARTICLES = 2;
+const float TELEPORT_FLAG_TDEATH = 4;
+const float TELEPORT_FLAG_FORCE_TDEATH = 8;
+
+#define TELEPORT_FLAGS_WARPZONE   0
+#define TELEPORT_FLAGS_PORTAL     (TELEPORT_FLAG_SOUND | TELEPORT_FLAG_PARTICLES | TELEPORT_FLAG_TDEATH | TELEPORT_FLAG_FORCE_TDEATH)
+#define TELEPORT_FLAGS_TELEPORTER (TELEPORT_FLAG_SOUND | TELEPORT_FLAG_PARTICLES | TELEPORT_FLAG_TDEATH)
+
+// types for .teleportable entity setting
+const float TELEPORT_NORMAL = 1; // play sounds/effects etc
+const float TELEPORT_SIMPLE = 2; // only do teleport, nothing special
+
+void Reset_ArcBeam(entity player, vector forward);
+void TeleportPlayer(entity teleporter, entity player, vector to, vector to_angles, vector to_velocity, vector telefragmin, vector telefragmax, float tflags);
+
+entity Simple_TeleportPlayer(entity teleporter, entity player);
+
+void Teleport_Touch (void);
+
+void teleport_findtarget (void);
+
+entity Teleport_Find(vector mi, vector ma);
+
+entity teleport_first;
+.entity teleport_next;
+
+void WarpZone_PostTeleportPlayer_Callback(entity pl);
+#endif
+
+#endif
diff --git a/qcsrc/common/triggers/trigger/counter.qc b/qcsrc/common/triggers/trigger/counter.qc
new file mode 100644 (file)
index 0000000..bf1d9b2
--- /dev/null
@@ -0,0 +1,49 @@
+#ifdef SVQC
+void counter_use()
+{
+       self.count -= 1;
+       if (self.count < 0)
+               return;
+
+       if (self.count == 0)
+       {
+               if(IS_PLAYER(activator) && (self.spawnflags & SPAWNFLAG_NOMESSAGE) == 0)
+                       Send_Notification(NOTIF_ONE, activator, MSG_CENTER, CENTER_SEQUENCE_COMPLETED);
+
+               self.enemy = activator;
+               multi_trigger ();
+       }
+       else
+       {
+               if(IS_PLAYER(activator) && (self.spawnflags & SPAWNFLAG_NOMESSAGE) == 0)
+               if(self.count >= 4)
+                       Send_Notification(NOTIF_ONE, activator, MSG_CENTER, CENTER_SEQUENCE_COUNTER);
+               else
+                       Send_Notification(NOTIF_ONE, activator, MSG_CENTER, CENTER_SEQUENCE_COUNTER_FEWMORE, self.count);
+       }
+}
+
+void counter_reset()
+{
+       self.count = self.cnt;
+       multi_reset();
+}
+
+/*QUAKED spawnfunc_trigger_counter (.5 .5 .5) ? nomessage
+Acts as an intermediary for an action that takes multiple inputs.
+
+If nomessage is not set, t will print "1 more.. " etc when triggered and "sequence complete" when finished.
+
+After the counter has been triggered "count" times (default 2), it will fire all of it's targets and remove itself.
+*/
+void spawnfunc_trigger_counter()
+{
+       self.wait = -1;
+       if (!self.count)
+               self.count = 2;
+       self.cnt = self.count;
+
+       self.use = counter_use;
+       self.reset = counter_reset;
+}
+#endif
diff --git a/qcsrc/common/triggers/trigger/delay.qc b/qcsrc/common/triggers/trigger/delay.qc
new file mode 100644 (file)
index 0000000..b01efe3
--- /dev/null
@@ -0,0 +1,22 @@
+#ifdef SVQC
+void delay_use()
+{
+    self.think = SUB_UseTargets;
+   self.nextthink = self.wait;
+}
+
+void delay_reset()
+{
+       self.think = func_null;
+       self.nextthink = 0;
+}
+
+void spawnfunc_trigger_delay()
+{
+    if(!self.wait)
+        self.wait = 1;
+
+    self.use = delay_use;
+    self.reset = delay_reset;
+}
+#endif
diff --git a/qcsrc/common/triggers/trigger/disablerelay.qc b/qcsrc/common/triggers/trigger/disablerelay.qc
new file mode 100644 (file)
index 0000000..cd5fdff
--- /dev/null
@@ -0,0 +1,31 @@
+#ifdef SVQC
+void trigger_disablerelay_use()
+{
+       entity e;
+
+       float a, b;
+       a = b = 0;
+
+       for(e = world; (e = find(e, targetname, self.target)); )
+       {
+               if(e.use == SUB_UseTargets)
+               {
+                       e.use = SUB_DontUseTargets;
+                       ++a;
+               }
+               else if(e.use == SUB_DontUseTargets)
+               {
+                       e.use = SUB_UseTargets;
+                       ++b;
+               }
+       }
+
+       if((!a) == (!b))
+               print("Invalid use of trigger_disablerelay: ", ftos(a), " relays were on, ", ftos(b), " relays were off!\n");
+}
+
+void spawnfunc_trigger_disablerelay()
+{
+       self.use = trigger_disablerelay_use;
+}
+#endif
diff --git a/qcsrc/common/triggers/trigger/flipflop.qc b/qcsrc/common/triggers/trigger/flipflop.qc
new file mode 100644 (file)
index 0000000..12d8a59
--- /dev/null
@@ -0,0 +1,19 @@
+#ifdef SVQC
+/*QUAKED spawnfunc_trigger_flipflop (.5 .5 .5) (-8 -8 -8) (8 8 8) START_ENABLED
+"Flip-flop" trigger gate... lets only every second trigger event through
+*/
+void flipflop_use()
+{
+    self.state = !self.state;
+    if(self.state)
+        SUB_UseTargets();
+}
+
+void spawnfunc_trigger_flipflop()
+{
+    if(self.spawnflags & 1)
+        self.state = 1;
+    self.use = flipflop_use;
+    self.reset = spawnfunc_trigger_flipflop; // perfect resetter
+}
+#endif
diff --git a/qcsrc/common/triggers/trigger/gamestart.qc b/qcsrc/common/triggers/trigger/gamestart.qc
new file mode 100644 (file)
index 0000000..3ad419d
--- /dev/null
@@ -0,0 +1,22 @@
+#ifdef SVQC
+void gamestart_use()
+{
+       activator = self;
+       SUB_UseTargets();
+       remove(self);
+}
+
+void spawnfunc_trigger_gamestart()
+{
+       self.use = gamestart_use;
+       self.reset2 = spawnfunc_trigger_gamestart;
+
+       if(self.wait)
+       {
+               self.think = self.use;
+               self.nextthink = game_starttime + self.wait;
+       }
+       else
+               InitializeEntity(self, gamestart_use, INITPRIO_FINDTARGET);
+}
+#endif
diff --git a/qcsrc/common/triggers/trigger/gravity.qc b/qcsrc/common/triggers/trigger/gravity.qc
new file mode 100644 (file)
index 0000000..a709a1f
--- /dev/null
@@ -0,0 +1,106 @@
+#ifdef SVQC
+.entity trigger_gravity_check;
+void trigger_gravity_remove(entity own)
+{
+       if(own.trigger_gravity_check.owner == own)
+       {
+               UpdateCSQCProjectile(own);
+               own.gravity = own.trigger_gravity_check.gravity;
+               remove(own.trigger_gravity_check);
+       }
+       else
+               backtrace("Removing a trigger_gravity_check with no valid owner");
+       own.trigger_gravity_check = world;
+}
+void trigger_gravity_check_think()
+{
+       // This spawns when a player enters the gravity zone and checks if he left.
+       // Each frame, self.count is set to 2 by trigger_gravity_touch() and decreased by 1 here.
+       // It the player has left the gravity trigger, this will be allowed to reach 0 and indicate that.
+       if(self.count <= 0)
+       {
+               if(self.owner.trigger_gravity_check == self)
+                       trigger_gravity_remove(self.owner);
+               else
+                       remove(self);
+               return;
+       }
+       else
+       {
+               self.count -= 1;
+               self.nextthink = time;
+       }
+}
+
+void trigger_gravity_use()
+{
+       self.state = !self.state;
+}
+
+void trigger_gravity_touch()
+{
+       float g;
+
+       if(self.state != true)
+               return;
+
+       EXACTTRIGGER_TOUCH;
+
+       g = self.gravity;
+
+       if (!(self.spawnflags & 1))
+       {
+               if(other.trigger_gravity_check)
+               {
+                       if(self == other.trigger_gravity_check.enemy)
+                       {
+                               // same?
+                               other.trigger_gravity_check.count = 2; // gravity one more frame...
+                               return;
+                       }
+
+                       // compare prio
+                       if(self.cnt > other.trigger_gravity_check.enemy.cnt)
+                               trigger_gravity_remove(other);
+                       else
+                               return;
+               }
+               other.trigger_gravity_check = spawn();
+               other.trigger_gravity_check.enemy = self;
+               other.trigger_gravity_check.owner = other;
+               other.trigger_gravity_check.gravity = other.gravity;
+               other.trigger_gravity_check.think = trigger_gravity_check_think;
+               other.trigger_gravity_check.nextthink = time;
+               other.trigger_gravity_check.count = 2;
+               if(other.gravity)
+                       g *= other.gravity;
+       }
+
+       if (other.gravity != g)
+       {
+               other.gravity = g;
+               if(self.noise != "")
+                       sound (other, CH_TRIGGER, self.noise, VOL_BASE, ATTEN_NORM);
+               UpdateCSQCProjectile(self.owner);
+       }
+}
+
+void spawnfunc_trigger_gravity()
+{
+       if(self.gravity == 1)
+               return;
+
+       EXACTTRIGGER_INIT;
+       self.touch = trigger_gravity_touch;
+       if(self.noise != "")
+               precache_sound(self.noise);
+
+       self.state = true;
+       IFTARGETED
+       {
+               self.use = trigger_gravity_use;
+               if(self.spawnflags & 2)
+                       self.state = false;
+       }
+}
+#endif
diff --git a/qcsrc/common/triggers/trigger/heal.qc b/qcsrc/common/triggers/trigger/heal.qc
new file mode 100644 (file)
index 0000000..6d68610
--- /dev/null
@@ -0,0 +1,42 @@
+#ifdef SVQC
+.float triggerhealtime;
+void trigger_heal_touch()
+{
+       if (self.active != ACTIVE_ACTIVE)
+               return;
+
+       // only do the EXACTTRIGGER_TOUCH checks when really needed (saves some cpu)
+       if (other.iscreature)
+       {
+               if (other.takedamage)
+               if (!other.deadflag)
+               if (other.triggerhealtime < time)
+               {
+                       EXACTTRIGGER_TOUCH;
+                       other.triggerhealtime = time + 1;
+
+                       if (other.health < self.max_health)
+                       {
+                               other.health = min(other.health + self.health, self.max_health);
+                               other.pauserothealth_finished = max(other.pauserothealth_finished, time + autocvar_g_balance_pause_health_rot);
+                               sound (other, CH_TRIGGER, self.noise, VOL_BASE, ATTEN_NORM);
+                       }
+               }
+       }
+}
+
+void spawnfunc_trigger_heal()
+{
+       self.active = ACTIVE_ACTIVE;
+
+       EXACTTRIGGER_INIT;
+       self.touch = trigger_heal_touch;
+       if (!self.health)
+               self.health = 10;
+       if (!self.max_health)
+               self.max_health = 200; //Max health topoff for field
+       if(self.noise == "")
+               self.noise = "misc/mediumhealth.wav";
+       precache_sound(self.noise);
+}
+#endif
diff --git a/qcsrc/common/triggers/trigger/hurt.qc b/qcsrc/common/triggers/trigger/hurt.qc
new file mode 100644 (file)
index 0000000..3a15a77
--- /dev/null
@@ -0,0 +1,92 @@
+#ifdef SVQC
+void trigger_hurt_use()
+{
+       if(IS_PLAYER(activator))
+               self.enemy = activator;
+       else
+               self.enemy = world; // let's just destroy it, if taking over is too much work
+}
+
+.float triggerhurttime;
+void trigger_hurt_touch()
+{
+       if (self.active != ACTIVE_ACTIVE)
+               return;
+
+       if(self.team)
+               if(((self.spawnflags & 4) == 0) == (self.team != other.team))
+                       return;
+
+       // only do the EXACTTRIGGER_TOUCH checks when really needed (saves some cpu)
+       if (other.iscreature)
+       {
+               if (other.takedamage)
+               if (other.triggerhurttime < time)
+               {
+                       EXACTTRIGGER_TOUCH;
+                       other.triggerhurttime = time + 1;
+
+                       entity own;
+                       own = self.enemy;
+                       if (!IS_PLAYER(own))
+                       {
+                               own = self;
+                               self.enemy = world; // I still hate you all
+                       }
+
+                       Damage (other, self, own, self.dmg, DEATH_HURTTRIGGER, other.origin, '0 0 0');
+               }
+       }
+       else if(other.damagedbytriggers)
+       {
+               if(other.takedamage)
+               {
+                       EXACTTRIGGER_TOUCH;
+                       Damage(other, self, self, self.dmg, DEATH_HURTTRIGGER, other.origin, '0 0 0');
+               }
+       }
+
+       return;
+}
+
+/*QUAKED spawnfunc_trigger_hurt (.5 .5 .5) ?
+Any object touching this will be hurt
+set dmg to damage amount
+defalt dmg = 5
+*/
+.entity trigger_hurt_next;
+entity trigger_hurt_last;
+entity trigger_hurt_first;
+void spawnfunc_trigger_hurt()
+{
+       EXACTTRIGGER_INIT;
+       self.active = ACTIVE_ACTIVE;
+       self.touch = trigger_hurt_touch;
+       self.use = trigger_hurt_use;
+       self.enemy = world; // I hate you all
+       if (!self.dmg)
+               self.dmg = 1000;
+       if (self.message == "")
+               self.message = "was in the wrong place";
+       if (self.message2 == "")
+               self.message2 = "was thrown into a world of hurt by";
+       // self.message = "someone like %s always gets wrongplaced";
+
+       if(!trigger_hurt_first)
+               trigger_hurt_first = self;
+       if(trigger_hurt_last)
+               trigger_hurt_last.trigger_hurt_next = self;
+       trigger_hurt_last = self;
+}
+
+float tracebox_hits_trigger_hurt(vector start, vector mi, vector ma, vector end)
+{
+       entity th;
+
+       for(th = trigger_hurt_first; th; th = th.trigger_hurt_next)
+               if(tracebox_hits_box(start, mi, ma, end, th.absmin, th.absmax))
+                       return true;
+
+       return false;
+}
+#endif
diff --git a/qcsrc/common/triggers/trigger/impulse.qc b/qcsrc/common/triggers/trigger/impulse.qc
new file mode 100644 (file)
index 0000000..926268e
--- /dev/null
@@ -0,0 +1,194 @@
+// targeted (directional) mode
+void trigger_impulse_touch1()
+{
+       entity targ;
+       float pushdeltatime;
+       float str;
+
+       if (self.active != ACTIVE_ACTIVE)
+               return;
+
+       if (!isPushable(other))
+               return;
+
+       EXACTTRIGGER_TOUCH;
+
+       targ = find(world, targetname, self.target);
+       if(!targ)
+       {
+               objerror("trigger_force without a (valid) .target!\n");
+               remove(self);
+               return;
+       }
+
+       str = min(self.radius, vlen(self.origin - other.origin));
+
+       if(self.falloff == 1)
+               str = (str / self.radius) * self.strength;
+       else if(self.falloff == 2)
+               str = (1 - (str / self.radius)) * self.strength;
+       else
+               str = self.strength;
+
+       pushdeltatime = time - other.lastpushtime;
+       if (pushdeltatime > 0.15) pushdeltatime = 0;
+       other.lastpushtime = time;
+       if(!pushdeltatime) return;
+
+       other.velocity = other.velocity + normalize(targ.origin - self.origin) * str * pushdeltatime;
+       other.flags &= ~FL_ONGROUND;
+#ifdef SVQC
+       UpdateCSQCProjectile(other);
+#endif
+}
+
+// Directionless (accelerator/decelerator) mode
+void trigger_impulse_touch2()
+{
+       float pushdeltatime;
+
+       if (self.active != ACTIVE_ACTIVE)
+               return;
+
+       if (!isPushable(other))
+               return;
+
+       EXACTTRIGGER_TOUCH;
+
+       pushdeltatime = time - other.lastpushtime;
+       if (pushdeltatime > 0.15) pushdeltatime = 0;
+       other.lastpushtime = time;
+       if(!pushdeltatime) return;
+
+       // div0: ticrate independent, 1 = identity (not 20)
+       other.velocity = other.velocity * pow(self.strength, pushdeltatime);
+#ifdef SVQC
+       UpdateCSQCProjectile(other);
+#endif
+}
+
+// Spherical (gravity/repulsor) mode
+void trigger_impulse_touch3()
+{
+       float pushdeltatime;
+       float str;
+
+       if (self.active != ACTIVE_ACTIVE)
+               return;
+
+       if (!isPushable(other))
+               return;
+
+       EXACTTRIGGER_TOUCH;
+
+       pushdeltatime = time - other.lastpushtime;
+       if (pushdeltatime > 0.15) pushdeltatime = 0;
+       other.lastpushtime = time;
+       if(!pushdeltatime) return;
+
+       setsize(self, '-1 -1 -1' * self.radius,'1 1 1' * self.radius);
+
+       str = min(self.radius, vlen(self.origin - other.origin));
+
+       if(self.falloff == 1)
+               str = (1 - str / self.radius) * self.strength; // 1 in the inside
+       else if(self.falloff == 2)
+               str = (str / self.radius) * self.strength; // 0 in the inside
+       else
+               str = self.strength;
+
+       other.velocity = other.velocity + normalize(other.origin - self.origin) * str * pushdeltatime;
+#ifdef SVQC
+       UpdateCSQCProjectile(other);
+#endif
+}
+
+/*QUAKED spawnfunc_trigger_impulse (.5 .5 .5) ?
+-------- KEYS --------
+target : If this is set, this points to the spawnfunc_target_position to which the player will get pushed.
+                If not, this trigger acts like a damper/accelerator field.
+
+strength : This is how mutch force to add in the direction of .target each second
+                  when .target is set. If not, this is hoe mutch to slow down/accelerate
+                  someting cought inside this trigger. (1=no change, 0,5 half speed rougthly each tic, 2 = doubble)
+
+radius   : If set, act as a spherical device rather then a liniar one.
+
+falloff : 0 = none, 1 = liniar, 2 = inverted liniar
+
+-------- NOTES --------
+Use a brush textured with common/origin in the trigger entity to determine the origin of the force
+in directional and sperical mode. For damper/accelerator mode this is not nessesary (and has no effect).
+*/
+#ifdef SVQC
+bool trigger_impulse_send(entity to, int sf)
+{
+       WriteByte(MSG_ENTITY, ENT_CLIENT_TRIGGER_IMPULSE);
+
+       WriteCoord(MSG_ENTITY, self.radius);
+       WriteCoord(MSG_ENTITY, self.strength);
+       WriteByte(MSG_ENTITY, self.falloff);
+       WriteByte(MSG_ENTITY, self.active);
+
+       trigger_common_write(true);
+
+       return true;
+}
+
+void trigger_impulse_link()
+{
+       //Net_LinkEntity(self, 0, false, trigger_impulse_send);
+}
+
+void spawnfunc_trigger_impulse()
+{
+       self.active = ACTIVE_ACTIVE;
+
+       EXACTTRIGGER_INIT;
+       if(self.radius)
+       {
+               if(!self.strength) self.strength = 2000 * autocvar_g_triggerimpulse_radial_multiplier;
+               setorigin(self, self.origin);
+               setsize(self, '-1 -1 -1' * self.radius,'1 1 1' * self.radius);
+               self.touch = trigger_impulse_touch3;
+       }
+       else
+       {
+               if(self.target)
+               {
+                       if(!self.strength) self.strength = 950 * autocvar_g_triggerimpulse_directional_multiplier;
+                       self.touch = trigger_impulse_touch1;
+               }
+               else
+               {
+                       if(!self.strength) self.strength = 0.9;
+                       self.strength = pow(self.strength, autocvar_g_triggerimpulse_accel_power) * autocvar_g_triggerimpulse_accel_multiplier;
+                       self.touch = trigger_impulse_touch2;
+               }
+       }
+
+       trigger_impulse_link();
+}
+#elif defined(CSQC)
+void ent_trigger_impulse()
+{
+       self.radius = ReadCoord();
+       self.strength = ReadCoord();
+       self.falloff = ReadByte();
+       self.active = ReadByte();
+
+       trigger_common_read(true);
+
+
+       self.classname = "trigger_impulse";
+       self.solid = SOLID_TRIGGER;
+       self.entremove = trigger_remove_generic;
+       self.draw = trigger_draw_generic;
+       self.drawmask = MASK_NORMAL;
+       self.move_time = time;
+
+       if(self.radius) { self.trigger_touch = trigger_impulse_touch3; }
+       else if(self.target) { self.trigger_touch = trigger_impulse_touch1; }
+       else { self.trigger_touch = trigger_impulse_touch2; }
+}
+#endif
diff --git a/qcsrc/common/triggers/trigger/impulse.qh b/qcsrc/common/triggers/trigger/impulse.qh
new file mode 100644 (file)
index 0000000..a4c248d
--- /dev/null
@@ -0,0 +1,14 @@
+#ifndef TRIGGER_IMPULSE_H
+#define TRIGGER_IMPULSE_H
+
+// tZorks trigger impulse / gravity
+.float radius;
+.float falloff;
+.float strength;
+.float lastpushtime;
+
+#ifdef CSQC
+void ent_trigger_impulse();
+#endif
+
+#endif
diff --git a/qcsrc/common/triggers/trigger/include.qc b/qcsrc/common/triggers/trigger/include.qc
new file mode 100644 (file)
index 0000000..1498634
--- /dev/null
@@ -0,0 +1,24 @@
+#include "include.qh"
+
+#include "counter.qc"
+#include "delay.qc"
+#include "disablerelay.qc"
+#include "flipflop.qc"
+#include "gamestart.qc"
+#include "gravity.qc"
+#include "heal.qc"
+#include "hurt.qc"
+#include "impulse.qc"
+#include "jumppads.qc"
+#include "keylock.qc"
+#include "magicear.qc"
+#include "monoflop.qc"
+#include "multi.qc"
+#include "multivibrator.qc"
+#include "relay.qc"
+#include "relay_activators.qc"
+#include "relay_if.qc"
+#include "relay_teamcheck.qc"
+#include "secret.qc"
+#include "swamp.qc"
+#include "teleport.qc"
diff --git a/qcsrc/common/triggers/trigger/include.qh b/qcsrc/common/triggers/trigger/include.qh
new file mode 100644 (file)
index 0000000..1601143
--- /dev/null
@@ -0,0 +1,11 @@
+#ifndef TRIGGERS_TRIGGER_INCLUDE_H
+#define TRIGGERS_TRIGGER_INCLUDE_H
+
+#include "multi.qh"
+#include "jumppads.qh"
+#include "secret.qh"
+#include "swamp.qh"
+#include "keylock.qh"
+#include "impulse.qh"
+
+#endif
diff --git a/qcsrc/common/triggers/trigger/jumppads.qc b/qcsrc/common/triggers/trigger/jumppads.qc
new file mode 100644 (file)
index 0000000..6c6675e
--- /dev/null
@@ -0,0 +1,475 @@
+// TODO: split target_push and put it in the target folder
+#ifdef SVQC
+#include "../../../server/_all.qh"
+#include "jumppads.qh"
+#include "../../movetypes/movetypes.qh"
+
+void trigger_push_use()
+{
+       if(teamplay)
+       {
+               self.team = activator.team;
+               self.SendFlags |= 2;
+       }
+}
+#endif
+
+/*
+       trigger_push_calculatevelocity
+
+       Arguments:
+         org - origin of the object which is to be pushed
+         tgt - target entity (can be either a point or a model entity; if it is
+               the latter, its midpoint is used)
+         ht  - jump height, measured from the higher one of org and tgt's midpoint
+
+       Returns: velocity for the jump
+       the global trigger_push_calculatevelocity_flighttime is set to the total
+       jump time
+ */
+
+vector trigger_push_calculatevelocity(vector org, entity tgt, float ht)
+{
+       float grav, sdist, zdist, vs, vz, jumpheight;
+       vector sdir, torg;
+
+       torg = tgt.origin + (tgt.mins + tgt.maxs) * 0.5;
+
+       grav = PHYS_GRAVITY;
+       if(PHYS_ENTGRAVITY(other))
+               grav *= PHYS_ENTGRAVITY(other);
+
+       zdist = torg.z - org.z;
+       sdist = vlen(torg - org - zdist * '0 0 1');
+       sdir = normalize(torg - org - zdist * '0 0 1');
+
+       // how high do we need to push the player?
+       jumpheight = fabs(ht);
+       if(zdist > 0)
+               jumpheight = jumpheight + zdist;
+
+       /*
+               STOP.
+
+               You will not understand the following equations anyway...
+               But here is what I did to get them.
+
+               I used the functions
+
+                 s(t) = t * vs
+                 z(t) = t * vz - 1/2 grav t^2
+
+               and solved for:
+
+                 s(ti) = sdist
+                 z(ti) = zdist
+                 max(z, ti) = jumpheight
+
+               From these three equations, you will find the three parameters vs, vz
+               and ti.
+        */
+
+       // push him so high...
+       vz = sqrt(fabs(2 * grav * jumpheight)); // NOTE: sqrt(positive)!
+
+       // we start with downwards velocity only if it's a downjump and the jump apex should be outside the jump!
+       if(ht < 0)
+               if(zdist < 0)
+                       vz = -vz;
+
+       vector solution;
+       solution = solve_quadratic(0.5 * grav, -vz, zdist); // equation "z(ti) = zdist"
+       // ALWAYS solvable because jumpheight >= zdist
+       if(!solution.z)
+               solution_y = solution.x; // just in case it is not solvable due to roundoff errors, assume two equal solutions at their center (this is mainly for the usual case with ht == 0)
+       if(zdist == 0)
+               solution_x = solution.y; // solution_x is 0 in this case, so don't use it, but rather use solution_y (which will be sqrt(0.5 * jumpheight / grav), actually)
+
+       if(zdist < 0)
+       {
+               // down-jump
+               if(ht < 0)
+               {
+                       // almost straight line type
+                       // jump apex is before the jump
+                       // we must take the larger one
+                       trigger_push_calculatevelocity_flighttime = solution.y;
+               }
+               else
+               {
+                       // regular jump
+                       // jump apex is during the jump
+                       // we must take the larger one too
+                       trigger_push_calculatevelocity_flighttime = solution.y;
+               }
+       }
+       else
+       {
+               // up-jump
+               if(ht < 0)
+               {
+                       // almost straight line type
+                       // jump apex is after the jump
+                       // we must take the smaller one
+                       trigger_push_calculatevelocity_flighttime = solution.x;
+               }
+               else
+               {
+                       // regular jump
+                       // jump apex is during the jump
+                       // we must take the larger one
+                       trigger_push_calculatevelocity_flighttime = solution.y;
+               }
+       }
+       vs = sdist / trigger_push_calculatevelocity_flighttime;
+
+       // finally calculate the velocity
+       return sdir * vs + '0 0 1' * vz;
+}
+
+void trigger_push_touch()
+{
+       if (self.active == ACTIVE_NOT)
+               return;
+
+#ifdef SVQC
+       if (!isPushable(other))
+               return;
+#endif
+
+       if(self.team)
+               if(((self.spawnflags & 4) == 0) == (DIFF_TEAM(self, other)))
+                       return;
+
+       EXACTTRIGGER_TOUCH;
+
+       if(self.enemy)
+       {
+               other.velocity = trigger_push_calculatevelocity(other.origin, self.enemy, self.height);
+               other.move_velocity = other.velocity;
+       }
+       else if(self.target)
+       {
+               entity e;
+               RandomSelection_Init();
+               for(e = world; (e = find(e, targetname, self.target)); )
+               {
+                       if(e.cnt)
+                               RandomSelection_Add(e, 0, string_null, e.cnt, 1);
+                       else
+                               RandomSelection_Add(e, 0, string_null, 1, 1);
+               }
+               other.velocity = trigger_push_calculatevelocity(other.origin, RandomSelection_chosen_ent, self.height);
+               other.move_velocity = other.velocity;
+       }
+       else
+       {
+               other.velocity = self.movedir;
+               other.move_velocity = other.velocity;
+       }
+
+       UNSET_ONGROUND(other);
+
+       other.move_flags &= ~FL_ONGROUND;
+
+#ifdef SVQC
+       if (IS_PLAYER(other))
+       {
+               // reset tracking of oldvelocity for impact damage (sudden velocity changes)
+               other.oldvelocity = other.velocity;
+
+               if(self.pushltime < time)  // prevent "snorring" sound when a player hits the jumppad more than once
+               {
+                       // flash when activated
+                       pointparticles(particleeffectnum("jumppad_activate"), other.origin, other.velocity, 1);
+                       sound (other, CH_TRIGGER, self.noise, VOL_BASE, ATTEN_NORM);
+                       self.pushltime = time + 0.2;
+               }
+               if(IS_REAL_CLIENT(other) || IS_BOT_CLIENT(other))
+               {
+                       bool found = false;
+                       for(int i = 0; i < other.jumppadcount && i < NUM_JUMPPADSUSED; ++i)
+                               if(other.(jumppadsused[i]) == self)
+                                       found = true;
+                       if(!found)
+                       {
+                               other.(jumppadsused[other.jumppadcount % NUM_JUMPPADSUSED]) = self;
+                               other.jumppadcount = other.jumppadcount + 1;
+                       }
+
+                       if(IS_REAL_CLIENT(other))
+                       {
+                               if(self.message)
+                                       centerprint(other, self.message);
+                       }
+                       else
+                               other.lastteleporttime = time;
+
+                       if (other.deadflag == DEAD_NO)
+                               animdecide_setaction(other, ANIMACTION_JUMP, true);
+               }
+               else
+                       other.jumppadcount = true;
+
+               // reset tracking of who pushed you into a hazard (for kill credit)
+               other.pushltime = 0;
+               other.istypefrag = 0;
+       }
+
+       if(self.enemy.target)
+       {
+               entity oldself;
+               oldself = self;
+               activator = other;
+               self = self.enemy;
+               SUB_UseTargets();
+               self = oldself;
+       }
+
+       if (other.flags & FL_PROJECTILE)
+       {
+               other.angles = vectoangles (other.velocity);
+               switch(other.movetype)
+               {
+                       case MOVETYPE_FLY:
+                               other.movetype = MOVETYPE_TOSS;
+                               other.gravity = 1;
+                               break;
+                       case MOVETYPE_BOUNCEMISSILE:
+                               other.movetype = MOVETYPE_BOUNCE;
+                               other.gravity = 1;
+                               break;
+               }
+               UpdateCSQCProjectile(other);
+       }
+
+       if (self.spawnflags & PUSH_ONCE)
+       {
+               self.touch = func_null;
+               self.think = SUB_Remove;
+               self.nextthink = time;
+       }
+#endif
+}
+
+#ifdef SVQC
+void trigger_push_link();
+void trigger_push_updatelink();
+#endif
+void trigger_push_findtarget()
+{
+       entity t;
+       vector org;
+
+       // first calculate a typical start point for the jump
+       org = (self.absmin + self.absmax) * 0.5;
+       org_z = self.absmax.z - PL_MIN.z;
+
+       if (self.target)
+       {
+               float n = 0;
+               for(t = world; (t = find(t, targetname, self.target)); )
+               {
+                       ++n;
+#ifdef SVQC
+                       entity e = spawn();
+                       setorigin(e, org);
+                       setsize(e, PL_MIN, PL_MAX);
+                       e.velocity = trigger_push_calculatevelocity(org, t, self.height);
+                       tracetoss(e, e);
+                       if(e.movetype == MOVETYPE_NONE)
+                               waypoint_spawnforteleporter(self, trace_endpos, vlen(trace_endpos - org) / vlen(e.velocity));
+                       remove(e);
+#endif
+               }
+
+               if(!n)
+               {
+                       // no dest!
+#ifdef SVQC
+                       objerror ("Jumppad with nonexistant target");
+#endif
+                       return;
+               }
+               else if(n == 1)
+               {
+                       // exactly one dest - bots love that
+                       self.enemy = find(world, targetname, self.target);
+               }
+               else
+               {
+                       // have to use random selection every single time
+                       self.enemy = world;
+               }
+       }
+#ifdef SVQC
+       else
+       {
+               entity e = spawn();
+               setorigin(e, org);
+               setsize(e, PL_MIN, PL_MAX);
+               e.velocity = self.movedir;
+               tracetoss(e, e);
+               waypoint_spawnforteleporter(self, trace_endpos, vlen(trace_endpos - org) / vlen(e.velocity));
+               remove(e);
+       }
+
+       trigger_push_link();
+       defer(0.1, trigger_push_updatelink);
+#endif
+}
+
+#ifdef SVQC
+float trigger_push_send(entity to, float sf)
+{
+       WriteByte(MSG_ENTITY, ENT_CLIENT_TRIGGER_PUSH);
+       WriteByte(MSG_ENTITY, sf);
+
+       if(sf & 1)
+       {
+               WriteByte(MSG_ENTITY, self.team);
+               WriteInt24_t(MSG_ENTITY, self.spawnflags);
+               WriteByte(MSG_ENTITY, self.active);
+               WriteByte(MSG_ENTITY, self.height);
+
+               trigger_common_write(true);
+       }
+
+       if(sf & 2)
+       {
+               WriteByte(MSG_ENTITY, self.team);
+               WriteByte(MSG_ENTITY, self.active);
+       }
+
+       return true;
+}
+
+void trigger_push_updatelink()
+{
+       self.SendFlags |= 1;
+}
+
+void trigger_push_link()
+{
+       //Net_LinkEntity(self, false, 0, trigger_push_send);
+}
+#endif
+#ifdef SVQC
+/*
+ * ENTITY PARAMETERS:
+ *
+ *   target:  target of jump
+ *   height:  the absolute value is the height of the highest point of the jump
+ *            trajectory above the higher one of the player and the target.
+ *            the sign indicates whether the highest point is INSIDE (positive)
+ *            or OUTSIDE (negative) of the jump trajectory. General rule: use
+ *            positive values for targets mounted on the floor, and use negative
+ *            values to target a point on the ceiling.
+ *   movedir: if target is not set, this * speed * 10 is the velocity to be reached.
+ */
+void spawnfunc_trigger_push()
+{
+       SetMovedir ();
+
+       EXACTTRIGGER_INIT;
+
+       self.active = ACTIVE_ACTIVE;
+       self.use = trigger_push_use;
+       self.touch = trigger_push_touch;
+
+       // normal push setup
+       if (!self.speed)
+               self.speed = 1000;
+       self.movedir = self.movedir * self.speed * 10;
+
+       if (!self.noise)
+               self.noise = "misc/jumppad.wav";
+       precache_sound (self.noise);
+
+       // this must be called to spawn the teleport waypoints for bots
+       InitializeEntity(self, trigger_push_findtarget, INITPRIO_FINDTARGET);
+}
+
+
+float target_push_send(entity to, float sf)
+{
+       WriteByte(MSG_ENTITY, ENT_CLIENT_TARGET_PUSH);
+
+       WriteByte(MSG_ENTITY, self.cnt);
+       WriteString(MSG_ENTITY, self.targetname);
+       WriteCoord(MSG_ENTITY, self.origin_x);
+       WriteCoord(MSG_ENTITY, self.origin_y);
+       WriteCoord(MSG_ENTITY, self.origin_z);
+
+       return true;
+}
+
+void target_push_link()
+{
+       Net_LinkEntity(self, false, 0, target_push_send);
+       self.SendFlags |= 1; // update
+}
+
+void spawnfunc_target_push() { target_push_link(); }
+void spawnfunc_info_notnull() { target_push_link(); }
+void spawnfunc_target_position() { target_push_link(); }
+
+#endif
+
+#ifdef CSQC
+
+void ent_trigger_push()
+{
+       float sf = ReadByte();
+
+       if(sf & 1)
+       {
+               self.classname = "jumppad";
+               int mytm = ReadByte(); if(mytm) { self.team = mytm - 1; }
+               self.spawnflags = ReadInt24_t();
+               self.active = ReadByte();
+               self.height = ReadByte();
+
+               trigger_common_read(true);
+
+               self.entremove = trigger_remove_generic;
+               self.solid = SOLID_TRIGGER;
+               self.draw = trigger_draw_generic;
+               self.trigger_touch = trigger_push_touch;
+               self.drawmask = MASK_NORMAL;
+               self.move_time = time;
+               trigger_push_findtarget();
+       }
+
+       if(sf & 2)
+       {
+               self.team = ReadByte();
+               self.active = ReadByte();
+       }
+}
+
+void target_push_remove()
+{
+       if(self.classname)
+               strunzone(self.classname);
+       self.classname = string_null;
+
+       if(self.targetname)
+               strunzone(self.targetname);
+       self.targetname = string_null;
+}
+
+void ent_target_push()
+{
+       self.classname = "push_target";
+       self.cnt = ReadByte();
+       self.targetname = strzone(ReadString());
+       self.origin_x = ReadCoord();
+       self.origin_y = ReadCoord();
+       self.origin_z = ReadCoord();
+       setorigin(self, self.origin);
+
+       self.drawmask = MASK_NORMAL;
+       self.entremove = target_push_remove;
+}
+#endif
diff --git a/qcsrc/common/triggers/trigger/jumppads.qh b/qcsrc/common/triggers/trigger/jumppads.qh
new file mode 100644 (file)
index 0000000..bb0f475
--- /dev/null
@@ -0,0 +1,68 @@
+#ifndef T_JUMPPADS_H
+#define T_JUMPPADS_H
+
+const float PUSH_ONCE          = 1;
+const float PUSH_SILENT                = 2;
+
+.float pushltime;
+.float istypefrag;
+.float height;
+
+const int NUM_JUMPPADSUSED = 3;
+.float jumppadcount;
+.entity jumppadsused[NUM_JUMPPADSUSED];
+
+float trigger_push_calculatevelocity_flighttime;
+
+#ifdef SVQC
+void() SUB_UseTargets;
+void trigger_push_use();
+#endif
+
+#ifdef CSQC
+void ent_trigger_push();
+
+void ent_target_push();
+#endif
+
+/*
+       trigger_push_calculatevelocity
+
+       Arguments:
+         org - origin of the object which is to be pushed
+         tgt - target entity (can be either a point or a model entity; if it is
+               the latter, its midpoint is used)
+         ht  - jump height, measured from the higher one of org and tgt's midpoint
+
+       Returns: velocity for the jump
+       the global trigger_push_calculatevelocity_flighttime is set to the total
+       jump time
+ */
+
+vector trigger_push_calculatevelocity(vector org, entity tgt, float ht);
+
+void trigger_push_touch();
+
+.vector dest;
+void trigger_push_findtarget();
+
+/*
+ * ENTITY PARAMETERS:
+ *
+ *   target:  target of jump
+ *   height:  the absolute value is the height of the highest point of the jump
+ *            trajectory above the higher one of the player and the target.
+ *            the sign indicates whether the highest point is INSIDE (positive)
+ *            or OUTSIDE (negative) of the jump trajectory. General rule: use
+ *            positive values for targets mounted on the floor, and use negative
+ *            values to target a point on the ceiling.
+ *   movedir: if target is not set, this * speed * 10 is the velocity to be reached.
+ */
+#ifdef SVQC
+void spawnfunc_trigger_push();
+
+void spawnfunc_target_push();
+void spawnfunc_info_notnull();
+void spawnfunc_target_position();
+#endif
+#endif
diff --git a/qcsrc/common/triggers/trigger/keylock.qc b/qcsrc/common/triggers/trigger/keylock.qc
new file mode 100644 (file)
index 0000000..ec27d4b
--- /dev/null
@@ -0,0 +1,212 @@
+/**
+ * trigger given targets
+ */
+void trigger_keylock_trigger(string s)
+{
+       entity stemp = self;
+       entity otemp = other;
+       entity atemp = activator;
+
+       entity t;
+       for(t = world; (t = find(t, targetname, s)); )
+               if(t.use)
+               {
+                       self = t;
+                       other = stemp;
+                       activator = atemp;
+                       self.use();
+               }
+
+       self = stemp;
+       other = otemp;
+       activator = atemp;
+}
+
+/**
+ * kill killtarget of trigger keylock.
+ */
+void trigger_keylock_kill(string s)
+{
+       entity t;
+       for(t = world; (t = find(t, targetname, s)); )
+               remove(t);
+}
+
+void trigger_keylock_touch()
+{
+       bool key_used = false;
+       bool started_delay = false;
+
+       // only player may trigger the lock
+       if(!IS_PLAYER(other))
+               return;
+
+       // check silver key
+       if(self.itemkeys)
+               key_used = item_keys_usekey(self, other);
+
+       activator = other;
+
+       if(self.itemkeys)
+       {
+#ifdef SVQC
+               // at least one of the keys is missing
+               if(key_used)
+               {
+                       // one or more keys were given, but others are still missing!
+                       play2(other, self.noise1);
+                       Send_Notification(NOTIF_ONE, other, MSG_CENTER, CENTER_DOOR_LOCKED_ALSONEED, item_keys_keylist(self.itemkeys));
+                       other.key_door_messagetime = time + 2;
+               }
+               else if(other.key_door_messagetime <= time)
+               {
+                       // no keys were given
+                       play2(other, self.noise2);
+                       Send_Notification(NOTIF_ONE, other, MSG_CENTER, CENTER_DOOR_LOCKED_NEED, item_keys_keylist(self.itemkeys));
+                       other.key_door_messagetime = time + 2;
+               }
+#endif
+
+               // trigger target2
+               if(self.delay <= time || started_delay == true)
+               if(self.target2)
+               {
+                       trigger_keylock_trigger(self.target2);
+                       started_delay = true;
+                       self.delay = time + self.wait;
+               }
+       }
+       else
+       {
+#ifdef SVQC
+               // all keys were given!
+               play2(other, self.noise);
+               centerprint(other, self.message);
+#endif
+
+               if(self.target)
+                       trigger_keylock_trigger(self.target);
+
+               if(self.killtarget)
+                       trigger_keylock_kill(self.killtarget);
+
+               remove(self);
+       }
+
+}
+
+#ifdef SVQC
+bool trigger_keylock_send(entity to, int sf)
+{
+       WriteByte(MSG_ENTITY, ENT_CLIENT_KEYLOCK);
+
+       WriteInt24_t(MSG_ENTITY, self.itemkeys);
+       WriteByte(MSG_ENTITY, self.height);
+
+       trigger_common_write(true);
+
+       return true;
+}
+
+void trigger_keylock_link()
+{
+       // uncomment to network keylocks
+       //Net_LinkEntity(self, false, 0, trigger_keylock_send);
+}
+
+/*QUAKED trigger_keylock (.0 .5 .8) ?
+Keylock trigger.  Must target other entities.
+This trigger will trigger target entities when all required keys are provided.
+-------- KEYS --------
+itemkeys: A bit field with key IDs that are needed to open this lock.
+sounds: 1 to play misc/secret.wav, 2 to play misc/talk.wav, 3 to play misc/trigger1.wav (3 is default)
+target: trigger all entities with this targetname when triggered and all keys have been given to it, then remove this trigger
+target2: trigger all entities with this targetname when triggered without giving it all the required keys.
+killtarget: remove all entities with this targetname when triggered with all the needed keys.
+message: print this message to the player who activated the trigger when all needed keys have been given.
+message2: print this message to the player who activated the trigger when not all of the needed keys have been given.
+noise: sound to play when lock gets unlocked (default: see sounds)
+noise1: sound to play when only some of the needed key were used but not all (default: misc/decreasevalue.wav)
+noise2: sound to play when a key is missing (default: misc/talk.wav)
+wait: prevent triggering again for this amount of time (default: 5) - applies to target2, target3, target4.
+---------NOTES----------
+If spawned without any key specified in itemkeys, this trigger will display an error and remove itself.
+message2 and noise2 will be resent to the player every 2 seconds while he is in the trigger zone.
+*/
+void spawnfunc_trigger_keylock(void)
+{
+       if(!self.itemkeys) { remove(self); return; }
+
+       // set unlocked message
+       if(self.message == "")
+               self.message = "Unlocked!";
+
+       // set default unlock noise
+       if(self.noise == "")
+       {
+               if(self.sounds == 1)
+                       self.noise = "misc/secret.wav";
+               else if(self.sounds == 2)
+                       self.noise = "misc/talk.wav";
+               else //if (self.sounds == 3) {
+                       self.noise = "misc/trigger1.wav";
+       }
+
+       // set default use key sound
+       if(self.noise1 == "")
+               self.noise1 = "misc/decreasevalue.wav";
+
+       // set closed sourd
+       if(self.noise2 == "")
+               self.noise2 = "misc/talk.wav";
+
+       // delay between triggering message2 and trigger2
+       if(!self.wait) { self.wait = 5; }
+
+       // precache sounds
+       precache_sound(self.noise);
+       precache_sound(self.noise1);
+       precache_sound(self.noise2);
+
+       EXACTTRIGGER_INIT;
+
+       self.touch = trigger_keylock_touch;
+
+       trigger_keylock_link();
+}
+#elif defined(CSQC)
+void keylock_remove()
+{
+       if(self.target) { strunzone(self.target); }
+       self.target = string_null;
+
+       if(self.target2) { strunzone(self.target2); }
+       self.target2 = string_null;
+
+       if(self.target3) { strunzone(self.target3); }
+       self.target3 = string_null;
+
+       if(self.target4) { strunzone(self.target4); }
+       self.target4 = string_null;
+
+       if(self.killtarget) { strunzone(self.killtarget); }
+       self.killtarget = string_null;
+
+       if(self.targetname) { strunzone(self.targetname); }
+       self.targetname = string_null;
+}
+
+void ent_keylock()
+{
+       self.itemkeys = ReadInt24_t();
+       self.height = ReadByte();
+
+       trigger_common_read(true);
+
+       self.classname = "trigger_keylock";
+       self.drawmask = MASK_NORMAL;
+       self.draw = trigger_draw_generic;
+       self.trigger_touch = trigger_keylock_touch;
+       self.entremove = keylock_remove;
+}
+#endif
diff --git a/qcsrc/common/triggers/trigger/keylock.qh b/qcsrc/common/triggers/trigger/keylock.qh
new file mode 100644 (file)
index 0000000..b21145d
--- /dev/null
@@ -0,0 +1,20 @@
+#ifdef CSQC
+void ent_keylock();
+bool item_keys_usekey(entity l, entity p)
+{
+       float valid = l.itemkeys & p.itemkeys;
+
+       if (!valid) {
+               // other has none of the needed keys
+               return false;
+       } else if (l.itemkeys == valid) {
+               // ALL needed keys were given
+               l.itemkeys = 0;
+               return true;
+       } else {
+               // only some of the needed keys were given
+               l.itemkeys &= ~valid;
+               return true;
+       }
+}
+#endif
diff --git a/qcsrc/common/triggers/trigger/magicear.qc b/qcsrc/common/triggers/trigger/magicear.qc
new file mode 100644 (file)
index 0000000..f14b75c
--- /dev/null
@@ -0,0 +1,204 @@
+#ifdef SVQC
+float magicear_matched;
+float W_Tuba_HasPlayed(entity pl, string melody, float instrument, float ignorepitch, float mintempo, float maxtempo);
+string trigger_magicear_processmessage(entity ear, entity source, float teamsay, entity privatesay, string msgin)
+{
+       float domatch, dotrigger, matchstart, l;
+       string s, msg;
+       entity oldself;
+       string savemessage;
+
+       magicear_matched = false;
+
+       dotrigger = ((IS_PLAYER(source)) && (source.deadflag == DEAD_NO) && ((ear.radius == 0) || (vlen(source.origin - ear.origin) <= ear.radius)));
+       domatch = ((ear.spawnflags & 32) || dotrigger);
+
+       if (!domatch)
+               return msgin;
+
+       if (!msgin)
+       {
+               // we are in TUBA mode!
+               if (!(ear.spawnflags & 256))
+                       return msgin;
+
+               if(!W_Tuba_HasPlayed(source, ear.message, ear.movedir_x, !(ear.spawnflags & 512), ear.movedir_y, ear.movedir_z))
+                       return msgin;
+
+               magicear_matched = true;
+
+               if(dotrigger)
+               {
+                       oldself = self;
+                       activator = source;
+                       self = ear;
+                       savemessage = self.message;
+                       self.message = string_null;
+                       SUB_UseTargets();
+                       self.message = savemessage;
+                       self = oldself;
+               }
+
+               if(ear.netname != "")
+                       return ear.netname;
+
+               return msgin;
+       }
+
+       if(ear.spawnflags & 256) // ENOTUBA
+               return msgin;
+
+       if(privatesay)
+       {
+               if(ear.spawnflags & 4)
+                       return msgin;
+       }
+       else
+       {
+               if(!teamsay)
+                       if(ear.spawnflags & 1)
+                               return msgin;
+               if(teamsay > 0)
+                       if(ear.spawnflags & 2)
+                               return msgin;
+               if(teamsay < 0)
+                       if(ear.spawnflags & 8)
+                               return msgin;
+       }
+
+       matchstart = -1;
+       l = strlen(ear.message);
+
+       if(ear.spawnflags & 128)
+               msg = msgin;
+       else
+               msg = strdecolorize(msgin);
+
+       if(substring(ear.message, 0, 1) == "*")
+       {
+               if(substring(ear.message, -1, 1) == "*")
+               {
+                       // two wildcards
+                       // as we need multi-replacement here...
+                       s = substring(ear.message, 1, -2);
+                       l -= 2;
+                       if(strstrofs(msg, s, 0) >= 0)
+                               matchstart = -2; // we use strreplace on s
+               }
+               else
+               {
+                       // match at start
+                       s = substring(ear.message, 1, -1);
+                       l -= 1;
+                       if(substring(msg, -l, l) == s)
+                               matchstart = strlen(msg) - l;
+               }
+       }
+       else
+       {
+               if(substring(ear.message, -1, 1) == "*")
+               {
+                       // match at end
+                       s = substring(ear.message, 0, -2);
+                       l -= 1;
+                       if(substring(msg, 0, l) == s)
+                               matchstart = 0;
+               }
+               else
+               {
+                       // full match
+                       s = ear.message;
+                       if(msg == ear.message)
+                               matchstart = 0;
+               }
+       }
+
+       if(matchstart == -1) // no match
+               return msgin;
+
+       magicear_matched = true;
+
+       if(dotrigger)
+       {
+               oldself = self;
+               activator = source;
+               self = ear;
+               savemessage = self.message;
+               self.message = string_null;
+               SUB_UseTargets();
+               self.message = savemessage;
+               self = oldself;
+       }
+
+       if(ear.spawnflags & 16)
+       {
+               return ear.netname;
+       }
+       else if(ear.netname != "")
+       {
+               if(matchstart < 0)
+                       return strreplace(s, ear.netname, msg);
+               else
+                       return strcat(
+                               substring(msg, 0, matchstart),
+                               ear.netname,
+                               substring(msg, matchstart + l, -1)
+                       );
+       }
+       else
+               return msgin;
+}
+
+entity magicears;
+string trigger_magicear_processmessage_forallears(entity source, float teamsay, entity privatesay, string msgin)
+{
+       entity ear;
+       string msgout;
+       for(ear = magicears; ear; ear = ear.enemy)
+       {
+               msgout = trigger_magicear_processmessage(ear, source, teamsay, privatesay, msgin);
+               if(!(ear.spawnflags & 64))
+               if(magicear_matched)
+                       return msgout;
+               msgin = msgout;
+       }
+       return msgin;
+}
+
+void spawnfunc_trigger_magicear()
+{
+       self.enemy = magicears;
+       magicears = self;
+
+       // actually handled in "say" processing
+       // spawnflags:
+       //    1 = ignore say
+       //    2 = ignore teamsay
+       //    4 = ignore tell
+       //    8 = ignore tell to unknown player
+       //   16 = let netname replace the whole message (otherwise, netname is a word replacement if set)
+       //   32 = perform the replacement even if outside the radius or dead
+       //   64 = continue replacing/triggering even if this one matched
+       //  128 = don't decolorize message before matching
+       //  256 = message is a tuba note sequence (pitch.duration pitch.duration ...)
+       //  512 = tuba notes must be exact right pitch, no transposing
+       // message: either
+       //   *pattern*
+       // or
+       //   *pattern
+       // or
+       //   pattern*
+       // or
+       //   pattern
+       // netname:
+       //   if set, replacement for the matched text
+       // radius:
+       //   "hearing distance"
+       // target:
+       //   what to trigger
+       // movedir:
+       //   for spawnflags 256, defines 'instrument+1 mintempo maxtempo' (zero component doesn't matter)
+
+       self.movedir_x -= 1; // map to tuba instrument numbers
+}
+#endif
diff --git a/qcsrc/common/triggers/trigger/monoflop.qc b/qcsrc/common/triggers/trigger/monoflop.qc
new file mode 100644 (file)
index 0000000..45ce761
--- /dev/null
@@ -0,0 +1,49 @@
+#ifdef SVQC
+/*QUAKED spawnfunc_trigger_monoflop (.5 .5 .5) (-8 -8 -8) (8 8 8)
+"Mono-flop" trigger gate... turns one trigger event into one "on" and one "off" event, separated by a delay of "wait"
+*/
+void monoflop_use()
+{
+       self.nextthink = time + self.wait;
+       self.enemy = activator;
+       if(self.state)
+               return;
+       self.state = 1;
+       SUB_UseTargets();
+}
+void monoflop_fixed_use()
+{
+       if(self.state)
+               return;
+       self.nextthink = time + self.wait;
+       self.state = 1;
+       self.enemy = activator;
+       SUB_UseTargets();
+}
+
+void monoflop_think()
+{
+       self.state = 0;
+       activator = self.enemy;
+       SUB_UseTargets();
+}
+
+void monoflop_reset()
+{
+       self.state = 0;
+       self.nextthink = 0;
+}
+
+void spawnfunc_trigger_monoflop()
+{
+       if(!self.wait)
+               self.wait = 1;
+       if(self.spawnflags & 1)
+               self.use = monoflop_fixed_use;
+       else
+               self.use = monoflop_use;
+       self.think = monoflop_think;
+       self.state = 0;
+       self.reset = monoflop_reset;
+}
+#endif
diff --git a/qcsrc/common/triggers/trigger/multi.qc b/qcsrc/common/triggers/trigger/multi.qc
new file mode 100644 (file)
index 0000000..b852587
--- /dev/null
@@ -0,0 +1,204 @@
+// NOTE: also contains trigger_once at bottom
+
+#ifdef SVQC
+// the wait time has passed, so set back up for another activation
+void multi_wait()
+{
+       if (self.max_health)
+       {
+               self.health = self.max_health;
+               self.takedamage = DAMAGE_YES;
+               self.solid = SOLID_BBOX;
+       }
+}
+
+
+// the trigger was just touched/killed/used
+// self.enemy should be set to the activator so it can be held through a delay
+// so wait for the delay time before firing
+void multi_trigger()
+{
+       if (self.nextthink > time)
+       {
+               return;         // allready been triggered
+       }
+
+       if (self.classname == "trigger_secret")
+       {
+               if (!IS_PLAYER(self.enemy))
+                       return;
+               found_secrets = found_secrets + 1;
+               WriteByte (MSG_ALL, SVC_FOUNDSECRET);
+       }
+
+       if (self.noise)
+               sound (self.enemy, CH_TRIGGER, self.noise, VOL_BASE, ATTEN_NORM);
+
+// don't trigger again until reset
+       self.takedamage = DAMAGE_NO;
+
+       activator = self.enemy;
+       other = self.goalentity;
+       SUB_UseTargets();
+
+       if (self.wait > 0)
+       {
+               self.think = multi_wait;
+               self.nextthink = time + self.wait;
+       }
+       else if (self.wait == 0)
+       {
+               multi_wait(); // waiting finished
+       }
+       else
+       {       // we can't just remove (self) here, because this is a touch function
+               // called wheil C code is looping through area links...
+               self.touch = func_null;
+       }
+}
+
+void multi_use()
+{
+       self.goalentity = other;
+       self.enemy = activator;
+       multi_trigger();
+}
+
+void multi_touch()
+{
+       if(!(self.spawnflags & 2))
+       if(!other.iscreature)
+                       return;
+
+       if(self.team)
+               if(((self.spawnflags & 4) == 0) == (self.team != other.team))
+                       return;
+
+// if the trigger has an angles field, check player's facing direction
+       if (self.movedir != '0 0 0')
+       {
+               makevectors (other.angles);
+               if (v_forward * self.movedir < 0)
+                       return;         // not facing the right way
+       }
+
+       EXACTTRIGGER_TOUCH;
+
+       self.enemy = other;
+       self.goalentity = other;
+       multi_trigger ();
+}
+
+void multi_eventdamage (entity inflictor, entity attacker, float damage, int deathtype, vector hitloc, vector force)
+{
+       if (!self.takedamage)
+               return;
+       if(self.spawnflags & DOOR_NOSPLASH)
+               if(!(DEATH_ISSPECIAL(deathtype)) && (deathtype & HITTYPE_SPLASH))
+                       return;
+       self.health = self.health - damage;
+       if (self.health <= 0)
+       {
+               self.enemy = attacker;
+               self.goalentity = inflictor;
+               multi_trigger();
+       }
+}
+
+void multi_reset()
+{
+       if ( !(self.spawnflags & SPAWNFLAG_NOTOUCH) )
+               self.touch = multi_touch;
+       if (self.max_health)
+       {
+               self.health = self.max_health;
+               self.takedamage = DAMAGE_YES;
+               self.solid = SOLID_BBOX;
+       }
+       self.think = func_null;
+       self.nextthink = 0;
+       self.team = self.team_saved;
+}
+
+/*QUAKED spawnfunc_trigger_multiple (.5 .5 .5) ? notouch
+Variable sized repeatable trigger.  Must be targeted at one or more entities.  If "health" is set, the trigger must be killed to activate each time.
+If "delay" is set, the trigger waits some time after activating before firing.
+"wait" : Seconds between triggerings. (.2 default)
+If notouch is set, the trigger is only fired by other entities, not by touching.
+NOTOUCH has been obsoleted by spawnfunc_trigger_relay!
+sounds
+1)     secret
+2)     beep beep
+3)     large switch
+4)
+set "message" to text string
+*/
+void spawnfunc_trigger_multiple()
+{
+       self.reset = multi_reset;
+       if (self.sounds == 1)
+       {
+               precache_sound ("misc/secret.wav");
+               self.noise = "misc/secret.wav";
+       }
+       else if (self.sounds == 2)
+       {
+               precache_sound ("misc/talk.wav");
+               self.noise = "misc/talk.wav";
+       }
+       else if (self.sounds == 3)
+       {
+               precache_sound ("misc/trigger1.wav");
+               self.noise = "misc/trigger1.wav";
+       }
+
+       if (!self.wait)
+               self.wait = 0.2;
+       else if(self.wait < -1)
+               self.wait = 0;
+       self.use = multi_use;
+
+       EXACTTRIGGER_INIT;
+
+       self.team_saved = self.team;
+
+       if (self.health)
+       {
+               if (self.spawnflags & SPAWNFLAG_NOTOUCH)
+                       objerror ("health and notouch don't make sense\n");
+               self.max_health = self.health;
+               self.event_damage = multi_eventdamage;
+               self.takedamage = DAMAGE_YES;
+               self.solid = SOLID_BBOX;
+               setorigin (self, self.origin);  // make sure it links into the world
+       }
+       else
+       {
+               if ( !(self.spawnflags & SPAWNFLAG_NOTOUCH) )
+               {
+                       self.touch = multi_touch;
+                       setorigin (self, self.origin);  // make sure it links into the world
+               }
+       }
+}
+
+
+/*QUAKED spawnfunc_trigger_once (.5 .5 .5) ? notouch
+Variable sized trigger. Triggers once, then removes itself.  You must set the key "target" to the name of another object in the level that has a matching
+"targetname".  If "health" is set, the trigger must be killed to activate.
+If notouch is set, the trigger is only fired by other entities, not by touching.
+if "killtarget" is set, any objects that have a matching "target" will be removed when the trigger is fired.
+if "angle" is set, the trigger will only fire when someone is facing the direction of the angle.  Use "360" for an angle of 0.
+sounds
+1)     secret
+2)     beep beep
+3)     large switch
+4)
+set "message" to text string
+*/
+void spawnfunc_trigger_once()
+{
+       self.wait = -1;
+       spawnfunc_trigger_multiple();
+}
+#endif
diff --git a/qcsrc/common/triggers/trigger/multi.qh b/qcsrc/common/triggers/trigger/multi.qh
new file mode 100644 (file)
index 0000000..df14a51
--- /dev/null
@@ -0,0 +1,6 @@
+#ifdef SVQC
+void multi_trigger();
+void multi_reset();
+
+void spawnfunc_trigger_once();
+#endif
diff --git a/qcsrc/common/triggers/trigger/multivibrator.qc b/qcsrc/common/triggers/trigger/multivibrator.qc
new file mode 100644 (file)
index 0000000..02a258e
--- /dev/null
@@ -0,0 +1,73 @@
+#ifdef SVQC
+void multivibrator_send()
+{
+       float newstate;
+       float cyclestart;
+
+       cyclestart = floor((time + self.phase) / (self.wait + self.respawntime)) * (self.wait + self.respawntime) - self.phase;
+
+       newstate = (time < cyclestart + self.wait);
+
+       activator = self;
+       if(self.state != newstate)
+               SUB_UseTargets();
+       self.state = newstate;
+
+       if(self.state)
+               self.nextthink = cyclestart + self.wait + 0.01;
+       else
+               self.nextthink = cyclestart + self.wait + self.respawntime + 0.01;
+}
+
+void multivibrator_toggle()
+{
+       if(self.nextthink == 0)
+       {
+               multivibrator_send();
+       }
+       else
+       {
+               if(self.state)
+               {
+                       SUB_UseTargets();
+                       self.state = 0;
+               }
+               self.nextthink = 0;
+       }
+}
+
+void multivibrator_reset()
+{
+       if(!(self.spawnflags & 1))
+               self.nextthink = 0; // wait for a trigger event
+       else
+               self.nextthink = max(1, time);
+}
+
+/*QUAKED trigger_multivibrator (.5 .5 .5) (-8 -8 -8) (8 8 8) START_ON
+"Multivibrator" trigger gate... repeatedly sends trigger events. When triggered, turns on or off.
+-------- KEYS --------
+target: trigger all entities with this targetname when it goes off
+targetname: name that identifies this entity so it can be triggered; when off, it always uses the OFF state
+phase: offset of the timing
+wait: "on" cycle time (default: 1)
+respawntime: "off" cycle time (default: same as wait)
+-------- SPAWNFLAGS --------
+START_ON: assume it is already turned on (when targeted)
+*/
+void spawnfunc_trigger_multivibrator()
+{
+       if(!self.wait)
+               self.wait = 1;
+       if(!self.respawntime)
+               self.respawntime = self.wait;
+
+       self.state = 0;
+       self.use = multivibrator_toggle;
+       self.think = multivibrator_send;
+       self.nextthink = max(1, time);
+
+       IFTARGETED
+               multivibrator_reset();
+}
+#endif
diff --git a/qcsrc/common/triggers/trigger/relay.qc b/qcsrc/common/triggers/trigger/relay.qc
new file mode 100644 (file)
index 0000000..e037028
--- /dev/null
@@ -0,0 +1,10 @@
+#ifdef SVQC
+/*QUAKED spawnfunc_trigger_relay (.5 .5 .5) (-8 -8 -8) (8 8 8)
+This fixed size trigger cannot be touched, it can only be fired by other events.  It can contain killtargets, targets, delays, and messages.
+*/
+void spawnfunc_trigger_relay()
+{
+       self.use = SUB_UseTargets;
+       self.reset = spawnfunc_trigger_relay; // this spawnfunc resets fully
+}
+#endif
diff --git a/qcsrc/common/triggers/trigger/relay_activators.qc b/qcsrc/common/triggers/trigger/relay_activators.qc
new file mode 100644 (file)
index 0000000..83c0103
--- /dev/null
@@ -0,0 +1,45 @@
+#ifdef SVQC
+void relay_activators_use()
+{
+       entity trg, os;
+
+       os = self;
+
+       for(trg = world; (trg = find(trg, targetname, os.target)); )
+       {
+               self = trg;
+               if (trg.setactive)
+                       trg.setactive(os.cnt);
+               else
+               {
+                       //bprint("Not using setactive\n");
+                       if(os.cnt == ACTIVE_TOGGLE)
+                               if(trg.active == ACTIVE_ACTIVE)
+                                       trg.active = ACTIVE_NOT;
+                               else
+                                       trg.active = ACTIVE_ACTIVE;
+                       else
+                               trg.active = os.cnt;
+               }
+       }
+       self = os;
+}
+
+void spawnfunc_relay_activate()
+{
+       self.cnt = ACTIVE_ACTIVE;
+       self.use = relay_activators_use;
+}
+
+void spawnfunc_relay_deactivate()
+{
+       self.cnt = ACTIVE_NOT;
+       self.use = relay_activators_use;
+}
+
+void spawnfunc_relay_activatetoggle()
+{
+       self.cnt = ACTIVE_TOGGLE;
+       self.use = relay_activators_use;
+}
+#endif
diff --git a/qcsrc/common/triggers/trigger/relay_if.qc b/qcsrc/common/triggers/trigger/relay_if.qc
new file mode 100644 (file)
index 0000000..ade56c1
--- /dev/null
@@ -0,0 +1,20 @@
+#ifdef SVQC
+void trigger_relay_if_use()
+{
+       float n;
+       n = self.count;
+
+       // TODO make this generic AND faster than nextent()ing through all, if somehow possible
+       n = (cvar_string(self.netname) == cvar_string(self.message));
+       if(self.spawnflags & 1)
+               n = !n;
+
+       if(n)
+               SUB_UseTargets();
+}
+
+void spawnfunc_trigger_relay_if()
+{
+       self.use = trigger_relay_if_use;
+}
+#endif
diff --git a/qcsrc/common/triggers/trigger/relay_teamcheck.qc b/qcsrc/common/triggers/trigger/relay_teamcheck.qc
new file mode 100644 (file)
index 0000000..8a77cef
--- /dev/null
@@ -0,0 +1,35 @@
+#ifdef SVQC
+void trigger_relay_teamcheck_use()
+{
+       if(activator.team)
+       {
+               if(self.spawnflags & 2)
+               {
+                       if(DIFF_TEAM(activator, self))
+                               SUB_UseTargets();
+               }
+               else
+               {
+                       if(SAME_TEAM(activator, self))
+                               SUB_UseTargets();
+               }
+       }
+       else
+       {
+               if(self.spawnflags & 1)
+                       SUB_UseTargets();
+       }
+}
+
+void trigger_relay_teamcheck_reset()
+{
+       self.team = self.team_saved;
+}
+
+void spawnfunc_trigger_relay_teamcheck()
+{
+       self.team_saved = self.team;
+       self.use = trigger_relay_teamcheck_use;
+       self.reset = trigger_relay_teamcheck_reset;
+}
+#endif
diff --git a/qcsrc/common/triggers/trigger/secret.qc b/qcsrc/common/triggers/trigger/secret.qc
new file mode 100644 (file)
index 0000000..b93ab03
--- /dev/null
@@ -0,0 +1,85 @@
+#if defined(CSQC)
+#elif defined(MENUQC)
+#elif defined(SVQC)
+       #include "../../../server/_all.qh"
+    #include "../../util.qh"
+    #include "../../../server/defs.qh"
+    #include "secret.qh"
+#endif
+
+#ifdef SVQC
+
+void secrets_setstatus() {
+       self.stat_secrets_total = secrets_total;
+       self.stat_secrets_found = secrets_found;
+}
+
+/**
+ * A secret has been found (maybe :P)
+ */
+void trigger_secret_touch() {
+       // only a player can trigger this
+       if (!IS_PLAYER(other))
+               return;
+
+       // update secrets found counter
+       secrets_found += 1;
+       //print("Secret found: ", ftos(secret_counter.cnt), "/");
+       //print(ftos(secret_counter.count), "\n");
+
+       // centerprint message (multi_touch() doesn't always call centerprint())
+       centerprint(other, self.message);
+       self.message = "";
+
+       // handle normal trigger features
+       multi_touch();
+       remove(self);
+}
+
+/*QUAKED trigger_secret (.5 .5 .5) ?
+Variable sized secret trigger. Can be targeted at one or more entities.
+Basically, it's a trigger_once (with restrictions, see notes) that additionally updates the number of secrets found.
+-------- KEYS --------
+sounds: 1 to play misc/secret.wav, 2 to play misc/talk.wav, 3 to play misc/trigger1.wav (default: 1)
+noise: path to sound file, if you want to play something else
+target: trigger all entities with this targetname when triggered
+message: print this message to the player who activated the trigger instead of the standard 'You found a secret!'
+killtarget: remove all entities with this targetname when triggered
+-------- NOTES --------
+You should create a common/trigger textured brush covering the entrance to a secret room/area.
+Trigger secret can only be trigger by a player's touch and can not be a target itself.
+*/
+void spawnfunc_trigger_secret() {
+       // FIXME: should it be disabled in most modes?
+
+       // update secrets count
+       secrets_total += 1;
+
+       // add default message
+       if (self.message == "")
+               self.message = "You found a secret!";
+
+       // set default sound
+       if (self.noise == "")
+       if (!self.sounds)
+               self.sounds = 1; // misc/secret.wav
+
+       // this entity can't be a target itself!!!!
+       self.targetname = "";
+
+       // you can't just shoot a room to find it, can you?
+       self.health = 0;
+
+       // a secret can not be delayed
+       self.delay = 0;
+
+       // convert this trigger to trigger_once
+       self.classname = "trigger_once";
+       spawnfunc_trigger_once();
+
+       // take over the touch() function, so we can mark secret as found
+       self.touch = trigger_secret_touch;
+       // ignore triggering;
+       self.use = func_null;
+}
+#endif
diff --git a/qcsrc/common/triggers/trigger/secret.qh b/qcsrc/common/triggers/trigger/secret.qh
new file mode 100644 (file)
index 0000000..c09da6b
--- /dev/null
@@ -0,0 +1,24 @@
+#ifndef SECRET_H
+#define SECRET_H
+#ifdef SVQC
+
+/**
+ * Total number of secrets on the map.
+ */
+float secrets_total;
+
+/**
+ * Total numbe of secrets found on the map.
+ */
+float secrets_found;
+
+
+.float stat_secrets_total;
+.float stat_secrets_found;
+
+/**
+ * update secrets status.
+ */
+void secrets_setstatus();
+#endif
+#endif
diff --git a/qcsrc/common/triggers/trigger/swamp.qc b/qcsrc/common/triggers/trigger/swamp.qc
new file mode 100644 (file)
index 0000000..b99332b
--- /dev/null
@@ -0,0 +1,154 @@
+#if defined(CSQC)
+#elif defined(MENUQC)
+#elif defined(SVQC)
+       #include "../../../server/_all.qh"
+    #include "../../../warpzonelib/util_server.qh"
+    #include "../../weapons/all.qh"
+    #include "../../../server/defs.qh"
+    #include "../../deathtypes.qh"
+#endif
+
+/*
+*              t_swamp.c
+*              Adds spawnfunc_trigger_swamp and suppoart routines for xonotic 1.2.1+
+*              Author tZork (Jakob MG)
+*              jakob@games43.se
+*              2005 11 29
+*/
+
+.float swamp_interval; //Hurt players in swamp with this interval
+.float swamp_slowdown; //Players in swamp get slowd down by this mutch 0-1 is slowdown 1-~ is speedup (!?)
+.entity swampslug;
+
+#ifdef SVQC
+void spawnfunc_trigger_swamp(void);
+#endif
+void swamp_touch(void);
+void swampslug_think();
+
+
+/*
+* Uses a entity calld swampslug to handle players in the swamp
+* It works like this: When the plyer enters teh swamp the spawnfunc_trigger_swamp
+* attaches a new "swampslug" to the player. As long as the plyer is inside
+* the swamp the swamp gives the slug new health. But the slug slowly kills itself
+* so when the player goes outside the swamp, it dies and releases the player from the
+* swamps curses (dmg/slowdown)
+*
+* I do it this way becuz there is no "untouch" event.
+*/
+void swampslug_think(void)
+{
+       //Slowly kill the slug
+       self.health = self.health - 1;
+
+       //Slug dead? then remove curses.
+       if(self.health <= 0)
+       {
+               self.owner.in_swamp = 0;
+               remove(self);
+               //centerprint(self.owner,"Killing slug...\n");
+               return;
+       }
+
+       // Slug still alive, so we are still in the swamp
+       // Or we have exited it very recently.
+       // Do the damage and renew the timer.
+#ifdef SVQC
+       Damage (self.owner, self, self, self.dmg, DEATH_SWAMP, other.origin, '0 0 0');
+#endif
+
+       self.nextthink = time + self.swamp_interval;
+}
+
+void swamp_touch(void)
+{
+       // If whatever thats touching the swamp is not a player
+       // or if its a dead player, just dont care abt it.
+       if(!IS_PLAYER(other) || PHYS_DEAD(other))
+               return;
+
+       EXACTTRIGGER_TOUCH;
+
+       // Chech if player alredy got a swampslug.
+       if(other.in_swamp != 1)
+       {
+               // If not attach one.
+               //centerprint(other,"Entering swamp!\n");
+               other.swampslug = spawn();
+               other.swampslug.health = 2;
+               other.swampslug.think = swampslug_think;
+               other.swampslug.nextthink = time;
+               other.swampslug.owner = other;
+               other.swampslug.dmg = self.dmg;
+               other.swampslug.swamp_interval = self.swamp_interval;
+               other.swamp_slowdown = self.swamp_slowdown;
+               other.in_swamp = 1;
+               return;
+       }
+
+       //other.in_swamp = 1;
+
+       //Revitalize players swampslug
+       other.swampslug.health = 2;
+}
+
+#ifdef SVQC
+float swamp_send(entity to, float sf)
+{
+       WriteByte(MSG_ENTITY, ENT_CLIENT_LADDER);
+
+       WriteByte(MSG_ENTITY, self.dmg); // can probably get away with using a single byte here
+       WriteByte(MSG_ENTITY, self.swamp_slowdown);
+       WriteByte(MSG_ENTITY, self.swamp_interval);
+
+       trigger_common_write(false);
+
+       return true;
+}
+
+void swamp_link()
+{
+       Net_LinkEntity(self, false, 0, func_ladder_send);
+}
+
+/*QUAKED spawnfunc_trigger_swamp (.5 .5 .5) ?
+Players gettin into the swamp will
+get slowd down and damaged
+*/
+void spawnfunc_trigger_swamp(void)
+{
+       // Init stuff
+       EXACTTRIGGER_INIT;
+       self.touch = swamp_touch;
+
+       // Setup default keys, if missing
+       if(self.dmg <= 0)
+               self.dmg = 5;
+       if(self.swamp_interval <= 0)
+               self.swamp_interval = 1;
+       if(self.swamp_slowdown <= 0)
+               self.swamp_slowdown = 0.5;
+
+       swamp_link();
+}
+
+#elif defined(CSQC)
+
+void ent_swamp()
+{
+       self.dmg = ReadByte();
+       self.swamp_slowdown = ReadByte();
+       self.swamp_interval = ReadByte();
+
+       trigger_common_read(false);
+
+       self.classname = "trigger_swamp";
+       self.solid = SOLID_TRIGGER;
+       self.draw = trigger_draw_generic;
+       self.trigger_touch = swamp_touch;
+       self.drawmask = MASK_NORMAL;
+       self.move_time = time;
+       self.entremove = trigger_remove_generic;
+}
+#endif
diff --git a/qcsrc/common/triggers/trigger/swamp.qh b/qcsrc/common/triggers/trigger/swamp.qh
new file mode 100644 (file)
index 0000000..86b1431
--- /dev/null
@@ -0,0 +1,15 @@
+#ifndef TRIGGER_SWAMP_H
+#define TRIGGER_SWAMP_H
+
+.float swamp_interval; //Hurt players in swamp with this interval
+.float swamp_slowdown; //Players in swamp get slowd down by this mutch 0-1 is slowdown 1-~ is speedup (!?)
+.entity swampslug;
+
+.float in_swamp;              // bool
+.entity swampslug;            // Uses this to release from swamp ("untouch" fix)
+
+#ifdef CSQC
+void ent_swamp();
+#endif
+
+#endif
diff --git a/qcsrc/common/triggers/trigger/teleport.qc b/qcsrc/common/triggers/trigger/teleport.qc
new file mode 100644 (file)
index 0000000..5ff5fcf
--- /dev/null
@@ -0,0 +1,77 @@
+#ifdef SVQC
+void trigger_teleport_use()
+{
+       if(teamplay)
+               self.team = activator.team;
+#ifdef SVQC
+       self.SendFlags |= SF_TRIGGER_UPDATE;
+#endif
+}
+
+void Teleport_Touch (void)
+{
+       entity oldself;
+       string s;
+
+       if (self.active != ACTIVE_ACTIVE)
+               return;
+
+       if (!other.teleportable)
+               return;
+
+       if(other.vehicle)
+       if(!other.vehicle.teleportable)
+               return;
+
+       if(other.turrcaps_flags & TFL_TURRCAPS_ISTURRET)
+               return;
+
+       if(other.deadflag != DEAD_NO)
+               return;
+
+       if(self.team)
+               if(((self.spawnflags & 4) == 0) == (self.team != other.team))
+                       return;
+
+       EXACTTRIGGER_TOUCH;
+
+       if(IS_PLAYER(other))
+               RemoveGrapplingHook(other);
+
+       entity e;
+       e = Simple_TeleportPlayer(self, other);
+
+       activator = other;
+       s = self.target; self.target = string_null;
+       SUB_UseTargets();
+       if (!self.target) self.target = s;
+
+       oldself = self;
+       self = e;
+       SUB_UseTargets();
+       self = oldself;
+}
+
+void spawnfunc_trigger_teleport()
+{
+       self.angles = '0 0 0';
+
+       EXACTTRIGGER_INIT;
+
+       self.active = ACTIVE_ACTIVE;
+
+       self.use = trigger_teleport_use;
+
+       // this must be called to spawn the teleport waypoints for bots
+       InitializeEntity(self, teleport_findtarget, INITPRIO_FINDTARGET);
+
+       if (self.target == "")
+       {
+               objerror ("Teleporter with no target");
+               return;
+       }
+
+       self.teleport_next = teleport_first;
+       teleport_first = self;
+}
+#endif
diff --git a/qcsrc/common/triggers/triggers.qc b/qcsrc/common/triggers/triggers.qc
new file mode 100644 (file)
index 0000000..1e7715a
--- /dev/null
@@ -0,0 +1,277 @@
+void SUB_DontUseTargets() { }
+
+void() SUB_UseTargets;
+
+void DelayThink()
+{
+       activator = self.enemy;
+       SUB_UseTargets ();
+       remove(self);
+}
+
+void FixSize(entity e)
+{
+       e.mins_x = rint(e.mins_x);
+       e.mins_y = rint(e.mins_y);
+       e.mins_z = rint(e.mins_z);
+
+       e.maxs_x = rint(e.maxs_x);
+       e.maxs_y = rint(e.maxs_y);
+       e.maxs_z = rint(e.maxs_z);
+}
+
+#ifdef SVQC
+void trigger_common_write(bool withtarget)
+{
+       WriteByte(MSG_ENTITY, self.warpzone_isboxy);
+       WriteByte(MSG_ENTITY, self.scale);
+
+       if(withtarget)
+       {
+               WriteString(MSG_ENTITY, self.target);
+               WriteString(MSG_ENTITY, self.target2);
+               WriteString(MSG_ENTITY, self.target3);
+               WriteString(MSG_ENTITY, self.target4);
+               WriteString(MSG_ENTITY, self.targetname);
+               WriteString(MSG_ENTITY, self.killtarget);
+       }
+
+       WriteCoord(MSG_ENTITY, self.origin_x);
+       WriteCoord(MSG_ENTITY, self.origin_y);
+       WriteCoord(MSG_ENTITY, self.origin_z);
+
+       WriteCoord(MSG_ENTITY, self.mins_x);
+       WriteCoord(MSG_ENTITY, self.mins_y);
+       WriteCoord(MSG_ENTITY, self.mins_z);
+       WriteCoord(MSG_ENTITY, self.maxs_x);
+       WriteCoord(MSG_ENTITY, self.maxs_y);
+       WriteCoord(MSG_ENTITY, self.maxs_z);
+
+       WriteCoord(MSG_ENTITY, self.movedir_x);
+       WriteCoord(MSG_ENTITY, self.movedir_y);
+       WriteCoord(MSG_ENTITY, self.movedir_z);
+
+       WriteCoord(MSG_ENTITY, self.angles_x);
+       WriteCoord(MSG_ENTITY, self.angles_y);
+       WriteCoord(MSG_ENTITY, self.angles_z);
+}
+
+#elif defined(CSQC)
+
+void trigger_common_read(bool withtarget)
+{
+       self.warpzone_isboxy = ReadByte();
+       self.scale = ReadByte();
+
+       if(withtarget)
+       {
+               self.target = strzone(ReadString());
+               self.target2 = strzone(ReadString());
+               self.target3 = strzone(ReadString());
+               self.target4 = strzone(ReadString());
+               self.targetname = strzone(ReadString());
+               self.killtarget = strzone(ReadString());
+       }
+
+       self.origin_x = ReadCoord();
+       self.origin_y = ReadCoord();
+       self.origin_z = ReadCoord();
+       setorigin(self, self.origin);
+
+       self.mins_x = ReadCoord();
+       self.mins_y = ReadCoord();
+       self.mins_z = ReadCoord();
+       self.maxs_x = ReadCoord();
+       self.maxs_y = ReadCoord();
+       self.maxs_z = ReadCoord();
+       setsize(self, self.mins, self.maxs);
+
+       self.movedir_x = ReadCoord();
+       self.movedir_y = ReadCoord();
+       self.movedir_z = ReadCoord();
+
+       self.angles_x = ReadCoord();
+       self.angles_y = ReadCoord();
+       self.angles_z = ReadCoord();
+}
+
+void trigger_remove_generic()
+{
+       if(self.target) { strunzone(self.target); }
+       self.target = string_null;
+
+       if(self.target2) { strunzone(self.target2); }
+       self.target2 = string_null;
+
+       if(self.target3) { strunzone(self.target3); }
+       self.target3 = string_null;
+
+       if(self.target4) { strunzone(self.target4); }
+       self.target4 = string_null;
+
+       if(self.targetname) { strunzone(self.targetname); }
+       self.target = string_null;
+
+       if(self.killtarget) { strunzone(self.killtarget); }
+       self.killtarget = string_null;
+}
+#endif
+
+/*
+==============================
+SUB_UseTargets
+
+the global "activator" should be set to the entity that initiated the firing.
+
+If self.delay is set, a DelayedUse entity will be created that will actually
+do the SUB_UseTargets after that many seconds have passed.
+
+Centerprints any self.message to the activator.
+
+Removes all entities with a targetname that match self.killtarget,
+and removes them, so some events can remove other triggers.
+
+Search for (string)targetname in all entities that
+match (string)self.target and call their .use function
+
+==============================
+*/
+void SUB_UseTargets()
+{
+       entity t, stemp, otemp, act;
+       string s;
+       float i;
+
+//
+// check for a delay
+//
+       if (self.delay)
+       {
+       // create a temp object to fire at a later time
+               t = spawn();
+               t.classname = "DelayedUse";
+               t.nextthink = time + self.delay;
+               t.think = DelayThink;
+               t.enemy = activator;
+               t.message = self.message;
+               t.killtarget = self.killtarget;
+               t.target = self.target;
+               t.target2 = self.target2;
+               t.target3 = self.target3;
+               t.target4 = self.target4;
+               return;
+       }
+
+
+//
+// print the message
+//
+#ifdef SVQC
+       if(self)
+       if(IS_PLAYER(activator) && self.message != "")
+       if(IS_REAL_CLIENT(activator))
+       {
+               centerprint(activator, self.message);
+               if (self.noise == "")
+                       play2(activator, "misc/talk.wav");
+       }
+
+//
+// kill the killtagets
+//
+       s = self.killtarget;
+       if (s != "")
+       {
+               for(t = world; (t = find(t, targetname, s)); )
+                       remove(t);
+       }
+#endif
+
+//
+// fire targets
+//
+       act = activator;
+       stemp = self;
+       otemp = other;
+
+       if(stemp.target_random)
+               RandomSelection_Init();
+
+       for(i = 0; i < 4; ++i)
+       {
+               switch(i)
+               {
+                       default:
+                       case 0: s = stemp.target; break;
+                       case 1: s = stemp.target2; break;
+                       case 2: s = stemp.target3; break;
+                       case 3: s = stemp.target4; break;
+               }
+               if (s != "")
+               {
+                       // Flag to set func_clientwall state
+                       // 1 == deactivate, 2 == activate, 0 == do nothing
+                       float aw_flag = self.antiwall_flag;
+                       for(t = world; (t = find(t, targetname, s)); )
+                       if(t.use)
+                       {
+                               if(stemp.target_random)
+                               {
+                                       RandomSelection_Add(t, 0, string_null, 1, 0);
+                               }
+                               else
+                               {
+                                       if (t.classname == "func_clientwall" || t.classname == "func_clientillusionary")
+                                               t.antiwall_flag = aw_flag;
+                                       self = t;
+                                       other = stemp;
+                                       activator = act;
+                                       self.use();
+                               }
+                       }
+               }
+       }
+
+       if(stemp.target_random && RandomSelection_chosen_ent)
+       {
+               self = RandomSelection_chosen_ent;
+               other = stemp;
+               activator = act;
+               self.use();
+       }
+
+       activator = act;
+       self = stemp;
+       other = otemp;
+}
+
+#ifdef CSQC
+void trigger_touch_generic(void() touchfunc)
+{
+       entity e;
+       for(e = findradius((self.absmin + self.absmax) * 0.5, vlen(self.absmax - self.absmin) * 0.5 + 1); e; e = e.chain)
+       if(e.isplayermodel || e.classname == "csqcprojectile")
+       {
+               vector emin = e.absmin, emax = e.absmax;
+               if(self.solid == SOLID_BSP)
+               {
+                       emin -= '1 1 1';
+                       emax += '1 1 1';
+               }
+               if(boxesoverlap(emin, emax, self.absmin, self.absmax)) // quick
+               if(WarpZoneLib_BoxTouchesBrush(emin, emax, self, e)) // accurate
+               {
+                       other = e;
+                       touchfunc();
+               }
+       }
+}
+void trigger_draw_generic()
+{
+       float dt = time - self.move_time;
+       self.move_time = time;
+       if(dt <= 0) { return; }
+
+       if(self.trigger_touch) { trigger_touch_generic(self.trigger_touch); }
+}
+#endif
diff --git a/qcsrc/common/triggers/triggers.qh b/qcsrc/common/triggers/triggers.qh
new file mode 100644 (file)
index 0000000..474f797
--- /dev/null
@@ -0,0 +1,52 @@
+#ifndef TRIGGERS_H
+#define TRIGGERS_H
+
+const float SF_TRIGGER_INIT = 1;
+const float SF_TRIGGER_UPDATE = 2;
+const float SF_TRIGGER_RESET = 4;
+
+const float    SPAWNFLAG_NOMESSAGE = 1;
+const float    SPAWNFLAG_NOTOUCH = 1;
+
+.void() trigger_touch;
+
+.float antiwall_flag; // Variable to define what to do with func_clientwall
+// 0 == do nothing, 1 == deactivate, 2 == activate
+
+.float height;
+
+.float nottargeted;
+#define IFTARGETED if(!self.nottargeted && self.targetname != "")
+
+.float lip;
+
+// used elsewhere (will fix)
+#ifdef SVQC
+void trigger_common_write(bool withtarget);
+
+string trigger_magicear_processmessage_forallears(entity source, float teamsay, entity privatesay, string msgin);
+
+void target_voicescript_next(entity pl);
+void target_voicescript_clear(entity pl);
+#endif
+
+.float volume, atten;
+
+.vector dest;
+
+#ifdef CSQC
+void trigger_common_read(bool withtarget);
+void trigger_remove_generic();
+
+.float active;
+.string target;
+.string targetname;
+
+const int ACTIVE_NOT           = 0;
+const int ACTIVE_ACTIVE        = 1;
+const int ACTIVE_IDLE          = 2;
+const int ACTIVE_BUSY          = 2;
+const int ACTIVE_TOGGLE                = 3;
+#endif
+
+#endif
index 4a3b2dbf4924feebfb9bbf6e5b6a61c25d2bfa3f..5479698acadb11d2fb1080d446e5926ce82a33d1 100644 (file)
@@ -2570,7 +2570,7 @@ void FindConnectedComponent(entity e, .entity fld, findNextEntityNearFunction_t
 #ifdef SVQC
 vector combine_to_vector(float x, float y, float z)
 {
-       vector result; result.x = x; result.y = y; result.z = z;
+       vector result; result_x = x; result_y = y; result_z = z;
        return result;
 }
 
index 847c8371d6d920956d67b93e00c59dd28be5286b..36f08eff9017bd322cea18f6fc36f8590c714a81 100644 (file)
@@ -17,7 +17,7 @@
        #include "../../client/autocvars.qh"
        #include "../deathtypes.qh"
        #include "../../csqcmodellib/interpolate.qh"
-       #include "../../client/movetypes.qh"
+       #include "../movetypes/movetypes.qh"
        #include "../../client/main.qh"
        #include "../../csqcmodellib/cl_model.qh"
 #elif defined(MENUQC)
index 01b1ab6489c4133b987d1cf36223554839d6f600..82baf07bd1fa46b16d853fa019a13bc838257969 100644 (file)
@@ -38,6 +38,8 @@ PORTO_SETTINGS(WEP_ADD_CVAR, WEP_ADD_PROP)
 #endif
 #else
 #ifdef SVQC
+#include "../triggers/trigger/jumppads.qh"
+
 void spawnfunc_weapon_porto(void) { weapon_defaultspawnfunc(WEP_PORTO); }
 
 void W_Porto_Success(void)
@@ -122,7 +124,7 @@ void W_Porto_Touch(void)
        norm = trace_plane_normal;
        if(trace_ent.iscreature)
        {
-               traceline(trace_ent.origin, trace_ent.origin + '0 0 2' * PL_MIN_z, MOVE_WORLDONLY, self);
+               traceline(trace_ent.origin, trace_ent.origin + '0 0 2' * PL_MIN.z, MOVE_WORLDONLY, self);
                if(trace_fraction >= 1)
                        return;
                if(trace_dphitq3surfaceflags & Q3SURFACEFLAG_SLICK || trace_dphitcontents & DPCONTENTS_PLAYERCLIP)
index 1f3017afdefd64766649fc7636b9602034b718a9..4360527674e16be74180abadb8a95aac5279e226 100644 (file)
@@ -35,6 +35,7 @@
 #endif
 
 float autocvar_cl_movement_errorcompensation = 0;
+int autocvar_cl_movement = 1;
 
 // engine stuff
 float pmove_onground; // weird engine flag we shouldn't really use but have to for now
@@ -104,12 +105,12 @@ void CSQCPlayer_Unpredict()
        self.origin = csqcplayer_origin;
        self.velocity = csqcplayer_velocity;
        csqcplayer_moveframe = csqcplayer_sequence+1; //+1 because the recieved frame has the move already done (server side)
-       self.pmove_flags = player_pmflags;
+       self.flags = player_pmflags;
 }
 
 void CSQCPlayer_SetMinsMaxs()
 {
-       if(self.pmove_flags & PMF_DUCKED)
+       if(self.flags & FL_DUCKED)
        {
                self.mins = PL_CROUCH_MIN;
                self.maxs = PL_CROUCH_MAX;
@@ -125,13 +126,35 @@ void CSQCPlayer_SetMinsMaxs()
 
 void CSQCPlayer_SavePrediction()
 {
-       player_pmflags = self.pmove_flags;
+       player_pmflags = self.flags;
        csqcplayer_origin = self.origin;
        csqcplayer_velocity = self.velocity;
        csqcplayer_sequence = servercommandframe;
        csqcplayer_status = CSQCPLAYERSTATUS_PREDICTED;
 }
 
+void CSQC_ClientMovement_PlayerMove_Frame();
+
+void PM_Movement_Move()
+{
+       runstandardplayerphysics(self);
+#ifdef CSQC
+       self.flags = 
+                       ((self.pmove_flags & PMF_DUCKED) ? FL_DUCKED : 0) |
+                       (!(self.pmove_flags & PMF_JUMP_HELD) ? FL_JUMPRELEASED : 0) |
+                       ((self.pmove_flags & PMF_ONGROUND) ? FL_ONGROUND : 0);
+#endif
+}
+
+void CSQCPlayer_Physics(void)
+{
+       switch(autocvar_cl_movement)
+       {
+               case 1: CSQC_ClientMovement_PlayerMove_Frame(); break;
+               case 2: PM_Movement_Move(); break;
+       }
+}
+
 void CSQCPlayer_PredictTo(float endframe, float apply_error)
 {
        CSQCPlayer_Unpredict();
@@ -166,7 +189,7 @@ void CSQCPlayer_PredictTo(float endframe, float apply_error)
                {
                        if (!getinputstate(csqcplayer_moveframe))
                                break;
-                       runstandardplayerphysics(self);
+                       CSQCPlayer_Physics();
                        CSQCPlayer_SetMinsMaxs();
                        csqcplayer_moveframe++;
                }
@@ -177,7 +200,7 @@ void CSQCPlayer_PredictTo(float endframe, float apply_error)
        input_angles = view_angles;
 }
 
-float CSQCPlayer_IsLocalPlayer()
+bool CSQCPlayer_IsLocalPlayer()
 {
        return (self == csqcplayer);
 }
@@ -199,16 +222,16 @@ void CSQCPlayer_SetCamera()
                        self.view_ofs = '0 0 1' * getstati(STAT_VIEWHEIGHT);
 
                        // get crouch state from the server
-                       if(getstati(STAT_VIEWHEIGHT) == PL_VIEW_OFS_z)
-                               self.pmove_flags &= ~PMF_DUCKED;
-                       else if(getstati(STAT_VIEWHEIGHT) == PL_CROUCH_VIEW_OFS_z)
-                               self.pmove_flags |= PMF_DUCKED;
+                       if(getstati(STAT_VIEWHEIGHT) == PL_VIEW_OFS.z)
+                               self.flags &= ~FL_DUCKED;
+                       else if(getstati(STAT_VIEWHEIGHT) == PL_CROUCH_VIEW_OFS.z)
+                               self.flags |= FL_DUCKED;
 
                        // get onground state from the server
                        if(pmove_onground)
-                               self.pmove_flags |= PMF_ONGROUND;
+                               self.flags |= FL_ONGROUND;
                        else
-                               self.pmove_flags &= ~PMF_ONGROUND;
+                               self.flags &= ~FL_ONGROUND;
 
                        CSQCPlayer_SetMinsMaxs();
 
@@ -232,21 +255,21 @@ void CSQCPlayer_SetCamera()
                                v = v0;
                                csqcplayer_status = CSQCPLAYERSTATUS_PREDICTED;
                                CSQCPlayer_PredictTo(servercommandframe + 1, false);
-                               CSQCPlayer_SetPredictionError(self.origin - o, self.velocity - v, pmove_onground - !!(self.pmove_flags & PMF_ONGROUND));
+                               CSQCPlayer_SetPredictionError(self.origin - o, self.velocity - v, pmove_onground - !!(self.flags & FL_ONGROUND));
                                self.origin = o;
                                self.velocity = v;
 
                                // get crouch state from the server
-                               if(getstati(STAT_VIEWHEIGHT) == PL_VIEW_OFS_z)
-                                       self.pmove_flags &= ~PMF_DUCKED;
-                               else if(getstati(STAT_VIEWHEIGHT) == PL_CROUCH_VIEW_OFS_z)
-                                       self.pmove_flags |= PMF_DUCKED;
+                               if(getstati(STAT_VIEWHEIGHT) == PL_VIEW_OFS.z)
+                                       self.flags &= ~FL_DUCKED;
+                               else if(getstati(STAT_VIEWHEIGHT) == PL_CROUCH_VIEW_OFS.z)
+                                       self.flags |= FL_DUCKED;
 
                                // get onground state from the server
                                if(pmove_onground)
-                                       self.pmove_flags |= PMF_ONGROUND;
+                                       self.flags |= FL_ONGROUND;
                                else
-                                       self.pmove_flags &= ~PMF_ONGROUND;
+                                       self.flags &= ~FL_ONGROUND;
 
                                CSQCPlayer_SavePrediction();
                        }
@@ -254,10 +277,10 @@ void CSQCPlayer_SetCamera()
 
 #ifdef CSQCMODEL_SERVERSIDE_CROUCH
                        // get crouch state from the server (LAG)
-                       if(getstati(STAT_VIEWHEIGHT) == PL_VIEW_OFS_z)
-                               self.pmove_flags &= ~PMF_DUCKED;
-                       else if(getstati(STAT_VIEWHEIGHT) == PL_CROUCH_VIEW_OFS_z)
-                               self.pmove_flags |= PMF_DUCKED;
+                       if(getstati(STAT_VIEWHEIGHT) == PL_VIEW_OFS.z)
+                               self.flags &= ~FL_DUCKED;
+                       else if(getstati(STAT_VIEWHEIGHT) == PL_CROUCH_VIEW_OFS.z)
+                               self.flags |= FL_DUCKED;
 #endif
 
                        CSQCPlayer_SetMinsMaxs();
index 55e63cf56dfd144e886970d17f1b35967a6cccca..e427e6cf363f855510aaaa56b65b7df23384801a 100644 (file)
@@ -34,6 +34,8 @@ const int PMF_JUMP_HELD = 1;
 //const int PMF_DUCKED = 4;
 //const int PMF_ONGROUND = 8;
 
+const int FL_DUCKED = 524288;
+
 void CSQCPlayer_SetCamera();
 float CSQCPlayer_PreUpdate();
 float CSQCPlayer_PostUpdate();
index 202b840e07f2ccfd4d29fc27d8e413f4e473ccbf..21b7c5433b15224b67846b6c44ebe20bf997da34 100644 (file)
@@ -421,6 +421,7 @@ float( float b, ... ) max = #95;
 float(float minimum, float val, float maximum) bound = #96;
 float(float f, float f) pow = #97;
 entity(entity start, .float fld, float match) findfloat = #98;
+entity(entity start, .entity fld, entity match) findentity = #98;
 float(string s) checkextension = #99;
 // FrikaC and Telejano range #100-#199
 
index bf29683f6302b1bc889e68f09017a690ab0e55cd..d5e1f82eba4ab0ece3d7fb7995ec4b7838c455a3 100644 (file)
@@ -175,6 +175,9 @@ SKINBEGIN
        SKINVECTOR(COLOR_CLEARBUTTON_F, '1 1 1');
        SKINVECTOR(COLOR_CLEARBUTTON_C, '1 1 1');
 
+       // item: gametype list
+       SKINFLOAT(BOOL_GAMETYPELIST_ICON_BLUR, 1);
+
        // item: key grabber
        SKINVECTOR(COLOR_KEYGRABBER_TITLES, '1 1 1');
        SKINFLOAT(ALPHA_KEYGRABBER_TITLES, 1);
@@ -247,7 +250,6 @@ SKINBEGIN
        SKINVECTOR(COLOR_SERVERLIST_FAVORITE, '1 1 1');
        SKINFLOAT(ALPHA_SERVERLIST_IMPOSSIBLE, 0.7);
        SKINVECTOR(COLOR_SERVERLIST_IMPOSSIBLE, '0.3 0.3 0.3');
-       SKINSTRING(GFX_SERVERLIST_ICON, "icon");
        SKINFLOAT(ALPHA_SERVERLIST_ICON_NONPURE, 0.5);
 
        // item: server info
index 26845f0946430469120b374d59bfc0f0d785f7a3..030af6715339257b23ddb7d4a07531f1a489635c 100644 (file)
@@ -36,9 +36,11 @@ void XonoticGametypeList_configureXonoticGametypeList(entity me)
        me.configureXonoticListBox(me);
        me.nItems = GameType_GetCount();
 
-       // we want the pics mipmapped
-       for(int i = 0; i < GameType_GetCount(); ++i)
-               draw_PreloadPictureWithFlags(GameType_GetIcon(i), PRECACHE_PIC_MIPMAP);
+       if(SKINBOOL_GAMETYPELIST_ICON_BLUR)
+       {
+               for(int i = 0; i < GameType_GetCount(); ++i)
+                       draw_PreloadPictureWithFlags(GameType_GetIcon(i), PRECACHE_PIC_MIPMAP);
+       }
 
        me.loadCvars(me);
 }
index 8f8c2e58684ac0efdfba09f5098866a27d1d550e..8d53be8fb5d5a0a9d4c0e47efc01c14627773e06 100644 (file)
@@ -1177,57 +1177,46 @@ void XonoticServerList_drawListBoxItem(entity me, int i, vector absSize, bool is
        iconPos_x = (me.columnIconsSize - 3 * iconSize.x) * 0.5;
        iconPos_y = (1 - iconSize.y) * 0.5;
 
-       string n;
-
-       if (!(me.seenIPv4 && me.seenIPv6))
-       {
-               iconPos.x += iconSize.x * 0.5;
-       }
-       else if(me.seenIPv4 && me.seenIPv6)
+       // IP
+       if(me.seenIPv4 && me.seenIPv6)
        {
-               n = string_null;
                if(isv6)
-                       draw_PreloadPictureWithFlags(n = strcat(SKINGFX_SERVERLIST_ICON, "_ipv6"), 0); // PRECACHE_PIC_MIPMAP
+                       draw_Picture(iconPos, "icon_ipv6", iconSize, '1 1 1', 1);
                else if(isv4)
-                       draw_PreloadPictureWithFlags(n = strcat(SKINGFX_SERVERLIST_ICON, "_ipv4"), 0); // PRECACHE_PIC_MIPMAP
-               if(n)
-                       draw_Picture(iconPos, n, iconSize, '1 1 1', 1);
-               iconPos.x += iconSize.x;
+                       draw_Picture(iconPos, "icon_ipv4", iconSize, '1 1 1', 1);
        }
 
+       iconPos.x += iconSize.x;
+
+       // AES
        if(q > 0)
-       {
-               draw_PreloadPictureWithFlags(n = strcat(SKINGFX_SERVERLIST_ICON, "_aeslevel", ftos(q)), 0); // PRECACHE_PIC_MIPMAP
-               draw_Picture(iconPos, n, iconSize, '1 1 1', 1);
-       }
+               draw_Picture(iconPos, strcat("icon_aeslevel", ftos(q)), iconSize, '1 1 1', 1);
+
        iconPos.x += iconSize.x;
 
+       // Mod
        if(modname == "Xonotic")
        {
                if(pure == 0)
-               {
-                       draw_PreloadPictureWithFlags(n = strcat(SKINGFX_SERVERLIST_ICON, "_pure1"), PRECACHE_PIC_MIPMAP);
-                       draw_Picture(iconPos, n, iconSize, '1 1 1', 1);
-               }
+                       draw_Picture(iconPos, "icon_pure1", iconSize, '1 1 1', 1);
        }
        else
        {
-               draw_PreloadPictureWithFlags(n = strcat(SKINGFX_SERVERLIST_ICON, "_mod_", modname), PRECACHE_PIC_MIPMAP);
-               if(draw_PictureSize(n) == '0 0 0')
-                       draw_PreloadPictureWithFlags(n = strcat(SKINGFX_SERVERLIST_ICON, "_mod_"), PRECACHE_PIC_MIPMAP);
+               string icon = strcat("icon_mod_", modname);
+               if(draw_PictureSize(icon) == '0 0 0')
+                       icon = "icon_mod_";
+
                if(pure == 0)
-                       draw_Picture(iconPos, n, iconSize, '1 1 1', 1);
+                       draw_Picture(iconPos, icon, iconSize, '1 1 1', 1);
                else
-                       draw_Picture(iconPos, n, iconSize, '1 1 1', SKINALPHA_SERVERLIST_ICON_NONPURE);
+                       draw_Picture(iconPos, icon, iconSize, '1 1 1', SKINALPHA_SERVERLIST_ICON_NONPURE);
        }
+
        iconPos.x += iconSize.x;
 
+       // Stats
        if(sflags >= 0 && (sflags & SERVERFLAG_PLAYERSTATS))
-       {
-               draw_PreloadPictureWithFlags(n = strcat(SKINGFX_SERVERLIST_ICON, "_stats1"), 0); // PRECACHE_PIC_MIPMAP
-               draw_Picture(iconPos, n, iconSize, '1 1 1', 1);
-       }
-       iconPos.x += iconSize.x;
+               draw_Picture(iconPos, "icon_stats1", iconSize, '1 1 1', 1);
 
        // --------------
        //  RENDER TEXT
index e6bc6202e0981b75b127c4412b976d413c8b2cea..51b14aa0812e2c2af73a354b7cd204608beb736c 100644 (file)
@@ -621,13 +621,14 @@ string autocvar_sv_eventlog_files_nameprefix;
 string autocvar_sv_eventlog_files_namesuffix;
 bool autocvar_sv_eventlog_files_timestamps;
 float autocvar_sv_friction_on_land;
+var float autocvar_sv_friction_slick = 0.5;
 float autocvar_sv_gameplayfix_q2airaccelerate;
 int autocvar_sv_gentle;
 #define autocvar_sv_gravity cvar("sv_gravity")
 string autocvar_sv_intermission_cdtrack;
-string autocvar_sv_jumpspeedcap_max;
+float autocvar_sv_jumpspeedcap_max;
 float autocvar_sv_jumpspeedcap_max_disable_on_ramps;
-string autocvar_sv_jumpspeedcap_min;
+float autocvar_sv_jumpspeedcap_min;
 float autocvar_sv_jumpvelocity;
 bool autocvar_sv_logscores_bots;
 bool autocvar_sv_logscores_console;
@@ -700,7 +701,8 @@ int autocvar_timelimit_overtimes;
 float autocvar_timelimit_suddendeath;
 #define autocvar_utf8_enable cvar("utf8_enable")
 bool autocvar_waypoint_benchmark;
-bool autocvar_sv_gameplayfix_gravityunaffectedbyticrate;
+float autocvar_sv_gameplayfix_gravityunaffectedbyticrate;
+bool autocvar_sv_gameplayfix_upwardvelocityclearsongroundflag;
 float autocvar_g_trueaim_minrange;
 bool autocvar_g_debug_defaultsounds;
 float autocvar_g_grab_range;
@@ -866,5 +868,5 @@ float autocvar_g_buffs_vampire_damage_steal;
 float autocvar_g_buffs_invisible_alpha;
 float autocvar_g_buffs_flight_gravity;
 float autocvar_g_buffs_jump_height;
-
+float autocvar_sv_player_scale;
 #endif
index dfb615d03715ecd4e3ee5f5b5fb2ea32a6c4b497..9fa1667099b4b3ac017cde9987004d58dd082192 100644 (file)
@@ -9,6 +9,8 @@
 
 #include "../../../common/constants.qh"
 
+#include "../../../common/triggers/trigger/jumppads.qh"
+
 #include "../../../warpzonelib/common.qh"
 
 void havocbot_ai()
@@ -475,7 +477,7 @@ void havocbot_movetogoal()
 
                // Flying
                self.BUTTON_HOOK = true;
-               if(self.navigation_jetpack_point.z - PL_MAX_z + PL_MIN_z < self.origin.z)
+               if(self.navigation_jetpack_point.z - PL_MAX.z + PL_MIN.z < self.origin.z)
                {
                        self.movement_x = dir * v_forward * maxspeed;
                        self.movement_y = dir * v_right * maxspeed;
index 3c3f135cc4d35be6c9b2af1db02d5aada325983d..ad0a8d761d4bdfff7a8aca640ebc8c6ddf8b8d51 100644 (file)
@@ -7,6 +7,7 @@
 #include "../t_items.qh"
 
 #include "../../common/constants.qh"
+#include "../../common/triggers/trigger/jumppads.qh"
 
 void bot_debug(string input)
 {
@@ -397,7 +398,7 @@ entity navigation_findnearestwaypoint_withdist_except(entity ent, float walkfrom
        }
 
        org = ent.origin + 0.5 * (ent.mins + ent.maxs);
-       org.z = ent.origin.z + ent.mins.z - PL_MIN_z; // player height
+       org.z = ent.origin.z + ent.mins.z - PL_MIN.z; // player height
        // TODO possibly make other code have the same support for bboxes
        if(ent.tag_entity)
                org = org + ent.tag_entity.origin;
@@ -720,7 +721,7 @@ void navigation_routerating(entity e, float f, float rangebias)
                        float zdistance, xydistance, cost, t, fuel;
                        vector down, npa, npb;
 
-                       down = '0 0 -1' * (PL_MAX_z - PL_MIN_z) * 10;
+                       down = '0 0 -1' * (PL_MAX.z - PL_MIN.z) * 10;
 
                        do{
                                npa = pointa + down;
index 9e45474dadef57aaee49708f405cd32eaf944b73..627c622d2554de6f9b3a6015f5d8ffaebb2e7066 100644 (file)
@@ -208,7 +208,7 @@ void waypoint_think()
                        navigation_testtracewalk = 0;
                        if (!self.wpisbox)
                        {
-                               tracebox(sv - PL_MIN_z * '0 0 1', PL_MIN, PL_MAX, sv, false, self);
+                               tracebox(sv - PL_MIN.z * '0 0 1', PL_MIN, PL_MAX, sv, false, self);
                                if (!trace_startsolid)
                                {
                                        //dprint("sv deviation", vtos(trace_endpos - sv), "\n");
@@ -217,7 +217,7 @@ void waypoint_think()
                        }
                        if (!e.wpisbox)
                        {
-                               tracebox(ev - PL_MIN_z * '0 0 1', PL_MIN, PL_MAX, ev, false, e);
+                               tracebox(ev - PL_MIN.z * '0 0 1', PL_MIN, PL_MAX, ev, false, e);
                                if (!trace_startsolid)
                                {
                                        //dprint("ev deviation", vtos(trace_endpos - ev), "\n");
@@ -731,7 +731,7 @@ float waypoint_loadall()
 
 vector waypoint_fixorigin(vector position)
 {
-       tracebox(position + '0 0 1' * (1 - PL_MIN_z), PL_MIN, PL_MAX, position + '0 0 -512', MOVE_NOMONSTERS, world);
+       tracebox(position + '0 0 1' * (1 - PL_MIN.z), PL_MIN, PL_MAX, position + '0 0 -512', MOVE_NOMONSTERS, world);
        if(trace_fraction < 1)
                position = trace_endpos;
        //traceline(position, position + '0 0 -512', MOVE_NOMONSTERS, world);
index 2c091d56e15f19bed4cda5890861ef29fae33db2..650855989edebccc0ee31cf1aba8984b6a634b2f 100644 (file)
@@ -121,7 +121,6 @@ void CampaignPreInit()
        strunzone(title);
 }
 
-string GetMapname();
 void CampaignPostInit()
 {
        // now some sanity checks
index d9eb75915072bdd8d118d4dafb51804f1aba98c2..0c6fc7f39d483e7b0b931e18f25360fbc4730392 100644 (file)
@@ -3,7 +3,7 @@
 
 #include "g_damage.qh"
 #include "race.qh"
-#include "t_teleporters.qh"
+#include "../common/triggers/teleporters.qh"
 
 #include "mutators/mutators_include.qh"
 
 
 #include "../common/weapons/all.qh"
 
+#include "../common/triggers/subs.qh"
+
+#include "../common/triggers/func/breakable.qh"
+
 #include "../csqcmodellib/sv_model.qh"
 
 #include "../warpzonelib/anglestransform.qh"
index b73eebbf4a021b95d270a97fb81ae62586d44287..33491ff0e7bfa49cbbaae33e434094f83c11c084 100644 (file)
@@ -6,17 +6,14 @@
 #include "cl_impulse.qh"
 #include "cl_player.qh"
 #include "ent_cs.qh"
-#include "g_subs.qh"
 #include "ipban.qh"
 #include "miscfunctions.qh"
 #include "portals.qh"
 #include "teamplay.qh"
 #include "playerdemo.qh"
-#include "secret.qh"
 #include "spawnpoints.qh"
 #include "g_damage.qh"
 #include "g_hook.qh"
-#include "t_teleporters.qh"
 #include "command/common.qh"
 #include "cheats.qh"
 #include "g_world.qh"
 #include "weapons/weaponsystem.qh"
 
 #include "../common/net_notice.qh"
+#include "../common/physics.qh"
+
+#include "../common/triggers/subs.qh"
+#include "../common/triggers/triggers.qh"
+#include "../common/triggers/trigger/secret.qh"
 
 #include "../common/items/inventory.qh"
 
@@ -532,7 +534,7 @@ void PutClientInServer (void)
                self.respawn_flags = 0;
                self.respawn_time = 0;
                self.stat_respawn_time = 0;
-               self.scale = 0;
+               self.scale = autocvar_sv_player_scale;
                self.fade_time = 0;
                self.pain_frame = 0;
                self.pain_finished = 0;
@@ -1679,55 +1681,17 @@ void SetZoomState(float z)
 
 void GetPressedKeys(void) {
        MUTATOR_CALLHOOK(GetPressedKeys);
-       if (self.movement.x > 0) // get if movement keys are pressed
-       {       // forward key pressed
-               self.pressedkeys |= KEY_FORWARD;
-               self.pressedkeys &= ~KEY_BACKWARD;
-       }
-       else if (self.movement.x < 0)
-       {       // backward key pressed
-               self.pressedkeys |= KEY_BACKWARD;
-               self.pressedkeys &= ~KEY_FORWARD;
-       }
-       else
-       {       // no x input
-               self.pressedkeys &= ~KEY_FORWARD;
-               self.pressedkeys &= ~KEY_BACKWARD;
-       }
-
-       if (self.movement.y > 0)
-       {       // right key pressed
-               self.pressedkeys |= KEY_RIGHT;
-               self.pressedkeys &= ~KEY_LEFT;
-       }
-       else if (self.movement.y < 0)
-       {       // left key pressed
-               self.pressedkeys |= KEY_LEFT;
-               self.pressedkeys &= ~KEY_RIGHT;
-       }
-       else
-       {       // no y input
-               self.pressedkeys &= ~KEY_RIGHT;
-               self.pressedkeys &= ~KEY_LEFT;
-       }
-
-       if (self.BUTTON_JUMP) // get if jump and crouch keys are pressed
-               self.pressedkeys |= KEY_JUMP;
-       else
-               self.pressedkeys &= ~KEY_JUMP;
-       if (self.BUTTON_CROUCH)
-               self.pressedkeys |= KEY_CROUCH;
-       else
-               self.pressedkeys &= ~KEY_CROUCH;
-
-       if (self.BUTTON_ATCK)
-               self.pressedkeys |= KEY_ATCK;
-       else
-               self.pressedkeys &= ~KEY_ATCK;
-       if (self.BUTTON_ATCK2)
-               self.pressedkeys |= KEY_ATCK2;
-       else
-               self.pressedkeys &= ~KEY_ATCK2;
+       #define X(var,bit,flag) (flag ? var |= bit : var &= ~bit)
+       X(self.pressedkeys, KEY_FORWARD,        self.movement_x > 0);
+       X(self.pressedkeys, KEY_BACKWARD,       self.movement_x < 0);
+       X(self.pressedkeys, KEY_RIGHT,          self.movement_y > 0);
+       X(self.pressedkeys, KEY_LEFT,           self.movement_y < 0);
+
+       X(self.pressedkeys, KEY_JUMP,           PHYS_INPUT_BUTTON_JUMP(self));
+       X(self.pressedkeys, KEY_CROUCH,         PHYS_INPUT_BUTTON_CROUCH(self));
+       X(self.pressedkeys, KEY_ATCK,           PHYS_INPUT_BUTTON_ATCK(self));
+       X(self.pressedkeys, KEY_ATCK2,          PHYS_INPUT_BUTTON_ATCK2(self));
+       #undef X
 }
 
 /*
@@ -2500,8 +2464,8 @@ void PlayerPreThink (void)
                        if (!self.crouch)
                        {
                                self.crouch = true;
-                               self.view_ofs = PL_CROUCH_VIEW_OFS;
-                               setsize (self, PL_CROUCH_MIN, PL_CROUCH_MAX);
+                               self.view_ofs = self.stat_pl_crouch_view_ofs;
+                               setsize (self, self.stat_pl_crouch_min, self.stat_pl_crouch_max);
                                // setanim(self, self.anim_duck, false, true, true); // this anim is BROKEN anyway
                        }
                }
@@ -2509,12 +2473,12 @@ void PlayerPreThink (void)
                {
                        if (self.crouch)
                        {
-                               tracebox(self.origin, PL_MIN, PL_MAX, self.origin, false, self);
+                               tracebox(self.origin, self.stat_pl_min, self.stat_pl_max, self.origin, false, self);
                                if (!trace_startsolid)
                                {
                                        self.crouch = false;
-                                       self.view_ofs = PL_VIEW_OFS;
-                                       setsize (self, PL_MIN, PL_MAX);
+                                       self.view_ofs = self.stat_pl_view_ofs;
+                                       setsize (self, self.stat_pl_min, self.stat_pl_max);
                                }
                        }
                }
diff --git a/qcsrc/server/cl_physics.qc b/qcsrc/server/cl_physics.qc
deleted file mode 100644 (file)
index 8bc8660..0000000
+++ /dev/null
@@ -1,1395 +0,0 @@
-#include "_all.qh"
-#include "bot/bot.qh"
-#include "g_damage.qh"
-
-#if defined(CSQC)
-#elif defined(MENUQC)
-#elif defined(SVQC)
-       #include "../dpdefs/progsdefs.qh"
-    #include "../dpdefs/dpextensions.qh"
-    #include "../warpzonelib/mathlib.qh"
-    #include "../warpzonelib/server.qh"
-    #include "../common/constants.qh"
-    #include "../common/util.qh"
-    #include "../common/animdecide.qh"
-    #include "../common/monsters/sv_monsters.qh"
-    #include "../common/weapons/all.qh"
-    #include "t_items.qh"
-    #include "autocvars.qh"
-    #include "defs.qh"
-    #include "../common/notifications.qh"
-    #include "mutators/mutators_include.qh"
-    #include "../common/mapinfo.qh"
-    #include "../csqcmodellib/sv_model.qh"
-    #include "anticheat.qh"
-    #include "cheats.qh"
-    #include "g_hook.qh"
-    #include "race.qh"
-    #include "playerdemo.qh"
-#endif
-
-.float race_penalty;
-.float restart_jump;
-
-.float ladder_time;
-.entity ladder_entity;
-.float gravity;
-.float swamp_slowdown;
-.int lastflags;
-.float lastground;
-.float wasFlying;
-.float spectatorspeed;
-
-// client side physics
-bool Physics_Valid(string thecvar)
-{
-       if(!autocvar_g_physics_clientselect) { return false; }
-
-       string l = strcat(" ", autocvar_g_physics_clientselect_options, " ");
-
-       if(strstrofs(l, strcat(" ", thecvar, " "), 0) >= 0)
-               return true;
-
-       return false;
-}
-
-float Physics_ClientOption(entity pl, string option)
-{
-       if(Physics_Valid(pl.cvar_cl_physics))
-       {
-               string var = sprintf("g_physics_%s_%s", pl.cvar_cl_physics, option);
-               if(cvar_type(var) & CVAR_TYPEFLAG_EXISTS)
-                       return cvar(var);
-       }
-       if(autocvar_g_physics_clientselect && autocvar_g_physics_clientselect_default)
-       {
-               string var = sprintf("g_physics_%s_%s", autocvar_g_physics_clientselect_default, option);
-               if(cvar_type(var) & CVAR_TYPEFLAG_EXISTS)
-                       return cvar(var);
-       }
-       return cvar(strcat("sv_", option));
-}
-
-/*
-=============
-PlayerJump
-
-When you press the jump key
-returns true if handled
-=============
-*/
-float PlayerJump (void)
-{
-       if(self.frozen)
-               return true; // no jumping in freezetag when frozen
-
-       if(self.player_blocked)
-               return true; // no jumping while blocked
-
-       bool doublejump = false;
-       float mjumpheight = self.stat_sv_jumpvelocity;
-
-       player_multijump = doublejump;
-       player_jumpheight = mjumpheight;
-       if(MUTATOR_CALLHOOK(PlayerJump))
-               return true;
-
-       doublejump = player_multijump;
-       mjumpheight = player_jumpheight;
-
-       if (autocvar_sv_doublejump)
-       {
-               tracebox(self.origin + '0 0 0.01', self.mins, self.maxs, self.origin - '0 0 0.01', MOVE_NORMAL, self);
-               if (trace_fraction < 1 && trace_plane_normal.z > 0.7)
-               {
-                       doublejump = true;
-
-                       // we MUST clip velocity here!
-                       float f;
-                       f = self.velocity * trace_plane_normal;
-                       if(f < 0)
-                               self.velocity -= f * trace_plane_normal;
-               }
-       }
-
-       if (self.waterlevel >= WATERLEVEL_SWIMMING)
-       {
-               self.velocity_z = self.stat_sv_maxspeed * 0.7;
-               return true;
-       }
-
-       if (!doublejump)
-               if (!(self.flags & FL_ONGROUND))
-                       return !(self.flags & FL_JUMPRELEASED);
-
-       if(self.cvar_cl_movement_track_canjump)
-               if (!(self.flags & FL_JUMPRELEASED))
-                       return true;
-
-       // sv_jumpspeedcap_min/sv_jumpspeedcap_max act as baseline
-       // velocity bounds.  Final velocity is bound between (jumpheight *
-       // min + jumpheight) and (jumpheight * max + jumpheight);
-
-       if(autocvar_sv_jumpspeedcap_min != "")
-       {
-               float minjumpspeed;
-
-               minjumpspeed = mjumpheight * stof(autocvar_sv_jumpspeedcap_min);
-
-               if (self.velocity.z < minjumpspeed)
-                       mjumpheight += minjumpspeed - self.velocity.z;
-       }
-
-       if(autocvar_sv_jumpspeedcap_max != "")
-       {
-               // don't do jump speedcaps on ramps to preserve old xonotic ramjump style
-               tracebox(self.origin + '0 0 0.01', self.mins, self.maxs, self.origin - '0 0 0.01', MOVE_NORMAL, self);
-
-               if(!(trace_fraction < 1 && trace_plane_normal.z < 0.98 && autocvar_sv_jumpspeedcap_max_disable_on_ramps))
-               {
-                       float maxjumpspeed;
-
-                       maxjumpspeed = mjumpheight * stof(autocvar_sv_jumpspeedcap_max);
-
-                       if (self.velocity.z > maxjumpspeed)
-                               mjumpheight -= self.velocity.z - maxjumpspeed;
-               }
-       }
-
-       if(!(self.lastflags & FL_ONGROUND))
-       {
-               if(autocvar_speedmeter)
-                       dprint(strcat("landing velocity: ", vtos(self.velocity), " (abs: ", ftos(vlen(self.velocity)), ")\n"));
-               if(self.lastground < time - 0.3)
-               {
-                       self.velocity_x *= (1 - autocvar_sv_friction_on_land);
-                       self.velocity_y *= (1 - autocvar_sv_friction_on_land);
-               }
-               if(self.jumppadcount > 1)
-                       dprint(strcat(ftos(self.jumppadcount), "x jumppad combo\n"));
-               self.jumppadcount = 0;
-       }
-
-       self.velocity_z = self.velocity.z + mjumpheight;
-       self.oldvelocity_z = self.velocity.z;
-
-       self.flags &= ~FL_ONGROUND;
-       self.flags &= ~FL_JUMPRELEASED;
-
-       animdecide_setaction(self, ANIMACTION_JUMP, true);
-
-       if(autocvar_g_jump_grunt)
-               PlayerSound(playersound_jump, CH_PLAYER, VOICETYPE_PLAYERSOUND);
-
-       self.restart_jump = -1; // restart jump anim next time
-       // value -1 is used to not use the teleport bit (workaround for tiny hitch when re-jumping)
-       return true;
-}
-void CheckWaterJump()
-{
-       vector start, end;
-
-// check for a jump-out-of-water
-       makevectors (self.angles);
-       start = self.origin;
-       start.z = start.z + 8;
-       v_forward.z = 0;
-       normalize(v_forward);
-       end = start + v_forward*24;
-       traceline (start, end, true, self);
-       if (trace_fraction < 1)
-       {       // solid at waist
-               start.z = start.z + self.maxs.z - 8;
-               end = start + v_forward*24;
-               self.movedir = trace_plane_normal * -50;
-               traceline (start, end, true, self);
-               if (trace_fraction == 1)
-               {       // open at eye level
-                       self.flags |= FL_WATERJUMP;
-                       self.velocity_z = 225;
-                       self.flags &= ~FL_JUMPRELEASED;
-                       self.teleport_time = time + 2;  // safety net
-                       return;
-               }
-       }
-}
-
-.float jetpack_stopped;
-// Hack: shouldn't need to know about this
-.float multijump_count;
-void CheckPlayerJump()
-{
-       float was_flying = self.items & IT_USING_JETPACK;
-
-       if (self.cvar_cl_jetpack_jump < 2)
-               self.items &= ~IT_USING_JETPACK;
-
-       if (self.BUTTON_JUMP || self.BUTTON_JETPACK)
-       {
-               float air_jump = !PlayerJump() || self.multijump_count > 0; // PlayerJump() has important side effects
-               float activate = self.cvar_cl_jetpack_jump && air_jump && self.BUTTON_JUMP || self.BUTTON_JETPACK;
-               float has_fuel = !autocvar_g_jetpack_fuel || self.ammo_fuel || self.items & IT_UNLIMITED_WEAPON_AMMO;
-               if (!(self.items & IT_JETPACK)) { }
-               else if (self.jetpack_stopped) { }
-               else if (!has_fuel)
-               {
-                       if (was_flying) // TODO: ran out of fuel message
-                               Send_Notification(NOTIF_ONE, self, MSG_INFO, INFO_JETPACK_NOFUEL);
-                       else if (activate)
-                               Send_Notification(NOTIF_ONE, self, MSG_INFO, INFO_JETPACK_NOFUEL);
-                       self.jetpack_stopped = true;
-                       self.items &= ~IT_USING_JETPACK;
-               }
-               else if (activate && !self.frozen)
-                       self.items |= IT_USING_JETPACK;
-       }
-       else
-       {
-               self.jetpack_stopped = false;
-               self.items &= ~IT_USING_JETPACK;
-       }
-       if (!self.BUTTON_JUMP)
-               self.flags |= FL_JUMPRELEASED;
-
-       if (self.waterlevel == WATERLEVEL_SWIMMING)
-               CheckWaterJump ();
-}
-
-float racecar_angle(float forward, float down)
-{
-       float ret, angle_mult;
-
-       if(forward < 0)
-       {
-               forward = -forward;
-               down = -down;
-       }
-
-       ret = vectoyaw('0 1 0' * down + '1 0 0' * forward);
-
-       angle_mult = forward / (800 + forward);
-
-       if(ret > 180)
-               return ret * angle_mult + 360 * (1 - angle_mult);
-       else
-               return ret * angle_mult;
-}
-
-void RaceCarPhysics()
-{
-       // using this move type for "big rigs"
-       // the engine does not push the entity!
-
-       float accel, steer, f, myspeed, steerfactor;
-       vector angles_save, rigvel;
-
-       angles_save = self.angles;
-       accel = bound(-1, self.movement.x / self.stat_sv_maxspeed, 1);
-       steer = bound(-1, self.movement.y / self.stat_sv_maxspeed, 1);
-
-       if(g_bugrigs_reverse_speeding)
-       {
-               if(accel < 0)
-               {
-                       // back accel is DIGITAL
-                       // to prevent speedhack
-                       if(accel < -0.5)
-                               accel = -1;
-                       else
-                               accel = 0;
-               }
-       }
-
-       self.angles_x = 0;
-       self.angles_z = 0;
-       makevectors(self.angles); // new forward direction!
-
-       if(self.flags & FL_ONGROUND || g_bugrigs_air_steering)
-       {
-               float upspeed, accelfactor;
-
-               myspeed = self.velocity * v_forward;
-               upspeed = self.velocity * v_up;
-
-               // responsiveness factor for steering and acceleration
-               f = 1 / (1 + pow(max(-myspeed, myspeed) / g_bugrigs_speed_ref, g_bugrigs_speed_pow));
-               //MAXIMA: f(v) := 1 / (1 + (v / g_bugrigs_speed_ref) ^ g_bugrigs_speed_pow);
-
-               if(myspeed < 0 && g_bugrigs_reverse_spinning)
-                       steerfactor = -myspeed * g_bugrigs_steer;
-               else
-                       steerfactor = -myspeed * f * g_bugrigs_steer;
-
-               if(myspeed < 0 && g_bugrigs_reverse_speeding)
-                       accelfactor = g_bugrigs_accel;
-               else
-                       accelfactor = f * g_bugrigs_accel;
-               //MAXIMA: accel(v) := f(v) * g_bugrigs_accel;
-
-               if(accel < 0)
-               {
-                       if(myspeed > 0)
-                       {
-                               myspeed = max(0, myspeed - frametime * (g_bugrigs_friction_floor - g_bugrigs_friction_brake * accel));
-                       }
-                       else
-                       {
-                               if(!g_bugrigs_reverse_speeding)
-                                       myspeed = min(0, myspeed + frametime * g_bugrigs_friction_floor);
-                       }
-               }
-               else
-               {
-                       if(myspeed >= 0)
-                       {
-                               myspeed = max(0, myspeed - frametime * g_bugrigs_friction_floor);
-                       }
-                       else
-                       {
-                               if(g_bugrigs_reverse_stopping)
-                                       myspeed = 0;
-                               else
-                                       myspeed = min(0, myspeed + frametime * (g_bugrigs_friction_floor + g_bugrigs_friction_brake * accel));
-                       }
-               }
-               // terminal velocity = velocity at which 50 == accelfactor, that is, 1549 units/sec
-               //MAXIMA: friction(v) := g_bugrigs_friction_floor;
-
-               self.angles_y += steer * frametime * steerfactor; // apply steering
-               makevectors(self.angles); // new forward direction!
-
-               myspeed += accel * accelfactor * frametime;
-
-               rigvel = myspeed * v_forward + '0 0 1' * upspeed;
-       }
-       else
-       {
-               myspeed = vlen(self.velocity);
-
-               // responsiveness factor for steering and acceleration
-               f = 1 / (1 + pow(max(0, myspeed / g_bugrigs_speed_ref), g_bugrigs_speed_pow));
-               steerfactor = -myspeed * f;
-               self.angles_y += steer * frametime * steerfactor; // apply steering
-
-               rigvel = self.velocity;
-               makevectors(self.angles); // new forward direction!
-       }
-
-       rigvel = rigvel * max(0, 1 - vlen(rigvel) * g_bugrigs_friction_air * frametime);
-       //MAXIMA: airfriction(v) := v * v * g_bugrigs_friction_air;
-       //MAXIMA: total_acceleration(v) := accel(v) - friction(v) - airfriction(v);
-       //MAXIMA: solve(total_acceleration(v) = 0, v);
-
-       if(g_bugrigs_planar_movement)
-       {
-               vector rigvel_xy, neworigin, up;
-               float mt;
-
-               rigvel.z -= frametime * autocvar_sv_gravity; // 4x gravity plays better
-               rigvel_xy = vec2(rigvel);
-
-               if(g_bugrigs_planar_movement_car_jumping)
-                       mt = MOVE_NORMAL;
-               else
-                       mt = MOVE_NOMONSTERS;
-
-               tracebox(self.origin, self.mins, self.maxs, self.origin + '0 0 1024', mt, self);
-               up = trace_endpos - self.origin;
-
-               // BUG RIGS: align the move to the surface instead of doing collision testing
-               // can we move?
-               tracebox(trace_endpos, self.mins, self.maxs, trace_endpos + rigvel_xy * frametime, mt, self);
-
-               // align to surface
-               tracebox(trace_endpos, self.mins, self.maxs, trace_endpos - up + '0 0 1' * rigvel.z * frametime, mt, self);
-
-               if(trace_fraction < 0.5)
-               {
-                       trace_fraction = 1;
-                       neworigin = self.origin;
-               }
-               else
-                       neworigin = trace_endpos;
-
-               if(trace_fraction < 1)
-               {
-                       // now set angles_x so that the car points parallel to the surface
-                       self.angles = vectoangles(
-                                       '1 0 0' * v_forward.x * trace_plane_normal.z
-                                       +
-                                       '0 1 0' * v_forward.y * trace_plane_normal.z
-                                       +
-                                       '0 0 1' * -(v_forward.x * trace_plane_normal.x + v_forward.y * trace_plane_normal.y)
-                                       );
-                       self.flags |= FL_ONGROUND;
-               }
-               else
-               {
-                       // now set angles_x so that the car points forward, but is tilted in velocity direction
-                       self.flags &= ~FL_ONGROUND;
-               }
-
-               self.velocity = (neworigin - self.origin) * (1.0 / frametime);
-               self.movetype = MOVETYPE_NOCLIP;
-       }
-       else
-       {
-               rigvel.z -= frametime * autocvar_sv_gravity; // 4x gravity plays better
-               self.velocity = rigvel;
-               self.movetype = MOVETYPE_FLY;
-       }
-
-       trace_fraction = 1;
-       tracebox(self.origin, self.mins, self.maxs, self.origin - '0 0 4', MOVE_NORMAL, self);
-       if(trace_fraction != 1)
-       {
-               self.angles = vectoangles2(
-                               '1 0 0' * v_forward.x * trace_plane_normal.z
-                               +
-                               '0 1 0' * v_forward.y * trace_plane_normal.z
-                               +
-                               '0 0 1' * -(v_forward.x * trace_plane_normal.x + v_forward.y * trace_plane_normal.y),
-                               trace_plane_normal
-                               );
-       }
-       else
-       {
-               vector vel_local;
-
-               vel_local.x = v_forward * self.velocity;
-               vel_local.y = v_right * self.velocity;
-               vel_local.z = v_up * self.velocity;
-
-               self.angles_x = racecar_angle(vel_local.x, vel_local.z);
-               self.angles_z = racecar_angle(-vel_local.y, vel_local.z);
-       }
-
-       // smooth the angles
-       vector vf1, vu1, smoothangles;
-       makevectors(self.angles);
-       f = bound(0, frametime * g_bugrigs_angle_smoothing, 1);
-       if(f == 0)
-               f = 1;
-       vf1 = v_forward * f;
-       vu1 = v_up * f;
-       makevectors(angles_save);
-       vf1 = vf1 + v_forward * (1 - f);
-       vu1 = vu1 + v_up * (1 - f);
-       smoothangles = vectoangles2(vf1, vu1);
-       self.angles_x = -smoothangles.x;
-       self.angles_z =  smoothangles.z;
-}
-
-float IsMoveInDirection(vector mv, float angle) // key mix factor
-{
-       if(mv.x == 0 && mv.y == 0)
-               return 0; // avoid division by zero
-       angle -= RAD2DEG * atan2(mv.y, mv.x);
-       angle = remainder(angle, 360) / 45;
-       if(angle >  1)
-               return 0;
-       if(angle < -1)
-               return 0;
-       return 1 - fabs(angle);
-}
-
-float GeomLerp(float a, float lerp, float b)
-{
-       if(a == 0)
-       {
-               if(lerp < 1)
-                       return 0;
-               else
-                       return b;
-       }
-       if(b == 0)
-       {
-               if(lerp > 0)
-                       return 0;
-               else
-                       return a;
-       }
-       return a * pow(fabs(b / a), lerp);
-}
-
-void CPM_PM_Aircontrol(vector wishdir, float wishspeed)
-{
-       float zspeed, xyspeed, dot, k;
-
-#if 0
-       // this doesn't play well with analog input
-       if(self.movement_x == 0 || self.movement.y != 0)
-               return; // can't control movement if not moving forward or backward
-       k = 32;
-#else
-       k = 32 * (2 * IsMoveInDirection(self.movement, 0) - 1);
-       if(k <= 0)
-               return;
-#endif
-
-       k *= bound(0, wishspeed / self.stat_sv_maxairspeed, 1);
-
-       zspeed = self.velocity.z;
-       self.velocity_z = 0;
-       xyspeed = vlen(self.velocity); self.velocity = normalize(self.velocity);
-
-       dot = self.velocity * wishdir;
-
-       if(dot > 0) // we can't change direction while slowing down
-       {
-               k *= pow(dot, self.stat_sv_aircontrol_power)*frametime;
-               xyspeed = max(0, xyspeed - self.stat_sv_aircontrol_penalty * sqrt(max(0, 1 - dot*dot)) * k/32);
-               k *= self.stat_sv_aircontrol;
-               self.velocity = normalize(self.velocity * xyspeed + wishdir * k);
-       }
-
-       self.velocity = self.velocity * xyspeed;
-       self.velocity_z = zspeed;
-}
-
-float AdjustAirAccelQW(float accelqw, float factor)
-{
-       return copysign(bound(0.000001, 1 - (1 - fabs(accelqw)) * factor, 1), accelqw);
-}
-
-// example config for alternate speed clamping:
-//   sv_airaccel_qw 0.8
-//   sv_airaccel_sideways_friction 0
-//   prvm_globalset server speedclamp_mode 1
-//     (or 2)
-void PM_Accelerate(vector wishdir, float wishspeed, float wishspeed0, float accel, float accelqw, float stretchfactor, float sidefric, float speedlimit)
-{
-       float vel_straight;
-       float velZ;
-       vector vel_perpend;
-       float step;
-
-       vector vel_xy;
-       float vel_xy_current;
-       float vel_xy_backward, vel_xy_forward;
-       float speedclamp;
-
-       if(stretchfactor > 0)
-               speedclamp = stretchfactor;
-       else if(accelqw < 0)
-               speedclamp = 1; // full clamping, no stretch
-       else
-               speedclamp = -1; // no clamping
-
-       if(accelqw < 0)
-               accelqw = -accelqw;
-
-       if(autocvar_sv_gameplayfix_q2airaccelerate)
-               wishspeed0 = wishspeed;
-
-       vel_straight = self.velocity * wishdir;
-       velZ = self.velocity.z;
-       vel_xy = vec2(self.velocity);
-       vel_perpend = vel_xy - vel_straight * wishdir;
-
-       step = accel * frametime * wishspeed0;
-
-       vel_xy_current  = vlen(vel_xy);
-       if(speedlimit)
-               accelqw = AdjustAirAccelQW(accelqw, (speedlimit - bound(wishspeed, vel_xy_current, speedlimit)) / max(1, speedlimit - wishspeed));
-       vel_xy_forward  = vel_xy_current + bound(0, wishspeed - vel_xy_current, step) * accelqw + step * (1 - accelqw);
-       vel_xy_backward = vel_xy_current - bound(0, wishspeed + vel_xy_current, step) * accelqw - step * (1 - accelqw);
-       if(vel_xy_backward < 0)
-               vel_xy_backward = 0; // not that it REALLY occurs that this would cause wrong behaviour afterwards
-
-       vel_straight = vel_straight + bound(0, wishspeed - vel_straight, step) * accelqw + step * (1 - accelqw);
-
-       if(sidefric < 0 && (vel_perpend*vel_perpend))
-               // negative: only apply so much sideways friction to stay below the speed you could get by "braking"
-       {
-               float f, fminimum;
-               f = max(0, 1 + frametime * wishspeed * sidefric);
-               fminimum = (vel_xy_backward*vel_xy_backward - vel_straight*vel_straight) / (vel_perpend*vel_perpend);
-               // this cannot be > 1
-               if(fminimum <= 0)
-                       vel_perpend = vel_perpend * max(0, f);
-               else
-               {
-                       fminimum = sqrt(fminimum);
-                       vel_perpend = vel_perpend * max(fminimum, f);
-               }
-       }
-       else
-               vel_perpend = vel_perpend * max(0, 1 - frametime * wishspeed * sidefric);
-
-       vel_xy = vel_straight * wishdir + vel_perpend;
-
-       if(speedclamp >= 0)
-       {
-               float vel_xy_preclamp;
-               vel_xy_preclamp = vlen(vel_xy);
-               if(vel_xy_preclamp > 0) // prevent division by zero
-               {
-                       vel_xy_current += (vel_xy_forward - vel_xy_current) * speedclamp;
-                       if(vel_xy_current < vel_xy_preclamp)
-                               vel_xy = vel_xy * (vel_xy_current / vel_xy_preclamp);
-               }
-       }
-
-       self.velocity = vel_xy + velZ * '0 0 1';
-}
-
-void PM_AirAccelerate(vector wishdir, float wishspeed)
-{
-       vector curvel, wishvel, acceldir, curdir;
-       float addspeed, accelspeed, curspeed, f;
-       float dot;
-
-       if(wishspeed == 0)
-               return;
-
-       curvel = self.velocity;
-       curvel.z = 0;
-       curspeed = vlen(curvel);
-
-       if(wishspeed > curspeed * 1.01)
-       {
-               wishspeed = min(wishspeed, curspeed + self.stat_sv_warsowbunny_airforwardaccel * self.stat_sv_maxspeed * frametime);
-       }
-       else
-       {
-               f = max(0, (self.stat_sv_warsowbunny_topspeed - curspeed) / (self.stat_sv_warsowbunny_topspeed - self.stat_sv_maxspeed));
-               wishspeed = max(curspeed, self.stat_sv_maxspeed) + self.stat_sv_warsowbunny_accel * f * self.stat_sv_maxspeed * frametime;
-       }
-       wishvel = wishdir * wishspeed;
-       acceldir = wishvel - curvel;
-       addspeed = vlen(acceldir);
-       acceldir = normalize(acceldir);
-
-       accelspeed = min(addspeed, self.stat_sv_warsowbunny_turnaccel * self.stat_sv_maxspeed * frametime);
-
-       if(self.stat_sv_warsowbunny_backtosideratio < 1)
-       {
-               curdir = normalize(curvel);
-               dot = acceldir * curdir;
-               if(dot < 0)
-                       acceldir = acceldir - (1 - self.stat_sv_warsowbunny_backtosideratio) * dot * curdir;
-       }
-
-       self.velocity += accelspeed * acceldir;
-}
-
-.vector movement_old;
-.float buttons_old;
-.vector v_angle_old;
-.string lastclassname;
-
-.float() PlayerPhysplug;
-
-string specialcommand = "xwxwxsxsxaxdxaxdx1x ";
-.float specialcommand_pos;
-void SpecialCommand()
-{
-#ifdef TETRIS
-       TetrisImpulse();
-#else
-       if(!CheatImpulse(99))
-               print("A hollow voice says \"Plugh\".\n");
-#endif
-}
-
-string GetMapname(void);
-float speedaward_lastupdate;
-float speedaward_lastsent;
-void SV_PlayerPhysics()
-{
-       vector wishvel, wishdir, v;
-       float wishspeed, f, maxspd_mod, spd, maxairspd, airaccel, swampspd_mod, buttons;
-       string temps;
-       int buttons_prev;
-       float not_allowed_to_move;
-       string c;
-
-       WarpZone_PlayerPhysics_FixVAngle();
-
-       maxspd_mod = 1;
-       if(self.ballcarried)
-               if(g_keepaway)
-                       maxspd_mod *= autocvar_g_keepaway_ballcarrier_highspeed;
-
-       maxspd_mod *= autocvar_g_movement_highspeed;
-
-       // fix physics stats for g_movement_highspeed
-       // TODO maybe rather use maxairspeed? needs testing
-       self.stat_sv_airaccel_qw = AdjustAirAccelQW(Physics_ClientOption(self, "airaccel_qw"), maxspd_mod);
-       if(Physics_ClientOption(self, "airstrafeaccel_qw"))
-               self.stat_sv_airstrafeaccel_qw = AdjustAirAccelQW(Physics_ClientOption(self, "airstrafeaccel_qw"), maxspd_mod);
-       else
-               self.stat_sv_airstrafeaccel_qw = 0;
-       self.stat_sv_airspeedlimit_nonqw = Physics_ClientOption(self, "airspeedlimit_nonqw") * maxspd_mod;
-       self.stat_sv_maxspeed = Physics_ClientOption(self, "maxspeed") * maxspd_mod; // also slow walking
-       
-       // fix some new settings
-       self.stat_sv_airaccel_qw_stretchfactor = Physics_ClientOption(self, "airaccel_qw_stretchfactor");
-       self.stat_sv_maxairstrafespeed = Physics_ClientOption(self, "maxairstrafespeed");
-       self.stat_sv_maxairspeed = Physics_ClientOption(self, "maxairspeed");
-       self.stat_sv_airstrafeaccelerate = Physics_ClientOption(self, "airstrafeaccelerate");
-       self.stat_sv_warsowbunny_turnaccel = Physics_ClientOption(self, "warsowbunny_turnaccel");
-       self.stat_sv_airaccel_sideways_friction = Physics_ClientOption(self, "airaccel_sideways_friction");
-       self.stat_sv_aircontrol = Physics_ClientOption(self, "aircontrol");
-       self.stat_sv_aircontrol_power = Physics_ClientOption(self, "aircontrol_power");
-       self.stat_sv_aircontrol_penalty = Physics_ClientOption(self, "aircontrol_penalty");
-       self.stat_sv_warsowbunny_airforwardaccel = Physics_ClientOption(self, "warsowbunny_airforwardaccel");
-       self.stat_sv_warsowbunny_topspeed = Physics_ClientOption(self, "warsowbunny_topspeed");
-       self.stat_sv_warsowbunny_accel = Physics_ClientOption(self, "warsowbunny_accel");
-       self.stat_sv_warsowbunny_backtosideratio = Physics_ClientOption(self, "warsowbunny_backtosideratio");
-       self.stat_sv_friction = Physics_ClientOption(self, "friction");
-       self.stat_sv_accelerate = Physics_ClientOption(self, "accelerate");
-       self.stat_sv_stopspeed = Physics_ClientOption(self, "stopspeed");
-       self.stat_sv_airaccelerate = Physics_ClientOption(self, "airaccelerate");
-       self.stat_sv_airstopaccelerate = Physics_ClientOption(self, "airstopaccelerate");
-       self.stat_sv_jumpvelocity = Physics_ClientOption(self, "jumpvelocity");
-
-    if(self.PlayerPhysplug)
-        if(self.PlayerPhysplug())
-            return;
-
-       self.race_movetime_frac += frametime;
-       f = floor(self.race_movetime_frac);
-       self.race_movetime_frac -= f;
-       self.race_movetime_count += f;
-       self.race_movetime = self.race_movetime_frac + self.race_movetime_count;
-
-       anticheat_physics();
-
-       buttons = self.BUTTON_ATCK + 2 * self.BUTTON_JUMP + 4 * self.BUTTON_ATCK2 + 8 * self.BUTTON_ZOOM + 16 * self.BUTTON_CROUCH + 32 * self.BUTTON_HOOK + 64 * self.BUTTON_USE + 128 * (self.movement.x < 0) + 256 * (self.movement.x > 0) + 512 * (self.movement.y < 0) + 1024 * (self.movement.y > 0);
-
-       if(!buttons)
-               c = "x";
-       else if(buttons == 1)
-               c = "1";
-       else if(buttons == 2)
-               c = " ";
-       else if(buttons == 128)
-               c = "s";
-       else if(buttons == 256)
-               c = "w";
-       else if(buttons == 512)
-               c = "a";
-       else if(buttons == 1024)
-               c = "d";
-       else
-               c = "?";
-
-       if(c == substring(specialcommand, self.specialcommand_pos, 1))
-       {
-               self.specialcommand_pos += 1;
-               if(self.specialcommand_pos >= strlen(specialcommand))
-               {
-                       self.specialcommand_pos = 0;
-                       SpecialCommand();
-                       return;
-               }
-       }
-       else if(self.specialcommand_pos && (c != substring(specialcommand, self.specialcommand_pos - 1, 1)))
-               self.specialcommand_pos = 0;
-
-       if(sv_maxidle > 0)
-       {
-               if(buttons != self.buttons_old || self.movement != self.movement_old || self.v_angle != self.v_angle_old)
-                       self.parm_idlesince = time;
-       }
-       buttons_prev = self.buttons_old;
-       self.buttons_old = buttons;
-       self.movement_old = self.movement;
-       self.v_angle_old = self.v_angle;
-
-       if(time < self.nickspamtime)
-       if(self.nickspamcount >= autocvar_g_nick_flood_penalty_yellow)
-       {
-               // slight annoyance for nick change scripts
-               self.movement = -1 * self.movement;
-               self.BUTTON_ATCK = self.BUTTON_JUMP = self.BUTTON_ATCK2 = self.BUTTON_ZOOM = self.BUTTON_CROUCH = self.BUTTON_HOOK = self.BUTTON_USE = 0;
-
-               if(self.nickspamcount >= autocvar_g_nick_flood_penalty_red) // if you are persistent and the slight annoyance above does not stop you, I'll show you!
-               {
-                       self.angles_x = random() * 360;
-                       self.angles_y = random() * 360;
-                       // at least I'm not forcing retardedview by also assigning to angles_z
-                       self.fixangle = true;
-               }
-       }
-
-       if (self.punchangle != '0 0 0')
-       {
-               f = vlen(self.punchangle) - 10 * frametime;
-               if (f > 0)
-                       self.punchangle = normalize(self.punchangle) * f;
-               else
-                       self.punchangle = '0 0 0';
-       }
-
-       if (self.punchvector != '0 0 0')
-       {
-               f = vlen(self.punchvector) - 30 * frametime;
-               if (f > 0)
-                       self.punchvector = normalize(self.punchvector) * f;
-               else
-                       self.punchvector = '0 0 0';
-       }
-
-       if (IS_BOT_CLIENT(self))
-       {
-               if(playerdemo_read())
-                       return;
-               bot_think();
-       }
-
-       if(IS_PLAYER(self))
-       {
-               if(self.race_penalty)
-                       if(time > self.race_penalty)
-                               self.race_penalty = 0;
-
-               not_allowed_to_move = 0;
-               if(self.race_penalty)
-                       not_allowed_to_move = 1;
-               if(time < game_starttime)
-                       not_allowed_to_move = 1;
-
-               if(not_allowed_to_move)
-               {
-                       self.velocity = '0 0 0';
-                       self.movetype = MOVETYPE_NONE;
-                       self.disableclientprediction = 2;
-               }
-               else if(self.disableclientprediction == 2)
-               {
-                       if(self.movetype == MOVETYPE_NONE)
-                               self.movetype = MOVETYPE_WALK;
-                       self.disableclientprediction = 0;
-               }
-       }
-
-       if (self.movetype == MOVETYPE_NONE)
-               return;
-
-       // when we get here, disableclientprediction cannot be 2
-       self.disableclientprediction = 0;
-       if(time < self.ladder_time)
-               self.disableclientprediction = 1;
-
-       if(time < self.spider_slowness)
-       {
-               self.stat_sv_maxspeed *= 0.5; // half speed while slow from spider
-               self.stat_sv_airspeedlimit_nonqw *= 0.5;
-       }
-
-       if(self.frozen)
-       {
-               if(autocvar_sv_dodging_frozen && IS_REAL_CLIENT(self))
-               {
-                       self.movement_x = bound(-5, self.movement.x, 5);
-                       self.movement_y = bound(-5, self.movement.y, 5);
-                       self.movement_z = bound(-5, self.movement.z, 5);
-               }
-               else
-                       self.movement = '0 0 0';
-               self.disableclientprediction = 1;
-
-               vector midpoint = ((self.absmin + self.absmax) * 0.5);
-               if(pointcontents(midpoint) == CONTENT_WATER)
-               {
-                       self.velocity = self.velocity * 0.5;
-
-                       if(pointcontents(midpoint + '0 0 16') == CONTENT_WATER)
-                               { self.velocity_z = 200; }
-               }
-       }
-
-       MUTATOR_CALLHOOK(PlayerPhysics);
-
-       if(self.player_blocked)
-       {
-               self.movement = '0 0 0';
-               self.disableclientprediction = 1;
-       }
-
-       maxspd_mod = 1;
-
-       swampspd_mod = 1;
-       if(self.in_swamp) {
-               swampspd_mod = self.swamp_slowdown; //cvar("g_balance_swamp_moverate");
-       }
-
-       // conveyors: first fix velocity
-       if(self.conveyor.state)
-               self.velocity -= self.conveyor.movedir;
-
-       if (!IS_PLAYER(self))
-       {
-               maxspd_mod = autocvar_sv_spectator_speed_multiplier;
-               if(!self.spectatorspeed)
-                       self.spectatorspeed = maxspd_mod;
-               if(self.impulse && self.impulse <= 19 || (self.impulse >= 200 && self.impulse <= 209) || (self.impulse >= 220 && self.impulse <= 229))
-               {
-                       if(self.lastclassname != "player")
-                       {
-                               if(self.impulse == 10 || self.impulse == 15 || self.impulse == 18 || (self.impulse >= 200 && self.impulse <= 209))
-                                       self.spectatorspeed = bound(1, self.spectatorspeed + 0.5, 5);
-                               else if(self.impulse == 11)
-                                       self.spectatorspeed = maxspd_mod;
-                               else if(self.impulse == 12 || self.impulse == 16  || self.impulse == 19 || (self.impulse >= 220 && self.impulse <= 229))
-                                       self.spectatorspeed = bound(1, self.spectatorspeed - 0.5, 5);
-                               else if(self.impulse >= 1 && self.impulse <= 9)
-                                       self.spectatorspeed = 1 + 0.5 * (self.impulse - 1);
-                       } // otherwise just clear
-                       self.impulse = 0;
-               }
-               maxspd_mod = self.spectatorspeed;
-       }
-
-       spd = max(self.stat_sv_maxspeed, self.stat_sv_maxairspeed) * maxspd_mod * swampspd_mod;
-       if(self.speed != spd)
-       {
-               self.speed = spd;
-               temps = ftos(spd);
-               stuffcmd(self, strcat("cl_forwardspeed ", temps, "\n"));
-               stuffcmd(self, strcat("cl_backspeed ", temps, "\n"));
-               stuffcmd(self, strcat("cl_sidespeed ", temps, "\n"));
-               stuffcmd(self, strcat("cl_upspeed ", temps, "\n"));
-       }
-
-       maxspd_mod *= swampspd_mod; // only one common speed modder please!
-       swampspd_mod = 1;
-
-       // if dead, behave differently
-       if (self.deadflag)
-               goto end;
-
-       if (!self.fixangle && !g_bugrigs)
-       {
-               self.angles_x = 0;
-               self.angles_y = self.v_angle.y;
-               self.angles_z = 0;
-       }
-
-       if(self.flags & FL_ONGROUND)
-       if(IS_PLAYER(self)) // no fall sounds for observers thank you very much
-       if(self.wasFlying)
-       {
-               self.wasFlying = 0;
-
-               if(self.waterlevel < WATERLEVEL_SWIMMING)
-               if(time >= self.ladder_time)
-               if (!self.hook)
-               {
-                       self.nextstep = time + 0.3 + random() * 0.1;
-                       trace_dphitq3surfaceflags = 0;
-                       tracebox(self.origin, self.mins, self.maxs, self.origin - '0 0 1', MOVE_NOMONSTERS, self);
-                       if (!(trace_dphitq3surfaceflags & Q3SURFACEFLAG_NOSTEPS))
-                       {
-                               if(trace_dphitq3surfaceflags & Q3SURFACEFLAG_METALSTEPS)
-                                       GlobalSound(globalsound_metalfall, CH_PLAYER, VOICETYPE_PLAYERSOUND);
-                               else
-                                       GlobalSound(globalsound_fall, CH_PLAYER, VOICETYPE_PLAYERSOUND);
-                       }
-               }
-       }
-
-       if(IsFlying(self))
-               self.wasFlying = 1;
-
-       if(IS_PLAYER(self))
-               CheckPlayerJump();
-
-       if (self.flags & FL_WATERJUMP )
-       {
-               self.velocity_x = self.movedir.x;
-               self.velocity_y = self.movedir.y;
-               if (time > self.teleport_time || self.waterlevel == WATERLEVEL_NONE)
-               {
-                       self.flags &= ~FL_WATERJUMP;
-                       self.teleport_time = 0;
-               }
-       }
-       else if (g_bugrigs && IS_PLAYER(self))
-       {
-               RaceCarPhysics();
-       }
-       else if (self.movetype == MOVETYPE_NOCLIP || self.movetype == MOVETYPE_FLY || self.movetype == MOVETYPE_FLY_WORLDONLY)
-       {
-               // noclipping or flying
-               self.flags &= ~FL_ONGROUND;
-
-               self.velocity = self.velocity * (1 - frametime * self.stat_sv_friction);
-               makevectors(self.v_angle);
-               //wishvel = v_forward * self.movement_x + v_right * self.movement_y + v_up * self.movement_z;
-               wishvel = v_forward * self.movement.x + v_right * self.movement.y + '0 0 1' * self.movement.z;
-               // acceleration
-               wishdir = normalize(wishvel);
-               wishspeed = vlen(wishvel);
-               if (wishspeed > self.stat_sv_maxspeed*maxspd_mod)
-                       wishspeed = self.stat_sv_maxspeed*maxspd_mod;
-               if (time >= self.teleport_time)
-                       PM_Accelerate(wishdir, wishspeed, wishspeed, self.stat_sv_accelerate*maxspd_mod, 1, 0, 0, 0);
-       }
-       else if (self.waterlevel >= WATERLEVEL_SWIMMING)
-       {
-               // swimming
-               self.flags &= ~FL_ONGROUND;
-
-               makevectors(self.v_angle);
-               //wishvel = v_forward * self.movement_x + v_right * self.movement_y + v_up * self.movement_z;
-               wishvel = v_forward * self.movement.x + v_right * self.movement.y + '0 0 1' * self.movement.z;
-               if (wishvel == '0 0 0')
-                       wishvel = '0 0 -60'; // drift towards bottom
-
-               wishdir = normalize(wishvel);
-               wishspeed = vlen(wishvel);
-               if (wishspeed > self.stat_sv_maxspeed*maxspd_mod)
-                       wishspeed = self.stat_sv_maxspeed*maxspd_mod;
-               wishspeed = wishspeed * 0.7;
-
-               // water friction
-               self.velocity = self.velocity * (1 - frametime * self.stat_sv_friction);
-
-               // water acceleration
-               PM_Accelerate(wishdir, wishspeed, wishspeed, self.stat_sv_accelerate*maxspd_mod, 1, 0, 0, 0);
-       }
-       else if (time < self.ladder_time)
-       {
-               // on a spawnfunc_func_ladder or swimming in spawnfunc_func_water
-               self.flags &= ~FL_ONGROUND;
-
-               float g;
-               g = autocvar_sv_gravity * frametime;
-               if(self.gravity)
-                       g *= self.gravity;
-               if(autocvar_sv_gameplayfix_gravityunaffectedbyticrate)
-               {
-                       g *= 0.5;
-                       self.velocity_z += g;
-               }
-
-               self.velocity = self.velocity * (1 - frametime * self.stat_sv_friction);
-               makevectors(self.v_angle);
-               //wishvel = v_forward * self.movement_x + v_right * self.movement_y + v_up * self.movement_z;
-               wishvel = v_forward * self.movement.x + v_right * self.movement.y + '0 0 1' * self.movement.z;
-               self.velocity_z += g;
-               if (self.ladder_entity.classname == "func_water")
-               {
-                       f = vlen(wishvel);
-                       if (f > self.ladder_entity.speed)
-                               wishvel = wishvel * (self.ladder_entity.speed / f);
-
-                       self.watertype = self.ladder_entity.skin;
-                       f = self.ladder_entity.origin.z + self.ladder_entity.maxs.z;
-                       if ((self.origin.z + self.view_ofs.z) < f)
-                               self.waterlevel = WATERLEVEL_SUBMERGED;
-                       else if ((self.origin.z + (self.mins.z + self.maxs.z) * 0.5) < f)
-                               self.waterlevel = WATERLEVEL_SWIMMING;
-                       else if ((self.origin.z + self.mins.z + 1) < f)
-                               self.waterlevel = WATERLEVEL_WETFEET;
-                       else
-                       {
-                               self.waterlevel = WATERLEVEL_NONE;
-                               self.watertype = CONTENT_EMPTY;
-                       }
-               }
-               // acceleration
-               wishdir = normalize(wishvel);
-               wishspeed = vlen(wishvel);
-               if (wishspeed > self.stat_sv_maxspeed*maxspd_mod)
-                       wishspeed = self.stat_sv_maxspeed*maxspd_mod;
-               if (time >= self.teleport_time)
-               {
-                       // water acceleration
-                       PM_Accelerate(wishdir, wishspeed, wishspeed, self.stat_sv_accelerate*maxspd_mod, 1, 0, 0, 0);
-               }
-       }
-       else if (self.items & IT_USING_JETPACK)
-       {
-               //makevectors(self.v_angle_y * '0 1 0');
-               makevectors(self.v_angle);
-               wishvel = v_forward * self.movement.x + v_right * self.movement.y;
-               // add remaining speed as Z component
-               maxairspd = self.stat_sv_maxairspeed*max(1, maxspd_mod);
-               // fix speedhacks :P
-               wishvel = normalize(wishvel) * min(vlen(wishvel) / maxairspd, 1);
-               // add the unused velocity as up component
-               wishvel.z = 0;
-
-               // if(self.BUTTON_JUMP)
-                       wishvel.z = sqrt(max(0, 1 - wishvel * wishvel));
-
-               // it is now normalized, so...
-               float a_side, a_up, a_add, a_diff;
-               a_side = autocvar_g_jetpack_acceleration_side;
-               a_up = autocvar_g_jetpack_acceleration_up;
-               a_add = autocvar_g_jetpack_antigravity * autocvar_sv_gravity;
-
-               wishvel.x *= a_side;
-               wishvel.y *= a_side;
-               wishvel.z *= a_up;
-               wishvel.z += a_add;
-
-               float best;
-               best = 0;
-               //////////////////////////////////////////////////////////////////////////////////////
-               // finding the maximum over all vectors of above form
-               // with wishvel having an absolute value of 1
-               //////////////////////////////////////////////////////////////////////////////////////
-               // we're finding the maximum over
-               //   f(a_side, a_up, a_add, z) := a_side * (1 - z^2) + (a_add + a_up * z)^2;
-               // for z in the range from -1 to 1
-               //////////////////////////////////////////////////////////////////////////////////////
-               // maximum is EITHER attained at the single extreme point:
-               a_diff = a_side * a_side - a_up * a_up;
-               if(a_diff != 0)
-               {
-                       f = a_add * a_up / a_diff; // this is the zero of diff(f(a_side, a_up, a_add, z), z)
-                       if(f > -1 && f < 1) // can it be attained?
-                       {
-                               best = (a_diff + a_add * a_add) * (a_diff + a_up * a_up) / a_diff;
-                               //print("middle\n");
-                       }
-               }
-               // OR attained at z = 1:
-               f = (a_up + a_add) * (a_up + a_add);
-               if(f > best)
-               {
-                       best = f;
-                       //print("top\n");
-               }
-               // OR attained at z = -1:
-               f = (a_up - a_add) * (a_up - a_add);
-               if(f > best)
-               {
-                       best = f;
-                       //print("bottom\n");
-               }
-               best = sqrt(best);
-               //////////////////////////////////////////////////////////////////////////////////////
-
-               //print("best possible acceleration: ", ftos(best), "\n");
-
-               float fxy, fz;
-               fxy = bound(0, 1 - (self.velocity * normalize(wishvel.x * '1 0 0' + wishvel.y * '0 1 0')) / autocvar_g_jetpack_maxspeed_side, 1);
-               if(wishvel.z - autocvar_sv_gravity > 0)
-                       fz = bound(0, 1 - self.velocity.z / autocvar_g_jetpack_maxspeed_up, 1);
-               else
-                       fz = bound(0, 1 + self.velocity.z / autocvar_g_jetpack_maxspeed_up, 1);
-
-               wishvel.x *= fxy;
-               wishvel.y *= fxy;
-               wishvel.z = (wishvel.z - autocvar_sv_gravity) * fz + autocvar_sv_gravity;
-
-               float fvel;
-               fvel = min(1, vlen(wishvel) / best);
-               if(autocvar_g_jetpack_fuel && !(self.items & IT_UNLIMITED_WEAPON_AMMO))
-                       f = min(1, self.ammo_fuel / (autocvar_g_jetpack_fuel * frametime * fvel));
-               else
-                       f = 1;
-
-               //print("this acceleration: ", ftos(vlen(wishvel) * f), "\n");
-
-               if (f > 0 && wishvel != '0 0 0')
-               {
-                       self.velocity = self.velocity + wishvel * f * frametime;
-                       if (!(self.items & IT_UNLIMITED_WEAPON_AMMO))
-                               self.ammo_fuel -= autocvar_g_jetpack_fuel * frametime * fvel * f;
-                       self.flags &= ~FL_ONGROUND;
-                       self.items |= IT_USING_JETPACK;
-
-                       // jetpack also inhibits health regeneration, but only for 1 second
-                       self.pauseregen_finished = max(self.pauseregen_finished, time + autocvar_g_balance_pause_fuel_regen);
-               }
-       }
-       else if (self.flags & FL_ONGROUND)
-       {
-               // we get here if we ran out of ammo
-               if((self.items & IT_JETPACK) && self.BUTTON_HOOK && !(buttons_prev & 32) && self.ammo_fuel < 0.01)
-                       Send_Notification(NOTIF_ONE, self, MSG_INFO, INFO_JETPACK_NOFUEL);
-
-               // walking
-               makevectors(self.v_angle.y * '0 1 0');
-               wishvel = v_forward * self.movement.x + v_right * self.movement.y;
-
-               if(!(self.lastflags & FL_ONGROUND))
-               {
-                       if(autocvar_speedmeter)
-                               dprint(strcat("landing velocity: ", vtos(self.velocity), " (abs: ", ftos(vlen(self.velocity)), ")\n"));
-                       if(self.lastground < time - 0.3)
-                               self.velocity = self.velocity * (1 - autocvar_sv_friction_on_land);
-                       if(self.jumppadcount > 1)
-                               dprint(strcat(ftos(self.jumppadcount), "x jumppad combo\n"));
-                       self.jumppadcount = 0;
-               }
-
-               v = self.velocity;
-               v.z = 0;
-               f = vlen(v);
-               if(f > 0)
-               {
-                       if (f < self.stat_sv_stopspeed)
-                               f = 1 - frametime * (self.stat_sv_stopspeed / f) * self.stat_sv_friction;
-                       else
-                               f = 1 - frametime * self.stat_sv_friction;
-                       if (f > 0)
-                               self.velocity = self.velocity * f;
-                       else
-                               self.velocity = '0 0 0';
-                       /*
-                          Mathematical analysis time!
-
-                          Our goal is to invert this mess.
-
-                          For the two cases we get:
-                               v = v0 * (1 - frametime * (autocvar_sv_stopspeed / v0) * autocvar_sv_friction)
-                                 = v0 - frametime * autocvar_sv_stopspeed * autocvar_sv_friction
-                               v0 = v + frametime * autocvar_sv_stopspeed * autocvar_sv_friction
-                          and
-                               v = v0 * (1 - frametime * autocvar_sv_friction)
-                               v0 = v / (1 - frametime * autocvar_sv_friction)
-
-                          These cases would be chosen ONLY if:
-                               v0 < autocvar_sv_stopspeed
-                               v + frametime * autocvar_sv_stopspeed * autocvar_sv_friction < autocvar_sv_stopspeed
-                               v < autocvar_sv_stopspeed * (1 - frametime * autocvar_sv_friction)
-                          and, respectively:
-                               v0 >= autocvar_sv_stopspeed
-                               v / (1 - frametime * autocvar_sv_friction) >= autocvar_sv_stopspeed
-                               v >= autocvar_sv_stopspeed * (1 - frametime * autocvar_sv_friction)
-                        */
-               }
-
-               // acceleration
-               wishdir = normalize(wishvel);
-               wishspeed = vlen(wishvel);
-               if (wishspeed > self.stat_sv_maxspeed*maxspd_mod)
-                       wishspeed = self.stat_sv_maxspeed*maxspd_mod;
-               if (self.crouch)
-                       wishspeed = wishspeed * 0.5;
-               if (time >= self.teleport_time)
-                       PM_Accelerate(wishdir, wishspeed, wishspeed, self.stat_sv_accelerate*maxspd_mod, 1, 0, 0, 0);
-       }
-       else
-       {
-               float wishspeed0;
-               // we get here if we ran out of ammo
-               if((self.items & IT_JETPACK) && self.BUTTON_HOOK && !(buttons_prev & 32) && self.ammo_fuel < 0.01)
-                       Send_Notification(NOTIF_ONE, self, MSG_INFO, INFO_JETPACK_NOFUEL);
-
-               if(maxspd_mod < 1)
-               {
-                       maxairspd = self.stat_sv_maxairspeed*maxspd_mod;
-                       airaccel = self.stat_sv_airaccelerate*maxspd_mod;
-               }
-               else
-               {
-                       maxairspd = self.stat_sv_maxairspeed;
-                       airaccel = self.stat_sv_airaccelerate;
-               }
-               // airborn
-               makevectors(self.v_angle.y * '0 1 0');
-               wishvel = v_forward * self.movement.x + v_right * self.movement.y;
-               // acceleration
-               wishdir = normalize(wishvel);
-               wishspeed = wishspeed0 = vlen(wishvel);
-               if (wishspeed0 > self.stat_sv_maxspeed*maxspd_mod)
-                       wishspeed0 = self.stat_sv_maxspeed*maxspd_mod;
-               if (wishspeed > maxairspd)
-                       wishspeed = maxairspd;
-               if (self.crouch)
-                       wishspeed = wishspeed * 0.5;
-               if (time >= self.teleport_time)
-               {
-                       float accelerating;
-                       float wishspeed2;
-                       float airaccelqw;
-                       float strafity;
-
-                       airaccelqw = self.stat_sv_airaccel_qw;
-                       accelerating = (self.velocity * wishdir > 0);
-                       wishspeed2 = wishspeed;
-
-                       // CPM
-                       if(self.stat_sv_airstopaccelerate)
-                       {
-                               vector curdir;
-                               curdir = self.velocity;
-                               curdir.z = 0;
-                               curdir = normalize(curdir);
-                               airaccel = airaccel + (self.stat_sv_airstopaccelerate*maxspd_mod - airaccel) * max(0, -(curdir * wishdir));
-                       }
-                       // note that for straight forward jumping:
-                       // step = accel * frametime * wishspeed0;
-                       // accel  = bound(0, wishspeed - vel_xy_current, step) * accelqw + step * (1 - accelqw);
-                       // -->
-                       // dv/dt = accel * maxspeed (when slow)
-                       // dv/dt = accel * maxspeed * (1 - accelqw) (when fast)
-                       // log dv/dt = logaccel + logmaxspeed (when slow)
-                       // log dv/dt = logaccel + logmaxspeed + log(1 - accelqw) (when fast)
-                       strafity = IsMoveInDirection(self.movement, -90) + IsMoveInDirection(self.movement, +90); // if one is nonzero, other is always zero
-                       if(self.stat_sv_maxairstrafespeed)
-                               wishspeed = min(wishspeed, GeomLerp(self.stat_sv_maxairspeed*maxspd_mod, strafity, self.stat_sv_maxairstrafespeed*maxspd_mod));
-                       if(self.stat_sv_airstrafeaccelerate)
-                               airaccel = GeomLerp(airaccel, strafity, self.stat_sv_airstrafeaccelerate*maxspd_mod);
-                       if(self.stat_sv_airstrafeaccel_qw)
-                               airaccelqw = copysign(1-GeomLerp(1-fabs(self.stat_sv_airaccel_qw), strafity, 1-fabs(self.stat_sv_airstrafeaccel_qw)), ((strafity > 0.5) ? self.stat_sv_airstrafeaccel_qw : self.stat_sv_airaccel_qw));
-                       // !CPM
-
-                       if(self.stat_sv_warsowbunny_turnaccel && accelerating && self.movement_y == 0 && self.movement.x != 0)
-                               PM_AirAccelerate(wishdir, wishspeed);
-                       else
-                               PM_Accelerate(wishdir, wishspeed, wishspeed0, airaccel, airaccelqw, self.stat_sv_airaccel_qw_stretchfactor, self.stat_sv_airaccel_sideways_friction / maxairspd, self.stat_sv_airspeedlimit_nonqw);
-
-                       if(self.stat_sv_aircontrol)
-                               CPM_PM_Aircontrol(wishdir, wishspeed2);
-               }
-       }
-
-       if((g_cts || g_race) && !IS_OBSERVER(self))
-       {
-               if(vlen(self.velocity - self.velocity.z * '0 0 1') > speedaward_speed)
-               {
-                       speedaward_speed = vlen(self.velocity - self.velocity.z * '0 0 1');
-                       speedaward_holder = self.netname;
-                       speedaward_uid = self.crypto_idfp;
-                       speedaward_lastupdate = time;
-               }
-               if(speedaward_speed > speedaward_lastsent && time - speedaward_lastupdate > 1)
-               {
-                       string rr = (g_cts) ? CTS_RECORD : RACE_RECORD;
-                       race_send_speedaward(MSG_ALL);
-                       speedaward_lastsent = speedaward_speed;
-                       if (speedaward_speed > speedaward_alltimebest && speedaward_uid != "")
-                       {
-                               speedaward_alltimebest = speedaward_speed;
-                               speedaward_alltimebest_holder = speedaward_holder;
-                               speedaward_alltimebest_uid = speedaward_uid;
-                               db_put(ServerProgsDB, strcat(GetMapname(), rr, "speed/speed"), ftos(speedaward_alltimebest));
-                               db_put(ServerProgsDB, strcat(GetMapname(), rr, "speed/crypto_idfp"), speedaward_alltimebest_uid);
-                               race_send_speedaward_alltimebest(MSG_ALL);
-                       }
-               }
-       }
-
-       // WEAPONTODO
-       float xyspeed;
-       xyspeed = vlen('1 0 0' * self.velocity.x + '0 1 0' * self.velocity.y);
-       if(self.weapon == WEP_VORTEX && WEP_CVAR(vortex, charge) && WEP_CVAR(vortex, charge_velocity_rate) && xyspeed > WEP_CVAR(vortex, charge_minspeed))
-       {
-               // add a maximum of charge_velocity_rate when going fast (f = 1), gradually increasing from minspeed (f = 0) to maxspeed
-               xyspeed = min(xyspeed, WEP_CVAR(vortex, charge_maxspeed));
-               f = (xyspeed - WEP_CVAR(vortex, charge_minspeed)) / (WEP_CVAR(vortex, charge_maxspeed) - WEP_CVAR(vortex, charge_minspeed));
-               // add the extra charge
-               self.vortex_charge = min(1, self.vortex_charge + WEP_CVAR(vortex, charge_velocity_rate) * f * frametime);
-       }
-:end
-       if(self.flags & FL_ONGROUND)
-               self.lastground = time;
-
-       // conveyors: then break velocity again
-       if(self.conveyor.state)
-               self.velocity += self.conveyor.movedir;
-
-       self.lastflags = self.flags;
-       self.lastclassname = self.classname;
-}
index 059d754bb7a7d9ba3f0e058aeda97e96ad65c08f..aae39ea66eccbc9e9190e1797fedcabee3e366de 100644 (file)
@@ -5,7 +5,6 @@
 #include "cheats.qh"
 #include "g_damage.qh"
 #include "g_subs.qh"
-#include "g_triggers.qh"
 #include "g_violence.qh"
 #include "miscfunctions.qh"
 #include "portals.qh"
 #include "../common/animdecide.qh"
 #include "../common/csqcmodel_settings.qh"
 #include "../common/deathtypes.qh"
+#include "../common/triggers/subs.qh"
 #include "../common/playerstats.qh"
 #include "../csqcmodellib/sv_model.qh"
 
 #include "weapons/weaponstats.qh"
 
+#include "../common/animdecide.qh"
+
 void CopyBody_Think(void)
 {
        if(self.CopyBody_nextthink && time > self.CopyBody_nextthink)
index beb04103628d7e52e3a0b5890e7d5fb9772fb6cb..828c86302354c5a53c11ae4e0116f2e04a46ef6c 100644 (file)
@@ -8,7 +8,6 @@
 #include "../campaign.qh"
 #include "../cheats.qh"
 #include "../cl_player.qh"
-#include "../g_triggers.qh"
 #include "../ipban.qh"
 #include "../mapvoting.qh"
 #include "../scores.qh"
index 45a4cc7f270c09df18ff79e59aa11ce3d20efd77..53cab574729d7f524639222433f4fb6c1f4c3529 100644 (file)
@@ -2,7 +2,6 @@
 #include "radarmap.qh"
 #include "../_all.qh"
 
-#include "../g_subs.qh"
 #include "../g_world.qh"
 
 #include "../../common/util.qh"
index 1d4cc55232382a6fcd7f2491325b6364ec5a9736..c1def6b821e469d43d1183f371ff1039e0009d53 100644 (file)
@@ -23,5 +23,4 @@ const int NUM_PLAYERSKINS_TEAMPLAY = 3;
 
 const int ASSAULT_VALUE_INACTIVE = 1000;
 
-const int DOOR_NOSPLASH = 256; // generic anti-splashdamage spawnflag
 #endif
index 276f3f9ad810a051da1ac7531bf9702a69f0c422..9637cc67c8496dd02032e99d3b5a864dfd9211e2 100644 (file)
@@ -46,8 +46,6 @@ float g_jetpack;
 float sv_clones;
 float sv_foginterval;
 
-entity activator;
-
 float player_count;
 float currentbots;
 float bots_would_leave;
@@ -70,29 +68,12 @@ float server_is_dedicated;
 //.string      map;
 
 //.float       worldtype;
-.float delay;
-.float wait;
-.float lip;
-//.float       light_lev;
-.float speed;
-//.float       style;
-//.float       skill;
-.float sounds;
-.string  platmovetype;
-.float platmovetype_start, platmovetype_end;
-
-
 // Needed for dynamic clientwalls
 .float inactive; // Clientwall disappears when inactive
 .float alpha_max, alpha_min;
 .float fade_start, fade_end, fade_vertical_offset;
 .float default_solid; // Variable to store default self.solid for clientwalls
 
-.string killtarget;
-
-.vector        pos1, pos2;
-.vector        mangle;
-
 .float pain_finished;                  //Added by Supajoe
 .float pain_frame;                     //"
 .float  crouch;        // Crouching or not?
@@ -101,14 +82,7 @@ float server_is_dedicated;
 .float invincible_finished;
 .float superweapons_finished;
 
-.vector                finaldest, finalangle;          //plat.qc stuff
-.void()                think1;
-.float state;
-.float         t_length, t_width;
-
-.vector destvec;               // for rain
-.vector destvec2;              // for train
-.float cnt;            // for rain
+.float cnt; // used in too many places
 .float count;
 //.float cnt2;
 
@@ -120,15 +94,6 @@ float server_is_dedicated;
 .float fade_time;
 .float fade_rate;
 
-// player animation state
-.float animstate_startframe;
-.float animstate_numframes;
-.float animstate_framerate;
-.float animstate_starttime;
-.float animstate_endtime;
-.float animstate_override;
-.float animstate_looping;
-
 // weapon animation vectors:
 .vector anim_fire1;
 .vector anim_fire2;
@@ -217,26 +182,13 @@ const int W_TICSPERFRAME = 2;
 
 void weapon_defaultspawnfunc(float wpn);
 
-.vector dest1, dest2;
-
 float gameover;
 float intermission_running;
 float intermission_exittime;
 float alreadychangedlevel;
 
-// Keys player is holding
-.float itemkeys;
-// message delay for func_door locked by keys and key locks
-// this field is used on player entities
-.float key_door_messagetime;
-
-
 .float version;
 
-//swamp
-.float in_swamp;              // bool
-.entity swampslug;            // Uses this to release from swamp ("untouch" fix)
-
 // footstep interval
 .float nextstep;
 
@@ -291,10 +243,6 @@ float default_weapon_alpha;
 
 .float version_nagtime;
 
-const int NUM_JUMPPADSUSED = 3;
-.int jumppadcount;
-.entity jumppadsused[NUM_JUMPPADSUSED];
-
 string gamemode_name;
 
 float startitem_failed;
@@ -454,34 +402,6 @@ float round_starttime; //point in time when the countdown to round start is over
 .float stat_game_starttime;
 .float stat_round_starttime;
 
-.float stat_sv_airaccel_qw;
-.float stat_sv_airstrafeaccel_qw;
-.float stat_sv_airspeedlimit_nonqw;
-.float stat_sv_maxspeed;
-
-// new properties
-.float stat_sv_jumpvelocity;
-.float stat_sv_airaccel_qw_stretchfactor;
-.float stat_sv_maxairstrafespeed;
-.float stat_sv_maxairspeed;
-.float stat_sv_airstrafeaccelerate;
-.float stat_sv_warsowbunny_turnaccel;
-.float stat_sv_airaccel_sideways_friction;
-.float stat_sv_aircontrol;
-.float stat_sv_aircontrol_power;
-.float stat_sv_aircontrol_penalty;
-.float stat_sv_warsowbunny_airforwardaccel;
-.float stat_sv_warsowbunny_topspeed;
-.float stat_sv_warsowbunny_accel;
-.float stat_sv_warsowbunny_backtosideratio;
-.float stat_sv_friction;
-.float stat_sv_accelerate;
-.float stat_sv_stopspeed;
-.float stat_sv_airaccelerate;
-.float stat_sv_airstopaccelerate;
-
-.string cvar_cl_physics;
-
 void W_Porto_Remove (entity p);
 
 .int projectiledeathtype;
@@ -494,16 +414,6 @@ void W_Porto_Remove (entity p);
 // may be useful to all weapons
 .float bulletcounter;
 
-void target_voicescript_next(entity pl);
-void target_voicescript_clear(entity pl);
-
-.string target2;
-.string target3;
-.string target4;
-.string curvetarget;
-.float target_random;
-.float trigger_reverse;
-
 // Nexball
 .entity ballcarried; // Also used for keepaway
 .float metertime;
@@ -624,8 +534,6 @@ void PlayerUseKey();
 typedef vector(entity player, entity spot, vector current) spawn_evalfunc_t;
 .spawn_evalfunc_t spawn_evalfunc;
 
-.entity conveyor;
-
 string modname;
 
 .float missile_flags;
@@ -654,6 +562,8 @@ const int MIF_GUIDED_TAG = 128;
 .float elos;
 .float ranks;
 
+.string cvar_cl_physics;
+
 .float init_for_player_needed;
 .void(entity) init_for_player;
 
diff --git a/qcsrc/server/func_breakable.qc b/qcsrc/server/func_breakable.qc
deleted file mode 100644 (file)
index 2a3252f..0000000
+++ /dev/null
@@ -1,320 +0,0 @@
-#include "_all.qh"
-
-#include "g_subs.qh"
-#include "waypointsprites.qh"
-#include "g_damage.qh"
-#include "bot/bot.qh"
-#include "../common/csqcmodel_settings.qh"
-#include "../csqcmodellib/sv_model.qh"
-#include "weapons/common.qh"
-
-.entity sprite;
-
-.float dmg;
-.float dmg_edge;
-.float dmg_radius;
-.float dmg_force;
-.float debrismovetype;
-.float debrissolid;
-.vector debrisvelocity;
-.vector debrisvelocityjitter;
-.vector debrisavelocityjitter;
-.float debristime;
-.float debristimejitter;
-.float debrisfadetime;
-.float debrisdamageforcescale;
-.float debrisskin;
-
-.string mdl_dead; // or "" to hide when broken
-.string debris; // space separated list of debris models
-// other fields:
-//   mdl = particle effect name
-//   count = particle effect multiplier
-//   targetname = target to trigger to unbreak the model
-//   target = targets to trigger when broken
-//   health = amount of damage it can take
-//   spawnflags:
-//     1 = start disabled (needs to be triggered to activate)
-//     2 = indicate damage
-// notes:
-//   for mdl_dead to work, origin must be set (using a common/origin brush).
-//   Otherwise mdl_dead will be displayed at the map origin, and nobody would
-//   want that!
-
-void func_breakable_damage(entity inflictor, entity attacker, float damage, int deathtype, vector hitloc, vector force);
-
-//
-// func_breakable
-// - basically func_assault_destructible for general gameplay use
-//
-void LaunchDebris (string debrisname, vector force)
-{
-       entity dbr = spawn();
-       setorigin(dbr, self.absmin
-                  + '1 0 0' * random() * (self.absmax.x - self.absmin.x)
-                  + '0 1 0' * random() * (self.absmax.y - self.absmin.y)
-                  + '0 0 1' * random() * (self.absmax.z - self.absmin.z));
-       setmodel (dbr, debrisname );
-       dbr.skin = self.debrisskin;
-       dbr.colormap = self.colormap; // inherit team colors
-       dbr.owner = self; // do not be affected by our own explosion
-       dbr.movetype = self.debrismovetype;
-       dbr.solid = self.debrissolid;
-       if(dbr.solid != SOLID_BSP) // SOLID_BSP has exact collision, MAYBE this works? TODO check this out
-               setsize(dbr, '0 0 0', '0 0 0'); // needed for performance, until engine can deal better with it
-       dbr.velocity_x = self.debrisvelocity.x + self.debrisvelocityjitter.x * crandom();
-       dbr.velocity_y = self.debrisvelocity.y + self.debrisvelocityjitter.y * crandom();
-       dbr.velocity_z = self.debrisvelocity.z + self.debrisvelocityjitter.z * crandom();
-       self.velocity = self.velocity + force * self.debrisdamageforcescale;
-       dbr.avelocity_x = random()*self.debrisavelocityjitter.x;
-       dbr.avelocity_y = random()*self.debrisavelocityjitter.y;
-       dbr.avelocity_z = random()*self.debrisavelocityjitter.z;
-       dbr.damageforcescale = self.debrisdamageforcescale;
-       if(dbr.damageforcescale)
-               dbr.takedamage = DAMAGE_YES;
-       SUB_SetFade(dbr, time + self.debristime + crandom() * self.debristimejitter, self.debrisfadetime);
-}
-
-void func_breakable_colormod()
-{
-       float h;
-       if (!(self.spawnflags & 2))
-               return;
-       h = self.health / self.max_health;
-       if(h < 0.25)
-               self.colormod = '1 0 0';
-       else if(h <= 0.75)
-               self.colormod = '1 0 0' + '0 1 0' * (2 * h - 0.5);
-       else
-               self.colormod = '1 1 1';
-
-       CSQCMODEL_AUTOUPDATE();
-}
-
-void func_breakable_look_destroyed()
-{
-       float floorZ;
-
-       if(self.solid == SOLID_BSP) // in case a misc_follow moved me, save the current origin first
-               self.dropped_origin = self.origin;
-
-       if(self.mdl_dead == "")
-               self.effects |= EF_NODRAW;
-       else {
-               if (self.origin == '0 0 0')     {       // probably no origin brush, so don't spawn in the middle of the map..
-                       floorZ = self.absmin.z;
-                       setorigin(self,((self.absmax+self.absmin)*.5));
-                       self.origin_z = floorZ;
-               }
-               setmodel(self, self.mdl_dead);
-               self.effects &= ~EF_NODRAW;
-       }
-
-       CSQCMODEL_AUTOUPDATE();
-
-       self.solid = SOLID_NOT;
-}
-
-void func_breakable_look_restore()
-{
-       setmodel(self, self.mdl);
-       self.effects &= ~EF_NODRAW;
-
-       if(self.mdl_dead != "") // only do this if we use mdl_dead, to behave better with misc_follow
-               setorigin(self, self.dropped_origin);
-
-       CSQCMODEL_AUTOUPDATE();
-
-       self.solid = SOLID_BSP;
-}
-
-void func_breakable_behave_destroyed()
-{
-       self.health = self.max_health;
-       self.takedamage = DAMAGE_NO;
-       self.bot_attack = false;
-       self.event_damage = func_null;
-       self.state = 1;
-       func_breakable_colormod();
-       if (self.noise1)
-               stopsound (self, CH_TRIGGER_SINGLE);
-}
-
-void func_breakable_behave_restore()
-{
-       self.health = self.max_health;
-       if(self.sprite)
-       {
-               WaypointSprite_UpdateMaxHealth(self.sprite, self.max_health);
-               WaypointSprite_UpdateHealth(self.sprite, self.health);
-       }
-       self.takedamage = DAMAGE_AIM;
-       self.bot_attack = true;
-       self.event_damage = func_breakable_damage;
-       self.state = 0;
-       self.nextthink = 0; // cancel auto respawn
-       func_breakable_colormod();
-       if (self.noise1)
-               sound (self, CH_TRIGGER_SINGLE, self.noise1, VOL_BASE, ATTEN_NORM);
-}
-
-void func_breakable_init_for_player(entity player)
-{
-       if (self.noise1 && self.state == 0 && clienttype(player) == CLIENTTYPE_REAL)
-       {
-               msg_entity = player;
-               soundto (MSG_ONE, self, CH_TRIGGER_SINGLE, self.noise1, VOL_BASE, ATTEN_NORM);
-       }
-}
-
-void func_breakable_destroyed()
-{
-       func_breakable_look_destroyed();
-       func_breakable_behave_destroyed();
-
-       CSQCMODEL_AUTOUPDATE();
-}
-
-void func_breakable_restore()
-{
-       func_breakable_look_restore();
-       func_breakable_behave_restore();
-
-       CSQCMODEL_AUTOUPDATE();
-}
-
-vector debrisforce; // global, set before calling this
-void func_breakable_destroy() {
-       float n, i;
-       string oldmsg;
-
-       activator = self.owner;
-       self.owner = world; // set by W_PrepareExplosionByDamage
-
-       // now throw around the debris
-       n = tokenize_console(self.debris);
-       for(i = 0; i < n; ++i)
-               LaunchDebris(argv(i), debrisforce);
-
-       func_breakable_destroyed();
-
-       if(self.noise)
-               sound (self, CH_TRIGGER, self.noise, VOL_BASE, ATTEN_NORM);
-
-       if(self.dmg)
-               RadiusDamage(self, activator, self.dmg, self.dmg_edge, self.dmg_radius, self, world, self.dmg_force, DEATH_HURTTRIGGER, world);
-
-       if(self.cnt)
-               pointparticles(self.cnt, self.absmin * 0.5 + self.absmax * 0.5, '0 0 0', self.count);
-
-       if(self.respawntime)
-       {
-               self.think = func_breakable_restore;
-               self.nextthink = time + self.respawntime + crandom() * self.respawntimejitter;
-       }
-
-       oldmsg = self.message;
-       self.message = "";
-       SUB_UseTargets();
-       self.message = oldmsg;
-}
-
-void func_breakable_damage(entity inflictor, entity attacker, float damage, int deathtype, vector hitloc, vector force)
-{
-       if(self.state == 1)
-               return;
-       if(self.spawnflags & DOOR_NOSPLASH)
-               if(!(DEATH_ISSPECIAL(deathtype)) && (deathtype & HITTYPE_SPLASH))
-                       return;
-       if(self.team)
-               if(attacker.team == self.team)
-                       return;
-       self.health = self.health - damage;
-       if(self.sprite)
-       {
-               WaypointSprite_Ping(self.sprite);
-               WaypointSprite_UpdateHealth(self.sprite, self.health);
-       }
-       func_breakable_colormod();
-
-       if(self.health <= 0)
-       {
-               debrisforce = force;
-               W_PrepareExplosionByDamage(attacker, func_breakable_destroy);
-       }
-}
-
-void func_breakable_reset()
-{
-       self.team = self.team_saved;
-       func_breakable_look_restore();
-       if(self.spawnflags & 1)
-               func_breakable_behave_destroyed();
-       else
-               func_breakable_behave_restore();
-
-       CSQCMODEL_AUTOUPDATE();
-}
-
-// destructible walls that can be used to trigger target_objective_decrease
-void spawnfunc_func_breakable() {
-       float n, i;
-       if(!self.health)
-               self.health = 100;
-       self.max_health = self.health;
-
-       // yes, I know, MOVETYPE_NONE is not available here, not that one would want it here anyway
-       if(!self.debrismovetype) self.debrismovetype = MOVETYPE_BOUNCE;
-       if(!self.debrissolid) self.debrissolid = SOLID_NOT;
-       if(self.debrisvelocity == '0 0 0') self.debrisvelocity = '0 0 140';
-       if(self.debrisvelocityjitter == '0 0 0') self.debrisvelocityjitter = '70 70 70';
-       if(self.debrisavelocityjitter == '0 0 0') self.debrisavelocityjitter = '600 600 600';
-       if(!self.debristime) self.debristime = 3.5;
-       if(!self.debristimejitter) self.debristime = 2.5;
-
-       if(self.mdl != "")
-               self.cnt = particleeffectnum(self.mdl);
-       if(self.count == 0)
-               self.count = 1;
-
-       if(self.message == "")
-               self.message = "got too close to an explosion";
-       if(self.message2 == "")
-               self.message2 = "was pushed into an explosion by";
-       if(!self.dmg_radius)
-               self.dmg_radius = 150;
-       if(!self.dmg_force)
-               self.dmg_force = 200;
-
-       self.mdl = self.model;
-       SetBrushEntityModel();
-
-       self.use = func_breakable_restore;
-
-       // precache all the models
-       if (self.mdl_dead)
-               precache_model(self.mdl_dead);
-       n = tokenize_console(self.debris);
-       for(i = 0; i < n; ++i)
-               precache_model(argv(i));
-       if(self.noise)
-               precache_sound(self.noise);
-       if(self.noise1)
-               precache_sound(self.noise1);
-
-       self.team_saved = self.team;
-       self.dropped_origin = self.origin;
-
-       self.reset = func_breakable_reset;
-       func_breakable_reset();
-
-       self.init_for_player_needed = 1;
-       self.init_for_player = func_breakable_init_for_player;
-
-       CSQCMODEL_AUTOINIT();
-}
-
-// for use in maps with a "model" key set
-void spawnfunc_misc_breakablemodel() {
-       spawnfunc_func_breakable();
-}
index 24bce642d8c7509785f328464225ec793e34daa0..68e8f4f7325a1795a5e3d744572ab8b57e702153 100644 (file)
@@ -15,6 +15,7 @@
 #include "../common/constants.qh"
 #include "../common/deathtypes.qh"
 #include "../common/notifications.qh"
+#include "../common/movetypes/movetypes.qh"
 #include "../common/playerstats.qh"
 #include "../common/teams.qh"
 #include "../common/util.qh"
@@ -61,18 +62,6 @@ void Damage_DamageInfo(vector org, float coredamage, float edgedamage, float rad
        Net_LinkEntity(e, false, 0.2, Damage_DamageInfo_SendEntity);
 }
 
-float IsFlying(entity a)
-{
-       if(a.flags & FL_ONGROUND)
-               return 0;
-       if(a.waterlevel >= WATERLEVEL_SWIMMING)
-               return 0;
-       traceline(a.origin, a.origin - '0 0 48', MOVE_NORMAL, a);
-       if(trace_fraction < 1)
-               return 0;
-       return 1;
-}
-
 void UpdateFrags(entity player, float f)
 {
        PlayerTeamScore_AddScore(player, f);
@@ -924,8 +913,12 @@ void Damage (entity targ, entity inflictor, entity attacker, float damage, int d
                        farcent.think = SUB_Remove;
                }
                else
+               {
                        self.velocity = self.velocity + farce;
+                       self.move_velocity = self.velocity;
+               }
                self.flags &= ~FL_ONGROUND;
+               self.move_flags &= ~FL_ONGROUND;
                UpdateCSQCProjectile(self);
        }
        // apply damage
index 4a3f3101ac663eaf3b92c0a31f27f97ac4ae5365..7a127eddab3971938567faa9479a1f64c9930222 100644 (file)
@@ -6,7 +6,6 @@
 #include "weapons/selection.qh"
 #include "weapons/tracing.qh"
 #include "cl_player.qh"
-#include "t_teleporters.qh"
 #include "command/common.qh"
 #include "round_handler.qh"
 #include "vehicles/vehicle.qh"
index 6c4f16b54e519a50986d79fc9ca410175c1b9328..62b2cfb51dd5d3a5c9a52a38b78d9654c0e97e58 100644 (file)
@@ -1,7 +1,8 @@
 #include "_all.qh"
 
-#include "g_subs.qh"
-#include "g_triggers.qh"
+#include "../common/triggers/subs.qh"
+
+#include "../client/bgmscript.qh"
 
 #include "../common/constants.qh"
 #include "../csqcmodellib/sv_model.qh"
index e850b61b3e3b7535204cf1382fdcaaa0198645a1..eacba6452869ce4bb9c95836f1de2b28ccf356d7 100644 (file)
@@ -5,8 +5,6 @@
 #include "command/common.qh"
 #include "../warpzonelib/common.qh"
 
-void SUB_NullThink(void) { }
-
 void spawnfunc_info_null (void)
 {
        remove(self);
@@ -57,380 +55,6 @@ void updateanim(entity e)
        //print(ftos(time), " -> ", ftos(e.frame), "\n");
 }
 
-/*
-==================
-SUB_Remove
-
-Remove self
-==================
-*/
-void SUB_Remove (void)
-{
-       remove (self);
-}
-
-/*
-==================
-SUB_Friction
-
-Applies some friction to self
-==================
-*/
-void SUB_Friction (void)
-{
-       self.nextthink = time;
-       if(self.flags & FL_ONGROUND)
-               self.velocity = self.velocity * (1 - frametime * self.friction);
-}
-
-/*
-==================
-SUB_VanishOrRemove
-
-Makes client invisible or removes non-client
-==================
-*/
-void SUB_VanishOrRemove (entity ent)
-{
-       if (IS_CLIENT(ent))
-       {
-               // vanish
-               ent.alpha = -1;
-               ent.effects = 0;
-               ent.glow_size = 0;
-               ent.pflags = 0;
-       }
-       else
-       {
-               // remove
-               remove (ent);
-       }
-}
-
-void SUB_SetFade_Think (void)
-{
-       if(self.alpha == 0)
-               self.alpha = 1;
-       self.think = SUB_SetFade_Think;
-       self.nextthink = time;
-       self.alpha -= frametime * self.fade_rate;
-       if (self.alpha < 0.01)
-               SUB_VanishOrRemove(self);
-       else
-               self.nextthink = time;
-}
-
-/*
-==================
-SUB_SetFade
-
-Fade 'ent' out when time >= 'when'
-==================
-*/
-void SUB_SetFade (entity ent, float when, float fadetime)
-{
-       ent.fade_rate = 1/fadetime;
-       ent.think = SUB_SetFade_Think;
-       ent.nextthink = when;
-}
-
-/*
-=============
-SUB_CalcMove
-
-calculate self.velocity and self.nextthink to reach dest from
-self.origin traveling at speed
-===============
-*/
-void SUB_CalcMoveDone (void)
-{
-       // After moving, set origin to exact final destination
-
-       setorigin (self, self.finaldest);
-       self.velocity = '0 0 0';
-       self.nextthink = -1;
-       if (self.think1)
-               self.think1 ();
-}
-
-void SUB_CalcMove_controller_think (void)
-{
-       entity oldself;
-       float traveltime;
-       float phasepos;
-       float nexttick;
-       vector delta;
-       vector delta2;
-       vector veloc;
-       vector angloc;
-       vector nextpos;
-       delta = self.destvec;
-       delta2 = self.destvec2;
-       if(time < self.animstate_endtime) {
-               nexttick = time + sys_frametime;
-
-               traveltime = self.animstate_endtime - self.animstate_starttime;
-               phasepos = (nexttick - self.animstate_starttime) / traveltime; // range: [0, 1]
-               phasepos = cubic_speedfunc(self.platmovetype_start, self.platmovetype_end, phasepos);
-               nextpos = self.origin + (delta * phasepos) + (delta2 * phasepos * phasepos);
-               // derivative: delta + 2 * delta2 * phasepos (e.g. for angle positioning)
-
-               if(self.owner.platmovetype_turn)
-               {
-                       vector destangle;
-                       destangle = delta + 2 * delta2 * phasepos;
-                       destangle = vectoangles(destangle);
-                       destangle.x = -destangle.x; // flip up / down orientation
-
-                       // take the shortest distance for the angles
-                       self.owner.angles_x -= 360 * floor((self.owner.angles.x - destangle.x) / 360 + 0.5);
-                       self.owner.angles_y -= 360 * floor((self.owner.angles.y - destangle.y) / 360 + 0.5);
-                       self.owner.angles_z -= 360 * floor((self.owner.angles.z - destangle.z) / 360 + 0.5);
-                       angloc = destangle - self.owner.angles;
-                       angloc = angloc * (1 / sys_frametime); // so it arrives for the next frame
-                       self.owner.avelocity = angloc;
-               }
-               if(nexttick < self.animstate_endtime)
-                       veloc = nextpos - self.owner.origin;
-               else
-                       veloc = self.finaldest - self.owner.origin;
-               veloc = veloc * (1 / sys_frametime); // so it arrives for the next frame
-
-               self.owner.velocity = veloc;
-               self.nextthink = nexttick;
-       } else {
-               // derivative: delta + 2 * delta2 (e.g. for angle positioning)
-               oldself = self;
-               self.owner.think = self.think1;
-               self = self.owner;
-               remove(oldself);
-               self.think();
-       }
-}
-
-void SUB_CalcMove_controller_setbezier (entity controller, vector org, vector control, vector dest)
-{
-       // 0 * (1-t) * (1-t) + 2 * control * t * (1-t) + dest * t * t
-       // 2 * control * t - 2 * control * t * t + dest * t * t
-       // 2 * control * t + (dest - 2 * control) * t * t
-
-       controller.origin = org; // starting point
-       control -= org;
-       dest -= org;
-
-       controller.destvec = 2 * control; // control point
-       controller.destvec2 = dest - 2 * control; // quadratic part required to reach end point
-       // also: initial d/dphasepos origin = 2 * control, final speed = 2 * (dest - control)
-}
-
-void SUB_CalcMove_controller_setlinear (entity controller, vector org, vector dest)
-{
-       // 0 * (1-t) * (1-t) + 2 * control * t * (1-t) + dest * t * t
-       // 2 * control * t - 2 * control * t * t + dest * t * t
-       // 2 * control * t + (dest - 2 * control) * t * t
-
-       controller.origin = org; // starting point
-       dest -= org;
-
-       controller.destvec = dest; // end point
-       controller.destvec2 = '0 0 0';
-}
-
-void SUB_CalcMove_Bezier (vector tcontrol, vector tdest, float tspeedtype, float tspeed, void() func)
-{
-       float   traveltime;
-       entity controller;
-
-       if (!tspeed)
-               objerror ("No speed is defined!");
-
-       self.think1 = func;
-       self.finaldest = tdest;
-       self.think = SUB_CalcMoveDone;
-
-       switch(tspeedtype)
-       {
-               default:
-               case TSPEED_START:
-                       traveltime = 2 * vlen(tcontrol - self.origin) / tspeed;
-                       break;
-               case TSPEED_END:
-                       traveltime = 2 * vlen(tcontrol - tdest)       / tspeed;
-                       break;
-               case TSPEED_LINEAR:
-                       traveltime = vlen(tdest - self.origin)        / tspeed;
-                       break;
-               case TSPEED_TIME:
-                       traveltime = tspeed;
-                       break;
-       }
-
-       if (traveltime < 0.1) // useless anim
-       {
-               self.velocity = '0 0 0';
-               self.nextthink = self.ltime + 0.1;
-               return;
-       }
-
-       controller = spawn();
-       controller.classname = "SUB_CalcMove_controller";
-       controller.owner = self;
-       controller.platmovetype = self.platmovetype;
-       controller.platmovetype_start = self.platmovetype_start;
-       controller.platmovetype_end = self.platmovetype_end;
-       SUB_CalcMove_controller_setbezier(controller, self.origin, tcontrol, tdest);
-       controller.finaldest = (tdest + '0 0 0.125'); // where do we want to end? Offset to overshoot a bit.
-       controller.animstate_starttime = time;
-       controller.animstate_endtime = time + traveltime;
-       controller.think = SUB_CalcMove_controller_think;
-       controller.think1 = self.think;
-
-       // the thinking is now done by the controller
-       self.think = SUB_NullThink; // for PushMove
-       self.nextthink = self.ltime + traveltime;
-
-       // invoke controller
-       self = controller;
-       self.think();
-       self = self.owner;
-}
-
-void SUB_CalcMove (vector tdest, float tspeedtype, float tspeed, void() func)
-{
-       vector  delta;
-       float   traveltime;
-
-       if (!tspeed)
-               objerror ("No speed is defined!");
-
-       self.think1 = func;
-       self.finaldest = tdest;
-       self.think = SUB_CalcMoveDone;
-
-       if (tdest == self.origin)
-       {
-               self.velocity = '0 0 0';
-               self.nextthink = self.ltime + 0.1;
-               return;
-       }
-
-       delta = tdest - self.origin;
-
-       switch(tspeedtype)
-       {
-               default:
-               case TSPEED_START:
-               case TSPEED_END:
-               case TSPEED_LINEAR:
-                       traveltime = vlen (delta) / tspeed;
-                       break;
-               case TSPEED_TIME:
-                       traveltime = tspeed;
-                       break;
-       }
-
-       // 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.
-        // The only currently implemented alternative movement is linear (value 1)
-       if (traveltime < 0.15 || (self.platmovetype_start == 1 && self.platmovetype_end == 1)) // is this correct?
-       {
-               self.velocity = delta * (1/traveltime); // QuakeC doesn't allow vector/float division
-               self.nextthink = self.ltime + traveltime;
-               return;
-       }
-
-       // now just run like a bezier curve...
-       SUB_CalcMove_Bezier((self.origin + tdest) * 0.5, tdest, tspeedtype, tspeed, func);
-}
-
-void SUB_CalcMoveEnt (entity ent, vector tdest, float tspeedtype, float tspeed, void() func)
-{
-       entity  oldself;
-
-       oldself = self;
-       self = ent;
-
-       SUB_CalcMove (tdest, tspeedtype, tspeed, func);
-
-       self = oldself;
-}
-
-/*
-=============
-SUB_CalcAngleMove
-
-calculate self.avelocity and self.nextthink to reach destangle from
-self.angles rotating
-
-The calling function should make sure self.think is valid
-===============
-*/
-void SUB_CalcAngleMoveDone (void)
-{
-       // After rotating, set angle to exact final angle
-       self.angles = self.finalangle;
-       self.avelocity = '0 0 0';
-       self.nextthink = -1;
-       if (self.think1)
-               self.think1 ();
-}
-
-// FIXME: I fixed this function only for rotation around the main axes
-void SUB_CalcAngleMove (vector destangle, float tspeedtype, float tspeed, void() func)
-{
-       vector  delta;
-       float   traveltime;
-
-       if (!tspeed)
-               objerror ("No speed is defined!");
-
-       // take the shortest distance for the angles
-       self.angles_x -= 360 * floor((self.angles.x - destangle.x) / 360 + 0.5);
-       self.angles_y -= 360 * floor((self.angles.y - destangle.y) / 360 + 0.5);
-       self.angles_z -= 360 * floor((self.angles.z - destangle.z) / 360 + 0.5);
-       delta = destangle - self.angles;
-
-       switch(tspeedtype)
-       {
-               default:
-               case TSPEED_START:
-               case TSPEED_END:
-               case TSPEED_LINEAR:
-                       traveltime = vlen (delta) / tspeed;
-                       break;
-               case TSPEED_TIME:
-                       traveltime = tspeed;
-                       break;
-       }
-
-       self.think1 = func;
-       self.finalangle = destangle;
-       self.think = SUB_CalcAngleMoveDone;
-
-       if (traveltime < 0.1)
-       {
-               self.avelocity = '0 0 0';
-               self.nextthink = self.ltime + 0.1;
-               return;
-       }
-
-       self.avelocity = delta * (1 / traveltime);
-       self.nextthink = self.ltime + traveltime;
-}
-
-void SUB_CalcAngleMoveEnt (entity ent, vector destangle, float tspeedtype, float tspeed, void() func)
-{
-       entity  oldself;
-
-       oldself = self;
-       self = ent;
-
-       SUB_CalcAngleMove (destangle, tspeedtype, tspeed, func);
-
-       self = oldself;
-}
-
 /*
 ==================
 main
index 04477e62919dc48477448f104366f39d2f8224e6..d62bbb6124e57bd014b2976bde0d5532990b5f98 100644 (file)
@@ -70,12 +70,6 @@ void SUB_CalcMove_controller_setbezier (entity controller, vector org, vector co
 
 void SUB_CalcMove_controller_setlinear (entity controller, vector org, vector dest);
 
-float TSPEED_TIME = -1;
-float TSPEED_LINEAR = 0;
-float TSPEED_START = 1;
-float TSPEED_END = 2;
-// TODO average too?
-
 void SUB_CalcMove_Bezier (vector tcontrol, vector tdest, float tspeedtype, float tspeed, void() func);
 
 void SUB_CalcMove (vector tdest, float tspeedtype, float tspeed, void() func);
diff --git a/qcsrc/server/g_triggers.qc b/qcsrc/server/g_triggers.qc
deleted file mode 100644 (file)
index cdca7e2..0000000
+++ /dev/null
@@ -1,2140 +0,0 @@
-#include "g_triggers.qh"
-#include "_all.qh"
-
-#include "weapons/csqcprojectile.qh"
-#include "../common/deathtypes.qh"
-#include "../warpzonelib/util_server.qh"
-#include "g_subs.qh"
-#include "t_jumppads.qh"
-
-void SUB_DontUseTargets()
-{
-}
-
-
-void DelayThink()
-{
-       activator = self.enemy;
-       SUB_UseTargets ();
-       remove(self);
-}
-
-/*
-==============================
-SUB_UseTargets
-
-the global "activator" should be set to the entity that initiated the firing.
-
-If self.delay is set, a DelayedUse entity will be created that will actually
-do the SUB_UseTargets after that many seconds have passed.
-
-Centerprints any self.message to the activator.
-
-Removes all entities with a targetname that match self.killtarget,
-and removes them, so some events can remove other triggers.
-
-Search for (string)targetname in all entities that
-match (string)self.target and call their .use function
-
-==============================
-*/
-void SUB_UseTargets()
-{
-       entity t, stemp, otemp, act;
-       string s;
-       float i;
-
-//
-// check for a delay
-//
-       if (self.delay)
-       {
-       // create a temp object to fire at a later time
-               t = spawn();
-               t.classname = "DelayedUse";
-               t.nextthink = time + self.delay;
-               t.think = DelayThink;
-               t.enemy = activator;
-               t.message = self.message;
-               t.killtarget = self.killtarget;
-               t.target = self.target;
-               t.target2 = self.target2;
-               t.target3 = self.target3;
-               t.target4 = self.target4;
-               return;
-       }
-
-
-//
-// print the message
-//
-       if(self)
-       if(IS_PLAYER(activator) && self.message != "")
-       if(IS_REAL_CLIENT(activator))
-       {
-               centerprint(activator, self.message);
-               if (self.noise == "")
-                       play2(activator, "misc/talk.wav");
-       }
-
-//
-// kill the killtagets
-//
-       s = self.killtarget;
-       if (s != "")
-       {
-               for(t = world; (t = find(t, targetname, s)); )
-                       remove(t);
-       }
-
-//
-// fire targets
-//
-       act = activator;
-       stemp = self;
-       otemp = other;
-
-       if(stemp.target_random)
-               RandomSelection_Init();
-
-       for(i = 0; i < 4; ++i)
-       {
-               switch(i)
-               {
-                       default:
-                       case 0: s = stemp.target; break;
-                       case 1: s = stemp.target2; break;
-                       case 2: s = stemp.target3; break;
-                       case 3: s = stemp.target4; break;
-               }
-               if (s != "")
-               {
-                       // Flag to set func_clientwall state
-                       // 1 == deactivate, 2 == activate, 0 == do nothing
-                       float aw_flag = self.antiwall_flag;
-                       for(t = world; (t = find(t, targetname, s)); )
-                       if(t.use)
-                       {
-                               if(stemp.target_random)
-                               {
-                                       RandomSelection_Add(t, 0, string_null, 1, 0);
-                               }
-                               else
-                               {
-                                       if (t.classname == "func_clientwall" || t.classname == "func_clientillusionary")
-                                               t.antiwall_flag = aw_flag;
-                                       self = t;
-                                       other = stemp;
-                                       activator = act;
-                                       self.use();
-                               }
-                       }
-               }
-       }
-
-       if(stemp.target_random && RandomSelection_chosen_ent)
-       {
-               self = RandomSelection_chosen_ent;
-               other = stemp;
-               activator = act;
-               self.use();
-       }
-
-       activator = act;
-       self = stemp;
-       other = otemp;
-}
-
-
-//=============================================================================
-
-// the wait time has passed, so set back up for another activation
-void multi_wait()
-{
-       if (self.max_health)
-       {
-               self.health = self.max_health;
-               self.takedamage = DAMAGE_YES;
-               self.solid = SOLID_BBOX;
-       }
-}
-
-
-// the trigger was just touched/killed/used
-// self.enemy should be set to the activator so it can be held through a delay
-// so wait for the delay time before firing
-void multi_trigger()
-{
-       if (self.nextthink > time)
-       {
-               return;         // allready been triggered
-       }
-
-       if (self.classname == "trigger_secret")
-       {
-               if (!IS_PLAYER(self.enemy))
-                       return;
-               found_secrets = found_secrets + 1;
-               WriteByte (MSG_ALL, SVC_FOUNDSECRET);
-       }
-
-       if (self.noise)
-               sound (self.enemy, CH_TRIGGER, self.noise, VOL_BASE, ATTEN_NORM);
-
-// don't trigger again until reset
-       self.takedamage = DAMAGE_NO;
-
-       activator = self.enemy;
-       other = self.goalentity;
-       SUB_UseTargets();
-
-       if (self.wait > 0)
-       {
-               self.think = multi_wait;
-               self.nextthink = time + self.wait;
-       }
-       else if (self.wait == 0)
-       {
-               multi_wait(); // waiting finished
-       }
-       else
-       {       // we can't just remove (self) here, because this is a touch function
-               // called wheil C code is looping through area links...
-               self.touch = func_null;
-       }
-}
-
-void multi_use()
-{
-       self.goalentity = other;
-       self.enemy = activator;
-       multi_trigger();
-}
-
-void multi_touch()
-{
-       if(!(self.spawnflags & 2))
-       if(!other.iscreature)
-                       return;
-
-       if(self.team)
-               if(((self.spawnflags & 4) == 0) == (self.team != other.team))
-                       return;
-
-// if the trigger has an angles field, check player's facing direction
-       if (self.movedir != '0 0 0')
-       {
-               makevectors (other.angles);
-               if (v_forward * self.movedir < 0)
-                       return;         // not facing the right way
-       }
-
-       EXACTTRIGGER_TOUCH;
-
-       self.enemy = other;
-       self.goalentity = other;
-       multi_trigger ();
-}
-
-void multi_eventdamage (entity inflictor, entity attacker, float damage, int deathtype, vector hitloc, vector force)
-{
-       if (!self.takedamage)
-               return;
-       if(self.spawnflags & DOOR_NOSPLASH)
-               if(!(DEATH_ISSPECIAL(deathtype)) && (deathtype & HITTYPE_SPLASH))
-                       return;
-       self.health = self.health - damage;
-       if (self.health <= 0)
-       {
-               self.enemy = attacker;
-               self.goalentity = inflictor;
-               multi_trigger();
-       }
-}
-
-void multi_reset()
-{
-       if ( !(self.spawnflags & SPAWNFLAG_NOTOUCH) )
-               self.touch = multi_touch;
-       if (self.max_health)
-       {
-               self.health = self.max_health;
-               self.takedamage = DAMAGE_YES;
-               self.solid = SOLID_BBOX;
-       }
-       self.think = func_null;
-       self.nextthink = 0;
-       self.team = self.team_saved;
-}
-
-/*QUAKED spawnfunc_trigger_multiple (.5 .5 .5) ? notouch
-Variable sized repeatable trigger.  Must be targeted at one or more entities.  If "health" is set, the trigger must be killed to activate each time.
-If "delay" is set, the trigger waits some time after activating before firing.
-"wait" : Seconds between triggerings. (.2 default)
-If notouch is set, the trigger is only fired by other entities, not by touching.
-NOTOUCH has been obsoleted by spawnfunc_trigger_relay!
-sounds
-1)     secret
-2)     beep beep
-3)     large switch
-4)
-set "message" to text string
-*/
-void spawnfunc_trigger_multiple()
-{
-       self.reset = multi_reset;
-       if (self.sounds == 1)
-       {
-               precache_sound ("misc/secret.wav");
-               self.noise = "misc/secret.wav";
-       }
-       else if (self.sounds == 2)
-       {
-               precache_sound ("misc/talk.wav");
-               self.noise = "misc/talk.wav";
-       }
-       else if (self.sounds == 3)
-       {
-               precache_sound ("misc/trigger1.wav");
-               self.noise = "misc/trigger1.wav";
-       }
-
-       if (!self.wait)
-               self.wait = 0.2;
-       else if(self.wait < -1)
-               self.wait = 0;
-       self.use = multi_use;
-
-       EXACTTRIGGER_INIT;
-
-       self.team_saved = self.team;
-
-       if (self.health)
-       {
-               if (self.spawnflags & SPAWNFLAG_NOTOUCH)
-                       objerror ("health and notouch don't make sense\n");
-               self.max_health = self.health;
-               self.event_damage = multi_eventdamage;
-               self.takedamage = DAMAGE_YES;
-               self.solid = SOLID_BBOX;
-               setorigin (self, self.origin);  // make sure it links into the world
-       }
-       else
-       {
-               if ( !(self.spawnflags & SPAWNFLAG_NOTOUCH) )
-               {
-                       self.touch = multi_touch;
-                       setorigin (self, self.origin);  // make sure it links into the world
-               }
-       }
-}
-
-
-/*QUAKED spawnfunc_trigger_once (.5 .5 .5) ? notouch
-Variable sized trigger. Triggers once, then removes itself.  You must set the key "target" to the name of another object in the level that has a matching
-"targetname".  If "health" is set, the trigger must be killed to activate.
-If notouch is set, the trigger is only fired by other entities, not by touching.
-if "killtarget" is set, any objects that have a matching "target" will be removed when the trigger is fired.
-if "angle" is set, the trigger will only fire when someone is facing the direction of the angle.  Use "360" for an angle of 0.
-sounds
-1)     secret
-2)     beep beep
-3)     large switch
-4)
-set "message" to text string
-*/
-void spawnfunc_trigger_once()
-{
-       self.wait = -1;
-       spawnfunc_trigger_multiple();
-}
-
-//=============================================================================
-
-/*QUAKED spawnfunc_trigger_relay (.5 .5 .5) (-8 -8 -8) (8 8 8)
-This fixed size trigger cannot be touched, it can only be fired by other events.  It can contain killtargets, targets, delays, and messages.
-*/
-void spawnfunc_trigger_relay()
-{
-       self.use = SUB_UseTargets;
-       self.reset = spawnfunc_trigger_relay; // this spawnfunc resets fully
-}
-
-void delay_use()
-{
-    self.think = SUB_UseTargets;
-    self.nextthink = self.wait;
-}
-
-void delay_reset()
-{
-       self.think = func_null;
-       self.nextthink = 0;
-}
-
-void spawnfunc_trigger_delay()
-{
-    if(!self.wait)
-        self.wait = 1;
-
-    self.use = delay_use;
-    self.reset = delay_reset;
-}
-
-//=============================================================================
-
-
-void counter_use()
-{
-       self.count -= 1;
-       if (self.count < 0)
-               return;
-
-       if (self.count == 0)
-       {
-               if(IS_PLAYER(activator) && (self.spawnflags & SPAWNFLAG_NOMESSAGE) == 0)
-                       Send_Notification(NOTIF_ONE, activator, MSG_CENTER, CENTER_SEQUENCE_COMPLETED);
-
-               self.enemy = activator;
-               multi_trigger ();
-       }
-       else
-       {
-               if(IS_PLAYER(activator) && (self.spawnflags & SPAWNFLAG_NOMESSAGE) == 0)
-               if(self.count >= 4)
-                       Send_Notification(NOTIF_ONE, activator, MSG_CENTER, CENTER_SEQUENCE_COUNTER);
-               else
-                       Send_Notification(NOTIF_ONE, activator, MSG_CENTER, CENTER_SEQUENCE_COUNTER_FEWMORE, self.count);
-       }
-}
-
-void counter_reset()
-{
-       self.count = self.cnt;
-       multi_reset();
-}
-
-/*QUAKED spawnfunc_trigger_counter (.5 .5 .5) ? nomessage
-Acts as an intermediary for an action that takes multiple inputs.
-
-If nomessage is not set, t will print "1 more.. " etc when triggered and "sequence complete" when finished.
-
-After the counter has been triggered "count" times (default 2), it will fire all of it's targets and remove itself.
-*/
-void spawnfunc_trigger_counter()
-{
-       self.wait = -1;
-       if (!self.count)
-               self.count = 2;
-       self.cnt = self.count;
-
-       self.use = counter_use;
-       self.reset = counter_reset;
-}
-
-void trigger_hurt_use()
-{
-       if(IS_PLAYER(activator))
-               self.enemy = activator;
-       else
-               self.enemy = world; // let's just destroy it, if taking over is too much work
-}
-
-void trigger_hurt_touch()
-{
-       if (self.active != ACTIVE_ACTIVE)
-               return;
-
-       if(self.team)
-               if(((self.spawnflags & 4) == 0) == (self.team != other.team))
-                       return;
-
-       // only do the EXACTTRIGGER_TOUCH checks when really needed (saves some cpu)
-       if (other.iscreature)
-       {
-               if (other.takedamage)
-               if (other.triggerhurttime < time)
-               {
-                       EXACTTRIGGER_TOUCH;
-                       other.triggerhurttime = time + 1;
-
-                       entity own;
-                       own = self.enemy;
-                       if (!IS_PLAYER(own))
-                       {
-                               own = self;
-                               self.enemy = world; // I still hate you all
-                       }
-
-                       Damage (other, self, own, self.dmg, DEATH_HURTTRIGGER, other.origin, '0 0 0');
-               }
-       }
-       else if(other.damagedbytriggers)
-       {
-               if(other.takedamage)
-               {
-                       EXACTTRIGGER_TOUCH;
-                       Damage(other, self, self, self.dmg, DEATH_HURTTRIGGER, other.origin, '0 0 0');
-               }
-       }
-
-       return;
-}
-
-/*QUAKED spawnfunc_trigger_hurt (.5 .5 .5) ?
-Any object touching this will be hurt
-set dmg to damage amount
-defalt dmg = 5
-*/
-void spawnfunc_trigger_hurt()
-{
-       EXACTTRIGGER_INIT;
-       self.active = ACTIVE_ACTIVE;
-       self.touch = trigger_hurt_touch;
-       self.use = trigger_hurt_use;
-       self.enemy = world; // I hate you all
-       if (!self.dmg)
-               self.dmg = 1000;
-       if (self.message == "")
-               self.message = "was in the wrong place";
-       if (self.message2 == "")
-               self.message2 = "was thrown into a world of hurt by";
-       // self.message = "someone like %s always gets wrongplaced";
-
-       if(!trigger_hurt_first)
-               trigger_hurt_first = self;
-       if(trigger_hurt_last)
-               trigger_hurt_last.trigger_hurt_next = self;
-       trigger_hurt_last = self;
-}
-
-float tracebox_hits_trigger_hurt(vector start, vector mi, vector ma, vector end)
-{
-       entity th;
-
-       for(th = trigger_hurt_first; th; th = th.trigger_hurt_next)
-               if(tracebox_hits_box(start, mi, ma, end, th.absmin, th.absmax))
-                       return true;
-
-       return false;
-}
-
-//////////////////////////////////////////////////////////////
-//
-//
-//
-//Trigger heal --a04191b92fbd93aa67214ef7e72d6d2e
-//
-//////////////////////////////////////////////////////////////
-
-void trigger_heal_touch()
-{
-       if (self.active != ACTIVE_ACTIVE)
-               return;
-
-       // only do the EXACTTRIGGER_TOUCH checks when really needed (saves some cpu)
-       if (other.iscreature)
-       {
-               if (other.takedamage)
-               if (!other.deadflag)
-               if (other.triggerhealtime < time)
-               {
-                       EXACTTRIGGER_TOUCH;
-                       other.triggerhealtime = time + 1;
-
-                       if (other.health < self.max_health)
-                       {
-                               other.health = min(other.health + self.health, self.max_health);
-                               other.pauserothealth_finished = max(other.pauserothealth_finished, time + autocvar_g_balance_pause_health_rot);
-                               sound (other, CH_TRIGGER, self.noise, VOL_BASE, ATTEN_NORM);
-                       }
-               }
-       }
-}
-
-void spawnfunc_trigger_heal()
-{
-       self.active = ACTIVE_ACTIVE;
-
-       EXACTTRIGGER_INIT;
-       self.touch = trigger_heal_touch;
-       if (!self.health)
-               self.health = 10;
-       if (!self.max_health)
-               self.max_health = 200; //Max health topoff for field
-       if(self.noise == "")
-               self.noise = "misc/mediumhealth.wav";
-       precache_sound(self.noise);
-}
-
-
-//////////////////////////////////////////////////////////////
-//
-//
-//
-//End trigger_heal
-//
-//////////////////////////////////////////////////////////////
-
-void trigger_gravity_remove(entity own)
-{
-       if(own.trigger_gravity_check.owner == own)
-       {
-               UpdateCSQCProjectile(own);
-               own.gravity = own.trigger_gravity_check.gravity;
-               remove(own.trigger_gravity_check);
-       }
-       else
-               backtrace("Removing a trigger_gravity_check with no valid owner");
-       own.trigger_gravity_check = world;
-}
-void trigger_gravity_check_think()
-{
-       // This spawns when a player enters the gravity zone and checks if he left.
-       // Each frame, self.count is set to 2 by trigger_gravity_touch() and decreased by 1 here.
-       // It the player has left the gravity trigger, this will be allowed to reach 0 and indicate that.
-       if(self.count <= 0)
-       {
-               if(self.owner.trigger_gravity_check == self)
-                       trigger_gravity_remove(self.owner);
-               else
-                       remove(self);
-               return;
-       }
-       else
-       {
-               self.count -= 1;
-               self.nextthink = time;
-       }
-}
-
-void trigger_gravity_use()
-{
-       self.state = !self.state;
-}
-
-void trigger_gravity_touch()
-{
-       float g;
-
-       if(self.state != true)
-               return;
-
-       EXACTTRIGGER_TOUCH;
-
-       g = self.gravity;
-
-       if (!(self.spawnflags & 1))
-       {
-               if(other.trigger_gravity_check)
-               {
-                       if(self == other.trigger_gravity_check.enemy)
-                       {
-                               // same?
-                               other.trigger_gravity_check.count = 2; // gravity one more frame...
-                               return;
-                       }
-
-                       // compare prio
-                       if(self.cnt > other.trigger_gravity_check.enemy.cnt)
-                               trigger_gravity_remove(other);
-                       else
-                               return;
-               }
-               other.trigger_gravity_check = spawn();
-               other.trigger_gravity_check.enemy = self;
-               other.trigger_gravity_check.owner = other;
-               other.trigger_gravity_check.gravity = other.gravity;
-               other.trigger_gravity_check.think = trigger_gravity_check_think;
-               other.trigger_gravity_check.nextthink = time;
-               other.trigger_gravity_check.count = 2;
-               if(other.gravity)
-                       g *= other.gravity;
-       }
-
-       if (other.gravity != g)
-       {
-               other.gravity = g;
-               if(self.noise != "")
-                       sound (other, CH_TRIGGER, self.noise, VOL_BASE, ATTEN_NORM);
-               UpdateCSQCProjectile(self.owner);
-       }
-}
-
-void spawnfunc_trigger_gravity()
-{
-       if(self.gravity == 1)
-               return;
-
-       EXACTTRIGGER_INIT;
-       self.touch = trigger_gravity_touch;
-       if(self.noise != "")
-               precache_sound(self.noise);
-
-       self.state = true;
-       IFTARGETED
-       {
-               self.use = trigger_gravity_use;
-               if(self.spawnflags & 2)
-                       self.state = false;
-       }
-}
-
-//=============================================================================
-
-// TODO add a way to do looped sounds with sound(); then complete this entity
-void target_speaker_use_activator()
-{
-       if (!IS_REAL_CLIENT(activator))
-               return;
-       string snd;
-       if(substring(self.noise, 0, 1) == "*")
-       {
-               var .string sample;
-               sample = GetVoiceMessageSampleField(substring(self.noise, 1, -1));
-               if(GetPlayerSoundSampleField_notFound)
-                       snd = "misc/null.wav";
-               else if (activator.(sample) == "")
-                       snd = "misc/null.wav";
-               else
-               {
-                       tokenize_console(activator.(sample));
-                       float n;
-                       n = stof(argv(1));
-                       if(n > 0)
-                               snd = strcat(argv(0), ftos(floor(random() * n + 1)), ".wav"); // randomization
-                       else
-                               snd = strcat(argv(0), ".wav"); // randomization
-               }
-       }
-       else
-               snd = self.noise;
-       msg_entity = activator;
-       soundto(MSG_ONE, self, CH_TRIGGER, snd, VOL_BASE * self.volume, self.atten);
-}
-void target_speaker_use_on()
-{
-       string snd;
-       if(substring(self.noise, 0, 1) == "*")
-       {
-               var .string sample;
-               sample = GetVoiceMessageSampleField(substring(self.noise, 1, -1));
-               if(GetPlayerSoundSampleField_notFound)
-                       snd = "misc/null.wav";
-               else if (activator.(sample) == "")
-                       snd = "misc/null.wav";
-               else
-               {
-                       tokenize_console(activator.(sample));
-                       float n;
-                       n = stof(argv(1));
-                       if(n > 0)
-                               snd = strcat(argv(0), ftos(floor(random() * n + 1)), ".wav"); // randomization
-                       else
-                               snd = strcat(argv(0), ".wav"); // randomization
-               }
-       }
-       else
-               snd = self.noise;
-       sound(self, CH_TRIGGER_SINGLE, snd, VOL_BASE * self.volume, self.atten);
-       if(self.spawnflags & 3)
-               self.use = target_speaker_use_off;
-}
-void target_speaker_use_off()
-{
-       sound(self, CH_TRIGGER_SINGLE, "misc/null.wav", VOL_BASE * self.volume, self.atten);
-       self.use = target_speaker_use_on;
-}
-void target_speaker_reset()
-{
-       if(self.spawnflags & 1) // LOOPED_ON
-       {
-               if(self.use == target_speaker_use_on)
-                       target_speaker_use_on();
-       }
-       else if(self.spawnflags & 2)
-       {
-               if(self.use == target_speaker_use_off)
-                       target_speaker_use_off();
-       }
-}
-
-void spawnfunc_target_speaker()
-{
-       // TODO: "*" prefix to sound file name
-       // TODO: wait and random (just, HOW? random is not a field)
-       if(self.noise)
-               precache_sound (self.noise);
-
-       if(!self.atten && !(self.spawnflags & 4))
-       {
-               IFTARGETED
-                       self.atten = ATTEN_NORM;
-               else
-                       self.atten = ATTEN_STATIC;
-       }
-       else if(self.atten < 0)
-               self.atten = 0;
-
-       if(!self.volume)
-               self.volume = 1;
-
-       IFTARGETED
-       {
-               if(self.spawnflags & 8) // ACTIVATOR
-                       self.use = target_speaker_use_activator;
-               else if(self.spawnflags & 1) // LOOPED_ON
-               {
-                       target_speaker_use_on();
-                       self.reset = target_speaker_reset;
-               }
-               else if(self.spawnflags & 2) // LOOPED_OFF
-               {
-                       self.use = target_speaker_use_on;
-                       self.reset = target_speaker_reset;
-               }
-               else
-                       self.use = target_speaker_use_on;
-       }
-       else if(self.spawnflags & 1) // LOOPED_ON
-       {
-               ambientsound (self.origin, self.noise, VOL_BASE * self.volume, self.atten);
-               remove(self);
-       }
-       else if(self.spawnflags & 2) // LOOPED_OFF
-       {
-               objerror("This sound entity can never be activated");
-       }
-       else
-       {
-               // Quake/Nexuiz fallback
-               ambientsound (self.origin, self.noise, VOL_BASE * self.volume, self.atten);
-               remove(self);
-       }
-}
-
-
-void spawnfunc_func_stardust() {
-       self.effects = EF_STARDUST;
-}
-
-float pointparticles_SendEntity(entity to, float fl)
-{
-       WriteByte(MSG_ENTITY, ENT_CLIENT_POINTPARTICLES);
-
-       // optional features to save space
-       fl = fl & 0x0F;
-       if(self.spawnflags & 2)
-               fl |= 0x10; // absolute count on toggle-on
-       if(self.movedir != '0 0 0' || self.velocity != '0 0 0')
-               fl |= 0x20; // 4 bytes - saves CPU
-       if(self.waterlevel || self.count != 1)
-               fl |= 0x40; // 4 bytes - obscure features almost never used
-       if(self.mins != '0 0 0' || self.maxs != '0 0 0')
-               fl |= 0x80; // 14 bytes - saves lots of space
-
-       WriteByte(MSG_ENTITY, fl);
-       if(fl & 2)
-       {
-               if(self.state)
-                       WriteCoord(MSG_ENTITY, self.impulse);
-               else
-                       WriteCoord(MSG_ENTITY, 0); // off
-       }
-       if(fl & 4)
-       {
-               WriteCoord(MSG_ENTITY, self.origin.x);
-               WriteCoord(MSG_ENTITY, self.origin.y);
-               WriteCoord(MSG_ENTITY, self.origin.z);
-       }
-       if(fl & 1)
-       {
-               if(self.model != "null")
-               {
-                       WriteShort(MSG_ENTITY, self.modelindex);
-                       if(fl & 0x80)
-                       {
-                               WriteCoord(MSG_ENTITY, self.mins.x);
-                               WriteCoord(MSG_ENTITY, self.mins.y);
-                               WriteCoord(MSG_ENTITY, self.mins.z);
-                               WriteCoord(MSG_ENTITY, self.maxs.x);
-                               WriteCoord(MSG_ENTITY, self.maxs.y);
-                               WriteCoord(MSG_ENTITY, self.maxs.z);
-                       }
-               }
-               else
-               {
-                       WriteShort(MSG_ENTITY, 0);
-                       if(fl & 0x80)
-                       {
-                               WriteCoord(MSG_ENTITY, self.maxs.x);
-                               WriteCoord(MSG_ENTITY, self.maxs.y);
-                               WriteCoord(MSG_ENTITY, self.maxs.z);
-                       }
-               }
-               WriteShort(MSG_ENTITY, self.cnt);
-               if(fl & 0x20)
-               {
-                       WriteShort(MSG_ENTITY, compressShortVector(self.velocity));
-                       WriteShort(MSG_ENTITY, compressShortVector(self.movedir));
-               }
-               if(fl & 0x40)
-               {
-                       WriteShort(MSG_ENTITY, self.waterlevel * 16.0);
-                       WriteByte(MSG_ENTITY, self.count * 16.0);
-               }
-               WriteString(MSG_ENTITY, self.noise);
-               if(self.noise != "")
-               {
-                       WriteByte(MSG_ENTITY, floor(self.atten * 64));
-                       WriteByte(MSG_ENTITY, floor(self.volume * 255));
-               }
-               WriteString(MSG_ENTITY, self.bgmscript);
-               if(self.bgmscript != "")
-               {
-                       WriteByte(MSG_ENTITY, floor(self.bgmscriptattack * 64));
-                       WriteByte(MSG_ENTITY, floor(self.bgmscriptdecay * 64));
-                       WriteByte(MSG_ENTITY, floor(self.bgmscriptsustain * 255));
-                       WriteByte(MSG_ENTITY, floor(self.bgmscriptrelease * 64));
-               }
-       }
-       return 1;
-}
-
-void pointparticles_use()
-{
-       self.state = !self.state;
-       self.SendFlags |= 2;
-}
-
-void pointparticles_think()
-{
-       if(self.origin != self.oldorigin)
-       {
-               self.SendFlags |= 4;
-               self.oldorigin = self.origin;
-       }
-       self.nextthink = time;
-}
-
-void pointparticles_reset()
-{
-       if(self.spawnflags & 1)
-               self.state = 1;
-       else
-               self.state = 0;
-}
-
-void spawnfunc_func_pointparticles()
-{
-       if(self.model != "")
-               setmodel(self, self.model);
-       if(self.noise != "")
-               precache_sound (self.noise);
-
-       if(!self.bgmscriptsustain)
-               self.bgmscriptsustain = 1;
-       else if(self.bgmscriptsustain < 0)
-               self.bgmscriptsustain = 0;
-
-       if(!self.atten)
-               self.atten = ATTEN_NORM;
-       else if(self.atten < 0)
-               self.atten = 0;
-       if(!self.volume)
-               self.volume = 1;
-       if(!self.count)
-               self.count = 1;
-       if(!self.impulse)
-               self.impulse = 1;
-
-       if(!self.modelindex)
-       {
-               setorigin(self, self.origin + self.mins);
-               setsize(self, '0 0 0', self.maxs - self.mins);
-       }
-       if(!self.cnt)
-               self.cnt = particleeffectnum(self.mdl);
-
-       Net_LinkEntity(self, (self.spawnflags & 4), 0, pointparticles_SendEntity);
-
-       IFTARGETED
-       {
-               self.use = pointparticles_use;
-               self.reset = pointparticles_reset;
-               self.reset();
-       }
-       else
-               self.state = 1;
-       self.think = pointparticles_think;
-       self.nextthink = time;
-}
-
-void spawnfunc_func_sparks()
-{
-       // self.cnt is the amount of sparks that one burst will spawn
-       if(self.cnt < 1) {
-               self.cnt = 25.0; // nice default value
-       }
-
-       // self.wait is the probability that a sparkthink will spawn a spark shower
-       // range: 0 - 1, but 0 makes little sense, so...
-       if(self.wait < 0.05) {
-               self.wait = 0.25; // nice default value
-       }
-
-       self.count = self.cnt;
-       self.mins = '0 0 0';
-       self.maxs = '0 0 0';
-       self.velocity = '0 0 -1';
-       self.mdl = "TE_SPARK";
-       self.impulse = 10 * self.wait; // by default 2.5/sec
-       self.wait = 0;
-       self.cnt = 0; // use mdl
-
-       spawnfunc_func_pointparticles();
-}
-
-float rainsnow_SendEntity(entity to, int sf)
-{
-       WriteByte(MSG_ENTITY, ENT_CLIENT_RAINSNOW);
-       WriteByte(MSG_ENTITY, self.state);
-       WriteCoord(MSG_ENTITY, self.origin.x + self.mins.x);
-       WriteCoord(MSG_ENTITY, self.origin.y + self.mins.y);
-       WriteCoord(MSG_ENTITY, self.origin.z + self.mins.z);
-       WriteCoord(MSG_ENTITY, self.maxs.x - self.mins.x);
-       WriteCoord(MSG_ENTITY, self.maxs.y - self.mins.y);
-       WriteCoord(MSG_ENTITY, self.maxs.z - self.mins.z);
-       WriteShort(MSG_ENTITY, compressShortVector(self.dest));
-       WriteShort(MSG_ENTITY, self.count);
-       WriteByte(MSG_ENTITY, self.cnt);
-       return 1;
-}
-
-/*QUAKED spawnfunc_func_rain (0 .5 .8) ?
-This is an invisible area like a trigger, which rain falls inside of.
-
-Keys:
-"velocity"
- falling direction (should be something like '0 0 -700', use the X and Y velocity for wind)
-"cnt"
- sets color of rain (default 12 - white)
-"count"
- adjusts density, this many particles fall every second for a 1024x1024 area, default is 2000
-*/
-void spawnfunc_func_rain()
-{
-       self.dest = self.velocity;
-       self.velocity = '0 0 0';
-       if (!self.dest)
-               self.dest = '0 0 -700';
-       self.angles = '0 0 0';
-       self.movetype = MOVETYPE_NONE;
-       self.solid = SOLID_NOT;
-       SetBrushEntityModel();
-       if (!self.cnt)
-               self.cnt = 12;
-       if (!self.count)
-               self.count = 2000;
-       self.count = 0.01 * self.count * (self.size.x / 1024) * (self.size.y / 1024);
-       if (self.count < 1)
-               self.count = 1;
-       if(self.count > 65535)
-               self.count = 65535;
-
-       self.state = 1; // 1 is rain, 0 is snow
-       self.Version = 1;
-
-       Net_LinkEntity(self, false, 0, rainsnow_SendEntity);
-}
-
-
-/*QUAKED spawnfunc_func_snow (0 .5 .8) ?
-This is an invisible area like a trigger, which snow falls inside of.
-
-Keys:
-"velocity"
- falling direction (should be something like '0 0 -300', use the X and Y velocity for wind)
-"cnt"
- sets color of rain (default 12 - white)
-"count"
- adjusts density, this many particles fall every second for a 1024x1024 area, default is 2000
-*/
-void spawnfunc_func_snow()
-{
-       self.dest = self.velocity;
-       self.velocity = '0 0 0';
-       if (!self.dest)
-               self.dest = '0 0 -300';
-       self.angles = '0 0 0';
-       self.movetype = MOVETYPE_NONE;
-       self.solid = SOLID_NOT;
-       SetBrushEntityModel();
-       if (!self.cnt)
-               self.cnt = 12;
-       if (!self.count)
-               self.count = 2000;
-       self.count = 0.01 * self.count * (self.size.x / 1024) * (self.size.y / 1024);
-       if (self.count < 1)
-               self.count = 1;
-       if(self.count > 65535)
-               self.count = 65535;
-
-       self.state = 0; // 1 is rain, 0 is snow
-       self.Version = 1;
-
-       Net_LinkEntity(self, false, 0, rainsnow_SendEntity);
-}
-
-void misc_laser_aim()
-{
-       vector a;
-       if(self.enemy)
-       {
-               if(self.spawnflags & 2)
-               {
-                       if(self.enemy.origin != self.mangle)
-                       {
-                               self.mangle = self.enemy.origin;
-                               self.SendFlags |= 2;
-                       }
-               }
-               else
-               {
-                       a = vectoangles(self.enemy.origin - self.origin);
-                       a.x = -a.x;
-                       if(a != self.mangle)
-                       {
-                               self.mangle = a;
-                               self.SendFlags |= 2;
-                       }
-               }
-       }
-       else
-       {
-               if(self.angles != self.mangle)
-               {
-                       self.mangle = self.angles;
-                       self.SendFlags |= 2;
-               }
-       }
-       if(self.origin != self.oldorigin)
-       {
-               self.SendFlags |= 1;
-               self.oldorigin = self.origin;
-       }
-}
-
-void misc_laser_init()
-{
-       if(self.target != "")
-               self.enemy = find(world, targetname, self.target);
-}
-
-void misc_laser_think()
-{
-       vector o;
-       entity oldself;
-       entity hitent;
-       vector hitloc;
-
-       self.nextthink = time;
-
-       if(!self.state)
-               return;
-
-       misc_laser_aim();
-
-       if(self.enemy)
-       {
-               o = self.enemy.origin;
-               if (!(self.spawnflags & 2))
-                       o = self.origin + normalize(o - self.origin) * 32768;
-       }
-       else
-       {
-               makevectors(self.mangle);
-               o = self.origin + v_forward * 32768;
-       }
-
-       if(self.dmg || self.enemy.target != "")
-       {
-               traceline(self.origin, o, MOVE_NORMAL, self);
-       }
-       hitent = trace_ent;
-       hitloc = trace_endpos;
-
-       if(self.enemy.target != "") // DETECTOR laser
-       {
-               if(trace_ent.iscreature)
-               {
-                       self.pusher = hitent;
-                       if(!self.count)
-                       {
-                               self.count = 1;
-
-                               oldself = self;
-                               self = self.enemy;
-                               activator = self.pusher;
-                               SUB_UseTargets();
-                               self = oldself;
-                       }
-               }
-               else
-               {
-                       if(self.count)
-                       {
-                               self.count = 0;
-
-                               oldself = self;
-                               self = self.enemy;
-                               activator = self.pusher;
-                               SUB_UseTargets();
-                               self = oldself;
-                       }
-               }
-       }
-
-       if(self.dmg)
-       {
-               if(self.team)
-                       if(((self.spawnflags & 8) == 0) == (self.team != hitent.team))
-                               return;
-               if(hitent.takedamage)
-                       Damage(hitent, self, self, ((self.dmg < 0) ? 100000 : (self.dmg * frametime)), DEATH_HURTTRIGGER, hitloc, '0 0 0');
-       }
-}
-
-float laser_SendEntity(entity to, float fl)
-{
-       WriteByte(MSG_ENTITY, ENT_CLIENT_LASER);
-       fl = fl - (fl & 0xF0); // use that bit to indicate finite length laser
-       if(self.spawnflags & 2)
-               fl |= 0x80;
-       if(self.alpha)
-               fl |= 0x40;
-       if(self.scale != 1 || self.modelscale != 1)
-               fl |= 0x20;
-       if(self.spawnflags & 4)
-               fl |= 0x10;
-       WriteByte(MSG_ENTITY, fl);
-       if(fl & 1)
-       {
-               WriteCoord(MSG_ENTITY, self.origin.x);
-               WriteCoord(MSG_ENTITY, self.origin.y);
-               WriteCoord(MSG_ENTITY, self.origin.z);
-       }
-       if(fl & 8)
-       {
-               WriteByte(MSG_ENTITY, self.colormod.x * 255.0);
-               WriteByte(MSG_ENTITY, self.colormod.y * 255.0);
-               WriteByte(MSG_ENTITY, self.colormod.z * 255.0);
-               if(fl & 0x40)
-                       WriteByte(MSG_ENTITY, self.alpha * 255.0);
-               if(fl & 0x20)
-               {
-                       WriteByte(MSG_ENTITY, bound(0, self.scale * 16.0, 255));
-                       WriteByte(MSG_ENTITY, bound(0, self.modelscale * 16.0, 255));
-               }
-               if((fl & 0x80) || !(fl & 0x10)) // effect doesn't need sending if the laser is infinite and has collision testing turned off
-                       WriteShort(MSG_ENTITY, self.cnt + 1);
-       }
-       if(fl & 2)
-       {
-               if(fl & 0x80)
-               {
-                       WriteCoord(MSG_ENTITY, self.enemy.origin.x);
-                       WriteCoord(MSG_ENTITY, self.enemy.origin.y);
-                       WriteCoord(MSG_ENTITY, self.enemy.origin.z);
-               }
-               else
-               {
-                       WriteAngle(MSG_ENTITY, self.mangle.x);
-                       WriteAngle(MSG_ENTITY, self.mangle.y);
-               }
-       }
-       if(fl & 4)
-               WriteByte(MSG_ENTITY, self.state);
-       return 1;
-}
-
-/*QUAKED spawnfunc_misc_laser (.5 .5 .5) ? START_ON DEST_IS_FIXED
-Any object touching the beam will be hurt
-Keys:
-"target"
- spawnfunc_target_position where the laser ends
-"mdl"
- name of beam end effect to use
-"colormod"
- color of the beam (default: red)
-"dmg"
- damage per second (-1 for a laser that kills immediately)
-*/
-void laser_use()
-{
-       self.state = !self.state;
-       self.SendFlags |= 4;
-       misc_laser_aim();
-}
-
-void laser_reset()
-{
-       if(self.spawnflags & 1)
-               self.state = 1;
-       else
-               self.state = 0;
-}
-
-void spawnfunc_misc_laser()
-{
-       if(self.mdl)
-       {
-               if(self.mdl == "none")
-                       self.cnt = -1;
-               else
-               {
-                       self.cnt = particleeffectnum(self.mdl);
-                       if(self.cnt < 0)
-                               if(self.dmg)
-                                       self.cnt = particleeffectnum("laser_deadly");
-               }
-       }
-       else if(!self.cnt)
-       {
-               if(self.dmg)
-                       self.cnt = particleeffectnum("laser_deadly");
-               else
-                       self.cnt = -1;
-       }
-       if(self.cnt < 0)
-               self.cnt = -1;
-
-       if(self.colormod == '0 0 0')
-               if(!self.alpha)
-                       self.colormod = '1 0 0';
-       if(self.message == "")
-               self.message = "saw the light";
-       if (self.message2 == "")
-               self.message2 = "was pushed into a laser by";
-       if(!self.scale)
-               self.scale = 1;
-       if(!self.modelscale)
-               self.modelscale = 1;
-       else if(self.modelscale < 0)
-               self.modelscale = 0;
-       self.think = misc_laser_think;
-       self.nextthink = time;
-       InitializeEntity(self, misc_laser_init, INITPRIO_FINDTARGET);
-
-       self.mangle = self.angles;
-
-       Net_LinkEntity(self, false, 0, laser_SendEntity);
-
-       IFTARGETED
-       {
-               self.reset = laser_reset;
-               laser_reset();
-               self.use = laser_use;
-       }
-       else
-               self.state = 1;
-}
-
-// tZorks trigger impulse / gravity
-
-// targeted (directional) mode
-void trigger_impulse_touch1()
-{
-       entity targ;
-    float pushdeltatime;
-    float str;
-
-       if (self.active != ACTIVE_ACTIVE)
-               return;
-
-       if (!isPushable(other))
-               return;
-
-       EXACTTRIGGER_TOUCH;
-
-    targ = find(world, targetname, self.target);
-    if(!targ)
-    {
-        objerror("trigger_force without a (valid) .target!\n");
-        remove(self);
-        return;
-    }
-
-    str = min(self.radius, vlen(self.origin - other.origin));
-
-    if(self.falloff == 1)
-        str = (str / self.radius) * self.strength;
-    else if(self.falloff == 2)
-        str = (1 - (str / self.radius)) * self.strength;
-    else
-        str = self.strength;
-
-    pushdeltatime = time - other.lastpushtime;
-    if (pushdeltatime > 0.15) pushdeltatime = 0;
-    other.lastpushtime = time;
-    if(!pushdeltatime) return;
-
-    other.velocity = other.velocity + normalize(targ.origin - self.origin) * str * pushdeltatime;
-    other.flags &= ~FL_ONGROUND;
-    UpdateCSQCProjectile(other);
-}
-
-// Directionless (accelerator/decelerator) mode
-void trigger_impulse_touch2()
-{
-    float pushdeltatime;
-
-       if (self.active != ACTIVE_ACTIVE)
-               return;
-
-       if (!isPushable(other))
-               return;
-
-       EXACTTRIGGER_TOUCH;
-
-    pushdeltatime = time - other.lastpushtime;
-    if (pushdeltatime > 0.15) pushdeltatime = 0;
-    other.lastpushtime = time;
-    if(!pushdeltatime) return;
-
-    // div0: ticrate independent, 1 = identity (not 20)
-    other.velocity = other.velocity * pow(self.strength, pushdeltatime);
-    UpdateCSQCProjectile(other);
-}
-
-// Spherical (gravity/repulsor) mode
-void trigger_impulse_touch3()
-{
-    float pushdeltatime;
-    float str;
-
-       if (self.active != ACTIVE_ACTIVE)
-               return;
-
-       if (!isPushable(other))
-               return;
-
-       EXACTTRIGGER_TOUCH;
-
-    pushdeltatime = time - other.lastpushtime;
-    if (pushdeltatime > 0.15) pushdeltatime = 0;
-    other.lastpushtime = time;
-    if(!pushdeltatime) return;
-
-    setsize(self, '-1 -1 -1' * self.radius,'1 1 1' * self.radius);
-
-       str = min(self.radius, vlen(self.origin - other.origin));
-
-    if(self.falloff == 1)
-        str = (1 - str / self.radius) * self.strength; // 1 in the inside
-    else if(self.falloff == 2)
-        str = (str / self.radius) * self.strength; // 0 in the inside
-    else
-        str = self.strength;
-
-    other.velocity = other.velocity + normalize(other.origin - self.origin) * str * pushdeltatime;
-    UpdateCSQCProjectile(other);
-}
-
-/*QUAKED spawnfunc_trigger_impulse (.5 .5 .5) ?
--------- KEYS --------
-target : If this is set, this points to the spawnfunc_target_position to which the player will get pushed.
-         If not, this trigger acts like a damper/accelerator field.
-
-strength : This is how mutch force to add in the direction of .target each second
-           when .target is set. If not, this is hoe mutch to slow down/accelerate
-           someting cought inside this trigger. (1=no change, 0,5 half speed rougthly each tic, 2 = doubble)
-
-radius   : If set, act as a spherical device rather then a liniar one.
-
-falloff : 0 = none, 1 = liniar, 2 = inverted liniar
-
--------- NOTES --------
-Use a brush textured with common/origin in the trigger entity to determine the origin of the force
-in directional and sperical mode. For damper/accelerator mode this is not nessesary (and has no effect).
-*/
-
-void spawnfunc_trigger_impulse()
-{
-       self.active = ACTIVE_ACTIVE;
-
-       EXACTTRIGGER_INIT;
-    if(self.radius)
-    {
-        if(!self.strength) self.strength = 2000 * autocvar_g_triggerimpulse_radial_multiplier;
-        setorigin(self, self.origin);
-        setsize(self, '-1 -1 -1' * self.radius,'1 1 1' * self.radius);
-        self.touch = trigger_impulse_touch3;
-    }
-    else
-    {
-        if(self.target)
-        {
-            if(!self.strength) self.strength = 950 * autocvar_g_triggerimpulse_directional_multiplier;
-            self.touch = trigger_impulse_touch1;
-        }
-        else
-        {
-            if(!self.strength) self.strength = 0.9;
-                       self.strength = pow(self.strength, autocvar_g_triggerimpulse_accel_power) * autocvar_g_triggerimpulse_accel_multiplier;
-            self.touch = trigger_impulse_touch2;
-        }
-    }
-}
-
-/*QUAKED spawnfunc_trigger_flipflop (.5 .5 .5) (-8 -8 -8) (8 8 8) START_ENABLED
-"Flip-flop" trigger gate... lets only every second trigger event through
-*/
-void flipflop_use()
-{
-       self.state = !self.state;
-       if(self.state)
-               SUB_UseTargets();
-}
-
-void spawnfunc_trigger_flipflop()
-{
-       if(self.spawnflags & 1)
-               self.state = 1;
-       self.use = flipflop_use;
-       self.reset = spawnfunc_trigger_flipflop; // perfect resetter
-}
-
-/*QUAKED spawnfunc_trigger_monoflop (.5 .5 .5) (-8 -8 -8) (8 8 8)
-"Mono-flop" trigger gate... turns one trigger event into one "on" and one "off" event, separated by a delay of "wait"
-*/
-void monoflop_use()
-{
-       self.nextthink = time + self.wait;
-       self.enemy = activator;
-       if(self.state)
-               return;
-       self.state = 1;
-       SUB_UseTargets();
-}
-void monoflop_fixed_use()
-{
-       if(self.state)
-               return;
-       self.nextthink = time + self.wait;
-       self.state = 1;
-       self.enemy = activator;
-       SUB_UseTargets();
-}
-
-void monoflop_think()
-{
-       self.state = 0;
-       activator = self.enemy;
-       SUB_UseTargets();
-}
-
-void monoflop_reset()
-{
-       self.state = 0;
-       self.nextthink = 0;
-}
-
-void spawnfunc_trigger_monoflop()
-{
-       if(!self.wait)
-               self.wait = 1;
-       if(self.spawnflags & 1)
-               self.use = monoflop_fixed_use;
-       else
-               self.use = monoflop_use;
-       self.think = monoflop_think;
-       self.state = 0;
-       self.reset = monoflop_reset;
-}
-
-void multivibrator_send()
-{
-       float newstate;
-       float cyclestart;
-
-       cyclestart = floor((time + self.phase) / (self.wait + self.respawntime)) * (self.wait + self.respawntime) - self.phase;
-
-       newstate = (time < cyclestart + self.wait);
-
-       activator = self;
-       if(self.state != newstate)
-               SUB_UseTargets();
-       self.state = newstate;
-
-       if(self.state)
-               self.nextthink = cyclestart + self.wait + 0.01;
-       else
-               self.nextthink = cyclestart + self.wait + self.respawntime + 0.01;
-}
-
-void multivibrator_toggle()
-{
-       if(self.nextthink == 0)
-       {
-               multivibrator_send();
-       }
-       else
-       {
-               if(self.state)
-               {
-                       SUB_UseTargets();
-                       self.state = 0;
-               }
-               self.nextthink = 0;
-       }
-}
-
-void multivibrator_reset()
-{
-       if(!(self.spawnflags & 1))
-               self.nextthink = 0; // wait for a trigger event
-       else
-               self.nextthink = max(1, time);
-}
-
-/*QUAKED trigger_multivibrator (.5 .5 .5) (-8 -8 -8) (8 8 8) START_ON
-"Multivibrator" trigger gate... repeatedly sends trigger events. When triggered, turns on or off.
--------- KEYS --------
-target: trigger all entities with this targetname when it goes off
-targetname: name that identifies this entity so it can be triggered; when off, it always uses the OFF state
-phase: offset of the timing
-wait: "on" cycle time (default: 1)
-respawntime: "off" cycle time (default: same as wait)
--------- SPAWNFLAGS --------
-START_ON: assume it is already turned on (when targeted)
-*/
-void spawnfunc_trigger_multivibrator()
-{
-       if(!self.wait)
-               self.wait = 1;
-       if(!self.respawntime)
-               self.respawntime = self.wait;
-
-       self.state = 0;
-       self.use = multivibrator_toggle;
-       self.think = multivibrator_send;
-       self.nextthink = max(1, time);
-
-       IFTARGETED
-               multivibrator_reset();
-}
-
-
-void follow_init()
-{
-       entity src, dst;
-       src = world;
-       dst = world;
-       if(self.killtarget != "")
-               src = find(world, targetname, self.killtarget);
-       if(self.target != "")
-               dst = find(world, targetname, self.target);
-
-       if(!src && !dst)
-       {
-               objerror("follow: could not find target/killtarget");
-               return;
-       }
-
-       if(self.jointtype)
-       {
-               // already done :P entity must stay
-               self.aiment = src;
-               self.enemy = dst;
-       }
-       else if(!src || !dst)
-       {
-               objerror("follow: could not find target/killtarget");
-               return;
-       }
-       else if(self.spawnflags & 1)
-       {
-               // attach
-               if(self.spawnflags & 2)
-               {
-                       setattachment(dst, src, self.message);
-               }
-               else
-               {
-                       attach_sameorigin(dst, src, self.message);
-               }
-
-               dst.solid = SOLID_NOT; // solid doesn't work with attachment
-               remove(self);
-       }
-       else
-       {
-               if(self.spawnflags & 2)
-               {
-                       dst.movetype = MOVETYPE_FOLLOW;
-                       dst.aiment = src;
-                       // dst.punchangle = '0 0 0'; // keep unchanged
-                       dst.view_ofs = dst.origin;
-                       dst.v_angle = dst.angles;
-               }
-               else
-               {
-                       follow_sameorigin(dst, src);
-               }
-
-               remove(self);
-       }
-}
-
-void spawnfunc_misc_follow()
-{
-       InitializeEntity(self, follow_init, INITPRIO_FINDTARGET);
-}
-
-
-
-void gamestart_use() {
-       activator = self;
-       SUB_UseTargets();
-       remove(self);
-}
-
-void spawnfunc_trigger_gamestart() {
-       self.use = gamestart_use;
-       self.reset2 = spawnfunc_trigger_gamestart;
-
-       if(self.wait)
-       {
-               self.think = self.use;
-               self.nextthink = game_starttime + self.wait;
-       }
-       else
-               InitializeEntity(self, gamestart_use, INITPRIO_FINDTARGET);
-}
-
-
-
-
-void target_voicescript_clear(entity pl)
-{
-       pl.voicescript = world;
-}
-
-void target_voicescript_use()
-{
-       if(activator.voicescript != self)
-       {
-               activator.voicescript = self;
-               activator.voicescript_index = 0;
-               activator.voicescript_nextthink = time + self.delay;
-       }
-}
-
-void target_voicescript_next(entity pl)
-{
-       entity vs;
-       float i, n, dt;
-
-       vs = pl.voicescript;
-       if(!vs)
-               return;
-       if(vs.message == "")
-               return;
-       if (!IS_PLAYER(pl))
-               return;
-       if(gameover)
-               return;
-
-       if(time >= pl.voicescript_voiceend)
-       {
-               if(time >= pl.voicescript_nextthink)
-               {
-                       // get the next voice...
-                       n = tokenize_console(vs.message);
-
-                       if(pl.voicescript_index < vs.cnt)
-                               i = pl.voicescript_index * 2;
-                       else if(n > vs.cnt * 2)
-                               i = ((pl.voicescript_index - vs.cnt) % ((n - vs.cnt * 2 - 1) / 2)) * 2 + vs.cnt * 2 + 1;
-                       else
-                               i = -1;
-
-                       if(i >= 0)
-                       {
-                               play2(pl, strcat(vs.netname, "/", argv(i), ".wav"));
-                               dt = stof(argv(i + 1));
-                               if(dt >= 0)
-                               {
-                                       pl.voicescript_voiceend = time + dt;
-                                       pl.voicescript_nextthink = pl.voicescript_voiceend + vs.wait * (0.5 + random());
-                               }
-                               else
-                               {
-                                       pl.voicescript_voiceend = time - dt;
-                                       pl.voicescript_nextthink = pl.voicescript_voiceend;
-                               }
-
-                               pl.voicescript_index += 1;
-                       }
-                       else
-                       {
-                               pl.voicescript = world; // stop trying then
-                       }
-               }
-       }
-}
-
-void spawnfunc_target_voicescript()
-{
-       // netname: directory of the sound files
-       // message: list of "sound file" duration "sound file" duration, a *, and again a list
-       //          foo1 4.1 foo2 4.0 foo3 -3.1 * fool1 1.1 fool2 7.1 fool3 9.1 fool4 3.7
-       //          Here, a - in front of the duration means that no delay is to be
-       //          added after this message
-       // wait: average time between messages
-       // delay: initial delay before the first message
-
-       float i, n;
-       self.use = target_voicescript_use;
-
-       n = tokenize_console(self.message);
-       self.cnt = n / 2;
-       for(i = 0; i+1 < n; i += 2)
-       {
-               if(argv(i) == "*")
-               {
-                       self.cnt = i / 2;
-                       ++i;
-               }
-               precache_sound(strcat(self.netname, "/", argv(i), ".wav"));
-       }
-}
-
-
-
-void trigger_relay_teamcheck_use()
-{
-       if(activator.team)
-       {
-               if(self.spawnflags & 2)
-               {
-                       if(activator.team != self.team)
-                               SUB_UseTargets();
-               }
-               else
-               {
-                       if(activator.team == self.team)
-                               SUB_UseTargets();
-               }
-       }
-       else
-       {
-               if(self.spawnflags & 1)
-                       SUB_UseTargets();
-       }
-}
-
-void trigger_relay_teamcheck_reset()
-{
-       self.team = self.team_saved;
-}
-
-void spawnfunc_trigger_relay_teamcheck()
-{
-       self.team_saved = self.team;
-       self.use = trigger_relay_teamcheck_use;
-       self.reset = trigger_relay_teamcheck_reset;
-}
-
-
-
-void trigger_disablerelay_use()
-{
-       entity e;
-
-       float a, b;
-       a = b = 0;
-
-       for(e = world; (e = find(e, targetname, self.target)); )
-       {
-               if(e.use == SUB_UseTargets)
-               {
-                       e.use = SUB_DontUseTargets;
-                       ++a;
-               }
-               else if(e.use == SUB_DontUseTargets)
-               {
-                       e.use = SUB_UseTargets;
-                       ++b;
-               }
-       }
-
-       if((!a) == (!b))
-               print("Invalid use of trigger_disablerelay: ", ftos(a), " relays were on, ", ftos(b), " relays were off!\n");
-}
-
-void spawnfunc_trigger_disablerelay()
-{
-       self.use = trigger_disablerelay_use;
-}
-
-string trigger_magicear_processmessage(entity ear, entity source, float teamsay, entity privatesay, string msgin)
-{
-       float domatch, dotrigger, matchstart, l;
-       string s, msg;
-       entity oldself;
-       string savemessage;
-
-       magicear_matched = false;
-
-       dotrigger = ((IS_PLAYER(source)) && (source.deadflag == DEAD_NO) && ((ear.radius == 0) || (vlen(source.origin - ear.origin) <= ear.radius)));
-       domatch = ((ear.spawnflags & 32) || dotrigger);
-
-       if (!domatch)
-               return msgin;
-
-       if (!msgin)
-       {
-               // we are in TUBA mode!
-               if (!(ear.spawnflags & 256))
-                       return msgin;
-
-               if(!W_Tuba_HasPlayed(source, ear.message, ear.movedir.x, !(ear.spawnflags & 512), ear.movedir.y, ear.movedir.z))
-                       return msgin;
-
-               magicear_matched = true;
-
-               if(dotrigger)
-               {
-                       oldself = self;
-                       activator = source;
-                       self = ear;
-                       savemessage = self.message;
-                       self.message = string_null;
-                       SUB_UseTargets();
-                       self.message = savemessage;
-                       self = oldself;
-               }
-
-               if(ear.netname != "")
-                       return ear.netname;
-
-               return msgin;
-       }
-
-       if(ear.spawnflags & 256) // ENOTUBA
-               return msgin;
-
-       if(privatesay)
-       {
-               if(ear.spawnflags & 4)
-                       return msgin;
-       }
-       else
-       {
-               if(!teamsay)
-                       if(ear.spawnflags & 1)
-                               return msgin;
-               if(teamsay > 0)
-                       if(ear.spawnflags & 2)
-                               return msgin;
-               if(teamsay < 0)
-                       if(ear.spawnflags & 8)
-                               return msgin;
-       }
-
-       matchstart = -1;
-       l = strlen(ear.message);
-
-       if(ear.spawnflags & 128)
-               msg = msgin;
-       else
-               msg = strdecolorize(msgin);
-
-       if(substring(ear.message, 0, 1) == "*")
-       {
-               if(substring(ear.message, -1, 1) == "*")
-               {
-                       // two wildcards
-                       // as we need multi-replacement here...
-                       s = substring(ear.message, 1, -2);
-                       l -= 2;
-                       if(strstrofs(msg, s, 0) >= 0)
-                               matchstart = -2; // we use strreplace on s
-               }
-               else
-               {
-                       // match at start
-                       s = substring(ear.message, 1, -1);
-                       l -= 1;
-                       if(substring(msg, -l, l) == s)
-                               matchstart = strlen(msg) - l;
-               }
-       }
-       else
-       {
-               if(substring(ear.message, -1, 1) == "*")
-               {
-                       // match at end
-                       s = substring(ear.message, 0, -2);
-                       l -= 1;
-                       if(substring(msg, 0, l) == s)
-                               matchstart = 0;
-               }
-               else
-               {
-                       // full match
-                       s = ear.message;
-                       if(msg == ear.message)
-                               matchstart = 0;
-               }
-       }
-
-       if(matchstart == -1) // no match
-               return msgin;
-
-       magicear_matched = true;
-
-       if(dotrigger)
-       {
-               oldself = self;
-               activator = source;
-               self = ear;
-               savemessage = self.message;
-               self.message = string_null;
-               SUB_UseTargets();
-               self.message = savemessage;
-               self = oldself;
-       }
-
-       if(ear.spawnflags & 16)
-       {
-               return ear.netname;
-       }
-       else if(ear.netname != "")
-       {
-               if(matchstart < 0)
-                       return strreplace(s, ear.netname, msg);
-               else
-                       return strcat(
-                               substring(msg, 0, matchstart),
-                               ear.netname,
-                               substring(msg, matchstart + l, -1)
-                       );
-       }
-       else
-               return msgin;
-}
-
-string trigger_magicear_processmessage_forallears(entity source, float teamsay, entity privatesay, string msgin)
-{
-       entity ear;
-       string msgout;
-       for(ear = magicears; ear; ear = ear.enemy)
-       {
-               msgout = trigger_magicear_processmessage(ear, source, teamsay, privatesay, msgin);
-               if(!(ear.spawnflags & 64))
-               if(magicear_matched)
-                       return msgout;
-               msgin = msgout;
-       }
-       return msgin;
-}
-
-void spawnfunc_trigger_magicear()
-{
-       self.enemy = magicears;
-       magicears = self;
-
-       // actually handled in "say" processing
-       // spawnflags:
-       //    1 = ignore say
-       //    2 = ignore teamsay
-       //    4 = ignore tell
-       //    8 = ignore tell to unknown player
-       //   16 = let netname replace the whole message (otherwise, netname is a word replacement if set)
-       //   32 = perform the replacement even if outside the radius or dead
-       //   64 = continue replacing/triggering even if this one matched
-       //  128 = don't decolorize message before matching
-       //  256 = message is a tuba note sequence (pitch.duration pitch.duration ...)
-       //  512 = tuba notes must be exact right pitch, no transposing
-       // message: either
-       //   *pattern*
-       // or
-       //   *pattern
-       // or
-       //   pattern*
-       // or
-       //   pattern
-       // netname:
-       //   if set, replacement for the matched text
-       // radius:
-       //   "hearing distance"
-       // target:
-       //   what to trigger
-       // movedir:
-       //   for spawnflags 256, defines 'instrument+1 mintempo maxtempo' (zero component doesn't matter)
-
-       self.movedir_x -= 1; // map to tuba instrument numbers
-}
-
-void relay_activators_use()
-{
-       entity trg, os;
-
-       os = self;
-
-       for(trg = world; (trg = find(trg, targetname, os.target)); )
-       {
-               self = trg;
-               if (trg.setactive)
-                       trg.setactive(os.cnt);
-               else
-               {
-                       //bprint("Not using setactive\n");
-                       if(os.cnt == ACTIVE_TOGGLE)
-                               if(trg.active == ACTIVE_ACTIVE)
-                                       trg.active = ACTIVE_NOT;
-                               else
-                                       trg.active = ACTIVE_ACTIVE;
-                       else
-                               trg.active = os.cnt;
-               }
-       }
-       self = os;
-}
-
-void spawnfunc_relay_activate()
-{
-       self.cnt = ACTIVE_ACTIVE;
-       self.use = relay_activators_use;
-}
-
-void spawnfunc_relay_deactivate()
-{
-       self.cnt = ACTIVE_NOT;
-       self.use = relay_activators_use;
-}
-
-void spawnfunc_relay_activatetoggle()
-{
-       self.cnt = ACTIVE_TOGGLE;
-       self.use = relay_activators_use;
-}
-
-void spawnfunc_target_changelevel_use()
-{
-       if(self.gametype != "")
-               MapInfo_SwitchGameType(MapInfo_Type_FromString(self.gametype));
-
-       if (self.chmap == "")
-               localcmd("endmatch\n");
-       else
-               localcmd(strcat("changelevel ", self.chmap, "\n"));
-}
-
-void spawnfunc_target_changelevel()
-{
-       self.use = spawnfunc_target_changelevel_use;
-}
diff --git a/qcsrc/server/g_triggers.qh b/qcsrc/server/g_triggers.qh
deleted file mode 100644 (file)
index 46face3..0000000
+++ /dev/null
@@ -1,392 +0,0 @@
-#ifndef G_TRIGGERS_H
-#define G_TRIGGERS_H
-
-void SUB_DontUseTargets();
-
-
-void() SUB_UseTargets;
-
-void DelayThink();
-
-/*
-==============================
-SUB_UseTargets
-
-the global "activator" should be set to the entity that initiated the firing.
-
-If self.delay is set, a DelayedUse entity will be created that will actually
-do the SUB_UseTargets after that many seconds have passed.
-
-Centerprints any self.message to the activator.
-
-Removes all entities with a targetname that match self.killtarget,
-and removes them, so some events can remove other triggers.
-
-Search for (string)targetname in all entities that
-match (string)self.target and call their .use function
-
-==============================
-*/
-void SUB_UseTargets();
-
-
-//=============================================================================
-
-const float    SPAWNFLAG_NOMESSAGE = 1;
-const float    SPAWNFLAG_NOTOUCH = 1;
-
-// the wait time has passed, so set back up for another activation
-void multi_wait();
-
-
-// the trigger was just touched/killed/used
-// self.enemy should be set to the activator so it can be held through a delay
-// so wait for the delay time before firing
-void multi_trigger();
-
-void multi_use();
-
-void multi_touch();
-
-void multi_eventdamage (entity inflictor, entity attacker, float damage, int deathtype, vector hitloc, vector force);
-
-void multi_reset();
-
-/*QUAKED spawnfunc_trigger_multiple (.5 .5 .5) ? notouch
-Variable sized repeatable trigger.  Must be targeted at one or more entities.  If "health" is set, the trigger must be killed to activate each time.
-If "delay" is set, the trigger waits some time after activating before firing.
-"wait" : Seconds between triggerings. (.2 default)
-If notouch is set, the trigger is only fired by other entities, not by touching.
-NOTOUCH has been obsoleted by spawnfunc_trigger_relay!
-sounds
-1)     secret
-2)     beep beep
-3)     large switch
-4)
-set "message" to text string
-*/
-void spawnfunc_trigger_multiple();
-
-
-/*QUAKED spawnfunc_trigger_once (.5 .5 .5) ? notouch
-Variable sized trigger. Triggers once, then removes itself.  You must set the key "target" to the name of another object in the level that has a matching
-"targetname".  If "health" is set, the trigger must be killed to activate.
-If notouch is set, the trigger is only fired by other entities, not by touching.
-if "killtarget" is set, any objects that have a matching "target" will be removed when the trigger is fired.
-if "angle" is set, the trigger will only fire when someone is facing the direction of the angle.  Use "360" for an angle of 0.
-sounds
-1)     secret
-2)     beep beep
-3)     large switch
-4)
-set "message" to text string
-*/
-void spawnfunc_trigger_once();
-
-//=============================================================================
-
-/*QUAKED spawnfunc_trigger_relay (.5 .5 .5) (-8 -8 -8) (8 8 8)
-This fixed size trigger cannot be touched, it can only be fired by other events.  It can contain killtargets, targets, delays, and messages.
-*/
-void spawnfunc_trigger_relay();
-
-void delay_use();
-
-void delay_reset();
-
-void spawnfunc_trigger_delay();
-
-//=============================================================================
-
-
-void counter_use();
-
-void counter_reset();
-
-/*QUAKED spawnfunc_trigger_counter (.5 .5 .5) ? nomessage
-Acts as an intermediary for an action that takes multiple inputs.
-
-If nomessage is not set, t will print "1 more.. " etc when triggered and "sequence complete" when finished.
-
-After the counter has been triggered "count" times (default 2), it will fire all of it's targets and remove itself.
-*/
-void spawnfunc_trigger_counter();
-
-void trigger_hurt_use();
-
-.float triggerhurttime;
-void trigger_hurt_touch();
-
-/*QUAKED spawnfunc_trigger_hurt (.5 .5 .5) ?
-Any object touching this will be hurt
-set dmg to damage amount
-defalt dmg = 5
-*/
-.entity trigger_hurt_next;
-entity trigger_hurt_last;
-entity trigger_hurt_first;
-void spawnfunc_trigger_hurt();
-
-float tracebox_hits_trigger_hurt(vector start, vector mi, vector ma, vector end);
-
-//////////////////////////////////////////////////////////////
-//
-//
-//
-//Trigger heal --a04191b92fbd93aa67214ef7e72d6d2e
-//
-//////////////////////////////////////////////////////////////
-
-.float triggerhealtime;
-void trigger_heal_touch();
-
-void spawnfunc_trigger_heal();
-
-
-//////////////////////////////////////////////////////////////
-//
-//
-//
-//End trigger_heal
-//
-//////////////////////////////////////////////////////////////
-
-.entity trigger_gravity_check;
-void trigger_gravity_remove(entity own);
-void trigger_gravity_check_think();
-
-void trigger_gravity_use();
-
-void trigger_gravity_touch();
-
-void spawnfunc_trigger_gravity();
-
-//=============================================================================
-
-// TODO add a way to do looped sounds with sound(); then complete this entity
-.float volume, atten;
-void target_speaker_use_off();
-void target_speaker_use_activator();
-void target_speaker_use_on();
-void target_speaker_use_off();
-void target_speaker_reset();
-
-void spawnfunc_target_speaker();
-
-
-void spawnfunc_func_stardust();
-
-.string bgmscript;
-.float bgmscriptattack;
-.float bgmscriptdecay;
-.float bgmscriptsustain;
-.float bgmscriptrelease;
-float pointparticles_SendEntity(entity to, float fl);
-
-void pointparticles_use();
-
-void pointparticles_think();
-
-void pointparticles_reset();
-
-void spawnfunc_func_pointparticles();
-
-void spawnfunc_func_sparks();
-
-float rainsnow_SendEntity(entity to, int sf);
-
-/*QUAKED spawnfunc_func_rain (0 .5 .8) ?
-This is an invisible area like a trigger, which rain falls inside of.
-
-Keys:
-"velocity"
- falling direction (should be something like '0 0 -700', use the X and Y velocity for wind)
-"cnt"
- sets color of rain (default 12 - white)
-"count"
- adjusts density, this many particles fall every second for a 1024x1024 area, default is 2000
-*/
-void spawnfunc_func_rain();
-
-
-/*QUAKED spawnfunc_func_snow (0 .5 .8) ?
-This is an invisible area like a trigger, which snow falls inside of.
-
-Keys:
-"velocity"
- falling direction (should be something like '0 0 -300', use the X and Y velocity for wind)
-"cnt"
- sets color of rain (default 12 - white)
-"count"
- adjusts density, this many particles fall every second for a 1024x1024 area, default is 2000
-*/
-void spawnfunc_func_snow();
-
-.float modelscale;
-void misc_laser_aim();
-
-void misc_laser_init();
-
-.entity pusher;
-void misc_laser_think();
-
-float laser_SendEntity(entity to, float fl);
-
-/*QUAKED spawnfunc_misc_laser (.5 .5 .5) ? START_ON DEST_IS_FIXED
-Any object touching the beam will be hurt
-Keys:
-"target"
- spawnfunc_target_position where the laser ends
-"mdl"
- name of beam end effect to use
-"colormod"
- color of the beam (default: red)
-"dmg"
- damage per second (-1 for a laser that kills immediately)
-*/
-void laser_use();
-
-void laser_reset();
-
-void spawnfunc_misc_laser();
-
-// tZorks trigger impulse / gravity
-.float radius;
-.float falloff;
-.float strength;
-.float lastpushtime;
-
-// targeted (directional) mode
-void trigger_impulse_touch1();
-
-// Directionless (accelerator/decelerator) mode
-void trigger_impulse_touch2();
-
-// Spherical (gravity/repulsor) mode
-void trigger_impulse_touch3();
-
-/*QUAKED spawnfunc_trigger_impulse (.5 .5 .5) ?
--------- KEYS --------
-target : If this is set, this points to the spawnfunc_target_position to which the player will get pushed.
-         If not, this trigger acts like a damper/accelerator field.
-
-strength : This is how mutch force to add in the direction of .target each second
-           when .target is set. If not, this is hoe mutch to slow down/accelerate
-           someting cought inside this trigger. (1=no change, 0,5 half speed rougthly each tic, 2 = doubble)
-
-radius   : If set, act as a spherical device rather then a liniar one.
-
-falloff : 0 = none, 1 = liniar, 2 = inverted liniar
-
--------- NOTES --------
-Use a brush textured with common/origin in the trigger entity to determine the origin of the force
-in directional and sperical mode. For damper/accelerator mode this is not nessesary (and has no effect).
-*/
-
-void spawnfunc_trigger_impulse();
-
-/*QUAKED spawnfunc_trigger_flipflop (.5 .5 .5) (-8 -8 -8) (8 8 8) START_ENABLED
-"Flip-flop" trigger gate... lets only every second trigger event through
-*/
-void flipflop_use();
-
-void spawnfunc_trigger_flipflop();
-
-/*QUAKED spawnfunc_trigger_monoflop (.5 .5 .5) (-8 -8 -8) (8 8 8)
-"Mono-flop" trigger gate... turns one trigger event into one "on" and one "off" event, separated by a delay of "wait"
-*/
-void monoflop_use();
-void monoflop_fixed_use();
-
-void monoflop_think();
-
-void monoflop_reset();
-
-void spawnfunc_trigger_monoflop();
-
-void multivibrator_send();
-
-void multivibrator_toggle();
-
-void multivibrator_reset();
-
-/*QUAKED trigger_multivibrator (.5 .5 .5) (-8 -8 -8) (8 8 8) START_ON
-"Multivibrator" trigger gate... repeatedly sends trigger events. When triggered, turns on or off.
--------- KEYS --------
-target: trigger all entities with this targetname when it goes off
-targetname: name that identifies this entity so it can be triggered; when off, it always uses the OFF state
-phase: offset of the timing
-wait: "on" cycle time (default: 1)
-respawntime: "off" cycle time (default: same as wait)
--------- SPAWNFLAGS --------
-START_ON: assume it is already turned on (when targeted)
-*/
-void spawnfunc_trigger_multivibrator();
-
-
-void follow_init();
-
-void spawnfunc_misc_follow();
-
-
-
-void gamestart_use();
-
-void spawnfunc_trigger_gamestart();
-
-
-
-
-.entity voicescript; // attached voice script
-.float voicescript_index; // index of next voice, or -1 to use the randomized ones
-.float voicescript_nextthink; // time to play next voice
-.float voicescript_voiceend; // time when this voice ends
-
-void target_voicescript_clear(entity pl);
-
-void target_voicescript_use();
-
-void target_voicescript_next(entity pl);
-
-void spawnfunc_target_voicescript();
-
-
-
-void trigger_relay_teamcheck_use();
-
-void trigger_relay_teamcheck_reset();
-
-void spawnfunc_trigger_relay_teamcheck();
-
-
-
-void trigger_disablerelay_use();
-
-void spawnfunc_trigger_disablerelay();
-
-float magicear_matched;
-float W_Tuba_HasPlayed(entity pl, string melody, float instrument, float ignorepitch, float mintempo, float maxtempo);
-string trigger_magicear_processmessage(entity ear, entity source, float teamsay, entity privatesay, string msgin);
-
-entity magicears;
-string trigger_magicear_processmessage_forallears(entity source, float teamsay, entity privatesay, string msgin);
-
-void spawnfunc_trigger_magicear();
-
-void relay_activators_use();
-
-void spawnfunc_relay_activate();
-
-void spawnfunc_relay_deactivate();
-
-void spawnfunc_relay_activatetoggle();
-
-.string chmap, gametype;
-void spawnfunc_target_changelevel_use();
-
-void spawnfunc_target_changelevel();
-
-.float antiwall_flag; // Variable to define what to do with func_clientwall
-// 0 == do nothing, 1 == deactivate, 2 == activate
-
-#endif
index 13d63dab3fd28fca0beef5f61d38b9ac7ec428aa..9c2003cc19d506afbe5de24d4b7e5adf0c7bdacb 100644 (file)
@@ -17,7 +17,6 @@
 #include "mutators/mutators_include.qh"
 #include "race.qh"
 #include "scores.qh"
-#include "secret.qh"
 #include "teamplay.qh"
 #include "waypointsprites.qh"
 #include "weapons/weaponstats.qh"
@@ -578,6 +577,7 @@ void Nagger_Init();
 void ClientInit_Spawn();
 void WeaponStats_Init();
 void WeaponStats_Shutdown();
+void Physics_AddStats();
 void spawnfunc_worldspawn (void)
 {
        float fd, l, j, n;
@@ -819,11 +819,8 @@ void spawnfunc_worldspawn (void)
        addstat(STAT_FROZEN, AS_INT, frozen);
        addstat(STAT_REVIVE_PROGRESS, AS_FLOAT, revive_progress);
 
-       // g_movementspeed hack
-       addstat(STAT_MOVEVARS_AIRSPEEDLIMIT_NONQW, AS_FLOAT, stat_sv_airspeedlimit_nonqw);
-       addstat(STAT_MOVEVARS_MAXSPEED, AS_FLOAT, stat_sv_maxspeed);
-       addstat(STAT_MOVEVARS_AIRACCEL_QW, AS_FLOAT, stat_sv_airaccel_qw);
-       addstat(STAT_MOVEVARS_AIRSTRAFEACCEL_QW, AS_FLOAT, stat_sv_airstrafeaccel_qw);
+       // physics
+       Physics_AddStats();
 
        // new properties
        addstat(STAT_MOVEVARS_JUMPVELOCITY, AS_FLOAT, stat_sv_jumpvelocity);
index 1aa054d07eba3d8a722812977edb9acf330ac3ad..1518151f2c85b457fff515b37634030b68f838b0 100644 (file)
@@ -1,6 +1,7 @@
 #include "item_key.qh"
 #include "_all.qh"
 
+#include "../common/triggers/subs.qh"
 #include "../common/monsters/all.qh"
 #include "../common/notifications.qh"
 #include "../common/util.qh"
@@ -15,7 +16,8 @@ TODO:
 - should keys have a trigger?
 */
 
-bool item_keys_usekey(entity l, entity p) {
+bool item_keys_usekey(entity l, entity p)
+{
        float valid = l.itemkeys & p.itemkeys;
 
        if (!valid) {
@@ -273,156 +275,3 @@ void spawnfunc_item_key2(void) {
        self.itemkeys = ITEM_KEY_BIT(0);
        spawnfunc_item_key();
 };
-
-
-/*
-================================
-trigger_keylock
-================================
-*/
-
-/**
- * trigger givent targets
- */
-void trigger_keylock_trigger(string s) {
-       entity stemp = self;
-       entity otemp = other;
-       entity atemp = activator;
-
-       entity t;
-       for(t = world; (t = find(t, targetname, s)); )
-               if (t.use) {
-                       self = t;
-                       other = stemp;
-                       activator = atemp;
-                       self.use();
-               }
-
-       self = stemp;
-       other = otemp;
-       activator = atemp;
-};
-
-/**
- * kill killtarget of trigger keylock.
- */
-void trigger_keylock_kill(string s) {
-       entity t;
-       for(t = world; (t = find(t, targetname, s)); )
-               remove(t);
-};
-
-void trigger_keylock_touch(void) {
-       bool key_used = false;
-       bool started_delay = false;
-
-       // only player may trigger the lock
-       if (!IS_PLAYER(other))
-               return;
-
-
-       // check silver key
-       if (self.itemkeys)
-               key_used = item_keys_usekey(self, other);
-
-       activator = other;
-
-       if (self.itemkeys) {
-               // at least one of the keys is missing
-               if (key_used) {
-                       // one or more keys were given, but others are still missing!
-                       play2(other, self.noise1);
-                       Send_Notification(NOTIF_ONE, other, MSG_CENTER, CENTER_DOOR_LOCKED_ALSONEED, item_keys_keylist(self.itemkeys));
-                       other.key_door_messagetime = time + 2;
-               } else if (other.key_door_messagetime <= time) {
-                       // no keys were given
-                       play2(other, self.noise2);
-                       Send_Notification(NOTIF_ONE, other, MSG_CENTER, CENTER_DOOR_LOCKED_NEED, item_keys_keylist(self.itemkeys));
-                       other.key_door_messagetime = time + 2;
-               }
-
-               // trigger target2
-               if (self.delay <= time || started_delay == true)
-               if (self.target2) {
-                       trigger_keylock_trigger(self.target2);
-                       started_delay = true;
-                       self.delay = time + self.wait;
-               }
-       } else {
-               // all keys were given!
-               play2(other, self.noise);
-               centerprint(other, self.message);
-
-               if (self.target)
-                       trigger_keylock_trigger(self.target);
-
-               if (self.killtarget)
-                       trigger_keylock_kill(self.killtarget);
-
-               remove(self);
-       }
-
-};
-
-/*QUAKED trigger_keylock (.0 .5 .8) ?
-Keylock trigger.  Must target other entities.
-This trigger will trigger target entities when all required keys are provided.
--------- KEYS --------
-itemkeys: A bit field with key IDs that are needed to open this lock.
-sounds: 1 to play misc/secret.wav, 2 to play misc/talk.wav, 3 to play misc/trigger1.wav (3 is default)
-target: trigger all entities with this targetname when triggered and all keys have been given to it, then remove this trigger
-target2: trigger all entities with this targetname when triggered without giving it all the required keys.
-killtarget: remove all entities with this targetname when triggered with all the needed keys.
-message: print this message to the player who activated the trigger when all needed keys have been given.
-message2: print this message to the player who activated the trigger when not all of the needed keys have been given.
-noise: sound to play when lock gets unlocked (default: see sounds)
-noise1: sound to play when only some of the needed key were used but not all (default: misc/decreasevalue.wav)
-noise2: sound to play when a key is missing (default: misc/talk.wav)
-wait: prevent triggering again for this amount of time (default: 5) - applies to target2, target3, target4.
----------NOTES----------
-If spawned without any key specified in itemkeys, this trigger will display an error and remove itself.
-message2 and noise2 will be resent to the player every 2 seconds while he is in the trigger zone.
-*/
-void spawnfunc_trigger_keylock(void) {
-       if (!self.itemkeys) {
-               remove(self);
-               return;
-       }
-
-       // set unlocked message
-       if (self.message == "")
-               self.message = "Unlocked!";
-
-       // set default unlock noise
-       if (self.noise == "") {
-               if (self.sounds == 1)
-                       self.noise = "misc/secret.wav";
-               else if (self.sounds == 2)
-                       self.noise = "misc/talk.wav";
-               else //if (self.sounds == 3) {
-                       self.noise = "misc/trigger1.wav";
-       }
-
-       // set default use key sound
-       if (self.noise1 == "")
-               self.noise1 = "misc/decreasevalue.wav";
-
-       // set closed sourd
-       if (self.noise2 == "")
-               self.noise2 = "misc/talk.wav";
-
-       // delay between triggering message2 and trigger2
-       if (!self.wait)
-               self.wait = 5;
-
-       // precache sounds
-       precache_sound(self.noise);
-       precache_sound(self.noise1);
-       precache_sound(self.noise2);
-
-       EXACTTRIGGER_INIT;
-
-       self.touch = trigger_keylock_touch;
-};
-
-
index 6ab56050368601b25496c20717744e5ff1ff9b29..4a2acd85b7d1c015d8303f3c4d120b4b25178292 100644 (file)
@@ -11,6 +11,7 @@
 /**
  * list of key names.
  */
+#ifdef SVQC
 string item_keys_names[ITEM_KEY_MAX];
 
 /**
@@ -24,3 +25,5 @@ float item_keys_usekey(entity l, entity p);
  */
 string item_keys_keylist(float keylist);
 #endif
+
+#endif
index 556ad393a69effdae610e01137accc520346c58b..2354e128e30bd9d1874e1ca0e57c16ec2f8c07d2 100644 (file)
@@ -1,6 +1,5 @@
 #include "miscfunctions.qh"
 #include "_all.qh"
-
 #include "antilag.qh"
 #include "command/common.qh"
 #include "constants.qh"
@@ -19,6 +18,7 @@
 #include "../common/notifications.qh"
 #include "../common/playerstats.qh"
 #include "../common/teams.qh"
+#include "../common/triggers/subs.qh"
 #include "../common/urllib.qh"
 #include "../common/util.qh"
 #include "../common/weapons/all.qh"
@@ -222,19 +222,6 @@ entity findnearest(vector point, .string field, string value, vector axismod)
     return nearest_entity[0];
 }
 
-void spawnfunc_target_location()
-{
-    self.classname = "target_location";
-    // location name in netname
-    // eventually support: count, teamgame selectors, line of sight?
-}
-
-void spawnfunc_info_location()
-{
-    self.classname = "target_location";
-    self.message = self.netname;
-}
-
 string NearestLocation(vector p)
 {
     entity loc;
@@ -774,7 +761,7 @@ void readplayerstartcvars()
        warmup_start_ammo_fuel = max(0, warmup_start_ammo_fuel);
 }
 
-float sound_allowed(float _dest, entity e)
+float sound_allowed(float destin, entity e)
 {
     // sounds from world may always pass
     for (;;)
@@ -789,7 +776,7 @@ float sound_allowed(float _dest, entity e)
             break;
     }
     // sounds to self may always pass
-    if (_dest == MSG_ONE)
+    if (destin == MSG_ONE)
         if (e == msg_entity)
             return true;
     // sounds by players can be removed
@@ -801,14 +788,14 @@ float sound_allowed(float _dest, entity e)
 }
 
 #undef sound
-void sound(entity e, float chan, string samp, float vol, float _atten)
+void sound(entity e, float chan, string samp, float vol, float attenu)
 {
     if (!sound_allowed(MSG_BROADCAST, e))
         return;
-    sound7(e, chan, samp, vol, _atten, 0, 0);
+    sound7(e, chan, samp, vol, attenu, 0, 0);
 }
 
-void soundtoat(float _dest, entity e, vector o, float chan, string samp, float vol, float _atten)
+void soundtoat(float _dest, entity e, vector o, float chan, string samp, float vol, float attenu)
 {
     float entno, idx;
 
@@ -821,12 +808,12 @@ void soundtoat(float _dest, entity e, vector o, float chan, string samp, float v
     int sflags;
     sflags = 0;
 
-    _atten = floor(_atten * 64);
+    attenu = floor(attenu * 64);
     vol = floor(vol * 255);
 
     if (vol != 255)
         sflags |= SND_VOLUME;
-    if (_atten != 64)
+    if (attenu != 64)
         sflags |= SND_ATTENUATION;
     if (entno >= 8192 || chan < 0 || chan > 7)
         sflags |= SND_LARGEENTITY;
@@ -838,7 +825,7 @@ void soundtoat(float _dest, entity e, vector o, float chan, string samp, float v
     if (sflags & SND_VOLUME)
         WriteByte(_dest, vol);
     if (sflags & SND_ATTENUATION)
-        WriteByte(_dest, _atten);
+        WriteByte(_dest, attenu);
     if (sflags & SND_LARGEENTITY)
     {
         WriteShort(_dest, entno);
@@ -1270,7 +1257,6 @@ void SetCustomizer(entity e, float(void) customizer, void(void) uncustomizer)
     e.uncustomizeentityforclient_set = !!uncustomizer;
 }
 
-
 void Net_LinkEntity(entity e, bool docull, float dt, bool(entity, int) sendfunc)
 {
     vector mi, ma;
index b9ae44be93a490619ddc01f0d505cb0352151b72..12dbe3a2c795a72512c012cf9deaba5cb1dbbfe6 100644 (file)
@@ -141,15 +141,11 @@ void WarpZone_crosshair_trace(entity pl);
 void WarpZone_traceline_antilag (entity source, vector v1, vector v2, float nomonst, entity forent, float lag);
 
 
-#define IFTARGETED if(!self.nottargeted && self.targetname != "")
-
 #define ITEM_TOUCH_NEEDKILL() (((trace_dpstartcontents | trace_dphitcontents) & DPCONTENTS_NODROP) || (trace_dphitq3surfaceflags & Q3SURFACEFLAG_SKY))
 #define ITEM_DAMAGE_NEEDKILL(dt) (((dt) == DEATH_HURTTRIGGER) || ((dt) == DEATH_SLIME) || ((dt) == DEATH_LAVA) || ((dt) == DEATH_SWAMP))
 
 #define PROJECTILE_TOUCH if(WarpZone_Projectile_Touch()) return
 
-#define move_out_of_solid(e) WarpZoneLib_MoveOutOfSolid(e)
-
 const string STR_PLAYER = "player";
 const string STR_SPECTATOR = "spectator";
 const string STR_OBSERVER = "observer";
@@ -443,10 +439,6 @@ const float SND_ATTENUATION = 2;
 const float SND_LARGEENTITY = 8;
 const float SND_LARGESOUND = 16;
 
-// WARNING: this kills the trace globals
-#define EXACTTRIGGER_TOUCH if(WarpZoneLib_ExactTrigger_Touch()) return
-#define EXACTTRIGGER_INIT  WarpZoneLib_ExactTrigger_Init()
-
 const float INITPRIO_FIRST                             = 0;
 const float INITPRIO_GAMETYPE                  = 0;
 const float INITPRIO_GAMETYPE_FALLBACK         = 1;
index 101bdece81d943748c9be2b44e75894451601345..32e48f370162f6c89c4c11b8c4fb0e26aa802bec 100644 (file)
@@ -8,8 +8,6 @@
 #include "../cl_impulse.qh"
 #include "../cheats.qh"
 #include "../g_damage.qh"
-#include "../g_subs.qh"
-#include "../t_teleporters.qh"
 #include "../round_handler.qh"
 #include "../scores.qh"
 #include "../scores_rules.qh"
@@ -34,6 +32,8 @@
 
 #include "../../common/deathtypes.qh"
 #include "../../common/notifications.qh"
+#include "../../common/triggers/teleporters.qh"
+#include "../../common/triggers/subs.qh"
 #include "../../common/stats.qh"
 #include "../../common/teams.qh"
 
index 330707e67c949bf097192658d2c0d57c8a2048bb..1266492ca091e4b99a05f3cc89ffa13822e853a5 100644 (file)
@@ -29,6 +29,5 @@ const float ST_ASSAULT_OBJECTIVES = 1;
 const float SP_ASSAULT_OBJECTIVES = 4;
 
 // predefined spawnfuncs
-void spawnfunc_func_breakable();
 void target_objective_decrease_activate();
 #endif
index d8a4e357bbde7ea69a37477d28e710bcb6956a6e..c2189155c421299f254533c88393dfb8b4851bae 100644 (file)
 #include "../../warpzonelib/common.qh"
 #include "../../warpzonelib/mathlib.qh"
 
-// ================================================================
-//  Official capture the flag game mode coding, reworked by Samual
-//  Last updated: September, 2012
-// ================================================================
-
 void ctf_FakeTimeLimit(entity e, float t)
 {
        msg_entity = e;
@@ -26,7 +21,7 @@ void ctf_FakeTimeLimit(entity e, float t)
                WriteCoord(MSG_ONE, (t + 1) / 60);
 }
 
-void ctf_EventLog(string mode, float flagteam, entity actor) // use an alias for easy changing and quick editing later
+void ctf_EventLog(string mode, int flagteam, entity actor) // use an alias for easy changing and quick editing later
 {
        if(autocvar_sv_eventlog)
                GameLogEcho(strcat(":ctf:", mode, ":", ftos(flagteam), ((actor != world) ? (strcat(":", ftos(actor.playerid))) : "")));
@@ -92,7 +87,7 @@ void ctf_CalculatePassVelocity(entity flag, vector to, vector from, float turnra
        else { flag.velocity = (desired_direction * autocvar_g_ctf_pass_velocity); }
 }
 
-float ctf_CheckPassDirection(vector head_center, vector passer_center, vector passer_angle, vector nearest_to_passer)
+bool ctf_CheckPassDirection(vector head_center, vector passer_center, vector passer_angle, vector nearest_to_passer)
 {
        if(autocvar_g_ctf_pass_directional_max || autocvar_g_ctf_pass_directional_min)
        {
@@ -126,7 +121,7 @@ float ctf_CheckPassDirection(vector head_center, vector passer_center, vector pa
 // CaptureShield Functions
 // =======================
 
-float ctf_CaptureShield_CheckStatus(entity p)
+bool ctf_CaptureShield_CheckStatus(entity p)
 {
        float s, se;
        entity e;
@@ -961,6 +956,7 @@ void ctf_DelayedFlagSetup(void) // called after a flag is placed on a map by ctf
        self.bot_basewaypoint = self.nearestwaypoint;
 
        // waypointsprites
+       // move_origin isnt accessible just yet
        WaypointSprite_SpawnFixed(((self.team == NUM_TEAM_1) ? "redbase" : "bluebase"), self.origin + FLAG_WAYPOINT_OFFSET, self, wps_flagbase, RADARICON_FLAG, colormapPaletteColor(self.team - 1, false));
        WaypointSprite_UpdateTeamRadar(self.wps_flagbase, RADARICON_FLAG, colormapPaletteColor(self.team - 1, false));
 
index 302f6ce104b305054a8f3dd36830a5a3b8200025..b183a2ae58f340a4c1e1052c56148447325c9a27 100644 (file)
@@ -7,17 +7,17 @@
 void ctf_RespawnFlag(entity flag);
 
 // score rule declarations
-const float ST_CTF_CAPS = 1;
-const float SP_CTF_CAPS = 4;
-const float SP_CTF_CAPTIME = 5;
-const float SP_CTF_PICKUPS = 6;
-const float SP_CTF_DROPS = 7;
-const float SP_CTF_FCKILLS = 8;
-const float SP_CTF_RETURNS = 9;
+const int ST_CTF_CAPS = 1;
+const int SP_CTF_CAPS = 4;
+const int SP_CTF_CAPTIME = 5;
+const int SP_CTF_PICKUPS = 6;
+const int SP_CTF_DROPS = 7;
+const int SP_CTF_FCKILLS = 8;
+const int SP_CTF_RETURNS = 9;
 
 // flag constants // for most of these, there is just one question to be asked: WHYYYYY?
-#define FLAG_MIN (PL_MIN + '0 0 -13')
-#define FLAG_MAX (PL_MAX + '0 0 -13')
+#define FLAG_MIN (PL_MIN_CONST + '0 0 -13')
+#define FLAG_MAX (PL_MAX_CONST + '0 0 -13')
 
 const float FLAG_SCALE = 0.6;
 
@@ -25,14 +25,14 @@ const float FLAG_THINKRATE = 0.2;
 const float FLAG_TOUCHRATE = 0.5;
 const float WPFE_THINKRATE = 0.5;
 
-#define FLAG_DROP_OFFSET ('0 0 32')
-#define FLAG_CARRY_OFFSET ('-16 0 8')
-#define FLAG_SPAWN_OFFSET ('0 0 1' * (PL_MAX_z - 13))
-#define FLAG_WAYPOINT_OFFSET ('0 0 64')
-#define FLAG_FLOAT_OFFSET ('0 0 32')
-#define FLAG_PASS_ARC_OFFSET ('0 0 -10')
+const vector FLAG_DROP_OFFSET = ('0 0 32');
+const vector FLAG_CARRY_OFFSET = ('-16 0 8');
+#define FLAG_SPAWN_OFFSET ('0 0 1' * (PL_MAX_CONST.z - 13))
+const vector FLAG_WAYPOINT_OFFSET = ('0 0 64');
+const vector FLAG_FLOAT_OFFSET = ('0 0 32');
+const vector FLAG_PASS_ARC_OFFSET = ('0 0 -10');
 
-#define VEHICLE_FLAG_OFFSET ('0 0 96')
+const vector VEHICLE_FLAG_OFFSET = ('0 0 96');
 const float VEHICLE_FLAG_SCALE = 1.0;
 
 // waypoint colors
@@ -71,27 +71,27 @@ float wpforenemy_announced;
 float wpforenemy_nextthink;
 
 // statuses
-const float FLAG_BASE = 1;
-const float FLAG_DROPPED = 2;
-const float FLAG_CARRY = 3;
-const float FLAG_PASSING = 4;
+const int FLAG_BASE = 1;
+const int FLAG_DROPPED = 2;
+const int FLAG_CARRY = 3;
+const int FLAG_PASSING = 4;
 
-const float DROP_NORMAL = 1;
-const float DROP_THROW = 2;
-const float DROP_PASS = 3;
-const float DROP_RESET = 4;
+const int DROP_NORMAL = 1;
+const int DROP_THROW = 2;
+const int DROP_PASS = 3;
+const int DROP_RESET = 4;
 
-const float PICKUP_BASE = 1;
-const float PICKUP_DROPPED = 2;
+const int PICKUP_BASE = 1;
+const int PICKUP_DROPPED = 2;
 
-const float CAPTURE_NORMAL = 1;
-const float CAPTURE_DROPPED = 2;
+const int CAPTURE_NORMAL = 1;
+const int CAPTURE_DROPPED = 2;
 
-const float RETURN_TIMEOUT = 1;
-const float RETURN_DROPPED = 2;
-const float RETURN_DAMAGE = 3;
-const float RETURN_SPEEDRUN = 4;
-const float RETURN_NEEDKILL = 5;
+const int RETURN_TIMEOUT = 1;
+const int RETURN_DROPPED = 2;
+const int RETURN_DAMAGE = 3;
+const int RETURN_SPEEDRUN = 4;
+const int RETURN_NEEDKILL = 5;
 
 void ctf_Handle_Throw(entity player, entity receiver, float droptype);
 
@@ -105,6 +105,7 @@ float ctf_captimerecord; // record time for capturing the flag
 .entity ctf_dropper; // don't allow spam of dropping the flag
 .float max_flag_health;
 .float next_take_time;
+.float ctf_thinkrate;
 
 // passing/throwing properties
 .float pass_distance;
@@ -121,15 +122,15 @@ float ctf_captureshield_max_ratio; // punish at most 30% of each team
 float ctf_captureshield_force; // push force of the shield
 
 // bot player logic
-const float HAVOCBOT_CTF_ROLE_NONE = 0;
-const float HAVOCBOT_CTF_ROLE_DEFENSE = 2;
-const float HAVOCBOT_CTF_ROLE_MIDDLE = 4;
-const float HAVOCBOT_CTF_ROLE_OFFENSE = 8;
-const float HAVOCBOT_CTF_ROLE_CARRIER = 16;
-const float HAVOCBOT_CTF_ROLE_RETRIEVER = 32;
-const float HAVOCBOT_CTF_ROLE_ESCORT = 64;
-
-.float havocbot_cantfindflag;
+const int HAVOCBOT_CTF_ROLE_NONE = 0;
+const int HAVOCBOT_CTF_ROLE_DEFENSE = 2;
+const int HAVOCBOT_CTF_ROLE_MIDDLE = 4;
+const int HAVOCBOT_CTF_ROLE_OFFENSE = 8;
+const int HAVOCBOT_CTF_ROLE_CARRIER = 16;
+const int HAVOCBOT_CTF_ROLE_RETRIEVER = 32;
+const int HAVOCBOT_CTF_ROLE_ESCORT = 64;
+
+.bool havocbot_cantfindflag;
 
 vector havocbot_ctf_middlepoint;
 float havocbot_ctf_middlepoint_radius;
index 6b62e7da1b65d7bda40c0e618cb12bbddd7d3c17..ed69612d15c42e6f0e5abe4cf0e02c4ed811f30b 100644 (file)
@@ -25,6 +25,7 @@ void ka_EventLog(string mode, entity actor) // use an alias for easy changing an
                GameLogEcho(strcat(":ka:", mode, ((actor != world) ? (strcat(":", ftos(actor.playerid))) : "")));
 }
 
+void ka_TouchEvent();
 void ka_RespawnBall() // runs whenever the ball needs to be relocated
 {
        if(gameover) { return; }
@@ -42,6 +43,7 @@ void ka_RespawnBall() // runs whenever the ball needs to be relocated
        self.velocity = '0 0 200';
        self.angles = '0 0 0';
        self.effects = autocvar_g_keepawayball_effects;
+       self.touch = ka_TouchEvent;
        self.think = ka_RespawnBall;
        self.nextthink = time + autocvar_g_keepawayball_respawntime;
 
@@ -169,7 +171,14 @@ void ka_Reset() // used to clear the ballcarrier whenever the match switches fro
        if((self.owner) && (IS_PLAYER(self.owner)))
                ka_DropEvent(self.owner);
 
-       ka_RespawnBall();
+       if(time < game_starttime)
+       {
+               self.think = ka_RespawnBall;
+               self.touch = func_null;
+               self.nextthink = game_starttime;
+       }
+       else
+               ka_RespawnBall();
 }
 
 
@@ -192,9 +201,8 @@ void havocbot_goalrating_ball(float ratingscale, vector org)
                t = (self.health + self.armorvalue) / (ball_owner.health + ball_owner.armorvalue);
                navigation_routerating(ball_owner, t * ratingscale, 2000);
        }
-
-       // Ball has been dropped so collect.
-       navigation_routerating(ka_ball, ratingscale, 2000);
+       else // Ball has been dropped so collect.
+               navigation_routerating(ka_ball, ratingscale, 2000);
 }
 
 void havocbot_role_ka_carrier()
index e7f3289982d12411ab0b7f9910b2eb2515f1eeac..3771093a20a193de21ac10029961d24f61f4b438 100644 (file)
@@ -275,7 +275,7 @@ void kh_Key_Detach(entity key) // runs every time a key is dropped or lost. Runs
        }
        // in any case:
        setattachment(key, world, "");
-       setorigin(key, key.owner.origin + '0 0 1' * (PL_MIN_z - KH_KEY_MIN_z));
+       setorigin(key, key.owner.origin + '0 0 1' * (PL_MIN.z - KH_KEY_MIN_z));
        key.angles = key.owner.angles;
 #else
        setorigin(key, key.owner.origin + key.origin.z * '0 0 1');
index 4c081ab445f0c3abab6bc944c12c3a73edafc54e..9844875b3d9eb27ac56efc69049e59aed1a70f2c 100644 (file)
@@ -3,8 +3,6 @@
 
 #include "gamemode.qh"
 
-#include "../t_jumppads.qh"
-
 float autocvar_g_nexball_safepass_turnrate;
 float autocvar_g_nexball_safepass_maxdist;
 float autocvar_g_nexball_safepass_holdtime;
index d7474fab45e3c75352109cdadc38b88426409556..36bf631bbd3551608130cabe1a10ff80f6e649a7 100644 (file)
@@ -9,8 +9,6 @@
 #include "../cl_impulse.qh"
 #include "../cheats.qh"
 #include "../g_damage.qh"
-#include "../g_subs.qh"
-#include "../t_teleporters.qh"
 #include "../round_handler.qh"
 #include "../scores.qh"
 #include "../scores_rules.qh"
@@ -34,6 +32,8 @@
 
 #include "../../common/deathtypes.qh"
 #include "../../common/notifications.qh"
+#include "../../common/triggers/teleporters.qh"
+#include "../../common/triggers/subs.qh"
 #include "../../common/stats.qh"
 #include "../../common/teams.qh"
 
index 4537d047610b1c904cb5b505494036ef94715c0a..ab70fb217da5829ae99d1df98c8dd7c9e5dfb007 100644 (file)
@@ -1,3 +1,34 @@
+#ifdef CSQC
+       #define PHYS_DODGING_FRAMETIME                          (1 / (frametime <= 0 ? 60 : frametime))
+       #define PHYS_DODGING                                            getstati(STAT_DODGING)
+       #define PHYS_DODGING_DELAY                                      getstatf(STAT_DODGING_DELAY)
+       #define PHYS_DODGING_TIMEOUT(s)                         getstatf(STAT_DODGING_TIMEOUT)
+       #define PHYS_DODGING_HORIZ_SPEED_FROZEN         getstatf(STAT_DODGING_HORIZ_SPEED_FROZEN)
+       #define PHYS_DODGING_FROZEN_NODOUBLETAP         getstati(STAT_DODGING_FROZEN_NO_DOUBLETAP)
+       #define PHYS_DODGING_HORIZ_SPEED                        getstatf(STAT_DODGING_HORIZ_SPEED)
+       #define PHYS_DODGING_PRESSED_KEYS(s)            s.pressedkeys
+       #define PHYS_DODGING_HEIGHT_THRESHOLD           getstatf(STAT_DODGING_HEIGHT_THRESHOLD)
+       #define PHYS_DODGING_DISTANCE_THRESHOLD         getstatf(STAT_DODGING_DISTANCE_THRESHOLD)
+       #define PHYS_DODGING_RAMP_TIME                          getstatf(STAT_DODGING_RAMP_TIME)
+       #define PHYS_DODGING_UP_SPEED                           getstatf(STAT_DODGING_UP_SPEED)
+       #define PHYS_DODGING_WALL                                       getstatf(STAT_DODGING_WALL)
+#elif defined(SVQC)
+       #define PHYS_DODGING_FRAMETIME                          sys_frametime
+       #define PHYS_DODGING                                            g_dodging
+       #define PHYS_DODGING_DELAY                                      autocvar_sv_dodging_delay
+       #define PHYS_DODGING_TIMEOUT(s)                         s.cvar_cl_dodging_timeout
+       #define PHYS_DODGING_HORIZ_SPEED_FROZEN         autocvar_sv_dodging_horiz_speed_frozen
+       #define PHYS_DODGING_FROZEN_NODOUBLETAP         autocvar_sv_dodging_frozen_doubletap
+       #define PHYS_DODGING_HORIZ_SPEED                        autocvar_sv_dodging_horiz_speed
+       #define PHYS_DODGING_PRESSED_KEYS(s)            s.pressedkeys
+       #define PHYS_DODGING_HEIGHT_THRESHOLD           autocvar_sv_dodging_height_threshold
+       #define PHYS_DODGING_DISTANCE_THRESHOLD         autocvar_sv_dodging_wall_distance_threshold
+       #define PHYS_DODGING_RAMP_TIME                          autocvar_sv_dodging_ramp_time
+       #define PHYS_DODGING_UP_SPEED                           autocvar_sv_dodging_up_speed
+       #define PHYS_DODGING_WALL                                       autocvar_sv_dodging_wall_dodging
+#endif
+
+#ifdef SVQC
 #include "mutator_dodging.qh"
 #include "../_all.qh"
 
@@ -7,6 +38,26 @@
 
 .float cvar_cl_dodging_timeout;
 
+.float stat_dodging;
+.float stat_dodging_delay;
+.float stat_dodging_horiz_speed_frozen;
+.float stat_dodging_frozen_nodoubletap;
+.float stat_dodging_frozen;
+.float stat_dodging_horiz_speed;
+.float stat_dodging_height_threshold;
+.float stat_dodging_distance_threshold;
+.float stat_dodging_ramp_time;
+.float stat_dodging_up_speed;
+.float stat_dodging_wall;
+
+#endif
+
+// set to 1 to indicate dodging has started.. reset by physics hook after dodge has been done..
+.float dodging_action;
+
+// the jump part of the dodge cannot be ramped
+.float dodging_single_action;
+
 
 // these are used to store the last key press time for each of the keys..
 .float last_FORWARD_KEY_time;
 // until it's 0.
 .float dodging_velocity_gain;
 
-MUTATOR_HOOKFUNCTION(dodging_GetCvars) {
-       GetCvars_handleFloat(get_cvars_s, get_cvars_f, cvar_cl_dodging_timeout, "cl_dodging_timeout");
-       return 0;
+#ifdef CSQC
+.int pressedkeys;
+
+#elif defined(SVQC)
+
+void dodging_UpdateStats()
+{
+       self.stat_dodging = PHYS_DODGING;
+       self.stat_dodging_delay = PHYS_DODGING_DELAY;
+       self.stat_dodging_horiz_speed_frozen = PHYS_DODGING_HORIZ_SPEED_FROZEN;
+       self.stat_dodging_frozen = PHYS_DODGING_FROZEN;
+       self.stat_dodging_frozen_nodoubletap = PHYS_DODGING_FROZEN_NODOUBLETAP;
+       self.stat_dodging_height_threshold = PHYS_DODGING_HEIGHT_THRESHOLD;
+       self.stat_dodging_distance_threshold = PHYS_DODGING_DISTANCE_THRESHOLD;
+       self.stat_dodging_ramp_time = PHYS_DODGING_RAMP_TIME;
+       self.stat_dodging_up_speed = PHYS_DODGING_UP_SPEED;
+       self.stat_dodging_wall = PHYS_DODGING_WALL;
 }
 
-MUTATOR_HOOKFUNCTION(dodging_PlayerPhysics) {
-       // print("dodging_PlayerPhysics\n");
+void dodging_Initialize()
+{
+       addstat(STAT_DODGING, AS_INT, stat_dodging);
+       addstat(STAT_DODGING_DELAY, AS_FLOAT, stat_dodging_delay);
+       addstat(STAT_DODGING_TIMEOUT, AS_FLOAT, cvar_cl_dodging_timeout); // we stat this, so it is updated on the client when updated on server (otherwise, chaos)
+       addstat(STAT_DODGING_FROZEN_NO_DOUBLETAP, AS_INT, stat_dodging_frozen_nodoubletap);
+       addstat(STAT_DODGING_HORIZ_SPEED_FROZEN, AS_FLOAT, stat_dodging_horiz_speed_frozen);
+       addstat(STAT_DODGING_FROZEN, AS_INT, stat_dodging_frozen);
+       addstat(STAT_DODGING_HORIZ_SPEED, AS_FLOAT, stat_dodging_horiz_speed);
+       addstat(STAT_DODGING_HEIGHT_THRESHOLD, AS_FLOAT, stat_dodging_height_threshold);
+       addstat(STAT_DODGING_DISTANCE_THRESHOLD, AS_FLOAT, stat_dodging_distance_threshold);
+       addstat(STAT_DODGING_RAMP_TIME, AS_FLOAT, stat_dodging_ramp_time);
+       addstat(STAT_DODGING_UP_SPEED, AS_FLOAT, stat_dodging_up_speed);
+       addstat(STAT_DODGING_WALL, AS_FLOAT, stat_dodging_wall);
+}
+
+#endif
+
+// returns 1 if the player is close to a wall
+bool check_close_to_wall(float threshold)
+{
+       if (PHYS_DODGING_WALL == 0) { return false; }
+
+       #define X(OFFSET)                                                                                                                               \
+       tracebox(self.origin, self.mins, self.maxs, self.origin + OFFSET, true, self);  \
+       if (trace_fraction < 1 && vlen (self.origin - trace_endpos) < threshold)                \
+               return true;
+       X(1000*v_right);
+       X(-1000*v_right);
+       X(1000*v_forward);
+       X(-1000*v_forward);
+       #undef X
+
+       return false;
+}
+
+bool check_close_to_ground(float threshold)
+{
+       return IS_ONGROUND(self) ? true : false;
+}
+
+float PM_dodging_checkpressedkeys()
+{
+       if(!PHYS_DODGING)
+               return false;
+
+       float frozen_dodging = (PHYS_FROZEN(self) && PHYS_DODGING_FROZEN);
+       float frozen_no_doubletap = (frozen_dodging && !PHYS_DODGING_FROZEN_NODOUBLETAP);
+
+       // first check if the last dodge is far enough back in time so we can dodge again
+       if ((time - self.last_dodging_time) < PHYS_DODGING_DELAY)
+               return false;
+
+       makevectors(self.angles);
 
-       float common_factor;
-       float new_velocity_gain;
-       float velocity_difference;
-       float clean_up_and_do_nothing;
-       float horiz_speed = autocvar_sv_dodging_horiz_speed;
+       if (check_close_to_ground(PHYS_DODGING_HEIGHT_THRESHOLD) != 1
+               && check_close_to_wall(PHYS_DODGING_DISTANCE_THRESHOLD) != 1)
+               return true;
 
-       if(self.frozen)
-               horiz_speed = autocvar_sv_dodging_horiz_speed_frozen;
+       float tap_direction_x = 0;
+       float tap_direction_y = 0;
+       float dodge_detected = 0;
+
+       #define X(COND,BTN,RESULT)                                                                                                                      \
+       if (self.movement_##COND)                                                                                               \
+               /* is this a state change? */                                                                                                   \
+               if(!(PHYS_DODGING_PRESSED_KEYS(self) & KEY_##BTN) || frozen_no_doubletap) {             \
+                               tap_direction_##RESULT;                                                                                                 \
+                               if ((time - self.last_##BTN##_KEY_time) < PHYS_DODGING_TIMEOUT(self))   \
+                                       dodge_detected = 1;                                                                                                     \
+                               self.last_##BTN##_KEY_time = time;                                                                              \
+               }
+       X(x < 0, BACKWARD,      x--);
+       X(x > 0, FORWARD,       x++);
+       X(y < 0, LEFT,          y--);
+       X(y > 0, RIGHT,         y++);
+       #undef X
+
+       if (dodge_detected == 1)
+       {
+               self.last_dodging_time = time;
 
-    if (self.deadflag != DEAD_NO)
-        return 0;
+               self.dodging_action = 1;
+               self.dodging_single_action = 1;
 
-       new_velocity_gain = 0;
-       clean_up_and_do_nothing = 0;
+               self.dodging_velocity_gain = PHYS_DODGING_HORIZ_SPEED;
 
-       if (g_dodging == 0)
-               clean_up_and_do_nothing = 1;
+               self.dodging_direction_x = tap_direction_x;
+               self.dodging_direction_y = tap_direction_y;
+
+               // normalize the dodging_direction vector.. (unlike UT99) XD
+               float length = self.dodging_direction_x * self.dodging_direction_x
+                                       + self.dodging_direction_y * self.dodging_direction_y;
+               length = sqrt(length);
+
+               self.dodging_direction_x = self.dodging_direction_x * 1.0 / length;
+               self.dodging_direction_y = self.dodging_direction_y * 1.0 / length;
+               return true;
+       }
+       return false;
+}
+
+void PM_dodging()
+{
+       if (!PHYS_DODGING)
+               return;
+
+#ifdef SVQC
+       dodging_UpdateStats();
+#endif
+
+    if (PHYS_DEAD(self))
+        return;
 
        // when swimming, no dodging allowed..
        if (self.waterlevel >= WATERLEVEL_SWIMMING)
-               clean_up_and_do_nothing = 1;
-
-       if (clean_up_and_do_nothing != 0) {
+       {
                self.dodging_action = 0;
                self.dodging_direction_x = 0;
                self.dodging_direction_y = 0;
-               return 0;
+               return;
        }
 
        // make sure v_up, v_right and v_forward are sane
@@ -69,192 +225,77 @@ MUTATOR_HOOKFUNCTION(dodging_PlayerPhysics) {
        // if we have e.g. 0.5 sec ramptime and a frametime of 0.25, then the ramp code
        // will be called ramp_time/frametime times = 2 times. so, we need to
        // add 0.5 * the total speed each frame until the dodge action is done..
-       common_factor = sys_frametime / autocvar_sv_dodging_ramp_time;
+       float common_factor = PHYS_DODGING_FRAMETIME / PHYS_DODGING_RAMP_TIME;
 
        // if ramp time is smaller than frametime we get problems ;D
-       if (common_factor > 1)
-               common_factor = 1;
+       common_factor = min(common_factor, 1);
 
-       new_velocity_gain = self.dodging_velocity_gain - (common_factor * horiz_speed);
-       if (new_velocity_gain < 0)
-               new_velocity_gain = 0;
+       float horiz_speed = PHYS_FROZEN(self) ? PHYS_DODGING_HORIZ_SPEED_FROZEN : PHYS_DODGING_HORIZ_SPEED;
+       float new_velocity_gain = self.dodging_velocity_gain - (common_factor * horiz_speed);
+       new_velocity_gain = max(0, new_velocity_gain);
 
-       velocity_difference = self.dodging_velocity_gain - new_velocity_gain;
+       float velocity_difference = self.dodging_velocity_gain - new_velocity_gain;
 
        // ramp up dodging speed by adding some velocity each frame.. TODO: do it! :D
-       if (self.dodging_action == 1) {
+       if (self.dodging_action == 1)
+       {
                //disable jump key during dodge accel phase
-               if (self.movement.z > 0) self.movement_z = 0;
+               if(self.movement_z > 0) { self.movement_z = 0; }
 
-               self.velocity =
-                         self.velocity
-                       + ((self.dodging_direction.y * velocity_difference) * v_right)
-                       + ((self.dodging_direction.x * velocity_difference) * v_forward);
+               self.velocity += ((self.dodging_direction_y * velocity_difference) * v_right)
+                                       + ((self.dodging_direction_x * velocity_difference) * v_forward);
 
                self.dodging_velocity_gain = self.dodging_velocity_gain - velocity_difference;
        }
 
        // the up part of the dodge is a single shot action
-       if (self.dodging_single_action == 1) {
-               self.flags &= ~FL_ONGROUND;
+       if (self.dodging_single_action == 1)
+       {
+               UNSET_ONGROUND(self);
 
-               self.velocity =
-                         self.velocity
-                       + (autocvar_sv_dodging_up_speed * v_up);
+               self.velocity += PHYS_DODGING_UP_SPEED * v_up;
 
+#ifdef SVQC
                if (autocvar_sv_dodging_sound)
                        PlayerSound(playersound_jump, CH_PLAYER, VOICETYPE_PLAYERSOUND);
 
                animdecide_setaction(self, ANIMACTION_JUMP, true);
+#endif
 
                self.dodging_single_action = 0;
        }
 
        // are we done with the dodging ramp yet?
-       if((self.dodging_action == 1) && ((time - self.last_dodging_time) > autocvar_sv_dodging_ramp_time))
+       if((self.dodging_action == 1) && ((time - self.last_dodging_time) > PHYS_DODGING_RAMP_TIME))
        {
                // reset state so next dodge can be done correctly
                self.dodging_action = 0;
                self.dodging_direction_x = 0;
                self.dodging_direction_y = 0;
        }
-
-       return 0;
 }
 
+#ifdef SVQC
 
-// returns 1 if the player is close to a wall
-bool check_close_to_wall(float threshold)
+MUTATOR_HOOKFUNCTION(dodging_GetCvars)
 {
-       if(!autocvar_sv_dodging_wall_dodging)
-               return false;
-
-       vector trace_start;
-       vector trace_end;
-
-       trace_start = self.origin;
-
-       trace_end = self.origin + (1000*v_right);
-       tracebox(trace_start, self.mins, self.maxs, trace_end, true, self);
-       if (trace_fraction < 1 && vlen (self.origin - trace_endpos) < threshold)
-               return true;
-
-       trace_end = self.origin - (1000*v_right);
-       tracebox(trace_start, self.mins, self.maxs, trace_end, true, self);
-       if (trace_fraction < 1 && vlen (self.origin - trace_endpos) < threshold)
-               return true;
-
-       trace_end = self.origin + (1000*v_forward);
-       tracebox(trace_start, self.mins, self.maxs, trace_end, true, self);
-       if (trace_fraction < 1 && vlen (self.origin - trace_endpos) < threshold)
-               return true;
-
-       trace_end = self.origin - (1000*v_forward);
-       tracebox(trace_start, self.mins, self.maxs, trace_end, true, self);
-       if (trace_fraction < 1 && vlen (self.origin - trace_endpos) < threshold)
-               return true;
-
+       GetCvars_handleFloat(get_cvars_s, get_cvars_f, cvar_cl_dodging_timeout, "cl_dodging_timeout");
        return false;
 }
 
-bool check_close_to_ground(float threshold)
+MUTATOR_HOOKFUNCTION(dodging_PlayerPhysics)
 {
-       if (self.flags & FL_ONGROUND)
-               return true;
+       // print("dodging_PlayerPhysics\n");
+       PM_dodging();
 
        return false;
 }
 
+MUTATOR_HOOKFUNCTION(dodging_GetPressedKeys)
+{
+       PM_dodging_checkpressedkeys();
 
-MUTATOR_HOOKFUNCTION(dodging_GetPressedKeys) {
-       // print("dodging_PlayerPhysics\n");
-
-       float length;
-       vector tap_direction = '0 0 0';
-
-       float frozen_dodging, frozen_no_doubletap;
-       frozen_dodging = (self.frozen && autocvar_sv_dodging_frozen);
-       frozen_no_doubletap = (frozen_dodging && !autocvar_sv_dodging_frozen_doubletap);
-
-       float dodge_detected;
-       if (g_dodging == 0)
-               return 0;
-
-       dodge_detected = 0;
-
-       // first check if the last dodge is far enough back in time so we can dodge again
-       if ((time - self.last_dodging_time) < autocvar_sv_dodging_delay)
-               return 0;
-
-       if (check_close_to_ground(autocvar_sv_dodging_height_threshold) != 1
-               && check_close_to_wall(autocvar_sv_dodging_wall_distance_threshold) != 1)
-               return 0;
-
-       if (self.movement.x > 0) {
-               // is this a state change?
-               if (!(self.pressedkeys & KEY_FORWARD) || frozen_no_doubletap) {
-                       if ((time - self.last_FORWARD_KEY_time) < self.cvar_cl_dodging_timeout) {
-                               tap_direction.x = 1.0;
-                               dodge_detected = 1;
-                       }
-                       self.last_FORWARD_KEY_time = time;
-               }
-       }
-
-       if (self.movement.x < 0) {
-               // is this a state change?
-               if (!(self.pressedkeys & KEY_BACKWARD) || frozen_no_doubletap) {
-                       tap_direction.x = -1.0;
-                       if ((time - self.last_BACKWARD_KEY_time) < self.cvar_cl_dodging_timeout)        {
-                               dodge_detected = 1;
-                       }
-                       self.last_BACKWARD_KEY_time = time;
-               }
-       }
-
-       if (self.movement.y > 0) {
-               // is this a state change?
-               if (!(self.pressedkeys & KEY_RIGHT) || frozen_no_doubletap) {
-                       tap_direction.y = 1.0;
-                       if ((time - self.last_RIGHT_KEY_time) < self.cvar_cl_dodging_timeout)   {
-                               dodge_detected = 1;
-                       }
-                       self.last_RIGHT_KEY_time = time;
-               }
-       }
-
-       if (self.movement.y < 0) {
-               // is this a state change?
-               if (!(self.pressedkeys & KEY_LEFT) || frozen_no_doubletap) {
-                       tap_direction.y = -1.0;
-                       if ((time - self.last_LEFT_KEY_time) < self.cvar_cl_dodging_timeout)    {
-                               dodge_detected = 1;
-                       }
-                       self.last_LEFT_KEY_time = time;
-               }
-       }
-
-       if (dodge_detected == 1) {
-               self.last_dodging_time = time;
-
-               self.dodging_action = 1;
-               self.dodging_single_action = 1;
-
-               self.dodging_velocity_gain = autocvar_sv_dodging_horiz_speed;
-
-               self.dodging_direction_x = tap_direction.x;
-               self.dodging_direction_y = tap_direction.y;
-
-               // normalize the dodging_direction vector.. (unlike UT99) XD
-               length =          self.dodging_direction.x * self.dodging_direction.x;
-               length = length + self.dodging_direction.y * self.dodging_direction.y;
-               length = sqrt(length);
-
-               self.dodging_direction_x = self.dodging_direction.x * 1.0/length;
-               self.dodging_direction_y = self.dodging_direction.y * 1.0/length;
-       }
-
-       return 0;
+       return false;
 }
 
 MUTATOR_DEFINITION(mutator_dodging)
@@ -272,7 +313,8 @@ MUTATOR_DEFINITION(mutator_dodging)
        // this just turns on the cvar.
        MUTATOR_ONADD
        {
-               g_dodging = 1;
+               g_dodging = cvar("g_dodging");
+               dodging_Initialize();
        }
 
        // this just turns off the cvar.
@@ -281,9 +323,6 @@ MUTATOR_DEFINITION(mutator_dodging)
                g_dodging = 0;
        }
 
-       MUTATOR_ONREMOVE
-       {
-       }
-
-       return 0;
+       return false;
 }
+#endif
index 235845184cf13b73bb66b0d9da1dbed2f6d210bb..dd879e5de4bc4858c3bf19c14c0f561843c13b0d 100644 (file)
@@ -1,34 +1,71 @@
-#include "../_all.qh"
-
-#include "mutator.qh"
-
-#include "../antilag.qh"
+#ifdef SVQC
+       #include "../_all.qh"
+       #include "mutator.qh"
+       #include "../antilag.qh"
+#endif
 
 .float multijump_count;
 .float multijump_ready;
 
-MUTATOR_HOOKFUNCTION(multijump_PlayerPhysics)
+#ifdef CSQC
+
+#define PHYS_MULTIJUMP                                 getstati(STAT_MULTIJUMP)
+#define PHYS_MULTIJUMP_SPEED           getstatf(STAT_MULTIJUMP_SPEED)
+#define PHYS_MULTIJUMP_ADD                     getstati(STAT_MULTIJUMP_ADD)
+
+#elif defined(SVQC)
+
+#define PHYS_MULTIJUMP                                 autocvar_g_multijump
+#define PHYS_MULTIJUMP_SPEED           autocvar_g_multijump_speed
+#define PHYS_MULTIJUMP_ADD                     autocvar_g_multijump_add
+
+
+.float stat_multijump;
+.float stat_multijump_speed;
+.float stat_multijump_add;
+
+void multijump_UpdateStats()
 {
-       if(self.flags & FL_ONGROUND)
-               self.multijump_count = 0;
+       self.stat_multijump = PHYS_MULTIJUMP;
+       self.stat_multijump_speed = PHYS_MULTIJUMP_SPEED;
+       self.stat_multijump_add = PHYS_MULTIJUMP_ADD;
+}
 
-       return false;
+void multijump_AddStats()
+{
+       addstat(STAT_MULTIJUMP, AS_INT, stat_multijump);
+       addstat(STAT_MULTIJUMP_SPEED, AS_FLOAT, stat_multijump_speed);
+       addstat(STAT_MULTIJUMP_ADD, AS_INT, stat_multijump_add);
 }
 
-MUTATOR_HOOKFUNCTION(multijump_PlayerJump)
+#endif
+
+void PM_multijump()
 {
-       if (self.flags & FL_JUMPRELEASED && !(self.flags & FL_ONGROUND)) // jump button pressed this frame and we are in midair
+       if(!PHYS_MULTIJUMP) { return; }
+
+       if(IS_ONGROUND(self))
+       {
+               self.multijump_count = 0;
+       }
+}
+
+float PM_multijump_checkjump()
+{
+       if(!PHYS_MULTIJUMP) { return false; }
+
+       if (!IS_JUMP_HELD(self) && !IS_ONGROUND(self)) // jump button pressed this frame and we are in midair
                self.multijump_ready = true;  // this is necessary to check that we released the jump button and pressed it again
        else
                self.multijump_ready = false;
 
-       if(!player_multijump && self.multijump_ready && (autocvar_g_multijump == -1 || self.multijump_count < autocvar_g_multijump) && self.velocity.z > autocvar_g_multijump_speed)
+       if(!player_multijump && self.multijump_ready && (self.multijump_count < PHYS_MULTIJUMP || PHYS_MULTIJUMP == -1) && self.velocity_z > PHYS_MULTIJUMP_SPEED)
        {
-               if (autocvar_g_multijump)
+               if (PHYS_MULTIJUMP)
                {
-                       if (autocvar_g_multijump_add == 0) // in this case we make the z velocity == jumpvelocity
+                       if (!PHYS_MULTIJUMP_ADD) // in this case we make the z velocity == jumpvelocity
                        {
-                               if (self.velocity.z < autocvar_sv_jumpvelocity)
+                               if (self.velocity_z < PHYS_JUMPVELOCITY)
                                {
                                        player_multijump = true;
                                        self.velocity_z = 0;
@@ -39,21 +76,26 @@ MUTATOR_HOOKFUNCTION(multijump_PlayerJump)
 
                        if(player_multijump)
                        {
-                               if(self.movement.x != 0 || self.movement.y != 0) // don't remove all speed if player isnt pressing any movement keys
+                               if(self.movement_x != 0 || self.movement_y != 0) // don't remove all speed if player isnt pressing any movement keys
                                {
                                        float curspeed;
                                        vector wishvel, wishdir;
 
+#ifdef SVQC
                                        curspeed = max(
                                                vlen(vec2(self.velocity)), // current xy speed
                                                vlen(vec2(antilag_takebackavgvelocity(self, max(self.lastteleporttime + sys_frametime, time - 0.25), time))) // average xy topspeed over the last 0.25 secs
                                        );
-                                       makevectors(self.v_angle.y * '0 1 0');
-                                       wishvel = v_forward * self.movement.x + v_right * self.movement.y;
+#elif defined(CSQC)
+                                       curspeed = vlen(vec2(self.velocity));
+#endif
+
+                                       makevectors(self.v_angle_y * '0 1 0');
+                                       wishvel = v_forward * self.movement_x + v_right * self.movement_y;
                                        wishdir = normalize(wishvel);
 
-                                       self.velocity_x = wishdir.x * curspeed; // allow "dodging" at a multijump
-                                       self.velocity_y = wishdir.y * curspeed;
+                                       self.velocity_x = wishdir_x * curspeed; // allow "dodging" at a multijump
+                                       self.velocity_y = wishdir_y * curspeed;
                                        // keep velocity_z unchanged!
                                }
                                self.multijump_count += 1;
@@ -65,6 +107,20 @@ MUTATOR_HOOKFUNCTION(multijump_PlayerJump)
        return false;
 }
 
+#ifdef SVQC
+MUTATOR_HOOKFUNCTION(multijump_PlayerPhysics)
+{
+       multijump_UpdateStats();
+       PM_multijump();
+
+       return false;
+}
+
+MUTATOR_HOOKFUNCTION(multijump_PlayerJump)
+{
+       return PM_multijump_checkjump();
+}
+
 MUTATOR_HOOKFUNCTION(multijump_BuildMutatorsString)
 {
        ret_string = strcat(ret_string, ":multijump");
@@ -84,5 +140,11 @@ MUTATOR_DEFINITION(mutator_multijump)
        MUTATOR_HOOK(BuildMutatorsString, multijump_BuildMutatorsString, CBC_ORDER_ANY);
        MUTATOR_HOOK(BuildMutatorsPrettyString, multijump_BuildMutatorsPrettyString, CBC_ORDER_ANY);
 
+       MUTATOR_ONADD
+       {
+               multijump_AddStats();
+       }
+
        return false;
 }
+#endif
index 4debaa5a88e9d3891b611f30f81dd17a82976359..e139865ab56395936c000fe3e28a6981d8f553a5 100644 (file)
@@ -74,7 +74,6 @@
     #include "../playerdemo.qh"
     #include "../round_handler.qh"
     #include "../item_key.qh"
-    #include "../secret.qh"
     #include "../pathlib/pathlib.qh"
     #include "../tturrets/include/turrets.qh"
     #include "../vehicles/all.qh"
index cbc43240de75421a01cd7beddb37e7d31bc873fd..bfd9c0b35fca09633e67710f2f7b5acf26d49933 100644 (file)
@@ -1,8 +1,6 @@
 #include "pathlib.qh"
 #include "_all.qh"
 
-#include "g_subs.qh"
-
 #define medium spawnshieldtime
 
 //#define DEBUGPATHING
index cb671ddc70fd7840b5cf0a027dadb24fc6a489f9..ad8aea50cdf068c9c0f164a2ab384b9e5fa7bac9 100644 (file)
@@ -2,12 +2,12 @@
 #include "_all.qh"
 
 #include "g_hook.qh"
-#include "g_subs.qh"
 #include "mutators/mutators_include.qh"
-#include "t_teleporters.qh"
 #include "../common/constants.qh"
 #include "../common/deathtypes.qh"
 #include "../common/notifications.qh"
+#include "../common/triggers/teleporters.qh"
+#include "../common/triggers/subs.qh"
 #include "../common/util.qh"
 #include "../common/weapons/all.qh"
 #include "../csqcmodellib/sv_model.qh"
@@ -306,8 +306,8 @@ void Portal_Touch()
                return;
 
        /*
-       if(other.mins_x < PL_MIN_x || other.mins_y < PL_MIN_y || other.mins_z < PL_MIN_z
-       || other.maxs_x > PL_MAX_x || other.maxs_y > PL_MAX_y || other.maxs_z > PL_MAX_z)
+       if(other.mins_x < PL_MIN.x || other.mins_y < PL_MIN.y || other.mins_z < PL_MIN.z
+       || other.maxs_x > PL_MAX.x || other.maxs_y > PL_MAX.y || other.maxs_z > PL_MAX.z)
        {
                // can't teleport this
                return;
index 75301d592972ee3e67ade8b01a84a72c7d188f19..ab87642aaa5843e6f9b05150b6f80aa0ec198ab0 100644 (file)
@@ -12,11 +12,9 @@ campaign.qc
 cheats.qc
 cl_client.qc
 cl_impulse.qc
-cl_physics.qc
 cl_player.qc
 csqceffects.qc
 ent_cs.qc
-func_breakable.qc
 g_casings.qc
 g_damage.qc
 g_hook.qc
@@ -24,7 +22,6 @@ g_hook.qc
 g_models.qc
 g_subs.qc
 g_tetris.qc
-g_triggers.qc
 g_violence.qc
 g_world.qc
 ipban.qc
@@ -39,21 +36,14 @@ race.qc
 round_handler.qc
 scores.qc
 scores_rules.qc
-secret.qc
 spawnpoints.qc
 steerlib.qc
 sv_main.qc
-target_music.qc
-target_spawn.qc
 teamplay.qc
 t_halflife.qc
 t_items.qc
-t_jumppads.qc
-t_plats.qc
-t_quake.qc
 t_quake3.qc
-t_swamp.qc
-t_teleporters.qc
+t_quake.qc
 waypointsprites.qc
 
 bot/aim.qc
@@ -100,11 +90,14 @@ weapons/weaponsystem.qc
 ../common/monsters/all.qc
 ../common/monsters/spawn.qc
 ../common/monsters/sv_monsters.qc
+../common/movetypes/include.qc
 ../common/nades.qc
 ../common/net_notice.qc
 ../common/notifications.qc
+../common/physics.qc
 ../common/playerstats.qc
 ../common/test.qc
+../common/triggers/include.qc
 ../common/urllib.qc
 ../common/util.qc
 
diff --git a/qcsrc/server/secret.qc b/qcsrc/server/secret.qc
deleted file mode 100644 (file)
index f83026f..0000000
+++ /dev/null
@@ -1,80 +0,0 @@
-#include "secret.qh"
-#include "_all.qh"
-
-#include "g_triggers.qh"
-#include "../common/util.qh"
-
-void secrets_setstatus() {
-       self.stat_secrets_total = secrets_total;
-       self.stat_secrets_found = secrets_found;
-}
-
-/**
- * A secret has been found (maybe :P)
- */
-void trigger_secret_touch() {
-       // only a player can trigger this
-       if (!IS_PLAYER(other))
-               return;
-
-       // update secrets found counter
-       secrets_found += 1;
-       //print("Secret found: ", ftos(secret_counter.cnt), "/");
-       //print(ftos(secret_counter.count), "\n");
-
-       // centerprint message (multi_touch() doesn't always call centerprint())
-       centerprint(other, self.message);
-       self.message = "";
-
-       // handle normal trigger features
-       multi_touch();
-       remove(self);
-}
-
-/*QUAKED trigger_secret (.5 .5 .5) ?
-Variable sized secret trigger. Can be targeted at one or more entities.
-Basically, it's a trigger_once (with restrictions, see notes) that additionally updates the number of secrets found.
--------- KEYS --------
-sounds: 1 to play misc/secret.wav, 2 to play misc/talk.wav, 3 to play misc/trigger1.wav (default: 1)
-noise: path to sound file, if you want to play something else
-target: trigger all entities with this targetname when triggered
-message: print this message to the player who activated the trigger instead of the standard 'You found a secret!'
-killtarget: remove all entities with this targetname when triggered
--------- NOTES --------
-You should create a common/trigger textured brush covering the entrance to a secret room/area.
-Trigger secret can only be trigger by a player's touch and can not be a target itself.
-*/
-void spawnfunc_trigger_secret() {
-       // FIXME: should it be disabled in most modes?
-
-       // update secrets count
-       secrets_total += 1;
-
-       // add default message
-       if (self.message == "")
-               self.message = "You found a secret!";
-
-       // set default sound
-       if (self.noise == "")
-       if (!self.sounds)
-               self.sounds = 1; // misc/secret.wav
-
-       // this entity can't be a target itself!!!!
-       self.targetname = "";
-
-       // you can't just shoot a room to find it, can you?
-       self.health = 0;
-
-       // a secret can not be delayed
-       self.delay = 0;
-
-       // convert this trigger to trigger_once
-       self.classname = "trigger_once";
-       spawnfunc_trigger_once();
-
-       // take over the touch() function, so we can mark secret as found
-       self.touch = trigger_secret_touch;
-       // ignore triggering;
-       self.use = func_null;
-}
-
diff --git a/qcsrc/server/secret.qh b/qcsrc/server/secret.qh
deleted file mode 100644 (file)
index 6e6ae0b..0000000
+++ /dev/null
@@ -1,22 +0,0 @@
-#ifndef SECRET_H
-#define SECRET_H
-
-/**
- * Total number of secrets on the map.
- */
-float secrets_total;
-
-/**
- * Total numbe of secrets found on the map.
- */
-float secrets_found;
-
-
-.float stat_secrets_total;
-.float stat_secrets_found;
-
-/**
- * update secrets status.
- */
-void secrets_setstatus();
-#endif
index e8e70402a3968f340f5c2ac68021c9836193abea..b86da6b54ee9f0823dec8d72f2a34d11de7f40b0 100644 (file)
@@ -61,13 +61,13 @@ void relocate_spawnpoint()
     // nudge off the floor
     setorigin(self, self.origin + '0 0 1');
 
-    tracebox(self.origin, PL_MIN, PL_MAX, self.origin, true, self);
+    tracebox(self.origin, PL_MIN_CONST, PL_MAX_CONST, self.origin, true, self);
     if (trace_startsolid)
     {
         vector o;
         o = self.origin;
-        self.mins = PL_MIN;
-        self.maxs = PL_MAX;
+        self.mins = PL_MIN_CONST;
+        self.maxs = PL_MAX_CONST;
         if (!move_out_of_solid(self))
             objerror("could not get out of solid at all!");
         print("^1NOTE: this map needs FIXING. Spawnpoint at ", vtos(o - '0 0 1'));
index 0f7d1763b0ce2ebf184661f5ad8182f7173498b2..48cd05361ed9b9192a4949b27117c9287b2462ab 100644 (file)
@@ -140,6 +140,7 @@ void CreatureFrame (void)
                        if (g_footsteps)
                        if (!gameover)
                        if (self.flags & FL_ONGROUND)
+                       if (!self.crouch)
                        if (velocity_len > autocvar_sv_maxspeed * 0.6)
                        if (!self.deadflag)
                        if (time < self.lastground + 0.2)
index 32d597937cc2a422b35176da195cd697ed6b1c90..f041dd39659aebd31a8b375b6e14d1fd51820f1c 100644 (file)
@@ -1,11 +1,3 @@
-#include "_all.qh"
-
-#include "../warpzonelib/util_server.qh"
-#include "vehicles/vehicle.qh"
-
-.float  ladder_time;
-.entity ladder_entity;
-
 .float  roomtype;
 .float  radius;
 .float  pitch;
@@ -40,30 +32,3 @@ void spawnfunc_info_node() {}
 void spawnfunc_env_sound() {}
 void spawnfunc_light_spot() {}
 void spawnfunc_func_healthcharger() {}
-
-
-void func_ladder_touch()
-{
-       if (!other.iscreature)
-               return;
-       if (other.vehicle_flags & VHF_ISVEHICLE)
-               return;
-
-       EXACTTRIGGER_TOUCH;
-
-       other.ladder_time = time + 0.1;
-       other.ladder_entity = self;
-}
-
-void spawnfunc_func_ladder()
-{
-       EXACTTRIGGER_INIT;
-       self.touch = func_ladder_touch;
-}
-
-void spawnfunc_func_water()
-{
-       EXACTTRIGGER_INIT;
-       self.touch = func_ladder_touch;
-}
-
index f817957c8213d62df0ea1dd856bb446760708728..4e3e376e3b26625d03bb5c8ff1cd8f22c20a60c0 100644 (file)
@@ -5,7 +5,6 @@
 #if defined(SVQC)
     #include "_all.qh"
 
-    #include "g_subs.qh"
     #include "waypointsprites.qh"
 
     #include "bot/bot.qh"
@@ -20,6 +19,7 @@
     #include "../common/constants.qh"
     #include "../common/deathtypes.qh"
     #include "../common/notifications.qh"
+       #include "../common/triggers/subs.qh"
     #include "../common/util.qh"
 
     #include "../common/monsters/all.qh"
diff --git a/qcsrc/server/t_jumppads.qc b/qcsrc/server/t_jumppads.qc
deleted file mode 100644 (file)
index 4cafb85..0000000
+++ /dev/null
@@ -1,342 +0,0 @@
-#include "t_jumppads.qh"
-#include "_all.qh"
-
-#include "g_subs.qh"
-
-#include "bot/navigation.qh"
-#include "bot/waypoints.qh"
-
-#include "weapons/csqcprojectile.qh"
-
-#include "../common/animdecide.qh"
-
-#include "../warpzonelib/util_server.qh"
-
-void trigger_push_use()
-{
-       if(teamplay)
-               self.team = activator.team;
-}
-
-/*
-       trigger_push_calculatevelocity
-
-       Arguments:
-         org - origin of the object which is to be pushed
-         tgt - target entity (can be either a point or a model entity; if it is
-               the latter, its midpoint is used)
-         ht  - jump height, measured from the higher one of org and tgt's midpoint
-
-       Returns: velocity for the jump
-       the global trigger_push_calculatevelocity_flighttime is set to the total
-       jump time
- */
-
-vector trigger_push_calculatevelocity(vector org, entity tgt, float ht)
-{
-       float grav, sdist, zdist, vs, vz, jumpheight;
-       vector sdir, torg;
-
-       torg = tgt.origin + (tgt.mins + tgt.maxs) * 0.5;
-
-       grav = autocvar_sv_gravity;
-       if(other.gravity)
-               grav *= other.gravity;
-
-       zdist = torg.z - org.z;
-       sdist = vlen(torg - org - zdist * '0 0 1');
-       sdir = normalize(torg - org - zdist * '0 0 1');
-
-       // how high do we need to push the player?
-       jumpheight = fabs(ht);
-       if(zdist > 0)
-               jumpheight = jumpheight + zdist;
-
-       /*
-               STOP.
-
-               You will not understand the following equations anyway...
-               But here is what I did to get them.
-
-               I used the functions
-
-                 s(t) = t * vs
-                 z(t) = t * vz - 1/2 grav t^2
-
-               and solved for:
-
-                 s(ti) = sdist
-                 z(ti) = zdist
-                 max(z, ti) = jumpheight
-
-               From these three equations, you will find the three parameters vs, vz
-               and ti.
-        */
-
-       // push him so high...
-       vz = sqrt(fabs(2 * grav * jumpheight)); // NOTE: sqrt(positive)!
-
-       // we start with downwards velocity only if it's a downjump and the jump apex should be outside the jump!
-       if(ht < 0)
-               if(zdist < 0)
-                       vz = -vz;
-
-       vector solution;
-       solution = solve_quadratic(0.5 * grav, -vz, zdist); // equation "z(ti) = zdist"
-       // ALWAYS solvable because jumpheight >= zdist
-       if(!solution.z)
-               solution.y = solution.x; // just in case it is not solvable due to roundoff errors, assume two equal solutions at their center (this is mainly for the usual case with ht == 0)
-       if(zdist == 0)
-               solution.x = solution.y; // solution_x is 0 in this case, so don't use it, but rather use solution_y (which will be sqrt(0.5 * jumpheight / grav), actually)
-
-       if(zdist < 0)
-       {
-               // down-jump
-               if(ht < 0)
-               {
-                       // almost straight line type
-                       // jump apex is before the jump
-                       // we must take the larger one
-                       trigger_push_calculatevelocity_flighttime = solution.y;
-               }
-               else
-               {
-                       // regular jump
-                       // jump apex is during the jump
-                       // we must take the larger one too
-                       trigger_push_calculatevelocity_flighttime = solution.y;
-               }
-       }
-       else
-       {
-               // up-jump
-               if(ht < 0)
-               {
-                       // almost straight line type
-                       // jump apex is after the jump
-                       // we must take the smaller one
-                       trigger_push_calculatevelocity_flighttime = solution.x;
-               }
-               else
-               {
-                       // regular jump
-                       // jump apex is during the jump
-                       // we must take the larger one
-                       trigger_push_calculatevelocity_flighttime = solution.y;
-               }
-       }
-       vs = sdist / trigger_push_calculatevelocity_flighttime;
-
-       // finally calculate the velocity
-       return sdir * vs + '0 0 1' * vz;
-}
-
-void trigger_push_touch()
-{
-       if (self.active == ACTIVE_NOT)
-               return;
-
-       if (!isPushable(other))
-               return;
-
-       if(self.team)
-               if(((self.spawnflags & 4) == 0) == (self.team != other.team))
-                       return;
-
-       EXACTTRIGGER_TOUCH;
-
-       if(self.enemy)
-       {
-               other.velocity = trigger_push_calculatevelocity(other.origin, self.enemy, self.height);
-       }
-       else if(self.target)
-       {
-               entity e;
-               RandomSelection_Init();
-               for(e = world; (e = find(e, targetname, self.target)); )
-               {
-                       if(e.cnt)
-                               RandomSelection_Add(e, 0, string_null, e.cnt, 1);
-                       else
-                               RandomSelection_Add(e, 0, string_null, 1, 1);
-               }
-               other.velocity = trigger_push_calculatevelocity(other.origin, RandomSelection_chosen_ent, self.height);
-       }
-       else
-       {
-               other.velocity = self.movedir;
-       }
-
-       other.flags &= ~FL_ONGROUND;
-
-       if (IS_PLAYER(other))
-       {
-               // reset tracking of oldvelocity for impact damage (sudden velocity changes)
-               other.oldvelocity = other.velocity;
-
-               if(self.pushltime < time)  // prevent "snorring" sound when a player hits the jumppad more than once
-               {
-                       // flash when activated
-                       pointparticles(particleeffectnum("jumppad_activate"), other.origin, other.velocity, 1);
-                       sound (other, CH_TRIGGER, self.noise, VOL_BASE, ATTEN_NORM);
-                       self.pushltime = time + 0.2;
-               }
-               if(IS_REAL_CLIENT(other) || IS_BOT_CLIENT(other))
-               {
-                       bool found = false;
-                       for(int i = 0; i < other.jumppadcount && i < NUM_JUMPPADSUSED; ++i)
-                               if(other.(jumppadsused[i]) == self)
-                                       found = true;
-                       if(!found)
-                       {
-                               other.(jumppadsused[other.jumppadcount % NUM_JUMPPADSUSED]) = self;
-                               other.jumppadcount = other.jumppadcount + 1;
-                       }
-
-                       if(IS_REAL_CLIENT(other))
-                       {
-                               if(self.message)
-                                       centerprint(other, self.message);
-                       }
-                       else
-                               other.lastteleporttime = time;
-
-                       if (other.deadflag == DEAD_NO)
-                               animdecide_setaction(other, ANIMACTION_JUMP, true);
-               }
-               else
-                       other.jumppadcount = true;
-
-               // reset tracking of who pushed you into a hazard (for kill credit)
-               other.pushltime = 0;
-               other.istypefrag = 0;
-       }
-
-       if(self.enemy.target)
-       {
-               entity oldself;
-               oldself = self;
-               activator = other;
-               self = self.enemy;
-               SUB_UseTargets();
-               self = oldself;
-       }
-
-       if (other.flags & FL_PROJECTILE)
-       {
-               other.angles = vectoangles (other.velocity);
-               switch(other.movetype)
-               {
-                       case MOVETYPE_FLY:
-                               other.movetype = MOVETYPE_TOSS;
-                               other.gravity = 1;
-                               break;
-                       case MOVETYPE_BOUNCEMISSILE:
-                               other.movetype = MOVETYPE_BOUNCE;
-                               other.gravity = 1;
-                               break;
-               }
-               UpdateCSQCProjectile(other);
-       }
-
-       if (self.spawnflags & PUSH_ONCE)
-       {
-               self.touch = func_null;
-               self.think = SUB_Remove;
-               self.nextthink = time;
-       }
-}
-
-void trigger_push_findtarget()
-{
-       entity e, t;
-       vector org;
-
-       // first calculate a typical start point for the jump
-       org = (self.absmin + self.absmax) * 0.5;
-       org.z = self.absmax.z - PL_MIN_z;
-
-       if (self.target)
-       {
-               float n;
-               n = 0;
-               for(t = world; (t = find(t, targetname, self.target)); )
-               {
-                       ++n;
-                       e = spawn();
-                       setorigin(e, org);
-                       setsize(e, PL_MIN, PL_MAX);
-                       e.velocity = trigger_push_calculatevelocity(org, t, self.height);
-                       tracetoss(e, e);
-                       if(e.movetype == MOVETYPE_NONE)
-                               waypoint_spawnforteleporter(self, trace_endpos, vlen(trace_endpos - org) / vlen(e.velocity));
-                       remove(e);
-               }
-
-               if(n == 0)
-               {
-                       // no dest!
-                       objerror ("Jumppad with nonexistant target");
-                       return;
-               }
-               else if(n == 1)
-               {
-                       // exactly one dest - bots love that
-                       self.enemy = find(world, targetname, self.target);
-               }
-               else
-               {
-                       // have to use random selection every single time
-                       self.enemy = world;
-               }
-       }
-       else
-       {
-               e = spawn();
-               setorigin(e, org);
-               setsize(e, PL_MIN, PL_MAX);
-               e.velocity = self.movedir;
-               tracetoss(e, e);
-               waypoint_spawnforteleporter(self, trace_endpos, vlen(trace_endpos - org) / vlen(e.velocity));
-               remove(e);
-       }
-}
-
-/*
- * ENTITY PARAMETERS:
- *
- *   target:  target of jump
- *   height:  the absolute value is the height of the highest point of the jump
- *            trajectory above the higher one of the player and the target.
- *            the sign indicates whether the highest point is INSIDE (positive)
- *            or OUTSIDE (negative) of the jump trajectory. General rule: use
- *            positive values for targets mounted on the floor, and use negative
- *            values to target a point on the ceiling.
- *   movedir: if target is not set, this * speed * 10 is the velocity to be reached.
- */
-void spawnfunc_trigger_push()
-{
-       SetMovedir ();
-
-       EXACTTRIGGER_INIT;
-
-       self.active = ACTIVE_ACTIVE;
-       self.use = trigger_push_use;
-       self.touch = trigger_push_touch;
-
-       // normal push setup
-       if (!self.speed)
-               self.speed = 1000;
-       self.movedir = self.movedir * self.speed * 10;
-
-       if (!self.noise)
-               self.noise = "misc/jumppad.wav";
-       precache_sound (self.noise);
-
-       // this must be called to spawn the teleport waypoints for bots
-       InitializeEntity(self, trigger_push_findtarget, INITPRIO_FINDTARGET);
-}
-
-void spawnfunc_target_push() {}
-void spawnfunc_info_notnull() {}
-void spawnfunc_target_position() {}
diff --git a/qcsrc/server/t_jumppads.qh b/qcsrc/server/t_jumppads.qh
deleted file mode 100644 (file)
index 9d66e98..0000000
+++ /dev/null
@@ -1,55 +0,0 @@
-#ifndef T_JUMPPADS_H
-#define T_JUMPPADS_H
-
-const float PUSH_ONCE          = 1;
-const float PUSH_SILENT                = 2;
-
-.float pushltime;
-.float istypefrag;
-.float height;
-
-void() SUB_UseTargets;
-
-float trigger_push_calculatevelocity_flighttime;
-
-void trigger_push_use();
-
-/*
-       trigger_push_calculatevelocity
-
-       Arguments:
-         org - origin of the object which is to be pushed
-         tgt - target entity (can be either a point or a model entity; if it is
-               the latter, its midpoint is used)
-         ht  - jump height, measured from the higher one of org and tgt's midpoint
-
-       Returns: velocity for the jump
-       the global trigger_push_calculatevelocity_flighttime is set to the total
-       jump time
- */
-
-vector trigger_push_calculatevelocity(vector org, entity tgt, float ht);
-
-void trigger_push_touch();
-
-.vector dest;
-void trigger_push_findtarget();
-
-/*
- * ENTITY PARAMETERS:
- *
- *   target:  target of jump
- *   height:  the absolute value is the height of the highest point of the jump
- *            trajectory above the higher one of the player and the target.
- *            the sign indicates whether the highest point is INSIDE (positive)
- *            or OUTSIDE (negative) of the jump trajectory. General rule: use
- *            positive values for targets mounted on the floor, and use negative
- *            values to target a point on the ceiling.
- *   movedir: if target is not set, this * speed * 10 is the velocity to be reached.
- */
-void spawnfunc_trigger_push();
-
-void spawnfunc_target_push();
-void spawnfunc_info_notnull();
-void spawnfunc_target_position();
-#endif
index 40d6a4d4365e4a18a2b09efb7a19d74f002392be..b93b1d57770c1748b392f496250180963fcd8030 100644 (file)
@@ -5,7 +5,6 @@
 #include "command/common.qh"
 
 #include "g_damage.qh"
-#include "g_subs.qh"
 #include "item_key.qh"
 
 #include "../common/constants.qh"
diff --git a/qcsrc/server/t_swamp.qc b/qcsrc/server/t_swamp.qc
deleted file mode 100644 (file)
index 20b49ec..0000000
+++ /dev/null
@@ -1,111 +0,0 @@
-#if defined(CSQC)
-#elif defined(MENUQC)
-#elif defined(SVQC)
-       #include "_all.qh"
-
-    #include "../warpzonelib/util_server.qh"
-    #include "../common/weapons/all.qh"
-    #include "../common/deathtypes.qh"
-#endif
-
-/*
-*              t_swamp.c
-*              Adds spawnfunc_trigger_swamp and suppoart routines for xonotic 1.2.1+
-*              Author tZork (Jakob MG)
-*              jakob@games43.se
-*              2005 11 29
-*/
-
-.float swamp_interval; //Hurt players in swamp with this interval
-.float swamp_slowdown; //Players in swamp get slowd down by this mutch 0-1 is slowdown 1-~ is speedup (!?)
-.entity swampslug;
-
-void spawnfunc_trigger_swamp(void);
-void swamp_touch(void);
-void swampslug_think();
-
-
-/*
-* Uses a entity calld swampslug to handle players in the swamp
-* It works like this: When the plyer enters teh swamp the spawnfunc_trigger_swamp
-* attaches a new "swampslug" to the player. As long as the plyer is inside
-* the swamp the swamp gives the slug new health. But the slug slowly kills itself
-* so when the player goes outside the swamp, it dies and releases the player from the
-* swamps curses (dmg/slowdown)
-*
-* I do it this way becuz there is no "untouch" event.
-*
-* --NOTE--
-* THE ACCTUAL slowdown is done in cl_physics.c on line 57-60
-* --NOTE--
-*/
-void swampslug_think(void)
-{
-       //Slowly kill the slug
-       self.health = self.health - 1;
-
-       //Slug dead? then remove curses.
-       if(self.health <= 0) {
-               self.owner.in_swamp = 0;
-               remove(self);
-               //centerprint(self.owner,"Killing slug...\n");
-               return;
-       }
-
-       // Slug still alive, so we are still in the swamp
-       // Or we have exited it very recently.
-       // Do the damage and renew the timer.
-       Damage (self.owner, self, self, self.dmg, DEATH_SWAMP, other.origin, '0 0 0');
-
-       self.nextthink = time + self.swamp_interval;
-}
-
-void swamp_touch(void)
-{
-       // If whatever thats touching the swamp is not a player
-       // or if its a dead player, just dont care abt it.
-       if(!IS_PLAYER(other) || other.deadflag != DEAD_NO)
-               return;
-
-       EXACTTRIGGER_TOUCH;
-
-       // Chech if player alredy got a swampslug.
-       if(other.in_swamp != 1) {
-               // If not attach one.
-               //centerprint(other,"Entering swamp!\n");
-               other.swampslug = spawn();
-               other.swampslug.health = 2;
-               other.swampslug.think = swampslug_think;
-               other.swampslug.nextthink = time;
-               other.swampslug.owner = other;
-               other.swampslug.dmg = self.dmg;
-               other.swampslug.swamp_interval = self.swamp_interval;
-               other.swamp_slowdown = self.swamp_slowdown;
-               other.in_swamp = 1;
-               return;
-       }
-
-       //other.in_swamp = 1;
-
-       //Revitalize players swampslug
-       other.swampslug.health = 2;
-}
-
-/*QUAKED spawnfunc_trigger_swamp (.5 .5 .5) ?
-Players gettin into the swamp will
-get slowd down and damaged
-*/
-void spawnfunc_trigger_swamp(void)
-{
-       // Init stuff
-       EXACTTRIGGER_INIT;
-       self.touch = swamp_touch;
-
-       // Setup default keys, if missing
-       if(self.dmg <= 0)
-               self.dmg = 5;
-       if(self.swamp_interval <= 0)
-               self.swamp_interval = 1;
-       if(self.swamp_slowdown <= 0)
-               self.swamp_slowdown = 0.5;
-}
diff --git a/qcsrc/server/t_teleporters.qc b/qcsrc/server/t_teleporters.qc
deleted file mode 100644 (file)
index 4ea5f3b..0000000
+++ /dev/null
@@ -1,347 +0,0 @@
-#include "t_teleporters.qh"
-#include "_all.qh"
-
-#include "anticheat.qh"
-#include "cl_player.qh"
-#include "g_hook.qh"
-#include "bot/waypoints.qh"
-#include "tturrets/include/turrets_early.qh"
-#include "vehicles/vehicle.qh"
-#include "weapons/csqcprojectile.qh"
-#include "../common/constants.qh"
-#include "../common/deathtypes.qh"
-#include "../common/mapinfo.qh"
-#include "../common/util.qh"
-#include "../warpzonelib/common.qh"
-#include "../warpzonelib/server.qh"
-#include "../warpzonelib/util_server.qh"
-
-.float lastteleporttime;
-
-void trigger_teleport_use()
-{
-       if(teamplay)
-               self.team = activator.team;
-}
-
-float check_tdeath(entity player, vector org, vector telefragmin, vector telefragmax)
-{
-       if (IS_PLAYER(player) && player.health >= 1)
-       {
-               TDEATHLOOP(org)
-               {
-                       if (!(teamplay && autocvar_g_telefrags_teamplay && head.team == player.team))
-                               if(IS_PLAYER(head))
-                                       if(head.health >= 1)
-                                               return 1;
-               }
-       }
-       return 0;
-}
-
-void tdeath(entity player, entity teleporter, entity telefragger, vector telefragmin, vector telefragmax)
-{
-       TDEATHLOOP(player.origin)
-       {
-               if (IS_PLAYER(player) && player.health >= 1)
-               {
-                       if (!(teamplay && autocvar_g_telefrags_teamplay && head.team == player.team))
-                       {
-                               if(IS_PLAYER(head))
-                                       if(head.health >= 1)
-                                               ++tdeath_hit;
-                               Damage (head, teleporter, telefragger, 10000, DEATH_TELEFRAG, head.origin, '0 0 0');
-                       }
-               }
-               else // dead bodies and monsters gib themselves instead of telefragging
-                       Damage (telefragger, teleporter, telefragger, 10000, DEATH_TELEFRAG, telefragger.origin, '0 0 0');
-       }
-}
-
-void spawn_tdeath(vector v0, entity e, vector v)
-{
-       tdeath(e, e, e, '0 0 0', '0 0 0');
-}
-
-void TeleportPlayer(entity teleporter, entity player, vector to, vector to_angles, vector to_velocity, vector telefragmin, vector telefragmax, float tflags)
-{
-       entity telefragger;
-       vector from;
-
-       if(teleporter.owner)
-               telefragger = teleporter.owner;
-       else
-               telefragger = player;
-
-       makevectors (to_angles);
-
-       if(player.teleportable == TELEPORT_NORMAL) // don't play sounds or show particles for anything that isn't a player, maybe change later to block only observers
-       {
-               if(self.pushltime < time) // only show one teleport effect per teleporter per 0.2 seconds, for better fps
-               {
-                       if(tflags & TELEPORT_FLAG_SOUND)
-                               sound (player, CH_TRIGGER, "misc/teleport.wav", VOL_BASE, ATTEN_NORM);
-                       if(tflags & TELEPORT_FLAG_PARTICLES)
-                       {
-                               pointparticles(particleeffectnum("teleport"), player.origin, '0 0 0', 1);
-                               pointparticles(particleeffectnum("teleport"), to + v_forward * 32, '0 0 0', 1);
-                       }
-                       self.pushltime = time + 0.2;
-               }
-       }
-
-       // Relocate the player
-       // assuming to allows PL_MIN to PL_MAX box and some more
-       from = player.origin;
-       setorigin (player, to);
-       player.oldorigin = to; // don't undo the teleport by unsticking
-       player.angles = to_angles;
-       player.fixangle = true;
-       player.velocity = to_velocity;
-       BITXOR_ASSIGN(player.effects, EF_TELEPORT_BIT);
-
-       makevectors(player.angles);
-       Reset_ArcBeam(player, v_forward);
-       UpdateCSQCProjectileAfterTeleport(player);
-
-       if(IS_PLAYER(player))
-       {
-               if(tflags & TELEPORT_FLAG_TDEATH)
-                       if(player.takedamage && player.deadflag == DEAD_NO && !g_race && !g_cts && (autocvar_g_telefrags || (tflags & TELEPORT_FLAG_FORCE_TDEATH)))
-                               tdeath(player, teleporter, telefragger, telefragmin, telefragmax);
-
-               // player no longer is on ground
-               player.flags &= ~FL_ONGROUND;
-
-               // reset tracking of oldvelocity for impact damage (sudden velocity changes)
-               player.oldvelocity = player.velocity;
-
-               // reset tracking of who pushed you into a hazard (for kill credit)
-               if(teleporter.owner)
-               {
-                       player.pusher = teleporter.owner;
-                       player.pushltime = time + autocvar_g_maxpushtime;
-                       player.istypefrag = player.BUTTON_CHAT;
-               }
-               else
-               {
-                       player.pushltime = 0;
-                       player.istypefrag = 0;
-               }
-
-               player.lastteleporttime = time;
-       }
-}
-
-entity Simple_TeleportPlayer(entity teleporter, entity player)
-{
-       vector locout;
-       entity e;
-       float p;
-
-       // Find the output teleporter
-       if(teleporter.enemy)
-       {
-               e = teleporter.enemy;
-       }
-       else
-       {
-               RandomSelection_Init();
-               for(e = world; (e = find(e, targetname, teleporter.target)); )
-               {
-                       p = 1;
-                       if(autocvar_g_telefrags_avoid)
-                       {
-                               locout = e.origin + '0 0 1' * (1 - player.mins.z - 24);
-                               if(check_tdeath(player, locout, '0 0 0', '0 0 0'))
-                                       p = 0;
-                       }
-                       RandomSelection_Add(e, 0, string_null, (e.cnt ? e.cnt : 1), p);
-               }
-               e = RandomSelection_chosen_ent;
-       }
-
-       if(!e) { sprint(player, "Teleport destination vanished. Sorry... please complain to the mapper.\n"); }
-
-       makevectors(e.mangle);
-
-       if(e.speed)
-               if(vlen(player.velocity) > e.speed)
-                       player.velocity = normalize(player.velocity) * max(0, e.speed);
-
-       if(autocvar_g_teleport_maxspeed)
-               if(vlen(player.velocity) > autocvar_g_teleport_maxspeed)
-                       player.velocity = normalize(player.velocity) * max(0, autocvar_g_teleport_maxspeed);
-
-       locout = e.origin + '0 0 1' * (1 - player.mins.z - 24);
-       TeleportPlayer(teleporter, player, locout, e.mangle, v_forward * vlen(player.velocity), '0 0 0', '0 0 0', TELEPORT_FLAGS_TELEPORTER);
-
-       return e;
-}
-
-void Teleport_Touch (void)
-{
-       entity oldself;
-       string s;
-
-       if (self.active != ACTIVE_ACTIVE)
-               return;
-
-       if (!other.teleportable)
-               return;
-
-       if(other.vehicle)
-       if(!other.vehicle.teleportable)
-               return;
-
-       if(other.turrcaps_flags & TFL_TURRCAPS_ISTURRET)
-               return;
-
-       if(other.deadflag != DEAD_NO)
-               return;
-
-       if(self.team)
-               if(((self.spawnflags & 4) == 0) == (self.team != other.team))
-                       return;
-
-       EXACTTRIGGER_TOUCH;
-
-       if(IS_PLAYER(other))
-               RemoveGrapplingHook(other);
-
-       entity e;
-       e = Simple_TeleportPlayer(self, other);
-
-       activator = other;
-       s = self.target; self.target = string_null;
-       SUB_UseTargets();
-       if (!self.target) self.target = s;
-
-       oldself = self;
-       self = e;
-       SUB_UseTargets();
-       self = oldself;
-}
-
-void spawnfunc_info_teleport_destination (void)
-{
-       self.classname = "info_teleport_destination";
-
-       self.mangle = self.angles;
-       self.angles = '0 0 0';
-
-       //setorigin (self, self.origin + '0 0 27');     // To fix a mappers' habit as old as Quake
-       setorigin (self, self.origin);
-
-       IFTARGETED
-       {
-       }
-       else
-               objerror ("^3Teleport destination without a targetname");
-}
-
-void spawnfunc_misc_teleporter_dest (void)
-{
-       spawnfunc_info_teleport_destination();
-}
-
-void spawnfunc_target_teleporter (void)
-{
-       spawnfunc_info_teleport_destination();
-}
-
-void teleport_findtarget (void)
-{
-       entity e;
-       float n;
-
-       n = 0;
-       for(e = world; (e = find(e, targetname, self.target)); )
-       {
-               ++n;
-               if(e.movetype == MOVETYPE_NONE)
-                       waypoint_spawnforteleporter(self, e.origin, 0);
-               if(e.classname != "info_teleport_destination")
-                       print("^3MAPPER ERROR: teleporter does target an invalid teleport destination entity. Angles will not work.\n");
-       }
-
-       if(n == 0)
-       {
-               // no dest!
-               objerror ("Teleporter with nonexistant target");
-               return;
-       }
-       else if(n == 1)
-       {
-               // exactly one dest - bots love that
-               self.enemy = find(e, targetname, self.target);
-       }
-       else
-       {
-               // have to use random selection every single time
-               self.enemy = world;
-       }
-
-       // now enable touch
-       self.touch = Teleport_Touch;
-}
-
-entity Teleport_Find(vector mi, vector ma)
-{
-       entity e;
-       for(e = world; (e = find(e, classname, "trigger_teleport")); )
-               if(WarpZoneLib_BoxTouchesBrush(mi, ma, e, world))
-                       return e;
-       return world;
-}
-
-void spawnfunc_trigger_teleport (void)
-{
-       self.angles = '0 0 0';
-
-       EXACTTRIGGER_INIT;
-
-       self.active = ACTIVE_ACTIVE;
-
-       self.use = trigger_teleport_use;
-
-       // this must be called to spawn the teleport waypoints for bots
-       InitializeEntity(self, teleport_findtarget, INITPRIO_FINDTARGET);
-
-       if (self.target == "")
-       {
-               objerror ("Teleporter with no target");
-               return;
-       }
-
-       self.teleport_next = teleport_first;
-       teleport_first = self;
-}
-
-void WarpZone_PostTeleportPlayer_Callback(entity pl)
-{
-       makevectors(pl.angles);
-       Reset_ArcBeam(pl, v_forward);
-       UpdateCSQCProjectileAfterTeleport(pl);
-       {
-               entity oldself = self;
-               self = pl;
-               anticheat_fixangle();
-               self = oldself;
-       }
-       // "disown" projectiles after teleport
-       if(pl.owner)
-       if(pl.owner == pl.realowner)
-       {
-               if(!(pl.flags & FL_PROJECTILE))
-                       print("A non-projectile got through a warpzone and its owner cleared. It's a ", pl.classname, ".\n");
-               pl.owner = world;
-       }
-       if(IS_PLAYER(pl))
-       {
-               // reset tracking of oldvelocity for impact damage (sudden velocity changes)
-               pl.oldvelocity = pl.velocity;
-               // reset teleport time tracking too (or multijump can cause insane speeds)
-               pl.lastteleporttime = time;
-       }
-}
diff --git a/qcsrc/server/t_teleporters.qh b/qcsrc/server/t_teleporters.qh
deleted file mode 100644 (file)
index 03e3c3f..0000000
+++ /dev/null
@@ -1,71 +0,0 @@
-#ifndef T_TELEPORTERS_H
-#define T_TELEPORTERS_H
-
-void trigger_teleport_use();
-
-#define TDEATHLOOP(o) \
-       entity head; \
-       vector deathmin; \
-       vector deathmax; \
-       float deathradius; \
-       deathmin = (o) + player.mins; \
-       deathmax = (o) + player.maxs; \
-       if(telefragmin != telefragmax) \
-       { \
-               if(deathmin.x > telefragmin.x) deathmin.x = telefragmin.x; \
-               if(deathmin.y > telefragmin.y) deathmin.y = telefragmin.y; \
-               if(deathmin.z > telefragmin.z) deathmin.z = telefragmin.z; \
-               if(deathmax.x < telefragmax.x) deathmax.x = telefragmax.x; \
-               if(deathmax.y < telefragmax.y) deathmax.y = telefragmax.y; \
-               if(deathmax.z < telefragmax.z) deathmax.z = telefragmax.z; \
-       } \
-       deathradius = max(vlen(deathmin), vlen(deathmax)); \
-       for(head = findradius(o, deathradius); head; head = head.chain) \
-               if(head != player) \
-                       if(head.takedamage) \
-                               if(boxesoverlap(deathmin, deathmax, head.absmin, head.absmax))
-
-
-float check_tdeath(entity player, vector org, vector telefragmin, vector telefragmax);
-float tdeath_hit;
-void tdeath(entity player, entity teleporter, entity telefragger, vector telefragmin, vector telefragmax);
-
-void spawn_tdeath(vector v0, entity e, vector v);
-
-.entity pusher;
-const float TELEPORT_FLAG_SOUND = 1;
-const float TELEPORT_FLAG_PARTICLES = 2;
-const float TELEPORT_FLAG_TDEATH = 4;
-const float TELEPORT_FLAG_FORCE_TDEATH = 8;
-
-#define TELEPORT_FLAGS_WARPZONE   0
-#define TELEPORT_FLAGS_PORTAL     (TELEPORT_FLAG_SOUND | TELEPORT_FLAG_PARTICLES | TELEPORT_FLAG_TDEATH | TELEPORT_FLAG_FORCE_TDEATH)
-#define TELEPORT_FLAGS_TELEPORTER (TELEPORT_FLAG_SOUND | TELEPORT_FLAG_PARTICLES | TELEPORT_FLAG_TDEATH)
-
-// types for .teleportable entity setting
-const float TELEPORT_NORMAL = 1; // play sounds/effects etc
-const float TELEPORT_SIMPLE = 2; // only do teleport, nothing special
-
-void Reset_ArcBeam(entity player, vector forward);
-void TeleportPlayer(entity teleporter, entity player, vector to, vector to_angles, vector to_velocity, vector telefragmin, vector telefragmax, float tflags);
-
-entity Simple_TeleportPlayer(entity teleporter, entity player);
-
-void Teleport_Touch (void);
-
-void spawnfunc_info_teleport_destination (void);
-
-void spawnfunc_misc_teleporter_dest (void);
-
-void spawnfunc_target_teleporter (void);
-
-void teleport_findtarget (void);
-
-entity Teleport_Find(vector mi, vector ma);
-
-entity teleport_first;
-.entity teleport_next;
-void spawnfunc_trigger_teleport (void);
-
-void WarpZone_PostTeleportPlayer_Callback(entity pl);
-#endif
diff --git a/qcsrc/server/target_spawn.qc b/qcsrc/server/target_spawn.qc
deleted file mode 100644 (file)
index 6ebb837..0000000
+++ /dev/null
@@ -1,363 +0,0 @@
-#include "_all.qh"
-
-.entity pusher;
-.float pushltime;
-
-// spawner entity
-// "classname" "target_spawn"
-// "message" "fieldname value fieldname value ..."
-// "spawnflags"
-//   1 = call the spawn function
-//   2 = trigger on map load
-
-float target_spawn_initialized;
-.void() target_spawn_spawnfunc;
-float target_spawn_spawnfunc_field;
-.entity target_spawn_activator;
-.float target_spawn_id;
-float target_spawn_count;
-
-void target_spawn_helper_setmodel()
-{
-       setmodel(self, self.model);
-}
-
-void target_spawn_helper_setsize()
-{
-       setsize(self, self.mins, self.maxs);
-}
-
-void target_spawn_edit_entity(entity e, string msg, entity kt, entity t2, entity t3, entity t4, entity act)
-{
-       float i, n, valuefieldpos;
-       string key, value, valuefield, valueoffset, valueoffsetrandom;
-       entity valueent;
-       vector data, data2;
-       entity oldself;
-       entity oldactivator;
-
-       n = tokenize_console(msg);
-
-       for(i = 0; i < n-1; i += 2)
-       {
-               key = argv(i);
-               value = argv(i+1);
-               if(key == "$")
-               {
-                       data.x = -1;
-                       data.y = FIELD_STRING;
-               }
-               else
-               {
-                       data = stov(db_get(TemporaryDB, strcat("/target_spawn/field/", key)));
-                       if(data.y == 0) // undefined field, i.e., invalid type
-                       {
-                               print("target_spawn: invalid/unknown entity key ", key, " specified, ignored!\n");
-                               continue;
-                       }
-               }
-               if(substring(value, 0, 1) == "$")
-               {
-                       value = substring(value, 1, strlen(value) - 1);
-                       if(substring(value, 0, 1) == "$")
-                       {
-                               // deferred replacement
-                               // do nothing
-                               // useful for creating target_spawns with this!
-                       }
-                       else
-                       {
-                               // replace me!
-                               valuefieldpos = strstrofs(value, "+", 0);
-                               valueoffset = "";
-                               if(valuefieldpos != -1)
-                               {
-                                       valueoffset = substring(value, valuefieldpos + 1, strlen(value) - valuefieldpos - 1);
-                                       value = substring(value, 0, valuefieldpos);
-                               }
-
-                               valuefieldpos = strstrofs(valueoffset, "+", 0);
-                               valueoffsetrandom = "";
-                               if(valuefieldpos != -1)
-                               {
-                                       valueoffsetrandom = substring(valueoffset, valuefieldpos + 1, strlen(valueoffset) - valuefieldpos - 1);
-                                       valueoffset = substring(valueoffset, 0, valuefieldpos);
-                               }
-
-                               valuefieldpos = strstrofs(value, ".", 0);
-                               valuefield = "";
-                               if(valuefieldpos != -1)
-                               {
-                                       valuefield = substring(value, valuefieldpos + 1, strlen(value) - valuefieldpos - 1);
-                                       value = substring(value, 0, valuefieldpos);
-                               }
-
-                               if(value == "self")
-                               {
-                                       valueent = self;
-                                       value = "";
-                               }
-                               else if(value == "activator")
-                               {
-                                       valueent = act;
-                                       value = "";
-                               }
-                               else if(value == "other")
-                               {
-                                       valueent = other;
-                                       value = "";
-                               }
-                               else if(value == "pusher")
-                               {
-                                       if(time < act.pushltime)
-                                               valueent = act.pusher;
-                                       else
-                                               valueent = world;
-                                       value = "";
-                               }
-                               else if(value == "target")
-                               {
-                                       valueent = e;
-                                       value = "";
-                               }
-                               else if(value == "killtarget")
-                               {
-                                       valueent = kt;
-                                       value = "";
-                               }
-                               else if(value == "target2")
-                               {
-                                       valueent = t2;
-                                       value = "";
-                               }
-                               else if(value == "target3")
-                               {
-                                       valueent = t3;
-                                       value = "";
-                               }
-                               else if(value == "target4")
-                               {
-                                       valueent = t4;
-                                       value = "";
-                               }
-                               else if(value == "time")
-                               {
-                                       valueent = world;
-                                       value = ftos(time);
-                               }
-                               else
-                               {
-                                       print("target_spawn: invalid/unknown variable replacement ", value, " specified, ignored!\n");
-                                       continue;
-                               }
-
-                               if(valuefield == "")
-                               {
-                                       if(value == "")
-                                               value = ftos(num_for_edict(valueent));
-                               }
-                               else
-                               {
-                                       if(value != "")
-                                       {
-                                               print("target_spawn: try to get a field of a non-entity, ignored!\n");
-                                               continue;
-                                       }
-                                       data2 = stov(db_get(TemporaryDB, strcat("/target_spawn/field/", valuefield)));
-                                       if(data2_y == 0) // undefined field, i.e., invalid type
-                                       {
-                                               print("target_spawn: invalid/unknown entity key replacement ", valuefield, " specified, ignored!\n");
-                                               continue;
-                                       }
-                                       value = getentityfieldstring(data2_x, valueent);
-                               }
-
-                               if(valueoffset != "")
-                               {
-                                       switch(data.y)
-                                       {
-                                               case FIELD_STRING:
-                                                       value = strcat(value, valueoffset);
-                                                       break;
-                                               case FIELD_FLOAT:
-                                                       value = ftos(stof(value) + stof(valueoffset));
-                                                       break;
-                                               case FIELD_VECTOR:
-                                                       value = vtos(stov(value) + stov(valueoffset));
-                                                       break;
-                                               default:
-                                                       print("target_spawn: only string, float and vector fields can do calculations, calculation ignored!\n");
-                                                       break;
-                                       }
-                               }
-
-                               if(valueoffsetrandom != "")
-                               {
-                                       switch(data.y)
-                                       {
-                                               case FIELD_FLOAT:
-                                                       value = ftos(stof(value) + random() * stof(valueoffsetrandom));
-                                                       break;
-                                               case FIELD_VECTOR:
-                                                       data2 = stov(valueoffsetrandom);
-                                                       value = vtos(stov(value) + random() * data2_x * '1 0 0' + random() * data2_y * '0 1 0' + random() * data2_z * '0 0 1');
-                                                       break;
-                                               default:
-                                                       print("target_spawn: only float and vector fields can do random calculations, calculation ignored!\n");
-                                                       break;
-                                       }
-                               }
-                       }
-               }
-               if(key == "$")
-               {
-                       if(substring(value, 0, 1) == "_")
-                               value = strcat("target_spawn_helper", value);
-                       putentityfieldstring(target_spawn_spawnfunc_field, e, value);
-
-                       oldself = self;
-                       oldactivator = activator;
-
-                       self = e;
-                       activator = act;
-
-                       self.target_spawn_spawnfunc();
-
-                       self = oldself;
-                       activator = oldactivator;
-
-                       // We called an external function, so we have to re-tokenize msg.
-                       n = tokenize_console(msg);
-               }
-               else
-               {
-                       if(data.y == FIELD_VECTOR)
-                               value = strreplace("'", "", value); // why?!?
-                       putentityfieldstring(data.x, e, value);
-               }
-       }
-}
-
-void target_spawn_useon(entity e)
-{
-       self.target_spawn_activator = activator;
-       target_spawn_edit_entity(
-               e,
-               self.message,
-               find(world, targetname, self.killtarget),
-               find(world, targetname, self.target2),
-               find(world, targetname, self.target3),
-               find(world, targetname, self.target4),
-               activator
-       );
-}
-
-float target_spawn_cancreate()
-{
-       float c;
-       entity e;
-
-       c = self.count;
-       if(c == 0) // no limit?
-               return 1;
-
-       ++c; // increase count to not include MYSELF
-       for(e = world; (e = findfloat(e, target_spawn_id, self.target_spawn_id)); --c)
-               ;
-
-       // if c now is 0, we have AT LEAST the given count (maybe more), so don't spawn any more
-       if(c == 0)
-               return 0;
-       return 1;
-}
-
-void target_spawn_use()
-{
-       entity e;
-
-       if(self.target == "")
-       {
-               // spawn new entity
-               if(!target_spawn_cancreate())
-                       return;
-               e = spawn();
-               target_spawn_useon(e);
-               e.target_spawn_id = self.target_spawn_id;
-       }
-       else if(self.target == "*activator")
-       {
-               // edit entity
-               if(activator)
-                       target_spawn_useon(activator);
-       }
-       else
-       {
-               // edit entity
-               for(e = world; (e = find(e, targetname, self.target)); )
-                       target_spawn_useon(e);
-       }
-}
-
-void target_spawn_spawnfirst()
-{
-       activator = self.target_spawn_activator;
-       if(self.spawnflags & 2)
-               target_spawn_use();
-}
-
-void initialize_field_db()
-{
-       if(!target_spawn_initialized)
-       {
-               float n, i;
-               string fn;
-               vector prev, next;
-               float ft;
-
-               n = numentityfields();
-               for(i = 0; i < n; ++i)
-               {
-                       fn = entityfieldname(i);
-                       ft = entityfieldtype(i);
-                       next = i * '1 0 0' + ft * '0 1 0' + '0 0 1';
-                       prev = stov(db_get(TemporaryDB, strcat("/target_spawn/field/", fn)));
-                       if(prev.y == 0)
-                       {
-                               db_put(TemporaryDB, strcat("/target_spawn/field/", fn), vtos(next));
-                               if(fn == "target_spawn_spawnfunc")
-                                       target_spawn_spawnfunc_field = i;
-                       }
-               }
-
-               target_spawn_initialized = 1;
-       }
-}
-
-void spawnfunc_target_spawn()
-{
-       initialize_field_db();
-       self.use = target_spawn_use;
-       self.message = strzone(strreplace("'", "\"", self.message));
-       self.target_spawn_id = ++target_spawn_count;
-       InitializeEntity(self, target_spawn_spawnfirst, INITPRIO_LAST);
-}
-
-
-void trigger_relay_if_use()
-{
-       float n;
-       n = self.count;
-
-       // TODO make this generic AND faster than nextent()ing through all, if somehow possible
-       n = (cvar_string(self.netname) == cvar_string(self.message));
-       if(self.spawnflags & 1)
-               n = !n;
-
-       if(n)
-               SUB_UseTargets();
-}
-
-void spawnfunc_trigger_relay_if()
-{
-       self.use = trigger_relay_if_use;
-}
index 98eca069e0cb943fbda8b667bfbe17c5b8e709e1..1885f67c173a756d88ecc19afc289408feb51fc9 100644 (file)
@@ -229,7 +229,6 @@ void turrets_precash()
 
 
 #ifdef TURRET_DEBUG
-void SUB_Remove();
 void marker_think()
 {
     if(self.cnt)
index 205cd0b5311eb8c15bc82e9d149cdc3ee507283e..0f2de3c869ab1a96fe43328c54f7f4627d018248 100644 (file)
@@ -1,5 +1,3 @@
-#include "../../g_subs.qh"
-
 void spawnfunc_turret_targettrigger();
 void turret_targettrigger_touch();
 
index 812fb22456628dc8c8e3b558c8cf0d7bdfbd1c6a..f70f7ab26724a6eaa794d9fd917fa718f8b27901 100644 (file)
@@ -2,7 +2,6 @@
 
 #include "../../movelib.qh"
 #include "../../steerlib.qh"
-#include "../../t_teleporters.qh"
 
 const float ANIM_NO         = 0;
 const float ANIM_TURN       = 1;
index 545e309e4fecc1720b76df3e8a13b317602e563c..8cecdfa84017ad25187fa9c55a7f5c6a3beaecd3 100644 (file)
@@ -2,6 +2,8 @@
 #include "racer.qh"
 
 #ifdef SVQC
+#include "../../common/triggers/trigger/impulse.qh"
+
 void racer_exit(float eject);
 void racer_enter();
 
index 696ea1a59fe2eadf896966fad727fcf20cc59474..59d9ef76d31c92c8fde90ca0f23c966a9639883f 100644 (file)
@@ -7,7 +7,6 @@
 #include "../cl_player.qh"
 #include "../g_damage.qh"
 #include "../g_hook.qh"
-#include "../g_subs.qh"
 #include "../movelib.qh"
 
 #include "../bot/bot.qh"
index 76e159738fbfc02d9f900bf2dd2c5d93714cc192..0564bdc76243dcb9674e1ac209957fa8b8f7838c 100644 (file)
@@ -2,7 +2,6 @@
 #include "../_all.qh"
 
 #include "../antilag.qh"
-#include "../g_subs.qh"
 #include "../../common/weapons/all.qh"
 
 vector W_HitPlotUnnormalizedUntransform(vector screenforward, vector screenright, vector screenup, vector v)
index 1fea5fb39318350f86b4ffeb13c40c5652ed9b54..d14bcfd1cfdec2d48aacdef8a572f0af9b0b0dd8 100644 (file)
@@ -5,7 +5,6 @@
 #include "../mutators/mutators_include.qh"
 #include "../t_items.qh"
 #include "../g_damage.qh"
-#include "../g_subs.qh"
 #include "../../common/mapinfo.qh"
 #include "../../common/notifications.qh"
 #include "../../common/util.qh"
index 028724afba3a69e2c1a61f7991660b7eb7ea1e49..706acde4448aef1d6bad8eb039570cbecabc6129 100644 (file)
@@ -573,6 +573,45 @@ vector WarpZoneLib_NearestPointOnBox(vector mi, vector ma, vector org)
        return nearest;
 }
 
+float WarpZoneLib_BadClassname(string myclassname)
+{
+       switch(myclassname)
+       {
+               case "weapon_info":
+               case "monster_info":
+               case "deathtype":
+               case "callback":
+               case "callbackchain":
+               case "weaponentity":
+               case "exteriorweaponentity":
+               case "csqc_score_team":
+               case "pingplreport":
+               case "ent_client_scoreinfo":
+               case "saved_cvar_value":
+               case "accuracy":
+               case "entcs_sender_v2":
+               case "entcs_receiver_v2":
+               case "clientinit":
+               case "sprite_waypoint":
+               case "waypoint":
+               case "gibsplash":
+               //case "net_linked": // actually some real entities are linked without classname, fail
+               case "":
+                       return true;
+       }
+
+       if(startsWith(myclassname, "msg_"))
+               return true;
+
+       if(startsWith(myclassname, "target_"))
+               return true;
+
+       if(startsWith(myclassname, "info_"))
+               return true;
+
+       return false;
+}
+
 .float WarpZone_findradius_hit;
 .entity WarpZone_findradius_next;
 void WarpZone_FindRadius_Recurse(vector org, float rad,        vector org0,               vector transform, vector shift, float needlineofsight)
@@ -591,6 +630,8 @@ void WarpZone_FindRadius_Recurse(vector org, float rad,        vector org0,
 
        for(e = e0; e; e = e.chain)
        {
+               if(WarpZoneLib_BadClassname(e.classname))
+                       continue;
                p = WarpZoneLib_NearestPointOnBox(e.origin + e.mins, e.origin + e.maxs, org0);
                if(needlineofsight)
                {
@@ -626,6 +667,9 @@ void WarpZone_FindRadius_Recurse(vector org, float rad,        vector org0,
        }
        for(e = wz; e; e = e.WarpZone_findradius_next)
        {
+               if(WarpZoneLib_BadClassname(e.classname))
+                       continue;
+
                org0_new = WarpZone_TransformOrigin(e, org);
                traceline(e.warpzone_targetorigin, org0_new, MOVE_NOMONSTERS, e);
                org_new = trace_endpos;
@@ -772,3 +816,64 @@ entity WarpZone_RefSys_SpawnSameRefSys(entity me)
        WarpZone_RefSys_Copy(e, me);
        return e;
 }
+
+float WarpZoneLib_ExactTrigger_Touch()
+{
+       return !WarpZoneLib_BoxTouchesBrush(other.absmin, other.absmax, self, other);
+}
+
+
+void WarpZoneLib_MoveOutOfSolid_Expand(entity e, vector by)
+{
+       float eps = 0.0625;
+       tracebox(e.origin, e.mins - '1 1 1' * eps, e.maxs + '1 1 1' * eps, e.origin + by, MOVE_WORLDONLY, e);
+       if (trace_startsolid)
+               return;
+       if (trace_fraction < 1)
+       {
+               // hit something
+               // adjust origin in the other direction...
+               setorigin(e,e.origin - by * (1 - trace_fraction));
+       }
+}
+
+float WarpZoneLib_MoveOutOfSolid(entity e)
+{
+       vector o, m0, m1;
+
+       o = e.origin;
+       traceline(o, o, MOVE_WORLDONLY, e);
+       if (trace_startsolid)
+               return false;
+
+       tracebox(o, e.mins, e.maxs, o, MOVE_WORLDONLY, e);
+       if (!trace_startsolid)
+               return true;
+
+       m0 = e.mins;
+       m1 = e.maxs;
+       e.mins = '0 0 0';
+       e.maxs = '0 0 0';
+       WarpZoneLib_MoveOutOfSolid_Expand(e, '1 0 0' * m0_x);
+       e.mins_x = m0_x;
+       WarpZoneLib_MoveOutOfSolid_Expand(e, '1 0 0' * m1_x);
+       e.maxs_x = m1_x;
+       WarpZoneLib_MoveOutOfSolid_Expand(e, '0 1 0' * m0_y);
+       e.mins_y = m0_y;
+       WarpZoneLib_MoveOutOfSolid_Expand(e, '0 1 0' * m1_y);
+       e.maxs_y = m1_y;
+       WarpZoneLib_MoveOutOfSolid_Expand(e, '0 0 1' * m0_z);
+       e.mins_z = m0_z;
+       WarpZoneLib_MoveOutOfSolid_Expand(e, '0 0 1' * m1_z);
+       e.maxs_z = m1_z;
+       setorigin(e, e.origin);
+
+       tracebox(e.origin, e.mins, e.maxs, e.origin, MOVE_WORLDONLY, e);
+       if (trace_startsolid)
+       {
+               setorigin(e, o);
+               return false;
+       }
+
+       return true;
+}
index 7742db8b9b101f54968503b3f5e238f993120faf..3e2b7c7c63eacae9b1dfd93c4dfc55feb77bef3c 100644 (file)
@@ -104,4 +104,13 @@ entity WarpZone_RefSys_SpawnSameRefSys(entity me); // spawn().R = me.R
 #ifndef BITXOR_ASSIGN
 # define BITXOR_ASSIGN(a,b) ((a) = ((a) | (b)) - ((a) & (b)))
 #endif
+float WarpZoneLib_MoveOutOfSolid(entity e);
+#define move_out_of_solid(e) WarpZoneLib_MoveOutOfSolid(e)
+
+float WarpZoneLib_ExactTrigger_Touch();
+void WarpZoneLib_ExactTrigger_Init();
+
+// WARNING: this kills the trace globals
+#define EXACTTRIGGER_TOUCH if(WarpZoneLib_ExactTrigger_Touch()) return
+#define EXACTTRIGGER_INIT  WarpZoneLib_ExactTrigger_Init()
 #endif
index bd6266ea0ca008f8141244638a2bd1ee4b6043e5..53d903902517cccf9eccc76f790c3a68ac5d25d6 100644 (file)
@@ -5,6 +5,7 @@
 #elif defined(MENUQC)
 #elif defined(SVQC)
        #include "../common/constants.qh"
+       #include "../common/triggers/subs.qh"
        #include "../common/util.qh"
        #include "../dpdefs/dpextensions.qh"
        #include "../dpdefs/progsdefs.qh"
index e82c4ba6e212b41a25714f457448382ca4b95cb8..0af6d3c0dcd44dacfb266a3f3ed9f400b0ee7bf4 100644 (file)
@@ -9,66 +9,7 @@
     #include "../dpdefs/dpextensions.qh"
        #include "../dpdefs/progsdefs.qh"
 #endif
-
-void WarpZoneLib_MoveOutOfSolid_Expand(entity e, vector by)
-{
-       float eps = 0.0625;
-       tracebox(e.origin, e.mins - '1 1 1' * eps, e.maxs + '1 1 1' * eps, e.origin + by, MOVE_WORLDONLY, e);
-       if (trace_startsolid)
-               return;
-       if (trace_fraction < 1)
-       {
-               // hit something
-               // adjust origin in the other direction...
-               setorigin(e,e.origin - by * (1 - trace_fraction));
-       }
-}
-
-float WarpZoneLib_MoveOutOfSolid(entity e)
-{
-       vector o, m0, m1;
-
-       o = e.origin;
-       traceline(o, o, MOVE_WORLDONLY, e);
-       if (trace_startsolid)
-               return false;
-
-       tracebox(o, e.mins, e.maxs, o, MOVE_WORLDONLY, e);
-       if (!trace_startsolid)
-               return true;
-
-       m0 = e.mins;
-       m1 = e.maxs;
-       e.mins = '0 0 0';
-       e.maxs = '0 0 0';
-       WarpZoneLib_MoveOutOfSolid_Expand(e, '1 0 0' * m0_x);
-       e.mins_x = m0_x;
-       WarpZoneLib_MoveOutOfSolid_Expand(e, '1 0 0' * m1_x);
-       e.maxs_x = m1_x;
-       WarpZoneLib_MoveOutOfSolid_Expand(e, '0 1 0' * m0_y);
-       e.mins_y = m0_y;
-       WarpZoneLib_MoveOutOfSolid_Expand(e, '0 1 0' * m1_y);
-       e.maxs_y = m1_y;
-       WarpZoneLib_MoveOutOfSolid_Expand(e, '0 0 1' * m0_z);
-       e.mins_z = m0_z;
-       WarpZoneLib_MoveOutOfSolid_Expand(e, '0 0 1' * m1_z);
-       e.maxs_z = m1_z;
-       setorigin(e, e.origin);
-
-       tracebox(e.origin, e.mins, e.maxs, e.origin, MOVE_WORLDONLY, e);
-       if (trace_startsolid)
-       {
-               setorigin(e, o);
-               return false;
-       }
-
-       return true;
-}
-
-float WarpZoneLib_ExactTrigger_Touch()
-{
-       return !WarpZoneLib_BoxTouchesBrush(other.absmin, other.absmax, self, other);
-}
+#include "common.qh"
 
 void WarpZoneLib_ExactTrigger_Init()
 {
index 38df7fe0cc804f55c344a368046b4e5adb428fe3..0a42d7d12c67227417356d86403ae37c1718d3b3 100644 (file)
@@ -3,5 +3,7 @@
 
 float WarpZoneLib_MoveOutOfSolid(entity e);
 float WarpZoneLib_ExactTrigger_Touch();
+#ifdef SVQC
 void WarpZoneLib_ExactTrigger_Init();
 #endif
+#endif
index 1364b61b8a8fbbc1cdf86f4ac3335f947bd1496d..57fcfc8d423e363f0d2055e1fbfc2db4fc714d8c 100644 (file)
@@ -131,6 +131,7 @@ Dale "graphitemaster" Weiler
 **Other Active Contributors
 Erik "Ablu" Schilling
 Mircea "Taoki" Kitsune
+Penguinum
 
 
 **Translators