Merge branch 'master' into Mario/qc_physics_prehax
authorMario <zacjardine@y7mail.com>
Wed, 15 Jul 2015 05:43:30 +0000 (15:43 +1000)
committerMario <zacjardine@y7mail.com>
Wed, 15 Jul 2015 05:43:30 +0000 (15:43 +1000)
Conflicts:
qcsrc/server/autocvars.qh
qcsrc/server/defs.qh
qcsrc/server/g_triggers.qc
qcsrc/server/g_triggers.qh

206 files changed:
defaultXonotic.cfg
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/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/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/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_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

index 756ab23..c0d585e 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 e03a6d4..c109e11 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 77926d0..6dadcf1 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 e2a3ef6..a3e42a9 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 0acb6e9..8597e9e 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 cac90b4..6cb0780 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 cd70955..5921bb4 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 f9c5895..8961af2 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 18fcfd1..ed8cb19 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 dbe999f..92cef15 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 39c422e..3626c44 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 99a9944..c0c378f 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 c617106..298e9d5 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 7648b12..982056f 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 f9e9705..845a564 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 6c64396..0c2be7f 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 e610711..e997d15 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 586f3f0..791313f 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 9df8372..1d67e17 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 8ba12b9..a9d0c56 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 ca631fc..3981b29 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 b74024d..58c031e 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 0af4d4b..ba92d7c 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 8430668..1304d3a 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 d3bcf92..ffdde41 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"
 
index d84fa46..04c0dce 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 aed0865..0b32de7 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 cd4f9b7..efa6509 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 9c998f3..939c6d7 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 fdc555e..5e5ff42 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 ce25475..239f8fe 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 4225a19..fcdc553 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 bc520e5..fb9958b 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 f888e1b..b488df1 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
+  &