Files
firestorm/Gameleap/code/mw4/Code/MW4/Vehicle.cpp
T
Cyd 2b8ca921cb Initial full mirror of c:\VWE (source + assets + toolchain + outputs) via Git LFS
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.
2026-06-24 21:28:16 -05:00

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 &current_position, Stuff::YawPitchRoll &current_rotation)
{
current_position = GetLocalToWorld();
current_rotation = GetLocalToWorld();
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void Vehicle::GetDeadReckonedNetworkPosition(Stuff::Point3D &current_position, Stuff::YawPitchRoll &current_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
}
}