class Ext_AICommandBasePet extends AICommand_Base_Zed; var transient Pawn OwnerPawn; var transient float NextSightCheckTime; final function vector PickPointNearOwner() { local byte i; local vector V,HL,HN,Start; Start = OwnerPawn.Location; if(OwnerPawn.Physics==PHYS_Falling) { if(Pawn.Trace(HL,HN,OwnerPawn.Location-vect(0,0,5000),OwnerPawn.Location,false,vect(20,20,60))!=None) Start = HL; } while(true) { ++i; V.X = FRand()-0.5; V.Y = FRand()-0.5; V = Start + Normal2D(V) * (100.f+FRand()*500.f); if(i<20 && !FastTrace(V,Start)) // Destination is inside a wall. continue; if(i<20 && FastTrace(V-vect(0,0,100),V)) // Destination is above a pit. continue; break; } OwnerPawn = None; return V; } final function bool CanSeeOwner() { local Pawn P; NextSightCheckTime = WorldInfo.TimeSeconds+1.f + FRand(); P = Ext_T_MonsterPRI(PlayerReplicationInfo)!=None ? Ext_T_MonsterPRI(PlayerReplicationInfo).OwnerController.Pawn : None; if(P!=None && !LineOfSightTo(P)) return false; return true; } state ZedBaseCommand { Begin: if(Pawn.Physics == PHYS_Falling) { DisableMeleeRangeEventProbing(); WaitForLanding(); } EnableMeleeRangeEventProbing(); // Check for any interrupt transitions CheckInterruptCombatTransitions(); // Select nearest enemy if current enemy is invalid if(Enemy == none || Enemy.Health <= 0 || !IsValidAttackTarget(KFPawn(Enemy))) SelectEnemy(); // Handle special case if I'm supposed to be attacking a door if(DoorEnemy != none && DoorEnemy.Health > 0 && VSizeSq(DoorEnemy.Location - Pawn.Location) < (DoorMeleeDistance * DoorMeleeDistance)) //200UU { `AILog(self$" DoorEnemy: "$DoorEnemy$" starting melee attack", 'Command_Base'); UpdateHistoryString("[Attacking : "$DoorEnemy$" at "$WorldInfo.TimeSeconds$"]"); class'AICommand_Attack_Melee'.static.Melee(Outer, DoorEnemy); } // See if we are close to our owner RecheckOwner: OwnerPawn = Ext_T_MonsterPRI(PlayerReplicationInfo)!=None ? Ext_T_MonsterPRI(PlayerReplicationInfo).OwnerController.Pawn : None; if(OwnerPawn!=None) { if(Enemy!=None && LineOfSightTo(OwnerPawn) && LineOfSightTo(Enemy)) // We have sight to our owner and can see enemy, go for it! { OwnerPawn = None; bWaitingOnMovementPlugIn = true; SetEnemyMoveGoal(self, true,,, ShouldAttackWhileMoving()); NextSightCheckTime = WorldInfo.TimeSeconds+2.f; while(bWaitingOnMovementPlugIn && bUsePluginsForMovement) { if(NextSightCheckTime640000.f || !LineOfSightTo(OwnerPawn)) // 800.f - Need to move closer to our owner. { bWaitingOnMovementPlugIn = true; SetMovePoint(PickPointNearOwner(),OwnerPawn,,300.f); while(bWaitingOnMovementPlugIn && bUsePluginsForMovement) { Sleep(0.03); } } else // Standing next to our owner. { OwnerPawn = None; Sleep(0.2+FRand()*0.5); } } else if(IsValidAttackTarget(KFPawn(Enemy))) { `AILog("Calling SetEnemyMoveGoal [Dist:"$VSize(Enemy.Location - Pawn.Location)$"] using offset of "$AttackRange$", because IsWithinBasicMeleeRange() returned false ", 'Command_Base'); bWaitingOnMovementPlugIn = true; SetEnemyMoveGoal(self, true,,, ShouldAttackWhileMoving()); while(bWaitingOnMovementPlugIn && bUsePluginsForMovement) { Sleep(0.03); } `AiLog("Back from waiting for the movement plug in!!!"); if(Enemy == none) { Sleep(FRand() + 0.1f); Goto('Begin'); } } else { `AILog("Enemy is invalid melee target" @ Enemy, 'Command_Base'); bFailedToMoveToEnemy = true; } // Check combat transitions CheckCombatTransition(); if(bFailedToMoveToEnemy) { if(bFailedPathfind) { bFailedPathfind = false; Sleep(0.f); } else { Sleep(0.f); } SetEnemy(GetClosestEnemy(Enemy)); } else { Sleep(0.f); } Goto('Begin'); } DefaultProperties { }