This repository has been archived on 2025-02-27. You can view files and clone it, but cannot push or open issues or pull requests.
CnC_Renegade/Code/Combat/surfaceeffects.cpp

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