2020-12-13 15:01:13 +00:00
/ * *
* Copyright 1998 - 2013 Epic Games , Inc . All Rights Reserved .
* /
class GameExplosionActor extends Actor
abstract
config ( Weapon )
native ;
/** True if explosion has occurred. */
var protected transient bool bHasExploded ;
/ * * T r u e i f t h i s a c t o r c a n e x p l o d e m o r e t h a n o n c e a n d d o e s n ' t d i e a f t e r a n e x p l o s i o n .
* Used by placeable actors whose explosions are triggered via matinee
* /
var ( ) protected bool bExplodeMoreThanOnce ;
/** The actual light used for the explosion. */
var protected transient PointLightComponent ExplosionLight ;
` if( ` _ _TW _ )
/** Temp data for light flickering. */
var protected transient float LastLightBrightness ;
var protected transient float LightFlickerIntensity ;
var protected transient float LightFlickerInterpSpeed ;
/** Temp data for light fading. */
var protected transient float LightFadeStartTime ;
` endif // __TW_
/** Radial blur for explosion */
var protected transient RadialBlurComponent ExplosionRadialBlur ;
/** Temp data for light fading. */
var protected transient float LightFadeTime ;
var protected transient float LightFadeTimeRemaining ;
var protected transient float LightInitialBrightness ;
/** Temp data for radial blur fading. */
var protected transient float RadialBlurFadeTime ;
var protected transient float RadialBlurFadeTimeRemaining ;
var protected transient float RadialBlurMaxBlurAmount ;
/** Temp reference to the explosion template, used for delayed damage */
var GameExplosion ExplosionTemplate ;
/ * *
* If TRUE , take the Explosion ParticleSystem lifespan into account when determining
* the lifespan of the GameExplosionActor . This is useful in cases where the GEA
* needs to do further processing while the particle system is active .
* For example , in the case of a smoke grenade , you would want to ensure that the
* explosion actor stayed around long enough to properly trigger coughing , etc . when
* a pawn enters / exits the smoke area .
* /
var bool bTrackExplosionParticleSystemLifespan ;
/** Used to push physics when explosion goes off. */
var protected RB _RadialImpulseComponent RadialImpulseComponent ;
/** player responsible for damage */
var Controller InstigatorController ;
/** This the saved off hit actor and location from the GetPhysicalMaterial trace so we can see if it is a FluidSurfaceActor and then apply some forces to it **/
var Actor HitActorFromPhysMaterialTrace ;
var vector HitLocationFromPhysMaterialTrace ;
/** Are we attached to something? Used to attach FX for stuff like the smoke grenade. */
var Actor Attachee ;
var Controller AttacheeController ;
/** Minimum dot product for explosion to be able to affect a point. Used as an optimization for directional explosions. */
var transient float DirectionalExplosionMinDot ;
/** Forward dir for directional explosions. */
var transient vector ExplosionDirection ;
/** Toggles debug explosion rendering. */
` if( ` _ _TW _ )
var config bool bDrawDebug ;
` else
var bool bDrawDebug ;
` endif // __TW_
replication
{
if ( bNetInitial )
ExplosionDirection ;
}
cpptext
{
virtual void TickSpecial ( FLOAT DeltaSeconds ) ;
}
` if( ` _ _TW _ )
// Custom line trace for more specific TraceFlags control
function Actor TraceExplosive ( vector TraceEnd , vector TraceStart )
{
return StaticTraceExplosive ( TraceEnd , TraceStart , self ) ;
}
native static function Actor StaticTraceExplosive ( vector TraceEnd , vector TraceStart , Actor SourceActor ) ;
` endif
event PreBeginPlay ( )
{
Super . PreBeginPlay ( ) ;
if ( Instigator != None && InstigatorController == None )
{
InstigatorController = Instigator . Controller ;
}
}
/ * *
* Internal . Tries to find a physical material for the surface the explosion occurred upon .
* @ note : It sucks doing an extra trace here . We could conceivably pass the physical material info around
* by changing the lower level physics code ( e . g . processHitWall ) , but that ' s a big engine - level change .
* /
simulated protected function PhysicalMaterial GetPhysicalMaterial ( )
{
local PhysicalMaterial Retval ;
local vector TraceStart , TraceDest , OutHitNorm , ExploNormal ;
local TraceHitInfo OutHitInfo ;
// here we have to do an additional trace shooting straight down to see if we are under water.
TraceStart = Location + ( vect ( 0 , 0 , 1 ) * 256. f ) ;
TraceDest = Location - ( vect ( 0 , 0 , 1 ) * 16. f ) ;
HitActorFromPhysMaterialTrace = Trace ( HitLocationFromPhysMaterialTrace , OutHitNorm , TraceDest , TraceStart , TRUE , vect ( 0 , 0 , 0 ) , OutHitInfo , TRACEFLAG _Bullet | TRACEFLAG _PhysicsVolumes ) ;
//DrawDebugLine( TraceStart, TraceDest, 0, 255, 0, TRUE);
//`log("EXPLOSION SURFACE:"@HitActorFromPhysMaterialTrace);
//DrawDebugCoordinateSystem( TraceStart, Rotation, 10.0f, TRUE );
if ( FluidSurfaceActor ( HitActorFromPhysMaterialTrace ) != None )
{
Retval = OutHitInfo . PhysMaterial ;
return Retval ;
}
ExploNormal = vector ( Rotation ) ;
TraceStart = Location + ( ExploNormal * 8. f ) ;
TraceDest = TraceStart - ( ExploNormal * 64. f ) ;
HitActorFromPhysMaterialTrace = Trace ( HitLocationFromPhysMaterialTrace , OutHitNorm , TraceDest , TraceStart , TRUE , vect ( 0 , 0 , 0 ) , OutHitInfo , TRACEFLAG _Bullet ) ;
//DrawDebugLine( TraceStart, TraceDest, 0, 255, 0, TRUE);
//DrawDebugCoordinateSystem( TraceStart, Rotation, 10.0f, TRUE );
if ( HitActorFromPhysMaterialTrace != None )
{
Retval = OutHitInfo . PhysMaterial ;
}
return Retval ;
}
simulated function bool DoFullDamageToActor ( Actor Victim )
{
return ( Victim . bStatic || Victim . IsA ( 'KActor' ) || Victim . IsA ( 'InterpActor' ) || Victim . IsA ( 'FracturedStaticMeshPart' ) ) ;
}
simulated protected function bool IsBehindExplosion ( Actor A )
{
if ( ExplosionTemplate . bDirectionalExplosion && ! IsZero ( ExplosionDirection ) )
{
// @todo, for certain types of actors (e.g. large actors), we may want to test a location other than the
// actor's location. Like a cone/bbox check or something.
// @todo, maybe use Actor's bbox center, like damage does below?
return ( ExplosionDirection dot Normal ( A . Location - Location ) ) < DirectionalExplosionMinDot ;
}
return FALSE ;
}
/ * *
* Returns distance from bounding box to point
* /
final static native function float BoxDistanceToPoint ( vector Start , Box BBox ) ;
/ * *
* Does damage modeling and application for explosions
* @ PARAM bCauseDamage if true cause damage to actors within damage radius
* @ PARAM bCauseEffects if true apply other affects to actors within appropriate radii
* @ RETURN TRUE if at least one Pawn victim got hurt . ( This is only valid if bCauseDamage == TRUE )
* /
protected simulated function bool DoExplosionDamage ( bool bCauseDamage , bool bCauseEffects )
{
` if( ` _ _TW _ )
local Actor Victim , HitActor ;
local vector BBoxCenter ;
` else
local Actor Victim , HitActor ;
local vector HitL , HitN , Dir , BBoxCenter ; //, BBoxExtent;
` endif
2023-05-11 15:55:04 +00:00
local bool bDamageBlocked , bDoFullDamage , bCauseFractureEffects , bCausePawnEffects , bCauseDamageEffects , bHurtSomeone , bAdjustRadiusDamage ;
local float ColRadius , ColHeight , CheckRadius , VictimDist , VictimColRadius , VictimColHeight ;
2020-12-13 15:01:13 +00:00
local array < Actor > VictimsList ;
local Box BBox ;
local Controller ModInstigator ;
local GamePawn VictimPawn ;
local FracturedStaticMeshActor FracActor ;
local byte WantPhysChunksAndParticles ;
local TraceHitInfo HitInfo ;
local KActorFromStatic NewKActor ;
local StaticMeshComponent HitStaticMesh ;
2023-05-11 15:55:04 +00:00
bAdjustRadiusDamage = true ;
2020-12-13 15:01:13 +00:00
// can pre-calculate this condition now
bCauseFractureEffects = bCauseEffects && WorldInfo . NetMode != NM _DedicatedServer && ExplosionTemplate . bCausesFracture ;
bCauseEffects = bCauseEffects && WorldInfo . NetMode != NM _Client ;
bHurtSomeone = FALSE ;
// determine radius to check
CheckRadius = GetEffectCheckRadius ( bCauseDamage , bCauseFractureEffects , bCauseEffects ) ;
if ( CheckRadius > 0.0 )
{
foreach CollidingActors ( class 'Actor' , Victim , CheckRadius , Location , ExplosionTemplate . bUseOverlapCheck , , HitInfo )
{
// check for static mesh that can become dynamic
if ( Victim . bWorldGeometry )
{
HitStaticMesh = StaticMeshComponent ( HitInfo . HitComponent ) ;
` if( ` _ _TW _ )
if ( ( HitStaticMesh != None ) && HitStaticMesh . CanBecomeDynamic ( ) &&
! WorldInfo . bDropDetail && WorldInfo . GetDetailMode ( ) > DM _Low )
` else
if ( ( HitStaticMesh != None ) && HitStaticMesh . CanBecomeDynamic ( ) )
` endif
{
NewKActor = class 'KActorFromStatic' . Static . MakeDynamic ( HitStaticMesh ) ;
if ( NewKActor != None )
{
Victim = NewKActor ;
}
}
}
// Look for things that are not yourself and not world geom
if ( Victim != Self
&& ( ! Victim . bWorldGeometry || Victim . bCanBeDamaged )
&& ( NavigationPoint ( Victim ) == None )
&& Victim != ExplosionTemplate . ActorToIgnoreForDamage
&& ( ! ExplosionTemplate . bIgnoreInstigator || Victim != Instigator )
&& ! ClassIsChildOf ( Victim . Class , ExplosionTemplate . ActorClassToIgnoreForDamage )
&& ! IsBehindExplosion ( Victim ) )
{
// If attached to a pawn and victim is a pawn on other team
VictimPawn = GamePawn ( Victim ) ;
// check if visible, unless physics object
// note: using bbox center instead of location, because that's what visiblecollidingactors does
Victim . GetComponentsBoundingBox ( BBox ) ;
// adjust distance if using overlap check
if ( ExplosionTemplate . bUseOverlapCheck )
{
VictimDist = BoxDistanceToPoint ( Location , BBox ) ;
}
else
{
VictimDist = VSize ( Location - Victim . Location ) ;
}
2023-05-11 15:55:04 +00:00
if ( ExplosionTemplate . bDoCylinderCheck )
{
Victim . GetBoundingCylinder ( ColRadius , ColHeight ) ;
Instigator . GetBoundingCylinder ( VictimColRadius , VictimColHeight ) ;
// If the distance between them is more than the size of half the height of both cilinders, don't consider as target
if ( Abs ( Victim . Location . Z - Instigator . Location . Z ) >= ( ColHeight * 0.5 f ) + ( VictimColHeight * 0.5 f ) )
{
continue ;
}
}
2020-12-13 15:01:13 +00:00
// Do fracturing
if ( bCauseFractureEffects && ( VictimPawn == None ) )
{
FracActor = FracturedStaticMeshActor ( Victim ) ;
if ( ( FracActor != None )
&& ( VictimDist < ExplosionTemplate . FractureMeshRadius )
&& ( FracActor . Physics == PHYS _None )
&& FracActor . IsFracturedByDamageType ( ExplosionTemplate . MyDamageType )
&& FracActor . FractureEffectIsRelevant ( false , Instigator , WantPhysChunksAndParticles ) )
{
// Let kismet know that we were hit by an explosion
FracActor . NotifyHitByExplosion ( InstigatorController , ExplosionTemplate . Damage , ExplosionTemplate . MyDamageType ) ;
FracActor . BreakOffPartsInRadius ( Location , ExplosionTemplate . FractureMeshRadius , ExplosionTemplate . FracturePartVel , WantPhysChunksAndParticles == 1 ? true : false ) ;
}
}
bCausePawnEffects = bCauseEffects && ( VictimPawn != None ) && ! VictimPawn . InGodMode ( ) ;
bCauseDamageEffects = bCauseDamage && ( VictimDist < ExplosionTemplate . DamageRadius ) ;
// skip line check for some objects
if ( DoFullDamageToActor ( Victim ) )
{
bDamageBlocked = FALSE ;
bDoFullDamage = TRUE ; // force full damage for these objects
}
else if ( bCausePawnEffects || bCauseDamageEffects )
{
BBoxCenter = ( BBox . Min + BBox . Max ) * 0.5 f ;
` if( ` _ _TW _ )
HitActor = TraceExplosive ( BBoxCenter , Location + vect ( 0 , 0 , 20 ) ) ;
` else
HitActor = Trace ( HitL , HitN , BBoxCenter , Location + vect ( 0 , 0 , 20 ) , FALSE , , , TRACEFLAG _Bullet ) ;
` endif
bDamageBlocked = ( HitActor != None && HitActor != Victim ) ;
//`endif
bDoFullDamage = FALSE ;
}
2023-05-11 15:55:04 +00:00
if ( ExplosionTemplate . bAlwaysFullDamage )
{
bAdjustRadiusDamage = false ;
bDoFullDamage = true ;
}
2020-12-13 15:01:13 +00:00
if ( ! bDamageBlocked )
{
if ( bCauseDamageEffects )
{
// apply damage
ModInstigator = InstigatorController ;
// Same team check always returns FALSE if PRI is None
if ( AttacheeController != None && AttacheeController . PlayerReplicationInfo != None && VictimPawn != None && ! WorldInfo . GRI . OnSameTeam ( AttacheeController , VictimPawn . Controller ) )
{
ModInstigator = AttacheeController ; // Make the instigator the base pawn's controller
}
` if( ` _ _TW _ )
2023-05-11 15:55:04 +00:00
Victim . TakeRadiusDamage ( ModInstigator , GetDamageFor ( Victim ) , ExplosionTemplate . DamageRadius , ExplosionTemplate . MyDamageType , ExplosionTemplate . MomentumTransferScale , Location , bDoFullDamage , ( Owner != None ) ? Owner : self , ExplosionTemplate . DamageFalloffExponent , bAdjustRadiusDamage ) ;
2020-12-13 15:01:13 +00:00
` else
2023-05-11 15:55:04 +00:00
Victim . TakeRadiusDamage ( ModInstigator , ExplosionTemplate . Damage , ExplosionTemplate . DamageRadius , ExplosionTemplate . MyDamageType , ExplosionTemplate . MomentumTransferScale , Location , bDoFullDamage , ( Owner != None ) ? Owner : self , ExplosionTemplate . DamageFalloffExponent , bAdjustRadiusDamage ) ;
2020-12-13 15:01:13 +00:00
` endif
VictimsList [ VictimsList . Length ] = Victim ;
if ( Victim . IsA ( 'Pawn' ) )
{
bHurtSomeone = TRUE ;
}
}
if ( bCausePawnEffects )
{
SpecialPawnEffectsFor ( VictimPawn , VictimDist ) ;
}
else if ( bCauseEffects )
{
SpecialCringeEffectsFor ( Victim , VictimDist ) ;
}
}
}
` if( ` _ _TW _ )
//Allow the explosion to handle behavior related to actors in range that are ignored
HandleIgnoredVictim ( Victim ) ;
` endif
}
if ( ExplosionTemplate . bFullDamageToAttachee && VictimsList . Find ( Attachee ) == INDEX _NONE )
{
Victim = Attachee ;
Victim . GetBoundingCylinder ( ColRadius , ColHeight ) ;
` if( ` _ _TW _ )
Victim . TakeRadiusDamage ( InstigatorController , GetDamageFor ( Victim ) , ExplosionTemplate . DamageRadius , ExplosionTemplate . MyDamageType ,
ExplosionTemplate . MomentumTransferScale , Location , true , ( Owner != None ) ? Owner : self ) ;
` else
Dir = Normal ( Victim . Location - Location ) ;
Victim . TakeDamage ( ExplosionTemplate . Damage , InstigatorController , Victim . Location - 0.5 * ( ColHeight + ColRadius ) * dir ,
( ExplosionTemplate . MomentumTransferScale * Dir ) , ExplosionTemplate . MyDamageType , , ( Owner != None ) ? Owner : self ) ;
` endif
}
}
return bHurtSomeone ;
}
` if( ` _ _TW _ )
function HandleIgnoredVictim ( Actor Victim ) ;
` endif
/** Return the desired radius to check for actors which get effects from explosion */
function float GetEffectCheckRadius ( bool bCauseDamage , bool bCauseFractureEffects , bool bCauseEffects )
{
local float CheckRadius ;
if ( bCauseFractureEffects )
{
CheckRadius = ExplosionTemplate . FractureMeshRadius ;
}
if ( bCauseDamage )
{
CheckRadius = FMax ( CheckRadius , ExplosionTemplate . DamageRadius ) ;
}
if ( bCauseEffects )
{
CheckRadius = FMax ( CheckRadius , ExplosionTemplate . KnockDownRadius ) ;
CheckRadius = FMax ( CheckRadius , ExplosionTemplate . CringeRadius ) ;
}
return CheckRadius ;
}
` if( ` _ _TW _ )
/** Gets explosion damage for specific target (allows children to override) */
simulated function float GetDamageFor ( Actor Victim )
{
return ExplosionTemplate . Damage ;
}
` endif
/ * *
* Handle making pawns cringe or fall down from nearby explosions . Server only .
* /
protected function SpecialPawnEffectsFor ( GamePawn VictimPawn , float VictimDist ) ;
/ * *
* Handle applying cringe to non - pawn actors
*
* @ param Victim - the actor hit
*
* @ param VictimDist - the distance the victim was from the blast
* /
protected function SpecialCringeEffectsFor ( Actor Victim , float VictimDist ) ;
/ * *
* Internal . Extract what data we can from the physical material - based effects system
* and stuff it into the ExplosionTemplate .
* Data in the physical material will take precedence .
*
* We are also going to be checking for relevance here as when any of these params are "none" / invalid we do not
* play those effects in Explode ( ) . So this way we avoid any work on looking things up in the physmaterial
*
* /
simulated protected function UpdateExplosionTemplateWithPerMaterialFX ( PhysicalMaterial PhysMaterial ) ;
simulated function SpawnExplosionParticleSystem ( ParticleSystem Template ) ;
simulated function SpawnExplosionDecal ( ) ;
simulated function SpawnExplosionFogVolume ( ) ;
/ * *
* @ todo break this up into the same methods that < Game > Weapon uses ( SpawnImpactEffects , SpawnImpactSounds , SpawnImpactDecal ) as they are all
* orthogonal and so indiv subclasses can choose to have base functionality or override
*
* @ param Direction For bDirectionalExplosion = true explosions , this is the forward direction of the blast .
* * /
simulated function Explode ( GameExplosion NewExplosionTemplate , optional vector Direction )
{
local float HowLongToLive ;
local PhysicalMaterial PhysMat ;
local bool bHurtSomeone ;
// copy our significant data
ExplosionTemplate = NewExplosionTemplate ;
if ( ExplosionTemplate . bDirectionalExplosion )
{
ExplosionDirection = Normal ( Direction ) ;
DirectionalExplosionMinDot = Cos ( ExplosionTemplate . DirectionalExplosionAngleDeg * DegToRad ) ;
}
// by default, live just long enough to go boom
HowLongToLive = LifeSpan + ExplosionTemplate . DamageDelay + 0.01 f ;
if ( ! bHasExploded || bExplodeMoreThanOnce )
{
// maybe find the physical material and extract the properties we need
if ( ExplosionTemplate . bAllowPerMaterialFX )
{
PhysMat = GetPhysicalMaterial ( ) ;
` if( ` _ _TW _ )
// Go ahead and update anyway, if its none or not so that we KNOW that it's none and can handle appropriately
UpdateExplosionTemplateWithPerMaterialFX ( PhysMat ) ;
` else
if ( PhysMat != None )
{
UpdateExplosionTemplateWithPerMaterialFX ( PhysMat ) ;
}
` endif // __TW_
}
// spawn explosion effects
if ( WorldInfo . NetMode != NM _DedicatedServer )
{
if ( ExplosionTemplate . ParticleEmitterTemplate != none )
{
SpawnExplosionParticleSystem ( ExplosionTemplate . ParticleEmitterTemplate ) ;
if ( bTrackExplosionParticleSystemLifespan == TRUE )
{
// Let the particle system contribute to life span determination...
HowLongToLive = FMax ( ExplosionTemplate . ParticleEmitterTemplate . GetMaxLifespan ( 0.0 f ) + 0.1 f , HowLongToLive ) ;
}
}
// spawn a decal
SpawnExplosionDecal ( ) ;
// turn on the light
` if( ` _ _TW _ )
if ( ExplosionTemplate . ExploLight != None && WorldInfo . bAllowExplosionLights && ! WorldInfo . bDropDetail )
` else
if ( ExplosionTemplate . ExploLight != None )
` endif
{
if ( ExplosionLight != None )
{
// If there is already an explosion light, detach it
DetachComponent ( ExplosionLight ) ;
}
// construct a copy of the PLC, turn it on
ExplosionLight = new ( self ) class 'PointLightComponent' ( ExplosionTemplate . ExploLight ) ;
if ( ExplosionLight != None )
{
AttachComponent ( ExplosionLight ) ;
ExplosionLight . SetEnabled ( TRUE ) ;
SetTimer ( ExplosionTemplate . ExploLightFadeOutTime ) ;
LightFadeTime = ExplosionTemplate . ExploLightFadeOutTime ;
LightFadeTimeRemaining = LightFadeTime ;
` if( ` _ _TW _ )
LightFadeStartTime = ExplosionTemplate . ExploLightStartFadeOutTime ;
LightFlickerIntensity = ExplosionTemplate . ExploLightFlickerIntensity ;
LightFlickerInterpSpeed = ExplosionTemplate . ExploLightFlickerInterpSpeed ;
HowLongToLive = FMax ( LightFadeTime + LightFadeStartTime + 0.2 f , HowLongToLive ) ;
` else
HowLongToLive = FMax ( LightFadeTime + 0.2 f , HowLongToLive ) ;
` endif // __TW_
LightInitialBrightness = ExplosionTemplate . ExploLight . Brightness ;
}
}
// radial blur
if ( ExplosionTemplate . ExploRadialBlur != None )
{
if ( ( ExplosionTemplate . bPerformRadialBlurRelevanceCheck == false ) || ImpactEffectIsRelevant ( Instigator , Location + vect ( 0 , 0 , 1 ) , false , 4000.0 f , 350.0 f , true ) )
{
if ( ExplosionRadialBlur != None )
{
// If there is already a radial blur, detach it
DetachComponent ( ExplosionRadialBlur ) ;
}
ExplosionRadialBlur = new ( self ) class 'RadialBlurComponent' ( ExplosionTemplate . ExploRadialBlur ) ;
if ( ExplosionRadialBlur != None )
{
AttachComponent ( ExplosionRadialBlur ) ;
RadialBlurFadeTime = ExplosionTemplate . ExploRadialBlurFadeOutTime ;
RadialBlurFadeTimeRemaining = RadialBlurFadeTime ;
RadialBlurMaxBlurAmount = ExplosionTemplate . ExploRadialBlurMaxBlur ;
SetTimer ( FMax ( RadialBlurFadeTime , LightFadeTime ) ) ;
HowLongToLive = FMax ( RadialBlurFadeTime + 0.2 f , HowLongToLive ) ;
}
}
}
// cam shakes
DoExplosionCameraEffects ( ) ;
// Apply impulse to physics stuff (before we do fracture)
` if( ` _ _TW _ )
// fixed grenade log spam
if ( RadialImpulseComponent == None )
{
}
else
` endif
if ( ExplosionTemplate . MyDamageType != None && ExplosionTemplate . MyDamageType . default . RadialDamageImpulse > 0.0 )
{
RadialImpulseComponent . ImpulseRadius = FMax ( ExplosionTemplate . DamageRadius , ExplosionTemplate . KnockDownRadius ) ;
RadialImpulseComponent . ImpulseStrength = ExplosionTemplate . MyDamageType . default . RadialDamageImpulse ;
RadialImpulseComponent . bVelChange = ExplosionTemplate . MyDamageType . default . bRadialDamageVelChange ;
RadialImpulseComponent . ImpulseFalloff = RIF _Constant ;
//`log("AA"@ExplosionTemplate.MyDamageType@RadialImpulseComponent.ImpulseStrength@RadialImpulseComponent.ImpulseRadius);
RadialImpulseComponent . FireImpulse ( Location ) ;
}
SpawnExplosionFogVolume ( ) ;
if ( FluidSurfaceActor ( HitActorFromPhysMaterialTrace ) != none )
{
FluidSurfaceActor ( HitActorFromPhysMaterialTrace ) . FluidComponent . ApplyForce ( HitLocationFromPhysMaterialTrace , 1024.0 f , 20.0 f , FALSE ) ;
}
}
// do damage
// delay the damage if necessary,
bHurtSomeone = FALSE ;
if ( ExplosionTemplate . Damage > 0.0 )
{
if ( ExplosionTemplate . DamageDelay > 0.0 )
{
// cause effects now, damage later
DoExplosionDamage ( false , true ) ;
SetTimer ( ExplosionTemplate . DamageDelay , FALSE , nameof ( DelayedExplosionDamage ) ) ;
}
else
{
// otherwise apply immediately
bHurtSomeone = DoExplosionDamage ( true , true ) ;
}
}
else
{
DoExplosionDamage ( false , true ) ;
}
// play the sound
if ( WorldInfo . NetMode != NM _DedicatedServer )
{
if ( bHurtSomeone && ExplosionTemplate . ExplosionSoundHurtSomeone != None )
{
//`log( "Playing Explosion Sound (debug left in to test distance)" @ ExplosionTemplate.ExplosionSound );
` if( ` _ _TW _WWISE _ )
// sound location can't be the same as sound player location, so add a little offset to make Wwise happy
PlaySoundBase ( ExplosionTemplate . ExplosionSoundHurtSomeone , TRUE , TRUE , FALSE , Location + vect ( 0 , 0 , 0.1 ) , TRUE ) ;
` else
PlaySound ( ExplosionTemplate . ExplosionSoundHurtSomeone , TRUE , TRUE , FALSE , Location , TRUE ) ;
` endif // __TW_WWISE_
}
else if ( ExplosionTemplate . ExplosionSound != None )
{
//`log( "Playing Explosion Sound (debug left in to test distance)" @ ExplosionTemplate.ExplosionSound );
` if( ` _ _TW _WWISE _ )
// sound location can't be the same as sound player location, so add a little offset to make Wwise happy
PlaySoundBase ( ExplosionTemplate . ExplosionSound , TRUE , TRUE , FALSE , Location + vect ( 0 , 0 , 0.1 ) , TRUE ) ;
` else
PlaySound ( ExplosionTemplate . ExplosionSound , TRUE , TRUE , FALSE , Location , TRUE ) ;
` endif // __TW_WWISE_
}
}
if ( Role == Role _Authority )
{
MakeNoise ( 1.0 ) ;
}
` if( ` notdefined ( FINAL _RELEASE ) )
if ( bDrawDebug )
{
DrawDebug ( ) ;
}
` endif
bHasExploded = TRUE ;
// done with it
if ( ! bPendingDelete && ! bDeleteMe )
{
// Live forever if this actor can explode more than once
LifeSpan = bExplodeMoreThanOnce ? 0.0 : HowLongToLive ;
}
}
}
simulated function DelayedExplosionDamage ( )
{
DoExplosionDamage ( true , false ) ;
}
simulated function DrawDebug ( )
{
local Color C ;
local float Angle ;
// debug spheres
if ( ExplosionTemplate . bDirectionalExplosion )
{
C . R = 255 ;
C . G = 128 ;
C . B = 16 ;
C . A = 255 ;
Angle = ExplosionTemplate . DirectionalExplosionAngleDeg * DegToRad ;
DrawDebugCone ( Location , ExplosionDirection , ExplosionTemplate . DamageRadius , Angle , Angle , 8 , C , TRUE ) ;
}
else
{
DrawDebugSphere ( Location , ExplosionTemplate . DamageRadius , 10 , 255 , 128 , 16 , TRUE ) ;
//DrawDebugLine(Location, Location + HitNormal*16, 255, 255, 255, TRUE);
}
}
simulated function DoExplosionCameraEffects ( )
{
local CameraShake Shake ;
local float ShakeScale ;
local PlayerController PC ;
// do camera shake(s)
// note: intentionally letting directional explosions still shake everything
foreach WorldInfo . LocalPlayerControllers ( class 'PlayerController' , PC )
{
if ( PC . PlayerCamera != None )
{
Shake = ChooseCameraShake ( Location , PC ) ;
if ( Shake != None )
{
ShakeScale = PC . PlayerCamera . CalcRadialShakeScale ( PC . PlayerCamera , Location , ExplosionTemplate . CamShakeInnerRadius , ExplosionTemplate . CamShakeOuterRadius , ExplosionTemplate . CamShakeFalloff ) ;
if ( ExplosionTemplate . bOrientCameraShakeTowardsEpicenter )
{
PC . ClientPlayCameraShake ( Shake , ShakeScale , ExplosionTemplate . bAutoControllerVibration , CAPS _UserDefined , rotator ( Location - PC . ViewTarget . Location ) ) ;
}
else
{
PC . ClientPlayCameraShake ( Shake , ShakeScale , ExplosionTemplate . bAutoControllerVibration ) ;
}
}
}
}
// do lens effects
SpawnCameraLensEffects ( ) ;
}
/ * *
* Spawns the camera lens effect ( s ) if needed by this explosion
* /
simulated function SpawnCameraLensEffects ( )
{
local PlayerController PC ;
if ( ExplosionTemplate . CameraLensEffect != None )
{
foreach WorldInfo . LocalPlayerControllers ( class 'PlayerController' , PC )
{
// splatter some blood on their camera if they are a human and decently close
if ( PC . Pawn != None &&
VSize ( PC . Pawn . Location - Location ) < ExplosionTemplate . CameraLensEffectRadius &&
PC . IsAimingAt ( self , 0.1 ) && // if we are semi looking in the direction of the explosion
! IsBehindExplosion ( PC . Pawn ) )
{
PC . ClientSpawnCameraLensEffect ( ExplosionTemplate . CameraLensEffect ) ;
}
}
}
}
/ * *
* Internal . When using directional camera shakes , used to determine which anim to use .
* @ todo : nativise for speed ?
* /
protected simulated function CameraShake ChooseCameraShake ( vector Epicenter , PlayerController PC )
{
local vector CamX , CamY , CamZ , ToEpicenter ;
local float FwdDot , RtDot ;
local CameraShake ChosenShake ;
local Rotator NoPitchRot ;
if ( ExplosionTemplate . bOrientCameraShakeTowardsEpicenter )
{
return ExplosionTemplate . CamShake ;
}
// expected to be false much of the time, so maybe bypass the math
else if ( ( ExplosionTemplate . CamShake _Left != None ) || ( ExplosionTemplate . CamShake _Right != None ) || ( ExplosionTemplate . CamShake _Rear != None ) )
{
ToEpicenter = Epicenter - PC . PlayerCamera . Location ;
ToEpicenter . Z = 0. f ;
ToEpicenter = Normal ( ToEpicenter ) ;
NoPitchRot = PC . PlayerCamera . Rotation ;
NoPitchRot . Pitch = 0. f ;
GetAxes ( NoPitchRot , CamX , CamY , CamZ ) ;
FwdDot = CamX dot ToEpicenter ;
if ( FwdDot > 0.707 f )
{
// use forward
ChosenShake = ExplosionTemplate . CamShake ;
}
else if ( FwdDot > - 0.707 f )
{
// need to determine r or l
RtDot = CamY dot ToEpicenter ;
ChosenShake = ( RtDot > 0. f ) ? ExplosionTemplate . CamShake _Right : ExplosionTemplate . CamShake _Left ;
}
else
{
// use back
ChosenShake = ExplosionTemplate . CamShake _Rear ;
}
}
if ( ChosenShake == None )
{
// fall back to forward
ChosenShake = ExplosionTemplate . CamShake ;
}
return ChosenShake ;
}
defaultproperties
{
RemoteRole = ROLE _None
bExplodeMoreThanOnce = False ;
Begin Object Class = RB _RadialImpulseComponent Name = ImpulseComponent0
End Object
RadialImpulseComponent = ImpulseComponent0
Components . Add ( ImpulseComponent0 )
//bDebug=TRUE
}