837 lines
23 KiB
C++
837 lines
23 KiB
C++
|
|
||
|
// VehicleDriver.cpp
|
||
|
//
|
||
|
// Copyright 2002-2004 Raven Software
|
||
|
//----------------------------------------------------------------
|
||
|
|
||
|
#include "../../idlib/precompiled.h"
|
||
|
#pragma hdrstop
|
||
|
|
||
|
#include "../Game_local.h"
|
||
|
|
||
|
#include <cfloat>
|
||
|
|
||
|
const idEventDef VD_ChoosePathTarget( "choosePathTarget", "e", 'e' );
|
||
|
const idEventDef EV_FollowOffset( "followOffset", "v" );
|
||
|
const idEventDef EV_FireWeapon ( "fireWeapon", "ff" );
|
||
|
|
||
|
CLASS_DECLARATION( idActor, rvVehicleDriver )
|
||
|
EVENT( EV_PostSpawn , rvVehicleDriver::Event_PostSpawn )
|
||
|
EVENT( AI_EnterVehicle , rvVehicleDriver::Event_EnterVehicle )
|
||
|
EVENT( AI_ExitVehicle , rvVehicleDriver::Event_ExitVehicle )
|
||
|
EVENT( AI_ScriptedMove , rvVehicleDriver::Event_ScriptedMove )
|
||
|
EVENT( AI_ScriptedDone , rvVehicleDriver::Event_ScriptedDone )
|
||
|
EVENT( AI_ScriptedStop , rvVehicleDriver::Event_ScriptedStop )
|
||
|
EVENT( EV_Activate , rvVehicleDriver::Event_Trigger )
|
||
|
EVENT( EV_Speed , rvVehicleDriver::Event_SetSpeed )
|
||
|
EVENT( EV_FireWeapon , rvVehicleDriver::Event_FireWeapon )
|
||
|
EVENT( AI_FaceEntity , rvVehicleDriver::Event_FaceEntity )
|
||
|
EVENT( AI_LookAt , rvVehicleDriver::Event_LookAt )
|
||
|
EVENT( AI_SetLeader , rvVehicleDriver::Event_SetLeader )
|
||
|
|
||
|
//twhitaker: remove - begin
|
||
|
EVENT( EV_FollowOffset , rvVehicleDriver::Event_SetFollowOffset )
|
||
|
//twhitaker: remove - end
|
||
|
END_CLASS
|
||
|
|
||
|
/*
|
||
|
================
|
||
|
rvVehicleDriver::rvVehicleDriver
|
||
|
================
|
||
|
*/
|
||
|
rvVehicleDriver::rvVehicleDriver ( void ) {
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
================
|
||
|
rvVehicleDriver::Spawn
|
||
|
================
|
||
|
*/
|
||
|
void rvVehicleDriver::Spawn ( void ) {
|
||
|
currentThrottle = 1.0f;
|
||
|
faceTarget = NULL;
|
||
|
lookTarget = NULL;
|
||
|
leader = NULL;
|
||
|
leaderFlags = 0;
|
||
|
decelDistance = 0.0f;
|
||
|
minDistance = 0.0f;
|
||
|
fireEndTime = 0.0f;
|
||
|
isMoving = false;
|
||
|
avoidingLeader = false;
|
||
|
pathingMode = VDPM_Random;
|
||
|
pathingOrigin = vec3_origin;
|
||
|
pathingEntity = NULL;
|
||
|
|
||
|
SIMDProcessor->Memset( &pathTargetInfo, 0, sizeof( PathTargetInfo ) );
|
||
|
SIMDProcessor->Memset( &lastPathTargetInfo, 0, sizeof( PathTargetInfo ) );
|
||
|
|
||
|
PostEventMS( &EV_PostSpawn, 0 );
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
================
|
||
|
rvVehicleDriver::Think
|
||
|
================
|
||
|
*/
|
||
|
void rvVehicleDriver::Think ( void ) {
|
||
|
if ( !IsDriving() ) {
|
||
|
if ( leader ) {
|
||
|
leader->SetLeaderHint( VDLH_Continue );
|
||
|
leader = NULL;
|
||
|
}
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
if ( pathTargetInfo.node ) {
|
||
|
float targetDistance = 0.0f;
|
||
|
float dotForward = 0.0f;
|
||
|
float dotRight = 0.0f;
|
||
|
float desiredThrottle = 1.0f;
|
||
|
idEntity* currentPathTarget = pathTargetInfo.node;
|
||
|
rvVehicle* vehicle = vehicleController.GetVehicle();
|
||
|
idMat3 vehicleAxis = vehicle->GetAxis();
|
||
|
idVec3 targetOrigin;
|
||
|
idVec3 dirToTarget;
|
||
|
usercmd_t cmd;
|
||
|
idAngles ang;
|
||
|
|
||
|
// We may want to hack the auto correction variable based on what the state of the driver and the driver's leader.
|
||
|
UpdateAutoCorrection();
|
||
|
|
||
|
if ( vehicle->IsAutoCorrecting( ) ) {
|
||
|
if ( lastPathTargetInfo.node ) {
|
||
|
currentPathTarget = lastPathTargetInfo.node;
|
||
|
}
|
||
|
|
||
|
if ( g_debugVehicleDriver.GetInteger() != 0 ) {
|
||
|
gameRenderWorld->DebugBounds( colorCyan, vehicle->GetPhysics()->GetAbsBounds().Expand( 30 ) );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Touch all the triggers that the vehicle touches
|
||
|
vehicle->TouchTriggers();
|
||
|
|
||
|
GetTargetInfo( currentPathTarget, &targetOrigin, &dirToTarget, &dotForward, &dotRight, &targetDistance );
|
||
|
|
||
|
// The primary purpose of the following portions of code is to set the desiredThrottle variable.
|
||
|
if ( IsMoving() ) {
|
||
|
|
||
|
// if we're slowing down lerp throttle.
|
||
|
if ( pathTargetInfo.throttle < lastPathTargetInfo.throttle ) {
|
||
|
desiredThrottle = idMath::Lerp( lastPathTargetInfo.throttle, pathTargetInfo.throttle, targetDistance / pathTargetInfo.initialDistance );
|
||
|
} else {
|
||
|
// otherwise accelerate rapidly or maintain throttle.
|
||
|
desiredThrottle = pathTargetInfo.throttle;
|
||
|
}
|
||
|
|
||
|
// we could potentially be a leader, so check the leader flags
|
||
|
if ( leaderFlags & VDLH_SlowDown ) {
|
||
|
desiredThrottle = 0.1f;
|
||
|
} else if ( leaderFlags & VDLH_Wait ) {
|
||
|
desiredThrottle = 0.0f;
|
||
|
}
|
||
|
|
||
|
// if we're following a someone ...
|
||
|
if ( leader ) {
|
||
|
bool canSeeLeader = vehicle->CanSee( leader->vehicleController.GetVehicle(), false );
|
||
|
float distanceToLeader = ( leader->GetPhysics()->GetOrigin() - GetPhysics()->GetOrigin() ).Length();
|
||
|
avoidingLeader = false;
|
||
|
|
||
|
// update the leaders flags based on distance and visibility
|
||
|
if ( !canSeeLeader ) {
|
||
|
// if we can't see the leader, tell him to stop
|
||
|
leader->SetLeaderHint( VDLH_Wait );
|
||
|
|
||
|
} else if ( distanceToLeader > decelDistance ) {
|
||
|
// if we're too far from the leader, tell him to slow down
|
||
|
leader->SetLeaderHint( VDLH_SlowDown );
|
||
|
|
||
|
} else {
|
||
|
// we're within range of the leader so let him move along at a normal rate
|
||
|
leader->SetLeaderHint( VDLH_Continue );
|
||
|
|
||
|
// if we're too close to the leader, we need to slow ourself down
|
||
|
if ( distanceToLeader < minDistance ) {
|
||
|
desiredThrottle = -0.2f;
|
||
|
avoidingLeader = true;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// the desiredThrottle variable should be set at this point, the only other thing that could change it
|
||
|
// would be in SimulateKeys, if the dotForward < 0.
|
||
|
}
|
||
|
|
||
|
vehicleController.GetInput( cmd, ang );
|
||
|
|
||
|
// Simulate Input
|
||
|
SimulateKeys( cmd, dotForward, dotRight, desiredThrottle * currentThrottle, targetDistance );
|
||
|
SimulateMouseMove( cmd );
|
||
|
SimulateButtons( cmd );
|
||
|
|
||
|
vehicleController.SetInput( cmd, ang );
|
||
|
|
||
|
// Node Transition
|
||
|
if( (!vehicle->IsAutoCorrecting() || !IsValidPathNode( currentPathTarget )) && isMoving ) {
|
||
|
idVec3 point = targetOrigin - dirToTarget * pathTargetInfo.minDistance;
|
||
|
|
||
|
if( vehicle->GetPhysics()->GetAbsBounds().ContainsPoint( point ) ) {
|
||
|
int numTargets = NumValidTargets( currentPathTarget );
|
||
|
lastPathTargetInfo = pathTargetInfo;
|
||
|
//TODO: ponder - should I be setting lastPathTargetInfo.node to pathTargetInfo.node or currentPathTarget???
|
||
|
|
||
|
if( numTargets ) {
|
||
|
Event_ScriptedMove( ChooseNextNode( currentPathTarget ), 0, 0 );
|
||
|
}
|
||
|
|
||
|
if( lastPathTargetInfo.exitVehicle ) {
|
||
|
Event_ExitVehicle( true );
|
||
|
} else if( lastPathTargetInfo.throttle == 0 || !numTargets ) {
|
||
|
Event_ScriptedStop();
|
||
|
|
||
|
if( !numTargets ) {
|
||
|
pathTargetInfo.node = NULL;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Debug Output
|
||
|
if ( g_debugVehicleDriver.GetInteger( ) != 0 ) {
|
||
|
gameRenderWorld->DebugBounds( colorRed, idBounds(idVec3(-5, -5, -5), idVec3(5, 5, 5)), point );
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
} else { // no path
|
||
|
float dotForward;
|
||
|
float dotRight;
|
||
|
usercmd_t cmd;
|
||
|
idAngles ang;
|
||
|
|
||
|
vehicleController.GetInput( cmd, ang );
|
||
|
|
||
|
if ( GetTargetInfo( faceTarget, NULL, NULL, &dotForward, &dotRight, NULL) ) {
|
||
|
if( ( 1.0f - dotForward ) < VECTOR_EPSILON ) {
|
||
|
Event_ScriptedStop();
|
||
|
} else {
|
||
|
SimulateKeys( cmd, dotForward, dotRight, 0.0f );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
SimulateMouseMove( cmd );
|
||
|
SimulateButtons( cmd );
|
||
|
|
||
|
vehicleController.SetInput( cmd, ang );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
================
|
||
|
rvVehicleDriver::GetTargetInfo
|
||
|
================
|
||
|
*/
|
||
|
bool rvVehicleDriver::GetTargetInfo( const idEntity* target, idVec3* targetOrigin, idVec3* dirToTarget, float* dotForward, float* dotRight, float* distance ) const {
|
||
|
if( !target || !IsDriving() ) {
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
idMat3 vehicleAxis = vehicleController.GetVehicle()->GetAxis();
|
||
|
idVec3 vehicleOrigin = vehicleController.GetVehicle()->GetOrigin();
|
||
|
idVec3 forward = vehicleAxis[ 0 ];
|
||
|
idVec3 localTargetOrigin = target->GetPhysics()->GetOrigin();
|
||
|
localTargetOrigin.z = vehicleOrigin.z;// Find way to include vehicleAxis here
|
||
|
idVec3 vectorToTarget = localTargetOrigin - vehicleOrigin;
|
||
|
idVec3 localDirToTarget = vectorToTarget;
|
||
|
float distToTarget = localDirToTarget.Normalize();
|
||
|
|
||
|
if( targetOrigin ) {
|
||
|
*targetOrigin = localTargetOrigin;
|
||
|
}
|
||
|
|
||
|
if( dirToTarget ) {
|
||
|
*dirToTarget = localDirToTarget;
|
||
|
}
|
||
|
|
||
|
if( distance ) {
|
||
|
*distance = distToTarget;
|
||
|
}
|
||
|
|
||
|
if( dotForward ) {
|
||
|
*dotForward = localDirToTarget * forward;
|
||
|
}
|
||
|
|
||
|
if( dotRight ) {
|
||
|
*dotRight = localDirToTarget * vehicleAxis[ 1 ];
|
||
|
}
|
||
|
|
||
|
// Debug help
|
||
|
if ( g_debugVehicleDriver.GetInteger( ) != 0 ) {
|
||
|
idStr temp;
|
||
|
const idVec3 & origin = GetPhysics()->GetOrigin();
|
||
|
gameRenderWorld->DebugLine( colorBlue, origin, target->GetPhysics()->GetOrigin() );
|
||
|
gameRenderWorld->DebugBounds( colorBlue, GetPhysics()->GetAbsBounds() );
|
||
|
gameRenderWorld->DebugBounds( colorBlue, target->GetPhysics()->GetBounds(), target->GetPhysics()->GetOrigin() );
|
||
|
gameRenderWorld->DrawText( target->GetName(), vehicleOrigin + vectorToTarget + vehicleAxis[2] * 5.0f, 1.0f, colorBlue, gameLocal.GetLocalPlayer()->viewAxis );
|
||
|
gameRenderWorld->DrawText( idStr( "State: " ) + LeaderHintsString( leaderFlags, temp ), origin, 1.0f, colorBlue, gameLocal.GetLocalPlayer()->viewAxis );
|
||
|
|
||
|
if ( vehicleController.GetVehicle()->IsAutoCorrecting() ) {
|
||
|
gameRenderWorld->DebugBounds( colorPurple, vehicleController.GetVehicle()->GetPhysics()->GetAbsBounds() );
|
||
|
gameRenderWorld->DrawText( "Auto-Correcting", vehicleOrigin, 1.0f, colorPurple, gameLocal.GetLocalPlayer()->viewAxis );
|
||
|
}
|
||
|
|
||
|
if ( pathTargetInfo.node && g_debugVehicleDriver.GetInteger( ) == 2 ) {
|
||
|
gameRenderWorld->DebugBounds( colorOrange, pathTargetInfo.node->GetPhysics()->GetBounds() );
|
||
|
|
||
|
for ( int ix = 0; ix < pathTargetInfo.node->targets.Num(); ix++ ) {
|
||
|
gameRenderWorld->DebugLine( colorOrange, pathTargetInfo.node->GetPhysics()->GetOrigin(), pathTargetInfo.node->targets[ ix ]->GetPhysics()->GetOrigin(), 0, true );
|
||
|
}
|
||
|
}
|
||
|
if ( leader ) {
|
||
|
idVec4 & color = colorGreen;
|
||
|
|
||
|
if ( leader->GetLeaderHint() & VDLH_SlowDown ) {
|
||
|
color = colorYellow;
|
||
|
} else if ( leader->GetLeaderHint() & VDLH_Wait ) {
|
||
|
color = colorRed;
|
||
|
}
|
||
|
|
||
|
idStr str = idStr( "decel_distance: " ) + idStr( decelDistance ) + idStr( "\nmin_distance: " ) + idStr( minDistance );
|
||
|
|
||
|
gameRenderWorld->DrawText( str, leader->GetPhysics()->GetOrigin(), 1.0f, color, gameLocal.GetLocalPlayer()->viewAxis, 1, 0, true );
|
||
|
gameRenderWorld->DebugLine( color, GetPhysics()->GetOrigin(), leader->GetPhysics()->GetOrigin(), 0, true );
|
||
|
gameRenderWorld->DebugBounds( color, leader->vehicleController.GetVehicle()->GetPhysics()->GetAbsBounds(), vec3_origin, 0, true );
|
||
|
gameRenderWorld->DebugCircle( color, leader->GetPhysics()->GetOrigin(), idVec3( 0, 0, 1 ), decelDistance, 10, 0, true );
|
||
|
gameRenderWorld->DebugCircle( color, leader->GetPhysics()->GetOrigin(), idVec3( 0, 0, 1 ), minDistance, 10, 0, true );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
================
|
||
|
rvVehicleDriver::ChooseNextNode
|
||
|
================
|
||
|
*/
|
||
|
idEntity* rvVehicleDriver::ChooseNextNode( idEntity* target ) {
|
||
|
if( !target ) {
|
||
|
return NULL;
|
||
|
}
|
||
|
|
||
|
if( func.Init( target->spawnArgs.GetString( "call_nextNode" )) <= 0 ) {
|
||
|
idEntity * best = NULL;
|
||
|
float bestDist;
|
||
|
|
||
|
switch ( pathingMode ) {
|
||
|
case VDPM_MoveTo:
|
||
|
bestDist = FLT_MAX;
|
||
|
|
||
|
for ( int i = target->targets.Num() - 1; i; i -- ) {
|
||
|
float distance = ( target->targets[ i ]->GetPhysics()->GetOrigin() - pathingOrigin ).LengthSqr();
|
||
|
|
||
|
if ( bestDist > distance ) {
|
||
|
bestDist = distance;
|
||
|
best = target->targets[ i ];
|
||
|
}
|
||
|
}
|
||
|
|
||
|
break;
|
||
|
|
||
|
case VDPM_MoveAway:
|
||
|
bestDist = FLT_MIN;
|
||
|
|
||
|
for ( int i = target->targets.Num() - 1; i; i -- ) {
|
||
|
float distance = ( target->targets[ i ]->GetPhysics()->GetOrigin() - pathingOrigin ).LengthSqr();
|
||
|
|
||
|
if ( bestDist < distance ) {
|
||
|
bestDist = distance;
|
||
|
best = target->targets[ i ];
|
||
|
}
|
||
|
}
|
||
|
|
||
|
case VDPM_Custom:
|
||
|
if ( pathingEntity ) {
|
||
|
// best = pathingCallback( target );
|
||
|
pathingEntity->ProcessEvent( &VD_ChoosePathTarget, target );
|
||
|
best = gameLocal.program.GetReturnedEntity();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return ( best ) ? best : RandomValidTarget( target );
|
||
|
}
|
||
|
|
||
|
func.InsertEntity( this, 0 );
|
||
|
func.CallFunc( &spawnArgs );
|
||
|
func.RemoveIndex( 0 );
|
||
|
|
||
|
return ( func.ReturnsAVal()) ? gameLocal.FindEntity( spawnArgs.GetString( func.GetReturnKey() )) : const_cast<idEntity*>( target );
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
================
|
||
|
rvVehicleDriver::SimulateButtons
|
||
|
================
|
||
|
*/
|
||
|
void rvVehicleDriver::SimulateButtons( usercmd_t& cmd ) {
|
||
|
cmd.buttons = (fireEndTime >= gameLocal.time) ? BUTTON_ATTACK : 0;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
================
|
||
|
rvVehicleDriver::SimulateMouseMove
|
||
|
================
|
||
|
*/
|
||
|
void rvVehicleDriver::SimulateMouseMove( usercmd_t& cmd ) {
|
||
|
idVec3 origin;
|
||
|
idMat3 axis;
|
||
|
|
||
|
idVec3 vectorToTarget;
|
||
|
idAngles anglesToTarget;
|
||
|
idAngles turretAngles;
|
||
|
idAngles deltaAngles;
|
||
|
|
||
|
if( !lookTarget ) {
|
||
|
for( int ix = 0; ix < 3; ++ix ) {
|
||
|
cmd.angles[ix] = 0;
|
||
|
}
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
vehicleController.GetEyePosition( origin, axis );
|
||
|
vectorToTarget = (lookTarget->GetPhysics()->GetOrigin() - origin).ToNormal();
|
||
|
anglesToTarget = vectorToTarget.ToAngles().Normalize360();
|
||
|
turretAngles = (axis[0]).ToAngles().Normalize360();
|
||
|
deltaAngles = (anglesToTarget - turretAngles).Normalize180();
|
||
|
|
||
|
for( int ix = 0; ix < deltaAngles.GetDimension(); ++ix ) {
|
||
|
cmd.angles[ix] += ANGLE2SHORT( deltaAngles[ix] );
|
||
|
}
|
||
|
|
||
|
// Debug Output
|
||
|
if( g_debugVehicleDriver.GetInteger( ) != 0 ) {
|
||
|
gameRenderWorld->DebugLine( colorGreen, origin, origin + anglesToTarget.ToForward() * 100.0f, 17, true );
|
||
|
gameRenderWorld->DebugLine( colorYellow, origin, origin + turretAngles.ToForward() * 100.0f, 17, true );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
================
|
||
|
rvVehicleDriver::SimulateKeys
|
||
|
================
|
||
|
*/
|
||
|
void rvVehicleDriver::SimulateKeys( usercmd_t& cmd, float dotForward, float dotRight, float speed, float distance ) {
|
||
|
rvVehicle * vehicle = vehicleController.GetVehicle();
|
||
|
|
||
|
if( !vehicle ) {
|
||
|
cmd.forwardmove = 0;
|
||
|
cmd.rightmove = 0;
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
cmd.forwardmove = static_cast< signed char >( (!vehicle->IsAutoCorrecting() ? 127.0f : forwardThrustScale) * dotForward * speed );
|
||
|
cmd.rightmove = static_cast< signed char >( ( ( dotForward < 0.0f ) ? rightThrustScale : -rightThrustScale ) * dotRight );
|
||
|
}
|
||
|
|
||
|
|
||
|
/*
|
||
|
================
|
||
|
rvVehicleDriver::SortValid
|
||
|
================
|
||
|
*/
|
||
|
int rvVehicleDriver::SortValid( const void* a, const void* b ) {
|
||
|
idEntityPtr<idEntity> A = *(idEntityPtr<idEntity>*)a;
|
||
|
idEntityPtr<idEntity> B = *(idEntityPtr<idEntity>*)b;
|
||
|
|
||
|
return rvVehicleDriver::IsValidTarget( B ) - rvVehicleDriver::IsValidTarget( A );
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
================
|
||
|
rvVehicleDriver::SortTargetList
|
||
|
================
|
||
|
*/
|
||
|
int rvVehicleDriver::SortTargetList( idEntity* ent ) const {
|
||
|
if( !ent ) {
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
int numValidEntities = 0;
|
||
|
idEntity * target;
|
||
|
|
||
|
ent->RemoveNullTargets();
|
||
|
qsort( ent->targets.Ptr(), NumTargets( ent ), ent->targets.TypeSize(), rvVehicleDriver::SortValid );
|
||
|
|
||
|
for( int ix = NumTargets( ent ) - 1; ix >= 0; --ix ) {
|
||
|
target = GetTarget( ent, ix );
|
||
|
|
||
|
if( IsValidTarget( target ) ) {
|
||
|
++numValidEntities;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return numValidEntities;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
================
|
||
|
rvVehicleDriver::RandomValidTarget
|
||
|
================
|
||
|
*/
|
||
|
idEntity* rvVehicleDriver::RandomValidTarget( idEntity* ent ) const {
|
||
|
int numValid = NumValidTargets( ent );
|
||
|
return (!numValid) ? NULL : ent->targets[ rvRandom::irand(0, numValid - 1) ];
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
================
|
||
|
rvVehicleDriver::NumValidTargets
|
||
|
================
|
||
|
*/
|
||
|
int rvVehicleDriver::NumValidTargets( idEntity* ent ) const {
|
||
|
return SortTargetList(ent);
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
================
|
||
|
rvVehicleDriver::NumTargets
|
||
|
================
|
||
|
*/
|
||
|
int rvVehicleDriver::NumTargets( const idEntity* ent ) const {
|
||
|
return (ent) ? ent->targets.Num() : 0;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
================
|
||
|
rvVehicleDriver::GetTarget
|
||
|
================
|
||
|
*/
|
||
|
idEntity* rvVehicleDriver::GetTarget( const idEntity* ent, int index ) const {
|
||
|
return (ent) ? ent->targets[index] : NULL;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
================
|
||
|
rvVehicleDriver::IsValidPathNode
|
||
|
================
|
||
|
*/
|
||
|
bool rvVehicleDriver::IsValidPathNode( const idEntity* ent ) {
|
||
|
if( !ent ) {
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
return ent->IsType( idTarget::GetClassType() );
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
================
|
||
|
rvVehicleDriver::IsValidTarget
|
||
|
================
|
||
|
*/
|
||
|
bool rvVehicleDriver::IsValidTarget( const idEntity* ent ) {
|
||
|
return IsValidPathNode( ent ) || ent->IsType( rvVehicle::GetClassType() ) || ent->IsType( idPlayer::GetClassType() );
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
================
|
||
|
rvVehicleDriver::SetLeader
|
||
|
================
|
||
|
*/
|
||
|
bool rvVehicleDriver::SetLeader( idEntity* ent ) {
|
||
|
if ( !ent || !ent->IsType( rvVehicleDriver::GetClassType() ) ) {
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
leader = static_cast<rvVehicleDriver*>( ent );
|
||
|
minDistance = leader->spawnArgs.GetFloat( "min_distance", "500" );
|
||
|
idStr decel_distance( minDistance * 3.0f );
|
||
|
decelDistance = leader->spawnArgs.GetFloat( "decel_distance", decel_distance );
|
||
|
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
================
|
||
|
rvVehicleDriver::Event_PostSpawn
|
||
|
================
|
||
|
*/
|
||
|
void rvVehicleDriver::Event_PostSpawn ( void ) {
|
||
|
for ( int i = targets.Num() - 1; i >= 0; i-- ) {
|
||
|
idEntity * ent = targets[ i ].GetEntity();
|
||
|
if ( ent->IsType( rvVehicle::GetClassType() ) ) {
|
||
|
Event_EnterVehicle( ent );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
for ( int i = targets.Num() - 1; i >= 0; i-- ) {
|
||
|
idEntity * ent = targets[ i ].GetEntity();
|
||
|
if ( ent->IsType( idTarget::GetClassType() ) ) {
|
||
|
Event_ScriptedMove( ent, 0, 0 );
|
||
|
}
|
||
|
if ( ent->IsType( rvVehicleDriver::GetClassType() ) ) {
|
||
|
SetLeader( ent );
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
================
|
||
|
rvVehicleDriver::Event_EnterVehicle
|
||
|
================
|
||
|
*/
|
||
|
void rvVehicleDriver::Event_EnterVehicle ( idEntity * vehicle ) {
|
||
|
if ( vehicle ) {
|
||
|
forwardThrustScale = vehicle->spawnArgs.GetFloat( "driver_forward_thrust", ".75" ) * 127.0f;
|
||
|
rightThrustScale = vehicle->spawnArgs.GetFloat( "driver_right_thrust", "1" ) * 127.0f;
|
||
|
|
||
|
EnterVehicle( vehicle );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
================
|
||
|
rvVehicleDriver::Event_ExitVehicle
|
||
|
================
|
||
|
*/
|
||
|
void rvVehicleDriver::Event_ExitVehicle( bool force ) {
|
||
|
Event_ScriptedStop();
|
||
|
|
||
|
if( vehicleController.GetVehicle() && force && !ExitVehicle(force) ) {
|
||
|
vehicleController.GetVehicle()->RemoveDriver( vehicleController.GetPosition(), force );
|
||
|
}
|
||
|
|
||
|
pathTargetInfo.node = NULL;
|
||
|
faceTarget = NULL;
|
||
|
lookTarget = NULL;
|
||
|
leader = NULL;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
================
|
||
|
rvVehicleDriver::Event_ScriptedMove
|
||
|
================
|
||
|
*/
|
||
|
void rvVehicleDriver::Event_ScriptedMove( idEntity *target, float minDist, bool exitVehicle ) {
|
||
|
isMoving = false;
|
||
|
|
||
|
if( !target ) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
if( IsValidTarget( target ) ) {
|
||
|
isMoving = true;
|
||
|
faceTarget = NULL;
|
||
|
pathTargetInfo.node = target;
|
||
|
pathTargetInfo.initialDistance = ( target->GetPhysics()->GetOrigin() - GetPhysics()->GetOrigin() ).Length();
|
||
|
|
||
|
if ( !target->IsType( idPlayer::GetClassType() ) ) {
|
||
|
pathTargetInfo.minDistance = target->spawnArgs.GetFloat( "min_distance", va("%f", Max(200.0f, minDist)) );
|
||
|
pathTargetInfo.throttle = idMath::ClampFloat( 0.0f, 1.0f, target->spawnArgs.GetFloat( "throttle", "1" ) );
|
||
|
pathTargetInfo.exitVehicle = target->spawnArgs.GetBool( "exit_vehicle", exitVehicle ? "1" : "0" );
|
||
|
|
||
|
if( pathTargetInfo.exitVehicle ) {
|
||
|
pathTargetInfo.throttle = 0.0f;
|
||
|
}
|
||
|
} else {
|
||
|
pathTargetInfo.minDistance = minDist;
|
||
|
pathTargetInfo.throttle = 1.0f;
|
||
|
pathTargetInfo.exitVehicle = false;
|
||
|
}
|
||
|
|
||
|
} else {
|
||
|
SIMDProcessor->Memset( &pathTargetInfo, 0, sizeof( PathTargetInfo ) );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
================
|
||
|
rvVehicleDriver::Event_ScriptedDone
|
||
|
================
|
||
|
*/
|
||
|
void rvVehicleDriver::Event_ScriptedDone( void ) {
|
||
|
idThread::ReturnFloat( !IsMoving() );
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
================
|
||
|
rvVehicleDriver::Event_ScriptedStop
|
||
|
================
|
||
|
*/
|
||
|
void rvVehicleDriver::Event_ScriptedStop( void ) {
|
||
|
if( IsDriving() ) {
|
||
|
usercmd_t cmd = { 0 };
|
||
|
|
||
|
vehicleController.SetInput( cmd, ang_zero );
|
||
|
|
||
|
if( pathTargetInfo.node ) {
|
||
|
if( func.Init( pathTargetInfo.node->spawnArgs.GetString( "call_doneMoving" )) > 0 ) {
|
||
|
func.InsertEntity( this, 0 );
|
||
|
func.CallFunc( &spawnArgs );
|
||
|
func.RemoveIndex( 0 );
|
||
|
}
|
||
|
|
||
|
pathTargetInfo.node->ActivateTargets(this);
|
||
|
pathTargetInfo.node = NULL;
|
||
|
}
|
||
|
|
||
|
isMoving = false;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
================
|
||
|
rvVehicleDriver::Event_Trigger
|
||
|
================
|
||
|
*/
|
||
|
void rvVehicleDriver::Event_Trigger( idEntity *activator ) {
|
||
|
if( IsDriving() && pathTargetInfo.node ) {
|
||
|
isMoving = true;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
================
|
||
|
rvVehicleDriver::Event_SetSpeed
|
||
|
================
|
||
|
*/
|
||
|
void rvVehicleDriver::Event_SetSpeed( float speed ) {
|
||
|
currentThrottle = speed;
|
||
|
}
|
||
|
|
||
|
//twhitaker: remove - begin
|
||
|
/*
|
||
|
================
|
||
|
rvVehicleDriver::Event_SetFollowOffset
|
||
|
================
|
||
|
*/
|
||
|
void rvVehicleDriver::Event_SetFollowOffset( const idVec3 &offset ) {
|
||
|
gameLocal.Warning( "Script Event \"followOffset\" is deprecated, please remove it immediately to avoid errors." );
|
||
|
}
|
||
|
//twhitaker: remove - end
|
||
|
|
||
|
/*
|
||
|
================
|
||
|
rvVehicleDriver::Event_FireWeapon
|
||
|
================
|
||
|
*/
|
||
|
void rvVehicleDriver::Event_FireWeapon( float weapon_index, float time ) {
|
||
|
if( IsDriving() ) {
|
||
|
fireEndTime = gameLocal.GetTime() + SEC2MS( time );
|
||
|
vehicleController.SelectWeapon( weapon_index );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
================
|
||
|
rvVehicleDriver::Event_FaceEntity
|
||
|
================
|
||
|
*/
|
||
|
void rvVehicleDriver::Event_FaceEntity( const idEntity* entity ) {
|
||
|
if( IsMoving() ) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
faceTarget = entity;
|
||
|
isMoving = true;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
================
|
||
|
rvVehicleDriver::Event_LookAt
|
||
|
================
|
||
|
*/
|
||
|
void rvVehicleDriver::Event_LookAt( const idEntity* entity ) {
|
||
|
lookTarget = entity;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
================
|
||
|
rvVehicleDriver::Event_SetLeader
|
||
|
================
|
||
|
*/
|
||
|
void rvVehicleDriver::Event_SetLeader( idEntity* newLeader ) {
|
||
|
SetLeader( newLeader );
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
================
|
||
|
rvVehicleDriver::UpdateAutoCorrection
|
||
|
================
|
||
|
*/
|
||
|
void rvVehicleDriver::UpdateAutoCorrection ( void ) {
|
||
|
if ( IsDriving() ) {
|
||
|
rvVehicle * vehicle = vehicleController.GetVehicle();
|
||
|
|
||
|
// Disregard your autocorrection state if we're slowing down to avoid collision with the leader.
|
||
|
if ( vehicle->IsAutoCorrecting() ) {
|
||
|
if ( avoidingLeader == true ) {
|
||
|
vehicle->autoCorrectionBegin = 0;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
================
|
||
|
rvVehicleDriver::Save
|
||
|
================
|
||
|
*/
|
||
|
void rvVehicleDriver::Save ( idSaveGame *savefile ) const {
|
||
|
savefile->Write ( &pathTargetInfo, sizeof ( pathTargetInfo ) );
|
||
|
savefile->Write ( &lastPathTargetInfo, sizeof ( lastPathTargetInfo ) );
|
||
|
savefile->WriteFloat ( currentThrottle );
|
||
|
|
||
|
faceTarget.Save ( savefile );
|
||
|
lookTarget.Save ( savefile );
|
||
|
|
||
|
leader.Save ( savefile );
|
||
|
savefile->WriteInt ( leaderFlags );
|
||
|
savefile->WriteFloat ( decelDistance );
|
||
|
savefile->WriteFloat ( minDistance );
|
||
|
savefile->WriteBool ( avoidingLeader );
|
||
|
|
||
|
savefile->WriteFloat ( fireEndTime );
|
||
|
|
||
|
func.Save ( savefile );
|
||
|
|
||
|
savefile->WriteBool ( isMoving );
|
||
|
|
||
|
savefile->WriteInt ( (int&)pathingMode );
|
||
|
pathingEntity.Save ( savefile );
|
||
|
savefile->WriteVec3 ( pathingOrigin );
|
||
|
|
||
|
savefile->WriteFloat( forwardThrustScale ); // cnicholson: Added unsaved var
|
||
|
savefile->WriteFloat( rightThrustScale ); // cnicholson: Added unsaved var
|
||
|
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
================
|
||
|
rvVehicleDriver::Restore
|
||
|
================
|
||
|
*/
|
||
|
void rvVehicleDriver::Restore ( idRestoreGame *savefile ) {
|
||
|
savefile->Read ( &pathTargetInfo, sizeof ( pathTargetInfo ) );
|
||
|
savefile->Read ( &lastPathTargetInfo, sizeof ( lastPathTargetInfo ) );
|
||
|
savefile->ReadFloat ( currentThrottle );
|
||
|
|
||
|
faceTarget.Restore ( savefile );
|
||
|
lookTarget.Restore ( savefile );
|
||
|
|
||
|
leader.Restore ( savefile );
|
||
|
savefile->ReadInt ( leaderFlags );
|
||
|
savefile->ReadFloat ( decelDistance );
|
||
|
savefile->ReadFloat ( minDistance );
|
||
|
savefile->ReadBool ( avoidingLeader );
|
||
|
|
||
|
savefile->ReadFloat ( fireEndTime );
|
||
|
|
||
|
func.Restore ( savefile );
|
||
|
|
||
|
savefile->ReadBool ( isMoving );
|
||
|
|
||
|
savefile->ReadInt ( (int&)pathingMode );
|
||
|
pathingEntity.Restore ( savefile );
|
||
|
savefile->ReadVec3 ( pathingOrigin );
|
||
|
|
||
|
savefile->ReadFloat( forwardThrustScale ); // cnicholson: Added unrestored var
|
||
|
savefile->ReadFloat( rightThrustScale ); // cnicholson: Added unrestored var
|
||
|
}
|