Fix new line symbol issues

This commit is contained in:
Anton Tarasenko 2020-04-19 00:44:48 +07:00
commit d66d86b2b1
846 changed files with 33896 additions and 12983 deletions

View file

@ -1,51 +1,90 @@
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
}
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

@ -1,14 +1,21 @@
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'
}
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

@ -1,5 +1,17 @@
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
}
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

@ -1,127 +1,292 @@
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
}
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

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

View file

@ -1,13 +1,14 @@
//=============================================================================
// 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'
}
//=============================================================================
// 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

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

View file

@ -1,86 +1,220 @@
// 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
}
// 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

@ -1,58 +1,148 @@
// 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
}
// 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

@ -1,8 +1,11 @@
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
}
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

@ -1,4 +1,10 @@
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'
}
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

@ -1,4 +1,12 @@
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'
}
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

@ -1,271 +1,622 @@
// 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'
}
// 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

@ -1,15 +1,70 @@
// 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)
}
// 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)
}

File diff suppressed because it is too large Load diff

View file

@ -1,45 +1,129 @@
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)
}
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

@ -1,190 +1,353 @@
//-----------------------------------------------------------
//
//-----------------------------------------------------------
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
{
}
//-----------------------------------------------------------
//
//-----------------------------------------------------------
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

@ -1,255 +1,509 @@
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'
}
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

@ -1,47 +1,129 @@
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)
}
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

@ -1,29 +1,39 @@
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
}
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

@ -1,126 +1,205 @@
// 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'
}
// 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

@ -1,27 +1,72 @@
// 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)
}
// 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

@ -1,90 +1,143 @@
// 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'
}
// 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

@ -1,77 +1,176 @@
// 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
}
// 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

@ -1,59 +1,94 @@
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
{
}
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

@ -1,29 +1,97 @@
// 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)
}
// 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

@ -1,116 +1,197 @@
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
}
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

@ -1,139 +1,242 @@
// 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'
}
// 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

@ -1,18 +1,63 @@
// 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)
}
// 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

@ -1,153 +1,307 @@
// 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'
}
// 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

@ -1,10 +1,20 @@
// 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
}
// 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

@ -1,21 +1,80 @@
//=============================================================================
// 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)
}
//=============================================================================
// 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

@ -1,97 +1,225 @@
//=============================================================================
// 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
{
}
//=============================================================================
// 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

@ -1,83 +1,180 @@
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'
}
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

@ -1,297 +1,657 @@
// 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'
}
// 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

@ -1,24 +1,84 @@
// 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)
}
// 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

@ -1,43 +1,63 @@
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
{
}
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

@ -1,221 +1,450 @@
// 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
}
// 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

@ -1,39 +1,104 @@
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)
}
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

@ -1,145 +1,244 @@
// 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'
}
// 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

@ -1,16 +1,100 @@
// 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)
}
// 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

@ -1,194 +1,455 @@
// 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'
}
// 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

@ -1,24 +1,97 @@
// 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)
}
// 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

@ -1,13 +1,24 @@
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
{
}
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

@ -1,147 +1,297 @@
// 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'
}
// 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

@ -1,16 +1,57 @@
// 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)
}
// 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)
}

File diff suppressed because it is too large Load diff