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 }