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

1714 lines
46 KiB
Ucode

class ZombieBossOS extends KFMonsterOS;
var BossHPNeedle CurrentNeedle;
var bool bChargingPlayer,bClientCharg,bFireAtWill,bMinigunning,bIsBossView;
var float RageStartTime,LastChainGunTime,LastMissileTime,LastSneakedTime;
var bool bClientMiniGunning;
var name ChargingAnim;
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 float MGFireDuration;
var float MGLostSightTimeout;
var() float MGDamage;
var() float ClawMeleeDamageRange;
var() float ImpaleMeleeDamageRange;
var float LastChargeTime;
var float LastForceChargeTime;
var int NumChargeAttacks;
var float ChargeDamage;
var float LastDamageTime;
var float SneakStartTime;
var int SneakCount;
var() float PipeBombDamageScale;
replication
{
reliable if( Role==ROLE_Authority )
bChargingPlayer,SyringeCount,TraceHitPos,bMinigunning,bIsBossView;
}
simulated function CalcAmbientRelevancyScale()
{
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 )
{
if( Mass > 900 )
{
NewVelocity.Z += 90;
}
}
}
return NewVelocity;
}
function ZombieMoan()
{
if( !bShotAnim )
super.ZombieMoan();
}
simulated function SetBurningBehavior(){}
simulated function UnSetBurningBehavior(){}
function bool CanGetOutOfWay()
{
return false;
}
simulated function Tick(float DeltaTime)
{
local KFHumanPawn HP;
super.Tick(DeltaTime);
if( Role == ROLE_Authority )
{
PipeBombDamageScale -= DeltaTime * 0.33;
if( PipeBombDamageScale < 0 )
{
PipeBombDamageScale = 0;
}
}
if( Level.NetMode==NM_DedicatedServer )
return;
bSpecialCalcView = bIsBossView;
if( bZapped )
{
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( !bSpotted )
{
bSpotted = true;
CloakBoss();
}
return;
}
if( bSpotted )
{
bSpotted = false;
bUnlit = false;
CloakBoss();
}
}
}
simulated function CloakBoss()
{
local Controller C;
local int Index;
if( bZapped )
{
return;
}
if( bSpotted )
{
Visibility = 120;
if( Level.NetMode==NM_DedicatedServer )
return;
Skins[0] = Finalblend'KFOldSchoolZeds_Textures.Patriarch.BossGlowFB';
Skins[1] = Finalblend'KFOldSchoolZeds_Textures.Patriarch.BossGlowFB';
Skins[2] = Finalblend'KFOldSchoolZeds_Textures.Patriarch.BossGlowFB';
Skins[3] = Finalblend'KFOldSchoolZeds_Textures.Patriarch.BossGlowFB';
Skins[4] = Finalblend'KFOldSchoolZeds_Textures.Patriarch.BossGlowFB';
Skins[5] = Finalblend'KFOldSchoolZeds_Textures.Patriarch.BossGlowFB';
Skins[6] = Finalblend'KFOldSchoolZeds_Textures.Patriarch.BossGlowFB';
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;
}
}
if( Level.NetMode==NM_DedicatedServer )
return;
Skins[0] = Shader'KFOldSchoolZeds_Textures.BossCloakShader';
Skins[1] = Shader'KFOldSchoolZeds_Textures.BossCloakShader';
Skins[2] = Shader'KFOldSchoolZeds_Textures.BossCloakShader';
Skins[3] = Shader'KFOldSchoolZeds_Textures.BossCloakShader';
Skins[4] = Shader'KFOldSchoolZeds_Textures.BossCloakShader';
Skins[5] = Shader'KFOldSchoolZeds_Textures.BossCloakShader';
Skins[6] = Texture'KillingFloorLabTextures.LabCommon.voidtex';
if(PlayerShadow != none)
PlayerShadow.bShadowActive = false;
Projectors.Remove(0, Projectors.Length);
bAcceptsProjectors = false;
SetOverlayMaterial(FinalBlend'KFOldSchoolZeds_Textures.Patriarch.BossCloakFizzleFB', 1.0, true);
if ( FRand() < 0.10 )
{
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 );
}
simulated function SetZappedBehavior()
{
super.SetZappedBehavior();
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);
}
}
simulated function UnSetZappedBehavior()
{
super.UnSetZappedBehavior();
if( Level.Netmode != NM_DedicatedServer )
{
LastCheckTimes = Level.TimeSeconds;
SetOverlayMaterial(none, 0.0f, true);
}
}
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;
}
simulated function PostBeginPlay()
{
super.PostBeginPlay();
if( Role < ROLE_Authority )
{
return;
}
if (Level.Game != none)
{
if( Level.Game.NumPlayers == 1 )
{
if( Level.Game.GameDifficulty < 2.0 )
{
MGDamage = default.MGDamage * 0.375;
}
else if( Level.Game.GameDifficulty < 4.0 )
{
MGDamage = default.MGDamage * 0.75;
}
else if( Level.Game.GameDifficulty < 5.0 )
{
MGDamage = default.MGDamage * 1.15;
}
else
{
MGDamage = default.MGDamage * 1.3;
}
}
else
{
if( Level.Game.GameDifficulty < 2.0 )
{
MGDamage = default.MGDamage * 0.375;
}
else if( Level.Game.GameDifficulty < 4.0 )
{
MGDamage = default.MGDamage * 1.0;
}
else if( Level.Game.GameDifficulty < 5.0 )
{
MGDamage = default.MGDamage * 1.15;
}
else
{
MGDamage = default.MGDamage * 1.3;
}
}
}
HealingLevels[0] = Health/1.25;
HealingLevels[1] = Health/2.f;
HealingLevels[2] = Health/3.2;
HealingAmount = Health/4;
}
function bool MakeGrandEntry()
{
bShotAnim = true;
Acceleration = vect(0,0,0);
SetAnimAction('Entrance');
HandleWaitForAnim('Entrance');
GotoState('MakingEntrance');
return true;
}
state MakingEntrance
{
Ignores RangedAttack;
function Tick( float Delta )
{
Acceleration = vect(0,0,0);
global.Tick(Delta);
}
Begin:
Sleep(GetAnimDuration('Entrance'));
GotoState('InitialSneak');
}
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,, 'Bip01 Spine1');
super.PostNetBeginPlay();
TraceHitPos = vect(0,0,0);
bNetNotify = true;
}
function PlayTakeHit(vector HitLocation, int Damage, class<DamageType> DamageType)
{
if( Level.TimeSeconds - LastPainAnim < MinTimeBetweenPainAnims )
return;
if( Damage>=150 || (DamageType.name=='DamTypeStunNade' && rand(5)>3) || (DamageType.name=='DamTypeCrossbowHeadshot' && Damage>=200) )
PlayDirectionalHit(HitLocation);
LastPainAnim = Level.TimeSeconds;
if( Level.TimeSeconds - LastPainSound < MinTimeBetweenPainSounds )
return;
LastPainSound = Level.TimeSeconds;
PlaySound(HitSound[0], SLOT_Pain,2*TransientSoundVolume,,400);
}
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;
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);
}
}
else if( Level.TimeSeconds - LastSneakedTime > 20.0 )
{
if( FRand() < 0.3 )
{
LastSneakedTime = Level.TimeSeconds;
return;
}
SetAnimAction('BossHitF');
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())) )
{
SetAnimAction('BossHitF');
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('PreFireMG');
HandleWaitForAnim('PreFireMG');
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) )
{
Controller.Target = Other;
Controller.Focus = Other;
bShotAnim = true;
Acceleration = (Other.Location-Location);
SetAnimAction('MeleeClaw');
PlaySound(sound'Claw2s', SLOT_None);
HandleWaitForAnim('MeleeClaw');
}
}
simulated function AddTraceHitFX( vector HitPos )
{
local vector Start,SpawnVel,SpawnDir;
local float hitDist;
PlaySound(sound'KFOldSchoolZeds_Sounds.MinigunFire',SLOT_Misc,2,,1400,0.9+FRand()*0.2);
Start = GetBoneCoords('tip').Origin;
if( mTracer==none )
mTracer = Spawn(class'KFMod.KFNewTracer',,,Start);
else mTracer.SetLocation(Start);
if( mMuzzleFlash==none )
{
mMuzzleFlash = Spawn(class'NewMinigunMFlashOS');
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;
}
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,Momentum,damageType);
if( InstigatedBy != none )
{
DamagerDistSq = VSizeSquared(Location - InstigatedBy.Location);
if( (ChargeDamage > 200 && DamagerDistSq < (500 * 500)) || DamagerDistSq < (100 * 100) )
{
SetAnimAction('BossHitF');
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('BossHitF');
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()
{
OnlineHeadshotOffset=default.OnlineHeadshotOffset;
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 )
{
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--;
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;
Dir = Normal(vector(R)+VRand()*0.06); //*0.04
End = Start+Dir*10000;
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('');
}
if( bFireAtWill )
FireMGShot();
Sleep(0.05);
}
}
state FireMissile
{
Ignores RangedAttack;
function bool ShouldChargeFromDamage()
{
return false;
}
function BeginState()
{
OnlineHeadshotOffset=default.OnlineHeadshotOffset;
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(Sound'KFOldSchoolZeds_Sounds.Shared.LAWFire');
Spawn(class'BossLAWProjOS',,,Start,R);
bShotAnim = true;
Acceleration = vect(0,0,0);
SetAnimAction('FireEndMG');
HandleWaitForAnim('FireEndMG');
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;
return super.MeleeDamageTarget(hitdamage, pushdir);
}
state Charging
{
function bool CanSpeedAdjust()
{
return false;
}
function bool ShouldChargeFromDamage()
{
return false;
}
//Dont play hit anims while charging so people don't complain about broken head hitboxes :)
function OldPlayHit(float Damage, Pawn InstigatedBy, vector HitLocation, class<DamageType> damageType, vector Momentum, optional int HitIndex){}
function BeginState()
{
bChargingPlayer = true;
if( Level.NetMode!=NM_DedicatedServer )
PostNetReceive();
NumChargeAttacks = Rand(2) + 1;
OnlineHeadshotOffset.Z=60;
OnlineHeadshotOffset.X=30;
}
function EndState()
{
SetGroundSpeed(GetOriginalGroundSpeed());
bChargingPlayer = false;
ChargeDamage = 0;
if( Level.NetMode!=NM_DedicatedServer )
PostNetReceive();
OnlineHeadshotOffset=default.OnlineHeadshotOffset;
LastChargeTime = Level.TimeSeconds;
}
function Tick( float Delta )
{
if( NumChargeAttacks <= 0 )
{
GoToState('');
}
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();
}
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();
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);
}
}
function bool MeleeDamageTarget(int hitdamage, vector pushdir)
{
return Global.MeleeDamageTarget(hitdamage, pushdir*1.5);
}
function Tick( float Delta )
{
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();
}
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++;
if( SneakCount > 1000 || (Controller != none && ControllerBossOS(Controller).bAlreadyFoundEnemy) ) //BossZombieController to ControllerBossOS
{
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('Bip01 R Finger0').Origin);
CurrentNeedle.DroppedNow();
CurrentNeedle = none;
}
}
simulated function NotifySyringeA()
{
if( Level.NetMode!=NM_Client )
{
if( SyringeCount<3 )
SyringeCount++;
if( Level.NetMode!=NM_DedicatedServer )
PostNetReceive();
}
if( Level.NetMode!=NM_DedicatedServer )
{
DropNeedle();
CurrentNeedle = Spawn(class'BossHPNeedle');
AttachToBone(CurrentNeedle,'Bip01 R Finger0');
}
}
function NotifySyringeB()
{
if( Level.NetMode != NM_Client )
{
Health += HealingAmount;
bHealed = true;
}
}
simulated function NotifySyringeC()
{
if( Level.NetMode!=NM_DedicatedServer && CurrentNeedle!=none )
{
CurrentNeedle.Velocity = vect(-45,300,-90) >> Rotation;
DropNeedle();
}
}
simulated function PostNetReceive()
{
if( bClientMiniGunning != bMinigunning )
{
bClientMiniGunning = bMinigunning;
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,'SyringeBoneOne');
Break;
Case 2:
SetBoneScale(3,0,'SyringeBoneOne');
SetBoneScale(4,0,'SyringeBoneTwo');
Break;
Case 3:
SetBoneScale(3,0,'SyringeBoneOne');
SetBoneScale(4,0,'SyringeBoneTwo');
SetBoneScale(5,0,'SyringeBoneThree');
Break;
default:
SetBoneScale(3,1,'SyringeBoneOne');
SetBoneScale(4,1,'SyringeBoneTwo');
SetBoneScale(5,1,'SyringeBoneThree');
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=='BossHitF')
{
AnimBlendParams(1, 1.0, 0.0,, 'Bip01 Spine1');
PlayAnim(AnimName,, 0.0, 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];
CurrentDamtype = ZombieDamType[meleeAnimIndex];
}
ExpectingChannel = DoAnimAction(NewAction);
if( Controller != none )
{
ControllerBossOS(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;
}
}
simulated function HandleWaitForAnim( name NewAnim )
{
local float RageAnimDur;
Controller.GoToState('WaitForAnim');
RageAnimDur = GetAnimDuration(NewAnim);
ControllerBossOS(Controller).SetWaitForAnimTimout(RageAnimDur,NewAnim); //BossZombieController to ControllerBossOS
}
simulated function bool AnimNeedsWait(name TestAnim)
{
if(TestAnim == 'FireMG' ||TestAnim == 'PreFireMG' || TestAnim == 'FireEndMG' || TestAnim == 'Heal'
|| TestAnim == 'KnockDown' || TestAnim == 'Entrance' || TestAnim == 'VictoryLaugh' )
{
return true;
}
return false;
}
simulated function HandleBumpGlass()
{
}
function bool FlipOver()
{
return false;
}
function bool ShouldChargeFromDamage()
{
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 TakeDamage( int Damage, Pawn InstigatedBy, Vector Hitlocation, Vector Momentum, class<DamageType> damageType, optional int HitIndex)
{
local float DamagerDistSq;
local float UsedPipeBombDamScale;
if ( class<DamTypeCrossbow>(damageType) == none && class<DamTypeCrossbowHeadShot>(damageType) == none )
{
bOnlyDamagedByCrossbow = false;
}
if ( class<DamTypePipeBomb>(damageType) != none )
{
UsedPipeBombDamScale = FMax(0,(1.0 - PipeBombDamageScale));
PipeBombDamageScale += 0.075;
if( PipeBombDamageScale > 1.0 )
{
PipeBombDamageScale = 1.0;
}
Damage *= UsedPipeBombDamScale;
}
super.TakeDamage(Damage,instigatedBy,hitlocation,Momentum,damageType);
if( Level.TimeSeconds - LastDamageTime > 10 )
{
ChargeDamage = 0;
}
else
{
LastDamageTime = Level.TimeSeconds;
ChargeDamage += Damage;
}
if( ShouldChargeFromDamage() && ChargeDamage > 200 )
{
if( InstigatedBy != none )
{
DamagerDistSq = VSizeSquared(Location - InstigatedBy.Location);
if( DamagerDistSq < (700 * 700) )
{
SetAnimAction('BossHitF');
ChargeDamage=0;
LastForceChargeTime = Level.TimeSeconds;
GoToState('Charging');
return;
}
}
}
if( Health<=0 || SyringeCount==3 || IsInState('Escaping') || IsInState('KnockDown'))
return;
if( ((SyringeCount==0 && Health<HealingLevels[0]) || (SyringeCount==1 && Health<HealingLevels[1]) || (SyringeCount==2 && Health<HealingLevels[2]) ))
{
bShotAnim = true;
Acceleration = vect(0,0,0);
SetAnimAction('KnockDown');
HandleWaitForAnim('KnockDown');
KFMonsterController(Controller).bUseFreezeHack = true;
GoToState('KnockDown');
}
}
function OldPlayHit(float Damage, Pawn InstigatedBy, vector HitLocation, class<DamageType> damageType, vector Momentum, optional int HitIndex)
{
if(LastChainGunTime<Level.TimeSeconds + 1 || LastMissileTime<Level.TimeSeconds + 1)
return;
else
super.OldPlayHit(Damage,InstigatedBy,HitLocation,damageType,Momentum,HitIndex);
}
function DoorAttack(Actor A)
{
if ( bShotAnim )
return;
else if ( A!=none )
{
Controller.Target = A;
bShotAnim = true;
Acceleration = vect(0,0,0);
SetAnimAction('PreFireMG');
HandleWaitForAnim('PreFireMG');
MGFireCounter = Rand(20);
GoToState('FireMissile');
}
}
function RemoveHead();
function PlayDirectionalHit(Vector HitLoc);
function bool SameSpeciesAs(Pawn P)
{
return false;
}
function bool SetBossLaught()
{
local Controller C;
local string sHealthInfo;
GoToState('');
bShotAnim = true;
Acceleration = vect(0,0,0);
SetAnimAction('VictoryLaugh');
HandleWaitForAnim('VictoryLaugh');
bIsBossView = true;
bSpecialCalcView = true;
sHealthInfo = class'Utility'.static.ParseTagsStatic("^r^" $ MenuName $ "^w^'s health is ^b^" $ health $ " ^w^/ ^b^" $ HealthMax $ "^w^. Syringes used - ^b^" $ SyringeCount);
for (C = Level.ControllerList; C != none; C = C.NextController)
{
// ignore Messaging Spectators (Web Admin)
if (c.bIsPlayer && PlayerController(C) != none)
{
PlayerController(C).SetViewTarget(self);
PlayerController(C).ClientSetViewTarget(self);
PlayerController(C).ClientSetBehindView(true);
// send a cute message to loosers
PlayerController(C).teamMessage(none, sHealthInfo, 'ZombieBossOS');
}
}
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;
}
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);
if( Anim == 'MeleeImpale' )
{
bDamagedSomeone = MeleeDamageTarget(UsedMeleeDamage, PushDir);
}
else
{
OldTarget = Controller.Target;
foreach DynamicActors(class'KFHumanPawn', P)
{
if ( (P.Location - Location) dot PushDir > 0.0 )
{
Controller.Target = P;
bDamagedSomeone = bDamagedSomeone || MeleeDamageTarget(UsedMeleeDamage, damageForce * Normal(P.Location - Location));
}
}
Controller.Target = OldTarget;
}
MeleeRange = default.MeleeRange;
}
static simulated function PreCacheMaterials(LevelInfo myLevel)
{
myLevel.AddPrecacheMaterial(FinalBlend'KFPatch2.BossHairFB');
myLevel.AddPrecacheMaterial(FinalBlend'KFOldSchoolZeds_Textures.Patriarch.BossCloakFizzleFB');
myLevel.AddPrecacheMaterial(Finalblend'KFOldSchoolZeds_Textures.Patriarch.BossGlowFB');
myLevel.AddPrecacheMaterial(Texture'KFPatch2.BossBits');
myLevel.AddPrecacheMaterial(Texture'KFPatch2.GunPoundSkin');
myLevel.AddPrecacheMaterial(Texture'KFPatch2.BossGun');
myLevel.AddPrecacheMaterial(Texture'KillingFloorLabTextures.LabCommon.voidtex');
myLevel.AddPrecacheMaterial(Shader'KFPatch2.LaserShader');
myLevel.AddPrecacheMaterial(Shader'KFOldSchoolZeds_Textures.BossCloakShader');
}
defaultproperties
{
ChargingAnim="RunF"
HealingLevels(0)=5600
HealingLevels(1)=3500
HealingLevels(2)=2187
HealingAmount=1750
MGDamage=6.000000
ClawMeleeDamageRange=85.000000
ImpaleMeleeDamageRange=45.000000
MoanVoice=SoundGroup'KFOldSchoolZeds_Sounds.Patriarch.Patriarch_Speech'
ZapThreshold=5.000000
ZappedDamageMod=1.250000
ZapResistanceScale=1.000000
bHarpoonToHeadStuns=False
bHarpoonToBodyStuns=False
DamageToMonsterScale=5.000000
ZombieFlag=3
MeleeDamage=100
damageForce=170000
bFatAss=True
KFRagdollName="FleshPoundRag"
JumpSound=Sound'KFOldSchoolZeds_Sounds.Shared.Male_ZombieJump'
bMeleeStunImmune=True
CrispUpThreshhold=1
bCanDistanceAttackDoors=True
bUseExtendedCollision=True
ColOffset=(Z=65.000000)
ColRadius=27.000000
ColHeight=25.000000
PlayerCountHealthScale=0.750000
BurningWalkFAnims(0)="WalkF"
BurningWalkFAnims(1)="WalkF"
BurningWalkFAnims(2)="WalkF"
BurningWalkAnims(0)="WalkF"
BurningWalkAnims(1)="WalkF"
BurningWalkAnims(2)="WalkF"
OnlineHeadshotOffset=(X=2.000000,Y=-6.000000,Z=74.000000)
OnlineHeadshotScale=1.100000
MotionDetectorThreat=10.000000
bBoss=True
HitSound(0)=SoundGroup'KFOldSchoolZeds_Sounds.Patriarch.Patriarch_Pain'
DeathSound(0)=SoundGroup'KFOldSchoolZeds_Sounds.Shared.Male_ZombieDeath'
ScoringValue=500
IdleHeavyAnim="BossIdle"
IdleRifleAnim="BossIdle"
RagDeathVel=80.000000
RagDeathUpKick=100.000000
MeleeRange=10.000000
GroundSpeed=120.000000
WaterSpeed=120.000000
HealthMax=4000.000000
Health=4000
HeadRadius=8.200000
HeadHeight=8.500000
MenuName="Patriarch 2.5"
ControllerClass=Class'KFOldSchoolZeds.ControllerBossOS'
MovementAnims(0)="WalkF"
MovementAnims(1)="WalkF"
MovementAnims(2)="WalkF"
MovementAnims(3)="WalkF"
TurnLeftAnim="BossHitF"
TurnRightAnim="BossHitF"
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"
AmbientSound=Sound'KFOldSchoolZeds_Sounds.Shared.Male_ZombieBreath'
Mesh=SkeletalMesh'KFBossOld.Boss'
PrePivot=(Z=3.000000)
Skins(0)=FinalBlend'KFPatch2.BossHairFB'
Skins(1)=Texture'KFPatch2.BossBits'
Skins(2)=Texture'KFPatch2.GunPoundSkin'
Skins(3)=Texture'KFPatch2.BossGun'
Skins(4)=Texture'KFPatch2.BossBits'
Skins(5)=Texture'KFPatch2.BossBits'
Skins(6)=Shader'KFPatch2.LaserShader'
bNetNotify=False
Mass=1000.000000
RotationRate=(Yaw=36000,Roll=0)
}