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

386 lines
11 KiB
Ucode

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