Complete disaster-recovery snapshot: engine/game source, game data assets, VC6 toolchain + DX SDKs, build outputs, deployed game, and _UNUSED archive. Large binaries in Git LFS; text preserved byte-for-byte (core.autocrlf=false, no eol attributes). See RECOVERY.md for the one-clone rebuild procedure.
2929 lines
80 KiB
C++
2929 lines
80 KiB
C++
//===========================================================================//
|
|
// File: Vehicle.cpp
|
|
//---------------------------------------------------------------------------//
|
|
// Date Who Modification //
|
|
// -------- --- ---------------------------------------------------------- //
|
|
// 09/09/98 JSE Inital base class based off of Shadowrun/Adept //
|
|
// 09/23/98 BDB State engine update //
|
|
//---------------------------------------------------------------------------//
|
|
// Copyright (C) 1998, Fasa Interactive //
|
|
// All Rights reserved worldwide //
|
|
// This unpublished sourcecode is PROPRIETARY and CONFIDENTIAL //
|
|
//===========================================================================//
|
|
|
|
#include "MW4Headers.hpp"
|
|
|
|
#include "Vehicle.hpp"
|
|
#include "Subsystem.hpp"
|
|
#include "Engine.hpp"
|
|
#include "Torso.hpp"
|
|
#include "VehicleInterface.hpp"
|
|
#include "Weapon.hpp"
|
|
#include "MWDamageObject.hpp"
|
|
#include "bridge.hpp"
|
|
#include "tank.hpp"
|
|
#include "MWApplication.hpp"
|
|
#include "SubsystemClassData.hpp"
|
|
#include "aiutils.hpp"
|
|
#include "MWMission.hpp"
|
|
|
|
#include <Adept\GUITextManager.hpp>
|
|
#include <Adept\GUITextObject.hpp>
|
|
#include <Adept\DamageObject.hpp>
|
|
#include <Adept\EntityManager.hpp>
|
|
#include <Adept\Effect.hpp>
|
|
#include <Adept\Map.hpp>
|
|
#include <Adept\Renderer.hpp>
|
|
|
|
#include <Adept\CollisionGrid.hpp>
|
|
#include <Adept\CollisionVolume.hpp>
|
|
#include <AI.hpp>
|
|
#include "gameinfo.hpp"
|
|
|
|
#include "MWPlayer.hpp"
|
|
#include <MLR\MLRShadowLight.hpp>
|
|
#include <Adept\LightManager.hpp>
|
|
#include <Adept\Mission.hpp>
|
|
#include "mwguimanager.hpp"
|
|
#include "hudcomm.hpp"
|
|
// MSL 5.02 Shadow
|
|
#include <Adept\CameraComponent.hpp>
|
|
|
|
|
|
const Stuff::Time sensor_map_update_frequency = 1.0f;
|
|
|
|
#ifdef NEW_SENSOR_TIMING
|
|
extern __int64 tVehicleTime;
|
|
#endif
|
|
|
|
|
|
void MechWarrior4::VehicleSecurityCheckStart()
|
|
{
|
|
_asm
|
|
{
|
|
nop
|
|
}
|
|
}
|
|
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
//
|
|
Stuff::Scalar
|
|
Vehicle::GetHeightAtPoint(
|
|
const Stuff::Point3D& point,
|
|
Adept::Entity* from_who
|
|
)
|
|
{
|
|
Verify(Adept::Map::GetInstance() != 0);
|
|
|
|
Stuff::Line3D line;
|
|
line.m_length = 100.0f;
|
|
line.m_direction = Vector3D::Down;
|
|
line.m_origin = point;
|
|
line.m_origin.y += 50.0f;
|
|
Stuff::Normal3D normal;
|
|
Entity::CollisionQuery query(&line,&normal,Entity::CanBeWalkedOnFlag,from_who);
|
|
Check_Object(CollisionGrid::Instance);
|
|
if (CollisionGrid::Instance->ProjectLine(&query) == Adept::Map::GetInstance())
|
|
{
|
|
Stuff::Point3D hit_spot;
|
|
line.FindEnd(&hit_spot);
|
|
return (hit_spot.y);
|
|
}
|
|
|
|
return -100000.0;
|
|
}
|
|
|
|
//#############################################################################
|
|
//################## Vehicle::ExecutionStateEngine ######################
|
|
//#############################################################################
|
|
|
|
const StateEngine::StateEntry
|
|
Vehicle::ExecutionStateEngine::StateEntries[]=
|
|
{
|
|
STATE_ENTRY(Vehicle__ExecutionStateEngine, FallingMotion),
|
|
STATE_ENTRY(Vehicle__ExecutionStateEngine, FlyingMotion),
|
|
STATE_ENTRY(Vehicle__ExecutionStateEngine, AnimatedMotion),
|
|
STATE_ENTRY(Vehicle__ExecutionStateEngine, DrivingMotion),
|
|
STATE_ENTRY(Vehicle__ExecutionStateEngine, Destroyed),
|
|
STATE_ENTRY(Vehicle__ExecutionStateEngine, AIMotion),
|
|
STATE_ENTRY(Vehicle__ExecutionStateEngine, Dying),
|
|
STATE_ENTRY(Vehicle__ExecutionStateEngine, Dropping)
|
|
};
|
|
|
|
|
|
Vehicle::ExecutionStateEngine::ClassData*
|
|
Vehicle::ExecutionStateEngine::DefaultData = NULL;
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
//
|
|
void
|
|
Vehicle::ExecutionStateEngine::InitializeClass()
|
|
{
|
|
Check_Object(BaseClass::DefaultData);
|
|
Verify(!DefaultData);
|
|
|
|
DefaultData =
|
|
new ClassData(
|
|
Vehicle__ExecutionStateEngineClassID,
|
|
"Vehicle::ExecutionStateEngine",
|
|
BaseClass::DefaultData,
|
|
ELEMENTS(StateEntries), StateEntries,
|
|
(Entity::ExecutionStateEngine::Factory)Make,
|
|
(Entity::ExecutionStateEngine::FactoryRequest::Factory)
|
|
&FactoryRequest::ConstructFactoryRequest
|
|
);
|
|
Register_Object(DefaultData);
|
|
}
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
//
|
|
void
|
|
Vehicle::ExecutionStateEngine::TerminateClass()
|
|
{
|
|
Unregister_Object(DefaultData);
|
|
delete DefaultData;
|
|
DefaultData = NULL;
|
|
}
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
//
|
|
Vehicle::ExecutionStateEngine*
|
|
Vehicle::ExecutionStateEngine::Make(
|
|
Vehicle *vehicle,
|
|
FactoryRequest *request
|
|
)
|
|
{
|
|
Check_Object(vehicle);
|
|
Check_Object(request);
|
|
gos_PushCurrentHeap(Heap);
|
|
Vehicle::ExecutionStateEngine *engine =
|
|
new Vehicle::ExecutionStateEngine(DefaultData, vehicle, request);
|
|
gos_PopCurrentHeap();
|
|
return engine;
|
|
|
|
}
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
//
|
|
int
|
|
Vehicle::ExecutionStateEngine::RequestState(
|
|
int new_state,
|
|
void* data
|
|
)
|
|
{
|
|
Check_Object(this);
|
|
Check_Object(owningEntity);
|
|
|
|
//
|
|
//-------------------------------------------
|
|
// If there is no change of state just return
|
|
//-------------------------------------------
|
|
//
|
|
if (new_state == currentState)
|
|
return currentState;
|
|
|
|
Vehicle *veh;
|
|
veh = Cast_Object (Vehicle *,owningEntity);
|
|
|
|
if ((currentState == AIMotionState) && veh->m_AI && (!veh->m_AI->TurningOn ()))
|
|
{
|
|
return currentState;
|
|
}
|
|
|
|
//
|
|
//----------------------------------------------
|
|
// Now, switch the state and tickle the watchers
|
|
//----------------------------------------------
|
|
//
|
|
switch (BaseClass::RequestState(new_state, data))
|
|
{
|
|
case DestroyedState:
|
|
owningEntity->EnterNeverExecuteState();
|
|
break;
|
|
}
|
|
|
|
return currentState;
|
|
}
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
//
|
|
void
|
|
Vehicle::ExecutionStateEngine::TestInstance() const
|
|
{
|
|
Verify(IsDerivedFrom(DefaultData));
|
|
}
|
|
|
|
//#############################################################################
|
|
//############################### Vehicle ##############################
|
|
//#############################################################################
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
//
|
|
Vehicle::ClassData*
|
|
Vehicle::DefaultData = NULL;
|
|
|
|
DWORD MechWarrior4::Executed_Vehicle_Count = 0;
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
//
|
|
void
|
|
Vehicle::InitializeClass()
|
|
{
|
|
Check_Object(BaseClass::DefaultData);
|
|
Verify(!DefaultData);
|
|
DefaultData =
|
|
new ClassData(
|
|
VehicleClassID,
|
|
"MechWarrior4::Vehicle",
|
|
BaseClass::DefaultData,
|
|
0, NULL,
|
|
(Entity::Factory)Make,
|
|
(Entity::CreateMessage::Factory)CreateMessage::ConstructCreateMessage,
|
|
ExecutionStateEngine::DefaultData,
|
|
(Entity::GameModel::Factory)GameModel::ConstructGameModel,
|
|
(Entity::GameModel::Factory)GameModel::ConstructOBBStream,
|
|
(Entity::GameModel::ReadAndVerifier)GameModel::ReadAndVerify,
|
|
(Entity::GameModel::ModelWrite)GameModel::WriteToText,
|
|
(Entity::GameModel::ModelSave)GameModel::SaveGameModel,
|
|
AnimationStateEngine::Make
|
|
);
|
|
Register_Object(DefaultData);
|
|
|
|
|
|
INDIRECT_STATE_ATTRIBUTE(
|
|
Vehicle,
|
|
ExecutionState,
|
|
executionState,
|
|
Vehicle__ExecutionStateEngine
|
|
);
|
|
|
|
CUSTOM_DIRECT_ATTRIBUTE(Vehicle,
|
|
SpeedDemandMPS,speedDemandMPS,Scalar,ScalarClassID);
|
|
CUSTOM_DIRECT_ATTRIBUTE(Vehicle,
|
|
CurrentSpeedMPS,currentSpeedMPS,Scalar,ScalarClassID);
|
|
|
|
CUSTOM_DIRECT_ATTRIBUTE(Vehicle,
|
|
LocalGroundPitch,localGroundPitch,Scalar,ScalarClassID);
|
|
CUSTOM_DIRECT_ATTRIBUTE(Vehicle,
|
|
LocalGroundRoll,localGroundRoll,Scalar,ScalarClassID);
|
|
|
|
CUSTOM_DIRECT_ATTRIBUTE(Vehicle,
|
|
RawLocalGroundPitch,rawLocalGroundPitch,Scalar,ScalarClassID);
|
|
CUSTOM_DIRECT_ATTRIBUTE(Vehicle,
|
|
RawLocalGroundRoll,rawLocalGroundRoll,Scalar,ScalarClassID);
|
|
|
|
CUSTOM_DIRECT_ATTRIBUTE(
|
|
Vehicle,
|
|
VehicleMovingSFX,
|
|
vehicleMovingSFX,
|
|
int,
|
|
IntClassID
|
|
);
|
|
|
|
CUSTOM_DIRECT_ATTRIBUTE(
|
|
Vehicle,
|
|
VehicleIdleSFX,
|
|
vehicleIdleSFX,
|
|
int,
|
|
IntClassID
|
|
);
|
|
|
|
CUSTOM_DIRECT_ATTRIBUTE(
|
|
Vehicle,
|
|
VehicleDyingSFX,
|
|
vehicleDyingSFX,
|
|
int,
|
|
IntClassID
|
|
);
|
|
|
|
DIRECT_GAME_MODEL_ATTRIBUTE(
|
|
Vehicle__GameModel,
|
|
FullStopTurnRate,
|
|
fullStopTurnRate,
|
|
Radian
|
|
);
|
|
DIRECT_GAME_MODEL_ATTRIBUTE(
|
|
Vehicle__GameModel,
|
|
TopSpeedTurnRate,
|
|
topSpeedTurnRate,
|
|
Radian
|
|
);
|
|
DIRECT_GAME_MODEL_ATTRIBUTE(
|
|
Vehicle__GameModel,
|
|
MaxReverseSpeed,
|
|
maxReverseSpeed,
|
|
Scalar
|
|
);
|
|
DIRECT_GAME_MODEL_ATTRIBUTE(
|
|
Vehicle__GameModel,
|
|
MinReverseSpeed,
|
|
minReverseSpeed,
|
|
Scalar
|
|
);
|
|
DIRECT_GAME_MODEL_ATTRIBUTE(
|
|
Vehicle__GameModel,
|
|
MaxSpeed,
|
|
maxSpeed,
|
|
Scalar
|
|
);
|
|
DIRECT_GAME_MODEL_ATTRIBUTE(
|
|
Vehicle__GameModel,
|
|
MinMaxSpeed,
|
|
minMaxSpeed,
|
|
Scalar
|
|
);
|
|
DIRECT_GAME_MODEL_ATTRIBUTE(
|
|
Vehicle__GameModel,
|
|
MinSpeed,
|
|
minSpeed,
|
|
Scalar
|
|
);
|
|
DIRECT_GAME_MODEL_ATTRIBUTE(
|
|
Vehicle__GameModel,
|
|
Acceleration,
|
|
acceleration,
|
|
Scalar
|
|
);
|
|
DIRECT_GAME_MODEL_ATTRIBUTE(
|
|
Vehicle__GameModel,
|
|
Decceleration,
|
|
decceleration,
|
|
Scalar
|
|
);
|
|
DIRECT_GAME_MODEL_ATTRIBUTE(
|
|
Vehicle__GameModel,
|
|
ReverseAccelerationMultiplier,
|
|
reverseAccelerationMultiplier,
|
|
Scalar
|
|
);
|
|
DIRECT_GAME_MODEL_ATTRIBUTE(
|
|
Vehicle__GameModel,
|
|
ReverseDeccelerationMultiplier,
|
|
reverseDeccelerationMultiplier,
|
|
Scalar
|
|
);
|
|
DIRECT_GAME_MODEL_ATTRIBUTE(
|
|
Vehicle__GameModel,
|
|
TargetLockTime,
|
|
targetLockTime,
|
|
Scalar
|
|
);
|
|
|
|
DIRECT_GAME_MODEL_ATTRIBUTE(
|
|
Vehicle__GameModel,
|
|
MaxGimpSpeed,
|
|
maxGimpSpeed,
|
|
Scalar
|
|
);
|
|
|
|
DIRECT_GAME_MODEL_ATTRIBUTE(
|
|
Vehicle__GameModel,
|
|
MinStandTransitionSpeed,
|
|
minStandTransitionSpeed,
|
|
Scalar
|
|
);
|
|
|
|
|
|
DIRECT_GAME_MODEL_ATTRIBUTE(
|
|
Vehicle__GameModel,
|
|
MaxSlope,
|
|
modelmaxSlope,
|
|
Scalar
|
|
);
|
|
|
|
DIRECT_GAME_MODEL_ATTRIBUTE(
|
|
Vehicle__GameModel,
|
|
StartSlopeDeceleration,
|
|
modelstartSlopeDeceleration,
|
|
Scalar
|
|
);
|
|
|
|
|
|
DIRECT_GAME_MODEL_ATTRIBUTE(
|
|
Vehicle__GameModel,
|
|
SlopeDecel1,
|
|
slopeDecel1,
|
|
Scalar
|
|
);
|
|
|
|
|
|
DIRECT_GAME_MODEL_ATTRIBUTE(
|
|
Vehicle__GameModel,
|
|
SlopeDecel2,
|
|
slopeDecel2,
|
|
Scalar
|
|
);
|
|
|
|
|
|
DIRECT_GAME_MODEL_ATTRIBUTE(
|
|
Vehicle__GameModel,
|
|
SlopeDecel3,
|
|
slopeDecel3,
|
|
Scalar
|
|
);
|
|
|
|
CUSTOM_DIRECT_GAME_MODEL_ATTRIBUTE(
|
|
Vehicle__GameModel,
|
|
AttackType,
|
|
m_attackType,
|
|
int,
|
|
IntClassID
|
|
);
|
|
|
|
DIRECT_GAME_MODEL_ATTRIBUTE(
|
|
Vehicle__GameModel,
|
|
TreadLength,
|
|
m_treadLength,
|
|
Scalar
|
|
);
|
|
}
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
//
|
|
void
|
|
Vehicle::TerminateClass()
|
|
{
|
|
Unregister_Object(DefaultData);
|
|
delete DefaultData;
|
|
DefaultData = NULL;
|
|
}
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
//
|
|
Vehicle*
|
|
Vehicle::Make(
|
|
CreateMessage *message,
|
|
ReplicatorID *base_id
|
|
)
|
|
{
|
|
Check_Object(message);
|
|
gos_PushCurrentHeap(Heap);
|
|
Vehicle *new_entity = new
|
|
Vehicle(DefaultData, message, base_id, NULL);
|
|
gos_PopCurrentHeap();
|
|
Check_Object(new_entity);
|
|
|
|
|
|
Check_Object(EntityManager::GetInstance());
|
|
EntityManager::GetInstance()->AddMover(new_entity);
|
|
return new_entity;
|
|
}
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
//
|
|
Vehicle::Vehicle(
|
|
ClassData *class_data,
|
|
CreateMessage *message,
|
|
ReplicatorID *base_id,
|
|
ElementRenderer::Element *element
|
|
):
|
|
MWObject(class_data, message, base_id, element),
|
|
currentTrailEffect(NULL),
|
|
m_SensorCellMapX(-1),
|
|
m_SensorCellMapZ(-1),
|
|
m_LastSensorCellPositionUpdate(0),
|
|
m_SecondaryExplosion (NULL)
|
|
{
|
|
Check_Pointer(this);
|
|
Check_Object(message);
|
|
|
|
MW4AI::UserConstants::IncrementRefCount();
|
|
CommonCreation(message);
|
|
|
|
engine = NULL;
|
|
vehicleInterface = NULL;
|
|
shadow = NULL;
|
|
// MSL 5.02 Shadow
|
|
shadowIntensity = 0.0f;
|
|
|
|
respawnCount = 0;
|
|
|
|
m_doesHaveLightAmp = false;
|
|
|
|
teamNumber = MWApplication::No_Team;
|
|
|
|
m_groundRaycastHeight = 20.0f;
|
|
|
|
}
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
//
|
|
|
|
Vehicle::~Vehicle()
|
|
{
|
|
DESTRUCTOR("Vehicle");
|
|
|
|
Check_Object(this);
|
|
if (shadow)
|
|
{
|
|
Check_Object(shadow);
|
|
delete shadow;
|
|
shadow = NULL;
|
|
// MSL 5.02 Shadow
|
|
shadowIntensity = 0.0f;
|
|
}
|
|
Check_Object(EntityManager::GetInstance());
|
|
EntityManager::GetInstance()->RemoveMover(this);
|
|
MW4AI::UserConstants::DecrementRefCount();
|
|
|
|
RemoveFromSensorCellMap();
|
|
}
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
//
|
|
void
|
|
Vehicle::Reuse(
|
|
const CreateMessage *message,
|
|
ReplicatorID *base_id
|
|
)
|
|
{
|
|
Check_Object(this);
|
|
Check_Object(message);
|
|
|
|
STOP(("Not implemented"));
|
|
BaseClass::Reuse(message, base_id);
|
|
}
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
//
|
|
void Vehicle::TurnOff (void)
|
|
{
|
|
m_LastState = executionState->GetState ();
|
|
executionState->RequestState(ExecutionStateEngine::AIMotionState);
|
|
}
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
//
|
|
void Vehicle::TurnOn (void)
|
|
{
|
|
lastParameterization = gos_GetElapsedTime ();
|
|
// executionState->RequestState(ExecutionStateEngine::AnimatedMotionState);
|
|
if (executionState->GetState () == ExecutionStateEngine::AIMotionState)
|
|
executionState->RequestState(m_LastState);
|
|
}
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
//
|
|
void
|
|
Vehicle::CommonCreation(CreateMessage *message)
|
|
{
|
|
Check_Object(this);
|
|
Check_Object(message);
|
|
|
|
m_DeathDamageMode = m_DeathDamageType = -1;
|
|
m_LastState = ExecutionStateEngine::DrivingMotionState;
|
|
Verify (MW4AI::UserConstants::Instance ());
|
|
Stuff::Scalar upslope;
|
|
const GameModel *model = GetGameModel();
|
|
Check_Object(model);
|
|
switch (model->moveTypeFlag)
|
|
{
|
|
case MechWarrior4::MWObject__GameModel::LEG_MOVETYPE:
|
|
case MechWarrior4::MWObject__GameModel::LEGJUMP_MOVETYPE:
|
|
upslope = Arccos (MW4AI::UserConstants::Instance ()->Get (MW4AI::UserConstants::leg_up_slope));
|
|
break;
|
|
case MechWarrior4::MWObject__GameModel::TRACK_MOVETYPE:
|
|
upslope = Arccos (MW4AI::UserConstants::Instance ()->Get (MW4AI::UserConstants::track_up_slope));
|
|
break;
|
|
case MechWarrior4::MWObject__GameModel::WHEEL_MOVETYPE:
|
|
upslope = Arccos (MW4AI::UserConstants::Instance ()->Get (MW4AI::UserConstants::wheel_up_slope));
|
|
break;
|
|
case MechWarrior4::MWObject__GameModel::HOVER_MOVETYPE:
|
|
upslope = Arccos (MW4AI::UserConstants::Instance ()->Get (MW4AI::UserConstants::hover_up_slope));
|
|
break;
|
|
default:
|
|
upslope = Pi_Over_2;
|
|
break;
|
|
}
|
|
// if (model->startSlopeDeceleration < upslope)
|
|
{
|
|
startSlopeDeceleration = upslope;
|
|
maxSlope = startSlopeDeceleration + (15.0f * Radians_Per_Degree);
|
|
|
|
}
|
|
#ifdef LAB_ONLY
|
|
m_DebugFast = false;
|
|
#endif
|
|
|
|
speedDemand = 0.0f;
|
|
yawDemand = 0.0f;
|
|
pitchDemand = 0.0f;
|
|
rollDemand = 0.0f;
|
|
speedDemandMPS = 0.0f;
|
|
currentSpeedMPS = 0.0f;
|
|
currentSpeedKPH = 0.0;
|
|
speedDemandKPH = 0.0;
|
|
|
|
m_needSelfDestruct = 0;
|
|
|
|
dirRequest = Point3D (0,0,0);
|
|
followDirRequest = false;
|
|
followDirRequestReverse = false;
|
|
|
|
internalViewPoint = false;
|
|
usingEyeSpring = false;
|
|
|
|
localGroundRoll = 0.0f;
|
|
localGroundPitch = 0.0f;
|
|
|
|
rawLocalGroundPitch = localGroundPitch;
|
|
rawLocalGroundRoll = localGroundRoll;
|
|
|
|
materialHit = -1;
|
|
currentTrailID = ResourceID::Null;
|
|
currentTrailEffect.Remove();
|
|
|
|
vehicleRunsInterface = true;
|
|
|
|
|
|
correctingPosition = false;
|
|
correctingRotation = false;
|
|
correctionPosition = Point3D::Identity;
|
|
correctionAngle = YawPitchRoll::Identity;
|
|
rotationCorrectionTime = 0.0f;
|
|
positionCorrectionTime = 0.0f;
|
|
|
|
}
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
//
|
|
void
|
|
Vehicle::Respawn(Entity::CreateMessage *message)
|
|
{
|
|
Check_Object(this);
|
|
Check_Object(message);
|
|
|
|
|
|
BaseClass::Respawn(message);
|
|
CreateMessage *vehicle_message = Cast_Pointer(CreateMessage *, message);
|
|
CommonCreation(vehicle_message);
|
|
}
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
//
|
|
int
|
|
Vehicle::GetExecutionSlot()
|
|
{
|
|
Check_Object(this);
|
|
return VehicleExecutionSlot;
|
|
}
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
//
|
|
void
|
|
Vehicle::PreCollisionExecute(Time till)
|
|
{
|
|
Check_Object(this);
|
|
|
|
PRECOLLISION_LOGIC("Vehicle");
|
|
#if defined(LAB_ONLY)
|
|
if (!instanceName)
|
|
MWGameInfo::g_LastVehicle[0] = '\0';
|
|
else
|
|
{
|
|
strncpy(MWGameInfo::g_LastVehicle, instanceName, sizeof(MWGameInfo::g_LastVehicle)-1);
|
|
MWGameInfo::g_LastVehicle[sizeof(MWGameInfo::g_LastVehicle)-1] = '\0';
|
|
}
|
|
MWGameInfo::g_LastVehiclePos = GetLocalToWorld ();
|
|
#endif
|
|
|
|
//
|
|
//----------------------------------------
|
|
// Run our interface if we are supposed to
|
|
//----------------------------------------
|
|
//
|
|
Set_Statistic(Executed_Vehicle_Count, Executed_Vehicle_Count+1);
|
|
if (vehicleRunsInterface && vehicleInterface != NULL)
|
|
{
|
|
Check_Object(vehicleInterface);
|
|
vehicleInterface->PreCollisionExecute(till);
|
|
Verify(!vehicleInterface->IsUsingPostCollision() || EntityManager::GetInstance()->IsInPostCollisionExecution(vehicleInterface));
|
|
}
|
|
|
|
UpdateSensorCellMapPosition();
|
|
|
|
//
|
|
//-------------------------------------------------------------
|
|
// Let the base class deal with setting up the motion variables
|
|
//-------------------------------------------------------------
|
|
//
|
|
BaseClass::PreCollisionExecute(till);
|
|
|
|
//
|
|
//----------------------------------------------------------------------
|
|
// Any executing vehicle should not be in never execute state and should
|
|
// always use post collision
|
|
//----------------------------------------------------------------------
|
|
//
|
|
Check_Object(executionState);
|
|
int pre_state = executionState->GetState();
|
|
Verify(pre_state != ExecutionStateEngine::NeverExecuteState);
|
|
|
|
//
|
|
//-------------------------------------------------------------------------
|
|
// Do the appropriate type of simulation. Animated motion state is for BRB
|
|
//-------------------------------------------------------------------------
|
|
//
|
|
|
|
FindGroundAngle ();
|
|
|
|
Verify(GetTimeSlice(till) < 1.0f);
|
|
Scalar time_slice = GetTimeSlice(till);
|
|
switch (pre_state)
|
|
{
|
|
case ExecutionStateEngine::AnimatedMotionState:
|
|
switch (animStateEngine->GetState())
|
|
{
|
|
case AnimationStateEngine::Test1State:
|
|
case AnimationStateEngine::Test2State:
|
|
case AnimationStateEngine::Test3State:
|
|
case AnimationStateEngine::Test4State:
|
|
break;
|
|
default:
|
|
animStateEngine->RequestState(AnimationStateEngine::Test1State);
|
|
break;
|
|
}
|
|
|
|
if (animationArrayCount > 0)
|
|
{
|
|
animStateEngine->RunStates(time_slice);
|
|
}
|
|
StraightLineMotionSimulation(till);
|
|
break;
|
|
|
|
//
|
|
//-----------------------------------------------
|
|
// Flying or driving state needs to apply gravity
|
|
//-----------------------------------------------
|
|
//
|
|
case ExecutionStateEngine::DrivingMotionState:
|
|
case ExecutionStateEngine::DyingState:
|
|
case ExecutionStateEngine::FlyingMotionState:
|
|
{
|
|
UnitVector3D world_down_in_local;
|
|
GetLocalToWorld().GetWorldDownInLocal(&world_down_in_local);
|
|
localSpaceAcceleration.linearMotion.AddScaled(
|
|
localSpaceAcceleration.linearMotion,
|
|
world_down_in_local,
|
|
g_Gravity
|
|
);
|
|
}
|
|
break;
|
|
|
|
//
|
|
//------------------------------------
|
|
// This state is not used for vehicles
|
|
//------------------------------------
|
|
//
|
|
case ExecutionStateEngine::AlwaysExecuteState:
|
|
STOP(("AlwaysExecuteState is not a valid state for vehicle"));
|
|
break;
|
|
|
|
case ExecutionStateEngine::AIMotionState:
|
|
{
|
|
PRECOLLISION_LOGIC("Vehicle::Boardgame");
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
//
|
|
#if defined(LAB_ONLY)
|
|
void Vehicle::SyncMatrices(bool update_matrix)
|
|
{
|
|
SYNC_LOGIC("Vehicle");
|
|
BaseClass::SyncMatrices(update_matrix);
|
|
}
|
|
#endif
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
//
|
|
bool
|
|
Vehicle::CollisionHandler(
|
|
Stuff::LinearMatrix4D *new_position,
|
|
Stuff::DynamicArrayOf<CollisionData> *collisions
|
|
)
|
|
{
|
|
Check_Object(this);
|
|
Check_Object(collisions);
|
|
Verify(GetInterestLevel() != DormantInterestLevel);
|
|
|
|
bool shoulddamage=true;
|
|
//
|
|
//----------------------------------------------------------
|
|
// If we are destroyed, delete the collision list and return
|
|
//----------------------------------------------------------
|
|
//
|
|
if (IsDestroyed())
|
|
{
|
|
delete collisions;
|
|
Verify(!newCollisions);
|
|
return false;
|
|
}
|
|
|
|
//
|
|
//--------------------------------
|
|
// Ask the AI about the collision
|
|
//--------------------------------
|
|
//
|
|
if (m_AI)
|
|
{
|
|
if (!m_AI->ReactToCollision(collisions,shoulddamage)) // ai will return false if collision should not occur
|
|
{
|
|
Verify(!newCollisions);
|
|
delete collisions;
|
|
return false;
|
|
}
|
|
}
|
|
|
|
|
|
//
|
|
//----------------------------------------
|
|
// Turn off collision if we are going fast
|
|
//----------------------------------------
|
|
//
|
|
#ifdef LAB_ONLY
|
|
if (m_DebugFast)
|
|
{
|
|
Verify(!newCollisions);
|
|
delete collisions;
|
|
return false;
|
|
}
|
|
#endif
|
|
|
|
//
|
|
//-----------------------
|
|
// Iterate the collisions
|
|
//-----------------------
|
|
//
|
|
for (int i=0; i<collisions->GetLength(); ++i)
|
|
{
|
|
CollisionData *data = &(*collisions)[i];
|
|
Check_Pointer(data);
|
|
|
|
//
|
|
//---------------------------------------------------------------------
|
|
// Look to see if we have run into a collider, and if so, calculate the
|
|
// relative velocity
|
|
//---------------------------------------------------------------------
|
|
//
|
|
Entity *other = data->m_otherEntity;
|
|
if (other->IsACollider())
|
|
{
|
|
Adept::Mover *adeptmover = Cast_Object(Adept::Mover*, other);
|
|
Vector3D rel_vel;
|
|
rel_vel.Subtract(adeptmover->worldSpaceVelocity.linearMotion, worldSpaceVelocity.linearMotion);
|
|
Scalar speed = rel_vel.GetLengthSquared();
|
|
MWObject *mover = Cast_Object(MWObject*, other);
|
|
|
|
//
|
|
//-------------------------------
|
|
// If its a mech, get its ratings
|
|
//-------------------------------
|
|
//
|
|
if (other->GetClassID() == MechClassID)
|
|
{
|
|
Mech *mech = Cast_Object(Mech*, mover);
|
|
int mech_class;
|
|
Scalar damage_received;
|
|
Scalar mech_speed;
|
|
if (mech->GetTonage() >= MW4AI::UserConstants::m_Instance->Get(MW4AI::UserConstants::collision_mech_heavy_tonage))
|
|
{
|
|
damage_received = MW4AI::UserConstants::m_Instance->Get(MW4AI::UserConstants::collision_mech_heavy_damage);
|
|
mech_speed = MW4AI::UserConstants::m_Instance->Get(MW4AI::UserConstants::collision_mech_heavy_building_speed);
|
|
mech_class = HeavyCanWalkThruType;
|
|
}
|
|
else if (mech->GetTonage() >= MW4AI::UserConstants::m_Instance->Get(MW4AI::UserConstants::collision_mech_medium_tonage))
|
|
{
|
|
damage_received = MW4AI::UserConstants::m_Instance->Get(MW4AI::UserConstants::collision_mech_medium_damage);
|
|
mech_speed = MW4AI::UserConstants::m_Instance->Get(MW4AI::UserConstants::collision_mech_medium_building_speed);
|
|
mech_class = MediumCanWalkThruType;
|
|
}
|
|
else
|
|
{
|
|
damage_received = MW4AI::UserConstants::m_Instance->Get(MW4AI::UserConstants::collision_mech_light_damage);
|
|
mech_speed = MW4AI::UserConstants::m_Instance->Get(MW4AI::UserConstants::collision_mech_light_building_speed);
|
|
mech_class = AnythingCanWalkThruType;
|
|
}
|
|
|
|
//
|
|
//----------------------
|
|
// See if it squishes us
|
|
//----------------------
|
|
//
|
|
if (GetWalkThruType() >= mech_class)
|
|
{
|
|
if (shoulddamage)
|
|
{
|
|
TakeDamageMessage damage_message(
|
|
GetReplicatorID(),
|
|
other->GetReplicatorID(),
|
|
100000.0f,
|
|
// MSL 5.05 Advance Mode
|
|
0, // Modifier
|
|
RammingDamageFromAboveType,
|
|
data->m_normal,
|
|
data->m_worldIntersectionPoint,
|
|
0.0f,
|
|
false,
|
|
TakeDamageMessage::DefaultWeaponType
|
|
);
|
|
Receive(&damage_message);
|
|
}
|
|
}
|
|
|
|
//
|
|
//-----------------------------------------------
|
|
// See if we are going fast enough to get damaged
|
|
//-----------------------------------------------
|
|
//
|
|
else if (speed >= mech_speed)
|
|
{
|
|
speed = Sqrt(speed)*mover->GetTonage();
|
|
|
|
//
|
|
//------------------------
|
|
// Evaluate the DFA states
|
|
//------------------------
|
|
//
|
|
Scalar min_dfa_vel = MW4AI::UserConstants::m_Instance->Get(MW4AI::UserConstants::collision_dfa_speed);
|
|
Entity::ExecutionStateEngine *engine = mech->executionState;
|
|
bool their_dfa = mech->worldSpaceVelocity.linearMotion.y <= -min_dfa_vel && engine->GetState() == ExecutionStateEngine::FlyingMotionState;
|
|
|
|
//
|
|
//---------------------------------------
|
|
// Use the appropriate collision modifier
|
|
//---------------------------------------
|
|
//
|
|
char damage_type;
|
|
if (their_dfa)
|
|
{
|
|
damage_type = RammingDamageFromAboveType;
|
|
speed *= MW4AI::UserConstants::m_Instance->Get(MW4AI::UserConstants::collision_dfa_damage_given);
|
|
}
|
|
else
|
|
{
|
|
damage_type = RammingDamageFromAheadType;
|
|
speed *= damage_received;
|
|
}
|
|
|
|
//
|
|
//----------------------------------
|
|
// Set the damage message to ourself
|
|
//----------------------------------
|
|
//
|
|
if (shoulddamage)
|
|
{
|
|
// MSL 5.06 Armor Mode
|
|
MWApplication *m_App;
|
|
m_App = MWApplication::GetInstance ();
|
|
m_ArmorMode = m_App->GetLocalNetParams()->m_armormodeOn;
|
|
|
|
TakeDamageMessage damage_message(
|
|
GetReplicatorID(),
|
|
other->GetReplicatorID(),
|
|
speed,
|
|
m_ArmorMode,
|
|
damage_type,
|
|
data->m_normal,
|
|
data->m_worldIntersectionPoint,
|
|
0.0f,
|
|
false,
|
|
TakeDamageMessage::DefaultWeaponType
|
|
);
|
|
Receive(&damage_message);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
//--------------------------------------------------------------
|
|
// If we've run into a static object, see if we can just kill it
|
|
//--------------------------------------------------------------
|
|
//
|
|
else if (GetWalkThruType() <= other->GetWalkThruType())
|
|
{
|
|
// if (shoulddamage)
|
|
{
|
|
TakeDamageMessage damage_message(
|
|
other->GetReplicatorID(),
|
|
GetReplicatorID(),
|
|
100000.0f,
|
|
// MSL 5.05 Advance Mode
|
|
0,
|
|
RammingDamageFromAboveType,
|
|
data->m_otherNormal,
|
|
data->m_worldIntersectionPoint,
|
|
0.0f,
|
|
false,
|
|
TakeDamageMessage::DefaultWeaponType
|
|
);
|
|
other->Receive(&damage_message);
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
//-----------------
|
|
// Stop the vehicle
|
|
//-----------------
|
|
//
|
|
Entity::ExecutionStateEngine *engine = executionState;
|
|
if (engine->GetState() == ExecutionStateEngine::NeverExecuteState || !IsUsingPostCollision())
|
|
{
|
|
delete collisions;
|
|
Verify(!newCollisions);
|
|
return false;
|
|
}
|
|
Verify(EntityManager::GetInstance()->IsInPostCollisionExecution(this));
|
|
newCollisions = collisions;
|
|
currentSpeedMPS = 0.0f;
|
|
currentSpeedKPH = currentSpeedMPS * 3.6f;
|
|
*new_position = GetLocalToParent();
|
|
return true;
|
|
}
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
//
|
|
void
|
|
Vehicle::PostCollisionExecute(Time till)
|
|
{
|
|
Check_Object(this);
|
|
Check_Object(executionState);
|
|
POSTCOLLISION_LOGIC("Vehicle");
|
|
|
|
//
|
|
//------------------------------------------------------------------------
|
|
// If we are trying to go to a particular spot, figure out where that is
|
|
// relative to us, negating the values if we are trying to back up to that
|
|
// spot
|
|
//------------------------------------------------------------------------
|
|
//
|
|
if (followDirRequest)
|
|
{
|
|
Point3D distance_to_target;
|
|
Point3D loc (GetLocalToWorld ());
|
|
LinearMatrix4D tempmat (GetLocalToWorld ());
|
|
|
|
tempmat.AlignLocalAxisToWorldVector (Vector3D::Up,Y_Axis,X_Axis,Z_Axis);
|
|
distance_to_target.MultiplyByInverse(dirRequest, tempmat);
|
|
if (followDirRequestReverse)
|
|
{
|
|
distance_to_target.x = -distance_to_target.x;
|
|
distance_to_target.z = -distance_to_target.z;
|
|
}
|
|
|
|
//
|
|
//-------------------------------------------------------------------
|
|
// If we are still some distance from the target spot, figure out yaw
|
|
// to the target angle
|
|
//-------------------------------------------------------------------
|
|
//
|
|
yawDemand = 0.0f;
|
|
if (!Small_Enough(distance_to_target.z))
|
|
{
|
|
Scalar heading_adjustment = Arctan(distance_to_target.x, distance_to_target.z);
|
|
|
|
//
|
|
//--------------------------------------------------------------
|
|
// Only turn if we are off by more than a degree. If so, figure
|
|
// out how long we need to turn at full speed to point at the
|
|
// target
|
|
//--------------------------------------------------------------
|
|
//
|
|
Scalar turn_amount = Abs(heading_adjustment);
|
|
if (turn_amount > Radians_Per_Degree)
|
|
{
|
|
const GameModel *model = GetGameModel();
|
|
Scalar time_slice = GetTimeSlice(till);
|
|
Scalar turn_step = heading_adjustment * time_slice;
|
|
Scalar max_step = Lerp(model->fullStopTurnRate, model->topSpeedTurnRate, currentSpeedMPS/GetMaxSpeed());
|
|
// Scalar max_step = model->fullStopTurnRate * time_slice;
|
|
max_step *= time_slice;
|
|
Verify(max_step >= SMALL);
|
|
|
|
//
|
|
//-------------------------------------------------------------
|
|
// If we need to turn more than we can arrange this time slice,
|
|
// turn at full rate
|
|
//-------------------------------------------------------------
|
|
//
|
|
yawDemand = turn_step/max_step;
|
|
Clamp(yawDemand, -1.0f, 1.0f);
|
|
}
|
|
}
|
|
#if 0
|
|
if (!strnicmp("ambush2", instanceName, 7))
|
|
{
|
|
SPEWALWAYS(("afarrier", (char*)instanceName));
|
|
SPEWALWAYS(("afarrier", " local position = %f,%f,%f", loc.x, loc.y, loc.z));
|
|
SPEWALWAYS(("afarrier", " world target = %f,%f,%f", dirRequest.x, dirRequest.y, dirRequest.z));
|
|
SPEWALWAYS(("afarrier", " local target = %f,%f,%f", distance_to_target.x, distance_to_target.y, distance_to_target.z));
|
|
SPEWALWAYS(("afarrier", " yaw = %f", yawDemand));
|
|
}
|
|
#endif
|
|
}
|
|
|
|
//
|
|
//------------------------------------------
|
|
// Reset the parameterization for next frame
|
|
//------------------------------------------
|
|
//
|
|
lastParameterization = till;
|
|
initialLocalToParent = GetLocalToParent();
|
|
|
|
//
|
|
//---------------------------------------------------------------------
|
|
// We need to execute watchers for our children if they are part of us,
|
|
// then we create another trail object
|
|
//---------------------------------------------------------------------
|
|
//
|
|
CreateTrail();
|
|
|
|
// if(m_needSelfDestruct)
|
|
// ProcessSelfDestruct();
|
|
|
|
//
|
|
//------------------------------------------------
|
|
// If we have a shadow, first get its current data
|
|
//------------------------------------------------
|
|
//
|
|
if (shadow && TiledLightManager::s_ShadowMode != TiledLightManager::e_NoShadows && Mission::GetInstance()->GetMainLight())
|
|
{
|
|
POSTCOLLISION_LOGIC("Vehicle::Cast Shadow");
|
|
Check_Object(shadow);
|
|
gosFX::LightManager::Info info;
|
|
shadow->GetInfo(&info);
|
|
|
|
//
|
|
//-----------------------------------------------------------------
|
|
// Get the orientation of our main light and set our shadow to that
|
|
//-----------------------------------------------------------------
|
|
//
|
|
gosFX::LightManager::Info main_info;
|
|
gosFX::Light *main_light = Mission::GetInstance()->GetMainLight();
|
|
Check_Object(main_light);
|
|
main_light->GetInfo(&main_info);
|
|
info.m_origin = main_info.m_origin;
|
|
|
|
//
|
|
//----------------------------------------------------------
|
|
// Cast a ray from our center of collision toward the ground
|
|
//----------------------------------------------------------
|
|
//
|
|
Stuff::Line3D line;
|
|
line.m_length = 500.0f;
|
|
info.m_origin.GetLocalForwardInWorld(&line.m_direction);
|
|
CollisionVolume *cv = GetSolidVolume();
|
|
if (cv)
|
|
{
|
|
Check_Object(cv);
|
|
LinearMatrix4D new_bounds;
|
|
new_bounds.Multiply(cv->m_localSpaceBounds.localToParent, GetLocalToWorld());
|
|
line.m_origin = new_bounds;
|
|
Stuff::Normal3D normal;
|
|
CollisionQuery query(&line, &normal, CanBeWalkedOnFlag, this);
|
|
Check_Object(CollisionGrid::Instance);
|
|
CollisionGrid::Instance->ProjectLine(&query);
|
|
|
|
//
|
|
//--------------------------------------------------------------------
|
|
// Set our shadow position to the end of the line, and compute the new
|
|
// radius of the light (it will be inner radius / cos(light angle).
|
|
// We will automatically set the radius to the height of the center
|
|
// of the collision volume over the vehicles origin
|
|
//--------------------------------------------------------------------
|
|
//
|
|
Point3D end;
|
|
line.FindEnd(&end);
|
|
info.m_origin.BuildTranslation(end);
|
|
Stuff::Point3D vehicle_translation(GetLocalToWorld());
|
|
info.m_inner = line.m_origin.y - vehicle_translation.y;
|
|
if(Small_Enough(line.m_direction.y))
|
|
{
|
|
info.m_outer=info.m_inner+10.0f;
|
|
}
|
|
else
|
|
{
|
|
info.m_outer = info.m_inner / -line.m_direction.y;
|
|
}
|
|
|
|
// MSL 5.02 Shadow
|
|
//
|
|
// Needed a way to clip out a shadow when it is out of range.
|
|
// Out of range is when it is past the fogend setting of the current mission.
|
|
//
|
|
//
|
|
// if (distance to object is > max fog distance then we turn off the lightmap for the shadow, otherwise we turn it on)
|
|
//
|
|
|
|
//
|
|
// First get the distance from the vehicle to the camera
|
|
//
|
|
if (Adept::Player::GetInstance()->playerInterface)
|
|
{
|
|
MWMission * mission = Cast_Object(MWMission*, MWMission::GetInstance());
|
|
VehicleInterface * vi = Cast_Object(VehicleInterface*, Adept::Player::GetInstance()->playerInterface);
|
|
if (vi->m_reticuleCamera && mission)
|
|
{
|
|
Stuff::Point3D camera_local_to_world;
|
|
Stuff::Point3D vehicle_local_to_world;
|
|
camera_local_to_world = vi->m_reticuleCamera->GetLocalToWorld();
|
|
vehicle_local_to_world = this->GetLocalToWorld();
|
|
Stuff::Point3D vehicleDistance;
|
|
vehicleDistance.Subtract(camera_local_to_world, vehicle_local_to_world);
|
|
Stuff::Scalar vehicle_height = vehicle_local_to_world.y;
|
|
Stuff::Scalar distanceSquared = vehicleDistance.GetLengthSquared();
|
|
Stuff::Scalar fog_start_squared;
|
|
Stuff::Scalar fog_end_squared;
|
|
Stuff::Scalar fog_end_height;
|
|
Stuff::Scalar fog_distance;
|
|
Stuff::Scalar fog_height;
|
|
//
|
|
// Then get the fog distance
|
|
fog_start_squared = mission->m_generalFogStart * mission->m_generalFogStart;
|
|
fog_end_squared = mission->m_generalFogEnd * mission->m_generalFogEnd;
|
|
fog_end_height = mission->m_heightFogEnd;
|
|
//
|
|
// Then compare
|
|
//
|
|
MidLevelRenderer::MLRShadowLight * mlrshadowlight = shadow->GetMLRLight();
|
|
info.m_intensity = shadowIntensity; // default
|
|
fog_distance = shadowIntensity * ((fog_end_squared - distanceSquared)/(fog_end_squared - fog_start_squared));
|
|
fog_height = shadowIntensity * (vehicle_height/fog_end_height);
|
|
if (mlrshadowlight)
|
|
{
|
|
if ((fog_end_squared > distanceSquared) || (vehicle_height < fog_end_height))
|
|
{
|
|
if (fog_end_squared > distanceSquared)
|
|
{
|
|
shadow->GetMLRLight()->ShowShadow(true);
|
|
//
|
|
// Set the intensity here so it pops less.
|
|
//
|
|
if (!Small_Enough(fog_end_squared - fog_start_squared))
|
|
{
|
|
Min_Clamp(distanceSquared, fog_start_squared);
|
|
fog_distance = shadowIntensity * ((fog_end_squared - distanceSquared)/(fog_end_squared - fog_start_squared));
|
|
}
|
|
}
|
|
if (fog_distance < fog_height)
|
|
info.m_intensity = fog_distance;
|
|
else
|
|
info.m_intensity = fog_height;
|
|
}
|
|
else
|
|
{
|
|
shadow->GetMLRLight()->ShowShadow(false);
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
}
|
|
shadow->ChangeLight(&info);
|
|
}
|
|
}
|
|
|
|
//
|
|
//---------------------------------
|
|
// Let the parent handle things now
|
|
//---------------------------------
|
|
//
|
|
BaseClass::PostCollisionExecute(till);
|
|
}
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
//
|
|
void
|
|
Vehicle::ComputeForwardSpeed(Stuff::Scalar time_slice)
|
|
{
|
|
Check_Object(this);
|
|
const GameModel *model = GetGameModel();
|
|
Check_Object(model);
|
|
Stuff::Scalar maxSpeedMod;
|
|
bool wallslope=false;
|
|
|
|
//
|
|
//----------------------------------------------------
|
|
// Set the demands to zero if the vehicle is shut down
|
|
//----------------------------------------------------
|
|
//
|
|
if (vehicleShutDown)
|
|
{
|
|
speedDemand = 0.0f;
|
|
speedDemandKPH = 0.0f;
|
|
}
|
|
|
|
Verify (model->moveTypeFlag != MWObject__GameModel::WATER_MOVETYPE); // these should be boats
|
|
|
|
maxSpeedMod = 1.0f;
|
|
switch (materialHit)
|
|
{
|
|
case WaterMaterial:
|
|
// PAUSE (("vehicle walked on water or swamp material"));
|
|
case ConcreteMaterial:
|
|
switch (model->moveTypeFlag)
|
|
{
|
|
case MWObject__GameModel::WHEEL_MOVETYPE:
|
|
maxSpeedMod = 1.25f;
|
|
break;
|
|
}
|
|
break;
|
|
case SnowMaterial:
|
|
switch (model->moveTypeFlag)
|
|
{
|
|
case MWObject__GameModel::TRACK_MOVETYPE:
|
|
maxSpeedMod = 0.5f;
|
|
break;
|
|
case MWObject__GameModel::WHEEL_MOVETYPE:
|
|
maxSpeedMod = 0.25f;
|
|
break;
|
|
}
|
|
break;
|
|
case FakeShallowWaterMaterial:
|
|
switch (model->moveTypeFlag)
|
|
{
|
|
case MWObject__GameModel::WHEEL_MOVETYPE:
|
|
maxSpeedMod = 0.0f;
|
|
break;
|
|
case MWObject__GameModel::TRACK_MOVETYPE:
|
|
maxSpeedMod = 0.0f;
|
|
break;
|
|
}
|
|
break;
|
|
case FakeMidWaterMaterial:
|
|
switch (model->moveTypeFlag)
|
|
{
|
|
case MWObject__GameModel::WHEEL_MOVETYPE:
|
|
case MWObject__GameModel::TRACK_MOVETYPE:
|
|
maxSpeedMod = 0.0f;
|
|
break;
|
|
}
|
|
break;
|
|
case FakeOceanicWaterMaterial:
|
|
switch (model->moveTypeFlag)
|
|
{
|
|
case MWObject__GameModel::WHEEL_MOVETYPE:
|
|
case MWObject__GameModel::TRACK_MOVETYPE:
|
|
maxSpeedMod = 0.0f;
|
|
break;
|
|
}
|
|
break;
|
|
// MSL 5.03 Lava
|
|
case CrackedLavaMaterial:
|
|
case OpenLavaMaterial:
|
|
switch (model->moveTypeFlag)
|
|
{
|
|
case MWObject__GameModel::WHEEL_MOVETYPE:
|
|
case MWObject__GameModel::TRACK_MOVETYPE:
|
|
//collide against the lava
|
|
maxSpeedMod = 0.0f;
|
|
break;
|
|
}
|
|
break;
|
|
}
|
|
|
|
if (maxSpeedMod == 0.0f) // tell the ai we had a collision based on material type
|
|
{
|
|
if ((m_AI) && (m_AI->IsDerivedFrom (MoverAI::DefaultData)))
|
|
{
|
|
MoverAI *ai = Cast_Object(MoverAI*, m_AI);
|
|
ai->SlopeCollide();
|
|
}
|
|
}
|
|
|
|
//
|
|
//------------------------------------------------------------------------
|
|
// Figure out our desired speed. If we are stopped, set the KPH variables
|
|
// to zero and quit
|
|
//------------------------------------------------------------------------
|
|
//
|
|
if (speedDemand >= 0.0f)
|
|
speedDemandMPS = (speedDemand * GetMaxSpeed()*maxSpeedMod);
|
|
else
|
|
speedDemandMPS = -(speedDemand * model->maxReverseSpeed*maxSpeedMod);
|
|
if (speedDemandMPS == 0.0f && currentSpeedMPS == 0.0f)
|
|
{
|
|
speedDemandKPH = 0.0f;
|
|
currentSpeedKPH = 0.0f;
|
|
SetIdle ();
|
|
return;
|
|
}
|
|
|
|
//
|
|
//----------------------------------------------------------------------
|
|
// Figure out the grade we are on. If it is less than 45 degrees, we
|
|
// can do regular motion on it.
|
|
//
|
|
// NOTE: This approach is a big steamy pile of doo-doo. I will kill it!
|
|
//----------------------------------------------------------------------
|
|
//
|
|
Scalar grade;
|
|
Scalar cosang;
|
|
UnitVector3D forward;
|
|
GetLocalToWorld().GetLocalForwardInWorld(&forward);
|
|
cosang = (forward.x*m_WorldAvgNormal.x)+(forward.y*m_WorldAvgNormal.y)+(forward.z*m_WorldAvgNormal.z);
|
|
|
|
// if (m_LocalNormal.z < 0)
|
|
if (cosang < 0) // going up the slope,
|
|
{
|
|
grade = Abs (m_RawGroundPitch);
|
|
m_RawGroundPitch = m_RawGroundPitch < 0 ? m_RawGroundPitch * -1.0f : m_RawGroundPitch;
|
|
}
|
|
else
|
|
{
|
|
grade = 0;
|
|
m_RawGroundPitch = m_RawGroundPitch > 0 ? m_RawGroundPitch * -1.0f : m_RawGroundPitch;
|
|
}
|
|
|
|
Scalar decceleration = model->decceleration;
|
|
// bool ai_grade_collide = false;
|
|
if (grade <= startSlopeDeceleration)
|
|
{
|
|
Scalar clamped_pitch = m_RawGroundPitch;
|
|
Scalar clamped_roll = rawLocalGroundRoll;
|
|
|
|
//
|
|
//----------------------------------------------------
|
|
// The maximum slope we will request is +/- 44 degrees
|
|
//----------------------------------------------------
|
|
//
|
|
Scalar max_value = startSlopeDeceleration - Radians_Per_Degree;
|
|
Clamp(clamped_pitch, -max_value, max_value);
|
|
Clamp(clamped_roll, -max_value, max_value);
|
|
|
|
//
|
|
//----------------------------------------------------------------------
|
|
// If we are currently driving forward, and we are not heading downhill,
|
|
// figure out how much to reduce our speed demand by
|
|
//----------------------------------------------------------------------
|
|
//
|
|
if (currentSpeedMPS > 0.0f)
|
|
{
|
|
if (clamped_pitch >= 0.0f)
|
|
{
|
|
Stuff::Scalar speed_effect = 0.4f * clamped_pitch / startSlopeDeceleration;
|
|
speedDemandMPS -= speedDemandMPS * speed_effect;
|
|
}
|
|
|
|
//
|
|
//------------------------------------------------------
|
|
// Otherwise, if we are driving downhill, speed up a bit
|
|
//------------------------------------------------------
|
|
//
|
|
else
|
|
{
|
|
Stuff::Scalar speed_effect = -0.3f * clamped_pitch / startSlopeDeceleration;
|
|
speedDemandMPS += speedDemandMPS * speed_effect;
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
//------------------------------------------------------------------------
|
|
// We need to deal if an extreme case, so see if we are running up a cliff
|
|
// face. If we are, we are going to want to stop
|
|
//------------------------------------------------------------------------
|
|
//
|
|
else if (m_RawGroundPitch > 0.0f)
|
|
{
|
|
speedDemandMPS = 0.0f;
|
|
|
|
//
|
|
//------------------------------------------------------------------
|
|
// If the grade is higher than we can tolerate, this is treated as a
|
|
// collision - tell the AI
|
|
//------------------------------------------------------------------
|
|
//
|
|
if (grade >= maxSlope)
|
|
{
|
|
// SetCollide();
|
|
decceleration = model->slopeDecel3;
|
|
#if 0
|
|
if ((m_AI) && (m_AI->IsDerivedFrom (MoverAI::DefaultData)))
|
|
{
|
|
MoverAI *ai = Cast_Object(MoverAI*, m_AI);
|
|
ai->SlopeCollide();
|
|
}
|
|
#endif
|
|
wallslope = true;
|
|
}
|
|
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
// We can tolerate the slope, but will slow down, so pick which deceleration
|
|
// band to use
|
|
//--------------------------------------------------------------------------
|
|
//
|
|
else
|
|
{
|
|
#if 0
|
|
ai_grade_collide = m_AI && m_AI->IsDerivedFrom(MoverAI::DefaultData);
|
|
if ((m_AI) && (m_AI->IsDerivedFrom (MoverAI::DefaultData)))
|
|
{
|
|
MoverAI *ai = Cast_Object(MoverAI*, m_AI);
|
|
ai->SlopeCollide();
|
|
}
|
|
#endif
|
|
Verify(startSlopeDeceleration <= (Pi_Over_4+Pi_Over_8));
|
|
Scalar band_size = (maxSlope - startSlopeDeceleration)/3.0f;
|
|
Scalar grade_band_one = startSlopeDeceleration + band_size;
|
|
|
|
if (grade < grade_band_one)
|
|
decceleration = model->slopeDecel1;
|
|
else
|
|
{
|
|
Scalar grade_band_two = grade_band_one + band_size;
|
|
decceleration =
|
|
(grade < grade_band_two) ? model->slopeDecel2 : model->slopeDecel3;
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
//--------------------------------------------
|
|
// Anything can go down - ain't gravity great?
|
|
//--------------------------------------------
|
|
//
|
|
else
|
|
{
|
|
Stuff::Scalar adjust_pitch = -m_RawGroundPitch;
|
|
Max_Clamp(adjust_pitch, Pi_Over_4);
|
|
Stuff::Scalar speed_effect = 0.3f * adjust_pitch / Pi_Over_4;
|
|
speedDemandMPS += speedDemandMPS * speed_effect;
|
|
}
|
|
|
|
//
|
|
//--------------------------------------------------------------------
|
|
// If we are not going backwards, find out what speed we want after we
|
|
// consider braking
|
|
//--------------------------------------------------------------------
|
|
//
|
|
if (currentSpeedMPS >= 0.0f)
|
|
{
|
|
Stuff::Scalar decel_speed = currentSpeedMPS - (model->decceleration * time_slice);
|
|
|
|
if (!wallslope)
|
|
{
|
|
Min_Clamp (decel_speed,20);
|
|
}
|
|
//
|
|
//--------------------------------------------------------------------
|
|
// If we end up wanting to go backwards and this is a collision to the
|
|
// AI, tell the AI we collided
|
|
//--------------------------------------------------------------------
|
|
//
|
|
#if 0
|
|
if (decel_speed <= 0.0f && ai_grade_collide)
|
|
{
|
|
if (m_AI)
|
|
{
|
|
MoverAI *ai = Cast_Object(MoverAI*, m_AI);
|
|
ai->SlopeCollide();
|
|
}
|
|
}
|
|
#endif
|
|
|
|
//
|
|
//--------------------------------------
|
|
// Otherwise, adjust our speed as needed
|
|
//--------------------------------------
|
|
//
|
|
if (speedDemandMPS > currentSpeedMPS)
|
|
{
|
|
currentSpeedMPS += (model->acceleration * time_slice);
|
|
Max_Clamp(currentSpeedMPS, speedDemandMPS);
|
|
}
|
|
else if (speedDemandMPS < currentSpeedMPS)
|
|
{
|
|
currentSpeedMPS -= (model->decceleration * time_slice);
|
|
Min_Clamp(currentSpeedMPS, speedDemandMPS);
|
|
}
|
|
}
|
|
|
|
//
|
|
//--------------------------------------------------------------
|
|
// We are backing up, so tell the AI if we had a grade collision
|
|
//--------------------------------------------------------------
|
|
//
|
|
else
|
|
{
|
|
Stuff::Scalar decel_spped = currentSpeedMPS + (model->reverseDeccelerationMultiplier * time_slice);
|
|
if (!wallslope)
|
|
{
|
|
Max_Clamp (decel_spped,-20);
|
|
}
|
|
#if 0
|
|
if ((decel_spped >= 0) && (ai_grade_collide))
|
|
{
|
|
if (m_AI)
|
|
{
|
|
MoverAI *ai = Cast_Object(MoverAI *,m_AI);
|
|
ai->SlopeCollide();
|
|
}
|
|
}
|
|
#endif
|
|
|
|
//
|
|
//-------------------------------------------------------------
|
|
// Deal with the stand transition if that is the case we are in
|
|
//-------------------------------------------------------------
|
|
//
|
|
|
|
//
|
|
//-------------------------------------
|
|
// Otherwise adjust our speed as needed
|
|
//-------------------------------------
|
|
//
|
|
if (speedDemandMPS < currentSpeedMPS)
|
|
{
|
|
currentSpeedMPS -= (model->acceleration * model->reverseAccelerationMultiplier * time_slice);
|
|
Min_Clamp(currentSpeedMPS, speedDemandMPS);
|
|
}
|
|
else if (speedDemandMPS > currentSpeedMPS)
|
|
{
|
|
currentSpeedMPS += (model->decceleration * model->reverseDeccelerationMultiplier * time_slice);
|
|
Max_Clamp(currentSpeedMPS, speedDemandMPS);
|
|
}
|
|
}
|
|
|
|
//
|
|
//----------------------------------------------------
|
|
// Now adjust all this to the limits in the model file
|
|
//----------------------------------------------------
|
|
//
|
|
Clamp(currentSpeedMPS, model->maxReverseSpeed*maxSpeedMod, GetMaxSpeed()*maxSpeedMod);
|
|
|
|
//
|
|
//---------------------------
|
|
// Set up the velocity vector
|
|
//---------------------------
|
|
//
|
|
localSpaceVelocity.linearMotion.z = currentSpeedMPS;
|
|
|
|
//
|
|
//----------------------
|
|
// Set the KPH variables
|
|
//----------------------
|
|
//
|
|
currentSpeedKPH = currentSpeedMPS * 3.6f;
|
|
speedDemandKPH = speedDemandMPS * 3.6f;
|
|
|
|
if (currentSpeedKPH == 0)
|
|
SetIdle ();
|
|
else
|
|
SetMoving ();
|
|
}
|
|
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
//
|
|
// called when the unit is dieing and needs to slow to a stop
|
|
void Vehicle::ComputeFrictionSpeed(Stuff::Scalar time_slice)
|
|
{
|
|
Check_Object(this);
|
|
Stuff::Scalar friction;
|
|
|
|
speedDemand = 0.0f;
|
|
speedDemandKPH = 0.0f;
|
|
yawDemand = 0;
|
|
speedDemandMPS = 0;
|
|
if (speedDemandMPS == 0.0f && currentSpeedMPS == 0.0f)
|
|
{
|
|
speedDemandKPH = 0.0f;
|
|
currentSpeedKPH = 0.0f;
|
|
return;
|
|
}
|
|
#ifdef _ARMOR
|
|
const GameModel *model = GetGameModel();
|
|
Check_Object(model);
|
|
Verify (model->moveTypeFlag != MWObject__GameModel::WATER_MOVETYPE); // these should be boats
|
|
#endif
|
|
|
|
friction = 10.0f;
|
|
switch (materialHit)
|
|
{
|
|
case WaterMaterial:
|
|
case ConcreteMaterial:
|
|
friction = 8.0f;
|
|
break;
|
|
case SnowMaterial:
|
|
friction = 5.0f;
|
|
break;
|
|
case FakeShallowWaterMaterial:
|
|
friction = 50.0f;
|
|
break;
|
|
case FakeMidWaterMaterial:
|
|
friction = 1000.0f; // stop it now
|
|
break;
|
|
case FakeOceanicWaterMaterial:
|
|
friction = 1000.0f; // stop it now
|
|
break;
|
|
}
|
|
|
|
|
|
//
|
|
//----------------------------------------------------------------------
|
|
// Figure out the grade we are on. If it is less than 45 degrees, we
|
|
// can do regular motion on it.
|
|
//
|
|
// NOTE: This approach is a big steamy pile of doo-doo. I will kill it!
|
|
//----------------------------------------------------------------------
|
|
//
|
|
Scalar grade;
|
|
Scalar cosang;
|
|
UnitVector3D forward;
|
|
GetLocalToWorld().GetLocalForwardInWorld(&forward);
|
|
cosang = (forward.x*m_WorldAvgNormal.x)+(forward.y*m_WorldAvgNormal.y)+(forward.z*m_WorldAvgNormal.z);
|
|
|
|
if (cosang < 0) // going up the slope,
|
|
{
|
|
grade = Abs (m_RawGroundPitch);
|
|
friction += 2.0f * (((float) sin (grade)) * g_Gravity);
|
|
}
|
|
else
|
|
{
|
|
grade = Abs (m_RawGroundPitch);
|
|
friction -= 2.0f * (((float) sin (grade)) * g_Gravity);
|
|
}
|
|
|
|
Clamp (friction,0.5f,1000.0f);
|
|
if (currentSpeedMPS < 0)
|
|
{
|
|
currentSpeedMPS += friction * time_slice;
|
|
Max_Clamp (currentSpeedMPS,0);
|
|
}
|
|
else
|
|
{
|
|
currentSpeedMPS -= friction * time_slice;
|
|
Min_Clamp (currentSpeedMPS,0);
|
|
}
|
|
|
|
|
|
localSpaceVelocity.linearMotion.z = currentSpeedMPS;
|
|
|
|
//
|
|
//----------------------
|
|
// Set the KPH variables
|
|
//----------------------
|
|
//
|
|
currentSpeedKPH = currentSpeedMPS * 3.6f;
|
|
speedDemandKPH = speedDemandMPS * 3.6f;
|
|
}
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
//
|
|
|
|
void Vehicle::FindGroundAngle(void)
|
|
{
|
|
Check_Object(this);
|
|
PRECOLLISION_LOGIC("Vehicle::FindGroundAngle");
|
|
|
|
//
|
|
//---------------------------------------------------
|
|
// Look for the centerpoint of where our feet are now
|
|
//---------------------------------------------------
|
|
//
|
|
const LinearMatrix4D& local_to_world = GetLocalToWorld();
|
|
Stuff::Line3D line;
|
|
line.m_length = 2000.0f;
|
|
line.m_direction = Vector3D::Down;
|
|
line.m_origin = local_to_world;
|
|
line.m_origin.y += 1000.0f;
|
|
Stuff::Normal3D normal;
|
|
CollisionQuery query(&line, &normal, CanBeWalkedOnFlag, this);
|
|
Check_Object(CollisionGrid::Instance);
|
|
|
|
Entity *entity_hit = CollisionGrid::Instance->ProjectLine(&query);
|
|
|
|
if (!entity_hit)
|
|
STOP(("Vehicle is off edge"));
|
|
|
|
materialHit = query.m_material;
|
|
//
|
|
//----------------------------------------------------------------
|
|
// Figure out the slope of the ground under the center of the vehicle
|
|
//----------------------------------------------------------------
|
|
//
|
|
Stuff::Vector3D world_avg_normal(*query.m_normal);
|
|
m_WorldAvgNormal = world_avg_normal;
|
|
m_RawGroundPitch = Arccos (world_avg_normal.y);
|
|
|
|
LinearMatrix4D tempmat (GetLocalToWorld ());
|
|
tempmat.AlignLocalAxisToWorldVector (Vector3D::Up,Y_Axis,X_Axis,Z_Axis);
|
|
|
|
m_LocalNormal.MultiplyByInverse(world_avg_normal, tempmat);
|
|
rawLocalGroundRoll = -Arctan(m_LocalNormal.x, m_LocalNormal.y);
|
|
rawLocalGroundPitch = -Arctan(m_LocalNormal.z, m_LocalNormal.y);
|
|
|
|
//
|
|
//--------------------------------------------------------------
|
|
// take our normal sum and figure out the local pitch/roll angle
|
|
//--------------------------------------------------------------
|
|
//
|
|
localGroundRoll = rawLocalGroundRoll;
|
|
localGroundPitch = rawLocalGroundPitch;
|
|
|
|
Point3D tr;
|
|
line.FindEnd(&tr);
|
|
Check_Object (Map::GetInstance());
|
|
Check_Object (Map::GetInstance()->GetGameModel ());
|
|
m_WaterDelta = Map::GetInstance()->GetGameModel ()->m_waterLevel - tr.y;
|
|
if ((m_WaterDelta >= ShallowWaterDepth) && (m_WaterDelta < MidWaterDepth))
|
|
{
|
|
materialHit = FakeShallowWaterMaterial;
|
|
}
|
|
else if ((m_WaterDelta >= MidWaterDepth) && (m_WaterDelta < DeepWaterDepth))
|
|
{
|
|
materialHit = FakeMidWaterMaterial;
|
|
}
|
|
else if (m_WaterDelta >= DeepWaterDepth)
|
|
{
|
|
materialHit = FakeOceanicWaterMaterial;
|
|
}
|
|
|
|
Min_Clamp (m_WaterDelta,0);
|
|
}
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
//
|
|
|
|
void Vehicle::FindGroundAngleUnitDiamond(void)
|
|
{
|
|
Check_Object(this);
|
|
|
|
//
|
|
//----------------------------------------------------
|
|
// Figure out where the four points of the diamond are
|
|
//----------------------------------------------------
|
|
//
|
|
const Stuff::LinearMatrix4D &local_to_world = GetLocalToWorld();
|
|
Check_Object(&local_to_world);
|
|
|
|
|
|
//
|
|
//------------------------------------------
|
|
// Set up our variables and the query object
|
|
//------------------------------------------
|
|
//
|
|
Stuff::Point3D local_point;
|
|
local_point = local_to_world;
|
|
Stuff::Vector3D world_avg_normal;
|
|
Stuff::Normal3D normal;
|
|
Stuff::Line3D line;
|
|
CollisionQuery query(&line, &normal, CanBeWalkedOnFlag, this);
|
|
|
|
//
|
|
//---------------------------------------------------------------------------
|
|
// Test the forward ray. If we don't hit anything, assume the ground is flat
|
|
//---------------------------------------------------------------------------
|
|
//
|
|
line.m_length = 20.0f;
|
|
line.m_direction = Vector3D::Down;
|
|
line.m_origin.Multiply(Stuff::Point3D(0.0f, 0.0f, 1.5f), local_to_world);
|
|
line.m_origin.y += 5.0f;
|
|
Check_Object(CollisionGrid::Instance);
|
|
Stuff::Point3D collide_point;
|
|
if (CollisionGrid::Instance->ProjectLine(&query))
|
|
world_avg_normal = *query.m_normal;
|
|
else
|
|
{
|
|
localGroundPitch = 0;
|
|
localGroundRoll = 0;
|
|
return;
|
|
}
|
|
|
|
//
|
|
//---------------------
|
|
// Test the reverse ray
|
|
//---------------------
|
|
//
|
|
line.m_length = 20.0f;
|
|
line.m_origin.Multiply(Stuff::Point3D(0.0f, 0.0f, -1.5f), local_to_world);
|
|
line.m_origin.y += 5.0f;
|
|
if (CollisionGrid::Instance->ProjectLine(&query))
|
|
world_avg_normal += *query.m_normal;
|
|
else
|
|
{
|
|
localGroundPitch = 0;
|
|
localGroundRoll = 0;
|
|
return;
|
|
}
|
|
|
|
//
|
|
//------------------
|
|
// Test the left ray
|
|
//------------------
|
|
//
|
|
line.m_length = 20.0f;
|
|
line.m_origin.Multiply(Stuff::Point3D(1.0f, 0.0f, 0.0f), local_to_world);
|
|
line.m_origin.y += 5.0f;
|
|
if (CollisionGrid::Instance->ProjectLine(&query))
|
|
world_avg_normal += *query.m_normal;
|
|
else
|
|
{
|
|
localGroundPitch = 0;
|
|
localGroundRoll = 0;
|
|
return;
|
|
}
|
|
|
|
//
|
|
//-------------------
|
|
// Test the right ray
|
|
//-------------------
|
|
//
|
|
line.m_length = 20.0f;
|
|
line.m_origin.Multiply(Stuff::Point3D(-1.0f, 0.0f, 0.0f), local_to_world);
|
|
line.m_origin.y += 5.0f;
|
|
if (CollisionGrid::Instance->ProjectLine(&query))
|
|
world_avg_normal += *query.m_normal;
|
|
else
|
|
{
|
|
localGroundPitch = 0;
|
|
localGroundRoll = 0;
|
|
return;
|
|
}
|
|
|
|
//
|
|
//---------------------------------------------------------------------
|
|
// put the summed normal into local space, then figure out the relative
|
|
// roll and pitch angles
|
|
//---------------------------------------------------------------------
|
|
//
|
|
Stuff::Vector3D local_normal;
|
|
local_normal.MultiplyByInverse(world_avg_normal, local_to_world);
|
|
localGroundRoll = Arctan(local_normal.x, local_normal.y);
|
|
localGroundPitch = Arctan(local_normal.z, local_normal.y);
|
|
rawLocalGroundPitch = localGroundPitch;
|
|
rawLocalGroundRoll = localGroundRoll;
|
|
}
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
//
|
|
void
|
|
Vehicle::UpdateVehiclePosition(Scalar time_slice)
|
|
{
|
|
Check_Object(this);
|
|
|
|
//
|
|
//---------------------------------------------------------------------
|
|
// Apply velocity/acceleration to position and acceleration to velocity
|
|
//---------------------------------------------------------------------
|
|
//
|
|
PRECOLLISION_LOGIC("Vehicle::UpdateVehiclePosition");
|
|
const LinearMatrix4D &vehicle_to_world = GetLocalToWorld();
|
|
Point3D world_translation(vehicle_to_world);
|
|
Vector3D delta;
|
|
delta.AddScaled(
|
|
localSpaceVelocity.linearMotion,
|
|
localSpaceAcceleration.linearMotion,
|
|
0.5f * time_slice
|
|
);
|
|
Vector3D world_delta;
|
|
world_delta.Multiply(delta, vehicle_to_world);
|
|
Max_Clamp(world_delta.y, 0.0f);
|
|
world_translation.AddScaled(world_translation, world_delta, time_slice);
|
|
localSpaceVelocity.linearMotion.y += localSpaceAcceleration.linearMotion.y*time_slice;
|
|
Max_Clamp(localSpaceVelocity.linearMotion.y, 0.0f);
|
|
|
|
//
|
|
//--------------------------------------
|
|
// Update our turn rates and orientation
|
|
//--------------------------------------
|
|
//
|
|
const GameModel *model = GetGameModel();
|
|
Check_Object(model);
|
|
Scalar temp = Lerp(model->fullStopTurnRate, model->topSpeedTurnRate, currentSpeedMPS/GetMaxSpeed());
|
|
YawPitchRoll new_rotation(vehicle_to_world);
|
|
temp *= time_slice;
|
|
new_rotation.yaw += yawDemand * temp;
|
|
new_rotation.pitch += pitchDemand * temp;
|
|
new_rotation.roll += rollDemand * temp;
|
|
|
|
|
|
|
|
//
|
|
//----------------------------------------------
|
|
// Adjust position from network
|
|
//----------------------------------------------
|
|
//
|
|
|
|
YawPitchRoll correction_angle;
|
|
Point3D correction_position;
|
|
|
|
GetNetworkAdjustment(time_slice, correction_position, correction_angle);
|
|
|
|
|
|
new_rotation.yaw.angle += correction_angle.yaw.angle;
|
|
new_rotation.pitch.angle += correction_angle.pitch.angle;
|
|
new_rotation.roll.angle += correction_angle.roll.angle;
|
|
world_translation += correction_position;
|
|
|
|
|
|
//
|
|
//----------------------------------------------
|
|
// Cast a ray down to see what we are driving on
|
|
//----------------------------------------------
|
|
//
|
|
Stuff::Line3D line;
|
|
line.m_length = m_groundRaycastHeight + 0.001f;
|
|
line.m_direction = Vector3D::Down;
|
|
line.m_origin = world_translation;
|
|
line.m_origin.y += m_groundRaycastHeight;
|
|
Stuff::Normal3D normal;
|
|
CollisionQuery query(&line, &normal, CanBeWalkedOnFlag, this);
|
|
Check_Object(CollisionGrid::Instance);
|
|
|
|
|
|
|
|
|
|
//
|
|
//------------------------------------------------
|
|
// If we miss the ground, go to where gravity said
|
|
//------------------------------------------------
|
|
//
|
|
LinearMatrix4D new_local_to_world;
|
|
new_local_to_world.BuildRotation(new_rotation);
|
|
if (!CollisionGrid::Instance->ProjectLine(&query))
|
|
{
|
|
materialHit = MaterialCount;
|
|
worldSpaceVelocity.linearMotion.Multiply(
|
|
localSpaceVelocity.linearMotion,
|
|
vehicle_to_world
|
|
);
|
|
Max_Clamp(worldSpaceVelocity.linearMotion.y, 0.0f);
|
|
|
|
|
|
|
|
new_local_to_world.BuildTranslation(world_translation);
|
|
}
|
|
|
|
//
|
|
//--------------------------------------------------
|
|
// Otherwise, adapt the vehicle to the ground normal
|
|
//--------------------------------------------------
|
|
//
|
|
else
|
|
{
|
|
materialHit = query.m_material;
|
|
line.FindEnd(&world_translation);
|
|
|
|
|
|
new_local_to_world.BuildTranslation(world_translation);
|
|
new_local_to_world.AlignLocalAxisToWorldVector(normal, Y_Axis, X_Axis, Z_Axis);
|
|
|
|
//
|
|
//--------------------------------------------------
|
|
// Update the velocities to conform to the landscape
|
|
//--------------------------------------------------
|
|
//
|
|
localSpaceVelocity.linearMotion.x = 0.0f;
|
|
localSpaceVelocity.linearMotion.y = 0.0f;
|
|
localSpaceVelocity.linearMotion.z = currentSpeedMPS;
|
|
worldSpaceVelocity.linearMotion.Multiply(
|
|
localSpaceVelocity.linearMotion,
|
|
new_local_to_world
|
|
);
|
|
}
|
|
SetNewLocalToParent(new_local_to_world);
|
|
}
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
//
|
|
void
|
|
Vehicle::HookUpSubsystems()
|
|
{
|
|
Stuff::ChainIteratorOf<Subsystem*> iterator(&subsystemsInVehicle);
|
|
Subsystem *subsystem;
|
|
while ((subsystem = iterator.ReadAndNext()) != NULL)
|
|
{
|
|
if (subsystem->IsDerivedFrom(Engine::DefaultData))
|
|
{
|
|
engine = Cast_Object(Engine *, subsystem);
|
|
}
|
|
else if (subsystem->IsDerivedFrom(Torso::DefaultData))
|
|
{
|
|
Torso* torso = Cast_Object(Torso*, subsystem);
|
|
if (std::find(m_Torsos.begin(),m_Torsos.end(),torso) == m_Torsos.end())
|
|
{
|
|
m_Torsos.push_back(torso);
|
|
}
|
|
}
|
|
//Tell the Damage Zone that holds the subsystem that it is there!!!
|
|
}
|
|
if(engine)
|
|
engine->ConnectEngine(this);
|
|
}
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
//
|
|
void
|
|
Vehicle::BecomeInteresting(bool render_me)
|
|
{
|
|
Check_Object(this);
|
|
LOAD_LOGIC("Become Interesting::Vehicle");
|
|
|
|
|
|
|
|
|
|
|
|
|
|
//
|
|
//--------------------------------------------------------------------
|
|
// Let our parent deal with the video renderer. If we have a shadow,
|
|
// don't make another
|
|
//--------------------------------------------------------------------
|
|
//
|
|
BaseClass::BecomeInteresting(render_me);
|
|
|
|
CollisionVolume *cv = NULL;;
|
|
cv = GetSolidVolume();
|
|
if (!cv)
|
|
{
|
|
cv = this->GetHierarchicalVolume();
|
|
}
|
|
if (cv)
|
|
{
|
|
ExtentBox collisionBox(cv->m_localSpaceBounds);
|
|
Point3D rotated_axis_extents;
|
|
rotated_axis_extents.Multiply(cv->m_localSpaceBounds.axisExtents, cv->m_localSpaceBounds.localToParent);
|
|
m_groundRaycastHeight = cv->m_localSpaceBounds.localToParent.entries[7];
|
|
m_groundRaycastHeight += Stuff::Fabs(rotated_axis_extents.y);
|
|
}
|
|
|
|
|
|
if (!render_me || shadow)
|
|
return;
|
|
|
|
//
|
|
//------------------------------
|
|
// Otherwise, load up our shadow
|
|
//------------------------------
|
|
//
|
|
gosFX::Light *main_light = Mission::GetInstance()->GetMainLight();
|
|
const GameModel *model = GetGameModel();
|
|
Check_Object(model);
|
|
if (!model->shadowStream || !main_light)
|
|
{
|
|
shadow = NULL;
|
|
// MSL 5.02 Shadow
|
|
shadowIntensity = 0.0f;
|
|
}
|
|
else
|
|
{
|
|
Resource shadow_stream(model->shadowStream);
|
|
Verify(shadow_stream.DoesResourceExist());
|
|
gos_PushCurrentHeap(MidLevelRenderer::LightsHeap);
|
|
int version = MidLevelRenderer::ReadMLRVersion(&shadow_stream);
|
|
Check_Object(TiledLightManager::Instance);
|
|
while (shadow_stream.GetBytesRemaining() > 0)
|
|
{
|
|
int light_type;
|
|
shadow_stream >> light_type;
|
|
Verify(light_type == MidLevelRenderer::MLRLight::ShadowLight);
|
|
MidLevelRenderer::MLRShadowLight *shadow_light =
|
|
new MidLevelRenderer::MLRShadowLight(&shadow_stream, version);
|
|
Check_Object(shadow_light);
|
|
shadow =
|
|
Cast_Object(ShadowLight*, TiledLightManager::Instance->MakeLight(shadow_light));
|
|
shadow->m_shadowCaster = GetElement();
|
|
// MSL 5.02 Shadow
|
|
gosFX::LightManager::Info info;
|
|
shadow->GetInfo(&info);
|
|
shadowIntensity = info.m_intensity;
|
|
}
|
|
gos_PopCurrentHeap();
|
|
}
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
//
|
|
void
|
|
Vehicle::BecomeUninteresting()
|
|
{
|
|
Check_Object(this);
|
|
BaseClass::BecomeUninteresting();
|
|
|
|
//
|
|
//------------------
|
|
// Remove our shadow
|
|
//------------------
|
|
//
|
|
if (shadow)
|
|
{
|
|
Check_Object(shadow);
|
|
delete shadow;
|
|
shadow = NULL;
|
|
}
|
|
}
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
//
|
|
void
|
|
Vehicle::CreateTrail()
|
|
{
|
|
Check_Object(this);
|
|
POSTCOLLISION_LOGIC("Vehicle::Create Trail");
|
|
|
|
const GameModel *model = GetGameModel();
|
|
Check_Object(model);
|
|
|
|
//
|
|
//---------------------------------------------------------------------
|
|
// If we hit legal material and can have a trail, get the ID of the one
|
|
// we should use and see if it is the same as the one we currently have
|
|
//---------------------------------------------------------------------
|
|
//
|
|
if (materialHit>=0 && materialHit<MaterialCount && model->trailEffectsTable != ResourceID::Null)
|
|
{
|
|
ResourceID trail_id;
|
|
char material = materialHit;
|
|
if(GetLocalToWorld().entries[7] - 1.0f <= Map::GetInstance()->GetGameModel ()->m_waterLevel)
|
|
material = WaterMaterial;
|
|
|
|
model->GetEffectResourceID(&trail_id, model->trailEffectsTable, material);
|
|
if (trail_id == currentTrailID)
|
|
return;
|
|
|
|
//
|
|
//----------------------------------------
|
|
// If we currently have an effect, kill it
|
|
//----------------------------------------
|
|
//
|
|
if (currentTrailEffect.GetCurrent())
|
|
{
|
|
currentTrailEffect.GetCurrent()->executionState->RequestState(
|
|
Effect::ExecutionStateEngine::StoppingState);
|
|
currentTrailEffect.Remove();
|
|
}
|
|
|
|
//
|
|
//-------------------------------------
|
|
// Make the new effect if it isn't null
|
|
//-------------------------------------
|
|
//
|
|
if (trail_id != ResourceID::Null)
|
|
{
|
|
Effect *effect = CreateLoopingEffect(trail_id, this, true);
|
|
if(effect)
|
|
{
|
|
Check_Object(effect);
|
|
effect->BecomeInteresting (true);
|
|
effect->videoStatus = true;
|
|
effect->executionState->RequestState(Effect::ExecutionStateEngine::RunningState);
|
|
currentTrailEffect.Add(effect);
|
|
}
|
|
}
|
|
currentTrailID = trail_id;
|
|
}
|
|
}
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
//
|
|
void
|
|
Vehicle::RemoveFromExecution()
|
|
{
|
|
Check_Object(this);
|
|
|
|
if(currentTrailEffect.GetCurrent())
|
|
{
|
|
currentTrailEffect.GetCurrent()->executionState->RequestState(
|
|
Effect::ExecutionStateEngine::StoppingState);
|
|
currentTrailEffect.Remove();
|
|
}
|
|
BaseClass::RemoveFromExecution();
|
|
}
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
//
|
|
void
|
|
Vehicle::ProcessSelfDestruct()
|
|
{
|
|
Check_Object(this);
|
|
|
|
if (!IsDestroyed())
|
|
{
|
|
m_weaponShotMeLast = -50;
|
|
ReactToDestruction(InternalDamageObject::DestructionDamageMode, 0);
|
|
// Remove our shadow
|
|
if (shadow)
|
|
{
|
|
Check_Object(shadow);
|
|
delete shadow;
|
|
shadow = NULL;
|
|
}
|
|
}
|
|
}
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
//
|
|
void
|
|
Vehicle::SelfDestruct()
|
|
{
|
|
Check_Object(this);
|
|
|
|
m_whoShotMeLast = GetReplicatorID();
|
|
|
|
if (!IsDestroyed() && executionState->GetState() != Entity::ExecutionStateEngine::NeverExecuteState)
|
|
{
|
|
m_needSelfDestruct = 1;
|
|
}
|
|
}
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
//
|
|
void
|
|
Vehicle::TestInstance() const
|
|
{
|
|
Verify(IsDerivedFrom(DefaultData));
|
|
}
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
//
|
|
Stuff::Point3D&
|
|
Vehicle::EstimateFuturePosition(
|
|
Stuff::Point3D *new_position,
|
|
Stuff::Scalar seconds,
|
|
bool consider_terrain
|
|
)
|
|
{
|
|
Check_Object(this);
|
|
Check_Pointer(new_position);
|
|
|
|
//
|
|
//-------------------------------------------------
|
|
// If we aren't moving, return our current position
|
|
//-------------------------------------------------
|
|
//
|
|
*new_position = GetLocalToWorld();
|
|
if ((Small_Enough(currentSpeedMPS)) || (IsDestroyed() == true))
|
|
return *new_position;
|
|
|
|
//
|
|
//------------------------------------------------------------------------
|
|
// Figure out our height based on current position. If we are supposed to
|
|
// consider the terrain, snap our position to the terrain
|
|
//------------------------------------------------------------------------
|
|
//
|
|
Stuff::Scalar old_height = new_position->y;
|
|
if (consider_terrain)
|
|
{
|
|
Stuff::Scalar h = GetHeightAtPoint(*new_position, this);
|
|
if (h != -100000.0f)
|
|
old_height = h;
|
|
}
|
|
|
|
//
|
|
//--------------------------
|
|
// Project our forward speed
|
|
//--------------------------
|
|
//
|
|
Stuff::UnitVector3D local_forward;
|
|
GetLocalToWorld().GetLocalForwardInWorld(&local_forward);
|
|
new_position->AddScaled(*new_position, local_forward, seconds * currentSpeedMPS);
|
|
|
|
//
|
|
//-------------------------------------------------------------
|
|
// Assume that we swerve left at 1 mps at full stick deflection
|
|
//
|
|
// NOTE: This looks like doo-doo too
|
|
//-------------------------------------------------------------
|
|
//
|
|
Stuff::UnitVector3D local_left;
|
|
GetLocalToWorld().GetLocalLeftInWorld(&local_left);
|
|
new_position->AddScaled(*new_position, local_left, seconds * yawDemand);
|
|
|
|
//
|
|
//--------------------------------------------------------------------
|
|
// If we are considering terrain, see how our altitude would have been
|
|
// affected
|
|
//--------------------------------------------------------------------
|
|
//
|
|
if (consider_terrain)
|
|
{
|
|
Stuff::Scalar new_height = GetHeightAtPoint(*new_position, this);
|
|
if (new_height != -100000.0f)
|
|
new_position->y += new_height - old_height;
|
|
}
|
|
|
|
return *new_position;
|
|
}
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
//
|
|
void Vehicle::ClearLancemate(void)
|
|
{
|
|
if (MWGUIManager::GetInstance ())
|
|
{
|
|
HUDComm *comm;
|
|
HUDComponent *hud;
|
|
hud = MWGUIManager::GetInstance ()->Component (MWGUIManager::HUD_COMM);
|
|
Verify (hud);
|
|
comm = Cast_Object (HUDComm *,hud);
|
|
comm->DeselectLancemate ();
|
|
}
|
|
}
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
//
|
|
|
|
void Vehicle::ProcessCommand(int command)
|
|
{
|
|
|
|
MWPlayer *player;
|
|
|
|
MWApplication *app = MWApplication::GetInstance();
|
|
|
|
if (app->networkingFlag)
|
|
player = Cast_Object(MWPlayer *, app->servedConnectionData[GetReplicatorID().connectionID].clientPlayer);
|
|
else
|
|
player = Cast_Object(MWPlayer *, MWPlayer::GetInstance());
|
|
|
|
if (app->networkingFlag)
|
|
{
|
|
switch(command)
|
|
{
|
|
case VehicleCommand::LancemateAttackMyTargetCommand:
|
|
case VehicleCommand::LancemateDefendMyTargetCommand:
|
|
case VehicleCommand::LancemateFormOnMeCommand:
|
|
case VehicleCommand::LancemateHoldFireCommand:
|
|
case VehicleCommand::LancemateGoToMyNavPointCommand:
|
|
case VehicleCommand::LancemateStopCommand:
|
|
case VehicleCommand::LancemateShutDownCommand:
|
|
case VehicleCommand::LancemateAttackNearestCommand:
|
|
case VehicleCommand::LancemateRepairCommand:
|
|
return;
|
|
}
|
|
}
|
|
|
|
switch(command)
|
|
{
|
|
case VehicleCommand::JumpCommand:
|
|
JumpRequest();
|
|
break;
|
|
case VehicleCommand::StopJumpCommand:
|
|
StopJumpRequest();
|
|
break;
|
|
case VehicleCommand::PowerDownCommand:
|
|
if (vehicleShutDown)
|
|
ShutDownRequest(false);
|
|
else
|
|
ShutDownRequest(true);
|
|
break;
|
|
case VehicleCommand::CrouchCommand:
|
|
CrouchRequest();
|
|
break;
|
|
case VehicleCommand::GetUpCommand:
|
|
GetUpRequestRequest();
|
|
break;
|
|
case VehicleCommand::PressFlushCommand:
|
|
SetCooling(1);
|
|
break;
|
|
case VehicleCommand::ReleaseFlushCommand:
|
|
SetCooling(0);
|
|
break;
|
|
case VehicleCommand::SelfDestructCommand:
|
|
SelfDestruct();
|
|
break;
|
|
case VehicleCommand::EjectCommand:
|
|
Eject ();
|
|
break;
|
|
case VehicleCommand::SetTargetCommand:
|
|
break;
|
|
case VehicleCommand::ToggleSearchLightCommand:
|
|
ToggleSearchLight();
|
|
break;
|
|
case VehicleCommand::LancemateAttackMyTargetCommand:
|
|
if (player->m_LancemateCommandProxy.GetMode() == MWPlayer::LancemateCommandProxy::INVALID)
|
|
{
|
|
if (player->m_LancemateCommandProxy.SetMode(MWPlayer::LancemateCommandProxy::LANCEMATE_1) == true)
|
|
{
|
|
if (MWGUIManager::GetInstance ())
|
|
{
|
|
HUDComm *comm;
|
|
HUDComponent *hud;
|
|
hud = MWGUIManager::GetInstance ()->Component (MWGUIManager::HUD_COMM);
|
|
Verify (hud);
|
|
comm = Cast_Object (HUDComm *,hud);
|
|
comm->SelectLancemate (1);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (player->m_LancemateCommandProxy.IssueCommand(this,MW4AI::LancemateCommands::LANCEMATE_ATTACKPLAYERSTARGET) == true)
|
|
{
|
|
ClearLancemate ();
|
|
}
|
|
}
|
|
break;
|
|
|
|
case VehicleCommand::LancemateDefendMyTargetCommand:
|
|
if (player->m_LancemateCommandProxy.GetMode() == MWPlayer::LancemateCommandProxy::INVALID)
|
|
{
|
|
if (player->m_LancemateCommandProxy.SetMode(MWPlayer::LancemateCommandProxy::LANCEMATE_2) == true)
|
|
{
|
|
if (MWGUIManager::GetInstance ())
|
|
{
|
|
HUDComm *comm;
|
|
HUDComponent *hud;
|
|
hud = MWGUIManager::GetInstance ()->Component (MWGUIManager::HUD_COMM);
|
|
Verify (hud);
|
|
comm = Cast_Object (HUDComm *,hud);
|
|
comm->SelectLancemate (2);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (player->m_LancemateCommandProxy.IssueCommand(this,MW4AI::LancemateCommands::LANCEMATE_DEFENDPLAYERSTARGET) == true)
|
|
{
|
|
ClearLancemate ();
|
|
}
|
|
}
|
|
break;
|
|
|
|
case VehicleCommand::LancemateFormOnMeCommand:
|
|
if (player->m_LancemateCommandProxy.GetMode() == MWPlayer::LancemateCommandProxy::INVALID)
|
|
{
|
|
if (player->m_LancemateCommandProxy.SetMode(MWPlayer::LancemateCommandProxy::LANCEMATE_3) == true)
|
|
{
|
|
if (MWGUIManager::GetInstance ())
|
|
{
|
|
HUDComm *comm;
|
|
HUDComponent *hud;
|
|
hud = MWGUIManager::GetInstance ()->Component (MWGUIManager::HUD_COMM);
|
|
Verify (hud);
|
|
comm = Cast_Object (HUDComm *,hud);
|
|
comm->SelectLancemate (3);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (player->m_LancemateCommandProxy.IssueCommand(this,MW4AI::LancemateCommands::LANCEMATE_FORMONME) == true)
|
|
{
|
|
ClearLancemate ();
|
|
}
|
|
}
|
|
break;
|
|
|
|
case VehicleCommand::LancemateHoldFireCommand:
|
|
if (player->m_LancemateCommandProxy.GetMode() == MWPlayer::LancemateCommandProxy::INVALID)
|
|
{
|
|
if (player->m_LancemateCommandProxy.SetMode(MWPlayer::LancemateCommandProxy::LANCEMATE_ALL) == true)
|
|
{
|
|
if (MWGUIManager::GetInstance ())
|
|
{
|
|
HUDComm *comm;
|
|
HUDComponent *hud;
|
|
hud = MWGUIManager::GetInstance ()->Component (MWGUIManager::HUD_COMM);
|
|
Verify (hud);
|
|
comm = Cast_Object (HUDComm *,hud);
|
|
comm->SelectAllLancemates ();
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (player->m_LancemateCommandProxy.IssueCommand(this,MW4AI::LancemateCommands::LANCEMATE_HOLDFIRE) == true)
|
|
{
|
|
ClearLancemate ();
|
|
}
|
|
}
|
|
break;
|
|
|
|
case VehicleCommand::LancemateGoToMyNavPointCommand:
|
|
if (player->m_LancemateCommandProxy.IssueCommand(this,MW4AI::LancemateCommands::LANCEMATE_GOTOMYNAVPOINT) == true)
|
|
{
|
|
ClearLancemate ();
|
|
}
|
|
break;
|
|
|
|
case VehicleCommand::LancemateStopCommand:
|
|
if (player->m_LancemateCommandProxy.IssueCommand(this,MW4AI::LancemateCommands::LANCEMATE_STOP) == true)
|
|
{
|
|
ClearLancemate ();
|
|
}
|
|
break;
|
|
|
|
case VehicleCommand::LancemateShutDownCommand:
|
|
if (player->m_LancemateCommandProxy.IssueCommand(this,MW4AI::LancemateCommands::LANCEMATE_SHUTDOWN) == true)
|
|
{
|
|
ClearLancemate ();
|
|
}
|
|
break;
|
|
|
|
case VehicleCommand::LancemateAttackNearestCommand:
|
|
if (player->m_LancemateCommandProxy.IssueCommand(this,MW4AI::LancemateCommands::LANCEMATE_ATTACKNEARESTTHREAT) == true)
|
|
{
|
|
ClearLancemate ();
|
|
}
|
|
break;
|
|
|
|
case VehicleCommand::LancemateRepairCommand:
|
|
if (player->m_LancemateCommandProxy.IssueCommand(this,MW4AI::LancemateCommands::LANCEMATE_REPAIRATNEARESTREPAIRBAY) == true)
|
|
{
|
|
ClearLancemate ();
|
|
}
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
Scalar Vehicle::GetMaxSpeed()
|
|
{
|
|
Check_Object(this);
|
|
if(GetEngine())
|
|
return GetEngine()->GetMaxSpeed();
|
|
else
|
|
{
|
|
const GameModel *model = GetGameModel();
|
|
return model->maxSpeed;
|
|
}
|
|
}
|
|
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
//
|
|
|
|
void Vehicle::GetNetworkPosition(Stuff::Point3D ¤t_position, Stuff::YawPitchRoll ¤t_rotation)
|
|
{
|
|
current_position = GetLocalToWorld();
|
|
current_rotation = GetLocalToWorld();
|
|
}
|
|
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
//
|
|
|
|
void Vehicle::GetDeadReckonedNetworkPosition(Stuff::Point3D ¤t_position, Stuff::YawPitchRoll ¤t_rotation)
|
|
{
|
|
|
|
current_position = GetLocalToWorld();
|
|
current_rotation = GetLocalToWorld();
|
|
|
|
current_position += correctionPosition;
|
|
current_rotation.yaw += correctionAngle.yaw;
|
|
current_rotation.pitch += correctionAngle.pitch;
|
|
current_rotation.roll += correctionAngle.roll;
|
|
|
|
}
|
|
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
//
|
|
|
|
void Vehicle::ClearNetworkPosition()
|
|
{
|
|
correctionPosition = Point3D::Identity;
|
|
correctionAngle = YawPitchRoll::Identity;
|
|
|
|
rotationCorrectionTime = 0.0f;
|
|
positionCorrectionTime = 0.0f;
|
|
|
|
correctingPosition = false;
|
|
correctingRotation = false;
|
|
|
|
}
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
//
|
|
|
|
void Vehicle::SetNetworkCorretionPosition(Stuff::Point3D new_position, Stuff::YawPitchRoll new_rotation, Point3D velocity, Scalar vel_request, Scalar latency, Scalar update_rate)
|
|
{
|
|
|
|
YawPitchRoll original_rotation;
|
|
Point3D original_translation;
|
|
original_rotation = GetLocalToWorld();
|
|
original_translation = GetLocalToWorld();
|
|
|
|
new_rotation.Normalize();
|
|
original_rotation.Normalize();
|
|
correctionAngle.yaw.angle = new_rotation.yaw.angle - original_rotation.yaw.angle;
|
|
correctionAngle.pitch.angle = new_rotation.pitch.angle - original_rotation.pitch.angle;
|
|
correctionAngle.roll.angle = new_rotation.roll.angle - original_rotation.roll.angle;
|
|
correctionAngle.Normalize();
|
|
|
|
correctionPosition.Subtract(new_position, original_translation);
|
|
|
|
|
|
Scalar rotation_length_squared = Abs(correctionAngle.yaw.angle*correctionAngle.yaw.angle + correctionAngle.pitch.angle*correctionAngle.pitch.angle + correctionAngle.roll.angle*correctionAngle.roll.angle);
|
|
|
|
Scalar correction_scale = rotation_length_squared / (15.0f*Degrees_Per_Radian);
|
|
Clamp(correction_scale, 0.0f, 1.0f);
|
|
rotationCorrectionTime = Lerp( 0.1f, 0.5f, correction_scale);
|
|
|
|
correction_scale = correctionPosition.GetLengthSquared() / 25.0f;
|
|
Clamp(correction_scale, 0.0f, 1.0f);
|
|
positionCorrectionTime = Lerp( 0.1f, 0.5f, correction_scale);
|
|
|
|
|
|
correctingPosition = true;
|
|
correctingRotation = true;
|
|
|
|
}
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
//
|
|
|
|
bool Vehicle::GetNetworkAdjustment(Stuff::Scalar time_slice, Point3D &adjustment_position, Stuff::YawPitchRoll &adjustment_rotation)
|
|
{
|
|
|
|
adjustment_position = Point3D::Identity;
|
|
adjustment_rotation = YawPitchRoll::Identity;
|
|
|
|
|
|
|
|
if (correctingPosition)
|
|
{
|
|
|
|
// if we have an amount of time to correct over
|
|
if (positionCorrectionTime > 0.0f)
|
|
{
|
|
// else incrementaly add in some of that position.
|
|
Stuff::Scalar lerp_value = positionCorrectionTime / time_slice;
|
|
Max_Clamp(lerp_value, 1.0f);
|
|
|
|
adjustment_position.Lerp(Point3D::Identity, correctionPosition, lerp_value);
|
|
|
|
correctionPosition -= adjustment_position;
|
|
positionCorrectionTime -= time_slice;
|
|
|
|
}
|
|
else
|
|
{
|
|
// else we are done
|
|
adjustment_position = correctionPosition;
|
|
correctionPosition = Point3D::Identity;
|
|
positionCorrectionTime = 0.0f;
|
|
correctingPosition = false;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
if (correctingRotation)
|
|
{
|
|
if (rotationCorrectionTime > 0.0f)
|
|
{
|
|
|
|
Stuff::Scalar lerp_value = rotationCorrectionTime / time_slice;
|
|
Max_Clamp(lerp_value, 1.0f);
|
|
|
|
|
|
adjustment_rotation.Lerp(YawPitchRoll::Identity, correctionAngle, lerp_value);
|
|
|
|
correctionAngle.yaw.angle -= adjustment_rotation.yaw.angle;
|
|
correctionAngle.pitch -= adjustment_rotation.pitch.angle;
|
|
correctionAngle.roll.angle -= adjustment_rotation.roll.angle;
|
|
rotationCorrectionTime -= time_slice;
|
|
|
|
}
|
|
else
|
|
{
|
|
// else we are done
|
|
adjustment_rotation = correctionAngle;
|
|
correctionAngle = YawPitchRoll::Identity;
|
|
rotationCorrectionTime = 0.0f;
|
|
correctingRotation = false;
|
|
}
|
|
}
|
|
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
//
|
|
void Vehicle::ReactToDestruction(int damage_mode, int damage_type)
|
|
{
|
|
switch(damage_mode)
|
|
{
|
|
case InternalDamageObject::DestructionDamageMode:
|
|
SetDead ();
|
|
break;
|
|
}
|
|
BaseClass::ReactToDestruction (damage_mode,damage_type);
|
|
}
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
//
|
|
void
|
|
Vehicle::AddStatsToString(std::string& s)
|
|
{
|
|
if (GetTeam() != MWApplication::No_Team)
|
|
{
|
|
s += "Team: ";
|
|
s += IntToString(GetTeam());
|
|
s += " ";
|
|
}
|
|
|
|
MWObject::AddStatsToString(s);
|
|
}
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
//
|
|
void
|
|
Vehicle::AddToSensorCellMap()
|
|
{
|
|
SensorCellMap* sensor_cell_map = GetSensorCellMap();
|
|
|
|
if (sensor_cell_map != 0)
|
|
{
|
|
sensor_cell_map->Add(this,m_SensorCellMapX,m_SensorCellMapZ);
|
|
}
|
|
}
|
|
|
|
void
|
|
Vehicle::RemoveFromSensorCellMap()
|
|
{
|
|
if (m_SensorCellMapX < 0)
|
|
{
|
|
return;
|
|
}
|
|
|
|
SensorCellMap* sensor_cell_map = GetSensorCellMap();
|
|
|
|
if (sensor_cell_map != 0)
|
|
{
|
|
sensor_cell_map->Remove(this,m_SensorCellMapX,m_SensorCellMapZ);
|
|
}
|
|
}
|
|
|
|
SensorCellMap*
|
|
Vehicle::GetSensorCellMap()
|
|
{
|
|
if (MWMission::GetInstance() == 0)
|
|
{
|
|
return(0);
|
|
}
|
|
|
|
MWMission* mwmission = Cast_Object(MWMission*,MWMission::GetInstance());
|
|
return (&mwmission->m_VehicleAndTurretCellMap);
|
|
}
|
|
|
|
void
|
|
Vehicle::UpdateSensorCellMapPosition()
|
|
{
|
|
#ifdef NEW_SENSOR_TIMING
|
|
__int64 start_time = GetCycles();
|
|
#endif
|
|
|
|
if (m_LastSensorCellPositionUpdate + sensor_map_update_frequency > gos_GetElapsedTime())
|
|
{
|
|
#ifdef NEW_SENSOR_TIMING
|
|
tVehicleTime += GetCycles() - start_time;
|
|
#endif
|
|
return;
|
|
}
|
|
|
|
m_LastSensorCellPositionUpdate = gos_GetElapsedTime();
|
|
|
|
SensorCellMap* sensor_cell_map = GetSensorCellMap();
|
|
|
|
if (sensor_cell_map == 0)
|
|
{
|
|
#ifdef NEW_SENSOR_TIMING
|
|
tVehicleTime += GetCycles() - start_time;
|
|
#endif
|
|
return;
|
|
}
|
|
|
|
int x,z;
|
|
sensor_cell_map->GetCellPos(x,z,GetLocalToWorld());
|
|
|
|
if ((x != m_SensorCellMapX) ||
|
|
(z != m_SensorCellMapZ))
|
|
{
|
|
RemoveFromSensorCellMap();
|
|
|
|
m_SensorCellMapX = x;
|
|
m_SensorCellMapZ = z;
|
|
|
|
AddToSensorCellMap();
|
|
}
|
|
|
|
#ifdef NEW_SENSOR_TIMING
|
|
tVehicleTime += GetCycles() - start_time;
|
|
#endif
|
|
}
|
|
|
|
|
|
void MechWarrior4::VehicleSecurityCheckStop()
|
|
{
|
|
_asm
|
|
{
|
|
nop
|
|
}
|
|
}
|