844 lines
46 KiB
Markdown
844 lines
46 KiB
Markdown
# HEROLL 本地 H5 玩法验证 GDD
|
||
|
||
> **仅限本机内部验证、未取得发行授权、不得公开或分发。**
|
||
>
|
||
> 本文档只描述 **HEROLL**。`art-bible/`、`design/gameplay-monetization/`、`outputs/iaa/` 以及任何 Dice Lords 资料均为非规范历史资料,不得作为本验证版的设计、数值或美术依据。
|
||
>
|
||
> 复用提取的 HEROLL 图像、动画、音效与文本并不消除版权风险。数字化复制仍受著作权法保护,合理使用例外具有条件限制;本包不得用于发行、公开演示或向第三方分发。[《中华人民共和国著作权法》](https://www.npc.gov.cn/c2/c30834/202011/t20201119_308796.html)
|
||
|
||
文档版本:1.1-RC(关卡刷新逆向纠偏)
|
||
目标引擎:Cocos Creator 3.x / TypeScript 5.x
|
||
目标平台:本地浏览器、竖屏 H5、简体中文
|
||
权威目录:`design/gameplay-validation/`
|
||
配置索引:[specs/README.md](./specs/README.md)
|
||
|
||
关卡楼层、增益地块与 Boss 压力的权威逆向覆盖层:[HEROLL 2.0.1 关卡楼层分布与刷新规则逆向报告](../../analysis/stage-floor-rules/2026-08-31_逆向-HEROLL关卡楼层分布与刷新规则-report.md)。该报告优先于本文件旧版本中的“每层固定 2 个有益地块”“第 1 关运行 Boss Gauge”“六类事件保证袋”等验证版假设。
|
||
|
||
## 1. 文档状态与签收边界
|
||
|
||
本 GDD 可直接用于搭建完整的 10 层玩法验证包。产品循环、界面、数据结构、装备、祝福、药水、商店、事件、存档和 QA 契约均已锁定;当前构建不开放广告或即时增强入口。
|
||
|
||
战斗公式采用两层契约:
|
||
|
||
1. **验证实现公式**:本文 §5.4 给出的确定性公式,来源标记为“验证版新增”,开发可立即实现。
|
||
2. **HEROLL 拟合公式**:必须用无局外加成的实机样本对验证公式校正;伤害中位误差 ≤5%,战斗时长误差 ≤10% 后,才可把 `formula-fit.csv` 的门禁改为 `PASS`。在此之前不得宣称“原作公式已还原”。
|
||
|
||
当前桌面实机已确认棋盘、宝箱、铁匠铺、自动战斗与局内点数表现,但测试账号带有局外属性,不能作为裸装公式样本。最小补录矩阵已经写入 [formula-fit.csv](./specs/formula-fit.csv),这是拟合签收所需的全部输入,不是隐藏的设计项。
|
||
|
||
### 1.1 规范优先级
|
||
|
||
发生冲突时按以下顺序执行:
|
||
|
||
1. 本 GDD、上方链接的专项逆向覆盖层与同目录 `specs/*.csv`;专项报告在其命名领域内优先。
|
||
2. HEROLL 提取源码表与 IL2CPP 元数据。
|
||
3. 无局外加成的 HEROLL 实机黑盒结果。
|
||
4. 用户提供的视频、截图说明和当前桌面试玩观察。
|
||
5. 其他历史文档;仅作检索线索,不具规范效力。
|
||
|
||
同一优先级内,CSV 数值优先于正文示例。所有新增规则必须登记来源标签。
|
||
|
||
### 1.2 来源标签
|
||
|
||
| 标签 | 含义 | 可否直接实现 |
|
||
|---|---|---|
|
||
| `[源码确认]` | 来自提取 CSV、元数据字段或可复现调用边界 | 是 |
|
||
| `[实机观察]` | 从 HEROLL 视频、截图或桌面运行直接观察 | 是;仅限观察到的表现 |
|
||
| `[黑盒拟合]` | 由受控样本拟合且通过误差门槛 | 是 |
|
||
| `[验证版新增]` | 为本地玩法验证明确新增或裁剪 | 是;不得冒充原作 |
|
||
|
||
完整 Evidence → Finding → Design 映射见 [evidence-map.csv](./specs/evidence-map.csv)。
|
||
|
||
## 2. 产品目标与范围
|
||
|
||
### 2.1 一句话定义
|
||
|
||
玩家手动投掷两枚公平 D6,在不可控移动中利用装备、祝福、药水、商店和事件构筑解决方案,连续完成 10 层并击败 `boss_0`;单局目标 10–12 分钟。
|
||
|
||
### 2.2 验证假设
|
||
|
||
| 假设 | 成功信号 | 失败信号 |
|
||
|---|---|---|
|
||
| 公平双骰能制造可读的局势变化 | 玩家接受坏落点并转向构筑补救 | 玩家认为结果被暗改或无法理解 |
|
||
| 6–8 次装备查看足以形成构筑方向 | 第 6 层前出现攻击、攻速/暴击或生存路线 | 装备选择只看品质,不看词条 |
|
||
| 10 层能形成完整起承转合 | 前 3 层学习、中 4 层成型、后 3 层压力、Boss 验收 | 过程重复或 Boss 与构筑无关 |
|
||
| 固定棋盘和原作地块图标能让移动与落点一眼可读 | 玩家能预判顺时针路径并辨认战斗、商店和事件格 | 玩家误以为棋盘旋转,或只能靠颜色猜地块 |
|
||
|
||
### 2.3 包含内容
|
||
|
||
- 一个 Grass 主题、Normal、`stage=0` 的 10 层关卡。
|
||
- 两枚公平D6、40个独立实际地块、逐层推进和Boss。
|
||
- 自动战斗、手动药水、四类普通敌人与 `boss_0`。
|
||
- 40 件局内装备、13 类装备属性、五档品质。
|
||
- 31 个启用源码祝福和 1 个“第三骰替换”祝福。
|
||
- 药水容量、治疗等级与 9 个启用药水强化。
|
||
- 商人、97 条商品、12 种可安装事件地块。
|
||
- 固定棋盘、顺时针玩家移动,以及战斗、商店和 12 类安装事件的原作图标。
|
||
- 本地安全点存档、确定性随机、隐藏调试台。
|
||
- HUD、棋盘、战斗、构筑、事件、结算与功能音效。
|
||
|
||
### 2.4 明确排除
|
||
|
||
- 账号、登录、服务器、联网依赖和云存档。
|
||
- 局外成长、英雄养成、宠物、坐骑、遗物、任务、成就、AFK。
|
||
- 体力、抽卡、钻石、IAP、订阅、通行证、真实广告 SDK、插屏、横幅,以及任何广告/即时增强/广告复活按钮。
|
||
- 剧情、对白、开场、结局、背景音乐。
|
||
- 完整 IAA GDD 与 MVP GDD;待本包完成 10 局现场签收后另行抽象。
|
||
|
||
## 3. 单局结构
|
||
|
||
### 3.1 开局配置
|
||
|
||
| 配置 | 值 | 来源 |
|
||
|---|---:|---|
|
||
| `stage` / `mode` | `0` / `Normal` | `[源码确认]` |
|
||
| 场景主题 | `Grass` | `[源码确认]` |
|
||
| 层数 | 10 | `[源码确认] stageinfo.bossSpawnRuleValue` |
|
||
| Boss | `boss_0` / `Board_Boss001` | `[源码确认]` |
|
||
| Boss 技能 | `skill_0` | `[源码确认]` |
|
||
| Boss 攻击冷却 | 7 秒 | `[源码确认]` |
|
||
| 玩家装备 | 四槽全空 | `[验证版新增]` |
|
||
| 玩家丹药 | 容量 1、基础恢复 50 HP | `[native 公式确认] PotionController.GetPotionFinalHeal` |
|
||
| 初始局内点数 | 0 | `[验证版新增]` |
|
||
| 普通入口速度 | 固定 1× | `[验证版新增]` |
|
||
|
||
### 3.2 核心循环
|
||
|
||
1. 创建随机 `runSeed`,初始化四条 RNG 流与空构筑。
|
||
2. 运行原作初始化链:初始地块、全部角落地块、玩家有益地块牌组 8 次抽取,进入 `AwaitRoll`。
|
||
3. 玩家手动掷两枚 D6;若持有第三骰祝福,再选择替换 A、替换 B或放弃。
|
||
4. 棋盘保持固定;玩家按最终点数从底部起点沿左侧、顶部、右侧顺时针逐格移动。每1点对应1个实际地块,只在最终落点结算。
|
||
5. 仅当 `bossSpawnRule=DiceGauge` 时推进 Boss 压力循环;本验证关 `stage=0` 为 `Default`,不运行压力 Gauge。
|
||
6. 原子结算落点:战斗、装备、祝福、商店、事件或空地。
|
||
7. 完整结算后写安全点,返回掷骰。
|
||
8. 越过起点完成一圈:层数 +1、恢复 30% 最大生命,并按 `AddRandomGoodTiles(loopIndex+2)` 从玩家牌组执行安装尝试,再按 `enemyTilesPerLoop` 增加敌人。
|
||
9. 第 10 层结束后进入 `boss_0`;胜利进入结算,死亡且无可用复活则失败。
|
||
|
||
### 3.3 局状态机
|
||
|
||
```mermaid
|
||
stateDiagram-v2
|
||
[*] --> RunInit
|
||
RunInit --> FloorSetup
|
||
FloorSetup --> AwaitRoll
|
||
AwaitRoll --> DiceResolve: 手动掷骰
|
||
DiceResolve --> ThirdDieChoice: 持有第三骰祝福
|
||
DiceResolve --> Moving: 无第三骰
|
||
ThirdDieChoice --> Moving: 替换A/替换B/放弃
|
||
Moving --> BossGaugeResolve: DiceGauge关卡行动完成
|
||
Moving --> TileResolve: Default关卡行动完成
|
||
BossGaugeResolve --> BossBoardAction: Gauge 满值
|
||
BossGaugeResolve --> TileResolve: Gauge 未满
|
||
BossBoardAction --> TileResolve: Boss 行动演出完成
|
||
TileResolve --> Battle
|
||
TileResolve --> Event
|
||
TileResolve --> Shop
|
||
TileResolve --> RewardChoice
|
||
Battle --> ReviveResolve: 玩家生命归零
|
||
ReviveResolve --> Battle: 源码复生祝福成功
|
||
ReviveResolve --> RunFailed: 祝福不可用或结算后仍死亡
|
||
Battle --> Checkpoint: 胜利
|
||
Event --> Checkpoint
|
||
Shop --> Checkpoint
|
||
RewardChoice --> Checkpoint
|
||
Checkpoint --> FloorTransition: 越过起点
|
||
Checkpoint --> AwaitRoll: 未过层
|
||
FloorTransition --> BossBattle: 完成第10层
|
||
FloorTransition --> FloorSetup: 继续
|
||
BossBattle --> RunClear: 胜利
|
||
BossBattle --> ReviveResolve: 死亡
|
||
RunClear --> [*]
|
||
RunFailed --> [*]
|
||
```
|
||
|
||
## 4. 骰子、棋盘与层推进
|
||
|
||
### 4.1 公平骰子
|
||
|
||
- `dieA` 与 `dieB` 分别调用 `diceRng.nextInt(1, 6)`,互相独立。
|
||
- 36 个有序结果严格等权;移动值为 `dieA + dieB`。
|
||
- 对子触发“机缘”,但不得改变基础骰面分布:随机令本局伤害、最大生命或防御之一永久 +10%,可叠加;并令下一次落入战斗格时开场造成 100% 当前攻击的先攻伤害。下一落点不是战斗格则只清除先攻,永久属性仍保留。
|
||
- 禁止暗改、保底、重掷、锁骰、提高期望值或根据 DDA 调整骰面。
|
||
- 每次掷骰记录 `rollIndex`、两枚原始结果、第三骰结果、最终结果与 RNG 索引。
|
||
- 同一 `runSeed + RngStreamState + 输入序列` 必须得到相同结果。
|
||
|
||
第三骰祝福的顺序固定为:先生成 A、B → 再独立生成 C → 弹出三按钮 → 选择替换 → 计算移动值。放弃 C 不回滚 RNG。
|
||
|
||
### 4.2 40格实际棋盘
|
||
|
||
- 棋盘固定40个独立实际落点:`Start=0`、`Tile01–Tile39`,不存在只用于表现但不参与移动的装饰格。
|
||
- 棋盘本体和镜头不因玩家位置旋转。底部为`Start`,索引递增方向固定为底部→左侧→顶部→右侧,即屏幕顺时针。
|
||
- 玩家位置用`tileIndex: 0..39`表示;每1点执行一次`(index + 1) % 40`。例如从起点ROLL 10,最终停在`Tile10`,移动动画必须恰好播放10次逐格位移。
|
||
- 从39移动到0即完成一圈;每层只完成一圈,因此一次骰子最多触发一次过层。
|
||
- 每个功能格必须使用HEROLL原作像素图标:普通战斗格使用`tile_battle`和`icon_NormalEnemy_1..4`;图标骷髅数量严格等于实际敌人数;商店使用`icon_start_shop`;12类事件使用对应`Installtile_*`。
|
||
- 事件、商店、普通战斗或小Boss完整结算后,该格立即变为`EMPTY/荒地`。荒地跨层原位保留,只有Boss污染或玩家新增地块可以覆盖;禁止下一层整盘重新生成。
|
||
- 过层有益地块按 `loopIndex+2` 执行元数据抽取/安装尝试;尝试数不等于保证可见新增数。当前静态证据不能证明“只能覆盖荒地”这一完整优先级,原作实现必须记录候选与失败原因,禁止把旧验证版保护表冒充源码规则。
|
||
|
||
| 地块 | 数据 | 停留效果 | 结算后 |
|
||
|---|---|---|---|
|
||
| 起点 | `START` | 越过时升层并恢复30%最大生命 | 永久保留 |
|
||
| 荒地 | `EMPTY` | 无效果 | 跨层保留 |
|
||
| 1–4怪物 | `BATTLE.enemyCount=1..4` | 进入对应数量的自动战斗 | 胜利后变荒地 |
|
||
| 小Boss | `MINIBOSS.enemyCount=1..4` | 怪物数按60%/25%/10%/5%生成,敌方攻击×1.12、生命×1.18 | 胜利后变荒地 |
|
||
| 污染Boss | `INFECTED_BOSS` | 同小Boss战;若过层时仍未消灭,则普通怪物格污染+1 | 胜利后变荒地;未消灭最多生效3次 |
|
||
| 商店 | `MERCHANT` | 3固定丹药商品+3随机商品 | 离开后变荒地 |
|
||
| 12类事件 | `EVENT` | 按§9和`events.csv`结算 | 完整结算或主动离开后变荒地 |
|
||
|
||
### 4.3 Boss Gauge / 倒计时循环(仅 `DiceGauge` 关卡)
|
||
|
||
`stage=0` 的规则为 `Default=10`,本节在第 1 关普通验证流程中完全禁用;它仅用于复现后续 `DiceGauge` 关卡。
|
||
|
||
- `BUILDUP`:新局从 `0/2` 开始。玩家每完成一次 ROLL 计 1 轮;第一次显示 `1/2`,第二次结束后不立即执行 Boss 行动,而是进入倒计时。
|
||
- `COUNTDOWN`:从 `4` 开始;玩家之后每完成一次 ROLL 依次显示 `3、2、1、0`。第 4 次 ROLL 的移动演出结束后,Boss 才执行行动。
|
||
- `BOSS ACTION`:从符合当前候选谓词的非祝福地块中无放回选择 4 个,逐个转换为怪物战斗地块;转换完成后回到 `BUILDUP 0/2`,持续循环。
|
||
- 祝福地块是实机确认的排除项。起点及其他模式专属保护项、合法候选不足 4 个时的降级策略尚未由静态代码闭环;实现时必须配置化并记录日志,不得标为源码确认。
|
||
- 该机制只负责棋盘回合节拍,**不是**“玩家攻击 Boss 若干次后 Boss 才攻击一次”。进入战斗后,双方仍按 §5 的攻击间隔与技能冷却独立行动。
|
||
- 表现顺序固定为:骰子演出 → 逐格移动/落地 → Boss 计数变化 → 满倒计时警示/棋盘震动 → 4 格逐个感染 → 才允许落点事件或战斗界面出现。所有环节均无跳过按钮,禁止直接硬切。
|
||
- 逆向层仅确认 `DiceGauge=50` 是原作增量、实时值范围 0..100 且 `MoveTile` 调用 `TrySpawnBossTilesByGauge`;本节的 `2 + 4 + 转换 4 格` 时序采用负责人实机确认,优先于旧验证版的“+50 后立即行动”解释。
|
||
|
||
污染Boss跨层规则:若棋盘上仍有`INFECTED_BOSS`且剩余次数大于0,则进入新层时所有普通`BATTLE`地块的`enemyCount +1`(上限4)、`pollutionLevel +1`(上限3),然后该Boss剩余次数减1。污染只改变普通怪物地块,不改变骰面、事件、小Boss或最终Boss。
|
||
|
||
### 4.4 十层节奏
|
||
|
||
逐层敌方基础数值见 [floor-balance.csv](./specs/floor-balance.csv)。层号 UI 使用 1–10,源码 `loop` 使用 0–9。
|
||
|
||
| 层段 | 设计目的 | 强制内容 |
|
||
|---|---|---|
|
||
| 1–2 | 学会掷骰、战斗、装备比较 | 首次掷骰与首次换装轻引导 |
|
||
| 3 | 第一次经济决策 | 商人出现 |
|
||
| 4–5 | 形成第一条构筑方向 | 至少已安装 3 种核心事件 |
|
||
| 6 | 中盘修正 | 商人出现、敌人明显增强 |
|
||
| 7–8 | 检查生存与伤害平衡 | 保证六类核心事件已全部生成 |
|
||
| 9 | Boss 前最后调整 | 商人出现 |
|
||
| 10 | 构筑压力测试 | 完成后立即进入 Boss |
|
||
|
||
每次过层按以下原子顺序执行:
|
||
|
||
1. 结算当前地块并写 `beforeFloorTransition` 安全点。
|
||
2. `floor += 1`。
|
||
3. `hp = min(maxHp, hp + round(maxHp * 0.30))`。
|
||
4. 从玩家当前有益地块洗牌牌组抽取 `loopIndex+2` 次并逐次尝试安装;同一牌组轮内无放回,耗尽后重洗。
|
||
5. 应用 DDA 层状态并写 `afterFloorTransition` 安全点。
|
||
6. 第 10 层已结束则进入 Boss,否则恢复掷骰输入。
|
||
|
||
## 5. 角色、敌人与战斗
|
||
|
||
### 5.1 玩家裸装属性
|
||
|
||
| 属性 | 初始值 | 语义 |
|
||
|---|---:|---|
|
||
| `range` | 5 | 目标搜索距离 |
|
||
| `moveSpeed` | 40 | 角色移动表现值 |
|
||
| `attackPower` | 10 | 基础攻击 |
|
||
| `attackSpeed` | 100 | 100 表示每秒 1 次基础攻击 |
|
||
| `health/maxHealth` | 150 | 当前/最大生命 |
|
||
| `defense` | 0 | 减伤参数 |
|
||
| `lifeSteal` | 0 | 伤害吸血百分比 |
|
||
| `evasion` | 0 | 闪避参数 |
|
||
| `aim` | 5 | 命中参数 |
|
||
|
||
以上全部为 `[源码确认] playerdefaultstat.csv`。验证版不得读取账号、装备、宠物、遗物或其他局外属性。
|
||
|
||
### 5.2 敌人角色
|
||
|
||
| 角色 | `EAttackType` | 行为规则 | 可读性要求 |
|
||
|---|---|---|---|
|
||
| 剑士 | `SWORD` | 接近后近战攻击;作为基础压力 | 近战挥砍与命中帧明确 |
|
||
| 直射 | `STRAIGHT` | 保持射程,直线弹道命中 | 弹道方向与命中点一致 |
|
||
| 抛射 | `CURVED` | 高伤低频弧线弹道 | 地面落点预告不少于 300ms |
|
||
| 冲锋 | `RUSH` | 短前摇后直线突进 | 前摇、冲锋线、碰撞结束分离 |
|
||
|
||
`group_10` 源码角色配额为 `SWORD,SWORD,STRAIGHT,STRAIGHT`;为了完整验证四种敌人,本验证版在每层按 `enemyRng` 从四种角色中生成 2 个,保证十层内每种至少出现两次。这个角色覆盖规则为 `[验证版新增]`,敌人属性仍逐层读取源码 `board.csv`。
|
||
|
||
同角色的 prefab 变体及倍率来自 `monsterinfo.csv`。普通敌人生成后属性为:
|
||
|
||
```text
|
||
attack = floor.attackPower × prefab.attackMultiplier × dda.attackMultiplier
|
||
maxHp = floor.health × prefab.healthMultiplier × dda.healthMultiplier
|
||
defense = floor.defense × prefab.defenseMultiplier
|
||
attackInterval = floor.attackSpeed × prefab.attackSpeedMultiplier
|
||
evasion = floor.evasion × prefab.evasionMultiplier
|
||
aim = floor.aim × prefab.aimMultiplier
|
||
lifeSteal = floor.lifeSteal × prefab.lifeStealMultiplier
|
||
```
|
||
|
||
`boss_0` 使用第 10 层基础值和源码倍率:攻击 ×2、生命 ×4、防御 ×1、攻速间隔 ×1、闪避 ×1、命中 ×1、吸血 ×0;技能为 `skill_0`,主动技能冷却 7 秒。
|
||
|
||
### 5.3 战斗控制与阶段
|
||
|
||
战斗固定为 `Prepare → Spawn → PreBattle → Fight → PostBattle`:
|
||
|
||
- `Prepare`:冻结棋盘输入,创建 `BattleSnapshot`,加载敌人与背景。
|
||
- `Spawn`:按角色槽位生成实体;逻辑已生成,表现可跳过。
|
||
- `PreBattle`:结算开战祝福、护盾与先手效果。
|
||
- `Fight`:双方自动索敌、移动、普攻和技能。玩家唯一主动输入是药水。
|
||
- `PostBattle`:停止计时器,按一次事务生成掉落、祝福触发和点数,再写安全点。
|
||
|
||
普通入口只显示 1×速度;隐藏调试台可切换 1×/4×。暂停只冻结逻辑时钟,不改变 RNG。
|
||
|
||
### 5.4 验证实现公式与拟合门禁
|
||
|
||
以下公式是可立即编码的 `[验证版新增]` 基线,绝不标记为 HEROLL 原作还原:
|
||
|
||
```text
|
||
hitChance = clamp(0.05, 0.95, (100 + attacker.aim - defender.evasion) / 100)
|
||
isHit = combatRng.nextFloat01() < hitChance
|
||
variance = combatRng.uniform(0.85, 1.15) // EnemyDamgeRange = 15
|
||
mitigation = 100 / (100 + max(0, defender.defense))
|
||
normalDamage = max(1, round(attacker.attackPower × mitigation × variance))
|
||
criticalDamage = round(normalDamage × 1.50) // BaseCritical = 150
|
||
criticalRoll = combatRng.nextInt(0, 100) // 含 0 与 100,共 101 个整数
|
||
critical = criticalRate > criticalRoll
|
||
totalDamage = round((critical ? criticalDamage : normalDamage) × (1 + totalDamagePct/100))
|
||
finalDamage = max(1, totalDamage + trueDamage)
|
||
lifeStealHeal = floor(finalDamage × lifeStealPct / 100)
|
||
playerAttackInterval = max(0.15, 1 / (attackSpeed / 100))
|
||
enemyAttackInterval = max(0.15, boardAttackSpeed × prefabAttackSpeedMultiplier)
|
||
```
|
||
|
||
- 暴击判定恢复为 `criticalRate > RandomInt[0,100]`;不再额外钳制为 5%–95%,裸装为 0。
|
||
- 闪避发生在暴击前;未命中不触发吸血、反击和命中祝福。
|
||
- 玩家面板保留“反击”属性;玩家受击后按反击率独立判定,成功则立即对该攻击者造成一次当前攻击力伤害。
|
||
- 护盾先扣,生命后扣;真伤仍受命中但无视防御。
|
||
- 同一逻辑 tick 内的伤害按 `sourceEntityId` 排序,避免帧率改变结果。
|
||
- 生命、护盾和点数均为整数;防御、攻速和百分比保留 2 位小数。
|
||
|
||
拟合时只允许修改 [formula-fit.csv](./specs/formula-fit.csv) 中声明的函数和常量,不得改 `board.csv` 行值。门禁统计使用至少 30 次普攻命中样本和 10 场完整战斗:
|
||
|
||
- 每类伤害样本的预测/实测中位绝对百分比误差 ≤5%。
|
||
- 每个层段战斗时长中位绝对百分比误差 ≤10%。
|
||
- 命中、闪避、暴击和吸血的 Wilson 95% 区间必须覆盖预测值。
|
||
- 若不通过,`build.releaseCandidate=false`,但可继续内部原型调试。
|
||
|
||
### 5.5 动态难度
|
||
|
||
源码确认参数为:`dynamicDifficultyAbleStage=0..6`、条件 `5,8,12`、值 `10,15,20,5,5,5`,另有 `normalEnemyBattleTimeStatUp=13,3,100,5,1`。方法体未恢复,当前不得擅自解释各数组下标。
|
||
|
||
为了让裸装初始配置稳定学会循环,当前 DEMO 对普通敌人增加 `[验证版新增]` 的前期容错:1/2/3 层攻击倍率为 `0.72/0.76/0.80`、生命倍率为 `0.88/0.90/0.92`;4–5 层为 `0.90/0.96`;第 6 层起恢复 `1.0/1.0`,Boss 始终为 `1.0/1.0`。该倍率不修改配置表原值,且在战斗快照中可复现。
|
||
|
||
实现必须:
|
||
|
||
- 保留 `DdaState`、配置原值、每次状态迁移原因和应用前后倍率。
|
||
- DDA 只可改变敌人战斗属性,禁止改变骰面、商店、掉落品质和事件生成。
|
||
- 威胁倍率限制在 `0.78..1.05`,前三层上限固定为 `1.0`;最近生命均值低于 `0.45` 时下降 `0.12`,低于 `0.70` 或战斗超过 11 秒时下降 `0.06`,高熟练表现最多上升 `0.02`。
|
||
- 黑盒签收后只替换 `DdaEvaluator`,不得改变存档结构和日志字段。
|
||
|
||
这条门禁确保没有用猜测公式冒充源码结果。
|
||
|
||
### 5.6 死亡与复活
|
||
|
||
1. 玩家生命归零时冻结战斗逻辑并创建死亡快照。
|
||
2. 若持有可用的源码 `Revive` 祝福,先按祝福规则结算并消耗对应次数。
|
||
3. 若祝福不可用或结算后生命仍为 0,播放完整 `DEFEAT` 演出后直接进入失败结算。
|
||
4. 当前 DEMO 不渲染广告复活、放弃广告或任何伪广告按钮;历史 `instantRevive`/事务字段只用于旧存档兼容,不可从普通 UI 触发。
|
||
|
||
## 6. 局内装备
|
||
|
||
### 6.1 目录、槽位与品质
|
||
|
||
四个槽位为武器、戒指、护手、鞋子;开局为空。完整 40 件目录见 [gear-catalog.csv](./specs/gear-catalog.csv),装备属性定义见 [gear-status.csv](./specs/gear-status.csv)。
|
||
|
||
| 品质 | rank | 权重 | 颜色 | 分解点数 |
|
||
|---|---:|---:|---|---:|
|
||
| 普通 | 0 | 210 | 灰白 | 1 |
|
||
| 稀有 | 1 | 60 | 蓝 | 2 |
|
||
| 英雄 | 2 | 28 | 紫 | 4 |
|
||
| 传奇 | 3 | 4 | 金 | 8 |
|
||
| 神话 | 4 | 1 | 红 | 16 |
|
||
|
||
权重取源码 `210:60:28:4:1`,只使用前五档;源码数组中的第六个 `4` 不进入本验证版品质池。
|
||
|
||
### 6.2 生成与属性
|
||
|
||
每件掉落按固定顺序消费 `lootRng`:
|
||
|
||
1. 选择槽位;若某槽为空,其权重为 2,其余为 1。
|
||
2. 在该槽的 10 个外观 ID 中等权选 1 个。
|
||
3. 按五档权重选择品质。
|
||
4. 从 13 个 `weaponstatus` 中等权选 1 个主属性。
|
||
5. `baseRoll = uniform(baseMin, baseMax)`。
|
||
6. `value = baseRoll + plusValue × qualityRank`;按属性显示精度取整。
|
||
7. 创建不可变 `GearInstance.instanceId`。
|
||
|
||
第 4–6 步为 `[验证版新增]` 的明确生成规则,因为 IL2CPP 元数据没有恢复原作 `GetStatus` 方法体。CSV 保留原始 `baseValue`、`plusValue`、百分比和浮点标记,后续可替换生成器而不改目录。
|
||
|
||
### 6.3 查看、换装与分解
|
||
|
||
- 大量随机局的装备“查看机会”P50 必须为 6–8;机会包括宝箱、战斗掉落、铁匠铺、传奇装备地块和小游戏奖励。
|
||
- 掉落弹窗同时显示新旧装备、品质、主属性和总面板变化;增加为绿色、降低为红色、无变化为灰色。
|
||
- 选择装备:新装备飞入固定槽,旧装备从槽中掉出,随后自动分解并增加点数。
|
||
- 选择分解:新装备直接转为品质对应点数。
|
||
- 关闭掉落弹窗:所有未处理装备按出现顺序自动分解。
|
||
- 同一掉落批次只在 `PostBattle/RewardChoice` 提交一次;动画重播不得重复加点。
|
||
|
||
### 6.4 铁匠铺
|
||
|
||
选择当前已装备的一件装备后,可选择“直接领取”或“挑战强化”:
|
||
|
||
- 目标从当前品质升至下一品质;神话不可强化。
|
||
- 基础成功率依次为 80% / 50% / 30% / 10%。
|
||
- 成功:品质 +1,按新 rank 重算主属性,保留 instanceId 并增加 `upgradeCount`。
|
||
- 失败:装备销毁,从槽位移除,并按失败前品质自动分解。
|
||
- 确认后先写 intent,随机完成后整体提交;刷新只能恢复最终结果,不能重抽。
|
||
|
||
## 7. 祝福与药水
|
||
|
||
### 7.1 祝福池
|
||
|
||
祝福池为源码 31 个 `isEnabled=True` 条目加 `ThirdDieReplace`,共 32 个,详见 [blessings.csv](./specs/blessings.csv)。源码四档权重为 `50:30:20:5`;第三骰固定为传奇档。
|
||
|
||
祝福获得规则:
|
||
|
||
- 每次只揭示 1 张卡;卡片生成后立即把 `blessingId` 写入待选择状态。
|
||
- 玩家直接接受已揭示卡;当前构建不提供广告重抽或强制换卡。
|
||
- 可叠加、最大层数、依赖项和参数严格读取 CSV。
|
||
- 同一祝福的星级费用读取 `starsPerAugment=1,2,2,3,3,3,4,4,4,4,5…`;持有层数从 0 计数,用 `min(level, 29)` 取值。
|
||
|
||
`ThirdDieReplace`:每次正常投出 A/B 后投 C;玩家必须选择“替换 A”“替换 B”“放弃”。它只改变当前移动值,不修改 A/B 的基础随机结果与统计日志。
|
||
|
||
### 7.2 丹药
|
||
|
||
全局有且只有一种丹药。开局容量 1、基础恢复 `50 HP`。固定商店升级可把容量提升到 10;基础恢复曲线为 `50、80、110……650 HP`,每次升级增加 30 HP,完整曲线见 [potions.csv](./specs/potions.csv)。
|
||
|
||
- 解密后的 `PotionController.GetPotionFinalHeal` native 方法体确认最终基线为 `(healLevelValue + percentHealBonus) × adaptiveMultiplier / 100`:`healLevelValue` 是 `potion.csv` 的固定 HP;独立 `Potion_PercentHeal(valueA=10)` 才计算 `maxHp × 10%` 后与基线相加。网络检索未发现更高置信度的公开精确公式。
|
||
- 因此本 MVP 不采用“基础恢复 10%、每级 ×1.25”的无证据回退公式;该公式只在 native 证据缺失时才启用。最终生命仍不得超过最大生命。
|
||
- 丹药只允许在 `Fight` 且玩家存活、剩余次数 >0 时手动使用。
|
||
- 点击后先锁按钮,结算恢复和丹药强化,再扣 1 次,最后解锁。
|
||
- 同一输入 tick 只接受一次;暂停、连点或恢复存档不得重复生效。
|
||
- 治疗不超过最大生命;最大生命增加类效果先改上限再治疗。
|
||
- 丹药只能通过三个固定商店商品和丹药相关祝福强化,不再存在第二种丹药或独立的随机“丹药强化商品”。原 9 条 `potionaugment` 数据仅作为丹药相关祝福的设计参数参考;禁用的 `GuaranteedDouble` 不进入任何池。
|
||
|
||
## 8. 商店与局内点数
|
||
|
||
### 8.1 商人出现与界面
|
||
|
||
- 商人在第 3、6、9 层的一个合法普通格上出现;只保证生成,不强制玩家落点。
|
||
- 商店显示 3 个固定升级槽和 3 个随机商品槽。
|
||
- 固定槽为:补充 1 瓶且不超过上限的 `potionAdd`、只令持有上限 +1 的 `potionCount`、令基础恢复量 +30 HP 的 `potionHeal`。
|
||
- 随机槽从地块、祝福、直接属性、彩票和新增第三骰商品中无放回抽取;不再抽取独立 `POTION` 商品。
|
||
- 已达购买上限、依赖不满足或本局已售罄的商品不进入候选池。
|
||
- 刷新费用为 `3 + 2 × rerollCount` 点;支付和换货同一事务提交。
|
||
|
||
### 8.2 97 条商品
|
||
|
||
[shop-offers.csv](./specs/shop-offers.csv) 完整保留源码 96 条商品,并新增:
|
||
|
||
| ID | 类型 | 内容 | 价格 | 上限 | 来源 |
|
||
|---|---|---|---:|---:|---|
|
||
| 196 | `AUGMENT` | `ThirdDieReplace` | 27 点 | 1 | `[验证版新增]` |
|
||
|
||
原 96 条数据仍完整保存用于溯源;其中 9 条药水强化不进入 DEMO 商店随机池,其效果只允许经丹药相关祝福设计接入。其余价格、购买上限、固定槽和排序值按源码表。
|
||
|
||
### 8.3 点数源汇
|
||
|
||
点数是本验证版唯一局内货币,不跨局保存:
|
||
|
||
- 来源:移动结算、战斗/事件奖励、装备分解、祝福 `PointSupply`、即时商人 +20。
|
||
- 消耗:商店购买、商店刷新和需要点数的小游戏。
|
||
- 所有变化写 `transactionId、reason、delta、balanceAfter`。
|
||
- 余额不足时按钮禁用并显示缺口;任何界面不得允许负余额。
|
||
- 结算页只展示本局获得/消耗,不结转到下一局。
|
||
|
||
## 9. 事件与广告入口边界
|
||
|
||
### 9.1 自然生成与玩家有益地块注入
|
||
|
||
原作不存在已证实的“十层内六类各生成一次”保证袋。严格还原采用两条独立入口:
|
||
|
||
1. 自然生成候选受 `newtile.isEnabled`、`minStage`、`minLoop` 与 `maxCornerPerGame` 过滤,完整资格表见 [enabled-tile-eligibility.csv](../../analysis/stage-floor-rules/tables/enabled-tile-eligibility.csv)。
|
||
2. 玩家有益地块从当前可用/装备的 `OutGameTile` 元数据构成洗牌牌组;牌组内无放回,耗尽后重洗。
|
||
3. 新账号 8 个默认装备地块见 [default-good-tile-deck.csv](../../analysis/stage-floor-rules/tables/default-good-tile-deck.csv);初始化调用固定抽取 8 次。
|
||
4. 换层调用固定为 `AddRandomGoodTiles(loopIndex+2)`;这是抽取/安装尝试数,不保证成功落盘数。
|
||
5. 商店依附起点,每 3 个显示楼层出现,不进入普通 GoodEvent 随机池。
|
||
|
||
### 9.2 十二种可安装地块
|
||
|
||
商店购买后可安装全部 12 种源码地块:137、138、139、140、141、142、143、144、151、152、164、166。基础规则、源码参数、界面和调试验收见 [events.csv](./specs/events.csv)。
|
||
|
||
- 安装时玩家选择一个高亮合法格;取消则商品保持 `PurchasedPendingInstall`,不退款、不丢失。
|
||
- 同一格只能有一个事件;事件完整结算或主动离开后变为荒地,不能在后续楼层重复触发。玩家购买或过层新增地块可覆盖荒地。
|
||
- 额外事件不要求十局自然覆盖,必须通过调试台逐项强制生成验收。
|
||
- 挖掘固定 5×5、基础 5 次;覆盖形状读取 `herobodyminigame.csv`。
|
||
- 抓娃娃每局面生成 20 个胶囊;权重读取 `clawcraneminigame.csv`。
|
||
- 泡泡龙从 6 个源码模板中按 seed 选 1 个;基础 3 发、18 个目标配置。
|
||
|
||
### 9.3 广告与即时增强入口
|
||
|
||
当前 MVP 不渲染任何广告、`▶` 即时增强、事件重抽、补次数、赠送点数或广告复活按钮。事件全部按基础次数和基础收益结算;祝福卡揭示后直接接受。旧 [reward-boosts.csv](./specs/reward-boosts.csv) 与 `RewardBoostTransaction` 类型仅作为未来 IAA 讨论和旧存档迁移的休眠接口,当前普通 UI 和调试台都不可触发。后续增加 IAA 必须在 DEMO 签收后另行设计、授权和验收。
|
||
|
||
## 10. UI/UX 规范
|
||
|
||
### 10.1 全局布局
|
||
|
||
- 基准画布 750×1334,`Fit Height`,安全区内边距左右 24、顶部 44、底部 34。
|
||
- 世界和像素资源只允许整数倍缩放;相机与节点落点取整,禁止静止时半像素。
|
||
- 顶部:点数、层数、暂停;仅 `DiceGauge` 调试关额外显示 Boss Gauge。
|
||
- 中部:棋盘或战斗舞台;弹窗不得遮住当前关键目标或 Boss 预警。
|
||
- 下部:HP、四装备槽、主操作按钮、药水。
|
||
- 普通入口隐藏自动掷骰、战斗加速和所有调试功能。
|
||
|
||
### 10.2 页面清单
|
||
|
||
完整控件和状态见 [ui-screens.csv](./specs/ui-screens.csv)。必须实现:
|
||
|
||
1. 本地开始页:开始新局、继续安全点、清除本局。
|
||
2. 棋盘 HUD:点数、层数、HP、四槽、Roll、丹药、暂停;`Default` 第 1 关不显示 Boss 蓄势,只有 `DiceGauge` 调试关显示蓄势(0/2)或倒计时(4..0)。
|
||
3. 骰子结果/第三骰选择。
|
||
4. 自动战斗 HUD:双方血条、伤害数字、状态、药水。
|
||
5. 掉落比较与批量未处理提示。
|
||
6. 商店与地块安装。
|
||
7. 宝箱、营火、铁匠铺、祝福、挖掘、彩票、21点、抓娃娃、黄金马车、骰子机、泡泡龙。
|
||
8. 源码复生祝福、失败、Boss 胜利和局结算;不含广告复活页。
|
||
9. 暂停、玩家面板与退出确认;玩家面板必须显示反击。
|
||
10. 隐藏调试台。
|
||
|
||
### 10.3 轻引导
|
||
|
||
仅提供三个一次性上下文提示,状态保存在本机设置而非 run:
|
||
|
||
- 首次掷骰:高亮 Roll,说明“两枚骰子点数相加”。
|
||
- 首次战斗:说明自动攻击,药水可手动使用。
|
||
- 首次装备:指向属性对比与替换/分解。
|
||
|
||
提示不暂停计时战斗;如果触发于战斗前,必须在 `PreBattle` 完成后再开始逻辑时钟。
|
||
|
||
## 11. 美术、动作、特效与音频
|
||
|
||
### 11.1 美术边界
|
||
|
||
- 只使用可提取的 HEROLL 内部原型资产;不得引用 Dice Lords 形象、配色、构图或旧 Art Bible 规则。
|
||
- 风格为传统低清像素:深色森林/地牢底、紫灰轨道、青绿有益地块、红黑 Boss 压力、厚描边 UI。
|
||
- Grass 场景使用分层远景、暗绿色地表、灰蓝岩石与暖色火把;不引入其他章节主题。
|
||
- 角色、怪物、场景、UI、动作和特效映射见 [assets.csv](./specs/assets.csv) 与 [animation-vfx.csv](./specs/animation-vfx.csv)。
|
||
|
||
### 11.2 角色与怪物规则
|
||
|
||
- 玩家剑士保持短身像素比例、红兜帽/深甲/亮剑盾轮廓;武器和盾牌必须在战斗缩放下可区分。
|
||
- 剑士敌人用宽肩和近战武器;直射敌人用细长水平武器;抛射敌人用高举/弧线姿态;冲锋敌人用前倾轮廓。
|
||
- Boss 至少是玩家高度 2.5 倍,黑灰主体、红色外沿、紫色能量点;不依赖文字传达危险。
|
||
- 同屏角色描边至少 1 个原始像素;状态着色不能覆盖面部、武器和攻击预警。
|
||
|
||
### 11.3 动作和特效规则
|
||
|
||
- 全系统最低反馈契约:按钮按压、骰子、逐格移动、落地、楼层切换、Boss 计数/转换、战斗进出、双方攻击/受击、伤害/治疗数字、装备揭示/换装/分解、祝福、商店购买、每类事件、复活和最终结算都必须保留至少一段可读动画或数值过渡;普通入口禁止跳过、瞬移式结算或无反馈硬切。
|
||
- 逻辑命中由时间轴事件驱动,不能绑定动画结束回调。
|
||
- 普攻:180–300ms,命中前摇 40–80ms,命中停顿 60–90ms。
|
||
- Boss 攻击:预警 ≥500ms,预警形状与伤害区域误差 ≤4 个逻辑像素。
|
||
- 暴击:允许一次 ≤10px 相机脉冲;全屏白闪不得超过 33ms。
|
||
- 伤害数字同来源 150ms 内合并,同屏最多 24 个。
|
||
- 治疗用绿/青白核心,护盾用青色轮廓,受伤用红色但不与有益效果混色。
|
||
- 低特效模式仍保留地面预警、命中点和状态图标。
|
||
|
||
### 11.4 音频
|
||
|
||
不使用背景音乐。只加载必要功能音效:按钮、骰子滚动/落定、移动步、近战命中、直射、抛射落点、冲锋、暴击、受伤、治疗、护盾、装备获取/分解、祝福揭示、购买、事件成功/失败、Boss 预警与胜负。
|
||
|
||
同类短音效 80ms 内最多播放一次;战斗退出时停止循环或延迟音;页面切换不得残留音频实例。
|
||
|
||
## 12. 技术架构与数据契约
|
||
|
||
### 12.1 模块边界
|
||
|
||
```text
|
||
Bootstrap
|
||
├─ RunDomain // 状态机、楼层、输入、结算
|
||
├─ RngDomain // 多流确定性随机
|
||
├─ BoardDomain // 棋盘、移动、地块安装
|
||
├─ BattleDomain // 实体、公式、祝福、药水、DDA
|
||
├─ BuildDomain // 装备、分解、祝福
|
||
├─ ShopDomain // 报价、购买、刷新
|
||
├─ EventDomain // 12事件;历史增强结构保持休眠
|
||
├─ SaveDomain // 安全点、迁移、幂等事务
|
||
├─ Presentation // Cocos 场景、UI、动画、音频
|
||
└─ DebugDomain // 隐藏调试台与技术日志
|
||
```
|
||
|
||
表现层只能派发 command,不得直接修改 HP、点数、库存或 RNG。所有 Domain command 返回不可变 result,再由表现层播放。
|
||
|
||
### 12.2 TypeScript 接口
|
||
|
||
```typescript
|
||
type RunPhase =
|
||
| 'RUN_INIT' | 'FLOOR_SETUP' | 'AWAIT_ROLL' | 'DICE_RESOLVE'
|
||
| 'THIRD_DIE_CHOICE' | 'MOVING' | 'TILE_RESOLVE' | 'BATTLE'
|
||
| 'EVENT' | 'SHOP' | 'REWARD_CHOICE' | 'FLOOR_TRANSITION'
|
||
| 'BOSS_BATTLE' | 'RUN_CLEAR' | 'RUN_FAILED';
|
||
|
||
type RngStreamName = 'dice' | 'board' | 'loot' | 'combat' | 'shop' | 'event';
|
||
|
||
interface RngStreamState {
|
||
algorithm: 'xoshiro128ss';
|
||
runSeed: string;
|
||
stream: RngStreamName;
|
||
state: [number, number, number, number];
|
||
index: number;
|
||
}
|
||
|
||
interface DdaState {
|
||
enabled: boolean;
|
||
tier: number;
|
||
sourceConditions: [5, 8, 12];
|
||
sourceValues: [10, 15, 20, 5, 5, 5];
|
||
recentBattleMs: number[];
|
||
attackMultiplier: number;
|
||
healthMultiplier: number;
|
||
lastTransitionReason: string;
|
||
}
|
||
|
||
interface DiceRoll {
|
||
rollId: string;
|
||
rollIndex: number;
|
||
dieA: number;
|
||
dieB: number;
|
||
thirdDie?: number;
|
||
thirdDieAction?: 'REPLACE_A' | 'REPLACE_B' | 'DISCARD';
|
||
finalA: number;
|
||
finalB: number;
|
||
moveValue: number;
|
||
rngIndexBefore: number;
|
||
rngIndexAfter: number;
|
||
}
|
||
|
||
interface BattleSnapshot {
|
||
battleId: string;
|
||
floor: number;
|
||
isBoss: boolean;
|
||
logicTimeMs: number;
|
||
player: CombatantSnapshot;
|
||
enemies: CombatantSnapshot[];
|
||
statuses: StatusSnapshot[];
|
||
cooldowns: Record<string, number>;
|
||
combatRng: RngStreamState;
|
||
reviveBlessingResolved: boolean;
|
||
instantReviveUsed: boolean; // 旧存档兼容字段,当前 UI 不可达
|
||
}
|
||
|
||
interface GearInstance {
|
||
instanceId: string;
|
||
catalogId: number;
|
||
slot: 'WEAPON' | 'RING' | 'GAUNTLET' | 'BOOTS';
|
||
qualityRank: 0 | 1 | 2 | 3 | 4;
|
||
statusId: number;
|
||
value: number;
|
||
upgradeCount: number;
|
||
}
|
||
|
||
interface BlessingInstance {
|
||
instanceId: string;
|
||
blessingId: string;
|
||
level: number;
|
||
stacks: number;
|
||
remainingTriggers?: number;
|
||
}
|
||
|
||
interface PotionState {
|
||
countLevel: number;
|
||
healLevel: number;
|
||
charges: number;
|
||
maxCharges: number;
|
||
healAmount: number; // fixed HP from HEAL_LEVEL.value
|
||
augments: number[];
|
||
inputLocked: boolean;
|
||
}
|
||
|
||
interface ShopState {
|
||
merchantInstanceId: string;
|
||
floor: number;
|
||
fixedOfferIds: number[];
|
||
randomOfferIds: number[];
|
||
purchasedCountByOffer: Record<string, number>;
|
||
rerollCount: number;
|
||
pendingInstallOfferId?: number;
|
||
}
|
||
|
||
interface EventInstance {
|
||
instanceId: string;
|
||
tileId: number | 'MERCHANT';
|
||
floor: number;
|
||
tileIndex: number;
|
||
visitIndex: number;
|
||
phase: 'READY' | 'RESOLVING' | 'RESOLVED';
|
||
boostUsed: boolean; // 旧存档兼容字段,当前 UI 不可达
|
||
payload: Record<string, unknown>;
|
||
}
|
||
|
||
interface RewardBoostTransaction { // 仅保留旧存档迁移,不创建新事务
|
||
transactionId: string;
|
||
placementId: string;
|
||
eventInstanceId: string;
|
||
status: 'PENDING' | 'COMMITTED';
|
||
rewardPayload: Record<string, number | string | boolean>;
|
||
createdAtLogicTick: number;
|
||
committedAtLogicTick?: number;
|
||
}
|
||
|
||
interface Checkpoint {
|
||
schemaVersion: 1;
|
||
checkpointId: string;
|
||
createdAtLogicTick: number;
|
||
reason: string;
|
||
run: RunState;
|
||
rng: Record<RngStreamName, RngStreamState>;
|
||
committedTransactionIds: string[];
|
||
checksum: string;
|
||
}
|
||
|
||
interface RunState {
|
||
runId: string;
|
||
runSeed: string;
|
||
phase: RunPhase;
|
||
floor: number;
|
||
tileIndex: number;
|
||
lapSteps: number;
|
||
points: number;
|
||
playerStats: CombatStats;
|
||
gear: Partial<Record<GearInstance['slot'], GearInstance>>;
|
||
blessings: BlessingInstance[];
|
||
potion: PotionState;
|
||
shop?: ShopState;
|
||
activeEvent?: EventInstance;
|
||
activeBattle?: BattleSnapshot;
|
||
dda: DdaState;
|
||
reviveUsedThisFloor: boolean; // 旧存档兼容字段
|
||
generatedCoreEvents: string[];
|
||
pendingTileInstalls: number[];
|
||
saveVersion: 1;
|
||
}
|
||
```
|
||
|
||
`CombatantSnapshot`、`StatusSnapshot` 和 `CombatStats` 是纯数据对象,禁止包含 Cocos Node、组件引用或函数。CSV 到运行时字段的映射见 [data-contracts.csv](./specs/data-contracts.csv)。
|
||
|
||
### 12.3 随机流
|
||
|
||
所有流从 `hash(runSeed + ':' + streamName)` 派生,禁止共享索引:
|
||
|
||
- `dice`:基础骰与第三骰。
|
||
- `board`:地块位置和普通战斗格。
|
||
- `loot`:装备、品质、词条、祝福。
|
||
- `combat`:命中、暴击、伤害波动、战斗触发。
|
||
- `shop`:随机商品和刷新。
|
||
- `event`:保证袋、小游戏与事件奖励。
|
||
|
||
UI 动画、粒子和音效不得消费任何 Domain RNG;表现随机使用不保存的 `visualRng`。
|
||
|
||
## 13. 安全点、恢复与日志
|
||
|
||
### 13.1 安全点规则
|
||
|
||
每次地块、战斗、购买、换装、祝福选择或事件**完整结算后**写安全点。禁止在扣点后、发奖前或随机已消费但结果未提交时保存半状态。
|
||
|
||
```mermaid
|
||
flowchart TD
|
||
command["接收玩家命令"] --> clone["复制当前 RunState"]
|
||
clone --> resolve["在副本消费 RNG 并完整结算"]
|
||
resolve --> validate{"不变量通过?"}
|
||
validate -- 否 --> rollback["丢弃副本并记录异常"]
|
||
validate -- 是 --> tx["提交事务与 RunState"]
|
||
tx --> save["序列化 Checkpoint + checksum"]
|
||
save --> atomic["写临时槽并原子替换主槽"]
|
||
atomic --> render["通知表现层"]
|
||
crash["刷新/崩溃/重新打开"] --> load["读取主槽;失败则读备份槽"]
|
||
load --> verify{"版本和 checksum 有效?"}
|
||
verify -- 否 --> newRun["提示存档损坏并开始新局"]
|
||
verify -- 是 --> dedupe["恢复已提交 transactionId 集合"]
|
||
dedupe --> resume["从最近安全 phase 恢复"]
|
||
```
|
||
|
||
### 13.2 本地存储
|
||
|
||
- 主槽 `heroll.validation.run.v1`,备份槽 `heroll.validation.run.v1.bak`。
|
||
- 设置槽 `heroll.validation.settings.v1` 只保存音量、轻引导和低特效选项。
|
||
- 写入顺序:序列化 → checksum → 写备份 → 写主槽 → 删除临时键。
|
||
- `schemaVersion` 不匹配时调用显式迁移;迁移失败不得猜字段。
|
||
- 开始新局、胜利或失败确认后清除 run 主/备份槽,不清除设置。
|
||
|
||
### 13.3 技术日志
|
||
|
||
只记录:`runId`、seed、各 RNG 索引、DDA 状态、状态迁移、事务、异常、checkpointId、checksum 和存档版本。禁止制作人工试玩表、用户画像或跨局统计。
|
||
|
||
日志为内存环形缓冲 2,000 条,可从调试台导出 JSON;普通界面不显示。单局时长只由现场负责人观察确认,不写聚合分析。
|
||
|
||
## 14. 隐藏调试台
|
||
|
||
入口:开始页标题连续点击 7 次,或开发构建按 `Ctrl+Shift+D`。发布型本地包默认隐藏但保留代码。
|
||
|
||
调试能力:
|
||
|
||
- 指定/复制 seed、重新开局、查看各流索引。
|
||
- 调整层数、位置、HP、点数、药水和战斗速度 1×/4×。
|
||
- 重置/注入 DDA tier。
|
||
- 强制 12 种事件、指定装备/品质/词条、指定祝福、强制死亡。
|
||
- 跳到 Boss、重放最近安全点、模拟刷新、重复提交 transactionId。
|
||
- 导出技术日志和当前 `Checkpoint`。
|
||
|
||
调试命令必须经过同一 Domain API,不得直接改 Cocos 节点;每条命令日志标记 `debug=true`,避免与普通 seed 复现混淆。
|
||
|
||
## 15. QA 与发布验收
|
||
|
||
详细用例见 [qa-cases.csv](./specs/qa-cases.csv)。以下为发布门禁:
|
||
|
||
1. 两枚骰子 36 个有序结果理论等权;100 万次模拟通过卡方检验,`p >= 0.01`。
|
||
2. 同一 seed、DDA 和输入序列生成完全相同的棋盘、掉落、战斗和商店。
|
||
3. 十层推进、30% 恢复、初始化 8 次玩家有益牌组抽取、换层 `loopIndex+2` 次尝试、商人 3/6/9 与最终 Boss 全部通过。
|
||
4. 第 1 关不得创建 Boss Gauge/倒计时状态;专项 DiceGauge 调试关才验证 2 次 ROLL 蓄势 + 4 次 ROLL 倒计时、4 个唯一非祝福转换与 Gauge 清零。
|
||
5. 战斗拟合伤害误差 ≤5%、时长误差 ≤10%;未通过则不得标记原作还原。
|
||
6. 40 件装备均可生成;品质权重正确;10 万局模拟中查看机会 P50 为 6–8。
|
||
7. 32 个祝福、第三骰三种处理、丹药 10/21 级曲线和 9 个强化全部可达。
|
||
8. 商店恰好 97 条;固定链、价格、上限、刷新和地块安装通过。
|
||
9. 12 种事件逐项检查输入、表现、结算、存档、退出和调试强制路径。
|
||
10. 普通 UI 中广告、即时增强、广告复活、重抽和补次数按钮数量均为 0;历史事务接口不可达。
|
||
11. 每种弹窗、战斗结束、购买、换装和事件边界恢复时不存在半结算。
|
||
12. 750×1334 以及 808×1440 视口无裁切;像素资源静止时无半像素模糊。
|
||
13. 构建中不存在账号、局外成长、体力、抽卡、IAP、广告按钮、真实广告 SDK、剧情或 BGM 入口。
|
||
14. 固定棋盘在移动前后计算样式均为 `transform:none`,角色下一格位于起点左侧;每类功能格加载对应原作图标。
|
||
15. 初始配置不使用丹药、广告或调试加成时,自动战斗可连续击败前三组普通怪物。
|
||
|
||
### 15.1 十局现场签收
|
||
|
||
项目组现场完成 10 局,不制作用户统计表:
|
||
|
||
- 至少 3 局攻击构筑。
|
||
- 至少 3 局攻速/暴击构筑。
|
||
- 至少 3 局生存构筑。
|
||
- 至少 1 局自由构筑。
|
||
- 覆盖无复生祝福的直接失败路径,以及源码 `Revive` 祝福路径。
|
||
- 负责人现场确认大体处于 10–12 分钟,并记录“通过/不通过”会议结论;不保存个人行为数据。
|
||
|
||
十局签收通过后,才进入“完整 IAA GDD”讨论;当前 DEMO 不预埋可见广告入口。
|
||
|
||
## 16. Evidence → Finding → Design
|
||
|
||
### 16.1 关键 Findings
|
||
|
||
| Finding | 证据 | 设计落点 | 置信度 |
|
||
|---|---|---|---|
|
||
| 双骰、棋盘、自动战斗、构筑和 Boss 是核心循环 | E-001/E-002/E-019/E-020 | §3–5 | 高 |
|
||
| `stage=0` 为 Grass、10 层、`boss_0/skill_0`、冷却 7 | E-001 | §3.1 | 高 |
|
||
| 第 1 关 10 行敌方基础属性可逐层复原 | E-002 | `floor-balance.csv` | 高 |
|
||
| 玩家裸装初始属性已完整提取 | E-003 | §5.1 | 高 |
|
||
| 31 个启用祝福、40 件局内装备和 96 条商店商品可完整列举 | E-006/E-007/E-009 | `blessings/gear/shop` CSV | 高 |
|
||
| 原作方法体不足以证明最终战斗与 DDA 公式 | E-012/E-016 | §5.4–5.5、拟合门禁 | 高 |
|
||
| 当前实机账号带局外属性,不适合作为裸装拟合样本 | E-019 | `formula-fit.csv` 补录矩阵 | 高 |
|
||
| 提取资产支持低清像素 Grass、Boss、UI 和 VFX 复用 | E-018 | §11、资产表 | 高 |
|
||
|
||
### 16.2 实现 Path
|
||
|
||
`P-001`:`stageinfo/board/playerdefaultstat` → 关卡与角色配置 → `RunDomain/BattleDomain` → 十层 Boss 验收。
|
||
`P-002`:`weapondrop/weaponstatus/augment/potion` → 构筑实例 → 战斗属性快照 → 掉落、换装与祝福验收。
|
||
`P-003`:`ingameshoplist/newtile/小游戏表` → 商店报价 → 事件安装与基础结算;历史增强事务保持休眠不可达。
|
||
`P-004`:run command → 完整副本结算 → transaction commit → checkpoint → 崩溃恢复。
|
||
|
||
逐条可复现路径、文件哈希和 Design ID 见 [evidence-map.csv](./specs/evidence-map.csv)。
|
||
|
||
## 17. 开发交付清单
|
||
|
||
- 主 GDD:本文件。
|
||
- 配置索引:[specs/README.md](./specs/README.md)。
|
||
- 证据:[evidence-map.csv](./specs/evidence-map.csv)。
|
||
- 关卡/公式:[floor-balance.csv](./specs/floor-balance.csv)、[formula-fit.csv](./specs/formula-fit.csv)。
|
||
- 构筑:[gear-catalog.csv](./specs/gear-catalog.csv)、[gear-status.csv](./specs/gear-status.csv)、[blessings.csv](./specs/blessings.csv)、[potions.csv](./specs/potions.csv)。
|
||
- 商店/事件:[shop-offers.csv](./specs/shop-offers.csv)、[events.csv](./specs/events.csv)、[reward-boosts.csv](./specs/reward-boosts.csv)。
|
||
- 技术/UI/美术:[data-contracts.csv](./specs/data-contracts.csv)、[ui-screens.csv](./specs/ui-screens.csv)、[assets.csv](./specs/assets.csv)、[animation-vfx.csv](./specs/animation-vfx.csv)。
|
||
- 验收:[qa-cases.csv](./specs/qa-cases.csv)。
|
||
|
||
完成定义:所有 CSV 通过结构校验,Mermaid 可解析,97/40/32 数量正确,禁用系统无残留入口,QA `P0` 全部通过,战斗拟合门禁通过,并完成 10 局现场签收。
|