482 lines
14 KiB
Ucode
482 lines
14 KiB
Ucode
//=============================================================================
|
|
// KFProj_Cannonball_Blunderbuss
|
|
//=============================================================================
|
|
// Killing Floor 2
|
|
// Copyright (C) 2020 Tripwire Interactive LLC
|
|
// - Tulio Beloqui (Saber Interactive)
|
|
//=============================================================================
|
|
|
|
class KFProj_Cannonball_Blunderbuss extends KFProj_BallisticExplosive
|
|
hidedropdown;
|
|
|
|
/** Additional X Axis velocity to apply when launching this projectile */
|
|
var(Projectile) float TossX;
|
|
|
|
/** Factor added to the rolling speed of the ball when bouncing **/
|
|
var(Projectilve) float RollingFactor;
|
|
|
|
/** Indicates that the ball hit the wall and is doing rolling animations **/
|
|
var transient bool bIsRolling;
|
|
|
|
/** Flag indicating that the player is no longer pressing the fire button and the projectile should explode as soon as it cans **/
|
|
var transient bool bReadyToDetonate;
|
|
|
|
/** Amount of roll stored for this cannonball **/
|
|
var transient float CurrentRoll;
|
|
|
|
var ParticleSystemComponent ProjIndicatorEffects;
|
|
var bool IndicatorActive;
|
|
var bool bHasAlreadyBounced;
|
|
/** This is the effect indicator that is played for the current user **/
|
|
var(Projectile) ParticleSystem ProjIndicatorTemplate;
|
|
|
|
/** This is the effect indicator that is played for the current user in zed time **/
|
|
var(Projectile) ParticleSystem ProjIndicatorTemplateZedTime;
|
|
|
|
//var array<Actor> vActorsTouched;
|
|
|
|
function Init(vector Direction)
|
|
{
|
|
Super.Init(Direction);
|
|
//Velocity.X += TossX;
|
|
}
|
|
|
|
simulated function PreBeginPlay()
|
|
{
|
|
Super.PreBeginPlay();
|
|
bHasAlreadyBounced = false;
|
|
}
|
|
|
|
simulated function TryActivateIndicator()
|
|
{
|
|
if(!IndicatorActive && Instigator != None)
|
|
{
|
|
IndicatorActive = true;
|
|
|
|
if(WorldInfo.NetMode == NM_Standalone || Instigator.Role == Role_AutonomousProxy ||
|
|
(Instigator.Role == ROLE_Authority && WorldInfo.NetMode == NM_ListenServer && Instigator.IsLocallyControlled() ))
|
|
{
|
|
if( `IsInZedTime(self) && ProjIndicatorTemplateZedTime != none )
|
|
{
|
|
ProjIndicatorEffects = WorldInfo.MyEmitterPool.SpawnEmitterCustomLifetime(ProjIndicatorTemplateZedTime);
|
|
}
|
|
// Play normal speed flight effects
|
|
else if( ProjIndicatorTemplate != None )
|
|
{
|
|
ProjIndicatorEffects = WorldInfo.MyEmitterPool.SpawnEmitterCustomLifetime(ProjIndicatorTemplate);
|
|
}
|
|
|
|
if(ProjIndicatorEffects != None)
|
|
{
|
|
ProjIndicatorEffects.SetAbsolute(false, false, false);
|
|
ProjIndicatorEffects.SetLODLevel(WorldInfo.bDropDetail ? 1 : 0);
|
|
ProjIndicatorEffects.bUpdateComponentInTick = true;
|
|
AttachComponent(ProjIndicatorEffects);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/** Called when the owning instigator controller has left a game */
|
|
simulated function OnInstigatorControllerLeft()
|
|
{
|
|
if( WorldInfo.NetMode != NM_Client )
|
|
{
|
|
SetTimer( 1.f + Rand(5) + fRand(), false, nameOf(Timer_Detonate) );
|
|
}
|
|
}
|
|
|
|
function Timer_Detonate()
|
|
{
|
|
Detonate();
|
|
}
|
|
|
|
function Detonate()
|
|
{
|
|
local vector ExplosionNormal, vExplosionOffset;
|
|
|
|
// Check if the bomb should explode right now
|
|
if (bIsRolling && !bHasExploded && !bHasDisintegrated)
|
|
{
|
|
ExplosionNormal = vect(0,0,1) >> Rotation;
|
|
vExplosionOffset.x = 0;
|
|
vExplosionOffset.y = 0;
|
|
vExplosionOffset.z = 10;
|
|
SetLocation(Location + vExplosionOffset);
|
|
CallExplode(Location, ExplosionNormal);
|
|
}
|
|
// If not, mark the bomb to explode as soon as it hits something
|
|
else
|
|
{
|
|
bIsTimedExplosive = false;
|
|
bNetDirty = true;
|
|
}
|
|
}
|
|
|
|
simulated function TriggerExplosion(Vector HitLocation, Vector HitNormal, Actor HitActor)
|
|
{
|
|
local KFWeap_Pistol_Blunderbuss Blunderbuss;
|
|
|
|
if (Role == ROLE_Authority)
|
|
{
|
|
Blunderbuss = KFWeap_Pistol_Blunderbuss(Owner);
|
|
if (Blunderbuss != none)
|
|
{
|
|
Blunderbuss.RemoveDeployedCannonball(, self);
|
|
}
|
|
}
|
|
|
|
Super.TriggerExplosion(HitLocation, HitNormal, HitActor);
|
|
}
|
|
|
|
simulated function Disintegrate( rotator InDisintegrateEffectRotation )
|
|
{
|
|
local KFWeap_Pistol_Blunderbuss Blunderbuss;
|
|
|
|
if (Role == ROLE_Authority)
|
|
{
|
|
Blunderbuss = KFWeap_Pistol_Blunderbuss(Owner);
|
|
if (Blunderbuss != none)
|
|
{
|
|
Blunderbuss.RemoveDeployedCannonball(, self);
|
|
}
|
|
}
|
|
|
|
super.Disintegrate(InDisintegrateEffectRotation);
|
|
}
|
|
|
|
simulated function SetIsDud(bool bWantsClientSideDudHit, vector HitNormal)
|
|
{
|
|
// This projectile doesn't dud.
|
|
}
|
|
|
|
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);
|
|
}
|
|
|
|
TryActivateIndicator();
|
|
Super.Tick(DeltaTime);
|
|
}
|
|
|
|
simulated event HitWall(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);
|
|
}
|
|
}
|
|
|
|
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)))
|
|
{
|
|
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);
|
|
}
|
|
|
|
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;
|
|
}
|
|
}
|
|
|
|
StopSimulating();
|
|
}
|
|
}
|
|
|
|
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;
|
|
|
|
if (ProjIndicatorEffects!=None)
|
|
{
|
|
ProjIndicatorEffects.DeactivateSystem();
|
|
}
|
|
}
|
|
|
|
simulated function SyncOriginalLocation()
|
|
{
|
|
local Actor HitActor;
|
|
local vector HitLocation, HitNormal;
|
|
local TraceHitInfo HitInfo;
|
|
|
|
if (Role < ROLE_Authority && Instigator != none && Instigator.IsLocallyControlled())
|
|
{
|
|
HitActor = Trace(HitLocation, HitNormal, OriginalLocation, Location,,, HitInfo, TRACEFLAG_Bullet);
|
|
if (HitActor != none)
|
|
{
|
|
ServerForceExplosion();
|
|
}
|
|
}
|
|
|
|
Super.SyncOriginalLocation();
|
|
}
|
|
|
|
reliable server function ServerForceExplosion()
|
|
{
|
|
local vector ExplosionNormal;
|
|
|
|
if (Instigator.Role == ROLE_Authority && !bHasExploded && !bHasDisintegrated)
|
|
{
|
|
ExplosionNormal = vect(0,0,1) >> Rotation;
|
|
CallExplode(Location, ExplosionNormal);
|
|
}
|
|
}
|
|
|
|
defaultproperties
|
|
{
|
|
TouchTimeThreshhold = 60.0f
|
|
Physics=PHYS_Falling
|
|
Speed=3200
|
|
MaxSpeed=3200
|
|
TerminalVelocity=3200
|
|
TossZ=150
|
|
GravityScale=1.0
|
|
MomentumTransfer=50000.0
|
|
LifeSpan=0.f
|
|
bWarnAIWhenFired=true
|
|
RollingFactor=1100
|
|
MinSpeedBeforeStop=5
|
|
ResetRotationOnStop=false
|
|
|
|
// Rolling and dampen values
|
|
TossX=150.0
|
|
DampenFactor=0.1
|
|
DampenFactorParallel=0
|
|
WallHitDampenFactor=0.5
|
|
WallHitDampenFactorParallel=0.5
|
|
|
|
ExtraLineCollisionOffsets.Add((Y=-30))
|
|
ExtraLineCollisionOffsets.Add((Y=30))
|
|
ExtraLineCollisionOffsets.Add((Z=-30))
|
|
ExtraLineCollisionOffsets.Add((Z=30))
|
|
// Since we're still using an extent cylinder, we need a line at 0
|
|
ExtraLineCollisionOffsets.Add(())
|
|
|
|
bNetTemporary=False
|
|
NetPriority=5
|
|
NetUpdateFrequency=200
|
|
|
|
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'KFExplosion_BlunderbussCannonball'
|
|
|
|
ProjFlightTemplate=ParticleSystem'WEP_Blunderbuss_EMIT.FX_Cannonball_Projectile'
|
|
ProjFlightTemplateZedTime=ParticleSystem'WEP_Blunderbuss_EMIT.FX_Cannonball_Projectile_ZEDTIME'
|
|
|
|
ProjIndicatorTemplate=ParticleSystem'WEP_Blunderbuss_EMIT.FX_Cannonball_Projectile_Indicator'
|
|
ProjIndicatorTemplateZedTime=ParticleSystem'WEP_Blunderbuss_EMIT.FX_Cannonball_Projectile_Indicator_ZEDTIME'
|
|
|
|
GrenadeBounceEffectInfo=KFImpactEffectInfo'FX_Impacts_ARCH.DefaultGrenadeImpacts'
|
|
ProjDisintegrateTemplate=ParticleSystem'ZED_Siren_EMIT.FX_Siren_grenade_disable_01'
|
|
AltExploEffects=KFImpactEffectInfo'WEP_Blunderbuss_ARCH.Cannonball_Explosion_Concussive_Force'
|
|
|
|
// 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
|
|
bCastPerObjectShadows=false
|
|
bEnabled=FALSE
|
|
LightingChannels=(Indoor=TRUE,Outdoor=TRUE,bInitialized=TRUE)
|
|
End Object
|
|
|
|
// explosion
|
|
Begin Object Class=KFGameExplosion Name=ExploTemplate0
|
|
Damage=250
|
|
DamageRadius=750
|
|
DamageFalloffExponent=2
|
|
DamageDelay=0.f
|
|
|
|
// Damage Effects
|
|
MyDamageType=class'KFDT_Explosive_Blunderbuss'
|
|
KnockDownStrength=0
|
|
FractureMeshRadius=200.0
|
|
FracturePartVel=500.0
|
|
ExplosionEffects=KFImpactEffectInfo'WEP_Blunderbuss_ARCH.Cannonball_Explosion'
|
|
ExplosionSound=AkEvent'WW_WEP_SA_M79.Play_WEP_SA_M79_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
|
|
End Object
|
|
ExplosionTemplate=ExploTemplate0
|
|
|
|
//AmbientSoundPlayEvent=AkEvent'WW_WEP_SA_M79.Play_WEP_SA_M79_Projectile_Loop'
|
|
//AmbientSoundStopEvent=AkEvent'WW_WEP_SA_M79.Stop_WEP_SA_M79_Projectile_Loop'
|
|
}
|