local base = require("Battle/Logic/AI/Decision/Decision_Base") local Decision_Patrol = class("Decision_Patrol", base) local private = {} --[[ 初始化 --]] function Decision_Patrol:initialize(entity) --类型 self.Type = LuaConst.DecisionType.Patrol --瞬时 self.IsOnce = false end --[[ 进入该Decision --]] function Decision_Patrol:DecisionIn(entity, decisionData) local characterData = entity.components.base.CurCharacterData --重新索敌 local warnRange = characterData.WarnRange SystemManager.OnSearchTarget(entity, 1, warnRange, 1, 0, entity.components.target.IgnoreCanNotBeTargetBuffInDecisionPatrolOrChangeTarget) entity.components.patrol.PatrolFrame = 0 end --[[ 退出该Decision --]] function Decision_Patrol:DecisionOut(entity, decisionData) if entity.components.base.CurCharacterData.PatrolType == LuaConst.PatrolType.PatrolOnPath then entity.components.patrol.ExitPatrolPoint = entity.components.move.LogicPos end entity.components.aiBase:ClearDecisionRecord(decisionData) end --[[ 能否进入 --]] function Decision_Patrol:CanEnter(entity, decisionData, command) --若无目标,则进行巡逻 return not entity.components.target.Target end --[[ 退出条件: 有群组时:Leader在执行策划配置的AI 或 目标在警戒范围内 无群组时:目标在警戒范围内 --]] function Decision_Patrol:CanExit(entity, decisionData, command) local comp_Target = entity.components.target local leader = entity.components.group.LeaderEntity local warnRange = entity.components.base.CurCharacterData.WarnRange if leader and leader.components.aiBase.CurDecisionPriority < LuaConst.GroupDecisionPriority then return true elseif warnRange == FP.Const.Zero or not comp_Target.Target then --警戒范围为0 或 无目标 return false elseif comp_Target.ToTargetDisSqr <= warnRange then --目标在警戒范围内 return true else return false end end local LAST_SEARCH_TARGET_FRAMSE_KEY = "lastSearchTargetFrame" --[[ 输出指令 --]] function Decision_Patrol:SetCommand(entity, decisionData, command) local characterData = entity.components.base.CurCharacterData --在巡逻过程中,每 10 帧索一次敌 local lastSearchTargetFrame = entity.components.aiBase:GetDecisionRecord(decisionData,LAST_SEARCH_TARGET_FRAMSE_KEY) or -99999 local passFrame = LogicUpdateManager._clientHandleFrame - lastSearchTargetFrame if passFrame ~= 0 and passFrame >= 10 and not entity.components.target.Target then entity.components.aiBase:SetDecisionRecord(decisionData,LAST_SEARCH_TARGET_FRAMSE_KEY,LogicUpdateManager._clientHandleFrame) local warnRange = characterData.WarnRange SystemManager.OnSearchTarget(entity, 1, warnRange, 1, 0, entity.components.target.IgnoreCanNotBeTargetBuffInDecisionPatrolOrChangeTarget) end if characterData.PatrolType == LuaConst.PatrolType.PatrolOnPath then return self:SetCommand_PatrolOnPath(entity, decisionData, command) else return self:SetCommand_PatrolInRange(entity, decisionData, command) end end function Decision_Patrol:SetCommand_PatrolInRange(entity, decisionData, command) command.RotateDir:Reset(entity.components.patrol.PatrolDir) command.MoveDir:Reset(command.RotateDir) command.DecisionID = decisionData.ID return command end function Decision_Patrol:SetCommand_PatrolOnPath(entity, decisionData, command) local path = entity.components.base.CurCharacterData.PatrolParam return self:PatrolOnPath(entity, command, false, path) end local LAST_CHANGE_DIR_FRAME_KEY = "lastChangeDirFrame" function Decision_Patrol:Handle(entity, decisionData, stateType, animName) local patrolComponent = entity.components.patrol local aiBaseComponent = entity.components.aiBase local moveComponent = entity.components.move local animComponent = entity.components.anim local characterData = entity.components.base.CurCharacterData local patrolRange = FP.newV1(characterData.PatrolParam) if patrolRange == FP.Const.Zero or characterData.PatrolType == LuaConst.PatrolType.Idle then --巡逻范围为0 或 巡逻类型为 Idle 时,Idle if aiBaseComponent.CurStateType == LuaConst.StateType.Idle then return LuaConst.StateType.None, nil else return LuaConst.StateType.Idle, nil end end --巡逻行为:每Idle 3-5秒的随机时间,360°随机转换一次移动方向移动(使用walk_front_loop动作)3-5秒 if patrolComponent.PatrolFrame == 0 or patrolComponent.PatrolFrame >= patrolComponent.PatrolTimeLimit then patrolComponent.PatrolFrame = 0 aiBaseComponent:SetDecisionRecord(decisionData,LAST_CHANGE_DIR_FRAME_KEY,LogicUpdateManager._clientHandleFrame) patrolComponent.PatrolTimeLimit = LogicRandom.GetIntegerValue(nil, 60, 100 ,1010) local bornPoint = entity._logicInitData.BornPoint if FP.V2.IsDistanceSqrGreater(bornPoint, moveComponent.LogicPos, patrolRange) then --若出圈,往回走 patrolComponent.PatrolDir = bornPoint - moveComponent.LogicPos stateType = LuaConst.StateType.Common animName = "walk_front_loop" elseif aiBaseComponent.CurStateType == LuaConst.StateType.Idle then --若之前为Idle,朝随机方向移动 local randAngle = LogicRandom.GetRangeValue(entity, FP.Const.Zero, FP.Const.Degree360 ,1021) patrolComponent.PatrolDir = FP.V2.GetOffsetToAngle(randAngle, FP.Const.Vector2_Forward) stateType = LuaConst.StateType.Common animName = "walk_front_loop" else --若之前在移动,置为Idle patrolComponent.PatrolDir:Reset() stateType = LuaConst.StateType.Idle end elseif animComponent.IsCurAnimOver and aiBaseComponent.CurStateType == LuaConst.StateType.Common then --避免循环播放徘徊动画时无位移 stateType = LuaConst.StateType.Common animName = "walk_front_loop" end --patrolComponent.PatrolFrame = patrolComponent.PatrolFrame + 1--old 没有考虑间隔帧的情况 --new 考虑了间隔帧 local lastChangeDirFrame = aiBaseComponent:GetDecisionRecord(decisionData,LAST_CHANGE_DIR_FRAME_KEY) if lastChangeDirFrame then --这里不是每帧+1的意思 patrolComponent.PatrolFrame = 1 + LogicUpdateManager._clientHandleFrame - lastChangeDirFrame --(这里必须得+1 不然上面的patrolComponent.PatrolFrame == 0判断会一直通过) end return stateType, animName end return Decision_Patrol