//============================================================================= // NailGunProjectile //============================================================================= // Nail Gun projectile class //============================================================================= // Killing Floor Source // Copyright (C) 2012 Tripwire Interactive LLC // - John "Ramm-Jaeger" Gibson and IJC //============================================================================= class NailGunProjectile extends ShotgunBullet; var String ImpactSoundRefs[6]; var String StaticMeshRef; var byte Bounces; var bool bFinishedPenetrating; var KFMonster MonsterHeadAttached; var ProjectileBodyPart Giblet; replication { reliable if (bNetInitial && Role == ROLE_Authority) Bounces; reliable if (Role == ROLE_Authority) MonsterHeadAttached, bFinishedPenetrating; } static function PreloadAssets() { //default.AmbientSound = sound(DynamicLoadObject(default.AmbientSoundRef, class'Sound', true)); default.ImpactSounds[0] = sound(DynamicLoadObject(default.ImpactSoundRefs[0], class'Sound', true)); default.ImpactSounds[1] = sound(DynamicLoadObject(default.ImpactSoundRefs[1], class'Sound', true)); default.ImpactSounds[2] = sound(DynamicLoadObject(default.ImpactSoundRefs[2], class'Sound', true)); default.ImpactSounds[3] = sound(DynamicLoadObject(default.ImpactSoundRefs[3], class'Sound', true)); default.ImpactSounds[4] = sound(DynamicLoadObject(default.ImpactSoundRefs[4], class'Sound', true)); default.ImpactSounds[5] = sound(DynamicLoadObject(default.ImpactSoundRefs[5], class'Sound', true)); UpdateDefaultStaticMesh(StaticMesh(DynamicLoadObject(default.StaticMeshRef, class'StaticMesh', true))); } static function bool UnloadAssets() { default.ImpactSounds[0] = none; default.ImpactSounds[1] = none; default.ImpactSounds[2] = none; default.ImpactSounds[3] = none; default.ImpactSounds[4] = none; default.ImpactSounds[5] = none; UpdateDefaultStaticMesh(none); return true; } simulated function PostBeginPlay() { super(Projectile).PostBeginPlay(); Velocity = Speed * Vector(Rotation); // starts off slower so combo can be done closer SetTimer(0.4, false); if ( Level.NetMode != NM_DedicatedServer ) { if ( !PhysicsVolume.bWaterVolume ) { Trail = Spawn(class'NailGunTracer',self); Trail.Lifespan = Lifespan; } } } simulated function PostNetReceive() { local Coords boneCoords; super.PostNetReceive(); if( Giblet == none && MonsterHeadAttached != none ) { boneCoords = MonsterHeadAttached.GetBoneCoords( 'head' ); Giblet = Spawn( Class'ProjectileBodyPart',,, boneCoords.Origin, Rotator(boneCoords.XAxis) ); Giblet.SetStaticMesh(MonsterHeadAttached.DetachedHeadClass.default.StaticMesh); Giblet.SetLocation(Location); Giblet.SetPhysics( PHYS_None ); Giblet.SetBase(self); Giblet.Lifespan = Lifespan; } } simulated function ProcessTouch (Actor Other, vector HitLocation) { local vector X; local Vector TempHitLocation, HitNormal; local array HitPoints; local KFPawn HitPawn; local bool bWasDecapitated; local KFMonster KFM; if ( Other == none || Other == Instigator || Other.Base == Instigator || !Other.bBlockHitPointTraces ) return; if( bFinishedPenetrating ) { return; } X = Vector(Rotation); if( ROBulletWhipAttachment(Other) != none ) { if(!Other.Base.bDeleteMe) { Other = Instigator.HitPointTrace(TempHitLocation, HitNormal, HitLocation + (200 * X), HitPoints, HitLocation,, 1); if( Other == none || HitPoints.Length == 0 ) return; HitPawn = KFPawn(Other); if (Role == ROLE_Authority) { if ( HitPawn != none ) { // Hit detection debugging /*log("Bullet hit "$HitPawn.PlayerReplicationInfo.PlayerName); HitPawn.HitStart = HitLocation; HitPawn.HitEnd = HitLocation + (65535 * X);*/ if( !HitPawn.bDeleteMe ) HitPawn.ProcessLocationalDamage(Damage, Instigator, TempHitLocation, MomentumTransfer * Normal(Velocity), MyDamageType,HitPoints); // Hit detection debugging //if( Level.NetMode == NM_Standalone) // HitPawn.DrawBoneLocation(); } } } } else { if (Pawn(Other) != none && Pawn(Other).IsHeadShot(HitLocation, X, 1.0)) { KFM = KFMonster(Other); if( KFM != none ) { bWasDecapitated = KFM.bDecapitated; } Pawn(Other).TakeDamage(Damage * HeadShotDamageMult, Instigator, HitLocation, MomentumTransfer * Normal(Velocity), MyDamageType); if( Role == ROLE_Authority && Bounces > 0 && MonsterHeadAttached == none && KFM != none && !bWasDecapitated && KFM.Health < 0 ) { MonsterHeadAttached = KFM; if( Level.NetMode == NM_ListenServer || Level.NetMode == NM_StandAlone ) { PostNetReceive(); } Bounces=0; } } else { KFM = KFMonster(Other.Base); if( KFM != none ) { bWasDecapitated = KFM.bDecapitated; } Other.TakeDamage(Damage, Instigator, HitLocation, MomentumTransfer * Normal(Velocity), MyDamageType); if( Role == ROLE_Authority && Bounces > 0 && MonsterHeadAttached == none && KFM != none && !bWasDecapitated && KFM.Health < 0 && KFM.IsHeadShot(HitLocation, X, 1.0)) { MonsterHeadAttached = KFM; if( Level.NetMode == NM_ListenServer || Level.NetMode == NM_StandAlone ) { PostNetReceive(); } Bounces=0; } } } if ( KFPlayerReplicationInfo(Instigator.PlayerReplicationInfo) != none && KFPlayerReplicationInfo(Instigator.PlayerReplicationInfo).ClientVeteranSkill != none ) { PenDamageReduction = KFPlayerReplicationInfo(Instigator.PlayerReplicationInfo).ClientVeteranSkill.static.GetShotgunPenetrationDamageMulti(KFPlayerReplicationInfo(Instigator.PlayerReplicationInfo),default.PenDamageReduction); } else { PenDamageReduction = default.PenDamageReduction; } Damage *= PenDamageReduction; // Keep going, but lose effectiveness each time. // if we've struck through more than the max number of foes, destroy. if ( Damage / default.Damage <= PenDamageReduction / MaxPenetrations ) { bFinishedPenetrating = true; Velocity = PhysicsVolume.Gravity; SetPhysics(PHYS_Falling); Bounces=0; } speed = VSize(Velocity); if( Speed < (default.Speed * 0.25) ) { bFinishedPenetrating = true; Velocity = PhysicsVolume.Gravity; SetPhysics(PHYS_Falling); Bounces=0; } } simulated function Tick( float DeltaTime ) { if ( Level.NetMode != NM_DedicatedServer && Physics != PHYS_None ) { SetRotation(Rotator(Normal(Velocity))); } } simulated function HitWall( vector HitNormal, actor Wall ) { if ( !Wall.bStatic && !Wall.bWorldGeometry && ((Mover(Wall) == None) || Mover(Wall).bDamageTriggered) ) { if ( Level.NetMode != NM_Client ) { if ( Instigator == None || Instigator.Controller == None ) Wall.SetDelayedDamageInstigatorController( InstigatorController ); Wall.TakeDamage( Damage, instigator, Location, MomentumTransfer * Normal(Velocity), MyDamageType); } Destroy(); return; } SetRotation(rotator(Normal(Velocity))); SetPhysics(PHYS_Falling); if (Bounces > 0) { if ( !Level.bDropDetail && (FRand() < 0.4) ) Playsound(ImpactSounds[Rand(6)]); Velocity = 0.65 * (Velocity - 2.0*HitNormal*(Velocity dot HitNormal)); Bounces = Bounces - 1; if ( !Level.bDropDetail && (Level.NetMode != NM_DedicatedServer)) { Spawn(class'ROEffects.ROBulletHitMetalEffect',,,Location, rotator(hitnormal)); } return; } else { if (ImpactEffect != None && (Level.NetMode != NM_DedicatedServer)) { Spawn(ImpactEffect,,, Location, rotator(-HitNormal)); } SetPhysics(PHYS_None); LifeSpan = 5.0; } bBounce = false; if (Trail != None) { Trail.mRegen=False; Trail.SetPhysics(PHYS_None); //Trail.mRegenRange[0] = 0.0;//trail.mRegenRange[0] * 0.6; //Trail.mRegenRange[1] = 0.0;//trail.mRegenRange[1] * 0.6; } } simulated function PhysicsVolumeChange( PhysicsVolume Volume ) { if (Volume.bWaterVolume) { if ( Trail != None ) Trail.mRegen=False; Velocity *= 0.65; } } simulated function Landed( Vector HitNormal ) { SetPhysics(PHYS_None); LifeSpan = 5.0; } simulated function Destroyed() { super.Destroyed(); if( Giblet != none ) { Giblet.Destroy(); Giblet = none; } if( MonsterHeadAttached != none ) { MonsterHeadAttached = none; } } defaultproperties { DamageAtten=5.000000 MaxPenetrations=2 PenDamageReduction=0.750000 HeadShotDamageMult=1.500000 Speed=3500.000000 MaxSpeed=4000.000000 bSwitchToZeroCollision=True Damage=35.000000 DamageRadius=0.000000 MomentumTransfer=50000.000000 MyDamageType=Class'KFMod.DamTypeNailGun' ExplosionDecal=Class'KFMod.NailGunDecal' DrawType=DT_StaticMesh StaticMeshRef="EffectsSM.Weapons.Vlad_9000_Nail" CullDistance=3000.000000 LifeSpan=10.000000 DrawScale=1.0 Style=STY_Alpha ImpactEffect=class'ROBulletHitEffect' ImpactSoundRefs[0]="ProjectileSounds.Bullets.Impact_Metal" ImpactSoundRefs[1]="ProjectileSounds.Bullets.Impact_Metal" ImpactSoundRefs[2]="ProjectileSounds.Bullets.Impact_Metal" ImpactSoundRefs[3]="ProjectileSounds.Bullets.Impact_Metal" ImpactSoundRefs[4]="ProjectileSounds.Bullets.Impact_Metal" ImpactSoundRefs[5]="ProjectileSounds.Bullets.Impact_Metal" bBounce=true Bounces=2 NetPriority=2.500000 bNetTemporary=false bNetNotify=true }