Imports the current Win32 source for the pod-racing game 'Red Planet', built on the MUNGA engine and its L4 (Win32/DirectX) platform layer: - MUNGA / MUNGA_L4: cross-platform engine core and Win32 backend - RP / RP_L4: Red Planet game logic and Win32 application - DivLoader, Setup1: asset loader and installer project - lib, MUNGA_L4/openal, MUNGA_L4/sos: third-party audio dependencies Removed stale Subversion metadata and added .gitignore/.gitattributes. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
3200 lines
75 KiB
C++
3200 lines
75 KiB
C++
#include "rp.h"
|
|
#pragma hdrstop
|
|
|
|
#include "vtv.h"
|
|
#include "vtvmppr.h"
|
|
#include "vtvpwr.h"
|
|
#include "weapsys.h"
|
|
#include "thruster.h"
|
|
#include "booster.h"
|
|
#include "chute.h"
|
|
#include "rpplayer.h"
|
|
#include "..\munga\door.h"
|
|
#include "..\munga\line.h"
|
|
#include "..\munga\random.h"
|
|
#include "..\munga\fileutil.h"
|
|
#include "..\munga\boxsolid.h"
|
|
#include "..\munga\app.h"
|
|
#include "..\munga\hostmgr.h"
|
|
#include "..\munga\namelist.h"
|
|
|
|
//#############################################################################
|
|
// Shared Data Support
|
|
//
|
|
Derivation* VTV::GetClassDerivations()
|
|
{ static Derivation classDerivations(JointedMover::GetClassDerivations(), "VTV");
|
|
return &classDerivations;
|
|
}
|
|
|
|
|
|
VTV::SharedData
|
|
VTV::DefaultData(
|
|
VTV::GetClassDerivations(),
|
|
VTV::MessageHandlers,
|
|
VTV::GetAttributeIndex(),
|
|
VTV::StateCount,
|
|
(Entity::MakeHandler)VTV::Make
|
|
);
|
|
|
|
//#############################################################################
|
|
// Message Support
|
|
//
|
|
const VTV::HandlerEntry
|
|
VTV::MessageHandlerEntries[]=
|
|
{
|
|
MESSAGE_ENTRY(VTV, TakeDamage),
|
|
MESSAGE_ENTRY(VTV, Kavorkian),
|
|
MESSAGE_ENTRY(VTV, MarkSpot),
|
|
MESSAGE_ENTRY(VTV, RestoreToSpot),
|
|
MESSAGE_ENTRY(VTV, ZoomIn),
|
|
MESSAGE_ENTRY(VTV, ZoomOut),
|
|
MESSAGE_ENTRY(VTV, PlayerLink)
|
|
};
|
|
|
|
VTV::MessageHandlerSet
|
|
VTV::MessageHandlers(
|
|
ELEMENTS(VTV::MessageHandlerEntries),
|
|
VTV::MessageHandlerEntries,
|
|
JointedMover::GetMessageHandlers()
|
|
);
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
//
|
|
void
|
|
VTV::PlayerLinkMessageHandler(PlayerLinkMessage *message)
|
|
{
|
|
Entity::PlayerLinkMessageHandler(message);
|
|
RPPlayer
|
|
*rp_player = Cast_Object(RPPlayer*, playerLink);
|
|
rp_player->SetPlayerVehicle(this);
|
|
|
|
//
|
|
//----------------------------------------------------------------------
|
|
// If our player is a runner, add two boosters to each booster subsystem
|
|
//----------------------------------------------------------------------
|
|
//
|
|
switch (rp_player->playerType)
|
|
{
|
|
case RPPlayer::RunnerPlayerType:
|
|
{
|
|
Check_Pointer(subsystemArray);
|
|
for (int i=0; i<subsystemCount; ++i)
|
|
{
|
|
if (subsystemArray[i])
|
|
{
|
|
Check(subsystemArray[i]);
|
|
if (subsystemArray[i]->GetClassID() == BoosterClassID)
|
|
{
|
|
Booster *booster = (Booster*)subsystemArray[i];
|
|
booster->maxBoosters += 2;
|
|
booster->boostersRemaining += 2;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
goto Is_A_Scorer;
|
|
|
|
case RPPlayer::GeneralPlayerType:
|
|
case RPPlayer::CrusherPlayerType:
|
|
case RPPlayer::BlockerPlayerType:
|
|
Is_A_Scorer:
|
|
rp_player->SetScoringPlayerFlag();
|
|
break;
|
|
}
|
|
Check_Fpu();
|
|
}
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
//
|
|
void
|
|
VTV::TakeDamageMessageHandler(TakeDamageMessage *message)
|
|
{
|
|
Check(this);
|
|
Check(message);
|
|
|
|
//
|
|
//-------------------------------------------------
|
|
// Check If the damage was caused by another Player
|
|
//-------------------------------------------------
|
|
//
|
|
Check(application);
|
|
HostManager
|
|
*host = application->GetHostManager();
|
|
Check(host);
|
|
Entity
|
|
*offensive_entity = Cast_Object(
|
|
Entity*,
|
|
host->GetEntityPointer(message->inflictingEntity)
|
|
);
|
|
|
|
//
|
|
//------------------------------------
|
|
// make sure we can enter something...
|
|
//------------------------------------
|
|
//
|
|
if (offensive_entity)
|
|
{
|
|
Check(offensive_entity);
|
|
RPPlayer
|
|
*offensive_player = (RPPlayer*)offensive_entity->GetPlayerLink();
|
|
|
|
if (offensive_player)
|
|
{
|
|
//
|
|
//---------------------------------------------------------------
|
|
// Notify this player that damaged was caused by offensive_player
|
|
//---------------------------------------------------------------
|
|
//
|
|
Check(offensive_player);
|
|
RPPlayer::OffensivePlayer
|
|
*shooter = new RPPlayer::OffensivePlayer(
|
|
offensive_player,
|
|
message->damageData.damageType,
|
|
Now()
|
|
);
|
|
Register_Object(shooter);
|
|
RPPlayer
|
|
*our_player = (RPPlayer*)GetPlayerLink();
|
|
Check(our_player);
|
|
our_player->AddOffensivePlayer(*shooter);
|
|
}
|
|
}
|
|
|
|
//
|
|
//--------------------------------------------------------------------
|
|
// We we have received a collision packet, we should calculate our own
|
|
// damage loss
|
|
//--------------------------------------------------------------------
|
|
//
|
|
switch (message->damageData.damageType)
|
|
{
|
|
case Damage::CollisionDamageType:
|
|
{
|
|
//
|
|
//-----------------------------------------------------------
|
|
// Find out who hit us, and get our velocity relative to them
|
|
//-----------------------------------------------------------
|
|
//
|
|
Check(application);
|
|
HostManager
|
|
*host = application->GetHostManager();
|
|
Check(host);
|
|
Mover
|
|
*mover = (Mover*)host->GetEntityPointer(message->inflictingEntity);
|
|
Verify(mover->IsDerivedFrom(Mover::GetClassDerivations()));
|
|
Vector3D
|
|
v;
|
|
v.Subtract(worldLinearVelocity, mover->worldLinearVelocity);
|
|
|
|
//
|
|
//-----------------------------------------------------------------------
|
|
// The normal to bounce against is passed via the damage force, so see if
|
|
// it opposes our motion. If not, we can ignore this message
|
|
//-----------------------------------------------------------------------
|
|
//
|
|
if (v*message->damageData.surfaceNormal <= 0.0f)
|
|
{
|
|
Check_Fpu();
|
|
return;
|
|
}
|
|
|
|
//
|
|
//----------------------------------------------------------------------
|
|
// Otherwise, do a dynamic bounce, and apply the damage to ourselves.
|
|
// We save the original positions of the vehicles because we do not know
|
|
// what time slice to use
|
|
//----------------------------------------------------------------------
|
|
//
|
|
Point3D
|
|
old_us = localOrigin.linearPosition,
|
|
old_them = mover->localOrigin.linearPosition;
|
|
Scalar
|
|
elasticity = elasticityCoefficient;
|
|
|
|
message->damageData.surfaceNormal.x =
|
|
-message->damageData.surfaceNormal.x;
|
|
message->damageData.surfaceNormal.y =
|
|
-message->damageData.surfaceNormal.y;
|
|
message->damageData.surfaceNormal.z =
|
|
-message->damageData.surfaceNormal.z;
|
|
|
|
message->damageData.damageAmount =
|
|
DynamicBounce(
|
|
mover,
|
|
0.1f,
|
|
1.0f,
|
|
message->damageData.surfaceNormal,
|
|
&elasticity
|
|
);
|
|
localOrigin.linearPosition = old_us;
|
|
mover->localOrigin.linearPosition = old_them;
|
|
break;
|
|
}
|
|
|
|
default:
|
|
{
|
|
message->damageData.damageForce /= moverMass;
|
|
Check_Fpu();
|
|
worldLinearVelocity += message->damageData.damageForce;
|
|
break;
|
|
}
|
|
}
|
|
Entity::TakeDamageMessageHandler(message);
|
|
Check_Fpu();
|
|
}
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
//
|
|
void
|
|
VTV::KavorkianMessageHandler(
|
|
ReceiverDataMessageOf<ControlsButton> *message
|
|
)
|
|
{
|
|
Check(this);
|
|
Check(message);
|
|
|
|
if (message->dataContents > 0)
|
|
{
|
|
Damage
|
|
damage;
|
|
|
|
Check_Pointer(damageZones);
|
|
Check(*damageZones);
|
|
damage.damageType = Damage::ExplosiveDamageType;
|
|
damage.damageAmount =
|
|
1.0f / (*damageZones)->damageScale[Damage::ExplosiveDamageType];
|
|
Check_Fpu();
|
|
damage.damageForce = Vector3D::Identity;
|
|
(*damageZones)->TakeDamage(damage);
|
|
}
|
|
Check_Fpu();
|
|
}
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
//
|
|
void
|
|
VTV::MarkSpotMessageHandler(
|
|
ReceiverDataMessageOf<ControlsButton> *message
|
|
)
|
|
{
|
|
Check(this);
|
|
Check(message);
|
|
|
|
if (message->dataContents > 0)
|
|
{
|
|
savedOrigin = localOrigin;
|
|
}
|
|
Check_Fpu();
|
|
}
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
//
|
|
void
|
|
VTV::RestoreToSpotMessageHandler(
|
|
ReceiverDataMessageOf<ControlsButton> *message
|
|
)
|
|
{
|
|
Check(this);
|
|
Check(message);
|
|
|
|
if (message->dataContents > 0)
|
|
{
|
|
Reset(savedOrigin, VTV::RegularReset);
|
|
}
|
|
Check_Fpu();
|
|
}
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
//
|
|
void
|
|
VTV::ZoomInMessageHandler(
|
|
ReceiverDataMessageOf<ControlsButton> *message
|
|
)
|
|
{
|
|
Check(this);
|
|
Check(message);
|
|
|
|
if (message->dataContents > 0)
|
|
{
|
|
targetRangeExponent;
|
|
if (--targetRangeExponent < 0.0f)
|
|
{
|
|
targetRangeExponent = 0.0f;
|
|
}
|
|
}
|
|
Check_Fpu();
|
|
}
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
//
|
|
void
|
|
VTV::ZoomOutMessageHandler(
|
|
ReceiverDataMessageOf<ControlsButton> *message
|
|
)
|
|
{
|
|
Check(this);
|
|
Check(message);
|
|
|
|
if (message->dataContents > 0)
|
|
{
|
|
if (++targetRangeExponent > 5.0f)
|
|
{
|
|
targetRangeExponent = 5.0f;
|
|
}
|
|
}
|
|
Check_Fpu();
|
|
}
|
|
|
|
//#############################################################################
|
|
// Attribute Support
|
|
//
|
|
const VTV::IndexEntry
|
|
VTV::AttributePointers[]=
|
|
{
|
|
ATTRIBUTE_ENTRY(VTV, HeightAboveTerrain, heightAboveTerrain),
|
|
ATTRIBUTE_ENTRY(VTV, ThrusterPitch, thrusterPitch),
|
|
ATTRIBUTE_ENTRY(VTV, CollisionState, collisionState),
|
|
ATTRIBUTE_ENTRY(VTV, CollisionMaterialType, collisionMaterialType),
|
|
ATTRIBUTE_ENTRY(VTV, CollisionNormal, collisionNormal),
|
|
ATTRIBUTE_ENTRY(VTV, CollisionSpeed, collisionSpeed),
|
|
ATTRIBUTE_ENTRY(VTV, EyepointRotation, eyepointRotation),
|
|
ATTRIBUTE_ENTRY(VTV, BoosterOn, boosterOn),
|
|
ATTRIBUTE_ENTRY(VTV, BoosterScale, boosterScale),
|
|
ATTRIBUTE_ENTRY(VTV, BoosterSmokeDensity, boosterSmokeDensity),
|
|
ATTRIBUTE_ENTRY(VTV, ForwardVelocity, forwardVelocity),
|
|
ATTRIBUTE_ENTRY(VTV, CompassHeading, compassHeading),
|
|
ATTRIBUTE_ENTRY(VTV, TargetReticle, targetReticle),
|
|
ATTRIBUTE_ENTRY(VTV, NavigationRange, navigationRange),
|
|
ATTRIBUTE_ENTRY(VTV, NavigationLinearPosition,navigationLinearPosition),
|
|
ATTRIBUTE_ENTRY(VTV, NavigationAngularPosition,navigationAngularPosition),
|
|
ATTRIBUTE_ENTRY(VTV, ScoreState, scoreState),
|
|
ATTRIBUTE_ENTRY(VTV, ScoreType, scoreType),
|
|
ATTRIBUTE_ENTRY(VTV, HornBlast, hornBlast) // HACK - ECH 7/31/95 - For replicants
|
|
};
|
|
|
|
VTV::AttributeIndexSet& VTV::GetAttributeIndex()
|
|
{
|
|
static VTV::AttributeIndexSet attributeIndex(ELEMENTS(VTV::AttributePointers),
|
|
VTV::AttributePointers,
|
|
JointedMover::GetAttributeIndex()
|
|
);
|
|
return attributeIndex;
|
|
}
|
|
|
|
//#############################################################################
|
|
// Model support
|
|
//
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
//
|
|
void
|
|
VTV::SetMappingSubsystem(VTVControlsMapper *mapper)
|
|
{
|
|
Check(this);
|
|
Check(mapper);
|
|
|
|
Check_Pointer(subsystemArray);
|
|
if (subsystemArray[ControlsMapperSubsystem])
|
|
{
|
|
Unregister_Object(subsystemArray[ControlsMapperSubsystem]);
|
|
delete subsystemArray[ControlsMapperSubsystem];
|
|
}
|
|
subsystemArray[ControlsMapperSubsystem] = mapper;
|
|
Check_Fpu();
|
|
}
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
//
|
|
Logical
|
|
VTV::BoosterOn()
|
|
{
|
|
Check(this);
|
|
for(int ii=BasicSubsystemCount;ii<subsystemCount;ii++)
|
|
{
|
|
Subsystem
|
|
*subsystem = GetSubsystem(ii);
|
|
if (subsystem)
|
|
{
|
|
Check(subsystem);
|
|
if (subsystem->GetClassID() == BoosterClassID)
|
|
{
|
|
Booster
|
|
*booster = (Booster*) subsystem;
|
|
Check(booster);
|
|
if (booster->GetSimulationState() == Booster::BoosterBurning)
|
|
{
|
|
Check_Fpu();
|
|
return True;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
Check_Fpu();
|
|
return False;
|
|
}
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
//
|
|
void
|
|
VTV::MoveAndCollide(Scalar time_slice)
|
|
{
|
|
Check(this);
|
|
JointSubsystem
|
|
*joint_subsystem = GetJointSubsystem();
|
|
Check(joint_subsystem);
|
|
Verify(joint_subsystem->GetJointCount());
|
|
|
|
VTVControlsMapper
|
|
*mapper = Cast_Object(
|
|
VTVControlsMapper*,
|
|
GetSubsystem(VTV::ControlsMapperSubsystem)
|
|
);
|
|
Check(mapper);
|
|
|
|
//
|
|
//-------------------------------------------------------------------------
|
|
// Figure out the average height and thruster rotation. While doing this,
|
|
// calculate the differences in the corner forces so that we can figure out
|
|
// the roll and pitch accelerations due to terrain
|
|
//-------------------------------------------------------------------------
|
|
//
|
|
heightAboveTerrain = 0.0f;
|
|
thrusterPitch.rotationAmount = 0.0f;
|
|
Vector3D difference = Vector3D::Identity;
|
|
int valid_thruster=0;
|
|
|
|
for (int i=0; i<MAX_THRUSTERS; ++i)
|
|
{
|
|
Thruster
|
|
*thruster = (Thruster*) subsystemArray[Thruster1Subsystem + i];
|
|
if (thruster)
|
|
{
|
|
heightAboveTerrain += thruster->currentHeight;
|
|
switch (joint_subsystem->GetJoint(valid_thruster)->GetJointType())
|
|
{
|
|
|
|
case Joint::BallJointType:
|
|
thrusterPitch.rotationAmount +=
|
|
joint_subsystem->GetJoint(valid_thruster)->
|
|
GetEulerAngles().pitch;
|
|
break;
|
|
case Joint::HingeXJointType:
|
|
thrusterPitch.rotationAmount +=
|
|
joint_subsystem->GetJoint(valid_thruster)->
|
|
GetHinge().rotationAmount;
|
|
break;
|
|
|
|
}
|
|
++valid_thruster;
|
|
|
|
//
|
|
//----------------------------------------------------------------
|
|
// Generate the pseudoforce. Ignore the center engines of the VTV
|
|
//----------------------------------------------------------------
|
|
//
|
|
if ((i == 2 || i == 3) && joint_subsystem->GetJointCount() > 4)
|
|
{
|
|
continue;
|
|
}
|
|
if (thruster->thrusterOffset.x > 0.0f)
|
|
{
|
|
difference.z -= thruster->currentHeight;
|
|
}
|
|
else
|
|
{
|
|
difference.z += thruster->currentHeight;
|
|
}
|
|
if (thruster->thrusterOffset.z > 0.0f)
|
|
{
|
|
difference.x += thruster->currentHeight;
|
|
}
|
|
else
|
|
{
|
|
difference.x -= thruster->currentHeight;
|
|
}
|
|
}
|
|
}
|
|
heightAboveTerrain /= joint_subsystem->GetJointCount();
|
|
thrusterPitch.rotationAmount /= joint_subsystem->GetJointCount();
|
|
Check_Fpu();
|
|
|
|
//
|
|
//---------------------------------------------------
|
|
// If we are not burning, follow the terrain and turn
|
|
//---------------------------------------------------
|
|
//
|
|
if (GetSimulationState() != BurningState)
|
|
{
|
|
difference *= angularSpringFactor / (15.0f + 2.0f*heightAboveTerrain);
|
|
Check_Fpu();
|
|
|
|
//
|
|
//-------------------------------------------------------------------
|
|
// If this is a two-engine VTV, try to straighten out the pitch value
|
|
//-------------------------------------------------------------------
|
|
//
|
|
if (joint_subsystem->GetJointCount() == 2)
|
|
{
|
|
Scalar
|
|
len =
|
|
localToWorld(2,1)*localToWorld(2,1)
|
|
+ localToWorld(1,1)*localToWorld(1,1);
|
|
if (!Small_Enough(len))
|
|
{
|
|
difference.x = 3.0f*localToWorld(2,1) / Sqrt(len);
|
|
Check_Fpu();
|
|
}
|
|
difference.z *= 0.5f;
|
|
}
|
|
else
|
|
{
|
|
difference *= 0.25f;
|
|
}
|
|
if (groundEffectDomainSquared)
|
|
{
|
|
difference.z -= bankFactor * mapper->powerDemand.x;
|
|
localAcceleration.angularMotion += difference;
|
|
|
|
//
|
|
//-------------------------------------
|
|
// Stabilize the craft at high altitude
|
|
//-------------------------------------
|
|
//
|
|
if (heightAboveTerrain > 10.0f)
|
|
{
|
|
Vector3D
|
|
world_g = Vector3D::Identity,
|
|
local_g,
|
|
torque;
|
|
|
|
world_g.y = -GetEnvironment()->gravityConstant;
|
|
local_g.MultiplyByInverse(world_g, localToWorld);
|
|
world_g.y = -1.0f;
|
|
torque.Cross(world_g, local_g);
|
|
torque *= momentOfInertia;
|
|
localAcceleration.angularMotion += torque;
|
|
}
|
|
}
|
|
|
|
//
|
|
//---------------------------------------
|
|
// Turn the VTV as required by the mapper
|
|
//---------------------------------------
|
|
//
|
|
Vector3D
|
|
vel;
|
|
vel.Multiply(localVelocity.angularMotion, localToWorld);
|
|
Scalar
|
|
step = mapper->angularVelocityDemand.y - vel.y;
|
|
difference = Vector3D::Identity;
|
|
if (step > 0.0f)
|
|
{
|
|
if (maxAngularAcceleration.y * time_slice > step)
|
|
{
|
|
difference.y += step / time_slice;
|
|
Check_Fpu();
|
|
}
|
|
else
|
|
{
|
|
difference.y += maxAngularAcceleration.y;
|
|
}
|
|
}
|
|
else if (step < 0.0f)
|
|
{
|
|
if (maxAngularAcceleration.y * time_slice > -step)
|
|
{
|
|
difference.y += step / time_slice;
|
|
Check_Fpu();
|
|
}
|
|
else
|
|
{
|
|
difference.y -= maxAngularAcceleration.y;
|
|
}
|
|
}
|
|
|
|
vel.MultiplyByInverse(difference,localToWorld);
|
|
localAcceleration.angularMotion += vel;
|
|
}
|
|
|
|
//
|
|
//--------------------------------------------------------------------
|
|
// Apply the standard air resistance and gravity, then project the VTV
|
|
// motion through the time slice
|
|
//--------------------------------------------------------------------
|
|
//
|
|
ApplyAirResistanceAndGravity();
|
|
|
|
Vector3D
|
|
old_acceleration = worldLinearAcceleration;
|
|
|
|
worldLinearAcceleration.x *= ZIPPY;
|
|
worldLinearAcceleration.z *= ZIPPY;
|
|
|
|
Point3D
|
|
old_position(localOrigin.linearPosition);
|
|
ApplyWorldAccelerations(time_slice);
|
|
|
|
//
|
|
//-------------------------------------------------------------------------
|
|
// We have now moved the VTV. We should now update our collision volume to
|
|
// the new location and check it against the tree
|
|
//-------------------------------------------------------------------------
|
|
//
|
|
Vector3D
|
|
zippy_accel = worldLinearAcceleration,
|
|
impact_velocity = localVelocity.linearMotion;
|
|
|
|
worldLinearAcceleration = old_acceleration;
|
|
MoveCollisionVolume();
|
|
|
|
//
|
|
//-------------------------------------------
|
|
// If we are going fast enough, do a ray-cast
|
|
//-------------------------------------------
|
|
//
|
|
BoxedSolidCollisionList* collision_list = AllocateCollisionList();
|
|
Line line;
|
|
line = old_position;
|
|
|
|
Vector3D step;
|
|
step.Subtract(localOrigin.linearPosition, old_position);
|
|
if (step.LengthSquared() > SMALL)
|
|
{
|
|
line = step;
|
|
Scalar diameter = collisionTemplate->maxX - collisionTemplate->minX;
|
|
if (diameter < line.length)
|
|
{
|
|
CollideCenterOfMotion(&line, collision_list);
|
|
}
|
|
}
|
|
if (!collision_list->GetCollisionCount())
|
|
{
|
|
GetCurrentCollisions(collision_list);
|
|
}
|
|
|
|
//
|
|
//-------------------------------
|
|
// Process each of the collisions
|
|
//-------------------------------
|
|
//
|
|
collisionTemporaryState = NoCollisionState;
|
|
|
|
Damage
|
|
collision_damage;
|
|
|
|
ProcessCollisionList(
|
|
collision_list,
|
|
time_slice,
|
|
old_position,
|
|
&collision_damage
|
|
);
|
|
|
|
//
|
|
//-------------------------
|
|
// Set Collision attributes
|
|
//-------------------------
|
|
//
|
|
if (collision_damage.damageAmount > 0.0f)
|
|
{
|
|
BoxedSolid
|
|
*boxed_solid;
|
|
|
|
Check(&(*lastCollisionList)[0]);
|
|
boxed_solid = (*lastCollisionList)[0].GetTreeVolume();
|
|
Check(boxed_solid);
|
|
collisionMaterialType = boxed_solid->materialType;
|
|
|
|
collisionNormal.MultiplyByInverse(
|
|
collision_damage.surfaceNormal,
|
|
localToWorld
|
|
);
|
|
collisionSpeed = -(collisionNormal * impact_velocity);
|
|
|
|
if (GetSimulationState() != BurningState)
|
|
{
|
|
collisionState.SetState(collisionTemporaryState);
|
|
}
|
|
|
|
//
|
|
//---------------------------------------------------------------------
|
|
// Take the damage, allowing any slope less than 45 degrees relative to
|
|
// the keel to take one-tenth the damage
|
|
//---------------------------------------------------------------------
|
|
//
|
|
if (collisionNormal.y > 0.7f)
|
|
{
|
|
collision_damage.damageAmount *= bottomArmorScale;
|
|
}
|
|
|
|
Check_Pointer(damageZones);
|
|
Check(*damageZones);
|
|
|
|
//TODO: IMPORTANT COLLISION CODE HERE
|
|
|
|
(*damageZones)->TakeDamage(collision_damage);
|
|
|
|
//
|
|
//------------------------------------------------------------------
|
|
// If the VTV is burning, apply the world-space force vectors to the
|
|
// craft as torque. The spinning will start next frame
|
|
//------------------------------------------------------------------
|
|
//
|
|
if (GetSimulationState() == BurningState)
|
|
{
|
|
Vector3D
|
|
force,
|
|
torque;
|
|
Point3D
|
|
moment;
|
|
|
|
force.MultiplyByInverse(collision_damage.damageForce, localToWorld);
|
|
moment.MultiplyByInverse(
|
|
(Vector3D)collision_damage.impactPoint,
|
|
localToWorld
|
|
);
|
|
torque.Cross(moment, force);
|
|
torque *= momentOfInertia;
|
|
Clamp(torque.x, -20.0f, 20.0f);
|
|
Clamp(torque.y, -20.0f, 20.0f);
|
|
Clamp(torque.z, -20.0f, 20.0f);
|
|
localVelocity.angularMotion += torque;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
collisionState.SetState(NoCollisionState);
|
|
}
|
|
|
|
//
|
|
//------------------------------------------
|
|
// Check to see if we should blow up the VTV
|
|
//------------------------------------------
|
|
//
|
|
{
|
|
InterestManager
|
|
*interest_mgr = application->GetInterestManager();
|
|
Check(interest_mgr);
|
|
InterestZone
|
|
*zone = interest_mgr->GetInterestZone(interestZoneID);
|
|
Check(zone);
|
|
BoundingBoxTree
|
|
*tree = zone->GetExistanceRoot();
|
|
Check(tree);
|
|
Check_Pointer(damageZones);
|
|
Check(*damageZones);
|
|
insideWorld =
|
|
tree->FindBoundingBoxContaining(localOrigin.linearPosition) != NULL;
|
|
if (!insideWorld && (*damageZones)->damageLevel < 1.0f)
|
|
{
|
|
collision_damage.damageType = Damage::ExplosiveDamageType;
|
|
collision_damage.damageAmount =
|
|
1.0f / (*damageZones)->damageScale[Damage::ExplosiveDamageType];
|
|
Check_Fpu();
|
|
collision_damage.damageForce = Vector3D::Identity;
|
|
Check_Pointer(damageZones);
|
|
Check(*damageZones);
|
|
(*damageZones)->TakeDamage(collision_damage);
|
|
|
|
localVelocity.angularMotion.x += 8.0f * Random - 4.0f;
|
|
localVelocity.angularMotion.y += 8.0f * Random - 4.0f;
|
|
localVelocity.angularMotion.z += 8.0f * Random - 4.0f;
|
|
}
|
|
}
|
|
worldLinearAcceleration = zippy_accel;
|
|
if (collisionState.GetState() == RestState)
|
|
{
|
|
worldLinearAcceleration = Vector3D::Identity;
|
|
}
|
|
UpdateLocalMotion();
|
|
|
|
//
|
|
//-------------------------------------------------
|
|
// Update the stuff that the secondary screen wants
|
|
//-------------------------------------------------
|
|
//
|
|
compassHeading = localToWorld;
|
|
forwardVelocity = -3.6f * localVelocity.linearMotion.z;
|
|
if (targetRangeExponent > currentRangeExponent)
|
|
{
|
|
currentRangeExponent += time_slice;
|
|
if (currentRangeExponent > targetRangeExponent)
|
|
{
|
|
currentRangeExponent = targetRangeExponent;
|
|
}
|
|
navigationRange = 250.0f * pow(2.0f, currentRangeExponent);
|
|
}
|
|
else if (targetRangeExponent < currentRangeExponent)
|
|
{
|
|
currentRangeExponent -= time_slice;
|
|
if (currentRangeExponent < targetRangeExponent)
|
|
{
|
|
currentRangeExponent = targetRangeExponent;
|
|
}
|
|
navigationRange = 250.0f * pow(2.0f, currentRangeExponent);
|
|
}
|
|
Check_Fpu();
|
|
|
|
//
|
|
//------------------------------------------------------------------------
|
|
// Run the dead reckoner, and then see how far apart the two positions are
|
|
//------------------------------------------------------------------------
|
|
//
|
|
(this->*deadReckoner)();
|
|
|
|
Vector3D
|
|
error;
|
|
error.Subtract(
|
|
projectedOrigin.linearPosition,
|
|
localOrigin.linearPosition
|
|
);
|
|
Quaternion
|
|
angular_deviation;
|
|
angular_deviation.Subtract(
|
|
projectedOrigin.angularPosition,
|
|
localOrigin.angularPosition
|
|
);
|
|
if (
|
|
error.LengthSquared() > 0.04f
|
|
|| Abs(angular_deviation.w) < 0.997f
|
|
|| collisionState.GetState() != collisionState.GetOldState()
|
|
|| lastPerformance - lastUpdate > 2.0f
|
|
)
|
|
{
|
|
ForceUpdate();
|
|
}
|
|
Check_Fpu();
|
|
}
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
//
|
|
void
|
|
VTV::PerformAndWatch(
|
|
const Time &till,
|
|
MemoryStream *update_stream
|
|
)
|
|
{
|
|
Check(this);
|
|
Check(&till);
|
|
|
|
if (GetInstance() != ReplicantInstance)
|
|
{
|
|
boosterOn = False;
|
|
boosterScale = Vector3D::Identity;
|
|
boosterSmokeDensity = 0.0f;
|
|
}
|
|
JointedMover::PerformAndWatch(till, update_stream);
|
|
Check_Fpu();
|
|
}
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
//
|
|
void
|
|
VTV::WriteUpdateRecord(Simulation::UpdateRecord *message, int update_model)
|
|
{
|
|
Check(this);
|
|
Check_Pointer(message);
|
|
|
|
JointedMover::WriteUpdateRecord(message, update_model);
|
|
|
|
UpdateRecord
|
|
*record = (UpdateRecord*)message;
|
|
|
|
record->recordLength = sizeof(*record);
|
|
|
|
record->collisionState = collisionState.GetState();
|
|
record->collisionMaterialType = collisionMaterialType;
|
|
record->collisionNormal = collisionNormal;
|
|
record->collisionSpeed = collisionSpeed;
|
|
|
|
record->boosterOn = boosterOn;
|
|
record->boosterScale = boosterScale;
|
|
record->boosterSmokeDensity = boosterSmokeDensity;
|
|
|
|
record->hornBlast = hornBlast; // HACK - ECH 7/31/95 - For replicants
|
|
Check_Fpu();
|
|
}
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
//
|
|
void
|
|
VTV::ReadUpdateRecord(Simulation::UpdateRecord *message)
|
|
{
|
|
Check(this);
|
|
Check_Pointer(message);
|
|
|
|
JointedMover::ReadUpdateRecord(message);
|
|
|
|
UpdateRecord
|
|
*record = (UpdateRecord*)message;
|
|
|
|
collisionState.SetState(record->collisionState);
|
|
collisionMaterialType = record->collisionMaterialType;
|
|
collisionNormal = record->collisionNormal;
|
|
collisionSpeed = record->collisionSpeed;
|
|
|
|
boosterOn = record->boosterOn;
|
|
boosterScale = record->boosterScale;
|
|
boosterSmokeDensity = record->boosterSmokeDensity;
|
|
|
|
hornBlast = record->hornBlast; // HACK - ECH 7/31/95 - For replicants
|
|
|
|
if (
|
|
GetSimulationState() == DefaultState
|
|
&& simulationState.GetOldState() == BurningState
|
|
)
|
|
{
|
|
localOrigin = updateOrigin;
|
|
|
|
if (IsCollisionVolume())
|
|
{
|
|
MoveCollisionVolume();
|
|
}
|
|
}
|
|
Check_Fpu();
|
|
}
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
//
|
|
void
|
|
VTV::ProcessCollision(
|
|
Scalar time_slice,
|
|
BoxedSolidCollision &collision,
|
|
const Point3D &old_position,
|
|
Damage *damage
|
|
)
|
|
{
|
|
Scalar
|
|
penetration,
|
|
r,
|
|
elasticity = elasticityCoefficient,
|
|
friction = frictionCoefficient,
|
|
added_damage = 0.0f;
|
|
Door
|
|
*door = NULL;
|
|
|
|
//
|
|
//-----------------------------------------------------------------------
|
|
// Find out if we have hit a mover or something static. Moving collision
|
|
// volumes must have an owning entity
|
|
//-----------------------------------------------------------------------
|
|
//
|
|
BoxedSolid
|
|
*box = collision.GetTreeVolume();
|
|
Check(box);
|
|
|
|
Simulation
|
|
*sim = box->GetOwningSimulation();
|
|
if (!sim)
|
|
{
|
|
goto Static_Bounce;
|
|
}
|
|
if (sim->IsDerivedFrom(*Mover::GetClassDerivations()))
|
|
{
|
|
Mover *mover = (Mover*)sim;
|
|
|
|
//
|
|
//--------------------------------------------------------------------
|
|
// If we hit a mover, make sure that the collision is valid, and if it
|
|
// is, calculate the energy_loss
|
|
//--------------------------------------------------------------------
|
|
//
|
|
if (
|
|
!collisionVolume->ProcessCollision(
|
|
collision,
|
|
worldLinearVelocity,
|
|
lastCollisionList,
|
|
&damage->surfaceNormal,
|
|
&penetration
|
|
)
|
|
)
|
|
{
|
|
Check_Fpu();
|
|
return;
|
|
}
|
|
|
|
//
|
|
//--------------------------------------------------------------
|
|
// Make sure that we are not rearending somebody going faster...
|
|
//--------------------------------------------------------------
|
|
//
|
|
Vector3D
|
|
rel_v;
|
|
rel_v.Subtract(worldLinearVelocity, mover->worldLinearVelocity);
|
|
if (rel_v * damage->surfaceNormal >= -SMALL)
|
|
{
|
|
Check_Fpu();
|
|
return;
|
|
}
|
|
|
|
//
|
|
//---------------------------------
|
|
// Calculate the moving energy loss
|
|
//---------------------------------
|
|
//
|
|
Max_Clamp(penetration, time_slice);
|
|
r = penetration / time_slice;
|
|
Check_Fpu();
|
|
|
|
damage->damageAmount =
|
|
DynamicBounce(
|
|
mover,
|
|
time_slice,
|
|
r,
|
|
damage->surfaceNormal,
|
|
&elasticity
|
|
);
|
|
|
|
//
|
|
//--------------------------------------------------------------------
|
|
// If we hit another VTV, we will have to send it a damage message and
|
|
// enter it into the queue of guys what hit us in the last two seconds
|
|
//--------------------------------------------------------------------
|
|
//
|
|
if (mover->GetClassID() == VTVClassID)
|
|
{
|
|
TakeDamageMessage
|
|
message(
|
|
TakeDamageMessageID,
|
|
sizeof(TakeDamageMessage),
|
|
GetEntityID(),
|
|
0,
|
|
*damage
|
|
);
|
|
mover->Dispatch(&message);
|
|
|
|
RPPlayer
|
|
*offensive_player = (RPPlayer*)mover->GetPlayerLink();
|
|
if (offensive_player)
|
|
{
|
|
Check(offensive_player);
|
|
RPPlayer::OffensivePlayer
|
|
*shooter = new RPPlayer::OffensivePlayer(
|
|
offensive_player,
|
|
message.damageData.damageType,
|
|
lastPerformance
|
|
);
|
|
Register_Object(shooter);
|
|
RPPlayer
|
|
*our_player = (RPPlayer*)GetPlayerLink();
|
|
Check(our_player);
|
|
our_player->AddOffensivePlayer(*shooter);
|
|
}
|
|
}
|
|
|
|
//
|
|
//-------------------------------------
|
|
// Set up the collision state correctly
|
|
//-------------------------------------
|
|
//
|
|
goto Set_Collision_State;
|
|
}
|
|
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
// If we hit a moving door, adjust our relative velocity to the door. If we
|
|
// get hit by both doors, CRUSH the vtv!!!
|
|
//--------------------------------------------------------------------------
|
|
//
|
|
else if (sim->IsDerivedFrom(*Door::GetClassDerivations()))
|
|
{
|
|
door = (Door*)sim;
|
|
Check(door);
|
|
worldLinearVelocity += door->currentVelocity;
|
|
}
|
|
|
|
//--------------------------------------------------------------------
|
|
// If we really have a collision, do a static bounce off of the normal
|
|
// generated
|
|
//--------------------------------------------------------------------
|
|
//
|
|
Static_Bounce:
|
|
if (
|
|
collisionVolume->ProcessCollision(
|
|
collision,
|
|
worldLinearVelocity,
|
|
lastCollisionList,
|
|
&damage->surfaceNormal,
|
|
&penetration
|
|
)
|
|
)
|
|
{
|
|
Max_Clamp(penetration, time_slice);
|
|
r = penetration / time_slice;
|
|
Check_Fpu();
|
|
|
|
damage->damageAmount =
|
|
StaticBounce(
|
|
old_position,
|
|
time_slice,
|
|
r,
|
|
damage->surfaceNormal,
|
|
&elasticity,
|
|
minimumBounceSpeed,
|
|
&friction
|
|
) + added_damage;
|
|
|
|
//
|
|
//----------------------------------------
|
|
// If we have hit a door, look for a crush
|
|
//----------------------------------------
|
|
//
|
|
if (door)
|
|
{
|
|
if (
|
|
lastPerformance - lastDoorHit < 0.1f
|
|
&& doorHitNormal*damage->surfaceNormal < -0.7f
|
|
&& damageZones[0]->damageLevel < 1.0f
|
|
)
|
|
{
|
|
damageZones[0]->damageLevel = 1.0f;
|
|
}
|
|
doorHitNormal = damage->surfaceNormal;
|
|
lastDoorHit = lastPerformance;
|
|
}
|
|
|
|
//
|
|
//--------------------------
|
|
// Determine collision state
|
|
//--------------------------
|
|
//
|
|
Set_Collision_State:
|
|
if (!elasticity)
|
|
{
|
|
if (!friction)
|
|
{
|
|
if (collisionTemporaryState == NoCollisionState)
|
|
{
|
|
collisionTemporaryState = RestState;
|
|
}
|
|
}
|
|
else if (collisionTemporaryState != InitialHitState)
|
|
{
|
|
collisionTemporaryState = SlideState;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
collisionTemporaryState = InitialHitState;
|
|
}
|
|
}
|
|
|
|
if (door)
|
|
{
|
|
worldLinearVelocity -= door->currentVelocity;
|
|
}
|
|
Check_Fpu();
|
|
}
|
|
|
|
//#############################################################################
|
|
// Scoring support
|
|
//
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
// HACK - ECH 7/6/95 - Allow the player vehicle to respond to score
|
|
// messages, allows attribute system to be used for scoring
|
|
//
|
|
void
|
|
VTV::RespondToScoreMessage(Message *message)
|
|
{
|
|
Check(this);
|
|
Check(message);
|
|
|
|
RPPlayer::ScoreMessage
|
|
*score_message = Cast_Object(RPPlayer::ScoreMessage*, message);
|
|
|
|
//
|
|
// Set the score state and toggle the state to trigger the watchers
|
|
//
|
|
Check(score_message);
|
|
scoreType = score_message->scoreType;
|
|
Verify(
|
|
score_message->scoreType > RPPlayer::NullPointType &&
|
|
score_message->scoreType < RPPlayer::PointTypeCount
|
|
);
|
|
scoreState.SetState(score_message->scoreType);
|
|
scoreState.SetState(RPPlayer::NullPointType);
|
|
Check_Fpu();
|
|
}
|
|
|
|
//#############################################################################
|
|
// Construction and Destruction
|
|
//
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
//
|
|
VTV::VTV(
|
|
VTV::MakeMessage *creation_message,
|
|
VTV::SharedData &virtual_data
|
|
):
|
|
JointedMover(creation_message, virtual_data),
|
|
collisionState(CollisionStateCount),
|
|
collisionNormal(0.0f, 0.0f, 1.0f),
|
|
scoreState(RPPlayer::PointTypeCount)
|
|
{
|
|
SetValidFlag();
|
|
|
|
//
|
|
//------------------------
|
|
// Setup the initial state
|
|
//------------------------
|
|
//
|
|
Check(application);
|
|
Check(application->GetInterestManager());
|
|
|
|
InterestZone
|
|
*interest_zone =
|
|
application->GetInterestManager()->GetInterestZone(interestZoneID);
|
|
Check(interest_zone);
|
|
|
|
localEnvironment = interest_zone->GetEnvironment();
|
|
Check(localEnvironment);
|
|
|
|
//
|
|
//----------------
|
|
// Init attributes
|
|
//----------------
|
|
//
|
|
collisionMaterialType = BoxedSolid::SteelMaterial;
|
|
// collisionNormal = Normal::Identity; // Fails TestInstance
|
|
collisionSpeed = 0.0f;
|
|
collisionState.SetState(NoCollisionState);
|
|
scoreState.SetState(RPPlayer::NullPointType);
|
|
scoreType = -1;
|
|
boosterOn = False;
|
|
boosterScale = Vector3D::Identity;
|
|
boosterSmokeDensity = 0.0f;
|
|
doorHitNormal = Vector3D::Identity;
|
|
lastDoorHit = Time::Null;
|
|
heightAboveTerrain = 0.0f;
|
|
forwardVelocity = 0.0f;
|
|
hornBlast = -1;
|
|
insideWorld = True;
|
|
thrusterAngle = 0.0f;
|
|
|
|
//
|
|
//-----------------------------
|
|
// Set the correct motion model
|
|
//-----------------------------
|
|
//
|
|
BoxedSolid::Material
|
|
material;
|
|
|
|
SetDeadReckoner(&VTV::AcceleratedDeadReckoner);
|
|
if (GetInstance() == ReplicantInstance)
|
|
{
|
|
SetPerformance(&VTV::DeadReckon);
|
|
material = BoxedSolid::OtherCraftMaterial;
|
|
}
|
|
else
|
|
{
|
|
SetPerformance(&VTV::MoveAndCollide);
|
|
material = BoxedSolid::OurCraftMaterial;
|
|
}
|
|
|
|
//
|
|
//-----------------------------
|
|
// Initialize the VTV variables
|
|
//-----------------------------
|
|
//
|
|
ResourceFile
|
|
*res_file = application->GetResourceFile();
|
|
ResourceDescription
|
|
*res = res_file->SearchList(
|
|
resourceID,
|
|
ResourceDescription::GameModelResourceType
|
|
);
|
|
Check(res);
|
|
res->Lock();
|
|
|
|
ModelResource
|
|
*model_resource = (ModelResource*)res->resourceAddress;
|
|
Check_Pointer(model_resource);
|
|
|
|
groundEffectDomainSquared = model_resource->groundEffectDomainSquared;
|
|
groundEffectRange = model_resource->groundEffectRange;
|
|
|
|
maxAngularAcceleration = model_resource->maxAngularAcceleration;
|
|
maxYawVelocity = model_resource->maxYawVelocity;
|
|
angularSpringFactor = model_resource->angularSpringFactor;
|
|
bankFactor = model_resource->bankFactor;
|
|
maxImpactSpeed = model_resource->maxImpactSpeed;
|
|
bottomArmorScale = model_resource->bottomArmorScale;
|
|
|
|
Scalar death_speed_km = model_resource->deathSpeed;
|
|
//
|
|
// Convert to Meters/Second
|
|
//
|
|
deathSpeed = death_speed_km/3.6f;
|
|
deathScoreLoss = model_resource->deathScoreLoss;
|
|
powerDive = model_resource->powerDive;
|
|
|
|
res->Unlock();
|
|
|
|
deathConstant = (2000 * deathScoreLoss) /
|
|
(
|
|
(1 - (elasticityCoefficient * elasticityCoefficient)) *
|
|
(deathSpeed * deathSpeed) * moverMass
|
|
);
|
|
Check_Fpu();
|
|
|
|
thrusterPitch.rotationAmount = 0.0f;
|
|
thrusterPitch.axisNumber = X_Axis;
|
|
compassHeading = YawPitchRoll::Identity;
|
|
eyepointRotation = EulerAngles::Identity;
|
|
|
|
navigationRange = 1000.0; // initial range = 1km
|
|
currentRangeExponent = 3.0f;
|
|
targetRangeExponent = 3.0f;
|
|
|
|
Str_Copy(vehicleBadge, creation_message->vehicleBadge, sizeof(vehicleBadge));
|
|
Str_Copy(vehicleColor, creation_message->vehicleColor, sizeof(vehicleColor));
|
|
|
|
//
|
|
//-------------------------------------------------
|
|
// Set up secondary display variables
|
|
// The indirection is required by the camera ship,
|
|
// but may be exploited for viewing the nav
|
|
// display at an arbitrary location, if desired.
|
|
//-------------------------------------------------
|
|
// If 'navigationLinearPosition' is NULL, it will
|
|
// extract the position from the entity directly.
|
|
// Not so for 'navigationAngularPosition': if it
|
|
// is NULL, the display will not 'turn' with the
|
|
// vehicle. It must be explicitly set if used.
|
|
//-------------------------------------------------
|
|
//
|
|
navigationLinearPosition = NULL;
|
|
navigationAngularPosition = &localOrigin.angularPosition;
|
|
//
|
|
//---------------------------------------------------
|
|
// Make sure to correctly set the material of the VTV
|
|
//---------------------------------------------------
|
|
//
|
|
collisionVolume->materialType = material;
|
|
|
|
//
|
|
//------------------------------------------------------------------------
|
|
// Setup the subsystems. The mapper subsystem will be left blank, so that
|
|
// the caller can hook in the appropriate mapper
|
|
//------------------------------------------------------------------------
|
|
//
|
|
res =
|
|
application->GetResourceFile()->SearchList(
|
|
resourceID,
|
|
ResourceDescription::SubsystemModelStreamResourceType
|
|
);
|
|
Check(res);
|
|
res->Lock();
|
|
|
|
Check_Pointer(res->resourceAddress);
|
|
MemoryStream
|
|
subsystems(res->resourceAddress, res->resourceSize);
|
|
|
|
subsystemCount = *(int*)subsystems.GetPointer() + BasicSubsystemCount;
|
|
subsystems.AdvancePointer(sizeof(int));
|
|
Verify(!subsystemArray);
|
|
subsystemArray = new (Subsystem (*[subsystemCount]));
|
|
Register_Pointer(subsystemArray);
|
|
|
|
if (GetInstance() == ReplicantInstance)
|
|
{
|
|
VTVControlsMapper::SubsystemResource
|
|
control_subsystem_resource;
|
|
|
|
Str_Copy(
|
|
control_subsystem_resource.subsystemName,
|
|
"ControlsMapper",
|
|
sizeof(control_subsystem_resource.subsystemName)
|
|
);
|
|
control_subsystem_resource.classID = TrivialSubsystemClassID;
|
|
control_subsystem_resource.subsystemModelSize =
|
|
sizeof(control_subsystem_resource);
|
|
control_subsystem_resource.segmentIndex = -1;
|
|
control_subsystem_resource.subsystemFlags = DontExecuteFlag;
|
|
|
|
subsystemArray[ControlsMapperSubsystem] =
|
|
new VTVControlsMapper(
|
|
this,
|
|
VTV::ControlsMapperSubsystem,
|
|
&control_subsystem_resource,
|
|
VTVControlsMapper::DefaultData
|
|
);
|
|
Register_Object(subsystemArray[ControlsMapperSubsystem]);
|
|
}
|
|
else
|
|
{
|
|
subsystemArray[ControlsMapperSubsystem] = NULL;
|
|
}
|
|
|
|
res->Unlock();
|
|
//
|
|
//--------------------------------------------------------------------
|
|
// Make the power subsystem. All VTVs have one in the designated slot
|
|
//--------------------------------------------------------------------
|
|
//
|
|
VTVPower::SubsystemResource
|
|
power_subsystem_resource;
|
|
|
|
Str_Copy(
|
|
power_subsystem_resource.subsystemName,
|
|
"Power",
|
|
sizeof(power_subsystem_resource.subsystemName)
|
|
);
|
|
power_subsystem_resource.classID = TrivialSubsystemClassID;
|
|
power_subsystem_resource.subsystemModelSize =
|
|
sizeof(power_subsystem_resource);
|
|
power_subsystem_resource.maxAcceleration = model_resource->maxAcceleration;
|
|
power_subsystem_resource.segmentIndex = -1;
|
|
power_subsystem_resource.subsystemFlags = 0;
|
|
|
|
|
|
subsystemArray[PowerSubsystem] =
|
|
new VTVPower(this, PowerSubsystem, &power_subsystem_resource);
|
|
Register_Object(subsystemArray[PowerSubsystem]);
|
|
|
|
//
|
|
//---------------------------------------------------------------
|
|
// Make the thruster subsystems. We can handle up to 6 thrusters
|
|
//---------------------------------------------------------------
|
|
//
|
|
Thruster::SubsystemResource
|
|
thruster_subsystem_resource;
|
|
|
|
Str_Copy(
|
|
thruster_subsystem_resource.subsystemName,
|
|
"Thruster?",
|
|
sizeof(thruster_subsystem_resource.subsystemName)
|
|
);
|
|
thruster_subsystem_resource.classID = TrivialSubsystemClassID;
|
|
thruster_subsystem_resource.subsystemModelSize =
|
|
sizeof(thruster_subsystem_resource);
|
|
thruster_subsystem_resource.subsystemFlags = 0;
|
|
thruster_subsystem_resource.primeThruster = True;
|
|
|
|
//
|
|
// Initialize the thruster subsystems from the jointSubystem
|
|
// Information
|
|
//
|
|
|
|
#if DEBUG_LEVEL>0
|
|
JointSubsystem
|
|
*joint_subsystem = GetJointSubsystem();
|
|
Check(joint_subsystem);
|
|
Verify(joint_subsystem->GetJointCount() <= MAX_THRUSTERS);
|
|
#endif
|
|
|
|
int i;
|
|
for (i=0; i<MAX_THRUSTERS; ++i)
|
|
{
|
|
if (
|
|
model_resource->jointIndex[i] == -1
|
|
|| model_resource->segmentIndex[i] == -1
|
|
)
|
|
{
|
|
subsystemArray[Thruster1Subsystem + i] = NULL;
|
|
continue;
|
|
}
|
|
//
|
|
// Append thruster number to subsystem name
|
|
//
|
|
thruster_subsystem_resource.subsystemName[8] = (char)(i + '0');
|
|
|
|
//
|
|
// assign joint and segment Indicies
|
|
//
|
|
thruster_subsystem_resource.jointIndex =
|
|
model_resource->jointIndex[i];
|
|
thruster_subsystem_resource.segmentIndex =
|
|
model_resource->segmentIndex[i];
|
|
|
|
//
|
|
// Get maxThrusterRotation Rate
|
|
//
|
|
thruster_subsystem_resource.maxThrusterRotationRate =
|
|
model_resource->maxThrusterRotationRate;
|
|
|
|
//
|
|
// Construct the thruster Subsystem
|
|
//
|
|
subsystemArray[Thruster1Subsystem + i] =
|
|
new Thruster(
|
|
this,
|
|
Thruster1Subsystem+i,
|
|
&thruster_subsystem_resource);
|
|
|
|
Register_Object(subsystemArray[Thruster1Subsystem + i]);
|
|
thruster_subsystem_resource.primeThruster = False;
|
|
}
|
|
|
|
//
|
|
// Add the JointSubsystem to the subsystemArray
|
|
//
|
|
subsystemArray[JointSubsystemID] = GetJointSubsystem();
|
|
|
|
//
|
|
//---------------------------------------------------
|
|
// Read and process the subsystem array specification
|
|
//---------------------------------------------------
|
|
//
|
|
for (i=BasicSubsystemCount; i<subsystemCount; ++i)
|
|
{
|
|
Subsystem::SubsystemResource
|
|
*subsystem_resource =
|
|
(Subsystem::SubsystemResource*)subsystems.GetPointer();
|
|
if (
|
|
!(subsystem_resource->subsystemFlags & Subsystem::DontReplicateFlag)
|
|
|| GetInstance() != ReplicantInstance
|
|
)
|
|
{
|
|
switch (subsystem_resource->classID)
|
|
{
|
|
case RegisteredClass::BoosterClassID:
|
|
subsystemArray[i] =
|
|
new Booster(
|
|
this,
|
|
i,
|
|
(Booster::SubsystemResource*)subsystem_resource
|
|
);
|
|
break;
|
|
case RegisteredClass::ChuteClassID:
|
|
subsystemArray[i] =
|
|
new Chute(this, i, (Chute::SubsystemResource*)subsystem_resource);
|
|
break;
|
|
case RegisteredClass::RivetGunClassID:
|
|
subsystemArray[i] =
|
|
new RivetGun(this, i, (RivetGun::SubsystemResource*)subsystem_resource);
|
|
break;
|
|
case RegisteredClass::LaserDrillClassID:
|
|
subsystemArray[i] =
|
|
new LaserGun(this, i, (LaserGun::SubsystemResource*)subsystem_resource);
|
|
break;
|
|
case RegisteredClass::DemolitionPackDropperClassID:
|
|
subsystemArray[i] =
|
|
new DemolitionPackDropper(
|
|
this,
|
|
i,
|
|
(DemolitionPackDropper::SubsystemResource*)subsystem_resource);
|
|
break;
|
|
}
|
|
Register_Object(subsystemArray[i]);
|
|
}
|
|
else
|
|
{
|
|
subsystemArray[i] = NULL;
|
|
}
|
|
subsystems.AdvancePointer(subsystem_resource->subsystemModelSize);
|
|
}
|
|
|
|
//
|
|
// Read In DamageZones and Create DamageZoneID's
|
|
//
|
|
|
|
ResourceDescription
|
|
*dmg_res = application->GetResourceFile()->SearchList(
|
|
resourceID,
|
|
ResourceDescription::DamageZoneStreamResourceType
|
|
);
|
|
Check(dmg_res);
|
|
dmg_res->Lock();
|
|
|
|
DynamicMemoryStream
|
|
damage_zone_stream(
|
|
dmg_res->resourceAddress,
|
|
dmg_res->resourceSize
|
|
);
|
|
//
|
|
//-------------------------------------
|
|
// skip over damageZoneCount in Stream
|
|
//-------------------------------------
|
|
//
|
|
damage_zone_stream.AdvancePointer(sizeof(damageZoneCount));
|
|
//
|
|
// Allocate damageZones Array
|
|
//
|
|
for(int ii=0;ii<damageZoneCount;++ii)
|
|
{
|
|
damageZones[ii] = new VTV::DamageZone(
|
|
this,
|
|
ii,
|
|
&damage_zone_stream
|
|
);
|
|
Register_Object(damageZones[ii]);
|
|
}
|
|
|
|
dmg_res->Unlock();
|
|
//
|
|
//--------------------
|
|
// Init the reticle
|
|
//--------------------
|
|
//
|
|
targetReticle.reticlePosition = model_resource->reticleResource.reticlePosition;
|
|
Check_Fpu();
|
|
}
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
//
|
|
VTV::~VTV()
|
|
{
|
|
Check(this);
|
|
Check_Fpu();
|
|
}
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
//
|
|
void
|
|
VTV::Reset(const Origin& new_origin, int reset_command)
|
|
{
|
|
Check(this);
|
|
Check(&new_origin);
|
|
|
|
localOrigin = new_origin;
|
|
localVelocity = Motion::Identity;
|
|
localAcceleration = Motion::Identity;
|
|
worldLinearVelocity = Vector3D::Identity;
|
|
worldLinearAcceleration = Vector3D::Identity;
|
|
localToWorld = localOrigin;
|
|
SetSimulationState(VTV::DefaultState);
|
|
damageZones[0]->damageLevel = 0.0f;
|
|
collisionState.SetState(NoCollisionState);
|
|
boosterOn = False;
|
|
boosterScale = Vector3D::Identity;
|
|
boosterSmokeDensity = 0.0f;
|
|
|
|
//
|
|
//-------------------------
|
|
// Reset subsystems
|
|
//-------------------------
|
|
//
|
|
for(int ii=BasicSubsystemCount;ii<subsystemCount;ii++)
|
|
{
|
|
Subsystem *subsystem = GetSubsystem(ii);
|
|
if (subsystem)
|
|
{
|
|
Check(subsystem);
|
|
subsystem->DeathReset(reset_command);
|
|
}
|
|
}
|
|
Check_Fpu();
|
|
}
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
//
|
|
void
|
|
VTV::DeathShutdown(int shutdown_command)
|
|
{
|
|
//
|
|
//-------------------------
|
|
// Shutdown subsystems
|
|
//-------------------------
|
|
//
|
|
for(int ii=BasicSubsystemCount;ii<subsystemCount;ii++)
|
|
{
|
|
Subsystem *subsystem = GetSubsystem(ii);
|
|
subsystem->DeathShutdown(shutdown_command);
|
|
}
|
|
Check_Fpu();
|
|
}
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
//
|
|
ResourceDescription::ResourceID
|
|
VTV::CreateModelResource(
|
|
ResourceFile *resource_file,
|
|
const char *model_name,
|
|
NotationFile *model_file,
|
|
const ResourceDirectories *directories,
|
|
ModelResource *model
|
|
)
|
|
{
|
|
Check(resource_file);
|
|
Check_Pointer(model_name);
|
|
Check(model_file);
|
|
Check_Pointer(directories);
|
|
|
|
//
|
|
//-----------------------------------------------------------------------
|
|
// If we were not provided a buffer to write the model data into, we must
|
|
// create it ourselves. Then make sure that the model stuff is read in
|
|
//-----------------------------------------------------------------------
|
|
//
|
|
ModelResource
|
|
*local_model = model;
|
|
if (!local_model)
|
|
{
|
|
local_model = new ModelResource;
|
|
Register_Pointer(local_model);
|
|
}
|
|
if (
|
|
JointedMover::CreateModelResource(
|
|
resource_file,
|
|
model_name,
|
|
model_file,
|
|
directories,
|
|
local_model
|
|
) == -1
|
|
)
|
|
{
|
|
Dump_And_Die:
|
|
if (!model)
|
|
{
|
|
Unregister_Pointer(local_model);
|
|
delete local_model;
|
|
}
|
|
Check_Fpu();
|
|
return -1;
|
|
}
|
|
if (
|
|
Reticle::CreateModelResource(
|
|
resource_file,
|
|
model_name,
|
|
model_file,
|
|
directories,
|
|
&local_model->reticleResource
|
|
) == -1
|
|
)
|
|
{
|
|
goto Dump_And_Die;
|
|
}
|
|
//
|
|
//-----------------------------
|
|
// Read in the max acceleration
|
|
//-----------------------------
|
|
//
|
|
if (
|
|
!model_file->GetEntry(
|
|
"gamedata",
|
|
"MaxAcceleration",
|
|
&local_model->maxAcceleration
|
|
)
|
|
)
|
|
{
|
|
DEBUG_STREAM << model_name << " missing MaxAcceleration!\n" << std::flush;
|
|
goto Dump_And_Die;
|
|
}
|
|
if (
|
|
!model_file->GetEntry(
|
|
"gamedata",
|
|
"MaxImpactSpeed",
|
|
&local_model->maxImpactSpeed
|
|
)
|
|
)
|
|
{
|
|
DEBUG_STREAM << model_name << " missing MaxImpactSpeed!\n" << std::flush;
|
|
goto Dump_And_Die;
|
|
}
|
|
|
|
//
|
|
//-------------------------
|
|
// Read in the bottom armor
|
|
//-------------------------
|
|
//
|
|
local_model->bottomArmorScale = 0.1f;
|
|
model_file->GetEntry(
|
|
"gamedata",
|
|
"BottomArmorScale",
|
|
&local_model->bottomArmorScale
|
|
);
|
|
|
|
//
|
|
//-------------------------
|
|
// Read in the bottom armor
|
|
//-------------------------
|
|
//
|
|
local_model->powerDive = 0.0f;
|
|
model_file->GetEntry(
|
|
"gamedata",
|
|
"PowerDive",
|
|
&local_model->powerDive
|
|
);
|
|
if (local_model->powerDive > 0.0 || local_model->powerDive < -1.0f)
|
|
{
|
|
DEBUG_STREAM << model_name << " has an invalid powerDive!\n" << std::flush;
|
|
goto Dump_And_Die;
|
|
}
|
|
|
|
//
|
|
//-----------------------------------
|
|
// Read in the thruster rotation rate
|
|
//-----------------------------------
|
|
//
|
|
if (
|
|
!model_file->GetEntry(
|
|
"gamedata",
|
|
"MaxThrusterRotationRate",
|
|
&local_model->maxThrusterRotationRate
|
|
)
|
|
)
|
|
{
|
|
DEBUG_STREAM << model_name << " missing MaxThrusterRotationRate!\n" << std::flush;
|
|
goto Dump_And_Die;
|
|
}
|
|
|
|
//
|
|
//--------------------------------
|
|
// Read in the ground effect stuff
|
|
//--------------------------------
|
|
//
|
|
if (
|
|
!model_file->GetEntry(
|
|
"gamedata",
|
|
"GroundEffectDomainSquared",
|
|
&local_model->groundEffectDomainSquared
|
|
)
|
|
)
|
|
{
|
|
DEBUG_STREAM << model_name << " missing GroundEffectDomainSquared!\n" << std::flush;
|
|
goto Dump_And_Die;
|
|
}
|
|
|
|
if (
|
|
!model_file->GetEntry(
|
|
"gamedata",
|
|
"GroundEffectRange",
|
|
&local_model->groundEffectRange
|
|
)
|
|
)
|
|
{
|
|
DEBUG_STREAM << model_name << " missing GroundEffectRange!\n" << std::flush;
|
|
goto Dump_And_Die;
|
|
}
|
|
|
|
|
|
//
|
|
//----------------------------------------
|
|
// Read in the angular acceleration limits
|
|
//----------------------------------------
|
|
//
|
|
const char *entry;
|
|
if (
|
|
!model_file->GetEntry(
|
|
"gamedata",
|
|
"MaxAngularAcceleration",
|
|
&entry
|
|
)
|
|
)
|
|
{
|
|
DEBUG_STREAM << model_name << " missing MaxAngularAcceleration!\n" << std::flush;
|
|
goto Dump_And_Die;
|
|
}
|
|
sscanf(
|
|
entry,
|
|
"%f %f %f",
|
|
&local_model->maxAngularAcceleration.x,
|
|
&local_model->maxAngularAcceleration.y,
|
|
&local_model->maxAngularAcceleration.z
|
|
);
|
|
|
|
if (
|
|
!model_file->GetEntry(
|
|
"gamedata",
|
|
"AngularSpringFactor",
|
|
&local_model->angularSpringFactor
|
|
)
|
|
)
|
|
{
|
|
DEBUG_STREAM << model_name << " missing AngularSpringFactor!\n" << std::flush;
|
|
goto Dump_And_Die;
|
|
}
|
|
if (
|
|
!model_file->GetEntry(
|
|
"gamedata",
|
|
"DeathSpeed",
|
|
&local_model->deathSpeed
|
|
)
|
|
)
|
|
{
|
|
DEBUG_STREAM << model_name << " missing DeathSpeed!\n" << std::flush;
|
|
goto Dump_And_Die;
|
|
}
|
|
if (
|
|
!model_file->GetEntry(
|
|
"gamedata",
|
|
"DeathScoreLoss",
|
|
&local_model->deathScoreLoss
|
|
)
|
|
)
|
|
{
|
|
DEBUG_STREAM << model_name << " missing DeathScoreLoss!\n" << std::flush;
|
|
goto Dump_And_Die;
|
|
}
|
|
if (
|
|
!model_file->GetEntry(
|
|
"gamedata",
|
|
"ElasticityCoefficient",
|
|
&local_model->elasticityCoefficient
|
|
)
|
|
)
|
|
{
|
|
DEBUG_STREAM << model_name << " missing ElasticityCoefficient!\n" << std::flush;
|
|
goto Dump_And_Die;
|
|
}
|
|
if (
|
|
!model_file->GetEntry(
|
|
"gamedata",
|
|
"MaxYawVelocity",
|
|
&local_model->maxYawVelocity
|
|
)
|
|
)
|
|
{
|
|
DEBUG_STREAM << model_name << " missing MaxYawVelocity!\n" << std::flush;
|
|
goto Dump_And_Die;
|
|
}
|
|
local_model->maxYawVelocity *= RAD_PER_DEG;
|
|
|
|
if (
|
|
!model_file->GetEntry(
|
|
"gamedata",
|
|
"BankFactor",
|
|
&local_model->bankFactor
|
|
)
|
|
)
|
|
{
|
|
DEBUG_STREAM << model_name << " missing BankFactor!\n" << std::flush;
|
|
goto Dump_And_Die;
|
|
}
|
|
|
|
//
|
|
// Read in Segment and Joint information from the .skl file
|
|
// for the thrusters
|
|
//
|
|
//-------------------
|
|
// Find the .skl file
|
|
//-------------------
|
|
//
|
|
const char
|
|
*skl_entry;
|
|
if (
|
|
!model_file->GetEntry(
|
|
"video",
|
|
"skeleton",
|
|
&skl_entry
|
|
)
|
|
)
|
|
{
|
|
DEBUG_STREAM << model_name << " is missing .skl file specification!\n" << std::flush;
|
|
return -1;
|
|
}
|
|
char
|
|
*skl_filename =
|
|
MakePathedFilename(directories->videoDirectory, skl_entry);
|
|
Register_Pointer(skl_filename);
|
|
|
|
NotationFile
|
|
*skl_file = new NotationFile(skl_filename);
|
|
Register_Object(skl_file);
|
|
|
|
if (!skl_file->PageCount())
|
|
{
|
|
DEBUG_STREAM << skl_filename << " is empty or missing!\n" << std::flush;
|
|
Dump_And_Die_1:
|
|
Unregister_Pointer(skl_filename);
|
|
delete[] skl_filename;
|
|
Unregister_Object(skl_file);
|
|
delete skl_file;
|
|
Check_Fpu();
|
|
return ResourceDescription::NullResourceID;
|
|
}
|
|
|
|
//
|
|
// Make a pagelist of all the segments
|
|
//
|
|
NameList
|
|
*segment_pages = skl_file->MakePageList();
|
|
Register_Object(segment_pages);
|
|
|
|
NameList::Entry
|
|
*segment_entry = segment_pages->GetFirstEntry();
|
|
int segment_count=0;
|
|
int joint_count=0;
|
|
//
|
|
// Initialize the thruster Model Resource info to No Thruster!
|
|
//
|
|
for(int ii=0;ii<MAX_THRUSTERS;ii++)
|
|
{
|
|
local_model->jointIndex[ii] = -1;
|
|
local_model->segmentIndex[ii] = -1;
|
|
}
|
|
|
|
while(segment_entry)
|
|
{
|
|
char entry[32];
|
|
Str_Copy(entry, segment_entry->GetName(), sizeof(entry));
|
|
//
|
|
//
|
|
// Skip LAB_ONLY and Damage_Zones
|
|
if(
|
|
(strcmp(entry, "LAB_ONLY") == 0) ||
|
|
(strcmp(entry,"DamageZones") ==0)
|
|
)
|
|
{
|
|
segment_entry = segment_entry->GetNextEntry();
|
|
continue;
|
|
}
|
|
if (strncmp(entry,"joint",5)==0)
|
|
{
|
|
if (!stricmp(entry,"jointrfeng"))
|
|
{
|
|
local_model->jointIndex[0] = joint_count;
|
|
local_model->segmentIndex[0] = segment_count;
|
|
}
|
|
else if (!stricmp(entry,"jointlfeng"))
|
|
{
|
|
local_model->jointIndex[1] = joint_count;
|
|
local_model->segmentIndex[1] = segment_count;
|
|
}
|
|
else if (!stricmp(entry,"jointreng") || !stricmp(entry,"jointrmeng"))
|
|
{
|
|
local_model->jointIndex[2] = joint_count;
|
|
local_model->segmentIndex[2] = segment_count;
|
|
}
|
|
else if (!stricmp(entry,"jointleng") || !stricmp(entry,"jointlmeng"))
|
|
{
|
|
local_model->jointIndex[3] = joint_count;
|
|
local_model->segmentIndex[3] = segment_count;
|
|
}
|
|
else if (!strcmp(entry,"jointrbeng"))
|
|
{
|
|
local_model->jointIndex[4] = joint_count;
|
|
local_model->segmentIndex[4] = segment_count;
|
|
}
|
|
else if (!strcmp(entry,"jointlbeng"))
|
|
{
|
|
local_model->jointIndex[5] = joint_count;
|
|
local_model->segmentIndex[5] = segment_count;
|
|
}
|
|
|
|
++joint_count;
|
|
}
|
|
++segment_count;
|
|
segment_entry = segment_entry->GetNextEntry();
|
|
}
|
|
|
|
//
|
|
//-------------------------------------------------------------------------
|
|
// If we created the model buffer, then we have the responsibility to write
|
|
// it out to the resource file
|
|
//-------------------------------------------------------------------------
|
|
//
|
|
if (!model)
|
|
{
|
|
ResourceDescription
|
|
*new_res = resource_file->AddResource(
|
|
model_name,
|
|
ResourceDescription::GameModelResourceType,
|
|
1,
|
|
ResourceDescription::Preload,
|
|
local_model,
|
|
sizeof(*local_model)
|
|
);
|
|
Unregister_Pointer(local_model);
|
|
delete local_model;
|
|
Unregister_Pointer(skl_filename);
|
|
delete skl_filename;
|
|
Unregister_Object(skl_file);
|
|
delete skl_file;
|
|
Unregister_Object(segment_pages);
|
|
delete segment_pages;
|
|
|
|
Check(new_res);
|
|
Check_Fpu();
|
|
return new_res->resourceID;
|
|
}
|
|
else
|
|
{
|
|
Unregister_Pointer(local_model);
|
|
delete local_model;
|
|
Unregister_Pointer(skl_filename);
|
|
delete skl_filename;
|
|
Unregister_Object(skl_file);
|
|
delete skl_file;
|
|
Unregister_Object(segment_pages);
|
|
delete segment_pages;
|
|
|
|
Check_Fpu();
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
//
|
|
ResourceDescription::ResourceID
|
|
VTV::CreateSubsystemStream(
|
|
ResourceFile *resource_file,
|
|
const char *model_name,
|
|
NotationFile *model_file,
|
|
const ResourceDirectories *directories
|
|
)
|
|
{
|
|
Check(resource_file);
|
|
Check_Pointer(model_name);
|
|
Check(model_file);
|
|
Check_Pointer(directories);
|
|
|
|
//
|
|
//-------------------
|
|
// Find the .sub file
|
|
//-------------------
|
|
//
|
|
const char
|
|
*entry_data;
|
|
if (
|
|
!model_file->GetEntry(
|
|
"gamedata",
|
|
"Subsystems",
|
|
&entry_data
|
|
)
|
|
)
|
|
{
|
|
DEBUG_STREAM << model_name << " is missing .sub file specification!\n" << std::flush;
|
|
Check_Fpu();
|
|
return -1;
|
|
}
|
|
|
|
char
|
|
*filename = MakePathedFilename(directories->modelDirectory, entry_data);
|
|
Register_Pointer(filename);
|
|
|
|
NotationFile
|
|
*sys_file = new NotationFile(filename);
|
|
Register_Object(sys_file);
|
|
if (!sys_file->PageCount())
|
|
{
|
|
DEBUG_STREAM << filename << " cannot be found!\n" << std::flush;
|
|
Unregister_Pointer(filename);
|
|
delete filename;
|
|
Unregister_Object(sys_file);
|
|
delete sys_file;
|
|
Check_Fpu();
|
|
return -1;
|
|
}
|
|
|
|
//
|
|
//-------------------------------------------------------
|
|
// Figure out how many subsystems will have to be created
|
|
//-------------------------------------------------------
|
|
//
|
|
NameList
|
|
*namelist = sys_file->MakePageList();
|
|
Register_Object(namelist);
|
|
NameList::Entry
|
|
*entry = namelist->GetFirstEntry();
|
|
|
|
int
|
|
count = 0;
|
|
while (entry)
|
|
{
|
|
++count;
|
|
entry = entry->GetNextEntry();
|
|
}
|
|
|
|
//
|
|
//---------------------------------------------------------------------
|
|
// Step through each of the pages, and create an entry in the subsystem
|
|
// stream
|
|
//---------------------------------------------------------------------
|
|
//
|
|
int
|
|
*subsystems = new int[sizeof(RivetGun::SubsystemResource)*count];
|
|
Register_Pointer(subsystems);
|
|
|
|
MemoryStream
|
|
stream(subsystems,sizeof(RivetGun::SubsystemResource)*count*sizeof(int));
|
|
|
|
*(int*)stream.GetPointer() = count;
|
|
stream.AdvancePointer(sizeof(int));
|
|
|
|
entry = namelist->GetFirstEntry();
|
|
while (entry)
|
|
{
|
|
const char
|
|
*subsystem_name = entry->GetName(),
|
|
*type;
|
|
if (
|
|
!sys_file->GetEntry(
|
|
subsystem_name,
|
|
"Type",
|
|
&type
|
|
)
|
|
)
|
|
{
|
|
DEBUG_STREAM << model_name << ':' <<
|
|
subsystem_name << " missing Type!\n" << std::flush;
|
|
Dump_And_Die:
|
|
Unregister_Pointer(subsystems);
|
|
delete[] subsystems;
|
|
Unregister_Object(namelist);
|
|
delete namelist;
|
|
Unregister_Pointer(filename);
|
|
delete filename;
|
|
Unregister_Object(sys_file);
|
|
delete sys_file;
|
|
return -1;
|
|
}
|
|
|
|
Subsystem::SubsystemResource
|
|
*subsystem_resource =
|
|
(Subsystem::SubsystemResource*)stream.GetPointer();
|
|
if (!strcmp(type, "BoosterClassID"))
|
|
{
|
|
if (
|
|
Booster::CreateStreamedSubsystem(
|
|
model_file,
|
|
model_name,
|
|
subsystem_name,
|
|
(Booster::SubsystemResource*)subsystem_resource,
|
|
sys_file,
|
|
directories
|
|
) == -1
|
|
)
|
|
{
|
|
goto Dump_And_Die;
|
|
}
|
|
}
|
|
else
|
|
if (!strcmp(type, "ChuteClassID"))
|
|
{
|
|
if (
|
|
Chute::CreateStreamedSubsystem(
|
|
model_file,
|
|
model_name,
|
|
subsystem_name,
|
|
(Chute::SubsystemResource*)subsystem_resource,
|
|
sys_file,
|
|
directories
|
|
) == -1
|
|
)
|
|
{
|
|
goto Dump_And_Die;
|
|
}
|
|
}
|
|
else if (!strcmp(type, "RivetGunClassID"))
|
|
{
|
|
if (
|
|
RivetGun::CreateStreamedSubsystem(
|
|
model_file,
|
|
model_name,
|
|
subsystem_name,
|
|
(RivetGun::SubsystemResource*)subsystem_resource,
|
|
sys_file,
|
|
resource_file,
|
|
directories
|
|
) == -1
|
|
)
|
|
{
|
|
goto Dump_And_Die;
|
|
}
|
|
}
|
|
else if (!strcmp(type, "DemolitionPackDropperClassID"))
|
|
{
|
|
if (
|
|
DemolitionPackDropper::CreateStreamedSubsystem(
|
|
model_file,
|
|
model_name,
|
|
subsystem_name,
|
|
(DemolitionPackDropper::SubsystemResource*)subsystem_resource,
|
|
sys_file,
|
|
resource_file,
|
|
directories
|
|
) == -1
|
|
)
|
|
{
|
|
goto Dump_And_Die;
|
|
}
|
|
}
|
|
else if (!strcmp(type, "LaserDrillClassID"))
|
|
{
|
|
if (
|
|
LaserGun::CreateStreamedSubsystem(
|
|
model_file,
|
|
model_name,
|
|
subsystem_name,
|
|
(LaserGun::SubsystemResource*)subsystem_resource,
|
|
sys_file,
|
|
resource_file,
|
|
directories
|
|
) == -1
|
|
)
|
|
{
|
|
goto Dump_And_Die;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
DEBUG_STREAM << model_name << ':' << subsystem_name
|
|
<< " has an unknown component type of " << type << std::endl << std::flush;
|
|
goto Dump_And_Die;
|
|
}
|
|
|
|
stream.AdvancePointer(subsystem_resource->subsystemModelSize);
|
|
entry = entry->GetNextEntry();
|
|
}
|
|
|
|
//
|
|
//--------------------------------------------------------------------
|
|
// Write the stream out to disk. Size is equal to the byte difference
|
|
// between stream and subsystems
|
|
//--------------------------------------------------------------------
|
|
//
|
|
ResourceDescription
|
|
*new_res = resource_file->AddResource(
|
|
model_name,
|
|
ResourceDescription::SubsystemModelStreamResourceType,
|
|
1,
|
|
ResourceDescription::Preload,
|
|
subsystems,
|
|
(char*)stream.GetPointer() - (char*)subsystems
|
|
);
|
|
Check(new_res);
|
|
|
|
//
|
|
//-------------------------
|
|
// Clean up after ourselves
|
|
//-------------------------
|
|
//
|
|
Unregister_Pointer(subsystems);
|
|
delete[] subsystems;
|
|
Unregister_Object(namelist);
|
|
delete namelist;
|
|
Unregister_Pointer(filename);
|
|
delete filename;
|
|
Unregister_Object(sys_file);
|
|
delete sys_file;
|
|
Check_Fpu();
|
|
return new_res->resourceID;
|
|
}
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
//
|
|
Simulation::SharedData*
|
|
VTV::GetSubsystemSharedData(const char *name)
|
|
{
|
|
Check_Pointer(name);
|
|
|
|
Check_Fpu();
|
|
if (!strcmp(name, ""))
|
|
{
|
|
return &VTV::DefaultData;
|
|
}
|
|
else if (!strcmp(name, "BoosterClassID"))
|
|
{
|
|
return &Booster::DefaultData;
|
|
}
|
|
else if (!strcmp(name, "ChuteClassID"))
|
|
{
|
|
return &Chute::DefaultData;
|
|
}
|
|
else if (!strcmp(name, "Power"))
|
|
{
|
|
return &VTVPower::DefaultData;
|
|
}
|
|
else if (!strcmp(name, "ControlsMapper"))
|
|
{
|
|
return &VTVControlsMapper::DefaultData;
|
|
}
|
|
else if (!strcmp(name, "RivetGunClassID"))
|
|
{
|
|
return &RivetGun::DefaultData;
|
|
}
|
|
else if (!strcmp(name, "LaserDrillClassID"))
|
|
{
|
|
return &LaserGun::DefaultData;
|
|
}
|
|
else if (!strcmp(name, "DemolitionPackDropperClassID"))
|
|
{
|
|
return &DemolitionPackDropper::DefaultData;
|
|
}
|
|
else
|
|
{
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
//
|
|
ResourceDescription::ResourceID
|
|
VTV::CreateControlMappingStream(
|
|
const char* mapping_name,
|
|
NotationFile *mapping_file,
|
|
FindNameFunction find_name,
|
|
ResourceFile *resource_file,
|
|
const char *model_name,
|
|
NotationFile *model_file,
|
|
const ResourceDirectories *directories,
|
|
PlatformTool *current_tool
|
|
)
|
|
{
|
|
Check(mapping_file);
|
|
Check(resource_file);
|
|
Check_Pointer(model_name);
|
|
Check(model_file);
|
|
Check_Pointer(directories);
|
|
|
|
NameList
|
|
*namelist = mapping_file->MakePageList();
|
|
Register_Object(namelist);
|
|
|
|
NameList::Entry
|
|
*entry = namelist->GetFirstEntry();
|
|
int
|
|
count=mapping_file->PageCount();
|
|
|
|
if (!count)
|
|
{
|
|
DEBUG_STREAM << model_name << " is missing control mappings!\n" << std::flush;
|
|
Unwind_1:
|
|
Unregister_Object(namelist);
|
|
delete namelist;
|
|
Check_Fpu();
|
|
return -1;
|
|
}
|
|
|
|
int
|
|
*stream = (int*)new ControlsMapping[(count+1)*6];
|
|
Register_Pointer(stream);
|
|
int
|
|
*mappings = stream;
|
|
*mappings++ = count;
|
|
int
|
|
real_count = 0;
|
|
|
|
//
|
|
//----------------------------------
|
|
// For each page, create one mapping
|
|
//----------------------------------
|
|
//
|
|
for (int i=0; i<count; ++i)
|
|
{
|
|
ControlsMapping
|
|
*mapping = (ControlsMapping*)mappings;
|
|
const char
|
|
*control_name,
|
|
*page_name = entry->GetName();
|
|
if (
|
|
!mapping_file->GetEntry(
|
|
page_name,
|
|
"IOMapping",
|
|
&control_name
|
|
)
|
|
)
|
|
{
|
|
DEBUG_STREAM << model_name << ':' << page_name
|
|
<< " has no IOMapping !\n" << std::flush;
|
|
Unwind_2:
|
|
Unregister_Pointer(stream);
|
|
delete stream;
|
|
goto Unwind_1;
|
|
}
|
|
if (!(*find_name)(control_name, mapping))
|
|
{
|
|
DEBUG_STREAM << model_name << ':' << control_name
|
|
<< " does not exist!\n" << std::flush;
|
|
goto Unwind_2;
|
|
}
|
|
|
|
//
|
|
//--------------------------------------------------------
|
|
// Now, find the right subsystem to attach this control to
|
|
//--------------------------------------------------------
|
|
//
|
|
const char
|
|
*subsystem_name;
|
|
mapping->subsystemID = Entity::EntitySubsystemID;
|
|
char
|
|
subsystem_type[80];
|
|
*subsystem_type = '\0';
|
|
if (
|
|
mapping_file->GetEntry(
|
|
page_name,
|
|
"Subsystem",
|
|
&subsystem_name
|
|
)
|
|
)
|
|
{
|
|
//
|
|
//--------------------------------------------------
|
|
// See if this is one of the always there subsystems
|
|
//--------------------------------------------------
|
|
//
|
|
if (!strcmp(subsystem_name, "ControlsMapper"))
|
|
{
|
|
mapping->subsystemID = ControlsMapperSubsystem;
|
|
Str_Copy(subsystem_type, subsystem_name, sizeof(subsystem_type));
|
|
}
|
|
else if (!strcmp(subsystem_name, "Power"))
|
|
{
|
|
mapping->subsystemID = PowerSubsystem;
|
|
Str_Copy(subsystem_type, subsystem_name, sizeof(subsystem_type));
|
|
}
|
|
else if (!strncmp(subsystem_name, "Thruster", 8))
|
|
{
|
|
mapping->subsystemID =
|
|
Thruster1Subsystem + (subsystem_name[8] - '0');
|
|
Str_Copy(subsystem_type, subsystem_name, sizeof(subsystem_type));
|
|
}
|
|
|
|
//
|
|
//--------------------------------------------------------------
|
|
// Otherwise, we have to open up the subsystem file and find the
|
|
// matching page, keeping track of the count
|
|
//--------------------------------------------------------------
|
|
//
|
|
else
|
|
{
|
|
const char
|
|
*entry_data;
|
|
model_file->GetEntry("gamedata", "Subsystems", &entry_data);
|
|
|
|
char
|
|
*filename =
|
|
MakePathedFilename(directories->modelDirectory, entry_data);
|
|
Register_Pointer(filename);
|
|
|
|
NotationFile
|
|
*sys_file = new NotationFile(
|
|
filename,
|
|
NotationFile::CleanListMode|NotationFile::IgnoreCaseMode
|
|
);
|
|
Register_Object(sys_file);
|
|
|
|
NameList
|
|
*sublist = sys_file->MakePageList();
|
|
Register_Object(sublist);
|
|
|
|
NameList::Entry
|
|
*subsystem_entry = sublist->GetFirstEntry();
|
|
|
|
//
|
|
//-----------------------------------------------------------------
|
|
// Look through all the pages of the subsystem file, looking for
|
|
// our subsystem. When we find it, remember what number it was and
|
|
// what type it was
|
|
//-----------------------------------------------------------------
|
|
//
|
|
mapping->subsystemID = BasicSubsystemCount;
|
|
while (subsystem_entry)
|
|
{
|
|
if (!strcmp(subsystem_entry->GetName(), subsystem_name))
|
|
{
|
|
if (!sys_file->GetEntry(subsystem_name, "Type", &entry_data))
|
|
{
|
|
DEBUG_STREAM <<
|
|
model_name << ':' << subsystem_name <<
|
|
" does not have a 'Type' in file '" <<
|
|
filename << "'!\n" << std::flush;
|
|
}
|
|
Str_Copy(subsystem_type, entry_data, sizeof(subsystem_type));
|
|
subsystem_name = entry_data;
|
|
break;
|
|
}
|
|
subsystem_entry = subsystem_entry->GetNextEntry();
|
|
++mapping->subsystemID;
|
|
}
|
|
|
|
Unregister_Pointer(filename);
|
|
delete filename;
|
|
|
|
if (!subsystem_entry)
|
|
{
|
|
DEBUG_STREAM << model_name << ":subsystem '" << subsystem_name
|
|
<< "' does not exist!\n" << std::flush;
|
|
Unregister_Object(sys_file);
|
|
delete sys_file;
|
|
|
|
Unregister_Object(sublist);
|
|
delete sublist;
|
|
|
|
Unregister_Pointer(stream);
|
|
delete[] stream;
|
|
goto Unwind_1;
|
|
}
|
|
Unregister_Object(sys_file);
|
|
delete sys_file;
|
|
|
|
Unregister_Object(sublist);
|
|
delete sublist;
|
|
}
|
|
}
|
|
|
|
//
|
|
//--------------------------
|
|
// Find the Mode to use
|
|
//--------------------------
|
|
//
|
|
const char
|
|
*entry_string;
|
|
if (
|
|
!mapping_file->GetEntry(
|
|
page_name,
|
|
"Mode",
|
|
&entry_string
|
|
)
|
|
)
|
|
{
|
|
DEBUG_STREAM <<
|
|
model_name << ':' << page_name << " is missing Mode!\n" << std::flush;
|
|
Unwind_3:
|
|
Unregister_Pointer(stream);
|
|
delete[] stream;
|
|
goto Unwind_1;
|
|
}
|
|
|
|
CString
|
|
match_filter_string = entry_string,
|
|
mode_string,
|
|
null_string = NULL;
|
|
|
|
ModeMask
|
|
mode_mask;
|
|
|
|
mapping->modeMask = (ModeMask) 0;
|
|
{
|
|
int
|
|
i;
|
|
|
|
for(
|
|
i=0;
|
|
(mode_string=match_filter_string.GetNthToken(i)) != null_string;
|
|
++i
|
|
)
|
|
{
|
|
Logical
|
|
result = current_tool->ConvertStringToModeMask(
|
|
mode_string,
|
|
&mode_mask
|
|
);
|
|
|
|
if (result == False)
|
|
{
|
|
DEBUG_STREAM <<
|
|
model_name << ':' <<
|
|
page_name << " has an unsupported Mode type '" <<
|
|
mode_string << "'\n" << std::flush;
|
|
goto Unwind_3;
|
|
}
|
|
|
|
mapping->modeMask |= mode_mask;
|
|
}
|
|
|
|
if (i == 0)
|
|
{
|
|
DEBUG_STREAM <<
|
|
model_name << ':' <<
|
|
page_name << " has no Mode specified!\n" << std::flush;
|
|
goto Unwind_3;
|
|
}
|
|
}
|
|
//
|
|
//------------------------------------
|
|
// Find the type of connection to make
|
|
//------------------------------------
|
|
//
|
|
const char
|
|
*type;
|
|
if (
|
|
!mapping_file->GetEntry(
|
|
page_name,
|
|
"Type",
|
|
&type
|
|
)
|
|
)
|
|
{
|
|
DEBUG_STREAM <<
|
|
model_name << ':' <<
|
|
page_name << " is missing type!\n" << std::flush;
|
|
goto Unwind_3;
|
|
}
|
|
|
|
Simulation::SharedData
|
|
*shared_data = GetSubsystemSharedData(subsystem_type);
|
|
if (!shared_data)
|
|
{
|
|
DEBUG_STREAM <<
|
|
model_name << ':' <<
|
|
subsystem_name << " is an unsupported subsystem!\n" << std::flush;
|
|
goto Unwind_3;
|
|
}
|
|
|
|
//
|
|
//----------------------------------------------------------------------
|
|
// If this is an event mapping, we have to have a message ID.
|
|
//----------------------------------------------------------------------
|
|
//
|
|
if (!strcmp(type, "EventMapping"))
|
|
{
|
|
mapping->mappingType = ControlsMapping::EventMapping;
|
|
const char* message;
|
|
if (
|
|
!mapping_file->GetEntry(
|
|
page_name,
|
|
"MessageID",
|
|
&message
|
|
)
|
|
)
|
|
{
|
|
DEBUG_STREAM <<
|
|
model_name << ':' <<
|
|
page_name << " is missing MessageID!\n" << std::flush;
|
|
goto Unwind_3;
|
|
}
|
|
const Receiver::HandlerEntry
|
|
*entry = shared_data->activeMessageHandlers->Find(message);
|
|
if (!entry)
|
|
{
|
|
DEBUG_STREAM <<
|
|
model_name << ':' <<
|
|
subsystem_name << ':' <<
|
|
message << " is an unsupported messageID!\n" << std::flush;
|
|
goto Unwind_3;
|
|
}
|
|
mapping->messageID = entry->entryID;
|
|
}
|
|
|
|
//
|
|
//----------------------------------------------------------------------
|
|
// If this is a direct mapping, we have to have an attribute ID. How do
|
|
// we find an unknown attribute of an unknown subsystem?
|
|
//----------------------------------------------------------------------
|
|
//
|
|
else if (!strcmp(type, "DirectMapping"))
|
|
{
|
|
mapping->mappingType = ControlsMapping::DirectMapping;
|
|
const char
|
|
*attribute;
|
|
if (
|
|
!mapping_file->GetEntry(
|
|
page_name,
|
|
"AttributeID",
|
|
&attribute
|
|
)
|
|
)
|
|
{
|
|
DEBUG_STREAM <<
|
|
model_name << ':' <<
|
|
page_name << " is missing AttributeID!\n" << std::flush;
|
|
goto Unwind_3;
|
|
}
|
|
const Simulation::IndexEntry
|
|
*entry = shared_data->activeAttributeIndex->FindEntry(attribute);
|
|
if (!entry)
|
|
{
|
|
DEBUG_STREAM <<
|
|
model_name << ':' <<
|
|
subsystem_name << ':' <<
|
|
attribute << " is an unsupported AttributeID!\n" << std::flush;
|
|
goto Unwind_3;
|
|
}
|
|
mapping->attributeID = entry->entryID;
|
|
}
|
|
else
|
|
{
|
|
DEBUG_STREAM <<
|
|
model_name << ':' <<
|
|
page_name << " has unknown mapping type!\n" << std::flush;
|
|
goto Unwind_3;
|
|
}
|
|
|
|
mappings = (int*)((char*)mappings + sizeof(ControlsMapping));
|
|
++real_count;
|
|
|
|
//
|
|
//-------------------------
|
|
// Move to the next mapping
|
|
//-------------------------
|
|
//
|
|
entry = entry->GetNextEntry();
|
|
}
|
|
|
|
//
|
|
//--------------------------------------------------------------------
|
|
// Write the stream out to disk. Size is equal to the byte difference
|
|
// between stream and subsystems
|
|
//--------------------------------------------------------------------
|
|
//
|
|
*stream = real_count;
|
|
ResourceDescription
|
|
*new_res = resource_file->AddResource(
|
|
mapping_name,
|
|
ResourceDescription::ControlMappingStreamResourceType,
|
|
1,
|
|
ResourceDescription::Preload,
|
|
stream,
|
|
(char*)mappings - (char*)stream
|
|
);
|
|
Check(new_res);
|
|
|
|
//
|
|
//-------------------------
|
|
// Clean up after ourselves
|
|
//-------------------------
|
|
//
|
|
Unregister_Pointer(stream);
|
|
delete[] stream;
|
|
Unregister_Object(namelist);
|
|
delete namelist;
|
|
Check_Fpu();
|
|
return new_res->resourceID;
|
|
}
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
//
|
|
VTV*
|
|
VTV::Make(VTV::MakeMessage *creation_message)
|
|
{
|
|
Check_Fpu();
|
|
return new VTV(creation_message);
|
|
}
|
|
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
//
|
|
|
|
ResourceDescription::ResourceID
|
|
VTV::CreateDamageZoneStream(
|
|
ResourceFile *resource_file,
|
|
const char *model_name,
|
|
NotationFile *model_file,
|
|
const ResourceDirectories *directories
|
|
)
|
|
{
|
|
Check(resource_file);
|
|
Check_Pointer(model_name);
|
|
Check(model_file);
|
|
Check_Pointer(directories);
|
|
|
|
//
|
|
//-------------------
|
|
// Find the .skl file
|
|
//-------------------
|
|
//
|
|
const char
|
|
*skl_entry;
|
|
if (
|
|
!model_file->GetEntry(
|
|
"video",
|
|
"skeleton",
|
|
&skl_entry
|
|
)
|
|
)
|
|
{
|
|
DEBUG_STREAM << model_name << " is missing .skl file specification!\n" << std::flush;
|
|
Check_Fpu();
|
|
return -1;
|
|
}
|
|
char
|
|
*skl_filename =
|
|
MakePathedFilename(directories->videoDirectory, skl_entry);
|
|
Register_Pointer(skl_filename);
|
|
|
|
NotationFile
|
|
*skl_file = new NotationFile(skl_filename);
|
|
Register_Object(skl_file);
|
|
|
|
if (!skl_file->PageCount())
|
|
{
|
|
DEBUG_STREAM << skl_filename << " is empty or missing!\n" << std::flush;
|
|
Dump_And_Die_1:
|
|
Unregister_Pointer(skl_filename);
|
|
delete[] skl_filename;
|
|
Unregister_Object(skl_file);
|
|
delete skl_file;
|
|
Check_Fpu();
|
|
return ResourceDescription::NullResourceID;
|
|
}
|
|
|
|
//
|
|
//-------------------
|
|
// Find the .dmg file
|
|
//-------------------
|
|
//
|
|
const char
|
|
*dmg_entry;
|
|
if (
|
|
!model_file->GetEntry(
|
|
"gamedata",
|
|
"DamageZones",
|
|
&dmg_entry
|
|
)
|
|
)
|
|
{
|
|
DEBUG_STREAM << model_name << " is missing .dmg file specification!\n" << std::flush;
|
|
goto Dump_And_Die_1;
|
|
}
|
|
|
|
char
|
|
*dmg_filename =
|
|
MakePathedFilename(directories->modelDirectory, dmg_entry);
|
|
Register_Pointer(dmg_filename);
|
|
|
|
NotationFile
|
|
*dmg_file = new NotationFile(dmg_filename);
|
|
Register_Object(dmg_file);
|
|
|
|
if (!dmg_file->PageCount())
|
|
{
|
|
DEBUG_STREAM << dmg_filename << " is empty or missing!\n" << std::flush;
|
|
Dump_And_Die_2:
|
|
Unregister_Pointer(dmg_filename);
|
|
delete[] dmg_filename;
|
|
Unregister_Object(dmg_file);
|
|
delete dmg_file;
|
|
goto Dump_And_Die_1;
|
|
}
|
|
|
|
//
|
|
// Get the number of Damage zones
|
|
//
|
|
int
|
|
dzone_count;
|
|
if (
|
|
!skl_file->GetEntry(
|
|
"ROOT",
|
|
"DZoneCount",
|
|
&dzone_count
|
|
)
|
|
)
|
|
{
|
|
DEBUG_STREAM << model_name << " is missing DZoneCount \n" << std::flush;
|
|
goto Dump_And_Die_2;
|
|
}
|
|
|
|
//
|
|
// Make a stream big enough for all the damage zones
|
|
//
|
|
DynamicMemoryStream
|
|
damage_zone_stream;
|
|
|
|
//
|
|
// write the Damage Zone Count Info from the
|
|
// .dmg file to the stream
|
|
//
|
|
damage_zone_stream << dzone_count;
|
|
|
|
//
|
|
// Make an entry list of all the entries in the damage zone page
|
|
//
|
|
NameList
|
|
*dzone_namelist = skl_file->MakeEntryList("DamageZones","dz_");
|
|
Register_Object(dzone_namelist);
|
|
|
|
if (dzone_namelist->EntryCount() == 0)
|
|
{
|
|
DEBUG_STREAM << "No dZones listed in DamageZones Page"<<std::endl << std::flush;
|
|
Dump_And_Die:
|
|
Unregister_Pointer(skl_filename);
|
|
delete skl_filename;
|
|
Unregister_Object(skl_file);
|
|
delete skl_file;
|
|
Unregister_Pointer(dmg_filename);
|
|
delete dmg_filename;
|
|
Unregister_Object(dmg_file);
|
|
delete dmg_file;
|
|
Check_Fpu();
|
|
return -1;
|
|
}
|
|
NameList::Entry
|
|
*dzone_entry;
|
|
char
|
|
current_dzone_name[32];
|
|
int
|
|
num_dzones_found = 0;
|
|
|
|
dzone_entry = dzone_namelist->GetFirstEntry();
|
|
|
|
while (dzone_entry)
|
|
{
|
|
++num_dzones_found;
|
|
//
|
|
// Get dzone name in .skl file
|
|
//
|
|
Str_Copy(
|
|
current_dzone_name,
|
|
dzone_entry->GetName(),
|
|
sizeof(current_dzone_name)
|
|
);
|
|
|
|
//
|
|
// Create the stream for this damage zone
|
|
// This Should Default to VTV::DamageZone::Create
|
|
//
|
|
VTV__DamageZone::CreateStreamedDamageZone(
|
|
model_file,
|
|
model_name,
|
|
skl_file,
|
|
current_dzone_name,
|
|
&damage_zone_stream,
|
|
dmg_file,
|
|
directories
|
|
);
|
|
//
|
|
// Get next dzone entry in this segment page
|
|
//
|
|
dzone_entry = dzone_entry->GetNextEntry();
|
|
} // End while more dzone_entries
|
|
|
|
if(dzone_count != num_dzones_found)
|
|
{
|
|
DEBUG_STREAM <<
|
|
"DZoneCount != damage zones found in Page DamageZones"<<std::endl << std::flush;
|
|
goto Dump_And_Die;
|
|
}
|
|
|
|
//
|
|
//--------------------------------------------------------------------
|
|
// Write the stream out to disk. Size is equal to the byte difference
|
|
// between stream and damage zone buffer
|
|
//--------------------------------------------------------------------
|
|
//
|
|
ResourceDescription
|
|
*new_res = resource_file->AddResourceMemoryStream(
|
|
model_name,
|
|
ResourceDescription::DamageZoneStreamResourceType,
|
|
1,
|
|
ResourceDescription::Preload,
|
|
&damage_zone_stream
|
|
);
|
|
Check(new_res);
|
|
//
|
|
// Free mem
|
|
//
|
|
Unregister_Pointer(skl_filename);
|
|
delete skl_filename;
|
|
Unregister_Object(skl_file);
|
|
delete skl_file;
|
|
Unregister_Pointer(dmg_filename);
|
|
delete dmg_filename;
|
|
Unregister_Object(dmg_file);
|
|
delete dmg_file;
|
|
Unregister_Object(dzone_namelist);
|
|
delete dzone_namelist;
|
|
Check_Fpu();
|
|
return new_res->resourceID;
|
|
}
|
|
|
|
//#############################################################################
|
|
//VTV::DamageZone
|
|
//
|
|
void
|
|
VTV__DamageZone::TakeDamage(Damage& damage)
|
|
{
|
|
Check(this);
|
|
Check_Pointer(&damage);
|
|
|
|
DamageZone::TakeDamage(damage);
|
|
|
|
//
|
|
//---------------------------------
|
|
// If the VTV is dead, make it burn
|
|
//---------------------------------
|
|
//
|
|
VTV
|
|
*vtv = (VTV*)GetOwningSimulation();
|
|
Check(vtv);
|
|
if (damageLevel >= 1.0f)
|
|
{
|
|
vtv->SetSimulationState(VTV::BurningState);
|
|
}
|
|
|
|
//
|
|
//----------------------------------------------------------------------
|
|
// If this VTV is linked to a player, send the score loss to that player
|
|
//----------------------------------------------------------------------
|
|
//
|
|
RPPlayer
|
|
*rp_player = (RPPlayer*) vtv->GetPlayerLink();
|
|
if (rp_player)
|
|
{
|
|
Check(rp_player);
|
|
//
|
|
//--------------------------------------------------------
|
|
// If we died, send the I'm dead now message to the player
|
|
//--------------------------------------------------------
|
|
//
|
|
if (
|
|
vtv->GetSimulationState() == VTV::BurningState &&
|
|
vtv->simulationState.GetOldState() != VTV::BurningState
|
|
)
|
|
{
|
|
RPPlayer::VehicleDeadMessage
|
|
dead(
|
|
RPPlayer::VehicleDeadMessageID,
|
|
sizeof(RPPlayer::VehicleDeadMessage)
|
|
);
|
|
rp_player->Dispatch(&dead);
|
|
}
|
|
|
|
//
|
|
//-------------------------------------------
|
|
// Notify this player that damaged was caused
|
|
//-------------------------------------------
|
|
//
|
|
RPPlayer::TakeDamageMessage
|
|
damage_message(
|
|
RPPlayer::TakeDamageMessageID,
|
|
sizeof(RPPlayer::TakeDamageMessage),
|
|
rp_player->GetEntityID(),
|
|
0,
|
|
damage
|
|
);
|
|
rp_player->Dispatch(&damage_message);
|
|
}
|
|
Check_Fpu();
|
|
}
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
//
|
|
VTV__DamageZone::VTV__DamageZone(VTV *vtv, int damage_zone_index, MemoryStream *damage_zone_stream) :
|
|
DamageZone(vtv, damage_zone_index, damage_zone_stream)
|
|
{
|
|
Scalar
|
|
dmg_data(vtv->maxImpactSpeed);
|
|
|
|
dmg_data /= 3.6f;
|
|
|
|
damageScale[Damage::CollisionDamageType] =
|
|
2000.0f
|
|
/ vtv->moverMass
|
|
/ (dmg_data*dmg_data)
|
|
/ (
|
|
1.0f
|
|
- vtv->elasticityCoefficient
|
|
* vtv->elasticityCoefficient
|
|
);
|
|
}
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
//
|
|
Logical
|
|
VTV__DamageZone::CreateStreamedDamageZone(
|
|
NotationFile *model_file,
|
|
const char *model_name,
|
|
NotationFile *skl_file,
|
|
const char *damage_zone_name,
|
|
MemoryStream *damage_zone_stream,
|
|
NotationFile *dmg_file,
|
|
const ResourceDirectories *directories
|
|
)
|
|
{
|
|
if(!DamageZone::CreateStreamedDamageZone(
|
|
model_file,
|
|
model_name,
|
|
skl_file,
|
|
damage_zone_name,
|
|
damage_zone_stream,
|
|
dmg_file,
|
|
directories
|
|
)
|
|
)
|
|
{
|
|
Check_Fpu();
|
|
return -1;
|
|
}
|
|
Check_Fpu();
|
|
return True;
|
|
}
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
//
|
|
Logical
|
|
VTV::TestInstance() const
|
|
{
|
|
return IsDerivedFrom(*GetClassDerivations());
|
|
}
|
|
|