local FirstPersonEventDispatcher = require"Battle/Logic/Event/FirstPersonEventDispatcher" local BattleEffect = require"Battle/Logic/BattleEffect/BattleEffect" local BattlePropSystem = {} local private = {} local CONST_BTNINDEXMAX = 5 -- 按钮最大数量 local EnumOverlayType = LuaConst.EnumBattlePropOverlayType local tempFindList = {} local pairs = pairs -- 添加一个道具 function BattlePropSystem.AddBattleProp(entity, battlePropID, unlimitedUse) local battlePropData = DataSheetManager.BattlePropData:GetDataByID(battlePropID) if battlePropData == nil then logging.error(LTAG.ERROR, "[gws]: 添加道具时,无道具数据,battlePropID = ", battlePropID) return end local battlePropComp = entity.components.battleProp --1:寻找按钮位置 local btnIndex = private.FindNewPropBtnIndex(battlePropData, battlePropComp) --2:添加道具到按钮位置 if btnIndex == -1 then -- 最大数量了 FirstPersonEventDispatcher.OnBattlePropUpToMax(entity,btnIndex) else --3: 是否删除同位置的其他所有道具 local beforeCount = battlePropComp:GetBattlePropNum(battlePropID) if battlePropData.DeleteOthers then local curUsingPropInfo = battlePropComp.curUsingBattlePropDic[btnIndex] if curUsingPropInfo and curUsingPropInfo.battlePropID ~= battlePropID then -- 如果所在按钮位置正在使用道具,且道具的id与新道具ID不同则终止使用 battlePropComp:SetCurUsingBattleProp(btnIndex, nil) -- 终止使用 end battlePropComp:DeleteAllBattlePropsInBtnIndex(btnIndex, battlePropID, tempFindList) if #tempFindList > 0 then ExpSystemManager.HideBattlePropExistUI(entity, tempFindList) table.clear(tempFindList) end end battlePropComp:AddOneProp(btnIndex, battlePropData, unlimitedUse) LevelManager.CurLevelFunc:OnEntityAddProp(entity, btnIndex, battlePropData) if battlePropData.IsNewToUp or #battlePropComp.propDic[btnIndex] == 1 then FirstPersonEventDispatcher.OnAddNewBattleProp(entity,btnIndex) end if beforeCount == 0 then ExpSystemManager.OnBattlePropExistChange(entity.ExpEntity, battlePropData.ExtraUIType, true, battlePropData.InitCD) end end end function BattlePropSystem.TryStartUseBattleProp(entity, btnIndex) local canUse, cantTipsIDS, propInfo, battlePropData = BattlePropSystem.CanUseBattlePropBntIndex(entity, btnIndex) if canUse then if battlePropData.Duration > 0 then BattlePropSystem.StartUseBattleProp(entity, btnIndex, propInfo, battlePropData) else entity.components.battleProp:SetCurUsingBattleProp(btnIndex, propInfo, battlePropData) BattlePropSystem.FinishUseBattleProp(entity, btnIndex) end else if not string.isNilOrEmpty(cantTipsIDS) then ExpLevelManager.ShowTips(entity,cantTipsIDS) end end end -- 开始使用道具 function BattlePropSystem.StartUseBattleProp(entity, btnIndex, propInfo, battlePropData) entity.components.battleProp:SetCurUsingBattleProp(btnIndex, propInfo, battlePropData) FirstPersonEventDispatcher.OnStartUseBattleProp(entity, btnIndex, battlePropData) end function BattlePropSystem.OnLogicUpdate(entity) local battleProp_Comp = entity.components.battleProp if battleProp_Comp then local propDic = battleProp_Comp.propDic -- 更新正在使用道具 local usingTimerDic = battleProp_Comp.curUsingTimerDic for btnIndex = 1, CONST_BTNINDEXMAX do local curUsingTimer = usingTimerDic[btnIndex] if curUsingTimer and curUsingTimer > 0 then curUsingTimer = curUsingTimer - LuaConst.TimeSpanMs -- 暂时没支持时间缩放功能 battleProp_Comp:SetCurUsingTimer(btnIndex, curUsingTimer) if curUsingTimer <= 0 then curUsingTimer = 0 BattlePropSystem.FinishUseBattleProp(entity, btnIndex) battleProp_Comp:SetCurUsingBattleProp(btnIndex, nil) end end end -- 更新道具CD( 一定注意 :如果在退出CD时要执行逻辑代码,必须使用ipairs,且不能遍历propCDDic。 推荐:可以让逻辑代码Update时查询) local propCDDic = battleProp_Comp.propCDDic for propID, leftCD in pairs(propCDDic) do -- pairs safe if propCDDic[propID] > 0 then propCDDic[propID] = leftCD - LuaConst.TimeSpanMs if propCDDic[propID] <= 0 then battleProp_Comp:ExitCD(propID) FirstPersonEventDispatcher.OnRefreshBattleProp(entity) -- 退出CD时 ,5个按钮都刷新下(因为case:多个按钮同个道具) end end end --更新道具的存活时间 for btnIndex = 1, CONST_BTNINDEXMAX do local stackList = propDic[btnIndex] if stackList then local count = #stackList for i=count,1,-1 do local propInfo = stackList[i] if propInfo.lifeTime > 0 then propInfo.lifeTime = propInfo.lifeTime - LuaConst.TimeSpanMs if propInfo.lifeTime <= 0 then BattlePropSystem.DeleteBattleProp(entity, btnIndex, i) -- 时间到删除道具 BattleEventSystem.OnBattlePropTimeUp(entity, propInfo.battlePropID) end end end end end end end -- 受击中断 function BattlePropSystem.OnHpChange(entity, realDelta, hpChangeType) if entity:IsHero() then if realDelta < FP.Const.Zero then if hpChangeType ~= LuaConst.EHpChangeType.DOT then --策划要求Dot不打断交互 BattlePropSystem.AbortUseBattleProp(entity) end end end end -- 释放技能中断(以后如果策划需求有些道具不用被打断,可以在道具表加上字段控制) function BattlePropSystem.OnAttackEnter(entity) BattlePropSystem.AbortUseBattleProp(entity) end function BattlePropSystem.OnSwitchHero(fromEntity, toEntity) BattlePropSystem.AbortUseBattleProp(fromEntity) toEntity.components.battleProp:Copy(fromEntity.components.battleProp) end function BattlePropSystem.OnDead(entity) BattlePropSystem.AbortUseBattleProp(entity) end -- 中断使用 。btnIndex:nil全部中断 ,有值则中断具体某个按钮 function BattlePropSystem.AbortUseBattleProp(entity, onlyBtnIndex) local battlePropComp = entity.components.battleProp if battlePropComp then local curUsingBattlePropDic = battlePropComp.curUsingBattlePropDic for btnIndex,v in pairs(curUsingBattlePropDic) do -- pairs safe if onlyBtnIndex == nil or btnIndex == onlyBtnIndex then battlePropComp:SetCurUsingBattleProp(btnIndex, nil) -- 这设置数据,不做逻辑业务 FirstPersonEventDispatcher.OnAbortUseBattleProp(entity, btnIndex) end end end end -- 使用道具成功 function BattlePropSystem.FinishUseBattleProp(entity, btnIndex) local battlePropComp = entity.components.battleProp local propInfo = battlePropComp.curUsingBattlePropDic[btnIndex] -- CD拦截 if battlePropComp.propCDDic[propInfo.battlePropID] then logging.crit(LTAG.CRIT, "[gws]: 使用道具失败OnFinish ,原因:道具处于CD中了, battlePropID=", propInfo.battlePropID, "cd =", battlePropComp.propCDDic[propInfo.battlePropID]) return end local battlePropData = DataSheetManager.BattlePropData:GetDataByID(propInfo.battlePropID) --先执行battleEffect,再删除道具,最后进入CD if #battlePropData.BattleEffectIDs > 0 then for i,battleEffectID in ipairs(battlePropData.BattleEffectIDs) do BattleEffect.ExecuteByEffectId(battleEffectID, {battlePropData = battlePropData}, entity, entity) end end battlePropComp:UsePropInfoOver(btnIndex, propInfo, battlePropData) -- 删除道具 FirstPersonEventDispatcher.OnFinishUseBattleProp(entity, battlePropData.BtnIndex > 0 and btnIndex or nil) if battlePropComp:GetBattlePropNum(propInfo.battlePropID) == 0 then ExpSystemManager.OnBattlePropExistChange(entity.ExpEntity, battlePropData.ExtraUIType, false) end end --删除道具(将btnIndex的堆栈中的propInfo删除。如果count有值则只减少此propInfo的数量) --btnIndex:按钮位置 ;index:此按钮的堆栈list中的第几个 ;count :nil则删除此位置所有道具,如果大于0则是从本位置去掉几个 function BattlePropSystem.DeleteBattleProp(entity, btnIndex, index, count) local battlePropComp = entity.components.battleProp if battlePropComp then local stackList = battlePropComp.propDic[btnIndex] local propInfo = stackList and stackList[index] local needAbort = propInfo and battlePropComp.curUsingBattlePropDic[btnIndex] == propInfo -- 当前正在使用的道具就是这个这个,则中断 local isTop = #stackList == index battlePropComp:DeleteBattleProp(btnIndex, index, count) if needAbort then -- 如果是正在使用的道具,则中断使用 BattlePropSystem.AbortUseBattleProp(entity, btnIndex) else if isTop then FirstPersonEventDispatcher.OnRefreshBattleProp(entity, btnIndex) end end if battlePropComp:GetBattlePropNum(propInfo.battlePropID) == 0 then local battlePropData = DataSheetManager.BattlePropData:GetDataByID(propInfo.battlePropID) ExpSystemManager.OnBattlePropExistChange(entity.ExpEntity, battlePropData.ExtraUIType, false) end end end --region 一些不在生命周期的代码(被外界特除调用的) --删除entity身上的某道具。battlePropID:要删除的道具ID, count:0 删除全部。 function BattlePropSystem.DeleteBattlePropsByID(entity, battlePropID, count) local battlePropComp = entity.components.battleProp if battlePropComp then local deleteNum = 0 for btnIndex = 1, CONST_BTNINDEXMAX do local stackList = battlePropComp.propDic[btnIndex] if stackList then for index=#stackList, 1 ,-1 do -- 从堆栈中移除count个 local propInfo = stackList[index] if propInfo.battlePropID == battlePropID then if count>0 then if deleteNum + propInfo.count <= count then BattlePropSystem.DeleteBattleProp(entity, btnIndex, index) deleteNum = deleteNum + propInfo.count else local delta = count - deleteNum if delta > 0 then BattlePropSystem.DeleteBattleProp(entity, btnIndex, index, delta) deleteNum = count end return -- 已删除够数了 end else BattlePropSystem.DeleteBattleProp(entity, btnIndex, index) end end end end end end end -- 获取某个位置按钮是否可用 function BattlePropSystem.CanUseBattlePropBntIndex(entity, btnIndex) if not entity:IsValid() then -- 玩家能使用道具需是active 且 alive 时 ,如有特殊,请在excel加字段控制 return false end if btnIndex < 1 or btnIndex > CONST_BTNINDEXMAX then logging.error(LTAG.ERROR, "[gws]: 判断使用道具异常 ,原因btnIndex是个非法值,btnIndex=", btnIndex , " 。本报错不会影响游戏运行") return false end local battlePropComp = entity.components.battleProp local propInfo = battlePropComp:GetTopBattlePropInfo(btnIndex) -- 堆栈中的信息table if propInfo == nil then -- 按钮上没有道具 return false end -- 当前按钮正在使用中,不能再点击使用 if battlePropComp.curUsingBattlePropDic[btnIndex] then return false end local battlePropID = propInfo.battlePropID local canUse, cantUseTips = BattlePropSystem.CanUseBattlePropID(entity, battlePropID) local battlePropData = DataSheetManager.BattlePropData:GetDataByID(battlePropID) return canUse, cantUseTips, propInfo, battlePropData end -- 获取某个战斗道具是否可用【限制条件:CD、data配置条件 】 function BattlePropSystem.CanUseBattlePropID(entity, battlePropID) local cantUseTips -- 只有被战斗道具表配置的阻挡条件拦截时才有值 local battlePropComp = entity.components.battleProp -- CD拦截 (当前道具正在CD中) if battlePropComp.propCDDic[battlePropID] then return false end -- 当前道具配置的其他禁用条件 local battlePropData = DataSheetManager.BattlePropData:GetDataByID(battlePropID) local conditionTree = nil for i,conditionData in ipairs(battlePropData.CanNotUseCondition_Table) do conditionTree = ConditionManager.GetConditionDataTree(conditionData.ConditionStr) if ConditionManager.MeetCondition(entity, conditionTree) then cantUseTips = conditionData.ReasonTipIDS return false, cantUseTips end end return true, nil end --endregion 一些不在生命周期的代码(被外界特除调用的) --region 很细节的逻辑 ,不要被外部使用 --寻找新增道具的按钮位置,如果已到最大数量限制(覆盖类型为刷新除外)返回-1 function private.FindNewPropBtnIndex(battlePropData, battlePropComp) local btnIndex = nil -- -1 表示超过最大数量 local curCount = battlePropComp:GetBattlePropNum(battlePropData.ID) local maxNum = battlePropData.MaxNum if battlePropData.BtnIndex > CONST_BTNINDEXMAX then logging.error(LTAG.ERROR, "[策划注意]: 添加道具按钮配置错误:BtnIndex 超过了5 ,代码默认改为1。 道具ID:", battlePropData.ID) btnIndex = curCount >= maxNum and -1 or 1 elseif battlePropData.BtnIndex > 0 then -- 在 0-5 返回正常值 btnIndex = curCount >= maxNum and -1 or battlePropData.BtnIndex else -- 小于等于0,则通过MaxNum 和 OverlayType 来找按钮 local overlayType = battlePropData.OverlayType if overlayType == EnumOverlayType.Refresh then -- 若为替换掉同位置道具 if curCount < maxNum then -- 没到最大数时,查找最少数量的按钮位置 local minCount = 999 for btn =1,CONST_BTNINDEXMAX do local count = battlePropComp.propDic[btn] and #battlePropComp.propDic[btn] or 0 if count < minCount then minCount = count btnIndex = btn end end else -- 当达到最大数时,查找最短存活时长的道具所在按钮位置 local minLifeTime = 9999999 for btn =1,CONST_BTNINDEXMAX do local stackList = battlePropComp.propDic[btn] if stackList then for j=1,#stackList do if stackList[j].battlePropID == battlePropData.ID and stackList[j].lifeTime < minLifeTime then minLifeTime = stackList[j].lifeTime btnIndex = btn end end end end end else -- 若为堆叠、增加数量 if curCount >= maxNum then btnIndex = -1 -- 已是最大数量 else local minCount = 999 for btn =1,CONST_BTNINDEXMAX do local count = battlePropComp.propDic[btn] and #battlePropComp.propDic[btn] or 0 if count < minCount then minCount = count btnIndex = btn end end end end end return btnIndex -- -1 表示超过最大数量 end --endregion return BattlePropSystem