//----------------------------------------------------------- // //----------------------------------------------------------- class HealingProjectile extends ROBallisticProjectile; // camera shakes // var() vector ShakeRotMag; // how far to rot view var() vector ShakeRotRate; // how fast to rot view var() float ShakeRotTime; // how much time to rot the instigator's view var() vector ShakeOffsetMag; // max view offset vertically var() vector ShakeOffsetRate; // how fast to offset view vertically var() float ShakeOffsetTime; // how much time to offset view var() vector RotMag; // how far to rot view var() vector RotRate; // how fast to rot view var() float RotTime; // how much time to rot the instigator's view var() vector OffsetMag; // max view offset vertically var() vector OffsetRate; // how fast to offset view vertically var() float OffsetTime; // how much time to offset view var PanzerfaustTrail SmokeTrail; var vector Dir; var bool bRing,bHitWater,bWaterStart; var() sound ExplosionSound; // The sound of the rocket exploding var () int HealBoostAmount; var bool bHitHealTarget; // Hit a target we can heal. var bool bHasExploded; var vector HealLocation; var rotator HealRotation; // 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; var string StaticMeshRef; var string ExplosionSoundRef; var string AmbientSoundRef; var string DisintegrateSoundRef; replication { reliable if(Role == ROLE_Authority) HealLocation, HealRotation; } static function PreloadAssets() { default.ExplosionSound = sound(DynamicLoadObject(default.ExplosionSoundRef, class'Sound', true)); default.AmbientSound = sound(DynamicLoadObject(default.AmbientSoundRef, class'Sound', true)); UpdateDefaultStaticMesh(StaticMesh(DynamicLoadObject(default.StaticMeshRef, class'StaticMesh', true))); } static function bool UnloadAssets() { default.ExplosionSound = none; default.AmbientSound = none; UpdateDefaultStaticMesh(none); return true; } simulated function PostNetReceive() { if( bHidden && !bHitHealTarget ) { if( HealLocation != vect(0,0,0) ) { log("PostNetReceive calling HitHealTarget for location of "$HealLocation); HitHealTarget(HealLocation,vector(HealRotation)); } else { log("PostNetReceive calling HitHealTarget for self location of "$HealLocation); HitHealTarget(Location,-vector(Rotation)); } } } simulated function Tick( float DeltaTime ) { SetRotation(Rotator(Normal(Velocity))); 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"); bTrueBallistics = false; } } // Do a healing effect/sound instead of standard "explode" simulated function HitHealTarget(vector HitLocation, vector HitNormal) { bHitHealTarget = true; bHidden = true; SetPhysics(PHYS_None); HealLocation = HitLocation; HealRotation = rotator(HitNormal); if( Role == ROLE_Authority ) { SetTimer(0.1, false); NetUpdateTime = Level.TimeSeconds - 1; } PlaySound(ExplosionSound,,2.0); if ( EffectIsRelevant(Location,false) ) { Spawn(Class'KFMod.HealingFX',,, HitLocation, rotator(HitNormal)); } } function Timer() { Destroy(); } function ShakeView() { local Controller C; local PlayerController PC; local float Dist, Scale; for ( C=Level.ControllerList; C!=None; C=C.NextController ) { PC = PlayerController(C); if ( PC != None && PC.ViewTarget != None ) { Dist = VSize(Location - PC.ViewTarget.Location); if ( Dist < DamageRadius * 2.0) { if (Dist < DamageRadius) Scale = 1.0; else Scale = (DamageRadius*2.0 - Dist) / (DamageRadius); C.ShakeView(ShakeRotMag*Scale, ShakeRotRate, ShakeRotTime, ShakeOffsetMag*Scale, ShakeOffsetRate, ShakeOffsetTime); } } } } simulated function HitWall(vector HitNormal, actor Wall) { super(Projectile).HitWall(HitNormal,Wall); } simulated function Explode(vector HitLocation, vector HitNormal) { bHasExploded = True; // Don't do the regular effect if we healed someone if( bHitHealTarget ) { return; } SetPhysics(PHYS_None); if(Level.NetMode != NM_DedicatedServer) { Spawn(class'ROBulletHitEffect',,, Location, rotator(-HitNormal)); } BlowUp(HitLocation); Destroy(); } simulated function PostBeginPlay() { OrigLoc = Location; Dir = vector(Rotation); Velocity = speed * Dir; if (PhysicsVolume.bWaterVolume) { bHitWater = True; Velocity=0.6*Velocity; } Super.PostBeginPlay(); } function TakeDamage( int Damage, Pawn InstigatedBy, Vector Hitlocation, Vector Momentum, class damageType, optional int HitIndex) { Explode(HitLocation, vect(0,0,0)); } simulated function Destroyed() { if ( SmokeTrail != None ) { SmokeTrail.HandleOwnerDestroyed(); } if( !bHasExploded && !bHidden ) Explode(Location,vect(0,0,1)); if( bHidden && !bHitHealTarget ) { if( HealLocation != vect(0,0,0) ) { HitHealTarget(HealLocation,vector(HealRotation)); } else { HitHealTarget(Location,-vector(Rotation)); } } Super.Destroyed(); } /* HurtRadius() Hurt locally authoritative actors within the radius. */ simulated function HurtRadius( float DamageAmount, float DamageRadius, class DamageType, float Momentum, vector HitLocation ) { return; } simulated function ProcessTouch(Actor Other, Vector HitLocation) { local KFPlayerReplicationInfo PRI; local int MedicReward; local KFHumanPawn Healed; local float HealSum; // for modifying based on perks if ( Other == none || Other == Instigator || Other.Base == Instigator ) return; if( Role == ROLE_Authority ) { Healed = KFHumanPawn(Other); if( Healed != none ) { HitHealTarget(HitLocation, -vector(Rotation)); } if( Instigator != none && Healed != none && Healed.Health > 0 && Healed.Health < Healed.HealthMax && Healed.bCanBeHealed ) { MedicReward = HealBoostAmount; PRI = KFPlayerReplicationInfo(Instigator.PlayerReplicationInfo); if ( PRI != none && PRI.ClientVeteranSkill != none ) { MedicReward *= PRI.ClientVeteranSkill.Static.GetHealPotency(PRI); } HealSum = MedicReward; if ( (Healed.Health + Healed.healthToGive + MedicReward) > Healed.HealthMax ) { MedicReward = Healed.HealthMax - (Healed.Health + Healed.healthToGive); if ( MedicReward < 0 ) { MedicReward = 0; } } Healed.GiveHealth(HealSum, Healed.HealthMax); if ( PRI != None ) { if ( MedicReward > 0 && KFSteamStatsAndAchievements(PRI.SteamStatsAndAchievements) != none ) { AddDamagedHealStats( MedicReward ); } // Give the medic reward money as a percentage of how much of the person's health they healed MedicReward = int((FMin(float(MedicReward),Healed.HealthMax)/Healed.HealthMax) * 60); // Increased to 80 in Balance Round 6, reduced to 60 in Round 7 PRI.ReceiveRewardForHealing( MedicReward, Healed ); if ( KFHumanPawn(Instigator) != none ) { KFHumanPawn(Instigator).AlphaAmount = 255; } if( KFMedicGun(Instigator.Weapon) != none ) { KFMedicGun(Instigator.Weapon).ClientSuccessfulHeal(Healed.GetPlayerName()); } } } } else if( KFHumanPawn(Other) != none ) { bHidden = true; SetPhysics(PHYS_None); return; } Explode(HitLocation,-vector(Rotation)); } function AddDamagedHealStats( int MedicReward ) { local KFSteamStatsAndAchievements KFSteamStats; if ( Instigator == none || Instigator.PlayerReplicationInfo == none ) { return; } KFSteamStats = KFSteamStatsAndAchievements( Instigator.PlayerReplicationInfo.SteamStatsAndAchievements ); if ( KFSteamStats != none ) { KFSteamStats.AddDamageHealed(MedicReward); } } defaultproperties { HealBoostAmount=20 ShakeRotMag=(X=600.000000,Y=600.000000,Z=600.000000) ShakeRotRate=(X=12500.000000,Y=12500.000000,Z=12500.000000) ShakeRotTime=6.000000 ShakeOffsetMag=(X=5.000000,Y=10.000000,Z=5.000000) ShakeOffsetRate=(X=300.000000,Y=300.000000,Z=300.000000) ShakeOffsetTime=3.500000 RotMag=(X=700.000000,Y=700.000000,Z=700.000000) RotRate=(X=12500.000000,Y=12500.000000,Z=12500.000000) RotTime=6.000000 OffsetMag=(X=5.000000,Y=10.000000,Z=7.000000) OffsetRate=(X=300.000000,Y=300.000000,Z=300.000000) OffsetTime=3.500000 Speed=10000.000000 MaxSpeed=12500.000000 Damage=650 DamageRadius=200.000000 MomentumTransfer=125000.000000 //MyDamageType=Class'KFMod.DamTypeLAW' ExplosionDecal=Class'KFMod.ShotgunDecal' LightHue=25 LightSaturation=100 LightBrightness=250.000000 LightRadius=10.000000 DrawType=DT_StaticMesh StaticMeshRef="KF_pickups2_Trip.MP7_Dart" LifeSpan=10.000000 DrawScale=1.000000 bUnlit=False ForceRadius=300.000000 ForceScale=10.000000 ExplosionSoundRef="KF_MP7Snd.MP7_DartImpact" bTrueBallistics=true AmbientSoundRef="KF_MP7Snd.MP7_DartFlyLoop" SoundVolume=128 SoundRadius=250 AmbientVolumeScale=2.0 TransientSoundVolume=2.0 TransientSoundRadius=500 bFullVolume=false bBlockHitPointTraces=false StraightFlightTime=0.1 bUpdateSimulatedPosition=true bNetNotify=true bNetTemporary=false }