云计算百科
云计算领域专业知识百科平台

ItemMgr 追加 UseItem 道具使用接口 + 道具效果分发

设计约定:

  • ItemMgr 只做【消耗校验、扣数量、分发事件】,不写具体业务效果(效果交给订阅事件的各个Mgr解耦实现)
  • 支持一次性消耗、批量使用、使用CD、物品使用前置校验、绑定规则、唯一道具限制
  • 和已有 ItemMgr 无缝合并,配合 EventMgr 解耦;消耗成功抛出 ITEM_USE 事件,奖励/BUFF/传送/礼包由外部模块监听处理
  • 区分:消耗品、礼包、卷轴、回城石这类可使用道具;装备不走UseItem(装备由BagMgr穿戴接口处理)
  • 追加错误码

    ITEM_CANT_USE = -106, — 该物品不可使用
    ITEM_USE_CD = -107, — 使用冷却中
    ITEM_USE_COND_FAIL = -108, — 使用条件不满足(等级、行会、副本等)

    追加到 item_mgr.lua 代码

    — 使用物品统一入口
    — @param player 玩家对象
    — @param item 物品实例
    — @param use_cnt 本次使用数量
    — @param extra_param 扩展参数 {target_uid, select_idx…礼包多选之类}
    — @return ret, msg
    function ItemMgr:UseItem(player, item, use_cnt, extra_param)
    local role_id = player:GetRoleId()
    use_cnt = math.floor(use_cnt or 1)
    local cfg = ConfigMgr:GetItemCfg(item.item_id)
    — 基础校验
    if not cfg or not cfg.can_use then
    return ERR.ITEM_CANT_USE, "该物品不能使用"
    end
    if use_cnt <= 0 or use_cnt > item.count then
    return ERR.ITEM_COUNT_INVALID, "使用数量非法"
    end
    — 唯一物品只能单次使用1个
    if cfg.is_unique and use_cnt > 1 then
    return ERR.ITEM_UNIQUE_EXIST, "唯一道具只能单次使用1个"
    end

    — CD校验,CD数据存在player临时data里
    if cfg.use_cd > 0 then
    local last_use = player:GetTempData("item_cd_"..item.item_id) or 0
    local now = os.time()
    if now – last_use < cfg.use_cd then
    return ERR.ITEM_USE_CD, string.format("冷却中,剩余%d秒", cfg.use_cd – (now – last_use))
    end
    end

    — 策划配置的前置条件(等级、转生、是否在副本内等)
    if cfg.use_cond then
    local ok, tip = self:CheckUseCond(player, cfg.use_cond)
    if not ok then
    return ERR.ITEM_USE_COND_FAIL, tip
    end
    end

    — 消耗数量
    local remain = item.count – use_cnt
    local consume_item = {}
    — 复制一份被消耗的物品用于日志
    for k, v in pairs(item) do
    consume_item[k] = v
    end
    consume_item.count = use_cnt

    — 扣数量逻辑
    if remain <= 0 then
    — 全部用完,销毁物品
    ItemMgr:DestroyItem(item, "item_use")
    else
    item.count = remain
    end

    — 更新CD时间
    if cfg.use_cd and cfg.use_cd > 0 then
    player:SetTempData("item_cd_"..item.item_id, os.time())
    end

    — 写消耗日志
    LogMgr:WriteItemLog(role_id, "item_use", {consume_item})

    — 抛出事件,业务逻辑订阅这个事件实现,解耦!
    EventMgr:Fire("ITEM_USE", player, consume_item, extra_param or {})

    return ERR.SUCCESS, "使用成功"
    end

    — 使用条件校验函数(配置驱动)
    — cond 配置示例:{level=50, no_dungeon=true, no_battle=false}
    function ItemMgr:CheckUseCond(player, cond)
    local role_attr = AttrMgr:GetPlayerAttr(player)
    if cond.level and role_attr.level < cond.level then
    return false, string.format("需要等级%d", cond.level)
    end
    if cond.rein and role_attr.rein < cond.rein then
    return false, string.format("需要转生%d重", cond.rein)
    end
    if cond.no_dungeon and player:IsInInstance() then
    return false, "副本内无法使用"
    end
    if cond.no_battle and player:IsInBattle() then
    return false, "战斗状态无法使用"
    end
    return true, "ok"
    end

    配套 客户端消息协议 proto

    // 请求使用物品
    message C2SUseItem {
    uint64 item_uid = 1;
    uint32 use_cnt = 2;
    map<string, string> extra_param = 3;
    }
    message S2CUseItemRsp {
    int32 ret = 1;
    string msg = 2;
    }

    BagMgr 消息处理器(玩家背包点击使用,转发调用ItemMgr)

    背包负责:根据uid找到格子物品、锁定保护校验,再调用ItemMgr.UseItem

    — bag_mgr.lua 追加
    function BagMgr:OnC2SUseItem(player, req)
    local uid = req.item_uid
    local use_cnt = req.use_cnt
    local extra = req.extra_param
    local bag_data = player:GetBagData()
    local find_pos = nil
    local target_item = nil
    for pos, item in pairs(bag_data.items) do
    if item and item.uid == uid then
    find_pos = pos
    target_item = item
    break
    end
    end
    if not target_item then
    player:SendMsg("S2CUseItemRsp", {ret=-1, msg="物品不存在"})
    return
    end
    — 锁定物品禁止使用
    if target_item.is_lock then
    player:SendMsg("S2CUseItemRsp", {ret=-1, msg="物品已锁定"})
    return
    end
    local ret, msg = ItemMgr:UseItem(player, target_item, use_cnt, extra)
    player:SendMsg("S2CUseItemRsp", {ret=ret, msg=msg})
    — 使用成功同步背包
    if ret == 0 then
    self:SyncBagAll(player)
    end
    end

    事件订阅示例(各个业务模块监听 ITEM_USE,示例)

    — 礼包奖励 RewardMgr 订阅示例
    EventMgr:Subscribe("ITEM_USE", function(player, consume_item, extra)
    local cfg = ConfigMgr:GetItemCfg(consume_item.item_id)
    if cfg.item_type ~= 10 then return end — 10=礼包类型
    — 发放礼包奖励
    RewardMgr:GiveReward(player, cfg.reward_list)
    end)

    — 回城卷轴 传送逻辑 MapMgr订阅
    EventMgr:Subscribe("ITEM_USE", function(player, consume_item, extra)
    local cfg = ConfigMgr:GetItemCfg(consume_item.item_id)
    if cfg.item_type ~= 11 then return end — 11=回城卷
    MapMgr:PlayerTeleport(player, cfg.tele_map_id, cfg.tele_x, cfg.tele_y)
    end)

    — 药品Buff BuffMgr订阅
    EventMgr:Subscribe("ITEM_USE", function(player, consume_item, extra)
    local cfg = ConfigMgr:GetItemCfg(consume_item.item_id)
    if cfg.item_type ~= 12 then return end — 12=药品
    BuffMgr:AddBuff(player, cfg.buff_id, cfg.buff_time)
    end)

    使用流程时序

    客户端点击物品 → C2SUseItem
    → BagMgr 根据uid查找物品、锁定校验
    → ItemMgr:UseItem 做通用校验(CD、等级、数量)
    → 扣减/销毁物品,写日志
    → Fire ITEM_USE事件
    → RewardMgr / BuffMgr / MapMgr 各自监听处理对应效果
    → 返回S2CUseItemRsp + 同步背包

    重点坑点说明

  • ❌ 不要在ItemMgr里写礼包、回城、加血逻辑,全部走事件订阅,方便策划配表新增道具类型,不用改ItemMgr代码
  • CD放在玩家临时内存数据,不落地数据库;重启服务CD清空(如果需要持久CD,改成存db)
  • 消耗的时候,先复制一份consume_item用于日志,避免物品被销毁后日志拿不到数据
  • 仓库内道具一般不支持UseItem;如果要支持仓库使用,新增StoreMgr的UseItem入口,同样调用ItemMgr接口
  • 下一个可选方向

  • give_item GM刷物品指令(对接ItemMgr.CreateItem)
  • 物品丢弃、交易过滤校验接口
  • 物品序列化/反序列化(DB存库)
  • 赞(0)
    未经允许不得转载:网硕互联帮助中心 » ItemMgr 追加 UseItem 道具使用接口 + 道具效果分发
    分享到: 更多 (0)

    评论 抢沙发

    评论前必须登录!