local Base = require("Battle/Logic/AI/Decision/Decision_Base") local Decision_UseSkill = class("Decision_UseSkill", Base) local SkillMissileSystem = require"Battle/Logic/Entity/System/SkillMissileSystem" local INSTANT_SKILL_MAX_WAIT_FRAME = 200 --瞬时技能最大等待帧数,如果瞬时技能释放意图等待时间超过该帧数,就无脑跳出 local MoveByHandleDistanceSqr = 9 -- 当技能时通过遥感移动时,要能追击目标,到达3米处停止 local newV2=FP.newV2 local IsDistanceSqrGreater = FP.V2.IsDistanceSqrGreater --[[ 初始化 --]] function Decision_UseSkill:initialize(entity) --类型 self.Type = LuaConst.DecisionType.UseSkill --瞬时 self.IsOnce = false end function Decision_UseSkill:GetSkillDataByDecisionData(decisionData, skillBaseComponent) if decisionData.IntParam1 > 0 then return skillBaseComponent:GetSkillDataBySkillIndex(decisionData.IntParam1) else return skillBaseComponent:GetSkillDataByKeyPosition(-decisionData.IntParam1) end end --[[ 能否进入 执行条件:能释放技能 --]] function Decision_UseSkill:CanEnter(entity, decisionData, command) -- ↓ new 访问统一接口 Decision_Base:CanAiUseSkill local skillBaseComponent = entity.components.skillBase local skillData = self:GetSkillDataByDecisionData(decisionData, skillBaseComponent) if skillData then return self:CanAiUseSkill(entity, skillBaseComponent, skillData, ConditionManager.GetConditionDataTree(skillData.AutoBattleCondition)) else return false end -- ↑ new end --[[ 能否退出 完成条件:技能后摇时间到 --]] function Decision_UseSkill:CanExit(entity, decisionData, command) local skillBaseComponent = entity.components.skillBase -- new↓ 增加判断,如果从未使用过技能,则不退出Decision。(之前没用过技能,也会退出decision,比如处于虚弱状态时) local decisionRecordDic = entity.components.aiBase:GetDecisionRecordDic(decisionData) local skillData = decisionRecordDic.skillData if skillData ~= nil then if skillData.SwitchAllTime == FP.Const.Zero then --后摇时间为0的瞬时技能,退出逻辑需要特殊处理,以保证2点:1、不会卡住出不去; 2、尽可能是释放了之后才Exit if LogicUpdateManager._clientHandleFrame - decisionRecordDic.decisionInFrame >= INSTANT_SKILL_MAX_WAIT_FRAME then --瞬时技能的超时退出 logging.error(LTAG.ERROR, "HYW : 瞬时技能等待超时! skillData.ID : ", skillData.ID, " characterID : ", entity:GetCharacterID()) return true else local record = entity.components.record:GetLatestInstantSkillAttackEnterRecord(skillData.ID) if record and record.frameStamp >= decisionRecordDic.decisionInFrame then --如果有瞬时技能释放记录,且这个释放晚于当前decision的进入时间,则表示释放过了,可以return true return true end end else --查询技能是否使用过 if not decisionRecordDic.skillUsed then if skillBaseComponent.CurSkillData then decisionRecordDic.skillUsed = skillData.BelongTo == skillBaseComponent.CurSkillData.BelongTo end end --如果技能没有使用过 if not decisionRecordDic.skillUsed then if skillBaseComponent:IsSkillInCDByBelongTo(skillData.BelongTo) then --如果AI没有感知到技能使用过,且这个技能还进CD了,直接Exit (这个修改解决在极限情况下AI会卡在这个Decision一直持续一个技能CD的问题 。 细节:之前当EnterCD和SkillTimeSystem.OnInterruptSkill接连发生,且没有调用到Decision_UseSkill:CanExit,就会卡住) return true else --如果技能没有使用过,且技能不在CD中,则不能退出这个技能 return false end end end else logging.error(LTAG.ERROR, "HYW : skillData nil Error index/KeyPosition : ", decisionData.IntParam1) return true end -- new↑ --下面是在技能使用过之后的判断 if skillBaseComponent.CanSwitchAll then --如果使用过技能,且技能进入后摇 if skillBaseComponent.CurSkillData then if skillBaseComponent.CurSkillData.AutoNextID > 0 then --如果使用过技能,且技能进入后摇,但有自动后续技,则不能退出 return false else --如果使用过技能,且技能进入后摇,且没有自动后续技,只要进入后摇的这个技能的belongTo跟Decision释放的技能的belongTo一致就可以退出 return skillData.BelongTo == skillBaseComponent.CurSkillData.BelongTo end else --如果使用过技能,且进入后摇,并且当前技能为空,则直接退出 return true end end return false end --[[ 进入该Decision --]] function Decision_UseSkill:DecisionIn(entity, decisionData) local skillBaseComp = entity.components.skillBase local skillData = self:GetSkillDataByDecisionData(decisionData, skillBaseComp) --logging.error(LTAG.ERROR, "HYW : UseSkill : ", skillData.KeyPosition, " entityId : ", entity._logicInitData.EntityID) local decisionRecordDic = entity.components.aiBase:GetDecisionRecordDic(decisionData) decisionRecordDic.skillUsed = false decisionRecordDic.skillData = skillData if skillData then if decisionData.IntParam1 > 0 then decisionRecordDic.skillIndex = decisionData.IntParam1 else decisionRecordDic.skillIndex = skillBaseComp:GetSkillIndexByKeyPosition(skillData.KeyPosition) end local skillJoystickData = skillBaseComp:GetSkillJoystickData(skillData.ID) if skillJoystickData and SkillMissileSystem.GetJoystickCtrlMissileType(skillJoystickData) then decisionRecordDic.skillJoystickData = skillJoystickData end end Base.DecisionIn(self,entity, decisionData) --gws 由于canenter的时候没有判断是否后摇,导致技能可能放不出去;在上一个技能进入后摇时会清理pos,所以本AI的技能就没有了pos ;不能做成记录{SkillIndex = pos },因为其他AI还可能释放同样的技能但需无pos。 if decisionData.IntParam2 == 0 or decisionData.IntParam2 == 3 then return end --SystemManager.OnSetSkillAimPos(entity, pos) --SystemManager.SetSkillMissilePos(entity,pos) if decisionData.SkillSpecialPos == nil then decisionData.SkillSpecialPos = FP.newV2(decisionData.IntParam3 / 10,decisionData.IntParam4 / 10) end end --[[ 退出该Decision --]] function Decision_UseSkill:DecisionOut(entity, decisionData) Base.DecisionOut(self,entity, decisionData) if decisionData.IntParam2 == 0 then return end --gws 下面两个代码去掉,在技能完毕的时候或者技能中断都会设置这两个值为空 ;如果打开下面的代码,会导致技能没有释放完毕,这个值就清理掉子弹发射就没有目标位置了(抛物线会抛异常) --SystemManager.OnSetSkillAimPos(entity, nil) --SystemManager.SetSkillMissilePos(entity,nil) entity.components.aiBase:ClearDecisionRecord(decisionData) end --[[ 输出指令 --]] function Decision_UseSkill:SetCommand(entity, decisionData, command) if decisionData.StringParam1 == "true" then if self:ShouldBlockAI() then return command end end local decisionRecordDic = entity.components.aiBase:GetDecisionRecordDic(decisionData) command.SkillIndex = decisionRecordDic.skillIndex or -1 if decisionData.SkillSpecialPos then command.AISkillAimPos = decisionData.SkillSpecialPos if decisionData.IntParam2 == 2 then -- 子弹强制设置基准位置 entity.components.aiBase:SetDecisionRecord(decisionData, "forceMissilePos", true) end else if decisionRecordDic.skillJoystickData and entity.components.target.Target then -- 有子弹类型的双摇杆,则技能杆执行目标 entity.components.aiBase:SetAISkillJoystickPos(entity.components.target.Target:GetLogicPos()) end if decisionData.IntParam2 == 3 and decisionData.IntParam1 == -8 then -- 闪避摇杆随机朝向 local theta = LogicRandom.GetRangeValue(entity, FP.Const.Zero, FP.Const.PI2 ,20240520) --极坐标θ local sin = FP.Math.Sin(theta) local cos = FP.Math.Cos(theta) command.SkillJoystickPosX:Reset(sin) command.SkillJoystickPosZ:Reset(cos) -- 随机朝向释放 end end if decisionRecordDic.skillData.IsMoveByHandle then local targetEntity = entity.components.target.Target if targetEntity then local targetEntityPos = targetEntity:GetLogicPos() if IsDistanceSqrGreater(entity:GetLogicPos(), targetEntityPos, MoveByHandleDistanceSqr) then command.MoveDir:Reset(targetEntityPos):Sub(entity:GetLogicPos()):Normalize() command.RotateDir:Reset(command.MoveDir) end end end return command end return Decision_UseSkill