mirror of
https://github.com/jmarshall23/DoomRTX.git
synced 2026-08-13 08:41:28 +02:00
2150 lines
55 KiB
C++
2150 lines
55 KiB
C++
/*
|
|
===========================================================================
|
|
|
|
IceTech GPL Source Code
|
|
Copyright (C) 2026 Justin Marshall
|
|
|
|
This file is part of the IceTech GPL Source Code (?IceTech Source Code?).
|
|
|
|
IceTech Source Code is free software: you can redistribute it and/or modify
|
|
it under the terms of the GNU General Public License as published by
|
|
the Free Software Foundation, either version 3 of the License, or
|
|
(at your option) any later version.
|
|
|
|
IceTech Source Code is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with IceTech Source Code. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
In addition, the IceTech Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the IceTech Source Code. If not, please request a copy in writing from id Software at the address below.
|
|
|
|
If you have questions concerning this license or the applicable additional terms, you may contact in writing Justin Marshall, justinmarshall20@gmail.com
|
|
|
|
===========================================================================
|
|
*/
|
|
|
|
#include "precompiled.h"
|
|
#pragma hdrstop
|
|
|
|
#include "../Game_local.h"
|
|
|
|
|
|
// movement parameters
|
|
const float PM_STOPSPEED = 100.0f;
|
|
const float PM_SWIMSCALE = 0.5f;
|
|
const float PM_LADDERSPEED = 100.0f;
|
|
const float PM_STEPSCALE = 1.0f;
|
|
|
|
const float PM_ACCELERATE = 10.0f;
|
|
const float PM_AIRACCELERATE = 1.0f;
|
|
const float PM_WATERACCELERATE = 4.0f;
|
|
const float PM_FLYACCELERATE = 8.0f;
|
|
|
|
const float PM_FRICTION = 6.0f;
|
|
const float PM_AIRFRICTION = 0.2f;
|
|
const float PM_WATERFRICTION = 1.0f;
|
|
const float PM_FLYFRICTION = 6.0f;
|
|
const float PM_NOCLIPFRICTION = 12.0f;
|
|
|
|
const float MIN_WALK_NORMAL = 0.7f; // can't walk on very steep slopes
|
|
const float OVERCLIP = 1.001f;
|
|
|
|
// movementFlags
|
|
const int PMF_DUCKED = 1; // set when ducking
|
|
const int PMF_JUMPED = 2; // set when the player jumped this frame
|
|
const int PMF_STEPPED_UP = 4; // set when the player stepped up this frame
|
|
const int PMF_STEPPED_DOWN = 8; // set when the player stepped down this frame
|
|
const int PMF_JUMP_HELD = 16; // set when jump button is held down
|
|
const int PMF_TIME_LAND = 32; // movementTime is time before rejump
|
|
const int PMF_TIME_KNOCKBACK = 64; // movementTime is an air-accelerate only time
|
|
const int PMF_TIME_WATERJUMP = 128; // movementTime is waterjump
|
|
const int PMF_ALL_TIMES = (PMF_TIME_WATERJUMP | PMF_TIME_LAND | PMF_TIME_KNOCKBACK);
|
|
|
|
int c_pmove = 0;
|
|
|
|
|
|
/*
|
|
==================
|
|
PM_CmdScale
|
|
|
|
ETQW-style command scaling helper. The original Doom 3 CmdScale only
|
|
ignored upmove while walking; air movement also has no vertical control,
|
|
so jump/crouch input should not reduce horizontal air wishspeed.
|
|
==================
|
|
*/
|
|
static float PM_CmdScale(const usercmd_t& cmd, const float playerSpeed, const bool noVertical) {
|
|
int max;
|
|
float total;
|
|
int forwardmove;
|
|
int rightmove;
|
|
int upmove;
|
|
|
|
forwardmove = cmd.forwardmove;
|
|
rightmove = cmd.rightmove;
|
|
upmove = noVertical ? 0 : cmd.upmove;
|
|
|
|
max = abs(forwardmove);
|
|
if (abs(rightmove) > max) {
|
|
max = abs(rightmove);
|
|
}
|
|
if (abs(upmove) > max) {
|
|
max = abs(upmove);
|
|
}
|
|
|
|
if (!max) {
|
|
return 0.0f;
|
|
}
|
|
|
|
total = idMath::Sqrt((float)forwardmove * forwardmove + rightmove * rightmove + upmove * upmove);
|
|
return (float)playerSpeed * max / (127.0f * total);
|
|
}
|
|
|
|
/*
|
|
==================
|
|
PM_IsWalkablePlane
|
|
==================
|
|
*/
|
|
static bool PM_IsWalkablePlane(const idVec3& normal, const idVec3& gravityNormal) {
|
|
return (normal * -gravityNormal) >= MIN_WALK_NORMAL;
|
|
}
|
|
|
|
/*
|
|
==================
|
|
PM_AdjustVertically
|
|
|
|
Given a walkable plane normal, adjust the vertical component so the vector lies
|
|
in the plane without changing the horizontal direction. This is the portable
|
|
part of ETQW's idPhysics_Player::AdjustVertically and replaces several slope
|
|
speed-loss cases caused by plain ProjectOntoPlane.
|
|
==================
|
|
*/
|
|
static idVec3 PM_AdjustVertically(const idVec3& normal, const idVec3& _in, const idVec3& gravityNormal) {
|
|
idVec3 out;
|
|
idVec3 in = _in;
|
|
idVec3 up = -gravityNormal;
|
|
idVec3 velocityPlane = in.Cross(up);
|
|
|
|
// same horizontal direction and in the walkable plane
|
|
out = velocityPlane.Cross(normal);
|
|
|
|
// make sure direction is ok
|
|
if (in * out < 0.0f) {
|
|
out = -out;
|
|
}
|
|
|
|
// maintain an overclip on the vertical component so rounding problems do not
|
|
// put us back into solids. For normal Doom 3 gravity this matches ETQW's z test.
|
|
float upComponent = out * up;
|
|
idVec3 vertical = up * upComponent;
|
|
idVec3 horizontal = out - vertical;
|
|
if (upComponent >= 0.0f) {
|
|
vertical *= OVERCLIP;
|
|
}
|
|
else {
|
|
vertical /= OVERCLIP;
|
|
}
|
|
return horizontal + vertical;
|
|
}
|
|
|
|
/*
|
|
============
|
|
idPhysics_Player::CmdScale
|
|
|
|
Returns the scale factor to apply to cmd movements
|
|
This allows the clients to use axial -127 to 127 values for all directions
|
|
without getting a sqrt(2) distortion in speed.
|
|
============
|
|
*/
|
|
float idPhysics_Player::CmdScale(const usercmd_t& cmd) const {
|
|
// Preserve Doom 3's public behavior for callers that do not opt in to the
|
|
// explicit noVertical path: walking ignores upmove, non-walking modes keep it.
|
|
return PM_CmdScale(cmd, playerSpeed, walking);
|
|
}
|
|
|
|
/*
|
|
==============
|
|
idPhysics_Player::Accelerate
|
|
|
|
Handles user intended acceleration
|
|
==============
|
|
*/
|
|
void idPhysics_Player::Accelerate(const idVec3& wishdir, const float wishspeed, const float accel) {
|
|
#if 1
|
|
// q2 style
|
|
float addspeed, accelspeed, currentspeed;
|
|
|
|
currentspeed = current.velocity * wishdir;
|
|
addspeed = wishspeed - currentspeed;
|
|
if (addspeed <= 0) {
|
|
return;
|
|
}
|
|
accelspeed = accel * frametime * wishspeed;
|
|
if (accelspeed > addspeed) {
|
|
accelspeed = addspeed;
|
|
}
|
|
|
|
current.velocity += accelspeed * wishdir;
|
|
#else
|
|
// proper way (avoids strafe jump maxspeed bug), but feels bad
|
|
idVec3 wishVelocity;
|
|
idVec3 pushDir;
|
|
float pushLen;
|
|
float canPush;
|
|
|
|
wishVelocity = wishdir * wishspeed;
|
|
pushDir = wishVelocity - current.velocity;
|
|
pushLen = pushDir.Normalize();
|
|
|
|
canPush = accel * frametime * wishspeed;
|
|
if (canPush > pushLen) {
|
|
canPush = pushLen;
|
|
}
|
|
|
|
current.velocity += canPush * pushDir;
|
|
#endif
|
|
}
|
|
|
|
/*
|
|
==================
|
|
idPhysics_Player::SlideMove
|
|
|
|
Returns true if the velocity was clipped in some way
|
|
==================
|
|
*/
|
|
#define MAX_CLIP_PLANES 5
|
|
|
|
bool idPhysics_Player::SlideMove(bool gravity, bool stepUp, bool stepDown, bool push) {
|
|
int i, j, k, pushFlags;
|
|
int bumpcount, numbumps, numplanes;
|
|
float d, time_left, into, totalMass;
|
|
idVec3 dir, planes[MAX_CLIP_PLANES];
|
|
idVec3 end, stepEnd, primal_velocity, endVelocity, endClipVelocity, clipVelocity;
|
|
trace_t trace, stepTrace, downTrace;
|
|
bool nearGround, stepped, pushed;
|
|
|
|
numbumps = 4;
|
|
|
|
primal_velocity = current.velocity;
|
|
|
|
if (gravity) {
|
|
endVelocity = current.velocity + gravityVector * frametime;
|
|
current.velocity = (current.velocity + endVelocity) * 0.5f;
|
|
primal_velocity = endVelocity;
|
|
if (groundPlane) {
|
|
// slide along the ground plane without losing horizontal speed on walkable slopes
|
|
if (PM_IsWalkablePlane(groundTrace.c.normal, gravityNormal)) {
|
|
current.velocity = PM_AdjustVertically(groundTrace.c.normal, current.velocity, gravityNormal);
|
|
}
|
|
else {
|
|
current.velocity.ProjectOntoPlane(groundTrace.c.normal, OVERCLIP);
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
endVelocity = current.velocity;
|
|
}
|
|
|
|
time_left = frametime;
|
|
|
|
// never turn against the ground plane
|
|
if (groundPlane) {
|
|
numplanes = 1;
|
|
planes[0] = groundTrace.c.normal;
|
|
}
|
|
else {
|
|
numplanes = 0;
|
|
}
|
|
|
|
// never turn against original velocity
|
|
planes[numplanes] = current.velocity;
|
|
planes[numplanes].Normalize();
|
|
numplanes++;
|
|
|
|
for (bumpcount = 0; bumpcount < numbumps; bumpcount++) {
|
|
|
|
// calculate position we are trying to move to
|
|
end = current.origin + time_left * current.velocity;
|
|
|
|
// see if we can make it there
|
|
gameLocal.clip.Translation(trace, current.origin, end, clipModel, clipModel->GetAxis(), clipMask, self);
|
|
|
|
time_left -= time_left * trace.fraction;
|
|
current.origin = trace.endpos;
|
|
|
|
// if moved the entire distance
|
|
if (trace.fraction >= 1.0f) {
|
|
break;
|
|
}
|
|
|
|
stepped = pushed = false;
|
|
|
|
// if we are allowed to step up, only do stair stepping for non-walkable hits
|
|
if (stepUp && (trace.c.normal * -gravityNormal) < MIN_WALK_NORMAL) {
|
|
|
|
nearGround = groundPlane || ladder;
|
|
|
|
if (!nearGround) {
|
|
// trace down to see if the player is near the ground
|
|
// step checking when near the ground allows the player to move up stairs smoothly while jumping
|
|
stepEnd = current.origin + maxStepHeight * gravityNormal;
|
|
gameLocal.clip.Translation(downTrace, current.origin, stepEnd, clipModel, clipModel->GetAxis(), clipMask, self);
|
|
nearGround = (downTrace.fraction < 1.0f && (downTrace.c.normal * -gravityNormal) > MIN_WALK_NORMAL);
|
|
}
|
|
|
|
// may only step up if near the ground or on a ladder
|
|
if (nearGround) {
|
|
|
|
// step up
|
|
stepEnd = current.origin - maxStepHeight * gravityNormal;
|
|
gameLocal.clip.Translation(downTrace, current.origin, stepEnd, clipModel, clipModel->GetAxis(), clipMask, self);
|
|
|
|
// trace along velocity
|
|
stepEnd = downTrace.endpos + time_left * current.velocity;
|
|
gameLocal.clip.Translation(stepTrace, downTrace.endpos, stepEnd, clipModel, clipModel->GetAxis(), clipMask, self);
|
|
|
|
// step down
|
|
stepEnd = stepTrace.endpos + maxStepHeight * gravityNormal;
|
|
gameLocal.clip.Translation(downTrace, stepTrace.endpos, stepEnd, clipModel, clipModel->GetAxis(), clipMask, self);
|
|
|
|
if (downTrace.fraction >= 1.0f || (downTrace.c.normal * -gravityNormal) > MIN_WALK_NORMAL) {
|
|
|
|
// if moved the entire distance
|
|
if (stepTrace.fraction >= 1.0f) {
|
|
time_left = 0;
|
|
current.stepUp -= (downTrace.endpos - current.origin) * gravityNormal;
|
|
current.origin = downTrace.endpos;
|
|
current.movementFlags |= PMF_STEPPED_UP;
|
|
current.velocity *= PM_STEPSCALE;
|
|
break;
|
|
}
|
|
|
|
// if the move is further when stepping up
|
|
if (stepTrace.fraction > trace.fraction) {
|
|
time_left -= time_left * stepTrace.fraction;
|
|
current.stepUp -= (downTrace.endpos - current.origin) * gravityNormal;
|
|
current.origin = downTrace.endpos;
|
|
current.movementFlags |= PMF_STEPPED_UP;
|
|
current.velocity *= PM_STEPSCALE;
|
|
trace = stepTrace;
|
|
stepped = true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// if we can push other entities and not blocked by the world
|
|
if (push && trace.c.entityNum != ENTITYNUM_WORLD) {
|
|
|
|
clipModel->SetPosition(current.origin, clipModel->GetAxis());
|
|
|
|
// clip movement, only push idMoveables, don't push entities the player is standing on
|
|
// apply impact to pushed objects
|
|
pushFlags = PUSHFL_CLIP | PUSHFL_ONLYMOVEABLE | PUSHFL_NOGROUNDENTITIES | PUSHFL_APPLYIMPULSE;
|
|
|
|
// clip & push
|
|
totalMass = gameLocal.push.ClipTranslationalPush(trace, self, pushFlags, end, end - current.origin);
|
|
|
|
if (totalMass > 0.0f) {
|
|
// decrease velocity based on the total mass of the objects being pushed ?
|
|
current.velocity *= 1.0f - idMath::ClampFloat(0.0f, 1000.0f, totalMass - 20.0f) * (1.0f / 950.0f);
|
|
pushed = true;
|
|
}
|
|
|
|
current.origin = trace.endpos;
|
|
time_left -= time_left * trace.fraction;
|
|
|
|
// if moved the entire distance
|
|
if (trace.fraction >= 1.0f) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!stepped) {
|
|
// let the entity know about the collision
|
|
self->Collide(trace, current.velocity);
|
|
}
|
|
|
|
if (numplanes >= MAX_CLIP_PLANES) {
|
|
// MrElusive: I think we have some relatively high poly LWO models with a lot of slanted tris
|
|
// where it may hit the max clip planes
|
|
current.velocity = vec3_origin;
|
|
return true;
|
|
}
|
|
|
|
//
|
|
// if this is the same plane we hit before, nudge velocity
|
|
// out along it, which fixes some epsilon issues with
|
|
// non-axial planes
|
|
//
|
|
for (i = 0; i < numplanes; i++) {
|
|
if ((trace.c.normal * planes[i]) > 0.999f) {
|
|
if (PM_IsWalkablePlane(trace.c.normal, gravityNormal)) {
|
|
current.velocity = PM_AdjustVertically(trace.c.normal, current.velocity, gravityNormal);
|
|
}
|
|
else {
|
|
// clip into the trace normal just in case this normal is almost but not exactly
|
|
// the same as the groundTrace normal, then nudge out along the normal.
|
|
current.velocity.ProjectOntoPlane(trace.c.normal, OVERCLIP);
|
|
current.velocity += trace.c.normal;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
if (i < numplanes) {
|
|
continue;
|
|
}
|
|
planes[numplanes] = trace.c.normal;
|
|
numplanes++;
|
|
|
|
//
|
|
// modify velocity so it parallels all of the clip planes
|
|
//
|
|
|
|
// find a plane that it enters
|
|
for (i = 0; i < numplanes; i++) {
|
|
into = current.velocity * planes[i];
|
|
if (into >= 0.1f) {
|
|
continue; // move doesn't interact with the plane
|
|
}
|
|
|
|
if (PM_IsWalkablePlane(planes[i], gravityNormal)) {
|
|
clipVelocity = PM_AdjustVertically(planes[i], current.velocity, gravityNormal);
|
|
endClipVelocity = PM_AdjustVertically(planes[i], endVelocity, gravityNormal);
|
|
}
|
|
else {
|
|
// slide along the plane
|
|
clipVelocity = current.velocity;
|
|
clipVelocity.ProjectOntoPlane(planes[i], OVERCLIP);
|
|
|
|
// slide along the plane
|
|
endClipVelocity = endVelocity;
|
|
endClipVelocity.ProjectOntoPlane(planes[i], OVERCLIP);
|
|
}
|
|
|
|
// see if there is a second plane that the new move enters
|
|
for (j = 0; j < numplanes; j++) {
|
|
if (j == i) {
|
|
continue;
|
|
}
|
|
if ((clipVelocity * planes[j]) >= 0.1f) {
|
|
continue; // move doesn't interact with the plane
|
|
}
|
|
|
|
if (PM_IsWalkablePlane(planes[j], gravityNormal)) {
|
|
clipVelocity = PM_AdjustVertically(planes[j], clipVelocity, gravityNormal);
|
|
endClipVelocity = PM_AdjustVertically(planes[j], endClipVelocity, gravityNormal);
|
|
}
|
|
else {
|
|
// try clipping the move to the plane
|
|
clipVelocity.ProjectOntoPlane(planes[j], OVERCLIP);
|
|
endClipVelocity.ProjectOntoPlane(planes[j], OVERCLIP);
|
|
}
|
|
|
|
// see if it goes back into the first clip plane
|
|
if ((clipVelocity * planes[i]) >= 0) {
|
|
continue;
|
|
}
|
|
|
|
// slide the original velocity along the crease
|
|
dir = planes[i].Cross(planes[j]);
|
|
dir.Normalize();
|
|
d = dir * current.velocity;
|
|
clipVelocity = d * dir;
|
|
|
|
dir = planes[i].Cross(planes[j]);
|
|
dir.Normalize();
|
|
d = dir * endVelocity;
|
|
endClipVelocity = d * dir;
|
|
|
|
// see if there is a third plane the the new move enters
|
|
for (k = 0; k < numplanes; k++) {
|
|
if (k == i || k == j) {
|
|
continue;
|
|
}
|
|
if ((clipVelocity * planes[k]) >= 0.1f) {
|
|
continue; // move doesn't interact with the plane
|
|
}
|
|
|
|
// stop dead at a tripple plane interaction
|
|
current.velocity = vec3_origin;
|
|
return true;
|
|
}
|
|
}
|
|
|
|
// if we have fixed all interactions, try another move
|
|
current.velocity = clipVelocity;
|
|
endVelocity = endClipVelocity;
|
|
break;
|
|
}
|
|
}
|
|
|
|
// step down
|
|
if (stepDown && groundPlane) {
|
|
stepEnd = current.origin + gravityNormal * maxStepHeight;
|
|
gameLocal.clip.Translation(downTrace, current.origin, stepEnd, clipModel, clipModel->GetAxis(), clipMask, self);
|
|
if (downTrace.fraction > 1e-4f && downTrace.fraction < 1.0f) {
|
|
current.stepUp -= (downTrace.endpos - current.origin) * gravityNormal;
|
|
current.origin = downTrace.endpos;
|
|
current.movementFlags |= PMF_STEPPED_DOWN;
|
|
current.velocity *= PM_STEPSCALE;
|
|
}
|
|
}
|
|
|
|
if (gravity) {
|
|
current.velocity = endVelocity;
|
|
}
|
|
|
|
// come to a dead stop when the velocity orthogonal to the gravity flipped
|
|
clipVelocity = current.velocity - gravityNormal * current.velocity * gravityNormal;
|
|
endClipVelocity = endVelocity - gravityNormal * endVelocity * gravityNormal;
|
|
if (clipVelocity * endClipVelocity < 0.0f) {
|
|
current.velocity = gravityNormal * current.velocity * gravityNormal;
|
|
}
|
|
|
|
return (bool)(bumpcount == 0);
|
|
}
|
|
|
|
/*
|
|
==================
|
|
idPhysics_Player::Friction
|
|
|
|
Handles both ground friction and water friction
|
|
==================
|
|
*/
|
|
void idPhysics_Player::Friction(void) {
|
|
idVec3 vel;
|
|
float speed, newspeed, control;
|
|
float drop;
|
|
|
|
vel = current.velocity;
|
|
if (walking) {
|
|
// ignore slope movement, remove all velocity in gravity direction
|
|
vel += (vel * gravityNormal) * gravityNormal;
|
|
}
|
|
|
|
speed = vel.Length();
|
|
if (speed < 1.0f) {
|
|
// remove all movement orthogonal to gravity, allows for sinking underwater
|
|
if (fabs(current.velocity * gravityNormal) < 1e-5f) {
|
|
current.velocity.Zero();
|
|
}
|
|
else if (current.movementType == PM_SPECTATOR) {
|
|
current.velocity.Zero();
|
|
}
|
|
else {
|
|
current.velocity = (current.velocity * gravityNormal) * gravityNormal;
|
|
}
|
|
// FIXME: still have z friction underwater?
|
|
return;
|
|
}
|
|
|
|
drop = 0;
|
|
|
|
// spectator friction
|
|
if (current.movementType == PM_SPECTATOR) {
|
|
drop += speed * PM_FLYFRICTION * frametime;
|
|
}
|
|
// apply ground friction
|
|
else if (walking && waterLevel <= WATERLEVEL_FEET) {
|
|
// no friction on slick surfaces
|
|
if (!(groundMaterial && groundMaterial->GetSurfaceFlags() & SURF_SLICK)) {
|
|
// if getting knocked back, no friction
|
|
if (!(current.movementFlags & PMF_TIME_KNOCKBACK)) {
|
|
control = speed < PM_STOPSPEED ? PM_STOPSPEED : speed;
|
|
drop += control * PM_FRICTION * frametime;
|
|
}
|
|
}
|
|
}
|
|
// apply water friction even if just wading
|
|
else if (waterLevel) {
|
|
drop += speed * PM_WATERFRICTION * waterLevel * frametime;
|
|
}
|
|
// apply air friction
|
|
else {
|
|
drop += speed * PM_AIRFRICTION * frametime;
|
|
}
|
|
|
|
// scale the velocity
|
|
newspeed = speed - drop;
|
|
if (newspeed < 0) {
|
|
newspeed = 0;
|
|
}
|
|
current.velocity *= (newspeed / speed);
|
|
|
|
// snap tiny components away to avoid denormal slow paths in long-running physics frames
|
|
current.velocity.FixDenormals();
|
|
}
|
|
|
|
/*
|
|
===================
|
|
idPhysics_Player::WaterJumpMove
|
|
|
|
Flying out of the water
|
|
===================
|
|
*/
|
|
void idPhysics_Player::WaterJumpMove(void) {
|
|
|
|
// waterjump has no control, but falls
|
|
idPhysics_Player::SlideMove(true, true, false, false);
|
|
|
|
// add gravity
|
|
current.velocity += gravityNormal * frametime;
|
|
// if falling down
|
|
if (current.velocity * gravityNormal > 0.0f) {
|
|
// cancel as soon as we are falling down again
|
|
current.movementFlags &= ~PMF_ALL_TIMES;
|
|
current.movementTime = 0;
|
|
}
|
|
}
|
|
|
|
/*
|
|
===================
|
|
idPhysics_Player::WaterMove
|
|
===================
|
|
*/
|
|
void idPhysics_Player::WaterMove(void) {
|
|
idVec3 wishvel;
|
|
float wishspeed;
|
|
idVec3 wishdir;
|
|
float scale;
|
|
float vel;
|
|
|
|
if (idPhysics_Player::CheckWaterJump()) {
|
|
idPhysics_Player::WaterJumpMove();
|
|
return;
|
|
}
|
|
|
|
idPhysics_Player::Friction();
|
|
|
|
scale = idPhysics_Player::CmdScale(command);
|
|
|
|
// user intentions
|
|
if (!scale) {
|
|
wishvel = gravityNormal * 60; // sink towards bottom
|
|
}
|
|
else {
|
|
wishvel = scale * (viewForward * command.forwardmove + viewRight * command.rightmove);
|
|
wishvel -= scale * gravityNormal * command.upmove;
|
|
}
|
|
|
|
wishdir = wishvel;
|
|
wishspeed = wishdir.Normalize();
|
|
|
|
if (wishspeed > playerSpeed * PM_SWIMSCALE) {
|
|
wishspeed = playerSpeed * PM_SWIMSCALE;
|
|
}
|
|
|
|
idPhysics_Player::Accelerate(wishdir, wishspeed, PM_WATERACCELERATE);
|
|
|
|
// make sure we can go up slopes easily under water
|
|
if (groundPlane && (current.velocity * groundTrace.c.normal) < 0.0f) {
|
|
vel = current.velocity.Length();
|
|
// slide along the ground plane
|
|
current.velocity.ProjectOntoPlane(groundTrace.c.normal, OVERCLIP);
|
|
|
|
current.velocity.Normalize();
|
|
current.velocity *= vel;
|
|
}
|
|
|
|
idPhysics_Player::SlideMove(false, true, false, false);
|
|
}
|
|
|
|
/*
|
|
===================
|
|
idPhysics_Player::FlyMove
|
|
===================
|
|
*/
|
|
void idPhysics_Player::FlyMove(void) {
|
|
idVec3 wishvel;
|
|
float wishspeed;
|
|
idVec3 wishdir;
|
|
float scale;
|
|
|
|
// normal slowdown
|
|
idPhysics_Player::Friction();
|
|
|
|
scale = idPhysics_Player::CmdScale(command);
|
|
|
|
if (!scale) {
|
|
wishvel = vec3_origin;
|
|
}
|
|
else {
|
|
wishvel = scale * (viewForward * command.forwardmove + viewRight * command.rightmove);
|
|
wishvel -= scale * gravityNormal * command.upmove;
|
|
}
|
|
|
|
wishdir = wishvel;
|
|
wishspeed = wishdir.Normalize();
|
|
|
|
idPhysics_Player::Accelerate(wishdir, wishspeed, PM_FLYACCELERATE);
|
|
|
|
idPhysics_Player::SlideMove(false, false, false, false);
|
|
}
|
|
|
|
/*
|
|
===================
|
|
idPhysics_Player::AirMove
|
|
===================
|
|
*/
|
|
void idPhysics_Player::AirMove(void) {
|
|
idVec3 wishvel;
|
|
idVec3 wishdir;
|
|
float wishspeed;
|
|
float scale;
|
|
|
|
idPhysics_Player::Friction();
|
|
|
|
scale = PM_CmdScale(command, playerSpeed, true);
|
|
|
|
// project moves down to flat plane
|
|
viewForward -= (viewForward * gravityNormal) * gravityNormal;
|
|
viewRight -= (viewRight * gravityNormal) * gravityNormal;
|
|
viewForward.Normalize();
|
|
viewRight.Normalize();
|
|
|
|
wishvel = viewForward * command.forwardmove + viewRight * command.rightmove;
|
|
wishvel -= (wishvel * gravityNormal) * gravityNormal;
|
|
wishdir = wishvel;
|
|
wishspeed = wishdir.Normalize();
|
|
wishspeed *= scale;
|
|
|
|
// not on ground, so little effect on velocity
|
|
idPhysics_Player::Accelerate(wishdir, wishspeed, PM_AIRACCELERATE);
|
|
|
|
// we may have a ground plane that is very steep, even
|
|
// though we don't have a groundentity
|
|
// slide along the steep plane
|
|
if (groundPlane) {
|
|
current.velocity.ProjectOntoPlane(groundTrace.c.normal, OVERCLIP);
|
|
}
|
|
|
|
idPhysics_Player::SlideMove(true, false, false, false);
|
|
}
|
|
|
|
/*
|
|
===================
|
|
idPhysics_Player::WalkMove
|
|
===================
|
|
*/
|
|
void idPhysics_Player::WalkMove(void) {
|
|
idVec3 wishvel;
|
|
idVec3 wishdir;
|
|
float wishspeed;
|
|
float scale;
|
|
float accelerate;
|
|
idVec3 vel;
|
|
|
|
if (waterLevel > WATERLEVEL_WAIST && (viewForward * groundTrace.c.normal) > 0.0f) {
|
|
// begin swimming
|
|
idPhysics_Player::WaterMove();
|
|
return;
|
|
}
|
|
|
|
if (idPhysics_Player::CheckJump()) {
|
|
// jumped away
|
|
if (waterLevel > WATERLEVEL_FEET) {
|
|
idPhysics_Player::WaterMove();
|
|
}
|
|
else {
|
|
idPhysics_Player::AirMove();
|
|
}
|
|
return;
|
|
}
|
|
|
|
idPhysics_Player::Friction();
|
|
|
|
scale = PM_CmdScale(command, playerSpeed, true);
|
|
|
|
// project moves down to flat plane
|
|
viewForward -= (viewForward * gravityNormal) * gravityNormal;
|
|
viewRight -= (viewRight * gravityNormal) * gravityNormal;
|
|
|
|
// project the forward and right directions onto the ground plane while preserving
|
|
// their horizontal components, matching ETQW's slope movement fix.
|
|
viewForward = PM_AdjustVertically(groundTrace.c.normal, viewForward, gravityNormal);
|
|
viewRight = PM_AdjustVertically(groundTrace.c.normal, viewRight, gravityNormal);
|
|
viewForward.Normalize();
|
|
viewRight.Normalize();
|
|
|
|
wishvel = viewForward * command.forwardmove + viewRight * command.rightmove;
|
|
wishdir = wishvel;
|
|
wishspeed = wishdir.Normalize();
|
|
wishspeed *= scale;
|
|
|
|
// clamp the speed lower if wading or walking on the bottom
|
|
if (waterLevel) {
|
|
float waterScale;
|
|
|
|
waterScale = waterLevel / 3.0f;
|
|
waterScale = 1.0f - (1.0f - PM_SWIMSCALE) * waterScale;
|
|
if (wishspeed > playerSpeed * waterScale) {
|
|
wishspeed = playerSpeed * waterScale;
|
|
}
|
|
}
|
|
|
|
// when a player gets hit, they temporarily lose full control, which allows them to be moved a bit
|
|
if ((groundMaterial && groundMaterial->GetSurfaceFlags() & SURF_SLICK) || current.movementFlags & PMF_TIME_KNOCKBACK) {
|
|
accelerate = PM_AIRACCELERATE;
|
|
}
|
|
else {
|
|
accelerate = PM_ACCELERATE;
|
|
}
|
|
|
|
idPhysics_Player::Accelerate(wishdir, wishspeed, accelerate);
|
|
|
|
if ((groundMaterial && groundMaterial->GetSurfaceFlags() & SURF_SLICK) || current.movementFlags & PMF_TIME_KNOCKBACK) {
|
|
current.velocity += gravityVector * frametime;
|
|
}
|
|
|
|
// slide along the ground plane without losing horizontal speed on walkable slopes
|
|
current.velocity = PM_AdjustVertically(groundTrace.c.normal, current.velocity, gravityNormal);
|
|
|
|
// don't do anything if standing still
|
|
vel = current.velocity - (current.velocity * gravityNormal) * gravityNormal;
|
|
if (!vel.LengthSqr()) {
|
|
return;
|
|
}
|
|
|
|
gameLocal.push.InitSavingPushedEntityPositions();
|
|
|
|
idPhysics_Player::SlideMove(false, true, true, true);
|
|
}
|
|
|
|
/*
|
|
==============
|
|
idPhysics_Player::DeadMove
|
|
==============
|
|
*/
|
|
void idPhysics_Player::DeadMove(void) {
|
|
// ETQW keeps dead players moving through the same ground/air paths with input
|
|
// already zeroed by MovePlayer, so gravity and sliding still resolve correctly.
|
|
if (walking) {
|
|
idPhysics_Player::WalkMove();
|
|
}
|
|
else {
|
|
idPhysics_Player::AirMove();
|
|
}
|
|
}
|
|
|
|
/*
|
|
===============
|
|
idPhysics_Player::NoclipMove
|
|
===============
|
|
*/
|
|
void idPhysics_Player::NoclipMove(void) {
|
|
float speed, drop, friction, newspeed, stopspeed;
|
|
float scale, wishspeed;
|
|
idVec3 wishdir;
|
|
|
|
// friction
|
|
speed = current.velocity.Length();
|
|
if (speed < 20.0f) {
|
|
current.velocity = vec3_origin;
|
|
}
|
|
else {
|
|
stopspeed = playerSpeed * 0.3f;
|
|
if (speed < stopspeed) {
|
|
speed = stopspeed;
|
|
}
|
|
friction = PM_NOCLIPFRICTION;
|
|
drop = speed * friction * frametime;
|
|
|
|
// scale the velocity
|
|
newspeed = speed - drop;
|
|
if (newspeed < 0) {
|
|
newspeed = 0;
|
|
}
|
|
|
|
current.velocity *= newspeed / speed;
|
|
}
|
|
|
|
// accelerate
|
|
scale = idPhysics_Player::CmdScale(command);
|
|
|
|
wishdir = scale * (viewForward * command.forwardmove + viewRight * command.rightmove);
|
|
wishdir -= scale * gravityNormal * command.upmove;
|
|
wishspeed = wishdir.Normalize();
|
|
wishspeed *= scale;
|
|
|
|
idPhysics_Player::Accelerate(wishdir, wishspeed, PM_ACCELERATE);
|
|
|
|
// move
|
|
current.origin += frametime * current.velocity;
|
|
}
|
|
|
|
/*
|
|
===============
|
|
idPhysics_Player::SpectatorMove
|
|
===============
|
|
*/
|
|
void idPhysics_Player::SpectatorMove(void) {
|
|
idVec3 wishvel;
|
|
float wishspeed;
|
|
idVec3 wishdir;
|
|
float scale;
|
|
|
|
trace_t trace;
|
|
idVec3 end;
|
|
|
|
// fly movement
|
|
|
|
idPhysics_Player::Friction();
|
|
|
|
scale = idPhysics_Player::CmdScale(command);
|
|
|
|
if (!scale) {
|
|
wishvel = vec3_origin;
|
|
}
|
|
else {
|
|
wishvel = scale * (viewForward * command.forwardmove + viewRight * command.rightmove);
|
|
wishvel -= scale * gravityNormal * command.upmove;
|
|
}
|
|
|
|
wishdir = wishvel;
|
|
wishspeed = wishdir.Normalize();
|
|
|
|
idPhysics_Player::Accelerate(wishdir, wishspeed, PM_FLYACCELERATE);
|
|
|
|
idPhysics_Player::SlideMove(false, false, false, false);
|
|
}
|
|
|
|
/*
|
|
============
|
|
idPhysics_Player::LadderMove
|
|
============
|
|
*/
|
|
void idPhysics_Player::LadderMove(void) {
|
|
idVec3 wishdir, wishvel, right;
|
|
float wishspeed, scale;
|
|
float upscale;
|
|
|
|
// stick to the ladder
|
|
wishvel = -100.0f * ladderNormal;
|
|
current.velocity = (gravityNormal * current.velocity) * gravityNormal + wishvel;
|
|
|
|
upscale = (-gravityNormal * viewForward + 0.5f) * 2.5f;
|
|
if (upscale > 1.0f) {
|
|
upscale = 1.0f;
|
|
}
|
|
else if (upscale < -1.0f) {
|
|
upscale = -1.0f;
|
|
}
|
|
|
|
scale = idPhysics_Player::CmdScale(command);
|
|
wishvel = -0.9f * gravityNormal * upscale * scale * (float)command.forwardmove;
|
|
|
|
// strafe
|
|
if (command.rightmove) {
|
|
// right vector orthogonal to gravity
|
|
right = viewRight - (gravityNormal * viewRight) * gravityNormal;
|
|
// project right vector into ladder plane
|
|
right = right - (ladderNormal * right) * ladderNormal;
|
|
right.Normalize();
|
|
|
|
// if we are looking away from the ladder, reverse the right vector
|
|
if (ladderNormal * viewForward > 0.0f) {
|
|
right = -right;
|
|
}
|
|
wishvel += 2.0f * right * scale * (float)command.rightmove;
|
|
}
|
|
|
|
// up down movement
|
|
if (command.upmove) {
|
|
wishvel += -0.5f * gravityNormal * scale * (float)command.upmove;
|
|
}
|
|
|
|
// do strafe friction
|
|
idPhysics_Player::Friction();
|
|
|
|
// accelerate
|
|
wishspeed = wishvel.Normalize();
|
|
idPhysics_Player::Accelerate(wishvel, wishspeed, PM_ACCELERATE);
|
|
|
|
// cap the vertical velocity
|
|
upscale = current.velocity * -gravityNormal;
|
|
if (upscale < -PM_LADDERSPEED) {
|
|
current.velocity += gravityNormal * (upscale + PM_LADDERSPEED);
|
|
}
|
|
else if (upscale > PM_LADDERSPEED) {
|
|
current.velocity += gravityNormal * (upscale - PM_LADDERSPEED);
|
|
}
|
|
|
|
if ((wishvel * gravityNormal) == 0.0f) {
|
|
if (current.velocity * gravityNormal < 0.0f) {
|
|
current.velocity += gravityVector * frametime;
|
|
if (current.velocity * gravityNormal > 0.0f) {
|
|
current.velocity -= (gravityNormal * current.velocity) * gravityNormal;
|
|
}
|
|
}
|
|
else {
|
|
current.velocity -= gravityVector * frametime;
|
|
if (current.velocity * gravityNormal < 0.0f) {
|
|
current.velocity -= (gravityNormal * current.velocity) * gravityNormal;
|
|
}
|
|
}
|
|
}
|
|
|
|
idPhysics_Player::SlideMove(false, (command.forwardmove > 0), false, false);
|
|
}
|
|
|
|
/*
|
|
=============
|
|
idPhysics_Player::CorrectAllSolid
|
|
=============
|
|
*/
|
|
void idPhysics_Player::CorrectAllSolid(trace_t& trace, int contents) {
|
|
if (debugLevel) {
|
|
gameLocal.Printf("%i:allsolid\n", c_pmove);
|
|
}
|
|
|
|
// FIXME: jitter around to find a free spot ?
|
|
|
|
if (trace.fraction >= 1.0f) {
|
|
memset(&trace, 0, sizeof(trace));
|
|
trace.endpos = current.origin;
|
|
trace.endAxis = clipModelAxis;
|
|
trace.fraction = 0.0f;
|
|
trace.c.dist = current.origin.z;
|
|
trace.c.normal.Set(0, 0, 1);
|
|
trace.c.point = current.origin;
|
|
trace.c.entityNum = ENTITYNUM_WORLD;
|
|
trace.c.id = 0;
|
|
trace.c.type = CONTACT_TRMVERTEX;
|
|
trace.c.material = NULL;
|
|
trace.c.contents = contents;
|
|
}
|
|
}
|
|
|
|
/*
|
|
=============
|
|
idPhysics_Player::CheckGround
|
|
=============
|
|
*/
|
|
void idPhysics_Player::CheckGround(void) {
|
|
int i, contents;
|
|
idVec3 point;
|
|
bool hadGroundContacts;
|
|
|
|
hadGroundContacts = HasGroundContacts();
|
|
|
|
// set the clip model origin before getting the contacts
|
|
clipModel->SetPosition(current.origin, clipModel->GetAxis());
|
|
|
|
EvaluateContacts();
|
|
|
|
// setup a ground trace from the contacts
|
|
groundTrace.endpos = current.origin;
|
|
groundTrace.endAxis = clipModel->GetAxis();
|
|
if (contacts.Num()) {
|
|
groundTrace.fraction = 0.0f;
|
|
groundTrace.c = contacts[0];
|
|
for (i = 1; i < contacts.Num(); i++) {
|
|
groundTrace.c.normal += contacts[i].normal;
|
|
}
|
|
groundTrace.c.normal.Normalize();
|
|
}
|
|
else {
|
|
groundTrace.fraction = 1.0f;
|
|
}
|
|
|
|
contents = gameLocal.clip.Contents(current.origin, clipModel, clipModel->GetAxis(), -1, self);
|
|
if (contents & MASK_SOLID) {
|
|
// do something corrective if stuck in solid
|
|
idPhysics_Player::CorrectAllSolid(groundTrace, contents);
|
|
}
|
|
|
|
// if the trace didn't hit anything, we are in free fall
|
|
if (groundTrace.fraction == 1.0f) {
|
|
groundPlane = false;
|
|
walking = false;
|
|
groundEntityPtr = NULL;
|
|
return;
|
|
}
|
|
|
|
groundMaterial = groundTrace.c.material;
|
|
groundEntityPtr = gameLocal.entities[groundTrace.c.entityNum];
|
|
|
|
// check if getting thrown off the ground
|
|
if ((current.velocity * -gravityNormal) > 0.0f && (current.velocity * groundTrace.c.normal) > 1.0f) {
|
|
if (debugLevel) {
|
|
gameLocal.Printf("%i:kickoff\n", c_pmove);
|
|
}
|
|
|
|
groundPlane = false;
|
|
walking = false;
|
|
return;
|
|
}
|
|
|
|
// slopes that are too steep will not be considered onground
|
|
if ((groundTrace.c.normal * -gravityNormal) < MIN_WALK_NORMAL) {
|
|
if (debugLevel) {
|
|
gameLocal.Printf("%i:steep\n", c_pmove);
|
|
}
|
|
|
|
// FIXME: if they can't slide down the slope, let them walk (sharp crevices)
|
|
|
|
// make sure we don't die from sliding down a steep slope
|
|
if (current.velocity * gravityNormal > 150.0f) {
|
|
current.velocity -= (current.velocity * gravityNormal - 150.0f) * gravityNormal;
|
|
}
|
|
|
|
groundPlane = true;
|
|
walking = false;
|
|
return;
|
|
}
|
|
|
|
groundPlane = true;
|
|
walking = true;
|
|
|
|
// hitting solid ground will end a waterjump
|
|
if (current.movementFlags & PMF_TIME_WATERJUMP) {
|
|
current.movementFlags &= ~(PMF_TIME_WATERJUMP | PMF_TIME_LAND);
|
|
current.movementTime = 0;
|
|
}
|
|
|
|
// if the player didn't have ground contacts the previous frame
|
|
if (!hadGroundContacts) {
|
|
|
|
// don't do landing time if we were just going down a slope
|
|
if ((current.velocity * -gravityNormal) < -200.0f) {
|
|
// don't allow another jump for a little while
|
|
current.movementFlags |= PMF_TIME_LAND;
|
|
current.movementTime = 250;
|
|
}
|
|
}
|
|
|
|
// let the entity know about the collision
|
|
self->Collide(groundTrace, current.velocity);
|
|
|
|
if (groundEntityPtr.GetEntity()) {
|
|
impactInfo_t info;
|
|
groundEntityPtr.GetEntity()->GetImpactInfo(self, groundTrace.c.id, groundTrace.c.point, &info);
|
|
if (info.invMass != 0.0f) {
|
|
groundEntityPtr.GetEntity()->ApplyImpulse(self, groundTrace.c.id, groundTrace.c.point, current.velocity / (info.invMass * 10.0f));
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
==============
|
|
idPhysics_Player::CheckDuck
|
|
|
|
Sets clip model size
|
|
==============
|
|
*/
|
|
void idPhysics_Player::CheckDuck(void) {
|
|
trace_t trace;
|
|
idVec3 end;
|
|
idBounds bounds;
|
|
float maxZ;
|
|
|
|
if (current.movementType == PM_DEAD) {
|
|
maxZ = pm_deadheight.GetFloat();
|
|
}
|
|
else {
|
|
// stand up when up against a ladder, and do not duck while swimming deeply
|
|
if (command.upmove < 0 && !ladder && waterLevel <= WATERLEVEL_FEET) {
|
|
// duck
|
|
current.movementFlags |= PMF_DUCKED;
|
|
}
|
|
else {
|
|
// stand up if possible
|
|
if (current.movementFlags & PMF_DUCKED) {
|
|
// try to stand up
|
|
end = current.origin - (pm_normalheight.GetFloat() - pm_crouchheight.GetFloat()) * gravityNormal;
|
|
gameLocal.clip.Translation(trace, current.origin, end, clipModel, clipModel->GetAxis(), clipMask, self);
|
|
if (trace.fraction >= 1.0f) {
|
|
current.movementFlags &= ~PMF_DUCKED;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (current.movementFlags & PMF_DUCKED) {
|
|
playerSpeed = crouchSpeed;
|
|
maxZ = pm_crouchheight.GetFloat();
|
|
}
|
|
else {
|
|
maxZ = pm_normalheight.GetFloat();
|
|
}
|
|
}
|
|
// if the clipModel height should change
|
|
if (clipModel->GetBounds()[1][2] != maxZ) {
|
|
|
|
bounds = clipModel->GetBounds();
|
|
bounds[1][2] = maxZ;
|
|
if (pm_usecylinder.GetBool()) {
|
|
clipModel->LoadModel(idTraceModel(bounds, 8));
|
|
}
|
|
else {
|
|
clipModel->LoadModel(idTraceModel(bounds));
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
================
|
|
idPhysics_Player::CheckLadder
|
|
================
|
|
*/
|
|
void idPhysics_Player::CheckLadder(void) {
|
|
idVec3 forward, start, end;
|
|
trace_t trace;
|
|
float tracedist;
|
|
|
|
if (current.movementTime) {
|
|
return;
|
|
}
|
|
|
|
// if on the ground moving backwards
|
|
if (walking && command.forwardmove <= 0) {
|
|
return;
|
|
}
|
|
|
|
// forward vector orthogonal to gravity
|
|
forward = viewForward - (gravityNormal * viewForward) * gravityNormal;
|
|
forward.Normalize();
|
|
|
|
if (walking) {
|
|
// don't want to get sucked towards the ladder when still walking
|
|
tracedist = 1.0f;
|
|
}
|
|
else {
|
|
tracedist = 48.0f;
|
|
}
|
|
|
|
end = current.origin + tracedist * forward;
|
|
gameLocal.clip.Translation(trace, current.origin, end, clipModel, clipModel->GetAxis(), clipMask, self);
|
|
|
|
// if near a surface
|
|
if (trace.fraction < 1.0f) {
|
|
|
|
// if a ladder surface
|
|
if (trace.c.material && (trace.c.material->GetSurfaceFlags() & SURF_LADDER)) {
|
|
|
|
// check a step height higher
|
|
end = current.origin - gravityNormal * (maxStepHeight * 0.75f);
|
|
gameLocal.clip.Translation(trace, current.origin, end, clipModel, clipModel->GetAxis(), clipMask, self);
|
|
start = trace.endpos;
|
|
end = start + tracedist * forward;
|
|
gameLocal.clip.Translation(trace, start, end, clipModel, clipModel->GetAxis(), clipMask, self);
|
|
|
|
// if also near a surface a step height higher
|
|
if (trace.fraction < 1.0f) {
|
|
|
|
// if it also is a ladder surface
|
|
if (trace.c.material && trace.c.material->GetSurfaceFlags() & SURF_LADDER) {
|
|
ladder = true;
|
|
ladderNormal = trace.c.normal;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
=============
|
|
idPhysics_Player::CheckJump
|
|
=============
|
|
*/
|
|
bool idPhysics_Player::CheckJump(void) {
|
|
idVec3 addVelocity;
|
|
|
|
if (command.upmove < 10) {
|
|
// not holding jump
|
|
return false;
|
|
}
|
|
|
|
// must wait for jump to be released
|
|
if (current.movementFlags & PMF_JUMP_HELD) {
|
|
return false;
|
|
}
|
|
|
|
if (current.movementFlags & PMF_TIME_LAND) {
|
|
return false;
|
|
}
|
|
|
|
// don't jump if we can't stand up
|
|
if (current.movementFlags & PMF_DUCKED) {
|
|
return false;
|
|
}
|
|
|
|
// Make sure the current velocity is leaving the walkable ground plane before
|
|
// adding jump velocity; this avoids sticking or losing jump speed on slopes.
|
|
if (groundPlane && PM_IsWalkablePlane(groundTrace.c.normal, gravityNormal) && (current.velocity * groundTrace.c.normal) < 0.0f) {
|
|
current.velocity = PM_AdjustVertically(groundTrace.c.normal, current.velocity, gravityNormal);
|
|
}
|
|
|
|
groundPlane = false; // jumping away
|
|
walking = false;
|
|
current.movementFlags |= PMF_JUMP_HELD | PMF_JUMPED;
|
|
|
|
addVelocity = 2.0f * maxJumpHeight * -gravityVector;
|
|
addVelocity *= idMath::Sqrt(addVelocity.Normalize());
|
|
current.velocity += addVelocity;
|
|
|
|
return true;
|
|
}
|
|
|
|
/*
|
|
=============
|
|
idPhysics_Player::CheckWaterJump
|
|
=============
|
|
*/
|
|
bool idPhysics_Player::CheckWaterJump(void) {
|
|
idVec3 spot;
|
|
int cont;
|
|
idVec3 flatforward;
|
|
|
|
if (current.movementTime) {
|
|
return false;
|
|
}
|
|
|
|
if (command.forwardmove <= 0) {
|
|
return false;
|
|
}
|
|
|
|
// check for water jump
|
|
if (waterLevel != WATERLEVEL_WAIST) {
|
|
return false;
|
|
}
|
|
|
|
flatforward = viewForward - (viewForward * gravityNormal) * gravityNormal;
|
|
flatforward.Normalize();
|
|
|
|
spot = current.origin + 30.0f * flatforward;
|
|
spot -= 4.0f * gravityNormal;
|
|
cont = gameLocal.clip.Contents(spot, NULL, mat3_identity, CONTENTS_SOLID, self);
|
|
if (!(cont & CONTENTS_SOLID)) {
|
|
return false;
|
|
}
|
|
|
|
float height = clipModel->GetBounds().Size().z;
|
|
|
|
spot -= (height * 0.75f) * gravityNormal;
|
|
cont = gameLocal.clip.Contents(spot, NULL, mat3_identity, -1, self);
|
|
if (cont) {
|
|
return false;
|
|
}
|
|
|
|
// jump out of water
|
|
current.velocity = 200.0f * viewForward - 400.0f * gravityNormal;
|
|
current.movementFlags |= PMF_TIME_WATERJUMP;
|
|
current.movementTime = 2000;
|
|
|
|
return true;
|
|
}
|
|
|
|
/*
|
|
=============
|
|
idPhysics_Player::SetWaterLevel
|
|
=============
|
|
*/
|
|
void idPhysics_Player::SetWaterLevel(void) {
|
|
idVec3 point;
|
|
idBounds bounds;
|
|
int contents;
|
|
|
|
//
|
|
// get waterlevel, accounting for ducking
|
|
//
|
|
waterLevel = WATERLEVEL_NONE;
|
|
waterType = 0;
|
|
|
|
bounds = clipModel->GetBounds();
|
|
|
|
// check at feet level
|
|
point = current.origin - (bounds[0][2] + 1.0f) * gravityNormal;
|
|
contents = gameLocal.clip.Contents(point, NULL, mat3_identity, -1, self);
|
|
if (contents & MASK_WATER) {
|
|
|
|
waterType = contents;
|
|
waterLevel = WATERLEVEL_FEET;
|
|
|
|
// check at waist level
|
|
point = current.origin - (bounds[1][2] - bounds[0][2]) * 0.5f * gravityNormal;
|
|
contents = gameLocal.clip.Contents(point, NULL, mat3_identity, -1, self);
|
|
if (contents & MASK_WATER) {
|
|
|
|
waterLevel = WATERLEVEL_WAIST;
|
|
|
|
// check at head level
|
|
point = current.origin - (bounds[1][2] - 1.0f) * gravityNormal;
|
|
contents = gameLocal.clip.Contents(point, NULL, mat3_identity, -1, self);
|
|
if (contents & MASK_WATER) {
|
|
waterLevel = WATERLEVEL_HEAD;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
================
|
|
idPhysics_Player::DropTimers
|
|
================
|
|
*/
|
|
void idPhysics_Player::DropTimers(void) {
|
|
// drop misc timing counter
|
|
if (current.movementTime) {
|
|
if (framemsec >= current.movementTime) {
|
|
current.movementFlags &= ~PMF_ALL_TIMES;
|
|
current.movementTime = 0;
|
|
}
|
|
else {
|
|
current.movementTime -= framemsec;
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
================
|
|
idPhysics_Player::MovePlayer
|
|
================
|
|
*/
|
|
void idPhysics_Player::MovePlayer(int msec) {
|
|
|
|
// this counter lets us debug movement problems with a journal
|
|
// by setting a conditional breakpoint for the previous frame
|
|
c_pmove++;
|
|
|
|
walking = false;
|
|
groundPlane = false;
|
|
ladder = false;
|
|
|
|
// determine the time
|
|
framemsec = msec;
|
|
frametime = framemsec * 0.001f;
|
|
|
|
// default speed
|
|
playerSpeed = walkSpeed;
|
|
|
|
// remove jumped and stepped up flag
|
|
current.movementFlags &= ~(PMF_JUMPED | PMF_STEPPED_UP | PMF_STEPPED_DOWN);
|
|
current.stepUp = 0.0f;
|
|
|
|
if (command.upmove < 10) {
|
|
// not holding jump
|
|
current.movementFlags &= ~PMF_JUMP_HELD;
|
|
}
|
|
|
|
// if no movement at all
|
|
if (current.movementType == PM_FREEZE) {
|
|
return;
|
|
}
|
|
|
|
// move the player velocity into the frame of a pusher
|
|
current.velocity -= current.pushVelocity;
|
|
|
|
// view vectors
|
|
viewAngles.ToVectors(&viewForward, NULL, NULL);
|
|
viewForward *= clipModelAxis;
|
|
viewRight = gravityNormal.Cross(viewForward);
|
|
viewRight.Normalize();
|
|
|
|
// fly in spectator mode
|
|
if (current.movementType == PM_SPECTATOR) {
|
|
SpectatorMove();
|
|
idPhysics_Player::DropTimers();
|
|
return;
|
|
}
|
|
|
|
// special no clip mode
|
|
if (current.movementType == PM_NOCLIP) {
|
|
idPhysics_Player::NoclipMove();
|
|
idPhysics_Player::DropTimers();
|
|
return;
|
|
}
|
|
|
|
// no control when dead
|
|
if (current.movementType == PM_DEAD) {
|
|
command.forwardmove = 0;
|
|
command.rightmove = 0;
|
|
command.upmove = 0;
|
|
}
|
|
|
|
// set watertype and waterlevel
|
|
idPhysics_Player::SetWaterLevel();
|
|
|
|
// check for ground
|
|
idPhysics_Player::CheckGround();
|
|
|
|
// check if up against a ladder
|
|
idPhysics_Player::CheckLadder();
|
|
|
|
// set clip model size
|
|
idPhysics_Player::CheckDuck();
|
|
|
|
// handle timers
|
|
idPhysics_Player::DropTimers();
|
|
|
|
// move
|
|
if (current.movementType == PM_DEAD) {
|
|
// dead
|
|
idPhysics_Player::DeadMove();
|
|
}
|
|
else if (ladder) {
|
|
// going up or down a ladder
|
|
idPhysics_Player::LadderMove();
|
|
}
|
|
else if (current.movementFlags & PMF_TIME_WATERJUMP) {
|
|
// jumping out of water
|
|
idPhysics_Player::WaterJumpMove();
|
|
}
|
|
else if (waterLevel > 1) {
|
|
// swimming
|
|
idPhysics_Player::WaterMove();
|
|
}
|
|
else if (walking) {
|
|
// walking on ground
|
|
idPhysics_Player::WalkMove();
|
|
}
|
|
else {
|
|
// airborne
|
|
idPhysics_Player::AirMove();
|
|
}
|
|
|
|
// set watertype, waterlevel and groundentity
|
|
idPhysics_Player::SetWaterLevel();
|
|
idPhysics_Player::CheckGround();
|
|
|
|
// move the player velocity back into the world frame
|
|
current.velocity += current.pushVelocity;
|
|
current.pushVelocity.Zero();
|
|
}
|
|
|
|
/*
|
|
================
|
|
idPhysics_Player::GetWaterLevel
|
|
================
|
|
*/
|
|
waterLevel_t idPhysics_Player::GetWaterLevel(void) const {
|
|
return waterLevel;
|
|
}
|
|
|
|
/*
|
|
================
|
|
idPhysics_Player::GetWaterType
|
|
================
|
|
*/
|
|
int idPhysics_Player::GetWaterType(void) const {
|
|
return waterType;
|
|
}
|
|
|
|
/*
|
|
================
|
|
idPhysics_Player::HasJumped
|
|
================
|
|
*/
|
|
bool idPhysics_Player::HasJumped(void) const {
|
|
return ((current.movementFlags & PMF_JUMPED) != 0);
|
|
}
|
|
|
|
/*
|
|
================
|
|
idPhysics_Player::HasSteppedUp
|
|
================
|
|
*/
|
|
bool idPhysics_Player::HasSteppedUp(void) const {
|
|
return ((current.movementFlags & (PMF_STEPPED_UP | PMF_STEPPED_DOWN)) != 0);
|
|
}
|
|
|
|
/*
|
|
================
|
|
idPhysics_Player::GetStepUp
|
|
================
|
|
*/
|
|
float idPhysics_Player::GetStepUp(void) const {
|
|
return current.stepUp;
|
|
}
|
|
|
|
/*
|
|
================
|
|
idPhysics_Player::IsCrouching
|
|
================
|
|
*/
|
|
bool idPhysics_Player::IsCrouching(void) const {
|
|
return ((current.movementFlags & PMF_DUCKED) != 0);
|
|
}
|
|
|
|
/*
|
|
================
|
|
idPhysics_Player::OnLadder
|
|
================
|
|
*/
|
|
bool idPhysics_Player::OnLadder(void) const {
|
|
return ladder;
|
|
}
|
|
|
|
/*
|
|
================
|
|
idPhysics_Player::idPhysics_Player
|
|
================
|
|
*/
|
|
idPhysics_Player::idPhysics_Player(void) {
|
|
debugLevel = false;
|
|
clipModel = NULL;
|
|
clipMask = 0;
|
|
memset(¤t, 0, sizeof(current));
|
|
saved = current;
|
|
walkSpeed = 0;
|
|
crouchSpeed = 0;
|
|
maxStepHeight = 0;
|
|
maxJumpHeight = 0;
|
|
memset(&command, 0, sizeof(command));
|
|
viewAngles.Zero();
|
|
framemsec = 0;
|
|
frametime = 0;
|
|
playerSpeed = 0;
|
|
viewForward.Zero();
|
|
viewRight.Zero();
|
|
walking = false;
|
|
groundPlane = false;
|
|
memset(&groundTrace, 0, sizeof(groundTrace));
|
|
groundMaterial = NULL;
|
|
ladder = false;
|
|
ladderNormal.Zero();
|
|
waterLevel = WATERLEVEL_NONE;
|
|
waterType = 0;
|
|
}
|
|
|
|
/*
|
|
================
|
|
idPhysics_Player_SavePState
|
|
================
|
|
*/
|
|
void idPhysics_Player_SavePState(idSaveGame* savefile, const playerPState_t& state) {
|
|
savefile->WriteVec3(state.origin);
|
|
savefile->WriteVec3(state.velocity);
|
|
savefile->WriteVec3(state.localOrigin);
|
|
savefile->WriteVec3(state.pushVelocity);
|
|
savefile->WriteFloat(state.stepUp);
|
|
savefile->WriteInt(state.movementType);
|
|
savefile->WriteInt(state.movementFlags);
|
|
savefile->WriteInt(state.movementTime);
|
|
}
|
|
|
|
/*
|
|
================
|
|
idPhysics_Player_RestorePState
|
|
================
|
|
*/
|
|
void idPhysics_Player_RestorePState(idRestoreGame* savefile, playerPState_t& state) {
|
|
savefile->ReadVec3(state.origin);
|
|
savefile->ReadVec3(state.velocity);
|
|
savefile->ReadVec3(state.localOrigin);
|
|
savefile->ReadVec3(state.pushVelocity);
|
|
savefile->ReadFloat(state.stepUp);
|
|
savefile->ReadInt(state.movementType);
|
|
savefile->ReadInt(state.movementFlags);
|
|
savefile->ReadInt(state.movementTime);
|
|
}
|
|
|
|
/*
|
|
================
|
|
idPhysics_Player::Save
|
|
================
|
|
*/
|
|
void idPhysics_Player::Save(idSaveGame* savefile) const {
|
|
|
|
idPhysics_Player_SavePState(savefile, current);
|
|
idPhysics_Player_SavePState(savefile, saved);
|
|
|
|
savefile->WriteFloat(walkSpeed);
|
|
savefile->WriteFloat(crouchSpeed);
|
|
savefile->WriteFloat(maxStepHeight);
|
|
savefile->WriteFloat(maxJumpHeight);
|
|
savefile->WriteInt(debugLevel);
|
|
|
|
savefile->WriteUsercmd(command);
|
|
savefile->WriteAngles(viewAngles);
|
|
|
|
savefile->WriteInt(framemsec);
|
|
savefile->WriteFloat(frametime);
|
|
savefile->WriteFloat(playerSpeed);
|
|
savefile->WriteVec3(viewForward);
|
|
savefile->WriteVec3(viewRight);
|
|
|
|
savefile->WriteBool(walking);
|
|
savefile->WriteBool(groundPlane);
|
|
savefile->WriteTrace(groundTrace);
|
|
savefile->WriteMaterial(groundMaterial);
|
|
|
|
savefile->WriteBool(ladder);
|
|
savefile->WriteVec3(ladderNormal);
|
|
|
|
savefile->WriteInt((int)waterLevel);
|
|
savefile->WriteInt(waterType);
|
|
}
|
|
|
|
/*
|
|
================
|
|
idPhysics_Player::Restore
|
|
================
|
|
*/
|
|
void idPhysics_Player::Restore(idRestoreGame* savefile) {
|
|
|
|
idPhysics_Player_RestorePState(savefile, current);
|
|
idPhysics_Player_RestorePState(savefile, saved);
|
|
|
|
savefile->ReadFloat(walkSpeed);
|
|
savefile->ReadFloat(crouchSpeed);
|
|
savefile->ReadFloat(maxStepHeight);
|
|
savefile->ReadFloat(maxJumpHeight);
|
|
savefile->ReadInt(debugLevel);
|
|
|
|
savefile->ReadUsercmd(command);
|
|
savefile->ReadAngles(viewAngles);
|
|
|
|
savefile->ReadInt(framemsec);
|
|
savefile->ReadFloat(frametime);
|
|
savefile->ReadFloat(playerSpeed);
|
|
savefile->ReadVec3(viewForward);
|
|
savefile->ReadVec3(viewRight);
|
|
|
|
savefile->ReadBool(walking);
|
|
savefile->ReadBool(groundPlane);
|
|
savefile->ReadTrace(groundTrace);
|
|
savefile->ReadMaterial(groundMaterial);
|
|
|
|
savefile->ReadBool(ladder);
|
|
savefile->ReadVec3(ladderNormal);
|
|
|
|
savefile->ReadInt((int&)waterLevel);
|
|
savefile->ReadInt(waterType);
|
|
}
|
|
|
|
/*
|
|
================
|
|
idPhysics_Player::SetPlayerInput
|
|
================
|
|
*/
|
|
void idPhysics_Player::SetPlayerInput(const usercmd_t& cmd, const idAngles& newViewAngles) {
|
|
command = cmd;
|
|
viewAngles = newViewAngles; // can't use cmd.angles cause of the delta_angles
|
|
}
|
|
|
|
/*
|
|
================
|
|
idPhysics_Player::SetSpeed
|
|
================
|
|
*/
|
|
void idPhysics_Player::SetSpeed(const float newWalkSpeed, const float newCrouchSpeed) {
|
|
walkSpeed = newWalkSpeed;
|
|
crouchSpeed = newCrouchSpeed;
|
|
}
|
|
|
|
/*
|
|
================
|
|
idPhysics_Player::SetMaxStepHeight
|
|
================
|
|
*/
|
|
void idPhysics_Player::SetMaxStepHeight(const float newMaxStepHeight) {
|
|
maxStepHeight = newMaxStepHeight;
|
|
}
|
|
|
|
/*
|
|
================
|
|
idPhysics_Player::GetMaxStepHeight
|
|
================
|
|
*/
|
|
float idPhysics_Player::GetMaxStepHeight(void) const {
|
|
return maxStepHeight;
|
|
}
|
|
|
|
/*
|
|
================
|
|
idPhysics_Player::SetMaxJumpHeight
|
|
================
|
|
*/
|
|
void idPhysics_Player::SetMaxJumpHeight(const float newMaxJumpHeight) {
|
|
maxJumpHeight = newMaxJumpHeight;
|
|
}
|
|
|
|
/*
|
|
================
|
|
idPhysics_Player::SetMovementType
|
|
================
|
|
*/
|
|
void idPhysics_Player::SetMovementType(const pmtype_t type) {
|
|
current.movementType = type;
|
|
}
|
|
|
|
/*
|
|
================
|
|
idPhysics_Player::SetKnockBack
|
|
================
|
|
*/
|
|
void idPhysics_Player::SetKnockBack(const int knockBackTime) {
|
|
if (current.movementTime) {
|
|
return;
|
|
}
|
|
current.movementFlags |= PMF_TIME_KNOCKBACK;
|
|
current.movementTime = knockBackTime;
|
|
}
|
|
|
|
/*
|
|
================
|
|
idPhysics_Player::SetDebugLevel
|
|
================
|
|
*/
|
|
void idPhysics_Player::SetDebugLevel(bool set) {
|
|
debugLevel = set;
|
|
}
|
|
|
|
/*
|
|
================
|
|
idPhysics_Player::Evaluate
|
|
================
|
|
*/
|
|
bool idPhysics_Player::Evaluate(int timeStepMSec, int endTimeMSec) {
|
|
idVec3 masterOrigin, oldOrigin;
|
|
idMat3 masterAxis;
|
|
|
|
// eliminate tiny values that can otherwise create denormals in long-running physics simulations
|
|
current.velocity.FixDenormals();
|
|
current.pushVelocity.FixDenormals();
|
|
current.origin.FixDenormals();
|
|
current.localOrigin.FixDenormals();
|
|
|
|
waterLevel = WATERLEVEL_NONE;
|
|
waterType = 0;
|
|
oldOrigin = current.origin;
|
|
|
|
clipModel->Unlink();
|
|
|
|
// if bound to a master
|
|
if (masterEntity) {
|
|
self->GetMasterPosition(masterOrigin, masterAxis);
|
|
current.origin = masterOrigin + current.localOrigin * masterAxis;
|
|
clipModel->Link(gameLocal.clip, self, 0, current.origin, clipModel->GetAxis());
|
|
if (timeStepMSec > 0) {
|
|
current.velocity = (current.origin - oldOrigin) / (timeStepMSec * 0.001f);
|
|
}
|
|
masterDeltaYaw = masterYaw;
|
|
masterYaw = masterAxis[0].ToYaw();
|
|
masterDeltaYaw = masterYaw - masterDeltaYaw;
|
|
return true;
|
|
}
|
|
|
|
ActivateContactEntities();
|
|
|
|
idPhysics_Player::MovePlayer(timeStepMSec);
|
|
|
|
clipModel->Link(gameLocal.clip, self, 0, current.origin, clipModel->GetAxis());
|
|
|
|
if (IsOutsideWorld()) {
|
|
gameLocal.Warning("clip model outside world bounds for entity '%s' at (%s)", self->name.c_str(), current.origin.ToString(0));
|
|
}
|
|
|
|
return true; //( current.origin != oldOrigin );
|
|
}
|
|
|
|
/*
|
|
================
|
|
idPhysics_Player::UpdateTime
|
|
================
|
|
*/
|
|
void idPhysics_Player::UpdateTime(int endTimeMSec) {
|
|
}
|
|
|
|
/*
|
|
================
|
|
idPhysics_Player::GetTime
|
|
================
|
|
*/
|
|
int idPhysics_Player::GetTime(void) const {
|
|
return gameLocal.time;
|
|
}
|
|
|
|
/*
|
|
================
|
|
idPhysics_Player::GetImpactInfo
|
|
================
|
|
*/
|
|
void idPhysics_Player::GetImpactInfo(const int id, const idVec3& point, impactInfo_t* info) const {
|
|
info->invMass = invMass;
|
|
info->invInertiaTensor.Zero();
|
|
info->position.Zero();
|
|
info->velocity = current.velocity;
|
|
}
|
|
|
|
/*
|
|
================
|
|
idPhysics_Player::ApplyImpulse
|
|
================
|
|
*/
|
|
void idPhysics_Player::ApplyImpulse(const int id, const idVec3& point, const idVec3& impulse) {
|
|
if (current.movementType != PM_NOCLIP) {
|
|
idVec3 add = impulse * invMass;
|
|
if (groundPlane) {
|
|
add -= (add * groundTrace.c.normal) * groundTrace.c.normal;
|
|
}
|
|
current.velocity += add;
|
|
}
|
|
}
|
|
|
|
/*
|
|
================
|
|
idPhysics_Player::IsAtRest
|
|
================
|
|
*/
|
|
bool idPhysics_Player::IsAtRest(void) const {
|
|
return false;
|
|
}
|
|
|
|
/*
|
|
================
|
|
idPhysics_Player::GetRestStartTime
|
|
================
|
|
*/
|
|
int idPhysics_Player::GetRestStartTime(void) const {
|
|
return -1;
|
|
}
|
|
|
|
/*
|
|
================
|
|
idPhysics_Player::SaveState
|
|
================
|
|
*/
|
|
void idPhysics_Player::SaveState(void) {
|
|
saved = current;
|
|
}
|
|
|
|
/*
|
|
================
|
|
idPhysics_Player::RestoreState
|
|
================
|
|
*/
|
|
void idPhysics_Player::RestoreState(void) {
|
|
current = saved;
|
|
|
|
clipModel->Link(gameLocal.clip, self, 0, current.origin, clipModel->GetAxis());
|
|
|
|
EvaluateContacts();
|
|
}
|
|
|
|
/*
|
|
================
|
|
idPhysics_Player::SetOrigin
|
|
================
|
|
*/
|
|
void idPhysics_Player::SetOrigin(const idVec3& newOrigin, int id) {
|
|
idVec3 masterOrigin;
|
|
idMat3 masterAxis;
|
|
|
|
current.localOrigin = newOrigin;
|
|
if (masterEntity) {
|
|
self->GetMasterPosition(masterOrigin, masterAxis);
|
|
current.origin = masterOrigin + newOrigin * masterAxis;
|
|
}
|
|
else {
|
|
current.origin = newOrigin;
|
|
}
|
|
|
|
clipModel->Link(gameLocal.clip, self, 0, current.origin, clipModel->GetAxis());
|
|
}
|
|
|
|
/*
|
|
================
|
|
idPhysics_Player::GetOrigin
|
|
================
|
|
*/
|
|
const idVec3& idPhysics_Player::PlayerGetOrigin(void) const {
|
|
return current.origin;
|
|
}
|
|
|
|
/*
|
|
================
|
|
idPhysics_Player::SetAxis
|
|
================
|
|
*/
|
|
void idPhysics_Player::SetAxis(const idMat3& newAxis, int id) {
|
|
clipModel->Link(gameLocal.clip, self, 0, current.origin, newAxis);
|
|
}
|
|
|
|
/*
|
|
================
|
|
idPhysics_Player::Translate
|
|
================
|
|
*/
|
|
void idPhysics_Player::Translate(const idVec3& translation, int id) {
|
|
|
|
current.localOrigin += translation;
|
|
current.origin += translation;
|
|
|
|
clipModel->Link(gameLocal.clip, self, 0, current.origin, clipModel->GetAxis());
|
|
}
|
|
|
|
/*
|
|
================
|
|
idPhysics_Player::Rotate
|
|
================
|
|
*/
|
|
void idPhysics_Player::Rotate(const idRotation& rotation, int id) {
|
|
idVec3 masterOrigin;
|
|
idMat3 masterAxis;
|
|
|
|
current.origin *= rotation;
|
|
if (masterEntity) {
|
|
self->GetMasterPosition(masterOrigin, masterAxis);
|
|
current.localOrigin = (current.origin - masterOrigin) * masterAxis.Transpose();
|
|
}
|
|
else {
|
|
current.localOrigin = current.origin;
|
|
}
|
|
|
|
clipModel->Link(gameLocal.clip, self, 0, current.origin, clipModel->GetAxis() * rotation.ToMat3());
|
|
}
|
|
|
|
/*
|
|
================
|
|
idPhysics_Player::SetLinearVelocity
|
|
================
|
|
*/
|
|
void idPhysics_Player::SetLinearVelocity(const idVec3& newLinearVelocity, int id) {
|
|
current.velocity = newLinearVelocity;
|
|
}
|
|
|
|
/*
|
|
================
|
|
idPhysics_Player::GetLinearVelocity
|
|
================
|
|
*/
|
|
const idVec3& idPhysics_Player::GetLinearVelocity(int id) const {
|
|
return current.velocity;
|
|
}
|
|
|
|
/*
|
|
================
|
|
idPhysics_Player::SetPushed
|
|
================
|
|
*/
|
|
void idPhysics_Player::SetPushed(int deltaTime) {
|
|
idVec3 velocity;
|
|
float d;
|
|
|
|
// velocity with which the player is pushed
|
|
velocity = (current.origin - saved.origin) / (deltaTime * idMath::M_MS2SEC);
|
|
|
|
// remove any downward push velocity
|
|
d = velocity * gravityNormal;
|
|
if (d > 0.0f) {
|
|
velocity -= d * gravityNormal;
|
|
}
|
|
|
|
current.pushVelocity += velocity;
|
|
}
|
|
|
|
/*
|
|
================
|
|
idPhysics_Player::GetPushedLinearVelocity
|
|
================
|
|
*/
|
|
const idVec3& idPhysics_Player::GetPushedLinearVelocity(const int id) const {
|
|
return current.pushVelocity;
|
|
}
|
|
|
|
/*
|
|
================
|
|
idPhysics_Player::ClearPushedVelocity
|
|
================
|
|
*/
|
|
void idPhysics_Player::ClearPushedVelocity(void) {
|
|
current.pushVelocity.Zero();
|
|
}
|
|
|
|
/*
|
|
================
|
|
idPhysics_Player::SetMaster
|
|
|
|
the binding is never orientated
|
|
================
|
|
*/
|
|
void idPhysics_Player::SetMaster(idEntity* master, const bool orientated) {
|
|
idVec3 masterOrigin;
|
|
idMat3 masterAxis;
|
|
|
|
if (master) {
|
|
if (!masterEntity) {
|
|
// transform from world space to master space
|
|
self->GetMasterPosition(masterOrigin, masterAxis);
|
|
current.localOrigin = (current.origin - masterOrigin) * masterAxis.Transpose();
|
|
masterEntity = master;
|
|
masterYaw = masterAxis[0].ToYaw();
|
|
}
|
|
ClearContacts();
|
|
}
|
|
else {
|
|
if (masterEntity) {
|
|
masterEntity = NULL;
|
|
}
|
|
}
|
|
}
|
|
|
|
const float PLAYER_VELOCITY_MAX = 4000;
|
|
const int PLAYER_VELOCITY_TOTAL_BITS = 16;
|
|
const int PLAYER_VELOCITY_EXPONENT_BITS = idMath::BitsForInteger(idMath::BitsForFloat(PLAYER_VELOCITY_MAX)) + 1;
|
|
const int PLAYER_VELOCITY_MANTISSA_BITS = PLAYER_VELOCITY_TOTAL_BITS - 1 - PLAYER_VELOCITY_EXPONENT_BITS;
|
|
const int PLAYER_MOVEMENT_TYPE_BITS = 3;
|
|
const int PLAYER_MOVEMENT_FLAGS_BITS = 8;
|
|
|
|
/*
|
|
================
|
|
idPhysics_Player::WriteToSnapshot
|
|
================
|
|
*/
|
|
void idPhysics_Player::WriteToSnapshot(idBitMsgDelta& msg) const {
|
|
msg.WriteFloat(current.origin[0]);
|
|
msg.WriteFloat(current.origin[1]);
|
|
msg.WriteFloat(current.origin[2]);
|
|
msg.WriteFloat(current.velocity[0], PLAYER_VELOCITY_EXPONENT_BITS, PLAYER_VELOCITY_MANTISSA_BITS);
|
|
msg.WriteFloat(current.velocity[1], PLAYER_VELOCITY_EXPONENT_BITS, PLAYER_VELOCITY_MANTISSA_BITS);
|
|
msg.WriteFloat(current.velocity[2], PLAYER_VELOCITY_EXPONENT_BITS, PLAYER_VELOCITY_MANTISSA_BITS);
|
|
msg.WriteDeltaFloat(current.origin[0], current.localOrigin[0]);
|
|
msg.WriteDeltaFloat(current.origin[1], current.localOrigin[1]);
|
|
msg.WriteDeltaFloat(current.origin[2], current.localOrigin[2]);
|
|
msg.WriteDeltaFloat(0.0f, current.pushVelocity[0], PLAYER_VELOCITY_EXPONENT_BITS, PLAYER_VELOCITY_MANTISSA_BITS);
|
|
msg.WriteDeltaFloat(0.0f, current.pushVelocity[1], PLAYER_VELOCITY_EXPONENT_BITS, PLAYER_VELOCITY_MANTISSA_BITS);
|
|
msg.WriteDeltaFloat(0.0f, current.pushVelocity[2], PLAYER_VELOCITY_EXPONENT_BITS, PLAYER_VELOCITY_MANTISSA_BITS);
|
|
msg.WriteDeltaFloat(0.0f, current.stepUp);
|
|
msg.WriteBits(current.movementType, PLAYER_MOVEMENT_TYPE_BITS);
|
|
msg.WriteBits(current.movementFlags, PLAYER_MOVEMENT_FLAGS_BITS);
|
|
msg.WriteDeltaLong(0, current.movementTime);
|
|
}
|
|
|
|
/*
|
|
================
|
|
idPhysics_Player::ReadFromSnapshot
|
|
================
|
|
*/
|
|
void idPhysics_Player::ReadFromSnapshot(const idBitMsgDelta& msg) {
|
|
current.origin[0] = msg.ReadFloat();
|
|
current.origin[1] = msg.ReadFloat();
|
|
current.origin[2] = msg.ReadFloat();
|
|
current.velocity[0] = msg.ReadFloat(PLAYER_VELOCITY_EXPONENT_BITS, PLAYER_VELOCITY_MANTISSA_BITS);
|
|
current.velocity[1] = msg.ReadFloat(PLAYER_VELOCITY_EXPONENT_BITS, PLAYER_VELOCITY_MANTISSA_BITS);
|
|
current.velocity[2] = msg.ReadFloat(PLAYER_VELOCITY_EXPONENT_BITS, PLAYER_VELOCITY_MANTISSA_BITS);
|
|
current.localOrigin[0] = msg.ReadDeltaFloat(current.origin[0]);
|
|
current.localOrigin[1] = msg.ReadDeltaFloat(current.origin[1]);
|
|
current.localOrigin[2] = msg.ReadDeltaFloat(current.origin[2]);
|
|
current.pushVelocity[0] = msg.ReadDeltaFloat(0.0f, PLAYER_VELOCITY_EXPONENT_BITS, PLAYER_VELOCITY_MANTISSA_BITS);
|
|
current.pushVelocity[1] = msg.ReadDeltaFloat(0.0f, PLAYER_VELOCITY_EXPONENT_BITS, PLAYER_VELOCITY_MANTISSA_BITS);
|
|
current.pushVelocity[2] = msg.ReadDeltaFloat(0.0f, PLAYER_VELOCITY_EXPONENT_BITS, PLAYER_VELOCITY_MANTISSA_BITS);
|
|
current.stepUp = msg.ReadDeltaFloat(0.0f);
|
|
current.movementType = msg.ReadBits(PLAYER_MOVEMENT_TYPE_BITS);
|
|
current.movementFlags = msg.ReadBits(PLAYER_MOVEMENT_FLAGS_BITS);
|
|
current.movementTime = msg.ReadDeltaLong(0);
|
|
|
|
current.origin.FixDenormals();
|
|
current.velocity.FixDenormals();
|
|
current.localOrigin.FixDenormals();
|
|
current.pushVelocity.FixDenormals();
|
|
|
|
if (clipModel) {
|
|
clipModel->Link(gameLocal.clip, self, 0, current.origin, clipModel->GetAxis());
|
|
}
|
|
}
|
|
|