// Siren that is capable of shattering frozen zeds // @author PooSH, 2015 class ZombieSirenCryo extends ZombieSiren abstract; var transient float LastScreamTime; // prevent scream canceling var transient float NextFrozenCheckTime; // next time we'll need to check for frozen zeds var FreezeRules FreezeRules; // this is set in FreezeRules.PostBeginPlay() var class ShatterProjClass; function TakeDamage(int Damage, Pawn InstigatedBy, Vector HitLocation, Vector Momentum, class DamType, optional int HitIndex) { if (InstigatedBy == none || class(DamType) == none) Super(Monster).TakeDamage(Damage, instigatedBy, hitLocation, momentum, DamType); // skip NONE-reference error -- PooSH else Super(KFMonster).TakeDamage(Damage, instigatedBy, hitLocation, momentum, DamType); } // should Siren scream to shatter zeds between her and A function bool ShouldDoShatterScream(Actor A) { local int i, FrozenCount, CanShatterCount; local float ScreamRadiusSqr; if ( FreezeRules == none || Level.TimeSeconds < NextFrozenCheckTime ) return false; // already checked NextFrozenCheckTime = Level.TimeSeconds + 2.0; if ( FreezeRules.Frozen.Length == 0 ) return false; NextFrozenCheckTime = Level.TimeSeconds + 5; ScreamRadiusSqr = ScreamRadius * ScreamRadius; for ( i = 0; i < FreezeRules.Frozen.Length; ++i ) { if ( FreezeRules.Frozen[i].M != none && FreezeRules.Frozen[i].bFrozen ) { ++FrozenCount; if ( VSizeSquared(FreezeRules.Frozen[i].M.Location - Location) < ScreamRadiusSqr) { ++CanShatterCount; } } } if ( CanShatterCount > 0 && CanShatterCount > 5.0 * frand() ) { NextFrozenCheckTime = Level.TimeSeconds + 5; // give cool down before next shattering return true; } if ( CanShatterCount > 0 ) NextFrozenCheckTime = Level.TimeSeconds + 0.5; else if ( FrozenCount > 0 ) NextFrozenCheckTime = Level.TimeSeconds + 1; return false; } function RangedAttack(Actor A) { local int LastFireTime; local float Dist; if ( bShotAnim ) return; Dist = VSize(A.Location - Location); if ( Physics == PHYS_Swimming ) { SetAnimAction('Claw'); bShotAnim = true; LastFireTime = Level.TimeSeconds; } else if ( Dist < MeleeRange + CollisionRadius + A.CollisionRadius && (bDecapitated || bZapped || Level.TimeSeconds - LastScreamTime < 7) ) { bShotAnim = true; LastFireTime = Level.TimeSeconds; SetAnimAction('Claw'); //PlaySound(sound'Claw2s', SLOT_Interact); KFTODO: Replace this Controller.bPreparingMove = true; Acceleration = vect(0,0,0); } else if( Dist <= ScreamRadius && !bDecapitated && !bZapped || (Level.TimeSeconds > NextFrozenCheckTime && ShouldDoShatterScream(A)) ) { LastScreamTime = Level.TimeSeconds; bShotAnim=true; SetAnimAction('Siren_Scream'); // Only stop moving if we are close if( Dist < ScreamRadius * 0.25 ) { Controller.bPreparingMove = true; Acceleration = vect(0,0,0); } else { Acceleration = AccelRate * Normal(A.Location - Location); } } } // overridden to shatter zeds // fixed instigator in calling TakeDamage() simulated function HurtRadius( float DamageAmount, float DamageRadius, class DamageType, float Momentum, vector HitLocation ) { local actor Victims; local KFMonster M; local float damageScale, dist; local vector dir; local float UsedDamageAmount; if( bHurtEntry ) return; bHurtEntry = true; foreach VisibleCollidingActors( class 'Actor', Victims, DamageRadius, HitLocation ) { // don't let blast damage affect fluid - VisibleCollisingActors doesn't really work for them - jag // Or Karma actors in this case. Self inflicted Death due to flying chairs is uncool for a zombie of your stature. if( (Victims != self) && !Victims.IsA('FluidSurfaceInfo') && !Victims.IsA('ExtendedZCollision') ) { Momentum = ScreamForce; // bugfix, when pull wasn't applied always -- PooSH dir = Victims.Location - HitLocation; dist = FMax(1,VSize(dir)); dir = dir/dist; M = KFMonster(Victims); if ( M != none ) { UsedDamageAmount = 1; // just a little bit to pass it in NetDamage() damageScale = 1; if ( FreezeRules != none && FreezeRules.IsFrozen(M) ) { ShatterFrozenZed(M, HitLocation, dist); continue; } } else { damageScale = 1 - FMax(0,(dist - Victims.CollisionRadius)/DamageRadius); if (!Victims.IsA('KFHumanPawn')) // If it aint human, don't pull the vortex crap on it. Momentum = 0; if (Victims.IsA('KFGlassMover')) UsedDamageAmount = 100000; // Siren always shatters glass else UsedDamageAmount = DamageAmount; } Victims.TakeDamage(damageScale * UsedDamageAmount, self, Victims.Location - 0.5 * (Victims.CollisionHeight + Victims.CollisionRadius) * dir,(damageScale * Momentum * dir),DamageType); if ( Vehicle(Victims) != None && Vehicle(Victims).Health > 0) Vehicle(Victims).DriverRadiusDamage(UsedDamageAmount, DamageRadius, Controller, DamageType, Momentum, HitLocation); } } bHurtEntry = false; } // turns frozen monster into deadly ice shards function ShatterFrozenZed(KFMonster M, Vector HitLocation, float Distance) { local vector ProjLoc; local float ColH, ColR; local float DmgMult; local int Count; local Projectile proj; local float DistancePct; // save zed's data, because he'll be dead after shattering HitLocation = M.Location; // calculate shard offset from the center of zed, not where it got hit ColH = M.CollisionHeight; ColR = M.CollisionRadius; Count = M.Health / 50; if ( !FreezeRules.ShatterZed(M, -1, Controller, ScreamDamageType) ) return; // unable to shatter zed DistancePct = Distance/ScreamRadius; if ( M != none ) M.SetCollision(false); DmgMult = DifficultyDamageModifer(); if ( Count > 20 ) { DmgMult += float(Count-20) / 20.0; Count = 20; } ProjLoc = HitLocation; // first projectile always spawn in the center to fly a straight line while ( Count > 0 ) { --Count; proj = Spawn(ShatterProjClass, self, '', ProjLoc, rotator(ProjLoc - Location)); if ( proj != none ) { proj.Instigator = self; proj.Damage *= DmgMult; // if ( DistancePct > 0.5 ) { // proj.Damage *= 1.5 - DistancePct; // } } // pickup random location inside collision cylinder for the next projectile ProjLoc = HitLocation; ProjLoc.X += ColR * (0.70 - 1.4 * frand()); ProjLoc.Y += ColR * (0.70 - 1.4 * frand()); ProjLoc.Z += ColH * (0.50 - 1.0 * frand()); } } defaultproperties { ShatterProjClass=Class'ScrnHTec.SirenDart' ScreamDamage=6 MenuName="Cryo Siren" }