744 lines
23 KiB
C++
744 lines
23 KiB
C++
|
/*
|
||
|
** Command & Conquer Renegade(tm)
|
||
|
** Copyright 2025 Electronic Arts Inc.
|
||
|
**
|
||
|
** This program is free software: you can redistribute it and/or modify
|
||
|
** it under the terms of the GNU General Public License as published by
|
||
|
** the Free Software Foundation, either version 3 of the License, or
|
||
|
** (at your option) any later version.
|
||
|
**
|
||
|
** This program is distributed in the hope that it will be useful,
|
||
|
** but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||
|
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||
|
** GNU General Public License for more details.
|
||
|
**
|
||
|
** You should have received a copy of the GNU General Public License
|
||
|
** along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||
|
*/
|
||
|
|
||
|
/***********************************************************************************************
|
||
|
*** Confidential - Westwood Studios ***
|
||
|
***********************************************************************************************
|
||
|
* *
|
||
|
* Project Name : Commando *
|
||
|
* *
|
||
|
* $Archive:: /Commando/Code/Combat/surfaceeffects.cpp $*
|
||
|
* *
|
||
|
* $Author:: Greg_h $*
|
||
|
* *
|
||
|
* $Modtime:: 6/14/02 10:48a $*
|
||
|
* *
|
||
|
* $Revision:: 56 $*
|
||
|
* *
|
||
|
*---------------------------------------------------------------------------------------------*
|
||
|
* Functions: *
|
||
|
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
|
||
|
|
||
|
#include "surfaceeffects.h"
|
||
|
#include "w3d_file.h"
|
||
|
#include "part_emt.h"
|
||
|
#include "assets.h"
|
||
|
#include "pscene.h"
|
||
|
#include "debug.h"
|
||
|
#include "wwstring.h"
|
||
|
#include "crandom.h"
|
||
|
#include "physcon.h"
|
||
|
#include "wwaudio.h"
|
||
|
#include "rndstrng.h"
|
||
|
#include "sound3d.h"
|
||
|
#include "phys.h"
|
||
|
#include "wwprofile.h"
|
||
|
#include "gameobjref.h"
|
||
|
#include "physicalgameobj.h"
|
||
|
#include "combat.h"
|
||
|
#include "ccamera.h"
|
||
|
#include "effectrecycler.h"
|
||
|
#include "timeddecophys.h"
|
||
|
|
||
|
#define RECYCLE_EMITTERS 1
|
||
|
|
||
|
const float _MAX_SURFACE_EFFECT_DISTANCE2 = 50.0f * 50.0f; // no surface effects created farther than this from camera
|
||
|
const float _SURFACE_EFFECT_NEAR_DISTANCE2 = 10.0f * 10.0f; // force surface effects within this distance
|
||
|
|
||
|
#define SECTION_BULLET_STOPPING "Bullet_Stopping"
|
||
|
#define SECTION_DAMAGE_WARHEAD "Damage_Warhead"
|
||
|
#define SECTION_DAMAGE_RATE "Damage_Rate"
|
||
|
|
||
|
SurfaceEffectsManager::MODE SurfaceEffectsManager::Mode = SurfaceEffectsManager::MODE_FULL;
|
||
|
int SurfaceEffectsManager::OverrideSurfaceType = -1;
|
||
|
bool _IsSurfaceEffectsInitted = false;
|
||
|
|
||
|
// If there exist more than this many particle buffers, don't make emitters
|
||
|
#define PARTICLE_BUFFER_LIMIT 5
|
||
|
|
||
|
/*
|
||
|
** These are the names for each Hitter Type. If you add a hitter type, add the name here
|
||
|
*/
|
||
|
static const char * _HitterTypeName[ SurfaceEffectsManager::NUM_HITTER_TYPES ] = {
|
||
|
"None", // HITTER_TYPE_NONE, // used to shut off persistant effects
|
||
|
"Generic Object", // HITTER_TYPE_GENERIC_OBJECT, // generic dynamic object
|
||
|
|
||
|
"Footstep Run", // HITTER_TYPE_FOOTSTEP_RUN,
|
||
|
"Footstep Walk", // HITTER_TYPE_FOOTSTEP_WALK,
|
||
|
"Footstep Crouched", // HITTER_TYPE_FOOTSTEP_CROUCHED,
|
||
|
"Footstep Jump", // HITTER_TYPE_FOOTSTEP_JUMP,
|
||
|
"Footstep Land", // HITTER_TYPE_FOOTSTEP_LAND,
|
||
|
|
||
|
"Bullet", // HITTER_TYPE_BULLET,
|
||
|
"Bullet Fire", // HITTER_TYPE_BULLET_FIRE,
|
||
|
"Bullet Grenade", // HITTER_TYPE_BULLET_GRENADE,
|
||
|
"Bullet Chem", // HITTER_TYPE_BULLET_CHEM,
|
||
|
"Bullet Electric", // HITTER_TYPE_BULLET_ELECTRIC,
|
||
|
"Bullet Laser", // HITTER_TYPE_BULLET_LASER,
|
||
|
"Bullet Squish", // HITTER_TYPE_BULLET_SQUISH,
|
||
|
"Bullet Tib Spray", // HITTER_TYPE_BULLET_TIB_SPRAY,
|
||
|
"Bullet Tib", // HITTER_TYPE_BULLET_TIB_BULLET,
|
||
|
"Bullet Shotgun", // HITTER_TYPE_BULLET_SHOTGUN,
|
||
|
"Bullet Silenced", // HITTER_TYPE_BULLET_SILENCED,
|
||
|
"Bullet Sniper", // HITTER_TYPE_BULLET_SNIPER,
|
||
|
"Bullet Water", // HITTER_TYPE_BULLET_WATER,
|
||
|
|
||
|
"Eject Casing", // HITTER_TYPE_EJECT_CASING, // casings ejected from weapons (all are assumed to make the same sounds)
|
||
|
"Shell Shotgun", // HITTER_TYPE_SHELL_SHOTGUN,
|
||
|
|
||
|
"Tire Rolling", // HITTER_TYPE_TIRE_ROLLING, // wheels on humvees, etc...
|
||
|
"Tire Sliding", // HITTER_TYPE_TIRE_SLIDING,
|
||
|
"Track Rolling", // HITTER_TYPE_TRACK_ROLLING, // tank tracks...
|
||
|
"Track Sliding", // HITTER_TYPE_TRACK_SLIDING,
|
||
|
|
||
|
"Footstep Ladder", // HITTER_TYPE_FOOTSTEP_LADDER,
|
||
|
|
||
|
};
|
||
|
|
||
|
|
||
|
/*
|
||
|
** Recycler for all "fire-and-forget" surface effect emitters
|
||
|
*/
|
||
|
#if (RECYCLE_EMITTERS)
|
||
|
static EffectRecyclerClass _EmitterRecycler;
|
||
|
#endif
|
||
|
|
||
|
|
||
|
/*
|
||
|
** Persistant Surface Effects
|
||
|
*/
|
||
|
class PersistantSurfaceEffectClass
|
||
|
{
|
||
|
public:
|
||
|
PersistantSurfaceEffectClass( void );
|
||
|
~PersistantSurfaceEffectClass( void );
|
||
|
|
||
|
int CurrentSurfaceType;
|
||
|
int CurrentHitterType;
|
||
|
|
||
|
friend SurfaceEffectsManager;
|
||
|
};
|
||
|
|
||
|
class PersistantSurfaceSoundClass : public PersistantSurfaceEffectClass
|
||
|
{
|
||
|
public:
|
||
|
PersistantSurfaceSoundClass( void );
|
||
|
~PersistantSurfaceSoundClass( void );
|
||
|
|
||
|
void Set_Sound( const char * name );
|
||
|
|
||
|
AudibleSoundClass * Sound;
|
||
|
|
||
|
friend SurfaceEffectsManager;
|
||
|
};
|
||
|
|
||
|
class PersistantSurfaceEmitterClass : public PersistantSurfaceEffectClass
|
||
|
{
|
||
|
private:
|
||
|
PersistantSurfaceEmitterClass( void );
|
||
|
~PersistantSurfaceEmitterClass( void );
|
||
|
|
||
|
void Set_Emitter( const char * name );
|
||
|
|
||
|
ParticleEmitterClass * Emitter;
|
||
|
|
||
|
friend SurfaceEffectsManager;
|
||
|
};
|
||
|
|
||
|
/*
|
||
|
** PersistantSurfaceEffectClass
|
||
|
*/
|
||
|
PersistantSurfaceEffectClass::PersistantSurfaceEffectClass(void) :
|
||
|
CurrentSurfaceType( -1 ),
|
||
|
CurrentHitterType( -1 )
|
||
|
{
|
||
|
}
|
||
|
|
||
|
PersistantSurfaceEffectClass::~PersistantSurfaceEffectClass(void)
|
||
|
{
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
** PersistantSurfaceSoundClass
|
||
|
*/
|
||
|
PersistantSurfaceSoundClass::PersistantSurfaceSoundClass( void ) :
|
||
|
Sound( NULL )
|
||
|
{
|
||
|
}
|
||
|
|
||
|
PersistantSurfaceSoundClass::~PersistantSurfaceSoundClass( void )
|
||
|
{
|
||
|
Set_Sound( NULL );
|
||
|
}
|
||
|
|
||
|
void PersistantSurfaceSoundClass::Set_Sound( const char * name )
|
||
|
{
|
||
|
// Stop Old Sound
|
||
|
if ( Sound != NULL ) {
|
||
|
Sound->Stop();
|
||
|
Sound->Remove_From_Scene();
|
||
|
Sound->Release_Ref();
|
||
|
Sound = NULL;
|
||
|
}
|
||
|
|
||
|
// Start new Sound
|
||
|
if ( name != NULL ) {
|
||
|
Sound = WWAudioClass::Get_Instance()->Create_Continuous_Sound( name );
|
||
|
if ( Sound != NULL ) {
|
||
|
Sound->Add_To_Scene( true );
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
/*
|
||
|
** PersistantSurfaceEmitterClass
|
||
|
*/
|
||
|
PersistantSurfaceEmitterClass::PersistantSurfaceEmitterClass( void ) :
|
||
|
Emitter( NULL )
|
||
|
{
|
||
|
}
|
||
|
|
||
|
PersistantSurfaceEmitterClass::~PersistantSurfaceEmitterClass( void )
|
||
|
{
|
||
|
Set_Emitter( NULL );
|
||
|
}
|
||
|
|
||
|
void PersistantSurfaceEmitterClass::Set_Emitter( const char * name )
|
||
|
{
|
||
|
// Stop Old Emitter
|
||
|
if ( Emitter != NULL ) {
|
||
|
// Check if it is in the scene, it may ahve already been remove by completion
|
||
|
if ( Emitter->Peek_Scene() != NULL ) {
|
||
|
PhysicsSceneClass::Get_Instance()->Remove_Render_Object( Emitter );
|
||
|
}
|
||
|
Emitter->Stop();
|
||
|
Emitter->Release_Ref();
|
||
|
Emitter = NULL;
|
||
|
}
|
||
|
|
||
|
// Start new Emitter
|
||
|
if ( name != NULL ) {
|
||
|
Emitter = (ParticleEmitterClass *)WW3DAssetManager::Get_Instance()->Create_Render_Obj( name );
|
||
|
if ( Emitter ) {
|
||
|
SET_REF_OWNER( Emitter );
|
||
|
// Emitter->Set_Remove_On_Complete( false );
|
||
|
Emitter->Start();
|
||
|
PhysicsSceneClass::Get_Instance()->Add_Render_Object( Emitter );
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
** Surface Effect Database
|
||
|
*/
|
||
|
class SurfaceEffectClass {
|
||
|
|
||
|
public:
|
||
|
RandomStringClass Sound;
|
||
|
RandomStringClass Emitter;
|
||
|
RandomStringClass Decal;
|
||
|
float DecalSize;
|
||
|
float DecalSizeRandom;
|
||
|
};
|
||
|
|
||
|
SurfaceEffectClass * SurfaceEffectsDatabase[SURFACE_TYPE_MAX][SurfaceEffectsManager::NUM_HITTER_TYPES];
|
||
|
bool SurfaceStopsBullets[SURFACE_TYPE_MAX];
|
||
|
int SurfaceDamageWarhead[SURFACE_TYPE_MAX];
|
||
|
float SurfaceDamageRate[SURFACE_TYPE_MAX];
|
||
|
|
||
|
/*
|
||
|
** These are the physics object types for each Hitter Type. These are only used to pass
|
||
|
** the friction and drag constants on to the physics system. If your hitter type does not
|
||
|
** correspond to a physics object type, put a -1 for its entry here.
|
||
|
*/
|
||
|
const int HitterPhysicsTypes[SurfaceEffectsManager::NUM_HITTER_TYPES] =
|
||
|
{
|
||
|
-1, //"Bullet",
|
||
|
-1, //"Eject Casing"
|
||
|
-1, //"Footstep Run",
|
||
|
-1, //"Footstep Walk",
|
||
|
-1, //"Footstep Crouch",
|
||
|
PhysicsConstants::DYNAMIC_OBJ_TYPE_TIRE, //"Tire Rolling",
|
||
|
-1, //"Tire Sliding",
|
||
|
PhysicsConstants::DYNAMIC_OBJ_TYPE_TRACK, //"Track Rolling",
|
||
|
-1, //"Track Sliding",
|
||
|
PhysicsConstants::DYNAMIC_OBJ_TYPE_GENERIC, //"Generic Object"
|
||
|
-1,
|
||
|
};
|
||
|
|
||
|
|
||
|
#define SURFACE_EFFECTS_INI_FILENAME "surfaceeffects.ini"
|
||
|
|
||
|
bool SurfaceEffectsManagerDebug = false;
|
||
|
|
||
|
void SurfaceEffectsManager::Toggle_Debug( void )
|
||
|
{
|
||
|
SurfaceEffectsManagerDebug = !SurfaceEffectsManagerDebug;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
**
|
||
|
*/
|
||
|
void SurfaceEffectsManager::Init( void )
|
||
|
{
|
||
|
if (_IsSurfaceEffectsInitted) {
|
||
|
Shutdown();
|
||
|
}
|
||
|
|
||
|
for ( int i = 0; i < SURFACE_TYPE_MAX; i++ ) {
|
||
|
SurfaceStopsBullets[i] = true;
|
||
|
SurfaceDamageWarhead[i] = 0;
|
||
|
SurfaceDamageRate[i] = 0;
|
||
|
}
|
||
|
|
||
|
// Read Database INI file
|
||
|
INIClass * ini = Get_INI( SURFACE_EFFECTS_INI_FILENAME );
|
||
|
|
||
|
if ( ini != NULL ) {
|
||
|
WWASSERT( ini && ini->Section_Count() > 0 );
|
||
|
|
||
|
int surface;
|
||
|
|
||
|
// Read Bullet Stopping
|
||
|
for ( surface = 0; surface < SURFACE_TYPE_MAX; surface++ ) {
|
||
|
SurfaceStopsBullets[ surface ] = ini->Get_Bool( SECTION_BULLET_STOPPING, SURFACE_TYPE_STRINGS[surface], true );
|
||
|
}
|
||
|
|
||
|
// Read Surface Damage Data
|
||
|
for ( surface = 0; surface < SURFACE_TYPE_MAX; surface++ ) {
|
||
|
StringClass warhead(0,true);
|
||
|
ini->Get_String(warhead,SECTION_DAMAGE_WARHEAD, SURFACE_TYPE_STRINGS[surface] );
|
||
|
if ( !warhead.Is_Empty() ) {
|
||
|
SurfaceDamageWarhead[surface] = ArmorWarheadManager::Get_Warhead_Type( warhead );
|
||
|
}
|
||
|
SurfaceDamageRate[surface] = ini->Get_Float( SECTION_DAMAGE_RATE, SURFACE_TYPE_STRINGS[surface], 0 );
|
||
|
|
||
|
if ( SurfaceDamageRate[surface] != 0 ) {
|
||
|
// Debug_Say(( "Surface %s Rate %f Warhead %s\n", SURFACE_TYPE_STRINGS[surface],
|
||
|
// SurfaceDamageRate[surface], ArmorWarheadManager::Get_Warhead_Name( SurfaceDamageWarhead[surface] ) ));
|
||
|
}
|
||
|
}
|
||
|
|
||
|
for ( surface = 0; surface < SURFACE_TYPE_MAX; surface++ ) {
|
||
|
for ( int hitter = 0; hitter < NUM_HITTER_TYPES; hitter++ ) {
|
||
|
StringClass section_name;
|
||
|
section_name.Format( "%s_%s", SURFACE_TYPE_STRINGS[surface], _HitterTypeName[hitter] );
|
||
|
|
||
|
if ( ini->Entry_Count( section_name ) == 0 ) {
|
||
|
SurfaceEffectsDatabase[ surface ][ hitter ] = NULL;
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
// Debug_Say(( "Surface Effect Section %s\n", section_name ));
|
||
|
|
||
|
// create the effect
|
||
|
SurfaceEffectClass * surface_effect = new SurfaceEffectClass;
|
||
|
|
||
|
// Read sounds
|
||
|
for ( int sound = 0; ; sound++ ) {
|
||
|
StringClass sound_entry(0,true);
|
||
|
sound_entry.Format( "Sound%d", sound );
|
||
|
StringClass sound_name(0,true);
|
||
|
ini->Get_String(sound_name, section_name, sound_entry );
|
||
|
if ( sound_name.Is_Empty() ) {
|
||
|
break;
|
||
|
}
|
||
|
// Debug_Say(( "Add Sound %s\n", sound_name ));
|
||
|
surface_effect->Sound.Add_String( sound_name );
|
||
|
}
|
||
|
|
||
|
// Read emitters
|
||
|
for ( int emitter = 0; ; emitter++ ) {
|
||
|
StringClass emitter_entry(0,true);
|
||
|
emitter_entry.Format( "Emitter%d", emitter );
|
||
|
StringClass emitter_name(0,true);
|
||
|
ini->Get_String(emitter_name, section_name, emitter_entry );
|
||
|
if ( emitter_name.Is_Empty() ) {
|
||
|
break;
|
||
|
}
|
||
|
// Debug_Say(( "Add Emitter %s\n", emitter_name ));
|
||
|
surface_effect->Emitter.Add_String( emitter_name );
|
||
|
}
|
||
|
|
||
|
// Read decals
|
||
|
for ( int decal = 0; ; decal++ ) {
|
||
|
StringClass decal_entry(0,true);
|
||
|
decal_entry.Format( "Decal%d", decal );
|
||
|
StringClass decal_name(0,true);
|
||
|
ini->Get_String(decal_name, section_name, decal_entry );
|
||
|
if ( decal_name.Is_Empty() ) {
|
||
|
break;
|
||
|
}
|
||
|
// Debug_Say(( "Add Decal %s\n", decal_name ));
|
||
|
surface_effect->Decal.Add_String( decal_name );
|
||
|
}
|
||
|
surface_effect->DecalSize = ini->Get_Float( section_name, "DecalSize", 1 );
|
||
|
surface_effect->DecalSizeRandom = ini->Get_Float( section_name, "DecalSizeRandom", 0 );
|
||
|
|
||
|
|
||
|
// save the effect
|
||
|
SurfaceEffectsDatabase[ surface ][ hitter ] = surface_effect;
|
||
|
|
||
|
// (gth) if this is one of the physics "hitter types" copy the
|
||
|
// constants into the physics engine
|
||
|
if (HitterPhysicsTypes[hitter] != -1) {
|
||
|
|
||
|
float friction = ini->Get_Float( section_name,
|
||
|
"Friction",
|
||
|
PhysicsConstants::DefaultContactFriction );
|
||
|
|
||
|
float drag = ini->Get_Float( section_name,
|
||
|
"Drag",
|
||
|
PhysicsConstants::DefaultContactDrag );
|
||
|
|
||
|
|
||
|
PhysicsConstants::Set_Contact_Friction_Coefficient( HitterPhysicsTypes[hitter],
|
||
|
surface,
|
||
|
friction );
|
||
|
|
||
|
PhysicsConstants::Set_Contact_Drag_Coefficient( HitterPhysicsTypes[hitter],
|
||
|
surface,
|
||
|
drag );
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
Release_INI( ini );
|
||
|
ini = NULL;
|
||
|
} else {
|
||
|
Debug_Say(("SurfaceEffectsManager::Init - Unable to load %s\n", SURFACE_EFFECTS_INI_FILENAME ));
|
||
|
}
|
||
|
|
||
|
WWASSERT(ini == NULL);
|
||
|
_IsSurfaceEffectsInitted = true;
|
||
|
}
|
||
|
|
||
|
void SurfaceEffectsManager::Shutdown( void )
|
||
|
{
|
||
|
#if (RECYCLE_EMITTERS)
|
||
|
_EmitterRecycler.Reset();
|
||
|
#endif
|
||
|
|
||
|
for ( int surface = 0; surface < SURFACE_TYPE_MAX; surface++ ) {
|
||
|
for ( int hitter = 0; hitter < NUM_HITTER_TYPES; hitter++ ) {
|
||
|
if ( SurfaceEffectsDatabase[ surface ][ hitter ] ) {
|
||
|
delete SurfaceEffectsDatabase[ surface ][ hitter ];
|
||
|
SurfaceEffectsDatabase[ surface ][ hitter ] = NULL;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
_IsSurfaceEffectsInitted = false;
|
||
|
}
|
||
|
|
||
|
const char * SurfaceEffectsManager::Hitter_Type_Name( int hitter )
|
||
|
{
|
||
|
return _HitterTypeName[ hitter ];
|
||
|
}
|
||
|
|
||
|
void SurfaceEffectsManager::Apply_Effect
|
||
|
(
|
||
|
int surface_type,
|
||
|
int hitter_type,
|
||
|
const Matrix3D & tm,
|
||
|
PhysClass * hit_obj,
|
||
|
PhysicalGameObj * creator,
|
||
|
bool allow_decals,
|
||
|
bool allow_emitters
|
||
|
)
|
||
|
{
|
||
|
WWPROFILE( "Apply Surface Effect" );
|
||
|
|
||
|
bool ok = ( (surface_type >= 0) &&
|
||
|
(surface_type < SURFACE_TYPE_MAX) &&
|
||
|
(hitter_type >=0 ) &&
|
||
|
(hitter_type < NUM_HITTER_TYPES) );
|
||
|
if (!ok) {
|
||
|
WWDEBUG_SAY(("Invalid surface params. surface_type: %d hitter_type: %d\r\n",surface_type,hitter_type));
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
// Return if all surface effects are currently disabled
|
||
|
if ( Mode == MODE_OFF ) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
// If the user has specified an override type, use it.
|
||
|
if (OverrideSurfaceType != -1) { surface_type = OverrideSurfaceType; }
|
||
|
|
||
|
// Return if we don't find any surface effect settings for this combination of surface_type, hitter_type
|
||
|
SurfaceEffectClass * surface_effect = SurfaceEffectsDatabase[ surface_type ][ hitter_type ];
|
||
|
if ( surface_effect == NULL ) {
|
||
|
if ( SurfaceEffectsManagerDebug ) {
|
||
|
Debug_Say(( "No Surface Effect for %s and %s\n", SURFACE_TYPE_STRINGS[surface_type], _HitterTypeName[hitter_type] ));
|
||
|
}
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
const char * sound_name = surface_effect->Sound.Get_String();
|
||
|
const char * emitter_name = surface_effect->Emitter.Get_String();
|
||
|
const char * decal_name = surface_effect->Decal.Get_String();
|
||
|
|
||
|
if ( SurfaceEffectsManagerDebug ) {
|
||
|
Debug_Say(( "Applying Surface Effect for %s and %s\n--Sound:%s Emitter:%s Decal:%s\n",
|
||
|
SURFACE_TYPE_STRINGS[surface_type],
|
||
|
_HitterTypeName[hitter_type],
|
||
|
sound_name ? sound_name : "none",
|
||
|
emitter_name ? emitter_name : "none",
|
||
|
decal_name ? decal_name : "none" ));
|
||
|
}
|
||
|
|
||
|
// Create the sound
|
||
|
if ( sound_name ) {
|
||
|
|
||
|
WWPROFILE( "Sound" );
|
||
|
|
||
|
// I may need to plug an owner in here
|
||
|
RefCountedGameObjReference *owner_ref = new RefCountedGameObjReference;
|
||
|
owner_ref->Set_Ptr( creator );
|
||
|
WWAudioClass::Get_Instance()->Create_Instant_Sound( sound_name, tm, owner_ref, 0, CLASSID_PSEUDO3D );
|
||
|
REF_PTR_RELEASE( owner_ref );
|
||
|
}
|
||
|
|
||
|
// Return if this effect is far from the camera or not within the view frustum
|
||
|
// But, do this after the sound is created!!!
|
||
|
Vector3 effect_point;
|
||
|
tm.Get_Translation(&effect_point);
|
||
|
float dist2 = (effect_point - COMBAT_CAMERA->Get_Position()).Length2();
|
||
|
|
||
|
if ( (dist2 > _SURFACE_EFFECT_NEAR_DISTANCE2) ) {
|
||
|
if ( (dist2 > _MAX_SURFACE_EFFECT_DISTANCE2) ||
|
||
|
(CollisionMath::Overlap_Test(COMBAT_CAMERA->Get_Frustum(),effect_point) == CollisionMath::OUTSIDE) )
|
||
|
{
|
||
|
return;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Create the emitter
|
||
|
if ((allow_emitters) && (emitter_name) && (Mode != MODE_NO_EMITTERS)) {
|
||
|
WWPROFILE( "Emitter" );
|
||
|
|
||
|
#if (RECYCLE_EMITTERS)
|
||
|
_EmitterRecycler.Spawn_Effect(emitter_name,tm);
|
||
|
#else
|
||
|
ParticleEmitterClass * emitter = (ParticleEmitterClass *)WW3DAssetManager::Get_Instance()->Create_Render_Obj( emitter_name );
|
||
|
if ( emitter ) {
|
||
|
SET_REF_OWNER( emitter );
|
||
|
emitter->Set_Transform( tm );
|
||
|
emitter->Start();
|
||
|
emitter->Enable_Remove_On_Complete(true);
|
||
|
PhysicsSceneClass::Get_Instance()->Add_Render_Object( emitter );
|
||
|
emitter->Release_Ref();
|
||
|
}
|
||
|
#endif
|
||
|
}
|
||
|
|
||
|
// Create decal, (will only apply to static physics objects)
|
||
|
if ((allow_decals) && (decal_name)) {
|
||
|
|
||
|
WWPROFILE( "Decal" );
|
||
|
|
||
|
float size = surface_effect->DecalSize;
|
||
|
size += FreeRandom.Get_Float( -surface_effect->DecalSizeRandom, surface_effect->DecalSizeRandom );
|
||
|
StringClass new_name( true );
|
||
|
Strip_Path_From_Filename( new_name, decal_name );
|
||
|
|
||
|
|
||
|
// If this is a glass decal, we enable "apply_to_translucent_meshes" and we
|
||
|
// also pass in the object that was hit so the decal is *only* applied to that
|
||
|
// object. Otherwise, we use normal decal processing which collects the meshes
|
||
|
// that overlap the volume and applies the decal to all of them
|
||
|
if ((surface_type == SURFACE_TYPE_GLASS) || (surface_type == SURFACE_TYPE_GLASS_PERMEABLE)) {
|
||
|
PhysicsSceneClass::Get_Instance()->Create_Decal( tm, new_name, size, false, true, hit_obj );
|
||
|
} else {
|
||
|
PhysicsSceneClass::Get_Instance()->Create_Decal( tm, new_name, size );
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
PersistantSurfaceSoundClass * SurfaceEffectsManager::Create_Persistant_Sound( void )
|
||
|
{
|
||
|
return new PersistantSurfaceSoundClass;
|
||
|
}
|
||
|
|
||
|
void SurfaceEffectsManager::Destroy_Persistant_Sound( PersistantSurfaceSoundClass * effect )
|
||
|
{
|
||
|
delete effect;
|
||
|
}
|
||
|
|
||
|
void SurfaceEffectsManager::Update_Persistant_Sound( PersistantSurfaceSoundClass * effect,
|
||
|
int surface_type, int hitter_type, const Matrix3D & tm )
|
||
|
{
|
||
|
WWASSERT( surface_type >= 0 );
|
||
|
WWASSERT( surface_type < SURFACE_TYPE_MAX );
|
||
|
WWASSERT( hitter_type >= 0 );
|
||
|
WWASSERT( hitter_type < NUM_HITTER_TYPES );
|
||
|
|
||
|
if ( Mode == MODE_OFF ) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
// If the user has specified an override type, use it.
|
||
|
if (OverrideSurfaceType != -1) { surface_type = OverrideSurfaceType; }
|
||
|
|
||
|
// If something changed, re-lookup the sound
|
||
|
if ( ( surface_type != effect->CurrentSurfaceType ) || ( hitter_type != effect->CurrentHitterType ) ) {
|
||
|
|
||
|
effect->CurrentSurfaceType = surface_type;
|
||
|
effect->CurrentHitterType = hitter_type;
|
||
|
|
||
|
SurfaceEffectClass * surface_effect = SurfaceEffectsDatabase[ surface_type ][ hitter_type ];
|
||
|
|
||
|
if ( surface_effect == NULL ) {
|
||
|
if ( SurfaceEffectsManagerDebug ) {
|
||
|
Debug_Say(( "No Surface Effect for %s and %s\n", SURFACE_TYPE_STRINGS[surface_type], _HitterTypeName[hitter_type] ));
|
||
|
}
|
||
|
effect->Set_Sound( NULL );
|
||
|
|
||
|
} else {
|
||
|
|
||
|
const char * sound_name = surface_effect->Sound.Get_String();
|
||
|
if ( SurfaceEffectsManagerDebug ) {
|
||
|
Debug_Say(( "Applying Persistant Surface Sound for %s and %s Sound:%s\n",
|
||
|
SURFACE_TYPE_STRINGS[surface_type],
|
||
|
_HitterTypeName[hitter_type],
|
||
|
sound_name ? sound_name : "none" ));
|
||
|
}
|
||
|
effect->Set_Sound( sound_name );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Update Position
|
||
|
if ( effect->Sound != NULL ) {
|
||
|
effect->Sound->Set_Transform( tm );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
PersistantSurfaceEmitterClass * SurfaceEffectsManager::Create_Persistant_Emitter( void )
|
||
|
{
|
||
|
return new PersistantSurfaceEmitterClass;
|
||
|
}
|
||
|
|
||
|
void SurfaceEffectsManager::Destroy_Persistant_Emitter( PersistantSurfaceEmitterClass * effect )
|
||
|
{
|
||
|
delete effect;
|
||
|
}
|
||
|
|
||
|
void SurfaceEffectsManager::Update_Persistant_Emitter( PersistantSurfaceEmitterClass * effect,
|
||
|
int surface_type, int hitter_type, const Matrix3D & tm )
|
||
|
{
|
||
|
WWASSERT( surface_type >= 0 );
|
||
|
WWASSERT( surface_type < SURFACE_TYPE_MAX );
|
||
|
WWASSERT( hitter_type >= 0 );
|
||
|
WWASSERT( hitter_type < NUM_HITTER_TYPES );
|
||
|
|
||
|
if ( Mode != MODE_FULL ) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
// If the user has specified an override type, use it.
|
||
|
if (OverrideSurfaceType != -1) { surface_type = OverrideSurfaceType; }
|
||
|
|
||
|
// If something changed, re-lookup the emitter
|
||
|
if ( ( surface_type != effect->CurrentSurfaceType ) || ( hitter_type != effect->CurrentHitterType ) ) {
|
||
|
|
||
|
effect->CurrentSurfaceType = surface_type;
|
||
|
effect->CurrentHitterType = hitter_type;
|
||
|
SurfaceEffectClass * surface_effect = SurfaceEffectsDatabase[ surface_type ][ hitter_type ];
|
||
|
|
||
|
if ( surface_effect == NULL ) {
|
||
|
if ( SurfaceEffectsManagerDebug ) {
|
||
|
Debug_Say(( "No Surface Effect for %s and %s\n", SURFACE_TYPE_STRINGS[surface_type], _HitterTypeName[hitter_type] ));
|
||
|
}
|
||
|
effect->Set_Emitter( NULL );
|
||
|
|
||
|
} else {
|
||
|
|
||
|
const char * emitter_name = surface_effect->Emitter.Get_String();
|
||
|
if ( SurfaceEffectsManagerDebug ) {
|
||
|
Debug_Say(( "Applying Persistant Surface Emitter for %s and %s Emitter:%s\n",
|
||
|
SURFACE_TYPE_STRINGS[surface_type],
|
||
|
_HitterTypeName[hitter_type],
|
||
|
emitter_name ? emitter_name : "none" ));
|
||
|
}
|
||
|
effect->Set_Emitter( emitter_name );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Update the transform
|
||
|
if ( effect->Emitter != NULL ) {
|
||
|
effect->Emitter->Set_Transform( tm );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
bool SurfaceEffectsManager::Does_Surface_Stop_Bullets( int surface_type )
|
||
|
{
|
||
|
WWASSERT( surface_type >= 0 );
|
||
|
WWASSERT( surface_type < SURFACE_TYPE_MAX );
|
||
|
return SurfaceStopsBullets[ surface_type ];
|
||
|
}
|
||
|
|
||
|
void SurfaceEffectsManager::Apply_Damage( int surface_type, PhysicalGameObj * obj )
|
||
|
{
|
||
|
|
||
|
#ifndef PARAM_EDITING_ON
|
||
|
|
||
|
// If the user has specified an override type, use it.
|
||
|
if (OverrideSurfaceType != -1) { surface_type = OverrideSurfaceType; }
|
||
|
|
||
|
if ( SurfaceDamageRate[surface_type] > 0 ) {
|
||
|
OffenseObjectClass offense_obj( SurfaceDamageRate[surface_type], SurfaceDamageWarhead[surface_type] );
|
||
|
obj->Apply_Damage_Extended( offense_obj, TimeManager::Get_Frame_Seconds() );
|
||
|
}
|
||
|
|
||
|
#endif //PARAM_EDITING_ON
|
||
|
|
||
|
}
|
||
|
|
||
|
bool SurfaceEffectsManager::Is_Surface_Permeable( int surface )
|
||
|
{
|
||
|
switch (surface) {
|
||
|
case SURFACE_TYPE_FOLIAGE_PERMEABLE:
|
||
|
case SURFACE_TYPE_GLASS_PERMEABLE:
|
||
|
case SURFACE_TYPE_ICE_PERMEABLE:
|
||
|
case SURFACE_TYPE_CLOTH_PERMEABLE:
|
||
|
case SURFACE_TYPE_ELECTRICAL_PERMEABLE:
|
||
|
case SURFACE_TYPE_FLAMMABLE_PERMEABLE:
|
||
|
case SURFACE_TYPE_STEAM_PERMEABLE:
|
||
|
case SURFACE_TYPE_WATER_PERMEABLE:
|
||
|
case SURFACE_TYPE_TIBERIUM_WATER_PERMEABLE:
|
||
|
return true;
|
||
|
}
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
void SurfaceEffectsManager::Set_Override_Surface_Type(int type)
|
||
|
{
|
||
|
if ((type < 0) || (type >= SURFACE_TYPE_MAX)) {
|
||
|
OverrideSurfaceType = -1;
|
||
|
} else {
|
||
|
OverrideSurfaceType = type;
|
||
|
}
|
||
|
|
||
|
PhysicsConstants::Set_Override_Surface_Type(OverrideSurfaceType);
|
||
|
}
|
||
|
|
||
|
|