Merge branch 'terencehill/connection_msg_fix' into 'master'
authorTimePath <andrew.hardaker1995@gmail.com>
Thu, 24 Dec 2015 00:56:28 +0000 (00:56 +0000)
committerTimePath <andrew.hardaker1995@gmail.com>
Thu, 24 Dec 2015 00:56:28 +0000 (00:56 +0000)
connection msg fix

See merge request !269

97 files changed:
qcsrc/Makefile
qcsrc/client/csqcmodel_hooks.qc
qcsrc/client/hud/hud.qh
qcsrc/client/main.qc
qcsrc/client/movelib.qc [deleted file]
qcsrc/client/movelib.qh [deleted file]
qcsrc/client/progs.inc
qcsrc/client/t_items.qc
qcsrc/client/view.qc
qcsrc/client/weapons/projectile.qc
qcsrc/common/_all.inc [new file with mode: 0644]
qcsrc/common/effects/all.qc
qcsrc/common/effects/qc/casings.qc
qcsrc/common/effects/qc/damageeffects.qc
qcsrc/common/effects/qc/gibs.qc
qcsrc/common/gamemodes/gamemode/onslaught/onslaught.qc
qcsrc/common/minigames/minigames.qc
qcsrc/common/monsters/all.qc
qcsrc/common/monsters/sv_monsters.qc
qcsrc/common/movetypes/follow.qc [deleted file]
qcsrc/common/movetypes/include.qc [deleted file]
qcsrc/common/movetypes/include.qh [deleted file]
qcsrc/common/movetypes/movetypes.qc [deleted file]
qcsrc/common/movetypes/movetypes.qh [deleted file]
qcsrc/common/movetypes/push.qc [deleted file]
qcsrc/common/movetypes/push.qh [deleted file]
qcsrc/common/movetypes/step.qc [deleted file]
qcsrc/common/movetypes/toss.qc [deleted file]
qcsrc/common/movetypes/toss.qh [deleted file]
qcsrc/common/movetypes/walk.qc [deleted file]
qcsrc/common/movetypes/walk.qh [deleted file]
qcsrc/common/mutators/mutator/buffs/buffs.qc
qcsrc/common/mutators/mutator/bugrigs/bugrigs.qc
qcsrc/common/mutators/mutator/dodging/dodging.qc
qcsrc/common/mutators/mutator/doublejump/doublejump.qc
qcsrc/common/mutators/mutator/multijump/multijump.qc
qcsrc/common/mutators/mutator/new_toys/new_toys.qc
qcsrc/common/mutators/mutator/nix/nix.qc
qcsrc/common/mutators/mutator/overkill/overkill.qc
qcsrc/common/mutators/mutator/pinata/pinata.qc
qcsrc/common/mutators/mutator/superspec/superspec.qc
qcsrc/common/physics.qc [deleted file]
qcsrc/common/physics.qh [deleted file]
qcsrc/common/physics/all.inc [new file with mode: 0644]
qcsrc/common/physics/movelib.qc [new file with mode: 0644]
qcsrc/common/physics/movelib.qh [new file with mode: 0644]
qcsrc/common/physics/movetypes/all.inc [new file with mode: 0644]
qcsrc/common/physics/movetypes/follow.qc [new file with mode: 0644]
qcsrc/common/physics/movetypes/movetypes.qc [new file with mode: 0644]
qcsrc/common/physics/movetypes/movetypes.qh [new file with mode: 0644]
qcsrc/common/physics/movetypes/push.qc [new file with mode: 0644]
qcsrc/common/physics/movetypes/step.qc [new file with mode: 0644]
qcsrc/common/physics/movetypes/toss.qc [new file with mode: 0644]
qcsrc/common/physics/movetypes/walk.qc [new file with mode: 0644]
qcsrc/common/physics/player.qc [new file with mode: 0644]
qcsrc/common/physics/player.qh [new file with mode: 0644]
qcsrc/common/stats.qh
qcsrc/common/triggers/func/breakable.qc
qcsrc/common/triggers/trigger/jumppads.qc
qcsrc/common/turrets/all.qc
qcsrc/common/turrets/turret/ewheel.qc
qcsrc/common/turrets/turret/walker.qc
qcsrc/common/vehicles/sv_vehicles.qh
qcsrc/common/vehicles/vehicle/spiderbot.qc
qcsrc/common/weapons/all.qc
qcsrc/lib/arraylist.qh
qcsrc/lib/csqcmodel/cl_player.qc
qcsrc/lib/iter.qh
qcsrc/lib/net.qh
qcsrc/menu/progs.inc
qcsrc/menu/xonotic/dialog_multiplayer_create_mutators.qc
qcsrc/server/_all.qh
qcsrc/server/anticheat.qc
qcsrc/server/bot/havocbot/havocbot.qc
qcsrc/server/cheats.qc
qcsrc/server/cl_client.qc
qcsrc/server/cl_player.qc
qcsrc/server/cl_player.qh
qcsrc/server/command/cmd.qc
qcsrc/server/command/common.qc
qcsrc/server/defs.qh
qcsrc/server/g_damage.qc
qcsrc/server/g_subs.qc
qcsrc/server/g_world.qc
qcsrc/server/miscfunctions.qc
qcsrc/server/miscfunctions.qh
qcsrc/server/movelib.qc [deleted file]
qcsrc/server/movelib.qh [deleted file]
qcsrc/server/progs.inc
qcsrc/server/scores.qc
qcsrc/server/sv_main.qc
qcsrc/server/t_items.qc
qcsrc/server/teamplay.qc
qcsrc/server/weapons/spawning.qc
qcsrc/server/weapons/throwing.qc
qcsrc/server/weapons/weaponsystem.qc
qcsrc/test/compilationunit.sh

index bea2993..fd566d7 100644 (file)
@@ -6,17 +6,14 @@ NDEBUG ?= 1
  
 QCCVERSIONFILE := qccversion.$(shell (cd server && $(QCC) --version) > qccversion.txt && git hash-object qccversion.txt)
 
-# We eventually need to get rid of these.
+# We eventually need to get rid of these
 QCCFLAGS_WTFS ?= \
        -Wno-field-redeclared
 
 QCCFLAGS_FEATURES ?= \
-       -DVEHICLES_ENABLED=1 \
-       -DVEHICLES_USE_ODE=0 \
        -DBUILD_MOD=$(BUILD_MOD)
 
 # -Ooverlap-locals is required
-
 QCCFLAGS ?= \
        -std=gmqcc \
        -Ooverlap-locals \
index 83c3f44..7ebc11f 100644 (file)
@@ -2,7 +2,7 @@
 #include "player_skeleton.qh"
 #include "weapons/projectile.qh"
 #include "../common/animdecide.qh"
-#include "../common/movetypes/movetypes.qh"
+#include "../common/physics/movetypes/movetypes.qh"
 #include "../common/viewloc.qh"
 #include "../lib/csqcmodel/cl_model.qh"
 #include "../lib/csqcmodel/cl_player.qh"
index 47126a7..0526fcc 100644 (file)
@@ -70,7 +70,6 @@ float scoreboard_bottom;
 int weapon_accuracy[Weapons_MAX];
 
 int complain_weapon;
-string complain_weapon_name;
 float complain_weapon_type;
 float complain_weapon_time;
 
index 05a1108..5fc5374 100644 (file)
@@ -1099,8 +1099,6 @@ NET_HANDLE(TE_CSQC_PINGPLREPORT, bool isNew)
 NET_HANDLE(TE_CSQC_WEAPONCOMPLAIN, bool isNew)
 {
        complain_weapon = ReadByte();
-       if (complain_weapon_name) strunzone(complain_weapon_name);
-       complain_weapon_name = strzone(Weapons_from(complain_weapon).m_name);
        complain_weapon_type = ReadByte();
        return = true;
 
diff --git a/qcsrc/client/movelib.qc b/qcsrc/client/movelib.qc
deleted file mode 100644 (file)
index 074f146..0000000
+++ /dev/null
@@ -1 +0,0 @@
-#include "../server/movelib.qc"
diff --git a/qcsrc/client/movelib.qh b/qcsrc/client/movelib.qh
deleted file mode 100644 (file)
index a0634f6..0000000
+++ /dev/null
@@ -1 +0,0 @@
-#include "../server/movelib.qh"
index 2727db4..70bf45e 100644 (file)
@@ -11,7 +11,6 @@
 #include "main.qc"
 #include "mapvoting.qc"
 #include "miscfunctions.qc"
-#include "movelib.qc"
 #include "player_skeleton.qc"
 #include "scoreboard.qc"
 #include "shownames.qc"
 
 #include "weapons/projectile.qc" // TODO
 
-#include "../common/anim.qc"
-#include "../common/animdecide.qc"
-#include "../common/effects/effectinfo.qc"
-#include "../common/ent_cs.qc"
-#include "../common/mapinfo.qc"
-#include "../common/movetypes/include.qc"
-#include "../common/net_notice.qc"
-#include "../common/notifications.qc"
-#include "../common/physics.qc"
-#include "../common/playerstats.qc"
-#include "../common/util.qc"
-
-#include "../common/viewloc.qc"
-
-#include "../common/minigames/minigames.qc"
-#include "../common/minigames/cl_minigames.qc"
-
-#include "../common/deathtypes/all.qc"
-#include "../common/effects/all.qc"
-#include "../common/gamemodes/all.qc"
-#include "../common/impulses/all.qc"
-#include "../common/items/all.qc"
-#include "../common/monsters/all.qc"
-#include "../common/mutators/all.qc"
-#include "../common/turrets/all.qc"
-#include "../common/vehicles/all.qc"
-#include "../common/weapons/all.qc"
-
-#include "../common/turrets/cl_turrets.qc"
-
-#include "../common/triggers/include.qc"
+#include "../common/_all.inc"
 
 #include "../lib/csqcmodel/cl_model.qc"
 #include "../lib/csqcmodel/cl_player.qc"
index b0e1315..097f546 100644 (file)
@@ -1,5 +1,5 @@
 
-#include "../common/movetypes/movetypes.qh"
+#include "../common/physics/movetypes/movetypes.qh"
 #include "../common/weapons/all.qh"
 #include "../lib/csqcmodel/cl_model.qh"
 #include "../lib/csqcmodel/common.qh"
index 8350523..cdeb247 100644 (file)
@@ -14,7 +14,7 @@
 #include "../common/debug.qh"
 #include "../common/mapinfo.qh"
 #include "../common/gamemodes/all.qh"
-#include "../common/physics.qh"
+#include "../common/physics/player.qh"
 #include "../common/stats.qh"
 #include "../common/triggers/target/music.qh"
 #include "../common/teams.qh"
index 04d7284..48f02c4 100644 (file)
@@ -6,7 +6,7 @@
 #include "../mutators/events.qh"
 
 #include "../../common/constants.qh"
-#include "../../common/movetypes/movetypes.qh"
+#include "../../common/physics/movetypes/movetypes.qh"
 
 #include "../../lib/csqcmodel/interpolate.qh"
 
diff --git a/qcsrc/common/_all.inc b/qcsrc/common/_all.inc
new file mode 100644 (file)
index 0000000..a272029
--- /dev/null
@@ -0,0 +1,43 @@
+#ifndef MENUQC
+#include "anim.qc"
+#include "animdecide.qc"
+#include "ent_cs.qc"
+#include "net_notice.qc"
+#endif
+
+#include "mapinfo.qc"
+#include "playerstats.qc"
+#include "util.qc"
+
+#ifndef CSQC
+#include "campaign_file.qc"
+#include "campaign_setup.qc"
+#endif
+
+#ifndef MENUQC
+#include "physics/all.inc"
+#include "triggers/include.qc"
+#include "viewloc.qc"
+#endif
+
+#ifndef MENUQC
+#include "minigames/minigames.qc"
+#endif
+
+#include "debug.qh"
+
+#ifndef MENUQC
+#include "deathtypes/all.qc"
+#include "effects/all.qc"
+#include "impulses/all.qc"
+#include "notifications.qc"
+#endif
+
+#include "items/all.qc"
+    #include "weapons/all.qc"
+        #include "monsters/all.qc"
+        #include "turrets/all.qc"
+        #include "vehicles/all.qc"
+
+#include "mutators/all.qc"
+    #include "gamemodes/all.qc"
index 347a211..8c13a68 100644 (file)
@@ -91,3 +91,5 @@ void Send_Effect_(string eff_name, vector eff_loc, vector eff_vel, int eff_cnt)
        __pointparticles(_particleeffectnum(eff_name), eff_loc, eff_vel, eff_cnt);
 }
 #endif
+
+#include "effectinfo.qc"
index 7393217..5d8a87d 100644 (file)
@@ -3,7 +3,7 @@
 #include "../../util.qh"
 
 #ifdef CSQC
-#include "../../movetypes/movetypes.qh"
+#include "../../physics/movetypes/movetypes.qh"
 #include "rubble.qh"
 #endif
 
index e77f63f..2de4ef1 100644 (file)
@@ -3,7 +3,7 @@
 
 #ifdef CSQC
 #include "../../deathtypes/all.qh"
-#include "../../movetypes/movetypes.qh"
+#include "../../physics/movetypes/movetypes.qh"
 #include "../../../client/mutators/events.qh"
 #include "../../vehicles/all.qh"
 #include "../../weapons/all.qh"
index 19f4750..2e24809 100644 (file)
@@ -59,7 +59,7 @@ void Violence_GibSplash(entity source, float type, float amount, entity attacker
 .bool silent;
 
 #include "rubble.qh"
-#include "../common/movetypes/movetypes.qh"
+#include "../common/physics/movetypes/movetypes.qh"
 
 .float scale;
 .float alpha;
index 9cafd1a..485fc1b 100644 (file)
@@ -1330,7 +1330,6 @@ void havocbot_goalrating_ons_offenseitems(float ratingscale, vector org, float s
 {SELFPARAM();
        entity head;
        float t, c;
-       int i;
        bool needarmor = false, needweapons = false;
 
        // Needs armor/health?
@@ -1339,13 +1338,11 @@ void havocbot_goalrating_ons_offenseitems(float ratingscale, vector org, float s
 
        // Needs weapons?
        c = 0;
-       for(i = WEP_FIRST; i <= WEP_LAST ; ++i)
-       {
-               // Find weapon
-               if(self.weapons & WepSet_FromWeapon(Weapons_from(i)))
-               if(++c>=4)
+       FOREACH(Weapons, it != WEP_Null, LAMBDA(
+               if(self.weapons & (it.m_wepset))
+               if(++c >= 4)
                        break;
-       }
+       ));
 
        if(c<4)
                needweapons = true;
index 8a29bc2..2a63233 100644 (file)
@@ -138,3 +138,10 @@ int minigame_count_players(entity minigame)
                pl_num++;
        return pl_num;
 }
+
+#ifdef CSQC
+#include "cl_minigames.qc"
+#endif
+#ifdef SVQC
+#include "sv_minigames.qc"
+#endif
index f5c973b..2b15e40 100644 (file)
@@ -18,4 +18,9 @@ string M_Model(string m_mdl)
 #include "all.inc"
 #undef IMPLEMENTATION
 
+#ifdef SVQC
+#include "spawn.qc"
+#include "sv_monsters.qc"
+#endif
+
 #endif
index e949e3a..27e1246 100644 (file)
@@ -7,6 +7,7 @@
     #include "../util.qh"
     #include "all.qh"
     #include "sv_monsters.qh"
+       #include "../physics/movelib.qh"
     #include "../weapons/all.qh"
     #include "../../server/autocvars.qh"
     #include "../../server/defs.qh"
@@ -710,7 +711,7 @@ void Monster_Move(float runspeed, float walkspeed, float stpspeed)
                if(!(self.spawnflags & MONSTERFLAG_INVINCIBLE) && self.sprite)
                        WaypointSprite_UpdateHealth(self.sprite, self.health);
 
-               movelib_beak_simple(stpspeed);
+               movelib_brake_simple(stpspeed);
                setanim(self, self.anim_idle, true, false, false);
 
                self.enemy = world;
@@ -729,7 +730,7 @@ void Monster_Move(float runspeed, float walkspeed, float stpspeed)
                if(!(self.spawnflags & MONSTERFLAG_INVINCIBLE) && self.sprite)
                        WaypointSprite_UpdateHealth(self.sprite, self.health);
 
-               movelib_beak_simple(stpspeed);
+               movelib_brake_simple(stpspeed);
                setanim(self, self.anim_idle, true, false, false);
 
                self.enemy = world;
@@ -798,7 +799,7 @@ void Monster_Move(float runspeed, float walkspeed, float stpspeed)
                runspeed = walkspeed = 0;
                if(time >= self.spawn_time)
                        setanim(self, self.anim_idle, true, false, false);
-               movelib_beak_simple(stpspeed);
+               movelib_brake_simple(stpspeed);
                return;
        }
 
@@ -879,7 +880,7 @@ void Monster_Move(float runspeed, float walkspeed, float stpspeed)
                else if(e.target)
                        self.target2 = e.target;
 
-               movelib_beak_simple(stpspeed);
+               movelib_brake_simple(stpspeed);
                if(time > self.anim_finished)
                if(time > self.pain_finished)
                if(!self.state)
@@ -1129,7 +1130,7 @@ void Monster_Move_2D(float mspeed, float allow_jumpoff)
                mspeed = 0;
                if(time >= self.spawn_time)
                        setanim(self, self.anim_idle, true, false, false);
-               movelib_beak_simple(0.6);
+               movelib_brake_simple(0.6);
                return;
        }
 
diff --git a/qcsrc/common/movetypes/follow.qc b/qcsrc/common/movetypes/follow.qc
deleted file mode 100644 (file)
index 2d3e24f..0000000
+++ /dev/null
@@ -1,31 +0,0 @@
-void _Movetype_Physics_Follow(entity this) // SV_Physics_Follow
-{
-       entity e = this.move_aiment; // TODO: networking?
-
-       // LordHavoc: implemented rotation on MOVETYPE_FOLLOW objects
-       if(this.move_angles == this.move_punchangle)
-       {
-               this.move_origin = e.move_origin + this.view_ofs;
-       }
-       else
-       {
-               vector ang, v;
-               ang_x = -this.move_punchangle_x;
-               ang_y = this.move_punchangle_y;
-               ang_z = this.move_punchangle_z;
-               makevectors(ang);
-               v_x = this.view_ofs_x * v_forward_x + this.view_ofs_y * v_right_x + this.view_ofs_z * v_up_x;
-               v_y = this.view_ofs_x * v_forward_y + this.view_ofs_y * v_right_y + this.view_ofs_z * v_up_y;
-               v_z = this.view_ofs_x * v_forward_z + this.view_ofs_y * v_right_z + this.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);
-               this.move_origin_x = v_x * v_forward_x + v_y * v_forward_y + v_z * v_forward_z + e.move_origin_x;
-               this.move_origin_x = v_x * v_right_x + v_y * v_right_y + v_z * v_right_z + e.move_origin_y;
-               this.move_origin_x = v_x * v_up_x + v_y * v_up_y + v_z * v_up_z + e.move_origin_z;
-       }
-
-       this.move_angles = e.move_angles + this.v_angle;
-       _Movetype_LinkEdict(this, false);
-}
diff --git a/qcsrc/common/movetypes/include.qc b/qcsrc/common/movetypes/include.qc
deleted file mode 100644 (file)
index 322b3c4..0000000
+++ /dev/null
@@ -1,7 +0,0 @@
-#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
deleted file mode 100644 (file)
index a96e595..0000000
+++ /dev/null
@@ -1,8 +0,0 @@
-#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
deleted file mode 100644 (file)
index fc9396b..0000000
+++ /dev/null
@@ -1,717 +0,0 @@
-#include "include.qh"
-#include "../physics.qh"
-
-#if defined(CSQC)
-       #include "../../client/defs.qh"
-       #include "../stats.qh"
-       #include "../util.qh"
-       #include "movetypes.qh"
-       #include "../../lib/csqcmodel/common.qh"
-       #include "../../server/t_items.qh"
-#elif defined(MENUQC)
-#elif defined(SVQC)
-       #include "../../server/autocvars.qh"
-#endif
-
-void _Movetype_WallFriction(entity this, vector stepnormal)  // SV_WallFriction
-{
-       /*float d, i;
-       vector into, side;
-       makevectors(this.v_angle);
-       d = (stepnormal * v_forward) + 0.5;
-
-       if(d < 0)
-       {
-           i = (stepnormal * this.move_velocity);
-           into = i * stepnormal;
-           side = this.move_velocity - into;
-           this.move_velocity_x = side.x * (1 * d);
-           this.move_velocity_y = side.y * (1 * d);
-       }*/
-}
-
-vector planes[MAX_CLIP_PLANES];
-int _Movetype_FlyMove(entity this, 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, restore_velocity;
-
-       for(i = 0; i < MAX_CLIP_PLANES; ++i)
-               planes[i] = '0 0 0';
-
-       if(applygravity)
-       {
-               this.move_didgravity = 1;
-               grav = dt * (PHYS_ENTGRAVITY(this) ? PHYS_ENTGRAVITY(this) : 1) * PHYS_GRAVITY(this);
-
-               if(!GAMEPLAYFIX_NOGRAVITYONGROUND || !(this.move_flags & FL_ONGROUND))
-               {
-                       if(GAMEPLAYFIX_GRAVITYUNAFFECTEDBYTICRATE)
-                               this.move_velocity_z -= grav * 0.5;
-                       else
-                               this.move_velocity_z -= grav;
-               }
-       }
-
-       original_velocity = primal_velocity = restore_velocity = this.move_velocity;
-
-       for(bumpcount = 0;bumpcount < MAX_CLIP_PLANES;bumpcount++)
-       {
-               if(this.move_velocity == '0 0 0')
-                       break;
-
-               push = this.move_velocity * time_left;
-               _Movetype_PushEntity(this, push, true);
-               if(trace_startsolid)
-               {
-                       // 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)
-               {
-                       this.move_velocity = restore_velocity;
-                       return 3;
-               }
-
-               if(trace_fraction == 1)
-                       break;
-
-               float my_trace_fraction = trace_fraction;
-               vector my_trace_plane_normal = trace_plane_normal;
-
-               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;
-                               }
-
-                               this.move_flags |= FL_ONGROUND;
-                               this.move_groundentity = trace_ent;
-                       }
-               }
-               else if(stepheight)
-               {
-                       // step - handle it immediately
-                       vector org = this.move_origin;
-                       vector steppush = '0 0 1' * stepheight;
-
-                       _Movetype_PushEntity(this, steppush, true);
-                       if(trace_startsolid)
-                       {
-                               blocked |= 8;
-                               break;
-                       }
-                       _Movetype_PushEntity(this, push, true);
-                       if(trace_startsolid)
-                       {
-                               blocked |= 8;
-                               break;
-                       }
-                       float trace2_fraction = trace_fraction;
-                       steppush = '0 0 1' * (org_z - this.move_origin_z);
-                       _Movetype_PushEntity(this, steppush, true);
-                       if(trace_startsolid)
-                       {
-                               blocked |= 8;
-                               break;
-                       }
-
-                       // accept the new position if it made some progress...
-                       if(fabs(this.move_origin_x - org_x) >= 0.03125 || fabs(this.move_origin_y - org_y) >= 0.03125)
-                       {
-                               trace_endpos = this.move_origin;
-                               time_left *= 1 - trace2_fraction;
-                               numplanes = 0;
-                               continue;
-                       }
-                       else
-                               this.move_origin = org;
-               }
-               else
-               {
-                       // step - return it to caller
-                       blocked |= 2;
-                       // save the trace for player extrafriction
-                       if(stepnormal)
-                               stepnormal = trace_plane_normal;
-               }
-
-               if(my_trace_fraction >= 0.001)
-               {
-                       // actually covered some distance
-                       original_velocity = this.move_velocity;
-                       numplanes = 0;
-               }
-
-               time_left *= 1 - my_trace_fraction;
-
-               // clipped to another plane
-               if(numplanes >= MAX_CLIP_PLANES)
-               {
-                       // this shouldn't really happen
-                       this.move_velocity = '0 0 0';
-                       blocked = 3;
-                       break;
-               }
-
-               planes[numplanes] = my_trace_plane_normal;
-               numplanes++;
-
-               // modify original_velocity so it parallels all of the clip planes
-               vector new_velocity = '0 0 0';
-               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
-                       this.move_velocity = new_velocity;
-               }
-               else
-               {
-                       // go along the crease
-                       if(numplanes != 2)
-                       {
-                               this.move_velocity = '0 0 0';
-                               blocked = 7;
-                               break;
-                       }
-                       vector dir = cross(planes[0], planes[1]);
-                       // 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 * this.move_velocity);
-                       this.move_velocity = dir * d;
-               }
-
-               // if current velocity is against the original velocity,
-               // stop dead to avoid tiny occilations in sloping corners
-               if((this.move_velocity * primal_velocity) <= 0)
-               {
-                       this.move_velocity = '0 0 0';
-                       break;
-               }
-       }
-
-       // LordHavoc: this came from QW and allows you to get out of water more easily
-       if(GAMEPLAYFIX_EASIERWATERJUMP(this) && (this.move_flags & FL_WATERJUMP) && !(blocked & 8))
-               this.move_velocity = primal_velocity;
-
-       if(applygravity)
-       {
-               if(!GAMEPLAYFIX_NOGRAVITYONGROUND || !(this.move_flags & FL_ONGROUND))
-               {
-                       if(GAMEPLAYFIX_GRAVITYUNAFFECTEDBYTICRATE)
-                               this.move_velocity_z -= grav * 0.5f;
-               }
-       }
-
-       return blocked;
-}
-
-void _Movetype_CheckVelocity(entity this)  // SV_CheckVelocity
-{
-       // if(vlen(this.move_velocity) < 0.0001)
-       // this.move_velocity = '0 0 0';
-}
-
-bool _Movetype_CheckWater(entity this)  // SV_CheckWater
-{
-       vector point = this.move_origin;
-       point.z += this.mins.z + 1;
-
-       int nativecontents = pointcontents(point);
-       if(this.move_watertype && this.move_watertype != nativecontents)
-       {
-               // dprintf("_Movetype_CheckWater(): Original: '%d', New: '%d'\n", this.move_watertype, nativecontents);
-               if(this.contentstransition)
-                       this.contentstransition(this.move_watertype, nativecontents);
-       }
-
-       this.move_waterlevel = WATERLEVEL_NONE;
-       this.move_watertype = CONTENT_EMPTY;
-
-       int supercontents = Mod_Q1BSP_SuperContentsFromNativeContents(nativecontents);
-       if(supercontents & DPCONTENTS_LIQUIDSMASK)
-       {
-               this.move_watertype = nativecontents;
-               this.move_waterlevel = WATERLEVEL_WETFEET;
-               point.z = this.move_origin.z + (this.mins.z + this.maxs.z) * 0.5;
-               if(Mod_Q1BSP_SuperContentsFromNativeContents(pointcontents(point)) & DPCONTENTS_LIQUIDSMASK)
-               {
-                       this.move_waterlevel = WATERLEVEL_SWIMMING;
-                       point.z = this.move_origin.z + this.view_ofs.z;
-                       if(Mod_Q1BSP_SuperContentsFromNativeContents(pointcontents(point)) & DPCONTENTS_LIQUIDSMASK)
-                               this.move_waterlevel = WATERLEVEL_SUBMERGED;
-               }
-       }
-
-       return this.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 this, entity oth)  // SV_Impact
-{
-       entity oldother = other;
-
-       if(this.move_touch)
-       {
-               other = oth;
-
-               WITH(entity, self, this, this.move_touch());
-
-               other = oldother;
-       }
-
-       if(oth.move_touch)
-       {
-               other = this;
-
-               WITH(entity, self, oth, oth.move_touch());
-
-               other = oldother;
-       }
-}
-
-void _Movetype_LinkEdict_TouchAreaGrid(entity this)  // SV_LinkEdict_TouchAreaGrid
-{
-       entity oldother = other;
-
-       for (entity e = findradius(0.5 * (this.absmin + this.absmax), 0.5 * vlen(this.absmax - this.absmin)); e; e = e.chain)
-       {
-               if(e.move_nomonsters != MOVE_NOMONSTERS && e.move_nomonsters != MOVE_WORLDONLY)
-               if(e.move_touch && boxesoverlap(e.absmin, e.absmax, this.absmin, this.absmax))
-               {
-                       other = this;
-
-                       trace_allsolid = false;
-                       trace_startsolid = false;
-                       trace_fraction = 1;
-                       trace_inwater = false;
-                       trace_inopen = true;
-                       trace_endpos = e.move_origin;
-                       trace_plane_normal = '0 0 1';
-                       trace_plane_dist = 0;
-                       trace_ent = this;
-
-                       WITH(entity, self, e, e.move_touch());
-               }
-       }
-
-       other = oldother;
-}
-
-void _Movetype_LinkEdict(entity this, bool touch_triggers)  // SV_LinkEdict
-{
-       vector mi, ma;
-       if(this.solid == SOLID_BSP)
-       {
-               // TODO set the absolute bbox
-               mi = this.mins;
-               ma = this.maxs;
-       }
-       else
-       {
-               mi = this.mins;
-               ma = this.maxs;
-       }
-       mi += this.move_origin;
-       ma += this.move_origin;
-
-       if(this.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;
-       }
-
-       this.absmin = mi;
-       this.absmax = ma;
-
-       if(touch_triggers)
-               _Movetype_LinkEdict_TouchAreaGrid(this);
-}
-
-bool _Movetype_TestEntityPosition(entity this, vector ofs)  // SV_TestEntityPosition
-{
-//     vector org = this.move_origin + ofs;
-
-       int cont = this.dphitcontentsmask;
-       this.dphitcontentsmask = DPCONTENTS_SOLID;
-       tracebox(this.move_origin, this.mins, this.maxs, this.move_origin, ((this.move_movetype == MOVETYPE_FLY_WORLDONLY) ? MOVE_WORLDONLY : MOVE_NOMONSTERS), this);
-       this.dphitcontentsmask = cont;
-
-       if(trace_startsolid)
-               return true;
-
-       if(vlen(trace_endpos - this.move_origin) > 0.0001)
-               this.move_origin = trace_endpos;
-       return false;
-}
-
-bool _Movetype_UnstickEntity(entity this)  // SV_UnstickEntity
-{
-       if(!_Movetype_TestEntityPosition(this, '0 0 0')) return true;
-       if(!_Movetype_TestEntityPosition(this, '-1 0 0')) goto success;
-       if(!_Movetype_TestEntityPosition(this, '1 0 0')) goto success;
-       if(!_Movetype_TestEntityPosition(this, '0 -1 0')) goto success;
-       if(!_Movetype_TestEntityPosition(this, '0 1 0')) goto success;
-       if(!_Movetype_TestEntityPosition(this, '-1 -1 0')) goto success;
-       if(!_Movetype_TestEntityPosition(this, '1 -1 0')) goto success;
-       if(!_Movetype_TestEntityPosition(this, '-1 1 0')) goto success;
-       if(!_Movetype_TestEntityPosition(this, '1 1 0')) goto success;
-       for (int i = 1; i <= 17; ++i)
-       {
-               if(!_Movetype_TestEntityPosition(this, '0 0 -1' * i)) goto success;
-               if(!_Movetype_TestEntityPosition(this, '0 0 1' * i)) goto success;
-       }
-       LOG_DEBUGF("Can't unstick an entity (edict: %d, classname: %s, origin: %s)\n",
-               etof(this), this.classname, vtos(this.move_origin));
-       return false;
-       : success;
-       LOG_DEBUGF("Sucessfully unstuck an entity (edict: %d, classname: %s, origin: %s)\n",
-               etof(this), this.classname, vtos(this.move_origin));
-       _Movetype_LinkEdict(this, 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(entity this, vector push)
-{
-       vector end = this.move_origin + push;
-       int type;
-       if(this.move_nomonsters)
-               type = max(0, this.move_nomonsters);
-       else if(this.move_movetype == MOVETYPE_FLYMISSILE)
-               type = MOVE_MISSILE;
-       else if(this.move_movetype == MOVETYPE_FLY_WORLDONLY)
-               type = MOVE_WORLDONLY;
-       else if(this.solid == SOLID_TRIGGER || this.solid == SOLID_NOT)
-               type = MOVE_NOMONSTERS;
-       else
-               type = MOVE_NORMAL;
-
-       tracebox(this.move_origin, this.mins, this.maxs, end, type, this);
-}
-
-float _Movetype_PushEntity(entity this, vector push, bool failonstartsolid)  // SV_PushEntity
-{
-       _Movetype_PushEntityTrace(this, push);
-
-       if(trace_startsolid && failonstartsolid)
-               return trace_fraction;
-
-       this.move_origin = trace_endpos;
-
-       if(trace_fraction < 1)
-               if(this.solid >= SOLID_TRIGGER && (!(this.move_flags & FL_ONGROUND) || (this.move_groundentity != trace_ent)))
-                       _Movetype_Impact(this, 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(entity this, float movedt)
-{
-       this.move_didgravity = -1;
-       switch (this.move_movetype)
-       {
-               case MOVETYPE_PUSH:
-               case MOVETYPE_FAKEPUSH:
-                       _Movetype_Physics_Pusher(this, movedt);
-                       break;
-               case MOVETYPE_NONE:
-                       break;
-               case MOVETYPE_FOLLOW:
-                       _Movetype_Physics_Follow(this);
-                       break;
-               case MOVETYPE_NOCLIP:
-                       _Movetype_CheckWater(this);
-                       this.move_origin = this.move_origin + TICRATE * this.move_velocity;
-                       this.move_angles = this.move_angles + TICRATE * this.move_avelocity;
-                       _Movetype_LinkEdict(this, false);
-                       break;
-               case MOVETYPE_STEP:
-                       _Movetype_Physics_Step(this, movedt);
-                       break;
-               case MOVETYPE_WALK:
-                       _Movetype_Physics_Walk(this, movedt);
-                       break;
-               case MOVETYPE_TOSS:
-               case MOVETYPE_BOUNCE:
-               case MOVETYPE_BOUNCEMISSILE:
-               case MOVETYPE_FLYMISSILE:
-               case MOVETYPE_FLY:
-               case MOVETYPE_FLY_WORLDONLY:
-                       _Movetype_Physics_Toss(this, movedt);
-                       _Movetype_LinkEdict(this, true);
-                       break;
-               case MOVETYPE_PHYSICS:
-                       break;
-       }
-}
-
-void _Movetype_Physics_ClientFrame(entity this, float movedt)
-{
-       this.move_didgravity = -1;
-       switch (this.move_movetype)
-       {
-               case MOVETYPE_PUSH:
-               case MOVETYPE_FAKEPUSH:
-                       _Movetype_Physics_Pusher(this, movedt);
-                       break;
-               case MOVETYPE_NONE:
-                       break;
-               case MOVETYPE_FOLLOW:
-                       _Movetype_Physics_Follow(this);
-                       break;
-               case MOVETYPE_NOCLIP:
-                       _Movetype_CheckWater(this);
-                       this.move_origin = this.move_origin + TICRATE * this.move_velocity;
-                       this.move_angles = this.move_angles + TICRATE * this.move_avelocity;
-                       _Movetype_LinkEdict(this, false);
-                       break;
-               case MOVETYPE_STEP:
-                       _Movetype_Physics_Step(this, movedt);
-                       break;
-               case MOVETYPE_WALK:
-               case MOVETYPE_FLY:
-               case MOVETYPE_FLY_WORLDONLY:
-                       _Movetype_Physics_Walk(this, movedt);
-                       break;
-               case MOVETYPE_TOSS:
-               case MOVETYPE_BOUNCE:
-               case MOVETYPE_BOUNCEMISSILE:
-               case MOVETYPE_FLYMISSILE:
-                       _Movetype_Physics_Toss(this, movedt);
-                       break;
-               case MOVETYPE_PHYSICS:
-                       break;
-       }
-}
-
-void Movetype_Physics_NoMatchServer(entity this)  // optimized
-{
-       float movedt = time - this.move_time;
-       this.move_time = time;
-
-       _Movetype_Physics_Frame(this, movedt);
-       if(wasfreed(this))
-               return;
-
-       this.avelocity = this.move_avelocity;
-       this.velocity = this.move_velocity;
-       this.angles = this.move_angles;
-       setorigin(this, this.move_origin);
-}
-
-void Movetype_Physics_MatchServer(entity this, bool sloppy)
-{
-       Movetype_Physics_MatchTicrate(this, TICRATE, sloppy);
-}
-
-void Movetype_Physics_MatchTicrate(entity this, float tr, bool sloppy)  // SV_Physics_Entity
-{
-       if(tr <= 0)
-       {
-               Movetype_Physics_NoMatchServer(this);
-               return;
-       }
-
-       float dt = time - this.move_time;
-
-       int n = max(0, floor(dt / tr));
-       dt -= n * tr;
-       this.move_time += n * tr;
-
-       if(!this.move_didgravity)
-               this.move_didgravity = ((this.move_movetype == MOVETYPE_BOUNCE || this.move_movetype == MOVETYPE_TOSS) && !(this.move_flags & FL_ONGROUND));
-
-       for (int i = 0; i < n; ++i)
-       {
-               _Movetype_Physics_Frame(this, tr);
-               if(wasfreed(this))
-                       return;
-       }
-
-       this.avelocity = this.move_avelocity;
-
-       if(dt > 0 && this.move_movetype != MOVETYPE_NONE && !(this.move_flags & FL_ONGROUND))
-       {
-               // now continue the move from move_time to time
-               this.velocity = this.move_velocity;
-
-               if(this.move_didgravity > 0)
-               {
-                       this.velocity_z -= (GAMEPLAYFIX_GRAVITYUNAFFECTEDBYTICRATE ? 0.5 : 1)
-                           * dt
-                           * (this.gravity ? this.gravity : 1)
-                           * PHYS_GRAVITY(this);
-               }
-
-               this.angles = this.move_angles + dt * this.avelocity;
-
-               if(sloppy || this.move_movetype == MOVETYPE_NOCLIP)
-               {
-                       setorigin(this, this.move_origin + dt * this.velocity);
-               }
-               else
-               {
-                       _Movetype_PushEntityTrace(this, dt * this.velocity);
-                       if(!trace_startsolid)
-                               setorigin(this, trace_endpos);
-               }
-
-               if(this.move_didgravity > 0 && GAMEPLAYFIX_GRAVITYUNAFFECTEDBYTICRATE)
-                       this.velocity_z -= 0.5 * dt * (this.gravity ? this.gravity : 1) * PHYS_GRAVITY(this);
-       }
-       else
-       {
-               this.velocity = this.move_velocity;
-               this.angles = this.move_angles;
-               setorigin(this, this.move_origin);
-       }
-}
diff --git a/qcsrc/common/movetypes/movetypes.qh b/qcsrc/common/movetypes/movetypes.qh
deleted file mode 100644 (file)
index 7578ae2..0000000
+++ /dev/null
@@ -1,90 +0,0 @@
-#ifndef MOVETYPES_H
-#define MOVETYPES_H
-
-.float move_ltime;
-.void()move_think;
-.float move_nextthink;
-.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()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;
-
-.entity move_groundentity;  // FIXME add move_groundnetworkentity?
-.float move_suspendedinair;
-.float move_didgravity;
-
-void _Movetype_WallFriction(entity this, vector stepnormal);
-int _Movetype_FlyMove(entity this, float dt, bool applygravity, vector stepnormal, float stepheight);
-void _Movetype_CheckVelocity(entity this);
-void _Movetype_CheckWaterTransition(entity ent);
-float _Movetype_CheckWater(entity ent);
-void _Movetype_LinkEdict_TouchAreaGrid(entity this);
-void _Movetype_LinkEdict(entity this, float touch_triggers);
-float _Movetype_TestEntityPosition(entity this, vector ofs);
-float _Movetype_UnstickEntity(entity this);
-vector _Movetype_ClipVelocity(vector vel, vector norm, float f);
-void _Movetype_PushEntityTrace(entity this, vector push);
-float _Movetype_PushEntity(entity this, vector push, float failonstartsolid);
-void makevectors_matrix(vector myangles);
-
-void Movetype_Physics_MatchTicrate(entity this, float tr, bool sloppy);
-void Movetype_Physics_MatchServer(entity this, bool sloppy);
-void Movetype_Physics_NoMatchServer(entity this);
-void _Movetype_LinkEdict(entity this, float touch_triggers);
-void _Movetype_LinkEdict_TouchAreaGrid(entity this);
-
-float _Movetype_UnstickEntity(entity this);
-
-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_PHYSICS          = 32;
-const int MOVETYPE_FLY_WORLDONLY    = 33;
-
-const int FL_ITEM                   = 256;
-const int FL_ONGROUND                          = 512;
-#endif
-
-const int MOVETYPE_FAKEPUSH         = 13;
-
-const int MOVEFLAG_VALID = BIT(23);
-const int MOVEFLAG_Q2AIRACCELERATE = BIT(0);
-const int MOVEFLAG_NOGRAVITYONGROUND = BIT(1);
-const int MOVEFLAG_GRAVITYUNAFFECTEDBYTICRATE = BIT(2);
-
-#ifdef CSQC
-#define moveflags STAT(MOVEFLAGS)
-#endif
-
-#endif
diff --git a/qcsrc/common/movetypes/push.qc b/qcsrc/common/movetypes/push.qc
deleted file mode 100644 (file)
index b832465..0000000
+++ /dev/null
@@ -1,157 +0,0 @@
-void _Movetype_PushMove(entity this, float dt)  // SV_PushMove
-{
-       if (this.move_velocity == '0 0 0' && this.move_avelocity == '0 0 0')
-       {
-               this.move_ltime += dt;
-               return;
-       }
-
-       switch (this.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:
-                       this.move_origin = this.move_origin + dt * this.move_velocity;
-                       this.move_angles = this.move_angles + dt * this.move_avelocity;
-                       this.move_angles_x -= 360.0 * floor(this.move_angles.x * (1.0 / 360.0));
-                       this.move_angles_y -= 360.0 * floor(this.move_angles.y * (1.0 / 360.0));
-                       this.move_angles_z -= 360.0 * floor(this.move_angles.z * (1.0 / 360.0));
-                       this.move_ltime += dt;
-                       _Movetype_LinkEdict(this, true);
-                       return;
-               default:
-                       LOG_TRACEF("_Movetype_PushMove: entity %e, unrecognized solid type %d\n", this, this.solid);
-                       return;
-       }
-
-       bool rotated = (this.move_angles * this.move_angles) + (this.move_avelocity * this.move_avelocity) > 0;
-
-       vector move1 = this.move_velocity * dt;
-       vector moveangle = this.move_avelocity * dt;
-
-       makevectors_matrix(-moveangle);
-
-//     vector pushorig = this.move_origin;
-//     vector pushang = this.move_angles;
-//     float pushltime = this.move_ltime;
-
-// move the pusher to its final position
-
-       this.move_origin = this.move_origin + dt * this.move_velocity;
-       this.move_angles = this.move_angles + dt * this.move_avelocity;
-
-       this.move_ltime += dt;
-       _Movetype_LinkEdict(this, true);
-
-       int savesolid = this.solid;
-
-       if (this.move_movetype != MOVETYPE_FAKEPUSH)
-       {
-               for (entity check = findradius(0.5 * (this.absmin + this.absmax), 0.5 * vlen(this.absmax - this.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 == this)
-                               continue;
-
-                       if (this.owner == check)
-                               continue;
-
-                       vector pivot = check.mins + 0.5 * (check.maxs - check.mins);
-                       vector move;
-                       if (rotated)
-                       {
-                               vector org = (check.move_origin - this.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;
-                               WITH(entity, this, check, _Movetype_LinkEdict(this, true));
-                               continue;
-                       }
-
-                       // try moving the contacted entity
-                       this.solid = SOLID_NOT;
-                       bool flag = false;
-                       WITH(entity, this, check, {
-                               flag = _Movetype_PushEntity(this, move, true);
-                       });
-                       if (!flag)
-                       {
-                               // entity "check" got teleported
-                               check.move_angles_y += trace_fraction * moveangle.y;
-                               this.solid = savesolid;
-                               continue;  // pushed enough
-                       }
-                       // FIXME: turn players specially
-                       check.move_angles_y += trace_fraction * moveangle.y;
-                       this.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 != this))
-                               check.move_flags &= ~FL_ONGROUND;
-               }
-       }
-
-       this.move_angles_x -= 360.0 * floor(this.move_angles.x * (1.0 / 360.0));
-       this.move_angles_y -= 360.0 * floor(this.move_angles.y * (1.0 / 360.0));
-       this.move_angles_z -= 360.0 * floor(this.move_angles.z * (1.0 / 360.0));
-}
-
-void _Movetype_Physics_Pusher(entity this, float dt)  // SV_Physics_Pusher
-{
-       float oldltime = this.move_ltime;
-       float thinktime = this.move_nextthink;
-       float movetime;
-       if (thinktime < this.move_ltime + dt)
-       {
-               movetime = thinktime - this.move_ltime;
-               if (movetime < 0)
-                       movetime = 0;
-       }
-       else
-       {
-               movetime = dt;
-       }
-
-       if (movetime)
-               // advances this.move_ltime if not blocked
-               _Movetype_PushMove(this, movetime);
-
-       if (thinktime > oldltime && thinktime <= this.move_ltime)
-       {
-               this.move_nextthink = 0;
-               this.move_time = time;
-               other = world;
-               WITH(entity, self, this, this.move_think());
-       }
-}
diff --git a/qcsrc/common/movetypes/push.qh b/qcsrc/common/movetypes/push.qh
deleted file mode 100644 (file)
index d0c8493..0000000
+++ /dev/null
@@ -1,6 +0,0 @@
-#ifndef MOVETYPE_PUSH_H
-#define MOVETYPE_PUSH_H
-
-void _Movetype_Physics_Pusher(entity this, float dt);
-
-#endif
diff --git a/qcsrc/common/movetypes/step.qc b/qcsrc/common/movetypes/step.qc
deleted file mode 100644 (file)
index d7a2d56..0000000
+++ /dev/null
@@ -1,23 +0,0 @@
-void _Movetype_Physics_Step(entity this, float dt) // SV_Physics_Step
-{
-       if(this.move_flags & FL_ONGROUND)
-       {
-               if(this.velocity_z >= (1.0 / 32.0) && UPWARD_VELOCITY_CLEARS_ONGROUND(this))
-               {
-                       this.move_flags &= ~FL_ONGROUND;
-                       _Movetype_CheckVelocity(this);
-                       _Movetype_FlyMove(this, dt, true, '0 0 0', 0);
-                       _Movetype_LinkEdict(this, true);
-               }
-       }
-       else
-       {
-               _Movetype_CheckVelocity(this);
-               _Movetype_FlyMove(this, dt, true, '0 0 0', 0);
-               _Movetype_LinkEdict(this, true);
-
-               // TODO? movetypesteplandevent
-       }
-
-       _Movetype_CheckWaterTransition(this);
-}
diff --git a/qcsrc/common/movetypes/toss.qc b/qcsrc/common/movetypes/toss.qc
deleted file mode 100644 (file)
index 43b5a8a..0000000
+++ /dev/null
@@ -1,115 +0,0 @@
-#include "../physics.qh"
-
-void _Movetype_Physics_Toss(entity this, float dt)  // SV_Physics_Toss
-{
-       if (this.move_flags & FL_ONGROUND)
-       {
-               if (this.move_velocity.z >= 1 / 32 && UPWARD_VELOCITY_CLEARS_ONGROUND(this))
-               {
-                       this.move_flags &= ~FL_ONGROUND;
-               }
-               else if (!this.move_groundentity)
-               {
-                       return;
-               }
-               else if (this.move_suspendedinair && wasfreed(this.move_groundentity))
-               {
-                       this.move_groundentity = world;
-                       return;
-               }
-       }
-
-       this.move_suspendedinair = false;
-
-       _Movetype_CheckVelocity(this);
-
-       if (this.move_movetype == MOVETYPE_BOUNCE || this.move_movetype == MOVETYPE_TOSS)
-       {
-               this.move_didgravity = 1;
-               this.move_velocity_z -= (GAMEPLAYFIX_GRAVITYUNAFFECTEDBYTICRATE ? 0.5 : 1)
-                   * dt
-                   * (this.gravity ? this.gravity : 1)
-                   * PHYS_GRAVITY(this);
-       }
-
-       this.move_angles = this.move_angles + this.move_avelocity * dt;
-
-       float movetime = dt;
-       for (int bump = 0; bump < MAX_CLIP_PLANES && movetime > 0; ++bump)
-       {
-               vector move = this.move_velocity * movetime;
-               _Movetype_PushEntity(this, move, true);
-               if (wasfreed(this))
-                       return;
-
-               if (trace_startsolid)
-               {
-                       _Movetype_UnstickEntity(this);
-                       _Movetype_PushEntity(this, move, false);
-                       if (wasfreed(this))
-                               return;
-               }
-
-               if (trace_fraction == 1)
-                       break;
-
-               movetime *= 1 - min(1, trace_fraction);
-
-               if (this.move_movetype == MOVETYPE_BOUNCEMISSILE)
-               {
-                       this.move_velocity = _Movetype_ClipVelocity(this.move_velocity, trace_plane_normal, 2.0);
-                       this.move_flags &= ~FL_ONGROUND;
-               }
-               else if (this.move_movetype == MOVETYPE_BOUNCE)
-               {
-                       float bouncefac = this.move_bounce_factor;     if (!bouncefac)  bouncefac = 0.5;
-                       float bouncestop = this.move_bounce_stopspeed; if (!bouncestop) bouncestop = 60 / 800;
-                       bouncestop *= (this.gravity ? this.gravity : 1) * PHYS_GRAVITY(this);
-
-                       this.move_velocity = _Movetype_ClipVelocity(this.move_velocity, trace_plane_normal, 1 + bouncefac);
-
-                       float d = trace_plane_normal * this.move_velocity;
-                       if (trace_plane_normal.z > 0.7 && d < bouncestop && d > -bouncestop)
-                       {
-                               this.move_flags |= FL_ONGROUND;
-                               this.move_groundentity = trace_ent;
-                               this.move_velocity = '0 0 0';
-                               this.move_avelocity = '0 0 0';
-                       }
-                       else
-                       {
-                               this.move_flags &= ~FL_ONGROUND;
-                       }
-               }
-               else
-               {
-                       this.move_velocity = _Movetype_ClipVelocity(this.move_velocity, trace_plane_normal, 1.0);
-                       if (trace_plane_normal.z > 0.7)
-                       {
-                               this.move_flags |= FL_ONGROUND;
-                               this.move_groundentity = trace_ent;
-                               if (trace_ent.solid == SOLID_BSP)
-                                       this.move_suspendedinair = true;
-                               this.move_velocity = '0 0 0';
-                               this.move_avelocity = '0 0 0';
-                       }
-                       else
-                       {
-                               this.move_flags &= ~FL_ONGROUND;
-                       }
-               }
-
-               // DP revision 8905 (just, WHY...)
-               if (this.move_movetype == MOVETYPE_BOUNCEMISSILE)
-                       break;
-
-               // DP revision 8918 (WHY...)
-               if (this.move_flags & FL_ONGROUND)
-                       break;
-       }
-
-       if (GAMEPLAYFIX_GRAVITYUNAFFECTEDBYTICRATE && this.move_didgravity > 0 && !(this.move_flags & FL_ONGROUND))
-               this.move_velocity_z -= 0.5 * dt * (this.gravity ? this.gravity : 1) * PHYS_GRAVITY(this);
-
-       _Movetype_CheckWaterTransition(this);
-}
diff --git a/qcsrc/common/movetypes/toss.qh b/qcsrc/common/movetypes/toss.qh
deleted file mode 100644 (file)
index cf5cf49..0000000
+++ /dev/null
@@ -1,6 +0,0 @@
-#ifndef MOVETYPE_TOSS_H
-#define MOVETYPE_TOSS_H
-
-void _Movetype_Physics_Toss(entity this, float dt);
-
-#endif
diff --git a/qcsrc/common/movetypes/walk.qc b/qcsrc/common/movetypes/walk.qc
deleted file mode 100644 (file)
index e926246..0000000
+++ /dev/null
@@ -1,176 +0,0 @@
-void _Movetype_Physics_Walk(entity this, 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(this))
-               _Movetype_UnstickEntity(this);
-
-       bool applygravity = (!_Movetype_CheckWater(this) && this.move_movetype == MOVETYPE_WALK && !(this.move_flags & FL_WATERJUMP));
-
-       _Movetype_CheckVelocity(this);
-
-       // do a regular slide move unless it looks like you ran into a step
-       bool oldonground = (this.move_flags & FL_ONGROUND);
-
-       vector start_origin = this.move_origin;
-       vector start_velocity = this.move_velocity;
-
-       int clip = _Movetype_FlyMove(this, dt, applygravity, stepnormal, GAMEPLAYFIX_STEPMULTIPLETIMES(this) ? PHYS_STEPHEIGHT(this) : 0);
-
-       if (GAMEPLAYFIX_DOWNTRACEONGROUND(this) && !(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 = this.move_origin + '0 0 1';
-               vector downmove = this.move_origin - '0 0 1';
-               int type;
-               if (this.move_movetype == MOVETYPE_FLYMISSILE)
-                       type = MOVE_MISSILE;
-               else if (this.move_movetype == MOVETYPE_FLY_WORLDONLY)
-                       type = MOVE_WORLDONLY;
-               else if (this.solid == SOLID_TRIGGER || this.solid == SOLID_NOT)
-                       type = MOVE_NOMONSTERS;
-               else type = MOVE_NORMAL;
-               tracebox(upmove, this.mins, this.maxs, downmove, type, this);
-               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))
-               this.move_flags &= ~FL_ONGROUND;
-
-       _Movetype_CheckVelocity(this);
-       _Movetype_LinkEdict(this, true);
-
-       if (clip & 8)  // teleport
-               return;
-
-       if (this.move_flags & FL_WATERJUMP)
-               return;
-
-       if (PHYS_NOSTEP(this))
-               return;
-
-       vector originalmove_origin = this.move_origin;
-       vector originalmove_velocity = this.move_velocity;
-       // originalmove_clip = clip;
-       int originalmove_flags = this.move_flags;
-       entity originalmove_groundentity = this.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 (this.move_movetype != MOVETYPE_FLY)
-               {
-                       // return if gibbed by a trigger
-                       if (this.move_movetype != MOVETYPE_WALK)
-                               return;
-
-                       // return if attempting to jump while airborn (unless sv_jumpstep)
-                       if (!PHYS_JUMPSTEP(this))
-                               if (!oldonground && this.move_waterlevel == 0)
-                                       return;
-               }
-
-               // try moving up and forward to go up a step
-               // back to start pos
-               this.move_origin = start_origin;
-               this.move_velocity = start_velocity;
-
-               // move up
-               vector upmove = '0 0 1' * PHYS_STEPHEIGHT(this);
-               _Movetype_PushEntity(this, upmove, true);
-               if(wasfreed(this))
-                       return;
-               if(trace_startsolid)
-               {
-                       // we got teleported when upstepping... must abort the move
-                       return;
-               }
-
-               // move forward
-               this.move_velocity_z = 0;
-               clip = _Movetype_FlyMove(this, dt, applygravity, stepnormal, 0);
-               this.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(this);
-               _Movetype_LinkEdict(this, true);
-
-               // check for stuckness, possibly due to the limited precision of floats
-               // in the clipping hulls
-               if (clip
-                   && fabs(originalmove_origin.y - this.move_origin.y) < 0.03125
-                   && fabs(originalmove_origin.x - this.move_origin.x) < 0.03125)
-               {
-                       // Con_Printf("wall\n");
-                       // stepping up didn't make any progress, revert to original move
-                       this.move_origin = originalmove_origin;
-                       this.move_velocity = originalmove_velocity;
-                       // clip = originalmove_clip;
-                       this.move_flags = originalmove_flags;
-                       this.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(this))
-                       _Movetype_WallFriction(this, 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(this) || this.move_waterlevel >= 3 || start_velocity.z >= (1.0 / 32.0) || !oldonground || (this.move_flags & FL_ONGROUND))
-       {
-               return;
-       }
-
-       // move down
-       vector downmove = '0 0 1' * (-PHYS_STEPHEIGHT(this) + start_velocity.z * dt);
-       _Movetype_PushEntity(this, downmove, true);
-       if(wasfreed(this))
-               return;
-
-       if(trace_startsolid)
-       {
-               // 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
-               this.move_origin = originalmove_origin;
-               this.move_velocity = originalmove_velocity;
-               this.move_flags = originalmove_flags;
-               this.move_groundentity = originalmove_groundentity;
-       }
-
-       _Movetype_CheckVelocity(this);
-       _Movetype_LinkEdict(this, true);
-}
diff --git a/qcsrc/common/movetypes/walk.qh b/qcsrc/common/movetypes/walk.qh
deleted file mode 100644 (file)
index a920c7a..0000000
+++ /dev/null
@@ -1,6 +0,0 @@
-#ifndef MOVETYPE_WALK_H
-#define MOVETYPE_WALK_H
-
-void _Movetype_Physics_Walk(entity this, float dt);
-
-#endif
index 23bf8a8..9552bc7 100644 (file)
@@ -697,15 +697,15 @@ MUTATOR_HOOKFUNCTION(buffs, ForbidThrowCurrentWeapon)
        {
                float best_distance = autocvar_g_buffs_swapper_range;
                entity closest = world;
-               entity player;
-               FOR_EACH_PLAYER(player)
-               if(DIFF_TEAM(self, player))
-               if(player.deadflag == DEAD_NO && !player.frozen && !player.vehicle)
-               if(vlen(self.origin - player.origin) <= best_distance)
-               {
-                       best_distance = vlen(self.origin - player.origin);
-                       closest = player;
-               }
+               FOREACH_CLIENT(IS_PLAYER(it), LAMBDA(
+                       if(it.deadflag == DEAD_NO && !it.frozen && !it.vehicle)
+                       if(DIFF_TEAM(it, self))
+                       if(vlen(self.origin - it.origin) <= best_distance)
+                       {
+                               best_distance = vlen(self.origin - it.origin);
+                               closest = it;
+                       }
+               ));
 
                if(closest)
                {
index 1943413..887dcbf 100644 (file)
@@ -2,7 +2,7 @@
 #ifdef SVQC
        #include "../../../../server/antilag.qh"
 #endif
-#include "../../../physics.qh"
+#include "../../../physics/player.qh"
 
 
 #if defined(SVQC)
index d2debd8..053cdd7 100644 (file)
@@ -33,7 +33,7 @@ bool autocvar_sv_dodging_sound;
 .float dodging_single_action;
 
 #include "../../../animdecide.qh"
-#include "../../../physics.qh"
+#include "../../../physics/player.qh"
 
 .float cvar_cl_dodging_timeout = _STAT(DODGING_TIMEOUT);
 
index a7aaa78..a72644d 100644 (file)
@@ -2,7 +2,7 @@
 #ifdef SVQC
        #include "../../../../server/antilag.qh"
 #endif
-#include "../../../physics.qh"
+#include "../../../physics/player.qh"
 
 #ifdef SVQC
 REGISTER_MUTATOR(doublejump, autocvar_sv_doublejump);
index 7a8b292..eb5e64a 100644 (file)
@@ -2,7 +2,7 @@
 #ifdef SVQC
        #include "../../../../server/antilag.qh"
 #endif
-#include "../../../physics.qh"
+#include "../../../physics/player.qh"
 
 
 #if defined(SVQC)
index 20a9d94..ab0567d 100644 (file)
@@ -77,16 +77,18 @@ REGISTER_MUTATOR(nt, cvar("g_new_toys") && !cvar("g_instagib") && !cvar("g_overk
                        error("This cannot be added at runtime\n");
 
                // mark the guns as ok to use by e.g. impulse 99
-               for(int i = WEP_FIRST; i <= WEP_LAST; ++i)
-                       if(nt_IsNewToy(i))
-                               Weapons_from(i).spawnflags &= ~WEP_FLAG_MUTATORBLOCKED;
+               FOREACH(Weapons, it != WEP_Null, LAMBDA(
+                       if(nt_IsNewToy(it.m_id))
+                               it.spawnflags &= ~WEP_FLAG_MUTATORBLOCKED;
+               ));
        }
 
        MUTATOR_ONROLLBACK_OR_REMOVE
        {
-               for(int i = WEP_FIRST; i <= WEP_LAST; ++i)
-                       if(nt_IsNewToy(i))
-                               Weapons_from(i).spawnflags |= WEP_FLAG_MUTATORBLOCKED;
+               FOREACH(Weapons, it != WEP_Null, LAMBDA(
+                       if(nt_IsNewToy(it.m_id))
+                               it.spawnflags |= WEP_FLAG_MUTATORBLOCKED;
+               ));
        }
 
        MUTATOR_ONREMOVE
@@ -158,7 +160,7 @@ MUTATOR_HOOKFUNCTION(nt, SetStartItems)
        // apply those bits that are set by start_weapon_defaultmask
        // same for warmup
 
-       float i, j, k, n;
+       float j, n;
 
        WepSet newdefault;
        WepSet warmup_newdefault;
@@ -166,26 +168,22 @@ MUTATOR_HOOKFUNCTION(nt, SetStartItems)
        newdefault = '0 0 0';
        warmup_newdefault = '0 0 0';
 
-       for(i = WEP_FIRST; i <= WEP_LAST; ++i)
-       {
-               entity e = Weapons_from(i);
-               if(e == WEP_Null) continue;
+       WepSet seti = '0 0 0';
 
-               n = tokenize_console(nt_GetReplacement(e.netname, autocvar_g_new_toys_autoreplace));
+       FOREACH(Weapons, it != WEP_Null, LAMBDA(
+               seti = it.m_wepset;
+               n = tokenize_console(nt_GetReplacement(it.netname, autocvar_g_new_toys_autoreplace));
 
-               for (j = 0; j < n; ++j)
-                       for (k = WEP_FIRST; k <= WEP_LAST; ++k)
-                       {
-                               Weapon w = Weapons_from(k);
-                               if (w.netname == argv(j))
+               for(j = 0; j < n; ++j)
+                       FOREACH(Weapons, it != WEP_Null, LAMBDA(
+                               if(it.netname == argv(j))
                                {
-                                       WepSet seti = e.m_wepset;
-                                       WepSet setk = w.m_wepset;
-                                       if (start_weapons & seti) newdefault |= setk;
-                                       if (warmup_start_weapons & seti) warmup_newdefault |= setk;
+                                       WepSet setk = it.m_wepset;
+                                       if(start_weapons & seti) newdefault |= setk;
+                                       if(warmup_start_weapons & seti) warmup_newdefault |= setk;
                                }
-                       }
-       }
+                       ));
+       ));
 
        newdefault &= start_weapons_defaultmask;
        start_weapons &= ~start_weapons_defaultmask;
index a6d42d5..2f4a7cd 100644 (file)
@@ -43,11 +43,7 @@ REGISTER_MUTATOR(nix, cvar("g_nix") && !cvar("g_instagib") && !cvar("g_overkill"
                nix_nextchange = 0;
                nix_nextweapon = 0;
 
-               for (int i = WEP_FIRST; i <= WEP_LAST; ++i)
-                       if (NIX_CanChooseWeapon(i)) {
-                               Weapon w = Weapons_from(i);
-                               w.wr_init(w);
-                       }
+               FOREACH(Weapons, it != WEP_Null && NIX_CanChooseWeapon(it.m_id), LAMBDA(it.wr_init(it)));
        }
 
        MUTATOR_ONROLLBACK_OR_REMOVE
@@ -98,11 +94,11 @@ bool NIX_CanChooseWeapon(int wpn)
 }
 void NIX_ChooseNextWeapon()
 {
-       float j;
        RandomSelection_Init();
-       for(j = WEP_FIRST; j <= WEP_LAST; ++j)
-               if(NIX_CanChooseWeapon(j))
-                       RandomSelection_Add(world, j, string_null, 1, (j != nix_weapon));
+       FOREACH(Weapons, it != WEP_Null, LAMBDA(
+               if(NIX_CanChooseWeapon(it.m_id))
+                       RandomSelection_Add(world, it.m_id, string_null, 1, (it.m_id != nix_weapon));
+       ));
        nix_nextweapon = RandomSelection_chosen_float;
 }
 
index bd1a9ef..d50c75b 100644 (file)
@@ -244,8 +244,7 @@ MUTATOR_HOOKFUNCTION(ok, PlayerSpawn)
 {SELFPARAM();
        if(autocvar_g_overkill_ammo_charge)
        {
-               for(int i = WEP_FIRST; i <= WEP_LAST; ++i)
-                       self.ammo_charge[i] = autocvar_g_overkill_ammo_charge_limit;
+               FOREACH(Weapons, it != WEP_Null, LAMBDA(self.ammo_charge[it.m_id] = autocvar_g_overkill_ammo_charge_limit));
 
                self.ok_use_ammocharge = 1;
                self.ok_notice_time = time;
index d5cbb7f..d9b7666 100644 (file)
@@ -3,11 +3,12 @@ REGISTER_MUTATOR(pinata, cvar("g_pinata") && !cvar("g_instagib") && !cvar("g_ove
 
 MUTATOR_HOOKFUNCTION(pinata, PlayerDies)
 {SELFPARAM();
-       for(int j = WEP_FIRST; j <= WEP_LAST; ++j)
-       if(self.weapons & WepSet_FromWeapon(Weapons_from(j)))
-       if(PS(self).m_switchweapon.m_id != j)
-       if(W_IsWeaponThrowable(j))
-               W_ThrowNewWeapon(self, j, false, self.origin + (self.mins + self.maxs) * 0.5, randomvec() * 175 + '0 0 325');
+       FOREACH(Weapons, it != WEP_Null, LAMBDA(
+               if(self.weapons & WepSet_FromWeapon(it))
+               if(PS(self).m_switchweapon != it)
+               if(W_IsWeaponThrowable(it.m_id))
+                       W_ThrowNewWeapon(self, it.m_id, false, CENTER_OR_VIEWOFS(self), randomvec() * 175 + '0 0 325');
+       ));
 
        return true;
 }
index 887b184..3c183c7 100644 (file)
@@ -101,10 +101,10 @@ MUTATOR_HOOKFUNCTION(superspec, ItemTouch)
 {SELFPARAM();
        entity _item = self;
 
-       entity e;
-       FOR_EACH_CLIENT(e) if (IS_SPEC(e) || IS_OBSERVER(e))
-       {
-               setself(e);
+       FOREACH_CLIENT(true, LAMBDA(
+               if(!IS_SPEC(it) && !IS_OBSERVER(it))
+                       continue;
+               setself(it);
                if(self.superspec_flags & SSF_ITEMMSG)
                        if(superspec_filteritem(self, _item))
                        {
@@ -144,7 +144,7 @@ MUTATOR_HOOKFUNCTION(superspec, ItemTouch)
                                }
                        }
                }
-       }
+       ));
 
        setself(this);
 
diff --git a/qcsrc/common/physics.qc b/qcsrc/common/physics.qc
deleted file mode 100644 (file)
index 32c2fbf..0000000
+++ /dev/null
@@ -1,1545 +0,0 @@
-#include "physics.qh"
-#include "triggers/include.qh"
-#include "viewloc.qh"
-
-#ifdef SVQC
-
-#include "../server/miscfunctions.qh"
-#include "triggers/trigger/viewloc.qh"
-
-// client side physics
-bool Physics_Valid(string thecvar)
-{
-       return autocvar_g_physics_clientselect && strhasword(autocvar_g_physics_clientselect_options, thecvar);
-}
-
-float Physics_ClientOption(entity this, string option)
-{
-       if(Physics_Valid(this.cvar_cl_physics))
-       {
-               string s = sprintf("g_physics_%s_%s", this.cvar_cl_physics, option);
-               if(cvar_type(s) & CVAR_TYPEFLAG_EXISTS)
-                       return cvar(s);
-       }
-       if(autocvar_g_physics_clientselect && autocvar_g_physics_clientselect_default)
-       {
-               string s = sprintf("g_physics_%s_%s", autocvar_g_physics_clientselect_default, option);
-               if(cvar_type(s) & CVAR_TYPEFLAG_EXISTS)
-                       return cvar(s);
-       }
-       return cvar(strcat("sv_", option));
-}
-
-void Physics_UpdateStats(entity this, float maxspd_mod)
-{
-       STAT(MOVEVARS_AIRACCEL_QW, this) = AdjustAirAccelQW(Physics_ClientOption(this, "airaccel_qw"), maxspd_mod);
-       STAT(MOVEVARS_AIRSTRAFEACCEL_QW, this) = (Physics_ClientOption(this, "airstrafeaccel_qw"))
-               ? AdjustAirAccelQW(Physics_ClientOption(this, "airstrafeaccel_qw"), maxspd_mod)
-               : 0;
-       STAT(MOVEVARS_AIRSPEEDLIMIT_NONQW, this) = Physics_ClientOption(this, "airspeedlimit_nonqw") * maxspd_mod;
-       STAT(MOVEVARS_MAXSPEED, this) = Physics_ClientOption(this, "maxspeed") * maxspd_mod; // also slow walking
-
-       // old stats
-       // fix some new settings
-       STAT(MOVEVARS_AIRACCEL_QW_STRETCHFACTOR, this) = Physics_ClientOption(this, "airaccel_qw_stretchfactor");
-       STAT(MOVEVARS_MAXAIRSTRAFESPEED, this) = Physics_ClientOption(this, "maxairstrafespeed");
-       STAT(MOVEVARS_MAXAIRSPEED, this) = Physics_ClientOption(this, "maxairspeed");
-       STAT(MOVEVARS_AIRSTRAFEACCELERATE, this) = Physics_ClientOption(this, "airstrafeaccelerate");
-       STAT(MOVEVARS_WARSOWBUNNY_TURNACCEL, this) = Physics_ClientOption(this, "warsowbunny_turnaccel");
-       STAT(MOVEVARS_AIRACCEL_SIDEWAYS_FRICTION, this) = Physics_ClientOption(this, "airaccel_sideways_friction");
-       STAT(MOVEVARS_AIRCONTROL, this) = Physics_ClientOption(this, "aircontrol");
-       STAT(MOVEVARS_AIRCONTROL_POWER, this) = Physics_ClientOption(this, "aircontrol_power");
-       STAT(MOVEVARS_AIRCONTROL_PENALTY, this) = Physics_ClientOption(this, "aircontrol_penalty");
-       STAT(MOVEVARS_WARSOWBUNNY_AIRFORWARDACCEL, this) = Physics_ClientOption(this, "warsowbunny_airforwardaccel");
-       STAT(MOVEVARS_WARSOWBUNNY_TOPSPEED, this) = Physics_ClientOption(this, "warsowbunny_topspeed");
-       STAT(MOVEVARS_WARSOWBUNNY_ACCEL, this) = Physics_ClientOption(this, "warsowbunny_accel");
-       STAT(MOVEVARS_WARSOWBUNNY_BACKTOSIDERATIO, this) = Physics_ClientOption(this, "warsowbunny_backtosideratio");
-       STAT(MOVEVARS_FRICTION, this) = Physics_ClientOption(this, "friction");
-       STAT(MOVEVARS_ACCELERATE, this) = Physics_ClientOption(this, "accelerate");
-       STAT(MOVEVARS_STOPSPEED, this) = Physics_ClientOption(this, "stopspeed");
-       STAT(MOVEVARS_AIRACCELERATE, this) = Physics_ClientOption(this, "airaccelerate");
-       STAT(MOVEVARS_AIRSTOPACCELERATE, this) = Physics_ClientOption(this, "airstopaccelerate");
-       STAT(MOVEVARS_JUMPVELOCITY, this) = Physics_ClientOption(this, "jumpvelocity");
-       STAT(MOVEVARS_TRACK_CANJUMP, this) = Physics_ClientOption(this, "track_canjump");
-}
-#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);
-}
-
-#define unstick_offsets(X) \
-/* 1 no nudge (just return the original if this test passes) */ \
-       X(' 0.000  0.000  0.000') \
-/* 6 simple nudges */ \
-       X(' 0.000  0.000  0.125') X('0.000  0.000 -0.125') \
-       X('-0.125  0.000  0.000') X('0.125  0.000  0.000') \
-       X(' 0.000 -0.125  0.000') X('0.000  0.125  0.000') \
-/* 4 diagonal flat nudges */ \
-       X('-0.125 -0.125  0.000') X('0.125 -0.125  0.000') \
-       X('-0.125  0.125  0.000') X('0.125  0.125  0.000') \
-/* 8 diagonal upward nudges */ \
-       X('-0.125  0.000  0.125') X('0.125  0.000  0.125') \
-       X(' 0.000 -0.125  0.125') X('0.000  0.125  0.125') \
-       X('-0.125 -0.125  0.125') X('0.125 -0.125  0.125') \
-       X('-0.125  0.125  0.125') X('0.125  0.125  0.125') \
-/* 8 diagonal downward nudges */ \
-       X('-0.125  0.000 -0.125') X('0.125  0.000 -0.125') \
-       X(' 0.000 -0.125 -0.125') X('0.000  0.125 -0.125') \
-       X('-0.125 -0.125 -0.125') X('0.125 -0.125 -0.125') \
-       X('-0.125  0.125 -0.125') X('0.125  0.125 -0.125') \
-/**/
-
-void PM_ClientMovement_Unstick(entity this)
-{
-       #define X(unstick_offset) \
-       { \
-               vector neworigin = unstick_offset + this.origin; \
-               tracebox(neworigin, PL_CROUCH_MIN, PL_CROUCH_MAX, neworigin, MOVE_NORMAL, this); \
-               if (!trace_startsolid) \
-               { \
-                       setorigin(this, neworigin); \
-                       return; \
-               } \
-       }
-       unstick_offsets(X);
-       #undef X
-}
-
-void PM_ClientMovement_UpdateStatus(entity this, bool ground)
-{
-#ifdef CSQC
-       if(!IS_PLAYER(this))
-               return;
-       // make sure player is not stuck
-       if(autocvar_cl_movement == 3)
-               PM_ClientMovement_Unstick(this);
-
-       // set crouched
-       if (PHYS_INPUT_BUTTON_CROUCH(this))
-       {
-               // wants to crouch, this always works
-               if (!IS_DUCKED(this)) SET_DUCKED(this);
-       }
-       else
-       {
-               // wants to stand, if currently crouching we need to check for a low ceiling first
-               if (IS_DUCKED(this))
-               {
-                       tracebox(this.origin, PL_MIN, PL_MAX, this.origin, MOVE_NORMAL, this);
-                       if (!trace_startsolid) UNSET_DUCKED(this);
-               }
-       }
-
-       // set onground
-       vector origin1 = this.origin + '0 0 1';
-       vector origin2 = this.origin - '0 0 1';
-
-       if (ground && autocvar_cl_movement == 3)
-       {
-               tracebox(origin1, this.mins, this.maxs, origin2, MOVE_NORMAL, this);
-               if (trace_fraction < 1.0 && trace_plane_normal.z > 0.7)
-               {
-                       SET_ONGROUND(this);
-
-                       // this code actually "predicts" an impact; so let's clip velocity first
-                       this.velocity -= this.velocity * trace_plane_normal * trace_plane_normal;
-               }
-               else
-                       UNSET_ONGROUND(this);
-       }
-
-       if(autocvar_cl_movement == 3)
-       {
-               // set watertype/waterlevel
-               origin1 = this.origin;
-               origin1.z += this.mins_z + 1;
-               this.waterlevel = WATERLEVEL_NONE;
-
-               int thepoint = pointcontents(origin1);
-
-               this.watertype = (thepoint == CONTENT_WATER || thepoint == CONTENT_LAVA || thepoint == CONTENT_SLIME);
-
-               if (this.watertype)
-               {
-                       this.waterlevel = WATERLEVEL_WETFEET;
-                       origin1.z = this.origin.z + (this.mins.z + this.maxs.z) * 0.5;
-                       thepoint = pointcontents(origin1);
-                       if (thepoint == CONTENT_WATER || thepoint == CONTENT_LAVA || thepoint == CONTENT_SLIME)
-                       {
-                               this.waterlevel = WATERLEVEL_SWIMMING;
-                               origin1.z = this.origin.z + 22;
-                               thepoint = pointcontents(origin1);
-                               if (thepoint == CONTENT_WATER || thepoint == CONTENT_LAVA || thepoint == CONTENT_SLIME)
-                                       this.waterlevel = WATERLEVEL_SUBMERGED;
-                       }
-               }
-       }
-
-       if (IS_ONGROUND(this) || this.velocity.z <= 0 || pmove_waterjumptime <= 0)
-               pmove_waterjumptime = 0;
-#endif
-}
-
-void PM_ClientMovement_Move(entity this)
-{
-#ifdef CSQC
-
-       PM_ClientMovement_UpdateStatus(this, false);
-       if(autocvar_cl_movement == 1)
-               return;
-
-       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';
-
-       primalvelocity = this.velocity;
-       for(bump = 0, t = PHYS_INPUT_TIMELENGTH; bump < 8 && (this.velocity * this.velocity) > 0; bump++)
-       {
-               neworigin = this.origin + t * this.velocity;
-               tracebox(this.origin, this.mins, this.maxs, neworigin, MOVE_NORMAL, this);
-               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 = this.origin;
-                       currentorigin2_z += PHYS_STEPHEIGHT(this);
-                       neworigin2 = neworigin;
-                       neworigin2_z += PHYS_STEPHEIGHT(this);
-                       tracebox(currentorigin2, this.mins, this.maxs, neworigin2, MOVE_NORMAL, this);
-                       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 = this.origin_z;
-                               tracebox(currentorigin2, this.mins, this.maxs, neworigin2, MOVE_NORMAL, this);
-                               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(this, 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(this);
-
-               t -= t * trace1_fraction;
-
-               f = (this.velocity * trace1_plane_normal);
-               this.velocity = this.velocity + -f * trace1_plane_normal;
-       }
-       if(PHYS_TELEPORT_TIME(this) > 0)
-               this.velocity = primalvelocity;
-#endif
-}
-
-void CPM_PM_Aircontrol(entity this, vector wishdir, float wishspeed)
-{
-       float k = 32 * (2 * IsMoveInDirection(this.movement, 0) - 1);
-       if (k <= 0)
-               return;
-
-       k *= bound(0, wishspeed / PHYS_MAXAIRSPEED(this), 1);
-
-       float zspeed = this.velocity_z;
-       this.velocity_z = 0;
-       float xyspeed = vlen(this.velocity);
-       this.velocity = normalize(this.velocity);
-
-       float dot = this.velocity * wishdir;
-
-       if (dot > 0) // we can't change direction while slowing down
-       {
-               k *= pow(dot, PHYS_AIRCONTROL_POWER(this)) * PHYS_INPUT_TIMELENGTH;
-               xyspeed = max(0, xyspeed - PHYS_AIRCONTROL_PENALTY(this) * sqrt(max(0, 1 - dot*dot)) * k/32);
-               k *= PHYS_AIRCONTROL(this);
-               this.velocity = normalize(this.velocity * xyspeed + wishdir * k);
-       }
-
-       this.velocity = this.velocity * xyspeed;
-       this.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(entity this, 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 = this.velocity * wishdir;
-       float vel_z = this.velocity_z;
-       vector vel_xy = vec2(this.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 themin = (vel_xy_backward * vel_xy_backward - vel_straight * vel_straight) / (vel_perpend * vel_perpend);
-               // assume: themin > 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 (themin <= 0)
-                       vel_perpend *= f;
-               else
-               {
-                       themin = sqrt(themin);
-                       vel_perpend *= max(themin, 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);
-               }
-       }
-
-       this.velocity = vel_xy + vel_z * '0 0 1';
-}
-
-void PM_AirAccelerate(entity this, vector wishdir, float wishspeed)
-{
-       if (wishspeed == 0)
-               return;
-
-       vector curvel = this.velocity;
-       curvel_z = 0;
-       float curspeed = vlen(curvel);
-
-       if (wishspeed > curspeed * 1.01)
-               wishspeed = min(wishspeed, curspeed + PHYS_WARSOWBUNNY_AIRFORWARDACCEL(this) * PHYS_MAXSPEED(this) * PHYS_INPUT_TIMELENGTH);
-       else
-       {
-               float f = max(0, (PHYS_WARSOWBUNNY_TOPSPEED(this) - curspeed) / (PHYS_WARSOWBUNNY_TOPSPEED(this) - PHYS_MAXSPEED(this)));
-               wishspeed = max(curspeed, PHYS_MAXSPEED(this)) + PHYS_WARSOWBUNNY_ACCEL(this) * f * PHYS_MAXSPEED(this) * 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(this) * PHYS_MAXSPEED(this) * PHYS_INPUT_TIMELENGTH);
-
-       if (PHYS_WARSOWBUNNY_BACKTOSIDERATIO(this) < 1)
-       {
-               vector curdir = normalize(curvel);
-               float dot = acceldir * curdir;
-               if (dot < 0)
-                       acceldir -= (1 - PHYS_WARSOWBUNNY_BACKTOSIDERATIO(this)) * dot * curdir;
-       }
-
-       this.velocity += accelspeed * acceldir;
-}
-
-
-/*
-=============
-PlayerJump
-
-When you press the jump key
-returns true if handled
-=============
-*/
-bool PlayerJump(entity this)
-{
-       if (PHYS_FROZEN(this))
-               return true; // no jumping in freezetag when frozen
-
-#ifdef SVQC
-       if (this.player_blocked)
-               return true; // no jumping while blocked
-#endif
-
-       bool doublejump = false;
-       float mjumpheight = PHYS_JUMPVELOCITY(this);
-
-       if (MUTATOR_CALLHOOK(PlayerJump, this, doublejump, mjumpheight))
-               return true;
-
-       doublejump = player_multijump;
-       mjumpheight = player_jumpheight;
-
-       if (this.waterlevel >= WATERLEVEL_SWIMMING)
-       {
-               if(this.viewloc)
-               {
-                       doublejump = true;
-                       mjumpheight *= 0.7;
-               }
-               else
-               {
-                       this.velocity_z = PHYS_MAXSPEED(this) * 0.7;
-                       return true;
-               }
-       }
-
-       if (!doublejump)
-               if (!IS_ONGROUND(this))
-                       return IS_JUMP_HELD(this);
-
-       bool track_jump = PHYS_CL_TRACK_CANJUMP(this);
-       if(PHYS_TRACK_CANJUMP(this))
-               track_jump = true;
-
-       if (track_jump)
-               if (IS_JUMP_HELD(this))
-                       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 * stof(PHYS_JUMPSPEEDCAP_MIN);
-
-               if (this.velocity_z < minjumpspeed)
-                       mjumpheight += minjumpspeed - this.velocity_z;
-       }
-
-       if(PHYS_JUMPSPEEDCAP_MAX != "")
-       {
-               // don't do jump speedcaps on ramps to preserve old xonotic ramjump style
-               tracebox(this.origin + '0 0 0.01', this.mins, this.maxs, this.origin - '0 0 0.01', MOVE_NORMAL, this);
-
-               if (!(trace_fraction < 1 && trace_plane_normal_z < 0.98 && PHYS_JUMPSPEEDCAP_DISABLE_ONRAMPS(this)))
-               {
-                       float maxjumpspeed = mjumpheight * stof(PHYS_JUMPSPEEDCAP_MAX);
-
-                       if (this.velocity_z > maxjumpspeed)
-                               mjumpheight -= this.velocity_z - maxjumpspeed;
-               }
-       }
-
-       if (!WAS_ONGROUND(this))
-       {
-#ifdef SVQC
-               if(autocvar_speedmeter)
-                       LOG_TRACE(strcat("landing velocity: ", vtos(this.velocity), " (abs: ", ftos(vlen(this.velocity)), ")\n"));
-#endif
-               if(this.lastground < time - 0.3)
-               {
-                       float f = (1 - PHYS_FRICTION_ONLAND(this));
-                       this.velocity_x *= f;
-                       this.velocity_y *= f;
-               }
-#ifdef SVQC
-               if(this.jumppadcount > 1)
-                       LOG_TRACE(strcat(ftos(this.jumppadcount), "x jumppad combo\n"));
-               this.jumppadcount = 0;
-#endif
-       }
-
-       this.velocity_z += mjumpheight;
-
-       UNSET_ONGROUND(this);
-       SET_JUMP_HELD(this);
-
-#ifdef SVQC
-
-       this.oldvelocity_z = this.velocity_z;
-
-       animdecide_setaction(this, ANIMACTION_JUMP, true);
-
-       if (autocvar_g_jump_grunt)
-               PlayerSound(this, playersound_jump, CH_PLAYER, VOICETYPE_PLAYERSOUND);
-#endif
-       return true;
-}
-
-void CheckWaterJump(entity this)
-{
-// check for a jump-out-of-water
-       makevectors(this.v_angle);
-       vector start = this.origin;
-       start_z += 8;
-       v_forward_z = 0;
-       normalize(v_forward);
-       vector end = start + v_forward*24;
-       traceline (start, end, true, this);
-       if (trace_fraction < 1)
-       {       // solid at waist
-               start_z = start_z + this.maxs_z - 8;
-               end = start + v_forward*24;
-               this.movedir = trace_plane_normal * -50;
-               traceline(start, end, true, this);
-               if (trace_fraction == 1)
-               {       // open at eye level
-                       this.velocity_z = 225;
-                       this.flags |= FL_WATERJUMP;
-                       SET_JUMP_HELD(this);
-               #ifdef SVQC
-                       PHYS_TELEPORT_TIME(this) = 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;
-void CheckPlayerJump(entity this)
-{
-#ifdef SVQC
-       float was_flying = ITEMS_STAT(this) & IT_USING_JETPACK;
-#endif
-       if (JETPACK_JUMP(this) < 2)
-               ITEMS_STAT(this) &= ~IT_USING_JETPACK;
-
-       if(PHYS_INPUT_BUTTON_JUMP(this) || PHYS_INPUT_BUTTON_JETPACK(this))
-       {
-               float air_jump = !PlayerJump(this) || player_multijump; // PlayerJump() has important side effects
-               float activate = JETPACK_JUMP(this) && air_jump && PHYS_INPUT_BUTTON_JUMP(this) || PHYS_INPUT_BUTTON_JETPACK(this);
-               float has_fuel = !PHYS_JETPACK_FUEL(this) || PHYS_AMMO_FUEL(this) || ITEMS_STAT(this) & IT_UNLIMITED_WEAPON_AMMO;
-
-               if (!(ITEMS_STAT(this) & ITEM_Jetpack.m_itemid)) { }
-               else if (this.jetpack_stopped) { }
-               else if (!has_fuel)
-               {
-#ifdef SVQC
-                       if (was_flying) // TODO: ran out of fuel message
-                               Send_Notification(NOTIF_ONE, this, MSG_INFO, INFO_JETPACK_NOFUEL);
-                       else if (activate)
-                               Send_Notification(NOTIF_ONE, this, MSG_INFO, INFO_JETPACK_NOFUEL);
-#endif
-                       this.jetpack_stopped = true;
-                       ITEMS_STAT(this) &= ~IT_USING_JETPACK;
-               }
-               else if (activate && !PHYS_FROZEN(this))
-                       ITEMS_STAT(this) |= IT_USING_JETPACK;
-       }
-       else
-       {
-               this.jetpack_stopped = false;
-               ITEMS_STAT(this) &= ~IT_USING_JETPACK;
-       }
-       if (!PHYS_INPUT_BUTTON_JUMP(this))
-               UNSET_JUMP_HELD(this);
-
-       if (this.waterlevel == WATERLEVEL_SWIMMING)
-               CheckWaterJump(this);
-}
-
-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;
-}
-
-string specialcommand = "xwxwxsxsxaxdxaxdx1x ";
-.float specialcommand_pos;
-void SpecialCommand()
-{
-#ifdef SVQC
-       if (!CheatImpulse(CHIMPULSE_GIVE_ALL.impulse))
-               LOG_INFO("A hollow voice says \"Plugh\".\n");
-#endif
-}
-
-float PM_check_specialcommand(entity this, 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, this.specialcommand_pos, 1))
-       {
-               this.specialcommand_pos += 1;
-               if (this.specialcommand_pos >= strlen(specialcommand))
-               {
-                       this.specialcommand_pos = 0;
-                       SpecialCommand();
-                       return true;
-               }
-       }
-       else if (this.specialcommand_pos && (c != substring(specialcommand, this.specialcommand_pos - 1, 1)))
-               this.specialcommand_pos = 0;
-#endif
-       return false;
-}
-
-void PM_check_nickspam(entity this)
-{
-#ifdef SVQC
-       if (time >= this.nickspamtime)
-               return;
-       if (this.nickspamcount >= autocvar_g_nick_flood_penalty_yellow)
-       {
-               // slight annoyance for nick change scripts
-               this.movement = -1 * this.movement;
-               this.BUTTON_ATCK = this.BUTTON_JUMP = this.BUTTON_ATCK2 = this.BUTTON_ZOOM = this.BUTTON_CROUCH = this.BUTTON_HOOK = this.BUTTON_USE = 0;
-
-               if (this.nickspamcount >= autocvar_g_nick_flood_penalty_red) // if you are persistent and the slight annoyance above does not stop you, I'll show you!
-               {
-                       this.v_angle_x = random() * 360;
-                       this.v_angle_y = random() * 360;
-                       // at least I'm not forcing retardedview by also assigning to angles_z
-                       this.fixangle = true;
-               }
-       }
-#endif
-}
-
-void PM_check_punch(entity this)
-{
-#ifdef SVQC
-       if (this.punchangle != '0 0 0')
-       {
-               float f = vlen(this.punchangle) - 10 * PHYS_INPUT_TIMELENGTH;
-               if (f > 0)
-                       this.punchangle = normalize(this.punchangle) * f;
-               else
-                       this.punchangle = '0 0 0';
-       }
-
-       if (this.punchvector != '0 0 0')
-       {
-               float f = vlen(this.punchvector) - 30 * PHYS_INPUT_TIMELENGTH;
-               if (f > 0)
-                       this.punchvector = normalize(this.punchvector) * f;
-               else
-                       this.punchvector = '0 0 0';
-       }
-#endif
-}
-
-// predict frozen movement, as frozen players CAN move in some cases
-void PM_check_frozen(entity this)
-{
-       if (!PHYS_FROZEN(this))
-               return;
-       if (PHYS_DODGING_FROZEN(this)
-#ifdef SVQC
-       && IS_REAL_CLIENT(this)
-#endif
-       )
-       {
-               this.movement_x = bound(-5, this.movement.x, 5);
-               this.movement_y = bound(-5, this.movement.y, 5);
-               this.movement_z = bound(-5, this.movement.z, 5);
-       }
-       else
-               this.movement = '0 0 0';
-
-       vector midpoint = ((this.absmin + this.absmax) * 0.5);
-       if (pointcontents(midpoint) == CONTENT_WATER)
-       {
-               this.velocity = this.velocity * 0.5;
-
-               if (pointcontents(midpoint + '0 0 16') == CONTENT_WATER)
-                       this.velocity_z = 200;
-       }
-}
-
-void PM_check_hitground(entity this)
-{
-#ifdef SVQC
-       if (!this.wasFlying) return;
-    this.wasFlying = false;
-    if (this.waterlevel >= WATERLEVEL_SWIMMING) return;
-    if (time < this.ladder_time) return;
-    if (this.hook) return;
-    this.nextstep = time + 0.3 + random() * 0.1;
-    trace_dphitq3surfaceflags = 0;
-    tracebox(this.origin, this.mins, this.maxs, this.origin - '0 0 1', MOVE_NOMONSTERS, this);
-    if (trace_dphitq3surfaceflags & Q3SURFACEFLAG_NOSTEPS) return;
-    entity gs = (trace_dphitq3surfaceflags & Q3SURFACEFLAG_METALSTEPS)
-       ? GS_FALL_METAL
-       : GS_FALL;
-    GlobalSound(this, gs, CH_PLAYER, VOICETYPE_PLAYERSOUND);
-#endif
-}
-
-void PM_Footsteps(entity this)
-{
-#ifdef SVQC
-       if (!g_footsteps) return;
-       if (IS_DUCKED(this)) return;
-       if (time >= this.lastground + 0.2) return;
-       if (vdist(this.velocity, <=, autocvar_sv_maxspeed * 0.6)) return;
-       if ((time > this.nextstep) || (time < (this.nextstep - 10.0)))
-       {
-               this.nextstep = time + 0.3 + random() * 0.1;
-               trace_dphitq3surfaceflags = 0;
-               tracebox(this.origin, this.mins, this.maxs, this.origin - '0 0 1', MOVE_NOMONSTERS, this);
-               if (trace_dphitq3surfaceflags & Q3SURFACEFLAG_NOSTEPS) return;
-               entity gs = (trace_dphitq3surfaceflags & Q3SURFACEFLAG_METALSTEPS)
-                       ? GS_STEP_METAL
-                       : GS_STEP;
-               GlobalSound(this, gs, CH_PLAYER, VOICETYPE_PLAYERSOUND);
-       }
-#endif
-}
-
-void PM_check_blocked(entity this)
-{
-#ifdef SVQC
-       if (!this.player_blocked)
-               return;
-       this.movement = '0 0 0';
-       this.disableclientprediction = 1;
-#endif
-}
-
-void PM_fly(entity this, float maxspd_mod)
-{
-       // noclipping or flying
-       UNSET_ONGROUND(this);
-
-       this.velocity = this.velocity * (1 - PHYS_INPUT_TIMELENGTH * PHYS_FRICTION(this));
-       makevectors(this.v_angle);
-       //wishvel = v_forward * this.movement.x + v_right * this.movement.y + v_up * this.movement.z;
-       vector wishvel = v_forward * this.movement.x
-                                       + v_right * this.movement.y
-                                       + '0 0 1' * this.movement.z;
-       // acceleration
-       vector wishdir = normalize(wishvel);
-       float wishspeed = min(vlen(wishvel), PHYS_MAXSPEED(this) * maxspd_mod);
-#ifdef SVQC
-       if(time >= PHYS_TELEPORT_TIME(this))
-#endif
-               PM_Accelerate(this, wishdir, wishspeed, wishspeed, PHYS_ACCELERATE(this) * maxspd_mod, 1, 0, 0, 0);
-       PM_ClientMovement_Move(this);
-}
-
-void PM_swim(entity this, float maxspd_mod)
-{
-       // swimming
-       UNSET_ONGROUND(this);
-
-       float jump = PHYS_INPUT_BUTTON_JUMP(this);
-       // water jump only in certain situations
-       // this mimics quakeworld code
-       if (jump && this.waterlevel == WATERLEVEL_SWIMMING && this.velocity_z >= -180 && !this.viewloc)
-       {
-               vector yawangles = '0 1 0' * this.v_angle.y;
-               makevectors(yawangles);
-               vector forward = v_forward;
-               vector spot = this.origin + 24 * forward;
-               spot_z += 8;
-               traceline(spot, spot, MOVE_NOMONSTERS, this);
-               if (trace_startsolid)
-               {
-                       spot_z += 24;
-                       traceline(spot, spot, MOVE_NOMONSTERS, this);
-                       if (!trace_startsolid)
-                       {
-                               this.velocity = forward * 50;
-                               this.velocity_z = 310;
-                       #ifdef CSQC
-                               pmove_waterjumptime = 2;
-                       #endif
-                               UNSET_ONGROUND(this);
-                               SET_JUMP_HELD(this);
-                       }
-               }
-       }
-       makevectors(this.v_angle);
-       //wishvel = v_forward * this.movement.x + v_right * this.movement.y + v_up * this.movement.z;
-       vector wishvel = v_forward * this.movement.x
-                                       + v_right * this.movement.y
-                                       + '0 0 1' * this.movement.z;
-       if(this.viewloc)
-               wishvel.z = -160; // drift anyway
-       else if (wishvel == '0 0 0')
-               wishvel = '0 0 -60'; // drift towards bottom
-
-
-       vector wishdir = normalize(wishvel);
-       float wishspeed = min(vlen(wishvel), PHYS_MAXSPEED(this) * maxspd_mod) * 0.7;
-
-       if (IS_DUCKED(this))
-       wishspeed *= 0.5;
-
-//     if (pmove_waterjumptime <= 0) // TODO: use
-    {
-               // water friction
-               float f = 1 - PHYS_INPUT_TIMELENGTH * PHYS_FRICTION(this);
-               f = min(max(0, f), 1);
-               this.velocity *= f;
-
-               f = wishspeed - this.velocity * wishdir;
-               if (f > 0)
-               {
-                       float accelspeed = min(PHYS_ACCELERATE(this) * PHYS_INPUT_TIMELENGTH * wishspeed, f);
-                       this.velocity += accelspeed * wishdir;
-               }
-
-               // holding jump button swims upward slowly
-               if (jump && !this.viewloc)
-               {
-#if 0
-                       if (this.watertype & CONTENT_LAVA)
-                               this.velocity_z =  50;
-                       else if (this.watertype & CONTENT_SLIME)
-                               this.velocity_z =  80;
-                       else
-                       {
-                               if (IS_NEXUIZ_DERIVED(gamemode))
-#endif
-                                       this.velocity_z = 200;
-#if 0
-                               else
-                                       this.velocity_z = 100;
-                       }
-#endif
-               }
-       }
-       if(this.viewloc)
-       {
-               const float addspeed = wishspeed - this.velocity * wishdir;
-               if (addspeed > 0)
-               {
-                       const float accelspeed = min(PHYS_ACCELERATE(this) * PHYS_INPUT_TIMELENGTH * wishspeed, addspeed);
-                       this.velocity += accelspeed * wishdir;
-               }
-       }
-       else
-       {
-               // water acceleration
-               PM_Accelerate(this, wishdir, wishspeed, wishspeed, PHYS_ACCELERATE(this) * maxspd_mod, 1, 0, 0, 0);
-               PM_ClientMovement_Move(this);
-       }
-}
-
-.vector oldmovement;
-void PM_ladder(entity this, float maxspd_mod)
-{
-       // on a spawnfunc_func_ladder or swimming in spawnfunc_func_water
-       UNSET_ONGROUND(this);
-
-       float g;
-       g = PHYS_GRAVITY(this) * PHYS_INPUT_TIMELENGTH;
-       if (PHYS_ENTGRAVITY(this))
-               g *= PHYS_ENTGRAVITY(this);
-       if (GAMEPLAYFIX_GRAVITYUNAFFECTEDBYTICRATE)
-       {
-               g *= 0.5;
-               this.velocity_z += g;
-       }
-
-       this.velocity = this.velocity * (1 - PHYS_INPUT_TIMELENGTH * PHYS_FRICTION(this));
-       makevectors(this.v_angle);
-       //wishvel = v_forward * this.movement.x + v_right * this.movement.y + v_up * this.movement.z;
-       vector wishvel = v_forward * this.movement_x
-                                       + v_right * this.movement_y
-                                       + '0 0 1' * this.movement_z;
-       if(this.viewloc)
-               wishvel.z = this.oldmovement.x;
-       this.velocity_z += g;
-       if (this.ladder_entity.classname == "func_water")
-       {
-               float f = vlen(wishvel);
-               if (f > this.ladder_entity.speed)
-                       wishvel *= (this.ladder_entity.speed / f);
-
-               this.watertype = this.ladder_entity.skin;
-               f = this.ladder_entity.origin_z + this.ladder_entity.maxs_z;
-               if ((this.origin_z + this.view_ofs_z) < f)
-                       this.waterlevel = WATERLEVEL_SUBMERGED;
-               else if ((this.origin_z + (this.mins_z + this.maxs_z) * 0.5) < f)
-                       this.waterlevel = WATERLEVEL_SWIMMING;
-               else if ((this.origin_z + this.mins_z + 1) < f)
-                       this.waterlevel = WATERLEVEL_WETFEET;
-               else
-               {
-                       this.waterlevel = WATERLEVEL_NONE;
-                       this.watertype = CONTENT_EMPTY;
-               }
-       }
-       // acceleration
-       vector wishdir = normalize(wishvel);
-       float wishspeed = min(vlen(wishvel), PHYS_MAXSPEED(this) * maxspd_mod);
-       if(time >= PHYS_TELEPORT_TIME(this))
-               // water acceleration
-               PM_Accelerate(this, wishdir, wishspeed, wishspeed, PHYS_ACCELERATE(this)*maxspd_mod, 1, 0, 0, 0);
-       PM_ClientMovement_Move(this);
-}
-
-void PM_jetpack(entity this, float maxspd_mod)
-{
-       //makevectors(this.v_angle.y * '0 1 0');
-       makevectors(this.v_angle);
-       vector wishvel = v_forward * this.movement_x
-                                       + v_right * this.movement_y;
-       // add remaining speed as Z component
-       float maxairspd = PHYS_MAXAIRSPEED(this) * 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 (this.BUTTON_JUMP)
-               wishvel_z = sqrt(max(0, 1 - wishvel * wishvel));
-
-       // it is now normalized, so...
-       float a_side = PHYS_JETPACK_ACCEL_SIDE(this);
-       float a_up = PHYS_JETPACK_ACCEL_UP(this);
-       float a_add = PHYS_JETPACK_ANTIGRAVITY(this) * PHYS_GRAVITY(this);
-
-       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 - (this.velocity * normalize(wishvel_x * '1 0 0' + wishvel_y * '0 1 0')) / PHYS_JETPACK_MAXSPEED_SIDE(this), 1);
-       if (wishvel_z - PHYS_GRAVITY(this) > 0)
-               fz = bound(0, 1 - this.velocity_z / PHYS_JETPACK_MAXSPEED_UP(this), 1);
-       else
-               fz = bound(0, 1 + this.velocity_z / PHYS_JETPACK_MAXSPEED_UP(this), 1);
-
-       float fvel;
-       fvel = vlen(wishvel);
-       wishvel_x *= fxy;
-       wishvel_y *= fxy;
-       wishvel_z = (wishvel_z - PHYS_GRAVITY(this)) * fz + PHYS_GRAVITY(this);
-
-       fvel = min(1, vlen(wishvel) / best);
-       if (PHYS_JETPACK_FUEL(this) && !(ITEMS_STAT(this) & IT_UNLIMITED_WEAPON_AMMO))
-               f = min(1, PHYS_AMMO_FUEL(this) / (PHYS_JETPACK_FUEL(this) * PHYS_INPUT_TIMELENGTH * fvel));
-       else
-               f = 1;
-
-       //print("this acceleration: ", ftos(vlen(wishvel) * f), "\n");
-
-       if (f > 0 && wishvel != '0 0 0')
-       {
-               this.velocity = this.velocity + wishvel * f * PHYS_INPUT_TIMELENGTH;
-               UNSET_ONGROUND(this);
-
-#ifdef SVQC
-               if (!(ITEMS_STAT(this) & IT_UNLIMITED_WEAPON_AMMO))
-                       this.ammo_fuel -= PHYS_JETPACK_FUEL(this) * PHYS_INPUT_TIMELENGTH * fvel * f;
-
-               ITEMS_STAT(this) |= IT_USING_JETPACK;
-
-               // jetpack also inhibits health regeneration, but only for 1 second
-               this.pauseregen_finished = max(this.pauseregen_finished, time + autocvar_g_balance_pause_fuel_regen);
-#endif
-       }
-
-#ifdef CSQC
-       float g = PHYS_GRAVITY(this) * PHYS_ENTGRAVITY(this) * PHYS_INPUT_TIMELENGTH;
-       if(autocvar_cl_movement == 3)
-       {
-               if (GAMEPLAYFIX_GRAVITYUNAFFECTEDBYTICRATE)
-                       this.velocity_z -= g * 0.5;
-               else
-                       this.velocity_z -= g;
-       }
-       PM_ClientMovement_Move(this);
-       if(autocvar_cl_movement == 3)
-       {
-               if (!IS_ONGROUND(this) || !(GAMEPLAYFIX_NOGRAVITYONGROUND))
-                       if (GAMEPLAYFIX_GRAVITYUNAFFECTEDBYTICRATE)
-                               this.velocity_z -= g * 0.5;
-       }
-#endif
-}
-
-void PM_walk(entity this, float maxspd_mod)
-{
-       if (!WAS_ONGROUND(this))
-       {
-#ifdef SVQC
-               if (autocvar_speedmeter)
-                       LOG_TRACE(strcat("landing velocity: ", vtos(this.velocity), " (abs: ", ftos(vlen(this.velocity)), ")\n"));
-#endif
-               if (this.lastground < time - 0.3)
-                       this.velocity *= (1 - PHYS_FRICTION_ONLAND(this));
-#ifdef SVQC
-               if (this.jumppadcount > 1)
-                       LOG_TRACE(strcat(ftos(this.jumppadcount), "x jumppad combo\n"));
-               this.jumppadcount = 0;
-#endif
-       }
-
-       // walking
-       makevectors(this.v_angle.y * '0 1 0');
-       const vector wishvel = v_forward * this.movement.x
-                                               + v_right * this.movement.y;
-       // acceleration
-       const vector wishdir = normalize(wishvel);
-       float wishspeed = vlen(wishvel);
-       wishspeed = min(wishspeed, PHYS_MAXSPEED(this) * maxspd_mod);
-       if (IS_DUCKED(this)) wishspeed *= 0.5;
-
-       // apply edge friction
-       const float f2 = vlen2(vec2(this.velocity));
-       if (f2 > 0)
-       {
-               trace_dphitq3surfaceflags = 0;
-               tracebox(this.origin, this.mins, this.maxs, this.origin - '0 0 1', MOVE_NOMONSTERS, this);
-               // TODO: apply edge friction
-               // apply ground friction
-               const int realfriction = (trace_dphitq3surfaceflags & Q3SURFACEFLAG_SLICK)
-                       ? PHYS_FRICTION_SLICK(this)
-                       : PHYS_FRICTION(this);
-
-               float f = sqrt(f2);
-               f = 1 - PHYS_INPUT_TIMELENGTH * realfriction * ((f < PHYS_STOPSPEED(this)) ? (PHYS_STOPSPEED(this) / f) : 1);
-               f = max(0, f);
-               this.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(this) / v0) * PHYS_FRICTION(this))
-                         = v0 - PHYS_INPUT_TIMELENGTH * PHYS_STOPSPEED(this) * PHYS_FRICTION(this)
-                       v0 = v + PHYS_INPUT_TIMELENGTH * PHYS_STOPSPEED(this) * PHYS_FRICTION(this)
-                  and
-                       v = v0 * (1 - PHYS_INPUT_TIMELENGTH * PHYS_FRICTION(this))
-                       v0 = v / (1 - PHYS_INPUT_TIMELENGTH * PHYS_FRICTION(this))
-
-                  These cases would be chosen ONLY if:
-                       v0 < PHYS_STOPSPEED(this)
-                       v + PHYS_INPUT_TIMELENGTH * PHYS_STOPSPEED(this) * PHYS_FRICTION(this) < PHYS_STOPSPEED(this)
-                       v < PHYS_STOPSPEED(this) * (1 - PHYS_INPUT_TIMELENGTH * PHYS_FRICTION(this))
-                  and, respectively:
-                       v0 >= PHYS_STOPSPEED(this)
-                       v / (1 - PHYS_INPUT_TIMELENGTH * PHYS_FRICTION(this)) >= PHYS_STOPSPEED(this)
-                       v >= PHYS_STOPSPEED(this) * (1 - PHYS_INPUT_TIMELENGTH * PHYS_FRICTION(this))
-                */
-       }
-       const float addspeed = wishspeed - this.velocity * wishdir;
-       if (addspeed > 0)
-       {
-               const float accelspeed = min(PHYS_ACCELERATE(this) * PHYS_INPUT_TIMELENGTH * wishspeed, addspeed);
-               this.velocity += accelspeed * wishdir;
-       }
-#ifdef CSQC
-       float g = PHYS_GRAVITY(this) * PHYS_ENTGRAVITY(this) * PHYS_INPUT_TIMELENGTH;
-       if(autocvar_cl_movement == 3)
-       {
-               if (!(GAMEPLAYFIX_NOGRAVITYONGROUND))
-                       this.velocity_z -= g * (GAMEPLAYFIX_GRAVITYUNAFFECTEDBYTICRATE ? 0.5 : 1);
-       }
-       if (vdist(this.velocity, >, 0))
-               PM_ClientMovement_Move(this);
-       if(autocvar_cl_movement == 3)
-       {
-               if (GAMEPLAYFIX_GRAVITYUNAFFECTEDBYTICRATE)
-                       if (!IS_ONGROUND(this) || !GAMEPLAYFIX_NOGRAVITYONGROUND)
-                               this.velocity_z -= g * 0.5;
-       }
-#endif
-}
-
-void PM_air(entity this, float buttons_prev, float maxspd_mod)
-{
-       makevectors(this.v_angle.y * '0 1 0');
-       vector wishvel = v_forward * this.movement.x
-                                       + v_right * this.movement.y;
-       // acceleration
-       vector wishdir = normalize(wishvel);
-       float wishspeed = vlen(wishvel);
-
-#ifdef SVQC
-       if(time >= PHYS_TELEPORT_TIME(this))
-#elif defined(CSQC)
-       if(pmove_waterjumptime <= 0)
-#endif
-       {
-               float maxairspd = PHYS_MAXAIRSPEED(this) * min(maxspd_mod, 1);
-
-               // apply air speed limit
-               float airaccelqw = PHYS_AIRACCEL_QW(this);
-               float wishspeed0 = wishspeed;
-               wishspeed = min(wishspeed, maxairspd);
-               if (IS_DUCKED(this))
-                       wishspeed *= 0.5;
-               float airaccel = PHYS_AIRACCELERATE(this) * min(maxspd_mod, 1);
-
-               float accelerating = (this.velocity * wishdir > 0);
-               float wishspeed2 = wishspeed;
-
-               // CPM: air control
-               if (PHYS_AIRSTOPACCELERATE(this))
-               {
-                       vector curdir = normalize(vec2(this.velocity));
-                       airaccel += (PHYS_AIRSTOPACCELERATE(this)*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(this.movement, -90) + IsMoveInDirection(this.movement, +90); // if one is nonzero, other is always zero
-               if (PHYS_MAXAIRSTRAFESPEED(this))
-                       wishspeed = min(wishspeed, GeomLerp(PHYS_MAXAIRSPEED(this)*maxspd_mod, strafity, PHYS_MAXAIRSTRAFESPEED(this)*maxspd_mod));
-               if (PHYS_AIRSTRAFEACCELERATE(this))
-                       airaccel = GeomLerp(airaccel, strafity, PHYS_AIRSTRAFEACCELERATE(this)*maxspd_mod);
-               if (PHYS_AIRSTRAFEACCEL_QW(this))
-                       airaccelqw =
-               (((strafity > 0.5 ? PHYS_AIRSTRAFEACCEL_QW(this) : PHYS_AIRACCEL_QW(this)) >= 0) ? +1 : -1)
-               *
-               (1 - GeomLerp(1 - fabs(PHYS_AIRACCEL_QW(this)), strafity, 1 - fabs(PHYS_AIRSTRAFEACCEL_QW(this))));
-               // !CPM
-
-               if (PHYS_WARSOWBUNNY_TURNACCEL(this) && accelerating && this.movement.y == 0 && this.movement.x != 0)
-                       PM_AirAccelerate(this, wishdir, wishspeed2);
-               else
-                       PM_Accelerate(this, wishdir, wishspeed, wishspeed0, airaccel, airaccelqw, PHYS_AIRACCEL_QW_STRETCHFACTOR(this), PHYS_AIRACCEL_SIDEWAYS_FRICTION(this) / maxairspd, PHYS_AIRSPEEDLIMIT_NONQW(this));
-
-               if (PHYS_AIRCONTROL(this))
-                       CPM_PM_Aircontrol(this, wishdir, wishspeed2);
-       }
-#ifdef CSQC
-       float g = PHYS_GRAVITY(this) * PHYS_ENTGRAVITY(this) * PHYS_INPUT_TIMELENGTH;
-       if(autocvar_cl_movement == 3)
-       if (GAMEPLAYFIX_GRAVITYUNAFFECTEDBYTICRATE)
-               this.velocity_z -= g * 0.5;
-       else
-               this.velocity_z -= g;
-#endif
-       PM_ClientMovement_Move(this);
-#ifdef CSQC
-       if(autocvar_cl_movement == 3)
-       if (!IS_ONGROUND(this) || !(GAMEPLAYFIX_NOGRAVITYONGROUND))
-               if (GAMEPLAYFIX_GRAVITYUNAFFECTEDBYTICRATE)
-                       this.velocity_z -= g * 0.5;
-#endif
-}
-
-// used for calculating airshots
-bool IsFlying(entity this)
-{
-       if(IS_ONGROUND(this))
-               return false;
-       if(this.waterlevel >= WATERLEVEL_SWIMMING)
-               return false;
-       traceline(this.origin, this.origin - '0 0 48', MOVE_NORMAL, this);
-       if(trace_fraction < 1)
-               return false;
-       return true;
-}
-
-void PM_Main(entity this)
-{
-       int buttons = PHYS_INPUT_BUTTON_MASK(this);
-#ifdef CSQC
-       this.items = STAT(ITEMS);
-
-       this.movement = PHYS_INPUT_MOVEVALUES(this);
-
-       this.spectatorspeed = STAT(SPECTATORSPEED);
-
-       vector oldv_angle = this.v_angle;
-       vector oldangles = this.angles; // we need to save these, as they're abused by other code
-       this.v_angle = PHYS_INPUT_ANGLES(this);
-       this.angles = PHYS_WORLD_ANGLES(this);
-
-       this.team = myteam + 1; // is this correct?
-       if (!(PHYS_INPUT_BUTTON_JUMP(this))) // !jump
-               UNSET_JUMP_HELD(this); // canjump = true
-       pmove_waterjumptime -= PHYS_INPUT_TIMELENGTH;
-
-       PM_ClientMovement_UpdateStatus(this, true);
-#endif
-
-       this.oldmovement = this.movement;
-
-
-#ifdef SVQC
-       WarpZone_PlayerPhysics_FixVAngle();
-#endif
-       float maxspeed_mod = 1;
-       maxspeed_mod *= PHYS_HIGHSPEED(this);
-
-#ifdef SVQC
-       Physics_UpdateStats(this, maxspeed_mod);
-
-       if (this.PlayerPhysplug)
-               if (this.PlayerPhysplug())
-                       return;
-#endif
-
-#ifdef SVQC
-       anticheat_physics(this);
-#endif
-
-       if (PM_check_specialcommand(this, buttons))
-               return;
-#ifdef SVQC
-       if (sv_maxidle > 0)
-       {
-               if (buttons != this.buttons_old || this.movement != this.movement_old || this.v_angle != this.v_angle_old)
-                       this.parm_idlesince = time;
-       }
-#endif
-       int buttons_prev = this.buttons_old;
-       this.buttons_old = buttons;
-       this.movement_old = this.movement;
-       this.v_angle_old = this.v_angle;
-
-       PM_check_nickspam(this);
-
-       PM_check_punch(this);
-#ifdef SVQC
-       if (IS_BOT_CLIENT(this))
-       {
-               if (playerdemo_read(this))
-                       return;
-               WITH(entity, self, this, bot_think());
-       }
-#endif
-
-#ifdef SVQC
-       if (IS_PLAYER(this))
-       {
-               const bool allowed_to_move = (time >= game_starttime);
-               if (!allowed_to_move)
-               {
-                       this.velocity = '0 0 0';
-                       this.movetype = MOVETYPE_NONE;
-                       this.disableclientprediction = 2;
-               }
-               else if (this.disableclientprediction == 2)
-               {
-                       if (this.movetype == MOVETYPE_NONE)
-                               this.movetype = MOVETYPE_WALK;
-                       this.disableclientprediction = 0;
-               }
-       }
-#endif
-
-#ifdef SVQC
-       if (this.movetype == MOVETYPE_NONE)
-               return;
-
-       // when we get here, disableclientprediction cannot be 2
-       this.disableclientprediction = 0;
-#endif
-
-       viewloc_PlayerPhysics(this);
-
-       PM_check_frozen(this);
-
-       PM_check_blocked(this);
-
-       maxspeed_mod = 1;
-
-       if (this.in_swamp)
-               maxspeed_mod *= this.swamp_slowdown; //cvar("g_balance_swamp_moverate");
-
-       // conveyors: first fix velocity
-       if (this.conveyor.state)
-               this.velocity -= this.conveyor.movedir;
-
-       MUTATOR_CALLHOOK(PlayerPhysics, this);
-
-       if (!IS_PLAYER(this))
-       {
-#ifdef SVQC
-               maxspeed_mod = autocvar_sv_spectator_speed_multiplier;
-               if (!this.spectatorspeed)
-                       this.spectatorspeed = maxspeed_mod;
-               if (this.impulse && this.impulse <= 19 || (this.impulse >= 200 && this.impulse <= 209) || (this.impulse >= 220 && this.impulse <= 229))
-               {
-                       if (this.lastclassname != "player")
-                       {
-                               if (this.impulse == 10 || this.impulse == 15 || this.impulse == 18 || (this.impulse >= 200 && this.impulse <= 209))
-                                       this.spectatorspeed = bound(1, this.spectatorspeed + 0.5, 5);
-                               else if (this.impulse == 11)
-                                       this.spectatorspeed = maxspeed_mod;
-                               else if (this.impulse == 12 || this.impulse == 16  || this.impulse == 19 || (this.impulse >= 220 && this.impulse <= 229))
-                                       this.spectatorspeed = bound(1, this.spectatorspeed - 0.5, 5);
-                               else if (this.impulse >= 1 && this.impulse <= 9)
-                                       this.spectatorspeed = 1 + 0.5 * (this.impulse - 1);
-                       } // otherwise just clear
-                       this.impulse = 0;
-               }
-#endif
-               maxspeed_mod = this.spectatorspeed;
-       }
-#ifdef SVQC
-
-       float spd = max(PHYS_MAXSPEED(this), PHYS_MAXAIRSPEED(this)) * maxspeed_mod;
-       if(this.speed != spd)
-       {
-               this.speed = spd;
-               string temps = ftos(spd);
-               stuffcmd(this, strcat("cl_forwardspeed ", temps, "\n"));
-               stuffcmd(this, strcat("cl_backspeed ", temps, "\n"));
-               stuffcmd(this, strcat("cl_sidespeed ", temps, "\n"));
-               stuffcmd(this, strcat("cl_upspeed ", temps, "\n"));
-       }
-
-       if(this.jumpspeedcap_min != autocvar_sv_jumpspeedcap_min)
-       {
-               this.jumpspeedcap_min = autocvar_sv_jumpspeedcap_min;
-               stuffcmd(this, sprintf("\ncl_jumpspeedcap_min \"%s\"\n", autocvar_sv_jumpspeedcap_min));
-       }
-       if(this.jumpspeedcap_max != autocvar_sv_jumpspeedcap_max)
-       {
-               this.jumpspeedcap_max = autocvar_sv_jumpspeedcap_max;
-               stuffcmd(this, sprintf("\ncl_jumpspeedcap_max \"%s\"\n", autocvar_sv_jumpspeedcap_max));
-       }
-#endif
-
-       if(PHYS_DEAD(this))
-       {
-               // handle water here
-               vector midpoint = ((this.absmin + this.absmax) * 0.5);
-               if(pointcontents(midpoint) == CONTENT_WATER)
-               {
-                       this.velocity = this.velocity * 0.5;
-
-                       // do we want this?
-                       //if(pointcontents(midpoint + '0 0 2') == CONTENT_WATER)
-                               //{ this.velocity_z = 70; }
-               }
-               goto end;
-       }
-
-#ifdef SVQC
-       if (!this.fixangle)
-               this.angles = '0 1 0' * this.v_angle.y;
-#endif
-
-       if (IS_PLAYER(this) && IS_ONGROUND(this))
-       {
-               PM_check_hitground(this);
-               PM_Footsteps(this);
-       }
-
-       if(IsFlying(this))
-               this.wasFlying = 1;
-
-       if (IS_PLAYER(this))
-               CheckPlayerJump(this);
-
-       if (this.flags & FL_WATERJUMP)
-       {
-               this.velocity_x = this.movedir.x;
-               this.velocity_y = this.movedir.y;
-               if (time > PHYS_TELEPORT_TIME(this) || this.waterlevel == WATERLEVEL_NONE)
-               {
-                       this.flags &= ~FL_WATERJUMP;
-                       PHYS_TELEPORT_TIME(this) = 0;
-               }
-       }
-
-       else if (MUTATOR_CALLHOOK(PM_Physics, this, maxspeed_mod))
-               { }
-
-#ifdef SVQC
-       else if (this.movetype == MOVETYPE_NOCLIP || this.movetype == MOVETYPE_FLY || this.movetype == MOVETYPE_FLY_WORLDONLY || MUTATOR_CALLHOOK(IsFlying, this))
-#elif defined(CSQC)
-       else if (this.move_movetype == MOVETYPE_NOCLIP || this.move_movetype == MOVETYPE_FLY || this.move_movetype == MOVETYPE_FLY_WORLDONLY || MUTATOR_CALLHOOK(IsFlying, this))
-#endif
-               PM_fly(this, maxspeed_mod);
-
-       else if (this.waterlevel >= WATERLEVEL_SWIMMING)
-               PM_swim(this, maxspeed_mod);
-
-       else if (time < this.ladder_time)
-               PM_ladder(this, maxspeed_mod);
-
-       else if (ITEMS_STAT(this) & IT_USING_JETPACK)
-               PM_jetpack(this, maxspeed_mod);
-
-       else if (IS_ONGROUND(this))
-               PM_walk(this, maxspeed_mod);
-
-       else
-               PM_air(this, buttons_prev, maxspeed_mod);
-
-:end
-       if (IS_ONGROUND(this))
-               this.lastground = time;
-
-       // conveyors: then break velocity again
-       if(this.conveyor.state)
-               this.velocity += this.conveyor.movedir;
-
-       this.lastflags = this.flags;
-
-       this.lastclassname = this.classname;
-
-#ifdef CSQC
-       this.v_angle = oldv_angle;
-       this.angles = oldangles;
-#endif
-}
-
-#if defined(SVQC)
-void SV_PlayerPhysics()
-#elif defined(CSQC)
-void CSQC_ClientMovement_PlayerMove_Frame(entity this)
-#endif
-{
-#ifdef SVQC
-       SELFPARAM();
-#endif
-       PM_Main(this);
-}
diff --git a/qcsrc/common/physics.qh b/qcsrc/common/physics.qh
deleted file mode 100644 (file)
index 66824bb..0000000
+++ /dev/null
@@ -1,310 +0,0 @@
-#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;
-#ifdef SVQC
-.float spectatorspeed = _STAT(SPECTATORSPEED);
-#elif defined(CSQC)
-.float spectatorspeed;
-#endif
-
-.vector movement_old;
-.float buttons_old;
-.vector v_angle_old;
-.string lastclassname;
-
-.float() PlayerPhysplug;
-float AdjustAirAccelQW(float accelqw, float factor);
-
-bool IsFlying(entity a);
-
-#define BUFFS_STAT(s)                       STAT(BUFFS, s)
-
-#define GAMEPLAYFIX_DOWNTRACEONGROUND(s)    STAT(GAMEPLAYFIX_DOWNTRACEONGROUND, s)
-#define GAMEPLAYFIX_EASIERWATERJUMP(s)      STAT(GAMEPLAYFIX_EASIERWATERJUMP, s)
-#define GAMEPLAYFIX_STEPDOWN(s)             STAT(GAMEPLAYFIX_STEPDOWN, s)
-#define GAMEPLAYFIX_STEPMULTIPLETIMES(s)    STAT(GAMEPLAYFIX_STEPMULTIPLETIMES, s)
-#define GAMEPLAYFIX_UNSTICKPLAYERS(s)       STAT(GAMEPLAYFIX_UNSTICKPLAYERS, s)
-
-#define PHYS_ACCELERATE(s)                  STAT(MOVEVARS_ACCELERATE, s)
-#define PHYS_AIRACCELERATE(s)               STAT(MOVEVARS_AIRACCELERATE, s)
-#define PHYS_AIRACCEL_QW(s)                 STAT(MOVEVARS_AIRACCEL_QW, s)
-#define PHYS_AIRACCEL_QW_STRETCHFACTOR(s)   STAT(MOVEVARS_AIRACCEL_QW_STRETCHFACTOR, s)
-#define PHYS_AIRACCEL_SIDEWAYS_FRICTION(s)  STAT(MOVEVARS_AIRACCEL_SIDEWAYS_FRICTION, s)
-#define PHYS_AIRCONTROL(s)                  STAT(MOVEVARS_AIRCONTROL, s)
-#define PHYS_AIRCONTROL_PENALTY(s)          STAT(MOVEVARS_AIRCONTROL_PENALTY, s)
-#define PHYS_AIRCONTROL_POWER(s)            STAT(MOVEVARS_AIRCONTROL_POWER, s)
-#define PHYS_AIRSPEEDLIMIT_NONQW(s)         STAT(MOVEVARS_AIRSPEEDLIMIT_NONQW, s)
-#define PHYS_AIRSTOPACCELERATE(s)           STAT(MOVEVARS_AIRSTOPACCELERATE, s)
-#define PHYS_AIRSTRAFEACCELERATE(s)         STAT(MOVEVARS_AIRSTRAFEACCELERATE, s)
-#define PHYS_AIRSTRAFEACCEL_QW(s)           STAT(MOVEVARS_AIRSTRAFEACCEL_QW, s)
-
-#define PHYS_AMMO_FUEL(s)                   STAT(FUEL, s)
-
-#define PHYS_DODGING_FROZEN(s)              STAT(DODGING_FROZEN, s)
-
-#define PHYS_FRICTION(s)                    STAT(MOVEVARS_FRICTION, s)
-#define PHYS_FRICTION_ONLAND(s)             STAT(MOVEVARS_FRICTION_ONLAND, s)
-#define PHYS_FRICTION_SLICK(s)              STAT(MOVEVARS_FRICTION_SLICK, s)
-
-#define PHYS_FROZEN(s)                      STAT(FROZEN, s)
-
-#define PHYS_HIGHSPEED(s)                   STAT(MOVEVARS_HIGHSPEED, s)
-
-#define PHYS_JETPACK_ACCEL_SIDE(s)          STAT(JETPACK_ACCEL_SIDE, s)
-#define PHYS_JETPACK_ACCEL_UP(s)            STAT(JETPACK_ACCEL_UP, s)
-#define PHYS_JETPACK_ANTIGRAVITY(s)         STAT(JETPACK_ANTIGRAVITY, s)
-#define PHYS_JETPACK_FUEL(s)                STAT(JETPACK_FUEL, s)
-#define PHYS_JETPACK_MAXSPEED_SIDE(s)       STAT(JETPACK_MAXSPEED_SIDE, s)
-#define PHYS_JETPACK_MAXSPEED_UP(s)         STAT(JETPACK_MAXSPEED_UP, s)
-
-#define PHYS_JUMPSPEEDCAP_DISABLE_ONRAMPS(s) STAT(MOVEVARS_JUMPSPEEDCAP_DISABLE_ONRAMPS, s)
-#define PHYS_JUMPSTEP(s)                    STAT(MOVEVARS_JUMPSTEP, s)
-#define PHYS_JUMPVELOCITY(s)                STAT(MOVEVARS_JUMPVELOCITY, s)
-
-#define PHYS_MAXAIRSPEED(s)                 STAT(MOVEVARS_MAXAIRSPEED, s)
-#define PHYS_MAXAIRSTRAFESPEED(s)           STAT(MOVEVARS_MAXAIRSTRAFESPEED, s)
-#define PHYS_MAXSPEED(s)                    STAT(MOVEVARS_MAXSPEED, s)
-
-#define PHYS_NOSTEP(s)                      STAT(NOSTEP, s)
-#define PHYS_STEPHEIGHT(s)                  STAT(MOVEVARS_STEPHEIGHT, s)
-
-#define PHYS_STOPSPEED(s)                   STAT(MOVEVARS_STOPSPEED, s)
-
-#define PHYS_TRACK_CANJUMP(s)               STAT(MOVEVARS_TRACK_CANJUMP, s)
-
-#define PHYS_WALLFRICTION(s)                STAT(MOVEVARS_WALLFRICTION, s)
-
-#define PHYS_WARSOWBUNNY_ACCEL(s)           STAT(MOVEVARS_WARSOWBUNNY_ACCEL, s)
-#define PHYS_WARSOWBUNNY_AIRFORWARDACCEL(s) STAT(MOVEVARS_WARSOWBUNNY_AIRFORWARDACCEL, s)
-#define PHYS_WARSOWBUNNY_BACKTOSIDERATIO(s) STAT(MOVEVARS_WARSOWBUNNY_BACKTOSIDERATIO, s)
-#define PHYS_WARSOWBUNNY_TOPSPEED(s)        STAT(MOVEVARS_WARSOWBUNNY_TOPSPEED, s)
-#define PHYS_WARSOWBUNNY_TURNACCEL(s)       STAT(MOVEVARS_WARSOWBUNNY_TURNACCEL, s)
-
-#define UPWARD_VELOCITY_CLEARS_ONGROUND(s)  STAT(GAMEPLAYFIX_UPVELOCITYCLEARSONGROUND, s)
-
-#define PHYS_INPUT_BUTTON_ATCK(s)           PHYS_INPUT_BUTTON_BUTTON1(s)
-#define PHYS_INPUT_BUTTON_JUMP(s)           PHYS_INPUT_BUTTON_BUTTON2(s)
-#define PHYS_INPUT_BUTTON_ATCK2(s)          PHYS_INPUT_BUTTON_BUTTON3(s)
-#define PHYS_INPUT_BUTTON_ZOOM(s)           PHYS_INPUT_BUTTON_BUTTON4(s)
-#define PHYS_INPUT_BUTTON_CROUCH(s)         PHYS_INPUT_BUTTON_BUTTON5(s)
-#define PHYS_INPUT_BUTTON_HOOK(s)           PHYS_INPUT_BUTTON_BUTTON6(s)
-#define PHYS_INPUT_BUTTON_INFO(s)           PHYS_INPUT_BUTTON_BUTTON7(s)
-#define PHYS_INPUT_BUTTON_DRAG(s)           PHYS_INPUT_BUTTON_BUTTON8(s)
-#define PHYS_INPUT_BUTTON_USE(s)            PHYS_INPUT_BUTTON_BUTTON_USE(s)
-#define PHYS_INPUT_BUTTON_CHAT(s)           PHYS_INPUT_BUTTON_BUTTON_CHAT(s)
-#define PHYS_INPUT_BUTTON_PRYDON(s)         PHYS_INPUT_BUTTON_BUTTON_PRYDON(s)
-#define PHYS_INPUT_BUTTON_ZOOMSCRIPT(s)     PHYS_INPUT_BUTTON_BUTTON9(s)
-#define PHYS_INPUT_BUTTON_JETPACK(s)        PHYS_INPUT_BUTTON_BUTTON10(s)
-
-// if more buttons are needed, start using impulse bits as buttons
-
-#define PHYS_INPUT_BUTTON_BACKWARD(s)       (PHYS_INPUT_MOVEVALUES(s).x < 0)
-#define PHYS_INPUT_BUTTON_FORWARD(s)        (PHYS_INPUT_MOVEVALUES(s).x > 0)
-#define PHYS_INPUT_BUTTON_LEFT(s)           (PHYS_INPUT_MOVEVALUES(s).y < 0)
-#define PHYS_INPUT_BUTTON_RIGHT(s)          (PHYS_INPUT_MOVEVALUES(s).y > 0)
-
-// used for special commands and idle checking, not from the engine
-// TODO: cache
-#define PHYS_INPUT_BUTTON_MASK(s) ( \
-         (1 <<  0) * PHYS_INPUT_BUTTON_ATCK(s) \
-       | (1 <<  1) * PHYS_INPUT_BUTTON_JUMP(s) \
-       | (1 <<  2) * PHYS_INPUT_BUTTON_ATCK2(s) \
-       | (1 <<  3) * PHYS_INPUT_BUTTON_ZOOM(s) \
-       | (1 <<  4) * PHYS_INPUT_BUTTON_CROUCH(s) \
-       | (1 <<  5) * PHYS_INPUT_BUTTON_HOOK(s) \
-       | (1 <<  6) * PHYS_INPUT_BUTTON_USE(s) \
-       | (1 <<  7) * PHYS_INPUT_BUTTON_BACKWARD(s) \
-       | (1 <<  8) * PHYS_INPUT_BUTTON_FORWARD(s) \
-       | (1 <<  9) * PHYS_INPUT_BUTTON_LEFT(s) \
-       | (1 << 10) * PHYS_INPUT_BUTTON_RIGHT(s) \
-       )
-
-#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)                      boolean((s).flags & FL_ONGROUND)
-#define SET_ONGROUND(s)                     ((s).flags |= FL_ONGROUND)
-#define UNSET_ONGROUND(s)                   ((s).flags &= ~FL_ONGROUND)
-
-#define WAS_ONGROUND(s)                     boolean((s).lastflags & FL_ONGROUND)
-
-#define ITEMS_STAT(s)                       ((s).items)
-
-#ifdef CSQC
-
-       string autocvar_cl_jumpspeedcap_min;
-       string autocvar_cl_jumpspeedcap_max;
-
-       noref float pmove_waterjumptime;
-
-       const int FL_WATERJUMP = 2048;  // player jumping out of water
-       const int FL_JUMPRELEASED = 4096;  // for jump debouncing
-
-       .float watertype;
-       .float waterlevel;
-       .int items;
-
-       .vector movement;
-       .vector v_angle;
-
-// TODO
-       #define IS_CLIENT(s)                        ((s).isplayermodel)
-       #define IS_PLAYER(s)                        ((s).isplayermodel)
-       #define IS_NOT_A_CLIENT(s)                  (!(s).isplayermodel)
-       #define isPushable(s)                       ((s).isplayermodel || (s).pushable || ((s).flags & FL_PROJECTILE))
-
-       //float player_multijump;
-       //float player_jumpheight;
-
-       #define PHYS_GRAVITY(s)                     STAT(MOVEVARS_GRAVITY, s)
-
-       #define PHYS_TELEPORT_TIME(s)               ((s).teleport_time)
-
-       #define TICRATE                             ticrate
-
-       #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_BUTTON1(s)        boolean(input_buttons & BIT(0))
-       #define PHYS_INPUT_BUTTON_BUTTON2(s)        boolean(input_buttons & BIT(1))
-       #define PHYS_INPUT_BUTTON_BUTTON3(s)        boolean(input_buttons & BIT(2))
-       #define PHYS_INPUT_BUTTON_BUTTON4(s)        boolean(input_buttons & BIT(3))
-       #define PHYS_INPUT_BUTTON_BUTTON5(s)        boolean(input_buttons & BIT(4))
-       #define PHYS_INPUT_BUTTON_BUTTON6(s)        boolean(input_buttons & BIT(5))
-       #define PHYS_INPUT_BUTTON_BUTTON7(s)        boolean(input_buttons & BIT(6))
-       #define PHYS_INPUT_BUTTON_BUTTON8(s)        boolean(input_buttons & BIT(7))
-       #define PHYS_INPUT_BUTTON_BUTTON_USE(s)     boolean(input_buttons & BIT(8))
-       #define PHYS_INPUT_BUTTON_BUTTON_CHAT(s)    boolean(input_buttons & BIT(9))
-       #define PHYS_INPUT_BUTTON_BUTTON_PRYDON(s)  boolean(input_buttons & BIT(10))
-       #define PHYS_INPUT_BUTTON_BUTTON9(s)        boolean(input_buttons & BIT(11))
-       #define PHYS_INPUT_BUTTON_BUTTON10(s)       boolean(input_buttons & BIT(12))
-       #define PHYS_INPUT_BUTTON_BUTTON11(s)       boolean(input_buttons & BIT(13))
-       #define PHYS_INPUT_BUTTON_BUTTON12(s)       boolean(input_buttons & BIT(14))
-       #define PHYS_INPUT_BUTTON_BUTTON13(s)       boolean(input_buttons & BIT(15))
-       #define PHYS_INPUT_BUTTON_BUTTON14(s)       boolean(input_buttons & BIT(16))
-       #define PHYS_INPUT_BUTTON_BUTTON15(s)       boolean(input_buttons & BIT(17))
-       #define PHYS_INPUT_BUTTON_BUTTON16(s)       boolean(input_buttons & BIT(18))
-
-       #define PHYS_DEAD(s)                        ((s).csqcmodel_isdead)
-
-       #define GAMEPLAYFIX_GRAVITYUNAFFECTEDBYTICRATE  (boolean(moveflags & MOVEFLAG_GRAVITYUNAFFECTEDBYTICRATE))
-       #define GAMEPLAYFIX_NOGRAVITYONGROUND           (boolean(moveflags & MOVEFLAG_NOGRAVITYONGROUND))
-       #define GAMEPLAYFIX_Q2AIRACCELERATE             (boolean(moveflags & MOVEFLAG_Q2AIRACCELERATE))
-
-       #define IS_DUCKED(s)                        (boolean((s).flags & FL_DUCKED))
-       #define SET_DUCKED(s)                       ((s).flags |= FL_DUCKED)
-       #define UNSET_DUCKED(s)                     ((s).flags &= ~FL_DUCKED)
-
-       #define PHYS_JUMPSPEEDCAP_MIN               autocvar_cl_jumpspeedcap_min
-       #define PHYS_JUMPSPEEDCAP_MAX               autocvar_cl_jumpspeedcap_max
-
-       #define PHYS_CL_TRACK_CANJUMP(s)            STAT(MOVEVARS_CL_TRACK_CANJUMP, s)
-       // FIXME: 0 doesn't mean zero gravity
-       #define PHYS_ENTGRAVITY(s)                  STAT(MOVEVARS_ENTGRAVITY, s)
-
-#elif defined(SVQC)
-
-       bool Physics_Valid(string thecvar);
-
-       .float stat_sv_airspeedlimit_nonqw = _STAT(MOVEVARS_AIRSPEEDLIMIT_NONQW);
-       .float stat_sv_maxspeed = _STAT(MOVEVARS_MAXSPEED);
-
-       /** Not real stats */
-       .string jumpspeedcap_min;
-       .string jumpspeedcap_max;
-
-       #define PHYS_TELEPORT_TIME(s)               ((s).teleport_time)
-
-       #define PHYS_GRAVITY(s)                     autocvar_sv_gravity
-
-       #define TICRATE sys_frametime
-
-       #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)
-
-       #define PHYS_INPUT_BUTTON_BUTTON1(s)        ((s).button0)
-       #define PHYS_INPUT_BUTTON_BUTTON2(s)        ((s).button2)
-       #define PHYS_INPUT_BUTTON_BUTTON3(s)        ((s).button3)
-       #define PHYS_INPUT_BUTTON_BUTTON4(s)        ((s).button4)
-       #define PHYS_INPUT_BUTTON_BUTTON5(s)        ((s).button5)
-       #define PHYS_INPUT_BUTTON_BUTTON6(s)        ((s).button6)
-       #define PHYS_INPUT_BUTTON_BUTTON7(s)        ((s).button7)
-       #define PHYS_INPUT_BUTTON_BUTTON8(s)        ((s).button8)
-       #define PHYS_INPUT_BUTTON_BUTTON_USE(s)     ((s).buttonuse)
-       #define PHYS_INPUT_BUTTON_BUTTON_CHAT(s)    ((s).buttonchat)
-       #define PHYS_INPUT_BUTTON_BUTTON_PRYDON(s)  ((s).cursor_active)
-       #define PHYS_INPUT_BUTTON_BUTTON9(s)        ((s).button9)
-       #define PHYS_INPUT_BUTTON_BUTTON10(s)       ((s).button10)
-       #define PHYS_INPUT_BUTTON_BUTTON11(s)       ((s).button11)
-       #define PHYS_INPUT_BUTTON_BUTTON12(s)       ((s).button12)
-       #define PHYS_INPUT_BUTTON_BUTTON13(s)       ((s).button13)
-       #define PHYS_INPUT_BUTTON_BUTTON14(s)       ((s).button14)
-       #define PHYS_INPUT_BUTTON_BUTTON15(s)       ((s).button15)
-       #define PHYS_INPUT_BUTTON_BUTTON16(s)       ((s).button16)
-
-       #define PHYS_DEAD(s)                        ((s).deadflag != DEAD_NO)
-
-       #define GAMEPLAYFIX_GRAVITYUNAFFECTEDBYTICRATE  autocvar_sv_gameplayfix_gravityunaffectedbyticrate
-       #define GAMEPLAYFIX_NOGRAVITYONGROUND           autocvar_sv_gameplayfix_nogravityonground
-       #define GAMEPLAYFIX_Q2AIRACCELERATE             autocvar_sv_gameplayfix_q2airaccelerate
-
-       #define IS_DUCKED(s)                        ((s).crouch)
-       #define SET_DUCKED(s)                       ((s).crouch = true)
-       #define UNSET_DUCKED(s)                     ((s).crouch = false)
-
-       #define PHYS_JUMPSPEEDCAP_MIN               autocvar_sv_jumpspeedcap_min
-       #define PHYS_JUMPSPEEDCAP_MAX               autocvar_sv_jumpspeedcap_max
-
-       #define PHYS_CL_TRACK_CANJUMP(s)            ((s).cvar_cl_movement_track_canjump)
-       #define PHYS_ENTGRAVITY(s)                  ((s).gravity)
-
-#endif
-
-REGISTER_NET_C2S(setpause)
-#ifdef CSQC
-void unpause_update()
-{
-       static bool waspaused;
-       bool ispaused = PHYS_INPUT_BUTTON_CHAT(this);
-       if (ispaused == waspaused) return;
-       waspaused = ispaused;
-       // if (!serverispaused) return; // TODO: find out somehow
-       if (ispaused) return; // ignore setting pause, server will get those presses anyway, but it won't get releases
-       int channel = MSG_C2S;
-       WriteHeader(channel, setpause);
-       WriteByte(channel, ispaused);
-}
-#endif
-#ifdef SVQC
-NET_HANDLE(setpause, bool)
-{
-       bool ispaused = boolean(ReadByte());
-       PHYS_INPUT_BUTTON_CHAT(sender) = ispaused;
-       return true;
-}
-#endif
-
-#endif
diff --git a/qcsrc/common/physics/all.inc b/qcsrc/common/physics/all.inc
new file mode 100644 (file)
index 0000000..1825120
--- /dev/null
@@ -0,0 +1,3 @@
+#include "player.qc"
+#include "movelib.qc"
+#include "movetypes/all.inc"
diff --git a/qcsrc/common/physics/movelib.qc b/qcsrc/common/physics/movelib.qc
new file mode 100644 (file)
index 0000000..38afbe2
--- /dev/null
@@ -0,0 +1,237 @@
+#include "movelib.qh"
+
+#ifdef SVQC
+.vector moveto;
+
+/**
+    Simulate drag
+    self.velocity = movelib_dragvec(self.velocity,0.02,0.5);
+**/
+vector movelib_dragvec(float drag, float exp_)
+{SELFPARAM();
+    float lspeed,ldrag;
+
+    lspeed = vlen(self.velocity);
+    ldrag = lspeed * drag;
+    ldrag = ldrag * (drag * exp_);
+    ldrag = 1 - (ldrag / lspeed);
+
+    return self.velocity * ldrag;
+}
+
+/**
+    Simulate drag
+    self.velocity *= movelib_dragflt(somespeed,0.01,0.7);
+**/
+float movelib_dragflt(float fspeed,float drag,float exp_)
+{
+    float ldrag;
+
+    ldrag = fspeed * drag;
+    ldrag = ldrag * ldrag * exp_;
+    ldrag = 1 - (ldrag / fspeed);
+
+    return ldrag;
+}
+
+/**
+    Do a inertia simulation based on velocity.
+    Basicaly, this allows you to simulate loss of steering with higher speed.
+    self.velocity = movelib_inertmove_byspeed(self.velocity,newvel,1000,0.1,0.9);
+**/
+vector movelib_inertmove_byspeed(vector vel_new, float vel_max,float newmin,float oldmax)
+{SELFPARAM();
+    float influense;
+
+    influense = vlen(self.velocity) * (1 / vel_max);
+
+    influense = bound(newmin,influense,oldmax);
+
+    return (vel_new * (1 - influense)) + (self.velocity * influense);
+}
+
+vector movelib_inertmove(vector new_vel,float new_bias)
+{SELFPARAM();
+    return new_vel * new_bias + self.velocity * (1-new_bias);
+}
+
+void movelib_move(vector force,float max_velocity,float drag,float theMass,float breakforce)
+{SELFPARAM();
+    float deltatime;
+    float acceleration;
+    float mspeed;
+    vector breakvec;
+
+    deltatime = time - self.movelib_lastupdate;
+    if (deltatime > 0.15) deltatime = 0;
+    self.movelib_lastupdate = time;
+    if (!deltatime) return;
+
+    mspeed = vlen(self.velocity);
+
+    if (theMass)
+        acceleration = vlen(force) / theMass;
+    else
+        acceleration = vlen(force);
+
+    if (self.flags & FL_ONGROUND)
+    {
+        if (breakforce)
+        {
+            breakvec = (normalize(self.velocity) * (breakforce / theMass) * deltatime);
+            self.velocity = self.velocity - breakvec;
+        }
+
+        self.velocity = self.velocity + force * (acceleration * deltatime);
+    }
+
+    if (drag)
+        self.velocity = movelib_dragvec(drag, 1);
+
+    if (self.waterlevel > 1)
+    {
+        self.velocity = self.velocity + force * (acceleration * deltatime);
+        self.velocity = self.velocity + '0 0 0.05' * autocvar_sv_gravity * deltatime;
+    }
+    else
+        self.velocity = self.velocity + '0 0 -1' * autocvar_sv_gravity * deltatime;
+
+    mspeed = vlen(self.velocity);
+
+    if (max_velocity)
+        if (mspeed > max_velocity)
+            self.velocity = normalize(self.velocity) * (mspeed - 50);//* max_velocity;
+}
+
+/*
+.float mass;
+.float side_friction;
+.float ground_friction;
+.float air_friction;
+.float water_friction;
+.float buoyancy;
+float movelib_deltatime;
+
+void movelib_startupdate()
+{
+    movelib_deltatime = time - self.movelib_lastupdate;
+
+    if (movelib_deltatime > 0.5)
+        movelib_deltatime = 0;
+
+    self.movelib_lastupdate = time;
+}
+
+void movelib_update(vector dir,float force)
+{
+    vector acceleration;
+    float old_speed;
+    float ffriction,v_z;
+
+    vector breakvec;
+    vector old_dir;
+    vector ggravity;
+    vector old;
+
+    if(!movelib_deltatime)
+        return;
+    v_z = self.velocity_z;
+    old_speed    = vlen(self.velocity);
+    old_dir      = normalize(self.velocity);
+
+    //ggravity      =  (autocvar_sv_gravity / self.mass) * '0 0 100';
+    acceleration =  (force / self.mass) * dir;
+    //acceleration -= old_dir * (old_speed / self.mass);
+    acceleration -= ggravity;
+
+    if(self.waterlevel > 1)
+    {
+        ffriction = self.water_friction;
+        acceleration += self.buoyancy * '0 0 1';
+    }
+    else
+        if(self.flags & FL_ONGROUND)
+            ffriction = self.ground_friction;
+        else
+            ffriction = self.air_friction;
+
+    acceleration *= ffriction;
+    //self.velocity = self.velocity * (ffriction * movelib_deltatime);
+    self.velocity += acceleration * movelib_deltatime;
+    self.velocity_z = v_z;
+
+}
+*/
+
+void movelib_brake_simple(float force)
+{SELFPARAM();
+    float mspeed;
+    vector mdir;
+    float vz;
+
+    mspeed = max(0,vlen(self.velocity) - force);
+    mdir   = normalize(self.velocity);
+    vz = self.velocity.z;
+    self.velocity = mdir * mspeed;
+    self.velocity_z = vz;
+}
+
+/**
+Pitches and rolls the entity to match the gound.
+Yed need to set v_up and v_forward (generally by calling makevectors) before calling this.
+**/
+#endif
+
+void movelib_groundalign4point(float spring_length, float spring_up, float blendrate, float _max)
+{SELFPARAM();
+    vector a, b, c, d, e, r, push_angle, ahead, side;
+
+    push_angle.y = 0;
+    r = (self.absmax + self.absmin) * 0.5 + (v_up * spring_up);
+    e = v_up * spring_length;
+
+    // Put springs slightly inside bbox
+    ahead = v_forward * (self.maxs.x * 0.8);
+    side  = v_right   * (self.maxs.y * 0.8);
+
+    a = r + ahead + side;
+    b = r + ahead - side;
+    c = r - ahead + side;
+    d = r - ahead - side;
+
+    traceline(a, a - e,MOVE_NORMAL,self);
+    a.z =  (1 - trace_fraction);
+    r = trace_endpos;
+
+    traceline(b, b - e,MOVE_NORMAL,self);
+    b.z =  (1 - trace_fraction);
+    r += trace_endpos;
+
+    traceline(c, c - e,MOVE_NORMAL,self);
+    c.z =  (1 - trace_fraction);
+    r += trace_endpos;
+
+    traceline(d, d - e,MOVE_NORMAL,self);
+    d.z =  (1 - trace_fraction);
+    r += trace_endpos;
+
+    a.x = r.z;
+    r = self.origin;
+    r.z = r.z;
+
+    push_angle.x = (a.z - c.z) * _max;
+    push_angle.x += (b.z - d.z) * _max;
+
+    push_angle.z = (b.z - a.z) * _max;
+    push_angle.z += (d.z - c.z) * _max;
+
+    //self.angles_x += push_angle_x * 0.95;
+    //self.angles_z += push_angle_z * 0.95;
+
+    self.angles_x = ((1-blendrate) *  self.angles.x)  + (push_angle.x * blendrate);
+    self.angles_z = ((1-blendrate) *  self.angles.z)  + (push_angle.z * blendrate);
+
+    //a = self.origin;
+    setorigin(self,r);
+}
+
diff --git a/qcsrc/common/physics/movelib.qh b/qcsrc/common/physics/movelib.qh
new file mode 100644 (file)
index 0000000..c1fd574
--- /dev/null
@@ -0,0 +1,53 @@
+#ifndef MOVELIB_H
+#define MOVELIB_H
+
+#ifdef SVQC
+.vector moveto;
+
+/**
+    Simulate drag
+    self.velocity = movelib_dragvec(self.velocity,0.02,0.5);
+**/
+vector movelib_dragvec(float drag, float exp_);
+
+/**
+    Simulate drag
+    self.velocity *= movelib_dragflt(somespeed,0.01,0.7);
+**/
+float movelib_dragflt(float fspeed,float drag,float exp_);
+
+/**
+    Do a inertia simulation based on velocity.
+    Basicaly, this allows you to simulate loss of steering with higher speed.
+    self.velocity = movelib_inertmove_byspeed(self.velocity,newvel,1000,0.1,0.9);
+**/
+vector movelib_inertmove_byspeed(vector vel_new, float vel_max,float newmin,float oldmax);
+
+vector movelib_inertmove(vector new_vel,float new_bias);
+
+.float  movelib_lastupdate;
+void movelib_move(vector force,float max_velocity,float drag,float theMass,float breakforce);
+
+/*
+void movelib_move_simple(vector newdir,float velo,float blendrate)
+{
+    self.velocity = self.velocity * (1 - blendrate) + (newdir * blendrate) * velo;
+}
+*/
+#define movelib_move_simple(newdir,velo,blendrate) \
+    self.velocity = self.velocity * (1 - blendrate) + (newdir * blendrate) * velo
+
+#define movelib_move_simple_gravity(newdir,velo,blendrate) \
+    if(self.flags & FL_ONGROUND) movelib_move_simple(newdir,velo,blendrate)
+
+void movelib_brake_simple(float force);
+
+/**
+Pitches and rolls the entity to match the gound.
+Yed need to set v_up and v_forward (generally by calling makevectors) before calling this.
+**/
+#endif
+
+void movelib_groundalign4point(float spring_length, float spring_up, float blendrate, float _max);
+
+#endif
diff --git a/qcsrc/common/physics/movetypes/all.inc b/qcsrc/common/physics/movetypes/all.inc
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/physics/movetypes/follow.qc b/qcsrc/common/physics/movetypes/follow.qc
new file mode 100644 (file)
index 0000000..2d3e24f
--- /dev/null
@@ -0,0 +1,31 @@
+void _Movetype_Physics_Follow(entity this) // SV_Physics_Follow
+{
+       entity e = this.move_aiment; // TODO: networking?
+
+       // LordHavoc: implemented rotation on MOVETYPE_FOLLOW objects
+       if(this.move_angles == this.move_punchangle)
+       {
+               this.move_origin = e.move_origin + this.view_ofs;
+       }
+       else
+       {
+               vector ang, v;
+               ang_x = -this.move_punchangle_x;
+               ang_y = this.move_punchangle_y;
+               ang_z = this.move_punchangle_z;
+               makevectors(ang);
+               v_x = this.view_ofs_x * v_forward_x + this.view_ofs_y * v_right_x + this.view_ofs_z * v_up_x;
+               v_y = this.view_ofs_x * v_forward_y + this.view_ofs_y * v_right_y + this.view_ofs_z * v_up_y;
+               v_z = this.view_ofs_x * v_forward_z + this.view_ofs_y * v_right_z + this.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);
+               this.move_origin_x = v_x * v_forward_x + v_y * v_forward_y + v_z * v_forward_z + e.move_origin_x;
+               this.move_origin_x = v_x * v_right_x + v_y * v_right_y + v_z * v_right_z + e.move_origin_y;
+               this.move_origin_x = v_x * v_up_x + v_y * v_up_y + v_z * v_up_z + e.move_origin_z;
+       }
+
+       this.move_angles = e.move_angles + this.v_angle;
+       _Movetype_LinkEdict(this, false);
+}
diff --git a/qcsrc/common/physics/movetypes/movetypes.qc b/qcsrc/common/physics/movetypes/movetypes.qc
new file mode 100644 (file)
index 0000000..fa169e9
--- /dev/null
@@ -0,0 +1,716 @@
+#include "../player.qh"
+
+#if defined(CSQC)
+       #include "../../../client/defs.qh"
+       #include "../../stats.qh"
+       #include "../../util.qh"
+       #include "movetypes.qh"
+       #include "../../../lib/csqcmodel/common.qh"
+       #include "../../../server/t_items.qh"
+#elif defined(MENUQC)
+#elif defined(SVQC)
+       #include "../../../server/autocvars.qh"
+#endif
+
+void _Movetype_WallFriction(entity this, vector stepnormal)  // SV_WallFriction
+{
+       /*float d, i;
+       vector into, side;
+       makevectors(this.v_angle);
+       d = (stepnormal * v_forward) + 0.5;
+
+       if(d < 0)
+       {
+           i = (stepnormal * this.move_velocity);
+           into = i * stepnormal;
+           side = this.move_velocity - into;
+           this.move_velocity_x = side.x * (1 * d);
+           this.move_velocity_y = side.y * (1 * d);
+       }*/
+}
+
+vector planes[MAX_CLIP_PLANES];
+int _Movetype_FlyMove(entity this, 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, restore_velocity;
+
+       for(i = 0; i < MAX_CLIP_PLANES; ++i)
+               planes[i] = '0 0 0';
+
+       if(applygravity)
+       {
+               this.move_didgravity = 1;
+               grav = dt * (PHYS_ENTGRAVITY(this) ? PHYS_ENTGRAVITY(this) : 1) * PHYS_GRAVITY(this);
+
+               if(!GAMEPLAYFIX_NOGRAVITYONGROUND || !(this.move_flags & FL_ONGROUND))
+               {
+                       if(GAMEPLAYFIX_GRAVITYUNAFFECTEDBYTICRATE)
+                               this.move_velocity_z -= grav * 0.5;
+                       else
+                               this.move_velocity_z -= grav;
+               }
+       }
+
+       original_velocity = primal_velocity = restore_velocity = this.move_velocity;
+
+       for(bumpcount = 0;bumpcount < MAX_CLIP_PLANES;bumpcount++)
+       {
+               if(this.move_velocity == '0 0 0')
+                       break;
+
+               push = this.move_velocity * time_left;
+               _Movetype_PushEntity(this, push, true);
+               if(trace_startsolid)
+               {
+                       // 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)
+               {
+                       this.move_velocity = restore_velocity;
+                       return 3;
+               }
+
+               if(trace_fraction == 1)
+                       break;
+
+               float my_trace_fraction = trace_fraction;
+               vector my_trace_plane_normal = trace_plane_normal;
+
+               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;
+                               }
+
+                               this.move_flags |= FL_ONGROUND;
+                               this.move_groundentity = trace_ent;
+                       }
+               }
+               else if(stepheight)
+               {
+                       // step - handle it immediately
+                       vector org = this.move_origin;
+                       vector steppush = '0 0 1' * stepheight;
+
+                       _Movetype_PushEntity(this, steppush, true);
+                       if(trace_startsolid)
+                       {
+                               blocked |= 8;
+                               break;
+                       }
+                       _Movetype_PushEntity(this, push, true);
+                       if(trace_startsolid)
+                       {
+                               blocked |= 8;
+                               break;
+                       }
+                       float trace2_fraction = trace_fraction;
+                       steppush = '0 0 1' * (org_z - this.move_origin_z);
+                       _Movetype_PushEntity(this, steppush, true);
+                       if(trace_startsolid)
+                       {
+                               blocked |= 8;
+                               break;
+                       }
+
+                       // accept the new position if it made some progress...
+                       if(fabs(this.move_origin_x - org_x) >= 0.03125 || fabs(this.move_origin_y - org_y) >= 0.03125)
+                       {
+                               trace_endpos = this.move_origin;
+                               time_left *= 1 - trace2_fraction;
+                               numplanes = 0;
+                               continue;
+                       }
+                       else
+                               this.move_origin = org;
+               }
+               else
+               {
+                       // step - return it to caller
+                       blocked |= 2;
+                       // save the trace for player extrafriction
+                       if(stepnormal)
+                               stepnormal = trace_plane_normal;
+               }
+
+               if(my_trace_fraction >= 0.001)
+               {
+                       // actually covered some distance
+                       original_velocity = this.move_velocity;
+                       numplanes = 0;
+               }
+
+               time_left *= 1 - my_trace_fraction;
+
+               // clipped to another plane
+               if(numplanes >= MAX_CLIP_PLANES)
+               {
+                       // this shouldn't really happen
+                       this.move_velocity = '0 0 0';
+                       blocked = 3;
+                       break;
+               }
+
+               planes[numplanes] = my_trace_plane_normal;
+               numplanes++;
+
+               // modify original_velocity so it parallels all of the clip planes
+               vector new_velocity = '0 0 0';
+               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
+                       this.move_velocity = new_velocity;
+               }
+               else
+               {
+                       // go along the crease
+                       if(numplanes != 2)
+                       {
+                               this.move_velocity = '0 0 0';
+                               blocked = 7;
+                               break;
+                       }
+                       vector dir = cross(planes[0], planes[1]);
+                       // 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 * this.move_velocity);
+                       this.move_velocity = dir * d;
+               }
+
+               // if current velocity is against the original velocity,
+               // stop dead to avoid tiny occilations in sloping corners
+               if((this.move_velocity * primal_velocity) <= 0)
+               {
+                       this.move_velocity = '0 0 0';
+                       break;
+               }
+       }
+
+       // LordHavoc: this came from QW and allows you to get out of water more easily
+       if(GAMEPLAYFIX_EASIERWATERJUMP(this) && (this.move_flags & FL_WATERJUMP) && !(blocked & 8))
+               this.move_velocity = primal_velocity;
+
+       if(applygravity)
+       {
+               if(!GAMEPLAYFIX_NOGRAVITYONGROUND || !(this.move_flags & FL_ONGROUND))
+               {
+                       if(GAMEPLAYFIX_GRAVITYUNAFFECTEDBYTICRATE)
+                               this.move_velocity_z -= grav * 0.5f;
+               }
+       }
+
+       return blocked;
+}
+
+void _Movetype_CheckVelocity(entity this)  // SV_CheckVelocity
+{
+       // if(vlen(this.move_velocity) < 0.0001)
+       // this.move_velocity = '0 0 0';
+}
+
+bool _Movetype_CheckWater(entity this)  // SV_CheckWater
+{
+       vector point = this.move_origin;
+       point.z += this.mins.z + 1;
+
+       int nativecontents = pointcontents(point);
+       if(this.move_watertype && this.move_watertype != nativecontents)
+       {
+               // dprintf("_Movetype_CheckWater(): Original: '%d', New: '%d'\n", this.move_watertype, nativecontents);
+               if(this.contentstransition)
+                       this.contentstransition(this.move_watertype, nativecontents);
+       }
+
+       this.move_waterlevel = WATERLEVEL_NONE;
+       this.move_watertype = CONTENT_EMPTY;
+
+       int supercontents = Mod_Q1BSP_SuperContentsFromNativeContents(nativecontents);
+       if(supercontents & DPCONTENTS_LIQUIDSMASK)
+       {
+               this.move_watertype = nativecontents;
+               this.move_waterlevel = WATERLEVEL_WETFEET;
+               point.z = this.move_origin.z + (this.mins.z + this.maxs.z) * 0.5;
+               if(Mod_Q1BSP_SuperContentsFromNativeContents(pointcontents(point)) & DPCONTENTS_LIQUIDSMASK)
+               {
+                       this.move_waterlevel = WATERLEVEL_SWIMMING;
+                       point.z = this.move_origin.z + this.view_ofs.z;
+                       if(Mod_Q1BSP_SuperContentsFromNativeContents(pointcontents(point)) & DPCONTENTS_LIQUIDSMASK)
+                               this.move_waterlevel = WATERLEVEL_SUBMERGED;
+               }
+       }
+
+       return this.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 this, entity oth)  // SV_Impact
+{
+       entity oldother = other;
+
+       if(this.move_touch)
+       {
+               other = oth;
+
+               WITH(entity, self, this, this.move_touch());
+
+               other = oldother;
+       }
+
+       if(oth.move_touch)
+       {
+               other = this;
+
+               WITH(entity, self, oth, oth.move_touch());
+
+               other = oldother;
+       }
+}
+
+void _Movetype_LinkEdict_TouchAreaGrid(entity this)  // SV_LinkEdict_TouchAreaGrid
+{
+       entity oldother = other;
+
+       for (entity e = findradius(0.5 * (this.absmin + this.absmax), 0.5 * vlen(this.absmax - this.absmin)); e; e = e.chain)
+       {
+               if(e.move_nomonsters != MOVE_NOMONSTERS && e.move_nomonsters != MOVE_WORLDONLY)
+               if(e.move_touch && boxesoverlap(e.absmin, e.absmax, this.absmin, this.absmax))
+               {
+                       other = this;
+
+                       trace_allsolid = false;
+                       trace_startsolid = false;
+                       trace_fraction = 1;
+                       trace_inwater = false;
+                       trace_inopen = true;
+                       trace_endpos = e.move_origin;
+                       trace_plane_normal = '0 0 1';
+                       trace_plane_dist = 0;
+                       trace_ent = this;
+
+                       WITH(entity, self, e, e.move_touch());
+               }
+       }
+
+       other = oldother;
+}
+
+void _Movetype_LinkEdict(entity this, bool touch_triggers)  // SV_LinkEdict
+{
+       vector mi, ma;
+       if(this.solid == SOLID_BSP)
+       {
+               // TODO set the absolute bbox
+               mi = this.mins;
+               ma = this.maxs;
+       }
+       else
+       {
+               mi = this.mins;
+               ma = this.maxs;
+       }
+       mi += this.move_origin;
+       ma += this.move_origin;
+
+       if(this.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;
+       }
+
+       this.absmin = mi;
+       this.absmax = ma;
+
+       if(touch_triggers)
+               _Movetype_LinkEdict_TouchAreaGrid(this);
+}
+
+bool _Movetype_TestEntityPosition(entity this, vector ofs)  // SV_TestEntityPosition
+{
+//     vector org = this.move_origin + ofs;
+
+       int cont = this.dphitcontentsmask;
+       this.dphitcontentsmask = DPCONTENTS_SOLID;
+       tracebox(this.move_origin, this.mins, this.maxs, this.move_origin, ((this.move_movetype == MOVETYPE_FLY_WORLDONLY) ? MOVE_WORLDONLY : MOVE_NOMONSTERS), this);
+       this.dphitcontentsmask = cont;
+
+       if(trace_startsolid)
+               return true;
+
+       if(vlen(trace_endpos - this.move_origin) > 0.0001)
+               this.move_origin = trace_endpos;
+       return false;
+}
+
+bool _Movetype_UnstickEntity(entity this)  // SV_UnstickEntity
+{
+       if(!_Movetype_TestEntityPosition(this, '0 0 0')) return true;
+       if(!_Movetype_TestEntityPosition(this, '-1 0 0')) goto success;
+       if(!_Movetype_TestEntityPosition(this, '1 0 0')) goto success;
+       if(!_Movetype_TestEntityPosition(this, '0 -1 0')) goto success;
+       if(!_Movetype_TestEntityPosition(this, '0 1 0')) goto success;
+       if(!_Movetype_TestEntityPosition(this, '-1 -1 0')) goto success;
+       if(!_Movetype_TestEntityPosition(this, '1 -1 0')) goto success;
+       if(!_Movetype_TestEntityPosition(this, '-1 1 0')) goto success;
+       if(!_Movetype_TestEntityPosition(this, '1 1 0')) goto success;
+       for (int i = 1; i <= 17; ++i)
+       {
+               if(!_Movetype_TestEntityPosition(this, '0 0 -1' * i)) goto success;
+               if(!_Movetype_TestEntityPosition(this, '0 0 1' * i)) goto success;
+       }
+       LOG_DEBUGF("Can't unstick an entity (edict: %d, classname: %s, origin: %s)\n",
+               etof(this), this.classname, vtos(this.move_origin));
+       return false;
+       : success;
+       LOG_DEBUGF("Sucessfully unstuck an entity (edict: %d, classname: %s, origin: %s)\n",
+               etof(this), this.classname, vtos(this.move_origin));
+       _Movetype_LinkEdict(this, 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(entity this, vector push)
+{
+       vector end = this.move_origin + push;
+       int type;
+       if(this.move_nomonsters)
+               type = max(0, this.move_nomonsters);
+       else if(this.move_movetype == MOVETYPE_FLYMISSILE)
+               type = MOVE_MISSILE;
+       else if(this.move_movetype == MOVETYPE_FLY_WORLDONLY)
+               type = MOVE_WORLDONLY;
+       else if(this.solid == SOLID_TRIGGER || this.solid == SOLID_NOT)
+               type = MOVE_NOMONSTERS;
+       else
+               type = MOVE_NORMAL;
+
+       tracebox(this.move_origin, this.mins, this.maxs, end, type, this);
+}
+
+float _Movetype_PushEntity(entity this, vector push, bool failonstartsolid)  // SV_PushEntity
+{
+       _Movetype_PushEntityTrace(this, push);
+
+       if(trace_startsolid && failonstartsolid)
+               return trace_fraction;
+
+       this.move_origin = trace_endpos;
+
+       if(trace_fraction < 1)
+               if(this.solid >= SOLID_TRIGGER && (!(this.move_flags & FL_ONGROUND) || (this.move_groundentity != trace_ent)))
+                       _Movetype_Impact(this, 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(entity this, float movedt)
+{
+       this.move_didgravity = -1;
+       switch (this.move_movetype)
+       {
+               case MOVETYPE_PUSH:
+               case MOVETYPE_FAKEPUSH:
+                       _Movetype_Physics_Pusher(this, movedt);
+                       break;
+               case MOVETYPE_NONE:
+                       break;
+               case MOVETYPE_FOLLOW:
+                       _Movetype_Physics_Follow(this);
+                       break;
+               case MOVETYPE_NOCLIP:
+                       _Movetype_CheckWater(this);
+                       this.move_origin = this.move_origin + TICRATE * this.move_velocity;
+                       this.move_angles = this.move_angles + TICRATE * this.move_avelocity;
+                       _Movetype_LinkEdict(this, false);
+                       break;
+               case MOVETYPE_STEP:
+                       _Movetype_Physics_Step(this, movedt);
+                       break;
+               case MOVETYPE_WALK:
+                       _Movetype_Physics_Walk(this, movedt);
+                       break;
+               case MOVETYPE_TOSS:
+               case MOVETYPE_BOUNCE:
+               case MOVETYPE_BOUNCEMISSILE:
+               case MOVETYPE_FLYMISSILE:
+               case MOVETYPE_FLY:
+               case MOVETYPE_FLY_WORLDONLY:
+                       _Movetype_Physics_Toss(this, movedt);
+                       _Movetype_LinkEdict(this, true);
+                       break;
+               case MOVETYPE_PHYSICS:
+                       break;
+       }
+}
+
+void _Movetype_Physics_ClientFrame(entity this, float movedt)
+{
+       this.move_didgravity = -1;
+       switch (this.move_movetype)
+       {
+               case MOVETYPE_PUSH:
+               case MOVETYPE_FAKEPUSH:
+                       _Movetype_Physics_Pusher(this, movedt);
+                       break;
+               case MOVETYPE_NONE:
+                       break;
+               case MOVETYPE_FOLLOW:
+                       _Movetype_Physics_Follow(this);
+                       break;
+               case MOVETYPE_NOCLIP:
+                       _Movetype_CheckWater(this);
+                       this.move_origin = this.move_origin + TICRATE * this.move_velocity;
+                       this.move_angles = this.move_angles + TICRATE * this.move_avelocity;
+                       _Movetype_LinkEdict(this, false);
+                       break;
+               case MOVETYPE_STEP:
+                       _Movetype_Physics_Step(this, movedt);
+                       break;
+               case MOVETYPE_WALK:
+               case MOVETYPE_FLY:
+               case MOVETYPE_FLY_WORLDONLY:
+                       _Movetype_Physics_Walk(this, movedt);
+                       break;
+               case MOVETYPE_TOSS:
+               case MOVETYPE_BOUNCE:
+               case MOVETYPE_BOUNCEMISSILE:
+               case MOVETYPE_FLYMISSILE:
+                       _Movetype_Physics_Toss(this, movedt);
+                       break;
+               case MOVETYPE_PHYSICS:
+                       break;
+       }
+}
+
+void Movetype_Physics_NoMatchServer(entity this)  // optimized
+{
+       float movedt = time - this.move_time;
+       this.move_time = time;
+
+       _Movetype_Physics_Frame(this, movedt);
+       if(wasfreed(this))
+               return;
+
+       this.avelocity = this.move_avelocity;
+       this.velocity = this.move_velocity;
+       this.angles = this.move_angles;
+       setorigin(this, this.move_origin);
+}
+
+void Movetype_Physics_MatchServer(entity this, bool sloppy)
+{
+       Movetype_Physics_MatchTicrate(this, TICRATE, sloppy);
+}
+
+void Movetype_Physics_MatchTicrate(entity this, float tr, bool sloppy)  // SV_Physics_Entity
+{
+       if(tr <= 0)
+       {
+               Movetype_Physics_NoMatchServer(this);
+               return;
+       }
+
+       float dt = time - this.move_time;
+
+       int n = max(0, floor(dt / tr));
+       dt -= n * tr;
+       this.move_time += n * tr;
+
+       if(!this.move_didgravity)
+               this.move_didgravity = ((this.move_movetype == MOVETYPE_BOUNCE || this.move_movetype == MOVETYPE_TOSS) && !(this.move_flags & FL_ONGROUND));
+
+       for (int i = 0; i < n; ++i)
+       {
+               _Movetype_Physics_Frame(this, tr);
+               if(wasfreed(this))
+                       return;
+       }
+
+       this.avelocity = this.move_avelocity;
+
+       if(dt > 0 && this.move_movetype != MOVETYPE_NONE && !(this.move_flags & FL_ONGROUND))
+       {
+               // now continue the move from move_time to time
+               this.velocity = this.move_velocity;
+
+               if(this.move_didgravity > 0)
+               {
+                       this.velocity_z -= (GAMEPLAYFIX_GRAVITYUNAFFECTEDBYTICRATE ? 0.5 : 1)
+                           * dt
+                           * (this.gravity ? this.gravity : 1)
+                           * PHYS_GRAVITY(this);
+               }
+
+               this.angles = this.move_angles + dt * this.avelocity;
+
+               if(sloppy || this.move_movetype == MOVETYPE_NOCLIP)
+               {
+                       setorigin(this, this.move_origin + dt * this.velocity);
+               }
+               else
+               {
+                       _Movetype_PushEntityTrace(this, dt * this.velocity);
+                       if(!trace_startsolid)
+                               setorigin(this, trace_endpos);
+               }
+
+               if(this.move_didgravity > 0 && GAMEPLAYFIX_GRAVITYUNAFFECTEDBYTICRATE)
+                       this.velocity_z -= 0.5 * dt * (this.gravity ? this.gravity : 1) * PHYS_GRAVITY(this);
+       }
+       else
+       {
+               this.velocity = this.move_velocity;
+               this.angles = this.move_angles;
+               setorigin(this, this.move_origin);
+       }
+}
diff --git a/qcsrc/common/physics/movetypes/movetypes.qh b/qcsrc/common/physics/movetypes/movetypes.qh
new file mode 100644 (file)
index 0000000..7578ae2
--- /dev/null
@@ -0,0 +1,90 @@
+#ifndef MOVETYPES_H
+#define MOVETYPES_H
+
+.float move_ltime;
+.void()move_think;
+.float move_nextthink;
+.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()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;
+
+.entity move_groundentity;  // FIXME add move_groundnetworkentity?
+.float move_suspendedinair;
+.float move_didgravity;
+
+void _Movetype_WallFriction(entity this, vector stepnormal);
+int _Movetype_FlyMove(entity this, float dt, bool applygravity, vector stepnormal, float stepheight);
+void _Movetype_CheckVelocity(entity this);
+void _Movetype_CheckWaterTransition(entity ent);
+float _Movetype_CheckWater(entity ent);
+void _Movetype_LinkEdict_TouchAreaGrid(entity this);
+void _Movetype_LinkEdict(entity this, float touch_triggers);
+float _Movetype_TestEntityPosition(entity this, vector ofs);
+float _Movetype_UnstickEntity(entity this);
+vector _Movetype_ClipVelocity(vector vel, vector norm, float f);
+void _Movetype_PushEntityTrace(entity this, vector push);
+float _Movetype_PushEntity(entity this, vector push, float failonstartsolid);
+void makevectors_matrix(vector myangles);
+
+void Movetype_Physics_MatchTicrate(entity this, float tr, bool sloppy);
+void Movetype_Physics_MatchServer(entity this, bool sloppy);
+void Movetype_Physics_NoMatchServer(entity this);
+void _Movetype_LinkEdict(entity this, float touch_triggers);
+void _Movetype_LinkEdict_TouchAreaGrid(entity this);
+
+float _Movetype_UnstickEntity(entity this);
+
+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_PHYSICS          = 32;
+const int MOVETYPE_FLY_WORLDONLY    = 33;
+
+const int FL_ITEM                   = 256;
+const int FL_ONGROUND                          = 512;
+#endif
+
+const int MOVETYPE_FAKEPUSH         = 13;
+
+const int MOVEFLAG_VALID = BIT(23);
+const int MOVEFLAG_Q2AIRACCELERATE = BIT(0);
+const int MOVEFLAG_NOGRAVITYONGROUND = BIT(1);
+const int MOVEFLAG_GRAVITYUNAFFECTEDBYTICRATE = BIT(2);
+
+#ifdef CSQC
+#define moveflags STAT(MOVEFLAGS)
+#endif
+
+#endif
diff --git a/qcsrc/common/physics/movetypes/push.qc b/qcsrc/common/physics/movetypes/push.qc
new file mode 100644 (file)
index 0000000..b832465
--- /dev/null
@@ -0,0 +1,157 @@
+void _Movetype_PushMove(entity this, float dt)  // SV_PushMove
+{
+       if (this.move_velocity == '0 0 0' && this.move_avelocity == '0 0 0')
+       {
+               this.move_ltime += dt;
+               return;
+       }
+
+       switch (this.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:
+                       this.move_origin = this.move_origin + dt * this.move_velocity;
+                       this.move_angles = this.move_angles + dt * this.move_avelocity;
+                       this.move_angles_x -= 360.0 * floor(this.move_angles.x * (1.0 / 360.0));
+                       this.move_angles_y -= 360.0 * floor(this.move_angles.y * (1.0 / 360.0));
+                       this.move_angles_z -= 360.0 * floor(this.move_angles.z * (1.0 / 360.0));
+                       this.move_ltime += dt;
+                       _Movetype_LinkEdict(this, true);
+                       return;
+               default:
+                       LOG_TRACEF("_Movetype_PushMove: entity %e, unrecognized solid type %d\n", this, this.solid);
+                       return;
+       }
+
+       bool rotated = (this.move_angles * this.move_angles) + (this.move_avelocity * this.move_avelocity) > 0;
+
+       vector move1 = this.move_velocity * dt;
+       vector moveangle = this.move_avelocity * dt;
+
+       makevectors_matrix(-moveangle);
+
+//     vector pushorig = this.move_origin;
+//     vector pushang = this.move_angles;
+//     float pushltime = this.move_ltime;
+
+// move the pusher to its final position
+
+       this.move_origin = this.move_origin + dt * this.move_velocity;
+       this.move_angles = this.move_angles + dt * this.move_avelocity;
+
+       this.move_ltime += dt;
+       _Movetype_LinkEdict(this, true);
+
+       int savesolid = this.solid;
+
+       if (this.move_movetype != MOVETYPE_FAKEPUSH)
+       {
+               for (entity check = findradius(0.5 * (this.absmin + this.absmax), 0.5 * vlen(this.absmax - this.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 == this)
+                               continue;
+
+                       if (this.owner == check)
+                               continue;
+
+                       vector pivot = check.mins + 0.5 * (check.maxs - check.mins);
+                       vector move;
+                       if (rotated)
+                       {
+                               vector org = (check.move_origin - this.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;
+                               WITH(entity, this, check, _Movetype_LinkEdict(this, true));
+                               continue;
+                       }
+
+                       // try moving the contacted entity
+                       this.solid = SOLID_NOT;
+                       bool flag = false;
+                       WITH(entity, this, check, {
+                               flag = _Movetype_PushEntity(this, move, true);
+                       });
+                       if (!flag)
+                       {
+                               // entity "check" got teleported
+                               check.move_angles_y += trace_fraction * moveangle.y;
+                               this.solid = savesolid;
+                               continue;  // pushed enough
+                       }
+                       // FIXME: turn players specially
+                       check.move_angles_y += trace_fraction * moveangle.y;
+                       this.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 != this))
+                               check.move_flags &= ~FL_ONGROUND;
+               }
+       }
+
+       this.move_angles_x -= 360.0 * floor(this.move_angles.x * (1.0 / 360.0));
+       this.move_angles_y -= 360.0 * floor(this.move_angles.y * (1.0 / 360.0));
+       this.move_angles_z -= 360.0 * floor(this.move_angles.z * (1.0 / 360.0));
+}
+
+void _Movetype_Physics_Pusher(entity this, float dt)  // SV_Physics_Pusher
+{
+       float oldltime = this.move_ltime;
+       float thinktime = this.move_nextthink;
+       float movetime;
+       if (thinktime < this.move_ltime + dt)
+       {
+               movetime = thinktime - this.move_ltime;
+               if (movetime < 0)
+                       movetime = 0;
+       }
+       else
+       {
+               movetime = dt;
+       }
+
+       if (movetime)
+               // advances this.move_ltime if not blocked
+               _Movetype_PushMove(this, movetime);
+
+       if (thinktime > oldltime && thinktime <= this.move_ltime)
+       {
+               this.move_nextthink = 0;
+               this.move_time = time;
+               other = world;
+               WITH(entity, self, this, this.move_think());
+       }
+}
diff --git a/qcsrc/common/physics/movetypes/step.qc b/qcsrc/common/physics/movetypes/step.qc
new file mode 100644 (file)
index 0000000..d7a2d56
--- /dev/null
@@ -0,0 +1,23 @@
+void _Movetype_Physics_Step(entity this, float dt) // SV_Physics_Step
+{
+       if(this.move_flags & FL_ONGROUND)
+       {
+               if(this.velocity_z >= (1.0 / 32.0) && UPWARD_VELOCITY_CLEARS_ONGROUND(this))
+               {
+                       this.move_flags &= ~FL_ONGROUND;
+                       _Movetype_CheckVelocity(this);
+                       _Movetype_FlyMove(this, dt, true, '0 0 0', 0);
+                       _Movetype_LinkEdict(this, true);
+               }
+       }
+       else
+       {
+               _Movetype_CheckVelocity(this);
+               _Movetype_FlyMove(this, dt, true, '0 0 0', 0);
+               _Movetype_LinkEdict(this, true);
+
+               // TODO? movetypesteplandevent
+       }
+
+       _Movetype_CheckWaterTransition(this);
+}
diff --git a/qcsrc/common/physics/movetypes/toss.qc b/qcsrc/common/physics/movetypes/toss.qc
new file mode 100644 (file)
index 0000000..dbd25c8
--- /dev/null
@@ -0,0 +1,115 @@
+#include "../player.qh"
+
+void _Movetype_Physics_Toss(entity this, float dt)  // SV_Physics_Toss
+{
+       if (this.move_flags & FL_ONGROUND)
+       {
+               if (this.move_velocity.z >= 1 / 32 && UPWARD_VELOCITY_CLEARS_ONGROUND(this))
+               {
+                       this.move_flags &= ~FL_ONGROUND;
+               }
+               else if (!this.move_groundentity)
+               {
+                       return;
+               }
+               else if (this.move_suspendedinair && wasfreed(this.move_groundentity))
+               {
+                       this.move_groundentity = world;
+                       return;
+               }
+       }
+
+       this.move_suspendedinair = false;
+
+       _Movetype_CheckVelocity(this);
+
+       if (this.move_movetype == MOVETYPE_BOUNCE || this.move_movetype == MOVETYPE_TOSS)
+       {
+               this.move_didgravity = 1;
+               this.move_velocity_z -= (GAMEPLAYFIX_GRAVITYUNAFFECTEDBYTICRATE ? 0.5 : 1)
+                   * dt
+                   * (this.gravity ? this.gravity : 1)
+                   * PHYS_GRAVITY(this);
+       }
+
+       this.move_angles = this.move_angles + this.move_avelocity * dt;
+
+       float movetime = dt;
+       for (int bump = 0; bump < MAX_CLIP_PLANES && movetime > 0; ++bump)
+       {
+               vector move = this.move_velocity * movetime;
+               _Movetype_PushEntity(this, move, true);
+               if (wasfreed(this))
+                       return;
+
+               if (trace_startsolid)
+               {
+                       _Movetype_UnstickEntity(this);
+                       _Movetype_PushEntity(this, move, false);
+                       if (wasfreed(this))
+                               return;
+               }
+
+               if (trace_fraction == 1)
+                       break;
+
+               movetime *= 1 - min(1, trace_fraction);
+
+               if (this.move_movetype == MOVETYPE_BOUNCEMISSILE)
+               {
+                       this.move_velocity = _Movetype_ClipVelocity(this.move_velocity, trace_plane_normal, 2.0);
+                       this.move_flags &= ~FL_ONGROUND;
+               }
+               else if (this.move_movetype == MOVETYPE_BOUNCE)
+               {
+                       float bouncefac = this.move_bounce_factor;     if (!bouncefac)  bouncefac = 0.5;
+                       float bouncestop = this.move_bounce_stopspeed; if (!bouncestop) bouncestop = 60 / 800;
+                       bouncestop *= (this.gravity ? this.gravity : 1) * PHYS_GRAVITY(this);
+
+                       this.move_velocity = _Movetype_ClipVelocity(this.move_velocity, trace_plane_normal, 1 + bouncefac);
+
+                       float d = trace_plane_normal * this.move_velocity;
+                       if (trace_plane_normal.z > 0.7 && d < bouncestop && d > -bouncestop)
+                       {
+                               this.move_flags |= FL_ONGROUND;
+                               this.move_groundentity = trace_ent;
+                               this.move_velocity = '0 0 0';
+                               this.move_avelocity = '0 0 0';
+                       }
+                       else
+                       {
+                               this.move_flags &= ~FL_ONGROUND;
+                       }
+               }
+               else
+               {
+                       this.move_velocity = _Movetype_ClipVelocity(this.move_velocity, trace_plane_normal, 1.0);
+                       if (trace_plane_normal.z > 0.7)
+                       {
+                               this.move_flags |= FL_ONGROUND;
+                               this.move_groundentity = trace_ent;
+                               if (trace_ent.solid == SOLID_BSP)
+                                       this.move_suspendedinair = true;
+                               this.move_velocity = '0 0 0';
+                               this.move_avelocity = '0 0 0';
+                       }
+                       else
+                       {
+                               this.move_flags &= ~FL_ONGROUND;
+                       }
+               }
+
+               // DP revision 8905 (just, WHY...)
+               if (this.move_movetype == MOVETYPE_BOUNCEMISSILE)
+                       break;
+
+               // DP revision 8918 (WHY...)
+               if (this.move_flags & FL_ONGROUND)
+                       break;
+       }
+
+       if (GAMEPLAYFIX_GRAVITYUNAFFECTEDBYTICRATE && this.move_didgravity > 0 && !(this.move_flags & FL_ONGROUND))
+               this.move_velocity_z -= 0.5 * dt * (this.gravity ? this.gravity : 1) * PHYS_GRAVITY(this);
+
+       _Movetype_CheckWaterTransition(this);
+}
diff --git a/qcsrc/common/physics/movetypes/walk.qc b/qcsrc/common/physics/movetypes/walk.qc
new file mode 100644 (file)
index 0000000..e926246
--- /dev/null
@@ -0,0 +1,176 @@
+void _Movetype_Physics_Walk(entity this, 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(this))
+               _Movetype_UnstickEntity(this);
+
+       bool applygravity = (!_Movetype_CheckWater(this) && this.move_movetype == MOVETYPE_WALK && !(this.move_flags & FL_WATERJUMP));
+
+       _Movetype_CheckVelocity(this);
+
+       // do a regular slide move unless it looks like you ran into a step
+       bool oldonground = (this.move_flags & FL_ONGROUND);
+
+       vector start_origin = this.move_origin;
+       vector start_velocity = this.move_velocity;
+
+       int clip = _Movetype_FlyMove(this, dt, applygravity, stepnormal, GAMEPLAYFIX_STEPMULTIPLETIMES(this) ? PHYS_STEPHEIGHT(this) : 0);
+
+       if (GAMEPLAYFIX_DOWNTRACEONGROUND(this) && !(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 = this.move_origin + '0 0 1';
+               vector downmove = this.move_origin - '0 0 1';
+               int type;
+               if (this.move_movetype == MOVETYPE_FLYMISSILE)
+                       type = MOVE_MISSILE;
+               else if (this.move_movetype == MOVETYPE_FLY_WORLDONLY)
+                       type = MOVE_WORLDONLY;
+               else if (this.solid == SOLID_TRIGGER || this.solid == SOLID_NOT)
+                       type = MOVE_NOMONSTERS;
+               else type = MOVE_NORMAL;
+               tracebox(upmove, this.mins, this.maxs, downmove, type, this);
+               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))
+               this.move_flags &= ~FL_ONGROUND;
+
+       _Movetype_CheckVelocity(this);
+       _Movetype_LinkEdict(this, true);
+
+       if (clip & 8)  // teleport
+               return;
+
+       if (this.move_flags & FL_WATERJUMP)
+               return;
+
+       if (PHYS_NOSTEP(this))
+               return;
+
+       vector originalmove_origin = this.move_origin;
+       vector originalmove_velocity = this.move_velocity;
+       // originalmove_clip = clip;
+       int originalmove_flags = this.move_flags;
+       entity originalmove_groundentity = this.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 (this.move_movetype != MOVETYPE_FLY)
+               {
+                       // return if gibbed by a trigger
+                       if (this.move_movetype != MOVETYPE_WALK)
+                               return;
+
+                       // return if attempting to jump while airborn (unless sv_jumpstep)
+                       if (!PHYS_JUMPSTEP(this))
+                               if (!oldonground && this.move_waterlevel == 0)
+                                       return;
+               }
+
+               // try moving up and forward to go up a step
+               // back to start pos
+               this.move_origin = start_origin;
+               this.move_velocity = start_velocity;
+
+               // move up
+               vector upmove = '0 0 1' * PHYS_STEPHEIGHT(this);
+               _Movetype_PushEntity(this, upmove, true);
+               if(wasfreed(this))
+                       return;
+               if(trace_startsolid)
+               {
+                       // we got teleported when upstepping... must abort the move
+                       return;
+               }
+
+               // move forward
+               this.move_velocity_z = 0;
+               clip = _Movetype_FlyMove(this, dt, applygravity, stepnormal, 0);
+               this.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(this);
+               _Movetype_LinkEdict(this, true);
+
+               // check for stuckness, possibly due to the limited precision of floats
+               // in the clipping hulls
+               if (clip
+                   && fabs(originalmove_origin.y - this.move_origin.y) < 0.03125
+                   && fabs(originalmove_origin.x - this.move_origin.x) < 0.03125)
+               {
+                       // Con_Printf("wall\n");
+                       // stepping up didn't make any progress, revert to original move
+                       this.move_origin = originalmove_origin;
+                       this.move_velocity = originalmove_velocity;
+                       // clip = originalmove_clip;
+                       this.move_flags = originalmove_flags;
+                       this.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(this))
+                       _Movetype_WallFriction(this, 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(this) || this.move_waterlevel >= 3 || start_velocity.z >= (1.0 / 32.0) || !oldonground || (this.move_flags & FL_ONGROUND))
+       {
+               return;
+       }
+
+       // move down
+       vector downmove = '0 0 1' * (-PHYS_STEPHEIGHT(this) + start_velocity.z * dt);
+       _Movetype_PushEntity(this, downmove, true);
+       if(wasfreed(this))
+               return;
+
+       if(trace_startsolid)
+       {
+               // 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
+               this.move_origin = originalmove_origin;
+               this.move_velocity = originalmove_velocity;
+               this.move_flags = originalmove_flags;
+               this.move_groundentity = originalmove_groundentity;
+       }
+
+       _Movetype_CheckVelocity(this);
+       _Movetype_LinkEdict(this, true);
+}
diff --git a/qcsrc/common/physics/player.qc b/qcsrc/common/physics/player.qc
new file mode 100644 (file)
index 0000000..1fc9ccd
--- /dev/null
@@ -0,0 +1,1545 @@
+#include "player.qh"
+#include "../triggers/include.qh"
+#include "../viewloc.qh"
+
+#ifdef SVQC
+
+#include "../../server/miscfunctions.qh"
+#include "../triggers/trigger/viewloc.qh"
+
+// client side physics
+bool Physics_Valid(string thecvar)
+{
+       return autocvar_g_physics_clientselect && strhasword(autocvar_g_physics_clientselect_options, thecvar);
+}
+
+float Physics_ClientOption(entity this, string option)
+{
+       if(Physics_Valid(this.cvar_cl_physics))
+       {
+               string s = sprintf("g_physics_%s_%s", this.cvar_cl_physics, option);
+               if(cvar_type(s) & CVAR_TYPEFLAG_EXISTS)
+                       return cvar(s);
+       }
+       if(autocvar_g_physics_clientselect && autocvar_g_physics_clientselect_default)
+       {
+               string s = sprintf("g_physics_%s_%s", autocvar_g_physics_clientselect_default, option);
+               if(cvar_type(s) & CVAR_TYPEFLAG_EXISTS)
+                       return cvar(s);
+       }
+       return cvar(strcat("sv_", option));
+}
+
+void Physics_UpdateStats(entity this, float maxspd_mod)
+{
+       STAT(MOVEVARS_AIRACCEL_QW, this) = AdjustAirAccelQW(Physics_ClientOption(this, "airaccel_qw"), maxspd_mod);
+       STAT(MOVEVARS_AIRSTRAFEACCEL_QW, this) = (Physics_ClientOption(this, "airstrafeaccel_qw"))
+               ? AdjustAirAccelQW(Physics_ClientOption(this, "airstrafeaccel_qw"), maxspd_mod)
+               : 0;
+       STAT(MOVEVARS_AIRSPEEDLIMIT_NONQW, this) = Physics_ClientOption(this, "airspeedlimit_nonqw") * maxspd_mod;
+       STAT(MOVEVARS_MAXSPEED, this) = Physics_ClientOption(this, "maxspeed") * maxspd_mod; // also slow walking
+
+       // old stats
+       // fix some new settings
+       STAT(MOVEVARS_AIRACCEL_QW_STRETCHFACTOR, this) = Physics_ClientOption(this, "airaccel_qw_stretchfactor");
+       STAT(MOVEVARS_MAXAIRSTRAFESPEED, this) = Physics_ClientOption(this, "maxairstrafespeed");
+       STAT(MOVEVARS_MAXAIRSPEED, this) = Physics_ClientOption(this, "maxairspeed");
+       STAT(MOVEVARS_AIRSTRAFEACCELERATE, this) = Physics_ClientOption(this, "airstrafeaccelerate");
+       STAT(MOVEVARS_WARSOWBUNNY_TURNACCEL, this) = Physics_ClientOption(this, "warsowbunny_turnaccel");
+       STAT(MOVEVARS_AIRACCEL_SIDEWAYS_FRICTION, this) = Physics_ClientOption(this, "airaccel_sideways_friction");
+       STAT(MOVEVARS_AIRCONTROL, this) = Physics_ClientOption(this, "aircontrol");
+       STAT(MOVEVARS_AIRCONTROL_POWER, this) = Physics_ClientOption(this, "aircontrol_power");
+       STAT(MOVEVARS_AIRCONTROL_PENALTY, this) = Physics_ClientOption(this, "aircontrol_penalty");
+       STAT(MOVEVARS_WARSOWBUNNY_AIRFORWARDACCEL, this) = Physics_ClientOption(this, "warsowbunny_airforwardaccel");
+       STAT(MOVEVARS_WARSOWBUNNY_TOPSPEED, this) = Physics_ClientOption(this, "warsowbunny_topspeed");
+       STAT(MOVEVARS_WARSOWBUNNY_ACCEL, this) = Physics_ClientOption(this, "warsowbunny_accel");
+       STAT(MOVEVARS_WARSOWBUNNY_BACKTOSIDERATIO, this) = Physics_ClientOption(this, "warsowbunny_backtosideratio");
+       STAT(MOVEVARS_FRICTION, this) = Physics_ClientOption(this, "friction");
+       STAT(MOVEVARS_ACCELERATE, this) = Physics_ClientOption(this, "accelerate");
+       STAT(MOVEVARS_STOPSPEED, this) = Physics_ClientOption(this, "stopspeed");
+       STAT(MOVEVARS_AIRACCELERATE, this) = Physics_ClientOption(this, "airaccelerate");
+       STAT(MOVEVARS_AIRSTOPACCELERATE, this) = Physics_ClientOption(this, "airstopaccelerate");
+       STAT(MOVEVARS_JUMPVELOCITY, this) = Physics_ClientOption(this, "jumpvelocity");
+       STAT(MOVEVARS_TRACK_CANJUMP, this) = Physics_ClientOption(this, "track_canjump");
+}
+#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);
+}
+
+#define unstick_offsets(X) \
+/* 1 no nudge (just return the original if this test passes) */ \
+       X(' 0.000  0.000  0.000') \
+/* 6 simple nudges */ \
+       X(' 0.000  0.000  0.125') X('0.000  0.000 -0.125') \
+       X('-0.125  0.000  0.000') X('0.125  0.000  0.000') \
+       X(' 0.000 -0.125  0.000') X('0.000  0.125  0.000') \
+/* 4 diagonal flat nudges */ \
+       X('-0.125 -0.125  0.000') X('0.125 -0.125  0.000') \
+       X('-0.125  0.125  0.000') X('0.125  0.125  0.000') \
+/* 8 diagonal upward nudges */ \
+       X('-0.125  0.000  0.125') X('0.125  0.000  0.125') \
+       X(' 0.000 -0.125  0.125') X('0.000  0.125  0.125') \
+       X('-0.125 -0.125  0.125') X('0.125 -0.125  0.125') \
+       X('-0.125  0.125  0.125') X('0.125  0.125  0.125') \
+/* 8 diagonal downward nudges */ \
+       X('-0.125  0.000 -0.125') X('0.125  0.000 -0.125') \
+       X(' 0.000 -0.125 -0.125') X('0.000  0.125 -0.125') \
+       X('-0.125 -0.125 -0.125') X('0.125 -0.125 -0.125') \
+       X('-0.125  0.125 -0.125') X('0.125  0.125 -0.125') \
+/**/
+
+void PM_ClientMovement_Unstick(entity this)
+{
+       #define X(unstick_offset) \
+       { \
+               vector neworigin = unstick_offset + this.origin; \
+               tracebox(neworigin, PL_CROUCH_MIN, PL_CROUCH_MAX, neworigin, MOVE_NORMAL, this); \
+               if (!trace_startsolid) \
+               { \
+                       setorigin(this, neworigin); \
+                       return; \
+               } \
+       }
+       unstick_offsets(X);
+       #undef X
+}
+
+void PM_ClientMovement_UpdateStatus(entity this, bool ground)
+{
+#ifdef CSQC
+       if(!IS_PLAYER(this))
+               return;
+       // make sure player is not stuck
+       if(autocvar_cl_movement == 3)
+               PM_ClientMovement_Unstick(this);
+
+       // set crouched
+       if (PHYS_INPUT_BUTTON_CROUCH(this))
+       {
+               // wants to crouch, this always works
+               if (!IS_DUCKED(this)) SET_DUCKED(this);
+       }
+       else
+       {
+               // wants to stand, if currently crouching we need to check for a low ceiling first
+               if (IS_DUCKED(this))
+               {
+                       tracebox(this.origin, PL_MIN, PL_MAX, this.origin, MOVE_NORMAL, this);
+                       if (!trace_startsolid) UNSET_DUCKED(this);
+               }
+       }
+
+       // set onground
+       vector origin1 = this.origin + '0 0 1';
+       vector origin2 = this.origin - '0 0 1';
+
+       if (ground && autocvar_cl_movement == 3)
+       {
+               tracebox(origin1, this.mins, this.maxs, origin2, MOVE_NORMAL, this);
+               if (trace_fraction < 1.0 && trace_plane_normal.z > 0.7)
+               {
+                       SET_ONGROUND(this);
+
+                       // this code actually "predicts" an impact; so let's clip velocity first
+                       this.velocity -= this.velocity * trace_plane_normal * trace_plane_normal;
+               }
+               else
+                       UNSET_ONGROUND(this);
+       }
+
+       if(autocvar_cl_movement == 3)
+       {
+               // set watertype/waterlevel
+               origin1 = this.origin;
+               origin1.z += this.mins_z + 1;
+               this.waterlevel = WATERLEVEL_NONE;
+
+               int thepoint = pointcontents(origin1);
+
+               this.watertype = (thepoint == CONTENT_WATER || thepoint == CONTENT_LAVA || thepoint == CONTENT_SLIME);
+
+               if (this.watertype)
+               {
+                       this.waterlevel = WATERLEVEL_WETFEET;
+                       origin1.z = this.origin.z + (this.mins.z + this.maxs.z) * 0.5;
+                       thepoint = pointcontents(origin1);
+                       if (thepoint == CONTENT_WATER || thepoint == CONTENT_LAVA || thepoint == CONTENT_SLIME)
+                       {
+                               this.waterlevel = WATERLEVEL_SWIMMING;
+                               origin1.z = this.origin.z + 22;
+                               thepoint = pointcontents(origin1);
+                               if (thepoint == CONTENT_WATER || thepoint == CONTENT_LAVA || thepoint == CONTENT_SLIME)
+                                       this.waterlevel = WATERLEVEL_SUBMERGED;
+                       }
+               }
+       }
+
+       if (IS_ONGROUND(this) || this.velocity.z <= 0 || pmove_waterjumptime <= 0)
+               pmove_waterjumptime = 0;
+#endif
+}
+
+void PM_ClientMovement_Move(entity this)
+{
+#ifdef CSQC
+
+       PM_ClientMovement_UpdateStatus(this, false);
+       if(autocvar_cl_movement == 1)
+               return;
+
+       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';
+
+       primalvelocity = this.velocity;
+       for(bump = 0, t = PHYS_INPUT_TIMELENGTH; bump < 8 && (this.velocity * this.velocity) > 0; bump++)
+       {
+               neworigin = this.origin + t * this.velocity;
+               tracebox(this.origin, this.mins, this.maxs, neworigin, MOVE_NORMAL, this);
+               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 = this.origin;
+                       currentorigin2_z += PHYS_STEPHEIGHT(this);
+                       neworigin2 = neworigin;
+                       neworigin2_z += PHYS_STEPHEIGHT(this);
+                       tracebox(currentorigin2, this.mins, this.maxs, neworigin2, MOVE_NORMAL, this);
+                       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 = this.origin_z;
+                               tracebox(currentorigin2, this.mins, this.maxs, neworigin2, MOVE_NORMAL, this);
+                               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(this, 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(this);
+
+               t -= t * trace1_fraction;
+
+               f = (this.velocity * trace1_plane_normal);
+               this.velocity = this.velocity + -f * trace1_plane_normal;
+       }
+       if(PHYS_TELEPORT_TIME(this) > 0)
+               this.velocity = primalvelocity;
+#endif
+}
+
+void CPM_PM_Aircontrol(entity this, vector wishdir, float wishspeed)
+{
+       float k = 32 * (2 * IsMoveInDirection(this.movement, 0) - 1);
+       if (k <= 0)
+               return;
+
+       k *= bound(0, wishspeed / PHYS_MAXAIRSPEED(this), 1);
+
+       float zspeed = this.velocity_z;
+       this.velocity_z = 0;
+       float xyspeed = vlen(this.velocity);
+       this.velocity = normalize(this.velocity);
+
+       float dot = this.velocity * wishdir;
+
+       if (dot > 0) // we can't change direction while slowing down
+       {
+               k *= pow(dot, PHYS_AIRCONTROL_POWER(this)) * PHYS_INPUT_TIMELENGTH;
+               xyspeed = max(0, xyspeed - PHYS_AIRCONTROL_PENALTY(this) * sqrt(max(0, 1 - dot*dot)) * k/32);
+               k *= PHYS_AIRCONTROL(this);
+               this.velocity = normalize(this.velocity * xyspeed + wishdir * k);
+       }
+
+       this.velocity = this.velocity * xyspeed;
+       this.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(entity this, 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 = this.velocity * wishdir;
+       float vel_z = this.velocity_z;
+       vector vel_xy = vec2(this.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 themin = (vel_xy_backward * vel_xy_backward - vel_straight * vel_straight) / (vel_perpend * vel_perpend);
+               // assume: themin > 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 (themin <= 0)
+                       vel_perpend *= f;
+               else
+               {
+                       themin = sqrt(themin);
+                       vel_perpend *= max(themin, 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);
+               }
+       }
+
+       this.velocity = vel_xy + vel_z * '0 0 1';
+}
+
+void PM_AirAccelerate(entity this, vector wishdir, float wishspeed)
+{
+       if (wishspeed == 0)
+               return;
+
+       vector curvel = this.velocity;
+       curvel_z = 0;
+       float curspeed = vlen(curvel);
+
+       if (wishspeed > curspeed * 1.01)
+               wishspeed = min(wishspeed, curspeed + PHYS_WARSOWBUNNY_AIRFORWARDACCEL(this) * PHYS_MAXSPEED(this) * PHYS_INPUT_TIMELENGTH);
+       else
+       {
+               float f = max(0, (PHYS_WARSOWBUNNY_TOPSPEED(this) - curspeed) / (PHYS_WARSOWBUNNY_TOPSPEED(this) - PHYS_MAXSPEED(this)));
+               wishspeed = max(curspeed, PHYS_MAXSPEED(this)) + PHYS_WARSOWBUNNY_ACCEL(this) * f * PHYS_MAXSPEED(this) * 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(this) * PHYS_MAXSPEED(this) * PHYS_INPUT_TIMELENGTH);
+
+       if (PHYS_WARSOWBUNNY_BACKTOSIDERATIO(this) < 1)
+       {
+               vector curdir = normalize(curvel);
+               float dot = acceldir * curdir;
+               if (dot < 0)
+                       acceldir -= (1 - PHYS_WARSOWBUNNY_BACKTOSIDERATIO(this)) * dot * curdir;
+       }
+
+       this.velocity += accelspeed * acceldir;
+}
+
+
+/*
+=============
+PlayerJump
+
+When you press the jump key
+returns true if handled
+=============
+*/
+bool PlayerJump(entity this)
+{
+       if (PHYS_FROZEN(this))
+               return true; // no jumping in freezetag when frozen
+
+#ifdef SVQC
+       if (this.player_blocked)
+               return true; // no jumping while blocked
+#endif
+
+       bool doublejump = false;
+       float mjumpheight = PHYS_JUMPVELOCITY(this);
+
+       if (MUTATOR_CALLHOOK(PlayerJump, this, doublejump, mjumpheight))
+               return true;
+
+       doublejump = player_multijump;
+       mjumpheight = player_jumpheight;
+
+       if (this.waterlevel >= WATERLEVEL_SWIMMING)
+       {
+               if(this.viewloc)
+               {
+                       doublejump = true;
+                       mjumpheight *= 0.7;
+               }
+               else
+               {
+                       this.velocity_z = PHYS_MAXSPEED(this) * 0.7;
+                       return true;
+               }
+       }
+
+       if (!doublejump)
+               if (!IS_ONGROUND(this))
+                       return IS_JUMP_HELD(this);
+
+       bool track_jump = PHYS_CL_TRACK_CANJUMP(this);
+       if(PHYS_TRACK_CANJUMP(this))
+               track_jump = true;
+
+       if (track_jump)
+               if (IS_JUMP_HELD(this))
+                       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 * stof(PHYS_JUMPSPEEDCAP_MIN);
+
+               if (this.velocity_z < minjumpspeed)
+                       mjumpheight += minjumpspeed - this.velocity_z;
+       }
+
+       if(PHYS_JUMPSPEEDCAP_MAX != "")
+       {
+               // don't do jump speedcaps on ramps to preserve old xonotic ramjump style
+               tracebox(this.origin + '0 0 0.01', this.mins, this.maxs, this.origin - '0 0 0.01', MOVE_NORMAL, this);
+
+               if (!(trace_fraction < 1 && trace_plane_normal_z < 0.98 && PHYS_JUMPSPEEDCAP_DISABLE_ONRAMPS(this)))
+               {
+                       float maxjumpspeed = mjumpheight * stof(PHYS_JUMPSPEEDCAP_MAX);
+
+                       if (this.velocity_z > maxjumpspeed)
+                               mjumpheight -= this.velocity_z - maxjumpspeed;
+               }
+       }
+
+       if (!WAS_ONGROUND(this))
+       {
+#ifdef SVQC
+               if(autocvar_speedmeter)
+                       LOG_TRACE(strcat("landing velocity: ", vtos(this.velocity), " (abs: ", ftos(vlen(this.velocity)), ")\n"));
+#endif
+               if(this.lastground < time - 0.3)
+               {
+                       float f = (1 - PHYS_FRICTION_ONLAND(this));
+                       this.velocity_x *= f;
+                       this.velocity_y *= f;
+               }
+#ifdef SVQC
+               if(this.jumppadcount > 1)
+                       LOG_TRACE(strcat(ftos(this.jumppadcount), "x jumppad combo\n"));
+               this.jumppadcount = 0;
+#endif
+       }
+
+       this.velocity_z += mjumpheight;
+
+       UNSET_ONGROUND(this);
+       SET_JUMP_HELD(this);
+
+#ifdef SVQC
+
+       this.oldvelocity_z = this.velocity_z;
+
+       animdecide_setaction(this, ANIMACTION_JUMP, true);
+
+       if (autocvar_g_jump_grunt)
+               PlayerSound(this, playersound_jump, CH_PLAYER, VOICETYPE_PLAYERSOUND);
+#endif
+       return true;
+}
+
+void CheckWaterJump(entity this)
+{
+// check for a jump-out-of-water
+       makevectors(this.v_angle);
+       vector start = this.origin;
+       start_z += 8;
+       v_forward_z = 0;
+       normalize(v_forward);
+       vector end = start + v_forward*24;
+       traceline (start, end, true, this);
+       if (trace_fraction < 1)
+       {       // solid at waist
+               start_z = start_z + this.maxs_z - 8;
+               end = start + v_forward*24;
+               this.movedir = trace_plane_normal * -50;
+               traceline(start, end, true, this);
+               if (trace_fraction == 1)
+               {       // open at eye level
+                       this.velocity_z = 225;
+                       this.flags |= FL_WATERJUMP;
+                       SET_JUMP_HELD(this);
+               #ifdef SVQC
+                       PHYS_TELEPORT_TIME(this) = 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;
+void CheckPlayerJump(entity this)
+{
+#ifdef SVQC
+       float was_flying = ITEMS_STAT(this) & IT_USING_JETPACK;
+#endif
+       if (JETPACK_JUMP(this) < 2)
+               ITEMS_STAT(this) &= ~IT_USING_JETPACK;
+
+       if(PHYS_INPUT_BUTTON_JUMP(this) || PHYS_INPUT_BUTTON_JETPACK(this))
+       {
+               float air_jump = !PlayerJump(this) || player_multijump; // PlayerJump() has important side effects
+               float activate = JETPACK_JUMP(this) && air_jump && PHYS_INPUT_BUTTON_JUMP(this) || PHYS_INPUT_BUTTON_JETPACK(this);
+               float has_fuel = !PHYS_JETPACK_FUEL(this) || PHYS_AMMO_FUEL(this) || ITEMS_STAT(this) & IT_UNLIMITED_WEAPON_AMMO;
+
+               if (!(ITEMS_STAT(this) & ITEM_Jetpack.m_itemid)) { }
+               else if (this.jetpack_stopped) { }
+               else if (!has_fuel)
+               {
+#ifdef SVQC
+                       if (was_flying) // TODO: ran out of fuel message
+                               Send_Notification(NOTIF_ONE, this, MSG_INFO, INFO_JETPACK_NOFUEL);
+                       else if (activate)
+                               Send_Notification(NOTIF_ONE, this, MSG_INFO, INFO_JETPACK_NOFUEL);
+#endif
+                       this.jetpack_stopped = true;
+                       ITEMS_STAT(this) &= ~IT_USING_JETPACK;
+               }
+               else if (activate && !PHYS_FROZEN(this))
+                       ITEMS_STAT(this) |= IT_USING_JETPACK;
+       }
+       else
+       {
+               this.jetpack_stopped = false;
+               ITEMS_STAT(this) &= ~IT_USING_JETPACK;
+       }
+       if (!PHYS_INPUT_BUTTON_JUMP(this))
+               UNSET_JUMP_HELD(this);
+
+       if (this.waterlevel == WATERLEVEL_SWIMMING)
+               CheckWaterJump(this);
+}
+
+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;
+}
+
+string specialcommand = "xwxwxsxsxaxdxaxdx1x ";
+.float specialcommand_pos;
+void SpecialCommand()
+{
+#ifdef SVQC
+       if (!CheatImpulse(CHIMPULSE_GIVE_ALL.impulse))
+               LOG_INFO("A hollow voice says \"Plugh\".\n");
+#endif
+}
+
+float PM_check_specialcommand(entity this, 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, this.specialcommand_pos, 1))
+       {
+               this.specialcommand_pos += 1;
+               if (this.specialcommand_pos >= strlen(specialcommand))
+               {
+                       this.specialcommand_pos = 0;
+                       SpecialCommand();
+                       return true;
+               }
+       }
+       else if (this.specialcommand_pos && (c != substring(specialcommand, this.specialcommand_pos - 1, 1)))
+               this.specialcommand_pos = 0;
+#endif
+       return false;
+}
+
+void PM_check_nickspam(entity this)
+{
+#ifdef SVQC
+       if (time >= this.nickspamtime)
+               return;
+       if (this.nickspamcount >= autocvar_g_nick_flood_penalty_yellow)
+       {
+               // slight annoyance for nick change scripts
+               this.movement = -1 * this.movement;
+               this.BUTTON_ATCK = this.BUTTON_JUMP = this.BUTTON_ATCK2 = this.BUTTON_ZOOM = this.BUTTON_CROUCH = this.BUTTON_HOOK = this.BUTTON_USE = 0;
+
+               if (this.nickspamcount >= autocvar_g_nick_flood_penalty_red) // if you are persistent and the slight annoyance above does not stop you, I'll show you!
+               {
+                       this.v_angle_x = random() * 360;
+                       this.v_angle_y = random() * 360;
+                       // at least I'm not forcing retardedview by also assigning to angles_z
+                       this.fixangle = true;
+               }
+       }
+#endif
+}
+
+void PM_check_punch(entity this)
+{
+#ifdef SVQC
+       if (this.punchangle != '0 0 0')
+       {
+               float f = vlen(this.punchangle) - 10 * PHYS_INPUT_TIMELENGTH;
+               if (f > 0)
+                       this.punchangle = normalize(this.punchangle) * f;
+               else
+                       this.punchangle = '0 0 0';
+       }
+
+       if (this.punchvector != '0 0 0')
+       {
+               float f = vlen(this.punchvector) - 30 * PHYS_INPUT_TIMELENGTH;
+               if (f > 0)
+                       this.punchvector = normalize(this.punchvector) * f;
+               else
+                       this.punchvector = '0 0 0';
+       }
+#endif
+}
+
+// predict frozen movement, as frozen players CAN move in some cases
+void PM_check_frozen(entity this)
+{
+       if (!PHYS_FROZEN(this))
+               return;
+       if (PHYS_DODGING_FROZEN(this)
+#ifdef SVQC
+       && IS_REAL_CLIENT(this)
+#endif
+       )
+       {
+               this.movement_x = bound(-5, this.movement.x, 5);
+               this.movement_y = bound(-5, this.movement.y, 5);
+               this.movement_z = bound(-5, this.movement.z, 5);
+       }
+       else
+               this.movement = '0 0 0';
+
+       vector midpoint = ((this.absmin + this.absmax) * 0.5);
+       if (pointcontents(midpoint) == CONTENT_WATER)
+       {
+               this.velocity = this.velocity * 0.5;
+
+               if (pointcontents(midpoint + '0 0 16') == CONTENT_WATER)
+                       this.velocity_z = 200;
+       }
+}
+
+void PM_check_hitground(entity this)
+{
+#ifdef SVQC
+       if (!this.wasFlying) return;
+    this.wasFlying = false;
+    if (this.waterlevel >= WATERLEVEL_SWIMMING) return;
+    if (time < this.ladder_time) return;
+    if (this.hook) return;
+    this.nextstep = time + 0.3 + random() * 0.1;
+    trace_dphitq3surfaceflags = 0;
+    tracebox(this.origin, this.mins, this.maxs, this.origin - '0 0 1', MOVE_NOMONSTERS, this);
+    if (trace_dphitq3surfaceflags & Q3SURFACEFLAG_NOSTEPS) return;
+    entity gs = (trace_dphitq3surfaceflags & Q3SURFACEFLAG_METALSTEPS)
+       ? GS_FALL_METAL
+       : GS_FALL;
+    GlobalSound(this, gs, CH_PLAYER, VOICETYPE_PLAYERSOUND);
+#endif
+}
+
+void PM_Footsteps(entity this)
+{
+#ifdef SVQC
+       if (!g_footsteps) return;
+       if (IS_DUCKED(this)) return;
+       if (time >= this.lastground + 0.2) return;
+       if (vdist(this.velocity, <=, autocvar_sv_maxspeed * 0.6)) return;
+       if ((time > this.nextstep) || (time < (this.nextstep - 10.0)))
+       {
+               this.nextstep = time + 0.3 + random() * 0.1;
+               trace_dphitq3surfaceflags = 0;
+               tracebox(this.origin, this.mins, this.maxs, this.origin - '0 0 1', MOVE_NOMONSTERS, this);
+               if (trace_dphitq3surfaceflags & Q3SURFACEFLAG_NOSTEPS) return;
+               entity gs = (trace_dphitq3surfaceflags & Q3SURFACEFLAG_METALSTEPS)
+                       ? GS_STEP_METAL
+                       : GS_STEP;
+               GlobalSound(this, gs, CH_PLAYER, VOICETYPE_PLAYERSOUND);
+       }
+#endif
+}
+
+void PM_check_blocked(entity this)
+{
+#ifdef SVQC
+       if (!this.player_blocked)
+               return;
+       this.movement = '0 0 0';
+       this.disableclientprediction = 1;
+#endif
+}
+
+void PM_fly(entity this, float maxspd_mod)
+{
+       // noclipping or flying
+       UNSET_ONGROUND(this);
+
+       this.velocity = this.velocity * (1 - PHYS_INPUT_TIMELENGTH * PHYS_FRICTION(this));
+       makevectors(this.v_angle);
+       //wishvel = v_forward * this.movement.x + v_right * this.movement.y + v_up * this.movement.z;
+       vector wishvel = v_forward * this.movement.x
+                                       + v_right * this.movement.y
+                                       + '0 0 1' * this.movement.z;
+       // acceleration
+       vector wishdir = normalize(wishvel);
+       float wishspeed = min(vlen(wishvel), PHYS_MAXSPEED(this) * maxspd_mod);
+#ifdef SVQC
+       if(time >= PHYS_TELEPORT_TIME(this))
+#endif
+               PM_Accelerate(this, wishdir, wishspeed, wishspeed, PHYS_ACCELERATE(this) * maxspd_mod, 1, 0, 0, 0);
+       PM_ClientMovement_Move(this);
+}
+
+void PM_swim(entity this, float maxspd_mod)
+{
+       // swimming
+       UNSET_ONGROUND(this);
+
+       float jump = PHYS_INPUT_BUTTON_JUMP(this);
+       // water jump only in certain situations
+       // this mimics quakeworld code
+       if (jump && this.waterlevel == WATERLEVEL_SWIMMING && this.velocity_z >= -180 && !this.viewloc)
+       {
+               vector yawangles = '0 1 0' * this.v_angle.y;
+               makevectors(yawangles);
+               vector forward = v_forward;
+               vector spot = this.origin + 24 * forward;
+               spot_z += 8;
+               traceline(spot, spot, MOVE_NOMONSTERS, this);
+               if (trace_startsolid)
+               {
+                       spot_z += 24;
+                       traceline(spot, spot, MOVE_NOMONSTERS, this);
+                       if (!trace_startsolid)
+                       {
+                               this.velocity = forward * 50;
+                               this.velocity_z = 310;
+                       #ifdef CSQC
+                               pmove_waterjumptime = 2;
+                       #endif
+                               UNSET_ONGROUND(this);
+                               SET_JUMP_HELD(this);
+                       }
+               }
+       }
+       makevectors(this.v_angle);
+       //wishvel = v_forward * this.movement.x + v_right * this.movement.y + v_up * this.movement.z;
+       vector wishvel = v_forward * this.movement.x
+                                       + v_right * this.movement.y
+                                       + '0 0 1' * this.movement.z;
+       if(this.viewloc)
+               wishvel.z = -160; // drift anyway
+       else if (wishvel == '0 0 0')
+               wishvel = '0 0 -60'; // drift towards bottom
+
+
+       vector wishdir = normalize(wishvel);
+       float wishspeed = min(vlen(wishvel), PHYS_MAXSPEED(this) * maxspd_mod) * 0.7;
+
+       if (IS_DUCKED(this))
+       wishspeed *= 0.5;
+
+//     if (pmove_waterjumptime <= 0) // TODO: use
+    {
+               // water friction
+               float f = 1 - PHYS_INPUT_TIMELENGTH * PHYS_FRICTION(this);
+               f = min(max(0, f), 1);
+               this.velocity *= f;
+
+               f = wishspeed - this.velocity * wishdir;
+               if (f > 0)
+               {
+                       float accelspeed = min(PHYS_ACCELERATE(this) * PHYS_INPUT_TIMELENGTH * wishspeed, f);
+                       this.velocity += accelspeed * wishdir;
+               }
+
+               // holding jump button swims upward slowly
+               if (jump && !this.viewloc)
+               {
+#if 0
+                       if (this.watertype & CONTENT_LAVA)
+                               this.velocity_z =  50;
+                       else if (this.watertype & CONTENT_SLIME)
+                               this.velocity_z =  80;
+                       else
+                       {
+                               if (IS_NEXUIZ_DERIVED(gamemode))
+#endif
+                                       this.velocity_z = 200;
+#if 0
+                               else
+                                       this.velocity_z = 100;
+                       }
+#endif
+               }
+       }
+       if(this.viewloc)
+       {
+               const float addspeed = wishspeed - this.velocity * wishdir;
+               if (addspeed > 0)
+               {
+                       const float accelspeed = min(PHYS_ACCELERATE(this) * PHYS_INPUT_TIMELENGTH * wishspeed, addspeed);
+                       this.velocity += accelspeed * wishdir;
+               }
+       }
+       else
+       {
+               // water acceleration
+               PM_Accelerate(this, wishdir, wishspeed, wishspeed, PHYS_ACCELERATE(this) * maxspd_mod, 1, 0, 0, 0);
+               PM_ClientMovement_Move(this);
+       }
+}
+
+.vector oldmovement;
+void PM_ladder(entity this, float maxspd_mod)
+{
+       // on a spawnfunc_func_ladder or swimming in spawnfunc_func_water
+       UNSET_ONGROUND(this);
+
+       float g;
+       g = PHYS_GRAVITY(this) * PHYS_INPUT_TIMELENGTH;
+       if (PHYS_ENTGRAVITY(this))
+               g *= PHYS_ENTGRAVITY(this);
+       if (GAMEPLAYFIX_GRAVITYUNAFFECTEDBYTICRATE)
+       {
+               g *= 0.5;
+               this.velocity_z += g;
+       }
+
+       this.velocity = this.velocity * (1 - PHYS_INPUT_TIMELENGTH * PHYS_FRICTION(this));
+       makevectors(this.v_angle);
+       //wishvel = v_forward * this.movement.x + v_right * this.movement.y + v_up * this.movement.z;
+       vector wishvel = v_forward * this.movement_x
+                                       + v_right * this.movement_y
+                                       + '0 0 1' * this.movement_z;
+       if(this.viewloc)
+               wishvel.z = this.oldmovement.x;
+       this.velocity_z += g;
+       if (this.ladder_entity.classname == "func_water")
+       {
+               float f = vlen(wishvel);
+               if (f > this.ladder_entity.speed)
+                       wishvel *= (this.ladder_entity.speed / f);
+
+               this.watertype = this.ladder_entity.skin;
+               f = this.ladder_entity.origin_z + this.ladder_entity.maxs_z;
+               if ((this.origin_z + this.view_ofs_z) < f)
+                       this.waterlevel = WATERLEVEL_SUBMERGED;
+               else if ((this.origin_z + (this.mins_z + this.maxs_z) * 0.5) < f)
+                       this.waterlevel = WATERLEVEL_SWIMMING;
+               else if ((this.origin_z + this.mins_z + 1) < f)
+                       this.waterlevel = WATERLEVEL_WETFEET;
+               else
+               {
+                       this.waterlevel = WATERLEVEL_NONE;
+                       this.watertype = CONTENT_EMPTY;
+               }
+       }
+       // acceleration
+       vector wishdir = normalize(wishvel);
+       float wishspeed = min(vlen(wishvel), PHYS_MAXSPEED(this) * maxspd_mod);
+       if(time >= PHYS_TELEPORT_TIME(this))
+               // water acceleration
+               PM_Accelerate(this, wishdir, wishspeed, wishspeed, PHYS_ACCELERATE(this)*maxspd_mod, 1, 0, 0, 0);
+       PM_ClientMovement_Move(this);
+}
+
+void PM_jetpack(entity this, float maxspd_mod)
+{
+       //makevectors(this.v_angle.y * '0 1 0');
+       makevectors(this.v_angle);
+       vector wishvel = v_forward * this.movement_x
+                                       + v_right * this.movement_y;
+       // add remaining speed as Z component
+       float maxairspd = PHYS_MAXAIRSPEED(this) * 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 (this.BUTTON_JUMP)
+               wishvel_z = sqrt(max(0, 1 - wishvel * wishvel));
+
+       // it is now normalized, so...
+       float a_side = PHYS_JETPACK_ACCEL_SIDE(this);
+       float a_up = PHYS_JETPACK_ACCEL_UP(this);
+       float a_add = PHYS_JETPACK_ANTIGRAVITY(this) * PHYS_GRAVITY(this);
+
+       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 - (this.velocity * normalize(wishvel_x * '1 0 0' + wishvel_y * '0 1 0')) / PHYS_JETPACK_MAXSPEED_SIDE(this), 1);
+       if (wishvel_z - PHYS_GRAVITY(this) > 0)
+               fz = bound(0, 1 - this.velocity_z / PHYS_JETPACK_MAXSPEED_UP(this), 1);
+       else
+               fz = bound(0, 1 + this.velocity_z / PHYS_JETPACK_MAXSPEED_UP(this), 1);
+
+       float fvel;
+       fvel = vlen(wishvel);
+       wishvel_x *= fxy;
+       wishvel_y *= fxy;
+       wishvel_z = (wishvel_z - PHYS_GRAVITY(this)) * fz + PHYS_GRAVITY(this);
+
+       fvel = min(1, vlen(wishvel) / best);
+       if (PHYS_JETPACK_FUEL(this) && !(ITEMS_STAT(this) & IT_UNLIMITED_WEAPON_AMMO))
+               f = min(1, PHYS_AMMO_FUEL(this) / (PHYS_JETPACK_FUEL(this) * PHYS_INPUT_TIMELENGTH * fvel));
+       else
+               f = 1;
+
+       //print("this acceleration: ", ftos(vlen(wishvel) * f), "\n");
+
+       if (f > 0 && wishvel != '0 0 0')
+       {
+               this.velocity = this.velocity + wishvel * f * PHYS_INPUT_TIMELENGTH;
+               UNSET_ONGROUND(this);
+
+#ifdef SVQC
+               if (!(ITEMS_STAT(this) & IT_UNLIMITED_WEAPON_AMMO))
+                       this.ammo_fuel -= PHYS_JETPACK_FUEL(this) * PHYS_INPUT_TIMELENGTH * fvel * f;
+
+               ITEMS_STAT(this) |= IT_USING_JETPACK;
+
+               // jetpack also inhibits health regeneration, but only for 1 second
+               this.pauseregen_finished = max(this.pauseregen_finished, time + autocvar_g_balance_pause_fuel_regen);
+#endif
+       }
+
+#ifdef CSQC
+       float g = PHYS_GRAVITY(this) * PHYS_ENTGRAVITY(this) * PHYS_INPUT_TIMELENGTH;
+       if(autocvar_cl_movement == 3)
+       {
+               if (GAMEPLAYFIX_GRAVITYUNAFFECTEDBYTICRATE)
+                       this.velocity_z -= g * 0.5;
+               else
+                       this.velocity_z -= g;
+       }
+       PM_ClientMovement_Move(this);
+       if(autocvar_cl_movement == 3)
+       {
+               if (!IS_ONGROUND(this) || !(GAMEPLAYFIX_NOGRAVITYONGROUND))
+                       if (GAMEPLAYFIX_GRAVITYUNAFFECTEDBYTICRATE)
+                               this.velocity_z -= g * 0.5;
+       }
+#endif
+}
+
+void PM_walk(entity this, float maxspd_mod)
+{
+       if (!WAS_ONGROUND(this))
+       {
+#ifdef SVQC
+               if (autocvar_speedmeter)
+                       LOG_TRACE(strcat("landing velocity: ", vtos(this.velocity), " (abs: ", ftos(vlen(this.velocity)), ")\n"));
+#endif
+               if (this.lastground < time - 0.3)
+                       this.velocity *= (1 - PHYS_FRICTION_ONLAND(this));
+#ifdef SVQC
+               if (this.jumppadcount > 1)
+                       LOG_TRACE(strcat(ftos(this.jumppadcount), "x jumppad combo\n"));
+               this.jumppadcount = 0;
+#endif
+       }
+
+       // walking
+       makevectors(this.v_angle.y * '0 1 0');
+       const vector wishvel = v_forward * this.movement.x
+                                               + v_right * this.movement.y;
+       // acceleration
+       const vector wishdir = normalize(wishvel);
+       float wishspeed = vlen(wishvel);
+       wishspeed = min(wishspeed, PHYS_MAXSPEED(this) * maxspd_mod);
+       if (IS_DUCKED(this)) wishspeed *= 0.5;
+
+       // apply edge friction
+       const float f2 = vlen2(vec2(this.velocity));
+       if (f2 > 0)
+       {
+               trace_dphitq3surfaceflags = 0;
+               tracebox(this.origin, this.mins, this.maxs, this.origin - '0 0 1', MOVE_NOMONSTERS, this);
+               // TODO: apply edge friction
+               // apply ground friction
+               const int realfriction = (trace_dphitq3surfaceflags & Q3SURFACEFLAG_SLICK)
+                       ? PHYS_FRICTION_SLICK(this)
+                       : PHYS_FRICTION(this);
+
+               float f = sqrt(f2);
+               f = 1 - PHYS_INPUT_TIMELENGTH * realfriction * ((f < PHYS_STOPSPEED(this)) ? (PHYS_STOPSPEED(this) / f) : 1);
+               f = max(0, f);
+               this.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(this) / v0) * PHYS_FRICTION(this))
+                         = v0 - PHYS_INPUT_TIMELENGTH * PHYS_STOPSPEED(this) * PHYS_FRICTION(this)
+                       v0 = v + PHYS_INPUT_TIMELENGTH * PHYS_STOPSPEED(this) * PHYS_FRICTION(this)
+                  and
+                       v = v0 * (1 - PHYS_INPUT_TIMELENGTH * PHYS_FRICTION(this))
+                       v0 = v / (1 - PHYS_INPUT_TIMELENGTH * PHYS_FRICTION(this))
+
+                  These cases would be chosen ONLY if:
+                       v0 < PHYS_STOPSPEED(this)
+                       v + PHYS_INPUT_TIMELENGTH * PHYS_STOPSPEED(this) * PHYS_FRICTION(this) < PHYS_STOPSPEED(this)
+                       v < PHYS_STOPSPEED(this) * (1 - PHYS_INPUT_TIMELENGTH * PHYS_FRICTION(this))
+                  and, respectively:
+                       v0 >= PHYS_STOPSPEED(this)
+                       v / (1 - PHYS_INPUT_TIMELENGTH * PHYS_FRICTION(this)) >= PHYS_STOPSPEED(this)
+                       v >= PHYS_STOPSPEED(this) * (1 - PHYS_INPUT_TIMELENGTH * PHYS_FRICTION(this))
+                */
+       }
+       const float addspeed = wishspeed - this.velocity * wishdir;
+       if (addspeed > 0)
+       {
+               const float accelspeed = min(PHYS_ACCELERATE(this) * PHYS_INPUT_TIMELENGTH * wishspeed, addspeed);
+               this.velocity += accelspeed * wishdir;
+       }
+#ifdef CSQC
+       float g = PHYS_GRAVITY(this) * PHYS_ENTGRAVITY(this) * PHYS_INPUT_TIMELENGTH;
+       if(autocvar_cl_movement == 3)
+       {
+               if (!(GAMEPLAYFIX_NOGRAVITYONGROUND))
+                       this.velocity_z -= g * (GAMEPLAYFIX_GRAVITYUNAFFECTEDBYTICRATE ? 0.5 : 1);
+       }
+       if (vdist(this.velocity, >, 0))
+               PM_ClientMovement_Move(this);
+       if(autocvar_cl_movement == 3)
+       {
+               if (GAMEPLAYFIX_GRAVITYUNAFFECTEDBYTICRATE)
+                       if (!IS_ONGROUND(this) || !GAMEPLAYFIX_NOGRAVITYONGROUND)
+                               this.velocity_z -= g * 0.5;
+       }
+#endif
+}
+
+void PM_air(entity this, float buttons_prev, float maxspd_mod)
+{
+       makevectors(this.v_angle.y * '0 1 0');
+       vector wishvel = v_forward * this.movement.x
+                                       + v_right * this.movement.y;
+       // acceleration
+       vector wishdir = normalize(wishvel);
+       float wishspeed = vlen(wishvel);
+
+#ifdef SVQC
+       if(time >= PHYS_TELEPORT_TIME(this))
+#elif defined(CSQC)
+       if(pmove_waterjumptime <= 0)
+#endif
+       {
+               float maxairspd = PHYS_MAXAIRSPEED(this) * min(maxspd_mod, 1);
+
+               // apply air speed limit
+               float airaccelqw = PHYS_AIRACCEL_QW(this);
+               float wishspeed0 = wishspeed;
+               wishspeed = min(wishspeed, maxairspd);
+               if (IS_DUCKED(this))
+                       wishspeed *= 0.5;
+               float airaccel = PHYS_AIRACCELERATE(this) * min(maxspd_mod, 1);
+
+               float accelerating = (this.velocity * wishdir > 0);
+               float wishspeed2 = wishspeed;
+
+               // CPM: air control
+               if (PHYS_AIRSTOPACCELERATE(this))
+               {
+                       vector curdir = normalize(vec2(this.velocity));
+                       airaccel += (PHYS_AIRSTOPACCELERATE(this)*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(this.movement, -90) + IsMoveInDirection(this.movement, +90); // if one is nonzero, other is always zero
+               if (PHYS_MAXAIRSTRAFESPEED(this))
+                       wishspeed = min(wishspeed, GeomLerp(PHYS_MAXAIRSPEED(this)*maxspd_mod, strafity, PHYS_MAXAIRSTRAFESPEED(this)*maxspd_mod));
+               if (PHYS_AIRSTRAFEACCELERATE(this))
+                       airaccel = GeomLerp(airaccel, strafity, PHYS_AIRSTRAFEACCELERATE(this)*maxspd_mod);
+               if (PHYS_AIRSTRAFEACCEL_QW(this))
+                       airaccelqw =
+               (((strafity > 0.5 ? PHYS_AIRSTRAFEACCEL_QW(this) : PHYS_AIRACCEL_QW(this)) >= 0) ? +1 : -1)
+               *
+               (1 - GeomLerp(1 - fabs(PHYS_AIRACCEL_QW(this)), strafity, 1 - fabs(PHYS_AIRSTRAFEACCEL_QW(this))));
+               // !CPM
+
+               if (PHYS_WARSOWBUNNY_TURNACCEL(this) && accelerating && this.movement.y == 0 && this.movement.x != 0)
+                       PM_AirAccelerate(this, wishdir, wishspeed2);
+               else
+                       PM_Accelerate(this, wishdir, wishspeed, wishspeed0, airaccel, airaccelqw, PHYS_AIRACCEL_QW_STRETCHFACTOR(this), PHYS_AIRACCEL_SIDEWAYS_FRICTION(this) / maxairspd, PHYS_AIRSPEEDLIMIT_NONQW(this));
+
+               if (PHYS_AIRCONTROL(this))
+                       CPM_PM_Aircontrol(this, wishdir, wishspeed2);
+       }
+#ifdef CSQC
+       float g = PHYS_GRAVITY(this) * PHYS_ENTGRAVITY(this) * PHYS_INPUT_TIMELENGTH;
+       if(autocvar_cl_movement == 3)
+       if (GAMEPLAYFIX_GRAVITYUNAFFECTEDBYTICRATE)
+               this.velocity_z -= g * 0.5;
+       else
+               this.velocity_z -= g;
+#endif
+       PM_ClientMovement_Move(this);
+#ifdef CSQC
+       if(autocvar_cl_movement == 3)
+       if (!IS_ONGROUND(this) || !(GAMEPLAYFIX_NOGRAVITYONGROUND))
+               if (GAMEPLAYFIX_GRAVITYUNAFFECTEDBYTICRATE)
+                       this.velocity_z -= g * 0.5;
+#endif
+}
+
+// used for calculating airshots
+bool IsFlying(entity this)
+{
+       if(IS_ONGROUND(this))
+               return false;
+       if(this.waterlevel >= WATERLEVEL_SWIMMING)
+               return false;
+       //traceline(this.origin, this.origin - '0 0 48', MOVE_NORMAL, this);
+       //if(trace_fraction < 1)
+               //return false;
+       return true;
+}
+
+void PM_Main(entity this)
+{
+       int buttons = PHYS_INPUT_BUTTON_MASK(this);
+#ifdef CSQC
+       this.items = STAT(ITEMS);
+
+       this.movement = PHYS_INPUT_MOVEVALUES(this);
+
+       this.spectatorspeed = STAT(SPECTATORSPEED);
+
+       vector oldv_angle = this.v_angle;
+       vector oldangles = this.angles; // we need to save these, as they're abused by other code
+       this.v_angle = PHYS_INPUT_ANGLES(this);
+       this.angles = PHYS_WORLD_ANGLES(this);
+
+       this.team = myteam + 1; // is this correct?
+       if (!(PHYS_INPUT_BUTTON_JUMP(this))) // !jump
+               UNSET_JUMP_HELD(this); // canjump = true
+       pmove_waterjumptime -= PHYS_INPUT_TIMELENGTH;
+
+       PM_ClientMovement_UpdateStatus(this, true);
+#endif
+
+       this.oldmovement = this.movement;
+
+
+#ifdef SVQC
+       WarpZone_PlayerPhysics_FixVAngle();
+#endif
+       float maxspeed_mod = 1;
+       maxspeed_mod *= PHYS_HIGHSPEED(this);
+
+#ifdef SVQC
+       Physics_UpdateStats(this, maxspeed_mod);
+
+       if (this.PlayerPhysplug)
+               if (this.PlayerPhysplug())
+                       return;
+#endif
+
+#ifdef SVQC
+       anticheat_physics(this);
+#endif
+
+       if (PM_check_specialcommand(this, buttons))
+               return;
+#ifdef SVQC
+       if (sv_maxidle > 0)
+       {
+               if (buttons != this.buttons_old || this.movement != this.movement_old || this.v_angle != this.v_angle_old)
+                       this.parm_idlesince = time;
+       }
+#endif
+       int buttons_prev = this.buttons_old;
+       this.buttons_old = buttons;
+       this.movement_old = this.movement;
+       this.v_angle_old = this.v_angle;
+
+       PM_check_nickspam(this);
+
+       PM_check_punch(this);
+#ifdef SVQC
+       if (IS_BOT_CLIENT(this))
+       {
+               if (playerdemo_read(this))
+                       return;
+               WITH(entity, self, this, bot_think());
+       }
+#endif
+
+#ifdef SVQC
+       if (IS_PLAYER(this))
+       {
+               const bool allowed_to_move = (time >= game_starttime);
+               if (!allowed_to_move)
+               {
+                       this.velocity = '0 0 0';
+                       this.movetype = MOVETYPE_NONE;
+                       this.disableclientprediction = 2;
+               }
+               else if (this.disableclientprediction == 2)
+               {
+                       if (this.movetype == MOVETYPE_NONE)
+                               this.movetype = MOVETYPE_WALK;
+                       this.disableclientprediction = 0;
+               }
+       }
+#endif
+
+#ifdef SVQC
+       if (this.movetype == MOVETYPE_NONE)
+               return;
+
+       // when we get here, disableclientprediction cannot be 2
+       this.disableclientprediction = 0;
+#endif
+
+       viewloc_PlayerPhysics(this);
+
+       PM_check_frozen(this);
+
+       PM_check_blocked(this);
+
+       maxspeed_mod = 1;
+
+       if (this.in_swamp)
+               maxspeed_mod *= this.swamp_slowdown; //cvar("g_balance_swamp_moverate");
+
+       // conveyors: first fix velocity
+       if (this.conveyor.state)
+               this.velocity -= this.conveyor.movedir;
+
+       MUTATOR_CALLHOOK(PlayerPhysics, this);
+
+       if (!IS_PLAYER(this))
+       {
+#ifdef SVQC
+               maxspeed_mod = autocvar_sv_spectator_speed_multiplier;
+               if (!this.spectatorspeed)
+                       this.spectatorspeed = maxspeed_mod;
+               if (this.impulse && this.impulse <= 19 || (this.impulse >= 200 && this.impulse <= 209) || (this.impulse >= 220 && this.impulse <= 229))
+               {
+                       if (this.lastclassname != "player")
+                       {
+                               if (this.impulse == 10 || this.impulse == 15 || this.impulse == 18 || (this.impulse >= 200 && this.impulse <= 209))
+                                       this.spectatorspeed = bound(1, this.spectatorspeed + 0.5, 5);
+                               else if (this.impulse == 11)
+                                       this.spectatorspeed = maxspeed_mod;
+                               else if (this.impulse == 12 || this.impulse == 16  || this.impulse == 19 || (this.impulse >= 220 && this.impulse <= 229))
+                                       this.spectatorspeed = bound(1, this.spectatorspeed - 0.5, 5);
+                               else if (this.impulse >= 1 && this.impulse <= 9)
+                                       this.spectatorspeed = 1 + 0.5 * (this.impulse - 1);
+                       } // otherwise just clear
+            &nbs