diff --git a/src/client/predict.qc b/src/client/predict.qc index 2eded587..4b9f2c8a 100644 --- a/src/client/predict.qc +++ b/src/client/predict.qc @@ -14,8 +14,6 @@ * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ -void PMove_SetSize(entity targ); - /* ================= Predict_EntityUpdate @@ -44,7 +42,7 @@ Predict_EntityUpdate(player pl, float new) pl.customphysics = __NULL__; } - PMove_SetSize(pl); + pl.Physics_SetViewParms(); pl.drawmask = MASK_ENGINE; return; } diff --git a/src/shared/include.src b/src/shared/include.src index 4f0b3287..908e3e01 100644 --- a/src/shared/include.src +++ b/src/shared/include.src @@ -1,7 +1,9 @@ #includelist spectator.qc pmove.qc +pmove_custom.qc sound.qc math.qc player.qc +player_pmove.qc #endlist diff --git a/src/shared/player.h b/src/shared/player.h index 9533f6b0..49ab0886 100644 --- a/src/shared/player.h +++ b/src/shared/player.h @@ -75,6 +75,18 @@ 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 int sequence; float pitch; diff --git a/src/shared/player.qc b/src/shared/player.qc index d29fa0c0..61a6ef0f 100644 --- a/src/shared/player.qc +++ b/src/shared/player.qc @@ -35,7 +35,7 @@ base_player::ReceiveEntity(float new, float fl) /* HACK: we need to make this more reliable */ if (fl == UPDATE_ALL) { /* we respawned */ - gravity = __NULL__; + gravity = 1.0f; } if (fl & PLAYER_MODELINDEX) @@ -344,5 +344,4 @@ base_player::SendEntity(entity ePEnt, float fChanged) void base_player::base_player(void) { - } diff --git a/src/shared/player_pmove.qc b/src/shared/player_pmove.qc new file mode 100644 index 00000000..93dcd13b --- /dev/null +++ b/src/shared/player_pmove.qc @@ -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(); +} diff --git a/src/shared/pmove.h b/src/shared/pmove.h index d968d45f..a9bd2983 100644 --- a/src/shared/pmove.h +++ b/src/shared/pmove.h @@ -27,16 +27,5 @@ void PMove_StartFrame(void); #endif 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); diff --git a/src/shared/pmove.qc b/src/shared/pmove.qc index e66f14e5..e42fb490 100644 --- a/src/shared/pmove.qc +++ b/src/shared/pmove.qc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016-2020 Marco Hladik + * Copyright (c) 2016-2021 Marco Hladik * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -16,6 +16,13 @@ #define CUSTOMPLAYERPHYSICS +#ifdef CUSTOMPLAYERPHYSICS + void PMoveCustom_Init(void); + #ifdef SERVER + void PMoveCustom_StartFrame(void); + #endif +#endif + #ifndef PMOVE_STEPHEIGHT #define PMOVE_STEPHEIGHT 18 #endif @@ -105,197 +112,27 @@ var float autocvar_sv_wateraccelerate = PMOVE_WATERACCELERATE; var float autocvar_sv_accelerate = PMOVE_ACCELERATE; 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 PMove_Init(void) { -} - -void -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 CUSTOMPLAYERPHYSICS + PMoveCustom_Init(); +#endif } #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 PMove_StartFrame(void) { - PMove_UpdateVar("phy_stepheight", "sv_stepheight"); - PMove_UpdateVar("phy_airstepheight", "sv_airstepheight"); - PMove_UpdateVar("phy_friction", "sv_friction"); - 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"); +#ifdef CUSTOMPLAYERPHYSICS + PMoveCustom_StartFrame(); +#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 */ int -QPMove_IsStuck(entity eTarget, vector vOffset, vector vecMins, vector vecMaxs) +PMove_IsStuck(entity eTarget, vector vOffset, vector vecMins, vector vecMaxs) { vector bound; @@ -308,450 +145,6 @@ QPMove_IsStuck(entity eTarget, vector vOffset, vector vecMins, vector vecMaxs) 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 obvious prediction purposes. The SERVER will usually do this in the Game_RunClientCommand function and the CLIENT will do so in both the @@ -759,104 +152,7 @@ PMove_Move(void) void PMove_Run(void) { - float punch; + player pl = (player)self; - - 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(); + pl.Physics_Run(); } diff --git a/src/shared/pmove_custom.qc b/src/shared/pmove_custom.qc new file mode 100644 index 00000000..44353c9d --- /dev/null +++ b/src/shared/pmove_custom.qc @@ -0,0 +1,550 @@ +/* + * Copyright (c) 2016-2021 Marco Hladik + * + * 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