## ADDED Requirements ### Requirement: 装备子类型与部位 系统 MUST 提供 4 种装备 EquipItemType:Material(1) / Equip(2) / Drawing(3) / DrawingMaterial(4)。完整成品装备(Equip)MUST 占用 8 个部位中的一个,由 `EquipSubType` 枚举:Head(1) / BreastPlate(2) / Weapon(3) / Gloves(4) / Pants(5) / Accessories1(6) / Accessories2(7) / Shoes(8)。 #### Scenario: 装备数据结构 - **WHEN** `ItemConfig.itemType == Equipment` - **THEN** 必须有关联的 `EquipConfig.subType` 字段(8 个部位之一) - **AND** 必须有 `EquipConfig.equipItemType`(Material/Equip/Drawing/DrawingMaterial) ### Requirement: 英雄穿戴装备 玩家 MUST 能为指定英雄穿戴成品装备(EquipItemType == Equip)。同部位已穿装备 MUST 自动卸下并回到背包未绑定状态。 #### Scenario: 穿戴空槽位装备 - **WHEN** 玩家发送 `C2G_HeroWearEquip { heroId, equipItemIndex }` - **AND** 该装备类型为 Equip - **AND** 英雄对应 SubType 槽位为空 - **THEN** 服务端将该装备 itemIndex 写入 `Hero.equipSlots[SubType]` - **AND** 设置该装备 `ItemInfo.heroId = heroId` - **AND** 推送 `Hero_HeroInfo` + `Item_ItemInfo` #### Scenario: 替换已穿装备 - **WHEN** 玩家穿戴新装备,对应槽位已有装备 - **THEN** 服务端 MUST 先卸下旧装备(旧 ItemInfo.heroId = 0) - **AND** 穿戴新装备 - **AND** 推送两条 `Item_ItemInfo`(旧/新装备)+ 一条 `Hero_HeroInfo` #### Scenario: 穿戴非装备道具 - **WHEN** 玩家发送 wear 请求但 itemIndex 对应的不是 Equip - **THEN** 服务端 MUST 拒绝并返回 `NotEquipment` ### Requirement: 卸下装备 玩家 MUST 能从英雄身上卸下指定部位装备。卸下后装备 MUST 回到背包未绑定状态(heroId = 0)。 #### Scenario: 卸下装备 - **WHEN** 玩家发送 `C2G_TakeOffEquip { heroId, subType }` - **AND** 该英雄对应槽位有装备 - **THEN** 服务端清空 `Hero.equipSlots[SubType]` - **AND** 该装备 `ItemInfo.heroId = 0` - **AND** 推送 `Hero_HeroInfo` + `Item_ItemInfo` #### Scenario: 卸下空槽位 - **WHEN** 该英雄对应槽位为空 - **THEN** 服务端 MUST 拒绝并返回 `EquipSlotEmpty` ### Requirement: 装备属性 每件装备 MUST 提供属性加成,由 `EquipConfig.attrTypes[] + attrNumbers[]` 定义。英雄装备总属性 MUST 等于所有已穿装备的属性总和。 #### Scenario: 计算英雄装备属性 - **WHEN** 客户端调用 `HeroExtensions.GetEquipAttr(hero)` - **THEN** 返回字典 `{ AttrType -> 数值 }`,键值是 8 个部位装备属性累加 ### Requirement: 装备品质排序 客户端 MUST 提供装备列表排序: - HighQuality:按品质降序 - LowQuality:按品质升序 - Worn:先显示已穿戴,再未穿戴 - Exclusive:只显示英雄专属(`EquipConfig.exclusive` 字段) #### Scenario: 按品质排序 - **WHEN** 客户端调用 `BagComponentExtensions.GetEquipItemsBySortType(EquipSortType.HighQuality)` - **THEN** 返回所有 Equip 类道具 - **AND** 按 `EquipConfig.quality` 字段降序 ### Requirement: 装备锻造(制作成品) 玩家 MUST 能用图纸(Drawing) + 图纸材料(DrawingMaterial)合成成品装备。每个图纸 MUST 在 `EquipConfig.requireMaterials[]` 定义所需材料和数量。 #### Scenario: 锻造成功 - **WHEN** 玩家发送 `C2G_MakeEquipment { drawingItemId }` - **AND** 玩家持有该图纸 1 个 + 所有必需材料足够 - **THEN** 服务端扣除图纸 1 个 + 各材料数量 - **AND** 创建新 Equip 类 ItemInfo(heroId=0) - **AND** 推送所有相关 `Item_ItemInfo` 变更 #### Scenario: 材料不足 - **WHEN** 玩家发送锻造请求但材料不够 - **THEN** 服务端拒绝并返回 `MaterialNotEnough` - **AND** 不消耗任何道具 ### Requirement: 装备分解 玩家 MUST 能分解未穿戴的成品装备,按 `EquipConfig.resolveRewards[]` 返还材料/碎片。 #### Scenario: 分解成功 - **WHEN** 玩家发送 `C2G_ResolveEquipment { itemIndex }` - **AND** 该装备 `heroId == 0`(未穿戴) - **THEN** 服务端删除该 itemIndex - **AND** 按配置发放奖励道具 - **AND** 推送 `Item_ItemInfo` 多条 #### Scenario: 已穿戴装备不能分解 - **WHEN** 该装备 `heroId != 0` - **THEN** 拒绝并返回 `EquipmentInUse` ### Requirement: 材料合成 玩家 MUST 能用低级材料合成高级材料,由 `MaterialDefine.mixRules[]` 定义规则(如 5 个 1 级材料 → 1 个 2 级材料)。 #### Scenario: 合成材料 - **WHEN** 玩家发送 `C2G_MixMaterial { sourceItemId, count }` - **AND** 数量是合成倍数(如 5 / 10 / 15) - **AND** 玩家拥有足够数量 - **THEN** 服务端扣除源材料 - **AND** 增加目标材料 `count / mixRatio` 个 - **AND** 推送 `Item_ItemInfo` ### Requirement: 锻造红点 客户端 MUST 提供"有装备可锻造"红点: - `HasEquipCanBeForged()` 返回是否任一图纸的材料齐全 - 玩家 MUST 能"忽略某图纸的红点"(本地存储),`AddForgeEquipIgnoreRedDot(drawingItemId)` #### Scenario: 检测可锻造红点 - **WHEN** 玩家拥有图纸 A 和它的所有材料足够 - **AND** 调用 `HasEquipCanBeForged()` - **THEN** 返回 true #### Scenario: 忽略红点后不再提示 - **WHEN** 玩家忽略图纸 A 的红点 - **AND** 调用 `HasEquipCanBeForged()` 仅检测其他图纸 - **THEN** 即使图纸 A 满足条件也不计入