Moved the PMove code into the base_player class, so mods can override

sections at will
This commit is contained in:
Marco Cawthorne 2021-06-08 15:30:47 +02:00
parent 3efae50481
commit 17368be5e7
8 changed files with 904 additions and 738 deletions

View file

@ -14,8 +14,6 @@
* OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/ */
void PMove_SetSize(entity targ);
/* /*
================= =================
Predict_EntityUpdate Predict_EntityUpdate
@ -44,7 +42,7 @@ Predict_EntityUpdate(player pl, float new)
pl.customphysics = __NULL__; pl.customphysics = __NULL__;
} }
PMove_SetSize(pl); pl.Physics_SetViewParms();
pl.drawmask = MASK_ENGINE; pl.drawmask = MASK_ENGINE;
return; return;
} }

View file

@ -1,7 +1,9 @@
#includelist #includelist
spectator.qc spectator.qc
pmove.qc pmove.qc
pmove_custom.qc
sound.qc sound.qc
math.qc math.qc
player.qc player.qc
player_pmove.qc
#endlist #endlist

View file

@ -75,6 +75,18 @@ base_player
void(void) base_player; void(void) base_player;
virtual void(float) Physics_Fall;
virtual void(void) Physics_Crouch;
virtual void(void) Physics_Jump;
virtual void(float) Physics_CheckJump;
virtual void(void) Physics_SetViewParms;
virtual void(void) Physics_WaterJump;
virtual void(void) Physics_WaterMove;
virtual float(void) Physics_MaxSpeed;
virtual void(void) Physics_InputPreMove;
virtual void(void) Physics_InputPostMove;
virtual void(void) Physics_Run;
#ifdef CLIENT #ifdef CLIENT
int sequence; int sequence;
float pitch; float pitch;

View file

@ -35,7 +35,7 @@ base_player::ReceiveEntity(float new, float fl)
/* HACK: we need to make this more reliable */ /* HACK: we need to make this more reliable */
if (fl == UPDATE_ALL) { if (fl == UPDATE_ALL) {
/* we respawned */ /* we respawned */
gravity = __NULL__; gravity = 1.0f;
} }
if (fl & PLAYER_MODELINDEX) if (fl & PLAYER_MODELINDEX)
@ -344,5 +344,4 @@ base_player::SendEntity(entity ePEnt, float fChanged)
void void
base_player::base_player(void) base_player::base_player(void)
{ {
} }

320
src/shared/player_pmove.qc Normal file
View file

@ -0,0 +1,320 @@
void
base_player::Physics_Fall(float flDownforce)
{
if (flDownforce > 580) {
#ifdef SERVER
float fFallDamage = (flDownforce - 580) * (100 / (1024 - 580));
Damage_Apply(this, world, fFallDamage, 0, DMG_FALL);
Sound_Play(this, CHAN_VOICE, "player.fall");
#endif
punchangle += [15,0,(input_sequence & 1) ? 15 : -15];
} else if (flDownforce > 400) {
punchangle += [15,0,0];
#ifdef SERVER
Sound_Play(this, CHAN_VOICE, "player.lightfall");
#endif
}
}
void
base_player::Physics_Crouch(void)
{
int iFixCrouch = FALSE;
if (input_buttons & INPUT_BUTTON8) {
flags |= FL_CROUCHING;
} else {
// If we aren't holding down duck anymore and 'attempt' to stand up, prevent it
if (flags & FL_CROUCHING) {
if (PMove_IsStuck(this, [0,0,36], PHY_HULL_MIN, PHY_HULL_MAX) == FALSE) {
flags &= ~FL_CROUCHING;
iFixCrouch = TRUE;
}
} else {
flags &= ~FL_CROUCHING;
}
}
if (flags & FL_CROUCHING) {
setsize(this, PHY_HULL_CROUCHED_MIN, PHY_HULL_CROUCHED_MAX);
view_ofs = PHY_VIEWPOS_CROUCHED;
} else {
setsize(this, PHY_HULL_MIN, PHY_HULL_MAX);
if (iFixCrouch && PMove_IsStuck(this, [0,0,0], PHY_HULL_MIN, PHY_HULL_MAX)) {
for (int i = 0; i < 36; i++) {
origin[2] += 1;
if (PMove_IsStuck(this, [0,0,0], mins, maxs) == FALSE) {
break;
}
}
}
setorigin(this, origin);
view_ofs = PHY_VIEWPOS;
}
}
void
base_player::Physics_Jump(void)
{
/* climb out of substances when underwater */
if (waterlevel >= 2) {
if (watertype == CONTENT_WATER) {
velocity[2] = 100;
} else if (watertype == CONTENT_SLIME) {
velocity[2] = 80;
} else {
velocity[2] = 50;
}
} else {
if (flags & FL_ONGROUND)
velocity[2] += 240;
}
}
/* check if we're elligible to jump */
void
base_player::Physics_CheckJump(float premove)
{
/* unset jump-key whenever it's not set */
if (!(input_buttons & INPUT_BUTTON2)) {
flags |= FL_JUMPRELEASED;
return;
}
if (flags & FL_WATERJUMP)
return;
if (!(flags & FL_ONGROUND))
return;
if (!(flags & FL_JUMPRELEASED))
return;
if (input_buttons & INPUT_BUTTON2 && premove) {
if (velocity[2] < 0) {
velocity[2] = 0;
}
Physics_Jump();
flags &= ~FL_ONGROUND;
flags &= ~FL_JUMPRELEASED;
}
}
/* establish the right size and camera position */
void
base_player::Physics_SetViewParms(void)
{
if (flags & FL_CROUCHING) {
mins = PHY_HULL_CROUCHED_MIN;
maxs = PHY_HULL_CROUCHED_MAX;
view_ofs = PHY_VIEWPOS_CROUCHED;
} else {
mins = PHY_HULL_MIN;
maxs = PHY_HULL_MAX;
view_ofs = PHY_VIEWPOS;
}
setsize(this, mins, maxs);
}
void
base_player::Physics_WaterJump(void)
{
vector vStart;
vector vEnd;
makevectors(angles);
vStart = origin;
vStart[2] = vStart[2] + 8;
v_forward[2] = 0;
normalize(v_forward);
vEnd = vStart + (v_forward * 24);
traceline(vStart, vEnd, TRUE, this);
if (trace_fraction < 1) {
vStart[2] = vStart[2] + maxs[2];
vEnd = vStart + (v_forward * 24);
//movedir = trace_plane_normal * -50;
traceline(vStart, vEnd, TRUE, this);
if (trace_fraction == 1) {
flags |= FL_WATERJUMP;
velocity[2] = 350;
flags &= ~FL_JUMPRELEASED;
return;
}
}
}
/* handle your time underwater */
void
base_player::Physics_WaterMove(void)
{
if (movetype == MOVETYPE_NOCLIP) {
return;
}
#ifdef SERVER
if (health < 0) {
return;
}
/* we've just exited water */
if (waterlevel != 3) {
if (underwater_time < time) {
Sound_Play(this, CHAN_BODY, "player.gasplight");
} else if (underwater_time < time + 9) {
Sound_Play(this, CHAN_BODY, "player.gaspheavy");
}
underwater_time = time + 12;
} else if (underwater_time < time) {
/* we've been underwater... for too long. */
if (pain_time < time) {
Damage_Apply(this, world, 5, DMG_DROWN, 0);
pain_time = time + 1;
}
}
#endif
if (!waterlevel){
if (flags & FL_INWATER) {
#ifdef SERVER
Sound_Play(this, CHAN_BODY, "player.waterexit");
#endif
flags &= ~FL_INWATER;
}
return;
}
#ifdef SERVER
if (watertype == CONTENT_LAVA) {
if (pain_time < time) {
pain_time = time + 0.2;
Damage_Apply(this, world, 10*waterlevel, DMG_BURN, 0);
}
} else if (watertype == CONTENT_SLIME) {
if (pain_time < time) {
pain_time = time + 1;
Damage_Apply(this, world, 4*waterlevel, DMG_ACID, 0);
}
}
#endif
if (!(flags & FL_INWATER)) {
#ifdef SERVER
Sound_Play(this, CHAN_BODY, "player.waterenter");
pain_time = 0;
#endif
flags |= FL_INWATER;
}
/* we might need to apply extra-velocity to get out of water-volumes */
if (waterlevel >= 2) {
Physics_WaterJump();
}
}
float
base_player::Physics_MaxSpeed(void)
{
return (flags & FL_CROUCHING) ? 135 : 270;
}
void
base_player::Physics_InputPreMove(void)
{
/* when pressing the 'use' button, we also walk slower for precision */
if (input_buttons & INPUT_BUTTON5) {
input_movevalues *= 0.25;
}
if (flags & FL_FROZEN || movetype == MOVETYPE_NONE) {
input_movevalues = [0,0,0];
input_buttons &= ~INPUT_BUTTON2;
}
/* move camera up (noclip, fly) when holding jump */
if (input_buttons & INPUT_BUTTON2) {
input_movevalues[2] = 240;
}
/* move camera down (noclip, fly) when holding crouching */
if (input_buttons & INPUT_BUTTON8) {
input_movevalues[2] = -240;
}
}
/* timers get processed here after physics are run */
void
base_player::Physics_InputPostMove(void)
{
float punch;
/* timers, these are predicted and shared across client and server */
w_attack_next = max(0, w_attack_next - input_timelength);
w_idle_next = max(0, w_idle_next - input_timelength);
weapontime += input_timelength;
punch = max(0, 1.0f - (input_timelength * 4));
punchangle[0] *= punch;
punchangle[1] *= punch;
punchangle[2] *= punch;
/* player animation code */
Animation_TimerUpdate((player)this, input_timelength);
Animation_PlayerUpdate((player)this);
/* allow vehicles to prevent weapon logic from happening */
#ifdef SERVER
Vehicle_Input();
#endif
/* weapon/item logic of what the player controls */
Game_Input();
}
/* the main physics routine, the head */
void
base_player::Physics_Run(void)
{
float flFallVel = (flags & FL_ONGROUND) ? 0 : -velocity[2];
/* maxspeed changes when crouching, TODO: make this game-specific */
maxspeed = Physics_MaxSpeed();
/* give us a chance to manipulate input_ globals before running physics */
Physics_InputPreMove();
/* handle drowning and other environmental factors */
Physics_WaterMove();
/* grappling hook stuff */
#if 0
if (pl.hook.skin == 1) {
pl.velocity = (pl.hook.origin - pl.origin);
pl.velocity = (pl.velocity * (1 / (vlen(pl.velocity) / 750)));
}
#endif
Physics_SetViewParms();
Physics_Crouch();
Physics_CheckJump(TRUE);
#ifdef CUSTOMPLAYERPHYSICS
/* QuakeC powered physics (slow, but more customizable) */
PMoveCustom_RunPlayerPhysics(this);
#else
/* fast engine-side player physics */
runstandardplayerphysics(this);
#endif
Physics_CheckJump(FALSE);
if (waterlevel != 0) {
flFallVel = 0;
}
if ((flags & FL_ONGROUND) && movetype == MOVETYPE_WALK) {
Physics_Fall(flFallVel);
}
Physics_InputPostMove();
}

View file

@ -27,16 +27,5 @@ void PMove_StartFrame(void);
#endif #endif
void PMove_Init(void); void PMove_Init(void);
int PMove_Contents(vector org);
float PMove_Gravity(entity ent);
void PMove_Categorize(void);
void PMove_CheckWaterJump(void);
int QPMove_IsStuck(entity eTarget, vector vOffset, vector vecMins, vector vecMaxs);
void PMove_AccelToss(float move_time, float premove);
void PMove_Crouch(float move_time, float premove);
void PMove_AccelWater(float move_time, float premove);
void PMove_AccelLadder(float move_time, float premove, vector wish_dir, float wish_speed);
void PMove_AccelFriction(float move_time, float premove, vector wish_dir, float wish_speed);
void PMove_AccelGravity(float move_time, float premove, vector wish_dir, float wish_speed);
void PMove_Jump(float move_time, float premove);
void PMove_Run(void); void PMove_Run(void);

View file

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2016-2020 Marco Hladik <marco@icculus.org> * Copyright (c) 2016-2021 Marco Hladik <marco@icculus.org>
* *
* Permission to use, copy, modify, and distribute this software for any * Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above * purpose with or without fee is hereby granted, provided that the above
@ -16,6 +16,13 @@
#define CUSTOMPLAYERPHYSICS #define CUSTOMPLAYERPHYSICS
#ifdef CUSTOMPLAYERPHYSICS
void PMoveCustom_Init(void);
#ifdef SERVER
void PMoveCustom_StartFrame(void);
#endif
#endif
#ifndef PMOVE_STEPHEIGHT #ifndef PMOVE_STEPHEIGHT
#define PMOVE_STEPHEIGHT 18 #define PMOVE_STEPHEIGHT 18
#endif #endif
@ -105,197 +112,27 @@ var float autocvar_sv_wateraccelerate = PMOVE_WATERACCELERATE;
var float autocvar_sv_accelerate = PMOVE_ACCELERATE; var float autocvar_sv_accelerate = PMOVE_ACCELERATE;
var float autocvar_sv_maxspeed = PMOVE_MAXSPEED; var float autocvar_sv_maxspeed = PMOVE_MAXSPEED;
/* FIXME: jumptime should use the time global, as time intervals are not
* predictable - decrement it based upon input_timelength */
/* serverinfo keys are the only way both client and server are kept in sync
* about physics variables. so none of the traditional cvars will work.
* otherwise we could not have reliable prediction code for player movement.
*/
void void
PMove_Init(void) PMove_Init(void)
{ {
} #ifdef CUSTOMPLAYERPHYSICS
PMoveCustom_Init();
void #endif
PMove_SetSize(entity targ)
{
if (targ.flags & FL_CROUCHING)
setsize(targ, PHY_HULL_CROUCHED_MIN, PHY_HULL_CROUCHED_MAX);
else
setsize(targ, PHY_HULL_MIN, PHY_HULL_MAX);
} }
#ifdef SERVER #ifdef SERVER
/* we need to network our changes everytime cvars are updated */
void
PMove_UpdateVar(string info, string cv)
{
float d = cvar(cv);
if (serverkeyfloat(info) != d) {
readcmd(sprintf("serverinfo %s %d\n", info, d));
}
}
void void
PMove_StartFrame(void) PMove_StartFrame(void)
{ {
PMove_UpdateVar("phy_stepheight", "sv_stepheight"); #ifdef CUSTOMPLAYERPHYSICS
PMove_UpdateVar("phy_airstepheight", "sv_airstepheight"); PMoveCustom_StartFrame();
PMove_UpdateVar("phy_friction", "sv_friction"); #endif
PMove_UpdateVar("phy_edgefriction", "sv_edgefriction");
PMove_UpdateVar("phy_stopspeed", "sv_stopspeed");
PMove_UpdateVar("phy_gravity", "sv_gravity");
PMove_UpdateVar("phy_airaccelerate", "sv_airaccelerate");
PMove_UpdateVar("phy_wateraccelerate", "sv_wateraccelerate");
PMove_UpdateVar("phy_accelerate", "sv_accelerate");
PMove_UpdateVar("phy_maxspeed", "sv_maxspeed");
} }
#endif #endif
/* pointcontents reimplementation, only way we can effectively trace
* against ladders and liquids that are defined in the game-logic.
*/
int
PMove_Contents(vector org)
{
int oldhitcontents = self.hitcontentsmaski;
self.hitcontentsmaski = -1;
traceline(org, org, TRUE, self);
self.hitcontentsmaski = oldhitcontents;
return trace_endcontentsi;
}
/* used for trigger_gravity type entities */
float
PMove_Gravity(entity ent)
{
if (ent.gravity) {
return serverkeyfloat("phy_gravity") * ent.gravity;
} else {
return serverkeyfloat("phy_gravity");
}
}
void
PMove_SetViewOfs(void)
{
if (self.flags & FL_CROUCHING) {
self.mins = PHY_HULL_CROUCHED_MIN;
self.maxs = PHY_HULL_CROUCHED_MAX;
self.view_ofs = PHY_VIEWPOS_CROUCHED;
} else {
self.mins = PHY_HULL_MIN;
self.maxs = PHY_HULL_MAX;
self.view_ofs = PHY_VIEWPOS;
}
setsize(self, self.mins, self.maxs);
}
/* figure out where we are in the geometry. void, solid, liquid, etc. */
void
PMove_Categorize(void)
{
int contents;
tracebox(self.origin, self.mins, self.maxs, self.origin - [0,0,1], MOVE_NORMAL, self);
if (!trace_startsolid) {
if ((trace_fraction < 1) && (trace_plane_normal[2] > 0.7)) {
self.flags |= FL_ONGROUND;
self.groundentity = trace_ent;
if (self.groundentity) {
self.basevelocity += self.groundentity.velocity;
}
} else {
self.flags &= ~FL_ONGROUND;
}
}
self.flags &= ~FL_WATERJUMP;
/*if (self.basevelocity[2] > 0)
self.flags &= ~FL_ONGROUND;*/
/* ladder content testing */
int oldhitcontents = self.hitcontentsmaski;
self.hitcontentsmaski = CONTENTBIT_FTELADDER;
tracebox(self.origin, self.mins, self.maxs, self.origin, MOVE_NORMAL, self);
self.hitcontentsmaski = oldhitcontents;
if (trace_endcontentsi & CONTENTBIT_FTELADDER) {
self.flags |= FL_ONLADDER;
} else {
self.flags &= ~FL_ONLADDER;
}
contents = PMove_Contents(self.origin + self.mins + [0,0,1]);
if (contents & CONTENTBIT_WATER) {
contents = CONTENT_WATER;
} else if (contents & CONTENTBIT_SLIME) {
contents = CONTENT_SLIME;
} else if (contents & CONTENTBIT_LAVA) {
contents = CONTENT_LAVA;
}
/* how far underwater are we? */
if (contents < CONTENT_SOLID && !(self.flags & FL_ONLADDER)) {
self.watertype = contents;
if (PMove_Contents(self.origin + (self.mins + self.maxs) * 0.5)
& CONTENTBITS_FLUID)
{
if (PMove_Contents(self.origin + self.maxs - '0 0 1')
& CONTENTBITS_FLUID)
{
self.waterlevel = 3;
} else {
self.waterlevel = 2;
}
} else {
self.waterlevel = 1;
}
} else {
self.watertype = CONTENT_EMPTY;
self.waterlevel = 0;
}
}
/* spammed whenever we're near a ledge, getting out of a pool or something */
void
PMove_CheckWaterJump(void)
{
vector vStart;
vector vEnd;
makevectors(self.angles);
vStart = self.origin;
vStart[2] = vStart[2] + 8;
v_forward[2] = 0;
normalize(v_forward);
vEnd = vStart + (v_forward * 24);
traceline(vStart, vEnd, TRUE, self);
if (trace_fraction < 1) {
vStart[2] = vStart[2] + self.maxs[2];
vEnd = vStart + (v_forward * 24);
//self.movedir = trace_plane_normal * -50;
traceline(vStart, vEnd, TRUE, self);
if (trace_fraction == 1) {
self.flags |= FL_WATERJUMP;
self.velocity[2] = 350;
self.flags &= ~FL_JUMPRELEASED;
return;
}
}
}
/* simple bounds check */ /* simple bounds check */
int int
QPMove_IsStuck(entity eTarget, vector vOffset, vector vecMins, vector vecMaxs) PMove_IsStuck(entity eTarget, vector vOffset, vector vecMins, vector vecMaxs)
{ {
vector bound; vector bound;
@ -308,450 +145,6 @@ QPMove_IsStuck(entity eTarget, vector vOffset, vector vecMins, vector vecMaxs)
return trace_startsolid; return trace_startsolid;
} }
void
PMove_AccelToss(float move_time, float premove)
{
self.velocity[2] = self.velocity[2] - (PMove_Gravity(self) * move_time);
}
void
PMove_Crouch(float move_time, float premove)
{
int iFixCrouch = FALSE;
if (input_buttons & INPUT_BUTTON8) {
self.flags |= FL_CROUCHING;
} else {
// If we aren't holding down duck anymore and 'attempt' to stand up, prevent it
if (self.flags & FL_CROUCHING) {
if (QPMove_IsStuck(self, [0,0,36], PHY_HULL_MIN, PHY_HULL_MAX) == FALSE) {
self.flags &= ~FL_CROUCHING;
iFixCrouch = TRUE;
}
} else {
self.flags &= ~FL_CROUCHING;
}
}
if (self.flags & FL_CROUCHING) {
setsize(self, PHY_HULL_CROUCHED_MIN, PHY_HULL_CROUCHED_MAX);
self.view_ofs = PHY_VIEWPOS_CROUCHED;
} else {
setsize(self, PHY_HULL_MIN, PHY_HULL_MAX);
if (iFixCrouch && QPMove_IsStuck(self, [0,0,0], PHY_HULL_MIN, PHY_HULL_MAX)) {
for (int i = 0; i < 36; i++) {
self.origin[2] += 1;
if (QPMove_IsStuck(self, [0,0,0], self.mins, self.maxs) == FALSE) {
break;
}
}
}
setorigin(self, self.origin);
self.view_ofs = PHY_VIEWPOS;
}
}
void
PMove_AccelWater(float move_time, float premove)
{
float flFriction;
float wish_speed;
vector vecWishVel;
self.flags &= ~FL_ONGROUND;
if (input_movevalues == [0,0,0]) {
vecWishVel = [0,0,-60]; // drift towards bottom
} else {
vecWishVel = v_forward * input_movevalues[0];
vecWishVel += v_right * input_movevalues[1];
vecWishVel += v_up * input_movevalues[2];
}
wish_speed = vlen(vecWishVel);
if (wish_speed > self.maxspeed) {
wish_speed = self.maxspeed;
}
wish_speed = wish_speed * 0.7;
// water friction
if (self.velocity != [0,0,0]) {
flFriction = vlen(self.velocity) * (1 - move_time * serverkeyfloat("phy_friction"));
if (flFriction > 0) {
self.velocity = normalize(self.velocity) * flFriction;
} else {
self.velocity = [0,0,0];
}
} else {
flFriction = 0;
}
// water acceleration
if (wish_speed <= flFriction) {
return;
}
flFriction = min(wish_speed - flFriction, serverkeyfloat("phy_wateraccelerate") * wish_speed * move_time);
self.velocity = self.velocity + normalize(vecWishVel) * flFriction;
}
void
PMove_AccelLadder(float move_time, float premove, vector wish_dir, float wish_speed)
{
vector vPlayerVector;
makevectors(input_angles);
vPlayerVector = v_forward;
vPlayerVector = (vPlayerVector * 240);
if (input_movevalues[0] > 0) {
self.velocity = vPlayerVector;
} else {
self.velocity = [0,0,0];
}
}
void
PMove_AccelFriction(float move_time, float premove, vector wish_dir, float wish_speed)
{
float flApplyFriction;
float flFriction;
vector vecTemp;
flApplyFriction = serverkeyfloat("phy_friction");
/* per frame basis friction modifier */
if (self.friction != 0.0f) {
flApplyFriction /= self.friction;
self.friction = 0.0f;
}
/* apply friction */
if (self.velocity[0] || self.velocity[1]) {
vecTemp = self.velocity;
vecTemp[2] = 0;
flFriction = vlen(vecTemp);
// if the leading edge is over a dropoff, increase friction
vecTemp = self.origin + normalize(vecTemp) * 16 + '0 0 1' * PHY_HULL_MIN[2];
traceline(vecTemp, vecTemp + '0 0 -34', TRUE, self);
// apply friction
if (trace_fraction == 1.0) {
if (flFriction < serverkeyfloat("phy_stopspeed")) {
flFriction = 1 - move_time * (serverkeyfloat("phy_stopspeed") / flFriction) * flApplyFriction * serverkeyfloat("phy_edgefriction");
} else {
flFriction = 1 - move_time * flApplyFriction * serverkeyfloat("phy_edgefriction");
}
} else {
if (flFriction < serverkeyfloat("phy_stopspeed")) {
flFriction = 1 - move_time * (serverkeyfloat("phy_stopspeed") / flFriction) * flApplyFriction;
} else {
flFriction = 1 - move_time * flApplyFriction;
}
}
if (flFriction < 0) {
self.velocity = [0,0,0];
} else {
self.velocity = self.velocity * flFriction;
}
}
// acceleration
flFriction = wish_speed - (self.velocity * wish_dir);
if (flFriction > 0) {
self.velocity += wish_dir * min(flFriction, serverkeyfloat("phy_accelerate") * move_time * wish_speed);
}
}
void
PMove_AccelGravity(float move_time, float premove, vector wish_dir, float wish_speed)
{
float flFriction;
/* apply gravity */
self.velocity[2] = self.velocity[2] - (PMove_Gravity(self) * move_time);
if (wish_speed < 30) {
flFriction = wish_speed - (self.velocity * wish_dir);
} else {
flFriction = 30 - (self.velocity * wish_dir);
}
if (flFriction > 0) {
float fric;
fric = min(flFriction, serverkeyfloat("phy_airaccelerate") * wish_speed * move_time);
self.velocity += wish_dir * fric;
}
}
void
PMove_Jump(float move_time, float premove)
{
/* unset jump-key whenever it's not set */
if (!(input_buttons & INPUT_BUTTON2)) {
self.flags |= FL_JUMPRELEASED;
return;
}
if (self.flags & FL_WATERJUMP)
return;
if (!(self.flags & FL_ONGROUND))
return;
if (!(self.flags & FL_JUMPRELEASED))
return;
if (input_buttons & INPUT_BUTTON2 && premove) {
if (self.velocity[2] < 0) {
self.velocity[2] = 0;
}
GamePMove_Jump((player)self);
self.flags &= ~FL_ONGROUND;
self.flags &= ~FL_JUMPRELEASED;
}
}
#ifdef CUSTOMPLAYERPHYSICS
/* two-pass acceleration */
void
PMove_Acceleration(float move_time, float premove)
{
vector vecWishVel;
vector wish_dir;
float wish_speed;
self.jumptime -= move_time;
self.teleport_time -= move_time;
makevectors(input_angles);
/* figure out where we are in the world */
PMove_Categorize();
/* everything but MOVETYPE_NOCLIP has acceleration */
if (self.movetype != MOVETYPE_NOCLIP) {
if (self.movetype == MOVETYPE_TOSS) {
PMove_AccelToss(move_time, premove);
return;
}
if (self.waterlevel >= 2) {
PMove_AccelWater(move_time, premove);
return;
}
}
/*if (self.teleport_time > 0 && input_movevalues[0] < 0) {
vecWishVel = v_right * input_movevalues[1];
} else */ {
/* on the ground, only yaw matters in terms of direction */
if (self.flags & FL_ONGROUND) {
makevectors(input_angles[1] * [0,1,0]);
}
vecWishVel = v_forward * input_movevalues[0] + v_right * input_movevalues[1];
}
if (self.movetype != MOVETYPE_WALK) {
vecWishVel[2] += input_movevalues[2];
} else {
vecWishVel[2] = 0;
}
wish_dir = normalize(vecWishVel);
wish_speed = vlen(vecWishVel);
if (wish_speed > self.maxspeed) {
wish_speed = self.maxspeed;
}
if (self.movetype == MOVETYPE_NOCLIP) {
self.flags &= ~FL_ONGROUND;
self.velocity = wish_dir * wish_speed;
} else {
if (self.flags & FL_ONLADDER) {
PMove_AccelLadder(move_time, premove, wish_dir, wish_speed);
} else if (self.flags & FL_ONGROUND) {
PMove_AccelFriction(move_time, premove, wish_dir, wish_speed);
} else {
PMove_AccelGravity(move_time, premove, wish_dir, wish_speed);
}
}
}
/* touch other solid entities */
void
PMove_DoTouch(entity tother)
{
entity oself = self;
if (tother.touch) {
other = self;
self = tother;
self.touch();
}
self = oself;
}
/* bounce us back off a place normal */
static void
PMove_Rebound(vector normal)
{
self.velocity = self.velocity - normal * (self.velocity * normal);
if (normal[2] > 0.7) {
self.groundentity = trace_ent;
self.flags |= FL_ONGROUND;
}
}
/* brute force unstuck function */
float
PMove_Fix_Origin(void)
{
float x, y, z;
vector norg, oorg = self.origin;
for (z = 0; z < 3; z++) {
norg[2] = oorg[2] + ((z==2)?-1:z)*0.0125;
for (x = 0; x < 3; x++) {
norg[0] = oorg[0] + ((x==2)?-1:x)*0.0125;
for (y = 0; y < 3; y++) {
norg[1] = oorg[1] + ((y==2)?-1:y)*0.0125;
tracebox(norg, self.mins, self.maxs, norg, MOVE_NORMAL, self);
if (!trace_startsolid) {
self.origin = norg;
return (1);
}
}
}
}
return (0);
}
/* move the player based on the given acceleration */
void
PMove_Move(void)
{
vector dest;
vector saved_plane;
float stepped;
float move_time;
int i;
/* no friction for the deceased */
if (self.movetype == MOVETYPE_NOCLIP) {
self.origin = self.origin + self.velocity * input_timelength;
return;
}
/* hacky attempt at base-velocity, this needs to be cleared/wiped at the end */
if (!(self.flags & FL_ONGROUND)) {
self.basevelocity[2] = 0;
}
self.velocity += self.basevelocity;
/* we need to bounce off surfaces (in order to slide along them),
* so we need at 2 attempts */
for (i = 3, move_time = input_timelength; move_time > 0 && i; i--) {
dest = self.origin + (self.velocity * move_time);
tracebox(self.origin, self.mins, self.maxs, dest, MOVE_NORMAL, self);
if (trace_startsolid) {
if (!PMove_Fix_Origin()) {
return;
}
continue;
}
self.origin = trace_endpos;
if (trace_fraction > 1) {
break;
}
saved_plane = trace_plane_normal;
move_time -= move_time * trace_fraction;
if (move_time) {
/* step up if we can */
trace_endpos = self.origin;
if (self.flags & FL_ONGROUND) {
trace_endpos[2] += serverkeyfloat("phy_stepheight");
} else {
trace_endpos[2] += serverkeyfloat("phy_airstepheight");
}
tracebox(self.origin, self.mins, self.maxs, trace_endpos, MOVE_NORMAL, self);
stepped = trace_endpos[2] - self.origin[2];
float roof_fraction = trace_fraction;
vector roof_plane_normal = trace_plane_normal;
dest = trace_endpos + self.velocity*move_time;
dest[2] = trace_endpos[2]; /*only horizontally*/
/* move forwards */
tracebox(trace_endpos, self.mins, self.maxs, dest, MOVE_NORMAL, self);
/* if we got anywhere, make this raised-step move count */
if (trace_fraction != 0) {
float fwfrac = trace_fraction;
vector fwplane = trace_plane_normal;
/* move down */
dest = trace_endpos;
dest[2] -= stepped + 1;
tracebox(trace_endpos, self.mins, self.maxs, dest, MOVE_NORMAL, self);
if (trace_fraction < 1 && trace_plane_normal[2] > 0.7f) {
move_time -= move_time * fwfrac;
/* bounce off the ceiling */
if (roof_fraction < 1) {
PMove_Rebound(roof_plane_normal);
}
if (trace_fraction < 1) {
PMove_Rebound(trace_plane_normal);
} else if (fwfrac < 1) {
PMove_Rebound(fwplane);
}
self.origin = trace_endpos;
continue;
}
}
}
/* stepping failed, just bounce off */
PMove_Rebound(saved_plane);
PMove_DoTouch(trace_ent);
}
/* touch whatever is below */
if (self.flags & FL_ONGROUND) {
dest = self.origin;
dest[2] -= serverkeyfloat("phy_stepheight");
tracebox(self.origin, self.mins, self.maxs, dest, MOVE_NORMAL, self);
if (trace_fraction >= 1) {
return;
}
/*if (trace_startsolid) {
if (!PMove_Fix_Origin()) {
return;
}
}*/
PMove_DoTouch(trace_ent);
self.groundentity = trace_ent;
}
/* make sure that the basevelocity we've applied is discarded by next frame */
self.velocity -= self.basevelocity;
}
#endif
/* it all starts here, this function is called by both CLIENT and SERVER for /* it all starts here, this function is called by both CLIENT and SERVER for
obvious prediction purposes. The SERVER will usually do this in the obvious prediction purposes. The SERVER will usually do this in the
Game_RunClientCommand function and the CLIENT will do so in both the Game_RunClientCommand function and the CLIENT will do so in both the
@ -759,104 +152,7 @@ PMove_Move(void)
void void
PMove_Run(void) PMove_Run(void)
{ {
float punch;
player pl = (player)self; player pl = (player)self;
pl.Physics_Run();
float flFallVel = (self.flags & FL_ONGROUND) ? 0 : -self.velocity[2];
/* maxspeed changes when crouching, TODO: make this game-specific */
self.maxspeed = GamePMove_Maxspeed((player)self);
/* when pressing the 'use' button, we also walk slower for precision */
if (input_buttons & INPUT_BUTTON5) {
input_movevalues *= 0.25;
}
if (pl.flags & FL_FROZEN || pl.movetype == MOVETYPE_NONE) {
input_movevalues = [0,0,0];
input_buttons &= ~INPUT_BUTTON2;
}
/* move camera up (noclip, fly) when holding jump */
if (input_buttons & INPUT_BUTTON2) {
input_movevalues[2] = 240;
}
/* move camera down (noclip, fly) when holding crouching */
if (input_buttons & INPUT_BUTTON8) {
input_movevalues[2] = -240;
}
/* establish which water elements we're dealing in */
GamePMove_WaterMove(pl);
/* we might need to apply extra-velocity to get out of water-volumes */
if (self.waterlevel >= 2) {
PMove_CheckWaterJump();
}
/* grappling hook stuff */
if (pl.hook.skin == 1) {
pl.velocity = (pl.hook.origin - pl.origin);
pl.velocity = (pl.velocity * (1 / (vlen(pl.velocity) / 750)));
}
PMove_SetViewOfs();
PMove_Crouch(input_timelength, TRUE);
PMove_Jump(input_timelength, TRUE);
/* call accelerate before and after the actual move,
* with half the move each time. this reduces framerate dependence.
* and makes controlling jumps slightly easier */
#ifdef CUSTOMPLAYERPHYSICS
PMove_Acceleration(input_timelength / 2, TRUE);
PMove_Move();
PMove_Acceleration(input_timelength / 2, FALSE);
/* NOTE: should clip to network precision here if lower than a float */
self.angles = input_angles;
self.angles[0] *= -0.333;
/* clear base-velocity and ground-entity */
self.basevelocity = [0,0,0];
self.groundentity = __NULL__;
/* activate any SOLID_TRIGGER entities */
touchtriggers();
#else
/* fix gravity */
if (self.gravity == 0.0f)
self.gravity = 1.0f;
/* fast engine-side player physics */
runstandardplayerphysics(self);
#endif
if (self.waterlevel != 0) {
flFallVel = 0;
}
if ((self.flags & FL_ONGROUND) && self.movetype == MOVETYPE_WALK) {
GamePMove_Fall((player)self, flFallVel);
}
/* timers, these are predicted and shared across client and server */
pl.w_attack_next = max(0, pl.w_attack_next - input_timelength);
pl.w_idle_next = max(0, pl.w_idle_next - input_timelength);
pl.weapontime += input_timelength;
punch = max(0, 1.0f - (input_timelength * 4));
pl.punchangle[0] *= punch;
pl.punchangle[1] *= punch;
pl.punchangle[2] *= punch;
/* player animation code */
Animation_TimerUpdate(pl, input_timelength);
Animation_PlayerUpdate(pl);
/* allow vehicles to prevent weapon logic from happening */
#ifdef SERVER
Vehicle_Input();
#endif
/* weapon/item logic of what the player controls */
Game_Input();
} }

550
src/shared/pmove_custom.qc Normal file
View file

@ -0,0 +1,550 @@
/*
* Copyright (c) 2016-2021 Marco Hladik <marco@icculus.org>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER
* IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
* OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#ifdef CUSTOMPLAYERPHYSICS
void
PMoveCustom_Init(void)
{
}
#ifdef SERVER
/* we need to network our changes everytime cvars are updated */
void
PMoveCustom_UpdateVar(string info, string cv)
{
float d = cvar(cv);
if (serverkeyfloat(info) != d) {
readcmd(sprintf("serverinfo %s %d\n", info, d));
}
}
void
PMoveCustom_StartFrame(void)
{
PMoveCustom_UpdateVar("phy_stepheight", "sv_stepheight");
PMoveCustom_UpdateVar("phy_airstepheight", "sv_airstepheight");
PMoveCustom_UpdateVar("phy_friction", "sv_friction");
PMoveCustom_UpdateVar("phy_edgefriction", "sv_edgefriction");
PMoveCustom_UpdateVar("phy_stopspeed", "sv_stopspeed");
PMoveCustom_UpdateVar("phy_gravity", "sv_gravity");
PMoveCustom_UpdateVar("phy_airaccelerate", "sv_airaccelerate");
PMoveCustom_UpdateVar("phy_wateraccelerate", "sv_wateraccelerate");
PMoveCustom_UpdateVar("phy_accelerate", "sv_accelerate");
PMoveCustom_UpdateVar("phy_maxspeed", "sv_maxspeed");
}
#endif
/* pointcontents reimplementation, only way we can effectively trace
* against ladders and liquids that are defined in the game-logic.
*/
int
PMoveCustom_Contents(vector org)
{
int oldhitcontents = self.hitcontentsmaski;
self.hitcontentsmaski = -1;
traceline(org, org, TRUE, self);
self.hitcontentsmaski = oldhitcontents;
return trace_endcontentsi;
}
/* used for trigger_gravity type entities */
float
PMoveCustom_Gravity(entity ent)
{
if (ent.gravity) {
return serverkeyfloat("phy_gravity") * ent.gravity;
} else {
return serverkeyfloat("phy_gravity");
}
}
/* figure out where we are in the geometry. void, solid, liquid, etc. */
void
PMoveCustom_Categorize(void)
{
int contents;
tracebox(self.origin, self.mins, self.maxs, self.origin - [0,0,1], MOVE_NORMAL, self);
if (!trace_startsolid) {
if ((trace_fraction < 1) && (trace_plane_normal[2] > 0.7)) {
self.flags |= FL_ONGROUND;
self.groundentity = trace_ent;
if (self.groundentity) {
self.basevelocity += self.groundentity.velocity;
}
} else {
self.flags &= ~FL_ONGROUND;
}
}
self.flags &= ~FL_WATERJUMP;
/*if (self.basevelocity[2] > 0)
self.flags &= ~FL_ONGROUND;*/
/* ladder content testing */
int oldhitcontents = self.hitcontentsmaski;
self.hitcontentsmaski = CONTENTBIT_FTELADDER;
tracebox(self.origin, self.mins, self.maxs, self.origin, MOVE_NORMAL, self);
self.hitcontentsmaski = oldhitcontents;
if (trace_endcontentsi & CONTENTBIT_FTELADDER) {
self.flags |= FL_ONLADDER;
} else {
self.flags &= ~FL_ONLADDER;
}
contents = PMoveCustom_Contents(self.origin + self.mins + [0,0,1]);
if (contents & CONTENTBIT_WATER) {
contents = CONTENT_WATER;
} else if (contents & CONTENTBIT_SLIME) {
contents = CONTENT_SLIME;
} else if (contents & CONTENTBIT_LAVA) {
contents = CONTENT_LAVA;
}
/* how far underwater are we? */
if (contents < CONTENT_SOLID && !(self.flags & FL_ONLADDER)) {
self.watertype = contents;
if (PMoveCustom_Contents(self.origin + (self.mins + self.maxs) * 0.5)
& CONTENTBITS_FLUID)
{
if (PMoveCustom_Contents(self.origin + self.maxs - [0,0,1])
& CONTENTBITS_FLUID)
{
self.waterlevel = 3;
} else {
self.waterlevel = 2;
}
} else {
self.waterlevel = 1;
}
} else {
self.watertype = CONTENT_EMPTY;
self.waterlevel = 0;
}
}
void
PMoveCustom_AccelToss(float move_time, float premove)
{
self.velocity[2] = self.velocity[2] - (PMoveCustom_Gravity(self) * move_time);
}
void
PMoveCustom_AccelWater(float move_time, float premove)
{
float flFriction;
float wish_speed;
vector vecWishVel;
self.flags &= ~FL_ONGROUND;
if (input_movevalues == [0,0,0]) {
vecWishVel = [0,0,-60]; // drift towards bottom
} else {
vecWishVel = v_forward * input_movevalues[0];
vecWishVel += v_right * input_movevalues[1];
vecWishVel += v_up * input_movevalues[2];
}
wish_speed = vlen(vecWishVel);
if (wish_speed > self.maxspeed) {
wish_speed = self.maxspeed;
}
wish_speed = wish_speed * 0.7;
// water friction
if (self.velocity != [0,0,0]) {
flFriction = vlen(self.velocity) * (1 - move_time * serverkeyfloat("phy_friction"));
if (flFriction > 0) {
self.velocity = normalize(self.velocity) * flFriction;
} else {
self.velocity = [0,0,0];
}
} else {
flFriction = 0;
}
// water acceleration
if (wish_speed <= flFriction) {
return;
}
flFriction = min(wish_speed - flFriction, serverkeyfloat("phy_wateraccelerate") * wish_speed * move_time);
self.velocity = self.velocity + normalize(vecWishVel) * flFriction;
}
void
PMoveCustom_AccelLadder(float move_time, float premove, vector wish_dir, float wish_speed)
{
vector vPlayerVector;
makevectors(input_angles);
vPlayerVector = v_forward;
vPlayerVector = (vPlayerVector * 240);
if (input_movevalues[0] > 0) {
self.velocity = vPlayerVector;
} else {
self.velocity = [0,0,0];
}
}
void
PMoveCustom_AccelFriction(float move_time, float premove, vector wish_dir, float wish_speed)
{
float flApplyFriction;
float flFriction;
vector vecTemp;
flApplyFriction = serverkeyfloat("phy_friction");
/* per frame basis friction modifier */
if (self.friction != 0.0f) {
flApplyFriction /= self.friction;
self.friction = 0.0f;
}
/* apply friction */
if (self.velocity[0] || self.velocity[1]) {
vecTemp = self.velocity;
vecTemp[2] = 0;
flFriction = vlen(vecTemp);
// if the leading edge is over a dropoff, increase friction
vecTemp = self.origin + normalize(vecTemp) * 16 + [0,0,1] * PHY_HULL_MIN[2];
traceline(vecTemp, vecTemp + [0,0,-34], TRUE, self);
// apply friction
if (trace_fraction == 1.0) {
if (flFriction < serverkeyfloat("phy_stopspeed")) {
flFriction = 1 - move_time * (serverkeyfloat("phy_stopspeed") / flFriction) * flApplyFriction * serverkeyfloat("phy_edgefriction");
} else {
flFriction = 1 - move_time * flApplyFriction * serverkeyfloat("phy_edgefriction");
}
} else {
if (flFriction < serverkeyfloat("phy_stopspeed")) {
flFriction = 1 - move_time * (serverkeyfloat("phy_stopspeed") / flFriction) * flApplyFriction;
} else {
flFriction = 1 - move_time * flApplyFriction;
}
}
if (flFriction < 0) {
self.velocity = [0,0,0];
} else {
self.velocity = self.velocity * flFriction;
}
}
// acceleration
flFriction = wish_speed - (self.velocity * wish_dir);
if (flFriction > 0) {
self.velocity += wish_dir * min(flFriction, serverkeyfloat("phy_accelerate") * move_time * wish_speed);
}
}
void
PMoveCustom_AccelGravity(float move_time, float premove, vector wish_dir, float wish_speed)
{
float flFriction;
/* apply gravity */
self.velocity[2] = self.velocity[2] - (PMoveCustom_Gravity(self) * move_time);
if (wish_speed < 30) {
flFriction = wish_speed - (self.velocity * wish_dir);
} else {
flFriction = 30 - (self.velocity * wish_dir);
}
if (flFriction > 0) {
float fric;
fric = min(flFriction, serverkeyfloat("phy_airaccelerate") * wish_speed * move_time);
self.velocity += wish_dir * fric;
}
}
/* two-pass acceleration */
void
PMoveCustom_Acceleration(float move_time, float premove)
{
vector vecWishVel;
vector wish_dir;
float wish_speed;
self.jumptime -= move_time;
self.teleport_time -= move_time;
makevectors(input_angles);
/* figure out where we are in the world */
PMoveCustom_Categorize();
/* everything but MOVETYPE_NOCLIP has acceleration */
if (self.movetype != MOVETYPE_NOCLIP) {
if (self.movetype == MOVETYPE_TOSS) {
PMoveCustom_AccelToss(move_time, premove);
return;
}
if (self.waterlevel >= 2) {
PMoveCustom_AccelWater(move_time, premove);
return;
}
}
/*if (self.teleport_time > 0 && input_movevalues[0] < 0) {
vecWishVel = v_right * input_movevalues[1];
} else */ {
/* on the ground, only yaw matters in terms of direction */
if (self.flags & FL_ONGROUND) {
makevectors(input_angles[1] * [0,1,0]);
}
vecWishVel = v_forward * input_movevalues[0] + v_right * input_movevalues[1];
}
if (self.movetype != MOVETYPE_WALK) {
vecWishVel[2] += input_movevalues[2];
} else {
vecWishVel[2] = 0;
}
wish_dir = normalize(vecWishVel);
wish_speed = vlen(vecWishVel);
if (wish_speed > self.maxspeed) {
wish_speed = self.maxspeed;
}
if (self.movetype == MOVETYPE_NOCLIP) {
self.flags &= ~FL_ONGROUND;
self.velocity = wish_dir * wish_speed;
} else {
if (self.flags & FL_ONLADDER) {
PMoveCustom_AccelLadder(move_time, premove, wish_dir, wish_speed);
} else if (self.flags & FL_ONGROUND) {
PMoveCustom_AccelFriction(move_time, premove, wish_dir, wish_speed);
} else {
PMoveCustom_AccelGravity(move_time, premove, wish_dir, wish_speed);
}
}
}
/* touch other solid entities */
void
PMoveCustom_DoTouch(entity tother)
{
entity oself = self;
if (tother.touch) {
other = self;
self = tother;
self.touch();
}
self = oself;
}
/* bounce us back off a place normal */
static void
PMoveCustom_Rebound(vector normal)
{
self.velocity = self.velocity - normal * (self.velocity * normal);
if (normal[2] > 0.7) {
self.groundentity = trace_ent;
self.flags |= FL_ONGROUND;
}
}
/* brute force unstuck function */
float
PMoveCustom_Fix_Origin(void)
{
float x, y, z;
vector norg, oorg = self.origin;
for (z = 0; z < 3; z++) {
norg[2] = oorg[2] + ((z==2)?-1:z)*0.0125;
for (x = 0; x < 3; x++) {
norg[0] = oorg[0] + ((x==2)?-1:x)*0.0125;
for (y = 0; y < 3; y++) {
norg[1] = oorg[1] + ((y==2)?-1:y)*0.0125;
tracebox(norg, self.mins, self.maxs, norg, MOVE_NORMAL, self);
if (!trace_startsolid) {
self.origin = norg;
return (1);
}
}
}
}
return (0);
}
/* move the player based on the given acceleration */
void
PMoveCustom_Move(void)
{
vector dest;
vector saved_plane;
float stepped;
float move_time;
int i;
/* no friction for the deceased */
if (self.movetype == MOVETYPE_NOCLIP) {
self.origin = self.origin + self.velocity * input_timelength;
return;
}
/* hacky attempt at base-velocity, this needs to be cleared/wiped at the end */
if (!(self.flags & FL_ONGROUND)) {
self.basevelocity[2] = 0;
}
self.velocity += self.basevelocity;
/* we need to bounce off surfaces (in order to slide along them),
* so we need at 2 attempts */
for (i = 3, move_time = input_timelength; move_time > 0 && i; i--) {
dest = self.origin + (self.velocity * move_time);
tracebox(self.origin, self.mins, self.maxs, dest, MOVE_NORMAL, self);
if (trace_startsolid) {
if (!PMoveCustom_Fix_Origin()) {
return;
}
continue;
}
self.origin = trace_endpos;
if (trace_fraction > 1) {
break;
}
saved_plane = trace_plane_normal;
move_time -= move_time * trace_fraction;
if (move_time) {
/* step up if we can */
trace_endpos = self.origin;
if (self.flags & FL_ONGROUND) {
trace_endpos[2] += serverkeyfloat("phy_stepheight");
} else {
trace_endpos[2] += serverkeyfloat("phy_airstepheight");
}
tracebox(self.origin, self.mins, self.maxs, trace_endpos, MOVE_NORMAL, self);
stepped = trace_endpos[2] - self.origin[2];
float roof_fraction = trace_fraction;
vector roof_plane_normal = trace_plane_normal;
dest = trace_endpos + self.velocity*move_time;
dest[2] = trace_endpos[2]; /*only horizontally*/
/* move forwards */
tracebox(trace_endpos, self.mins, self.maxs, dest, MOVE_NORMAL, self);
/* if we got anywhere, make this raised-step move count */
if (trace_fraction != 0) {
float fwfrac = trace_fraction;
vector fwplane = trace_plane_normal;
/* move down */
dest = trace_endpos;
dest[2] -= stepped + 1;
tracebox(trace_endpos, self.mins, self.maxs, dest, MOVE_NORMAL, self);
if (trace_fraction < 1 && trace_plane_normal[2] > 0.7f) {
move_time -= move_time * fwfrac;
/* bounce off the ceiling */
if (roof_fraction < 1) {
PMoveCustom_Rebound(roof_plane_normal);
}
if (trace_fraction < 1) {
PMoveCustom_Rebound(trace_plane_normal);
} else if (fwfrac < 1) {
PMoveCustom_Rebound(fwplane);
}
self.origin = trace_endpos;
continue;
}
}
}
/* stepping failed, just bounce off */
PMoveCustom_Rebound(saved_plane);
PMoveCustom_DoTouch(trace_ent);
}
/* touch whatever is below */
if (self.flags & FL_ONGROUND) {
dest = self.origin;
dest[2] -= serverkeyfloat("phy_stepheight");
tracebox(self.origin, self.mins, self.maxs, dest, MOVE_NORMAL, self);
if (trace_fraction >= 1) {
return;
}
/*if (trace_startsolid) {
if (!PMoveCustom_Fix_Origin()) {
return;
}
}*/
PMoveCustom_DoTouch(trace_ent);
self.groundentity = trace_ent;
}
/* make sure that the basevelocity we've applied is discarded by next frame */
self.velocity -= self.basevelocity;
}
/* this is called for when we want to run the custom QC player physics */
void
PMoveCustom_RunPlayerPhysics(entity target)
{
entity oldself = self;
self = target;
/* call accelerate before and after the actual move,
* with half the move each time. this reduces framerate dependence.
* and makes controlling jumps slightly easier */
PMoveCustom_Acceleration(input_timelength / 2, TRUE);
PMoveCustom_Move();
PMoveCustom_Acceleration(input_timelength / 2, FALSE);
/* NOTE: should clip to network precision here if lower than a float */
self.angles = input_angles;
self.angles[0] *= -0.333;
/* clear base-velocity and ground-entity */
self.basevelocity = [0,0,0];
self.groundentity = __NULL__;
/* activate any SOLID_TRIGGER entities */
touchtriggers();
self = oldself;
}
#endif