djcev.com

//

Git Repos / fte_dogmode / commit d597d86

Commit: d597d866862bfe0658f62366e11f6a8a6462c7f4
Parent: b57cc767e3517f4a408ec752ce850e563c66d7ca
Author: Cameron Vanderzanden, 2023-11-11 18:40
Committer: Cameron Vanderzanden, 2023-11-11 18:40

Commit Message

pmove ladders & water move, fix a prediction error

A cvar (pm_standardphysics) was implemented to switch between
custom physics and the engine's standard physics.

Fixed a prediction error in csqc_player.qc (though flag tracking
errors still remain). The prediction appears more accurate at
sv_minping 100 (and works correctly when running standard physics).

More large changes to pmove: PM_NuclideMove renamed to PM_DanceMove
(I probably shouldn't be using the name of somebody else's project),
ladder code was moved from client.qc into pmove, and water jump was
added into the water movement. Also reworked timers into countdowns
that decrement based on input_timelength.

Removed defs_constants.qc and moved those definitions back into
pmove.qc.

trigger_ladder code was moved from rubicon2.qc into its own (short)
file.

There's probably other changes I'm forgetting.

Change List

Diff qc/client.qc

diff --git a/qc/client.qc b/qc/client.qc
index 7ebb852..89d90db 100644
--- a/qc/client.qc
+++ b/qc/client.qc
@@ -10,7 +10,7 @@ void() player_stand1;
//void (vector org) spawn_tfog; //moved to items.qc -- dumptruck_ds
void (vector org, entity death_owner) spawn_tdeath;

-float modelindex_eyes, modelindex_player;
+float modelindex_eyes, modelindex_player;

void() SetChangeParms =
{
@@ -227,18 +227,18 @@ float(entity to, float fl) SendPlayer =
{
WriteByte (MSG_ENTITY, CLASS_PLAYER);
WriteByte (MSG_ENTITY, self.frame);
- WriteShort (MSG_ENTITY, self.angles_x * 32767 / 360);
- WriteShort (MSG_ENTITY, self.angles_y * 32767 / 360);
- WriteShort (MSG_ENTITY, self.angles_z * 32767 / 360);
WriteCoord (MSG_ENTITY, self.origin_x);
WriteCoord (MSG_ENTITY, self.origin_y);
WriteCoord (MSG_ENTITY, self.origin_z);
+ WriteShort (MSG_ENTITY, self.angles_x * 32767 / 360);
+ WriteShort (MSG_ENTITY, self.angles_y * 32767 / 360);
+ WriteShort (MSG_ENTITY, self.angles_z * 32767 / 360);
WriteShort (MSG_ENTITY, self.velocity_x);
WriteShort (MSG_ENTITY, self.velocity_y);
WriteShort (MSG_ENTITY, self.velocity_z);
WriteFloat (MSG_ENTITY, self.flags);
WriteFloat (MSG_ENTITY, self.pmove_flags);
- WriteFloat (MSG_ENTITY, self.movetype);
+ // WriteFloat (MSG_ENTITY, self.movetype);
return TRUE;
};

@@ -276,7 +276,6 @@ void() PutClientInServer =
self.deathtype = "";
self.gravity = 0;
self.wantedgravity = 0;
- self.onladder = 0;
self.customkeys = 0; // support for item_key_custom -- iw

// Setup cutscene stuff. Legacy code from Zerstorer. -- dumptruck_ds
@@ -344,7 +343,7 @@ void() PutClientInServer =

// TODO CEV
self.SendEntity = SendPlayer;
- self.SendFlags = FULLSEND;
+ self.SendFlags = 0xffffff;

player_stand1 ();

@@ -615,18 +614,6 @@ void() PlayerJump =
self.button2 = 0;
// player jumping sound
sound (self, CHAN_BODY, "player/plyrjmp8.wav", 1, ATTN_NORM);
-
- // pmove code predicts this
- // self.velocity_z = self.velocity_z + 270;
-};
-
-//======================================================================
-// PlayerClimb
-// johnfitz
-//======================================================================
-void() PlayerClimb =
-{
- self.velocity = '0 0 160';
};

//======================================================================
@@ -730,47 +717,6 @@ void() WaterMove =
};

//======================================================================
-// CheckWaterJump
-//======================================================================
-void() CheckWaterJump =
-{
- // from Copper -- dumptruck_ds
- if (self.movetype == MOVETYPE_NOCLIP)
- return;
-
- local vector start, end;
-
- // check for a jump-out-of-water
- makevectors (self.angles);
- start = self.origin;
- start_z = start_z + 8;
- v_forward_z = 0;
- normalize (v_forward);
- end = start + v_forward * 24;
- traceline (start, end, TRUE, self);
- if (trace_fraction < 1)
- {
- // solid at waist
- start_z = start_z + self.maxs_z - 8;
- end = start + v_forward*24;
- self.movedir = trace_plane_normal * -50;
- traceline (start, end, TRUE, self);
- if (trace_fraction == 1)
- {
- // open at eye level
- self.flags |= FL_WATERJUMP;
- // TODO CEV
- self.pmove_flags |= PMF_WATERJUMP;
- self.velocity_z = 225;
- // self.flags -= self.flags & FL_JUMPRELEASED;
- self.flags &~= FL_JUMPRELEASED;
- self.teleport_time = time + 2; // safety net
- return;
- }
- }
-};
-
-//======================================================================
// PlayerPreThink
// Called every frame before physics are run
//======================================================================
@@ -797,6 +743,8 @@ void() PlayerPreThink =
// whether the player is dead, so that the player's gravity will be
// correctly updated even if they e.g. fell off a ladder because
// they died -- iw
+ // TODO CEV hacking on ladders
+ /*
if (self.onladder)
{
do_ladder_physics = TRUE;
@@ -807,12 +755,16 @@ void() PlayerPreThink =
else
{
do_ladder_physics = FALSE;
+ */
self.gravity = self.wantedgravity;
+ /*
}
+ */

// If just spawned in, try to recover previous fog values from
// own client entity, if any
- if (cleanUpClientStuff) fog_setFromEnt(self, self);
+ if (cleanUpClientStuff)
+ fog_setFromEnt(self, self);

// is this still used?
makevectors (self.v_angle);
@@ -820,9 +772,6 @@ void() PlayerPreThink =
CheckRules ();
WaterMove ();

- if (self.waterlevel == 2)
- CheckWaterJump ();
-
if (self.deadflag >= DEAD_DEAD)
{
PlayerDeathThink ();
@@ -838,8 +787,6 @@ void() PlayerPreThink =
{
if (self.button2)
{
- PlayerClimb ();
-
/* no ladder footsteps for now
if (time > self.ladder_step_finished)
{
@@ -857,8 +804,6 @@ void() PlayerPreThink =
else
{
self.flags = self.flags | FL_JUMPRELEASED;
- self.velocity = 0.9 * self.velocity;
- self.velocity_z = 0;
}
}
else
@@ -873,8 +818,7 @@ void() PlayerPreThink =
if (time < self.pausetime)
self.velocity = '0 0 0';

- if (time > self.attack_finished
- && self.currentammo == 0
+ if (time > self.attack_finished && self.currentammo == 0
&& self.weapon != IT_AXE)
{
self.weapon = W_BestWeapon ();
@@ -888,8 +832,8 @@ void() PlayerPreThink =
}

// TODO CEV
- if (!self.groundboost_time && (self.teleport_time > time - 0.1))
- self.groundboost_time = PM_GROUNDBOOST_WINDOW;
+ if (!self.groundboost_timer && (self.teleport_time > time - 0.1))
+ self.groundboost_timer = PM_GROUNDBOOST_WINDOW;
};

//======================================================================
@@ -1053,15 +997,15 @@ void() CheckPowerups =
//======================================================================
void() PlayerPostThink =
{
- // TODO CEV
- self.SendFlags = FULLSEND;
-
// from Copper -- dumptruck_ds
if (self.movetype != MOVETYPE_NOCLIP)
{
self.flags = not(self.flags, FL_FLY);
}

+ // TODO CEV
+ self.SendFlags = 0xffffff;
+
if (self.view_ofs == '0 0 0')
// intermission or finale
return;

Return to the top of this page or return to the overview of this repo.

Diff qc/csqc/csqc_defsbuiltins.qc

diff --git a/qc/csqc/csqc_defsbuiltins.qc b/qc/csqc/csqc_defsbuiltins.qc
index c1c7d0d..9997a29 100644
--- a/qc/csqc/csqc_defsbuiltins.qc
+++ b/qc/csqc/csqc_defsbuiltins.qc
@@ -38,6 +38,25 @@ void(string n) objerror = #11;
float(vector) vlen = #12;
entity() spawn = #14;
void(entity e) remove = #15;
+
+// Traces a thin line through the world from v1 towards v2. Will not collide
+// with ent, ent.owner, or any entity who's owner field refers to ent. The
+// passed entity will also be used to determine whether to use a capsule
+// trace, the contents that the trace should impact, and a couple of other
+// extra fields that define the trace.
+//
+// There are no side effects beyond the trace_* globals being written.
+// flags&MOVE_NOMONSTERS will not impact on non-bsp entities.
+// flags&MOVE_MISSILE will impact with increased size.
+// flags&MOVE_HITMODEL will impact upon model meshes, instead of their
+// bounding boxes.
+// flags&MOVE_TRIGGERS will also stop on triggers
+// flags&MOVE_EVERYTHING will stop if it hits anything, even non-solid entities.
+// flags&MOVE_LAGGED will backdate entity positions for the purposes of
+// this builtin according to the indicated player ent's latency, to provide
+// lag compensation.
+void(vector v1, vector v2, float flags, entity ent) traceline = #16;
+
// Precaches a model, making it known to clients and loading it from disk
// if it has a .bsp extension. This builtin (strongly) should be called
// during spawn functions. This must be called for each model name before

Return to the top of this page or return to the overview of this repo.

Diff qc/csqc/csqc_defsclient.qc

diff --git a/qc/csqc/csqc_defsclient.qc b/qc/csqc/csqc_defsclient.qc
index f75a756..c889ddf 100644
--- a/qc/csqc/csqc_defsclient.qc
+++ b/qc/csqc/csqc_defsclient.qc
@@ -53,12 +53,6 @@ float servercommandframe;

string CSQC_PING = "csqcping"; // Test command to check if CSCQ alive

-float input_timelength; // from fteextensions.qc -- CEV
-vector input_angles; /* +x=DOWN */
-vector input_movevalues;
-float input_buttons;
-float input_impulse;
-
vector VEC_ORIGIN = '0 0 0';
vector VEC_HULL_MIN = '-16 -16 -24';
vector VEC_HULL_MAX = '16 16 32';
@@ -75,6 +69,8 @@ float player_localentnum;

vector player_org; // prediction globals; from CSQCTest -- CEV
vector player_vel;
+float player_flags;
+// float player_gravity;
float player_pmflags;
float player_sequence;
float player_step;
@@ -116,12 +112,6 @@ float pmove_frame;
// the original model's)
.float modelflags;

-.float pmove_flags; // custom movement flags -- CEV
-.float nostick_time; // last time player stuck to the floor -- CEV
-.float jump_time; // last time the player jumped -- CEV
-.float groundboost_time; // time to simulate low friction -- CEV
-.void() customphysics;
-
.float ext_csqc; // Client Server Quake C HUD alive!

.float entnum; // The entity number as its known on the server

Return to the top of this page or return to the overview of this repo.

Diff qc/csqc/csqc_entrypoints.qc

diff --git a/qc/csqc/csqc_entrypoints.qc b/qc/csqc/csqc_entrypoints.qc
index eac9683..2d6f838 100644
--- a/qc/csqc/csqc_entrypoints.qc
+++ b/qc/csqc/csqc_entrypoints.qc
@@ -103,7 +103,10 @@ void(entity ent) CSQC_DrawViewModel =
void(float vwidth, float vheight, float notmenu) CSQC_UpdateView =
{
local vector ssize;
- ssize_x = vwidth; ssize_y = vheight; ssize_z = 0;
+
+ ssize_x = vwidth;
+ ssize_y = vheight;
+ ssize_z = 0;

// Is the CSQC functionality enabled/disabled?
nocsqc = cvar ("cl_nocsqc");
@@ -130,7 +133,12 @@ void(float vwidth, float vheight, float notmenu) CSQC_UpdateView =
makevectors (view_angles);
SetListener (vieworg, v_forward, v_right, v_up);

- CSQC_DrawViewModel (viewentity);
+ if (intermission)
+ // don't draw crosshair in intermission
+ setproperty (VF_DRAWCROSSHAIR, FALSE);
+ else
+ // draw view model when not in intermission
+ CSQC_DrawViewModel (viewentity);
}
else
{
@@ -180,7 +188,7 @@ float CSQC_Parse_TempEntity()
float eid;
entity e;

- handled = TRUE;
+ handled = FALSE;
teid = ReadByte ();

switch(teid)
@@ -194,10 +202,12 @@ float CSQC_Parse_TempEntity()
end_y = ReadCoord ();
end_z = ReadCoord ();
// pointparticles (
- // particleeffectnum("TE_LIGHTNING2"),
- // pos, end, 1);
- // te_lightning2 (player_local, pos, end);
- te_lightning2 (viewentity, pos, end);
+ // particleeffectnum("TE_LIGHTNING2"),
+ // pos, end, 1);
+ if (player_local)
+ te_lightning2 (player_local, pos, end);
+ else
+ te_lightning2 (viewentity, pos, end);
handled = TRUE;
break;
default:
@@ -217,10 +227,19 @@ void(float isnew) CSQC_Ent_Update =

classtype = ReadByte ();

+ switch (classtype)
+ {
+ case CLASS_PLAYER:
+ PlayerUpdate (isnew);
+ break;
+ }
+
+ /*
if (classtype == CLASS_PLAYER)
{
PlayerUpdate (isnew);
}
+ */
};

//----------------------------------------------------------------------

Return to the top of this page or return to the overview of this repo.

Diff qc/csqc/csqc_player.qc

diff --git a/qc/csqc/csqc_player.qc b/qc/csqc/csqc_player.qc
index 767e476..b16f9c8 100644
--- a/qc/csqc/csqc_player.qc
+++ b/qc/csqc/csqc_player.qc
@@ -24,6 +24,7 @@ void(entity ent) PlayerResetPrediction =
{
ent.origin = player_org;
ent.velocity = player_vel;
+ ent.flags = player_flags;
ent.pmove_flags = player_pmflags;

// +1 because the received frame has the move already done (server side)
@@ -49,6 +50,10 @@ void(entity ent, float endframe) PlayerRunMovement =
// you can comment out this block and the player will
// continue to be predicted locally. But its best to
// freeze them
+
+ // as a note we also hit this block when the console is
+ // down -- CEV
+
player_sequence = servercommandframe - 63;
return;
}
@@ -80,21 +85,16 @@ void(entity ent, float endframe) PlayerRunMovement =
if (!getinputstate(pmove_frame))
{
// no input from client
- pmove_frame++;
- continue;
- }
-
- if (input_timelength <= 0)
- {
- // partial frame / incomplete input packet
- pmove_frame++;
- continue;
+ // pmove_frame++;
+ break;
}

- // for testing
- // runstandardplayerphysics (ent);
- // for real
- PM_Move (ent);
+ if (autocvar(pm_standardphysics, FALSE))
+ // for testing
+ runstandardplayerphysics (ent);
+ else
+ // for real
+ PM_Move (ent);
pmove_frame++;
}

@@ -151,11 +151,12 @@ float() PlayerPreDraw =

// correct the view position to compensate for any errors,
// slowly over time, 0.1 seconds.
- /*
if (pmove_errortime - time > 0)
+ {
+ dprint ("PlayerPreDraw: pmove error\n");
vieworg += (pmove_errortime - time) *
ERRORTIME * pmove_error;
- */
+ }

if (player_steptime - time > 0)
vieworg_z += (player_steptime - time) *
@@ -210,18 +211,18 @@ void(float isnew) PlayerUpdate =
local float f;

f = ReadByte ();
- self.angles_x = ReadShort () / (32767 / 360);
- self.angles_y = ReadShort () / (32767 / 360);
- self.angles_z = ReadShort () / (32767 / 360);
self.origin_x = ReadCoord ();
self.origin_y = ReadCoord ();
self.origin_z = ReadCoord ();
+ self.angles_x = ReadShort () / (32767 / 360);
+ self.angles_y = ReadShort () / (32767 / 360);
+ self.angles_z = ReadShort () / (32767 / 360);
self.velocity_x = ReadShort ();
self.velocity_y = ReadShort ();
self.velocity_z = ReadShort ();
self.flags = ReadFloat ();
self.pmove_flags = ReadFloat ();
- self.movetype = ReadFloat ();
+ // self.movetype = ReadFloat ();

if (isnew && !(self.predraw))
PlayerNew ();
@@ -236,25 +237,56 @@ void(float isnew) PlayerUpdate =
if (self.entnum == player_localentnum)
{
local vector o, v;
- local float pmflags;
+ local float pmflags, tempflags;

// this is us; save for later
player_local = self;
o = self.origin;
v = self.velocity;
+ tempflags = self.flags;
pmflags = self.pmove_flags;

- // reset prediction
- PlayerResetPrediction (self);
+ // PlayerRunMovement calls ResetPrediction -- CEV
+ // PlayerResetPrediction (self);
PlayerRunMovement (self, servercommandframe + 1);

+ // TODO CEV ????
+ player_flags = tempflags;
+ player_pmflags = pmflags;
player_org = o;
player_vel = v;
- // TODO CEV ????
- player_pmflags = self.pmove_flags;
- // player_pmflags = pmflags;
player_sequence = servercommandframe;
- PlayerResetPrediction (self);
+
+ if (autocvar(cg_noerror, TRUE))
+ {
+ pmove_error = '0 0 0';
+ pmove_errortime = time;
+ PlayerResetPrediction (self);
+ }
+ else
+ {
+ PlayerRunMovement (self, clientcommandframe);
+ o = self.origin;
+ // again: PlayerRunMovement calls ResetPrediction
+ // PlayerResetPrediction (self);
+ PlayerRunMovement (self, clientcommandframe);
+
+ if (vlen(o - self.origin) > 64)
+ {
+ // teleport
+ pmove_error = '0 0 0';
+ pmove_errortime = time;
+ }
+ else
+ {
+ pmove_error = (pmove_errortime - time) *
+ ERRORTIME * pmove_error +
+ (o - self.origin);
+ if (vlen(pmove_error) < 1)
+ pmove_error = '0 0 0';
+ pmove_errortime = time + 1 / ERRORTIME;
+ }
+ }
}

setsize (self, VEC_HULL_MIN, VEC_HULL_MAX);

Return to the top of this page or return to the overview of this repo.

Diff qc/csqc/csqc_progs.src

diff --git a/qc/csqc/csqc_progs.src b/qc/csqc/csqc_progs.src
index 87d8245..fd7728b 100644
--- a/qc/csqc/csqc_progs.src
+++ b/qc/csqc/csqc_progs.src
@@ -1,15 +1,13 @@
#pragma progs_dat "../../csprogs.dat"

-#pragma noref 1
-#pragma target fte
-
#define FTE
+#pragma target fte
+#pragma noref 1
#define CSQC

#includelist
../defs_globalvars.qc
../defs_entvars.qc
-../defs_constants.qc
csqc_defsbuiltins.qc
csqc_defsclient.qc
csqc_defs.qc

Return to the top of this page or return to the overview of this repo.

Diff qc/defs_constants.qc

diff --git a/qc/defs_constants.qc b/qc/defs_constants.qc
deleted file mode 100644
index 491a3d2..0000000
--- a/qc/defs_constants.qc
+++ /dev/null
@@ -1,55 +0,0 @@
-/*==============================================================================
- CONSTANT VALS COLLECTED HERE
-==============================================================================*/
-
-//
-#define FULLSEND 0xffffff
-
-// PMOVE constants
-#define PM_AIRACCEL 7.5 // 10 in Q1; now 8
-#define PM_AIRACCELQ3 0.75 // 1.0 in Q3 ?; now 0.8
-#define PM_AIRACCELFWD 1.5 // 1 feels close to Q3 / CPM
-#define PM_AIRACCELBACK 5 // Air stop speed in Q3?
-#define PM_AIRACCELTURN 100 // affects +fwd style turning radius
-#define PM_BOOSTACCEL 12 // 12 is fast, 10 is enough
-#define PM_BOOSTFRICTION 4 // Q1 friction (4) is plenty low
-#define PM_FRICTION 8 // 4 for Q1, 6 for Q3, 8 for CPM
-#define PM_GROUNDACCEL 15 // 10 is standard ground accel
-#define PM_GROUNDDIST_V '0 0 1' // distance for ground check
-#define PM_GRAVITY 800 // 800 always
-#define PM_OVERCLIP 1.0f // Quake3's OVERCLIP is 1.001f
-#define PM_STEPHEIGHT 18 // 18 for Q1, 22 for later games?
-#define PM_WALLDIST_V '0 0 40' // min. distance from ground for wj
-#define PM_WATERACCEL 10 // water acceleration
-#define PM_WATERFRICTION 4 // friction in water
-
-#define PM_JUMPSPEED 270 // standard jump Z velocity; 90 * 3
-#define PM_DOUBLEJUMPSPEED 360 // 270 * 1.5 in CPM?; 360 = 90 * 4
-#define PM_TELEJUMPSPEED 360 // same as DOUBLEJUMPSPEED
-#define PM_WALLJUMPFORCE 90 // amount of push away from wall
-#define PM_WALLJUMPSPEED PM_JUMPSPEED // upward vel for walljumps
-#define PM_MAXSPEED 320 // 320 always
-#define PM_MAXAIRSPEED 30 // 30 for Q1 air control
-#define PM_STOPSPEED 100
-#define PM_WATERMAXSPEED 224 // 320 * 0.7
-#define PM_WATERSINKSPEED 60
-
-#define PM_DOUBLEJUMP_WINDOW 0.4 // 2 jumps in this time is a double
-#define PM_DOUBLEJUMP_COOLDOWN 0.4 //
-#define PM_NOSTICK_WINDOW 0.3 // duration to perform onesided clips
-#define PM_TELEJUMP_WINDOW 0.4 // duration to perform a telejump
-#define PM_WALLJUMP_WINDOW 0.2 // duration between walljumps
-#define PM_WALLCLIP_WINDOW 0.25 //
-#define PM_GROUNDBOOST_WINDOW 0.4 // groundboost duration
-
-#define PM_JUMPPADADDHEIGHT 64 // not currently in use
-#define PM_TELEDROP '0 0 -64' // drop teleporter exit to floor if
- // floor is within this distance
-#define PM_TELEEXITSPEED 400 // exit teleporters at this speed
-
-// water level
-#define WATERLEVEL_NONE 0
-#define WATERLEVEL_FEET 1
-#define WATERLEVEL_WAIST 2
-#define WATERLEVEL_EYES 3
-

Return to the top of this page or return to the overview of this repo.

Diff qc/defs_misc.qc

diff --git a/qc/defs_misc.qc b/qc/defs_misc.qc
index 80a9bf1..faa3059 100644
--- a/qc/defs_misc.qc
+++ b/qc/defs_misc.qc
@@ -300,17 +300,6 @@ float AS_TURRET = 5;
.float air_finished; // when time > air_finished, drown
.float bubble_count; // keeps track of the number of bubbles
.string deathtype; // keeps track of how the player died
-.float pmove_flags; // custom movement flags -- CEV
-.float groundboost_time; // time to simulate low friction -- CEV
-.float jump_time; // last time the player jumped -- CEV
-.float nostick_time; // time to not stick to the floor -- CEV
-.void() customphysics;
-
-float input_timelength; // from fteextensions.qc -- CEV
-vector input_angles; // +x = DOWN
-vector input_movevalues;
-float input_buttons;
-float input_impulse;

//======================================================================
// object stuff
@@ -482,7 +471,6 @@ float STAT_TOTALMONSTERS = 12; // required by Hipnotic code
float BREAKABLE_NO_MONSTERS = 1;

.float wantedgravity; // thanks Spike!
-.float onladder;
// .float ladder_step_finished; // footsteps on ladder -- dumptruck_ds

// dumptruck_ds defs for breakables in rubcon2.qc

Return to the top of this page or return to the overview of this repo.

Diff qc/pmove.qc

diff --git a/qc/pmove.qc b/qc/pmove.qc
index 5fb8be3..1039a5c 100644
--- a/qc/pmove.qc
+++ b/qc/pmove.qc
@@ -2,17 +2,80 @@
// PMOVE
//==============================================================================

+// TODO: re-implement PM_WallJump
// TODO: implement PMF_WINDMOVE / wind brush nostick timer
-// TODO: look into reworking the jump_time timer

+// globals managed by FTEQW
+float input_timelength; // from fteextensions.qc -- CEV
+vector input_angles; // +x = DOWN
+vector input_movevalues;
+float input_buttons;
+float input_impulse;
+
+// fields
.entity groundentity;
.vector groundnormal;

+.float pmove_flags; // custom movement flags -- CEV
+.float doublejump_timer; // time in which a player can doublejump
+.float groundboost_timer; // time to simulate low friction -- CEV
+.float nostick_timer; // time to not stick to the floor -- CEV
+.float skim_timer; // time to skim/clip past walls -- CEV
+.void() customphysics;
+
+// pmove constants
+// It's definitely possible to rework these as cvars -- CEV
+const float PM_AIRACCEL = 7.5f; // 10 in Q1; now 8
+const float PM_AIRACCELQ3 = 0.75f; // 1.0 in Q3 ?; now 0.8
+const float PM_AIRACCELFWD = 1.5f; // 1 feels close to Q3 / CPM
+const float PM_AIRACCELBACK = 5.0f; // Air stop speed in Q3?
+const float PM_AIRACCELTURN = 100.0f; // affects +fwd style turning radius
+const float PM_BOOSTACCEL = 12.0f; // 12 is fast, 10 is enough
+const float PM_BOOSTFRICTION = 4.0f; // Q1 friction (4) is plenty low
+const float PM_GROUNDACCEL = 15.0f; // 10 is normal, 15 is CPM
+const float PM_GROUNDFRICTION = 8.0f; // 4 for Q1, 6 for Q3, 8 for CPM
+const vector PM_GROUNDDIST_V = '0 0 1'; // distance for ground check
+const float PM_GRAVITY = 800.0f; // unless sv_gravity, scaled by .gravity
+const float PM_OVERCLIP = 1.0f; // Quake3's OVERCLIP is 1.001f
+const float PM_STEPHEIGHT = 18.0f; // 18 for Q1, 22 for later games?
+const vector PM_WALLDIST_V = '0 0 40'; // min. distance from ground for wj
+const float PM_WATERACCEL = 10.0f; // water acceleration
+const float PM_WATERFRICTION = 4.0f; // friction in water
+
+const float PM_JUMPSPEED = 270.0f; // standard jump Z velocity; 90 * 3
+const float PM_DOUBLEJUMPSPEED = 360.0f;// 270 * 1.5 in CPM?; 360 = 90 * 4
+const float PM_TELEJUMPSPEED = 360.0f; // same as DOUBLEJUMPSPEED
+const float PM_WALLJUMPFORCE = 90.0f; // amount of push away from wall
+const float PM_WALLJUMPSPEED = 270.0f; // upward vel for walljumps
+const float PM_MAXSPEED = 320.0f; // 320 always
+const float PM_MAXAIRSPEED = 30.0f; // 30 for Q1 air control
+const float PM_STOPSPEED = 100.0f;
+const float PM_WATERMAXSPEED = 224.0f; // 320 * 0.7
+const float PM_WATERSINKSPEED = 60.0f;
+
+const float PM_DOUBLEJUMP_WINDOW = 0.4f;// 2 jumps in this time is a double
+const float PM_GROUNDBOOST_WINDOW = 0.4f;// groundboost duration
+const float PM_NOSTICK_WINDOW = 0.35f; // duration to perform onesided clips
+const float PM_TELEJUMP_WINDOW = 0.4f; // duration to perform a telejump
+const float PM_WALLJUMP_WINDOW = 0.2f; // duration between walljumps
+const float PM_WALLCLIP_WINDOW = 0.25f; //
+
+const vector PM_TELEDROP = '0 0 -64'; // drop teleporter exit to floor if
+ // floor is within this distance
+const float PM_TELEEXITSPEED = 400.0f; // exit teleporters at this speed
+
+// water level
+const int WATERLEVEL_NONE = 0;
+const int WATERLEVEL_FEET = 1;
+const int WATERLEVEL_WAIST = 2;
+const int WATERLEVEL_EYES = 3;
+
// pmove_flags
enumflags
{
PMF_ONGROUND, // entity is on ground
PMF_STARTGROUND, // entity started the move on ground
+ PMF_ONLADDER, // entity is on a ladder
PMF_JUMP_HELD, // player is holding the jump key
PMF_DOUBLEJUMPED, // entity has doublejumped
PMF_WALLJUMP, // entity has walljumped
@@ -21,6 +84,26 @@ enumflags
PMF_NOSTICK // entity won't stick to the floor
};

+// prototypes
+static void(entity target) PM_DoTouch;
+float() PM_Nudge;
+void(float dogravity, float sticky, float dostep, float doskim) PM_DanceMove;
+void(entity ground_e, vector ground_v, int docheck) PM_SetOnground;
+void() PM_CategorizePosition;
+void(float friction, float move_time) PM_Friction;
+void(vector wishdir, float wishspeed, float accel, float move_time)
+ PM_Accelerate;
+void(vector wishvel, float wishspeed, float accel, float move_time)
+ PM_AirAccelerate;
+void(vector wishdir, float wishspeed, float move_time) PM_AirControl;
+void() PM_Jump;
+// TODO CEV void(vector checkdir) PM_WallJump;
+void(vector wishvel, float premove, float mt) PM_WalkAccelerate;
+void(vector wishvel, float move_time) PM_SwimAccelerate;
+void(vector wishvel, float scale, float move_time) PM_NoClipAccelerate;
+void(float move_time) PM_ManageTimers;
+void(entity target) PM_Move;
+
//======================================================================
// PM_DoTouch
// call when self has touched target (to run target's touch function)
@@ -43,7 +126,7 @@ static void(entity target) PM_DoTouch =
// PM_Nudge
//
// from the GPL2 CSQCTest code that comes with FTEQW
-// This isn't working correctly from what I can tell. Not sure why.
+// This isn't working correctly from what I can tell. Not sure why. -- TODO CEV
//----------------------------------------------------------------------
float() PM_Nudge =
{
@@ -87,7 +170,7 @@ float() PM_Nudge =
};

//======================================================================
-// PM_NuclideMove
+// PM_DanceMove
//
// Updates origin, moves the player through the world. Similar to Q3
// SlideMove and Q1 FlyMove. This version handles steps, applies gravity,
@@ -95,13 +178,13 @@ float() PM_Nudge =
// features of Q3's SlideMove).
//
// Based on code from the Nuclide SDK (presumably by Eukara), specifically
-// the function PMoveCustom_Move found in the file pmove_custom.qc.
-// This in turn appears to be based on a similar function in CSQCTest.
-// -- CEV
+// the function PMoveCustom_Move found in the file pmove_custom.qc. This
+// in turn appears to be based on a similar function in the CSQCTest code
+// that comes with FTEQW. -- CEV
//----------------------------------------------------------------------
-void(float dogravity, float sticky, float dostep, float doskim) PM_NuclideMove =
+void(float dogravity, float sticky, float dostep, float doskim) PM_DanceMove =
{
- vector end, prev_plane, plane, start_v;
+ vector end, prev_plane, prev_plane2, plane, start_v;
float backoff, grav, stepsize, time_left;
int i;
entity touched_ent;
@@ -130,11 +213,12 @@ void(float dogravity, float sticky, float dostep, float doskim) PM_NuclideMove =
dostep = FALSE;

// we need to bounce off surfaces (in order to slide along them),
- // so we need at 2 attempts -- comment from Nuclide SDK
+ // so we need at 2 attempts -- comment from the Nuclide SDK
// I've changed this to a larger number of attempts (from 3 to 5)
// to accommodate more complex plane interactions -- CEV
for (i = 5, time_left = input_timelength; time_left > 0 && i; i--)
{
+ // set our destination & test the move -- CEV
end = self.origin + (self.velocity * time_left);
tracebox (self.origin, self.mins, self.maxs, end, FALSE, self);

@@ -147,24 +231,27 @@ void(float dogravity, float sticky, float dostep, float doskim) PM_NuclideMove =
// nah, we're stuck. don't build up falling damage
// but allow sideways acceleration -- CEV
#ifdef SSQC
- dprint ("PM_NuclideMove: entity trapped in a solid\n");
+ dprint ("PM_DanceMove: entity trapped in a solid\n");
#endif
self.velocity_z = 0;
break;
}

+ // accept the move -- CEV
self.origin = trace_endpos;

if (trace_fraction >= 1.0f)
// no obstructions, made the whole move -- CEV
break;

+ // zero out stepsize, then store the plane normal and touched
+ // ent for later, then reduce time_left -- CEV
stepsize = 0;
plane = trace_plane_normal;
touched_ent = trace_ent;
time_left -= time_left * trace_fraction;

- // integrated StepSlideMove -- CEV
+ // integrated StepSlideMove from Nuclide / CSQCTest -- CEV
// only attempt to step if there's time left, stepping was
// requested, and we hit something like a vertical plane
if (dostep && time_left && plane_z >= 0 && plane_z <= 0.7f)
@@ -253,6 +340,29 @@ void(float dogravity, float sticky, float dostep, float doskim) PM_NuclideMove =

self.velocity -= plane * backoff;

+ // need to do the ground check & touch the saved ent before
+ // we might stop the loop early -- CEV
+ if (plane_z > 0.7)
+ {
+ if (touched_ent.solid == SOLID_BSP)
+ {
+ self.groundentity = touched_ent;
+ self.groundnormal = plane;
+ self.flags |= FL_ONGROUND;
+ self.pmove_flags |= PMF_ONGROUND;
+ }
+ }
+ else
+ {
+ self.groundentity = __NULL__;
+ self.groundnormal = __NULL__;
+ self.flags &= ~FL_ONGROUND;
+ self.pmove_flags &= ~PMF_ONGROUND;
+ }
+
+ // touch the saved entity -- CEV
+ PM_DoTouch (touched_ent);
+
// if velocity interacts with prev_plane then clip to it.
// I'm duplicating a little code here (ClipVelocity),
// hopefully the audience will forgive me. -- CEV
@@ -279,49 +389,47 @@ void(float dogravity, float sticky, float dostep, float doskim) PM_NuclideMove =
end = normalize (end);
self.velocity = end * (end * self.velocity);
}
- }

- // integrated ground check -- CEV
- if (plane_z > 0.7)
- {
- if (touched_ent.solid == SOLID_BSP)
+ // An optimization from Quake 3 -- CEV
+ if (prev_plane2 && prev_plane2 != plane &&
+ prev_plane2 != prev_plane &&
+ self.velocity * prev_plane2 < 0)
{
- self.groundentity = touched_ent;
- self.groundnormal = plane;
- self.pmove_flags |= PMF_ONGROUND;
+ // Stop if we interact with three planes -- CEV
+ #ifdef SSQC
+ dprint ("PM_DanceMove: triple plane stop\n");
+ #endif
+ self.velocity = '0 0 0';
+ break;
}
}
- else
- {
- self.groundentity = __NULL__;
- self.groundnormal = __NULL__;
- self.pmove_flags &= ~PMF_ONGROUND;
- }

- // touch the saved entity -- CEV
- PM_DoTouch (touched_ent);
-
- // copied from Q1FlyMove, not sure this is necessary -- CEV
+ // an optimization from Quake 1; is this necessary? -- CEV
if ((self.velocity * start_v) <= 0)
{
+ // we've turned against original velocity so
+ // clear vel and stop the loop
self.velocity = '0 0 0';
break;
}

- // store current plane for later -- CEV
+ // store current plane and prev_plane for later -- CEV
+ if (prev_plane)
+ prev_plane2 = prev_plane;
prev_plane = plane;
}

// wallclip / wall skim timer check
// if doskim is true and velocity is non-zero and we lost speed
- // during the move -- CEV
+ // during the move then restore velocity -- CEV
if (doskim && self.velocity && (vlen(start_v) > vlen(self.velocity)))
{
+ /*
#ifdef SSQC
- dprint ("PM_NuclideMove: wall skim, restoring velocity...\n");
+ dprint ("PM_DanceMove: wall skim, restoring velocity...\n");
#endif
- // restore velocity, taking either the start Z or clipped Z
- // depending on which is lower -- CEV
+ */
+ // take the start Z or clipped Z, whichever is lower -- CEV
if (start_v_z > self.velocity_z)
self.velocity = [start_v_x, start_v_y, self.velocity_z];
else
@@ -337,13 +445,9 @@ void(float dogravity, float sticky, float dostep, float doskim) PM_NuclideMove =
if (stepsize && !(self.pmove_flags & PMF_STARTGROUND) &&
self.pmove_flags & PMF_ONGROUND)
{
- // manage timers -- CEV
- if (self.groundboost_time <= 0)
- self.groundboost_time = PM_GROUNDBOOST_WINDOW;
-
// more debugging -- CEV
#ifdef SSQC
- dprint (sprintf("PM_NuclideMove: airstep: %g\n", stepsize));
+ dprint (sprintf("PM_DanceMove: airstep: %g\n", stepsize));
#endif
}

@@ -351,7 +455,7 @@ void(float dogravity, float sticky, float dostep, float doskim) PM_NuclideMove =
#ifdef SSQC
if (cvar("developer"))
// for debugging purposes -- CEV
- dprint (sprintf("PM_NuclideMove: %g bumps\n", 5 - i));
+ dprint (sprintf("PM_DanceMove: %g bumps\n", 5 - i));
#endif
*/
};
@@ -387,7 +491,6 @@ void(entity ground_e, vector ground_v, int docheck) PM_SetOnground =
}

// assume we're on ground if we have a groundnormal field -- CEV
- // is there a situation where groundnormal could be '0 0 0'? -- CEV
if (self.groundnormal != __NULL__)
{
// on ground
@@ -422,27 +525,8 @@ void() PM_CategorizePosition =
// do a trace to check
PM_SetOnground (__NULL__, __NULL__, TRUE);

- // clear doublejumped if outside of DOUBLEJUMP_COOLDOWN
- if ((self.pmove_flags & PMF_DOUBLEJUMPED)
- && self.jump_time <= (time - PM_DOUBLEJUMP_COOLDOWN))
- {
- self.pmove_flags &= ~PMF_DOUBLEJUMPED;
- }
-
- // zero out groundboost timer if it's gone negative -- CEV
- if (self.groundboost_time < 0)
- self.groundboost_time = 0;
-
- // PMF_NOSTICK if within nostick_time, otherwise clear -- CEV
- if (self.nostick_time > 0)
- {
- self.pmove_flags |= PMF_NOSTICK;
- }
- else
- {
- self.nostick_time = 0;
- self.pmove_flags &= ~PMF_NOSTICK;
- }
+ // clear timers that have gone negative
+ PM_ManageTimers (0);

// check water levels
point = self.origin;
@@ -470,6 +554,17 @@ void() PM_CategorizePosition =
self.watertype = CONTENT_EMPTY;
self.waterlevel = WATERLEVEL_NONE;
}
+
+ // can't be waterjumping if we're on ground
+ if (self.pmove_flags & PMF_WATERJUMP &&
+ (!self.waterlevel || self.pmove_flags & PMF_ONGROUND))
+ {
+ #ifdef SSQC
+ dprint ("PM_CategorizePosition: clearing FL_WATERJUMP\n");
+ #endif
+ self.pmove_flags &= ~PMF_WATERJUMP;
+ self.flags &= ~FL_WATERJUMP;
+ }
};

//======================================================================
@@ -529,6 +624,8 @@ void(vector wishvel, float wishspeed, float accel, float move_time)
{
float newspeed, speed, wishspd;

+ // need to ignore Z velocity -- CEV
+ wishvel = [wishvel_x, wishvel_y, 0];
wishspd = vlen (wishvel);
wishvel = normalize (wishvel);
if (wishspd > PM_MAXAIRSPEED)
@@ -572,16 +669,9 @@ void(vector wishdir, float wishspeed, float move_time) PM_AirControl =
//----------------------------------------------------------------------
void() PM_Jump =
{
- // are we already waterjumping?
- if (self.pmove_flags & PMF_WATERJUMP)
- return;
-
- // are we noclipping?
- if (self.movetype == MOVETYPE_NOCLIP)
- return;
-
- // don't pogo
- if (self.pmove_flags & PMF_JUMP_HELD)
+ // are we already waterjumping, or is jump being held?
+ if (self.pmove_flags & PMF_WATERJUMP ||
+ self.pmove_flags & PMF_JUMP_HELD)
return;

// make sure we get at least jumpspeed upwards from
@@ -600,62 +690,48 @@ void() PM_Jump =
if (self.velocity_z < 0)
self.velocity_z = 0;

- if ((self.jump_time > time - PM_TELEJUMP_WINDOW) &&
- (self.teleport_time > time - (PM_TELEJUMP_WINDOW + 0.2)))
+ if (self.doublejump_timer > 0)
{
- // a teleport jump: allow a larger (+0.2) window to
- // account for time to travel thru teleporter -- CEV
- #ifdef SSQC
- dprint (sprintf("PM_Jump: telejump %g, ", self.velocity_z));
- #endif
-
- if !(self.pmove_flags & PMF_DOUBLEJUMPED)
- self.pmove_flags |= PMF_DOUBLEJUMPED;
-
- // set the nostick timer whenever we doublejump -- CEV
- if (self.nostick_time <= 0)
- self.nostick_time = PM_NOSTICK_WINDOW;
-
- if (self.groundnormal && self.groundnormal_z == 1)
- self.velocity_z = 0;
-
- self.pmove_flags |= PMF_NOSTICK;
-
- self.velocity_z += PM_TELEJUMPSPEED;
- }
- else if (self.jump_time > (time - PM_DOUBLEJUMP_WINDOW))
- {
- // A doublejump is two jumps within 400ms -- CEV
- if (self.groundboost_time > 0)
+ // it may be useful in the future (for a tricks mode, etc)
+ // to distinguish between different jump types -- CEV
+ if (self.teleport_time > time - (PM_TELEJUMP_WINDOW + 0.2))
{
+ // a teleport jump: allow a larger (+0.2) window to
+ // account for time to travel thru teleporter -- CEV
#ifdef SSQC
- dprint ("PM_Jump: stairjump ");
+ dprint (sprintf("PM_Jump: telejump %g, ",
+ self.velocity_z));
#endif
+ // additive jump, though it shouldn't matter -- CEV
+ self.velocity_z += PM_TELEJUMPSPEED;
+ }
+ else if (self.groundnormal && self.groundnormal_z == 1 &&
+ self.groundboost_timer > 0)
+ {
+ #ifdef SSQC
+ dprint (sprintf("PM_Jump: stairjump %g, ",
+ self.velocity_z));
+ #endif
+ // don't do additive stairjumps on flat ground -- CEV
+ self.velocity_z = PM_DOUBLEJUMPSPEED;
}
else
{
#ifdef SSQC
- dprint ("PM_Jump: doublejump ");
+ dprint (sprintf("PM_Jump: doublejump %g, ",
+ self.velocity_z));
#endif
+ // we want an additive doublejump here -- CEV
+ self.velocity_z += PM_DOUBLEJUMPSPEED;
}

- #ifdef SSQC
- dprint (sprintf("%g, ", self.velocity_z));
- #endif
-
- if (!(self.pmove_flags & PMF_DOUBLEJUMPED))
- self.pmove_flags |= PMF_DOUBLEJUMPED;
-
- // set the nostick timer whenever we doublejump -- CEV
- if (self.nostick_time <= 0)
- self.nostick_time = PM_NOSTICK_WINDOW;
-
- if (self.groundnormal && self.groundnormal_z == 1)
- self.velocity_z = 0;
-
- self.pmove_flags |= PMF_NOSTICK;
+ // set the doublejump countdown timer -- CEV
+ self.doublejump_timer = PM_DOUBLEJUMP_WINDOW;
+ self.pmove_flags |= PMF_DOUBLEJUMPED;

- self.velocity_z += PM_DOUBLEJUMPSPEED;
+ // re-set the nostick timer whenever we doublejump -- CEV
+ if (self.nostick_timer <= 0)
+ self.nostick_timer = PM_NOSTICK_WINDOW;
}
else
{
@@ -665,9 +741,12 @@ void() PM_Jump =
#endif

self.velocity_z += PM_JUMPSPEED;
+
+ // set the doublejump countdown timer -- CEV
+ self.doublejump_timer = PM_DOUBLEJUMP_WINDOW;
}

- // report Z velocity
+ // report new Z velocity
#ifdef SSQC
dprint (sprintf("%g\n", self.velocity_z));
#endif
@@ -677,12 +756,12 @@ void() PM_Jump =
self.pmove_flags |= PMF_JUMP_HELD;

// timers
- self.jump_time = time;
- if (self.groundboost_time > 0)
- self.groundboost_time = 0;
+ self.groundboost_timer = PM_GROUNDBOOST_WINDOW;
+ self.skim_timer = PM_WALLCLIP_WINDOW;
};

//----------------------------------------------------------------------
+/* TODO CEV this needs to be reworked.
void(vector checkdir) PM_WallJump =
{
// is our upward speed greater than walljump speed?
@@ -705,10 +784,6 @@ void(vector checkdir) PM_WallJump =
if (self.pmove_flags & PMF_JUMP_HELD)
return;

- // don't let the player jump more often than WALLJUMP_WINDOW -- CEV
- if ((self.jump_time) && (self.jump_time > time - PM_WALLJUMP_WINDOW))
- return;
-
// TODO CEV
return;

@@ -734,7 +809,7 @@ void(vector checkdir) PM_WallJump =
self.velocity_x += trace_plane_normal_x * PM_WALLJUMPFORCE;
self.velocity_y += trace_plane_normal_y * PM_WALLJUMPFORCE;
// now modify Z
- if (self.jump_time > (time - PM_DOUBLEJUMP_WINDOW))
+ if (self.doublejump_timer > 0)
{
self.velocity_z += PM_DOUBLEJUMPSPEED;
}
@@ -749,27 +824,9 @@ void(vector checkdir) PM_WallJump =
self.button2 = 0;
self.groundnormal = __NULL__;
self.groundentity = __NULL__;
- // set jump timer
- self.jump_time = time;
- }
-};
-
-//----------------------------------------------------------------------
-void() PM_WaterJump =
-{
- // can't be waterjumping if we're on ground
- if ((self.pmove_flags & PMF_WATERJUMP) &&
- (((time > self.teleport_time) && !(self.waterlevel)) ||
- (self.pmove_flags & PMF_ONGROUND)))
- {
- #ifdef SSQC
- dprint ("PM_WaterJump: clearing FL_WATERJUMP\n");
- #endif
- self.pmove_flags &= ~PMF_WATERJUMP;
- self.flags &= ~FL_WATERJUMP;
- self.teleport_time = 0;
}
};
+*/

//======================================================================
// PM_WalkAccelerate
@@ -777,16 +834,11 @@ void() PM_WaterJump =
// Handle acceleration for MOVETYPE_WALK entities (players).
// Modifies velocity. -- CEV
//----------------------------------------------------------------------
-void(float premove, float mt) PM_WalkAccelerate =
+void(vector wishvel, float premove, float mt) PM_WalkAccelerate =
{
- vector wishvel, wishdir, wishang;
+ vector wishdir, wishang;
float wishspeed;

- makevectors (input_angles);
-
- wishvel = v_forward * input_movevalues_x;
- wishvel += v_right * input_movevalues_y;
- wishvel += v_up * input_movevalues_z;
wishspeed = vlen ([wishvel_x, wishvel_y, 0]);
wishdir = normalize ([wishvel_x, wishvel_y, 0]);

@@ -795,13 +847,36 @@ void(float premove, float mt) PM_WalkAccelerate =

if (premove)
{
- if (input_buttons & 2)
+ if (self.pmove_flags & PMF_ONLADDER)
{
- // +jump was pressed
- if (self.pmove_flags & PMF_ONGROUND)
- PM_Jump ();
- // else
- // PM_WallJump (right);
+ // ladder physics
+ if (input_buttons & 2)
+ {
+ // PlayerClimb
+ // johnfitz
+ self.velocity = '0 0 160';
+ }
+ else
+ {
+ self.flags |= FL_JUMPRELEASED;
+ self.pmove_flags &= ~PMF_JUMP_HELD;
+ self.velocity = 0.9 * self.velocity;
+ self.velocity_z = 0;
+ }
+
+ self.pmove_flags &= ~PMF_ONGROUND;
+ }
+ else
+ {
+ if (input_buttons & 2)
+ {
+ // +jump was pressed
+ if (self.pmove_flags & PMF_ONGROUND)
+ // normal jump
+ PM_Jump ();
+ // else
+ // PM_WallJump (right);
+ }
}

if (self.pmove_flags & PMF_ONGROUND)
@@ -817,14 +892,14 @@ void(float premove, float mt) PM_WalkAccelerate =
{
// we're on ground so apply friction and pick an
// acceleration value -- CEV
- if (self.groundboost_time > 0)
+ if (self.groundboost_timer > 0)
{
PM_Friction (PM_BOOSTFRICTION, mt);
PM_Accelerate (wishdir, wishspeed, PM_BOOSTACCEL, mt);
}
else
{
- PM_Friction (PM_FRICTION, mt);
+ PM_Friction (PM_GROUNDFRICTION, mt);
PM_Accelerate (wishdir, wishspeed, PM_GROUNDACCEL, mt);
}
}
@@ -846,17 +921,19 @@ void(float premove, float mt) PM_WalkAccelerate =
// calculate the difference between the angle
// we're currently moving and the angle the
// user is requesting to move -- CEV
+ // there's probably a way to do this with input_angles
wishang = vectoangles (wishdir) -
- vectoangles (self.velocity);
- wishang_y = anglemod (wishang_y);
+ vectoangles (self.velocity);

// limit wishang_y to a range of 0-180 (roughly)
+ if (wishang_y < 0)
+ wishang_y = wishang_y + 360;
if (wishang_y >= 180)
wishang_y = fabs (wishang_y - 360);

if ((wishang_y > 45) && (wishang_y < 135) &&
vlen ([self.velocity_x, self.velocity_y, 0])
- >= 180)
+ >= 90)
{
// Q1 air control when requesting movement
// within 45 to 135 degrees of current
@@ -889,36 +966,66 @@ void(float premove, float mt) PM_WalkAccelerate =
// PM_SwimAccelerate
// Largely copied from id Software's WaterMove. Works like NoClipAccelerate.
//----------------------------------------------------------------------
-void(float move_time) PM_SwimAccelerate =
+void(vector wishvel, float move_time) PM_SwimAccelerate =
{
- vector wishdir;
float wishspeed;

- makevectors (input_angles);
+ // CheckWaterJump
+ if (self.waterlevel == WATERLEVEL_WAIST)
+ {
+ local vector start, end;

- wishdir = v_forward * input_movevalues_x;
- wishdir += v_right * input_movevalues_y;
- wishdir += v_up * input_movevalues_z;
+ start = self.origin;
+ start_z = start_z + 8;
+ // use wishvel instead of v_forward -- CEV
+ end = [wishvel_x, wishvel_y, 0];
+ end = normalize (end);
+ traceline (start, (start + end * 24), TRUE, self);

- if (input_buttons & 2)
+ if (trace_fraction < 1)
+ {
+ // solid at the waist
+ start_z = start_z + self.maxs_z - 8;
+ self.movedir = trace_plane_normal * -50;
+ traceline (start, (start + end * 24), TRUE, self);
+
+ if (trace_fraction == 1)
+ {
+ // open at eye level
+ self.flags |= FL_WATERJUMP;
+ self.pmove_flags |= PMF_WATERJUMP;
+ self.flags &= ~FL_JUMPRELEASED;
+ self.pmove_flags &= ~PMF_JUMP_HELD;
+ // Z velocity was 225
+ self.velocity_z = PM_JUMPSPEED;
+ // safety net
+ self.teleport_time = time + 2;
+ // don't stick to the floor
+ self.nostick_timer = PM_NOSTICK_WINDOW;
+ // don't skim/clip past walls
+ self.skim_timer = 0;
+ }
+ }
+ }
+
+ if (input_buttons & 2 && !(self.pmove_flags & PMF_WATERJUMP))
// smartjump
- wishdir_z = max (PM_MAXSPEED, wishdir_z);
+ wishvel_z = max (PM_MAXSPEED, wishvel_z);
else if (!input_movevalues)
- // drift towards bottom; value copied from WinQuake -- CEV
- wishdir_z -= PM_WATERSINKSPEED;
+ // drift towards bottom -- CEV
+ wishvel_z -= PM_WATERSINKSPEED;

- wishspeed = vlen (wishdir);
+ wishspeed = vlen (wishvel);
if (wishspeed > PM_WATERMAXSPEED)
wishspeed = PM_WATERMAXSPEED;
- // value copied from WinQuake (id Software's) SV_WaterMove -- CEV
- // wishspeed *= 0.7;
- wishdir = normalize (wishdir);
+ wishvel = normalize (wishvel);

// water friction; reuse PM_Friction with a special constant -- CEV
if (!(self.pmove_flags & PMF_WATERJUMP))
PM_Friction (PM_WATERFRICTION, move_time);

- PM_Accelerate (wishdir, wishspeed, PM_WATERACCEL, move_time);
+ // accelerate; reuse PM_Accelerate with a special constant -- CEV
+ PM_Accelerate (wishvel, wishspeed, PM_WATERACCEL, move_time);
};

//======================================================================
@@ -927,26 +1034,48 @@ void(float move_time) PM_SwimAccelerate =
// Basic acceleration for flying / noclipping players. Not suitable for
// swimming. -- CEV
//----------------------------------------------------------------------
-void(float scale, float move_time) PM_NoClipAccelerate =
+void(vector wishvel, float scale, float move_time) PM_NoClipAccelerate =
{
- vector wishdir;
float wishspeed;

- makevectors (input_angles);
-
- wishdir = v_forward * input_movevalues_x;
- wishdir += v_right * input_movevalues_y;
- wishdir += v_up * input_movevalues_z;
-
// smartjump
if (input_buttons & 2)
- wishdir_z = max (PM_MAXSPEED, wishdir_z);
+ wishvel_z = max (PM_MAXSPEED, wishvel_z);

- wishspeed = vlen (wishdir) * scale;
- wishdir = normalize (wishdir);
+ wishspeed = vlen (wishvel) * scale;
+ wishvel = normalize (wishvel);

- PM_Friction (PM_FRICTION, move_time);
- PM_Accelerate (wishdir, wishspeed, PM_GROUNDACCEL, move_time);
+ PM_Friction (PM_GROUNDFRICTION, move_time);
+ PM_Accelerate (wishvel, wishspeed, PM_GROUNDACCEL, move_time);
+};
+
+//----------------------------------------------------------------------
+void(float move_time) PM_ManageTimers =
+{
+ if (self.doublejump_timer > 0 && move_time > 0)
+ {
+ self.doublejump_timer -= move_time;
+ }
+ else if (self.doublejump_timer <= 0)
+ {
+ self.doublejump_timer = 0;
+ self.pmove_flags &= ~PMF_DOUBLEJUMPED;
+ }
+
+ if (self.groundboost_timer > 0 && move_time > 0)
+ self.groundboost_timer -= move_time;
+ else if (self.groundboost_timer < 0)
+ self.groundboost_timer = 0;
+
+ if (self.nostick_timer > 0 && move_time > 0)
+ self.nostick_timer -= move_time;
+ else if (self.nostick_timer < 0)
+ self.nostick_timer = 0;
+
+ if (self.skim_timer > 0 && move_time > 0)
+ self.skim_timer -= move_time;
+ else if (self.skim_timer < 0)
+ self.skim_timer = 0;
};

//======================================================================
@@ -954,6 +1083,7 @@ void(float scale, float move_time) PM_NoClipAccelerate =
//----------------------------------------------------------------------
void(entity target) PM_Move =
{
+ vector wishvel;
entity oldself;

oldself = self;
@@ -965,7 +1095,6 @@ void(entity target) PM_Move =
self.pmove_flags &= ~PMF_JUMP_HELD;

PM_CategorizePosition ();
- PM_WaterJump ();

if (input_timelength < 0)
{
@@ -975,90 +1104,106 @@ void(entity target) PM_Move =
return;
}

+ // make vectors & prepare wishvel here, once, instead of in the
+ // individual accelerate functions -- CEV
+ makevectors (input_angles);
+
+ wishvel = v_forward * input_movevalues_x;
+ wishvel += v_right * input_movevalues_y;
+ wishvel += v_up * input_movevalues_z;
+
switch (self.movetype)
{
case MOVETYPE_WALK:
local float dogravity, doskim, dostep, sticky;

dostep = TRUE;
- doskim = TRUE;
+ doskim = FALSE;
sticky = FALSE;

// split the acceleration in two to reduce
// framerate dependence. -- CEV
if (self.waterlevel >= WATERLEVEL_WAIST)
{
- // no gravity, steps, or velocity-restoring
- // behavior underwater -- CEV
+ // no gravity, velocity-restoring behavior,
+ // or sticky clips underwater -- CEV
dogravity = FALSE;
doskim = FALSE;
- dostep = FALSE;
+ dostep = TRUE;
sticky = FALSE;
// swim acceleration
- PM_SwimAccelerate (input_timelength * 0.5f);
+ PM_SwimAccelerate (wishvel,
+ input_timelength * 0.5f);
}
else
{
- PM_WalkAccelerate (TRUE,
+ PM_WalkAccelerate (wishvel, TRUE,
input_timelength * 0.5f);

// WalkAccelerate calls Jump which might
// alter pmove_flags -- CEV
- if (self.pmove_flags & PMF_NOSTICK)
+ if (self.nostick_timer > 0)
sticky = FALSE;
else
sticky = TRUE;

+ // gravity checks
dogravity = !(self.pmove_flags & PMF_ONGROUND);
- // apply gravity when we're on a ramp
if (self.groundnormal && self.groundnormal_z <1)
+ // apply gravity when we're on a ramp
dogravity = TRUE;

- if ((self.jump_time <=
- (time - PM_WALLCLIP_WINDOW)) ||
- (self.teleport_time > (time - 0.1)) ||
- (self.groundboost_time > 0) ||
- (self.pmove_flags & PMF_WATERJUMP))
- doskim = FALSE;
+ // skim / wallclipping checks -- CEV
+ if (self.skim_timer > 0)
+ doskim = TRUE;

+ if (self.pmove_flags & PMF_ONLADDER)
+ {
+ // feature set for ladders -- CEV
+ dogravity = FALSE;
+ doskim = FALSE;
+ dostep = TRUE;
+ sticky = FALSE;
+ }
}

// timers (part 1) -- CEV
- if (self.groundboost_time > 0)
- self.groundboost_time -= input_timelength *0.5f;
- if (self.nostick_time > 0)
- self.nostick_time -= input_timelength * 0.5f;
+ PM_ManageTimers (input_timelength * 0.5f);

// do the move
- PM_NuclideMove (dogravity, sticky, dostep, doskim);
+ PM_DanceMove (dogravity, sticky, dostep, doskim);

// second pass at acceleration
- if (self.waterlevel >= 2)
- PM_SwimAccelerate (input_timelength * 0.5f);
+ if (self.waterlevel >= WATERLEVEL_WAIST)
+ PM_SwimAccelerate (wishvel,
+ input_timelength * 0.5f);
else
- PM_WalkAccelerate (FALSE,
+ PM_WalkAccelerate (wishvel, FALSE,
input_timelength * 0.5f);

// timers (part 2) -- CEV
- if (self.groundboost_time > 0)
- self.groundboost_time -= input_timelength *0.5f;
- if (self.nostick_time > 0)
- self.nostick_time -= input_timelength * 0.5f;
+ PM_ManageTimers (input_timelength * 0.5f);
+
+ // clear ladder flag -- CEV
+ if (self.pmove_flags & PMF_ONLADDER)
+ self.pmove_flags &= ~PMF_ONLADDER;

break;

case MOVETYPE_FLY:
// split the acceleration in two to reduce
// framerate dependence. -- CEV
- PM_NoClipAccelerate (1.0f, input_timelength * 0.5f);
- PM_NuclideMove (FALSE, TRUE, TRUE, FALSE);
- PM_NoClipAccelerate (1.0f, input_timelength * 0.5f);
+ PM_NoClipAccelerate (wishvel, 1.0f,
+ input_timelength * 0.5f);
+ PM_DanceMove (FALSE, TRUE, TRUE, FALSE);
+ PM_NoClipAccelerate (wishvel, 1.0f,
+ input_timelength * 0.5f);
break;

case MOVETYPE_NOCLIP:
// noclip is a debugging feature, no need to
// worry about consistency. -- CEV
- PM_NoClipAccelerate (1.0f, input_timelength);
+ PM_NoClipAccelerate (wishvel, 1.0f, input_timelength);
self.origin += self.velocity * input_timelength;
break;

@@ -1066,10 +1211,10 @@ void(entity target) PM_Move =
break;
}

- if (self.groundentity)
- PM_DoTouch (self.groundentity);
PM_SetOnground (self.groundentity, self.groundnormal, TRUE);
+ #ifdef SSQC
touchtriggers (self);
+ #endif
self = oldself;
};

Return to the top of this page or return to the overview of this repo.

Diff qc/progs.src

diff --git a/qc/progs.src b/qc/progs.src
index 988058b..c426f56 100644
--- a/qc/progs.src
+++ b/qc/progs.src
@@ -6,7 +6,6 @@
#define SSQC

#includelist
-defs_constants.qc // compiler defines (fixed constant values)
defs_globalvars.qc // globalvars_t
defs_entvars.qc // entvars_t
defs_builtins.qc // builtin functions (& overrides)
@@ -39,6 +38,7 @@ doors.qc
buttons.qc
triggers.qc // added trigger_push_custom based on Hipnotic
triggers_heal.qc // trigger_heal (was in dtmisc.qc) -- CEV
+triggers_ladder.qc // ladders (from rubicon2) -- CEV
triggers_push.qc // wind/push brushes, jumppads -- CEV
triggers_shake.qc // triggerable shake from Zer cutscenes; was dtquake.qc
triggers_teleport.qc // was in triggers.qc -- CEV

Return to the top of this page or return to the overview of this repo.

Diff qc/rubicon2.qc

diff --git a/qc/rubicon2.qc b/qc/rubicon2.qc
index 4e54653..7c33972 100644
--- a/qc/rubicon2.qc
+++ b/qc/rubicon2.qc
@@ -539,60 +539,6 @@ void () func_breakable = {

/*
===============================================================================
-trigger_ladder
-===============================================================================
-*/
-
-void() ladder_touch =
-{
- // from Copper -- dumptruck_ds
- if (!CheckValidTouch()) return;
-
- // prevent the player "sticking" to a ladder if they are standing on
- // the platform at the top of the ladder with the bottom of their
- // bounding box flush with the top of the trigger -- iw
- if (other.absmin_z + 1 >= self.absmax_z - 1)
- return;
-
- // if the trigger has an angles field, check player's facing direction
- if (self.movedir != '0 0 0')
- {
- makevectors (other.angles);
- if (v_forward * self.movedir < 0)
- return; // not facing the right way
- }
- other.onladder = 1;
-}
-
-/*QUAKED trigger_ladder (.5 .5 .5) ? X X X X X X X X NOT_ON_EASY NOT_ON_NORMAL NOT_ON_HARD_OR_NIGHTMARE NOT_IN_DEATHMATCH NOT_IN_COOP NOT_IN_SINGLEPLAYER X NOT_ON_HARD_ONLY NOT_ON_NIGHTMARE_ONLY
-invisible ladder entity. when player is touching this entity, he can climb by pushing 'jump'
-
-Keys:
-
-"angle" the direction player must be facing to climb ladder
-*/
-void() trigger_ladder =
-{
- if (SUB_Inhibit ()) // new spawnflags for all entities -- iw
- return;
-
- // ignore an "up" or "down" angle (doesn't make sense for a ladder)
- if (self.angles_y == -1 || self.angles_y == -2)
- {
- dprint ("WARNING: trigger_ladder ignored bad 'angle' value: ");
- dprint (ftos (self.angles_y));
- dprint ("\n");
-
- self.angles_y = 0;
- }
-
- InitTrigger ();
- self.touch = ladder_touch;
-
- SUB_CheckWaiting();
-};
-/*
-===============================================================================
func_laser
===============================================================================
*/

Return to the top of this page or return to the overview of this repo.

Diff qc/sv_runclient.qc

diff --git a/qc/sv_runclient.qc b/qc/sv_runclient.qc
index ef48409..19686d2 100644
--- a/qc/sv_runclient.qc
+++ b/qc/sv_runclient.qc
@@ -3,7 +3,10 @@ void(entity ent) PM_Move;
void() SV_RunClientCommand =
{
// should match the one used by csqc.
- PM_Move (self);
- // for testing -- CEV
- // runstandardplayerphysics (self);
+ if (autocvar(pm_standardphysics, FALSE))
+ // for testing -- CEV
+ runstandardplayerphysics (self);
+ else
+ // for real
+ PM_Move (self);
};

Return to the top of this page or return to the overview of this repo.

Diff qc/triggers_ladder.qc

diff --git a/qc/triggers_ladder.qc b/qc/triggers_ladder.qc
new file mode 100644
index 0000000..fa397be
--- /dev/null
+++ b/qc/triggers_ladder.qc
@@ -0,0 +1,67 @@
+//==============================================================================
+// LADDERS (from rubicon2.qc)
+//==============================================================================
+
+// selections from Rubicon 2 qc by john fitzgibbons
+
+//======================================================================
+// ladder_touch
+//----------------------------------------------------------------------
+void() ladder_touch =
+{
+ // from Copper -- dumptruck_ds
+ if (!CheckValidTouch())
+ return;
+
+ // prevent the player "sticking" to a ladder if they are standing on
+ // the platform at the top of the ladder with the bottom of their
+ // bounding box flush with the top of the trigger -- iw
+ if (other.absmin_z + 1 >= self.absmax_z - 1)
+ return;
+
+ // if the trigger has an angles field, check player's facing direction
+ if (self.movedir != '0 0 0')
+ {
+ makevectors (other.angles);
+ if (v_forward * self.movedir < 0)
+ // not facing the right way
+ return;
+ }
+
+ // changed to PMFLAGS -- CEV
+ other.pmove_flags |= PMF_ONLADDER;
+}
+
+//======================================================================
+// trigger_ladder
+//----------------------------------------------------------------------
+
+/*QUAKED trigger_ladder (.5 .5 .5) ? X X X X X X X X NOT_ON_EASY NOT_ON_NORMAL NOT_ON_HARD_OR_NIGHTMARE NOT_IN_DEATHMATCH NOT_IN_COOP NOT_IN_SINGLEPLAYER X NOT_ON_HARD_ONLY NOT_ON_NIGHTMARE_ONLY
+invisible ladder entity. when player is touching this entity, he can climb by pushing 'jump'
+
+Keys:
+
+"angle" the direction player must be facing to climb ladder
+*/
+void() trigger_ladder =
+{
+ if (SUB_Inhibit())
+ // new spawnflags for all entities -- iw
+ return;
+
+ // ignore an "up" or "down" angle (doesn't make sense for a ladder)
+ if (self.angles_y == -1 || self.angles_y == -2)
+ {
+ dprint ("WARNING: trigger_ladder ignored bad 'angle' value: ");
+ dprint (ftos (self.angles_y));
+ dprint ("\n");
+
+ self.angles_y = 0;
+ }
+
+ InitTrigger ();
+ self.touch = ladder_touch;
+
+ SUB_CheckWaiting ();
+};
+

Return to the top of this page or return to the overview of this repo.

Diff qc/triggers_push.qc

diff --git a/qc/triggers_push.qc b/qc/triggers_push.qc
index 0cc7617..091c016 100644
--- a/qc/triggers_push.qc
+++ b/qc/triggers_push.qc
@@ -75,8 +75,7 @@ vector(float a, float b, float c) solve_quadratic =
}
}
return v;
-}
-
+};

//======================================================================
// trigger_push_calculatevelocity

Return to the top of this page or return to the overview of this repo.

Diff qc/triggers_teleport.qc

diff --git a/qc/triggers_teleport.qc b/qc/triggers_teleport.qc
index f55704a..eb656b2 100644
--- a/qc/triggers_teleport.qc
+++ b/qc/triggers_teleport.qc
@@ -278,11 +278,9 @@ void() teleport_touch =
// turn this way immediately
other.fixangle = 1;
other.teleport_time = time + 0.7;
- // TODO CEV
- /*
- if (other.flags & FL_ONGROUND)
- other.flags = other.flags - FL_ONGROUND;
- */
+ // don't restore velocity on exiting teleporter -- CEV
+ other.skim_timer = 0;
+ // push player out at PM_TELEEXITSPEED -- CEV
other.velocity = v_forward * PM_TELEEXITSPEED;
}
other.flags = other.flags - other.flags & FL_ONGROUND;

Return to the top of this page or return to the overview of this repo.