Files
firestorm/Gameleap/code/mw4/Code/MW4/boat.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

460 lines
13 KiB
C++

#include "MW4Headers.hpp"
#include "Boat.hpp"
#include "gameinfo.hpp"
#include <AI.hpp>
#include <moverai.hpp>
#include <Adept\CollisionGrid.hpp>
#include <Adept\EntityManager.hpp>
//#############################################################################
//############################### Boat ##############################
//#############################################################################
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
Boat::ClassData*
Boat::DefaultData = NULL;
DWORD MechWarrior4::Executed_Boat_Count = 0;
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void Boat::InitializeClass()
{
Check_Object(MWMover::ExecutionStateEngine::DefaultData);
Verify(!DefaultData);
DefaultData =
new ClassData(
BoatClassID,
"MechWarrior4::Boat",
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);
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void Boat::TerminateClass()
{
Unregister_Object(DefaultData);
delete DefaultData;
DefaultData = NULL;
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
Boat* Boat::Make(CreateMessage *message,ReplicatorID *base_id)
{
Check_Object(message);
gos_PushCurrentHeap(Heap);
Boat *new_entity = new
Boat(DefaultData, message, base_id, NULL);
gos_PopCurrentHeap();
Check_Object(new_entity);
Check_Object(EntityManager::GetInstance());
EntityManager::GetInstance()->AddMover(new_entity);
return new_entity;
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
Boat::Boat(ClassData *class_data,CreateMessage *message,ReplicatorID *base_id,ElementRenderer::Element *element):
Vehicle(class_data, message, base_id, element)
{
Check_Pointer(this);
Check_Object(message);
HookUpSubsystems();
CommonCreation(message);
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
Boat::~Boat()
{
Check_Object(EntityManager::GetInstance());
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void Boat::Reuse(
const CreateMessage *message,
ReplicatorID *base_id
)
{
Check_Object(this);
Check_Object(message);
STOP(("Not implemented"));
BaseClass::Reuse(message, base_id);
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void Boat::CommonCreation(CreateMessage *message)
{
Check_Object(this);
Check_Object(message);
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void Boat::Respawn(Entity::CreateMessage *message)
{
Check_Object(this);
Check_Object(message);
BaseClass::Respawn(message);
CreateMessage *vehicle_message = Cast_Pointer(CreateMessage *, message);
CommonCreation(vehicle_message);
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void Boat::TurnOn (void)
{
lastParameterization = gos_GetElapsedTime ();
executionState->RequestState(ExecutionStateEngine::DrivingMotionState);
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void Boat::PreCollisionExecute(Time till)
{
Check_Object(this);
PRECOLLISION_LOGIC("Boat");
#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
//
//-------------------------------------------------------------
// Let the base class deal with setting up the motion variables
//-------------------------------------------------------------
//
BaseClass::PreCollisionExecute(till);
Set_Statistic(Executed_Boat_Count, Executed_Boat_Count+1);
//
//----------------------------------------------------------------------
// 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
//-------------------------------------------------------------------------
//
Scalar time_slice = GetTimeSlice(till);
switch (pre_state)
{
//
//----------------------------------------------------
// Always execute state is for the normal driving code
//----------------------------------------------------
//
case ExecutionStateEngine::DrivingMotionState:
ComputeForwardSpeed(time_slice);
UpdateVehiclePosition(time_slice);
break;
case ExecutionStateEngine::AIMotionState:
Stuff::Scalar maxSpeedMod=1.0f;
switch (materialHit)
{
case WaterMaterial:
case ConcreteMaterial:
case SnowMaterial:
case FakeShallowWaterMaterial:
maxSpeedMod = 0.0f;
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();
}
currentSpeedMPS = 0;
speedDemandKPH = 0.0f;
currentSpeedKPH = 0.0f;
}
break;
}
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void Boat::TestInstance() const
{
Verify(IsDerivedFrom(DefaultData));
}
void Boat::ComputeForwardSpeed(Stuff::Scalar time_slice)
{
Check_Object(this);
const GameModel *model = GetGameModel();
Check_Object(model);
Stuff::Scalar maxSpeedMod;
//
//----------------------------------------------------
// 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:
case ConcreteMaterial:
case SnowMaterial:
case FakeShallowWaterMaterial:
maxSpeedMod = 0.0f;
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;
return;
}
//
//--------------------------------------------------------------------
// If we are not going backwards, find out what speed we want after we
// consider braking
//--------------------------------------------------------------------
//
if (currentSpeedMPS >= 0.0f)
{
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);
}
}
else
{
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;
//
//----------------------
// Compute pitching and rolling in the water
//----------------------
//
// x is pitch
// y is yaw
// z is roll
YawPitchRoll ypr (GetLocalToWorld ());
localSpaceAcceleration.angularMotion.x = -ypr.pitch;
localSpaceAcceleration.angularMotion.y = 0;
localSpaceAcceleration.angularMotion.z = -ypr.roll;
// localSpaceAcceleration.angularMotion.x += ((Stuff::Random::GetFraction ()*Pi)-Pi_Over_2);
// localSpaceAcceleration.angularMotion.z += ((Stuff::Random::GetFraction ()*Pi)-Pi_Over_2);
}
void Boat::UpdateVehiclePosition(Scalar time_slice)
{
//
//---------------------------------------------------------------------
// Apply velocity/acceleration to position and acceleration to velocity
//---------------------------------------------------------------------
//
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);
world_translation.AddScaled(world_translation, world_delta, time_slice);
localSpaceVelocity.linearMotion.y += localSpaceAcceleration.linearMotion.y*time_slice;
localSpaceVelocity.angularMotion.x += localSpaceAcceleration.angularMotion.x * time_slice;
localSpaceVelocity.angularMotion.y += localSpaceAcceleration.angularMotion.y * time_slice;
localSpaceVelocity.angularMotion.z += localSpaceAcceleration.angularMotion.z * time_slice;
//
//--------------------------------------
// 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 += localSpaceVelocity.angularMotion.x * time_slice;
new_rotation.roll += localSpaceVelocity.angularMotion.z * time_slice;
//
//----------------------------------------------
// Cast a ray down to see what we are driving on
//----------------------------------------------
//
Stuff::Line3D line;
line.m_length = 40.001f + m_WaterDelta;
line.m_direction = Vector3D::Down;
line.m_origin = world_translation;
line.m_origin.y += 20.0f;
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
);
new_local_to_world.BuildTranslation(world_translation);
}
//
//--------------------------------------------------
// Otherwise, adapt the vehicle to the ground normal
//--------------------------------------------------
//
else
{
materialHit = query.m_material;
line.FindEnd(&world_translation);
world_translation.y += m_WaterDelta;
new_local_to_world.BuildTranslation(world_translation);
new_local_to_world.AlignLocalAxisToWorldVector(Vector3D::Up, 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);
}