1
0
KF2-Dev-Scripts/KFGameContent/Classes/KFProj_Grenade_GravityImploderAlt.uc
2024-01-23 19:25:12 +03:00

588 lines
16 KiB
Ucode

//=============================================================================
// KFProj_Grenade_GravityImploderAlt
//=============================================================================
// Killing Floor 2
// Copyright (C) 2020 Tripwire Interactive LLC
//=============================================================================
class KFProj_Grenade_GravityImploderAlt extends KFProj_BallisticExplosive
hidedropdown;
/** Factor added to the rolling speed of the ball when bouncing **/
var(Projectile) float RollingFactor;
/** Indicates that the ball hit the wall and is doing rolling animations **/
var transient bool bIsRolling;
/** Amount of roll stored for this cannonball **/
var transient float CurrentRoll;
var bool bHasAlreadyBounced;
/** Collider **/
var Object Collider;
/** Time before starting the implosion effect **/
var float PreparationTime;
/** Vortex params. */
var float VortexDuration;
var float VortexTime;
var float VortexRadius;
var float VortexAbsorptionStrength;
var float VortexElevationStrength;
var bool bVortexReduceImpulseOnDist;
var protected transient bool bFirstAbsorption;
var protected transient vector VortexLocation;
var protected vector VortexNormal;
var protected KFImpactEffectInfo VortexImpactEffects;
simulated state PreparingState
{
simulated function BeginState(Name PrevStateName)
{
super.BeginState(PrevStateName);
SetTimer( PreparationTime, false, nameOf(Timer_Ready) );
}
simulated event Tick(float DeltaTime)
{
local vector RollDelta;
local rotator NewRotation;
// Let's roll (only in the client)
if ( bIsRolling && WorldInfo.NetMode != NM_DedicatedServer && Physics != PHYS_None && (Velocity.X != 0 || Velocity.Y != 0) )
{
CurrentRoll -= (Abs(Velocity.X) + Abs(Velocity.Y)) * DeltaTime * RollingFactor;
RollDelta = ((vect(1, 0 , 0) * (Velocity.X)) + (vect(0, 1, 0) * (Velocity.Y) ));
NewRotation = Rotator(RollDelta);
NewRotation.pitch += CurrentRoll;
SetRotation(NewRotation);
}
Super.Tick(DeltaTime);
}
simulated event HitWall(vector HitNormal, actor Wall, PrimitiveComponent WallComp)
{
VortexNormal = HitNormal;
ProcessRebound(HitNormal, Wall, WallComp);
if( !bDud && !bIsTimedExplosive )
{
Super.HitWall(HitNormal, Wall, WallComp);
}
}
simulated function ProcessTouch(Actor Other, Vector HitLocation, Vector HitNormal)
{
local bool bWantsClientSideDudHit;
local float TraveledDistance;
local Vector VNorm;
//local int Index;
// If we collided with a Siren shield, let the shield code handle touches
if( Other.IsA('KFTrigger_SirenProjectileShield') )
{
return;
}
if ( !bCollideWithTeammates && Pawn(Other) != None )
{
// Don't hit teammates
if( Other.GetTeamNum() == GetTeamNum() )
{
return;
}
}
// Need to do client side dud hits if this is a client
if( Instigator != none && Instigator.Role < ROLE_Authority )
{
bWantsClientSideDudHit = true;
}
TraveledDistance = (`TimeSince(CreationTime) * Speed);
TraveledDistance *= TraveledDistance;
if( (!bDud || ( bWantsClientSideDudHit && !bClientDudHit)) && ((TraveledDistance < ArmDistSquared) || bIsTimedExplosive || (OriginalLocation == vect(0,0,0) && ArmDistSquared > 0)))
{
// Don't touch the same actor multiple time's immediately after just
// touching it if the TouchTimeThreshhold is set to greater than 0.
// This was causing projectiles just to "stop" sometimes when hitting
// dead/ragdolled pawns because it was counting as multiple penetrations
if( LastTouched.Actor == Other && TouchTimeThreshhold > 0
&& `TimeSince(LastTouched.Time) <= TouchTimeThreshhold )
{
return;
}
//TODO: Add an impact sound here
SetIsDud(bWantsClientSideDudHit, HitNormal);
if (Other != Instigator && !Other.bStatic && Other.GetTeamNum() != GetTeamNum() && !CheckRepeatingTouch(Other))
{
ProcessBulletTouch(Other, HitLocation, HitNormal);
}
// Reflect off Wall w/damping but allow penetration if the pawn is dead
//if(KFPawn_Monster(Other) == None || KFPawn_Monster(Other).Health > 0)
//{
VNorm = (Velocity dot HitNormal) * HitNormal;
Velocity = -VNorm * DampenFactor + (Velocity - VNorm) * DampenFactorParallel;
Speed = VSize(Velocity);
//}
}
else if (!bDud && !bIsTimedExplosive)
{
// Process impact hits
if (Other != Instigator && !Other.bStatic)
{
// check/ignore repeat touch events
if( !CheckRepeatingTouch(Other) && Other.GetTeamNum() != GetTeamNum())
{
ProcessBulletTouch(Other, HitLocation, HitNormal);
}
}
if( WorldInfo.NetMode == NM_Standalone ||
(WorldInfo.NetMode == NM_ListenServer && Instigator != none && Instigator.IsLocallyControlled()) )
{
Super.ProcessTouch( Other, HitLocation, HitNormal );
return;
}
if( Owner != none && KFWeapon( Owner ) != none && Instigator != none )
{
if( Instigator.Role < ROLE_Authority && Instigator.IsLocallyControlled() )
{
KFWeapon(Owner).HandleClientProjectileExplosion(HitLocation, self);
Super.ProcessTouch( Other, HitLocation, HitNormal );
return;
}
}
}
}
}
simulated state WaitingToImplode
{
simulated function BeginState(Name PrevStateName)
{
super.BeginState(PrevStateName);
}
simulated event HitWall(vector HitNormal, actor Wall, PrimitiveComponent WallComp)
{
if (HitNormal Dot vect(0,0,1) > 0.5f)
{
GotoState('ImplodingState');
return;
}
ProcessRebound(HitNormal, Wall, WallComp);
if(!bDud && !bIsTimedExplosive)
{
Super.HitWall(HitNormal, Wall, WallComp);
}
}
simulated function Tick(float Delta)
{
if (vSize(Velocity) < 0.05f)
{
GotoState('ImplodingState');
return;
}
super.Tick(Delta);
}
simulated function ProcessTouch(Actor Other, Vector HitLocation, Vector HitNormal)
{
// Prevent default funcionality;
super.ProcessTouch(Other, HitLocation, HitNormal);
}
}
simulated state ImplodingState
{
simulated function BeginState(Name PrevStateName)
{
super.BeginState(PrevStateName);
StopSimulating();
AdjustVortexForPerk();
if ( WorldInfo.NetMode != NM_Client )
{
Velocity=vect(0,0,0);
Acceleration=vect(0,0,0);
RotationRate=rot(0,0,0);
GravityScale=0.0;
bFirstAbsorption=true;
VortexTime=0.0f;
SetTimer(VortexDuration, false, nameOf(Detonation_Ready) );
}
VortexLocation = Location + vect(0,0,1) * 125;
// Start VGC
StartVortexVFX();
}
simulated function StartVortexVFX()
{
if(VortexImpactEffects != None)
{
if(VortexImpactEffects.DefaultImpactEffect.ParticleTemplate != None)
{
WorldInfo.MyEmitterPool.SpawnEmitter(VortexImpactEffects.DefaultImpactEffect.ParticleTemplate, VortexLocation, rotator(VortexNormal));
}
if(VortexImpactEffects.DefaultImpactEffect.Sound != None)
{
PlaySoundBase(VortexImpactEffects.DefaultImpactEffect.Sound, true,,, VortexLocation);
}
}
}
simulated function EndState(Name NextStateName)
{
super.EndState(NextStateName);
}
simulated event Tick(float DeltaTime)
{
Super.Tick(DeltaTime);
// Avoids to be moved in case there's any force applied.
SetLocation(VortexLocation);
`if(`notdefined(ShippingPC))
if( KFWeap_GravityImploder(Owner) != none && KFWeap_GravityImploder(Owner).bDebugDrawVortex)
{
DrawDebugSphere(Location, VortexRadius, 125, 0, 0, 255, false);
}
`endif
if (WorldInfo.NetMode < NM_Client)
{
VortexTime += DeltaTime;
AbsorbEnemies();
bFirstAbsorption=false;
}
}
function AdjustVortexForPerk()
{
local KFPlayerController KFPC;
local KFPerk Perk;
KFPC = KFPlayerController(InstigatorController);
if( KFPC != none )
{
Perk = KFPC.GetPerk();
if(Perk != none)
{
VortexRadius = default.VortexRadius * Perk.GetAoERadiusModifier();
}
}
}
simulated function AbsorbEnemies()
{
local Actor Victim;
local TraceHitInfo HitInfo;
local KFPawn KFP;
local KFPawn_Monster KFPM;
local float ColRadius, ColHeight;
local float Dist;
local vector Dir;
local vector Momentum;
local float MomentumModifier;
foreach CollidingActors(class'Actor', Victim, VortexRadius, VortexLocation, true,, HitInfo)
{
KFP = KFPawn(Victim);
KFPM = KFPawn_Monster(Victim);
if (Victim != Self
&& (!Victim.bWorldGeometry || Victim.bCanBeDamaged)
&& (NavigationPoint(Victim) == None)
&& Victim != Instigator
&& KFP != None
&& KFPawn_Human(Victim) == none // No player's character
&& KFPawn_Scripted(Victim) == none
&& KFPawn_AutoTurret(Victim) == none
&& KFPawn_HRG_Warthog(Victim) == none
&& (KFPM == none || VortexTime < VortexDuration*KFPM.GetVortexAttractionModifier()) )
{
KFP.GetBoundingCylinder(ColRadius, ColHeight);
if (bFirstAbsorption)
{
Dir = vect(0,0,1);
Momentum = Dir * VortexElevationStrength;
}
else
{
Dir = Normal(VortexLocation - KFP.Location);
Dist = FMax(vSize(Dir) - ColRadius, 0.f);
MomentumModifier = bVortexReduceImpulseOnDist ? (1.0f - Dist / VortexRadius) : 1.0f;
Momentum = Dir * VortexAbsorptionStrength * MomentumModifier + vect(0,0,1) * (Dist/VortexRadius) * VortexAbsorptionStrength;
}
if(KFPM != none)
{
Momentum *= KFPM.GetVortexAttractionModifier();
}
KFP.AddVelocity( Momentum, KFP.Location - 0.5 * (ColHeight + ColRadius) * Dir, class 'KFDT_Explosive_GravityImploder');
}
}
}
}
simulated state DetonatingState
{
simulated function BeginState(Name PrevStateName)
{
super.BeginState(PrevStateName);
Detonate();
}
}
simulated function PostBeginPlay()
{
Super.PostBeginPlay();
GotoState('PreparingState');
}
simulated function Timer_Ready()
{
// GotoState('ImplodingState');
GotoState('WaitingToImplode');
}
simulated function Detonation_Ready()
{
GotoState('DetonatingState');
}
simulated function Detonate()
{
local vector ExplosionNormal, vExplosionOffset;
// Check if the bomb should explode right now
if (!bHasExploded && !bHasDisintegrated)
{
ExplosionNormal = vect(0,0,1) >> Rotation;
vExplosionOffset.x = 0;
vExplosionOffset.y = 0;
vExplosionOffset.z = 10;
SetLocation(VortexLocation + vExplosionOffset);
CallExplode(VortexLocation, ExplosionNormal);
}
// If not, mark the bomb to explode as soon as it hits something
else
{
bIsTimedExplosive = false;
bNetDirty = true;
}
}
simulated function SetIsDud(bool bWantsClientSideDudHit, vector HitNormal)
{
// This projectile doesn't dud.
}
simulated protected function StopSimulating()
{
Velocity = vect(0,0,0);
Acceleration = vect(0,0,0);
RotationRate = rot(0,0,0);
SetCollision(FALSE, FALSE);
StopFlightEffects();
bRotationFollowsVelocity = FALSE;
}
simulated function ProcessRebound(vector HitNormal, actor Wall, PrimitiveComponent WallComp)
{
local Vector VNorm;
local rotator NewRotation;
local Vector Offset;
local bool bWantsClientSideDudHit;
local TraceHitInfo HitInfo;
local float TraveledDistance;
bIsRolling = true;
// Need to do client side dud hits if this is a client
if( Instigator != none && Instigator.Role < ROLE_Authority )
{
bWantsClientSideDudHit = true;
}
TraveledDistance = (`TimeSince(CreationTime) * Speed);
TraveledDistance *= TraveledDistance;
// Bounce off the wall and cause the shell to dud if we hit too close
if( bDud || ((TraveledDistance < ArmDistSquared) || bIsTimedExplosive || (OriginalLocation == vect(0,0,0) && ArmDistSquared > 0)))
{
// Reflect off Wall w/damping
VNorm = (Velocity dot HitNormal) * HitNormal;
Velocity = -VNorm * WallHitDampenFactor + (Velocity - VNorm) * WallHitDampenFactorParallel;
Speed = VSize(Velocity);
if( (!bDud || ( bWantsClientSideDudHit && !bClientDudHit)) )
{
SetIsDud(bWantsClientSideDudHit, HitNormal);
}
if ( WorldInfo.NetMode != NM_DedicatedServer && Pawn(Wall) == none && bHasAlreadyBounced == false )
{
// do the impact effects
bHasAlreadyBounced = true;
`ImpactEffectManager.PlayImpactEffects(Location, Instigator, HitNormal, GrenadeBounceEffectInfo, true );
}
// if we hit a pawn or we are moving too slowly stop moving and lay down flat
if ( Speed < MinSpeedBeforeStop )
{
ImpactedActor = Wall;
SetPhysics(PHYS_None);
if( ProjEffects != none )
{
ProjEffects.SetTranslation(LandedTranslationOffset);
}
// Position the shell on the ground
RotationRate.Yaw = 0;
RotationRate.Pitch = 0;
RotationRate.Roll = 0;
NewRotation = Rotation;
NewRotation.Pitch = 0;
if(ResetRotationOnStop)
{
SetRotation(NewRotation);
}
Offset.Z = LandedTranslationOffset.X;
SetLocation(Location + Offset);
}
if( !Wall.bStatic && Wall.bCanBeDamaged && (DamageRadius == 0 || bDamageDestructiblesOnTouch) && !CheckRepeatingTouch(Wall) )
{
HitInfo.HitComponent = WallComp;
HitInfo.Item = INDEX_None;
Wall.TakeDamage( Damage, InstigatorController, Location, MomentumTransfer * Normal(Velocity), MyDamageType, HitInfo, self);
}
}
}
defaultproperties
{
TouchTimeThreshhold = 60.0f
Physics=PHYS_Falling
Speed=3200
MaxSpeed=3200
TerminalVelocity=3200
GravityScale=1.0
MomentumTransfer=100000
LifeSpan=0.f
bWarnAIWhenFired=true
RollingFactor=1100
MinSpeedBeforeStop=5
ResetRotationOnStop=false
// Rolling and dampen values
DampenFactor=0.1
DampenFactorParallel=0
WallHitDampenFactor=0.4 //0.5
WallHitDampenFactorParallel=0.4 //0.5
bCollideComplex=TRUE // Ignore simple collision on StaticMeshes, and collide per poly
bUseClientSideHitDetection=true
bNoReplicationToInstigator=false
bAlwaysReplicateExplosion=true;
bUpdateSimulatedPosition=true
Begin Object Name=CollisionCylinder
CollisionRadius=0.f
CollisionHeight=0.f
BlockNonZeroExtent=false
End Object
ExplosionActorClass=class'KFExplosionActor'
ProjFlightTemplate=ParticleSystem'WEP_Gravity_Imploder_EMIT.FX_Blue_Projectile'
ProjFlightTemplateZedTime=ParticleSystem'WEP_Gravity_Imploder_EMIT.FX_Blue_Projectile_ZEDTIME'
GrenadeBounceEffectInfo=KFImpactEffectInfo'FX_Impacts_ARCH.DefaultGrenadeImpacts'
ProjDisintegrateTemplate=ParticleSystem'ZED_Siren_EMIT.FX_Siren_grenade_disable_01'
AltExploEffects=KFImpactEffectInfo'WEP_Gravity_Imploder_ARCH.Blue_Explosion_Concussive_Force'
// Grenade explosion light
Begin Object Class=PointLightComponent Name=ExplosionPointLight
LightColor=(R=0,G=50,B=171,A=255)
Brightness=4.f
Radius=2000.f
FalloffExponent=10.f
CastShadows=False
CastStaticShadows=FALSE
CastDynamicShadows=False
bCastPerObjectShadows=false
bEnabled=FALSE
LightingChannels=(Indoor=TRUE,Outdoor=TRUE,bInitialized=TRUE)
End Object
// explosion
Begin Object Class=KFGameExplosion Name=ExploTemplate0
Damage=1
DamageRadius=450 //600
DamageFalloffExponent=0.f //2
DamageDelay=0.f
MomentumTransferScale=10000
// Damage Effects
MyDamageType=class'KFDT_Explosive_GravityImploderWave'
KnockDownStrength=150
FractureMeshRadius=200.0
FracturePartVel=500.0
ExplosionEffects=KFImpactEffectInfo'WEP_Gravity_Imploder_ARCH.Blue_Explosion'
ExplosionSound=AkEvent'WW_WEP_Gravity_Imploder.Play_WEP_Gravity_Imploder_Grenade_Blue_Explosion'
// Dynamic Light
ExploLight=ExplosionPointLight
ExploLightStartFadeOutTime=0.0
ExploLightFadeOutTime=0.2
// Camera Shake
CamShake=CameraShake'FX_CameraShake_Arch.Misc_Explosions.Light_Explosion_Rumble'
CamShakeInnerRadius=200
CamShakeOuterRadius=900
CamShakeFalloff=1.5f
bOrientCameraShakeTowardsEpicenter=true
bIgnoreInstigator=false
End Object
ExplosionTemplate=ExploTemplate0
bIsTimedExplosive=true;
PreparationTime=0.8 //1.0
VortexDuration=0.5 //0.7
VortexRadius=500 //650
VortexAbsorptionStrength=120 //100
VortexElevationStrength=700
bVortexReduceImpulseOnDist=false
bFirstAbsorption=true
VortexImpactEffects=KFImpactEffectInfo'WEP_Gravity_Imploder_ARCH.Blue_Attract'
}