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

183 lines
6.6 KiB
Ucode

class ScrnNade extends Nade;
var int ScrakeUnstunDamageThreshold;
var bool bBlewInHands;
simulated function HurtRadius( float DamageAmount, float DamageRadius, class<DamageType> DamageType, float Momentum, vector HitLocation )
{
local actor Victims;
local float damageScale, dist;
local vector dir;
local int NumKilled;
local KFMonster KFMonsterVictim;
local bool bMonster;
local Pawn P;
local KFPawn KFP;
local array<Pawn> CheckedPawns;
local int i;
local bool bAlreadyChecked;
local SRStatsBase Stats;
if ( bHurtEntry )
return;
bHurtEntry = true;
if( Role == ROLE_Authority && Instigator != none && Instigator.PlayerReplicationInfo != none )
Stats = SRStatsBase(Instigator.PlayerReplicationInfo.SteamStatsAndAchievements);
foreach CollidingActors (class 'Actor', Victims, DamageRadius, HitLocation)
{
P = none;
KFMonsterVictim = none;
bMonster = false;
KFP = none;
bAlreadyChecked = false;
// 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;
dir = Victims.Location - HitLocation;
dist = FMax(1,VSize(dir));
dir = dir/dist;
damageScale = 1 - FMax(0,(dist - Victims.CollisionRadius)/DamageRadius);
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 )
continue;
CheckedPawns[CheckedPawns.Length] = P;
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));
bMonster = true; // in case TakeDamage() and further Die() deletes the monster
}
else if( KFP != none ) {
damageScale *= KFP.GetExposureTo(Location + 15 * -Normal(PhysicsVolume.Gravity));
}
if ( damageScale <= 0)
continue;
// Scrake Nader ach
if ( Role == ROLE_Authority && KFMonsterVictim != none && ZombieScrake(KFMonsterVictim) != none ) {
// need to check Scrake's stun before dealing damage, because he can unstun by himself from damage received
ScrakeNader(damageScale * DamageAmount, ZombieScrake(KFMonsterVictim), Stats);
}
}
Victims.TakeDamage(damageScale * DamageAmount,Instigator,Victims.Location - 0.5 * (Victims.CollisionHeight + Victims.CollisionRadius) * dir
,(damageScale * Momentum * dir), DamageType);
if( bMonster && (KFMonsterVictim == none || KFMonsterVictim.Health < 1) ) {
NumKilled++;
}
if (Vehicle(Victims) != None && Vehicle(Victims).Health > 0)
{
Vehicle(Victims).DriverRadiusDamage(DamageAmount, DamageRadius, InstigatorController, DamageType, Momentum, HitLocation);
}
}
}
if( Role == ROLE_Authority )
{
if ( bBlewInHands && NumKilled >= 5 && Stats != none )
class'ScrnBalanceSrv.ScrnAchievements'.static.ProgressAchievementByID(Stats.Rep, 'SuicideBomber', 1);
if ( NumKilled >= 4 )
{
KFGameType(Level.Game).DramaticEvent(0.05);
}
else if ( NumKilled >= 2 )
{
KFGameType(Level.Game).DramaticEvent(0.03);
}
}
bHurtEntry = false;
}
//grant achievement for nading stunned Scrakes
function ScrakeNader(int DamageAmount, ZombieScrake Scrake, SRStatsBase Stats)
{
local name Sequence;
local float Frame, Rate;
if ( Scrake == none || Stats == none || DamageAmount < ScrakeUnstunDamageThreshold )
return;
Scrake.GetAnimParams(Scrake.ExpectingChannel, Sequence, Frame, Rate);
if ( Scrake.bShotAnim && (Sequence == 'KnockDown' || Sequence == 'SawZombieIdle') ) {
//break the stun
Scrake.bShotAnim= false;
Scrake.SetAnimAction(Scrake.WalkAnims[0]);
SawZombieController(Scrake.Controller).GoToState('ZombieHunt');
//progress achievement
class'ScrnBalanceSrv.ScrnAchievements'.static.ProgressAchievementByID(Stats.Rep, 'ScrakeNader', 1);
//mark Scrake as naded in game rules
if ( ScrnHumanPawn(Instigator) != none && ScrnPlayerController(Instigator.Controller) != none )
ScrnPlayerController(Instigator.Controller).Mut.GameRules.ScrakeNaded(Scrake);
}
}
simulated function Explode(vector HitLocation, vector HitNormal)
{
local PlayerController LocalPlayer;
local Projectile P;
local byte i;
bHasExploded = True;
BlowUp(HitLocation);
// null reference fix
if ( ExplodeSounds.length > 0 )
PlaySound(ExplodeSounds[rand(ExplodeSounds.length)],,2.0);
// Shrapnel
for( i=Rand(6); i<10; i++ )
{
P = Spawn(ShrapnelClass,,,,RotRand(True));
if( P!=None )
P.RemoteRole = ROLE_None;
}
if ( EffectIsRelevant(Location,false) )
{
Spawn(Class'KFmod.KFNadeExplosion',,, HitLocation, rotator(vect(0,0,1)));
Spawn(ExplosionDecal,self,,HitLocation, rotator(-HitNormal));
}
// Shake nearby players screens
LocalPlayer = Level.GetLocalPlayerController();
if ( (LocalPlayer != None) && (VSize(Location - LocalPlayer.ViewTarget.Location) < (DamageRadius * 1.5)) )
LocalPlayer.ShakeView(RotMag, RotRate, RotTime, OffsetMag, OffsetRate, OffsetTime);
Destroy();
}
defaultproperties
{
ScrakeUnstunDamageThreshold=50
}