--这个AI比较特殊,只会且只能在主城使用,所以可以访问战斗外部代码 local base = require("Battle/Logic/AI/Decision/Decision_Base") local Decision_TeamFollow = class("Decision_TeamFollow", base) local EPhase = { CHASING_LEADER = 1, --距离队长很远,没有进入火车连接,此时追着队长跑 CHAINED = 2, --距离队长较近后,进入火车连接,此时追着属于自己的槽位跑 } local ABOARD_DISTANCE_SQR = 25 local RUN_TO_PATH_TOLERANCE_SQR = 2 local FOLLOW_NEAR_DIST_SQR = 1 local Const_Vector2_Zero = FP.Const.Vector2_Zero --组队跟随 --[[ 初始化 --]] function Decision_TeamFollow:initialize(entity) base.initialize(self,entity) --类型 self.Type = LuaConst.DecisionType.TeamFollow --瞬时 self.IsOnce = false end --[[ 进入该Decision --]] function Decision_TeamFollow:DecisionIn(entity, decisionData) local aiBaseComponent = entity.components.aiBase aiBaseComponent:ClearDecisionRecord(decisionData) local decisionRecordDic = aiBaseComponent:GetDecisionRecordDic(decisionData) decisionRecordDic.goAboardFrame = -99999 decisionRecordDic.dontAutoRide = true end --[[ 退出该Decision --]] function Decision_TeamFollow:DecisionOut(entity, decisionData) entity.components.aiBase:ClearDecisionRecord(decisionData) --entity.components.autoFight:StopAutoFindPath() if CTL.Team.IsMemberInTeam(BattleDataGetter.GetAcID()) then MDL.Team.TeamFollowReq(false) end end function Decision_TeamFollow:IsInTeamFollowState(entity) return MDL.Team.IsFollowing() end --[[ 能否进入 --]] function Decision_TeamFollow:CanEnter(entity, decisionData, command) local comp_AutoFight = entity.components.autoFight return comp_AutoFight.IsAutoFindPath and self:IsInTeamFollowState() end --[[ 能否退出 --]] function Decision_TeamFollow:CanExit(entity, decisionData, command) local comp_AutoFight = entity.components.autoFight if not comp_AutoFight.IsAutoFindPath then return true end local teamLeader = self:GetTeamLeaderEntity() --如果不处于组队跟随状态,或者自己是队长,就退出组队跟随 return not self:IsInTeamFollowState() or teamLeader == entity end function Decision_TeamFollow:GetTeamLeaderEntity() local teamLeaderAcid = CTL.Team.GetTeamLeaderId() if teamLeaderAcid then return SyncMoveManager.GetEntityByAcid(teamLeaderAcid) end end function Decision_TeamFollow:GetTargetPosByFollowIndex(entity, teamLeaderPos, followIndex, teamLeaderAcid) local pos if followIndex == 1 then pos = teamLeaderPos else repeat local chasingTeammateAcid = MDL.Team.GetTeammateAcidByFollowIndex(followIndex - 1, teamLeaderAcid) if not chasingTeammateAcid then logging.error(LTAG.ERROR, "HYW : GetTeammateAcidByFollowIndex failed! followIndex - 1: ", followIndex - 1 , " teamLeaderAcid : ", teamLeaderAcid) break end local targetEntity = SyncMoveManager.GetEntityByAcid(chasingTeammateAcid) if not targetEntity then --正常情况,比如目标玩家下线了,此时直接追队长 pos = teamLeaderPos break end pos = targetEntity:GetLogicPos() until true end return pos end --是否可以上火车了 function Decision_TeamFollow:CanGoAboard(entity, teamLeaderPos) local followIndex = MDL.Team.GetFollowIndexOfEntity(entity) if followIndex and followIndex >= 0 then -- 已经跟随了(在火车队伍里了),不要再跟随上火车了。 return false end return not FP.V2.IsDistanceSqrGreater(entity:GetLogicPos(), teamLeaderPos, ABOARD_DISTANCE_SQR) end function Decision_TeamFollow:ShouldRunToPath(entity, targetPos, decisionRecordDic) if targetPos then if decisionRecordDic.isRunning then return FP.V2.IsDistanceSqrGreater(entity:GetLogicPos(), targetPos, FOLLOW_NEAR_DIST_SQR) else return FP.V2.IsDistanceSqrGreater(entity:GetLogicPos(), targetPos, RUN_TO_PATH_TOLERANCE_SQR) end else return false end end local CONVEY_REQ_INTERVAL = 40 --40帧才能请求一次传送 local _pathLength = FP.newV1(0) local _point = FP.newV2(0,0) function Decision_TeamFollow:ShouldConveyToLeader(entity, decisionRecordDic, teamLeader) local should = false decisionRecordDic.conveyReqValidFrame = decisionRecordDic.conveyReqValidFrame or 0 if decisionRecordDic.conveyReqValidFrame <= LogicUpdateManager._clientHandleFrame then if teamLeader then _pathLength:Reset(0) local pathPoints = entity.components.path.Path _point:Reset(entity:GetLogicPos()) if pathPoints then for i,v in ipairs(pathPoints) do _pathLength:Add(_point:Sub(v):Magnitude()) _point:Reset(v) end if _pathLength >= MDL.Team.GetTeamFollowMaxDistanceC() then should = true end end else should = true end end return should end local GO_ABOARD_INTERVAL = 20 --上车消息发送间隔为20帧 --尝试上车 function Decision_TeamFollow:TryGoAboard(entity, teamLeaderPos, decisionRecordDic) if LogicUpdateManager._clientHandleFrame - decisionRecordDic.goAboardFrame > GO_ABOARD_INTERVAL then if self:CanGoAboard(entity, teamLeaderPos) then self:GoAboard(entity,decisionRecordDic) end end end function Decision_TeamFollow:GoAboard(entity, decisionRecordDic) decisionRecordDic.goAboardFrame = LogicUpdateManager._clientHandleFrame MDL.Team.SendC2GTeamMemberFollowIndex() -- 发送这个协议后,服务器会判断跟随index是否变化,如果变化则向所有客户端发回MessageS2CRoleChangeInfo roleShowInfo.snakeIndex 修改followIndex end local SMALL_DIST = FP.newV1(0.05) --[[ 输出指令 --]] function Decision_TeamFollow:SetCommand(entity, decisionData, command) --本AI比较特殊,只会且能在主城使用,可以访问战斗外部代码 local teamLeader = self:GetTeamLeaderEntity() local decisionRecordDic = entity.components.aiBase:GetDecisionRecordDic(decisionData) if teamLeader ~= entity then --teamLeader可能为nil,因为entity离队长很远,队长没刷出来 if teamLeader then local teamLeaderAcid = SyncMoveManager.GetAcIdOfEntity(teamLeader) local teamLeaderPos = MDL.Team.GetTeamLeaderPos(teamLeaderAcid) --teamLeaderPos可能为nil local targetPos = nil local curPhase local followIndex = MDL.Team.GetFollowIndexByAcid(BattleDataGetter.GetAcID()) if followIndex and followIndex > 0 then curPhase = EPhase.CHAINED targetPos = self:GetTargetPosByFollowIndex(entity, teamLeaderPos, followIndex, teamLeaderAcid) else curPhase = EPhase.CHASING_LEADER targetPos = teamLeaderPos end if self:ShouldRunToPath(entity, targetPos,decisionRecordDic) then self:CommandFindPathAndRunTo(entity, targetPos, command, true, decisionData) if self:ShouldConveyToLeader(entity, decisionRecordDic, teamLeader) then -- 寻路太远了,则传送 self:SendConveyToLeaderReq(decisionRecordDic) else decisionRecordDic.isRunning = true if curPhase == EPhase.CHASING_LEADER then self:TryGoAboard(entity, teamLeaderPos, decisionRecordDic) elseif curPhase == EPhase.CHAINED then local offset = targetPos - entity:GetLogicPos() if offset:Magnitude() > SMALL_DIST then command.MoveDir = offset command.RotateDir:Reset(command.MoveDir) end end end else decisionRecordDic.isRunning = false self:TryGoAboard(entity, teamLeaderPos, decisionRecordDic) end else --如果队长没有刷出来,就尝试请求传送到队长身边 if self:ShouldConveyToLeader(entity, decisionRecordDic, teamLeader) then self:SendConveyToLeaderReq(decisionRecordDic) end end end end function Decision_TeamFollow:SendConveyToLeaderReq(decisionRecordDic) decisionRecordDic.conveyReqValidFrame = LogicUpdateManager._clientHandleFrame + CONVEY_REQ_INTERVAL MDL.Team.SendTeamMemberConveyToLeaderReq() end return Decision_TeamFollow