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.
460 lines
13 KiB
C++
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);
|
|
}
|
|
|