Skip to main content

肉鸽词条选择、英雄归属与池次数

本页于 2026-10-15 整合现有功能稿、飞书内容及客户端 origin/develop@7f0636c9 的功能资料。原 draft/unknown 不因整理自动变为批准;设计规则、源配置与运行结果分别说明。本次只做文档及源码审阅,没有执行游戏功能测试。

入口与边界​

该功能是宝箱/玩法配置触发的Buff三选一,与每波手牌5选3分别建池。候选按权重抽取,英雄归属限制有效英雄,次数耗尽移出;选择期间暂停刷怪、怪物、普攻与手动技能,完成恢复。怪物可按掉落配置触发宝箱选择,出现次数与权重由配置决定,不默认所有词条有前置链。

池、配置与实现​

概念速查​

概念来源说明
词条 (Buff/Perk)TbBattleEffectSelectconfig即肉鸽 Buff,Id = Perk 资源 Id。分 DefaultAttr / HeroAttr / HeroLevelAttr 三种类型
三选一池 (_pool)BuffSelectServiceList<BuffInfo>,每个条目有 SelectNum(可被选次数)和 Weight(权重)
携带宝箱 BUFFTbHeroconfig.BattleEffectIDs英雄表上的 int[],定义该英雄"能刷出哪些词条"。用于构建白名单过滤池
英雄归属TbBattleEffectSelectconfig.OwnershipHero0=通用(全员施加),>0=仅该 roleId 英雄
AttrType.DefaultAttrAttrType 枚举默认词条,池初始化时加载
AttrType.HeroAttrAttrType 枚举英雄携带词条,池初始化时与 DefaultAttr 一起加载(按白名单过滤)
AttrType.HeroLevelAttrAttrType 枚举等级解锁词条,不在初始化时加载,由 GetNewAttr 注入
InitialGetAttrTbHeroLevelAttrconfig 多态字段战斗开始直接获得词条 + 占用一次池子可选次数
GetNewAttrTbHeroLevelAttrconfig 多态字段战斗开始将词条注入三选一池(增加一个可选项)
OverloadUnlockTbHeroLevelAttrconfig 多态字段超载解锁标记,不影响池,由 ComHeroSpellItem 运行时检查
Mulberry32BuffSelectService._rng32可序列化 32 位 PRNG,支持快照恢复后续抽

核心状态​

_pool List<BuffInfo> 三选一池(Id/Weight/SelectNum/OwnershipHero)
_selectedHeroIds HashSet<int> 当前出战英雄 roleId
_battleDeployedHeroEcsUniqueIds HashSet<int> 场上英雄 ECS UniqueId(通用词条全员施加用)
_accumulatedChosenBuffIds List<int> 本局已选词条 Id(按选择顺序),写入快照
_rng32 / _rngBound uint / bool Mulberry32 状态
_preloadCts CancellationTokenSource 预加载取消令牌

两条初始化路径​

路径 1:正常开局​

LevelBattleInitState.RunQueueBootstrapAsync()
│
├─ 1. InitializeBattlePool(heroesWithLevel) ← 先建池
│ ├─ 汇总白名单(各英雄 BattleEffectIDs)
│ ├─ 遍历 TbBattleEffectSelectconfig:DefaultAttr / HeroAttr + 白名单过滤 → _pool
│ ├─ RaiseBuffSelectChanged([])
│ └─ StartPreloadPoolPerkConfigs() (异步)
│
├─ 2. 循环 CreateHeroEntityAsync(applyMilestonePerks: true)
│ └─ ApplyHeroMilestonePerks(uid, roleId, level):
│ ├─ InitialGetAttr → ConsumeBuffSelectNum + ApplyPerkToActor
│ └─ GetNewAttr → EnsureBuffInPool (注入池)
│
└─ 3. (不再发 EvtRebuildBuffPool)

关键时序:池在英雄创建之前建好,确保 ApplyHeroMilestonePerks 中的 ConsumeBuffSelectNum 能找到目标词条。

路径 2:当前快照恢复​

当前快照v8在创建英雄时使用 DeferToSnapshot 避免重复授予开局词条。恢复保存的池子、已选词条和RNG状态,再按快照登记新英雄实体并恢复词条;不同时重跑开局里程碑和历史选择列表来重复叠加。准确调用顺序及角色/区域/手牌恢复见战斗续战。旧技术README逐条重放历史Buff的流程不能继续作为当前恢复契约。

池子生命周期​

InitializeBattlePool → 清空 + 重建 (正常开局)
RebuildBattlePoolFromHeroRoleIds → 清空 + 重建 (EvtRebuildBuffPool, 战斗中追加)
RestoreFromBattleSnapshot → 清空 + 从快照恢复 (重登)
ConsumeChosenBuff → SelectNum -= 1, 到 0 移除
ConsumeBuffSelectNum → SelectNum -= 1, 到 0 移除 (不施加 Perk)
EnsureBuffInPool → 已存在则 SelectNum += count, 否则新建

Perk 施加路径​

用户选择宝箱 Buff
→ ConsumeChosenBuff → ApplyChosenBuffPerk
└─ ApplyPerkToActorSafe: TryGetPerkDefinition → AddPerkToActor
未注册 → LoadPerkConfig 异步 → AddPerkToActor

开局 InitialGetAttr
→ ApplyHeroMilestonePerks → ApplyPerkToActor (CharacterFacade)
└─ TryGetPerkDefinition → AddPerkToActor
未注册 → TryLoadAndApplyPerkAsync.Forget()

重登恢复
→ 按快照v8恢复角色词条;不额外重复授予历史Buff
└─ 同上 ApplyPerkToActorSafe 路径

两条路径都有 Perk 未注册时的异步 fallback,不会静默丢失。

快照序列化​

保存: BattleSnapshotCollector.Collect → CopyBuffSelectStateTo()
写入: _rng32 / _selectedHeroIds(排序) / _pool(Id+SelectNum) / _accumulatedChosenBuffIds

恢复: BattleSnapshotWorldApplier.TryApply → RestoreFromBattleSnapshot()
读取: 同上四字段
注意: BuffSelectResumeBlockPresent 为 true 时才恢复(旧快照无此块)

关键数据表​

表关键字段用途
TbBattleEffectSelectconfigId, AttrType, Weight, SelectNum, OwnershipHero词条定义,池数据源
TbHeroconfigBattleEffectIDs (int[])英雄携带宝箱 BUFF,构建白名单
TbHeroLevelAttrconfigRoleID, Level, HeroLevelAttrTypeData (多态)英雄等级里程碑
TbHeroLevelAttrEXGetMilestonesUpToLevel(heroId, level)查询指定等级以下所有里程碑

当前恢复边界​

技术README保存了早期初始化说明;当前战斗快照v8通过DeferToSnapshot避免英雄预注册词条重复授予,最终恢复顺序以战斗续战为准。OverloadUnlock名称属于旧词条定义,当前Lv.4偏向以进化表的OverloadReplaceSkill决定;不得把旧英雄配置字段的存在解释为能量或赌超载恢复启用。

来源​

客户端Gameplay/Features/RoguelikeUpgrade/README.md、Docs/战斗读档逻辑梳理.md;飞书关卡流程的Buff池、次数、暂停要求;旧combat-skill/battle-mission。