1
0
KF2-Dev-Scripts/KFGame/Classes/KFProj_Grenade.uc
2022-09-01 18:58:51 +03:00

371 lines
9.7 KiB
Ucode

//=============================================================================
// 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;
}
/* epic ===============================================
* ::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.75f );
}
}
/** 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();
}
}
/** Adjusts movement/physics of projectile.
* 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()
{
local Weapon OwnerWeapon;
local KFPawn_Human OwnerPawn;
if (bUpgradable)
{
super.PrepareExplosionTemplate();
}
else
{
GetRadialDamageValues(ExplosionTemplate.Damage, ExplosionTemplate.DamageRadius, ExplosionTemplate.DamageFalloffExponent);
OwnerWeapon = Weapon(Owner);
if (OwnerWeapon != none)
{
OwnerPawn = KFPawn_Human(OwnerWeapon.Owner);
if (OwnerPawn != none)
{
ExplosionTemplate.DamageRadius *= OwnerPawn.GetPerk().GetAoERadiusModifier();
}
}
}
}
//==============
// 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
}