9.4 KiB
ADDED Requirements
Requirement: 通用 Item 数据模型(全游戏统一)
系统 MUST 用统一的 ItemConfig 表示所有游戏内可获得的资源——包括但不限于金币、钻石、英雄碎片、装备、装备材料、装备图纸、经验书、升星石、觉醒符文、礼包、邮件附件、活动奖励。ItemConfig MUST 至少包含字段:id(itemId)、name、icon、itemType(BagItemType 大类)、subType(细分类,如 EquipItemType 或货币子类)、maxStack(堆叠上限,0 表示不限)、useType(使用行为,如 HeroExp/HeroStarExp/Gift/None)、quality(品质 1-5)、desc、sortIndex。
Scenario: 金币也是 Item
- WHEN 玩家获得 100 金币
- THEN 服务端调用
BagComponentSystem.AddItem(player, itemId=1 /* 金币的固定 itemId */, 100) - AND 前端
BagComponent中存在 itemId=1 的 ItemInfo - AND UI 通过
BagItemUICommon.Show(itemId=1, count=100)渲染图标和数量
Scenario: 钻石使用同一套接口
- WHEN 玩家抽卡消耗 10 钻石
- THEN 服务端调用
BagComponentSystem.RemoveItem(player, itemId=2 /* 钻石的固定 itemId */, 10) - AND 不需要任何"特殊货币处理逻辑"
Scenario: 不允许新增非 Item 资源
- WHEN 业务需要引入新资源类型(如新货币)
- THEN MUST 通过新增 ItemConfig 条目实现,不允许在 Player Entity 上添加新字段
Requirement: 通用奖励/消耗结构
系统 MUST 提供统一的奖励/消耗描述结构 RewardEntry { itemId, count } 和 CostEntry { itemId, count }(结构相同,命名上分语义)。所有"列表型奖励/消耗"业务 MUST 使用 repeated RewardEntry 或 repeated CostEntry,包括但不限于:抽卡奖励列表、升级消耗、升星消耗、技能升级消耗、礼包道具列表、邮件附件、关卡奖励、商店售卖、活动签到奖励。
Scenario: 升级消耗用 CostEntry
- WHEN
HeroSkillLevelConfig配置技能升级消耗 - THEN 字段类型 MUST 是
repeated CostEntry(例:[{ itemId: 2001, count: 5 }, { itemId: 1, count: 100 }]) - AND 服务端通过
BagComponentSystem.RemoveItems(player, costs)批量扣除 - AND 任一项不足则全部失败,不扣任何道具
Scenario: 抽卡奖励用 RewardEntry
- WHEN 抽卡响应包含奖励列表
- THEN 字段类型 MUST 是
repeated RewardEntry - AND 服务端通过
BagComponentSystem.AddItems(player, rewards)批量发放
Requirement: 通用道具 UI 组件
客户端 MUST 提供 BagItemUICommon YIUI UICommon,输入 (itemId, count, optional: showName, showCount, sizePreset),自动渲染图标、品质边框、数量、新道具标记。该组件 MUST 被所有道具相关 UI 复用:背包列表、抽卡结果、奖励飘字、邮件附件、商店货品、关卡结算、签到奖励。
Scenario: 抽卡结果显示
- WHEN GachaResultPanel 渲染 10 个奖励
- THEN 为每个奖励实例化一个 BagItemUICommon
- AND 调用
Show(reward.itemId, reward.count)
Scenario: 邮件附件显示
- WHEN 邮件面板渲染附件列表
- THEN 复用相同的 BagItemUICommon
- AND 不需要重写道具图标渲染逻辑
Requirement: Bag 数据模型
系统 MUST 为每个玩家提供一个 BagComponent 挂在玩家根 Entity 下。BagComponent MUST 维护一个 ItemInfo 列表,每个 ItemInfo 持有:itemIndex(全局唯一索引)、itemId(对应 ItemConfig.id)、count(数量/堆叠)、itemType(大类)、heroId(仅装备类有效,归属英雄)。
Scenario: 注入背包数据
- WHEN 玩家登录后服务端推送
Item_ItemInfoList { items } - THEN 客户端
BagComponent清空旧 items 列表 - AND 为每个 ItemInfo 创建客户端缓存
- AND 按
ItemType分类放入对应子缓存(resourceItems/speedupItems/...)
Scenario: 增量更新单个道具
- WHEN 服务端推送
Item_ItemInfo { item } - THEN 客户端按 itemIndex 找到对应缓存
- AND 若 count > 0 更新;若 count == 0 移除
- AND 触发
OnItemChanged(itemId)事件
Requirement: 道具大类与堆叠规则
系统 MUST 提供 6 大类道具类型(BagItemType):Resource(1) / Speedup(2) / Boost(3) / Equipment(4) / Other(5) / Icon(6)。
- 非 Equipment 类道具 MUST 按 itemId 堆叠(同 id 累加 count)
- Equipment 类道具 MUST 每个独立 itemIndex(不堆叠),即使同 itemId
Scenario: Resource 类堆叠
- WHEN 玩家已有 itemId=1001 数量 50
- AND 服务端发放 100 个 itemId=1001
- THEN 服务端 MUST 更新该道具 count 为 150(不创建新条目)
Scenario: Equipment 类不堆叠
- WHEN 玩家拥有 1 件 itemId=8001 装备
- AND 服务端发放另 1 件相同 itemId 装备
- THEN 服务端 MUST 创建新 itemIndex 条目,count=1
- AND 两个装备实例独立存在
Requirement: 道具数量查询
客户端 MUST 提供以下查询接口:
BagComponentExtensions.GetItemNum(itemId)返回某 itemId 的总数(非装备类)BagComponentExtensions.GetItemIndex(itemId)返回单条目的 itemIndex(非装备类)BagComponentExtensions.GetItemInfoListByType(BagItemType)返回某类下所有 ItemInfoBagComponentExtensions.HasItem(itemId, requiredCount)判断是否足够
Scenario: 查询道具数量
- WHEN 玩家拥有 itemId=2001 数量 30
- AND 调用
GetItemNum(2001) - THEN 返回 30
Scenario: 查询不存在的道具
- WHEN 玩家未拥有 itemId=9999
- AND 调用
GetItemNum(9999) - THEN 返回 0(不抛异常)
Requirement: 道具使用
玩家 MUST 能消耗指定数量道具触发其效果。可使用道具由 ItemConfig.useType 标识。服务端 MUST 是真权威,校验所有使用操作。
Scenario: 使用经验书
- WHEN 玩家发送
C2G_UseItem { itemId, count } - AND
ItemConfig.useType == HeroExp - AND 玩家拥有 >= count
- THEN 服务端转发到对应处理器(如 HeroAddExp)
Scenario: 使用礼包道具
- WHEN 玩家使用 useType=Gift 类道具
- THEN 服务端按
ItemConfig.giftRewards发放奖励 - AND 扣除礼包道具
- AND 推送多个
Item_ItemInfo同步所有变化
Scenario: 使用数量不足
- WHEN 玩家请求使用 5 个道具,但只有 3 个
- THEN 服务端 MUST 拒绝并返回
ItemNotEnough
Requirement: 服务端道具增减接口
服务端 MUST 提供以下基础接口给业务调用(如 Hero/Equipment 模块),所有变更 MUST 推送 Item_ItemInfo:
BagComponentSystem.AddItem(itemId, count)增加道具BagComponentSystem.RemoveItem(itemId, count)减少道具,返回是否成功BagComponentSystem.RemoveItemByIndex(itemIndex)按索引删除(用于唯一装备)BagComponentSystem.SetHeroIdOnEquip(itemIndex, heroId)装备绑定/解绑英雄
Scenario: 增加道具触发同步
- WHEN 服务端业务调用
AddItem(itemId=1001, 50) - THEN 修改 BagComponent
- AND 标记脏数据待落库
- AND 推送
Item_ItemInfo { itemId=1001, count=新count }
Scenario: 扣除道具失败
- WHEN 业务调用
RemoveItem(itemId, count)但数量不足 - THEN 返回 false
- AND 不修改任何数据
- AND 不推送消息
Requirement: 新道具标记
客户端 MUST 维护"新到道具"红点标记,用 ISave 本地存储(避免与服务端通信开销)。当 BagComponent 接收到新 itemId 或 count 增加时,MUST 自动标记 isNew。当玩家在 UI 中浏览过该道具,MUST 标记为已读。
Scenario: 获得新道具标记
- WHEN 服务端推送
Item_ItemInfo增加 itemId=5001(之前为 0) - THEN 客户端 MUST 在本地 ISave 中标记
isNew[5001] = true
Scenario: 浏览后清除标记
- WHEN 玩家在 BagPanel 中点击查看 itemId=5001
- THEN 客户端 MUST 调用
SetLocalItemToOld(5001) - AND 红点消失
Requirement: 背包红点
系统 MUST 提供按类型聚合的红点接口:
GetBagReddotNumByType(BagItemType)返回某类新道具数量GetBagReddotTotal()返回所有类型新道具总数ClearAllReddotRecord()清空所有红点
Scenario: 分类红点
- WHEN 玩家有 3 个新 Resource 道具 + 2 个新 Equipment 道具
- AND 客户端调用
GetBagReddotNumByType(BagItemType.Resource) - THEN 返回 3
Requirement: UI 列表分页与循环加载
背包 UI MUST 使用 cn.etetet.yiuiloopscrollrectasync 实现循环列表,支持万级道具不卡顿。MUST 按类型分页(Resource/Speedup/Boost/Equipment/Other 各为一页),每页内按 ItemConfig.sortIndex 排序。
Scenario: 切换到装备页
- WHEN 玩家点击 BagPanel 上"装备"分页按钮
- THEN UI 加载
BagComponent.equipItems数据源 - AND 通过
LoopScrollRect异步刷新可见格子
Requirement: 道具详情 Tooltip
点击道具 MUST 弹出 BagItemTooltip 浮层,显示:图标、名称、品质边框、描述、堆叠数量、使用按钮(若可用)、来源跳转入口。
Scenario: 显示 Tooltip
- WHEN 玩家点击背包格子
- THEN 在格子位置/右侧弹出 BagItemTooltip
- AND 显示对应 ItemConfig 的全部信息
Scenario: 装备 Tooltip 显示属性
- WHEN 点击的是 Equipment 类道具
- THEN Tooltip 显示装备属性条目(攻击/防御/血量等)
- AND 显示穿戴/卸下按钮