class CrossbuzzsawBlade extends ROBallisticProjectile; #exec OBJ LOAD FILE=KF_InventorySnd.uax var xEmitter Trail; var Emitter Corona; var() class 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) 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 }