mirror of
https://github.com/jmarshall23/DoomRTX.git
synced 2026-08-12 08:11:10 +02:00
446 lines
13 KiB
C++
446 lines
13 KiB
C++
#include "precompiled.h"
|
|
#pragma hdrstop
|
|
|
|
#include "prey_local.h"
|
|
|
|
const idEventDef EV_RemoveAll( "removeAll", NULL );
|
|
|
|
CLASS_DECLARATION( idEntity, hhDebrisSpawner )
|
|
EVENT( EV_Activate, hhDebrisSpawner::Activate )
|
|
END_CLASS
|
|
|
|
/*
|
|
============
|
|
hhDebrisSpawner::hhDebrisSpawner()
|
|
============
|
|
*/
|
|
hhDebrisSpawner::hhDebrisSpawner() {
|
|
}
|
|
|
|
|
|
/*
|
|
============
|
|
hhDebrisSpawner::~hhDebrisSpawner()
|
|
============
|
|
*/
|
|
hhDebrisSpawner::~hhDebrisSpawner() {
|
|
}
|
|
|
|
|
|
/*
|
|
============
|
|
hhDebrisSpawner::Spawn()
|
|
Spawns the entity, and all other entities associated with the mass
|
|
============
|
|
*/
|
|
void hhDebrisSpawner::Spawn() {
|
|
|
|
// Set the defaults
|
|
activated = false;
|
|
sourceEntity = NULL;
|
|
hasBounds = false;
|
|
|
|
// Get the passed in
|
|
spawnArgs.GetVector( "origin", "0 0 0", origin );
|
|
spawnArgs.GetVector( "orientation", "1 0 0", orientation );
|
|
spawnArgs.GetVector( "velocity", "0 0 0", velocity );
|
|
spawnArgs.GetVector( "power", "50 50 50", power );
|
|
spawnArgs.GetFloat( "duration", "0", duration );
|
|
spawnArgs.GetBool( "multi_trigger", "0", multiActivate );
|
|
spawnArgs.GetBool( "spawnUsingEntity", "0", useEntity );
|
|
spawnArgs.GetBool( "fillBounds", "1", fillBounds );
|
|
spawnArgs.GetBool( "testBounds", "1", testBounds );
|
|
spawnArgs.GetBool( "nonsolid", "0", nonSolid );
|
|
spawnArgs.GetBool( "useAFBounds", "0", useAFBounds ); //rww
|
|
|
|
// Hide the model
|
|
GetPhysics()->SetContents( 0 );
|
|
Hide();
|
|
bounds = GetPhysics()->GetClipModel()->GetBounds();
|
|
GetPhysics()->DisableClip();
|
|
|
|
// Do stuff
|
|
power *= 20;
|
|
|
|
// spawnWhenActivated means that
|
|
if ( !useEntity && !spawnArgs.GetInt( "trigger", "0" ) ) {
|
|
Activate( NULL );
|
|
}
|
|
}
|
|
|
|
void hhDebrisSpawner::Save(idSaveGame *savefile) const {
|
|
savefile->WriteVec3(origin);
|
|
savefile->WriteVec3(orientation);
|
|
savefile->WriteVec3(velocity);
|
|
savefile->WriteVec3(power);
|
|
savefile->WriteBool(activated);
|
|
savefile->WriteBool(multiActivate);
|
|
savefile->WriteBool(hasBounds);
|
|
savefile->WriteBounds(bounds);
|
|
savefile->WriteFloat(duration);
|
|
savefile->WriteBool(fillBounds);
|
|
savefile->WriteBool(testBounds);
|
|
savefile->WriteObject(sourceEntity);
|
|
}
|
|
|
|
void hhDebrisSpawner::Restore( idRestoreGame *savefile ) {
|
|
savefile->ReadVec3(origin);
|
|
savefile->ReadVec3(orientation);
|
|
savefile->ReadVec3(velocity);
|
|
savefile->ReadVec3(power);
|
|
savefile->ReadBool(activated);
|
|
savefile->ReadBool(multiActivate);
|
|
savefile->ReadBool(hasBounds);
|
|
savefile->ReadBounds(bounds);
|
|
savefile->ReadFloat(duration);
|
|
savefile->ReadBool(fillBounds);
|
|
savefile->ReadBool(testBounds);
|
|
savefile->ReadObject( reinterpret_cast<idClass *&>(sourceEntity) );
|
|
|
|
spawnArgs.GetBool( "spawnUsingEntity", "0", useEntity );
|
|
spawnArgs.GetBool( "nonsolid", "0", nonSolid );
|
|
spawnArgs.GetBool( "useAFBounds", "0", useAFBounds );
|
|
}
|
|
|
|
|
|
/*
|
|
================
|
|
hhDebrisSpawner::Activate
|
|
Do the actual spawning of debris, FX, etc..
|
|
================
|
|
*/
|
|
void hhDebrisSpawner::Activate( idEntity *aSourceEntity ) {
|
|
|
|
if ( useEntity ) {
|
|
sourceEntity = aSourceEntity;
|
|
}
|
|
else { // If we don't have an entity, use our bounds instead
|
|
hasBounds = true;
|
|
}
|
|
|
|
if ( !activated || multiActivate ) {
|
|
SpawnDebris();
|
|
|
|
SpawnFX();
|
|
SpawnDecals();
|
|
|
|
// If we have a fixed duration, remove ourselves after they are removed
|
|
if ( ( duration > 0.0f ) && !multiActivate ) {
|
|
PostEventSec( &EV_Remove, duration + 1.0f );
|
|
}
|
|
|
|
activated = true;
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
============
|
|
hhDebrisSpawner::SpawnDebris
|
|
============
|
|
*/
|
|
void hhDebrisSpawner::SpawnDebris() {
|
|
const idKeyValue * kv = NULL;
|
|
idEntity * debris;
|
|
idDict args;
|
|
idStr debrisEntity;
|
|
idList<idStr> debrisEntities;
|
|
int numDebris;
|
|
// For entity sources
|
|
idBounds sourceBounds;
|
|
idVec3 sourceBoundCenter;
|
|
idVec3 debrisOrigin;
|
|
idBounds searchBounds;
|
|
idVec3 debrisVelocity;
|
|
bool fit;
|
|
idBounds afBounds; //rww
|
|
bool gotAFBounds = false; //rww
|
|
idVec3 defOrigin = vec3_zero;
|
|
idAngles defAngles = ang_zero;
|
|
|
|
// Optimization: if player is far away, don't spawn the chunks
|
|
if (!gameLocal.isMultiplayer) {
|
|
float maxDistance = spawnArgs.GetFloat("max_debris_distance");
|
|
if (maxDistance > 0.0f) {
|
|
float distSquared = (gameLocal.GetLocalPlayer()->GetOrigin() - origin).LengthSqr();
|
|
if (distSquared > maxDistance*maxDistance) {
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
args.SetInt( "nodrop", 1 );
|
|
args.SetVector( "origin", origin );
|
|
args.SetFloat( "duration", duration );
|
|
|
|
// Pass along variabes requested
|
|
hhUtils::PassArgs( spawnArgs, args, "pass_" );
|
|
|
|
if (useAFBounds && sourceEntity && sourceEntity->IsType(idActor::Type)) { //rww - try for AF bounds
|
|
idActor *actor = static_cast<idActor *>(sourceEntity);
|
|
if (actor->IsActiveAF()) { //they are ragdolling, so we have a chance.
|
|
idPhysics_AF *afPhys = actor->GetAFPhysics();
|
|
|
|
if (afPhys) { //got the af physics object, now loop through the bodies and collect an appropriate bounds.
|
|
afBounds.Zero();
|
|
|
|
for (int bodyCount = 0; bodyCount < afPhys->GetNumBodies(); bodyCount++) {
|
|
idAFBody *b = afPhys->GetBody(bodyCount);
|
|
if (b) {
|
|
idClipModel *bodyClip = b->GetClipModel();
|
|
if (bodyClip) { //now add the bounds of the clipModel into afBounds
|
|
idBounds bodyBounds = bodyClip->GetBounds();
|
|
idVec3 point = (b->GetWorldOrigin()-origin);
|
|
|
|
bodyBounds.AddPoint(point);
|
|
afBounds.AddBounds(bodyBounds);
|
|
gotAFBounds = true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Find all Debris
|
|
while ( GetNextDebrisData( debrisEntities, numDebris, kv, defOrigin, defAngles ) ) {
|
|
int numEntities = debrisEntities.Num();
|
|
for ( int count = 0; count < numDebris; ++count ) {
|
|
// Select a debris to use from the list
|
|
debrisEntity = debrisEntities[ gameLocal.random.RandomInt( numEntities ) ];
|
|
|
|
// If we have a bounding box to fill, use that
|
|
if ( fillBounds && ( sourceEntity || hasBounds ) ) {
|
|
if (gotAFBounds) { //rww
|
|
sourceBounds = afBounds;
|
|
//gameRenderWorld->DebugBounds(colorGreen, afBounds, origin, 1000);
|
|
}
|
|
else if ( sourceEntity ) {
|
|
sourceBounds = sourceEntity->GetPhysics()->GetBounds();
|
|
}
|
|
else {
|
|
sourceBounds = bounds;
|
|
}
|
|
|
|
fit = false;
|
|
for ( int i = 0; i < 4; i++ ) {
|
|
if ( i == 0 && defOrigin != vec3_zero ) {
|
|
//first try bone origin without random offset
|
|
debrisOrigin = defOrigin;
|
|
} else {
|
|
debrisOrigin = origin + hhUtils::RandomPointInBounds( sourceBounds );
|
|
}
|
|
|
|
if ( !testBounds || hhUtils::EntityDefWillFit( debrisEntity, debrisOrigin, defAngles.ToMat3(), CONTENTS_SOLID, NULL ) ) {
|
|
fit = true;
|
|
break; // Found a spot for the gib
|
|
}
|
|
}
|
|
|
|
if ( !fit ) { // Gib didn't fit after 4 attempts, so don't spawn the gib
|
|
//common->Warning( "SpawnDebris: gib didn't fit when spawned (%s)\n", debrisEntity.c_str() );
|
|
defAngles = ang_zero;
|
|
continue;
|
|
}
|
|
|
|
args.SetVector( "origin", debrisOrigin );
|
|
if ( sourceEntity ) {
|
|
idMat3 sourceAxis = idAngles( 0, sourceEntity->GetAxis().ToAngles().yaw, 0 ).ToMat3();
|
|
args.SetMatrix( "rotation", defAngles.ToMat3() * sourceAxis );
|
|
} else {
|
|
args.SetMatrix( "rotation", defAngles.ToMat3() );
|
|
}
|
|
}
|
|
|
|
|
|
if ( duration ) {
|
|
args.SetFloat( "removeTime", duration + duration * gameLocal.random.CRandomFloat() );
|
|
}
|
|
|
|
// Spawn the object
|
|
debris = gameLocal.SpawnObject( debrisEntity, &args );
|
|
HH_ASSERT(debris != NULL); // JRM - Nick, I added this because of a crash I got. Hopefully this will catch it sooner
|
|
|
|
// mdl: Added this check to make sure no AFs go through the debris spawner
|
|
if ( debris->IsType( idAFEntity_Base::Type ) ) {
|
|
gameLocal.Warning( "hhDebrisSpawner spawned an articulated entity: '%s'.", debrisEntity.c_str() );
|
|
}
|
|
|
|
if ( nonSolid ) {
|
|
|
|
idVec3 origin;
|
|
if ( debris->GetFloorPos( 64.0f, origin ) ) {
|
|
debris->SetOrigin( origin ); // Start on floor, since we're not going to be moving at all
|
|
debris->RunPhysics(); // Make sure any associated effects get updated before we turn collision off
|
|
}
|
|
|
|
// Turn off collision
|
|
debris->GetPhysics()->SetContents( 0 );
|
|
debris->GetPhysics()->SetClipMask( 0 );
|
|
debris->GetPhysics()->UnlinkClip();
|
|
debris->GetPhysics()->PutToRest();
|
|
|
|
} else {
|
|
// Add in random velocity
|
|
idVec3 randVel( gameLocal.random.RandomInt( power.x ) - power.x / 2,
|
|
gameLocal.random.RandomInt( power.y ) - power.y / 2,
|
|
gameLocal.random.RandomInt( power.z ) - power.z / 2 );
|
|
|
|
// Set linear velocity
|
|
debrisVelocity.x = velocity.x * power.x * 0.25;
|
|
debrisVelocity.y = velocity.y * power.y * 0.25;
|
|
debrisVelocity.z = 0.0f;
|
|
debris->GetPhysics()->SetLinearVelocity( debrisVelocity + randVel );
|
|
|
|
// Add random angular velocity
|
|
idVec3 aVel;
|
|
aVel.x = spawnArgs.GetFloat( "ang_vel", "90.0" ) * gameLocal.random.CRandomFloat();
|
|
aVel.y = spawnArgs.GetFloat( "ang_vel", "90.0" ) * gameLocal.random.CRandomFloat();
|
|
aVel.z = spawnArgs.GetFloat( "ang_vel", "90.0" ) * gameLocal.random.CRandomFloat();
|
|
|
|
if ( defAngles == ang_zero ) {
|
|
debris->GetPhysics()->SetAxis( idVec3( 1, 0, 0 ).ToMat3() );
|
|
}
|
|
debris->GetPhysics()->SetAngularVelocity( aVel );
|
|
}
|
|
}
|
|
defAngles = ang_zero;
|
|
|
|
// Zero out the list
|
|
debrisEntities.Clear();
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
/*
|
|
============
|
|
hhDebrisSpawner::SpawnFX
|
|
============
|
|
*/
|
|
void hhDebrisSpawner::SpawnFX() {
|
|
hhFxInfo fxInfo;
|
|
|
|
fxInfo.RemoveWhenDone( true );
|
|
BroadcastFxInfoPrefixed( "fx", origin, orientation.ToMat3(), &fxInfo );
|
|
}
|
|
|
|
|
|
//=============================================================================
|
|
//
|
|
// hhDebrisSpawner::SpawnDecals
|
|
//
|
|
//=============================================================================
|
|
|
|
void hhDebrisSpawner::SpawnDecals( void ) {
|
|
const int DIR_COUNT = 5;
|
|
idVec3 dir[DIR_COUNT] = {
|
|
idVec3(1.0f, 0.0f, 0.0f),
|
|
idVec3(-1.0f, 0.0f, 0.0f),
|
|
idVec3(0.0f, 0.0f, -1.0f),
|
|
idVec3(0.0f, 1.0f, 0.0f),
|
|
idVec3(0.0f, -1.0f, 0.0f)
|
|
};
|
|
|
|
// do blood splats
|
|
float size = spawnArgs.GetFloat( "decal_size", "96" );
|
|
|
|
const idKeyValue* kv = spawnArgs.MatchPrefix( "mtr_decal" );
|
|
while( kv ) {
|
|
if( kv->GetValue().Length() ) {
|
|
gameLocal.ProjectDecal( origin, dir[ gameLocal.random.RandomInt( DIR_COUNT ) ], 64.0f, true, size, kv->GetValue().c_str() );
|
|
}
|
|
kv = spawnArgs.MatchPrefix( "mtr_decal", kv );
|
|
}
|
|
}
|
|
|
|
/*
|
|
============
|
|
hhDebrisSpawner::GetNextDebrisData
|
|
============
|
|
*/
|
|
bool hhDebrisSpawner::GetNextDebrisData( idList<idStr> &entityDefs, int &count, const idKeyValue * &kv, idVec3 &origin, idAngles &angle ) {
|
|
static char debrisDef[] = "def_debris";
|
|
static char debrisKey[] = "debris";
|
|
idStr indexStr;
|
|
idStr entityDefBase;
|
|
int numDebris, minDebris, maxDebris;
|
|
origin = vec3_zero;
|
|
|
|
// Loop through looking for the next valid debris key
|
|
for ( kv = spawnArgs.MatchPrefix( debrisDef, kv );
|
|
kv && kv->GetValue().Length();
|
|
kv = spawnArgs.MatchPrefix( debrisDef, kv ) ) {
|
|
indexStr = kv->GetKey();
|
|
indexStr.Strip( debrisDef );
|
|
|
|
// Is a valid debris base And it isn't a variation. (ie, contains a .X postfix)
|
|
if ( idStr::IsNumeric( indexStr ) && indexStr.Find( '.' ) < 0 ) {
|
|
|
|
// Get Number of Debris
|
|
numDebris = -1;
|
|
|
|
if ( sourceEntity && sourceEntity->IsType( idAI::Type ) ) {
|
|
idVec3 bonePos;
|
|
idMat3 boneAxis;
|
|
idStr bone = spawnArgs.GetString( va( "%s%s%s", debrisKey, "_bone", ( const char * ) indexStr ) );
|
|
if( !bone.IsEmpty() ) {
|
|
static_cast<idAI*>(sourceEntity)->GetJointWorldTransform( bone.c_str(), bonePos, boneAxis );
|
|
origin = bonePos;
|
|
angle = spawnArgs.GetAngles( va( "%s%s%s", debrisKey, "_angle", ( const char * ) indexStr ) );
|
|
}
|
|
}
|
|
if ( !spawnArgs.GetInt( va( "%s%s%s", debrisKey, "_num",
|
|
( const char * ) indexStr ),
|
|
"-1", numDebris ) || numDebris < 0 ) {
|
|
// No num found, look for min and max
|
|
if ( spawnArgs.GetInt( va( "%s%s%s", debrisKey, "_min",
|
|
( const char * ) indexStr ),
|
|
"-1", minDebris ) && minDebris >= 0 &&
|
|
spawnArgs.GetInt( va( "%s%s%s", debrisKey, "_max",
|
|
( const char * ) indexStr ),
|
|
"-1", maxDebris ) && maxDebris >= 0 ) {
|
|
numDebris =
|
|
gameLocal.random.RandomInt( ( maxDebris - minDebris ) ) + minDebris;
|
|
} //. No min/max found
|
|
|
|
} //. No num found
|
|
|
|
// No valid num found
|
|
if ( numDebris < 0 ) {
|
|
gameLocal.Warning( "ERROR: No debris num could be be found for %s%s",
|
|
( const char * ) debrisDef,
|
|
( const char * ) indexStr );
|
|
}
|
|
else { // Valid num found
|
|
const char * entityDefPrefix;
|
|
const idKeyValue * otherDefKey = NULL;
|
|
|
|
entityDefBase = kv->GetValue();
|
|
count = numDebris;
|
|
|
|
entityDefs.Append( entityDefBase );
|
|
|
|
// Find the other defs that may be there. using .X scheme
|
|
entityDefPrefix = va( "%s.", (const char *) kv->GetKey() );
|
|
|
|
// gameLocal.Printf( "Looking for %s\n", entityDefPrefix );
|
|
|
|
for ( otherDefKey = spawnArgs.MatchPrefix( entityDefPrefix, otherDefKey );
|
|
otherDefKey && otherDefKey->GetValue().Length();
|
|
otherDefKey = spawnArgs.MatchPrefix( entityDefPrefix, otherDefKey ) ) {
|
|
// gameLocal.Printf( "Would have added %s\n", (const char *) otherDefKey->GetValue() );
|
|
entityDefs.Append( otherDefKey->GetValue() );
|
|
}
|
|
|
|
return( true );
|
|
}
|
|
|
|
} //. Valid debris base
|
|
} //. debris loop
|
|
|
|
|
|
return( false );
|
|
}
|
|
|