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>
504 lines
12 KiB
C++
504 lines
12 KiB
C++
#include "rp.h"
|
|
#pragma hdrstop
|
|
|
|
#include "rivet.h"
|
|
#include "..\munga\line.h"
|
|
#include "..\munga\explode.h"
|
|
#include "..\munga\boxsolid.h"
|
|
#include "..\munga\subsystm.h"
|
|
#include "..\munga\app.h"
|
|
#include "..\munga\hostmgr.h"
|
|
#include "..\munga\interest.h"
|
|
#include "..\munga\notation.h"
|
|
|
|
//#############################################################################
|
|
// Shared Data Support
|
|
//
|
|
Derivation* Rivet::GetClassDerivations()
|
|
{ static Derivation classDerivations(Mover::GetClassDerivations(), "Rivet");
|
|
return &classDerivations;
|
|
}
|
|
|
|
|
|
Rivet::SharedData
|
|
Rivet::DefaultData(
|
|
Rivet::GetClassDerivations(),
|
|
Rivet::GetMessageHandlers(),
|
|
Rivet::GetAttributeIndex(),
|
|
Rivet::StateCount,
|
|
(Entity::MakeHandler)Rivet::Make
|
|
);
|
|
|
|
//#############################################################################
|
|
// Attribute Support
|
|
//
|
|
const Rivet::IndexEntry
|
|
Rivet::AttributePointers[]=
|
|
{
|
|
ATTRIBUTE_ENTRY(Rivet, DamageData, damageData)
|
|
};
|
|
|
|
Rivet::AttributeIndexSet& Rivet::GetAttributeIndex()
|
|
{
|
|
static Rivet::AttributeIndexSet attributeIndex(ELEMENTS(Rivet::AttributePointers), Rivet::AttributePointers, Mover::GetAttributeIndex());
|
|
return attributeIndex;
|
|
}
|
|
|
|
//#############################################################################
|
|
// Model support
|
|
//
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
//
|
|
void
|
|
Rivet::MoveToTarget(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();
|
|
ApplyWorldAccelerations(time_slice);
|
|
|
|
Vector3D
|
|
motion_vector;
|
|
motion_vector.Subtract(
|
|
localOrigin.linearPosition,
|
|
distance_traveled.origin
|
|
);
|
|
distance_traveled = motion_vector;
|
|
Check(&distance_traveled);
|
|
localToWorld = localOrigin;
|
|
|
|
BoxedSolid
|
|
*target = FindBoxedSolidHitBy(&distance_traveled, shootingEntity);
|
|
//
|
|
//-------------------------------------------------------
|
|
// If the projectile hit something, sentence it to death!
|
|
//-------------------------------------------------------
|
|
//
|
|
if (target)
|
|
{
|
|
CondemnToDeathRow();
|
|
Origin
|
|
explode_origin = localOrigin;
|
|
distance_traveled.FindEnd(&explode_origin.linearPosition);
|
|
|
|
//
|
|
//-----------------------
|
|
// Find the entity we hit
|
|
//-----------------------
|
|
//
|
|
Simulation
|
|
*sim = target->GetOwningSimulation();
|
|
Check(sim);
|
|
Entity
|
|
*entity;
|
|
if (sim->IsDerivedFrom(*Subsystem::GetClassDerivations()))
|
|
{
|
|
Check((Subsystem*)sim);
|
|
entity = ((Subsystem*)sim)->GetEntity();
|
|
}
|
|
else if (sim->IsDerivedFrom(*Mover::GetClassDerivations()))
|
|
{
|
|
Mover
|
|
*mover = (Mover*)sim;
|
|
Check(mover);
|
|
|
|
Vector3D
|
|
impact_speed;
|
|
|
|
impact_speed.Subtract(worldLinearVelocity, mover->worldLinearVelocity);
|
|
Scalar
|
|
damage_scale = impact_speed.Length() / 350.0f; // HACK
|
|
damageData.damageAmount *= damage_scale*damage_scale;
|
|
entity = mover;
|
|
}
|
|
else
|
|
{
|
|
entity = (Entity*)sim;
|
|
}
|
|
Check(entity);
|
|
|
|
//
|
|
//------------------
|
|
// Make an Explosion
|
|
//------------------
|
|
//
|
|
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,
|
|
explode_origin,
|
|
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 the thing we hit
|
|
//------------------------------------------
|
|
//
|
|
Entity::TakeDamageMessage
|
|
take_damage(
|
|
Entity::TakeDamageMessageID,
|
|
sizeof(Entity::TakeDamageMessage),
|
|
shootingEntity->GetEntityID(),
|
|
0,
|
|
damageData
|
|
);
|
|
entity->Dispatch(&take_damage);
|
|
}
|
|
|
|
//
|
|
//---------------------------------------------
|
|
// Otherwise, see if we have to update anything
|
|
//---------------------------------------------
|
|
//
|
|
else
|
|
{
|
|
UpdateLocalMotion();
|
|
|
|
//
|
|
//------------------------------------------------------------------------
|
|
// Run the dead reckoner, and then see how far apart the two positions are
|
|
//------------------------------------------------------------------------
|
|
//
|
|
(this->*deadReckoner)();
|
|
Vector3D
|
|
error;
|
|
error.Subtract(
|
|
projectedOrigin.linearPosition,
|
|
localOrigin.linearPosition
|
|
);
|
|
if (error.LengthSquared() > 0.1f)
|
|
{
|
|
ForceUpdate();
|
|
}
|
|
}
|
|
|
|
//
|
|
//---------------------------------------------------
|
|
// If the bullet has outlived its lifetime, delete it
|
|
//---------------------------------------------------
|
|
//
|
|
if ((Now() - creationTime) > RIVET_MAXIMUM_LIFETIME)
|
|
{
|
|
CondemnToDeathRow();
|
|
}
|
|
Check_Fpu();
|
|
}
|
|
|
|
//#############################################################################
|
|
// Construction and Destruction
|
|
//
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
//
|
|
Rivet::Rivet(
|
|
Rivet::MakeMessage *creation_message,
|
|
Rivet::SharedData &virtual_data
|
|
):
|
|
Mover(creation_message, virtual_data)
|
|
{
|
|
Check_Pointer(this);
|
|
|
|
//
|
|
//------------------------
|
|
// Setup the initial state
|
|
//------------------------
|
|
//
|
|
|
|
SetValidFlag();
|
|
|
|
Check(application);
|
|
Check(application->GetInterestManager());
|
|
InterestZone
|
|
*interest_zone = application->GetInterestManager()->
|
|
GetInterestZone(interestZoneID);
|
|
Check(interest_zone);
|
|
|
|
localEnvironment = interest_zone->GetEnvironment();
|
|
Check(localEnvironment);
|
|
|
|
//
|
|
//
|
|
SetDeadReckoner(&Rivet::AcceleratedDeadReckoner);
|
|
if (GetInstance() == ReplicantInstance)
|
|
{
|
|
SetPerformance(&Rivet::DeadReckon);
|
|
}
|
|
else
|
|
{
|
|
SetPerformance(&Rivet::MoveToTarget);
|
|
StartCollisionAssistant();
|
|
}
|
|
|
|
//
|
|
// Initialize the Projectile variables
|
|
//
|
|
ResourceDescription *res =
|
|
application->GetResourceFile()->SearchList(
|
|
resourceID,
|
|
ResourceDescription::GameModelResourceType
|
|
);
|
|
Check(res);
|
|
res->Lock();
|
|
|
|
ModelResource
|
|
*model = (ModelResource*)res->resourceAddress;
|
|
Check_Pointer(model);
|
|
|
|
damageData.damageType = model->damageData.damageType;
|
|
damageData.damageAmount = model->damageData.damageAmount;
|
|
damageData.damageForce = Vector3D::Identity;
|
|
damageData.surfaceNormal.x = 1.0f;
|
|
damageData.surfaceNormal.y = 0.0f;
|
|
damageData.surfaceNormal.z = 0.0f;
|
|
damageData.impactPoint = Point3D::Identity;
|
|
explosionResourceID = model->explosionResourceID;
|
|
|
|
res->Unlock();
|
|
|
|
HostManager
|
|
*host_mgr = application->GetHostManager();
|
|
Check(host_mgr);
|
|
|
|
shootingEntity =
|
|
host_mgr->GetEntityPointer(creation_message->shootingEntity);
|
|
Check_Fpu();
|
|
}
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
//
|
|
Rivet*
|
|
Rivet::Make(Rivet::MakeMessage *creation_message)
|
|
{
|
|
return new Rivet(creation_message);
|
|
}
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
//
|
|
Rivet::~Rivet()
|
|
{
|
|
Check(this);
|
|
Check_Fpu();
|
|
}
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
// CreateModelResource
|
|
//
|
|
int
|
|
Rivet::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(
|
|
Mover::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",
|
|
"DamageAmount",
|
|
&local_model->damageData.damageAmount
|
|
)
|
|
)
|
|
{
|
|
std::cout << model_name << " missing DamageAmount!\n";
|
|
goto Dump_And_Die;
|
|
}
|
|
|
|
//
|
|
//----------------------------------------------------------------------
|
|
// Read in the Model Filename of the Explosion renderable, then find its
|
|
// resource ID in the resource file
|
|
//----------------------------------------------------------------------
|
|
//
|
|
const char
|
|
*damage_type;
|
|
if(
|
|
!model_file->GetEntry(
|
|
"gamedata",
|
|
"DamageType",
|
|
&damage_type
|
|
)
|
|
)
|
|
{
|
|
std::cout << model_name << " missing DamageType!\n";
|
|
goto Dump_And_Die;
|
|
}
|
|
if (!stricmp(damage_type, "CollisionDamage"))
|
|
{
|
|
local_model->damageData.damageType = Damage::CollisionDamageType;
|
|
}
|
|
else if (!stricmp(damage_type, "BallisticDamage"))
|
|
{
|
|
local_model->damageData.damageType = Damage::BallisticDamageType;
|
|
}
|
|
else if (!stricmp(damage_type, "ExplosiveDamage"))
|
|
{
|
|
local_model->damageData.damageType = Damage::ExplosiveDamageType;
|
|
}
|
|
else if (!stricmp(damage_type, "LaserDamage"))
|
|
{
|
|
local_model->damageData.damageType = Damage::LaserDamageType;
|
|
}
|
|
else if (!stricmp(damage_type, "EnergyDamage"))
|
|
{
|
|
local_model->damageData.damageType = Damage::EnergyDamageType;
|
|
}
|
|
else
|
|
{
|
|
std::cout << model_name << " has an unknown DamageType of " << damage_type
|
|
<< std::endl;
|
|
goto Dump_And_Die;
|
|
}
|
|
//
|
|
//----------------------------
|
|
// Init damage fields
|
|
//----------------------------
|
|
//
|
|
local_model->damageData.damageForce = Vector3D::Identity;
|
|
local_model->damageData.surfaceNormal.x = 0.0f;
|
|
local_model->damageData.surfaceNormal.y = 1.0f;
|
|
local_model->damageData.surfaceNormal.z = 0.0f;
|
|
local_model->damageData.impactPoint = Vector3D::Identity;
|
|
|
|
//
|
|
//----------------------------------------------------------------------
|
|
// Read in the Model Filename of the Explosion renderable, then find its
|
|
// resource ID in the resource file
|
|
//----------------------------------------------------------------------
|
|
//
|
|
const char
|
|
*explosion;
|
|
if(
|
|
!model_file->GetEntry(
|
|
"gamedata",
|
|
"ExplosionModelFile",
|
|
&explosion
|
|
)
|
|
)
|
|
{
|
|
std::cout << model_name << " missing ExplosionModelFile!\n";
|
|
goto Dump_And_Die;
|
|
}
|
|
ResourceDescription
|
|
*res = resource_file->FindResourceDescription(
|
|
explosion,
|
|
ResourceDescription::ModelListResourceType
|
|
);
|
|
if (!res)
|
|
{
|
|
std::cout << model_name << " cannot find " << explosion
|
|
<< " in resource file!\n";
|
|
goto Dump_And_Die;
|
|
}
|
|
local_model->explosionResourceID = res->resourceID;
|
|
|
|
//
|
|
//-------------------------------------------------------------------------
|
|
// 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
|
|
Rivet::TestInstance() const
|
|
{
|
|
return IsDerivedFrom(*GetClassDerivations());
|
|
}
|