Class Ext_T_ZEDHelper extends Info transient DependsOn(Ext_TraitZED_Summon) config(ServerExt); var config array ZedTypes; // Dummy hack, not really config but merely just for being able to set class on defaults. var() float FriendlyScalar; var Pawn PawnOwner; var byte CurLevel,RespawnHelperTime,NoLiveCounter; var KFPawn_Monster LiveHelper; var class PrevMonster; var float PrevMonsterHP; var float HPScale,DamageScale; var int OldKillsValue; var bool bNeedsKillZed,bIsExtra; function PostBeginPlay() { PawnOwner = Pawn(Owner); bNeedsKillZed = true; if (PawnOwner==None) Destroy(); else { OldKillsValue = PawnOwner.PlayerReplicationInfo.Kills; SetTimer(1+(FRand()*0.1),true); } } function Timer() { if (PawnOwner==None || PawnOwner.Health<=0 || PawnOwner.PlayerReplicationInfo==None) Destroy(); else if (bNeedsKillZed) { if (RespawnHelperTime>1) --RespawnHelperTime; if (OldKillsValue==PawnOwner.PlayerReplicationInfo.Kills) return; bNeedsKillZed = false; } else if (RespawnHelperTime>0) { if (--RespawnHelperTime==0) SpawnHelper(); } else if (LiveHelper==None || LiveHelper.Health<=0) { OldKillsValue = PawnOwner.PlayerReplicationInfo.Kills; bNeedsKillZed = true; RespawnHelperTime = 60; } else if (!HasLiveZeds()) { if (NoLiveCounter==0) { PrevMonster = LiveHelper.Class; PrevMonsterHP = (float(LiveHelper.Health) / LiveHelper.HealthMax); LiveHelper.Died(None,class'KFDT_Healing',vect(0,0,0)); RespawnHelperTime = 5; } else --NoLiveCounter; } else NoLiveCounter = 5; } function Destroyed() { if (LiveHelper!=None && LiveHelper.Health>0) LiveHelper.Died(None,class'DmgType_Suicided',vect(0,0,0)); } final function bool HasLiveZeds() { local KFPawn_Monster M; if (KFGameReplicationInfo(WorldInfo.GRI).WaveNum>=KFGameReplicationInfo(WorldInfo.GRI).WaveMax) // No pets on possible bonus waves. return false; foreach WorldInfo.AllPawns(Class'KFPawn_Monster',M) if (M.Health>0 && M.GetTeamNum()!=0) return true; return false; } final function SpawnHelper() { local class MC; local byte i; local vector V; local rotator R; local Controller C; local Ext_T_MonsterPRI PRI; local AkBaseSoundObject TempSound; local bool bFinalWave; if (PawnOwner.PlayerReplicationInfo==None || !HasLiveZeds()) { RespawnHelperTime = 3; return; } NoLiveCounter = 5; bFinalWave = KFGameReplicationInfo(WorldInfo.GRI).IsFinalWave(); if (bFinalWave && Class(PrevMonster)!=None) PrevMonster = None; MC = (PrevMonster!=None ? PrevMonster : PickRandomMonster(CurLevel,bFinalWave)); if (MC!=None) { R.Yaw = Rand(65536); if (MC.Default.SoundGroupArch!=None) { // Make no entrance roam (for FP's and Scrakes). TempSound = MC.Default.SoundGroupArch.EntranceSound; MC.Default.SoundGroupArch.EntranceSound = None; } for (i=0; i<40; ++i) { V = PawnOwner.Location; V.X += (FRand()*300.f-150.f); V.Y += (FRand()*300.f-150.f); if (!PawnOwner.FastTrace(V,PawnOwner.Location)) continue; LiveHelper = Spawn(MC,,,V,R); if (LiveHelper!=None) break; } if (MC.Default.SoundGroupArch!=None) MC.Default.SoundGroupArch.EntranceSound = TempSound; } if (LiveHelper==None) RespawnHelperTime = 2; else { // Downscale. LiveHelper.SetDrawScale(LiveHelper.DrawScale*FriendlyScalar); LiveHelper.SetCollisionSize(LiveHelper.GetCollisionRadius()*FriendlyScalar,LiveHelper.GetCollisionHeight()*FriendlyScalar); // Setup AI C = Spawn(LiveHelper.ControllerClass); if (KFAIController(C)!=None) { KFAIController(C).bCanTeleportCloser = false; KFAIController(C).DefaultCommandClass = class'Ext_AICommandBasePet'; KFAIController(C).StuckCheckInterval = 100000.f; // ~27 hours KFAIController(C).LastStuckCheckTime = WorldInfo.TimeSeconds; } LiveHelper.SpecialMoveHandler.SpecialMoveClasses[SM_Taunt] = class'Ext_AINoTaunt'; C.Possess(LiveHelper,false); // Set HP. LiveHelper.HealthMax = Clamp(LiveHelper.Default.Health,180,900)*HPScale; LiveHelper.Health = LiveHelper.HealthMax; LiveHelper.DamageScaling = DamageScale; LiveHelper.SetWeakGrabCoolDown(28800.f); // Never get grabbed (for 80 hours). LiveHelper.bWeakZedGrab = true; LiveHelper.bCanGrabAttack = false; // Scale by previous zed HP. if (PrevMonster!=None) { LiveHelper.Health *= PrevMonsterHP; PrevMonster = None; } // Setup PRI. if (C.PlayerReplicationInfo!=None) C.PlayerReplicationInfo.Destroy(); PRI = Spawn(class'Ext_T_MonsterPRI',LiveHelper); LiveHelper.PlayerReplicationInfo = PRI; C.PlayerReplicationInfo = PRI; PRI.PawnOwner = LiveHelper; PRI.HealthMax = LiveHelper.HealthMax; PRI.MonsterName = Class'KFExtendedHUD'.Static.GetNameOf(MC); PRI.OwnerPRI = PawnOwner.PlayerReplicationInfo; PRI.MonsterType = MC; PRI.PlayerName = PawnOwner.PlayerReplicationInfo.PlayerName$"'s "$PRI.MonsterName; PRI.OwnerController = PawnOwner.Controller; if (PawnOwner.PlayerReplicationInfo.Team!=None) PawnOwner.PlayerReplicationInfo.Team.AddToTeam(C); PRI.Timer(); if (WorldInfo.NetMode!=NM_DedicatedServer) PRI.NotifyOwner(); } } final function SetDamageScale(float Sc) { DamageScale = Default.DamageScale*Sc; if (LiveHelper!=None) LiveHelper.DamageScaling = DamageScale; } final function SetHealthScale(float Sc) { HPScale = Default.HPScale*Sc; } static final function LoadMonsterList() { local int i,j; local array SA; local class C; Default.ZedTypes.Length = class'Ext_TraitZED_Summon'.Default.ZedTypes.Length; for (i=0; i(DynamicLoadObject(SA[j],Class'Class')); if (C==None) continue; Default.ZedTypes[i].Zeds[Default.ZedTypes[i].Zeds.Length] = C; } if (Default.ZedTypes[i].Zeds.Length==0) Default.ZedTypes[i].Zeds[Default.ZedTypes[i].Zeds.Length] = Class'KFPawn_ZedClot_Alpha'; } } static final function class PickRandomMonster(byte Level, bool bNotBoss) { local byte i; local class Res; Level = Min(Default.ZedTypes.Length-1,Level); for (i=0; i<5; ++i) { Res = Default.ZedTypes[Level].Zeds[Rand(Default.ZedTypes[Level].Zeds.Length)]; if (!bNotBoss || class(Res)==None) break; } if (bNotBoss && class(Res)!=None) Res = Class'KFPawn_ZedFleshpound'; return Res; } defaultproperties { RespawnHelperTime=1 HPScale=0.5 DamageScale=2 FriendlyScalar=0.65 }