167 lines
12 KiB
Ucode
167 lines
12 KiB
Ucode
//=============================================================================
|
|
// MedicNade
|
|
//=============================================================================
|
|
// Medic grenade that heals friends and hurts enemies
|
|
//=============================================================================
|
|
// Killing Floor Source
|
|
// Copyright (C) 2012 Tripwire Interactive
|
|
// Author - John "Ramm-Jaeger" Gibson
|
|
//=============================================================================
|
|
class NiceMedicNade extends Nade;
|
|
#exec OBJ LOAD FILE=KF_GrenadeSnd.uax
|
|
#exec OBJ LOAD FILE=Inf_WeaponsTwo.uax
|
|
#exec OBJ LOAD FILE=KF_LAWSnd.uax
|
|
var() int HealBoostAmount;// How much we heal a player by default with the medic nade
|
|
var int TotalHeals; // The total number of times this nade has healed (or hurt enemies)
|
|
var() int MaxHeals; // The total number of times this nade will heal (or hurt enemies) until its done healing
|
|
var float NextHealTime; // The next time that this nade will heal friendlies or hurt enemies
|
|
var() float HealInterval; // How often to do healing
|
|
var() sound ExplosionSound; // The sound of the rocket exploding
|
|
var localized string SuccessfulHealMessage;
|
|
var int MaxNumberOfPlayers;
|
|
var bool bNeedToPlayEffects; // Whether or not effects have been played yet
|
|
replication
|
|
{
|
|
reliable if (Role==ROLE_Authority)
|
|
bNeedToPlayEffects;
|
|
}
|
|
simulated function PostNetReceive()
|
|
{
|
|
super.PostNetReceive();
|
|
if( !bHasExploded && bNeedToPlayEffects )
|
|
{
|
|
bNeedToPlayEffects = false;
|
|
Explode(Location, vect(0,0,1));
|
|
}
|
|
}
|
|
simulated function Explode(vector HitLocation, vector HitNormal)
|
|
{
|
|
bHasExploded = True;
|
|
BlowUp(HitLocation);
|
|
PlaySound(ExplosionSound,,TransientSoundVolume);
|
|
if( Role == ROLE_Authority )
|
|
{
|
|
bNeedToPlayEffects = true;
|
|
AmbientSound=Sound'Inf_WeaponsTwo.smoke_loop';
|
|
}
|
|
if ( EffectIsRelevant(Location,false) )
|
|
{
|
|
Spawn(Class'NicePack.NiceNadeHealing',,, HitLocation, rotator(vect(0,0,1)));
|
|
}
|
|
}
|
|
function Timer()
|
|
{
|
|
if( !bHidden )
|
|
{
|
|
if( !bHasExploded )
|
|
{
|
|
Explode(Location, vect(0,0,1));
|
|
}
|
|
}
|
|
else if( bDisintegrated )
|
|
{
|
|
AmbientSound=none;
|
|
Destroy();
|
|
}
|
|
}
|
|
simulated function BlowUp(vector HitLocation)
|
|
{
|
|
HealOrHurt(Damage,DamageRadius, MyDamageType, MomentumTransfer, HitLocation);
|
|
if ( Role == ROLE_Authority )
|
|
MakeNoise(1.0);
|
|
}
|
|
function HealOrHurt(float DamageAmount, float DamageRadius, class<DamageType> DamageType, float Momentum, vector HitLocation)
|
|
{
|
|
local actor Victims;
|
|
local float damageScale;
|
|
local vector dir;
|
|
local int NumKilled;
|
|
local KFMonster KFMonsterVictim;
|
|
local Pawn P;
|
|
local KFPawn KFP;
|
|
local array<Pawn> CheckedPawns;
|
|
local int i;
|
|
local bool bAlreadyChecked;
|
|
// Healing
|
|
local KFPlayerReplicationInfo PRI;
|
|
local int MedicReward;
|
|
local float HealSum; // for modifying based on perks
|
|
local int PlayersHealed;
|
|
if ( bHurtEntry )
|
|
return;
|
|
NextHealTime = Level.TimeSeconds + HealInterval;
|
|
bHurtEntry = true;
|
|
if( Fear != none )
|
|
{
|
|
Fear.StartleBots();
|
|
}
|
|
foreach CollidingActors (class 'Actor', Victims, DamageRadius, HitLocation)
|
|
{
|
|
// don't let blast damage affect fluid - VisibleCollisingActors doesn't really work for them - jag
|
|
if( (Victims != self) && (Hurtwall != Victims) && (Victims.Role == ROLE_Authority) && !Victims.IsA('FluidSurfaceInfo')
|
|
&& ExtendedZCollision(Victims)==none )
|
|
{
|
|
if( (Instigator==none || Instigator.Health<=0) && KFPawn(Victims)!=none )
|
|
Continue;
|
|
|
|
damageScale = 1.0;
|
|
|
|
if ( Instigator == none || Instigator.Controller == none )
|
|
{
|
|
Victims.SetDelayedDamageInstigatorController( InstigatorController );
|
|
}
|
|
|
|
P = Pawn(Victims);
|
|
|
|
if( P != none )
|
|
{
|
|
for (i = 0; i < CheckedPawns.Length; i++)
|
|
{
|
|
if (CheckedPawns[i] == P)
|
|
{
|
|
bAlreadyChecked = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if( bAlreadyChecked )
|
|
{
|
|
bAlreadyChecked = false;
|
|
P = none;
|
|
continue;
|
|
}
|
|
|
|
KFMonsterVictim = KFMonster(Victims);
|
|
|
|
if( KFMonsterVictim != none && KFMonsterVictim.Health <= 0 )
|
|
{
|
|
KFMonsterVictim = none;
|
|
}
|
|
|
|
KFP = KFPawn(Victims);
|
|
|
|
if( KFMonsterVictim != none )
|
|
{
|
|
damageScale *= KFMonsterVictim.GetExposureTo(Location + 15 * -Normal(PhysicsVolume.Gravity));
|
|
}
|
|
else if( KFP != none )
|
|
{
|
|
damageScale *= KFP.GetExposureTo(Location + 15 * -Normal(PhysicsVolume.Gravity));
|
|
}
|
|
|
|
CheckedPawns[CheckedPawns.Length] = P;
|
|
|
|
if ( damageScale <= 0)
|
|
{
|
|
P = none;
|
|
continue;
|
|
}
|
|
else
|
|
{
|
|
//Victims = P;
|
|
P = none;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
continue;
|
|
}
|
|
|
|
if( KFP == none )
|
|
{
|
|
//log(Level.TimeSeconds@"Hurting "$Victims$" for "$(damageScale * DamageAmount)$" damage");
|
|
|
|
if( Pawn(Victims) != none && Pawn(Victims).Health > 0 )
|
|
{
|
|
Victims.TakeDamage(damageScale * DamageAmount,Instigator,Victims.Location - 0.5 * (Victims.CollisionHeight + Victims.CollisionRadius)
|
|
* dir,(damageScale * Momentum * dir),DamageType);
|
|
|
|
if( Role == ROLE_Authority && KFMonsterVictim != none && KFMonsterVictim.Health <= 0 )
|
|
{
|
|
NumKilled++;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if(Instigator != none && KFP.Health > 0)
|
|
{
|
|
if ( KFP.bCanBeHealed )
|
|
{
|
|
PlayersHealed += 1;
|
|
MedicReward = HealBoostAmount;
|
|
|
|
PRI = KFPlayerReplicationInfo(Instigator.PlayerReplicationInfo);
|
|
|
|
if ( PRI != none && PRI.ClientVeteranSkill != none )
|
|
{
|
|
MedicReward *= PRI.ClientVeteranSkill.Static.GetHealPotency(PRI);
|
|
}
|
|
|
|
HealSum = MedicReward;
|
|
|
|
if ( (KFP.Health + KFP.healthToGive + MedicReward) > KFP.HealthMax )
|
|
{
|
|
MedicReward = KFP.HealthMax - (KFP.Health + KFP.healthToGive);
|
|
if ( MedicReward < 0 )
|
|
{
|
|
MedicReward = 0;
|
|
}
|
|
}
|
|
|
|
//log(Level.TimeSeconds@"Healing "$KFP$" for "$HealSum$" base healamount "$HealBoostAmount$" health");
|
|
KFP.GiveHealth(HealSum, KFP.HealthMax);
|
|
KFP.ShieldStrength += HealSum * 0.25;
|
|
if(KFP.ShieldStrength > KFP.ShieldStrengthMax)
|
|
KFP.ShieldStrength = KFP.ShieldStrengthMax;
|
|
|
|
if ( PRI != none )
|
|
{
|
|
if ( MedicReward > 0 && KFSteamStatsAndAchievements(PRI.SteamStatsAndAchievements) != none )
|
|
{
|
|
KFSteamStatsAndAchievements(PRI.SteamStatsAndAchievements).AddDamageHealed(MedicReward, false, true);
|
|
}
|
|
|
|
// Give the medic reward money as a percentage of how much of the person's health they healed
|
|
MedicReward = int((FMin(float(MedicReward),KFP.HealthMax)/KFP.HealthMax) * 60);
|
|
|
|
PRI.ReceiveRewardForHealing( MedicReward, KFP );
|
|
|
|
if ( KFHumanPawn(Instigator) != none )
|
|
{
|
|
KFHumanPawn(Instigator).AlphaAmount = 255;
|
|
}
|
|
|
|
if( PlayerController(Instigator.Controller) != none )
|
|
{
|
|
PlayerController(Instigator.Controller).ClientMessage(SuccessfulHealMessage$KFP.GetPlayerName(), 'CriticalEvent');
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
KFP = none;
|
|
}
|
|
|
|
if (PlayersHealed >= MaxNumberOfPlayers)
|
|
{
|
|
if (PRI != none)
|
|
{
|
|
KFSteamStatsAndAchievements(PRI.SteamStatsAndAchievements).HealedTeamWithMedicGrenade();
|
|
}
|
|
}
|
|
}
|
|
bHurtEntry = false;
|
|
}
|
|
// Shoot nades in mid-air
|
|
// Alex
|
|
function TakeDamage( int Damage, Pawn InstigatedBy, Vector Hitlocation, Vector Momentum, class<DamageType> damageType, optional int HitIndex)
|
|
{
|
|
if ( Monster(instigatedBy) != none || instigatedBy == Instigator )
|
|
{
|
|
if( damageType == class'SirenScreamDamage')
|
|
{
|
|
Disintegrate(HitLocation, vect(0,0,1));
|
|
}
|
|
}
|
|
}
|
|
// Overridden to tweak the handling of the impact sound
|
|
simulated function HitWall( vector HitNormal, actor Wall )
|
|
{
|
|
local Vector VNorm;
|
|
local PlayerController PC;
|
|
if ( (Pawn(Wall) != none) || (GameObjective(Wall) != none) )
|
|
{
|
|
Explode(Location, HitNormal);
|
|
return;
|
|
}
|
|
if (!bTimerSet)
|
|
{
|
|
SetTimer(ExplodeTimer, false);
|
|
bTimerSet = true;
|
|
}
|
|
// Reflect off Wall w/damping
|
|
VNorm = (Velocity dot HitNormal) * HitNormal;
|
|
Velocity = -VNorm * DampenFactor + (Velocity - VNorm) * DampenFactorParallel;
|
|
RandSpin(100000);
|
|
DesiredRotation.Roll = 0;
|
|
RotationRate.Roll = 0;
|
|
Speed = VSize(Velocity);
|
|
if ( Speed < 20 )
|
|
{
|
|
bBounce = False;
|
|
PrePivot.Z = -1.5;
|
|
SetPhysics(PHYS_none);
|
|
Timer();
|
|
SetTimer(0.0,False);
|
|
DesiredRotation = Rotation;
|
|
DesiredRotation.Roll = 0;
|
|
DesiredRotation.Pitch = 0;
|
|
SetRotation(DesiredRotation);
|
|
|
|
if( Fear == none )
|
|
{
|
|
//(jc) Changed to use MedicNade-specific grenade that's overridden to not make the ringmaster fear it
|
|
Fear = Spawn(class'AvoidMarker_MedicNade');
|
|
Fear.SetCollisionSize(DamageRadius,DamageRadius);
|
|
Fear.StartleBots();
|
|
}
|
|
|
|
if ( Trail != none )
|
|
Trail.mRegen = false; // stop the emitter from regenerating
|
|
}
|
|
else
|
|
{
|
|
if ( (Level.NetMode != NM_DedicatedServer) && (Speed > 50) )
|
|
PlaySound(ImpactSound, SLOT_Misc );
|
|
else
|
|
{
|
|
bFixedRotationDir = false;
|
|
bRotateToDesired = true;
|
|
DesiredRotation.Pitch = 0;
|
|
RotationRate.Pitch = 50000;
|
|
}
|
|
if ( !Level.bDropDetail && (Level.DetailMode != DM_Low) && (Level.TimeSeconds - LastSparkTime > 0.5) && EffectIsRelevant(Location,false) )
|
|
{
|
|
PC = Level.GetLocalPlayerController();
|
|
if ( (PC.ViewTarget != none) && VSize(PC.ViewTarget.Location - Location) < 6000 )
|
|
Spawn(HitEffectClass,,, Location, Rotator(HitNormal));
|
|
LastSparkTime = Level.TimeSeconds;
|
|
}
|
|
}
|
|
}
|
|
function Tick( float DeltaTime )
|
|
{
|
|
if( Role < ROLE_Authority )
|
|
{
|
|
return;
|
|
}
|
|
// if( class'ROEngine.ROLevelInfo'.static.RODebugMode() )
|
|
// {
|
|
// DrawDebugSphere( Location, DamageRadius, 12, 255, 0, 0);
|
|
// }
|
|
if( TotalHeals < MaxHeals && NextHealTime > 0 && NextHealTime < Level.TimeSeconds )
|
|
{
|
|
TotalHeals += 1;
|
|
|
|
HealOrHurt(Damage,DamageRadius, MyDamageType, MomentumTransfer, Location);
|
|
|
|
if( TotalHeals >= MaxHeals )
|
|
{
|
|
AmbientSound=none;
|
|
}
|
|
}
|
|
}
|
|
defaultproperties
|
|
{
|
|
HealBoostAmount=10
|
|
MaxHeals=8
|
|
HealInterval=1.000000
|
|
ExplosionSound=SoundGroup'KF_GrenadeSnd.NadeBase.MedicNade_Explode'
|
|
SuccessfulHealMessage="You healed "
|
|
MaxNumberOfPlayers=6
|
|
Damage=50.000000
|
|
DamageRadius=175.000000
|
|
MyDamageType=Class'KFMod.DamTypeMedicNade'
|
|
StaticMesh=StaticMesh'KF_pickups5_Trip.nades.MedicNade_Pickup'
|
|
DrawScale=1.000000
|
|
SoundVolume=150
|
|
SoundRadius=100.000000
|
|
TransientSoundVolume=2.000000
|
|
TransientSoundRadius=200.000000
|
|
}
|