First commit
This commit is contained in:
commit
5b48414900
263 changed files with 24830 additions and 0 deletions
51
sources/Zeds/Nice/NiceBossHPNeedle.uc
Normal file
51
sources/Zeds/Nice/NiceBossHPNeedle.uc
Normal file
|
|
@ -0,0 +1,51 @@
|
|||
class NiceBossHPNeedle extends Decoration
|
||||
NotPlaceable;
|
||||
#exec obj load file="NewPatchSM.usx"
|
||||
simulated function DroppedNow()
|
||||
{
|
||||
SetCollision(True);
|
||||
SetPhysics(PHYS_Falling);
|
||||
bFixedRotationDir = True;
|
||||
RotationRate = RotRand(True);
|
||||
}
|
||||
simulated function HitWall( vector HitNormal, actor HitWall )
|
||||
{
|
||||
local rotator R;
|
||||
if( VSize(Velocity)<40 )
|
||||
{
SetPhysics(PHYS_none);
R.Roll = Rand(65536);
R.Yaw = Rand(65536);
SetRotation(R);
Return;
|
||||
}
|
||||
Velocity = MirrorVectorByNormal(Velocity,HitNormal)*0.75;
|
||||
if( HitWall!=none && HitWall.Physics!=PHYS_none )
Velocity+=HitWall.Velocity;
|
||||
}
|
||||
simulated function Landed( vector HitNormal )
|
||||
{
|
||||
HitWall(HitNormal,none);
|
||||
}
|
||||
function TakeDamage( int NDamage, Pawn instigatedBy, Vector hitlocation,
Vector momentum, class<DamageType> damageType, optional int HitIndex)
|
||||
{
|
||||
if( Physics==PHYS_none )
|
||||
{
SetPhysics(PHYS_Falling);
bFixedRotationDir = True;
RotationRate = RotRand(True);
Velocity = vect(0,0,0);
|
||||
}
|
||||
Velocity+=momentum/10;
|
||||
}
|
||||
simulated function Destroyed();
|
||||
function Bump( actor Other );
|
||||
singular function PhysicsVolumeChange( PhysicsVolume NewVolume );
|
||||
// Overriden so it doesn't damage the patriarch when he drops a needle!
|
||||
singular function BaseChange()
|
||||
{
|
||||
if( Velocity.Z < -500 )
TakeDamage( (1-Velocity.Z/30),Instigator,Location,vect(0,0,0) , class'Crushed');
|
||||
if( base == none )
|
||||
{
if ( !bInterpolating && bPushable && (Physics == PHYS_none) )
SetPhysics(PHYS_Falling);
|
||||
}
|
||||
else if( Pawn(Base) != none )
|
||||
{
//Base.TakeDamage( (1-Velocity.Z/400)* mass/Base.Mass,Instigator,Location,0.5 * Velocity , class'Crushed');
Velocity.Z = 100;
if (FRand() < 0.5)
Velocity.X += 70;
else
Velocity.Y += 70;
SetPhysics(PHYS_Falling);
|
||||
}
|
||||
else if( Decoration(Base)!=none && Velocity.Z<-500 )
|
||||
{
Base.TakeDamage((1 - Mass/Base.Mass * Velocity.Z/30), Instigator, Location, 0.2 * Velocity, class'Crushed');
Velocity.Z = 100;
if (FRand() < 0.5)
Velocity.X += 70;
else
Velocity.Y += 70;
SetPhysics(PHYS_Falling);
|
||||
}
|
||||
else
instigator = none;
|
||||
}
|
||||
defaultproperties
|
||||
{
DrawType=DT_StaticMesh
StaticMesh=StaticMesh'NewPatchSM.BossSyringe'
bStatic=False
RemoteRole=ROLE_None
LifeSpan=300.000000
CollisionRadius=4.000000
CollisionHeight=4.000000
bCollideWorld=True
bProjTarget=True
bBounce=True
|
||||
}
|
||||
14
sources/Zeds/Nice/NiceBossLAWProj.uc
Normal file
14
sources/Zeds/Nice/NiceBossLAWProj.uc
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
class NiceBossLAWProj extends LAWProj;
|
||||
//-----------------------------------------------------------------------------
|
||||
// PostBeginPlay
|
||||
//-----------------------------------------------------------------------------
|
||||
simulated function PostBeginPlay()
|
||||
{
|
||||
// Difficulty Scaling
|
||||
if(Level.Game != none){
if(Level.Game.GameDifficulty >= 5.0) // Hell on Earth & Suicidal
damage = default.damage * 1.3;
else
damage = default.damage * 1.0;
|
||||
}
|
||||
super.PostBeginPlay();
|
||||
}
|
||||
defaultproperties
|
||||
{
ArmDistSquared=0.000000
Damage=200.000000
MyDamageType=Class'KFMod.DamTypeFrag'
|
||||
}
|
||||
5
sources/Zeds/Nice/NiceDamTypePoundCrushed.uc
Normal file
5
sources/Zeds/Nice/NiceDamTypePoundCrushed.uc
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
class NiceDamTypePoundCrushed extends NiceZedMeleeDamageType
|
||||
abstract;
|
||||
defaultproperties
|
||||
{
DeathString="%o was pounded by %k."
FemaleSuicide="%o was pounded."
MaleSuicide="%o was pounded."
bArmorStops=False
bLocationalHit=False
bThrowRagdoll=True
bExtraMomentumZ=True
GibPerterbation=1.000000
KDamageImpulse=7000.000000
KDeathVel=350.000000
KDeathUpKick=100.000000
HumanObliterationThreshhold=500
|
||||
}
|
||||
5
sources/Zeds/Nice/NiceEnviromentalDamage.uc
Normal file
5
sources/Zeds/Nice/NiceEnviromentalDamage.uc
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
class NiceEnviromentalDamage extends NiceWeaponDamageType
|
||||
abstract;
|
||||
defaultproperties
|
||||
{
|
||||
}
|
||||
6
sources/Zeds/Nice/NiceEnviromentalDamageFire.uc
Normal file
6
sources/Zeds/Nice/NiceEnviromentalDamageFire.uc
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
class NiceEnviromentalDamageFire extends NiceEnviromentalDamage
|
||||
abstract;
|
||||
defaultproperties
|
||||
{
|
||||
heatPart=1.0
|
||||
}
|
||||
127
sources/Zeds/Nice/NiceHuskFireProjectile.uc
Normal file
127
sources/Zeds/Nice/NiceHuskFireProjectile.uc
Normal file
|
|
@ -0,0 +1,127 @@
|
|||
class NiceHuskFireProjectile extends LAWProj;
|
||||
var Emitter FlameTrail;
|
||||
var xEmitter Trail;
|
||||
var class<DamageType> MyAdditionalDamageType;
|
||||
var float additionalDamagePart;
|
||||
//-----------------------------------------------------------------------------
|
||||
// PostBeginPlay
|
||||
//-----------------------------------------------------------------------------
|
||||
simulated function PostBeginPlay()
|
||||
{
|
||||
if ( Level.NetMode != NM_DedicatedServer )
|
||||
{
if ( !PhysicsVolume.bWaterVolume )
{
FlameTrail = Spawn(class'FlameThrowerFlameB',self);
Trail = Spawn(class'FlameThrowerFlame',self);
}
|
||||
}
|
||||
// Difficulty Scaling
|
||||
if (Level.Game != none)
|
||||
{
if(Level.Game.GameDifficulty >= 5.0) // Hell on Earth & Suicidal
damage = default.damage * 1.3;
else
damage = default.damage * 1.0;
|
||||
}
|
||||
OrigLoc = Location;
|
||||
if( !bDud )
|
||||
{
Dir = vector(Rotation);
Velocity = speed * Dir;
|
||||
}
|
||||
super(ROBallisticProjectile).PostBeginPlay();
|
||||
}
|
||||
simulated function Explode(vector HitLocation, vector HitNormal)
|
||||
{
|
||||
local Controller C;
|
||||
local PlayerController LocalPlayer;
|
||||
local float ShakeScale;
|
||||
bHasExploded = True;
|
||||
// Don't explode if this is a dud
|
||||
if( bDud )
|
||||
{
Velocity = vect(0,0,0);
LifeSpan=1.0;
SetPhysics(PHYS_Falling);
|
||||
}
|
||||
PlaySound(ExplosionSound,,2.0);
|
||||
if ( EffectIsRelevant(Location,false) )
|
||||
{
Spawn(class'KFMod.FlameImpact',,,HitLocation + HitNormal*20,rotator(HitNormal));
Spawn(ExplosionDecal,self,,HitLocation, rotator(-HitNormal));
|
||||
}
|
||||
BlowUp(HitLocation);
|
||||
Destroy();
|
||||
// Shake nearby players screens
|
||||
LocalPlayer = Level.GetLocalPlayerController();
|
||||
if ( LocalPlayer != none )
|
||||
{
ShakeScale = GetShakeScale(Location, LocalPlayer.ViewTarget.Location);
if( ShakeScale > 0 )
{
LocalPlayer.ShakeView(RotMag * ShakeScale, RotRate, RotTime, OffsetMag * ShakeScale, OffsetRate, OffsetTime);
}
|
||||
}
|
||||
for ( C=Level.ControllerList; C!=none; C=C.NextController )
|
||||
{
if ( PlayerController(C) != none && C != LocalPlayer )
{
ShakeScale = GetShakeScale(Location, PlayerController(C).ViewTarget.Location);
if( ShakeScale > 0 )
{
C.ShakeView(RotMag * ShakeScale, RotRate, RotTime, OffsetMag * ShakeScale, OffsetRate, OffsetTime);
}
}
|
||||
}
|
||||
}
|
||||
// Get the shake amount for when this projectile explodes
|
||||
simulated function float GetShakeScale(vector ViewLocation, vector EventLocation)
|
||||
{
|
||||
local float Dist;
|
||||
local float scale;
|
||||
Dist = VSize(ViewLocation - EventLocation);
|
||||
if (Dist < DamageRadius * 2.0 )
|
||||
{
scale = (DamageRadius*2.0 - Dist) / (DamageRadius*2.0);
|
||||
}
|
||||
return scale;
|
||||
}
|
||||
/* HurtRadius()
|
||||
Hurt locally authoritative actors within the radius.
|
||||
Overriden so it doesn't attemt to damage the bullet whiz cylinder - TODO: maybe implement the same thing in the superclass - Ramm
|
||||
*/
|
||||
simulated function HurtRadius( float DamageAmount, float DamageRadius, class<DamageType> DamageType, float Momentum, vector HitLocation )
|
||||
{
|
||||
local actor Victims;
|
||||
local float damageScale, dist;
|
||||
local vector dirs;
|
||||
local int NumKilled;
|
||||
local KFMonster KFMonsterVictim;
|
||||
local Pawn P;
|
||||
local KFPawn KFP;
|
||||
local array<Pawn> CheckedPawns;
|
||||
local int i;
|
||||
local bool bAlreadyChecked;
|
||||
if ( bHurtEntry )
return;
|
||||
bHurtEntry = true;
|
||||
foreach CollidingActors (class 'Actor', Victims, DamageRadius, HitLocation)
|
||||
{
// don't let blast damage affect fluid - VisibleCollisingActors doesn't really work for them - jag
if( (Victims != self) && (Victims != Instigator) &&(Hurtwall != Victims)
&& (Victims.Role == ROLE_Authority) && !Victims.IsA('FluidSurfaceInfo')
&& ExtendedZCollision(Victims)==none && KFBulletWhipAttachment(Victims)==none )
{
dirs = Victims.Location - HitLocation;
dist = FMax(1,VSize(dirs));
dirs = dirs/dist;
damageScale = 1 - FMax(0,(dist - Victims.CollisionRadius)/DamageRadius);
if ( Instigator == none || Instigator.Controller == none )
Victims.SetDelayedDamageInstigatorController( InstigatorController );
if ( Victims == LastTouched )
LastTouched = none;
|
||||
P = Pawn(Victims);
|
||||
if( P != none )
{
for (i = 0; i < CheckedPawns.Length; i++)
{
if (CheckedPawns[i] == P)
{
bAlreadyChecked = true;
break;
}
}
|
||||
if( bAlreadyChecked )
{
bAlreadyChecked = false;
P = none;
continue;
}
|
||||
KFMonsterVictim = KFMonster(Victims);
|
||||
if( KFMonsterVictim != none && KFMonsterVictim.Health <= 0 )
{
KFMonsterVictim = none;
}
|
||||
KFP = KFPawn(Victims);
|
||||
if( KFMonsterVictim != none )
{
damageScale *= KFMonsterVictim.GetExposureTo(HitLocation);
}
else if( KFP != none )
{
damageScale *= KFP.GetExposureTo(HitLocation);
}
|
||||
CheckedPawns[CheckedPawns.Length] = P;
|
||||
if ( damageScale <= 0)
{
P = none;
continue;
}
else
{
P = none;
}
}
|
||||
Victims.TakeDamage
(
damageScale * DamageAmount * (1.0 - additionalDamagePart),
Instigator,
Victims.Location - 0.5 * (Victims.CollisionHeight + Victims.CollisionRadius) * dirs,
(damageScale * Momentum * dirs),
DamageType
);
Victims.TakeDamage
(
damageScale * DamageAmount * additionalDamagePart,
Instigator,
Victims.Location - 0.5 * (Victims.CollisionHeight + Victims.CollisionRadius) * dirs,
Vect(0,0,0),
MyAdditionalDamageType
);
if (Vehicle(Victims) != none && Vehicle(Victims).Health > 0)
Vehicle(Victims).DriverRadiusDamage(DamageAmount, DamageRadius, InstigatorController, DamageType, Momentum, HitLocation);
|
||||
if( Role == ROLE_Authority && KFMonsterVictim != none && KFMonsterVictim.Health <= 0 )
{
NumKilled++;
}
}
|
||||
}
|
||||
if ( (LastTouched != none) && (LastTouched != self) && (LastTouched != Instigator) &&
(LastTouched.Role == ROLE_Authority) && !LastTouched.IsA('FluidSurfaceInfo') )
|
||||
{
Victims = LastTouched;
LastTouched = none;
dirs = Victims.Location - HitLocation;
dist = FMax(1,VSize(dirs));
dirs = dirs/dist;
damageScale = FMax(Victims.CollisionRadius/(Victims.CollisionRadius + Victims.CollisionHeight),1 - FMax(0,(dist - Victims.CollisionRadius)/DamageRadius));
if ( Instigator == none || Instigator.Controller == none )
Victims.SetDelayedDamageInstigatorController(InstigatorController);
|
||||
Victims.TakeDamage
(
damageScale * DamageAmount,
Instigator,
Victims.Location - 0.5 * (Victims.CollisionHeight + Victims.CollisionRadius) * dirs,
(damageScale * Momentum * dirs),
DamageType
);
if (Vehicle(Victims) != none && Vehicle(Victims).Health > 0)
Vehicle(Victims).DriverRadiusDamage(DamageAmount, DamageRadius, InstigatorController, DamageType, Momentum, HitLocation);
|
||||
}
|
||||
if( Role == ROLE_Authority )
|
||||
{
if( NumKilled >= 4 )
{
KFGameType(Level.Game).DramaticEvent(0.05);
}
else if( NumKilled >= 2 )
{
KFGameType(Level.Game).DramaticEvent(0.03);
}
|
||||
}
|
||||
bHurtEntry = false;
|
||||
}
|
||||
//==============
|
||||
// Touching
|
||||
// Overridden to not touch the bulletwhip attachment
|
||||
simulated singular function Touch(Actor Other){
|
||||
if(Other == none || KFBulletWhipAttachment(Other) != none || Role < ROLE_Authority)
return;
|
||||
super.Touch(Other);
|
||||
}
|
||||
// Don't hit Zed extra collision cylinders
|
||||
// Do hit :3
|
||||
simulated function ProcessTouch(Actor Other, Vector HitLocation)
|
||||
{
|
||||
/*if(ExtendedZCollision(Other) != none){
return;*/
|
||||
super.ProcessTouch(Other, HitLocation);
|
||||
}
|
||||
simulated function Destroyed()
|
||||
{
|
||||
if ( Trail != none )
|
||||
{
Trail.mRegen=False;
Trail.SetPhysics(PHYS_none);
Trail.GotoState('');
|
||||
}
|
||||
if ( FlameTrail != none )
|
||||
{
FlameTrail.Kill();
FlameTrail.SetPhysics(PHYS_none);
|
||||
}
|
||||
Super.Destroyed();
|
||||
}
|
||||
defaultproperties
|
||||
{
MyAdditionalDamageType=Class'KFMod.DamTypeLAW'
ExplosionSound=SoundGroup'KF_EnemiesFinalSnd.Husk.Husk_FireImpact'
ArmDistSquared=0.000000
Speed=1800.000000
MaxSpeed=2200.000000
Damage=25.000000
DamageRadius=150.000000
MyDamageType=Class'NicePack.NiceDamTypeFire'
ExplosionDecal=Class'KFMod.FlameThrowerBurnMark'
LightType=LT_Steady
LightHue=45
LightSaturation=169
LightBrightness=90.000000
LightRadius=16.000000
LightCone=16
StaticMesh=StaticMesh'EffectsSM.Weapons.Ger_Tracer'
bDynamicLight=True
bNetTemporary=False
AmbientSound=Sound'KF_BaseHusk.Fire.husk_fireball_loop'
DrawScale=2.000000
AmbientGlow=254
bUnlit=True
|
||||
}
|
||||
4
sources/Zeds/Nice/NiceSeveredArmSick.uc
Normal file
4
sources/Zeds/Nice/NiceSeveredArmSick.uc
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
class NiceSeveredArmSick extends SeveredArm;
|
||||
defaultproperties
|
||||
{
StaticMesh=StaticMesh'NicePackSM.MonsterSick.Arm'
|
||||
}
|
||||
13
sources/Zeds/Nice/NiceSeveredHeadSick.uc
Normal file
13
sources/Zeds/Nice/NiceSeveredHeadSick.uc
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
//=============================================================================
|
||||
// SeveredHeadClot
|
||||
//=============================================================================
|
||||
// Detached head gib class for the clot
|
||||
//=============================================================================
|
||||
// Killing Floor Source
|
||||
// Copyright (C) 2009 Tripwire Interactive LLC
|
||||
// - John "Ramm-Jaeger" Gibson
|
||||
//=============================================================================
|
||||
class NiceSeveredHeadSick extends SeveredHead;
|
||||
defaultproperties
|
||||
{
StaticMesh=StaticMesh'NicePackSM.MonsterSick.head'
|
||||
}
|
||||
4
sources/Zeds/Nice/NiceSeveredLegSick.uc
Normal file
4
sources/Zeds/Nice/NiceSeveredLegSick.uc
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
class NiceSeveredLegSick extends SeveredLeg;
|
||||
defaultproperties
|
||||
{
StaticMesh=StaticMesh'NicePackSM.MonsterSick.Leg'
|
||||
}
|
||||
86
sources/Zeds/Nice/NiceSickVomit.uc
Normal file
86
sources/Zeds/Nice/NiceSickVomit.uc
Normal file
|
|
@ -0,0 +1,86 @@
|
|||
// The Nice, nasty barf we'll be using for the Bloat's ranged attack.
|
||||
class NiceSickVomit extends BioGlob;
|
||||
simulated function PostBeginPlay()
|
||||
{
|
||||
SetOwner(none);
|
||||
if (Role == ROLE_Authority)
|
||||
{
Velocity = Vector(Rotation) * Speed;
Velocity.Z += TossZ;
|
||||
}
|
||||
if (Role == ROLE_Authority)
Rand3 = Rand(3);
|
||||
if ( (Level.NetMode != NM_DedicatedServer) && ((Level.DetailMode == DM_Low) || Level.bDropDetail) )
|
||||
{
bDynamicLight = false;
LightType = LT_none;
|
||||
}
|
||||
// Difficulty Scaling
|
||||
if (Level.Game != none)
|
||||
{
BaseDamage = Max((DifficultyDamageModifer() * BaseDamage),1);
Damage = Max((DifficultyDamageModifer() * Damage),1);
|
||||
}
|
||||
}
|
||||
// Scales the damage this Zed deals by the difficulty level
|
||||
function float DifficultyDamageModifer()
|
||||
{
|
||||
local float AdjustedDamageModifier;
|
||||
if(Level.Game.GameDifficulty >= 5.0) // Hell on Earth & Suicidal
damage = default.damage * 2.5;
|
||||
else
damage = default.damage * 1.5;
|
||||
return AdjustedDamageModifier;
|
||||
}
|
||||
state OnGround
|
||||
{
|
||||
simulated function BeginState()
|
||||
{
SetTimer(RestTime, false);
BlowUp(Location);
|
||||
}
|
||||
simulated function Timer()
|
||||
{
if (bDrip)
{
bDrip = false;
SetCollisionSize(default.CollisionHeight, default.CollisionRadius);
Velocity = PhysicsVolume.Gravity * 0.2;
SetPhysics(PHYS_Falling);
bCollideWorld = true;
bCheckedsurface = false;
bProjTarget = false;
GotoState('Flying');
}
else BlowUp(Location);
|
||||
}
|
||||
simulated function ProcessTouch(Actor Other, Vector HitLocation)
|
||||
{
if ( Other != none )
BlowUp(Location);
|
||||
}
|
||||
function TakeDamage( int Damage, Pawn InstigatedBy, Vector Hitlocation, Vector Momentum, class<DamageType> damageType, optional int HitIndex)
|
||||
{
if (DamageType.default.bDetonatesGoop)
{
bDrip = false;
SetTimer(0.1, false);
}
|
||||
}
|
||||
simulated function AnimEnd(int Channel)
|
||||
{
local float DotProduct;
|
||||
if (!bCheckedSurface)
{
DotProduct = SurfaceNormal dot Vect(0,0,-1);
if (DotProduct > 0.7)
{
bDrip = true;
SetTimer(DripTime, false);
if (bOnMover)
BlowUp(Location);
}
else if (DotProduct > -0.5)
{
if (bOnMover)
BlowUp(Location);
}
bCheckedSurface = true;
}
|
||||
}
|
||||
simulated function MergeWithGlob(int AdditionalGoopLevel)
|
||||
{
local int NewGoopLevel, ExtraSplash;
NewGoopLevel = AdditionalGoopLevel + GoopLevel;
if (NewGoopLevel > MaxGoopLevel)
{
Rand3 = (Rand3 + 1) % 3;
ExtraSplash = Rand3;
if (Role == ROLE_Authority)
SplashGlobs(NewGoopLevel - MaxGoopLevel + ExtraSplash);
NewGoopLevel = MaxGoopLevel - ExtraSplash;
}
SetGoopLevel(NewGoopLevel);
SetCollisionSize(GoopVolume*10.0, GoopVolume*10.0);
PlaySound(ImpactSound, SLOT_Misc);
bCheckedSurface = false;
SetTimer(RestTime, false);
|
||||
}
|
||||
}
|
||||
singular function SplashGlobs(int NumGloblings)
|
||||
{
|
||||
local int g;
|
||||
local NiceSickVomit NewGlob;
|
||||
local Vector VNorm;
|
||||
for (g=0; g<NumGloblings; g++)
|
||||
{
NewGlob = Spawn(Class, self,, Location+GoopVolume*(CollisionHeight+4.0)*SurfaceNormal);
if (NewGlob != none)
{
NewGlob.Velocity = (GloblingSpeed + FRand()*150.0) * (SurfaceNormal + VRand()*0.8);
if (Physics == PHYS_Falling)
{
VNorm = (Velocity dot SurfaceNormal) * SurfaceNormal;
NewGlob.Velocity += (-VNorm + (Velocity - VNorm)) * 0.1;
}
NewGlob.InstigatorController = InstigatorController;
}
//else log("unable to spawn globling");
|
||||
}
|
||||
}
|
||||
simulated function Destroyed()
|
||||
{
|
||||
if ( !bNoFX && EffectIsRelevant(Location,false) )
|
||||
{
//Spawn(class'xEffects.GoopSmoke');
Spawn(class'KFmod.VomGroundSplash');
|
||||
}
|
||||
if ( Fear != none )
Fear.Destroy();
|
||||
if (Trail != none)
Trail.Destroy();
|
||||
//Super.Destroyed();
|
||||
}
|
||||
|
||||
auto state Flying
|
||||
{
|
||||
simulated function Landed( Vector HitNormal )
|
||||
{
local Rotator NewRot;
local int CoreGoopLevel;
|
||||
if ( Level.NetMode != NM_DedicatedServer )
{
PlaySound(ImpactSound, SLOT_Misc);
// explosion effects
}
|
||||
SurfaceNormal = HitNormal;
|
||||
// spawn globlings
CoreGoopLevel = Rand3 + MaxGoopLevel - 3;
if (GoopLevel > CoreGoopLevel)
{
if (Role == ROLE_Authority)
SplashGlobs(GoopLevel - CoreGoopLevel);
SetGoopLevel(CoreGoopLevel);
}
spawn(class'KFMod.VomitDecal',,,, rotator(-HitNormal));
|
||||
bCollideWorld = false;
SetCollisionSize(GoopVolume*10.0, GoopVolume*10.0);
bProjTarget = true;
|
||||
NewRot = Rotator(HitNormal);
NewRot.Roll += 32768;
SetRotation(NewRot);
SetPhysics(PHYS_none);
bCheckedsurface = false;
Fear = Spawn(class'AvoidMarker');
GotoState('OnGround');
|
||||
}
|
||||
simulated function HitWall( Vector HitNormal, Actor Wall )
|
||||
{
Landed(HitNormal);
if ( !Wall.bStatic && !Wall.bWorldGeometry )
{
bOnMover = true;
SetBase(Wall);
if (Base == none)
BlowUp(Location);
}
|
||||
}
|
||||
simulated function ProcessTouch(Actor Other, Vector HitLocation)
|
||||
{
if( ExtendedZCollision(Other)!=none )
Return;
if (Other != Instigator && (Other.IsA('Pawn') || Other.IsA('DestroyableObjective') || Other.bProjTarget))
HurtRadius(Damage,DamageRadius, MyDamageType, MomentumTransfer, HitLocation );
else if ( Other != Instigator && Other.bBlockActors )
HitWall( Normal(HitLocation-Location), Other );
|
||||
}
|
||||
}
|
||||
defaultproperties
|
||||
{
BaseDamage=4
TouchDetonationDelay=0.000000
Speed=600.000000
Damage=5.000000
MomentumTransfer=3500.000000
MyDamageType=Class'KFMod.DamTypeVomit'
ImpactSound=SoundGroup'KF_EnemiesFinalSnd.Bloat.Bloat_AcidSplash'
DrawType=DT_StaticMesh
StaticMesh=StaticMesh'kf_gore_trip_sm.puke.puke_chunk'
bDynamicLight=False
LifeSpan=8.000000
Skins(0)=Combiner'kf_fx_trip_t.Gore.intestines_cmb'
bUseCollisionStaticMesh=False
bBlockHitPointTraces=False
|
||||
}
|
||||
7
sources/Zeds/Nice/NiceSickZombieController.uc
Normal file
7
sources/Zeds/Nice/NiceSickZombieController.uc
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
class NiceSickZombieController extends NiceMonsterController;
|
||||
// Custom Zombie Thinkerating
|
||||
// By : Alex
|
||||
// Randomly plays a different moan sound for the Zombie each time it is called. Gruesome!
|
||||
defaultproperties
|
||||
{
|
||||
}
|
||||
58
sources/Zeds/Nice/NiceTeslaEMPNade.uc
Normal file
58
sources/Zeds/Nice/NiceTeslaEMPNade.uc
Normal file
|
|
@ -0,0 +1,58 @@
|
|||
// used for Tesla Husk self-destruct explosion on decapitation
|
||||
class NiceTeslaEMPNade extends NiceNade;
|
||||
var() class<Emitter> ExplosionEffect;
|
||||
function Timer(){
|
||||
if(bHidden)
Destroy();
|
||||
else if(Instigator != none && Instigator.Health > 0)
Explode(Location, vect(0,0,1));
|
||||
else
Disintegrate(Location, vect(0,0,1));
|
||||
}
|
||||
function TakeDamage( int Damage, Pawn InstigatedBy, Vector Hitlocation, Vector Momentum, class<DamageType> damageType, optional int HitIndex){}
|
||||
simulated function Explode(vector HitLocation, vector HitNormal)
|
||||
{
|
||||
local PlayerController LocalPlayer;
|
||||
bHasExploded = True;
|
||||
BlowUp(HitLocation);
|
||||
if(ExplodeSounds.length > 0)
PlaySound(ExplodeSounds[rand(ExplodeSounds.length)],, 2.0);
|
||||
if(EffectIsRelevant(Location, false)){
Spawn(ExplosionEffect,,, HitLocation, rotator(vect(0,0,1)));
Spawn(ExplosionDecal,self,,HitLocation, rotator(-HitNormal));
|
||||
}
|
||||
// Shake nearby players screens
|
||||
LocalPlayer = Level.GetLocalPlayerController();
|
||||
if((LocalPlayer != none) && (VSize(Location - LocalPlayer.ViewTarget.Location) < (DamageRadius * 1.5)))
LocalPlayer.ShakeView(RotMag, RotRate, RotTime, OffsetMag, OffsetRate, OffsetTime);
|
||||
if(Instigator != none){
// blow up the instigator
Instigator.TakeDamage(1000000, Instigator, Instigator.Location, vect(0,0,1), MyDamageType);
|
||||
}
|
||||
Destroy();
|
||||
}
|
||||
simulated function HurtRadius( float DamageAmount, float DamageRadius, class<DamageType> DamageType, float Momentum, vector HitLocation )
|
||||
{
|
||||
local actor Victims;
|
||||
local float damageScale, dist;
|
||||
local vector dir;
|
||||
local KFMonster KFMonsterVictim;
|
||||
local Pawn P;
|
||||
local KFPawn KFP;
|
||||
local array<Pawn> CheckedPawns;
|
||||
local int i;
|
||||
local bool bAlreadyChecked;
|
||||
|
||||
if ( bHurtEntry )
return;
|
||||
bHurtEntry = true;
|
||||
foreach CollidingActors (class 'Actor', Victims, DamageRadius, HitLocation)
|
||||
{
// don't let blast damage affect fluid - VisibleCollisingActors doesn't really work for them - jag
if( (Victims != self) && (Hurtwall != Victims) && (Victims.Role == ROLE_Authority) && !Victims.IsA('FluidSurfaceInfo')
&& ExtendedZCollision(Victims)==none )
{
if( (Instigator==none || Instigator.Health<=0) && KFPawn(Victims)!=none )
Continue;
dir = Victims.Location - HitLocation;
dist = FMax(1,VSize(dir));
dir = dir/dist;
damageScale = 1 - FMax(0,(dist - Victims.CollisionRadius)/DamageRadius);
|
||||
if ( Instigator == none || Instigator.Controller == none )
{
Victims.SetDelayedDamageInstigatorController( InstigatorController );
}
|
||||
P = Pawn(Victims);
|
||||
if( P != none )
{
for (i = 0; i < CheckedPawns.Length; i++)
{
if (CheckedPawns[i] == P)
{
bAlreadyChecked = true;
break;
}
}
|
||||
if( bAlreadyChecked )
{
bAlreadyChecked = false;
P = none;
continue;
}
|
||||
KFMonsterVictim = KFMonster(Victims);
|
||||
if( KFMonsterVictim != none && KFMonsterVictim.Health <= 0 )
{
KFMonsterVictim = none;
}
|
||||
KFP = KFPawn(Victims);
|
||||
if( KFMonsterVictim != none )
{
// 10x more damage zeds
damageScale *= 10.0 * KFMonsterVictim.GetExposureTo(Location + 15 * -Normal(PhysicsVolume.Gravity));
if ( ZombieFleshpound(KFMonsterVictim) != none )
damageScale *= 2.0; // compensate 50% dmg.res.
}
else if( KFP != none )
{
damageScale *= KFP.GetExposureTo(Location + 15 * -Normal(PhysicsVolume.Gravity));
}
|
||||
CheckedPawns[CheckedPawns.Length] = P;
|
||||
if ( damageScale <= 0)
{
P = none;
continue;
}
else
{
//Victims = P;
P = none;
}
}
|
||||
Victims.TakeDamage(damageScale * DamageAmount,Instigator,Victims.Location - 0.5 * (Victims.CollisionHeight + Victims.CollisionRadius)
* dir,(damageScale * Momentum * dir),DamageType);
|
||||
if (Vehicle(Victims) != none && Vehicle(Victims).Health > 0)
{
Vehicle(Victims).DriverRadiusDamage(DamageAmount, DamageRadius, InstigatorController, DamageType, Momentum, HitLocation);
}
}
|
||||
}
|
||||
bHurtEntry = false;
|
||||
}
|
||||
defaultproperties
|
||||
{
ExplosionEffect=Class'KFMod.ZEDMKIISecondaryProjectileExplosion'
ShrapnelClass=None
ExplodeSounds(0)=Sound'KF_FY_ZEDV2SND.Fire.WEP_ZEDV2_Secondary_Fire_S'
ExplodeSounds(1)=Sound'KF_FY_ZEDV2SND.Fire.WEP_ZEDV2_Secondary_Fire_S'
ExplodeSounds(2)=Sound'KF_FY_ZEDV2SND.Fire.WEP_ZEDV2_Secondary_Fire_S'
Speed=0.000000
Damage=50.000000
DamageRadius=400.000000
MyDamageType=Class'ScrnZedPack.DamTypeEMP'
DrawType=DT_None
bCollideActors=False
bBlockZeroExtentTraces=False
bBlockNonZeroExtentTraces=False
|
||||
}
|
||||
8
sources/Zeds/Nice/NiceZedDamageType.uc
Normal file
8
sources/Zeds/Nice/NiceZedDamageType.uc
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
class NiceZedDamageType extends NiceWeaponDamageType
|
||||
abstract;
|
||||
var material HUDDamageTex;
|
||||
var material HUDUberDamageTex;
|
||||
var float HUDTime;
|
||||
defaultproperties
|
||||
{
HUDDamageTex=Texture'KillingFloorHUD.BluntSplashNormal'
HUDUberDamageTex=Shader'KillingFloorHUD.BluntShaderuber'
HUDTime=0.900000
|
||||
}
|
||||
4
sources/Zeds/Nice/NiceZedMeleeDamageType.uc
Normal file
4
sources/Zeds/Nice/NiceZedMeleeDamageType.uc
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
class NiceZedMeleeDamageType extends NiceZedDamageType;
|
||||
defaultproperties
|
||||
{
DeathString="%o was eaten by %k."
FemaleSuicide="%o ate herself."
MaleSuicide="%o ate himself."
PawnDamageEmitter=Class'ROEffects.ROBloodPuff'
LowGoreDamageEmitter=Class'ROEffects.ROBloodPuffNoGore'
LowDetailEmitter=Class'ROEffects.ROBloodPuffSmall'
|
||||
}
|
||||
4
sources/Zeds/Nice/NiceZedSlashingDamageType.uc
Normal file
4
sources/Zeds/Nice/NiceZedSlashingDamageType.uc
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
class NiceZedSlashingDamageType extends NiceZedDamageType;
|
||||
defaultproperties
|
||||
{
HUDDamageTex=FinalBlend'KillingFloorHUD.SlashSplashNormalFB'
HUDUberDamageTex=FinalBlend'KillingFloorHUD.SlashSplashUberFB'
DeathString="%o was eaten by %k."
FemaleSuicide="%o ate herself."
MaleSuicide="%o ate himself."
PawnDamageEmitter=Class'ROEffects.ROBloodPuff'
LowGoreDamageEmitter=Class'ROEffects.ROBloodPuffNoGore'
LowDetailEmitter=Class'ROEffects.ROBloodPuffSmall'
|
||||
}
|
||||
271
sources/Zeds/Nice/NiceZombieBloat.uc
Normal file
271
sources/Zeds/Nice/NiceZombieBloat.uc
Normal file
|
|
@ -0,0 +1,271 @@
|
|||
// Zombie Monster for KF Invasion gametype
|
||||
class NiceZombieBloat extends NiceZombieBloatBase;
|
||||
#exec OBJ LOAD FILE=KF_EnemiesFinalSnd.uax
|
||||
//----------------------------------------------------------------------------
|
||||
// NOTE: All Variables are declared in the base class to eliminate hitching
|
||||
//----------------------------------------------------------------------------
|
||||
var class<FleshHitEmitter> BileExplosion;
|
||||
var class<FleshHitEmitter> BileExplosionHeadless;
|
||||
function bool FlipOver()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
// don't interrupt the bloat while he is puking
|
||||
simulated function bool HitCanInterruptAction()
|
||||
{
|
||||
if( bShotAnim )
|
||||
{
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
function DoorAttack(Actor A)
|
||||
{
|
||||
if ( bShotAnim || Physics == PHYS_Swimming)
return;
|
||||
else if ( A!=none )
|
||||
{
bShotAnim = true;
if( !bDecapitated && bDistanceAttackingDoor )
{
SetAnimAction('ZombieBarf');
}
else
{
SetAnimAction('DoorBash');
GotoState('DoorBashing');
}
|
||||
}
|
||||
}
|
||||
function RangedAttack(Actor A)
|
||||
{
|
||||
local int LastFireTime;
|
||||
if ( bShotAnim )
return;
|
||||
if ( Physics == PHYS_Swimming )
|
||||
{
SetAnimAction('Claw');
bShotAnim = true;
LastFireTime = Level.TimeSeconds;
|
||||
}
|
||||
else if ( VSize(A.Location - Location) < MeleeRange + CollisionRadius + A.CollisionRadius )
|
||||
{
bShotAnim = true;
LastFireTime = Level.TimeSeconds;
SetAnimAction('Claw');
//PlaySound(sound'Claw2s', SLOT_Interact); KFTODO: Replace this
Controller.bPreparingMove = true;
Acceleration = vect(0,0,0);
|
||||
}
|
||||
else if ( (KFDoorMover(A) != none || VSize(A.Location-Location) <= 250) && !bDecapitated )
|
||||
{
bShotAnim = true;
|
||||
// Randomly do a moving attack so the player can't kite the zed
if( FRand() < 0.8 )
{
SetAnimAction('ZombieBarfMoving');
RunAttackTimeout = GetAnimDuration('ZombieBarf', 1.0);
bMovingPukeAttack=true;
}
else
{
SetAnimAction('ZombieBarf');
Controller.bPreparingMove = true;
Acceleration = vect(0,0,0);
}
|
||||
// Randomly send out a message about Bloat Vomit burning(3% chance)
if ( FRand() < 0.03 && KFHumanPawn(A) != none && PlayerController(KFHumanPawn(A).Controller) != none )
{
PlayerController(KFHumanPawn(A).Controller).Speech('AUTO', 7, "");
}
|
||||
}
|
||||
}
|
||||
// Overridden to handle playing upper body only attacks when moving
|
||||
simulated event SetAnimAction(name NewAction)
|
||||
{
|
||||
local int meleeAnimIndex;
|
||||
local bool bWantsToAttackAndMove;
|
||||
if( NewAction=='' )
Return;
|
||||
bWantsToAttackAndMove = NewAction == 'ZombieBarfMoving';
|
||||
if( NewAction == 'Claw' )
|
||||
{
meleeAnimIndex = Rand(3);
NewAction = meleeAnims[meleeAnimIndex];
|
||||
}
|
||||
if( bWantsToAttackAndMove )
|
||||
{
ExpectingChannel = AttackAndMoveDoAnimAction(NewAction);
|
||||
}
|
||||
else
|
||||
{
ExpectingChannel = DoAnimAction(NewAction);
|
||||
}
|
||||
if( !bWantsToAttackAndMove && AnimNeedsWait(NewAction) )
|
||||
{
bWaitForAnim = true;
|
||||
}
|
||||
else
|
||||
{
bWaitForAnim = false;
|
||||
}
|
||||
if( Level.NetMode!=NM_Client )
|
||||
{
AnimAction = NewAction;
bResetAnimAct = True;
ResetAnimActTime = Level.TimeSeconds+0.3;
|
||||
}
|
||||
}
|
||||
// Handle playing the anim action on the upper body only if we're attacking and moving
|
||||
simulated function int AttackAndMoveDoAnimAction( name AnimName )
|
||||
{
|
||||
if( AnimName=='ZombieBarfMoving' )
|
||||
{
AnimBlendParams(1, 1.0, 0.0,, FireRootBone);
PlayAnim('ZombieBarf',, 0.1, 1);
|
||||
return 1;
|
||||
}
|
||||
return super.DoAnimAction( AnimName );
|
||||
}
|
||||
|
||||
function PlayDyingSound()
|
||||
{
|
||||
if( Level.NetMode!=NM_Client )
|
||||
{
if ( bGibbed )
{
PlaySound(sound'KF_EnemiesFinalSnd.Bloat_DeathPop', SLOT_Pain,2.0,true,525);
return;
}
|
||||
if( bDecapitated )
{
PlaySound(HeadlessDeathSound, SLOT_Pain,1.30,true,525);
}
else
{
PlaySound(sound'KF_EnemiesFinalSnd.Bloat_DeathPop', SLOT_Pain,2.0,true,525);
}
|
||||
}
|
||||
}
|
||||
|
||||
// Barf Time.
|
||||
function SpawnTwoShots()
|
||||
{
|
||||
local vector X,Y,Z, FireStart;
|
||||
local rotator FireRotation;
|
||||
if( Controller!=none && KFDoorMover(Controller.Target)!=none )
|
||||
{
Controller.Target.TakeDamage(22,Self,Location,vect(0,0,0),Class'DamTypeVomit');
return;
|
||||
}
|
||||
GetAxes(Rotation,X,Y,Z);
|
||||
FireStart = Location+(vect(30,0,64) >> Rotation)*DrawScale;
|
||||
if ( !SavedFireProperties.bInitialized )
|
||||
{
SavedFireProperties.AmmoClass = Class'SkaarjAmmo';
SavedFireProperties.ProjectileClass = Class'KFBloatVomit';
SavedFireProperties.WarnTargetPct = 1;
SavedFireProperties.MaxRange = 500;
SavedFireProperties.bTossed = False;
SavedFireProperties.bTrySplash = False;
SavedFireProperties.bLeadTarget = True;
SavedFireProperties.bInstantHit = True;
SavedFireProperties.bInitialized = True;
|
||||
}
|
||||
// Turn off extra collision before spawning vomit, otherwise spawn fails
|
||||
ToggleAuxCollision(false);
|
||||
FireRotation = Controller.AdjustAim(SavedFireProperties,FireStart,600);
|
||||
Spawn(Class'KFBloatVomit',,,FireStart,FireRotation);
|
||||
FireStart-=(0.5*CollisionRadius*Y);
|
||||
FireRotation.Yaw -= 1200;
|
||||
spawn(Class'KFBloatVomit',,,FireStart, FireRotation);
|
||||
FireStart+=(CollisionRadius*Y);
|
||||
FireRotation.Yaw += 2400;
|
||||
spawn(Class'KFBloatVomit',,,FireStart, FireRotation);
|
||||
// Turn extra collision back on
|
||||
ToggleAuxCollision(true);
|
||||
}
|
||||
|
||||
simulated function Tick(float deltatime)
|
||||
{
|
||||
local vector BileExplosionLoc;
|
||||
local FleshHitEmitter GibBileExplosion;
|
||||
Super.tick(deltatime);
|
||||
if( Role == ROLE_Authority && bMovingPukeAttack )
|
||||
{
// Keep moving toward the target until the timer runs out (anim finishes)
if( RunAttackTimeout > 0 )
{
RunAttackTimeout -= DeltaTime;
|
||||
if( RunAttackTimeout <= 0 )
{
RunAttackTimeout = 0;
bMovingPukeAttack=false;
}
}
|
||||
// Keep the gorefast moving toward its target when attacking
if( bShotAnim && !bWaitForAnim )
{
if( LookTarget!=none )
{
Acceleration = AccelRate * Normal(LookTarget.Location - Location);
}
}
|
||||
}
|
||||
// Hack to force animation updates on the server for the bloat if he is relevant to someone
|
||||
// He has glitches when some of his animations don't play on the server. If we
|
||||
// find some other fix for the glitches take this out - Ramm
|
||||
if( Level.NetMode != NM_Client && Level.NetMode != NM_Standalone )
|
||||
{
if( (Level.TimeSeconds-LastSeenOrRelevantTime) < 1.0 )
{
bForceSkelUpdate=true;
}
else
{
bForceSkelUpdate=false;
}
|
||||
}
|
||||
if ( Level.NetMode!=NM_DedicatedServer && /*Gored>0*/Health <= 0 && !bPlayBileSplash &&
HitDamageType != class'DamTypeBleedOut' )
|
||||
{
if ( !class'GameInfo'.static.UseLowGore() )
{
BileExplosionLoc = self.Location;
BileExplosionLoc.z += (CollisionHeight - (CollisionHeight * 0.5));
|
||||
if (bDecapitated)
{
GibBileExplosion = Spawn(BileExplosionHeadless,self,, BileExplosionLoc );
}
else
{
GibBileExplosion = Spawn(BileExplosion,self,, BileExplosionLoc );
}
bPlayBileSplash = true;
}
else
{
BileExplosionLoc = self.Location;
BileExplosionLoc.z += (CollisionHeight - (CollisionHeight * 0.5));
|
||||
GibBileExplosion = Spawn(class 'LowGoreBileExplosion',self,, BileExplosionLoc );
bPlayBileSplash = true;
}
|
||||
}
|
||||
}
|
||||
function BileBomb()
|
||||
{
|
||||
BloatJet = spawn(class'BileJet', self,,Location,Rotator(-PhysicsVolume.Gravity));
|
||||
}
|
||||
function PlayDyingAnimation(class<DamageType> DamageType, vector HitLoc)
|
||||
{
|
||||
// local bool AttachSucess;
|
||||
super.PlayDyingAnimation(DamageType, HitLoc);
|
||||
// Don't blow up with bleed out
|
||||
if( bDecapitated && DamageType == class'DamTypeBleedOut' )
|
||||
{
return;
|
||||
}
|
||||
if ( !class'GameInfo'.static.UseLowGore() )
|
||||
{
HideBone(SpineBone2);
|
||||
}
|
||||
if(Role == ROLE_Authority)
|
||||
{
BileBomb();
|
||||
// if(BloatJet!=none)
|
||||
// {
|
||||
// if(Gored < 5)
|
||||
// AttachSucess=AttachToBone(BloatJet,FireRootBone);
|
||||
// // else
|
||||
// // AttachSucess=AttachToBone(BloatJet,SpineBone1);
|
||||
//
|
||||
// if(!AttachSucess)
|
||||
// {
|
||||
// log("DEAD Bloaty Bile didn't like the Boning :o");
|
||||
// BloatJet.SetBase(self);
|
||||
// }
|
||||
// BloatJet.SetRelativeRotation(rot(0,-4096,0));
|
||||
// }
|
||||
}
|
||||
}
|
||||
simulated function ProcessHitFX()
|
||||
{
|
||||
local Coords boneCoords;
|
||||
local class<xEmitter> HitEffects[4];
|
||||
local int i,j;
|
||||
local float GibPerterbation;
|
||||
if( (Level.NetMode == NM_DedicatedServer) || bSkeletized || (Mesh == SkeletonMesh))
|
||||
{
SimHitFxTicker = HitFxTicker;
return;
|
||||
}
|
||||
for ( SimHitFxTicker = SimHitFxTicker; SimHitFxTicker != HitFxTicker; SimHitFxTicker = (SimHitFxTicker + 1) % ArrayCount(HitFX) )
|
||||
{
j++;
if ( j > 30 )
{
SimHitFxTicker = HitFxTicker;
return;
}
|
||||
if( (HitFX[SimHitFxTicker].damtype == none) || (Level.bDropDetail && (Level.TimeSeconds - LastRenderTime > 3) && !IsHumanControlled()) )
continue;
|
||||
//log("Processing effects for damtype "$HitFX[SimHitFxTicker].damtype);
|
||||
if( HitFX[SimHitFxTicker].bone == 'obliterate' && !class'GameInfo'.static.UseLowGore())
{
SpawnGibs( HitFX[SimHitFxTicker].rotDir, 1);
bGibbed = true;
// Wait a tick on a listen server so the obliteration can replicate before the pawn is destroyed
if( Level.NetMode == NM_ListenServer )
{
bDestroyNextTick = true;
TimeSetDestroyNextTickTime = Level.TimeSeconds;
}
else
{
Destroy();
}
return;
}
|
||||
boneCoords = GetBoneCoords( HitFX[SimHitFxTicker].bone );
|
||||
if ( !Level.bDropDetail && !class'GameInfo'.static.NoBlood() && !bSkeletized && !class'GameInfo'.static.UseLowGore() )
{
//AttachEmitterEffect( BleedingEmitterClass, HitFX[SimHitFxTicker].bone, boneCoords.Origin, HitFX[SimHitFxTicker].rotDir );
|
||||
HitFX[SimHitFxTicker].damtype.static.GetHitEffects( HitEffects, Health );
|
||||
if( !PhysicsVolume.bWaterVolume ) // don't attach effects under water
{
for( i = 0; i < ArrayCount(HitEffects); i++ )
{
if( HitEffects[i] == none )
continue;
|
||||
AttachEffect( HitEffects[i], HitFX[SimHitFxTicker].bone, boneCoords.Origin, HitFX[SimHitFxTicker].rotDir );
}
}
}
|
||||
if ( class'GameInfo'.static.UseLowGore() )
{
HitFX[SimHitFxTicker].bSever = false;
|
||||
switch( HitFX[SimHitFxTicker].bone )
{
case 'head':
if( !bHeadGibbed )
{
if ( HitFX[SimHitFxTicker].damtype == class'DamTypeDecapitation' )
{
DecapFX( boneCoords.Origin, HitFX[SimHitFxTicker].rotDir, false);
}
else if( HitFX[SimHitFxTicker].damtype == class'DamTypeProjectileDecap' )
{
DecapFX( boneCoords.Origin, HitFX[SimHitFxTicker].rotDir, false, true);
}
else if( HitFX[SimHitFxTicker].damtype == class'DamTypeMeleeDecapitation' )
{
DecapFX( boneCoords.Origin, HitFX[SimHitFxTicker].rotDir, true);
}
|
||||
bHeadGibbed=true;
}
break;
}
|
||||
return;
}
|
||||
if( HitFX[SimHitFxTicker].bSever )
{
GibPerterbation = HitFX[SimHitFxTicker].damtype.default.GibPerterbation;
|
||||
switch( HitFX[SimHitFxTicker].bone )
{
case 'obliterate':
break;
|
||||
case LeftThighBone:
if( !bLeftLegGibbed )
{
SpawnSeveredGiblet( DetachedLegClass, boneCoords.Origin, HitFX[SimHitFxTicker].rotDir, GibPerterbation, GetBoneRotation(HitFX[SimHitFxTicker].bone) );
KFSpawnGiblet( class 'KFMod.KFGibBrain',boneCoords.Origin, HitFX[SimHitFxTicker].rotDir, GibPerterbation, 250 ) ;
KFSpawnGiblet( class 'KFMod.KFGibBrainb',boneCoords.Origin, HitFX[SimHitFxTicker].rotDir, GibPerterbation, 250 ) ;
KFSpawnGiblet( class 'KFMod.KFGibBrain',boneCoords.Origin, HitFX[SimHitFxTicker].rotDir, GibPerterbation, 250 ) ;
bLeftLegGibbed=true;
}
break;
|
||||
case RightThighBone:
if( !bRightLegGibbed )
{
SpawnSeveredGiblet( DetachedLegClass, boneCoords.Origin, HitFX[SimHitFxTicker].rotDir, GibPerterbation, GetBoneRotation(HitFX[SimHitFxTicker].bone) );
KFSpawnGiblet( class 'KFMod.KFGibBrain',boneCoords.Origin, HitFX[SimHitFxTicker].rotDir, GibPerterbation, 250 ) ;
KFSpawnGiblet( class 'KFMod.KFGibBrainb',boneCoords.Origin, HitFX[SimHitFxTicker].rotDir, GibPerterbation, 250 ) ;
KFSpawnGiblet( class 'KFMod.KFGibBrain',boneCoords.Origin, HitFX[SimHitFxTicker].rotDir, GibPerterbation, 250 ) ;
bRightLegGibbed=true;
}
break;
|
||||
case LeftFArmBone:
if( !bLeftArmGibbed )
{
SpawnSeveredGiblet( DetachedArmClass, boneCoords.Origin, HitFX[SimHitFxTicker].rotDir, GibPerterbation, GetBoneRotation(HitFX[SimHitFxTicker].bone) );
KFSpawnGiblet( class 'KFMod.KFGibBrain',boneCoords.Origin, HitFX[SimHitFxTicker].rotDir, GibPerterbation, 250 ) ;
KFSpawnGiblet( class 'KFMod.KFGibBrainb',boneCoords.Origin, HitFX[SimHitFxTicker].rotDir, GibPerterbation, 250 ) ;;
bLeftArmGibbed=true;
}
break;
|
||||
case RightFArmBone:
if( !bRightArmGibbed )
{
SpawnSeveredGiblet( DetachedArmClass, boneCoords.Origin, HitFX[SimHitFxTicker].rotDir, GibPerterbation, GetBoneRotation(HitFX[SimHitFxTicker].bone) );
KFSpawnGiblet( class 'KFMod.KFGibBrain',boneCoords.Origin, HitFX[SimHitFxTicker].rotDir, GibPerterbation, 250 ) ;
KFSpawnGiblet( class 'KFMod.KFGibBrainb',boneCoords.Origin, HitFX[SimHitFxTicker].rotDir, GibPerterbation, 250 ) ;
bRightArmGibbed=true;
}
break;
|
||||
case 'head':
if( !bHeadGibbed )
{
if ( HitFX[SimHitFxTicker].damtype == class'DamTypeDecapitation' )
{
DecapFX( boneCoords.Origin, HitFX[SimHitFxTicker].rotDir, false);
}
else if( HitFX[SimHitFxTicker].damtype == class'DamTypeProjectileDecap' )
{
DecapFX( boneCoords.Origin, HitFX[SimHitFxTicker].rotDir, false, true);
}
else if( HitFX[SimHitFxTicker].damtype == class'DamTypeMeleeDecapitation' )
{
DecapFX( boneCoords.Origin, HitFX[SimHitFxTicker].rotDir, true);
}
|
||||
bHeadGibbed=true;
}
break;
}
|
||||
// Don't do this right now until we get the effects sorted - Ramm
if( HitFX[SimHitFXTicker].bone != 'Spine' && HitFX[SimHitFXTicker].bone != FireRootBone &&
HitFX[SimHitFXTicker].bone != LeftFArmBone && HitFX[SimHitFXTicker].bone != RightFArmBone &&
HitFX[SimHitFXTicker].bone != 'head' && Health <=0 )
HideBone(HitFX[SimHitFxTicker].bone);
}
|
||||
}
|
||||
}
|
||||
simulated function HideBone(name boneName)
|
||||
{
|
||||
local int BoneScaleSlot;
|
||||
local coords boneCoords;
|
||||
local bool bValidBoneToHide;
|
||||
if( boneName == LeftThighBone )
|
||||
{
boneScaleSlot = 0;
bValidBoneToHide = true;
if( SeveredLeftLeg == none )
{
SeveredLeftLeg = Spawn(SeveredLegAttachClass,self);
SeveredLeftLeg.SetDrawScale(SeveredLegAttachScale);
boneCoords = GetBoneCoords( 'lleg' );
AttachEmitterEffect( LimbSpurtEmitterClass, 'lleg', boneCoords.Origin, rot(0,0,0) );
AttachToBone(SeveredLeftLeg, 'lleg');
}
|
||||
}
|
||||
else if ( boneName == RightThighBone )
|
||||
{
boneScaleSlot = 1;
bValidBoneToHide = true;
if( SeveredRightLeg == none )
{
SeveredRightLeg = Spawn(SeveredLegAttachClass,self);
SeveredRightLeg.SetDrawScale(SeveredLegAttachScale);
boneCoords = GetBoneCoords( 'rleg' );
AttachEmitterEffect( LimbSpurtEmitterClass, 'rleg', boneCoords.Origin, rot(0,0,0) );
AttachToBone(SeveredRightLeg, 'rleg');
}
|
||||
}
|
||||
else if( boneName == RightFArmBone )
|
||||
{
boneScaleSlot = 2;
bValidBoneToHide = true;
if( SeveredRightArm == none )
{
SeveredRightArm = Spawn(SeveredArmAttachClass,self);
SeveredRightArm.SetDrawScale(SeveredArmAttachScale);
boneCoords = GetBoneCoords( 'rarm' );
AttachEmitterEffect( LimbSpurtEmitterClass, 'rarm', boneCoords.Origin, rot(0,0,0) );
AttachToBone(SeveredRightArm, 'rarm');
}
|
||||
}
|
||||
else if ( boneName == LeftFArmBone )
|
||||
{
boneScaleSlot = 3;
bValidBoneToHide = true;
if( SeveredLeftArm == none )
{
SeveredLeftArm = Spawn(SeveredArmAttachClass,self);
SeveredLeftArm.SetDrawScale(SeveredArmAttachScale);
boneCoords = GetBoneCoords( 'larm' );
AttachEmitterEffect( LimbSpurtEmitterClass, 'larm', boneCoords.Origin, rot(0,0,0) );
AttachToBone(SeveredLeftArm, 'larm');
}
|
||||
}
|
||||
else if ( boneName == HeadBone )
|
||||
{
// Only scale the bone down once
if( SeveredHead == none )
{
bValidBoneToHide = true;
boneScaleSlot = 4;
SeveredHead = Spawn(SeveredHeadAttachClass,self);
SeveredHead.SetDrawScale(SeveredHeadAttachScale);
boneCoords = GetBoneCoords( 'neck' );
AttachEmitterEffect( NeckSpurtEmitterClass, 'neck', boneCoords.Origin, rot(0,0,0) );
AttachToBone(SeveredHead, 'neck');
}
else
{
return;
}
|
||||
}
|
||||
else if ( boneName == 'spine' )
|
||||
{
bValidBoneToHide = true;
boneScaleSlot = 5;
|
||||
}
|
||||
else if ( boneName == SpineBone2 )
|
||||
{
bValidBoneToHide = true;
boneScaleSlot = 6;
|
||||
}
|
||||
// Only hide the bone if it is one of the arms, legs, or head, don't hide other misc bones
|
||||
if( bValidBoneToHide )
|
||||
{
SetBoneScale(BoneScaleSlot, 0.0, BoneName);
|
||||
}
|
||||
}
|
||||
|
||||
State Dying
|
||||
{
|
||||
function tick(float deltaTime)
|
||||
{
|
||||
if (BloatJet != none)
|
||||
{
|
||||
BloatJet.SetLocation(location);
|
||||
BloatJet.SetRotation(GetBoneRotation(FireRootBone));
|
||||
}
|
||||
super.tick(deltaTime);
|
||||
}
|
||||
}
|
||||
function RemoveHead()
|
||||
{
|
||||
bCanDistanceAttackDoors = False;
|
||||
Super.RemoveHead();
|
||||
}
|
||||
function ModDamage(out int Damage, Pawn instigatedBy, Vector hitlocation, Vector momentum, class<NiceWeaponDamageType> damageType, float headshotLevel, KFPlayerReplicationInfo KFPRI, optional float lockonTime){
|
||||
if(damageType == class 'DamTypeVomit' || damageType == class 'DamTypeBlowerThrower')
Damage = 0;
|
||||
else
Super.ModDamage(Damage, instigatedBy, hitlocation, momentum, damageType, headshotLevel, KFPRI);
|
||||
}
|
||||
static simulated function PreCacheStaticMeshes(LevelInfo myLevel)
|
||||
{//should be derived and used.
|
||||
Super.PreCacheStaticMeshes(myLevel);
|
||||
myLevel.AddPrecacheStaticMesh(StaticMesh'kf_gore_trip_sm.limbs.bloat_head');
|
||||
}
|
||||
static simulated function PreCacheMaterials(LevelInfo myLevel)
|
||||
{
|
||||
myLevel.AddPrecacheMaterial(Combiner'KF_Specimens_Trip_T.bloat_cmb');
|
||||
myLevel.AddPrecacheMaterial(Combiner'KF_Specimens_Trip_T.bloat_env_cmb');
|
||||
myLevel.AddPrecacheMaterial(Texture'KF_Specimens_Trip_T.bloat_diffuse');
|
||||
}
|
||||
defaultproperties
|
||||
{
BileExplosion=Class'KFMod.BileExplosion'
BileExplosionHeadless=Class'KFMod.BileExplosionHeadless'
stunLoopStart=0.100000
stunLoopEnd=0.600000
idleInsertFrame=0.950000
EventClasses(0)="NicePack.NiceZombieBloat"
MoanVoice=SoundGroup'KF_EnemiesFinalSnd.Bloat.Bloat_Talk'
MeleeAttackHitSound=SoundGroup'KF_EnemiesFinalSnd.Bloat.Bloat_HitPlayer'
JumpSound=SoundGroup'KF_EnemiesFinalSnd.Bloat.Bloat_Jump'
DetachedArmClass=Class'KFChar.SeveredArmBloat'
DetachedLegClass=Class'KFChar.SeveredLegBloat'
DetachedHeadClass=Class'KFChar.SeveredHeadBloat'
HitSound(0)=SoundGroup'KF_EnemiesFinalSnd.Bloat.Bloat_Pain'
DeathSound(0)=SoundGroup'KF_EnemiesFinalSnd.Bloat.Bloat_Death'
ChallengeSound(0)=SoundGroup'KF_EnemiesFinalSnd.Bloat.Bloat_Challenge'
ChallengeSound(1)=SoundGroup'KF_EnemiesFinalSnd.Bloat.Bloat_Challenge'
ChallengeSound(2)=SoundGroup'KF_EnemiesFinalSnd.Bloat.Bloat_Challenge'
ChallengeSound(3)=SoundGroup'KF_EnemiesFinalSnd.Bloat.Bloat_Challenge'
AmbientSound=Sound'KF_BaseBloat.Bloat_Idle1Loop'
Mesh=SkeletalMesh'KF_Freaks_Trip.Bloat_Freak'
Skins(0)=Combiner'KF_Specimens_Trip_T.bloat_cmb'
|
||||
}
|
||||
15
sources/Zeds/Nice/NiceZombieBloatBase.uc
Normal file
15
sources/Zeds/Nice/NiceZombieBloatBase.uc
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
// Zombie Monster for KF Invasion gametype
|
||||
class NiceZombieBloatBase extends NiceMonster
|
||||
abstract;
|
||||
#exec OBJ LOAD FILE=KF_EnemiesFinalSnd.uax
|
||||
var BileJet BloatJet;
|
||||
var bool bPlayBileSplash;
|
||||
var bool bMovingPukeAttack;
|
||||
var float RunAttackTimeout;
|
||||
//-------------------------------------------------------------------------------
|
||||
// NOTE: All Code resides in the child class(this class was only created to
|
||||
// eliminate hitching caused by loading default properties during play)
|
||||
//-------------------------------------------------------------------------------
|
||||
defaultproperties
|
||||
{
StunThreshold=4.000000
fuelRatio=0.250000
clientHeadshotScale=1.500000
MeleeAnims(0)="BloatChop2"
MeleeAnims(1)="BloatChop2"
MeleeAnims(2)="BloatChop2"
BleedOutDuration=6.000000
ZapThreshold=0.500000
ZappedDamageMod=1.500000
bHarpoonToBodyStuns=False
ZombieFlag=1
MeleeDamage=14
damageForce=70000
bFatAss=True
KFRagdollName="Bloat_Trip"
PuntAnim="BloatPunt"
Intelligence=BRAINS_Stupid
bCanDistanceAttackDoors=True
bUseExtendedCollision=True
ColOffset=(Z=60.000000)
ColRadius=27.000000
ColHeight=22.000000
SeveredArmAttachScale=1.100000
SeveredLegAttachScale=1.300000
SeveredHeadAttachScale=1.700000
PlayerCountHealthScale=0.250000
OnlineHeadshotOffset=(X=5.000000,Z=70.000000)
OnlineHeadshotScale=1.500000
AmmunitionClass=Class'KFMod.BZombieAmmo'
ScoringValue=17
IdleHeavyAnim="BloatIdle"
IdleRifleAnim="BloatIdle"
MeleeRange=30.000000
GroundSpeed=75.000000
WaterSpeed=102.000000
HealthMax=525.000000
Health=525
HeadHeight=2.500000
HeadScale=1.500000
AmbientSoundScaling=8.000000
MenuName="Nice Bloat"
MovementAnims(0)="WalkBloat"
MovementAnims(1)="WalkBloat"
WalkAnims(0)="WalkBloat"
WalkAnims(1)="WalkBloat"
WalkAnims(2)="WalkBloat"
WalkAnims(3)="WalkBloat"
IdleCrouchAnim="BloatIdle"
IdleWeaponAnim="BloatIdle"
IdleRestAnim="BloatIdle"
DrawScale=1.075000
PrePivot=(Z=5.000000)
SoundVolume=200
Mass=400.000000
RotationRate=(Yaw=45000,Roll=0)
|
||||
}
|
||||
871
sources/Zeds/Nice/NiceZombieBoss.uc
Normal file
871
sources/Zeds/Nice/NiceZombieBoss.uc
Normal file
|
|
@ -0,0 +1,871 @@
|
|||
// Zombie Monster for KF Invasion gametype
|
||||
class NiceZombieBoss extends NiceZombieBossBase;
|
||||
#exec OBJ LOAD FILE=KFPatch2.utx
|
||||
#exec OBJ LOAD FILE=KF_Specimens_Trip_T.utx
|
||||
//----------------------------------------------------------------------------
|
||||
// NOTE: Most Variables are declared in the base class to eliminate hitching
|
||||
//----------------------------------------------------------------------------
|
||||
var NiceBossHPNeedle CurrentNeedle;
|
||||
// Last time we checked if a player was melee exploiting us
|
||||
var float LastMeleeExploitCheckTime;
|
||||
// Used to track what type of melee exploiters you have
|
||||
var int NumLumberJacks;
|
||||
var int NumNinjas;
|
||||
// Make the Boss's ambient scale higher, since there is only 1, doesn't matter if he's relevant almost all the time
|
||||
simulated function CalcAmbientRelevancyScale()
|
||||
{
// Make the zed only relevant by thier ambient sound out to a range of 100 meters
CustomAmbientRelevancyScale = 5000/(100 * SoundRadius);
|
||||
}
|
||||
function vector ComputeTrajectoryByTime( vector StartPosition, vector EndPosition, float fTimeEnd )
|
||||
{
|
||||
local vector NewVelocity;
|
||||
NewVelocity = Super.ComputeTrajectoryByTime( StartPosition, EndPosition, fTimeEnd );
|
||||
if( PhysicsVolume.IsA( 'KFPhysicsVolume' ) && StartPosition.Z < EndPosition.Z )
|
||||
{
if( PhysicsVolume.Gravity.Z < class'PhysicsVolume'.default.Gravity.Z )
{
// Just checking mass to be extra-cautious.
if( Mass > 900 )
{
// Extra velocity boost to counter oversized mass weighing the boss down.
NewVelocity.Z += 90;
}
}
|
||||
}
|
||||
return NewVelocity;
|
||||
}
|
||||
function ZombieMoan()
|
||||
{
|
||||
if( !bShotAnim ) // Do not moan while taunting
Super.ZombieMoan();
|
||||
}
|
||||
// Speech notifies called from the anims
|
||||
function PatriarchKnockDown()
|
||||
{
|
||||
PlaySound(SoundGroup'KF_EnemiesFinalSnd.Patriarch.Kev_KnockedDown', SLOT_Misc, 2.0,true,500.0);
|
||||
}
|
||||
function PatriarchEntrance()
|
||||
{
|
||||
PlaySound(SoundGroup'KF_EnemiesFinalSnd.Patriarch.Kev_Entrance', SLOT_Misc, 2.0,true,500.0);
|
||||
}
|
||||
function PatriarchVictory()
|
||||
{
|
||||
PlaySound(SoundGroup'KF_EnemiesFinalSnd.Patriarch.Kev_Victory', SLOT_Misc, 2.0,true,500.0);
|
||||
}
|
||||
function PatriarchMGPreFire()
|
||||
{
|
||||
PlaySound(SoundGroup'KF_EnemiesFinalSnd.Patriarch.Kev_WarnGun', SLOT_Misc, 2.0,true,1000.0);
|
||||
}
|
||||
function PatriarchMisslePreFire()
|
||||
{
|
||||
PlaySound(SoundGroup'KF_EnemiesFinalSnd.Patriarch.Kev_WarnRocket', SLOT_Misc, 2.0,true,1000.0);
|
||||
}
|
||||
// Taunt to use when doing the melee exploit radial attack
|
||||
function PatriarchRadialTaunt()
|
||||
{
|
||||
if( NumNinjas > 0 && NumNinjas > NumLumberJacks )
|
||||
{
PlaySound(SoundGroup'KF_EnemiesFinalSnd.Patriarch.Kev_TauntNinja', SLOT_Misc, 2.0,true,500.0);
|
||||
}
|
||||
else if( NumLumberJacks > 0 && NumLumberJacks > NumNinjas )
|
||||
{
PlaySound(SoundGroup'KF_EnemiesFinalSnd.Patriarch.Kev_TauntLumberJack', SLOT_Misc, 2.0,true,500.0);
|
||||
}
|
||||
else
|
||||
{
PlaySound(SoundGroup'KF_EnemiesFinalSnd.Patriarch.Kev_TauntRadial', SLOT_Misc, 2.0,true,500.0);
|
||||
}
|
||||
}
|
||||
// Don't do this for the Patriarch
|
||||
// No need to overload these when inheriting from NiceMonster
|
||||
/*simulated function SetBurningBehavior(){}
|
||||
simulated function UnSetBurningBehavior(){}*/
|
||||
function bool CanGetOutOfWay(){
|
||||
return false;
|
||||
}
|
||||
simulated function Tick(float DeltaTime)
|
||||
{
|
||||
local KFHumanPawn HP;
|
||||
Super.Tick(DeltaTime);
|
||||
// Process the pipe bomb time damage scale, reducing the scale over time so
|
||||
// it goes back up to 100% damage over a few seconds
|
||||
if( Role == ROLE_Authority )
|
||||
{
PipeBombDamageScale -= DeltaTime * 0.33;
|
||||
if( PipeBombDamageScale < 0 )
{
PipeBombDamageScale = 0;
}
|
||||
}
|
||||
if( Level.NetMode==NM_DedicatedServer )
Return; // Servers aren't intrested in this info.
|
||||
bSpecialCalcView = bIsBossView;
|
||||
if( bZapped )
|
||||
{
// Make sure we check if we need to be cloaked as soon as the zap wears off
LastCheckTimes = Level.TimeSeconds;
|
||||
}
|
||||
else if( bCloaked && Level.TimeSeconds>LastCheckTimes )
|
||||
{
LastCheckTimes = Level.TimeSeconds+0.8;
ForEach VisibleCollidingActors(Class'KFHumanPawn',HP,1000,Location)
{
if( HP.Health <= 0 || !HP.ShowStalkers() )
continue;
|
||||
// If he's a commando, we've been spotted.
if( !bSpotted )
{
bSpotted = True;
CloakBoss();
}
Return;
}
// if we're uberbrite, turn down the light
if( bSpotted )
{
bSpotted = False;
bUnlit = false;
CloakBoss();
}
|
||||
}
|
||||
}
|
||||
simulated function CloakBoss()
|
||||
{
|
||||
local Controller C;
|
||||
local int Index;
|
||||
// No cloaking if zapped
|
||||
if( bZapped )
|
||||
{
return;
|
||||
}
|
||||
if( bSpotted )
|
||||
{
Visibility = 120;
if( Level.NetMode==NM_DedicatedServer )
Return;
Skins[0] = Finalblend'KFX.StalkerGlow';
Skins[1] = Finalblend'KFX.StalkerGlow';
bUnlit = true;
return;
|
||||
}
|
||||
Visibility = 1;
|
||||
bCloaked = true;
|
||||
if( Level.NetMode!=NM_Client )
|
||||
{
For( C=Level.ControllerList; C!=none; C=C.NextController )
{
if( C.bIsPlayer && C.Enemy==Self )
C.Enemy = none; // Make bots lose sight with me.
}
|
||||
}
|
||||
if( Level.NetMode==NM_DedicatedServer )
Return;
|
||||
Skins[0] = Shader'KF_Specimens_Trip_T.patriarch_invisible_gun';
|
||||
Skins[1] = Shader'KF_Specimens_Trip_T.patriarch_invisible';
|
||||
// Invisible - no shadow
|
||||
if(PlayerShadow != none)
PlayerShadow.bShadowActive = false;
|
||||
// Remove/disallow projectors on invisible people
|
||||
Projectors.Remove(0, Projectors.Length);
|
||||
bAcceptsProjectors = false;
|
||||
SetOverlayMaterial(FinalBlend'KF_Specimens_Trip_T.patriarch_fizzle_FB', 1.0, true);
|
||||
// Randomly send out a message about Patriarch going invisible(10% chance)
|
||||
if ( FRand() < 0.10 )
|
||||
{
// Pick a random Player to say the message
Index = Rand(Level.Game.NumPlayers);
|
||||
for ( C = Level.ControllerList; C != none; C = C.NextController )
{
if ( PlayerController(C) != none )
{
if ( Index == 0 )
{
PlayerController(C).Speech('AUTO', 8, "");
break;
}
|
||||
Index--;
}
}
|
||||
}
|
||||
}
|
||||
simulated function UnCloakBoss()
|
||||
{
|
||||
if( bZapped )
|
||||
{
return;
|
||||
}
|
||||
Visibility = default.Visibility;
|
||||
bCloaked = false;
|
||||
bSpotted = False;
|
||||
bUnlit = False;
|
||||
if( Level.NetMode==NM_DedicatedServer )
Return;
|
||||
Skins = Default.Skins;
|
||||
if (PlayerShadow != none)
PlayerShadow.bShadowActive = true;
|
||||
bAcceptsProjectors = true;
|
||||
SetOverlayMaterial( none, 0.0, true );
|
||||
}
|
||||
// Set the zed to the zapped behavior
|
||||
simulated function SetZappedBehavior()
|
||||
{
|
||||
super.SetZappedBehavior();
|
||||
// Handle setting the zed to uncloaked so the zapped overlay works properly
|
||||
if( Level.Netmode != NM_DedicatedServer )
|
||||
{
bUnlit = false;
Skins = Default.Skins;
|
||||
if (PlayerShadow != none)
PlayerShadow.bShadowActive = true;
|
||||
bAcceptsProjectors = true;
SetOverlayMaterial(Material'KFZED_FX_T.Energy.ZED_overlay_Hit_Shdr', 999, true);
|
||||
}
|
||||
}
|
||||
// Turn off the zapped behavior
|
||||
simulated function UnSetZappedBehavior()
|
||||
{
|
||||
super.UnSetZappedBehavior();
|
||||
// Handle getting the zed back cloaked if need be
|
||||
if( Level.Netmode != NM_DedicatedServer )
|
||||
{
LastCheckTimes = Level.TimeSeconds;
SetOverlayMaterial(none, 0.0f, true);
|
||||
}
|
||||
}
|
||||
// Overridden because we need to handle the overlays differently for zombies that can cloak
|
||||
function SetZapped(float ZapAmount, Pawn Instigator)
|
||||
{
|
||||
LastZapTime = Level.TimeSeconds;
|
||||
if( bZapped )
|
||||
{
TotalZap = ZapThreshold;
RemainingZap = ZapDuration;
|
||||
}
|
||||
else
|
||||
{
TotalZap += ZapAmount;
|
||||
if( TotalZap >= ZapThreshold )
{
RemainingZap = ZapDuration;
bZapped = true;
}
|
||||
}
|
||||
ZappedBy = Instigator;
|
||||
}
|
||||
//-----------------------------------------------------------------------------
|
||||
// PostBeginPlay
|
||||
//-----------------------------------------------------------------------------
|
||||
simulated function PostBeginPlay()
|
||||
{
|
||||
super.PostBeginPlay();
|
||||
if( Role < ROLE_Authority )
|
||||
{
return;
|
||||
}
|
||||
// Difficulty Scaling
|
||||
if (Level.Game != none)
|
||||
{
if(Level.Game.GameDifficulty >= 5.0) // Hell on Earth & Suicidal
MGDamage = default.MGDamage * 1.3;
else
MGDamage = default.MGDamage * 1.0;
|
||||
}
|
||||
HealingLevels[0] = Health/1.25; // Around 5600 HP
|
||||
HealingLevels[1] = Health/2.f; // Around 3500 HP
|
||||
HealingLevels[2] = Health/3.2; // Around 2187 HP
|
||||
// log("Health = "$Health);
|
||||
// log("HealingLevels[0] = "$HealingLevels[0]);
|
||||
// log("HealingLevels[1] = "$HealingLevels[1]);
|
||||
// log("HealingLevels[2] = "$HealingLevels[2]);
|
||||
HealingAmount = Health/4; // 1750 HP
|
||||
// log("HealingAmount = "$HealingAmount);
|
||||
}
|
||||
function bool MakeGrandEntry()
|
||||
{
|
||||
bShotAnim = true;
|
||||
Acceleration = vect(0,0,0);
|
||||
SetAnimAction('Entrance');
|
||||
HandleWaitForAnim('Entrance');
|
||||
GotoState('MakingEntrance');
|
||||
return True;
|
||||
}
|
||||
// State of playing the initial entrance anim
|
||||
state MakingEntrance
|
||||
{
|
||||
Ignores RangedAttack;
|
||||
function Tick( float Delta )
|
||||
{
Acceleration = vect(0,0,0);
|
||||
global.Tick(Delta);
|
||||
}
|
||||
Begin:
|
||||
Sleep(GetAnimDuration('Entrance'));
|
||||
GotoState('InitialSneak');
|
||||
}
|
||||
// State of doing a radial damaging attack that we do when people are trying to melee exploit
|
||||
state RadialAttack
|
||||
{
|
||||
Ignores RangedAttack;
|
||||
function bool ShouldChargeFromDamage()
|
||||
{
return false;
|
||||
}
|
||||
function Tick( float Delta )
|
||||
{
Acceleration = vect(0,0,0);
|
||||
//DrawDebugSphere( Location, 150, 12, 0, 255, 0);
|
||||
global.Tick(Delta);
|
||||
}
|
||||
function ClawDamageTarget()
|
||||
{
local vector PushDir;
local float UsedMeleeDamage;
local bool bDamagedSomeone, bDamagedThisHit;
local KFHumanPawn P;
local Actor OldTarget;
local float RadialDamageBase;
|
||||
MeleeRange = 150;
|
||||
if(Controller!=none && Controller.Target!=none)
PushDir = (damageForce * Normal(Controller.Target.Location - Location));
else
PushDir = damageForce * vector(Rotation);
|
||||
OldTarget = Controller.Target;
|
||||
// Damage all players within a radius
foreach DynamicActors(class'KFHumanPawn', P)
{
if ( VSize(P.Location - Location) < MeleeRange)
{
Controller.Target = P;
|
||||
// This attack cuts through shields, so crank up the damage if they have a lot of shields
if( P.ShieldStrength >= 50 )
{
RadialDamageBase = 240;
}
else
{
RadialDamageBase = 120;
}
|
||||
// Randomize the damage a bit so everyone gets really hurt, but only some poeple die
UsedMeleeDamage = (RadialDamageBase - (RadialDamageBase * 0.55)) + (RadialDamageBase * (FRand() * 0.45));
//log("UsedMeleeDamage = "$UsedMeleeDamage);
|
||||
bDamagedThisHit = MeleeDamageTarget(UsedMeleeDamage, damageForce * Normal(P.Location - Location));
if( !bDamagedSomeone && bDamagedThisHit )
{
bDamagedSomeone = true;
}
MeleeRange = 150;
}
}
|
||||
Controller.Target = OldTarget;
|
||||
MeleeRange = Default.MeleeRange;
|
||||
if ( bDamagedSomeone )
{
// Maybe cause zedtime when the patriarch does his radial attack
KFGameType(Level.Game).DramaticEvent(0.3);
PlaySound(MeleeAttackHitSound, SLOT_Interact, 2.0);
}
|
||||
}
|
||||
function EndState()
|
||||
{
NumLumberJacks = 0;
NumNinjas = 0;
|
||||
}
|
||||
Begin:
|
||||
// Don't let the zed move and play the radial attack
|
||||
bShotAnim = true;
|
||||
Acceleration = vect(0,0,0);
|
||||
SetAnimAction('RadialAttack');
|
||||
KFMonsterController(Controller).bUseFreezeHack = True;
|
||||
HandleWaitForAnim('RadialAttack');
|
||||
Sleep(GetAnimDuration('RadialAttack'));
|
||||
// TODO: this sleep is here to allow for playing the taunt sound. Take it out when the animation is extended with the taunt - Ramm
|
||||
//Sleep(2.5);
|
||||
GotoState('');
|
||||
}
|
||||
simulated function Destroyed()
|
||||
{
|
||||
if( mTracer!=none )
mTracer.Destroy();
|
||||
if( mMuzzleFlash!=none )
mMuzzleFlash.Destroy();
|
||||
Super.Destroyed();
|
||||
}
|
||||
simulated Function PostNetBeginPlay()
|
||||
{
|
||||
EnableChannelNotify ( 1,1);
|
||||
AnimBlendParams(1, 1.0, 0.0,, SpineBone1);
|
||||
super.PostNetBeginPlay();
|
||||
TraceHitPos = vect(0,0,0);
|
||||
bNetNotify = True;
|
||||
}
|
||||
function bool IsStunPossible(){
|
||||
return false;
|
||||
}
|
||||
function bool CheckStun(int stunScore,
Pawn instigatedBy,
Vector hitLocation,
Vector momentum,
class<NiceWeaponDamageType> damageType,
float headshotLevel,
KFPlayerReplicationInfo KFPRI){
|
||||
return false;
|
||||
}
|
||||
function bool CheckMiniFlinch( int flinchScore,
Pawn instigatedBy,
Vector hitLocation,
Vector momentum,
class<NiceWeaponDamageType> damageType,
float headshotLevel,
KFPlayerReplicationInfo KFPRI){
|
||||
return false;
|
||||
}
|
||||
function bool OnlyEnemyAround( Pawn Other )
|
||||
{
|
||||
local Controller C;
|
||||
For( C=Level.ControllerList; C!=none; C=C.NextController )
|
||||
{
if( C.bIsPlayer && C.Pawn!=none && C.Pawn!=Other && ((VSize(C.Pawn.Location-Location)<1500 && FastTrace(C.Pawn.Location,Location))
|| (VSize(C.Pawn.Location-Other.Location)<1000 && FastTrace(C.Pawn.Location,Other.Location))) )
Return False;
|
||||
}
|
||||
Return True;
|
||||
}
|
||||
function bool IsCloseEnuf( Actor A )
|
||||
{
|
||||
local vector V;
|
||||
if( A==none )
Return False;
|
||||
V = A.Location-Location;
|
||||
if( Abs(V.Z)>(CollisionHeight+A.CollisionHeight) )
Return False;
|
||||
V.Z = 0;
|
||||
Return (VSize(V)<(CollisionRadius+A.CollisionRadius+25));
|
||||
}
|
||||
function RangedAttack(Actor A)
|
||||
{
|
||||
local float D;
|
||||
local bool bOnlyE;
|
||||
local bool bDesireChainGun;
|
||||
// Randomly make him want to chaingun more
|
||||
if( Controller.LineOfSightTo(A) && FRand() < 0.15 && LastChainGunTime<Level.TimeSeconds )
|
||||
{
bDesireChainGun = true;
|
||||
}
|
||||
if ( bShotAnim )
return;
|
||||
D = VSize(A.Location-Location);
|
||||
bOnlyE = (Pawn(A)!=none && OnlyEnemyAround(Pawn(A)));
|
||||
if ( IsCloseEnuf(A) )
|
||||
{
bShotAnim = true;
if( Health>1500 && Pawn(A)!=none && FRand() < 0.5 )
{
SetAnimAction('MeleeImpale');
}
else
{
SetAnimAction('MeleeClaw');
//PlaySound(sound'Claw2s', SLOT_none); KFTODO: Replace this
}
|
||||
}
|
||||
else if( Level.TimeSeconds - LastSneakedTime > 20.0 )
|
||||
{
if( FRand() < 0.3 )
{
// Wait another 20 to try this again
LastSneakedTime = Level.TimeSeconds;//+FRand()*120;
Return;
}
SetAnimAction('transition');
GoToState('SneakAround');
|
||||
}
|
||||
else if( bChargingPlayer && (bOnlyE || D<200) )
Return;
|
||||
else if( !bDesireChainGun && !bChargingPlayer && (D<300 || (D<700 && bOnlyE)) &&
(Level.TimeSeconds - LastChargeTime > (5.0 + 5.0 * FRand())) ) // Don't charge again for a few seconds
|
||||
{
SetAnimAction('transition');
GoToState('Charging');
|
||||
}
|
||||
else if( LastMissileTime<Level.TimeSeconds && D > 500 )
|
||||
{
if( !Controller.LineOfSightTo(A) || FRand() > 0.75 )
{
LastMissileTime = Level.TimeSeconds+FRand() * 5;
Return;
}
|
||||
LastMissileTime = Level.TimeSeconds + 10 + FRand() * 15;
|
||||
bShotAnim = true;
Acceleration = vect(0,0,0);
SetAnimAction('PreFireMissile');
|
||||
HandleWaitForAnim('PreFireMissile');
|
||||
GoToState('FireMissile');
|
||||
}
|
||||
else if ( !bWaitForAnim && !bShotAnim && LastChainGunTime<Level.TimeSeconds )
|
||||
{
if ( !Controller.LineOfSightTo(A) || FRand()> 0.85 )
{
LastChainGunTime = Level.TimeSeconds+FRand()*4;
Return;
}
|
||||
LastChainGunTime = Level.TimeSeconds + 5 + FRand() * 10;
|
||||
bShotAnim = true;
Acceleration = vect(0,0,0);
SetAnimAction('PreFireMG');
|
||||
HandleWaitForAnim('PreFireMG');
MGFireCounter = Rand(60) + 35;
|
||||
GoToState('FireChaingun');
|
||||
}
|
||||
}
|
||||
event Bump(actor Other)
|
||||
{
|
||||
Super(Monster).Bump(Other);
|
||||
if( Other==none )
return;
|
||||
if( Other.IsA('NetKActor') && Physics != PHYS_Falling && !bShotAnim && Abs(Other.Location.Z-Location.Z)<(CollisionHeight+Other.CollisionHeight) )
|
||||
{ // Kill the annoying deco brat.
Controller.Target = Other;
Controller.Focus = Other;
bShotAnim = true;
Acceleration = (Other.Location-Location);
SetAnimAction('MeleeClaw');
//PlaySound(sound'Claw2s', SLOT_none); KFTODO: Replace this
HandleWaitForAnim('MeleeClaw');
|
||||
}
|
||||
}
|
||||
simulated function AddTraceHitFX( vector HitPos )
|
||||
{
|
||||
local vector Start,SpawnVel,SpawnDir;
|
||||
local float hitDist;
|
||||
Start = GetBoneCoords('tip').Origin;
|
||||
if( mTracer==none )
mTracer = Spawn(Class'KFMod.KFNewTracer',,,Start);
|
||||
else mTracer.SetLocation(Start);
|
||||
if( mMuzzleFlash==none )
|
||||
{
// KFTODO: Replace this
mMuzzleFlash = Spawn(Class'MuzzleFlash3rdMG');
AttachToBone(mMuzzleFlash, 'tip');
|
||||
}
|
||||
else mMuzzleFlash.SpawnParticle(1);
|
||||
hitDist = VSize(HitPos - Start) - 50.f;
|
||||
if( hitDist>10 )
|
||||
{
SpawnDir = Normal(HitPos - Start);
SpawnVel = SpawnDir * 10000.f;
mTracer.Emitters[0].StartVelocityRange.X.Min = SpawnVel.X;
mTracer.Emitters[0].StartVelocityRange.X.Max = SpawnVel.X;
mTracer.Emitters[0].StartVelocityRange.Y.Min = SpawnVel.Y;
mTracer.Emitters[0].StartVelocityRange.Y.Max = SpawnVel.Y;
mTracer.Emitters[0].StartVelocityRange.Z.Min = SpawnVel.Z;
mTracer.Emitters[0].StartVelocityRange.Z.Max = SpawnVel.Z;
mTracer.Emitters[0].LifetimeRange.Min = hitDist / 10000.f;
mTracer.Emitters[0].LifetimeRange.Max = mTracer.Emitters[0].LifetimeRange.Min;
mTracer.SpawnParticle(1);
|
||||
}
|
||||
Instigator = Self;
|
||||
if( HitPos != vect(0,0,0) )
|
||||
{
Spawn(class'ROBulletHitEffect',,, HitPos, Rotator(Normal(HitPos - Start)));
|
||||
}
|
||||
}
|
||||
simulated function AnimEnd( int Channel )
|
||||
{
|
||||
local name Sequence;
|
||||
local float Frame, Rate;
|
||||
if( Level.NetMode==NM_Client && bMinigunning )
|
||||
{
GetAnimParams( Channel, Sequence, Frame, Rate );
|
||||
if( Sequence != 'PreFireMG' && Sequence != 'FireMG' )
{
Super.AnimEnd(Channel);
return;
}
|
||||
PlayAnim('FireMG');
bWaitForAnim = true;
bShotAnim = true;
IdleTime = Level.TimeSeconds;
|
||||
}
|
||||
else Super.AnimEnd(Channel);
|
||||
}
|
||||
state FireChaingun
|
||||
{
|
||||
function RangedAttack(Actor A)
|
||||
{
Controller.Target = A;
Controller.Focus = A;
|
||||
}
|
||||
// Chaingun mode handles this itself
|
||||
function bool ShouldChargeFromDamage()
|
||||
{
return false;
|
||||
}
|
||||
function TakeDamage( int Damage, Pawn InstigatedBy, Vector Hitlocation, Vector Momentum, class<DamageType> damageType, optional int HitIndex)
|
||||
{
local float EnemyDistSq, DamagerDistSq;
|
||||
global.TakeDamage(Damage,instigatedBy,hitlocation,vect(0,0,0),damageType);
|
||||
// if someone close up is shooting us, just charge them
if( InstigatedBy != none )
{
DamagerDistSq = VSizeSquared(Location - InstigatedBy.Location);
|
||||
if( (ChargeDamage > 200 && DamagerDistSq < (500 * 500)) || DamagerDistSq < (100 * 100) )
{
SetAnimAction('transition');
//log("Frak this shizz, Charging!!!!");
GoToState('Charging');
return;
}
}
|
||||
if( Controller.Enemy != none && InstigatedBy != none && InstigatedBy != Controller.Enemy )
{
EnemyDistSq = VSizeSquared(Location - Controller.Enemy.Location);
DamagerDistSq = VSizeSquared(Location - InstigatedBy.Location);
}
|
||||
if( InstigatedBy != none && (DamagerDistSq < EnemyDistSq || Controller.Enemy == none) )
{
MonsterController(Controller).ChangeEnemy(InstigatedBy,Controller.CanSee(InstigatedBy));
Controller.Target = InstigatedBy;
Controller.Focus = InstigatedBy;
|
||||
if( DamagerDistSq < (500 * 500) )
{
SetAnimAction('transition');
GoToState('Charging');
}
}
|
||||
}
|
||||
function EndState()
|
||||
{
TraceHitPos = vect(0,0,0);
bMinigunning = False;
|
||||
AmbientSound = default.AmbientSound;
SoundVolume=default.SoundVolume;
SoundRadius=default.SoundRadius;
MGFireCounter=0;
|
||||
LastChainGunTime = Level.TimeSeconds + 5 + (FRand()*10);
|
||||
}
|
||||
function BeginState()
|
||||
{
bFireAtWill = False;
Acceleration = vect(0,0,0);
MGLostSightTimeout = 0.0;
bMinigunning = True;
|
||||
}
|
||||
function AnimEnd( int Channel )
|
||||
{
if( MGFireCounter <= 0 )
{
bShotAnim = true;
Acceleration = vect(0,0,0);
SetAnimAction('FireEndMG');
HandleWaitForAnim('FireEndMG');
GoToState('');
}
else
{
if ( Controller.Enemy != none )
{
if ( Controller.LineOfSightTo(Controller.Enemy) && FastTrace(GetBoneCoords('tip').Origin,Controller.Enemy.Location))
{
MGLostSightTimeout = 0.0;
Controller.Focus = Controller.Enemy;
Controller.FocalPoint = Controller.Enemy.Location;
}
else
{
MGLostSightTimeout = Level.TimeSeconds + (0.25 + FRand() * 0.35);
Controller.Focus = none;
}
|
||||
Controller.Target = Controller.Enemy;
}
else
{
MGLostSightTimeout = Level.TimeSeconds + (0.25 + FRand() * 0.35);
Controller.Focus = none;
}
|
||||
if( !bFireAtWill )
{
MGFireDuration = Level.TimeSeconds + (0.75 + FRand() * 0.5);
}
else if ( FRand() < 0.03 && Controller.Enemy != none && PlayerController(Controller.Enemy.Controller) != none )
{
// Randomly send out a message about Patriarch shooting chain gun(3% chance)
PlayerController(Controller.Enemy.Controller).Speech('AUTO', 9, "");
}
|
||||
bFireAtWill = True;
bShotAnim = true;
Acceleration = vect(0,0,0);
|
||||
SetAnimAction('FireMG');
bWaitForAnim = true;
}
|
||||
}
|
||||
function FireMGShot()
|
||||
{
local vector Start,End,HL,HN,Dir;
local rotator R;
local Actor A;
|
||||
MGFireCounter--;
|
||||
if( AmbientSound != MiniGunFireSound )
{
SoundVolume=255;
SoundRadius=400;
AmbientSound = MiniGunFireSound;
}
|
||||
Start = GetBoneCoords('tip').Origin;
if( Controller.Focus!=none )
R = rotator(Controller.Focus.Location-Start);
else R = rotator(Controller.FocalPoint-Start);
if( NeedToTurnFor(R) )
R = Rotation;
// KFTODO: Maybe scale this accuracy by his skill or the game difficulty
Dir = Normal(vector(R)+VRand()*0.06); //*0.04
End = Start+Dir*10000;
|
||||
// Have to turn of hit point collision so trace doesn't hit the Human Pawn's bullet whiz cylinder
bBlockHitPointTraces = false;
A = Trace(HL,HN,End,Start,True);
bBlockHitPointTraces = true;
|
||||
if( A==none )
Return;
TraceHitPos = HL;
if( Level.NetMode!=NM_DedicatedServer )
AddTraceHitFX(HL);
|
||||
if( A!=Level )
{
A.TakeDamage(MGDamage+Rand(3),Self,HL,Dir*500,Class'DamageType');
}
|
||||
}
|
||||
function bool NeedToTurnFor( rotator targ )
|
||||
{
local int YawErr;
|
||||
targ.Yaw = DesiredRotation.Yaw & 65535;
YawErr = (targ.Yaw - (Rotation.Yaw & 65535)) & 65535;
return !((YawErr < 2000) || (YawErr > 64535));
|
||||
}
|
||||
Begin:
|
||||
While( True )
|
||||
{
Acceleration = vect(0,0,0);
|
||||
if( MGLostSightTimeout > 0 && Level.TimeSeconds > MGLostSightTimeout )
{
bShotAnim = true;
Acceleration = vect(0,0,0);
SetAnimAction('FireEndMG');
HandleWaitForAnim('FireEndMG');
GoToState('');
}
|
||||
if( MGFireCounter <= 0 )
{
bShotAnim = true;
Acceleration = vect(0,0,0);
SetAnimAction('FireEndMG');
HandleWaitForAnim('FireEndMG');
GoToState('');
}
|
||||
// Give some randomness to the patriarch's firing
if( Level.TimeSeconds > MGFireDuration )
{
if( AmbientSound != MiniGunSpinSound )
{
SoundVolume=185;
SoundRadius=200;
AmbientSound = MiniGunSpinSound;
}
Sleep(0.5 + FRand() * 0.75);
MGFireDuration = Level.TimeSeconds + (0.75 + FRand() * 0.5);
}
else
{
if( bFireAtWill )
FireMGShot();
Sleep(0.05);
}
|
||||
}
|
||||
}
|
||||
state FireMissile
|
||||
{
|
||||
Ignores RangedAttack;
|
||||
function bool ShouldChargeFromDamage()
|
||||
{
return false;
|
||||
}
|
||||
function BeginState()
|
||||
{
Acceleration = vect(0,0,0);
|
||||
}
|
||||
function AnimEnd( int Channel )
|
||||
{
local vector Start;
local Rotator R;
|
||||
Start = GetBoneCoords('tip').Origin;
|
||||
if ( !SavedFireProperties.bInitialized )
{
SavedFireProperties.AmmoClass = MyAmmo.Class;
SavedFireProperties.ProjectileClass = MyAmmo.ProjectileClass;
SavedFireProperties.WarnTargetPct = 0.15;
SavedFireProperties.MaxRange = 10000;
SavedFireProperties.bTossed = False;
SavedFireProperties.bTrySplash = False;
SavedFireProperties.bLeadTarget = True;
SavedFireProperties.bInstantHit = True;
SavedFireProperties.bInitialized = true;
}
|
||||
R = AdjustAim(SavedFireProperties,Start,100);
PlaySound(RocketFireSound,SLOT_Interact,2.0,,TransientSoundRadius,,false);
Spawn(Class'NiceBossLAWProj',,,Start,R);
|
||||
bShotAnim = true;
Acceleration = vect(0,0,0);
SetAnimAction('FireEndMissile');
HandleWaitForAnim('FireEndMissile');
|
||||
// Randomly send out a message about Patriarch shooting a rocket(5% chance)
if ( FRand() < 0.05 && Controller.Enemy != none && PlayerController(Controller.Enemy.Controller) != none )
{
PlayerController(Controller.Enemy.Controller).Speech('AUTO', 10, "");
}
|
||||
GoToState('');
|
||||
}
|
||||
Begin:
|
||||
while ( true )
|
||||
{
Acceleration = vect(0,0,0);
Sleep(0.1);
|
||||
}
|
||||
}
|
||||
function bool MeleeDamageTarget(int hitdamage, vector pushdir)
|
||||
{
|
||||
if( Controller.Target!=none && Controller.Target.IsA('NetKActor') )
pushdir = Normal(Controller.Target.Location-Location)*100000; // Fly bitch!
|
||||
// Used to set MeleeRange = Default.MeleeRange; in Balance Round 1, fixed in Balance Round 2
|
||||
return Super.MeleeDamageTarget(hitdamage, pushdir);
|
||||
}
|
||||
state Charging
|
||||
{
|
||||
// Don't override speed in this state
|
||||
function bool CanSpeedAdjust()
|
||||
{
return false;
|
||||
}
|
||||
function bool ShouldChargeFromDamage()
|
||||
{
return false;
|
||||
}
|
||||
function BeginState()
|
||||
{
bChargingPlayer = True;
if( Level.NetMode!=NM_DedicatedServer )
PostNetReceive();
|
||||
// How many charge attacks we can do randomly 1-3
NumChargeAttacks = Rand(2) + 1;
|
||||
}
|
||||
function EndState()
|
||||
{
SetGroundSpeed(GetOriginalGroundSpeed());
bChargingPlayer = False;
ChargeDamage = 0;
if( Level.NetMode!=NM_DedicatedServer )
PostNetReceive();
|
||||
LastChargeTime = Level.TimeSeconds;
|
||||
}
|
||||
function Tick( float Delta )
|
||||
{
|
||||
if( NumChargeAttacks <= 0 )
{
GoToState('');
}
|
||||
// Keep the flesh pound moving toward its target when attacking
if( Role == ROLE_Authority && bShotAnim)
{
if( bChargingPlayer )
{
bChargingPlayer = false;
if( Level.NetMode!=NM_DedicatedServer )
PostNetReceive();
}
SetGroundSpeed(OriginalGroundSpeed * 1.25);
if( LookTarget!=none )
{
Acceleration = AccelRate * Normal(LookTarget.Location - Location);
}
}
else
{
if( !bChargingPlayer )
{
bChargingPlayer = true;
if( Level.NetMode!=NM_DedicatedServer )
PostNetReceive();
}
|
||||
// Zapping slows him down, but doesn't stop him
if( bZapped )
{
SetGroundSpeed(OriginalGroundSpeed * 1.5);
}
else
{
SetGroundSpeed(OriginalGroundSpeed * 2.5);
}
}
|
||||
Global.Tick(Delta);
|
||||
}
|
||||
function bool MeleeDamageTarget(int hitdamage, vector pushdir)
|
||||
{
local bool RetVal;
|
||||
NumChargeAttacks--;
|
||||
RetVal = Global.MeleeDamageTarget(hitdamage, pushdir*1.5);
if( RetVal )
GoToState('');
return RetVal;
|
||||
}
|
||||
function RangedAttack(Actor A)
|
||||
{
if( VSize(A.Location-Location)>700 && Level.TimeSeconds - LastForceChargeTime > 3.0 )
GoToState('');
Global.RangedAttack(A);
|
||||
}
|
||||
Begin:
|
||||
Sleep(6);
|
||||
GoToState('');
|
||||
}
|
||||
function BeginHealing()
|
||||
{
|
||||
MonsterController(Controller).WhatToDoNext(55);
|
||||
}
|
||||
|
||||
state Healing // Healing
|
||||
{
|
||||
function bool ShouldChargeFromDamage()
|
||||
{
return false;
|
||||
}
|
||||
Begin:
|
||||
Sleep(GetAnimDuration('Heal'));
|
||||
GoToState('');
|
||||
}
|
||||
state KnockDown // Knocked
|
||||
{
|
||||
function bool ShouldChargeFromDamage()
|
||||
{
return false;
|
||||
}
|
||||
Begin:
|
||||
if( Health > 0 )
|
||||
{
Sleep(GetAnimDuration('KnockDown'));
CloakBoss();
PlaySound(sound'KF_EnemiesFinalSnd.Patriarch.Kev_SaveMe', SLOT_Misc, 2.0,,500.0);
if( KFGameType(Level.Game).FinalSquadNum == SyringeCount )
{
KFGameType(Level.Game).AddBossBuddySquad();
}
GotoState('Escaping');
|
||||
}
|
||||
else
|
||||
{
GotoState('');
|
||||
}
|
||||
}
|
||||
State Escaping extends Charging // Got hurt and running away...
|
||||
{
|
||||
function BeginHealing()
|
||||
{
bShotAnim = true;
Acceleration = vect(0,0,0);
SetAnimAction('Heal');
HandleWaitForAnim('Heal');
|
||||
GoToState('Healing');
|
||||
}
|
||||
function RangedAttack(Actor A)
|
||||
{
if ( bShotAnim )
return;
else if ( IsCloseEnuf(A) )
{
if( bCloaked )
UnCloakBoss();
bShotAnim = true;
Acceleration = vect(0,0,0);
Acceleration = (A.Location-Location);
SetAnimAction('MeleeClaw');
//PlaySound(sound'Claw2s', SLOT_none); Claw2s
}
|
||||
}
|
||||
function bool MeleeDamageTarget(int hitdamage, vector pushdir)
|
||||
{
return Global.MeleeDamageTarget(hitdamage, pushdir*1.5);
|
||||
}
|
||||
function Tick( float Delta )
|
||||
{
|
||||
// Keep the flesh pound moving toward its target when attacking
if( Role == ROLE_Authority && bShotAnim)
{
if( bChargingPlayer )
{
bChargingPlayer = false;
if( Level.NetMode!=NM_DedicatedServer )
PostNetReceive();
}
SetGroundSpeed(GetOriginalGroundSpeed());
}
else
{
if( !bChargingPlayer )
{
bChargingPlayer = true;
if( Level.NetMode!=NM_DedicatedServer )
PostNetReceive();
}
|
||||
// Zapping slows him down, but doesn't stop him
if( bZapped )
{
SetGroundSpeed(OriginalGroundSpeed * 1.5);
}
else
{
SetGroundSpeed(OriginalGroundSpeed * 2.5);
}
}
|
||||
Global.Tick(Delta);
|
||||
}
|
||||
function EndState()
|
||||
{
SetGroundSpeed(GetOriginalGroundSpeed());
bChargingPlayer = False;
if( Level.NetMode!=NM_DedicatedServer )
PostNetReceive();
if( bCloaked )
UnCloakBoss();
|
||||
}
|
||||
Begin:
|
||||
While( true )
|
||||
{
Sleep(0.5);
if( !bCloaked && !bShotAnim )
CloakBoss();
if( !Controller.IsInState('SyrRetreat') && !Controller.IsInState('WaitForAnim'))
Controller.GoToState('SyrRetreat');
|
||||
}
|
||||
}
|
||||
State SneakAround extends Escaping // Attempt to sneak around.
|
||||
{
|
||||
function BeginHealing()
|
||||
{
MonsterController(Controller).WhatToDoNext(56);
GoToState('');
|
||||
}
|
||||
function bool MeleeDamageTarget(int hitdamage, vector pushdir)
|
||||
{
local bool RetVal;
|
||||
RetVal = super.MeleeDamageTarget(hitdamage, pushdir);
|
||||
GoToState('');
return RetVal;
|
||||
}
|
||||
function BeginState()
|
||||
{
super.BeginState();
SneakStartTime = Level.TimeSeconds;
|
||||
}
|
||||
function EndState()
|
||||
{
super.EndState();
LastSneakedTime = Level.TimeSeconds;
|
||||
}
|
||||
|
||||
Begin:
|
||||
CloakBoss();
|
||||
While( true )
|
||||
{
Sleep(0.5);
|
||||
if( Level.TimeSeconds - SneakStartTime > 10.0 )
{
GoToState('');
}
|
||||
if( !bCloaked && !bShotAnim )
CloakBoss();
if( !Controller.IsInState('ZombieHunt') && !Controller.IsInState('WaitForAnim') )
{
Controller.GoToState('ZombieHunt');
}
|
||||
}
|
||||
}
|
||||
State InitialSneak extends SneakAround // Sneak attack the players straight off the bat.
|
||||
{
|
||||
Begin:
|
||||
CloakBoss();
|
||||
While( true )
|
||||
{
Sleep(0.5);
SneakCount++;
|
||||
// Added sneakcount hack to try and fix the endless loop crash. Try and track down what was causing this later - Ramm
if( SneakCount > 1000 || (Controller != none && NiceZombieBossController(Controller).bAlreadyFoundEnemy) )
{
GoToState('');
}
|
||||
if( !bCloaked && !bShotAnim )
CloakBoss();
if( !Controller.IsInState('InitialHunting') && !Controller.IsInState('WaitForAnim') )
{
Controller.GoToState('InitialHunting');
}
|
||||
}
|
||||
}
|
||||
simulated function DropNeedle()
|
||||
{
|
||||
if( CurrentNeedle!=none )
|
||||
{
DetachFromBone(CurrentNeedle);
CurrentNeedle.SetLocation(GetBoneCoords('Rpalm_MedAttachment').Origin);
CurrentNeedle.DroppedNow();
CurrentNeedle = none;
|
||||
}
|
||||
}
|
||||
simulated function NotifySyringeA()
|
||||
{
|
||||
//log("Heal Part 1");
|
||||
if( Level.NetMode!=NM_Client )
|
||||
{
if( SyringeCount<3 )
SyringeCount++;
if( Level.NetMode!=NM_DedicatedServer )
PostNetReceive();
|
||||
}
|
||||
if( Level.NetMode!=NM_DedicatedServer )
|
||||
{
DropNeedle();
CurrentNeedle = Spawn(Class'NiceBossHPNeedle');
AttachToBone(CurrentNeedle,'Rpalm_MedAttachment');
|
||||
}
|
||||
}
|
||||
function NotifySyringeB()
|
||||
{
|
||||
//log("Heal Part 2");
|
||||
if( Level.NetMode != NM_Client )
|
||||
{
if(bOnFire)
Health += HealingAmount / 2;
else
Health += HealingAmount;
RemoveFlamingEffects();
StopBurnFX();
bOnFire = false;
bHealed = true;
FlameFuel = InitFlameFuel;
|
||||
}
|
||||
}
|
||||
simulated function NotifySyringeC()
|
||||
{
|
||||
//log("Heal Part 3");
|
||||
if( Level.NetMode!=NM_DedicatedServer && CurrentNeedle!=none )
|
||||
{
CurrentNeedle.Velocity = vect(-45,300,-90) >> Rotation;
DropNeedle();
|
||||
}
|
||||
}
|
||||
simulated function PostNetReceive()
|
||||
{
|
||||
if( bClientMiniGunning != bMinigunning )
|
||||
{
bClientMiniGunning = bMinigunning;
// Hack so Patriarch won't go out of MG Firing to play his idle anim online
if( bMinigunning )
{
IdleHeavyAnim='FireMG';
IdleRifleAnim='FireMG';
IdleCrouchAnim='FireMG';
IdleWeaponAnim='FireMG';
IdleRestAnim='FireMG';
}
else
{
IdleHeavyAnim='BossIdle';
IdleRifleAnim='BossIdle';
IdleCrouchAnim='BossIdle';
IdleWeaponAnim='BossIdle';
IdleRestAnim='BossIdle';
}
|
||||
}
|
||||
if( bClientCharg!=bChargingPlayer )
|
||||
{
bClientCharg = bChargingPlayer;
if (bChargingPlayer)
{
MovementAnims[0] = ChargingAnim;
MovementAnims[1] = ChargingAnim;
MovementAnims[2] = ChargingAnim;
MovementAnims[3] = ChargingAnim;
}
else if( !bChargingPlayer )
{
MovementAnims[0] = default.MovementAnims[0];
MovementAnims[1] = default.MovementAnims[1];
MovementAnims[2] = default.MovementAnims[2];
MovementAnims[3] = default.MovementAnims[3];
}
|
||||
}
|
||||
else if( ClientSyrCount!=SyringeCount )
|
||||
{
ClientSyrCount = SyringeCount;
Switch( SyringeCount )
{
Case 1:
SetBoneScale(3,0,'Syrange1');
Break;
Case 2:
SetBoneScale(3,0,'Syrange1');
SetBoneScale(4,0,'Syrange2');
Break;
Case 3:
SetBoneScale(3,0,'Syrange1');
SetBoneScale(4,0,'Syrange2');
SetBoneScale(5,0,'Syrange3');
Break;
Default: // WTF? reset...?
SetBoneScale(3,1,'Syrange1');
SetBoneScale(4,1,'Syrange2');
SetBoneScale(5,1,'Syrange3');
Break;
}
|
||||
}
|
||||
else if( TraceHitPos!=vect(0,0,0) )
|
||||
{
AddTraceHitFX(TraceHitPos);
TraceHitPos = vect(0,0,0);
|
||||
}
|
||||
else if( bClientCloaked!=bCloaked )
|
||||
{
bClientCloaked = bCloaked;
bCloaked = !bCloaked;
if( bCloaked )
UnCloakBoss();
else CloakBoss();
bCloaked = bClientCloaked;
|
||||
}
|
||||
}
|
||||
simulated function int DoAnimAction( name AnimName )
|
||||
{
|
||||
if( AnimName=='MeleeImpale' || AnimName=='MeleeClaw' || AnimName=='transition' /*|| AnimName=='FireMG'*/ )
|
||||
{
AnimBlendParams(1, 1.0, 0.0,, SpineBone1);
PlayAnim(AnimName,, 0.1, 1);
Return 1;
|
||||
}
|
||||
else if( AnimName=='RadialAttack' )
|
||||
{
// Get rid of blending, this is a full body anim
AnimBlendParams(1, 0.0);
PlayAnim(AnimName,,0.1);
return 0;
|
||||
}
|
||||
Return Super.DoAnimAction(AnimName);
|
||||
}
|
||||
|
||||
simulated event SetAnimAction(name NewAction)
|
||||
{
|
||||
local int meleeAnimIndex;
|
||||
if( NewAction=='' )
Return;
|
||||
if(NewAction == 'Claw')
|
||||
{
meleeAnimIndex = Rand(3);
NewAction = meleeAnims[meleeAnimIndex];
|
||||
}
|
||||
ExpectingChannel = DoAnimAction(NewAction);
|
||||
if( Controller != none )
|
||||
{
NiceZombieBossController(Controller).AnimWaitChannel = ExpectingChannel;
|
||||
}
|
||||
if( AnimNeedsWait(NewAction) )
|
||||
{
bWaitForAnim = true;
|
||||
}
|
||||
else
|
||||
{
bWaitForAnim = false;
|
||||
}
|
||||
if( Level.NetMode!=NM_Client )
|
||||
{
AnimAction = NewAction;
bResetAnimAct = True;
|
||||
ResetAnimActTime = Level.TimeSeconds+0.3;
|
||||
}
|
||||
}
|
||||
// Hand sending the controller to the WaitForAnim state
|
||||
simulated function HandleWaitForAnim( name NewAnim )
|
||||
{
|
||||
local float RageAnimDur;
|
||||
Controller.GoToState('WaitForAnim');
|
||||
RageAnimDur = GetAnimDuration(NewAnim);
|
||||
NiceZombieBossController(Controller).SetWaitForAnimTimout(RageAnimDur,NewAnim);
|
||||
}
|
||||
// The animation is full body and should set the bWaitForAnim flag
|
||||
simulated function bool AnimNeedsWait(name TestAnim)
|
||||
{
|
||||
if( /*TestAnim == 'MeleeImpale' || TestAnim =='MeleeClaw' || TestAnim =='transition' ||*/ TestAnim == 'FireMG' ||
TestAnim == 'PreFireMG' || TestAnim == 'PreFireMissile' || TestAnim == 'FireEndMG'|| TestAnim == 'FireEndMissile' ||
TestAnim == 'Heal' || TestAnim == 'KnockDown' || TestAnim == 'Entrance' || TestAnim == 'VictoryLaugh' || TestAnim == 'RadialAttack' )
|
||||
{
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
simulated function HandleBumpGlass()
|
||||
{
|
||||
}
|
||||
|
||||
function bool FlipOver()
|
||||
{
|
||||
Return False;
|
||||
}
|
||||
// Return true if we want to charge from taking too much damage
|
||||
function bool ShouldChargeFromDamage()
|
||||
{
|
||||
// If we don;t want to heal, charge whoever damaged us!!!
|
||||
if( (SyringeCount==0 && Health<HealingLevels[0]) || (SyringeCount==1 && Health<HealingLevels[1]) || (SyringeCount==2 && Health<HealingLevels[2]) )
|
||||
{
return false;
|
||||
}
|
||||
else if( !bChargingPlayer && Level.TimeSeconds - LastForceChargeTime > (5.0 + 5.0 * FRand()) )
|
||||
{
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
function TakeDamageClient(int Damage, Pawn InstigatedBy, Vector Hitlocation, Vector Momentum, class<NiceWeaponDamageType> damageType, float headshotLevel, float lockonTime){
|
||||
local float DamagerDistSq;
|
||||
//local float UsedPipeBombDamScale;
|
||||
local KFHumanPawn P;
|
||||
local int NumPlayersSurrounding;
|
||||
local bool bDidRadialAttack;
|
||||
if( Level.TimeSeconds - LastMeleeExploitCheckTime > 1.0 && (class<NiceDamageTypeVetBerserker>(damageType) != none) )
|
||||
{
LastMeleeExploitCheckTime = Level.TimeSeconds;
NumLumberJacks = 0;
NumNinjas = 0;
|
||||
foreach DynamicActors(class'KFHumanPawn', P)
{
// look for guys attacking us within 3 meters
if ( VSize(P.Location - Location) < 150 )
{
NumPlayersSurrounding++;
|
||||
if( P != none && P.Weapon != none )
{
if( Axe(P.Weapon) != none || Chainsaw(P.Weapon) != none )
{
NumLumberJacks++;
}
else if( Katana(P.Weapon) != none )
{
NumNinjas++;
}
}
|
||||
if( !bDidRadialAttack && NumPlayersSurrounding >= 3 )
{
bDidRadialAttack = true;
GotoState('RadialAttack');
break;
}
}
}
|
||||
}
|
||||
// Scale damage from the pipebomb down a bit if lots of pipe bomb damage happens
|
||||
// at around the same times. Prevent players from putting all thier pipe bombs
|
||||
// in one place and owning the patriarch in one blow.
|
||||
/*if ( class<NiceDamTypePipeBomb>(damageType) != none )
|
||||
{
UsedPipeBombDamScale = FMax(0,(1.0 - PipeBombDamageScale));
|
||||
PipeBombDamageScale += 0.075;
|
||||
if( PipeBombDamageScale > 1.0 )
{
PipeBombDamageScale = 1.0;
}
|
||||
Damage *= UsedPipeBombDamScale;
|
||||
}*/
|
||||
Super.TakeDamageClient(Damage,instigatedBy,hitlocation,Momentum,damageType,headshotLevel,lockonTime);
|
||||
if( Level.TimeSeconds - LastDamageTime > 10 )
|
||||
{
ChargeDamage = 0;
|
||||
}
|
||||
else
|
||||
{
LastDamageTime = Level.TimeSeconds;
ChargeDamage += Damage;
|
||||
}
|
||||
if( ShouldChargeFromDamage() && ChargeDamage > 200 )
|
||||
{
// If someone close up is shooting us, just charge them
if( InstigatedBy != none )
{
DamagerDistSq = VSizeSquared(Location - InstigatedBy.Location);
|
||||
if( DamagerDistSq < (700 * 700) )
{
SetAnimAction('transition');
ChargeDamage=0;
LastForceChargeTime = Level.TimeSeconds;
GoToState('Charging');
return;
}
}
|
||||
}
|
||||
if( Health<=0 || SyringeCount==3 || IsInState('Escaping') || IsInState('KnockDown') || IsInState('RadialAttack') || bDidRadialAttack/*|| bShotAnim*/ )
Return;
|
||||
if( (SyringeCount==0 && Health<HealingLevels[0]) || (SyringeCount==1 && Health<HealingLevels[1]) || (SyringeCount==2 && Health<HealingLevels[2]) )
|
||||
{
//log(GetStateName()$" Took damage and want to heal!!! Health="$Health$" HealingLevels "$HealingLevels[SyringeCount]);
|
||||
bShotAnim = true;
Acceleration = vect(0,0,0);
SetAnimAction('KnockDown');
HandleWaitForAnim('KnockDown');
KFMonsterController(Controller).bUseFreezeHack = True;
GoToState('KnockDown');
|
||||
}
|
||||
}
|
||||
function DoorAttack(Actor A)
|
||||
{
|
||||
if ( bShotAnim )
return;
|
||||
else if ( A!=none )
|
||||
{
Controller.Target = A;
bShotAnim = true;
Acceleration = vect(0,0,0);
SetAnimAction('PreFireMissile');
HandleWaitForAnim('PreFireMissile');
GoToState('FireMissile');
|
||||
}
|
||||
}
|
||||
function RemoveHead();
|
||||
function PlayDirectionalHit(Vector HitLoc);
|
||||
function bool SameSpeciesAs(Pawn P)
|
||||
{
|
||||
return False;
|
||||
}
|
||||
// Creapy endgame camera when the evil wins.
|
||||
function bool SetBossLaught()
|
||||
{
|
||||
local Controller C;
|
||||
GoToState('');
|
||||
bShotAnim = true;
|
||||
Acceleration = vect(0,0,0);
|
||||
SetAnimAction('VictoryLaugh');
|
||||
HandleWaitForAnim('VictoryLaugh');
|
||||
bIsBossView = True;
|
||||
bSpecialCalcView = True;
|
||||
For( C=Level.ControllerList; C!=none; C=C.NextController )
|
||||
{
if( PlayerController(C)!=none )
{
PlayerController(C).SetViewTarget(Self);
PlayerController(C).ClientSetViewTarget(Self);
PlayerController(C).ClientSetBehindView(True);
}
|
||||
}
|
||||
Return True;
|
||||
}
|
||||
simulated function bool SpectatorSpecialCalcView(PlayerController Viewer, out Actor ViewActor, out vector CameraLocation, out rotator CameraRotation)
|
||||
{
|
||||
Viewer.bBehindView = True;
|
||||
ViewActor = Self;
|
||||
CameraRotation.Yaw = Rotation.Yaw-32768;
|
||||
CameraRotation.Pitch = 0;
|
||||
CameraRotation.Roll = Rotation.Roll;
|
||||
CameraLocation = Location + (vect(80,0,80) >> Rotation);
|
||||
Return True;
|
||||
}
|
||||
// Overridden to do a cool slomo death view of the patriarch dying
|
||||
function Died(Controller Killer, class<DamageType> damageType, vector HitLocation)
|
||||
{
|
||||
local Controller C;
|
||||
super.Died(Killer,damageType,HitLocation);
|
||||
KFGameType(Level.Game).DoBossDeath();
|
||||
For( C=Level.ControllerList; C!=none; C=C.NextController )
|
||||
{
if( PlayerController(C)!=none )
{
PlayerController(C).SetViewTarget(Self);
PlayerController(C).ClientSetViewTarget(Self);
PlayerController(C).bBehindView = true;
PlayerController(C).ClientSetBehindView(True);
}
|
||||
}
|
||||
}
|
||||
function ClawDamageTarget()
|
||||
{
|
||||
local vector PushDir;
|
||||
local name Anim;
|
||||
local float frame,rate;
|
||||
local float UsedMeleeDamage;
|
||||
local bool bDamagedSomeone;
|
||||
local KFHumanPawn P;
|
||||
local Actor OldTarget;
|
||||
if( MeleeDamage > 1 )
|
||||
{
UsedMeleeDamage = (MeleeDamage - (MeleeDamage * 0.05)) + (MeleeDamage * (FRand() * 0.1));
|
||||
}
|
||||
else
|
||||
{
UsedMeleeDamage = MeleeDamage;
|
||||
}
|
||||
GetAnimParams(1, Anim,frame,rate);
|
||||
if( Anim == 'MeleeImpale' )
|
||||
{
MeleeRange = ImpaleMeleeDamageRange;
|
||||
}
|
||||
else
|
||||
{
MeleeRange = ClawMeleeDamageRange;
|
||||
}
|
||||
if(Controller!=none && Controller.Target!=none)
PushDir = (damageForce * Normal(Controller.Target.Location - Location));
|
||||
else
PushDir = damageForce * vector(Rotation);
|
||||
// Begin Balance Round 1(damages everyone in Round 2 and added seperate code path for MeleeImpale in Round 3)
|
||||
if( Anim == 'MeleeImpale' )
|
||||
{
bDamagedSomeone = MeleeDamageTarget(UsedMeleeDamage, PushDir);
|
||||
}
|
||||
else
|
||||
{
OldTarget = Controller.Target;
|
||||
foreach DynamicActors(class'KFHumanPawn', P)
{
if ( (P.Location - Location) dot PushDir > 0.0 ) // Added dot Product check in Balance Round 3
{
Controller.Target = P;
bDamagedSomeone = bDamagedSomeone || MeleeDamageTarget(UsedMeleeDamage, damageForce * Normal(P.Location - Location)); // Always pushing players away added in Balance Round 3
}
}
|
||||
Controller.Target = OldTarget;
|
||||
}
|
||||
MeleeRange = Default.MeleeRange;
|
||||
// End Balance Round 1, 2, and 3
|
||||
if ( bDamagedSomeone )
|
||||
{
if( Anim == 'MeleeImpale' )
{
PlaySound(MeleeImpaleHitSound, SLOT_Interact, 2.0);
}
else
{
PlaySound(MeleeAttackHitSound, SLOT_Interact, 2.0);
}
|
||||
}
|
||||
}
|
||||
simulated function ProcessHitFX()
|
||||
{
|
||||
local Coords boneCoords;
|
||||
local class<xEmitter> HitEffects[4];
|
||||
local int i,j;
|
||||
local float GibPerterbation;
|
||||
if( (Level.NetMode == NM_DedicatedServer) || bSkeletized || (Mesh == SkeletonMesh))
|
||||
{
SimHitFxTicker = HitFxTicker;
return;
|
||||
}
|
||||
for ( SimHitFxTicker = SimHitFxTicker; SimHitFxTicker != HitFxTicker; SimHitFxTicker = (SimHitFxTicker + 1) % ArrayCount(HitFX) )
|
||||
{
j++;
if ( j > 30 )
{
SimHitFxTicker = HitFxTicker;
return;
}
|
||||
if( (HitFX[SimHitFxTicker].damtype == none) || (Level.bDropDetail && (Level.TimeSeconds - LastRenderTime > 3) && !IsHumanControlled()) )
continue;
|
||||
//log("Processing effects for damtype "$HitFX[SimHitFxTicker].damtype);
|
||||
if( HitFX[SimHitFxTicker].bone == 'obliterate' && !class'GameInfo'.static.UseLowGore())
{
SpawnGibs( HitFX[SimHitFxTicker].rotDir, 1);
bGibbed = true;
// Wait a tick on a listen server so the obliteration can replicate before the pawn is destroyed
if( Level.NetMode == NM_ListenServer )
{
bDestroyNextTick = true;
TimeSetDestroyNextTickTime = Level.TimeSeconds;
}
else
{
Destroy();
}
return;
}
|
||||
boneCoords = GetBoneCoords( HitFX[SimHitFxTicker].bone );
|
||||
if ( !Level.bDropDetail && !class'GameInfo'.static.NoBlood() && !bSkeletized && !class'GameInfo'.static.UseLowGore() )
{
//AttachEmitterEffect( BleedingEmitterClass, HitFX[SimHitFxTicker].bone, boneCoords.Origin, HitFX[SimHitFxTicker].rotDir );
|
||||
HitFX[SimHitFxTicker].damtype.static.GetHitEffects( HitEffects, Health );
|
||||
if( !PhysicsVolume.bWaterVolume ) // don't attach effects under water
{
for( i = 0; i < ArrayCount(HitEffects); i++ )
{
if( HitEffects[i] == none )
continue;
|
||||
AttachEffect( HitEffects[i], HitFX[SimHitFxTicker].bone, boneCoords.Origin, HitFX[SimHitFxTicker].rotDir );
}
}
}
if ( class'GameInfo'.static.UseLowGore() )
HitFX[SimHitFxTicker].bSever = false;
|
||||
if( HitFX[SimHitFxTicker].bSever )
{
GibPerterbation = HitFX[SimHitFxTicker].damtype.default.GibPerterbation;
|
||||
switch( HitFX[SimHitFxTicker].bone )
{
case 'obliterate':
break;
|
||||
case LeftThighBone:
if( !bLeftLegGibbed )
{
SpawnSeveredGiblet( DetachedLegClass, boneCoords.Origin, HitFX[SimHitFxTicker].rotDir, GibPerterbation, GetBoneRotation(HitFX[SimHitFxTicker].bone) );
KFSpawnGiblet( class 'KFMod.KFGibBrain',boneCoords.Origin, HitFX[SimHitFxTicker].rotDir, GibPerterbation, 250 ) ;
KFSpawnGiblet( class 'KFMod.KFGibBrainb',boneCoords.Origin, HitFX[SimHitFxTicker].rotDir, GibPerterbation, 250 ) ;
KFSpawnGiblet( class 'KFMod.KFGibBrain',boneCoords.Origin, HitFX[SimHitFxTicker].rotDir, GibPerterbation, 250 ) ;
bLeftLegGibbed=true;
}
break;
|
||||
case RightThighBone:
if( !bRightLegGibbed )
{
SpawnSeveredGiblet( DetachedLegClass, boneCoords.Origin, HitFX[SimHitFxTicker].rotDir, GibPerterbation, GetBoneRotation(HitFX[SimHitFxTicker].bone) );
KFSpawnGiblet( class 'KFMod.KFGibBrain',boneCoords.Origin, HitFX[SimHitFxTicker].rotDir, GibPerterbation, 250 ) ;
KFSpawnGiblet( class 'KFMod.KFGibBrainb',boneCoords.Origin, HitFX[SimHitFxTicker].rotDir, GibPerterbation, 250 ) ;
KFSpawnGiblet( class 'KFMod.KFGibBrain',boneCoords.Origin, HitFX[SimHitFxTicker].rotDir, GibPerterbation, 250 ) ;
bRightLegGibbed=true;
}
break;
|
||||
case LeftFArmBone:
if( !bLeftArmGibbed )
{
SpawnSeveredGiblet( DetachedSpecialArmClass, boneCoords.Origin, HitFX[SimHitFxTicker].rotDir, GibPerterbation, GetBoneRotation(HitFX[SimHitFxTicker].bone) );
KFSpawnGiblet( class 'KFMod.KFGibBrain',boneCoords.Origin, HitFX[SimHitFxTicker].rotDir, GibPerterbation, 250 ) ;
KFSpawnGiblet( class 'KFMod.KFGibBrainb',boneCoords.Origin, HitFX[SimHitFxTicker].rotDir, GibPerterbation, 250 ) ;;
bLeftArmGibbed=true;
}
break;
|
||||
case RightFArmBone:
if( !bRightArmGibbed )
{
SpawnSeveredGiblet( DetachedArmClass, boneCoords.Origin, HitFX[SimHitFxTicker].rotDir, GibPerterbation, GetBoneRotation(HitFX[SimHitFxTicker].bone) );
KFSpawnGiblet( class 'KFMod.KFGibBrain',boneCoords.Origin, HitFX[SimHitFxTicker].rotDir, GibPerterbation, 250 ) ;
KFSpawnGiblet( class 'KFMod.KFGibBrainb',boneCoords.Origin, HitFX[SimHitFxTicker].rotDir, GibPerterbation, 250 ) ;
bRightArmGibbed=true;
}
break;
|
||||
case 'head':
if( !bHeadGibbed )
{
if ( HitFX[SimHitFxTicker].damtype == class'DamTypeDecapitation' )
{
DecapFX( boneCoords.Origin, HitFX[SimHitFxTicker].rotDir, false);
}
else if( HitFX[SimHitFxTicker].damtype == class'DamTypeProjectileDecap' )
{
DecapFX( boneCoords.Origin, HitFX[SimHitFxTicker].rotDir, false, true);
}
else if( HitFX[SimHitFxTicker].damtype == class'DamTypeMeleeDecapitation' )
{
DecapFX( boneCoords.Origin, HitFX[SimHitFxTicker].rotDir, true);
}
|
||||
bHeadGibbed=true;
}
break;
}
|
||||
if( HitFX[SimHitFXTicker].bone != 'Spine' && HitFX[SimHitFXTicker].bone != FireRootBone &&
HitFX[SimHitFXTicker].bone != 'head' && Health <=0 )
HideBone(HitFX[SimHitFxTicker].bone);
}
|
||||
}
|
||||
}
|
||||
static simulated function PreCacheMaterials(LevelInfo myLevel)
|
||||
{//should be derived and used.
|
||||
/*
|
||||
myLevel.AddPrecacheMaterial(Combiner'KF_Specimens_Trip_T.gatling_cmb');
|
||||
myLevel.AddPrecacheMaterial(Combiner'KF_Specimens_Trip_T.gatling_env_cmb');
|
||||
myLevel.AddPrecacheMaterial(Texture'KF_Specimens_Trip_T.gatling_D');
|
||||
myLevel.AddPrecacheMaterial(Combiner'KF_Specimens_Trip_T.PatGungoInvisible_cmb');
|
||||
myLevel.AddPrecacheMaterial(Combiner'KF_Specimens_Trip_T.patriarch_cmb');
|
||||
myLevel.AddPrecacheMaterial(Combiner'KF_Specimens_Trip_T.patriarch_env_cmb');
|
||||
myLevel.AddPrecacheMaterial(Texture'KF_Specimens_Trip_T.patriarch_D');
|
||||
myLevel.AddPrecacheMaterial(Material'KF_Specimens_Trip_T.patriarch_invisible');
|
||||
myLevel.AddPrecacheMaterial(Material'KF_Specimens_Trip_T.patriarch_invisible_gun');
|
||||
myLevel.AddPrecacheMaterial(Material'KF_Specimens_Trip_T.patriarch_fizzle_FB');
|
||||
myLevel.AddPrecacheMaterial(Texture'kf_fx_trip_t.Gore.Patriarch_Gore_Limbs_Diff');
|
||||
myLevel.AddPrecacheMaterial(Texture'kf_fx_trip_t.Gore.Patriarch_Gore_Limbs_Spec');
|
||||
*/
|
||||
}
|
||||
defaultproperties
|
||||
{
RocketFireSound=SoundGroup'KF_EnemiesFinalSnd.Patriarch.Kev_FireRocket'
MiniGunFireSound=Sound'KF_BasePatriarch.Attack.Kev_MG_GunfireLoop'
MiniGunSpinSound=Sound'KF_BasePatriarch.Attack.Kev_MG_TurbineFireLoop'
MeleeImpaleHitSound=SoundGroup'KF_EnemiesFinalSnd.Patriarch.Kev_HitPlayer_Impale'
MoanVoice=SoundGroup'KF_EnemiesFinalSnd.Patriarch.Kev_Talk'
MeleeAttackHitSound=SoundGroup'KF_EnemiesFinalSnd.Patriarch.Kev_HitPlayer_Fist'
JumpSound=SoundGroup'KF_EnemiesFinalSnd.Patriarch.Kev_Jump'
DetachedArmClass=Class'KFChar.SeveredArmPatriarch'
DetachedLegClass=Class'KFChar.SeveredLegPatriarch'
DetachedHeadClass=Class'KFChar.SeveredHeadPatriarch'
DetachedSpecialArmClass=Class'KFChar.SeveredRocketArmPatriarch'
HitSound(0)=SoundGroup'KF_EnemiesFinalSnd.Patriarch.Kev_Pain'
DeathSound(0)=SoundGroup'KF_EnemiesFinalSnd.Patriarch.Kev_Death'
ControllerClass=Class'NicePack.NiceZombieBossController'
AmbientSound=Sound'KF_BasePatriarch.Idle.Kev_IdleLoop'
Mesh=SkeletalMesh'KF_Freaks_Trip.Patriarch_Freak'
Skins(0)=Combiner'KF_Specimens_Trip_T.gatling_cmb'
Skins(1)=Combiner'KF_Specimens_Trip_T.patriarch_cmb'
|
||||
}
|
||||
45
sources/Zeds/Nice/NiceZombieBossBase.uc
Normal file
45
sources/Zeds/Nice/NiceZombieBossBase.uc
Normal file
|
|
@ -0,0 +1,45 @@
|
|||
class NiceZombieBossBase extends NiceMonster
|
||||
abstract;
|
||||
#exec OBJ LOAD FILE=KFPatch2.utx
|
||||
#exec OBJ LOAD FILE=KF_Specimens_Trip_T.utx
|
||||
var bool bChargingPlayer,bClientCharg,bFireAtWill,bMinigunning,bIsBossView;
|
||||
var float RageStartTime,LastChainGunTime,LastMissileTime,LastSneakedTime;
|
||||
var bool bClientMiniGunning;
|
||||
var name ChargingAnim; // How he runs when charging the player.
|
||||
var byte SyringeCount,ClientSyrCount;
|
||||
var int MGFireCounter;
|
||||
var vector TraceHitPos;
|
||||
var Emitter mTracer,mMuzzleFlash;
|
||||
var bool bClientCloaked;
|
||||
var float LastCheckTimes;
|
||||
var int HealingLevels[3],HealingAmount;
|
||||
var(Sounds) sound RocketFireSound; // The sound of the rocket being fired
|
||||
var(Sounds) sound MiniGunFireSound; // The sound of the minigun being fired
|
||||
var(Sounds) sound MiniGunSpinSound; // The sound of the minigun spinning
|
||||
var(Sounds) sound MeleeImpaleHitSound;// The sound of melee impale attack hitting the player
|
||||
var float MGFireDuration; // How long to fire for this burst
|
||||
var float MGLostSightTimeout; // When to stop firing because we lost sight of the target
|
||||
var() float MGDamage; // How much damage the MG will do
|
||||
var() float ClawMeleeDamageRange;// How long his arms melee strike is
|
||||
var() float ImpaleMeleeDamageRange;// How long his spike melee strike is
|
||||
var float LastChargeTime; // Last time the patriarch charged
|
||||
var float LastForceChargeTime;// Last time patriarch was forced to charge
|
||||
var int NumChargeAttacks; // Number of attacks this charge
|
||||
var float ChargeDamage; // How much damage he's taken since the last charge
|
||||
var float LastDamageTime; // Last Time we took damage
|
||||
// Sneaking
|
||||
var float SneakStartTime; // When did we start sneaking
|
||||
var int SneakCount; // Keep track of the loop that sends the boss to initial hunting state
|
||||
// PipeBomb damage
|
||||
var() float PipeBombDamageScale;// Scale the pipe bomb damage over time
|
||||
replication
|
||||
{
|
||||
reliable if( Role==ROLE_Authority )
bChargingPlayer,SyringeCount,TraceHitPos,bMinigunning,bIsBossView;
|
||||
}
|
||||
//-------------------------------------------------------------------------------
|
||||
// NOTE: All Code resides in the child class(this class was only created to
|
||||
// eliminate hitching caused by loading default properties during play)
|
||||
//-------------------------------------------------------------------------------
|
||||
defaultproperties
|
||||
{
ChargingAnim="RunF"
HealingLevels(0)=5600
HealingLevels(1)=3500
HealingLevels(2)=2187
HealingAmount=1750
MGDamage=6.000000
ClawMeleeDamageRange=85.000000
ImpaleMeleeDamageRange=45.000000
fuelRatio=0.400000
bFrugalFuelUsage=False
clientHeadshotScale=1.200000
ZapThreshold=5.000000
ZappedDamageMod=1.250000
ZapResistanceScale=1.000000
bHarpoonToHeadStuns=False
bHarpoonToBodyStuns=False
ZombieFlag=3
MeleeDamage=75
damageForce=170000
bFatAss=True
KFRagdollName="Patriarch_Trip"
bMeleeStunImmune=True
CrispUpThreshhold=1
bCanDistanceAttackDoors=True
bUseExtendedCollision=True
ColOffset=(Z=65.000000)
ColRadius=27.000000
ColHeight=25.000000
SeveredArmAttachScale=1.100000
SeveredLegAttachScale=1.200000
SeveredHeadAttachScale=1.500000
PlayerCountHealthScale=0.750000
BurningWalkFAnims(0)="WalkF"
BurningWalkFAnims(1)="WalkF"
BurningWalkFAnims(2)="WalkF"
BurningWalkAnims(0)="WalkF"
BurningWalkAnims(1)="WalkF"
BurningWalkAnims(2)="WalkF"
OnlineHeadshotOffset=(X=28.000000,Z=75.000000)
OnlineHeadshotScale=1.200000
HeadHealth=100000.000000
MotionDetectorThreat=10.000000
bOnlyDamagedByCrossbow=True
bBoss=True
ScoringValue=500
IdleHeavyAnim="BossIdle"
IdleRifleAnim="BossIdle"
RagDeathVel=80.000000
RagDeathUpKick=100.000000
MeleeRange=10.000000
GroundSpeed=120.000000
WaterSpeed=120.000000
HealthMax=8000.000000
Health=8000
HeadScale=1.300000
MenuName="Nice Patriarch"
MovementAnims(0)="WalkF"
MovementAnims(1)="WalkF"
MovementAnims(2)="WalkF"
MovementAnims(3)="WalkF"
AirAnims(0)="JumpInAir"
AirAnims(1)="JumpInAir"
AirAnims(2)="JumpInAir"
AirAnims(3)="JumpInAir"
TakeoffAnims(0)="JumpTakeOff"
TakeoffAnims(1)="JumpTakeOff"
TakeoffAnims(2)="JumpTakeOff"
TakeoffAnims(3)="JumpTakeOff"
LandAnims(0)="JumpLanded"
LandAnims(1)="JumpLanded"
LandAnims(2)="JumpLanded"
LandAnims(3)="JumpLanded"
AirStillAnim="JumpInAir"
TakeoffStillAnim="JumpTakeOff"
IdleCrouchAnim="BossIdle"
IdleWeaponAnim="BossIdle"
IdleRestAnim="BossIdle"
DrawScale=1.050000
PrePivot=(Z=3.000000)
SoundVolume=75
bNetNotify=False
Mass=1000.000000
RotationRate=(Yaw=36000,Roll=0)
|
||||
}
|
||||
190
sources/Zeds/Nice/NiceZombieBossController.uc
Normal file
190
sources/Zeds/Nice/NiceZombieBossController.uc
Normal file
|
|
@ -0,0 +1,190 @@
|
|||
//-----------------------------------------------------------
|
||||
//
|
||||
//-----------------------------------------------------------
|
||||
class NiceZombieBossController extends KFMonsterController;
|
||||
var NavigationPoint HidingSpots;
|
||||
var float WaitAnimTimeout; // How long until the Anim we are waiting for is completed; Hack so the server doesn't get stuck in idle when its doing the Rage anim
|
||||
var int AnimWaitChannel; // The channel we are waiting to end in WaitForAnim
|
||||
var name AnimWaitingFor; // The animation we are waiting to end in WaitForAnim, mostly used for debugging
|
||||
var bool bAlreadyFoundEnemy; // The Boss has already found an enemy at least once
|
||||
function bool CanKillMeYet()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
function TimedFireWeaponAtEnemy()
|
||||
{
|
||||
if ( (Enemy == none) || FireWeaponAt(Enemy) )
SetCombatTimer();
|
||||
else
SetTimer(0.01, True);
|
||||
}
|
||||
// Overridden to support a quick initial attack to get the boss to the players quickly
|
||||
function FightEnemy(bool bCanCharge)
|
||||
{
|
||||
if( KFM.bShotAnim )
|
||||
{
GoToState('WaitForAnim');
Return;
|
||||
}
|
||||
if (KFM.MeleeRange != KFM.default.MeleeRange)
KFM.MeleeRange = KFM.default.MeleeRange;
|
||||
if ( Enemy == none || Enemy.Health <= 0 )
FindNewEnemy();
|
||||
if ( (Enemy == FailedHuntEnemy) && (Level.TimeSeconds == FailedHuntTime) )
|
||||
{
|
||||
// if ( Enemy.Controller.bIsPlayer )
// FindNewEnemy();
|
||||
if ( Enemy == FailedHuntEnemy )
{
GoalString = "FAILED HUNT - HANG OUT";
if ( EnemyVisible() )
bCanCharge = false;
}
|
||||
}
|
||||
if ( !EnemyVisible() )
|
||||
{
// Added sneakcount hack to try and fix the endless loop crash. Try and track down what was causing this later - Ramm
if( bAlreadyFoundEnemy || NiceZombieBoss(Pawn).SneakCount > 2 )
{
bAlreadyFoundEnemy = true;
GoalString = "Hunt";
GotoState('ZombieHunt');
}
else
{
// Added sneakcount hack to try and fix the endless loop crash. Try and track down what was causing this later - Ramm
NiceZombieBoss(Pawn).SneakCount++;
GoalString = "InitialHunt";
GotoState('InitialHunting');
}
return;
|
||||
}
|
||||
// see enemy - decide whether to charge it or strafe around/stand and fire
|
||||
Target = Enemy;
|
||||
GoalString = "Charge";
|
||||
PathFindState = 2;
|
||||
DoCharge();
|
||||
}
|
||||
|
||||
// Get the boss to the players quickly after initial spawn
|
||||
state InitialHunting extends Hunting
|
||||
{
|
||||
event SeePlayer(Pawn SeenPlayer)
|
||||
{
super.SeePlayer(SeenPlayer);
bAlreadyFoundEnemy = true;
GoalString = "Hunt";
GotoState('ZombieHunt');
|
||||
}
|
||||
function BeginState()
|
||||
{
local float ZDif;
|
||||
// Added sneakcount hack to try and fix the endless loop crash. Try and track down what was causing this later - Ramm
NiceZombieBoss(Pawn).SneakCount++;
|
||||
if( Pawn.CollisionRadius>27 || Pawn.CollisionHeight>46 )
{
ZDif = Pawn.CollisionHeight-44;
Pawn.SetCollisionSize(24,44);
Pawn.MoveSmooth(vect(0,0,-1)*ZDif);
}
|
||||
super.BeginState();
|
||||
}
|
||||
function EndState()
|
||||
{
local float ZDif;
|
||||
if( Pawn.CollisionRadius!=Pawn.Default.CollisionRadius || Pawn.CollisionHeight!=Pawn.Default.CollisionHeight )
{
ZDif = Pawn.Default.CollisionRadius-44;
Pawn.MoveSmooth(vect(0,0,1)*ZDif);
Pawn.SetCollisionSize(Pawn.Default.CollisionRadius,Pawn.Default.CollisionHeight);
}
|
||||
super.EndState();
|
||||
}
|
||||
}
|
||||
state ZombieCharge
|
||||
{
|
||||
function bool StrafeFromDamage(float Damage, class<DamageType> DamageType, bool bFindDest)
|
||||
{
return false;
|
||||
}
|
||||
// I suspect this function causes bloats to get confused
|
||||
function bool TryStrafe(vector sideDir)
|
||||
{
return false;
|
||||
}
|
||||
function Timer()
|
||||
{
Disable('NotifyBump');
Target = Enemy;
TimedFireWeaponAtEnemy();
|
||||
}
|
||||
WaitForAnim:
|
||||
if ( Monster(Pawn).bShotAnim )
|
||||
{
Goto('Moving');
|
||||
}
|
||||
if ( !FindBestPathToward(Enemy, false,true) )
GotoState('ZombieRestFormation');
|
||||
Moving:
|
||||
MoveToward(Enemy);
|
||||
WhatToDoNext(17);
|
||||
if ( bSoaking )
SoakStop("STUCK IN CHARGING!");
|
||||
}
|
||||
state RunSomewhere
|
||||
{
|
||||
Ignores HearNoise,DamageAttitudeTo,Tick,EnemyChanged,Startle;
|
||||
function BeginState()
|
||||
{
HidingSpots = none;
Enemy = none;
SetTimer(0.1,True);
|
||||
}
|
||||
event SeePlayer(Pawn SeenPlayer)
|
||||
{
SetEnemy(SeenPlayer);
|
||||
}
|
||||
function Timer()
|
||||
{
if( Enemy==none )
Return;
Target = Enemy;
KFM.RangedAttack(Target);
|
||||
}
|
||||
Begin:
|
||||
if( Pawn.Physics==PHYS_Falling )
WaitForLanding();
|
||||
While( KFM.bShotAnim )
Sleep(0.25);
|
||||
if( HidingSpots==none )
HidingSpots = FindRandomDest();
|
||||
if( HidingSpots==none )
NiceZombieBoss(Pawn).BeginHealing();
|
||||
if( ActorReachable(HidingSpots) )
|
||||
{
MoveTarget = HidingSpots;
HidingSpots = none;
|
||||
}
|
||||
else FindBestPathToward(HidingSpots,True,False);
|
||||
if( MoveTarget==none )
NiceZombieBoss(Pawn).BeginHealing();
|
||||
if( Enemy!=none && VSize(Enemy.Location-Pawn.Location)<100 )
MoveToward(MoveTarget,Enemy,,False);
|
||||
else MoveToward(MoveTarget,MoveTarget,,False);
|
||||
if( HidingSpots==none || !PlayerSeesMe() )
NiceZombieBoss(Pawn).BeginHealing();
|
||||
GoTo'Begin';
|
||||
}
|
||||
State SyrRetreat
|
||||
{
|
||||
Ignores HearNoise,DamageAttitudeTo,Tick,EnemyChanged,Startle;
|
||||
function BeginState()
|
||||
{
HidingSpots = none;
Enemy = none;
SetTimer(0.1,True);
|
||||
}
|
||||
event SeePlayer(Pawn SeenPlayer)
|
||||
{
SetEnemy(SeenPlayer);
|
||||
}
|
||||
function Timer()
|
||||
{
if( Enemy==none )
Return;
Target = Enemy;
KFM.RangedAttack(Target);
|
||||
}
|
||||
function FindHideSpot()
|
||||
{
local NavigationPoint N,BN;
local float Dist,BDist,MDist;
local vector EnemyDir;
|
||||
if( Enemy==none )
{
HidingSpots = FindRandomDest();
Return;
}
EnemyDir = Normal(Enemy.Location-Pawn.Location);
For( N=Level.NavigationPointList; N!=none; N=N.NextNavigationPoint )
{
MDist = VSize(N.Location-Pawn.Location);
if( MDist<2500 && !FastTrace(N.Location,Enemy.Location) && FindPathToward(N)!=none )
{
Dist = VSize(N.Location-Enemy.Location)/FMax(MDist/800.f,1.5);
if( (EnemyDir Dot Normal(Enemy.Location-N.Location))<0.2 )
Dist/=10;
if( BN==none || BDist<Dist )
{
BN = N;
BDist = Dist;
}
}
}
if( BN==none )
HidingSpots = FindRandomDest();
else HidingSpots = BN;
|
||||
}
|
||||
Begin:
|
||||
if( Pawn.Physics==PHYS_Falling )
WaitForLanding();
|
||||
While( KFM.bShotAnim )
Sleep(0.25);
|
||||
if( HidingSpots==none )
FindHideSpot();
|
||||
if( HidingSpots==none )
NiceZombieBoss(Pawn).BeginHealing();
|
||||
if( ActorReachable(HidingSpots) )
|
||||
{
MoveTarget = HidingSpots;
HidingSpots = none;
|
||||
}
|
||||
else FindBestPathToward(HidingSpots,True,False);
|
||||
if( MoveTarget==none )
NiceZombieBoss(Pawn).BeginHealing();
|
||||
if( Enemy!=none && VSize(Enemy.Location-Pawn.Location)<100 )
MoveToward(MoveTarget,Enemy,,False);
|
||||
else MoveToward(MoveTarget,MoveTarget,,False);
|
||||
if( HidingSpots==none )
NiceZombieBoss(Pawn).BeginHealing();
|
||||
GoTo'Begin';
|
||||
}
|
||||
function bool PlayerSeesMe()
|
||||
{
|
||||
local Controller C;
|
||||
For( C=Level.ControllerList; C!=none; C=C.NextController )
|
||||
{
if( C.bIsPlayer && C.Pawn!=none && C.Pawn!=Pawn && LineOfSightTo(C.Pawn) )
Return True;
|
||||
}
|
||||
Return False;
|
||||
}
|
||||
// Used to set a timeout for the WaitForAnim state. This is a bit of a hack fix
|
||||
// for the Patriach getting stuck in its idle anim on a dedicated server when it
|
||||
// is supposed to doing something. For some reason, on a dedicated server only, it
|
||||
// never gets an animend call for some of the anims, instead the anim gets
|
||||
// interrupted by the idle anim. If we figure that bug out, we can
|
||||
// probably take this out in the future. But for now the fix works - Ramm
|
||||
function SetWaitForAnimTimout(float NewWaitAnimTimeout, name AnimToWaitFor)
|
||||
{
|
||||
WaitAnimTimeout = NewWaitAnimTimeout;
|
||||
AnimWaitingFor = AnimToWaitFor;
|
||||
}
|
||||
state WaitForAnim
|
||||
{
|
||||
Ignores SeePlayer,HearNoise,Timer,EnemyNotVisible,NotifyBump,Startle;
|
||||
|
||||
// The anim has ended, clear the flags and let the AI do its thing
|
||||
function WaitTimeout()
|
||||
{
if( bUseFreezeHack )
{
if( Pawn!=none )
{
Pawn.AccelRate = Pawn.Default.AccelRate;
Pawn.GroundSpeed = Pawn.Default.GroundSpeed;
}
bUseFreezeHack = False;
}
|
||||
AnimEnd(AnimWaitChannel);
|
||||
}
|
||||
event AnimEnd(int Channel)
|
||||
{
/*local name Sequence;
local float Frame, Rate;
|
||||
Pawn.GetAnimParams( KFMonster(Pawn).ExpectingChannel, Sequence, Frame, Rate );
|
||||
log(GetStateName()$" AnimEnd for Exp Chan "$KFMonster(Pawn).ExpectingChannel$" = "$Sequence$" Channel = "$Channel);
|
||||
Pawn.GetAnimParams( 0, Sequence, Frame, Rate );
log(GetStateName()$" AnimEnd for Chan 0 = "$Sequence);
|
||||
Pawn.GetAnimParams( 1, Sequence, Frame, Rate );
log(GetStateName()$" AnimEnd for Chan 1 = "$Sequence);
|
||||
log(GetStateName()$" AnimEnd bShotAnim = "$Monster(Pawn).bShotAnim); */
|
||||
Pawn.AnimEnd(Channel);
if ( !Monster(Pawn).bShotAnim )
WhatToDoNext(99);
|
||||
}
|
||||
function Tick( float Delta )
|
||||
{
Global.Tick(Delta);
|
||||
if( WaitAnimTimeout > 0 )
{
WaitAnimTimeout -= Delta;
|
||||
if( WaitAnimTimeout <= 0 )
{
WaitAnimTimeout = 0;
WaitTimeout();
}
}
|
||||
if( bUseFreezeHack )
{
MoveTarget = none;
MoveTimer = -1;
Pawn.Acceleration = vect(0,0,0);
Pawn.GroundSpeed = 1;
Pawn.AccelRate = 0;
}
|
||||
}
|
||||
function EndState()
|
||||
{
super.EndState();
|
||||
AnimWaitingFor = '';
|
||||
}
|
||||
}
|
||||
defaultproperties
|
||||
{
|
||||
}
|
||||
255
sources/Zeds/Nice/NiceZombieBrute.uc
Normal file
255
sources/Zeds/Nice/NiceZombieBrute.uc
Normal file
|
|
@ -0,0 +1,255 @@
|
|||
class NiceZombieBrute extends NiceZombieBruteBase;
|
||||
var float BlockMeleeDmgMul; //Multiplier for melee damage taken, when Brute is blocking (no matter where the hit was landed)
|
||||
var float HeadShotgunDmgMul; //Multiplier for shotgun damage taken into UNBLOCKED head
|
||||
var float HeadBulletDmgMul; //Multiplier for non-sniper bullet damage taken into UNBLOCKED head
|
||||
simulated function PostNetBeginPlay()
|
||||
{
|
||||
super.PostNetBeginPlay();
|
||||
EnableChannelNotify(1,1);
|
||||
EnableChannelNotify(2,1);
|
||||
AnimBlendParams(1, 1.0, 0.0,, SpineBone1);
|
||||
StartCharging();
|
||||
}
|
||||
function ServerRaiseBlock()
|
||||
{
|
||||
bServerBlock = true;
|
||||
SetAnimAction('BlockLoop');
|
||||
}
|
||||
function ServerLowerBlock()
|
||||
{
|
||||
local name Sequence;
|
||||
local float Frame, Rate;
|
||||
bServerBlock = false;
|
||||
GetAnimParams(1, Sequence, Frame, Rate);
|
||||
if (Sequence == 'BlockLoop')
AnimStopLooping(1);
|
||||
}
|
||||
simulated function PostNetReceive()
|
||||
{
|
||||
local name Sequence;
|
||||
local float Frame, Rate;
|
||||
if(bClientCharge != bChargingPlayer)
|
||||
{
bClientCharge = bChargingPlayer;
if (bChargingPlayer)
{
MovementAnims[0] = ChargingAnim;
MeleeAnims[0] = 'BruteRageAttack';
MeleeAnims[1] = 'BruteRageAttack';
MeleeAnims[2] = 'BruteRageAttack';
}
else
{
MovementAnims[0] = default.MovementAnims[0];
MeleeAnims[0] = default.MeleeAnims[0];
MeleeAnims[1] = default.MeleeAnims[1];
MeleeAnims[2] = default.MeleeAnims[2];
}
|
||||
}
|
||||
if (bClientBlock != bServerBlock)
|
||||
{
bClientBlock = bServerBlock;
if (bClientBlock)
SetAnimAction('BlockLoop');
else
{
GetAnimParams(1, Sequence, Frame, Rate);
if (Sequence == 'BlockLoop')
AnimStopLooping(1);
}
|
||||
}
|
||||
}
|
||||
simulated function Tick(float DeltaTime)
|
||||
{
|
||||
super.Tick(DeltaTime);
|
||||
if (Role == ROLE_Authority)
|
||||
{
// Lock to target when attacking (except on beginner!)
if (bShotAnim && LookTarget != none)
Acceleration = AccelRate * Normal(LookTarget.Location - Location);
// Block according to rules
if (Role == ROLE_Authority && !bServerBlock && !bShotAnim)
if (Controller != none && Controller.Target != none)
ServerRaiseBlock();
|
||||
}
|
||||
}
|
||||
// Override to always move when attacking
|
||||
function RangedAttack(Actor A){
|
||||
if (bShotAnim || Physics == PHYS_Swimming)
return;
|
||||
else if (CanAttack(A))
|
||||
{
if (bChargingPlayer)
SetAnimAction('AoeClaw');
else
{
if (Rand(BlockHitsLanded) < 1)
SetAnimAction('BlockClaw');
else
SetAnimAction('Claw');
}
bShotAnim = true;
return;
|
||||
}
|
||||
}
|
||||
function bool IsHeadShot(vector Loc, vector Ray, float AdditionalScale)
|
||||
{
|
||||
local float D;
|
||||
local float AddScale;
|
||||
local bool bIsBlocking;
|
||||
bBlockedHS = false;
|
||||
if (bServerBlock && !IsTweening(1))
|
||||
{
bIsBlocking = true;
AddScale = AdditionalScale + BlockAddScale;
|
||||
}
|
||||
else
AddScale = AdditionalScale + 1.0;
|
||||
if (Super.IsHeadShot(Loc, Ray, AddScale))
|
||||
{
if (bIsBlocking)
{
D = vector(Rotation) dot Ray;
if (-D > 0.20) {
bBlockedHS = true;
return false;
}
else
return true;
}
else
return true;
|
||||
}
|
||||
else
return false;
|
||||
}
|
||||
function bool CheckMiniFlinch( int flinchScore,
Pawn instigatedBy,
Vector hitLocation,
Vector momentum,
class<NiceWeaponDamageType> damageType,
float headshotLevel,
KFPlayerReplicationInfo KFPRI){
|
||||
return false;
|
||||
}
|
||||
function TakeDamageClient(int Damage, Pawn InstigatedBy, Vector HitLocation, Vector Momentum, class<NiceWeaponDamageType> damageType, float headshotLevel, float lockonTime){
|
||||
local float D;
|
||||
local bool bIsHeadshot;
|
||||
local bool bIsBlocking;
|
||||
bBlockedHS = false;
|
||||
if(bServerBlock && !IsTweening(1))
bIsBlocking = true;
|
||||
if(headshotLevel > 0.0 && bIsBlocking){
D = vector(Rotation) dot Normal(Momentum);
D *= -1;
if(D > 0.30) {
bBlockedHS = true;
headshotLevel = 0.0;
}
|
||||
}
|
||||
bIsHeadShot = (headshotLevel > 0.0);
|
||||
// damage, which doen't make headshots, always does full damage to Brute -- PooSH
|
||||
if (damageType != none && damageType.default.bCheckForHeadShots) {
if (!bIsHeadShot && bBlockedHS)
{
if(damageType != none || damageType.default.bIsProjectile)
PlaySound(class'MetalHitEmitter'.default.ImpactSounds[rand(3)],, 128);
else if(class<NiceDamageTypeVetBerserker>(damageType) != none)
PlaySound(Sound'KF_KnifeSnd.Knife_HitMetal',, 128);
if(damageType.default.bDealBurningDamage && !damageType.default.bIsPowerWeapon)
Damage *= BlockFireDmgMul; // Fire damage isn't reduced as much, excluding TrenchGun
else
Damage *= BlockDmgMul; // Greatly reduce damage as we only hit the metal plating
}
else if(bServerBlock && class<NiceDamageTypeVetBerserker>(damageType) != none)
Damage *= BlockMeleeDmgMul; // Give Brute higher melee damage resistance, but apply it only if Brute is blocking
else if(bIsHeadShot){
if (damageType.default.bIsPowerWeapon)
Damage *= HeadShotgunDmgMul; // Give Brute's head resistance to stotguns
}
|
||||
}
|
||||
// Record damage over 2-second frames
|
||||
if (LastDamagedTime < Level.TimeSeconds)
|
||||
{
TwoSecondDamageTotal = 0;
LastDamagedTime = Level.TimeSeconds + 2;
|
||||
}
|
||||
TwoSecondDamageTotal += Damage;
|
||||
// If criteria is met make him rage
|
||||
if (!bDecapitated && !bChargingPlayer && TwoSecondDamageTotal > RageDamageThreshold)
StartCharging();
|
||||
Super(NiceMonster).TakeDamageClient(Damage, instigatedBy, hitLocation, momentum, damageType, headshotLevel, lockonTime);
|
||||
if (bDecapitated)
Died(InstigatedBy.Controller, damageType, HitLocation);
|
||||
}
|
||||
function TakeFireDamage(int Damage, Pawn Instigator)
|
||||
{
|
||||
Super.TakeFireDamage(Damage, Instigator);
|
||||
// Adjust movement speed if not charging
|
||||
if (!bChargingPlayer)
|
||||
{
if (bBurnified)
GroundSpeed = GetOriginalGroundSpeed() * BurnGroundSpeedMul;
else
GroundSpeed = GetOriginalGroundSpeed();
|
||||
}
|
||||
}
|
||||
function ClawDamageTarget()
|
||||
{
|
||||
local KFHumanPawn HumanTarget;
|
||||
local float UsedMeleeDamage;
|
||||
local Actor OldTarget;
|
||||
local name Sequence;
|
||||
local float Frame, Rate;
|
||||
local bool bHitSomeone;
|
||||
if (MeleeDamage > 1)
UsedMeleeDamage = (MeleeDamage - (MeleeDamage * 0.05)) + (MeleeDamage * (FRand() * 0.1));
|
||||
else
UsedMeleeDamage = MeleeDamage;
GetAnimParams(1, Sequence, Frame, Rate);
|
||||
if (Controller != none && Controller.Target != none)
|
||||
{
if (Sequence == 'BruteRageAttack')
{
OldTarget = Controller.Target;
foreach VisibleCollidingActors(class'KFHumanPawn', HumanTarget, MeleeRange + class'KFHumanPawn'.default.CollisionRadius)
{
bHitSomeone = ClawDamageSingleTarget(UsedMeleeDamage, HumanTarget);
}
Controller.Target = OldTarget;
if (bHitSomeone)
BlockHitsLanded++;
}
else if (Sequence != 'BruteAttack1' && Sequence != 'BruteAttack2' && Sequence != 'DoorBash') // Block attack
{
bHitSomeone = ClawDamageSingleTarget(UsedMeleeDamage, Controller.Target);
if (bHitSomeone)
BlockHitsLanded++;
}
else
bHitSomeone = ClawDamageSingleTarget(UsedMeleeDamage, Controller.Target);
if (bHitSomeone)
PlaySound(MeleeAttackHitSound, SLOT_Interact, 1.25);
|
||||
}
|
||||
}
|
||||
function bool ClawDamageSingleTarget(float UsedMeleeDamage, Actor ThisTarget)
|
||||
{
|
||||
local Pawn HumanTarget;
|
||||
local KFPlayerController HumanTargetController;
|
||||
local bool bHitSomeone;
|
||||
local float EnemyAngle;
|
||||
local vector PushForceVar;
|
||||
EnemyAngle = Normal(ThisTarget.Location - Location) dot vector(Rotation);
|
||||
if (EnemyAngle > 0)
|
||||
{
Controller.Target = ThisTarget;
if (MeleeDamageTarget(UsedMeleeDamage, vect(0, 0, 0)))
{
HumanTarget = KFHumanPawn(ThisTarget);
if (HumanTarget != none)
{
EnemyAngle = (EnemyAngle * 0.5) + 0.5; // Players at sides get knocked back half as much
PushForceVar = (PushForce * Normal(HumanTarget.Location - Location) * EnemyAngle) + PushAdd;
if (!bChargingPlayer)
PushForceVar *= 0.85;
// (!) I'm sure the VeterancyName string is localized but I'm not sure of another way compatible with ServerPerks
if (KFPlayerReplicationInfo(HumanTarget.Controller.PlayerReplicationInfo).ClientVeteranSkill != none)
if (KFPlayerReplicationInfo(HumanTarget.Controller.PlayerReplicationInfo).ClientVeteranSkill
.default.VeterancyName == "Berserker")
PushForceVar *= 0.75;
if (!(HumanTarget.Physics == PHYS_WALKING || HumanTarget.Physics == PHYS_none))
PushForceVar *= vect(1, 1, 0); // (!) Don't throw upwards if we are not on the ground - adjust for more flexibility
|
||||
HumanTarget.AddVelocity(PushForceVar);
|
||||
HumanTargetController = KFPlayerController(HumanTarget.Controller);
if (HumanTargetController != none)
HumanTargetController.ShakeView(ShakeViewRotMag, ShakeViewRotRate, ShakeViewRotTime,
ShakeViewOffsetMag, ShakeViewOffsetRate, ShakeViewOffsetTime);
bHitSomeone = true;
}
}
|
||||
}
|
||||
return bHitSomeone;
|
||||
}
|
||||
function StartCharging()
|
||||
{
|
||||
// How many times should we hit before we cool down?
|
||||
if (Level.Game.NumPlayers <= 3)
MaxRageCounter = 2;
|
||||
else
MaxRageCounter = 3;
|
||||
RageCounter = MaxRageCounter;
|
||||
PlaySound(RageSound, SLOT_Talk, 255);
|
||||
GotoState('RageCharging');
|
||||
}
|
||||
state RageCharging
|
||||
{
|
||||
Ignores StartCharging;
|
||||
function bool CanGetOutOfWay()
|
||||
{
return false;
|
||||
}
|
||||
function bool CanSpeedAdjust()
|
||||
{
return false;
|
||||
}
|
||||
function BeginState()
|
||||
{
bFrustrated = false;
bChargingPlayer = true;
RageSpeedTween = 0.0;
if (Level.NetMode != NM_DedicatedServer)
ClientChargingAnims();
|
||||
NetUpdateTime = Level.TimeSeconds - 1;
|
||||
}
|
||||
function EndState()
|
||||
{
bChargingPlayer = false;
|
||||
NiceZombieBruteController(Controller).RageFrustrationTimer = 0;
|
||||
if (Health > 0)
{
GroundSpeed = GetOriginalGroundSpeed();
if (bBurnified)
GroundSpeed *= BurnGroundSpeedMul;
}
|
||||
if( Level.NetMode!=NM_DedicatedServer )
ClientChargingAnims();
|
||||
NetUpdateTime = Level.TimeSeconds - 1;
|
||||
}
|
||||
function Tick(float Delta)
|
||||
{
if (!bShotAnim)
{
RageSpeedTween = FClamp(RageSpeedTween + (Delta * 0.75), 0, 1.0);
GroundSpeed = OriginalGroundSpeed + ((OriginalGroundSpeed * 0.75 / MaxRageCounter * (RageCounter + 1) * RageSpeedTween));
if (bBurnified)
GroundSpeed *= BurnGroundSpeedMul;
}
|
||||
Global.Tick(Delta);
|
||||
}
|
||||
function bool MeleeDamageTarget(int HitDamage, vector PushDir)
|
||||
{
local bool DamDone, bWasEnemy;
|
||||
bWasEnemy = (Controller.Target == Controller.Enemy);
|
||||
DamDone = Super.MeleeDamageTarget(HitDamage * RageDamageMul, vect(0, 0, 0));
if(Controller == none)
return true;
|
||||
if (bWasEnemy && DamDone)
{
//ChangeTarget();
CalmDown();
}
|
||||
return DamDone;
|
||||
}
|
||||
function CalmDown()
|
||||
{
RageCounter = FClamp(RageCounter - 1, 0, MaxRageCounter);
if (RageCounter == 0)
GotoState('');
|
||||
}
|
||||
function ChangeTarget()
|
||||
{
local Controller C;
local Pawn BestPawn;
local float Dist, BestDist;
for (C = Level.ControllerList; C != none; C = C.NextController)
if (C.Pawn != none && KFHumanPawn(C.Pawn) != none)
{
Dist = VSize(C.Pawn.Location - Location);
if (C.Pawn == Controller.Target)
Dist += GroundSpeed * 4;
if (BestPawn == none)
{
BestPawn = C.Pawn;
BestDist = Dist;
}
else if (Dist < BestDist)
{
BestPawn = C.Pawn;
BestDist = Dist;
}
}
if (BestPawn != none && BestPawn != Controller.Enemy)
MonsterController(Controller).ChangeEnemy(BestPawn, Controller.CanSee(BestPawn));
|
||||
}
|
||||
}
|
||||
// Override to prevent stunning
|
||||
function bool FlipOver()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
// Shouldn't fight with our own
|
||||
function bool SameSpeciesAs(Pawn P)
|
||||
{
|
||||
return (NiceZombieBrute(P) != none);
|
||||
}
|
||||
// ------------------------------------------------------
|
||||
// Animation --------------------------------------------
|
||||
// ------------------------------------------------------
|
||||
// Overridden to handle playing upper body only attacks when moving
|
||||
simulated event SetAnimAction(name NewAction)
|
||||
{
|
||||
if (NewAction=='')
return;
|
||||
if (NewAction == 'Claw')
|
||||
{
NewAction = MeleeAnims[rand(2)];
|
||||
}
|
||||
else if (NewAction == 'BlockClaw')
|
||||
{
NewAction = 'BruteBlockSlam';
|
||||
}
|
||||
else if (NewAction == 'AoeClaw')
|
||||
{
NewAction = 'BruteRageAttack';
|
||||
}
|
||||
ExpectingChannel = DoAnimAction(NewAction);
|
||||
if (AnimNeedsWait(NewAction))
bWaitForAnim = true;
|
||||
else
bWaitForAnim = false;
|
||||
if (Level.NetMode != NM_Client)
|
||||
{
AnimAction = NewAction;
bResetAnimAct = True;
ResetAnimActTime = Level.TimeSeconds+0.3;
|
||||
}
|
||||
}
|
||||
simulated function int DoAnimAction( name AnimName )
|
||||
{
|
||||
if (AnimName=='BruteAttack1' || AnimName=='BruteAttack2' || AnimName=='ZombieFireGun' || AnimName == 'DoorBash')
|
||||
{
if (Role == ROLE_Authority)
ServerLowerBlock();
AnimBlendParams(1, 1.0, 0.0,, FireRootBone);
PlayAnim(AnimName,, 0.1, 1);
return 1;
|
||||
}
|
||||
else if (AnimName == 'BruteRageAttack')
|
||||
{
if (Role == ROLE_Authority)
ServerLowerBlock();
AnimBlendParams(1, 1.0, 0.0,, FireRootBone);
PlayAnim(AnimName,, 0.1, 1);
return 1;
|
||||
}
|
||||
else if (AnimName == 'BlockLoop')
|
||||
{
AnimBlendParams(1, 1.0, 0.0,, FireRootBone);
LoopAnim(AnimName,, 0.25, 1);
return 1;
|
||||
}
|
||||
else if (AnimName == 'BruteBlockSlam')
|
||||
{
AnimBlendParams(2, 1.0, 0.0,, FireRootBone);
PlayAnim(AnimName,, 0.1, 2);
return 2;
|
||||
}
|
||||
return Super.DoAnimAction(AnimName);
|
||||
}
|
||||
// The animation is full body and should set the bWaitForAnim flag
|
||||
simulated function bool AnimNeedsWait(name TestAnim)
|
||||
{
|
||||
if (TestAnim == 'DoorBash')
return true;
|
||||
return false;
|
||||
}
|
||||
simulated function AnimEnd(int Channel)
|
||||
{
|
||||
local name Sequence;
|
||||
local float Frame, Rate;
|
||||
GetAnimParams(Channel, Sequence, Frame, Rate);
|
||||
// Don't allow notification for a looping animation
|
||||
if (Sequence == 'BlockLoop')
return;
|
||||
// Disable channel 2 when we're done with it
|
||||
if (Channel == 2 && Sequence == 'BruteBlockSlam')
|
||||
{
AnimBlendParams(2, 0);
bShotAnim = false;
return;
|
||||
}
|
||||
Super.AnimEnd(Channel);
|
||||
}
|
||||
simulated function ClientChargingAnims()
|
||||
{
|
||||
PostNetReceive();
|
||||
}
|
||||
function PlayHit(float Damage, Pawn InstigatedBy, vector HitLocation, class<DamageType> damageType, vector Momentum, optional int HitIdx)
|
||||
{
|
||||
local Actor A;
|
||||
if (bBlockedHS)
A = Spawn(class'NiceBlockHitEmitter', InstigatedBy,, HitLocation, rotator(Normal(HitLocation - Location)));
|
||||
else
Super.PlayHit(Damage, InstigatedBy, HitLocation, damageType, Momentum, HitIdx);
|
||||
}
|
||||
defaultproperties
|
||||
{
BlockMeleeDmgMul=1.000000
HeadShotgunDmgMul=1.000000
HeadBulletDmgMul=1.000000
stunLoopStart=0.130000
stunLoopEnd=0.650000
idleInsertFrame=0.950000
DetachedArmClass=Class'ScrnZedPack.SeveredArmBrute'
DetachedLegClass=Class'ScrnZedPack.SeveredLegBrute'
DetachedHeadClass=Class'ScrnZedPack.SeveredHeadBrute'
ControllerClass=Class'NicePack.NiceZombieBruteController'
|
||||
}
|
||||
47
sources/Zeds/Nice/NiceZombieBruteBase.uc
Normal file
47
sources/Zeds/Nice/NiceZombieBruteBase.uc
Normal file
|
|
@ -0,0 +1,47 @@
|
|||
class NiceZombieBruteBase extends NiceMonster;
|
||||
#exec load obj file=ScrnZedPack_T.utx
|
||||
#exec load obj file=ScrnZedPack_S.uax
|
||||
#exec load obj file=ScrnZedPack_A.ukx
|
||||
#exec OBJ LOAD FILE=KFWeaponSound.uax
|
||||
var bool bChargingPlayer;
|
||||
var bool bClientCharge;
|
||||
var bool bFrustrated;
|
||||
var int MaxRageCounter; // Maximum amount of players we can hit before calming down
|
||||
var int RageCounter; // Decreases each time we successfully hit a player
|
||||
var float RageSpeedTween;
|
||||
var int TwoSecondDamageTotal;
|
||||
var float LastDamagedTime;
|
||||
var int RageDamageThreshold;
|
||||
var int BlockHitsLanded; // Hits made while blocking or raging
|
||||
var name ChargingAnim;
|
||||
var Sound RageSound;
|
||||
// View shaking for players
|
||||
var() vector ShakeViewRotMag;
|
||||
var() vector ShakeViewRotRate;
|
||||
var() float ShakeViewRotTime;
|
||||
var() vector ShakeViewOffsetMag;
|
||||
var() vector ShakeViewOffsetRate;
|
||||
var() float ShakeViewOffsetTime;
|
||||
var float PushForce;
|
||||
var vector PushAdd; // Used to add additional height to push
|
||||
var float RageDamageMul; // Multiplier for hit damage when raging
|
||||
var float RageBumpDamage; // Damage done when we hit other specimens while raging
|
||||
var float BlockAddScale; // Additional head scale when blocking
|
||||
var bool bBlockedHS;
|
||||
var bool bBlocking;
|
||||
var bool bServerBlock;
|
||||
var bool bClientBlock;
|
||||
var float BlockDmgMul; // Multiplier for damage taken from blocked shots
|
||||
var float BlockFireDmgMul;
|
||||
var float BurnGroundSpeedMul; // Multiplier for ground speed when burning
|
||||
replication
|
||||
{
|
||||
reliable if(Role == ROLE_Authority)
bChargingPlayer, bServerBlock;
|
||||
}
|
||||
//--------------------------------------------------------------------------
|
||||
// NOTE: All Code resides in the child class(this class was only created to
|
||||
// eliminate hitching caused by loading default properties during play)
|
||||
//--------------------------------------------------------------------------
|
||||
defaultproperties
|
||||
{
RageDamageThreshold=50
ChargingAnim="BruteRun"
RageSound=SoundGroup'ScrnZedPack_S.Brute.BruteRage'
ShakeViewRotMag=(X=500.000000,Y=500.000000,Z=600.000000)
ShakeViewRotRate=(X=12500.000000,Y=12500.000000,Z=12500.000000)
ShakeViewRotTime=6.000000
ShakeViewOffsetMag=(X=5.000000,Y=10.000000,Z=5.000000)
ShakeViewOffsetRate=(X=300.000000,Y=300.000000,Z=300.000000)
ShakeViewOffsetTime=3.500000
PushForce=860.000000
PushAdd=(Z=150.000000)
RageDamageMul=1.100000
RageBumpDamage=4.000000
BlockAddScale=2.500000
BlockDmgMul=0.100000
BlockFireDmgMul=1.000000
BurnGroundSpeedMul=0.700000
StunThreshold=4.000000
flameFuel=0.500000
clientHeadshotScale=1.300000
MeleeAnims(0)="BruteAttack1"
MeleeAnims(1)="BruteAttack2"
MeleeAnims(2)="BruteBlockSlam"
MoanVoice=SoundGroup'ScrnZedPack_S.Brute.BruteTalk'
BleedOutDuration=7.000000
ZombieFlag=3
MeleeDamage=20
damageForce=25000
bFatAss=True
KFRagdollName="FleshPound_Trip"
MeleeAttackHitSound=SoundGroup'ScrnZedPack_S.Brute.BruteHitPlayer'
JumpSound=SoundGroup'ScrnZedPack_S.Brute.BruteJump'
SpinDamConst=20.000000
SpinDamRand=20.000000
bMeleeStunImmune=True
bUseExtendedCollision=True
ColOffset=(Z=52.000000)
ColRadius=35.000000
ColHeight=25.000000
SeveredArmAttachScale=1.300000
SeveredLegAttachScale=1.200000
SeveredHeadAttachScale=1.500000
PlayerCountHealthScale=0.250000
OnlineHeadshotOffset=(X=22.000000,Z=68.000000)
OnlineHeadshotScale=1.300000
HeadHealth=180.000000
PlayerNumHeadHealthScale=0.200000
MotionDetectorThreat=5.000000
HitSound(0)=SoundGroup'ScrnZedPack_S.Brute.BrutePain'
DeathSound(0)=SoundGroup'ScrnZedPack_S.Brute.BruteDeath'
ChallengeSound(0)=SoundGroup'ScrnZedPack_S.Brute.BruteChallenge'
ChallengeSound(1)=SoundGroup'ScrnZedPack_S.Brute.BruteChallenge'
ChallengeSound(2)=SoundGroup'ScrnZedPack_S.Brute.BruteChallenge'
ChallengeSound(3)=SoundGroup'ScrnZedPack_S.Brute.BruteChallenge'
ScoringValue=60
IdleHeavyAnim="BruteIdle"
IdleRifleAnim="BruteIdle"
RagDeathUpKick=100.000000
MeleeRange=85.000000
GroundSpeed=140.000000
WaterSpeed=120.000000
HealthMax=1000.000000
Health=1000
HeadHeight=2.500000
HeadScale=1.300000
MenuName="Brute"
MovementAnims(0)="BruteWalkC"
MovementAnims(1)="BruteWalkC"
WalkAnims(0)="BruteWalkC"
WalkAnims(1)="BruteWalkC"
WalkAnims(2)="RunL"
WalkAnims(3)="RunR"
IdleCrouchAnim="BruteIdle"
IdleWeaponAnim="BruteIdle"
IdleRestAnim="BruteIdle"
AmbientSound=SoundGroup'ScrnZedPack_S.Brute.BruteIdle1Shot'
Mesh=SkeletalMesh'ScrnZedPack_A.BruteMesh'
PrePivot=(Z=0.000000)
Skins(0)=Combiner'ScrnZedPack_T.Brute.Brute_Final'
Mass=600.000000
RotationRate=(Yaw=45000,Roll=0)
|
||||
}
|
||||
29
sources/Zeds/Nice/NiceZombieBruteController.uc
Normal file
29
sources/Zeds/Nice/NiceZombieBruteController.uc
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
class NiceZombieBruteController extends NiceMonsterController;
|
||||
var float RageAnimTimeout; // How long until the RageAnim is completed; Hack so the server doesn't get stuck in idle when its doing the Rage anim
|
||||
var bool bDoneSpottedCheck;
|
||||
var float RageFrustrationTimer; // Tracks how long we have been walking toward a visible enemy
|
||||
var float RageFrustrationThreshhold; // Base value for how long the FP should walk torward an enemy without reaching them before getting frustrated and raging
|
||||
function TimedFireWeaponAtEnemy()
|
||||
{
|
||||
if ( (Enemy == none) || FireWeaponAt(Enemy) )
SetCombatTimer();
|
||||
else
SetTimer(0.01, True);
|
||||
}
|
||||
state ZombieCharge
|
||||
{
|
||||
function bool StrafeFromDamage(float Damage, class<DamageType> DamageType, bool bFindDest)
|
||||
{
return false;
|
||||
}
|
||||
function bool TryStrafe(vector sideDir)
|
||||
{
return false;
|
||||
}
|
||||
function Timer()
|
||||
{
Disable('NotifyBump');
Target = Enemy;
TimedFireWeaponAtEnemy();
|
||||
}
|
||||
function BeginState()
|
||||
{
super.BeginState();
|
||||
RageFrustrationThreshhold = default.RageFrustrationThreshhold + (Frand() * 5);
|
||||
}
|
||||
}
|
||||
defaultproperties
|
||||
{
RageFrustrationThreshhold=10.000000
|
||||
}
|
||||
126
sources/Zeds/Nice/NiceZombieClot.uc
Normal file
126
sources/Zeds/Nice/NiceZombieClot.uc
Normal file
|
|
@ -0,0 +1,126 @@
|
|||
// Zombie Monster for KF Invasion gametype
|
||||
class NiceZombieClot extends NiceZombieClotBase;
|
||||
#exec OBJ LOAD FILE=KF_Freaks_Trip.ukx
|
||||
#exec OBJ LOAD FILE=KF_Specimens_Trip_T.utx
|
||||
#exec OBJ LOAD FILE=MeanZedSkins.utx
|
||||
function ClawDamageTarget()
|
||||
{
|
||||
local vector PushDir;
|
||||
local KFPawn KFP;
|
||||
local float UsedMeleeDamage;
|
||||
|
||||
if( MeleeDamage > 1 )
|
||||
{
UsedMeleeDamage = (MeleeDamage - (MeleeDamage * 0.05)) + (MeleeDamage * (FRand() * 0.1));
|
||||
}
|
||||
else
|
||||
{
UsedMeleeDamage = MeleeDamage;
|
||||
}
|
||||
// If zombie has latched onto us...
|
||||
if ( MeleeDamageTarget( UsedMeleeDamage, PushDir))
|
||||
{
KFP = KFPawn(Controller.Target);
|
||||
PlaySound(MeleeAttackHitSound, SLOT_Interact, 2.0);
|
||||
if( !bDecapitated && KFP != none )
{
if( KFPlayerReplicationInfo(KFP.PlayerReplicationInfo) == none ||
KFP.GetVeteran().static.CanBeGrabbed(KFPlayerReplicationInfo(KFP.PlayerReplicationInfo), self))
{
if( DisabledPawn != none )
{
DisabledPawn.bMovementDisabled = false;
}
|
||||
KFP.DisableMovement(GrappleDuration);
DisabledPawn = KFP;
}
}
|
||||
}
|
||||
}
|
||||
function RangedAttack(Actor A)
|
||||
{
|
||||
if ( bShotAnim || Physics == PHYS_Swimming)
return;
|
||||
else if ( CanAttack(A) )
|
||||
{
bShotAnim = true;
SetAnimAction('Claw');
return;
|
||||
}
|
||||
}
|
||||
simulated event SetAnimAction(name NewAction)
|
||||
{
|
||||
local int meleeAnimIndex;
|
||||
if( NewAction=='' )
Return;
|
||||
if(NewAction == 'Claw')
|
||||
{
meleeAnimIndex = Rand(3);
NewAction = meleeAnims[meleeAnimIndex];
|
||||
}
|
||||
ExpectingChannel = DoAnimAction(NewAction);
|
||||
if( AnimNeedsWait(NewAction) )
|
||||
{
bWaitForAnim = true;
|
||||
}
|
||||
if( Level.NetMode!=NM_Client )
|
||||
{
AnimAction = NewAction;
bResetAnimAct = True;
ResetAnimActTime = Level.TimeSeconds+0.3;
|
||||
}
|
||||
}
|
||||
simulated function bool AnimNeedsWait(name TestAnim)
|
||||
{
|
||||
if( TestAnim == 'KnockDown' || TestAnim == 'DoorBash' )
|
||||
{
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
simulated function int DoAnimAction( name AnimName )
|
||||
{
|
||||
if( AnimName=='ClotGrapple' || AnimName=='ClotGrappleTwo' || AnimName=='ClotGrappleThree' )
|
||||
{
AnimBlendParams(1, 1.0, 0.1,, FireRootBone);
PlayAnim(AnimName,, 0.1, 1);
|
||||
// Randomly send out a message about Clot grabbing you(10% chance)
if ( FRand() < 0.10 && LookTarget != none && KFPlayerController(LookTarget.Controller) != none &&
VSizeSquared(Location - LookTarget.Location) < 2500 /* (MeleeRange + 20)^2 */ &&
Level.TimeSeconds - KFPlayerController(LookTarget.Controller).LastClotGrabMessageTime > ClotGrabMessageDelay &&
KFPlayerController(LookTarget.Controller).SelectedVeterancy != class'KFVetBerserker' )
{
PlayerController(LookTarget.Controller).Speech('AUTO', 11, "");
KFPlayerController(LookTarget.Controller).LastClotGrabMessageTime = Level.TimeSeconds;
}
|
||||
bGrappling = true;
GrappleEndTime = Level.TimeSeconds + GrappleDuration;
|
||||
return 1;
|
||||
}
|
||||
return super.DoAnimAction( AnimName );
|
||||
}
|
||||
simulated function Tick(float DeltaTime)
|
||||
{
|
||||
super.Tick(DeltaTime);
|
||||
if( bShotAnim && Role == ROLE_Authority )
|
||||
{
if( LookTarget!=none )
{
Acceleration = AccelRate * Normal(LookTarget.Location - Location);
}
|
||||
}
|
||||
if( Role == ROLE_Authority && bGrappling )
|
||||
{
if( Level.TimeSeconds > GrappleEndTime )
{
bGrappling = false;
}
|
||||
}
|
||||
// if we move out of melee range, stop doing the grapple animation
|
||||
if( bGrappling && LookTarget != none )
|
||||
{
if( VSize(LookTarget.Location - Location) > MeleeRange + CollisionRadius + LookTarget.CollisionRadius )
{
bGrappling = false;
AnimEnd(1);
}
|
||||
}
|
||||
}
|
||||
function RemoveHead()
|
||||
{
|
||||
Super.RemoveHead();
|
||||
MeleeAnims[0] = 'Claw';
|
||||
MeleeAnims[1] = 'Claw';
|
||||
MeleeAnims[2] = 'Claw2';
|
||||
MeleeDamage *= 2;
|
||||
MeleeRange *= 2;
|
||||
if( DisabledPawn != none )
|
||||
{
DisabledPawn.bMovementDisabled = false;
DisabledPawn = none;
|
||||
}
|
||||
}
|
||||
function Died(Controller Killer, class<DamageType> damageType, vector HitLocation)
|
||||
{
|
||||
if( DisabledPawn != none )
|
||||
{
DisabledPawn.bMovementDisabled = false;
DisabledPawn = none;
|
||||
}
|
||||
super.Died(Killer, damageType, HitLocation);
|
||||
}
|
||||
simulated function Destroyed()
|
||||
{
|
||||
super.Destroyed();
|
||||
if( DisabledPawn != none )
|
||||
{
DisabledPawn.bMovementDisabled = false;
DisabledPawn = none;
|
||||
}
|
||||
}
|
||||
static simulated function PreCacheStaticMeshes(LevelInfo myLevel)
|
||||
{//should be derived and used.
|
||||
Super.PreCacheStaticMeshes(myLevel);
|
||||
/*
|
||||
myLevel.AddPrecacheStaticMesh(StaticMesh'kf_gore_trip_sm.clot.clothead_piece_1');
|
||||
myLevel.AddPrecacheStaticMesh(StaticMesh'kf_gore_trip_sm.clot.clothead_piece_2');
|
||||
myLevel.AddPrecacheStaticMesh(StaticMesh'kf_gore_trip_sm.clot.clothead_piece_3');
|
||||
myLevel.AddPrecacheStaticMesh(StaticMesh'kf_gore_trip_sm.clot.clothead_piece_4');
|
||||
myLevel.AddPrecacheStaticMesh(StaticMesh'kf_gore_trip_sm.clot.clothead_piece_5');
|
||||
myLevel.AddPrecacheStaticMesh(StaticMesh'kf_gore_trip_sm.clot.clothead_piece_6');
|
||||
*/
|
||||
}
|
||||
static simulated function PreCacheMaterials(LevelInfo myLevel)
|
||||
{//should be derived and used.
|
||||
myLevel.AddPrecacheMaterial(Combiner'KF_Specimens_Trip_T.clot_cmb');
|
||||
myLevel.AddPrecacheMaterial(Combiner'KF_Specimens_Trip_T.clot_env_cmb');
|
||||
myLevel.AddPrecacheMaterial(Texture'KF_Specimens_Trip_T.clot_diffuse');
|
||||
myLevel.AddPrecacheMaterial(Texture'KF_Specimens_Trip_T.clot_spec');
|
||||
}
|
||||
defaultproperties
|
||||
{
idleInsertFrame=0.468000
EventClasses(0)="NicePack.NiceZombieClot"
MoanVoice=SoundGroup'KF_EnemiesFinalSnd.clot.Clot_Talk'
MeleeAttackHitSound=SoundGroup'KF_EnemiesFinalSnd.clot.Clot_HitPlayer'
JumpSound=SoundGroup'KF_EnemiesFinalSnd.clot.Clot_Jump'
DetachedArmClass=Class'KFChar.SeveredArmClot'
DetachedLegClass=Class'KFChar.SeveredLegClot'
DetachedHeadClass=Class'KFChar.SeveredHeadClot'
HitSound(0)=SoundGroup'KF_EnemiesFinalSnd.clot.Clot_Pain'
DeathSound(0)=SoundGroup'KF_EnemiesFinalSnd.clot.Clot_Death'
ChallengeSound(0)=SoundGroup'KF_EnemiesFinalSnd.clot.Clot_Challenge'
ChallengeSound(1)=SoundGroup'KF_EnemiesFinalSnd.clot.Clot_Challenge'
ChallengeSound(2)=SoundGroup'KF_EnemiesFinalSnd.clot.Clot_Challenge'
ChallengeSound(3)=SoundGroup'KF_EnemiesFinalSnd.clot.Clot_Challenge'
AmbientSound=Sound'KF_BaseClot.Clot_Idle1Loop'
Mesh=SkeletalMesh'KF_Freaks_Trip.CLOT_Freak'
Skins(0)=Combiner'KF_Specimens_Trip_T.clot_cmb'
|
||||
}
|
||||
27
sources/Zeds/Nice/NiceZombieClotBase.uc
Normal file
27
sources/Zeds/Nice/NiceZombieClotBase.uc
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
// Zombie Monster for KF Invasion gametype
|
||||
class NiceZombieClotBase extends NiceMonster
|
||||
abstract;
|
||||
#exec OBJ LOAD FILE=KF_Freaks_Trip.ukx
|
||||
#exec OBJ LOAD FILE=KF_Specimens_Trip_T.utx
|
||||
var KFPawn DisabledPawn; // The pawn that has been disabled by this zombie's grapple
|
||||
var bool bGrappling; // This zombie is grappling someone
|
||||
var float GrappleEndTime; // When the current grapple should be over
|
||||
var() float GrappleDuration; // How long a grapple by this zombie should last
|
||||
var float ClotGrabMessageDelay; // Amount of time between a player saying "I've been grabbed" message
|
||||
replication
|
||||
{
|
||||
reliable if(bNetDirty && Role == ROLE_Authority)
bGrappling;
|
||||
}
|
||||
function BreakGrapple()
|
||||
{
|
||||
if( DisabledPawn != none )
|
||||
{
DisabledPawn.bMovementDisabled = false;
DisabledPawn = none;
|
||||
}
|
||||
}
|
||||
//-------------------------------------------------------------------------------
|
||||
// NOTE: All Code resides in the child class(this class was only created to
|
||||
// eliminate hitching caused by loading default properties during play)
|
||||
//-------------------------------------------------------------------------------
|
||||
defaultproperties
|
||||
{
GrappleDuration=1.500000
ClotGrabMessageDelay=12.000000
fuelRatio=0.900000
clientHeadshotScale=1.500000
MeleeAnims(0)="ClotGrapple"
MeleeAnims(1)="ClotGrappleTwo"
MeleeAnims(2)="ClotGrappleThree"
bCannibal=True
MeleeDamage=6
damageForce=5000
KFRagdollName="Clot_Trip"
CrispUpThreshhold=9
PuntAnim="ClotPunt"
AdditionalWalkAnims(0)="ClotWalk2"
Intelligence=BRAINS_Mammal
bUseExtendedCollision=True
ColOffset=(Z=48.000000)
ColRadius=25.000000
ColHeight=5.000000
ExtCollAttachBoneName="Collision_Attach"
SeveredArmAttachScale=0.800000
SeveredLegAttachScale=0.800000
SeveredHeadAttachScale=0.800000
OnlineHeadshotOffset=(X=20.000000,Z=37.000000)
OnlineHeadshotScale=1.300000
MotionDetectorThreat=0.340000
ScoringValue=7
MeleeRange=20.000000
GroundSpeed=105.000000
WaterSpeed=105.000000
JumpZ=340.000000
HealthMax=130.000000
Health=130
MenuName="Nice Clot"
MovementAnims(0)="ClotWalk"
WalkAnims(0)="ClotWalk"
WalkAnims(1)="ClotWalk"
WalkAnims(2)="ClotWalk"
WalkAnims(3)="ClotWalk"
DrawScale=1.100000
PrePivot=(Z=5.000000)
RotationRate=(Yaw=45000,Roll=0)
|
||||
}
|
||||
90
sources/Zeds/Nice/NiceZombieCrawler.uc
Normal file
90
sources/Zeds/Nice/NiceZombieCrawler.uc
Normal file
|
|
@ -0,0 +1,90 @@
|
|||
// Zombie Monster for KF Invasion gametype
|
||||
class NiceZombieCrawler extends NiceZombieCrawlerBase;
|
||||
//----------------------------------------------------------------------------
|
||||
// NOTE: All Variables are declared in the base class to eliminate hitching
|
||||
//----------------------------------------------------------------------------
|
||||
function bool DoPounce()
|
||||
{
|
||||
if (bZapped || bIsCrouched || bWantsToCrouch || (Physics != PHYS_Walking) || VSize(Location - Controller.Target.Location) > (MeleeRange * 5))
return false;
|
||||
Velocity = Normal(Controller.Target.Location-Location)*PounceSpeed;
|
||||
Velocity.Z = JumpZ;
|
||||
SetPhysics(PHYS_Falling);
|
||||
ZombieSpringAnim();
|
||||
bPouncing=true;
|
||||
return true;
|
||||
}
|
||||
function TakeDamage(int Damage, Pawn InstigatedBy, Vector HitLocation, Vector Momentum, class<DamageType> DamType, optional int HitIndex)
|
||||
{
|
||||
local int OldHeadHealth;
|
||||
OldHeadHealth = HeadHealth;
|
||||
Super(NiceMonster).TakeDamage(Damage, instigatedBy, hitLocation, momentum, DamType);
|
||||
// If crawler's head was damaged, but not yet removed -- I say kill the goddamn thing
|
||||
if(HeadHealth < OldHeadHealth && HeadHealth > 0)
RemoveHead();
|
||||
}
|
||||
simulated function ZombieSpringAnim()
|
||||
{
|
||||
SetAnimAction('ZombieSpring');
|
||||
}
|
||||
event Landed(vector HitNormal)
|
||||
{
|
||||
bPouncing=false;
|
||||
super.Landed(HitNormal);
|
||||
}
|
||||
event Bump(actor Other)
|
||||
{
|
||||
// TODO: is there a better way
|
||||
if(bPouncing && KFHumanPawn(Other)!=none )
|
||||
{
KFHumanPawn(Other).TakeDamage(((MeleeDamage - (MeleeDamage * 0.05)) + (MeleeDamage * (FRand() * 0.1))), self ,self.Location,self.velocity, class 'NicePack.NiceZedMeleeDamageType');
if (KFHumanPawn(Other).Health <=0)
{
//TODO - move this to humanpawn.takedamage? Also see KFMonster.MeleeDamageTarget
KFHumanPawn(Other).SpawnGibs(self.rotation, 1);
}
//After impact, there'll be no momentum for further bumps
bPouncing=false;
|
||||
}
|
||||
}
|
||||
// Blend his attacks so he can hit you in mid air.
|
||||
simulated function int DoAnimAction( name AnimName )
|
||||
{
|
||||
if( AnimName=='InAir_Attack1' || AnimName=='InAir_Attack2' )
|
||||
{
AnimBlendParams(1, 1.0, 0.0,, FireRootBone);
PlayAnim(AnimName,, 0.0, 1);
return 1;
|
||||
}
|
||||
if( AnimName=='HitF' )
|
||||
{
AnimBlendParams(1, 1.0, 0.0,, NeckBone);
PlayAnim(AnimName,, 0.0, 1);
return 1;
|
||||
}
|
||||
if( AnimName=='ZombieSpring' )
|
||||
{
PlayAnim(AnimName,,0.02);
return 0;
|
||||
}
|
||||
return Super.DoAnimAction(AnimName);
|
||||
}
|
||||
simulated event SetAnimAction(name NewAction)
|
||||
{
|
||||
local int meleeAnimIndex;
|
||||
if( NewAction=='' )
Return;
|
||||
if(NewAction == 'Claw')
|
||||
{
meleeAnimIndex = Rand(2);
if( Physics == PHYS_Falling )
{
NewAction = MeleeAirAnims[meleeAnimIndex];
}
else
{
NewAction = meleeAnims[meleeAnimIndex];
}
|
||||
}
|
||||
ExpectingChannel = DoAnimAction(NewAction);
|
||||
if( AnimNeedsWait(NewAction) )
|
||||
{
bWaitForAnim = true;
|
||||
}
|
||||
if( Level.NetMode!=NM_Client )
|
||||
{
AnimAction = NewAction;
bResetAnimAct = True;
ResetAnimActTime = Level.TimeSeconds+0.3;
|
||||
}
|
||||
}
|
||||
// The animation is full body and should set the bWaitForAnim flag
|
||||
simulated function bool AnimNeedsWait(name TestAnim)
|
||||
{
|
||||
if( TestAnim == 'ZombieSpring' || TestAnim == 'DoorBash' )
|
||||
{
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
function bool FlipOver()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
static simulated function PreCacheMaterials(LevelInfo myLevel)
|
||||
{//should be derived and used.
|
||||
myLevel.AddPrecacheMaterial(Combiner'KF_Specimens_Trip_T.crawler_cmb');
|
||||
myLevel.AddPrecacheMaterial(Combiner'KF_Specimens_Trip_T.crawler_env_cmb');
|
||||
myLevel.AddPrecacheMaterial(Texture'KF_Specimens_Trip_T.crawler_diff');
|
||||
}
|
||||
defaultproperties
|
||||
{
stunLoopStart=0.110000
stunLoopEnd=0.570000
idleInsertFrame=0.900000
EventClasses(0)="NicePack.NiceZombieCrawler"
MoanVoice=SoundGroup'KF_EnemiesFinalSnd.Crawler.Crawler_Talk'
MeleeAttackHitSound=SoundGroup'KF_EnemiesFinalSnd.Crawler.Crawler_HitPlayer'
JumpSound=SoundGroup'KF_EnemiesFinalSnd.Crawler.Crawler_Jump'
DetachedArmClass=Class'KFChar.SeveredArmCrawler'
DetachedLegClass=Class'KFChar.SeveredLegCrawler'
DetachedHeadClass=Class'KFChar.SeveredHeadCrawler'
HitSound(0)=SoundGroup'KF_EnemiesFinalSnd.Crawler.Crawler_Pain'
DeathSound(0)=SoundGroup'KF_EnemiesFinalSnd.Crawler.Crawler_Death'
ChallengeSound(0)=SoundGroup'KF_EnemiesFinalSnd.Crawler.Crawler_Acquire'
ChallengeSound(1)=SoundGroup'KF_EnemiesFinalSnd.Crawler.Crawler_Acquire'
ChallengeSound(2)=SoundGroup'KF_EnemiesFinalSnd.Crawler.Crawler_Acquire'
ChallengeSound(3)=SoundGroup'KF_EnemiesFinalSnd.Crawler.Crawler_Acquire'
ControllerClass=Class'NicePack.NiceZombieCrawlerController'
AmbientSound=Sound'KF_BaseCrawler.Crawler_Idle'
Mesh=SkeletalMesh'KF_Freaks_Trip.Crawler_Freak'
Skins(0)=Combiner'KF_Specimens_Trip_T.crawler_cmb'
|
||||
}
|
||||
77
sources/Zeds/Nice/NiceZombieCrawlerBase.uc
Normal file
77
sources/Zeds/Nice/NiceZombieCrawlerBase.uc
Normal file
|
|
@ -0,0 +1,77 @@
|
|||
// Zombie Monster for KF Invasion gametype
|
||||
class NiceZombieCrawlerBase extends NiceMonster
|
||||
abstract;
|
||||
#exec OBJ LOAD FILE=
|
||||
var() float PounceSpeed;
|
||||
var bool bPouncing;
|
||||
var(Anims) name MeleeAirAnims[3]; // Attack anims for when flying through the air
|
||||
//-------------------------------------------------------------------------------
|
||||
// NOTE: All Code resides in the child class(this class was only created to
|
||||
// eliminate hitching caused by loading default properties during play)
|
||||
//-------------------------------------------------------------------------------
|
||||
function bool DoPounce()
|
||||
{
|
||||
if ( bZapped || bIsCrouched || bWantsToCrouch || (Physics != PHYS_Walking) || VSize(Location - Controller.Target.Location) > (MeleeRange * 5) )
return false;
|
||||
Velocity = Normal(Controller.Target.Location-Location)*PounceSpeed;
|
||||
Velocity.Z = JumpZ;
|
||||
SetPhysics(PHYS_Falling);
|
||||
ZombieSpringAnim();
|
||||
bPouncing=true;
|
||||
return true;
|
||||
}
|
||||
simulated function ZombieSpringAnim()
|
||||
{
|
||||
SetAnimAction('ZombieSpring');
|
||||
}
|
||||
event Landed(vector HitNormal)
|
||||
{
|
||||
bPouncing=false;
|
||||
super.Landed(HitNormal);
|
||||
}
|
||||
event Bump(actor Other)
|
||||
{
|
||||
// TODO: is there a better way
|
||||
if(bPouncing && KFHumanPawn(Other)!=none )
|
||||
{
KFHumanPawn(Other).TakeDamage(((MeleeDamage - (MeleeDamage * 0.05)) + (MeleeDamage * (FRand() * 0.1))), self ,self.Location,self.velocity, class 'NicePack.NiceZedMeleeDamageType');
if (KFHumanPawn(Other).Health <=0)
{
//TODO - move this to humanpawn.takedamage? Also see KFMonster.MeleeDamageTarget
KFHumanPawn(Other).SpawnGibs(self.rotation, 1);
}
//After impact, there'll be no momentum for further bumps
bPouncing=false;
|
||||
}
|
||||
}
|
||||
// Blend his attacks so he can hit you in mid air.
|
||||
simulated function int DoAnimAction( name AnimName )
|
||||
{
|
||||
if( AnimName=='InAir_Attack1' || AnimName=='InAir_Attack2' )
|
||||
{
AnimBlendParams(1, 1.0, 0.0,, FireRootBone);
PlayAnim(AnimName,, 0.0, 1);
return 1;
|
||||
}
|
||||
if( AnimName=='HitF' )
|
||||
{
AnimBlendParams(1, 1.0, 0.0,, NeckBone);
PlayAnim(AnimName,, 0.0, 1);
return 1;
|
||||
}
|
||||
if( AnimName=='ZombieSpring' )
|
||||
{
PlayAnim(AnimName,,0.02);
return 0;
|
||||
}
|
||||
return Super.DoAnimAction(AnimName);
|
||||
}
|
||||
simulated event SetAnimAction(name NewAction)
|
||||
{
|
||||
local int meleeAnimIndex;
|
||||
if( NewAction=='' )
Return;
|
||||
if(NewAction == 'Claw')
|
||||
{
meleeAnimIndex = Rand(2);
if( Physics == PHYS_Falling )
{
NewAction = MeleeAirAnims[meleeAnimIndex];
}
else
{
NewAction = meleeAnims[meleeAnimIndex];
}
|
||||
}
|
||||
ExpectingChannel = DoAnimAction(NewAction);
|
||||
if( AnimNeedsWait(NewAction) )
|
||||
{
bWaitForAnim = true;
|
||||
}
|
||||
if( Level.NetMode!=NM_Client )
|
||||
{
AnimAction = NewAction;
bResetAnimAct = True;
ResetAnimActTime = Level.TimeSeconds+0.3;
|
||||
}
|
||||
}
|
||||
// The animation is full body and should set the bWaitForAnim flag
|
||||
simulated function bool AnimNeedsWait(name TestAnim)
|
||||
{
|
||||
if( TestAnim == 'ZombieSpring' || TestAnim == 'DoorBash' )
|
||||
{
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
defaultproperties
|
||||
{
PounceSpeed=330.000000
MeleeAirAnims(0)="InAir_Attack1"
MeleeAirAnims(1)="InAir_Attack2"
StunThreshold=2.000000
fuelRatio=0.800000
bWeakHead=True
clientHeadshotScale=1.350000
MeleeAnims(0)="ZombieLeapAttack"
MeleeAnims(1)="ZombieLeapAttack2"
HitAnims(1)="HitF"
HitAnims(2)="HitF"
KFHitFront="HitF"
KFHitBack="HitF"
KFHitLeft="HitF"
KFHitRight="HitF"
bStunImmune=True
bCannibal=True
ZombieFlag=2
MeleeDamage=6
damageForce=5000
KFRagdollName="Crawler_Trip"
CrispUpThreshhold=10
Intelligence=BRAINS_Mammal
SeveredArmAttachScale=0.800000
SeveredLegAttachScale=0.850000
SeveredHeadAttachScale=1.100000
OnlineHeadshotOffset=(X=28.000000,Z=7.000000)
OnlineHeadshotScale=1.200000
MotionDetectorThreat=0.340000
ScoringValue=10
IdleHeavyAnim="ZombieLeapIdle"
IdleRifleAnim="ZombieLeapIdle"
bCrawler=True
GroundSpeed=140.000000
WaterSpeed=130.000000
JumpZ=350.000000
HealthMax=70.000000
Health=70
HeadHeight=2.500000
HeadScale=1.050000
MenuName="Nice Crawler"
bDoTorsoTwist=False
MovementAnims(0)="ZombieScuttle"
MovementAnims(1)="ZombieScuttleB"
MovementAnims(2)="ZombieScuttleL"
MovementAnims(3)="ZombieScuttleR"
WalkAnims(0)="ZombieScuttle"
WalkAnims(1)="ZombieScuttleB"
WalkAnims(2)="ZombieScuttleL"
WalkAnims(3)="ZombieScuttleR"
AirAnims(0)="ZombieSpring"
AirAnims(1)="ZombieSpring"
AirAnims(2)="ZombieSpring"
AirAnims(3)="ZombieSpring"
TakeoffAnims(0)="ZombieSpring"
TakeoffAnims(1)="ZombieSpring"
TakeoffAnims(2)="ZombieSpring"
TakeoffAnims(3)="ZombieSpring"
AirStillAnim="ZombieSpring"
TakeoffStillAnim="ZombieLeapIdle"
IdleCrouchAnim="ZombieLeapIdle"
IdleWeaponAnim="ZombieLeapIdle"
IdleRestAnim="ZombieLeapIdle"
bOrientOnSlope=True
DrawScale=1.100000
PrePivot=(Z=0.000000)
CollisionHeight=25.000000
|
||||
}
|
||||
59
sources/Zeds/Nice/NiceZombieCrawlerController.uc
Normal file
59
sources/Zeds/Nice/NiceZombieCrawlerController.uc
Normal file
|
|
@ -0,0 +1,59 @@
|
|||
class NiceZombieCrawlerController extends NiceMonsterController;
|
||||
var float LastPounceTime;
|
||||
var bool bDoneSpottedCheck;
|
||||
state ZombieHunt
|
||||
{
|
||||
event SeePlayer(Pawn SeenPlayer)
|
||||
{
if ( !bDoneSpottedCheck && PlayerController(SeenPlayer.Controller) != none )
{
// 25% chance of first player to see this Crawler saying something
if ( !KFGameType(Level.Game).bDidSpottedCrawlerMessage && FRand() < 0.25 )
{
PlayerController(SeenPlayer.Controller).Speech('AUTO', 18, "");
KFGameType(Level.Game).bDidSpottedCrawlerMessage = true;
}
|
||||
bDoneSpottedCheck = true;
}
|
||||
super.SeePlayer(SeenPlayer);
|
||||
}
|
||||
}
|
||||
function bool IsInPounceDist(actor PTarget)
|
||||
{
|
||||
local vector DistVec;
|
||||
local float time;
|
||||
local float HeightMoved;
|
||||
local float EndHeight;
|
||||
//work out time needed to reach target
|
||||
DistVec = pawn.location - PTarget.location;
|
||||
DistVec.Z=0;
|
||||
time = vsize(DistVec)/NiceZombieCrawler(pawn).PounceSpeed;
|
||||
// vertical change in that time
|
||||
//assumes downward grav only
|
||||
HeightMoved = Pawn.JumpZ*time + 0.5*pawn.PhysicsVolume.Gravity.z*time*time;
|
||||
EndHeight = pawn.Location.z +HeightMoved;
|
||||
//log(Vsize(Pawn.Location - PTarget.Location));
|
||||
|
||||
if((abs(EndHeight - PTarget.Location.Z) < Pawn.CollisionHeight + PTarget.CollisionHeight) &&
|
||||
VSize(pawn.Location - PTarget.Location) < KFMonster(pawn).MeleeRange * 5)
|
||||
return true;
|
||||
else
|
||||
return false;
|
||||
}
|
||||
function bool FireWeaponAt(Actor A)
|
||||
{
|
||||
local vector aFacing,aToB;
|
||||
local float RelativeDir;
|
||||
if ( A == none )
A = Enemy;
|
||||
if ( (A == none) || (Focus != A) )
return false;
|
||||
if(CanAttack(A))
|
||||
{
Target = A;
Monster(Pawn).RangedAttack(Target);
|
||||
}
|
||||
else
|
||||
{
//TODO - base off land time rather than launch time?
if((LastPounceTime + (4.5 - (FRand() * 3.0))) < Level.TimeSeconds )
{
aFacing=Normal(Vector(Pawn.Rotation));
// Get the vector from A to B
aToB=A.Location-Pawn.Location;
|
||||
RelativeDir = aFacing dot aToB;
if ( RelativeDir > 0.85 )
{
//Facing enemy
if(IsInPounceDist(A) )
{
if(NiceZombieCrawler(Pawn).DoPounce()==true )
LastPounceTime = Level.TimeSeconds;
}
}
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
function bool NotifyLanded(vector HitNormal)
|
||||
{
|
||||
if( NiceZombieCrawler(pawn).bPouncing )
|
||||
{
// restart pathfinding from landing location
GotoState('hunting');
return false;
|
||||
}
|
||||
else
|
||||
return super.NotifyLanded(HitNormal);
|
||||
}
|
||||
defaultproperties
|
||||
{
|
||||
}
|
||||
580
sources/Zeds/Nice/NiceZombieFleshPound.uc
Normal file
580
sources/Zeds/Nice/NiceZombieFleshPound.uc
Normal file
|
|
@ -0,0 +1,580 @@
|
|||
// Zombie Monster for KF Invasion gametype
|
||||
class NiceZombieFleshpound extends NiceZombieFleshpoundBase;
|
||||
var bool bFirstRageAttack;
|
||||
simulated function PostNetBeginPlay(){
|
||||
if(AvoidArea == none)
|
||||
AvoidArea = Spawn(class'NiceAvoidMarkerFP', self);
|
||||
if(AvoidArea != none)
|
||||
AvoidArea.InitFor(Self);
|
||||
EnableChannelNotify(1, 1);
|
||||
AnimBlendParams(1, 1.0, 0.0,, SpineBone1);
|
||||
super.PostNetBeginPlay();
|
||||
}
|
||||
function bool CanGetOutOfWay(){
|
||||
return false;
|
||||
}
|
||||
function bool IsStunPossible(){
|
||||
return false;
|
||||
}
|
||||
// This zed has been taken control of. Boost its health and speed
|
||||
function SetMindControlled(bool bNewMindControlled)
|
||||
{
|
||||
if( bNewMindControlled )
|
||||
{
|
||||
NumZCDHits++;
|
||||
|
||||
// if we hit him a couple of times, make him rage!
|
||||
if( NumZCDHits > 1 )
|
||||
{
|
||||
if( !IsInState('ChargeToMarker') )
|
||||
{
|
||||
GotoState('ChargeToMarker');
|
||||
}
|
||||
else
|
||||
{
|
||||
NumZCDHits = 1;
|
||||
if( IsInState('ChargeToMarker') )
|
||||
{
|
||||
GotoState('');
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if( IsInState('ChargeToMarker') )
|
||||
{
|
||||
GotoState('');
|
||||
}
|
||||
}
|
||||
|
||||
if( bNewMindControlled != bZedUnderControl )
|
||||
{
|
||||
SetGroundSpeed(OriginalGroundSpeed * 1.25);
|
||||
Health *= 1.25;
|
||||
HealthMax *= 1.25;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
NumZCDHits=0;
|
||||
}
|
||||
bZedUnderControl = bNewMindControlled;
|
||||
}
|
||||
// Handle the zed being commanded to move to a new location
|
||||
function GivenNewMarker()
|
||||
{
|
||||
if( bChargingPlayer && NumZCDHits > 1 )
|
||||
{
|
||||
GotoState('ChargeToMarker');
|
||||
}
|
||||
else
|
||||
{
|
||||
GotoState('');
|
||||
}
|
||||
}
|
||||
function bool CheckMiniFlinch(int flinchScore, Pawn instigatedBy, Vector hitLocation, Vector momentum, class<NiceWeaponDamageType> damageType, float headshotLevel, KFPlayerReplicationInfo KFPRI){
|
||||
if(Controller.IsInState('WaitForAnim') || flinchScore < 100)
|
||||
return false;
|
||||
return super.CheckMiniFlinch(flinchScore, instigatedBy, hitLocation, momentum, damageType, headshotLevel, KFPRI);
|
||||
}
|
||||
function ModDamage(out int Damage, Pawn instigatedBy, Vector hitLocation, Vector momentum, class<NiceWeaponDamageType> damageType, float headshotLevel, KFPlayerReplicationInfo KFPRI, optional float lockonTime){
|
||||
super.ModDamage(Damage, instigatedBy, hitLocation, momentum, damageType, headshotLevel, KFPRI);
|
||||
// Already low damage also isn't cut down
|
||||
if(Damage <= 35)
|
||||
return;
|
||||
// Melee weapons damage is cut by the same amount
|
||||
if(class<NiceDamageTypeVetBerserker>(DamageType) != none){
|
||||
Damage *= 0.75;
|
||||
return;
|
||||
}//MEANTODO
|
||||
// He takes less damage to small arms fire (non explosives)
|
||||
// Frags and LAW rockets will bring him down way faster than bullets and shells.
|
||||
if (true/* DamageType != class 'DamTypeFrag' && DamageType != class 'DamTypePipeBomb'
|
||||
&& DamageType!=class'NiceDamTypeM41AGrenade' && DamageType != class'NiceDamTypeRocket'
|
||||
&& (DamageType == none || !DamageType.default.bIsExplosive)*/)
|
||||
{
|
||||
// Don't reduce the damage so much if it's a headshot
|
||||
if(headshotLevel > 0.0){
|
||||
/*if(DamageType!= none && DamageType.default.HeadShotDamageMult >= 1.5)
|
||||
Damage *= 0.75;
|
||||
else
|
||||
Damage *= 0.5;*/
|
||||
Damage *= 0.75;
|
||||
}
|
||||
else
|
||||
Damage *= 0.5;
|
||||
}
|
||||
// double damage from handheld explosives or poison
|
||||
else if (DamageType == class 'DamTypeFrag' || DamageType == class 'DamTypePipeBomb' || DamageType == class 'DamTypeMedicNade'){
|
||||
Damage *= 2.0;
|
||||
}
|
||||
// A little extra damage from the grenade launchers, they are HE not shrapnel,
|
||||
// and its shrapnel that REALLY hurts the FP ;)//MEANTODO
|
||||
else if(/* DamageType == class'NiceDamTypeM41AGrenade'
|
||||
||*/ (DamageType != none && DamageType.default.bIsExplosive))
|
||||
Damage *= 1.25;
|
||||
if(AnimAction == 'PoundBlock')
|
||||
Damage *= BlockDamageReduction;
|
||||
if(damageType == class 'DamTypeVomit')
|
||||
Damage = 0;
|
||||
else if(damageType == class 'DamTypeBlowerThrower')
|
||||
Damage *= 0.25;
|
||||
}
|
||||
function TakeDamageClient(int Damage, Pawn InstigatedBy, Vector Hitlocation, Vector Momentum, class<NiceWeaponDamageType> damageType, float headshotLevel, float lockonTime){
|
||||
local bool bWasBurning;
|
||||
local int OldHealth;
|
||||
bWasBurning = bOnFire;
|
||||
if(LastDamagedTime < Level.TimeSeconds )
|
||||
TwoSecondDamageTotal = 0;
|
||||
LastDamagedTime = Level.TimeSeconds + 2;
|
||||
OldHealth = Health;
|
||||
// Shut off his "Device" when dead
|
||||
if(Damage >= Health)
|
||||
PostNetReceive();
|
||||
Super.TakeDamageClient(Damage, instigatedBy, hitLocation, momentum, damageType, headshotLevel, lockonTime);
|
||||
TwoSecondDamageTotal += OldHealth - Health;
|
||||
if( !bDecapitated && TwoSecondDamageTotal > RageDamageThreshold && !bChargingPlayer &&
|
||||
(!(bWasBurning && bCrispified) || bFrustrated) )
|
||||
StartChargingFP(InstigatedBy);
|
||||
}
|
||||
// changes colors on Device (notified in anim)
|
||||
simulated function DeviceGoRed()
|
||||
{
|
||||
Skins[1]=Shader'KFCharacters.FPRedBloomShader';
|
||||
}
|
||||
simulated function DeviceGoNormal()
|
||||
{
|
||||
Skins[1] = Shader'KFCharacters.FPAmberBloomShader';
|
||||
}
|
||||
function RangedAttack(Actor A){
|
||||
local NiceZombieFleshpoundController fpController;
|
||||
if(bShotAnim || Physics == PHYS_Swimming)
|
||||
return;
|
||||
else if(CanAttack(A))
|
||||
{
|
||||
fpController = NiceZombieFleshpoundController(Controller);
|
||||
if(fpController != none)
|
||||
fpController.RageFrustrationTimer = 0;
|
||||
bShotAnim = true;
|
||||
SetAnimAction('Claw');
|
||||
return;
|
||||
}
|
||||
}
|
||||
// Sets the FP in a berserk charge state until he either strikes his target, or hits timeout
|
||||
function StartChargingFP(Pawn instigatedBy){
|
||||
local float RageAnimDur;
|
||||
local NiceZombieFleshpoundController fpController;
|
||||
local NiceHumanPawn rageTarget, altRageTarget;
|
||||
if(Health <= 0)
|
||||
return;
|
||||
bFirstRageAttack = true;
|
||||
SetAnimAction('PoundRage');
|
||||
Acceleration = vect(0,0,0);
|
||||
bShotAnim = true;
|
||||
Velocity.X = 0;
|
||||
Velocity.Y = 0;
|
||||
Controller.GoToState('WaitForAnim');
|
||||
fpController = NiceZombieFleshpoundController(Controller);
|
||||
if(fpController != none)
|
||||
fpController.bUseFreezeHack = True;
|
||||
RageAnimDur = GetAnimDuration('PoundRage');
|
||||
if(fpController != none)
|
||||
fpController.SetPoundRageTimout(RageAnimDur);
|
||||
GoToState('BeginRaging');
|
||||
rageTarget = NiceHumanPawn(instigatedBy);
|
||||
altRageTarget = NiceHumanPawn(controller.focus);
|
||||
if( rageTarget != none && KFGameType(Level.Game) != none
|
||||
&& class'NiceVeterancyTypes'.static.HasSkill(NicePlayerController(rageTarget.Controller),
|
||||
class'NiceSkillCommandoPerfectExecution') ){
|
||||
KFGameType(Level.Game).DramaticEvent(1.0);
|
||||
}
|
||||
else if( altRageTarget != none && KFGameType(Level.Game) != none
|
||||
&& class'NiceVeterancyTypes'.static.HasSkill(NicePlayerController(altRageTarget.Controller),
|
||||
class'NiceSkillCommandoPerfectExecution') ){
|
||||
KFGameType(Level.Game).DramaticEvent(1.0);
|
||||
}
|
||||
}
|
||||
state BeginRaging
|
||||
{
|
||||
Ignores StartChargingFP;
|
||||
function bool CanGetOutOfWay()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
simulated function bool HitCanInterruptAction()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
function Tick( float Delta )
|
||||
{
|
||||
Acceleration = vect(0,0,0);
|
||||
|
||||
global.Tick(Delta);
|
||||
}
|
||||
Begin:
|
||||
Sleep(GetAnimDuration('PoundRage'));
|
||||
GotoState('RageCharging');
|
||||
}
|
||||
state RageCharging
|
||||
{
|
||||
Ignores StartChargingFP;
|
||||
function bool CheckMiniFlinch(int flinchScore, Pawn instigatedBy, Vector hitLocation, Vector momentum, class<NiceWeaponDamageType> damageType, float headshotLevel, KFPlayerReplicationInfo KFPRI){
|
||||
if(!bShotAnim)
|
||||
return super.CheckMiniFlinch(flinchScore, instigatedBy, hitLocation, momentum, damageType, headshotLevel, KFPRI);
|
||||
return false;
|
||||
}
|
||||
function bool CanGetOutOfWay()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
// Don't override speed in this state
|
||||
function bool CanSpeedAdjust()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
function BeginState()
|
||||
{
|
||||
bChargingPlayer = true;
|
||||
if( Level.NetMode!=NM_DedicatedServer )
|
||||
ClientChargingAnims();
|
||||
|
||||
RageEndTime = (Level.TimeSeconds + 15) + (FRand() * 18);
|
||||
NetUpdateTime = Level.TimeSeconds - 1;
|
||||
}
|
||||
function EndState()
|
||||
{
|
||||
local NiceZombieFleshpoundController fpController;
|
||||
bChargingPlayer = False;
|
||||
bFrustrated = false;
|
||||
|
||||
fpController = NiceZombieFleshPoundController(Controller);
|
||||
if(fpController == none)
|
||||
fpController.RageFrustrationTimer = 0;
|
||||
|
||||
if( Health>0 && !bZapped )
|
||||
{
|
||||
SetGroundSpeed(GetOriginalGroundSpeed());
|
||||
}
|
||||
|
||||
if( Level.NetMode!=NM_DedicatedServer )
|
||||
ClientChargingAnims();
|
||||
|
||||
NetUpdateTime = Level.TimeSeconds - 1;
|
||||
}
|
||||
function Tick( float Delta )
|
||||
{
|
||||
if( !bShotAnim )
|
||||
{
|
||||
SetGroundSpeed(OriginalGroundSpeed * 2.3);//2.0;
|
||||
if( !bFrustrated && !bZedUnderControl && Level.TimeSeconds>RageEndTime )
|
||||
{
|
||||
GoToState('');
|
||||
}
|
||||
}
|
||||
|
||||
// Keep the flesh pound moving toward its target when attacking
|
||||
if( Role == ROLE_Authority && bShotAnim)
|
||||
{
|
||||
if( LookTarget!=none )
|
||||
{
|
||||
Acceleration = AccelRate * Normal(LookTarget.Location - Location);
|
||||
}
|
||||
}
|
||||
|
||||
global.Tick(Delta);
|
||||
}
|
||||
function Bump( Actor Other )
|
||||
{
|
||||
local float RageBumpDamage;
|
||||
local KFMonster KFMonst;
|
||||
|
||||
KFMonst = KFMonster(Other);
|
||||
|
||||
// Hurt/Kill enemies that we run into while raging
|
||||
if( !bShotAnim && KFMonst!=none && NiceZombieFleshPound(Other)==none && Pawn(Other).Health>0 )
|
||||
{
|
||||
// Random chance of doing obliteration damage
|
||||
if( FRand() < 0.4 )
|
||||
{
|
||||
RageBumpDamage = 501;
|
||||
}
|
||||
else
|
||||
{
|
||||
RageBumpDamage = 450;
|
||||
}
|
||||
|
||||
RageBumpDamage *= KFMonst.PoundRageBumpDamScale;
|
||||
|
||||
Log("DAMAGE!"@String(RageBumpDamage));
|
||||
Other.TakeDamage(RageBumpDamage, self, Other.Location, Velocity * Other.Mass, class'NiceDamTypePoundCrushed');
|
||||
}
|
||||
else Global.Bump(Other);
|
||||
}
|
||||
// If fleshie hits his target on a charge, then he should settle down for abit.
|
||||
function bool MeleeDamageTarget(int hitdamage, vector pushdir)
|
||||
{
|
||||
local bool RetVal,bWasEnemy;
|
||||
|
||||
bWasEnemy = (Controller.Target==Controller.Enemy);
|
||||
RetVal = Super.MeleeDamageTarget(hitdamage*1.75, pushdir*3);
|
||||
if( RetVal && bWasEnemy )
|
||||
GoToState('');
|
||||
return RetVal;
|
||||
}
|
||||
}
|
||||
// State where the zed is charging to a marked location.
|
||||
// Not sure if we need this since its just like RageCharging,
|
||||
// but keeping it here for now in case we need to implement some
|
||||
// custom behavior for this state
|
||||
state ChargeToMarker extends RageCharging
|
||||
{
|
||||
Ignores StartChargingFP;
|
||||
function Tick( float Delta )
|
||||
{
|
||||
if( !bShotAnim )
|
||||
{
|
||||
SetGroundSpeed(OriginalGroundSpeed * 2.3);
|
||||
if( !bFrustrated && !bZedUnderControl && Level.TimeSeconds>RageEndTime )
|
||||
{
|
||||
GoToState('');
|
||||
}
|
||||
}
|
||||
|
||||
// Keep the flesh pound moving toward its target when attacking
|
||||
if( Role == ROLE_Authority && bShotAnim)
|
||||
{
|
||||
if( LookTarget!=none )
|
||||
{
|
||||
Acceleration = AccelRate * Normal(LookTarget.Location - Location);
|
||||
}
|
||||
}
|
||||
|
||||
global.Tick(Delta);
|
||||
}
|
||||
}
|
||||
simulated function PostNetReceive()
|
||||
{
|
||||
if( bClientCharge!=bChargingPlayer && !bZapped )
|
||||
{
|
||||
bClientCharge = bChargingPlayer;
|
||||
if (bChargingPlayer)
|
||||
{
|
||||
MovementAnims[0]=ChargingAnim;
|
||||
MeleeAnims[0]='FPRageAttack';
|
||||
MeleeAnims[1]='FPRageAttack';
|
||||
MeleeAnims[2]='FPRageAttack';
|
||||
DeviceGoRed();
|
||||
}
|
||||
else
|
||||
{
|
||||
MovementAnims[0]=default.MovementAnims[0];
|
||||
MeleeAnims[0]=default.MeleeAnims[0];
|
||||
MeleeAnims[1]=default.MeleeAnims[1];
|
||||
MeleeAnims[2]=default.MeleeAnims[2];
|
||||
DeviceGoNormal();
|
||||
}
|
||||
}
|
||||
}
|
||||
simulated function PlayDyingAnimation(class<DamageType> DamageType, vector HitLoc)
|
||||
{
|
||||
Super.PlayDyingAnimation(DamageType,HitLoc);
|
||||
if( Level.NetMode!=NM_DedicatedServer )
|
||||
DeviceGoNormal();
|
||||
}
|
||||
simulated function ClientChargingAnims()
|
||||
{
|
||||
PostNetReceive();
|
||||
}
|
||||
function ClawDamageTarget()
|
||||
{
|
||||
local vector PushDir;
|
||||
local KFHumanPawn HumanTarget;
|
||||
local KFPlayerController HumanTargetController;
|
||||
local float UsedMeleeDamage;
|
||||
local name Sequence;
|
||||
local float Frame, Rate;
|
||||
GetAnimParams( ExpectingChannel, Sequence, Frame, Rate );
|
||||
if( MeleeDamage > 1 )
|
||||
{
|
||||
UsedMeleeDamage = (MeleeDamage - (MeleeDamage * 0.05)) + (MeleeDamage * (FRand() * 0.1));
|
||||
}
|
||||
else
|
||||
{
|
||||
UsedMeleeDamage = MeleeDamage;
|
||||
}
|
||||
// Reduce the melee damage for anims with repeated attacks, since it does repeated damage over time
|
||||
if( Sequence == 'PoundAttack1' )
|
||||
{
|
||||
UsedMeleeDamage *= 0.5;
|
||||
}
|
||||
else if( Sequence == 'PoundAttack2' )
|
||||
{
|
||||
UsedMeleeDamage *= 0.25;
|
||||
}
|
||||
if(Controller!=none && Controller.Target!=none)
|
||||
{
|
||||
//calculate based on relative positions
|
||||
PushDir = (damageForce * Normal(Controller.Target.Location - Location));
|
||||
}
|
||||
else
|
||||
{
|
||||
//calculate based on way Monster is facing
|
||||
PushDir = damageForce * vector(Rotation);
|
||||
}
|
||||
if ( MeleeDamageTarget( UsedMeleeDamage, PushDir))
|
||||
{
|
||||
HumanTarget = KFHumanPawn(Controller.Target);
|
||||
if( HumanTarget!=none )
|
||||
HumanTargetController = KFPlayerController(HumanTarget.Controller);
|
||||
if( HumanTargetController!=none )
|
||||
HumanTargetController.ShakeView(RotMag, RotRate, RotTime, OffsetMag, OffsetRate, OffsetTime);
|
||||
PlaySound(MeleeAttackHitSound, SLOT_Interact, 1.25);
|
||||
}
|
||||
}
|
||||
function SpinDamage(actor Target)
|
||||
{
|
||||
local vector HitLocation;
|
||||
local Name TearBone;
|
||||
local Float dummy;
|
||||
local float DamageAmount;
|
||||
local vector PushDir;
|
||||
local KFHumanPawn HumanTarget;
|
||||
if(target==none)
|
||||
return;
|
||||
PushDir = (damageForce * Normal(Target.Location - Location));
|
||||
damageamount = (SpinDamConst + rand(SpinDamRand) );
|
||||
// FLING DEM DEAD BODIEZ!
|
||||
if (Target.IsA('KFHumanPawn') && Pawn(Target).Health <= DamageAmount)
|
||||
{
|
||||
KFHumanPawn(Target).RagDeathVel *= 3;
|
||||
KFHumanPawn(Target).RagDeathUpKick *= 1.5;
|
||||
}
|
||||
if (Target !=none && Target.IsA('KFDoorMover'))
|
||||
{
|
||||
Target.TakeDamage(DamageAmount , self ,HitLocation,pushdir, class 'NicePack.NiceZedMeleeDamageType');
|
||||
PlaySound(MeleeAttackHitSound, SLOT_Interact, 1.25);
|
||||
}
|
||||
if (KFHumanPawn(Target)!=none)
|
||||
{
|
||||
HumanTarget = KFHumanPawn(Target);
|
||||
if (HumanTarget.Controller != none)
|
||||
HumanTarget.Controller.ShakeView(RotMag, RotRate, RotTime, OffsetMag, OffsetRate, OffsetTime);
|
||||
|
||||
//TODO - line below was KFPawn. Does this whole block need to be KFPawn, or is it OK as KFHumanPawn?
|
||||
KFHumanPawn(Target).TakeDamage(DamageAmount, self ,HitLocation,pushdir, class 'NicePack.NiceZedMeleeDamageType');
|
||||
|
||||
if (KFHumanPawn(Target).Health <=0)
|
||||
{
|
||||
KFHumanPawn(Target).SpawnGibs(rotator(pushdir), 1);
|
||||
TearBone=KFPawn(Target).GetClosestBone(HitLocation,Velocity,dummy);
|
||||
KFHumanPawn(Controller.Target).HideBone(TearBone);
|
||||
}
|
||||
}
|
||||
}
|
||||
simulated function int DoAnimAction( name AnimName )
|
||||
{
|
||||
if( AnimName=='PoundAttack1' || AnimName=='PoundAttack2' || AnimName=='PoundAttack3'
|
||||
||AnimName=='FPRageAttack' || AnimName=='ZombieFireGun' )
|
||||
{
|
||||
AnimBlendParams(1, 1.0, 0.0,, FireRootBone);
|
||||
PlayAnim(AnimName,, 0.1, 1);
|
||||
Return 1;
|
||||
}
|
||||
Return Super.DoAnimAction(AnimName);
|
||||
}
|
||||
simulated event SetAnimAction(name NewAction)
|
||||
{
|
||||
local int meleeAnimIndex;
|
||||
if( NewAction=='' )
|
||||
Return;
|
||||
// Remove one of the attacks during fp's rage
|
||||
if(NewAction == 'Claw')
|
||||
{
|
||||
if(IsInState('RageCharging') && bFirstRageAttack){
|
||||
bFirstRageAttack = false;
|
||||
meleeAnimIndex = Rand(2);
|
||||
NewAction = meleeAnims[meleeAnimIndex+1];
|
||||
}
|
||||
else{
|
||||
meleeAnimIndex = Rand(3);
|
||||
NewAction = meleeAnims[meleeAnimIndex];
|
||||
}
|
||||
}
|
||||
ExpectingChannel = DoAnimAction(NewAction);
|
||||
if( AnimNeedsWait(NewAction) )
|
||||
{
|
||||
bWaitForAnim = true;
|
||||
}
|
||||
if( Level.NetMode!=NM_Client )
|
||||
{
|
||||
AnimAction = NewAction;
|
||||
bResetAnimAct = True;
|
||||
ResetAnimActTime = Level.TimeSeconds+0.3;
|
||||
}
|
||||
}
|
||||
// The animation is full body and should set the bWaitForAnim flag
|
||||
simulated function bool AnimNeedsWait(name TestAnim)
|
||||
{
|
||||
if( TestAnim == 'PoundRage' || TestAnim == 'DoorBash' )
|
||||
{
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
simulated function Tick(float DeltaTime)
|
||||
{
|
||||
super.Tick(DeltaTime);
|
||||
// Keep the flesh pound moving toward its target when attacking
|
||||
if( Role == ROLE_Authority && bShotAnim)
|
||||
{
|
||||
if( LookTarget!=none )
|
||||
{
|
||||
Acceleration = AccelRate * Normal(LookTarget.Location - Location);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function bool FlipOver()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
function bool SameSpeciesAs(Pawn P)
|
||||
{
|
||||
return (NiceZombieFleshPound(P)!=none);
|
||||
}
|
||||
simulated function Destroyed()
|
||||
{
|
||||
if( AvoidArea!=none )
|
||||
AvoidArea.Destroy();
|
||||
Super.Destroyed();
|
||||
}
|
||||
|
||||
static simulated function PreCacheMaterials(LevelInfo myLevel)
|
||||
{//should be derived and used.
|
||||
myLevel.AddPrecacheMaterial(Combiner'KF_Specimens_Trip_T.fleshpound_cmb');
|
||||
myLevel.AddPrecacheMaterial(Combiner'KF_Specimens_Trip_T.fleshpound_env_cmb');
|
||||
myLevel.AddPrecacheMaterial(Texture'KF_Specimens_Trip_T.fleshpound_diff');
|
||||
}
|
||||
defaultproperties
|
||||
{
|
||||
stunLoopStart=0.140000
|
||||
stunLoopEnd=0.650000
|
||||
idleInsertFrame=0.950000
|
||||
EventClasses(0)="NicePack.NiceZombieFleshpound"
|
||||
MoanVoice=SoundGroup'KF_EnemiesFinalSnd.Fleshpound.FP_Talk'
|
||||
MeleeAttackHitSound=SoundGroup'KF_EnemiesFinalSnd.Fleshpound.FP_HitPlayer'
|
||||
JumpSound=SoundGroup'KF_EnemiesFinalSnd.Fleshpound.FP_Jump'
|
||||
DetachedArmClass=Class'KFChar.SeveredArmPound'
|
||||
DetachedLegClass=Class'KFChar.SeveredLegPound'
|
||||
DetachedHeadClass=Class'KFChar.SeveredHeadPound'
|
||||
HitSound(0)=SoundGroup'KF_EnemiesFinalSnd.Fleshpound.FP_Pain'
|
||||
DeathSound(0)=SoundGroup'KF_EnemiesFinalSnd.Fleshpound.FP_Death'
|
||||
ChallengeSound(0)=SoundGroup'KF_EnemiesFinalSnd.Fleshpound.FP_Challenge'
|
||||
ChallengeSound(1)=SoundGroup'KF_EnemiesFinalSnd.Fleshpound.FP_Challenge'
|
||||
ChallengeSound(2)=SoundGroup'KF_EnemiesFinalSnd.Fleshpound.FP_Challenge'
|
||||
ChallengeSound(3)=SoundGroup'KF_EnemiesFinalSnd.Fleshpound.FP_Challenge'
|
||||
ControllerClass=Class'NicePack.NiceZombieFleshpoundController'
|
||||
AmbientSound=Sound'KF_BaseFleshpound.FP_IdleLoop'
|
||||
Mesh=SkeletalMesh'KF_Freaks_Trip.FleshPound_Freak'
|
||||
Skins(0)=Combiner'KF_Specimens_Trip_T.fleshpound_cmb'
|
||||
}
|
||||
29
sources/Zeds/Nice/NiceZombieFleshpoundBase.uc
Normal file
29
sources/Zeds/Nice/NiceZombieFleshpoundBase.uc
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
// Zombie Monster for KF Invasion gametype
|
||||
class NiceZombieFleshpoundBase extends NiceMonster
|
||||
abstract;
|
||||
var () float BlockDamageReduction;
|
||||
var bool bChargingPlayer,bClientCharge;
|
||||
var int TwoSecondDamageTotal;
|
||||
var float LastDamagedTime,RageEndTime;
|
||||
var() vector RotMag; // how far to rot view
|
||||
var() vector RotRate; // how fast to rot view
|
||||
var() float RotTime; // how much time to rot the instigator's view
|
||||
var() vector OffsetMag; // max view offset vertically
|
||||
var() vector OffsetRate; // how fast to offset view vertically
|
||||
var() float OffsetTime; // how much time to offset view
|
||||
var name ChargingAnim; // How he runs when charging the player.
|
||||
//var ONSHeadlightCorona DeviceGlow; //KFTODO: Don't think this is needed, its not reffed anywhere
|
||||
var() int RageDamageThreshold; // configurable.
|
||||
var NiceAvoidMarkerFP AvoidArea; // Make the other AI fear this AI
|
||||
var bool bFrustrated; // The fleshpound is tired of being kited and is pissed and ready to attack
|
||||
replication
|
||||
{
|
||||
reliable if(Role == ROLE_Authority)
bChargingPlayer, bFrustrated;
|
||||
}
|
||||
//-------------------------------------------------------------------------------
|
||||
// NOTE: All Code resides in the child class(this class was only created to
|
||||
// eliminate hitching caused by loading default properties during play)
|
||||
//-------------------------------------------------------------------------------
|
||||
defaultproperties
|
||||
{
BlockDamageReduction=0.400000
RotMag=(X=500.000000,Y=500.000000,Z=600.000000)
RotRate=(X=12500.000000,Y=12500.000000,Z=12500.000000)
RotTime=6.000000
OffsetMag=(X=5.000000,Y=10.000000,Z=5.000000)
OffsetRate=(X=300.000000,Y=300.000000,Z=300.000000)
OffsetTime=3.500000
ChargingAnim="PoundRun"
RageDamageThreshold=360
StunThreshold=4.000000
fuelRatio=0.150000
MeleeAnims(0)="PoundAttack1"
MeleeAnims(1)="PoundAttack2"
MeleeAnims(2)="PoundAttack3"
StunsRemaining=1
BleedOutDuration=7.000000
ZapThreshold=1.750000
ZappedDamageMod=1.250000
bHarpoonToBodyStuns=False
ZombieFlag=3
MeleeDamage=35
damageForce=15000
bFatAss=True
KFRagdollName="FleshPound_Trip"
SpinDamConst=20.000000
SpinDamRand=20.000000
bMeleeStunImmune=True
Intelligence=BRAINS_Mammal
bUseExtendedCollision=True
ColOffset=(Z=52.000000)
ColRadius=36.000000
ColHeight=35.000000
SeveredArmAttachScale=1.300000
SeveredLegAttachScale=1.200000
SeveredHeadAttachScale=1.500000
PlayerCountHealthScale=0.250000
OnlineHeadshotOffset=(X=22.000000,Z=68.000000)
OnlineHeadshotScale=1.300000
HeadHealth=700.000000
PlayerNumHeadHealthScale=0.300000
MotionDetectorThreat=5.000000
bBoss=True
ScoringValue=200
IdleHeavyAnim="PoundIdle"
IdleRifleAnim="PoundIdle"
RagDeathUpKick=100.000000
MeleeRange=55.000000
GroundSpeed=130.000000
WaterSpeed=120.000000
HealthMax=1650.000000
Health=1650
HeadHeight=2.500000
HeadScale=1.300000
MenuName="Nice Flesh Pound"
MovementAnims(0)="PoundWalk"
MovementAnims(1)="WalkB"
WalkAnims(0)="PoundWalk"
WalkAnims(1)="WalkB"
WalkAnims(2)="RunL"
WalkAnims(3)="RunR"
IdleCrouchAnim="PoundIdle"
IdleWeaponAnim="PoundIdle"
IdleRestAnim="PoundIdle"
PrePivot=(Z=0.000000)
Skins(1)=Shader'KFCharacters.FPAmberBloomShader'
Mass=600.000000
RotationRate=(Yaw=45000,Roll=0)
|
||||
}
|
||||
116
sources/Zeds/Nice/NiceZombieFleshpoundController.uc
Normal file
116
sources/Zeds/Nice/NiceZombieFleshpoundController.uc
Normal file
|
|
@ -0,0 +1,116 @@
|
|||
class NiceZombieFleshpoundController extends NiceMonsterController;
|
||||
var float RageAnimTimeout; // How long until the RageAnim is completed; Hack so the server doesn't get stuck in idle when its doing the Rage anim
|
||||
var bool bDoneSpottedCheck;
|
||||
var float RageFrustrationTimer; // Tracks how long we have been walking toward a visible enemy
|
||||
var float RageFrustrationThreshhold; // Base value for how long the FP should walk torward an enemy without reaching them before getting frustrated and raging
|
||||
simulated function PostBeginPlay(){
|
||||
super.PostBeginPlay();
|
||||
RageFrustrationTimer = 0;
|
||||
}
|
||||
function Tick(float Delta){
|
||||
local bool bSeesPlayers;
|
||||
local Controller PC;
|
||||
local KFHumanPawn Human;
|
||||
local NiceZombieFleshPound ZFP;
|
||||
super.Tick(Delta);
|
||||
bSeesPlayers = false;
|
||||
for(PC = Level.ControllerList;PC != none;PC = PC.NextController){
Human = KFHumanPawn(PC.Pawn);
if(Human != none && Human.Health > 0 && !Human.bPendingDelete && CanSee(Human)){
bSeesPlayers = true;
break;
}
|
||||
}
|
||||
if(bSeesPlayers){
if(RageFrustrationTimer < RageFrustrationThreshhold){
RageFrustrationTimer += Delta;
if(RageFrustrationTimer >= RageFrustrationThreshhold){
ZFP = NiceZombieFleshPound(Pawn);
if(ZFP != none && !ZFP.bChargingPlayer){
ZFP.StartChargingFP(Pawn(focus));
ZFP.bFrustrated = true;
}
}
}
|
||||
}
|
||||
else
RageFrustrationTimer = 0;
|
||||
}
|
||||
// Never do that, you too cool
|
||||
function GetOutOfTheWayOfShot(vector ShotDirection, vector ShotOrigin){}
|
||||
state ZombieHunt
|
||||
{
|
||||
event SeePlayer(Pawn SeenPlayer)
|
||||
{
if ( !bDoneSpottedCheck && PlayerController(SeenPlayer.Controller) != none )
{
// 25% chance of first player to see this Fleshpound saying something
if ( !KFGameType(Level.Game).bDidSpottedFleshpoundMessage && FRand() < 0.25 )
{
PlayerController(SeenPlayer.Controller).Speech('AUTO', 12, "");
KFGameType(Level.Game).bDidSpottedFleshpoundMessage = true;
}
|
||||
bDoneSpottedCheck = true;
}
|
||||
super.SeePlayer(SeenPlayer);
|
||||
}
|
||||
}
|
||||
function TimedFireWeaponAtEnemy()
|
||||
{
|
||||
if ( (Enemy == none) || FireWeaponAt(Enemy) )
SetCombatTimer();
|
||||
else
SetTimer(0.01, True);
|
||||
}
|
||||
state SpinAttack
|
||||
{
|
||||
ignores EnemyNotVisible;
|
||||
// Don't do this in this state
|
||||
function GetOutOfTheWayOfShot(vector ShotDirection, vector ShotOrigin){}
|
||||
function DoSpinDamage()
|
||||
{
local Actor A;
|
||||
//log("FLESHPOUND DOSPINDAMAGE!");
foreach CollidingActors(class'actor', A, (NiceZombieFleshpound(pawn).MeleeRange * 1.5)+pawn.CollisionRadius, pawn.Location)
NiceZombieFleshpound(pawn).SpinDamage(A);
|
||||
}
|
||||
Begin:
|
||||
WaitForAnim:
|
||||
While( KFM.bShotAnim )
|
||||
{
Sleep(0.1);
DoSpinDamage();
|
||||
}
|
||||
WhatToDoNext(152);
|
||||
if ( bSoaking )
SoakStop("STUCK IN SPINATTACK!!!");
|
||||
}
|
||||
state ZombieCharge
|
||||
{
|
||||
function bool StrafeFromDamage(float Damage, class<DamageType> DamageType, bool bFindDest)
|
||||
{
return false;
|
||||
}
|
||||
// I suspect this function causes bloats to get confused
|
||||
function bool TryStrafe(vector sideDir)
|
||||
{
return false;
|
||||
}
|
||||
function Timer()
|
||||
{
Disable('NotifyBump');
Target = Enemy;
TimedFireWeaponAtEnemy();
|
||||
}
|
||||
WaitForAnim:
|
||||
if ( Monster(Pawn).bShotAnim )
|
||||
{
Goto('Moving');
|
||||
}
|
||||
if ( !FindBestPathToward(Enemy, false,true) )
GotoState('ZombieRestFormation');
|
||||
Moving:
|
||||
MoveToward(Enemy);
|
||||
WhatToDoNext(17);
|
||||
if ( bSoaking )
SoakStop("STUCK IN CHARGING!");
|
||||
}
|
||||
// Used to set a timeout for the WaitForAnim state. This is a bit of a hack fix
|
||||
// for the FleshPound getting stuck in its idle anim on a dedicated server when it
|
||||
// is supposed to be raging. For some reason, on a dedicated server only, it
|
||||
// never gets an animend call for the PoundRage anim, instead the anim gets
|
||||
// interrupted by the PoundIdle anim. If we figure that bug out, we can
|
||||
// probably take this out in the future. But for now the fix works - Ramm
|
||||
function SetPoundRageTimout(float NewRageTimeOut)
|
||||
{
|
||||
RageAnimTimeout = NewRageTimeOut;
|
||||
}
|
||||
state WaitForAnim
|
||||
{
|
||||
Ignores SeePlayer,HearNoise,Timer,EnemyNotVisible,NotifyBump;
|
||||
// Don't do this in this state
|
||||
function GetOutOfTheWayOfShot(vector ShotDirection, vector ShotOrigin){}
|
||||
function BeginState()
|
||||
{
bUseFreezeHack = False;
|
||||
}
|
||||
// The rage anim has ended, clear the flags and let the AI do its thing
|
||||
function RageTimeout()
|
||||
{
if( bUseFreezeHack )
{
if( Pawn!=none )
{
Pawn.AccelRate = Pawn.Default.AccelRate;
Pawn.GroundSpeed = Pawn.Default.GroundSpeed;
}
bUseFreezeHack = False;
AnimEnd(0);
}
|
||||
}
|
||||
function Tick( float Delta )
|
||||
{
Global.Tick(Delta);
|
||||
if( RageAnimTimeout > 0 )
{
RageAnimTimeout -= Delta;
|
||||
if( RageAnimTimeout <= 0 )
{
RageAnimTimeout = 0;
RageTimeout();
}
}
|
||||
if( bUseFreezeHack )
{
MoveTarget = none;
MoveTimer = -1;
Pawn.Acceleration = vect(0,0,0);
Pawn.GroundSpeed = 1;
Pawn.AccelRate = 0;
}
|
||||
}
|
||||
function EndState()
|
||||
{
if( Pawn!=none )
{
Pawn.AccelRate = Pawn.Default.AccelRate;
Pawn.GroundSpeed = Pawn.Default.GroundSpeed;
}
bUseFreezeHack = False;
|
||||
}
|
||||
Begin:
|
||||
While( KFM.bShotAnim )
|
||||
{
Sleep(0.15);
|
||||
}
|
||||
WhatToDoNext(99);
|
||||
}
|
||||
defaultproperties
|
||||
{
RageFrustrationThreshhold=10.000000
|
||||
}
|
||||
139
sources/Zeds/Nice/NiceZombieGhost.uc
Normal file
139
sources/Zeds/Nice/NiceZombieGhost.uc
Normal file
|
|
@ -0,0 +1,139 @@
|
|||
// Completely invisible Stalker
|
||||
// (c) PooSH, 2014
|
||||
// used 'ClawAndMove' code from Scary Ghost's SuperStaler
|
||||
// Ported to 'NiceMonster' parent class
|
||||
class NiceZombieGhost extends NiceZombieStalker;
|
||||
// max distance squared for player to see cloacked Stalkers.
|
||||
// Beyond that distance Stalkers will appear completely invisible.
|
||||
var float CloakDistanceSqr;
|
||||
// Unclock distance squared for Commandos
|
||||
var float UncloakDistanceSqr;
|
||||
var const Material CloakMat;
|
||||
var const Material InvisibleMat;
|
||||
var const Material UncloakMat, UncloakFBMat;
|
||||
var const Material GlowFX;
|
||||
static simulated function PreCacheMaterials(LevelInfo myLevel)
|
||||
{
|
||||
myLevel.AddPrecacheMaterial(default.CloakMat);
|
||||
myLevel.AddPrecacheMaterial(default.InvisibleMat);
|
||||
myLevel.AddPrecacheMaterial(default.UncloakMat);
|
||||
myLevel.AddPrecacheMaterial(default.UncloakFBMat);
|
||||
myLevel.AddPrecacheMaterial(default.GlowFX);
|
||||
myLevel.AddPrecacheMaterial(Combiner'KF_Specimens_Trip_T.stalker_env_cmb');
|
||||
myLevel.AddPrecacheMaterial(Texture'KF_Specimens_Trip_T.stalker_diff');
|
||||
myLevel.AddPrecacheMaterial(Texture'KF_Specimens_Trip_T.stalker_spec');
|
||||
myLevel.AddPrecacheMaterial(Combiner'KF_Specimens_Trip_T.StalkerCloakOpacity_cmb');
|
||||
myLevel.AddPrecacheMaterial(Material'KF_Specimens_Trip_T.StalkerCloakEnv_rot');
|
||||
myLevel.AddPrecacheMaterial(Material'KF_Specimens_Trip_T.stalker_opacity_osc');
|
||||
myLevel.AddPrecacheMaterial(Material'KFCharacters.StalkerSkin');
|
||||
}
|
||||
|
||||
|
||||
simulated function PostNetBeginPlay()
|
||||
{
|
||||
super.PostNetBeginPlay();
|
||||
if ( LocalKFHumanPawn != none ) {
CloakDistanceSqr = fmax(CloakDistanceSqr, 3.0 * UncloakDistanceSqr * LocalKFHumanPawn.GetStalkerViewDistanceMulti());
UncloakDistanceSqr *= LocalKFHumanPawn.GetStalkerViewDistanceMulti();
|
||||
}
|
||||
}
|
||||
|
||||
function RenderOverlays(Canvas Canvas)
|
||||
{
|
||||
Canvas.SetDrawColor(0, 92, 255, 255);
|
||||
super.RenderOverlays(Canvas);
|
||||
}
|
||||
// makes Stalker invisible or glowing (for commando)
|
||||
simulated function CloakStalker()
|
||||
{
|
||||
// No cloaking if zapped
|
||||
if( bZapped )
|
||||
{
return;
|
||||
}
|
||||
if ( bSpotted ) {
if( Level.NetMode == NM_DedicatedServer )
return;
|
||||
Skins[0] = GlowFX;
Skins[1] = GlowFX;
bUnlit = true;
|
||||
}
|
||||
else if ( !bDecapitated && !bCrispified ) // No head, no cloak, honey. updated : Being charred means no cloak either :D
|
||||
{
Visibility = 1;
bCloaked = true;
|
||||
if( Level.NetMode == NM_DedicatedServer )
Return;
|
||||
Skins[0] = InvisibleMat;
Skins[1] = InvisibleMat;
bUnlit = false;
|
||||
// Invisible - no shadow
if(PlayerShadow != none)
PlayerShadow.bShadowActive = false;
if(RealTimeShadow != none)
RealTimeShadow.Destroy();
|
||||
// Remove/disallow projectors on invisible people
Projectors.Remove(0, Projectors.Length);
bAcceptsProjectors = false;
SetOverlayMaterial(Material'KFX.FBDecloakShader', 0.25, true);
|
||||
}
|
||||
}
|
||||
simulated function UnCloakStalker()
|
||||
{
|
||||
if( bZapped )
|
||||
{
return;
|
||||
}
|
||||
if( !bCrispified )
|
||||
{
LastUncloakTime = Level.TimeSeconds;
|
||||
Visibility = default.Visibility;
bCloaked = false;
bUnlit = false;
|
||||
// 25% chance of our Enemy saying something about us being invisible
if( Level.NetMode!=NM_Client && !KFGameType(Level.Game).bDidStalkerInvisibleMessage && FRand()<0.25 && Controller.Enemy!=none &&
PlayerController(Controller.Enemy.Controller)!=none )
{
PlayerController(Controller.Enemy.Controller).Speech('AUTO', 17, "");
KFGameType(Level.Game).bDidStalkerInvisibleMessage = true;
}
if( Level.NetMode == NM_DedicatedServer )
Return;
|
||||
if ( Skins[0] != UncloakMat )
{
Skins[1] = FinalBlend'KF_Specimens_Trip_T.stalker_fb';
Skins[0] = UncloakMat;
|
||||
if (PlayerShadow != none)
PlayerShadow.bShadowActive = true;
|
||||
bAcceptsProjectors = true;
|
||||
SetOverlayMaterial(Material'KFX.FBDecloakShader', 0.25, true);
}
|
||||
}
|
||||
}
|
||||
function RemoveHead()
|
||||
{
|
||||
Super(NiceMonster).RemoveHead();
|
||||
if (!bCrispified)
|
||||
{
Skins[1] = UncloakFBMat;
Skins[0] = UncloakMat;
|
||||
}
|
||||
}
|
||||
simulated function PlayDying(class<DamageType> DamageType, vector HitLoc)
|
||||
{
|
||||
Super(NiceMonster).PlayDying(DamageType,HitLoc);
|
||||
if(bUnlit)
bUnlit=!bUnlit;
|
||||
LocalKFHumanPawn = none;
|
||||
if (!bCrispified)
|
||||
{
Skins[1] = UncloakFBMat;
Skins[0] = UncloakMat;
|
||||
}
|
||||
}
|
||||
|
||||
simulated function Tick(float DeltaTime)
|
||||
{
|
||||
local float DistanceSqr;
|
||||
Super(NiceMonster).Tick(DeltaTime);
|
||||
// Keep the stalker moving toward its target when attacking
|
||||
if( Role == ROLE_Authority && bShotAnim && !bWaitForAnim && !bZapped ) {
if( LookTarget!=none ) {
Acceleration = AccelRate * Normal(LookTarget.Location - Location);
}
|
||||
}
|
||||
if( Level.NetMode==NM_DedicatedServer )
Return; // Servers aren't intrested in this info.
|
||||
if( bZapped ) {
// Make sure we check if we need to be cloaked as soon as the zap wears off
NextCheckTime = Level.TimeSeconds;
|
||||
}
|
||||
else if( Level.TimeSeconds > NextCheckTime && Health > 0 )
|
||||
{
NextCheckTime = Level.TimeSeconds + 0.5;
|
||||
bSpotted = false;
if ( LocalKFHumanPawn != none ) {
DistanceSqr = VSizeSquared(Location - LocalKFHumanPawn.Location);
if( LocalKFHumanPawn.Health > 0 && LocalKFHumanPawn.ShowStalkers()
&& DistanceSqr < UncloakDistanceSqr )
{
bSpotted = True;
if ( Skins[0] != GlowFX ) {
Skins[0] = GlowFX;
Skins[1] = GlowFX;
bUnlit = true; }
}
else if ( DistanceSqr < CloakDistanceSqr ) {
if ( bCloaked && Skins[0] != CloakMat ) {
Skins[0] = CloakMat;
Skins[1] = CloakMat;
bUnlit = false;
}
}
else if ( Skins[0] != InvisibleMat ) {
CloakStalker();
}
}
|
||||
}
|
||||
}
|
||||
|
||||
function RangedAttack(Actor A)
|
||||
{
|
||||
if ( !bShotAnim && Physics != PHYS_Swimming && CanAttack(A) ) {
bShotAnim = true;
SetAnimAction('ClawAndMove');
|
||||
}
|
||||
}
|
||||
// copied from ZombieSuperStalker (c) Scary Ghost
|
||||
simulated event SetAnimAction(name NewAction)
|
||||
{
|
||||
if( NewAction=='' )
Return;
|
||||
ExpectingChannel = AttackAndMoveDoAnimAction(NewAction);
|
||||
bWaitForAnim= false;
|
||||
if( Level.NetMode!=NM_Client ) {
AnimAction = NewAction;
bResetAnimAct = True;
ResetAnimActTime = Level.TimeSeconds+0.3;
|
||||
}
|
||||
}
|
||||
// copied from ZombieSuperStalker (c) Scary Ghost
|
||||
simulated function int AttackAndMoveDoAnimAction( name AnimName )
|
||||
{
|
||||
local int meleeAnimIndex;
|
||||
local float duration;
|
||||
if( AnimName == 'ClawAndMove' ) {
meleeAnimIndex = Rand(3);
AnimName = meleeAnims[meleeAnimIndex];
|
||||
duration= GetAnimDuration(AnimName, 1.0);
|
||||
}
|
||||
if( AnimName=='StalkerSpinAttack' || AnimName=='StalkerAttack1' || AnimName=='JumpAttack') {
AnimBlendParams(1, 1.0, 0.0,, FireRootBone);
PlayAnim(AnimName,, 0.1, 1);
|
||||
return 1;
|
||||
}
|
||||
return super.DoAnimAction( AnimName );
|
||||
}
|
||||
defaultproperties
|
||||
{
CloakDistanceSqr=62500.000000
UncloakDistanceSqr=360000.000000
CloakMat=Shader'KF_Specimens_Trip_T.stalker_invisible'
InvisibleMat=Shader'KF_Specimens_Trip_T.patriarch_invisible'
UncloakMat=Shader'KF_Specimens_Trip_T.stalker_invisible'
UncloakFBMat=Shader'KF_Specimens_Trip_T.stalker_invisible'
MenuName="Ghost"
Skins(0)=Shader'KF_Specimens_Trip_T.patriarch_invisible'
Skins(1)=Shader'KF_Specimens_Trip_T.patriarch_invisible'
|
||||
}
|
||||
18
sources/Zeds/Nice/NiceZombieGoreFastBase.uc
Normal file
18
sources/Zeds/Nice/NiceZombieGoreFastBase.uc
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
// Zombie Monster for KF Invasion gametype
|
||||
// He's speedy, and swings with a Single enlongated arm, affording him slightly more range
|
||||
class NiceZombieGoreFastBase extends NiceMonster
|
||||
abstract;
|
||||
#exec OBJ LOAD FILE=
|
||||
var bool bRunning;
|
||||
var float RunAttackTimeout;
|
||||
replication
|
||||
{
|
||||
reliable if(Role == ROLE_Authority)
bRunning;
|
||||
}
|
||||
//-------------------------------------------------------------------------------
|
||||
// NOTE: All Code resides in the child class(this class was only created to
|
||||
// eliminate hitching caused by loading default properties during play)
|
||||
//-------------------------------------------------------------------------------
|
||||
defaultproperties
|
||||
{
fuelRatio=0.650000
clientHeadshotScale=1.600000
MeleeAnims(0)="GoreAttack1"
MeleeAnims(1)="GoreAttack2"
MeleeAnims(2)="GoreAttack1"
bCannibal=True
MeleeDamage=15
damageForce=5000
KFRagdollName="GoreFast_Trip"
CrispUpThreshhold=8
bUseExtendedCollision=True
ColOffset=(Z=52.000000)
ColRadius=25.000000
ColHeight=10.000000
ExtCollAttachBoneName="Collision_Attach"
SeveredArmAttachScale=0.900000
SeveredLegAttachScale=0.900000
PlayerCountHealthScale=0.150000
OnlineHeadshotOffset=(X=5.000000,Z=53.000000)
OnlineHeadshotScale=1.500000
MotionDetectorThreat=0.500000
ScoringValue=12
IdleHeavyAnim="GoreIdle"
IdleRifleAnim="GoreIdle"
MeleeRange=30.000000
GroundSpeed=120.000000
WaterSpeed=140.000000
HealthMax=250.000000
Health=250
HeadHeight=2.500000
HeadScale=1.500000
MenuName="Nice Gorefast"
MovementAnims(0)="GoreWalk"
WalkAnims(0)="GoreWalk"
WalkAnims(1)="GoreWalk"
WalkAnims(2)="GoreWalk"
WalkAnims(3)="GoreWalk"
IdleCrouchAnim="GoreIdle"
IdleWeaponAnim="GoreIdle"
IdleRestAnim="GoreIdle"
DrawScale=1.200000
PrePivot=(Z=10.000000)
Mass=350.000000
RotationRate=(Yaw=45000,Roll=0)
|
||||
}
|
||||
153
sources/Zeds/Nice/NiceZombieGorefast.uc
Normal file
153
sources/Zeds/Nice/NiceZombieGorefast.uc
Normal file
|
|
@ -0,0 +1,153 @@
|
|||
// Zombie Monster for KF Invasion gametype
|
||||
// GOREFAST.
|
||||
// He's speedy, and swings with a Single enlongated arm, affording him slightly more range
|
||||
class NiceZombieGoreFast extends NiceZombieGoreFastBase;
|
||||
#exec OBJ LOAD FILE=
|
||||
//----------------------------------------------------------------------------
|
||||
// NOTE: All Variables are declared in the base class to eliminate hitching
|
||||
//----------------------------------------------------------------------------
|
||||
simulated function PostNetReceive(){
|
||||
if(bRunning)
MovementAnims[0] = 'ZombieRun';
|
||||
else
MovementAnims[0] = default.MovementAnims[0];
|
||||
}
|
||||
// This zed has been taken control of. Boost its health and speed
|
||||
function SetMindControlled(bool bNewMindControlled)
|
||||
{
|
||||
if( bNewMindControlled )
|
||||
{
NumZCDHits++;
|
||||
// if we hit him a couple of times, make him rage!
if( NumZCDHits > 1 )
{
if( !IsInState('RunningToMarker') )
{
GotoState('RunningToMarker');
}
else
{
NumZCDHits = 1;
if( IsInState('RunningToMarker') )
{
GotoState('');
}
}
}
else
{
if( IsInState('RunningToMarker') )
{
GotoState('');
}
}
|
||||
if( bNewMindControlled != bZedUnderControl )
{
SetGroundSpeed(OriginalGroundSpeed * 1.25);
Health *= 1.25;
HealthMax *= 1.25;
}
|
||||
}
|
||||
else
|
||||
{
NumZCDHits=0;
|
||||
}
|
||||
bZedUnderControl = bNewMindControlled;
|
||||
}
|
||||
// Handle the zed being commanded to move to a new location
|
||||
function GivenNewMarker()
|
||||
{
|
||||
if( bRunning && NumZCDHits > 1 )
|
||||
{
GotoState('RunningToMarker');
|
||||
}
|
||||
else
|
||||
{
GotoState('');
|
||||
}
|
||||
}
|
||||
function RangedAttack(Actor A){
|
||||
super.RangedAttack(A);
|
||||
if(!bShotAnim && !bDecapitated && VSize(A.Location - Location) <= 700)
GoToState('RunningState');
|
||||
}
|
||||
simulated function Tick(float DeltaTime){
|
||||
super.Tick(DeltaTime);
|
||||
if(IsInState('RunningState'))
SetGroundSpeed(GetOriginalGroundSpeed() * 1.875);
|
||||
else
SetGroundSpeed(GetOriginalGroundSpeed());
|
||||
}
|
||||
state RunningState{
|
||||
// Set the zed to the zapped behavior
|
||||
simulated function SetZappedBehavior(){
Global.SetZappedBehavior();
GoToState('');
|
||||
}
|
||||
// Don't override speed in this state
|
||||
function bool CanSpeedAdjust(){
return false;
|
||||
}
|
||||
simulated function BeginState(){
if(bZapped)
GoToState('');
else{
SetGroundSpeed(OriginalGroundSpeed * 1.875);
bRunning = true;
if(Level.NetMode != NM_DedicatedServer)
PostNetReceive();
|
||||
NetUpdateTime = Level.TimeSeconds - 1;
}
|
||||
}
|
||||
function EndState(){
SetGroundSpeed(GetOriginalGroundSpeed());
bRunning = false;
if(Level.NetMode != NM_DedicatedServer)
PostNetReceive();
|
||||
RunAttackTimeout=0;
|
||||
NetUpdateTime = Level.TimeSeconds - 1;
|
||||
}
|
||||
function RemoveHead(){
GoToState('');
Global.RemoveHead();
|
||||
}
|
||||
function RangedAttack(Actor A){
|
||||
if(bShotAnim || Physics == PHYS_Swimming)
return;
else if (CanAttack(A)){
bShotAnim = true;
|
||||
// Randomly do a moving attack so the player can't kite the zed
if(FRand() < 0.4){
SetAnimAction('ClawAndMove');
RunAttackTimeout = GetAnimDuration('GoreAttack1', 1.0);
}
else{
SetAnimAction('Claw');
Controller.bPreparingMove = true;
Acceleration = vect(0,0,0);
// Once we attack stop running
GoToState('');
}
return;
}
|
||||
}
|
||||
simulated function Tick(float DeltaTime){
// Keep moving toward the target until the timer runs out (anim finishes)
if(RunAttackTimeout > 0){
RunAttackTimeout -= DeltaTime;
|
||||
if(RunAttackTimeout <= 0 && !bZedUnderControl){
RunAttackTimeout = 0;
GoToState('');
}
}
|
||||
// Keep the gorefast moving toward its target when attacking
if(Role == ROLE_Authority && bShotAnim && !bWaitForAnim){
if(LookTarget != none)
Acceleration = AccelRate * Normal(LookTarget.Location - Location);
}
global.Tick(DeltaTime);
|
||||
}
|
||||
Begin:
|
||||
GoTo('CheckCharge');
|
||||
CheckCharge:
|
||||
if( Controller!=none && Controller.Target!=none && VSize(Controller.Target.Location-Location)<700 )
|
||||
{
Sleep(0.5+ FRand() * 0.5);
//log("Still charging");
GoTo('CheckCharge');
|
||||
}
|
||||
else
|
||||
{
//log("Done charging");
GoToState('');
|
||||
}
|
||||
}
|
||||
// State where the zed is charging to a marked location.
|
||||
state RunningToMarker extends RunningState
|
||||
{
|
||||
simulated function Tick(float DeltaTime)
|
||||
{
// Keep moving toward the target until the timer runs out (anim finishes)
if( RunAttackTimeout > 0 )
{
RunAttackTimeout -= DeltaTime;
|
||||
if( RunAttackTimeout <= 0 && !bZedUnderControl )
{
RunAttackTimeout = 0;
GoToState('');
}
}
|
||||
// Keep the gorefast moving toward its target when attacking
if( Role == ROLE_Authority && bShotAnim && !bWaitForAnim )
{
if( LookTarget!=none )
{
Acceleration = AccelRate * Normal(LookTarget.Location - Location);
}
}
|
||||
global.Tick(DeltaTime);
|
||||
}
|
||||
|
||||
Begin:
|
||||
GoTo('CheckCharge');
|
||||
CheckCharge:
|
||||
if( bZedUnderControl || (Controller!=none && Controller.Target!=none && VSize(Controller.Target.Location-Location)<700) )
|
||||
{
Sleep(0.5+ FRand() * 0.5);
GoTo('CheckCharge');
|
||||
}
|
||||
else
|
||||
{
GoToState('');
|
||||
}
|
||||
}
|
||||
// Overridden to handle playing upper body only attacks when moving
|
||||
simulated event SetAnimAction(name NewAction)
|
||||
{
|
||||
local int meleeAnimIndex;
|
||||
local bool bWantsToAttackAndMove;
|
||||
if( NewAction=='' )
Return;
|
||||
bWantsToAttackAndMove = NewAction == 'ClawAndMove';
|
||||
if( NewAction == 'Claw' )
|
||||
{
meleeAnimIndex = Rand(3);
NewAction = meleeAnims[meleeAnimIndex];
|
||||
}
|
||||
if( bWantsToAttackAndMove )
|
||||
{
ExpectingChannel = AttackAndMoveDoAnimAction(NewAction);
|
||||
}
|
||||
else
|
||||
{
ExpectingChannel = DoAnimAction(NewAction);
|
||||
}
|
||||
if( !bWantsToAttackAndMove && AnimNeedsWait(NewAction) )
|
||||
{
bWaitForAnim = true;
|
||||
}
|
||||
else
|
||||
{
bWaitForAnim = false;
|
||||
}
|
||||
if( Level.NetMode!=NM_Client )
|
||||
{
AnimAction = NewAction;
bResetAnimAct = True;
ResetAnimActTime = Level.TimeSeconds+0.3;
|
||||
}
|
||||
}
|
||||
// Handle playing the anim action on the upper body only if we're attacking and moving
|
||||
simulated function int AttackAndMoveDoAnimAction( name AnimName )
|
||||
{
|
||||
local int meleeAnimIndex;
|
||||
if( AnimName == 'ClawAndMove' )
|
||||
{
meleeAnimIndex = Rand(3);
AnimName = meleeAnims[meleeAnimIndex];
|
||||
}
|
||||
if( AnimName=='GoreAttack1' || AnimName=='GoreAttack2' )
|
||||
{
AnimBlendParams(1, 1.0, 0.0,, FireRootBone);
PlayAnim(AnimName,, 0.1, 1);
|
||||
return 1;
|
||||
}
|
||||
return super.DoAnimAction( AnimName );
|
||||
}
|
||||
simulated function HideBone(name boneName)
|
||||
{
|
||||
// Gorefast does not have a left arm and does not need it to be hidden
|
||||
if (boneName != LeftFArmBone)
|
||||
{
super.HideBone(boneName);
|
||||
}
|
||||
}
|
||||
static simulated function PreCacheMaterials(LevelInfo myLevel)
|
||||
{//should be derived and used.
|
||||
myLevel.AddPrecacheMaterial(Combiner'KF_Specimens_Trip_T.gorefast_cmb');
|
||||
myLevel.AddPrecacheMaterial(Combiner'KF_Specimens_Trip_T.gorefast_env_cmb');
|
||||
myLevel.AddPrecacheMaterial(Texture'KF_Specimens_Trip_T.gorefast_diff');
|
||||
}
|
||||
defaultproperties
|
||||
{
stunLoopStart=0.287500
stunLoopEnd=0.637500
idleInsertFrame=0.750000
EventClasses(0)="NicePack.NiceZombieGorefast"
MoanVoice=SoundGroup'KF_EnemiesFinalSnd.GoreFast.Gorefast_Talk'
MeleeAttackHitSound=SoundGroup'KF_EnemiesFinalSnd.GoreFast.Gorefast_HitPlayer'
JumpSound=SoundGroup'KF_EnemiesFinalSnd.GoreFast.Gorefast_Jump'
DetachedArmClass=Class'KFChar.SeveredArmGorefast'
DetachedLegClass=Class'KFChar.SeveredLegGorefast'
DetachedHeadClass=Class'KFChar.SeveredHeadGorefast'
bLeftArmGibbed=True
HitSound(0)=SoundGroup'KF_EnemiesFinalSnd.GoreFast.Gorefast_Pain'
DeathSound(0)=SoundGroup'KF_EnemiesFinalSnd.GoreFast.Gorefast_Death'
ChallengeSound(0)=SoundGroup'KF_EnemiesFinalSnd.GoreFast.Gorefast_Challenge'
ChallengeSound(1)=SoundGroup'KF_EnemiesFinalSnd.GoreFast.Gorefast_Challenge'
ChallengeSound(2)=SoundGroup'KF_EnemiesFinalSnd.GoreFast.Gorefast_Challenge'
ChallengeSound(3)=SoundGroup'KF_EnemiesFinalSnd.GoreFast.Gorefast_Challenge'
ControllerClass=Class'NicePack.NiceZombieGorefastController'
AmbientSound=Sound'KF_BaseGorefast.Gorefast_Idle'
Mesh=SkeletalMesh'KF_Freaks_Trip.GoreFast_Freak'
Skins(0)=Combiner'KF_Specimens_Trip_T.gorefast_cmb'
|
||||
}
|
||||
10
sources/Zeds/Nice/NiceZombieGorefastController.uc
Normal file
10
sources/Zeds/Nice/NiceZombieGorefastController.uc
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
// Custom code to make the Gorefast act abit more interesting.
|
||||
class NiceZombieGorefastController extends NiceMonsterController;
|
||||
var bool bDoneSpottedCheck;
|
||||
state ZombieHunt{
|
||||
event SeePlayer(Pawn SeenPlayer){
if(!bDoneSpottedCheck && PlayerController(SeenPlayer.Controller) != none){
// 25% chance of first player to see this Gorefast saying something
if(!KFGameType(Level.Game).bDidSpottedGorefastMessage && FRand() < 0.25){
PlayerController(SeenPlayer.Controller).Speech('AUTO', 13, "");
KFGameType(Level.Game).bDidSpottedGorefastMessage = true;
}
bDoneSpottedCheck = true;
}
global.SeePlayer(SeenPlayer);
|
||||
}
|
||||
}
|
||||
defaultproperties
|
||||
{
StrafingAbility=0.500000
|
||||
}
|
||||
383
sources/Zeds/Nice/NiceZombieHusk.uc
Normal file
383
sources/Zeds/Nice/NiceZombieHusk.uc
Normal file
|
|
@ -0,0 +1,383 @@
|
|||
//=============================================================================
|
||||
// ZombieHusk
|
||||
//=============================================================================
|
||||
// Husk burned up fire projectile launching zed pawn class
|
||||
//=============================================================================
|
||||
// Killing Floor Source
|
||||
// Copyright (C) 2009 Tripwire Interactive LLC
|
||||
// - John "Ramm-Jaeger" Gibson
|
||||
//=============================================================================
|
||||
class NiceZombieHusk extends NiceZombieHuskBase;
|
||||
//----------------------------------------------------------------------------
|
||||
// NOTE: All Variables are declared in the base class to eliminate hitching
|
||||
//----------------------------------------------------------------------------
|
||||
var class<Projectile> HuskFireProjClass;
|
||||
simulated function HeatTick(){
|
||||
if(heat < 50){
|
||||
if(heat > 40)
|
||||
heat = 50;
|
||||
else
|
||||
heat = 50 - 0.8 * (50 - heat);
|
||||
}
|
||||
super.HeatTick();
|
||||
}
|
||||
simulated function PostBeginPlay()
|
||||
{
|
||||
// Difficulty Scaling
|
||||
if (Level.Game != none && !bDiffAdjusted){
|
||||
ProjectileFireInterval = default.ProjectileFireInterval * 0.6;
|
||||
}
|
||||
super.PostBeginPlay();
|
||||
}
|
||||
// don't interrupt the bloat while he is puking
|
||||
simulated function bool HitCanInterruptAction()
|
||||
{
|
||||
if( bShotAnim )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
function DoorAttack(Actor A)
|
||||
{
|
||||
if ( bShotAnim || Physics == PHYS_Swimming)
|
||||
return;
|
||||
else if ( A!=none )
|
||||
{
|
||||
bShotAnim = true;
|
||||
if( !bDecapitated && bDistanceAttackingDoor )
|
||||
{
|
||||
SetAnimAction('ShootBurns');
|
||||
}
|
||||
else
|
||||
{
|
||||
SetAnimAction('DoorBash');
|
||||
GotoState('DoorBashing');
|
||||
}
|
||||
}
|
||||
}
|
||||
function RangedAttack(Actor A)
|
||||
{
|
||||
local int LastFireTime;
|
||||
if ( bShotAnim )
|
||||
return;
|
||||
if ( Physics == PHYS_Swimming )
|
||||
{
|
||||
SetAnimAction('Claw');
|
||||
bShotAnim = true;
|
||||
LastFireTime = Level.TimeSeconds;
|
||||
}
|
||||
else if ( VSize(A.Location - Location) < MeleeRange + CollisionRadius + A.CollisionRadius )
|
||||
{
|
||||
bShotAnim = true;
|
||||
LastFireTime = Level.TimeSeconds;
|
||||
SetAnimAction('Claw');
|
||||
//PlaySound(sound'Claw2s', SLOT_Interact); KFTODO: Replace this
|
||||
Controller.bPreparingMove = true;
|
||||
Acceleration = vect(0,0,0);
|
||||
}
|
||||
else if ( (KFDoorMover(A) != none ||
|
||||
(!Region.Zone.bDistanceFog && VSize(A.Location-Location) <= 65535) ||
|
||||
(Region.Zone.bDistanceFog && VSizeSquared(A.Location-Location) < (Square(Region.Zone.DistanceFogEnd) * 0.8))) // Make him come out of the fog a bit
|
||||
&& !bDecapitated )
|
||||
{
|
||||
bShotAnim = true;
|
||||
|
||||
SetAnimAction('ShootBurns');
|
||||
Controller.bPreparingMove = true;
|
||||
Acceleration = vect(0,0,0);
|
||||
|
||||
NextFireProjectileTime = Level.TimeSeconds + ProjectileFireInterval + (FRand() * 2.0);
|
||||
}
|
||||
}
|
||||
// Overridden to handle playing upper body only attacks when moving
|
||||
simulated event SetAnimAction(name NewAction)
|
||||
{
|
||||
local int meleeAnimIndex;
|
||||
local bool bWantsToAttackAndMove;
|
||||
if( NewAction=='' )
|
||||
Return;
|
||||
if( NewAction == 'Claw' )
|
||||
{
|
||||
meleeAnimIndex = Rand(3);
|
||||
NewAction = meleeAnims[meleeAnimIndex];
|
||||
}
|
||||
ExpectingChannel = DoAnimAction(NewAction);
|
||||
if( !bWantsToAttackAndMove && AnimNeedsWait(NewAction) )
|
||||
{
|
||||
bWaitForAnim = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
bWaitForAnim = false;
|
||||
}
|
||||
if( Level.NetMode!=NM_Client )
|
||||
{
|
||||
AnimAction = NewAction;
|
||||
bResetAnimAct = True;
|
||||
ResetAnimActTime = Level.TimeSeconds+0.3;
|
||||
}
|
||||
}
|
||||
function float GetStunDurationMult(Pawn instigatedBy, Vector hitLocation, Vector momentum, class<NiceWeaponDamageType> damageType,
|
||||
float headshotLevel, KFPlayerReplicationInfo KFPRI){
|
||||
if(headshotLevel > 0)
|
||||
return 1.0;
|
||||
return 0.5;
|
||||
}
|
||||
|
||||
function SpawnTwoShots(){
|
||||
local vector X,Y,Z, FireStart;
|
||||
local rotator FireRotation;
|
||||
local KFMonsterController KFMonstControl;
|
||||
if(controller != none && KFDoorMover(controller.Target) != none){
|
||||
controller.Target.TakeDamage(22,Self,Location,vect(0,0,0),Class'DamTypeVomit');
|
||||
return;
|
||||
}
|
||||
GetAxes(Rotation,X,Y,Z);
|
||||
FireStart = GetBoneCoords('Barrel').Origin;
|
||||
if(!SavedFireProperties.bInitialized){
|
||||
SavedFireProperties.AmmoClass = Class'SkaarjAmmo';
|
||||
SavedFireProperties.ProjectileClass = HuskFireProjClass;
|
||||
SavedFireProperties.WarnTargetPct = 1;
|
||||
SavedFireProperties.MaxRange = 65535;
|
||||
SavedFireProperties.bTossed = false;
|
||||
SavedFireProperties.bTrySplash = true;
|
||||
SavedFireProperties.bLeadTarget = true;
|
||||
SavedFireProperties.bInstantHit = false;
|
||||
SavedFireProperties.bInitialized = true;
|
||||
}
|
||||
// Turn off extra collision before spawning vomit, otherwise spawn fails
|
||||
ToggleAuxCollision(false);
|
||||
if(controller != none)
|
||||
FireRotation = controller.AdjustAim(SavedFireProperties,FireStart,600);
|
||||
foreach DynamicActors(class'KFMonsterController', KFMonstControl)
|
||||
if(KFMonstControl != controller && PointDistToLine(KFMonstControl.Pawn.Location, vector(FireRotation), FireStart) < 75)
|
||||
KFMonstControl.GetOutOfTheWayOfShot(vector(FireRotation),FireStart);
|
||||
Spawn(HuskFireProjClass,,, FireStart, FireRotation);
|
||||
// Turn extra collision back on
|
||||
ToggleAuxCollision(true);
|
||||
}
|
||||
// Get the closest point along a line to another point
|
||||
simulated function float PointDistToLine(vector Point, vector Line, vector Origin, optional out vector OutClosestPoint)
|
||||
{
|
||||
local vector SafeDir;
|
||||
SafeDir = Normal(Line);
|
||||
OutClosestPoint = Origin + (SafeDir * ((Point-Origin) dot SafeDir));
|
||||
return VSize(OutClosestPoint-Point);
|
||||
}
|
||||
simulated function Tick(float deltatime)
|
||||
{
|
||||
Super.tick(deltatime);
|
||||
// Hack to force animation updates on the server for the bloat if he is relevant to someone
|
||||
// He has glitches when some of his animations don't play on the server. If we
|
||||
// find some other fix for the glitches take this out - Ramm
|
||||
if( Level.NetMode != NM_Client && Level.NetMode != NM_Standalone )
|
||||
{
|
||||
if( (Level.TimeSeconds-LastSeenOrRelevantTime) < 1.0 )
|
||||
{
|
||||
bForceSkelUpdate=true;
|
||||
}
|
||||
else
|
||||
{
|
||||
bForceSkelUpdate=false;
|
||||
}
|
||||
}
|
||||
}
|
||||
function RemoveHead()
|
||||
{
|
||||
bCanDistanceAttackDoors = False;
|
||||
Super.RemoveHead();
|
||||
}
|
||||
function ModDamage(out int damage, Pawn instigatedBy, Vector hitlocation, Vector momentum, class<NiceWeaponDamageType> damageType, float headshotLevel, KFPlayerReplicationInfo KFPRI, optional float lockonTime){
|
||||
local float damageMod;
|
||||
if(damageType != none){
|
||||
damageMod = 0.2 * damageType.default.freezePower;
|
||||
damageMod = FMax(damageMod, 1.0);
|
||||
}
|
||||
damage *= damageMod;
|
||||
Super.ModDamage(damage, instigatedBy, hitlocation, momentum, damageType, headshotLevel, KFPRI);
|
||||
}
|
||||
simulated function ProcessHitFX()
|
||||
{
|
||||
local Coords boneCoords;
|
||||
local class<xEmitter> HitEffects[4];
|
||||
local int i,j;
|
||||
local float GibPerterbation;
|
||||
if( (Level.NetMode == NM_DedicatedServer) || bSkeletized || (Mesh == SkeletonMesh))
|
||||
{
|
||||
SimHitFxTicker = HitFxTicker;
|
||||
return;
|
||||
}
|
||||
for ( SimHitFxTicker = SimHitFxTicker; SimHitFxTicker != HitFxTicker; SimHitFxTicker = (SimHitFxTicker + 1) % ArrayCount(HitFX) )
|
||||
{
|
||||
j++;
|
||||
if ( j > 30 )
|
||||
{
|
||||
SimHitFxTicker = HitFxTicker;
|
||||
return;
|
||||
}
|
||||
|
||||
if( (HitFX[SimHitFxTicker].damtype == none) || (Level.bDropDetail && (Level.TimeSeconds - LastRenderTime > 3) && !IsHumanControlled()) )
|
||||
continue;
|
||||
|
||||
//log("Processing effects for damtype "$HitFX[SimHitFxTicker].damtype);
|
||||
|
||||
if( HitFX[SimHitFxTicker].bone == 'obliterate' && !class'GameInfo'.static.UseLowGore())
|
||||
{
|
||||
SpawnGibs( HitFX[SimHitFxTicker].rotDir, 1);
|
||||
bGibbed = true;
|
||||
// Wait a tick on a listen server so the obliteration can replicate before the pawn is destroyed
|
||||
if( Level.NetMode == NM_ListenServer )
|
||||
{
|
||||
bDestroyNextTick = true;
|
||||
TimeSetDestroyNextTickTime = Level.TimeSeconds;
|
||||
}
|
||||
else
|
||||
{
|
||||
Destroy();
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
boneCoords = GetBoneCoords( HitFX[SimHitFxTicker].bone );
|
||||
|
||||
if ( !Level.bDropDetail && !class'GameInfo'.static.NoBlood() && !bSkeletized && !class'GameInfo'.static.UseLowGore() )
|
||||
{
|
||||
//AttachEmitterEffect( BleedingEmitterClass, HitFX[SimHitFxTicker].bone, boneCoords.Origin, HitFX[SimHitFxTicker].rotDir );
|
||||
|
||||
HitFX[SimHitFxTicker].damtype.static.GetHitEffects( HitEffects, Health );
|
||||
|
||||
if( !PhysicsVolume.bWaterVolume ) // don't attach effects under water
|
||||
{
|
||||
for( i = 0; i < ArrayCount(HitEffects); i++ )
|
||||
{
|
||||
if( HitEffects[i] == none )
|
||||
continue;
|
||||
|
||||
AttachEffect( HitEffects[i], HitFX[SimHitFxTicker].bone, boneCoords.Origin, HitFX[SimHitFxTicker].rotDir );
|
||||
}
|
||||
}
|
||||
}
|
||||
if ( class'GameInfo'.static.UseLowGore() )
|
||||
HitFX[SimHitFxTicker].bSever = false;
|
||||
|
||||
if( HitFX[SimHitFxTicker].bSever )
|
||||
{
|
||||
GibPerterbation = HitFX[SimHitFxTicker].damtype.default.GibPerterbation;
|
||||
|
||||
switch( HitFX[SimHitFxTicker].bone )
|
||||
{
|
||||
case 'obliterate':
|
||||
break;
|
||||
|
||||
case LeftThighBone:
|
||||
if( !bLeftLegGibbed )
|
||||
{
|
||||
SpawnSeveredGiblet( DetachedLegClass, boneCoords.Origin, HitFX[SimHitFxTicker].rotDir, GibPerterbation, GetBoneRotation(HitFX[SimHitFxTicker].bone) );
|
||||
KFSpawnGiblet( class 'KFMod.KFGibBrain',boneCoords.Origin, HitFX[SimHitFxTicker].rotDir, GibPerterbation, 250 ) ;
|
||||
KFSpawnGiblet( class 'KFMod.KFGibBrainb',boneCoords.Origin, HitFX[SimHitFxTicker].rotDir, GibPerterbation, 250 ) ;
|
||||
KFSpawnGiblet( class 'KFMod.KFGibBrain',boneCoords.Origin, HitFX[SimHitFxTicker].rotDir, GibPerterbation, 250 ) ;
|
||||
bLeftLegGibbed=true;
|
||||
}
|
||||
break;
|
||||
|
||||
case RightThighBone:
|
||||
if( !bRightLegGibbed )
|
||||
{
|
||||
SpawnSeveredGiblet( DetachedLegClass, boneCoords.Origin, HitFX[SimHitFxTicker].rotDir, GibPerterbation, GetBoneRotation(HitFX[SimHitFxTicker].bone) );
|
||||
KFSpawnGiblet( class 'KFMod.KFGibBrain',boneCoords.Origin, HitFX[SimHitFxTicker].rotDir, GibPerterbation, 250 ) ;
|
||||
KFSpawnGiblet( class 'KFMod.KFGibBrainb',boneCoords.Origin, HitFX[SimHitFxTicker].rotDir, GibPerterbation, 250 ) ;
|
||||
KFSpawnGiblet( class 'KFMod.KFGibBrain',boneCoords.Origin, HitFX[SimHitFxTicker].rotDir, GibPerterbation, 250 ) ;
|
||||
bRightLegGibbed=true;
|
||||
}
|
||||
break;
|
||||
|
||||
case LeftFArmBone:
|
||||
if( !bLeftArmGibbed )
|
||||
{
|
||||
SpawnSeveredGiblet( DetachedArmClass, boneCoords.Origin, HitFX[SimHitFxTicker].rotDir, GibPerterbation, GetBoneRotation(HitFX[SimHitFxTicker].bone) );
|
||||
KFSpawnGiblet( class 'KFMod.KFGibBrain',boneCoords.Origin, HitFX[SimHitFxTicker].rotDir, GibPerterbation, 250 ) ;
|
||||
KFSpawnGiblet( class 'KFMod.KFGibBrainb',boneCoords.Origin, HitFX[SimHitFxTicker].rotDir, GibPerterbation, 250 ) ;;
|
||||
bLeftArmGibbed=true;
|
||||
}
|
||||
break;
|
||||
|
||||
case RightFArmBone:
|
||||
if( !bRightArmGibbed )
|
||||
{
|
||||
SpawnSeveredGiblet( DetachedSpecialArmClass, boneCoords.Origin, HitFX[SimHitFxTicker].rotDir, GibPerterbation, GetBoneRotation(HitFX[SimHitFxTicker].bone) );
|
||||
KFSpawnGiblet( class 'KFMod.KFGibBrain',boneCoords.Origin, HitFX[SimHitFxTicker].rotDir, GibPerterbation, 250 ) ;
|
||||
KFSpawnGiblet( class 'KFMod.KFGibBrainb',boneCoords.Origin, HitFX[SimHitFxTicker].rotDir, GibPerterbation, 250 ) ;
|
||||
bRightArmGibbed=true;
|
||||
}
|
||||
break;
|
||||
|
||||
case 'head':
|
||||
if( !bHeadGibbed )
|
||||
{
|
||||
if ( HitFX[SimHitFxTicker].damtype == class'DamTypeDecapitation' )
|
||||
{
|
||||
DecapFX( boneCoords.Origin, HitFX[SimHitFxTicker].rotDir, false);
|
||||
}
|
||||
else if( HitFX[SimHitFxTicker].damtype == class'DamTypeProjectileDecap' )
|
||||
{
|
||||
DecapFX( boneCoords.Origin, HitFX[SimHitFxTicker].rotDir, false, true);
|
||||
}
|
||||
else if( HitFX[SimHitFxTicker].damtype == class'DamTypeMeleeDecapitation' )
|
||||
{
|
||||
DecapFX( boneCoords.Origin, HitFX[SimHitFxTicker].rotDir, true);
|
||||
}
|
||||
|
||||
bHeadGibbed=true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
if( HitFX[SimHitFXTicker].bone != 'Spine' && HitFX[SimHitFXTicker].bone != FireRootBone &&
|
||||
HitFX[SimHitFXTicker].bone != 'head' && Health <=0 )
|
||||
HideBone(HitFX[SimHitFxTicker].bone);
|
||||
}
|
||||
}
|
||||
}
|
||||
function bool CheckStun(int stunScore, Pawn instigatedBy, Vector hitLocation, Vector momentum, class<NiceWeaponDamageType> damageType, float headshotLevel, KFPlayerReplicationInfo KFPRI){
|
||||
if(Health > 0 && damageType != none && damageType.default.HeadShotDamageMult >= 1.2
|
||||
&& stunScore >= 250 && ( /*(DamageType != class'NiceDamTypeMagnumPistol') ||*/ headshotLevel > 0.0) )//MEANTODO
|
||||
return true;
|
||||
return super.CheckStun(stunScore, instigatedBy, hitLocation, momentum, damageType, headshotLevel, KFPRI);
|
||||
}
|
||||
static simulated function PreCacheMaterials(LevelInfo myLevel)
|
||||
{//should be derived and used.
|
||||
myLevel.AddPrecacheMaterial(Texture'KF_Specimens_Trip_T_Two.burns_diff');
|
||||
myLevel.AddPrecacheMaterial(Texture'KF_Specimens_Trip_T_Two.burns_emissive_mask');
|
||||
myLevel.AddPrecacheMaterial(Combiner'KF_Specimens_Trip_T_Two.burns_energy_cmb');
|
||||
myLevel.AddPrecacheMaterial(Combiner'KF_Specimens_Trip_T_Two.burns_env_cmb');
|
||||
myLevel.AddPrecacheMaterial(Combiner'KF_Specimens_Trip_T_Two.burns_fire_cmb');
|
||||
myLevel.AddPrecacheMaterial(Material'KF_Specimens_Trip_T_Two.burns_shdr');
|
||||
myLevel.AddPrecacheMaterial(Combiner'KF_Specimens_Trip_T_Two.burns_cmb');
|
||||
}
|
||||
defaultproperties
|
||||
{
|
||||
HuskFireProjClass=Class'NicePack.NiceHuskFireProjectile'
|
||||
stunLoopStart=0.080000
|
||||
stunLoopEnd=0.900000
|
||||
idleInsertFrame=0.930000
|
||||
Heat=50.000000
|
||||
EventClasses(0)="NicePack.NiceZombieHusk"
|
||||
MoanVoice=SoundGroup'KF_EnemiesFinalSnd.Husk.Husk_Talk'
|
||||
MeleeAttackHitSound=SoundGroup'KF_EnemiesFinalSnd.Bloat.Bloat_HitPlayer'
|
||||
JumpSound=SoundGroup'KF_EnemiesFinalSnd.Husk.Husk_Jump'
|
||||
DetachedArmClass=Class'KFChar.SeveredArmHusk'
|
||||
DetachedLegClass=Class'KFChar.SeveredLegHusk'
|
||||
DetachedHeadClass=Class'KFChar.SeveredHeadHusk'
|
||||
DetachedSpecialArmClass=Class'KFChar.SeveredArmHuskGun'
|
||||
HitSound(0)=SoundGroup'KF_EnemiesFinalSnd.Husk.Husk_Pain'
|
||||
DeathSound(0)=SoundGroup'KF_EnemiesFinalSnd.Husk.Husk_Death'
|
||||
ChallengeSound(0)=SoundGroup'KF_EnemiesFinalSnd.Husk.Husk_Challenge'
|
||||
ChallengeSound(1)=SoundGroup'KF_EnemiesFinalSnd.Husk.Husk_Challenge'
|
||||
ChallengeSound(2)=SoundGroup'KF_EnemiesFinalSnd.Husk.Husk_Challenge'
|
||||
ChallengeSound(3)=SoundGroup'KF_EnemiesFinalSnd.Husk.Husk_Challenge'
|
||||
ControllerClass=Class'NicePack.NiceZombieHuskController'
|
||||
AmbientSound=Sound'KF_BaseHusk.Husk_IdleLoop'
|
||||
Mesh=SkeletalMesh'KF_Freaks2_Trip.Burns_Freak'
|
||||
Skins(0)=Texture'KF_Specimens_Trip_T_Two.burns.burns_tatters'
|
||||
}
|
||||
21
sources/Zeds/Nice/NiceZombieHuskBase.uc
Normal file
21
sources/Zeds/Nice/NiceZombieHuskBase.uc
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
//=============================================================================
|
||||
// ZombieHusk
|
||||
//=============================================================================
|
||||
// Husk burned up fire projectile launching zed pawn class
|
||||
//=============================================================================
|
||||
// Killing Floor Source
|
||||
// Copyright (C) 2009 Tripwire Interactive LLC
|
||||
// - John "Ramm-Jaeger" Gibson
|
||||
//=============================================================================
|
||||
class NiceZombieHuskBase extends NiceMonster
|
||||
abstract;
|
||||
var float NextFireProjectileTime; // Track when we will fire again
|
||||
var() float ProjectileFireInterval; // How often to fire the fire projectile
|
||||
var() float BurnDamageScale; // How much to reduce fire damage for the Husk
|
||||
//-------------------------------------------------------------------------------
|
||||
// NOTE: All Code resides in the child class(this class was only created to
|
||||
// eliminate hitching caused by loading default properties during play)
|
||||
//-------------------------------------------------------------------------------
|
||||
defaultproperties
|
||||
{
ProjectileFireInterval=5.500000
BurnDamageScale=0.250000
bFireImmune=True
bCanBurn=False
fuelRatio=0.000000
clientHeadshotScale=1.100000
MeleeAnims(0)="Strike"
MeleeAnims(1)="Strike"
MeleeAnims(2)="Strike"
BleedOutDuration=6.000000
ZapThreshold=0.750000
bHarpoonToBodyStuns=False
ZombieFlag=1
MeleeDamage=15
damageForce=70000
bFatAss=True
KFRagdollName="Burns_Trip"
Intelligence=BRAINS_Mammal
bCanDistanceAttackDoors=True
bUseExtendedCollision=True
ColOffset=(Z=36.000000)
ColRadius=30.000000
ColHeight=33.000000
SeveredArmAttachScale=0.900000
SeveredLegAttachScale=0.900000
SeveredHeadAttachScale=0.900000
PlayerCountHealthScale=0.100000
OnlineHeadshotOffset=(X=20.000000,Z=55.000000)
HeadHealth=200.000000
PlayerNumHeadHealthScale=0.050000
AmmunitionClass=Class'KFMod.BZombieAmmo'
ScoringValue=17
IdleHeavyAnim="Idle"
IdleRifleAnim="Idle"
MeleeRange=30.000000
GroundSpeed=115.000000
WaterSpeed=102.000000
HealthMax=600.000000
Health=600
HeadHeight=1.000000
HeadScale=1.350000
AmbientSoundScaling=8.000000
MenuName="Nice Husk"
MovementAnims(0)="WalkF"
MovementAnims(1)="WalkB"
MovementAnims(2)="WalkL"
MovementAnims(3)="WalkR"
WalkAnims(1)="WalkB"
WalkAnims(2)="WalkL"
WalkAnims(3)="WalkR"
IdleCrouchAnim="Idle"
IdleWeaponAnim="Idle"
IdleRestAnim="Idle"
DrawScale=1.400000
PrePivot=(Z=22.000000)
Skins(1)=Shader'KF_Specimens_Trip_T_Two.burns.burns_shdr'
SoundVolume=200
Mass=400.000000
RotationRate=(Yaw=45000,Roll=0)
|
||||
}
|
||||
97
sources/Zeds/Nice/NiceZombieHuskController.uc
Normal file
97
sources/Zeds/Nice/NiceZombieHuskController.uc
Normal file
|
|
@ -0,0 +1,97 @@
|
|||
//=============================================================================
|
||||
// HuskZombieController
|
||||
//=============================================================================
|
||||
// Controller class for the husk zombie
|
||||
//=============================================================================
|
||||
// Killing Floor Source
|
||||
// Copyright (C) 2009 Tripwire Interactive LLC
|
||||
// - John "Ramm-Jaeger" Gibson
|
||||
//=============================================================================
|
||||
class NiceZombieHuskController extends NiceMonsterController;
|
||||
// Overridden to create a delay between when the husk fires his projectiles
|
||||
function bool FireWeaponAt(Actor A)
|
||||
{
|
||||
if ( A == none )
A = Enemy;
|
||||
if ( (A == none) || (Focus != A) )
return false;
|
||||
Target = A;
|
||||
if( (VSize(A.Location - Pawn.Location) >= NiceZombieHusk(Pawn).MeleeRange + Pawn.CollisionRadius + Target.CollisionRadius) &&
NiceZombieHusk(Pawn).NextFireProjectileTime - Level.TimeSeconds > 0 )
|
||||
{
return false;
|
||||
}
|
||||
Monster(Pawn).RangedAttack(Target);
|
||||
return false;
|
||||
}
|
||||
/*
|
||||
AdjustAim()
|
||||
Returns a rotation which is the direction the bot should aim - after introducing the appropriate aiming error
|
||||
Overridden to cause the zed to fire at the feet more often - Ramm
|
||||
*/
|
||||
function rotator AdjustAim(FireProperties FiredAmmunition, vector projStart, int aimerror)
|
||||
{
|
||||
local rotator FireRotation, TargetLook;
|
||||
local float FireDist, TargetDist, ProjSpeed;
|
||||
local actor HitActor;
|
||||
local vector FireSpot, FireDir, TargetVel, HitLocation, HitNormal;
|
||||
local int realYaw;
|
||||
local bool bDefendMelee, bClean, bLeadTargetNow;
|
||||
local bool bWantsToAimAtFeet;
|
||||
if ( FiredAmmunition.ProjectileClass != none )
projspeed = FiredAmmunition.ProjectileClass.default.speed;
|
||||
// make sure bot has a valid target
|
||||
if ( Target == none )
|
||||
{
Target = Enemy;
if ( Target == none )
return Rotation;
|
||||
}
|
||||
FireSpot = Target.Location;
|
||||
TargetDist = VSize(Target.Location - Pawn.Location);
|
||||
// perfect aim at stationary objects
|
||||
if ( Pawn(Target) == none )
|
||||
{
if ( !FiredAmmunition.bTossed )
return rotator(Target.Location - projstart);
else
{
FireDir = AdjustToss(projspeed,ProjStart,Target.Location,true);
SetRotation(Rotator(FireDir));
return Rotation;
}
|
||||
}
|
||||
bLeadTargetNow = FiredAmmunition.bLeadTarget && bLeadTarget;
|
||||
bDefendMelee = ( (Target == Enemy) && DefendMelee(TargetDist) );
|
||||
aimerror = AdjustAimError(aimerror,TargetDist,bDefendMelee,FiredAmmunition.bInstantHit, bLeadTargetNow);
|
||||
// lead target with non instant hit projectiles
|
||||
if ( bLeadTargetNow )
|
||||
{
TargetVel = Target.Velocity;
// hack guess at projecting falling velocity of target
if ( Target.Physics == PHYS_Falling )
{
if ( Target.PhysicsVolume.Gravity.Z <= Target.PhysicsVolume.Default.Gravity.Z )
TargetVel.Z = FMin(TargetVel.Z + FMax(-400, Target.PhysicsVolume.Gravity.Z * FMin(1,TargetDist/projSpeed)),0);
else
TargetVel.Z = FMin(0, TargetVel.Z);
}
// more or less lead target (with some random variation)
FireSpot += FMin(1, 0.7 + 0.6 * FRand()) * TargetVel * TargetDist/projSpeed;
FireSpot.Z = FMin(Target.Location.Z, FireSpot.Z);
|
||||
if ( (Target.Physics != PHYS_Falling) && (FRand() < 0.55) && (VSize(FireSpot - ProjStart) > 1000) )
{
// don't always lead far away targets, especially if they are moving sideways with respect to the bot
TargetLook = Target.Rotation;
if ( Target.Physics == PHYS_Walking )
TargetLook.Pitch = 0;
bClean = ( ((Vector(TargetLook) Dot Normal(Target.Velocity)) >= 0.71) && FastTrace(FireSpot, ProjStart) );
}
else // make sure that bot isn't leading into a wall
bClean = FastTrace(FireSpot, ProjStart);
if ( !bClean)
{
// reduce amount of leading
if ( FRand() < 0.3 )
FireSpot = Target.Location;
else
FireSpot = 0.5 * (FireSpot + Target.Location);
}
|
||||
}
|
||||
bClean = false; //so will fail first check unless shooting at feet
|
||||
// Randomly determine if we should try and splash damage with the fire projectile
|
||||
if( FiredAmmunition.bTrySplash )
|
||||
{
if( Skill < 2.0 )
{
if(FRand() > 0.85)
{
bWantsToAimAtFeet = true;
}
}
else if( Skill < 3.0 )
{
if(FRand() > 0.5)
{
bWantsToAimAtFeet = true;
}
}
else if( Skill >= 3.0 )
{
if(FRand() > 0.25)
{
bWantsToAimAtFeet = true;
}
}
|
||||
}
|
||||
if ( FiredAmmunition.bTrySplash && (Pawn(Target) != none) && (((Target.Physics == PHYS_Falling)
&& (Pawn.Location.Z + 80 >= Target.Location.Z)) || ((Pawn.Location.Z + 19 >= Target.Location.Z)
&& (bDefendMelee || bWantsToAimAtFeet))) )
|
||||
{
HitActor = Trace(HitLocation, HitNormal, FireSpot - vect(0,0,1) * (Target.CollisionHeight + 10), FireSpot, false);
|
||||
bClean = (HitActor == none);
if ( !bClean )
{
FireSpot = HitLocation + vect(0,0,3);
bClean = FastTrace(FireSpot, ProjStart);
}
else
bClean = ( (Target.Physics == PHYS_Falling) && FastTrace(FireSpot, ProjStart) );
|
||||
}
|
||||
if ( !bClean )
|
||||
{
//try middle
FireSpot.Z = Target.Location.Z;
bClean = FastTrace(FireSpot, ProjStart);
|
||||
}
|
||||
if ( FiredAmmunition.bTossed && !bClean && bEnemyInfoValid )
|
||||
{
FireSpot = LastSeenPos;
HitActor = Trace(HitLocation, HitNormal, FireSpot, ProjStart, false);
if ( HitActor != none )
{
bCanFire = false;
FireSpot += 2 * Target.CollisionHeight * HitNormal;
}
bClean = true;
|
||||
}
|
||||
if( !bClean )
|
||||
{
// try head
FireSpot.Z = Target.Location.Z + 0.9 * Target.CollisionHeight;
bClean = FastTrace(FireSpot, ProjStart);
|
||||
}
|
||||
if ( !bClean && (Target == Enemy) && bEnemyInfoValid )
|
||||
{
FireSpot = LastSeenPos;
if ( Pawn.Location.Z >= LastSeenPos.Z )
FireSpot.Z -= 0.4 * Enemy.CollisionHeight;
HitActor = Trace(HitLocation, HitNormal, FireSpot, ProjStart, false);
if ( HitActor != none )
{
FireSpot = LastSeenPos + 2 * Enemy.CollisionHeight * HitNormal;
if ( Monster(Pawn).SplashDamage() && (Skill >= 4) )
{
HitActor = Trace(HitLocation, HitNormal, FireSpot, ProjStart, false);
if ( HitActor != none )
FireSpot += 2 * Enemy.CollisionHeight * HitNormal;
}
bCanFire = false;
}
|
||||
}
|
||||
// adjust for toss distance
|
||||
if ( FiredAmmunition.bTossed )
FireDir = AdjustToss(projspeed,ProjStart,FireSpot,true);
|
||||
else
FireDir = FireSpot - ProjStart;
|
||||
FireRotation = Rotator(FireDir);
|
||||
realYaw = FireRotation.Yaw;
|
||||
InstantWarnTarget(Target,FiredAmmunition,vector(FireRotation));
|
||||
FireRotation.Yaw = SetFireYaw(FireRotation.Yaw + aimerror);
|
||||
FireDir = vector(FireRotation);
|
||||
// avoid shooting into wall
|
||||
FireDist = FMin(VSize(FireSpot-ProjStart), 400);
|
||||
FireSpot = ProjStart + FireDist * FireDir;
|
||||
HitActor = Trace(HitLocation, HitNormal, FireSpot, ProjStart, false);
|
||||
if ( HitActor != none )
|
||||
{
if ( HitNormal.Z < 0.7 )
{
FireRotation.Yaw = SetFireYaw(realYaw - aimerror);
FireDir = vector(FireRotation);
FireSpot = ProjStart + FireDist * FireDir;
HitActor = Trace(HitLocation, HitNormal, FireSpot, ProjStart, false);
}
if ( HitActor != none )
{
FireSpot += HitNormal * 2 * Target.CollisionHeight;
if ( Skill >= 4 )
{
HitActor = Trace(HitLocation, HitNormal, FireSpot, ProjStart, false);
if ( HitActor != none )
FireSpot += Target.CollisionHeight * HitNormal;
}
FireDir = Normal(FireSpot - ProjStart);
FireRotation = rotator(FireDir);
}
|
||||
}
|
||||
SetRotation(FireRotation);
|
||||
return FireRotation;
|
||||
}
|
||||
defaultproperties
|
||||
{
|
||||
}
|
||||
83
sources/Zeds/Nice/NiceZombieJason.uc
Normal file
83
sources/Zeds/Nice/NiceZombieJason.uc
Normal file
|
|
@ -0,0 +1,83 @@
|
|||
class NiceZombieJason extends NiceZombieScrake;
|
||||
#exec load obj file=ScrnZedPack_T.utx
|
||||
#exec load obj file=ScrnZedPack_S.uax
|
||||
#exec load obj file=ScrnZedPack_A.ukx
|
||||
var int OriginalMeleeDamage; // default melee damage, adjusted by game's difficulty
|
||||
var bool bWasRaged; // set to true, if Jason is raged or was raged before
|
||||
var float RageHealthPct;
|
||||
var float RegenDelay;
|
||||
var float RegenRate; // Speed of regeneration, in percents of max health
|
||||
var float RegenAcc;
|
||||
var float RegenAccHead;
|
||||
simulated function PostBeginPlay(){
|
||||
super.PostBeginPlay();
|
||||
OriginalMeleeDamage = MeleeDamage;
|
||||
}
|
||||
function bool IsStunPossible(){
|
||||
return false;
|
||||
}
|
||||
function bool CheckMiniFlinch(int flinchScore, Pawn instigatedBy, Vector hitLocation, Vector momentum, class<NiceWeaponDamageType> damageType, float headshotLevel, KFPlayerReplicationInfo KFPRI){
|
||||
return super.CheckMiniFlinch(flinchScore * 1.5, instigatedBy, hitLocation, momentum, damageType, headshotLevel, KFPRI);
|
||||
}
|
||||
simulated function Tick(float DeltaTime){
|
||||
super.Tick(DeltaTime);
|
||||
if(Role < ROLE_Authority)
return;
|
||||
if(lastTookDamageTime + RegenDelay < Level.TimeSeconds && Health > 0){
RegenAcc += DeltaTime * RegenRate * HealthMax * 0.01;
RegenAccHead += DeltaTime * RegenRate * HeadHealthMax * 0.01;
if(RegenAcc > 1){
Health += RegenAcc;
if(Health > HealthMax)
Health = HealthMax;
RegenAcc = 0.0;
}
if(RegenAccHead > 1){
HeadHealth += RegenAccHead;
if(HeadHealth > HeadHealthMax)
HeadHealth = HeadHealthMax;
RegenAccHead = 0.0;
}
|
||||
}
|
||||
else{
RegenAcc = 0.0;
RegenAccHead = 0.0;
|
||||
}
|
||||
}
|
||||
// Machete has no Exhaust ;)
|
||||
simulated function SpawnExhaustEmitter(){}
|
||||
simulated function UpdateExhaustEmitter(){}
|
||||
function bool CanGetOutOfWay()
|
||||
{
|
||||
return !bIsStunned; // can't dodge husk fireballs while stunned
|
||||
}
|
||||
simulated function Unstun(){
|
||||
bCharging = true;
|
||||
MovementAnims[0] = 'ChargeF';
|
||||
super.Unstun();
|
||||
}
|
||||
function TakeDamageClient(int Damage, Pawn InstigatedBy, Vector Hitlocation, Vector Momentum, class<NiceWeaponDamageType> damageType, float headshotLevel, float lockonTime){
|
||||
Super.TakeDamageClient(Damage, instigatedBy, hitLocation, momentum, damageType, headshotLevel, lockonTime);
|
||||
if(bIsStunned && Health > 0 && (headshotLevel <= 0.0) && Level.TimeSeconds > LastStunTime + 0.1)
Unstun();
|
||||
}
|
||||
function TakeFireDamage(int Damage, Pawn Instigator){
|
||||
Super.TakeFireDamage(Damage, Instigator);
|
||||
if(bIsStunned && Health > 0 && Damage > 150 && Level.TimeSeconds > LastStunTime + 0.1)
Unstun();
|
||||
}
|
||||
function RangedAttack(Actor A)
|
||||
{
|
||||
if ( bShotAnim || Physics == PHYS_Swimming)
return;
|
||||
else if ( CanAttack(A) )
|
||||
{
bShotAnim = true;
SetAnimAction(MeleeAnims[Rand(2)]);
if(NiceMonster(A) == none)
GoToState('SawingLoop');
|
||||
}
|
||||
if( !bShotAnim && !bDecapitated ) {
if(bConfusedState)
return;
if ( bWasRaged || float(Health)/HealthMax < 0.5
|| (float(Health)/HealthMax < RageHealthPct) )
GoToState('RunningState');
|
||||
}
|
||||
}
|
||||
State SawingLoop
|
||||
{
|
||||
function RangedAttack(Actor A)
|
||||
{
if ( bShotAnim )
return;
else if ( CanAttack(A) )
{
Acceleration = vect(0,0,0);
bShotAnim = true;
MeleeDamage = OriginalMeleeDamage * 0.6;
SetAnimAction('SawImpaleLoop');
if( AmbientSound != SawAttackLoopSound )
{
AmbientSound=SawAttackLoopSound;
}
}
else GoToState('');
|
||||
}
|
||||
}
|
||||
simulated function float GetOriginalGroundSpeed()
|
||||
{
|
||||
local float result;
|
||||
result = OriginalGroundSpeed;
|
||||
if ( bWasRaged || bCharging )
result *= 3.5;
|
||||
else if( bZedUnderControl )
result *= 1.25;
return result;
|
||||
}
|
||||
state RunningState
|
||||
{
|
||||
function BeginState()
|
||||
{
local NiceHumanPawn rageTarget;
bWasRaged = true;
if(bWasCalm){
bWasCalm = false;
rageTarget = NiceHumanPawn(Controller.focus);
if( rageTarget != none && KFGameType(Level.Game) != none
&& class'NiceVeterancyTypes'.static.HasSkill(NicePlayerController(rageTarget.Controller),
class'NiceSkillCommandoPerfectExecution') ){
KFGameType(Level.Game).DramaticEvent(1.0);
}
}
if( bZapped )
GoToState('');
else {
bCharging = true;
SetGroundSpeed(GetOriginalGroundSpeed());
if( Level.NetMode!=NM_DedicatedServer )
PostNetReceive();
NetUpdateTime = Level.TimeSeconds - 1;
}
|
||||
}
|
||||
function EndState()
|
||||
{
bCharging = False;
if( !bZapped )
SetGroundSpeed(GetOriginalGroundSpeed());
if( Level.NetMode!=NM_DedicatedServer )
PostNetReceive();
|
||||
}
|
||||
}
|
||||
defaultproperties
|
||||
{
RageHealthPct=0.750000
RegenDelay=5.000000
RegenRate=4.000000
SawAttackLoopSound=Sound'KF_BaseGorefast.Attack.Gorefast_AttackSwish3'
ChainSawOffSound=None
StunThreshold=1.000000
MoanVoice=None
StunsRemaining=5
BleedOutDuration=7.000000
MeleeDamage=25
MeleeAttackHitSound=SoundGroup'KF_EnemiesFinalSnd.GoreFast.Gorefast_HitPlayer'
JumpSound=None
HeadHealth=800.000000
HitSound(0)=None
DeathSound(0)=None
ChallengeSound(0)=None
ChallengeSound(1)=None
ChallengeSound(2)=None
ChallengeSound(3)=None
ScoringValue=300
MenuName="Jason"
AmbientSound=Sound'ScrnZedPack_S.Jason.Jason_Sound'
Mesh=SkeletalMesh'ScrnZedPack_A.JasonMesh'
Skins(0)=Shader'ScrnZedPack_T.Jason.Jason__FB'
Skins(1)=Texture'ScrnZedPack_T.Jason.JVMaskB'
Skins(2)=Combiner'ScrnZedPack_T.Jason.Machete_cmb'
|
||||
}
|
||||
297
sources/Zeds/Nice/NiceZombieScrake.uc
Normal file
297
sources/Zeds/Nice/NiceZombieScrake.uc
Normal file
|
|
@ -0,0 +1,297 @@
|
|||
// Chainsaw Zombie Monster for KF Invasion gametype
|
||||
// He's not quite as speedy as the other Zombies, But his attacks are TRULY damaging.
|
||||
class NiceZombieScrake extends NiceZombieScrakeBase;
|
||||
var bool bConfusedState;
|
||||
var bool bWasCalm;
|
||||
//----------------------------------------------------------------------------
|
||||
// NOTE: All Variables are declared in the base class to eliminate hitching
|
||||
//----------------------------------------------------------------------------
|
||||
simulated function PostNetBeginPlay()
|
||||
{
|
||||
EnableChannelNotify ( 1,1);
|
||||
AnimBlendParams(1, 1.0, 0.0,, SpineBone1);
|
||||
super.PostNetBeginPlay();
|
||||
}
|
||||
simulated function PostBeginPlay()
|
||||
{
|
||||
super.PostBeginPlay();
|
||||
bWasCalm = true;
|
||||
SpawnExhaustEmitter();
|
||||
}
|
||||
// Make the scrakes's ambient scale higher, since there are just a few, and thier chainsaw need to be heard from a distance
|
||||
simulated function CalcAmbientRelevancyScale()
|
||||
{
|
||||
// Make the zed only relevant by their ambient sound out to a range of 30 meters
|
||||
CustomAmbientRelevancyScale = 1500 / (100 * SoundRadius);
|
||||
}
|
||||
simulated function PostNetReceive()
|
||||
{
|
||||
if (bCharging)
MovementAnims[0]='ChargeF';
|
||||
else if( !(bCrispified && bBurnified) )
MovementAnims[0]=default.MovementAnims[0];
|
||||
}
|
||||
// Deprecated
|
||||
function bool FlipOverWithIntsigator(Pawn InstigatedBy){
|
||||
local bool bFlippedOver;
|
||||
bFlippedOver = super.FlipOverWithIntsigator(InstigatedBy);
|
||||
if(bFlippedOver){
// do not rotate while stunned
Controller.Focus = none;
Controller.FocalPoint = Location + 512*vector(Rotation);
|
||||
}
|
||||
return bFlippedOver;
|
||||
}
|
||||
function bool CanGetOutOfWay()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
function float GetIceCrustScale(){
|
||||
//return 25000 / (default.health * default.health);
|
||||
return 0.01;
|
||||
}
|
||||
// This zed has been taken control of. Boost its health and speed
|
||||
function SetMindControlled(bool bNewMindControlled)
|
||||
{
|
||||
if( bNewMindControlled )
|
||||
{
NumZCDHits++;
|
||||
// if we hit him a couple of times, make him rage!
if( NumZCDHits > 1 )
{
if( !IsInState('RunningToMarker') )
{
GotoState('RunningToMarker');
}
else
{
NumZCDHits = 1;
if( IsInState('RunningToMarker') )
{
GotoState('');
}
}
}
else
{
if( IsInState('RunningToMarker') )
{
GotoState('');
}
}
|
||||
if( bNewMindControlled != bZedUnderControl )
{
SetGroundSpeed(OriginalGroundSpeed * 1.25);
Health *= 1.25;
HealthMax *= 1.25;
}
|
||||
}
|
||||
else
|
||||
{
NumZCDHits=0;
|
||||
}
|
||||
bZedUnderControl = bNewMindControlled;
|
||||
}
|
||||
// Handle the zed being commanded to move to a new location
|
||||
function GivenNewMarker()
|
||||
{
|
||||
if( bCharging && NumZCDHits > 1 )
|
||||
{
GotoState('RunningToMarker');
|
||||
}
|
||||
else
|
||||
{
GotoState('');
|
||||
}
|
||||
}
|
||||
simulated function SpawnExhaustEmitter()
|
||||
{
|
||||
if ( Level.NetMode != NM_DedicatedServer )
|
||||
{
if ( ExhaustEffectClass != none )
{
ExhaustEffect = Spawn(ExhaustEffectClass, self);
|
||||
if ( ExhaustEffect != none )
{
AttachToBone(ExhaustEffect, 'Chainsaw_lod1');
ExhaustEffect.SetRelativeLocation(vect(0, -20, 0));
}
}
|
||||
}
|
||||
}
|
||||
simulated function UpdateExhaustEmitter()
|
||||
{
|
||||
local byte Throttle;
|
||||
if ( Level.NetMode != NM_DedicatedServer )
|
||||
{
if ( ExhaustEffect != none )
{
if ( bShotAnim )
{
Throttle = 3;
}
else
{
Throttle = 0;
}
}
else
{
if ( !bNoExhaustRespawn )
{
SpawnExhaustEmitter();
}
}
|
||||
}
|
||||
}
|
||||
simulated function Tick(float DeltaTime)
|
||||
{
|
||||
super.Tick(DeltaTime);
|
||||
UpdateExhaustEmitter();
|
||||
}
|
||||
function RangedAttack(Actor A)
|
||||
{
|
||||
if ( bShotAnim || Physics == PHYS_Swimming)
return;
|
||||
else if ( CanAttack(A) )
|
||||
{
bShotAnim = true;
SetAnimAction(MeleeAnims[Rand(2)]);
//PlaySound(sound'Claw2s', SLOT_none); KFTODO: Replace this
if(NiceMonster(A) == none)
GoToState('SawingLoop');
|
||||
}
|
||||
if( !bShotAnim && !bDecapitated )
|
||||
{
if(bConfusedState)
return;
if ( float(Health)/HealthMax < 0.75)
GoToState('RunningState');
|
||||
}
|
||||
}
|
||||
state RunningState
|
||||
{
|
||||
// Set the zed to the zapped behavior
|
||||
simulated function SetZappedBehavior()
|
||||
{
Global.SetZappedBehavior();
GoToState('');
|
||||
}
|
||||
// Don't override speed in this state
|
||||
function bool CanSpeedAdjust()
|
||||
{
return false;
|
||||
}
|
||||
simulated function float GetOriginalGroundSpeed() {
return 3.5 * OriginalGroundSpeed;
|
||||
}
|
||||
function BeginState(){
local NiceHumanPawn rageTarget, rageCause;
|
||||
if(Health <= 0)
return;
|
||||
if(bWasCalm){
bWasCalm = false;
rageTarget = NiceHumanPawn(Controller.focus);
rageCause = NiceHumanPawn(LastDamagedBy);
if( rageTarget != none && KFGameType(Level.Game) != none
&& class'NiceVeterancyTypes'.static.HasSkill(NicePlayerController(rageTarget.Controller),
class'NiceSkillCommandoPerfectExecution') ){
KFGameType(Level.Game).DramaticEvent(1.0);
}
else if( rageCause != none && KFGameType(Level.Game) != none
&& class'NiceVeterancyTypes'.static.HasSkill(NicePlayerController(rageCause.Controller),
class'NiceSkillCommandoPerfectExecution') ){
KFGameType(Level.Game).DramaticEvent(1.0);
}
}
if(bZapped)
GoToState('');
else{
SetGroundSpeed(OriginalGroundSpeed * 3.5);
bCharging = true;
if( Level.NetMode!=NM_DedicatedServer )
PostNetReceive();
|
||||
NetUpdateTime = Level.TimeSeconds - 1;
}
|
||||
}
|
||||
function EndState()
|
||||
{
if( !bZapped )
{
SetGroundSpeed(GetOriginalGroundSpeed());
}
bCharging = False;
if( Level.NetMode!=NM_DedicatedServer )
PostNetReceive();
|
||||
}
|
||||
function RemoveHead()
|
||||
{
GoToState('');
Global.RemoveHead();
|
||||
}
|
||||
function RangedAttack(Actor A)
|
||||
{
if ( bShotAnim || Physics == PHYS_Swimming)
return;
else if ( CanAttack(A) )
{
bShotAnim = true;
SetAnimAction(MeleeAnims[Rand(2)]);
if(NiceMonster(A) == none)
GoToState('SawingLoop');
}
|
||||
}
|
||||
}
|
||||
// State where the zed is charging to a marked location.
|
||||
// Not sure if we need this since its just like RageCharging,
|
||||
// but keeping it here for now in case we need to implement some
|
||||
// custom behavior for this state
|
||||
state RunningToMarker extends RunningState
|
||||
{
|
||||
}
|
||||
|
||||
State SawingLoop
|
||||
{
|
||||
// Don't override speed in this state
|
||||
function bool CanSpeedAdjust()
|
||||
{
return false;
|
||||
}
|
||||
simulated function float GetOriginalGroundSpeed() {
return OriginalGroundSpeed * AttackChargeRate;
|
||||
}
|
||||
function bool CanGetOutOfWay()
|
||||
{
return false;
|
||||
}
|
||||
function BeginState()
|
||||
{
bConfusedState = false;
|
||||
// Randomly have the scrake charge during an attack so it will be less predictable
if(Health/HealthMax < 0.5 || FRand() <= 0.95)
{
SetGroundSpeed(OriginalGroundSpeed * AttackChargeRate);
bCharging = true;
if( Level.NetMode!=NM_DedicatedServer )
PostNetReceive();
|
||||
NetUpdateTime = Level.TimeSeconds - 1;
}
|
||||
}
|
||||
function RangedAttack(Actor A)
|
||||
{
if ( bShotAnim )
return;
else if ( CanAttack(A) )
{
Acceleration = vect(0,0,0);
bShotAnim = true;
MeleeDamage = default.MeleeDamage*0.6;
SetAnimAction('SawImpaleLoop');
if( AmbientSound != SawAttackLoopSound )
{
AmbientSound=SawAttackLoopSound;
}
}
else GoToState('');
|
||||
}
|
||||
function AnimEnd( int Channel )
|
||||
{
Super.AnimEnd(Channel);
if( Controller!=none && Controller.Enemy!=none )
RangedAttack(Controller.Enemy); // Keep on attacking if possible.
|
||||
}
|
||||
function Tick( float Delta )
|
||||
{
// Keep the scrake moving toward its target when attacking
if( Role == ROLE_Authority && bShotAnim && !bWaitForAnim )
{
if( LookTarget!=none )
{
Acceleration = AccelRate * Normal(LookTarget.Location - Location);
}
}
|
||||
global.Tick(Delta);
|
||||
}
|
||||
function EndState()
|
||||
{
AmbientSound=default.AmbientSound;
MeleeDamage = Max( DifficultyDamageModifer() * default.MeleeDamage, 1 );
|
||||
SetGroundSpeed(GetOriginalGroundSpeed());
bCharging = False;
if(Level.NetMode != NM_DedicatedServer)
PostNetReceive();
|
||||
}
|
||||
}
|
||||
function ModDamage(out int Damage, Pawn instigatedBy, Vector hitLocation, Vector momentum, class<NiceWeaponDamageType> damageType, float headshotLevel, KFPlayerReplicationInfo KFPRI, optional float lockonTime){
|
||||
super.ModDamage(Damage, instigatedBy, hitLocation, momentum, damageType, headshotLevel, KFPRI);
|
||||
if(damageType == class'ScrnZedPack.DamTypeEMP')
Damage *= 0.01;
|
||||
}
|
||||
function TakeDamageClient(int Damage, Pawn InstigatedBy, Vector Hitlocation, Vector Momentum, class<NiceWeaponDamageType> damageType, float headshotLevel, float lockonTime){
|
||||
local bool bCanGetConfused;
|
||||
local int OldHealth;
|
||||
local PlayerController PC;
|
||||
local KFSteamStatsAndAchievements Stats;
|
||||
OldHealth = Health;
|
||||
bCanGetConfused = false;
|
||||
if(StunsRemaining != 0 && float(Health)/HealthMax >= 0.75)
bCanGetConfused = true;
|
||||
super.takeDamageClient(Damage, instigatedBy, hitLocation, momentum, damageType, headshotLevel, lockonTime);
|
||||
if (
bCanGetConfused &&
!IsInState('SawingLoop') &&
(OldHealth - Health) <= (float(default.Health)/1.5) && float(Health)/HealthMax < 0.75 &&
(LastDamageAmount >= (0.5 * default.Health) ||
(VSize(LastDamagedBy.Location - Location) <= (MeleeRange * 2) && ClassIsChildOf(LastDamagedbyType,class 'DamTypeMelee') &&
KFPawn(LastDamagedBy) != none && LastDamageAmount > (0.10 * default.Health)))
)
bConfusedState = true;
|
||||
if(bConfusedState && Health > 0 && (headshotLevel <= 0.0) && damageType != none){
bConfusedState = false;
GoToState('RunningState');
|
||||
}
|
||||
if(!bConfusedState && !IsInState('SawingLoop') && !IsInState('RunningState') && float(Health) / HealthMax < 0.75)
RangedAttack(InstigatedBy);
|
||||
if(damageType == class'DamTypeDBShotgun'){
PC = PlayerController( InstigatedBy.Controller );
if(PC != none){
Stats = KFSteamStatsAndAchievements( PC.SteamStatsAndAchievements );
if( Stats != none )
Stats.CheckAndSetAchievementComplete( Stats.KFACHIEVEMENT_PushScrakeSPJ );
}
|
||||
}
|
||||
}
|
||||
function TakeFireDamage(int Damage, Pawn Instigator)
|
||||
{
|
||||
Super.TakeFireDamage(Damage, Instigator);
|
||||
if(bConfusedState && Health > 0 && Damage > 150){
bConfusedState = false;
GoToState('RunningState');
|
||||
}
|
||||
}
|
||||
function bool CheckMiniFlinch(int flinchScore, Pawn instigatedBy, Vector hitLocation, Vector momentum, class<NiceWeaponDamageType> damageType, float headshotLevel, KFPlayerReplicationInfo KFPRI){
|
||||
// Scrakes are better at enduring pain, so we need a bit more to flinch them
|
||||
if(StunsRemaining == 0 || flinchScore < 150)
return false;
|
||||
return super.CheckMiniFlinch(flinchScore, instigatedBy, hitLocation, momentum, damageType, headshotLevel, KFPRI);
|
||||
}
|
||||
function DoStun(optional Pawn instigatedBy, optional Vector hitLocation, optional Vector momentum, optional class<NiceWeaponDamageType> damageType, optional float headshotLevel, optional KFPlayerReplicationInfo KFPRI){
|
||||
super.DoStun(instigatedBy, hitLocation, momentum, damageType, headshotLevel, KFPRI);
|
||||
StunsRemaining = 0;
|
||||
}
|
||||
simulated function int DoAnimAction( name AnimName )
|
||||
{
|
||||
if( AnimName=='SawZombieAttack1' || AnimName=='SawZombieAttack2' )
|
||||
{
AnimBlendParams(1, 1.0, 0.0,, FireRootBone);
PlayAnim(AnimName,, 0.1, 1);
Return 1;
|
||||
}
|
||||
Return Super.DoAnimAction(AnimName);
|
||||
}
|
||||
simulated event SetAnimAction(name NewAction)
|
||||
{
|
||||
local int meleeAnimIndex;
|
||||
if( NewAction=='' )
Return;
|
||||
if(NewAction == 'Claw')
|
||||
{
meleeAnimIndex = Rand(3);
NewAction = meleeAnims[meleeAnimIndex];
|
||||
}
|
||||
ExpectingChannel = DoAnimAction(NewAction);
|
||||
if( AnimNeedsWait(NewAction) )
|
||||
{
bWaitForAnim = true;
|
||||
}
|
||||
if( Level.NetMode!=NM_Client )
|
||||
{
AnimAction = NewAction;
bResetAnimAct = True;
ResetAnimActTime = Level.TimeSeconds+0.3;
|
||||
}
|
||||
}
|
||||
// The animation is full body and should set the bWaitForAnim flag
|
||||
simulated function bool AnimNeedsWait(name TestAnim)
|
||||
{
|
||||
if( TestAnim == 'SawImpaleLoop' || TestAnim == 'DoorBash' || TestAnim == 'KnockDown' )
|
||||
{
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
function PlayDyingSound()
|
||||
{
|
||||
if( Level.NetMode!=NM_Client )
|
||||
{
if ( bGibbed )
{
// Do nothing for now
PlaySound(GibGroupClass.static.GibSound(), SLOT_Pain,2.0,true,525);
return;
}
|
||||
if( bDecapitated )
{
|
||||
PlaySound(HeadlessDeathSound, SLOT_Pain,1.30,true,525);
}
else
{
PlaySound(DeathSound[0], SLOT_Pain,1.30,true,525);
}
|
||||
PlaySound(ChainSawOffSound, SLOT_Misc, 2.0,,525.0);
|
||||
}
|
||||
}
|
||||
function Died(Controller Killer, class<DamageType> damageType, vector HitLocation)
|
||||
{
|
||||
AmbientSound = none;
|
||||
if ( ExhaustEffect != none )
|
||||
{
ExhaustEffect.Destroy();
ExhaustEffect = none;
bNoExhaustRespawn = true;
|
||||
}
|
||||
super.Died( Killer, damageType, HitLocation );
|
||||
}
|
||||
simulated function ProcessHitFX()
|
||||
{
|
||||
local Coords boneCoords;
|
||||
local class<xEmitter> HitEffects[4];
|
||||
local int i,j;
|
||||
local float GibPerterbation;
|
||||
if( (Level.NetMode == NM_DedicatedServer) || bSkeletized || (Mesh == SkeletonMesh))
|
||||
{
SimHitFxTicker = HitFxTicker;
return;
|
||||
}
|
||||
for ( SimHitFxTicker = SimHitFxTicker; SimHitFxTicker != HitFxTicker; SimHitFxTicker = (SimHitFxTicker + 1) % ArrayCount(HitFX) )
|
||||
{
j++;
if ( j > 30 )
{
SimHitFxTicker = HitFxTicker;
return;
}
|
||||
if( (HitFX[SimHitFxTicker].damtype == none) || (Level.bDropDetail && (Level.TimeSeconds - LastRenderTime > 3) && !IsHumanControlled()) )
continue;
|
||||
//log("Processing effects for damtype "$HitFX[SimHitFxTicker].damtype);
|
||||
if( HitFX[SimHitFxTicker].bone == 'obliterate' && !class'GameInfo'.static.UseLowGore())
{
SpawnGibs( HitFX[SimHitFxTicker].rotDir, 1);
bGibbed = true;
// Wait a tick on a listen server so the obliteration can replicate before the pawn is destroyed
if( Level.NetMode == NM_ListenServer )
{
bDestroyNextTick = true;
TimeSetDestroyNextTickTime = Level.TimeSeconds;
}
else
{
Destroy();
}
return;
}
|
||||
boneCoords = GetBoneCoords( HitFX[SimHitFxTicker].bone );
|
||||
if ( !Level.bDropDetail && !class'GameInfo'.static.NoBlood() && !bSkeletized && !class'GameInfo'.static.UseLowGore() )
{
//AttachEmitterEffect( BleedingEmitterClass, HitFX[SimHitFxTicker].bone, boneCoords.Origin, HitFX[SimHitFxTicker].rotDir );
|
||||
HitFX[SimHitFxTicker].damtype.static.GetHitEffects( HitEffects, Health );
|
||||
if( !PhysicsVolume.bWaterVolume ) // don't attach effects under water
{
for( i = 0; i < ArrayCount(HitEffects); i++ )
{
if( HitEffects[i] == none )
continue;
|
||||
AttachEffect( HitEffects[i], HitFX[SimHitFxTicker].bone, boneCoords.Origin, HitFX[SimHitFxTicker].rotDir );
}
}
}
if ( class'GameInfo'.static.UseLowGore() )
HitFX[SimHitFxTicker].bSever = false;
|
||||
if( HitFX[SimHitFxTicker].bSever )
{
GibPerterbation = HitFX[SimHitFxTicker].damtype.default.GibPerterbation;
|
||||
switch( HitFX[SimHitFxTicker].bone )
{
case 'obliterate':
break;
|
||||
case LeftThighBone:
if( !bLeftLegGibbed )
{
SpawnSeveredGiblet( DetachedLegClass, boneCoords.Origin, HitFX[SimHitFxTicker].rotDir, GibPerterbation, GetBoneRotation(HitFX[SimHitFxTicker].bone) );
KFSpawnGiblet( class 'KFMod.KFGibBrain',boneCoords.Origin, HitFX[SimHitFxTicker].rotDir, GibPerterbation, 250 ) ;
KFSpawnGiblet( class 'KFMod.KFGibBrainb',boneCoords.Origin, HitFX[SimHitFxTicker].rotDir, GibPerterbation, 250 ) ;
KFSpawnGiblet( class 'KFMod.KFGibBrain',boneCoords.Origin, HitFX[SimHitFxTicker].rotDir, GibPerterbation, 250 ) ;
bLeftLegGibbed=true;
}
break;
|
||||
case RightThighBone:
if( !bRightLegGibbed )
{
SpawnSeveredGiblet( DetachedLegClass, boneCoords.Origin, HitFX[SimHitFxTicker].rotDir, GibPerterbation, GetBoneRotation(HitFX[SimHitFxTicker].bone) );
KFSpawnGiblet( class 'KFMod.KFGibBrain',boneCoords.Origin, HitFX[SimHitFxTicker].rotDir, GibPerterbation, 250 ) ;
KFSpawnGiblet( class 'KFMod.KFGibBrainb',boneCoords.Origin, HitFX[SimHitFxTicker].rotDir, GibPerterbation, 250 ) ;
KFSpawnGiblet( class 'KFMod.KFGibBrain',boneCoords.Origin, HitFX[SimHitFxTicker].rotDir, GibPerterbation, 250 ) ;
bRightLegGibbed=true;
}
break;
|
||||
case LeftFArmBone:
if( !bLeftArmGibbed )
{
SpawnSeveredGiblet( DetachedArmClass, boneCoords.Origin, HitFX[SimHitFxTicker].rotDir, GibPerterbation, GetBoneRotation(HitFX[SimHitFxTicker].bone) );
KFSpawnGiblet( class 'KFMod.KFGibBrain',boneCoords.Origin, HitFX[SimHitFxTicker].rotDir, GibPerterbation, 250 ) ;
KFSpawnGiblet( class 'KFMod.KFGibBrainb',boneCoords.Origin, HitFX[SimHitFxTicker].rotDir, GibPerterbation, 250 ) ;;
bLeftArmGibbed=true;
}
break;
|
||||
case RightFArmBone:
if( !bRightArmGibbed )
{
SpawnSeveredGiblet( DetachedSpecialArmClass, boneCoords.Origin, HitFX[SimHitFxTicker].rotDir, GibPerterbation, GetBoneRotation(HitFX[SimHitFxTicker].bone) );
KFSpawnGiblet( class 'KFMod.KFGibBrain',boneCoords.Origin, HitFX[SimHitFxTicker].rotDir, GibPerterbation, 250 ) ;
KFSpawnGiblet( class 'KFMod.KFGibBrainb',boneCoords.Origin, HitFX[SimHitFxTicker].rotDir, GibPerterbation, 250 ) ;
bRightArmGibbed=true;
}
break;
|
||||
case 'head':
if( !bHeadGibbed )
{
if ( HitFX[SimHitFxTicker].damtype == class'DamTypeDecapitation' )
{
DecapFX( boneCoords.Origin, HitFX[SimHitFxTicker].rotDir, false);
}
else if( HitFX[SimHitFxTicker].damtype == class'DamTypeProjectileDecap' )
{
DecapFX( boneCoords.Origin, HitFX[SimHitFxTicker].rotDir, false, true);
}
else if( HitFX[SimHitFxTicker].damtype == class'DamTypeMeleeDecapitation' )
{
DecapFX( boneCoords.Origin, HitFX[SimHitFxTicker].rotDir, true);
}
|
||||
bHeadGibbed=true;
}
break;
}
|
||||
if( HitFX[SimHitFXTicker].bone != 'Spine' && HitFX[SimHitFXTicker].bone != FireRootBone &&
HitFX[SimHitFXTicker].bone != 'head' && Health <=0 )
HideBone(HitFX[SimHitFxTicker].bone);
}
|
||||
}
|
||||
}
|
||||
// Maybe spawn some chunks when the player gets obliterated
|
||||
simulated function SpawnGibs(Rotator HitRotation, float ChunkPerterbation)
|
||||
{
|
||||
if ( ExhaustEffect != none )
|
||||
{
ExhaustEffect.Destroy();
ExhaustEffect = none;
bNoExhaustRespawn = true;
|
||||
}
|
||||
super.SpawnGibs(HitRotation,ChunkPerterbation);
|
||||
}
|
||||
static simulated function PreCacheMaterials(LevelInfo myLevel)
|
||||
{//should be derived and used.
|
||||
myLevel.AddPrecacheMaterial(Combiner'KF_Specimens_Trip_T.scrake_env_cmb');
|
||||
myLevel.AddPrecacheMaterial(Texture'KF_Specimens_Trip_T.scrake_diff');
|
||||
myLevel.AddPrecacheMaterial(Texture'KF_Specimens_Trip_T.scrake_spec');
|
||||
myLevel.AddPrecacheMaterial(Material'KF_Specimens_Trip_T.scrake_saw_panner');
|
||||
myLevel.AddPrecacheMaterial(Material'KF_Specimens_Trip_T.scrake_FB');
|
||||
myLevel.AddPrecacheMaterial(Texture'KF_Specimens_Trip_T.Chainsaw_blade_diff');
|
||||
}
|
||||
defaultproperties
|
||||
{
SawAttackLoopSound=Sound'KF_BaseScrake.Chainsaw.Scrake_Chainsaw_Impale'
ChainSawOffSound=SoundGroup'KF_ChainsawSnd.Chainsaw_Deselect'
remainingStuns=1
stunLoopStart=0.240000
stunLoopEnd=0.820000
idleInsertFrame=0.900000
EventClasses(0)="NicePack.NiceZombieScrake"
MoanVoice=SoundGroup'KF_EnemiesFinalSnd.Scrake.Scrake_Talk'
MeleeAttackHitSound=SoundGroup'KF_EnemiesFinalSnd.Scrake.Scrake_Chainsaw_HitPlayer'
JumpSound=SoundGroup'KF_EnemiesFinalSnd.Scrake.Scrake_Jump'
DetachedArmClass=Class'KFChar.SeveredArmScrake'
DetachedLegClass=Class'KFChar.SeveredLegScrake'
DetachedHeadClass=Class'KFChar.SeveredHeadScrake'
DetachedSpecialArmClass=Class'KFChar.SeveredArmScrakeSaw'
HitSound(0)=SoundGroup'KF_EnemiesFinalSnd.Scrake.Scrake_Pain'
DeathSound(0)=SoundGroup'KF_EnemiesFinalSnd.Scrake.Scrake_Death'
ChallengeSound(0)=SoundGroup'KF_EnemiesFinalSnd.Scrake.Scrake_Challenge'
ChallengeSound(1)=SoundGroup'KF_EnemiesFinalSnd.Scrake.Scrake_Challenge'
ChallengeSound(2)=SoundGroup'KF_EnemiesFinalSnd.Scrake.Scrake_Challenge'
ChallengeSound(3)=SoundGroup'KF_EnemiesFinalSnd.Scrake.Scrake_Challenge'
ControllerClass=Class'NicePack.NiceZombieScrakeController'
AmbientSound=Sound'KF_BaseScrake.Chainsaw.Scrake_Chainsaw_Idle'
Mesh=SkeletalMesh'KF_Freaks_Trip.Scrake_Freak'
Skins(0)=Shader'KF_Specimens_Trip_T.scrake_FB'
Skins(1)=TexPanner'KF_Specimens_Trip_T.scrake_saw_panner'
|
||||
}
|
||||
24
sources/Zeds/Nice/NiceZombieScrakeBase.uc
Normal file
24
sources/Zeds/Nice/NiceZombieScrakeBase.uc
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
// Chainsaw Zombie Monster for KF Invasion gametype
|
||||
// He's not quite as speedy as the other Zombies, But his attacks are TRULY damaging.
|
||||
class NiceZombieScrakeBase extends NiceMonster
|
||||
abstract;
|
||||
#exec OBJ LOAD FILE=
|
||||
var(Sounds) sound SawAttackLoopSound; // THe sound for the saw revved up, looping
|
||||
var(Sounds) sound ChainSawOffSound; //The sound of this zombie dieing without a head
|
||||
var bool bCharging; // Scrake charges when his health gets low
|
||||
var() float AttackChargeRate; // Ratio to increase scrake movement speed when charging and attacking
|
||||
// Exhaust effects
|
||||
var() class<VehicleExhaustEffect> ExhaustEffectClass; // Effect class for the exhaust emitter
|
||||
var() VehicleExhaustEffect ExhaustEffect;
|
||||
var bool bNoExhaustRespawn;
|
||||
replication
|
||||
{
|
||||
reliable if(Role == ROLE_Authority)
bCharging;
|
||||
}
|
||||
//-------------------------------------------------------------------------------
|
||||
// NOTE: All Code resides in the child class(this class was only created to
|
||||
// eliminate hitching caused by loading default properties during play)
|
||||
//-------------------------------------------------------------------------------
|
||||
defaultproperties
|
||||
{
AttackChargeRate=2.500000
ExhaustEffectClass=Class'KFMod.ChainsawExhaust'
fuelRatio=0.400000
clientHeadshotScale=1.500000
MeleeAnims(0)="SawZombieAttack1"
MeleeAnims(1)="SawZombieAttack2"
StunsRemaining=1
BleedOutDuration=6.000000
ZapThreshold=1.250000
ZappedDamageMod=1.250000
bHarpoonToBodyStuns=False
DamageToMonsterScale=8.000000
ZombieFlag=3
MeleeDamage=20
damageForce=-75000
bFatAss=True
KFRagdollName="Scrake_Trip"
bMeleeStunImmune=True
Intelligence=BRAINS_Mammal
bUseExtendedCollision=True
ColOffset=(Z=55.000000)
ColRadius=29.000000
ColHeight=18.000000
SeveredArmAttachScale=1.100000
SeveredLegAttachScale=1.100000
PlayerCountHealthScale=0.500000
PoundRageBumpDamScale=0.010000
OnlineHeadshotOffset=(X=22.000000,Y=5.000000,Z=58.000000)
OnlineHeadshotScale=1.500000
HeadHealth=650.000000
PlayerNumHeadHealthScale=0.300000
MotionDetectorThreat=3.000000
ScoringValue=75
IdleHeavyAnim="SawZombieIdle"
IdleRifleAnim="SawZombieIdle"
MeleeRange=40.000000
GroundSpeed=85.000000
WaterSpeed=75.000000
HealthMax=1000.000000
Health=1000
HeadHeight=2.200000
MenuName="Nice Scrake"
MovementAnims(0)="SawZombieWalk"
MovementAnims(1)="SawZombieWalk"
MovementAnims(2)="SawZombieWalk"
MovementAnims(3)="SawZombieWalk"
WalkAnims(0)="SawZombieWalk"
WalkAnims(1)="SawZombieWalk"
WalkAnims(2)="SawZombieWalk"
WalkAnims(3)="SawZombieWalk"
IdleCrouchAnim="SawZombieIdle"
IdleWeaponAnim="SawZombieIdle"
IdleRestAnim="SawZombieIdle"
DrawScale=1.050000
PrePivot=(Z=3.000000)
SoundVolume=175
SoundRadius=100.000000
Mass=500.000000
RotationRate=(Yaw=45000,Roll=0)
|
||||
}
|
||||
43
sources/Zeds/Nice/NiceZombieScrakeController.uc
Normal file
43
sources/Zeds/Nice/NiceZombieScrakeController.uc
Normal file
|
|
@ -0,0 +1,43 @@
|
|||
class NiceZombieScrakeController extends NiceMonsterController;
|
||||
// Custom Zombie Thinkerating
|
||||
// By : Alex
|
||||
var bool bDoneSpottedCheck;
|
||||
// Never do that, you too cool
|
||||
function GetOutOfTheWayOfShot(vector ShotDirection, vector ShotOrigin){}
|
||||
state ZombieHunt
|
||||
{
|
||||
event SeePlayer(Pawn SeenPlayer)
|
||||
{
if ( !bDoneSpottedCheck && PlayerController(SeenPlayer.Controller) != none )
{
// 25% chance of first player to see this Scrake saying something
if ( !KFGameType(Level.Game).bDidSpottedScrakeMessage && FRand() < 0.25 )
{
PlayerController(SeenPlayer.Controller).Speech('AUTO', 14, "");
KFGameType(Level.Game).bDidSpottedScrakeMessage = true;
}
|
||||
bDoneSpottedCheck = true;
}
|
||||
super.SeePlayer(SeenPlayer);
|
||||
}
|
||||
}
|
||||
function TimedFireWeaponAtEnemy()
|
||||
{
|
||||
if ( (Enemy == none) || FireWeaponAt(Enemy) )
SetCombatTimer();
|
||||
else SetTimer(0.01, True);
|
||||
}
|
||||
state ZombieCharge
|
||||
{
|
||||
// Don't do this in this state
|
||||
function GetOutOfTheWayOfShot(vector ShotDirection, vector ShotOrigin){}
|
||||
function bool StrafeFromDamage(float Damage, class<DamageType> DamageType, bool bFindDest)
|
||||
{
return false;
|
||||
}
|
||||
function bool TryStrafe(vector sideDir)
|
||||
{
return false;
|
||||
}
|
||||
function Timer()
|
||||
{
Disable('NotifyBump');
Target = Enemy;
TimedFireWeaponAtEnemy();
|
||||
}
|
||||
WaitForAnim:
|
||||
While( Monster(Pawn).bShotAnim )
Sleep(0.25);
|
||||
if ( !FindBestPathToward(Enemy, false,true) )
GotoState('ZombieRestFormation');
|
||||
Moving:
|
||||
MoveToward(Enemy);
|
||||
WhatToDoNext(17);
|
||||
if ( bSoaking )
SoakStop("STUCK IN CHARGING!");
|
||||
}
|
||||
defaultproperties
|
||||
{
|
||||
}
|
||||
221
sources/Zeds/Nice/NiceZombieShiver.uc
Normal file
221
sources/Zeds/Nice/NiceZombieShiver.uc
Normal file
|
|
@ -0,0 +1,221 @@
|
|||
// Different naming scheme 'cause kf-scrntestingrounds has a stupid restriction on what zeds can be used in it's spawns
|
||||
class NiceZombieShiver extends NiceZombieShiverBase;
|
||||
var float TeleportBlockTime;
|
||||
var float HeadOffsetY;
|
||||
var transient bool bRunning, bClientRunning;
|
||||
replication
|
||||
{
|
||||
reliable if ( Role == ROLE_Authority)
bRunning;
|
||||
}
|
||||
simulated function PostNetReceive()
|
||||
{
|
||||
super.PostNetReceive();
|
||||
if( bClientRunning != bRunning )
|
||||
{
bClientRunning = bRunning;
if( bRunning ) {
MovementAnims[0] = RunAnim;
}
else {
MovementAnims[0] = WalkAnim;
}
|
||||
}
|
||||
}
|
||||
simulated function PostBeginPlay()
|
||||
{
|
||||
Super.PostBeginPlay();
|
||||
if (Level.NetMode != NM_DedicatedServer)
|
||||
{
MatAlphaSkin = ColorModifier(Level.ObjectPool.AllocateObject(class'ColorModifier'));
if (MatAlphaSkin != none)
{
MatAlphaSkin.Color = class'Canvas'.static.MakeColor(255, 255, 255, 255);
MatAlphaSkin.RenderTwoSided = false;
MatAlphaSkin.AlphaBlend = true;
MatAlphaSkin.Material = Skins[0];
Skins[0] = MatAlphaSkin;
}
|
||||
}
|
||||
}
|
||||
simulated function Destroyed()
|
||||
{
|
||||
if (Level.NetMode != NM_DedicatedServer && MatAlphaSkin != none)
|
||||
{
Skins[0] = default.Skins[0];
Level.ObjectPool.FreeObject(MatAlphaSkin);
|
||||
}
|
||||
Super.Destroyed();
|
||||
}
|
||||
// Overridden to add HeadOffsetY
|
||||
function bool IsHeadShot(vector loc, vector ray, float AdditionalScale)
|
||||
{
|
||||
local coords C;
|
||||
local vector HeadLoc, B, M, diff;
|
||||
local float t, DotMM, Distance;
|
||||
local int look;
|
||||
local bool bUseAltHeadShotLocation;
|
||||
local bool bWasAnimating;
|
||||
if (HeadBone == '')
return False;
|
||||
// If we are a dedicated server estimate what animation is most likely playing on the client
|
||||
if (Level.NetMode == NM_DedicatedServer)
|
||||
{
if (Physics == PHYS_Falling)
PlayAnim(AirAnims[0], 1.0, 0.0);
else if (Physics == PHYS_Walking)
{
// Only play the idle anim if we're not already doing a different anim.
// This prevents anims getting interrupted on the server and borking things up - Ramm
|
||||
if( !IsAnimating(0) && !IsAnimating(1) )
{
if (bIsCrouched)
{
PlayAnim(IdleCrouchAnim, 1.0, 0.0);
}
else
{
bUseAltHeadShotLocation=true;
}
}
else
{
bWasAnimating = true;
}
|
||||
if ( bDoTorsoTwist )
{
SmoothViewYaw = Rotation.Yaw;
SmoothViewPitch = ViewPitch;
|
||||
look = (256 * ViewPitch) & 65535;
if (look > 32768)
look -= 65536;
|
||||
SetTwistLook(0, look);
}
}
else if (Physics == PHYS_Swimming)
PlayAnim(SwimAnims[0], 1.0, 0.0);
|
||||
if( !bWasAnimating )
{
SetAnimFrame(0.5);
}
|
||||
}
|
||||
if( bUseAltHeadShotLocation )
|
||||
{
HeadLoc = Location + (OnlineHeadshotOffset >> Rotation);
AdditionalScale *= OnlineHeadshotScale;
|
||||
}
|
||||
else
|
||||
{
C = GetBoneCoords(HeadBone);
|
||||
HeadLoc = C.Origin + (HeadHeight * HeadScale * AdditionalScale * C.XAxis)
+ HeadOffsetY * C.YAxis;
|
||||
}
|
||||
//ServerHeadLocation = HeadLoc;
|
||||
// Express snipe trace line in terms of B + tM
|
||||
B = loc;
|
||||
M = ray * (2.0 * CollisionHeight + 2.0 * CollisionRadius);
|
||||
// Find Point-Line Squared Distance
|
||||
diff = HeadLoc - B;
|
||||
t = M Dot diff;
|
||||
if (t > 0)
|
||||
{
DotMM = M dot M;
if (t < DotMM)
{
t = t / DotMM;
diff = diff - (t * M);
}
else
{
t = 1;
diff -= M;
}
|
||||
}
|
||||
else
t = 0;
|
||||
Distance = Sqrt(diff Dot diff);
|
||||
return (Distance < (HeadRadius * HeadScale * AdditionalScale));
|
||||
}
|
||||
function bool FlipOver()
|
||||
{
|
||||
if ( super.FlipOver() ) {
TeleportBlockTime = Level.TimeSeconds + 3.0; // can't teleport during stun
// do not rotate while stunned
Controller.Focus = none;
Controller.FocalPoint = Location + 512*vector(Rotation);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
simulated function StopBurnFX()
|
||||
{
|
||||
if (bBurnApplied)
|
||||
{
MatAlphaSkin.Material = Texture'PatchTex.Common.ZedBurnSkin';
Skins[0] = MatAlphaSkin;
|
||||
}
|
||||
Super.StopBurnFX();
|
||||
}
|
||||
function RangedAttack(Actor A)
|
||||
{
|
||||
if (bShotAnim || Physics == PHYS_Swimming)
return;
|
||||
else if (CanAttack(A))
|
||||
{
bShotAnim = true;
SetAnimAction('Claw');
return;
|
||||
}
|
||||
}
|
||||
state Running
|
||||
{
|
||||
function Tick(float Delta)
|
||||
{
Global.Tick(Delta);
if (RunUntilTime < Level.TimeSeconds)
GotoState('');
GroundSpeed = GetOriginalGroundSpeed();
|
||||
}
|
||||
function BeginState()
|
||||
{
bRunning = true;
RunUntilTime = Level.TimeSeconds + PeriodRunBase + FRand() * PeriodRunRan;
MovementAnims[0] = RunAnim;
|
||||
}
|
||||
function EndState()
|
||||
{
bRunning = false;
GroundSpeed = global.GetOriginalGroundSpeed();
RunCooldownEnd = Level.TimeSeconds + PeriodRunCoolBase + FRand() * PeriodRunCoolRan;
MovementAnims[0] = WalkAnim;
|
||||
}
|
||||
function float GetOriginalGroundSpeed()
|
||||
{
return global.GetOriginalGroundSpeed() * 2.5;
|
||||
}
|
||||
function bool CanSpeedAdjust()
|
||||
{
return false;
|
||||
}
|
||||
}
|
||||
/*function TakeDamage(int Damage, Pawn InstigatedBy, Vector HitLocation, Vector Momentum, class<DamageType> DamType, optional int HitIndex)
|
||||
{
|
||||
if (InstigatedBy == none || class<KFWeaponDamageType>(DamType) == none)
Super(Monster).TakeDamage(Damage, instigatedBy, hitLocation, momentum, DamType); // skip none-reference error
|
||||
else
Super(KFMonster).TakeDamage(Damage, instigatedBy, hitLocation, momentum, DamType);
|
||||
}*/
|
||||
// returns true also for KnockDown (stun) animation -- PooSH
|
||||
simulated function bool AnimNeedsWait(name TestAnim)
|
||||
{
|
||||
if( TestAnim == 'DoorBash' || TestAnim == 'KnockDown' )
|
||||
{
return true;
|
||||
}
|
||||
return ExpectingChannel == 0;
|
||||
}
|
||||
simulated function float GetOriginalGroundSpeed()
|
||||
{
|
||||
local float result;
|
||||
result = OriginalGroundSpeed;
|
||||
if( bZedUnderControl )
result *= 1.25;
return result;
|
||||
}
|
||||
simulated function HandleAnimation(float Delta)
|
||||
{
|
||||
// hehehe
|
||||
}
|
||||
simulated function Tick(float Delta)
|
||||
{
|
||||
Super.Tick(Delta);
|
||||
if (Health > 0 && !bBurnApplied)
|
||||
{
if (Level.NetMode != NM_DedicatedServer)
HandleAnimation(Delta);
// Handle targetting
if (Level.NetMode != NM_Client && !bDecapitated)
{
if (Controller == none || Controller.Target == none || !Controller.LineOfSightTo(Controller.Target))
{
if (bCanSeeTarget) bCanSeeTarget = false;
}
else
{
if (!bCanSeeTarget)
{
bCanSeeTarget = true;
SeeTargetTime = Level.TimeSeconds;
}
else if (Level.TimeSeconds > SeeTargetTime + PeriodSeeTarget)
{
if (VSize(Controller.Target.Location - Location) < MaxTeleportDist)
{
if (VSize(Controller.Target.Location - Location) > MinTeleportDist || !Controller.ActorReachable(Controller.Target))
{
if (CanTeleport())
StartTelePort();
}
else
{
if (CanRun())
GotoState('Running');
}
}
}
}
}
|
||||
}
|
||||
// Handle client-side teleport variables
|
||||
if (!bBurnApplied)
|
||||
{
if (Level.NetMode != NM_DedicatedServer && OldFadeStage != FadeStage)
{
OldFadeStage = FadeStage;
if (FadeStage == 2)
AlphaFader = 0;
else
AlphaFader = 255;
}
// Handle teleporting
if (FadeStage == 1) // Fade out (pre-teleport)
{
AlphaFader = FMax(AlphaFader - Delta * 512, 0);
|
||||
if (Level.NetMode != NM_Client && AlphaFader == 0)
{
SetCollision(true, true);
FlashTeleport();
SetCollision(false, false);
FadeStage = 2;
}
}
else if (FadeStage == 2) // Fade in (post-teleport)
{
AlphaFader = FMin(AlphaFader + Delta * 512, 255);
if (Level.NetMode != NM_Client && AlphaFader == 255)
{
FadeStage = 0;
SetCollision(true, true);
GotoState('Running');
}
}
|
||||
if (Level.NetMode != NM_DedicatedServer && ColorModifier(Skins[0]) != none)
ColorModifier(Skins[0]).Color.A = AlphaFader;
|
||||
}
|
||||
}
|
||||
//can't teleport if set on fire
|
||||
function bool CanTeleport()
|
||||
{
|
||||
return !bFlashTeleporting && !bOnFire && Physics == PHYS_Walking && Level.TimeSeconds > TeleportBlockTime
&& LastFlashTime + 7.5 < Level.TimeSeconds && !bIsStunned;
|
||||
}
|
||||
function bool CanRun()
|
||||
{
|
||||
local float distanceToTargetSquared;
|
||||
if(controller == none) return false;
|
||||
if(controller.focus != none){
distanceToTargetSquared = VSize(controller.focus.location - location);
if(distanceToTargetSquared > 900 * 2500) // (30 * 50)^2 / 30 meters
return false;
|
||||
}
|
||||
return (!bFlashTeleporting && !IsInState('Running') && RunCooldownEnd < Level.TimeSeconds);
|
||||
}
|
||||
function StartTeleport()
|
||||
{
|
||||
FadeStage = 1;
|
||||
AlphaFader = 255;
|
||||
SetCollision(false, false);
|
||||
bFlashTeleporting = true;
|
||||
}
|
||||
function FlashTeleport()
|
||||
{
|
||||
local Actor Target;
|
||||
local vector OldLoc;
|
||||
local vector NewLoc;
|
||||
local vector HitLoc;
|
||||
local vector HitNorm;
|
||||
local rotator RotOld;
|
||||
local rotator RotNew;
|
||||
local float LandTargetDist;
|
||||
local int iEndAngle;
|
||||
local int iAttempts;
|
||||
if (Controller == none || Controller.Target == none)
return;
|
||||
Target = Controller.Target;
|
||||
RotOld = rotator(Target.Location - Location);
|
||||
RotNew = RotOld;
|
||||
OldLoc = Location;
|
||||
for (iEndAngle = 0; iEndAngle < MaxTeleportAngles; iEndAngle++)
|
||||
{
RotNew = RotOld;
RotNew.Yaw += iEndAngle * (65536 / MaxTelePortAngles);
for (iAttempts = 0; iAttempts < MaxTeleportAttempts; iAttempts++)
{
LandTargetDist = Target.CollisionRadius + CollisionRadius +
MinLandDist + (MaxLandDist - MinLandDist) * (iAttempts / (MaxTeleportAttempts - 1.0));
|
||||
NewLoc = Target.Location - vector(RotNew) * LandTargetDist; // Target.Location - Location
NewLoc.Z = Target.Location.Z;
|
||||
if (Trace(HitLoc, HitNorm, NewLoc + vect(0, 0, -500), NewLoc) != none)
NewLoc.Z = HitLoc.Z + CollisionHeight;
|
||||
// Try a new location
if (SetLocation(NewLoc))
{
SetPhysics(PHYS_Walking);
if (Controller.PointReachable(Target.Location))
{
Velocity = vect(0, 0, 0);
Acceleration = vect(0, 0, 0);
SetRotation(rotator(Target.Location - Location));
PlaySound(Sound'ScrnZedPack_S.Shiver.ShiverWarpGroup', SLOT_Interact, 4.0);
Controller.GotoState('');
MonsterController(Controller).WhatToDoNext(0);
goto Teleported;
}
}
// Reset location
SetLocation(OldLoc);
}
|
||||
}
|
||||
Teleported:
|
||||
bFlashTeleporting = false;
|
||||
LastFlashTime = Level.TimeSeconds;
|
||||
}
|
||||
function Died(Controller Killer, class<DamageType> damageType, vector HitLocation)
|
||||
{
|
||||
// (!)
|
||||
Super.Died(Killer, damageType, HitLocation);
|
||||
}
|
||||
function RemoveHead()
|
||||
{
|
||||
local class<KFWeaponDamageType> KFDamType;
|
||||
KFDamType = class<KFWeaponDamageType>(LastDamagedByType);
|
||||
if ( KFDamType != none && !KFDamType.default.bIsPowerWeapon
&& !KFDamType.default.bSniperWeapon && !KFDamType.default.bIsMeleeDamage
&& !KFDamType.default.bIsExplosive && !KFDamType.default.bDealBurningDamage
&& !ClassIsChildOf(KFDamType, class'DamTypeDualies')
&& !ClassIsChildOf(KFDamType, class'DamTypeMK23Pistol')
&& !ClassIsChildOf(KFDamType, class'DamTypeMagnum44Pistol') )
|
||||
{
LastDamageAmount *= 3.5; //significantly raise decapitation bonus for Assault Rifles
|
||||
//award shiver kill on decap for Commandos
if ( KFPawn(LastDamagedBy)!=none && KFPlayerController(LastDamagedBy.Controller) != none
&& KFSteamStatsAndAchievements(KFPlayerController(LastDamagedBy.Controller).SteamStatsAndAchievements) != none )
{
KFDamType.Static.AwardKill(
KFSteamStatsAndAchievements(KFPlayerController(LastDamagedBy.Controller).SteamStatsAndAchievements),
KFPlayerController(LastDamagedBy.Controller), self);
}
|
||||
}
|
||||
if (IsInState('Running'))
GotoState('');
|
||||
Super(NiceMonster).RemoveHead();
|
||||
}
|
||||
function bool CheckMiniFlinch(int flinchScore, Pawn instigatedBy, Vector hitLocation, Vector momentum, class<NiceWeaponDamageType> damageType, float headshotLevel, KFPlayerReplicationInfo KFPRI){
|
||||
if(IsInState('Running'))
return false;
|
||||
return super.CheckMiniFlinch(flinchScore, instigatedBy, hitLocation, momentum, damageType, headshotLevel, KFPRI);
|
||||
}
|
||||
simulated function int DoAnimAction( name AnimName )
|
||||
{
|
||||
if (AnimName=='Claw' || AnimName=='Claw2' || AnimName=='Claw3')
|
||||
{
AnimBlendParams(1, 1.0, 0.1,, FireRootBone);
PlayAnim(AnimName,, 0.1, 1);
return 1;
|
||||
}
|
||||
return Super.DoAnimAction(AnimName);
|
||||
}
|
||||
defaultproperties
|
||||
{
HeadOffsetY=-3.000000
idleInsertFrame=0.468000
PlayerCountHealthScale=0.200000
OnlineHeadshotOffset=(X=19.000000,Z=39.000000)
ScoringValue=15
HealthMax=300.000000
Health=300
HeadRadius=8.000000
HeadHeight=3.000000
|
||||
}
|
||||
39
sources/Zeds/Nice/NiceZombieShiverBase.uc
Normal file
39
sources/Zeds/Nice/NiceZombieShiverBase.uc
Normal file
|
|
@ -0,0 +1,39 @@
|
|||
class NiceZombieShiverBase extends NiceMonster;
|
||||
#exec load obj file=ScrnZedPack_T.utx
|
||||
#exec load obj file=ScrnZedPack_S.uax
|
||||
#exec load obj file=ScrnZedPack_A.ukx
|
||||
var name WalkAnim, RunAnim;
|
||||
// Head twitch
|
||||
var rotator CurHeadRot, NextHeadRot, HeadRot;
|
||||
var float NextHeadTime;
|
||||
var float MaxHeadTime;
|
||||
var float MaxTilt, MaxTurn;
|
||||
// Targetting, charging
|
||||
var bool bDelayedReaction;
|
||||
var bool bCanSeeTarget;
|
||||
var float SeeTargetTime;
|
||||
var float RunUntilTime;
|
||||
var float RunCooldownEnd;
|
||||
var float PeriodSeeTarget;
|
||||
var float PeriodRunBase;
|
||||
var float PeriodRunRan;
|
||||
var float PeriodRunCoolBase;
|
||||
var float PeriodRunCoolRan;
|
||||
// Teleporting
|
||||
var byte FadeStage;
|
||||
var byte OldFadeStage;
|
||||
var float AlphaFader;
|
||||
var bool bFlashTeleporting;
|
||||
var float LastFlashTime;
|
||||
var float MinTeleportDist, MaxTeleportDist;
|
||||
var float MinLandDist, MaxLandDist; // How close we can teleport to the target (collision cylinders are taken into account)
|
||||
var int MaxTeleportAttempts; // Attempts per angle
|
||||
var int MaxTeleportAngles;
|
||||
var ColorModifier MatAlphaSkin;
|
||||
replication
|
||||
{
|
||||
reliable if (Role == ROLE_Authority)
FadeStage;
|
||||
}
|
||||
defaultproperties
|
||||
{
WalkAnim="ClotWalk"
RunAnim="Run"
MaxHeadTime=0.100000
MaxTilt=10000.000000
MaxTurn=20000.000000
bDelayedReaction=True
PeriodSeeTarget=2.000000
PeriodRunBase=4.000000
PeriodRunRan=4.000000
PeriodRunCoolBase=4.000000
PeriodRunCoolRan=3.000000
AlphaFader=255.000000
MinTeleportDist=550.000000
MaxTeleportDist=2000.000000
MinLandDist=150.000000
MaxLandDist=500.000000
MaxTeleportAttempts=3
MaxTeleportAngles=3
fuelRatio=0.800000
clientHeadshotScale=1.400000
MoanVoice=SoundGroup'ScrnZedPack_S.Shiver.ShiverTalkGroup'
bCannibal=True
MeleeDamage=8
damageForce=5000
KFRagdollName="Clot_Trip"
JumpSound=SoundGroup'KF_EnemiesFinalSnd.clot.Clot_Jump'
CrispUpThreshhold=9
PuntAnim="ClotPunt"
Intelligence=BRAINS_Mammal
bUseExtendedCollision=True
ColOffset=(Z=48.000000)
ColRadius=25.000000
ColHeight=5.000000
ExtCollAttachBoneName="Collision_Attach"
SeveredArmAttachScale=0.800000
SeveredLegAttachScale=0.800000
SeveredHeadAttachScale=0.800000
DetachedArmClass=Class'ScrnZedPack.SeveredArmShiver'
DetachedLegClass=Class'ScrnZedPack.SeveredLegShiver'
DetachedHeadClass=Class'ScrnZedPack.SeveredHeadShiver'
OnlineHeadshotOffset=(X=20.000000,Z=37.000000)
OnlineHeadshotScale=1.300000
MotionDetectorThreat=0.340000
HitSound(0)=SoundGroup'ScrnZedPack_S.Shiver.ShiverPainGroup'
DeathSound(0)=SoundGroup'ScrnZedPack_S.Shiver.ShiverDeathGroup'
ChallengeSound(0)=SoundGroup'ScrnZedPack_S.Shiver.ShiverTalkGroup'
ChallengeSound(1)=SoundGroup'ScrnZedPack_S.Shiver.ShiverTalkGroup'
ChallengeSound(2)=SoundGroup'ScrnZedPack_S.Shiver.ShiverTalkGroup'
ChallengeSound(3)=SoundGroup'ScrnZedPack_S.Shiver.ShiverTalkGroup'
ScoringValue=7
GroundSpeed=100.000000
WaterSpeed=100.000000
AccelRate=1024.000000
JumpZ=340.000000
HealthMax=650.000000
Health=350
MenuName="Shiver"
MovementAnims(0)="ClotWalk"
AmbientSound=SoundGroup'ScrnZedPack_S.Shiver.ShiverIdleGroup'
Mesh=SkeletalMesh'ScrnZedPack_A.ShiverMesh'
DrawScale=1.100000
PrePivot=(Z=5.000000)
Skins(0)=Combiner'ScrnZedPack_T.Shiver.CmbRemoveAlpha'
RotationRate=(Yaw=45000,Roll=0)
|
||||
}
|
||||
145
sources/Zeds/Nice/NiceZombieSick.uc
Normal file
145
sources/Zeds/Nice/NiceZombieSick.uc
Normal file
|
|
@ -0,0 +1,145 @@
|
|||
// Zombie Monster for KF Invasion gametype
|
||||
class NiceZombieSick extends NiceZombieSickBase;
|
||||
#exec OBJ LOAD FILE=KF_EnemiesFinalSnd.uax
|
||||
#exec OBJ LOAD FILE=NicePackT.utx
|
||||
//----------------------------------------------------------------------------
|
||||
// NOTE: All Variables are declared in the base class to eliminate hitching
|
||||
//----------------------------------------------------------------------------
|
||||
var name SpitAnimation;
|
||||
var transient float NextVomitTime;
|
||||
function bool FlipOver(){
|
||||
return true;
|
||||
}
|
||||
// don't interrupt the bloat while he is puking
|
||||
simulated function bool HitCanInterruptAction(){
|
||||
if(bShotAnim)
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
function DoorAttack(Actor A)
|
||||
{
|
||||
if ( bShotAnim || Physics == PHYS_Swimming)
return;
|
||||
else if ( A!=none )
|
||||
{
bShotAnim = true;
if( !bDecapitated && bDistanceAttackingDoor )
{
SetAnimAction('ZombieBarf');
}
else
{
SetAnimAction('DoorBash');
GotoState('DoorBashing');
}
|
||||
}
|
||||
}
|
||||
function RangedAttack(Actor A)
|
||||
{
|
||||
local int LastFireTime;
|
||||
local float ChargeChance;
|
||||
if ( bShotAnim )
return;
|
||||
if ( Physics == PHYS_Swimming )
|
||||
{
SetAnimAction('Claw');
bShotAnim = true;
LastFireTime = Level.TimeSeconds;
|
||||
}
|
||||
else if ( VSize(A.Location - Location) < MeleeRange + CollisionRadius + A.CollisionRadius )
|
||||
{
bShotAnim = true;
LastFireTime = Level.TimeSeconds;
SetAnimAction('Claw');
//PlaySound(sound'Claw2s', SLOT_Interact); KFTODO: Replace this
Controller.bPreparingMove = true;
Acceleration = vect(0,0,0);
|
||||
}
|
||||
else if ( (KFDoorMover(A) != none || VSize(A.Location-Location) <= 250) && !bDecapitated )
|
||||
{
bShotAnim = true;
|
||||
// Decide what chance the bloat has of charging during a puke attack
if( Level.Game.GameDifficulty < 2.0 )
{
ChargeChance = 0.6;
}
else if( Level.Game.GameDifficulty < 4.0 )
{
ChargeChance = 0.8;
}
else if( Level.Game.GameDifficulty < 5.0 )
{
ChargeChance = 1.0;
}
else // Hardest difficulty
{
ChargeChance = 1.2;
}
|
||||
// Randomly do a moving attack so the player can't kite the zed
if( FRand() < ChargeChance )
{
SetAnimAction('ZombieBarfMoving');
RunAttackTimeout = GetAnimDuration('ZombieBarf', 0.5);
bMovingPukeAttack=true;
}
else
{
SetAnimAction('ZombieBarf');
Controller.bPreparingMove = true;
Acceleration = vect(0,0,0);
}
|
||||
// Randomly send out a message about Bloat Vomit burning(3% chance)
if ( FRand() < 0.03 && KFHumanPawn(A) != none && PlayerController(KFHumanPawn(A).Controller) != none )
{
PlayerController(KFHumanPawn(A).Controller).Speech('AUTO', 7, "");
}
|
||||
}
|
||||
}
|
||||
// Overridden to handle playing upper body only attacks when moving
|
||||
simulated event SetAnimAction(name NewAction)
|
||||
{
|
||||
local int meleeAnimIndex;
|
||||
local bool bWantsToAttackAndMove;
|
||||
if( NewAction=='' )
Return;
|
||||
bWantsToAttackAndMove = NewAction == 'ZombieBarfMoving';
|
||||
if( NewAction == 'Claw' )
|
||||
{
meleeAnimIndex = Rand(3);
NewAction = meleeAnims[meleeAnimIndex];
|
||||
}
|
||||
if( bWantsToAttackAndMove )
|
||||
{
ExpectingChannel = AttackAndMoveDoAnimAction(NewAction);
|
||||
}
|
||||
else
|
||||
{
ExpectingChannel = DoAnimAction(NewAction);
|
||||
}
|
||||
if( !bWantsToAttackAndMove && AnimNeedsWait(NewAction) )
|
||||
{
bWaitForAnim = true;
|
||||
}
|
||||
else
|
||||
{
bWaitForAnim = false;
|
||||
}
|
||||
if( Level.NetMode!=NM_Client )
|
||||
{
AnimAction = NewAction;
bResetAnimAct = True;
ResetAnimActTime = Level.TimeSeconds+0.2;
|
||||
}
|
||||
}
|
||||
// Handle playing the anim action on the upper body only if we're attacking and moving
|
||||
simulated function int AttackAndMoveDoAnimAction( name AnimName )
|
||||
{
|
||||
if( AnimName=='ZombieBarfMoving' )
|
||||
{
AnimBlendParams(1, 1.0, 0.0,, FireRootBone);
PlayAnim('ZombieBarf',, 0.1, 1);
|
||||
return 1;
|
||||
}
|
||||
return super.DoAnimAction( AnimName );
|
||||
}
|
||||
function PlayDyingSound()
|
||||
{
|
||||
if( Level.NetMode!=NM_Client )
|
||||
{
if ( bGibbed )
{
PlaySound(sound'KF_EnemiesFinalSnd.Bloat_DeathPop', SLOT_Pain,2.0,true,525);
return;
}
|
||||
if( bDecapitated )
{
PlaySound(HeadlessDeathSound, SLOT_Pain,1.30,true,525);
}
else
{
PlaySound(sound'KF_EnemiesFinalSnd.Bloat_DeathPop', SLOT_Pain,2.0,true,525);
}
|
||||
}
|
||||
}
|
||||
|
||||
// Barf Time.
|
||||
function SpawnTwoShots()
|
||||
{
|
||||
local vector X,Y,Z, FireStart;
|
||||
local rotator FireRotation;
|
||||
if( Controller!=none && KFDoorMover(Controller.Target)!=none )
|
||||
{
Controller.Target.TakeDamage(22,Self,Location,vect(0,0,0),Class'DamTypeVomit');
return;
|
||||
}
|
||||
GetAxes(Rotation,X,Y,Z);
|
||||
FireStart = Location+(vect(30,0,64) >> Rotation)*DrawScale;
|
||||
if ( !SavedFireProperties.bInitialized )
|
||||
{
SavedFireProperties.AmmoClass = Class'SkaarjAmmo';
SavedFireProperties.ProjectileClass = Class'NiceSickVomit';
SavedFireProperties.WarnTargetPct = 1;
SavedFireProperties.MaxRange = 600;
SavedFireProperties.bTossed = False;
SavedFireProperties.bTrySplash = False;
SavedFireProperties.bLeadTarget = True;
SavedFireProperties.bInstantHit = True;
SavedFireProperties.bInitialized = True;
|
||||
}
|
||||
// Turn off extra collision before spawning vomit, otherwise spawn fails
|
||||
ToggleAuxCollision(false);
|
||||
FireRotation = Controller.AdjustAim(SavedFireProperties,FireStart,600);
|
||||
Spawn(Class'NiceSickVomit',,,FireStart,FireRotation);
|
||||
FireStart-=(0.5*CollisionRadius*Y);
|
||||
FireRotation.Yaw -= 1200;
|
||||
spawn(Class'NiceSickVomit',,,FireStart, FireRotation);
|
||||
FireStart+=(CollisionRadius*Y);
|
||||
FireRotation.Yaw += 2400;
|
||||
spawn(Class'NiceSickVomit',,,FireStart, FireRotation);
|
||||
// Turn extra collision back on
|
||||
ToggleAuxCollision(true);
|
||||
}
|
||||
|
||||
|
||||
function BileBomb()
|
||||
{
|
||||
BloatJet = spawn(class'BileJet', self,,Location,Rotator(-PhysicsVolume.Gravity));
|
||||
}
|
||||
|
||||
State Dying
|
||||
{
|
||||
function tick(float deltaTime)
|
||||
{
|
||||
if (BloatJet != none)
|
||||
{
|
||||
BloatJet.SetLocation(location);
|
||||
BloatJet.SetRotation(GetBoneRotation(FireRootBone));
|
||||
}
|
||||
super.tick(deltaTime);
|
||||
}
|
||||
}
|
||||
function RemoveHead()
|
||||
{
|
||||
bCanDistanceAttackDoors = False;
|
||||
Super.RemoveHead();
|
||||
}
|
||||
static simulated function PreCacheMaterials(LevelInfo myLevel)
|
||||
{//should be derived and used.
|
||||
myLevel.AddPrecacheMaterial(Texture'NicePackT.MonsterSick.Sick_diffuse');
|
||||
myLevel.AddPrecacheMaterial(Combiner'NicePackT.MonsterSick.Sick_env');
|
||||
myLevel.AddPrecacheMaterial(Combiner'NicePackT.MonsterSick.Sick_cmb');
|
||||
}
|
||||
defaultproperties
|
||||
{
DetachedArmClass=Class'NicePack.NiceSeveredArmSick'
DetachedLegClass=Class'NicePack.NiceSeveredLegSick'
DetachedHeadClass=Class'NicePack.NiceSeveredHeadSick'
ControllerClass=Class'NicePack.NiceSickZombieController'
|
||||
}
|
||||
16
sources/Zeds/Nice/NiceZombieSickBase.uc
Normal file
16
sources/Zeds/Nice/NiceZombieSickBase.uc
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
// Zombie Monster for KF Invasion gametype
|
||||
class NiceZombieSickBase extends NiceMonster;
|
||||
var name WalkAnim, RunAnim;
|
||||
#exec OBJ LOAD FILE=KF_EnemiesFinalSnd.uax
|
||||
var BileJet BloatJet;
|
||||
var bool bPlayBileSplash;
|
||||
var bool bMovingPukeAttack;
|
||||
var float RunAttackTimeout;
|
||||
var() float AttackChargeRate;
|
||||
//-------------------------------------------------------------------------------
|
||||
// NOTE: All Code resides in the child class(this class was only created to
|
||||
// eliminate hitching caused by loading default properties during play)
|
||||
//-------------------------------------------------------------------------------
|
||||
defaultproperties
|
||||
{
AttackChargeRate=2.500000
StunThreshold=4.000000
fuelRatio=0.250000
bWeakHead=True
clientHeadshotScale=1.500000
MeleeAnims(0)="Strike"
MeleeAnims(1)="Strike"
MeleeAnims(2)="Strike"
MoanVoice=SoundGroup'KF_EnemiesFinalSnd.GoreFast.Gorefast_Talk'
BleedOutDuration=6.000000
ZombieFlag=3
MeleeDamage=14
damageForce=70000
bFatAss=True
KFRagdollName="Clot_Trip"
MeleeAttackHitSound=SoundGroup'KF_EnemiesFinalSnd.GoreFast.Gorefast_HitPlayer'
JumpSound=SoundGroup'KF_EnemiesFinalSnd.GoreFast.Gorefast_Jump'
PuntAnim="BloatPunt"
Intelligence=BRAINS_Stupid
bCanDistanceAttackDoors=True
bUseExtendedCollision=True
ColOffset=(Z=55.000000)
ColRadius=29.000000
ColHeight=18.000000
SeveredArmAttachScale=1.400000
SeveredLegAttachScale=1.400000
SeveredHeadAttachScale=1.400000
PlayerCountHealthScale=0.250000
HeadlessWalkAnims(0)="WalkF"
HeadlessWalkAnims(1)="WalkB"
HeadlessWalkAnims(2)="WalkL"
HeadlessWalkAnims(3)="WalkR"
BurningWalkFAnims(0)="WalkF"
BurningWalkFAnims(1)="WalkF"
BurningWalkFAnims(2)="WalkF"
BurningWalkAnims(0)="WalkB"
BurningWalkAnims(1)="WalkL"
BurningWalkAnims(2)="WalkR"
PoundRageBumpDamScale=0.010000
OnlineHeadshotOffset=(X=22.000000,Y=5.000000,Z=58.000000)
HeadHealth=50.000000
MotionDetectorThreat=3.000000
HitSound(0)=SoundGroup'KF_EnemiesFinalSnd.GoreFast.Gorefast_Pain'
DeathSound(0)=SoundGroup'KF_EnemiesFinalSnd.GoreFast.Gorefast_Death'
ChallengeSound(0)=SoundGroup'KF_EnemiesFinalSnd.GoreFast.Gorefast_Challenge'
ChallengeSound(1)=SoundGroup'KF_EnemiesFinalSnd.GoreFast.Gorefast_Challenge'
ChallengeSound(2)=SoundGroup'KF_EnemiesFinalSnd.GoreFast.Gorefast_Challenge'
ChallengeSound(3)=SoundGroup'KF_EnemiesFinalSnd.GoreFast.Gorefast_Challenge'
AmmunitionClass=Class'KFMod.BZombieAmmo'
ScoringValue=75
IdleHeavyAnim="Idle"
IdleRifleAnim="Idle"
RagdollLifeSpan=20.000000
RagDeathVel=150.000000
RagShootStrength=300.000000
RagSpinScale=12.500000
RagDeathUpKick=50.000000
MeleeRange=30.000000
GroundSpeed=175.000000
WaterSpeed=150.000000
HealthMax=925.000000
Health=925
HeadHeight=2.200000
HeadScale=1.610000
AmbientSoundScaling=8.000000
MenuName="Sick"
MovementAnims(0)="WalkF"
MovementAnims(1)="WalkB"
MovementAnims(2)="WalkL"
MovementAnims(3)="WalkR"
WalkAnims(1)="WalkB"
WalkAnims(2)="WalkL"
WalkAnims(3)="WalkR"
IdleCrouchAnim="Idle"
IdleWeaponAnim="Idle"
IdleRestAnim="Idle"
AmbientSound=Sound'KF_BaseGorefast.Gorefast_Idle'
Mesh=SkeletalMesh'NicePackA.MonsterSick.Sick'
DrawScale=1.100000
PrePivot=(Z=5.000000)
Skins(0)=Combiner'NicePackT.MonsterSick.Sick_cmb'
SoundVolume=200
Mass=400.000000
RotationRate=(Yaw=45000,Roll=0)
|
||||
}
|
||||
194
sources/Zeds/Nice/NiceZombieSiren.uc
Normal file
194
sources/Zeds/Nice/NiceZombieSiren.uc
Normal file
|
|
@ -0,0 +1,194 @@
|
|||
// Zombie Monster for KF Invasion gametype
|
||||
class NiceZombieSiren extends NiceZombieSirenBase;
|
||||
var float screamLength;
|
||||
var float screamStartTime;
|
||||
var int currScreamTiming;
|
||||
var int currentScreamID;
|
||||
var array<float> screamTimings;
|
||||
simulated function PostBeginPlay()
|
||||
{
|
||||
super.PostBeginPlay();
|
||||
screamLength = GetAnimDuration('Siren_Scream');
|
||||
}
|
||||
simulated event SetAnimAction(name NewAction)
|
||||
{
|
||||
local int meleeAnimIndex;
|
||||
if( NewAction=='' )
Return;
|
||||
if(NewAction == 'Claw')
|
||||
{
meleeAnimIndex = Rand(3);
NewAction = meleeAnims[meleeAnimIndex];
|
||||
}
|
||||
ExpectingChannel = DoAnimAction(NewAction);
|
||||
if( AnimNeedsWait(NewAction) )
|
||||
{
bWaitForAnim = true;
|
||||
}
|
||||
else
|
||||
{
bWaitForAnim = false;
|
||||
}
|
||||
if( Level.NetMode!=NM_Client )
|
||||
{
AnimAction = NewAction;
bResetAnimAct = True;
ResetAnimActTime = Level.TimeSeconds+0.3;
|
||||
}
|
||||
}
|
||||
simulated function bool AnimNeedsWait(name TestAnim)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
function bool FlipOver()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
function DoorAttack(Actor A)
|
||||
{
|
||||
if ( bShotAnim || Physics == PHYS_Swimming || bDecapitated || A==none )
return;
|
||||
bShotAnim = true;
|
||||
SetAnimAction('Siren_Scream');
|
||||
}
|
||||
function MakeNewScreamBall(){
|
||||
local int i;
|
||||
local NicePack niceMut;
|
||||
if(screamStartTime > 0){
niceMut = class'NicePack'.static.Myself(Level);
if(niceMut != none){
for(i = 0;i < niceMut.playersList.Length;i ++)
if(niceMut.playersList[i] != none && screamStartTime > 0)
niceMut.playersList[i].SpawnSirenBall(self);
}
|
||||
}
|
||||
}
|
||||
function DiscardCurrentScreamBall(){
|
||||
local int i;
|
||||
local NicePack niceMut;
|
||||
if(screamStartTime > 0){
niceMut = class'NicePack'.static.Myself(Level);
if(niceMut != none){
for(i = 0;i < niceMut.playersList.Length;i ++)
if(niceMut.playersList[i] != none)
niceMut.playersList[i].ClientRemoveSirenBall(currentScreamID);
}
screamStartTime = -1.0;
currScreamTiming = -1;
|
||||
}
|
||||
}
|
||||
function RangedAttack(Actor A)
|
||||
{
|
||||
local int LastFireTime;
|
||||
local float Dist;
|
||||
if ( bShotAnim )
return;
|
||||
Dist = VSize(A.Location - Location);
|
||||
if ( Physics == PHYS_Swimming )
|
||||
{
SetAnimAction('Claw');
bShotAnim = true;
LastFireTime = Level.TimeSeconds;
|
||||
}
|
||||
else if(Dist < MeleeRange + CollisionRadius + A.CollisionRadius && A != Self)
|
||||
{
bShotAnim = true;
LastFireTime = Level.TimeSeconds;
SetAnimAction('Claw');
Controller.bPreparingMove = true;
Acceleration = vect(0,0,0);
|
||||
}
|
||||
else if( Dist <= ScreamRadius && !bDecapitated && !bZapped )
|
||||
{
bShotAnim=true;
SetAnimAction('Siren_Scream');
if(screamStartTime > 0)
DiscardCurrentScreamBall();
currScreamTiming = 0;
screamStartTime = Level.TimeSeconds;
// Only stop moving if we are close
if( Dist < ScreamRadius * 0.25 )
{
Controller.bPreparingMove = true;
Acceleration = vect(0,0,0);
}
else
{
Acceleration = AccelRate * Normal(A.Location - Location);
}
|
||||
}
|
||||
}
|
||||
simulated function int DoAnimAction( name AnimName )
|
||||
{
|
||||
if( AnimName=='Siren_Scream' || AnimName=='Siren_Bite' || AnimName=='Siren_Bite2' )
|
||||
{
AnimBlendParams(1, 1.0, 0.0,, SpineBone1);
PlayAnim(AnimName,, 0.1, 1);
return 1;
|
||||
}
|
||||
PlayAnim(AnimName,,0.1);
|
||||
Return 0;
|
||||
}
|
||||
// Scream Time
|
||||
simulated function SpawnTwoShots(){
|
||||
if(bZapped)
return;
|
||||
if(Health > 0 && HeadHealth > 0 && !bIsStunned)
DoShakeEffect();
|
||||
if( Level.NetMode!=NM_Client )
|
||||
{
// Deal Actual Damage.
if(controller!=none && KFDoorMover(Controller.Target) != none)
Controller.Target.TakeDamage(ScreamDamage*0.6,Self,Location,vect(0,0,0),ScreamDamageType);
else HurtRadius(ScreamDamage ,ScreamRadius, ScreamDamageType, ScreamForce, Location);
if(screamStartTime > 0)
currScreamTiming ++;
else
Log("ERROR: unexpected siren scream happend!");
|
||||
}
|
||||
}
|
||||
// Shake nearby players screens
|
||||
simulated function DoShakeEffect()
|
||||
{
|
||||
local PlayerController PC;
|
||||
local NicePlayerController nicePlayer;
|
||||
local float Dist, scale, BlurScale;
|
||||
//viewshake
|
||||
if (Level.NetMode != NM_DedicatedServer)
|
||||
{
PC = Level.GetLocalPlayerController();
|
||||
nicePlayer = NicePlayerController(PC);
if (PC != none && PC.ViewTarget != none)
{
Dist = VSize(Location - PC.ViewTarget.Location);
if (Dist < ScreamRadius )
{
scale = (ScreamRadius - Dist) / (ScreamRadius);
scale *= ShakeEffectScalar;
|
||||
if(nicePlayer != none)
|
||||
scale *= nicePlayer.sirenScreamMod;
|
||||
BlurScale = scale;
|
||||
// Reduce blur if there is something between us and the siren
if( !FastTrace(PC.ViewTarget.Location,Location) )
{
scale *= 0.25;
BlurScale = scale;
}
else
{
if(nicePlayer != none)
|
||||
scale = Lerp(scale, MinShakeEffectScale * nicePlayer.sirenScreamMod, 1.0);
|
||||
else
|
||||
scale = Lerp(scale, MinShakeEffectScale, 1.0);
}
|
||||
PC.SetAmbientShake(Level.TimeSeconds + ShakeFadeTime, ShakeTime, OffsetMag * Scale, OffsetRate, RotMag * Scale, RotRate);
|
||||
if( KFHumanPawn(PC.ViewTarget) != none )
{
KFHumanPawn(PC.ViewTarget).AddBlur(ShakeTime, BlurScale * ScreamBlurScale);
}
|
||||
// 10% chance of player saying something about our scream
if ( Level != none && Level.Game != none && !KFGameType(Level.Game).bDidSirenScreamMessage && FRand() < 0.10 )
{
PC.Speech('AUTO', 16, "");
KFGameType(Level.Game).bDidSirenScreamMessage = true;
}
}
}
|
||||
}
|
||||
}
|
||||
simulated function HurtRadius(float DamageAmount, float DamageRadius, class<DamageType> DamageType, float Momentum, vector HitLocation)
|
||||
{
|
||||
local actor Victims;
|
||||
local float InitMomentum;
|
||||
local float damageScale, dist;
|
||||
local vector dir;
|
||||
local float UsedDamageAmount;
|
||||
local KFHumanPawn humanPawn;
|
||||
local class<NiceVeterancyTypes> niceVet;
|
||||
if(bHurtEntry || Health <= 0 || HeadHealth <= 0 || bIsStunned)
return;
|
||||
bHurtEntry = true;
|
||||
InitMomentum = Momentum;
|
||||
if(screamStartTime > 0 && currScreamTiming == 0)
MakeNewScreamBall();
|
||||
foreach VisibleCollidingActors(class 'Actor', Victims, DamageRadius, HitLocation){
Momentum = InitMomentum;
// don't let blast damage affect fluid - VisibleCollisingActors doesn't really work for them - jag
// Or Karma actors in this case. Self inflicted Death due to flying chairs is uncool for a zombie of your stature.
if((Victims != self) && !Victims.IsA('FluidSurfaceInfo') && !Victims.IsA('KFMonster') && !Victims.IsA('ExtendedZCollision')){
dir = Victims.Location - HitLocation;
dist = FMax(1,VSize(dir));
dir = dir/dist;
damageScale = 1 - FMax(0,(dist - Victims.CollisionRadius)/DamageRadius);
humanPawn = KFHumanPawn(Victims);
if(humanPawn == none) // If it aint human, don't pull the vortex crap on it.
Momentum = 0;
else{ // Also don't do it if we're sharpshooter with a right skill
niceVet = class'NiceVeterancyTypes'.static.GetVeterancy(humanPawn.PlayerReplicationInfo);
if(niceVet != none && !niceVet.static.CanBePulled(KFPlayerReplicationInfo(humanPawn.PlayerReplicationInfo)))
Momentum = 0;
}
|
||||
if(Victims.IsA('KFGlassMover')) // Hack for shattering in interesting ways.
UsedDamageAmount = 100000; // Siren always shatters glass
else
UsedDamageAmount = DamageAmount;
|
||||
Victims.TakeDamage(damageScale * UsedDamageAmount,Instigator, Victims.Location - 0.5 * (Victims.CollisionHeight + Victims.CollisionRadius) * dir, (damageScale * Momentum * dir), DamageType);
|
||||
if (Instigator != none && Vehicle(Victims) != none && Vehicle(Victims).Health > 0)
Vehicle(Victims).DriverRadiusDamage(UsedDamageAmount, DamageRadius, Instigator.Controller, DamageType, Momentum, HitLocation);
}
|
||||
}
|
||||
bHurtEntry = false;
|
||||
}
|
||||
// When siren loses her head she's got nothin' Kill her.
|
||||
function RemoveHead(){
|
||||
Super.RemoveHead();
|
||||
}
|
||||
simulated function Tick( float Delta )
|
||||
{
|
||||
local float currScreamTime;
|
||||
Super.Tick(Delta);
|
||||
if( bAboutToDie && Level.TimeSeconds>DeathTimer )
|
||||
{
if( Health>0 && Level.NetMode!=NM_Client )
KilledBy(LastDamagedBy);
bAboutToDie = False;
|
||||
}
|
||||
if( Role == ROLE_Authority )
|
||||
{
if( bShotAnim )
{
SetGroundSpeed(GetOriginalGroundSpeed() * 0.65);
|
||||
if( LookTarget!=none )
{
Acceleration = AccelRate * Normal(LookTarget.Location - Location);
}
}
else
{
SetGroundSpeed(GetOriginalGroundSpeed());
}
|
||||
}
|
||||
if(Role == ROLE_Authority && screamStartTime > 0){
currScreamTime = Level.TimeSeconds - screamStartTime;
if(currScreamTiming >= screamTimings.Length ||
currScreamTime - 0.1 > screamTimings[currScreamTiming] * screamLength){
DiscardCurrentScreamBall();
}
|
||||
}
|
||||
if(bOnFire && !bShotAnim)
RangedAttack(Self);
|
||||
}
|
||||
function PlayDyingSound()
|
||||
{
|
||||
if( !bAboutToDie )
Super.PlayDyingSound();
|
||||
}
|
||||
simulated function ProcessHitFX()
|
||||
{
|
||||
local Coords boneCoords;
|
||||
local class<xEmitter> HitEffects[4];
|
||||
local int i,j;
|
||||
local float GibPerterbation;
|
||||
if( (Level.NetMode == NM_DedicatedServer) || bSkeletized || (Mesh == SkeletonMesh))
|
||||
{
SimHitFxTicker = HitFxTicker;
return;
|
||||
}
|
||||
for ( SimHitFxTicker = SimHitFxTicker; SimHitFxTicker != HitFxTicker; SimHitFxTicker = (SimHitFxTicker + 1) % ArrayCount(HitFX) )
|
||||
{
j++;
if ( j > 30 )
{
SimHitFxTicker = HitFxTicker;
return;
}
|
||||
if( (HitFX[SimHitFxTicker].damtype == none) || (Level.bDropDetail && (Level.TimeSeconds - LastRenderTime > 3) && !IsHumanControlled()) )
continue;
|
||||
//log("Processing effects for damtype "$HitFX[SimHitFxTicker].damtype);
|
||||
if( HitFX[SimHitFxTicker].bone == 'obliterate' && !class'GameInfo'.static.UseLowGore())
{
SpawnGibs( HitFX[SimHitFxTicker].rotDir, 1);
bGibbed = true;
// Wait a tick on a listen server so the obliteration can replicate before the pawn is destroyed
if( Level.NetMode == NM_ListenServer )
{
bDestroyNextTick = true;
TimeSetDestroyNextTickTime = Level.TimeSeconds;
}
else
{
Destroy();
}
return;
}
|
||||
boneCoords = GetBoneCoords( HitFX[SimHitFxTicker].bone );
|
||||
if ( !Level.bDropDetail && !class'GameInfo'.static.NoBlood() && !bSkeletized && !class'GameInfo'.static.UseLowGore())
{
//AttachEmitterEffect( BleedingEmitterClass, HitFX[SimHitFxTicker].bone, boneCoords.Origin, HitFX[SimHitFxTicker].rotDir );
|
||||
HitFX[SimHitFxTicker].damtype.static.GetHitEffects( HitEffects, Health );
|
||||
if( !PhysicsVolume.bWaterVolume ) // don't attach effects under water
{
for( i = 0; i < ArrayCount(HitEffects); i++ )
{
if( HitEffects[i] == none )
continue;
|
||||
AttachEffect( HitEffects[i], HitFX[SimHitFxTicker].bone, boneCoords.Origin, HitFX[SimHitFxTicker].rotDir );
}
}
}
if ( class'GameInfo'.static.UseLowGore() )
HitFX[SimHitFxTicker].bSever = false;
|
||||
if( HitFX[SimHitFxTicker].bSever )
{
GibPerterbation = HitFX[SimHitFxTicker].damtype.default.GibPerterbation;
|
||||
switch( HitFX[SimHitFxTicker].bone )
{
case 'obliterate':
break;
|
||||
case LeftThighBone:
if( !bLeftLegGibbed )
{
SpawnSeveredGiblet( DetachedLegClass, boneCoords.Origin, HitFX[SimHitFxTicker].rotDir, GibPerterbation, GetBoneRotation(HitFX[SimHitFxTicker].bone) );
KFSpawnGiblet( class 'KFMod.KFGibBrain',boneCoords.Origin, HitFX[SimHitFxTicker].rotDir, GibPerterbation, 250 ) ;
KFSpawnGiblet( class 'KFMod.KFGibBrainb',boneCoords.Origin, HitFX[SimHitFxTicker].rotDir, GibPerterbation, 250 ) ;
KFSpawnGiblet( class 'KFMod.KFGibBrain',boneCoords.Origin, HitFX[SimHitFxTicker].rotDir, GibPerterbation, 250 ) ;
bLeftLegGibbed=true;
}
break;
|
||||
case RightThighBone:
if( !bRightLegGibbed )
{
SpawnSeveredGiblet( DetachedLegClass, boneCoords.Origin, HitFX[SimHitFxTicker].rotDir, GibPerterbation, GetBoneRotation(HitFX[SimHitFxTicker].bone) );
KFSpawnGiblet( class 'KFMod.KFGibBrain',boneCoords.Origin, HitFX[SimHitFxTicker].rotDir, GibPerterbation, 250 ) ;
KFSpawnGiblet( class 'KFMod.KFGibBrainb',boneCoords.Origin, HitFX[SimHitFxTicker].rotDir, GibPerterbation, 250 ) ;
KFSpawnGiblet( class 'KFMod.KFGibBrain',boneCoords.Origin, HitFX[SimHitFxTicker].rotDir, GibPerterbation, 250 ) ;
bRightLegGibbed=true;
}
break;
|
||||
case LeftFArmBone:
break;
|
||||
case RightFArmBone:
break;
|
||||
case 'head':
if( !bHeadGibbed )
{
if ( HitFX[SimHitFxTicker].damtype == class'DamTypeDecapitation' )
{
DecapFX( boneCoords.Origin, HitFX[SimHitFxTicker].rotDir, false);
}
else if( HitFX[SimHitFxTicker].damtype == class'DamTypeProjectileDecap' )
{
DecapFX( boneCoords.Origin, HitFX[SimHitFxTicker].rotDir, false, true);
}
else if( HitFX[SimHitFxTicker].damtype == class'DamTypeMeleeDecapitation' )
{
DecapFX( boneCoords.Origin, HitFX[SimHitFxTicker].rotDir, true);
}
|
||||
bHeadGibbed=true;
}
break;
}
|
||||
if( HitFX[SimHitFXTicker].bone != 'Spine' && HitFX[SimHitFXTicker].bone != FireRootBone &&
HitFX[SimHitFXTicker].bone != LeftFArmBone && HitFX[SimHitFXTicker].bone != RightFArmBone &&
HitFX[SimHitFXTicker].bone != 'head' && Health <=0 )
HideBone(HitFX[SimHitFxTicker].bone);
}
|
||||
}
|
||||
}
|
||||
static simulated function PreCacheMaterials(LevelInfo myLevel)
|
||||
{//should be derived and used.
|
||||
myLevel.AddPrecacheMaterial(Combiner'KF_Specimens_Trip_T.siren_cmb');
|
||||
myLevel.AddPrecacheMaterial(Combiner'KF_Specimens_Trip_T.siren_env_cmb');
|
||||
myLevel.AddPrecacheMaterial(Texture'KF_Specimens_Trip_T.siren_diffuse');
|
||||
myLevel.AddPrecacheMaterial(Texture'KF_Specimens_Trip_T.siren_hair');
|
||||
myLevel.AddPrecacheMaterial(Material'KF_Specimens_Trip_T.siren_hair_fb');
|
||||
}
|
||||
defaultproperties
|
||||
{
screamTimings(0)=0.420000
screamTimings(1)=0.510000
screamTimings(2)=0.590000
screamTimings(3)=0.670000
screamTimings(4)=0.760000
screamTimings(5)=0.840000
stunLoopStart=0.200000
stunLoopEnd=0.820000
idleInsertFrame=0.920000
EventClasses(0)="NicePack.NiceZombieSiren"
MoanVoice=SoundGroup'KF_EnemiesFinalSnd.siren.Siren_Talk'
JumpSound=SoundGroup'KF_EnemiesFinalSnd.siren.Siren_Jump'
DetachedLegClass=Class'KFChar.SeveredLegSiren'
DetachedHeadClass=Class'KFChar.SeveredHeadSiren'
HitSound(0)=SoundGroup'KF_EnemiesFinalSnd.siren.Siren_Pain'
DeathSound(0)=SoundGroup'KF_EnemiesFinalSnd.siren.Siren_Death'
ControllerClass=Class'NicePack.NiceZombieSirenController'
AmbientSound=Sound'KF_BaseSiren.Siren_IdleLoop'
Mesh=SkeletalMesh'KF_Freaks_Trip.Siren_Freak'
Skins(0)=FinalBlend'KF_Specimens_Trip_T.siren_hair_fb'
Skins(1)=Combiner'KF_Specimens_Trip_T.siren_cmb'
|
||||
}
|
||||
24
sources/Zeds/Nice/NiceZombieSirenBase.uc
Normal file
24
sources/Zeds/Nice/NiceZombieSirenBase.uc
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
// Zombie Monster for KF Invasion gametype
|
||||
class NiceZombieSirenBase extends NiceMonster
|
||||
abstract;
|
||||
var () int ScreamRadius; // AOE for scream attack.
|
||||
var () class <DamageType> ScreamDamageType;
|
||||
var () int ScreamForce;
|
||||
var(Shake) rotator RotMag; // how far to rot view
|
||||
var(Shake) float RotRate; // how fast to rot view
|
||||
var(Shake) vector OffsetMag; // max view offset vertically
|
||||
var(Shake) float OffsetRate; // how fast to offset view vertically
|
||||
var(Shake) float ShakeTime; // how long to shake for per scream
|
||||
var(Shake) float ShakeFadeTime; // how long after starting to shake to start fading out
|
||||
var(Shake) float ShakeEffectScalar; // Overall scale for shake/blur effect
|
||||
var(Shake) float MinShakeEffectScale;// The minimum that the shake effect drops off over distance
|
||||
var(Shake) float ScreamBlurScale; // How much motion blur to give from screams
|
||||
var bool bAboutToDie;
|
||||
var float DeathTimer;
|
||||
//-------------------------------------------------------------------------------
|
||||
// NOTE: All Code resides in the child class(this class was only created to
|
||||
// eliminate hitching caused by loading default properties during play)
|
||||
//-------------------------------------------------------------------------------
|
||||
defaultproperties
|
||||
{
ScreamRadius=700
ScreamDamageType=Class'KFMod.SirenScreamDamage'
ScreamForce=-150000
RotMag=(Pitch=150,Yaw=150,Roll=150)
RotRate=500.000000
OffsetMag=(Y=5.000000,Z=1.000000)
OffsetRate=500.000000
ShakeTime=2.000000
ShakeFadeTime=0.250000
ShakeEffectScalar=1.000000
MinShakeEffectScale=0.600000
ScreamBlurScale=0.850000
StunThreshold=3.000000
fuelRatio=0.650000
clientHeadshotScale=1.200000
niceZombieDamType=Class'NicePack.NiceZedSlashingDamageType'
MeleeAnims(0)="Siren_Bite"
MeleeAnims(1)="Siren_Bite2"
MeleeAnims(2)="Siren_Bite"
HitAnims(0)="HitReactionF"
HitAnims(1)="HitReactionF"
HitAnims(2)="HitReactionF"
ZapThreshold=0.500000
ZappedDamageMod=1.500000
ZombieFlag=1
MeleeDamage=13
damageForce=5000
KFRagdollName="Siren_Trip"
ScreamDamage=10
CrispUpThreshhold=7
bCanDistanceAttackDoors=True
bUseExtendedCollision=True
ColOffset=(Z=48.000000)
ColRadius=25.000000
ColHeight=5.000000
ExtCollAttachBoneName="Collision_Attach"
SeveredLegAttachScale=0.700000
PlayerCountHealthScale=0.100000
OnlineHeadshotOffset=(X=6.000000,Z=41.000000)
OnlineHeadshotScale=1.200000
HeadHealth=200.000000
PlayerNumHeadHealthScale=0.050000
MotionDetectorThreat=2.000000
ChallengeSound(0)=SoundGroup'KF_EnemiesFinalSnd.siren.Siren_Challenge'
ChallengeSound(1)=SoundGroup'KF_EnemiesFinalSnd.siren.Siren_Challenge'
ChallengeSound(2)=SoundGroup'KF_EnemiesFinalSnd.siren.Siren_Challenge'
ChallengeSound(3)=SoundGroup'KF_EnemiesFinalSnd.siren.Siren_Challenge'
ScoringValue=25
SoundGroupClass=Class'KFMod.KFFemaleZombieSounds'
IdleHeavyAnim="Siren_Idle"
IdleRifleAnim="Siren_Idle"
MeleeRange=45.000000
GroundSpeed=100.000000
WaterSpeed=80.000000
HealthMax=240.000000
Health=240
HeadHeight=1.000000
HeadScale=1.000000
MenuName="Nice Siren"
MovementAnims(0)="Siren_Walk"
MovementAnims(1)="Siren_Walk"
MovementAnims(2)="Siren_Walk"
MovementAnims(3)="Siren_Walk"
WalkAnims(0)="Siren_Walk"
WalkAnims(1)="Siren_Walk"
WalkAnims(2)="Siren_Walk"
WalkAnims(3)="Siren_Walk"
IdleCrouchAnim="Siren_Idle"
IdleWeaponAnim="Siren_Idle"
IdleRestAnim="Siren_Idle"
DrawScale=1.050000
PrePivot=(Z=3.000000)
RotationRate=(Yaw=45000,Roll=0)
|
||||
}
|
||||
13
sources/Zeds/Nice/NiceZombieSirenController.uc
Normal file
13
sources/Zeds/Nice/NiceZombieSirenController.uc
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
class NiceZombieSirenController extends NiceMonsterController;
|
||||
var bool bDoneSpottedCheck;
|
||||
state ZombieHunt
|
||||
{
|
||||
event SeePlayer(Pawn SeenPlayer)
|
||||
{
if ( !bDoneSpottedCheck && PlayerController(SeenPlayer.Controller) != none )
{
// 25% chance of first player to see this Siren saying something
if ( !KFGameType(Level.Game).bDidSpottedSirenMessage && FRand() < 0.25 )
{
PlayerController(SeenPlayer.Controller).Speech('AUTO', 15, "");
KFGameType(Level.Game).bDidSpottedSirenMessage = true;
}
|
||||
bDoneSpottedCheck = true;
}
|
||||
super.SeePlayer(SeenPlayer);
|
||||
}
|
||||
}
|
||||
defaultproperties
|
||||
{
|
||||
}
|
||||
147
sources/Zeds/Nice/NiceZombieStalker.uc
Normal file
147
sources/Zeds/Nice/NiceZombieStalker.uc
Normal file
|
|
@ -0,0 +1,147 @@
|
|||
// Zombie Monster for KF Invasion gametype
|
||||
class NiceZombieStalker extends NiceZombieStalkerBase;
|
||||
#exec OBJ LOAD FILE=KFX.utx
|
||||
#exec OBJ LOAD FILE=KF_BaseStalker.uax
|
||||
//----------------------------------------------------------------------------
|
||||
// NOTE: All Variables are declared in the base class to eliminate hitching
|
||||
//----------------------------------------------------------------------------
|
||||
simulated function PostBeginPlay()
|
||||
{
|
||||
CloakStalker();
|
||||
super.PostBeginPlay();
|
||||
}
|
||||
simulated function PostNetBeginPlay()
|
||||
{
|
||||
local PlayerController PC;
|
||||
super.PostNetBeginPlay();
|
||||
if( Level.NetMode!=NM_DedicatedServer )
|
||||
{
PC = Level.GetLocalPlayerController();
if( PC != none && PC.Pawn != none )
{
LocalKFHumanPawn = KFHumanPawn(PC.Pawn);
}
|
||||
}
|
||||
}
|
||||
simulated event SetAnimAction(name NewAction)
|
||||
{
|
||||
if ( NewAction == 'Claw' || NewAction == MeleeAnims[0] || NewAction == MeleeAnims[1] || NewAction == MeleeAnims[2] )
|
||||
{
UncloakStalker();
|
||||
}
|
||||
super.SetAnimAction(NewAction);
|
||||
}
|
||||
simulated function Tick(float DeltaTime)
|
||||
{
|
||||
Super.Tick(DeltaTime);
|
||||
if( Level.NetMode==NM_DedicatedServer )
Return; // Servers aren't intrested in this info.
|
||||
if( bZapped )
|
||||
{
// Make sure we check if we need to be cloaked as soon as the zap wears off
NextCheckTime = Level.TimeSeconds;
|
||||
}
|
||||
else if( Level.TimeSeconds > NextCheckTime && Health > 0 )
|
||||
{
NextCheckTime = Level.TimeSeconds + 0.5;
|
||||
if( LocalKFHumanPawn != none && LocalKFHumanPawn.Health > 0 && LocalKFHumanPawn.ShowStalkers() &&
VSizeSquared(Location - LocalKFHumanPawn.Location) < LocalKFHumanPawn.GetStalkerViewDistanceMulti() * 640000.0 ) // 640000 = 800 Units
{
bSpotted = True;
}
else
{
bSpotted = false;
}
|
||||
if ( !bSpotted && !bCloaked && Skins[0] != Combiner'KF_Specimens_Trip_T.stalker_cmb' )
{
UncloakStalker();
}
else if ( Level.TimeSeconds - LastUncloakTime > 1.2 )
{
// if we're uberbrite, turn down the light
if( bSpotted && Skins[0] != Finalblend'KFX.StalkerGlow' )
{
bUnlit = false;
CloakStalker();
}
else if ( Skins[0] != Shader'KF_Specimens_Trip_T.stalker_invisible' )
{
CloakStalker();
}
}
|
||||
}
|
||||
}
|
||||
// Cloak Functions ( called from animation notifies to save Gibby trouble ;) )
|
||||
simulated function CloakStalker()
|
||||
{
|
||||
// No cloaking if zapped
|
||||
if( bZapped )
|
||||
{
return;
|
||||
}
|
||||
if ( bSpotted )
|
||||
{
if( Level.NetMode == NM_DedicatedServer )
return;
|
||||
Skins[0] = Finalblend'KFX.StalkerGlow';
Skins[1] = Finalblend'KFX.StalkerGlow';
bUnlit = true;
return;
|
||||
}
|
||||
if ( !bDecapitated && !bCrispified ) // No head, no cloak, honey. updated : Being charred means no cloak either :D
|
||||
{
Visibility = 1;
bCloaked = true;
|
||||
if( Level.NetMode == NM_DedicatedServer )
Return;
|
||||
Skins[0] = Shader'KF_Specimens_Trip_T.stalker_invisible';
Skins[1] = Shader'KF_Specimens_Trip_T.stalker_invisible';
|
||||
// Invisible - no shadow
if(PlayerShadow != none)
PlayerShadow.bShadowActive = false;
if(RealTimeShadow != none)
RealTimeShadow.Destroy();
|
||||
// Remove/disallow projectors on invisible people
Projectors.Remove(0, Projectors.Length);
bAcceptsProjectors = false;
SetOverlayMaterial(Material'KFX.FBDecloakShader', 0.25, true);
|
||||
}
|
||||
}
|
||||
simulated function UnCloakStalker()
|
||||
{
|
||||
if( bZapped )
|
||||
{
return;
|
||||
}
|
||||
if( !bCrispified )
|
||||
{
LastUncloakTime = Level.TimeSeconds;
|
||||
Visibility = default.Visibility;
bCloaked = false;
bUnlit = false;
|
||||
// 25% chance of our Enemy saying something about us being invisible
if( Level.NetMode!=NM_Client && !KFGameType(Level.Game).bDidStalkerInvisibleMessage && FRand()<0.25 && Controller.Enemy!=none &&
PlayerController(Controller.Enemy.Controller)!=none )
{
PlayerController(Controller.Enemy.Controller).Speech('AUTO', 17, "");
KFGameType(Level.Game).bDidStalkerInvisibleMessage = true;
}
if( Level.NetMode == NM_DedicatedServer )
Return;
|
||||
if ( Skins[0] != Combiner'KF_Specimens_Trip_T.stalker_cmb' )
{
Skins[1] = FinalBlend'KF_Specimens_Trip_T.stalker_fb';
Skins[0] = Combiner'KF_Specimens_Trip_T.stalker_cmb';
|
||||
if (PlayerShadow != none)
PlayerShadow.bShadowActive = true;
|
||||
bAcceptsProjectors = true;
|
||||
SetOverlayMaterial(Material'KFX.FBDecloakShader', 0.25, true);
}
|
||||
}
|
||||
}
|
||||
// Set the zed to the zapped behavior
|
||||
simulated function SetZappedBehavior()
|
||||
{
|
||||
super.SetZappedBehavior();
|
||||
bUnlit = false;
|
||||
// Handle setting the zed to uncloaked so the zapped overlay works properly
|
||||
if( Level.Netmode != NM_DedicatedServer )
|
||||
{
Skins[1] = FinalBlend'KF_Specimens_Trip_T.stalker_fb';
Skins[0] = Combiner'KF_Specimens_Trip_T.stalker_cmb';
|
||||
if (PlayerShadow != none)
PlayerShadow.bShadowActive = true;
|
||||
bAcceptsProjectors = true;
SetOverlayMaterial(Material'KFZED_FX_T.Energy.ZED_overlay_Hit_Shdr', 999, true);
|
||||
}
|
||||
}
|
||||
// Turn off the zapped behavior
|
||||
simulated function UnSetZappedBehavior()
|
||||
{
|
||||
super.UnSetZappedBehavior();
|
||||
// Handle getting the zed back cloaked if need be
|
||||
if( Level.Netmode != NM_DedicatedServer )
|
||||
{
NextCheckTime = Level.TimeSeconds;
SetOverlayMaterial(none, 0.0f, true);
|
||||
}
|
||||
}
|
||||
// Overridden because we need to handle the overlays differently for zombies that can cloak
|
||||
function SetZapped(float ZapAmount, Pawn Instigator)
|
||||
{
|
||||
LastZapTime = Level.TimeSeconds;
|
||||
if( bZapped )
|
||||
{
TotalZap = ZapThreshold;
RemainingZap = ZapDuration;
|
||||
}
|
||||
else
|
||||
{
TotalZap += ZapAmount;
|
||||
if( TotalZap >= ZapThreshold )
{
RemainingZap = ZapDuration;
bZapped = true;
}
|
||||
}
|
||||
ZappedBy = Instigator;
|
||||
}
|
||||
function RemoveHead()
|
||||
{
|
||||
Super.RemoveHead();
|
||||
if (!bCrispified)
|
||||
{
Skins[1] = FinalBlend'KF_Specimens_Trip_T.stalker_fb';
Skins[0] = Combiner'KF_Specimens_Trip_T.stalker_cmb';
|
||||
}
|
||||
}
|
||||
simulated function PlayDying(class<DamageType> DamageType, vector HitLoc)
|
||||
{
|
||||
Super.PlayDying(DamageType,HitLoc);
|
||||
if(bUnlit)
bUnlit=!bUnlit;
|
||||
LocalKFHumanPawn = none;
|
||||
if (!bCrispified)
|
||||
{
Skins[1] = FinalBlend'KF_Specimens_Trip_T.stalker_fb';
Skins[0] = Combiner'KF_Specimens_Trip_T.stalker_cmb';
|
||||
}
|
||||
}
|
||||
// Give her the ability to spring.
|
||||
function bool DoJump( bool bUpdating )
|
||||
{
|
||||
if ( !bIsCrouched && !bWantsToCrouch && ((Physics == PHYS_Walking) || (Physics == PHYS_Ladder) || (Physics == PHYS_Spider)) )
|
||||
{
if ( Role == ROLE_Authority )
{
if (Level.Game != none)
MakeNoise(1.0);
if ( bCountJumps && (Inventory != none) )
Inventory.OwnerEvent('Jumped');
}
if ( Physics == PHYS_Spider )
Velocity = JumpZ * Floor;
else if ( Physics == PHYS_Ladder )
Velocity.Z = 0;
else if ( bIsWalking )
{
Velocity.Z = Default.JumpZ;
Velocity.X = (Default.JumpZ * 0.6);
}
else
{
Velocity.Z = JumpZ;
Velocity.X = (JumpZ * 0.6);
}
if ( (Base != none) && !Base.bWorldGeometry )
{
Velocity.Z += Base.Velocity.Z;
Velocity.X += Base.Velocity.X;
}
SetPhysics(PHYS_Falling);
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
static simulated function PreCacheMaterials(LevelInfo myLevel)
|
||||
{//should be derived and used.
|
||||
myLevel.AddPrecacheMaterial(Combiner'KF_Specimens_Trip_T.stalker_cmb');
|
||||
myLevel.AddPrecacheMaterial(Combiner'KF_Specimens_Trip_T.stalker_env_cmb');
|
||||
myLevel.AddPrecacheMaterial(Texture'KF_Specimens_Trip_T.stalker_diff');
|
||||
myLevel.AddPrecacheMaterial(Texture'KF_Specimens_Trip_T.stalker_spec');
|
||||
myLevel.AddPrecacheMaterial(Material'KF_Specimens_Trip_T.stalker_invisible');
|
||||
myLevel.AddPrecacheMaterial(Combiner'KF_Specimens_Trip_T.StalkerCloakOpacity_cmb');
|
||||
myLevel.AddPrecacheMaterial(Material'KF_Specimens_Trip_T.StalkerCloakEnv_rot');
|
||||
myLevel.AddPrecacheMaterial(Material'KF_Specimens_Trip_T.stalker_opacity_osc');
|
||||
myLevel.AddPrecacheMaterial(Material'KFCharacters.StalkerSkin');
|
||||
}
|
||||
defaultproperties
|
||||
{
stunLoopStart=0.250000
stunLoopEnd=0.890000
idleInsertFrame=0.950000
EventClasses(0)="NicePack.NiceZombieStalker"
MoanVoice=SoundGroup'KF_EnemiesFinalSnd.Stalker.Stalker_Talk'
MeleeAttackHitSound=SoundGroup'KF_EnemiesFinalSnd.Stalker.Stalker_HitPlayer'
JumpSound=SoundGroup'KF_EnemiesFinalSnd.Stalker.Stalker_Jump'
DetachedArmClass=Class'KFChar.SeveredArmStalker'
DetachedLegClass=Class'KFChar.SeveredLegStalker'
DetachedHeadClass=Class'KFChar.SeveredHeadStalker'
HitSound(0)=SoundGroup'KF_EnemiesFinalSnd.Stalker.Stalker_Pain'
DeathSound(0)=SoundGroup'KF_EnemiesFinalSnd.Stalker.Stalker_Death'
ChallengeSound(0)=SoundGroup'KF_EnemiesFinalSnd.Stalker.Stalker_Challenge'
ChallengeSound(1)=SoundGroup'KF_EnemiesFinalSnd.Stalker.Stalker_Challenge'
ChallengeSound(2)=SoundGroup'KF_EnemiesFinalSnd.Stalker.Stalker_Challenge'
ChallengeSound(3)=SoundGroup'KF_EnemiesFinalSnd.Stalker.Stalker_Challenge'
AmbientSound=Sound'KF_BaseStalker.Stalker_IdleLoop'
Mesh=SkeletalMesh'KF_Freaks_Trip.Stalker_Freak'
Skins(0)=Shader'KF_Specimens_Trip_T.stalker_invisible'
Skins(1)=Shader'KF_Specimens_Trip_T.stalker_invisible'
|
||||
}
|
||||
16
sources/Zeds/Nice/NiceZombieStalkerBase.uc
Normal file
16
sources/Zeds/Nice/NiceZombieStalkerBase.uc
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
// Zombie Monster for KF Invasion gametype
|
||||
class NiceZombieStalkerBase extends NiceMonster
|
||||
abstract;
|
||||
#exec OBJ LOAD FILE=
|
||||
#exec OBJ LOAD FILE=KFX.utx
|
||||
#exec OBJ LOAD FILE=KF_BaseStalker.uax
|
||||
var float NextCheckTime;
|
||||
var KFHumanPawn LocalKFHumanPawn;
|
||||
var float LastUncloakTime;
|
||||
//-------------------------------------------------------------------------------
|
||||
// NOTE: All Code resides in the child class(this class was only created to
|
||||
// eliminate hitching caused by loading default properties during play)
|
||||
//-------------------------------------------------------------------------------
|
||||
defaultproperties
|
||||
{
fuelRatio=0.850000
clientHeadshotScale=1.200000
niceZombieDamType=Class'NicePack.NiceZedSlashingDamageType'
MeleeAnims(0)="StalkerSpinAttack"
MeleeAnims(1)="StalkerAttack1"
MeleeAnims(2)="JumpAttack"
MeleeDamage=9
damageForce=5000
KFRagdollName="Stalker_Trip"
CrispUpThreshhold=10
PuntAnim="ClotPunt"
SeveredArmAttachScale=0.800000
SeveredLegAttachScale=0.700000
OnlineHeadshotOffset=(X=18.000000,Z=33.000000)
OnlineHeadshotScale=1.200000
MotionDetectorThreat=0.250000
ScoringValue=15
SoundGroupClass=Class'KFMod.KFFemaleZombieSounds'
IdleHeavyAnim="StalkerIdle"
IdleRifleAnim="StalkerIdle"
MeleeRange=30.000000
GroundSpeed=200.000000
WaterSpeed=180.000000
JumpZ=350.000000
Health=100
HeadHeight=2.500000
MenuName="Nice Stalker"
MovementAnims(0)="ZombieRun"
MovementAnims(1)="ZombieRun"
MovementAnims(2)="ZombieRun"
MovementAnims(3)="ZombieRun"
WalkAnims(0)="ZombieRun"
WalkAnims(1)="ZombieRun"
WalkAnims(2)="ZombieRun"
WalkAnims(3)="ZombieRun"
IdleCrouchAnim="StalkerIdle"
IdleWeaponAnim="StalkerIdle"
IdleRestAnim="StalkerIdle"
DrawScale=1.100000
PrePivot=(Z=5.000000)
RotationRate=(Yaw=45000,Roll=0)
|
||||
}
|
||||
327
sources/Zeds/Nice/NiceZombieTeslaHusk.uc
Normal file
327
sources/Zeds/Nice/NiceZombieTeslaHusk.uc
Normal file
|
|
@ -0,0 +1,327 @@
|
|||
class NiceZombieTeslaHusk extends NiceZombieHusk;
|
||||
#exec OBJ LOAD FILE=KF_EnemiesFinalSnd_CIRCUS.uax
|
||||
#exec OBJ LOAD FILE=ScrnZedPack_A.ukx
|
||||
#exec OBJ LOAD FILE=ScrnZedPack_T.utx
|
||||
#exec OBJ LOAD FILE=ScrnZedPack_SM.usx
|
||||
|
||||
var class<TeslaBeam> BeamClass;
|
||||
var TeslaBeam PrimaryBeam;
|
||||
var float MaxPrimaryBeamRange; // how far Husk can shoot with his tesla gun?
|
||||
var float MaxChildBeamRange; // max distance from primary target for a chain reaction
|
||||
var float ChainedBeamRangeExtension; // if pawn is already chained, chain max range is multiplied by this value
|
||||
// used for optimization
|
||||
var private float MaxPrimaryBeamRangeSquared, MaxChildBeamRangeSquared;
|
||||
var float Energy;
|
||||
var float EnergyMax;
|
||||
var float EnergyRestoreRate;
|
||||
var() float DischargeDamage; // beam damage per second
|
||||
var() float ChainDamageMult; // how much damage drops on the next chain
|
||||
var() class<DamageType> MyDamageType;
|
||||
var() byte MaxChainLevel;
|
||||
var() int MaxChainActors;
|
||||
var ZedBeamSparks DecapitationSparks;
|
||||
var bool bChainThoughZED; // chain tesla beam though a zed, which is between target and the husk, to extend firing range
|
||||
var transient float NextChainZedSearchTime;
|
||||
var transient float ChainRebuildTimer; // how many seconds between rebuilding chained actors
|
||||
var float DischargeRate; // time between damaging chained pawns. Do not set it too low due to rounding precission of DischargeDamage
|
||||
var transient float LastDischargeTime;
|
||||
// Tesla Husks are able to heal each other.
|
||||
var() float HealRate; // Percent of HealthMax to heal per second
|
||||
var() float HealEnergyDrain; // amount of energy required to heal 1%
|
||||
var transient float NextHealAttemptTime, LastHealTime;
|
||||
|
||||
struct SActorDamages
|
||||
{
|
||||
var Actor Victim;
|
||||
var int Damage;
|
||||
};
|
||||
var protected array<SActorDamages> DischargedActors;
|
||||
simulated function PostBeginPlay()
|
||||
{
|
||||
// Difficulty Scaling
|
||||
if(Level.Game != none && !bDiffAdjusted)
|
||||
{
if(Level.Game.GameDifficulty >= 5.0){
DischargeDamage *= 1.75;
EnergyMax *= 1.75;
EnergyRestoreRate *= 1.75;
MaxPrimaryBeamRange *= 1.75;
MaxChildBeamRange *= 1.75;
}
else{
DischargeDamage *= 1.0;
EnergyMax *= 1.0;
EnergyRestoreRate *= 1.0;
MaxPrimaryBeamRange *= 1.0;
MaxChildBeamRange *= 1.0;
}
|
||||
}
|
||||
Energy = EnergyMax;
|
||||
MaxPrimaryBeamRangeSquared = MaxPrimaryBeamRange * MaxPrimaryBeamRange;
|
||||
MaxChildBeamRangeSquared = MaxChildBeamRange * MaxChildBeamRange;
|
||||
NextHealAttemptTime = Level.TimeSeconds + 5.0;
|
||||
super.PostBeginPlay();
|
||||
BurnDamageScale = 1.0; // no fire damage resistance
|
||||
}
|
||||
simulated function HideBone(name boneName)
|
||||
{
|
||||
local int BoneScaleSlot;
|
||||
local bool bValidBoneToHide;
|
||||
if( boneName == LeftThighBone )
|
||||
{
boneScaleSlot = 0;
bValidBoneToHide = true;
|
||||
}
|
||||
else if ( boneName == RightThighBone )
|
||||
{
boneScaleSlot = 1;
bValidBoneToHide = true;
|
||||
}
|
||||
else if( boneName == RightFArmBone )
|
||||
{
boneScaleSlot = 2;
bValidBoneToHide = true;
|
||||
}
|
||||
else if ( boneName == LeftFArmBone )
|
||||
{
boneScaleSlot = 3;
bValidBoneToHide = true;
|
||||
}
|
||||
else if ( boneName == HeadBone )
|
||||
{
// Only scale the bone down once
if( SeveredHead == none )
{
bValidBoneToHide = true;
boneScaleSlot = 4;
}
else
{
return;
}
|
||||
}
|
||||
else if ( boneName == 'spine' )
|
||||
{
bValidBoneToHide = true;
boneScaleSlot = 5;
|
||||
}
|
||||
// Only hide the bone if it is one of the arms, legs, or head, don't hide other misc bones
|
||||
if( bValidBoneToHide )
|
||||
{
SetBoneScale(BoneScaleSlot, 0.0, BoneName);
|
||||
}
|
||||
}
|
||||
function TeslaBeam SpawnTeslaBeam()
|
||||
{
|
||||
local TeslaBeam beam;
|
||||
beam = spawn(BeamClass, self);
|
||||
if ( beam != none ) {
beam.Instigator = self;
|
||||
}
|
||||
return beam;
|
||||
}
|
||||
|
||||
function TeslaBeam SpawnPrimaryBeam()
|
||||
{
|
||||
if ( PrimaryBeam == none ) {
PrimaryBeam = SpawnTeslaBeam();
PrimaryBeam.StartActor = self;
PrimaryBeam.StartBoneName = 'Barrel';
|
||||
}
|
||||
return PrimaryBeam;
|
||||
}
|
||||
function FreePrimaryBeam()
|
||||
{
|
||||
if ( PrimaryBeam != none ) {
//PrimaryBeam.DestroyChildBeams();
PrimaryBeam.EndActor = none;
PrimaryBeam.EffectEndTime = Level.TimeSeconds - 1;
PrimaryBeam.Instigator = none; // mark to delete
PrimaryBeam = none; }}
|
||||
function RangedAttack(Actor A)
|
||||
{
|
||||
local float Dist;
|
||||
local NiceMonster M;
|
||||
if ( bShotAnim || bDecapitated )
return;
|
||||
Dist = VSize(A.Location - Location);
|
||||
if ( Physics == PHYS_Swimming )
|
||||
{
SetAnimAction('Claw');
bShotAnim = true;
|
||||
}
|
||||
else if ( Energy < 100 && Dist < MeleeRange + CollisionRadius + A.CollisionRadius ) // do melee hits only when low on energy
|
||||
{
bShotAnim = true;
SetAnimAction('Claw');
//PlaySound(sound'Claw2s', SLOT_Interact); KFTODO: Replace this
Controller.bPreparingMove = true;
Acceleration = vect(0,0,0);
|
||||
}
|
||||
else if ( Energy > 50 && Dist < MaxPrimaryBeamRange*(0.7 + 0.3*frand()) )
|
||||
{
bShotAnim = true;
|
||||
SetAnimAction('ShootBurns');
Controller.bPreparingMove = true;
Acceleration = vect(0,0,0);
|
||||
NextFireProjectileTime = Level.TimeSeconds + ProjectileFireInterval + (FRand() * 2.0);
|
||||
}
|
||||
else if ( bChainThoughZED && Energy > 100 && Controller.Enemy == A && KFPawn(A) != none
&& NextChainZedSearchTime < Level.TimeSeconds
&& Dist < MaxPrimaryBeamRange + MaxChildBeamRange )
|
||||
{
NextChainZedSearchTime = Level.TimeSeconds + 3.0;
foreach VisibleCollidingActors(class'NiceMonster', M, MaxPrimaryBeamRange*0.9) {
if ( !M.bDeleteMe && M.Health > 0 && NiceZombieTeslaHusk(M) == none
&& VSizeSquared(M.Location - A.Location) < MaxChildBeamRangeSquared*0.9
&& M.FastTrace(A.Location, M.Location) )
{
Controller.Target = M;
RangedAttack(M);
return;
}
}
|
||||
}
|
||||
}
|
||||
|
||||
function SpawnTwoShots()
|
||||
{
|
||||
SpawnPrimaryBeam();
|
||||
if ( PrimaryBeam == none ) {
log("Unable to spawn Primary Beam!", 'TeslaHusk');
return;
|
||||
}
|
||||
PrimaryBeam.EndActor = Controller.Target;
|
||||
if( Controller!=none && KFDoorMover(Controller.Target)!=none )
|
||||
{
PrimaryBeam.EffectEndTime = Level.TimeSeconds + 1.0;
Controller.Target.TakeDamage(22,Self,Location,vect(0,0,0), MyDamageType);
return;
|
||||
}
|
||||
PrimaryBeam.EffectEndTime = Level.TimeSeconds + 2.5;
|
||||
GotoState('Shooting');
|
||||
}
|
||||
simulated function Tick( float Delta )
|
||||
{
|
||||
if ( Role < ROLE_Authority ) {
if ( bDecapitated && Health > 0 && DecapitationSparks == none ) {
SpawnDecapitationEffects();
}
|
||||
}
|
||||
Super(NiceMonster).Tick(Delta);
|
||||
if ( Role == ROLE_Authority ) {
if ( Energy < EnergyMax )
Energy += EnergyRestoreRate * Delta;
if ( Energy > 150 && NextHealAttemptTime < Level.TimeSeconds ) {
NextHealAttemptTime = Level.TimeSeconds + 3.0;
TryHealing();
}
|
||||
}
|
||||
}
|
||||
function TryHealing()
|
||||
{
|
||||
local Controller C;
|
||||
local NiceMonsterController MyMC;
|
||||
local NiceZombieTeslaHusk BestPatient; local bool bAnyHusks;
|
||||
MyMC = NiceMonsterController(Controller);
|
||||
if ( MyMC == none )
return; // just in case
|
||||
if ( MyMC.Enemy != none && VSizeSquared(MyMC.Enemy.Location - Location) < MaxPrimaryBeamRangeSquared )
return; // no healing when need to fight
for ( C=Level.ControllerList; C!=none; C=C.NextController ) {
if ( NiceZombieTeslaHusk(C.Pawn) != none && C.Pawn != self && C.Pawn.Health > 0 ) {
bAnyHusks = true;
if ( (BestPatient == none || BestPatient.Health > C.Pawn.Health)
&& VSizeSquared(C.Pawn.Location - Location) < MaxPrimaryBeamRangeSquared )
BestPatient = NiceZombieTeslaHusk(C.Pawn);
}
|
||||
}
|
||||
if ( !bAnyHusks )
NextHealAttemptTime = Level.TimeSeconds + 10.0; // no other husks on the map - so no need to do searching so often
|
||||
else if ( BestPatient != none ) {
if ( BestPatient.Health < BestPatient.HealthMax * 0.90 ) {
MyMC.Target = BestPatient;
MyMC.ChangeEnemy(BestPatient, MyMC.CanSee(BestPatient));
MyMC.GotoState('RangedAttack');
LastHealTime = Level.TimeSeconds + 3.0; // do not heal until beam is spawned
GotoState('Healing'); }
else
NextHealAttemptTime = Level.TimeSeconds + 1.0; // if there are other Tesla Husks nearby, then check their health each second
|
||||
}
|
||||
else
NextHealAttemptTime = Level.TimeSeconds + 3.0; // there are other Tesla Husks on the map, but not too close
|
||||
}
|
||||
function Died(Controller Killer, class<DamageType> damageType, vector HitLocation)
|
||||
{
|
||||
if ( PrimaryBeam != none )
PrimaryBeam.Instigator = none;
|
||||
if ( DecapitationSparks != none )
DecapitationSparks.Destroy();
|
||||
AmbientSound = none;
super.Died(Killer, damageType, HitLocation);
|
||||
}
|
||||
simulated event PlayDying(class<DamageType> DamageType, vector HitLoc)
|
||||
{
|
||||
super.PlayDying(DamageType, HitLoc);
|
||||
if ( DecapitationSparks != none )
DecapitationSparks.Destroy(); AmbientSound = none;
|
||||
}
|
||||
// instead of roaming around, activate self-destruct
|
||||
function RemoveHead()
|
||||
{
|
||||
Intelligence = BRAINS_Retarded; // Headless dumbasses!
|
||||
bDecapitated = true;
|
||||
DECAP = true;
|
||||
DecapTime = Level.TimeSeconds;
|
||||
bShotAnim = true;
|
||||
SetAnimAction('KnockDown');
|
||||
Acceleration = vect(0, 0, 0);
|
||||
Velocity.X = 0;
|
||||
Velocity.Y = 0;
|
||||
Controller.GoToState('WaitForAnim');
|
||||
NiceMonsterController(Controller).bUseFreezeHack = True;
|
||||
SetGroundSpeed(0);
|
||||
AirSpeed = 0;
|
||||
WaterSpeed = 0;
|
||||
// No more raspy breathin'...cuz he has no throat or mouth :S
|
||||
AmbientSound = MiscSound;
|
||||
// super.TakeDamage(LastDamageAmount) isn't called yet, so set self-destruct sequence only if
|
||||
// zed can survive the hit
|
||||
if( Health > LastDamageAmount && Energy > 50 ) {
SpawnDecapitationEffects();
BleedOutTime = Level.TimeSeconds + BleedOutDuration;
GotoState('SelfDestruct');
|
||||
}
|
||||
|
||||
PlaySound(DecapitationSound, SLOT_Misc,1.30,true,525);
|
||||
}
|
||||
simulated function Destroyed()
|
||||
{
|
||||
if ( PrimaryBeam != none )
PrimaryBeam.Destroy();
if ( DecapitationSparks != none )
DecapitationSparks.Destroy();
super.Destroyed();
|
||||
}
|
||||
simulated function SpawnDecapitationEffects()
|
||||
{
|
||||
if (Level.NetMode == NM_DedicatedServer)
return;
DecapitationSparks = spawn(class'ZedBeamSparks', self,, GetBoneCoords(HeadBone).Origin, Rotation);
|
||||
}
|
||||
simulated function int DoAnimAction( name AnimName )
|
||||
{
|
||||
// faked animation with forces to play the end of shoot animation (where Huks is putting his gun down)
|
||||
if ( AnimName == 'ShootBurnsEnd' ) {
PlayAnim('ShootBurns');
SetAnimFrame(120, 0, 1);
return 0;
|
||||
}
|
||||
return super.DoAnimAction(AnimName);
|
||||
}
|
||||
function float RangedAttackTime()
|
||||
{
|
||||
return 5.0;
|
||||
}
|
||||
// Overridden so that anims don't get interrupted on the server if one is already playing
|
||||
function bool IsHeadShot(vector loc, vector ray, float AdditionalScale)
|
||||
{
|
||||
local coords C;
|
||||
local vector HeadLoc, B, M, diff;
|
||||
local float t, DotMM, Distance;
|
||||
local int look;
|
||||
local bool bUseAltHeadShotLocation;
|
||||
local bool bWasAnimating;
|
||||
if (HeadBone == '')
return False;
|
||||
// If we are a dedicated server estimate what animation is most likely playing on the client
|
||||
if (Level.NetMode == NM_DedicatedServer)
|
||||
{
if (Physics == PHYS_Falling)
PlayAnim(AirAnims[0], 1.0, 0.0);
else if (Physics == PHYS_Walking)
{
// Only play the idle anim if we're not already doing a different anim.
// This prevents anims getting interrupted on the server and borking things up - Ramm
|
||||
if( !IsAnimating(0) && !IsAnimating(1) )
{
if (bIsCrouched)
{
PlayAnim(IdleCrouchAnim, 1.0, 0.0);
}
else
{
bUseAltHeadShotLocation=true;
}
}
else
{
bWasAnimating = true;
}
|
||||
if ( bDoTorsoTwist )
{
SmoothViewYaw = Rotation.Yaw;
SmoothViewPitch = ViewPitch;
|
||||
look = (256 * ViewPitch) & 65535;
if (look > 32768)
look -= 65536;
|
||||
SetTwistLook(0, look);
}
}
else if (Physics == PHYS_Swimming)
PlayAnim(SwimAnims[0], 1.0, 0.0);
|
||||
if( !bWasAnimating )
{
SetAnimFrame(0.5);
}
|
||||
}
|
||||
if( bUseAltHeadShotLocation )
|
||||
{
HeadLoc = Location + (OnlineHeadshotOffset >> Rotation);
AdditionalScale *= OnlineHeadshotScale;
|
||||
}
|
||||
else
|
||||
{
C = GetBoneCoords(HeadBone);
|
||||
HeadLoc = C.Origin + (HeadHeight * HeadScale * AdditionalScale * C.XAxis) -5.0*C.XAxis - 2.0*C.YAxis;
|
||||
}
|
||||
//ServerHeadLocation = HeadLoc;
|
||||
// Express snipe trace line in terms of B + tM
|
||||
B = loc;
|
||||
M = ray * (2.0 * CollisionHeight + 2.0 * CollisionRadius);
|
||||
// Find Point-Line Squared Distance
|
||||
diff = HeadLoc - B;
|
||||
t = M Dot diff;
|
||||
if (t > 0)
|
||||
{
DotMM = M dot M;
if (t < DotMM)
{
t = t / DotMM;
diff = diff - (t * M);
}
else
{
t = 1;
diff -= M;
}
|
||||
}
|
||||
else
t = 0;
|
||||
Distance = Sqrt(diff Dot diff);
|
||||
return (Distance < (HeadRadius * HeadScale * AdditionalScale));
|
||||
}
|
||||
state Healing
|
||||
{
|
||||
ignores TryHealing;
|
||||
function BeginState()
|
||||
{
//log("Entering Healing State @ " $ Level.TimeSeconds, 'TeslaHusk');
LastHealTime = Level.TimeSeconds + 3.0; // do not heal until beam is spawned
|
||||
}
|
||||
function EndState()
|
||||
{
local name SeqName;
local float AnimFrame, AnimRate;
|
||||
//log("Exiting Healing State @ " $ Level.TimeSeconds, 'TeslaHusk');
NextHealAttemptTime = Level.TimeSeconds + 5.0;
FreePrimaryBeam();
|
||||
// end shooting animation
GetAnimParams(0, SeqName, AnimFrame, AnimRate);
if ( SeqName == 'ShootBurns' && AnimFrame < 120 )
SetAnimAction('ShootBurnsEnd'); // faked anim
Controller.Enemy = none;
NiceMonsterController(Controller).FindNewEnemy();
|
||||
}
|
||||
function SpawnTwoShots()
|
||||
{
SpawnPrimaryBeam();
if ( Controller != none )
PrimaryBeam.EndActor = Controller.Target;
PrimaryBeam.EffectEndTime = Level.TimeSeconds + 2.5;
LastHealTime = Level.TimeSeconds; // start healing
|
||||
}
|
||||
function bool FlipOver()
|
||||
{
if ( global.FlipOver() ) {
GotoState('');
return true;
}
return false;
|
||||
}
|
||||
function DoStun(optional Pawn instigatedBy, optional Vector hitLocation, optional Vector momentum, optional class<NiceWeaponDamageType> damageType, optional float headshotLevel, optional KFPlayerReplicationInfo KFPRI){
super.DoStun(instigatedBy, hitLocation, momentum, damageType, headshotLevel, KFPRI);
GotoState('');
|
||||
}
|
||||
function Tick(float DeltaTime)
|
||||
{
local NiceZombieTeslaHusk Patient;
local int HealthToAdd;
super.Tick(DeltaTime);
if ( Level.TimeSeconds >= LastHealTime+0.25 ) {
if ( PrimaryBeam == none || Energy <= 0 || Level.TimeSeconds > PrimaryBeam.EffectEndTime ) {
GotoState('');
return;
}
|
||||
NextHealAttemptTime = Level.TimeSeconds + 0.25;
Patient = NiceZombieTeslaHusk(PrimaryBeam.EndActor);
if ( Patient == none || Patient.Health <= 0 || Patient.Health >= Patient.HealthMax )
GotoState('');
else {
HealthToAdd = min(Patient.HealthMax*HealRate*(Level.TimeSeconds-LastHealTime), Patient.HealthMax - Patient.Health);
if ( HealthToAdd*HealEnergyDrain > Energy )
HealthToAdd = Energy / HealEnergyDrain;
Patient.Health += HealthToAdd;
Energy -= HealthToAdd*HealEnergyDrain;
LastHealTime = Level.TimeSeconds;
}
}
|
||||
}}
|
||||
|
||||
// todo: make custom controller, moving states there
|
||||
state Shooting
|
||||
{
|
||||
ignores TryHealing;
|
||||
function BeginState()
|
||||
{
//log("Entering Shooting State @ " $ Level.TimeSeconds, 'TeslaHusk');
if ( PrimaryBeam == none ) {
SpawnPrimaryBeam();
PrimaryBeam.EndActor = Controller.Target;
}
|
||||
PrimaryBeam.EffectEndTime = Level.TimeSeconds + 2.5;
BuildChain();
LastDischargeTime = Level.TimeSeconds;
Discharge(DischargeRate);
|
||||
}
|
||||
function EndState()
|
||||
{
local name SeqName;
local float AnimFrame, AnimRate;
//log("Exiting Shooting State @ " $ Level.TimeSeconds, 'TeslaHusk');
|
||||
FreePrimaryBeam();
// end shooting animation
GetAnimParams(0, SeqName, AnimFrame, AnimRate);
if ( SeqName == 'ShootBurns' && AnimFrame < 120 )
SetAnimAction('ShootBurnsEnd'); // faked anim
|
||||
}
|
||||
function BuildChain()
|
||||
{
local int ChainsRemaining;
ChainRebuildTimer = default.ChainRebuildTimer;
ChainsRemaining = MaxChainActors;
SpawnChildBeams(PrimaryBeam, 1, ChainsRemaining); }
|
||||
function SpawnChildBeams(TeslaBeam MasterBeam, byte level, out int ChainsRemaining)
|
||||
{
local Pawn P;
local TeslaBeam ChildBeam;
local int i;
if ( MasterBeam == none || level > MaxChainLevel || ChainsRemaining <= 0 )
return;
for ( i=0; i<MasterBeam.ChildBeams.length; ++i ) {
if ( MasterBeam.ChildBeams[i] == none )
MasterBeam.ChildBeams.remove(i--, 1);
}
if ( MasterBeam.EndActor == none || MasterBeam.EndActor.bDeleteMe )
return;
foreach MasterBeam.EndActor.VisibleCollidingActors(class'Pawn', P, MaxChildBeamRange, MasterBeam.EndActor.Location) {
if ( !P.bDeleteMe && P != MasterBeam.StartActor && P != MasterBeam.EndActor
&& NiceZombieTeslaHusk(P) == none && !PrimaryBeam.IsChainedTo(P) )
{
i = MasterBeam.ChildIndex(P);
if ( i != -1 ) {
ChildBeam = MasterBeam.ChildBeams[i];
ChildBeam.EffectEndTime = PrimaryBeam.EffectEndTime;
}
else {
ChildBeam = SpawnTeslaBeam();
if ( ChildBeam != none ) {
ChildBeam.StartActor = MasterBeam.EndActor;
ChildBeam.EndActor = P;
ChildBeam.EffectEndTime = PrimaryBeam.EffectEndTime;
MasterBeam.ChildBeams[MasterBeam.ChildBeams.length] = ChildBeam;
}
}
if ( --ChainsRemaining <= 0 )
return;
}
}
for ( i=0; i<MasterBeam.ChildBeams.length && ChainsRemaining > 0; ++i ) {
SpawnChildBeams(MasterBeam.ChildBeams[i], level+1, ChainsRemaining);
}
|
||||
}
|
||||
function ChainDamage(TeslaBeam Beam, float Damage, byte ChainLevel)
|
||||
{
local int i;
local float DmgMult;
if ( Beam == none || Beam.EndActor == none || Energy < 0 || Controller == none)
return;
Damage = clamp(Damage, 1, Energy);
// look if actor already received a damage during this discharge
for ( i=0; i<DischargedActors.length; ++i ) {
if ( DischargedActors[i].Victim == Beam.EndActor ) {
if ( DischargedActors[i].Damage >= Damage )
return; // Victim already received enough damage
else {
Damage -= DischargedActors[i].Damage;
DischargedActors[i].Damage += Damage;
break;
}
}
}
if ( i == DischargedActors.length ) {
// new victim
DischargedActors.insert(i, 1);
DischargedActors[i].Victim = Beam.EndActor;
DischargedActors[i].Damage = Damage;
}
// deal higher damage to monsters, but do not drain energy quicker
DmgMult = 1.0;
if ( NiceMonster(Beam.EndActor) != none ) {
if ( ZombieFleshpound(Beam.EndActor) != none )
DmgMult = 20.0;
else
DmgMult = 3.0;
}
// if pawn is already chained, then damage him until he gets out of the primary range
Beam.SetBeamLocation(); // ensure that StartEffect and EndEffect are set
if ( VSizeSquared(Beam.EndEffect - Beam.StartEffect) <= MaxPrimaryBeamRangeSquared * ChainedBeamRangeExtension
&& FastTrace(Beam.EndEffect, Beam.StartEffect) )
{
Beam.EndActor.TakeDamage(Damage*DmgMult, self, Beam.EndActor.Location, Beam.SetBeamRotation(), MyDamageType);
if(Controller == none)
return;
Energy -= Damage;
Damage *= ChainDamageMult;
ChainLevel++;
if ( Damage > 0 && ChainLevel <= MaxChainLevel) {
for ( i=0; i<Beam.ChildBeams.length && Energy > 0; ++i )
ChainDamage(Beam.ChildBeams[i], Damage, ChainLevel);
}
}
else {
Beam.Instigator = none; // delete beam
}
|
||||
}
|
||||
function Discharge(float DeltaTime)
|
||||
{
LastDischargeTime = Level.TimeSeconds;
DischargedActors.length = 0;
ChainDamage(PrimaryBeam, DischargeDamage*DeltaTime, 0);
if (Energy <= 0)
GotoState(''); // out of energy - stop shooting
|
||||
}
|
||||
function bool FlipOver()
|
||||
{
if ( global.FlipOver() ) {
GotoState('');
return true;
}
return false;
|
||||
}
|
||||
function DoStun(optional Pawn instigatedBy, optional Vector hitLocation, optional Vector momentum, optional class<NiceWeaponDamageType> damageType, optional float headshotLevel, optional KFPlayerReplicationInfo KFPRI){
super.DoStun(instigatedBy, hitLocation, momentum, damageType, headshotLevel, KFPRI);
GotoState('');
|
||||
}
|
||||
function Tick(float DeltaTime)
|
||||
{
super.Tick(DeltaTime);
|
||||
//log("Shooting.Tick @ " $ Level.TimeSeconds $ ":" @"Energy="$Energy @"PrimaryBeam="$PrimaryBeam, 'TeslaHusk');
ChainRebuildTimer -= DeltaTime;
if ( Energy <= 0 || PrimaryBeam == none || PrimaryBeam.EndActor == none || Level.TimeSeconds > PrimaryBeam.EffectEndTime ) {
GotoState('');
}
else if ( LastDischargeTime + DischargeRate <= Level.TimeSeconds ) {
if ( ChainRebuildTimer <= 0 )
BuildChain();
Discharge(Level.TimeSeconds - LastDischargeTime); // damage chained actors
}
}
|
||||
}
|
||||
state SelfDestruct
|
||||
{
|
||||
ignores PlayDirectionalHit, RangedAttack, MeleeDamageTarget;
|
||||
function BeginState()
|
||||
{
local NiceTeslaEMPNade MyNade;
MyNade = spawn(class'NiceTeslaEMPNade', self,, Location);
if ( MyNade != none ) {
AttachToBone(MyNade, RootBone);
MyNade.SetTimer(3.0, false);
MyNade.bTimerSet = true;
MyNade.Damage = max(20, Energy * 0.45);
}
else {
// wtf?
Died(LastDamagedBy.Controller,class'DamTypeBleedOut',Location);
}
|
||||
}
|
||||
function bool CheckMiniFlinch(int flinchScore, Pawn instigatedBy, Vector hitLocation, Vector momentum, class<NiceWeaponDamageType> damageType, float headshotLevel, KFPlayerReplicationInfo KFPRI){
return false;
|
||||
}
|
||||
function bool IsStunPossible(){
return false;
|
||||
}
|
||||
function bool CheckStun(int stunScore, Pawn instigatedBy, Vector hitLocation, Vector momentum, class<NiceWeaponDamageType> damageType, float headshotLevel, KFPlayerReplicationInfo KFPRI){
return false;
|
||||
}
|
||||
function bool CanGetOutOfWay(){
return false;
|
||||
}
|
||||
}
|
||||
static simulated function PreCacheMaterials(LevelInfo myLevel)
|
||||
{
|
||||
myLevel.AddPrecacheMaterial(Shader'ScrnZedPack_T.TeslaHusk.TeslaHusk_SHADER');
|
||||
myLevel.AddPrecacheMaterial(FinalBlend'KFZED_FX_T.Energy.ZED_FX_Beam_FB');
|
||||
}
|
||||
defaultproperties
|
||||
{
BeamClass=Class'ScrnZedPack.TeslaBeam'
MaxPrimaryBeamRange=300.000000
MaxChildBeamRange=150.000000
ChainedBeamRangeExtension=1.200000
Energy=200.000000
EnergyMax=200.000000
EnergyRestoreRate=20.000000
DischargeDamage=20.000000
ChainDamageMult=0.700000
MyDamageType=Class'ScrnZedPack.DamTypeTesla'
MaxChainLevel=5
MaxChainActors=20
DischargeRate=0.250000
HealRate=0.250000
HealEnergyDrain=0.200000
ProjectileFireInterval=5.000000
BurnDamageScale=1.000000
mind=0.000000
bFireImmune=False
MoanVoice=SoundGroup'KF_EnemiesFinalSnd_CIRCUS.Husk.Husk_Talk'
BleedOutDuration=5.000000
MiscSound=Sound'KF_FY_ZEDV2SND.foley.WEP_ZEDV2_Projectile_Loop'
DecapitationSound=Sound'KF_FY_ZEDV2SND.Fire.WEP_ZEDV2_Secondary_Explode'
MeleeAttackHitSound=SoundGroup'KF_EnemiesFinalSnd_CIRCUS.Bloat.Bloat_HitPlayer'
JumpSound=SoundGroup'KF_EnemiesFinalSnd_CIRCUS.Husk.Husk_Jump'
ColHeight=30.000000
ProjectileBloodSplatClass=None
DetachedArmClass=Class'ScrnZedPack.SeveredArmTeslaHusk'
DetachedLegClass=Class'ScrnZedPack.SeveredLegTeslaHusk'
DetachedHeadClass=Class'ScrnZedPack.SeveredHeadTeslaHusk'
DetachedSpecialArmClass=Class'ScrnZedPack.SeveredGunTeslaHusk'
OnlineHeadshotOffset=(X=22.000000,Z=50.000000)
HitSound(0)=SoundGroup'KF_EnemiesFinalSnd_CIRCUS.Husk.Husk_Pain'
DeathSound(0)=SoundGroup'KF_EnemiesFinalSnd_CIRCUS.Husk.Husk_Death'
ChallengeSound(0)=SoundGroup'KF_EnemiesFinalSnd_CIRCUS.Husk.Husk_Challenge'
ChallengeSound(1)=SoundGroup'KF_EnemiesFinalSnd_CIRCUS.Husk.Husk_Challenge'
ChallengeSound(2)=SoundGroup'KF_EnemiesFinalSnd_CIRCUS.Husk.Husk_Challenge'
ChallengeSound(3)=SoundGroup'KF_EnemiesFinalSnd_CIRCUS.Husk.Husk_Challenge'
ScoringValue=25
GroundSpeed=100.000000
WaterSpeed=80.000000
HealthMax=800.000000
Health=800
HeadHeight=-7.500000
MenuName="Tesla Husk"
AmbientSound=Sound'KF_BaseHusk_CIRCUS.Husk_IdleLoop'
Mesh=SkeletalMesh'ScrnZedPack_A.TeslaHuskMesh'
Skins(0)=Shader'ScrnZedPack_T.TeslaHusk.TeslaHusk_SHADER'
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue