Files
CydandClaude Opus 4.8 4abbf8879f Initial import of Red Planet v4.10 Win32 source
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>
2026-06-30 07:59:51 -05:00

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());
}