448 lines
12 KiB
Ucode
448 lines
12 KiB
Ucode
//=============================================================================
|
|
// SeekerSixRocketProjectile
|
|
//=============================================================================
|
|
// Rocket projectile class for the SeekerSix mini rocket launcher
|
|
//=============================================================================
|
|
// Killing Floor Source
|
|
// Copyright (C) 2013 Tripwire Interactive LLC
|
|
// - John "Ramm-Jaeger" Gibson
|
|
//=============================================================================
|
|
class SeekerSixRocketProjectile extends LAWProj;
|
|
|
|
//var Emitter Corona;
|
|
var xEmitter RocketTrail;
|
|
var xEmitter RocketSmoke;
|
|
|
|
var byte FlockIndex;
|
|
var SeekerSixRocketProjectile Flock[6];
|
|
|
|
var(Flocking) float FlockRadius;
|
|
var(Flocking) float FlockStiffness;
|
|
var(Flocking) float FlockMaxForce;
|
|
var(Flocking) float FlockCurlForce;
|
|
var bool bCurl;
|
|
|
|
var string AmbientSoundTwoRef;
|
|
var string AmbientSoundThreeRef;
|
|
var(Sound) sound AmbientSoundTwo; // Ambient sound effect.
|
|
var(Sound) sound AmbientSoundThree; // Ambient sound effect.
|
|
|
|
var() InterpCurve AppliedMomentumCurve; // How much momentum to apply to a zed based on how much mass it has
|
|
|
|
replication
|
|
{
|
|
reliable if ( bNetInitial && (Role == ROLE_Authority) )
|
|
FlockIndex, bCurl;
|
|
}
|
|
|
|
static function PreloadAssets()
|
|
{
|
|
default.ExplosionSound = sound(DynamicLoadObject(default.ExplosionSoundRef, class'Sound', true));
|
|
default.AmbientSound = sound(DynamicLoadObject(default.AmbientSoundRef, class'Sound', true));
|
|
default.AmbientSoundTwo = sound(DynamicLoadObject(default.AmbientSoundTwoRef, class'Sound', true));
|
|
default.AmbientSoundThree = sound(DynamicLoadObject(default.AmbientSoundThreeRef, class'Sound', true));
|
|
default.DisintegrateSound = sound(DynamicLoadObject(default.DisintegrateSoundRef, class'Sound', true));
|
|
|
|
UpdateDefaultStaticMesh(StaticMesh(DynamicLoadObject(default.StaticMeshRef, class'StaticMesh', true)));
|
|
}
|
|
|
|
static function bool UnloadAssets()
|
|
{
|
|
default.ExplosionSound = none;
|
|
default.AmbientSound = none;
|
|
default.AmbientSoundTwo = none;
|
|
default.AmbientSoundThree = none;
|
|
default.DisintegrateSound = none;
|
|
|
|
UpdateDefaultStaticMesh(none);
|
|
|
|
return true;
|
|
}
|
|
|
|
simulated function PostBeginPlay()
|
|
{
|
|
BCInverse = 1 / BallisticCoefficient;
|
|
|
|
if( FRand() < 0.33 )
|
|
{
|
|
AmbientSound = AmbientSoundTwo;
|
|
}
|
|
else if( FRand() < 0.33 )
|
|
{
|
|
AmbientSound = AmbientSoundThree;
|
|
}
|
|
|
|
if ( Level.NetMode != NM_DedicatedServer)
|
|
{
|
|
RocketSmoke = Spawn(class'SeekerSixRocketSmokeX',self);
|
|
RocketTrail = Spawn(class'SeekerSixRocketTrailX',self);
|
|
}
|
|
|
|
OrigLoc = Location;
|
|
|
|
if( !bDud )
|
|
{
|
|
Dir = vector(Rotation);
|
|
Velocity = speed * Dir;
|
|
}
|
|
|
|
if (PhysicsVolume.bWaterVolume)
|
|
{
|
|
bHitWater = True;
|
|
Velocity=0.6*Velocity;
|
|
}
|
|
super(Projectile).PostBeginPlay();
|
|
}
|
|
|
|
simulated function PostNetBeginPlay()
|
|
{
|
|
local SeekerSixRocketProjectile R;
|
|
local int i, p, q;
|
|
local Array<SeekerSixRocketProjectile> Rockets;
|
|
|
|
Super.PostNetBeginPlay();
|
|
|
|
if ( FlockIndex != 0 )
|
|
{
|
|
SetTimer(0.1, true);
|
|
|
|
// look for other rockets and set thier flock values
|
|
if ( Flock[1] == None )
|
|
{
|
|
Rockets[0]=self;
|
|
i=1;
|
|
|
|
ForEach DynamicActors(class'SeekerSixRocketProjectile',R)
|
|
if ( R.FlockIndex == FlockIndex )
|
|
{
|
|
if( R == Self )
|
|
{
|
|
continue;
|
|
}
|
|
Rockets[i] = R;
|
|
i++;
|
|
if ( i == 6 )
|
|
break;
|
|
}
|
|
|
|
for ( p = 0; p < Rockets.Length; p++ )
|
|
{
|
|
if ( Rockets[p] != None )
|
|
{
|
|
i = 0;
|
|
for ( q=0; q<Rockets.Length; q++ )
|
|
{
|
|
if ( (p != q) && (Rockets[q] != None) )
|
|
{
|
|
Rockets[p].Flock[i] = Rockets[q];
|
|
i++;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
simulated function Explode(vector HitLocation, vector HitNormal)
|
|
{
|
|
local Controller C;
|
|
local PlayerController LocalPlayer;
|
|
|
|
bHasExploded = True;
|
|
|
|
// Don't explode if this is a dud
|
|
if( bDud )
|
|
{
|
|
Velocity = vect(0,0,0);
|
|
LifeSpan=1.0;
|
|
SetPhysics(PHYS_Falling);
|
|
}
|
|
|
|
PlaySound(ExplosionSound,,2.0);
|
|
if ( EffectIsRelevant(Location,false) )
|
|
{
|
|
Spawn(class'KFMod.SeekerSixExplosionEmitter',,,HitLocation + HitNormal*20,rotator(HitNormal));
|
|
Spawn(ExplosionDecal,self,,HitLocation, rotator(-HitNormal));
|
|
}
|
|
|
|
BlowUp(HitLocation);
|
|
Destroy();
|
|
|
|
// Shake nearby players screens
|
|
LocalPlayer = Level.GetLocalPlayerController();
|
|
if ( (LocalPlayer != None) && (VSize(Location - LocalPlayer.ViewTarget.Location) < DamageRadius) )
|
|
LocalPlayer.ShakeView(RotMag, RotRate, RotTime, OffsetMag, OffsetRate, OffsetTime);
|
|
|
|
for ( C=Level.ControllerList; C!=None; C=C.NextController )
|
|
if ( (PlayerController(C) != None) && (C != LocalPlayer)
|
|
&& (VSize(Location - PlayerController(C).ViewTarget.Location) < DamageRadius) )
|
|
C.ShakeView(RotMag, RotRate, RotTime, OffsetMag, OffsetRate, OffsetTime);
|
|
}
|
|
|
|
simulated function Timer()
|
|
{
|
|
local vector ForceDir, CurlDir;
|
|
local float ForceMag;
|
|
local int i;
|
|
|
|
// Handle disintegration
|
|
if( bDisintegrated && Role == ROLE_Authority )
|
|
{
|
|
Destroy();
|
|
return;
|
|
}
|
|
|
|
Velocity = Default.Speed * Normal(Dir * 0.5 * Default.Speed + Velocity);
|
|
|
|
// Work out force between flock to add madness
|
|
for(i=0; i<6; i++)
|
|
{
|
|
if(Flock[i] == None)
|
|
continue;
|
|
|
|
// Attract if distance between rockets is over 2*FlockRadius, repulse if below.
|
|
ForceDir = Flock[i].Location - Location;
|
|
ForceMag = FlockStiffness * ( (2 * FlockRadius) - VSize(ForceDir) );
|
|
Acceleration = Normal(ForceDir) * Min(ForceMag, FlockMaxForce);
|
|
|
|
// Vector 'curl'
|
|
CurlDir = Flock[i].Velocity Cross ForceDir;
|
|
if ( bCurl == Flock[i].bCurl )
|
|
Acceleration += Normal(CurlDir) * FlockCurlForce;
|
|
else
|
|
Acceleration -= Normal(CurlDir) * FlockCurlForce;
|
|
}
|
|
}
|
|
|
|
function TakeDamage( int Damage, Pawn InstigatedBy, Vector Hitlocation, Vector Momentum, class<DamageType> damageType, optional int HitIndex)
|
|
{
|
|
if( damageType == class'SirenScreamDamage')
|
|
{
|
|
Disintegrate(HitLocation, vect(0,0,1));
|
|
}
|
|
}
|
|
|
|
simulated function Destroyed()
|
|
{
|
|
// if( Corona != none )
|
|
// {
|
|
// Corona.Kill();
|
|
// }
|
|
//
|
|
if ( RocketTrail != none )
|
|
{
|
|
RocketTrail.mRegen=False;
|
|
RocketTrail.SetPhysics(PHYS_None);
|
|
RocketTrail.GotoState('');
|
|
}
|
|
|
|
if ( RocketSmoke != none )
|
|
{
|
|
RocketSmoke.mRegen=False;
|
|
RocketSmoke.SetPhysics(PHYS_None);
|
|
RocketSmoke.GotoState('');
|
|
}
|
|
|
|
|
|
Super.Destroyed();
|
|
}
|
|
|
|
/* HurtRadius()
|
|
Hurt locally authoritative actors within the radius.
|
|
Overridden to scale the momentum of the zeds hit by mass based on a curve we define
|
|
*/
|
|
simulated function HurtRadius( float DamageAmount, float DamageRadius, class<DamageType> DamageType, float Momentum, vector HitLocation )
|
|
{
|
|
local actor Victims;
|
|
local float damageScale, dist;
|
|
local vector dirs;
|
|
local int NumKilled;
|
|
local KFMonster KFMonsterVictim;
|
|
local Pawn P;
|
|
local KFPawn KFP;
|
|
local array<Pawn> CheckedPawns;
|
|
local int i;
|
|
local bool bAlreadyChecked;
|
|
local float UsedMomentum;
|
|
|
|
if ( bHurtEntry )
|
|
return;
|
|
|
|
bHurtEntry = true;
|
|
|
|
foreach CollidingActors (class 'Actor', Victims, DamageRadius, HitLocation)
|
|
{
|
|
// don't let blast damage affect fluid - VisibleCollisingActors doesn't really work for them - jag
|
|
if( (Victims != self) && (Hurtwall != Victims) && (Victims.Role == ROLE_Authority) && !Victims.IsA('FluidSurfaceInfo')
|
|
&& ExtendedZCollision(Victims)==None )
|
|
{
|
|
dirs = Victims.Location - HitLocation;
|
|
dist = FMax(1,VSize(dirs));
|
|
dirs = dirs/dist;
|
|
damageScale = 1 - FMax(0,(dist - Victims.CollisionRadius)/DamageRadius);
|
|
if ( Instigator == None || Instigator.Controller == None )
|
|
Victims.SetDelayedDamageInstigatorController( InstigatorController );
|
|
if ( Victims == LastTouched )
|
|
LastTouched = None;
|
|
|
|
P = Pawn(Victims);
|
|
|
|
UsedMomentum = Momentum;
|
|
|
|
if( P != none )
|
|
{
|
|
for (i = 0; i < CheckedPawns.Length; i++)
|
|
{
|
|
if (CheckedPawns[i] == P)
|
|
{
|
|
bAlreadyChecked = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if( bAlreadyChecked )
|
|
{
|
|
bAlreadyChecked = false;
|
|
P = none;
|
|
continue;
|
|
}
|
|
|
|
// Scale the momentum by the mass of the pawn hit
|
|
UsedMomentum *= InterpCurveEval(AppliedMomentumCurve,Victims.Mass);
|
|
|
|
KFMonsterVictim = KFMonster(Victims);
|
|
|
|
if( KFMonsterVictim != none && KFMonsterVictim.Health <= 0 )
|
|
{
|
|
KFMonsterVictim = none;
|
|
}
|
|
|
|
KFP = KFPawn(Victims);
|
|
|
|
if( KFMonsterVictim != none )
|
|
{
|
|
damageScale *= KFMonsterVictim.GetExposureTo(HitLocation/*Location + 15 * -Normal(PhysicsVolume.Gravity)*/);
|
|
}
|
|
else if( KFP != none )
|
|
{
|
|
damageScale *= KFP.GetExposureTo(HitLocation/*Location + 15 * -Normal(PhysicsVolume.Gravity)*/);
|
|
}
|
|
|
|
CheckedPawns[CheckedPawns.Length] = P;
|
|
|
|
if ( damageScale <= 0)
|
|
{
|
|
P = none;
|
|
continue;
|
|
}
|
|
else
|
|
{
|
|
//Victims = P;
|
|
P = none;
|
|
}
|
|
}
|
|
|
|
|
|
Victims.TakeDamage
|
|
(
|
|
damageScale * DamageAmount,
|
|
Instigator,
|
|
Victims.Location - 0.5 * (Victims.CollisionHeight + Victims.CollisionRadius) * dirs,
|
|
(damageScale * UsedMomentum * dirs),
|
|
DamageType
|
|
);
|
|
|
|
if (Vehicle(Victims) != None && Vehicle(Victims).Health > 0)
|
|
Vehicle(Victims).DriverRadiusDamage(DamageAmount, DamageRadius, InstigatorController, DamageType, UsedMomentum, HitLocation);
|
|
|
|
if( Role == ROLE_Authority && KFMonsterVictim != none && KFMonsterVictim.Health <= 0 )
|
|
{
|
|
NumKilled++;
|
|
}
|
|
}
|
|
}
|
|
if ( (LastTouched != None) && (LastTouched != self) && (LastTouched.Role == ROLE_Authority) && !LastTouched.IsA('FluidSurfaceInfo') )
|
|
{
|
|
Victims = LastTouched;
|
|
LastTouched = None;
|
|
dirs = Victims.Location - HitLocation;
|
|
dist = FMax(1,VSize(dirs));
|
|
dirs = dirs/dist;
|
|
damageScale = FMax(Victims.CollisionRadius/(Victims.CollisionRadius + Victims.CollisionHeight),1 - FMax(0,(dist - Victims.CollisionRadius)/DamageRadius));
|
|
if ( Instigator == None || Instigator.Controller == None )
|
|
Victims.SetDelayedDamageInstigatorController(InstigatorController);
|
|
|
|
UsedMomentum = Momentum;
|
|
|
|
if( Pawn(Victims) != none )
|
|
{
|
|
UsedMomentum *= InterpCurveEval(AppliedMomentumCurve,Victims.Mass);
|
|
}
|
|
|
|
Victims.TakeDamage
|
|
(
|
|
damageScale * DamageAmount,
|
|
Instigator,
|
|
Victims.Location - 0.5 * (Victims.CollisionHeight + Victims.CollisionRadius) * dirs,
|
|
(damageScale * UsedMomentum * dirs),
|
|
DamageType
|
|
);
|
|
if (Vehicle(Victims) != None && Vehicle(Victims).Health > 0)
|
|
Vehicle(Victims).DriverRadiusDamage(DamageAmount, DamageRadius, InstigatorController, DamageType, UsedMomentum, HitLocation);
|
|
}
|
|
|
|
if( Role == ROLE_Authority )
|
|
{
|
|
if( NumKilled >= 4 )
|
|
{
|
|
KFGameType(Level.Game).DramaticEvent(0.05);
|
|
}
|
|
else if( NumKilled >= 2 )
|
|
{
|
|
KFGameType(Level.Game).DramaticEvent(0.03);
|
|
}
|
|
}
|
|
|
|
bHurtEntry = false;
|
|
}
|
|
|
|
defaultproperties
|
|
{
|
|
StaticMeshRef="KF_IJC_Halloween_Weps2.seeker6_projectile"
|
|
LifeSpan=10.000000
|
|
DrawScale=2.500000
|
|
|
|
MyDamageType=Class'KFMod.DamTypeSeekerSixRocket'
|
|
ImpactDamageType=Class'KFMod.DamTypeSeekerRocketImpact'
|
|
RemoteRole=ROLE_SimulatedProxy
|
|
bBounce=false
|
|
RotationRate=(Roll=50000)
|
|
ExplosionDecal=Class'KFMod.FlameThrowerBurnMark_Medium'
|
|
|
|
ImpactDamage=75
|
|
Damage=100
|
|
DamageRadius=150
|
|
ArmDistSquared=10000 // 2 meters
|
|
|
|
Speed=2000.000000
|
|
MaxSpeed=2000.000000
|
|
|
|
FlockRadius=12
|
|
FlockStiffness=-100
|
|
FlockMaxForce=600
|
|
FlockCurlForce=450
|
|
|
|
ExplosionSoundRef="KF_FY_SeekerSixSND.WEP_Seeker_Explode"
|
|
AmbientSoundRef="KF_FY_SeekerSixSND.WEP_Seeker_Rocket_LP"
|
|
AmbientSoundTwoRef="KF_FY_SeekerSixSND.WEP_Seeker_Rocket_LP_02"
|
|
AmbientSoundThreeRef="KF_FY_SeekerSixSND.WEP_Seeker_Rocket_LP_03"
|
|
SoundVolume=255
|
|
SoundRadius=250
|
|
AmbientVolumeScale=5.0
|
|
TransientSoundVolume=2.0
|
|
TransientSoundRadius=500
|
|
|
|
AppliedMomentumCurve=(Points=((InVal=0.0,OutVal=0.5),(InVal=100.0,OutVal=0.5),(InVal=350.0,OutVal=1.0),(InVal=600.0,OutVal=1.0)))
|
|
}
|