mirror of
https://github.com/jmarshall23/DoomRTX.git
synced 2026-08-12 08:11:10 +02:00
574 lines
15 KiB
C++
574 lines
15 KiB
C++
#include "precompiled.h"
|
|
#pragma hdrstop
|
|
|
|
#include "prey_local.h"
|
|
|
|
// HUMANHEAD nla - Class constants
|
|
const int hhPhysics_AI::NO_FLY_DIRECTION = 1000;
|
|
|
|
CLASS_DECLARATION( idPhysics_Monster, hhPhysics_AI )
|
|
END_CLASS
|
|
|
|
/*
|
|
=====================
|
|
hhPhysics_AI::hhPhysics_AI
|
|
=====================
|
|
*/
|
|
hhPhysics_AI::hhPhysics_AI() {
|
|
// HUMANHEAD nla
|
|
flyStepDirection = NO_FLY_DIRECTION;
|
|
//HUMANHEAD END
|
|
lastMoveTouch = NULL;
|
|
useGravity = TRUE;
|
|
bGravClipModelAxis = false;
|
|
}
|
|
|
|
/*
|
|
=====================
|
|
hhPhysics_AI::SlideMove
|
|
=====================
|
|
// HUMANHEAD nla - if touched is NULL, the touched entities will be added
|
|
// directly. Otherwise they will be added to the list
|
|
*/
|
|
monsterMoveResult_t hhPhysics_AI::SlideMove( idVec3 &start, idVec3 &velocity, const idVec3 &delta, idList<int> *touched ) {
|
|
trace_t tr;
|
|
idVec3 move;
|
|
int i;
|
|
|
|
blockingEntity = NULL;
|
|
move = delta;
|
|
for( i = 0; i < 3; i++ ) {
|
|
gameLocal.clip.Translation( tr, start, start + move, clipModel, clipModel->GetAxis(), clipMask, self );
|
|
|
|
if ( tr.c.entityNum != ENTITYNUM_WORLD && tr.c.entityNum != ENTITYNUM_NONE && gameLocal.entities[ tr.c.entityNum ] )
|
|
lastMoveTouch = gameLocal.entities[ tr.c.entityNum ];
|
|
|
|
|
|
start = tr.endpos;
|
|
|
|
if ( tr.fraction == 1.0f ) {
|
|
if ( i > 0 ) {
|
|
return MM_SLIDING;
|
|
}
|
|
return MM_OK;
|
|
}
|
|
|
|
if ( tr.c.entityNum != ENTITYNUM_NONE ) {
|
|
blockingEntity = gameLocal.entities[ tr.c.entityNum ];
|
|
}
|
|
|
|
/*
|
|
// clip the movement delta and velocity
|
|
move.ProjectOntoPlane( tr.c.normal, OVERCLIP );
|
|
velocity.ProjectOntoPlane( tr.c.normal, OVERCLIP );
|
|
*/
|
|
|
|
// HUMANHEAD nla - Added logic to allow monsters to push
|
|
// if we can push other entities and not blocked by the world. Added PushCosine
|
|
if ( self->Pushes() && ( tr.c.entityNum != ENTITYNUM_WORLD ) ) {
|
|
// Early out if we aren't facing enough towards the object
|
|
idVec3 normVelocity( velocity );
|
|
|
|
normVelocity.NormalizeFast();
|
|
/*
|
|
gameLocal.Printf( "%d Trying %s * %s = %.2f vs %.2f\n", gameLocal.time,
|
|
normVelocity.ToString(), tr.c.normal.ToString(),
|
|
idMath::Fabs( normVelocity * tr.c.normal ), self->PushCosine() );
|
|
*/
|
|
if ( idMath::Fabs( normVelocity * tr.c.normal ) >= self->PushCosine() ) {
|
|
trace_t trPush;
|
|
int pushFlags;
|
|
float totalMass;
|
|
|
|
clipModel->SetPosition( start, 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;
|
|
|
|
// clip & push
|
|
totalMass = gameLocal.push.ClipTranslationalPush( trPush, self, pushFlags, start + move, move );
|
|
|
|
if ( totalMass > 0.0f ) {
|
|
// decrease velocity based on the total mass of the objects being pushed ?
|
|
/*? Put back in later?
|
|
if ( velocity.LengthSqr() > Square( crouchSpeed * 0.8f ) ) {
|
|
velocity *= crouchSpeed * 0.8f / velocity.Length();
|
|
}
|
|
pushed = true;
|
|
*/
|
|
}
|
|
|
|
// Added logic to prevent pushing
|
|
tr = trPush;
|
|
start = tr.endpos;
|
|
// time_left -= time_left * trace.fraction;
|
|
|
|
// if moved the entire distance
|
|
// Changed from orig.
|
|
/*
|
|
if ( trace.fraction >= 1.0f ) {
|
|
break;
|
|
}
|
|
*/
|
|
// Changed to this! = )
|
|
if ( tr.fraction == 1.0f ) {
|
|
if ( i > 0 ) {
|
|
return MM_SLIDING;
|
|
}
|
|
return MM_OK;
|
|
}
|
|
}
|
|
}
|
|
// HUMANHEAD END
|
|
|
|
// clip the movement delta and velocity
|
|
move.ProjectOntoPlane( tr.c.normal, OVERCLIP );
|
|
velocity.ProjectOntoPlane( tr.c.normal, OVERCLIP );
|
|
}
|
|
|
|
/* NOTE: Underlying code changed some, fit this back in if needed.
|
|
//HUMANHEAD nla
|
|
if (touched == NULL) {
|
|
if (tr.fraction < 1.0f) {
|
|
AddTouchEnt(tr.c.entityNum);
|
|
}
|
|
}
|
|
else {
|
|
touched->Append(tr.c.entityNum);
|
|
}
|
|
*/
|
|
return MM_BLOCKED;
|
|
}
|
|
|
|
// HUMANHEAD nla
|
|
/*
|
|
=====================
|
|
hhPhysics_AI::FlyMove
|
|
|
|
move start into the delta direction
|
|
the velocity is clipped conform any collisions
|
|
=====================
|
|
*/
|
|
#define NUM_DIRECTIONS 2
|
|
monsterMoveResult_t hhPhysics_AI::FlyMove( idVec3 &start, idVec3 &velocity, const idVec3 &delta ) {
|
|
trace_t tr;
|
|
idVec3 up, down, noStepPos, noStepVel, stepPos, stepVel, dirGrav;
|
|
monsterMoveResult_t result1, result2;
|
|
idVec3 originalStart, originalVelocity, bestStart, bestVelocity;
|
|
float noStepDistSq, distSq, bestDistSq;
|
|
int bestFlyStepDirection;
|
|
monsterMoveResult_t bestReturn;
|
|
idList<int> noStepEntities, stepEntities, bestEntities;
|
|
|
|
|
|
if ( delta == vec3_origin ) {
|
|
return MM_OK;
|
|
}
|
|
|
|
// Initialize
|
|
originalStart = start;
|
|
originalVelocity = velocity;
|
|
bestFlyStepDirection = NO_FLY_DIRECTION;
|
|
|
|
// try to move without stepping up
|
|
noStepPos = start;
|
|
noStepVel = velocity;
|
|
result1 = SlideMove( noStepPos, noStepVel, delta, &noStepEntities );
|
|
if ( result1 == MM_OK ) {
|
|
AddTouchEntList( noStepEntities );
|
|
start = noStepPos;
|
|
velocity = noStepVel;
|
|
return MM_OK;
|
|
}
|
|
|
|
// Assume no stepping is the best
|
|
bestStart = noStepPos;
|
|
bestVelocity = noStepVel;
|
|
bestReturn = result1;
|
|
bestEntities = noStepEntities;
|
|
// Add small fudge factor to take into account float issues
|
|
noStepDistSq = ((noStepPos - originalStart) * 1.001f).LengthSqr();
|
|
bestDistSq = noStepDistSq;
|
|
|
|
// Try to move around the obstacle
|
|
for ( int direction = 0; direction < NUM_DIRECTIONS ; direction++ ) {
|
|
stepEntities.Clear();
|
|
|
|
//! Maybe make this relative to the clip model?
|
|
if (direction == 0) { // Set the direction. Assumes 2x
|
|
dirGrav.Set(0, 0, -1);
|
|
}
|
|
else {
|
|
dirGrav *= -1;
|
|
}
|
|
|
|
// try to step "up"
|
|
up = start - dirGrav * maxStepHeight;
|
|
gameLocal.clip.Translation( tr, start, up, clipModel, clipModel->GetAxis(), clipMask, self );
|
|
if ( tr.fraction == 0.0f ) {
|
|
start = originalStart; // Reset
|
|
velocity = originalVelocity;
|
|
continue;
|
|
}
|
|
|
|
// Add the entity touched
|
|
if ( tr.fraction < 1.0f ) {
|
|
stepEntities.Append( tr.c.entityNum );
|
|
}
|
|
|
|
// try to move at the stepped up position
|
|
stepPos = tr.endpos;
|
|
stepVel = velocity;
|
|
result2 = SlideMove( stepPos, stepVel, delta, &stepEntities );
|
|
if ( result2 == MM_BLOCKED ) { // Couldn't move all the way at stepped pos
|
|
start = originalStart; // Reset
|
|
velocity = originalVelocity;
|
|
distSq = (stepPos - tr.endpos).LengthSqr(); // See if we moved further up there. Ignore the step up
|
|
if ((distSq > bestDistSq)) {
|
|
bestStart = stepPos;
|
|
bestVelocity = stepVel;
|
|
bestReturn = result2;
|
|
bestEntities = stepEntities;
|
|
bestDistSq = distSq;
|
|
bestFlyStepDirection = direction;
|
|
}
|
|
continue;
|
|
}
|
|
|
|
// Stepping is the best move
|
|
start = originalStart; // Reset
|
|
velocity = originalVelocity;
|
|
distSq = ( stepPos - originalStart ).LengthSqr(); // Include the distance up we traveled
|
|
if ( ( direction == flyStepDirection ) || // Going the same direction as last time
|
|
( ( ( direction < flyStepDirection ) || // Haven't tested the last direction
|
|
( bestFlyStepDirection != flyStepDirection ) ) && // Have tested the last, but coundn't go that way
|
|
( distSq > bestDistSq ) ) // Closer than the best
|
|
) {
|
|
bestStart = stepPos; // Save the no step version
|
|
bestVelocity = stepVel;
|
|
bestReturn = MM_STEPPED;
|
|
bestEntities = stepEntities;
|
|
bestDistSq = distSq;
|
|
bestFlyStepDirection = direction;
|
|
}
|
|
|
|
} //. Direction loop
|
|
|
|
// Didn't work, use the no step return
|
|
start = bestStart;
|
|
velocity = bestVelocity;
|
|
flyStepDirection = bestFlyStepDirection;
|
|
AddTouchEntList( bestEntities );
|
|
|
|
return( bestReturn );
|
|
}
|
|
|
|
/*
|
|
================
|
|
hhPhysics_AI::Evaluate
|
|
================
|
|
*/
|
|
bool hhPhysics_AI::Evaluate( int timeStepMSec, int endTimeMSec ) {
|
|
idVec3 masterOrigin, oldOrigin;
|
|
idMat3 masterAxis;
|
|
float timeStep;
|
|
float oldMasterYaw, oldMasterDeltaYaw; // HUMANHEAD pdm
|
|
|
|
timeStep = MS2SEC( timeStepMSec );
|
|
|
|
moveResult = MM_OK;
|
|
blockingEntity = NULL;
|
|
oldOrigin = current.origin;
|
|
oldMasterYaw = masterYaw; // HUMANHEAD pdm
|
|
oldMasterDeltaYaw = masterDeltaYaw; // HUMANHEAD pdm
|
|
|
|
//HUMANHEAD: aob
|
|
HadGroundContacts( HasGroundContacts() );
|
|
//HUMANHEAD END
|
|
|
|
// 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() );
|
|
//HUMANHEAD rww
|
|
if (!timeStep) {
|
|
current.velocity = vec3_origin;
|
|
}
|
|
else {
|
|
//HUMANHEAD END
|
|
current.velocity = ( current.origin - oldOrigin ) / timeStep;
|
|
}
|
|
masterDeltaYaw = masterYaw;
|
|
masterYaw = masterAxis[0].ToYaw();
|
|
masterDeltaYaw = masterYaw - masterDeltaYaw;
|
|
|
|
// HUMANHEAD pdm: If we haven't moved and master is at rest, put me to rest
|
|
if (current.origin == oldOrigin && masterYaw == oldMasterYaw && masterDeltaYaw == oldMasterDeltaYaw) {
|
|
if (masterEntity->IsAtRest()) {
|
|
Rest();
|
|
}
|
|
return false;
|
|
}
|
|
// HUMANHEAD END
|
|
return true;
|
|
}
|
|
|
|
// if the monster is at rest
|
|
if ( current.atRest >= 0 ) {
|
|
return false;
|
|
}
|
|
|
|
assert(timeStep != 0.0f); //HUMANHEAD rww
|
|
|
|
ActivateContactEntities();
|
|
|
|
// move the monster velocity into the frame of a pusher
|
|
current.velocity -= current.pushVelocity;
|
|
|
|
clipModel->Unlink();
|
|
|
|
// check if on the ground
|
|
//HUMANHEAD: aob - moved ground check to after movement
|
|
|
|
// if not on the ground or moving upwards
|
|
float upspeed;
|
|
if ( gravityNormal != vec3_zero ) {
|
|
upspeed = -( current.velocity * gravityNormal );
|
|
} else {
|
|
upspeed = current.velocity.z;
|
|
}
|
|
if ( fly || self->fl.isTractored || ( !forceDeltaMove && ( !current.onGround || upspeed > 1.0f ) ) ) {
|
|
if ( upspeed < 0.0f ) {
|
|
moveResult = MM_FALLING;
|
|
}
|
|
else {
|
|
current.onGround = false;
|
|
moveResult = MM_OK;
|
|
}
|
|
delta = current.velocity * timeStep;
|
|
if ( delta != vec3_origin ) {
|
|
//HUMANHEAD: aob - removed scope hardcode
|
|
moveResult = SlideMove( current.origin, current.velocity, delta );
|
|
delta.Zero();
|
|
}
|
|
|
|
if ( !fly && !self->fl.isTractored && IsGravityEnabled()) { // HUMANHEAD JRM: Gravity toggle
|
|
current.velocity += gravityVector * timeStep;
|
|
}
|
|
} else {
|
|
if ( useVelocityMove ) {
|
|
delta = current.velocity * timeStep;
|
|
} else {
|
|
current.velocity = delta / timeStep;
|
|
}
|
|
|
|
current.velocity -= ( current.velocity * gravityNormal ) * gravityNormal;
|
|
|
|
if ( delta == vec3_origin ) {
|
|
Rest();
|
|
} else {
|
|
// try moving into the desired direction
|
|
//HUMANHEAD: aob
|
|
current.velocity = delta / timeStep;
|
|
//HUMANHEAD END
|
|
|
|
//HUMANHEAD: aob - removed scope hardcode
|
|
//moveResult = idPhysics_Monster::StepMove( current.origin, current.velocity, delta );
|
|
if( IsGravityEnabled() ) {
|
|
moveResult = StepMove( current.origin, current.velocity, delta );
|
|
} else {
|
|
moveResult = SlideMove( current.origin, current.velocity, delta );
|
|
}
|
|
|
|
delta.Zero();
|
|
}
|
|
}
|
|
|
|
//HUMANHEAD: aob - for changing gravity zones
|
|
idVec3 rotationCheckOrigin = GetOrigin() + GetAxis()[2] * GetBounds()[1].z;
|
|
IterativeRotateMove( GetAxis()[2], -GetGravityNormal(), GetOrigin(), rotationCheckOrigin, p_iterRotMoveNumIterations.GetInteger() );
|
|
//HUMANHEAD END
|
|
|
|
clipModel->Link( gameLocal.clip, self, 0, current.origin, clipModel->GetAxis() );
|
|
|
|
// check if on the ground
|
|
//HUMANHEAD: aob - removed scope hardcode
|
|
CheckGround( current );
|
|
|
|
// get all the ground contacts
|
|
EvaluateContacts();
|
|
|
|
// move the monster velocity back into the world frame
|
|
current.velocity += current.pushVelocity;
|
|
current.pushVelocity.Zero();
|
|
|
|
if ( IsOutsideWorld() ) {
|
|
// HUMANHEAD pdm: Allow some things to go outside world without warning
|
|
if (!self->IsType(hhWraith::Type) && !self->IsType( hhTalon::Type ) ) {
|
|
gameLocal.Warning( "clip model outside world bounds for entity '%s' at (%s)", self->name.c_str(), current.origin.ToString(0) );
|
|
}
|
|
Rest();
|
|
}
|
|
|
|
return ( current.origin != oldOrigin );
|
|
}
|
|
|
|
/*
|
|
==================
|
|
hhPhysics_AI::ApplyFriction
|
|
|
|
Handles both ground friction and water friction
|
|
|
|
//HUMANHEAD: aob
|
|
==================
|
|
*/
|
|
idVec3 hhPhysics_AI::ApplyFriction( const idVec3& vel, const float deltaTime ) {
|
|
float speed = 0.0f;
|
|
float newSpeed = 0.0f;
|
|
float control = 0.0f;
|
|
float drop = 0.0f;
|
|
idVec3 velocity = vel;
|
|
|
|
speed = velocity.Length();
|
|
if( speed <= VECTOR_EPSILON ) {
|
|
return vec3_origin;
|
|
}
|
|
|
|
if( fly || self->fl.isTractored ) {
|
|
drop += speed * PM_FLYFRICTION * deltaTime;
|
|
}
|
|
else if( !current.onGround ) {
|
|
drop += speed * PM_AIRFRICTION * deltaTime;
|
|
}
|
|
else if( current.onGround ) {
|
|
if( !(GetGroundSurfaceFlags() & SURF_SLICK) ) {
|
|
drop += speed * PM_FRICTION * deltaTime;
|
|
}
|
|
}
|
|
|
|
// scale the velocity
|
|
newSpeed = speed - drop;
|
|
if( newSpeed < 0.0f ) {
|
|
newSpeed = 0.0f;
|
|
}
|
|
|
|
return vel * ( newSpeed / speed );
|
|
}
|
|
|
|
/*
|
|
================
|
|
hhPhysics_AI::AddForce
|
|
HUMANHEAD pdm: added so that springs will work on monsters
|
|
================
|
|
*/
|
|
void hhPhysics_AI::AddForce( const int id, const idVec3 &point, const idVec3 &force ) {
|
|
// HUMANHEAD pdm: so we can use forces on them
|
|
current.velocity += (force / GetMass()) * USERCMD_ONE_OVER_HZ;
|
|
|
|
Activate();
|
|
}
|
|
|
|
/*
|
|
================
|
|
hhPhysics_AI::SetLinearVelocity
|
|
================
|
|
*/
|
|
void hhPhysics_AI::SetLinearVelocity( const idVec3 &newLinearVelocity, int id ) {
|
|
current.velocity = newLinearVelocity;
|
|
|
|
Activate();
|
|
}
|
|
|
|
/*
|
|
================
|
|
hhPhysics_AI::GetLinearVelocity
|
|
================
|
|
*/
|
|
const idVec3& hhPhysics_AI::GetLinearVelocity( int id ) const {
|
|
return current.velocity;
|
|
}
|
|
|
|
/*
|
|
================
|
|
hhPhysics_AI::ApplyImpulse
|
|
================
|
|
*/
|
|
void hhPhysics_AI::ApplyImpulse( const int id, const idVec3 &point, const idVec3 &impulse ) {
|
|
// HUMANHEAD: aob - so we can use forces on them (crane)
|
|
current.velocity += impulse / GetMass();
|
|
// HUMANHEAD END
|
|
|
|
Activate();
|
|
}
|
|
|
|
/*
|
|
================
|
|
hhPhysics_AI::SetMaster
|
|
overridden to set localAxis
|
|
================
|
|
*/
|
|
void hhPhysics_AI::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();
|
|
idAI *entAI = static_cast<idAI*>(self);
|
|
if ( entAI ) {
|
|
idAngles angles( 0, entAI->spawnArgs.GetFloat( "angle" ), 0 );
|
|
localAxis = angles.ToMat3() * masterAxis.Transpose();
|
|
}
|
|
}
|
|
ClearContacts();
|
|
}
|
|
else {
|
|
if ( masterEntity ) {
|
|
masterEntity = NULL;
|
|
Activate();
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
================
|
|
hhPhysics_AI::SetGravity
|
|
overridden to call SetClipModelAxis for asteroid gravity
|
|
================
|
|
*/
|
|
void hhPhysics_AI::SetGravity( const idVec3 &newGravity ) {
|
|
idPhysics_Monster::SetGravity( newGravity );
|
|
if ( bGravClipModelAxis ) {
|
|
SetClipModelAxis();
|
|
}
|
|
}
|
|
|
|
//================
|
|
//hhPhysics_AI::Save
|
|
//================
|
|
void hhPhysics_AI::Save( idSaveGame *savefile ) const {
|
|
savefile->WriteInt( flyStepDirection );
|
|
lastMoveTouch.Save( savefile );
|
|
savefile->WriteBool( useGravity );
|
|
savefile->WriteMat3( localAxis );
|
|
savefile->WriteBool( bGravClipModelAxis );
|
|
}
|
|
|
|
//================
|
|
//hhPhysics_AI::Restore
|
|
//================
|
|
void hhPhysics_AI::Restore( idRestoreGame *savefile ) {
|
|
savefile->ReadInt( flyStepDirection );
|
|
lastMoveTouch.Restore( savefile );
|
|
savefile->ReadBool( useGravity );
|
|
savefile->ReadMat3( localAxis );
|
|
savefile->ReadBool( bGravClipModelAxis );
|
|
}
|
|
|