package.loaded.Constants = nil; require("Constants") require("Actors/AI/HumanBehaviors") require("Actors/AI/CrabBehaviors") function Create(self) ---------------- AI variables start ---------------- self.Ctrl = self:GetController() self.lateralMoveState = Actor.LAT_STILL self.deviceState = ACrab.STILL self.lastAIMode = Actor.AIMODE_NONE self.teamBlockState = Actor.NOTBLOCKED self.SentryFacing = self.HFlipped self.fire = false self.ReloadTimer = Timer() self.BlockedTimer = Timer() self.TargetLostTimer = Timer() self.PlayerInterferedTimer = Timer() self.PlayerInterferedTimer:SetSimTimeLimitMS(500) self.AlarmTimer = Timer() self.AlarmTimer:SetSimTimeLimitMS(500) -- check if this team is controlled by a human local Activ = ActivityMan:GetActivity() for player = Activity.PLAYER_1, Activity.MAXPLAYERCOUNT - 1 do if Activ:PlayerActive(player) and Activ:PlayerHuman(player) and self.Team == Activ:GetTeamOfPlayer(player) then self.isPlayerOwned = true break end end function self:CreatePatrolBehavior() self.NextBehavior = coroutine.create(HumanBehaviors.Patrol) self.NextCleanup = nil self.NextBehaviorName = "Patrol" end function self:CreateSentryBehavior() if not self.FirearmIsReady then return end self.NextBehavior = coroutine.create(CrabBehaviors.Sentry) self.NextCleanup = nil self.NextBehaviorName = "Sentry" end function self:CreateAttackBehavior() if self.FirearmIsReady then self.NextBehavior = coroutine.create(CrabBehaviors.ShootTarget) self.NextBehaviorName = "ShootTarget" else if self.FirearmIsEmpty then self:ReloadFirearm() end return end self.NextCleanup = function(self) self.fire = false self.Target = nil self.deviceState = ACrab.STILL end end function self:CreateSuppressBehavior() if self.FirearmIsReady then self.NextBehavior = coroutine.create(CrabBehaviors.ShootArea) self.NextBehaviorName = "ShootArea" else if self.FirearmIsEmpty then self:ReloadFirearm() end return end self.NextCleanup = function(self) self.fire = false self.UnseenTarget = nil self.deviceState = ACrab.STILL end end function self:CreateGoToBehavior() self.NextBehavior = coroutine.create(CrabBehaviors.GoToWpt) self.NextBehaviorName = "GoToWpt" self.NextCleanup = function(self) self.lateralMoveState = Actor.LAT_STILL self:UpdateMovePath() end end function self:CreateBrainSearchBehavior() self.NextBehavior = coroutine.create(HumanBehaviors.BrainSearch) self.NextCleanup = nil self.NextBehaviorName = "BrainSearch" end function self:CreateFaceAlarmBehavior() self.NextBehavior = coroutine.create(HumanBehaviors.FaceAlarm) self.NextBehaviorName = "FaceAlarm" self.NextCleanup = nil end ---------------- AI variables end ---------------- end function UpdateAI(self) if self.PlayerInterferedTimer:IsPastSimTimeLimit() then self.Behavior = nil -- remove the current behavior if self.BehaviorCleanup then self.BehaviorCleanup(self) -- clean up after the current behavior self.BehaviorCleanup = nil end self.Target = nil self.UnseenTarget = nil self.OldTargetPos = nil self.fire = false self.SentryFacing = self.HFlipped self.lastAIMode = Actor.AIMODE_NONE end self.PlayerInterferedTimer:Reset() if self.Target and not MovableMan:IsActor(self.Target) then self.Target = nil end if self.UnseenTarget and not MovableMan:IsActor(self.UnseenTarget) then self.UnseenTarget = nil end -- switch to the next behavior, if avaliable if self.NextBehavior then if self.BehaviorCleanup then self.BehaviorCleanup(self) end self.Behavior = self.NextBehavior self.BehaviorCleanup = self.NextCleanup self.BehaviorName = self.NextBehaviorName self.NextBehavior = nil end -- check if the AI mode has changed or if we need a new behavior if self.AIMode ~= self.lastAIMode or (not self.Behavior and not self.GoToBehavior) then self.Behavior = nil if self.BehaviorCleanup then self.BehaviorCleanup(self) -- stop the current behavior self.BehaviorCleanup = nil end -- select a new behavior based on AI mode if self.AIMode == Actor.AIMODE_GOTO then self:CreateGoToBehavior() elseif self.AIMode == Actor.AIMODE_PATROL then self:CreatePatrolBehavior() elseif self.AIMode == Actor.AIMODE_BRAINHUNT then self:CreateBrainSearchBehavior() else if self.AIMode ~= self.lastAIMode and self.AIMode == Actor.AIMODE_SENTRY then self.SentryFacing = self.HFlipped -- store the direction in which we should be looking end self:CreateSentryBehavior() end self.lastAIMode = self.AIMode end -- look for targets local FoundMO, HitPoint = CrabBehaviors.LookForTargets(self) if FoundMO then if self.Target and MovableMan:IsActor(self.Target) and FoundMO.ID == self.Target.ID then -- found the same target self.TargetOffset = SceneMan:ShortestDistance(self.Target.Pos, HitPoint, false) self.TargetLostTimer:Reset() self.ReloadTimer:Reset() elseif MovableMan:IsActor(FoundMO) then if FoundMO.Team == self.Team then -- found an ally if self.Target then if SceneMan:ShortestDistance(self.Pos, FoundMO.Pos, false).Magnitude < SceneMan:ShortestDistance(self.Pos, self.Target.Pos, false).Magnitude then self.Target = nil -- stop shooting end elseif FoundMO.ClassName ~= "ADoor" and SceneMan:ShortestDistance(self.Pos, FoundMO.Pos, false).Magnitude < self.Diameter + FoundMO.Diameter then self.BlockingActor = ToActor(FoundMO) -- this actor is blocking our path end else if FoundMO.ClassName == "AHuman" then FoundMO = ToAHuman(FoundMO) elseif FoundMO.ClassName == "ACrab" then FoundMO = ToACrab(FoundMO) elseif FoundMO.ClassName == "ACRocket" then FoundMO = ToACRocket(FoundMO) elseif FoundMO.ClassName == "ACDropShip" then FoundMO = ToACDropShip(FoundMO) elseif FoundMO.ClassName == "ADoor" then FoundMO = ToADoor(FoundMO) elseif FoundMO.ClassName == "Actor" then FoundMO = ToActor(FoundMO) else FoundMO = nil end if FoundMO then self.ReloadTimer:Reset() self.TargetLostTimer:Reset() if self.Target then -- check if this MO sould be targeted instead if HumanBehaviors.CalculateThreatLevel(self, FoundMO) > HumanBehaviors.CalculateThreatLevel(self, self.Target) + 0.2 then self.OldTargetPos = self.Target.Pos self.Target = FoundMO self.TargetOffset = SceneMan:ShortestDistance(self.Target.Pos, HitPoint, false) -- this is the distance vector from the target center to the point we hit with our ray self:CreateAttackBehavior() end else self.OldTargetPos = nil self.Target = FoundMO self.TargetOffset = SceneMan:ShortestDistance(self.Target.Pos, HitPoint, false) -- this is the distance vector from the target center to the point we hit with our ray self:CreateAttackBehavior() end end end end else -- no target found this frame if self.Target and self.TargetLostTimer:IsPastSimTimeLimit() then self.OldTargetPos = self.Target.Pos self.Target = nil -- the target has been out of sight for too long, ignore it end if self.ReloadTimer:IsPastSimMS(8000) then -- check if we need to reload self.ReloadTimer:Reset() if ToACrab(self).FirearmNeedsReload then self:ReloadFirearm() end end end -- run the selected behavior and delete it if it returns true if self.Behavior then local msg, done = coroutine.resume(self.Behavior, self) if not msg then ConsoleMan:PrintString(self.PresetName .. " behavior " .. self.BehaviorName .. " error:\n" .. done) -- print the error message done = true end if done then self.Behavior = nil if self.BehaviorCleanup then self.BehaviorCleanup(self) self.BehaviorCleanup = nil end end end -- listen and react to relevant AlarmEvents if not self.Target and not self.UnseenTarget then if self.AlarmTimer:IsPastSimTimeLimit() and HumanBehaviors.ProcessAlarmEvent(self) then self.AlarmTimer:Reset() end end if self.teamBlockState == Actor.IGNORINGBLOCK then if self.BlockedTimer:IsPastSimMS(20000) then self.teamBlockState = Actor.NOTBLOCKED end elseif self.teamBlockState == Actor.BLOCKED then -- we are blocked by a teammate, stop self.lateralMoveState = Actor.LAT_STILL if self.BlockedTimer:IsPastSimMS(10000) then self.BlockedTimer:Reset() self.teamBlockState = Actor.IGNORINGBLOCK self.BlockingActor = nil end else self.BlockedTimer:Reset() end -- controller states if self.fire then self.Ctrl:SetState(Controller.WEAPON_FIRE, true) end if self.deviceState == ACrab.AIMING then self.Ctrl:SetState(Controller.AIM_SHARP, true) end if self.lateralMoveState == Actor.LAT_LEFT then self.Ctrl:SetState(Controller.MOVE_LEFT, true) elseif self.lateralMoveState == Actor.LAT_RIGHT then self.Ctrl:SetState(Controller.MOVE_RIGHT, true) end end