// 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 damageType, float headshotLevel, KFPlayerReplicationInfo KFPRI){ return false; } function bool CheckMiniFlinch( int flinchScore, Pawn instigatedBy, Vector hitLocation, Vector momentum, class 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 && LastChainGunTime1500 && 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 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 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, 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 (5.0 + 5.0 * FRand()) ) { return true; } return false; } function TakeDamageClient(int Damage, Pawn InstigatedBy, Vector Hitlocation, Vector Momentum, class 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(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(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> Rotation); Return True; } // Overridden to do a cool slomo death view of the patriarch dying function Died(Controller Killer, class 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 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' }