# GameItems模块 > 游戏物品系统 - 综合管理游戏内所有物品的生命周期 ## 目录 1. [模块概述](#1-模块概述) 2. [数据结构设计](#2-数据结构设计) 3. [核心功能](#3-核心功能) 4. [物品过期机制](#4-物品过期机制) 5. [宝箱系统](#5-宝箱系统) 6. [物品获取与堆叠](#6-物品获取与堆叠) 7. [物品产出限制](#7-物品产出限制) 8. [日志记录系统](#8-日志记录系统) 9. [物品合成系统](#9-物品合成系统) 10. [物品分解系统](#10-物品分解系统) 11. [物品绑定机制](#11-物品绑定机制) 12. [系统交互](#12-系统交互) 13. [最佳实践与注意事项](#13-最佳实践与注意事项) ## 1. 模块概述 ### 1.1 功能与目的 GameItems模块是游戏核心系统之一,负责管理游戏内所有物品的完整生命周期,包括创建、获取、使用、交易、过期和销毁。该模块为游戏内经济系统和玩家进度提供基础支持,是连接多个游戏子系统的核心组件。 ### 1.2 主要功能 - **物品基础属性管理**:名称、描述、图标、类型等 - **物品获取与消耗逻辑**:添加物品到用户背包、消耗物品 - **物品库存管理**:堆叠、数量限制、过期处理 - **物品交易系统**:玩家间交易、商店交易 - **特殊物品效果**:使用效果、装备效果 - **宝箱系统**:宝箱配置、开启机制、保底机制 - **物品过期管理**:全局过期和用户特定过期 - **物品合成系统**:配方、材料、成功率 - **物品分解系统**:分解规则、结果计算 - **物品绑定机制**:绑定类型、交易限制 ### 1.3 核心特点 1. **多物品宝箱系统**:支持一个宝箱同时掉落多个物品,可配置数量范围和概率 2. **灵活的过期机制**:支持全局过期时间和用户特定过期时间 3. **完善的属性系统**:通过JSON格式存储物品属性,支持复杂物品效果 4. **单独属性物品**:支持每个物品实例拥有独特属性(如装备) 5. **物品绑定机制**:限制物品流通,保持游戏经济平衡 6. **物品产出限制**:控制稀有物品的产出数量和频率 7. **全面的日志系统**:记录所有物品相关操作,支持数据分析和问题排查 ## 2. 数据结构设计 ### 2.1 设计原则 游戏物品模块采用关系型数据库设计,通过多个相互关联的数据表实现物品管理。数据结构设计遵循以下原则: 1. **模块化**:各个表结构清晰,职责单一 2. **扩展性**:支持通过属性表扩展物品特性,无需修改数据库结构 3. **性能优化**:合理的索引设计和关联关系 4. **数据完整性**:使用外键和事务确保数据一致性 ### 2.2 核心数据表 #### 2.2.1 物品基础表 | 表名 | 主要功能 | 关键字段 | |------|---------|---------| | item_categories | 物品分类管理 | id, name, code, parent_id | | item_items | 统一属性物品定义 | id, name, type, is_unique, attributes | | item_instances | 单独属性物品实例 | id, item_id, attributes, is_bound | | item_users | 用户物品关联 | user_id, item_id, instance_id, quantity | | item_groups | 物品组定义 | id, name, code | | item_group_items | 物品组内容 | group_id, item_id, weight | #### 2.2.2 宝箱系统表 | 表名 | 主要功能 | 关键字段 | |------|---------|---------| | item_chest_configs | 宝箱配置(新系统) | item_id, consume_group_id, reward_group_id, condition_group_id | | item_pity_times | 用户宝箱保底计数 | user_id, chest_id, chest_content_id, current_count | | item_chest_open_logs | 宝箱开启记录 | user_id, chest_id, result_items, pity_triggered | #### 2.2.3 物品限制与日志表 | 表名 | 主要功能 | 关键字段 | |------|---------|---------| | item_output_limits | 物品产出限制 | item_id, limit_type, max_quantity | | item_user_output_counters | 用户产出限制计数 | user_id, limit_id, current_count | | item_transaction_logs | 物品交易记录 | user_id, item_id, quantity, transaction_type | #### 2.2.4 物品合成与分解表 | 表名 | 主要功能 | 关键字段 | |------|---------|---------| | item_recipes | 合成配方定义 | id, result_item_id, success_rate | | item_recipe_materials | 配方材料需求 | recipe_id, item_id, quantity | | item_user_recipes | 用户配方解锁状态 | user_id, recipe_id, is_unlocked | | item_craft_logs | 物品合成记录 | user_id, recipe_id, is_success | | item_dismantle_rules | 物品分解规则 | item_id/category_id, priority | | item_dismantle_logs | 物品分解记录 | user_id, item_id, results | ### 2.3 详细表结构 #### 2.3.1 item_categories 表(物品分类) | 字段名 | 类型 | 说明 | | --- | --- | --- | | id | int | 分类ID,主键 | | name | varchar | 分类名称 | | code | varchar | 分类编码(唯一) | | icon | varchar | 分类图标 | | sort | int | 排序权重 | | parent_id | int | 父分类ID(可为空,用于实现分类层级) | | created_at | timestamp | 创建时间 | | updated_at | timestamp | 更新时间 | #### 2.3.2 item_items 表(统一属性物品) | 字段名 | 类型 | 说明 | | --- | --- | --- | | id | int | 物品ID,主键 | | name | varchar | 物品名称 | | description | text | 物品描述 | | category_id | int | 物品分类ID,外键关联item_categories表 | | type | tinyint | 物品类型(1:可使用, 2:可装备, 3:可合成, 4:可交任务, 5:可开启...) | | is_unique | tinyint | 是否是单独属性物品(0:否,默认, 1:是) | | icon | varchar | 物品图标路径 | | max_stack | int | 最大堆叠数量 | | sell_price | int | 出售价格 | | tradable | tinyint | 是否可交易(0:不可交易, 1:可交易,默认) | | dismantlable | tinyint | 是否可分解(0:不可分解, 1:可分解,默认) | | default_expire_seconds | int | 玩家获取物品后的默认有效秒数(0表示永久有效) | | display_attributes | json | 展示属性,以JSON格式存储键值对,用于界面展示和描述的属性 | | numeric_attributes | json | 数值属性,以JSON格式存储键值对,用于计算和游戏逻辑的属性 | | global_expire_at | timestamp | 物品全局过期时间(可为空) | | created_at | timestamp | 创建时间 | | updated_at | timestamp | 更新时间 | #### 2.3.3 item_instances 表(单独属性物品) | 字段名 | 类型 | 说明 | | --- | --- | --- | | id | int | 唯一物品ID,主键 | | item_id | int | 关联的基础物品ID,外键关联item_items表 | | name | varchar | 物品名称(可以与基础物品不同,如"锐利的钢刀") | | display_attributes | json | 展示属性,以JSON格式存储键值对,用于界面展示和描述的属性 | | numeric_attributes | json | 数值属性,以JSON格式存储键值对,用于计算和游戏逻辑的属性 | | tradable | tinyint | 是否可交易(0:不可交易, 1:可交易,默认) | | is_bound | tinyint | 是否已绑定(0:未绑定, 1:已绑定) | | bound_to | varchar | 绑定对象(账号ID或角色ID) | | bind_exp_time | timestamp | 绑定过期时间(为空表示永久绑定) | | expire_at | timestamp | 物品过期时间(可为空) | | created_at | timestamp | 创建时间 | | updated_at | timestamp | 更新时间 | #### 2.3.4 item_users 表(用户物品关联) | 字段名 | 类型 | 说明 | | --- | --- | --- | | id | int | 记录ID,主键 | | user_id | int | 用户ID,外键 | | item_id | int | 统一属性物品ID,外键关联item_items表(始终有值) | | instance_id | int | 单独属性物品ID,外键关联item_instances表(可为空) | | quantity | int | 数量(对于单独属性物品,该值始终为1) | | expire_at | timestamp | 用户物品过期时间(可为空) | | created_at | timestamp | 获取时间 | | updated_at | timestamp | 更新时间 | ### 2.4 数据库索引设计 为确保系统高性能运行,对关键字段进行了索引设计: #### 2.4.1 物品基础表索引 | 表名 | 索引字段 | 索引类型 | 说明 | | --- | --- | --- | --- | | item_items | id | 主键 | 物品ID主键索引 | | item_items | category_id | 普通索引 | 加速按物品分类查询 | | item_items | type | 普通索引 | 加速按物品类型查询 | | item_items | is_unique | 普通索引 | 加速查询单独属性物品 | | item_items | tradable | 普通索引 | 加速查询可交易/不可交易物品 | | item_items | dismantlable | 普通索引 | 加速查询可分解/不可分解物品 | | item_items | global_expire_at | 普通索引 | 加速过期物品查询 | | item_categories | id | 主键 | 分类ID主键索引 | | item_categories | parent_id | 普通索引 | 加速查询子分类 | | item_categories | code | 唯一索引 | 确保分类编码唯一性 | | item_instances | id | 主键 | 唯一物品ID主键索引 | | item_instances | item_id | 普通索引 | 加速根据基础物品查询唯一物品 | | item_instances | tradable | 普通索引 | 加速查询可交易/不可交易的单独属性物品 | | item_instances | is_bound | 普通索引 | 加速查询已绑定的单独属性物品 | | item_instances | bound_to | 普通索引 | 加速查询绑定到特定对象的单独属性物品 | | item_instances | bind_exp_time | 普通索引 | 加速查询需要解绑的单独属性物品 | | item_instances | expire_at | 普通索引 | 加速过期物品查询 | | item_users | user_id, item_id | 复合索引 | 加速用户统一属性物品查询 | | item_users | user_id, instance_id | 复合索引 | 加速用户单独属性物品查询 | | item_users | expire_at | 普通索引 | 加速过期物品查询 | #### 2.4.2 宝箱系统表索引 | 表名 | 索引字段 | 索引类型 | 说明 | | --- | --- | --- | --- | | item_chest_configs | item_id | 唯一索引 | 确保每个宝箱只有一个配置 | | item_chest_configs | consume_group_id | 普通索引 | 加速消耗组查询 | | item_chest_configs | reward_group_id | 普通索引 | 加速奖励组查询 | | item_chest_configs | condition_group_id | 普通索引 | 加速条件组查询 | | item_pity_times | user_id, chest_id, chest_content_id | 复合索引 | 加速查询用户对特定宝箱内容的保底计数 | | item_chest_open_logs | user_id, open_time | 复合索引 | 加速查询用户的宝箱开启历史 | | item_chest_open_logs | chest_id | 普通索引 | 加速查询特定宝箱的开启记录 | ### 2.5 数据模型关联关系 以下是主要数据模型之间的关联关系: 1. **用户与物品关系** - 用户可以拥有统一属性物品和单独属性物品,通过 item_users 表关联 - 对于统一属性物品,直接关联 item_items 表 - 对于单独属性物品,通过 item_instances 表间接关联 item_items 表 2. **物品分类层级** - item_categories 表通过 parent_id 字段实现自关联,形成分类层级结构 - item_items 表通过 category_id 字段关联到 item_categories 表 3. **物品组与物品** - item_groups 表定义物品组 - item_group_items 表建立物品组与物品的多对多关系 4. **宝箱与内容关系** - 宝箱是 item_items 表中 type=5 的特殊物品 - item_chest_contents 表定义宝箱可能掉落的内容 - 宝箱内容可以是具体物品(item_id)或物品组(group_id) 5. **物品合成关系** - item_recipes 表定义合成配方 - item_recipe_materials 表定义配方所需材料 - 用户通过 item_user_recipes 表记录已解锁的配方 6. **物品分解关系** - item_dismantle_rules 表定义分解规则 - 分解奖励通过奖励组系统(reward_group_id)配置 - 分解规则可以针对特定物品或整个分类 ### 2.6 单独属性物品实现 为支持"每个用户拥有的同类物品可以有不同属性"的需求(如不同的武器有不同的属性),系统采用了统一属性物品和单独属性物品两种设计: #### 2.6.1 实现方式 1. **物品类型区分** - item_items 表中的 is_unique 字段标记该物品是否为单独属性物品 - 单独属性物品(如装备)在 item_instances 表中创建独立记录 - 统一属性物品(如消耗品)直接使用 item_items 表的属性 2. **属性存储方式** - 基础属性存储在 item_items 表中 - 单独属性物品的特殊属性存储在 item_instances 表中 - 属性使用 JSON 格式存储,分为展示属性和数值属性 3. **用户物品关联** - item_users 表通过 item_id 和 instance_id 字段关联两种物品 - 对于统一属性物品,instance_id 为空,quantity 可以大于1 - 对于单独属性物品,instance_id 有值,quantity 始终为1 ## 3. 核心功能 ### 3.1 物品管理接口 系统提供以下核心接口用于物品管理: #### 3.1.1 获取用户物品 - **功能**:获取用户拥有的所有物品或特定类型的物品 - **参数**:用户ID,物品类型(可选),分页参数 - **返回**:物品列表,包含数量、属性等信息 - **处理逻辑**: 1. 查询用户在 item_users 表中的记录 2. 关联 item_items 表获取物品基础信息 3. 对于单独属性物品,关联 item_instances 表获取特殊属性 4. 过滤掉已过期的物品(检查全局过期和用户物品过期) 5. 按类型、分类等条件筛选 #### 3.1.2 添加物品到用户背包 - **功能**:向用户背包添加指定物品 - **参数**:用户ID、物品ID、数量、过期时间(可选)、属性参数(可选) - **返回**:添加结果、当前数量 - **处理逻辑**: 1. 检查物品是否存在 2. 检查物品是否有全局过期时间 3. 对于统一属性物品: - 检查用户是否已有该物品且过期时间相同 - 如果已有,增加数量(注意最大堆叠限制) - 如果没有,创建新记录 4. 对于单独属性物品: - 创建 item_instances 记录 - 关联到用户 5. 记录物品获取日志 #### 3.1.3 使用物品 - **功能**:消耗指定数量的物品并触发效果 - **参数**:用户ID、物品ID/实例ID、数量、使用参数 - **返回**:使用结果、剩余数量、触发效果 - **处理逻辑**: 1. 检查用户是否拥有足够数量的物品 2. 检查物品是否已过期 3. 根据物品类型执行不同的使用逻辑 4. 扣除物品数量 5. 记录物品消耗日志 6. 返回使用效果 #### 3.1.4 删除用户物品 - **功能**:从用户背包中删除指定物品 - **参数**:用户ID、物品ID/实例ID、数量、删除原因 - **返回**:删除结果、剩余数量 - **处理逻辑**: 1. 检查用户是否拥有足够数量的物品 2. 扣除物品数量 3. 如果数量为0,删除记录 4. 对于单独属性物品,可选择是否删除 item_instances 记录 5. 记录物品删除日志 ### 3.2 物品交易接口 系统支持玩家间物品交易和与NPC的交易: #### 3.2.1 玩家间交易 - **功能**:将物品从一个玩家转移到另一个玩家 - **参数**:发送方ID、接收方ID、物品ID/实例ID、数量 - **返回**:交易结果 - **处理逻辑**: 1. 检查物品是否可交易(tradable=1) 2. 检查物品是否已绑定(is_bound=0) 3. 检查发送方是否拥有足够数量的物品 4. 从发送方扣除物品 5. 向接收方添加物品 6. 记录交易日志 #### 3.2.2 商店购买 - **功能**:玩家使用游戏货币购买物品 - **参数**:用户ID、物品ID、数量 - **返回**:购买结果、剩余货币 - **处理逻辑**: 1. 检查物品价格和玩家货币是否足够 2. 扣除玩家货币 3. 添加物品到玩家背包 4. 记录购买日志 #### 3.2.3 商店出售 - **功能**:玩家将物品出售给商店获得货币 - **参数**:用户ID、物品ID/实例ID、数量 - **返回**:出售结果、获得货币 - **处理逻辑**: 1. 检查玩家是否拥有足够数量的物品 2. 计算出售价格(可能受物品品质、耐久度等影响) 3. 从玩家背包中扣除物品 4. 增加玩家货币 5. 记录出售日志 ### 3.3 单独属性物品接口 系统提供以下接口用于管理单独属性物品: #### 3.3.1 创建单独属性物品 - **功能**:为用户创建具有独特属性的物品 - **参数**:用户ID、基础物品ID、属性参数 - **返回**:创建的单独属性物品信息 - **处理逻辑**: 1. 检查基础物品是否存在且 is_unique=1 2. 生成物品属性(可能包含随机因素) 3. 创建 item_instances 记录 4. 关联到用户 5. 记录物品创建日志 #### 3.3.2 修改单独属性物品 - **功能**:修改单独属性物品的属性(如强化、附魔) - **参数**:用户ID、实例ID、属性变更 - **返回**:修改后的物品信息 - **处理逻辑**: 1. 检查用户是否拥有该物品 2. 验证属性变更的合法性 3. 更新 item_instances 记录 4. 记录物品修改日志 #### 3.3.3 获取单独属性物品详情 - **功能**:获取单独属性物品的详细信息 - **参数**:实例ID - **返回**:物品详细信息,包括基础属性和特殊属性 - **处理逻辑**: 1. 查询 item_instances 表获取物品实例 2. 关联 item_items 表获取基础信息 3. 合并基础属性和特殊属性 4. 返回完整的物品信息 ## 4. 物品过期机制 物品模块支持两种过期时间机制,用于实现物品的时效性管理。 ### 4.1 全局过期时间 全局过期时间定义在 `item_items` 表的 `global_expire_at` 字段中,表示该物品在所有用户中的绝对过期时间。当超过这个时间点后,所有用户的该物品均失效。 #### 4.1.1 应用场景 - **限时活动物品**:活动结束后自动失效 - **赠送的体验物品**:在特定日期后全部失效 - **版本更新物品**:新版本发布后旧版本物品自动失效 #### 4.1.2 实现方式 1. 在物品定义时设置 `global_expire_at` 字段 2. 在获取用户物品时,检查物品的全局过期时间 3. 定期运行任务,清理全局过期的物品 ### 4.2 用户物品过期时间 用户物品过期时间定义在 `item_users` 表的 `expire_at` 字段中,表示特定用户的特定物品的过期时间。这允许每个用户的同一物品有不同的过期时间。 #### 4.2.1 应用场景 - **限时使用的装备或道具**:从获取开始计时 - **会员特权物品**:与会员有效期绑定 - **租借物品**:租借期结束后自动失效 - **试用物品**:试用期结束后自动失效 #### 4.2.2 实现方式 1. 在添加物品到用户背包时,根据物品的 `default_expire_seconds` 计算过期时间 2. 如果 `default_expire_seconds` 为0,则物品永久有效 3. 在获取用户物品时,检查用户物品的过期时间 4. 定期运行任务,清理用户过期物品 ### 4.3 过期处理机制 系统定期运行任务检查并处理过期物品,确保游戏内物品的时效性。 #### 4.3.1 全局过期物品处理 1. **查询全局过期物品** ```sql SELECT id FROM item_items WHERE global_expire_at IS NOT NULL AND global_expire_at < NOW() ``` 2. **删除用户物品记录** ```sql DELETE FROM item_users WHERE item_id IN (已过期物品ID列表) ``` 3. **记录日志** - 在 item_transaction_logs 表中记录物品过期删除日志 - 交易类型设置为 "过期失效" 4. **可选操作** - 发送通知给用户 - 提供过期物品的替代品 #### 4.3.2 用户物品过期处理 1. **查询用户过期物品** ```sql SELECT id, user_id, item_id, instance_id FROM item_users WHERE expire_at IS NOT NULL AND expire_at < NOW() ``` 2. **删除用户物品记录** ```sql DELETE FROM item_users WHERE id IN (已过期记录ID列表) ``` 3. **处理单独属性物品** - 对于单独属性物品,可以选择删除或保留 item_instances 记录 - 如果删除,需要确保没有其他用户关联到该实例 4. **记录日志** - 在 item_transaction_logs 表中记录物品过期删除日志 - 记录用户ID、物品ID、过期时间等信息 ### 4.4 过期时间的优先级 当同时存在全局过期时间和用户物品过期时间时,系统采用以下优先级规则: 1. **最早到期优先**:系统使用两个过期时间中较早的一个作为实际过期时间 2. **全局过期强制执行**:即使用户物品过期时间晚于全局过期时间,全局过期时间到达后物品仍会失效 3. **用户物品过期独立执行**:用户物品过期不影响其他用户的同类物品 ## 5. 宝箱系统 宝箱系统是游戏物品系统的重要组成部分,允许玩家通过开启宝箱获得随机物品,增加游戏的随机性和乐趣。 ### 5.1 宝箱类型 系统支持多种类型的宝箱,可以根据游戏需求进行配置: #### 5.1.1 按稀有度分类 1. **普通宝箱**:包含基础消耗品和少量资源,开出稀有物品的概率较低 2. **稀有宝箱**:有较高机会获得稀有装备和较多资源 3. **史诗宝箱**:包含高价值物品,有较高概率获得史诗级物品 4. **传说宝箱**:最高级别宝箱,有机会获得传说级物品 #### 5.1.2 按获取方式分类 1. **任务宝箱**:完成任务获得的宝箱,内容通常与任务相关 2. **活动宝箱**:特定活动期间可获得的宝箱,包含活动限定物品 3. **商店宝箱**:通过游戏货币或真实货币购买的宝箱 4. **成就宝箱**:达成特定成就后获得的宝箱 5. **副本宝箱**:在游戏副本中获得的宝箱,内容通常与副本主题相关 ### 5.2 宝箱配置 宝箱的内容和概率通过 `item_chest_contents` 表进行配置: #### 5.2.1 基本配置 1. **宝箱定义**:宝箱本身是 `item_items` 表中 `type=5` 的特殊物品 2. **内容配置**:通过 `item_chest_contents` 表定义宝箱可能掉落的内容 3. **物品组支持**:宝箱内容可以是具体物品(item_id)或物品组(group_id) 4. **数量范围**:每个内容可以设置最小和最大掉落数量 5. **概率控制**:通过 weight 字段控制每个内容的掉落概率 #### 5.2.2 多物品掉落 宝箱系统支持一个宝箱同时掉落多个物品: 1. **掉落数量控制**: - 宝箱物品在 `numeric_attributes` 字段中定义 `min_drop_count` 和 `max_drop_count` 属性 - 系统在开启宝箱时随机决定实际掉落的物品数量 2. **重复控制**: - 通过 `allow_duplicate` 字段控制物品是否可以在同一宝箱中重复掉落 - 当设置为0时,同一内容在一次开启中最多出现一次 3. **物品组随机**: - 当内容配置为物品组时,系统会根据物品组中的权重随机选择一个物品 - 物品组可以包含多个物品,增加宝箱内容的多样性 ### 5.3 概率机制 宝箱内容的概率由 `item_chest_contents` 表中的 `weight` 字段决定: #### 5.3.1 基础概率计算 1. **权重定义**: - 每个宝箱内容的权重为三位小数 - 同一宝箱的所有内容权重总和为100 - 每个内容的概率直接等于其权重值(百分比) 2. **概率计算公式**: ``` 内容获取概率 = 内容权重 / 宝箱内容权重总和 * 100% ``` 3. **多物品抽取**: - 当宝箱配置为掉落多个物品时,系统会进行多次抽取 - 每次抽取都会根据权重进行随机选择 - 如果不允许重复,已选中的内容会从后续抽取中排除 ### 5.4 保底机制 为了确保玩家在多次开启宝箱后能获得稀有物品,系统实现了保底机制: #### 5.4.1 保底机制设计 保底机制直接集成在宝箱内容配置中,具有以下特点: 1. **内容级别保底**: - 每个宝箱内容可以设置自己的保底次数(`pity_count`字段) - 当玩家连续未获得该内容达到指定次数后,必定获得该内容 2. **灵活的概率调整**: - 通过 `pity_weight_factor` 字段控制递增概率的幅度 - 可以为不同内容设置不同的递增策略 3. **保底计数记录**: - 在 `item_pity_times` 表中记录用户对每个宝箱内容的尝试次数 - 当获得内容后,相应的计数会重置 #### 5.4.2 保底实现方式 系统支持两种保底实现方式: 1. **确定保底**: - 当玩家开启次数达到 `pity_count` 时,100%获得该内容 - 适用于重要的稀有物品 2. **递增概率**: - 每次未获得目标内容时,增加下次获得的概率 - 概率增加公式: ``` 调整后概率 = 基础概率 * (1 + (当前计数 / pity_count) * pity_weight_factor) ``` - 当计数达到 `pity_count` 时,概率变为100% #### 5.4.3 保底机制实现流程 1. **获取宝箱内容配置** - 从 `item_chest_contents` 表查询宝箱的所有内容配置 - 过滤出 `pity_count > 0` 的内容,这些是启用了保底的内容 2. **获取用户保底计数** - 从 `item_pity_times` 表查询用户对各宝箱内容的当前计数 - 如果不存在,创建新记录并初始化计数 3. **调整掉落概率** - 根据保底计数调整各内容的掉落概率 - 如果有内容的计数达到保底次数,直接选择该内容 4. **更新保底计数** - 对于未获得的内容,增加保底计数 - 对于已获得的内容,重置保底计数 ### 5.5 宝箱开启接口 系统提供以下接口用于宝箱开启: #### 5.5.1 开启宝箱 - **功能**:消耗宝箱并获取随机物品 - **参数**:用户ID、宝箱ID、数量 - **返回**:获得的物品列表、剩余宝箱数量 - **处理逻辑**: 1. 检查用户是否拥有足够数量的宝箱 2. 获取宝箱配置和用户保底计数 3. 计算实际掉落物品数量 4. 根据概率和保底机制选择掉落内容 5. 从用户背包中扣除宝箱 6. 向用户背包添加获得的物品 7. 更新保底计数 8. 记录宝箱开启日志 #### 5.5.2 获取宝箱内容预览 - **功能**:预览宝箱可能获得的物品及概率 - **参数**:宝箱ID - **返回**:可能获得的物品列表及概率 - **处理逻辑**: 1. 获取宝箱配置 2. 计算各内容的实际概率 3. 对于物品组,展开显示组内物品 4. 返回完整的可能获得物品列表及概率 ### 5.6 宝箱日志记录 每次宝箱开启都会记录详细日志,用于数据分析和问题排查: #### 5.6.1 日志内容 在 `item_chest_open_logs` 表中记录以下信息: 1. **用户信息**:开启宝箱的用户ID 2. **宝箱信息**:宝箱ID、开启数量 3. **开启时间**:宝箱开启的时间戳 4. **获得物品**:以JSON格式记录获得的所有物品ID和数量 5. **保底触发**:是否触发保底机制,触发的内容ID 6. **安全信息**:IP地址、设备信息等 #### 5.6.2 日志用途 1. **数据分析**:分析宝箱开启模式和物品掉落分布 2. **概率验证**:验证实际掉落概率是否符合配置 3. **用户支持**:解决用户关于宝箱开启的问题 4. **异常检测**:发现可能的作弊或异常行为 ## 6. 物品获取与堆叠 当玩家多次获得同一物品时,系统需要根据物品类型和过期时间进行不同的处理,以确保物品管理的合理性和高效性。 ### 6.1 统一属性物品的处理 对于统一属性物品(如消耗品、材料等),系统采用以下处理策略: #### 6.1.1 基本堆叠规则 1. **数量累加**: - 当玩家获得已拥有的统一属性物品时,系统会增加物品数量 - 在 `item_users` 表中更新 `quantity` 字段 2. **最大堆叠限制**: - 每种物品都有最大堆叠数量限制,定义在 `item_items` 表的 `max_stack` 字段 - 当数量超过最大堆叠限制时,系统会创建新的堆叠 3. **堆叠创建逻辑**: ```php // 伪代码示例 $currentQuantity = $userItem->quantity; $maxStack = $item->max_stack; $newQuantity = $currentQuantity + $addQuantity; if ($newQuantity <= $maxStack) { // 直接增加数量 $userItem->quantity = $newQuantity; $userItem->save(); } else { // 先填满当前堆叠 $userItem->quantity = $maxStack; $userItem->save(); // 创建新堆叠 $remainingQuantity = $newQuantity - $maxStack; $this->addNewStack($userId, $itemId, $remainingQuantity); } ``` #### 6.1.2 过期时间分组策略 对于有过期时间的统一属性物品,系统按照过期时间分组存储: 1. **过期时间匹配**: - 相同物品但过期时间不同,创建不同的记录 - 相同物品且过期时间相同,数量累加 2. **过期时间查找逻辑**: ```php // 伪代码示例 $userItem = UserItem::where('user_id', $userId) ->where('item_id', $itemId) ->where('expire_at', $expireAt) ->first(); if ($userItem) { // 找到相同过期时间的记录,增加数量 $userItem->quantity += $quantity; $userItem->save(); } else { // 创建新记录 UserItem::create([ 'user_id' => $userId, 'item_id' => $itemId, 'quantity' => $quantity, 'expire_at' => $expireAt ]); } ``` 3. **使用优先级**: - 在使用物品时,优先使用即将过期的物品 - 系统按过期时间升序排序,先使用最早过期的物品 ### 6.2 单独属性物品的处理 对于单独属性物品(如装备、宠物等),每次获取都创建新记录,即使是相同物品: #### 6.2.1 单独属性物品创建 1. **实例创建**: - 每个单独属性物品都在 `item_instances` 表中创建一条记录 - 生成独特的属性(可能包含随机因素) 2. **用户关联**: - 在 `item_users` 表中创建记录,关联用户和物品实例 - `quantity` 字段始终为1 3. **创建流程**: ```php // 伪代码示例 // 1. 创建物品实例 $instance = ItemInstance::create([ 'item_id' => $itemId, 'name' => $generatedName, 'display_attributes' => $displayAttributes, 'numeric_attributes' => $numericAttributes, 'expire_at' => $expireAt ]); // 2. 关联到用户 UserItem::create([ 'user_id' => $userId, 'item_id' => $itemId, 'instance_id' => $instance->id, 'quantity' => 1, 'expire_at' => $expireAt ]); ``` #### 6.2.2 属性差异化 单独属性物品可以通过多种方式实现属性差异化: 1. **随机属性生成**: - 在创建物品实例时生成随机属性 - 属性范围可以根据物品基础属性和品质定义 2. **命名差异化**: - 可以根据属性生成不同的物品名称 - 例如:"锐利的钢刀"、"坚固的钢刀"等 3. **属性继承与变异**: - 从基础物品继承部分属性 - 添加随机变异或特殊效果 ### 6.3 物品获取来源记录 系统记录物品的获取来源,便于数据分析和问题排查: #### 6.3.1 来源类型 在 `item_transaction_logs` 表中记录物品获取来源: 1. **任务奖励**:完成任务获得的物品 2. **商店购买**:通过游戏商店购买的物品 3. **宝箱开启**:通过开启宝箱获得的物品 4. **怪物掉落**:击败怪物获得的物品 5. **玩家交易**:通过与其他玩家交易获得的物品 6. **系统赠送**:系统活动或补偿赠送的物品 7. **合成获得**:通过物品合成获得的物品 8. **分解获得**:通过物品分解获得的物品 #### 6.3.2 来源记录格式 ```json { "source_type": "chest_open", "source_id": 12345, "details": { "chest_id": 101, "open_time": "2023-05-01 12:34:56", "is_pity": false } } ``` ### 6.4 物品获取限制 系统支持对物品获取进行限制,防止物品过度产出: #### 6.4.1 背包容量限制 1. **背包容量检查**: - 在添加物品前检查用户背包是否有足够空间 - 背包容量可以根据用户等级或VIP等级动态调整 2. **超出处理策略**: - 拒绝添加:当背包已满时拒绝添加新物品 - 邮件发送:将无法添加的物品通过邮件发送给用户 - 临时扩容:临时扩大背包容量,但用户需要尽快清理 #### 6.4.2 物品数量限制 1. **单个物品限制**: - 某些特殊物品可以设置最大持有数量 - 当达到限制时,无法再获取该物品 2. **物品组限制**: - 可以对一组相关物品设置总数限制 - 例如,限制所有任务道具的总数量 ## 7. 物品产出限制 为了防止某些物品产出超过预期,影响游戏平衡,系统实现了物品产出限制机制。 ### 7.1 限制类型 系统支持多种限制类型,可以根据游戏需求灵活配置: #### 7.1.1 全局总量限制 - **定义**:限制物品在整个游戏中的总产出数量 - **应用场景**:限量版物品、稀有收藏品 - **实现方式**: - 在 `item_output_limits` 表中设置 `limit_type=1` - 使用 `current_quantity` 字段记录当前已产出数量 - 每次产出前检查是否超过 `max_quantity` #### 7.1.2 单个用户限制 - **定义**:限制每个用户可获得的物品总数量 - **应用场景**:防止单个用户囤积稀有物品、控制关键物品分配 - **实现方式**: - 在 `item_output_limits` 表中设置 `limit_type=2` - 在 `item_user_output_counters` 表中记录每个用户的获取数量 - 每次产出前检查用户是否超过限制 #### 7.1.3 单日全局限制 - **定义**:限制每天在整个游戏中的产出数量 - **应用场景**:控制物品流通速度、防止市场泛滥 - **实现方式**: - 在 `item_output_limits` 表中设置 `limit_type=3` - 使用 `current_quantity` 字段记录当日已产出数量 - 每天重置计数 #### 7.1.4 单日用户限制 - **定义**:限制每个用户每天可获得的物品数量 - **应用场景**:日常活动物品、防止刷取行为 - **实现方式**: - 在 `item_output_limits` 表中设置 `limit_type=4` - 在 `item_user_output_counters` 表中记录用户当日获取数量 - 每天重置计数 ### 7.2 重置机制 不同类型的限制可以设置不同的重置周期,满足多样化的游戏需求: #### 7.2.1 重置类型 1. **不重置**(`reset_type=0`): - 永久限制,适用于限量物品 - 一旦达到限制,永远不会重置 2. **每日重置**(`reset_type=1`): - 每天重置限制计数 - 适用于日常活动物品 - 重置时间通常为凌晨0点 3. **每周重置**(`reset_type=2`): - 每周重置限制计数 - 适用于周常活动物品 - 重置时间通常为每周一凌晨0点 4. **每月重置**(`reset_type=3`): - 每月重置限制计数 - 适用于月度活动物品 - 重置时间通常为每月1日凌晨0点 #### 7.2.2 重置实现 系统通过定时任务实现自动重置: ```php // 伪代码示例 public function resetLimits() { $now = new DateTime(); // 重置每日限制 $dailyLimits = OutputLimit::where('reset_type', 1) ->where('last_reset_time', '<', $now->format('Y-m-d 00:00:00')) ->get(); foreach ($dailyLimits as $limit) { $limit->current_quantity = 0; $limit->last_reset_time = $now; $limit->save(); // 重置用户计数 UserOutputCounter::where('limit_id', $limit->id) ->update(['current_count' => 0]); } // 类似地处理每周和每月重置 // ... } ``` ### 7.3 关联物品限制 系统支持将多个相关物品关联到同一个限制规则下,防止通过不同渠道绕过限制: #### 7.3.1 共享限制额度 - **定义**:多个物品共享同一个限制额度 - **应用场景**:不同渠道获取的相同物品、功能相似的物品 - **实现方式**: - 在 `item_output_limits` 表的 `related_items` 字段中存储关联物品ID列表 - 检查限制时,同时考虑所有关联物品的产出数量 #### 7.3.2 关联物品配置 ```json // related_items字段示例 { "items": [1001, 1002, 1003], "relation_type": "shared_limit" } ``` ### 7.4 限制检查流程 在物品产出前,系统会执行以下检查流程,确保不超过设定的限制: #### 7.4.1 检查步骤 1. **查询物品限制**: ```php $limit = OutputLimit::where('item_id', $itemId)->first(); if (!$limit) { // 没有限制,直接允许产出 return true; } ``` 2. **检查限制类型**: ```php switch ($limit->limit_type) { case 1: // 全局总量限制 return $this->checkGlobalTotalLimit($limit, $quantity); case 2: // 单个用户限制 return $this->checkUserTotalLimit($limit, $userId, $quantity); case 3: // 单日全局限制 return $this->checkDailyGlobalLimit($limit, $quantity); case 4: // 单日用户限制 return $this->checkDailyUserLimit($limit, $userId, $quantity); } ``` 3. **检查关联物品**: ```php if (!empty($limit->related_items)) { $relatedItems = json_decode($limit->related_items, true); foreach ($relatedItems['items'] as $relatedItemId) { // 检查关联物品的产出记录 // ... } } ``` 4. **更新计数**: ```php if ($isGlobalLimit) { $limit->current_quantity += $quantity; $limit->save(); } else { $counter = UserOutputCounter::firstOrCreate([ 'user_id' => $userId, 'limit_id' => $limit->id ]); $counter->current_count += $quantity; $counter->save(); } ``` #### 7.4.2 超限处理 当检测到物品产出将超过限制时,系统可以采取以下处理方式: 1. **拒绝产出**:直接拒绝物品产出,返回错误信息 2. **部分产出**:产出不超过限制的部分数量 3. **替代产出**:提供替代物品作为补偿 4. **等待队列**:将超出部分放入等待队列,等待下次重置后产出 ### 7.5 监控与预警 系统提供监控和预警机制,帮助游戏运营团队及时了解物品产出情况: #### 7.5.1 接近限制预警 当物品产出数量接近限制时(例如达到80%),系统可以发送预警通知: ```php if ($currentQuantity >= $maxQuantity * 0.8 && $currentQuantity < $maxQuantity * 0.9) { // 发送80%预警 $this->sendWarning($itemId, '80%'); } elseif ($currentQuantity >= $maxQuantity * 0.9) { // 发送90%预警 $this->sendWarning($itemId, '90%'); } ``` #### 7.5.2 产出统计报告 系统定期生成物品产出统计报告,包括: 1. **总产出数量**:各物品的总产出数量 2. **限制使用情况**:各限制规则的使用情况 3. **用户获取分布**:物品在用户间的分布情况 4. **时间分布**:物品产出的时间分布 ## 8. 日志记录系统 物品系统的所有操作都会记录详细日志,用于数据分析、问题排查和安全审计。 ### 8.1 日志类型 系统根据不同的操作类型记录多种日志: #### 8.1.1 物品交易日志 在 `item_transaction_logs` 表中记录所有物品获取和消耗操作: | 字段名 | 类型 | 说明 | | --- | --- | --- | | id | int | 日志ID,主键 | | user_id | int | 用户ID | | item_id | int | 物品ID | | instance_id | int | 单独属性物品ID(可为空) | | quantity | int | 数量(正数表示获取,负数表示消耗) | | transaction_type | tinyint | 交易类型(1:获取, 2:使用, 3:交易, 4:出售, 5:过期, 6:删除...) | | source_type | varchar | 来源类型(任务、商店、宝箱等) | | source_id | int | 来源ID(任务ID、商店ID、宝箱ID等) | | details | json | 详细信息,以JSON格式存储 | | transaction_time | timestamp | 交易时间 | | ip_address | varchar | 操作的IP地址 | | device_info | varchar | 设备信息 | | created_at | timestamp | 创建时间 | #### 8.1.2 宝箱开启日志 在 `item_chest_open_logs` 表中记录宝箱开启操作: | 字段名 | 类型 | 说明 | | --- | --- | --- | | id | int | 日志ID,主键 | | user_id | int | 用户ID | | chest_id | int | 宝箱ID | | quantity | int | 开启数量 | | results | json | 获得的物品,以JSON格式存储 | | pity_triggered | tinyint | 是否触发保底机制(0:否, 1:是) | | pity_content_id | int | 触发保底的内容ID(可为空) | | open_time | timestamp | 开启时间 | | ip_address | varchar | 操作的IP地址 | | device_info | varchar | 设备信息 | | created_at | timestamp | 创建时间 | #### 8.1.3 物品合成日志 在 `item_craft_logs` 表中记录物品合成操作: | 字段名 | 类型 | 说明 | | --- | --- | --- | | id | int | 日志ID,主键 | | user_id | int | 用户ID | | recipe_id | int | 配方ID | | materials | json | 消耗的材料,以JSON格式存储 | | result_item_id | int | 获得的物品ID | | result_instance_id | int | 获得的单独属性物品ID(可为空) | | result_quantity | int | 获得的物品数量 | | is_success | tinyint | 是否成功(0:失败, 1:成功) | | craft_time | timestamp | 合成时间 | | ip_address | varchar | 操作的IP地址 | | device_info | varchar | 设备信息 | | created_at | timestamp | 创建时间 | #### 8.1.4 物品分解日志 在 `item_dismantle_logs` 表中记录物品分解操作: | 字段名 | 类型 | 说明 | | --- | --- | --- | | id | int | 记录ID,主键 | | user_id | int | 用户ID | | item_id | int | 被分解的物品ID | | instance_id | int | 被分解的单独属性物品ID(可为空) | | quantity | int | 分解数量 | | rule_id | int | 使用的分解规则ID | | results | json | 分解结果,包含获得的物品ID、数量等信息 | | dismantle_time | timestamp | 分解时间 | | ip_address | varchar | 操作的IP地址 | | device_info | varchar | 设备信息 | | created_at | timestamp | 创建时间 | ### 8.2 日志记录策略 系统采用以下策略记录日志,确保数据完整性和系统性能: #### 8.2.1 事务内记录 日志记录应在数据库事务内进行,确保操作和日志的一致性: ```php DB::beginTransaction(); try { // 执行物品操作 $this->addItemToUser($userId, $itemId, $quantity); // 记录日志 $this->logTransaction($userId, $itemId, $quantity, 1, $sourceType, $sourceId); DB::commit(); } catch (\Exception $e) { DB::rollBack(); throw $e; } ``` #### 8.2.2 异步日志记录 对于高频操作,可以使用队列实现异步日志记录,减轻数据库压力: ```php // 将日志记录任务推送到队列 Queue::push(new RecordItemTransactionLog($userId, $itemId, $quantity, $transactionType, $sourceType, $sourceId)); ``` #### 8.2.3 批量日志记录 对于批量操作,可以使用批量插入优化性能: ```php $logs = []; foreach ($items as $item) { $logs[] = [ 'user_id' => $userId, 'item_id' => $item['id'], 'quantity' => $item['quantity'], 'transaction_type' => $transactionType, 'transaction_time' => now(), // 其他字段... ]; } // 批量插入日志 DB::table('item_transaction_logs')->insert($logs); ``` ### 8.3 日志查询接口 系统提供以下接口用于查询日志: #### 8.3.1 查询用户物品历史 - **功能**:查询用户的物品获取和消耗历史 - **参数**:用户ID、物品ID(可选)、交易类型(可选)、时间范围(可选)、分页参数 - **返回**:物品交易记录列表 - **查询示例**: ```php $logs = TransactionLog::where('user_id', $userId) ->when($itemId, function ($query) use ($itemId) { return $query->where('item_id', $itemId); }) ->when($transactionType, function ($query) use ($transactionType) { return $query->where('transaction_type', $transactionType); }) ->when($startTime && $endTime, function ($query) use ($startTime, $endTime) { return $query->whereBetween('transaction_time', [$startTime, $endTime]); }) ->orderBy('transaction_time', 'desc') ->paginate($perPage); ``` #### 8.3.2 查询宝箱开启历史 - **功能**:查询用户的宝箱开启历史 - **参数**:用户ID、宝箱ID(可选)、时间范围(可选)、分页参数 - **返回**:宝箱开启记录列表 - **查询示例**: ```php $logs = ChestOpenLog::where('user_id', $userId) ->when($chestId, function ($query) use ($chestId) { return $query->where('chest_id', $chestId); }) ->when($startTime && $endTime, function ($query) use ($startTime, $endTime) { return $query->whereBetween('open_time', [$startTime, $endTime]); }) ->orderBy('open_time', 'desc') ->paginate($perPage); ``` ### 8.4 日志分析与应用 系统日志可用于多种分析和应用场景: #### 8.4.1 数据分析 1. **物品流通分析**: - 分析物品的获取和消耗渠道 - 识别最受欢迎的物品和获取方式 - 监控物品经济平衡 2. **用户行为分析**: - 分析用户的物品使用模式 - 识别高价值用户和活跃用户 - 发现潜在的游戏优化点 #### 8.4.2 问题排查 1. **物品丢失排查**: - 通过日志追踪物品的完整生命周期 - 确认物品是否正常获取和消耗 - 识别可能的系统错误或漏洞 2. **异常行为检测**: - 识别可能的作弊或滥用行为 - 监控异常的物品获取模式 - 发现潜在的系统漏洞 #### 8.4.3 用户支持 1. **物品恢复**: - 在系统错误导致物品丢失时,通过日志确认物品状态 - 提供准确的物品恢复服务 2. **用户查询**: - 回答用户关于物品获取和消耗的问题 - 提供物品历史记录查询服务 ### 8.5 日志清理与归档 为了管理日志数据量,系统实现了日志清理和归档机制: #### 8.5.1 日志保留策略 1. **实时日志**: - 保留最近30天的日志在主数据库中 - 用于实时查询和问题排查 2. **归档日志**: - 30天以上的日志移动到归档数据库 - 用于长期数据分析和审计 3. **统计汇总**: - 对超过180天的日志进行统计汇总 - 保留关键统计数据,删除详细记录 #### 8.5.2 归档实现 ```php // 伪代码示例 public function archiveLogs() { $cutoffDate = now()->subDays(30); // 查找需要归档的日志 $logsToArchive = TransactionLog::where('transaction_time', '<', $cutoffDate) ->limit(1000) ->get(); if ($logsToArchive->isEmpty()) { return; } // 插入到归档表 DB::connection('archive')->table('archived_transaction_logs')->insert( $logsToArchive->toArray() ); // 删除已归档的日志 $ids = $logsToArchive->pluck('id')->toArray(); TransactionLog::whereIn('id', $ids)->delete(); } ## 9. 物品合成系统 物品合成系统允许玩家使用已有物品合成新物品,增加游戏深度和物品获取途径。 ### 9.1 合成系统概述 物品合成系统具有以下特点: #### 9.1.1 系统特点 1. **多材料合成**:支持使用多种不同物品作为材料 2. **概率成功**:合成可以设置成功率,增加游戏随机性 3. **等级限制**:可以设置合成的等级或条件限制 4. **配方解锁**:玩家需要先解锁配方才能进行合成 5. **多币种成本**:支持消耗多种货币作为合成成本 #### 9.1.2 合成流程 1. **选择配方**:玩家从已解锁的配方中选择要合成的物品 2. **检查材料**:系统检查玩家是否拥有足够的材料 3. **支付成本**:扣除合成所需的货币成本 4. **消耗材料**:从玩家背包中扣除材料 5. **概率判定**:根据配方的成功率判定是否合成成功 6. **发放物品**:合成成功时,向玩家发放合成物品 7. **记录日志**:记录合成操作的详细信息 ### 9.2 数据库设计 合成系统涉及三个主要数据表:配方表、配方材料表和用户配方表。 #### 9.2.1 item_recipes 表(合成配方) | 字段名 | 类型 | 说明 | | --- | --- | --- | | id | int | 配方ID,主键 | | name | varchar | 配方名称 | | result_item_id | int | 产出物品ID,外键关联item_items表 | | result_min_quantity | int | 最小产出数量 | | result_max_quantity | int | 最大产出数量 | | success_rate | decimal(5,2) | 成功率(百分比,最大100) | | coin_cost | json | 货币成本,以JSON格式存储多种货币类型和数量 | | level_required | int | 所需等级 | | is_default_unlocked | tinyint | 是否默认解锁(0:否, 1:是) | | unlock_condition | json | 解锁条件,以JSON格式存储 | | cooldown_seconds | int | 冷却时间(秒) | | category_id | int | 配方分类ID | | sort_order | int | 排序权重 | | is_active | tinyint | 是否激活(0:否, 1:是) | | created_at | timestamp | 创建时间 | | updated_at | timestamp | 更新时间 | #### 9.2.2 item_recipe_materials 表(配方材料) | 字段名 | 类型 | 说明 | | --- | --- | --- | | id | int | 记录ID,主键 | | recipe_id | int | 配方ID,外键关联item_recipes表 | | item_id | int | 材料物品ID,外键关联item_items表 | | quantity | int | 所需数量 | | is_consumed | tinyint | 是否消耗(0:不消耗, 1:消耗) | | created_at | timestamp | 创建时间 | | updated_at | timestamp | 更新时间 | #### 9.2.3 item_user_recipes 表(用户配方) | 字段名 | 类型 | 说明 | | --- | --- | --- | | id | int | 记录ID,主键 | | user_id | int | 用户ID | | recipe_id | int | 配方ID,外键关联item_recipes表 | | is_unlocked | tinyint | 是否已解锁(0:否, 1:是) | | unlock_time | timestamp | 解锁时间 | | last_craft_time | timestamp | 最后合成时间 | | craft_count | int | 合成次数 | | created_at | timestamp | 创建时间 | | updated_at | timestamp | 更新时间 | ### 9.3 多币种成本设计 系统支持使用多种货币作为合成成本,增加游戏经济的深度: #### 9.3.1 币种类型 1. **游戏金币**:基础游戏货币 2. **钻石**:高级游戏货币,通常与真实货币关联 3. **声望**:特定阵营或活动的声望货币 4. **活动代币**:限时活动专用货币 #### 9.3.2 成本存储格式 在 `item_recipes` 表的 `coin_cost` 字段中,使用JSON格式存储多币种成本: ```json { "gold": 1000, "diamond": 5, "reputation": { "faction_id": 2, "amount": 100 }, "event_token": { "token_id": 101, "amount": 20 } } ``` #### 9.3.3 成本检查逻辑 ```php // 伪代码示例 public function checkCraftCost($userId, $recipeId) { $recipe = Recipe::find($recipeId); $coinCost = json_decode($recipe->coin_cost, true); // 检查各种货币是否足够 foreach ($coinCost as $coinType => $amount) { if (is_array($amount)) { // 处理特殊货币(声望、活动代币等) $specialCoinId = $amount['faction_id'] ?? $amount['token_id']; $specialCoinAmount = $amount['amount']; if (!$this->hasEnoughSpecialCoin($userId, $coinType, $specialCoinId, $specialCoinAmount)) { return false; } } else { // 处理基础货币(金币、钻石) if (!$this->hasEnoughCoin($userId, $coinType, $amount)) { return false; } } } return true; } ``` ### 9.4 合成成功率机制 系统支持概率性合成,增加游戏的随机性和挑战性: #### 9.4.1 基础成功率 每个配方都有一个基础成功率,定义在 `item_recipes` 表的 `success_rate` 字段中: - 成功率范围:0-100(百分比) - 成功率为100表示必定成功 - 成功率为0表示无法通过常规方式成功 #### 9.4.2 成功率调整因素 基础成功率可以受多种因素影响: 1. **玩家等级**:等级越高,成功率可能越高 2. **技能加成**:特定技能可以提高成功率 3. **道具加成**:使用特殊道具可以提高成功率 4. **设备加成**:使用特殊设备进行合成可以提高成功率 #### 9.4.3 成功率计算 ```php // 伪代码示例 public function calculateSuccessRate($userId, $recipeId) { $recipe = Recipe::find($recipeId); $baseRate = $recipe->success_rate; // 获取玩家等级加成 $user = User::find($userId); $levelBonus = $this->getLevelSuccessBonus($user->level); // 获取技能加成 $skillBonus = $this->getSkillSuccessBonus($userId); // 获取道具加成 $itemBonus = $this->getItemSuccessBonus($userId); // 计算最终成功率 $finalRate = $baseRate + $levelBonus + $skillBonus + $itemBonus; // 确保成功率在0-100范围内 return max(0, min(100, $finalRate)); } ``` ### 9.5 合成接口 系统提供以下接口用于物品合成: #### 9.5.1 获取可用配方 - **功能**:获取用户可用的合成配方列表 - **参数**:用户ID、分类ID(可选) - **返回**:配方列表,包含名称、材料、成功率等信息 - **处理逻辑**: 1. 查询用户已解锁的配方 2. 检查配方是否激活 3. 关联配方材料信息 4. 检查用户是否拥有足够材料 5. 返回配方列表,标记可合成状态 #### 9.5.2 执行合成 - **功能**:执行物品合成操作 - **参数**:用户ID、配方ID - **返回**:合成结果、获得的物品 - **处理逻辑**: 1. 检查配方是否存在且已解锁 2. 检查冷却时间是否已过 3. 检查用户是否拥有足够材料和货币 4. 扣除材料和货币 5. 根据成功率判定是否成功 6. 发放合成物品或返回失败信息 7. 更新用户配方使用记录 8. 记录合成日志 #### 9.5.3 解锁配方 - **功能**:解锁新的合成配方 - **参数**:用户ID、配方ID - **返回**:解锁结果 - **处理逻辑**: 1. 检查配方是否存在 2. 检查用户是否已解锁该配方 3. 检查解锁条件是否满足 4. 解锁配方并记录解锁时间 5. 返回解锁结果 ### 9.6 配方解锁机制 系统支持多种配方解锁机制,增加游戏的探索性和进度感: #### 9.6.1 解锁方式 1. **默认解锁**: - 在 `item_recipes` 表中设置 `is_default_unlocked=1` - 玩家创建角色时自动解锁 2. **等级解锁**: - 在 `unlock_condition` 中设置所需等级 - 玩家达到指定等级时自动解锁 3. **任务解锁**: - 在 `unlock_condition` 中设置关联任务 - 完成指定任务后解锁 4. **成就解锁**: - 在 `unlock_condition` 中设置关联成就 - 达成指定成就后解锁 5. **物品解锁**: - 使用特定物品(如配方书)解锁 - 系统检测物品使用事件并解锁对应配方 #### 9.6.2 解锁条件格式 在 `item_recipes` 表的 `unlock_condition` 字段中,使用JSON格式存储解锁条件: ```json { "type": "level", "value": 20 } ``` 或者更复杂的条件: ```json { "type": "multi", "conditions": [ { "type": "level", "value": 15 }, { "type": "quest", "quest_id": 1024, "status": "completed" }, { "type": "item", "item_id": 5001, "quantity": 1 } ], "logic": "and" } ``` ### 9.7 合成结果计算 系统支持多种合成结果计算方式,增加游戏的多样性: #### 9.7.1 固定结果 最简单的合成方式,成功后获得固定数量的物品: ```php // 伪代码示例 if ($this->isSuccess($userId, $recipeId)) { $recipe = Recipe::find($recipeId); $resultItemId = $recipe->result_item_id; $quantity = $recipe->result_min_quantity; // 固定数量 $this->addItemToUser($userId, $resultItemId, $quantity); return ['success' => true, 'item_id' => $resultItemId, 'quantity' => $quantity]; } else { return ['success' => false]; } ``` #### 9.7.2 随机数量 成功后获得的物品数量在一个范围内随机: ```php // 伪代码示例 if ($this->isSuccess($userId, $recipeId)) { $recipe = Recipe::find($recipeId); $resultItemId = $recipe->result_item_id; $minQuantity = $recipe->result_min_quantity; $maxQuantity = $recipe->result_max_quantity; // 随机数量 $quantity = rand($minQuantity, $maxQuantity); $this->addItemToUser($userId, $resultItemId, $quantity); return ['success' => true, 'item_id' => $resultItemId, 'quantity' => $quantity]; } else { return ['success' => false]; } ``` #### 9.7.3 品质变化 对于单独属性物品,合成可能影响物品的品质: ```php // 伪代码示例 if ($this->isSuccess($userId, $recipeId)) { $recipe = Recipe::find($recipeId); $resultItemId = $recipe->result_item_id; // 计算品质 $baseQuality = 100; $qualityVariation = rand(-10, 20); // 品质随机变化 $finalQuality = $baseQuality + $qualityVariation; // 创建单独属性物品 $attributes = [ 'quality' => $finalQuality, // 其他属性... ]; $instanceId = $this->createItemInstance($resultItemId, $attributes); $this->addItemInstanceToUser($userId, $resultItemId, $instanceId); return [ 'success' => true, 'item_id' => $resultItemId, 'instance_id' => $instanceId, 'quality' => $finalQuality ]; } else { return ['success' => false]; } ``` ## 10. 物品分解系统 物品分解系统允许玩家将不需要的物品分解为基础材料或其他资源,增加物品循环利用的途径,丰富游戏经济系统。 ### 10.1 分解系统概述 #### 10.1.1 系统特点 1. **资源回收**:将不需要的物品转化为有用的材料或资源 2. **经济平衡**:为过剩物品提供消耗渠道,维持游戏经济平衡 3. **多样化奖励**:分解可以获得多种类型的材料和资源 4. **随机性**:分解结果可以包含随机因素,增加游戏乐趣 5. **品质影响**:物品品质影响分解获得的材料数量和种类 #### 10.1.2 分解系统流程 1. **物品选择**:玩家选择要分解的物品和数量 2. **分解预览**:系统显示可能获得的材料及概率 3. **确认分解**:玩家确认分解操作 4. **规则匹配**:系统根据物品特性匹配适用的分解规则 5. **结果计算**:根据规则计算分解结果 6. **物品扣除**:从玩家背包中扣除被分解的物品 7. **材料发放**:向玩家发放分解获得的材料 8. **日志记录**:记录分解操作的详细信息 ### 10.2 数据库设计 分解系统涉及三个主要数据表:分解规则表、分解结果配置表和分解日志表。 #### 10.2.1 item_dismantle_rules 表(物品分解规则) | 字段名 | 类型 | 说明 | | --- | --- | --- | | id | int | 规则ID,主键 | | item_id | int | 物品ID,外键关联item_items表 | | category_id | int | 分类ID,外键关联item_categories表(与item_id二选一,用于对整个分类设置规则) | | min_rarity | tinyint | 最小适用稀有度 | | max_rarity | tinyint | 最大适用稀有度 | | priority | int | 规则优先级,当多个规则适用时,使用优先级最高的规则 | | is_active | tinyint | 是否激活(0:否, 1:是) | | created_at | timestamp | 创建时间 | | updated_at | timestamp | 更新时间 | #### 10.2.2 item_dismantle_results 表(分解结果配置) | 字段名 | 类型 | 说明 | | --- | --- | --- | | id | int | 记录ID,主键 | | rule_id | int | 分解规则ID,外键关联item_dismantle_rules表 | | result_item_id | int | 结果物品ID,外键关联item_items表 | | min_quantity | int | 最小数量 | | max_quantity | int | 最大数量 | | base_chance | decimal(5,2) | 基础获取概率(百分比,最大100) | | rarity_factor | decimal(5,2) | 稀有度影响因子,物品稀有度对数量的影响系数 | | quality_factor | decimal(5,2) | 品质影响因子,物品品质对数量的影响系数 | | created_at | timestamp | 创建时间 | | updated_at | timestamp | 更新时间 | #### 10.2.3 item_dismantle_logs 表(分解记录) | 字段名 | 类型 | 说明 | | --- | --- | --- | | id | int | 记录ID,主键 | | user_id | int | 用户ID | | item_id | int | 被分解的物品ID | | instance_id | int | 被分解的单独属性物品ID(可为空) | | quantity | int | 分解数量 | | rule_id | int | 使用的分解规则ID | | results | json | 分解结果,包含获得的物品ID、数量等信息 | | dismantle_time | timestamp | 分解时间 | | ip_address | varchar | 操作的IP地址 | | device_info | varchar | 设备信息 | | created_at | timestamp | 创建时间 | ### 10.3 分解规则和机制 物品分解系统支持多种规则和机制,确保分解结果的合理性和游戏平衡。 #### 10.3.1 规则匹配逻辑 系统按照以下优先级顺序匹配分解规则: 1. **精确匹配**:根据物品ID精确匹配分解规则 2. **分类匹配**:当没有精确匹配规则时,根据物品分类匹配规则 3. **稀有度范围**:规则可以设置适用的稀有度范围 4. **优先级**:当多个规则都适用时,使用优先级最高的规则 ```php // 伪代码示例 public function findDismantleRule($itemId, $rarity) { // 1. 尝试精确匹配 $rule = DismantleRule::where('item_id', $itemId) ->where('is_active', 1) ->where('min_rarity', '<=', $rarity) ->where('max_rarity', '>=', $rarity) ->orderBy('priority', 'desc') ->first(); if ($rule) { return $rule; } // 2. 尝试分类匹配 $item = Item::find($itemId); $rule = DismantleRule::where('category_id', $item->category_id) ->where('item_id', null) ->where('is_active', 1) ->where('min_rarity', '<=', $rarity) ->where('max_rarity', '>=', $rarity) ->orderBy('priority', 'desc') ->first(); return $rule; } ``` #### 10.3.2 特殊物品处理 不同类型的物品在分解时有特殊处理逻辑: 1. **绑定物品**:绑定物品分解后,获得的材料也会继承绑定状态 2. **不可分解物品**:通过item_items表中的dismantlable字段标记物品是否可分解 3. **单独属性物品**:分解单独属性物品时,可以根据其特殊属性调整分解结果 ```php // 伪代码示例 public function canDismantle($itemId, $instanceId = null) { $item = Item::find($itemId); // 检查物品是否可分解 if ($item->dismantlable == 0) { return false; } // 检查单独属性物品 if ($instanceId) { $instance = ItemInstance::find($instanceId); // 可以添加特殊条件,如不允许分解特定品质以上的装备 if ($instance->numeric_attributes->quality > 90) { return false; } } return true; } ``` #### 10.3.3 批量分解功能 系统支持多种批量分解方式,提高用户体验: 1. **同类物品批量分解**:一次分解多个相同物品 2. **多类物品批量分解**:一次分解多种不同物品 3. **自动分解**:可以设置自动分解规则,如自动分解低品质物品 ```php // 伪代码示例 public function batchDismantle($userId, $items) { $results = []; $totalResults = []; DB::beginTransaction(); try { foreach ($items as $item) { $itemId = $item['item_id']; $instanceId = $item['instance_id'] ?? null; $quantity = $item['quantity']; // 检查是否可分解 if (!$this->canDismantle($itemId, $instanceId)) { continue; } // 执行分解 $dismantleResult = $this->dismantleItem($userId, $itemId, $instanceId, $quantity); $results[] = $dismantleResult; // 合并结果 foreach ($dismantleResult['materials'] as $material) { if (isset($totalResults[$material['item_id']])) { $totalResults[$material['item_id']]['quantity'] += $material['quantity']; } else { $totalResults[$material['item_id']] = $material; } } } DB::commit(); return [ 'success' => true, 'detail_results' => $results, 'total_materials' => array_values($totalResults) ]; } catch (\Exception $e) { DB::rollBack(); return ['success' => false, 'message' => $e->getMessage()]; } } ``` ### 10.4 分解结果计算 分解结果的计算是一个多步骤的过程,考虑多种因素。 #### 10.4.1 基础数量计算 首先计算基础数量: ```php // 伪代码示例 $baseQuantity = rand($resultConfig->min_quantity, $resultConfig->max_quantity); ``` #### 10.4.2 稀有度和品质影响 物品的稀有度和品质会影响最终获得的数量: ```php // 伪代码示例 $rarityBonus = ($item->rarity - 1) * $resultConfig->rarity_factor; // 对于单独属性物品,考虑品质 $qualityBonus = 0; if ($instanceId) { $instance = ItemInstance::find($instanceId); $quality = $instance->numeric_attributes->quality ?? 0; $qualityBonus = $quality * $resultConfig->quality_factor; } $finalQuantity = ceil($baseQuantity * (1 + $rarityBonus) * (1 + $qualityBonus)); ``` #### 10.4.3 概率计算 每个可能的分解结果都有一个获取概率,也受物品稀有度和品质影响: ```php // 伪代码示例 $baseChance = $resultConfig->base_chance; $rarityBonus = ($item->rarity - 1) * 5; // 每级稀有度增加5%概率 // 对于单独属性物品,考虑品质 $qualityBonus = 0; if ($instanceId) { $instance = ItemInstance::find($instanceId); $quality = $instance->numeric_attributes->quality ?? 0; $qualityBonus = $quality * 0.1; // 每点品质增加0.1%概率 } $finalChance = $baseChance + $rarityBonus + $qualityBonus; $finalChance = min(100, $finalChance); // 最大100% // 判断是否获得该结果 $isObtained = (mt_rand(1, 100) <= $finalChance); ``` #### 10.4.4 特殊结果机制 某些特殊情况下,分解可能有额外结果: 1. **暴击分解**:有小概率获得双倍或三倍材料 2. **稀有材料**:分解高品质物品有几率获得稀有材料 3. **特殊道具**:某些物品分解可能获得特殊道具或货币 ```php // 伪代码示例 // 检查是否触发暴击 $criticalChance = 5; // 5%暴击概率 $isCritical = (mt_rand(1, 100) <= $criticalChance); if ($isCritical) { $criticalMultiplier = (mt_rand(1, 100) <= 30) ? 3 : 2; // 30%几率3倍,70%几率2倍 $finalQuantity *= $criticalMultiplier; $results['is_critical'] = true; $results['critical_multiplier'] = $criticalMultiplier; } // 检查是否获得稀有材料 if ($item->rarity >= 3 && mt_rand(1, 100) <= 10) { $rareMaterials = $this->getRareMaterials($item->category_id); // 添加稀有材料到结果中 } ``` ### 10.5 分解系统接口 系统提供以下接口用于物品分解: #### 10.5.1 获取物品分解预览 - **功能**:预览物品分解可能获得的材料 - **参数**:用户ID、物品ID、数量 - **返回**:可能获得的材料列表及概率 - **处理逻辑**: 1. 检查物品是否可分解 2. 获取适用的分解规则 3. 获取可能的分解结果及概率 4. 返回预览信息 #### 10.5.2 执行物品分解 - **功能**:分解指定物品并获得材料 - **参数**:用户ID、物品ID、数量 - **返回**:分解结果、获得的材料列表 - **处理逻辑**: 1. 检查用户是否拥有足够数量的物品 2. 检查物品是否可分解 3. 获取适用的分解规则 4. 计算分解结果 5. 从用户背包中扣除物品 6. 向用户背包添加获得的材料 7. 记录分解日志 #### 10.5.3 批量分解物品 - **功能**:批量分解多个物品 - **参数**:用户ID、物品ID列表及数量 - **返回**:分解结果、获得的材料列表 - **处理逻辑**: 1. 检查用户是否拥有所有指定物品 2. 对每个物品执行分解操作 3. 合并分解结果 4. 返回总体分解结果 #### 10.5.4 获取分解历史 - **功能**:查询用户的物品分解历史 - **参数**:用户ID、时间范围(可选) - **返回**:分解历史记录列表 - **处理逻辑**: 1. 查询用户的分解日志记录 2. 按时间排序 3. 返回分解历史列表 ### 10.6 分解日志记录 系统记录所有分解操作,确保可追溯性和数据分析需求: #### 10.6.1 日志内容 在 `item_dismantle_logs` 表中记录以下信息: 1. **用户信息**:执行分解的用户ID 2. **物品信息**:被分解的物品ID、数量 3. **分解结果**:获得的材料列表及数量 4. **时间信息**:分解操作的时间 5. **规则信息**:应用的分解规则ID 6. **安全信息**:IP地址、设备信息等 #### 10.6.2 日志用途 1. **数据分析**:分析玩家分解行为和偏好 2. **问题排查**:解决玩家关于分解结果的疑问 3. **系统监控**:监控分解系统的运行状态 4. **平衡调整**:为游戏平衡调整提供数据支持 ### 10.7 与其他系统的交互 分解系统与游戏中的多个系统有紧密的交互关系: #### 10.7.1 与背包系统交互 - 分解操作会减少背包中的物品,并添加新的材料 - 需要检查背包容量是否足够存放分解获得的材料 #### 10.7.2 与任务和成就系统交互 - 分解特定物品或获得特定材料可能触发任务进度 - 累计分解次数或特定物品分解可能触发成就 #### 10.7.3 与合成系统交互 - 分解获得的材料可用于物品合成 - 分解和合成形成物品循环利用的闭环 #### 10.7.4 与商店系统交互 - 分解获得的材料可以在商店出售或用于购买其他物品 - 商店可以提供分解服务,收取一定费用 ## 11. 物品绑定机制 物品绑定机制用于限制物品的流通和交易,保持游戏经济平衡,并增加游戏内容的专属性。 ### 11.1 绑定机制概述 #### 11.1.1 绑定机制目的 物品绑定机制主要用于以下目的: 1. **限制物品交易**:防止高价值物品在玩家间无限制流通 2. **增加成就感**:使某些物品具有专属性,提高获取难度和价值感 3. **控制游戏经济**:防止物品市场泛滥和通货膨胀 4. **区分物品来源**:通过不同的绑定状态区分物品的获取途径 5. **增加游戏深度**:为不同物品设计不同的绑定规则,增加游戏策略性 #### 11.1.2 绑定与不可交易的区别 物品绑定和物品不可交易是两个不同的概念,虽然它们都会限制物品的流通,但在实现方式、应用场景和用户体验上有明显区别: 1. **概念区别**: - **物品绑定**:指物品与特定玩家或角色建立关联,限制物品只能被该玩家或角色使用 - **物品不可交易**:指物品本身的一种固有属性,决定该物品是否可以参与玩家间的交易 2. **状态变化**: - **物品绑定**:是一种状态变化,物品可以从未绑定变为已绑定 - **物品不可交易**:是物品的固有特性,通常在物品创建时就已确定 3. **触发条件**: - **物品绑定**:通常发生在特定触发条件下(获取、使用、装备等) - **物品不可交易**:不依赖于物品的使用状态或归属,始终保持不变 ### 11.2 绑定类型 系统支持多种绑定类型,可以根据游戏需求灵活配置: #### 11.2.1 按绑定时机分类 1. **不绑定**:物品可以自由交易和转移 2. **获取绑定**:物品一旦被玩家获取后绑定到该玩家账号 3. **使用绑定**:物品在首次使用后绑定到玩家账号 4. **装备绑定**:装备类物品在首次装备后绑定 #### 11.2.2 按绑定对象分类 1. **角色绑定**:物品绑定到特定角色,不可在同一账号的不同角色间转移 2. **账号绑定**:物品绑定到账号,可在同一账号的不同角色间转移 #### 11.2.3 按绑定时长分类 1. **永久绑定**:一旦绑定,永远不会解除 2. **时间绑定**:物品在特定时间段内绑定,之后可以解除绑定 ### 11.3 数据库设计 物品绑定机制主要针对单独属性物品(item_instances),因为这些物品具有独特性和个体差异。 #### 11.3.1 item_instances 表中的绑定相关字段 | 字段名 | 类型 | 说明 | | --- | --- | --- | | is_bound | tinyint | 是否已绑定(0:未绑定, 1:已绑定) | | bound_to | varchar | 绑定对象(账号ID或角色ID) | | bind_exp_time | timestamp | 绑定过期时间(为空表示永久绑定) | 这种设计的优势: 1. **与物品实例直接关联**:绑定状态直接与物品实例关联,而不是与用户物品关系关联 2. **简化数据查询**:不需要跨表查询即可获取物品的绑定状态 3. **更好的语义表达**:绑定是物品实例的属性,而不是用户与物品关系的属性 4. **支持物品转移**:当物品在用户间转移时,绑定状态随物品实例一起转移 ### 11.4 绑定状态处理逻辑 物品绑定状态的处理逻辑主要针对单独属性物品(item_instances): #### 11.4.1 获取绑定处理 ```php // 伪代码示例 public function handleAcquireBind($itemId, $instanceId, $userId) { $item = Item::find($itemId); // 检查物品是否需要获取绑定 if ($item->bind_type == 1) { // 获取绑定 $instance = ItemInstance::find($instanceId); $instance->is_bound = 1; $instance->bound_to = $userId; $instance->bind_exp_time = null; // 永久绑定 $instance->save(); // 记录绑定日志 $this->logItemBind($instanceId, $userId, 'acquire'); } } ``` #### 11.4.2 使用绑定处理 ```php // 伪代码示例 public function handleUseBind($itemId, $instanceId, $userId) { $item = Item::find($itemId); // 检查物品是否需要使用绑定 if ($item->bind_type == 2) { // 使用绑定 $instance = ItemInstance::find($instanceId); // 只有未绑定的物品才需要处理 if ($instance->is_bound == 0) { $instance->is_bound = 1; $instance->bound_to = $userId; $instance->bind_exp_time = null; // 永久绑定 $instance->save(); // 记录绑定日志 $this->logItemBind($instanceId, $userId, 'use'); } } } ``` #### 11.4.3 装备绑定处理 ```php // 伪代码示例 public function handleEquipBind($itemId, $instanceId, $userId) { $item = Item::find($itemId); // 检查物品是否需要装备绑定 if ($item->bind_type == 3) { // 装备绑定 $instance = ItemInstance::find($instanceId); // 只有未绑定的物品才需要处理 if ($instance->is_bound == 0) { $instance->is_bound = 1; $instance->bound_to = $userId; $instance->bind_exp_time = null; // 永久绑定 $instance->save(); // 记录绑定日志 $this->logItemBind($instanceId, $userId, 'equip'); } } } ``` #### 11.4.4 临时绑定处理 ```php // 伪代码示例 public function handleTimeBind($itemId, $instanceId, $userId, $bindDuration) { $item = Item::find($itemId); // 检查物品是否需要时间绑定 if ($item->bind_type == 6) { // 时间绑定 $instance = ItemInstance::find($instanceId); $instance->is_bound = 1; $instance->bound_to = $userId; // 计算绑定过期时间 $expTime = now()->addSeconds($bindDuration); $instance->bind_exp_time = $expTime; $instance->save(); // 记录绑定日志 $this->logItemBind($instanceId, $userId, 'time', $expTime); } } ``` ### 11.5 绑定物品的交易限制 单独属性物品的交易限制由两个因素决定:物品的可交易性(tradable)和绑定状态(is_bound): #### 11.5.1 交易检查流程 ```php // 伪代码示例 public function canTrade($instanceId, $fromUserId, $toUserId) { $instance = ItemInstance::find($instanceId); // 检查物品是否可交易 if ($instance->tradable == 0) { return [ 'can_trade' => false, 'reason' => 'item_not_tradable' ]; } // 检查物品是否已绑定 if ($instance->is_bound == 1) { // 检查绑定对象 if ($instance->bound_to != $fromUserId) { return [ 'can_trade' => false, 'reason' => 'item_bound_to_another_user' ]; } // 检查绑定类型(账号绑定可以在同一账号的角色间转移) $fromUser = User::find($fromUserId); $toUser = User::find($toUserId); if ($fromUser->account_id != $toUser->account_id) { return [ 'can_trade' => false, 'reason' => 'item_bound_to_account' ]; } } return [ 'can_trade' => true ]; } ``` #### 11.5.2 商店出售限制 ```php // 伪代码示例 public function canSellToShop($instanceId, $userId) { $instance = ItemInstance::find($instanceId); // 不可交易物品通常不能出售给NPC商店 if ($instance->tradable == 0) { return [ 'can_sell' => false, 'reason' => 'item_not_tradable' ]; } // 已绑定物品只能由绑定的玩家出售 if ($instance->is_bound == 1 && $instance->bound_to != $userId) { return [ 'can_sell' => false, 'reason' => 'item_bound_to_another_user' ]; } return [ 'can_sell' => true, // 已绑定物品可能有价格折扣 'price_modifier' => $instance->is_bound ? 0.5 : 1.0 ]; } ``` #### 11.5.3 邮件发送限制 ```php // 伪代码示例 public function canSendByMail($instanceId, $fromUserId, $toUserId) { // 邮件发送限制与交易限制相同 return $this->canTrade($instanceId, $fromUserId, $toUserId); } ``` ### 11.6 应用场景 物品绑定机制适用于多种场景: #### 11.6.1 任务奖励 重要任务奖励通常设置为获取绑定,确保玩家亲自完成任务: ```php // 伪代码示例 public function giveQuestReward($userId, $questId) { $quest = Quest::find($questId); foreach ($quest->rewards as $reward) { $itemId = $reward['item_id']; $quantity = $reward['quantity']; // 创建物品实例并设置为获取绑定 $instanceId = $this->createItemInstance($itemId); $this->handleAcquireBind($itemId, $instanceId, $userId); // 添加到用户背包 $this->addItemInstanceToUser($userId, $itemId, $instanceId); } } ``` #### 11.6.2 成就奖励 成就奖励物品通常设置为账号绑定,表彰玩家的个人成就: ```php // 伪代码示例 public function giveAchievementReward($userId, $achievementId) { $achievement = Achievement::find($achievementId); $user = User::find($userId); foreach ($achievement->rewards as $reward) { $itemId = $reward['item_id']; $quantity = $reward['quantity']; // 创建物品实例并设置为账号绑定 $instanceId = $this->createItemInstance($itemId); $instance = ItemInstance::find($instanceId); $instance->is_bound = 1; $instance->bound_to = $user->account_id; // 绑定到账号 $instance->save(); // 添加到用户背包 $this->addItemInstanceToUser($userId, $itemId, $instanceId); } } ``` #### 11.6.3 活动物品 活动限定物品通常设置为获取绑定,防止二次交易: ```php // 伪代码示例 public function giveEventItem($userId, $eventId, $itemId) { $event = Event::find($eventId); // 创建物品实例并设置为获取绑定 $instanceId = $this->createItemInstance($itemId); $this->handleAcquireBind($itemId, $instanceId, $userId); // 添加到用户背包 $this->addItemInstanceToUser($userId, $itemId, $instanceId); } ``` ### 11.7 绑定状态的显示 在游戏界面中,应清晰显示物品的绑定状态: #### 11.7.1 物品图标标记 已绑定物品在图标上添加特殊标记,区分不同的绑定类型: ```php // 伪代码示例 public function getItemIconWithBindMark($itemId, $instanceId) { $item = Item::find($itemId); $iconPath = $item->icon; if ($instanceId) { $instance = ItemInstance::find($instanceId); if ($instance->is_bound) { // 根据绑定类型添加不同的标记 if ($instance->bind_exp_time) { return $iconPath . '?mark=time_bound'; } else { return $iconPath . '?mark=bound'; } } } return $iconPath; } ``` #### 11.7.2 物品描述 在物品描述中注明绑定类型和状态: ```php // 伪代码示例 public function getItemDescription($itemId, $instanceId) { $item = Item::find($itemId); $description = $item->description; if ($instanceId) { $instance = ItemInstance::find($instanceId); if ($instance->is_bound) { $description .= "\n已绑定到: " . $this->getBindTargetName($instance->bound_to); if ($instance->bind_exp_time) { $description .= "\n绑定解除时间: " . $instance->bind_exp_time; } else { $description .= "\n永久绑定"; } } } return $description; } ``` #### 11.7.3 交易提示 尝试交易绑定物品时,显示明确的错误提示: ```php // 伪代码示例 public function getTradeErrorMessage($result) { switch ($result['reason']) { case 'item_not_tradable': return "该物品不可交易"; case 'item_bound_to_another_user': return "该物品已绑定到其他玩家,无法交易"; case 'item_bound_to_account': return "该物品已绑定到账号,只能在同一账号的角色间转移"; default: return "无法交易该物品"; } } ## 12. 系统交互 GameItems模块作为游戏的核心系统之一,与其他多个系统有紧密的交互关系。 ### 12.1 与其他系统的交互 #### 12.1.1 与用户系统交互 物品系统需要与用户系统交互,确保物品正确关联到用户: ```php // 伪代码示例 public function checkUserExists($userId) { $user = User::find($userId); if (!$user) { throw new UserNotFoundException("用户不存在: $userId"); } return $user; } ``` #### 12.1.2 与任务系统交互 物品系统与任务系统交互,支持任务相关的物品操作: ```php // 伪代码示例 public function checkQuestItemRequirement($userId, $questId) { $quest = Quest::find($questId); $requirements = $quest->item_requirements; foreach ($requirements as $req) { $itemId = $req['item_id']; $quantity = $req['quantity']; $userItem = UserItem::where('user_id', $userId) ->where('item_id', $itemId) ->first(); if (!$userItem || $userItem->quantity < $quantity) { return false; } } return true; } ``` #### 12.1.3 与商店系统交互 物品系统与商店系统交互,支持物品的购买和出售: ```php // 伪代码示例 public function buyItemFromShop($userId, $shopItemId, $quantity) { $shopItem = ShopItem::find($shopItemId); $itemId = $shopItem->item_id; $price = $shopItem->price * $quantity; // 检查用户余额 $user = User::find($userId); if ($user->balance < $price) { return [ 'success' => false, 'message' => '余额不足' ]; } // 扣除余额 $user->balance -= $price; $user->save(); // 添加物品 $this->addItemToUser($userId, $itemId, $quantity); return [ 'success' => true, 'item_id' => $itemId, 'quantity' => $quantity, 'price' => $price ]; } ``` #### 12.1.4 与邮件系统交互 物品系统与邮件系统交互,支持通过邮件发送物品: ```php // 伪代码示例 public function sendItemByMail($fromUserId, $toUserId, $itemId, $quantity, $message) { // 检查物品是否可以发送 if (!$this->canSendItem($fromUserId, $itemId, $quantity)) { return [ 'success' => false, 'message' => '物品不可发送' ]; } // 从发送者背包中扣除物品 $this->removeItemFromUser($fromUserId, $itemId, $quantity); // 创建邮件 $mail = Mail::create([ 'from_user_id' => $fromUserId, 'to_user_id' => $toUserId, 'subject' => '物品发送', 'message' => $message, 'has_attachment' => 1 ]); // 添加邮件附件 MailAttachment::create([ 'mail_id' => $mail->id, 'item_id' => $itemId, 'quantity' => $quantity ]); return [ 'success' => true, 'mail_id' => $mail->id ]; } ``` ### 12.2 事件系统 物品系统使用事件机制与其他系统进行松耦合交互: #### 12.2.1 物品相关事件 系统定义了以下物品相关事件: 1. **ItemAcquired**:物品添加到用户背包时触发 2. **ItemConsumed**:物品从用户背包消耗时触发 3. **ItemQuantityChanged**:物品数量变更时触发(增加或减少) 4. **ItemUsed**:物品被使用时触发 5. **ItemTraded**:物品被交易时触发 6. **ChestOpened**:宝箱被开启时触发 7. **ItemCrafted**:物品被合成时触发 8. **ItemDismantled**:物品被分解时触发 9. **ItemBound**:物品被绑定时触发 #### 12.2.2 事件监听 其他系统可以监听这些事件并执行相应操作: ```php // 伪代码示例 // 在服务提供者中注册事件监听 public function boot() { // 监听物品获取事件 Event::listen(ItemAcquired::class, function ($event) { // 更新任务进度 $this->questService->updateItemCollectionProgress( $event->userId, $event->itemId, $event->quantity ); // 更新成就进度 $this->achievementService->updateItemCollectionProgress( $event->userId, $event->itemId, $event->quantity ); }); // 监听物品数量变更事件 Event::listen(ItemQuantityChanged::class, function ($event) { // 记录物品数量变更 \Log::info('物品数量变更', [ 'user_id' => $event->userId, 'item_id' => $event->itemId, 'old_quantity' => $event->oldQuantity, 'new_quantity' => $event->newQuantity, 'change_amount' => $event->changeAmount, ]); // 触发相关系统更新 if ($event->changeAmount > 0) { // 物品数量增加的处理 $this->inventoryService->checkInventoryAchievements($event->userId, $event->itemId, $event->newQuantity); } else { // 物品数量减少的处理 $this->inventoryService->checkInventoryQuests($event->userId, $event->itemId, $event->newQuantity); } }); } ``` ### 12.3 API接口 物品系统提供以下API接口供其他系统调用: #### 12.3.1 物品查询接口 ```php // 伪代码示例 public function getUserItems($userId, $filters = []) { $query = UserItem::where('user_id', $userId); // 应用过滤条件 if (isset($filters['item_id'])) { $query->where('item_id', $filters['item_id']); } if (isset($filters['category_id'])) { $query->whereHas('item', function ($q) use ($filters) { $q->where('category_id', $filters['category_id']); }); } // 排除过期物品 $query->where(function ($q) { $q->whereNull('expire_at') ->orWhere('expire_at', '>', now()); }); return $query->get(); } ``` #### 12.3.2 物品操作接口 ```php // 伪代码示例 public function addItem($userId, $itemId, $quantity, $options = []) { // 检查用户是否存在 $this->checkUserExists($userId); // 检查物品是否存在 $item = Item::find($itemId); if (!$item) { throw new ItemNotFoundException("物品不存在: $itemId"); } // 处理单独属性物品 if ($item->is_unique) { return $this->addUniqueItem($userId, $itemId, $options); } // 处理统一属性物品 return $this->addNormalItem($userId, $itemId, $quantity, $options); } ``` ## 13. 最佳实践与注意事项 ### 13.1 性能优化 为确保物品系统的高性能运行,应遵循以下最佳实践: #### 13.1.1 数据库优化 1. **合理使用索引**: - 为频繁查询的字段创建索引 - 避免过多索引导致写入性能下降 - 定期分析查询性能,优化索引设计 2. **批量操作**: - 使用批量插入和更新减少数据库操作次数 - 对于大量物品操作,使用事务确保原子性 3. **分表策略**: - 对于高频访问的日志表,考虑按时间或用户ID分表 - 历史数据可以归档到单独的表中 #### 13.1.2 缓存策略 1. **用户物品缓存**: - 缓存用户常用物品列表,减少数据库查询 - 使用Redis等内存数据库存储临时数据 2. **物品定义缓存**: - 缓存物品基础定义,避免重复查询 - 使用缓存标签管理相关缓存 3. **缓存失效策略**: - 在物品数据变更时主动清除相关缓存 - 设置合理的缓存过期时间 #### 13.1.3 异步处理 1. **队列处理**: - 使用队列处理非即时性操作,如日志记录 - 批量物品操作可以放入队列异步处理 2. **定时任务**: - 使用定时任务处理物品过期、绑定解除等周期性操作 - 将资源密集型操作安排在服务器负载较低的时间执行 ### 13.2 安全考虑 物品系统涉及游戏经济核心,需要特别注意安全性: #### 13.2.1 防作弊措施 1. **操作验证**: - 所有物品操作都需要进行权限和合法性验证 - 敏感操作(如删除物品)需要额外的验证步骤 2. **异常监控**: - 监控异常的物品获取和消耗模式 - 设置物品操作的频率限制和阈值警报 3. **日志审计**: - 记录所有物品操作的详细日志 - 定期审计物品流通情况,发现潜在问题 #### 13.2.2 并发控制 1. **乐观锁**: - 使用版本号或时间戳实现乐观锁 - 在高并发场景下防止数据不一致 2. **悲观锁**: - 对于关键操作,使用数据库行锁或表锁 - 在事务中锁定相关记录,防止并发修改 3. **分布式锁**: - 在分布式环境中,使用Redis等实现分布式锁 - 确保跨服务器的操作原子性 ### 13.3 通用注意事项 在实现和维护物品系统时,应注意以下事项: 1. **物品使用逻辑**应根据物品类型进行不同处理 2. **需要考虑物品数量上限和背包容量限制** 3. **稀有物品获取应有日志记录** 4. **物品交易需考虑并发安全问题,建议使用数据库事务** 5. **敏感操作(如删除物品)需要有权限验证** 6. **在获取用户物品时,始终检查过期时间,不展示已过期物品** 7. **在添加物品到用户背包时,需要检查全局过期时间** 8. **宝箱开启操作应使用事务确保原子性** 9. **宝箱配置应由管理员仔细调整和测试,确保概率合理** 10. **当物品数量超过最大堆叠数量时,应自动创建新的堆叠** 11. **在物品产出前应检查产出限制,防止超过预期产出** 12. **对于关联物品限制,需要同时检查所有相关物品的产出记录** 13. **定期检查并重置限制计数,确保重置机制正常运行** 14. **对于限量物品,应设置监控机制,在接近限制时发出警告** 15. **所有物品获取和消耗操作应记录到交易日志表中** 16. **宝箱开启操作应同时记录到宝箱开启日志和物品交易日志** 17. **定期清理和归档旧日志,防止日志表过大影响系统性能** 18. **对于高频率的日志写入操作,考虑使用队列或批量写入方式提高性能** 19. **物品合成操作应使用事务确保原子性,防止材料扣除后合成失败导致数据不一致** 20. **合成配方的成功率和奖励应由管理员仔细调整和测试,确保游戏平衡** 21. **对于高价值物品的合成,应考虑添加额外的验证和确认机制** 22. **合成系统应考虑与物品产出限制系统集成,防止通过合成绕过产出限制** 23. **处理多币种成本时,应确保在事务中同时检查和扣除所有必要的代币,防止部分扣除导致的数据不一致** 24. **绑定物品的状态变更应在事务中处理,确保数据一致性** 25. **对于时间绑定物品,需要定期检查并更新绑定状态** 26. **在物品交易前,必须检查物品的绑定状态,防止绑定物品被交易** 27. **物品分解操作应使用事务确保原子性,防止物品被扣除但未获得分解结果** 28. **分解绑定物品时,获得的材料应继承绑定状态** 29. **分解单独属性物品时,应考虑物品的特殊属性对分解结果的影响** 30. **批量分解时,应注意性能优化,避免大量物品同时分解导致系统负担**