358 lines
9.4 KiB
Ucode
358 lines
9.4 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()
|
|
{
|
|
if (bUpgradable)
|
|
{
|
|
super.PrepareExplosionTemplate();
|
|
}
|
|
else
|
|
{
|
|
GetRadialDamageValues(ExplosionTemplate.Damage, ExplosionTemplate.DamageRadius, ExplosionTemplate.DamageFalloffExponent);
|
|
}
|
|
}
|
|
|
|
//==============
|
|
// 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
|
|
}
|
|
|