2020-12-13 15:01:13 +00:00
//=============================================================================
// KFProj_Grenade
//=============================================================================
// Base class for grenade projectiles
//=============================================================================
// Killing Floor 2
// Copyright (C) 2015 Tripwire Interactive LLC
// John "Ramm-Jaeger" Gibson
//=============================================================================
class KFProj _Grenade extends KFProjectile
abstract
native ;
/** How long to wait until this grenade explodes */
var float FuseTime ;
/** Information about grenade bounce effects for this class */
var KFImpactEffectInfo GrenadeBounceEffectInfo ;
/** Dampen amount for every bounce */
var ( ) float DampenFactor ;
/** Dampen amount for parallel angle to velocity */
var ( ) float DampenFactorParallel ;
/** How much to offset the emitter mesh when the grenade has landed so that it doesn't penetrate the ground */
var ( ) vector LandedTranslationOffset ;
/** Whether or not we want to prevent this grenade from being tossed during the initial zed grab rotation. If the nade can insta kill the player, set to false */
var ( Grab ) bool bAllowTossDuringZedGrabRotation ;
///** The UI image for this grenade. Needed so the images have a reference and are cooked into packages. */
var ( ) Texture2D WeaponSelectTexture ;
/** Set on the server and replicated to clients. Ensures that even if Instigator isn't relevant, we still have a valid team */
var repnotify byte TeamNum ;
/** Should the grenade have the upgrade values of the owning weapon. */
var bool bUpgradable ;
replication
{
if ( bNetInitial && ! bNetOwner )
TeamNum ;
}
/ * e p i c = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
* : : ReplicatedEvent
*
* Called when a variable with the property flag "RepNotify" is replicated
*
* === === === === === === === === === === === === === === === === === ==
* /
simulated event ReplicatedEvent ( name VarName )
{
if ( VarName == nameOf ( TeamNum ) )
{
EnableGrenadeWarning ( ) ;
}
else
{
super . ReplicatedEvent ( VarName ) ;
}
}
event PreBeginPlay ( )
{
super . PreBeginPlay ( ) ;
if ( Instigator != none )
{
TeamNum = Instigator . GetTeamNum ( ) ;
}
}
/ * *
* Set the initial velocity and cook time
* /
simulated event PostBeginPlay ( )
{
Super . PostBeginPlay ( ) ;
RandSpin ( 100000 ) ;
if ( Role == ROLE _Authority )
{
SetTimer ( FuseTime , false , 'ExplodeTimer' ) ;
}
AdjustCanDisintigrate ( ) ;
}
/ * *
* Spawns any effects needed for the flight of this projectile
* /
simulated function SpawnFlightEffects ( )
{
super . SpawnFlightEffects ( ) ;
// Enable the warning on listen servers (and standalone technically, but likely not used)
if ( WorldInfo . Role == ROLE _Authority )
{
EnableGrenadeWarning ( ) ;
}
}
/** Toggles an emitter in the projectile effects particle system to display a warning sprite */
simulated function EnableGrenadeWarning ( )
{
local PlayerController LocalPC ;
if ( ProjEffects == none || GetTeamNum ( ) != 0 )
{
return ;
}
LocalPC = GetALocalPlayerController ( ) ;
if ( LocalPC != none && ! LocalPC . IsSpectating ( ) && LocalPC . GetTeamNum ( ) != GetTeamNum ( ) )
{
ProjEffects . SetFloatParameter ( 'Warning' , 0.75 f ) ;
}
}
/** Override so we can grab our replicated TeamNum */
simulated function byte GetTeamNum ( )
{
if ( ! bNetOwner )
{
return TeamNum ;
}
return super . GetTeamNum ( ) ;
}
/ * *
* Explode after a certain amount of time
* /
function ExplodeTimer ( )
{
local Actor HitActor ;
local vector HitLocation , HitNormal ;
GetExplodeEffectLocation ( HitLocation , HitNormal , HitActor ) ;
TriggerExplosion ( HitLocation , HitNormal , HitActor ) ;
}
simulated function Destroyed ( )
{
local Actor HitActor ;
local vector HitLocation , HitNormal ;
// Final Failsafe check for explosion effect
if ( ! bHasExploded && ! bHasDisintegrated )
{
GetExplodeEffectLocation ( HitLocation , HitNormal , HitActor ) ;
TriggerExplosion ( HitLocation , HitNormal , HitActor ) ;
}
super . Destroyed ( ) ;
}
/ * *
* Trace down and get the location to spawn the explosion effects and decal
* /
simulated function GetExplodeEffectLocation ( out vector HitLocation , out vector HitRotation , out Actor HitActor )
{
local vector EffectStartTrace , EffectEndTrace ;
local TraceHitInfo HitInfo ;
EffectStartTrace = Location + vect ( 0 , 0 , 1 ) * 4. f ;
EffectEndTrace = EffectStartTrace - vect ( 0 , 0 , 1 ) * 32. f ;
// Find where to put the decal
HitActor = Trace ( HitLocation , HitRotation , EffectEndTrace , EffectStartTrace , false , , HitInfo , TRACEFLAG _Bullet ) ;
// If the locations are zero (probably because this exploded in the air) set defaults
if ( IsZero ( HitLocation ) )
{
HitLocation = Location ;
}
if ( IsZero ( HitRotation ) )
{
HitRotation = vect ( 0 , 0 , 1 ) ;
}
}
/ * *
* Give a little bounce
* /
simulated event HitWall ( vector HitNormal , Actor Wall , PrimitiveComponent WallComp )
{
// check to make sure we didn't hit a pawn
if ( Pawn ( Wall ) != none )
{
return ;
}
Bounce ( HitNormal , Wall ) ;
// if we are moving too slowly stop moving and lay down flat
// also, don't allow rest on -Z surfaces.
if ( Speed < 40 && HitNormal . Z > 0 )
{
ImpactedActor = Wall ;
GrenadeIsAtRest ( ) ;
}
}
/ * * A d j u s t s m o v e m e n t / p h y s i c s o f p r o j e c t i l e .
* Returns true if projectile actually bounced / was allowed to bounce * /
simulated function bool Bounce ( vector HitNormal , Actor BouncedOff )
{
local vector VNorm ;
if ( WorldInfo . NetMode != NM _DedicatedServer )
{
// do the impact effects
` ImpactEffectManager.PlayImpactEffects(Location, Instigator, HitNormal, GrenadeBounceEffectInfo, true );
}
// Reflect off BouncedOff w/damping
VNorm = ( Velocity dot HitNormal ) * HitNormal ;
Velocity = - VNorm * DampenFactor + ( Velocity - VNorm ) * DampenFactorParallel ;
Speed = VSize ( Velocity ) ;
// also done from ProcessDestructibleTouchOnBounce. update LastBounced to solve problem with bouncing rapidly between world/non-world geometry
LastBounced . Actor = BouncedOff ;
LastBounced . Time = WorldInfo . TimeSeconds ;
return true ;
}
/** Called once the grenade has finished moving */
simulated event GrenadeIsAtRest ( )
{
local rotator NewRotation ;
SetPhysics ( PHYS _None ) ;
if ( ProjEffects != none )
{
ProjEffects . SetTranslation ( LandedTranslationOffset ) ;
}
RotationRate . Yaw = 0 ;
RotationRate . Pitch = 0 ;
RotationRate . Roll = 0 ;
NewRotation . Pitch = 16384 ;
SetRotation ( NewRotation ) ;
}
/** Overriding so that the grenade doesn't take on the */
simulated protected function PrepareExplosionTemplate ( )
{
2022-09-01 15:58:51 +00:00
local Weapon OwnerWeapon ;
local KFPawn _Human OwnerPawn ;
2020-12-13 15:01:13 +00:00
if ( bUpgradable )
{
super . PrepareExplosionTemplate ( ) ;
}
else
{
GetRadialDamageValues ( ExplosionTemplate . Damage , ExplosionTemplate . DamageRadius , ExplosionTemplate . DamageFalloffExponent ) ;
2022-09-01 15:58:51 +00:00
OwnerWeapon = Weapon ( Owner ) ;
if ( OwnerWeapon != none )
{
OwnerPawn = KFPawn _Human ( OwnerWeapon . Owner ) ;
if ( OwnerPawn != none )
{
ExplosionTemplate . DamageRadius *= OwnerPawn . GetPerk ( ) . GetAoERadiusModifier ( ) ;
}
}
2020-12-13 15:01:13 +00:00
}
}
//==============
// Touching
simulated function ProcessTouch ( Actor Other , Vector HitLocation , Vector HitNormal )
{
if ( Other != none && Other != Instigator && ( ! Other . bWorldGeometry || ! Other . bStatic ) )
{
if ( Pawn ( other ) != None )
{
// For opposing team, make the grenade stop and just start falling
if ( Pawn ( Other ) . GetTeamNum ( ) != GetTeamNum ( ) )
{
// Setting SetCollision makes the grenade undetectable by the Siren's scream, so instead
// disable Touch & HitWall event notifications. If there's a problem with using execDisable, we
// can add a new flag instead, like "bStopBounce" to exit ProcessTouch & HitWall early if true.
// Events with indices between NAME_PROBEMIN and NAME_PROBEMAX can be enabled/disabled.
Disable ( 'Touch' ) ;
Velocity = Vect ( 0 , 0 , 0 ) ;
}
}
else if ( ! Other . bCanBeDamaged && Other . bBlockActors )
{
// Not a destructible... treat as if it's bWorldGeometry=TRUE
// e.g. SkeletalMeshActor
if ( ! CheckRepeatingTouch ( Other ) )
{
HitWall ( HitNormal , Other , LastTouchComponent ) ;
}
}
else
{
ProcessDestructibleTouchOnBounce ( Other , HitLocation , HitNormal ) ;
}
}
}
defaultproperties
{
Physics = PHYS _Falling
Speed = 2500
MaxSpeed = 2500
TossZ = 250
bBounce = true
DampenFactor = 0.250000
DampenFactorParallel = 0.400000
LandedTranslationOffset = ( X = 2 )
bCollideComplex = TRUE // Ignore simple collision on StaticMeshes, and collide per poly
TeamNum = 128 // Set to a value none of our teams use
// CollideActors=true allows detection via OverlappingActors or CollidingActors (for Siren scream)
Begin Object Name = CollisionCylinder
CollisionRadius = 10. f
CollisionHeight = 10. f
BlockNonZeroExtent = false
CollideActors = true
End Object
GrenadeBounceEffectInfo = KFImpactEffectInfo 'FX_Impacts_ARCH.DefaultGrenadeImpacts'
// Grenade explosion light
Begin Object Class = PointLightComponent Name = ExplosionPointLight
LightColor = ( R = 252 , G = 218 , B = 171 , A = 255 )
Brightness = 4. f
Radius = 2000. f
FalloffExponent = 10. f
CastShadows = False
CastStaticShadows = FALSE
CastDynamicShadows = False
bEnabled = FALSE
LightingChannels = ( Indoor = TRUE , Outdoor = TRUE , bInitialized = TRUE )
End Object
// network
bNetTemporary = False
bAlwaysReplicateExplosion = true
bAlwaysReplicateDisintegration = true
AlwaysRelevantDistanceSquared = 6250000 // 25m
// gameplay
FuseTime = 2.0
bBlockedByInstigator = false
bCanDisintegrate = true
TouchTimeThreshhold = 0.15
GlassShatterType = FMGS _ShatterAll
bAllowTossDuringZedGrabRotation = false
bUpgradable = false
}