rott/kf_sources/KFMod/Classes/NailGunProjectile.uc
2026-07-14 20:27:09 +07:00

355 lines
10 KiB
Ucode

//=============================================================================
// 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<int> 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
}