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

448 lines
12 KiB
Ucode

class CrossbuzzsawBlade extends ROBallisticProjectile;
#exec OBJ LOAD FILE=KF_InventorySnd.uax
var xEmitter Trail;
var Emitter Corona;
var() class<DamageType> DamageTypeHeadShot;
// Sounds
var sound BladeHitWall; // Sound of the blade hitting a wall
var sound BladeHitArmor; // Sound of the blade hitting an armored enemy
var sound BladeHitFlesh; // Sound of the blade hitting flesh
var() float HeadShotDamageMult;
var Actor ImpactActor;
var Pawn IgnoreImpactPawn;
// Dynamic loading
var() string AmbientSoundRef;
var string StaticMeshRef;
var string BladeHitWallRef;
var string BladeHitArmorRef;
var string BladeHitFleshRef;
// Bouncing vars
var byte Bounces; // Number of times this blade has bounced
var int MaxBounces; // Maximum number of times this blade should be able to bounce
// Physics
var() float StraightFlightTime; // How long the projectile and flies straight
var float TotalFlightTime; // How long the rocket has been in flight
var bool bOutOfPropellant; // Projectile is out of propellant
// Physics debugging
var vector OuttaPropLocation;
replication
{
reliable if ( Role==ROLE_Authority && bNetInitial )
ImpactActor;
reliable if (bNetInitial && Role == ROLE_Authority)
Bounces;
}
static function PreloadAssets()
{
default.AmbientSound = sound(DynamicLoadObject(default.AmbientSoundRef, class'Sound', true));
default.BladeHitWall= sound(DynamicLoadObject(default.BladeHitWallRef, class'Sound', true));
default.BladeHitArmor = sound(DynamicLoadObject(default.BladeHitArmorRef, class'Sound', true));
default.BladeHitFlesh = sound(DynamicLoadObject(default.BladeHitFleshRef, class'Sound', true));
UpdateDefaultStaticMesh(StaticMesh(DynamicLoadObject(default.StaticMeshRef, class'StaticMesh', true)));
}
static function bool UnloadAssets()
{
default.AmbientSound = none;
default.BladeHitWall = none;
default.BladeHitArmor = none;
default.BladeHitFlesh = none;
UpdateDefaultStaticMesh(none);
return true;
}
simulated function PostBeginPlay()
{
local vector Dir;
BCInverse = 1 / BallisticCoefficient;
OrigLoc = Location;
Dir = vector(Rotation);
Velocity = Speed * Dir;
if (PhysicsVolume.bWaterVolume)
{
Velocity=0.6*Velocity;
}
if( Level.NetMode != NM_DedicatedServer )
{
if ( !PhysicsVolume.bWaterVolume )
{
Trail = Spawn(class'CrossbuzzsawTracer',self);
Trail.Lifespan = Lifespan;
Corona = Spawn(class'CrossbuzzsawCorona',self);
Corona.Lifespan = Lifespan;
}
}
Bounces = MaxBounces;
super(Projectile).PostBeginPlay();
}
simulated function Tick( float DeltaTime )
{
SetRotation(Rotator(Normal(Velocity)));
if( Physics == PHYS_Projectile && VSizeSquared(Velocity) < ((Speed * Speed) * 0.1) )
{
SetPhysics(PHYS_Falling);
}
if( !bOutOfPropellant )
{
if ( TotalFlightTime <= StraightFlightTime )
{
TotalFlightTime += DeltaTime;
}
else
{
OuttaPropLocation = Location;
bOutOfPropellant = true;
}
}
if( bOutOfPropellant && !bTrueBallistics )
{
//log(" Projectile flew "$(VSize(OrigLoc - OuttaPropLocation)/50.0)$" meters before running out of juice");
//SetPhysics(PHYS_Falling);
bTrueBallistics = true;
}
}
simulated function PostNetReceive()
{
if( ImpactActor!=None && Base != ImpactActor )
{
GoToState('OnWall');
}
}
simulated state OnWall
{
Ignores HitWall;
function ProcessTouch (Actor Other, vector HitLocation)
{
local Inventory inv;
if( Pawn(Other)!=None && Pawn(Other).Inventory!=None )
{
for( inv=Pawn(Other).Inventory; inv!=None; inv=inv.Inventory )
{
if( Crossbuzzsaw(Inv)!=None && Weapon(inv).AmmoAmount(0)<Weapon(inv).MaxAmmo(0) )
{
KFweapon(Inv).AddAmmo(1,0) ;
PlaySound(Sound'KF_InventorySnd.Ammo_GenericPickup', SLOT_Pain,2*TransientSoundVolume,,400);
if( PlayerController(Instigator.Controller)!=none )
{
PlayerController(Instigator.Controller).ReceiveLocalizedMessage(class'KFmod.ProjectilePickupMessage',1);
}
Destroy();
}
}
}
}
simulated function Tick( float Delta )
{
if( Base==None )
{
if( Level.NetMode==NM_Client )
bHidden = True;
else Destroy();
}
}
simulated function BeginState()
{
bCollideWorld = False;
//if( Level.NetMode!=NM_DedicatedServer )
AmbientSound = None;
if( Trail!=None )
Trail.mRegen = False;
if( Corona != none )
{
Corona.Kill();
}
SetCollisionSize(75,50);
UV2Texture=FadeColor'PatchTex.Common.PickupOverlay';
}
}
simulated function Explode(vector HitLocation, vector HitNormal);
simulated function ProcessTouch (Actor Other, vector HitLocation)
{
local vector X,End,HL,HN;
local Vector TempHitLocation, HitNormal;
local array<int> HitPoints;
local KFPawn HitPawn;
local bool bHitWhipAttachment;
if ( Other == none || Other == Instigator || Other.Base == Instigator || !Other.bBlockHitPointTraces || Other==IgnoreImpactPawn ||
(IgnoreImpactPawn != none && Other.Base == IgnoreImpactPawn) )
return;
X = Vector(Rotation);
if( ROBulletWhipAttachment(Other) != none )
{
bHitWhipAttachment=true;
if(!Other.Base.bDeleteMe)
{
Other = Instigator.HitPointTrace(TempHitLocation, HitNormal, HitLocation + (65535 * 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 * X, MyDamageType,HitPoints);
Damage/=1.25;
Velocity*=0.85;
IgnoreImpactPawn = HitPawn;
if( Level.NetMode!=NM_Client )
PlayhitNoise(Pawn(Other)!=none && Pawn(Other).ShieldStrength>0);
// Hit detection debugging
/*if( Level.NetMode == NM_Standalone)
HitPawn.DrawBoneLocation();*/
return;
}
}
}
else
{
return;
}
}
if( Level.NetMode!=NM_Client )
{
PlayhitNoise(Pawn(Other)!=none && Pawn(Other).ShieldStrength>0);
}
if( Physics==PHYS_Projectile && Pawn(Other)!=None && Vehicle(Other)==None )
{
IgnoreImpactPawn = Pawn(Other);
if( IgnoreImpactPawn.IsHeadShot(HitLocation, X, 1.0) )
Other.TakeDamage(Damage * HeadShotDamageMult, Instigator, HitLocation, MomentumTransfer * X, DamageTypeHeadShot);
else Other.TakeDamage(Damage, Instigator, HitLocation, MomentumTransfer * X, MyDamageType);
Damage/=1.25;
Velocity*=0.85;
Return;
}
else if( ExtendedZCollision(Other)!=None && Pawn(Other.Owner)!=None )
{
if( Other.Owner==IgnoreImpactPawn )
Return;
IgnoreImpactPawn = Pawn(Other.Owner);
if ( IgnoreImpactPawn.IsHeadShot(HitLocation, X, 1.0))
Other.TakeDamage(Damage * HeadShotDamageMult, Instigator, HitLocation, MomentumTransfer * X, DamageTypeHeadShot);
else Other.TakeDamage(Damage, Instigator, HitLocation, MomentumTransfer * X, MyDamageType);
Damage/=1.25;
Velocity*=0.85;
Return;
}
if( Level.NetMode!=NM_DedicatedServer && SkeletalMesh(Other.Mesh)!=None && Other.DrawType==DT_Mesh && Pawn(Other)!=None )
{ // Attach victim to the wall behind if it dies.
End = Other.Location+X*600;
if( Other.Trace(HL,HN,End,Other.Location,False)!=None )
Spawn(Class'BodyAttacher',Other,,HitLocation).AttachEndPoint = HL-HN;
}
//Stick(Other,HitLocation);
if( Level.NetMode!=NM_Client )
{
if (Pawn(Other) != none && Pawn(Other).IsHeadShot(HitLocation, X, 1.0))
Pawn(Other).TakeDamage(Damage * HeadShotDamageMult, Instigator, HitLocation, MomentumTransfer * X, DamageTypeHeadShot);
else Other.TakeDamage(Damage, Instigator, HitLocation, MomentumTransfer * X, MyDamageType);
}
}
function PlayHitNoise( bool bArmored )
{
if( bArmored )
{
PlaySound(BladeHitArmor,,2.0); // implies hit a target with shield/armor
}
else
{
PlaySound(BladeHitFlesh,,2.0);
}
}
simulated function HitWall( vector HitNormal, actor Wall )
{
if ( (Mover(Wall) != None) && Mover(Wall).bDamageTriggered )
{
if ( Level.NetMode != NM_Client )
{
Wall.TakeDamage( Damage, instigator, Location, MomentumTransfer * Normal(Velocity), MyDamageType);
}
Destroy();
return;
}
PlaySound(BladeHitWall,SLOT_None,0.6,,300,0.9+frand()*0.2);
if ( EffectIsRelevant(Location,false) )
{
Spawn(class'CrossbuzzsawImpact',,,, rotator(hitnormal));
}
if ( Physics == PHYS_Projectile && Bounces > 0)
{
Velocity = 0.95 * (Velocity - 2.0*HitNormal*(Velocity dot HitNormal)); // slow the projectile after bounce
Bounces = Bounces - 1;
return;
}
else
{
bBounce = false;
Stick(Wall,Location);
}
SetRotation(rotator(Velocity));
if( Instigator!=None && Level.NetMode!=NM_Client )
MakeNoise(0.3);
}
simulated function Landed(vector HitNormal)
{
HitWall(HitNormal, None);
}
simulated function Stick(actor HitActor, vector HitLocation)
{
local name NearestBone;
local float dist;
SetPhysics(PHYS_None);
if (Pawn(HitActor) != none)
{
NearestBone = GetClosestBone(HitLocation, HitLocation, dist , 'CHR_Spine2' , 15 );
HitActor.AttachToBone(self,NearestBone);
}
else
{
SetBase(HitActor);
}
ImpactActor = HitActor;
if (Base==None)
Destroy();
else GoToState('OnWall');
}
simulated function PhysicsVolumeChange( PhysicsVolume Volume )
{
if( Volume.bWaterVolume && !PhysicsVolume.bWaterVolume )
{
if ( Trail != None )
Trail.mRegen=False;
if( Corona != none )
{
Corona.Kill();
}
Velocity*=0.65;
}
}
simulated function Destroyed()
{
if (Trail !=None)
Trail.mRegen = False;
if( Corona != none )
{
Corona.Kill();
}
Super.Destroyed();
}
defaultproperties
{
DamageTypeHeadShot=Class'KFMod.DamTypeCrossbuzzsawHeadShot'
BladeHitWallRef="KF_IJC_HalloweenSnd.KF_SawbladeBow_Projectile_Hit"
BladeHitArmorRef="KF_IJC_HalloweenSnd.KF_SawbladeBow_Projectile_Hit"
BladeHitFleshRef="KF_AxeSnd.Axe_HitFlesh"
HeadShotDamageMult=2.000000
Speed=1500.000000
MaxSpeed=2000.000000
Damage=500.000000
MomentumTransfer=50000
MyDamageType=Class'KFMod.DamTypeCrossbuzzsaw'
ExplosionDecal=Class'KFMod.ShotgunDecal'
CullDistance=7500.000000
AmbientSoundRef="KF_IJC_HalloweenSnd.KF_SawbladeBow_Projectile_Loop"
LifeSpan=180
DrawType=DT_StaticMesh
StaticMeshRef="EffectsSM.Weapons.cheetah_blade"
DrawScale=3.000000
AmbientGlow=0
Style=STY_Alpha
bUnlit=False
bNetTemporary=False
CollisionRadius=0
CollisionHeight=0
TransientSoundVolume=0.5
AmbientVolumeScale=2.0
SoundVolume=175
SoundRadius=250
bFullVolume=false
bTrueBallistics=False
MaxBounces=5
Bounces=1
bBounce=true
Physics = PHYS_Projectile
StraightFlightTime=0.65
bUpdateSimulatedPosition=true
}