874 lines
26 KiB
C++
874 lines
26 KiB
C++
// Copyright (C) 2007 Id Software, Inc.
|
|
//
|
|
|
|
#include "../precompiled.h"
|
|
#pragma hdrstop
|
|
|
|
#if defined( _DEBUG ) && !defined( ID_REDIRECT_NEWDELETE )
|
|
#define new DEBUG_NEW
|
|
#undef THIS_FILE
|
|
static char THIS_FILE[] = __FILE__;
|
|
#endif
|
|
|
|
#include "DeliveryVehicle.h"
|
|
#include "../script/Script_Helper.h"
|
|
#include "../physics/Physics.h"
|
|
#include "../ContentMask.h"
|
|
|
|
|
|
#define JOTUN_FLY_HEIGHT 4096
|
|
#define JOTUN_FLY_HEIGHT_MIN 400
|
|
#define JOTUN_FLY_HEIGHT_RESCUE 2300
|
|
|
|
#define HOVER_HEIGHT_MIN 400
|
|
#define HOVER_HEIGHT_RESCUE 2100
|
|
#define HOVER_HEIGHT_AIM 2300
|
|
#define HOVER_DOWNCAST_LENGTH 2500
|
|
|
|
|
|
/*
|
|
===============================================================================
|
|
|
|
sdDeliveryVehicle
|
|
|
|
===============================================================================
|
|
*/
|
|
const idEventDef EV_StartJotunDelivery( "startJotunDelivery", "fff" );
|
|
const idEventDef EV_StartJotunReturn( "startJotunReturn", "fff" );
|
|
|
|
const idEventDef EV_StartMagogDelivery( "startMagogDelivery", "fffvf" );
|
|
const idEventDef EV_StartMagogReturn( "startMagogReturn", "fffv" );
|
|
|
|
CLASS_DECLARATION( sdScriptEntity, sdDeliveryVehicle )
|
|
EVENT( EV_StartJotunDelivery, sdDeliveryVehicle::Event_StartJotunDelivery )
|
|
EVENT( EV_StartJotunReturn, sdDeliveryVehicle::Event_StartJotunReturn )
|
|
|
|
EVENT( EV_StartMagogDelivery, sdDeliveryVehicle::Event_StartMagogDelivery )
|
|
EVENT( EV_StartMagogReturn, sdDeliveryVehicle::Event_StartMagogReturn )
|
|
END_CLASS
|
|
|
|
/*
|
|
================
|
|
sdDeliveryVehicle::Spawn
|
|
================
|
|
*/
|
|
void sdDeliveryVehicle::Spawn( void ) {
|
|
vehicleMode = VMODE_NONE;
|
|
deliveryMode = DMODE_NONE;
|
|
modeStartTime = 0;
|
|
|
|
lastRollAccel = 0.0f;
|
|
pathLength = 0.0f;
|
|
pathSpeed = 0.0f;
|
|
leadTime = 0.0f;
|
|
itemRotation = 0.0f;
|
|
endPoint.Zero();
|
|
|
|
maxZAccel = spawnArgs.GetFloat( "max_thrust" );
|
|
maxZVel = spawnArgs.GetFloat( "max_z_vel" );
|
|
}
|
|
|
|
/*
|
|
================
|
|
sdDeliveryVehicle::Think
|
|
================
|
|
*/
|
|
void sdDeliveryVehicle::Think( void ) {
|
|
|
|
if ( vehicleMode == VMODE_JOTUN ) {
|
|
Jotun_Think();
|
|
} else if ( vehicleMode == VMODE_MAGOG ) {
|
|
Magog_Think();
|
|
}
|
|
|
|
sdScriptEntity::Think();
|
|
}
|
|
|
|
/*
|
|
================
|
|
sdDeliveryVehicle::PostThink
|
|
================
|
|
*/
|
|
void sdDeliveryVehicle::PostThink( void ) {
|
|
sdScriptEntity::PostThink();
|
|
}
|
|
|
|
/*
|
|
================
|
|
sdDeliveryVehicle::Event_StartJotunDelivery
|
|
================
|
|
*/
|
|
void sdDeliveryVehicle::Event_StartJotunDelivery( float startTime, float _pathSpeed, float _leadTime ) {
|
|
vehicleMode = VMODE_JOTUN;
|
|
deliveryMode = DMODE_DELIVER;
|
|
modeStartTime = SEC2MS( startTime );
|
|
|
|
pathSpeed = _pathSpeed;
|
|
pathLength = PathGetLength();
|
|
leadTime = _leadTime;
|
|
endPoint = PathGetPoint( PathGetNumPoints() - 1 );
|
|
endPoint.z += JOTUN_FLY_HEIGHT;
|
|
}
|
|
|
|
/*
|
|
================
|
|
sdDeliveryVehicle::Event_StartJotunReturn
|
|
================
|
|
*/
|
|
void sdDeliveryVehicle::Event_StartJotunReturn( float startTime, float _pathSpeed, float _leadTime ) {
|
|
vehicleMode = VMODE_JOTUN;
|
|
deliveryMode = DMODE_RETURN;
|
|
modeStartTime = SEC2MS( startTime );
|
|
|
|
pathSpeed = _pathSpeed;
|
|
pathLength = PathGetLength();
|
|
leadTime = _leadTime;
|
|
endPoint = PathGetPoint( PathGetNumPoints() - 1 );
|
|
endPoint.z += JOTUN_FLY_HEIGHT;
|
|
}
|
|
|
|
/*
|
|
================
|
|
sdDeliveryVehicle::Jotun_Think
|
|
================
|
|
*/
|
|
void sdDeliveryVehicle::Jotun_Think( void ) {
|
|
if ( deliveryMode == DMODE_NONE || gameLocal.IsPaused() ) {
|
|
return;
|
|
}
|
|
if ( PathGetNumPoints() < 2 ) {
|
|
return;
|
|
}
|
|
|
|
float time = MS2SEC( gameLocal.time - modeStartTime );
|
|
float frameTime = MS2SEC( gameLocal.msec );
|
|
float aheadPosition = ( time + leadTime ) * pathSpeed;
|
|
|
|
// look ahead by a couple of seconds
|
|
idVec3 aheadPoint;
|
|
idVec3 aheadPointDir;
|
|
PathGetPosition( aheadPosition, aheadPoint );
|
|
PathGetDirection( aheadPosition, aheadPointDir );
|
|
aheadPoint.z += JOTUN_FLY_HEIGHT;
|
|
|
|
if ( deliveryMode == DMODE_DELIVER ) {
|
|
bool levelOut = false;
|
|
if ( aheadPosition > pathLength - 10000.0f ) {
|
|
levelOut = true;
|
|
}
|
|
|
|
Jotun_DoMove( aheadPoint, aheadPointDir, endPoint, levelOut, false, pathSpeed );
|
|
} else if ( deliveryMode == DMODE_RETURN ) {
|
|
Jotun_DoMove( aheadPoint, aheadPointDir, endPoint, false, true, pathSpeed );
|
|
}
|
|
}
|
|
|
|
/*
|
|
================
|
|
sdDeliveryVehicle::Jotun_DoMove
|
|
================
|
|
*/
|
|
void sdDeliveryVehicle::Jotun_DoMove( const idVec3& aheadPointIdeal, const idVec3& aheadPointDirIdeal, const idVec3& endPoint, bool levelOut, bool leaving, float pathSpeed ) {
|
|
float frameTime = MS2SEC( gameLocal.msec );
|
|
|
|
idVec3 aheadPoint = aheadPointIdeal;
|
|
idVec3 aheadPointDir = aheadPointDirIdeal;
|
|
|
|
const idVec3& origin = GetPhysics()->GetOrigin();
|
|
const idVec3& velocity = GetPhysics()->GetLinearVelocity();
|
|
const idMat3& axis = GetPhysics()->GetAxis();
|
|
const idAngles angles = axis.ToAngles();
|
|
const idVec3& angVel = GetPhysics()->GetAngularVelocity();
|
|
|
|
const idVec3& currentFwd = axis[ 0 ];
|
|
const idVec3& currentRight = axis[ 1 ];
|
|
const idVec3& currentUp = axis[ 2 ];
|
|
|
|
float rollVel = angVel * currentFwd;
|
|
float pitchVel = angVel * currentRight;
|
|
float yawVel = angVel * currentUp;
|
|
|
|
float aheadToEnd = ( aheadPoint - endPoint ).Length();
|
|
if ( aheadToEnd < 4096.0f && !leaving ) {
|
|
aheadPoint.z += 4096.0f;
|
|
}
|
|
|
|
float currentHeight = JOTUN_FLY_HEIGHT_RESCUE;
|
|
|
|
// check out ahead and see that we're not going to collide with something
|
|
if ( velocity != vec3_origin ) {
|
|
idVec3 futureSpot = velocity * 1.0f;
|
|
futureSpot.z -= 4096.0f;
|
|
futureSpot.Normalize();;
|
|
futureSpot *= 3500.0f;
|
|
futureSpot += origin;
|
|
|
|
trace_t trace;
|
|
gameLocal.clip.TraceBounds( CLIP_DEBUG_PARMS trace, origin, futureSpot, GetPhysics()->GetBounds(), axis, MASK_SOLID | MASK_OPAQUE, this );
|
|
futureSpot = trace.endpos;
|
|
float futureFrac = trace.fraction;
|
|
|
|
float futureDist = futureFrac * 3500.0f;
|
|
if ( futureDist < 3500.0f ) {
|
|
int surfaceFlags = trace.c.material != NULL ? trace.c.material->GetSurfaceFlags() : 0;
|
|
if ( !( surfaceFlags & SURF_NOIMPACT ) ) {
|
|
float moveUp = ( 3500.0f - futureDist ) / 3500.0f;
|
|
moveUp = idMath::Sqrt( moveUp );
|
|
|
|
if ( aheadToEnd < 32.0f || leaving ) {
|
|
aheadPoint = origin + currentFwd * 3500.0f;
|
|
aheadPoint.z += moveUp * 4096.0f;
|
|
aheadPointDir.z += moveUp * 10.0f;
|
|
}
|
|
|
|
aheadPoint.z += moveUp * 4096.0f;
|
|
aheadPointDir.z += moveUp * 2.0f;
|
|
aheadPointDir.Normalize();
|
|
} else {
|
|
futureDist = 3500.0f;
|
|
}
|
|
}
|
|
|
|
currentHeight = futureDist;
|
|
|
|
// gameRenderWorld->DebugCircle( colorRed, origin, idVec3( 0.0f, 0.0f, 1.0f ), 16, 8 );
|
|
// gameRenderWorld->DebugArrow( colorRed, origin, futureSpot, 256 );
|
|
// gameRenderWorld->DebugCircle( colorRed, futureSpot, idVec3( 0.0f, 0.0f, 1.0f ), 16, 8 );
|
|
}
|
|
// gameRenderWorld->DebugCircle( colorGreen, aheadPoint, idVec3( 0.0f, 0.0f, 1.0f ), 256, 16 );
|
|
|
|
|
|
// generate a cubic spline between the two points
|
|
idVec3 point = vec3_origin;
|
|
|
|
{
|
|
idVec3 x0 = origin;
|
|
idVec3 x1 = aheadPoint;
|
|
idVec3 dx0 = velocity * 3.0f; // maintaining our current velocity is more important
|
|
idVec3 dx1 = aheadPointDir * pathSpeed * 1.0f; // than matching the destination vector
|
|
|
|
// calculate coefficients
|
|
idVec3 D = x0;
|
|
idVec3 C = dx0;
|
|
idVec3 B = 3*x1 - dx1 - 2*C - 3*D;
|
|
idVec3 A = x1 - B - C - D;
|
|
|
|
float distanceLeft = ( endPoint - origin ).Length();
|
|
float lookaheadFactor = ( distanceLeft / 4096.0f ) * 0.5f;
|
|
lookaheadFactor = idMath::ClampFloat( 0.2f, 0.5f, lookaheadFactor );
|
|
|
|
point = ( A * lookaheadFactor + B )*lookaheadFactor*lookaheadFactor + C*lookaheadFactor + D;
|
|
}
|
|
|
|
idVec3 idealNewForward = point - origin;
|
|
idealNewForward.Normalize();
|
|
|
|
idVec3 delta = point - origin;
|
|
idVec3 endDelta = endPoint - origin;
|
|
endDelta.Normalize();
|
|
endDelta.z = 0.0f;
|
|
|
|
idAngles endAngles = endDelta.ToAngles().Normalize180();
|
|
|
|
// treat the local delta as if the thing is on a flat plane - simplifies everything
|
|
idVec3 localDelta = axis.TransposeMultiply( delta );
|
|
|
|
idVec3 newVelocity = vec3_origin;
|
|
idVec3 newAngVel = angVel;
|
|
|
|
//
|
|
// A flying vehicle needs to roll and then pull up to turn (to look cool)
|
|
//
|
|
|
|
//
|
|
// Roll!
|
|
float targetRoll = endAngles.roll;
|
|
if ( !levelOut ) {
|
|
targetRoll = -( localDelta.y / 1024.0f ) * 45.0f;
|
|
targetRoll = idMath::ClampFloat( -30.0f, 30.0f, targetRoll );
|
|
}
|
|
|
|
float rollAmount = idMath::AngleNormalize180( targetRoll - angles.roll );
|
|
|
|
// so we know how much we need to roll to get to our new angle
|
|
// how fast do we want to go to get there?
|
|
float targetRollVel = ( rollAmount / 15 ) * 30;
|
|
targetRollVel = idMath::ClampFloat( -30.0f, 30.0f, targetRollVel );
|
|
|
|
float maxRollAccel = 15.0f;
|
|
if ( levelOut ) {
|
|
maxRollAccel = 35.0f;
|
|
}
|
|
|
|
// now figure out what roll acceleration that needs, clamp it
|
|
float rollAccel = idMath::AngleNormalize180( targetRollVel - rollVel ) / frameTime;
|
|
rollAccel = idMath::ClampFloat( -maxRollAccel, maxRollAccel, rollAccel );
|
|
|
|
// dampen out the rolling inputs, like its a guy on a stick controlling it
|
|
rollAccel = rollAccel * 0.2f + lastRollAccel * 0.8f;
|
|
if ( idMath::Fabs( rollAccel ) < idMath::FLT_EPSILON ) {
|
|
rollAccel = 0.f;
|
|
}
|
|
newAngVel += rollAccel * frameTime * currentFwd;
|
|
|
|
lastRollAccel = rollAccel;
|
|
|
|
//
|
|
// Pull up!
|
|
float targetPitch = -( localDelta.z / 256.0f ) * 15.0f;
|
|
if ( !leaving ) {
|
|
targetPitch = idMath::ClampFloat( -25.0f, 15.0f, targetPitch );
|
|
} else {
|
|
targetPitch = idMath::ClampFloat( -25.0f, 15.0f, targetPitch );
|
|
}
|
|
|
|
if ( leaving && targetPitch > -15.0f ) {
|
|
targetPitch = -15.0f;
|
|
}
|
|
|
|
float pitchAmount = idMath::AngleNormalize180( targetPitch - angles.pitch );
|
|
|
|
// so we know how much we need to pitch to get to our new angle
|
|
// how fast do we want to go to get there?
|
|
float targetPitchVel = ( pitchAmount / 8 ) * 15;
|
|
if ( !leaving ) {
|
|
targetPitchVel = idMath::ClampFloat( -25.0f, 15.0f, targetPitchVel );
|
|
} else {
|
|
targetPitchVel = idMath::ClampFloat( -35.0f, 15.0f, targetPitchVel );
|
|
}
|
|
|
|
// now figure out what roll acceleration that needs, clamp it
|
|
float pitchAccel = idMath::AngleNormalize180( targetPitchVel - pitchVel ) / frameTime;
|
|
if ( !leaving ) {
|
|
pitchAccel = idMath::ClampFloat( -12.0f, 12.0f, pitchAccel );
|
|
} else {
|
|
pitchAccel = idMath::ClampFloat( -36.0f, 12.0f, pitchAccel );
|
|
}
|
|
|
|
newAngVel += pitchAccel * frameTime * currentRight;
|
|
|
|
//
|
|
// Yaw
|
|
float targetYaw = angles.yaw;
|
|
if ( !levelOut ) {
|
|
targetRoll = -( localDelta.y / 1024.0f ) * 5.0f;
|
|
targetRoll = idMath::ClampFloat( -8.0f, 8.0f, targetRoll );
|
|
} else {
|
|
targetYaw = endAngles.yaw;
|
|
}
|
|
|
|
float yawAmount = idMath::AngleNormalize180( targetYaw - angles.yaw );
|
|
|
|
// so we know how much we need to yaw to get to our new angle
|
|
// how fast do we want to go to get there?
|
|
float targetYawVel = ( yawAmount / 8 ) * 15;
|
|
targetYawVel = idMath::ClampFloat( -15.0f, 15.0f, targetYawVel );
|
|
|
|
// now figure out what yaw acceleration that needs, clamp it
|
|
float yawAccel = idMath::AngleNormalize180( targetYawVel - yawVel ) / frameTime;
|
|
yawAccel = idMath::ClampFloat( -10.0f, 10.0f, yawAccel );
|
|
|
|
newAngVel += yawAccel * frameTime * currentUp;
|
|
|
|
newVelocity = currentFwd * pathSpeed;
|
|
if ( levelOut ) {
|
|
// HACK: Drift back towards the target, to try to ensure it gets there ok
|
|
newVelocity = newVelocity * 0.7f + endDelta * pathSpeed * 0.3f;
|
|
}
|
|
|
|
|
|
// HACK: ensure it never gets below the minimum height
|
|
if ( currentHeight < JOTUN_FLY_HEIGHT_MIN ) {
|
|
idVec3 newOrigin = origin;
|
|
newOrigin.z = newOrigin.z - currentHeight + JOTUN_FLY_HEIGHT_MIN;
|
|
GetPhysics()->SetOrigin( newOrigin );
|
|
} else if ( currentHeight < JOTUN_FLY_HEIGHT_RESCUE ) {
|
|
float oldNewVelLength = newVelocity.Length();
|
|
|
|
float scale = ( JOTUN_FLY_HEIGHT_RESCUE - currentHeight ) / ( JOTUN_FLY_HEIGHT_RESCUE - JOTUN_FLY_HEIGHT_MIN );
|
|
scale = idMath::Sqrt( scale );
|
|
float rescueVelocity = scale * ( JOTUN_FLY_HEIGHT_RESCUE - JOTUN_FLY_HEIGHT_MIN ) / 0.5f;
|
|
float rescueAcceleration = ( rescueVelocity - velocity.z ) / frameTime;
|
|
rescueAcceleration = idMath::ClampFloat( 0.0f, 1800.0f, rescueAcceleration );
|
|
rescueVelocity = velocity.z + rescueAcceleration * frameTime;
|
|
if ( rescueVelocity > newVelocity.z ) {
|
|
newVelocity.z = rescueVelocity;
|
|
}
|
|
|
|
newVelocity.Normalize();
|
|
newVelocity *= oldNewVelLength;
|
|
}
|
|
|
|
//
|
|
// set the new stuff
|
|
//
|
|
GetPhysics()->SetLinearVelocity( newVelocity );
|
|
GetPhysics()->SetAngularVelocity( newAngVel );
|
|
}
|
|
|
|
|
|
/*
|
|
================
|
|
sdDeliveryVehicle::Event_StartMagogDelivery
|
|
================
|
|
*/
|
|
void sdDeliveryVehicle::Event_StartMagogDelivery( float startTime, float _pathSpeed, float _leadTime, const idVec3& _endPoint, float _itemRotation ) {
|
|
vehicleMode = VMODE_MAGOG;
|
|
deliveryMode = DMODE_DELIVER;
|
|
modeStartTime = SEC2MS( startTime );
|
|
|
|
pathSpeed = _pathSpeed;
|
|
pathLength = PathGetLength();
|
|
leadTime = _leadTime;
|
|
|
|
endPoint = _endPoint;
|
|
itemRotation = _itemRotation;
|
|
}
|
|
|
|
/*
|
|
================
|
|
sdDeliveryVehicle::Event_StartMagogReturn
|
|
================
|
|
*/
|
|
void sdDeliveryVehicle::Event_StartMagogReturn( float startTime, float _pathSpeed, float _leadTime, const idVec3& _endPoint ) {
|
|
vehicleMode = VMODE_MAGOG;
|
|
deliveryMode = DMODE_RETURN;
|
|
modeStartTime = SEC2MS( startTime );
|
|
|
|
pathSpeed = _pathSpeed;
|
|
pathLength = PathGetLength();
|
|
leadTime = _leadTime;
|
|
|
|
endPoint = _endPoint;
|
|
}
|
|
|
|
/*
|
|
================
|
|
sdDeliveryVehicle::Magog_Think
|
|
================
|
|
*/
|
|
void sdDeliveryVehicle::Magog_Think() {
|
|
int numPoints = PathGetNumPoints();
|
|
if ( numPoints < 2 ) {
|
|
return;
|
|
}
|
|
if ( deliveryMode == DMODE_NONE || gameLocal.IsPaused() ) {
|
|
return;
|
|
}
|
|
assert( gameLocal.msec != 0 );
|
|
|
|
float time = MS2SEC( gameLocal.time - modeStartTime );
|
|
float frameTime = MS2SEC( gameLocal.msec );
|
|
|
|
float position = time * pathSpeed;
|
|
|
|
// look ahead by a couple of seconds
|
|
float aheadPosition = position + leadTime * pathSpeed;
|
|
idVec3 aheadPoint;
|
|
idVec3 aheadPointDir;
|
|
PathGetPosition( aheadPosition, aheadPoint );
|
|
PathGetDirection( aheadPosition, aheadPointDir );
|
|
|
|
bool approachingEnd = false;
|
|
bool clampRoll = true;
|
|
float yawScale = 1.0f;
|
|
bool slowNearEnd = false;
|
|
|
|
if ( deliveryMode == DMODE_DELIVER ) {
|
|
slowNearEnd = true;
|
|
|
|
if ( position > pathLength - 6000.0f ) {
|
|
approachingEnd = true;
|
|
clampRoll = false;
|
|
|
|
// scale the max yaw
|
|
yawScale = ( ( pathLength - position ) / 4096.0f );
|
|
yawScale = 5.0f - 5.0f * yawScale * yawScale * yawScale;
|
|
yawScale = idMath::ClampFloat( 0.0f, 5.0f, yawScale );
|
|
}
|
|
|
|
} else {
|
|
aheadPoint.z += HOVER_HEIGHT_AIM;
|
|
|
|
if ( position < 4096.0f ) {
|
|
clampRoll = false;
|
|
|
|
// scale the max yaw
|
|
yawScale = ( 4096.0f - position ) / 4096.0f;
|
|
yawScale = 5.0f - 5.0f * yawScale * yawScale * yawScale;
|
|
yawScale = idMath::ClampFloat( 0.0f, 5.0f, yawScale );
|
|
}
|
|
}
|
|
|
|
Magog_DoMove( aheadPoint, aheadPointDir, endPoint, itemRotation, yawScale, approachingEnd, clampRoll, slowNearEnd, pathSpeed );
|
|
}
|
|
|
|
/*
|
|
================
|
|
sdDeliveryVehicle::Magog_DoMove
|
|
================
|
|
*/
|
|
void sdDeliveryVehicle::Magog_DoMove( const idVec3& aheadPointIdeal, const idVec3& aheadPointDir, const idVec3& endPoint, float itemRotation, float maxYawScale, bool orientToEnd, bool clampRoll, bool slowNearEnd, float pathSpeed ) {
|
|
float frameTime = MS2SEC( gameLocal.msec );
|
|
|
|
idVec3 aheadPoint = aheadPointIdeal;
|
|
const idVec3& origin = GetPhysics()->GetOrigin();
|
|
const idVec3& velocity = GetPhysics()->GetLinearVelocity();
|
|
const idMat3& axis = GetPhysics()->GetAxis();
|
|
const idAngles angles = axis.ToAngles();
|
|
const idVec3& angVel = GetPhysics()->GetAngularVelocity();
|
|
|
|
const idVec3& currentFwd = axis[ 0 ];
|
|
const idVec3& currentRight = axis[ 1 ];
|
|
const idVec3& currentUp = axis[ 2 ];
|
|
|
|
float rollVel = angVel * currentFwd;
|
|
float pitchVel = angVel * currentRight;
|
|
float yawVel = angVel * currentUp;
|
|
|
|
// find the current height of the vehicle
|
|
idVec3 futureContribution = velocity * 1.0f;
|
|
futureContribution.z = 0.0f;
|
|
float futureContributionLength = futureContribution.Length();
|
|
if ( futureContributionLength > 2000.0f ) {
|
|
futureContribution = futureContribution * ( 2000.0f / futureContributionLength );
|
|
}
|
|
|
|
idVec3 futureSpot = origin + futureContribution;
|
|
futureSpot.z -= HOVER_DOWNCAST_LENGTH;
|
|
|
|
trace_t trace;
|
|
gameLocal.clip.TraceBounds( CLIP_DEBUG_PARMS trace, origin, futureSpot, GetPhysics()->GetBounds(), axis, MASK_SOLID | MASK_OPAQUE, this );
|
|
futureSpot = trace.endpos;
|
|
|
|
// shift the ahead point based on the trace
|
|
float currentHeight = origin.z - futureSpot.z;
|
|
if ( currentHeight < HOVER_DOWNCAST_LENGTH ) {
|
|
aheadPoint.z = origin.z - currentHeight + HOVER_HEIGHT_AIM;
|
|
} else {
|
|
aheadPoint.z = origin.z - HOVER_DOWNCAST_LENGTH + HOVER_HEIGHT_AIM;
|
|
}
|
|
|
|
// sys.debugCircle( g_colorRed, origin, '0 0 1', 16, 8, 0 );
|
|
// sys.debugArrow( g_colorRed, origin, futureSpot, 256, 0 );
|
|
// sys.debugCircle( g_colorRed, futureSpot, '0 0 1', 16, 8, 0 );
|
|
// sys.debugCircle( '0 1 0', aheadPoint, '0 0 1', 256.0f, 16, 0 );
|
|
|
|
|
|
idVec3 point;
|
|
float lookaheadFactor;
|
|
|
|
{
|
|
// generate a cubic spline between the two points
|
|
idVec3 x0 = origin;
|
|
idVec3 x1 = aheadPoint;
|
|
idVec3 dx0 = velocity * 3.0f; // maintaining our current velocity is more important
|
|
idVec3 dx1 = aheadPointDir * pathSpeed * 1.0f; // than matching the destination vector
|
|
|
|
// calculate coefficients
|
|
idVec3 D = x0;
|
|
idVec3 C = dx0;
|
|
idVec3 B = 3*x1 - dx1 - 2*C - 3*D;
|
|
idVec3 A = x1 - B - C - D;
|
|
|
|
float distanceLeft = ( endPoint - origin ).Length();
|
|
lookaheadFactor = ( distanceLeft / 6096.0f ) * 0.5f;
|
|
lookaheadFactor = idMath::ClampFloat( 0.2f, 0.5f, lookaheadFactor );
|
|
if ( !slowNearEnd ) {
|
|
lookaheadFactor = 0.5f;
|
|
}
|
|
|
|
point = ( A * lookaheadFactor + B )*lookaheadFactor*lookaheadFactor + C*lookaheadFactor + D;
|
|
}
|
|
|
|
|
|
//
|
|
// Follower logic
|
|
//
|
|
idVec3 delta = point - origin;
|
|
idVec3 aheadDelta = aheadPoint - origin;
|
|
|
|
idVec3 newVelocity = vec3_origin;
|
|
|
|
//
|
|
// Z axis
|
|
//
|
|
|
|
// figure out what Z velocity is needed to get where we want to go within a frame
|
|
float Zvel = aheadDelta.z / frameTime;
|
|
Zvel = idMath::ClampFloat( -maxZVel, maxZVel, Zvel );
|
|
|
|
// figure out what Z acceleration is neccessary
|
|
float ZAccel = ( Zvel - velocity.z ) / frameTime;
|
|
ZAccel = idMath::ClampFloat( -maxZAccel, maxZAccel, ZAccel );
|
|
|
|
// chop the Z acceleration when its nearing the end - helps to avoid settling issues
|
|
if ( lookaheadFactor < 0.5f ) {
|
|
ZAccel *= idMath::Sqrt( lookaheadFactor * 2.0f );
|
|
}
|
|
|
|
Zvel = velocity.z + ZAccel * frameTime;
|
|
|
|
// rapidly prevent acceleration downwards when its below the target
|
|
if ( aheadDelta.z > 0.0f && Zvel < 0.0f ) {
|
|
Zvel *= 0.9f;
|
|
}
|
|
|
|
//
|
|
// X & Y
|
|
//
|
|
|
|
// ignore Z
|
|
delta.z = 0.0f;
|
|
aheadDelta.z = 0.0f;
|
|
idVec3 flatVelocity = velocity;
|
|
flatVelocity.z = 0.0f;
|
|
|
|
float distance = delta.Length();
|
|
idVec3 direction;
|
|
if ( distance > idMath::FLT_EPSILON ) {
|
|
direction = delta / distance;
|
|
} else {
|
|
direction = vec3_origin;
|
|
}
|
|
|
|
// figure out how fast it needs to go to get there in time
|
|
float vel = distance / 0.5f;
|
|
vel = idMath::ClampFloat( -pathSpeed, pathSpeed, vel );
|
|
|
|
idVec3 vecVel = vel * direction;
|
|
|
|
// figure out what acceleration is neccessary
|
|
idVec3 velDelta = vecVel - flatVelocity;
|
|
idVec3 velDeltaDir = velDelta;
|
|
float velDeltaLength = velDeltaDir.Normalize();
|
|
|
|
float accel = velDeltaLength / frameTime;
|
|
accel = idMath::ClampFloat( -600.0f, 600.0f, accel );
|
|
|
|
idVec3 vecAccel = accel * frameTime * velDeltaDir;
|
|
newVelocity = flatVelocity + vecAccel;
|
|
|
|
|
|
newVelocity.z = Zvel;
|
|
|
|
//
|
|
// Angles
|
|
//
|
|
idVec3 velDir = velocity;
|
|
float velLength = velDir.Normalize();
|
|
|
|
// calculate what acceleration we are undergoing
|
|
idVec3 velAccel = ( newVelocity - velocity ) / frameTime;
|
|
|
|
// calculate a component due to air resistance
|
|
float speed = InchesToMetres( velLength );
|
|
float rho = 1.2f;
|
|
float sideArea = InchesToMetres( 650.0f ) * InchesToMetres( 650.0f );
|
|
float Cd = 0.6f;
|
|
float dragForceMagnitude = MetresToInches( 0.5 * Cd * sideArea * rho * speed * speed );
|
|
// assume mass is 10,000 -> I know this works nicely
|
|
idVec3 dragAccel = ( dragForceMagnitude / 10000.f ) * velDir;
|
|
|
|
idVec3 desiredAccel = velAccel + dragAccel;
|
|
desiredAccel *= 0.4f;
|
|
desiredAccel.z += MetresToInches( 9.8f );
|
|
|
|
// ok, so we desire to be looking at the target
|
|
idVec3 forwards = endPoint - origin;
|
|
forwards.z = 0.0f;
|
|
forwards.Normalize();
|
|
|
|
if ( orientToEnd ) {
|
|
idAngles targetAngles = ang_zero;
|
|
targetAngles.yaw = idMath::AngleNormalize180( itemRotation );
|
|
forwards = targetAngles.ToForward();
|
|
}
|
|
|
|
// figure out the axes corresponding to this orientation
|
|
idVec3 up = desiredAccel;
|
|
up.Normalize();
|
|
idVec3 right = up.Cross( forwards );
|
|
right.Normalize();
|
|
forwards = right.Cross( up );
|
|
forwards.Normalize();
|
|
|
|
// convert that to an angles
|
|
idAngles desiredAngles = ( idMat3( forwards, right, up ) ).ToAngles();
|
|
if ( clampRoll ) {
|
|
desiredAngles.roll = idMath::ClampFloat( -9.0f, 9.0f, desiredAngles.roll );
|
|
} else {
|
|
desiredAngles.roll = idMath::ClampFloat( -30.0f, 30.0f, desiredAngles.roll );
|
|
}
|
|
desiredAngles.pitch = idMath::ClampFloat( -30.0f, 30.0f, desiredAngles.pitch );
|
|
|
|
// find the diff between that and what we currently have
|
|
idAngles diffAngles = ( desiredAngles - angles ).Normalize180();
|
|
diffAngles = diffAngles * 0.1f;
|
|
diffAngles = diffAngles / frameTime;
|
|
diffAngles *= 0.1f;
|
|
|
|
|
|
// translate the old angular velocity back to an angle diff style value
|
|
idAngles oldDiffAngles;
|
|
oldDiffAngles.pitch = angVel * currentRight;
|
|
oldDiffAngles.yaw = angVel * currentUp;
|
|
oldDiffAngles.roll = angVel * currentFwd;
|
|
|
|
// blend the old and the new to soften the quick changes
|
|
diffAngles = oldDiffAngles * 0.9f + diffAngles * 0.1f;
|
|
|
|
// figure out how much we're trying to change by in a single frame
|
|
idAngles angleAccel = diffAngles - oldDiffAngles;
|
|
float maxAngleAccel = 45.0f * frameTime;
|
|
float maxYawAccel = maxAngleAccel * maxYawScale;
|
|
|
|
angleAccel.pitch = idMath::ClampFloat( -maxAngleAccel, maxAngleAccel, angleAccel.pitch );
|
|
angleAccel.yaw = idMath::ClampFloat( -maxYawAccel, maxYawAccel, angleAccel.yaw );
|
|
angleAccel.roll = idMath::ClampFloat( -maxAngleAccel, maxAngleAccel, angleAccel.roll );
|
|
|
|
diffAngles = oldDiffAngles + angleAccel;
|
|
idVec3 newAngVel = diffAngles.pitch * currentRight +
|
|
diffAngles.yaw * currentUp +
|
|
diffAngles.roll * currentFwd;
|
|
|
|
// HACK: ensure it never gets below the minimum height
|
|
if ( currentHeight < HOVER_HEIGHT_MIN ) {
|
|
idVec3 newOrigin = origin;
|
|
newOrigin.z = origin.z - currentHeight + HOVER_HEIGHT_MIN;
|
|
GetPhysics()->SetOrigin( newOrigin );
|
|
} else if ( currentHeight < HOVER_HEIGHT_RESCUE ) {
|
|
float oldNewVelLength = newVelocity.Length();
|
|
|
|
float scale = ( HOVER_HEIGHT_RESCUE - currentHeight ) / ( HOVER_HEIGHT_RESCUE - HOVER_HEIGHT_MIN );
|
|
scale = idMath::Sqrt( scale );
|
|
float rescueVelocity = scale * ( HOVER_HEIGHT_RESCUE - HOVER_HEIGHT_MIN ) / 0.5f;
|
|
float rescueAcceleration = ( rescueVelocity - velocity.z ) / frameTime;
|
|
rescueAcceleration = idMath::ClampFloat( 0.0f, 1800.0f, rescueAcceleration );
|
|
rescueVelocity = velocity.z + rescueAcceleration * frameTime;
|
|
if ( rescueVelocity > newVelocity.z ) {
|
|
newVelocity.z = rescueVelocity;
|
|
}
|
|
|
|
newVelocity.Normalize();
|
|
newVelocity *= oldNewVelLength;
|
|
}
|
|
|
|
GetPhysics()->SetLinearVelocity( newVelocity );
|
|
GetPhysics()->SetAxis( angles.ToMat3() );
|
|
GetPhysics()->SetAngularVelocity( newAngVel );
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
==============
|
|
sdDeliveryVehicleBroadcastData::MakeDefault
|
|
==============
|
|
*/
|
|
void sdDeliveryVehicleBroadcastData::MakeDefault( void ) {
|
|
sdScriptEntityBroadcastData::MakeDefault();
|
|
|
|
lastRollAccel = 0.0f;
|
|
}
|
|
|
|
/*
|
|
==============
|
|
sdDeliveryVehicleBroadcastData::Write
|
|
==============
|
|
*/
|
|
void sdDeliveryVehicleBroadcastData::Write( idFile* file ) const {
|
|
sdScriptEntityBroadcastData::Write( file );
|
|
|
|
file->WriteFloat( lastRollAccel );
|
|
}
|
|
|
|
/*
|
|
==============
|
|
sdDeliveryVehicleBroadcastData::Read
|
|
==============
|
|
*/
|
|
void sdDeliveryVehicleBroadcastData::Read( idFile* file ) {
|
|
sdScriptEntityBroadcastData::Read( file );
|
|
|
|
file->ReadFloat( lastRollAccel );
|
|
}
|
|
|
|
|
|
/*
|
|
================
|
|
sdDeliveryVehicle::ApplyNetworkState
|
|
================
|
|
*/
|
|
void sdDeliveryVehicle::ApplyNetworkState( networkStateMode_t mode, const sdEntityStateNetworkData& newState ) {
|
|
if ( mode == NSM_BROADCAST ) {
|
|
NET_GET_NEW( sdDeliveryVehicleBroadcastData );
|
|
|
|
lastRollAccel = newData.lastRollAccel;
|
|
}
|
|
|
|
sdScriptEntity::ApplyNetworkState( mode, newState );
|
|
}
|
|
|
|
/*
|
|
==============
|
|
sdDeliveryVehicle::ReadNetworkState
|
|
==============
|
|
*/
|
|
void sdDeliveryVehicle::ReadNetworkState( networkStateMode_t mode, const sdEntityStateNetworkData& baseState, sdEntityStateNetworkData& newState, const idBitMsg& msg ) const {
|
|
if ( mode == NSM_BROADCAST ) {
|
|
NET_GET_STATES( sdDeliveryVehicleBroadcastData );
|
|
|
|
// read state
|
|
newData.lastRollAccel = msg.ReadDeltaFloat( baseData.lastRollAccel );
|
|
}
|
|
|
|
sdScriptEntity::ReadNetworkState( mode, baseState, newState, msg );
|
|
}
|
|
|
|
/*
|
|
==============
|
|
sdDeliveryVehicle::WriteNetworkState
|
|
==============
|
|
*/
|
|
void sdDeliveryVehicle::WriteNetworkState( networkStateMode_t mode, const sdEntityStateNetworkData& baseState, sdEntityStateNetworkData& newState, idBitMsg& msg ) const {
|
|
if ( mode == NSM_BROADCAST ) {
|
|
NET_GET_STATES( sdDeliveryVehicleBroadcastData );
|
|
|
|
// update state
|
|
newData.lastRollAccel = lastRollAccel;
|
|
|
|
// write state
|
|
msg.WriteDeltaFloat( baseData.lastRollAccel, newData.lastRollAccel );
|
|
}
|
|
|
|
sdScriptEntity::WriteNetworkState( mode, baseState, newState, msg );
|
|
}
|
|
|
|
/*
|
|
==============
|
|
sdDeliveryVehicle::CheckNetworkStateChanges
|
|
==============
|
|
*/
|
|
bool sdDeliveryVehicle::CheckNetworkStateChanges( networkStateMode_t mode, const sdEntityStateNetworkData& baseState ) const {
|
|
if ( mode == NSM_BROADCAST ) {
|
|
NET_GET_BASE( sdDeliveryVehicleBroadcastData );
|
|
|
|
// note that lastRollAccel changing on its own will not make
|
|
// this send a new packet, however it will be sent if any new packets are sent!
|
|
}
|
|
return sdScriptEntity::CheckNetworkStateChanges( mode, baseState );
|
|
}
|
|
|
|
/*
|
|
==============
|
|
sdDeliveryVehicle::CreateNetworkStructure
|
|
==============
|
|
*/
|
|
sdEntityStateNetworkData* sdDeliveryVehicle::CreateNetworkStructure( networkStateMode_t mode ) const {
|
|
if ( mode == NSM_BROADCAST ) {
|
|
return new sdDeliveryVehicleBroadcastData();
|
|
}
|
|
return sdScriptEntity::CreateNetworkStructure( mode );
|
|
}
|