djcev.com

//

Git Repos / fte_dogmode / commit b08b2be

Commit: b08b2be7a820b3b765066496c25d50108acd182c
Parent: e134091dd10c4c29e67852ee5f03b16dceca3658
Author: Cameron Vanderzanden, 2024-02-27 20:30
Committer: Cameron Vanderzanden, 2024-02-27 20:30

Commit Message

Bullet projectile, pmove changes, misc

Finished up the projectile rewrite by implementing bullets. They're
based on BDW's Flak Ogre projectiles and the Arcane Dimensions
projectile bullets. (I'm currently using the projectile model from
AD in fact, not included in this repo).

Made several changes to the pmove code, including:
* refactors to the slidemove to hopefully speed it up,
* an alternate slidemove that clips velocity the way Q3's does,
* changes to jumps, the transition-to-ground state, and input
    movevalues to hopefully make stairs easier to use,
* changes to walljumps (must be moving away from the wall now)

I've also written a goofy and very simple input monitor that
will replace the crosshair when the cvar "crosshair" is 0.

There are surely other changes I'm forgetting.

Change List

Diff qc/base_entities.qc

diff --git a/qc/base_entities.qc b/qc/base_entities.qc
index af2a8cf..12f5d1d 100644
--- a/qc/base_entities.qc
+++ b/qc/base_entities.qc
@@ -928,11 +928,11 @@ class base_mapentity: base_entity
#ifdef SSQC
// according to Nuclide start offsets differ between
// client and server Quake C
- for (int i = 1; i < (tokenize(__fullspawndata) - 1);
+ for (float i = 1; i < (tokenize(__fullspawndata) - 1);
i += 2)
#else
// client starts at 0
- for (int i = 0; i < (tokenize(__fullspawndata) - 1);
+ for (float i = 0; i < (tokenize(__fullspawndata) - 1);
i += 2)
#endif
{

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

Diff qc/base_monster.qc

diff --git a/qc/base_monster.qc b/qc/base_monster.qc
index 18052e5..8167d3f 100644
--- a/qc/base_monster.qc
+++ b/qc/base_monster.qc
@@ -330,6 +330,28 @@ class base_monster: base_mapentity
};

//--------------------------------------------------------------
+ nonvirtual void(vector org, vector dir, vector spread) fire_shotgun =
+ {
+ local vector vdir;
+ local float vspeed;
+
+ // 4 bullets instead of 6 -- CEV
+ for (float i = 0; i < 4; i++)
+ {
+ vspeed = crandom () * 10 + BULLET_SPEED;
+ vdir = dir + crandom() * spread_x * v_right +
+ crandom() * spread_y * v_up;
+
+ vdir *= min (vspeed, frame_maxvelocity);
+
+ spawn (projectile_bullet,
+ owner: this,
+ origin: org,
+ velocity: vdir);
+ }
+ };
+
+ //--------------------------------------------------------------
nonvirtual void(vector org, vector dir, float damage, float projspeed)
fire_spike =
{

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

Diff qc/base_projectile.qc

diff --git a/qc/base_projectile.qc b/qc/base_projectile.qc
index 0a386a5..384cf51 100644
--- a/qc/base_projectile.qc
+++ b/qc/base_projectile.qc
@@ -141,6 +141,10 @@ class base_projectile: base_tempentity
sound (p, CHAN_WEAPON, p.snd_hit, 1, ATTN_STATIC);
WriteByte (MSG_BROADCAST, TE_GUNSHOT);
}
+ else if (p.classtype == CT_PROJECTILE_BULLET)
+ {
+ WriteByte (MSG_BROADCAST, TE_GUNSHOT);
+ }
else if (p.classtype == CT_PROJECTILE_WIZARDMISSILE)
{
WriteByte (MSG_BROADCAST, TE_WIZSPIKE);
@@ -352,10 +356,13 @@ class base_projectile: base_tempentity
//------------------------------------------------------------------------------
class base_projectile_qcphys: base_projectile
{
+ vector movement_mins;
+ vector movement_maxs;
+
//--------------------------------------------------------------
// This is a vague imitation of SV_Physics_Toss as found in
- // Ironwail but with a fixed mins and maxs of '0 0 0' when
- // testing movement. -- CEV
+ // Ironwail but with an alternate mins and maxs when testing
+ // movement -- CEV
//--------------------------------------------------------------
virtual void() customphysics =
{
@@ -387,12 +394,12 @@ class base_projectile_qcphys: base_projectile
this.velocity_z -= grav * frame_gravity * frametime;
}

- // test the move with a fixed mins and maxs of '0 0 0' and
+ // test the move with an alternate mins and maxs and
// suitable flags for FLYMISSILE if set -- CEV
- tracebox (this.origin, '0 0 0', '0 0 0',
+ tracebox (this.origin, movement_mins, movement_maxs,
this.origin + (this.velocity * frametime),
- movetype == MOVETYPE_FLYMISSILE ? MOVE_MISSILE : FALSE,
- this.owner);
+ movetype == MOVETYPE_FLYMISSILE ? MOVE_MISSILE: FALSE,
+ this);

if (trace_allsolid || trace_startsolid)
{
@@ -419,8 +426,11 @@ class base_projectile_qcphys: base_projectile
setorigin (this, trace_endpos);

if (trace_fraction >= 1.0f)
- // didn't hit anything
+ {
+ // didn't hit anything; touch triggers and exit
+ touchtriggers ();
return;
+ }

// immediate clipvelocity for MOVETYPE_BOUNCE -- CEV
if (this.movetype == MOVETYPE_BOUNCE)
@@ -477,5 +487,15 @@ class base_projectile_qcphys: base_projectile
this.touch ();
}
other = oldother;
+
+ // be sure to touch triggers! -- CEV
+ touchtriggers ();
+ };
+
+ //--------------------------------------------------------------
+ void() base_projectile_qcphys =
+ {
+ this.movement_mins = '-1 -1 -1';
+ this.movement_maxs = '1 1 1';
};
};

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 3f54f5c..39237c9 100644
--- a/qc/csqc/csqc_defsclient.qc
+++ b/qc/csqc/csqc_defsclient.qc
@@ -87,6 +87,7 @@ float frame_gravity;
float frame_maxvelocity;
float frame_nostep;
float frame_standardphysics;
+float frame_walljump;

//----------------------------------------------------------------------
// Extra fields
@@ -248,6 +249,12 @@ const float CONTENT_SLIME = -4;
const float CONTENT_LAVA = -5;
const float CONTENT_SKY = -6;

+// entity effects
+const float EF_BRIGHTFIELD = 1;
+const float EF_MUZZLEFLASH = 2;
+const float EF_BRIGHTLIGHT = 4;
+const float EF_DIMLIGHT = 8;
+
//----------------------------------------------------------------------
// Valid as a return value from the predraw function. Returning this will
// simply move on to the next entity without the autoadd behaviour, so can

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 3b67765..b72b155 100644
--- a/qc/csqc/csqc_entrypoints.qc
+++ b/qc/csqc/csqc_entrypoints.qc
@@ -95,8 +95,9 @@ void(entity ent) CSQC_DrawViewModel =

ent.origin = '0 0 0';
ent.angles = '0 0 0';
+ ent.effects = player_local.effects;

- addentity (ent);
+ // addentity (ent);
};

//----------------------------------------------------------------------
@@ -115,7 +116,7 @@ void(float vwidth, float vheight, float notmenu) CSQC_UpdateView =

// TODO CEV
// addentities (MASK_ENGINE|MASK_VIEWMODEL);
- addentities (MASK_NORMAL | MASK_ENGINE);
+ // addentities (MASK_NORMAL | MASK_ENGINE);
setproperty (VF_DRAWWORLD, (float)1);
setproperty (VF_MIN, '0 0');
setproperty (VF_SIZE, ssize);
@@ -128,7 +129,7 @@ void(float vwidth, float vheight, float notmenu) CSQC_UpdateView =
if (self.entnum == player_localentnum)
{
// read our smoothed & predicted view origin
- setproperty (VF_VIEWENTITY, player_localentnum);
+ // setproperty (VF_VIEWENTITY, player_localentnum);
setproperty (VF_ORIGIN, view_origin);
setproperty (VF_ANGLES, view_angles);
// setproperty (VF_ANGLES, player_local.angles);
@@ -148,13 +149,19 @@ void(float vwidth, float vheight, float notmenu) CSQC_UpdateView =
// draw view model when not in intermission
CSQC_DrawViewModel (viewentity);
}
+
+ if (player_local.effects & EF_MUZZLEFLASH)
+ dprint ("CSQC_UpdateView: muzzleflash\n");
+
+ setproperty (VF_VIEWENTITY, viewentity);
+ addentities (MASK_ENGINE | MASK_VIEWMODEL | MASK_NORMAL);
}
else
{
// engine didn't tell us about our player entity.
// that's not right...
dprint ("csqc UpdateView: no player_local\n");
- addentities (MASK_VIEWMODEL);
+ addentities (MASK_ENGINE | MASK_VIEWMODEL | MASK_NORMAL);
}

renderscene ();

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

Diff qc/csqc/csqc_hudvanilla.qc

diff --git a/qc/csqc/csqc_hudvanilla.qc b/qc/csqc/csqc_hudvanilla.qc
index 4068808..bc721a6 100644
--- a/qc/csqc/csqc_hudvanilla.qc
+++ b/qc/csqc/csqc_hudvanilla.qc
@@ -826,7 +826,7 @@ void(vector pos) Hud_InputMonitor =
if (input_movevalues_x < 0)
// backward
drawcharacter ([pos_x, pos_y, pos_z],
- str2chr ("+", 0),
+ str2chr ("-", 0),
HUDSIZE_8, HUDRGB_DEF, hudalpha, 0);
else if (input_movevalues_x > 0)
// forward
@@ -872,7 +872,7 @@ void(vector pos) Hud_Speedometer =
// fancy -- CEV
local vector v = [player_local.velocity_x, player_local.velocity_y, 0];

- Hud_DrawNoFont8 (pos, rint(vlen(v)), 3, FALSE, HUDFONT_WHITE);
+ Hud_DrawNoFont8 (pos, ceil(vlen(v)), 3, FALSE, HUDFONT_WHITE);
};

//======================================================================

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 b969773..260a27b 100644
--- a/qc/csqc/csqc_player.qc
+++ b/qc/csqc/csqc_player.qc
@@ -88,6 +88,7 @@ void(entity ent, float endframe) PlayerRunMovement =
frame_maxvelocity = autocvar (sv_maxvelocity, 10000.0);
frame_nostep = autocvar (pm_nostep, FALSE);
frame_standardphysics = autocvar (pm_standardphysics, FALSE);
+ frame_walljump = autocvar (pm_walljump, TRUE);

while (pmove_frame <= endframe)
{
@@ -242,7 +243,7 @@ void(float isnew) PlayerUpdate =
if (intermission)
{
// zero out velocity if in intermission -- CEV
- frame_gravity = 0.0;
+ frame_gravity = 0.0f;
input_movevalues = '0 0 0';
self.velocity = '0 0 0';
viewentity.angles = self.angles;

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

Diff qc/defs_builtins.qc

diff --git a/qc/defs_builtins.qc b/qc/defs_builtins.qc
index 0bd0067..2878afd 100644
--- a/qc/defs_builtins.qc
+++ b/qc/defs_builtins.qc
@@ -180,6 +180,9 @@ float(string) stof = #81;
void(vector start, vector mins, vector maxs, vector end, float nomonsters,
entity ent) tracebox = #90;

+// DP_QC_GETLIGHT
+vector(vector org) getlight = #92;
+
// Part of DP_QC_MINMAXBOUND Returns the lowest value of its arguments.
float(float a, float b, ...) min = #94;
// Part of DP_QC_MINMAXBOUND Returns the highest value of its arguments.

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 362d1ca..58881ca 100644
--- a/qc/defs_misc.qc
+++ b/qc/defs_misc.qc
@@ -114,10 +114,6 @@ const vector VEC_ORIGIN = '0 0 0';
const vector VEC_HULL_MIN = '-16 -16 -24';
const vector VEC_HULL_MAX = '16 16 32';
const vector VEC_HULL_SIZE = '32 32 56';
-// Player crouching
-const vector VEC_CHULL_MIN = '-16 -16 -18';
-const vector VEC_CHULL_MAX = '16 16 18';
-const vector VEC_CHULL_SIZE = '32 32 36';
// Ogres, Shalrath, Demon, Shambler
const vector VEC_HULL2_MIN = '-32 -32 -24';
const vector VEC_HULL2_MAX = '32 32 64';
@@ -245,6 +241,7 @@ float frame_gravity;
float frame_maxvelocity;
float frame_nostep;
float frame_standardphysics;
+float frame_walljump;

// per-map settings
float known_release; // ID for a release, values above -- iw

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

Diff qc/entrypoints.qc

diff --git a/qc/entrypoints.qc b/qc/entrypoints.qc
index 1551e16..64c4961 100644
--- a/qc/entrypoints.qc
+++ b/qc/entrypoints.qc
@@ -401,6 +401,7 @@ void() IntermissionThink =
{
// full send -- CEV
// self.fixangle = TRUE;
+ self.velocity = '0 0 0';
self.SendFlags = 0xffffff;

if (time < intermission_exittime)

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

Diff qc/func/breakable.qc

diff --git a/qc/func/breakable.qc b/qc/func/breakable.qc
index 653dbf7..895e5ef 100644
--- a/qc/func/breakable.qc
+++ b/qc/func/breakable.qc
@@ -183,7 +183,7 @@ class base_breakable: base_func
local float count = 0;
local string break_template = "";

- for (int i = 1; i < 6; i++)
+ for (float i = 1; i < 6; i++)
{
switch (i)
{
@@ -211,7 +211,7 @@ class base_breakable: base_func

if (break_template != __NULL__ && break_template != "")
{
- for (int j = 0; j < count; j++)
+ for (float j = 0; j < count; j++)
{
template_single_debris (break_template);
}
@@ -229,7 +229,7 @@ class base_breakable: base_func
//--------------------------------------------------------------
nonvirtual void() make_breakable_debris =
{
- for (int i = 0; i < this.cnt; i++)
+ for (float i = 0; i < this.cnt; i++)
single_debris ();
};

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

Diff qc/monsters/playerclient.qc

diff --git a/qc/monsters/playerclient.qc b/qc/monsters/playerclient.qc
index df83e32..86ae5aa 100644
--- a/qc/monsters/playerclient.qc
+++ b/qc/monsters/playerclient.qc
@@ -551,16 +551,38 @@ class player: base_entity
//--------------------------------------------------------------
nonvirtual void() fire_shotgun =
{
- local vector dir;
+ local vector org, dir, vdir;
+ local float vspeed;

sound (this, CHAN_WEAPON, "weapons/guncock.wav", 1, ATTN_NORM);

this.punchangle_x = -2;

this.currentammo = this.ammo_shells = this.ammo_shells - 1;
- dir = aim (this, 100000);
- // TODO CEV
- FireBullets (6, dir, '0.04 0.04 0');
+ // dir = aim (this, 100000);
+ makevectors (this.v_angle);
+
+ org = origin + (v_forward * 10) + (v_right * 0) + (v_up * 8);
+ dir = normalize (v_forward * BULLET_SPEED);
+
+ // 5 bullets instead of 6 -- CEV
+ for (float i = 0; i < 5; i++)
+ {
+ vspeed = crandom () * 10 + BULLET_SPEED;
+ vdir = dir + crandom() * 0.04 * v_right +
+ crandom() * 0.04 * v_up;
+ /*
+ vdir = dir +
+ ((((i / 10.0) - 0.3) * 0.12) * v_right) +
+ ((((i / 10.0) - 0.3) * 0.12) * v_up);
+ */
+ vdir *= min (vspeed, frame_maxvelocity);
+
+ spawn (projectile_bullet,
+ owner: this,
+ origin: org,
+ velocity: vdir);
+ }
};

//--------------------------------------------------------------
@@ -568,7 +590,8 @@ class player: base_entity
//--------------------------------------------------------------
nonvirtual void() fire_supershotgun =
{
- local vector dir;
+ local vector org, dir, vdir;
+ local float vspeed;

if (this.currentammo == 1)
{
@@ -581,8 +604,25 @@ class player: base_entity
this.punchangle_x = -4;

this.currentammo = this.ammo_shells = this.ammo_shells - 2;
- dir = aim (this, 100000);
- FireBullets (14, dir, '0.14 0.08 0');
+ // dir = aim (this, 100000);
+ makevectors (this.v_angle);
+
+ org = origin + (v_forward * 10) + (v_right * 0) + (v_up * 8);
+ dir = normalize (v_forward * BULLET_SPEED);
+
+ // 12 bullets instead of 14 -- CEV
+ for (float i = 0; i < 12; i++)
+ {
+ vspeed = crandom () * 10 + BULLET_SPEED;
+ vdir = dir + (crandom() * 0.14) * v_right +
+ (crandom() * 0.08) * v_up;
+ vdir *= min (vspeed, frame_maxvelocity);
+
+ spawn (projectile_bullet,
+ owner: this,
+ origin: org,
+ velocity: vdir);
+ }
};

//--------------------------------------------------------------
@@ -1589,6 +1629,11 @@ class player: base_entity
this.gravity = this.wantedgravity;
}

+ /*
+ dprint (sprintf("player::prethink: getlight %v\n",
+ getlight (this.origin)));
+ */
+
// If just spawned in, try to recover previous fog values
// from own client entity, if any
if (cleanUpClientStuff)

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

Diff qc/monsters/soldier.qc

diff --git a/qc/monsters/soldier.qc b/qc/monsters/soldier.qc
index 6e43c72..1e64e89 100644
--- a/qc/monsters/soldier.qc
+++ b/qc/monsters/soldier.qc
@@ -250,7 +250,6 @@ class monster_army: base_walkmonster
else
{
local vector dir;
- local entity en;

ai_face ();

@@ -258,12 +257,15 @@ class monster_army: base_walkmonster
sound_attack (this, CHAN_WEAPON, "soldier/sattck1.wav",
1, ATTN_NORM);

+ // FireBullets (4, dir, '0.1 0.1 0');
+
// fire somewhat behind the player, so a dodging player
// is harder to hit
- en = this.enemy;
- dir = en.origin - en.velocity * 0.2;
+ dir = this.enemy.origin - this.enemy.velocity * 0.2;
dir = normalize (dir - this.origin);
- FireBullets (4, dir, '0.1 0.1 0');
+ makevectors (this.angles);
+ fire_shotgun (this.origin + (v_forward * 10 + '0 0 12'),
+ dir, '0.1 0.1 0');
}
};

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 15d7286..fbfebb9 100644
--- a/qc/pmove.qc
+++ b/qc/pmove.qc
@@ -47,28 +47,23 @@ vector input_movevalues; // movement requested by client
// accel 10.666, friction 6, T 125, wishspeed 360 = 475
// accel 10.666, friction 6, T 125, wishspeed 380 = 502
// accel 10.666, friction 6, T 125, wishspeed 400 = 528
-// accel 11.666, friction 6, T 125, wishspeed 320 = 441
-// accel 11.666, friction 6, T 125, wishspeed 360 = 496
-// accel 11.666, friction 6, T 125, wishspeed 380 = 523
-// accel 15.666, friction 8, T 125, wishspeed 320 = 440
-// accel 15.666, friction 8, T 125, wishspeed 380 = 523

// acceleration & friction (cheers for the number of the beast)
const float PM_AIRACCEL = 6.666f; // 10 in Q1; now 7.5 or 8.0
const float PM_AIRACCELQ3 = 0.666f; // 1.0 in Q3 ?; now 0.75 or 0.8
-const float PM_AIRACCELFWD = 0.666; // 1 feels close to Q3 / CPM
+const float PM_AIRACCELFWD = 0.666f; // 1 feels close to Q3 / CPM
const float PM_AIRACCELBACK = 6.666f; // Air stop speed in Q3? 5.0f?
const float PM_AIRACCELTURN = 666.0f; // affects +fwd turning radius; 100.0f
const float PM_BOOSTACCEL = 10.666f; // ground boost accel; 10; 10 + 1.25
-const float PM_BOOSTFRICTION = 6.0f; // ground boost friction; 4 is Q1
-const float PM_GROUNDACCEL = 10.666f; // 10 is Q1, 15 is CPM; 15 - 1.25 13.75
+const float PM_BOOSTFRICTION = 1.0f; // ground boost friction; 4 is Q1
+const float PM_GROUNDACCEL = 10.666f; // 10 is Q1, 15 is CPM
const float PM_GROUNDFRICTION = 6.0f; // 4 for Q1, 6 for Q3, 8 for (old?) CPM
const vector PM_GROUNDDIST_V = '0 0 1'; // distance for ground check
const float PM_WATERACCEL = 10.0f; // water acceleration
const float PM_WATERFRICTION = 4.0f; // friction in water

// horizontal speeds (mostly)
-const float PM_BOOSTWISHSPEED = 380.0f; //
+const float PM_BOOSTWISHSPEED = 380.0f; // 320, 400
const float PM_CROUCHSPEED = 120.0f; // ???
const float PM_MAXSPEED = 320.0f; // 320 always
const float PM_MAXAIRSPEED = 30.0f; // 30 for Q1 air control
@@ -85,7 +80,7 @@ const float PM_JUMPSPEED = 270.0f; // standard jump Z velocity; 90 * 3
const float PM_DOUBLEJUMPSPEED = 270.0f;// 270 * 1.5 in CPM; 360 here
const float PM_STAIRJUMPSPEED = 360.0f; // 360 = 90 * 4
const float PM_TELEJUMPSPEED = 360.0f; // same as STAIRJUMPSPEED
-const float PM_WALLJUMPFORCE = 90.0f; // 90 * 2; push away from wall
+const float PM_WALLJUMPFORCE = 90.0f; // push away from wall
const float PM_WALLJUMPGROUND = 28.0f; // distance from ground to allow WJ (36)
const float PM_WALLJUMPLIMIT = -225.f; // no walljump if Z vel below this
const float PM_WALLJUMPSPEED = 270.0f; // 225 = 90 * 2.5; wall jump
@@ -99,41 +94,50 @@ const float PM_WALLJUMP_WINDOW = 0.2f; // duration between walljumps
const float PM_WALLCLIP_WINDOW = 0.25f; //

// misc
+const float PM_MAX_CLIP_PLANES = 5;
const float PM_OVERCLIP = 1.0f; // Quake3's OVERCLIP is 1.001f
-const float PM_ONESIDEDCLIP = -0.05; // -0.001, -0.01, -0.1
+const float PM_ONESIDEDCLIP = -0.001f; // -0.001, -0.01, -0.1
const float PM_STEPHEIGHT = 18.0f; // 18 for Q1, 22 for later games?
const vector PM_TELEDROP = '0 0 -64'; // drop teleporter exit to floor if
// floor is within this distance
// water level
-const int WATERLEVEL_NONE = 0;
-const int WATERLEVEL_FEET = 1;
-const int WATERLEVEL_WAIST = 2;
-const int WATERLEVEL_EYES = 3;
-
-//
-const vector CROUCHED_MINS = '-16 -16 -18';// VEC_HULL_MIN is '-16 -16 -24'
-const vector CROUCHED_MAXS = '16 16 18';// VEC_HULL_MAX is '16 16 32'
-
-// pmove_flags
+const float WATERLEVEL_NONE = 0;
+const float WATERLEVEL_FEET = 1;
+const float WATERLEVEL_WAIST = 2;
+const float WATERLEVEL_EYES = 3;
+
+// Player Sizes
+const vector PM_STAND_MIN = VEC_HULL_MIN;
+const vector PM_STAND_MAX = VEC_HULL_MAX;
+const vector PM_CROUCH_MIN = '-16 -16 -18';
+const vector PM_CROUCH_MAX = '16 16 18';
+
+// pmove_flags is used by the engine; FTE defines two constants:
+// PMF_JUMP_HELD = 1, PMF_LADDER = 2. So those two constants should
+// to be first (and in that order) in our enum below. -- CEV
enumflags
{
+ PMF_JUMP_HELD, // player is holding the jump key
+ PMF_ONLADDER, // entity is on a ladder
PMF_ONGROUND, // entity is on ground
PMF_STARTGROUND, // entity started the move on ground
- PMF_ONLADDER, // entity is on a ladder
PMF_INWATER, // entity is in water
PMF_CROUCH_HELD, // player is holding the crouch key
PMF_CROUCHED, // entity is crouching
- PMF_JUMP_HELD, // player is holding the jump key
PMF_DOUBLEJUMPED, // entity has doublejumped
PMF_WALLJUMPED, // entity has walljumped
PMF_WATERJUMPED, // entity has waterjumped
PMF_PRE_MOVE // hint that origin hasn't updated yet
};

-// prototypes
+//======================================================================
+// forward declarations
+//======================================================================
+
static void(entity ent) PM_DoTouch;
float() PM_Nudge;
void(float dogravity, float sticky, float dostep, float doskim) PM_DanceMove;
+void(float dogravity, float sticky, float dostep, float doskim) PM_DanceMoveQ3;
void() PM_CategorizePosition;
void(float friction, float move_time) PM_Friction;
void(vector wishdir, float wishspeed, float accel, float move_time)
@@ -226,8 +230,8 @@ float() PM_Nudge =
// Updates origin according to velocity, moves the player through the world.
// Same as Q3 SlideMove and Q1 FlyMove. This version handles steps, applies
// gravity, and restores velocity based on a timer check (those latter two
-// are features of Q3's SlideMove). I've inlined a number of functions
-// (ClipVelocity, DoTouch) to hopefully improve performance.
+// are features of Q3's SlideMove). I've inlined ClipVelocity to hopefully
+// improve performance.
//
// Based on code from the Nuclide SDK (presumably by Eukara), specifically
// the function PMoveCustom_Move found in the file pmove_custom.qc. This
@@ -236,14 +240,14 @@ float() PM_Nudge =
//----------------------------------------------------------------------
void(float dogravity, float sticky, float dostep, float doskim) PM_DanceMove =
{
- vector end, prev_plane, prev_plane2, plane, start_v;
- float backoff, grav, stepsize, time_left;
- int i;
+ vector end, prev_plane, prev_plane2, start_vel;
+ float f, grav, i, stepsize, time_left;
entity touched_ent;

// silence some FTEQCC warnings -- CEV
- end = prev_plane = prev_plane2 = plane = start_v = '0 0 0';
- backoff = grav = stepsize = time_left = 0;
+ end = prev_plane = prev_plane2 = '0 0 0';
+ f = grav = i = stepsize = 0;
+ time_left = input_timelength;

if (dogravity)
{
@@ -253,13 +257,13 @@ void(float dogravity, float sticky, float dostep, float doskim) PM_DanceMove =
grav = 1.0;
// frame_gravity is set in StartFrame (on the server)
// or PlayerUpdate (client) -- CEV
- grav = grav * frame_gravity * input_timelength;
+ grav *= frame_gravity * time_left;
// Half now, half later. Apparently affects framerate
// dependence. -- CEV
self.velocity_z -= grav * 0.5;
}

- start_v = self.velocity;
+ start_vel = self.velocity;

// Borrowing FTEQW's pm_airstep cvar here. If false, don't step up
// while in the air (changes stairs) -- CEV
@@ -274,7 +278,7 @@ void(float dogravity, float sticky, float dostep, float doskim) PM_DanceMove =
// 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--)
+ for (i = 5; time_left > 0 && i; i--)
{
// set our destination & test the move -- CEV
end = self.origin + (self.velocity * time_left);
@@ -288,17 +292,12 @@ void(float dogravity, float sticky, float dostep, float doskim) PM_DanceMove =

// nah, we're stuck. don't build up falling damage
// but allow sideways acceleration -- CEV
- /*
- #ifdef CSQC
+ #if defined(CSQC)
dprint ("PM_DanceMove: client ");
- #endif
- #ifdef SSQC
+ #elif defined(SSQC)
dprint ("PM_DanceMove: server ");
#endif
- #if defined(CSQC) || defined(SSQC)
dprint ("entity trapped in a solid!\n");
- #endif
- */
self.velocity_z = 0;
break;
}
@@ -313,53 +312,54 @@ void(float dogravity, float sticky, float dostep, float doskim) PM_DanceMove =
// 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 from Nuclide / CSQCTest -- CEV
+ // integrated StepSlideMove from Nuclide / CSQCTest
// 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)
+ // requested, and we hit a vertical plane -- CEV
+ if (dostep && time_left > 0 && trace_plane_normal_z == 0)
{
+ // store the entity and plane normal from our
+ // first move attempt, setup a vec to track
+ // velocity changes -- CEV
+ local entity first_ent = trace_ent;
+ local vector first_plane = trace_plane_normal;
+ local vector new_vel = self.velocity;
+ f = time_left;
+
// first: move up
- trace_endpos = self.origin;
trace_endpos_z += PM_STEPHEIGHT;
tracebox (self.origin, self.mins, self.maxs,
trace_endpos, FALSE, self);

- // don't like having to store another float & vector
- // but this solves more interactions (ztndm3) -- CEV
- local float roof_fraction = trace_fraction;
- local vector roof_plane = trace_plane_normal;
+ if (trace_fraction < 1.0f)
+ {
+ // do a simple clipvel to the roof plane
+ // right now -- CEV
+ f -= f * trace_fraction;
+ new_vel -= trace_plane_normal *
+ (new_vel * trace_plane_normal);
+
+ if (trace_ent && trace_ent.touch)
+ touched_ent = trace_ent;
+ }

// second: move forward
stepsize = trace_endpos_z - self.origin_z;
- end = trace_endpos + (self.velocity * time_left);
+ end = trace_endpos + (new_vel * f);
end_z = trace_endpos_z;
tracebox (trace_endpos, self.mins, self.maxs, end,
FALSE, self);

- // save trace_ent if it has a touch function
- if (trace_ent && trace_ent.touch != __NULL__)
- touched_ent = trace_ent;
-
+ #ifdef SSQC
if (trace_allsolid || trace_startsolid)
- {
- // reject the move immediately -- CEV
- #ifdef SSQC
+ // caution
dprint ("PM_DanceMove: fwd move solid\n");
- #endif
- stepsize = 0;
- self.velocity = '0 0 0';
- break;
- }
- else if (trace_fraction >= 1.0f)
- {
- // storing another vector, disappointed in
- // myself -- CEV
- local vector fwd_plane = trace_plane_normal;
+ #endif

+ if (trace_fraction >= 1.0f)
+ {
// forward move didn't hit anything
// third: move down
end = trace_endpos;
@@ -369,41 +369,28 @@ void(float dogravity, float sticky, float dostep, float doskim) PM_DanceMove =

if (trace_allsolid || trace_startsolid)
{
- // again reject the move -- CEV
+ // reject the move
#ifdef SSQC
- dprint ("PM_DanceMove: down move "
- "solid\n");
+ dprint ("PM_DanceMove: down solid\n");
#endif
- stepsize = 0;
- self.velocity = '0 0 0';
- break;
+ goto PM_DanceMove_RejectStep;
}
else if (trace_fraction < 1.0f &&
trace_plane_normal_z > 0.7f)
{
- // good ground, accept the move
- if (roof_fraction < 1.0f &&
- roof_plane != fwd_plane &&
- roof_plane != plane)
- {
- // do a simple clipvel to the
- // roof plane right now -- CEV
- self.velocity -= roof_plane *
- (self.velocity *
- roof_plane);
- }
-
+ // Update time_left, stepsize, origin,
+ // velocity & touched_ent (if need);
+ // the later inline ClipVel will
+ // handle the plane -- CEV
+ time_left = f - (f * trace_fraction);
stepsize = trace_endpos_z -
self.origin_z;
self.origin = trace_endpos;
- time_left -= time_left * trace_fraction;

- prev_plane2 = __NULL__;
- prev_plane = fwd_plane;
- plane = trace_plane_normal;
+ if (new_vel != self.velocity)
+ self.velocity = new_vel;

- if (trace_ent &&
- trace_ent.touch != __NULL__)
+ if (trace_ent && trace_ent.touch)
touched_ent = trace_ent;

// don't attempt to step again
@@ -411,54 +398,43 @@ void(float dogravity, float sticky, float dostep, float doskim) PM_DanceMove =
}
else
{
- // down move didn't hit and/or
- // it hit a plane too steep to
- // be ground -- CEV
- stepsize = 0;
+ // reject the move
+ goto PM_DanceMove_RejectStep;
}
}
- else if (trace_plane_normal != plane)
+ else
{
- // raised forward move hit something different
- // than the initial move so clip velocity to
- // it. solves issues with weird ceilings and
- // complex movement on stairs. note that we're
- // not updating origin. -- CEV
- prev_plane2 = __NULL__;
- prev_plane = __NULL__;
- time_left -= time_left * trace_fraction;
- stepsize = 0;
+ // forward move hit something
+ if (trace_ent && trace_ent.touch)
+ touched_ent = trace_ent;

- if (roof_fraction < 1.0f &&
- roof_plane != trace_plane_normal)
- {
- // save the roof plane & fwd plane
- prev_plane = roof_plane;
- plane = trace_plane_normal;
- }
- else if (roof_plane == trace_plane_normal)
+ if (trace_plane_normal != first_plane &&
+ trace_plane_normal_z == 0)
{
- // roof plane is fwd plane, so save
- // first and fwd plane
- prev_plane = plane;
- plane = trace_plane_normal;
+ // hit something different than the
+ // first move; note that we're not
+ // restoring the first plane normal
+ time_left -= time_left * trace_fraction;
+ stepsize = 0;
}
else
{
- // only save fwd plane
- plane = trace_plane_normal;
+ // revert to move before step attempt
+ PM_DanceMove_RejectStep:
+ trace_ent = first_ent;
+ trace_plane_normal = first_plane;
+ stepsize = 0;
}
}
}

- // need to do the ground check & touch the saved ent before
- // we might stop the loop early -- CEV
- if (plane_z > 0.7)
+ // do the ground check before we might stop the loop -- CEV
+ if (trace_plane_normal_z > 0.7)
{
if (touched_ent.solid == SOLID_BSP)
{
self.groundentity = touched_ent;
- self.groundnormal = plane;
+ self.groundnormal = trace_plane_normal;
self.flags |= FL_ONGROUND;
self.pmove_flags |= PMF_ONGROUND;
}
@@ -471,64 +447,49 @@ void(float dogravity, float sticky, float dostep, float doskim) PM_DanceMove =
self.pmove_flags &= ~PMF_ONGROUND;
}

- // touch the saved entity; inline PM_DoTouch -- CEV
- if (touched_ent && touched_ent.touch != __NULL__)
- {
- PM_DoTouch (touched_ent);
- /*
- local entity orig_self;
-
- orig_self = self;
- other = self;
- self = touched_ent;
- self.touch ();
- self = orig_self;
- */
- }
-
- // clip velocity to the saved plane
- // inline PM_Rebound aka PM_ClipVelocity -- CEV
- backoff = self.velocity * plane;
+ // clip velocity to the plane -- CEV
+ f = self.velocity * trace_plane_normal;

- if (backoff < 0)
- backoff *= PM_OVERCLIP;
+ if (f < 0)
+ f *= PM_OVERCLIP;
else
if (sticky)
- backoff /= PM_OVERCLIP;
+ f /= PM_OVERCLIP;
else
- backoff *= PM_ONESIDEDCLIP;
+ f *= PM_ONESIDEDCLIP;

- self.velocity -= plane * backoff;
+ self.velocity -= trace_plane_normal * f;

// if velocity interacts with prev_plane then clip to it.
- // I'm duplicating a little code here (ClipVelocity),
+ // I'm duplicating a little code here (PM_ClipVelocity),
// hopefully the audience will forgive me. -- CEV
- if (prev_plane && prev_plane != plane &&
+ if (prev_plane && prev_plane != trace_plane_normal &&
self.velocity * prev_plane < 0)
{
- backoff = self.velocity * prev_plane;
+ f = self.velocity * prev_plane;

- if (backoff < 0)
- backoff *= PM_OVERCLIP;
+ if (f < 0)
+ f *= PM_OVERCLIP;
else
if (sticky)
- backoff /= PM_OVERCLIP;
+ f /= PM_OVERCLIP;
else
- backoff *= PM_ONESIDEDCLIP;
+ f *= PM_ONESIDEDCLIP;

- self.velocity -= prev_plane * backoff;
+ self.velocity -= prev_plane * f;

// Slide along the crease of previous plane and
// current plane if the two still interact -- CEV
- if (self.velocity * plane < 0)
+ if (self.velocity * trace_plane_normal < 0)
{
- end = crossproduct (prev_plane, plane);
+ end = crossproduct (prev_plane,
+ trace_plane_normal);
end = normalize (end);
self.velocity = end * (end * self.velocity);
}

// An optimization from Quake 3 -- CEV
- if (prev_plane2 && prev_plane2 != plane &&
+ if (prev_plane2 && prev_plane2 != trace_plane_normal &&
prev_plane2 != prev_plane &&
self.velocity * prev_plane2 < 0)
{
@@ -537,40 +498,44 @@ void(float dogravity, float sticky, float dostep, float doskim) PM_DanceMove =
dprint ("PM_DanceMove: triple plane stop\n");
#endif
self.velocity = '0 0 0';
+ if (touched_ent && touched_ent.touch)
+ PM_DoTouch (touched_ent);
+
break;
}
}

// an optimization from Quake 1; is this necessary? -- CEV
- if ((self.velocity * start_v) <= 0)
+ if (self.velocity * start_vel <= 0)
{
- // we've turned against original velocity so
- // clear vel and stop the loop
+ // we've turned against original velocity so zero
+ // out vel, touch any ents, and stop the loop
self.velocity = '0 0 0';
+ if (touched_ent && touched_ent.touch)
+ PM_DoTouch (touched_ent);
break;
}

// store current plane and prev_plane for later -- CEV
- if (prev_plane)
- prev_plane2 = prev_plane;
- prev_plane = plane;
+ prev_plane2 = prev_plane;
+ prev_plane = trace_plane_normal;
+
+ // touch the saved entity last in case doing so overwrites
+ // trace_plane_normal -- CEV
+ if (touched_ent && touched_ent.touch)
+ PM_DoTouch (touched_ent);
}

// wallclip / wall skim timer check
// if doskim is true and velocity is non-zero and we lost speed
// during the move then restore velocity -- CEV
- if (doskim && self.velocity && (vlen(start_v) > vlen(self.velocity)))
+ if (doskim && self.velocity && (vlen(start_vel) > vlen(self.velocity)))
{
- /*
- #ifdef SSQC
- dprint ("PM_DanceMove: wall skim, restoring velocity...\n");
- #endif
- */
- // 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];
+ if (sticky)
+ self.velocity = [start_vel_x, start_vel_y,
+ min (start_vel_z, self.velocity_z)];
else
- self.velocity = start_v;
+ self.velocity = start_vel;
}

// final gravity check here -- CEV
@@ -580,267 +545,605 @@ void(float dogravity, float sticky, float dostep, float doskim) PM_DanceMove =
// if stepsize is nonzero and we changed from inair to onground then
// we've airstepped. this check is here as a placeholder for possible
// functionality later -- CEV
- #ifdef SSQC
+ /*
if (stepsize > 0 && !(self.pmove_flags & PMF_STARTGROUND) &&
self.pmove_flags & PMF_ONGROUND)
{
// more debugging -- CEV
dprint (sprintf("PM_DanceMove: airstep: %g\n", stepsize));
}
- #endif
+ */

// a final call to setorigin to update links -- CEV
setorigin (self, self.origin);
-
- /*
- #ifdef SSQC
- if (cvar("developer"))
- // for debugging purposes -- CEV
- dprint (sprintf("PM_DanceMove: %g bumps\n", 5 - i));
- #endif
- */
};

//----------------------------------------------------------------------
-// PM_CategorizePosition
-// Based on similarly-named function in the GPL2 purecsqc pmove.qc
+// PM_DanceMoveQ3
+//
+// An alternate SlideMove based more closely on Q3's SlideMove. This
+// solves all interactions correctly by using an array of planes and a
+// lot of loops. Unfortunately it's slow and greatly affects the way
+// movement feels - heavier, slower - and sometimes seems to affect
+// framerate. Included here (in a working form) so I can experiment
+// with it. -- CEV
//----------------------------------------------------------------------
-void() PM_CategorizePosition =
+void(float dogravity, float sticky, float dostep, float doskim) PM_DanceMoveQ3 =
{
- vector point;
- float contents;
+ vector plane[PM_MAX_CLIP_PLANES], end, start_v, new_v;
+ float backoff, grav, h, i, j, k, np, stepsize, time_left;
+ entity touched_ent;

- if (self.movetype == MOVETYPE_NOCLIP || self.movetype == MOVETYPE_FLY)
+ // silence some FTEQCC warnings -- CEV
+ backoff = grav = h = i = j = k = np = stepsize = time_left = 0;
+
+ if (dogravity)
{
- // noclip is never on ground
- self.groundentity = __NULL__;
- self.groundnormal = __NULL__;
- self.pmove_flags &= ~PMF_DOUBLEJUMPED;
- self.pmove_flags &= ~PMF_WALLJUMPED;
- self.pmove_flags &= ~PMF_ONGROUND;
- self.flags &= ~FL_ONGROUND;
+ if (self.gravity)
+ grav = self.gravity;
+ else
+ grav = 1.0;
+ // frame_gravity is set in StartFrame (on the server)
+ // or PlayerUpdate (client) -- CEV
+ grav = grav * frame_gravity * input_timelength;
+ // Half now, half later. Apparently affects framerate
+ // dependence. -- CEV
+ self.velocity_z -= grav * 0.5;
}
- else
+
+ new_v = start_v = self.velocity;
+
+ if (!(frame_airstep) && !(self.pmove_flags & PMF_ONGROUND))
+ dostep = FALSE;
+
+ if (frame_nostep)
+ dostep = FALSE;
+
+ for (h = 5, time_left = input_timelength; time_left > 0 && h; h--)
{
- // TODO CEV there must be a way to optimize out this
- // tracebox ground check some of the time
+ // set our destination & test the move -- CEV
+ end = self.origin + (self.velocity * time_left);
+ tracebox (self.origin, self.mins, self.maxs, end, FALSE, self);

- // do a trace to check for ground
- tracebox (self.origin, self.mins, self.maxs,
- self.origin - PM_GROUNDDIST_V, FALSE, self);
- // only onground if we hit it & it faces upwards
- if (trace_fraction < 1.0f && trace_plane_normal_z > 0.7f)
- {
- // on ground
- self.groundentity = trace_ent;
- self.groundnormal = trace_plane_normal;
- self.flags |= FL_ONGROUND;
- self.pmove_flags |= PMF_ONGROUND;
- self.pmove_flags &= ~PMF_WALLJUMPED;
- }
- else
+ if (trace_allsolid || trace_startsolid)
{
- // not on ground
- self.groundentity = __NULL__;
- self.groundnormal = __NULL__;
- self.flags &= ~FL_ONGROUND;
- self.pmove_flags &= ~PMF_ONGROUND;
+ // entity is trapped in a solid; attempt to nudge out
+ if (PM_Nudge())
+ continue;
+
+ // nah, we're stuck. don't build up falling damage
+ // but allow sideways acceleration -- CEV
+ self.velocity_z = 0;
+ break;
}
- }

- // check water levels
- point = self.origin;
- point_z = self.origin_z + self.mins_z + 1;
- contents = pointcontents (point);
- if (contents < CONTENT_SOLID)
- {
- self.watertype = contents;
- point_z = self.origin_z + (self.mins_z + self.maxs_z) * 0.5;
- if (pointcontents(point) < CONTENT_SOLID)
+ // accept the move -- CEV
+ self.origin = trace_endpos;
+
+ if (trace_fraction >= 1.0f)
+ // no obstructions, made the whole move -- CEV
+ break;
+
+ if (np >= PM_MAX_CLIP_PLANES)
{
- point_z = self.origin_z + self.maxs_z;
- if (pointcontents(point) < CONTENT_SOLID)
- self.waterlevel = WATERLEVEL_EYES;
- else
- self.waterlevel = WATERLEVEL_WAIST;
+ dprint (sprintf("PM_DanceMoveQ3: no more plane storage,"
+ " numbumps: %g\n", fabs(h - 5)));
+ self.velocity = '0 0 0';
+ break;
}
- else
+
+ // duplicate plane check from Quake3 -- CEV
+ /*
+ for (i = 0; i < np; i++)
{
- self.waterlevel = WATERLEVEL_FEET;
+ if (trace_plane_normal * plane[i] > 0.99)
+ {
+ #ifdef SSQC
+ dprint ("PM_DanceMoveQ3: non-axial plane\n");
+ #endif
+ self.velocity += trace_plane_normal;
+ break;
+ }
}
- }
- else
- {
- self.watertype = CONTENT_EMPTY;
- self.waterlevel = WATERLEVEL_NONE;
- }

- // can't be waterjumping if we're on ground
- if (self.pmove_flags & PMF_WATERJUMPED &&
- (self.waterlevel == WATERLEVEL_NONE ||
- self.pmove_flags & PMF_ONGROUND))
- {
- #ifdef SSQC
- dprint ("PM_CategorizePosition: clearing FL_WATERJUMP\n");
- #endif
- self.pmove_flags &= ~PMF_WATERJUMPED;
- self.flags &= ~FL_WATERJUMP;
- }
-};
+ if (i < np)
+ continue;
+ */

-//----------------------------------------------------------------------
-// PM_Friction
-// Ground friction
-//----------------------------------------------------------------------
-void(float friction, float move_time) PM_Friction =
-{
- float control, newspeed, speed;
+ // zero out stepsize, then store the plane normal and touched
+ // ent for later, then reduce time_left -- CEV
+ stepsize = 0;
+ touched_ent = trace_ent;
+ plane[np] = trace_plane_normal;
+ np++;
+ time_left -= time_left * trace_fraction;

- speed = vlen (self.velocity);
+ // integrated StepSlideMove from Nuclide / CSQCTest -- CEV
+ if (dostep && time_left && trace_plane_normal_z == 0)
+ {
+ i = time_left;
+ new_v = self.velocity;

- if (speed < 1)
- {
- self.velocity = '0 0 0';
- return;
- }
+ // first: move up
+ trace_endpos_z += PM_STEPHEIGHT;
+ tracebox (self.origin, self.mins, self.maxs,
+ trace_endpos, FALSE, self);

- // calculate what their new speed should be
- control = speed < PM_STOPSPEED ? PM_STOPSPEED : speed;
- newspeed = speed - control * friction * move_time;
+ if (trace_fraction < 1.0f)
+ {
+ // do a simple clipvel to the
+ // roof plane right now -- CEV
+ i -= i * trace_fraction;
+ new_v -= trace_plane_normal *
+ (new_v * trace_plane_normal);
+ }

- // and slow them
- if (newspeed < 0)
- newspeed = 0;
+ // second: move forward
+ stepsize = trace_endpos_z - self.origin_z;
+ end = trace_endpos + (new_v * i);
+ end_z = trace_endpos_z;
+ tracebox (trace_endpos, self.mins, self.maxs, end,
+ FALSE, self);

- self.velocity = self.velocity * (newspeed / speed);
-};
+ // save trace_ent if it has a touch function
+ if (trace_ent && trace_ent.touch != __NULL__)
+ touched_ent = trace_ent;

-//----------------------------------------------------------------------
-// PM_Accelerate
-// Ground acceleration & Quake 3 style air acceleration -- CEV
-//----------------------------------------------------------------------
-void(vector wishdir, float wishspeed, float accel, float move_time)
- PM_Accelerate =
-{
- float newspeed, speed;
+ if (trace_allsolid || trace_startsolid)
+ {
+ // reject the move immediately -- CEV
+ #ifdef SSQC
+ dprint ("PM_DanceMoveQ3: fwd move solid\n");
+ #endif
+ stepsize = 0;
+ self.velocity = '0 0 0';
+ break;
+ }
+ else if (trace_fraction >= 1.0f)
+ {
+ // forward move didn't hit anything
+ // third: move down
+ end = trace_endpos;
+ end_z -= stepsize + 1;
+ tracebox (trace_endpos, self.mins, self.maxs,
+ end, FALSE, self);

- speed = wishspeed - (self.velocity * wishdir);
+ if (trace_allsolid || trace_startsolid)
+ {
+ // again reject the move -- CEV
+ #ifdef SSQC
+ dprint ("PM_DanceMoveQ3: down move "
+ "solid\n");
+ #endif
+ stepsize = 0;
+ self.velocity = '0 0 0';
+ break;
+ }
+ else if (trace_fraction < 1.0f &&
+ trace_plane_normal_z > 0.7f)
+ {
+ // we'll let our proper ClipVel loop
+ // below take care of this -- CEV
+ plane[np - 1] = trace_plane_normal;
+ time_left = i - (i * trace_fraction);
+ stepsize = trace_endpos_z -
+ self.origin_z;
+ self.origin = trace_endpos;

- if (speed <= 0)
- return;
+ if (new_v != self.velocity)
+ // hit a roof on the way
+ self.velocity = new_v;

- newspeed = accel * move_time * wishspeed;
- if (newspeed > speed)
- newspeed = speed;
+ touched_ent = trace_ent;

- self.velocity += newspeed * wishdir;
-};
+ // don't attempt to step again
+ dostep = FALSE;
+ }
+ else
+ {
+ // down move didn't hit and/or it hit
+ // a plane too steep to be ground.
+ // reset & continue on -- CEV
+ trace_plane_normal = plane[np - 1];
+ stepsize = 0;
+ }
+ }
+ else
+ {
+ // forward move hit something. reset
+ // and move on. -- CEV
+ trace_plane_normal = plane[np - 1];
+ stepsize = 0;
+ }
+ }

-//----------------------------------------------------------------------
-// PM_AirAccelerate
-// Quake 1 style air acceleration -- CEV
-//----------------------------------------------------------------------
-void(vector wishvel, float wishspeed, float accel, float move_time)
- PM_AirAccelerate =
-{
- float newspeed, speed, wishspd;
+ // need to do the ground check & touch the saved ent before
+ // we might stop the loop early -- CEV
+ if (trace_plane_normal_z > 0.7)
+ {
+ if (touched_ent.solid == SOLID_BSP)
+ {
+ self.groundentity = touched_ent;
+ self.groundnormal = trace_plane_normal;
+ 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;
+ }

- // need to ignore Z velocity -- CEV
- wishvel = [wishvel_x, wishvel_y, 0];
- wishspd = vlen (wishvel);
- wishvel = normalize (wishvel);
- if (wishspd > PM_MAXAIRSPEED)
- wishspd = PM_MAXAIRSPEED;
- speed = self.velocity * wishvel;
- speed = wishspd - speed;
+ // touch the saved entity -- CEV
+ if (touched_ent && touched_ent.touch != __NULL__)
+ {
+ PM_DoTouch (touched_ent);
+ }

- if (speed <= 0)
- return;
+ // Quake 3's plane interaction and velocity clipping scheme.
+ // Solves more interactions than Q1's FlyMove and Nuclide's
+ // Pmove_Custom. Probably the slowest of the three mentioned.
+ // -- CEV
+ for (i = 0; i < np; i++)
+ {
+ if (plane[i] != self.groundnormal)
+ if (self.velocity * plane[i] >= 0.1)
+ continue;

- newspeed = accel * move_time * wishspeed;
- if (newspeed > speed)
- newspeed = speed;
- self.velocity += newspeed * wishvel;
-};
+ // Inline ClipVelocity / PM_Rebound
+ backoff = self.velocity * plane[i];

-//----------------------------------------------------------------------
-// PM_AirControl
-// CPM / Painkiller-like Air Control. Based on Xonotic sources. -- CEV
-//----------------------------------------------------------------------
-void(vector wishdir, float wishspeed, float move_time) PM_AirControl =
-{
- local float speed, zspeed;
+ if (backoff < 0)
+ backoff *= PM_OVERCLIP;
+ else
+ if (sticky)
+ backoff /= PM_OVERCLIP;
+ else
+ backoff *= PM_ONESIDEDCLIP;

- zspeed = self.velocity_z;
- self.velocity_z = 0;
+ new_v = self.velocity - (plane[i] * backoff);

- speed = vlen (self.velocity);
- self.velocity = normalize (self.velocity);
+ // new_v -= plane[i] * (new_v * plane[i]);

- if ((self.velocity * wishdir) > 0)
+ for (j = 0; j < np; j++)
+ {
+ if (j == i)
+ continue;
+
+ if (plane[j] != self.groundnormal)
+ if (new_v * plane[j] >= 0.1)
+ continue;
+
+ // duplicating code here to avoid a function
+ // call -- CEV
+ backoff = new_v * plane[j];
+
+ if (backoff < 0)
+ backoff *= PM_OVERCLIP;
+ else
+ if (sticky)
+ backoff /= PM_OVERCLIP;
+ else
+ backoff *= PM_ONESIDEDCLIP;
+
+ new_v -= plane[j] * backoff;
+
+ // new_v -= plane[j] * (new_v * plane[j]);
+
+ // see if we still interact with the first
+ // clip plane (an optimization from Q3)
+ if (new_v * plane[i] >= 0)
+ continue;
+
+ if (sticky)
+ continue;
+
+ // slide along the crease
+ end = crossproduct (plane[i], plane[j]);
+ end = normalize (end);
+ new_v = end * (end * new_v);
+
+ // see if the move enters a third plane
+ for (k = 0; k < np; k++)
+ {
+ if (k == i || k == j)
+ continue;
+
+ if (new_v * plane[k] >= 0.1)
+ // no interaction
+ continue;
+
+ #ifdef SSQC
+ dprint ("PM_DanceMoveQ3: 3x stop\n");
+ #endif
+ self.velocity = '0 0 0';
+
+ // break out of all the loops now
+ // by skipping directly to the end
+ goto dancemove_end;
+ }
+ }
+
+ // try another move
+ self.velocity = new_v;
+ break;
+ }
+
+ // 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;
+ }
+ }
+
+ dancemove_end:
+
+ // wallclip / wall skim timer check
+ // if doskim is true and velocity is non-zero and we lost speed
+ // during the move then restore velocity -- CEV
+ if (doskim && self.velocity && (vlen(start_v) > vlen(self.velocity)))
{
- self.velocity = normalize (self.velocity * speed +
- wishdir * PM_AIRACCELTURN);
+ if (sticky)
+ self.velocity = [start_v_x, start_v_y,
+ min (start_v_z, self.velocity_z)];
+ else
+ self.velocity = start_v;
}

- self.velocity *= speed;
- self.velocity_z = zspeed;
+ // final gravity check here -- CEV
+ if (dogravity)
+ self.velocity_z -= grav * 0.5;
+
+ // a final call to setorigin to update links -- CEV
+ setorigin (self, self.origin);
};

//----------------------------------------------------------------------
-// PM_Jump
+// PM_CategorizePosition
+// Based on similarly-named function in the GPL2 purecsqc pmove.qc
//----------------------------------------------------------------------
-void() PM_Jump =
+void() PM_CategorizePosition =
{
- // are we already waterjumping, or is jump being held?
- if (self.pmove_flags & PMF_WATERJUMPED ||
- self.pmove_flags & PMF_JUMP_HELD)
- return;
-
- #ifdef SSQC
- local string msg = "";
- #endif
+ vector point;
+ float contents;

- // make sure we get at least jumpspeed upwards from
- // the ground plane by clamping it first.
- if (self.groundnormal && (self.velocity * self.groundnormal < 0))
+ if (self.movetype == MOVETYPE_NOCLIP || self.movetype == MOVETYPE_FLY)
{
- // #ifdef SSQC
- // dprint("PM_Jump: clamping to ground\n");
- // #endif
- self.velocity -= self.groundnormal *
- (self.velocity * self.groundnormal);
+ // noclip is never on ground
+ self.groundentity = __NULL__;
+ self.groundnormal = __NULL__;
+ self.pmove_flags &= ~PMF_DOUBLEJUMPED;
+ self.pmove_flags &= ~PMF_WALLJUMPED;
+ self.pmove_flags &= ~PMF_ONGROUND;
+ self.flags &= ~FL_ONGROUND;
}
-
- // make sure we get at least the indicated jump speed
- // this changes the behavior of downward ramps -- CEV
- if (self.velocity_z < 0)
- self.velocity_z = 0;
-
- if (self.doublejump_timer > 0)
+ else
{
- // 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
- msg = sprintf ("PM_Jump: telejump %g, ",
- self.velocity_z);
- #endif
- // non-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)
+ // TODO CEV there must be a way to optimize out this
+ // tracebox ground check some of the time
+
+ // do a trace to check for ground
+ tracebox (self.origin, self.mins, self.maxs,
+ self.origin - PM_GROUNDDIST_V, FALSE, self);
+ // only onground if we hit it & it faces upwards
+ if (trace_fraction < 1.0f && trace_plane_normal_z > 0.7f)
{
- #ifdef SSQC
- msg = sprintf ("PM_Jump: stairjump %g, ",
- self.velocity_z);
- #endif
- // don't do additive stairjumps on flat ground -- CEV
- self.velocity_z = PM_STAIRJUMPSPEED;
+ // on ground
+ self.groundentity = trace_ent;
+ self.groundnormal = trace_plane_normal;
+ self.flags |= FL_ONGROUND;
+ self.pmove_flags |= PMF_ONGROUND;
+ self.pmove_flags &= ~PMF_WALLJUMPED;
+ }
+ else
+ {
+ // not on ground
+ self.groundentity = __NULL__;
+ self.groundnormal = __NULL__;
+ self.flags &= ~FL_ONGROUND;
+ self.pmove_flags &= ~PMF_ONGROUND;
+ }
+ }
+
+ // check water levels
+ point = self.origin;
+ point_z = self.origin_z + self.mins_z + 1;
+ contents = pointcontents (point);
+ if (contents < CONTENT_SOLID)
+ {
+ self.watertype = contents;
+ point_z = self.origin_z + (self.mins_z + self.maxs_z) * 0.5;
+ if (pointcontents(point) < CONTENT_SOLID)
+ {
+ point_z = self.origin_z + self.maxs_z;
+ if (pointcontents(point) < CONTENT_SOLID)
+ self.waterlevel = WATERLEVEL_EYES;
+ else
+ self.waterlevel = WATERLEVEL_WAIST;
+ }
+ else
+ {
+ self.waterlevel = WATERLEVEL_FEET;
+ }
+ }
+ else
+ {
+ self.watertype = CONTENT_EMPTY;
+ self.waterlevel = WATERLEVEL_NONE;
+ }
+
+ // can't be waterjumping if we're on ground
+ if (self.pmove_flags & PMF_WATERJUMPED &&
+ (self.waterlevel == WATERLEVEL_NONE ||
+ self.pmove_flags & PMF_ONGROUND))
+ {
+ #ifdef SSQC
+ dprint ("PM_CategorizePosition: clearing FL_WATERJUMP\n");
+ #endif
+ self.pmove_flags &= ~PMF_WATERJUMPED;
+ self.flags &= ~FL_WATERJUMP;
+ }
+};
+
+//----------------------------------------------------------------------
+// PM_Friction
+// Ground friction
+//----------------------------------------------------------------------
+void(float friction, float move_time) PM_Friction =
+{
+ float control, newspeed, speed;
+
+ speed = vlen (self.velocity);
+
+ if (speed < 1)
+ {
+ self.velocity = '0 0 0';
+ return;
+ }
+
+ // calculate what their new speed should be
+ control = speed < PM_STOPSPEED ? PM_STOPSPEED : speed;
+ newspeed = speed - control * friction * move_time;
+
+ // and slow them
+ if (newspeed < 0)
+ newspeed = 0;
+
+ self.velocity = self.velocity * (newspeed / speed);
+};
+
+//----------------------------------------------------------------------
+// PM_Accelerate
+// Ground acceleration & Quake 3 style air acceleration -- CEV
+//----------------------------------------------------------------------
+void(vector wishdir, float wishspeed, float accel, float move_time)
+ PM_Accelerate =
+{
+ float newspeed, speed;
+
+ speed = wishspeed - (self.velocity * wishdir);
+
+ if (speed <= 0)
+ return;
+
+ newspeed = accel * move_time * wishspeed;
+ if (newspeed > speed)
+ newspeed = speed;
+
+ self.velocity += newspeed * wishdir;
+};
+
+//----------------------------------------------------------------------
+// PM_AirAccelerate
+// Quake 1 style air acceleration -- CEV
+//----------------------------------------------------------------------
+void(vector wishvel, float wishspeed, float accel, float move_time)
+ PM_AirAccelerate =
+{
+ 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)
+ wishspd = PM_MAXAIRSPEED;
+ speed = self.velocity * wishvel;
+ speed = wishspd - speed;
+
+ if (speed <= 0)
+ return;
+
+ newspeed = accel * move_time * wishspeed;
+ if (newspeed > speed)
+ newspeed = speed;
+ self.velocity += newspeed * wishvel;
+};
+
+//----------------------------------------------------------------------
+// PM_AirControl
+// CPM / Painkiller-like Air Control. Based on Xonotic sources. -- CEV
+//----------------------------------------------------------------------
+void(vector wishdir, float wishspeed, float move_time) PM_AirControl =
+{
+ local float speed, zspeed;
+
+ zspeed = self.velocity_z;
+ self.velocity_z = 0;
+
+ speed = vlen (self.velocity);
+ self.velocity = normalize (self.velocity);
+
+ if ((self.velocity * wishdir) > 0)
+ {
+ self.velocity = normalize (self.velocity * speed +
+ wishdir * PM_AIRACCELTURN);
+ }
+
+ self.velocity *= speed;
+ self.velocity_z = zspeed;
+};
+
+//----------------------------------------------------------------------
+// PM_Jump
+//----------------------------------------------------------------------
+void() PM_Jump =
+{
+ // are we already waterjumping, or is jump being held?
+ if (self.pmove_flags & PMF_WATERJUMPED ||
+ self.pmove_flags & PMF_JUMP_HELD)
+ return;
+
+ #ifdef SSQC
+ local string msg = "";
+ #endif
+
+ // make sure we get at least jumpspeed upwards from
+ // the ground plane by clamping it first.
+ if (self.groundnormal && (self.velocity * self.groundnormal < 0))
+ {
+ // #ifdef SSQC
+ // dprint("PM_Jump: clamping to ground\n");
+ // #endif
+ self.velocity -= self.groundnormal *
+ (self.velocity * self.groundnormal);
+ }
+
+ // make sure we get at least the indicated jump speed
+ // this changes the behavior of downward ramps -- CEV
+ if (self.velocity_z < 0)
+ self.velocity_z = 0;
+
+ if (self.doublejump_timer > 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
+ msg = sprintf ("PM_Jump: telejump %g, ",
+ self.velocity_z);
+ #endif
+ // non-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
+ msg = sprintf ("PM_Jump: stairjump %g, ",
+ self.velocity_z);
+ #endif
+ // don't do additive stairjumps on flat ground -- CEV
+ self.velocity_z = PM_STAIRJUMPSPEED;
}
else
{
@@ -866,7 +1169,9 @@ void() PM_Jump =

// do an additive jump on non-flat ground -- CEV
if (self.groundnormal_z == 1)
- self.velocity_z = PM_JUMPSPEED;
+ self.velocity_z = min (PM_STAIRJUMPSPEED,
+ self.velocity_z += PM_JUMPSPEED);
+ // self.velocity_z = PM_JUMPSPEED;
else
self.velocity_z += PM_JUMPSPEED;
}
@@ -916,1305 +1221,706 @@ void() PM_WallJump =

// mandatory interval between jump and walljump
if (self.doublejump_timer > (PM_DOUBLEJUMP_WINDOW - PM_WALLJUMP_WINDOW))
- {
- /*
- #ifdef SSQC
- dprint ("PM_WallJump: returning, interval\n");
- #endif
- */
return;
- }

if (self.velocity_z < PM_WALLJUMPLIMIT)
{
- // falling too fast to walljump
- // TODO CEV it would be a good idea to include a sound here.
- /*
- #ifdef SSQC
- dprint ("PM_WallJump: falling too fast, returning\n");
- #endif
- */
- return;
- }
-
- // minimum distance from ground to walljump
- local vector end = self.origin;
- end_z -= PM_WALLJUMPGROUND;
- tracebox (self.origin, self.mins, self.maxs, end, TRUE, self);
-
- if (trace_fraction < 1.0f)
- {
- /*
- #ifdef SSQC
- dprint ("PM_WallJump: too close to ground to walljump\n");
- #endif
- */
- return;
- }
-
- // start at STEPHEIGHT + 1 to try and clear any steps -- CEV
- local vector start;
- start = self.origin;
- start_z += PM_STEPHEIGHT + 1;
- end = start;
-
- for (int i = 0; i < 4; i++)
- {
- // I've borrowed a little logic from Quake Champions Classic
- // here, shoutout to RhapsodyInGeek, check out their good work
- // https://github.com/RhapsodyInGeek/Quake-Champions-Classic
- // v_forward and v_right should still correspond to the
- // makevectors call from PM_Move -- CEV
- if (i < 2)
- if (input_movevalues_x == 0)
- continue;
- else
- end = v_forward * 10;
- if (i > 1)
- if (input_movevalues_y == 0)
- continue;
- else
- end = v_right * 10;
- if (i == 1 || i == 3)
- end = end - end * 2;
-
- end = start + end;
-
- // this ends up firing *a lot* when hopping through a map
- // normally, unfortunately. traceline may be better here -- CEV
- tracebox (start, self.mins, self.maxs, end, TRUE, self);
-
- // in order: we hit something, that something is vaguely
- // vertical, we hit it at a speed greater than -90, and
- // that something is within 20 (units?) -- CEV
- if (trace_fraction < 1.0f &&
- trace_plane_normal_z <= 0.7f &&
- trace_plane_normal_z >= 0 &&
- self.velocity * trace_plane_normal >= -90 &&
- vlen(self.origin - trace_endpos) <= 20)
- {
- self.pmove_flags |= PMF_WALLJUMPED;
- break;
- }
- }
-
- if (self.pmove_flags & PMF_WALLJUMPED)
- {
- // bounce off the wall plane -- CEV
- self.velocity_x += trace_plane_normal_x * PM_WALLJUMPFORCE;
- self.velocity_y += trace_plane_normal_y * PM_WALLJUMPFORCE;
-
- // zero out negative Z, quarter positive Z -- CEV
- if (self.velocity_z < 0)
- self.velocity_z = 0;
- else if (self.velocity_z > 0)
- self.velocity_z *= 0.25;
-
- if (self.doublejump_timer > 0)
- {
- #ifdef SSQC
- dprint ("PM_WallJump: wall double ");
- #endif
- self.velocity_z += PM_WALLJUMPDOUBLE;
- }
- else
- {
- #ifdef SSQC
- dprint ("PM_WallJump: walljump ");
- #endif
- self.velocity_z += PM_WALLJUMPSPEED;
- }
-
- // manage flags & fields; PMF_WALLJUMPED is already set -- CEV
- self.pmove_flags |= PMF_JUMP_HELD;
- self.doublejump_timer = PM_DOUBLEJUMP_WINDOW;
-
- // server-side stuff
- #ifdef SSQC
- dprint (sprintf("dist %g, normalspeed %g\n",
- vlen(self.origin - trace_endpos),
- self.velocity * trace_plane_normal));
- self.button2 = 0;
- sound (self, CHAN_BODY, "player/plyrjmp8.wav", 1, ATTN_NORM);
- #endif
- }
-};
-
-//----------------------------------------------------------------------
-// PM_WalkAccelerate
-// Handle accel & friction for MOVETYPE_WALK entities (players). -- CEV
-//----------------------------------------------------------------------
-void(vector wishvel, float move_time) PM_WalkAccelerate =
-{
- vector wishdir;
- float accel, friction, newspeed, speed, temp, wishspeed;
-
- accel = friction = temp = 0;
-
- wishspeed = vlen (wishvel);
- wishdir = normalize (wishvel);
-
- if (self.pmove_flags & PMF_PRE_MOVE)
- {
- if (self.pmove_flags & PMF_ONLADDER)
- {
- // ladder physics
- // these currently stutter in high ping (or low netfps)
- // situations because we rely on touch()ing the ladder
- // trigger to set PMF_ONLADDER and the client doesn't
- // know about the trigger brush yet -- TODO CEV
- self.velocity = 0.75 * self.velocity;
-
- if (input_buttons & 2 || self.button2 > 0 ||
- input_movevalues_z > 0)
- {
- // PlayerClimb -- johnfitz
- self.velocity_z = 160;
- }
- else if (input_movevalues_z < 0)
- {
- // PlayerClimbDown -- CEV
- self.velocity_z = -160;
- }
- else
- {
- self.flags |= FL_JUMPRELEASED;
- self.pmove_flags &= ~PMF_JUMP_HELD;
- self.velocity_z = 0;
- }
-
- self.pmove_flags &= ~PMF_ONGROUND;
- }
- else
- {
- if (input_buttons & 2 || self.button2 > 0 ||
- input_movevalues_z > 0)
- {
- // +jump was pressed
- if (self.pmove_flags & PMF_ONGROUND)
- {
- // normal jump
- PM_Jump ();
- }
- // TODO CEV
- /*
- else if (autocvar(pm_walljump, FALSE))
- */
- else
- {
- // borrow FTE's pm_walljump cvar -- CEV
- PM_WallJump ();
- }
- }
- else
- {
- self.flags |= FL_JUMPRELEASED;
- }
- }
-
- if (self.pmove_flags & PMF_ONGROUND)
- self.pmove_flags |= PMF_STARTGROUND;
- else
- self.pmove_flags &= ~PMF_STARTGROUND;
- }
-
- // Test STARTGROUND (the ONGROUND status post jump) to avoid
- // a ground friction frame when chaining jumps together.
- // Probably has unintended consequences. -- CEV
- if (self.pmove_flags & PMF_STARTGROUND)
- {
- // we're on ground so pick accel and friction values -- CEV
- accel = PM_GROUNDACCEL;
- friction = PM_GROUNDFRICTION;
-
- if (self.groundboost_timer > 0)
- {
- /*
- accel = PM_BOOSTACCEL;
- friction = PM_BOOSTFRICTION;
- */
- if (wishspeed > PM_BOOSTWISHSPEED)
- wishspeed = PM_BOOSTWISHSPEED;
- /*
- // 320 + (-1 * ([0.x...0.4] - 0.4) * 250)
- temp = PM_MAXSPEED +
- (-1 * (self.groundboost_timer - 0.4) * 250);
- if (wishspeed > temp)
- wishspeed = temp;
-
- #ifdef SSQC
- dprint (sprintf("PM_WalkAccelerate: temp %f\n", temp));
- #endif
- */
- }
- else
- {
- /*
- accel = PM_GROUNDACCEL;
- friction = PM_GROUNDFRICTION;
- */
- if (wishspeed > PM_MAXSPEED)
- wishspeed = PM_MAXSPEED;
- }
- }
- else if (input_movevalues_x == 0)
- {
- // Q1 air accel when requesting sideways movement in the air
- // flag a check below with this -1 -- CEV
- if (wishspeed > PM_MAXSPEED)
- wishspeed = PM_MAXSPEED;
-
- accel = -1;
- }
- else
- {
- if (wishspeed > PM_MAXSPEED)
- wishspeed = PM_MAXSPEED;
-
- if (input_movevalues_y == 0)
- {
- // Painkiller style air control
- if (wishvel * self.velocity < 0)
- // against original velocity; we're slowing down
- accel = PM_AIRACCELBACK;
- else
- // we're with original velocity - gaining speed
- accel = PM_AIRACCELFWD;
-
- // borrow the friction var to flag a conditional below
- friction = -1;
- }
- else
- {
- // we're in the air and the player is requesting both
- // forward/back and sideways movement, so pick the Q3
- // air accel value -- CEV
- accel = PM_AIRACCELQ3;
- }
- }
-
- // I've duplicated a fair amount of code below, inlining the various
- // acceleration functions to hopefully improve performance -- CEV
-
- // apply friction (ground or otherwise)
- if (friction > 0)
- {
- // inline PM_Friction
- speed = vlen (self.velocity);
-
- if (speed < 1)
- {
- self.velocity = '0 0 0';
- }
- else
- {
- // calculate what their new speed should be
- temp = speed < PM_STOPSPEED ? PM_STOPSPEED : speed;
- newspeed = speed - temp * friction * move_time;
-
- // and slow them
- if (newspeed < 0)
- newspeed = 0;
-
- self.velocity = self.velocity * (newspeed / speed);
- }
- }
-
- if (accel == -1)
- {
- // Quake 1 sideways air acceleration; inline PM_AirAccelerate
- wishvel_z = 0;
- temp = vlen (wishvel);
- wishvel = normalize (wishvel);
- if (temp > PM_MAXAIRSPEED)
- temp = PM_MAXAIRSPEED;
- speed = self.velocity * wishvel;
- speed = temp - speed;
-
- if (speed > 0)
- {
- newspeed = PM_AIRACCEL * move_time * wishspeed;
- if (newspeed > speed)
- newspeed = speed;
- self.velocity += newspeed * wishvel;
- }
- }
- else if (accel > 0)
- {
- // ground & Quake 3 style acceleration; inline PM_Accelerate
- speed = wishspeed - (self.velocity * wishdir);
-
- if (speed > 0)
- {
- newspeed = accel * move_time * wishspeed;
- if (newspeed > speed)
- newspeed = speed;
-
- self.velocity += newspeed * wishdir;
- }
- }
-
- // CPM & PK-style air control, needs to happen after PM_Accelerate
- if (friction == -1)
- {
- // inline PM_AirControl
- temp = self.velocity_z;
- self.velocity_z = 0;
-
- speed = vlen (self.velocity);
- self.velocity = normalize (self.velocity);
-
- if ((self.velocity * wishdir) > 0)
- {
- self.velocity = normalize (self.velocity * speed +
- wishdir * PM_AIRACCELTURN);
- }
-
- self.velocity *= speed;
- self.velocity_z = temp;
- }
-
- if (!(self.pmove_flags & PMF_PRE_MOVE))
- self.pmove_flags &= ~PMF_STARTGROUND;
-};
-
-//----------------------------------------------------------------------
-// PM_SwimAccelerate
-// Largely copied from id Software's WaterMove. Works like NoClipAccelerate.
-//----------------------------------------------------------------------
-void(vector wishvel, float move_time) PM_SwimAccelerate =
-{
- float wishspeed;
-
- if (self.pmove_flags & PMF_PRE_MOVE)
- {
- // CheckWaterJump
- if (self.waterlevel == WATERLEVEL_WAIST)
- {
- local vector start, end;
-
- 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 (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_WATERJUMPED;
- self.flags &= ~FL_JUMPRELEASED;
- self.pmove_flags &= ~PMF_JUMP_HELD;
- // Z velocity was 225
- self.velocity_z = PM_JUMPSPEED;
- // we don't care about the 'safety net'
- // in this movement set (unlike vanilla)
- // so don't set teleport_time -- CEV
- // self.teleport_time = time + 2;
- }
- }
- }
-
- #ifdef SSQC
- // functionality copied into pmove from client.qc -- CEV
- if (self.waterlevel >= WATERLEVEL_WAIST)
- {
- if (self.swim_flag < time)
- {
- // play swimming sound
- self.swim_flag = time + 1;
- if (random() < 0.5)
- sound (self, CHAN_BODY,
- "misc/water1.wav", 1,ATTN_NORM);
- else
- sound (self, CHAN_BODY,
- "misc/water2.wav", 1,ATTN_NORM);
- }
- }
- #endif
- }
-
- if (input_buttons & 2 && !(self.pmove_flags & PMF_WATERJUMPED))
- // smartjump
- wishvel_z = max (PM_MAXSPEED, wishvel_z);
- else if (input_movevalues == '0 0 0')
- // drift towards bottom -- CEV
- wishvel_z -= PM_WATERSINKSPEED;
-
- wishspeed = vlen (wishvel);
- if (wishspeed > PM_WATERMAXSPEED)
- wishspeed = PM_WATERMAXSPEED;
- wishvel = normalize (wishvel);
-
- // water friction; reuse PM_Friction with a special constant -- CEV
- if (!(self.pmove_flags & PMF_WATERJUMPED))
- PM_Friction (PM_WATERFRICTION, move_time);
-
- // accelerate; reuse PM_Accelerate with a special constant -- CEV
- PM_Accelerate (wishvel, wishspeed, PM_WATERACCEL, move_time);
-};
-
-//----------------------------------------------------------------------
-// PM_NoClipAccelerate
-//
-// Basic acceleration for flying / noclipping players. Not suitable for
-// swimming. -- CEV
-//----------------------------------------------------------------------
-void(vector wishvel, float scale, float move_time) PM_NoClipAccelerate =
-{
- float wishspeed;
-
- // smartjump
- if (input_buttons & 2)
- wishvel_z = max (PM_MAXSPEED, wishvel_z);
-
- wishspeed = vlen (wishvel) * scale;
- wishvel = normalize (wishvel);
-
- 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;
-
- if (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;
-};
-
-//----------------------------------------------------------------------
-// PM_Move -- PMOVE entrypoint -- CEV
-//----------------------------------------------------------------------
-void(entity target) PM_Move =
-{
- if (input_timelength <= 0)
- // don't process partial frames -- CEV
- return;
-
- vector wishvel;
- entity oldself;
-
- oldself = self;
- self = target;
-
- // Nudge player's origin if the server is sending low-precision
- // (16 bit?) player coordinates. This can be fixed by setting
- // sv_bigcoords to 1 (so the server sends float coords). -- CEV
- if (!frame_bigcoords)
- PM_Nudge ();
-
- // rework user input, copying something like QuakeSpasm's always run
- // feature here. This turns the +speed button into a hold-to-walk
- // button when cl_run is 0 (when FTE's always run setting is on).
- // -- CEV
- if (frame_clrun == FALSE)
- {
- // local float s = 0;
- for (int i = 0; i < 3; i++)
- {
- // this works, it's just commented out to stop calling
- // autocvar quite so many times. to re-enable change
- // the check against "PM_RUNSPEED" below to "s" -- CEV
- /*
- if (i == 0)
- s = autocvar (cl_forwardspeed, PM_RUNSPEED);
- else if (i == 1)
- s = autocvar (cl_sidespeed, PM_RUNSPEED);
- else if (i == 2)
- s = autocvar (cl_upspeed, PM_RUNSPEED);
- */
- if (fabs(input_movevalues[i]) > 400.0f)
- input_movevalues[i] = PM_WALKSPEED *
- (input_movevalues[i] /
- fabs(input_movevalues[i]));
- else if (fabs(input_movevalues[i]) == 400.0f)
- input_movevalues[i] = PM_RUNSPEED *
- (input_movevalues[i] /
- fabs(input_movevalues[i]));
- // Don't need this, it's already PM_RUNSPEED -- CEV
- /*
- else if (input_movevalues[i] != 0)
- input_movevalues[i] = PM_RUNSPEED *
- (input_movevalues[i] /
- fabs(input_movevalues[i]));
- */
- }
- }
-
- // TODO CEV this is the wrong place to clear PMF_JUMP_HELD
- if (!(input_buttons & 2))
- self.pmove_flags &= ~PMF_JUMP_HELD;
-
- // figure out the properties of the player's position, then clear timers
- PM_CategorizePosition ();
- PM_ManageTimers (0);
-
- switch (self.movetype)
- {
- case MOVETYPE_WALK:
- local float dogravity, doskim, sticky;
-
- doskim = FALSE;
- sticky = FALSE;
-
- // signal acceleration functions that we're pre-move
- self.pmove_flags |= PMF_PRE_MOVE;
-
- // split the acceleration in two to reduce
- // framerate dependence. -- CEV
- if (self.waterlevel >= WATERLEVEL_WAIST)
- {
- // no gravity, velocity-restoring behavior,
- // or sticky clips underwater -- CEV
- dogravity = FALSE;
- doskim = FALSE;
- sticky = FALSE;
-
- // figure out wishvel -- CEV
- makevectors (input_angles);
- wishvel = v_forward * input_movevalues_x;
- wishvel += v_right * input_movevalues_y;
- wishvel += v_up * input_movevalues_z;
-
- // swim acceleration
- PM_SwimAccelerate (wishvel,
- input_timelength * 0.5f);
- }
- else
- {
- // only yaw matters here -- CEV
- makevectors ([0, input_angles_y, 0]);
- wishvel = v_forward * input_movevalues_x;
- wishvel += v_right * input_movevalues_y;
- // don't need wishvel_z -- CEV
- // wishvel += v_up * input_movevalues_z;
-
- PM_WalkAccelerate (wishvel,
- input_timelength * 0.5f);
-
- // WalkAccelerate calls Jump which might
- // alter pmove_flags, so do our feature
- // checks afterwards -- CEV
- if (self.doublejump_timer <= 0 ||
- self.pmove_flags & PMF_DOUBLEJUMPED ||
- self.pmove_flags & PMF_WALLJUMPED)
- // don't stick to the floor
- sticky = FALSE;
- else
- // we've jumped & haven't doublejumped,
- // so do sticky steps -- CEV
- sticky = TRUE;
-
- // apply gravity only when in the air or on
- // a ramp -- CEV
- if (self.groundnormal &&
- self.groundnormal_z < 1.0f)
- dogravity = TRUE;
- else if (self.pmove_flags & PMF_ONGROUND)
- dogravity = FALSE;
- else
- dogravity = TRUE;
-
- // skim / wallclipping checks -- CEV
- if (self.doublejump_timer >
- (PM_DOUBLEJUMP_WINDOW -
- PM_WALLCLIP_WINDOW) &&
- !(self.pmove_flags & PMF_WALLJUMPED) &&
- self.teleport_time <= (time - 0.1))
- doskim = TRUE;
-
- if (self.pmove_flags & PMF_ONLADDER)
- {
- // feature set for ladders -- CEV
- dogravity = FALSE;
- doskim = FALSE;
- sticky = FALSE;
- }
- }
+ // falling too fast to walljump
+ // TODO CEV it would be a good idea to include a sound here.
+ /*
+ #ifdef SSQC
+ dprint ("PM_WallJump: falling too fast, returning\n");
+ #endif
+ */
+ return;
+ }

- // timers (part 1) -- CEV
- PM_ManageTimers (input_timelength * 0.5f);
+ // minimum distance from ground to walljump
+ local vector end = self.origin;
+ end_z -= PM_WALLJUMPGROUND;
+ tracebox (self.origin, self.mins, self.maxs, end, TRUE, self);

- // Do the move. Bounce, Rock, Skate, Roll -- CEV
- PM_DanceMove (dogravity, sticky, TRUE, doskim);
+ if (trace_fraction < 1.0f)
+ return;

- // signal accel functions we're post-move
- self.pmove_flags &= ~PMF_PRE_MOVE;
+ // start at STEPHEIGHT + 1 to try and clear any steps -- CEV
+ local vector start;
+ local float f = 0;
+ start = self.origin;
+ start_z += PM_STEPHEIGHT + 1;
+ end = start;

- // second pass at acceleration
- if (self.waterlevel >= WATERLEVEL_WAIST)
- PM_SwimAccelerate (wishvel,
- input_timelength * 0.5f);
+ for (float i = 0; i < 4; i++)
+ {
+ // I've borrowed a little logic from Quake Champions Classic
+ // here, shoutout to RhapsodyInGeek, check out their good work
+ // https://github.com/RhapsodyInGeek/Quake-Champions-Classic
+ // v_forward and v_right should still correspond to the
+ // makevectors call from PM_Move -- CEV
+ if (i < 2)
+ if (input_movevalues_x == 0)
+ continue;
else
- PM_WalkAccelerate (wishvel,
- input_timelength * 0.5f);
+ end = v_forward * 10;
+ if (i > 1)
+ if (input_movevalues_y == 0)
+ continue;
+ else
+ end = v_right * 10;
+ if (i == 1 || i == 3)
+ end = end - end * 2;

- // timers (part 2) -- CEV
- PM_ManageTimers (input_timelength * 0.5f);
+ end = start + end;

- // clear ladder flag -- CEV
- if (self.pmove_flags & PMF_ONLADDER)
- self.pmove_flags &= ~PMF_ONLADDER;
- break;
+ // this ends up firing *a lot* when hopping through a map
+ // normally, unfortunately. traceline may be better here -- CEV
+ tracebox (start, self.mins, self.maxs, end, TRUE, self);

- case MOVETYPE_FLY:
- // split acceleration in two to reduce framerate
- // dependence. -- CEV
- makevectors (input_angles);
- wishvel = v_forward * input_movevalues_x;
- wishvel += v_right * input_movevalues_y;
- wishvel += v_up * input_movevalues_z;
+ // in order: we hit something, that something is vaguely
+ // vertical, we hit it at a speed greater than 0, and
+ // that something is within 20 (units?) -- CEV
+ // If self.velocity * trace_plane_normal is less than zero
+ // that means the user is directing their movement *into*
+ // the wall instead of away from it. Could be useful later
+ // for wallclimbing. -- CEV
+ if (trace_fraction < 1.0f &&
+ trace_plane_normal_z <= 0.7f &&
+ trace_plane_normal_z >= 0 &&
+ vlen(self.origin - trace_endpos) <= 20)
+ {
+ f = self.velocity * trace_plane_normal;
+ if (f >= 0)
+ {
+ // moving away from the wall
+ self.pmove_flags |= PMF_WALLJUMPED;
+ break;
+ }
+ #ifdef SSQC
+ else
+ {
+ dprint (sprintf("PM_WallJump: wallclimb %f\n",
+ f));
+ }
+ #endif
+ }
+ }

- 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;
+ if (self.pmove_flags & PMF_WALLJUMPED)
+ {
+ // bounce off the wall plane -- CEV
+ self.velocity_x += trace_plane_normal_x * PM_WALLJUMPFORCE;
+ self.velocity_y += trace_plane_normal_y * PM_WALLJUMPFORCE;

- case MOVETYPE_NOCLIP:
- // noclip is a debugging feature, no need to
- // worry about acceleration consistency. -- CEV
- makevectors (input_angles);
- wishvel = v_forward * input_movevalues_x;
- wishvel += v_right * input_movevalues_y;
- wishvel += v_up * input_movevalues_z;
+ if (self.doublejump_timer > 0)
+ {
+ #ifdef SSQC
+ dprint ("PM_WallJump: wall double ");
+ #endif
+ self.velocity_z = PM_WALLJUMPDOUBLE;
+ }
+ else
+ {
+ #ifdef SSQC
+ dprint ("PM_WallJump: walljump ");
+ #endif
+ self.velocity_z = PM_WALLJUMPSPEED;
+ }

- PM_NoClipAccelerate (wishvel, 1.0f, input_timelength);
- // we're noclipping so update origin directly -- CEV
- self.origin += self.velocity * input_timelength;
- break;
+ // manage flags & fields; PMF_WALLJUMPED is already set -- CEV
+ self.pmove_flags |= PMF_JUMP_HELD;
+ self.doublejump_timer = PM_DOUBLEJUMP_WINDOW;

- case MOVETYPE_NONE:
- break;
+ // server-side stuff
+ #ifdef SSQC
+ dprint (sprintf("dist %g, normalspeed %g\n",
+ vlen(self.origin - trace_endpos), f));
+ self.button2 = 0;
+ sound (self, CHAN_BODY, "player/plyrjmp8.wav", 1, ATTN_NORM);
+ #endif
}
-
- // make sure we've touched the ground entity (it might've slipped
- // through the slidemove above) -- CEV
- if (self.groundentity && self.groundentity.touch != __NULL__)
- PM_DoTouch (self.groundentity);
-
- touchtriggers ();
- setorigin (self, self.origin);
- self = oldself;
};

-//==============================================================================
-// Code graveyard below. Saved here for reference purposes. -- CEV
-//==============================================================================
-
//----------------------------------------------------------------------
-// PM_ClipVelocity
+// PM_WalkAccelerate
+// Handle accel & friction for MOVETYPE_WALK entities (players). -- CEV
//----------------------------------------------------------------------
-#if 0
-#define STOP_EPSILON 0.125
-vector(vector vel, vector normal, float ob, float oneside) PM_ClipVelocity =
+void(vector wishvel, float move_time) PM_WalkAccelerate =
{
- local float backoff;
+ float accel, friction, newspeed, speed, temp, wishspeed;

- backoff = vel * normal;
+ accel = friction = temp = 0;

- if (backoff < 0)
- backoff *= ob;
- else
- if (oneside)
- // backoff = 0;
- backoff *= -0.001;
- else
- backoff /= ob;
+ wishspeed = vlen (wishvel);
+ vector wishdir = normalize (wishvel);

- vel -= normal * backoff;
+ if (self.pmove_flags & PMF_PRE_MOVE)
+ {
+ if (self.pmove_flags & PMF_ONLADDER)
+ {
+ // ladder physics
+ // these currently stutter in high ping (or low netfps)
+ // situations because we rely on touch()ing the ladder
+ // trigger to set PMF_ONLADDER and the client doesn't
+ // know about the trigger brush yet -- TODO CEV
+ self.velocity = 0.75 * self.velocity;

- if (vel_x > -STOP_EPSILON && vel_x < STOP_EPSILON)
- vel_x = 0;
- if (vel_y > -STOP_EPSILON && vel_y < STOP_EPSILON)
- vel_y = 0;
- if (vel_z > -STOP_EPSILON && vel_z < STOP_EPSILON)
- vel_z = 0;
+ if (input_buttons & 2 || self.button2 > 0 ||
+ input_movevalues_z > 0)
+ {
+ // PlayerClimb -- johnfitz
+ self.velocity_z = 160;
+ }
+ else if (input_movevalues_z < 0)
+ {
+ // PlayerClimbDown -- CEV
+ self.velocity_z = -160;
+ }
+ else
+ {
+ self.flags |= FL_JUMPRELEASED;
+ self.pmove_flags &= ~PMF_JUMP_HELD;
+ self.velocity_z = 0;
+ }

- return vel;
-};
-#endif
+ self.pmove_flags &= ~PMF_ONGROUND;
+ }
+ else
+ {
+ if (input_buttons & 2 || self.button2 > 0 ||
+ input_movevalues_z > 0)
+ {
+ // +jump was pressed
+ if (self.pmove_flags & PMF_ONGROUND)
+ // normal jump
+ PM_Jump ();
+ else if (frame_walljump)
+ // walljump
+ PM_WallJump ();
+ }
+ else
+ {
+ self.flags |= FL_JUMPRELEASED;
+ }
+ }

-//----------------------------------------------------------------------
-// PM_Q3SlideMove
-//
-// Based on Quake 3's PM_SlideMove (found in the Quake III engine GPL2
-// source release, file bg_slidemove.c). This doesn't work reliably for
-// reasons that are unclear to me; I would guess it has to do with its
-// complex use of an array. I've left it here commented out for reference
-// purposes. -- CEV
-//----------------------------------------------------------------------
-#if 0
-int(float dogravity) PM_Q3SlideMove =
-{
- vector planes[PHYS_SLIDE_MAX_CLIP_PLANES];
- vector dir, end, new_v, start_v;
- vector grav_v, gnew_v;
- int blocked, bumpcount, numplanes, i, j, k;
- float d, time_left, ent_grav;
+ if (self.pmove_flags & PMF_ONGROUND)
+ self.pmove_flags |= PMF_STARTGROUND;
+ else
+ self.pmove_flags &= ~PMF_STARTGROUND;
+ }
+
+ // Test STARTGROUND (the ONGROUND status post jump) to avoid
+ // a ground friction frame when chaining jumps together.
+ // Probably has unintended consequences. -- CEV
+ if (self.pmove_flags & PMF_STARTGROUND)
+ {
+ // we're on ground so pick accel and friction values -- CEV
+ accel = PM_GROUNDACCEL;
+ friction = PM_GROUNDFRICTION;

- blocked = 0;
+ if (self.groundboost_timer > 0)
+ wishspeed = min (wishspeed, PM_BOOSTWISHSPEED);
+ else
+ wishspeed = min (wishspeed, PM_MAXSPEED);
+ }
+ else if (!(self.pmove_flags & PMF_STARTGROUND) &&
+ self.pmove_flags & PMF_ONGROUND)
+ {
+ // we're in the second acceleration pass and we've just
+ // landed. For this one-half acceleration frame set
+ // friction to zero and accel to GROUNDACCEL. This
+ // moment is particularly noticeable on stairs. -- CEV
+ // accel = PM_GROUNDACCEL;
+ accel = 1.0f;
+ friction = 0;
+
+ // an experiment - rewrite input values to direct
+ // movement forward in the half-frame after we've
+ // just landed. the neverending quest to improve
+ // Quake stairs -- CEV
+ if (input_movevalues_y && input_movevalues_x == 0)
+ {
+ // always be +fwd
+ input_movevalues_x = fabs (input_movevalues_y);

- // initialize all these vectors
- for (i = 0; i < PHYS_SLIDE_MAX_CLIP_PLANES; i++)
- planes[i] = '0 0 0';
- dir = end = new_v = grav_v = gnew_v = '0 0 0';
- start_v = self.velocity;
+ // we've altered input movevalues so rework wishvel
+ // start by ensuring v_ globals are correct
+ makevectors ([0, input_angles_y, 0]);
+ wishvel = v_forward * input_movevalues_x;
+ wishvel += v_right * input_movevalues_y;
+ wishspeed = vlen (wishvel);
+ wishdir = normalize (wishvel);
+ }

- if (dogravity)
+ if (self.groundboost_timer > 0)
+ wishspeed = min (wishspeed, PM_BOOSTWISHSPEED);
+ else
+ wishspeed = min (wishspeed, PM_MAXSPEED);
+ }
+ else if (input_movevalues_x == 0)
{
- grav_v = self.velocity;
- if (self.gravity)
- ent_grav = self.gravity;
+ // Q1 air accel when requesting sideways movement in the air
+ // flag a check below with this -1 -- CEV
+ if (wishspeed > PM_MAXSPEED)
+ wishspeed = PM_MAXSPEED;
+
+ accel = -1;
+ }
+ else
+ {
+ if (wishspeed > PM_MAXSPEED)
+ wishspeed = PM_MAXSPEED;
+
+ if (input_movevalues_y == 0)
+ {
+ // Painkiller style air control
+ if (wishvel * self.velocity < 0)
+ // against original velocity; we're slowing down
+ accel = PM_AIRACCELBACK;
+ else
+ // we're with original velocity - gaining speed
+ accel = PM_AIRACCELFWD;
+
+ // borrow the friction var to flag a conditional below
+ friction = -1;
+ }
else
- ent_grav = 1.0;
- grav_v_z -= ent_grav * cvar("sv_gravity") * input_timelength;
- self.velocity_z = (self.velocity_z + grav_v_z) * 0.5;
- start_v_z = grav_v_z;
- if (self.groundnormal)
{
- self.velocity = phys_clipvelocity (self.velocity,
- self.groundnormal, PM_OVERCLIP, FALSE);
+ // we're in the air and the player is requesting both
+ // forward/back and sideways movement, so pick the Q3
+ // air accel value -- CEV
+ accel = PM_AIRACCELQ3;
}
}

- if (self.groundnormal)
- {
- numplanes = 1;
- planes[0] = self.groundnormal;
- }
- else
- numplanes = 0;
-
- // never turn against original velocity
- planes[numplanes] = normalize (self.velocity);
- numplanes++;
+ // I've duplicated a fair amount of code below, inlining the various
+ // acceleration functions to hopefully improve performance -- CEV

- for (bumpcount = 0, time_left = input_timelength;
- time_left > 0 && bumpcount < PHYS_SLIDE_NUMBUMPS; bumpcount++)
+ // apply friction (ground or otherwise)
+ if (friction > 0)
{
- end = self.origin + self.velocity * time_left;
- tracebox(self.origin, self.mins, self.maxs, end, FALSE, self);
+ // inline PM_Friction
+ speed = vlen (self.velocity);

- if (trace_allsolid || trace_startsolid)
+ if (speed < 1)
{
- // entity is trapped in a solid; attempt to nudge out
- if (phys_nudge()) continue;
-
- // nah, we're stuck. don't build up falling damage
- // but allow sideways acceleration
- dprint ("PM_Q3SlideMove: entity trapped in a solid\n");
- self.velocity_z = 0;
- return 3;
+ self.velocity = '0 0 0';
}
+ else
+ {
+ // calculate what their new speed should be
+ temp = speed < PM_STOPSPEED ? PM_STOPSPEED : speed;
+ newspeed = speed - temp * friction * move_time;

- float cur_fraction = trace_fraction;
- vector cur_plane = trace_plane_normal;
-
- if (cur_fraction > 0)
- self.origin = trace_endpos;
-
- if (cur_fraction == 1) break;
+ // and slow them
+ if (newspeed < 0)
+ newspeed = 0;

- /*
- // this isn't working for some reason -- CEV
- if (cur_plane.z)
- if (cur_plane.z > 0.7)
- {
- blocked |= 1;
- self.flags |= FL_ONGROUND;
- self.groundnormal = cur_plane;
- self.groundentity = trace_ent;
- }
- else
- blocked |= 2;
- */
+ self.velocity = self.velocity * (newspeed / speed);
+ }
+ }

- phys_dotouch (trace_ent);
- time_left -= time_left * cur_fraction;
+ if (accel == -1)
+ {
+ // Quake 1 sideways air acceleration; inline PM_AirAccelerate
+ wishvel_z = 0;
+ temp = vlen (wishvel);
+ wishvel = normalize (wishvel);
+ if (temp > PM_MAXAIRSPEED)
+ temp = PM_MAXAIRSPEED;
+ speed = self.velocity * wishvel;
+ speed = temp - speed;

- if (numplanes >= PHYS_SLIDE_MAX_CLIP_PLANES)
+ if (speed > 0)
{
- // this shouldn't really happen
- if (cvar("developer"))
- {
- dprint ("PM_Q3SlideMove: numplanes >= max: ");
- dprint (ftos(numplanes));
- dprint ("\n");
- }
- self.velocity = '0 0 0';
- return 7;
+ newspeed = PM_AIRACCEL * move_time * wishspeed;
+ if (newspeed > speed)
+ newspeed = speed;
+ self.velocity += newspeed * wishvel;
}
+ }
+ else if (accel > 0)
+ {
+ // ground & Quake 3 style acceleration; inline PM_Accelerate
+ speed = wishspeed - (self.velocity * wishdir);

- // comment from Q3 source:
- // if this is the same plane we hit before nudge velocity out
- // along it; this fixes some issues with non-axial planes
- for (i = 0; i < numplanes; i++)
+ if (speed > 0)
{
- if (cur_plane * planes[i] > 0.99)
- {
- dprint ("PM_Q3SlideMove: non-axial plane\n");
- self.velocity += cur_plane;
- break;
- }
+ newspeed = accel * move_time * wishspeed;
+ if (newspeed > speed)
+ newspeed = speed;
+
+ self.velocity += newspeed * wishdir;
}
+ }

- if (i < numplanes) continue;
+ // CPM & PK-style air control, needs to happen after PM_Accelerate
+ if (friction == -1)
+ {
+ // inline PM_AirControl
+ temp = self.velocity_z;
+ self.velocity_z = 0;

- planes[numplanes] = cur_plane;
- numplanes++;
+ speed = vlen (self.velocity);
+ self.velocity = normalize (self.velocity);

- // modify velocity so it parallels all of the clip planes
- // find a plane that it enters
- for (i = 0; i < numplanes; i++)
+ if ((self.velocity * wishdir) > 0)
{
- if (self.velocity * planes[i] >= 0.1)
- continue;
+ self.velocity = normalize (self.velocity * speed +
+ wishdir * PM_AIRACCELTURN);
+ }

- // slide along the plane
- new_v = phys_clipvelocity (self.velocity, planes[i],
- PM_OVERCLIP, FALSE);
- if (dogravity)
- gnew_v = phys_clipvelocity (grav_v,
- planes[i], PM_OVERCLIP, FALSE);
+ self.velocity *= speed;
+ self.velocity_z = temp;
+ }

- for (j = 0; j < numplanes; j++)
- {
- if (new_v * planes[j] >= 0.1) continue;
+ if (!(self.pmove_flags & PMF_PRE_MOVE))
+ self.pmove_flags &= ~PMF_STARTGROUND;
+};

- // try clipping the move to the plane
- new_v = phys_clipvelocity (new_v, planes[j],
- PM_OVERCLIP, FALSE);
- if (dogravity)
- gnew_v = phys_clipvelocity (gnew_v,
- planes[j], PM_OVERCLIP, FALSE);
+//----------------------------------------------------------------------
+// PM_SwimAccelerate
+// Largely copied from id Software's WaterMove. Works like NoClipAccelerate.
+//----------------------------------------------------------------------
+void(vector wishvel, float move_time) PM_SwimAccelerate =
+{
+ float wishspeed;

- // see if it goes back into the first clip plane
- if (new_v * planes[i] >= 0) continue;
+ if (self.pmove_flags & PMF_PRE_MOVE)
+ {
+ // CheckWaterJump
+ if (self.waterlevel == WATERLEVEL_WAIST)
+ {
+ local vector start, end;

- // slide along the crease
- dir = crossproduct (planes[i], planes[j]);
- dir = normalize (dir);
- d = dir * self.velocity;
- new_v = dir * d;
+ 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 (dogravity)
- {
- dir = crossproduct(planes[i],planes[j]);
- dir = normalize (dir);
- d = dir * grav_v;
- gnew_v = dir * d;
- }
+ 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);

- // see if the move enters a third plane
- for (k = 0; k < numplanes; k++)
+ if (trace_fraction == 1)
{
- if (k == i || k == j)
- continue;
-
- if (new_v * planes[k] >= 0.1)
- // no interaction
- continue;
-
- // testing something here -- CEV
- if (numplanes < 3)
- continue;
-
- // stop at a triple plane interaction
- if (cvar("developer"))
- {
- // laugh at my 80 col terminal
- dprint ("PM_Q3SlideMove: ");
- dprint ("triple plane inter");
- dprint ("action; numplanes: ");
- dprint (ftos(numplanes));
- dprint ("\n");
- }
- self.velocity = '0 0 0';
- return 7;
+ // open at eye level
+ self.flags |= FL_WATERJUMP;
+ self.pmove_flags |= PMF_WATERJUMPED;
+ self.flags &= ~FL_JUMPRELEASED;
+ self.pmove_flags &= ~PMF_JUMP_HELD;
+ // Z velocity was 225
+ self.velocity_z = PM_JUMPSPEED;
+ // we don't care about the 'safety net'
+ // in this movement set (unlike vanilla)
+ // so don't set teleport_time -- CEV
+ // self.teleport_time = time + 2;
}
}
+ }

- // if we've fixed all interaction try another move
- self.velocity = new_v;
- if (dogravity) grav_v = gnew_v;
- break;
+ #ifdef SSQC
+ // functionality copied into pmove from client.qc -- CEV
+ if (self.waterlevel >= WATERLEVEL_WAIST)
+ {
+ if (self.swim_flag < time)
+ {
+ // play swimming sound
+ self.swim_flag = time + 1;
+ if (random() < 0.5)
+ sound (self, CHAN_BODY,
+ "misc/water1.wav", 1,ATTN_NORM);
+ else
+ sound (self, CHAN_BODY,
+ "misc/water2.wav", 1,ATTN_NORM);
+ }
}
+ #endif
}

- // final gravity check here
- if (dogravity) self.velocity = grav_v;
+ if (input_buttons & 2 && !(self.pmove_flags & PMF_WATERJUMPED))
+ // smartjump
+ wishvel_z = max (PM_MAXSPEED, wishvel_z);
+ else if (input_movevalues == '0 0 0')
+ // drift towards bottom -- CEV
+ wishvel_z -= PM_WATERSINKSPEED;
+
+ wishspeed = vlen (wishvel);
+ if (wishspeed > PM_WATERMAXSPEED)
+ wishspeed = PM_WATERMAXSPEED;
+ wishvel = normalize (wishvel);

- // wallclip / wall skim timer check
- if ((self.primal_speed > PM_MAXSPEED) &&
- (self.jump_time) &&
- (!(self.flags & FL_WATERJUMP)) &&
- (self.teleport_time <= (time - 0.1)) &&
- (self.boost_time <= time - PM_BOOST_WINDOW) &&
- (self.jump_time > (time - PM_WALLCLIP_WINDOW)) &&
- (vlen(start_v) > vlen(self.velocity)))
- {
- // dprint ("PM_Q3SlideMove: restoring velocity...\n");
- self.velocity = start_v;
- }
+ // water friction; reuse PM_Friction with a special constant -- CEV
+ if (!(self.pmove_flags & PMF_WATERJUMPED))
+ PM_Friction (PM_WATERFRICTION, move_time);

- if (bumpcount > 0) blocked |= 8;
- return blocked;
+ // accelerate; reuse PM_Accelerate with a special constant -- CEV
+ PM_Accelerate (wishvel, wishspeed, PM_WATERACCEL, move_time);
};
-#endif

//----------------------------------------------------------------------
-// PM_Q1FlyMove
+// PM_NoClipAccelerate
//
-// Based on Quake 1's SV_FlyMove function (found in id's engine, QuakeSpasm,
-// and others). Has the disadvantage of using an array of vectors, and
-// is slow (I assume) in QuakeC. -- CEV
+// Basic acceleration for flying / noclipping players. Not suitable for
+// swimming. -- CEV
//----------------------------------------------------------------------
-#if 0
-#define PM_SLIDE_MAX_CLIP_PLANES 5
-#define PM_SLIDE_NUMBUMPS 5
-int(float dogravity, float onesided) PM_Q1FlyMove =
+void(vector wishvel, float scale, float move_time) PM_NoClipAccelerate =
{
- vector planes[PM_SLIDE_MAX_CLIP_PLANES];
- vector dir, end, new_v, start_v;
- int blocked, bumpcount, numplanes, i, j;
- float d, ent_grav, grav, time_left;
+ float wishspeed;

- blocked = numplanes = 0;
+ // smartjump
+ if (input_buttons & 2)
+ wishvel_z = max (PM_MAXSPEED, wishvel_z);

- // initialize all these vectors
- for (i = 0; i < PM_SLIDE_MAX_CLIP_PLANES; i++)
- planes[i] = '0 0 0';
- dir = end = new_v = '0 0 0';
+ wishspeed = vlen (wishvel) * scale;
+ wishvel = normalize (wishvel);

- if (dogravity)
+ 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;
+
+ if (self.doublejump_timer == 0)
{
- if (self.gravity)
- ent_grav = self.gravity;
- else
- ent_grav = 1.0;
- grav = ent_grav * autocvar(sv_gravity, 800) * input_timelength;
- // we'll do half of it now, half later -- CEV
- self.velocity_z -= grav * 0.5;
+ self.pmove_flags &= ~PMF_DOUBLEJUMPED;
}

- // set start_v after gravity check above
- start_v = self.velocity;
+ if (self.groundboost_timer > 0 && move_time > 0)
+ self.groundboost_timer -= move_time;
+ else if (self.groundboost_timer < 0)
+ self.groundboost_timer = 0;
+};

- for (bumpcount = 0, time_left = input_timelength;
- time_left > 0 && bumpcount < PM_SLIDE_NUMBUMPS; bumpcount++)
- {
- end = self.origin + self.velocity * time_left;
- tracebox (self.origin, self.mins, self.maxs, end, FALSE, self);
+//----------------------------------------------------------------------
+// PM_Move -- PMOVE entrypoint -- CEV
+//----------------------------------------------------------------------
+void(entity target) PM_Move =
+{
+ if (input_timelength <= 0)
+ // don't process partial frames -- CEV
+ return;

- if (trace_allsolid || trace_startsolid)
- {
- // entity is trapped in a solid; attempt to nudge out
- if (PM_Nudge())
- continue;
+ vector wishvel;

- // nah, we're stuck. don't build up falling damage
- // but allow sideways acceleration
- #ifdef SSQC
- dprint ("PM_Q1FlyMove: entity trapped in a solid\n");
- #endif
- self.velocity_z = 0;
- return 3;
+ // Nudge player's origin if the server is sending low-precision
+ // (16 bit?) player coordinates. This can be fixed by setting
+ // sv_bigcoords to 1 (so the server sends float coords). -- CEV
+ if (!frame_bigcoords)
+ PM_Nudge ();
+
+ // rework user input, copying something like QuakeSpasm's always run
+ // feature here. This turns the +speed button into a hold-to-walk
+ // button when cl_run is 0 (when FTE's always run setting is on).
+ // -- CEV
+ if (frame_clrun == FALSE)
+ {
+ // local float s = 0;
+ for (float i = 0; i < 3; i++)
+ {
+ // this works, it's just commented out to stop calling
+ // autocvar quite so many times. to re-enable change
+ // the check against "PM_RUNSPEED" below to "s" -- CEV
+ /*
+ if (i == 0)
+ s = autocvar (cl_forwardspeed, PM_RUNSPEED);
+ else if (i == 1)
+ s = autocvar (cl_sidespeed, PM_RUNSPEED);
+ else if (i == 2)
+ s = autocvar (cl_upspeed, PM_RUNSPEED);
+ */
+ if (fabs(input_movevalues[i]) > 400.0f)
+ input_movevalues[i] = PM_WALKSPEED *
+ (input_movevalues[i] /
+ fabs(input_movevalues[i]));
+ else if (fabs(input_movevalues[i]) == 400.0f)
+ input_movevalues[i] = PM_RUNSPEED *
+ (input_movevalues[i] /
+ fabs(input_movevalues[i]));
+ // Don't need this, it's already PM_RUNSPEED -- CEV
+ /*
+ else if (input_movevalues[i] != 0)
+ input_movevalues[i] = PM_RUNSPEED *
+ (input_movevalues[i] /
+ fabs(input_movevalues[i]));
+ */
}
+ }

- if (trace_fraction >= 0.001)
- self.origin = trace_endpos;
+ // TODO CEV this is the wrong place to clear PMF_JUMP_HELD
+ if (!(input_buttons & 2))
+ self.pmove_flags &= ~PMF_JUMP_HELD;

- if (trace_fraction >= 1.0f)
- break;
+ // figure out the properties of the player's position, then clear timers
+ PM_CategorizePosition ();
+ PM_ManageTimers (0);

- if (trace_plane_normal_z > 0.7)
- {
- blocked |= 1;
- self.groundnormal = trace_plane_normal;
- self.groundentity = trace_ent;
- }
+ switch (self.movetype)
+ {
+ case MOVETYPE_WALK:
+ local float dogravity, doskim, sticky;

- if (!(trace_plane_normal_z))
- blocked |= 2;
+ doskim = FALSE;
+ sticky = FALSE;

- PM_DoTouch (trace_ent);
- time_left -= time_left * trace_fraction;
+ // signal acceleration functions that we're pre-move
+ self.pmove_flags |= PMF_PRE_MOVE;

- if (numplanes >= PM_SLIDE_MAX_CLIP_PLANES)
- {
- // this shouldn't really happen
- if (cvar("developer"))
+ // split the acceleration in two to reduce
+ // framerate dependence. -- CEV
+ if (self.waterlevel >= WATERLEVEL_WAIST)
{
- #ifdef SSQC
- dprint ("PM_Q1FlyMove: numplanes >= max: ");
- dprint (ftos(numplanes));
- dprint ("\n");
- #endif
- }
- self.velocity = '0 0 0';
- blocked = 7;
- break;
- }
+ // no gravity, velocity-restoring behavior,
+ // or sticky clips underwater -- CEV
+ dogravity = FALSE;
+ doskim = FALSE;
+ sticky = FALSE;

- planes[numplanes] = trace_plane_normal;
- numplanes++;
+ // figure out wishvel -- CEV
+ makevectors (input_angles);
+ wishvel = v_forward * input_movevalues_x;
+ wishvel += v_right * input_movevalues_y;
+ wishvel += v_up * input_movevalues_z;

- for (i = 0; i < numplanes; i++)
- {
- // slide along the plane
- new_v = PM_ClipVelocity (self.velocity,
- planes[i], PM_OVERCLIP, onesided);
+ // swim acceleration
+ PM_SwimAccelerate (wishvel,
+ input_timelength * 0.5f);
+ }
+ else
+ {
+ // only yaw matters here -- CEV
+ makevectors ([0, input_angles_y, 0]);
+ wishvel = v_forward * input_movevalues_x;
+ wishvel += v_right * input_movevalues_y;
+ // don't need wishvel_z -- CEV
+ // wishvel += v_up * input_movevalues_z;

- for (j = 0; j < numplanes; j++)
- if (j != i)
- // not ok (the comment says)
- if ((new_v * planes[j]) < 0)
- break;
+ PM_WalkAccelerate (wishvel,
+ input_timelength * 0.5f);

- if (j == numplanes)
- break;
- }
+ // WalkAccelerate calls Jump which might
+ // alter pmove_flags, so do our feature
+ // checks afterwards -- CEV
+ if (self.doublejump_timer <= 0 ||
+ self.pmove_flags & PMF_DOUBLEJUMPED ||
+ self.pmove_flags & PMF_WALLJUMPED)
+ // don't stick to the floor
+ sticky = FALSE;
+ else
+ // we've jumped & haven't doublejumped,
+ // so do sticky steps -- CEV
+ sticky = TRUE;

- if (i == numplanes)
- {
- if (numplanes != 2)
- {
+ // apply gravity only when in the air or on
+ // a ramp -- CEV
+ #ifdef CSQC
+ if (intermission)
+ #endif
#ifdef SSQC
- dprint ("PM_Q1FlyMove: stopping dead\n");
+ if (intermission_running)
#endif
- self.velocity = '0 0 0';
- blocked = 7;
- break;
- }
-
- // slide along the crease
- dir = crossproduct (planes[0], planes[1]);
- dir = normalize (dir);
- d = (dir * self.velocity);
- new_v = dir * d;
- }
-
- self.velocity = new_v;
-
- if ((self.velocity * start_v) <= 0)
- {
- #ifdef SSQC
- dprint ("PM_Q1FlyMove: turned against orig vel\n");
- #endif
- self.velocity = '0 0 0';
- break;
- }
- }
-
- // wallclip / wall skim timer check
- if ( // (self.primal_speed > PM_MAXSPEED) &&
- (self.velocity != '0 0 0') &&
- (self.jump_time) &&
- (!(self.pmove_flags & PMF_WATERJUMP)) &&
- (self.teleport_time <= (time - 0.1)) &&
- (self.boost_time <= time - PM_BOOST_WINDOW) &&
- (self.jump_time > (time - PM_WALLCLIP_WINDOW)) &&
- (vlen(start_v) > vlen(self.velocity)))
- {
- #ifdef SSQC
- dprint ("PM_Q1FlyMove: restoring velocity...\n");
- #endif
- self.velocity = [start_v_x, start_v_y, self.velocity_z];
- }
-
- // final gravity check here
- if (dogravity)
- self.velocity_z -= grav * 0.5;
-
- /* for debugging -- CEV
- #ifdef SSQC
- dprint ("PM_Q1FlyMove: numplanes ");
- dprint (ftos(numplanes));
- dprint ("\n");
- #endif
- */
-
- return blocked;
-};
-#endif
-
-//----------------------------------------------------------------------
-// PM_StepSlideMove
-//
-// Based on the StepSlideMove function found in the Quake III sourcecode.
-// Calls PM_Q1FlyMove several times. This works, but is slow and has
-// some tracking issues (probably my fault). -- CEV
-//----------------------------------------------------------------------
-#if 0
-void(float dogravity, float onesided) PM_StepSlideMove =
-{
- vector start_o, start_v, first_o, first_v;
- vector up, down;
- float stepsize;
- int clip, first_clip;
- int start_onground;
-
- clip = first_clip = 0;
- start_o = self.origin;
- start_v = self.velocity;
- start_onground = (self.pmove_flags & PMF_ONGROUND);
+ dogravity = FALSE;
+ else if (self.groundnormal &&
+ self.groundnormal_z < 1.0f)
+ dogravity = TRUE;
+ else if (self.pmove_flags & PMF_ONGROUND)
+ dogravity = FALSE;
+ else
+ dogravity = TRUE;

- // first try let's go
- first_clip = PM_Q1FlyMove (dogravity, onesided);
+ // skim / wallclipping checks -- CEV
+ if (self.doublejump_timer >
+ (PM_DOUBLEJUMP_WINDOW -
+ PM_WALLCLIP_WINDOW) &&
+ !(self.pmove_flags & PMF_WALLJUMPED) &&
+ self.teleport_time <= (time - 0.1))
+ {
+ doskim = TRUE;
+ }

- if (!(first_clip & 2) && !(first_clip & 8))
- {
- // we got where we wanted to go right away
- // #ifdef SSQC
- // dprint ("PM_StepSlideMove: accepting first move\n");
- // #endif
- if (first_clip & 1)
- self.pmove_flags |= PMF_ONGROUND;
- else
- self.pmove_flags &= ~PMF_ONGROUND;
- return;
- }
+ if (self.pmove_flags & PMF_ONLADDER)
+ {
+ // feature set for ladders -- CEV
+ dogravity = FALSE;
+ doskim = FALSE;
+ sticky = FALSE;
+ }
+ }

- if (self.movetype != MOVETYPE_WALK)
- // gibbed by a trigger?
- return;
+ // timers (part 1) -- CEV
+ PM_ManageTimers (input_timelength * 0.5f);

- if (autocvar(pm_nostep, FALSE))
- // no stepping. useful for testing -- CEV
- return;
+ // Do the move. Bounce, Rock, Skate, Roll -- CEV
+ PM_DanceMove (dogravity, sticky, TRUE, doskim);

- if (!(autocvar(pm_airstep, TRUE)) && !(self.pmove_flags & PMF_ONGROUND))
- // Borrowing FTE's pm_airstep cvar here. If false, don't
- // step up while in the air (changes stairs) -- CEV
- return;
+ // signal accel functions we're post-move
+ self.pmove_flags &= ~PMF_PRE_MOVE;

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

- self.origin = start_o;
- self.velocity = start_v;
+ // timers (part 2) -- CEV
+ PM_ManageTimers (input_timelength * 0.5f);

- up = start_o;
- up_z += PM_STEPHEIGHT;
- tracebox (start_o, self.mins, self.maxs, up, FALSE, self);
+ // clear ladder flag -- CEV
+ if (self.pmove_flags & PMF_ONLADDER)
+ self.pmove_flags &= ~PMF_ONLADDER;
+ break;

- if (trace_allsolid)
- {
- #ifdef SSQC
- dprint ("PM_StepSlideMove: can't step up\n");
- #endif
- self.origin = first_o;
- self.velocity = first_v;
- if (first_clip & 1)
- self.pmove_flags |= PMF_ONGROUND;
- else
- self.pmove_flags &= ~PMF_ONGROUND;
- return;
- }
+ case MOVETYPE_FLY:
+ // split acceleration in two to reduce framerate
+ // dependence. -- CEV
+ makevectors (input_angles);
+ wishvel = v_forward * input_movevalues_x;
+ wishvel += v_right * input_movevalues_y;
+ wishvel += v_up * input_movevalues_z;

- // try slidemove from this position (in air)
- stepsize = trace_endpos_z - start_o_z;
- self.origin = trace_endpos;
- self.velocity = start_v;
- clip = PM_Q1FlyMove (dogravity, onesided);
+ 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;

- // push down the final amount
- down = self.origin;
- down_z -= stepsize;
- tracebox (self.origin, self.mins, self.maxs, down, FALSE, self);
+ case MOVETYPE_NOCLIP:
+ // noclip is a debugging feature, no need to
+ // worry about acceleration consistency. -- CEV
+ makevectors (input_angles);
+ wishvel = v_forward * input_movevalues_x;
+ wishvel += v_right * input_movevalues_y;
+ wishvel += v_up * input_movevalues_z;

- if (trace_allsolid)
- {
- #ifdef SSQC
- dprint ("PM_StepSlideMove: second move is in a solid\n");
- #endif
- self.origin = first_o;
- self.velocity = first_v;
- if (first_clip & 1)
- self.pmove_flags |= PMF_ONGROUND;
- else
- self.pmove_flags &= ~PMF_ONGROUND;
- return;
- }
+ PM_NoClipAccelerate (wishvel, 1.0f, input_timelength);
+ // we're noclipping so update origin directly -- CEV
+ self.origin += self.velocity * input_timelength;
+ break;

- if (!trace_plane_normal_z || trace_plane_normal_z > 0.7)
- {
- // we're on 'good ground', accept the second slidemove
- // #ifdef SSQC
- // dprint ("PM_StepSlideMove: accepting second slidemove\n");
- // #endif
- self.origin = trace_endpos;
- if (trace_fraction < 1.0)
- {
- // clip to step
- self.velocity = PM_ClipVelocity (self.velocity,
- trace_plane_normal, PM_OVERCLIP, onesided);
- }
- if (trace_plane_normal_z > 0.7)
- self.pmove_flags |= PMF_ONGROUND;
- else
- self.pmove_flags &= ~PMF_ONGROUND;
- }
- else
- {
- // not on 'good ground', revert to first slidemove
- // see the similar section of SV_WalkMove
- // #ifdef SSQC
- // dprint ("PM_StepSlideMove: reverting to first move\n");
- // #endif
- self.origin = first_o;
- self.velocity = first_v;
- if (first_clip & 1)
- self.pmove_flags |= PMF_ONGROUND;
- else
- self.pmove_flags &= ~PMF_ONGROUND;
+ case MOVETYPE_NONE:
+ break;
}

- // this is a complicated check.
- // if we started in the air and we're now on the ground
- // and we stepped up and we lost some Z velocity then we've
- // clipped to ground on a step up
- if (!start_onground && (self.pmove_flags & PMF_ONGROUND) &&
- (start_v_z > self.velocity_z) &&
- (self.velocity_z < 1) &&
- (self.origin_z - start_o_z > 0))
- {
- if (!self.boost_time &&
- self.boost_time <= time - PM_BOOST_WINDOW)
- self.boost_time = time;
+ // make sure we've touched the ground entity (it might've slipped
+ // through the slidemove above) -- CEV
+ if (self.groundentity && self.groundentity.touch != __NULL__)
+ PM_DoTouch (self.groundentity);

- if (cvar("developer"))
- {
- #ifdef SSQC
- stepsize = self.origin_z - start_o_z;
- dprint (sprintf("PM_StepSlideMove: step up: %g\n",
- stepsize));
- #endif
- }
- }
+ touchtriggers ();
+ setorigin (self, self.origin);
};
-#endif

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

Diff qc/projectiles/bullet.qc

diff --git a/qc/projectiles/bullet.qc b/qc/projectiles/bullet.qc
index 33e9ca2..ae67628 100644
--- a/qc/projectiles/bullet.qc
+++ b/qc/projectiles/bullet.qc
@@ -3,100 +3,119 @@
//==============================================================================

//======================================================================
-// globals
+// constants
//======================================================================
-entity multi_ent;
-float multi_damage;
+const float BULLET_DIRECT_DAMAGE = 5; // id1 is 4
+ // bullets don't splash
+const float BULLET_SPEED = 2500.0f; // AD is 2000 units per for player

-//----------------------------------------------------------------------
-// TraceAttack
-//----------------------------------------------------------------------
-void(float damage, vector dir) TraceAttack =
-{
- local vector vel, org;
-
- vel = normalize (dir + v_up * crandom() + v_right * crandom());
- vel = vel + 2 * trace_plane_normal;
- vel = vel * 200;
-
- org = trace_endpos - dir * 4;
-
- if (trace_ent.takedamage)
- {
- SpawnBlood (org, vel * 0.2, damage);
- AddMultiDamage (trace_ent, damage);
- }
- else
- {
- WriteByte (MSG_BROADCAST, SVC_TEMPENTITY);
- WriteByte (MSG_BROADCAST, TE_GUNSHOT);
- WriteCoord (MSG_BROADCAST, org_x);
- WriteCoord (MSG_BROADCAST, org_y);
- WriteCoord (MSG_BROADCAST, org_z);
- }
-};
+const vector BULLET_MINS = '0 0 0'; // let's not change this -- CEV
+const vector BULLET_MAXS = '0 0 0'; //

-/*======================================================================
-MULTI-DAMAGE
-Collects multiple small damages into a single damage
-======================================================================*/
-void() ClearMultiDamage =
-{
- multi_ent = world;
- multi_damage = 0;
-};
+//======================================================================
+// fields
+//======================================================================

-void() ApplyMultiDamage =
-{
- if (!multi_ent)
- return;
- base_entity::t_damage2 (multi_ent, self, self, multi_damage);
-};
+// based on BDW's Flak Ogre code -- CEV
+.float bulletcount;

-void(entity hit, float damage) AddMultiDamage =
+//------------------------------------------------------------------------------
+class projectile_bullet: base_projectile
{
- if (!hit)
- return;
-
- if (hit != multi_ent)
+ //--------------------------------------------------------------
+ nonvirtual void() think_damage =
{
- ApplyMultiDamage ();
- multi_damage = damage;
- multi_ent = hit;
- }
- else
+ // get correct gib direction
+ this.origin = this.oldenemy.origin + this.oldorigin;
+ this.t_damage2 (this.oldenemy, this, this.owner,
+ this.oldenemy.bulletcount);
+ this.oldenemy.bulletcount = 0;
+ remove (this);
+ };
+
+ //--------------------------------------------------------------
+ virtual void(entity toucher) do_touch =
{
- multi_damage = multi_damage + damage;
- }
-};
-
-//----------------------------------------------------------------------
-// FireBullets
-// Used by shotgun, super shotgun, and enemy soldier firing. Go to the
-// trouble of combining multiple pellets into a single damage call.
-//----------------------------------------------------------------------
-void(float shotcount, vector dir, vector spread) FireBullets =
-{
- local vector direction;
- local vector src;
+ // hit something that bleeds
+ if (toucher.takedamage)
+ {
+ spawn_touchblood (this.direct_damage);
+
+ if (toucher.bulletcount)
+ {
+ // not the first one
+ toucher.bulletcount += this.direct_damage;
+ remove (this);
+ return;
+ }
+
+ // the first one...
+ toucher.bulletcount = this.direct_damage;
+
+ // stick around for a little while...
+ this.velocity = '0 0 0';
+ this.solid = SOLID_NOT;
+ this.touch = sub_null;
+ this.model = string_null;
+ // displacement from enemy origin (its gonna
+ // move next frame)
+ this.oldorigin = this.origin - toucher.origin;
+ this.oldenemy = toucher;
+ this.think = this.think_damage;
+ this.nextthink = time + 0.05;
+ return;
+ }

- makevectors (self.v_angle);
+ WriteByte (MSG_BROADCAST, SVC_TEMPENTITY);
+ WriteByte (MSG_BROADCAST, TE_GUNSHOT);
+ WriteCoord (MSG_BROADCAST, this.origin_x);
+ WriteCoord (MSG_BROADCAST, this.origin_y);
+ WriteCoord (MSG_BROADCAST, this.origin_z);

- src = self.origin + v_forward * 10;
- src_z = self.absmin_z + self.size_z * 0.7;
+ remove (this);
+ };

- ClearMultiDamage ();
- while (shotcount > 0)
+ //--------------------------------------------------------------
+ void() projectile_bullet =
{
- direction = dir + crandom() * spread_x * v_right
- + crandom() *spread_y * v_up;
-
- traceline (src, src + direction * 2048, FALSE, self);
- if (trace_fraction != 1.0)
- TraceAttack (4, direction);
-
- shotcount = shotcount - 1;
- }
-
- ApplyMultiDamage ();
+ this.classtype = CT_PROJECTILE_BULLET;
+ this.movetype = MOVETYPE_FLY;
+ this.solid = SOLID_BBOX;
+ this.angles = vectoangles (this.velocity);
+
+ if (!this.avelocity)
+ // avelocity from AD is randomized; fixed for now
+ this.avelocity = '100 200 0';
+
+ if (!this.proj_basespeed)
+ this.proj_basespeed = BULLET_SPEED;
+
+ if (!this.direct_damage)
+ this.direct_damage = BULLET_DIRECT_DAMAGE;
+
+ // dumptruck_ds
+ if (this.mdl_proj && this.mdl_proj != "")
+ {
+ setmodel (this, this.mdl_proj);
+ }
+ else
+ {
+ // AD projectile diamond model
+ setmodel (this, "progs/ad/proj_diam2.mdl");
+
+ // full range of sizes (comment from AD)
+ this.frame = random () * 15;
+
+ // Bright colours (comment from AD)
+ this.skin = 16 + random () * 7;
+ }
+
+ if (this.skin_proj)
+ this.skin = this.skin_proj;
+
+ setsize (this, BULLET_MINS, BULLET_MAXS);
+ setorigin (this, this.origin);
+ this.think = sub_remove;
+ this.nextthink = time + 6;
+ };
};

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

Diff qc/projectiles/flak.qc

diff --git a/qc/projectiles/flak.qc b/qc/projectiles/flak.qc
index fd39987..ff3f34d 100644
--- a/qc/projectiles/flak.qc
+++ b/qc/projectiles/flak.qc
@@ -43,9 +43,6 @@ class projectile_flak: base_projectile
//--------------------------------------------------------------
virtual void(entity toucher) do_touch =
{
- if (toucher.solid == SOLID_TRIGGER)
- return;
-
// hit something that bleeds
if (toucher.takedamage)
{

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 02c689c..4864ccc 100644
--- a/qc/triggers/push.qc
+++ b/qc/triggers/push.qc
@@ -67,6 +67,7 @@ vector(float a, float b, float c) solve_quadratic =
return v;
};

+//------------------------------------------------------------------------------
class base_trigger_push: base_trigger
{
// class fields

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 1f3be95..b907988 100644
--- a/qc/triggers/teleport.qc
+++ b/qc/triggers/teleport.qc
@@ -224,7 +224,8 @@ class trigger_teleport: base_trigger
return;

if (this.spawnflags & TRIGGER_TELEPORT_ONLYPLAYER)
- if (toucher.classtype != CT_PLAYER)
+ if (toucher.classtype != CT_PLAYER &&
+ toucher.classtype != CT_PROJECTILE_GRENADE)
return;

// is this going to work? dumptruck_ds
@@ -247,8 +248,9 @@ class trigger_teleport: base_trigger
// not fired yet
return;

- // only teleport living creatures
- if (toucher.health <= 0 || toucher.solid != SOLID_SLIDEBOX)
+ // only teleport living creatures (and projectiles)
+ if (toucher.health <= 0 || (toucher.solid != SOLID_SLIDEBOX &&
+ toucher.solid != SOLID_BBOX))
return;

sub_usetargets ();
@@ -358,7 +360,8 @@ class trigger_teleport: base_trigger
local vector o;
precache_sound ("ambience/hum1.wav");
o = (this.mins + this.maxs) * 0.5;
- ambientsound (o, "ambience/hum1.wav", 0.5, ATTN_STATIC);
+ // volume was 0.5 -- CEV
+ ambientsound (o, "ambience/hum1.wav", 0.3, ATTN_STATIC);
}

sub_checkwaiting ();

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

Diff qc/world.qc

diff --git a/qc/world.qc b/qc/world.qc
index 0153c20..9dbf2ed 100644
--- a/qc/world.qc
+++ b/qc/world.qc
@@ -331,6 +331,9 @@ void() WeaponPrecache =
precache_sound ("shambler/melee2.wav");
precache_sound ("shambler/smack.wav");
// dumptruck_ds mobot.qc END
+
+ // CEV bullet projectile model from AD
+ precache_model ("progs/ad/proj_diam2.mdl");
};

//----------------------------------------------------------------------
@@ -717,6 +720,7 @@ void() StartFrame =
frame_maxvelocity = autocvar (sv_maxvelocity, 10000.0);
frame_nostep = autocvar (pm_nostep, FALSE);
frame_standardphysics = autocvar (pm_standardphysics, FALSE);
+ frame_walljump = autocvar (pm_walljump, TRUE);

framecount = framecount + 1;

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