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>
582 lines
14 KiB
C++
582 lines
14 KiB
C++
#include "rp.h"
|
|
#pragma hdrstop
|
|
|
|
#include "demopack.h"
|
|
#include "..\munga\line.h"
|
|
#include "..\munga\explode.h"
|
|
#include "..\munga\boxsolid.h"
|
|
#include "..\munga\collasst.h"
|
|
#include "..\munga\app.h"
|
|
#include "..\munga\interest.h"
|
|
#include "vtv.h"
|
|
#include "..\munga\hostmgr.h"
|
|
#include "..\munga\notation.h"
|
|
|
|
//#############################################################################
|
|
// Shared Data Support
|
|
//
|
|
Derivation* DemolitionPack::GetClassDerivations()
|
|
{ static Derivation classDerivations(Rivet::GetClassDerivations(), "DemolitionPack");
|
|
return &classDerivations;
|
|
}
|
|
|
|
|
|
DemolitionPack::SharedData
|
|
DemolitionPack::DefaultData(
|
|
DemolitionPack::GetClassDerivations(),
|
|
DemolitionPack::GetMessageHandlers(),
|
|
DemolitionPack::GetAttributeIndex(),
|
|
DemolitionPack::StateCount,
|
|
(Entity::MakeHandler)DemolitionPack::Make
|
|
);
|
|
|
|
//#############################################################################
|
|
// Attribute Support
|
|
//
|
|
#if 0
|
|
const DemolitionPack::IndexEntry
|
|
DemolitionPack::AttributePointers[]=
|
|
{
|
|
ATTRIBUTE_ENTRY(DemolitionPack, DamageData, damageData)
|
|
};
|
|
|
|
DemolitionPack::AttributeIndexSet& DemolitionPack::GetAttributeIndex()
|
|
{
|
|
static DemolitionPack::AttributeIndexSet attributeIndex(ELEMENTS(DemolitionPack::AttributePointers), DemolitionPack::AttributePointers, Mover::GetAttributeIndex());
|
|
return attributeIndex;
|
|
}
|
|
#endif
|
|
|
|
//#############################################################################
|
|
// Model support
|
|
//
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
//
|
|
void
|
|
DemolitionPack::MoveAndHunt(Scalar time_slice)
|
|
{
|
|
Line
|
|
distance_traveled;
|
|
distance_traveled = localOrigin.linearPosition;
|
|
|
|
//
|
|
//-----------------------------------------------------------------
|
|
// Apply the standard air resistance and gravity, then project the
|
|
// Projectile motion through the time slice and look for collisions
|
|
//-----------------------------------------------------------------
|
|
//
|
|
ApplyAirResistanceAndGravity();
|
|
Point3D old_position(localOrigin.linearPosition);
|
|
ApplyWorldAccelerations(time_slice);
|
|
|
|
//
|
|
//--------------------
|
|
// Do a collision test
|
|
//--------------------
|
|
//
|
|
MoveCollisionVolume();
|
|
BoxedSolidCollisionList*
|
|
collision_list = GetCurrentCollisions();
|
|
|
|
//
|
|
//---------------------------------------------------------------------
|
|
// If our solid didn't hit anything, and we are going fast enough, do a
|
|
// ray-cast
|
|
//---------------------------------------------------------------------
|
|
//
|
|
if (!collision_list->GetCollisionCount())
|
|
{
|
|
Line
|
|
line;
|
|
line = localOrigin.linearPosition;
|
|
Vector3D
|
|
step;
|
|
step.Subtract(localOrigin.linearPosition, old_position);
|
|
line = step;
|
|
Scalar
|
|
diameter = collisionTemplate->maxX - collisionTemplate->minX;
|
|
if (diameter < line.length)
|
|
{
|
|
CollideCenterOfMotion(&line, collision_list);
|
|
}
|
|
}
|
|
|
|
//
|
|
//-------------------------------
|
|
// Process each of the collisions
|
|
//-------------------------------
|
|
//
|
|
Damage collision_damage;
|
|
ProcessCollisionList(
|
|
collision_list,
|
|
time_slice,
|
|
old_position,
|
|
&collision_damage
|
|
);
|
|
|
|
(this->*deadReckoner)();
|
|
Vector3D
|
|
error;
|
|
error.Subtract(
|
|
projectedOrigin.linearPosition,
|
|
localOrigin.linearPosition
|
|
);
|
|
if (error.LengthSquared() > 0.5f)
|
|
{
|
|
ForceUpdate();
|
|
}
|
|
|
|
//
|
|
//----------------------
|
|
// Hunt for VTVs to kill
|
|
//----------------------
|
|
//
|
|
Check_Fpu();
|
|
Hunt(time_slice);
|
|
}
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
//
|
|
void
|
|
DemolitionPack::Hunt(Scalar)
|
|
{
|
|
if ((lastPerformance - creationTime) >= fuseTime)
|
|
{
|
|
//
|
|
//---------------------------------
|
|
// Test against the tangible movers
|
|
//---------------------------------
|
|
//
|
|
Check(collisionAssistant);
|
|
CollisionAssistant::MovingEntityIterator
|
|
iterator(collisionAssistant);
|
|
Entity
|
|
*entity;
|
|
Check(collisionVolume);
|
|
while ((entity = iterator.ReadAndNext()) != NULL)
|
|
{
|
|
//
|
|
//------------------------------------------
|
|
// If the mover is not within range, no boom
|
|
//------------------------------------------
|
|
//
|
|
Vector3D
|
|
range;
|
|
range.Subtract(
|
|
entity->localOrigin.linearPosition,
|
|
localOrigin.linearPosition
|
|
);
|
|
if ((range.LengthSquared() >= detectionRadiusSquared) && ((lastPerformance - creationTime) <= (fuseTime + 60.0f)))
|
|
{
|
|
continue;
|
|
}
|
|
|
|
//
|
|
//----------------------------------
|
|
// We got a boom! Make an explosion
|
|
//----------------------------------
|
|
//
|
|
CondemnToDeathRow();
|
|
Check(application);
|
|
ResourceFile *resource_file = application->GetResourceFile();
|
|
Check(resource_file);
|
|
ResourceDescription
|
|
*explosion_res = resource_file->
|
|
FindResourceDescription(explosionResourceID);
|
|
Check(explosion_res);
|
|
explosion_res->Lock();
|
|
|
|
Explosion::MakeMessage exp_message(
|
|
Explosion::MakeMessageID,
|
|
sizeof(Explosion::MakeMessage),
|
|
ExplosionClassID,
|
|
EntityID::Null,
|
|
explosion_res->resourceID,
|
|
Explosion::DefaultFlags,
|
|
localOrigin,
|
|
entity->GetEntityID(),
|
|
shootingEntity->GetEntityID()
|
|
);
|
|
|
|
explosion_res->Unlock();
|
|
|
|
#if DEBUG_LEVEL>0
|
|
Explosion
|
|
*exp =
|
|
#endif
|
|
Explosion::Make(&exp_message);
|
|
Register_Object(exp);
|
|
|
|
//
|
|
//----------------------------------------------
|
|
// Send a damage message to whoever we hit
|
|
//----------------------------------------------
|
|
//
|
|
if ((lastPerformance - creationTime) <= (fuseTime + 60.0f))
|
|
{
|
|
iterator.First();
|
|
while ((entity = iterator.ReadAndNext()) != NULL)
|
|
{
|
|
if (entity->GetClassID() == RegisteredClass::VTVClassID)
|
|
{
|
|
VTV *vtv = Cast_Object(VTV*, entity);
|
|
Damage damage_data;
|
|
damage_data.damageForce.Subtract(
|
|
vtv->localOrigin.linearPosition,
|
|
localOrigin.linearPosition
|
|
);
|
|
if (damage_data.damageForce.LengthSquared() > blastRadiusSquared)
|
|
{
|
|
continue;
|
|
}
|
|
damage_data.damageForce.Normalize(damage_data.damageForce);
|
|
damage_data.damageForce *= concussiveForce;
|
|
damage_data.damageAmount = damageData.damageAmount;
|
|
damage_data.damageType = Damage::ExplosiveDamageType;
|
|
//
|
|
//------------------------------------------
|
|
// Send a damage message to the thing we hit
|
|
//------------------------------------------
|
|
//
|
|
Entity::TakeDamageMessage
|
|
take_damage(
|
|
Entity::TakeDamageMessageID,
|
|
sizeof(Entity::TakeDamageMessage),
|
|
shootingEntity->GetEntityID(),
|
|
0,
|
|
damage_data
|
|
);
|
|
entity->Dispatch(&take_damage);
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
Check_Fpu();
|
|
}
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
//
|
|
void
|
|
DemolitionPack::ProcessCollision(
|
|
Scalar time_slice,
|
|
BoxedSolidCollision &collision,
|
|
const Point3D &old_position,
|
|
Damage *damage
|
|
)
|
|
{
|
|
Scalar penetration,r;
|
|
Scalar elasticity = elasticityCoefficient;
|
|
Scalar friction = frictionCoefficient;
|
|
|
|
//
|
|
//-------------------------------------------------------------------------
|
|
// If we have hit a mover, adjust our velocity relative to it, unless we
|
|
// have collided with the VTV what dropped us, in which case we will ignore
|
|
// the hit
|
|
//-------------------------------------------------------------------------
|
|
//
|
|
BoxedSolid *box = collision.GetTreeVolume();
|
|
Check(box);
|
|
Simulation *sim = box->GetOwningSimulation();
|
|
Mover *mover = NULL;
|
|
if (sim && sim->IsDerivedFrom(*Mover::GetClassDerivations()))
|
|
{
|
|
mover = (Mover*)sim;
|
|
Check(mover);
|
|
if (mover == shootingEntity)
|
|
{
|
|
Check_Fpu();
|
|
return;
|
|
}
|
|
worldLinearVelocity -= mover->worldLinearVelocity;
|
|
}
|
|
|
|
//
|
|
//--------------------------------------------------------------------
|
|
// If we really have a collision, do a static bounce off of the normal
|
|
// generated
|
|
//--------------------------------------------------------------------
|
|
//
|
|
if (
|
|
collisionVolume->ProcessCollision(
|
|
collision,
|
|
worldLinearVelocity,
|
|
lastCollisionList,
|
|
&damage->surfaceNormal,
|
|
&penetration
|
|
)
|
|
)
|
|
{
|
|
Max_Clamp(penetration, time_slice);
|
|
r = penetration / time_slice;
|
|
Check_Fpu();
|
|
|
|
StaticBounce(
|
|
old_position,
|
|
time_slice,
|
|
r,
|
|
damage->surfaceNormal,
|
|
&elasticity,
|
|
minimumBounceSpeed,
|
|
&friction
|
|
);
|
|
|
|
//
|
|
//--------------------------
|
|
// Determine collision state
|
|
//--------------------------
|
|
//
|
|
if (!elasticity && !friction && !mover)
|
|
{
|
|
SetPerformance(&DemolitionPack::Hunt);
|
|
worldLinearVelocity = Vector3D::Identity;
|
|
worldLinearAcceleration = Vector3D::Identity;
|
|
localVelocity = Motion::Identity;
|
|
localAcceleration = Motion::Identity;
|
|
ForceUpdate();
|
|
mover = NULL;
|
|
}
|
|
}
|
|
if (mover)
|
|
{
|
|
worldLinearVelocity += mover->worldLinearVelocity;
|
|
}
|
|
Check_Fpu();
|
|
}
|
|
|
|
//#############################################################################
|
|
// Construction and Destruction
|
|
//
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
//
|
|
DemolitionPack::DemolitionPack(
|
|
DemolitionPack::MakeMessage *creation_message,
|
|
DemolitionPack::SharedData &virtual_data
|
|
):
|
|
Rivet(creation_message, virtual_data)
|
|
{
|
|
Check_Pointer(this);
|
|
|
|
//
|
|
//------------------------
|
|
// Setup the initial state
|
|
//------------------------
|
|
//
|
|
|
|
SetValidFlag();
|
|
InterestZone *interest_zone;
|
|
|
|
Check(application);
|
|
Check(application->GetInterestManager());
|
|
interest_zone =
|
|
application->GetInterestManager()->GetInterestZone(interestZoneID);
|
|
Check(interest_zone);
|
|
localEnvironment = interest_zone->GetEnvironment();
|
|
Check(localEnvironment);
|
|
|
|
//
|
|
//
|
|
SetDeadReckoner(&DemolitionPack::AcceleratedDeadReckoner);
|
|
if (GetInstance() == ReplicantInstance)
|
|
{
|
|
SetPerformance(&DemolitionPack::DeadReckon);
|
|
}
|
|
else
|
|
{
|
|
SetPerformance(&DemolitionPack::MoveAndHunt);
|
|
}
|
|
|
|
//
|
|
// Initialize the Projectile variables
|
|
//
|
|
ResourceDescription *res =
|
|
application->GetResourceFile()->SearchList(
|
|
resourceID,
|
|
ResourceDescription::GameModelResourceType
|
|
);
|
|
Check(res);
|
|
res->Lock();
|
|
|
|
ModelResource* model = (ModelResource*)res->resourceAddress;
|
|
Check_Pointer(model);
|
|
blastRadiusSquared = model->blastRadiusSquared;
|
|
detectionRadiusSquared = model->detectionRadiusSquared;
|
|
fuseTime = model->fuseTime;
|
|
concussiveForce = model->concussiveForce;
|
|
damageData = model->damageData;
|
|
explosionResourceID = model->explosionResourceID;
|
|
|
|
res->Unlock();
|
|
|
|
HostManager *host_mgr = application->GetHostManager();
|
|
Check(host_mgr);
|
|
shootingEntity =
|
|
host_mgr->GetEntityPointer(creation_message->shootingEntity);
|
|
Check_Fpu();
|
|
}
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
//
|
|
DemolitionPack*
|
|
DemolitionPack::Make(DemolitionPack::MakeMessage *creation_message)
|
|
{
|
|
return new DemolitionPack(creation_message);
|
|
}
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
//
|
|
DemolitionPack::~DemolitionPack()
|
|
{
|
|
Check(this);
|
|
Check_Fpu();
|
|
}
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
// CreateModelResource
|
|
//
|
|
int
|
|
DemolitionPack::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 mover stuff is read in
|
|
//-----------------------------------------------------------------------
|
|
//
|
|
ModelResource *local_model = model;
|
|
if (!local_model)
|
|
{
|
|
local_model = new ModelResource;
|
|
Register_Pointer(local_model);
|
|
}
|
|
|
|
if(
|
|
Rivet::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;
|
|
}
|
|
|
|
//
|
|
//---------------------------------------
|
|
// Read in the damage amount for the ammo
|
|
//---------------------------------------
|
|
//
|
|
if(
|
|
!model_file->GetEntry(
|
|
"gamedata",
|
|
"BlastRadius",
|
|
&local_model->blastRadiusSquared
|
|
)
|
|
)
|
|
{
|
|
std::cout << model_name << " missing BlastRadius!\n";
|
|
goto Dump_And_Die;
|
|
}
|
|
{
|
|
local_model->blastRadiusSquared *= local_model->blastRadiusSquared;
|
|
}
|
|
if(
|
|
!model_file->GetEntry(
|
|
"gamedata",
|
|
"DetectionRadius",
|
|
&local_model->detectionRadiusSquared
|
|
)
|
|
)
|
|
{
|
|
std::cout << model_name << " missing DetectionRadius!\n";
|
|
goto Dump_And_Die;
|
|
}
|
|
else
|
|
{
|
|
local_model->detectionRadiusSquared *= local_model->detectionRadiusSquared;
|
|
}
|
|
if(
|
|
!model_file->GetEntry(
|
|
"gamedata",
|
|
"ConcussiveForce",
|
|
&local_model->concussiveForce
|
|
)
|
|
)
|
|
{
|
|
std::cout << model_name << " missing ConcussiveForce!\n";
|
|
goto Dump_And_Die;
|
|
}
|
|
if(
|
|
!model_file->GetEntry(
|
|
"gamedata",
|
|
"FuseTime",
|
|
&local_model->fuseTime
|
|
)
|
|
)
|
|
{
|
|
std::cout << model_name << " missing FuseTime!\n";
|
|
goto Dump_And_Die;
|
|
}
|
|
|
|
//
|
|
//-------------------------------------------------------------------------
|
|
// 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;
|
|
Check(new_res);
|
|
Check_Fpu();
|
|
return new_res->resourceID;
|
|
}
|
|
else
|
|
{
|
|
Check_Fpu();
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
//
|
|
Logical
|
|
DemolitionPack::TestInstance() const
|
|
{
|
|
return IsDerivedFrom(*GetClassDerivations());
|
|
}
|