游戏物品系统 - 综合管理游戏内所有物品的生命周期
GameItems模块是游戏核心系统之一,负责管理游戏内所有物品的完整生命周期,包括创建、获取、使用、交易、过期和销毁。该模块为游戏内经济系统和玩家进度提供基础支持,是连接多个游戏子系统的核心组件。
游戏物品模块采用关系型数据库设计,通过多个相互关联的数据表实现物品管理。数据结构设计遵循以下原则:
| 表名 | 主要功能 | 关键字段 |
|---|---|---|
| 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 |
| 表名 | 主要功能 | 关键字段 |
|---|---|---|
| item_chest_contents | 宝箱内容配置 | chest_id, item_id/group_id, weight |
| item_pity_times | 用户宝箱保底计数 | user_id, chest_id, chest_content_id, current_count |
| item_chest_open_logs | 宝箱开启记录 | user_id, chest_id, result_items, pity_triggered |
| 表名 | 主要功能 | 关键字段 |
|---|---|---|
| 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 |
| 表名 | 主要功能 | 关键字段 |
|---|---|---|
| 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_results | 分解结果配置 | rule_id, result_item_id, chance |
| item_dismantle_logs | 物品分解记录 | user_id, item_id, results |
| 字段名 | 类型 | 说明 |
|---|---|---|
| id | int | 分类ID,主键 |
| name | varchar | 分类名称 |
| code | varchar | 分类编码(唯一) |
| icon | varchar | 分类图标 |
| sort | int | 排序权重 |
| parent_id | int | 父分类ID(可为空,用于实现分类层级) |
| created_at | timestamp | 创建时间 |
| updated_at | timestamp | 更新时间 |
| 字段名 | 类型 | 说明 |
|---|---|---|
| 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 | 更新时间 |
| 字段名 | 类型 | 说明 |
|---|---|---|
| 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 | 更新时间 |
| 字段名 | 类型 | 说明 |
|---|---|---|
| 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 | 更新时间 |
为确保系统高性能运行,对关键字段进行了索引设计:
| 表名 | 索引字段 | 索引类型 | 说明 |
|---|---|---|---|
| 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 | 普通索引 | 加速过期物品查询 |
| 表名 | 索引字段 | 索引类型 | 说明 |
|---|---|---|---|
| item_chest_contents | chest_id | 普通索引 | 加速宝箱内容查询 |
| item_chest_contents | item_id | 普通索引 | 加速物品在宝箱中的查询 |
| item_chest_contents | group_id | 普通索引 | 加速查询物品组在宝箱中的配置 |
| item_chest_contents | pity_count | 普通索引 | 加速查询启用保底的宝箱内容 |
| item_pity_times | user_id, chest_id, chest_content_id | 复合索引 | 加速查询用户对特定宝箱内容的保底计数 |
| item_chest_open_logs | user_id, open_time | 复合索引 | 加速查询用户的宝箱开启历史 |
| item_chest_open_logs | chest_id | 普通索引 | 加速查询特定宝箱的开启记录 |
以下是主要数据模型之间的关联关系:
用户与物品关系
物品分类层级
物品组与物品
宝箱与内容关系
物品合成关系
物品分解关系
为支持"每个用户拥有的同类物品可以有不同属性"的需求(如不同的武器有不同的属性),系统采用了统一属性物品和单独属性物品两种设计:
物品类型区分
属性存储方式
用户物品关联
系统提供以下核心接口用于物品管理:
系统支持玩家间物品交易和与NPC的交易:
系统提供以下接口用于管理单独属性物品:
物品模块支持两种过期时间机制,用于实现物品的时效性管理。
全局过期时间定义在 item_items 表的 global_expire_at 字段中,表示该物品在所有用户中的绝对过期时间。当超过这个时间点后,所有用户的该物品均失效。
global_expire_at 字段用户物品过期时间定义在 item_users 表的 expire_at 字段中,表示特定用户的特定物品的过期时间。这允许每个用户的同一物品有不同的过期时间。
default_expire_seconds 计算过期时间default_expire_seconds 为0,则物品永久有效系统定期运行任务检查并处理过期物品,确保游戏内物品的时效性。
查询全局过期物品
SELECT id FROM item_items WHERE global_expire_at IS NOT NULL AND global_expire_at < NOW()
删除用户物品记录
DELETE FROM item_users WHERE item_id IN (已过期物品ID列表)
记录日志
可选操作
查询用户过期物品
SELECT id, user_id, item_id, instance_id FROM item_users
WHERE expire_at IS NOT NULL AND expire_at < NOW()
删除用户物品记录
DELETE FROM item_users WHERE id IN (已过期记录ID列表)
处理单独属性物品
记录日志
当同时存在全局过期时间和用户物品过期时间时,系统采用以下优先级规则:
宝箱系统是游戏物品系统的重要组成部分,允许玩家通过开启宝箱获得随机物品,增加游戏的随机性和乐趣。
系统支持多种类型的宝箱,可以根据游戏需求进行配置:
宝箱的内容和概率通过 item_chest_contents 表进行配置:
item_items 表中 type=5 的特殊物品item_chest_contents 表定义宝箱可能掉落的内容宝箱系统支持一个宝箱同时掉落多个物品:
掉落数量控制:
numeric_attributes 字段中定义 min_drop_count 和 max_drop_count 属性重复控制:
allow_duplicate 字段控制物品是否可以在同一宝箱中重复掉落物品组随机:
宝箱内容的概率由 item_chest_contents 表中的 weight 字段决定:
权重定义:
概率计算公式:
内容获取概率 = 内容权重 / 宝箱内容权重总和 * 100%
多物品抽取:
为了确保玩家在多次开启宝箱后能获得稀有物品,系统实现了保底机制:
保底机制直接集成在宝箱内容配置中,具有以下特点:
内容级别保底:
pity_count字段)灵活的概率调整:
pity_weight_factor 字段控制递增概率的幅度保底计数记录:
item_pity_times 表中记录用户对每个宝箱内容的尝试次数系统支持两种保底实现方式:
确定保底:
pity_count 时,100%获得该内容递增概率:
概率增加公式:
调整后概率 = 基础概率 * (1 + (当前计数 / pity_count) * pity_weight_factor)
当计数达到 pity_count 时,概率变为100%
获取宝箱内容配置
item_chest_contents 表查询宝箱的所有内容配置pity_count > 0 的内容,这些是启用了保底的内容获取用户保底计数
item_pity_times 表查询用户对各宝箱内容的当前计数调整掉落概率
更新保底计数
系统提供以下接口用于宝箱开启:
每次宝箱开启都会记录详细日志,用于数据分析和问题排查:
在 item_chest_open_logs 表中记录以下信息:
当玩家多次获得同一物品时,系统需要根据物品类型和过期时间进行不同的处理,以确保物品管理的合理性和高效性。
对于统一属性物品(如消耗品、材料等),系统采用以下处理策略:
数量累加:
item_users 表中更新 quantity 字段最大堆叠限制:
item_items 表的 max_stack 字段堆叠创建逻辑:
// 伪代码示例
$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);
}
对于有过期时间的统一属性物品,系统按照过期时间分组存储:
过期时间匹配:
过期时间查找逻辑:
// 伪代码示例
$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
]);
}
使用优先级:
对于单独属性物品(如装备、宠物等),每次获取都创建新记录,即使是相同物品:
实例创建:
item_instances 表中创建一条记录用户关联:
item_users 表中创建记录,关联用户和物品实例quantity 字段始终为1创建流程:
// 伪代码示例
// 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
]);
单独属性物品可以通过多种方式实现属性差异化:
随机属性生成:
命名差异化:
属性继承与变异:
系统记录物品的获取来源,便于数据分析和问题排查:
在 item_transaction_logs 表中记录物品获取来源:
{
"source_type": "chest_open",
"source_id": 12345,
"details": {
"chest_id": 101,
"open_time": "2023-05-01 12:34:56",
"is_pity": false
}
}
系统支持对物品获取进行限制,防止物品过度产出:
背包容量检查:
超出处理策略:
单个物品限制:
物品组限制:
为了防止某些物品产出超过预期,影响游戏平衡,系统实现了物品产出限制机制。
系统支持多种限制类型,可以根据游戏需求灵活配置:
item_output_limits 表中设置 limit_type=1current_quantity 字段记录当前已产出数量max_quantityitem_output_limits 表中设置 limit_type=2item_user_output_counters 表中记录每个用户的获取数量item_output_limits 表中设置 limit_type=3current_quantity 字段记录当日已产出数量item_output_limits 表中设置 limit_type=4item_user_output_counters 表中记录用户当日获取数量不同类型的限制可以设置不同的重置周期,满足多样化的游戏需求:
不重置(reset_type=0):
每日重置(reset_type=1):
每周重置(reset_type=2):
每月重置(reset_type=3):
系统通过定时任务实现自动重置:
// 伪代码示例
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]);
}
// 类似地处理每周和每月重置
// ...
}
系统支持将多个相关物品关联到同一个限制规则下,防止通过不同渠道绕过限制:
item_output_limits 表的 related_items 字段中存储关联物品ID列表// related_items字段示例
{
"items": [1001, 1002, 1003],
"relation_type": "shared_limit"
}
在物品产出前,系统会执行以下检查流程,确保不超过设定的限制:
查询物品限制:
$limit = OutputLimit::where('item_id', $itemId)->first();
if (!$limit) {
// 没有限制,直接允许产出
return true;
}
检查限制类型:
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);
}
检查关联物品:
if (!empty($limit->related_items)) {
$relatedItems = json_decode($limit->related_items, true);
foreach ($relatedItems['items'] as $relatedItemId) {
// 检查关联物品的产出记录
// ...
}
}
更新计数:
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();
}
当检测到物品产出将超过限制时,系统可以采取以下处理方式:
系统提供监控和预警机制,帮助游戏运营团队及时了解物品产出情况:
当物品产出数量接近限制时(例如达到80%),系统可以发送预警通知:
if ($currentQuantity >= $maxQuantity * 0.8 && $currentQuantity < $maxQuantity * 0.9) {
// 发送80%预警
$this->sendWarning($itemId, '80%');
} elseif ($currentQuantity >= $maxQuantity * 0.9) {
// 发送90%预警
$this->sendWarning($itemId, '90%');
}
系统定期生成物品产出统计报告,包括:
物品系统的所有操作都会记录详细日志,用于数据分析、问题排查和安全审计。
系统根据不同的操作类型记录多种日志:
在 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 | 创建时间 |
在 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 | 创建时间 |
在 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 | 创建时间 |
在 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 | 创建时间 |
系统采用以下策略记录日志,确保数据完整性和系统性能:
日志记录应在数据库事务内进行,确保操作和日志的一致性:
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;
}
对于高频操作,可以使用队列实现异步日志记录,减轻数据库压力:
// 将日志记录任务推送到队列
Queue::push(new RecordItemTransactionLog($userId, $itemId, $quantity, $transactionType, $sourceType, $sourceId));
对于批量操作,可以使用批量插入优化性能:
$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);
系统提供以下接口用于查询日志:
查询示例:
$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);
查询示例:
$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);
系统日志可用于多种分析和应用场景:
物品流通分析:
用户行为分析:
物品丢失排查:
异常行为检测:
物品恢复:
用户查询:
为了管理日志数据量,系统实现了日志清理和归档机制:
实时日志:
归档日志:
统计汇总:
// 伪代码示例
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 "无法交易该物品";
}
}
GameItems模块作为游戏的核心系统之一,与其他多个系统有紧密的交互关系。
物品系统需要与用户系统交互,确保物品正确关联到用户:
// 伪代码示例
public function checkUserExists($userId)
{
$user = User::find($userId);
if (!$user) {
throw new UserNotFoundException("用户不存在: $userId");
}
return $user;
}
物品系统与任务系统交互,支持任务相关的物品操作:
// 伪代码示例
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;
}
物品系统与商店系统交互,支持物品的购买和出售:
// 伪代码示例
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
];
}
物品系统与邮件系统交互,支持通过邮件发送物品:
// 伪代码示例
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
];
}
物品系统使用事件机制与其他系统进行松耦合交互:
系统定义了以下物品相关事件:
其他系统可以监听这些事件并执行相应操作:
// 伪代码示例
// 在服务提供者中注册事件监听
public function boot()
{
Event::listen(ItemAdded::class, function ($event) {
// 更新任务进度
$this->questService->updateItemCollectionProgress(
$event->userId,
$event->itemId,
$event->quantity
);
// 更新成就进度
$this->achievementService->updateItemCollectionProgress(
$event->userId,
$event->itemId,
$event->quantity
);
});
}
物品系统提供以下API接口供其他系统调用:
// 伪代码示例
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();
}
// 伪代码示例
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);
}
为确保物品系统的高性能运行,应遵循以下最佳实践:
合理使用索引:
批量操作:
分表策略:
用户物品缓存:
物品定义缓存:
缓存失效策略:
队列处理:
定时任务:
物品系统涉及游戏经济核心,需要特别注意安全性:
操作验证:
异常监控:
日志审计:
乐观锁:
悲观锁:
分布式锁:
在实现和维护物品系统时,应注意以下事项: