First commit

This commit is contained in:
Anton Tarasenko 2020-02-16 19:53:59 +07:00
commit 5b48414900
263 changed files with 24830 additions and 0 deletions

View file

@ -0,0 +1,51 @@
class NiceBossHPNeedle extends Decoration
NotPlaceable;
#exec obj load file="NewPatchSM.usx"
simulated function DroppedNow()
{
SetCollision(True);
SetPhysics(PHYS_Falling);
bFixedRotationDir = True;
RotationRate = RotRand(True);
}
simulated function HitWall( vector HitNormal, actor HitWall )
{
local rotator R;
if( VSize(Velocity)<40 )
{ SetPhysics(PHYS_none); R.Roll = Rand(65536); R.Yaw = Rand(65536); SetRotation(R); Return;
}
Velocity = MirrorVectorByNormal(Velocity,HitNormal)*0.75;
if( HitWall!=none && HitWall.Physics!=PHYS_none ) Velocity+=HitWall.Velocity;
}
simulated function Landed( vector HitNormal )
{
HitWall(HitNormal,none);
}
function TakeDamage( int NDamage, Pawn instigatedBy, Vector hitlocation, Vector momentum, class<DamageType> damageType, optional int HitIndex)
{
if( Physics==PHYS_none )
{ SetPhysics(PHYS_Falling); bFixedRotationDir = True; RotationRate = RotRand(True); Velocity = vect(0,0,0);
}
Velocity+=momentum/10;
}
simulated function Destroyed();
function Bump( actor Other );
singular function PhysicsVolumeChange( PhysicsVolume NewVolume );
// Overriden so it doesn't damage the patriarch when he drops a needle!
singular function BaseChange()
{
if( Velocity.Z < -500 ) TakeDamage( (1-Velocity.Z/30),Instigator,Location,vect(0,0,0) , class'Crushed');
if( base == none )
{ if ( !bInterpolating && bPushable && (Physics == PHYS_none) ) SetPhysics(PHYS_Falling);
}
else if( Pawn(Base) != none )
{ //Base.TakeDamage( (1-Velocity.Z/400)* mass/Base.Mass,Instigator,Location,0.5 * Velocity , class'Crushed'); Velocity.Z = 100; if (FRand() < 0.5) Velocity.X += 70; else Velocity.Y += 70; SetPhysics(PHYS_Falling);
}
else if( Decoration(Base)!=none && Velocity.Z<-500 )
{ Base.TakeDamage((1 - Mass/Base.Mass * Velocity.Z/30), Instigator, Location, 0.2 * Velocity, class'Crushed'); Velocity.Z = 100; if (FRand() < 0.5) Velocity.X += 70; else Velocity.Y += 70; SetPhysics(PHYS_Falling);
}
else instigator = none;
}
defaultproperties
{ DrawType=DT_StaticMesh StaticMesh=StaticMesh'NewPatchSM.BossSyringe' bStatic=False RemoteRole=ROLE_None LifeSpan=300.000000 CollisionRadius=4.000000 CollisionHeight=4.000000 bCollideWorld=True bProjTarget=True bBounce=True
}

View file

@ -0,0 +1,14 @@
class NiceBossLAWProj extends LAWProj;
//-----------------------------------------------------------------------------
// PostBeginPlay
//-----------------------------------------------------------------------------
simulated function PostBeginPlay()
{
// Difficulty Scaling
if(Level.Game != none){ if(Level.Game.GameDifficulty >= 5.0) // Hell on Earth & Suicidal damage = default.damage * 1.3; else damage = default.damage * 1.0;
}
super.PostBeginPlay();
}
defaultproperties
{ ArmDistSquared=0.000000 Damage=200.000000 MyDamageType=Class'KFMod.DamTypeFrag'
}

View file

@ -0,0 +1,5 @@
class NiceDamTypePoundCrushed extends NiceZedMeleeDamageType
abstract;
defaultproperties
{ DeathString="%o was pounded by %k." FemaleSuicide="%o was pounded." MaleSuicide="%o was pounded." bArmorStops=False bLocationalHit=False bThrowRagdoll=True bExtraMomentumZ=True GibPerterbation=1.000000 KDamageImpulse=7000.000000 KDeathVel=350.000000 KDeathUpKick=100.000000 HumanObliterationThreshhold=500
}

View file

@ -0,0 +1,5 @@
class NiceEnviromentalDamage extends NiceWeaponDamageType
abstract;
defaultproperties
{
}

View file

@ -0,0 +1,6 @@
class NiceEnviromentalDamageFire extends NiceEnviromentalDamage
abstract;
defaultproperties
{
heatPart=1.0
}

View file

@ -0,0 +1,127 @@
class NiceHuskFireProjectile extends LAWProj;
var Emitter FlameTrail;
var xEmitter Trail;
var class<DamageType> MyAdditionalDamageType;
var float additionalDamagePart;
//-----------------------------------------------------------------------------
// PostBeginPlay
//-----------------------------------------------------------------------------
simulated function PostBeginPlay()
{
if ( Level.NetMode != NM_DedicatedServer )
{ if ( !PhysicsVolume.bWaterVolume ) { FlameTrail = Spawn(class'FlameThrowerFlameB',self); Trail = Spawn(class'FlameThrowerFlame',self); }
}
// Difficulty Scaling
if (Level.Game != none)
{ if(Level.Game.GameDifficulty >= 5.0) // Hell on Earth & Suicidal damage = default.damage * 1.3; else damage = default.damage * 1.0;
}
OrigLoc = Location;
if( !bDud )
{ Dir = vector(Rotation); Velocity = speed * Dir;
}
super(ROBallisticProjectile).PostBeginPlay();
}
simulated function Explode(vector HitLocation, vector HitNormal)
{
local Controller C;
local PlayerController LocalPlayer;
local float ShakeScale;
bHasExploded = True;
// Don't explode if this is a dud
if( bDud )
{ Velocity = vect(0,0,0); LifeSpan=1.0; SetPhysics(PHYS_Falling);
}
PlaySound(ExplosionSound,,2.0);
if ( EffectIsRelevant(Location,false) )
{ Spawn(class'KFMod.FlameImpact',,,HitLocation + HitNormal*20,rotator(HitNormal)); Spawn(ExplosionDecal,self,,HitLocation, rotator(-HitNormal));
}
BlowUp(HitLocation);
Destroy();
// Shake nearby players screens
LocalPlayer = Level.GetLocalPlayerController();
if ( LocalPlayer != none )
{ ShakeScale = GetShakeScale(Location, LocalPlayer.ViewTarget.Location); if( ShakeScale > 0 ) { LocalPlayer.ShakeView(RotMag * ShakeScale, RotRate, RotTime, OffsetMag * ShakeScale, OffsetRate, OffsetTime); }
}
for ( C=Level.ControllerList; C!=none; C=C.NextController )
{ if ( PlayerController(C) != none && C != LocalPlayer ) { ShakeScale = GetShakeScale(Location, PlayerController(C).ViewTarget.Location); if( ShakeScale > 0 ) { C.ShakeView(RotMag * ShakeScale, RotRate, RotTime, OffsetMag * ShakeScale, OffsetRate, OffsetTime); } }
}
}
// Get the shake amount for when this projectile explodes
simulated function float GetShakeScale(vector ViewLocation, vector EventLocation)
{
local float Dist;
local float scale;
Dist = VSize(ViewLocation - EventLocation);
if (Dist < DamageRadius * 2.0 )
{ scale = (DamageRadius*2.0 - Dist) / (DamageRadius*2.0);
}
return scale;
}
/* HurtRadius()
Hurt locally authoritative actors within the radius.
Overriden so it doesn't attemt to damage the bullet whiz cylinder - TODO: maybe implement the same thing in the superclass - Ramm
*/
simulated function HurtRadius( float DamageAmount, float DamageRadius, class<DamageType> DamageType, float Momentum, vector HitLocation )
{
local actor Victims;
local float damageScale, dist;
local vector dirs;
local int NumKilled;
local KFMonster KFMonsterVictim;
local Pawn P;
local KFPawn KFP;
local array<Pawn> CheckedPawns;
local int i;
local bool bAlreadyChecked;
if ( bHurtEntry ) return;
bHurtEntry = true;
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) && (Victims != Instigator) &&(Hurtwall != Victims) && (Victims.Role == ROLE_Authority) && !Victims.IsA('FluidSurfaceInfo') && ExtendedZCollision(Victims)==none && KFBulletWhipAttachment(Victims)==none ) { dirs = Victims.Location - HitLocation; dist = FMax(1,VSize(dirs)); dirs = dirs/dist; damageScale = 1 - FMax(0,(dist - Victims.CollisionRadius)/DamageRadius); if ( Instigator == none || Instigator.Controller == none ) Victims.SetDelayedDamageInstigatorController( InstigatorController ); if ( Victims == LastTouched ) LastTouched = none;
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(HitLocation); } else if( KFP != none ) { damageScale *= KFP.GetExposureTo(HitLocation); }
CheckedPawns[CheckedPawns.Length] = P;
if ( damageScale <= 0) { P = none; continue; } else { P = none; } }
Victims.TakeDamage ( damageScale * DamageAmount * (1.0 - additionalDamagePart), Instigator, Victims.Location - 0.5 * (Victims.CollisionHeight + Victims.CollisionRadius) * dirs, (damageScale * Momentum * dirs), DamageType ); Victims.TakeDamage ( damageScale * DamageAmount * additionalDamagePart, Instigator, Victims.Location - 0.5 * (Victims.CollisionHeight + Victims.CollisionRadius) * dirs, Vect(0,0,0), MyAdditionalDamageType ); if (Vehicle(Victims) != none && Vehicle(Victims).Health > 0) Vehicle(Victims).DriverRadiusDamage(DamageAmount, DamageRadius, InstigatorController, DamageType, Momentum, HitLocation);
if( Role == ROLE_Authority && KFMonsterVictim != none && KFMonsterVictim.Health <= 0 ) { NumKilled++; } }
}
if ( (LastTouched != none) && (LastTouched != self) && (LastTouched != Instigator) && (LastTouched.Role == ROLE_Authority) && !LastTouched.IsA('FluidSurfaceInfo') )
{ Victims = LastTouched; LastTouched = none; dirs = Victims.Location - HitLocation; dist = FMax(1,VSize(dirs)); dirs = dirs/dist; damageScale = FMax(Victims.CollisionRadius/(Victims.CollisionRadius + Victims.CollisionHeight),1 - FMax(0,(dist - Victims.CollisionRadius)/DamageRadius)); if ( Instigator == none || Instigator.Controller == none ) Victims.SetDelayedDamageInstigatorController(InstigatorController);
Victims.TakeDamage ( damageScale * DamageAmount, Instigator, Victims.Location - 0.5 * (Victims.CollisionHeight + Victims.CollisionRadius) * dirs, (damageScale * Momentum * dirs), DamageType ); if (Vehicle(Victims) != none && Vehicle(Victims).Health > 0) Vehicle(Victims).DriverRadiusDamage(DamageAmount, DamageRadius, InstigatorController, DamageType, Momentum, HitLocation);
}
if( Role == ROLE_Authority )
{ if( NumKilled >= 4 ) { KFGameType(Level.Game).DramaticEvent(0.05); } else if( NumKilled >= 2 ) { KFGameType(Level.Game).DramaticEvent(0.03); }
}
bHurtEntry = false;
}
//==============
// Touching
// Overridden to not touch the bulletwhip attachment
simulated singular function Touch(Actor Other){
if(Other == none || KFBulletWhipAttachment(Other) != none || Role < ROLE_Authority) return;
super.Touch(Other);
}
// Don't hit Zed extra collision cylinders
// Do hit :3
simulated function ProcessTouch(Actor Other, Vector HitLocation)
{
/*if(ExtendedZCollision(Other) != none){ return;*/
super.ProcessTouch(Other, HitLocation);
}
simulated function Destroyed()
{
if ( Trail != none )
{ Trail.mRegen=False; Trail.SetPhysics(PHYS_none); Trail.GotoState('');
}
if ( FlameTrail != none )
{ FlameTrail.Kill(); FlameTrail.SetPhysics(PHYS_none);
}
Super.Destroyed();
}
defaultproperties
{ MyAdditionalDamageType=Class'KFMod.DamTypeLAW' ExplosionSound=SoundGroup'KF_EnemiesFinalSnd.Husk.Husk_FireImpact' ArmDistSquared=0.000000 Speed=1800.000000 MaxSpeed=2200.000000 Damage=25.000000 DamageRadius=150.000000 MyDamageType=Class'NicePack.NiceDamTypeFire' ExplosionDecal=Class'KFMod.FlameThrowerBurnMark' LightType=LT_Steady LightHue=45 LightSaturation=169 LightBrightness=90.000000 LightRadius=16.000000 LightCone=16 StaticMesh=StaticMesh'EffectsSM.Weapons.Ger_Tracer' bDynamicLight=True bNetTemporary=False AmbientSound=Sound'KF_BaseHusk.Fire.husk_fireball_loop' DrawScale=2.000000 AmbientGlow=254 bUnlit=True
}

View file

@ -0,0 +1,4 @@
class NiceSeveredArmSick extends SeveredArm;
defaultproperties
{ StaticMesh=StaticMesh'NicePackSM.MonsterSick.Arm'
}

View file

@ -0,0 +1,13 @@
//=============================================================================
// SeveredHeadClot
//=============================================================================
// Detached head gib class for the clot
//=============================================================================
// Killing Floor Source
// Copyright (C) 2009 Tripwire Interactive LLC
// - John "Ramm-Jaeger" Gibson
//=============================================================================
class NiceSeveredHeadSick extends SeveredHead;
defaultproperties
{ StaticMesh=StaticMesh'NicePackSM.MonsterSick.head'
}

View file

@ -0,0 +1,4 @@
class NiceSeveredLegSick extends SeveredLeg;
defaultproperties
{ StaticMesh=StaticMesh'NicePackSM.MonsterSick.Leg'
}

View file

@ -0,0 +1,86 @@
// The Nice, nasty barf we'll be using for the Bloat's ranged attack.
class NiceSickVomit extends BioGlob;
simulated function PostBeginPlay()
{
SetOwner(none);
if (Role == ROLE_Authority)
{ Velocity = Vector(Rotation) * Speed; Velocity.Z += TossZ;
}
if (Role == ROLE_Authority) Rand3 = Rand(3);
if ( (Level.NetMode != NM_DedicatedServer) && ((Level.DetailMode == DM_Low) || Level.bDropDetail) )
{ bDynamicLight = false; LightType = LT_none;
}
// Difficulty Scaling
if (Level.Game != none)
{ BaseDamage = Max((DifficultyDamageModifer() * BaseDamage),1); Damage = Max((DifficultyDamageModifer() * Damage),1);
}
}
// Scales the damage this Zed deals by the difficulty level
function float DifficultyDamageModifer()
{
local float AdjustedDamageModifier;
if(Level.Game.GameDifficulty >= 5.0) // Hell on Earth & Suicidal damage = default.damage * 2.5;
else damage = default.damage * 1.5;
return AdjustedDamageModifier;
}
state OnGround
{
simulated function BeginState()
{ SetTimer(RestTime, false); BlowUp(Location);
}
simulated function Timer()
{ if (bDrip) { bDrip = false; SetCollisionSize(default.CollisionHeight, default.CollisionRadius); Velocity = PhysicsVolume.Gravity * 0.2; SetPhysics(PHYS_Falling); bCollideWorld = true; bCheckedsurface = false; bProjTarget = false; GotoState('Flying'); } else BlowUp(Location);
}
simulated function ProcessTouch(Actor Other, Vector HitLocation)
{ if ( Other != none ) BlowUp(Location);
}
function TakeDamage( int Damage, Pawn InstigatedBy, Vector Hitlocation, Vector Momentum, class<DamageType> damageType, optional int HitIndex)
{ if (DamageType.default.bDetonatesGoop) { bDrip = false; SetTimer(0.1, false); }
}
simulated function AnimEnd(int Channel)
{ local float DotProduct;
if (!bCheckedSurface) { DotProduct = SurfaceNormal dot Vect(0,0,-1); if (DotProduct > 0.7) { bDrip = true; SetTimer(DripTime, false); if (bOnMover) BlowUp(Location); } else if (DotProduct > -0.5) { if (bOnMover) BlowUp(Location); } bCheckedSurface = true; }
}
simulated function MergeWithGlob(int AdditionalGoopLevel)
{ local int NewGoopLevel, ExtraSplash; NewGoopLevel = AdditionalGoopLevel + GoopLevel; if (NewGoopLevel > MaxGoopLevel) { Rand3 = (Rand3 + 1) % 3; ExtraSplash = Rand3; if (Role == ROLE_Authority) SplashGlobs(NewGoopLevel - MaxGoopLevel + ExtraSplash); NewGoopLevel = MaxGoopLevel - ExtraSplash; } SetGoopLevel(NewGoopLevel); SetCollisionSize(GoopVolume*10.0, GoopVolume*10.0); PlaySound(ImpactSound, SLOT_Misc); bCheckedSurface = false; SetTimer(RestTime, false);
}
}
singular function SplashGlobs(int NumGloblings)
{
local int g;
local NiceSickVomit NewGlob;
local Vector VNorm;
for (g=0; g<NumGloblings; g++)
{ NewGlob = Spawn(Class, self,, Location+GoopVolume*(CollisionHeight+4.0)*SurfaceNormal); if (NewGlob != none) { NewGlob.Velocity = (GloblingSpeed + FRand()*150.0) * (SurfaceNormal + VRand()*0.8); if (Physics == PHYS_Falling) { VNorm = (Velocity dot SurfaceNormal) * SurfaceNormal; NewGlob.Velocity += (-VNorm + (Velocity - VNorm)) * 0.1; } NewGlob.InstigatorController = InstigatorController; } //else log("unable to spawn globling");
}
}
simulated function Destroyed()
{
if ( !bNoFX && EffectIsRelevant(Location,false) )
{ //Spawn(class'xEffects.GoopSmoke'); Spawn(class'KFmod.VomGroundSplash');
}
if ( Fear != none ) Fear.Destroy();
if (Trail != none) Trail.Destroy();
//Super.Destroyed();
}
auto state Flying
{
simulated function Landed( Vector HitNormal )
{ local Rotator NewRot; local int CoreGoopLevel;
if ( Level.NetMode != NM_DedicatedServer ) { PlaySound(ImpactSound, SLOT_Misc); // explosion effects }
SurfaceNormal = HitNormal;
// spawn globlings CoreGoopLevel = Rand3 + MaxGoopLevel - 3; if (GoopLevel > CoreGoopLevel) { if (Role == ROLE_Authority) SplashGlobs(GoopLevel - CoreGoopLevel); SetGoopLevel(CoreGoopLevel); } spawn(class'KFMod.VomitDecal',,,, rotator(-HitNormal));
bCollideWorld = false; SetCollisionSize(GoopVolume*10.0, GoopVolume*10.0); bProjTarget = true;
NewRot = Rotator(HitNormal); NewRot.Roll += 32768; SetRotation(NewRot); SetPhysics(PHYS_none); bCheckedsurface = false; Fear = Spawn(class'AvoidMarker'); GotoState('OnGround');
}
simulated function HitWall( Vector HitNormal, Actor Wall )
{ Landed(HitNormal); if ( !Wall.bStatic && !Wall.bWorldGeometry ) { bOnMover = true; SetBase(Wall); if (Base == none) BlowUp(Location); }
}
simulated function ProcessTouch(Actor Other, Vector HitLocation)
{ if( ExtendedZCollision(Other)!=none ) Return; if (Other != Instigator && (Other.IsA('Pawn') || Other.IsA('DestroyableObjective') || Other.bProjTarget)) HurtRadius(Damage,DamageRadius, MyDamageType, MomentumTransfer, HitLocation ); else if ( Other != Instigator && Other.bBlockActors ) HitWall( Normal(HitLocation-Location), Other );
}
}
defaultproperties
{ BaseDamage=4 TouchDetonationDelay=0.000000 Speed=600.000000 Damage=5.000000 MomentumTransfer=3500.000000 MyDamageType=Class'KFMod.DamTypeVomit' ImpactSound=SoundGroup'KF_EnemiesFinalSnd.Bloat.Bloat_AcidSplash' DrawType=DT_StaticMesh StaticMesh=StaticMesh'kf_gore_trip_sm.puke.puke_chunk' bDynamicLight=False LifeSpan=8.000000 Skins(0)=Combiner'kf_fx_trip_t.Gore.intestines_cmb' bUseCollisionStaticMesh=False bBlockHitPointTraces=False
}

View file

@ -0,0 +1,7 @@
class NiceSickZombieController extends NiceMonsterController;
// Custom Zombie Thinkerating
// By : Alex
// Randomly plays a different moan sound for the Zombie each time it is called. Gruesome!
defaultproperties
{
}

View file

@ -0,0 +1,58 @@
// used for Tesla Husk self-destruct explosion on decapitation
class NiceTeslaEMPNade extends NiceNade;
var() class<Emitter> ExplosionEffect;
function Timer(){
if(bHidden) Destroy();
else if(Instigator != none && Instigator.Health > 0) Explode(Location, vect(0,0,1));
else Disintegrate(Location, vect(0,0,1));
}
function TakeDamage( int Damage, Pawn InstigatedBy, Vector Hitlocation, Vector Momentum, class<DamageType> damageType, optional int HitIndex){}
simulated function Explode(vector HitLocation, vector HitNormal)
{
local PlayerController LocalPlayer;
bHasExploded = True;
BlowUp(HitLocation);
if(ExplodeSounds.length > 0) PlaySound(ExplodeSounds[rand(ExplodeSounds.length)],, 2.0);
if(EffectIsRelevant(Location, false)){ Spawn(ExplosionEffect,,, 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);
if(Instigator != none){ // blow up the instigator Instigator.TakeDamage(1000000, Instigator, Instigator.Location, vect(0,0,1), MyDamageType);
}
Destroy();
}
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 KFMonster KFMonsterVictim;
local Pawn P;
local KFPawn KFP;
local array<Pawn> CheckedPawns;
local int i;
local bool bAlreadyChecked;
if ( bHurtEntry ) return;
bHurtEntry = true;
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; 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 ) { bAlreadyChecked = false; P = none; continue; }
KFMonsterVictim = KFMonster(Victims);
if( KFMonsterVictim != none && KFMonsterVictim.Health <= 0 ) { KFMonsterVictim = none; }
KFP = KFPawn(Victims);
if( KFMonsterVictim != none ) { // 10x more damage zeds damageScale *= 10.0 * KFMonsterVictim.GetExposureTo(Location + 15 * -Normal(PhysicsVolume.Gravity)); if ( ZombieFleshpound(KFMonsterVictim) != none ) damageScale *= 2.0; // compensate 50% dmg.res. } 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; } }
Victims.TakeDamage(damageScale * DamageAmount,Instigator,Victims.Location - 0.5 * (Victims.CollisionHeight + Victims.CollisionRadius) * dir,(damageScale * Momentum * dir),DamageType);
if (Vehicle(Victims) != none && Vehicle(Victims).Health > 0) { Vehicle(Victims).DriverRadiusDamage(DamageAmount, DamageRadius, InstigatorController, DamageType, Momentum, HitLocation); } }
}
bHurtEntry = false;
}
defaultproperties
{ ExplosionEffect=Class'KFMod.ZEDMKIISecondaryProjectileExplosion' ShrapnelClass=None ExplodeSounds(0)=Sound'KF_FY_ZEDV2SND.Fire.WEP_ZEDV2_Secondary_Fire_S' ExplodeSounds(1)=Sound'KF_FY_ZEDV2SND.Fire.WEP_ZEDV2_Secondary_Fire_S' ExplodeSounds(2)=Sound'KF_FY_ZEDV2SND.Fire.WEP_ZEDV2_Secondary_Fire_S' Speed=0.000000 Damage=50.000000 DamageRadius=400.000000 MyDamageType=Class'ScrnZedPack.DamTypeEMP' DrawType=DT_None bCollideActors=False bBlockZeroExtentTraces=False bBlockNonZeroExtentTraces=False
}

View file

@ -0,0 +1,8 @@
class NiceZedDamageType extends NiceWeaponDamageType
abstract;
var material HUDDamageTex;
var material HUDUberDamageTex;
var float HUDTime;
defaultproperties
{ HUDDamageTex=Texture'KillingFloorHUD.BluntSplashNormal' HUDUberDamageTex=Shader'KillingFloorHUD.BluntShaderuber' HUDTime=0.900000
}

View file

@ -0,0 +1,4 @@
class NiceZedMeleeDamageType extends NiceZedDamageType;
defaultproperties
{ DeathString="%o was eaten by %k." FemaleSuicide="%o ate herself." MaleSuicide="%o ate himself." PawnDamageEmitter=Class'ROEffects.ROBloodPuff' LowGoreDamageEmitter=Class'ROEffects.ROBloodPuffNoGore' LowDetailEmitter=Class'ROEffects.ROBloodPuffSmall'
}

View file

@ -0,0 +1,4 @@
class NiceZedSlashingDamageType extends NiceZedDamageType;
defaultproperties
{ HUDDamageTex=FinalBlend'KillingFloorHUD.SlashSplashNormalFB' HUDUberDamageTex=FinalBlend'KillingFloorHUD.SlashSplashUberFB' DeathString="%o was eaten by %k." FemaleSuicide="%o ate herself." MaleSuicide="%o ate himself." PawnDamageEmitter=Class'ROEffects.ROBloodPuff' LowGoreDamageEmitter=Class'ROEffects.ROBloodPuffNoGore' LowDetailEmitter=Class'ROEffects.ROBloodPuffSmall'
}

View file

@ -0,0 +1,271 @@
// Zombie Monster for KF Invasion gametype
class NiceZombieBloat extends NiceZombieBloatBase;
#exec OBJ LOAD FILE=KF_EnemiesFinalSnd.uax
//----------------------------------------------------------------------------
// NOTE: All Variables are declared in the base class to eliminate hitching
//----------------------------------------------------------------------------
var class<FleshHitEmitter> BileExplosion;
var class<FleshHitEmitter> BileExplosionHeadless;
function bool FlipOver()
{
return true;
}
// don't interrupt the bloat while he is puking
simulated function bool HitCanInterruptAction()
{
if( bShotAnim )
{ return false;
}
return true;
}
function DoorAttack(Actor A)
{
if ( bShotAnim || Physics == PHYS_Swimming) return;
else if ( A!=none )
{ bShotAnim = true; if( !bDecapitated && bDistanceAttackingDoor ) { SetAnimAction('ZombieBarf'); } else { SetAnimAction('DoorBash'); GotoState('DoorBashing'); }
}
}
function RangedAttack(Actor A)
{
local int LastFireTime;
if ( bShotAnim ) return;
if ( Physics == PHYS_Swimming )
{ SetAnimAction('Claw'); bShotAnim = true; LastFireTime = Level.TimeSeconds;
}
else if ( VSize(A.Location - Location) < MeleeRange + CollisionRadius + A.CollisionRadius )
{ 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 ( (KFDoorMover(A) != none || VSize(A.Location-Location) <= 250) && !bDecapitated )
{ bShotAnim = true;
// Randomly do a moving attack so the player can't kite the zed if( FRand() < 0.8 ) { SetAnimAction('ZombieBarfMoving'); RunAttackTimeout = GetAnimDuration('ZombieBarf', 1.0); bMovingPukeAttack=true; } else { SetAnimAction('ZombieBarf'); Controller.bPreparingMove = true; Acceleration = vect(0,0,0); }
// Randomly send out a message about Bloat Vomit burning(3% chance) if ( FRand() < 0.03 && KFHumanPawn(A) != none && PlayerController(KFHumanPawn(A).Controller) != none ) { PlayerController(KFHumanPawn(A).Controller).Speech('AUTO', 7, ""); }
}
}
// Overridden to handle playing upper body only attacks when moving
simulated event SetAnimAction(name NewAction)
{
local int meleeAnimIndex;
local bool bWantsToAttackAndMove;
if( NewAction=='' ) Return;
bWantsToAttackAndMove = NewAction == 'ZombieBarfMoving';
if( NewAction == 'Claw' )
{ meleeAnimIndex = Rand(3); NewAction = meleeAnims[meleeAnimIndex];
}
if( bWantsToAttackAndMove )
{ ExpectingChannel = AttackAndMoveDoAnimAction(NewAction);
}
else
{ ExpectingChannel = DoAnimAction(NewAction);
}
if( !bWantsToAttackAndMove && AnimNeedsWait(NewAction) )
{ bWaitForAnim = true;
}
else
{ bWaitForAnim = false;
}
if( Level.NetMode!=NM_Client )
{ AnimAction = NewAction; bResetAnimAct = True; ResetAnimActTime = Level.TimeSeconds+0.3;
}
}
// Handle playing the anim action on the upper body only if we're attacking and moving
simulated function int AttackAndMoveDoAnimAction( name AnimName )
{
if( AnimName=='ZombieBarfMoving' )
{ AnimBlendParams(1, 1.0, 0.0,, FireRootBone); PlayAnim('ZombieBarf',, 0.1, 1);
return 1;
}
return super.DoAnimAction( AnimName );
}
function PlayDyingSound()
{
if( Level.NetMode!=NM_Client )
{ if ( bGibbed ) { PlaySound(sound'KF_EnemiesFinalSnd.Bloat_DeathPop', SLOT_Pain,2.0,true,525); return; }
if( bDecapitated ) { PlaySound(HeadlessDeathSound, SLOT_Pain,1.30,true,525); } else { PlaySound(sound'KF_EnemiesFinalSnd.Bloat_DeathPop', SLOT_Pain,2.0,true,525); }
}
}
// Barf Time.
function SpawnTwoShots()
{
local vector X,Y,Z, FireStart;
local rotator FireRotation;
if( Controller!=none && KFDoorMover(Controller.Target)!=none )
{ Controller.Target.TakeDamage(22,Self,Location,vect(0,0,0),Class'DamTypeVomit'); return;
}
GetAxes(Rotation,X,Y,Z);
FireStart = Location+(vect(30,0,64) >> Rotation)*DrawScale;
if ( !SavedFireProperties.bInitialized )
{ SavedFireProperties.AmmoClass = Class'SkaarjAmmo'; SavedFireProperties.ProjectileClass = Class'KFBloatVomit'; SavedFireProperties.WarnTargetPct = 1; SavedFireProperties.MaxRange = 500; SavedFireProperties.bTossed = False; SavedFireProperties.bTrySplash = False; SavedFireProperties.bLeadTarget = True; SavedFireProperties.bInstantHit = True; SavedFireProperties.bInitialized = True;
}
// Turn off extra collision before spawning vomit, otherwise spawn fails
ToggleAuxCollision(false);
FireRotation = Controller.AdjustAim(SavedFireProperties,FireStart,600);
Spawn(Class'KFBloatVomit',,,FireStart,FireRotation);
FireStart-=(0.5*CollisionRadius*Y);
FireRotation.Yaw -= 1200;
spawn(Class'KFBloatVomit',,,FireStart, FireRotation);
FireStart+=(CollisionRadius*Y);
FireRotation.Yaw += 2400;
spawn(Class'KFBloatVomit',,,FireStart, FireRotation);
// Turn extra collision back on
ToggleAuxCollision(true);
}
simulated function Tick(float deltatime)
{
local vector BileExplosionLoc;
local FleshHitEmitter GibBileExplosion;
Super.tick(deltatime);
if( Role == ROLE_Authority && bMovingPukeAttack )
{ // Keep moving toward the target until the timer runs out (anim finishes) if( RunAttackTimeout > 0 ) { RunAttackTimeout -= DeltaTime;
if( RunAttackTimeout <= 0 ) { RunAttackTimeout = 0; bMovingPukeAttack=false; } }
// Keep the gorefast moving toward its target when attacking if( bShotAnim && !bWaitForAnim ) { if( LookTarget!=none ) { Acceleration = AccelRate * Normal(LookTarget.Location - Location); } }
}
// Hack to force animation updates on the server for the bloat if he is relevant to someone
// He has glitches when some of his animations don't play on the server. If we
// find some other fix for the glitches take this out - Ramm
if( Level.NetMode != NM_Client && Level.NetMode != NM_Standalone )
{ if( (Level.TimeSeconds-LastSeenOrRelevantTime) < 1.0 ) { bForceSkelUpdate=true; } else { bForceSkelUpdate=false; }
}
if ( Level.NetMode!=NM_DedicatedServer && /*Gored>0*/Health <= 0 && !bPlayBileSplash && HitDamageType != class'DamTypeBleedOut' )
{ if ( !class'GameInfo'.static.UseLowGore() ) { BileExplosionLoc = self.Location; BileExplosionLoc.z += (CollisionHeight - (CollisionHeight * 0.5));
if (bDecapitated) { GibBileExplosion = Spawn(BileExplosionHeadless,self,, BileExplosionLoc ); } else { GibBileExplosion = Spawn(BileExplosion,self,, BileExplosionLoc ); } bPlayBileSplash = true; } else { BileExplosionLoc = self.Location; BileExplosionLoc.z += (CollisionHeight - (CollisionHeight * 0.5));
GibBileExplosion = Spawn(class 'LowGoreBileExplosion',self,, BileExplosionLoc ); bPlayBileSplash = true; }
}
}
function BileBomb()
{
BloatJet = spawn(class'BileJet', self,,Location,Rotator(-PhysicsVolume.Gravity));
}
function PlayDyingAnimation(class<DamageType> DamageType, vector HitLoc)
{
// local bool AttachSucess;
super.PlayDyingAnimation(DamageType, HitLoc);
// Don't blow up with bleed out
if( bDecapitated && DamageType == class'DamTypeBleedOut' )
{ return;
}
if ( !class'GameInfo'.static.UseLowGore() )
{ HideBone(SpineBone2);
}
if(Role == ROLE_Authority)
{ BileBomb();
// if(BloatJet!=none)
// {
// if(Gored < 5)
// AttachSucess=AttachToBone(BloatJet,FireRootBone);
// // else
// // AttachSucess=AttachToBone(BloatJet,SpineBone1);
//
// if(!AttachSucess)
// {
// log("DEAD Bloaty Bile didn't like the Boning :o");
// BloatJet.SetBase(self);
// }
// BloatJet.SetRelativeRotation(rot(0,-4096,0));
// }
}
}
simulated function ProcessHitFX()
{
local Coords boneCoords;
local class<xEmitter> HitEffects[4];
local int i,j;
local float GibPerterbation;
if( (Level.NetMode == NM_DedicatedServer) || bSkeletized || (Mesh == SkeletonMesh))
{ SimHitFxTicker = HitFxTicker; return;
}
for ( SimHitFxTicker = SimHitFxTicker; SimHitFxTicker != HitFxTicker; SimHitFxTicker = (SimHitFxTicker + 1) % ArrayCount(HitFX) )
{ j++; if ( j > 30 ) { SimHitFxTicker = HitFxTicker; return; }
if( (HitFX[SimHitFxTicker].damtype == none) || (Level.bDropDetail && (Level.TimeSeconds - LastRenderTime > 3) && !IsHumanControlled()) ) continue;
//log("Processing effects for damtype "$HitFX[SimHitFxTicker].damtype);
if( HitFX[SimHitFxTicker].bone == 'obliterate' && !class'GameInfo'.static.UseLowGore()) { SpawnGibs( HitFX[SimHitFxTicker].rotDir, 1); bGibbed = true; // Wait a tick on a listen server so the obliteration can replicate before the pawn is destroyed if( Level.NetMode == NM_ListenServer ) { bDestroyNextTick = true; TimeSetDestroyNextTickTime = Level.TimeSeconds; } else { Destroy(); } return; }
boneCoords = GetBoneCoords( HitFX[SimHitFxTicker].bone );
if ( !Level.bDropDetail && !class'GameInfo'.static.NoBlood() && !bSkeletized && !class'GameInfo'.static.UseLowGore() ) { //AttachEmitterEffect( BleedingEmitterClass, HitFX[SimHitFxTicker].bone, boneCoords.Origin, HitFX[SimHitFxTicker].rotDir );
HitFX[SimHitFxTicker].damtype.static.GetHitEffects( HitEffects, Health );
if( !PhysicsVolume.bWaterVolume ) // don't attach effects under water { for( i = 0; i < ArrayCount(HitEffects); i++ ) { if( HitEffects[i] == none ) continue;
AttachEffect( HitEffects[i], HitFX[SimHitFxTicker].bone, boneCoords.Origin, HitFX[SimHitFxTicker].rotDir ); } } }
if ( class'GameInfo'.static.UseLowGore() ) { HitFX[SimHitFxTicker].bSever = false;
switch( HitFX[SimHitFxTicker].bone ) { case 'head': if( !bHeadGibbed ) { if ( HitFX[SimHitFxTicker].damtype == class'DamTypeDecapitation' ) { DecapFX( boneCoords.Origin, HitFX[SimHitFxTicker].rotDir, false); } else if( HitFX[SimHitFxTicker].damtype == class'DamTypeProjectileDecap' ) { DecapFX( boneCoords.Origin, HitFX[SimHitFxTicker].rotDir, false, true); } else if( HitFX[SimHitFxTicker].damtype == class'DamTypeMeleeDecapitation' ) { DecapFX( boneCoords.Origin, HitFX[SimHitFxTicker].rotDir, true); }
bHeadGibbed=true; } break; }
return; }
if( HitFX[SimHitFxTicker].bSever ) { GibPerterbation = HitFX[SimHitFxTicker].damtype.default.GibPerterbation;
switch( HitFX[SimHitFxTicker].bone ) { case 'obliterate': break;
case LeftThighBone: if( !bLeftLegGibbed ) { SpawnSeveredGiblet( DetachedLegClass, boneCoords.Origin, HitFX[SimHitFxTicker].rotDir, GibPerterbation, GetBoneRotation(HitFX[SimHitFxTicker].bone) ); KFSpawnGiblet( class 'KFMod.KFGibBrain',boneCoords.Origin, HitFX[SimHitFxTicker].rotDir, GibPerterbation, 250 ) ; KFSpawnGiblet( class 'KFMod.KFGibBrainb',boneCoords.Origin, HitFX[SimHitFxTicker].rotDir, GibPerterbation, 250 ) ; KFSpawnGiblet( class 'KFMod.KFGibBrain',boneCoords.Origin, HitFX[SimHitFxTicker].rotDir, GibPerterbation, 250 ) ; bLeftLegGibbed=true; } break;
case RightThighBone: if( !bRightLegGibbed ) { SpawnSeveredGiblet( DetachedLegClass, boneCoords.Origin, HitFX[SimHitFxTicker].rotDir, GibPerterbation, GetBoneRotation(HitFX[SimHitFxTicker].bone) ); KFSpawnGiblet( class 'KFMod.KFGibBrain',boneCoords.Origin, HitFX[SimHitFxTicker].rotDir, GibPerterbation, 250 ) ; KFSpawnGiblet( class 'KFMod.KFGibBrainb',boneCoords.Origin, HitFX[SimHitFxTicker].rotDir, GibPerterbation, 250 ) ; KFSpawnGiblet( class 'KFMod.KFGibBrain',boneCoords.Origin, HitFX[SimHitFxTicker].rotDir, GibPerterbation, 250 ) ; bRightLegGibbed=true; } break;
case LeftFArmBone: if( !bLeftArmGibbed ) { SpawnSeveredGiblet( DetachedArmClass, boneCoords.Origin, HitFX[SimHitFxTicker].rotDir, GibPerterbation, GetBoneRotation(HitFX[SimHitFxTicker].bone) ); KFSpawnGiblet( class 'KFMod.KFGibBrain',boneCoords.Origin, HitFX[SimHitFxTicker].rotDir, GibPerterbation, 250 ) ; KFSpawnGiblet( class 'KFMod.KFGibBrainb',boneCoords.Origin, HitFX[SimHitFxTicker].rotDir, GibPerterbation, 250 ) ;; bLeftArmGibbed=true; } break;
case RightFArmBone: if( !bRightArmGibbed ) { SpawnSeveredGiblet( DetachedArmClass, boneCoords.Origin, HitFX[SimHitFxTicker].rotDir, GibPerterbation, GetBoneRotation(HitFX[SimHitFxTicker].bone) ); KFSpawnGiblet( class 'KFMod.KFGibBrain',boneCoords.Origin, HitFX[SimHitFxTicker].rotDir, GibPerterbation, 250 ) ; KFSpawnGiblet( class 'KFMod.KFGibBrainb',boneCoords.Origin, HitFX[SimHitFxTicker].rotDir, GibPerterbation, 250 ) ; bRightArmGibbed=true; } break;
case 'head': if( !bHeadGibbed ) { if ( HitFX[SimHitFxTicker].damtype == class'DamTypeDecapitation' ) { DecapFX( boneCoords.Origin, HitFX[SimHitFxTicker].rotDir, false); } else if( HitFX[SimHitFxTicker].damtype == class'DamTypeProjectileDecap' ) { DecapFX( boneCoords.Origin, HitFX[SimHitFxTicker].rotDir, false, true); } else if( HitFX[SimHitFxTicker].damtype == class'DamTypeMeleeDecapitation' ) { DecapFX( boneCoords.Origin, HitFX[SimHitFxTicker].rotDir, true); }
bHeadGibbed=true; } break; }
// Don't do this right now until we get the effects sorted - Ramm if( HitFX[SimHitFXTicker].bone != 'Spine' && HitFX[SimHitFXTicker].bone != FireRootBone && HitFX[SimHitFXTicker].bone != LeftFArmBone && HitFX[SimHitFXTicker].bone != RightFArmBone && HitFX[SimHitFXTicker].bone != 'head' && Health <=0 ) HideBone(HitFX[SimHitFxTicker].bone); }
}
}
simulated function HideBone(name boneName)
{
local int BoneScaleSlot;
local coords boneCoords;
local bool bValidBoneToHide;
if( boneName == LeftThighBone )
{ boneScaleSlot = 0; bValidBoneToHide = true; if( SeveredLeftLeg == none ) { SeveredLeftLeg = Spawn(SeveredLegAttachClass,self); SeveredLeftLeg.SetDrawScale(SeveredLegAttachScale); boneCoords = GetBoneCoords( 'lleg' ); AttachEmitterEffect( LimbSpurtEmitterClass, 'lleg', boneCoords.Origin, rot(0,0,0) ); AttachToBone(SeveredLeftLeg, 'lleg'); }
}
else if ( boneName == RightThighBone )
{ boneScaleSlot = 1; bValidBoneToHide = true; if( SeveredRightLeg == none ) { SeveredRightLeg = Spawn(SeveredLegAttachClass,self); SeveredRightLeg.SetDrawScale(SeveredLegAttachScale); boneCoords = GetBoneCoords( 'rleg' ); AttachEmitterEffect( LimbSpurtEmitterClass, 'rleg', boneCoords.Origin, rot(0,0,0) ); AttachToBone(SeveredRightLeg, 'rleg'); }
}
else if( boneName == RightFArmBone )
{ boneScaleSlot = 2; bValidBoneToHide = true; if( SeveredRightArm == none ) { SeveredRightArm = Spawn(SeveredArmAttachClass,self); SeveredRightArm.SetDrawScale(SeveredArmAttachScale); boneCoords = GetBoneCoords( 'rarm' ); AttachEmitterEffect( LimbSpurtEmitterClass, 'rarm', boneCoords.Origin, rot(0,0,0) ); AttachToBone(SeveredRightArm, 'rarm'); }
}
else if ( boneName == LeftFArmBone )
{ boneScaleSlot = 3; bValidBoneToHide = true; if( SeveredLeftArm == none ) { SeveredLeftArm = Spawn(SeveredArmAttachClass,self); SeveredLeftArm.SetDrawScale(SeveredArmAttachScale); boneCoords = GetBoneCoords( 'larm' ); AttachEmitterEffect( LimbSpurtEmitterClass, 'larm', boneCoords.Origin, rot(0,0,0) ); AttachToBone(SeveredLeftArm, 'larm'); }
}
else if ( boneName == HeadBone )
{ // Only scale the bone down once if( SeveredHead == none ) { bValidBoneToHide = true; boneScaleSlot = 4; SeveredHead = Spawn(SeveredHeadAttachClass,self); SeveredHead.SetDrawScale(SeveredHeadAttachScale); boneCoords = GetBoneCoords( 'neck' ); AttachEmitterEffect( NeckSpurtEmitterClass, 'neck', boneCoords.Origin, rot(0,0,0) ); AttachToBone(SeveredHead, 'neck'); } else { return; }
}
else if ( boneName == 'spine' )
{ bValidBoneToHide = true; boneScaleSlot = 5;
}
else if ( boneName == SpineBone2 )
{ bValidBoneToHide = true; boneScaleSlot = 6;
}
// Only hide the bone if it is one of the arms, legs, or head, don't hide other misc bones
if( bValidBoneToHide )
{ SetBoneScale(BoneScaleSlot, 0.0, BoneName);
}
}
State Dying
{
function tick(float deltaTime)
{
if (BloatJet != none)
{
BloatJet.SetLocation(location);
BloatJet.SetRotation(GetBoneRotation(FireRootBone));
}
super.tick(deltaTime);
}
}
function RemoveHead()
{
bCanDistanceAttackDoors = False;
Super.RemoveHead();
}
function ModDamage(out int Damage, Pawn instigatedBy, Vector hitlocation, Vector momentum, class<NiceWeaponDamageType> damageType, float headshotLevel, KFPlayerReplicationInfo KFPRI, optional float lockonTime){
if(damageType == class 'DamTypeVomit' || damageType == class 'DamTypeBlowerThrower') Damage = 0;
else Super.ModDamage(Damage, instigatedBy, hitlocation, momentum, damageType, headshotLevel, KFPRI);
}
static simulated function PreCacheStaticMeshes(LevelInfo myLevel)
{//should be derived and used.
Super.PreCacheStaticMeshes(myLevel);
myLevel.AddPrecacheStaticMesh(StaticMesh'kf_gore_trip_sm.limbs.bloat_head');
}
static simulated function PreCacheMaterials(LevelInfo myLevel)
{
myLevel.AddPrecacheMaterial(Combiner'KF_Specimens_Trip_T.bloat_cmb');
myLevel.AddPrecacheMaterial(Combiner'KF_Specimens_Trip_T.bloat_env_cmb');
myLevel.AddPrecacheMaterial(Texture'KF_Specimens_Trip_T.bloat_diffuse');
}
defaultproperties
{ BileExplosion=Class'KFMod.BileExplosion' BileExplosionHeadless=Class'KFMod.BileExplosionHeadless' stunLoopStart=0.100000 stunLoopEnd=0.600000 idleInsertFrame=0.950000 EventClasses(0)="NicePack.NiceZombieBloat" MoanVoice=SoundGroup'KF_EnemiesFinalSnd.Bloat.Bloat_Talk' MeleeAttackHitSound=SoundGroup'KF_EnemiesFinalSnd.Bloat.Bloat_HitPlayer' JumpSound=SoundGroup'KF_EnemiesFinalSnd.Bloat.Bloat_Jump' DetachedArmClass=Class'KFChar.SeveredArmBloat' DetachedLegClass=Class'KFChar.SeveredLegBloat' DetachedHeadClass=Class'KFChar.SeveredHeadBloat' HitSound(0)=SoundGroup'KF_EnemiesFinalSnd.Bloat.Bloat_Pain' DeathSound(0)=SoundGroup'KF_EnemiesFinalSnd.Bloat.Bloat_Death' ChallengeSound(0)=SoundGroup'KF_EnemiesFinalSnd.Bloat.Bloat_Challenge' ChallengeSound(1)=SoundGroup'KF_EnemiesFinalSnd.Bloat.Bloat_Challenge' ChallengeSound(2)=SoundGroup'KF_EnemiesFinalSnd.Bloat.Bloat_Challenge' ChallengeSound(3)=SoundGroup'KF_EnemiesFinalSnd.Bloat.Bloat_Challenge' AmbientSound=Sound'KF_BaseBloat.Bloat_Idle1Loop' Mesh=SkeletalMesh'KF_Freaks_Trip.Bloat_Freak' Skins(0)=Combiner'KF_Specimens_Trip_T.bloat_cmb'
}

View file

@ -0,0 +1,15 @@
// Zombie Monster for KF Invasion gametype
class NiceZombieBloatBase extends NiceMonster
abstract;
#exec OBJ LOAD FILE=KF_EnemiesFinalSnd.uax
var BileJet BloatJet;
var bool bPlayBileSplash;
var bool bMovingPukeAttack;
var float RunAttackTimeout;
//-------------------------------------------------------------------------------
// NOTE: All Code resides in the child class(this class was only created to
// eliminate hitching caused by loading default properties during play)
//-------------------------------------------------------------------------------
defaultproperties
{ StunThreshold=4.000000 fuelRatio=0.250000 clientHeadshotScale=1.500000 MeleeAnims(0)="BloatChop2" MeleeAnims(1)="BloatChop2" MeleeAnims(2)="BloatChop2" BleedOutDuration=6.000000 ZapThreshold=0.500000 ZappedDamageMod=1.500000 bHarpoonToBodyStuns=False ZombieFlag=1 MeleeDamage=14 damageForce=70000 bFatAss=True KFRagdollName="Bloat_Trip" PuntAnim="BloatPunt" Intelligence=BRAINS_Stupid bCanDistanceAttackDoors=True bUseExtendedCollision=True ColOffset=(Z=60.000000) ColRadius=27.000000 ColHeight=22.000000 SeveredArmAttachScale=1.100000 SeveredLegAttachScale=1.300000 SeveredHeadAttachScale=1.700000 PlayerCountHealthScale=0.250000 OnlineHeadshotOffset=(X=5.000000,Z=70.000000) OnlineHeadshotScale=1.500000 AmmunitionClass=Class'KFMod.BZombieAmmo' ScoringValue=17 IdleHeavyAnim="BloatIdle" IdleRifleAnim="BloatIdle" MeleeRange=30.000000 GroundSpeed=75.000000 WaterSpeed=102.000000 HealthMax=525.000000 Health=525 HeadHeight=2.500000 HeadScale=1.500000 AmbientSoundScaling=8.000000 MenuName="Nice Bloat" MovementAnims(0)="WalkBloat" MovementAnims(1)="WalkBloat" WalkAnims(0)="WalkBloat" WalkAnims(1)="WalkBloat" WalkAnims(2)="WalkBloat" WalkAnims(3)="WalkBloat" IdleCrouchAnim="BloatIdle" IdleWeaponAnim="BloatIdle" IdleRestAnim="BloatIdle" DrawScale=1.075000 PrePivot=(Z=5.000000) SoundVolume=200 Mass=400.000000 RotationRate=(Yaw=45000,Roll=0)
}

View file

@ -0,0 +1,871 @@
// Zombie Monster for KF Invasion gametype
class NiceZombieBoss extends NiceZombieBossBase;
#exec OBJ LOAD FILE=KFPatch2.utx
#exec OBJ LOAD FILE=KF_Specimens_Trip_T.utx
//----------------------------------------------------------------------------
// NOTE: Most Variables are declared in the base class to eliminate hitching
//----------------------------------------------------------------------------
var NiceBossHPNeedle CurrentNeedle;
// Last time we checked if a player was melee exploiting us
var float LastMeleeExploitCheckTime;
// Used to track what type of melee exploiters you have
var int NumLumberJacks;
var int NumNinjas;
// Make the Boss's ambient scale higher, since there is only 1, doesn't matter if he's relevant almost all the time
simulated function CalcAmbientRelevancyScale()
{ // Make the zed only relevant by thier ambient sound out to a range of 100 meters CustomAmbientRelevancyScale = 5000/(100 * SoundRadius);
}
function vector ComputeTrajectoryByTime( vector StartPosition, vector EndPosition, float fTimeEnd )
{
local vector NewVelocity;
NewVelocity = Super.ComputeTrajectoryByTime( StartPosition, EndPosition, fTimeEnd );
if( PhysicsVolume.IsA( 'KFPhysicsVolume' ) && StartPosition.Z < EndPosition.Z )
{ if( PhysicsVolume.Gravity.Z < class'PhysicsVolume'.default.Gravity.Z ) { // Just checking mass to be extra-cautious. if( Mass > 900 ) { // Extra velocity boost to counter oversized mass weighing the boss down. NewVelocity.Z += 90; } }
}
return NewVelocity;
}
function ZombieMoan()
{
if( !bShotAnim ) // Do not moan while taunting Super.ZombieMoan();
}
// Speech notifies called from the anims
function PatriarchKnockDown()
{
PlaySound(SoundGroup'KF_EnemiesFinalSnd.Patriarch.Kev_KnockedDown', SLOT_Misc, 2.0,true,500.0);
}
function PatriarchEntrance()
{
PlaySound(SoundGroup'KF_EnemiesFinalSnd.Patriarch.Kev_Entrance', SLOT_Misc, 2.0,true,500.0);
}
function PatriarchVictory()
{
PlaySound(SoundGroup'KF_EnemiesFinalSnd.Patriarch.Kev_Victory', SLOT_Misc, 2.0,true,500.0);
}
function PatriarchMGPreFire()
{
PlaySound(SoundGroup'KF_EnemiesFinalSnd.Patriarch.Kev_WarnGun', SLOT_Misc, 2.0,true,1000.0);
}
function PatriarchMisslePreFire()
{
PlaySound(SoundGroup'KF_EnemiesFinalSnd.Patriarch.Kev_WarnRocket', SLOT_Misc, 2.0,true,1000.0);
}
// Taunt to use when doing the melee exploit radial attack
function PatriarchRadialTaunt()
{
if( NumNinjas > 0 && NumNinjas > NumLumberJacks )
{ PlaySound(SoundGroup'KF_EnemiesFinalSnd.Patriarch.Kev_TauntNinja', SLOT_Misc, 2.0,true,500.0);
}
else if( NumLumberJacks > 0 && NumLumberJacks > NumNinjas )
{ PlaySound(SoundGroup'KF_EnemiesFinalSnd.Patriarch.Kev_TauntLumberJack', SLOT_Misc, 2.0,true,500.0);
}
else
{ PlaySound(SoundGroup'KF_EnemiesFinalSnd.Patriarch.Kev_TauntRadial', SLOT_Misc, 2.0,true,500.0);
}
}
// Don't do this for the Patriarch
// No need to overload these when inheriting from NiceMonster
/*simulated function SetBurningBehavior(){}
simulated function UnSetBurningBehavior(){}*/
function bool CanGetOutOfWay(){
return false;
}
simulated function Tick(float DeltaTime)
{
local KFHumanPawn HP;
Super.Tick(DeltaTime);
// Process the pipe bomb time damage scale, reducing the scale over time so
// it goes back up to 100% damage over a few seconds
if( Role == ROLE_Authority )
{ PipeBombDamageScale -= DeltaTime * 0.33;
if( PipeBombDamageScale < 0 ) { PipeBombDamageScale = 0; }
}
if( Level.NetMode==NM_DedicatedServer ) Return; // Servers aren't intrested in this info.
bSpecialCalcView = bIsBossView;
if( bZapped )
{ // Make sure we check if we need to be cloaked as soon as the zap wears off LastCheckTimes = Level.TimeSeconds;
}
else if( bCloaked && Level.TimeSeconds>LastCheckTimes )
{ LastCheckTimes = Level.TimeSeconds+0.8; ForEach VisibleCollidingActors(Class'KFHumanPawn',HP,1000,Location) { if( HP.Health <= 0 || !HP.ShowStalkers() ) continue;
// If he's a commando, we've been spotted. if( !bSpotted ) { bSpotted = True; CloakBoss(); } Return; } // if we're uberbrite, turn down the light if( bSpotted ) { bSpotted = False; bUnlit = false; CloakBoss(); }
}
}
simulated function CloakBoss()
{
local Controller C;
local int Index;
// No cloaking if zapped
if( bZapped )
{ return;
}
if( bSpotted )
{ Visibility = 120; if( Level.NetMode==NM_DedicatedServer ) Return; Skins[0] = Finalblend'KFX.StalkerGlow'; Skins[1] = Finalblend'KFX.StalkerGlow'; bUnlit = true; return;
}
Visibility = 1;
bCloaked = true;
if( Level.NetMode!=NM_Client )
{ For( C=Level.ControllerList; C!=none; C=C.NextController ) { if( C.bIsPlayer && C.Enemy==Self ) C.Enemy = none; // Make bots lose sight with me. }
}
if( Level.NetMode==NM_DedicatedServer ) Return;
Skins[0] = Shader'KF_Specimens_Trip_T.patriarch_invisible_gun';
Skins[1] = Shader'KF_Specimens_Trip_T.patriarch_invisible';
// Invisible - no shadow
if(PlayerShadow != none) PlayerShadow.bShadowActive = false;
// Remove/disallow projectors on invisible people
Projectors.Remove(0, Projectors.Length);
bAcceptsProjectors = false;
SetOverlayMaterial(FinalBlend'KF_Specimens_Trip_T.patriarch_fizzle_FB', 1.0, true);
// Randomly send out a message about Patriarch going invisible(10% chance)
if ( FRand() < 0.10 )
{ // Pick a random Player to say the message Index = Rand(Level.Game.NumPlayers);
for ( C = Level.ControllerList; C != none; C = C.NextController ) { if ( PlayerController(C) != none ) { if ( Index == 0 ) { PlayerController(C).Speech('AUTO', 8, ""); break; }
Index--; } }
}
}
simulated function UnCloakBoss()
{
if( bZapped )
{ return;
}
Visibility = default.Visibility;
bCloaked = false;
bSpotted = False;
bUnlit = False;
if( Level.NetMode==NM_DedicatedServer ) Return;
Skins = Default.Skins;
if (PlayerShadow != none) PlayerShadow.bShadowActive = true;
bAcceptsProjectors = true;
SetOverlayMaterial( none, 0.0, true );
}
// Set the zed to the zapped behavior
simulated function SetZappedBehavior()
{
super.SetZappedBehavior();
// Handle setting the zed to uncloaked so the zapped overlay works properly
if( Level.Netmode != NM_DedicatedServer )
{ bUnlit = false; Skins = Default.Skins;
if (PlayerShadow != none) PlayerShadow.bShadowActive = true;
bAcceptsProjectors = true; SetOverlayMaterial(Material'KFZED_FX_T.Energy.ZED_overlay_Hit_Shdr', 999, true);
}
}
// Turn off the zapped behavior
simulated function UnSetZappedBehavior()
{
super.UnSetZappedBehavior();
// Handle getting the zed back cloaked if need be
if( Level.Netmode != NM_DedicatedServer )
{ LastCheckTimes = Level.TimeSeconds; SetOverlayMaterial(none, 0.0f, true);
}
}
// Overridden because we need to handle the overlays differently for zombies that can cloak
function SetZapped(float ZapAmount, Pawn Instigator)
{
LastZapTime = Level.TimeSeconds;
if( bZapped )
{ TotalZap = ZapThreshold; RemainingZap = ZapDuration;
}
else
{ TotalZap += ZapAmount;
if( TotalZap >= ZapThreshold ) { RemainingZap = ZapDuration; bZapped = true; }
}
ZappedBy = Instigator;
}
//-----------------------------------------------------------------------------
// PostBeginPlay
//-----------------------------------------------------------------------------
simulated function PostBeginPlay()
{
super.PostBeginPlay();
if( Role < ROLE_Authority )
{ return;
}
// Difficulty Scaling
if (Level.Game != none)
{ if(Level.Game.GameDifficulty >= 5.0) // Hell on Earth & Suicidal MGDamage = default.MGDamage * 1.3; else MGDamage = default.MGDamage * 1.0;
}
HealingLevels[0] = Health/1.25; // Around 5600 HP
HealingLevels[1] = Health/2.f; // Around 3500 HP
HealingLevels[2] = Health/3.2; // Around 2187 HP
// log("Health = "$Health);
// log("HealingLevels[0] = "$HealingLevels[0]);
// log("HealingLevels[1] = "$HealingLevels[1]);
// log("HealingLevels[2] = "$HealingLevels[2]);
HealingAmount = Health/4; // 1750 HP
// log("HealingAmount = "$HealingAmount);
}
function bool MakeGrandEntry()
{
bShotAnim = true;
Acceleration = vect(0,0,0);
SetAnimAction('Entrance');
HandleWaitForAnim('Entrance');
GotoState('MakingEntrance');
return True;
}
// State of playing the initial entrance anim
state MakingEntrance
{
Ignores RangedAttack;
function Tick( float Delta )
{ Acceleration = vect(0,0,0);
global.Tick(Delta);
}
Begin:
Sleep(GetAnimDuration('Entrance'));
GotoState('InitialSneak');
}
// State of doing a radial damaging attack that we do when people are trying to melee exploit
state RadialAttack
{
Ignores RangedAttack;
function bool ShouldChargeFromDamage()
{ return false;
}
function Tick( float Delta )
{ Acceleration = vect(0,0,0);
//DrawDebugSphere( Location, 150, 12, 0, 255, 0);
global.Tick(Delta);
}
function ClawDamageTarget()
{ local vector PushDir; local float UsedMeleeDamage; local bool bDamagedSomeone, bDamagedThisHit; local KFHumanPawn P; local Actor OldTarget; local float RadialDamageBase;
MeleeRange = 150;
if(Controller!=none && Controller.Target!=none) PushDir = (damageForce * Normal(Controller.Target.Location - Location)); else PushDir = damageForce * vector(Rotation);
OldTarget = Controller.Target;
// Damage all players within a radius foreach DynamicActors(class'KFHumanPawn', P) { if ( VSize(P.Location - Location) < MeleeRange) { Controller.Target = P;
// This attack cuts through shields, so crank up the damage if they have a lot of shields if( P.ShieldStrength >= 50 ) { RadialDamageBase = 240; } else { RadialDamageBase = 120; }
// Randomize the damage a bit so everyone gets really hurt, but only some poeple die UsedMeleeDamage = (RadialDamageBase - (RadialDamageBase * 0.55)) + (RadialDamageBase * (FRand() * 0.45)); //log("UsedMeleeDamage = "$UsedMeleeDamage);
bDamagedThisHit = MeleeDamageTarget(UsedMeleeDamage, damageForce * Normal(P.Location - Location)); if( !bDamagedSomeone && bDamagedThisHit ) { bDamagedSomeone = true; } MeleeRange = 150; } }
Controller.Target = OldTarget;
MeleeRange = Default.MeleeRange;
if ( bDamagedSomeone ) { // Maybe cause zedtime when the patriarch does his radial attack KFGameType(Level.Game).DramaticEvent(0.3); PlaySound(MeleeAttackHitSound, SLOT_Interact, 2.0); }
}
function EndState()
{ NumLumberJacks = 0; NumNinjas = 0;
}
Begin:
// Don't let the zed move and play the radial attack
bShotAnim = true;
Acceleration = vect(0,0,0);
SetAnimAction('RadialAttack');
KFMonsterController(Controller).bUseFreezeHack = True;
HandleWaitForAnim('RadialAttack');
Sleep(GetAnimDuration('RadialAttack'));
// TODO: this sleep is here to allow for playing the taunt sound. Take it out when the animation is extended with the taunt - Ramm
//Sleep(2.5);
GotoState('');
}
simulated function Destroyed()
{
if( mTracer!=none ) mTracer.Destroy();
if( mMuzzleFlash!=none ) mMuzzleFlash.Destroy();
Super.Destroyed();
}
simulated Function PostNetBeginPlay()
{
EnableChannelNotify ( 1,1);
AnimBlendParams(1, 1.0, 0.0,, SpineBone1);
super.PostNetBeginPlay();
TraceHitPos = vect(0,0,0);
bNetNotify = True;
}
function bool IsStunPossible(){
return false;
}
function bool CheckStun(int stunScore, Pawn instigatedBy, Vector hitLocation, Vector momentum, class<NiceWeaponDamageType> damageType, float headshotLevel, KFPlayerReplicationInfo KFPRI){
return false;
}
function bool CheckMiniFlinch( int flinchScore, Pawn instigatedBy, Vector hitLocation, Vector momentum, class<NiceWeaponDamageType> damageType, float headshotLevel, KFPlayerReplicationInfo KFPRI){
return false;
}
function bool OnlyEnemyAround( Pawn Other )
{
local Controller C;
For( C=Level.ControllerList; C!=none; C=C.NextController )
{ if( C.bIsPlayer && C.Pawn!=none && C.Pawn!=Other && ((VSize(C.Pawn.Location-Location)<1500 && FastTrace(C.Pawn.Location,Location)) || (VSize(C.Pawn.Location-Other.Location)<1000 && FastTrace(C.Pawn.Location,Other.Location))) ) Return False;
}
Return True;
}
function bool IsCloseEnuf( Actor A )
{
local vector V;
if( A==none ) Return False;
V = A.Location-Location;
if( Abs(V.Z)>(CollisionHeight+A.CollisionHeight) ) Return False;
V.Z = 0;
Return (VSize(V)<(CollisionRadius+A.CollisionRadius+25));
}
function RangedAttack(Actor A)
{
local float D;
local bool bOnlyE;
local bool bDesireChainGun;
// Randomly make him want to chaingun more
if( Controller.LineOfSightTo(A) && FRand() < 0.15 && LastChainGunTime<Level.TimeSeconds )
{ bDesireChainGun = true;
}
if ( bShotAnim ) return;
D = VSize(A.Location-Location);
bOnlyE = (Pawn(A)!=none && OnlyEnemyAround(Pawn(A)));
if ( IsCloseEnuf(A) )
{ bShotAnim = true; if( Health>1500 && Pawn(A)!=none && FRand() < 0.5 ) { SetAnimAction('MeleeImpale'); } else { SetAnimAction('MeleeClaw'); //PlaySound(sound'Claw2s', SLOT_none); KFTODO: Replace this }
}
else if( Level.TimeSeconds - LastSneakedTime > 20.0 )
{ if( FRand() < 0.3 ) { // Wait another 20 to try this again LastSneakedTime = Level.TimeSeconds;//+FRand()*120; Return; } SetAnimAction('transition'); GoToState('SneakAround');
}
else if( bChargingPlayer && (bOnlyE || D<200) ) Return;
else if( !bDesireChainGun && !bChargingPlayer && (D<300 || (D<700 && bOnlyE)) && (Level.TimeSeconds - LastChargeTime > (5.0 + 5.0 * FRand())) ) // Don't charge again for a few seconds
{ SetAnimAction('transition'); GoToState('Charging');
}
else if( LastMissileTime<Level.TimeSeconds && D > 500 )
{ if( !Controller.LineOfSightTo(A) || FRand() > 0.75 ) { LastMissileTime = Level.TimeSeconds+FRand() * 5; Return; }
LastMissileTime = Level.TimeSeconds + 10 + FRand() * 15;
bShotAnim = true; Acceleration = vect(0,0,0); SetAnimAction('PreFireMissile');
HandleWaitForAnim('PreFireMissile');
GoToState('FireMissile');
}
else if ( !bWaitForAnim && !bShotAnim && LastChainGunTime<Level.TimeSeconds )
{ if ( !Controller.LineOfSightTo(A) || FRand()> 0.85 ) { LastChainGunTime = Level.TimeSeconds+FRand()*4; Return; }
LastChainGunTime = Level.TimeSeconds + 5 + FRand() * 10;
bShotAnim = true; Acceleration = vect(0,0,0); SetAnimAction('PreFireMG');
HandleWaitForAnim('PreFireMG'); MGFireCounter = Rand(60) + 35;
GoToState('FireChaingun');
}
}
event Bump(actor Other)
{
Super(Monster).Bump(Other);
if( Other==none ) return;
if( Other.IsA('NetKActor') && Physics != PHYS_Falling && !bShotAnim && Abs(Other.Location.Z-Location.Z)<(CollisionHeight+Other.CollisionHeight) )
{ // Kill the annoying deco brat. Controller.Target = Other; Controller.Focus = Other; bShotAnim = true; Acceleration = (Other.Location-Location); SetAnimAction('MeleeClaw'); //PlaySound(sound'Claw2s', SLOT_none); KFTODO: Replace this HandleWaitForAnim('MeleeClaw');
}
}
simulated function AddTraceHitFX( vector HitPos )
{
local vector Start,SpawnVel,SpawnDir;
local float hitDist;
Start = GetBoneCoords('tip').Origin;
if( mTracer==none ) mTracer = Spawn(Class'KFMod.KFNewTracer',,,Start);
else mTracer.SetLocation(Start);
if( mMuzzleFlash==none )
{ // KFTODO: Replace this mMuzzleFlash = Spawn(Class'MuzzleFlash3rdMG'); AttachToBone(mMuzzleFlash, 'tip');
}
else mMuzzleFlash.SpawnParticle(1);
hitDist = VSize(HitPos - Start) - 50.f;
if( hitDist>10 )
{ SpawnDir = Normal(HitPos - Start); SpawnVel = SpawnDir * 10000.f; mTracer.Emitters[0].StartVelocityRange.X.Min = SpawnVel.X; mTracer.Emitters[0].StartVelocityRange.X.Max = SpawnVel.X; mTracer.Emitters[0].StartVelocityRange.Y.Min = SpawnVel.Y; mTracer.Emitters[0].StartVelocityRange.Y.Max = SpawnVel.Y; mTracer.Emitters[0].StartVelocityRange.Z.Min = SpawnVel.Z; mTracer.Emitters[0].StartVelocityRange.Z.Max = SpawnVel.Z; mTracer.Emitters[0].LifetimeRange.Min = hitDist / 10000.f; mTracer.Emitters[0].LifetimeRange.Max = mTracer.Emitters[0].LifetimeRange.Min; mTracer.SpawnParticle(1);
}
Instigator = Self;
if( HitPos != vect(0,0,0) )
{ Spawn(class'ROBulletHitEffect',,, HitPos, Rotator(Normal(HitPos - Start)));
}
}
simulated function AnimEnd( int Channel )
{
local name Sequence;
local float Frame, Rate;
if( Level.NetMode==NM_Client && bMinigunning )
{ GetAnimParams( Channel, Sequence, Frame, Rate );
if( Sequence != 'PreFireMG' && Sequence != 'FireMG' ) { Super.AnimEnd(Channel); return; }
PlayAnim('FireMG'); bWaitForAnim = true; bShotAnim = true; IdleTime = Level.TimeSeconds;
}
else Super.AnimEnd(Channel);
}
state FireChaingun
{
function RangedAttack(Actor A)
{ Controller.Target = A; Controller.Focus = A;
}
// Chaingun mode handles this itself
function bool ShouldChargeFromDamage()
{ return false;
}
function TakeDamage( int Damage, Pawn InstigatedBy, Vector Hitlocation, Vector Momentum, class<DamageType> damageType, optional int HitIndex)
{ local float EnemyDistSq, DamagerDistSq;
global.TakeDamage(Damage,instigatedBy,hitlocation,vect(0,0,0),damageType);
// if someone close up is shooting us, just charge them if( InstigatedBy != none ) { DamagerDistSq = VSizeSquared(Location - InstigatedBy.Location);
if( (ChargeDamage > 200 && DamagerDistSq < (500 * 500)) || DamagerDistSq < (100 * 100) ) { SetAnimAction('transition'); //log("Frak this shizz, Charging!!!!"); GoToState('Charging'); return; } }
if( Controller.Enemy != none && InstigatedBy != none && InstigatedBy != Controller.Enemy ) { EnemyDistSq = VSizeSquared(Location - Controller.Enemy.Location); DamagerDistSq = VSizeSquared(Location - InstigatedBy.Location); }
if( InstigatedBy != none && (DamagerDistSq < EnemyDistSq || Controller.Enemy == none) ) { MonsterController(Controller).ChangeEnemy(InstigatedBy,Controller.CanSee(InstigatedBy)); Controller.Target = InstigatedBy; Controller.Focus = InstigatedBy;
if( DamagerDistSq < (500 * 500) ) { SetAnimAction('transition'); GoToState('Charging'); } }
}
function EndState()
{ TraceHitPos = vect(0,0,0); bMinigunning = False;
AmbientSound = default.AmbientSound; SoundVolume=default.SoundVolume; SoundRadius=default.SoundRadius; MGFireCounter=0;
LastChainGunTime = Level.TimeSeconds + 5 + (FRand()*10);
}
function BeginState()
{ bFireAtWill = False; Acceleration = vect(0,0,0); MGLostSightTimeout = 0.0; bMinigunning = True;
}
function AnimEnd( int Channel )
{ if( MGFireCounter <= 0 ) { bShotAnim = true; Acceleration = vect(0,0,0); SetAnimAction('FireEndMG'); HandleWaitForAnim('FireEndMG'); GoToState(''); } else { if ( Controller.Enemy != none ) { if ( Controller.LineOfSightTo(Controller.Enemy) && FastTrace(GetBoneCoords('tip').Origin,Controller.Enemy.Location)) { MGLostSightTimeout = 0.0; Controller.Focus = Controller.Enemy; Controller.FocalPoint = Controller.Enemy.Location; } else { MGLostSightTimeout = Level.TimeSeconds + (0.25 + FRand() * 0.35); Controller.Focus = none; }
Controller.Target = Controller.Enemy; } else { MGLostSightTimeout = Level.TimeSeconds + (0.25 + FRand() * 0.35); Controller.Focus = none; }
if( !bFireAtWill ) { MGFireDuration = Level.TimeSeconds + (0.75 + FRand() * 0.5); } else if ( FRand() < 0.03 && Controller.Enemy != none && PlayerController(Controller.Enemy.Controller) != none ) { // Randomly send out a message about Patriarch shooting chain gun(3% chance) PlayerController(Controller.Enemy.Controller).Speech('AUTO', 9, ""); }
bFireAtWill = True; bShotAnim = true; Acceleration = vect(0,0,0);
SetAnimAction('FireMG'); bWaitForAnim = true; }
}
function FireMGShot()
{ local vector Start,End,HL,HN,Dir; local rotator R; local Actor A;
MGFireCounter--;
if( AmbientSound != MiniGunFireSound ) { SoundVolume=255; SoundRadius=400; AmbientSound = MiniGunFireSound; }
Start = GetBoneCoords('tip').Origin; if( Controller.Focus!=none ) R = rotator(Controller.Focus.Location-Start); else R = rotator(Controller.FocalPoint-Start); if( NeedToTurnFor(R) ) R = Rotation; // KFTODO: Maybe scale this accuracy by his skill or the game difficulty Dir = Normal(vector(R)+VRand()*0.06); //*0.04 End = Start+Dir*10000;
// Have to turn of hit point collision so trace doesn't hit the Human Pawn's bullet whiz cylinder bBlockHitPointTraces = false; A = Trace(HL,HN,End,Start,True); bBlockHitPointTraces = true;
if( A==none ) Return; TraceHitPos = HL; if( Level.NetMode!=NM_DedicatedServer ) AddTraceHitFX(HL);
if( A!=Level ) { A.TakeDamage(MGDamage+Rand(3),Self,HL,Dir*500,Class'DamageType'); }
}
function bool NeedToTurnFor( rotator targ )
{ local int YawErr;
targ.Yaw = DesiredRotation.Yaw & 65535; YawErr = (targ.Yaw - (Rotation.Yaw & 65535)) & 65535; return !((YawErr < 2000) || (YawErr > 64535));
}
Begin:
While( True )
{ Acceleration = vect(0,0,0);
if( MGLostSightTimeout > 0 && Level.TimeSeconds > MGLostSightTimeout ) { bShotAnim = true; Acceleration = vect(0,0,0); SetAnimAction('FireEndMG'); HandleWaitForAnim('FireEndMG'); GoToState(''); }
if( MGFireCounter <= 0 ) { bShotAnim = true; Acceleration = vect(0,0,0); SetAnimAction('FireEndMG'); HandleWaitForAnim('FireEndMG'); GoToState(''); }
// Give some randomness to the patriarch's firing if( Level.TimeSeconds > MGFireDuration ) { if( AmbientSound != MiniGunSpinSound ) { SoundVolume=185; SoundRadius=200; AmbientSound = MiniGunSpinSound; } Sleep(0.5 + FRand() * 0.75); MGFireDuration = Level.TimeSeconds + (0.75 + FRand() * 0.5); } else { if( bFireAtWill ) FireMGShot(); Sleep(0.05); }
}
}
state FireMissile
{
Ignores RangedAttack;
function bool ShouldChargeFromDamage()
{ return false;
}
function BeginState()
{ Acceleration = vect(0,0,0);
}
function AnimEnd( int Channel )
{ local vector Start; local Rotator R;
Start = GetBoneCoords('tip').Origin;
if ( !SavedFireProperties.bInitialized ) { SavedFireProperties.AmmoClass = MyAmmo.Class; SavedFireProperties.ProjectileClass = MyAmmo.ProjectileClass; SavedFireProperties.WarnTargetPct = 0.15; SavedFireProperties.MaxRange = 10000; SavedFireProperties.bTossed = False; SavedFireProperties.bTrySplash = False; SavedFireProperties.bLeadTarget = True; SavedFireProperties.bInstantHit = True; SavedFireProperties.bInitialized = true; }
R = AdjustAim(SavedFireProperties,Start,100); PlaySound(RocketFireSound,SLOT_Interact,2.0,,TransientSoundRadius,,false); Spawn(Class'NiceBossLAWProj',,,Start,R);
bShotAnim = true; Acceleration = vect(0,0,0); SetAnimAction('FireEndMissile'); HandleWaitForAnim('FireEndMissile');
// Randomly send out a message about Patriarch shooting a rocket(5% chance) if ( FRand() < 0.05 && Controller.Enemy != none && PlayerController(Controller.Enemy.Controller) != none ) { PlayerController(Controller.Enemy.Controller).Speech('AUTO', 10, ""); }
GoToState('');
}
Begin:
while ( true )
{ Acceleration = vect(0,0,0); Sleep(0.1);
}
}
function bool MeleeDamageTarget(int hitdamage, vector pushdir)
{
if( Controller.Target!=none && Controller.Target.IsA('NetKActor') ) pushdir = Normal(Controller.Target.Location-Location)*100000; // Fly bitch!
// Used to set MeleeRange = Default.MeleeRange; in Balance Round 1, fixed in Balance Round 2
return Super.MeleeDamageTarget(hitdamage, pushdir);
}
state Charging
{
// Don't override speed in this state
function bool CanSpeedAdjust()
{ return false;
}
function bool ShouldChargeFromDamage()
{ return false;
}
function BeginState()
{ bChargingPlayer = True; if( Level.NetMode!=NM_DedicatedServer ) PostNetReceive();
// How many charge attacks we can do randomly 1-3 NumChargeAttacks = Rand(2) + 1;
}
function EndState()
{ SetGroundSpeed(GetOriginalGroundSpeed()); bChargingPlayer = False; ChargeDamage = 0; if( Level.NetMode!=NM_DedicatedServer ) PostNetReceive();
LastChargeTime = Level.TimeSeconds;
}
function Tick( float Delta )
{
if( NumChargeAttacks <= 0 ) { GoToState(''); }
// Keep the flesh pound moving toward its target when attacking if( Role == ROLE_Authority && bShotAnim) { if( bChargingPlayer ) { bChargingPlayer = false; if( Level.NetMode!=NM_DedicatedServer ) PostNetReceive(); } SetGroundSpeed(OriginalGroundSpeed * 1.25); if( LookTarget!=none ) { Acceleration = AccelRate * Normal(LookTarget.Location - Location); } } else { if( !bChargingPlayer ) { bChargingPlayer = true; if( Level.NetMode!=NM_DedicatedServer ) PostNetReceive(); }
// Zapping slows him down, but doesn't stop him if( bZapped ) { SetGroundSpeed(OriginalGroundSpeed * 1.5); } else { SetGroundSpeed(OriginalGroundSpeed * 2.5); } }
Global.Tick(Delta);
}
function bool MeleeDamageTarget(int hitdamage, vector pushdir)
{ local bool RetVal;
NumChargeAttacks--;
RetVal = Global.MeleeDamageTarget(hitdamage, pushdir*1.5); if( RetVal ) GoToState(''); return RetVal;
}
function RangedAttack(Actor A)
{ if( VSize(A.Location-Location)>700 && Level.TimeSeconds - LastForceChargeTime > 3.0 ) GoToState(''); Global.RangedAttack(A);
}
Begin:
Sleep(6);
GoToState('');
}
function BeginHealing()
{
MonsterController(Controller).WhatToDoNext(55);
}
state Healing // Healing
{
function bool ShouldChargeFromDamage()
{ return false;
}
Begin:
Sleep(GetAnimDuration('Heal'));
GoToState('');
}
state KnockDown // Knocked
{
function bool ShouldChargeFromDamage()
{ return false;
}
Begin:
if( Health > 0 )
{ Sleep(GetAnimDuration('KnockDown')); CloakBoss(); PlaySound(sound'KF_EnemiesFinalSnd.Patriarch.Kev_SaveMe', SLOT_Misc, 2.0,,500.0); if( KFGameType(Level.Game).FinalSquadNum == SyringeCount ) { KFGameType(Level.Game).AddBossBuddySquad(); } GotoState('Escaping');
}
else
{ GotoState('');
}
}
State Escaping extends Charging // Got hurt and running away...
{
function BeginHealing()
{ bShotAnim = true; Acceleration = vect(0,0,0); SetAnimAction('Heal'); HandleWaitForAnim('Heal');
GoToState('Healing');
}
function RangedAttack(Actor A)
{ if ( bShotAnim ) return; else if ( IsCloseEnuf(A) ) { if( bCloaked ) UnCloakBoss(); bShotAnim = true; Acceleration = vect(0,0,0); Acceleration = (A.Location-Location); SetAnimAction('MeleeClaw'); //PlaySound(sound'Claw2s', SLOT_none); Claw2s }
}
function bool MeleeDamageTarget(int hitdamage, vector pushdir)
{ return Global.MeleeDamageTarget(hitdamage, pushdir*1.5);
}
function Tick( float Delta )
{
// Keep the flesh pound moving toward its target when attacking if( Role == ROLE_Authority && bShotAnim) { if( bChargingPlayer ) { bChargingPlayer = false; if( Level.NetMode!=NM_DedicatedServer ) PostNetReceive(); } SetGroundSpeed(GetOriginalGroundSpeed()); } else { if( !bChargingPlayer ) { bChargingPlayer = true; if( Level.NetMode!=NM_DedicatedServer ) PostNetReceive(); }
// Zapping slows him down, but doesn't stop him if( bZapped ) { SetGroundSpeed(OriginalGroundSpeed * 1.5); } else { SetGroundSpeed(OriginalGroundSpeed * 2.5); } }
Global.Tick(Delta);
}
function EndState()
{ SetGroundSpeed(GetOriginalGroundSpeed()); bChargingPlayer = False; if( Level.NetMode!=NM_DedicatedServer ) PostNetReceive(); if( bCloaked ) UnCloakBoss();
}
Begin:
While( true )
{ Sleep(0.5); if( !bCloaked && !bShotAnim ) CloakBoss(); if( !Controller.IsInState('SyrRetreat') && !Controller.IsInState('WaitForAnim')) Controller.GoToState('SyrRetreat');
}
}
State SneakAround extends Escaping // Attempt to sneak around.
{
function BeginHealing()
{ MonsterController(Controller).WhatToDoNext(56); GoToState('');
}
function bool MeleeDamageTarget(int hitdamage, vector pushdir)
{ local bool RetVal;
RetVal = super.MeleeDamageTarget(hitdamage, pushdir);
GoToState(''); return RetVal;
}
function BeginState()
{ super.BeginState(); SneakStartTime = Level.TimeSeconds;
}
function EndState()
{ super.EndState(); LastSneakedTime = Level.TimeSeconds;
}
Begin:
CloakBoss();
While( true )
{ Sleep(0.5);
if( Level.TimeSeconds - SneakStartTime > 10.0 ) { GoToState(''); }
if( !bCloaked && !bShotAnim ) CloakBoss(); if( !Controller.IsInState('ZombieHunt') && !Controller.IsInState('WaitForAnim') ) { Controller.GoToState('ZombieHunt'); }
}
}
State InitialSneak extends SneakAround // Sneak attack the players straight off the bat.
{
Begin:
CloakBoss();
While( true )
{ Sleep(0.5); SneakCount++;
// Added sneakcount hack to try and fix the endless loop crash. Try and track down what was causing this later - Ramm if( SneakCount > 1000 || (Controller != none && NiceZombieBossController(Controller).bAlreadyFoundEnemy) ) { GoToState(''); }
if( !bCloaked && !bShotAnim ) CloakBoss(); if( !Controller.IsInState('InitialHunting') && !Controller.IsInState('WaitForAnim') ) { Controller.GoToState('InitialHunting'); }
}
}
simulated function DropNeedle()
{
if( CurrentNeedle!=none )
{ DetachFromBone(CurrentNeedle); CurrentNeedle.SetLocation(GetBoneCoords('Rpalm_MedAttachment').Origin); CurrentNeedle.DroppedNow(); CurrentNeedle = none;
}
}
simulated function NotifySyringeA()
{
//log("Heal Part 1");
if( Level.NetMode!=NM_Client )
{ if( SyringeCount<3 ) SyringeCount++; if( Level.NetMode!=NM_DedicatedServer ) PostNetReceive();
}
if( Level.NetMode!=NM_DedicatedServer )
{ DropNeedle(); CurrentNeedle = Spawn(Class'NiceBossHPNeedle'); AttachToBone(CurrentNeedle,'Rpalm_MedAttachment');
}
}
function NotifySyringeB()
{
//log("Heal Part 2");
if( Level.NetMode != NM_Client )
{ if(bOnFire) Health += HealingAmount / 2; else Health += HealingAmount; RemoveFlamingEffects(); StopBurnFX(); bOnFire = false; bHealed = true; FlameFuel = InitFlameFuel;
}
}
simulated function NotifySyringeC()
{
//log("Heal Part 3");
if( Level.NetMode!=NM_DedicatedServer && CurrentNeedle!=none )
{ CurrentNeedle.Velocity = vect(-45,300,-90) >> Rotation; DropNeedle();
}
}
simulated function PostNetReceive()
{
if( bClientMiniGunning != bMinigunning )
{ bClientMiniGunning = bMinigunning; // Hack so Patriarch won't go out of MG Firing to play his idle anim online if( bMinigunning ) { IdleHeavyAnim='FireMG'; IdleRifleAnim='FireMG'; IdleCrouchAnim='FireMG'; IdleWeaponAnim='FireMG'; IdleRestAnim='FireMG'; } else { IdleHeavyAnim='BossIdle'; IdleRifleAnim='BossIdle'; IdleCrouchAnim='BossIdle'; IdleWeaponAnim='BossIdle'; IdleRestAnim='BossIdle'; }
}
if( bClientCharg!=bChargingPlayer )
{ bClientCharg = bChargingPlayer; if (bChargingPlayer) { MovementAnims[0] = ChargingAnim; MovementAnims[1] = ChargingAnim; MovementAnims[2] = ChargingAnim; MovementAnims[3] = ChargingAnim; } else if( !bChargingPlayer ) { MovementAnims[0] = default.MovementAnims[0]; MovementAnims[1] = default.MovementAnims[1]; MovementAnims[2] = default.MovementAnims[2]; MovementAnims[3] = default.MovementAnims[3]; }
}
else if( ClientSyrCount!=SyringeCount )
{ ClientSyrCount = SyringeCount; Switch( SyringeCount ) { Case 1: SetBoneScale(3,0,'Syrange1'); Break; Case 2: SetBoneScale(3,0,'Syrange1'); SetBoneScale(4,0,'Syrange2'); Break; Case 3: SetBoneScale(3,0,'Syrange1'); SetBoneScale(4,0,'Syrange2'); SetBoneScale(5,0,'Syrange3'); Break; Default: // WTF? reset...? SetBoneScale(3,1,'Syrange1'); SetBoneScale(4,1,'Syrange2'); SetBoneScale(5,1,'Syrange3'); Break; }
}
else if( TraceHitPos!=vect(0,0,0) )
{ AddTraceHitFX(TraceHitPos); TraceHitPos = vect(0,0,0);
}
else if( bClientCloaked!=bCloaked )
{ bClientCloaked = bCloaked; bCloaked = !bCloaked; if( bCloaked ) UnCloakBoss(); else CloakBoss(); bCloaked = bClientCloaked;
}
}
simulated function int DoAnimAction( name AnimName )
{
if( AnimName=='MeleeImpale' || AnimName=='MeleeClaw' || AnimName=='transition' /*|| AnimName=='FireMG'*/ )
{ AnimBlendParams(1, 1.0, 0.0,, SpineBone1); PlayAnim(AnimName,, 0.1, 1); Return 1;
}
else if( AnimName=='RadialAttack' )
{ // Get rid of blending, this is a full body anim AnimBlendParams(1, 0.0); PlayAnim(AnimName,,0.1); return 0;
}
Return Super.DoAnimAction(AnimName);
}
simulated event SetAnimAction(name NewAction)
{
local int meleeAnimIndex;
if( NewAction=='' ) Return;
if(NewAction == 'Claw')
{ meleeAnimIndex = Rand(3); NewAction = meleeAnims[meleeAnimIndex];
}
ExpectingChannel = DoAnimAction(NewAction);
if( Controller != none )
{ NiceZombieBossController(Controller).AnimWaitChannel = ExpectingChannel;
}
if( AnimNeedsWait(NewAction) )
{ bWaitForAnim = true;
}
else
{ bWaitForAnim = false;
}
if( Level.NetMode!=NM_Client )
{ AnimAction = NewAction; bResetAnimAct = True;
ResetAnimActTime = Level.TimeSeconds+0.3;
}
}
// Hand sending the controller to the WaitForAnim state
simulated function HandleWaitForAnim( name NewAnim )
{
local float RageAnimDur;
Controller.GoToState('WaitForAnim');
RageAnimDur = GetAnimDuration(NewAnim);
NiceZombieBossController(Controller).SetWaitForAnimTimout(RageAnimDur,NewAnim);
}
// The animation is full body and should set the bWaitForAnim flag
simulated function bool AnimNeedsWait(name TestAnim)
{
if( /*TestAnim == 'MeleeImpale' || TestAnim =='MeleeClaw' || TestAnim =='transition' ||*/ TestAnim == 'FireMG' || TestAnim == 'PreFireMG' || TestAnim == 'PreFireMissile' || TestAnim == 'FireEndMG'|| TestAnim == 'FireEndMissile' || TestAnim == 'Heal' || TestAnim == 'KnockDown' || TestAnim == 'Entrance' || TestAnim == 'VictoryLaugh' || TestAnim == 'RadialAttack' )
{ return true;
}
return false;
}
simulated function HandleBumpGlass()
{
}
function bool FlipOver()
{
Return False;
}
// Return true if we want to charge from taking too much damage
function bool ShouldChargeFromDamage()
{
// If we don;t want to heal, charge whoever damaged us!!!
if( (SyringeCount==0 && Health<HealingLevels[0]) || (SyringeCount==1 && Health<HealingLevels[1]) || (SyringeCount==2 && Health<HealingLevels[2]) )
{ return false;
}
else if( !bChargingPlayer && Level.TimeSeconds - LastForceChargeTime > (5.0 + 5.0 * FRand()) )
{ return true;
}
return false;
}
function TakeDamageClient(int Damage, Pawn InstigatedBy, Vector Hitlocation, Vector Momentum, class<NiceWeaponDamageType> damageType, float headshotLevel, float lockonTime){
local float DamagerDistSq;
//local float UsedPipeBombDamScale;
local KFHumanPawn P;
local int NumPlayersSurrounding;
local bool bDidRadialAttack;
if( Level.TimeSeconds - LastMeleeExploitCheckTime > 1.0 && (class<NiceDamageTypeVetBerserker>(damageType) != none) )
{ LastMeleeExploitCheckTime = Level.TimeSeconds; NumLumberJacks = 0; NumNinjas = 0;
foreach DynamicActors(class'KFHumanPawn', P) { // look for guys attacking us within 3 meters if ( VSize(P.Location - Location) < 150 ) { NumPlayersSurrounding++;
if( P != none && P.Weapon != none ) { if( Axe(P.Weapon) != none || Chainsaw(P.Weapon) != none ) { NumLumberJacks++; } else if( Katana(P.Weapon) != none ) { NumNinjas++; } }
if( !bDidRadialAttack && NumPlayersSurrounding >= 3 ) { bDidRadialAttack = true; GotoState('RadialAttack'); break; } } }
}
// Scale damage from the pipebomb down a bit if lots of pipe bomb damage happens
// at around the same times. Prevent players from putting all thier pipe bombs
// in one place and owning the patriarch in one blow.
/*if ( class<NiceDamTypePipeBomb>(damageType) != none )
{ UsedPipeBombDamScale = FMax(0,(1.0 - PipeBombDamageScale));
PipeBombDamageScale += 0.075;
if( PipeBombDamageScale > 1.0 ) { PipeBombDamageScale = 1.0; }
Damage *= UsedPipeBombDamScale;
}*/
Super.TakeDamageClient(Damage,instigatedBy,hitlocation,Momentum,damageType,headshotLevel,lockonTime);
if( Level.TimeSeconds - LastDamageTime > 10 )
{ ChargeDamage = 0;
}
else
{ LastDamageTime = Level.TimeSeconds; ChargeDamage += Damage;
}
if( ShouldChargeFromDamage() && ChargeDamage > 200 )
{ // If someone close up is shooting us, just charge them if( InstigatedBy != none ) { DamagerDistSq = VSizeSquared(Location - InstigatedBy.Location);
if( DamagerDistSq < (700 * 700) ) { SetAnimAction('transition'); ChargeDamage=0; LastForceChargeTime = Level.TimeSeconds; GoToState('Charging'); return; } }
}
if( Health<=0 || SyringeCount==3 || IsInState('Escaping') || IsInState('KnockDown') || IsInState('RadialAttack') || bDidRadialAttack/*|| bShotAnim*/ ) Return;
if( (SyringeCount==0 && Health<HealingLevels[0]) || (SyringeCount==1 && Health<HealingLevels[1]) || (SyringeCount==2 && Health<HealingLevels[2]) )
{ //log(GetStateName()$" Took damage and want to heal!!! Health="$Health$" HealingLevels "$HealingLevels[SyringeCount]);
bShotAnim = true; Acceleration = vect(0,0,0); SetAnimAction('KnockDown'); HandleWaitForAnim('KnockDown'); KFMonsterController(Controller).bUseFreezeHack = True; GoToState('KnockDown');
}
}
function DoorAttack(Actor A)
{
if ( bShotAnim ) return;
else if ( A!=none )
{ Controller.Target = A; bShotAnim = true; Acceleration = vect(0,0,0); SetAnimAction('PreFireMissile'); HandleWaitForAnim('PreFireMissile'); GoToState('FireMissile');
}
}
function RemoveHead();
function PlayDirectionalHit(Vector HitLoc);
function bool SameSpeciesAs(Pawn P)
{
return False;
}
// Creapy endgame camera when the evil wins.
function bool SetBossLaught()
{
local Controller C;
GoToState('');
bShotAnim = true;
Acceleration = vect(0,0,0);
SetAnimAction('VictoryLaugh');
HandleWaitForAnim('VictoryLaugh');
bIsBossView = True;
bSpecialCalcView = True;
For( C=Level.ControllerList; C!=none; C=C.NextController )
{ if( PlayerController(C)!=none ) { PlayerController(C).SetViewTarget(Self); PlayerController(C).ClientSetViewTarget(Self); PlayerController(C).ClientSetBehindView(True); }
}
Return True;
}
simulated function bool SpectatorSpecialCalcView(PlayerController Viewer, out Actor ViewActor, out vector CameraLocation, out rotator CameraRotation)
{
Viewer.bBehindView = True;
ViewActor = Self;
CameraRotation.Yaw = Rotation.Yaw-32768;
CameraRotation.Pitch = 0;
CameraRotation.Roll = Rotation.Roll;
CameraLocation = Location + (vect(80,0,80) >> Rotation);
Return True;
}
// Overridden to do a cool slomo death view of the patriarch dying
function Died(Controller Killer, class<DamageType> damageType, vector HitLocation)
{
local Controller C;
super.Died(Killer,damageType,HitLocation);
KFGameType(Level.Game).DoBossDeath();
For( C=Level.ControllerList; C!=none; C=C.NextController )
{ if( PlayerController(C)!=none ) { PlayerController(C).SetViewTarget(Self); PlayerController(C).ClientSetViewTarget(Self); PlayerController(C).bBehindView = true; PlayerController(C).ClientSetBehindView(True); }
}
}
function ClawDamageTarget()
{
local vector PushDir;
local name Anim;
local float frame,rate;
local float UsedMeleeDamage;
local bool bDamagedSomeone;
local KFHumanPawn P;
local Actor OldTarget;
if( MeleeDamage > 1 )
{ UsedMeleeDamage = (MeleeDamage - (MeleeDamage * 0.05)) + (MeleeDamage * (FRand() * 0.1));
}
else
{ UsedMeleeDamage = MeleeDamage;
}
GetAnimParams(1, Anim,frame,rate);
if( Anim == 'MeleeImpale' )
{ MeleeRange = ImpaleMeleeDamageRange;
}
else
{ MeleeRange = ClawMeleeDamageRange;
}
if(Controller!=none && Controller.Target!=none) PushDir = (damageForce * Normal(Controller.Target.Location - Location));
else PushDir = damageForce * vector(Rotation);
// Begin Balance Round 1(damages everyone in Round 2 and added seperate code path for MeleeImpale in Round 3)
if( Anim == 'MeleeImpale' )
{ bDamagedSomeone = MeleeDamageTarget(UsedMeleeDamage, PushDir);
}
else
{ OldTarget = Controller.Target;
foreach DynamicActors(class'KFHumanPawn', P) { if ( (P.Location - Location) dot PushDir > 0.0 ) // Added dot Product check in Balance Round 3 { Controller.Target = P; bDamagedSomeone = bDamagedSomeone || MeleeDamageTarget(UsedMeleeDamage, damageForce * Normal(P.Location - Location)); // Always pushing players away added in Balance Round 3 } }
Controller.Target = OldTarget;
}
MeleeRange = Default.MeleeRange;
// End Balance Round 1, 2, and 3
if ( bDamagedSomeone )
{ if( Anim == 'MeleeImpale' ) { PlaySound(MeleeImpaleHitSound, SLOT_Interact, 2.0); } else { PlaySound(MeleeAttackHitSound, SLOT_Interact, 2.0); }
}
}
simulated function ProcessHitFX()
{
local Coords boneCoords;
local class<xEmitter> HitEffects[4];
local int i,j;
local float GibPerterbation;
if( (Level.NetMode == NM_DedicatedServer) || bSkeletized || (Mesh == SkeletonMesh))
{ SimHitFxTicker = HitFxTicker; return;
}
for ( SimHitFxTicker = SimHitFxTicker; SimHitFxTicker != HitFxTicker; SimHitFxTicker = (SimHitFxTicker + 1) % ArrayCount(HitFX) )
{ j++; if ( j > 30 ) { SimHitFxTicker = HitFxTicker; return; }
if( (HitFX[SimHitFxTicker].damtype == none) || (Level.bDropDetail && (Level.TimeSeconds - LastRenderTime > 3) && !IsHumanControlled()) ) continue;
//log("Processing effects for damtype "$HitFX[SimHitFxTicker].damtype);
if( HitFX[SimHitFxTicker].bone == 'obliterate' && !class'GameInfo'.static.UseLowGore()) { SpawnGibs( HitFX[SimHitFxTicker].rotDir, 1); bGibbed = true; // Wait a tick on a listen server so the obliteration can replicate before the pawn is destroyed if( Level.NetMode == NM_ListenServer ) { bDestroyNextTick = true; TimeSetDestroyNextTickTime = Level.TimeSeconds; } else { Destroy(); } return; }
boneCoords = GetBoneCoords( HitFX[SimHitFxTicker].bone );
if ( !Level.bDropDetail && !class'GameInfo'.static.NoBlood() && !bSkeletized && !class'GameInfo'.static.UseLowGore() ) { //AttachEmitterEffect( BleedingEmitterClass, HitFX[SimHitFxTicker].bone, boneCoords.Origin, HitFX[SimHitFxTicker].rotDir );
HitFX[SimHitFxTicker].damtype.static.GetHitEffects( HitEffects, Health );
if( !PhysicsVolume.bWaterVolume ) // don't attach effects under water { for( i = 0; i < ArrayCount(HitEffects); i++ ) { if( HitEffects[i] == none ) continue;
AttachEffect( HitEffects[i], HitFX[SimHitFxTicker].bone, boneCoords.Origin, HitFX[SimHitFxTicker].rotDir ); } } } if ( class'GameInfo'.static.UseLowGore() ) HitFX[SimHitFxTicker].bSever = false;
if( HitFX[SimHitFxTicker].bSever ) { GibPerterbation = HitFX[SimHitFxTicker].damtype.default.GibPerterbation;
switch( HitFX[SimHitFxTicker].bone ) { case 'obliterate': break;
case LeftThighBone: if( !bLeftLegGibbed ) { SpawnSeveredGiblet( DetachedLegClass, boneCoords.Origin, HitFX[SimHitFxTicker].rotDir, GibPerterbation, GetBoneRotation(HitFX[SimHitFxTicker].bone) ); KFSpawnGiblet( class 'KFMod.KFGibBrain',boneCoords.Origin, HitFX[SimHitFxTicker].rotDir, GibPerterbation, 250 ) ; KFSpawnGiblet( class 'KFMod.KFGibBrainb',boneCoords.Origin, HitFX[SimHitFxTicker].rotDir, GibPerterbation, 250 ) ; KFSpawnGiblet( class 'KFMod.KFGibBrain',boneCoords.Origin, HitFX[SimHitFxTicker].rotDir, GibPerterbation, 250 ) ; bLeftLegGibbed=true; } break;
case RightThighBone: if( !bRightLegGibbed ) { SpawnSeveredGiblet( DetachedLegClass, boneCoords.Origin, HitFX[SimHitFxTicker].rotDir, GibPerterbation, GetBoneRotation(HitFX[SimHitFxTicker].bone) ); KFSpawnGiblet( class 'KFMod.KFGibBrain',boneCoords.Origin, HitFX[SimHitFxTicker].rotDir, GibPerterbation, 250 ) ; KFSpawnGiblet( class 'KFMod.KFGibBrainb',boneCoords.Origin, HitFX[SimHitFxTicker].rotDir, GibPerterbation, 250 ) ; KFSpawnGiblet( class 'KFMod.KFGibBrain',boneCoords.Origin, HitFX[SimHitFxTicker].rotDir, GibPerterbation, 250 ) ; bRightLegGibbed=true; } break;
case LeftFArmBone: if( !bLeftArmGibbed ) { SpawnSeveredGiblet( DetachedSpecialArmClass, boneCoords.Origin, HitFX[SimHitFxTicker].rotDir, GibPerterbation, GetBoneRotation(HitFX[SimHitFxTicker].bone) ); KFSpawnGiblet( class 'KFMod.KFGibBrain',boneCoords.Origin, HitFX[SimHitFxTicker].rotDir, GibPerterbation, 250 ) ; KFSpawnGiblet( class 'KFMod.KFGibBrainb',boneCoords.Origin, HitFX[SimHitFxTicker].rotDir, GibPerterbation, 250 ) ;; bLeftArmGibbed=true; } break;
case RightFArmBone: if( !bRightArmGibbed ) { SpawnSeveredGiblet( DetachedArmClass, boneCoords.Origin, HitFX[SimHitFxTicker].rotDir, GibPerterbation, GetBoneRotation(HitFX[SimHitFxTicker].bone) ); KFSpawnGiblet( class 'KFMod.KFGibBrain',boneCoords.Origin, HitFX[SimHitFxTicker].rotDir, GibPerterbation, 250 ) ; KFSpawnGiblet( class 'KFMod.KFGibBrainb',boneCoords.Origin, HitFX[SimHitFxTicker].rotDir, GibPerterbation, 250 ) ; bRightArmGibbed=true; } break;
case 'head': if( !bHeadGibbed ) { if ( HitFX[SimHitFxTicker].damtype == class'DamTypeDecapitation' ) { DecapFX( boneCoords.Origin, HitFX[SimHitFxTicker].rotDir, false); } else if( HitFX[SimHitFxTicker].damtype == class'DamTypeProjectileDecap' ) { DecapFX( boneCoords.Origin, HitFX[SimHitFxTicker].rotDir, false, true); } else if( HitFX[SimHitFxTicker].damtype == class'DamTypeMeleeDecapitation' ) { DecapFX( boneCoords.Origin, HitFX[SimHitFxTicker].rotDir, true); }
bHeadGibbed=true; } break; }
if( HitFX[SimHitFXTicker].bone != 'Spine' && HitFX[SimHitFXTicker].bone != FireRootBone && HitFX[SimHitFXTicker].bone != 'head' && Health <=0 ) HideBone(HitFX[SimHitFxTicker].bone); }
}
}
static simulated function PreCacheMaterials(LevelInfo myLevel)
{//should be derived and used.
/*
myLevel.AddPrecacheMaterial(Combiner'KF_Specimens_Trip_T.gatling_cmb');
myLevel.AddPrecacheMaterial(Combiner'KF_Specimens_Trip_T.gatling_env_cmb');
myLevel.AddPrecacheMaterial(Texture'KF_Specimens_Trip_T.gatling_D');
myLevel.AddPrecacheMaterial(Combiner'KF_Specimens_Trip_T.PatGungoInvisible_cmb');
myLevel.AddPrecacheMaterial(Combiner'KF_Specimens_Trip_T.patriarch_cmb');
myLevel.AddPrecacheMaterial(Combiner'KF_Specimens_Trip_T.patriarch_env_cmb');
myLevel.AddPrecacheMaterial(Texture'KF_Specimens_Trip_T.patriarch_D');
myLevel.AddPrecacheMaterial(Material'KF_Specimens_Trip_T.patriarch_invisible');
myLevel.AddPrecacheMaterial(Material'KF_Specimens_Trip_T.patriarch_invisible_gun');
myLevel.AddPrecacheMaterial(Material'KF_Specimens_Trip_T.patriarch_fizzle_FB');
myLevel.AddPrecacheMaterial(Texture'kf_fx_trip_t.Gore.Patriarch_Gore_Limbs_Diff');
myLevel.AddPrecacheMaterial(Texture'kf_fx_trip_t.Gore.Patriarch_Gore_Limbs_Spec');
*/
}
defaultproperties
{ RocketFireSound=SoundGroup'KF_EnemiesFinalSnd.Patriarch.Kev_FireRocket' MiniGunFireSound=Sound'KF_BasePatriarch.Attack.Kev_MG_GunfireLoop' MiniGunSpinSound=Sound'KF_BasePatriarch.Attack.Kev_MG_TurbineFireLoop' MeleeImpaleHitSound=SoundGroup'KF_EnemiesFinalSnd.Patriarch.Kev_HitPlayer_Impale' MoanVoice=SoundGroup'KF_EnemiesFinalSnd.Patriarch.Kev_Talk' MeleeAttackHitSound=SoundGroup'KF_EnemiesFinalSnd.Patriarch.Kev_HitPlayer_Fist' JumpSound=SoundGroup'KF_EnemiesFinalSnd.Patriarch.Kev_Jump' DetachedArmClass=Class'KFChar.SeveredArmPatriarch' DetachedLegClass=Class'KFChar.SeveredLegPatriarch' DetachedHeadClass=Class'KFChar.SeveredHeadPatriarch' DetachedSpecialArmClass=Class'KFChar.SeveredRocketArmPatriarch' HitSound(0)=SoundGroup'KF_EnemiesFinalSnd.Patriarch.Kev_Pain' DeathSound(0)=SoundGroup'KF_EnemiesFinalSnd.Patriarch.Kev_Death' ControllerClass=Class'NicePack.NiceZombieBossController' AmbientSound=Sound'KF_BasePatriarch.Idle.Kev_IdleLoop' Mesh=SkeletalMesh'KF_Freaks_Trip.Patriarch_Freak' Skins(0)=Combiner'KF_Specimens_Trip_T.gatling_cmb' Skins(1)=Combiner'KF_Specimens_Trip_T.patriarch_cmb'
}

View file

@ -0,0 +1,45 @@
class NiceZombieBossBase extends NiceMonster
abstract;
#exec OBJ LOAD FILE=KFPatch2.utx
#exec OBJ LOAD FILE=KF_Specimens_Trip_T.utx
var bool bChargingPlayer,bClientCharg,bFireAtWill,bMinigunning,bIsBossView;
var float RageStartTime,LastChainGunTime,LastMissileTime,LastSneakedTime;
var bool bClientMiniGunning;
var name ChargingAnim; // How he runs when charging the player.
var byte SyringeCount,ClientSyrCount;
var int MGFireCounter;
var vector TraceHitPos;
var Emitter mTracer,mMuzzleFlash;
var bool bClientCloaked;
var float LastCheckTimes;
var int HealingLevels[3],HealingAmount;
var(Sounds) sound RocketFireSound; // The sound of the rocket being fired
var(Sounds) sound MiniGunFireSound; // The sound of the minigun being fired
var(Sounds) sound MiniGunSpinSound; // The sound of the minigun spinning
var(Sounds) sound MeleeImpaleHitSound;// The sound of melee impale attack hitting the player
var float MGFireDuration; // How long to fire for this burst
var float MGLostSightTimeout; // When to stop firing because we lost sight of the target
var() float MGDamage; // How much damage the MG will do
var() float ClawMeleeDamageRange;// How long his arms melee strike is
var() float ImpaleMeleeDamageRange;// How long his spike melee strike is
var float LastChargeTime; // Last time the patriarch charged
var float LastForceChargeTime;// Last time patriarch was forced to charge
var int NumChargeAttacks; // Number of attacks this charge
var float ChargeDamage; // How much damage he's taken since the last charge
var float LastDamageTime; // Last Time we took damage
// Sneaking
var float SneakStartTime; // When did we start sneaking
var int SneakCount; // Keep track of the loop that sends the boss to initial hunting state
// PipeBomb damage
var() float PipeBombDamageScale;// Scale the pipe bomb damage over time
replication
{
reliable if( Role==ROLE_Authority ) bChargingPlayer,SyringeCount,TraceHitPos,bMinigunning,bIsBossView;
}
//-------------------------------------------------------------------------------
// NOTE: All Code resides in the child class(this class was only created to
// eliminate hitching caused by loading default properties during play)
//-------------------------------------------------------------------------------
defaultproperties
{ ChargingAnim="RunF" HealingLevels(0)=5600 HealingLevels(1)=3500 HealingLevels(2)=2187 HealingAmount=1750 MGDamage=6.000000 ClawMeleeDamageRange=85.000000 ImpaleMeleeDamageRange=45.000000 fuelRatio=0.400000 bFrugalFuelUsage=False clientHeadshotScale=1.200000 ZapThreshold=5.000000 ZappedDamageMod=1.250000 ZapResistanceScale=1.000000 bHarpoonToHeadStuns=False bHarpoonToBodyStuns=False ZombieFlag=3 MeleeDamage=75 damageForce=170000 bFatAss=True KFRagdollName="Patriarch_Trip" bMeleeStunImmune=True CrispUpThreshhold=1 bCanDistanceAttackDoors=True bUseExtendedCollision=True ColOffset=(Z=65.000000) ColRadius=27.000000 ColHeight=25.000000 SeveredArmAttachScale=1.100000 SeveredLegAttachScale=1.200000 SeveredHeadAttachScale=1.500000 PlayerCountHealthScale=0.750000 BurningWalkFAnims(0)="WalkF" BurningWalkFAnims(1)="WalkF" BurningWalkFAnims(2)="WalkF" BurningWalkAnims(0)="WalkF" BurningWalkAnims(1)="WalkF" BurningWalkAnims(2)="WalkF" OnlineHeadshotOffset=(X=28.000000,Z=75.000000) OnlineHeadshotScale=1.200000 HeadHealth=100000.000000 MotionDetectorThreat=10.000000 bOnlyDamagedByCrossbow=True bBoss=True ScoringValue=500 IdleHeavyAnim="BossIdle" IdleRifleAnim="BossIdle" RagDeathVel=80.000000 RagDeathUpKick=100.000000 MeleeRange=10.000000 GroundSpeed=120.000000 WaterSpeed=120.000000 HealthMax=8000.000000 Health=8000 HeadScale=1.300000 MenuName="Nice Patriarch" MovementAnims(0)="WalkF" MovementAnims(1)="WalkF" MovementAnims(2)="WalkF" MovementAnims(3)="WalkF" AirAnims(0)="JumpInAir" AirAnims(1)="JumpInAir" AirAnims(2)="JumpInAir" AirAnims(3)="JumpInAir" TakeoffAnims(0)="JumpTakeOff" TakeoffAnims(1)="JumpTakeOff" TakeoffAnims(2)="JumpTakeOff" TakeoffAnims(3)="JumpTakeOff" LandAnims(0)="JumpLanded" LandAnims(1)="JumpLanded" LandAnims(2)="JumpLanded" LandAnims(3)="JumpLanded" AirStillAnim="JumpInAir" TakeoffStillAnim="JumpTakeOff" IdleCrouchAnim="BossIdle" IdleWeaponAnim="BossIdle" IdleRestAnim="BossIdle" DrawScale=1.050000 PrePivot=(Z=3.000000) SoundVolume=75 bNetNotify=False Mass=1000.000000 RotationRate=(Yaw=36000,Roll=0)
}

View file

@ -0,0 +1,190 @@
//-----------------------------------------------------------
//
//-----------------------------------------------------------
class NiceZombieBossController extends KFMonsterController;
var NavigationPoint HidingSpots;
var float WaitAnimTimeout; // How long until the Anim we are waiting for is completed; Hack so the server doesn't get stuck in idle when its doing the Rage anim
var int AnimWaitChannel; // The channel we are waiting to end in WaitForAnim
var name AnimWaitingFor; // The animation we are waiting to end in WaitForAnim, mostly used for debugging
var bool bAlreadyFoundEnemy; // The Boss has already found an enemy at least once
function bool CanKillMeYet()
{
return false;
}
function TimedFireWeaponAtEnemy()
{
if ( (Enemy == none) || FireWeaponAt(Enemy) ) SetCombatTimer();
else SetTimer(0.01, True);
}
// Overridden to support a quick initial attack to get the boss to the players quickly
function FightEnemy(bool bCanCharge)
{
if( KFM.bShotAnim )
{ GoToState('WaitForAnim'); Return;
}
if (KFM.MeleeRange != KFM.default.MeleeRange) KFM.MeleeRange = KFM.default.MeleeRange;
if ( Enemy == none || Enemy.Health <= 0 ) FindNewEnemy();
if ( (Enemy == FailedHuntEnemy) && (Level.TimeSeconds == FailedHuntTime) )
{
// if ( Enemy.Controller.bIsPlayer ) // FindNewEnemy();
if ( Enemy == FailedHuntEnemy ) { GoalString = "FAILED HUNT - HANG OUT"; if ( EnemyVisible() ) bCanCharge = false; }
}
if ( !EnemyVisible() )
{ // Added sneakcount hack to try and fix the endless loop crash. Try and track down what was causing this later - Ramm if( bAlreadyFoundEnemy || NiceZombieBoss(Pawn).SneakCount > 2 ) { bAlreadyFoundEnemy = true; GoalString = "Hunt"; GotoState('ZombieHunt'); } else { // Added sneakcount hack to try and fix the endless loop crash. Try and track down what was causing this later - Ramm NiceZombieBoss(Pawn).SneakCount++; GoalString = "InitialHunt"; GotoState('InitialHunting'); } return;
}
// see enemy - decide whether to charge it or strafe around/stand and fire
Target = Enemy;
GoalString = "Charge";
PathFindState = 2;
DoCharge();
}
// Get the boss to the players quickly after initial spawn
state InitialHunting extends Hunting
{
event SeePlayer(Pawn SeenPlayer)
{ super.SeePlayer(SeenPlayer); bAlreadyFoundEnemy = true; GoalString = "Hunt"; GotoState('ZombieHunt');
}
function BeginState()
{ local float ZDif;
// Added sneakcount hack to try and fix the endless loop crash. Try and track down what was causing this later - Ramm NiceZombieBoss(Pawn).SneakCount++;
if( Pawn.CollisionRadius>27 || Pawn.CollisionHeight>46 ) { ZDif = Pawn.CollisionHeight-44; Pawn.SetCollisionSize(24,44); Pawn.MoveSmooth(vect(0,0,-1)*ZDif); }
super.BeginState();
}
function EndState()
{ local float ZDif;
if( Pawn.CollisionRadius!=Pawn.Default.CollisionRadius || Pawn.CollisionHeight!=Pawn.Default.CollisionHeight ) { ZDif = Pawn.Default.CollisionRadius-44; Pawn.MoveSmooth(vect(0,0,1)*ZDif); Pawn.SetCollisionSize(Pawn.Default.CollisionRadius,Pawn.Default.CollisionHeight); }
super.EndState();
}
}
state ZombieCharge
{
function bool StrafeFromDamage(float Damage, class<DamageType> DamageType, bool bFindDest)
{ return false;
}
// I suspect this function causes bloats to get confused
function bool TryStrafe(vector sideDir)
{ return false;
}
function Timer()
{ Disable('NotifyBump'); Target = Enemy; TimedFireWeaponAtEnemy();
}
WaitForAnim:
if ( Monster(Pawn).bShotAnim )
{ Goto('Moving');
}
if ( !FindBestPathToward(Enemy, false,true) ) GotoState('ZombieRestFormation');
Moving:
MoveToward(Enemy);
WhatToDoNext(17);
if ( bSoaking ) SoakStop("STUCK IN CHARGING!");
}
state RunSomewhere
{
Ignores HearNoise,DamageAttitudeTo,Tick,EnemyChanged,Startle;
function BeginState()
{ HidingSpots = none; Enemy = none; SetTimer(0.1,True);
}
event SeePlayer(Pawn SeenPlayer)
{ SetEnemy(SeenPlayer);
}
function Timer()
{ if( Enemy==none ) Return; Target = Enemy; KFM.RangedAttack(Target);
}
Begin:
if( Pawn.Physics==PHYS_Falling ) WaitForLanding();
While( KFM.bShotAnim ) Sleep(0.25);
if( HidingSpots==none ) HidingSpots = FindRandomDest();
if( HidingSpots==none ) NiceZombieBoss(Pawn).BeginHealing();
if( ActorReachable(HidingSpots) )
{ MoveTarget = HidingSpots; HidingSpots = none;
}
else FindBestPathToward(HidingSpots,True,False);
if( MoveTarget==none ) NiceZombieBoss(Pawn).BeginHealing();
if( Enemy!=none && VSize(Enemy.Location-Pawn.Location)<100 ) MoveToward(MoveTarget,Enemy,,False);
else MoveToward(MoveTarget,MoveTarget,,False);
if( HidingSpots==none || !PlayerSeesMe() ) NiceZombieBoss(Pawn).BeginHealing();
GoTo'Begin';
}
State SyrRetreat
{
Ignores HearNoise,DamageAttitudeTo,Tick,EnemyChanged,Startle;
function BeginState()
{ HidingSpots = none; Enemy = none; SetTimer(0.1,True);
}
event SeePlayer(Pawn SeenPlayer)
{ SetEnemy(SeenPlayer);
}
function Timer()
{ if( Enemy==none ) Return; Target = Enemy; KFM.RangedAttack(Target);
}
function FindHideSpot()
{ local NavigationPoint N,BN; local float Dist,BDist,MDist; local vector EnemyDir;
if( Enemy==none ) { HidingSpots = FindRandomDest(); Return; } EnemyDir = Normal(Enemy.Location-Pawn.Location); For( N=Level.NavigationPointList; N!=none; N=N.NextNavigationPoint ) { MDist = VSize(N.Location-Pawn.Location); if( MDist<2500 && !FastTrace(N.Location,Enemy.Location) && FindPathToward(N)!=none ) { Dist = VSize(N.Location-Enemy.Location)/FMax(MDist/800.f,1.5); if( (EnemyDir Dot Normal(Enemy.Location-N.Location))<0.2 ) Dist/=10; if( BN==none || BDist<Dist ) { BN = N; BDist = Dist; } } } if( BN==none ) HidingSpots = FindRandomDest(); else HidingSpots = BN;
}
Begin:
if( Pawn.Physics==PHYS_Falling ) WaitForLanding();
While( KFM.bShotAnim ) Sleep(0.25);
if( HidingSpots==none ) FindHideSpot();
if( HidingSpots==none ) NiceZombieBoss(Pawn).BeginHealing();
if( ActorReachable(HidingSpots) )
{ MoveTarget = HidingSpots; HidingSpots = none;
}
else FindBestPathToward(HidingSpots,True,False);
if( MoveTarget==none ) NiceZombieBoss(Pawn).BeginHealing();
if( Enemy!=none && VSize(Enemy.Location-Pawn.Location)<100 ) MoveToward(MoveTarget,Enemy,,False);
else MoveToward(MoveTarget,MoveTarget,,False);
if( HidingSpots==none ) NiceZombieBoss(Pawn).BeginHealing();
GoTo'Begin';
}
function bool PlayerSeesMe()
{
local Controller C;
For( C=Level.ControllerList; C!=none; C=C.NextController )
{ if( C.bIsPlayer && C.Pawn!=none && C.Pawn!=Pawn && LineOfSightTo(C.Pawn) ) Return True;
}
Return False;
}
// Used to set a timeout for the WaitForAnim state. This is a bit of a hack fix
// for the Patriach getting stuck in its idle anim on a dedicated server when it
// is supposed to doing something. For some reason, on a dedicated server only, it
// never gets an animend call for some of the anims, instead the anim gets
// interrupted by the idle anim. If we figure that bug out, we can
// probably take this out in the future. But for now the fix works - Ramm
function SetWaitForAnimTimout(float NewWaitAnimTimeout, name AnimToWaitFor)
{
WaitAnimTimeout = NewWaitAnimTimeout;
AnimWaitingFor = AnimToWaitFor;
}
state WaitForAnim
{
Ignores SeePlayer,HearNoise,Timer,EnemyNotVisible,NotifyBump,Startle;
// The anim has ended, clear the flags and let the AI do its thing
function WaitTimeout()
{ if( bUseFreezeHack ) { if( Pawn!=none ) { Pawn.AccelRate = Pawn.Default.AccelRate; Pawn.GroundSpeed = Pawn.Default.GroundSpeed; } bUseFreezeHack = False; }
AnimEnd(AnimWaitChannel);
}
event AnimEnd(int Channel)
{ /*local name Sequence; local float Frame, Rate;
Pawn.GetAnimParams( KFMonster(Pawn).ExpectingChannel, Sequence, Frame, Rate );
log(GetStateName()$" AnimEnd for Exp Chan "$KFMonster(Pawn).ExpectingChannel$" = "$Sequence$" Channel = "$Channel);
Pawn.GetAnimParams( 0, Sequence, Frame, Rate ); log(GetStateName()$" AnimEnd for Chan 0 = "$Sequence);
Pawn.GetAnimParams( 1, Sequence, Frame, Rate ); log(GetStateName()$" AnimEnd for Chan 1 = "$Sequence);
log(GetStateName()$" AnimEnd bShotAnim = "$Monster(Pawn).bShotAnim); */
Pawn.AnimEnd(Channel); if ( !Monster(Pawn).bShotAnim ) WhatToDoNext(99);
}
function Tick( float Delta )
{ Global.Tick(Delta);
if( WaitAnimTimeout > 0 ) { WaitAnimTimeout -= Delta;
if( WaitAnimTimeout <= 0 ) { WaitAnimTimeout = 0; WaitTimeout(); } }
if( bUseFreezeHack ) { MoveTarget = none; MoveTimer = -1; Pawn.Acceleration = vect(0,0,0); Pawn.GroundSpeed = 1; Pawn.AccelRate = 0; }
}
function EndState()
{ super.EndState();
AnimWaitingFor = '';
}
}
defaultproperties
{
}

View file

@ -0,0 +1,255 @@
class NiceZombieBrute extends NiceZombieBruteBase;
var float BlockMeleeDmgMul; //Multiplier for melee damage taken, when Brute is blocking (no matter where the hit was landed)
var float HeadShotgunDmgMul; //Multiplier for shotgun damage taken into UNBLOCKED head
var float HeadBulletDmgMul; //Multiplier for non-sniper bullet damage taken into UNBLOCKED head
simulated function PostNetBeginPlay()
{
super.PostNetBeginPlay();
EnableChannelNotify(1,1);
EnableChannelNotify(2,1);
AnimBlendParams(1, 1.0, 0.0,, SpineBone1);
StartCharging();
}
function ServerRaiseBlock()
{
bServerBlock = true;
SetAnimAction('BlockLoop');
}
function ServerLowerBlock()
{
local name Sequence;
local float Frame, Rate;
bServerBlock = false;
GetAnimParams(1, Sequence, Frame, Rate);
if (Sequence == 'BlockLoop') AnimStopLooping(1);
}
simulated function PostNetReceive()
{
local name Sequence;
local float Frame, Rate;
if(bClientCharge != bChargingPlayer)
{ bClientCharge = bChargingPlayer; if (bChargingPlayer) { MovementAnims[0] = ChargingAnim; MeleeAnims[0] = 'BruteRageAttack'; MeleeAnims[1] = 'BruteRageAttack'; MeleeAnims[2] = 'BruteRageAttack'; } else { MovementAnims[0] = default.MovementAnims[0]; MeleeAnims[0] = default.MeleeAnims[0]; MeleeAnims[1] = default.MeleeAnims[1]; MeleeAnims[2] = default.MeleeAnims[2]; }
}
if (bClientBlock != bServerBlock)
{ bClientBlock = bServerBlock; if (bClientBlock) SetAnimAction('BlockLoop'); else { GetAnimParams(1, Sequence, Frame, Rate); if (Sequence == 'BlockLoop') AnimStopLooping(1); }
}
}
simulated function Tick(float DeltaTime)
{
super.Tick(DeltaTime);
if (Role == ROLE_Authority)
{ // Lock to target when attacking (except on beginner!) if (bShotAnim && LookTarget != none) Acceleration = AccelRate * Normal(LookTarget.Location - Location); // Block according to rules if (Role == ROLE_Authority && !bServerBlock && !bShotAnim) if (Controller != none && Controller.Target != none) ServerRaiseBlock();
}
}
// Override to always move when attacking
function RangedAttack(Actor A){
if (bShotAnim || Physics == PHYS_Swimming) return;
else if (CanAttack(A))
{ if (bChargingPlayer) SetAnimAction('AoeClaw'); else { if (Rand(BlockHitsLanded) < 1) SetAnimAction('BlockClaw'); else SetAnimAction('Claw'); } bShotAnim = true; return;
}
}
function bool IsHeadShot(vector Loc, vector Ray, float AdditionalScale)
{
local float D;
local float AddScale;
local bool bIsBlocking;
bBlockedHS = false;
if (bServerBlock && !IsTweening(1))
{ bIsBlocking = true; AddScale = AdditionalScale + BlockAddScale;
}
else AddScale = AdditionalScale + 1.0;
if (Super.IsHeadShot(Loc, Ray, AddScale))
{ if (bIsBlocking) { D = vector(Rotation) dot Ray; if (-D > 0.20) { bBlockedHS = true; return false; } else return true; } else return true;
}
else return false;
}
function bool CheckMiniFlinch( int flinchScore, Pawn instigatedBy, Vector hitLocation, Vector momentum, class<NiceWeaponDamageType> damageType, float headshotLevel, KFPlayerReplicationInfo KFPRI){
return false;
}
function TakeDamageClient(int Damage, Pawn InstigatedBy, Vector HitLocation, Vector Momentum, class<NiceWeaponDamageType> damageType, float headshotLevel, float lockonTime){
local float D;
local bool bIsHeadshot;
local bool bIsBlocking;
bBlockedHS = false;
if(bServerBlock && !IsTweening(1)) bIsBlocking = true;
if(headshotLevel > 0.0 && bIsBlocking){ D = vector(Rotation) dot Normal(Momentum); D *= -1; if(D > 0.30) { bBlockedHS = true; headshotLevel = 0.0; }
}
bIsHeadShot = (headshotLevel > 0.0);
// damage, which doen't make headshots, always does full damage to Brute -- PooSH
if (damageType != none && damageType.default.bCheckForHeadShots) { if (!bIsHeadShot && bBlockedHS) { if(damageType != none || damageType.default.bIsProjectile) PlaySound(class'MetalHitEmitter'.default.ImpactSounds[rand(3)],, 128); else if(class<NiceDamageTypeVetBerserker>(damageType) != none) PlaySound(Sound'KF_KnifeSnd.Knife_HitMetal',, 128); if(damageType.default.bDealBurningDamage && !damageType.default.bIsPowerWeapon) Damage *= BlockFireDmgMul; // Fire damage isn't reduced as much, excluding TrenchGun else Damage *= BlockDmgMul; // Greatly reduce damage as we only hit the metal plating } else if(bServerBlock && class<NiceDamageTypeVetBerserker>(damageType) != none) Damage *= BlockMeleeDmgMul; // Give Brute higher melee damage resistance, but apply it only if Brute is blocking else if(bIsHeadShot){ if (damageType.default.bIsPowerWeapon) Damage *= HeadShotgunDmgMul; // Give Brute's head resistance to stotguns }
}
// Record damage over 2-second frames
if (LastDamagedTime < Level.TimeSeconds)
{ TwoSecondDamageTotal = 0; LastDamagedTime = Level.TimeSeconds + 2;
}
TwoSecondDamageTotal += Damage;
// If criteria is met make him rage
if (!bDecapitated && !bChargingPlayer && TwoSecondDamageTotal > RageDamageThreshold) StartCharging();
Super(NiceMonster).TakeDamageClient(Damage, instigatedBy, hitLocation, momentum, damageType, headshotLevel, lockonTime);
if (bDecapitated) Died(InstigatedBy.Controller, damageType, HitLocation);
}
function TakeFireDamage(int Damage, Pawn Instigator)
{
Super.TakeFireDamage(Damage, Instigator);
// Adjust movement speed if not charging
if (!bChargingPlayer)
{ if (bBurnified) GroundSpeed = GetOriginalGroundSpeed() * BurnGroundSpeedMul; else GroundSpeed = GetOriginalGroundSpeed();
}
}
function ClawDamageTarget()
{
local KFHumanPawn HumanTarget;
local float UsedMeleeDamage;
local Actor OldTarget;
local name Sequence;
local float Frame, Rate;
local bool bHitSomeone;
if (MeleeDamage > 1) UsedMeleeDamage = (MeleeDamage - (MeleeDamage * 0.05)) + (MeleeDamage * (FRand() * 0.1));
else UsedMeleeDamage = MeleeDamage; GetAnimParams(1, Sequence, Frame, Rate);
if (Controller != none && Controller.Target != none)
{ if (Sequence == 'BruteRageAttack') { OldTarget = Controller.Target; foreach VisibleCollidingActors(class'KFHumanPawn', HumanTarget, MeleeRange + class'KFHumanPawn'.default.CollisionRadius) { bHitSomeone = ClawDamageSingleTarget(UsedMeleeDamage, HumanTarget); } Controller.Target = OldTarget; if (bHitSomeone) BlockHitsLanded++; } else if (Sequence != 'BruteAttack1' && Sequence != 'BruteAttack2' && Sequence != 'DoorBash') // Block attack { bHitSomeone = ClawDamageSingleTarget(UsedMeleeDamage, Controller.Target); if (bHitSomeone) BlockHitsLanded++; } else bHitSomeone = ClawDamageSingleTarget(UsedMeleeDamage, Controller.Target); if (bHitSomeone) PlaySound(MeleeAttackHitSound, SLOT_Interact, 1.25);
}
}
function bool ClawDamageSingleTarget(float UsedMeleeDamage, Actor ThisTarget)
{
local Pawn HumanTarget;
local KFPlayerController HumanTargetController;
local bool bHitSomeone;
local float EnemyAngle;
local vector PushForceVar;
EnemyAngle = Normal(ThisTarget.Location - Location) dot vector(Rotation);
if (EnemyAngle > 0)
{ Controller.Target = ThisTarget; if (MeleeDamageTarget(UsedMeleeDamage, vect(0, 0, 0))) { HumanTarget = KFHumanPawn(ThisTarget); if (HumanTarget != none) { EnemyAngle = (EnemyAngle * 0.5) + 0.5; // Players at sides get knocked back half as much PushForceVar = (PushForce * Normal(HumanTarget.Location - Location) * EnemyAngle) + PushAdd; if (!bChargingPlayer) PushForceVar *= 0.85; // (!) I'm sure the VeterancyName string is localized but I'm not sure of another way compatible with ServerPerks if (KFPlayerReplicationInfo(HumanTarget.Controller.PlayerReplicationInfo).ClientVeteranSkill != none) if (KFPlayerReplicationInfo(HumanTarget.Controller.PlayerReplicationInfo).ClientVeteranSkill .default.VeterancyName == "Berserker") PushForceVar *= 0.75; if (!(HumanTarget.Physics == PHYS_WALKING || HumanTarget.Physics == PHYS_none)) PushForceVar *= vect(1, 1, 0); // (!) Don't throw upwards if we are not on the ground - adjust for more flexibility
HumanTarget.AddVelocity(PushForceVar);
HumanTargetController = KFPlayerController(HumanTarget.Controller); if (HumanTargetController != none) HumanTargetController.ShakeView(ShakeViewRotMag, ShakeViewRotRate, ShakeViewRotTime, ShakeViewOffsetMag, ShakeViewOffsetRate, ShakeViewOffsetTime); bHitSomeone = true; } }
}
return bHitSomeone;
}
function StartCharging()
{
// How many times should we hit before we cool down?
if (Level.Game.NumPlayers <= 3) MaxRageCounter = 2;
else MaxRageCounter = 3;
RageCounter = MaxRageCounter;
PlaySound(RageSound, SLOT_Talk, 255);
GotoState('RageCharging');
}
state RageCharging
{
Ignores StartCharging;
function bool CanGetOutOfWay()
{ return false;
}
function bool CanSpeedAdjust()
{ return false;
}
function BeginState()
{ bFrustrated = false; bChargingPlayer = true; RageSpeedTween = 0.0; if (Level.NetMode != NM_DedicatedServer) ClientChargingAnims();
NetUpdateTime = Level.TimeSeconds - 1;
}
function EndState()
{ bChargingPlayer = false;
NiceZombieBruteController(Controller).RageFrustrationTimer = 0;
if (Health > 0) { GroundSpeed = GetOriginalGroundSpeed(); if (bBurnified) GroundSpeed *= BurnGroundSpeedMul; }
if( Level.NetMode!=NM_DedicatedServer ) ClientChargingAnims();
NetUpdateTime = Level.TimeSeconds - 1;
}
function Tick(float Delta)
{ if (!bShotAnim) { RageSpeedTween = FClamp(RageSpeedTween + (Delta * 0.75), 0, 1.0); GroundSpeed = OriginalGroundSpeed + ((OriginalGroundSpeed * 0.75 / MaxRageCounter * (RageCounter + 1) * RageSpeedTween)); if (bBurnified) GroundSpeed *= BurnGroundSpeedMul; }
Global.Tick(Delta);
}
function bool MeleeDamageTarget(int HitDamage, vector PushDir)
{ local bool DamDone, bWasEnemy;
bWasEnemy = (Controller.Target == Controller.Enemy);
DamDone = Super.MeleeDamageTarget(HitDamage * RageDamageMul, vect(0, 0, 0)); if(Controller == none) return true;
if (bWasEnemy && DamDone) { //ChangeTarget(); CalmDown(); }
return DamDone;
}
function CalmDown()
{ RageCounter = FClamp(RageCounter - 1, 0, MaxRageCounter); if (RageCounter == 0) GotoState('');
}
function ChangeTarget()
{ local Controller C; local Pawn BestPawn; local float Dist, BestDist; for (C = Level.ControllerList; C != none; C = C.NextController) if (C.Pawn != none && KFHumanPawn(C.Pawn) != none) { Dist = VSize(C.Pawn.Location - Location); if (C.Pawn == Controller.Target) Dist += GroundSpeed * 4; if (BestPawn == none) { BestPawn = C.Pawn; BestDist = Dist; } else if (Dist < BestDist) { BestPawn = C.Pawn; BestDist = Dist; } } if (BestPawn != none && BestPawn != Controller.Enemy) MonsterController(Controller).ChangeEnemy(BestPawn, Controller.CanSee(BestPawn));
}
}
// Override to prevent stunning
function bool FlipOver()
{
return true;
}
// Shouldn't fight with our own
function bool SameSpeciesAs(Pawn P)
{
return (NiceZombieBrute(P) != none);
}
// ------------------------------------------------------
// Animation --------------------------------------------
// ------------------------------------------------------
// Overridden to handle playing upper body only attacks when moving
simulated event SetAnimAction(name NewAction)
{
if (NewAction=='') return;
if (NewAction == 'Claw')
{ NewAction = MeleeAnims[rand(2)];
}
else if (NewAction == 'BlockClaw')
{ NewAction = 'BruteBlockSlam';
}
else if (NewAction == 'AoeClaw')
{ NewAction = 'BruteRageAttack';
}
ExpectingChannel = DoAnimAction(NewAction);
if (AnimNeedsWait(NewAction)) bWaitForAnim = true;
else bWaitForAnim = false;
if (Level.NetMode != NM_Client)
{ AnimAction = NewAction; bResetAnimAct = True; ResetAnimActTime = Level.TimeSeconds+0.3;
}
}
simulated function int DoAnimAction( name AnimName )
{
if (AnimName=='BruteAttack1' || AnimName=='BruteAttack2' || AnimName=='ZombieFireGun' || AnimName == 'DoorBash')
{ if (Role == ROLE_Authority) ServerLowerBlock(); AnimBlendParams(1, 1.0, 0.0,, FireRootBone); PlayAnim(AnimName,, 0.1, 1); return 1;
}
else if (AnimName == 'BruteRageAttack')
{ if (Role == ROLE_Authority) ServerLowerBlock(); AnimBlendParams(1, 1.0, 0.0,, FireRootBone); PlayAnim(AnimName,, 0.1, 1); return 1;
}
else if (AnimName == 'BlockLoop')
{ AnimBlendParams(1, 1.0, 0.0,, FireRootBone); LoopAnim(AnimName,, 0.25, 1); return 1;
}
else if (AnimName == 'BruteBlockSlam')
{ AnimBlendParams(2, 1.0, 0.0,, FireRootBone); PlayAnim(AnimName,, 0.1, 2); return 2;
}
return Super.DoAnimAction(AnimName);
}
// The animation is full body and should set the bWaitForAnim flag
simulated function bool AnimNeedsWait(name TestAnim)
{
if (TestAnim == 'DoorBash') return true;
return false;
}
simulated function AnimEnd(int Channel)
{
local name Sequence;
local float Frame, Rate;
GetAnimParams(Channel, Sequence, Frame, Rate);
// Don't allow notification for a looping animation
if (Sequence == 'BlockLoop') return;
// Disable channel 2 when we're done with it
if (Channel == 2 && Sequence == 'BruteBlockSlam')
{ AnimBlendParams(2, 0); bShotAnim = false; return;
}
Super.AnimEnd(Channel);
}
simulated function ClientChargingAnims()
{
PostNetReceive();
}
function PlayHit(float Damage, Pawn InstigatedBy, vector HitLocation, class<DamageType> damageType, vector Momentum, optional int HitIdx)
{
local Actor A;
if (bBlockedHS) A = Spawn(class'NiceBlockHitEmitter', InstigatedBy,, HitLocation, rotator(Normal(HitLocation - Location)));
else Super.PlayHit(Damage, InstigatedBy, HitLocation, damageType, Momentum, HitIdx);
}
defaultproperties
{ BlockMeleeDmgMul=1.000000 HeadShotgunDmgMul=1.000000 HeadBulletDmgMul=1.000000 stunLoopStart=0.130000 stunLoopEnd=0.650000 idleInsertFrame=0.950000 DetachedArmClass=Class'ScrnZedPack.SeveredArmBrute' DetachedLegClass=Class'ScrnZedPack.SeveredLegBrute' DetachedHeadClass=Class'ScrnZedPack.SeveredHeadBrute' ControllerClass=Class'NicePack.NiceZombieBruteController'
}

View file

@ -0,0 +1,47 @@
class NiceZombieBruteBase extends NiceMonster;
#exec load obj file=ScrnZedPack_T.utx
#exec load obj file=ScrnZedPack_S.uax
#exec load obj file=ScrnZedPack_A.ukx
#exec OBJ LOAD FILE=KFWeaponSound.uax
var bool bChargingPlayer;
var bool bClientCharge;
var bool bFrustrated;
var int MaxRageCounter; // Maximum amount of players we can hit before calming down
var int RageCounter; // Decreases each time we successfully hit a player
var float RageSpeedTween;
var int TwoSecondDamageTotal;
var float LastDamagedTime;
var int RageDamageThreshold;
var int BlockHitsLanded; // Hits made while blocking or raging
var name ChargingAnim;
var Sound RageSound;
// View shaking for players
var() vector ShakeViewRotMag;
var() vector ShakeViewRotRate;
var() float ShakeViewRotTime;
var() vector ShakeViewOffsetMag;
var() vector ShakeViewOffsetRate;
var() float ShakeViewOffsetTime;
var float PushForce;
var vector PushAdd; // Used to add additional height to push
var float RageDamageMul; // Multiplier for hit damage when raging
var float RageBumpDamage; // Damage done when we hit other specimens while raging
var float BlockAddScale; // Additional head scale when blocking
var bool bBlockedHS;
var bool bBlocking;
var bool bServerBlock;
var bool bClientBlock;
var float BlockDmgMul; // Multiplier for damage taken from blocked shots
var float BlockFireDmgMul;
var float BurnGroundSpeedMul; // Multiplier for ground speed when burning
replication
{
reliable if(Role == ROLE_Authority) bChargingPlayer, bServerBlock;
}
//--------------------------------------------------------------------------
// NOTE: All Code resides in the child class(this class was only created to
// eliminate hitching caused by loading default properties during play)
//--------------------------------------------------------------------------
defaultproperties
{ RageDamageThreshold=50 ChargingAnim="BruteRun" RageSound=SoundGroup'ScrnZedPack_S.Brute.BruteRage' ShakeViewRotMag=(X=500.000000,Y=500.000000,Z=600.000000) ShakeViewRotRate=(X=12500.000000,Y=12500.000000,Z=12500.000000) ShakeViewRotTime=6.000000 ShakeViewOffsetMag=(X=5.000000,Y=10.000000,Z=5.000000) ShakeViewOffsetRate=(X=300.000000,Y=300.000000,Z=300.000000) ShakeViewOffsetTime=3.500000 PushForce=860.000000 PushAdd=(Z=150.000000) RageDamageMul=1.100000 RageBumpDamage=4.000000 BlockAddScale=2.500000 BlockDmgMul=0.100000 BlockFireDmgMul=1.000000 BurnGroundSpeedMul=0.700000 StunThreshold=4.000000 flameFuel=0.500000 clientHeadshotScale=1.300000 MeleeAnims(0)="BruteAttack1" MeleeAnims(1)="BruteAttack2" MeleeAnims(2)="BruteBlockSlam" MoanVoice=SoundGroup'ScrnZedPack_S.Brute.BruteTalk' BleedOutDuration=7.000000 ZombieFlag=3 MeleeDamage=20 damageForce=25000 bFatAss=True KFRagdollName="FleshPound_Trip" MeleeAttackHitSound=SoundGroup'ScrnZedPack_S.Brute.BruteHitPlayer' JumpSound=SoundGroup'ScrnZedPack_S.Brute.BruteJump' SpinDamConst=20.000000 SpinDamRand=20.000000 bMeleeStunImmune=True bUseExtendedCollision=True ColOffset=(Z=52.000000) ColRadius=35.000000 ColHeight=25.000000 SeveredArmAttachScale=1.300000 SeveredLegAttachScale=1.200000 SeveredHeadAttachScale=1.500000 PlayerCountHealthScale=0.250000 OnlineHeadshotOffset=(X=22.000000,Z=68.000000) OnlineHeadshotScale=1.300000 HeadHealth=180.000000 PlayerNumHeadHealthScale=0.200000 MotionDetectorThreat=5.000000 HitSound(0)=SoundGroup'ScrnZedPack_S.Brute.BrutePain' DeathSound(0)=SoundGroup'ScrnZedPack_S.Brute.BruteDeath' ChallengeSound(0)=SoundGroup'ScrnZedPack_S.Brute.BruteChallenge' ChallengeSound(1)=SoundGroup'ScrnZedPack_S.Brute.BruteChallenge' ChallengeSound(2)=SoundGroup'ScrnZedPack_S.Brute.BruteChallenge' ChallengeSound(3)=SoundGroup'ScrnZedPack_S.Brute.BruteChallenge' ScoringValue=60 IdleHeavyAnim="BruteIdle" IdleRifleAnim="BruteIdle" RagDeathUpKick=100.000000 MeleeRange=85.000000 GroundSpeed=140.000000 WaterSpeed=120.000000 HealthMax=1000.000000 Health=1000 HeadHeight=2.500000 HeadScale=1.300000 MenuName="Brute" MovementAnims(0)="BruteWalkC" MovementAnims(1)="BruteWalkC" WalkAnims(0)="BruteWalkC" WalkAnims(1)="BruteWalkC" WalkAnims(2)="RunL" WalkAnims(3)="RunR" IdleCrouchAnim="BruteIdle" IdleWeaponAnim="BruteIdle" IdleRestAnim="BruteIdle" AmbientSound=SoundGroup'ScrnZedPack_S.Brute.BruteIdle1Shot' Mesh=SkeletalMesh'ScrnZedPack_A.BruteMesh' PrePivot=(Z=0.000000) Skins(0)=Combiner'ScrnZedPack_T.Brute.Brute_Final' Mass=600.000000 RotationRate=(Yaw=45000,Roll=0)
}

View file

@ -0,0 +1,29 @@
class NiceZombieBruteController extends NiceMonsterController;
var float RageAnimTimeout; // How long until the RageAnim is completed; Hack so the server doesn't get stuck in idle when its doing the Rage anim
var bool bDoneSpottedCheck;
var float RageFrustrationTimer; // Tracks how long we have been walking toward a visible enemy
var float RageFrustrationThreshhold; // Base value for how long the FP should walk torward an enemy without reaching them before getting frustrated and raging
function TimedFireWeaponAtEnemy()
{
if ( (Enemy == none) || FireWeaponAt(Enemy) ) SetCombatTimer();
else SetTimer(0.01, True);
}
state ZombieCharge
{
function bool StrafeFromDamage(float Damage, class<DamageType> DamageType, bool bFindDest)
{ return false;
}
function bool TryStrafe(vector sideDir)
{ return false;
}
function Timer()
{ Disable('NotifyBump'); Target = Enemy; TimedFireWeaponAtEnemy();
}
function BeginState()
{ super.BeginState();
RageFrustrationThreshhold = default.RageFrustrationThreshhold + (Frand() * 5);
}
}
defaultproperties
{ RageFrustrationThreshhold=10.000000
}

View file

@ -0,0 +1,126 @@
// Zombie Monster for KF Invasion gametype
class NiceZombieClot extends NiceZombieClotBase;
#exec OBJ LOAD FILE=KF_Freaks_Trip.ukx
#exec OBJ LOAD FILE=KF_Specimens_Trip_T.utx
#exec OBJ LOAD FILE=MeanZedSkins.utx
function ClawDamageTarget()
{
local vector PushDir;
local KFPawn KFP;
local float UsedMeleeDamage;
if( MeleeDamage > 1 )
{ UsedMeleeDamage = (MeleeDamage - (MeleeDamage * 0.05)) + (MeleeDamage * (FRand() * 0.1));
}
else
{ UsedMeleeDamage = MeleeDamage;
}
// If zombie has latched onto us...
if ( MeleeDamageTarget( UsedMeleeDamage, PushDir))
{ KFP = KFPawn(Controller.Target);
PlaySound(MeleeAttackHitSound, SLOT_Interact, 2.0);
if( !bDecapitated && KFP != none ) { if( KFPlayerReplicationInfo(KFP.PlayerReplicationInfo) == none || KFP.GetVeteran().static.CanBeGrabbed(KFPlayerReplicationInfo(KFP.PlayerReplicationInfo), self)) { if( DisabledPawn != none ) { DisabledPawn.bMovementDisabled = false; }
KFP.DisableMovement(GrappleDuration); DisabledPawn = KFP; } }
}
}
function RangedAttack(Actor A)
{
if ( bShotAnim || Physics == PHYS_Swimming) return;
else if ( CanAttack(A) )
{ bShotAnim = true; SetAnimAction('Claw'); return;
}
}
simulated event SetAnimAction(name NewAction)
{
local int meleeAnimIndex;
if( NewAction=='' ) Return;
if(NewAction == 'Claw')
{ meleeAnimIndex = Rand(3); NewAction = meleeAnims[meleeAnimIndex];
}
ExpectingChannel = DoAnimAction(NewAction);
if( AnimNeedsWait(NewAction) )
{ bWaitForAnim = true;
}
if( Level.NetMode!=NM_Client )
{ AnimAction = NewAction; bResetAnimAct = True; ResetAnimActTime = Level.TimeSeconds+0.3;
}
}
simulated function bool AnimNeedsWait(name TestAnim)
{
if( TestAnim == 'KnockDown' || TestAnim == 'DoorBash' )
{ return true;
}
return false;
}
simulated function int DoAnimAction( name AnimName )
{
if( AnimName=='ClotGrapple' || AnimName=='ClotGrappleTwo' || AnimName=='ClotGrappleThree' )
{ AnimBlendParams(1, 1.0, 0.1,, FireRootBone); PlayAnim(AnimName,, 0.1, 1);
// Randomly send out a message about Clot grabbing you(10% chance) if ( FRand() < 0.10 && LookTarget != none && KFPlayerController(LookTarget.Controller) != none && VSizeSquared(Location - LookTarget.Location) < 2500 /* (MeleeRange + 20)^2 */ && Level.TimeSeconds - KFPlayerController(LookTarget.Controller).LastClotGrabMessageTime > ClotGrabMessageDelay && KFPlayerController(LookTarget.Controller).SelectedVeterancy != class'KFVetBerserker' ) { PlayerController(LookTarget.Controller).Speech('AUTO', 11, ""); KFPlayerController(LookTarget.Controller).LastClotGrabMessageTime = Level.TimeSeconds; }
bGrappling = true; GrappleEndTime = Level.TimeSeconds + GrappleDuration;
return 1;
}
return super.DoAnimAction( AnimName );
}
simulated function Tick(float DeltaTime)
{
super.Tick(DeltaTime);
if( bShotAnim && Role == ROLE_Authority )
{ if( LookTarget!=none ) { Acceleration = AccelRate * Normal(LookTarget.Location - Location); }
}
if( Role == ROLE_Authority && bGrappling )
{ if( Level.TimeSeconds > GrappleEndTime ) { bGrappling = false; }
}
// if we move out of melee range, stop doing the grapple animation
if( bGrappling && LookTarget != none )
{ if( VSize(LookTarget.Location - Location) > MeleeRange + CollisionRadius + LookTarget.CollisionRadius ) { bGrappling = false; AnimEnd(1); }
}
}
function RemoveHead()
{
Super.RemoveHead();
MeleeAnims[0] = 'Claw';
MeleeAnims[1] = 'Claw';
MeleeAnims[2] = 'Claw2';
MeleeDamage *= 2;
MeleeRange *= 2;
if( DisabledPawn != none )
{ DisabledPawn.bMovementDisabled = false; DisabledPawn = none;
}
}
function Died(Controller Killer, class<DamageType> damageType, vector HitLocation)
{
if( DisabledPawn != none )
{ DisabledPawn.bMovementDisabled = false; DisabledPawn = none;
}
super.Died(Killer, damageType, HitLocation);
}
simulated function Destroyed()
{
super.Destroyed();
if( DisabledPawn != none )
{ DisabledPawn.bMovementDisabled = false; DisabledPawn = none;
}
}
static simulated function PreCacheStaticMeshes(LevelInfo myLevel)
{//should be derived and used.
Super.PreCacheStaticMeshes(myLevel);
/*
myLevel.AddPrecacheStaticMesh(StaticMesh'kf_gore_trip_sm.clot.clothead_piece_1');
myLevel.AddPrecacheStaticMesh(StaticMesh'kf_gore_trip_sm.clot.clothead_piece_2');
myLevel.AddPrecacheStaticMesh(StaticMesh'kf_gore_trip_sm.clot.clothead_piece_3');
myLevel.AddPrecacheStaticMesh(StaticMesh'kf_gore_trip_sm.clot.clothead_piece_4');
myLevel.AddPrecacheStaticMesh(StaticMesh'kf_gore_trip_sm.clot.clothead_piece_5');
myLevel.AddPrecacheStaticMesh(StaticMesh'kf_gore_trip_sm.clot.clothead_piece_6');
*/
}
static simulated function PreCacheMaterials(LevelInfo myLevel)
{//should be derived and used.
myLevel.AddPrecacheMaterial(Combiner'KF_Specimens_Trip_T.clot_cmb');
myLevel.AddPrecacheMaterial(Combiner'KF_Specimens_Trip_T.clot_env_cmb');
myLevel.AddPrecacheMaterial(Texture'KF_Specimens_Trip_T.clot_diffuse');
myLevel.AddPrecacheMaterial(Texture'KF_Specimens_Trip_T.clot_spec');
}
defaultproperties
{ idleInsertFrame=0.468000 EventClasses(0)="NicePack.NiceZombieClot" MoanVoice=SoundGroup'KF_EnemiesFinalSnd.clot.Clot_Talk' MeleeAttackHitSound=SoundGroup'KF_EnemiesFinalSnd.clot.Clot_HitPlayer' JumpSound=SoundGroup'KF_EnemiesFinalSnd.clot.Clot_Jump' DetachedArmClass=Class'KFChar.SeveredArmClot' DetachedLegClass=Class'KFChar.SeveredLegClot' DetachedHeadClass=Class'KFChar.SeveredHeadClot' HitSound(0)=SoundGroup'KF_EnemiesFinalSnd.clot.Clot_Pain' DeathSound(0)=SoundGroup'KF_EnemiesFinalSnd.clot.Clot_Death' ChallengeSound(0)=SoundGroup'KF_EnemiesFinalSnd.clot.Clot_Challenge' ChallengeSound(1)=SoundGroup'KF_EnemiesFinalSnd.clot.Clot_Challenge' ChallengeSound(2)=SoundGroup'KF_EnemiesFinalSnd.clot.Clot_Challenge' ChallengeSound(3)=SoundGroup'KF_EnemiesFinalSnd.clot.Clot_Challenge' AmbientSound=Sound'KF_BaseClot.Clot_Idle1Loop' Mesh=SkeletalMesh'KF_Freaks_Trip.CLOT_Freak' Skins(0)=Combiner'KF_Specimens_Trip_T.clot_cmb'
}

View file

@ -0,0 +1,27 @@
// Zombie Monster for KF Invasion gametype
class NiceZombieClotBase extends NiceMonster
abstract;
#exec OBJ LOAD FILE=KF_Freaks_Trip.ukx
#exec OBJ LOAD FILE=KF_Specimens_Trip_T.utx
var KFPawn DisabledPawn; // The pawn that has been disabled by this zombie's grapple
var bool bGrappling; // This zombie is grappling someone
var float GrappleEndTime; // When the current grapple should be over
var() float GrappleDuration; // How long a grapple by this zombie should last
var float ClotGrabMessageDelay; // Amount of time between a player saying "I've been grabbed" message
replication
{
reliable if(bNetDirty && Role == ROLE_Authority) bGrappling;
}
function BreakGrapple()
{
if( DisabledPawn != none )
{ DisabledPawn.bMovementDisabled = false; DisabledPawn = none;
}
}
//-------------------------------------------------------------------------------
// NOTE: All Code resides in the child class(this class was only created to
// eliminate hitching caused by loading default properties during play)
//-------------------------------------------------------------------------------
defaultproperties
{ GrappleDuration=1.500000 ClotGrabMessageDelay=12.000000 fuelRatio=0.900000 clientHeadshotScale=1.500000 MeleeAnims(0)="ClotGrapple" MeleeAnims(1)="ClotGrappleTwo" MeleeAnims(2)="ClotGrappleThree" bCannibal=True MeleeDamage=6 damageForce=5000 KFRagdollName="Clot_Trip" CrispUpThreshhold=9 PuntAnim="ClotPunt" AdditionalWalkAnims(0)="ClotWalk2" Intelligence=BRAINS_Mammal bUseExtendedCollision=True ColOffset=(Z=48.000000) ColRadius=25.000000 ColHeight=5.000000 ExtCollAttachBoneName="Collision_Attach" SeveredArmAttachScale=0.800000 SeveredLegAttachScale=0.800000 SeveredHeadAttachScale=0.800000 OnlineHeadshotOffset=(X=20.000000,Z=37.000000) OnlineHeadshotScale=1.300000 MotionDetectorThreat=0.340000 ScoringValue=7 MeleeRange=20.000000 GroundSpeed=105.000000 WaterSpeed=105.000000 JumpZ=340.000000 HealthMax=130.000000 Health=130 MenuName="Nice Clot" MovementAnims(0)="ClotWalk" WalkAnims(0)="ClotWalk" WalkAnims(1)="ClotWalk" WalkAnims(2)="ClotWalk" WalkAnims(3)="ClotWalk" DrawScale=1.100000 PrePivot=(Z=5.000000) RotationRate=(Yaw=45000,Roll=0)
}

View file

@ -0,0 +1,90 @@
// Zombie Monster for KF Invasion gametype
class NiceZombieCrawler extends NiceZombieCrawlerBase;
//----------------------------------------------------------------------------
// NOTE: All Variables are declared in the base class to eliminate hitching
//----------------------------------------------------------------------------
function bool DoPounce()
{
if (bZapped || bIsCrouched || bWantsToCrouch || (Physics != PHYS_Walking) || VSize(Location - Controller.Target.Location) > (MeleeRange * 5)) return false;
Velocity = Normal(Controller.Target.Location-Location)*PounceSpeed;
Velocity.Z = JumpZ;
SetPhysics(PHYS_Falling);
ZombieSpringAnim();
bPouncing=true;
return true;
}
function TakeDamage(int Damage, Pawn InstigatedBy, Vector HitLocation, Vector Momentum, class<DamageType> DamType, optional int HitIndex)
{
local int OldHeadHealth;
OldHeadHealth = HeadHealth;
Super(NiceMonster).TakeDamage(Damage, instigatedBy, hitLocation, momentum, DamType);
// If crawler's head was damaged, but not yet removed -- I say kill the goddamn thing
if(HeadHealth < OldHeadHealth && HeadHealth > 0) RemoveHead();
}
simulated function ZombieSpringAnim()
{
SetAnimAction('ZombieSpring');
}
event Landed(vector HitNormal)
{
bPouncing=false;
super.Landed(HitNormal);
}
event Bump(actor Other)
{
// TODO: is there a better way
if(bPouncing && KFHumanPawn(Other)!=none )
{ KFHumanPawn(Other).TakeDamage(((MeleeDamage - (MeleeDamage * 0.05)) + (MeleeDamage * (FRand() * 0.1))), self ,self.Location,self.velocity, class 'NicePack.NiceZedMeleeDamageType'); if (KFHumanPawn(Other).Health <=0) { //TODO - move this to humanpawn.takedamage? Also see KFMonster.MeleeDamageTarget KFHumanPawn(Other).SpawnGibs(self.rotation, 1); } //After impact, there'll be no momentum for further bumps bPouncing=false;
}
}
// Blend his attacks so he can hit you in mid air.
simulated function int DoAnimAction( name AnimName )
{
if( AnimName=='InAir_Attack1' || AnimName=='InAir_Attack2' )
{ AnimBlendParams(1, 1.0, 0.0,, FireRootBone); PlayAnim(AnimName,, 0.0, 1); return 1;
}
if( AnimName=='HitF' )
{ AnimBlendParams(1, 1.0, 0.0,, NeckBone); PlayAnim(AnimName,, 0.0, 1); return 1;
}
if( AnimName=='ZombieSpring' )
{ PlayAnim(AnimName,,0.02); return 0;
}
return Super.DoAnimAction(AnimName);
}
simulated event SetAnimAction(name NewAction)
{
local int meleeAnimIndex;
if( NewAction=='' ) Return;
if(NewAction == 'Claw')
{ meleeAnimIndex = Rand(2); if( Physics == PHYS_Falling ) { NewAction = MeleeAirAnims[meleeAnimIndex]; } else { NewAction = meleeAnims[meleeAnimIndex]; }
}
ExpectingChannel = DoAnimAction(NewAction);
if( AnimNeedsWait(NewAction) )
{ bWaitForAnim = true;
}
if( Level.NetMode!=NM_Client )
{ AnimAction = NewAction; bResetAnimAct = True; ResetAnimActTime = Level.TimeSeconds+0.3;
}
}
// The animation is full body and should set the bWaitForAnim flag
simulated function bool AnimNeedsWait(name TestAnim)
{
if( TestAnim == 'ZombieSpring' || TestAnim == 'DoorBash' )
{ return true;
}
return false;
}
function bool FlipOver()
{
return true;
}
static simulated function PreCacheMaterials(LevelInfo myLevel)
{//should be derived and used.
myLevel.AddPrecacheMaterial(Combiner'KF_Specimens_Trip_T.crawler_cmb');
myLevel.AddPrecacheMaterial(Combiner'KF_Specimens_Trip_T.crawler_env_cmb');
myLevel.AddPrecacheMaterial(Texture'KF_Specimens_Trip_T.crawler_diff');
}
defaultproperties
{ stunLoopStart=0.110000 stunLoopEnd=0.570000 idleInsertFrame=0.900000 EventClasses(0)="NicePack.NiceZombieCrawler" MoanVoice=SoundGroup'KF_EnemiesFinalSnd.Crawler.Crawler_Talk' MeleeAttackHitSound=SoundGroup'KF_EnemiesFinalSnd.Crawler.Crawler_HitPlayer' JumpSound=SoundGroup'KF_EnemiesFinalSnd.Crawler.Crawler_Jump' DetachedArmClass=Class'KFChar.SeveredArmCrawler' DetachedLegClass=Class'KFChar.SeveredLegCrawler' DetachedHeadClass=Class'KFChar.SeveredHeadCrawler' HitSound(0)=SoundGroup'KF_EnemiesFinalSnd.Crawler.Crawler_Pain' DeathSound(0)=SoundGroup'KF_EnemiesFinalSnd.Crawler.Crawler_Death' ChallengeSound(0)=SoundGroup'KF_EnemiesFinalSnd.Crawler.Crawler_Acquire' ChallengeSound(1)=SoundGroup'KF_EnemiesFinalSnd.Crawler.Crawler_Acquire' ChallengeSound(2)=SoundGroup'KF_EnemiesFinalSnd.Crawler.Crawler_Acquire' ChallengeSound(3)=SoundGroup'KF_EnemiesFinalSnd.Crawler.Crawler_Acquire' ControllerClass=Class'NicePack.NiceZombieCrawlerController' AmbientSound=Sound'KF_BaseCrawler.Crawler_Idle' Mesh=SkeletalMesh'KF_Freaks_Trip.Crawler_Freak' Skins(0)=Combiner'KF_Specimens_Trip_T.crawler_cmb'
}

View file

@ -0,0 +1,77 @@
// Zombie Monster for KF Invasion gametype
class NiceZombieCrawlerBase extends NiceMonster
abstract;
#exec OBJ LOAD FILE=
var() float PounceSpeed;
var bool bPouncing;
var(Anims) name MeleeAirAnims[3]; // Attack anims for when flying through the air
//-------------------------------------------------------------------------------
// NOTE: All Code resides in the child class(this class was only created to
// eliminate hitching caused by loading default properties during play)
//-------------------------------------------------------------------------------
function bool DoPounce()
{
if ( bZapped || bIsCrouched || bWantsToCrouch || (Physics != PHYS_Walking) || VSize(Location - Controller.Target.Location) > (MeleeRange * 5) ) return false;
Velocity = Normal(Controller.Target.Location-Location)*PounceSpeed;
Velocity.Z = JumpZ;
SetPhysics(PHYS_Falling);
ZombieSpringAnim();
bPouncing=true;
return true;
}
simulated function ZombieSpringAnim()
{
SetAnimAction('ZombieSpring');
}
event Landed(vector HitNormal)
{
bPouncing=false;
super.Landed(HitNormal);
}
event Bump(actor Other)
{
// TODO: is there a better way
if(bPouncing && KFHumanPawn(Other)!=none )
{ KFHumanPawn(Other).TakeDamage(((MeleeDamage - (MeleeDamage * 0.05)) + (MeleeDamage * (FRand() * 0.1))), self ,self.Location,self.velocity, class 'NicePack.NiceZedMeleeDamageType'); if (KFHumanPawn(Other).Health <=0) { //TODO - move this to humanpawn.takedamage? Also see KFMonster.MeleeDamageTarget KFHumanPawn(Other).SpawnGibs(self.rotation, 1); } //After impact, there'll be no momentum for further bumps bPouncing=false;
}
}
// Blend his attacks so he can hit you in mid air.
simulated function int DoAnimAction( name AnimName )
{
if( AnimName=='InAir_Attack1' || AnimName=='InAir_Attack2' )
{ AnimBlendParams(1, 1.0, 0.0,, FireRootBone); PlayAnim(AnimName,, 0.0, 1); return 1;
}
if( AnimName=='HitF' )
{ AnimBlendParams(1, 1.0, 0.0,, NeckBone); PlayAnim(AnimName,, 0.0, 1); return 1;
}
if( AnimName=='ZombieSpring' )
{ PlayAnim(AnimName,,0.02); return 0;
}
return Super.DoAnimAction(AnimName);
}
simulated event SetAnimAction(name NewAction)
{
local int meleeAnimIndex;
if( NewAction=='' ) Return;
if(NewAction == 'Claw')
{ meleeAnimIndex = Rand(2); if( Physics == PHYS_Falling ) { NewAction = MeleeAirAnims[meleeAnimIndex]; } else { NewAction = meleeAnims[meleeAnimIndex]; }
}
ExpectingChannel = DoAnimAction(NewAction);
if( AnimNeedsWait(NewAction) )
{ bWaitForAnim = true;
}
if( Level.NetMode!=NM_Client )
{ AnimAction = NewAction; bResetAnimAct = True; ResetAnimActTime = Level.TimeSeconds+0.3;
}
}
// The animation is full body and should set the bWaitForAnim flag
simulated function bool AnimNeedsWait(name TestAnim)
{
if( TestAnim == 'ZombieSpring' || TestAnim == 'DoorBash' )
{ return true;
}
return false;
}
defaultproperties
{ PounceSpeed=330.000000 MeleeAirAnims(0)="InAir_Attack1" MeleeAirAnims(1)="InAir_Attack2" StunThreshold=2.000000 fuelRatio=0.800000 bWeakHead=True clientHeadshotScale=1.350000 MeleeAnims(0)="ZombieLeapAttack" MeleeAnims(1)="ZombieLeapAttack2" HitAnims(1)="HitF" HitAnims(2)="HitF" KFHitFront="HitF" KFHitBack="HitF" KFHitLeft="HitF" KFHitRight="HitF" bStunImmune=True bCannibal=True ZombieFlag=2 MeleeDamage=6 damageForce=5000 KFRagdollName="Crawler_Trip" CrispUpThreshhold=10 Intelligence=BRAINS_Mammal SeveredArmAttachScale=0.800000 SeveredLegAttachScale=0.850000 SeveredHeadAttachScale=1.100000 OnlineHeadshotOffset=(X=28.000000,Z=7.000000) OnlineHeadshotScale=1.200000 MotionDetectorThreat=0.340000 ScoringValue=10 IdleHeavyAnim="ZombieLeapIdle" IdleRifleAnim="ZombieLeapIdle" bCrawler=True GroundSpeed=140.000000 WaterSpeed=130.000000 JumpZ=350.000000 HealthMax=70.000000 Health=70 HeadHeight=2.500000 HeadScale=1.050000 MenuName="Nice Crawler" bDoTorsoTwist=False MovementAnims(0)="ZombieScuttle" MovementAnims(1)="ZombieScuttleB" MovementAnims(2)="ZombieScuttleL" MovementAnims(3)="ZombieScuttleR" WalkAnims(0)="ZombieScuttle" WalkAnims(1)="ZombieScuttleB" WalkAnims(2)="ZombieScuttleL" WalkAnims(3)="ZombieScuttleR" AirAnims(0)="ZombieSpring" AirAnims(1)="ZombieSpring" AirAnims(2)="ZombieSpring" AirAnims(3)="ZombieSpring" TakeoffAnims(0)="ZombieSpring" TakeoffAnims(1)="ZombieSpring" TakeoffAnims(2)="ZombieSpring" TakeoffAnims(3)="ZombieSpring" AirStillAnim="ZombieSpring" TakeoffStillAnim="ZombieLeapIdle" IdleCrouchAnim="ZombieLeapIdle" IdleWeaponAnim="ZombieLeapIdle" IdleRestAnim="ZombieLeapIdle" bOrientOnSlope=True DrawScale=1.100000 PrePivot=(Z=0.000000) CollisionHeight=25.000000
}

View file

@ -0,0 +1,59 @@
class NiceZombieCrawlerController extends NiceMonsterController;
var float LastPounceTime;
var bool bDoneSpottedCheck;
state ZombieHunt
{
event SeePlayer(Pawn SeenPlayer)
{ if ( !bDoneSpottedCheck && PlayerController(SeenPlayer.Controller) != none ) { // 25% chance of first player to see this Crawler saying something if ( !KFGameType(Level.Game).bDidSpottedCrawlerMessage && FRand() < 0.25 ) { PlayerController(SeenPlayer.Controller).Speech('AUTO', 18, ""); KFGameType(Level.Game).bDidSpottedCrawlerMessage = true; }
bDoneSpottedCheck = true; }
super.SeePlayer(SeenPlayer);
}
}
function bool IsInPounceDist(actor PTarget)
{
local vector DistVec;
local float time;
local float HeightMoved;
local float EndHeight;
//work out time needed to reach target
DistVec = pawn.location - PTarget.location;
DistVec.Z=0;
time = vsize(DistVec)/NiceZombieCrawler(pawn).PounceSpeed;
// vertical change in that time
//assumes downward grav only
HeightMoved = Pawn.JumpZ*time + 0.5*pawn.PhysicsVolume.Gravity.z*time*time;
EndHeight = pawn.Location.z +HeightMoved;
//log(Vsize(Pawn.Location - PTarget.Location));
if((abs(EndHeight - PTarget.Location.Z) < Pawn.CollisionHeight + PTarget.CollisionHeight) &&
VSize(pawn.Location - PTarget.Location) < KFMonster(pawn).MeleeRange * 5)
return true;
else
return false;
}
function bool FireWeaponAt(Actor A)
{
local vector aFacing,aToB;
local float RelativeDir;
if ( A == none ) A = Enemy;
if ( (A == none) || (Focus != A) ) return false;
if(CanAttack(A))
{ Target = A; Monster(Pawn).RangedAttack(Target);
}
else
{ //TODO - base off land time rather than launch time? if((LastPounceTime + (4.5 - (FRand() * 3.0))) < Level.TimeSeconds ) { aFacing=Normal(Vector(Pawn.Rotation)); // Get the vector from A to B aToB=A.Location-Pawn.Location;
RelativeDir = aFacing dot aToB; if ( RelativeDir > 0.85 ) { //Facing enemy if(IsInPounceDist(A) ) { if(NiceZombieCrawler(Pawn).DoPounce()==true ) LastPounceTime = Level.TimeSeconds; } } }
}
return false;
}
function bool NotifyLanded(vector HitNormal)
{
if( NiceZombieCrawler(pawn).bPouncing )
{ // restart pathfinding from landing location GotoState('hunting'); return false;
}
else
return super.NotifyLanded(HitNormal);
}
defaultproperties
{
}

View file

@ -0,0 +1,580 @@
// Zombie Monster for KF Invasion gametype
class NiceZombieFleshpound extends NiceZombieFleshpoundBase;
var bool bFirstRageAttack;
simulated function PostNetBeginPlay(){
if(AvoidArea == none)
AvoidArea = Spawn(class'NiceAvoidMarkerFP', self);
if(AvoidArea != none)
AvoidArea.InitFor(Self);
EnableChannelNotify(1, 1);
AnimBlendParams(1, 1.0, 0.0,, SpineBone1);
super.PostNetBeginPlay();
}
function bool CanGetOutOfWay(){
return false;
}
function bool IsStunPossible(){
return false;
}
// This zed has been taken control of. Boost its health and speed
function SetMindControlled(bool bNewMindControlled)
{
if( bNewMindControlled )
{
NumZCDHits++;
// if we hit him a couple of times, make him rage!
if( NumZCDHits > 1 )
{
if( !IsInState('ChargeToMarker') )
{
GotoState('ChargeToMarker');
}
else
{
NumZCDHits = 1;
if( IsInState('ChargeToMarker') )
{
GotoState('');
}
}
}
else
{
if( IsInState('ChargeToMarker') )
{
GotoState('');
}
}
if( bNewMindControlled != bZedUnderControl )
{
SetGroundSpeed(OriginalGroundSpeed * 1.25);
Health *= 1.25;
HealthMax *= 1.25;
}
}
else
{
NumZCDHits=0;
}
bZedUnderControl = bNewMindControlled;
}
// Handle the zed being commanded to move to a new location
function GivenNewMarker()
{
if( bChargingPlayer && NumZCDHits > 1 )
{
GotoState('ChargeToMarker');
}
else
{
GotoState('');
}
}
function bool CheckMiniFlinch(int flinchScore, Pawn instigatedBy, Vector hitLocation, Vector momentum, class<NiceWeaponDamageType> damageType, float headshotLevel, KFPlayerReplicationInfo KFPRI){
if(Controller.IsInState('WaitForAnim') || flinchScore < 100)
return false;
return super.CheckMiniFlinch(flinchScore, instigatedBy, hitLocation, momentum, damageType, headshotLevel, KFPRI);
}
function ModDamage(out int Damage, Pawn instigatedBy, Vector hitLocation, Vector momentum, class<NiceWeaponDamageType> damageType, float headshotLevel, KFPlayerReplicationInfo KFPRI, optional float lockonTime){
super.ModDamage(Damage, instigatedBy, hitLocation, momentum, damageType, headshotLevel, KFPRI);
// Already low damage also isn't cut down
if(Damage <= 35)
return;
// Melee weapons damage is cut by the same amount
if(class<NiceDamageTypeVetBerserker>(DamageType) != none){
Damage *= 0.75;
return;
}//MEANTODO
// He takes less damage to small arms fire (non explosives)
// Frags and LAW rockets will bring him down way faster than bullets and shells.
if (true/* DamageType != class 'DamTypeFrag' && DamageType != class 'DamTypePipeBomb'
&& DamageType!=class'NiceDamTypeM41AGrenade' && DamageType != class'NiceDamTypeRocket'
&& (DamageType == none || !DamageType.default.bIsExplosive)*/)
{
// Don't reduce the damage so much if it's a headshot
if(headshotLevel > 0.0){
/*if(DamageType!= none && DamageType.default.HeadShotDamageMult >= 1.5)
Damage *= 0.75;
else
Damage *= 0.5;*/
Damage *= 0.75;
}
else
Damage *= 0.5;
}
// double damage from handheld explosives or poison
else if (DamageType == class 'DamTypeFrag' || DamageType == class 'DamTypePipeBomb' || DamageType == class 'DamTypeMedicNade'){
Damage *= 2.0;
}
// A little extra damage from the grenade launchers, they are HE not shrapnel,
// and its shrapnel that REALLY hurts the FP ;)//MEANTODO
else if(/* DamageType == class'NiceDamTypeM41AGrenade'
||*/ (DamageType != none && DamageType.default.bIsExplosive))
Damage *= 1.25;
if(AnimAction == 'PoundBlock')
Damage *= BlockDamageReduction;
if(damageType == class 'DamTypeVomit')
Damage = 0;
else if(damageType == class 'DamTypeBlowerThrower')
Damage *= 0.25;
}
function TakeDamageClient(int Damage, Pawn InstigatedBy, Vector Hitlocation, Vector Momentum, class<NiceWeaponDamageType> damageType, float headshotLevel, float lockonTime){
local bool bWasBurning;
local int OldHealth;
bWasBurning = bOnFire;
if(LastDamagedTime < Level.TimeSeconds )
TwoSecondDamageTotal = 0;
LastDamagedTime = Level.TimeSeconds + 2;
OldHealth = Health;
// Shut off his "Device" when dead
if(Damage >= Health)
PostNetReceive();
Super.TakeDamageClient(Damage, instigatedBy, hitLocation, momentum, damageType, headshotLevel, lockonTime);
TwoSecondDamageTotal += OldHealth - Health;
if( !bDecapitated && TwoSecondDamageTotal > RageDamageThreshold && !bChargingPlayer &&
(!(bWasBurning && bCrispified) || bFrustrated) )
StartChargingFP(InstigatedBy);
}
// changes colors on Device (notified in anim)
simulated function DeviceGoRed()
{
Skins[1]=Shader'KFCharacters.FPRedBloomShader';
}
simulated function DeviceGoNormal()
{
Skins[1] = Shader'KFCharacters.FPAmberBloomShader';
}
function RangedAttack(Actor A){
local NiceZombieFleshpoundController fpController;
if(bShotAnim || Physics == PHYS_Swimming)
return;
else if(CanAttack(A))
{
fpController = NiceZombieFleshpoundController(Controller);
if(fpController != none)
fpController.RageFrustrationTimer = 0;
bShotAnim = true;
SetAnimAction('Claw');
return;
}
}
// Sets the FP in a berserk charge state until he either strikes his target, or hits timeout
function StartChargingFP(Pawn instigatedBy){
local float RageAnimDur;
local NiceZombieFleshpoundController fpController;
local NiceHumanPawn rageTarget, altRageTarget;
if(Health <= 0)
return;
bFirstRageAttack = true;
SetAnimAction('PoundRage');
Acceleration = vect(0,0,0);
bShotAnim = true;
Velocity.X = 0;
Velocity.Y = 0;
Controller.GoToState('WaitForAnim');
fpController = NiceZombieFleshpoundController(Controller);
if(fpController != none)
fpController.bUseFreezeHack = True;
RageAnimDur = GetAnimDuration('PoundRage');
if(fpController != none)
fpController.SetPoundRageTimout(RageAnimDur);
GoToState('BeginRaging');
rageTarget = NiceHumanPawn(instigatedBy);
altRageTarget = NiceHumanPawn(controller.focus);
if( rageTarget != none && KFGameType(Level.Game) != none
&& class'NiceVeterancyTypes'.static.HasSkill(NicePlayerController(rageTarget.Controller),
class'NiceSkillCommandoPerfectExecution') ){
KFGameType(Level.Game).DramaticEvent(1.0);
}
else if( altRageTarget != none && KFGameType(Level.Game) != none
&& class'NiceVeterancyTypes'.static.HasSkill(NicePlayerController(altRageTarget.Controller),
class'NiceSkillCommandoPerfectExecution') ){
KFGameType(Level.Game).DramaticEvent(1.0);
}
}
state BeginRaging
{
Ignores StartChargingFP;
function bool CanGetOutOfWay()
{
return false;
}
simulated function bool HitCanInterruptAction()
{
return false;
}
function Tick( float Delta )
{
Acceleration = vect(0,0,0);
global.Tick(Delta);
}
Begin:
Sleep(GetAnimDuration('PoundRage'));
GotoState('RageCharging');
}
state RageCharging
{
Ignores StartChargingFP;
function bool CheckMiniFlinch(int flinchScore, Pawn instigatedBy, Vector hitLocation, Vector momentum, class<NiceWeaponDamageType> damageType, float headshotLevel, KFPlayerReplicationInfo KFPRI){
if(!bShotAnim)
return super.CheckMiniFlinch(flinchScore, instigatedBy, hitLocation, momentum, damageType, headshotLevel, KFPRI);
return false;
}
function bool CanGetOutOfWay()
{
return false;
}
// Don't override speed in this state
function bool CanSpeedAdjust()
{
return false;
}
function BeginState()
{
bChargingPlayer = true;
if( Level.NetMode!=NM_DedicatedServer )
ClientChargingAnims();
RageEndTime = (Level.TimeSeconds + 15) + (FRand() * 18);
NetUpdateTime = Level.TimeSeconds - 1;
}
function EndState()
{
local NiceZombieFleshpoundController fpController;
bChargingPlayer = False;
bFrustrated = false;
fpController = NiceZombieFleshPoundController(Controller);
if(fpController == none)
fpController.RageFrustrationTimer = 0;
if( Health>0 && !bZapped )
{
SetGroundSpeed(GetOriginalGroundSpeed());
}
if( Level.NetMode!=NM_DedicatedServer )
ClientChargingAnims();
NetUpdateTime = Level.TimeSeconds - 1;
}
function Tick( float Delta )
{
if( !bShotAnim )
{
SetGroundSpeed(OriginalGroundSpeed * 2.3);//2.0;
if( !bFrustrated && !bZedUnderControl && Level.TimeSeconds>RageEndTime )
{
GoToState('');
}
}
// Keep the flesh pound moving toward its target when attacking
if( Role == ROLE_Authority && bShotAnim)
{
if( LookTarget!=none )
{
Acceleration = AccelRate * Normal(LookTarget.Location - Location);
}
}
global.Tick(Delta);
}
function Bump( Actor Other )
{
local float RageBumpDamage;
local KFMonster KFMonst;
KFMonst = KFMonster(Other);
// Hurt/Kill enemies that we run into while raging
if( !bShotAnim && KFMonst!=none && NiceZombieFleshPound(Other)==none && Pawn(Other).Health>0 )
{
// Random chance of doing obliteration damage
if( FRand() < 0.4 )
{
RageBumpDamage = 501;
}
else
{
RageBumpDamage = 450;
}
RageBumpDamage *= KFMonst.PoundRageBumpDamScale;
Log("DAMAGE!"@String(RageBumpDamage));
Other.TakeDamage(RageBumpDamage, self, Other.Location, Velocity * Other.Mass, class'NiceDamTypePoundCrushed');
}
else Global.Bump(Other);
}
// If fleshie hits his target on a charge, then he should settle down for abit.
function bool MeleeDamageTarget(int hitdamage, vector pushdir)
{
local bool RetVal,bWasEnemy;
bWasEnemy = (Controller.Target==Controller.Enemy);
RetVal = Super.MeleeDamageTarget(hitdamage*1.75, pushdir*3);
if( RetVal && bWasEnemy )
GoToState('');
return RetVal;
}
}
// State where the zed is charging to a marked location.
// Not sure if we need this since its just like RageCharging,
// but keeping it here for now in case we need to implement some
// custom behavior for this state
state ChargeToMarker extends RageCharging
{
Ignores StartChargingFP;
function Tick( float Delta )
{
if( !bShotAnim )
{
SetGroundSpeed(OriginalGroundSpeed * 2.3);
if( !bFrustrated && !bZedUnderControl && Level.TimeSeconds>RageEndTime )
{
GoToState('');
}
}
// Keep the flesh pound moving toward its target when attacking
if( Role == ROLE_Authority && bShotAnim)
{
if( LookTarget!=none )
{
Acceleration = AccelRate * Normal(LookTarget.Location - Location);
}
}
global.Tick(Delta);
}
}
simulated function PostNetReceive()
{
if( bClientCharge!=bChargingPlayer && !bZapped )
{
bClientCharge = bChargingPlayer;
if (bChargingPlayer)
{
MovementAnims[0]=ChargingAnim;
MeleeAnims[0]='FPRageAttack';
MeleeAnims[1]='FPRageAttack';
MeleeAnims[2]='FPRageAttack';
DeviceGoRed();
}
else
{
MovementAnims[0]=default.MovementAnims[0];
MeleeAnims[0]=default.MeleeAnims[0];
MeleeAnims[1]=default.MeleeAnims[1];
MeleeAnims[2]=default.MeleeAnims[2];
DeviceGoNormal();
}
}
}
simulated function PlayDyingAnimation(class<DamageType> DamageType, vector HitLoc)
{
Super.PlayDyingAnimation(DamageType,HitLoc);
if( Level.NetMode!=NM_DedicatedServer )
DeviceGoNormal();
}
simulated function ClientChargingAnims()
{
PostNetReceive();
}
function ClawDamageTarget()
{
local vector PushDir;
local KFHumanPawn HumanTarget;
local KFPlayerController HumanTargetController;
local float UsedMeleeDamage;
local name Sequence;
local float Frame, Rate;
GetAnimParams( ExpectingChannel, Sequence, Frame, Rate );
if( MeleeDamage > 1 )
{
UsedMeleeDamage = (MeleeDamage - (MeleeDamage * 0.05)) + (MeleeDamage * (FRand() * 0.1));
}
else
{
UsedMeleeDamage = MeleeDamage;
}
// Reduce the melee damage for anims with repeated attacks, since it does repeated damage over time
if( Sequence == 'PoundAttack1' )
{
UsedMeleeDamage *= 0.5;
}
else if( Sequence == 'PoundAttack2' )
{
UsedMeleeDamage *= 0.25;
}
if(Controller!=none && Controller.Target!=none)
{
//calculate based on relative positions
PushDir = (damageForce * Normal(Controller.Target.Location - Location));
}
else
{
//calculate based on way Monster is facing
PushDir = damageForce * vector(Rotation);
}
if ( MeleeDamageTarget( UsedMeleeDamage, PushDir))
{
HumanTarget = KFHumanPawn(Controller.Target);
if( HumanTarget!=none )
HumanTargetController = KFPlayerController(HumanTarget.Controller);
if( HumanTargetController!=none )
HumanTargetController.ShakeView(RotMag, RotRate, RotTime, OffsetMag, OffsetRate, OffsetTime);
PlaySound(MeleeAttackHitSound, SLOT_Interact, 1.25);
}
}
function SpinDamage(actor Target)
{
local vector HitLocation;
local Name TearBone;
local Float dummy;
local float DamageAmount;
local vector PushDir;
local KFHumanPawn HumanTarget;
if(target==none)
return;
PushDir = (damageForce * Normal(Target.Location - Location));
damageamount = (SpinDamConst + rand(SpinDamRand) );
// FLING DEM DEAD BODIEZ!
if (Target.IsA('KFHumanPawn') && Pawn(Target).Health <= DamageAmount)
{
KFHumanPawn(Target).RagDeathVel *= 3;
KFHumanPawn(Target).RagDeathUpKick *= 1.5;
}
if (Target !=none && Target.IsA('KFDoorMover'))
{
Target.TakeDamage(DamageAmount , self ,HitLocation,pushdir, class 'NicePack.NiceZedMeleeDamageType');
PlaySound(MeleeAttackHitSound, SLOT_Interact, 1.25);
}
if (KFHumanPawn(Target)!=none)
{
HumanTarget = KFHumanPawn(Target);
if (HumanTarget.Controller != none)
HumanTarget.Controller.ShakeView(RotMag, RotRate, RotTime, OffsetMag, OffsetRate, OffsetTime);
//TODO - line below was KFPawn. Does this whole block need to be KFPawn, or is it OK as KFHumanPawn?
KFHumanPawn(Target).TakeDamage(DamageAmount, self ,HitLocation,pushdir, class 'NicePack.NiceZedMeleeDamageType');
if (KFHumanPawn(Target).Health <=0)
{
KFHumanPawn(Target).SpawnGibs(rotator(pushdir), 1);
TearBone=KFPawn(Target).GetClosestBone(HitLocation,Velocity,dummy);
KFHumanPawn(Controller.Target).HideBone(TearBone);
}
}
}
simulated function int DoAnimAction( name AnimName )
{
if( AnimName=='PoundAttack1' || AnimName=='PoundAttack2' || AnimName=='PoundAttack3'
||AnimName=='FPRageAttack' || AnimName=='ZombieFireGun' )
{
AnimBlendParams(1, 1.0, 0.0,, FireRootBone);
PlayAnim(AnimName,, 0.1, 1);
Return 1;
}
Return Super.DoAnimAction(AnimName);
}
simulated event SetAnimAction(name NewAction)
{
local int meleeAnimIndex;
if( NewAction=='' )
Return;
// Remove one of the attacks during fp's rage
if(NewAction == 'Claw')
{
if(IsInState('RageCharging') && bFirstRageAttack){
bFirstRageAttack = false;
meleeAnimIndex = Rand(2);
NewAction = meleeAnims[meleeAnimIndex+1];
}
else{
meleeAnimIndex = Rand(3);
NewAction = meleeAnims[meleeAnimIndex];
}
}
ExpectingChannel = DoAnimAction(NewAction);
if( AnimNeedsWait(NewAction) )
{
bWaitForAnim = true;
}
if( Level.NetMode!=NM_Client )
{
AnimAction = NewAction;
bResetAnimAct = True;
ResetAnimActTime = Level.TimeSeconds+0.3;
}
}
// The animation is full body and should set the bWaitForAnim flag
simulated function bool AnimNeedsWait(name TestAnim)
{
if( TestAnim == 'PoundRage' || TestAnim == 'DoorBash' )
{
return true;
}
return false;
}
simulated function Tick(float DeltaTime)
{
super.Tick(DeltaTime);
// Keep the flesh pound moving toward its target when attacking
if( Role == ROLE_Authority && bShotAnim)
{
if( LookTarget!=none )
{
Acceleration = AccelRate * Normal(LookTarget.Location - Location);
}
}
}
function bool FlipOver()
{
return false;
}
function bool SameSpeciesAs(Pawn P)
{
return (NiceZombieFleshPound(P)!=none);
}
simulated function Destroyed()
{
if( AvoidArea!=none )
AvoidArea.Destroy();
Super.Destroyed();
}
static simulated function PreCacheMaterials(LevelInfo myLevel)
{//should be derived and used.
myLevel.AddPrecacheMaterial(Combiner'KF_Specimens_Trip_T.fleshpound_cmb');
myLevel.AddPrecacheMaterial(Combiner'KF_Specimens_Trip_T.fleshpound_env_cmb');
myLevel.AddPrecacheMaterial(Texture'KF_Specimens_Trip_T.fleshpound_diff');
}
defaultproperties
{
stunLoopStart=0.140000
stunLoopEnd=0.650000
idleInsertFrame=0.950000
EventClasses(0)="NicePack.NiceZombieFleshpound"
MoanVoice=SoundGroup'KF_EnemiesFinalSnd.Fleshpound.FP_Talk'
MeleeAttackHitSound=SoundGroup'KF_EnemiesFinalSnd.Fleshpound.FP_HitPlayer'
JumpSound=SoundGroup'KF_EnemiesFinalSnd.Fleshpound.FP_Jump'
DetachedArmClass=Class'KFChar.SeveredArmPound'
DetachedLegClass=Class'KFChar.SeveredLegPound'
DetachedHeadClass=Class'KFChar.SeveredHeadPound'
HitSound(0)=SoundGroup'KF_EnemiesFinalSnd.Fleshpound.FP_Pain'
DeathSound(0)=SoundGroup'KF_EnemiesFinalSnd.Fleshpound.FP_Death'
ChallengeSound(0)=SoundGroup'KF_EnemiesFinalSnd.Fleshpound.FP_Challenge'
ChallengeSound(1)=SoundGroup'KF_EnemiesFinalSnd.Fleshpound.FP_Challenge'
ChallengeSound(2)=SoundGroup'KF_EnemiesFinalSnd.Fleshpound.FP_Challenge'
ChallengeSound(3)=SoundGroup'KF_EnemiesFinalSnd.Fleshpound.FP_Challenge'
ControllerClass=Class'NicePack.NiceZombieFleshpoundController'
AmbientSound=Sound'KF_BaseFleshpound.FP_IdleLoop'
Mesh=SkeletalMesh'KF_Freaks_Trip.FleshPound_Freak'
Skins(0)=Combiner'KF_Specimens_Trip_T.fleshpound_cmb'
}

View file

@ -0,0 +1,29 @@
// Zombie Monster for KF Invasion gametype
class NiceZombieFleshpoundBase extends NiceMonster
abstract;
var () float BlockDamageReduction;
var bool bChargingPlayer,bClientCharge;
var int TwoSecondDamageTotal;
var float LastDamagedTime,RageEndTime;
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 name ChargingAnim; // How he runs when charging the player.
//var ONSHeadlightCorona DeviceGlow; //KFTODO: Don't think this is needed, its not reffed anywhere
var() int RageDamageThreshold; // configurable.
var NiceAvoidMarkerFP AvoidArea; // Make the other AI fear this AI
var bool bFrustrated; // The fleshpound is tired of being kited and is pissed and ready to attack
replication
{
reliable if(Role == ROLE_Authority) bChargingPlayer, bFrustrated;
}
//-------------------------------------------------------------------------------
// NOTE: All Code resides in the child class(this class was only created to
// eliminate hitching caused by loading default properties during play)
//-------------------------------------------------------------------------------
defaultproperties
{ BlockDamageReduction=0.400000 RotMag=(X=500.000000,Y=500.000000,Z=600.000000) RotRate=(X=12500.000000,Y=12500.000000,Z=12500.000000) RotTime=6.000000 OffsetMag=(X=5.000000,Y=10.000000,Z=5.000000) OffsetRate=(X=300.000000,Y=300.000000,Z=300.000000) OffsetTime=3.500000 ChargingAnim="PoundRun" RageDamageThreshold=360 StunThreshold=4.000000 fuelRatio=0.150000 MeleeAnims(0)="PoundAttack1" MeleeAnims(1)="PoundAttack2" MeleeAnims(2)="PoundAttack3" StunsRemaining=1 BleedOutDuration=7.000000 ZapThreshold=1.750000 ZappedDamageMod=1.250000 bHarpoonToBodyStuns=False ZombieFlag=3 MeleeDamage=35 damageForce=15000 bFatAss=True KFRagdollName="FleshPound_Trip" SpinDamConst=20.000000 SpinDamRand=20.000000 bMeleeStunImmune=True Intelligence=BRAINS_Mammal bUseExtendedCollision=True ColOffset=(Z=52.000000) ColRadius=36.000000 ColHeight=35.000000 SeveredArmAttachScale=1.300000 SeveredLegAttachScale=1.200000 SeveredHeadAttachScale=1.500000 PlayerCountHealthScale=0.250000 OnlineHeadshotOffset=(X=22.000000,Z=68.000000) OnlineHeadshotScale=1.300000 HeadHealth=700.000000 PlayerNumHeadHealthScale=0.300000 MotionDetectorThreat=5.000000 bBoss=True ScoringValue=200 IdleHeavyAnim="PoundIdle" IdleRifleAnim="PoundIdle" RagDeathUpKick=100.000000 MeleeRange=55.000000 GroundSpeed=130.000000 WaterSpeed=120.000000 HealthMax=1650.000000 Health=1650 HeadHeight=2.500000 HeadScale=1.300000 MenuName="Nice Flesh Pound" MovementAnims(0)="PoundWalk" MovementAnims(1)="WalkB" WalkAnims(0)="PoundWalk" WalkAnims(1)="WalkB" WalkAnims(2)="RunL" WalkAnims(3)="RunR" IdleCrouchAnim="PoundIdle" IdleWeaponAnim="PoundIdle" IdleRestAnim="PoundIdle" PrePivot=(Z=0.000000) Skins(1)=Shader'KFCharacters.FPAmberBloomShader' Mass=600.000000 RotationRate=(Yaw=45000,Roll=0)
}

View file

@ -0,0 +1,116 @@
class NiceZombieFleshpoundController extends NiceMonsterController;
var float RageAnimTimeout; // How long until the RageAnim is completed; Hack so the server doesn't get stuck in idle when its doing the Rage anim
var bool bDoneSpottedCheck;
var float RageFrustrationTimer; // Tracks how long we have been walking toward a visible enemy
var float RageFrustrationThreshhold; // Base value for how long the FP should walk torward an enemy without reaching them before getting frustrated and raging
simulated function PostBeginPlay(){
super.PostBeginPlay();
RageFrustrationTimer = 0;
}
function Tick(float Delta){
local bool bSeesPlayers;
local Controller PC;
local KFHumanPawn Human;
local NiceZombieFleshPound ZFP;
super.Tick(Delta);
bSeesPlayers = false;
for(PC = Level.ControllerList;PC != none;PC = PC.NextController){ Human = KFHumanPawn(PC.Pawn); if(Human != none && Human.Health > 0 && !Human.bPendingDelete && CanSee(Human)){ bSeesPlayers = true; break; }
}
if(bSeesPlayers){ if(RageFrustrationTimer < RageFrustrationThreshhold){ RageFrustrationTimer += Delta; if(RageFrustrationTimer >= RageFrustrationThreshhold){ ZFP = NiceZombieFleshPound(Pawn); if(ZFP != none && !ZFP.bChargingPlayer){ ZFP.StartChargingFP(Pawn(focus)); ZFP.bFrustrated = true; } } }
}
else RageFrustrationTimer = 0;
}
// Never do that, you too cool
function GetOutOfTheWayOfShot(vector ShotDirection, vector ShotOrigin){}
state ZombieHunt
{
event SeePlayer(Pawn SeenPlayer)
{ if ( !bDoneSpottedCheck && PlayerController(SeenPlayer.Controller) != none ) { // 25% chance of first player to see this Fleshpound saying something if ( !KFGameType(Level.Game).bDidSpottedFleshpoundMessage && FRand() < 0.25 ) { PlayerController(SeenPlayer.Controller).Speech('AUTO', 12, ""); KFGameType(Level.Game).bDidSpottedFleshpoundMessage = true; }
bDoneSpottedCheck = true; }
super.SeePlayer(SeenPlayer);
}
}
function TimedFireWeaponAtEnemy()
{
if ( (Enemy == none) || FireWeaponAt(Enemy) ) SetCombatTimer();
else SetTimer(0.01, True);
}
state SpinAttack
{
ignores EnemyNotVisible;
// Don't do this in this state
function GetOutOfTheWayOfShot(vector ShotDirection, vector ShotOrigin){}
function DoSpinDamage()
{ local Actor A;
//log("FLESHPOUND DOSPINDAMAGE!"); foreach CollidingActors(class'actor', A, (NiceZombieFleshpound(pawn).MeleeRange * 1.5)+pawn.CollisionRadius, pawn.Location) NiceZombieFleshpound(pawn).SpinDamage(A);
}
Begin:
WaitForAnim:
While( KFM.bShotAnim )
{ Sleep(0.1); DoSpinDamage();
}
WhatToDoNext(152);
if ( bSoaking ) SoakStop("STUCK IN SPINATTACK!!!");
}
state ZombieCharge
{
function bool StrafeFromDamage(float Damage, class<DamageType> DamageType, bool bFindDest)
{ return false;
}
// I suspect this function causes bloats to get confused
function bool TryStrafe(vector sideDir)
{ return false;
}
function Timer()
{ Disable('NotifyBump'); Target = Enemy; TimedFireWeaponAtEnemy();
}
WaitForAnim:
if ( Monster(Pawn).bShotAnim )
{ Goto('Moving');
}
if ( !FindBestPathToward(Enemy, false,true) ) GotoState('ZombieRestFormation');
Moving:
MoveToward(Enemy);
WhatToDoNext(17);
if ( bSoaking ) SoakStop("STUCK IN CHARGING!");
}
// Used to set a timeout for the WaitForAnim state. This is a bit of a hack fix
// for the FleshPound getting stuck in its idle anim on a dedicated server when it
// is supposed to be raging. For some reason, on a dedicated server only, it
// never gets an animend call for the PoundRage anim, instead the anim gets
// interrupted by the PoundIdle anim. If we figure that bug out, we can
// probably take this out in the future. But for now the fix works - Ramm
function SetPoundRageTimout(float NewRageTimeOut)
{
RageAnimTimeout = NewRageTimeOut;
}
state WaitForAnim
{
Ignores SeePlayer,HearNoise,Timer,EnemyNotVisible,NotifyBump;
// Don't do this in this state
function GetOutOfTheWayOfShot(vector ShotDirection, vector ShotOrigin){}
function BeginState()
{ bUseFreezeHack = False;
}
// The rage anim has ended, clear the flags and let the AI do its thing
function RageTimeout()
{ if( bUseFreezeHack ) { if( Pawn!=none ) { Pawn.AccelRate = Pawn.Default.AccelRate; Pawn.GroundSpeed = Pawn.Default.GroundSpeed; } bUseFreezeHack = False; AnimEnd(0); }
}
function Tick( float Delta )
{ Global.Tick(Delta);
if( RageAnimTimeout > 0 ) { RageAnimTimeout -= Delta;
if( RageAnimTimeout <= 0 ) { RageAnimTimeout = 0; RageTimeout(); } }
if( bUseFreezeHack ) { MoveTarget = none; MoveTimer = -1; Pawn.Acceleration = vect(0,0,0); Pawn.GroundSpeed = 1; Pawn.AccelRate = 0; }
}
function EndState()
{ if( Pawn!=none ) { Pawn.AccelRate = Pawn.Default.AccelRate; Pawn.GroundSpeed = Pawn.Default.GroundSpeed; } bUseFreezeHack = False;
}
Begin:
While( KFM.bShotAnim )
{ Sleep(0.15);
}
WhatToDoNext(99);
}
defaultproperties
{ RageFrustrationThreshhold=10.000000
}

View file

@ -0,0 +1,139 @@
// Completely invisible Stalker
// (c) PooSH, 2014
// used 'ClawAndMove' code from Scary Ghost's SuperStaler
// Ported to 'NiceMonster' parent class
class NiceZombieGhost extends NiceZombieStalker;
// max distance squared for player to see cloacked Stalkers.
// Beyond that distance Stalkers will appear completely invisible.
var float CloakDistanceSqr;
// Unclock distance squared for Commandos
var float UncloakDistanceSqr;
var const Material CloakMat;
var const Material InvisibleMat;
var const Material UncloakMat, UncloakFBMat;
var const Material GlowFX;
static simulated function PreCacheMaterials(LevelInfo myLevel)
{
myLevel.AddPrecacheMaterial(default.CloakMat);
myLevel.AddPrecacheMaterial(default.InvisibleMat);
myLevel.AddPrecacheMaterial(default.UncloakMat);
myLevel.AddPrecacheMaterial(default.UncloakFBMat);
myLevel.AddPrecacheMaterial(default.GlowFX);
myLevel.AddPrecacheMaterial(Combiner'KF_Specimens_Trip_T.stalker_env_cmb');
myLevel.AddPrecacheMaterial(Texture'KF_Specimens_Trip_T.stalker_diff');
myLevel.AddPrecacheMaterial(Texture'KF_Specimens_Trip_T.stalker_spec');
myLevel.AddPrecacheMaterial(Combiner'KF_Specimens_Trip_T.StalkerCloakOpacity_cmb');
myLevel.AddPrecacheMaterial(Material'KF_Specimens_Trip_T.StalkerCloakEnv_rot');
myLevel.AddPrecacheMaterial(Material'KF_Specimens_Trip_T.stalker_opacity_osc');
myLevel.AddPrecacheMaterial(Material'KFCharacters.StalkerSkin');
}
simulated function PostNetBeginPlay()
{
super.PostNetBeginPlay();
if ( LocalKFHumanPawn != none ) { CloakDistanceSqr = fmax(CloakDistanceSqr, 3.0 * UncloakDistanceSqr * LocalKFHumanPawn.GetStalkerViewDistanceMulti()); UncloakDistanceSqr *= LocalKFHumanPawn.GetStalkerViewDistanceMulti();
}
}
function RenderOverlays(Canvas Canvas)
{
Canvas.SetDrawColor(0, 92, 255, 255);
super.RenderOverlays(Canvas);
}
// makes Stalker invisible or glowing (for commando)
simulated function CloakStalker()
{
// No cloaking if zapped
if( bZapped )
{ return;
}
if ( bSpotted ) { if( Level.NetMode == NM_DedicatedServer ) return;
Skins[0] = GlowFX; Skins[1] = GlowFX; bUnlit = true;
}
else if ( !bDecapitated && !bCrispified ) // No head, no cloak, honey. updated : Being charred means no cloak either :D
{ Visibility = 1; bCloaked = true;
if( Level.NetMode == NM_DedicatedServer ) Return;
Skins[0] = InvisibleMat; Skins[1] = InvisibleMat; bUnlit = false;
// Invisible - no shadow if(PlayerShadow != none) PlayerShadow.bShadowActive = false; if(RealTimeShadow != none) RealTimeShadow.Destroy();
// Remove/disallow projectors on invisible people Projectors.Remove(0, Projectors.Length); bAcceptsProjectors = false; SetOverlayMaterial(Material'KFX.FBDecloakShader', 0.25, true);
}
}
simulated function UnCloakStalker()
{
if( bZapped )
{ return;
}
if( !bCrispified )
{ LastUncloakTime = Level.TimeSeconds;
Visibility = default.Visibility; bCloaked = false; bUnlit = false;
// 25% chance of our Enemy saying something about us being invisible if( Level.NetMode!=NM_Client && !KFGameType(Level.Game).bDidStalkerInvisibleMessage && FRand()<0.25 && Controller.Enemy!=none && PlayerController(Controller.Enemy.Controller)!=none ) { PlayerController(Controller.Enemy.Controller).Speech('AUTO', 17, ""); KFGameType(Level.Game).bDidStalkerInvisibleMessage = true; } if( Level.NetMode == NM_DedicatedServer ) Return;
if ( Skins[0] != UncloakMat ) { Skins[1] = FinalBlend'KF_Specimens_Trip_T.stalker_fb'; Skins[0] = UncloakMat;
if (PlayerShadow != none) PlayerShadow.bShadowActive = true;
bAcceptsProjectors = true;
SetOverlayMaterial(Material'KFX.FBDecloakShader', 0.25, true); }
}
}
function RemoveHead()
{
Super(NiceMonster).RemoveHead();
if (!bCrispified)
{ Skins[1] = UncloakFBMat; Skins[0] = UncloakMat;
}
}
simulated function PlayDying(class<DamageType> DamageType, vector HitLoc)
{
Super(NiceMonster).PlayDying(DamageType,HitLoc);
if(bUnlit) bUnlit=!bUnlit;
LocalKFHumanPawn = none;
if (!bCrispified)
{ Skins[1] = UncloakFBMat; Skins[0] = UncloakMat;
}
}
simulated function Tick(float DeltaTime)
{
local float DistanceSqr;
Super(NiceMonster).Tick(DeltaTime);
// Keep the stalker moving toward its target when attacking
if( Role == ROLE_Authority && bShotAnim && !bWaitForAnim && !bZapped ) { if( LookTarget!=none ) { Acceleration = AccelRate * Normal(LookTarget.Location - Location); }
}
if( Level.NetMode==NM_DedicatedServer ) Return; // Servers aren't intrested in this info.
if( bZapped ) { // Make sure we check if we need to be cloaked as soon as the zap wears off NextCheckTime = Level.TimeSeconds;
}
else if( Level.TimeSeconds > NextCheckTime && Health > 0 )
{ NextCheckTime = Level.TimeSeconds + 0.5;
bSpotted = false; if ( LocalKFHumanPawn != none ) { DistanceSqr = VSizeSquared(Location - LocalKFHumanPawn.Location); if( LocalKFHumanPawn.Health > 0 && LocalKFHumanPawn.ShowStalkers() && DistanceSqr < UncloakDistanceSqr ) { bSpotted = True; if ( Skins[0] != GlowFX ) { Skins[0] = GlowFX; Skins[1] = GlowFX; bUnlit = true; } } else if ( DistanceSqr < CloakDistanceSqr ) { if ( bCloaked && Skins[0] != CloakMat ) { Skins[0] = CloakMat; Skins[1] = CloakMat; bUnlit = false; } } else if ( Skins[0] != InvisibleMat ) { CloakStalker(); } }
}
}
function RangedAttack(Actor A)
{
if ( !bShotAnim && Physics != PHYS_Swimming && CanAttack(A) ) { bShotAnim = true; SetAnimAction('ClawAndMove');
}
}
// copied from ZombieSuperStalker (c) Scary Ghost
simulated event SetAnimAction(name NewAction)
{
if( NewAction=='' ) Return;
ExpectingChannel = AttackAndMoveDoAnimAction(NewAction);
bWaitForAnim= false;
if( Level.NetMode!=NM_Client ) { AnimAction = NewAction; bResetAnimAct = True; ResetAnimActTime = Level.TimeSeconds+0.3;
}
}
// copied from ZombieSuperStalker (c) Scary Ghost
simulated function int AttackAndMoveDoAnimAction( name AnimName )
{
local int meleeAnimIndex;
local float duration;
if( AnimName == 'ClawAndMove' ) { meleeAnimIndex = Rand(3); AnimName = meleeAnims[meleeAnimIndex];
duration= GetAnimDuration(AnimName, 1.0);
}
if( AnimName=='StalkerSpinAttack' || AnimName=='StalkerAttack1' || AnimName=='JumpAttack') { AnimBlendParams(1, 1.0, 0.0,, FireRootBone); PlayAnim(AnimName,, 0.1, 1);
return 1;
}
return super.DoAnimAction( AnimName );
}
defaultproperties
{ CloakDistanceSqr=62500.000000 UncloakDistanceSqr=360000.000000 CloakMat=Shader'KF_Specimens_Trip_T.stalker_invisible' InvisibleMat=Shader'KF_Specimens_Trip_T.patriarch_invisible' UncloakMat=Shader'KF_Specimens_Trip_T.stalker_invisible' UncloakFBMat=Shader'KF_Specimens_Trip_T.stalker_invisible' MenuName="Ghost" Skins(0)=Shader'KF_Specimens_Trip_T.patriarch_invisible' Skins(1)=Shader'KF_Specimens_Trip_T.patriarch_invisible'
}

View file

@ -0,0 +1,18 @@
// Zombie Monster for KF Invasion gametype
// He's speedy, and swings with a Single enlongated arm, affording him slightly more range
class NiceZombieGoreFastBase extends NiceMonster
abstract;
#exec OBJ LOAD FILE=
var bool bRunning;
var float RunAttackTimeout;
replication
{
reliable if(Role == ROLE_Authority) bRunning;
}
//-------------------------------------------------------------------------------
// NOTE: All Code resides in the child class(this class was only created to
// eliminate hitching caused by loading default properties during play)
//-------------------------------------------------------------------------------
defaultproperties
{ fuelRatio=0.650000 clientHeadshotScale=1.600000 MeleeAnims(0)="GoreAttack1" MeleeAnims(1)="GoreAttack2" MeleeAnims(2)="GoreAttack1" bCannibal=True MeleeDamage=15 damageForce=5000 KFRagdollName="GoreFast_Trip" CrispUpThreshhold=8 bUseExtendedCollision=True ColOffset=(Z=52.000000) ColRadius=25.000000 ColHeight=10.000000 ExtCollAttachBoneName="Collision_Attach" SeveredArmAttachScale=0.900000 SeveredLegAttachScale=0.900000 PlayerCountHealthScale=0.150000 OnlineHeadshotOffset=(X=5.000000,Z=53.000000) OnlineHeadshotScale=1.500000 MotionDetectorThreat=0.500000 ScoringValue=12 IdleHeavyAnim="GoreIdle" IdleRifleAnim="GoreIdle" MeleeRange=30.000000 GroundSpeed=120.000000 WaterSpeed=140.000000 HealthMax=250.000000 Health=250 HeadHeight=2.500000 HeadScale=1.500000 MenuName="Nice Gorefast" MovementAnims(0)="GoreWalk" WalkAnims(0)="GoreWalk" WalkAnims(1)="GoreWalk" WalkAnims(2)="GoreWalk" WalkAnims(3)="GoreWalk" IdleCrouchAnim="GoreIdle" IdleWeaponAnim="GoreIdle" IdleRestAnim="GoreIdle" DrawScale=1.200000 PrePivot=(Z=10.000000) Mass=350.000000 RotationRate=(Yaw=45000,Roll=0)
}

View file

@ -0,0 +1,153 @@
// Zombie Monster for KF Invasion gametype
// GOREFAST.
// He's speedy, and swings with a Single enlongated arm, affording him slightly more range
class NiceZombieGoreFast extends NiceZombieGoreFastBase;
#exec OBJ LOAD FILE=
//----------------------------------------------------------------------------
// NOTE: All Variables are declared in the base class to eliminate hitching
//----------------------------------------------------------------------------
simulated function PostNetReceive(){
if(bRunning) MovementAnims[0] = 'ZombieRun';
else MovementAnims[0] = default.MovementAnims[0];
}
// This zed has been taken control of. Boost its health and speed
function SetMindControlled(bool bNewMindControlled)
{
if( bNewMindControlled )
{ NumZCDHits++;
// if we hit him a couple of times, make him rage! if( NumZCDHits > 1 ) { if( !IsInState('RunningToMarker') ) { GotoState('RunningToMarker'); } else { NumZCDHits = 1; if( IsInState('RunningToMarker') ) { GotoState(''); } } } else { if( IsInState('RunningToMarker') ) { GotoState(''); } }
if( bNewMindControlled != bZedUnderControl ) { SetGroundSpeed(OriginalGroundSpeed * 1.25); Health *= 1.25; HealthMax *= 1.25; }
}
else
{ NumZCDHits=0;
}
bZedUnderControl = bNewMindControlled;
}
// Handle the zed being commanded to move to a new location
function GivenNewMarker()
{
if( bRunning && NumZCDHits > 1 )
{ GotoState('RunningToMarker');
}
else
{ GotoState('');
}
}
function RangedAttack(Actor A){
super.RangedAttack(A);
if(!bShotAnim && !bDecapitated && VSize(A.Location - Location) <= 700) GoToState('RunningState');
}
simulated function Tick(float DeltaTime){
super.Tick(DeltaTime);
if(IsInState('RunningState')) SetGroundSpeed(GetOriginalGroundSpeed() * 1.875);
else SetGroundSpeed(GetOriginalGroundSpeed());
}
state RunningState{
// Set the zed to the zapped behavior
simulated function SetZappedBehavior(){ Global.SetZappedBehavior(); GoToState('');
}
// Don't override speed in this state
function bool CanSpeedAdjust(){ return false;
}
simulated function BeginState(){ if(bZapped) GoToState(''); else{ SetGroundSpeed(OriginalGroundSpeed * 1.875); bRunning = true; if(Level.NetMode != NM_DedicatedServer) PostNetReceive();
NetUpdateTime = Level.TimeSeconds - 1; }
}
function EndState(){ SetGroundSpeed(GetOriginalGroundSpeed()); bRunning = false; if(Level.NetMode != NM_DedicatedServer) PostNetReceive();
RunAttackTimeout=0;
NetUpdateTime = Level.TimeSeconds - 1;
}
function RemoveHead(){ GoToState(''); Global.RemoveHead();
}
function RangedAttack(Actor A){
if(bShotAnim || Physics == PHYS_Swimming) return; else if (CanAttack(A)){ bShotAnim = true;
// Randomly do a moving attack so the player can't kite the zed if(FRand() < 0.4){ SetAnimAction('ClawAndMove'); RunAttackTimeout = GetAnimDuration('GoreAttack1', 1.0); } else{ SetAnimAction('Claw'); Controller.bPreparingMove = true; Acceleration = vect(0,0,0); // Once we attack stop running GoToState(''); } return; }
}
simulated function Tick(float DeltaTime){ // Keep moving toward the target until the timer runs out (anim finishes) if(RunAttackTimeout > 0){ RunAttackTimeout -= DeltaTime;
if(RunAttackTimeout <= 0 && !bZedUnderControl){ RunAttackTimeout = 0; GoToState(''); } }
// Keep the gorefast moving toward its target when attacking if(Role == ROLE_Authority && bShotAnim && !bWaitForAnim){ if(LookTarget != none) Acceleration = AccelRate * Normal(LookTarget.Location - Location); } global.Tick(DeltaTime);
}
Begin:
GoTo('CheckCharge');
CheckCharge:
if( Controller!=none && Controller.Target!=none && VSize(Controller.Target.Location-Location)<700 )
{ Sleep(0.5+ FRand() * 0.5); //log("Still charging"); GoTo('CheckCharge');
}
else
{ //log("Done charging"); GoToState('');
}
}
// State where the zed is charging to a marked location.
state RunningToMarker extends RunningState
{
simulated function Tick(float DeltaTime)
{ // Keep moving toward the target until the timer runs out (anim finishes) if( RunAttackTimeout > 0 ) { RunAttackTimeout -= DeltaTime;
if( RunAttackTimeout <= 0 && !bZedUnderControl ) { RunAttackTimeout = 0; GoToState(''); } }
// Keep the gorefast moving toward its target when attacking if( Role == ROLE_Authority && bShotAnim && !bWaitForAnim ) { if( LookTarget!=none ) { Acceleration = AccelRate * Normal(LookTarget.Location - Location); } }
global.Tick(DeltaTime);
}
Begin:
GoTo('CheckCharge');
CheckCharge:
if( bZedUnderControl || (Controller!=none && Controller.Target!=none && VSize(Controller.Target.Location-Location)<700) )
{ Sleep(0.5+ FRand() * 0.5); GoTo('CheckCharge');
}
else
{ GoToState('');
}
}
// Overridden to handle playing upper body only attacks when moving
simulated event SetAnimAction(name NewAction)
{
local int meleeAnimIndex;
local bool bWantsToAttackAndMove;
if( NewAction=='' ) Return;
bWantsToAttackAndMove = NewAction == 'ClawAndMove';
if( NewAction == 'Claw' )
{ meleeAnimIndex = Rand(3); NewAction = meleeAnims[meleeAnimIndex];
}
if( bWantsToAttackAndMove )
{ ExpectingChannel = AttackAndMoveDoAnimAction(NewAction);
}
else
{ ExpectingChannel = DoAnimAction(NewAction);
}
if( !bWantsToAttackAndMove && AnimNeedsWait(NewAction) )
{ bWaitForAnim = true;
}
else
{ bWaitForAnim = false;
}
if( Level.NetMode!=NM_Client )
{ AnimAction = NewAction; bResetAnimAct = True; ResetAnimActTime = Level.TimeSeconds+0.3;
}
}
// Handle playing the anim action on the upper body only if we're attacking and moving
simulated function int AttackAndMoveDoAnimAction( name AnimName )
{
local int meleeAnimIndex;
if( AnimName == 'ClawAndMove' )
{ meleeAnimIndex = Rand(3); AnimName = meleeAnims[meleeAnimIndex];
}
if( AnimName=='GoreAttack1' || AnimName=='GoreAttack2' )
{ AnimBlendParams(1, 1.0, 0.0,, FireRootBone); PlayAnim(AnimName,, 0.1, 1);
return 1;
}
return super.DoAnimAction( AnimName );
}
simulated function HideBone(name boneName)
{
// Gorefast does not have a left arm and does not need it to be hidden
if (boneName != LeftFArmBone)
{ super.HideBone(boneName);
}
}
static simulated function PreCacheMaterials(LevelInfo myLevel)
{//should be derived and used.
myLevel.AddPrecacheMaterial(Combiner'KF_Specimens_Trip_T.gorefast_cmb');
myLevel.AddPrecacheMaterial(Combiner'KF_Specimens_Trip_T.gorefast_env_cmb');
myLevel.AddPrecacheMaterial(Texture'KF_Specimens_Trip_T.gorefast_diff');
}
defaultproperties
{ stunLoopStart=0.287500 stunLoopEnd=0.637500 idleInsertFrame=0.750000 EventClasses(0)="NicePack.NiceZombieGorefast" MoanVoice=SoundGroup'KF_EnemiesFinalSnd.GoreFast.Gorefast_Talk' MeleeAttackHitSound=SoundGroup'KF_EnemiesFinalSnd.GoreFast.Gorefast_HitPlayer' JumpSound=SoundGroup'KF_EnemiesFinalSnd.GoreFast.Gorefast_Jump' DetachedArmClass=Class'KFChar.SeveredArmGorefast' DetachedLegClass=Class'KFChar.SeveredLegGorefast' DetachedHeadClass=Class'KFChar.SeveredHeadGorefast' bLeftArmGibbed=True HitSound(0)=SoundGroup'KF_EnemiesFinalSnd.GoreFast.Gorefast_Pain' DeathSound(0)=SoundGroup'KF_EnemiesFinalSnd.GoreFast.Gorefast_Death' ChallengeSound(0)=SoundGroup'KF_EnemiesFinalSnd.GoreFast.Gorefast_Challenge' ChallengeSound(1)=SoundGroup'KF_EnemiesFinalSnd.GoreFast.Gorefast_Challenge' ChallengeSound(2)=SoundGroup'KF_EnemiesFinalSnd.GoreFast.Gorefast_Challenge' ChallengeSound(3)=SoundGroup'KF_EnemiesFinalSnd.GoreFast.Gorefast_Challenge' ControllerClass=Class'NicePack.NiceZombieGorefastController' AmbientSound=Sound'KF_BaseGorefast.Gorefast_Idle' Mesh=SkeletalMesh'KF_Freaks_Trip.GoreFast_Freak' Skins(0)=Combiner'KF_Specimens_Trip_T.gorefast_cmb'
}

View file

@ -0,0 +1,10 @@
// Custom code to make the Gorefast act abit more interesting.
class NiceZombieGorefastController extends NiceMonsterController;
var bool bDoneSpottedCheck;
state ZombieHunt{
event SeePlayer(Pawn SeenPlayer){ if(!bDoneSpottedCheck && PlayerController(SeenPlayer.Controller) != none){ // 25% chance of first player to see this Gorefast saying something if(!KFGameType(Level.Game).bDidSpottedGorefastMessage && FRand() < 0.25){ PlayerController(SeenPlayer.Controller).Speech('AUTO', 13, ""); KFGameType(Level.Game).bDidSpottedGorefastMessage = true; } bDoneSpottedCheck = true; } global.SeePlayer(SeenPlayer);
}
}
defaultproperties
{ StrafingAbility=0.500000
}

View file

@ -0,0 +1,383 @@
//=============================================================================
// ZombieHusk
//=============================================================================
// Husk burned up fire projectile launching zed pawn class
//=============================================================================
// Killing Floor Source
// Copyright (C) 2009 Tripwire Interactive LLC
// - John "Ramm-Jaeger" Gibson
//=============================================================================
class NiceZombieHusk extends NiceZombieHuskBase;
//----------------------------------------------------------------------------
// NOTE: All Variables are declared in the base class to eliminate hitching
//----------------------------------------------------------------------------
var class<Projectile> HuskFireProjClass;
simulated function HeatTick(){
if(heat < 50){
if(heat > 40)
heat = 50;
else
heat = 50 - 0.8 * (50 - heat);
}
super.HeatTick();
}
simulated function PostBeginPlay()
{
// Difficulty Scaling
if (Level.Game != none && !bDiffAdjusted){
ProjectileFireInterval = default.ProjectileFireInterval * 0.6;
}
super.PostBeginPlay();
}
// don't interrupt the bloat while he is puking
simulated function bool HitCanInterruptAction()
{
if( bShotAnim )
{
return false;
}
return true;
}
function DoorAttack(Actor A)
{
if ( bShotAnim || Physics == PHYS_Swimming)
return;
else if ( A!=none )
{
bShotAnim = true;
if( !bDecapitated && bDistanceAttackingDoor )
{
SetAnimAction('ShootBurns');
}
else
{
SetAnimAction('DoorBash');
GotoState('DoorBashing');
}
}
}
function RangedAttack(Actor A)
{
local int LastFireTime;
if ( bShotAnim )
return;
if ( Physics == PHYS_Swimming )
{
SetAnimAction('Claw');
bShotAnim = true;
LastFireTime = Level.TimeSeconds;
}
else if ( VSize(A.Location - Location) < MeleeRange + CollisionRadius + A.CollisionRadius )
{
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 ( (KFDoorMover(A) != none ||
(!Region.Zone.bDistanceFog && VSize(A.Location-Location) <= 65535) ||
(Region.Zone.bDistanceFog && VSizeSquared(A.Location-Location) < (Square(Region.Zone.DistanceFogEnd) * 0.8))) // Make him come out of the fog a bit
&& !bDecapitated )
{
bShotAnim = true;
SetAnimAction('ShootBurns');
Controller.bPreparingMove = true;
Acceleration = vect(0,0,0);
NextFireProjectileTime = Level.TimeSeconds + ProjectileFireInterval + (FRand() * 2.0);
}
}
// Overridden to handle playing upper body only attacks when moving
simulated event SetAnimAction(name NewAction)
{
local int meleeAnimIndex;
local bool bWantsToAttackAndMove;
if( NewAction=='' )
Return;
if( NewAction == 'Claw' )
{
meleeAnimIndex = Rand(3);
NewAction = meleeAnims[meleeAnimIndex];
}
ExpectingChannel = DoAnimAction(NewAction);
if( !bWantsToAttackAndMove && AnimNeedsWait(NewAction) )
{
bWaitForAnim = true;
}
else
{
bWaitForAnim = false;
}
if( Level.NetMode!=NM_Client )
{
AnimAction = NewAction;
bResetAnimAct = True;
ResetAnimActTime = Level.TimeSeconds+0.3;
}
}
function float GetStunDurationMult(Pawn instigatedBy, Vector hitLocation, Vector momentum, class<NiceWeaponDamageType> damageType,
float headshotLevel, KFPlayerReplicationInfo KFPRI){
if(headshotLevel > 0)
return 1.0;
return 0.5;
}
function SpawnTwoShots(){
local vector X,Y,Z, FireStart;
local rotator FireRotation;
local KFMonsterController KFMonstControl;
if(controller != none && KFDoorMover(controller.Target) != none){
controller.Target.TakeDamage(22,Self,Location,vect(0,0,0),Class'DamTypeVomit');
return;
}
GetAxes(Rotation,X,Y,Z);
FireStart = GetBoneCoords('Barrel').Origin;
if(!SavedFireProperties.bInitialized){
SavedFireProperties.AmmoClass = Class'SkaarjAmmo';
SavedFireProperties.ProjectileClass = HuskFireProjClass;
SavedFireProperties.WarnTargetPct = 1;
SavedFireProperties.MaxRange = 65535;
SavedFireProperties.bTossed = false;
SavedFireProperties.bTrySplash = true;
SavedFireProperties.bLeadTarget = true;
SavedFireProperties.bInstantHit = false;
SavedFireProperties.bInitialized = true;
}
// Turn off extra collision before spawning vomit, otherwise spawn fails
ToggleAuxCollision(false);
if(controller != none)
FireRotation = controller.AdjustAim(SavedFireProperties,FireStart,600);
foreach DynamicActors(class'KFMonsterController', KFMonstControl)
if(KFMonstControl != controller && PointDistToLine(KFMonstControl.Pawn.Location, vector(FireRotation), FireStart) < 75)
KFMonstControl.GetOutOfTheWayOfShot(vector(FireRotation),FireStart);
Spawn(HuskFireProjClass,,, FireStart, FireRotation);
// Turn extra collision back on
ToggleAuxCollision(true);
}
// Get the closest point along a line to another point
simulated function float PointDistToLine(vector Point, vector Line, vector Origin, optional out vector OutClosestPoint)
{
local vector SafeDir;
SafeDir = Normal(Line);
OutClosestPoint = Origin + (SafeDir * ((Point-Origin) dot SafeDir));
return VSize(OutClosestPoint-Point);
}
simulated function Tick(float deltatime)
{
Super.tick(deltatime);
// Hack to force animation updates on the server for the bloat if he is relevant to someone
// He has glitches when some of his animations don't play on the server. If we
// find some other fix for the glitches take this out - Ramm
if( Level.NetMode != NM_Client && Level.NetMode != NM_Standalone )
{
if( (Level.TimeSeconds-LastSeenOrRelevantTime) < 1.0 )
{
bForceSkelUpdate=true;
}
else
{
bForceSkelUpdate=false;
}
}
}
function RemoveHead()
{
bCanDistanceAttackDoors = False;
Super.RemoveHead();
}
function ModDamage(out int damage, Pawn instigatedBy, Vector hitlocation, Vector momentum, class<NiceWeaponDamageType> damageType, float headshotLevel, KFPlayerReplicationInfo KFPRI, optional float lockonTime){
local float damageMod;
if(damageType != none){
damageMod = 0.2 * damageType.default.freezePower;
damageMod = FMax(damageMod, 1.0);
}
damage *= damageMod;
Super.ModDamage(damage, instigatedBy, hitlocation, momentum, damageType, headshotLevel, KFPRI);
}
simulated function ProcessHitFX()
{
local Coords boneCoords;
local class<xEmitter> HitEffects[4];
local int i,j;
local float GibPerterbation;
if( (Level.NetMode == NM_DedicatedServer) || bSkeletized || (Mesh == SkeletonMesh))
{
SimHitFxTicker = HitFxTicker;
return;
}
for ( SimHitFxTicker = SimHitFxTicker; SimHitFxTicker != HitFxTicker; SimHitFxTicker = (SimHitFxTicker + 1) % ArrayCount(HitFX) )
{
j++;
if ( j > 30 )
{
SimHitFxTicker = HitFxTicker;
return;
}
if( (HitFX[SimHitFxTicker].damtype == none) || (Level.bDropDetail && (Level.TimeSeconds - LastRenderTime > 3) && !IsHumanControlled()) )
continue;
//log("Processing effects for damtype "$HitFX[SimHitFxTicker].damtype);
if( HitFX[SimHitFxTicker].bone == 'obliterate' && !class'GameInfo'.static.UseLowGore())
{
SpawnGibs( HitFX[SimHitFxTicker].rotDir, 1);
bGibbed = true;
// Wait a tick on a listen server so the obliteration can replicate before the pawn is destroyed
if( Level.NetMode == NM_ListenServer )
{
bDestroyNextTick = true;
TimeSetDestroyNextTickTime = Level.TimeSeconds;
}
else
{
Destroy();
}
return;
}
boneCoords = GetBoneCoords( HitFX[SimHitFxTicker].bone );
if ( !Level.bDropDetail && !class'GameInfo'.static.NoBlood() && !bSkeletized && !class'GameInfo'.static.UseLowGore() )
{
//AttachEmitterEffect( BleedingEmitterClass, HitFX[SimHitFxTicker].bone, boneCoords.Origin, HitFX[SimHitFxTicker].rotDir );
HitFX[SimHitFxTicker].damtype.static.GetHitEffects( HitEffects, Health );
if( !PhysicsVolume.bWaterVolume ) // don't attach effects under water
{
for( i = 0; i < ArrayCount(HitEffects); i++ )
{
if( HitEffects[i] == none )
continue;
AttachEffect( HitEffects[i], HitFX[SimHitFxTicker].bone, boneCoords.Origin, HitFX[SimHitFxTicker].rotDir );
}
}
}
if ( class'GameInfo'.static.UseLowGore() )
HitFX[SimHitFxTicker].bSever = false;
if( HitFX[SimHitFxTicker].bSever )
{
GibPerterbation = HitFX[SimHitFxTicker].damtype.default.GibPerterbation;
switch( HitFX[SimHitFxTicker].bone )
{
case 'obliterate':
break;
case LeftThighBone:
if( !bLeftLegGibbed )
{
SpawnSeveredGiblet( DetachedLegClass, boneCoords.Origin, HitFX[SimHitFxTicker].rotDir, GibPerterbation, GetBoneRotation(HitFX[SimHitFxTicker].bone) );
KFSpawnGiblet( class 'KFMod.KFGibBrain',boneCoords.Origin, HitFX[SimHitFxTicker].rotDir, GibPerterbation, 250 ) ;
KFSpawnGiblet( class 'KFMod.KFGibBrainb',boneCoords.Origin, HitFX[SimHitFxTicker].rotDir, GibPerterbation, 250 ) ;
KFSpawnGiblet( class 'KFMod.KFGibBrain',boneCoords.Origin, HitFX[SimHitFxTicker].rotDir, GibPerterbation, 250 ) ;
bLeftLegGibbed=true;
}
break;
case RightThighBone:
if( !bRightLegGibbed )
{
SpawnSeveredGiblet( DetachedLegClass, boneCoords.Origin, HitFX[SimHitFxTicker].rotDir, GibPerterbation, GetBoneRotation(HitFX[SimHitFxTicker].bone) );
KFSpawnGiblet( class 'KFMod.KFGibBrain',boneCoords.Origin, HitFX[SimHitFxTicker].rotDir, GibPerterbation, 250 ) ;
KFSpawnGiblet( class 'KFMod.KFGibBrainb',boneCoords.Origin, HitFX[SimHitFxTicker].rotDir, GibPerterbation, 250 ) ;
KFSpawnGiblet( class 'KFMod.KFGibBrain',boneCoords.Origin, HitFX[SimHitFxTicker].rotDir, GibPerterbation, 250 ) ;
bRightLegGibbed=true;
}
break;
case LeftFArmBone:
if( !bLeftArmGibbed )
{
SpawnSeveredGiblet( DetachedArmClass, boneCoords.Origin, HitFX[SimHitFxTicker].rotDir, GibPerterbation, GetBoneRotation(HitFX[SimHitFxTicker].bone) );
KFSpawnGiblet( class 'KFMod.KFGibBrain',boneCoords.Origin, HitFX[SimHitFxTicker].rotDir, GibPerterbation, 250 ) ;
KFSpawnGiblet( class 'KFMod.KFGibBrainb',boneCoords.Origin, HitFX[SimHitFxTicker].rotDir, GibPerterbation, 250 ) ;;
bLeftArmGibbed=true;
}
break;
case RightFArmBone:
if( !bRightArmGibbed )
{
SpawnSeveredGiblet( DetachedSpecialArmClass, boneCoords.Origin, HitFX[SimHitFxTicker].rotDir, GibPerterbation, GetBoneRotation(HitFX[SimHitFxTicker].bone) );
KFSpawnGiblet( class 'KFMod.KFGibBrain',boneCoords.Origin, HitFX[SimHitFxTicker].rotDir, GibPerterbation, 250 ) ;
KFSpawnGiblet( class 'KFMod.KFGibBrainb',boneCoords.Origin, HitFX[SimHitFxTicker].rotDir, GibPerterbation, 250 ) ;
bRightArmGibbed=true;
}
break;
case 'head':
if( !bHeadGibbed )
{
if ( HitFX[SimHitFxTicker].damtype == class'DamTypeDecapitation' )
{
DecapFX( boneCoords.Origin, HitFX[SimHitFxTicker].rotDir, false);
}
else if( HitFX[SimHitFxTicker].damtype == class'DamTypeProjectileDecap' )
{
DecapFX( boneCoords.Origin, HitFX[SimHitFxTicker].rotDir, false, true);
}
else if( HitFX[SimHitFxTicker].damtype == class'DamTypeMeleeDecapitation' )
{
DecapFX( boneCoords.Origin, HitFX[SimHitFxTicker].rotDir, true);
}
bHeadGibbed=true;
}
break;
}
if( HitFX[SimHitFXTicker].bone != 'Spine' && HitFX[SimHitFXTicker].bone != FireRootBone &&
HitFX[SimHitFXTicker].bone != 'head' && Health <=0 )
HideBone(HitFX[SimHitFxTicker].bone);
}
}
}
function bool CheckStun(int stunScore, Pawn instigatedBy, Vector hitLocation, Vector momentum, class<NiceWeaponDamageType> damageType, float headshotLevel, KFPlayerReplicationInfo KFPRI){
if(Health > 0 && damageType != none && damageType.default.HeadShotDamageMult >= 1.2
&& stunScore >= 250 && ( /*(DamageType != class'NiceDamTypeMagnumPistol') ||*/ headshotLevel > 0.0) )//MEANTODO
return true;
return super.CheckStun(stunScore, instigatedBy, hitLocation, momentum, damageType, headshotLevel, KFPRI);
}
static simulated function PreCacheMaterials(LevelInfo myLevel)
{//should be derived and used.
myLevel.AddPrecacheMaterial(Texture'KF_Specimens_Trip_T_Two.burns_diff');
myLevel.AddPrecacheMaterial(Texture'KF_Specimens_Trip_T_Two.burns_emissive_mask');
myLevel.AddPrecacheMaterial(Combiner'KF_Specimens_Trip_T_Two.burns_energy_cmb');
myLevel.AddPrecacheMaterial(Combiner'KF_Specimens_Trip_T_Two.burns_env_cmb');
myLevel.AddPrecacheMaterial(Combiner'KF_Specimens_Trip_T_Two.burns_fire_cmb');
myLevel.AddPrecacheMaterial(Material'KF_Specimens_Trip_T_Two.burns_shdr');
myLevel.AddPrecacheMaterial(Combiner'KF_Specimens_Trip_T_Two.burns_cmb');
}
defaultproperties
{
HuskFireProjClass=Class'NicePack.NiceHuskFireProjectile'
stunLoopStart=0.080000
stunLoopEnd=0.900000
idleInsertFrame=0.930000
Heat=50.000000
EventClasses(0)="NicePack.NiceZombieHusk"
MoanVoice=SoundGroup'KF_EnemiesFinalSnd.Husk.Husk_Talk'
MeleeAttackHitSound=SoundGroup'KF_EnemiesFinalSnd.Bloat.Bloat_HitPlayer'
JumpSound=SoundGroup'KF_EnemiesFinalSnd.Husk.Husk_Jump'
DetachedArmClass=Class'KFChar.SeveredArmHusk'
DetachedLegClass=Class'KFChar.SeveredLegHusk'
DetachedHeadClass=Class'KFChar.SeveredHeadHusk'
DetachedSpecialArmClass=Class'KFChar.SeveredArmHuskGun'
HitSound(0)=SoundGroup'KF_EnemiesFinalSnd.Husk.Husk_Pain'
DeathSound(0)=SoundGroup'KF_EnemiesFinalSnd.Husk.Husk_Death'
ChallengeSound(0)=SoundGroup'KF_EnemiesFinalSnd.Husk.Husk_Challenge'
ChallengeSound(1)=SoundGroup'KF_EnemiesFinalSnd.Husk.Husk_Challenge'
ChallengeSound(2)=SoundGroup'KF_EnemiesFinalSnd.Husk.Husk_Challenge'
ChallengeSound(3)=SoundGroup'KF_EnemiesFinalSnd.Husk.Husk_Challenge'
ControllerClass=Class'NicePack.NiceZombieHuskController'
AmbientSound=Sound'KF_BaseHusk.Husk_IdleLoop'
Mesh=SkeletalMesh'KF_Freaks2_Trip.Burns_Freak'
Skins(0)=Texture'KF_Specimens_Trip_T_Two.burns.burns_tatters'
}

View file

@ -0,0 +1,21 @@
//=============================================================================
// ZombieHusk
//=============================================================================
// Husk burned up fire projectile launching zed pawn class
//=============================================================================
// Killing Floor Source
// Copyright (C) 2009 Tripwire Interactive LLC
// - John "Ramm-Jaeger" Gibson
//=============================================================================
class NiceZombieHuskBase extends NiceMonster
abstract;
var float NextFireProjectileTime; // Track when we will fire again
var() float ProjectileFireInterval; // How often to fire the fire projectile
var() float BurnDamageScale; // How much to reduce fire damage for the Husk
//-------------------------------------------------------------------------------
// NOTE: All Code resides in the child class(this class was only created to
// eliminate hitching caused by loading default properties during play)
//-------------------------------------------------------------------------------
defaultproperties
{ ProjectileFireInterval=5.500000 BurnDamageScale=0.250000 bFireImmune=True bCanBurn=False fuelRatio=0.000000 clientHeadshotScale=1.100000 MeleeAnims(0)="Strike" MeleeAnims(1)="Strike" MeleeAnims(2)="Strike" BleedOutDuration=6.000000 ZapThreshold=0.750000 bHarpoonToBodyStuns=False ZombieFlag=1 MeleeDamage=15 damageForce=70000 bFatAss=True KFRagdollName="Burns_Trip" Intelligence=BRAINS_Mammal bCanDistanceAttackDoors=True bUseExtendedCollision=True ColOffset=(Z=36.000000) ColRadius=30.000000 ColHeight=33.000000 SeveredArmAttachScale=0.900000 SeveredLegAttachScale=0.900000 SeveredHeadAttachScale=0.900000 PlayerCountHealthScale=0.100000 OnlineHeadshotOffset=(X=20.000000,Z=55.000000) HeadHealth=200.000000 PlayerNumHeadHealthScale=0.050000 AmmunitionClass=Class'KFMod.BZombieAmmo' ScoringValue=17 IdleHeavyAnim="Idle" IdleRifleAnim="Idle" MeleeRange=30.000000 GroundSpeed=115.000000 WaterSpeed=102.000000 HealthMax=600.000000 Health=600 HeadHeight=1.000000 HeadScale=1.350000 AmbientSoundScaling=8.000000 MenuName="Nice Husk" MovementAnims(0)="WalkF" MovementAnims(1)="WalkB" MovementAnims(2)="WalkL" MovementAnims(3)="WalkR" WalkAnims(1)="WalkB" WalkAnims(2)="WalkL" WalkAnims(3)="WalkR" IdleCrouchAnim="Idle" IdleWeaponAnim="Idle" IdleRestAnim="Idle" DrawScale=1.400000 PrePivot=(Z=22.000000) Skins(1)=Shader'KF_Specimens_Trip_T_Two.burns.burns_shdr' SoundVolume=200 Mass=400.000000 RotationRate=(Yaw=45000,Roll=0)
}

View file

@ -0,0 +1,97 @@
//=============================================================================
// HuskZombieController
//=============================================================================
// Controller class for the husk zombie
//=============================================================================
// Killing Floor Source
// Copyright (C) 2009 Tripwire Interactive LLC
// - John "Ramm-Jaeger" Gibson
//=============================================================================
class NiceZombieHuskController extends NiceMonsterController;
// Overridden to create a delay between when the husk fires his projectiles
function bool FireWeaponAt(Actor A)
{
if ( A == none ) A = Enemy;
if ( (A == none) || (Focus != A) ) return false;
Target = A;
if( (VSize(A.Location - Pawn.Location) >= NiceZombieHusk(Pawn).MeleeRange + Pawn.CollisionRadius + Target.CollisionRadius) && NiceZombieHusk(Pawn).NextFireProjectileTime - Level.TimeSeconds > 0 )
{ return false;
}
Monster(Pawn).RangedAttack(Target);
return false;
}
/*
AdjustAim()
Returns a rotation which is the direction the bot should aim - after introducing the appropriate aiming error
Overridden to cause the zed to fire at the feet more often - Ramm
*/
function rotator AdjustAim(FireProperties FiredAmmunition, vector projStart, int aimerror)
{
local rotator FireRotation, TargetLook;
local float FireDist, TargetDist, ProjSpeed;
local actor HitActor;
local vector FireSpot, FireDir, TargetVel, HitLocation, HitNormal;
local int realYaw;
local bool bDefendMelee, bClean, bLeadTargetNow;
local bool bWantsToAimAtFeet;
if ( FiredAmmunition.ProjectileClass != none ) projspeed = FiredAmmunition.ProjectileClass.default.speed;
// make sure bot has a valid target
if ( Target == none )
{ Target = Enemy; if ( Target == none ) return Rotation;
}
FireSpot = Target.Location;
TargetDist = VSize(Target.Location - Pawn.Location);
// perfect aim at stationary objects
if ( Pawn(Target) == none )
{ if ( !FiredAmmunition.bTossed ) return rotator(Target.Location - projstart); else { FireDir = AdjustToss(projspeed,ProjStart,Target.Location,true); SetRotation(Rotator(FireDir)); return Rotation; }
}
bLeadTargetNow = FiredAmmunition.bLeadTarget && bLeadTarget;
bDefendMelee = ( (Target == Enemy) && DefendMelee(TargetDist) );
aimerror = AdjustAimError(aimerror,TargetDist,bDefendMelee,FiredAmmunition.bInstantHit, bLeadTargetNow);
// lead target with non instant hit projectiles
if ( bLeadTargetNow )
{ TargetVel = Target.Velocity; // hack guess at projecting falling velocity of target if ( Target.Physics == PHYS_Falling ) { if ( Target.PhysicsVolume.Gravity.Z <= Target.PhysicsVolume.Default.Gravity.Z ) TargetVel.Z = FMin(TargetVel.Z + FMax(-400, Target.PhysicsVolume.Gravity.Z * FMin(1,TargetDist/projSpeed)),0); else TargetVel.Z = FMin(0, TargetVel.Z); } // more or less lead target (with some random variation) FireSpot += FMin(1, 0.7 + 0.6 * FRand()) * TargetVel * TargetDist/projSpeed; FireSpot.Z = FMin(Target.Location.Z, FireSpot.Z);
if ( (Target.Physics != PHYS_Falling) && (FRand() < 0.55) && (VSize(FireSpot - ProjStart) > 1000) ) { // don't always lead far away targets, especially if they are moving sideways with respect to the bot TargetLook = Target.Rotation; if ( Target.Physics == PHYS_Walking ) TargetLook.Pitch = 0; bClean = ( ((Vector(TargetLook) Dot Normal(Target.Velocity)) >= 0.71) && FastTrace(FireSpot, ProjStart) ); } else // make sure that bot isn't leading into a wall bClean = FastTrace(FireSpot, ProjStart); if ( !bClean) { // reduce amount of leading if ( FRand() < 0.3 ) FireSpot = Target.Location; else FireSpot = 0.5 * (FireSpot + Target.Location); }
}
bClean = false; //so will fail first check unless shooting at feet
// Randomly determine if we should try and splash damage with the fire projectile
if( FiredAmmunition.bTrySplash )
{ if( Skill < 2.0 ) { if(FRand() > 0.85) { bWantsToAimAtFeet = true; } } else if( Skill < 3.0 ) { if(FRand() > 0.5) { bWantsToAimAtFeet = true; } } else if( Skill >= 3.0 ) { if(FRand() > 0.25) { bWantsToAimAtFeet = true; } }
}
if ( FiredAmmunition.bTrySplash && (Pawn(Target) != none) && (((Target.Physics == PHYS_Falling) && (Pawn.Location.Z + 80 >= Target.Location.Z)) || ((Pawn.Location.Z + 19 >= Target.Location.Z) && (bDefendMelee || bWantsToAimAtFeet))) )
{ HitActor = Trace(HitLocation, HitNormal, FireSpot - vect(0,0,1) * (Target.CollisionHeight + 10), FireSpot, false);
bClean = (HitActor == none); if ( !bClean ) { FireSpot = HitLocation + vect(0,0,3); bClean = FastTrace(FireSpot, ProjStart); } else bClean = ( (Target.Physics == PHYS_Falling) && FastTrace(FireSpot, ProjStart) );
}
if ( !bClean )
{ //try middle FireSpot.Z = Target.Location.Z; bClean = FastTrace(FireSpot, ProjStart);
}
if ( FiredAmmunition.bTossed && !bClean && bEnemyInfoValid )
{ FireSpot = LastSeenPos; HitActor = Trace(HitLocation, HitNormal, FireSpot, ProjStart, false); if ( HitActor != none ) { bCanFire = false; FireSpot += 2 * Target.CollisionHeight * HitNormal; } bClean = true;
}
if( !bClean )
{ // try head FireSpot.Z = Target.Location.Z + 0.9 * Target.CollisionHeight; bClean = FastTrace(FireSpot, ProjStart);
}
if ( !bClean && (Target == Enemy) && bEnemyInfoValid )
{ FireSpot = LastSeenPos; if ( Pawn.Location.Z >= LastSeenPos.Z ) FireSpot.Z -= 0.4 * Enemy.CollisionHeight; HitActor = Trace(HitLocation, HitNormal, FireSpot, ProjStart, false); if ( HitActor != none ) { FireSpot = LastSeenPos + 2 * Enemy.CollisionHeight * HitNormal; if ( Monster(Pawn).SplashDamage() && (Skill >= 4) ) { HitActor = Trace(HitLocation, HitNormal, FireSpot, ProjStart, false); if ( HitActor != none ) FireSpot += 2 * Enemy.CollisionHeight * HitNormal; } bCanFire = false; }
}
// adjust for toss distance
if ( FiredAmmunition.bTossed ) FireDir = AdjustToss(projspeed,ProjStart,FireSpot,true);
else FireDir = FireSpot - ProjStart;
FireRotation = Rotator(FireDir);
realYaw = FireRotation.Yaw;
InstantWarnTarget(Target,FiredAmmunition,vector(FireRotation));
FireRotation.Yaw = SetFireYaw(FireRotation.Yaw + aimerror);
FireDir = vector(FireRotation);
// avoid shooting into wall
FireDist = FMin(VSize(FireSpot-ProjStart), 400);
FireSpot = ProjStart + FireDist * FireDir;
HitActor = Trace(HitLocation, HitNormal, FireSpot, ProjStart, false);
if ( HitActor != none )
{ if ( HitNormal.Z < 0.7 ) { FireRotation.Yaw = SetFireYaw(realYaw - aimerror); FireDir = vector(FireRotation); FireSpot = ProjStart + FireDist * FireDir; HitActor = Trace(HitLocation, HitNormal, FireSpot, ProjStart, false); } if ( HitActor != none ) { FireSpot += HitNormal * 2 * Target.CollisionHeight; if ( Skill >= 4 ) { HitActor = Trace(HitLocation, HitNormal, FireSpot, ProjStart, false); if ( HitActor != none ) FireSpot += Target.CollisionHeight * HitNormal; } FireDir = Normal(FireSpot - ProjStart); FireRotation = rotator(FireDir); }
}
SetRotation(FireRotation);
return FireRotation;
}
defaultproperties
{
}

View file

@ -0,0 +1,83 @@
class NiceZombieJason extends NiceZombieScrake;
#exec load obj file=ScrnZedPack_T.utx
#exec load obj file=ScrnZedPack_S.uax
#exec load obj file=ScrnZedPack_A.ukx
var int OriginalMeleeDamage; // default melee damage, adjusted by game's difficulty
var bool bWasRaged; // set to true, if Jason is raged or was raged before
var float RageHealthPct;
var float RegenDelay;
var float RegenRate; // Speed of regeneration, in percents of max health
var float RegenAcc;
var float RegenAccHead;
simulated function PostBeginPlay(){
super.PostBeginPlay();
OriginalMeleeDamage = MeleeDamage;
}
function bool IsStunPossible(){
return false;
}
function bool CheckMiniFlinch(int flinchScore, Pawn instigatedBy, Vector hitLocation, Vector momentum, class<NiceWeaponDamageType> damageType, float headshotLevel, KFPlayerReplicationInfo KFPRI){
return super.CheckMiniFlinch(flinchScore * 1.5, instigatedBy, hitLocation, momentum, damageType, headshotLevel, KFPRI);
}
simulated function Tick(float DeltaTime){
super.Tick(DeltaTime);
if(Role < ROLE_Authority) return;
if(lastTookDamageTime + RegenDelay < Level.TimeSeconds && Health > 0){ RegenAcc += DeltaTime * RegenRate * HealthMax * 0.01; RegenAccHead += DeltaTime * RegenRate * HeadHealthMax * 0.01; if(RegenAcc > 1){ Health += RegenAcc; if(Health > HealthMax) Health = HealthMax; RegenAcc = 0.0; } if(RegenAccHead > 1){ HeadHealth += RegenAccHead; if(HeadHealth > HeadHealthMax) HeadHealth = HeadHealthMax; RegenAccHead = 0.0; }
}
else{ RegenAcc = 0.0; RegenAccHead = 0.0;
}
}
// Machete has no Exhaust ;)
simulated function SpawnExhaustEmitter(){}
simulated function UpdateExhaustEmitter(){}
function bool CanGetOutOfWay()
{
return !bIsStunned; // can't dodge husk fireballs while stunned
}
simulated function Unstun(){
bCharging = true;
MovementAnims[0] = 'ChargeF';
super.Unstun();
}
function TakeDamageClient(int Damage, Pawn InstigatedBy, Vector Hitlocation, Vector Momentum, class<NiceWeaponDamageType> damageType, float headshotLevel, float lockonTime){
Super.TakeDamageClient(Damage, instigatedBy, hitLocation, momentum, damageType, headshotLevel, lockonTime);
if(bIsStunned && Health > 0 && (headshotLevel <= 0.0) && Level.TimeSeconds > LastStunTime + 0.1) Unstun();
}
function TakeFireDamage(int Damage, Pawn Instigator){
Super.TakeFireDamage(Damage, Instigator);
if(bIsStunned && Health > 0 && Damage > 150 && Level.TimeSeconds > LastStunTime + 0.1) Unstun();
}
function RangedAttack(Actor A)
{
if ( bShotAnim || Physics == PHYS_Swimming) return;
else if ( CanAttack(A) )
{ bShotAnim = true; SetAnimAction(MeleeAnims[Rand(2)]); if(NiceMonster(A) == none) GoToState('SawingLoop');
}
if( !bShotAnim && !bDecapitated ) { if(bConfusedState) return; if ( bWasRaged || float(Health)/HealthMax < 0.5 || (float(Health)/HealthMax < RageHealthPct) ) GoToState('RunningState');
}
}
State SawingLoop
{
function RangedAttack(Actor A)
{ if ( bShotAnim ) return; else if ( CanAttack(A) ) { Acceleration = vect(0,0,0); bShotAnim = true; MeleeDamage = OriginalMeleeDamage * 0.6; SetAnimAction('SawImpaleLoop'); if( AmbientSound != SawAttackLoopSound ) { AmbientSound=SawAttackLoopSound; } } else GoToState('');
}
}
simulated function float GetOriginalGroundSpeed()
{
local float result;
result = OriginalGroundSpeed;
if ( bWasRaged || bCharging ) result *= 3.5;
else if( bZedUnderControl ) result *= 1.25; return result;
}
state RunningState
{
function BeginState()
{ local NiceHumanPawn rageTarget; bWasRaged = true; if(bWasCalm){ bWasCalm = false; rageTarget = NiceHumanPawn(Controller.focus); if( rageTarget != none && KFGameType(Level.Game) != none && class'NiceVeterancyTypes'.static.HasSkill(NicePlayerController(rageTarget.Controller), class'NiceSkillCommandoPerfectExecution') ){ KFGameType(Level.Game).DramaticEvent(1.0); } } if( bZapped ) GoToState(''); else { bCharging = true; SetGroundSpeed(GetOriginalGroundSpeed()); if( Level.NetMode!=NM_DedicatedServer ) PostNetReceive(); NetUpdateTime = Level.TimeSeconds - 1; }
}
function EndState()
{ bCharging = False; if( !bZapped ) SetGroundSpeed(GetOriginalGroundSpeed()); if( Level.NetMode!=NM_DedicatedServer ) PostNetReceive();
}
}
defaultproperties
{ RageHealthPct=0.750000 RegenDelay=5.000000 RegenRate=4.000000 SawAttackLoopSound=Sound'KF_BaseGorefast.Attack.Gorefast_AttackSwish3' ChainSawOffSound=None StunThreshold=1.000000 MoanVoice=None StunsRemaining=5 BleedOutDuration=7.000000 MeleeDamage=25 MeleeAttackHitSound=SoundGroup'KF_EnemiesFinalSnd.GoreFast.Gorefast_HitPlayer' JumpSound=None HeadHealth=800.000000 HitSound(0)=None DeathSound(0)=None ChallengeSound(0)=None ChallengeSound(1)=None ChallengeSound(2)=None ChallengeSound(3)=None ScoringValue=300 MenuName="Jason" AmbientSound=Sound'ScrnZedPack_S.Jason.Jason_Sound' Mesh=SkeletalMesh'ScrnZedPack_A.JasonMesh' Skins(0)=Shader'ScrnZedPack_T.Jason.Jason__FB' Skins(1)=Texture'ScrnZedPack_T.Jason.JVMaskB' Skins(2)=Combiner'ScrnZedPack_T.Jason.Machete_cmb'
}

View file

@ -0,0 +1,297 @@
// Chainsaw Zombie Monster for KF Invasion gametype
// He's not quite as speedy as the other Zombies, But his attacks are TRULY damaging.
class NiceZombieScrake extends NiceZombieScrakeBase;
var bool bConfusedState;
var bool bWasCalm;
//----------------------------------------------------------------------------
// NOTE: All Variables are declared in the base class to eliminate hitching
//----------------------------------------------------------------------------
simulated function PostNetBeginPlay()
{
EnableChannelNotify ( 1,1);
AnimBlendParams(1, 1.0, 0.0,, SpineBone1);
super.PostNetBeginPlay();
}
simulated function PostBeginPlay()
{
super.PostBeginPlay();
bWasCalm = true;
SpawnExhaustEmitter();
}
// Make the scrakes's ambient scale higher, since there are just a few, and thier chainsaw need to be heard from a distance
simulated function CalcAmbientRelevancyScale()
{
// Make the zed only relevant by their ambient sound out to a range of 30 meters
CustomAmbientRelevancyScale = 1500 / (100 * SoundRadius);
}
simulated function PostNetReceive()
{
if (bCharging) MovementAnims[0]='ChargeF';
else if( !(bCrispified && bBurnified) ) MovementAnims[0]=default.MovementAnims[0];
}
// Deprecated
function bool FlipOverWithIntsigator(Pawn InstigatedBy){
local bool bFlippedOver;
bFlippedOver = super.FlipOverWithIntsigator(InstigatedBy);
if(bFlippedOver){ // do not rotate while stunned Controller.Focus = none; Controller.FocalPoint = Location + 512*vector(Rotation);
}
return bFlippedOver;
}
function bool CanGetOutOfWay()
{
return false;
}
function float GetIceCrustScale(){
//return 25000 / (default.health * default.health);
return 0.01;
}
// This zed has been taken control of. Boost its health and speed
function SetMindControlled(bool bNewMindControlled)
{
if( bNewMindControlled )
{ NumZCDHits++;
// if we hit him a couple of times, make him rage! if( NumZCDHits > 1 ) { if( !IsInState('RunningToMarker') ) { GotoState('RunningToMarker'); } else { NumZCDHits = 1; if( IsInState('RunningToMarker') ) { GotoState(''); } } } else { if( IsInState('RunningToMarker') ) { GotoState(''); } }
if( bNewMindControlled != bZedUnderControl ) { SetGroundSpeed(OriginalGroundSpeed * 1.25); Health *= 1.25; HealthMax *= 1.25; }
}
else
{ NumZCDHits=0;
}
bZedUnderControl = bNewMindControlled;
}
// Handle the zed being commanded to move to a new location
function GivenNewMarker()
{
if( bCharging && NumZCDHits > 1 )
{ GotoState('RunningToMarker');
}
else
{ GotoState('');
}
}
simulated function SpawnExhaustEmitter()
{
if ( Level.NetMode != NM_DedicatedServer )
{ if ( ExhaustEffectClass != none ) { ExhaustEffect = Spawn(ExhaustEffectClass, self);
if ( ExhaustEffect != none ) { AttachToBone(ExhaustEffect, 'Chainsaw_lod1'); ExhaustEffect.SetRelativeLocation(vect(0, -20, 0)); } }
}
}
simulated function UpdateExhaustEmitter()
{
local byte Throttle;
if ( Level.NetMode != NM_DedicatedServer )
{ if ( ExhaustEffect != none ) { if ( bShotAnim ) { Throttle = 3; } else { Throttle = 0; } } else { if ( !bNoExhaustRespawn ) { SpawnExhaustEmitter(); } }
}
}
simulated function Tick(float DeltaTime)
{
super.Tick(DeltaTime);
UpdateExhaustEmitter();
}
function RangedAttack(Actor A)
{
if ( bShotAnim || Physics == PHYS_Swimming) return;
else if ( CanAttack(A) )
{ bShotAnim = true; SetAnimAction(MeleeAnims[Rand(2)]); //PlaySound(sound'Claw2s', SLOT_none); KFTODO: Replace this if(NiceMonster(A) == none) GoToState('SawingLoop');
}
if( !bShotAnim && !bDecapitated )
{ if(bConfusedState) return; if ( float(Health)/HealthMax < 0.75) GoToState('RunningState');
}
}
state RunningState
{
// Set the zed to the zapped behavior
simulated function SetZappedBehavior()
{ Global.SetZappedBehavior(); GoToState('');
}
// Don't override speed in this state
function bool CanSpeedAdjust()
{ return false;
}
simulated function float GetOriginalGroundSpeed() { return 3.5 * OriginalGroundSpeed;
}
function BeginState(){ local NiceHumanPawn rageTarget, rageCause;
if(Health <= 0) return;
if(bWasCalm){ bWasCalm = false; rageTarget = NiceHumanPawn(Controller.focus); rageCause = NiceHumanPawn(LastDamagedBy); if( rageTarget != none && KFGameType(Level.Game) != none && class'NiceVeterancyTypes'.static.HasSkill(NicePlayerController(rageTarget.Controller), class'NiceSkillCommandoPerfectExecution') ){ KFGameType(Level.Game).DramaticEvent(1.0); } else if( rageCause != none && KFGameType(Level.Game) != none && class'NiceVeterancyTypes'.static.HasSkill(NicePlayerController(rageCause.Controller), class'NiceSkillCommandoPerfectExecution') ){ KFGameType(Level.Game).DramaticEvent(1.0); } } if(bZapped) GoToState(''); else{ SetGroundSpeed(OriginalGroundSpeed * 3.5); bCharging = true; if( Level.NetMode!=NM_DedicatedServer ) PostNetReceive();
NetUpdateTime = Level.TimeSeconds - 1; }
}
function EndState()
{ if( !bZapped ) { SetGroundSpeed(GetOriginalGroundSpeed()); } bCharging = False; if( Level.NetMode!=NM_DedicatedServer ) PostNetReceive();
}
function RemoveHead()
{ GoToState(''); Global.RemoveHead();
}
function RangedAttack(Actor A)
{ if ( bShotAnim || Physics == PHYS_Swimming) return; else if ( CanAttack(A) ) { bShotAnim = true; SetAnimAction(MeleeAnims[Rand(2)]); if(NiceMonster(A) == none) GoToState('SawingLoop'); }
}
}
// State where the zed is charging to a marked location.
// Not sure if we need this since its just like RageCharging,
// but keeping it here for now in case we need to implement some
// custom behavior for this state
state RunningToMarker extends RunningState
{
}
State SawingLoop
{
// Don't override speed in this state
function bool CanSpeedAdjust()
{ return false;
}
simulated function float GetOriginalGroundSpeed() { return OriginalGroundSpeed * AttackChargeRate;
}
function bool CanGetOutOfWay()
{ return false;
}
function BeginState()
{ bConfusedState = false;
// Randomly have the scrake charge during an attack so it will be less predictable if(Health/HealthMax < 0.5 || FRand() <= 0.95) { SetGroundSpeed(OriginalGroundSpeed * AttackChargeRate); bCharging = true; if( Level.NetMode!=NM_DedicatedServer ) PostNetReceive();
NetUpdateTime = Level.TimeSeconds - 1; }
}
function RangedAttack(Actor A)
{ if ( bShotAnim ) return; else if ( CanAttack(A) ) { Acceleration = vect(0,0,0); bShotAnim = true; MeleeDamage = default.MeleeDamage*0.6; SetAnimAction('SawImpaleLoop'); if( AmbientSound != SawAttackLoopSound ) { AmbientSound=SawAttackLoopSound; } } else GoToState('');
}
function AnimEnd( int Channel )
{ Super.AnimEnd(Channel); if( Controller!=none && Controller.Enemy!=none ) RangedAttack(Controller.Enemy); // Keep on attacking if possible.
}
function Tick( float Delta )
{ // Keep the scrake moving toward its target when attacking if( Role == ROLE_Authority && bShotAnim && !bWaitForAnim ) { if( LookTarget!=none ) { Acceleration = AccelRate * Normal(LookTarget.Location - Location); } }
global.Tick(Delta);
}
function EndState()
{ AmbientSound=default.AmbientSound; MeleeDamage = Max( DifficultyDamageModifer() * default.MeleeDamage, 1 );
SetGroundSpeed(GetOriginalGroundSpeed()); bCharging = False; if(Level.NetMode != NM_DedicatedServer) PostNetReceive();
}
}
function ModDamage(out int Damage, Pawn instigatedBy, Vector hitLocation, Vector momentum, class<NiceWeaponDamageType> damageType, float headshotLevel, KFPlayerReplicationInfo KFPRI, optional float lockonTime){
super.ModDamage(Damage, instigatedBy, hitLocation, momentum, damageType, headshotLevel, KFPRI);
if(damageType == class'ScrnZedPack.DamTypeEMP') Damage *= 0.01;
}
function TakeDamageClient(int Damage, Pawn InstigatedBy, Vector Hitlocation, Vector Momentum, class<NiceWeaponDamageType> damageType, float headshotLevel, float lockonTime){
local bool bCanGetConfused;
local int OldHealth;
local PlayerController PC;
local KFSteamStatsAndAchievements Stats;
OldHealth = Health;
bCanGetConfused = false;
if(StunsRemaining != 0 && float(Health)/HealthMax >= 0.75) bCanGetConfused = true;
super.takeDamageClient(Damage, instigatedBy, hitLocation, momentum, damageType, headshotLevel, lockonTime);
if ( bCanGetConfused && !IsInState('SawingLoop') && (OldHealth - Health) <= (float(default.Health)/1.5) && float(Health)/HealthMax < 0.75 && (LastDamageAmount >= (0.5 * default.Health) || (VSize(LastDamagedBy.Location - Location) <= (MeleeRange * 2) && ClassIsChildOf(LastDamagedbyType,class 'DamTypeMelee') && KFPawn(LastDamagedBy) != none && LastDamageAmount > (0.10 * default.Health))) ) bConfusedState = true;
if(bConfusedState && Health > 0 && (headshotLevel <= 0.0) && damageType != none){ bConfusedState = false; GoToState('RunningState');
}
if(!bConfusedState && !IsInState('SawingLoop') && !IsInState('RunningState') && float(Health) / HealthMax < 0.75) RangedAttack(InstigatedBy);
if(damageType == class'DamTypeDBShotgun'){ PC = PlayerController( InstigatedBy.Controller ); if(PC != none){ Stats = KFSteamStatsAndAchievements( PC.SteamStatsAndAchievements ); if( Stats != none ) Stats.CheckAndSetAchievementComplete( Stats.KFACHIEVEMENT_PushScrakeSPJ ); }
}
}
function TakeFireDamage(int Damage, Pawn Instigator)
{
Super.TakeFireDamage(Damage, Instigator);
if(bConfusedState && Health > 0 && Damage > 150){ bConfusedState = false; GoToState('RunningState');
}
}
function bool CheckMiniFlinch(int flinchScore, Pawn instigatedBy, Vector hitLocation, Vector momentum, class<NiceWeaponDamageType> damageType, float headshotLevel, KFPlayerReplicationInfo KFPRI){
// Scrakes are better at enduring pain, so we need a bit more to flinch them
if(StunsRemaining == 0 || flinchScore < 150) return false;
return super.CheckMiniFlinch(flinchScore, instigatedBy, hitLocation, momentum, damageType, headshotLevel, KFPRI);
}
function DoStun(optional Pawn instigatedBy, optional Vector hitLocation, optional Vector momentum, optional class<NiceWeaponDamageType> damageType, optional float headshotLevel, optional KFPlayerReplicationInfo KFPRI){
super.DoStun(instigatedBy, hitLocation, momentum, damageType, headshotLevel, KFPRI);
StunsRemaining = 0;
}
simulated function int DoAnimAction( name AnimName )
{
if( AnimName=='SawZombieAttack1' || AnimName=='SawZombieAttack2' )
{ AnimBlendParams(1, 1.0, 0.0,, FireRootBone); PlayAnim(AnimName,, 0.1, 1); Return 1;
}
Return Super.DoAnimAction(AnimName);
}
simulated event SetAnimAction(name NewAction)
{
local int meleeAnimIndex;
if( NewAction=='' ) Return;
if(NewAction == 'Claw')
{ meleeAnimIndex = Rand(3); NewAction = meleeAnims[meleeAnimIndex];
}
ExpectingChannel = DoAnimAction(NewAction);
if( AnimNeedsWait(NewAction) )
{ bWaitForAnim = true;
}
if( Level.NetMode!=NM_Client )
{ AnimAction = NewAction; bResetAnimAct = True; ResetAnimActTime = Level.TimeSeconds+0.3;
}
}
// The animation is full body and should set the bWaitForAnim flag
simulated function bool AnimNeedsWait(name TestAnim)
{
if( TestAnim == 'SawImpaleLoop' || TestAnim == 'DoorBash' || TestAnim == 'KnockDown' )
{ return true;
}
return false;
}
function PlayDyingSound()
{
if( Level.NetMode!=NM_Client )
{ if ( bGibbed ) { // Do nothing for now PlaySound(GibGroupClass.static.GibSound(), SLOT_Pain,2.0,true,525); return; }
if( bDecapitated ) {
PlaySound(HeadlessDeathSound, SLOT_Pain,1.30,true,525); } else { PlaySound(DeathSound[0], SLOT_Pain,1.30,true,525); }
PlaySound(ChainSawOffSound, SLOT_Misc, 2.0,,525.0);
}
}
function Died(Controller Killer, class<DamageType> damageType, vector HitLocation)
{
AmbientSound = none;
if ( ExhaustEffect != none )
{ ExhaustEffect.Destroy(); ExhaustEffect = none; bNoExhaustRespawn = true;
}
super.Died( Killer, damageType, HitLocation );
}
simulated function ProcessHitFX()
{
local Coords boneCoords;
local class<xEmitter> HitEffects[4];
local int i,j;
local float GibPerterbation;
if( (Level.NetMode == NM_DedicatedServer) || bSkeletized || (Mesh == SkeletonMesh))
{ SimHitFxTicker = HitFxTicker; return;
}
for ( SimHitFxTicker = SimHitFxTicker; SimHitFxTicker != HitFxTicker; SimHitFxTicker = (SimHitFxTicker + 1) % ArrayCount(HitFX) )
{ j++; if ( j > 30 ) { SimHitFxTicker = HitFxTicker; return; }
if( (HitFX[SimHitFxTicker].damtype == none) || (Level.bDropDetail && (Level.TimeSeconds - LastRenderTime > 3) && !IsHumanControlled()) ) continue;
//log("Processing effects for damtype "$HitFX[SimHitFxTicker].damtype);
if( HitFX[SimHitFxTicker].bone == 'obliterate' && !class'GameInfo'.static.UseLowGore()) { SpawnGibs( HitFX[SimHitFxTicker].rotDir, 1); bGibbed = true; // Wait a tick on a listen server so the obliteration can replicate before the pawn is destroyed if( Level.NetMode == NM_ListenServer ) { bDestroyNextTick = true; TimeSetDestroyNextTickTime = Level.TimeSeconds; } else { Destroy(); } return; }
boneCoords = GetBoneCoords( HitFX[SimHitFxTicker].bone );
if ( !Level.bDropDetail && !class'GameInfo'.static.NoBlood() && !bSkeletized && !class'GameInfo'.static.UseLowGore() ) { //AttachEmitterEffect( BleedingEmitterClass, HitFX[SimHitFxTicker].bone, boneCoords.Origin, HitFX[SimHitFxTicker].rotDir );
HitFX[SimHitFxTicker].damtype.static.GetHitEffects( HitEffects, Health );
if( !PhysicsVolume.bWaterVolume ) // don't attach effects under water { for( i = 0; i < ArrayCount(HitEffects); i++ ) { if( HitEffects[i] == none ) continue;
AttachEffect( HitEffects[i], HitFX[SimHitFxTicker].bone, boneCoords.Origin, HitFX[SimHitFxTicker].rotDir ); } } } if ( class'GameInfo'.static.UseLowGore() ) HitFX[SimHitFxTicker].bSever = false;
if( HitFX[SimHitFxTicker].bSever ) { GibPerterbation = HitFX[SimHitFxTicker].damtype.default.GibPerterbation;
switch( HitFX[SimHitFxTicker].bone ) { case 'obliterate': break;
case LeftThighBone: if( !bLeftLegGibbed ) { SpawnSeveredGiblet( DetachedLegClass, boneCoords.Origin, HitFX[SimHitFxTicker].rotDir, GibPerterbation, GetBoneRotation(HitFX[SimHitFxTicker].bone) ); KFSpawnGiblet( class 'KFMod.KFGibBrain',boneCoords.Origin, HitFX[SimHitFxTicker].rotDir, GibPerterbation, 250 ) ; KFSpawnGiblet( class 'KFMod.KFGibBrainb',boneCoords.Origin, HitFX[SimHitFxTicker].rotDir, GibPerterbation, 250 ) ; KFSpawnGiblet( class 'KFMod.KFGibBrain',boneCoords.Origin, HitFX[SimHitFxTicker].rotDir, GibPerterbation, 250 ) ; bLeftLegGibbed=true; } break;
case RightThighBone: if( !bRightLegGibbed ) { SpawnSeveredGiblet( DetachedLegClass, boneCoords.Origin, HitFX[SimHitFxTicker].rotDir, GibPerterbation, GetBoneRotation(HitFX[SimHitFxTicker].bone) ); KFSpawnGiblet( class 'KFMod.KFGibBrain',boneCoords.Origin, HitFX[SimHitFxTicker].rotDir, GibPerterbation, 250 ) ; KFSpawnGiblet( class 'KFMod.KFGibBrainb',boneCoords.Origin, HitFX[SimHitFxTicker].rotDir, GibPerterbation, 250 ) ; KFSpawnGiblet( class 'KFMod.KFGibBrain',boneCoords.Origin, HitFX[SimHitFxTicker].rotDir, GibPerterbation, 250 ) ; bRightLegGibbed=true; } break;
case LeftFArmBone: if( !bLeftArmGibbed ) { SpawnSeveredGiblet( DetachedArmClass, boneCoords.Origin, HitFX[SimHitFxTicker].rotDir, GibPerterbation, GetBoneRotation(HitFX[SimHitFxTicker].bone) ); KFSpawnGiblet( class 'KFMod.KFGibBrain',boneCoords.Origin, HitFX[SimHitFxTicker].rotDir, GibPerterbation, 250 ) ; KFSpawnGiblet( class 'KFMod.KFGibBrainb',boneCoords.Origin, HitFX[SimHitFxTicker].rotDir, GibPerterbation, 250 ) ;; bLeftArmGibbed=true; } break;
case RightFArmBone: if( !bRightArmGibbed ) { SpawnSeveredGiblet( DetachedSpecialArmClass, boneCoords.Origin, HitFX[SimHitFxTicker].rotDir, GibPerterbation, GetBoneRotation(HitFX[SimHitFxTicker].bone) ); KFSpawnGiblet( class 'KFMod.KFGibBrain',boneCoords.Origin, HitFX[SimHitFxTicker].rotDir, GibPerterbation, 250 ) ; KFSpawnGiblet( class 'KFMod.KFGibBrainb',boneCoords.Origin, HitFX[SimHitFxTicker].rotDir, GibPerterbation, 250 ) ; bRightArmGibbed=true; } break;
case 'head': if( !bHeadGibbed ) { if ( HitFX[SimHitFxTicker].damtype == class'DamTypeDecapitation' ) { DecapFX( boneCoords.Origin, HitFX[SimHitFxTicker].rotDir, false); } else if( HitFX[SimHitFxTicker].damtype == class'DamTypeProjectileDecap' ) { DecapFX( boneCoords.Origin, HitFX[SimHitFxTicker].rotDir, false, true); } else if( HitFX[SimHitFxTicker].damtype == class'DamTypeMeleeDecapitation' ) { DecapFX( boneCoords.Origin, HitFX[SimHitFxTicker].rotDir, true); }
bHeadGibbed=true; } break; }
if( HitFX[SimHitFXTicker].bone != 'Spine' && HitFX[SimHitFXTicker].bone != FireRootBone && HitFX[SimHitFXTicker].bone != 'head' && Health <=0 ) HideBone(HitFX[SimHitFxTicker].bone); }
}
}
// Maybe spawn some chunks when the player gets obliterated
simulated function SpawnGibs(Rotator HitRotation, float ChunkPerterbation)
{
if ( ExhaustEffect != none )
{ ExhaustEffect.Destroy(); ExhaustEffect = none; bNoExhaustRespawn = true;
}
super.SpawnGibs(HitRotation,ChunkPerterbation);
}
static simulated function PreCacheMaterials(LevelInfo myLevel)
{//should be derived and used.
myLevel.AddPrecacheMaterial(Combiner'KF_Specimens_Trip_T.scrake_env_cmb');
myLevel.AddPrecacheMaterial(Texture'KF_Specimens_Trip_T.scrake_diff');
myLevel.AddPrecacheMaterial(Texture'KF_Specimens_Trip_T.scrake_spec');
myLevel.AddPrecacheMaterial(Material'KF_Specimens_Trip_T.scrake_saw_panner');
myLevel.AddPrecacheMaterial(Material'KF_Specimens_Trip_T.scrake_FB');
myLevel.AddPrecacheMaterial(Texture'KF_Specimens_Trip_T.Chainsaw_blade_diff');
}
defaultproperties
{ SawAttackLoopSound=Sound'KF_BaseScrake.Chainsaw.Scrake_Chainsaw_Impale' ChainSawOffSound=SoundGroup'KF_ChainsawSnd.Chainsaw_Deselect' remainingStuns=1 stunLoopStart=0.240000 stunLoopEnd=0.820000 idleInsertFrame=0.900000 EventClasses(0)="NicePack.NiceZombieScrake" MoanVoice=SoundGroup'KF_EnemiesFinalSnd.Scrake.Scrake_Talk' MeleeAttackHitSound=SoundGroup'KF_EnemiesFinalSnd.Scrake.Scrake_Chainsaw_HitPlayer' JumpSound=SoundGroup'KF_EnemiesFinalSnd.Scrake.Scrake_Jump' DetachedArmClass=Class'KFChar.SeveredArmScrake' DetachedLegClass=Class'KFChar.SeveredLegScrake' DetachedHeadClass=Class'KFChar.SeveredHeadScrake' DetachedSpecialArmClass=Class'KFChar.SeveredArmScrakeSaw' HitSound(0)=SoundGroup'KF_EnemiesFinalSnd.Scrake.Scrake_Pain' DeathSound(0)=SoundGroup'KF_EnemiesFinalSnd.Scrake.Scrake_Death' ChallengeSound(0)=SoundGroup'KF_EnemiesFinalSnd.Scrake.Scrake_Challenge' ChallengeSound(1)=SoundGroup'KF_EnemiesFinalSnd.Scrake.Scrake_Challenge' ChallengeSound(2)=SoundGroup'KF_EnemiesFinalSnd.Scrake.Scrake_Challenge' ChallengeSound(3)=SoundGroup'KF_EnemiesFinalSnd.Scrake.Scrake_Challenge' ControllerClass=Class'NicePack.NiceZombieScrakeController' AmbientSound=Sound'KF_BaseScrake.Chainsaw.Scrake_Chainsaw_Idle' Mesh=SkeletalMesh'KF_Freaks_Trip.Scrake_Freak' Skins(0)=Shader'KF_Specimens_Trip_T.scrake_FB' Skins(1)=TexPanner'KF_Specimens_Trip_T.scrake_saw_panner'
}

View file

@ -0,0 +1,24 @@
// Chainsaw Zombie Monster for KF Invasion gametype
// He's not quite as speedy as the other Zombies, But his attacks are TRULY damaging.
class NiceZombieScrakeBase extends NiceMonster
abstract;
#exec OBJ LOAD FILE=
var(Sounds) sound SawAttackLoopSound; // THe sound for the saw revved up, looping
var(Sounds) sound ChainSawOffSound; //The sound of this zombie dieing without a head
var bool bCharging; // Scrake charges when his health gets low
var() float AttackChargeRate; // Ratio to increase scrake movement speed when charging and attacking
// Exhaust effects
var() class<VehicleExhaustEffect> ExhaustEffectClass; // Effect class for the exhaust emitter
var() VehicleExhaustEffect ExhaustEffect;
var bool bNoExhaustRespawn;
replication
{
reliable if(Role == ROLE_Authority) bCharging;
}
//-------------------------------------------------------------------------------
// NOTE: All Code resides in the child class(this class was only created to
// eliminate hitching caused by loading default properties during play)
//-------------------------------------------------------------------------------
defaultproperties
{ AttackChargeRate=2.500000 ExhaustEffectClass=Class'KFMod.ChainsawExhaust' fuelRatio=0.400000 clientHeadshotScale=1.500000 MeleeAnims(0)="SawZombieAttack1" MeleeAnims(1)="SawZombieAttack2" StunsRemaining=1 BleedOutDuration=6.000000 ZapThreshold=1.250000 ZappedDamageMod=1.250000 bHarpoonToBodyStuns=False DamageToMonsterScale=8.000000 ZombieFlag=3 MeleeDamage=20 damageForce=-75000 bFatAss=True KFRagdollName="Scrake_Trip" bMeleeStunImmune=True Intelligence=BRAINS_Mammal bUseExtendedCollision=True ColOffset=(Z=55.000000) ColRadius=29.000000 ColHeight=18.000000 SeveredArmAttachScale=1.100000 SeveredLegAttachScale=1.100000 PlayerCountHealthScale=0.500000 PoundRageBumpDamScale=0.010000 OnlineHeadshotOffset=(X=22.000000,Y=5.000000,Z=58.000000) OnlineHeadshotScale=1.500000 HeadHealth=650.000000 PlayerNumHeadHealthScale=0.300000 MotionDetectorThreat=3.000000 ScoringValue=75 IdleHeavyAnim="SawZombieIdle" IdleRifleAnim="SawZombieIdle" MeleeRange=40.000000 GroundSpeed=85.000000 WaterSpeed=75.000000 HealthMax=1000.000000 Health=1000 HeadHeight=2.200000 MenuName="Nice Scrake" MovementAnims(0)="SawZombieWalk" MovementAnims(1)="SawZombieWalk" MovementAnims(2)="SawZombieWalk" MovementAnims(3)="SawZombieWalk" WalkAnims(0)="SawZombieWalk" WalkAnims(1)="SawZombieWalk" WalkAnims(2)="SawZombieWalk" WalkAnims(3)="SawZombieWalk" IdleCrouchAnim="SawZombieIdle" IdleWeaponAnim="SawZombieIdle" IdleRestAnim="SawZombieIdle" DrawScale=1.050000 PrePivot=(Z=3.000000) SoundVolume=175 SoundRadius=100.000000 Mass=500.000000 RotationRate=(Yaw=45000,Roll=0)
}

View file

@ -0,0 +1,43 @@
class NiceZombieScrakeController extends NiceMonsterController;
// Custom Zombie Thinkerating
// By : Alex
var bool bDoneSpottedCheck;
// Never do that, you too cool
function GetOutOfTheWayOfShot(vector ShotDirection, vector ShotOrigin){}
state ZombieHunt
{
event SeePlayer(Pawn SeenPlayer)
{ if ( !bDoneSpottedCheck && PlayerController(SeenPlayer.Controller) != none ) { // 25% chance of first player to see this Scrake saying something if ( !KFGameType(Level.Game).bDidSpottedScrakeMessage && FRand() < 0.25 ) { PlayerController(SeenPlayer.Controller).Speech('AUTO', 14, ""); KFGameType(Level.Game).bDidSpottedScrakeMessage = true; }
bDoneSpottedCheck = true; }
super.SeePlayer(SeenPlayer);
}
}
function TimedFireWeaponAtEnemy()
{
if ( (Enemy == none) || FireWeaponAt(Enemy) ) SetCombatTimer();
else SetTimer(0.01, True);
}
state ZombieCharge
{
// Don't do this in this state
function GetOutOfTheWayOfShot(vector ShotDirection, vector ShotOrigin){}
function bool StrafeFromDamage(float Damage, class<DamageType> DamageType, bool bFindDest)
{ return false;
}
function bool TryStrafe(vector sideDir)
{ return false;
}
function Timer()
{ Disable('NotifyBump'); Target = Enemy; TimedFireWeaponAtEnemy();
}
WaitForAnim:
While( Monster(Pawn).bShotAnim ) Sleep(0.25);
if ( !FindBestPathToward(Enemy, false,true) ) GotoState('ZombieRestFormation');
Moving:
MoveToward(Enemy);
WhatToDoNext(17);
if ( bSoaking ) SoakStop("STUCK IN CHARGING!");
}
defaultproperties
{
}

View file

@ -0,0 +1,221 @@
// Different naming scheme 'cause kf-scrntestingrounds has a stupid restriction on what zeds can be used in it's spawns
class NiceZombieShiver extends NiceZombieShiverBase;
var float TeleportBlockTime;
var float HeadOffsetY;
var transient bool bRunning, bClientRunning;
replication
{
reliable if ( Role == ROLE_Authority) bRunning;
}
simulated function PostNetReceive()
{
super.PostNetReceive();
if( bClientRunning != bRunning )
{ bClientRunning = bRunning; if( bRunning ) { MovementAnims[0] = RunAnim; } else { MovementAnims[0] = WalkAnim; }
}
}
simulated function PostBeginPlay()
{
Super.PostBeginPlay();
if (Level.NetMode != NM_DedicatedServer)
{ MatAlphaSkin = ColorModifier(Level.ObjectPool.AllocateObject(class'ColorModifier')); if (MatAlphaSkin != none) { MatAlphaSkin.Color = class'Canvas'.static.MakeColor(255, 255, 255, 255); MatAlphaSkin.RenderTwoSided = false; MatAlphaSkin.AlphaBlend = true; MatAlphaSkin.Material = Skins[0]; Skins[0] = MatAlphaSkin; }
}
}
simulated function Destroyed()
{
if (Level.NetMode != NM_DedicatedServer && MatAlphaSkin != none)
{ Skins[0] = default.Skins[0]; Level.ObjectPool.FreeObject(MatAlphaSkin);
}
Super.Destroyed();
}
// Overridden to add HeadOffsetY
function bool IsHeadShot(vector loc, vector ray, float AdditionalScale)
{
local coords C;
local vector HeadLoc, B, M, diff;
local float t, DotMM, Distance;
local int look;
local bool bUseAltHeadShotLocation;
local bool bWasAnimating;
if (HeadBone == '') return False;
// If we are a dedicated server estimate what animation is most likely playing on the client
if (Level.NetMode == NM_DedicatedServer)
{ if (Physics == PHYS_Falling) PlayAnim(AirAnims[0], 1.0, 0.0); else if (Physics == PHYS_Walking) { // Only play the idle anim if we're not already doing a different anim. // This prevents anims getting interrupted on the server and borking things up - Ramm
if( !IsAnimating(0) && !IsAnimating(1) ) { if (bIsCrouched) { PlayAnim(IdleCrouchAnim, 1.0, 0.0); } else { bUseAltHeadShotLocation=true; } } else { bWasAnimating = true; }
if ( bDoTorsoTwist ) { SmoothViewYaw = Rotation.Yaw; SmoothViewPitch = ViewPitch;
look = (256 * ViewPitch) & 65535; if (look > 32768) look -= 65536;
SetTwistLook(0, look); } } else if (Physics == PHYS_Swimming) PlayAnim(SwimAnims[0], 1.0, 0.0);
if( !bWasAnimating ) { SetAnimFrame(0.5); }
}
if( bUseAltHeadShotLocation )
{ HeadLoc = Location + (OnlineHeadshotOffset >> Rotation); AdditionalScale *= OnlineHeadshotScale;
}
else
{ C = GetBoneCoords(HeadBone);
HeadLoc = C.Origin + (HeadHeight * HeadScale * AdditionalScale * C.XAxis) + HeadOffsetY * C.YAxis;
}
//ServerHeadLocation = HeadLoc;
// Express snipe trace line in terms of B + tM
B = loc;
M = ray * (2.0 * CollisionHeight + 2.0 * CollisionRadius);
// Find Point-Line Squared Distance
diff = HeadLoc - B;
t = M Dot diff;
if (t > 0)
{ DotMM = M dot M; if (t < DotMM) { t = t / DotMM; diff = diff - (t * M); } else { t = 1; diff -= M; }
}
else t = 0;
Distance = Sqrt(diff Dot diff);
return (Distance < (HeadRadius * HeadScale * AdditionalScale));
}
function bool FlipOver()
{
if ( super.FlipOver() ) { TeleportBlockTime = Level.TimeSeconds + 3.0; // can't teleport during stun // do not rotate while stunned Controller.Focus = none; Controller.FocalPoint = Location + 512*vector(Rotation);
}
return false;
}
simulated function StopBurnFX()
{
if (bBurnApplied)
{ MatAlphaSkin.Material = Texture'PatchTex.Common.ZedBurnSkin'; Skins[0] = MatAlphaSkin;
}
Super.StopBurnFX();
}
function RangedAttack(Actor A)
{
if (bShotAnim || Physics == PHYS_Swimming) return;
else if (CanAttack(A))
{ bShotAnim = true; SetAnimAction('Claw'); return;
}
}
state Running
{
function Tick(float Delta)
{ Global.Tick(Delta); if (RunUntilTime < Level.TimeSeconds) GotoState(''); GroundSpeed = GetOriginalGroundSpeed();
}
function BeginState()
{ bRunning = true; RunUntilTime = Level.TimeSeconds + PeriodRunBase + FRand() * PeriodRunRan; MovementAnims[0] = RunAnim;
}
function EndState()
{ bRunning = false; GroundSpeed = global.GetOriginalGroundSpeed(); RunCooldownEnd = Level.TimeSeconds + PeriodRunCoolBase + FRand() * PeriodRunCoolRan; MovementAnims[0] = WalkAnim;
}
function float GetOriginalGroundSpeed()
{ return global.GetOriginalGroundSpeed() * 2.5;
}
function bool CanSpeedAdjust()
{ return false;
}
}
/*function TakeDamage(int Damage, Pawn InstigatedBy, Vector HitLocation, Vector Momentum, class<DamageType> DamType, optional int HitIndex)
{
if (InstigatedBy == none || class<KFWeaponDamageType>(DamType) == none) Super(Monster).TakeDamage(Damage, instigatedBy, hitLocation, momentum, DamType); // skip none-reference error
else Super(KFMonster).TakeDamage(Damage, instigatedBy, hitLocation, momentum, DamType);
}*/
// returns true also for KnockDown (stun) animation -- PooSH
simulated function bool AnimNeedsWait(name TestAnim)
{
if( TestAnim == 'DoorBash' || TestAnim == 'KnockDown' )
{ return true;
}
return ExpectingChannel == 0;
}
simulated function float GetOriginalGroundSpeed()
{
local float result;
result = OriginalGroundSpeed;
if( bZedUnderControl ) result *= 1.25; return result;
}
simulated function HandleAnimation(float Delta)
{
// hehehe
}
simulated function Tick(float Delta)
{
Super.Tick(Delta);
if (Health > 0 && !bBurnApplied)
{ if (Level.NetMode != NM_DedicatedServer) HandleAnimation(Delta); // Handle targetting if (Level.NetMode != NM_Client && !bDecapitated) { if (Controller == none || Controller.Target == none || !Controller.LineOfSightTo(Controller.Target)) { if (bCanSeeTarget) bCanSeeTarget = false; } else { if (!bCanSeeTarget) { bCanSeeTarget = true; SeeTargetTime = Level.TimeSeconds; } else if (Level.TimeSeconds > SeeTargetTime + PeriodSeeTarget) { if (VSize(Controller.Target.Location - Location) < MaxTeleportDist) { if (VSize(Controller.Target.Location - Location) > MinTeleportDist || !Controller.ActorReachable(Controller.Target)) { if (CanTeleport()) StartTelePort(); } else { if (CanRun()) GotoState('Running'); } } } } }
}
// Handle client-side teleport variables
if (!bBurnApplied)
{ if (Level.NetMode != NM_DedicatedServer && OldFadeStage != FadeStage) { OldFadeStage = FadeStage; if (FadeStage == 2) AlphaFader = 0; else AlphaFader = 255; } // Handle teleporting if (FadeStage == 1) // Fade out (pre-teleport) { AlphaFader = FMax(AlphaFader - Delta * 512, 0);
if (Level.NetMode != NM_Client && AlphaFader == 0) { SetCollision(true, true); FlashTeleport(); SetCollision(false, false); FadeStage = 2; } } else if (FadeStage == 2) // Fade in (post-teleport) { AlphaFader = FMin(AlphaFader + Delta * 512, 255); if (Level.NetMode != NM_Client && AlphaFader == 255) { FadeStage = 0; SetCollision(true, true); GotoState('Running'); } }
if (Level.NetMode != NM_DedicatedServer && ColorModifier(Skins[0]) != none) ColorModifier(Skins[0]).Color.A = AlphaFader;
}
}
//can't teleport if set on fire
function bool CanTeleport()
{
return !bFlashTeleporting && !bOnFire && Physics == PHYS_Walking && Level.TimeSeconds > TeleportBlockTime && LastFlashTime + 7.5 < Level.TimeSeconds && !bIsStunned;
}
function bool CanRun()
{
local float distanceToTargetSquared;
if(controller == none) return false;
if(controller.focus != none){ distanceToTargetSquared = VSize(controller.focus.location - location); if(distanceToTargetSquared > 900 * 2500) // (30 * 50)^2 / 30 meters return false;
}
return (!bFlashTeleporting && !IsInState('Running') && RunCooldownEnd < Level.TimeSeconds);
}
function StartTeleport()
{
FadeStage = 1;
AlphaFader = 255;
SetCollision(false, false);
bFlashTeleporting = true;
}
function FlashTeleport()
{
local Actor Target;
local vector OldLoc;
local vector NewLoc;
local vector HitLoc;
local vector HitNorm;
local rotator RotOld;
local rotator RotNew;
local float LandTargetDist;
local int iEndAngle;
local int iAttempts;
if (Controller == none || Controller.Target == none) return;
Target = Controller.Target;
RotOld = rotator(Target.Location - Location);
RotNew = RotOld;
OldLoc = Location;
for (iEndAngle = 0; iEndAngle < MaxTeleportAngles; iEndAngle++)
{ RotNew = RotOld; RotNew.Yaw += iEndAngle * (65536 / MaxTelePortAngles); for (iAttempts = 0; iAttempts < MaxTeleportAttempts; iAttempts++) { LandTargetDist = Target.CollisionRadius + CollisionRadius + MinLandDist + (MaxLandDist - MinLandDist) * (iAttempts / (MaxTeleportAttempts - 1.0));
NewLoc = Target.Location - vector(RotNew) * LandTargetDist; // Target.Location - Location NewLoc.Z = Target.Location.Z;
if (Trace(HitLoc, HitNorm, NewLoc + vect(0, 0, -500), NewLoc) != none) NewLoc.Z = HitLoc.Z + CollisionHeight;
// Try a new location if (SetLocation(NewLoc)) { SetPhysics(PHYS_Walking); if (Controller.PointReachable(Target.Location)) { Velocity = vect(0, 0, 0); Acceleration = vect(0, 0, 0); SetRotation(rotator(Target.Location - Location)); PlaySound(Sound'ScrnZedPack_S.Shiver.ShiverWarpGroup', SLOT_Interact, 4.0); Controller.GotoState(''); MonsterController(Controller).WhatToDoNext(0); goto Teleported; } } // Reset location SetLocation(OldLoc); }
}
Teleported:
bFlashTeleporting = false;
LastFlashTime = Level.TimeSeconds;
}
function Died(Controller Killer, class<DamageType> damageType, vector HitLocation)
{
// (!)
Super.Died(Killer, damageType, HitLocation);
}
function RemoveHead()
{
local class<KFWeaponDamageType> KFDamType;
KFDamType = class<KFWeaponDamageType>(LastDamagedByType);
if ( KFDamType != none && !KFDamType.default.bIsPowerWeapon && !KFDamType.default.bSniperWeapon && !KFDamType.default.bIsMeleeDamage && !KFDamType.default.bIsExplosive && !KFDamType.default.bDealBurningDamage && !ClassIsChildOf(KFDamType, class'DamTypeDualies') && !ClassIsChildOf(KFDamType, class'DamTypeMK23Pistol') && !ClassIsChildOf(KFDamType, class'DamTypeMagnum44Pistol') )
{ LastDamageAmount *= 3.5; //significantly raise decapitation bonus for Assault Rifles
//award shiver kill on decap for Commandos if ( KFPawn(LastDamagedBy)!=none && KFPlayerController(LastDamagedBy.Controller) != none && KFSteamStatsAndAchievements(KFPlayerController(LastDamagedBy.Controller).SteamStatsAndAchievements) != none ) { KFDamType.Static.AwardKill( KFSteamStatsAndAchievements(KFPlayerController(LastDamagedBy.Controller).SteamStatsAndAchievements), KFPlayerController(LastDamagedBy.Controller), self); }
}
if (IsInState('Running')) GotoState('');
Super(NiceMonster).RemoveHead();
}
function bool CheckMiniFlinch(int flinchScore, Pawn instigatedBy, Vector hitLocation, Vector momentum, class<NiceWeaponDamageType> damageType, float headshotLevel, KFPlayerReplicationInfo KFPRI){
if(IsInState('Running')) return false;
return super.CheckMiniFlinch(flinchScore, instigatedBy, hitLocation, momentum, damageType, headshotLevel, KFPRI);
}
simulated function int DoAnimAction( name AnimName )
{
if (AnimName=='Claw' || AnimName=='Claw2' || AnimName=='Claw3')
{ AnimBlendParams(1, 1.0, 0.1,, FireRootBone); PlayAnim(AnimName,, 0.1, 1); return 1;
}
return Super.DoAnimAction(AnimName);
}
defaultproperties
{ HeadOffsetY=-3.000000 idleInsertFrame=0.468000 PlayerCountHealthScale=0.200000 OnlineHeadshotOffset=(X=19.000000,Z=39.000000) ScoringValue=15 HealthMax=300.000000 Health=300 HeadRadius=8.000000 HeadHeight=3.000000
}

View file

@ -0,0 +1,39 @@
class NiceZombieShiverBase extends NiceMonster;
#exec load obj file=ScrnZedPack_T.utx
#exec load obj file=ScrnZedPack_S.uax
#exec load obj file=ScrnZedPack_A.ukx
var name WalkAnim, RunAnim;
// Head twitch
var rotator CurHeadRot, NextHeadRot, HeadRot;
var float NextHeadTime;
var float MaxHeadTime;
var float MaxTilt, MaxTurn;
// Targetting, charging
var bool bDelayedReaction;
var bool bCanSeeTarget;
var float SeeTargetTime;
var float RunUntilTime;
var float RunCooldownEnd;
var float PeriodSeeTarget;
var float PeriodRunBase;
var float PeriodRunRan;
var float PeriodRunCoolBase;
var float PeriodRunCoolRan;
// Teleporting
var byte FadeStage;
var byte OldFadeStage;
var float AlphaFader;
var bool bFlashTeleporting;
var float LastFlashTime;
var float MinTeleportDist, MaxTeleportDist;
var float MinLandDist, MaxLandDist; // How close we can teleport to the target (collision cylinders are taken into account)
var int MaxTeleportAttempts; // Attempts per angle
var int MaxTeleportAngles;
var ColorModifier MatAlphaSkin;
replication
{
reliable if (Role == ROLE_Authority) FadeStage;
}
defaultproperties
{ WalkAnim="ClotWalk" RunAnim="Run" MaxHeadTime=0.100000 MaxTilt=10000.000000 MaxTurn=20000.000000 bDelayedReaction=True PeriodSeeTarget=2.000000 PeriodRunBase=4.000000 PeriodRunRan=4.000000 PeriodRunCoolBase=4.000000 PeriodRunCoolRan=3.000000 AlphaFader=255.000000 MinTeleportDist=550.000000 MaxTeleportDist=2000.000000 MinLandDist=150.000000 MaxLandDist=500.000000 MaxTeleportAttempts=3 MaxTeleportAngles=3 fuelRatio=0.800000 clientHeadshotScale=1.400000 MoanVoice=SoundGroup'ScrnZedPack_S.Shiver.ShiverTalkGroup' bCannibal=True MeleeDamage=8 damageForce=5000 KFRagdollName="Clot_Trip" JumpSound=SoundGroup'KF_EnemiesFinalSnd.clot.Clot_Jump' CrispUpThreshhold=9 PuntAnim="ClotPunt" Intelligence=BRAINS_Mammal bUseExtendedCollision=True ColOffset=(Z=48.000000) ColRadius=25.000000 ColHeight=5.000000 ExtCollAttachBoneName="Collision_Attach" SeveredArmAttachScale=0.800000 SeveredLegAttachScale=0.800000 SeveredHeadAttachScale=0.800000 DetachedArmClass=Class'ScrnZedPack.SeveredArmShiver' DetachedLegClass=Class'ScrnZedPack.SeveredLegShiver' DetachedHeadClass=Class'ScrnZedPack.SeveredHeadShiver' OnlineHeadshotOffset=(X=20.000000,Z=37.000000) OnlineHeadshotScale=1.300000 MotionDetectorThreat=0.340000 HitSound(0)=SoundGroup'ScrnZedPack_S.Shiver.ShiverPainGroup' DeathSound(0)=SoundGroup'ScrnZedPack_S.Shiver.ShiverDeathGroup' ChallengeSound(0)=SoundGroup'ScrnZedPack_S.Shiver.ShiverTalkGroup' ChallengeSound(1)=SoundGroup'ScrnZedPack_S.Shiver.ShiverTalkGroup' ChallengeSound(2)=SoundGroup'ScrnZedPack_S.Shiver.ShiverTalkGroup' ChallengeSound(3)=SoundGroup'ScrnZedPack_S.Shiver.ShiverTalkGroup' ScoringValue=7 GroundSpeed=100.000000 WaterSpeed=100.000000 AccelRate=1024.000000 JumpZ=340.000000 HealthMax=650.000000 Health=350 MenuName="Shiver" MovementAnims(0)="ClotWalk" AmbientSound=SoundGroup'ScrnZedPack_S.Shiver.ShiverIdleGroup' Mesh=SkeletalMesh'ScrnZedPack_A.ShiverMesh' DrawScale=1.100000 PrePivot=(Z=5.000000) Skins(0)=Combiner'ScrnZedPack_T.Shiver.CmbRemoveAlpha' RotationRate=(Yaw=45000,Roll=0)
}

View file

@ -0,0 +1,145 @@
// Zombie Monster for KF Invasion gametype
class NiceZombieSick extends NiceZombieSickBase;
#exec OBJ LOAD FILE=KF_EnemiesFinalSnd.uax
#exec OBJ LOAD FILE=NicePackT.utx
//----------------------------------------------------------------------------
// NOTE: All Variables are declared in the base class to eliminate hitching
//----------------------------------------------------------------------------
var name SpitAnimation;
var transient float NextVomitTime;
function bool FlipOver(){
return true;
}
// don't interrupt the bloat while he is puking
simulated function bool HitCanInterruptAction(){
if(bShotAnim) return false;
return true;
}
function DoorAttack(Actor A)
{
if ( bShotAnim || Physics == PHYS_Swimming) return;
else if ( A!=none )
{ bShotAnim = true; if( !bDecapitated && bDistanceAttackingDoor ) { SetAnimAction('ZombieBarf'); } else { SetAnimAction('DoorBash'); GotoState('DoorBashing'); }
}
}
function RangedAttack(Actor A)
{
local int LastFireTime;
local float ChargeChance;
if ( bShotAnim ) return;
if ( Physics == PHYS_Swimming )
{ SetAnimAction('Claw'); bShotAnim = true; LastFireTime = Level.TimeSeconds;
}
else if ( VSize(A.Location - Location) < MeleeRange + CollisionRadius + A.CollisionRadius )
{ 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 ( (KFDoorMover(A) != none || VSize(A.Location-Location) <= 250) && !bDecapitated )
{ bShotAnim = true;
// Decide what chance the bloat has of charging during a puke attack if( Level.Game.GameDifficulty < 2.0 ) { ChargeChance = 0.6; } else if( Level.Game.GameDifficulty < 4.0 ) { ChargeChance = 0.8; } else if( Level.Game.GameDifficulty < 5.0 ) { ChargeChance = 1.0; } else // Hardest difficulty { ChargeChance = 1.2; }
// Randomly do a moving attack so the player can't kite the zed if( FRand() < ChargeChance ) { SetAnimAction('ZombieBarfMoving'); RunAttackTimeout = GetAnimDuration('ZombieBarf', 0.5); bMovingPukeAttack=true; } else { SetAnimAction('ZombieBarf'); Controller.bPreparingMove = true; Acceleration = vect(0,0,0); }
// Randomly send out a message about Bloat Vomit burning(3% chance) if ( FRand() < 0.03 && KFHumanPawn(A) != none && PlayerController(KFHumanPawn(A).Controller) != none ) { PlayerController(KFHumanPawn(A).Controller).Speech('AUTO', 7, ""); }
}
}
// Overridden to handle playing upper body only attacks when moving
simulated event SetAnimAction(name NewAction)
{
local int meleeAnimIndex;
local bool bWantsToAttackAndMove;
if( NewAction=='' ) Return;
bWantsToAttackAndMove = NewAction == 'ZombieBarfMoving';
if( NewAction == 'Claw' )
{ meleeAnimIndex = Rand(3); NewAction = meleeAnims[meleeAnimIndex];
}
if( bWantsToAttackAndMove )
{ ExpectingChannel = AttackAndMoveDoAnimAction(NewAction);
}
else
{ ExpectingChannel = DoAnimAction(NewAction);
}
if( !bWantsToAttackAndMove && AnimNeedsWait(NewAction) )
{ bWaitForAnim = true;
}
else
{ bWaitForAnim = false;
}
if( Level.NetMode!=NM_Client )
{ AnimAction = NewAction; bResetAnimAct = True; ResetAnimActTime = Level.TimeSeconds+0.2;
}
}
// Handle playing the anim action on the upper body only if we're attacking and moving
simulated function int AttackAndMoveDoAnimAction( name AnimName )
{
if( AnimName=='ZombieBarfMoving' )
{ AnimBlendParams(1, 1.0, 0.0,, FireRootBone); PlayAnim('ZombieBarf',, 0.1, 1);
return 1;
}
return super.DoAnimAction( AnimName );
}
function PlayDyingSound()
{
if( Level.NetMode!=NM_Client )
{ if ( bGibbed ) { PlaySound(sound'KF_EnemiesFinalSnd.Bloat_DeathPop', SLOT_Pain,2.0,true,525); return; }
if( bDecapitated ) { PlaySound(HeadlessDeathSound, SLOT_Pain,1.30,true,525); } else { PlaySound(sound'KF_EnemiesFinalSnd.Bloat_DeathPop', SLOT_Pain,2.0,true,525); }
}
}
// Barf Time.
function SpawnTwoShots()
{
local vector X,Y,Z, FireStart;
local rotator FireRotation;
if( Controller!=none && KFDoorMover(Controller.Target)!=none )
{ Controller.Target.TakeDamage(22,Self,Location,vect(0,0,0),Class'DamTypeVomit'); return;
}
GetAxes(Rotation,X,Y,Z);
FireStart = Location+(vect(30,0,64) >> Rotation)*DrawScale;
if ( !SavedFireProperties.bInitialized )
{ SavedFireProperties.AmmoClass = Class'SkaarjAmmo'; SavedFireProperties.ProjectileClass = Class'NiceSickVomit'; SavedFireProperties.WarnTargetPct = 1; SavedFireProperties.MaxRange = 600; SavedFireProperties.bTossed = False; SavedFireProperties.bTrySplash = False; SavedFireProperties.bLeadTarget = True; SavedFireProperties.bInstantHit = True; SavedFireProperties.bInitialized = True;
}
// Turn off extra collision before spawning vomit, otherwise spawn fails
ToggleAuxCollision(false);
FireRotation = Controller.AdjustAim(SavedFireProperties,FireStart,600);
Spawn(Class'NiceSickVomit',,,FireStart,FireRotation);
FireStart-=(0.5*CollisionRadius*Y);
FireRotation.Yaw -= 1200;
spawn(Class'NiceSickVomit',,,FireStart, FireRotation);
FireStart+=(CollisionRadius*Y);
FireRotation.Yaw += 2400;
spawn(Class'NiceSickVomit',,,FireStart, FireRotation);
// Turn extra collision back on
ToggleAuxCollision(true);
}
function BileBomb()
{
BloatJet = spawn(class'BileJet', self,,Location,Rotator(-PhysicsVolume.Gravity));
}
State Dying
{
function tick(float deltaTime)
{
if (BloatJet != none)
{
BloatJet.SetLocation(location);
BloatJet.SetRotation(GetBoneRotation(FireRootBone));
}
super.tick(deltaTime);
}
}
function RemoveHead()
{
bCanDistanceAttackDoors = False;
Super.RemoveHead();
}
static simulated function PreCacheMaterials(LevelInfo myLevel)
{//should be derived and used.
myLevel.AddPrecacheMaterial(Texture'NicePackT.MonsterSick.Sick_diffuse');
myLevel.AddPrecacheMaterial(Combiner'NicePackT.MonsterSick.Sick_env');
myLevel.AddPrecacheMaterial(Combiner'NicePackT.MonsterSick.Sick_cmb');
}
defaultproperties
{ DetachedArmClass=Class'NicePack.NiceSeveredArmSick' DetachedLegClass=Class'NicePack.NiceSeveredLegSick' DetachedHeadClass=Class'NicePack.NiceSeveredHeadSick' ControllerClass=Class'NicePack.NiceSickZombieController'
}

View file

@ -0,0 +1,16 @@
// Zombie Monster for KF Invasion gametype
class NiceZombieSickBase extends NiceMonster;
var name WalkAnim, RunAnim;
#exec OBJ LOAD FILE=KF_EnemiesFinalSnd.uax
var BileJet BloatJet;
var bool bPlayBileSplash;
var bool bMovingPukeAttack;
var float RunAttackTimeout;
var() float AttackChargeRate;
//-------------------------------------------------------------------------------
// NOTE: All Code resides in the child class(this class was only created to
// eliminate hitching caused by loading default properties during play)
//-------------------------------------------------------------------------------
defaultproperties
{ AttackChargeRate=2.500000 StunThreshold=4.000000 fuelRatio=0.250000 bWeakHead=True clientHeadshotScale=1.500000 MeleeAnims(0)="Strike" MeleeAnims(1)="Strike" MeleeAnims(2)="Strike" MoanVoice=SoundGroup'KF_EnemiesFinalSnd.GoreFast.Gorefast_Talk' BleedOutDuration=6.000000 ZombieFlag=3 MeleeDamage=14 damageForce=70000 bFatAss=True KFRagdollName="Clot_Trip" MeleeAttackHitSound=SoundGroup'KF_EnemiesFinalSnd.GoreFast.Gorefast_HitPlayer' JumpSound=SoundGroup'KF_EnemiesFinalSnd.GoreFast.Gorefast_Jump' PuntAnim="BloatPunt" Intelligence=BRAINS_Stupid bCanDistanceAttackDoors=True bUseExtendedCollision=True ColOffset=(Z=55.000000) ColRadius=29.000000 ColHeight=18.000000 SeveredArmAttachScale=1.400000 SeveredLegAttachScale=1.400000 SeveredHeadAttachScale=1.400000 PlayerCountHealthScale=0.250000 HeadlessWalkAnims(0)="WalkF" HeadlessWalkAnims(1)="WalkB" HeadlessWalkAnims(2)="WalkL" HeadlessWalkAnims(3)="WalkR" BurningWalkFAnims(0)="WalkF" BurningWalkFAnims(1)="WalkF" BurningWalkFAnims(2)="WalkF" BurningWalkAnims(0)="WalkB" BurningWalkAnims(1)="WalkL" BurningWalkAnims(2)="WalkR" PoundRageBumpDamScale=0.010000 OnlineHeadshotOffset=(X=22.000000,Y=5.000000,Z=58.000000) HeadHealth=50.000000 MotionDetectorThreat=3.000000 HitSound(0)=SoundGroup'KF_EnemiesFinalSnd.GoreFast.Gorefast_Pain' DeathSound(0)=SoundGroup'KF_EnemiesFinalSnd.GoreFast.Gorefast_Death' ChallengeSound(0)=SoundGroup'KF_EnemiesFinalSnd.GoreFast.Gorefast_Challenge' ChallengeSound(1)=SoundGroup'KF_EnemiesFinalSnd.GoreFast.Gorefast_Challenge' ChallengeSound(2)=SoundGroup'KF_EnemiesFinalSnd.GoreFast.Gorefast_Challenge' ChallengeSound(3)=SoundGroup'KF_EnemiesFinalSnd.GoreFast.Gorefast_Challenge' AmmunitionClass=Class'KFMod.BZombieAmmo' ScoringValue=75 IdleHeavyAnim="Idle" IdleRifleAnim="Idle" RagdollLifeSpan=20.000000 RagDeathVel=150.000000 RagShootStrength=300.000000 RagSpinScale=12.500000 RagDeathUpKick=50.000000 MeleeRange=30.000000 GroundSpeed=175.000000 WaterSpeed=150.000000 HealthMax=925.000000 Health=925 HeadHeight=2.200000 HeadScale=1.610000 AmbientSoundScaling=8.000000 MenuName="Sick" MovementAnims(0)="WalkF" MovementAnims(1)="WalkB" MovementAnims(2)="WalkL" MovementAnims(3)="WalkR" WalkAnims(1)="WalkB" WalkAnims(2)="WalkL" WalkAnims(3)="WalkR" IdleCrouchAnim="Idle" IdleWeaponAnim="Idle" IdleRestAnim="Idle" AmbientSound=Sound'KF_BaseGorefast.Gorefast_Idle' Mesh=SkeletalMesh'NicePackA.MonsterSick.Sick' DrawScale=1.100000 PrePivot=(Z=5.000000) Skins(0)=Combiner'NicePackT.MonsterSick.Sick_cmb' SoundVolume=200 Mass=400.000000 RotationRate=(Yaw=45000,Roll=0)
}

View file

@ -0,0 +1,194 @@
// Zombie Monster for KF Invasion gametype
class NiceZombieSiren extends NiceZombieSirenBase;
var float screamLength;
var float screamStartTime;
var int currScreamTiming;
var int currentScreamID;
var array<float> screamTimings;
simulated function PostBeginPlay()
{
super.PostBeginPlay();
screamLength = GetAnimDuration('Siren_Scream');
}
simulated event SetAnimAction(name NewAction)
{
local int meleeAnimIndex;
if( NewAction=='' ) Return;
if(NewAction == 'Claw')
{ meleeAnimIndex = Rand(3); NewAction = meleeAnims[meleeAnimIndex];
}
ExpectingChannel = DoAnimAction(NewAction);
if( AnimNeedsWait(NewAction) )
{ bWaitForAnim = true;
}
else
{ bWaitForAnim = false;
}
if( Level.NetMode!=NM_Client )
{ AnimAction = NewAction; bResetAnimAct = True; ResetAnimActTime = Level.TimeSeconds+0.3;
}
}
simulated function bool AnimNeedsWait(name TestAnim)
{
return false;
}
function bool FlipOver()
{
return true;
}
function DoorAttack(Actor A)
{
if ( bShotAnim || Physics == PHYS_Swimming || bDecapitated || A==none ) return;
bShotAnim = true;
SetAnimAction('Siren_Scream');
}
function MakeNewScreamBall(){
local int i;
local NicePack niceMut;
if(screamStartTime > 0){ niceMut = class'NicePack'.static.Myself(Level); if(niceMut != none){ for(i = 0;i < niceMut.playersList.Length;i ++) if(niceMut.playersList[i] != none && screamStartTime > 0) niceMut.playersList[i].SpawnSirenBall(self); }
}
}
function DiscardCurrentScreamBall(){
local int i;
local NicePack niceMut;
if(screamStartTime > 0){ niceMut = class'NicePack'.static.Myself(Level); if(niceMut != none){ for(i = 0;i < niceMut.playersList.Length;i ++) if(niceMut.playersList[i] != none) niceMut.playersList[i].ClientRemoveSirenBall(currentScreamID); } screamStartTime = -1.0; currScreamTiming = -1;
}
}
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 && A != Self)
{ bShotAnim = true; LastFireTime = Level.TimeSeconds; SetAnimAction('Claw'); Controller.bPreparingMove = true; Acceleration = vect(0,0,0);
}
else if( Dist <= ScreamRadius && !bDecapitated && !bZapped )
{ bShotAnim=true; SetAnimAction('Siren_Scream'); if(screamStartTime > 0) DiscardCurrentScreamBall(); currScreamTiming = 0; screamStartTime = Level.TimeSeconds; // 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); }
}
}
simulated function int DoAnimAction( name AnimName )
{
if( AnimName=='Siren_Scream' || AnimName=='Siren_Bite' || AnimName=='Siren_Bite2' )
{ AnimBlendParams(1, 1.0, 0.0,, SpineBone1); PlayAnim(AnimName,, 0.1, 1); return 1;
}
PlayAnim(AnimName,,0.1);
Return 0;
}
// Scream Time
simulated function SpawnTwoShots(){
if(bZapped) return;
if(Health > 0 && HeadHealth > 0 && !bIsStunned) DoShakeEffect();
if( Level.NetMode!=NM_Client )
{ // Deal Actual Damage. if(controller!=none && KFDoorMover(Controller.Target) != none) Controller.Target.TakeDamage(ScreamDamage*0.6,Self,Location,vect(0,0,0),ScreamDamageType); else HurtRadius(ScreamDamage ,ScreamRadius, ScreamDamageType, ScreamForce, Location); if(screamStartTime > 0) currScreamTiming ++; else Log("ERROR: unexpected siren scream happend!");
}
}
// Shake nearby players screens
simulated function DoShakeEffect()
{
local PlayerController PC;
local NicePlayerController nicePlayer;
local float Dist, scale, BlurScale;
//viewshake
if (Level.NetMode != NM_DedicatedServer)
{ PC = Level.GetLocalPlayerController();
nicePlayer = NicePlayerController(PC); if (PC != none && PC.ViewTarget != none) { Dist = VSize(Location - PC.ViewTarget.Location); if (Dist < ScreamRadius ) { scale = (ScreamRadius - Dist) / (ScreamRadius); scale *= ShakeEffectScalar;
if(nicePlayer != none)
scale *= nicePlayer.sirenScreamMod;
BlurScale = scale;
// Reduce blur if there is something between us and the siren if( !FastTrace(PC.ViewTarget.Location,Location) ) { scale *= 0.25; BlurScale = scale; } else { if(nicePlayer != none)
scale = Lerp(scale, MinShakeEffectScale * nicePlayer.sirenScreamMod, 1.0);
else
scale = Lerp(scale, MinShakeEffectScale, 1.0); }
PC.SetAmbientShake(Level.TimeSeconds + ShakeFadeTime, ShakeTime, OffsetMag * Scale, OffsetRate, RotMag * Scale, RotRate);
if( KFHumanPawn(PC.ViewTarget) != none ) { KFHumanPawn(PC.ViewTarget).AddBlur(ShakeTime, BlurScale * ScreamBlurScale); }
// 10% chance of player saying something about our scream if ( Level != none && Level.Game != none && !KFGameType(Level.Game).bDidSirenScreamMessage && FRand() < 0.10 ) { PC.Speech('AUTO', 16, ""); KFGameType(Level.Game).bDidSirenScreamMessage = true; } } }
}
}
simulated function HurtRadius(float DamageAmount, float DamageRadius, class<DamageType> DamageType, float Momentum, vector HitLocation)
{
local actor Victims;
local float InitMomentum;
local float damageScale, dist;
local vector dir;
local float UsedDamageAmount;
local KFHumanPawn humanPawn;
local class<NiceVeterancyTypes> niceVet;
if(bHurtEntry || Health <= 0 || HeadHealth <= 0 || bIsStunned) return;
bHurtEntry = true;
InitMomentum = Momentum;
if(screamStartTime > 0 && currScreamTiming == 0) MakeNewScreamBall();
foreach VisibleCollidingActors(class 'Actor', Victims, DamageRadius, HitLocation){ Momentum = InitMomentum; // 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('KFMonster') && !Victims.IsA('ExtendedZCollision')){ dir = Victims.Location - HitLocation; dist = FMax(1,VSize(dir)); dir = dir/dist; damageScale = 1 - FMax(0,(dist - Victims.CollisionRadius)/DamageRadius); humanPawn = KFHumanPawn(Victims); if(humanPawn == none) // If it aint human, don't pull the vortex crap on it. Momentum = 0; else{ // Also don't do it if we're sharpshooter with a right skill niceVet = class'NiceVeterancyTypes'.static.GetVeterancy(humanPawn.PlayerReplicationInfo); if(niceVet != none && !niceVet.static.CanBePulled(KFPlayerReplicationInfo(humanPawn.PlayerReplicationInfo))) Momentum = 0; }
if(Victims.IsA('KFGlassMover')) // Hack for shattering in interesting ways. UsedDamageAmount = 100000; // Siren always shatters glass else UsedDamageAmount = DamageAmount;
Victims.TakeDamage(damageScale * UsedDamageAmount,Instigator, Victims.Location - 0.5 * (Victims.CollisionHeight + Victims.CollisionRadius) * dir, (damageScale * Momentum * dir), DamageType);
if (Instigator != none && Vehicle(Victims) != none && Vehicle(Victims).Health > 0) Vehicle(Victims).DriverRadiusDamage(UsedDamageAmount, DamageRadius, Instigator.Controller, DamageType, Momentum, HitLocation); }
}
bHurtEntry = false;
}
// When siren loses her head she's got nothin' Kill her.
function RemoveHead(){
Super.RemoveHead();
}
simulated function Tick( float Delta )
{
local float currScreamTime;
Super.Tick(Delta);
if( bAboutToDie && Level.TimeSeconds>DeathTimer )
{ if( Health>0 && Level.NetMode!=NM_Client ) KilledBy(LastDamagedBy); bAboutToDie = False;
}
if( Role == ROLE_Authority )
{ if( bShotAnim ) { SetGroundSpeed(GetOriginalGroundSpeed() * 0.65);
if( LookTarget!=none ) { Acceleration = AccelRate * Normal(LookTarget.Location - Location); } } else { SetGroundSpeed(GetOriginalGroundSpeed()); }
}
if(Role == ROLE_Authority && screamStartTime > 0){ currScreamTime = Level.TimeSeconds - screamStartTime; if(currScreamTiming >= screamTimings.Length || currScreamTime - 0.1 > screamTimings[currScreamTiming] * screamLength){ DiscardCurrentScreamBall(); }
}
if(bOnFire && !bShotAnim) RangedAttack(Self);
}
function PlayDyingSound()
{
if( !bAboutToDie ) Super.PlayDyingSound();
}
simulated function ProcessHitFX()
{
local Coords boneCoords;
local class<xEmitter> HitEffects[4];
local int i,j;
local float GibPerterbation;
if( (Level.NetMode == NM_DedicatedServer) || bSkeletized || (Mesh == SkeletonMesh))
{ SimHitFxTicker = HitFxTicker; return;
}
for ( SimHitFxTicker = SimHitFxTicker; SimHitFxTicker != HitFxTicker; SimHitFxTicker = (SimHitFxTicker + 1) % ArrayCount(HitFX) )
{ j++; if ( j > 30 ) { SimHitFxTicker = HitFxTicker; return; }
if( (HitFX[SimHitFxTicker].damtype == none) || (Level.bDropDetail && (Level.TimeSeconds - LastRenderTime > 3) && !IsHumanControlled()) ) continue;
//log("Processing effects for damtype "$HitFX[SimHitFxTicker].damtype);
if( HitFX[SimHitFxTicker].bone == 'obliterate' && !class'GameInfo'.static.UseLowGore()) { SpawnGibs( HitFX[SimHitFxTicker].rotDir, 1); bGibbed = true; // Wait a tick on a listen server so the obliteration can replicate before the pawn is destroyed if( Level.NetMode == NM_ListenServer ) { bDestroyNextTick = true; TimeSetDestroyNextTickTime = Level.TimeSeconds; } else { Destroy(); } return; }
boneCoords = GetBoneCoords( HitFX[SimHitFxTicker].bone );
if ( !Level.bDropDetail && !class'GameInfo'.static.NoBlood() && !bSkeletized && !class'GameInfo'.static.UseLowGore()) { //AttachEmitterEffect( BleedingEmitterClass, HitFX[SimHitFxTicker].bone, boneCoords.Origin, HitFX[SimHitFxTicker].rotDir );
HitFX[SimHitFxTicker].damtype.static.GetHitEffects( HitEffects, Health );
if( !PhysicsVolume.bWaterVolume ) // don't attach effects under water { for( i = 0; i < ArrayCount(HitEffects); i++ ) { if( HitEffects[i] == none ) continue;
AttachEffect( HitEffects[i], HitFX[SimHitFxTicker].bone, boneCoords.Origin, HitFX[SimHitFxTicker].rotDir ); } } } if ( class'GameInfo'.static.UseLowGore() ) HitFX[SimHitFxTicker].bSever = false;
if( HitFX[SimHitFxTicker].bSever ) { GibPerterbation = HitFX[SimHitFxTicker].damtype.default.GibPerterbation;
switch( HitFX[SimHitFxTicker].bone ) { case 'obliterate': break;
case LeftThighBone: if( !bLeftLegGibbed ) { SpawnSeveredGiblet( DetachedLegClass, boneCoords.Origin, HitFX[SimHitFxTicker].rotDir, GibPerterbation, GetBoneRotation(HitFX[SimHitFxTicker].bone) ); KFSpawnGiblet( class 'KFMod.KFGibBrain',boneCoords.Origin, HitFX[SimHitFxTicker].rotDir, GibPerterbation, 250 ) ; KFSpawnGiblet( class 'KFMod.KFGibBrainb',boneCoords.Origin, HitFX[SimHitFxTicker].rotDir, GibPerterbation, 250 ) ; KFSpawnGiblet( class 'KFMod.KFGibBrain',boneCoords.Origin, HitFX[SimHitFxTicker].rotDir, GibPerterbation, 250 ) ; bLeftLegGibbed=true; } break;
case RightThighBone: if( !bRightLegGibbed ) { SpawnSeveredGiblet( DetachedLegClass, boneCoords.Origin, HitFX[SimHitFxTicker].rotDir, GibPerterbation, GetBoneRotation(HitFX[SimHitFxTicker].bone) ); KFSpawnGiblet( class 'KFMod.KFGibBrain',boneCoords.Origin, HitFX[SimHitFxTicker].rotDir, GibPerterbation, 250 ) ; KFSpawnGiblet( class 'KFMod.KFGibBrainb',boneCoords.Origin, HitFX[SimHitFxTicker].rotDir, GibPerterbation, 250 ) ; KFSpawnGiblet( class 'KFMod.KFGibBrain',boneCoords.Origin, HitFX[SimHitFxTicker].rotDir, GibPerterbation, 250 ) ; bRightLegGibbed=true; } break;
case LeftFArmBone: break;
case RightFArmBone: break;
case 'head': if( !bHeadGibbed ) { if ( HitFX[SimHitFxTicker].damtype == class'DamTypeDecapitation' ) { DecapFX( boneCoords.Origin, HitFX[SimHitFxTicker].rotDir, false); } else if( HitFX[SimHitFxTicker].damtype == class'DamTypeProjectileDecap' ) { DecapFX( boneCoords.Origin, HitFX[SimHitFxTicker].rotDir, false, true); } else if( HitFX[SimHitFxTicker].damtype == class'DamTypeMeleeDecapitation' ) { DecapFX( boneCoords.Origin, HitFX[SimHitFxTicker].rotDir, true); }
bHeadGibbed=true; } break; }
if( HitFX[SimHitFXTicker].bone != 'Spine' && HitFX[SimHitFXTicker].bone != FireRootBone && HitFX[SimHitFXTicker].bone != LeftFArmBone && HitFX[SimHitFXTicker].bone != RightFArmBone && HitFX[SimHitFXTicker].bone != 'head' && Health <=0 ) HideBone(HitFX[SimHitFxTicker].bone); }
}
}
static simulated function PreCacheMaterials(LevelInfo myLevel)
{//should be derived and used.
myLevel.AddPrecacheMaterial(Combiner'KF_Specimens_Trip_T.siren_cmb');
myLevel.AddPrecacheMaterial(Combiner'KF_Specimens_Trip_T.siren_env_cmb');
myLevel.AddPrecacheMaterial(Texture'KF_Specimens_Trip_T.siren_diffuse');
myLevel.AddPrecacheMaterial(Texture'KF_Specimens_Trip_T.siren_hair');
myLevel.AddPrecacheMaterial(Material'KF_Specimens_Trip_T.siren_hair_fb');
}
defaultproperties
{ screamTimings(0)=0.420000 screamTimings(1)=0.510000 screamTimings(2)=0.590000 screamTimings(3)=0.670000 screamTimings(4)=0.760000 screamTimings(5)=0.840000 stunLoopStart=0.200000 stunLoopEnd=0.820000 idleInsertFrame=0.920000 EventClasses(0)="NicePack.NiceZombieSiren" MoanVoice=SoundGroup'KF_EnemiesFinalSnd.siren.Siren_Talk' JumpSound=SoundGroup'KF_EnemiesFinalSnd.siren.Siren_Jump' DetachedLegClass=Class'KFChar.SeveredLegSiren' DetachedHeadClass=Class'KFChar.SeveredHeadSiren' HitSound(0)=SoundGroup'KF_EnemiesFinalSnd.siren.Siren_Pain' DeathSound(0)=SoundGroup'KF_EnemiesFinalSnd.siren.Siren_Death' ControllerClass=Class'NicePack.NiceZombieSirenController' AmbientSound=Sound'KF_BaseSiren.Siren_IdleLoop' Mesh=SkeletalMesh'KF_Freaks_Trip.Siren_Freak' Skins(0)=FinalBlend'KF_Specimens_Trip_T.siren_hair_fb' Skins(1)=Combiner'KF_Specimens_Trip_T.siren_cmb'
}

View file

@ -0,0 +1,24 @@
// Zombie Monster for KF Invasion gametype
class NiceZombieSirenBase extends NiceMonster
abstract;
var () int ScreamRadius; // AOE for scream attack.
var () class <DamageType> ScreamDamageType;
var () int ScreamForce;
var(Shake) rotator RotMag; // how far to rot view
var(Shake) float RotRate; // how fast to rot view
var(Shake) vector OffsetMag; // max view offset vertically
var(Shake) float OffsetRate; // how fast to offset view vertically
var(Shake) float ShakeTime; // how long to shake for per scream
var(Shake) float ShakeFadeTime; // how long after starting to shake to start fading out
var(Shake) float ShakeEffectScalar; // Overall scale for shake/blur effect
var(Shake) float MinShakeEffectScale;// The minimum that the shake effect drops off over distance
var(Shake) float ScreamBlurScale; // How much motion blur to give from screams
var bool bAboutToDie;
var float DeathTimer;
//-------------------------------------------------------------------------------
// NOTE: All Code resides in the child class(this class was only created to
// eliminate hitching caused by loading default properties during play)
//-------------------------------------------------------------------------------
defaultproperties
{ ScreamRadius=700 ScreamDamageType=Class'KFMod.SirenScreamDamage' ScreamForce=-150000 RotMag=(Pitch=150,Yaw=150,Roll=150) RotRate=500.000000 OffsetMag=(Y=5.000000,Z=1.000000) OffsetRate=500.000000 ShakeTime=2.000000 ShakeFadeTime=0.250000 ShakeEffectScalar=1.000000 MinShakeEffectScale=0.600000 ScreamBlurScale=0.850000 StunThreshold=3.000000 fuelRatio=0.650000 clientHeadshotScale=1.200000 niceZombieDamType=Class'NicePack.NiceZedSlashingDamageType' MeleeAnims(0)="Siren_Bite" MeleeAnims(1)="Siren_Bite2" MeleeAnims(2)="Siren_Bite" HitAnims(0)="HitReactionF" HitAnims(1)="HitReactionF" HitAnims(2)="HitReactionF" ZapThreshold=0.500000 ZappedDamageMod=1.500000 ZombieFlag=1 MeleeDamage=13 damageForce=5000 KFRagdollName="Siren_Trip" ScreamDamage=10 CrispUpThreshhold=7 bCanDistanceAttackDoors=True bUseExtendedCollision=True ColOffset=(Z=48.000000) ColRadius=25.000000 ColHeight=5.000000 ExtCollAttachBoneName="Collision_Attach" SeveredLegAttachScale=0.700000 PlayerCountHealthScale=0.100000 OnlineHeadshotOffset=(X=6.000000,Z=41.000000) OnlineHeadshotScale=1.200000 HeadHealth=200.000000 PlayerNumHeadHealthScale=0.050000 MotionDetectorThreat=2.000000 ChallengeSound(0)=SoundGroup'KF_EnemiesFinalSnd.siren.Siren_Challenge' ChallengeSound(1)=SoundGroup'KF_EnemiesFinalSnd.siren.Siren_Challenge' ChallengeSound(2)=SoundGroup'KF_EnemiesFinalSnd.siren.Siren_Challenge' ChallengeSound(3)=SoundGroup'KF_EnemiesFinalSnd.siren.Siren_Challenge' ScoringValue=25 SoundGroupClass=Class'KFMod.KFFemaleZombieSounds' IdleHeavyAnim="Siren_Idle" IdleRifleAnim="Siren_Idle" MeleeRange=45.000000 GroundSpeed=100.000000 WaterSpeed=80.000000 HealthMax=240.000000 Health=240 HeadHeight=1.000000 HeadScale=1.000000 MenuName="Nice Siren" MovementAnims(0)="Siren_Walk" MovementAnims(1)="Siren_Walk" MovementAnims(2)="Siren_Walk" MovementAnims(3)="Siren_Walk" WalkAnims(0)="Siren_Walk" WalkAnims(1)="Siren_Walk" WalkAnims(2)="Siren_Walk" WalkAnims(3)="Siren_Walk" IdleCrouchAnim="Siren_Idle" IdleWeaponAnim="Siren_Idle" IdleRestAnim="Siren_Idle" DrawScale=1.050000 PrePivot=(Z=3.000000) RotationRate=(Yaw=45000,Roll=0)
}

View file

@ -0,0 +1,13 @@
class NiceZombieSirenController extends NiceMonsterController;
var bool bDoneSpottedCheck;
state ZombieHunt
{
event SeePlayer(Pawn SeenPlayer)
{ if ( !bDoneSpottedCheck && PlayerController(SeenPlayer.Controller) != none ) { // 25% chance of first player to see this Siren saying something if ( !KFGameType(Level.Game).bDidSpottedSirenMessage && FRand() < 0.25 ) { PlayerController(SeenPlayer.Controller).Speech('AUTO', 15, ""); KFGameType(Level.Game).bDidSpottedSirenMessage = true; }
bDoneSpottedCheck = true; }
super.SeePlayer(SeenPlayer);
}
}
defaultproperties
{
}

View file

@ -0,0 +1,147 @@
// Zombie Monster for KF Invasion gametype
class NiceZombieStalker extends NiceZombieStalkerBase;
#exec OBJ LOAD FILE=KFX.utx
#exec OBJ LOAD FILE=KF_BaseStalker.uax
//----------------------------------------------------------------------------
// NOTE: All Variables are declared in the base class to eliminate hitching
//----------------------------------------------------------------------------
simulated function PostBeginPlay()
{
CloakStalker();
super.PostBeginPlay();
}
simulated function PostNetBeginPlay()
{
local PlayerController PC;
super.PostNetBeginPlay();
if( Level.NetMode!=NM_DedicatedServer )
{ PC = Level.GetLocalPlayerController(); if( PC != none && PC.Pawn != none ) { LocalKFHumanPawn = KFHumanPawn(PC.Pawn); }
}
}
simulated event SetAnimAction(name NewAction)
{
if ( NewAction == 'Claw' || NewAction == MeleeAnims[0] || NewAction == MeleeAnims[1] || NewAction == MeleeAnims[2] )
{ UncloakStalker();
}
super.SetAnimAction(NewAction);
}
simulated function Tick(float DeltaTime)
{
Super.Tick(DeltaTime);
if( Level.NetMode==NM_DedicatedServer ) Return; // Servers aren't intrested in this info.
if( bZapped )
{ // Make sure we check if we need to be cloaked as soon as the zap wears off NextCheckTime = Level.TimeSeconds;
}
else if( Level.TimeSeconds > NextCheckTime && Health > 0 )
{ NextCheckTime = Level.TimeSeconds + 0.5;
if( LocalKFHumanPawn != none && LocalKFHumanPawn.Health > 0 && LocalKFHumanPawn.ShowStalkers() && VSizeSquared(Location - LocalKFHumanPawn.Location) < LocalKFHumanPawn.GetStalkerViewDistanceMulti() * 640000.0 ) // 640000 = 800 Units { bSpotted = True; } else { bSpotted = false; }
if ( !bSpotted && !bCloaked && Skins[0] != Combiner'KF_Specimens_Trip_T.stalker_cmb' ) { UncloakStalker(); } else if ( Level.TimeSeconds - LastUncloakTime > 1.2 ) { // if we're uberbrite, turn down the light if( bSpotted && Skins[0] != Finalblend'KFX.StalkerGlow' ) { bUnlit = false; CloakStalker(); } else if ( Skins[0] != Shader'KF_Specimens_Trip_T.stalker_invisible' ) { CloakStalker(); } }
}
}
// Cloak Functions ( called from animation notifies to save Gibby trouble ;) )
simulated function CloakStalker()
{
// No cloaking if zapped
if( bZapped )
{ return;
}
if ( bSpotted )
{ if( Level.NetMode == NM_DedicatedServer ) return;
Skins[0] = Finalblend'KFX.StalkerGlow'; Skins[1] = Finalblend'KFX.StalkerGlow'; bUnlit = true; return;
}
if ( !bDecapitated && !bCrispified ) // No head, no cloak, honey. updated : Being charred means no cloak either :D
{ Visibility = 1; bCloaked = true;
if( Level.NetMode == NM_DedicatedServer ) Return;
Skins[0] = Shader'KF_Specimens_Trip_T.stalker_invisible'; Skins[1] = Shader'KF_Specimens_Trip_T.stalker_invisible';
// Invisible - no shadow if(PlayerShadow != none) PlayerShadow.bShadowActive = false; if(RealTimeShadow != none) RealTimeShadow.Destroy();
// Remove/disallow projectors on invisible people Projectors.Remove(0, Projectors.Length); bAcceptsProjectors = false; SetOverlayMaterial(Material'KFX.FBDecloakShader', 0.25, true);
}
}
simulated function UnCloakStalker()
{
if( bZapped )
{ return;
}
if( !bCrispified )
{ LastUncloakTime = Level.TimeSeconds;
Visibility = default.Visibility; bCloaked = false; bUnlit = false;
// 25% chance of our Enemy saying something about us being invisible if( Level.NetMode!=NM_Client && !KFGameType(Level.Game).bDidStalkerInvisibleMessage && FRand()<0.25 && Controller.Enemy!=none && PlayerController(Controller.Enemy.Controller)!=none ) { PlayerController(Controller.Enemy.Controller).Speech('AUTO', 17, ""); KFGameType(Level.Game).bDidStalkerInvisibleMessage = true; } if( Level.NetMode == NM_DedicatedServer ) Return;
if ( Skins[0] != Combiner'KF_Specimens_Trip_T.stalker_cmb' ) { Skins[1] = FinalBlend'KF_Specimens_Trip_T.stalker_fb'; Skins[0] = Combiner'KF_Specimens_Trip_T.stalker_cmb';
if (PlayerShadow != none) PlayerShadow.bShadowActive = true;
bAcceptsProjectors = true;
SetOverlayMaterial(Material'KFX.FBDecloakShader', 0.25, true); }
}
}
// Set the zed to the zapped behavior
simulated function SetZappedBehavior()
{
super.SetZappedBehavior();
bUnlit = false;
// Handle setting the zed to uncloaked so the zapped overlay works properly
if( Level.Netmode != NM_DedicatedServer )
{ Skins[1] = FinalBlend'KF_Specimens_Trip_T.stalker_fb'; Skins[0] = Combiner'KF_Specimens_Trip_T.stalker_cmb';
if (PlayerShadow != none) PlayerShadow.bShadowActive = true;
bAcceptsProjectors = true; SetOverlayMaterial(Material'KFZED_FX_T.Energy.ZED_overlay_Hit_Shdr', 999, true);
}
}
// Turn off the zapped behavior
simulated function UnSetZappedBehavior()
{
super.UnSetZappedBehavior();
// Handle getting the zed back cloaked if need be
if( Level.Netmode != NM_DedicatedServer )
{ NextCheckTime = Level.TimeSeconds; SetOverlayMaterial(none, 0.0f, true);
}
}
// Overridden because we need to handle the overlays differently for zombies that can cloak
function SetZapped(float ZapAmount, Pawn Instigator)
{
LastZapTime = Level.TimeSeconds;
if( bZapped )
{ TotalZap = ZapThreshold; RemainingZap = ZapDuration;
}
else
{ TotalZap += ZapAmount;
if( TotalZap >= ZapThreshold ) { RemainingZap = ZapDuration; bZapped = true; }
}
ZappedBy = Instigator;
}
function RemoveHead()
{
Super.RemoveHead();
if (!bCrispified)
{ Skins[1] = FinalBlend'KF_Specimens_Trip_T.stalker_fb'; Skins[0] = Combiner'KF_Specimens_Trip_T.stalker_cmb';
}
}
simulated function PlayDying(class<DamageType> DamageType, vector HitLoc)
{
Super.PlayDying(DamageType,HitLoc);
if(bUnlit) bUnlit=!bUnlit;
LocalKFHumanPawn = none;
if (!bCrispified)
{ Skins[1] = FinalBlend'KF_Specimens_Trip_T.stalker_fb'; Skins[0] = Combiner'KF_Specimens_Trip_T.stalker_cmb';
}
}
// Give her the ability to spring.
function bool DoJump( bool bUpdating )
{
if ( !bIsCrouched && !bWantsToCrouch && ((Physics == PHYS_Walking) || (Physics == PHYS_Ladder) || (Physics == PHYS_Spider)) )
{ if ( Role == ROLE_Authority ) { if (Level.Game != none) MakeNoise(1.0); if ( bCountJumps && (Inventory != none) ) Inventory.OwnerEvent('Jumped'); } if ( Physics == PHYS_Spider ) Velocity = JumpZ * Floor; else if ( Physics == PHYS_Ladder ) Velocity.Z = 0; else if ( bIsWalking ) { Velocity.Z = Default.JumpZ; Velocity.X = (Default.JumpZ * 0.6); } else { Velocity.Z = JumpZ; Velocity.X = (JumpZ * 0.6); } if ( (Base != none) && !Base.bWorldGeometry ) { Velocity.Z += Base.Velocity.Z; Velocity.X += Base.Velocity.X; } SetPhysics(PHYS_Falling); return true;
}
return false;
}
static simulated function PreCacheMaterials(LevelInfo myLevel)
{//should be derived and used.
myLevel.AddPrecacheMaterial(Combiner'KF_Specimens_Trip_T.stalker_cmb');
myLevel.AddPrecacheMaterial(Combiner'KF_Specimens_Trip_T.stalker_env_cmb');
myLevel.AddPrecacheMaterial(Texture'KF_Specimens_Trip_T.stalker_diff');
myLevel.AddPrecacheMaterial(Texture'KF_Specimens_Trip_T.stalker_spec');
myLevel.AddPrecacheMaterial(Material'KF_Specimens_Trip_T.stalker_invisible');
myLevel.AddPrecacheMaterial(Combiner'KF_Specimens_Trip_T.StalkerCloakOpacity_cmb');
myLevel.AddPrecacheMaterial(Material'KF_Specimens_Trip_T.StalkerCloakEnv_rot');
myLevel.AddPrecacheMaterial(Material'KF_Specimens_Trip_T.stalker_opacity_osc');
myLevel.AddPrecacheMaterial(Material'KFCharacters.StalkerSkin');
}
defaultproperties
{ stunLoopStart=0.250000 stunLoopEnd=0.890000 idleInsertFrame=0.950000 EventClasses(0)="NicePack.NiceZombieStalker" MoanVoice=SoundGroup'KF_EnemiesFinalSnd.Stalker.Stalker_Talk' MeleeAttackHitSound=SoundGroup'KF_EnemiesFinalSnd.Stalker.Stalker_HitPlayer' JumpSound=SoundGroup'KF_EnemiesFinalSnd.Stalker.Stalker_Jump' DetachedArmClass=Class'KFChar.SeveredArmStalker' DetachedLegClass=Class'KFChar.SeveredLegStalker' DetachedHeadClass=Class'KFChar.SeveredHeadStalker' HitSound(0)=SoundGroup'KF_EnemiesFinalSnd.Stalker.Stalker_Pain' DeathSound(0)=SoundGroup'KF_EnemiesFinalSnd.Stalker.Stalker_Death' ChallengeSound(0)=SoundGroup'KF_EnemiesFinalSnd.Stalker.Stalker_Challenge' ChallengeSound(1)=SoundGroup'KF_EnemiesFinalSnd.Stalker.Stalker_Challenge' ChallengeSound(2)=SoundGroup'KF_EnemiesFinalSnd.Stalker.Stalker_Challenge' ChallengeSound(3)=SoundGroup'KF_EnemiesFinalSnd.Stalker.Stalker_Challenge' AmbientSound=Sound'KF_BaseStalker.Stalker_IdleLoop' Mesh=SkeletalMesh'KF_Freaks_Trip.Stalker_Freak' Skins(0)=Shader'KF_Specimens_Trip_T.stalker_invisible' Skins(1)=Shader'KF_Specimens_Trip_T.stalker_invisible'
}

View file

@ -0,0 +1,16 @@
// Zombie Monster for KF Invasion gametype
class NiceZombieStalkerBase extends NiceMonster
abstract;
#exec OBJ LOAD FILE=
#exec OBJ LOAD FILE=KFX.utx
#exec OBJ LOAD FILE=KF_BaseStalker.uax
var float NextCheckTime;
var KFHumanPawn LocalKFHumanPawn;
var float LastUncloakTime;
//-------------------------------------------------------------------------------
// NOTE: All Code resides in the child class(this class was only created to
// eliminate hitching caused by loading default properties during play)
//-------------------------------------------------------------------------------
defaultproperties
{ fuelRatio=0.850000 clientHeadshotScale=1.200000 niceZombieDamType=Class'NicePack.NiceZedSlashingDamageType' MeleeAnims(0)="StalkerSpinAttack" MeleeAnims(1)="StalkerAttack1" MeleeAnims(2)="JumpAttack" MeleeDamage=9 damageForce=5000 KFRagdollName="Stalker_Trip" CrispUpThreshhold=10 PuntAnim="ClotPunt" SeveredArmAttachScale=0.800000 SeveredLegAttachScale=0.700000 OnlineHeadshotOffset=(X=18.000000,Z=33.000000) OnlineHeadshotScale=1.200000 MotionDetectorThreat=0.250000 ScoringValue=15 SoundGroupClass=Class'KFMod.KFFemaleZombieSounds' IdleHeavyAnim="StalkerIdle" IdleRifleAnim="StalkerIdle" MeleeRange=30.000000 GroundSpeed=200.000000 WaterSpeed=180.000000 JumpZ=350.000000 Health=100 HeadHeight=2.500000 MenuName="Nice Stalker" MovementAnims(0)="ZombieRun" MovementAnims(1)="ZombieRun" MovementAnims(2)="ZombieRun" MovementAnims(3)="ZombieRun" WalkAnims(0)="ZombieRun" WalkAnims(1)="ZombieRun" WalkAnims(2)="ZombieRun" WalkAnims(3)="ZombieRun" IdleCrouchAnim="StalkerIdle" IdleWeaponAnim="StalkerIdle" IdleRestAnim="StalkerIdle" DrawScale=1.100000 PrePivot=(Z=5.000000) RotationRate=(Yaw=45000,Roll=0)
}

View file

@ -0,0 +1,327 @@
class NiceZombieTeslaHusk extends NiceZombieHusk;
#exec OBJ LOAD FILE=KF_EnemiesFinalSnd_CIRCUS.uax
#exec OBJ LOAD FILE=ScrnZedPack_A.ukx
#exec OBJ LOAD FILE=ScrnZedPack_T.utx
#exec OBJ LOAD FILE=ScrnZedPack_SM.usx
var class<TeslaBeam> BeamClass;
var TeslaBeam PrimaryBeam;
var float MaxPrimaryBeamRange; // how far Husk can shoot with his tesla gun?
var float MaxChildBeamRange; // max distance from primary target for a chain reaction
var float ChainedBeamRangeExtension; // if pawn is already chained, chain max range is multiplied by this value
// used for optimization
var private float MaxPrimaryBeamRangeSquared, MaxChildBeamRangeSquared;
var float Energy;
var float EnergyMax;
var float EnergyRestoreRate;
var() float DischargeDamage; // beam damage per second
var() float ChainDamageMult; // how much damage drops on the next chain
var() class<DamageType> MyDamageType;
var() byte MaxChainLevel;
var() int MaxChainActors;
var ZedBeamSparks DecapitationSparks;
var bool bChainThoughZED; // chain tesla beam though a zed, which is between target and the husk, to extend firing range
var transient float NextChainZedSearchTime;
var transient float ChainRebuildTimer; // how many seconds between rebuilding chained actors
var float DischargeRate; // time between damaging chained pawns. Do not set it too low due to rounding precission of DischargeDamage
var transient float LastDischargeTime;
// Tesla Husks are able to heal each other.
var() float HealRate; // Percent of HealthMax to heal per second
var() float HealEnergyDrain; // amount of energy required to heal 1%
var transient float NextHealAttemptTime, LastHealTime;
struct SActorDamages
{
var Actor Victim;
var int Damage;
};
var protected array<SActorDamages> DischargedActors;
simulated function PostBeginPlay()
{
// Difficulty Scaling
if(Level.Game != none && !bDiffAdjusted)
{ if(Level.Game.GameDifficulty >= 5.0){ DischargeDamage *= 1.75; EnergyMax *= 1.75; EnergyRestoreRate *= 1.75; MaxPrimaryBeamRange *= 1.75; MaxChildBeamRange *= 1.75; } else{ DischargeDamage *= 1.0; EnergyMax *= 1.0; EnergyRestoreRate *= 1.0; MaxPrimaryBeamRange *= 1.0; MaxChildBeamRange *= 1.0; }
}
Energy = EnergyMax;
MaxPrimaryBeamRangeSquared = MaxPrimaryBeamRange * MaxPrimaryBeamRange;
MaxChildBeamRangeSquared = MaxChildBeamRange * MaxChildBeamRange;
NextHealAttemptTime = Level.TimeSeconds + 5.0;
super.PostBeginPlay();
BurnDamageScale = 1.0; // no fire damage resistance
}
simulated function HideBone(name boneName)
{
local int BoneScaleSlot;
local bool bValidBoneToHide;
if( boneName == LeftThighBone )
{ boneScaleSlot = 0; bValidBoneToHide = true;
}
else if ( boneName == RightThighBone )
{ boneScaleSlot = 1; bValidBoneToHide = true;
}
else if( boneName == RightFArmBone )
{ boneScaleSlot = 2; bValidBoneToHide = true;
}
else if ( boneName == LeftFArmBone )
{ boneScaleSlot = 3; bValidBoneToHide = true;
}
else if ( boneName == HeadBone )
{ // Only scale the bone down once if( SeveredHead == none ) { bValidBoneToHide = true; boneScaleSlot = 4; } else { return; }
}
else if ( boneName == 'spine' )
{ bValidBoneToHide = true; boneScaleSlot = 5;
}
// Only hide the bone if it is one of the arms, legs, or head, don't hide other misc bones
if( bValidBoneToHide )
{ SetBoneScale(BoneScaleSlot, 0.0, BoneName);
}
}
function TeslaBeam SpawnTeslaBeam()
{
local TeslaBeam beam;
beam = spawn(BeamClass, self);
if ( beam != none ) { beam.Instigator = self;
}
return beam;
}
function TeslaBeam SpawnPrimaryBeam()
{
if ( PrimaryBeam == none ) { PrimaryBeam = SpawnTeslaBeam(); PrimaryBeam.StartActor = self; PrimaryBeam.StartBoneName = 'Barrel';
}
return PrimaryBeam;
}
function FreePrimaryBeam()
{
if ( PrimaryBeam != none ) { //PrimaryBeam.DestroyChildBeams(); PrimaryBeam.EndActor = none; PrimaryBeam.EffectEndTime = Level.TimeSeconds - 1; PrimaryBeam.Instigator = none; // mark to delete PrimaryBeam = none; }}
function RangedAttack(Actor A)
{
local float Dist;
local NiceMonster M;
if ( bShotAnim || bDecapitated ) return;
Dist = VSize(A.Location - Location);
if ( Physics == PHYS_Swimming )
{ SetAnimAction('Claw'); bShotAnim = true;
}
else if ( Energy < 100 && Dist < MeleeRange + CollisionRadius + A.CollisionRadius ) // do melee hits only when low on energy
{ bShotAnim = true; SetAnimAction('Claw'); //PlaySound(sound'Claw2s', SLOT_Interact); KFTODO: Replace this Controller.bPreparingMove = true; Acceleration = vect(0,0,0);
}
else if ( Energy > 50 && Dist < MaxPrimaryBeamRange*(0.7 + 0.3*frand()) )
{ bShotAnim = true;
SetAnimAction('ShootBurns'); Controller.bPreparingMove = true; Acceleration = vect(0,0,0);
NextFireProjectileTime = Level.TimeSeconds + ProjectileFireInterval + (FRand() * 2.0);
}
else if ( bChainThoughZED && Energy > 100 && Controller.Enemy == A && KFPawn(A) != none && NextChainZedSearchTime < Level.TimeSeconds && Dist < MaxPrimaryBeamRange + MaxChildBeamRange )
{ NextChainZedSearchTime = Level.TimeSeconds + 3.0; foreach VisibleCollidingActors(class'NiceMonster', M, MaxPrimaryBeamRange*0.9) { if ( !M.bDeleteMe && M.Health > 0 && NiceZombieTeslaHusk(M) == none && VSizeSquared(M.Location - A.Location) < MaxChildBeamRangeSquared*0.9 && M.FastTrace(A.Location, M.Location) ) { Controller.Target = M; RangedAttack(M); return; } }
}
}
function SpawnTwoShots()
{
SpawnPrimaryBeam();
if ( PrimaryBeam == none ) { log("Unable to spawn Primary Beam!", 'TeslaHusk'); return;
}
PrimaryBeam.EndActor = Controller.Target;
if( Controller!=none && KFDoorMover(Controller.Target)!=none )
{ PrimaryBeam.EffectEndTime = Level.TimeSeconds + 1.0; Controller.Target.TakeDamage(22,Self,Location,vect(0,0,0), MyDamageType); return;
}
PrimaryBeam.EffectEndTime = Level.TimeSeconds + 2.5;
GotoState('Shooting');
}
simulated function Tick( float Delta )
{
if ( Role < ROLE_Authority ) { if ( bDecapitated && Health > 0 && DecapitationSparks == none ) { SpawnDecapitationEffects(); }
}
Super(NiceMonster).Tick(Delta);
if ( Role == ROLE_Authority ) { if ( Energy < EnergyMax ) Energy += EnergyRestoreRate * Delta; if ( Energy > 150 && NextHealAttemptTime < Level.TimeSeconds ) { NextHealAttemptTime = Level.TimeSeconds + 3.0; TryHealing(); }
}
}
function TryHealing()
{
local Controller C;
local NiceMonsterController MyMC;
local NiceZombieTeslaHusk BestPatient; local bool bAnyHusks;
MyMC = NiceMonsterController(Controller);
if ( MyMC == none ) return; // just in case
if ( MyMC.Enemy != none && VSizeSquared(MyMC.Enemy.Location - Location) < MaxPrimaryBeamRangeSquared ) return; // no healing when need to fight for ( C=Level.ControllerList; C!=none; C=C.NextController ) { if ( NiceZombieTeslaHusk(C.Pawn) != none && C.Pawn != self && C.Pawn.Health > 0 ) { bAnyHusks = true; if ( (BestPatient == none || BestPatient.Health > C.Pawn.Health) && VSizeSquared(C.Pawn.Location - Location) < MaxPrimaryBeamRangeSquared ) BestPatient = NiceZombieTeslaHusk(C.Pawn); }
}
if ( !bAnyHusks ) NextHealAttemptTime = Level.TimeSeconds + 10.0; // no other husks on the map - so no need to do searching so often
else if ( BestPatient != none ) { if ( BestPatient.Health < BestPatient.HealthMax * 0.90 ) { MyMC.Target = BestPatient; MyMC.ChangeEnemy(BestPatient, MyMC.CanSee(BestPatient)); MyMC.GotoState('RangedAttack'); LastHealTime = Level.TimeSeconds + 3.0; // do not heal until beam is spawned GotoState('Healing'); } else NextHealAttemptTime = Level.TimeSeconds + 1.0; // if there are other Tesla Husks nearby, then check their health each second
}
else NextHealAttemptTime = Level.TimeSeconds + 3.0; // there are other Tesla Husks on the map, but not too close
}
function Died(Controller Killer, class<DamageType> damageType, vector HitLocation)
{
if ( PrimaryBeam != none ) PrimaryBeam.Instigator = none;
if ( DecapitationSparks != none ) DecapitationSparks.Destroy();
AmbientSound = none; super.Died(Killer, damageType, HitLocation);
}
simulated event PlayDying(class<DamageType> DamageType, vector HitLoc)
{
super.PlayDying(DamageType, HitLoc);
if ( DecapitationSparks != none ) DecapitationSparks.Destroy(); AmbientSound = none;
}
// instead of roaming around, activate self-destruct
function RemoveHead()
{
Intelligence = BRAINS_Retarded; // Headless dumbasses!
bDecapitated = true;
DECAP = true;
DecapTime = Level.TimeSeconds;
bShotAnim = true;
SetAnimAction('KnockDown');
Acceleration = vect(0, 0, 0);
Velocity.X = 0;
Velocity.Y = 0;
Controller.GoToState('WaitForAnim');
NiceMonsterController(Controller).bUseFreezeHack = True;
SetGroundSpeed(0);
AirSpeed = 0;
WaterSpeed = 0;
// No more raspy breathin'...cuz he has no throat or mouth :S
AmbientSound = MiscSound;
// super.TakeDamage(LastDamageAmount) isn't called yet, so set self-destruct sequence only if
// zed can survive the hit
if( Health > LastDamageAmount && Energy > 50 ) { SpawnDecapitationEffects(); BleedOutTime = Level.TimeSeconds + BleedOutDuration; GotoState('SelfDestruct');
}
PlaySound(DecapitationSound, SLOT_Misc,1.30,true,525);
}
simulated function Destroyed()
{
if ( PrimaryBeam != none ) PrimaryBeam.Destroy(); if ( DecapitationSparks != none ) DecapitationSparks.Destroy(); super.Destroyed();
}
simulated function SpawnDecapitationEffects()
{
if (Level.NetMode == NM_DedicatedServer) return; DecapitationSparks = spawn(class'ZedBeamSparks', self,, GetBoneCoords(HeadBone).Origin, Rotation);
}
simulated function int DoAnimAction( name AnimName )
{
// faked animation with forces to play the end of shoot animation (where Huks is putting his gun down)
if ( AnimName == 'ShootBurnsEnd' ) { PlayAnim('ShootBurns'); SetAnimFrame(120, 0, 1); return 0;
}
return super.DoAnimAction(AnimName);
}
function float RangedAttackTime()
{
return 5.0;
}
// Overridden so that anims don't get interrupted on the server if one is already playing
function bool IsHeadShot(vector loc, vector ray, float AdditionalScale)
{
local coords C;
local vector HeadLoc, B, M, diff;
local float t, DotMM, Distance;
local int look;
local bool bUseAltHeadShotLocation;
local bool bWasAnimating;
if (HeadBone == '') return False;
// If we are a dedicated server estimate what animation is most likely playing on the client
if (Level.NetMode == NM_DedicatedServer)
{ if (Physics == PHYS_Falling) PlayAnim(AirAnims[0], 1.0, 0.0); else if (Physics == PHYS_Walking) { // Only play the idle anim if we're not already doing a different anim. // This prevents anims getting interrupted on the server and borking things up - Ramm
if( !IsAnimating(0) && !IsAnimating(1) ) { if (bIsCrouched) { PlayAnim(IdleCrouchAnim, 1.0, 0.0); } else { bUseAltHeadShotLocation=true; } } else { bWasAnimating = true; }
if ( bDoTorsoTwist ) { SmoothViewYaw = Rotation.Yaw; SmoothViewPitch = ViewPitch;
look = (256 * ViewPitch) & 65535; if (look > 32768) look -= 65536;
SetTwistLook(0, look); } } else if (Physics == PHYS_Swimming) PlayAnim(SwimAnims[0], 1.0, 0.0);
if( !bWasAnimating ) { SetAnimFrame(0.5); }
}
if( bUseAltHeadShotLocation )
{ HeadLoc = Location + (OnlineHeadshotOffset >> Rotation); AdditionalScale *= OnlineHeadshotScale;
}
else
{ C = GetBoneCoords(HeadBone);
HeadLoc = C.Origin + (HeadHeight * HeadScale * AdditionalScale * C.XAxis) -5.0*C.XAxis - 2.0*C.YAxis;
}
//ServerHeadLocation = HeadLoc;
// Express snipe trace line in terms of B + tM
B = loc;
M = ray * (2.0 * CollisionHeight + 2.0 * CollisionRadius);
// Find Point-Line Squared Distance
diff = HeadLoc - B;
t = M Dot diff;
if (t > 0)
{ DotMM = M dot M; if (t < DotMM) { t = t / DotMM; diff = diff - (t * M); } else { t = 1; diff -= M; }
}
else t = 0;
Distance = Sqrt(diff Dot diff);
return (Distance < (HeadRadius * HeadScale * AdditionalScale));
}
state Healing
{
ignores TryHealing;
function BeginState()
{ //log("Entering Healing State @ " $ Level.TimeSeconds, 'TeslaHusk'); LastHealTime = Level.TimeSeconds + 3.0; // do not heal until beam is spawned
}
function EndState()
{ local name SeqName; local float AnimFrame, AnimRate;
//log("Exiting Healing State @ " $ Level.TimeSeconds, 'TeslaHusk'); NextHealAttemptTime = Level.TimeSeconds + 5.0; FreePrimaryBeam();
// end shooting animation GetAnimParams(0, SeqName, AnimFrame, AnimRate); if ( SeqName == 'ShootBurns' && AnimFrame < 120 ) SetAnimAction('ShootBurnsEnd'); // faked anim Controller.Enemy = none; NiceMonsterController(Controller).FindNewEnemy();
}
function SpawnTwoShots()
{ SpawnPrimaryBeam(); if ( Controller != none ) PrimaryBeam.EndActor = Controller.Target; PrimaryBeam.EffectEndTime = Level.TimeSeconds + 2.5; LastHealTime = Level.TimeSeconds; // start healing
}
function bool FlipOver()
{ if ( global.FlipOver() ) { GotoState(''); return true; } return false;
}
function DoStun(optional Pawn instigatedBy, optional Vector hitLocation, optional Vector momentum, optional class<NiceWeaponDamageType> damageType, optional float headshotLevel, optional KFPlayerReplicationInfo KFPRI){ super.DoStun(instigatedBy, hitLocation, momentum, damageType, headshotLevel, KFPRI); GotoState('');
}
function Tick(float DeltaTime)
{ local NiceZombieTeslaHusk Patient; local int HealthToAdd; super.Tick(DeltaTime); if ( Level.TimeSeconds >= LastHealTime+0.25 ) { if ( PrimaryBeam == none || Energy <= 0 || Level.TimeSeconds > PrimaryBeam.EffectEndTime ) { GotoState(''); return; }
NextHealAttemptTime = Level.TimeSeconds + 0.25; Patient = NiceZombieTeslaHusk(PrimaryBeam.EndActor); if ( Patient == none || Patient.Health <= 0 || Patient.Health >= Patient.HealthMax ) GotoState(''); else { HealthToAdd = min(Patient.HealthMax*HealRate*(Level.TimeSeconds-LastHealTime), Patient.HealthMax - Patient.Health); if ( HealthToAdd*HealEnergyDrain > Energy ) HealthToAdd = Energy / HealEnergyDrain; Patient.Health += HealthToAdd; Energy -= HealthToAdd*HealEnergyDrain; LastHealTime = Level.TimeSeconds; } }
}}
// todo: make custom controller, moving states there
state Shooting
{
ignores TryHealing;
function BeginState()
{ //log("Entering Shooting State @ " $ Level.TimeSeconds, 'TeslaHusk'); if ( PrimaryBeam == none ) { SpawnPrimaryBeam(); PrimaryBeam.EndActor = Controller.Target; }
PrimaryBeam.EffectEndTime = Level.TimeSeconds + 2.5; BuildChain(); LastDischargeTime = Level.TimeSeconds; Discharge(DischargeRate);
}
function EndState()
{ local name SeqName; local float AnimFrame, AnimRate; //log("Exiting Shooting State @ " $ Level.TimeSeconds, 'TeslaHusk');
FreePrimaryBeam(); // end shooting animation GetAnimParams(0, SeqName, AnimFrame, AnimRate); if ( SeqName == 'ShootBurns' && AnimFrame < 120 ) SetAnimAction('ShootBurnsEnd'); // faked anim
}
function BuildChain()
{ local int ChainsRemaining; ChainRebuildTimer = default.ChainRebuildTimer; ChainsRemaining = MaxChainActors; SpawnChildBeams(PrimaryBeam, 1, ChainsRemaining); }
function SpawnChildBeams(TeslaBeam MasterBeam, byte level, out int ChainsRemaining)
{ local Pawn P; local TeslaBeam ChildBeam; local int i; if ( MasterBeam == none || level > MaxChainLevel || ChainsRemaining <= 0 ) return; for ( i=0; i<MasterBeam.ChildBeams.length; ++i ) { if ( MasterBeam.ChildBeams[i] == none ) MasterBeam.ChildBeams.remove(i--, 1); } if ( MasterBeam.EndActor == none || MasterBeam.EndActor.bDeleteMe ) return; foreach MasterBeam.EndActor.VisibleCollidingActors(class'Pawn', P, MaxChildBeamRange, MasterBeam.EndActor.Location) { if ( !P.bDeleteMe && P != MasterBeam.StartActor && P != MasterBeam.EndActor && NiceZombieTeslaHusk(P) == none && !PrimaryBeam.IsChainedTo(P) ) { i = MasterBeam.ChildIndex(P); if ( i != -1 ) { ChildBeam = MasterBeam.ChildBeams[i]; ChildBeam.EffectEndTime = PrimaryBeam.EffectEndTime; } else { ChildBeam = SpawnTeslaBeam(); if ( ChildBeam != none ) { ChildBeam.StartActor = MasterBeam.EndActor; ChildBeam.EndActor = P; ChildBeam.EffectEndTime = PrimaryBeam.EffectEndTime; MasterBeam.ChildBeams[MasterBeam.ChildBeams.length] = ChildBeam; } } if ( --ChainsRemaining <= 0 ) return; } } for ( i=0; i<MasterBeam.ChildBeams.length && ChainsRemaining > 0; ++i ) { SpawnChildBeams(MasterBeam.ChildBeams[i], level+1, ChainsRemaining); }
}
function ChainDamage(TeslaBeam Beam, float Damage, byte ChainLevel)
{ local int i; local float DmgMult; if ( Beam == none || Beam.EndActor == none || Energy < 0 || Controller == none) return; Damage = clamp(Damage, 1, Energy); // look if actor already received a damage during this discharge for ( i=0; i<DischargedActors.length; ++i ) { if ( DischargedActors[i].Victim == Beam.EndActor ) { if ( DischargedActors[i].Damage >= Damage ) return; // Victim already received enough damage else { Damage -= DischargedActors[i].Damage; DischargedActors[i].Damage += Damage; break; } } } if ( i == DischargedActors.length ) { // new victim DischargedActors.insert(i, 1); DischargedActors[i].Victim = Beam.EndActor; DischargedActors[i].Damage = Damage; } // deal higher damage to monsters, but do not drain energy quicker DmgMult = 1.0; if ( NiceMonster(Beam.EndActor) != none ) { if ( ZombieFleshpound(Beam.EndActor) != none ) DmgMult = 20.0; else DmgMult = 3.0; } // if pawn is already chained, then damage him until he gets out of the primary range Beam.SetBeamLocation(); // ensure that StartEffect and EndEffect are set if ( VSizeSquared(Beam.EndEffect - Beam.StartEffect) <= MaxPrimaryBeamRangeSquared * ChainedBeamRangeExtension && FastTrace(Beam.EndEffect, Beam.StartEffect) ) { Beam.EndActor.TakeDamage(Damage*DmgMult, self, Beam.EndActor.Location, Beam.SetBeamRotation(), MyDamageType); if(Controller == none) return; Energy -= Damage; Damage *= ChainDamageMult; ChainLevel++; if ( Damage > 0 && ChainLevel <= MaxChainLevel) { for ( i=0; i<Beam.ChildBeams.length && Energy > 0; ++i ) ChainDamage(Beam.ChildBeams[i], Damage, ChainLevel); } } else { Beam.Instigator = none; // delete beam }
}
function Discharge(float DeltaTime)
{ LastDischargeTime = Level.TimeSeconds; DischargedActors.length = 0; ChainDamage(PrimaryBeam, DischargeDamage*DeltaTime, 0); if (Energy <= 0) GotoState(''); // out of energy - stop shooting
}
function bool FlipOver()
{ if ( global.FlipOver() ) { GotoState(''); return true; } return false;
}
function DoStun(optional Pawn instigatedBy, optional Vector hitLocation, optional Vector momentum, optional class<NiceWeaponDamageType> damageType, optional float headshotLevel, optional KFPlayerReplicationInfo KFPRI){ super.DoStun(instigatedBy, hitLocation, momentum, damageType, headshotLevel, KFPRI); GotoState('');
}
function Tick(float DeltaTime)
{ super.Tick(DeltaTime);
//log("Shooting.Tick @ " $ Level.TimeSeconds $ ":" @"Energy="$Energy @"PrimaryBeam="$PrimaryBeam, 'TeslaHusk'); ChainRebuildTimer -= DeltaTime; if ( Energy <= 0 || PrimaryBeam == none || PrimaryBeam.EndActor == none || Level.TimeSeconds > PrimaryBeam.EffectEndTime ) { GotoState(''); } else if ( LastDischargeTime + DischargeRate <= Level.TimeSeconds ) { if ( ChainRebuildTimer <= 0 ) BuildChain(); Discharge(Level.TimeSeconds - LastDischargeTime); // damage chained actors } }
}
state SelfDestruct
{
ignores PlayDirectionalHit, RangedAttack, MeleeDamageTarget;
function BeginState()
{ local NiceTeslaEMPNade MyNade; MyNade = spawn(class'NiceTeslaEMPNade', self,, Location); if ( MyNade != none ) { AttachToBone(MyNade, RootBone); MyNade.SetTimer(3.0, false); MyNade.bTimerSet = true; MyNade.Damage = max(20, Energy * 0.45); } else { // wtf? Died(LastDamagedBy.Controller,class'DamTypeBleedOut',Location); }
}
function bool CheckMiniFlinch(int flinchScore, Pawn instigatedBy, Vector hitLocation, Vector momentum, class<NiceWeaponDamageType> damageType, float headshotLevel, KFPlayerReplicationInfo KFPRI){ return false;
}
function bool IsStunPossible(){ return false;
}
function bool CheckStun(int stunScore, Pawn instigatedBy, Vector hitLocation, Vector momentum, class<NiceWeaponDamageType> damageType, float headshotLevel, KFPlayerReplicationInfo KFPRI){ return false;
}
function bool CanGetOutOfWay(){ return false;
}
}
static simulated function PreCacheMaterials(LevelInfo myLevel)
{
myLevel.AddPrecacheMaterial(Shader'ScrnZedPack_T.TeslaHusk.TeslaHusk_SHADER');
myLevel.AddPrecacheMaterial(FinalBlend'KFZED_FX_T.Energy.ZED_FX_Beam_FB');
}
defaultproperties
{ BeamClass=Class'ScrnZedPack.TeslaBeam' MaxPrimaryBeamRange=300.000000 MaxChildBeamRange=150.000000 ChainedBeamRangeExtension=1.200000 Energy=200.000000 EnergyMax=200.000000 EnergyRestoreRate=20.000000 DischargeDamage=20.000000 ChainDamageMult=0.700000 MyDamageType=Class'ScrnZedPack.DamTypeTesla' MaxChainLevel=5 MaxChainActors=20 DischargeRate=0.250000 HealRate=0.250000 HealEnergyDrain=0.200000 ProjectileFireInterval=5.000000 BurnDamageScale=1.000000 mind=0.000000 bFireImmune=False MoanVoice=SoundGroup'KF_EnemiesFinalSnd_CIRCUS.Husk.Husk_Talk' BleedOutDuration=5.000000 MiscSound=Sound'KF_FY_ZEDV2SND.foley.WEP_ZEDV2_Projectile_Loop' DecapitationSound=Sound'KF_FY_ZEDV2SND.Fire.WEP_ZEDV2_Secondary_Explode' MeleeAttackHitSound=SoundGroup'KF_EnemiesFinalSnd_CIRCUS.Bloat.Bloat_HitPlayer' JumpSound=SoundGroup'KF_EnemiesFinalSnd_CIRCUS.Husk.Husk_Jump' ColHeight=30.000000 ProjectileBloodSplatClass=None DetachedArmClass=Class'ScrnZedPack.SeveredArmTeslaHusk' DetachedLegClass=Class'ScrnZedPack.SeveredLegTeslaHusk' DetachedHeadClass=Class'ScrnZedPack.SeveredHeadTeslaHusk' DetachedSpecialArmClass=Class'ScrnZedPack.SeveredGunTeslaHusk' OnlineHeadshotOffset=(X=22.000000,Z=50.000000) HitSound(0)=SoundGroup'KF_EnemiesFinalSnd_CIRCUS.Husk.Husk_Pain' DeathSound(0)=SoundGroup'KF_EnemiesFinalSnd_CIRCUS.Husk.Husk_Death' ChallengeSound(0)=SoundGroup'KF_EnemiesFinalSnd_CIRCUS.Husk.Husk_Challenge' ChallengeSound(1)=SoundGroup'KF_EnemiesFinalSnd_CIRCUS.Husk.Husk_Challenge' ChallengeSound(2)=SoundGroup'KF_EnemiesFinalSnd_CIRCUS.Husk.Husk_Challenge' ChallengeSound(3)=SoundGroup'KF_EnemiesFinalSnd_CIRCUS.Husk.Husk_Challenge' ScoringValue=25 GroundSpeed=100.000000 WaterSpeed=80.000000 HealthMax=800.000000 Health=800 HeadHeight=-7.500000 MenuName="Tesla Husk" AmbientSound=Sound'KF_BaseHusk_CIRCUS.Husk_IdleLoop' Mesh=SkeletalMesh'ScrnZedPack_A.TeslaHuskMesh' Skins(0)=Shader'ScrnZedPack_T.TeslaHusk.TeslaHusk_SHADER'
}