武器升级系统开发文档
武器升级系统服务端+客户端实现,含升级算法、碎片扣减、武器等级更新
武器升级系统开发文档
一、方案确定
方案 B — 金币+碎片双消耗,公式 1 对数平滑成长
消耗规则
每级消耗 = 基础消耗 × 品质倍率 × 等级系数
| 品质 | 倍率 |
|---|---|
| 白(quality=1) | ×1.0 |
| 蓝(quality=2) | ×1.5 |
| 紫(quality=3) | ×2.0 |
| 金(quality=4) | ×3.0 |
碎片消耗(每级):
碎片数量 = 基础碎片 × 品质倍率 × level
- 基础碎片:取该武器对应碎片的单次合成消耗(来自 Synthesis.csv 的 quantities[0])
- 例:武器 2000(配方1)基础碎片=30;武器 2012(配方13)基础碎片=100
金币消耗(每级):
金币 = 100 × 品质倍率 × level
- 例:白品质武器 1→2 级,金币=100×1.0×2=200;金品质武器 1→2 级,金币=100×3×2=600
成长公式
属性成长值 = 原值 × (1 + k × ln(level))
其中 k = 0.3,ln 为自然对数
k=0.3 各级成长系数:
| level | ln(level) | 系数 (1+0.3×ln) |
|---|---|---|
| 1 | 0.000 | ×1.000(基准) |
| 10 | 2.303 | ×1.691 |
| 20 | 2.996 | ×1.899 |
| 30 | 3.401 | ×2.020 |
| 40 | 3.689 | ×2.107 |
| 50 | 3.912 | ×2.174 |
| 60 | 4.094 | ×2.228 |
升级属性
全局属性(每级必升):
| 属性 | 每级成长 | 60级总成长 |
|---|---|---|
| initLife | +3%×k·ln | +180%×k≈+60% |
| sprintBonus | +1%×k·ln | +60%×k≈+20% |
| coinRate | +2%×k·ln | +120%×k≈+40% |
注:60级时 initLife 实际约为基础值的 2.23 倍(+123%)
武器特定属性(仅原值>0时参与):
| 属性 | 是否参与 | 说明 |
|---|---|---|
| distBonus | 原值>0 时参与 | 距离加成 |
| comboBonus | 原值>0 时参与 | 连击加分 |
| gemRate | 原值>0 时参与 | 宝石概率 |
| weaponRate | 原值>0 时参与 | 武器概率 |
成长方式同 initLife,按比例缩放。
二、数据结构
武器等级表 WeaponLevel.csv(新建)
| 字段 | 类型 | 说明 |
|---|---|---|
| weaponId | int | 武器ID(2000-2015) |
| level | int | 当前等级(1-60) |
| exp | int | 当前经验值(预留) |
注:经验值系统暂不实现,第一版仅支持等级。
新增数据库表 character_weapon_levels
CREATE TABLE character_weapon_levels (
character_id BIGINT NOT NULL,
weapon_id INT NOT NULL,
level INT DEFAULT 1,
PRIMARY KEY (character_id, weapon_id)
);
三、Proto 消息
// 升级请求
message C2S_WeaponUpgradeRequest {
int32 weapon_id = 1; // 武器ID(2000-2015)
}
// 升级结果
message S2C_WeaponUpgradeResult {
int32 code = 1; // 0=成功,其他=错误码
string msg = 2;
int32 weapon_id = 3; // 武器ID
int32 new_level = 4; // 新等级
int32 init_life_new = 5; // 新 initLife(服务端计算后的实际值)
int32 dist_bonus_new = 6; // 新 distBonus
int32 sprint_bonus_new = 7; // 新 sprintBonus
int32 combo_bonus_new = 8; // 新 comboBonus
int32 coin_rate_new = 9; // 新 coinRate
int32 gem_rate_new = 10; // 新 gemRate
int32 weapon_rate_new = 11; // 新 weaponRate
}
// 物品变化同步(复用现有)
message S2C_InventoryChanged {
int32 currency_delta = 1;
int32 gems_delta = 2;
map<int32, int32> items_delta = 3;
}
四、错误码
| code | 说明 |
|---|---|
| 0 | 成功 |
| 1 | 角色不存在 |
| 2 | 武器不存在或未拥有 |
| 3 | 碎片不足 |
| 4 | 金币不足 |
| 5 | 已达等级上限(60) |
| 6 | 背包不存在 |
五、完整流程
客户端 服务端
| |
|--- C2S_WeaponUpgradeRequest ->|
| (weapon_id=2000) |
| | 1. 读取 character_weapon_levels(当前等级)
| | 2. 校验等级 < 60
| | 3. 读取 WeaponLevel.csv + NinjaWeapon.csv(属性)
| | 4. 计算升级消耗(碎片 + 金币)
| | 5. 验证余额(inventory.items[碎片] >= 消耗)
| | 6. 验证余额(inventory.currency >= 金币消耗)
| | 7. 扣费(碎片 -N,金币 -M)
| | 8. 计算新属性值(基础 × (1 + 0.3 × ln(newLevel)))
| | 9. 写入 character_weapon_levels(level++)
| |
|<-- S2C_WeaponUpgradeResult ---|
| (code=0, new_level=2, |
| init_life_new=...) |
| |
|<-- S2C_InventoryChanged -----|
| (currency_delta=-200, |
| items_delta={1000:-30}) |
六、服务端实现
新增文件
server/WeaponUpgradeService.cs
职责:
- 加载 NinjaWeapon.csv(内存缓存)
- 加载/管理 character_weapon_levels
- 计算升级消耗(GetUpgradeCost)
- 计算新属性(CalculateUpgradedStats)
- 验证余额(CanUpgrade)
public class WeaponUpgradeService {
// 加载数据
public void LoadTables(string tableDir);
// 计算升级消耗
public (int fragmentCost, int goldCost) GetUpgradeCost(int weaponId, int currentLevel, int quality);
// 计算新属性
public WeaponStats CalculateUpgradedStats(int weaponId, int newLevel);
// 验证是否可升级
public (bool canUpgrade, int errorCode) CanUpgrade(long characterId, int weaponId);
// 执行升级
public async Task<UpgradeResult> UpgradeAsync(long characterId, int weaponId);
}
public class WeaponStats {
public int InitLife;
public int DistBonus;
public int SprintBonus;
public int ComboBonus;
public int CoinRate;
public int GemRate;
public int WeaponRate;
}
server/WeaponUpgradeController.cs
职责:
- WebSocket 处理(C2S_WeaponUpgradeRequest → S2C_WeaponUpgradeResult + S2C_InventoryChanged)
- 调用 WeaponUpgradeService
修改文件
- WebSocketServer.cs — 注册
MsgId.C2S_WeaponUpgradeRequest - Program.cs — DI 注册 WeaponUpgradeService
七、客户端实现
WebSocketService.cs
public event Action<S2C_WeaponUpgradeResult> OnWeaponUpgradeResult;
case MsgId.S2C_WeaponUpgradeResult:
var upResult = _decoder.DecodeS2C_WeaponUpgradeResult(pkg.Data);
MainThreadDispatcher.Invoke(() => OnWeaponUpgradeResult?.Invoke(upResult));
break;
public void RequestWeaponUpgrade(int weaponId) {
var req = new C2S_WeaponUpgradeRequest { WeaponId = weaponId };
Send(MsgId.C2S_WeaponUpgradeRequest, req);
}
InventoryService.cs / WeaponManager.cs
- 存储各武器当前等级(从 S2C_WeaponUpgradeResult 同步)
- UI 显示等级和属性变化
八、注意事项
- 等级存储:character_weapon_levels 每角色每武器独立记录,未解锁的武器默认等级 1
- 属性计算时机:升级后属性由服务端计算并返回,客户端不做演算
- 原子性:扣费(碎片+金币)和写入等级在同一事务或连续 DB 操作中完成
- 品质倍率:从 NinjaWeapon.csv quality 列读取,白=1/蓝=2/紫=3/金=4,再映射为倍率
- 基础碎片数量:从 Synthesis.csv 的 quantities[0] 读取(无需解析完整多材料格式)
九、已完成清单
- [x] 更新 Ninjiafall1Proto.proto(添加 C2S_WeaponUpgradeRequest / S2C_WeaponUpgradeResult)
- [x] 重新编译 protobuf(build.sh)
- [x] 服务端:WeaponUpgradeService.cs — CSV加载、消耗计算(ln平滑成长,k=0.3,MaxLevel=60)、属性计算
- [x] 服务端:WeaponUpgradeController.cs(WebSocket)— C2S_WeaponUpgradeRequest → S2C_WeaponUpgradeResult + S2C_InventoryChanged
- [x] 服务端:注册 MsgId.C2S_WeaponUpgradeRequest 到 WebSocketServer.cs
- [x] 服务端:Program.cs — DI 注册 WeaponUpgradeService
- [ ] 数据库:character_weapon_levels 表(建表语句已确认在 WeaponUpgradeService.cs 中)
- [x] 客户端:ProtobufTypes.cs — 添加 Protobuf 消息(C2S_WeaponUpgradeRequest / S2C_WeaponUpgradeResult,11字段)
- [x] 客户端:ProtobufCodec.cs — 编解码支持
- [x] 客户端:WebSocketService.cs — OnWeaponUpgradeResult + RequestWeaponUpgrade(int weaponId)
- [ ] 测试:碎片不足 / 金币不足 / 正常升级 / 等级上限判断(待联调)
所有内容仅供学习与交流,转载须标明链接。未经同意,禁止作为商业用途,有特殊需求请联系站长。
