--副将 local DecisionSystem = {} local LuaConst=LuaConst function DecisionSystem.HandleDecision(entity, command) local comp_AIBase = entity.components.aiBase --1.判断是否应退出当前决策 --是否满足退出DecisionGroup的条件(如果为true,则会重新从高priority的DecisionGroup开始扫描,进入第一个满足进入条件的DecisionGroup) local canExitByCondition = false --old↓ 没有考虑combine AI的情况 --这里得+1才是priority对应的数据 --local priorityDecisionGroup = comp_AIBase:GetDecisionGroupByPriority(comp_AIBase.CurDecisionPriority + 1) --old↑ local priorityDecisionGroup = comp_AIBase:GetDecisionGroupByPriority(comp_AIBase.CurDecisionPriority) --new 不用+1了 if priorityDecisionGroup then canExitByCondition = priorityDecisionGroup.ExitConditionNode:MeetCondition(entity) end --是否满足退出当前Decision的条件(如果为true,且canExitByCondition为false,则会试图切换同DecisionGroup的下一个Decision。如果没有成功切换下一个Decision,就会重新扫描所有DecisionGroup) local canExitByDecision = comp_AIBase.CurDecisionNode and comp_AIBase.CurDecisionNode:CanExit(entity, comp_AIBase.CurDecisionData, command) --若满足退出条件,则退出当前决策 if canExitByCondition or canExitByDecision then local runEntry = true if not canExitByCondition then if comp_AIBase.CurDecisionNode.Type ~= LuaConst.DecisionType.Broadcast then --群组广播的下个决策为广播的内容 local nextDecisionData = comp_AIBase.CurDecisionData.NextDecisionData if nextDecisionData then local decisionNode = DecisionManager.GetDecision(nextDecisionData.Type) if decisionNode and decisionNode:CanEnter(entity, nextDecisionData, command) then --满足该Decision的进入条件 --退出上一个Decision comp_AIBase.CurDecisionNode:DecisionOut(entity, comp_AIBase.CurDecisionData) --进入下一个Decision DecisionManager.SetNextDecision(nextDecisionData, entity, false) runEntry = false end end end end if runEntry then --跳出当前DecisionGroup(通过设置当前DG为优先级最低的DG(EntryDecisionGroup)实现) --判断 CurEntryData 跟 CurDecisionData 是不是同一个,如果是同一个,则既不要out,也不要RunEntryDecisionGroup if comp_AIBase.CurDecisionData ~= comp_AIBase.CurEntryData then --退出上一个Decision comp_AIBase.CurDecisionNode:DecisionOut(entity, comp_AIBase.CurDecisionData) --进入entry decisionGroup DecisionManager.RunEntryDecisionGroup(entity) end end end --2.判断是否被群组指令打断 --群组指令打断优先级更小的指令,且会被下一个群组指令打断 if comp_AIBase.CurDecisionPriority <= LuaConst.GroupDecisionPriority then local comp_Group = entity.components.group if comp_Group then local groupDecisionData = comp_Group.GroupDecisionData if groupDecisionData then local groupDecisionNode = DecisionManager.GetDecision(groupDecisionData.Type) if groupDecisionNode and groupDecisionNode:CanEnter(entity, groupDecisionData, command) then comp_AIBase.CurDecisionPriority = LuaConst.GroupDecisionPriority comp_AIBase.CurMinInterruptPriority = LuaConst.GroupDecisionPriority DecisionManager.SetNextDecision(groupDecisionData, entity, true) comp_Group:SetGroupDecisionData(nil) end end end end --2.判断是否被更高优先级的打断当前 local index = comp_AIBase.CurAiObject.PriorityDecisionGroupCount while(index > 0) do priorityDecisionGroup = comp_AIBase.CurAiObject.PriorityDecisionGroupList[index] if priorityDecisionGroup then --刷新CD if priorityDecisionGroup.CD > 0 then priorityDecisionGroup.CD = priorityDecisionGroup.CD - LuaConst.TimeSpanMs * comp_AIBase:GetAiUpdateInterval() end --↓ new --大修改 2020年7月7日 : 策划需求:是否可以被打断,需要走配置,CurMinInterruptPriority字段 ,Priority更大的,且Priority大于等于CurMinInterruptPriority的Decision,可以打断当前Decision -- 0号Decision依然可以被任何Priority大于0的Decision打断 if (comp_AIBase.CurDecisionPriority <= 0 and priorityDecisionGroup.Data.Priority > comp_AIBase.CurDecisionPriority or priorityDecisionGroup.Data.Priority > comp_AIBase.CurDecisionPriority and priorityDecisionGroup.Data.Priority >= comp_AIBase.CurMinInterruptPriority) and priorityDecisionGroup:IsAvailable(entity) then local decisionData = priorityDecisionGroup.DecisionDataList[1] if decisionData then local decisionNode = DecisionManager.GetDecision(decisionData.Type) if decisionNode and decisionNode:CanEnter(entity, decisionData, command) then --满足该Decision的进入条件 --重置CD if priorityDecisionGroup.Data.DelayCDMin == priorityDecisionGroup.Data.DelayCDMax then priorityDecisionGroup.CD = priorityDecisionGroup.Data.DelayCDMin else priorityDecisionGroup.CD = LogicRandom.GetIntegerValue(nil, priorityDecisionGroup.Data.DelayCDMin, priorityDecisionGroup.Data.DelayCDMax ,11) end --将该Decision置为当前Decision comp_AIBase:SetCurDecisionGroup(priorityDecisionGroup) --print("ID", entity._logicInitData.EntityID, "DecisionPriority", tostring(aiBaseComponent.CurDecisionPriority)) DecisionManager.SetNextDecision(decisionData, entity, true) end end end end index = index - 1 end --3.执行决策 if comp_AIBase.CurDecisionNode then return comp_AIBase.CurDecisionNode:SetCommand(entity, comp_AIBase.CurDecisionData, command) else --logging.error(LTAG.ERROR, "HYW : Ai 异常,CurDecisionNode为空,请立刻联系程序!") --一般来说是正常清空 return command end end function DecisionSystem.FilterDecision(entity, command, isOnce, stateType, animName) --这里的代码改过一版,之前isOnce的也会判断buffState,那样会导致在被控状态,所有的isOnce的都不handle,不符合策划需求 --写死判断 , if not isOnce then local buffState = entity.components.buff.CurrentBuffStates if not buffState:Get(LuaConst.SpecialBuffs.CanMove) and not buffState:Get(LuaConst.SpecialBuffs.CanSkill) and not buffState:Get(LuaConst.SpecialBuffs.CanRotate) then return false,stateType, animName end end --这里潜规则很多 --规则1、 command.DecisionID 是Decision(通常是瞬时Decision)在SetCommand里设置的。持续性Decision也可以设置,并且会带来额外的一个效果:Filter 提前return --规则2、 设置了 command.DecisionID 才会调用 Decision 节点的 Handle 方法。通常瞬时节点的实际业务代码会放在 Handle 方法里,所以在 SetCommand 里通常都会设置 DecisionID --规则3、 持续性 Decision 在设置了 command.DecisionID 后会造成 FilterManager.FilterCommand 里的提前 return --是 needReturn, stateType, animName = SystemManager.FilterDecision(entity, command, false, stateType, animName) 这一行下面会提前 return local decisionData = DecisionManager.GetDecisionData(command.DecisionID) if decisionData then local decisionNode = DecisionManager.GetDecision(decisionData.Type) if decisionNode and decisionNode.IsOnce == isOnce then return not isOnce, decisionNode:Handle(entity, decisionData, stateType, animName) end end return false, stateType, animName end function DecisionSystem.FilterDecision_NPC(entity, command, stateType, animName) local decisionData = DecisionManager.GetDecisionData(command.DecisionID) if decisionData then local decisionNode = DecisionManager.GetDecision(decisionData.Type) if decisionNode then return true, decisionNode:Handle(entity, decisionData, stateType, animName) end end return false, stateType, animName end function DecisionSystem.OnActive(entity) DecisionSystem.ActiveAI(entity) end --[[ 变身 --]] function DecisionSystem.OnSwitchHero(fromEntity, toEntity, dontBackground) if not dontBackground then DecisionSystem.DisActiveAI(fromEntity) DecisionSystem.ActiveAI(toEntity) --else --do nothing 因为切将前后单位的active状态不变 end end function DecisionSystem.AfterSwitchHero(fromEntity, toEntity, dontBackground) if fromEntity ~= toEntity then if fromEntity then if dontBackground then if not fromEntity.components.autoFight:IsAutoFight() then SystemManager.StopAutoFight(toEntity) end SystemManager.StartAutoFight(fromEntity)--这里要强行StartAutoFight,因为要重设AIID else if fromEntity.components.autoFight:IsAutoFight() then SystemManager.StopAutoFight(fromEntity) SystemManager.StartAutoFight(toEntity) end end end end end function DecisionSystem.OnBreak(entity, dontReset) if not dontReset then if entity:IsAlive() then local comp_AIBase = entity.components.aiBase if comp_AIBase then SystemManager.SetNextState(LuaConst.StateType.Idle, entity, 0) comp_AIBase.CurDecisionID = nil comp_AIBase.CurDecisionData = nil comp_AIBase.CurDecisionNode = nil comp_AIBase.CurDecisionPriority = -1 comp_AIBase.CurMinInterruptPriority = -1 end end end end --大变身 function DecisionSystem.OnChangeAvatar(entity) -- pvp自动战斗变身后不切换默认AI,用现在的自动战斗AI if entity.components.autoFight.IsSyncAuto then return end local comp_AIBase = entity.components.aiBase if entity:IsMonster() then --只有怪物的大变身需要重设AI,Hero大变身不重设 comp_AIBase:SetCurrentAIData(entity, entity.components.base.CurCharacterData) end comp_AIBase.CurDecisionID = nil comp_AIBase.CurDecisionData = nil comp_AIBase.CurDecisionNode = nil comp_AIBase.CurDecisionPriority = -1 comp_AIBase.CurMinInterruptPriority = -1 DecisionSystem.ActiveAI(entity) end -- 切换AI function DecisionSystem.SetAIByID(entity ,aiID) local comp_AIBase = entity.components.aiBase comp_AIBase:SetSingleCurrentAiByID(aiID) comp_AIBase.CurDecisionID = nil comp_AIBase.CurDecisionData = nil comp_AIBase.CurDecisionNode = nil DecisionSystem.ActiveAI(entity) end function DecisionSystem.OnBackground(entity) entity.components.aiBase.IsAIActive = false end function DecisionSystem.ActiveAI(entity) SystemManager.SetNextState(LuaConst.StateType.Idle, entity, 0) DecisionManager.RunEntryDecisionGroup(entity) entity.components.aiBase.IsAIActive = true end function DecisionSystem.DisActiveAI(entity) SystemManager.ChangeToSwitchAll(entity,false,LuaConst.SkillInterruptType.BreakEffect) SystemManager.SetNextState(LuaConst.StateType.Idle, entity, 0) DecisionManager.RunEntryDecisionGroup(entity) entity.components.aiBase.IsAIActive = false entity.components.move:SetWantDir() end return DecisionSystem