merge: 禁写变更记录批入库——规约/AGENTS/闸壳/技能四层指令面废除文档内变更日志(gdd_lint R4 反转为禁设+06b 新闸+三工具去豁免),48 份文档版本历史节与 game/README 流水段删除、代码注释 900 余行批次标签清偿(约束句保留、去注释零触碰自证)、卡片「修订记录」块改「审查回写要点」(--docs 闸 dafc5540 六件全绿+变异验证三态真拦:版本历史/修订记录/修改记录)
# Conflicts: # game/tests/static_scan.sh
This commit is contained in:
commit
c5200b6be9
235 files changed
+2647
-4167
No files matched your search
+1
-533
@@ -58,536 +58,4 @@ Godot_v4.7.2-stable_win64_console.exe --headless --path . --script res://smoke/a
|
||||
- **v2 追加**:LevelTable(线性节点链数据表)· C1-lite 关卡推进器 `level_runner.gd`(原 c1_level_runner,2026-09-11 领域命名批更名)(关卡域生命周期:共享 B4 关内延续 + HP 跨战斗携带 + 奖励/恢复节点兑付 + 关卡末清库)· Boss 遭遇行(Dmax=4)· 节点奖励 API(B4 `grant_node_active`)· **测试用角色数值调整板**(atk%/hp% 覆盖 + 批量模拟,模拟天赋树——正式侧归 C4)
|
||||
- **裁剪不变**:A5 / C1 路径分支选择(用户拍板线性)· C2–C4 / B5 战斗动画 / D3–D6 / A1 黄金向量与 F4 批量工具 / gdUnit · 注:标错惩罚(原 A2 OQ1)已于 2026-09-12 批上线(标空封印三选一确定性反噬:暴走 / 伏击 +2 层 / 失衡,坐标×板序号派生、不走随机流),本批(2026-09-15)已收编进现行文档,不再属裁剪项
|
||||
|
||||
## 2026-09-11 可选类供给控制器(用户拍板批——修改记录)
|
||||
|
||||
**背景**:供给控制器批前实测每关可选类 ≈24.8 件(≈18.5 把刀,刀总伤 ≈180 > Boss 130 HP)——"道具获取期望太高"定量坐实。用户重新定义三尺度指标并拍板实现:
|
||||
|
||||
| 尺度 | 指标 | 实现机制 |
|
||||
|---|---|---|
|
||||
| 安全格 | 期望极小(全图摊平 < 0.01/格) | 可选类**退出格子权重池**——格子池只剩即时类四行 |
|
||||
| 雷图 | ≈0.78/板 | 战斗首件保证 ÷ 每战板数(≈1.28)折算 |
|
||||
| 战斗 | 期望 ≈1,**首件保证**:获取首件前每张新板必注入 1 件;之后按 **p0×decay^(j-1) 指数递减**(永不绝育) | `RewardSupplyController`(B4 私有组合件,战斗域计数) |
|
||||
|
||||
**拍板四条 → 落点**:① 注入占比 小刀 60% / 精工短刀 30% / **治疗包 10%**(治疗资源宝贵)→ `reward.tres` active_inject 池;② **清图奖励改一次性高额伤害**(clear_knife 入库刀 → clear_blast instant_aoe 40,当场兑付全体存活敌,不入库、不占可选类供给——清图与供给脱钩);③ 注入选格**均匀随机**(雾格不加权);④ **指数递减**(active_p0=0.4 / active_decay=0.5 → 0.4/0.2/0.1/0.05…,RewardConfigTable 新参数,provisional 归 C3 标定)。
|
||||
|
||||
**实现结构**(对象封装——伪代码审阅后落地):
|
||||
|
||||
- `core/reward_supply_controller.gd`(新):`notify_battle_start()`(计数清零)/ `should_inject(stream)`(j==0 恒真;否则 `randf < p0×decay^(j-1)`,取号经传入封闭流)/ `notify_granted()` / `granted_count()`。行值快照抗热重载;零自有随机数。
|
||||
- `B4RewardEconomy`(改造):`_supply` 组合件 · `notify_battle_start()`(B1.begin_battle 转发)· `roll_board` 重写(身份批即时类单遍 → 清图槽 → 注入判定/选格;旧 F5 草稿重 roll 循环删除)· `grant()` 挂钩 `notify_granted()`(**发放即获取**——预分配未挖到不计数;去重幂等天然防双计)· `supply_granted_count()` 观测面。
|
||||
- `B1BattleOrchestrator`:begin_battle 门序列增 `b4.notify_battle_start()`(编排唯一性不变)。
|
||||
- 表/护栏:RewardTable `POOL_TAGS` + `active_inject`;RewardConfigTable + `active_p0/active_decay`、k_dmg 1→0(F5 休眠);ConfigDB 跨表护栏更新(clear_no_value 含即时类 / no_damage_row 扫描全池活行 / 新增 inject_pool 护栏:非可选类拒绝 + 空池致命)。
|
||||
|
||||
**实测(N=200 板/深度,工具 reward_expectation.gd)**:注入 1.00 件/板(j=0,多件板 0)· 占比 小刀 59–65% / 精工 25–29% / 治疗包 7–17%(名义 60/30/10)· 格池零可选类泄漏 ✓。**实际获取期望**(level_monte_carlo「可选类/战」列,N=15——工具已退役,数据归档):基线 0.88 → +200% 攻击 0.68 件/战(低于分配保证 1.0 的机理 = 注入格均匀随机,落在残余格/速杀未挖区时该件不获取;战斗末无兜底补发——**是否加"战斗末 j==0 补发 1 件"待用户拍板**)。每关合计 ≈ 4.5–6.5 件(原 24.8)✓ 拍板目标达成。
|
||||
|
||||
**⚠️ 供给侧副作用(本轮扫描发现,待拍板)**:可选类退出格子池后,其 25% 概率质量被动流向即时类——攻防 buff 每格频率从 44% 升至 59%,基线通关率 55%→80%(N=15)、整关承伤 127→80:**难度不降反升方向反了**。即时类是"每格必有货"锁定设计下无法清空的(除非引入空白格 = 违反锁定事实)。候选杠杆:① 接受更快的 buff 节奏,敌 HP 曲线上调对冲(C3 主杠杆)② 下调 attack_up/defense_up 在即时池内的相对权重(压 buff 频率)③ buff 幅度下调(触碰 B3 表值)。
|
||||
|
||||
**正式侧义务**:B4 GDD「install 期 roll / F5」章节修订(F5 → 战斗首件保证语义)+ registry reward_table/reward_config 条目改写 + RewardConfigTable knob 登记(active_p0/active_decay)——接口漂移批随实现落位。
|
||||
|
||||
## 2026-09-12 轮内刷新(方案 A,用户拍板批——修改记录)
|
||||
|
||||
**背景**:用户发现刷新实现(仅 Waiting 结算窗口受理)与设计预期不符——预期为**扫雷阶段遇到推理死路时,有剩余 AP 且 RAP 足够即可当场刷新继续挖**。影响分析(三方案对比 + 16 代码触点 + 8 文档 + 5 数值连锁)后用户拍板方案 A。
|
||||
|
||||
**拍板语义**:① Active 相位可刷(RAP ≥ 8 且本轮未达上限)→ 当场 retire+install 换新图,**轮次继续**:AP 保留不重置、相位不动、不触发结算(敌方时间税语义变更——GDD 修订义务);② Waiting 相位刷新保持现状(登记意图,exit_distribution 换图);③ **每轮兜底上限 2 次**(Active/Waiting 合计,`ActionEconomyTable.refresh_max_per_round = 2` 表值);④ RAP 来源不变(停手 1:1 转存)。
|
||||
|
||||
**实现落点**:A3 `request_refresh_active()`(仅扣 RAP,相位/AP 不动)· A2 `install_board/retire_board` 增 `during_round` 显式参数(Active 通道,其余相位守卫不变——fail-fast 保留)· B4 `query_full_snapshot()` · B1 `can_request_refresh()` 查询面 + `player_request_refresh()` 双相位重写 + `_refresh_used_this_round` 计数 + **facts 身份快照隔离**(缓冲期捕获身份快照、授予走快照——防旧图已挖格在轮末错拿新图同坐标身份;`_grant_cell` 增 snap_override)· 呈现层按钮"弃图刷新(RAP -8 · 每轮至多 2 次)"。
|
||||
|
||||
**核心实现难点(分析批未暴露)**:轮内换图后旧图已挖未结算格与新图**坐标重叠**——轮末授予 `b4.grant(cell)` 会错拿新图身份。解法 = facts 缓冲期用 `query_full_snapshot` 冻结身份,授予段直通快照(绕过现行 mapping/_granted,批次隔离;库存语义不变)。
|
||||
|
||||
**验证**:冒烟 88/88(+14 轮内刷新断言:当场换图/相位 AP 保留/RAP 扣减/上限拒绝/跨图 facts 完整兑现〔4 格跨三图快照授予〕/回放确定性)· 三探针全 PASS · 期望扫描与刷新前持平(80%/0.88——模拟策略不主动刷图,真人专用出口)。**调试教训**(规范 4.6 补充案例):蛆虫 HP 已调 24 而"18 未掉血"误判——排查数值问题先核对当前表值;GDScript lambda 对局部 int 按值捕获(计数器必须用容器)。
|
||||
|
||||
**正式侧义务(GDD/registry 修订清单)**:A3 GDD Rule 11(受理相位 Waiting → Active∪Waiting + 相位机表 + 时间税语义注记)· A2 GDD install 四不变式轮内场景 · B1 GDD 编排专章(轮内换图 + facts 快照协议)· B4 GDD E5/E6(query_full_snapshot/grant 快照直通)· game-concept 轮次流程 + "战斗:扫雷图 = 1:M"措辞(一轮跨多图新形态)· registry `action_economy`(refresh_max_per_round)· 数值连锁重跑(清图支配性条款⑤/获取期望——模拟不覆盖真人刷图行为,需 playtest 实测)。
|
||||
|
||||
**2026-09-15 补记(用户拍板)**:**Waiting(等待结算)相位刷新通道整体移除**——呈现层入口此前已按 OQ12 退役(2026-09-12 战斗屏批),本批拆除核心层残留:账本侧 Waiting 受理入口 `request_refresh()` 删除、编排侧 `can_request_refresh` 不再放行 Waiting、`player_request_refresh` 等待期登记意图分支删除、出口分发删除 `_refresh_used_this_waiting → new_board` 换图分支(非清图轮恒同图)、字段与诊断快照键同步移除。文档侧无需修订——[行动经济](../../design/gdd/systems/排雷循环/行动经济.md)只写挖掘期刷新、[战斗结算](../../design/gdd/systems/战斗循环/战斗结算.md)明文"非清图轮恒同图",拆完代码即双双收敛。冒烟断言改为"等待期刷新请求被拒且状态不变"。
|
||||
|
||||
**同批其它改动(口径读法)**:明细行协议 **13 类 → 15 类**(新增窗口打开锚行 `window_open`、窗口跳过锚行 `window_skip`,服务"窗口段恒在案"验收;单点定义见[战斗结算](../../design/gdd/systems/战斗循环/战斗结算.md));状态表 **四行 → 六行**(暴走 / 失衡两行随标错反噬批收编);达标判定回归**单一判据**(可推理率 ≥ 及格线);求解器禁读雷位真值。本文正文的 v1/v2 范围登记与各历史批条目保留当时陈述,计数以现行文档与代码为准。
|
||||
|
||||
## 2026-09-11 关卡扩展批 · 切片级偏差登记(实现期决策,正式化时须回 GDD/架构文档)
|
||||
|
||||
1. **C1 线性推进器 provisional**:节点链来自 LevelTable(结构位 = 未来 C1 的数据承载面);路径分支/奖励点分布定稿权归 C1/C3。节点类型 closed enum {battle, reward, rest, boss};末节点必须 Boss(LevelTable schema 校验)。
|
||||
2. **B4 新增 `grant_node_active()`**(接口漂移):关卡奖励点在可选类活行 {damage_item, heal_pack} 中 roll 一件直接入库;消耗 `reward_roll` 恰一 scope(与 board install 批同流异 scope,流集合封闭不变)。正式侧应走 B4 GDD 修订(节点奖励 = clear_reward 插槽族的新来源候选)。
|
||||
3. **B2 spawn max_hp 语义修复**(切片 bug):原实现 `max_hp = hero_hp 入参`——跨战斗携带 HP 时 max_hp 被压扁成当前 HP,恢复点/治疗包满血守卫失效。修复为 `max_hp = HeroTable.max_hp`(接口语义澄清,正式侧回 B2 GDD Rule 注记)。
|
||||
4. **A4 迷雾纪律遵守**:L1 全程无雾(迷雾 `unlock_level=2` 已锁事实——迷雾自第二层解锁);Boss 板以 12×12 大板区分。
|
||||
5. **属性覆盖 = 测试专用**:调整板直接写运行时 HeroTable(atk 域 [1,999] 内 clamp ≥1);关卡结束/回菜单恢复基线。正式天赋树归 C4,禁止生产代码引用本机制。
|
||||
6. **UI 复用 b1 引用**:关卡模式下呈现层 `b1 = runner.b1`(与调试模式共用 HUD/棋盘路径);runner 在关卡结束置 `b1 = null`。
|
||||
7. **runner 信号时序(playtest 缺陷①修复登记)**:`node_entered` 必须先于 `begin_battle` 发出——后发时首板安装的 `board_changed` 到达呈现层时 `b1` 尚未接手,首战棋盘不渲染。场景级探针 `smoke/ui_probe.tscn` 为该缺陷的回归断言载体。
|
||||
8. **A2 封印动作末点 AP 软锁(playtest 缺陷②修复登记——A2 核心 bug,v1 即存在)**:`_do_seal_place/_do_seal_pull` 花完末点 AP 后 A3 相位翻 Waiting 但不发 round_end——B1 的 Waiting 结算点依赖 round_end 到达,事件缺席 = 整局软锁(Boss 图封印多,末点 AP 花在封印上即触发)。修复 = 封印动作检查相位翻转并补发 `_emit_round_end_seals_and_payload()`(与 `_do_dig` 同款)。v1 冒烟盲区根源:自动玩家不用封印。回归断言入 smoke_all(封印动作耗尽 AP 必须进入第 2 轮)+ 行为探针 `smoke/ui_click_probe.tscn`(合成鼠标事件走真实 UI 路径,随机混合封印/撤封印/停手/窗口刷新/无效点击,4 种子全关通扫)。**正式侧义务**:A2 GDD F4 事件序契约须补"封印动作路径的 round_end 发射"措辞(接口漂移)。
|
||||
9. **呈现层固定分区(playtest 缺陷③修复登记)**:侧栏全部动态区(RAP/敌人/增益/库存/悬停状态)固定槽高 + `clip_contents`——悬停文案切换与库存增长不再推挤整体布局(此前"第 N 轮"库存行被挤出视口 + 全 UI 抖动)。区域独立,超长裁剪不回流。
|
||||
10. **L1 12×12 板生成余量**:D2–D4(及 L2)`max_gen_attempts` 16→24(F2c 24×144=3456 ≤ 16384 合法)——16 次尝试下偶发"重试耗尽接受最优"(best≈0.85–0.87 < target 0.88);24 次后仍偶发(best 0.866),密度×推理率的根本解归 C3。
|
||||
11. **道具使用后不消失(playtest 缺陷④修复登记——UI 层契约违例)**:B1 窗口选择契约 = `_window_items` **位置**(义务④"选定序"),UI 选用按钮却传 `item.index`(库存数组句柄)——未消耗过时二者巧合相等;一旦消耗过一件,已耗条目仍占库存数组位,后续窗口条目"句柄 = 位置 + 已耗数"错位 → 选中件不消耗、邻件被误耗(可用次数语义本身存在且正确:`use()` 一次性标记 Used 不可逆)。修复 = UI/行为探针一律传窗口位置;探针加"选用必生效"断言(选用件库存态变 Used,失效即退出码非零)。
|
||||
12. **数值调整板被遮挡不可见(playtest 缺陷⑤修复登记 + 专属测试补建)**:调整板覆盖层在场景树中位于**不透明的开始覆盖层之前**,`_show_tune_panel` 只置 visible 不隐藏开始层 → 面板被完全遮住 = "点击无反应"。修复 = 打开隐藏开始层 / 关闭(含"应用并关闭")恢复。**专属测试**:`smoke/ui_tune_probe.tscn`(场景级全链路:打开可见性〔缺陷⑤回归〕/ 滑条→生效预览联动 / 应用写 hero 表 12/75 / 重置回基线 6/50 / 批量模拟 N=10 全链路〔与 headless 扫描同口径〕/ 关闭回开始界面 / 加成跨模式持久)。
|
||||
13. **呈现层模块化重构(2026-09-11 用户批——"上帝对象"拆分)**:`main.gd` 随功能追加长成 ~1100 行上帝对象(棋盘渲染/HUD/日志/四种覆盖层/调整板/模拟器/关卡流程全糊在一个类)。拆分为 **9 个单一职责单元**(总行数不变,最大单类 433 行):`views/board_view.gd`(棋盘渲染+输入意图翻译,动作经信号外发)· `views/hud_view.gd`(侧栏纯视图,setter+定长槽)· `hud_presenter.gd`(核心状态→文案装配,MVP 静态装配器)· `views/battle_log_view.gd`(13 类行协议格式化)· `views/item_window_overlay.gd`(窗口,选择=位置语义收口)· `views/node_overlay.gd` · `views/level_strip_view.gd` · `views/tune_panel.gd`(调整板自持加成语义+批量模拟)· `views/ui_kit.gd`(共享小部件)。**组合根纪律**:main.gd(433 行)= 模式流转 + 信号接线,核心写调用(settle_batch/submit_window_selection/runner 推进)只在此处发生;视图零核心写、装配器无状态。**回归验证**:冒烟 74/74 + ui_probe/ui_tune_probe PASS + 行为探针 2 种子(通关 + 选用生效 1/2 失效 0)。
|
||||
|
||||
## 切片调参登记 v2(2026-09-12 起已由 C3 GDD 转正为 MVP 默认曲线——下方登记保留作溯源)
|
||||
|
||||
**EnemyTable L1 深度轴(D0–D4,Boss=Dmax=4——A1 第三轴登记约定)**,设计意图 = 基线难、天赋拉回可玩带:
|
||||
|
||||
| 节点 | 行 | 敌种×数量 | hp | atk | 环 |
|
||||
|---|---|---|---|---|---|
|
||||
| 战1 | (1,0) | 蛆虫×1 | 24 | 4 | [attack] |
|
||||
| 战2 | (1,1) | 蛆虫群×2 | 24 | 4 | [attack] |
|
||||
| 战3 | (1,2) | 伏击兽×1 | 36 | 6 | [attack,charge,heavy] |
|
||||
| 战4 | (1,3) | 伏击兽群×2 | 26 | 4 | [attack,charge,heavy] |
|
||||
| Boss | (1,4) | 遗迹守卫×1 | 130 | 7 | [attack,attack,charge,heavy] |
|
||||
|
||||
首版(敌 HP 总量 208:18/36/26/40/88)实测**基线通关率 73%**——预警⑥速杀退化在关卡尺度依旧成立,与"基线非常难"意图相反;v2 上调至 290(24/48/36/52/130)+ Boss atk 7。**群战对价**:unit_count=2 行单位 hp/atk 下调(B1 条款③满配语境约束;C3 正式调参须按 clause3_supply 复算——registry 已登记 unit_count>1 行须降密度或降 m_amb)。
|
||||
|
||||
**BoardGenTable L1 深度行**:D0/D1 = 10×10、D2/D3/D4 = 12×12,全部 d0.25/t0.88。**登记**:d0.28/t0.87 首测**不达标**(best≈0.805–0.817,降级告警"接受最优出图")——密度轴上调与求解器能力一起解,留给 C3。
|
||||
|
||||
**奖励获取期望实测(问题2 汇报批,工具 `smoke/reward_expectation.gd`,N=200 板/深度,走真实 B4 roll 管线含 F5 保底条件化)**:
|
||||
|
||||
| 深度 | 板面 | 资格格均(min/max) | 可选类/板 均 | 其中刀 | 治疗包 |
|
||||
|---|---|---|---|---|---|
|
||||
| D0 | 10×10 | 12.4(1/36) | 3.32 | 2.57 | 0.75 |
|
||||
| D1 | 10×10 | 11.1(2/32) | 3.00 | 2.40 | 0.60 |
|
||||
| D2 | 12×12 | 22.8(4/63) | 5.70 | 4.42 | 1.27 |
|
||||
| D3 | 12×12 | 23.8(5/61) | 5.87 | 4.55 | 1.31 |
|
||||
| D4 | 12×12 | 24.1(6/64) | 5.87 | 4.54 | 1.33 |
|
||||
|
||||
**公式**:每资格格 P(可选类) = 25/100 = 25%(小刀 15% / 精工短刀 4% / 治疗包 6%);每板期望 ≈ 25%×|资格格| + F5 保底条件化加成(k_dmg=1 整批重 roll 至少 1 刀——12×12 板无刀概率 0.81²³<1%,加成可忽略;10×10 板 ≈+0.2);清图奖励 clear_knife 仅真清图兑付(理性策略不整清 ≈0);完美排雷 ×2 条件苛刻不计基线;**节点奖励恒为 1 件可选类**。
|
||||
|
||||
**每关合计 ≈ 23.8(板)+ 1(节点奖励)≈ 24.8 件可选类**(≈18.5 把刀 + ≈5.3 治疗包)——刀总伤 ≈180,超过 Boss 单体 130 HP。**用户"道具获取期望太高"的定量坐实**:关内不清库(2026-09-11 裁决③)× 每格必有货(B4 锁定)× 12×12 深板资格格翻倍,三者相乘。**候选杠杆(全部 C3 所有权,等拍板再动)**:① 可选类占比 25%→8–12%(改 knife_t1/knife_adv/heal_pack 权重)② 敌 HP 曲线上调对冲 ③ 刀伤下调 ④ 库存上限(触碰 B4 E 节结构,需修订)。
|
||||
|
||||
**期望扫描数据(N=20/格,种子 4200+20,工具 level_monte_carlo.gd;理性玩家策略近似)**:
|
||||
|
||||
| atk% / hp% | 通关率 | 整关承伤 均/p90 | 通关剩余HP均(胜) | 进BossHP均 | 平均轮数 | 败北节点分布 |
|
||||
|---|---|---|---|---|---|---|
|
||||
| +0 / +0(基线) | **55%** | 126.8 / 190 | 33.1 | 44.6 | 18.4 | 蛆虫群×4 · 伏击兽×2 · 伏击兽群×3 |
|
||||
| +0 / +100 | 100% | 165.8 / 219 | 61.5 | 88.7 | 26.1 | — |
|
||||
| +50 / +0 | 70% | 109.8 / 155 | 27.1 | 45.5 | 17.1 | 分散(含 Boss×1) |
|
||||
| +100 / +0 | **100%** | 96.3 / 121 | 33.5 | 47.4 | 17.4 | — |
|
||||
| +200 / +0 | 95% | 61.1 / 99 | 36.6 | 49.0 | 12.9 | 伏击兽群×1 |
|
||||
|
||||
读法:**基线=明显吃紧(55%,死亡集中在前中期群战)→ 攻击+100% 即满通关带**;HP 翻倍是独立有效的第二杠杆(战斗拖长 → 承伤期望上涨 165 但预算翻倍)。v1 敌数值(HP 208)时基线通关 73%——预警⑥速杀退化在关卡尺度坐实,v2(HP 290)压到 55%。注意:模拟者 = 理性策略近似(免费链式开格、无避雷推理),真人基线通关率应显著低于 55%——**目标带定在哪儿(30%?10%?)待 playtest 拍板,杠杆 = EnemyTable hp/atk 深度曲线(C3)**。
|
||||
|
||||
## 已知热点(playtest 观察项)
|
||||
|
||||
- **基线难度体感**:基线(无面板加成)应"明显吃紧、可能打不过";如果基线就能轻松通关 → 敌数值仍偏弱(找 C3 杠杆:EnemyTable hp/atk 曲线);如果 +100% 攻击还打不过 → 奖励供给或恢复点数值偏弱。
|
||||
- **奖励点随机主动道具的体感**:roll 池 = 小刀 8 / 精工短刀 16 / 治疗包 12(等权);到手的刀是否改变了 Boss 战规划(囤刀 vs 即用)。
|
||||
- **恢复点(30% max_hp)时机**:位于战4 与 Boss 之间——是否制造"要不要省治疗包给 Boss"的决策。
|
||||
- **Boss 战节拍**:遗迹守卫环 [attack,attack,charge,heavy]——蓄力拍是否被玩家用作"安全挖掘拍";重击 14 点是否构成规划压力。
|
||||
- 节点进度条/奖励恢复覆盖层可读性;v1 遗留观测项(结算逐行归因、完美排雷可达性)继续有效(原"雾池体感"一项随雾格移除作废)。
|
||||
|
||||
## L2 试玩采集清单(分支节点图批 2026-09-17——关卡 2「幽暗地域」试玩观测项)
|
||||
|
||||
入口:「开始冒险」主入口推进到第二关段(单关测试直入口已退役——2026-09-23 用户拍板,唯一试玩入口 = 完整三关冒险;种子框同用)。
|
||||
|
||||
- **选路行为**:战斗胜利/兑付确认后弹出选路覆盖层(整图 + 候选按钮)——玩家是否理解"零自动推进、每跳都要选";单候选节点(恢复点→编号 6)是否被读成"卡住"。
|
||||
- **岔路使用率**:四处真实岔路的走向分布(入口 0→[1,2];战斗 1→[3,4];奖励 2→[4,5];深层 4→[6,7])——奖励点路线 vs 纯战斗路线的选择比例;整图渲染是否被实际用来规划(对比只点按钮的玩家)。
|
||||
- **新敌种可读性**:熊地精(突刺/重击池)的意图可读性;巨蜘蛛群行(2–3 只)的数量压力体感;蛛化卓尔精灵 Boss 的毒爆(×2.5)与重击(×2.0)是否构成规划差异(对照关卡 1 酋长的硬化池)。
|
||||
- **顶栏图条**:图形态行进序列(已过暗金 → 当前金 → 候选亮边)是否足以反映位置;编号显示(节点编号 = 关内地址)是否被理解。
|
||||
- **数值读数参照**(provisional,N=20 双策略扫描 2026-09-17):基线裸值通关 0%(死因集中 Boss×16);hp+100% 档 35% → atk+150%/hp+100% 档 80%——真人试玩体感与读数的偏差即试玩采集的主要增量。
|
||||
|
||||
## 2026-09-11 用户拍板(四条,已落地本切片)
|
||||
|
||||
| # | 拍板 | 切片实现落点 |
|
||||
|---|---|---|
|
||||
| ① | 基础攻击恒存在:atk 域 [1,999]、0 拒绝加载、MVP=6;战斗段主角先手攻击顺位最前敌,attack_up 唯一吃修正 | `hero.tres` atk=6 · `hero_table.gd` 域校验 · B1 战斗段⓪先手(`hero_attack` 行);attack_up 经 `effect_pool_map → hero_offense` 活行 |
|
||||
| ② | 奖励两类:即时 75%(当场兑付不进库存)/ 可选 25%(刀/治疗包入库走窗口);heal_pack 稀缺化 w6 p12 | `reward.tres` 行集 w75/25;B1 `_resolve_instant` |
|
||||
| ③ | 库存关内延续:跨战斗不清,关卡末 level_clear 清空 | B1 `_battle_over` 不清库;v2 起由 C1 runner 在关卡末统一调用 |
|
||||
| ④ | 道具窗口至多选一件 | B1 `submit_window_selection` 截断 `slice(0, 1)` |
|
||||
|
||||
**文档对齐**:四条拍板的 GDD/registry 回写已由文档会话先行完成(b1/b2/b4 GDD、registry、systems-index、d2、game-concept、b3);本切片程序与文档逐项对齐核查通过(详见 session-state 条目 16)。语义注:本切片为参考实现,与文档冲突以 GDD/registry 为准。
|
||||
|
||||
|
||||
## 2026-09-12 试玩日志器 + 诊断转储(playtest 数据采集基建)
|
||||
|
||||
**新增**:① **`PlaytestLogger`**(呈现层组件)——订阅 B1 新信号 `settlement_snapshot`(每结算末尾全量状态:关卡/深度/轮次/verdict/英雄/敌人/行动经济/板序号统计/buff/库存/本轮 facts)+ battle_finished/node_entered/level_finished,逐行写 **JSONL**:`user://logs/playtest_<seed>_<时间戳>.jsonl`(每行一个完整状态,直接可喂 pandas);② **诊断转储按钮**(侧栏底部"🩺 诊断转储")——卡死时点击,全量核心状态(B1 相位机/计数器/facts、A2 棋盘、A3、B4、runner)+ 最近 200 结算行/100 操作环形缓冲 → `user://logs/diag_*.json`(行为探针 _dump_state 的产品化)。
|
||||
|
||||
**顺手修复(日志首秀即抓真 bug)**:_start_full_level 面板加成**双写叠加**——tune_panel.apply() 先写表(6→18),runner.set_hero_override 再以 18 为基线 ×3(→54)。修复 = 关卡模式单一应用路径(runner 写表),日志快照 atk 54→18 证实。**教训(规范 5.x 补充案例)**:同一加成的多应用路径必须单一事实源;JSONL 快照是数值回归的最低成本探测器。
|
||||
|
||||
**冒烟 103/103**(+7:快照信号/字段齐备/板序号/JSONL 解析/诊断文件)。日志目录:`%APPDATA%/Godot/app_userdata/DungeonSweeperVerticalSlice/logs/`。
|
||||
|
||||
## 2026-09-12 C3 GDD 落地 + 试玩采集清单
|
||||
|
||||
**落地**:C3 GDD(`design/gdd/c3-difficulty-curve.md`,solo 出稿未评审)的设计承诺落回切片——① **DifficultyTable 结构位**(schema + 空表 + ConfigDB 挂载 + B2.spawn 套乘钩子:行值快照前施加、越域 clamp 至 [0.1,10];MVP 空表零行为——AC-10 冒烟断言含注入/越域验证);② 切片调参登记(敌深度轴/板参数/供给参数)**转正为 MVP 默认曲线**——切片当前数值 = C3 GDD 背书的设计态,试玩即体验 C3 定稿难度。冒烟 96/96。
|
||||
|
||||
**试玩数据采集清单(反哺 C3 六个 OQ——试玩时请记录)**:
|
||||
1. **OQ3 真人修正系数(首要)**:你无面板加成打第一关的通关/败北结果(模拟基线 80% 通关——真人预期显著更低,落差就是修正系数)。
|
||||
2. **OQ2 Boss 血量**:Boss 战实际打了多少轮、是否觉得血量压迫合理还是拖沓(模拟值 130HP 占比 0.81 越带)。
|
||||
3. **OQ6 轮内刷新**:你实际用了几次"弃图刷新"、是否觉得"挖-刷-挖"太顺滑(免结算=敌人不出手)。
|
||||
4. **OQ4 弃图倾向**:有没有"挖一半就想弃图刷 RAP"的冲动(模拟测不到真人策略)。
|
||||
5. **难度台阶可感知性**:D2 伏击兽/D4 Boss 相比 D0 蛆虫的变强是否有具体面孔(新敌种=新图像线索)。
|
||||
6. **奖励供给体感**:可选类道具(刀/治疗包)获取频率与囤积感受(供给控制器批后每关 ≈4.5–6.5 件)。
|
||||
|
||||
试玩方式:F5 → 「▶ 开始冒险」基线打一局(一条命三关连探——冒险结构实现批 2026-09-21 起,第一关读数即冒险首关) → 调整板 +100% 攻击再打一局(对照"数值提升→通关期望改善"的元进度体感)。
|
||||
|
||||
|
||||
## 2026-09-12 试玩批次 1 → v3 数值批(F7 流程实践)
|
||||
|
||||
**试玩反馈(v2 数值)**:满血全通关(三策略均通)/ Boss 血量 OK 或可上调 / 刷新零使用 / 零弃图倾向 / 治疗包零使用 / DND 命名指令(地精/熊地精/食人魔酋长)。
|
||||
|
||||
**v3 数值批**:atk 4/4/6/4/7 → **7/5/9/6/9**(承伤主杠杆);D0 goblin 24 保持(20 会被首轮粉尘秒杀——冒烟封印回归断言抓获)、D1 群 13×2、D2 bugbear 40/9、D3 群 26×2、**Boss ogre_chief 150/9**(用户拍板可上调);L2 锚行同步。**DND 命名**:goblin 地精 → bugbear 熊地精(DND 原型即伏击手)→ ogre_chief 食人魔酋长;enemy_id 同步更名。
|
||||
|
||||
**v3 扫描对照 F5**:基线 **60%**(带 40–60% 上沿 ✓,v2 80%)· 败北 4/6 集中 Boss(压迫预期兑现)· +50%→87% / +100%→93% / +200%→100%(天赋曲线成立)· 承伤 90(v2 80)。C3 GDD 回写:F3 带 [0.5,1.1](OQ2 裁决)、OQ2/3/4/6 试玩读数、试玩批次记录。
|
||||
|
||||
**冒烟 103/103**(封印回归断言改用 Boss 行——D0 20HP 级被盲挖粉尘秒杀无法承载场景;DifficultyTable 断言对齐 v3 行值)。**下一批 = v3 真人试玩**(OQ3 定量/OQ2 终裁)。
|
||||
|
||||
## 2026-09-12 遭遇随机化(Roguelike 局感引入批——Peglin 对比报告落地项)
|
||||
|
||||
**触发**:用户试玩反馈"关卡完全没有 Roguelike 的体现"→ Peglin 对比报告(`design/research/peglin-comparison-2026-09-12.md`)→ 用户拍板先做遭遇随机化(节点分支/多波/遗物协同后置)。
|
||||
|
||||
**设计**(C3 GDD Core Rule 8):EnemyTable 行 `unit_count` 升级为**区间语义** `[unit_count, unit_count_max]`(新字段可省略=定值,旧行零迁移);B1.begin_battle 实例化期按区间均匀骰,**RngService 新增 `encounter` 封闭流**(registry 接口漂移登记——挖掘决策域之外 P1 合规);**B2 零随机纪律保持**(spawn 收 override 即确定值);同种子全程可回放;新敌种首遇节点固定数量(教学位不加骰)。
|
||||
|
||||
**L1 区间**:地精 [1,2] · 地精一伙 [2,3](hp 13)· 熊地精固定(教学位)· 熊地精双雄 [2,3](hp 18)· Boss 固定——期望总量 ≈304(+8%),F6 带内。
|
||||
|
||||
**扫描(N=15)**:基线通关 **47%**(区间期望总量上移 +8% 的叠加效应——F5 带 40-60% 带内 ✓);+50% → 67%;败北分散(Boss×3/战1×4/战3×1)。**注意**:区间骰使难度分布更宽(运气成分进入遭遇层——Roguelike 体的核心),真人体感待批次 2。
|
||||
|
||||
**冒烟 149/149**(+46:区间双值出现/全域内/同种子可回放/D2 教学位固定)。
|
||||
|
||||
## 2026-09-12 试玩批次 1 → v3 数值批(F7 流程实践)
|
||||
|
||||
## 2026-09-12 攻击类型 × 护甲克制批(用户拍板——三段伤害 + 标错惩罚落地)
|
||||
|
||||
**触发**:设计会话(本日)定稿"怪物护甲类型(轻甲/魔法护甲/重甲)× 角色伤害三段构成(普通/穿刺/魔法)"的克制对策系统(穿甲弹/高爆弹式三角)+ 标错封印随机惩罚。(本段写于 2026-09-12,原文使用位置名 无甲/护甲A/护甲B 与 基础/伤害A/伤害B——2026-09-15 命名统一批已全仓改为语义名,映射见下方该批小节。)设定表经用户多轮审阅确认"没有问题"后落程序(GDD 正式修订另批进行——本批以表格结论为实现依据)。
|
||||
|
||||
**拍板结构 → 落点**:
|
||||
|
||||
| 设定 | 实现落点 |
|
||||
|---|---|
|
||||
| 敌人护甲类型:轻甲 / 魔法护甲 / 重甲 | EnemyTable 新字段 `armor_type`({light,ward,heavy},缺省 light 向后兼容)· 映射:goblin=light / bugbear=ward / ogre_chief=heavy(教学梯度候选,provisional)· B2.spawn 行值快照入单位字典 |
|
||||
| 伤害单分量(2026-09-19 充能退役) | ~~B1 三分量~~ 主角攻击 = 单一基础分量:量 = atk(表值)、类型 = hero.attack_type(营地换型 = 改字段);hero_attack 行携带实际攻击类型键 + roll/ac/tier 掷点读数(P2 归因)。**攻击类型是伤害的属性轴,与来源无关**(2026-09-15 抽象分离批纪律保留) |
|
||||
| ~~充能供给~~(2026-09-19 退役) | SettlementTable 字段 `charge_grants` 已删除——挖掘攒弹药的第二伤害供给取消,克制应对 = 冒险前配型(hero.attack_type)+ 棋盘火力(穿刺弹/魔法矢直伤道具) |
|
||||
| 克制矩阵:克制 ×150 / 中性 ×100 / 被克 ×50 | SettlementTable 新字段 `armor_effectiveness`({normal,pierce,magic}×{light,ward,heavy} percent,缺项回退 100)——刀/AOE/清图奖励**恒定表值不吃克制**(道具=万能稳定伤害,暂定待拍板) |
|
||||
| 标错封印 → 随机轻惩罚三选一(时效 1 结算) | `_apply_miss_backlash()`:① `enrage` 暴走(敌 +25% 进 enemy_output 池)② 敌方伏击 +2 层(复用 ambush 行)③ `off_guard` 失衡(我方 +25% 进 hero_defense 池 = 多挨打);新 B3 行 ×2(dur=1 与 weaken/ambush 同款先兑付后衰减);沿用 `_seal_miss_seen` 每危险格实例一次去重 |
|
||||
| 随机载体 | **零新流确定性派生**:(x×7 + y×13 + 板序号×5) mod 3——同种子可回放、不扰动既有三条封闭流取号序;正式侧候选 = RngService 新增封闭流(待拍板) |
|
||||
| 呈现 | 敌人卡环点行常驻护甲标签(轻甲/魔法护甲/重甲——克制对策可读前提)· 结算明细主角攻击行拆"(普通 X + 穿刺 Y + 魔法 Z)" · 反噬效果行入明细流 · settlement_snapshot 增 armor/charge 字段(JSONL 数值分析面) |
|
||||
|
||||
**数值全部 provisional 待拍板/调参**(登记于 enemy.tres / settlement.tres 注释):~~charge_grants~~(2026-09-19 退役)· ~~矩阵 150/100/50~~(加法核 +1/0/−1 换装)· enrage/off_guard 25% · 伏击 +2 层 · 护甲映射 goblin/bugbear/ogre——C3 调参时按 F6 供给平衡式复算(三段充能是新增伤害供给,裸装败北/基线通关率两条冒烟路径已复验仍成立)。
|
||||
|
||||
**正式侧义务(GDD/registry 修订清单,待文档批)**:B2 GDD(EnemyTable armor_type 字段 + schema)、B1 GDD(F3 主角攻击三分量构成 + seal_miss 反噬编排 + 克制矩阵载体)、B3 GDD(六行 MVP 状态表 + source_class seal_miss 授予)、A2 OQ1(标错惩罚候选升级为三选一随机池 + 重复性口径=每危险格实例一次)、registry(enemy/settlement_table/status_effect 条目 + A3 条款②锚与条款③不等式重推导——封印命中收益从纯 weaken 变"weaken+魔法 充能")、C3(数值 provisional 接管)。lore 侧:`design/research/lore-nest-metaphor-2026-09-12.md`(穿刺=地利/魔法=封印暴露、反噬=封印失败——待新会话拍板后写回 game-concept)。
|
||||
|
||||
**验证**:冒烟 **161/161**(+12:护甲快照三档传播 / 克制矩阵三行表值 / hero_attack 行三分量字段〔全战斗回放 ×2〕/ 标错封印 seal_miss+反噬效果行 / 标中雷充能进主角攻击 / 反噬派生同种子回放)· ui_probe PASS · ui_tune_probe PASS · ui_click_probe(done=true、选用失效=0、问题=0)。
|
||||
|
||||
## 2026-09-12 战斗屏五区改造批(battle.md 落地——ui-programmer 批)
|
||||
|
||||
**背景**:`design/ux/battle.md`(/ux-review APPROVED)+ `design/ux/interaction-patterns.md`(核心 8 pattern)落地到切片呈现层。**core/ 与 data/ 零改动**(本批纯 presentation/ + smoke/)。
|
||||
|
||||
**五区布局**(1280×800 基座):A 区 28px 顶栏关卡进度条(`level_strip_view.gd` 改造:暗金已过/奖励金当前高亮);B 区 252px 战斗画面(`battle_stage_view.gd` 新建 = `hero_card_view.gd` + 轮次·阶段指示 + 兑现演出区 + `enemy_card_view.gd` ×1–3〔顺位徽标/意图徽标常驻/环点拍数/伏击共享池/死亡灰化+碎裂残位禁瞬消〕+ 底部结算明细带);C 区 240px `hero_info_panel_view.gd`(直角细线数据框 + 修正来源角标 + RAP 点阵 + `status_slot_row.gd` ×4〔形状+色相双通道/层数角标/剩余结算点/悬停 tooltip 来源+语义+剩余/空态槽隐藏〕);D 区 `board_zone_view.gd`(`ap_bar_view.gd` 44px〔AP 点阵+停手居左;图例+弃图刷新居右——确认 chips 异位 pattern 7〕+ `board_view.gd` 改造〔悬停三语义自绘光标/目标格边框/BFS 路径蓝点预览/可挖域 8 向呼吸微光/奖励徽章 R₀ 域外/去饱和 shader〕+ `toast_stack.gd` 2.5s 消退 + `board_fx_view.gd` 演出层)+ `desaturation.gdshader`(去饱和+金橙偏移、明度不变);E 区 280px `inventory_panel_view.gd`(常读列表固定槽高 + 原位窗口态 pattern 2/8:描边金光/标题切换/时机指示/usable_now 禁选+原因/跳过/政策注记;属性简述投影、来源不呈现)。**旧 `hud_view.gd` 删除**(HUDView 职责由 B/C/D 三区视图承接)。
|
||||
|
||||
**全屏 States 与模态**:`legend_overlay.gd`(图例,状态槽空态说明)/ `settlement_review_overlay.gd`(明细回看)/ `battle_end_overlay.gd`(胜=金蜡封落于棋盘中央+判定行+回看入口;败=暮蓝 #5A6C8C+封印化光点收回)——统一模态登记序管理(`_open_modal` 打开时显式隐藏上方遮挡层,缺陷⑤纪律)。清图金粒喷泉+彩纸 / 完美排雷金光带+横幅 / 封印命中加重弹道 / Boss 紫赤影子爬棋盘 / 零合法板诚实 toast / chord·非相邻一次性提示(每 run 去重,`ux_hint_shown` 遥测)。**发现并修复历史 quirk**:TunePanel 默认 `visible=true` 靠被不透明开始层遮蔽的树序巧合——已显式初始化 false(缺陷⑤家族)。
|
||||
|
||||
**遥测**(playtest_logger 增 ux 行族,呈现层观测不改 core):`ux_round_input_ratio`(原始输入:AP 消耗,快照驱动)/ `ux_round_exit{path_tag}`(快照 round_facts.path——呈现层只订 B1 的接线纪律下唯一合法源)/ `ux_refresh_attempt{result, round_refresh_index}` / `ux_window_choice` / `ux_reject{source, sub_reason}` / `ux_hint_shown{hint_id}`。
|
||||
|
||||
**探针**:ui_probe 增五区构建断言;ui_click_probe 增刷新确认链/图例开关/tooltip 触发/模态遮挡断言(图例/回看/窗口态/终局)/窗口期去饱和断言/首格可点计时(进入战斗屏口径 133–151ms ≤500ms AC;场景冷启动 ~700ms 为脚本首载编译域,观测项)——既有全部断言保留。** Waiting 刷新代理已按 OQ12 退役**:UI 刷新按钮仅挖掘期 Active 受理(presenter 门禁),原探针"窗口期点刷新"行为由 Active 确认链测试替代。
|
||||
|
||||
**回归**:smoke_all 149/149 · ui_probe PASS · ui_tune_probe PASS · ui_click_probe PASS(PROBE_SEED 7/11/23/99/42/77 六种子 done=true、选用失效=0、问题=0)。
|
||||
|
||||
**占位登记**:敌人意图视觉映射按 enemy_id 程序化占位(资产定稿归 B5);D区演出(喷泉/彩纸/弹道/影子)全部程序化占位;CJK 字体依赖系统回退(Windows 可用,随包字体归 D1 正式化硬性前置——2026-09-12 定);金蜡封/暮蓝氛围为色块占位级。
|
||||
|
||||
**2026-09-15 补记(雾格移除)**:雾格机制已整体移除(见[不做清单](../../design/gdd/05-不做清单.md))——本节五区规格里的已开雾格 tint 残留、雾区全开一次性信号、图例「迷雾 = 无雷盲区」条目与雾首遇提示已随机制同批从实现与探针中删除(现行格态收窄为已开 / 未开两态,一次性提示只余 chord 与非相邻两项)。本文以上各历史批次条目保留当时陈述,现行实现与呈现义务以[战斗屏规格](../../design/ux/battle.md)与 [HUD框架](../../design/gdd/systems/表现与支撑/HUD框架.md)等现行文档为准。
|
||||
|
||||
## 2026-09-12 呈现层 code-review 修复批(/code-review CHANGES REQUIRED → 全项清偿)
|
||||
|
||||
**触发**:全切片 53 脚本 ~9400 行 /code-review(AGENTS.md 代码规范 + 架构文档对照),裁决 CHANGES REQUIRED(7 项必改)。core/ 12 文件与 smoke 断言面整体健康;病灶集中于 tune_panel 封装破口、main.gd 结构回涨、三处呈现契约错位。
|
||||
|
||||
**BLOCKING 修复**:
|
||||
|
||||
| # | 缺陷 | 修复 |
|
||||
|---|---|---|
|
||||
| 1 | **TunePanel 视图写核心表**(`apply()` 直写 `_db.hero.atk/.max_hp`)+ `run_simulation` 内 `load("res://smoke/...")` 驱动 core——面板模拟按 pct² 口径、模拟后表值残留污染真实对局(runner 把放大值当基线再乘) | tune_panel 重构为纯视图(`apply_requested`/`reset_requested`/`simulate_requested` 信号外发 + setter 注入);加成落表收口组合根唯一路径 `_apply_hero_override`(每次从 `_ready` 捕获基线重算,幂等禁叠加);模拟编排拆 `tune_sim_controller.gd`(先复位基线再移交);报告文案归 `HudPresenter.tune_sim_report` |
|
||||
| 2 | **意图徽标双翻译**:presenter 先把 beat key 译中文投影、EnemyCardView 再按英文 key 匹配 → 色相/重击"▼"通道永久失效;`BEAT_NAMES` 两处重复 | presenter 直传 B2 原键(attack/charge/heavy);名/色/标记渲染映射唯一定义处 = EnemyCardView(set_intent 契约头注成文) |
|
||||
| 3 | **C 区 RAP 死分支**:`_refresh_economy` 从不投影 `a3.rap`,"RAP 滚动归零"反馈(A3 D1 义务)不可达;视图靠解析自身 label 文本当状态 | presenter 持续投影 `a3.rap`;`hero_info_panel_view` 改 `_last_rap` 成员存储,删除 `_rap_value()` 文本解析 |
|
||||
| 4 | **smoke_all 恒真断言**(`check(true, "全部计数在域内")`——越域也 PASS) | 改带分布标签的真实域内断言 |
|
||||
|
||||
**结构/封装修复**:
|
||||
|
||||
- **main.gd 761 → 667 行**(评审记录基线 433 行,400 行审视线):节点构建拆 `ui_assembler.gd`(只 new/add_child/布局,零接线零核心写);模态层级状态机拆 `modal_stack.gd`(缺陷⑤纪律载体);战斗流程叙事文案拆 `battle_narrator.gd`(兑现 main 头注"不拼呈现字符串");模拟编排拆 `tune_sim_controller.gd`。剩余行数 = 组合根规定的接线 + 模式流转 + 核心写调用唯一出口(再压需移动组合根规定职责,登记为 D1 正式化批候审)。
|
||||
- **level_runner core↔core 接线 lambda → 方法 Callable ×4**(静态规则 S6——lambda 捕获 self 延长 RefCounted 生命周期/成环)。
|
||||
- **B1 新增 `diagnostic_snapshot()` 诊断观测面**:组合根诊断转储与 click_probe 停滞诊断全部改走成文 API,零下划线私有字段直读(此前直读 8 个)。
|
||||
- **信号处理体去 lambda 化**:main 三处多行/单行 lambda(inter 层继续/单场入口/库存拒绝)改命名方法 + `bind`(规范 4.1)。
|
||||
- 顺带:`can_request_refresh()` 断行格式修复(原单行内嵌 tab 不可读);`_build_inter_overlay` 死参 `parent_root` 删除;`_start_battle` 重复 `_start_overlay.visible = false` 去重;调试两层入口改由 `_start_battle` 统一取加成后满血(原传参在加成前取表值)。
|
||||
|
||||
**探针同步(缺陷回归断言随契约更新——3.3)**:ui_tune_probe 重写——应用/重置走真实按钮信号路径(`press_apply_for_probe` 接缝——合成输入走真实翻译出口口径);**新增两条回归断言**:⑦模拟后 hero 表保持基线(无残留)⑧应用加成 + 启动完整关卡后 runner 写表值 = 基线 × 单次加成(12/75;旧缺陷表现 24/112 在此被抓获);ui_click_probe 停滞状态与诊断转储改走 `diagnostic_snapshot()`。
|
||||
|
||||
**验证**:smoke_all **141/141** · ui_probe PASS · ui_tune_probe PASS(含新回归⑦⑧)· ui_click_probe 六种子(7/11/23/99/42/77)全 PASS(done=true、选用失效=0、问题=0)。新增 4 脚本 `.uid` 经 headless 编辑器扫描生成(语言纪律⑤)。
|
||||
|
||||
**登记待办(非本批范围)**:自动玩家策略四份复制收口(评审 W-3);schema 溯源行补齐(W-9);S1–S8 静态扫描脚本化后 S1 自建 RNG 豁免口径登记(W-1);views 转发层信号契约头注补齐(W-2)。
|
||||
|
||||
## 2026-09-13 封装收口批(代码架构审查 E1–E5/O1–O5/P3 修复——接口正式化候选项的切片侧预收口)
|
||||
|
||||
**触发**:/architecture-review 改编批——以 architecture.md + code-conventions 为基线做代码组织/封装专项审查(报告:`代码架构评审(已退役,git 历史可查)`,Verdict: CONCERNS),用户拍板 P1+P2+P3 全修(P2 中涉接口漂移两项另出设计稿待审)。
|
||||
|
||||
**核心封装修复(P1)**:
|
||||
|
||||
| # | 项 | 修复 |
|
||||
|---|---|---|
|
||||
| E1 | **B1 协作对象全公开 → 呈现层链式穿透**(`b1.b2.hero`、`b1.a3.phase` 式层层下钻 main×18 / presenter×15 处) | a1/b2/b3/b4 私有化(a2/a3 保留公有 = 架构文档 API Boundaries 的 D1 契约面);新增 `presentation_snapshot()`(载荷 schema 头注成文)/`is_battle_live()`/`stock_count()`/`query_reward_identity()` 四个成文只读出口——diagnostic_snapshot 同款模式 |
|
||||
| E2 | **A2 状态字典公开且被视图绕过查询面直读**(board_view:278/303 直读 `.revealed`,头注自称"只读查询面"自相矛盾) | `board/revealed/seals` 私有化;新增 `current_board()`/`reveal_record()` 查询面;B1/组合根/探针全部改走命名面 |
|
||||
| E3 | **B2 裸字典容器公开**(`hero/enemies` 可被任意写;presenter 为死亡残位直读原始数组) | 私有化 + `hero_unit()`/`all_enemies()`(含死亡残位,句柄语义与 query_alive 同构)投影面;presenter 残位渲染需求收进 API |
|
||||
| E5 | **"只读"字段仅靠纪律**(phase/ap/rap/last_round_end/master_seed 架构文档定只读,实际公开可变) | getter-only 属性 + 下划线后备字段(外部读法 `a3.phase` 不变,写入仅剩方法通道——GDScript 层面机制化) |
|
||||
|
||||
**组织修复(P1/P2/P3)**:
|
||||
|
||||
- **O1/O2 调试模式抽离**:main.gd 调试分支(单场/连续两层/HP 携带/共享 B4)拆 `presentation/debug_session.gd`——调试语义单一实现点(O2 双实现漂移收口);组合根保留接线(battle_created 先于 begin_battle 发出,接线唯一归组合根 + 缺陷①时序义务)与终局 UI 决策。ui_probe 抓获初版时序缺陷(battle_ready 才接手 b1)——即缺陷①同款,battle_created 接手修复。
|
||||
- **O3 DamageMath 抽取**:B1 伤害公式族(F2 乘区/充能分量,零状态纯算术)拆 `core/damage_math.gd`(规范 1.2 工具函数挂宿主类);board_generator 同一合法板重复调用两次 measure_inferability 合并为一次(全库最贵求解器)。
|
||||
- **P3 清债**:头注溯源补齐 ×4(board_data/config_db/rng_service/reward_supply_controller——规范 1.4);B4 grant/grant_clear/stock/use 失败返回统一 `false`→`null`(E6 消费方 `is Dictionary` 判定天然兼容);equivalence_supply 表行 duplicate 防配置源泄漏;load()→preload/类名直引 ×3(main logger/runner、tune_sim autoplay)。
|
||||
|
||||
**加固(基线既有噪声根治)**:main.gd `_on_board_action` 的清图演出判定在 settle_batch 同步重入(结算→battle_over→关卡终局置空 b1)后仍摸 `b1.a2` → SCRIPT ERROR(基线 click_probe 每跑必现)——补 `b1 != null` 守卫,探针 stderr 归零。
|
||||
|
||||
**验证**:冒烟 **141/141**(与基线一致)· ui_probe PASS · ui_tune_probe PASS · ui_click_probe PASS(done=true、选用失效=0、问题=0,**SCRIPT ERROR 0**——优于基线)· 新增脚本 .uid 经 headless 编辑器扫描生成(语言纪律⑤)· presentation 层残留穿透引用 grep 归零(呈现面调用 19 处全部为命名 API)。
|
||||
|
||||
**登记待办(P2 设计稿待审——见审查报告 §4)**:E4 `db.hero` 表值运行时改写参数化(涉 B2.spawn 契约 = 接口漂移,须 GDD/registry 修订批);O4 自动玩家策略四份复制收口(与既有待办 W-3 合并)。
|
||||
|
||||
## 2026-09-13 P2 收口批(E4 表值参数化 + O4 自动玩家单一实现——用户拍板两项均做)
|
||||
|
||||
**E4(db.hero 表值运行时改写 → 参数显式传递)**:
|
||||
- `B2.spawn` 增 `atk_override/max_hp_override` 参数(-1 = 表值兜底;沿用 unit_count_override 先例;EC16 越界守卫改对生效上限判);
|
||||
- `B1.begin_battle` encounter 增 `hero_atk/hero_max_hp` 字段(缺省表值——冒烟直呼路径零改动),战斗段 `_hero_atk` 快照替代 `db.hero.atk` 读取;
|
||||
- `LevelRunner`:`start_level` 表写删除、生效值进 encounter、`restore_hero_base` 只清百分比(表值不可变后无基线可恢复)、`_base_*` 字段删除(直接读恒定的表);
|
||||
- main:`_apply_hero_override/_scaled_override` 表写路径废除 → `_debug_hero_override()` 生效值供给 Callable(面板百分比现算经 encounter 入战斗);`_on_tune_apply/_on_tune_simulate` 去复位步骤;`_db_base_*` 字段删除。
|
||||
- **收益**:双写叠加(6→18→54)与双基线同步整类隐患随表写移除;D2「单一事实源」原则机制化(表值运行期不可变)。
|
||||
- **登记义务**:registry architecture.yaml 新增 `hero_override_parameter_passing` 条目(接口漂移候选;B2.spawn 签名 +2 / B1 encounter +2 = 接口漂移,正式侧 B1/B2 GDD 修订随正式化批)。
|
||||
- **探针语义升级**:ui_tune_probe ⑦「表值残留」→「hero 表运行期不可变」(reintroduce 表写即 FAIL);⑧ 双写叠加回归改为完整关卡路径 + 呈现快照单次生效断言(12/75;旧缺陷形态 24/112 仍被抓获)。ui_probe 的 `_start_single` 语义注记:调试单场恒基线(入口清零滑条——原行为,非缺陷)。
|
||||
|
||||
**O4(自动玩家策略四份复制 → 单一实现,合并既有待办 W-3)**:
|
||||
- 新增 `smoke/auto_player.gd`:`choose_dig`(最近可挖格)+ `window_selection`(治疗优先于刀;差异参数化 heal_hp_div 2/3 · round_gate 0/3 · knife_compare best/sum——四调用点原语义逐一保留:smoke_all 3/3/best · monte_carlo 2/0/sum · dump_battle 3/3/sum · level_autoplay 2/3/best);
|
||||
- 四消费方删除各自的 `_choose_dig`/窗口策略块改走单一实现;click_probe 的 `level_autoplay._choose_dig` 引用同步改指 AutoPlayer;
|
||||
- **等价性证明**:smoke_all 同种子回放断言(步骤流逐行相同)重构后保持全绿。
|
||||
|
||||
**验证**:冒烟 **141/141** · ui_probe PASS · ui_tune_probe PASS(含 E4 新断言)· ui_click_probe PASS(done=true、选用失效=0、问题=0)。
|
||||
|
||||
|
||||
## 2026-09-13 零承伤根因分割探针 + playtest-09-12 Bug #1–#3 处置批(报告 Top3 授权任务)
|
||||
|
||||
**触发**:playtest-2026-09-12-designer 报告 Top 3 Priorities(探针分割 → Bug 建档修复 → 多种子复扫基线);数值拍板与设计变更不在授权范围(待拍板清单见文末)。
|
||||
|
||||
**① 零承伤根因分割(新探针 `smoke/damage_probe.gd`——boss/level 两模式)**:
|
||||
- **快照盲区发现**:settlement_snapshot 取在衰减段之后,时效 1 的 defense_up 当轮授予当轮衰减——真人局 JSONL `buffs[]` 恒空 ≠ 池为空(报告悬置的直接原因)。探针改从结算行(effect 行 pool_value)重建战斗段时点池值,池归属经 settlement.effect_pool_map 表驱动(零字面量)。
|
||||
- **Run A(fresh 池零挖掘裸验)**:Boss 攻击拍 d_tick = atk.total 全额兑现(attack 9/9 ×2 · charge 0 设计语义 · heavy 18/18),6 轮击倒英雄(defeat)——**「Boss/敌攻击链缺陷」否证**;拍名轮转与真人局逐拍一致(attack→attack→charge→heavy→attack)。
|
||||
- **Run B(自动挖掘墙行为)**:池 -30 → d_tick 21→14、池 -60 → 残余 ×0.40,墙轮承伤恒 >0;表值判定墙最深 -60 > pool_clamp_lo -100 = **归零不可达(09-13 调参批在当前数据上重证)**。
|
||||
- **结论**:真人局 12/12 轮零承伤 = 旧数据 defense_up -50×2 = 池触 clamp 下限 ×0(设计语义)——任何 atk 曲线乘在 ×0 上(原报「v3 主杠杆乘在 ×0 上」坐实)。报告 Bug #2 = 设计问题非 bug。
|
||||
|
||||
**② Bug #1–#3 建档与处置(BUG-0002/0003/0004,`production/qa/bugs/`,编号自 BUG-0001 顺延)**:
|
||||
- **BUG-0002(报告 #1 浅深度空遭遇)——Rejected as coded**:seed 42 当前构建全关回放复现同款遥测签名(d0 spawn=1 / d1 spawn=3 + round-1 victory + unit_count 0)但实为**首轮全灭**(英雄爆发 + 爆裂粉尘 instant_aoe 全歼低 HP 敌群;与 20:41 局 d1 双地精各剩 1 HP 逐点吻合);遭遇骰数学下限 ≥1(B1 roll_lo)+ B2.spawn count<1 fail-fast 双闸,100 场扫描零越域。**快照盲区修复**:settlement_snapshot 增 `spawn_count`(实例化总数含死亡残位)——unit_count=0 时「全灭 vs 空场」可判别,同类误读机制化排除。
|
||||
- **BUG-0003(报告 #2 Boss 攻击零兑现)——Rejected as coded**:根因见①;候选改法(clamp 收紧 / 乘法衰减 / 重击拍穿防)列待设计批。
|
||||
- **BUG-0004(报告 #3 grant_node_active 绕过 _supply.notify_granted)——Fixed**:stock 受理成功后补入账(与 grant() 同一「发放即获取」口径);计数器战斗域清零语义不变 = **零战斗数值影响**,纯计数口径修复(报告 Top3 #3 的上报义务执行)。
|
||||
- **回归断言(代码规范 3.3)**:smoke_all +7——快照 spawn_count 字段与等值断言;遭遇全深度 × 12 种子全域内扫描(60 场 spawn ≥1 且 ∈[unit_count, unit_count_max]);`_smoke_attack_chain_fresh_pool`(fresh 池非蓄力拍 d_tick == atk.total 全额兑现——攻击链健康钉死,未来真缺陷在此被抓);`_smoke_supply` ⑦ 节点奖励发放即入账。
|
||||
|
||||
**③ 多种子关卡复扫(Q1 调参批基线——level_monte_carlo 增 F4 两列「普通承伤/场% · Boss承伤%」:逐战斗承伤切分 = round 回卷分段 + 末段 Boss 判定(须抵达 Boss 节点))**:
|
||||
- N=30 × 10 档(种子 4200–4229;F7 切片样本量口径)。**结论:全档位越带**——F5 基线通关率 **17%**(带 40–60%)· +100% 档 43%(中天赋 ≥95%)· +200% 档 63%(高天赋 100%);F4 普通场承伤 **35.1–43.1%/场**(带 ≤20%)、Boss 场 **71.3–134.9%**(带 ≤50%)(hp+0 档;hp+100 档普通 13.9–22.6 带内 / Boss 35.8–76.7 仍多数越带)。拆墙(-50→-30 且权重 24→16)与 v3 atk 曲线叠加后,难度由严重不足摆向**严重过剩**。**本批未动任何数值**(敌 hp 曲线与奖励供给禁独立调参——registry 预警⑥联动义务;杠杆方向列待拍板,本基线 = F7 流程的测量输入)。
|
||||
|
||||
**验证**:冒烟 **148/148**(141 既有 + 7 新回归)· 三探针 PASS(ui_probe / ui_click_probe 6 种子 done=true、选用失效=0、问题=0 / ui_tune_probe)· damage_probe boss 模式 7/7 + level 模式(种子 42/43/44)4/4 全 PASS。
|
||||
|
||||
**待拍板/待设计批清单(本会话零数值变更、零 GDD 变更)**:
|
||||
1. **难度杠杆重拍板**(F4/F5 全档位越带)——候选:enemy atk 曲线回撤部分 v3 增量 / defense_up 权重回升 / heal 供给联动——归 C3 按 F7 流程(本基线为测量输入);
|
||||
2. **墙语义候选改法**(clamp 收紧至 -60/-70 / stack 乘法衰减 / heavy 拍穿防)→ C3 + /propagate-design-change(连带 B1 池 clamp 语义 + B3 GDD "-50" 文字同步);
|
||||
3. **供给量重拍板**:「每战斗首件保证」的总量语义(候选:关卡域首件)→ C3(BUG-0004 已封计数漏洞,关卡供给账目可完整观测);
|
||||
4. **浅深度首轮跳战可玩性**(autoplay/真人 d0-d3 round-1 全灭跳过战斗过程)→ C3 结合 F4 复验数据裁决。
|
||||
|
||||
|
||||
## 2026-09-13 拍板落地批(四项拍板——难度杠杆/墙语义/供给量/跳战)
|
||||
|
||||
**拍板结果**(用户四问裁决):①难度主杠杆 = **先真人试玩再拍**(F5 真人修正系数标定优先于数值调整);②墙语义 = **维持现状 + 归零护栏**;③供给量语义 = **先观测再拍**;④跳战 = **并入难度复测再议**。本批零玩法数值变更。
|
||||
|
||||
**落地变更**:
|
||||
- smoke_all 新增 `_smoke_wall_zero_guard`(表级不变式:`2×defense_up.magnitude > pool_clamp_lo`)——BUG-0003 事故类(叠满触池底 = ×0 全额免伤)永久免疫:任何未来 magnitude/clamp 调参重新造出 ×0 墙即冒烟红灯,强制显式重审;
|
||||
- level_monte_carlo 增「**跳战率**」列(普通战斗 round-1 即胜占比——拍板④观测面,此后每次复测自动携带);`_damage_segments` 升级为逐段 {承伤, r1_win} 结构;
|
||||
- defense_scan 输出标签修正:「墙轮承伤(应为 0)」→「(合法——-30 批墙帽 ×0.40)」(旧 -50 数据时代的期望文案过时清理)。
|
||||
|
||||
**跳战率基线(N=30,拍板④"再议"数据)**:基线 **8%** · +50% 13% · +100% 24% · +150% 27% · +200% 38%(普通场 round-1 全灭占比)。**seed 42(试玩固定种子)为高跳战轨迹**(autoplay d0–d3 四场全部 round-1 全灭)——真人局 d0–d3 全跳的体验含显著种子成分;基线 talent 下跳战是少数派,拍板④"复测再议"的数据前提成立。其余各列与 Q1 基线逐值一致(collect_rows=true 开启不改变行为——确定性交叉验证)。
|
||||
|
||||
**验证**:冒烟 **149/149**(+1 护栏)。
|
||||
|
||||
**下一步(用户侧)**:开一局真人试玩(任意种子;JSONL 自动落 user://logs/)。观测清单(服务拍板①③复议):通关与否 + 败北节点(F5 修正系数 = 真人通关率 ÷ 模拟 17%)· 普通场掉血体感(对照 ≤20% 带)· Boss 战撑过几轮 · d0–d3 是否发生跳战 · 治疗包是否出现缺血时刻与库存残留(拍板③数据)。事后由会话分析 JSONL 出修正系数。
|
||||
|
||||
## 2026-09-14 完美策略档测量批(OQ7 技能轴上界仪——用户拍板"增加完美策略档测试验证",零玩法数值变更)
|
||||
|
||||
**背景**:用户(扫雷强玩家)指出"完全正确开图/标雷、零踩雷 ⇒ 存活无压力"——现行扫描只有弱策略基线档(不封印/邻格盲挖/朴素选道具),技能轴不可测;registry `calibration_gap`(真人显著强于模拟者)此前只有方向无机制。本批 = 测量工具批。
|
||||
|
||||
**落地变更**:
|
||||
- `auto_player.gd` 增完美策略求解器(`perfect_action`/`_deduce`):single + subset 推理不动点(A1 F1 `measure_inferability` 同族规则集,**去 safe_set oracle**——约束未知邻格含危险格,比生成期闸保守);**零盲挖**(只挖已证安全格 = 构造保证零踩雷,扫描末断言兜底违例 quit(1))· **已证危险格全封印**(封印先于挖——轮末 weaken + 魔法 首次充能,雷上封印不阻连锁)· 卡死且资源可 → `b1.player_request_refresh()` 轮内刷新 · 否则停手(残 AP 1:1 入 RAP)。输入只经 A2 查询面(reveal_record/is_revealed/has_seal),**禁读棋盘雷区真值**——测推理上界,非全知作弊。core 层零改动;
|
||||
- `window_selection` 增 `heal_no_waste` 选项(缺口 < 治疗量不用;完美档启用)+ 完美档轮次门解除(round_gate=0);
|
||||
- `level_autoplay.gd` 增 `policy` 参数(默认 "normal"——tune_sim_controller/defense_scan/level_debug 既有调用零改动);完美档经 `policy="perfect"` 由 `_decide_act` 分派承载(原专职驱动 `_autoplay_battle_perfect` 已随样板收口并入分派——move/dig/seal 走 A2 settle_batch、refresh 走 B1 编排、连续拒绝 ≥3 停手防死循环);st 增 `mine_hits`/`refreshes` 计数(mine_hit 证据行埋点);
|
||||
- `level_monte_carlo.gd` 增第 3 CLI 参 `[policy]`(normal/perfect/both,缺省 normal 兼容既有文档命令)+ 策略/踩雷/刷新列;脚注 F5 带文案同步 [30,50] 带锚定稿。
|
||||
|
||||
**完美档基线(N=30 · 种子 4200–4229 · 耗时 597.6s)**:**0 加成通关率 100%**(弱策略基线档 60%)、通关剩余 HP 均 **47.4/50**、整关承伤均 3.5(p90 18.0)、普通场承伤 0.5%(带 ≤20%)、Boss 场 5.3%(带 ≤50%)、踩雷 0.00/局(断言 ✓)、刷新 0.23/局、跳战率 20%。+50%~+200% 全档位恒 100% 满血通关(+200% 承伤均 0.4)——**技能轴上界完全碾压现行曲线:F5 带与 F6 S/K 区间对完美档零分辨力**(区间模型无策略水平轴的结构性证实;calibration_gap 获得机制解释与定量上界)。
|
||||
|
||||
**验证**:冒烟 **149/149** 全绿(默认行为零变更——弱策略档既有断言全通过)。
|
||||
|
||||
**待拍板**:① 0 加成完美档通关率的目标带(现行 [30,50] 按弱策略口径定义——是否改为按完美档口径验收?);② 杠杆选择(已预登记:r_boss 走廊 180–207 / 群战上探 / decay 0.6;本批新增候选:"上限掺运气"系——供给量值随机化(power 区间化)/ 承伤硬地板——均待完美档复测验证效果)。
|
||||
|
||||
## 2026-09-14 伤害量值区间批(用户拍板②——"上限掺运气"两步全落地 + 完美档无效性实测,宽度 provisional)
|
||||
|
||||
**拍板**:验收口径改完美策略档(0 加成通关 ≤40%);杠杆 = 伤害值区间随机(上阶/下阶,范围内 roll);覆盖面 = 奖励侧五行先行、不足扩敌方侧(分步授权已用尽)。roll 时机 = 身份预分配批(E14 快照口径不变);中点恒等现值(A3 条款①当量锚不漂移);宽度 provisional 归测量迭代。
|
||||
|
||||
**落地变更**:
|
||||
- **第一步·奖励侧五行**(reward.tres + reward_table.gd schema + reward_economy.gd):heal_minor [2,6] / knife_t1 [5,11] / heal_pack [6,18] / knife_adv [10,22] / clear_blast [25,55]——power_min/power_max 可选配对字段(缺省定值行向后兼容,armor_type 先例);schema 校验 = 成对/buff 禁止/中点锚;roll 在 roll_board 身份批内**追加取号**(既有取号序零扰动)+ grant_node_active 同路径;快照 power = roll 值,B1/呈现零改动,授予期零随机纪律不变;
|
||||
- **第二步·敌方攻击侧**(enemy.tres 六行 + enemy_table.gd schema + unit_container.spawn + battle_orchestrator):atk [min,max] = 行值 ±3 provisional(d0 7[4,10] / d1 5[2,8] / d2 9[6,12] / d3 6[3,9] / Boss 9[6,12] / L2 7[4,10]);roll = 遭遇骰同流(encounter 恰一 scope,数量骰先、atk 骰后)spawn 期**整战持久**——每拍抖动否决(数字可读性锚),B2 零随机纪律保持(收 override 即确定值);spawn 签名 +1 参数(接口漂移登记,随正式化批);
|
||||
- smoke_all 新增 `_smoke_power_interval`(4 断言:五行中点锚/roll 域内含清图槽/定值行零外溢/节点路径域内)。
|
||||
|
||||
**实测(N=30,种子 4200–4229)**:
|
||||
- 奖励侧单独:完美档 0 加成 **100%**(承伤 3.5——与区间前逐值一致);基线档 17%(不变);
|
||||
- 敌方侧叠加:完美档 0 加成仍 **100%**(承伤 3.5→3.9 / p90 18→19——方差真实流动但被血缓冲全吞);剩余 HP 均 47.4 不变;
|
||||
- **验收结论:≤40% 线用数值区间杠杆族到不了**——完美玩家结构性优势(零踩雷 = 零直伤 + 全封印 = weaken −65% 顶格 + 快杀 = 敌出手次数最少)不受值域方差影响。三选待裁决:①承伤硬地板(每结算不可减免平伤——S_min 压缩,B1/B2 域);②weaken 饱和/递减语义(拆"全封印顶格"基石,B3/B1 域);③极端宽度(数值可用但毁基线档与可读性锚——建议否决)。**本批区间机制保留**(对普通玩家方差体验有效,中点锚不伤当量)。
|
||||
|
||||
**验证**:冒烟 **154/149+5 全绿**(区间批 5 新断言 + 既有全部回归含遭遇骰分布/回放)。
|
||||
|
||||
## 2026-09-15 攻击/护甲命名统一批(用户拍板"一并修改,统一称呼"——纯改名,数值零改动)
|
||||
|
||||
**触发**:GDD 评审(战斗结算)问题 1——位置键 `{base, dmg_a, dmg_b}` / `{none, a, b}` 不承载语义(a/b 是纯位置占位符:既说不出来源,也说不出来克制谁),且同一件事在仓内已分裂成四种写法(敌人系统 `{无, 甲, 重甲}` / 03-内容规模 `无甲/甲A/甲B` / registry `{none, a, b}` / 本 README `无甲/护甲A/护甲B`)。
|
||||
|
||||
**新词汇(全仓唯一现行词)**:
|
||||
|
||||
| 面 | 新 | 旧(作废) |
|
||||
|---|---|---|
|
||||
| 攻击类型(结算表矩阵行键 / hero_attack 行分量) | `normal` / `pierce` / `magic` = 普通 / 穿刺 / 魔法 | `base` / `dmg_a` / `dmg_b` = 基础 / 伤害A / 伤害B |
|
||||
| 护甲类型(`armor_type` / 矩阵列键) | `light` / `ward` / `heavy` = 轻甲 / 魔法护甲 / 重甲 | `none` / `a` / `b` = 无甲 / 护甲A / 护甲B |
|
||||
| 结算表字段 | `charge_grants`(触发源→{attack_type, per_unit, cap},同日第二批抽象分离后定型) | `damage_a_per_cell` / `damage_a_cap` / `damage_b_per_seal`("类型×来源"焊死命名;同名批曾短暂改 `pierce_per_cell` / `pierce_cap` / `magic_per_seal`,随即作废) |
|
||||
| 结算快照 round_facts | `charge_counts`(各触发源计数)+ `charge_points`(按攻击类型分桶点数) | `charge_dmg_a` / `charge_dmg_b`(既有 JSONL 留存文件仍用旧键) |
|
||||
|
||||
映射沿教学梯度:goblin `none`→`light`、bugbear `a`→`ward`、ogre_chief `b`→`heavy`;**矩阵数值逐格不动**(pierce→ward 150 / pierce→heavy 50 / magic→heavy 150 / magic→ward 50 / normal 全 100)。
|
||||
|
||||
**同日第二批 · 攻击类型与奖励抽象分离**(用户裁定"攻击类型的定义和格子奖励要分离开;开格|标雷 → 奖励;奖励如何定义是可以修改的;不要将两者绑定"):攻击类型 = 伤害的属性轴(普通 / 穿刺 / 魔法,只决定查甲效矩阵哪一行,定义里不含任何棋盘来源);充能 = 按攻击类型分桶的附加伤害储备,**谁能充、充哪个桶 = 奖励定义**(`charge_grants`;开格 / 封印命中 / 踩雷 / 清图一律只作奖励触发源)。原 `pierce_per_cell` / `magic_per_seal` 式命名(类型与来源焊在一根名字里)作废。冒烟新增三条断言:奖励定义在位 ×2 + 行为断言(把"封印命中"奖励改投穿刺桶 → 同一战斗魔法分量归零;还原后步骤流逐行相同——证明改道唯一入口是奖励定义)。
|
||||
|
||||
**落点**:GDD(战斗结算 v0.5 / 敌人系统 / 难度曲线与Boss / 03-内容规模)+ registry(`enemy.armor_type`、`settlement_table.armor_effectiveness` 与三字段 + 批次注记)+ 切片(`enemy_table.gd` `ARMOR_TYPES`、`settlement_table.gd` `ARMOR_KEYS` / `ATTACK_TYPE_KEYS`〔原 `COMPONENT_KEYS`〕、`enemy.tres`、`settlement.tres`、`damage_math.gd`、`unit_container.gd`、`battle_orchestrator.gd`、`enemy_card_view.gd`〔敌卡常驻标签改 轻甲 / 魔法护甲 / 重甲〕、`battle_log_view.gd`〔拆行文案改"(普通 X + 穿刺 Y + 魔法 Z)"〕、`smoke_all.gd` / `damage_probe.gd` / `auto_player.gd`);playtest 观测记录保留当时键名并附现名。
|
||||
|
||||
**验证(本批实跑)**:冒烟 **201 项检查 0 失败**(含分离批三条新断言:奖励定义在位 ×2 + 行为断言"封印命中奖励改投穿刺桶 → 魔法分量归零、还原后步骤流逐行相同")· `ui_probe` PASS · `ui_tune_probe` PASS · `ui_click_probe`(done=true、选用失效=0、问题=0)· `ui_layout_probe` PASS——四探针 exit=0、FAIL 行 0。命名批纯改名零行为变更;分离批为结构重构,数值链路等价(旧"计数×每单位×克制"与现行"计数→按奖励定义折算点数→×克制"结果相同,冒烟全量回归含回放断言未动)。
|
||||
|
||||
## 2026-09-15 验收 + 收口批(两批改动闭合核对;零数值零机制改动;不碰 v0.7 文件)
|
||||
|
||||
**背景**:2026-09-15 两批(攻击/护甲命名统一 v0.5 + 攻击类型/奖励抽象分离 v0.6)已完成;本批只读核对"文档口径 ↔ 切片实现"是否闭合,并收口遗留开口。**范围纪律**:`战斗结算.md` / `地图战斗资源.md` 属 v0.7 格面资源批所有者文件、口径仍在移动——本批不改;③ 收口项落在 `registry` + 本 README + `active.md`,GDD 侧只报告、待 v0.7 批整体收口后回填。
|
||||
|
||||
**核对结论(两批核心语义 9 项全闭合)**:`charge_grants` 键与值(safe_cell→pierce 1/格 cap 8 · seal_hit→magic 2/雷 cap 0)· 甲效矩阵 9 格(normal 全 100 / pierce→ward 150、heavy 50 / magic→heavy 150、ward 50)· `armor_type` 三值(light/ward/heavy)· `hero_attack` 行分量字段(normal/pierce/magic)· round_facts 键(`charge_counts`/`charge_points`)· 敌卡标签文案(轻甲/魔法护甲/重甲)· 明细行拆行文案"(普通 X + 穿刺 Y + 魔法 Z)"· 攻击类型定义不含来源 · 矩阵数值逐格不动——GDD/registry/tres/代码逐项一致。残留旧名扫描仅命中批次注记"原名"与历史观测记录;来源-类型硬绑扫描确认代码只剩两处计数埋点(`_charge_counts["safe_cell"]` / `["seal_hit"]`)+ `charge_grants` 键 + 行 `source_class` 归因。
|
||||
|
||||
**实跑(本批)**:冒烟 **201 项检查 0 失败**;四场景探针 `ui_probe` / `ui_tune_probe` / `ui_click_probe` / `ui_layout_probe` 全部 **exit=0、无 FAIL 行**。
|
||||
|
||||
**收口(③,落在非 GDD 文件)**:
|
||||
- ① **收益对照表充能当量覆盖缺口**:登记于本 README + registry + `active.md`(GDD 收益对照表回填待 v0.7 批收口后)。候选口径 = 充能当量 = Σ各桶点数 × 当轮目标护甲平均甲效倍率 ÷ 100(与直伤同类 1 伤害 = 1 当量,守 行动经济 条款①单一当量);补式 / 登记待拍板二选一待回填批裁定。
|
||||
- ② **两处已知落差登记**:a) `charge_grants` 触发源可扩展 vs 切片仅两个触发源计数埋点(新增源须加埋点);b) `safe_cell`/`seal_hit` 来源标识跨命名空间同名(明细行 `source_class` / `charge_grants` 键;RewardTable.pool_tag 已随格面资源批改 `cell`)。
|
||||
- ③ playtest 观测记录的快照键"现名"注记由中间名订正为现行名 `charge_counts`/`charge_points`。
|
||||
|
||||
**注意(口径移动)**:本批运行期间 **v0.7 格面资源批**已在 GDD(战斗结算 v0.7 / 地图战斗资源 v0.4)与 registry 落地——段序七→五、行型 15→10、极性路由取代三套固定兑付、可选类道具删除。切片代码尚未同步(registry 注明"切片代码修改随新会话执行、指导已产出"),故本文以上各历史批次条目保留当时陈述。
|
||||
|
||||
**两轴分离措辞不变**:攻击类型 = 伤害属性轴(定义不含来源);充能 = 按攻击类型分桶的附加伤害储备,投桶归奖励定义 `charge_grants`;开格/封印命中/踩雷/清图只作奖励触发源。
|
||||
|
||||
## 2026-09-15 格面资源批 · 批 1(数据面 + 极性路由——设计权威 = 地图战斗资源 v0.4 / 战斗结算 v0.7 / 排雷玩法 v0.3 / registry 2026-09-15 条目)
|
||||
|
||||
**模型一句话**:格子分 安全|雷|封锁格 三型;每个非封锁格(安全格 + 危险格,雷安同池同分布)装板期一次摇定格面资源类型(五型 attack/defense/heal/blast/weaken);极性不在资源内,由「动作 × 格型」矩阵决定——开安全格/标中雷 = 正向(奖励),开危险格/标错安全格 = 负向(惩罚),封印后撤封印零兑付;全部兑付在轮末授予段。可选类道具(刀/治疗包)与库存/供给控制器/道具窗口整体删除。
|
||||
|
||||
**数据面**:
|
||||
- `reward.tres` 结构性重写(`reward_table.gd` 同批):删 `category`/`use_timing` 字段与 CATEGORIES/USE_TIMINGS 常量;新增 `cell_type`(五型封闭枚举,清单外加载拒绝)× `polarity`(positive/negative)+ `stacks`(可省,缺省 1);`pool_tag` 收窄 {cell, clear_default}。行集 = 十一行:格面池正向五行 attack_up(w20,t2)/defense_up(w16,t2)/spark_aoe(flat 4,w16,t1)/heal_minor(flat [2,6],w15,t1)/weaken(w12,t1) + 负向五行 enrage(attack,t2)/off_guard(defense,t2)/heal_backlash(heal 直伤 flat 4,t1,无 effect_id)/ambush_double(blast 加层 stacks=2,t1,无 effect_id)/sapped(weaken,t1)——负向行 weight=0 纯兑付字典不参与摇号;clear_blast 保留(flat [25,55],w1,t2,clear_default)。加载校验族重写:五型 × 正负两行齐备(缺任一致命)/ cell 池 ≥1 正向行 weight>0 / percent 行 effect_id 必填(悬空校验在 ConfigDB)/ flat 行 power≥1(**例外 = blast 负向加层行,消费 stacks 不消费 power**——战斗结算 兑付形态表"伏击加层"独立形态的行级兑现)/ 区间配对 + 中点锚不变。
|
||||
- **删除三件**:`core/reward_supply_controller.gd`(可选类供给控制器——首件保证/指数递减整体下线)、`data/schemas/reward_config_table.gd`、`data/tables/reward_config.tres`(RewardConfigTable 随可选类删除废止——注入概率/按板保底参数失义)。
|
||||
- `status_effects.tres` 六行→七行:新增 **sapped(乏力——weaken 型负向:hero · −25 percent · dur 1 · max_stack 16 · stack**,hero_offense 池,attack_up 镜像;值 provisional 归 难度曲线与Boss)。
|
||||
- `settlement.tres`:`segment_order` 七段→五段 [grant, buff_debuff, combat, decay, verdict](schema 冻结常量同步);`effect_pool_map` 增 sapped→hero_offense;`source_label_map` 删 item 键(ConfigDB 必需键清单同步)。`charge_grants` 路径不动(开格→穿刺 1/格 cap8 · 封印命中→魔法 2/雷)。
|
||||
- `config_db.gd`:reward_config 出表清单;跨表校验族重写——旧 ②池空/③clear正当量并入 RewardTable.validate,旧 ④⑤⑨(k_dmg 保底/零伤害后闸/注入池护栏)随 reward_config 删除退役,保留 ①effect_id 悬空 / poolmap 悬空 / source_label_map 键集 / level 节点悬空。
|
||||
|
||||
**路由面(core)**:
|
||||
- `minesweeper_rules.gd`:cell_revealed 事件字段 `reward_eligible` → **`resource`**——initial 恒 false(封锁格),chain/dig 恒 true **含危险格**(雷安同池——旧"mine 恒 false"作废)。
|
||||
- `reward_economy.gd` 重写为纯格面资源身份面:roll_board 对全部非封锁格逐一摇定类型(摇号只用正向行权重 = 类型频次,合计 79)+ 清图批尾摇 clear_blast + 区间行 power 追加取号;query_identity 返回 {reward_id, cell_type, tier},未开格恒可查(危险格同权);grant/grant_clear 幂等保持;新增 `polarity_row(cell_type, polarity)`(类型化字典行查询面,拷贝出口)与静默 `is_mapped`/`phase` 只读出口;**删库存轴整族**(stock/use/query_inventory/in_stock_count/clear_inventory/grant_node_active/supply_granted_count/notify_battle_start);equivalence_supply 投影保留(tier_min_buff = 我侧 percent 正向行最低档 = 2)。
|
||||
- `battle_orchestrator.gd` 授予段改**极性路由**:结算队列(开格/封印判定/清图三批)逐格兑付——开安全格→该格 cell_type 正向行(charge_grants 计数埋点不变)、开危险格→负向行、标中→该危险格正向行(_seal_consumed 每雷实例一次去重不变,高光 seal_hit 保持)、标空→封印未中归因行 + 该安全格负向行(_seal_miss_seen 同键去重不变);**删旧三套固定兑付**(`_convert_ambush` 踩雷固定伏击 / `_judge_seal` 标中固定 weaken / `_apply_miss_backlash` 标错三选一哈希派生——整段删除)。负向兑付:percent 走 apply_effect(enrage/off_guard/sapped);heal 负向 = 主角直伤 4(apply_damage,不经增益状态;战斗段增 hero 存活守卫 = 致死短路;同轮后到 heal 正向遇已倒下主角记"奖励未生效"行);blast 负向 = ambush +stacks 层(`_apply_ambush_grant` 逐层经 ambush_grant_plan 对 cap=4 守恒,溢出逐层"溢出未生效"行——协议不变)。正向兑付:percent 挂状态、flat 当场(满层/满血/无敌/主角已倒下记"奖励未生效"行)。抵消过滤既有语义保持(封印判定批以轮末在场封印为基线——发射面过滤,勿改)。区间行快照 power 只在正向路由覆盖表值(负向字典行自带定值 power)。**行型协议 15→10**(删 item/consumed_ineffective/use_rejected/window_open/window_skip),SOURCE_EXEMPT 两层豁免集合同步收窄。
|
||||
- `level_runner.gd`/`debug_session.gd`:共享 地图战斗资源 容器与库存延续语义随库存删除收窄——战斗各自持容器(映射随战斗退役,零跨战斗状态),runner 删 window 转发/inventory()/level_clear 清库/submit_window_selection;奖励点节点内容随功能道具渠道(P1 未定)**悬置为直通节点**(零兑付,node_entered 零载荷——内容归 关卡节点地图 定稿批,**待拍板**);battle_finished 载荷删 remaining_items。
|
||||
|
||||
**呈现面**:
|
||||
- 删除 `inventory_panel_view.gd`(E 区道具面板/窗口态整件);`ui_assembler` 五区→四区(玩法说明文案改格面资源口径);`hud_presenter` 删 _fill_inventory/_inventory_row/window_rows,refresh 签名收窄;`main.gd` 删窗口接线/窗口状态机/库存引用(ux_window_choice 遥测随之退役);`battle_narrator` 文案去道具(层间延续 = HP only);`battle_log_view` 行格式化收窄 10 类(AGG_TYPES = reward_ineffective/overflow 两类);`board_view` 奖励徽章键 category→cell_type(攻/守/医/爆/弱 占位字形——**类型图标预读/极性视觉归批 2,纯呈现**);`status_slot_row` 补 sapped 图标键与语义(七行全映射);`playtest_logger` battle_end/level_end 载荷去 remaining_items、reward 节点零载荷。
|
||||
- "剩余安全格计数"注记:切片呈现层现无该计数器(safe_remaining 无 UI 消费方),无可改文案;呈现口径改动随批 2 一并处理。
|
||||
|
||||
**smoke/tools**:
|
||||
- 删 `supply_scan.gd`(可选类供给跨种子扫描——对象已不存在);`_smoke_supply` 整段删除。
|
||||
- `smoke_all.gd` 大改(201→211 项):删窗口接线族与窗口锚行段、等待期刷新探针改挂衰减行(结算段内 Waiting 相位刷新必拒);`_smoke_power_interval` 收窄两区间行;`_smoke_damage_types` 标错断言改类型化负向兑付。**新增五段**:`_smoke_reward_identity_unit`(roll 恰一次/query_identity 投影/同格重复 grant 幂等/grant_clear 幂等/退役拒绝)、`_smoke_polarity_routing`(真实流多轮:挖安全格只出正向 + 每格 ≥1 行、挖危险格只出负向、标中正向 origin 指回危险格、标空归因 + 负向同格同批、封印兑付零泄漏)、`_smoke_negative_dictionary`(sapped 数据面在位 + effect_pool_map 收编 + 逐型负向直验:enrage/off_guard/sapped 效果行、heal 直伤 4、blast +2 层与 cap 溢出守恒)、`_smoke_resource_surface`(chain/dig resource 恒 true 含危险格、initial 恒 false、封锁格查询 null、未开危险格可查)、`_smoke_settlement_segments`(segment_order 五段冻结 + 行型封闭零残留 + 逐轮行相位单调 + 衰减/判定恰一段)。
|
||||
- `reward_expectation.gd` 重写为类型频次测量(实测收敛权重涌现值 attack 25.3/defense 20.3/blast 20.3/heal 19.0/weaken 15.2%,封锁格泄漏 0);`level_autoplay`/`monte_carlo`/`dump_battle`/`damage_probe`/`level_debug`/`level_monte_carlo`/`ui_probe`/`ui_click_probe`/`ui_layout_probe` 去窗口/库存面。**顺手修**:`monte_carlo.gd` 缺 AutoPlayer preload(HEAD 起即解析失败)已补。
|
||||
|
||||
**验证(本批实跑)**:冒烟 **211 项检查 0 失败**(回放断言全量保持——同 master_seed 同意图序列结算步骤流逐行相同);四场景探针 ui_probe / ui_tune_probe / ui_click_probe(PROBE_SEED=7 与 91 双种子)/ ui_layout_probe 全 PASS;工具回归 reward_expectation / monte_carlo / dump_battle / damage_probe(boss) / level_debug / level_monte_carlo(N=5) 零脚本错误。
|
||||
|
||||
**表实一致注记(对 registry reward_table 条目)**:十一行逐字段一致;两处切片级补全登记——① 负向 heal/blast 行 registry 未定 reward_id,切片取语义名 `heal_backlash` / `ambush_double`;② blast 负向加层行 effect_id 恒空(registry flat 行口径)+ power 校验例外(power 不消费——伏击加层为独立兑付形态,见战斗结算 GDD 兑付形态表)。
|
||||
|
||||
**待拍板(本批登记)**:① 关卡奖励点节点内容(功能道具渠道 P1——归 关卡节点地图 定稿批);② 已开格类型徽章与极性视觉(批 2——纯呈现);③ "剩余未探索安全区域"计数呈现口径(切片现无该计数器,随批 2)。
|
||||
|
||||
---
|
||||
|
||||
## 2026-09-16 敌人技能池批(批 1+2+3 合并落地——设计权威 proposal-skill-pool.md〔提案已吸收退役 git a6833b6a——现行事实源=敌人系统.md 技能池节〕 v1.0;文档面 v0.5/v0.11/v0.5/v0.6/v0.3/v0.4 同批)
|
||||
|
||||
**机制**:确定性出招剧本(行为环:拍枚举/环指针/同种锁步/heavy_multiplier)整体退役,改技能池 + 开轮摇号——每轮轮次开始排除冷却中技能、按行序逐敌摇定本拍动作(behavior_roll 封闭流,每敌子流 scope = spawn_index×1000000+摇号序),战斗段按技能行兑现(伤害技 = 三乘法链〔基线×倍率→d20 掷点→甲效→伏击后置→池乘区→max(1,·)〕,增益技 = 施放行 cast + 状态授予〔0 伤害不掷不折不兑伏击〕);两道闸 = 全技能各自冷却(普攻恒 0 锁死)+ 保底 M=4(连续 4 拍执行普攻 → 下拍必出特殊技、不受冷却限制);死亡即不动手改"摇定后兑现前死亡 → 作废不进账";拍结构保留(每结算每存活敌恰行动一次/行序/先作用后推进)。
|
||||
|
||||
**数据面**:新增 `skills.tres` + `skill_table.gd`(五行:普攻/突刺×1.5 cd2/蓄力 cd3/重击×2 cd3/硬化 cd4;pity_threshold=4;普攻倍率/冷却域校验锁死);`enemy.tres` 六行 pattern/heavy_multiplier → pool(地精 9/1 · 熊地精 6/2/2 · 酋长 6/2/2,全部 provisional);`status_effects.tres` 七→九行(empower 蓄力 +100 enemy_output 池 / fortify 硬化 +100 **enemy_defense 敌方防御池新词汇**——duration 2 = 1 个有效增益拍〔先兑付后衰减含施放当轮〕);`settlement.tres` effect_pool_map 增 empower/fortify 两键。
|
||||
|
||||
**core 面**:`rng_service.gd` 增 behavior_roll 流(salt 0x85EBCA7758C24E15,黄金向量 m=42 scope0=426657203655691839 见冒烟;open_scope 显式 scope 取号门 + BEHAVIOR_SUBSTREAM_STRIDE);新增 `core/skill_lottery.gd`(独立抽签器——输入可摇条目+子流、输出 skill_id,D9 封装纪律);`unit_container.gd` 重写(实例池快照/冷却递减/待发动作/保底计数;query_beat/query_attack/advance/steps/pattern 退役);`battle_orchestrator.gd` 开轮摇号编排 `_roll_round_actions()`(首轮同样摇、轮内刷新不重摇)+ 战斗段技能行兑现 + 主角攻击增敌方防御池乘区(Σ三分量后 ×(100+clamp(enemy_defense))/100);`status_container.apply_effect` 增 duration_override(技能行 buff_duration 覆盖);`config_db.gd` 挂技能表 + validate_skill_pool_links 跨表校验(悬空致命/每池恰一普攻/增益 ≤1)。
|
||||
|
||||
**呈现面(批 2)**:`presentation_snapshot` 敌人数组字段定稿(beat/ring_size/ring_index 拆除 → pending_skill/pending_type/pending_name + effects 敌方增益投影;池与权重不公开);`enemy_card_view` 环点拆除(护甲行常驻)、意图徽标 = 摇定动画占位(普攻零表示 —/攻击系 ⚔/增益系 ✦);`battle_log_view` 施放行 ✦ 格式化、伤害行具名技能;`status_slot_row` 图标键补 empower ⇧ / fortify ▣(九行全映射);`settlement_snapshot` 敌方 beat 键 → pending_skill;damage_probe 拍名重建改 pending_skill 口径。
|
||||
|
||||
**smoke(211→263 项)**:行为环断言全部改型(行相位 cast、fresh 池采样、削弱触底/AC12 采样改多种子扫描);新增 `_smoke_skill_pool` 六项验收(proposal §8):数据面(池模板/普攻锁死/空池不可能)、同种子回放逐位复现 + 单敌轨迹独立复放(D3 多实例逐轮记录 vs 只喂该敌子流的独立重放器逐位对照)、EV 抽样对锚(×5000 拍)、死亡打断作废(D2 特殊技种子 + hero_atk 999 先手击杀零行动行 + 不进账)、加载校验拒绝(悬空/池构成/普攻锁死/倍率域/权重域/空池)。
|
||||
|
||||
**验证(本批实跑)**:冒烟 **263 项检查 0 失败**;四探针 ui_probe / ui_tune_probe / ui_layout_probe / ui_click_probe(种子 7、23)全 PASS;damage_probe(boss/level 全 PASS 含施放行)、defense_scan、monte_carlo 工具零脚本错误。
|
||||
|
||||
**EV 抽样读数(×5000 拍,含闸长期速率 vs 静态锚)**:地精 1.1056(静态 1.05——保底闸把突刺率从自然 10% 抬到 ~22%,登记难度曲线重锚)/ 熊地精 0.9998(结构守恒恒 1)/ 酋长 1.0236。
|
||||
|
||||
**文档/registry 同批(批 3)**:敌人系统 v0.5(技能池章节重写)/ 战斗结算 v0.11(随机口径三处/三乘法链/施放行/敌方防御池/AC 措辞四处)/ 增益状态 v0.5(九行 + 敌方防御池;顺手清偿 v0.4 挂起的 sapped 文档同步)/ 难度曲线与Boss v0.6(曲线纪律/Boss 专属池/技能池投值表 + EV 实测登记)/ 战斗表现 v0.3 / HUD框架 v0.4 / 数据配置(敌人表措辞 + 技能表行)/ 03-内容规模 / 02-系统清单 / 关卡节点地图 / ux battle.md(改型注记)/ architecture.md(单位容器 API 面)/ registry(rng/skill 新条目/enemy/status_effect/settlement/术语七词条/修订记录)/ 06-待办清单(改型"单拍行为环体验验证"、收编关闭"行为环废除与蓄力拍退役"、新增 P1 五项 + P2 技能图鉴)。
|
||||
|
||||
**实现决策登记(提案未定处的自行拍板,按封装纪律报告)**:① 保底 M 载体 = 技能表全局单字段 pity_threshold(不分敌种,难度曲线单旋钮);② 冷却"施放后连续 N 拍不可摇中"的实现 = 摇号按当前冷却值排除、递减在其后(与提案"递减→排除"的可摇集合逐拍恒等);③ 增益持续与技能冷却两计时器 = apply_effect duration_override 通道;④ 敌方防御池乘区落点 = 主角攻击 Σ三分量后(hero_offense 现行走普通分量直乘的切片现状不动——登记遗留);⑤ 摇号器类名 SkillLottery(core/skill_lottery.gd)。
|
||||
|
||||
## 2026-09-19 技能池新效应词汇批(用户拍板——设计权威 proposal-status-vocabulary.md〔提案已吸收退役 git a6833b6a——现行事实源=敌人系统.md 词汇表/增益状态.md〕 v1.0)
|
||||
|
||||
**三词收敛两词**:妨害技(debuff 新类型)+ 必中附加分量(rider_mult,"固定值伤害"改形);多段命中经用户拍板退役(无防御磨损体系,无对照机制)。**实装面 = 仅 L2 Boss 蛛化卓尔精灵,其余敌种不动**:
|
||||
- 毒爆 venom_burst + `rider_mult = 0.75`——必中毒分量 = max(1, floor(atk × 0.75))(atk 开战掷定 2–4 → 毒 1–3 点),后置加法、必中全额、不进乘区不出暴击;伤害行 payload 增 `rider` 键 + 行文本「+附加N(必中)」。
|
||||
- 新技能行**蛛网束缚 web_snare**(debuff → off_guard 失衡,duration 2 = 1 有效结算,冷却 4,池权重 2 → 卓尔池 6/2/2/2 全 provisional)——0 伤害施放行,状态按 target_side 落主角(掷点阈值 10 → 8)。
|
||||
- 结构面:SkillTable type 枚举 +debuff;池纪律 +每池妨害 ≤1;施放类 side 守卫(buff→enemy / debuff→hero,`validate_skill_pool_links` 增 status_table 显式入参——装载序修正);意图徽标 +妨害系 ☠(紫)。
|
||||
- 验证:冒烟 306 项 0 失败(+新探针 `_smoke_debuff_rider`:毒爆行逐拍精确复算 / rider 键封闭 / 蛛网束缚落主角端到端 / 六项加载校验拒绝)+ 四探针 PASS;800 局模拟试玩(两关 × 20 种子 × 双画像)——L2 基线通关 90%→80%、死因全部集中 Boss(设计方向生效),L1 零行为变更回归一致(65%/85%)。
|
||||
- 顺手收口:smoke_all `variant_pool(1, ["dungeon"])` → `"dungeon"`(并行 board_type 批签名 Array→String 的漏网同步——此前该断言被 SCRIPT ERROR 静默吞掉);analyze_playtest.py 反事实重算 +rider 识别。
|
||||
|
||||
## 2026-09-21 冒险结构实现批(三关正式序列生效——AdventureRunner + LevelRunner 账本外置 + AdventureTable 建表)
|
||||
|
||||
**主入口升级**:「▶ 开始冒险(三关连探:地精洞窟 → 幽暗地域 → 灰矮人矿城)」——一条命走完三关的完整 Roguelike 冒险;`▶ 测试直入口 · 第二关/第三关` 保留单关试玩模式,L1 单关直入口随批取消(冒险首关即 L1)。
|
||||
|
||||
**冒险级语义**(设计权威 = 冒险结构.md v0.6):主种子恰一次注入(冒险内不重播种,同种子同操作序列逐位可复现);生命账本跨关同账(战后回写、恢复点按比例封顶、大奖励回满);GP 钱包跨关延续、冒险收束清零;统一冒险加算区(商店购买与大奖励同账);非末关通关发大奖励(上限提升 +10 读 adventure.tres、血回满、钥石 +10),末关不发上限提升、钥石照记;任意战斗倒下 = 冒险失败;结算屏出冒险摘要五件事(打穿没有/通关几关/止步第几关/钥石合计/主种子)。
|
||||
|
||||
**验证**:冒烟 437→482 项 0 失败三闸全过(冒险表 +4 / API 闸 +8 / 冒险验收 +33:表覆盖义务、满配通关、种子复现、大奖励恰 2 次与覆盖值传播、败北线两种子扫描、重复宣布拒绝、GP 跨关延续、加算区同账、恢复数学、放弃两态);MC 回归零行为变更(run_level 旧路径同种子逐位一致)。
|
||||
|
||||
## 2026-09-21 元进度实现批(跨局循环闭合——MetaTable + MetaProgression + 营地 + 跨局注入)
|
||||
|
||||
**跨局成长首批落地**(设计权威 = 元进度.md v1.2):「存档在变厚」成为玩家可感的现实——冒险收束入账(大奖励渠道 + Boss 首杀/重复杀两档,明细在结算屏可归因)、营地消费、余额 HUD 常驻、永久强化在下一局开局注入。
|
||||
|
||||
**玩法面**:首页新增「营地 · 永久强化」入口(三柜台:属性锻造三档 flat 直购可重复升级、几何递增定价;秘宝匣六件按累计通关次数解锁——局内效果排 Alpha 魔法道具重设计批;奖励格图鉴占位锁定);A 区顶栏钥石余额常驻;冒险结算屏追加钥石入账明细(大奖励 / 逐关 Boss 首杀或重复 / 工坊新解锁)。买强化 → 下一局开局即生效(表值 + 永久强化 flat + 冒险加算区,再乘调整板百分比)。
|
||||
|
||||
**技术面**:`data/tables/meta.tres` 四行族(forge_products / boss_keystone / workshop_items / conversion)+ `core/meta_progression.gd` 账本服务(Core 层零 UI 零信号);SaveManager.apply_adventure_summary 增第二参 boss_drops(缺省 [] 零行为兼容,摘要元素附副本);AdventureRunner.start_adventure 增 params.hero_override(缺省全零 = 白板)→ LevelRunner base_override。**数值全 provisional 归元进度设计批配读数**(骨架先行先例 = GP+商店房批)。
|
||||
|
||||
**验证**:冒烟 522→549 项 0 失败三闸全过(表结构 +4 / 定价与购买 +7 / 注入与白板 +8 / 入账工坊摘要 +8);四探针全 PASS(ui_probe 营地开合/禁用态、click_probe 营地购买合成点击全路径——隔离存档换注保确定性,禁碰真实档);白板成立 = 空参数 = 零强化参数 = 纯表值三轴逐位一致。
|
||||
|
||||
## 2026-09-24 伤害管线单据化批(伤害对象抽象补全——伤害控制器统一入口)
|
||||
|
||||
**架构面**:伤害此前无对象抽象——五族伤害来源(主角先手 / 敌方伤害技 / 棋盘克制直伤 / 群伤 / 反噬地形)在战斗编排器内各自内联组装管线。本批新建 `core/damage_controller.gd`(伤害单据 + 唯一兑付管线):九栏单据、五种兑付配方封闭枚举;battle_orchestrator 六调用点退化为开单点(流程决定何时开单给谁开单,单据怎么算归控制器),掷点 / 折扣 / 暴击 / 克制 / 池乘区 / 下限 / 攻击行文本算术全部迁入控制器(`_defender_ac` / `_clamped_pool` 留薄委托供快照面与冒烟白盒直呼);unit_container 新增 `unit_died` 死亡事件信号(单位自行宣布死亡——怪死其未结单据作废〔死亡即不动手〕、主角死兑付终止,Game Over 仍归判定段读数)。
|
||||
|
||||
**结账时机双模式(2026-09-24 用户三裁决之二三)**:目标 = 结账时刻照规则看现场解析(最前存活敌 / 主角 / 全体存活敌),开单不冻结对象;现行 = 段界结账(授予段尾 / 清图批同步点 / 战斗段尾统一兑现),开单即结保留为回切结构位(控制器 policy 常量一行切回=逐位还原迁移前行为)。同一输入两模式结果可不同(兑现时机语义差异),历史基线读数自本批以段界结账口径为准(重测归曲线调参批)。
|
||||
|
||||
**验证**:冒烟 557 项 0 失败双模式全绿三闸过;A/B 对拍探针四配置(L1/L2 × 双种子)两模式各自同种子逐位确定、四配置全部真实分岔(行数两两恒等、纯次序位移——分岔面 = 伤害行攒批位移与治疗满血判定时机,符合段界结账语义);三场景探针全 PASS。设计权威 = 战斗结算.md v0.24「伤害单据与伤害控制器」节。
|
||||
|
||||
## 2026-10-06 地图伤害账殿后批(案C,用户裁——「主角先手」歧义质询触发;设计权威 = 战斗结算.md v0.26)
|
||||
|
||||
**裁决**:每轮结算内三者顺序定死 = ①授予段兑非伤害账(资源/增益/治疗/行动点/GP/警觉)→ ③战斗段「主角先基础攻击 → 敌人按生成序逐个还手 → 地图伤害账殿后」→ ④衰减 → ⑤判定。授予段不再当场兑付任何伤害——全部伤害形态单据(爆裂/直伤/反噬/地形变异/战封印军规/熔火反噬/惊巢反制/魔能共鸣/清障炸药等)暂存,战斗段怪物还手后按授予段事件序补提交统一结账;主角死于敌方还手 → 暂存单整体不兑,逐单留「未生效(主角已倒下)」行(载荷 forfeit=hero_down)。
|
||||
|
||||
**切片落点**:`battle_orchestrator.gd` 暂存闸 `_defer_board_damage`/`_deferred_board` + `_submit` 案C 分流(sync 同闸——清图批一致延后)+ `_outcome_of`(暂存单按「将兑付」回报——完美第二份/高光份数依据开单时点事实)+ 授予段四处直提改走闸 + 战斗段末补提交块(死亡守卫+留痕);授予段界 flush 保留(对空队 no-op 防御位);`smoke_all.gd` 行相位分类器换代(damage 恒战斗段/reward_ineffective 按 forfeit 分流)+ 先手断言收紧(「存在或授予段终局」→「必存在」)+ 新增案C 段内序断言(地图账不得先于主角先手或插入敌方还手中间)。
|
||||
|
||||
**验证**:冒烟 783 项 0 失败三闸过 + run_all 17 套全绿 + probe_gate(ui_input/ui_struct/ui_contrast/--click 行为走查)全绿、残留 0。**旧行为 MC 锚全数作废**(死亡短路语义变化:授予段伤害不再能致死短路先手;读数重采归数值批)。
|
||||
|
||||
## 2026-10-10 秘宝匣改名(文档面同步——2026-10-10 天赋树设计会话裁)
|
||||
|
||||
营地柜台「道具工坊」正名「秘宝匣」(树上解锁节点与营地柜台同名):本文档现行正文扫改完毕;**代码标识符(workshop_items 等)与界面文案暂未动**——柜台 UI 名/表行语义连带面归 design/gdd/06-待办清单.md「天赋树结构重构批」实装批办理。
|
||||
|
||||
## 2026-10-11 专长实装批(迭代 20——奇物重设计 T0-T10 全落)
|
||||
|
||||
**换代面**:feats.tres 整表换代(旧 27 件退役→重设计 29 行=基础 27+迁入淬毒刃纹/封印爆裂——订阅行 on/when/do 四级 rarity+cap);效果引擎词表扩签(事件 18/效果类型 30/事实 18/算子 +mod_eq——heal/ac_bonus/damage_reduce/bonus_delta/blast_scaled/preview_area/target_attack_bonus 七新类型+adventure 域计数器跨战账本);池结构案D(四级档位 70/25/9/0=104+曝光槽 2+1+概率上限解耦案②+Boss 按档配比加权抽取);凭证五件入池(刷新券 F-1 修复/储备金袋/同调电池〔choose_battery_target 玩家点选〕/时沙/完美保险);流派标签/档位激活机制退役(战斗路径 FeatsEngine 求值与 rider_tier 注入摘除;HUD 特性段呈现随 2026-10-12 呈现修复批换代——逐流派聚合行退役改逐件行「件名 ×N」、卡面摘族名,原聚合行在新 29 行下的空键兜底僵尸呈现随之消解)。
|
||||
**验证**:冒烟 864/0+run_all 21 套+probe_gate 全绿;MC 同种子重放 30 局逐位复现;新基线 normal/off 100 局通关 0%(master 对照同为 0%=零配基线本相,止步分布 90/10→93/7 可归因曝光槽)。设计权威=奇物.md(反哺点清单见 design/qa/iter20-实现报告-2026-10-11.md)。
|
||||
|
||||
## 2026-10-12 迭代 21 天赋树实装批(U0——需求卡片 v1.7;设计权威 = 天赋树.md v0.8)
|
||||
|
||||
**结构换代 33→12 节点**:脊柱 2(生命/命中淬炼一线一节点 cap4——攻/护淬炼线随攻防所有权切分砍除,攻唯一入口=锻造工坊武器路线、AC 唯一入口=甲路线)+ 锻造脉 2(forge_workshop 钥匙 free 无前置 + easter_ley 彩蛋〔effects 摘攻/护分量〕)+ 钥石脉 2(秘宝匣/刷新升级)+ 同调脉 6(同调位 1/2/3——socket1 改根节点无前置 + Fork 3 单行双态)。schema 换代:FORK_GROUPS 4→3(fg_mastery 退役)/ RULE_KEYS 2→6 / row 字段退役(行门槛主语=节点第 N 级需全树总投入 ≥4×(N−1))/ requires 只留点亮前置(R1 裁撤案执行)。
|
||||
|
||||
**六键真效果**:开局姿态窗=每关首战首轮(FACTS 扩签 battle_index=关卡域战斗序);危险直觉锋=踩雷减半·守=标错返还 AP(EVENTS 扩签 seal_missed、契约 12 去重闸语义)——两组每关限次(adventure_counters domain=adventure、按关重置);财富之道聚=战斗掉落 GP 入账前×1.5、散=商品购买价×80%(双计价单点,刷新价不折)。数值全 provisional 归 M4。
|
||||
|
||||
**退役删除**:属性锻造柜台全家(buy_forge/forge_flat/meta_forge_row/forge_products 行族/forge_levels 存档域/营地·暂停层·生涯三投影);migrate_tree 三体系迁移(旧档=tree_struct 标记检测破坏性废弃重置,禁迁移折算);attunement_growth 谓词表(同调位槽位判定源切树点亮数——Q2 裁甲)。
|
||||
|
||||
**营地统一**:四柜台并排(树淬炼=树脊柱界面形态主界面/锻造工坊柜台钥匙门+零交互占位面板/秘宝匣柜台钥匙门+工坊六行+同调装配段+Y 升级行迁入/图鉴常开);刷新升级双入口同账(树上 ks_refresh 路由 buy_attune_refresh)。
|
||||
|
||||
**验证**:冒烟 886/0(基线 864/0+存量红修复——fx_reveal 三处空 source_class 补标签,a57489e9 落地件 51 行脚本错误清零)+ run_all 22 套(新增 tree_rules_v11 六键闭集校验)+ probe --click 全绿(树购买/双态切换点击路径换代);V10 行为段=引擎直构+编排授予段直注(守侧五场景/聚散同种子配对/限次生命周期)。存量红修复与帐目细节见 design/qa/iter21-实现报告-2026-10-12.md。
|
||||
|
||||
## 2026-10-12 迭代 23 锻造工坊实装批(U0——需求卡片 v1.2;设计权威 = 锻造工坊.md v0.6)
|
||||
|
||||
**六路线行族新建**:新表 `forge.tres`(schema `forge_table.gd`/ForgeTable——默认两轴常驻持有 + routes 六行;ConfigDB `forge_*` 访问器:forge_route/forge_routes/forge_default_attack_type/forge_default_armor_type/forge_weapon_route_by_type/forge_armor_route_by_type)。路线 id=sword/spear/staff/light/heavy/ward(甲路线 id=甲型值本身);provisional 沿锚表:cap 5/每级增量统一 +1/基础价 20×150%^n(20/30/45/67/101)/一次性解锁价 200/甲型基础 AC 10-12-9/初始持有 sword+light/并联双通道前置 spear·staff←sword、heavy·ward←light。
|
||||
|
||||
**两根类型轴所有权移交**:`hero.tres` armor_type/attack_type 两字段删除(schema 同步删)——默认值归 forge.tres 常驻持有(normal/light);选中态写存档域 forge_routes、冒险开始经参数面注入。单位快照读取面=unit_container.spawn 新两参(空串=锻造配置默认值回退,行为逐位连续);冒烟 hero_attack 行类型键主语换代为 forge_default_attack_type()。
|
||||
|
||||
**写口族与注入链**:MetaProgression 锻造写口(契约 9 唯一)——forge_buy_level(几何价扣钥石即盘)/forge_buy_unlock(一次性解锁价,已解锁含派生=拒绝零收费)/forge_select_weapon·armor(免费切换只受理已解锁)/forge_route_unlocked(派生单点:初始持有∨直购∨练满前一档)。hero_override 公式换代(FORGE_ROUTE_BONUS 预留常量退役):atk=当前选中武器攻击加值+树 flat、armor_class=当前甲 AC 加值+树 flat、max_hp=树 flat;main.build_adventure_params +attack_type/armor_type 两键 → AdventureRunner 参数面快照(契约 8 冒险开始锁死)→ LevelRunner.set_hero_axes → enc → spawn;入战 AC 三层=甲型基础 AC(选中甲路线 base_ac)+甲路线等级加值+局内加算区。
|
||||
|
||||
**存档 v7**:SAVE_FORMAT_VERSION 6→7 + 新域 forge_routes({levels 六键/unlocked 直购清单/weapon/armor 选中},无特效域)+ _migrate_v6_v7 空迁移(缺键补齐=初始持有态)+ 访问器组;旧档缺键补齐零折算。
|
||||
|
||||
**营地工作间**:锻造工坊柜台占位面板→两区六路线行板(武器锻造区/护甲锻造区;件名+Lv+解锁态双通道文案+锻造/解锁/选中钮;余额联动禁用;锁定态零行板零购买路径——占位纪律延续);三意图信号 forge_buy/unlock/select_requested 经 camp_shop_presenter 走写口唯一路径。
|
||||
|
||||
**验证**:冒烟 938/0(基线 898/0+40 锻造断言)+ run_all 24 套(新增 forge_table_test 41 断言+forge_workbench_test 12 断言)+ probe --click/--full 全绿 + render_gate + doc 三闸/熵增对基线零新增 + MC 同种子配对零漂移。实现报告=design/qa/iter23-实现报告-2026-10-12.md。
|
||||
> 历次批次的改动明细与决策过程见 git 提交史(`git log -- game/README.md` 及各批提交信息),本文只保留运行说明与范围。
|
||||
+26
-30
@@ -12,12 +12,12 @@ const PHASE_IDLE := "Idle"
|
||||
const PHASE_ACTIVE := "Active"
|
||||
const PHASE_WAITING := "Waiting"
|
||||
|
||||
# 相位轴(P6 状态机——code-review 2026-09-29 P2-2):内部 enum + match,外部 phase getter 保持
|
||||
# String 面(presentation_snapshot 与冒烟链的 "Idle"/"Active"/"Waiting" 对照契约零变化)。
|
||||
# 相位轴:内部 enum + match,外部 phase getter 保持
|
||||
# String 面(presentation_snapshot 与冒烟链的 "Idle"/"Active"/"Waiting" 对照契约)。
|
||||
enum Phase { IDLE, ACTIVE, WAITING }
|
||||
|
||||
var db: ConfigDb
|
||||
# 相位与账本只读出口(E5 封装收口 2026-09-13):外部读取经 getter-only 属性(架构文档
|
||||
# 相位与账本只读出口:外部读取经 getter-only 属性(架构文档
|
||||
# "ap/rap/phase 只读" 的机制化),写入只经 notify_* / request_* 方法通道。
|
||||
var _phase: int = Phase.IDLE
|
||||
var phase: String:
|
||||
@@ -33,17 +33,16 @@ var last_round_end: Dictionary[Variant, Variant]:
|
||||
get: return _last_round_end
|
||||
var _settlement_consumed := false # AC-14 门:notify_settlement_done 消费后才受理 round_start
|
||||
var _next_round_mode := "" # 调试观测量(同板续轮 / 换新图)
|
||||
var _pending_ap_delta := 0 # 下轮行动点修正排队(格面资源八型批 2026-09-20——action 型 ±1 排队,轮开始兑现后清零)
|
||||
# 蓄力流运行态(迭代 12 T5 → 内容数据驱动扩展批 T3 换代:旧 feat_ap_carry/feat_dig_extra 注入封印
|
||||
# 退役——转蓄力=round_end grant_ap(mode:transfer) 订阅(finish_round_stop 定夺)、深挖对价=dig_paid
|
||||
# counter_spend 订阅(request_dig 施加面);charge_layers=蓄力层账本)
|
||||
var _pending_ap_delta := 0 # 下轮行动点修正排队(action 型 ±1 排队,轮开始兑现后清零)
|
||||
# 蓄力流运行态:转蓄力=round_end grant_ap(mode:transfer) 订阅(finish_round_stop 定夺)、
|
||||
# 深挖对价=dig_paid counter_spend 订阅(request_dig 施加面);charge_layers=蓄力层账本
|
||||
var _charge_layers := 0
|
||||
var charge_layers: int:
|
||||
get: return _charge_layers
|
||||
# 效果引擎(内容数据驱动扩展批 T1——dig_paid/charge_full 发射面;round_end 转存决策面)
|
||||
# 效果引擎(dig_paid/charge_full 发射面;round_end 转存决策面)
|
||||
var _fx: EffectEngine = null
|
||||
var _last_stop_ap := 0 # 停手侧剩余 AP 记账(转存决策移至 round_end 发射点——grant_ap(transfer) 订阅定夺)
|
||||
var _ap_exhausted_last := false # 上轮 AP 耗尽封印(迭代 20 T1——一滴不剩 round_start when 面)
|
||||
var _last_stop_ap := 0 # 停手侧剩余 AP 记账(转存决策在 round_end 发射点——grant_ap(transfer) 订阅定夺)
|
||||
var _ap_exhausted_last := false # 上轮 AP 耗尽封印(一滴不剩 round_start when 面)
|
||||
|
||||
|
||||
func set_fx(fx: EffectEngine) -> void:
|
||||
@@ -67,7 +66,7 @@ func _cost(kind: String) -> int:
|
||||
var ae = db.action_economy
|
||||
match kind:
|
||||
"dig":
|
||||
return ae.dig_ap_cost # 基础挖格成本恒表值(钻头对价挂付费开掘侧——2026-10-04 缺陷修复)
|
||||
return ae.dig_ap_cost # 基础挖格成本恒表值(钻头对价挂付费开掘侧)
|
||||
"seal_place":
|
||||
return ae.seal_place_ap_cost
|
||||
"move":
|
||||
@@ -87,8 +86,7 @@ func try_spend(action_kind: String) -> bool:
|
||||
|
||||
|
||||
# 排雷玩法 挖掘专用:chain_size = 连锁附加格数(不含首格);clears_map = 本次动作后全图安全格开完。
|
||||
# 深挖钻头付费开掘(内容数据驱动扩展批 T3——dig_paid counter_spend 订阅施加面,2026-10-04 缺陷
|
||||
# 修复口径不变):按序消耗族——AP 富余(≥基础+对价)时本动作付全价并积本轮付费计数(效果引擎
|
||||
# 深挖钻头付费开掘(dig_paid counter_spend 订阅施加面):按序消耗族——AP 富余(≥基础+对价)时本动作付全价并积本轮付费计数(效果引擎
|
||||
# 计数器=授予段追加产出配额取数面);AP 不足全价时 fallback 降级只付基础 1 照常开格、不积计数
|
||||
# ——持有钻头永不锁死普通挖格(散件保底 B-2)。
|
||||
func request_dig(chain_size: int, clears_map: bool) -> bool:
|
||||
@@ -96,9 +94,9 @@ func request_dig(chain_size: int, clears_map: bool) -> bool:
|
||||
_reject("phase")
|
||||
return false
|
||||
var cost := _cost("dig")
|
||||
# dig_paid 发射+施加(内容数据驱动扩展批 T3——付费开掘发起、动作经济闸收对价前:
|
||||
# dig_paid 发射+施加(付费开掘发起、动作经济闸收对价前:
|
||||
# counter_spend 命中=尝试加收对价并积计数;资源不足走 fallback/耗尽即止=只付常规基础对价。
|
||||
# 迭代 20 T1——counter_add〔双管镐 付费挖格计数〕/ grant_ap〔每第 2 次返 1 AP——对价减免,
|
||||
# counter_add〔双管镐 付费挖格计数〕/ grant_ap〔每第 2 次返 1 AP——对价减免,
|
||||
# mod_eq 闸在行 when〕同面施加)
|
||||
var dig_refund := 0
|
||||
if _fx != null:
|
||||
@@ -153,16 +151,14 @@ func _spend_and_arbitrate(action_kind: String, clears_map: bool) -> bool:
|
||||
|
||||
|
||||
# 停手:剩余 AP 1:1 转存 RAP;行动经济 直发 stopped 路径 round_end(双发射源)。
|
||||
# 整备军备改写(迭代 12 T5——feat_ap_carry=true 时剩余 AP 转蓄力层〔上限 3〕替代 RAP 转存:
|
||||
# 「不动也是行动」——cleared 作废口径改写的停手侧承载;cleared 分支 AP 恒清零维持既有作废语义)。
|
||||
# 内容数据驱动扩展批 T1:本方法改记账制——剩余 AP 存 _last_stop_ap 后清零,转存决策移至
|
||||
# round_end 发射点 finish_round_stop(grant_ap(mode:transfer) 订阅定夺;双轨期旧封印 feat_ap_carry
|
||||
# 并行生效,行为与迁移前逐位一致)。
|
||||
# 转蓄力层〔上限 3〕替代 RAP 转存——「不动也是行动」;cleared 分支 AP 恒清零维持作废语义。
|
||||
# 本方法只记账——剩余 AP 存 _last_stop_ap 后清零,转存决策在 round_end 发射点
|
||||
# finish_round_stop(grant_ap(mode:transfer) 订阅定夺)。
|
||||
func request_stop() -> bool:
|
||||
if _phase != Phase.ACTIVE:
|
||||
_reject("phase")
|
||||
return false
|
||||
# dig_stop 发射+施加(迭代 20 T1——停手挂点〔弃图兑换〕:剩余 AP 随载荷外发,counter_add
|
||||
# dig_stop 发射+施加(停手挂点〔弃图兑换〕:剩余 AP 随载荷外发,counter_add
|
||||
# value_event="ap_left" 取值面〔值=停手时刻真实余量〕;发射先于记账清零)
|
||||
if _fx != null:
|
||||
for h in _fx.dispatch("dig_stop", {"ap_left": _ap}):
|
||||
@@ -178,14 +174,14 @@ func request_stop() -> bool:
|
||||
return true
|
||||
|
||||
|
||||
# 储备池注入(迭代 20 T4——储备金袋凭证:战首 RAP +N,节点级一次性;任意相位受理=战首注入时 Idle/Active 均合法)
|
||||
# 储备池注入(储备金袋凭证:战首 RAP +N,节点级一次性;任意相位受理=战首注入时 Idle/Active 均合法)
|
||||
func grant_reserve_ap(n: int) -> void:
|
||||
_rap += maxi(0, n)
|
||||
|
||||
|
||||
# 轮末停手转存决策(round_end 发射点调用——编排器授予段尾):transfer=true=剩余 AP 转蓄力层
|
||||
# (上限 3,整备军备面);false=按转存率转存 RAP(transfer_pct 百分比口径缺省 100=1:1 与旧逐位一致;
|
||||
# 迭代 22 秘宝批 T3——储备罐 rap_transfer_mult magnitude 125=1.25:1,整数除法向下取整=每 4 AP 多转 1)。
|
||||
# (上限 3,整备军备面);false=按转存率转存 RAP(transfer_pct 百分比口径缺省 100=1:1;
|
||||
# 储备罐 rap_transfer_mult magnitude 125=1.25:1,整数除法向下取整=每 4 AP 多转 1)。
|
||||
# 非停手轮(cleared/耗尽)_last_stop_ap=0 两分支均零操作。转蓄后达上限的沿发 charge_full(R6 框架先行——现无订阅者)。
|
||||
func finish_round_stop(transfer: bool, transfer_pct: int = 100) -> void:
|
||||
if transfer:
|
||||
@@ -198,7 +194,7 @@ func finish_round_stop(transfer: bool, transfer_pct: int = 100) -> void:
|
||||
_last_stop_ap = 0
|
||||
|
||||
|
||||
# 轮内刷新(2026-09-12 方案 A 用户拍板;等待期通道 2026-09-15 用户拍板整体移除):唯一入口 =
|
||||
# 轮内刷新:唯一入口 =
|
||||
# Active 相位——仅扣 RAP,相位与 AP 均不动。刷新不打断轮次、不触发结算;换图编排归 战斗结算
|
||||
# (retire + install 立即执行,玩家同轮继续挖新图)。决策主体 = 玩家(经 HUD框架发起)纪律不变。
|
||||
func request_refresh() -> bool:
|
||||
@@ -221,9 +217,9 @@ func notify_settlement_done(next_round_mode: String) -> bool:
|
||||
return true
|
||||
|
||||
|
||||
# 下轮行动点修正排队(格面资源八型批 2026-09-20):action 型正负行结算期调用,同轮多次授予累加;
|
||||
# 下轮行动点修正排队:action 型正负行结算期调用,同轮多次授予累加;
|
||||
# 相位无关(授予发生在结算段,此时本类可能处于 ACTIVE/WAITING 任一相位)
|
||||
# 第 9 API(registry 行动经济×① 接口漂移预授权——迭代 7 T2 秘宝内容批落位):ap_grant 道具的
|
||||
# 第 9 API(registry 行动经济×① 接口漂移预授权):ap_grant 道具的
|
||||
# 当轮行动点加量(GDD「加行动点效果给当轮」——区别于 queue_ap_delta 的下轮排队语义)。Active 受理、
|
||||
# 无上限域、零随机;使用道具本身零扣费(成本在获取时已付——捡道具格不出战斗奖励)。
|
||||
func grant_ap(amount: int) -> bool:
|
||||
@@ -262,7 +258,7 @@ func notify_combat_end() -> bool:
|
||||
_reject("phase")
|
||||
return false
|
||||
_phase = Phase.IDLE
|
||||
_rap = 0 # RAP 战斗域清零(用户拍板;HUD框架可见反馈义务)
|
||||
_rap = 0 # RAP 战斗域清零(HUD框架可见反馈义务)
|
||||
_ap = 0
|
||||
return true
|
||||
|
||||
@@ -273,11 +269,11 @@ func _finish_round(path_tag: String, continuation_hint: String) -> void:
|
||||
assert(path_tag != "ap_exhausted" or _ap == 0, "耗尽轮剩余行动点恒为 0(实现层断言兜底)")
|
||||
assert(path_tag != "cleared" or _ap == 0, "清图轮剩余行动点当场清零(不进储备池)")
|
||||
_last_round_end = {"path_tag": path_tag, "continuation_hint": continuation_hint}
|
||||
_ap_exhausted_last = path_tag == "ap_exhausted" # 迭代 20 T1——一滴不剩 when 面(轮始发射读数)
|
||||
_ap_exhausted_last = path_tag == "ap_exhausted" # 一滴不剩 when 面(轮始发射读数)
|
||||
_phase = Phase.WAITING
|
||||
|
||||
|
||||
# 上轮 AP 是否耗尽(迭代 20 T1——round_start 发射 payload 读数面;轮始兑现后保持到本轮末)
|
||||
# 上轮 AP 是否耗尽(round_start 发射 payload 读数面;轮始兑现后保持到本轮末)
|
||||
func ap_exhausted_last_round() -> bool:
|
||||
return _ap_exhausted_last
|
||||
|
||||
|
||||
+132
-134
@@ -7,9 +7,9 @@ class_name AdventureRunner
|
||||
extends RefCounted
|
||||
|
||||
signal level_started(level_index: int, display_name: String) # 每关开始(含冒险首关)——呈现层重取 runner()、刷新图条与层名行
|
||||
signal curio_choice_presented(payload: Dictionary) # 专长三选一停点(非末关通关恰一次;迭代 20 T5 换代正名——旧「提升三选一」退役,通道白名单后候选恒专长+回血在载荷)——{level_index, candidates, heal_gained, hp_before, hp_after, keystone_gained, keystone_total, gp, aux_cleared}
|
||||
signal curio_choice_presented(payload: Dictionary) # 专长三选一停点(非末关通关恰一次;通道白名单后候选恒专长+回血在载荷)——{level_index, candidates, heal_gained, hp_before, hp_after, keystone_gained, keystone_total, gp, aux_cleared}
|
||||
signal curio_resolved(result: Dictionary) # 三选一兑付完成(pick_curio/替换受理后恰一次)——{picked, value, hp_before, hp_after, level_index}
|
||||
signal adventure_finished(summary: Dictionary) # 冒险结算(completed/failed/abandoned)——summary 五件事:打穿没有/通关几关/止步第几关/钥石合计/主种子(+additive keystone_drops 迭代 10)
|
||||
signal adventure_finished(summary: Dictionary) # 冒险结算(completed/failed/abandoned)——summary 五件事:打穿没有/通关几关/止步第几关/钥石合计/主种子(+additive keystone_drops)
|
||||
# ---- 转发族(签名与 LevelRunner 同款——呈现层对接透明;S6 经 _forward_* 方法)----
|
||||
signal node_entered(node: Dictionary, info: Dictionary)
|
||||
signal choices_presented(candidates: Array)
|
||||
@@ -20,19 +20,19 @@ signal wallet_changed(event: String, gp: int, extra: Dictionary)
|
||||
|
||||
var db: ConfigDb
|
||||
var rng: RngServiceCore
|
||||
enum State { IDLE, RUNNING, SETTLING } # 会话状态机(迭代 27 T6——裸字符串 enum 化:内部 enum,外部 String 契约 getter 词表逐位一致)
|
||||
enum State { IDLE, RUNNING, SETTLING } # 会话状态机(内部 enum,外部 String 契约经 getter 词表)
|
||||
var _state: State = State.IDLE
|
||||
var state: String: # 外部契约面(只读——外部零写点实证;"idle"/"running"/"settling" 零变化)
|
||||
var state: String: # 外部契约面(只读——外部零写点实证;词表 "idle"/"running"/"settling")
|
||||
get:
|
||||
return State.keys()[_state].to_lower()
|
||||
var ledger := {"hero_hp": 0, "gp": 0} # 冒险账本(生命 + GP 钱包——经 start_level_external 注入 runner 共享)
|
||||
var run_ledger := RunLedger.new() # 共享四本账(迭代 27 T3——P1-4 收口:attune_charges/vision_counter/adventure_counters/bonus 单类持账;裁点②甲)
|
||||
var run_ledger := RunLedger.new() # 共享四本账(attune_charges/vision_counter/adventure_counters/bonus 单类持账)
|
||||
var purchased := {} # product_id → 已购档数(跨关延续、冒险收束清零——递增定价输入)
|
||||
var level_index := 0 # 当前关卡序号(1 起)
|
||||
var levels_total := 0 # 序列总关数(读 level 表行数)
|
||||
var keystone := 0 # 钥石入账累计(摘要第三件事的账面)
|
||||
var master_seed := 0 # 本局主种子(摘要第五件事)
|
||||
# 两根类型轴冒险开始快照(迭代 23 T4——契约 8:营地选中态经参数面注入、冒险开始后锁死;
|
||||
# 两根类型轴冒险开始快照(契约 8:营地选中态经参数面注入、冒险开始后锁死;
|
||||
# 公有=参数面快照的可断言面〔契约 9 写口唯一,本字段只读不改〕。空串=未注入→消费面回退锻造配置默认值)
|
||||
var hero_attack_type := ""
|
||||
var hero_armor_type := ""
|
||||
@@ -42,32 +42,32 @@ var _pending_next_level := false # 大奖励已兑付、等 continue_to
|
||||
var _last_level_payload := {} # 最近一次关卡结束载荷(失败结算的败退节点明细来源——数值验证取数面)
|
||||
var _override := [0, 0] # 调试浮窗百分比通道 [atk_pct, hp_pct](每关新 runner 都要应用)
|
||||
var _base_override := {"atk": 0, "max_hp": 0, "armor_class": 0}
|
||||
var _first_script := false # 首次脚本模式(关卡节点图生成 v0.4——true = 全关零抽签取池内模板 0;测量夹具恒 false) # 永久强化 flat 注入(元进度实现批——冒险开始参数面 params.hero_override 存入,共享给 LevelRunner;全零 = 白板)
|
||||
var _last_treasure_cleared := 0 # additive(迭代 7 T4——最近一次关卡末清空计数,summary 取数面)
|
||||
var _treasure_unlocked := {} # aux 解锁门控集(迭代 7 T5——params.treasure_unlocked 接收面)
|
||||
var _attunement: Array[Variant] = [] # 同调装配集(迭代 11 第①批 T5——params.attunement 接收面,start_level 外下传 LevelRunner)
|
||||
var _attune_refresh_x := 0 # 刷新件余量(迭代 11 第①批 T5b P1 甲——每碰三选一重置=X 表值;优先消耗、不结转)
|
||||
var _first_script := false # 首次脚本模式(关卡节点图生成 v0.4——true = 全关零抽签取池内模板 0;测量夹具恒 false) # 永久强化 flat 注入(冒险开始参数面 params.hero_override 存入,共享给 LevelRunner;全零 = 白板)
|
||||
var _last_treasure_cleared := 0 # additive(最近一次关卡末清空计数,summary 取数面)
|
||||
var _treasure_unlocked := {} # aux 解锁门控集(params.treasure_unlocked 接收面)
|
||||
var _attunement: Array[Variant] = [] # 同调装配集(params.attunement 接收面,start_level 外下传 LevelRunner)
|
||||
var _attune_refresh_x := 0 # 刷新件余量(每碰三选一重置=X 表值;优先消耗、不结转)
|
||||
var _attune_refresh_y := 0 # Y 冒险周期池(开局参数面注入,用完即无——营地升级行表值)
|
||||
var _curio_paid_refreshes := 0 # 本次停点已付费刷新次数(2026-10-06 用户令对齐商店 MC-1——GP 刷新价曲线输入,随停点创建清零=与商店进店清零同构)
|
||||
# ---- 凭证件三本新账(迭代 20 T4——奇物.md 凭证件表新 5 件;F-1 刷新写错账修复承载面)----
|
||||
var _refresh_stock := 0 # 刷新存量账(冒险级——刷新券加仓;**不经 _attune_refresh_x**:X 每停点重置会吞存量=旧写错账缺陷 F-1)
|
||||
var _curio_paid_refreshes := 0 # 本次停点已付费刷新次数(对齐商店 MC-1——GP 刷新价曲线输入,随停点创建清零=与商店进店清零同构)
|
||||
# ---- 凭证件三本账(奇物.md 凭证件表 5 件;F-1 刷新写错账修复承载面)----
|
||||
var _refresh_stock := 0 # 刷新存量账(冒险级——刷新券加仓;**不经 _attune_refresh_x**:X 每停点重置会吞存量=写错账缺陷 F-1)
|
||||
var _pending_rap := 0 # 储备金袋待注入(节点级——下一战 RAP +N,战首注入后清零)
|
||||
var _time_sand_queue := 0 # 时沙待注入(回合级——下一战首轮 AP +N,战首入队后清零)
|
||||
var _save_manager = null # 断点承载注入(迭代 8 卡①——null = 不写断点;模拟/冒烟缺省)
|
||||
var _save_manager = null # 断点承载注入(null = 不写断点;模拟/冒烟缺省)
|
||||
var _resume_bp := {} # 续玩快照暂存(resume_adventure → _start_current_level 单跳传递)
|
||||
# ---- 三选一停点态(迭代 10 奖励重构批)----
|
||||
# ---- 三选一停点态 ----
|
||||
var _curio_taken := {} # 件获取次数计数(kind 维度派生账本——断点附随件与摘要读数承载;池库存正主=_curio_deck)
|
||||
var _curio_deck: Array[Variant] = [] # 令牌池(TP0 v2.2——持久多重集,元素 {"kind","tier"};洗牌抽候选/选中踢池未选回池/池耗即枯竭;商店货架同库共账;迭代 12 T1 起规则改写型特性带=relic 令牌随表追加)
|
||||
var _feats: Array[String] = [] # 特性持有集(迭代 12 T1——局内随机渠道获得、冒险级跨关携带、收束清零;🔴 层级纪律承载面:不入同调位不入局外树)
|
||||
var _tree_rules: Array[Variant] = [] # 树 rule 键集(迭代 12 T10——Fork 激活侧 effects 透传;下传 enc.tree_rules 消费面)
|
||||
var _curio_deck: Array[Variant] = [] # 令牌池(TP0 v2.2——持久多重集,元素 {"kind","tier"};洗牌抽候选/选中踢池未选回池/池耗即枯竭;商店货架同库共账;规则改写型特性带=relic 令牌随表追加)
|
||||
var _feats: Array[String] = [] # 特性持有集(局内随机渠道获得、冒险级跨关携带、收束清零;🔴 层级纪律承载面:不入同调位不入局外树)
|
||||
var _tree_rules: Array[Variant] = [] # 树 rule 键集(Fork 激活侧 effects 透传;下传 enc.tree_rules 消费面)
|
||||
var _curio_pending := {} # 停点态:{"candidates": Array, "payload": Dictionary, "chosen"/"incoming": 替换在途}——空 = 无停点
|
||||
var _curio_result := {} # 已兑付待推进态(curio_resolved 载荷副本——中断续玩不重兑付)
|
||||
var _keystone_drops: Array[Variant] = [] # Boss 逐杀掉落明细(additive 摘要键 keystone_drops——结算明细/复盘归因数据源)
|
||||
var _node_curio_pending := {} # 奖励节点三选一停点态(迭代 10 追加批——共享池;断点不载:续玩重建到选路停点重走,恢复的计数器保证同候选重抽)
|
||||
var _node_curio_pending := {} # 奖励节点三选一停点态(共享池;断点不载:续玩重建到选路停点重走,恢复的计数器保证同候选重抽)
|
||||
var _feat_ticket_pity := 0 # 门票保底计数(T5——展示次数:节点开面/货架抽签/刷新重展各计一次;触发清零)
|
||||
var _feat_ticket_sold := 0 # 门票限购计数(T5——选中/买下即 +1;拿到即开不进持有集)
|
||||
var _feat_ticket_appeared := 0 # 门票出现计数(T5——quota 上限面)
|
||||
var _shop_mode := "legacy" # 商店模式(迭代 11 第②批 T3——"legacy"=固定三件加定价〔off 对照承载,缺省零扰动〕/"pool"=共享池抽 3 买 1 重抽;真实入口 main.gd 显式投 pool)
|
||||
var _shop_mode := "legacy" # 商店模式("legacy"=固定三件加定价〔off 对照承载,缺省零扰动〕/"pool"=共享池抽 3 买 1 重抽;真实入口 main.gd 显式投 pool)
|
||||
|
||||
|
||||
func _init(db_ref: ConfigDb, rng_ref: RngServiceCore) -> void:
|
||||
@@ -93,34 +93,34 @@ func start_adventure(seed_val: int, params: Dictionary = {}) -> bool:
|
||||
keystone = 0
|
||||
summary = {}
|
||||
_reset_curio_state()
|
||||
levels_total = db.level_count() # 表访问收口(2026-10-02——行集直扫退役)
|
||||
levels_total = db.level_count() # 表访问收口(关数读 level 表行数)
|
||||
level_index = 1
|
||||
_pending_next_level = false
|
||||
_base_override = _normalize_override(params.get("hero_override", {}))
|
||||
hero_attack_type = String(params.get("attack_type", "")) # 迭代 23 T4——两根类型轴快照(契约 8 锁死:开局后不随存档选中变化)
|
||||
hero_attack_type = String(params.get("attack_type", "")) # 两根类型轴快照(契约 8 锁死:开局后不随存档选中变化)
|
||||
hero_armor_type = String(params.get("armor_type", ""))
|
||||
_first_script = bool(params.get("first_script", false)) # 缺省 = 非首次(抽签路径;真实侧由呈现层读存档布尔位传入)
|
||||
_shop_mode = "pool" if String(params.get("shop_mode", "legacy")) == "pool" else "legacy" # 迭代 11 第②批 T3——缺省 legacy(off 对照零扰动)
|
||||
_shop_mode = "pool" if String(params.get("shop_mode", "legacy")) == "pool" else "legacy" # 缺省 legacy(off 对照零扰动)
|
||||
_treasure_unlocked = params.get("treasure_unlocked", {}) if params.get("treasure_unlocked", {}) is Dictionary else {}
|
||||
_attunement = params.get("attunement", []) if params.get("attunement", []) is Array else [] # additive(迭代 11 第①批 T5)
|
||||
_attune_refresh_y = maxi(0, int(params.get("attune_refresh_y", 0))) # additive(迭代 11 第①批 T5b——Y 冒险周期池)
|
||||
_seed_attune_charges() # 迭代 11 第①批 T6——冒险开始充能账本按装备主动件重置(每冒险充能语义)
|
||||
run_ledger.vision_counter = {"used": 0} # 迭代 22 秘宝批 T3——真视使用计数冒险开始归零(跨局累计归生涯档)
|
||||
# 刷新练习开局注入(迭代 22 秘宝批 T3——refresh_stock_grant 装配即得一次性加仓:
|
||||
_attunement = params.get("attunement", []) if params.get("attunement", []) is Array else [] # additive
|
||||
_attune_refresh_y = maxi(0, int(params.get("attune_refresh_y", 0))) # additive(Y 冒险周期池)
|
||||
_seed_attune_charges() # 冒险开始充能账本按装备主动件重置(每冒险充能语义)
|
||||
run_ledger.vision_counter = {"used": 0} # 真视使用计数冒险开始归零(跨局累计归生涯档)
|
||||
# 刷新练习开局注入(refresh_stock_grant 装配即得一次性加仓:
|
||||
# 装配集 kind 分发扫描;只入存量账 _refresh_stock、临时账不动=V8 契约;resume 不重注入〔存量随断点已在〕)
|
||||
for iid_v2 in _attunement:
|
||||
var rp_row: Dictionary[Variant, Variant] = db.treasure_item_row(String(iid_v2))
|
||||
if (rp_row.get("effect_kinds", []) as Array).has("refresh_stock_grant"):
|
||||
_refresh_stock += int(rp_row.get("magnitude", 0))
|
||||
# 特性开局注入(迭代 12 T7——MC 成型画像载体:params.relics_seed 非空=开局持有集〔七流派
|
||||
# 特性开局注入(MC 成型画像载体:params.relics_seed 非空=开局持有集〔七流派
|
||||
# 各 2 件档一成型——测量面专用;真实局规则改写型特性只走局内随机渠道,红线②层级纪律不破〕)
|
||||
var feat_seed: Array[Variant] = params.get("relics_seed", []) if params.get("relics_seed", []) is Array else []
|
||||
_tree_rules = params.get("tree_rules", []) if params.get("tree_rules", []) is Array else [] # 迭代 12 T10——A/B 面 rule 键集
|
||||
_tree_rules = params.get("tree_rules", []) if params.get("tree_rules", []) is Array else [] # A/B 面 rule 键集
|
||||
if not feat_seed.is_empty():
|
||||
_feats = []
|
||||
for r in feat_seed:
|
||||
_feats.append(String(r))
|
||||
ledger["hero_hp"] = effective_max_hp() # 生命账本置满(生效上限口径——T17 修复:含调试浮窗 pct,与战斗内同源)
|
||||
ledger["hero_hp"] = effective_max_hp() # 生命账本置满(生效上限口径:含调试浮窗 pct,与战斗内同源)
|
||||
_state = State.RUNNING
|
||||
return _start_current_level()
|
||||
|
||||
@@ -133,7 +133,7 @@ func _normalize_override(raw: Dictionary) -> Dictionary:
|
||||
return out
|
||||
|
||||
|
||||
# 断点续玩(迭代 8 卡①——「继续上局」入口;bp = save.breakpoint_snapshot() 快照,params 同 start_adventure
|
||||
# 断点续玩(「继续上局」入口;bp = save.breakpoint_snapshot() 快照,params 同 start_adventure
|
||||
# 的注入参数面):账面(生命/GP/加算区/已购档/钥石累计)逐字段回填 + rng.restore_adventure 回填
|
||||
# 「主种子+计数器」→ _start_current_level 续玩路径(模板随快照强制、重建到停点节点,零重抽)。
|
||||
# 快照缺字段按防御默认(空快照拒绝——调用方先 has_breakpoint)。
|
||||
@@ -155,7 +155,7 @@ func resume_adventure(bp: Dictionary, params: Dictionary = {}) -> bool:
|
||||
purchased = (bp.get("shop_purchased", {}) as Dictionary).duplicate(true)
|
||||
keystone = int(bp.get("keystone", 0))
|
||||
summary = {}
|
||||
levels_total = db.level_count() # 表访问收口(2026-10-02——行集直扫退役)
|
||||
levels_total = db.level_count() # 表访问收口(关数读 level 表行数)
|
||||
level_index = int(bp.get("level_index", 1))
|
||||
_pending_next_level = false
|
||||
_reset_curio_state()
|
||||
@@ -163,9 +163,9 @@ func resume_adventure(bp: Dictionary, params: Dictionary = {}) -> bool:
|
||||
for id in _curio_taken.keys():
|
||||
_curio_taken[id] = maxi(0, int(bp_taken.get(id, 0)))
|
||||
for id in bp_taken.keys():
|
||||
_curio_taken[String(id)] = maxi(0, int(bp_taken.get(id, 0))) # 迭代 20 T2——身份维键(relic:<feat_id>)随快照还原
|
||||
_curio_taken[String(id)] = maxi(0, int(bp_taken.get(id, 0))) # 身份维键(relic:<feat_id>)随快照还原
|
||||
# 令牌池余库还原(TP0 v2.2——快照缺键 = 旧版断点,保持满库重建态:旧档只读一版周期的防御口径;
|
||||
# 迭代 12 T1——relic 令牌 feat_id 随还原)
|
||||
# relic 令牌 feat_id 随还原)
|
||||
var bp_deck = bp.get("bonus_deck", null)
|
||||
if bp_deck is Array:
|
||||
var restored: Array[Variant] = []
|
||||
@@ -176,38 +176,38 @@ func resume_adventure(bp: Dictionary, params: Dictionary = {}) -> bool:
|
||||
rtok["feat_id"] = String(tok.get("feat_id", ""))
|
||||
restored.append(rtok)
|
||||
_curio_deck = restored
|
||||
# 特性持有集还原(迭代 12 T1——缺键 = 旧版断点防御空集)
|
||||
# 特性持有集还原(缺键 = 旧版断点防御空集)
|
||||
_feats = []
|
||||
for r in (bp.get("relics", []) as Array):
|
||||
_feats.append(String(r))
|
||||
_keystone_drops = (bp.get("keystone_drops", []) as Array).duplicate(true)
|
||||
_base_override = _normalize_override(params.get("hero_override", {}))
|
||||
hero_attack_type = String(params.get("attack_type", "")) # 迭代 23 T4——续玩参数面同 start_adventure(账号层选中态随 resume 重注入)
|
||||
hero_attack_type = String(params.get("attack_type", "")) # 续玩参数面同 start_adventure(账号层选中态随 resume 重注入)
|
||||
hero_armor_type = String(params.get("armor_type", ""))
|
||||
# 生命账本越界钳制(T17——spawn 守卫 EC16 免疫):断点可能由旧口径版本写出(如调试上限
|
||||
# 调高期带出的账本值 > 新口径生效上限,实例:hero_hp=28 > 基础 26)——恢复后钳到生效上限,
|
||||
# 生命账本越界钳制(T17——spawn 守卫 EC16 免疫):断点可能带出高于生效上限的账本值
|
||||
# (如调试浮窗调高期写出的值,实例:hero_hp=28 > 基础 26)——恢复后钳到生效上限,
|
||||
# 否则首战 spawn「hero_hp 入参越界」拒收 → 「敌人系统 spawn 被拒」fatal。上限 = 表值+永久
|
||||
# 强化 flat+加算区 flat 后乘调试浮窗 pct(effective_max_hp 同式——此刻加算区尚未恢复,
|
||||
# 钳制用 flat 已含的保守口径即可,加算区恢复后其余战斗照常)。
|
||||
ledger["hero_hp"] = mini(int(ledger.get("hero_hp", 0)), maxi(1,
|
||||
int(round(float(int(db.hero.max_hp) + int(_base_override.get("max_hp", 0))) * (100.0 + float(_override[1])) / 100.0))))
|
||||
_first_script = false # 续玩模板已随快照强制(已抽模板零重抽)——脚本模式不重放
|
||||
_shop_mode = "pool" if String(params.get("shop_mode", "legacy")) == "pool" else "legacy" # 迭代 11 第②批 T3——续玩参数面同 start_adventure(缺省 legacy)
|
||||
_shop_mode = "pool" if String(params.get("shop_mode", "legacy")) == "pool" else "legacy" # 续玩参数面同 start_adventure(缺省 legacy)
|
||||
_treasure_unlocked = params.get("treasure_unlocked", {}) if params.get("treasure_unlocked", {}) is Dictionary else {}
|
||||
_attunement = params.get("attunement", []) if params.get("attunement", []) is Array else [] # additive(迭代 11 第①批 T5)
|
||||
_attune_refresh_y = maxi(0, int(bp.get("attune_refresh_y", int(params.get("attune_refresh_y", 0))))) # 2026-10-13 缺陷修批——Y 余量优先读断点(花掉不回血=堵「用完退菜单再续玩满血复活」可利用漏洞);缺键=旧版断点,回退参数面整额注入(旧档行为零变化)
|
||||
_seed_attune_charges() # 迭代 11 第①批 T6——冒险开始充能账本按装备主动件重置(每冒险充能语义)
|
||||
_tree_rules = params.get("tree_rules", []) if params.get("tree_rules", []) is Array else [] # 迭代 12 T10——A/B 面 rule 键集(尾裁微批b 2026-10-08 卡⓪受理闭环:续玩参数面同 start_adventure :135 同式,消费面 set_tree_rules 下传现成零改)
|
||||
# 三选一停点续玩分支(迭代 10——选择态/已兑付态不重跑关卡;奖励选择态不重抽、已兑付不重付)
|
||||
_attunement = params.get("attunement", []) if params.get("attunement", []) is Array else [] # additive
|
||||
_attune_refresh_y = maxi(0, int(bp.get("attune_refresh_y", int(params.get("attune_refresh_y", 0))))) # Y 余量优先读断点(花掉不回血=堵「用完退菜单再续玩满血复活」可利用漏洞);缺键=旧版断点,回退参数面整额注入(旧档行为零变化)
|
||||
_seed_attune_charges() # 冒险开始充能账本按装备主动件重置(每冒险充能语义)
|
||||
_tree_rules = params.get("tree_rules", []) if params.get("tree_rules", []) is Array else [] # A/B 面 rule 键集(续玩参数面同 start_adventure 同式,消费面 set_tree_rules 下传)
|
||||
# 三选一停点续玩分支(选择态/已兑付态不重跑关卡;奖励选择态不重抽、已兑付不重付)
|
||||
var bp_pending: Dictionary[Variant, Variant] = bp.get("bonus_pending", {}) as Dictionary
|
||||
_feat_ticket_pity = int(bp.get("feat_ticket_pity", 0)) # T5 扩列——旧档缺键缺省 0(存档兼容)
|
||||
_feat_ticket_sold = int(bp.get("feat_ticket_sold", 0))
|
||||
# 迭代 20 T6/T7——adventure 域计数器回填(缺键 = 旧版断点防御空集;_reset_curio_state 已清零在先)
|
||||
# adventure 域计数器回填(缺键 = 旧版断点防御空集;_reset_curio_state 已清零在先)
|
||||
var bp_counters = bp.get("adventure_counters", null)
|
||||
if bp_counters is Dictionary:
|
||||
run_ledger.adventure_counters = (bp_counters as Dictionary).duplicate(true)
|
||||
_refresh_stock = maxi(0, int(bp.get("refresh_stock", 0))) # 迭代 20 T4/T7——旧档缺键缺省 0
|
||||
run_ledger.vision_counter = {"used": maxi(0, int(bp.get("vision_counter_used", 0)))} # 迭代 22 秘宝批 T3——真视计数随断点回填(缺键=旧断点缺省 0)
|
||||
_refresh_stock = maxi(0, int(bp.get("refresh_stock", 0))) # 旧档缺键缺省 0
|
||||
run_ledger.vision_counter = {"used": maxi(0, int(bp.get("vision_counter_used", 0)))} # 真视计数随断点回填(缺键=旧断点缺省 0)
|
||||
var bp_result: Dictionary[Variant, Variant] = bp.get("bonus_result", {}) as Dictionary
|
||||
if not bp_pending.is_empty():
|
||||
_pending_next_level = true
|
||||
@@ -234,7 +234,7 @@ func resume_adventure(bp: Dictionary, params: Dictionary = {}) -> bool:
|
||||
curio_resolved.emit(_curio_result.duplicate(true))
|
||||
return true
|
||||
_resume_bp = bp.duplicate(true)
|
||||
# 迭代 11 第①批 T6——充能账本随断点回局(缺键 = 旧档/无断点,保持 _seed_attune_charges 种子态)
|
||||
# 充能账本随断点回局(缺键 = 旧档/无断点,保持 _seed_attune_charges 种子态)
|
||||
var bp_charges = bp.get("attunement_charges", null)
|
||||
if bp_charges is Dictionary:
|
||||
run_ledger.attune_charges = (bp_charges as Dictionary).duplicate(true)
|
||||
@@ -243,7 +243,7 @@ func resume_adventure(bp: Dictionary, params: Dictionary = {}) -> bool:
|
||||
|
||||
|
||||
# 三选一停点态归零(冒险开始/结算收束共用;令牌池按表配额重建——池消耗即上限,账本随池重生。
|
||||
# 迭代 20 T6——adventure 域计数器账本随冒险开始清零;resume 路径调用后由断点附随件回填)
|
||||
# adventure 域计数器账本随冒险开始清零;resume 路径调用后由断点附随件回填)
|
||||
func _reset_curio_state() -> void:
|
||||
_curio_taken = {"attack": 0, "max_hp": 0, "armor": 0, "gp": 0, "ap": 0, "heal": 0}
|
||||
_curio_deck = _build_curio_deck()
|
||||
@@ -259,10 +259,9 @@ func _reset_curio_state() -> void:
|
||||
|
||||
|
||||
# 令牌池构建(TP0 v2.2——按表 kind×tier 配额展开为持久多重集;构建序确定 = CURIO_KINDS×TIERS 逐层,
|
||||
# 随机性只发生在洗牌消费 reward_pick 封闭流的时刻。迭代 12 T1:规则改写型特性带=relic 活行每件 1 张追加。
|
||||
# 迭代 20 T2/T3 四级换代——relic 令牌档 = 行 rarity 直映射(四级词表内直用;旧三档值 transitional
|
||||
# 回退映射 rarest→rare/其余→excellent,T3 行换代后回退分支自然休眠);relic 入普通档随新档位框架
|
||||
# 解禁〔旧「永不入普通档」系 TP0 三档时代约束,四级框架未沿用——奇物.md 池结构节无此限制〕)
|
||||
# 随机性只发生在洗牌消费 reward_pick 封闭流的时刻。规则改写型特性带=relic 活行每件 1 张追加。
|
||||
# relic 令牌档 = 行 rarity 直映射(四级词表内直用;词表外的 transitional 值回退映射
|
||||
# rarest→rare/其余→excellent);relic 可入普通档(奇物.md 池结构节无档位限制)。
|
||||
func _build_curio_deck() -> Array:
|
||||
var deck: Array[Variant] = []
|
||||
for kind in db.adventure.CURIO_KINDS:
|
||||
@@ -290,16 +289,15 @@ func set_hero_override(atk_pct: int, hp_pct: int) -> void:
|
||||
_runner.set_hero_override(atk_pct, hp_pct)
|
||||
|
||||
|
||||
# 冒险账本侧生效上限(T17 修复——账本置满/回血封顶与战斗内上限同式同源):
|
||||
# 冒险账本侧生效上限(账本置满/回血封顶与战斗内上限同式同源):
|
||||
# (表值 + 永久强化 flat + 加算区 flat)后乘调试浮窗百分比——叠加次序与 LevelRunner.effective_max_hp
|
||||
# 逐字一致。旧口径两处只算 flat 不乘 pct:调生命上限 26→104 时开局血仍 26(账本 26 / 战斗内 104)、
|
||||
# 通关 20% 回血封顶按 26 算。pct=0(模拟/冒烟默认)时乘法恒等——MC 锚零漂移。
|
||||
# 逐字一致。pct=0(模拟/冒烟默认)时乘法恒等——MC 锚零漂移。
|
||||
func effective_max_hp() -> int:
|
||||
var base: int = int(db.hero.max_hp) + int(_base_override.get("max_hp", 0)) + int(run_ledger.bonus.get("max_hp", 0))
|
||||
return maxi(1, int(round(float(base) * (100.0 + float(_override[1])) / 100.0)))
|
||||
|
||||
|
||||
# 断点承载注入(迭代 8 卡①——组合根冒险开始时调用;模拟/冒烟不注入 = 断点面整体静默)
|
||||
# 断点承载注入(组合根冒险开始时调用;模拟/冒烟不注入 = 断点面整体静默)
|
||||
func set_save_manager(sm: SaveManagerCore) -> void:
|
||||
_save_manager = sm
|
||||
|
||||
@@ -310,7 +308,7 @@ func runner() -> LevelRunner:
|
||||
|
||||
|
||||
# 接口③治疗封顶回报执行体(恢复点节点兑付——按比例回血并封顶)。算术宿主 = LevelRunner.rest_heal
|
||||
#(2026-10-02 修复批——同式双落点字面量退役,与 LevelRunner.rest_heal_amount 同一单点)
|
||||
#(与 LevelRunner.rest_heal_amount 同一单点)
|
||||
func apply_rest(pct: int, hp: int, max_hp: int) -> int:
|
||||
return LevelRunner.rest_heal(pct, hp, max_hp)
|
||||
|
||||
@@ -329,7 +327,7 @@ func continue_to_next_level() -> bool:
|
||||
return _start_current_level()
|
||||
|
||||
|
||||
# 停点空候选消化(2026-10-06 用户令——池空=三选一环节整体跳过:候选 0 件时停点视同已处理,
|
||||
# 停点空候选消化(池空=三选一环节整体跳过:候选 0 件时停点视同已处理,
|
||||
# 呈现层跳开选择面直接走后续推进。仅空候选合法;非空停点拒绝——零自动推进契约不破,正常
|
||||
# 消化仍只走 pick_curio/apply_node_curio 兑付。触发源=渠道白名单过滤后池空(关卡 Boss 通关
|
||||
# 渠道只出专长,专长零配置/旧档 deck 无 relic 余库即空集)。)
|
||||
@@ -342,9 +340,9 @@ func skip_empty_curio() -> bool:
|
||||
return true
|
||||
|
||||
|
||||
# ---- 提升三选一(迭代 10 奖励重构批——选择停点零自动推进、重复兑付被拒;兑付恰一件、未选项零生效)----
|
||||
# ---- 提升三选一(选择停点零自动推进、重复兑付被拒;兑付恰一件、未选项零生效)----
|
||||
|
||||
# 候选令牌匹配(身份键恒等——属性加成型特性 kind+tier、规则改写型特性 feat_id;迭代 12 T1 规则改写型特性带)
|
||||
# 候选令牌匹配(身份键恒等——属性加成型特性 kind+tier、规则改写型特性 feat_id)
|
||||
func _find_token(candidates: Array, kind: String, tier: String, feat_id: String = "") -> Dictionary:
|
||||
for t in candidates:
|
||||
var tok: Dictionary[Variant, Variant] = t as Dictionary
|
||||
@@ -358,7 +356,7 @@ func _find_token(candidates: Array, kind: String, tier: String, feat_id: String
|
||||
|
||||
# 玩家点选兑付(呈现层/冒烟/自动模拟确认入口):令牌 ∈ 停点候选(身份键精确匹配——relic 件
|
||||
# 另须 feat_id)。属性件写加算区、AP 件写第四轴、GP 件走钱包「奖励入账」入口、恢复件当即回血、
|
||||
# relic 件入特性持有集(🔴 层级纪律:只走局内随机渠道)、道具件受同调位约束(随池种退役不可达)。
|
||||
# relic 件入特性持有集(🔴 层级纪律:只走局内随机渠道)、道具件受同调位约束。
|
||||
# 消费=选中令牌踢池(未选回池由「抽签不摘牌、兑付才摘牌」语义承载——TP0 v2.2)。
|
||||
func pick_curio(kind: String, tier: String = "", feat_id: String = "") -> bool:
|
||||
if state != "running" or _curio_pending.is_empty():
|
||||
@@ -379,7 +377,7 @@ func pick_curio(kind: String, tier: String = "", feat_id: String = "") -> bool:
|
||||
return true
|
||||
|
||||
|
||||
# 兑付载荷 relic 伴随键(迭代 12 T1——反馈行取数面;非 relic 件零新增键)
|
||||
# 兑付载荷 relic 伴随键(反馈行取数面;非 relic 件零新增键)
|
||||
func _feat_payload_keys(feat_id: String) -> Dictionary:
|
||||
if feat_id == "":
|
||||
return {}
|
||||
@@ -387,7 +385,7 @@ func _feat_payload_keys(feat_id: String) -> Dictionary:
|
||||
return {"feat_id": feat_id, "feat_name": String(row.get("display_name", "")), "feat_archetype": String(row.get("archetype", ""))}
|
||||
|
||||
|
||||
# 三选一件兑付共享体(2026-10-02 修复批双胞胎收口;TP0 改令牌消费——摘牌单点;迭代 12 T1 relic 分支):
|
||||
# 三选一件兑付共享体(TP0——令牌消费、摘牌单点,含 relic 分支):
|
||||
# 属性件写加算区 + 件获取计数、AP 件写第四轴、GP 件走钱包、恢复件当即回血(封顶生效上限——
|
||||
# 与账本置满/通关回血同式 effective_max_hp)、relic 件入持有集(散件单体保底——效果接线面
|
||||
# 按挂点消费,档位质变由 FeatsEngine 求值)。兑付即从 _curio_deck 摘除该令牌(选中踢池;
|
||||
@@ -400,7 +398,7 @@ func _apply_curio_pick(kind: String, tier: String, value: int, feat_id: String =
|
||||
push_error("[冒险结构] 令牌消费失败——牌库无此令牌: %s|%s|%s" % [kind, tier, feat_id])
|
||||
return false
|
||||
if kind == "refresh":
|
||||
_refresh_stock += value # T4 F-1 修复——刷新券入刷新存量账(冒险级;**退役 _attune_refresh_x 写口**:X 每停点 _reset_refresh_x 重置会吞存量=写错账缺陷正主;摘牌先于落账=选中即消尽,旧早退路径零摘牌缺陷同批修正)
|
||||
_refresh_stock += value # T4 F-1——刷新券入刷新存量账(冒险级;**不写 _attune_refresh_x**:X 每停点 _reset_refresh_x 重置会吞存量;摘牌先于落账=选中即消尽)
|
||||
_curio_taken["refresh"] = int(_curio_taken.get("refresh", 0)) + 1
|
||||
return true
|
||||
if kind == "rap":
|
||||
@@ -415,9 +413,9 @@ func _apply_curio_pick(kind: String, tier: String, value: int, feat_id: String =
|
||||
if not _feats.has(feat_id):
|
||||
_feats.append(feat_id)
|
||||
_curio_taken["relic"] = int(_curio_taken.get("relic", 0)) + 1
|
||||
_curio_taken["relic:" + feat_id] = int(_curio_taken.get("relic:" + feat_id, 0)) + 1 # 迭代 20 T2 案②——身份维已拿计数(cap 消费面)
|
||||
_curio_taken["relic:" + feat_id] = int(_curio_taken.get("relic:" + feat_id, 0)) + 1 # 身份维已拿计数(cap 消费面)
|
||||
return true
|
||||
# 加算形态路由(内容数据驱动扩展批 T5——match 收敛进效果类型:属性值提升四类走
|
||||
# 加算形态路由(match 收敛进效果类型:属性值提升四类走
|
||||
# adventure_table.CURIO_APPLY_ROUTES 声明路由(bonus 落点键),加新加算类令牌零代码)
|
||||
var sink := db.adventure.curio_apply_route(kind)
|
||||
if sink != "":
|
||||
@@ -438,7 +436,7 @@ func _apply_curio_pick(kind: String, tier: String, value: int, feat_id: String =
|
||||
return true
|
||||
|
||||
|
||||
# 从牌库摘除一枚令牌(TP0——选中踢池单点;迭代 12 T1 relic 按 feat_id 精确摘;找不到 = false)
|
||||
# 从牌库摘除一枚令牌(TP0——选中踢池单点;relic 按 feat_id 精确摘;找不到 = false)
|
||||
func _consume_deck_token(kind: String, tier: String, feat_id: String = "") -> bool:
|
||||
for i in _curio_deck.size():
|
||||
var tok: Dictionary[Variant, Variant] = _curio_deck[i] as Dictionary
|
||||
@@ -456,9 +454,9 @@ func feats_held() -> Array:
|
||||
return _feats.duplicate()
|
||||
|
||||
|
||||
# ---- 商店共享池消费面(迭代 11 第②批 T3——P1 共享池单点账本;TP0 令牌池换代——库存=牌库剩余令牌)----
|
||||
# ---- 商店共享池消费面(共享池单点账本;TP0——库存=牌库剩余令牌)----
|
||||
|
||||
# 库存余量(TP0:stock = 牌库内该 kind 剩余令牌数——「池消耗即上限」,种类次数上限退役;
|
||||
# 库存余量(TP0:stock = 牌库内该 kind 剩余令牌数——「池消耗即上限」,无种类次数上限;
|
||||
# 奖励三选一兑付摘牌 与 商店购买消费 由同一 _curio_deck 承载——两渠道跨关共库,全冒险一次性)。
|
||||
func pool_stock(kind: String) -> int:
|
||||
var n := 0
|
||||
@@ -492,7 +490,7 @@ func _pool_stock_snapshot() -> Dictionary:
|
||||
|
||||
|
||||
# 兑付收口(恰一次):清停点态 → 记已兑付态 → emit → 停点快照(等继续确认——中断续玩不重兑付)。
|
||||
# picked = kind;tier 随载荷(TP0——大小档位读数面);relic 伴随键随载荷(迭代 12 T1——反馈行取数)
|
||||
# picked = kind;tier 随载荷(TP0——大小档位读数面);relic 伴随键随载荷(反馈行取数)
|
||||
func _finish_curio_pick(picked: String, value: int, hp_before: int, tier: String = "", feat_id: String = "") -> void:
|
||||
_curio_pending = {}
|
||||
_curio_result = {
|
||||
@@ -508,7 +506,7 @@ func _finish_curio_pick(picked: String, value: int, hp_before: int, tier: String
|
||||
_snapshot_breakpoint()
|
||||
|
||||
|
||||
# ---- 奖励节点三选一(迭代 10 追加批——用户拍板:与通关三选一共享同池同权重同次数上限)----
|
||||
# ---- 奖励节点三选一(与通关三选一共享同池同权重同次数上限)----
|
||||
|
||||
# 候选呈现(节点到达时调用——恰一 reward_pick scope 洗牌 + 道具件掷定 scope;本停点断点不载,
|
||||
# 续玩重建到选路停点后重走重抽,恢复的全局计数器保证同候选逐位复现)。
|
||||
@@ -518,8 +516,8 @@ func node_curio_candidates() -> Dictionary:
|
||||
|
||||
|
||||
func present_node_curio(candidates: Array) -> void:
|
||||
_reset_refresh_x() # 迭代 11 第①批 T5b——碰三选一重置 X 余量(不结转)
|
||||
_curio_paid_refreshes = 0 # 付费刷计数随停点清零(对齐商店进店清零——2026-10-06)
|
||||
_reset_refresh_x() # 碰三选一重置 X 余量(不结转)
|
||||
_curio_paid_refreshes = 0 # 付费刷计数随停点清零(对齐商店进店清零)
|
||||
_node_curio_pending = {"candidates": candidates.duplicate(true)}
|
||||
|
||||
|
||||
@@ -551,12 +549,12 @@ func _seed_attune_charges() -> void:
|
||||
run_ledger.attune_charges[String(iid)] = maxi(0, int(arow.get("charges_per_adventure", 0)))
|
||||
|
||||
|
||||
# ---- 三选一刷新(迭代 11 第①批 T5b——P1 甲 X/Y 双源:X=刷新件每碰三选一重给〔优先消耗、不结转〕、
|
||||
# ---- 三选一刷新(X/Y 双源:X=刷新件每碰三选一重给〔优先消耗、不结转〕、
|
||||
# Y=营地升级行冒险周期池〔用完即无〕;重抽消费 reward_pick 既有洗牌流,池规则全保留——不放回/
|
||||
# 达上限剔牌/掷定前置直显;停点可中断)----
|
||||
|
||||
func _attune_refresh_x_total() -> int:
|
||||
# 内容数据驱动扩展批 T4——按 id 特判拆除:kind 分发查询(装配集内 effect_kinds 含 bonus_refresh
|
||||
# kind 分发查询(装配集内 effect_kinds 含 bonus_refresh
|
||||
# 的行 magnitude 合计;刷新件类加件零 id 分支)
|
||||
var total := 0
|
||||
for iid_v in _attunement:
|
||||
@@ -570,7 +568,7 @@ func _reset_refresh_x() -> void:
|
||||
_attune_refresh_x = _attune_refresh_x_total()
|
||||
|
||||
|
||||
# 当前可刷新次数(X 优先 + Y 池 + 刷新存量账——呈现层余量读数口;T4 F-1 修复扩第三源)
|
||||
# 当前可刷新次数(X 优先 + Y 池 + 刷新存量账——呈现层余量读数口)
|
||||
func attune_refresh_left() -> int:
|
||||
return _attune_refresh_x + _attune_refresh_y + _refresh_stock
|
||||
|
||||
@@ -593,7 +591,7 @@ func refresh_curio() -> bool:
|
||||
return _refresh_curio_common(false)
|
||||
|
||||
|
||||
# 三选一 GP 付费刷新(2026-10-06 用户令对齐商店 MC-1——免费位 X/Y 用尽后转付费,同旋钮同曲线同钱包;
|
||||
# 三选一 GP 付费刷新(对齐商店 MC-1——免费位 X/Y 用尽后转付费,同旋钮同曲线同钱包;
|
||||
# 组合根先经 curio_stop_pending+钱包支出再调,本口只做计数与重抽)。池规则全保留——不放回/达上限剔牌/掷定前置直显。
|
||||
func refresh_curio_paid() -> bool:
|
||||
return _refresh_curio_common(true)
|
||||
@@ -617,7 +615,7 @@ func _refresh_curio_common(paid: bool) -> bool:
|
||||
_curio_paid_refreshes += 1
|
||||
else:
|
||||
# 免费位消耗序:X(刷新件每停点重给,优先消耗)→ Y(营地冒险周期池)→ 存量账(刷新券加仓,
|
||||
# 迭代 20 T4 F-1 修复新账——跨停点持久)
|
||||
# 跨停点持久)
|
||||
if _attune_refresh_x <= 0 and _attune_refresh_y <= 0 and _refresh_stock <= 0:
|
||||
return false
|
||||
if _attune_refresh_x > 0:
|
||||
@@ -631,7 +629,7 @@ func _refresh_curio_common(paid: bool) -> bool:
|
||||
_curio_pending["candidates"] = cands.duplicate(true)
|
||||
var payload: Dictionary[Variant, Variant] = (_curio_pending.get("payload", {}) as Dictionary).duplicate(true)
|
||||
payload["candidates"] = cands
|
||||
payload["refreshed"] = true # 重发载荷带标——呈现层直落选择面,不再重开胜利总结面(2026-10-06)
|
||||
payload["refreshed"] = true # 重发载荷带标——呈现层直落选择面,不再重开胜利总结面
|
||||
curio_choice_presented.emit(payload) # 同信号重 present(零自动推进契约不破——停点仍在途)
|
||||
_snapshot_breakpoint()
|
||||
return true
|
||||
@@ -660,13 +658,13 @@ func _finish_node_bonus(picked: String, value: int, tier: String = "", feat_id:
|
||||
# kind 出现率∝剩余令牌数〔档位配额即分布——无独立权重 knob〕;抽签不摘牌、兑付才摘牌=未选
|
||||
# 自动回池;池耗即枯竭——余库不足 N kind 时出齐剩余,空库 = 零候选合法态)。候选 = 令牌字典
|
||||
# {"kind","tier"}。消费 reward_pick 封闭流——一次停点恰一 scope,同种子同候选。
|
||||
# 渠道白名单过滤(迭代 15 卡② T4——渠道轴定位:关底 Boss 击败=通关渠道只出专长(feat 令牌);
|
||||
# 渠道白名单过滤(渠道轴定位:关底 Boss 击败=通关渠道只出专长(feat 令牌);
|
||||
# 奖励节点/商店只出属性值提升+资源(非 relic 令牌),专长在非 Boss 渠道只以门票形态出现(T5)。
|
||||
# 过滤式而非物理拆池:deck 物理结构/存档键 bonus_deck 混编余库/消耗语义(选中踢池未选回池)原样,
|
||||
# 白名单在候选视图层生效——配额结构锚 TIER_QUOTAS 70/25/3=98 零改动(专长令牌本系活行追加面,在锚外)。
|
||||
# 迭代 20 T2 案D 换代——曝光槽:非 Boss 渠道三选一 = **2 格全池 + 1 格优秀档以上子池**
|
||||
# 曝光槽:非 Boss 渠道三选一 = **2 格全池 + 1 格优秀档以上子池**
|
||||
# (rank≥excellent;子池空该格回退全池;池空直通同族兜底);凭证(feat_ticket)不进子池
|
||||
# 〔独立计数体系〕;已拿上限 cap(案②提前退役):taken≥cap 的身份在候选视图剔除〔牌库本体不动,
|
||||
# 〔独立计数体系〕;已拿上限 cap:taken≥cap 的身份在候选视图剔除〔牌库本体不动,
|
||||
# 拿了才计、刷出不计——_curio_cap_ok〕。
|
||||
func _draw_curio_candidates(channel: String = "reward_node") -> Array:
|
||||
var pool := _curio_deck.duplicate()
|
||||
@@ -681,8 +679,8 @@ func _draw_curio_candidates(channel: String = "reward_node") -> Array:
|
||||
return _curio_cap_ok(String(t.get("kind", "")), String(t.get("feat_id", ""))))
|
||||
var rg: RandomNumberGenerator = rng.open_stream("reward_pick")
|
||||
if channel == "level_clear":
|
||||
# 迭代 20 T5——Boss 渠道按档配比加权抽取(无放回加权抽:逐席按 boss_feat_weight 掷点,
|
||||
# 身份去重照旧;rarity 语义约束兑付面——奇物.md v3.1,配比数值归调参批)
|
||||
# Boss 渠道按档配比加权抽取(无放回加权抽:逐席按 boss_feat_weight 掷点,
|
||||
# 身份去重;rarity 语义约束兑付面——奇物.md v3.1,配比数值归 adventure 表)
|
||||
var out_w: Array[Variant] = []
|
||||
var seen_w: Array[Variant] = []
|
||||
var want_w := int(db.adventure.curio_candidate_count)
|
||||
@@ -754,7 +752,7 @@ func _draw_curio_candidates(channel: String = "reward_node") -> Array:
|
||||
return out
|
||||
|
||||
|
||||
# 已拿上限判定(迭代 20 T2 案②——身份维度:kind 走 curio_cap、relic 走 feats 行 cap 字段;
|
||||
# 已拿上限判定(身份维度:kind 走 curio_cap、relic 走 feats 行 cap 字段;
|
||||
# taken 只在兑付时 +1〔拿了才计〕、刷出不计)。缺省放行=白名单外 kind 兜底。
|
||||
func _curio_cap_ok(kind: String, feat_id: String = "") -> bool:
|
||||
var cap := 99
|
||||
@@ -768,7 +766,7 @@ func _curio_cap_ok(kind: String, feat_id: String = "") -> bool:
|
||||
return int(_curio_taken.get(kind, 0)) < cap
|
||||
|
||||
|
||||
# —— 专长获取门票(T5 迭代 15 卡②——非 Boss 渠道的专长入口;四口径契约④:拿到即开不可囤积/
|
||||
# —— 专长获取门票(T5——非 Boss 渠道的专长入口;四口径契约④:拿到即开不可囤积/
|
||||
# 罕见只在概率不在价格/一局限购一次/计步保底连续 5 展示未出第 6 必出——计数=展示次数、刷新
|
||||
# 重展计入、节点与商店共享、触发清零、限购满不再触发。调用即一次展示判定与记账)——
|
||||
func _feat_ticket_consider(rg: RandomNumberGenerator) -> bool:
|
||||
@@ -806,7 +804,7 @@ func _redeem_feat_ticket() -> bool:
|
||||
return true
|
||||
|
||||
|
||||
# Boss 钥石逐杀掉落(迭代 10——渠道改 Boss/精英击杀掉落;首杀/复杀两档读 meta.tres boss_keystone;
|
||||
# Boss 钥石逐杀掉落(掉落渠道 = Boss/精英击杀;首杀/复杀两档读 meta.tres boss_keystone;
|
||||
# 首杀判据 = 存档 boss_first_kill 标记位〔断点未注入 = 恒首杀——模拟/冒烟零扰动;标记仍在收束
|
||||
# apply_adventure_summary 逐通关关次置位〕;末关 Boss 照常入账;明细随摘要 additive 键供结算面归因)
|
||||
func _boss_keystone_drop(li: int) -> void:
|
||||
@@ -834,7 +832,7 @@ func abandon() -> bool:
|
||||
|
||||
# ---- 关卡推进(私有——序列编排单一出口)----
|
||||
|
||||
# 树限次计数按关重置(迭代 21 T3——危险直觉锋/守两组「每关限次」账本键:跨战持久由 adventure
|
||||
# 树限次计数按关重置(危险直觉锋/守两组「每关限次」账本键:跨战持久由 adventure
|
||||
# 域导出/回灌承载,此处随关清零闭合生命周期四态;键集=效果引擎树合成行声明的 counter_id;
|
||||
# 独立成口=V10 断言面直呼)
|
||||
func _reset_tree_limited_counters() -> void:
|
||||
@@ -854,38 +852,38 @@ func _start_current_level() -> bool:
|
||||
_runner.wallet_changed.connect(_forward_wallet_changed)
|
||||
_runner.level_finished.connect(_on_level_finished)
|
||||
_runner.set_hero_override(int(_override[0]), int(_override[1]))
|
||||
# 断点续玩分支(迭代 8 卡①):模板随快照强制(已抽模板零重抽零 RNG)、resume_state 带
|
||||
# 断点续玩分支:模板随快照强制(已抽模板零重抽零 RNG)、resume_state 带
|
||||
# 停点节点与已到序——重建到停点后经 present_resumed_choice 呈现(信号顺序契约同入口)。
|
||||
var resume_state: Dictionary[Variant, Variant] = {}
|
||||
var template_pick: int = 0 if _first_script else -1 # 首次脚本 = 每关固定模板 0 零抽签(v0.4 拍板点①);否则逐关抽签
|
||||
var template_pick: int = 0 if _first_script else -1 # 首次脚本 = 每关固定模板 0 零抽签;否则逐关抽签
|
||||
if not _resume_bp.is_empty():
|
||||
resume_state = {"node_id": int(_resume_bp.get("node_id", -1)),
|
||||
"visited_ids": _resume_bp.get("visited_ids", []) as Array}
|
||||
template_pick = int(_resume_bp.get("template_pick", -1))
|
||||
# 接口①开始关卡:生命经共享账本交接(账本当前值即开局生命——冒险开始置满 / 大奖励回满已写好);
|
||||
# _base_override 共享引用随载荷传入(永久强化 flat——LevelRunner.effective_* 叠加序第二项)。
|
||||
# 停点前移批 2026-09-23:defer_entry=true 起每关(含首关)——先 emit level_started(呈现层在
|
||||
# defer_entry=true 起每关(含首关)——先 emit level_started(呈现层在
|
||||
# 该回调重取 runner 实例),后入口候选呈现;顺序反了选路口会被呈现层的 runner 空防御静默丢弃。
|
||||
# 呈现零 RNG 消耗、pick 走同一 _enter_node——测量面逐位零漂移。
|
||||
_runner.set_run_ledger(run_ledger) # 迭代 27 T3——共享四账单类下传(原四 setter/参数散传收口;run_ledger=四账类,ledger=关卡账沿用旧名)
|
||||
_runner.sync_attune_inventory() # 迭代 11 第①批 T6 原语义——能力件骑库存面注入(随账下传同拍)
|
||||
_runner.set_run_ledger(run_ledger) # 共享四账单类下传(run_ledger=四账类,ledger=关卡账)
|
||||
_runner.sync_attune_inventory() # 能力件骑库存面注入(随账下传同拍)
|
||||
var ok: bool = _runner.start_level_external(level_index, ledger, purchased,
|
||||
apply_rest, _base_override, true, template_pick, resume_state)
|
||||
if ok:
|
||||
_runner.set_attunement(_attunement) # 迭代 11 第①批 T5——同调装配下传(enc 携带进战斗结算)
|
||||
_runner.set_feats(_feats) # 迭代 12 T1——特性持有集下传(begin_battle enc.feats 求值面)
|
||||
_runner.set_tree_rules(_tree_rules) # 迭代 12 T10——树 rule 键集下传(A/B 面消费)
|
||||
_runner.set_hero_axes(hero_attack_type, hero_armor_type) # 迭代 23 T4——两根类型轴快照下传(effective_ac 甲型基础 AC 层+enc 携带进 spawn)
|
||||
_runner.set_attunement(_attunement) # 同调装配下传(enc 携带进战斗结算)
|
||||
_runner.set_feats(_feats) # 特性持有集下传(begin_battle enc.feats 求值面)
|
||||
_runner.set_tree_rules(_tree_rules) # 树 rule 键集下传(A/B 面消费)
|
||||
_runner.set_hero_axes(hero_attack_type, hero_armor_type) # 两根类型轴快照下传(effective_ac 甲型基础 AC 层+enc 携带进 spawn)
|
||||
if _shop_mode == "pool":
|
||||
# 商店共享池注入(迭代 11 第②批 T3——库存账本引用+消费 handler 双下传;legacy 模式零注入零扰动。
|
||||
# 迭代 20 T2——cap 检查回调四参注入)
|
||||
# 商店共享池注入(库存账本引用+消费 handler 双下传;legacy 模式零注入零扰动。
|
||||
# cap 检查回调四参注入)
|
||||
_runner.set_shop_pool(_curio_deck, _consume_pool_purchase, _feat_ticket_consider, _curio_cap_ok)
|
||||
# 迭代 20 T4——凭证件三账注入(节点级/回合级一次性:注入即清零,未用即过期=寿命档语义)
|
||||
# 凭证件三账注入(节点级/回合级一次性:注入即清零,未用即过期=寿命档语义)
|
||||
_runner.set_voucher_grants(_pending_rap, _time_sand_queue)
|
||||
_pending_rap = 0
|
||||
_time_sand_queue = 0
|
||||
# 三选一道具件跨关携带注入(迭代 10)——迭代 11 第①批 T7 退役:aux 卡出池后 carry 恒空集,
|
||||
# 注入面随停点渠道退役拆除(容器字段保留=替换子交互机械惰性承载,零投放零行为)
|
||||
# 三选一道具件跨关携带注入面不存在(aux 卡不在池种内→ carry 恒空集;
|
||||
# 容器字段保留=替换子交互惰性承载,零投放零行为)
|
||||
level_started.emit(level_index, String(_runner.level_row.get("display_name", "")))
|
||||
# 入口停点断点(node_id=-1)与 defer_entry 同态(node_index=-1+单候选)——走入口呈现;
|
||||
# 中途停点(node_id>=0)走停点呈现(restore_treasure_items 先于呈现=C 区库存就位)。
|
||||
@@ -912,20 +910,20 @@ func _on_level_finished(payload: Dictionary) -> void:
|
||||
if result != "victory":
|
||||
push_error("[冒险结构] 关卡结束结果出域: %s" % result)
|
||||
return
|
||||
_last_treasure_cleared = int(payload.get("aux_cleared", 0)) # additive(迭代 7 T4——末关 summary 取数面)
|
||||
# Boss 钥石逐杀掉落(迭代 10——渠道改击杀掉落,末关照常入账;首杀/复杀读 meta.tres boss_keystone)
|
||||
_last_treasure_cleared = int(payload.get("aux_cleared", 0)) # additive(末关 summary 取数面)
|
||||
# Boss 钥石逐杀掉落(击杀掉落渠道,末关照常入账;首杀/复杀读 meta.tres boss_keystone)
|
||||
_boss_keystone_drop(level_index)
|
||||
if level_index < levels_total:
|
||||
# 通关兑付(非末关恰一次;迭代 10 三层奖励框架):部分回血先行 → 加权洗牌掷 3 → 三选一停点
|
||||
# 通关兑付(非末关恰一次;三层奖励框架):部分回血先行 → 加权洗牌掷 3 → 三选一停点
|
||||
#(顺序即契约——回血按当刻账本与上限结算,选择停点零自动推进)
|
||||
var hp_before := int(ledger.get("hero_hp", 0))
|
||||
var max_eff := effective_max_hp() # T17 修复:回血封顶口径含调试浮窗 pct(同战斗内上限)
|
||||
var max_eff := effective_max_hp() # 回血封顶口径含调试浮窗 pct(同战斗内上限)
|
||||
var heal := mini(int(floor(float(max_eff) * float(int(db.adventure.clear_heal_pct)) / 100.0)), max_eff - hp_before)
|
||||
ledger["hero_hp"] = hp_before + heal
|
||||
# 置位先于 emit(信号同步派发——处理器内立即确认时 _pending_next_level 必须已就位)
|
||||
_pending_next_level = true
|
||||
_reset_refresh_x() # 迭代 11 第①批 T5b——碰三选一重置 X 余量(不结转)
|
||||
_curio_paid_refreshes = 0 # 付费刷计数随停点清零(对齐商店进店清零——2026-10-06)
|
||||
_reset_refresh_x() # 碰三选一重置 X 余量(不结转)
|
||||
_curio_paid_refreshes = 0 # 付费刷计数随停点清零(对齐商店进店清零)
|
||||
var pick_payload: Dictionary[Variant, Variant] = {
|
||||
"level_index": level_index,
|
||||
"candidates": _draw_curio_candidates("level_clear"), # T4 渠道白名单——关底 Boss 击败=通关渠道只出专长
|
||||
@@ -935,7 +933,7 @@ func _on_level_finished(payload: Dictionary) -> void:
|
||||
"keystone_gained": int(_keystone_drops.back().get("amount", 0)) if not _keystone_drops.is_empty() else 0,
|
||||
"keystone_total": keystone,
|
||||
"gp": int(ledger.get("gp", 0)),
|
||||
"aux_cleared": int(payload.get("aux_cleared", 0)), # additive 扩签(迭代 7 T4——关卡末清空反馈取数)
|
||||
"aux_cleared": int(payload.get("aux_cleared", 0)), # additive 扩签(关卡末清空反馈取数)
|
||||
}
|
||||
_curio_pending = {"candidates": (pick_payload["candidates"] as Array).duplicate(true),
|
||||
"payload": pick_payload.duplicate(true)}
|
||||
@@ -955,13 +953,13 @@ func _settle(result: String) -> void:
|
||||
"stopped_at": 0 if result == "completed" else level_index,
|
||||
"keystone_total": keystone,
|
||||
"master_seed": master_seed,
|
||||
"aux_cleared_last": _last_treasure_cleared, # additive 扩签(迭代 7 T4——末关清空反馈;非末关走 bonus 载荷)
|
||||
"keystone_drops": _keystone_drops.duplicate(true), # additive 扩签(迭代 10——逐杀掉落明细;settle 入账明细/复盘归因数据源)
|
||||
"vision_used_delta": int(run_ledger.vision_counter.get("used", 0)), # additive 扩签(迭代 22 秘宝批 T3——真视使用本局计数;settle 聚合入档)
|
||||
"pool_stock": _pool_stock_snapshot(), # additive 扩签(迭代 11 第②批 T5——终局池读数:清账前快照,商店买走 vs 奖励领走 vs 枯竭的终局面)
|
||||
"relics": _feats.duplicate(), # additive 扩签(迭代 12 T1——特性终局持有集:清账前快照,成型判读取数面)
|
||||
"aux_cleared_last": _last_treasure_cleared, # additive 扩签(末关清空反馈;非末关走 bonus 载荷)
|
||||
"keystone_drops": _keystone_drops.duplicate(true), # additive 扩签(逐杀掉落明细;settle 入账明细/复盘归因数据源)
|
||||
"vision_used_delta": int(run_ledger.vision_counter.get("used", 0)), # additive 扩签(真视使用本局计数;settle 聚合入档)
|
||||
"pool_stock": _pool_stock_snapshot(), # additive 扩签(终局池读数:清账前快照,商店买走 vs 奖励领走 vs 枯竭的终局面)
|
||||
"relics": _feats.duplicate(), # additive 扩签(特性终局持有集:清账前快照,成型判读取数面)
|
||||
}
|
||||
# 败退明细(数值验证工具取数面 2026-09-22——tools/balance_verifier.py 止步节点分布数据源;
|
||||
# 败退明细(数值验证工具取数面——tools/balance_verifier.py 止步节点分布数据源;
|
||||
# 仅失败局附加,键缺省 = 未败退,既有五件事消费方零感知)
|
||||
if result == "failed" and not _last_level_payload.is_empty():
|
||||
summary["defeat"] = {
|
||||
@@ -970,11 +968,11 @@ func _settle(result: String) -> void:
|
||||
"node_type": String(_last_level_payload.get("node_type", "")),
|
||||
}
|
||||
if _runner != null:
|
||||
summary["gp_final"] = int(_runner.gp) # gp_final 清账前快照(迭代 27 T3——P1-3 GP 影子账收口:终局面板取数源,影子账随批退役)
|
||||
summary["gp_final"] = int(_runner.gp) # gp_final 清账前快照(终局面板取数源=runner 钱包单点)
|
||||
_runner.close_wallet() # GP 冒险收束清零(三结局都清——经转发发 wallet_changed "clear" 供日志/呈现)
|
||||
_reset_curio_state() # 三选一停点态收束清零(结算受理只算一次语义——重复宣布零变化)
|
||||
if _save_manager != null:
|
||||
_save_manager.clear_breakpoint() # 三结局清断点(迭代 8 卡①/T3——退出冒险=放弃流程+清除断点)
|
||||
_save_manager.clear_breakpoint() # 三结局清断点(退出冒险=放弃流程+清除断点)
|
||||
_save_manager.write_to_disk()
|
||||
_state = State.SETTLING
|
||||
adventure_finished.emit(summary)
|
||||
@@ -989,12 +987,12 @@ func _forward_node_entered(node: Dictionary, info: Dictionary) -> void:
|
||||
|
||||
func _forward_choices_presented(candidates: Array) -> void:
|
||||
choices_presented.emit(candidates)
|
||||
_snapshot_breakpoint() # 停点快照(迭代 8 卡①——选路停点=唯一可恢复停点形态;战斗中不存)
|
||||
_snapshot_breakpoint() # 停点快照(选路停点=唯一可恢复停点形态;战斗中不存)
|
||||
|
||||
|
||||
# ---- 断点快照(迭代 8 卡①——七字段拍板面+恢复必需附随件)----
|
||||
# 七字段(拍板面):关卡序号/节点位/生命/属性加成区/GP 钱包/商店已购档/已抽模板;
|
||||
# 附随件(续玩正确性必需,随批注记):主种子+取数计数器(rng 快照契约回填——续接原时间线)、
|
||||
# ---- 断点快照(七字段契约面+恢复必需附随件)----
|
||||
# 七字段(契约面):关卡序号/节点位/生命/属性加成区/GP 钱包/商店已购档/已抽模板;
|
||||
# 附随件(续玩正确性必需):主种子+取数计数器(rng 快照契约回填——续接原时间线)、
|
||||
# 钥石累计(大奖励中途值——终局摘要五件事不失真)、已到节点序(呈现层图条重建)、道具库存
|
||||
# (奖励节点已选件——防静默丢失)。注入缺省 null = 整面静默(模拟/冒烟零扰动)。
|
||||
func _snapshot_breakpoint() -> void:
|
||||
@@ -1004,7 +1002,7 @@ func _snapshot_breakpoint() -> void:
|
||||
if _runner != null and _runner.treasure_inventory != null:
|
||||
treasure_snapshot = _runner.treasure_inventory.snapshot_inventory()
|
||||
# 三选一停点续玩分支 _runner 为 null(关卡已收束、下一关未开)——节点域字段按缺省落档,
|
||||
# 该两态的续玩只消费 bonus_* 附随件(选择停点/已兑付停点快照,迭代 10)
|
||||
# 该两态的续玩只消费 bonus_* 附随件(选择停点/已兑付停点快照)
|
||||
var bp: Dictionary[Variant, Variant] = {
|
||||
"level_index": level_index,
|
||||
"node_id": _runner.current_node_id() if _runner != null else -1,
|
||||
@@ -1017,9 +1015,9 @@ func _snapshot_breakpoint() -> void:
|
||||
},
|
||||
"gp": int(ledger.get("gp", 0)),
|
||||
"shop_purchased": purchased.duplicate(true),
|
||||
"shop_shelf": _runner.shop_bp_shelf() if _runner != null else [], # 迭代 11 第②批 T2——P2 甲货架入断点(离开店/非店停点 = 空架合法态)
|
||||
"shop_visit_buys": _runner.shop_bp_visit_buys() if _runner != null else 0, # 迭代 11 第②批 T2——本次进店已购次数(A 链定价输入,P2 甲扩列)
|
||||
"attunement_charges": run_ledger.attune_charges.duplicate(true), # 迭代 11 第①批 T6——同调主动件充能余量随断点(save v4 域)
|
||||
"shop_shelf": _runner.shop_bp_shelf() if _runner != null else [], # 货架入断点(离开店/非店停点 = 空架合法态)
|
||||
"shop_visit_buys": _runner.shop_bp_visit_buys() if _runner != null else 0, # 本次进店已购次数(A 链定价输入)
|
||||
"attunement_charges": run_ledger.attune_charges.duplicate(true), # 同调主动件充能余量随断点(save v4 域)
|
||||
"template_pick": _runner.active_template_index if _runner != null else -1,
|
||||
"master_seed": master_seed,
|
||||
"rng_counter": rng.counter(),
|
||||
@@ -1028,16 +1026,16 @@ func _snapshot_breakpoint() -> void:
|
||||
"aux_items": treasure_snapshot,
|
||||
"bonus_taken": _curio_taken.duplicate(true), # 三选一件获取次数(kind 维度派生账本——断点附随件与读数承载)
|
||||
"bonus_deck": _curio_deck.duplicate(true), # 令牌池余库(TP0 v2.2——池消耗即上限,续玩余库逐枚还原)
|
||||
"relics": _feats.duplicate(), # 特性持有集(迭代 12 T1——局内随机渠道获得态续玩防丢失)
|
||||
"relics": _feats.duplicate(), # 特性持有集(局内随机渠道获得态续玩防丢失)
|
||||
"bonus_pending": _curio_pending.duplicate(true), # 三选一停点态(空 = 无停点——选择停点可中断契约)
|
||||
"feat_ticket_pity": _feat_ticket_pity, # T5 门票保底计数(扩列向后兼容——旧档缺键缺省 0)
|
||||
"feat_ticket_sold": _feat_ticket_sold, # T5 门票限购计数(同上)
|
||||
"adventure_counters": run_ledger.adventure_counters.duplicate(true), # 迭代 20 T6/T7——adventure 域计数器随断点(续玩跨战账不丢)
|
||||
"vision_counter_used": int(run_ledger.vision_counter.get("used", 0)), # 迭代 22 秘宝批 T3——真视使用计数随断点(续玩跨战账不丢)
|
||||
"refresh_stock": _refresh_stock, # 迭代 20 T4/T7——刷新存量账随断点(F-1 新账)
|
||||
"attune_refresh_y": _attune_refresh_y, # 2026-10-13 缺陷修批——Y 冒险周期池余量随断点(续玩不再自参数面整额重注入=堵「用完退菜单再续玩满血复活」可利用漏洞;扩列向后兼容——旧档缺键回退参数面)
|
||||
"adventure_counters": run_ledger.adventure_counters.duplicate(true), # adventure 域计数器随断点(续玩跨战账不丢)
|
||||
"vision_counter_used": int(run_ledger.vision_counter.get("used", 0)), # 真视使用计数随断点(续玩跨战账不丢)
|
||||
"refresh_stock": _refresh_stock, # 刷新存量账随断点
|
||||
"attune_refresh_y": _attune_refresh_y, # Y 冒险周期池余量随断点(续玩不再自参数面整额重注入=堵「用完退菜单再续玩满血复活」可利用漏洞;扩列向后兼容——旧档缺键回退参数面)
|
||||
"bonus_result": _curio_result.duplicate(true), # 已兑付待推进态(空 = 无——中断续玩不重兑付)
|
||||
"keystone_drops": _keystone_drops.duplicate(true), # 逐杀掉落明细(摘要归因续接——迭代 10)
|
||||
"keystone_drops": _keystone_drops.duplicate(true), # 逐杀掉落明细(摘要归因续接)
|
||||
}
|
||||
_save_manager.set_breakpoint(bp)
|
||||
_save_manager.write_to_disk()
|
||||
|
||||
+140
-144
@@ -7,28 +7,26 @@ class_name BattleOrchestrator
|
||||
extends RefCounted
|
||||
|
||||
signal step_row(row: Dictionary)
|
||||
signal battle_finished(payload: Dictionary) # {result, hero_hp, gp_dropped}——hero 在 despawn 前捕获(呈现层销毁后读状态的时序防护);gp_dropped = 战斗掉落合计(GP+商店房实施批 2026-09-21)
|
||||
signal battle_finished(payload: Dictionary) # {result, hero_hp, gp_dropped}——hero 在 despawn 前捕获(呈现层销毁后读状态的时序防护);gp_dropped = 战斗掉落合计
|
||||
signal round_started(round_index: int)
|
||||
signal board_changed()
|
||||
signal settlement_snapshot(snap: Dictionary) # 每结算末尾的全量状态快照(playtest_logger 消费——JSONL 数值分析)
|
||||
|
||||
# 明细流水行型协议(战斗结算 Core Rule;9 类——2026-09-20 伏击单轮上限退役,溢出未生效行 overflow 随之下线;
|
||||
# 2026-09-15 格面资源批由 15 类收拢,清单改动须同步 battle_log_view 的 _format_row 与本文档头注):
|
||||
# 明细流水行型协议(战斗结算 Core Rule;9 类;
|
||||
# 清单改动须同步 battle_log_view 的 _format_row 与本文档头注):
|
||||
# 效果行 effect · 伤害行 damage · 治疗行 heal ·
|
||||
# 奖励未生效行 reward_ineffective · 封印未中行 seal_miss · 施放行 cast(敌人技能池批由蓄力行 charge 改型)· 衰减行 decay ·
|
||||
# 奖励未生效行 reward_ineffective · 封印未中行 seal_miss · 施放行 cast · 衰减行 decay ·
|
||||
# 判定行 verdict · 高光行 highlight
|
||||
# 行来源断言豁免集合(行型层)——判定段 / 衰减段本就无棋盘来源,勿一刀切误炸
|
||||
const SOURCE_EXEMPT_TYPES := ["decay", "verdict"]
|
||||
# 行来源断言豁免集合(来源类别层)——事实来源是单位 / 清图而非棋盘格;
|
||||
# fx_reveal=专长揭示掷格(落空行无真实格〔迭代 21 存量红修复——a57489e9 落地件三处空来源
|
||||
# 触发行来源断言,51 行/冒烟,两轮确定性复现〕);
|
||||
# fx_grant_gp=专长·GP 入账(钱包面而非棋盘面,行无格子可指——与 fx_reveal 同款的漏网豁免,
|
||||
# 未豁免时每局触发行来源断言失败 7 次,因脚本错误不改退出码而被闸长期漏过)
|
||||
# fx_reveal=专长揭示掷格(落空行无真实格);
|
||||
# fx_grant_gp=专长·GP 入账(钱包面而非棋盘面,行无格子可指)
|
||||
const SOURCE_EXEMPT_CLASSES := ["hero_attack", "enemy_action", "clear_reward", "fx_reveal", "fx_grant_gp"]
|
||||
|
||||
var db: ConfigDb
|
||||
var rng: RngServiceCore
|
||||
# E1 封装收口(2026-09-13):协作对象 board_generator/enemy_system/status_effects/reward_economy 私有化——跨层读取一律经 presentation_snapshot()/
|
||||
# E1 封装收口:协作对象 board_generator/enemy_system/status_effects/reward_economy 私有化——跨层读取一律经 presentation_snapshot()/
|
||||
# diagnostic_snapshot() 成文出口(呈现层链式穿透 settlement._enemy_system.hero_unit() 的根因收口)。
|
||||
# minesweeper/action_economy 保持公有 = 架构文档 API Boundaries 的 HUD框架消费面(意图提交 settle_batch + compute_move_path
|
||||
# 只读;ap/rap/相位读取)——非本文档登记的契约面,勿增新用途。
|
||||
@@ -38,9 +36,9 @@ var action_economy: ActionEconomy
|
||||
var _enemy_system: UnitContainer
|
||||
var _status_effects: StatusContainer
|
||||
var _reward_economy: RewardEconomy
|
||||
var treasure_inventory: TreasureItems # 迭代 7 T3——LevelRunner 注入的关卡域库存引用;
|
||||
var treasure_inventory: TreasureItems # LevelRunner 注入的关卡域库存引用;
|
||||
# 每战新建本类,库存不归本类所有——注入面
|
||||
var _damage: DamageController # 伤害控制器(伤害管线单据化批 2026-09-24——单据唯一兑付管线)
|
||||
var _damage: DamageController # 伤害控制器(单据唯一兑付管线)
|
||||
|
||||
var encounter: Dictionary[String, Variant] = {}
|
||||
var round_index := 0
|
||||
@@ -51,38 +49,38 @@ var _facts_reveals: Array[Dictionary] = []
|
||||
var _facts_seals: Array[Dictionary] = []
|
||||
var _facts_path := ""
|
||||
var _settling := false
|
||||
var _defer_board_damage := false # 案C 暂存闸(2026-10-06 用户裁:地图奖励伤害兑付迁战斗段末——怪物还手后)
|
||||
var _defer_board_damage := false # 案C 暂存闸(地图奖励伤害兑付在战斗段末——怪物还手后)
|
||||
var _deferred_board: Array[Dictionary] = [] # 授予段暂存伤害单据(保授予段事件序;战斗段怪物还手后按序补提交统一结账)
|
||||
# 结算五段 Segment(迭代 27 T2——P1-1 拆段;段边界=审计五段〔裁点③甲〕,段序骨架冻结于
|
||||
# SettlementTable.segment_order 语义:授予→增益/减益→战斗→衰减→判定。本类退为段序编排+
|
||||
# 结算五段 Segment(段边界=审计五段,段序骨架冻结于
|
||||
# SettlementTable.segment_order 语义:授予→增益/减益→战斗→衰减→判定。本类做段序编排+
|
||||
# 行发射/开单原语单点——段体逻辑归 game/core/settlement/ 五类)
|
||||
var _grant_seg = null # GrantSegment(①授予段——含封印判与 counter/grant 助手单点)
|
||||
var _aura_seg = null # AuraSegment(②增益/减益段)
|
||||
var _combat_seg = null # CombatSegment(③战斗段——原 _combat_segment 体)
|
||||
var _combat_seg = null # CombatSegment(③战斗段)
|
||||
var _decay_seg = null # DecaySegment(④衰减段)
|
||||
var _verdict_seg = null # VerdictSegment(⑤判定段——返回裁定,终局早退语义归本类)
|
||||
var _refresh_used_this_round := 0 # 每轮刷新计数(Active 相位合计,兜底上限 = ActionEconomyTable.refresh_cap_per_round)
|
||||
var _board_seq := 0 # 板序号(本战斗第几张板——快照归因用)
|
||||
var _board_from_midround_refresh := false # 装板 provenance:轮内刷新换来的图 = true(清图奖励守卫——刷新图不发清图奖励)
|
||||
var _seal_consumed := {} # 兑付资格以危险格实例为键(板级;换图重置)
|
||||
# 同调装配运行态(迭代 11 第①批 T5——begin_battle 经 enc.attunement 注入;off=空集全部效果零扰动)
|
||||
# 同调装配运行态(begin_battle 经 enc.attunement 注入;off=空集全部效果零扰动)
|
||||
var attunement: Array[Variant] = [] # 装备中同调件 item_id 集
|
||||
var _ledger: RunLedger = null # 共享四本账(迭代 27 T3——begin_battle 经 enc["ledger"] 持引用;缺省新账=调试面)
|
||||
var _attune_safe_seen := 0 # 积点制件 battle 域安全开格计数(迭代 22 T3 通用化——装配集 ap_charge_safe 行扫描;反哺批⑤ 2026-10-12 用户裁:满 charge_n 自动兑付当轮 AP+1,无玩家发起入口)
|
||||
# ---- 效果引擎扩签运行态(迭代 20 专长实装批 T1——新效果类型施加面;空订阅零扰动)----
|
||||
var _ledger: RunLedger = null # 共享四本账(begin_battle 经 enc["ledger"] 持引用;缺省新账=调试面)
|
||||
var _attune_safe_seen := 0 # 积点制件 battle 域安全开格计数(装配集 ap_charge_safe 行扫描;满 charge_n 自动兑付当轮 AP+1,无玩家发起入口)
|
||||
# ---- 效果引擎扩签运行态(新效果类型施加面;空订阅零扰动)----
|
||||
var _fx_hero_ac_base := -1 # 主角 AC 基线快照(enc.hero_armor_class;-1=表值兜底未解析)
|
||||
var _fx_ac_round := 0 # ac_bonus 本轮累积器(封印桩形态——round_start 清零)
|
||||
var nest_preview_count := -1 # 巢穴预告读数口(迭代 22 秘宝批 T3——board_installed 命中落值;-1=未装配/未发射;呈现桥消费)
|
||||
var nest_preview_count := -1 # 巢穴预告读数口(board_installed 命中落值;-1=未装配/未发射;呈现桥消费)
|
||||
var _fx_ac_battle := 0 # ac_bonus 本战累积器(石肤形态——cap 封顶,begin_battle 清零)
|
||||
var _fx_bonus_delta := {"atk": 0, "max_hp": 0, "armor_class": 0, "ap": 0} # 本战 bonus_delta 累积(战末经 _ledger.bonus_add_axis 汇入后清零)
|
||||
var _fx_new_resource_types := {} # 本战已见资源类型集(地质学家 fact new_resource_type 判定——battle 域)
|
||||
var _fx_pending_reveals: Array[Dictionary] = [] # 揭示效果待兑队列(见血开窍/斩首学者/地质学家/跳格采掘——授予段末统一兑付,防轮 facts 迭代中追加重入)
|
||||
var _fx_revealing := false # fx 揭示兑付中标记(_on_cell_revealed 缓冲旁路——fx 揭示的奖励由兑付点直接入账,防二次缓冲二次兑付)
|
||||
# 特性运行态(内容数据驱动扩展批 T3——relic 持有集经 enc.feats 注入:效果行为=数据行 triggers 经
|
||||
# 特性运行态(relic 持有集经 enc.feats 注入:效果行为=数据行 triggers 经
|
||||
# 效果引擎分发;魔矢每战首次计数 battle 域。空集=零扰动)
|
||||
var _feat_first_dig_used := false
|
||||
# 效果引擎(内容数据驱动扩展批 T1——发布订阅解释器;begin_battle 从持有集/装备集重建订阅。
|
||||
# T3 起施加面全量切换:发射点 dispatch 返回命中,发射点按组合规则施加——旧 feat_seals 封印注入消亡)
|
||||
# 效果引擎(发布订阅解释器;begin_battle 从持有集/装备集重建订阅。
|
||||
# 施加面:发射点 dispatch 返回命中,发射点按组合规则施加)
|
||||
var _fx: EffectEngine
|
||||
|
||||
|
||||
@@ -95,7 +93,7 @@ func _fx_emit(event: String, payload: Dictionary) -> Array:
|
||||
|
||||
# round_start 发射+施加(内容数据驱动扩展批 T3——grant_ap mode:queue=当轮入队,行动经济轮切换
|
||||
# 即兑现;计数器 round_start 重置点在 dispatch 内生效。调用点两处:begin_battle 首轮 / 轮始段续轮。
|
||||
# 迭代 20 T1——payload 扩 ap_exhausted_last〔一滴不剩 when 面〕+ac_bonus 本轮累积器清零)
|
||||
# payload 扩 ap_exhausted_last〔一滴不剩 when 面〕+ac_bonus 本轮累积器清零)
|
||||
func _emit_round_start() -> void:
|
||||
_fx_ac_round = 0
|
||||
_sync_hero_ac()
|
||||
@@ -113,7 +111,7 @@ func _sync_hero_ac() -> void:
|
||||
_damage.set_hero_ac_override(_fx_hero_ac_base + _fx_ac_round + _fx_ac_battle)
|
||||
|
||||
|
||||
# ac_bonus 施加(迭代 20 T1——mode=round 本轮累积器〔封印桩,round_start 清零〕/
|
||||
# ac_bonus 施加(mode=round 本轮累积器〔封印桩,round_start 清零〕/
|
||||
# mode=battle 本战累积器〔石肤,cap 封顶;缺省 battle〕;cap=0/缺省=无上限)
|
||||
func _apply_ac_bonus(h: Dictionary) -> void:
|
||||
var mode := String(h.get("mode", "battle"))
|
||||
@@ -192,16 +190,15 @@ func _attune_has(item_id: String) -> bool:
|
||||
return attunement.has(item_id)
|
||||
|
||||
|
||||
# 装配集只读出口(迭代 19 卡④立——呈现桥消费面现归 attune_param_sum〔迭代 22 T4 换代〕。
|
||||
# _attune_has 契约化出口,save v6 只读访问器先例;private 面内部调用零改动。只读零副作用——
|
||||
# 装配集只读出口(_attune_has 契约化出口,save v6 只读访问器先例。只读零副作用——
|
||||
# 呈现侧判定专用,非结算写入口)
|
||||
func attune_has(item_id: String) -> bool:
|
||||
return _attune_has(item_id)
|
||||
|
||||
|
||||
# 装备件表值读取(charge_n 等——TreasureItemTable 行;未装配不读表。magnitude 读数面随 T4
|
||||
# 秘宝订阅迁移退役——行值经 value_from 在 rebuild 定影,零运行期按件取值)
|
||||
# 装配集参数合计(迭代 22 秘宝批 T3——常时参数面 kind 的装配集 Σmagnitude 读数口;
|
||||
# 装备件表值读取(charge_n 等——TreasureItemTable 行;未装配不读表。
|
||||
# 行值经 value_from 在 rebuild 定影,零运行期按件取值)
|
||||
# 装配集参数合计(常时参数面 kind 的装配集 Σmagnitude 读数口;
|
||||
# kind 分发查询先例=_attune_refresh_x_total bonus_refresh。零装配=0)
|
||||
func attune_param_sum(kind: String) -> int:
|
||||
var total := 0
|
||||
@@ -212,8 +209,8 @@ func attune_param_sum(kind: String) -> int:
|
||||
return total
|
||||
|
||||
|
||||
# 装配集内 ap_charge_safe 行读取(迭代 22 秘宝批 T3——掘进蓄能 id 特判通用化取数源;
|
||||
# 首行语义=装配集内至多一件积点制〔新目录恰试炼镐〕)
|
||||
# 装配集内 ap_charge_safe 行读取(取数源=装配集行扫描;
|
||||
# 首行语义=装配集内至多一件积点制〔试炼镐〕)
|
||||
func _attune_charge_safe_row() -> Dictionary:
|
||||
for iid_v in attunement:
|
||||
var arow: Dictionary[Variant, Variant] = db.treasure_item_row(String(iid_v))
|
||||
@@ -222,7 +219,7 @@ func _attune_charge_safe_row() -> Dictionary:
|
||||
return {}
|
||||
|
||||
|
||||
# 充能重置(迭代 22 秘宝批 T3——按周期回满:账本回 charges_per_adventure+骑库存差额补注入
|
||||
# 充能重置(按周期回满:账本回 charges_per_adventure+骑库存差额补注入
|
||||
# (used handle 不回收=本轮已耗充能作废,补新 handle=「回满」;差额注入防双份=level_runner
|
||||
# set_attune_charges 同构)。reset ∈ {battle, level}——adventure 周期归冒险开始种子面)
|
||||
func _reseed_attune_charges(reset: String) -> void:
|
||||
@@ -246,7 +243,7 @@ func _reseed_attune_charges(reset: String) -> void:
|
||||
treasure_inventory.add_item(iid, "attunement", Vector2i(-1, -1), 0)
|
||||
|
||||
|
||||
# 积点制件进度读数(迭代 22 反哺批⑤——自动兑付无玩家发起入口,读数口=C 区秘宝槽悬停浮窗进度行:
|
||||
# 积点制件进度读数(自动兑付无玩家发起入口,读数口=C 区秘宝槽悬停浮窗进度行:
|
||||
# assembled=装配在册/points=本战安全格进度(charge_n 取余)/need=charge_n/item_id=装配行 id。
|
||||
# 未装配=assembled false)
|
||||
func attune_convert_state() -> Dictionary:
|
||||
@@ -258,13 +255,13 @@ func attune_convert_state() -> Dictionary:
|
||||
"display_name": String(row.get("display_name", "")), "item_id": String(row.get("item_id", ""))}
|
||||
|
||||
|
||||
# 特性求值(内容数据驱动扩展批 T3——_feat_seals_for 挂点求值退役:效果行为=数据行 triggers 订阅,
|
||||
# 效果引擎 dispatch 分发;旧 23 hook 枚举与封印注入消亡。流派档位质变=FeatsEngine 纯函数求值
|
||||
# 特性求值(效果行为=数据行 triggers 订阅,效果引擎 dispatch 分发;
|
||||
# 流派档位质变=FeatsEngine 纯函数求值
|
||||
# (档位机制本身系引擎基建非内容效果),两运行态注伤害控制器:档一 rider+1 / 档二 rider 可暴击)
|
||||
var _seal_miss_seen := {} # 封印未中行去重(每安全格实例一次)
|
||||
var _perfect_hit_true := 0 # 板级完美排雷计数(install 重置)
|
||||
var _perfect_hit_false := 0
|
||||
var _perfect_forgiven := 0 # 完美保险豁免累计(迭代 31 卡②——单向累加自有账;契约 2 禁改写 _perfect_hit_false)
|
||||
var _perfect_forgiven := 0 # 完美保险豁免累计(单向累加自有账;契约 2 禁改写 _perfect_hit_false)
|
||||
var _hero_atk := 0 # 主角攻击基值快照(encounter.hero_atk;E4 收口——表值运行期不可变)
|
||||
|
||||
|
||||
@@ -287,7 +284,7 @@ func _init(db_ref: ConfigDb, rng_ref: RngServiceCore) -> void:
|
||||
minesweeper.seal_judged.connect(_on_seal_judged)
|
||||
minesweeper.round_end_board.connect(_on_round_end)
|
||||
action_economy.round_end_stopped.connect(_on_round_end)
|
||||
# 结算五段接线(迭代 27 T2——段持宿主引用;信号连接纪律不涉段〔段零信号连接〕)
|
||||
# 结算五段接线(段持宿主引用;信号连接纪律不涉段〔段零信号连接〕)
|
||||
_grant_seg = GrantSegment.new(self)
|
||||
_aura_seg = AuraSegment.new(self)
|
||||
_combat_seg = CombatSegment.new(self)
|
||||
@@ -302,24 +299,24 @@ func begin_battle(enc: Dictionary) -> bool:
|
||||
push_warning("[战斗结算] begin_battle 重复受理")
|
||||
return false
|
||||
encounter.assign(enc)
|
||||
escrow = 0 # 战斗域零 GP 持久(GP+商店房实施批——每场战斗缓冲从零起算)
|
||||
_damage.set_hero_ac_override(int(enc.get("hero_armor_class", -1))) # 主角护甲合流入参(伤害控制器 defender_ac 消费——口径同原 _defender_ac)
|
||||
escrow = 0 # 战斗域零 GP 持久(每场战斗缓冲从零起算)
|
||||
_damage.set_hero_ac_override(int(enc.get("hero_armor_class", -1))) # 主角护甲合流入参(伤害控制器 defender_ac 消费)
|
||||
db.set_reload_gate(true)
|
||||
if not _status_effects.notify_battle_start():
|
||||
_fatal("增益状态 开域被拒(编排违例)")
|
||||
return false
|
||||
# 遭遇骰(难度曲线与Boss GDD Core Rule 8——2026-09-12 用户拍板):行有 unit_count_max 时按 [unit_count, max] 均匀骰;
|
||||
# 2026-09-14 敌方攻击区间批扩展:行有 atk_min 时按 [atk_min, atk_max] 骰 spawn 期整战持久值
|
||||
# 遭遇骰(难度曲线与Boss GDD Core Rule 8):行有 unit_count_max 时按 [unit_count, max] 均匀骰;
|
||||
# 行有 atk_min 时按 [atk_min, atk_max] 骰 spawn 期整战持久值
|
||||
# (每拍抖动否决——数字可读性锚;中点 = 行值)。encounter 封闭流恰一 scope(数量骰先、atk 骰后);
|
||||
# 挖掘决策域之外——P1 合规;敌人系统 零随机纪律保持(收 override 即确定值)。
|
||||
# 遭遇骰(难度曲线与Boss GDD Core Rule 8——2026-09-12 用户拍板):行有 unit_count_max 时按 [unit_count, max] 均匀骰;
|
||||
# 2026-09-14 敌方攻击区间批扩展:行有 atk_min 时按 [atk_min, atk_max] 骰 spawn 期整战持久值
|
||||
# 遭遇骰(难度曲线与Boss GDD Core Rule 8):行有 unit_count_max 时按 [unit_count, max] 均匀骰;
|
||||
# 行有 atk_min 时按 [atk_min, atk_max] 骰 spawn 期整战持久值
|
||||
# (每拍抖动否决——数字可读性锚;中点 = 行值)。encounter 封闭流恰一 scope(数量骰先、atk 骰后);
|
||||
# 挖掘决策域之外——P1 合规;敌人系统 零随机纪律保持(收 override 即确定值)。
|
||||
# 逐行骰换代(2026-10-07 行集多行批 Q8 用户裁 + P1 补充):混编行集**每行各掷自己的区间骰**——
|
||||
# 逐行骰:混编行集**每行各掷自己的区间骰**——
|
||||
# 先按行序逐行数量骰、再按行序逐行 atk 骰(单行行集 = 既有一次数量骰 + 一次 atk 骰,判定条件、
|
||||
# 开流条件、消费序、消费次数逐位一致——零内容期随机流零变化;区间宽度为零的行不掷 = 「定值不骰」
|
||||
# 既有口径;atk 区间逐行同构 = 行值语义完整,代拍板随批留痕:属「每行一骰」预批准语义内的
|
||||
# 既有口径;atk 区间逐行同构 = 行值语义完整,属「每行一骰」语义内的
|
||||
# 行字段区间骰逐行化,非新增随机源形态)。
|
||||
var erowset_pre: Array[Variant] = db.resolve_enemy_rowset(int(enc.level_index), int(enc.node_depth))
|
||||
if erowset_pre.is_empty():
|
||||
@@ -351,21 +348,21 @@ func begin_battle(enc: Dictionary) -> bool:
|
||||
_hero_atk = int(enc.get("hero_atk", int(db.hero.atk)))
|
||||
if not _enemy_system.spawn(int(enc.level_index), int(enc.node_depth), int(enc.hero_hp), count_overrides,
|
||||
_hero_atk, int(enc.get("hero_max_hp", -1)), atk_overrides,
|
||||
String(enc.get("attack_type", "")), String(enc.get("armor_type", ""))): # 迭代 23 T4——两根类型轴(空串=锻造配置默认值回退,行为连续)
|
||||
String(enc.get("attack_type", "")), String(enc.get("armor_type", ""))): # 两根类型轴(空串=锻造配置默认值回退,行为连续)
|
||||
_fatal("敌人系统 spawn 被拒")
|
||||
return false
|
||||
if not _install_new_board():
|
||||
return false
|
||||
_reset_round_facts() # 首板 install 的初始展开事件不入轮 facts(resource 恒 false 的双保险)
|
||||
# 同调装配运行态注入(迭代 11 第①批 T5——enc.attunement 携带;off=空集零扰动。淬毒 N 注入
|
||||
# 同调装配运行态注入(enc.attunement 携带;off=空集零扰动。淬毒 N 注入
|
||||
# 伤害控制器、偏运 N 注入地图战斗资源——均须先于首板 install)
|
||||
attunement = (enc.get("attunement", []) as Array).duplicate()
|
||||
_attune_safe_seen = 0
|
||||
# 淬毒/偏运注入拆除(内容数据驱动扩展批 T4——两件被动改 triggers 订阅行:淬毒=clamp_check
|
||||
# modify 窗口、偏运=reward_roll bias_rewards;value 行字段 value_from 定影,零 begin_battle 注入)
|
||||
# 特性运行态(内容数据驱动扩展批 T3——enc.feats 携带持有集:效果行为=数据行 triggers 经效果
|
||||
# 淬毒/偏运=triggers 订阅行(淬毒=clamp_check
|
||||
# modify 窗口、偏运=reward_roll bias_rewards;value 行字段 value_from 定影,begin_battle 不注入)
|
||||
# 特性运行态(enc.feats 携带持有集:效果行为=数据行 triggers 经效果
|
||||
# 引擎分发;流派档位=FeatsEngine 纯函数求值两运行态注伤害控制器。魔矢每战首次开格计数同步归零)
|
||||
# 迭代 20 T1——扩签运行态就位:AC 基线快照、累积器清零、adventure 计数器/加算区共享引用接收
|
||||
# 扩签运行态就位:AC 基线快照、累积器清零、adventure 计数器/加算区共享引用接收
|
||||
_fx_hero_ac_base = int(enc.get("hero_armor_class", -1))
|
||||
_fx_ac_round = 0
|
||||
_fx_ac_battle = 0
|
||||
@@ -377,31 +374,31 @@ func begin_battle(enc: Dictionary) -> bool:
|
||||
_damage.fx_reveal_request = _fx_enqueue_reveal
|
||||
_damage.fx_ac_request = _apply_ac_bonus
|
||||
var feat_ids: Array[Variant] = enc.get("feats", []) as Array
|
||||
# 迭代 20 T3 换代(V14 旧实现摘除)——流派档位质变随重设计退役(奇物.md v1「不设流派标签、
|
||||
# 不设档位机制」):FeatsEngine 求值与 rider_tier 注入拆除,散件单体效果=唯一形态
|
||||
# 流派档位质变不设(奇物.md v1「不设流派标签、
|
||||
# 不设档位机制」):rider_tier 两注入恒零,散件单体效果=唯一形态
|
||||
_damage.rider_tier_bonus = 0
|
||||
_damage.rider_tier_crit = false
|
||||
_feat_first_dig_used = false
|
||||
# 树 rule 键消费(迭代 12 T10 → 内容数据驱动扩展批 T3 换代:Fork 激活侧效果=效果引擎合成
|
||||
# 订阅(rebuild 注入——round_start 首轮 AP+1 / damage_taken 减伤+1),旧直调拆除。其余 rule 键
|
||||
# 树 rule 键消费(Fork 激活侧效果=效果引擎合成
|
||||
# 订阅(rebuild 注入——round_start 首轮 AP+1 / damage_taken 减伤+1)。其余 rule 键
|
||||
# =stat 面投影不入战斗(T8 表内容 provisional 口径披露))
|
||||
var tree_rules: Array[Variant] = enc.get("tree_rules", []) as Array
|
||||
# 效果引擎订阅重建(内容数据驱动扩展批 T1——每战从持有集/装备集重建,中途获得下一战生效;
|
||||
# 效果引擎订阅重建(每战从持有集/装备集重建,中途获得下一战生效;
|
||||
# 树规则合成订阅同面装载。装载序=feats 表序→treasure 表序→树规则,写死保种子复放。
|
||||
# 迭代 20 T6——adventure 域计数器从共享引用回灌〔跨战持久〕)
|
||||
# adventure 域计数器从共享引用回灌〔跨战持久〕)
|
||||
_fx.rebuild(feat_ids, attunement, tree_rules)
|
||||
_fx.counters_seed(_ledger.counters_seed_snapshot())
|
||||
# 秘宝常时参数注入(迭代 22 秘宝批 T3——装配集 kind 分发:远程镐挖距放宽/信标网络封印可达豁免;
|
||||
# 缺省 0/false=零装配零扰动。注入先于首板 install 已不成立〔板在早段〕——本批板生成不读两参数,
|
||||
# 秘宝常时参数注入(装配集 kind 分发:远程镐挖距放宽/信标网络封印可达豁免;
|
||||
# 缺省 0/false=零装配零扰动。注入在首板 install 之后〔板在早段〕——板生成不读两参数,
|
||||
# 参数只影响挖掘/插棋动作判据,时序安全)
|
||||
minesweeper.dig_range_bonus = attune_param_sum("dig_range_bonus")
|
||||
minesweeper.seal_range_free = attune_param_sum("seal_range_free") > 0
|
||||
# 充能重置周期(迭代 22 秘宝批 T3——battle=每战回满/level=每关首战回满〔battle_index==1〕;
|
||||
# 充能重置周期(battle=每战回满/level=每关首战回满〔battle_index==1〕;
|
||||
# adventure 周期维持冒险开始 _seed_attune_charges 单次)
|
||||
_reseed_attune_charges("battle")
|
||||
if int(enc.get("battle_index", 1)) == 1:
|
||||
_reseed_attune_charges("level")
|
||||
# board_installed 发射+施加(迭代 22 秘宝批 T3——巢穴预告:装板后一次报本板危险格数量;
|
||||
# board_installed 发射+施加(巢穴预告:装板后一次报本板危险格数量;
|
||||
# 只报数量不标位置=信息红线,不碰分布=棋盘事实不可变契约 3。命中=读数口落值,呈现归
|
||||
# 呈现桥消费(无格来源不进结算明细行——行来源纪律);板已就绪〔早段 install〕、订阅行已在 rebuild 装载)
|
||||
nest_preview_count = -1
|
||||
@@ -409,27 +406,27 @@ func begin_battle(enc: Dictionary) -> bool:
|
||||
for h in bi_hits:
|
||||
if String(h.get("kind", "")) == "mine_count_preview":
|
||||
nest_preview_count = minesweeper.mine_total()
|
||||
# 迭代 10 奖励重构批——跨冒险行动点加成轴(统一冒险加算区第四轴):经排队修正通道兑现
|
||||
# 跨冒险行动点加成轴(统一冒险加算区第四轴):经排队修正通道兑现
|
||||
# 「每场战斗开始首轮 AP +加成值」(queue_ap_delta = 下轮开轮基线加算、notify_round_start 兑现后
|
||||
# 清零——首轮即消费,后续轮次不重复兑现;语义锚 = 行动经济 GDD「跨冒险行动点加成轴」节)
|
||||
var ap_bonus: int = int(enc.get("hero_ap_bonus", 0)) + int(enc.get("voucher_time_sand", 0)) # 迭代 20 T4——时沙同通道(首轮池增量,不改上限)
|
||||
var ap_bonus: int = int(enc.get("hero_ap_bonus", 0)) + int(enc.get("voucher_time_sand", 0)) # 时沙同通道(首轮池增量,不改上限)
|
||||
if ap_bonus > 0:
|
||||
action_economy.queue_ap_delta(ap_bonus)
|
||||
# 迭代 20 T4——储备金袋(节点级 RAP 注入)接收(完美保险注入通道随迭代 31 卡② 拆除——改由 feats.tres 订阅承载)
|
||||
# 储备金袋(节点级 RAP 注入)接收(完美保险注入= feats.tres 订阅承载,不经此通道)
|
||||
var voucher_rap: int = int(enc.get("voucher_rap", 0))
|
||||
if voucher_rap > 0:
|
||||
action_economy.grant_reserve_ap(voucher_rap)
|
||||
# 工兵晨令直调拆除(内容数据驱动扩展批 T4——round_start grant_ap(mode:queue) 订阅行承载,
|
||||
# when round_index ≤ effective_rounds 行字段 value_from 定影;发射位先于轮切换=当轮兑现)
|
||||
# round_start 发射+施加(内容数据驱动扩展批 T3——发射位先于行动经济轮切换:树规则 grant_ap
|
||||
# mode:queue 当轮入队、notify_round_start 即兑现(先手压制=首轮 AP+1 语义锚,与迁移前
|
||||
# begin_battle 直调 queue_ap_delta 逐位一致);计数器 round_start 重置点同点生效)
|
||||
# 工兵晨令=round_start grant_ap(mode:queue) 订阅行承载,
|
||||
# when round_index ≤ effective_rounds 行字段 value_from 定影;发射位先于轮切换=当轮兑现
|
||||
# round_start 发射+施加(发射位先于行动经济轮切换:树规则 grant_ap
|
||||
# mode:queue 当轮入队、notify_round_start 即兑现(先手压制=首轮 AP+1 语义锚);
|
||||
# 计数器 round_start 重置点同点生效)
|
||||
round_index = 1
|
||||
_emit_round_start()
|
||||
if not action_economy.notify_round_start():
|
||||
_fatal("行动经济 首轮 notify_round_start 被拒")
|
||||
return false
|
||||
_roll_round_actions() # 开轮摇号(敌人技能池批——首轮同样摇;先于 round_started,挖掘期动画位可读)
|
||||
_roll_round_actions() # 开轮摇号(首轮同样摇;先于 round_started,挖掘期动画位可读)
|
||||
round_started.emit(round_index)
|
||||
return true
|
||||
|
||||
@@ -439,7 +436,7 @@ func _install_new_board(during_round := false) -> bool:
|
||||
var board: BoardData = _board_generator.generate(int(encounter.level_index), int(encounter.node_depth), stream, db)
|
||||
if board == null:
|
||||
return false
|
||||
# 全局作用域变异决议(首发批 2026-10-04——装板序内前置于 roll_board:图级偏斜旋钮须在资源
|
||||
# 全局作用域变异决议(装板序内前置于 roll_board:图级偏斜旋钮须在资源
|
||||
# 摇号前定型;全局门控与板无关〔节点身份+unlock+board_types〕,确定性取用零取号)。
|
||||
# boss/elite 图命中全局型 → 写 board_variants(_place_variants 见非空即跳 region=替换制);
|
||||
# 全局池空 → 板级字段留空(_place_variants 原路走 region=兜底回落合法态)。
|
||||
@@ -462,7 +459,7 @@ func _install_new_board(during_round := false) -> bool:
|
||||
if not _reward_economy.roll_board(eligible):
|
||||
_fatal("地图战斗资源 roll_board 被拒")
|
||||
return false
|
||||
# 地形变异 布置段(2026-09-19 用户拍板棋盘设置顺序第 4 步:生成棋盘 → 打开状态初始化 →
|
||||
# 地形变异 布置段(棋盘设置顺序第 4 步:生成棋盘 → 打开状态初始化 →
|
||||
# 战斗资源格设置 → 变异格设置)——布置在初始展开与资源格之后,变异天然不落已开区
|
||||
_place_variants(board)
|
||||
_seal_consumed.clear() # 换图新雷 = 新实例,资格自然重置(行动经济 Rule 8)
|
||||
@@ -474,11 +471,11 @@ func _install_new_board(during_round := false) -> bool:
|
||||
_board_seq += 1
|
||||
board_changed.emit()
|
||||
if treasure_inventory != null:
|
||||
treasure_inventory.retire_overlays() # 迭代 7 T3——叠加层随图消亡(换图/刷新图装板点;已花道具不返还)
|
||||
treasure_inventory.retire_overlays() # 叠加层随图消亡(换图/刷新图装板点;已花道具不返还)
|
||||
return true
|
||||
|
||||
|
||||
# ---- 全局作用域变异 决议(首发批 2026-10-04——装板序内 roll_board 之前)----
|
||||
# ---- 全局作用域变异 决议(装板序内 roll_board 之前)----
|
||||
# boss/elite 图命中全局型:写板级 board_variants(恰一行)+ 注入资源面偏斜旋钮(图级投影在
|
||||
# RewardEconomy.roll_board 消费);不消费 variant_place 流(全局取用确定性——零取号合法态)。
|
||||
# 全局池空(含 L1 Boss 免疫/unlock 门控未达):字段留空、旋钮归零——_place_variants 原路走
|
||||
@@ -494,16 +491,16 @@ func _resolve_global_variant(board: BoardData) -> void:
|
||||
_reward_economy.board_skew = int(row.get("effect_value", 0))
|
||||
|
||||
|
||||
# ---- 地形变异 布置段(首批类型定稿批 2026-09-19——棋盘设置顺序第 4 步)----
|
||||
# ---- 地形变异 布置段(棋盘设置顺序第 4 步)----
|
||||
# 候选池门控(unlock_level ≤ 关卡序号 + board_types 与本地形交集,∅ 通配)经 config_db.variant_pool;
|
||||
# 池空 = 合法态,静默零布置(框架 P0 口径不变)。零特判:本段只写格子变异数据快照,不改扫雷规则本体。
|
||||
# 候选格域 = 安全格 − 初始展开区(伤害型 placement=closed_only 的结构保障——布置发生在初始展开之后,
|
||||
# 已开格天然排除);危险格不吃变异(挖雷一层惩罚 + 变异伤害双层叠罚污染逐行归因,首版排除——provisional)。
|
||||
# 每图至多一种类型(候选池内均匀选一,混型布置归因复杂留待后续批);数量 = round(候选池格数×percent) +
|
||||
# (关卡序号−1)×per_level_bonus——基数为候选池而非总格数(2026-09-21 用户拍板基数修正批:初始展开
|
||||
# 每图至多一种类型(候选池内均匀选一,混型布置归因复杂留待后续);数量 = round(候选池格数×percent) +
|
||||
# (关卡序号−1)×per_level_bonus——基数为候选池而非总格数(初始展开
|
||||
# 占全板 56–67%,总格数基数使有效密度膨胀 4–6 倍、L1 小板极端全池覆盖;percent 语义 = 占可挖格比例,
|
||||
# 关卡递增由 per_level_bonus 绝对格数承载);连通片生长(8 邻),单片 ≤ pack_max(2×2 / 1×4 / 1×3+1 等形)。
|
||||
# 替换制(全局作用域变异首发批 2026-10-04):板级 board_variants 非空 = 本图已带全局型,跳过 region
|
||||
# 替换制(全局作用域变异):板级 board_variants 非空 = 本图已带全局型,跳过 region
|
||||
# 布置(每图一型的板级推广:普通图=region,Boss/精英图=global);空 = 原路径(含 Boss 图全局池空回落)。
|
||||
func _place_variants(board: BoardData) -> void:
|
||||
if not board.board_variants.is_empty():
|
||||
@@ -572,14 +569,14 @@ func _on_cell_revealed(evt: Dictionary) -> void:
|
||||
# 轮 facts 缓冲不设相位守卫——轮末最后一挖的揭示事件在 request_dig 翻相之后才到(事件序:
|
||||
# cell_revealed 批 → seal_judged 批 → round_end),phase==Active 守卫会丢最后一挖的事实;
|
||||
# initial 通道事件(install 初始展开——封锁格)resource=false,进入授予段被资格检查自然跳过。
|
||||
# fx_reveal 通道事件旁路缓冲(迭代 20 T1——专长揭示的奖励由 _flush_pending_reveals 兑付点
|
||||
# fx_reveal 通道事件旁路缓冲(专长揭示的奖励由 _flush_pending_reveals 兑付点
|
||||
# 直接入账,此处缓冲会导致二次兑付)。
|
||||
if battle_result == "" and not _fx_revealing:
|
||||
# 积点自动兑付(迭代 11 同调批 T6 → 迭代 22 秘宝批 T3 通用化 → 反哺批⑤ 2026-10-12 用户裁:
|
||||
# 积点自动兑付:
|
||||
# 满 charge_n 直接当轮 grant_ap,无玩家发起入口、无积点账中转〔账即时清空,battle 域重置
|
||||
# 自然无感〕。落位=揭示缓冲点——事件序 cell_revealed 批先于本格扣费,AP 归零恰逢积点满时
|
||||
# 先兑再扣,轮次不因本格结束;授予段已无积点段(本段唯一计数点)。fx_reveal 旁路不计数=
|
||||
# 沿现役缓冲路径口径〔天眼开格不计积点〕。grant_ap 相位防御在 ACTIVE 挖格链内恒过)
|
||||
# 缓冲路径口径〔天眼开格不计积点〕。grant_ap 相位防御在 ACTIVE 挖格链内恒过)
|
||||
if String(evt.get("kind", "")) == "safe":
|
||||
var cs_row: Dictionary[Variant, Variant] = _attune_charge_safe_row()
|
||||
if not cs_row.is_empty():
|
||||
@@ -619,24 +616,24 @@ func _maybe_settle() -> void:
|
||||
func _settle() -> void:
|
||||
_settling = true
|
||||
var ri := round_index
|
||||
# 段序编排(迭代 27 T2——五段 Segment;段体归 game/core/settlement/,本类退为段序+
|
||||
# 行发射/开单原语单点。旧 259 行巨函数体已逐字迁五段类,git 史留痕)
|
||||
# 段序编排(五段 Segment;段体归 game/core/settlement/,本类只做段序+
|
||||
# 行发射/开单原语单点)
|
||||
_grant_seg.run(ri) # ① 授予段(案C 闸随段装载)
|
||||
_aura_seg.run(ri) # ② 增益/减益段(续存实例组合行)
|
||||
_combat_seg.run(ri) # ③ 战斗段(主角先手 → 逐敌还手 → 地图伤害账殿后〔案C〕)
|
||||
_decay_seg.run(ri) # ④ 衰减段(恰一次恒执行含末轮——先兑付后衰减)
|
||||
var verdict: String = _verdict_seg.run(ri) # ⑤ 判定段(防御性优先级 hero 先判)
|
||||
if verdict != "continue":
|
||||
return # 终局:旧早退语义逐位一致(_settling 不回置——_battle_over 已接管)
|
||||
return # 终局:_settling 不回置——_battle_over 已接管
|
||||
_settling = false
|
||||
|
||||
|
||||
# ---- 授予段 helpers(极性路由)----
|
||||
|
||||
# 开格产出乘区(内容数据驱动扩展批 T3 参数化——贪婪矿灯/矿脉直觉/赌徒的血签事件收集器注入):
|
||||
# 开格产出乘区(贪婪矿灯/矿脉直觉/赌徒的血签事件收集器注入):
|
||||
# positive=power ×(1+静态 mult% + 矿脉直觉连击加成 min(cap,combo)×10%);negative=power ×(1+cost%)。
|
||||
# 全零收集器=原快照原样返回(零扰动)。combo 读数=效果引擎 dig_combo 计数器(safe+1/mine 清零,
|
||||
# 跨轮累计——与迁移前 _feat_combo 逐位一致)。
|
||||
# 跨轮累计)。
|
||||
func _feat_yield_adjust(snap: Dictionary, positive: bool, pos_m: int, neg_m: int, combo_cap: int) -> Dictionary:
|
||||
var combo_val := _fx.counter("dig_combo") if _fx != null else 0
|
||||
if positive and pos_m <= 0 and combo_cap <= 0:
|
||||
@@ -687,8 +684,8 @@ func _hits_of(hits: Array, kind: String) -> Array:
|
||||
# 「动作 × 格型」矩阵的执行面:snap = 缓冲期冻结身份快照(cell_type = 该格装板期摇定的类型身份),
|
||||
# polarity 由动作决定(开安全格/封印命中 = positive,开危险格/标错封印 = negative),
|
||||
# 兑付行 = 奖励字典(类型, 极性)行。返回 true = percent 行实际生效(flat 行恒 false——兑付结果由行协议承载)。
|
||||
# ---- 秘宝(use_treasure 四步受理编排保留给同调主动件复用——迭代 11 第①批 T7:过渡授予路由
|
||||
# _grant_treasure 随掉落/停点渠道退役拆除;能力件注入归 LevelRunner.set_attune_charges 骑库存面)----
|
||||
# ---- 秘宝(use_treasure 四步受理编排,调用方=同调主动件;能力件注入归
|
||||
# LevelRunner.set_attune_charges 骑库存面)----
|
||||
|
||||
|
||||
# use_treasure 四步受理(registry use_orchestration 规范序——唯一调用权归本类;序即原子:
|
||||
@@ -701,26 +698,26 @@ func use_treasure(handle: int, target: Vector2i) -> Dictionary:
|
||||
return {"ok": false, "reason": "库存未接线"}
|
||||
if action_economy.phase != action_economy.PHASE_ACTIVE:
|
||||
return {"ok": false, "reason": "非挖掘期(使用窗口=Active)"}
|
||||
var board: MinesweeperRules = minesweeper # 恒等包装退役(2026-10-02 修复批——use_treasure 直引公有成员)
|
||||
# 板维度读数(迭代 22 T3 修——Rules 无 width 代理〔潜伏缺陷:旧目录期零直呼覆盖掩盖〕,读 BoardData 本体)
|
||||
var board: MinesweeperRules = minesweeper # 直引公有成员(无包装层)
|
||||
# 板维度读数(Rules 无 width 代理,读 BoardData 本体)
|
||||
var board_b: BoardData = board.current_board()
|
||||
var v: Dictionary[Variant, Variant] = treasure_inventory.validate_use(handle, target,
|
||||
Vector2(board_b.width, board_b.height),
|
||||
func(c: Vector2i): return not board.is_revealed(c))
|
||||
if not bool(v.ok):
|
||||
return v # ② 拒绝零消耗
|
||||
# treasure_used 发射+施加(内容数据驱动扩展批 T4——使用主动秘宝受理面:counter_spend 订阅=受理闸
|
||||
# (掘进蓄能积点账本),消耗成功才受理;积点不足拒绝零消耗。按 item_id 特判随本批拆除)
|
||||
# treasure_used 发射+施加(使用主动秘宝受理面:counter_spend 订阅=受理闸
|
||||
# (掘进蓄能积点账本),消耗成功才受理;积点不足拒绝零消耗。零 item_id 特判)
|
||||
var tu_hits := _fx_emit("treasure_used", {"item_id": String(v.item_id), "target": target})
|
||||
for h in tu_hits:
|
||||
if String(h.get("kind", "")) == "counter_spend":
|
||||
if not _fx.counter_spend(String(h.get("counter_id", "")), int(h.get("value", 1))):
|
||||
var vrow: Dictionary[Variant, Variant] = db.treasure_item_row(String(v.item_id))
|
||||
var cn_info: int = int(vrow.get("charge_n", 0)) # 迭代 22 T3——读受理行自身(旧 dig_charge 硬编码随行退役)
|
||||
var cn_info: int = int(vrow.get("charge_n", 0)) # 读受理行自身
|
||||
return {"ok": false, "reason": "%s:安全格积点未达(%d/%d)" % [String(vrow.get("display_name", "")), _attune_safe_seen % cn_info if cn_info > 0 else 0, cn_info]}
|
||||
var kinds: Array[Variant] = v.kinds
|
||||
if kinds.has("mine_reveal") or kinds.has("reveal_tiles"):
|
||||
# 真视写入(迭代 22 T3——reveal_tiles 新真视 kind 与 mine_reveal 同叠加层通道:
|
||||
# 真视写入(reveal_tiles 这类真视 kind 与 mine_reveal 同叠加层通道:
|
||||
# 危险格显示=信息呈现零回写,棋盘事实不可变契约 3)
|
||||
treasure_inventory.apply_overlay("mine_reveal", v.resolved)
|
||||
if kinds.has("reward_preview"):
|
||||
@@ -729,7 +726,7 @@ func use_treasure(handle: int, target: Vector2i) -> Dictionary:
|
||||
if not action_economy.grant_ap(int(v.magnitude)):
|
||||
return {"ok": false, "reason": "行动点加量被拒(相位防御)"}
|
||||
if kinds.has("safe_dig"):
|
||||
# 天眼安全格直开(迭代 22 秘宝批 T3——C5 裁全额兑付:fx_reveal 通道复用=免 AP 零相位、
|
||||
# 天眼安全格直开(C5 全额兑付:fx_reveal 通道复用=免 AP 零相位、
|
||||
# 连锁与格面资源与普通挖格同链;危险格仅真视显示不移除=契约 3;有封印格跳过——封印=玩家承诺不代开)
|
||||
var opened_any := false
|
||||
for c_v in v.resolved:
|
||||
@@ -741,51 +738,51 @@ func use_treasure(handle: int, target: Vector2i) -> Dictionary:
|
||||
if not opened_any:
|
||||
return {"ok": false, "reason": "天眼:窗口内安全格均已开(无可开格)"}
|
||||
if kinds.has("cell_removal"):
|
||||
# 爆破工兵包兑付(迭代 13 T8/D6——中立移除危险格:未揭示/无封印/是雷过滤归排雷玩法;
|
||||
# P1 甲邻接数字冻结零重算、清图分母零涉(危险格不入分母);空集拒绝零消耗——
|
||||
# 爆破工兵包兑付(中立移除危险格:未揭示/无封印/是雷过滤归排雷玩法;
|
||||
# 邻接数字冻结零重算、清图分母零涉(危险格不入分母);空集拒绝零消耗——
|
||||
# consume 之前 return(拒绝面注记扩「③内零消耗分支」),拒绝 reason 可呈现)
|
||||
var blasted: Array[Variant] = board.remove_mines_in(v.resolved)
|
||||
if blasted.is_empty():
|
||||
return {"ok": false, "reason": "爆破工兵包:3×3 区域内无可移除危险格(须未揭示且未封印)"}
|
||||
var rec: Dictionary[Variant, Variant] = treasure_inventory.consume(handle)
|
||||
# 同调主动件充能扣减(迭代 11 第①批 T6——共享账本直改:adventure/level/settlement 同一字典引用)
|
||||
# 同调主动件充能扣减(共享账本直改:adventure/level/settlement 同一字典引用)
|
||||
if not rec.is_empty() and String(rec.get("source_class", "")) == "attunement":
|
||||
_ledger.attune_charge_consume(String(rec.get("item_id", "")))
|
||||
# 真视类工具使用计数(迭代 22 秘宝批 T3——信标网络解锁行为条件:受理成功恰计一次;
|
||||
# 真视类工具使用计数(信标网络解锁行为条件:受理成功恰计一次;
|
||||
# 共享 dict 引用下传〔attune_charges 同款〕,冒险收束聚合入档 save.add_vision_used)
|
||||
if kinds.has("reveal_tiles") or kinds.has("safe_dig") or kinds.has("mine_reveal"):
|
||||
if not _ledger.vision_counter.is_empty():
|
||||
_ledger.vision_count_add(1) # 迭代 22 秘宝批 T3——受理成功恰计一次(缺省空账=调试不计)
|
||||
_ledger.vision_count_add(1) # 受理成功恰计一次(缺省空账=调试不计)
|
||||
return {"ok": true, "item_id": v.item_id, "resolved": v.resolved, "record": rec}
|
||||
|
||||
|
||||
# aux 解锁门控转发(迭代 7 T5——LevelRunner 注入;E1/E3 封装:外部经公有面,禁直读 _reward_economy)
|
||||
# aux 解锁门控转发(LevelRunner 注入;E1/E3 封装:外部经公有面,禁直读 _reward_economy)
|
||||
func set_treasure_unlocked(ids: Dictionary) -> void:
|
||||
_reward_economy.set_treasure_unlocked(ids)
|
||||
|
||||
|
||||
func _grant_routed(snap: Dictionary, polarity: String, source_class: String, cell: Vector2i, ri: int, highlight: String = "", label_suffix: String = "", halve_pct: int = 0) -> bool:
|
||||
# 迭代 11 第①批 T7——aux 授予分支随掉落/停点渠道退役拆除(reward 词表第十型 treasure_item 保留但零投放:
|
||||
# 无 aux 行入池、CURIO_KINDS 词表已除名);剩余类型走极性字典路由。
|
||||
# treasure_item 型零投放(无 aux 行入池、CURIO_KINDS 词表无此型),本口不处理;
|
||||
# 剩余类型走极性字典路由。
|
||||
var row: Dictionary[Variant, Variant] = _reward_economy.polarity_row(String(snap.get("cell_type", "")), polarity)
|
||||
if row.is_empty():
|
||||
_fatal("极性字典缺行: %s %s" % [String(snap.get("cell_type", "")), polarity])
|
||||
return false
|
||||
if polarity == "positive":
|
||||
row["power"] = int(snap.get("power", int(row.power))) # 区间行快照 power(身份批 roll 值)覆盖表值——授予期零随机口径(E14)
|
||||
# 标中兑付溢价(迭代14②判式换代批 T4——条款3 条件二封印溢价形态 a,2026-10-04 用户裁 P3):
|
||||
# 标中兑付溢价(条款3 条件二封印溢价形态 a):
|
||||
# δ 读 settlement.seal_hit_premium(percent 域 125=×1.25 provisional);100=无溢价直通零扰动。
|
||||
# 兑付公式一处=本块:只乘封印命中正向(flat power 乘后入单据/治疗/排队;percent 幅度经
|
||||
# premium_pct 贯穿 _apply_percent_row 应用与呈现同乘)——标空/挖格/清图不乘,雷安同池冻结不动。
|
||||
# 取整=四舍五入(与伤害管线 round_direction=floor 分域——registry clause3_inequality 随批登记;
|
||||
# 取整=四舍五入(与伤害管线 round_direction=floor 分域——registry clause3_inequality 登记;
|
||||
# 1 点量纲行 ac_up/action 取整后不变=整数粒度地板)。
|
||||
if source_class == "seal_hit" and int(db.settlement.seal_hit_premium) != 100:
|
||||
var prem_pct: int = int(db.settlement.seal_hit_premium)
|
||||
row = row.duplicate()
|
||||
row["power"] = int(round(int(row.power) * prem_pct / 100.0))
|
||||
row["premium_pct"] = prem_pct
|
||||
# 惊巢反制(迭代 11 同调批 T5 → 内容数据驱动扩展批 T4:按 id 特判拆除——减半系数=mine_hit
|
||||
# damage_mult 订阅命中(行 value 50=减 50%,取整 ceil 与迁移前逐位一致);反弹单=mine_hit
|
||||
# 惊巢反制(减半系数=mine_hit
|
||||
# damage_mult 订阅命中(行 value 50=减 50%,取整 ceil);反弹单=mine_hit
|
||||
# open_ticket channel reflect 订阅施加(编排器 mine_hit 施加段);duplicate 隔离——减半不回写极性字典)
|
||||
if halve_pct > 0 and polarity == "negative" and source_class == "mine_hit":
|
||||
row = row.duplicate()
|
||||
@@ -804,7 +801,7 @@ func _dispatch_row(row: Dictionary, source_class: String, cell: Vector2i, ri: in
|
||||
return false
|
||||
|
||||
|
||||
# percent 行兑付:GP 型行走战斗内缓冲记账(GP+商店房实施批);其余 apply_effect 入容器;
|
||||
# percent 行兑付:GP 型行走战斗内缓冲记账;其余 apply_effect 入容器;
|
||||
# 满层拒收 = "奖励未生效"行(零静默)
|
||||
func _apply_percent_row(row: Dictionary, source_class: String, cell: Vector2i, ri: int, highlight: String = "", label_suffix: String = "") -> bool:
|
||||
var eff := String(row.effect_id)
|
||||
@@ -827,7 +824,7 @@ func _apply_percent_row(row: Dictionary, source_class: String, cell: Vector2i, r
|
||||
return false
|
||||
|
||||
|
||||
# GP 型行兑付(GP+商店房实施批 2026-09-21):战斗内缓冲 escrow 记账——不入钱包不入增益容器。
|
||||
# GP 型行兑付:战斗内缓冲 escrow 记账——不入钱包不入增益容器。
|
||||
# 正向(gp_gain)= 挖开GP安全格/封印命中;负向(gp_loss)= 挖危险格/标错封印。兑付行经既有 effect 行
|
||||
# 通道外发(escrow 随行——归因可读),战斗结束随 gp_dropped 一次上抛后即弃。
|
||||
func _apply_gp_row(row: Dictionary, source_class: String, cell: Vector2i, ri: int, highlight: String = "", label_suffix: String = "") -> bool:
|
||||
@@ -838,7 +835,7 @@ func _apply_gp_row(row: Dictionary, source_class: String, cell: Vector2i, ri: in
|
||||
return true
|
||||
|
||||
|
||||
# flat 行兑付(负向恒即时,无库存语义):伤害型分支全部改开单据(伤害管线单据化批 2026-09-24——
|
||||
# flat 行兑付(负向恒即时,无库存语义):伤害型分支走开单据(
|
||||
# 管线/下限/目标解析归 伤害控制器,本函数只保留非伤害分支与单据装配):
|
||||
# action = 行动点排队(非伤害);blast 正向 = 群伤单据(无类型恒定表值打全体存活敌;负向 = 警觉 percent 行走 _apply_percent_row);
|
||||
# heal 正向 = 主角回复 clamp(满血 → 奖励未生效行);heal 负向 = 主角直伤单据(不经增益状态);
|
||||
@@ -859,7 +856,7 @@ func _apply_flat_row(row: Dictionary, source_class: String, cell: Vector2i, ri:
|
||||
DamageController.TARGET_ALL_ALIVE, ri, cell, "", 0, highlight, {"batch_key": String(row.reward_id)})
|
||||
return _outcome_of(_submit(t, sync))
|
||||
if ctype == "pierce" or ctype == "magic":
|
||||
# 伤害道具批(2026-09-18)语义不变:正向 = 单目标克制直伤(必中全额不掷点——棋盘来源伤害既有口径);
|
||||
# 正向 = 单目标克制直伤(必中全额不掷点——棋盘来源伤害既有口径);
|
||||
# 负向 = 主角直伤(复用反噬通道)。
|
||||
if String(row.get("polarity", "positive")) == "negative":
|
||||
return _submit_flat_hero(source_class, display, power, cell, ri, highlight, sync)
|
||||
@@ -927,7 +924,7 @@ func _grant_clear_reward(ri: int) -> void:
|
||||
|
||||
# 清图奖励 = 板级独立对象(不属于任何一格):兑付形态按其 cell_type 的正向字典形态走,
|
||||
# 行值全部取快照(身份批 roll 值)。返回实际有效兑付。
|
||||
# 同步开单(sync)——完美第二份与高光份数的流程依赖需要同步读数(开单权归流程 用户裁决③);
|
||||
# 同步开单(sync)——完美第二份与高光份数的流程依赖需要同步读数(开单权归流程);
|
||||
# 同步路径内先按序结清段内排队单据(B 模式保序),随后本批无视结账政策当场兑现。
|
||||
func _grant_clear_identity(snap: Dictionary, ri: int, suffix: String) -> bool:
|
||||
var row := {
|
||||
@@ -945,12 +942,12 @@ func _grant_clear_identity(snap: Dictionary, ri: int, suffix: String) -> bool:
|
||||
return _apply_flat_row(row, "clear_reward", Vector2i(-1, -1), ri, "clear_reward_h2", suffix, true) == "redeemed"
|
||||
|
||||
|
||||
# ---- 开轮摇号(敌人技能池批——共识点 B:技能选择前置到轮次开始,数值计算留结算段)----
|
||||
# ---- 开轮摇号(共识点 B:技能选择前置到轮次开始,数值计算留结算段)----
|
||||
# 每实例:保底判定(连续执行普攻 ≥ M → 必出特殊技、特殊技内按权重、不受冷却限制)→ 排除冷却中技能
|
||||
# (中签率 = 权重 ÷ 可摇权重合计——份额归一)→ 按行序逐敌从统一流顺序取号摇定本拍动作。
|
||||
# 随机消费只在换算层(本方法 + SkillLottery);单位容器收确定值(零随机纪律——armor_roll 先例同构)。
|
||||
# 并流批(2026-09-27 统一流裁决):behavior_roll 子流派生向量(spawn_index×STRIDE+序)退役——
|
||||
# 「单敌轨迹独立复放」前提随之移除(位置交错,单点定位由事件日志承载)。摇定后、兑现前死亡 → 本拍技能作废。
|
||||
# 统一流:behavior_roll 无子流派生向量(位置交错,单点定位由事件日志承载)。
|
||||
# 摇定后、兑现前死亡 → 本拍技能作废。
|
||||
func _roll_round_actions() -> void:
|
||||
var pity_m: int = int(db.skill.pity_threshold)
|
||||
for u in _enemy_system.query_alive("enemy"):
|
||||
@@ -970,7 +967,7 @@ func _roll_round_actions() -> void:
|
||||
if String((pe as Dictionary).get("skill_id", "")) == sid and (pe as Dictionary).has("unlock_hp_pct"):
|
||||
pct_v = int((pe as Dictionary).unlock_hp_pct)
|
||||
break
|
||||
# 迭代 20 T1——target_attack_bonus 施加面(处刑台:敌血量阈值沿挂单位字典标记,
|
||||
# target_attack_bonus 施加面(处刑台:敌血量阈值沿挂单位字典标记,
|
||||
# 主角攻击面 _resolve_hero_attack 通用消费——fx_atk_bonus 键加算进 base_value)
|
||||
var eht_hits := _fx_emit("enemy_hp_threshold", {"unit": String(u.enemy_id), "pct": pct_v,
|
||||
"is_boss": String(encounter.get("node_type", "")) == "boss"})
|
||||
@@ -988,15 +985,14 @@ func _roll_round_actions() -> void:
|
||||
|
||||
|
||||
# ---- 战斗段 ----
|
||||
# 伤害管线单据化批(2026-09-24):本段只开单——主角先手单 + 敌方伤害技单;掷点/折扣/暴击/克制/
|
||||
# 池乘区/下限/交付/commit 全部收口 伤害控制器(A 模式提交即结=与迁移前逐位一致;B 模式段尾 flush)。
|
||||
# 开单守卫归流程(用户裁决③——流程决定给谁开单):主角存活+目标存在才开先手单;hero hp=0 立即
|
||||
# 本段只开单——主角先手单 + 敌方伤害技单;掷点/折扣/暴击/克制/
|
||||
# 池乘区/下限/交付/commit 全部收口 伤害控制器(A 模式提交即结;B 模式段尾 flush)。
|
||||
# 开单守卫归流程(流程决定给谁开单):主角存活+目标存在才开先手单;hero hp=0 立即
|
||||
# 停开(B 模式下若 flush 中主角倒下,由控制器死亡事件终止余单——两模式同一结果)。
|
||||
|
||||
# ---- 算术读数(伤害管线单据化批 2026-09-24 迁移注记)----
|
||||
# 掷点/折扣/克制点数/池乘区/攻击行文本已全部迁入 伤害控制器(damage_controller.gd 管线单点);
|
||||
# 本类仅保留一处薄委托——_defender_ac(快照面消费)。_clamped_pool 委托壳已随 2026-10-02 修复批删除
|
||||
# (冒烟白盒断言消费面改直引 settlement._damage.clamped_pool——并行卡同批改 smoke_all)。
|
||||
# ---- 算术读数 ----
|
||||
# 掷点/折扣/克制点数/池乘区/攻击行文本 归 伤害控制器(damage_controller.gd 管线单点);
|
||||
# 本类只保留一处薄委托——_defender_ac(快照面消费)。
|
||||
|
||||
# 防守方护甲等级读数——实现归 伤害控制器 defender_ac(本委托供结算快照/呈现快照)
|
||||
func _defender_ac(unit: Dictionary) -> int:
|
||||
@@ -1052,7 +1048,7 @@ func _emit_settlement_snapshot(verdict: String) -> void:
|
||||
"round": round_index,
|
||||
"verdict": verdict,
|
||||
"unit_count": enemies.size(),
|
||||
"spawn_count": _enemy_system.all_enemies().size(), # 本战斗实例化总数(含死亡残位)——unit_count=0 时区分「首轮全灭」vs「空场」(playtest-2026-09-12 报告 Bug#1 误读教训)
|
||||
"spawn_count": _enemy_system.all_enemies().size(), # 本战斗实例化总数(含死亡残位)——unit_count=0 时区分「首轮全灭」vs「空场」
|
||||
"hero": {"hp": int(_enemy_system.hero_unit().get("hp", 0)), "max_hp": int(_enemy_system.hero_unit().get("max_hp", 0)), "atk": int(_enemy_system.hero_unit().get("atk", 0)), "armor_class": _defender_ac(_enemy_system.hero_unit())},
|
||||
"economy": {"phase": String(action_economy.phase), "ap": int(action_economy.ap), "rap": int(action_economy.rap), "refresh_used": _refresh_used_this_round},
|
||||
"board": board_stats,
|
||||
@@ -1095,7 +1091,7 @@ func can_refresh() -> bool:
|
||||
|
||||
|
||||
# 玩家经 HUD框架发起、战斗结算 代理调用(禁止 RAP 满自动刷)。
|
||||
# 挖掘期唯一通道(2026-09-12 方案 A 拍板 + 2026-09-15 等待期通道整体移除):Active 相位立即换图
|
||||
# 挖掘期唯一通道:Active 相位立即换图
|
||||
# 继续挖(RAP -8;AP 保留、相位不动、不触发结算);非挖掘期请求一律拒绝——非清图轮恒同图。
|
||||
func player_request_refresh() -> bool:
|
||||
if not can_refresh():
|
||||
@@ -1110,14 +1106,14 @@ func player_request_refresh() -> bool:
|
||||
|
||||
func _battle_over(result: String) -> void:
|
||||
battle_result = result
|
||||
# battle_won 发射+施加(迭代 20 T1——战地包扎 heal 订阅面;败北不发射。heal=英雄单位回血
|
||||
# battle_won 发射+施加(战地包扎 heal 订阅面;败北不发射。heal=英雄单位回血
|
||||
# 〔封顶单位上限〕,战后生命经 hero_hp_left 回写冒险账本——时点在捕获前=回血计入带入下一层)
|
||||
if result == "victory":
|
||||
for h in _fx_emit("battle_won", {"round": round_index}):
|
||||
if String(h.get("kind", "")) == "heal":
|
||||
var hu_w: Dictionary[Variant, Variant] = _enemy_system.hero_unit()
|
||||
hu_w["hp"] = mini(int(hu_w.get("hp", 0)) + int(h.get("value", 0)), maxi(1, int(hu_w.get("max_hp", 1))))
|
||||
# bonus_delta 战末汇入(迭代 20 T1——血税双向账:本战累积并入冒险加算区共享引用后清零)+
|
||||
# bonus_delta 战末汇入(血税双向账:本战累积并入冒险加算区共享引用后清零)+
|
||||
# adventure 域计数器导出回写(T6——共享引用覆写,attune_charges 先例)
|
||||
if not _ledger.bonus.is_empty():
|
||||
for axis in _fx_bonus_delta.keys():
|
||||
@@ -1127,19 +1123,19 @@ func _battle_over(result: String) -> void:
|
||||
_ledger.counters_merge_export(_fx.counters_export())
|
||||
# 映射随战斗结束退役(地图战斗资源 义务④——下一场战斗 = 新板新 scope 批,roll_board 相位守卫的前提)
|
||||
var hero_hp_left := int(_enemy_system.hero_unit().get("hp", 0)) # despawn 前捕获(信号载荷供呈现层"HP 带入下一层")
|
||||
var gp_dropped := _roll_gp_drop() # 战斗掉落(GP+商店房实施批——escrow 随此值上抛即弃)
|
||||
var gp_dropped := _roll_gp_drop() # 战斗掉落(escrow 随此值上抛即弃)
|
||||
_reward_economy.retire_mapping()
|
||||
_enemy_system.despawn_all()
|
||||
_status_effects.notify_battle_end()
|
||||
if treasure_inventory != null:
|
||||
treasure_inventory.retire_overlays() # 迭代 7 T3——叠加层随战斗结束清空(库存不动——关卡域跨战斗)
|
||||
action_economy.notify_combat_end() # → Idle,RAP 清零(2026-10-02 修复批:原被 tab 事故并进上一行行尾注释从不执行——每战新建实例掩盖了症状;P0-3)
|
||||
treasure_inventory.retire_overlays() # 叠加层随战斗结束清空(库存不动——关卡域跨战斗)
|
||||
action_economy.notify_combat_end() # → Idle,RAP 清零
|
||||
db.set_reload_gate(false)
|
||||
_reset_round_facts()
|
||||
battle_finished.emit({"result": result, "hero_hp": hero_hp_left, "gp_dropped": gp_dropped})
|
||||
|
||||
|
||||
# 战斗掉落掷定(GP+商店房实施批 2026-09-21):tier = Boss 节点 ? "boss" : "normal"(elite 留分支位);
|
||||
# 战斗掉落掷定:tier = Boss 节点 ? "boss" : "normal"(elite 留分支位);
|
||||
# gp_dropped = gp_drop 封闭流掷定(tier 行的 roll_min..roll_max 均匀整数——每战斗末恰一 scope)
|
||||
# + level_gp_bonus(level_index) + escrow。掷定行缺位 = 数据面缺行(fail 报错按 0 兑付——不哑掉)。
|
||||
func _roll_gp_drop() -> int:
|
||||
@@ -1152,25 +1148,25 @@ func _roll_gp_drop() -> int:
|
||||
var stream: RandomNumberGenerator = rng.open_stream("gp_drop")
|
||||
roll = stream.randi_range(int(drops.get("roll_min", 0)), int(drops.get("roll_max", 0)))
|
||||
var drop := roll + db.shop_level_gp_bonus(int(encounter.get("level_index", 0)))
|
||||
# 财富之道·聚(迭代 21 T3——战斗掉落 GP 乘算·入账前:只乘战斗掉落两成分〔掷定+关卡加成〕,
|
||||
# 店内 escrow 不乘〔范围=仅战斗掉落渠道,卡片§11②裁决〕;本函数返回值=结算框「GP 增量」行
|
||||
# 显示源,落在入账前=显示即实得;乘数 provisional 150%,值归 M4 数值批)
|
||||
# 财富之道·聚(战斗掉落 GP 乘算·入账前:只乘战斗掉落两成分〔掷定+关卡加成〕,
|
||||
# 店内 escrow 不乘〔范围=仅战斗掉落渠道〕;本函数返回值=结算框「GP 增量」行
|
||||
# 显示源,落在入账前=显示即实得;乘数 provisional 150%)
|
||||
if (encounter.get("tree_rules", []) as Array).has("economy_hoard"):
|
||||
drop = int(round(float(drop) * 1.5))
|
||||
return drop + escrow
|
||||
|
||||
|
||||
# ---- 呈现状态面(E1 封装收口 2026-09-13):呈现层 / headless 自动玩家的唯一读取出口——
|
||||
# ---- 呈现状态面(E1 封装收口):呈现层 / headless 自动玩家的唯一读取出口——
|
||||
# 协作对象(_enemy_system/_status_effects/_reward_economy)私有化后,跨层读取一律经本快照或下列命名面(diagnostic_snapshot 同款模式,
|
||||
# 禁再链式穿透 settlement.enemy_system.hero 一类路径)。载荷 schema 成文(code-conventions 2.1):
|
||||
# round / battle_result / phase / ap / rap / can_refresh / hero{hp,max_hp,atk,alive,armor_class} /
|
||||
# enemies[{order,display_name,hp,max_hp,enemy_id,armor_type,armor_class,pending_skill,pending_type,pending_name,effects[],alive}] /
|
||||
# (敌人技能池批批 2 定稿:beat/ring_size/ring_index 拆除 → pending_* 三字段 + 敌方增益 effects 投影;池与权重不公开;
|
||||
# 八型批 2026-09-20:ambush_layers 拆除——伏击退役,警觉并入 effects 投影)hero_effects[](增益状态 query_active 投影)
|
||||
# (字段面=pending_* 三字段 + 敌方增益 effects 投影;池与权重不公开;警觉走 effects 投影)
|
||||
# hero_effects[](增益状态 query_active 投影)
|
||||
func presentation_snapshot() -> Dictionary:
|
||||
var enemies: Array[Dictionary] = []
|
||||
var all: Array[Variant] = _enemy_system.all_enemies()
|
||||
# 敌方增益投影(side-wide 状态——蓄力/硬化/削弱/暴走 percent 行 + 警觉 ac 行,八型批 2026-09-20)
|
||||
# 敌方增益投影(side-wide 状态——蓄力/硬化/削弱/暴走 percent 行 + 警觉 ac 行)
|
||||
var enemy_effects: Array[Dictionary] = []
|
||||
for eff in _status_effects.query_active("enemy"):
|
||||
var erow: Dictionary[Variant, Variant] = db.status_effect_row(eff.effect_id)
|
||||
@@ -1206,11 +1202,11 @@ func presentation_snapshot() -> Dictionary:
|
||||
"phase": String(action_economy.phase),
|
||||
"ap": int(action_economy.ap), "rap": int(action_economy.rap),
|
||||
"can_refresh": can_refresh(),
|
||||
"refresh_used": _refresh_used_this_round, # 迭代 27 T3——P1-2 刷新双账收口:受理计数入呈现成文面(HUD 余量单源读此处)
|
||||
"refresh_used": _refresh_used_this_round, # 受理计数入呈现成文面(HUD 余量单源读此处)
|
||||
"hero": hero,
|
||||
"enemies": enemies,
|
||||
"hero_effects": _status_effects.query_active("hero").duplicate(true), # 值拷贝出户(code-review 2026-09-29 P2-3——快照消费方直改不渗入状态容器)
|
||||
# 板级变异投影(全局作用域变异首发批 2026-10-04——呈现横幅/常驻角标唯一读取面;
|
||||
"hero_effects": _status_effects.query_active("hero").duplicate(true), # 值拷贝出户(快照消费方直改不渗入状态容器)
|
||||
# 板级变异投影(呈现横幅/常驻角标唯一读取面;
|
||||
# 空数组 = 无全局型合法态:普通图与全局池空回落图恒空)
|
||||
"board_variants": (minesweeper.current_board().board_variant_ids().duplicate()
|
||||
if minesweeper.current_board() != null else ([] as Array[String])),
|
||||
@@ -1228,7 +1224,7 @@ func query_reward_identity(cell: Vector2i):
|
||||
|
||||
|
||||
# 诊断观测面(组合根诊断转储 / 行为探针消费):私有编排状态的成文只读出口——
|
||||
# 消费方禁止再直读本类下划线字段(code-review 2026-09-12 封装收口批)
|
||||
# 消费方禁止再直读本类下划线字段(封装收口)
|
||||
func diagnostic_snapshot() -> Dictionary:
|
||||
return {
|
||||
"battle_result": battle_result,
|
||||
@@ -1293,7 +1289,7 @@ func _fatal(msg: String) -> void:
|
||||
assert(false, msg)
|
||||
|
||||
|
||||
## 完美排雷谓词(迭代 31 卡② T4——两账相减:有效误标=误标总数−豁免数)
|
||||
## 完美排雷谓词(两账相减:有效误标=误标总数−豁免数)
|
||||
## 契约 2:_perfect_hit_false 保持纯净,豁免只累加 _perfect_forgiven,禁回退式改写与拦截式吞事件。
|
||||
func perfect_sweep_earned() -> bool:
|
||||
return _perfect_hit_false - _perfect_forgiven <= 0 and _perfect_hit_true >= 1
|
||||
@@ -4,7 +4,7 @@
|
||||
class_name BoardData
|
||||
extends RefCounted
|
||||
|
||||
# 出生 3×3 区格数(2026-10-02 修复批 T2 魔法数结构化——W×H−9 布雷域基的双落点收口单点:
|
||||
# 出生 3×3 区格数(W×H−9 布雷域基的双落点收口单点:
|
||||
# 棋盘生成 _attempt/_finalize 与 BoardGenTable 布雷数下限校验共用;与 spawn_cells() 的 3×3 几何同源)
|
||||
const SPAWN_AREA_CELLS := 9
|
||||
|
||||
@@ -14,7 +14,7 @@ var spawn_center := Vector2i.ZERO
|
||||
var mines := {} # Vector2i → true
|
||||
var digits := {} # Vector2i → int(安全格恒有值;数字恒真 = 8 邻雷数)
|
||||
var variants := {} # Vector2i → Array[String](生成期快照,抗表重载)
|
||||
var board_variants: Array[String] = [] # 板级变异 id 集(scope=global 整图作用域——全局作用域变异批 2026-10-04:
|
||||
var board_variants: Array[String] = [] # 板级变异 id 集(scope=global 整图作用域——
|
||||
# 战斗编排装板决议写入,随图生灭同 variants;普通图恒空)
|
||||
var board_type := "" # 图类型标签(词表 v1.0:dungeon/underdark;玩家侧显示名「地图类型」)
|
||||
var level_index: int = 1
|
||||
@@ -28,7 +28,7 @@ func in_bounds(c: Vector2i) -> bool:
|
||||
return c.x >= 0 and c.y >= 0 and c.x < width and c.y < height
|
||||
|
||||
|
||||
# 行主序排序键(2026-10-02 修复批——排雷玩法 封印判定批与 地图战斗资源 资格规范化两处手抄收口单点):
|
||||
# 行主序排序键(排雷玩法 封印判定批与 地图战斗资源 资格规范化两处手抄收口单点):
|
||||
# y×1000+x 字典序 = 行主序;板宽域 [8,64] 远小于 1000,x 不跨行、键值不碰撞。
|
||||
static func row_major_key(c: Vector2i) -> int:
|
||||
return c.y * 1000 + c.x
|
||||
@@ -55,7 +55,7 @@ func board_variant_ids() -> Array[String]:
|
||||
return board_variants
|
||||
|
||||
|
||||
# 中和格变异(迭代 12 T6 净化工兵——伤害型地形格变普通格;🔴 R4 甲仅限伤害型的过滤在调用方)
|
||||
# 中和格变异(伤害型地形格变普通格;🔴 仅限伤害型的过滤在调用方)
|
||||
func neighbors8(c: Vector2i) -> Array:
|
||||
var out: Array[Vector2i] = []
|
||||
for dy in range(-1, 2):
|
||||
@@ -103,7 +103,7 @@ func compute_r0() -> Array:
|
||||
var queue: Array[Variant] = b3.duplicate()
|
||||
while not queue.is_empty():
|
||||
var c: Vector2i = queue.pop_front()
|
||||
if is_safe(c) and digit_of(c) == 0: # 变异死分支退役(2026-09-18 用户拍板删——与 排雷玩法 展开实现口径归一;变异定稿批按正式语义重接)
|
||||
if is_safe(c) and digit_of(c) == 0: # 与 排雷玩法 展开实现同口径(安全且数字 0 才扩散)
|
||||
for n in neighbors8(c):
|
||||
if is_safe(n) and not r0.has(n):
|
||||
r0[n] = true
|
||||
|
||||
@@ -11,7 +11,7 @@ const METRIC_VERSION := 1
|
||||
# ==================== 生成入口 ====================
|
||||
|
||||
# rng_stream = 一次 open_stream("board_gen") 实例(一张图的全部取数;重试共用——attempt 边界非 scope 事件)。
|
||||
# db 参数不标具体类型原因:冒烟工具注入 FakeDB 桩(experiment_density.gd 等)——typed 形参会拒收桩表(2026-10-02 注)
|
||||
# db 参数不标具体类型原因:冒烟工具注入 FakeDB 桩(experiment_density.gd 等)——typed 形参会拒收桩表
|
||||
func generate(L: int, D: int, rng_stream: RandomNumberGenerator, db) -> BoardData: # type-hint: ignore(测量面 _BoardRowStub 冒烟夹具鸭子型注入——typed 参拒 stub)
|
||||
var row: Dictionary[Variant, Variant] = db.resolve_board_gen(L, D)
|
||||
if row.is_empty():
|
||||
@@ -86,7 +86,7 @@ func _attempt(params: Dictionary, rng_stream: RandomNumberGenerator, db) -> Boar
|
||||
b.level_index = params.L
|
||||
b.node_depth = params.D
|
||||
# ② 布雷:N = round(d×(W×H−9)) 先定数;域 = 全格 − 出生 3×3;均匀布雷后"先布后删"
|
||||
# (−9 = 出生区格数,常量单点 BoardData.SPAWN_AREA_CELLS——2026-10-02 修复批)
|
||||
# (−9 = 出生区格数,常量单点 BoardData.SPAWN_AREA_CELLS)
|
||||
var spawn := {}
|
||||
for c in b.spawn_cells():
|
||||
spawn[c] = true
|
||||
|
||||
+42
-42
@@ -19,13 +19,13 @@ const TABLE_MANIFEST := {
|
||||
"settlement": "settlement.tres",
|
||||
"level": "level.tres",
|
||||
"difficulty": "difficulty.tres",
|
||||
"shop": "shop.tres", # GP+商店房实施批 2026-09-21(表与 schema 落籍归并行数据面——本批只接线加载与消费)
|
||||
"meta": "meta.tres", # 元进度实现批 2026-09-21(MetaTable 四行族——schema 与表同批落籍)
|
||||
"meta_tree": "meta_tree.tres", # 迭代 12 T8 建域、迭代 21 T1 换代 12 节点(MetaTreeTable 定义表 Rule 4 豁免——天赋树.md v0.8 结构事实源)
|
||||
"treasure_items": "treasure_items.tres", # 迭代 7 T1 秘宝内容批(TreasureItemTable 六行——定义表 Rule 4 豁免;罗盘 retired/失义件 deferred 标记见 schema 头注)
|
||||
"adventure": "adventure.tres", # 冒险结构实现批 2026-09-21(AdventureTable 建表——大奖励与钥石行值单点)
|
||||
"feats": "feats.tres", # 迭代 12 T1 双层 BD 规则改写型特性基建(FeatTable 定义表 Rule 4 豁免——TreasureItemTable 先例;空表合法态=主时间线零扰动)
|
||||
"forge": "forge.tres", # 迭代 23 T1 锻造工坊实装批(ForgeTable——两根类型轴默认值+六路线行族;攻防所有权切分唯一数据面)
|
||||
"shop": "shop.tres", # ShopTable(表与 schema 落籍归数据面——本处只接线加载与消费)
|
||||
"meta": "meta.tres", # MetaTable 四行族(schema 与表同源落籍)
|
||||
"meta_tree": "meta_tree.tres", # MetaTreeTable 12 节点(定义表 Rule 4 豁免——天赋树.md v0.8 结构事实源)
|
||||
"treasure_items": "treasure_items.tres", # TreasureItemTable 六行(定义表 Rule 4 豁免;罗盘 retired/失义件 deferred 标记见 schema 头注)
|
||||
"adventure": "adventure.tres", # AdventureTable(大奖励与钥石行值单点)
|
||||
"feats": "feats.tres", # FeatTable 规则改写型特性(定义表 Rule 4 豁免——TreasureItemTable 先例;空表合法态=主时间线零扰动)
|
||||
"forge": "forge.tres", # ForgeTable——两根类型轴默认值+六路线行族;攻防所有权切分唯一数据面
|
||||
}
|
||||
|
||||
var action_economy: ActionEconomyTable
|
||||
@@ -39,20 +39,20 @@ var reward: RewardTable
|
||||
var settlement: SettlementTable
|
||||
var level: LevelTable
|
||||
var difficulty: DifficultyTable
|
||||
# GP+商店房实施批(2026-09-21):ShopTable 类型化持用(schema class_name 已落籍——code-review 2026-09-29 P2-4 收口;
|
||||
# ShopTable 类型化持用(schema class_name 已落籍;
|
||||
# 消费面一律走下方 shop_* 访问器,禁直读行集)
|
||||
var shop: ShopTable = null
|
||||
# 元进度实现批(2026-09-21):MetaTable 类型化持用(schema 与表同批落籍,无 shop 先例的落籍间隙问题)
|
||||
# MetaTable 类型化持用(schema 与表同源落籍)
|
||||
var meta: MetaTable
|
||||
var meta_tree: MetaTreeTable # 迭代 12 T8 类型化持用(消费面走 tree_node 访问器)
|
||||
var treasure_items: TreasureItemTable # 迭代 7 T1 类型化持用(消费面走 treasure_* 访问器)
|
||||
var meta_tree: MetaTreeTable # 类型化持用(消费面走 tree_node 访问器)
|
||||
var treasure_items: TreasureItemTable # 类型化持用(消费面走 treasure_* 访问器)
|
||||
var adventure: AdventureTable
|
||||
var feats: FeatTable # 迭代 12 T1 类型化持用(消费面走 feat_* 访问器)
|
||||
var forge: ForgeTable # 迭代 23 T1 锻造工坊配置(默认两轴+六路线行族——消费面走 forge_* 访问器)
|
||||
var feats: FeatTable # 类型化持用(消费面走 feat_* 访问器)
|
||||
var forge: ForgeTable # 锻造工坊配置(默认两轴+六路线行族——消费面走 forge_* 访问器)
|
||||
|
||||
var is_ready := false
|
||||
var _diagnostics: Array[Dictionary] = [] # {severity, category, message}
|
||||
var _resolver: ConfigDbResolver # R1 难度行解析协作类(2026-10-02 修复批 T5 拆分——门面公有签名零变化)
|
||||
var _resolver: ConfigDbResolver # R1 难度行解析协作类(门面公有签名不变)
|
||||
var _validator := ConfigDbCrossValidator.new() # 跨表校验协作类(无状态——声明期即建)
|
||||
var _reload_gate := false # 数据配置 EC8——战斗结算 唯一调用方
|
||||
|
||||
@@ -70,7 +70,7 @@ func push_diag(severity: String, category: String, message: String) -> void:
|
||||
push_error("[ConfigDB/%s] %s" % [category, message])
|
||||
|
||||
|
||||
# 运行期数据致命(2026-09-18 用户拍板 fail-fast;迭代 27 T6——退出执行归根:核心层不再自携
|
||||
# 运行期数据致命(fail-fast;退出执行归根:核心层不自携
|
||||
# quit/crash,越域记 Fatal 级日志后置 _fatal_flag,进程退出决策归组合根(main._ready 表加载
|
||||
# 失败已有同通道 fatal 屏+fail-fast 边界)——查询期消费方按返回值安全面走既有默认值回退)
|
||||
func _fatal(category: String, message: String) -> void:
|
||||
@@ -79,7 +79,7 @@ func _fatal(category: String, message: String) -> void:
|
||||
_fatal_flag = true
|
||||
|
||||
|
||||
var _fatal_flag := false # 运行期致命封印(迭代 27 T6——退出归根承载;组合根/闸读面)
|
||||
var _fatal_flag := false # 运行期致命封印(退出归根承载;组合根/闸读面)
|
||||
|
||||
|
||||
func is_fatal() -> bool:
|
||||
@@ -152,7 +152,7 @@ func load_tables(root: String, collect_only: bool) -> bool:
|
||||
return not has_errors
|
||||
|
||||
|
||||
# ---- relic 访问器(迭代 12 T1——消费面一律走本组,禁直读行集)----
|
||||
# ---- relic 访问器(消费面一律走本组,禁直读行集)----
|
||||
|
||||
func feat_row(feat_id: String) -> Dictionary:
|
||||
return feats.row_by_id(feat_id) if feats != null else {}
|
||||
@@ -163,7 +163,7 @@ func feat_rows() -> Array:
|
||||
return feats.rows if feats != null else []
|
||||
|
||||
|
||||
# 天赋树节点行(迭代 12 T8——MetaProgression 树段/营地树 UI 取数口)
|
||||
# 天赋树节点行(MetaProgression 树段/营地树 UI 取数口)
|
||||
func tree_node(node_id: String) -> Dictionary:
|
||||
return meta_tree.row_by_id(node_id) if meta_tree != null else {}
|
||||
|
||||
@@ -172,8 +172,8 @@ func tree_rows() -> Array:
|
||||
return meta_tree.rows if meta_tree != null else []
|
||||
|
||||
|
||||
# 跨表校验(悬空引用 / 池护栏——全部加载期致命)本体自 2026-10-02 修复批迁出:
|
||||
# ConfigDbCrossValidator(core/config_db_cross_validator.gd)承载,门面公有签名零变化。
|
||||
# 跨表校验(悬空引用 / 池护栏——全部加载期致命)本体:
|
||||
# ConfigDbCrossValidator(core/config_db_cross_validator.gd)承载,门面公有签名不变。
|
||||
func _cross_validate(loaded: Dictionary) -> void:
|
||||
_validator.cross_validate(loaded, push_diag)
|
||||
|
||||
@@ -183,15 +183,15 @@ func validate_skill_pool_links(skill_table: SkillTable, enemy_table: EnemyTable,
|
||||
return _validator.validate_skill_pool_links(skill_table, enemy_table, status_table)
|
||||
|
||||
|
||||
# ---- R1 难度行解析(2026-09-18 用户拍板修订:ceil 向上取整保证偏难;域越界与内容缺口 Fatal 退出)----
|
||||
# 解析本体自 2026-10-02 修复批迁出:ConfigDbResolver(core/config_db_resolver.gd)承载——
|
||||
# 含 _resolve_progress/_ceil_key 与 R1 告警去重注册表;门面同名方法委托,越域 Fatal 语义逐位一致。
|
||||
# ---- R1 难度行解析(ceil 向上取整保证偏难;域越界与内容缺口 Fatal 退出)----
|
||||
# 解析本体:ConfigDbResolver(core/config_db_resolver.gd)承载——
|
||||
# 含 _resolve_progress/_ceil_key 与 R1 告警去重注册表;门面同名方法委托,越域 Fatal 语义一致。
|
||||
func resolve_board_gen(L: int, D: int) -> Dictionary:
|
||||
return _resolver.resolve_board_gen(L, D)
|
||||
|
||||
|
||||
func resolve_enemy_rowset(L: int, D: int) -> Array[Dictionary]:
|
||||
return _resolver.resolve_enemy_rowset(L, D) # 行集数组形态(2026-10-07 行集多行批——原单行 Dictionary 退役)
|
||||
return _resolver.resolve_enemy_rowset(L, D) # 行集数组形态(非单行 Dictionary)
|
||||
|
||||
|
||||
# 难度曲线与Boss DifficultyTable 消费面(敌人系统.spawn 调用):(L,D) 精确匹配修饰乘数,越域值 clamp(GDD Edge Case——组合事故非配置事故)
|
||||
@@ -199,9 +199,9 @@ func resolve_difficulty_modifiers(L: int, D: int) -> Dictionary:
|
||||
return _resolver.resolve_difficulty_modifiers(L, D)
|
||||
|
||||
|
||||
# ---- 地形变异 variant_pool_gate:候选池门控(∅ board_types = 通配;2026-09-19 用户裁定字段名对齐
|
||||
# board_type 词表——biome_tags 系旧观念残留退役)----
|
||||
# 门控第三维 = 节点身份(全局作用域变异首发批 2026-10-04):scope=global 行仅 boss/elite 节点入池,
|
||||
# ---- 地形变异 variant_pool_gate:候选池门控(∅ board_types = 通配;字段名对齐
|
||||
# board_type 词表,无 biome_tags)----
|
||||
# 门控第三维 = 节点身份(全局作用域变异):scope=global 行仅 boss/elite 节点入池,
|
||||
# 其余节点(battle 等)静默跳过——门控兜底 P0 口径(零报错零警告);elite 系前向词位(首发零表行)。
|
||||
# node_type 缺省 "" = 旧双参调用口径(global 行跳过)——既有测试/冒烟调用零扰动。
|
||||
|
||||
@@ -238,8 +238,8 @@ func status_effect_row(effect_id: String) -> Dictionary:
|
||||
return status_effects.row_by_id(effect_id)
|
||||
|
||||
|
||||
# ---- 关卡行集访问器(2026-10-02 修复批 T5 表访问收口——level_runner/meta_progression/adventure_runner
|
||||
# 直扫 db.level.rows 退役;消费面一律走本访问器,行集解析单点归 LevelTable)----
|
||||
# ---- 关卡行集访问器(表访问收口——level_runner/meta_progression/adventure_runner
|
||||
# 不直扫 db.level.rows;消费面一律走本访问器,行集解析单点归 LevelTable)----
|
||||
|
||||
# 关卡行单点读取(键 = level_index,缺行返回空字典——消费方按既有空行防御路径处理)
|
||||
func level_row(level_index: int) -> Dictionary:
|
||||
@@ -251,27 +251,27 @@ func level_count() -> int:
|
||||
return level.rows.size() if level != null else 0
|
||||
|
||||
|
||||
# ---- GP+商店房实施批(2026-09-21):ShopTable 消费面(行集解析单点——runner/战斗编排禁自扫表)----
|
||||
# ── 秘宝访问器(迭代 7 T1——消费面一律走访问器,禁直读行集)──
|
||||
# ---- ShopTable 消费面(行集解析单点——runner/战斗编排禁自扫表)----
|
||||
# ── 秘宝访问器(消费面一律走访问器,禁直读行集)──
|
||||
# 活行集(非 retired 非 deferred)——投放校验/奖励节点池/呈现可用性的唯一取数口径
|
||||
func treasure_item_row(item_id: String) -> Dictionary:
|
||||
return treasure_items.row_by_id(item_id) if treasure_items != null else {}
|
||||
|
||||
|
||||
# 过渡渠道活行(form 空 = 切片期过渡形态)——掉落投放/停点池/工坊解锁取数口径;同调行(form 非空)
|
||||
# 永不入过渡渠道(迭代 11 第①批 T1;T7 退役面切换后本口径随渠道消亡)
|
||||
# 永不入过渡渠道
|
||||
func treasure_transitional_rows() -> Array:
|
||||
return treasure_items.transitional_rows() if treasure_items != null else []
|
||||
|
||||
|
||||
# 同调行集(form 非空——装配覆盖层/效果接线的取数口径,迭代 11 第①批 T1/T4)
|
||||
# 同调行集(form 非空——装配覆盖层/效果接线的取数口径)
|
||||
func treasure_attunement_rows() -> Array:
|
||||
return treasure_items.attunement_rows() if treasure_items != null else []
|
||||
|
||||
|
||||
# 活行词表(掉落投放 effect_id 白名单——过渡渠道口径)
|
||||
# ── 同调轴访问器已删除(迭代 21 T5——attunement_growth 谓词表随槽位收编退役:槽位数量=树同调位
|
||||
# 节点点亮数,判定源=Attunement.slots;boss_kill_count/clear_count 计数域保留——非槽位硬消费在册)──
|
||||
# ── 同调槽位数=树同调位节点点亮数,判定源=Attunement.slots(无 attunement_growth 谓词表访问器);
|
||||
# boss_kill_count/clear_count 计数域保留——非槽位硬消费在册 ──
|
||||
|
||||
|
||||
# 行族四轴(数据契约,行集归 shop.tres):gp_drops {tier, mean, roll_min, roll_max} ·
|
||||
@@ -311,7 +311,7 @@ func shop_product_row(product_id: String) -> Dictionary:
|
||||
return {}
|
||||
|
||||
|
||||
# ── 共享池货架访问器(M-4 迭代 11 第②批 T1——消费面一律走访问器,禁直读行集)──
|
||||
# ── 共享池货架访问器(M-4——消费面一律走访问器,禁直读行集)──
|
||||
|
||||
# 货架行族({kind_id, display_name, base_price}——行序即抽签/呈现稳定序)
|
||||
func shop_pool_rows() -> Array:
|
||||
@@ -342,8 +342,8 @@ func _shop_rows(family: String) -> Array:
|
||||
return arr if arr is Array else []
|
||||
|
||||
|
||||
# ---- 元进度实现批(2026-09-21):MetaTable 消费面(行集解析单点——MetaProgression/呈现层禁自扫表)----
|
||||
# 行族四轴(数据契约,行集归 meta.tres,数值全 provisional 归元进度设计批配读数):
|
||||
# ---- MetaTable 消费面(行集解析单点——MetaProgression/呈现层禁自扫表)----
|
||||
# 行族四轴(数据契约,行集归 meta.tres,数值全 provisional):
|
||||
# forge_products {attr, display_name, flat_value, base_price, price_ratio_pct} ·
|
||||
# boss_keystone {level_index, first_kill, repeat_kill} ·
|
||||
# workshop_items {item_id, display_name, clear_threshold} ·
|
||||
@@ -356,8 +356,8 @@ func meta_boss_keystone_row(level_index: int) -> Dictionary:
|
||||
return {}
|
||||
|
||||
|
||||
# meta_workshop_items 访问器已删除(迭代 22 T2——工坊行族退役,解锁混合制挂 treasure_items 行内;
|
||||
# 秘宝解锁投影面=MetaProgression.treasure_shop_status)
|
||||
# 秘宝解锁混合制挂 treasure_items 行内(无 meta_workshop_items 访问器);
|
||||
# 秘宝解锁投影面=MetaProgression.treasure_shop_status
|
||||
|
||||
|
||||
func meta_conversion_row(attr: String) -> Dictionary:
|
||||
@@ -367,7 +367,7 @@ func meta_conversion_row(attr: String) -> Dictionary:
|
||||
return {}
|
||||
|
||||
|
||||
# Y 刷新升级行单点读取(迭代 11 第①批 T4——键 = 等级,缺行返回空字典=未购/已满级合法态)
|
||||
# Y 刷新升级行单点读取(键 = 等级,缺行返回空字典=未购/已满级合法态)
|
||||
func meta_attune_refresh_row(level: int) -> Dictionary:
|
||||
for row in _meta_rows("attune_refresh_upgrade"):
|
||||
if int(row.get("level", -1)) == level:
|
||||
@@ -383,7 +383,7 @@ func _meta_rows(family: String) -> Array:
|
||||
return arr if arr is Array else []
|
||||
|
||||
|
||||
# ---- 锻造工坊(迭代 23 T1——ForgeTable 消费面:两根类型轴默认值+六路线行族;禁自扫行集)----
|
||||
# ---- 锻造工坊(ForgeTable 消费面:两根类型轴默认值+六路线行族;禁自扫行集)----
|
||||
# 设计权威 design/gdd/systems/跨局循环/锻造工坊.md v0.6:默认值归本表常驻持有(hero.tres 两轴已删——
|
||||
# 契约 2);路线行族=攻防数值成长唯一数据面(契约 1)。数值全 provisional 归 M4。
|
||||
|
||||
|
||||
@@ -15,7 +15,7 @@ func cross_validate(loaded: Dictionary, push_diag: Callable) -> void:
|
||||
for row in st.rows:
|
||||
effect_ids[row.get("effect_id", "")] = true
|
||||
# ① RewardTable effect_id 悬空(percent 行引用 StatusEffectTable——悬空加载致命)。
|
||||
# GP+商店房实施批(2026-09-21)例外:gp_gain/gp_loss = GP 战斗内缓冲(escrow)通道的路由键
|
||||
# 例外:gp_gain/gp_loss = GP 战斗内缓冲(escrow)通道的路由键
|
||||
# (战斗结算 _apply_gp_row 兑付——不入增益状态容器),词表封闭按字面豁免悬空引用校验。
|
||||
for row in rt.rows:
|
||||
if String(row.get("cell_type", "")) == RewardTable.TREASURE_ITEM_TYPE:
|
||||
@@ -23,7 +23,7 @@ func cross_validate(loaded: Dictionary, push_diag: Callable) -> void:
|
||||
var eff: String = row.get("effect_id", "")
|
||||
if eff != "" and eff != "gp_gain" and eff != "gp_loss" and not effect_ids.has(eff):
|
||||
push_diag.call("error", "dangling_effect", "RewardTable %s 引用不存在的 effect_id: %s" % [row.get("reward_id"), eff])
|
||||
# ①b RewardTable aux 投放行双表校验(迭代 7 T1——reward_dangling_treasure):aux 行 effect_id = item_id,
|
||||
# ①b RewardTable aux 投放行双表校验(reward_dangling_treasure):aux 行 effect_id = item_id,
|
||||
# 须命中 TreasureItemTable 活行(非 retired 非 deferred——罗盘退役/失义件不投放)。retired 行被引用=退役
|
||||
# 后仍投放(连带错误),同拦。
|
||||
var treasure_live := {}
|
||||
@@ -39,17 +39,14 @@ func cross_validate(loaded: Dictionary, push_diag: Callable) -> void:
|
||||
continue
|
||||
if not treasure_live.has(treasure_eff):
|
||||
push_diag.call("error", "reward_dangling_treasure", "RewardTable %s aux 行引用无效道具 %s(retired/deferred/不存在): %s" % [row.get("reward_id"), treasure_eff, "双表校验"])
|
||||
# ①c F2 投放维度覆盖(秘宝 GDD——五维度各 ≥1 掉落权重为正的活行):豁免面 {plan, info, counter}
|
||||
# = 罗盘退役(plan,2026-09-29 拍板①乙)+显影件 Alpha 重定(info/counter 悬置)——豁免随 Alpha 重定批消减
|
||||
# ①c F2 投放维度覆盖校验随战斗掉落渠道退役(2026-10-01 用户拍板甲——挖格不掉道具,秘宝
|
||||
# 唯一来路=三选一共享池〔节点/通关〕):旧校验=「掉落池五维度各 ≥1 正权重活行」,载体已随
|
||||
# reward.tres aux 投放四行删除而消失;共享池为「抽到道具位再动态掷定具体道具」,维度覆盖不可
|
||||
# 静态校验。静态覆盖保证的重立(如按维度钉共享池抽签面)归同调实施批/渠道设计批。
|
||||
# ①c F2 投放维度覆盖校验不启用:挖格不掉道具,秘宝
|
||||
# 唯一来路=三选一共享池〔节点/通关〕;共享池为「抽到道具位再动态掷定具体道具」,维度覆盖不可
|
||||
# 静态校验。
|
||||
# ② SettlementTable.effect_pool_map 悬空
|
||||
for eff in st_tab.effect_pool_map.keys():
|
||||
if not effect_ids.has(eff):
|
||||
push_diag.call("error", "poolmap_dangling", "SettlementTable.effect_pool_map 悬空 effect_id: %s" % eff)
|
||||
# ③ source_label_map 键集封闭性(键 = 增益状态 source_class ∪ {enemy_action, hero_attack, seal_miss}——2026-09-15 格面资源批删 item 键)
|
||||
# ③ source_label_map 键集封闭性(键 = 增益状态 source_class ∪ {enemy_action, hero_attack, seal_miss})
|
||||
var required_sources := ["safe_cell", "seal_hit", "mine_hit", "clear_reward", "hero_attack", "enemy_action", "seal_miss"]
|
||||
for s in required_sources:
|
||||
if not st_tab.source_label_map.has(s):
|
||||
@@ -91,7 +88,7 @@ func cross_validate(loaded: Dictionary, push_diag: Callable) -> void:
|
||||
if skt != null and en != null:
|
||||
for e in validate_skill_pool_links(skt, en, loaded.get("status_effects")):
|
||||
push_diag.call("error", e.category, e.message)
|
||||
# ⑥ 商店货架 × 冒险共享池跨表校验(M-4 迭代 11 第②批 T1——次数上限域):
|
||||
# ⑥ 商店货架 × 冒险共享池跨表校验(M-4——次数上限域):
|
||||
# pool_shelf kind 须命中 adventure.CURIO_KINDS 且为有限库存种(cap ∈ [1,99]——gp 无上限种/牌库外种
|
||||
# 入货架 = 库存账本无锚,加载致命;gp 袋负交易切边/规则改写型特性三轴切边由 ShopTable 词表封闭另拦)
|
||||
var adv = loaded.get("adventure")
|
||||
@@ -100,12 +97,12 @@ func cross_validate(loaded: Dictionary, push_diag: Callable) -> void:
|
||||
for row in shp.pool_shelf:
|
||||
var kind: String = String(row.get("kind_id", ""))
|
||||
if kind == "feat_ticket":
|
||||
continue # T5 门票豁免——货架专属机制种(非 deck 池种,零摘牌拿到即开),不入 CURIO_KINDS
|
||||
continue # 门票豁免——货架专属机制种(非 deck 池种,零摘牌拿到即开),不入 CURIO_KINDS
|
||||
if not adv.CURIO_KINDS.has(kind):
|
||||
push_diag.call("error", "shelf_dangling_kind", "ShopTable.pool_shelf kind %s 不在 AdventureTable.CURIO_KINDS(共享池牌库外种)" % kind)
|
||||
continue
|
||||
# TP0 令牌池——货架件须为有限令牌种(配额 >0;种类次数上限退役后库存=剩余令牌数。
|
||||
# 迭代 20 T2 四级换代——逐档累计遍历 TIERS 词表,不再点名档位常量)
|
||||
# TP0 令牌池——货架件须为有限令牌种(配额 >0;种类次数上限已废除,库存=剩余令牌数。
|
||||
# 四级词表——逐档累计遍历 TIERS 词表,不点名档位常量)
|
||||
var quota := 0
|
||||
for tier_v in adv.TIERS:
|
||||
quota += int(adv.curio_quota(kind, String(tier_v)))
|
||||
@@ -123,7 +120,7 @@ func validate_skill_pool_links(skill_table: SkillTable, enemy_table: EnemyTable,
|
||||
var skill_types := {}
|
||||
for row in skill_table.rows:
|
||||
skill_types[String(row.get("skill_id", ""))] = String(row.get("type", ""))
|
||||
# 施放类 side 守卫(新效应词汇批 2026-09-19):buff → 敌方侧状态 / debuff → 主角侧状态,防反向挂接
|
||||
# 施放类 side 守卫:buff → 敌方侧状态 / debuff → 主角侧状态,防反向挂接
|
||||
for row in skill_table.rows:
|
||||
var st: String = String(row.get("type", ""))
|
||||
if st != "buff" and st != "debuff":
|
||||
|
||||
@@ -4,11 +4,11 @@
|
||||
class_name ConfigDbResolver
|
||||
extends RefCounted
|
||||
|
||||
var _diag: Callable # 诊断/致命通道(迭代 27 T6——门面类型反持解耦,validator 先例:签名 (level, category, message))
|
||||
var _diag: Callable # 诊断/致命通道(门面类型反持解耦,validator 先例:签名 (level, category, message))
|
||||
var _board_gen_rows_get: Callable # () -> Array——board_gen 行集活读(数据面经 Callable 注入,ConfigDb 类型环断根)
|
||||
var _enemy_rows_get: Callable # () -> Array——enemy 行集活读
|
||||
var _difficulty_get: Callable # () -> DifficultyTable(可空——表缺省合法态)
|
||||
var _warn_dedup: Dictionary[String, bool] = {} # R1 四元组去重注册表(自 ConfigDb 迁入)
|
||||
var _warn_dedup: Dictionary[String, bool] = {} # R1 四元组去重注册表
|
||||
|
||||
|
||||
func _init(diag_cb: Callable, board_gen_rows_cb: Callable,
|
||||
@@ -19,19 +19,19 @@ func _init(diag_cb: Callable, board_gen_rows_cb: Callable,
|
||||
_difficulty_get = difficulty_cb
|
||||
|
||||
|
||||
# 去重注册表清空(门面 load_tables / reset_diagnostics 调用——与拆分前清空时点逐位一致)
|
||||
# 去重注册表清空(门面 load_tables / reset_diagnostics 调用)
|
||||
func reset_warn_dedup() -> void:
|
||||
_warn_dedup.clear()
|
||||
|
||||
|
||||
# ---- R1 难度行解析(2026-09-18 用户拍板修订:ceil 向上取整保证偏难;域越界与内容缺口 Fatal 退出)----
|
||||
# ---- R1 难度行解析(ceil 向上取整保证偏难;域越界与内容缺口 Fatal 退出)----
|
||||
|
||||
func resolve_board_gen(L: int, D: int) -> Dictionary:
|
||||
return _resolve_progress(_board_gen_rows_get.call(), L, D, "board_gen")
|
||||
|
||||
|
||||
func resolve_enemy_rowset(L: int, D: int) -> Array[Dictionary]:
|
||||
# 行集数组形态(2026-10-07 敌人表行集多行批——原单行 Dictionary 退役):ceil 命中 (l*,d*) 后
|
||||
# 行集数组形态:ceil 命中 (l*,d*) 后
|
||||
# 返回该坐标全部行、按表文件行序(行序即契约——同坐标多行文件内相邻由 EnemyTable.validate 兜底)。
|
||||
# 空集表达 = 空数组(生产表下 ceil 命中坐标恒有行,空集仅表空时可达——validate 已拦)。
|
||||
return _resolve_progress(_enemy_rows_get.call(), L, D, "enemy", true)
|
||||
@@ -67,7 +67,7 @@ func _resolve_progress(rows: Array, L: int, D: int, table_id: String, return_all
|
||||
var l_star := _ceil_key(level_keys, L)
|
||||
var l_clamped := l_star < 0
|
||||
if l_clamped:
|
||||
l_star = int(level_keys[-1]) # 超出最大行 → 取最大行封顶(2026-09-18 追加拍板:可取范围内向上取整,超出用最大值)
|
||||
l_star = int(level_keys[-1]) # 超出最大行 → 取最大行封顶(可取范围内向上取整,超出用最大值)
|
||||
var depth_keys: Array[int] = []
|
||||
for row in rows:
|
||||
if int(row.get("level_index", 0)) == l_star:
|
||||
|
||||
@@ -7,14 +7,14 @@ class_name DamageController
|
||||
extends RefCounted
|
||||
|
||||
const POLICY_IMMEDIATE := "immediate" # A:开单即结(现状行为——回切位)
|
||||
const POLICY_SEGMENT := "segment" # B:攒批到段界 flush 统一结(2026-09-24 用户拍板目标形态)
|
||||
const POLICY_SEGMENT := "segment" # B:攒批到段界 flush 统一结(目标形态)
|
||||
|
||||
# 兑付配方(kind → 步序):
|
||||
# hero_attack 战斗段·主角先手:掷点折扣暴击 → 克制点数(attack_type vs 敌护甲类型)→
|
||||
# hero_offense×enemy_defense 池乘区 → 下限 → 交付;行载荷带 roll/ac/tier/killed。
|
||||
# enemy_skill 战斗段·敌方伤害技(含普攻):基线 = round(atk×倍率)(开单侧算好入 base_value)→
|
||||
# 掷点折扣暴击 → 克制点数(attack_type vs 主角护甲类型——迭代 24 N3 轴开列,
|
||||
# 前态恒中性 delta 0)→ hero_defense×enemy_output
|
||||
# 掷点折扣暴击 → 克制点数(attack_type vs 主角护甲类型——
|
||||
# 无类型恒中性 delta 0)→ hero_defense×enemy_output
|
||||
# 池乘区 → 下限 → +rider(必中附加)→ 交付 → commit_action;来源敌已死 = 作废。
|
||||
# board_typed 棋盘·单目标克制直伤(穿刺弹/魔法矢正向):必中全额,克制点数进加法核,下限保底。
|
||||
# board_blast 棋盘·群伤(爆裂粉尘/清障炸药):无类型恒定表值,全体存活敌逐个交付。
|
||||
@@ -30,19 +30,19 @@ const TARGET_HERO := "hero"
|
||||
const TARGET_ALL_ALIVE := "all_alive_enemies"
|
||||
|
||||
var db: ConfigDb
|
||||
var rng: RngServiceCore # 注入成员类型注(2026-10-02 修复批——单测经真实 RngService 构造,标注安全)
|
||||
var rng: RngServiceCore # 注入成员类型注(单测经真实 RngService 构造,标注安全)
|
||||
var units: UnitContainer # UnitContainer——目标解析/伤害交付/存活查询(句柄即 Dictionary 引用)
|
||||
var effects: StatusContainer # StatusContainer——池值/AC 型效果读取(增益状态 只读消费面)
|
||||
var emit_row_cb: Callable # 行协议注入口:func(type, source_class, origin_cell, ri, display, value, extra, highlight)
|
||||
|
||||
var policy := POLICY_SEGMENT # 结账时机开关(2026-09-24 用户拍板第二步拨 B=攒批段界统一结;回切 A 改回 POLICY_IMMEDIATE 即还原迁移前行为)
|
||||
var policy := POLICY_SEGMENT # 结账时机开关(B=攒批段界统一结;回切 A 改 POLICY_IMMEDIATE 即开单即结)
|
||||
var _queue: Array[Dictionary] = [] # 未结单据(B 模式攒批;A 模式恒空)
|
||||
var _outcomes := {} # ticket_id → "redeemed"/"zero"/"voided"(结账后可查——清图高光份数消费)
|
||||
var _seq := 0 # 流水号
|
||||
var _halted := false # 主角倒下终止封印(死亡事件置位——flush 余单作废)
|
||||
var _hero_ac_override := -1 # 主角护甲等级合流入参(begin_battle 显式设定——-1 = 表值兜底,口径同战斗编排器)
|
||||
# 效果引擎(内容数据驱动扩展批 T1/T3——attack_commit/clamp_check/armor_roll/kill/damage_taken
|
||||
# 发射+施加面:dispatch 返回命中,本类按组合规则施加——T3 起旧 feat_seals 封印消费点全部拆除)
|
||||
# 效果引擎(attack_commit/clamp_check/armor_roll/kill/damage_taken
|
||||
# 发射+施加面:dispatch 返回命中,本类按组合规则施加)
|
||||
var _fx: EffectEngine = null
|
||||
# 战斗段运行态事实(编排器每轮刷新——fact 取数面:attack_commit 载荷 dig_streak /
|
||||
# charge_layers facts〔猎巢人 per/整备 attack_bonus per/厚积薄发 when/铁壁 shield per〕;
|
||||
@@ -80,7 +80,7 @@ func _fx_hits(event: String, payload: Dictionary) -> Array:
|
||||
|
||||
# 事件发射薄面(免施加观测位——enemy_hp_threshold 等现无订阅者的事件保留零扰动面)
|
||||
# 补充事实面(fact 取值第二优先源——payload 未携带的运行态读数:charge_layers 编排器每轮注入。
|
||||
# 迭代 20 T1——hp_ratio 补入〔背水 when 面,敌技 damage_taken 载荷不带血比例时回落〕;
|
||||
# hp_ratio 补入〔背水 when 面,敌技 damage_taken 载荷不带血比例时回落〕;
|
||||
# fx_reveal_request=揭示效果回调〔斩首学者 kill 订阅面——编排器注入,入队授予段末兑付〕)
|
||||
func _fx_fact(fact: String):
|
||||
match fact:
|
||||
@@ -135,7 +135,7 @@ func submit(t: Dictionary) -> int:
|
||||
|
||||
# 单据构造(缺省填齐——调用点只写差异栏;context = kind 专用:enemy_skill 带 skill_id/baseline,
|
||||
# board_flat_hero 的键并入行载荷,board_blast/board_typed 带 batch_key 供未生效行归因)。
|
||||
# attack_commit 发射点(内容数据驱动扩展批 T3——我方伤害单据开立入口,需求卡片 §3.1 定稿位;
|
||||
# attack_commit 发射点(我方伤害单据开立入口;
|
||||
# 命中挂单据 context 随行——施加面在 _resolve_hero_attack 管线算术前逐族施加,开单时点=主角攻击
|
||||
# 单恒为段内首单(结账现场与开单现场一致)。载荷 atk/target/matchup + 挖掘 streak 事实〔连珠矢/
|
||||
# 执拗订阅面〕+巢穴控制数〔猎巢人〕)
|
||||
@@ -170,7 +170,7 @@ func open_ticket(kind: String, source_class: String, display: String, channel: S
|
||||
return t
|
||||
|
||||
|
||||
# 同步开单(清图批专用——完美第二份与高光份数的流程依赖需要同步读数,开单权归流程 用户裁决③):
|
||||
# 同步开单(清图批专用——完美第二份与高光份数的流程依赖需要同步读数,开单权归流程):
|
||||
# 先按开单序结清段内排队的单据(保序),再无视结账时机政策当场兑现本单。
|
||||
func flush_submit(t: Dictionary) -> int:
|
||||
flush()
|
||||
@@ -203,7 +203,7 @@ func outcome(ticket_id: int) -> String:
|
||||
return String(_outcomes.get(ticket_id, "pending"))
|
||||
|
||||
|
||||
# ---- 死亡事件(用户裁决③——单位自行宣布,本类消费)----
|
||||
# ---- 死亡事件(单位自行宣布,本类消费)----
|
||||
|
||||
func _on_unit_died(u: Dictionary) -> void:
|
||||
if bool(u.get("is_hero", false)):
|
||||
@@ -243,12 +243,12 @@ func _resolve(t: Dictionary) -> void:
|
||||
|
||||
|
||||
# 主角先手攻击:掷点 → 折扣/暴击 → 克制点数 → hero_offense×enemy_defense 池乘区 → 下限 → 交付
|
||||
# (内容数据驱动扩展批 T3——attack_commit/clamp_check/armor_roll 订阅施加面:命中=开单入口随单
|
||||
# context 携带,按迁移前算序逐族施加——命中序=数据表声明序,确定性)。
|
||||
# 迭代 27 T2 姊妹病灶拆段(P1-1):原 148 行单体拆四步——命中面(_hero_attack_ctx,目标/护甲/
|
||||
# (attack_commit/clamp_check/armor_roll 订阅施加面:命中=开单入口随单
|
||||
# context 携带,按固定算序逐族施加——命中序=数据表声明序,确定性)。
|
||||
# 四步分段:命中面(_hero_attack_ctx,目标/护甲/
|
||||
# 掷点/按序改写族四拍/克制点数) → 基值装配(_hero_attack_base_value,加法族/计数器施加/倍乘) →
|
||||
# 掷点兑现(_hero_attack_redeem,rolled/池乘/下限/rider/落地/击杀发射) → 交付(_hero_attack_deliver,
|
||||
# 状态施加/标签/明细行)。步骤间经 ctx/redemption 字典穿线——算序/取值逐字保留(🔴 逐位一致契约)。
|
||||
# 状态施加/标签/明细行)。步骤间经 ctx/redemption 字典穿线——算序/取值固定(🔴 逐位一致契约)。
|
||||
func _resolve_hero_attack(t: Dictionary) -> void:
|
||||
var ctx := _hero_attack_ctx(t)
|
||||
if ctx.is_empty():
|
||||
@@ -272,10 +272,10 @@ func _hero_attack_ctx(t: Dictionary) -> Dictionary:
|
||||
var ac_payload: Dictionary[Variant, Variant] = (t.context.get("fx_attack_payload", {}) as Dictionary)
|
||||
var roll_read: Dictionary[Variant, Variant] = _armor_roll()
|
||||
var threshold: int = defender_ac(target) + int(db.settlement.attack_bonus)
|
||||
# armor_roll 发射点(内容数据驱动扩展批 T1——敌护甲掷骰判定;免掷恒折扣订阅面)
|
||||
# armor_roll 发射点(敌护甲掷骰判定;免掷恒折扣订阅面)
|
||||
var ar_hits := _fx_hits("armor_roll", {"target_ac": threshold, "roll": int(roll_read.roll)})
|
||||
# 按序改写族第一拍:矿工重锤命中交换(正负双修原生形态:命中判定减成 trade_hit 点〔更易命中〕
|
||||
# 换攻击增伤 trade_atk%——高 AC 负期望/低 AC 正期望;改写位先于掷点折扣读数,与迁移前一致)
|
||||
# 换攻击增伤 trade_atk%——高 AC 负期望/低 AC 正期望;改写位先于掷点折扣读数)
|
||||
var trade_hit := 0
|
||||
for h in ac_hits:
|
||||
if String(h.get("kind", "")) == "modify_ticket" and int(h.get("trade_hit", 0)) > 0:
|
||||
@@ -314,9 +314,9 @@ func _hero_attack_ctx(t: Dictionary) -> Dictionary:
|
||||
for h in clamp_hits:
|
||||
if String(h.get("kind", "")) == "modify_ticket" and h.has("delta"):
|
||||
cdelta += int(h.delta)
|
||||
# 淬毒刃纹(迭代 11 同调批 T5 → 内容数据驱动扩展批 T4:特判拆除——行 triggers clamp_check
|
||||
# modify(mode:clamp_bonus) 订阅命中已并入上方 delta 合计;mode 路由=主角攻击面专享,
|
||||
# 棋盘克制直伤窗只消费无模式 delta——棋盘不吃毒面与迁移前一致)
|
||||
# 淬毒刃纹(行 triggers clamp_check
|
||||
# modify(mode:clamp_bonus) 订阅命中并入上方 delta 合计;mode 路由=主角攻击面专享,
|
||||
# 棋盘克制直伤窗只消费无模式 delta——棋盘不吃毒面)
|
||||
return {"target": target, "armor": armor, "atk_type": atk_type, "ac_hits": ac_hits,
|
||||
"ac_payload": ac_payload, "roll_read": roll_read, "threshold": threshold,
|
||||
"crit": crit, "discount": discount, "spike_ready": spike_ready, "cdelta": cdelta}
|
||||
@@ -328,7 +328,7 @@ func _hero_attack_base_value(t: Dictionary, ctx: Dictionary) -> float:
|
||||
var ac_hits: Array[Variant] = ctx["ac_hits"]
|
||||
var ac_payload: Dictionary[Variant, Variant] = ctx["ac_payload"]
|
||||
var base_value := float(t.base_value)
|
||||
# 单位标记加伤(迭代 20 T1——处刑台 target_attack_bonus 挂单位字典 fx_atk_bonus 键;
|
||||
# 单位标记加伤(处刑台 target_attack_bonus 挂单位字典 fx_atk_bonus 键;
|
||||
# 阈值沿发射点已写入,本处通用消费——对该敌伤害加算)
|
||||
base_value += float(int((ctx["target"] as Dictionary).get("fx_atk_bonus", 0)))
|
||||
# 按序改写族第五拍:矿工重锤增伤面(交换的正向侧)
|
||||
@@ -336,7 +336,7 @@ func _hero_attack_base_value(t: Dictionary, ctx: Dictionary) -> float:
|
||||
if String(h.get("kind", "")) == "modify_ticket" and int(h.get("trade_atk", 0)) > 0:
|
||||
base_value *= 1.0 + float(int(h.trade_atk)) / 100.0
|
||||
# 加法族:整备军备「每层下轮首段 +1」(层数×value 加算)——attack_bonus per charge_layers。
|
||||
# 迭代 20 T1——value_counter 扩展〔疼痛记忆/弃图兑换/陷阱本能——rider 值=计数器账面,
|
||||
# value_counter 扩展〔疼痛记忆/弃图兑换/陷阱本能——rider 值=计数器账面,
|
||||
# 值读于施加时点=命中序先行 rider 后清零行〕
|
||||
for h in ac_hits:
|
||||
if String(h.get("kind", "")) == "attack_bonus":
|
||||
@@ -344,7 +344,7 @@ func _hero_attack_base_value(t: Dictionary, ctx: Dictionary) -> float:
|
||||
if h.has("value_counter") and String(h.get("value_counter", "")) != "":
|
||||
contrib = _fx.counter(String(h.get("value_counter", "")))
|
||||
base_value += float(contrib)
|
||||
# 计数器施加(迭代 20 T1——attack_commit 命中行内 counter_add〔用后清零行〕,命中序=声明序)
|
||||
# 计数器施加(attack_commit 命中行内 counter_add〔用后清零行〕,命中序=声明序)
|
||||
for h in ac_hits:
|
||||
if String(h.get("kind", "")) == "counter_add":
|
||||
var set_v = h.get("set", null)
|
||||
@@ -391,8 +391,8 @@ func _hero_attack_redeem(t: Dictionary, ctx: Dictionary, base_value: float) -> D
|
||||
landed += hero_rider
|
||||
payload["hero_rider"] = hero_rider
|
||||
units.apply_damage(target, landed)
|
||||
payload["killed"] = not target.alive # 击杀标记在扣血后取(原语义不变)
|
||||
# kill 发射+施加(内容数据驱动扩展批 T3——敌人死亡确认;贯穿射击溢出弹射订阅面:对下一存活敌
|
||||
payload["killed"] = not target.alive # 击杀标记在扣血后取
|
||||
# kill 发射+施加(敌人死亡确认;贯穿射击溢出弹射订阅面:对下一存活敌
|
||||
# 以穿刺类型弹射本体 value%〔无次敌=弹射落空〕。B 模式入队段尾 flush 兑现;A 模式递归 resolve)
|
||||
if bool(payload.get("killed", false)):
|
||||
for h in _fx_hits("kill", {"target": String(target.display_name), "overflow": landed}):
|
||||
@@ -403,7 +403,7 @@ func _hero_attack_redeem(t: Dictionary, ctx: Dictionary, base_value: float) -> D
|
||||
continue
|
||||
var next_target = units.default_target()
|
||||
if next_target != null:
|
||||
# 迭代 20 T9——source_class 中性名(旧件名 feat_pierce_shot 随贯穿射击退役;overflow 通道保留泛用面)
|
||||
# source_class 用中性名(fx_overflow 泛用通道,不挂件名)
|
||||
var of_t := open_ticket(DamageController.KIND_BOARD_TYPED, "fx_overflow",
|
||||
"贯穿弹射", "board", maxi(int(db.settlement.damage_floor), int(landed * int(h.get("value", 50)) / 100)),
|
||||
DamageController.TARGET_FRONT_ALIVE, int(t.round_index), t.origin_cell, "pierce")
|
||||
@@ -421,7 +421,7 @@ func _hero_attack_deliver(t: Dictionary, ctx: Dictionary, redemption: Dictionary
|
||||
# 重复授予由状态容器自行守卫)
|
||||
for h in (ctx["ac_hits"] as Array):
|
||||
if String(h.get("kind", "")) == "apply_status":
|
||||
# 迭代 20 T9——source_class 中性名(旧件名 feat_armor_crack 随碎甲震击退役;apply_status 通道保留泛用面)
|
||||
# source_class 用中性名(fx_apply_status 泛用通道,不挂件名)
|
||||
effects.apply_effect(String(h.get("status_id", "")), {"source_class": "fx_apply_status"}, -1)
|
||||
payload["cracked"] = true
|
||||
var label := _attack_label(String(t.display), bool(ctx["crit"]), int(ctx["discount"]))
|
||||
@@ -431,8 +431,8 @@ func _hero_attack_deliver(t: Dictionary, ctx: Dictionary, redemption: Dictionary
|
||||
_outcomes[int(t.ticket_id)] = "redeemed"
|
||||
|
||||
|
||||
# 敌方伤害技:掷点 → 折扣/暴击 → 克制点数(attack_type vs 主角护甲类型——迭代 24 N3 轴开列;
|
||||
# 前态恒中性 delta 0,写法同 hero_attack 的 _counter_delta 消费位)→ hero_defense×enemy_output
|
||||
# 敌方伤害技:掷点 → 折扣/暴击 → 克制点数(attack_type vs 主角护甲类型——
|
||||
# 无类型恒中性 delta 0,写法同 hero_attack 的 _counter_delta 消费位)→ hero_defense×enemy_output
|
||||
# 池乘区 → 下限 → +rider(后置必中附加)→ 交付 → commit_action(作废单不 commit——死亡即不动手)
|
||||
func _resolve_enemy_skill(t: Dictionary) -> void:
|
||||
var hero: Dictionary[Variant, Variant] = units.hero_unit()
|
||||
@@ -444,9 +444,9 @@ func _resolve_enemy_skill(t: Dictionary) -> void:
|
||||
var rolled: int = DamageMath.rolled_component(float(t.base_value), discount, crit, int(db.settlement.crit_multiplier), cdelta)
|
||||
var pooled: int = DamageMath.pool_damage(rolled, clamped_pool("hero_defense"), clamped_pool("enemy_output"))
|
||||
var total: int = maxi(int(db.settlement.damage_floor), pooled) + int(t.rider) # rider 后置于伤害下限——独立必中加项,不进乘区
|
||||
# damage_taken 发射+施加(内容数据驱动扩展批 T3——kind=enemy_skill;铁壁整备减伤订阅面:
|
||||
# damage_taken 发射+施加(kind=enemy_skill;铁壁整备减伤订阅面:
|
||||
# shield 加法族合计 value×per(charge_layers),稳健拓荒树规则同面(rebuild 合成订阅);
|
||||
# 减后过下限保底。迭代 20 T1——damage_reduce〔背水 hp_ratio when〕/counter_add〔保险丝/
|
||||
# 减后过下限保底。damage_reduce〔背水 hp_ratio when〕/counter_add〔保险丝/
|
||||
# 石肤〕/ac_bonus〔石肤〕同面施加;hp_ratio fact 由 _fx_fact 补充事实面回落提供)
|
||||
var shield := 0
|
||||
for h in _fx_hits("damage_taken", {"kind": "enemy_skill", "amount": total,
|
||||
@@ -488,7 +488,7 @@ func _resolve_board_typed(t: Dictionary) -> void:
|
||||
return
|
||||
var armor_b: String = String(target.get("armor_type", "light"))
|
||||
var atk_b: String = String(t.attack_type)
|
||||
# clamp_check 发射+施加(内容数据驱动扩展批 T3——克制判定修正点棋盘面;与 hero_attack 同表同点:
|
||||
# clamp_check 发射+施加(克制判定修正点棋盘面;与 hero_attack 同表同点:
|
||||
# modify delta 订阅改写合计进克制点数;discount_shift 档位面属主角攻击专享,本面不施加)
|
||||
var cdelta: int = _counter_delta(atk_b, armor_b)
|
||||
if atk_b != "":
|
||||
@@ -510,7 +510,7 @@ func _resolve_board_blast(t: Dictionary) -> void:
|
||||
_ineffective_row(t, "无存活敌人")
|
||||
_outcomes[int(t.ticket_id)] = "zero"
|
||||
return
|
||||
var blast_dmg: int = maxi(int(db.settlement.damage_floor), int(t.base_value)) # 伤害下限全来源(2026-09-20)
|
||||
var blast_dmg: int = maxi(int(db.settlement.damage_floor), int(t.base_value)) # 伤害下限全来源
|
||||
for u in alive:
|
||||
units.apply_damage(u, blast_dmg) # 恒定表值(下限保底),逐个;受击即死——死亡即不动手
|
||||
emit_row_cb.call("damage", String(t.source_class), t.origin_cell, int(t.round_index), "%s → 全体敌人 ×%d" % [String(t.display), alive.size()], blast_dmg,
|
||||
@@ -520,7 +520,7 @@ func _resolve_board_blast(t: Dictionary) -> void:
|
||||
|
||||
# 棋盘主角直伤(反噬/治疗包负向/地形变异):无类型必中,下限保底;context 键并入行载荷
|
||||
func _resolve_board_flat_hero(t: Dictionary) -> void:
|
||||
var dealt: int = maxi(int(db.settlement.damage_floor), int(t.base_value)) # 伤害下限全来源(2026-09-20)
|
||||
var dealt: int = maxi(int(db.settlement.damage_floor), int(t.base_value)) # 伤害下限全来源
|
||||
units.apply_damage(units.hero_unit(), dealt)
|
||||
var extra: Dictionary[Variant, Variant] = {"target": "hero"}
|
||||
for k in t.context.keys():
|
||||
@@ -567,7 +567,7 @@ func defender_ac(unit: Dictionary) -> int:
|
||||
|
||||
|
||||
# 克制修正点数读值(SettlementTable.armor_effectiveness:{攻击类型: {护甲: 点数}}——缺项回退 0 中性;
|
||||
# attack_type "" = 无类型恒不查(群伤/反噬/地形——敌方技自迭代 24 N3 起随单轴查表))
|
||||
# attack_type "" = 无类型恒不查(群伤/反噬/地形——敌方技随单轴查表))
|
||||
func _counter_delta(attack_type: String, armor: String) -> int:
|
||||
if attack_type == "":
|
||||
return 0
|
||||
@@ -576,7 +576,7 @@ func _counter_delta(attack_type: String, armor: String) -> int:
|
||||
return int(row.get(armor, 0))
|
||||
|
||||
|
||||
# 折扣档改善(迭代 12 T4 猎弱本能——折扣表向全额方向移 value 档;查不到当前档 = 不动防御口径)
|
||||
# 折扣档改善(猎弱本能——折扣表向全额方向移 value 档;查不到当前档 = 不动防御口径)
|
||||
func _discount_shift_up(discount: int, shift: int) -> int:
|
||||
var tbl: Array[Variant] = db.settlement.roll_tier_discounts
|
||||
var idx := tbl.find(discount)
|
||||
@@ -608,7 +608,7 @@ func _attack_label(base: String, crit: bool, discount: int) -> String:
|
||||
|
||||
# ---- 内部 ----
|
||||
|
||||
# 目标解析(裁决②——结账时刻照规则看现场;句柄即 Dictionary 引用)
|
||||
# 目标解析(结账时刻照规则看现场;句柄即 Dictionary 引用)
|
||||
func _target_for(rule: String):
|
||||
match rule:
|
||||
TARGET_FRONT_ALIVE:
|
||||
|
||||
@@ -11,11 +11,11 @@ static func pool_damage(d_raw: int, p_hero: int, p_enemy: int) -> int:
|
||||
return maxi(0, v)
|
||||
|
||||
|
||||
# 充能分量:充能点数(调用方已按奖励定义换算)+ 克制修正点数(加法核——2026-09-18 换装,原 percent 乘区退役);
|
||||
# 空桶(点数 ≤ 0)不吃修正恒 0——零充能分量无伤害可修正,不产生负贡献(等价旧量纲 0×任何 percent = 0;
|
||||
# 空桶不受 landed_floor 影响——没有伤害落地就没有下限可言)
|
||||
# 类型化点数分量:点数 + 克制修正(加法核)——2026-09-19 充能退役后唯一消费方 = 奖励直伤道具(棋盘火力)
|
||||
# landed_floor:落地伤害全局下限(读 settlement.damage_floor,调用方传入)——2026-09-20 用户拍板
|
||||
# 充能分量:充能点数(调用方已按奖励定义换算)+ 克制修正点数(加法核);
|
||||
# 空桶(点数 ≤ 0)不吃修正恒 0——零充能分量无伤害可修正,不产生负贡献;
|
||||
# 空桶不受 landed_floor 影响——没有伤害落地就没有下限可言。
|
||||
# 类型化点数分量:点数 + 克制修正(加法核)——唯一消费方 = 奖励直伤道具(棋盘火力)
|
||||
# landed_floor:落地伤害全局下限(读 settlement.damage_floor,调用方传入)——
|
||||
# 「任何伤害落地取值域 [1,+∞)」:克制修正后触底也保底 1,调参降档不可能打出 0/负伤害
|
||||
static func counter_component(points: int, counter_delta: int, landed_floor: int = 0) -> int:
|
||||
if points <= 0:
|
||||
@@ -23,7 +23,7 @@ static func counter_component(points: int, counter_delta: int, landed_floor: int
|
||||
return maxi(landed_floor, points + counter_delta)
|
||||
|
||||
|
||||
# ---- 护甲等级掷点批(2026-09-16 用户拍板——判定式与档位参数全在 settlement.tres,本类只做算术)----
|
||||
# ---- 护甲等级掷点(判定式与档位参数全在 settlement.tres,本类只做算术)----
|
||||
|
||||
# 掷点折扣查档(纯函数):掷点 ≥ 阈值 → 100 全额;低掷按缺口(阈值 − 掷点)查升序边界落档,
|
||||
# 返回档位值 percent(档数 = 边界数 + 1:缺口 ∈ [1, bounds[0]] → discounts[0],(bounds[i], bounds[i+1]] →
|
||||
|
||||
+24
-27
@@ -5,11 +5,10 @@
|
||||
class_name EffectEngine
|
||||
extends RefCounted
|
||||
|
||||
## 事件词汇(迭代 20 专长实装批 T1 扩签——16→18:+dig_stop 停手挂点〔弃图兑换〕
|
||||
## +battle_won 战斗胜利〔战地包扎〕;迭代 21 天赋树批 T3 扩签 18→19:+seal_missed 标错判定
|
||||
## 〔危险直觉·守挂点——_judge_seal 去重闸后发射,契约 12 触发主语=当回合标错动作;
|
||||
## 命名避让 _emit_row 在册 source_class 名 seal_miss〕;迭代 22 秘宝实装批 T3 扩签 19→20:
|
||||
## +board_installed 装板完成〔巢穴预告——每板恰一次,战斗板生成/重建发射点〕;原发射点不动)
|
||||
## 事件词汇(封闭枚举——dig_stop 停手挂点〔弃图兑换〕、battle_won 战斗胜利〔战地包扎〕、
|
||||
## seal_missed 标错判定〔危险直觉·守挂点——_judge_seal 去重闸后发射,契约 12 触发主语=
|
||||
## 当回合标错动作;命名避让 _emit_row 在册 source_class 名 seal_miss〕、board_installed
|
||||
## 装板完成〔巢穴预告——每板恰一次,战斗板生成/重建发射点〕)
|
||||
const EVENTS := ["dig_reveal", "dig_paid", "seal_hit", "mine_hit", "attack_commit", "clamp_check",
|
||||
"armor_roll", "kill", "damage_taken", "enemy_hp_threshold", "round_start", "seal_settle",
|
||||
"round_end", "reward_roll", "treasure_used", "charge_full", "dig_stop", "battle_won", "seal_missed",
|
||||
@@ -27,7 +26,7 @@ const EFFECT_KINDS := ["add_ticket_rider", "modify_ticket", "damage_mult", "open
|
||||
"grant_ap", "grant_gp", "bias_wallet", "shield", "spike", "apply_status", "counter_add",
|
||||
"counter_spend", "reveal_tiles", "remove_mines", "refresh_pick",
|
||||
"bias_rewards", "preview",
|
||||
# 迭代 20 专长实装批 T1 扩签六型(奇物.md 基础件表 27 件声明清单——落法映射 commit 留痕):
|
||||
# 六型效果(奇物.md 基础件表 27 件声明清单):
|
||||
# heal=回复生命〔血蜜/战地包扎〕;ac_bonus=护甲加值累积器〔封印桩本轮/石肤本场 cap〕;
|
||||
# damage_reduce=受伤减免〔背水/安全帽〕;bonus_delta=冒险加算区增量〔血税 双向——battle 末汇入
|
||||
# runner.bonus〕;blast_scaled=雷罚标定群伤〔陷阱爆发——值=事件伤害×mult〕;
|
||||
@@ -35,17 +34,15 @@ const EFFECT_KINDS := ["add_ticket_rider", "modify_ticket", "damage_mult", "open
|
||||
# target_attack_bonus=单位标记加伤〔处刑台——敌血量阈值挂单位字典,主角攻击面通用消费〕
|
||||
"heal", "ac_bonus", "damage_reduce", "bonus_delta", "blast_scaled", "preview_area",
|
||||
"target_attack_bonus",
|
||||
# 迭代 22 秘宝实装批 T3 扩签一型:mine_count_preview=危险格数量预告(巢穴预告——board_installed
|
||||
# mine_count_preview=危险格数量预告(巢穴预告——board_installed
|
||||
# 订阅 do;只报数量不标位置、不碰危险格分布=棋盘事实不可变契约;参数面常时 kind 不入本词表——
|
||||
# 它们无订阅行,schema 行内 effect_kinds 校验走 treasure_item_table 自有词表)
|
||||
"mine_count_preview",
|
||||
# 迭代 31 卡② 凭证件裁削批 T4 扩签一型:forgive_seal_miss=完美保险(本场首次封印落空不计入完美
|
||||
# forgive_seal_miss=完美保险(本场首次封印落空不计入完美
|
||||
# 封印判定——订阅 seal_missed 的豁免效果,消费点在授予段;feats.tres 首例 domain=battle 行)
|
||||
"forgive_seal_miss"]
|
||||
# 注:rewrite_board 已除名(迭代 20 T9——净化工兵随老 27 件退役,V14 连带清理;
|
||||
# 编排器消费分支 T1 先行摘除)
|
||||
|
||||
## fact 条件词汇(封闭枚举——迭代 20 T1 扩签 9→17:payload 同名键直读面〔fact 取值序 payload
|
||||
## fact 条件词汇(封闭枚举——payload 同名键直读面〔fact 取值序 payload
|
||||
## 优先——effect_engine._fact_value 既有机制〕。new_resource_type=首开资源类型〔地质学家〕;
|
||||
## is_edge_cell=边缘格〔边陲矿工〕;is_blank_zero=空白连锁〔跳格采掘〕;dug_under_seal=被挖格
|
||||
## 当时有封印〔破封一击〕;ap_exhausted_last=上轮 AP 耗尽〔一滴不剩〕;hp_ratio=生命比例〔背水
|
||||
@@ -54,13 +51,13 @@ const FACTS := ["matchup", "is_first_dig_of_battle", "terrain_kind", "target_arm
|
||||
"seals_count", "charge_layers", "nest_count", "round_index", "counter",
|
||||
"new_resource_type", "is_edge_cell", "is_blank_zero", "dug_under_seal",
|
||||
"ap_exhausted_last", "hp_ratio", "amount", "ap_left", "pct",
|
||||
"battle_index"] # 迭代 21 T3 扩签——关卡域战斗序(1 基,enc 携带;开局姿态组「每关首轮窗」条件面)
|
||||
"battle_index"] # 关卡域战斗序(1 基,enc 携带;开局姿态组「每关首轮窗」条件面)
|
||||
|
||||
## when 算子词汇(封闭——fact 查询面落法随批登记:mod_zero=计数器取模判满〔掘进蓄能〕、
|
||||
## when 算子词汇(封闭——fact 查询面落法:mod_zero=计数器取模判满〔掘进蓄能〕、
|
||||
## advantageous=克制矩阵正面判定〔淬毒刃纹〕、gte_ref/lt_ref=与另一计数器比较〔深挖钻头配额〕、
|
||||
## ends_with=后缀匹配〔对某甲族的泛条件〕、mod_eq=取模余数判定〔双管镐每第 2 次——value=模数
|
||||
## ref=余数;dispatch 预评估口径〔when 于命中前读计数器现值〕下「每第 N 次」系用 ref=N-1 表达〕。
|
||||
## 均系映射表现状条件装进 fact 的查询面落法,代拍板白名单覆盖,commit 留痕)
|
||||
## 均系映射表现状条件装进 fact 的查询面落法,白名单覆盖)
|
||||
const OPS := ["eq", "neq", "gt", "gte", "lt", "lte", "ends_with", "has", "mod_zero",
|
||||
"advantageous", "gte_ref", "lt_ref", "mod_eq"]
|
||||
|
||||
@@ -68,7 +65,7 @@ const OPS := ["eq", "neq", "gt", "gte", "lt", "lte", "ends_with", "has", "mod_ze
|
||||
const WHEN_KEYS := ["fact", "op", "value", "id", "ref", "value_from"]
|
||||
|
||||
## do 效果键白名单(trigger 行 do 字典键——零裸 flat 同族护栏:kind 外键全部参数引用面。
|
||||
## 迭代 20 T1 扩签四键:value_event=事件载荷取值键名〔爆破会计——counter_add 值=本轮挖格花费
|
||||
## value_event=事件载荷取值键名〔爆破会计——counter_add 值=本轮挖格花费
|
||||
## payload.ap_cost〕;value_counter=计数器取值键〔疼痛记忆/弃图兑换——attack rider 值=账面层值〕;
|
||||
## domain=计数器寿命域〔battle=战斗域重建清零〔缺省〕/adventure=跨战持久随 battle 末导出〕;
|
||||
## axis=bonus_delta 加算区轴向)
|
||||
@@ -78,15 +75,15 @@ const DO_KEYS := ["kind", "value", "value_from", "delta", "delta_from", "mult",
|
||||
"value_event", "value_counter", "domain", "axis"]
|
||||
|
||||
## 主动件分发通道(不经事件订阅——刷新/显影/移除是玩家主动动作;use_treasure 按 kind 分发)
|
||||
## 迭代 22 秘宝实装批 T3 扩签:+safe_dig 安全格直开(天眼——3×3 内安全格按普通挖格规则全额兑付
|
||||
## 含连锁展开与格面资源,危险格仅真视显示不移除——棋盘事实不可变契约;免行动点即效果本身,C5 裁)
|
||||
## safe_dig 安全格直开(天眼——3×3 内安全格按普通挖格规则全额兑付
|
||||
## 含连锁展开与格面资源,危险格仅真视显示不移除——棋盘事实不可变契约;免行动点即效果本身)
|
||||
const ACTIVE_KINDS := ["reveal_tiles", "preview", "grant_ap", "remove_mines", "refresh_pick", "safe_dig"]
|
||||
|
||||
# ---- 订阅集与计数器账本 ----
|
||||
var _triggers: Array[Variant] = [] # [{source, id, on, when, do}](装载序=声明序,重建时定死)
|
||||
var _counters: Dictionary[Variant, Variant] = {} # counter_id → int(battle 域账本;重建清零;重置点由订阅行声明)
|
||||
var _reset_on_round_start: Array[String] = [] # round_start 重置点计数器名单(装载时收集)
|
||||
var _adventure_counter_ids: Array[String] = [] # domain=adventure 计数器名单(T6——重建时收集、不清零;
|
||||
var _adventure_counter_ids: Array[String] = [] # domain=adventure 计数器名单(重建时收集、不清零;
|
||||
# battle 末 export → 冒险账本、begin_battle seed 回灌)
|
||||
var db: ConfigDb # 矩阵/表读取(advantageous 算子消费)——可空(纯计数器用法零依赖)
|
||||
|
||||
@@ -116,9 +113,9 @@ func rebuild(feat_ids: Array, item_ids: Array, tree_rules: Array = []) -> void:
|
||||
continue
|
||||
for tr in (row.get("triggers", []) as Array):
|
||||
_push_trigger("attune", iid, tr, row)
|
||||
# 树规则合成订阅(迭代 21 T3 换代——六键按 v0.8 三组双态):posture 两键=「每关首轮窗」语义
|
||||
# 〔battle_index==1 且 round_index==1;battle_index=关卡域战斗序由 enc 携带,原「每战首轮」
|
||||
# 实态随窗语义换代〕;sense 两组=每关限次〔adventure 域计数器 lt 上限闸 + 命中即 counter_add
|
||||
# 树规则合成订阅(六键按 v0.8 三组双态):posture 两键=「每关首轮窗」语义
|
||||
# 〔battle_index==1 且 round_index==1;battle_index=关卡域战斗序由 enc 携带〕;
|
||||
# sense 两组=每关限次〔adventure 域计数器 lt 上限闸 + 命中即 counter_add
|
||||
# 记账双行——同事件双行同预评估态见闸同记,序=装载序确定;生命周期四态:跨战持久=domain
|
||||
# adventure 导出/回灌、按关重置=AdventureRunner._start_current_level 清零、新冒险清零=账本
|
||||
# 归零、断点保留=断点快照随键〕;economy 两组零订阅行——消费点=GP 掉落乘算与商品计价折价的
|
||||
@@ -156,7 +153,7 @@ func rebuild(feat_ids: Array, item_ids: Array, tree_rules: Array = []) -> void:
|
||||
"when": [{"fact": "counter", "id": "tree_sense_guard_used", "op": "lt", "value": 2}],
|
||||
"do": {"kind": "counter_add", "counter_id": "tree_sense_guard_used",
|
||||
"value": 1, "domain": "adventure"}}, {})
|
||||
# 计数器账本重建(T6——battle 域清零照旧;adventure 域由 counters_seed 回灌〔rebuild 后调用〕)
|
||||
# 计数器账本重建(battle 域清零;adventure 域由 counters_seed 回灌〔rebuild 后调用〕)
|
||||
_counters = {}
|
||||
|
||||
|
||||
@@ -186,7 +183,7 @@ func _push_trigger(source: String, id: String, tr: Dictionary, row: Dictionary)
|
||||
var cid := String(do_d.counter_id)
|
||||
if not _reset_on_round_start.has(cid):
|
||||
_reset_on_round_start.append(cid)
|
||||
# 寿命域收集(T6——domain=adventure 计数器跨战持久:重建不清零、battle 末导出回冒险账本)
|
||||
# 寿命域收集(domain=adventure 计数器跨战持久:重建不清零、battle 末导出回冒险账本)
|
||||
if do_d.has("counter_id") and String(do_d.get("domain", "")) == "adventure":
|
||||
var aid := String(do_d.counter_id)
|
||||
if not _adventure_counter_ids.has(aid):
|
||||
@@ -245,7 +242,7 @@ func counter_spend(counter_id: String, value: int) -> bool:
|
||||
return true
|
||||
|
||||
|
||||
## adventure 域计数器导出(T6——battle 末调用,快照回冒险账本;仅 domain=adventure 名单内键)
|
||||
## adventure 域计数器导出(battle 末调用,快照回冒险账本;仅 domain=adventure 名单内键)
|
||||
func counters_export() -> Dictionary:
|
||||
var out := {}
|
||||
for cid in _adventure_counter_ids:
|
||||
@@ -253,7 +250,7 @@ func counters_export() -> Dictionary:
|
||||
return out
|
||||
|
||||
|
||||
## adventure 域计数器回灌(T6——rebuild 后由发射宿主调用:冒险账本值回填名单内键,缺省 0;
|
||||
## adventure 域计数器回灌(rebuild 后由发射宿主调用:冒险账本值回填名单内键,缺省 0;
|
||||
## 名单=本次 rebuild 装载行声明 domain=adventure 的 counter_id 集)
|
||||
func counters_seed(seed: Dictionary) -> void:
|
||||
for cid in _adventure_counter_ids:
|
||||
@@ -295,7 +292,7 @@ func _cond_ok(c: Dictionary, payload: Dictionary, facts: Callable) -> bool:
|
||||
return false
|
||||
return String(actual).ends_with(String(val))
|
||||
"has":
|
||||
# 在场判定(fact 查询面落法——代拍板白名单,commit 留痕):value 缺省=非空判定
|
||||
# 在场判定(fact 查询面落法——白名单覆盖):value 缺省=非空判定
|
||||
# (terrain_kind fact:伤害型变异集非空=挖开伤害型地形格/邻格有伤害型地形/地形伤种类在场);
|
||||
# value 给定=数组包含判定(保留泛用面)。
|
||||
if c.has("value"):
|
||||
@@ -304,7 +301,7 @@ func _cond_ok(c: Dictionary, payload: Dictionary, facts: Callable) -> bool:
|
||||
return false
|
||||
if actual is Array:
|
||||
return not (actual as Array).is_empty()
|
||||
return actual != null and String(actual) != "" # null 安全(迭代 20 T2 修正——敌技伤害载荷无 terrain_kind 时事实面回落 null,判「不在场」而非抛错;候选流变化后 devour 件首次被持有而暴露的潜伏缺陷)
|
||||
return actual != null and String(actual) != "" # null 安全(敌技伤害载荷无 terrain_kind 时事实面回落 null,判「不在场」而非抛错)
|
||||
"mod_zero":
|
||||
var m := int(val)
|
||||
return m > 0 and int(actual) % m == 0
|
||||
|
||||
+76
-80
@@ -7,7 +7,7 @@
|
||||
class_name LevelRunner
|
||||
extends RefCounted
|
||||
|
||||
signal node_entered(node: Dictionary, info: Dictionary) # info 按 type:battle/boss={} · reward={}(内容待拍板) · rest={healed,hp,max_hp} · shop={gp}
|
||||
signal node_entered(node: Dictionary, info: Dictionary) # info 按 type:battle/boss={} · reward={}(内容待定) · rest={healed,hp,max_hp} · shop={gp}
|
||||
signal choices_presented(candidates: Array) # 选路口(兑付确认后——候选节点引用数组,零自动推进)
|
||||
signal step_row(row: Dictionary) # 转发当前 战斗结算 结算行
|
||||
signal round_started(round_index: int)
|
||||
@@ -34,34 +34,33 @@ var gp: int: # GP 冒险钱包(局内货币—
|
||||
set(v): _ledger["gp"] = v
|
||||
var base_override := {"atk": 0, "max_hp": 0, "armor_class": 0} # 永久强化 flat 注入(元进度实现批——冒险模式 = AdventureRunner 共享字典经 start_level_external 注入;单关测试模式 = 私有全零)
|
||||
var _shop_purchased := {} # product_id → 已购档数(现价重算输入;冒险开始清零)
|
||||
# 协作对象私有(E1/E3 封装收口 2026-09-13):settlement 读取经 current_battle() 成文出口
|
||||
# 协作对象私有(E1/E3 封装收口):settlement 读取经 current_battle() 成文出口
|
||||
var _settlement: BattleOrchestrator = null # 当前战斗编排器(关卡结束置 null)
|
||||
var treasure_inventory: TreasureItems = null # 秘宝 关卡域库存(迭代 7 T3——TreasureItems;跨战斗延续,
|
||||
var _treasure_unlocked := {} # aux 解锁门控集(迭代 7 T5——拍板③甲;空集=零投放)
|
||||
var _attunement: Array[Variant] = [] # 同调装配集(迭代 11 第①批 T5——adventure params.attunement 下传,begin_battle enc 携带进战斗结算;空集=off 零扰动)
|
||||
var treasure_inventory: TreasureItems = null # 秘宝 关卡域库存(TreasureItems;跨战斗延续,
|
||||
var _treasure_unlocked := {} # aux 解锁门控集(空集=零投放)
|
||||
var _attunement: Array[Variant] = [] # 同调装配集(adventure params.attunement 下传,begin_battle enc 携带进战斗结算;空集=off 零扰动)
|
||||
# 关卡末清空+快照计数入 level_finished 载荷)
|
||||
# 两根类型轴(迭代 23 T4——adventure 参数面快照经 set_hero_axes 下传;空串=未注入→锻造配置默认值回退。
|
||||
# 两根类型轴(adventure 参数面快照经 set_hero_axes 下传;空串=未注入→锻造配置默认值回退。
|
||||
# 消费面:effective_ac 甲型基础 AC 层 + begin_battle enc 携带进 unit_container.spawn 英雄快照)
|
||||
var _hero_attack_type := ""
|
||||
var _hero_armor_type := ""
|
||||
# ---- 商店共享池模式态(迭代 11 第②批 T3——缺省 legacy=固定三件加定价 off 对照承载,零 RNG 零池消费;TP0 令牌池换代)----
|
||||
# ---- 商店共享池模式态(缺省 legacy=固定三件加定价 off 对照承载,零 RNG 零池消费;TP0 令牌池)----
|
||||
var _shop_pool_mode := false # pool 模式封印(set_shop_pool 注入时置位——AdventureRunner 按 params.shop_mode 下传)
|
||||
var _pool_deck: Array[Variant] = [] # 共享池令牌库共享引用(AdventureRunner._curio_deck——库存=剩余令牌数单点)
|
||||
var _pool_consume := Callable() # 池消费 handler(AdventureRunner._consume_pool_purchase——_apply_curio_pick 同口写加算区+账本+摘牌)
|
||||
var _pool_ticket := Callable() # 门票展示判定 handler(T5——AdventureRunner._feat_ticket_consider;节点与商店共享计数)
|
||||
var _pool_cap_check := Callable() # cap 检查回调(迭代 20 T2——AdventureRunner._curio_cap_ok;案②提前退役)
|
||||
var _pool_cap_check := Callable() # cap 检查回调(AdventureRunner._curio_cap_ok;cap 满剔出货架候选视图)
|
||||
var _shop_shelf: Array[Variant] = [] # 当前货架(进店抽 3/买后全重抽——令牌字典 {"kind","tier"} 序,同 kind 去重每 kind 至多一卡)
|
||||
var _shop_visit_buys := 0 # 本次进店已购次数(A 链定价输入——离开清零)
|
||||
var _shop_paid_refreshes := 0 # 本次进店已付费刷新次数(MC-1——GP 刷新价曲线输入,离开清零)
|
||||
|
||||
var hero_atk_pct := 0 # 测试面板覆盖值(切片专用)
|
||||
var hero_hp_pct := 0
|
||||
# ---- 关卡相位轴(2026-10-02 修复批——布尔封印收拢:_running/_awaiting_choice 两封印 → 显式 Phase enum;
|
||||
# 对齐 行动经济 Phase 先例(action_economy.gd:内部 enum + 守卫换算,外部读面契约零变化)——
|
||||
# level_result:String 与 awaiting_choice() 被 AdventureRunner/呈现层消费的语义逐字不变)----
|
||||
# ---- 关卡相位轴(显式 Phase enum:内部 enum + 守卫换算,对齐 行动经济 Phase 先例;
|
||||
# level_result:String 与 awaiting_choice() 是 AdventureRunner/呈现层消费的外部契约面)----
|
||||
enum Phase { IDLE, RUNNING, AWAITING_CHOICE, ENDED }
|
||||
var _phase: int = Phase.IDLE
|
||||
var level_result := "" # "" / "victory" / "defeat"(外部契约面——语义零变化)
|
||||
var level_result := "" # "" / "victory" / "defeat"(外部契约面)
|
||||
var _candidates: Array[Dictionary] = [] # 当前选路候选(节点引用数组)
|
||||
var visited: Array[Dictionary] = [] # 已兑付/到达节点序列(呈现层图条数据源;含当前节点)
|
||||
var _graph_mode := false # 图形态缓存(行集任一节点带 next;单链 = false)
|
||||
@@ -72,7 +71,7 @@ func _init(db_ref: ConfigDb, rng_ref: RngServiceCore) -> void:
|
||||
rng = rng_ref
|
||||
|
||||
|
||||
# 进行中谓词(原 _running 语义——RUNNING 或 AWAITING_CHOICE 两相位合计;内部守卫专用)
|
||||
# 进行中谓词(RUNNING 或 AWAITING_CHOICE 两相位合计;内部守卫专用)
|
||||
func _is_running() -> bool:
|
||||
return _phase == Phase.RUNNING or _phase == Phase.AWAITING_CHOICE
|
||||
|
||||
@@ -84,8 +83,8 @@ func set_hero_override(atk_pct: int, hp_pct: int) -> void:
|
||||
hero_hp_pct = hp_pct
|
||||
|
||||
|
||||
# 叠加次序(元进度实现批钉死):表值 → 永久强化 flat(base_override)→ 加算区 flat(bonus)先加
|
||||
# → 调试浮窗百分比后乘(调试数值工具恒乘最后——代拍板 6,与 GP 批既有次序兼容)
|
||||
# 叠加次序(钉死):表值 → 永久强化 flat(base_override)→ 加算区 flat(bonus)先加
|
||||
# → 调试浮窗百分比后乘(调试数值工具恒乘最后)
|
||||
func effective_atk() -> int:
|
||||
var base: int = int(db.hero.atk) + int(base_override.get("atk", 0)) + int(run_ledger.bonus.get("atk", 0))
|
||||
return maxi(1, int(round(float(base) * (100.0 + float(hero_atk_pct)) / 100.0)))
|
||||
@@ -96,7 +95,7 @@ func effective_max_hp() -> int:
|
||||
return maxi(1, int(round(float(base) * (100.0 + float(hero_hp_pct)) / 100.0)))
|
||||
|
||||
|
||||
# 护甲等级生效值(入战 AC 三层可加——迭代 23 T4:甲型基础 AC〔forge.tres 选中甲路线 base_ac〕+
|
||||
# 护甲等级生效值(入战 AC 三层可加:甲型基础 AC〔forge.tres 选中甲路线 base_ac〕+
|
||||
# 甲路线等级加值〔base_override.armor_class——hero_override 锻造分量〕+ 加算区 armor_class 轴;
|
||||
# 无调试浮窗百分比通道,纯 flat)
|
||||
func effective_ac() -> int:
|
||||
@@ -113,7 +112,7 @@ func _armor_base_ac() -> int:
|
||||
return int(row.get("base_ac", 0))
|
||||
|
||||
|
||||
# 行动点加成生效值(迭代 10 奖励重构批——统一冒险加算区第四轴 ap;经战斗开始入参 hero_ap_bonus
|
||||
# 行动点加成生效值(统一冒险加算区第四轴 ap;经战斗开始入参 hero_ap_bonus
|
||||
# 传入战斗域,兑现 = 每场战斗开始首轮行动点基线 +加成值〔行动经济 GDD 跨冒险行动点加成轴节〕)
|
||||
func effective_ap_bonus() -> int:
|
||||
return maxi(0, int(run_ledger.bonus.get("ap", 0)))
|
||||
@@ -128,10 +127,10 @@ func restore_hero_base() -> void:
|
||||
# ---- 关卡生命周期 ----
|
||||
|
||||
func start_level(level_index: int, seed_val: int, base_override_in: Dictionary = {}, template_pick: int = -1) -> bool:
|
||||
# 单关测试模式(旧路径——既有语义一字不改):私有账本、内置恢复数学、自调 new_adventure(漂移①
|
||||
# 单关测试模式:私有账本、内置恢复数学、自调 new_adventure(漂移①
|
||||
# 单关=冒险的历史简化在测试模式保留;正式种子域归 AdventureRunner.start_adventure)
|
||||
# base_override_in = 永久强化 flat 注入尾参(换算率标定批 2026-09-23——缺省 {} 全零白板,
|
||||
# 与既有调用方逐位一致零行为变更;契约同 AdventureRunner.start_adventure params.hero_override:
|
||||
# base_override_in = 永久强化 flat 注入尾参(缺省 {} 全零白板,
|
||||
# 契约同 AdventureRunner.start_adventure params.hero_override:
|
||||
# 三键 {atk, max_hp, armor_class}、缺键/-1 = 0、负值 clamp 0。游戏直入口仍恒白板——
|
||||
# 注入面只服务数值验证工具,非玩家可达路径)
|
||||
_external = false
|
||||
@@ -147,7 +146,7 @@ func start_level(level_index: int, seed_val: int, base_override_in: Dictionary =
|
||||
_candidates = []
|
||||
visited = []
|
||||
_graph_mode = false
|
||||
gp = 0 # 冒险钱包清零开局(GP+商店房实施批——局内货币生命周期起点)
|
||||
gp = 0 # 冒险钱包清零开局(局内货币生命周期起点)
|
||||
run_ledger.bonus = {"atk": 0, "max_hp": 0, "armor_class": 0} # 加算区冒险开始全 0(单关入口零元进度——注入面见上)
|
||||
base_override = {"atk": 0, "max_hp": 0, "armor_class": 0} # 永久强化 flat:缺省全零白板(单关入口零元进度——注入面见上)
|
||||
_shop_purchased = {} # 已购档数清零(跨冒险不延续——递增定价按冒险内累计)
|
||||
@@ -172,15 +171,15 @@ func start_level(level_index: int, seed_val: int, base_override_in: Dictionary =
|
||||
# 冒险结构接口①:开始关卡(载荷 = 关卡序号 + 当前生命 + 生效上限——生命经共享账本交接;
|
||||
# 种子域归冒险结构:本路径不重播种;账本归冒险结构:本路径不重置 gp/bonus/_shop_purchased,
|
||||
# 也不重置 hero_hp——账本当前值即开局生命,AdventureRunner 已在 ledger 写好)。
|
||||
# base_override = 永久强化 flat 共享引用(元进度实现批——缺省 {} 全零白板,与 B1 空参数路径逐位一致)。
|
||||
# base_override = 永久强化 flat 共享引用(缺省 {} 全零白板,与 B1 空参数路径同值)。
|
||||
# 流程与 start_level 同款(_find_level_row → 状态初始化 → _graph_mode 探测 → _enter_node(entry)),
|
||||
# 差异仅在账本/加算区/已购档/恢复执行体的注入与零重置。
|
||||
# 停点前移批 2026-09-23:defer_entry 尾参(缺省 false = 既有调用方零变更)——true 时不自动
|
||||
# defer_entry 尾参(缺省 false = 直接进节点)——true 时不自动
|
||||
# _enter_node(entry),改置选路态(awaiting_choice + 单候选 = 入口节点),由 AdventureRunner 在
|
||||
# level_started 之后调 present_entry_choice() 呈现、玩家 pick_next_node 显式进首战。拆两步的
|
||||
# 信号顺序契约:呈现层须先经 level_started 重取 runner 实例,choices_presented 才不会被
|
||||
# _on_choices_presented 的 runner 空防御静默丢弃。入口呈现零 RNG 消耗、pick 走同一个
|
||||
# _enter_node(entry)——与旧自动进入逐位等价(测量面零漂移红线)。
|
||||
# _enter_node(entry)——测量面零漂移红线。
|
||||
func start_level_external(level_index: int, ledger: Dictionary,
|
||||
purchased_ref: Dictionary, rest_handler: Callable, base_override_ref: Dictionary = {},
|
||||
defer_entry: bool = false, template_pick: int = -1, resume_state: Dictionary = {}) -> bool:
|
||||
@@ -205,7 +204,7 @@ func start_level_external(level_index: int, ledger: Dictionary,
|
||||
_graph_mode = true
|
||||
break
|
||||
_phase = Phase.RUNNING
|
||||
# 断点续玩路径(迭代 8 卡①——defer_entry 之外的第三种停点入口):直接重建到停点所在节点,
|
||||
# 断点续玩路径(defer_entry 之外的第三种停点入口):直接重建到停点所在节点,
|
||||
# 零 RNG 消耗(模板已随 template_pick 强制、节点状态纯重建不 _enter_node——当场战斗从头打)。
|
||||
# resume_state = {"node_id": int(-1 = 入口停点), "visited_ids": Array[int]};空字典 = 既有路径逐位不变。
|
||||
if not resume_state.is_empty():
|
||||
@@ -223,7 +222,7 @@ func start_level_external(level_index: int, ledger: Dictionary,
|
||||
return _enter_node(entry)
|
||||
|
||||
|
||||
# 入口停点呈现(停点前移批 2026-09-23——defer_entry=true 的第二步):发出入口候选供呈现层弹
|
||||
# 入口停点呈现(defer_entry=true 的第二步):发出入口候选供呈现层弹
|
||||
# 选路层;非停点态(缺省自动进入路径)拒绝。零 RNG 消耗——呈现与行进取号解耦。
|
||||
func present_entry_choice() -> bool:
|
||||
if _phase != Phase.AWAITING_CHOICE or _candidates.is_empty() or node_index != -1:
|
||||
@@ -233,7 +232,7 @@ func present_entry_choice() -> bool:
|
||||
return true
|
||||
|
||||
|
||||
# ---- 断点续玩(迭代 8 卡①——停点粒度重建)----
|
||||
# ---- 断点续玩(停点粒度重建)----
|
||||
|
||||
# 停点重建(start_level_external resume_state 路径的落点):node_id = 停点所在节点编号(-1 = 入口
|
||||
# 停点),visited_ids = 已到节点编号序(呈现层图条数据源重建)。纯状态重建零 RNG 消耗;
|
||||
@@ -284,7 +283,7 @@ func present_resumed_choice() -> bool:
|
||||
return true
|
||||
|
||||
|
||||
# 断点道具库存回填(迭代 8 卡①——奖励节点已选道具随停点快照回局;幂等,先清后入受理序重排)
|
||||
# 断点道具库存回填(奖励节点已选道具随停点快照回局;幂等,先清后入受理序重排)
|
||||
func restore_treasure_items(items: Array) -> void:
|
||||
if treasure_inventory == null:
|
||||
treasure_inventory = TreasureItems.new(db)
|
||||
@@ -293,14 +292,14 @@ func restore_treasure_items(items: Array) -> void:
|
||||
|
||||
|
||||
func _find_level_row(level_index: int) -> Dictionary:
|
||||
return db.level_row(level_index) # 表访问收口(2026-10-02——行集直扫归 LevelTable/ConfigDB 访问器)
|
||||
return db.level_row(level_index) # 表访问收口(行集直扫归 LevelTable/ConfigDB 访问器)
|
||||
|
||||
|
||||
# ---- 模板池解析(关卡节点图生成 v0.4——M-3 迭代 5 T5)----
|
||||
# ---- 模板池解析(关卡节点图生成 v0.4——M-3)----
|
||||
# row.templates 非空 = 池形态:template_pick >= 0 = 强制取该模板(首次脚本模式/冒烟固定图,零 RNG 消费);
|
||||
# -1 = 抽签(node_map 用途标签恰一 scope,均匀取模)。空池 = 旧单图形态原样返回(向后兼容零迁移)。
|
||||
# 解析产物 = row 副本换 nodes(下游 nodes()/校验/呈现全透明);active_template_name 供 MC 分层出数;
|
||||
# active_template_index 供断点快照备案(迭代 8 卡①——已抽模板续玩零重抽零 RNG 消耗)。
|
||||
# active_template_index 供断点快照备案(已抽模板续玩零重抽零 RNG 消耗)。
|
||||
var active_template_name := ""
|
||||
var active_template_index := -1 # -1 = 非池形态(旧单图)/未解析
|
||||
|
||||
@@ -427,7 +426,7 @@ func _enter_node(node: Dictionary) -> bool:
|
||||
"battle", "boss":
|
||||
return _start_battle(node)
|
||||
"reward":
|
||||
# 奖励点内容待拍板(格面资源批 2026-09-15:可选类道具删除,功能道具渠道 P1 未定——
|
||||
# 奖励点内容未定(可选类道具不在池、功能道具渠道未定——
|
||||
# 节点保留占位、零兑付直通);推进 = 兑付确认后选路(零自动推进)
|
||||
node_entered.emit(node, {})
|
||||
return true
|
||||
@@ -436,10 +435,10 @@ func _enter_node(node: Dictionary) -> bool:
|
||||
node_entered.emit(node, {"healed": healed, "hp": hero_hp, "max_hp": effective_max_hp()})
|
||||
return true
|
||||
"shop":
|
||||
# 商店房兑付(GP+商店房实施批 2026-09-21):到达即开店(覆盖层归呈现层),购买出账/
|
||||
# 商店房兑付:到达即开店(覆盖层归呈现层),购买出账/
|
||||
# 离开归玩家意图——零自动推进;离开确认经 continue_after_node 回选路口
|
||||
if _shop_pool_mode:
|
||||
_shop_open_pool() # 迭代 11 第②批 T3——进店抽 3(恰一 shop_draw scope;买后全重抽同签)
|
||||
_shop_open_pool() # 进店抽 3(恰一 shop_draw scope;买后全重抽同签)
|
||||
node_entered.emit(node, {"gp": gp, "shelf": true})
|
||||
return true
|
||||
node_entered.emit(node, {"gp": gp}) # legacy 固定三件(off 对照承载——零 RNG 零池消费)
|
||||
@@ -452,9 +451,9 @@ func _start_battle(node: Dictionary) -> bool:
|
||||
_settlement = BattleOrchestrator.new(db, rng)
|
||||
if treasure_inventory == null:
|
||||
treasure_inventory = TreasureItems.new(db) # 惰性建——关卡域单实例(跨战斗延续)
|
||||
_settlement.treasure_inventory = treasure_inventory # 迭代 7 T3——库存注入(授予路由+use_treasure 编排消费面)
|
||||
_settlement.treasure_inventory = treasure_inventory # 库存注入(授予路由+use_treasure 编排消费面)
|
||||
if _settlement != null:
|
||||
_settlement.set_treasure_unlocked(_treasure_unlocked) # 迭代 7 T5——解锁门控下传(装板摇号前)
|
||||
_settlement.set_treasure_unlocked(_treasure_unlocked) # 解锁门控下传(装板摇号前)
|
||||
# core↔core 接线只许方法 Callable(静态规则 S6——lambda 捕获 self 会延长
|
||||
# RefCounted 生命周期乃至成环:settlement 的信号持捕获 runner 的 lambda,runner 又持 settlement)
|
||||
_settlement.step_row.connect(_forward_step_row)
|
||||
@@ -462,14 +461,14 @@ func _start_battle(node: Dictionary) -> bool:
|
||||
_settlement.board_changed.connect(_forward_board_changed)
|
||||
_settlement.battle_finished.connect(_on_battle_finished)
|
||||
# node_entered 先于 begin_battle:呈现层须先拿到 settlement 引用,才能响应 begin 期间发出的
|
||||
# board_changed(首板安装)并重建棋盘——后发会导致首战棋盘不渲染(2026-09-11 playtest 缺陷①)
|
||||
# board_changed(首板安装)并重建棋盘——后发会导致首战棋盘不渲染
|
||||
node_entered.emit(node, {})
|
||||
if not _settlement.begin_battle({"level_index": int(level_row.level_index), "node_depth": int(node.get("node_depth", 0)),
|
||||
"hero_hp": hero_hp, "hero_atk": effective_atk(), "hero_max_hp": effective_max_hp(),
|
||||
"hero_armor_class": effective_ac(), "hero_ap_bonus": effective_ap_bonus(),
|
||||
"attack_type": _hero_attack_type, "armor_type": _hero_armor_type, # 迭代 23 T4——两根类型轴(spawn 英雄快照;空串=锻造配置默认值回退)
|
||||
"attack_type": _hero_attack_type, "armor_type": _hero_armor_type, # 两根类型轴(spawn 英雄快照;空串=锻造配置默认值回退)
|
||||
"attunement": _attunement,
|
||||
"ledger": run_ledger, # 迭代 27 T3——共享四账单类下传(原四散 dict 引用键收口;裁点②甲)
|
||||
"ledger": run_ledger, # 共享四账单类下传(单引用键)
|
||||
"feats": _feats,
|
||||
"tree_rules": _tree_rules,
|
||||
"battle_index": _battle_index(),
|
||||
@@ -478,7 +477,7 @@ func _start_battle(node: Dictionary) -> bool:
|
||||
"node_type": String(node.get("type", ""))}):
|
||||
push_error("[关卡节点地图] 战斗启动失败(depth %s)" % str(node.get("node_depth", -1)))
|
||||
return false
|
||||
_voucher_rap = 0 # 迭代 20 T4——节点级一次性:注入即消费(战斗未启动则保留至下次注入前不清)
|
||||
_voucher_rap = 0 # 节点级一次性:注入即消费(战斗未启动则保留至下次注入前不清)
|
||||
_voucher_time_sand = 0
|
||||
return true
|
||||
|
||||
@@ -489,7 +488,7 @@ func _forward_step_row(row: Dictionary) -> void:
|
||||
|
||||
# battle_index=第几场战斗(关卡域 1 基序——visited 已含当前节点〔_enter_node :437 先追加后启动〕;
|
||||
# 按已到节点 type=battle 计数,与图形态 node_depth/单链数组下标两编号空间解耦〔混编碰撞免疫〕;
|
||||
# 开局姿态组「每关首轮窗」条件面——迭代 21 T3)
|
||||
# 开局姿态组「每关首轮窗」条件面)
|
||||
func _battle_index() -> int:
|
||||
var n := 0
|
||||
for v in visited:
|
||||
@@ -524,7 +523,7 @@ func _on_battle_finished(payload: Dictionary) -> void:
|
||||
var treasure_cleared := 0
|
||||
if treasure_inventory != null:
|
||||
treasure_cleared = treasure_inventory.inventory_count() # 快照计数(清空前——「剩余道具随关卡结束清空」可见反馈取数)
|
||||
treasure_inventory.reset() # 迭代 7 T3——关卡末库存清空(无论输赢,幂等)
|
||||
treasure_inventory.reset() # 关卡末库存清空(无论输赢,幂等)
|
||||
level_finished.emit({"result": level_result, "hp": hp_left, "node_index": node_index,
|
||||
"node_id": _node_id(node), "node_type": String(node.get("type", "")),
|
||||
"aux_cleared": treasure_cleared}) # additive 扩签(不参与派生——node_map 扩签先例)
|
||||
@@ -534,8 +533,8 @@ func _on_battle_finished(payload: Dictionary) -> void:
|
||||
_present_choices()
|
||||
|
||||
|
||||
# 恢复数学单一路径(漂移③清偿;2026-10-02 修复批——同式双落点收口:算术宿主 = 本静态方法,
|
||||
# AdventureRunner.apply_rest 同式委托,字面量双写退役)。floor(上限 × 比例 ÷ 100) 后封顶缺口;
|
||||
# 恢复数学单一路径(漂移③清偿;算术宿主 = 本静态方法,
|
||||
# AdventureRunner.apply_rest 同式委托)。floor(上限 × 比例 ÷ 100) 后封顶缺口;
|
||||
# 冒险结构 GDD 公式逐位一致
|
||||
static func rest_heal(pct: int, hp: int, max_hp: int) -> int:
|
||||
return mini(int(floor(float(max_hp) * float(pct) / 100.0)), max_hp - hp)
|
||||
@@ -585,19 +584,19 @@ func pick_next_node(node_id: int) -> bool:
|
||||
func current_battle():
|
||||
return _settlement
|
||||
|
||||
# aux 解锁门控集注入(迭代 7 T5——AdventureRunner 自 params 传递)
|
||||
# aux 解锁门控集注入(AdventureRunner 自 params 传递)
|
||||
func set_treasure_unlocked(ids: Dictionary) -> void:
|
||||
_treasure_unlocked = ids
|
||||
if _settlement != null:
|
||||
_settlement.set_treasure_unlocked(ids)
|
||||
|
||||
|
||||
# 同调装配集注入(迭代 11 第①批 T5——冒险开始参数面;AdventureRunner 唯一调用方)
|
||||
# 同调装配集注入(冒险开始参数面;AdventureRunner 唯一调用方)
|
||||
func set_attunement(ids: Array) -> void:
|
||||
_attunement = ids.duplicate()
|
||||
|
||||
|
||||
# 特性持有集注入(迭代 12 T1——AdventureRunner._start_current_level 唯一调用方;begin_battle
|
||||
# 特性持有集注入(AdventureRunner._start_current_level 唯一调用方;begin_battle
|
||||
# enc.feats 携带进战斗求值;**共享引用不复制**——关内奖励节点兑付的 relic 须当关后续战斗生效
|
||||
# (attune_charges 共享账本同款);空集=零扰动)
|
||||
var _feats: Array[Variant] = []
|
||||
@@ -607,12 +606,12 @@ func set_feats(ids: Array) -> void:
|
||||
_feats = ids
|
||||
|
||||
|
||||
# 特性持有集读面(迭代 12 呈现补齐——HUD C 区特性段数据源)
|
||||
# 特性持有集读面(HUD C 区特性段数据源)
|
||||
func feats_held() -> Array:
|
||||
return _feats.duplicate()
|
||||
|
||||
|
||||
# 树 rule 键集注入(迭代 12 T10——begin_battle enc.tree_rules 携带;Fork 激活侧 effects)
|
||||
# 树 rule 键集注入(begin_battle enc.tree_rules 携带;Fork 激活侧 effects)
|
||||
var _tree_rules: Array[Variant] = []
|
||||
|
||||
|
||||
@@ -620,16 +619,16 @@ func set_tree_rules(rules: Array) -> void:
|
||||
_tree_rules = rules.duplicate()
|
||||
|
||||
|
||||
# 两根类型轴注入(迭代 23 T4——adventure 参数面快照;空串=未注入→锻造配置默认值回退。
|
||||
# 两根类型轴注入(adventure 参数面快照;空串=未注入→锻造配置默认值回退。
|
||||
# 消费面:effective_ac 甲型基础 AC 层 + begin_battle enc 携带进 unit_container.spawn 英雄快照)
|
||||
func set_hero_axes(attack_type: String, armor_type: String) -> void:
|
||||
_hero_attack_type = attack_type
|
||||
_hero_armor_type = armor_type
|
||||
|
||||
|
||||
# 共享四本账注入(迭代 27 T3——P1-4 收口:RunLedger 单类下传,替代原 bonus/attune_charges/
|
||||
# vision_counter/adventure_counters 四散引用;战斗侧只走账类原语,战末 export/开局 seed
|
||||
# 时点语义不变〔裁点②甲〕)
|
||||
# 共享四本账注入(RunLedger 单类下传,含 bonus/attune_charges/
|
||||
# vision_counter/adventure_counters 四账;战斗侧只走账类原语,战末 export/开局 seed
|
||||
# 时点语义不变)
|
||||
var run_ledger: RunLedger = RunLedger.new()
|
||||
|
||||
|
||||
@@ -637,7 +636,7 @@ func set_run_ledger(l: RunLedger) -> void:
|
||||
run_ledger = l
|
||||
|
||||
|
||||
# 凭证件三账注入(迭代 20 T4——储备金袋 RAP/时沙 首轮 AP/完美保险 标错豁免;节点级一次性,
|
||||
# 凭证件三账注入(储备金袋 RAP/时沙 首轮 AP/完美保险 标错豁免;节点级一次性,
|
||||
# battle begin 消费后归零;缺省 0=零扰动)
|
||||
var _voucher_rap := 0
|
||||
var _voucher_time_sand := 0
|
||||
@@ -648,10 +647,9 @@ func set_voucher_grants(rap: int, time_sand: int) -> void:
|
||||
_voucher_time_sand = maxi(0, time_sand)
|
||||
|
||||
|
||||
# 同调主动件充能账骑库存面注入(迭代 11 第①批 T6 原语义 + 迭代 27 T3 换代——账本住
|
||||
# RunLedger;能力件骑库存面注入保留:差额注入防续玩双份——resume 路径 restore_inventory
|
||||
# 已含断点内 attune 记录。原 set_attune_charges/set_vision_counter/set_adventure_counters
|
||||
# 三散 setter 随单类下传承接)
|
||||
# 同调主动件充能账骑库存面注入(账本住
|
||||
# RunLedger;差额注入防续玩双份——resume 路径 restore_inventory
|
||||
# 已含断点内 attune 记录)
|
||||
func sync_attune_inventory() -> void:
|
||||
var charges: Dictionary[Variant, Variant] = run_ledger.attune_charges
|
||||
if treasure_inventory == null:
|
||||
@@ -664,7 +662,7 @@ func sync_attune_inventory() -> void:
|
||||
for i in range(maxi(0, int(charges[iid]) - existing)):
|
||||
treasure_inventory.add_item(String(iid), "attunement", Vector2i(-1, -1), 0)
|
||||
|
||||
# 三选一 GP 袋件入账(迭代 10——钱包收口扩「奖励入账」入口;复用战斗完成同一入账原语,
|
||||
# 三选一 GP 袋件入账(钱包收口的「奖励入账」入口;复用战斗完成同一入账原语,
|
||||
# wallet_changed 载荷带 source 供呈现归因)
|
||||
func grant_curio_gp(amount: int) -> void:
|
||||
gp = maxi(0, gp + amount)
|
||||
@@ -680,13 +678,13 @@ func continue_after_node() -> bool:
|
||||
if t != "reward" and t != "rest" and t != "shop":
|
||||
return false
|
||||
if t == "shop":
|
||||
_shop_shelf = [] # 离开店面货架态清零(断点快照 shop_shelf 键随离开落空架合法态——迭代 11 第②批 T2)
|
||||
_shop_shelf = [] # 离开店面货架态清零(断点快照 shop_shelf 键随离开落空架合法态)
|
||||
_shop_visit_buys = 0
|
||||
_shop_paid_refreshes = 0 # MC-1
|
||||
return _present_choices()
|
||||
|
||||
|
||||
# ---- GP 冒险钱包(GP+商店房实施批 2026-09-21:入账/出账/收束清零三收口)----
|
||||
# ---- GP 冒险钱包(入账/出账/收束清零三收口)----
|
||||
|
||||
# 战斗完成入账(gp_dropped 载荷——clamp ≥0;破财可把战斗域 escrow 打负,钱包不透支)
|
||||
func _gain_wallet(amount: int) -> void:
|
||||
@@ -719,16 +717,15 @@ func shop_products() -> Array:
|
||||
|
||||
|
||||
# 现价 = base_price + price_level_bonus(level_index) + price_increment × 该商品已购档数
|
||||
# (散折价随批:economy_spend 激活时商品标价 ×80%——见 _spend_discounted)
|
||||
# (散折价:economy_spend 激活时商品标价 ×80%——见 _spend_discounted)
|
||||
func current_price(prow: Dictionary) -> int:
|
||||
return _spend_discounted(int(prow.get("base_price", 0)) + db.shop_price_level_bonus(int(level_row.level_index)) \
|
||||
+ int(prow.get("price_increment", 0)) * purchased_tier(String(prow.get("product_id", ""))))
|
||||
|
||||
|
||||
# 散折价单点(迭代 21 T3——财富之道·散 economy_spend 激活时商品购买价 ×80% provisional,
|
||||
# 散折价单点(财富之道·散 economy_spend 激活时商品购买价 ×80% provisional,
|
||||
# floor 取整偏向玩家、下限 1 防 1 元商品折成 0;范围=商品标价定义边界〔legacy 商品面 current_price
|
||||
# + 池货架面 pool_price 同折〕——商店刷新价 shop_refresh_cost 与三选一付费刷新不折〔卡片§11②裁决〕;
|
||||
# 折率归 M4 数值批)
|
||||
# + 池货架面 pool_price 同折〕——商店刷新价 shop_refresh_cost 与三选一付费刷新不折)
|
||||
func _spend_discounted(price: int) -> int:
|
||||
if _tree_rules.has("economy_spend"):
|
||||
return maxi(1, int(floor(float(price) * 0.8)))
|
||||
@@ -753,7 +750,7 @@ func buy_product(product_id: String) -> bool:
|
||||
var price := current_price(prow)
|
||||
if gp < price:
|
||||
push_warning("[关卡节点地图] 购买拒绝——余额不足: %s(需 %d,钱包 %d)" % [product_id, price, gp])
|
||||
return false # 余额不足(覆盖层按钮禁用为常态防线——此处兜底拒绝;2026-10-02 补静默留痕)
|
||||
return false # 余额不足(覆盖层按钮禁用为常态防线——此处兜底拒绝+静默留痕)
|
||||
gp -= price
|
||||
var flat := int(prow.get("flat_value", 0))
|
||||
run_ledger.bonus[attr] = int(run_ledger.bonus[attr]) + flat
|
||||
@@ -770,11 +767,11 @@ func player_request_refresh() -> bool:
|
||||
return _settlement.player_request_refresh()
|
||||
|
||||
|
||||
# ---- 商店共享池模式(迭代 11 第②批 T3——P1 共享池/P4 ceil;legacy 固定三件路径上方原样保留为 off 对照承载)----
|
||||
# ---- 商店共享池模式(legacy 固定三件路径保留为 off 对照承载)----
|
||||
|
||||
# 注入(AdventureRunner._start_current_level 唯一调用方——pool 模式专用):令牌库共享引用 +
|
||||
# 池消费 handler。legacy 模式不注入(_shop_pool_mode 恒 false——off 态零 RNG 零池消费零扰动)。
|
||||
# 迭代 20 T2——cap 检查回调(案②提前退役:cap 满 kind 剔出货架候选视图)。
|
||||
# cap 检查回调(cap 满 kind 剔出货架候选视图)。
|
||||
func set_shop_pool(deck_ref: Array, consume_handler: Callable, ticket_consider: Callable = Callable(), cap_check: Callable = Callable()) -> void:
|
||||
_pool_deck = deck_ref
|
||||
_pool_consume = consume_handler
|
||||
@@ -801,7 +798,7 @@ func _shop_draw_shelf() -> void:
|
||||
for row in db.shop_pool_rows():
|
||||
var shelf_kind := String(row.get("kind_id", ""))
|
||||
if _pool_cap_check.is_valid() and not bool(_pool_cap_check.call(shelf_kind, "")):
|
||||
continue # 迭代 20 T2 案②——cap 满提前退役(货架候选视图剔除;牌库本体不动)
|
||||
continue # cap 满剔除(货架候选视图剔除;牌库本体不动)
|
||||
for t in _pool_deck:
|
||||
var tok: Dictionary[Variant, Variant] = t as Dictionary
|
||||
if String(tok.get("kind", "")) == shelf_kind:
|
||||
@@ -826,10 +823,9 @@ func _shop_draw_shelf() -> void:
|
||||
# T5 门票注入(非 Boss 渠道唯一专长入口——货架与节点共享保底计数;判定含 pity 记账,替换末位占一席。
|
||||
# 取数复用本函数 shop_draw scope 的 stream——零新 scope,确定性序列不漂移)
|
||||
if _pool_ticket.is_valid() and not _shop_shelf.is_empty() and bool(_pool_ticket.call(stream)):
|
||||
_shop_shelf[_shop_shelf.size() - 1] = {"kind": "feat_ticket", "tier": "excellent"} # 迭代 20 T2——档名四级换代(旧 rare 语义=优秀)
|
||||
# 商店保底退役(迭代 15 卡② T4——渠道轴定位裁决:商店渠道只出属性值提升+资源,专长在非 Boss
|
||||
# 渠道只以门票形态出现(奇物.md v2.7/A4 断言)。迭代 12 T6「货架零特性挤卡换吞噬流件」简化口径
|
||||
# 与渠道白名单直接冲突(devour=规则改写型=专长系),随渠道分离退役;专长商店入口=T5 门票。
|
||||
_shop_shelf[_shop_shelf.size() - 1] = {"kind": "feat_ticket", "tier": "excellent"} # 门票占位档名 = excellent
|
||||
# 商店渠道无专长直接投放(渠道白名单:商店渠道只出属性值提升+资源,专长在非 Boss
|
||||
# 渠道只以门票形态出现——奇物.md v2.7/A4 断言;专长商店入口=T5 门票)。
|
||||
|
||||
|
||||
# 库存余量(TP0——令牌库内该 kind 剩余枚数单点读;未注入/防御读 = 0)
|
||||
@@ -857,7 +853,7 @@ func pool_price(kind: String) -> int:
|
||||
|
||||
|
||||
# 货架只读投影(呈现层数据源——kind/显示名/件值/现价/库存余量齐备,UI 零复算)。
|
||||
# 件值=在架令牌本体值(TP0——令牌级货架,kind+tier 定值;价格公式维持批②裁定 tier-blind)。
|
||||
# 件值=在架令牌本体值(TP0——令牌级货架,kind+tier 定值;价格公式 tier-blind〔不随档位变〕)。
|
||||
func shop_shelf() -> Array:
|
||||
var out: Array[Dictionary] = []
|
||||
for tok in _shop_shelf:
|
||||
@@ -882,16 +878,16 @@ func shop_shelf() -> Array:
|
||||
return out
|
||||
|
||||
|
||||
# 商店独立刷新(MC-1——P-C2 裁 2026-10-04 推翻 v1.5「无独立刷新」):免费次数(天赋增益位,基础
|
||||
# knob=0——Q2 裁「基础免费 0 次」)用尽后 GP 付费刷新;刷新=货架整架重抽(shop_draw 恰一新 scope,
|
||||
# 商店独立刷新(MC-1):免费次数(天赋增益位,基础
|
||||
# knob=0=「基础免费 0 次」)用尽后 GP 付费刷新;刷新=货架整架重抽(shop_draw 恰一新 scope,
|
||||
# 与进店/买后重抽同签——重抽同源共享池);池空/非池模式=不可刷新合法态。
|
||||
func shop_refresh_free_left() -> int:
|
||||
# 迭代 22 秘宝批 T3——刷新执照 shop_refresh_bonus 装配合计叠加(进店入账语义=读数即本店可用,
|
||||
# 与基础 knob 同生命周期;未装配=基值逐位同旧)
|
||||
# 刷新执照 shop_refresh_bonus 装配合计叠加(进店入账语义=读数即本店可用,
|
||||
# 与基础 knob 同生命周期;未装配=基值)
|
||||
return maxi(0, int(db.adventure.shop_refresh_free) + _attune_shop_refresh_bonus())
|
||||
|
||||
|
||||
# 刷新执照装配合计(迭代 22 秘宝批 T3——装配集 kind 分发扫描;零装配=0)
|
||||
# 刷新执照装配合计(装配集 kind 分发扫描;零装配=0)
|
||||
func _attune_shop_refresh_bonus() -> int:
|
||||
var total := 0
|
||||
for iid_v in _attunement:
|
||||
@@ -919,7 +915,7 @@ func shop_refresh() -> bool:
|
||||
return true
|
||||
|
||||
|
||||
# GP 冒险钱包单口支出(2026-10-06——三选一 GP 付费刷新与商店刷新共用同一钱包;余额不足 false 零副作用)
|
||||
# GP 冒险钱包单口支出(三选一 GP 付费刷新与商店刷新共用同一钱包;余额不足 false 零副作用)
|
||||
func spend_gp(amount: int) -> bool:
|
||||
if amount <= 0 or gp < amount:
|
||||
return false
|
||||
|
||||
@@ -9,7 +9,7 @@ var db: ConfigDb
|
||||
var save: SaveManagerCore
|
||||
|
||||
|
||||
# 迭代 27 T1 裁点①——MetaProgression 升格 autoload(RngService 先例:类名加 Core 后缀、
|
||||
# MetaProgression 是 autoload(仿 RngService 先例:类名加 Core 后缀、
|
||||
# autoload 占净名)。_init 参数可缺省兼容双形态:autoload 实例化走 .new() 零参 + _ready 从
|
||||
# autoload 链取依赖(工程序 ConfigDB→RngService→SaveManager→MetaProgression,_ready 时序
|
||||
# 已就绪);单测/工具直呼 .new(db, save) 形态不变(不入树零 _ready)。
|
||||
@@ -29,11 +29,9 @@ func keystone_balance() -> int:
|
||||
return save.keystone_balance()
|
||||
|
||||
|
||||
# ---- 秘宝解锁服务(迭代 22 T2 换代——解锁混合制,契约 6;旧工坊渠道面随批删除)----
|
||||
# 〔属性锻造柜台退役——迭代 21 T4/T5:buy_forge/forge_flat/forge_level/forge_price 与 meta_forge
|
||||
# 表族随攻防所有权切分删除。〕〔道具工坊解锁渠道面退役——迭代 22 T2:旧「总通关次数达阈值」
|
||||
# 行族与 _workshop_progress/workshop_status/refresh_workshop_unlocks 删除,解锁条件单一事实源=
|
||||
# treasure_items.tres 行内 unlock_keystone_cost/unlock_requires 谓词(C1 逐件分配锁定表)。〕
|
||||
# ---- 秘宝解锁服务(解锁混合制,契约 6)----
|
||||
# 解锁条件单一事实源=treasure_items.tres 行内 unlock_keystone_cost/unlock_requires 谓词
|
||||
# (C1 逐件分配锁定表)。
|
||||
|
||||
# 道具解锁读数访问器(组合根/呈现层解锁门控查询面——禁再穿透 meta.save 直读 save_manager)
|
||||
func is_item_unlocked(item_id: String) -> bool:
|
||||
@@ -123,11 +121,11 @@ func treasure_shop_status() -> Array:
|
||||
return out
|
||||
|
||||
|
||||
# ---- 锻造工坊写口族(迭代 23 T3——契约 9:扣币/升级/解锁/选中/写盘只经本组;UI 只发意图信号)----
|
||||
# ---- 锻造工坊写口族(契约 9:扣币/升级/解锁/选中/写盘只经本组;UI 只发意图信号)----
|
||||
# 设计权威 design/gdd/systems/跨局循环/锻造工坊.md v0.6:钥石单币(契约 3——GP 局内一次性货币
|
||||
# 永不互兑)、并联双通道解锁(4b-1——练满前一档 OR 一次性解锁价,互不影响不设串联)、
|
||||
# 一次冒险一武器一甲(契约 8——营地免费切换、冒险开始锁死由参数面快照保证)。
|
||||
# 等级语义(Q1):未解锁/未锻造=0 加值(裸体态);锻造第一级=「获得装备」=首个 +1;逐级递进。
|
||||
# 等级语义:未解锁/未锻造=0 加值(裸体态);锻造第一级=「获得装备」=首个 +1;逐级递进。
|
||||
|
||||
# 路线解锁态派生(不落档——初始持有∨直购∨练满前一档;派生单点只此一处)
|
||||
func forge_route_unlocked(route_id: String) -> bool:
|
||||
@@ -231,7 +229,7 @@ func hero_armor_type() -> String:
|
||||
return String(row.get("type_axis", ""))
|
||||
|
||||
|
||||
# ---- 锻造工坊柜台只读投影(迭代 23 T6——呈现层数据源:两区六路线行板;余额联动禁用归视图)----
|
||||
# ---- 锻造工坊柜台只读投影(呈现层数据源:两区六路线行板;余额联动禁用归视图)----
|
||||
# 每行 {route_id, display_name, kind, type_axis, level, cap, unlocked, initial_owned, selected,
|
||||
# next_price(未满级已解锁=下级几何价/否则 0), unlock_price, prereq_route, prereq_name,
|
||||
# prereq_level, prereq_cap, prereq_maxed}——纯结构面,禁嵌余额判断(视图按 balance 禁用,同秘宝匣先例)。
|
||||
@@ -271,16 +269,16 @@ func forge_shop_status() -> Array:
|
||||
# ---- Boss 两档与收束入账(结算屏文案数据源)----
|
||||
# 明细 {big_reward, boss_drops, total, new_unlocks};入账与标记经 save.apply_adventure_summary
|
||||
# (keystone_drops 副本随摘要入 recent_summaries)。
|
||||
# 迭代 10 奖励重构批:钥石改 Boss 逐杀掉落——掉落金额已在冒险内逐杀入账(summary.keystone_total
|
||||
# 含全部掉落;首杀/复杀裁决在 AdventureRunner._boss_keystone_drop,首杀标记仍在下方
|
||||
# apply_adventure_summary 逐通关关次置位)。本函数不再派生掉落金额,只做标记+入账+归档。
|
||||
# 钥石为 Boss 逐杀掉落——掉落金额已在冒险内逐杀入账(summary.keystone_total
|
||||
# 含全部掉落;首杀/复杀判定在 AdventureRunner._boss_keystone_drop,首杀标记在下方
|
||||
# apply_adventure_summary 逐通关关次置位)。本函数只做标记+入账+归档。
|
||||
func settle_adventure(summary: Dictionary) -> Dictionary:
|
||||
# 首次冒险布尔位置位(关卡节点图生成 v0.4——完成/失败/放弃三结局同经本收束挂点,写入只在收束)
|
||||
save.mark_first_adventure_done()
|
||||
var drops: Array[Variant] = (summary.get("keystone_drops", []) as Array).duplicate(true)
|
||||
save.apply_adventure_summary(summary, drops)
|
||||
save.add_vision_used(int(summary.get("vision_used_delta", 0))) # 迭代 22 T3——真视使用计数收束聚合入档(信标网络行为条件)
|
||||
var new_unlocks := refresh_treasure_unlocks() # 迭代 22 T2——条件解锁刷新换代(旧工坊面退役)
|
||||
save.add_vision_used(int(summary.get("vision_used_delta", 0))) # 真视使用计数收束聚合入档(信标网络行为条件)
|
||||
var new_unlocks := refresh_treasure_unlocks() # 条件解锁刷新
|
||||
save.write_to_disk()
|
||||
var total := int(summary.get("keystone_total", 0))
|
||||
return {
|
||||
@@ -292,13 +290,12 @@ func settle_adventure(summary: Dictionary) -> Dictionary:
|
||||
|
||||
|
||||
# ---- 跨局注入(冒险开始参数面)----
|
||||
# 迭代 23 T4 注入公式换代(锻造工坊实装批——契约 1 攻防所有权切分落地):基础值 + 树生命/命中淬炼
|
||||
# flat(命中线在账未注入——契约 6,消费面归数值批)+ **锻造路线加值**(当前选中武器攻击加值 + 当前甲
|
||||
# 注入公式(契约 1 攻防所有权切分):基础值 + 树生命/命中淬炼
|
||||
# flat(命中线在账未注入——契约 6)+ **锻造路线加值**(当前选中武器攻击加值 + 当前甲
|
||||
# AC 加值——读存档 forge_routes 域+路线表)+ 局内加算区(bonus 区域不经本面)。入战 AC 三层可加:
|
||||
# 甲型基础 AC(forge.tres 行 base_ac)+ 甲路线等级加值(本面 armor_class 分量)+ 局内加算区。
|
||||
# 旧属性柜台 forge_flat 项退役移除(V7/V12);攻/AC 树来源随所有权切分砍除(契约 9——树零攻防来源,
|
||||
# 现表零 atk/armor_class 行,tree_stat_flat 二轴恒 0 保留通式)。原 FORGE_ROUTE_BONUS 预留常量随本批
|
||||
# 退役(公式不再出现——V7/V12)。
|
||||
# 攻/AC 树来源按所有权切分砍除(契约 9——树零攻防来源,
|
||||
# 现表零 atk/armor_class 行,tree_stat_flat 二轴恒 0 保留通式)。
|
||||
# 全零 = 白板合法态(无解锁不得改变任何局内规则);override 禁写表(表运行期只读)。
|
||||
|
||||
func hero_override() -> Dictionary:
|
||||
@@ -309,7 +306,7 @@ func hero_override() -> Dictionary:
|
||||
}
|
||||
|
||||
|
||||
# ---- 天赋树段(迭代 12 T8——R1 甲融合案;设计权威 整合定稿 §1.4 一树双币三脉一脊)----
|
||||
# ---- 天赋树段(设计权威 整合定稿 §1.4 一树双币三脉一脊)----
|
||||
|
||||
# 全树已购总等级(row-gating 输入——「投入点数累计解锁新行」)
|
||||
func tree_spent_total() -> int:
|
||||
@@ -333,15 +330,15 @@ func tree_can_buy(node_id: String) -> bool:
|
||||
# 刷新升级节点:cap 主语=Y 账本(单一事实源——树上入口与装配段同 cap 同价,防双账漂移)
|
||||
if node_id == "ks_refresh" and db.meta_attune_refresh_row(save.attune_refresh_level() + 1).is_empty():
|
||||
return false
|
||||
# 行门槛(一线一节点:脊柱节点第 N 级需全树总投入 ≥4×(N−1)——迭代 21 主语平移,row 字段退役)
|
||||
# 行门槛(一线一节点:脊柱节点第 N 级需全树总投入 ≥4×(N−1))
|
||||
if String(row.get("vein", "")) == "spine" and String(row.get("kind", "")) == "stat":
|
||||
if tree_spent_total() < db.meta_tree.level_gate(lvl + 1):
|
||||
return false
|
||||
for req in (row.get("requires", []) as Array):
|
||||
if save.tree_level(String(req)) < 1:
|
||||
return false
|
||||
# 等级阈值前置(R1 机制位——requires_level {node_id: n}:前置节点等级 ≥ n 才可购;
|
||||
# 2026-10-13 用户推翻裁撤案随批重立,现役表零配置=空转零行为影响)
|
||||
# 等级阈值前置(requires_level {node_id: n}:前置节点等级 ≥ n 才可购;
|
||||
# 现役表零配置=空转零行为影响)
|
||||
var req_lv: Dictionary[Variant, Variant] = row.get("requires_level", {}) as Dictionary
|
||||
for req_id in req_lv.keys():
|
||||
if save.tree_level(String(req_id)) < int(req_lv[req_id]):
|
||||
@@ -349,9 +346,9 @@ func tree_can_buy(node_id: String) -> bool:
|
||||
return true
|
||||
|
||||
|
||||
# 节点购买(营地树 UI 唯一写口):树全钥石单币(2026-10-07 用户裁甲)。free=工作间钥匙零币点亮
|
||||
# 节点购买(营地树 UI 唯一写口):树全钥石单币。free=工作间钥匙零币点亮
|
||||
# (price 0,零额消费无副作用);彩蛋不经购买(can_buy 已拒——觉醒唯一入口=_check_easter_egg)。
|
||||
# 刷新升级节点走同账路由(Q1 裁乙——树上+秘宝匣装配段双入口写同一 attune_refresh_level 账本,
|
||||
# 刷新升级节点走同账路由(树上+秘宝匣装配段双入口写同一 attune_refresh_level 账本,
|
||||
# 价格/上限单一事实源=Y 行表值 keystone_cost;V15)。
|
||||
func tree_buy(node_id: String) -> bool:
|
||||
if node_id == "ks_refresh":
|
||||
@@ -479,7 +476,7 @@ func tree_fork_groups() -> Array:
|
||||
|
||||
|
||||
# 彩蛋觉醒检查(特化判定——生命/命中两线拉满 且 同调位 1/2/3 全解锁 → easter_ley 免费点亮;
|
||||
# 迭代 21 T2 换代:不依赖 requires/requires_level 前置形态,发现率监控呈报面沿袭)
|
||||
# 不依赖 requires/requires_level 前置形态)
|
||||
func _check_easter_egg() -> void:
|
||||
if save.tree_level("easter_ley") > 0:
|
||||
return
|
||||
@@ -493,15 +490,11 @@ func _check_easter_egg() -> void:
|
||||
save.set_tree_level("easter_ley", 1)
|
||||
|
||||
|
||||
# 三体系迁移函数已删除(迭代 21 T5——旧档破坏性废弃〔Q3 用户裁:开发阶段无迁移〕;换代前结构
|
||||
# 由 SaveManager._normalize 的 tree_struct 标记检测废弃重置,禁迁移禁折算)。
|
||||
|
||||
|
||||
# ---- 同调位账本(迭代 11 第①批 T4——装配关系/同调位求值/Y 刷新升级;设计权威 proposal §2 + 需求卡片 P1 甲)----
|
||||
# ---- 同调位账本(装配关系/同调位求值/Y 刷新升级;设计权威 proposal §2)----
|
||||
|
||||
# 装配切换(营地装配覆盖层唯一写口——冒险开始前配置;冒险中零写口=装配不可变契约 1,本口只服务营地):
|
||||
# 已装 → 卸下恒可(卸下后其余件前移位序——效果面只读装配集不受格位影响);未装 → 非同调行拒绝 +
|
||||
# 满位禁选 + 档位格校验(迭代 22 T2 契约 2:装入件 append 至末位 k=已装数+1,件档 rank ≤ k——
|
||||
# 满位禁选 + 档位格校验(契约 2:装入件 append 至末位 k=已装数+1,件档 rank ≤ k——
|
||||
# 格 1=普通格/格 2=优秀格/格 3=稀有格递增向下兼容;槽位来源=树同调位节点点亮数,树接口接缝现役)。
|
||||
# 装配关系写存档持久。
|
||||
func toggle_attunement(item_id: String) -> bool:
|
||||
|
||||
@@ -11,18 +11,18 @@ signal round_end_board(payload: Dictionary) # 双发射源之棋盘路径(
|
||||
|
||||
var db: ConfigDb
|
||||
var action_economy: ActionEconomy
|
||||
# 秘宝常时参数注入面(迭代 22 秘宝批 T3——编排器 begin_battle 按装配集 kind 分发注入:
|
||||
# 秘宝常时参数注入面(编排器 begin_battle 按装配集 kind 分发注入:
|
||||
# dig_range_bonus=挖掘切比雪夫距离放宽〔远程镐 dig_range_bonus magnitude〕/seal_range_free=
|
||||
# 封印不限相邻〔信标网络〕;缺省 0/false=零装配零扰动,棋盘事实不可变契约不受影响——
|
||||
# 两参数只放宽「动作可达性」判据,禁碰危险格/安全格分布)
|
||||
var dig_range_bonus := 0
|
||||
var seal_range_free := false
|
||||
# 棋盘状态私有(E2 封装收口 2026-09-13):对外只暴露 current_board()/reveal_record() 等查询面,
|
||||
# 禁止跨模块直读/直写原始字典——先例教训 = board_view 绕过查询面直读 revealed。
|
||||
# 棋盘状态私有(E2 封装收口):对外只暴露 current_board()/reveal_record() 等查询面,
|
||||
# 禁止跨模块直读/直写原始字典。
|
||||
var _board: BoardData = null
|
||||
var _revealed := {} # Vector2i → {"kind": "safe"/"mine", "digit": int}
|
||||
var _seals := {} # Vector2i → true(棋盘单态;跨轮保留随图存续)
|
||||
var _removed := {} # Vector2i → true(迭代 13 T8/D6——中立移除危险格集;随 install/retire 清空)
|
||||
var _removed := {} # Vector2i → true(中立移除危险格集;随 install/retire 清空)
|
||||
var _hero := Vector2i.ZERO
|
||||
var _spawn: Dictionary[Vector2i, bool] = {}
|
||||
|
||||
@@ -72,7 +72,7 @@ func clear_map_cleared() -> bool:
|
||||
return safe_remaining() == 0
|
||||
|
||||
|
||||
# ---- 中立移除(迭代 13 T8/D6——P1 甲:邻接数字=原始雷数冻结零重算;P2 甲:移除格呈现退色降档;
|
||||
# ---- 中立移除(P1 甲:邻接数字=原始雷数冻结零重算;P2 甲:移除格呈现退色降档;
|
||||
# 移除集=未揭示、无封印、是雷——封印判定基线零回溯;危险格不入清图分母/连锁/C_dig 模拟,判定逻辑零改动) ----
|
||||
|
||||
func remove_mines_in(cells: Array) -> Array:
|
||||
@@ -103,7 +103,7 @@ func has_unrevealed() -> bool:
|
||||
|
||||
# ---- install(编排边界相位 = 行动经济 Idle/Waiting;Active 拒绝——防轮内换板)----
|
||||
|
||||
# during_round = 轮内刷新编排路径(2026-09-12 方案 A,战斗结算 唯一调用方):Active 相位合法——
|
||||
# during_round = 轮内刷新编排路径(战斗结算 唯一调用方):Active 相位合法——
|
||||
# 玩家挖掘中立即换图,棋盘状态(revealed/seals)随 install 重置,初始展开事件照发(initial 通道)
|
||||
func install_board(b: BoardData, during_round := false) -> Dictionary:
|
||||
if action_economy.phase == action_economy.PHASE_ACTIVE and not during_round:
|
||||
@@ -138,7 +138,7 @@ func seal_count() -> int:
|
||||
return _seals.size()
|
||||
|
||||
|
||||
# during_round = 轮内刷新编排路径(2026-09-12 方案 A):Active 相位合法——换图前退役旧板
|
||||
# during_round = 轮内刷新编排路径:Active 相位合法——换图前退役旧板
|
||||
func retire_board(during_round := false) -> Dictionary:
|
||||
if _board == null:
|
||||
return _outcome(true, "executed", "", "") # 幂等受理
|
||||
@@ -267,7 +267,7 @@ func _do_dig(c: Vector2i) -> Dictionary:
|
||||
var sub := _board_face_dig(c)
|
||||
if sub != "":
|
||||
return _outcome(false, "rejected", "board_face", sub)
|
||||
# 挖封印格(封印不可逆批 2026-09-19):本格封印事务内隐式退役——链闭包按「本格无封印、其余封印照挡」
|
||||
# 挖封印格(封印不可逆):本格封印事务内隐式退役——链闭包按「本格无封印、其余封印照挡」
|
||||
# 计算(封印格为 0 时连锁照常外扩);request_dig 被拒则封印原地保留(事务性),受理后该封印轮末不判
|
||||
# (发射面基线 = 在场封印——标中/标空兑付让位于挖掘本体结局,四情况②④)。
|
||||
var was_sealed := has_seal(c)
|
||||
@@ -295,7 +295,7 @@ func _do_dig(c: Vector2i) -> Dictionary:
|
||||
continue # 闭包可穿过已开区——已开格零事件(cell_revealed × |O′ ∖ O|)
|
||||
var is_mine := _board.is_mine(rc)
|
||||
var channel := "dig" if rc == c else "chain"
|
||||
# 格面资源资格(2026-09-15 格面资源批):chain/dig 恒 true——安全格与危险格一视同仁(危险格亦携带,
|
||||
# 格面资源资格:chain/dig 恒 true——安全格与危险格一视同仁(危险格亦携带,
|
||||
# 负向兑付归 战斗结算 极性路由);initial(封锁格)恒 false。资源本体与极性不在本系统。
|
||||
# dug_under_seal = 首格带封印直挖标记(战斗结算 情况④兑付改判消费;连锁格恒 false——连锁不越封印)
|
||||
if is_mine:
|
||||
@@ -325,12 +325,12 @@ func _board_face_dig(c: Vector2i) -> String:
|
||||
if is_revealed(c):
|
||||
return "target_revealed"
|
||||
if _removed.has(c):
|
||||
return "target_removed" # 迭代 13 T8/D6——中立移除格不可交互(拒绝先于消耗,pattern 1 board_face 腿)
|
||||
# 封印不拦挖掘(封印不可逆批 2026-09-19):有封印格直接挖 = 对承诺的兑现,兑付改判归 战斗结算。
|
||||
return "target_removed" # 中立移除格不可交互(拒绝先于消耗,pattern 1 board_face 腿)
|
||||
# 封印不拦挖掘(封印不可逆):有封印格直接挖 = 对承诺的兑现,兑付改判归 战斗结算。
|
||||
return ""
|
||||
|
||||
|
||||
# fx_reveal 效果通道揭示(迭代 20 专长实装批 T1——订阅类「揭示 1 格」效果的棋盘面执行口:
|
||||
# fx_reveal 效果通道揭示(订阅类「揭示 1 格」效果的棋盘面执行口:
|
||||
# 不经行动经济〔零 AP 消耗零相位要求〕、不要求邻接〔任意揭示〕、只受理安全格〔信息红线=只揭真格,
|
||||
# 危险格/已开/出界/移除格=拒绝零副作用〕。连锁与揭示事件与 _do_dig 同构(chain 通道复用),资源兑付归
|
||||
# 战斗结算 _flush_pending_reveals 直接入账——channel=fx_reveal 供归因)
|
||||
@@ -400,7 +400,7 @@ func _do_seal_place(c: Vector2i) -> Dictionary:
|
||||
elif has_seal(c):
|
||||
sub = "seal_present"
|
||||
elif _removed.has(c):
|
||||
sub = "target_removed" # 迭代 13 T8/D6——中立移除格不可交互
|
||||
sub = "target_removed" # 中立移除格不可交互
|
||||
if sub != "":
|
||||
return _outcome(false, "rejected", "board_face", sub)
|
||||
if not action_economy.try_spend("seal_place"):
|
||||
@@ -408,7 +408,7 @@ func _do_seal_place(c: Vector2i) -> Dictionary:
|
||||
_seals[c] = true # 封印动作零格子事件外发(发射面封闭;反馈经调用返回值命令式驱动)
|
||||
if action_economy.phase != action_economy.PHASE_ACTIVE:
|
||||
# 封印动作花完末点 AP 同样终结轮次(行动经济 三出口——AP 用完与动作种类无关;缺漏使 round_end
|
||||
# 永不发出、战斗结算 的 Waiting 结算点永不到达 = 整局软锁。playtest 缺陷②,行为探针复现)
|
||||
# 永不发出、战斗结算 的 Waiting 结算点永不到达 = 整局软锁)
|
||||
_emit_round_end_seals_and_payload()
|
||||
return _outcome(true, "executed", "", "")
|
||||
|
||||
@@ -468,8 +468,8 @@ func _do_stop() -> Dictionary:
|
||||
# ---- 轮末封印判定 + 棋盘路径 round_end 外发 ----
|
||||
|
||||
func _emit_seals_batch() -> void:
|
||||
# 判定基线 = 轮次结束事务开始时封印位;行主序枚举(确定性——排序键单点 BoardData.row_major_key,
|
||||
# 2026-10-02 修复批);被拔的不判;诚实重复上报(去重归 战斗结算)
|
||||
# 判定基线 = 轮次结束事务开始时封印位;行主序枚举(确定性——排序键单点 BoardData.row_major_key);
|
||||
# 被拔的不判;诚实重复上报(去重归 战斗结算)
|
||||
var cells: Array[Variant] = _seals.keys()
|
||||
cells.sort_custom(func(a, b): return BoardData.row_major_key(a) < BoardData.row_major_key(b))
|
||||
for c in cells:
|
||||
@@ -487,14 +487,14 @@ func _adjacent_to_hero(c: Vector2i) -> bool:
|
||||
return maxi(d.x, d.y) == 1
|
||||
|
||||
|
||||
# 挖掘可达判据(迭代 22 秘宝批 T3——远程镐 dig_range_bonus 放宽:切比雪夫距离 ≤ 1+加值,
|
||||
# 加值 0=与旧「恰相邻」逐位一致;开格仍走既有格规则=不挖开不相邻事实之外 anything)
|
||||
# 挖掘可达判据(远程镐 dig_range_bonus 放宽:切比雪夫距离 ≤ 1+加值,
|
||||
# 加值 0=恰相邻;开格仍走既有格规则=不挖开不相邻事实之外 anything)
|
||||
func _within_dig_range(c: Vector2i) -> bool:
|
||||
var d := Vector2i(absi(c.x - _hero.x), absi(c.y - _hero.y))
|
||||
return maxi(d.x, d.y) <= 1 + dig_range_bonus
|
||||
|
||||
|
||||
# 本板危险格总数(迭代 22 秘宝批 T3——巢穴预告「装板后揭示本板危险格数量」取数口;
|
||||
# 本板危险格总数(巢穴预告「装板后揭示本板危险格数量」取数口;
|
||||
# 只读投影 BoardData.mines 规模,零分布泄露——数量非位置)
|
||||
func mine_total() -> int:
|
||||
return _board.mines.size() if _board != null else 0
|
||||
|
||||
@@ -16,7 +16,7 @@ var _granted := {} # Vector2i → 快照(重复受理幂等返
|
||||
var _clear_identity := ""
|
||||
var _clear_granted := false
|
||||
var _clear_snapshot := {}
|
||||
# 效果引擎(内容数据驱动扩展批 T1——reward_roll 发射面;偏运硬币 bias_rewards 订阅面)
|
||||
# 效果引擎(reward_roll 发射面;偏运硬币 bias_rewards 订阅面)
|
||||
var _fx: EffectEngine = null
|
||||
|
||||
|
||||
@@ -40,7 +40,7 @@ func roll_board(eligible: Array) -> bool:
|
||||
if eligible.is_empty():
|
||||
_fatal("roll_board 空资格清单(E1 兜底 fail-fast——排雷玩法 不变式④前置)")
|
||||
return false
|
||||
# 行主序规范化 + 去重(排序责任不外推给 战斗结算;排序键单点 BoardData.row_major_key——2026-10-02 修复批)
|
||||
# 行主序规范化 + 去重(排序责任不外推给 战斗结算;排序键单点 BoardData.row_major_key)
|
||||
var seen := {}
|
||||
var list: Array[Dictionary] = []
|
||||
for e in eligible:
|
||||
@@ -58,7 +58,7 @@ func roll_board(eligible: Array) -> bool:
|
||||
_clear_granted = false
|
||||
_clear_snapshot = {}
|
||||
var stream: RandomNumberGenerator = rng.open_stream("reward_roll") # 恰一 scope 批(格批 + 清图批尾 + 区间行追加取号)
|
||||
# reward_roll 发射+施加(内容数据驱动扩展批 T4——格面资源掷定、摇号前权重合成层:
|
||||
# reward_roll 发射+施加(格面资源掷定、摇号前权重合成层:
|
||||
# bias_rewards 订阅命中合计=偏运权重加算(行 value_from magnitude 定影);零命中=原行集零扰动)
|
||||
var luck_bias := 0
|
||||
if _fx != null:
|
||||
@@ -124,7 +124,7 @@ func query_full_snapshot(cell: Vector2i):
|
||||
return _snapshot(_row_by_id(rid), int(_power_roll.get(cell, -1)))
|
||||
|
||||
|
||||
# ---- 受理面(战斗结算 编排期调用;E6 收口 2026-09-13:失败统一返回 null)----
|
||||
# ---- 受理面(战斗结算 编排期调用;E6 收口:失败统一返回 null)----
|
||||
|
||||
func grant(cell: Vector2i):
|
||||
if _phase != "ready":
|
||||
@@ -180,12 +180,11 @@ func polarity_row(cell_type: String, polarity: String) -> Dictionary:
|
||||
|
||||
# ---- F1 选行算子纯函数 ----
|
||||
|
||||
# 偏运硬币 N(迭代 11 同调批 T5 → 内容数据驱动扩展批 T4:begin_battle 注入拆除——
|
||||
# reward_roll bias_rewards 订阅命中合计经 _biased_cell_rows 入参;0 = 未装配零扰动)
|
||||
# 偏运硬币 N(reward_roll bias_rewards 订阅命中合计经 _biased_cell_rows 入参;0 = 未装配零扰动)
|
||||
# 稀有正向资源 kinds(底稿 §3——权重投影面,池结构零触碰)
|
||||
const LUCK_BIAS_KINDS := ["heal", "gp", "pierce", "magic"]
|
||||
|
||||
# 图级偏斜旋钮(全局作用域变异首发批 2026-10-04——battle_orchestrator 装板决议注入,全局型行
|
||||
# 图级偏斜旋钮(battle_orchestrator 装板决议注入,全局型行
|
||||
# effect_value 语义=生存族权重下调百分比;0 = 中性 off 态。每板决议时显式重置/写入)
|
||||
var board_skew: int = 0
|
||||
|
||||
@@ -196,7 +195,7 @@ const SKEW_UP_KINDS := ["attack", "blast", "pierce", "magic"]
|
||||
|
||||
|
||||
# 偏运硬币权重投影(底稿 §3:只动池内权重、雷安仍同池同分布;luck_bias=0 逐位返回原行集——off 态零扰动)
|
||||
# 基座换源(首发批 2026-10-04):先经图级偏斜投影再叠件级加法——合成序锚=图级先、件级后
|
||||
# 合成序:先经图级偏斜投影再叠件级加法——合成序锚=图级先、件级后
|
||||
#(环境事实先定型、玩家构筑叠其上;两投影均为池内权重面,雷安同池与取号次数零变化)
|
||||
func _biased_cell_rows(bias: int) -> Array:
|
||||
var base: Array[Variant] = _skewed_cell_rows()
|
||||
@@ -213,7 +212,7 @@ func _biased_cell_rows(bias: int) -> Array:
|
||||
return out
|
||||
|
||||
|
||||
# 图级资源面偏斜投影(首发批 T3——整数最大余数法:生存族按 board_skew% 下调,下调量按进攻族
|
||||
# 图级资源面偏斜投影(整数最大余数法:生存族按 board_skew% 下调,下调量按进攻族
|
||||
# 原权重比例整数重分配,合计恒等于原池合计 77;负向行与白名单三行逐位不动)。
|
||||
# board_skew<=0 逐位返回原行集 = off 态零扰动第一断言:不进投影域、行集/行序/取号域与基线逐位同。
|
||||
func _skewed_cell_rows() -> Array:
|
||||
@@ -300,7 +299,7 @@ func weighted_pick(rows: Array, r: int) -> String:
|
||||
return ""
|
||||
|
||||
|
||||
# aux 解锁门控集(迭代 7 T5——拍板③甲「解锁→进投放池」:未解锁 aux 行不入摇号池。
|
||||
# aux 解锁门控集(「解锁→进投放池」:未解锁 aux 行不入摇号池。
|
||||
# 空集默认 = 全部 aux 不投放(白板/夹具态零投放——模拟读数零扰动;真实侧 main 组装工坊解锁集传入)
|
||||
var _treasure_unlocked := {}
|
||||
|
||||
|
||||
@@ -5,8 +5,8 @@
|
||||
class_name RngServiceCore
|
||||
extends Node
|
||||
|
||||
# 用途标签封闭词表(并流前七流名全保留;新增用途走扩签通道 = 改代码 + 评审——清单封闭语义由「流」改「标签」)
|
||||
const PURPOSE_TAGS := ["board_gen", "reward_roll", "encounter", "armor_roll", "behavior_roll", "variant_place", "gp_drop", "node_map", "reward_pick", "shop_draw", "fx_reveal"] # node_map = 关卡节点图生成 v0.4 扩签(M-3 迭代 5 T5);reward_pick = 提升三选一加权洗牌扩签(M-3 迭代 10 奖励重构批——每关大奖励抽签恰一 scope:E-S weight-key 每候选一取,标签不参与派生只进事件日志,派生/黄金向量零改动);shop_draw = 商店房货架抽签扩签(M-4 迭代 11 第②批 T1——每次抽 3〔进店+每次买后重抽〕恰一 scope,与 reward_pick 分签防取数序互扰;shop=off 零消费零扰动);fx_reveal = 专长订阅类揭示效果掷格(迭代 20 T1——见血开窍/斩首学者等「揭示 1 格」,与 reward_pick 分签防取数序互扰;只揭真格=安全格信息红线承载)
|
||||
# 用途标签封闭词表(流名清单封闭;新增用途走扩签通道 = 改代码 + 评审)
|
||||
const PURPOSE_TAGS := ["board_gen", "reward_roll", "encounter", "armor_roll", "behavior_roll", "variant_place", "gp_drop", "node_map", "reward_pick", "shop_draw", "fx_reveal"] # node_map = 关卡节点图生成 v0.4 扩签;reward_pick = 提升三选一加权洗牌扩签(每关大奖励抽签恰一 scope:E-S weight-key 每候选一取,标签不参与派生只进事件日志,派生/黄金向量零改动);shop_draw = 商店房货架抽签扩签(每次抽 3〔进店+每次买后重抽〕恰一 scope,与 reward_pick 分签防取数序互扰;shop=off 零消费零扰动);fx_reveal = 专长订阅类揭示效果掷格(见血开窍/斩首学者等「揭示 1 格」,与 reward_pick 分签防取数序互扰;只揭真格=安全格信息红线承载)
|
||||
|
||||
# 分段表达式书写(数据配置 R3 实现级规约——禁 ≥2^63 完整字面量)
|
||||
const GOLDEN_RATIO := (0x9E3779B9 << 32) | 0x7F4A7C15 # 0x9E3779B97F4A7C15
|
||||
@@ -18,7 +18,7 @@ const MIX_MUL_B := (0x94D049BB << 32) | 0x133111EB # 0x94D049BB133111EB
|
||||
const OUT_MASK := 0x7FFFFFFFFFFFFFFF
|
||||
const INT64_MIN := -9223372036854775808
|
||||
|
||||
# master_seed 只读出口(E5 封装收口 2026-09-13):写入只经 set_master_seed(计数器全零断言
|
||||
# master_seed 只读出口(E5 封装收口):写入只经 set_master_seed(计数器全零断言
|
||||
# 不可绕过);外部读 master_seed 供 存档序列化 快照契约观测。
|
||||
var _master_seed: int = -1 # -1 = 未播种(取数即致命,EC7)
|
||||
var master_seed: int:
|
||||
@@ -30,16 +30,16 @@ var _log_fa: FileAccess = null
|
||||
var _log_warned := false
|
||||
|
||||
|
||||
# 纯函数(测试/预览/日志回放入口,不推计数器)——单盐单参数(并流前 (流, scope) 双参退役)
|
||||
# 纯函数(测试/预览/日志回放入口,不推计数器)——单盐单参数
|
||||
func derive_at(scope: int) -> int:
|
||||
assert(scope >= 0, "负 scope 与 scope+2^63 经输出掩码别名(禁止)")
|
||||
var t: int = _master_seed + scope * GOLDEN_RATIO # wrap64 自然回绕——禁 float 中间量/禁 %/禁主动修复
|
||||
return _mix64(SINGLE_SALT ^ t)
|
||||
|
||||
|
||||
# R3 派生终末混淆(splitmix64 finalizer 变体,2026-09-19 掷点测量修复批 B 案):裸派生种子直接作
|
||||
# R3 派生终末混淆(splitmix64 finalizer 变体):裸派生种子直接作
|
||||
# PCG 初始状态时,「连续小整数 master × 头部 scope 窗口」的面值彩票会被短局复用放大进聚合统计
|
||||
# (验收重测批实证:30 连续种子 × 头部窗口 × 40 局复用 → 18 点聚合 +57%);此混淆使任何窄种子窗
|
||||
# (30 连续种子 × 头部窗口 × 40 局复用 → 18 点聚合 +57%);此混淆使任何窄种子窗
|
||||
# 表现如随机抽样。纯函数、逐 scope 复放语义不变;全程 OUT_MASK 63 位掩码——有符号 int64 的 >>
|
||||
# 为算术移位,63 位正值域内与逻辑移位逐位一致,这是与参考实现(Python 无符号模型)对齐的关键。
|
||||
func _mix64(z: int) -> int:
|
||||
@@ -63,14 +63,14 @@ func set_master_seed(seed_val: int) -> void:
|
||||
|
||||
|
||||
# 切片登记:冒险开始重播种入口(正式侧调用权归 冒险结构;切片主菜单 = 冒险起点)——
|
||||
# 并流批(2026-09-27 统一流裁决):重播种同时开本次冒险的事件日志会话。
|
||||
# 重播种同时开本次冒险的事件日志会话。
|
||||
func new_adventure(seed_val: int) -> void:
|
||||
_counter = 0
|
||||
set_master_seed(seed_val)
|
||||
_open_log_session(_master_seed)
|
||||
|
||||
|
||||
# 断点续玩恢复入口(迭代 8 卡①——快照契约「主种子+计数器」的回填面,调用权归 冒险结构):
|
||||
# 断点续玩恢复入口(快照契约「主种子+计数器」的回填面,调用权归 冒险结构):
|
||||
# 回填后首个 open_stream(scope=counter_val) 的派生种子与中断前逐位一致——续玩从断点号位精确
|
||||
# 续接原时间线(停点前已消耗的号位不重耗,停点后取数逐位可复现)。仅续玩路径调用;模拟/冒烟
|
||||
# 零触达(MC 对拍零扰动)。
|
||||
|
||||
@@ -1,15 +1,15 @@
|
||||
class_name RunLedger
|
||||
extends RefCounted
|
||||
# 共享四本账显式承载(迭代 27 T3——审计 P1-4「共享可变字典跨三层:所有权名义单点、实际三处
|
||||
# 可写」收口;裁点②甲 = 单类持四账)。
|
||||
# 共享四本账显式承载(审计 P1-4「共享可变字典跨三层:所有权名义单点、实际三处
|
||||
# 可写」收口 = 单类持四账)。
|
||||
# 所有权:AdventureRunner 持有(开局构造、冒险全程唯一宿主);LevelRunner 经 set_run_ledger
|
||||
# 下传并装配 encounter;战斗侧(BattleOrchestrator)经 encounter["ledger"] 拿本类引用,
|
||||
# 只走下方 read/append 原语——战末 export/开局 seed 时点语义不变(确定性设计原样保留,
|
||||
# 只把「函数签名上不可见的共享可变引用」换成签名可见的显式类)。
|
||||
var attune_charges: Dictionary[Variant, Variant] = {} # 同调主动件充能账(迭代 11 第①批 T6——use_treasure 扣减;断点随局回档)
|
||||
var vision_counter: Dictionary[Variant, Variant] = {} # 真视类使用计数(迭代 22 秘宝批 T3——冒险开始置 {"used":0};
|
||||
# 缺省空 = 调试/单场战斗不计——原 enc 缺键守卫语义)
|
||||
var adventure_counters: Dictionary[Variant, Variant] = {} # adventure 域效果计数器(迭代 20 T6——rebuild 回灌/战末导出回写目标)
|
||||
var attune_charges: Dictionary[Variant, Variant] = {} # 同调主动件充能账(use_treasure 扣减;断点随局回档)
|
||||
var vision_counter: Dictionary[Variant, Variant] = {} # 真视类使用计数(冒险开始置 {"used":0};
|
||||
# 缺省空 = 调试/单场战斗不计——enc 缺键守卫语义)
|
||||
var adventure_counters: Dictionary[Variant, Variant] = {} # adventure 域效果计数器(rebuild 回灌/战末导出回写目标)
|
||||
var bonus: Dictionary[Variant, Variant] = {} # 统一冒险加算区(冒险开始置四轴零账——商店购买/三选一属性件/战末 bonus_delta 汇入)
|
||||
|
||||
|
||||
|
||||
+39
-39
@@ -6,10 +6,10 @@
|
||||
class_name SaveManagerCore
|
||||
extends Node
|
||||
|
||||
const SAVE_FORMAT_VERSION := 7 # registry schema_version:v7=锻造工坊域(迭代 23 T5——meta 新域 forge_routes〔六路线等级 levels+直购解锁清单 unlocked+当前武器/甲选中标记 weapon/armor;无特效域——裁 6 满级特效本期不做〕,旧档缺键由 _normalize 按默认补齐=初始持有态〔sword/light 在档余四锁、选中=初始路线〕,v6→v7 空迁移=缺键补齐模式——v3→v4 先例);v6=生涯终身计数域(迭代 18 卡④——meta 两新域 adventure_total〔冒险总局数,开局即计含放弃与失败〕/adventure_cleared〔通关局数,三关全通才计 1——判定字段 summary.cleared〕,旧档缺键由 _normalize 按默认补零,收束自增唯一入账=apply_adventure_summary);v5=天赋树域(迭代 12 T9——meta 三新域 tree_nodes〔node_id→已购等级〕/fork_sides〔fork_group→a|b〕/migration_note〔三体系迁移补偿注记〕——M-4 迭代 12 批次二;需求卡片「v3→v4」系同调批未落时行文,v4 已被同调批占用顺延 v5 披露);v4=同调批扩域(meta 三新域 boss_kill_count/attunement_equipped/attune_refresh_level——M-4 迭代 11 第①批 T2;断点充能余量随 T6 接线,旧档缺键 = 合法空态);v3=断点附随件扩列+摘要 keystone_drops(M-3 迭代 10 奖励重构);v2=断点域出生(M-3 迭代 8 卡①);v1=首版;高版本拒读 owner=存档序列化
|
||||
const SAVE_FORMAT_VERSION := 7 # registry schema_version:v7=锻造工坊域(meta 新域 forge_routes〔六路线等级 levels+直购解锁清单 unlocked+当前武器/甲选中标记 weapon/armor;无特效域〕,旧档缺键由 _normalize 按默认补齐=初始持有态〔sword/light 在档余四锁、选中=初始路线〕,v6→v7 空迁移=缺键补齐模式);v6=生涯终身计数域(meta 两新域 adventure_total〔冒险总局数,开局即计含放弃与失败〕/adventure_cleared〔通关局数,三关全通才计 1——判定字段 summary.cleared〕,旧档缺键由 _normalize 按默认补零,收束自增唯一入账=apply_adventure_summary);v5=天赋树域(meta 三新域 tree_nodes〔node_id→已购等级〕/fork_sides〔fork_group→a|b〕/migration_note〔三体系迁移补偿注记〕);v4=同调位域(meta 三新域 boss_kill_count/attunement_equipped/attune_refresh_level——旧档缺键 = 合法空态);v3=断点附随件扩列+摘要 keystone_drops;v2=断点域出生;v1=首版;高版本拒读 owner=存档序列化
|
||||
const SUMMARY_CAP := 10 # 最近冒险摘要保留条数(最新在前)
|
||||
|
||||
const TREE_STRUCT_VERSION := 2 # 树结构版本标记(迭代 21 T5——12 节点 v0.8 结构;加载检测缺失
|
||||
const TREE_STRUCT_VERSION := 2 # 树结构版本标记(12 节点 v0.8 结构;加载检测缺失
|
||||
# 或更低 = 换代前结构 → 树域破坏性废弃重置〔契约 4:禁迁移禁折算〕)
|
||||
|
||||
var _path: String # 存档文件路径(默认 user://save.json;冒烟注入隔离路径)
|
||||
@@ -30,14 +30,14 @@ func _init(path: String = "user://save.json") -> void:
|
||||
register_migration(6, _migrate_v6_v7)
|
||||
|
||||
|
||||
# autoload 生命周期(迭代 27 T1 裁点①——SaveManager 升格跨场景持有者,RngService 先例:
|
||||
# 类名加 Core 后缀、autoload 占净名;读盘挂 _ready=autoload 先于主场景就绪,时序与旧
|
||||
# main._ready 读盘等价)。独立 .new() 实例(单测/工具)不入树零 _ready,读盘仍由调用方显式触发。
|
||||
# autoload 生命周期(SaveManager 为跨场景持有者,仿 RngService 先例:
|
||||
# 类名加 Core 后缀、autoload 占净名;读盘挂 _ready=autoload 先于主场景就绪)。独立
|
||||
# .new() 实例(单测/工具)不入树零 _ready,读盘仍由调用方显式触发。
|
||||
func _ready() -> void:
|
||||
load_from_disk()
|
||||
|
||||
|
||||
# 原地重置(迭代 27 T1——探针隔离面,沿 RngService.set_master_seed 单例原地重置先例:
|
||||
# 原地重置(探针隔离面,仿 RngService.set_master_seed 单例原地重置:
|
||||
# 换路径+回默认数据+重读盘;对象引用恒定,MetaProgression 持有的 save 引用零失效)
|
||||
func attach_path(path: String) -> void:
|
||||
_path = path
|
||||
@@ -54,25 +54,25 @@ func _migrate_v1_v2(data: Dictionary) -> Dictionary:
|
||||
return data
|
||||
|
||||
|
||||
# v2→v3:断点附随件扩列(迭代 10 奖励重构批——bonus_taken/bonus_pending/bonus_result/
|
||||
# keystone_drops + bonus.ap 轴)与摘要 additive 键 keystone_drops——旧档缺键〔bonus_aux_carry 键随迭代 22 T5 替换子交互拆除退役〕
|
||||
# v2→v3:断点附随件扩列(bonus_taken/bonus_pending/bonus_result/
|
||||
# keystone_drops + bonus.ap 轴)与摘要 additive 键 keystone_drops——旧档缺键
|
||||
# = 合法空态(读取方 .get 兜底),无结构迁移体;版本号推进仅为 schema_version 对齐
|
||||
func _migrate_v2_v3(data: Dictionary) -> Dictionary:
|
||||
data["version"] = SAVE_FORMAT_VERSION
|
||||
return data
|
||||
|
||||
|
||||
# v3→v4:同调批扩域(迭代 11 第①批 T2——meta 三新域 boss_kill_count/attunement_equipped/
|
||||
# v3→v4:同调位域(meta 三新域 boss_kill_count/attunement_equipped/
|
||||
# attune_refresh_level,旧档缺键由 _normalize 按默认补齐,无结构迁移体;版本号推进仅为
|
||||
# schema_version 对齐——v2→v3 先例)
|
||||
# schema_version 对齐)
|
||||
func _migrate_v3_v4(data: Dictionary) -> Dictionary:
|
||||
data["version"] = SAVE_FORMAT_VERSION
|
||||
return data
|
||||
|
||||
|
||||
# v4→v5:天赋树域(迭代 12 T9 建域——meta 三新域 tree_nodes/fork_sides/migration_note,缺键默认
|
||||
# 补齐;迭代 21 T5 换代:原「三体系迁移」映射体随 migrate_tree 一并删除——开发阶段破坏性废弃
|
||||
# 〔Q3 用户裁〕,换代前结构由 _normalize 的 tree_struct 标记检测废弃重置,本函数只剩缺键补齐)。
|
||||
# v4→v5:天赋树域(meta 三新域 tree_nodes/fork_sides/migration_note,缺键默认
|
||||
# 补齐;开发阶段破坏性废弃口径:换代前结构由 _normalize 的 tree_struct 标记检测废弃重置,
|
||||
# 本函数只做缺键补齐)。
|
||||
func _migrate_v4_v5(data: Dictionary) -> Dictionary:
|
||||
var meta: Dictionary[Variant, Variant] = data.get("meta", {}) as Dictionary
|
||||
if not meta.has("tree_nodes"):
|
||||
@@ -83,17 +83,17 @@ func _migrate_v4_v5(data: Dictionary) -> Dictionary:
|
||||
return data
|
||||
|
||||
|
||||
# v5→v6:生涯终身计数域(迭代 18 卡④——meta 两新域 adventure_total/adventure_cleared,旧档缺键
|
||||
# 由 _normalize 按默认补零,无结构迁移体;版本号推进仅为 schema_version 对齐——v3→v4 先例。
|
||||
# v5→v6:生涯终身计数域(meta 两新域 adventure_total/adventure_cleared,旧档缺键
|
||||
# 由 _normalize 按默认补零,无结构迁移体;版本号推进仅为 schema_version 对齐。
|
||||
# 幂等性:重复迁移只覆写 version,既有值零改动;加载后 _normalize 补齐缺省——旧档可载可玩)
|
||||
func _migrate_v5_v6(data: Dictionary) -> Dictionary:
|
||||
data["version"] = SAVE_FORMAT_VERSION
|
||||
return data
|
||||
|
||||
|
||||
# v6→v7:锻造工坊域(迭代 23 T5——meta 新域 forge_routes,旧档缺键由 _normalize 按默认补齐=
|
||||
# v6→v7:锻造工坊域(meta 新域 forge_routes,旧档缺键由 _normalize 按默认补齐=
|
||||
# 初始持有态〔开发阶段无迁移禁折算——契约 4〕,无结构迁移体;版本号推进仅为 schema_version 对齐
|
||||
# ——v3→v4 先例。幂等性:重复迁移只覆写 version,既有值零改动;加载后 _normalize 补齐缺省——旧档可载可玩)
|
||||
# 幂等性:重复迁移只覆写 version,既有值零改动;加载后 _normalize 补齐缺省——旧档可载可玩)
|
||||
func _migrate_v6_v7(data: Dictionary) -> Dictionary:
|
||||
data["version"] = SAVE_FORMAT_VERSION
|
||||
return data
|
||||
@@ -112,27 +112,27 @@ func _default_data() -> Dictionary[String, Variant]:
|
||||
"boss_kill_count": 0,
|
||||
"attunement_equipped": [],
|
||||
"attune_refresh_level": 0,
|
||||
"tree_nodes": {}, # 天赋树已购等级(node_id→int——迭代 12 T9 建域)
|
||||
"fork_sides": {}, # Fork 激活态(fork_group→"a"|"b"——一节点双态切换承载,迭代 21 收编语义)
|
||||
"tree_struct": TREE_STRUCT_VERSION, # 树结构版本标记(迭代 21 T5——缺失=换代前结构,加载废弃重置)
|
||||
"tree_nodes": {}, # 天赋树已购等级(node_id→int)
|
||||
"fork_sides": {}, # Fork 激活态(fork_group→"a"|"b"——一节点双态切换承载)
|
||||
"tree_struct": TREE_STRUCT_VERSION, # 树结构版本标记(缺失=换代前结构,加载废弃重置)
|
||||
"recent_summaries": [],
|
||||
"first_adventure_done": false,
|
||||
"adventure_total": 0, # 生涯终身计数·冒险总局数(迭代 18 卡④——开局即计含放弃与失败,收束唯一入账)
|
||||
"adventure_total": 0, # 生涯终身计数·冒险总局数(开局即计含放弃与失败,收束唯一入账)
|
||||
"adventure_cleared": 0, # 生涯终身计数·通关局数(三关全通才计 1——判定字段 summary.cleared)
|
||||
"vision_used": 0, # 生涯终身计数·真视类工具累计使用次数(迭代 22 T2——信标网络解锁行为条件;
|
||||
# 受理成功唯一计数点=battle 层经冒险层聚合、收束入账;缺键补零不升版本——迭代 21 先例)
|
||||
"forge_routes": { # 锻造工坊域(迭代 23 T5——六路线等级+直购解锁清单+当前选中标记;无特效域)
|
||||
"vision_used": 0, # 生涯终身计数·真视类工具累计使用次数(信标网络解锁行为条件;
|
||||
# 受理成功唯一计数点=battle 层经冒险层聚合、收束入账;缺键补零不升版本)
|
||||
"forge_routes": { # 锻造工坊域(六路线等级+直购解锁清单+当前选中标记;无特效域)
|
||||
"levels": {"sword": 0, "spear": 0, "staff": 0, "light": 0, "heavy": 0, "ward": 0},
|
||||
"unlocked": [], # 直购解锁路线 id 清单(初始持有与练满前一档=派生解锁不落档)
|
||||
"weapon": "sword", # 当前选中武器路线(初始持有=sword)
|
||||
"armor": "light", # 当前选中甲路线(初始持有=light)
|
||||
},
|
||||
},
|
||||
"breakpoint": {}, # 冒险断点快照(空 = 无断点合法态——「继续上局」隐藏;迭代 8 卡①)
|
||||
"breakpoint": {}, # 冒险断点快照(空 = 无断点合法态——「继续上局」隐藏)
|
||||
}
|
||||
|
||||
|
||||
# ---- 首次冒险布尔位(关卡节点图生成 v0.4——唯一新增存档字段,M-3 迭代 5 T5)----
|
||||
# ---- 首次冒险布尔位(关卡节点图生成 v0.4)----
|
||||
# false = 首次脚本模式(全关零抽签取池内模板 0=现行固定图);置位只在冒险收束挂点(MetaProgression.settle_adventure)。
|
||||
# 老档迁移:缺字段 = 该档已有游玩史 → true(不重放教学脚本)。
|
||||
|
||||
@@ -243,7 +243,7 @@ func boss_first_kill_marked(level_index: int) -> bool:
|
||||
return _data["meta"]["boss_first_kill"].has(str(level_index))
|
||||
|
||||
|
||||
# ---- 同调位账本域(迭代 11 第①批 T2——里程碑谓词表计数源 + 装配关系 + Y 升级态)----
|
||||
# ---- 同调位账本域(里程碑谓词表计数源 + 装配关系 + Y 升级态)----
|
||||
|
||||
# Boss 击杀总数(含重复——底稿裁决 4 计数源;收束入账 = apply_adventure_summary 逐通关关次 +1,
|
||||
# Boss 恒唯一终点故 levels_cleared 即本次冒险 Boss 击杀数)
|
||||
@@ -251,7 +251,7 @@ func boss_kill_count() -> int:
|
||||
return int(_data["meta"].get("boss_kill_count", 0))
|
||||
|
||||
|
||||
# ---- 生涯终身计数域(迭代 18 卡④——跨局汇总页数据源;只读面经访问器,
|
||||
# ---- 生涯终身计数域(跨局汇总页数据源;只读面经访问器,
|
||||
# 写入唯一经 apply_adventure_summary 收束口,禁第二写入点)----
|
||||
|
||||
# 冒险总局数(口径=开局即计:三结局 completed/failed/abandoned 同经收束恰计一次,含放弃与失败)
|
||||
@@ -265,7 +265,7 @@ func adventure_cleared() -> int:
|
||||
return int(_data["meta"].get("adventure_cleared", 0))
|
||||
|
||||
|
||||
# 真视类工具累计使用次数(迭代 22 T2——信标网络解锁行为条件;写入唯一经 add_vision_used
|
||||
# 真视类工具累计使用次数(信标网络解锁行为条件;写入唯一经 add_vision_used
|
||||
# 冒险收束聚合入账,受理计数点=battle 层经冒险层聚合)
|
||||
func vision_used() -> int:
|
||||
return int(_data["meta"].get("vision_used", 0))
|
||||
@@ -275,7 +275,7 @@ func add_vision_used(n: int) -> void:
|
||||
_data["meta"]["vision_used"] = maxi(0, vision_used() + maxi(0, n))
|
||||
|
||||
|
||||
# ---- 天赋树域(迭代 12 T9——MetaProgression 树段读写面)----
|
||||
# ---- 天赋树域(MetaProgression 树段读写面)----
|
||||
|
||||
func tree_nodes() -> Dictionary:
|
||||
return (_data["meta"].get("tree_nodes", {}) as Dictionary).duplicate()
|
||||
@@ -305,7 +305,7 @@ func set_fork_side(group: String, side: String) -> void:
|
||||
_data["meta"]["fork_sides"] = fs
|
||||
|
||||
|
||||
# ---- 锻造工坊域 forge_routes(迭代 23 T5——MetaProgression 锻造写口读写面;无特效域)----
|
||||
# ---- 锻造工坊域 forge_routes(MetaProgression 锻造写口读写面;无特效域)----
|
||||
# 解锁态不落档(派生):初始持有=表 initial_owned、练满前一档=levels[prereq]≥cap——判定单点在
|
||||
# MetaProgression.forge_route_unlocked(读表+读档);本类只承载直购清单 unlocked 与选中标记。
|
||||
# 域结构:{levels:{六路线 id→等级}, unlocked:[直购路线 id], weapon:当前武器路线 id, armor:当前甲路线 id}
|
||||
@@ -365,7 +365,7 @@ func set_forge_armor(route_id: String) -> void:
|
||||
|
||||
|
||||
# 装配关系(同调目录 item_id 有序数组——装配序即呈现序;跨冒险持久,冒险中途不可换装〔底稿 §8.5〕)。
|
||||
# 局外进度事实(元进度域同族),非契约 2 反面清单的「局内道具库存」——存档序列化.md 契约 2 随批注记。
|
||||
# 局外进度事实(元进度域同族),非契约 2 反面清单的「局内道具库存」——口径见 存档序列化.md 契约 2。
|
||||
func attunement_equipped() -> Array:
|
||||
return (_data["meta"].get("attunement_equipped", []) as Array).duplicate()
|
||||
|
||||
@@ -377,7 +377,7 @@ func set_attunement_equipped(item_ids: Array) -> void:
|
||||
_data["meta"]["attunement_equipped"] = out
|
||||
|
||||
|
||||
# Y 刷新升级购买等级(0 = 未购 → 每冒险刷新次数 0;等级行值归营地升级行族——T4 接线)
|
||||
# Y 刷新升级购买等级(0 = 未购 → 每冒险刷新次数 0;等级行值归营地升级行族)
|
||||
func attune_refresh_level() -> int:
|
||||
return int(_data["meta"].get("attune_refresh_level", 0))
|
||||
|
||||
@@ -393,10 +393,10 @@ func recent_summaries() -> Array:
|
||||
|
||||
|
||||
# 域更新原语(正式冒险收束唯一入账口——组合根经本函数入账,禁直改内部结构):
|
||||
# 钥石入账(summary.keystone_total——迭代 10 起逐杀掉落已在冒险内计入,boss_drops 参数降级为
|
||||
# 钥石入账(summary.keystone_total——逐杀掉落已在冒险内计入,boss_drops 参数为
|
||||
# 摘要归档副本不重复入账)+ 逐通关关次 clear_count +1 与 Boss 首杀标记 + Boss 击杀总数累计
|
||||
# (迭代 11 第①批 T2——同调位里程碑计数源)+ 摘要头部插入(cap 10,最新在前,元素附
|
||||
# keystone_drops 副本——复盘可归因)+ 生涯终身计数自增(迭代 18 卡④——总局数开局即计:
|
||||
# (同调位里程碑计数源)+ 摘要头部插入(cap 10,最新在前,元素附
|
||||
# keystone_drops 副本——复盘可归因)+ 生涯终身计数自增(总局数开局即计:
|
||||
# 三结局同经本口恰计一次;通关局数按 summary.cleared 判定三关全通才计 1。终身计数写入
|
||||
# 单口=本函数,禁第二写入点)。
|
||||
func apply_adventure_summary(summary: Dictionary, boss_drops: Array = []) -> void:
|
||||
@@ -409,7 +409,7 @@ func apply_adventure_summary(summary: Dictionary, boss_drops: Array = []) -> voi
|
||||
var key := str(li)
|
||||
_data["meta"]["level_clear_counts"][key] = int(_data["meta"]["level_clear_counts"].get(key, 0)) + 1
|
||||
_data["meta"]["boss_first_kill"][key] = true
|
||||
_data["meta"]["boss_kill_count"] = boss_kill_count() + cleared # 迭代 11 T2——Boss 恒唯一终点:levels_cleared 即本次冒险 Boss 击杀数(含重复)
|
||||
_data["meta"]["boss_kill_count"] = boss_kill_count() + cleared # Boss 恒唯一终点:levels_cleared 即本次冒险 Boss 击杀数(含重复)
|
||||
var entry: Dictionary[Variant, Variant] = summary.duplicate(true)
|
||||
entry["boss_drops"] = boss_drops.duplicate(true)
|
||||
var arr: Array[Variant] = _data["meta"]["recent_summaries"]
|
||||
@@ -418,7 +418,7 @@ func apply_adventure_summary(summary: Dictionary, boss_drops: Array = []) -> voi
|
||||
arr.pop_back()
|
||||
|
||||
|
||||
# ---- 冒险断点域(迭代 8 卡①——停点粒度续玩快照;内容装配归 AdventureRunner,本类照单承载)----
|
||||
# ---- 冒险断点域(停点粒度续玩快照;内容装配归 AdventureRunner,本类照单承载)----
|
||||
# 空字典 = 无断点合法态。值拷贝进出(禁碰内部结构契约同款)——快照写入后调用方再改不渗入存档态。
|
||||
|
||||
func set_breakpoint(bp: Dictionary) -> void:
|
||||
@@ -452,7 +452,7 @@ func _corrupt_fallback(reason: String) -> bool:
|
||||
|
||||
# 迁移后/低版本档字段补齐 + 退役域清退(改版不弃档:缺新域按默认补齐,既有值保留;
|
||||
# 退役域按默认模型键集白名单收口——换代退役键〔forge_levels/migration_note 等树域旧件〕不流入
|
||||
# 内存不回写盘,开发阶段破坏性废弃口径〔迭代 21 T5,Q3 用户裁:禁迁移禁折算〕)。
|
||||
# 内存不回写盘,开发阶段破坏性废弃口径〔禁迁移禁折算〕)。
|
||||
# 树结构检查先于缺键补齐:换代前结构(tree_struct 标记缺失或更低)→ 树域废弃重置——
|
||||
# tree_nodes/fork_sides 清空、钥石余额等他域零触碰(V16 断言面)。
|
||||
func _normalize(data: Dictionary) -> Dictionary[String, Variant]:
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
class_name AuraSegment
|
||||
extends RefCounted
|
||||
# ② 增益/减益段 Segment(迭代 27 T2——P1-1 结算巨函数拆段;段边界=审计五段〔裁点③甲〕)。
|
||||
# ② 增益/减益段 Segment(P1-1 结算巨函数拆段;段边界=审计五段〔裁点③甲〕)。
|
||||
# 职责:续存实例组合行(本结算新授予的已在授予段逐 grant 出行——增益状态 义务⑦)。
|
||||
# 行发射归宿主编排器(oc)——零算法改动(🔴 零行为变更契约)。
|
||||
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
class_name CombatSegment
|
||||
extends RefCounted
|
||||
# ③ 战斗段 Segment(迭代 27 T2——P1-1 结算巨函数拆段;段边界=审计五段〔裁点③甲〕)。
|
||||
# ③ 战斗段 Segment(P1-1 结算巨函数拆段;段边界=审计五段〔裁点③甲〕)。
|
||||
# 职责:案C 闸复位 + 本轮扫雷事实推导(执拗 streak/连珠矢段数)+ 蓄力层注入 + 主角先手
|
||||
# (含连珠矢分段开单)+ 敌方行动兑现 + 地图伤害账殿后(补提交统一结账)。
|
||||
# 调用序:主角先手 → 逐敌还手 → 地图伤害账殿后〔案C 2026-10-06 用户裁〕;hero hp=0 短路。
|
||||
# 调用序:主角先手 → 逐敌还手 → 地图伤害账殿后〔案C〕;hero hp=0 短路。
|
||||
# 行发射/开单/兑付原语仍归宿主编排器(oc)——本函数体为原编排器 _combat_segment 逐字搬运
|
||||
# (🔴 零行为变更契约)。
|
||||
|
||||
@@ -16,7 +16,7 @@ func _init(host) -> void:
|
||||
|
||||
func run(ri: int) -> void:
|
||||
oc._defer_board_damage = false # 案C 闸复位——战斗段起开单即入队(主角/敌方/补提交的地图单按开单序统一结账)
|
||||
# 本轮扫雷事实推导(迭代 12 T4——执拗 streak/连珠矢段数共用取数面):本轮 safe 事件数=
|
||||
# 本轮扫雷事实推导(执拗 streak/连珠矢段数共用取数面):本轮 safe 事件数=
|
||||
# streak;踩雷即清零(mine_hit 监听同口——执拗「踩雷清零」承载)。攻击在战斗段=授予段后
|
||||
# 同一次 _settle 内,facts 已定。
|
||||
var round_safe := 0
|
||||
@@ -30,14 +30,14 @@ func run(ri: int) -> void:
|
||||
# 蓄力层注入(内容数据驱动扩展批 T3——整备军备「每层下轮首段 +1」/厚积薄发满层 spike/铁壁整备
|
||||
# 减伤——三件 fact charge_layers 同读 action_economy;battle 域每轮刷新)
|
||||
oc._damage.charge_layers = oc.action_economy.charge_layers
|
||||
# ⓪ 主角先手攻击(基础攻击恒存在——2026-09-11 用户裁决;先于敌方行动。案C 后授予段不再产出伤害,
|
||||
# ⓪ 主角先手攻击(基础攻击恒存在;先于敌方行动。案C:授予段不再产出伤害,
|
||||
# 主角进入战斗段时恒存活——先手必兑,除非场上无敌)。
|
||||
# 单分量伤害构成(2026-09-19 充能退役批·用户拍板):主角攻击 = 唯一基础分量(atk,吃掷点含暴击;
|
||||
# 类型 = 主角 attack_type 表值——2026-09-18 营地扩展前置批:量与类型抽象分离;查甲效矩阵对应行取克制修正)。
|
||||
# 单分量伤害构成:主角攻击 = 唯一基础分量(atk,吃掷点含暴击;
|
||||
# 类型 = 主角 attack_type 表值(量与类型抽象分离);查甲效矩阵对应行取克制修正)。
|
||||
# 原"基础 + 穿刺/魔法充能三分量"模型随充能系统整体退役——克制策略归冒险前配置(营地换型 / 未来冒险前参数面),
|
||||
# 局内不再有挖掘攒弹药的第二伤害供给。attack_up 增益经 hero_offense 池乘攻击合计(见池乘区注记)。
|
||||
# 护甲等级掷点批(2026-09-16):基础分量吃掷点判定(含暴击);棋盘来源伤害不掷不折;
|
||||
# 任何伤害落地 ≥ damage_floor(全局 max(1,·)——2026-09-20 用户拍板扩全来源,下限在控制器管线单点收口)。
|
||||
# 护甲等级掷点:基础分量吃掷点判定(含暴击);棋盘来源伤害不掷不折;
|
||||
# 任何伤害落地 ≥ damage_floor(全局 max(1,·) 适用所有来源,下限在控制器管线单点收口)。
|
||||
var hero_alive: bool = int(oc._enemy_system.hero_unit().get("hp", 0)) > 0
|
||||
var hero_target = oc._enemy_system.default_target()
|
||||
if hero_alive and hero_target != null:
|
||||
@@ -50,7 +50,7 @@ func run(ri: int) -> void:
|
||||
for h in oc._hits_of(ht.context.get("fx_attack_hits", []) as Array, "attack_segments"):
|
||||
var segments := mini(int(h.get("cap", 2)), oc._damage.dig_streak / maxi(1, int(h.get("divisor", 3))))
|
||||
for s in segments:
|
||||
# 迭代 20 T9——source_class 中性名(旧件名 feat_chain_arrows 随连珠矢退役;attack_segments 兑付通道保留泛用面)
|
||||
# source_class 用中性名;attack_segments 兑付通道保留泛用面
|
||||
var ct := oc._damage.open_ticket(DamageController.KIND_BOARD_TYPED, "fx_streak_segments",
|
||||
"连击分段", "board", int(h.get("value", 0)), DamageController.TARGET_FRONT_ALIVE, ri,
|
||||
oc.minesweeper.hero_pos(), "pierce")
|
||||
@@ -66,8 +66,7 @@ func run(ri: int) -> void:
|
||||
oc._fatal("敌实例待发动作悬空: %s" % skill_id)
|
||||
continue
|
||||
if String(srow.type) == "buff" or String(srow.type) == "debuff":
|
||||
# 增益/妨害技(新效应词汇批 2026-09-19)= 0 伤害施放行:不掷不折;状态授予即数值兑现,技能冷却与增益持续两计时器分账。
|
||||
# (原"本轮喂的伏击逢施放拍不兑现"读怪奖励随伏击退役一并作废——2026-09-20 八型批)
|
||||
# 增益/妨害技 = 0 伤害施放行:不掷不折;状态授予即数值兑现,技能冷却与增益持续两计时器分账。
|
||||
# 落点由状态行 target_side 路由(buff → 敌方侧 / debuff → 主角侧——容器零改动,proposal-status-vocabulary §1)。
|
||||
var dur: int = int(srow.get("buff_duration", 1))
|
||||
oc._status_effects.apply_effect(String(srow.buff_ref), {"source_class": "enemy_action", "origin_cell": Vector2i(-1, -1), "round_index": ri}, dur)
|
||||
@@ -87,7 +86,7 @@ func run(ri: int) -> void:
|
||||
et["source_unit"] = u
|
||||
et["context"] = {"skill_id": skill_id, "baseline": baseline}
|
||||
oc._damage.submit(et)
|
||||
# 地图伤害兑付(案C 2026-10-06 用户裁——战斗段末:怪物还手后按授予段事件序补提交统一结账;
|
||||
# 地图伤害兑付(案C:战斗段末——怪物还手后按授予段事件序补提交统一结账;
|
||||
# 主角死于怪物 → 地图账不兑,逐单留「未生效」行不作废静默——没生效也要留痕口径;
|
||||
# 补提交后若地图直伤反杀主角,控制器 _halted 作废余单——战斗已败,胜负归判定段)
|
||||
if int(oc._enemy_system.hero_unit().get("hp", 0)) <= 0:
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
class_name DecaySegment
|
||||
extends RefCounted
|
||||
# ④ 衰减段 Segment(迭代 27 T2——P1-1 结算巨函数拆段;段边界=审计五段〔裁点③甲〕)。
|
||||
# ④ 衰减段 Segment(P1-1 结算巨函数拆段;段边界=审计五段〔裁点③甲〕)。
|
||||
# 职责:状态衰减恰一次恒执行含末轮——先兑付后衰减。行发射归宿主编排器(oc)——
|
||||
# 零算法改动(🔴 零行为变更契约)。
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
class_name GrantSegment
|
||||
extends RefCounted
|
||||
# ① 授予段 Segment(迭代 27 T2——P1-1 结算巨函数拆段;段边界=审计五段〔裁点③甲〕)。
|
||||
# ① 授予段 Segment(P1-1 结算巨函数拆段;段边界=审计五段〔裁点③甲〕)。
|
||||
# 职责:结算队列(开格/封印判定/清图)→ 极性路由逐批兑付 + 封印判/军规开单 + round_end 施加 +
|
||||
# 转存收口 + 揭示兑付 + 段界结账。行发射/开单/兑付原语仍归宿主编排器(oc)——
|
||||
# 本段只搬运时序,零算法改动(🔴 零行为变更契约:同种子完整局输出逐位一致)。
|
||||
@@ -17,9 +17,9 @@ func _init(host) -> void:
|
||||
|
||||
func run(ri: int) -> void:
|
||||
# ① 授予段:结算队列(开格/封印判定/清图)→ 极性路由逐批兑付(伤害形态单据暂存——见 _submit 案C 闸)。
|
||||
# 判定基线 = 轮末在场封印(发射面已过滤——被挖开的封印已随挖掘擦除零兑付,封印不可逆批 2026-09-19;
|
||||
# 判定基线 = 轮末在场封印(发射面已过滤——被挖开的封印已随挖掘擦除零兑付;
|
||||
# 开格事实不可撤)。
|
||||
oc._defer_board_damage = true # 案C:授予段伤害单据只暂存不结账(2026-10-06 用户裁——兑付时点=战斗段末)
|
||||
oc._defer_board_damage = true # 案C:授予段伤害单据只暂存不结账(兑付时点=战斗段末)
|
||||
for evt in oc._facts_reveals:
|
||||
if not bool(evt.get("resource", false)):
|
||||
continue # 封锁格(initial 通道)恒无资源——不入账(战斗结算 义务②)
|
||||
@@ -28,13 +28,13 @@ func run(ri: int) -> void:
|
||||
continue
|
||||
if evt.kind == "safe":
|
||||
if bool(evt.get("dug_under_seal", false)):
|
||||
# 封印不可逆四情况④(2026-09-19 用户拍板):插安全格再挖开 = 零惩罚零奖励——正向兑付
|
||||
# 封印不可逆四情况④:插安全格再挖开 = 零惩罚零奖励——正向兑付
|
||||
# 作废(归因行在案);标空判定已随封印擦除自然缺席(被挖的封印不在轮末在场基线)。
|
||||
oc._emit_row("reward_ineffective", "safe_cell", evt.cell, ri, "奖励作废(封印格直接挖开——零惩罚零奖励)", 0, {}, "")
|
||||
continue
|
||||
# dig_reveal 发射+施加(内容数据驱动扩展批 T3——桶序固定承载等价:
|
||||
# 乘区收集 → 正向兑付 → 计数器 → 追加产出(深挖)→ 奥术矢。
|
||||
# 迭代 20 T1——payload 扩三事实:is_edge_cell〔边陲矿工〕/is_blank_zero〔跳格采掘〕/
|
||||
# payload 携三事实:is_edge_cell〔边陲矿工〕/is_blank_zero〔跳格采掘〕/
|
||||
# new_resource_type〔地质学家——快照 cell_type 首见判定,见后集登记〕)
|
||||
var board_b: BoardData = oc.minesweeper.current_board()
|
||||
var snap_pre = evt.get("reward_snapshot")
|
||||
@@ -64,7 +64,7 @@ func run(ri: int) -> void:
|
||||
for h in dr_hits:
|
||||
if String(h.get("kind", "")) == "counter_add":
|
||||
_apply_counter_hit(h)
|
||||
# 揭示效果入队(迭代 20 T1——地质学家/跳格采掘 reveal_tiles 订阅面;授予段末
|
||||
# 揭示效果入队(地质学家/跳格采掘 reveal_tiles 订阅面;授予段末
|
||||
# _flush_pending_reveals 统一掷格兑付)
|
||||
if not oc._hits_of(dr_hits, "reveal_tiles").is_empty():
|
||||
oc._fx_enqueue_reveal()
|
||||
@@ -85,7 +85,7 @@ func run(ri: int) -> void:
|
||||
# 标中不发(封印已随挖掘擦除、不入轮末判定基线)。
|
||||
# mine_hit 发射+施加(内容数据驱动扩展批 T3——桶序固定承载等价:
|
||||
# 减半收集 → 负向兑付〔带减半系数〕→ 反打开单 → 连击清零计数器。
|
||||
# 迭代 20 T1——发射前移+payload 扩 dug_under_seal〔破封一击〕;新增施加面:
|
||||
# 发射时点前移并扩 payload dug_under_seal〔破封一击〕;施加面:
|
||||
# damage_reduce〔安全帽耗层减伤——value_counter 层值,一次清零〕/ heal〔血蜜〕/
|
||||
# blast_scaled〔陷阱爆发——雷伤×倍率全体敌〕/ bonus_delta〔血税〕/ reveal_tiles〔见血开窍〕)
|
||||
var mine_power := int((snap as Dictionary).get("power", 0))
|
||||
@@ -129,7 +129,7 @@ func run(ri: int) -> void:
|
||||
int(h.get("value", 0)), DamageController.TARGET_ALL_ALIVE, ri, evt.cell, "", 0, "", {})
|
||||
oc._submit(nct, false)
|
||||
# 地形变异 兑付段(授予段尾——触发枚举 on_open:挖开即伤、chain 通道同口径;伤害单据案C 暂存到
|
||||
# 战斗段末兑付〔2026-10-06 用户裁〕;信息型 hide_number 不进结算——呈现层读取,见 board_view 数字遮蔽;
|
||||
# 战斗段末兑付〔案C〕;信息型 hide_number 不进结算——呈现层读取,见 board_view 数字遮蔽;
|
||||
# 危险格恒无变异——布置段排除,双层叠罚不存在;封印格直挖照吃变异伤害——地形后果同 mine_hit 口径,
|
||||
# 非奖励不作废;吞噬流资源兑付〔荆棘编织/地脉补偿〕中伤害形态行同样经案C闸暂存,非伤害形态照常当场)
|
||||
for evt in oc._facts_reveals:
|
||||
@@ -138,8 +138,8 @@ func run(ri: int) -> void:
|
||||
var vrow: Dictionary[Variant, Variant] = oc.db.variation.row_by_id(String(vid))
|
||||
if String(vrow.get("effect", "")) != "flat_damage":
|
||||
continue # 触发词表按 effect 路由:flat_damage=on_open 兑现;hide_number/on_open 外组合 = 数据行新词表未接线(显式忽略面)
|
||||
# 地形变异伤害单据(伤害管线单据化批 2026-09-24)——无类型必中,下限保底在控制器管线内
|
||||
# 迭代 20 T1——damage_taken 发射前移(单据开立前):damage_reduce〔背水——hp_ratio when
|
||||
# 地形变异伤害单据——无类型必中,下限保底在控制器管线内
|
||||
# damage_taken 发射在单据开立前:damage_reduce〔背水——hp_ratio when
|
||||
# 面〕先收集再按减后值开单;counter_add〔保险丝/石肤〕+ ac_bonus〔石肤〕照常施加;
|
||||
# grant_material〔荆棘/地脉〕移到单据提交后(与迁移前兑付值序一致——收益不因减伤变化)
|
||||
var dt_amount := int(vrow.get("effect_value", 0))
|
||||
@@ -162,7 +162,7 @@ func run(ri: int) -> void:
|
||||
_apply_material_hit(h, evt.cell, ri)
|
||||
for evt in oc._facts_seals:
|
||||
_judge_seal(evt, ri)
|
||||
# 战封印军规开单段(迭代 12 T3 封印流——seal_judged 批后统一开单:轮末每面封印命中对最前存活敌
|
||||
# 战封印军规开单段(封印流:seal_judged 批后统一开单:轮末每面封印命中对最前存活敌
|
||||
# 结算 board_typed 必中伤害〔复用零新配方;「封印所在巢穴敌」简化为 default_target 最前存活敌——
|
||||
# 台账披露待追认〕;完美布防=本轮封印批全中(hit>0 且 miss==0)时封印伤 ×mult——全对推理奖励)。
|
||||
# 伤害单据案C 暂存战斗段末兑付。
|
||||
@@ -205,7 +205,7 @@ func run(ri: int) -> void:
|
||||
var mg_layers := oc._fx.counter(String(h.get("per", "")))
|
||||
var mg_total := int(h.get("value", 0)) * mg_layers
|
||||
if mg_total > 0:
|
||||
# 迭代 20 T9——source_class 中性名(旧件名 feat_magma_body 随熔火之躯退役;per-counter 反打通道保留泛用面)
|
||||
# source_class 用中性名;per-counter 反打通道保留泛用面
|
||||
var mg_t := oc._damage.open_ticket(DamageController.KIND_BOARD_BLAST, "fx_blast_counter",
|
||||
"积层反噬 ×%d" % mg_layers, "board", mg_total,
|
||||
DamageController.TARGET_ALL_ALIVE, ri, oc.minesweeper.hero_pos(), "", 0, "", {})
|
||||
@@ -213,7 +213,7 @@ func run(ri: int) -> void:
|
||||
for h in re_fx:
|
||||
if String(h.get("kind", "")) == "counter_add":
|
||||
_apply_counter_hit(h)
|
||||
# 迭代 20 T1——round_end 新施加面:grant_gp〔爆破会计/收盘——escrow 战斗域缓冲,随 gp_dropped 上抛〕/
|
||||
# round_end 施加面:grant_gp〔爆破会计/收盘——escrow 战斗域缓冲,随 gp_dropped 上抛〕/
|
||||
# grant_ap mode:queue〔封印回响——返 1 AP 下轮,queue_ap_delta 通道与轮始加成同面〕
|
||||
for h in re_fx:
|
||||
if String(h.get("kind", "")) == "grant_gp":
|
||||
@@ -228,11 +228,11 @@ func run(ri: int) -> void:
|
||||
rap_pct if rap_pct > 0 else 100)
|
||||
if oc._facts_path == "cleared":
|
||||
oc._grant_clear_reward(ri)
|
||||
oc._flush_pending_reveals(ri) # 迭代 20 T1——揭示效果兑付(授予段末统一掷格,防轮 facts 迭代重入)
|
||||
oc._flush_pending_reveals(ri) # 揭示效果兑付(授予段末统一掷格,防轮 facts 迭代重入)
|
||||
oc._damage.flush() # 授予段界结账(案C 后本段伤害单据全数暂存,此处对空队 no-op——保留作防御性段界)
|
||||
|
||||
|
||||
# 封印判定(迭代 12 T3 封印流本体随段迁入——原编排器 _judge_seal 逐字搬运,仅宿主引用加前缀;
|
||||
# 封印判定(封印流本体——自编排器 _judge_seal 迁入,宿主引用加前缀;
|
||||
# 唯一调用面=本段 run 的 facts_seals 循环)。
|
||||
func _judge_seal(evt: Dictionary, ri: int) -> void:
|
||||
var cell: Vector2i = evt.cell
|
||||
@@ -247,14 +247,14 @@ func _judge_seal(evt: Dictionary, ri: int) -> void:
|
||||
"terrain_kind": oc._harmful_adjacent(cell)})
|
||||
# 标中殉爆(迭代 11 同调批 T5 → 内容数据驱动扩展批 T4:订阅施加——行 triggers open_ticket
|
||||
# channel blast value_from magnitude;每标中一面封印全体存活敌吃 N 群伤,无类型走 blast 通道。
|
||||
# 迭代 20 T1——封印爆裂〔迁入专长〕同面消费:行 triggers open_ticket channel blast)
|
||||
# 封印爆裂〔专长〕同面消费:行 triggers open_ticket channel blast)
|
||||
for h in oc._hits_of(fh_hits, "open_ticket"):
|
||||
if String(h.get("channel", "")) != "blast":
|
||||
continue
|
||||
var fbt := oc._damage.open_ticket(DamageController.KIND_BOARD_BLAST, "feat_seal_burst", "封印爆裂", "board",
|
||||
int(h.get("value", 0)), DamageController.TARGET_ALL_ALIVE, ri, cell, "", 0, "highlight", {})
|
||||
oc._submit(fbt, false)
|
||||
# 迭代 20 T1——标中新施加面:counter_add〔血税 标中+1 攻——bonus_delta atk 轴〕/
|
||||
# 标中施加面:counter_add〔血税 标中+1 攻——bonus_delta atk 轴〕/
|
||||
# ac_bonus〔封印桩 本轮 AC+N〕/ preview_area〔示警烟火 3×3 资源类型预告〕/
|
||||
# remove_mines〔封印匠 每第 3 次该危险格中立移除——mod_eq 闸在行 when〕。
|
||||
# (净化工兵 rewrite_board 分支随老 27 件退役摘除——效果类型同批除名,T1 落法 commit 留痕)
|
||||
@@ -282,7 +282,7 @@ func _judge_seal(evt: Dictionary, ri: int) -> void:
|
||||
if oc.treasure_inventory != null and not types.is_empty():
|
||||
oc.treasure_inventory.apply_overlay("reward_preview", board_p.neighbors8(cell))
|
||||
oc._emit_row("fx_preview_area", "", cell, ri, "示警烟火·周围资源类型", 0, {"types": types}, "")
|
||||
# 雷区图谱(迭代 12 T3 封印流——标中雷额外获得一份格面资源:封印开始产出对标开格;
|
||||
# 雷区图谱(封印流——标中雷额外获得一份格面资源:封印开始产出对标开格;
|
||||
# 复用 grant 单点,额外兑付走同一 _grant_routed 正向路由)
|
||||
for h in fh_hits:
|
||||
if String(h.get("kind", "")) == "grant_material":
|
||||
@@ -300,7 +300,7 @@ func _judge_seal(evt: Dictionary, ri: int) -> void:
|
||||
if oc._perfect_hit_false == 1:
|
||||
hl = "perfect_broken" # 本板完美谓词已破的归因锚
|
||||
oc._emit_row("seal_miss", "seal_miss", cell, ri, "封印未中", 0, {}, hl)
|
||||
# 危险直觉·守挂点(迭代 21 T3——seal_missed 发射:契约 12 触发主语=「当回合发生的标错
|
||||
# 危险直觉·守挂点(seal_missed 发射:契约 12 触发主语=「当回合发生的标错
|
||||
# 动作」——去重闸=新旧标错判别器〔后续轮重判旧封印被闸挡回零触发〕;双条件②「结算时封印仍在
|
||||
# 场」由发射面既有过滤承载〔被挖开的封印已随挖掘擦除零兑付〕;grant_ap mode:queue 下一轮
|
||||
# 开轮兑现——末轮判出无下一轮=合法作废自动成立〔编排器每战新建,队列不跨战〕;限次=合成行
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
class_name VerdictSegment
|
||||
extends RefCounted
|
||||
# ⑤ 判定段 Segment(迭代 27 T2——P1-1 结算巨函数拆段;段边界=审计五段〔裁点③甲〕)。
|
||||
# ⑤ 判定段 Segment(P1-1 结算巨函数拆段;段边界=审计五段〔裁点③甲〕)。
|
||||
# 职责:防御性优先级 hero 先判(双死结构不可达的纵深防御)→ 全量状态快照(despawn 前
|
||||
# 组装——终局含 result)→ 终局行+收束分派 / 下一轮出口分发。
|
||||
# 返回值 = 裁定("victory"/"defeat"/"continue")——宿主 _settle 据此保持旧早退语义
|
||||
|
||||
+10
-10
@@ -52,17 +52,17 @@ func inventory_count() -> int:
|
||||
return _items.size()
|
||||
|
||||
|
||||
# 持有数(迭代 10 奖励重构批——同调位上限判定读数:只计未消耗件;Used 不可逆即不再持有)
|
||||
# 同调位守卫谓词——迭代 11 第①批 T7 退役(读取源 transitional_sync_slots 过渡键已随表退役;
|
||||
# 持有数(同调位上限判定读数:只计未消耗件;Used 不可逆即不再持有)
|
||||
# 同调位守卫谓词(读取源 transitional_sync_slots 过渡键已随表退役;
|
||||
# 容量闸正主 = 营地装配面 Attunement.slots 满位禁选。函数体保留空壳防四散引用误删遗漏——
|
||||
# T8 冒烟「过渡键读取点归零」断言覆盖)
|
||||
# 冒烟「过渡键读取点归零」断言覆盖)
|
||||
|
||||
|
||||
# 持有件记录(迭代 10 追加批——奖励节点替换子交互数据源:只列未消耗件,handle 定位丢弃目标)
|
||||
# 持有件记录(奖励节点替换子交互数据源:只列未消耗件,handle 定位丢弃目标)
|
||||
# 丢弃(同调位替换交互节点渠道——腾位收新;与 Used 消费分轨的一次性移除,快照不再含)
|
||||
# 板级零信息可用性预判(图标置灰/入口禁用判据——GDD「整张图都没东西可看」级):
|
||||
# 真视恒可用(零雷窗口=信息非空);AP 类恒可用(组合判据取 OR——任一效果有信息即可用);
|
||||
# 纯预读行 = 板上还有未开格才可用。(积点件不入使用面——反哺批⑤自动兑付无玩家入口)
|
||||
# 纯预读行 = 板上还有未开格才可用。(积点件不入使用面——自动兑付无玩家入口)
|
||||
func _usable_now(item_id: String, board_has_preview_info: Callable) -> bool:
|
||||
var row: Dictionary[Variant, Variant] = _item_row(item_id)
|
||||
if row.is_empty():
|
||||
@@ -77,7 +77,7 @@ func _item_row(item_id: String) -> Dictionary:
|
||||
if _db == null:
|
||||
return {}
|
||||
if _db.has_method("treasure_item_row"):
|
||||
return _db.treasure_item_row(item_id) # 表访问收口(2026-10-02——正式面走 ConfigDB 访问器)
|
||||
return _db.treasure_item_row(item_id) # 表访问收口(正式面走 ConfigDB 访问器)
|
||||
return _db.treasure_items.row_by_id(item_id) # 桩表面(单测 StubDB 无访问器——直读保留,随测试面演进退役)
|
||||
|
||||
# 不标具体类型原因:单元测试注入 StubDB 桩表(treasure_items_test.gd)——typed 赋值会运行期拒收,保守保持 null 注
|
||||
@@ -105,12 +105,12 @@ func validate_use(handle: int, target: Vector2i, board_size: Vector2i, cell_unre
|
||||
var kinds: Array[Variant] = row.get("effect_kinds", [])
|
||||
var resolved: Array[Variant] = []
|
||||
if kinds.has("mine_reveal") or kinds.has("reward_preview") or kinds.has("reveal_tiles") or kinds.has("safe_dig"):
|
||||
# 空间效果(迭代 22 秘宝批 T3 扩面——真视 reveal_tiles/天眼复合 safe_dig 入空间解析;
|
||||
# 空间效果(真视 reveal_tiles/天眼复合 safe_dig 入空间解析;
|
||||
# 真视窄域护栏 REVEAL_KINDS 同守):
|
||||
if target.x < 0 or target.y < 0 or target.x >= int(board_size.x) or target.y >= int(board_size.y):
|
||||
return _reject("目标越板")
|
||||
if String(row.get("target", "")) == "cell":
|
||||
# 单格档(迭代 22 T1——线香真视单格;≤3×3 窄域契约内的点目标)
|
||||
# 单格档(线香真视单格;≤3×3 窄域契约内的点目标)
|
||||
resolved.append(target)
|
||||
else:
|
||||
# 3×3 窗口 ∩ 板内格(边缘受理——护栏只缩不扩)
|
||||
@@ -172,7 +172,7 @@ func consume(handle: int) -> Dictionary:
|
||||
return rec.duplicate()
|
||||
|
||||
|
||||
# ---- 断点快照(迭代 8 卡①——关卡域库存随停点入档/回局)----
|
||||
# ---- 断点快照(关卡域库存随停点入档/回局)----
|
||||
# 快照面 = {item_id, source_class, origin_cell:[x,y], round_index, used}(JSON 安全形——Vector2i 转
|
||||
# 数组、键全 String);handle 不入档(受理序句柄,回局时按序重排)。叠加层集不入快照(战斗域
|
||||
# 随图消亡——停点处无战斗在飞,天然空集)。
|
||||
@@ -222,7 +222,7 @@ func _reject(reason: String) -> Dictionary:
|
||||
return {"ok": false, "reason": reason, "resolved": [], "item_id": "", "kinds": [], "magnitude": 0}
|
||||
|
||||
|
||||
# 探针出口(T7 收拢——断言面:库存序/叠加层集/拒绝原因)
|
||||
# 探针出口(断言面:库存序/叠加层集/拒绝原因)
|
||||
func probe() -> Dictionary:
|
||||
return {
|
||||
"count": _items.size(),
|
||||
|
||||
+24
-24
@@ -5,16 +5,16 @@
|
||||
class_name UnitContainer
|
||||
extends RefCounted
|
||||
|
||||
# 死亡事件(伤害管线单据化批 2026-09-24 用户裁决③——单位自行宣布死亡):apply_damage 扣血归零、
|
||||
# 死亡事件(单位自行宣布死亡):apply_damage 扣血归零、
|
||||
# alive 翻 false 的同一时点发出;载荷 = 单位句柄(Dictionary 引用,is_hero 区分主角/敌)。
|
||||
# 消费方 = 伤害控制器(怪死 → 其未结单据作废〔死亡即不动手〕;主角死 → 兑付终止封印)。
|
||||
signal unit_died(u: Dictionary)
|
||||
|
||||
var db: ConfigDb
|
||||
# 单位容器私有(E3 封装收口 2026-09-13):hero/enemies 原始数组不再外露——读取经 hero_unit()/
|
||||
# 单位容器私有(E3 封装收口):hero/enemies 原始数组不外露——读取经 hero_unit()/
|
||||
# all_enemies() 投影面(死亡残位查询包含在内),写入只经 apply_damage/heal/set_pending_action/commit_action/
|
||||
# tick_cooldowns/spawn/despawn。
|
||||
# 句柄语义不变:投影返回的 Dictionary 引用即单位句柄(同 query_alive 先例)。
|
||||
# 句柄语义:投影返回的 Dictionary 引用即单位句柄(同 query_alive)。
|
||||
var _hero: Dictionary[String, Variant] = {} # {hp, max_hp, atk, is_hero: true}
|
||||
var _enemies: Array[Dictionary] = [] # 行主序生成序;元素 = 单位 Dictionary
|
||||
var _spawned := false
|
||||
@@ -25,12 +25,12 @@ func _init(db_ref: ConfigDb) -> void:
|
||||
db = db_ref
|
||||
|
||||
|
||||
# E4 收口(2026-09-13):加成值经参数显式传入(atk_override/max_hp_override,-1 = 表值兜底)——
|
||||
# 此前调用方写 db.hero 表值再由本类读表 = 共享可变单例传参(双写叠加 6→18→54 教训根源)。
|
||||
# enemy_atk_override(2026-09-14 敌方攻击区间批):遭遇骰同流的 spawn 期整战持久 roll 结果
|
||||
# E4 收口:加成值经参数显式传入(atk_override/max_hp_override,-1 = 表值兜底)——
|
||||
# 调用方写 db.hero 表值再由本类读表 = 共享可变单例传参(双写叠加教训根源)。
|
||||
# enemy_atk_override:遭遇骰同流的 spawn 期整战持久 roll 结果
|
||||
# (-1 = 表值定值路径;≥0 = 行值替换后再乘关卡 atk_mult)。敌人系统 零随机纪律保持。
|
||||
# 行集受理换代(2026-10-07 行集多行批 P1 甲):unit_count_overrides / enemy_atk_overrides 改**逐行数组**
|
||||
# (下标=行集行序,长度短于行集时缺省 -1 = 该行行值路径;空数组 = 全行定值——旧调用点零适配)。
|
||||
# 行集受理:unit_count_overrides / enemy_atk_overrides 为**逐行数组**
|
||||
# (下标=行集行序,长度短于行集时缺省 -1 = 该行行值路径;空数组 = 全行定值)。
|
||||
# 逐行实例化、生成序 = 文件行序(行序即契约,敌人系统 v0.15);行集实例总数(各行传入数量求和)
|
||||
# ≤ ENEMY_ROWSET_INSTANCE_MAX 守卫 fail-fast。
|
||||
func spawn(level_index: int, node_depth: int, hero_hp: int, unit_count_overrides: Array = [],
|
||||
@@ -39,7 +39,7 @@ func spawn(level_index: int, node_depth: int, hero_hp: int, unit_count_overrides
|
||||
if _spawned:
|
||||
push_warning("[敌人系统] Alive 相位重复 spawn(相位守卫)")
|
||||
return false
|
||||
# 行集数组口径(2026-10-07 行集多行批——resolver 返回该坐标全部行、文件序)
|
||||
# 行集数组口径(resolver 返回该坐标全部行、文件序)
|
||||
var rowset: Array[Variant] = db.resolve_enemy_rowset(level_index, node_depth)
|
||||
if rowset.is_empty():
|
||||
push_error("[敌人系统] 遭遇行解析失败/越域 (%d,%d)" % [level_index, node_depth])
|
||||
@@ -50,10 +50,10 @@ func spawn(level_index: int, node_depth: int, hero_hp: int, unit_count_overrides
|
||||
if hero_hp > eff_max_hp or hero_hp <= 0:
|
||||
push_warning("[敌人系统] hero_hp 入参越界(EC16): %d" % hero_hp)
|
||||
return false
|
||||
# max_hp = 生效上限(非入参 hero_hp)——2026-09-11 关卡扩展批修复:跨战斗携带 HP 时上限不得压扁
|
||||
# (恢复点/治疗包的满血守卫与治疗上限随之失效——切片登记接口语义澄清)
|
||||
_hero = {"hp": hero_hp, "max_hp": eff_max_hp, "atk": eff_atk, "is_hero": true, "alive": true, "display_name": "探险者", "enemy_id": "hero", "armor_type": hero_armor_type if hero_armor_type != "" else db.forge_default_armor_type(), "attack_type": hero_attack_type if hero_attack_type != "" else db.forge_default_attack_type()} # 护甲/攻击类型轴快照(迭代 23 T2——hero.tres 两轴删除后改读注入值,空串回退锻造工坊配置默认值 normal/light=行为连续;甲效矩阵消费面)
|
||||
_enemies.clear() # 难度曲线与Boss DifficultyTable 修饰(2026-09-12 结构位兑现):施加于行值、行值快照之前——MVP 空表 = 恒 1.0 无行为(AC-10);混编行集内全部敌种同乘((L,D) 精确匹配一次取值——F9 同乘不改变异,敌人系统 v0.15)
|
||||
# max_hp = 生效上限(非入参 hero_hp)——跨战斗携带 HP 时上限不得压扁
|
||||
# (恢复点/治疗包的满血守卫与治疗上限随之失效)
|
||||
_hero = {"hp": hero_hp, "max_hp": eff_max_hp, "atk": eff_atk, "is_hero": true, "alive": true, "display_name": "探险者", "enemy_id": "hero", "armor_type": hero_armor_type if hero_armor_type != "" else db.forge_default_armor_type(), "attack_type": hero_attack_type if hero_attack_type != "" else db.forge_default_attack_type()} # 护甲/攻击类型轴快照(读注入值,空串回退锻造工坊配置默认值 normal/light;甲效矩阵消费面)
|
||||
_enemies.clear() # 难度曲线与Boss DifficultyTable 修饰:施加于行值、行值快照之前——MVP 空表 = 恒 1.0 无行为(AC-10);混编行集内全部敌种同乘((L,D) 精确匹配一次取值——F9 同乘不改变异,敌人系统 v0.15)
|
||||
var mod: Dictionary[Variant, Variant] = db.resolve_difficulty_modifiers(level_index, node_depth)
|
||||
var hp_mult: float = float(mod.get("hp_mult", 1.0))
|
||||
var atk_mult: float = float(mod.get("atk_mult", 1.0))
|
||||
@@ -88,7 +88,7 @@ func spawn(level_index: int, node_depth: int, hero_hp: int, unit_count_overrides
|
||||
"weight": int(entry.get("weight", 0)),
|
||||
"cooldown_left": 0,
|
||||
}
|
||||
# Boss 半血解锁技(迭代14④ T3——unlock_hp_pct 随池模板快照进实例池;缺省无字段=无门槛;
|
||||
# Boss 半血解锁技(unlock_hp_pct 随池模板快照进实例池;缺省无字段=无门槛;
|
||||
# unlocked=单向闩标记,初始锁定)
|
||||
if entry.has("unlock_hp_pct"):
|
||||
snap_entry["unlock_hp_pct"] = int(entry.get("unlock_hp_pct", 0))
|
||||
@@ -103,11 +103,11 @@ func spawn(level_index: int, node_depth: int, hero_hp: int, unit_count_overrides
|
||||
"atk": maxi(0, int(round(float(eff_row_atk) * atk_mult))),
|
||||
"pool": pool_snap, # 实例池(模板快照 + 冷却状态——独立摇号互不干涉)
|
||||
"pending_skill": "", # 本拍摇定动作(开轮摇号写入;兑现前死亡 = 作废不 commit)
|
||||
"roll_seq": 0, # 单敌摇号拍序(诊断标签——并流后不参与派生,2026-09-27 统一流裁决)
|
||||
"roll_seq": 0, # 单敌摇号拍序(诊断标签——不参与派生)
|
||||
"basic_streak": 0, # 保底计数:连续执行普攻的拍数(执行上报驱动)
|
||||
"armor_type": String(row.get("armor_type", "light")), # 护甲类型快照(克制矩阵消费面——战斗结算·战斗段;2026-09-15 命名统一批:light/ward/heavy)
|
||||
"armor_class": int(row.get("armor_class", 0)), # 护甲等级快照(掷点判定阈值基数——战斗结算·战斗段;2026-09-16 护甲等级掷点批)
|
||||
"attack_type": String(row.get("attack_type", "normal")), # 攻击类型快照(克制矩阵消费面——敌方伤害单据第 9 参;迭代 24 N3 轴开列,缺省 normal 向后兼容)
|
||||
"armor_type": String(row.get("armor_type", "light")), # 护甲类型快照(克制矩阵消费面——战斗结算·战斗段:light/ward/heavy)
|
||||
"armor_class": int(row.get("armor_class", 0)), # 护甲等级快照(掷点判定阈值基数——战斗结算·战斗段)
|
||||
"attack_type": String(row.get("attack_type", "normal")), # 攻击类型快照(克制矩阵消费面——敌方伤害单据第 9 参;缺省 normal)
|
||||
"alive": true,
|
||||
"is_hero": false,
|
||||
})
|
||||
@@ -130,9 +130,9 @@ func query_alive(side: String) -> Array:
|
||||
return out
|
||||
|
||||
|
||||
# ---- 实例池状态机(敌人技能池批——全部确定性状态,随机在 战斗结算换算层)----
|
||||
# ---- 实例池状态机(全部确定性状态,随机在 战斗结算换算层)----
|
||||
|
||||
# 池条目锁定判定(迭代14④ Boss 半血解锁——带 unlock_hp_pct 且未过闩 = 锁定:双面过滤不进
|
||||
# 池条目锁定判定(Boss 半血解锁——带 unlock_hp_pct 且未过闩 = 锁定:双面过滤不进
|
||||
# 可摇集合与保底特殊技集合;解锁后即可摇,冷却初值 0)
|
||||
func _entry_locked(e: Dictionary) -> bool:
|
||||
return e.has("unlock_hp_pct") and not bool(e.get("unlocked", false))
|
||||
@@ -154,7 +154,7 @@ func rollable_skills(u: Dictionary) -> Array:
|
||||
|
||||
|
||||
# 特殊技集合(保底专用——proposal §2.1:必出特殊技、特殊技内按权重、不受冷却限制):type ≠ 普攻的全部池条目。
|
||||
# 锁定条目(半血未解锁)双面过滤——保底不得逼出未解锁技(迭代14④ T3)。
|
||||
# 锁定条目(半血未解锁)双面过滤——保底不得逼出未解锁技。
|
||||
func special_skills(u: Dictionary) -> Array:
|
||||
if u.is_empty() or _despawned or u.get("is_hero", false):
|
||||
_diag("special_skills 失效句柄")
|
||||
@@ -169,7 +169,7 @@ func special_skills(u: Dictionary) -> Array:
|
||||
return out
|
||||
|
||||
|
||||
# 半血解锁闩检查(迭代14④ T3——开轮摇号前逐敌检查;hp ≤ round(max_hp × unlock_hp_pct/100) →
|
||||
# 半血解锁闩检查(开轮摇号前逐敌检查;hp ≤ round(max_hp × unlock_hp_pct/100) →
|
||||
# 一次过门、单向闩〔实例标记,不解锁回退〕。门=确定性谓词零随机;返回本拍新解锁的 skill_id 列表
|
||||
# 供编排层出归因行〔每技恰一次——闩已置位不再返回〕)
|
||||
func check_skill_unlocks(u: Dictionary) -> Array:
|
||||
@@ -257,7 +257,7 @@ func apply_damage(u: Dictionary, n: int) -> bool:
|
||||
u.hp = maxi(0, u.hp - n)
|
||||
if u.hp == 0:
|
||||
u.alive = false # Dead 不可逆(死亡判定时机归 战斗结算)
|
||||
unit_died.emit(u) # 死亡事件——单位自行宣布(伤害管线单据化批 2026-09-24 用户裁决③)
|
||||
unit_died.emit(u) # 死亡事件——单位自行宣布
|
||||
return true
|
||||
|
||||
|
||||
@@ -281,7 +281,7 @@ func default_target():
|
||||
|
||||
# ---- 只读投影面(E3 封装收口:外部读单位经此,禁直读容器数组)----
|
||||
|
||||
# 主角句柄(未 spawn/despawn 后 = {})——句柄即 Dictionary 引用(同 query_alive 先例)
|
||||
# 主角句柄(未 spawn/despawn 后 = {})——句柄即 Dictionary 引用(同 query_alive)
|
||||
func hero_unit() -> Dictionary:
|
||||
return _hero
|
||||
|
||||
|
||||
@@ -4,11 +4,11 @@
|
||||
class_name ActionEconomyTable
|
||||
extends Resource
|
||||
|
||||
@export var ap_per_round: int = 6 # [1,32] knob(默认值 = 现行表值基线,2026-09-16 AP 压速批 8→6 随动)
|
||||
@export var ap_per_round: int = 6 # [1,32] knob(默认值 = 现行表值基线,随 AP 压速调整)
|
||||
@export var dig_ap_cost: int = 1 # [1,8] knob(域下限 1 = 零预算系统失效拒绝)
|
||||
@export var seal_place_ap_cost: int = 1 # [0,8] knob(seal_pull_ap_cost 随撤封印退役批 2026-09-19 删除)
|
||||
@export var rap_refresh_cost: int = 6 # [1,64] knob(默认值随 AP 压速批 8→6 随动)
|
||||
@export var refresh_cap_per_round: int = 2 # [1,8] knob(2026-09-12 轮内刷新批——每轮刷新兜底上限,Active/Waiting 合计)
|
||||
@export var seal_place_ap_cost: int = 1 # [0,8] knob(无 seal_pull_ap_cost 字段——拔印不占 AP)
|
||||
@export var rap_refresh_cost: int = 6 # [1,64] knob(默认值随 AP 压速调整)
|
||||
@export var refresh_cap_per_round: int = 2 # [1,8] knob(每轮刷新兜底上限,Active/Waiting 合计)
|
||||
@export var move_ap_cost: int = 0 # 冻结 0(P4 氛围承载)
|
||||
@export var chain_ap_cost: int = 0 # 冻结 0(v2 定稿)
|
||||
|
||||
|
||||
@@ -9,44 +9,44 @@ extends Resource
|
||||
## 「奇物」(一次冒险内各渠道给予玩家的增益总称)。定义见 design/gdd/systems/冒险循环/奇物.md。
|
||||
## 它们是它的分类轴上的枚举(专长 / 属性值提升 / 资源),不是三套系统;
|
||||
## 本表承载属性值提升与资源的种类配额与件值。
|
||||
## (2026-10-05 命名统一 + v2.0 改名:旧称「数值件」→ 属性提升;顶层「特性」→「奖励」→「奇物」。
|
||||
## 常量与字段随之前缀 curio_*,**尚未**随顶层改名换到 reward_*——见 06-待办清单。
|
||||
## (顶层总称=奇物;属性值提升与资源是本表承载的两类。
|
||||
## 常量与字段前缀 curio_*,**尚未**换到 reward_*——见 06-待办清单。
|
||||
## 分类轴三个枚举:属性值提升〔生命上限 / AC / 攻击〕;资源〔行动点上限 /
|
||||
## 刷新次数 / GP / 回复,命名待定〕——本表两类混编;刷新次数与专长获取两项尚未入表。
|
||||
## 类内异质性:GP与回复是即时兑付,不进持有集、不持续生效。)
|
||||
## 三选一提升池 kind 词表(封闭——断点附随件与牌库剔除态依赖它;扩件走本表+registry 修订)。
|
||||
## treasure_item 池种退役(迭代 11 第①批 T7——同调批出池);heal 恢复件新入池(v2.2 令牌池追认 b)。
|
||||
## 迭代 20 T4 资源凭证入池五件;迭代 31 卡② 凭证件裁削批(用户 2026-10-18 逐裁)两件出池:
|
||||
## treasure_item 不在池种内(已出池);heal 恢复件在池(v2.2 令牌池口径)。
|
||||
## 资源凭证入池五件,两件出池:
|
||||
## 同调电池整件删除(呈现层零接线、玩家无分配途径——有 API 有账本唯独没有入口);
|
||||
## 完美保险整件迁出资源类归专长(它给的是规则豁免而非可调动的余量——判据见奇物.md §1.1,
|
||||
## 落表 feats.tres 第 30 行,效果类型 forgive_seal_miss)。留任 rap 储备金袋/time_sand 时沙。
|
||||
const CURIO_KINDS: Array[String] = ["attack", "max_hp", "armor", "gp", "ap", "heal", "refresh",
|
||||
"rap", "time_sand"] # T6 扩第七类(refresh=资源类成员——作为奇物授予的三选一重抽次数,作用对象=三选一界面非角色;见 registry refresh_boundary)
|
||||
## 令牌档位词表(迭代 20 T2 换代——跨层四级稀有度框架〔01-核心循环 v1.5 档名映射:普通→普通、
|
||||
"rap", "time_sand"] # 第七类(refresh=资源类成员——作为奇物授予的三选一重抽次数,作用对象=三选一界面非角色;见 registry refresh_boundary)
|
||||
## 令牌档位词表(跨层四级稀有度框架〔01-核心循环 v1.5 档名映射:普通→普通、
|
||||
## 稀有→优秀、罕见→稀有、传说=新增头奖位〕;1 级最稀有=legendary 优先)
|
||||
const TIERS: Array[String] = ["common", "excellent", "rare", "legendary"]
|
||||
const TIER_COMMON := "common"
|
||||
const TIER_EXCELLENT := "excellent"
|
||||
const TIER_RARE := "rare"
|
||||
const TIER_LEGENDARY := "legendary"
|
||||
## 档位配额结构锚(迭代 20 换代——T2 四级直映射 70/25/3/0;T4 凭证四件入稀有档投放 +6
|
||||
## (rap 2/battery 1/时沙 2/保险 1)→ 稀有 9、总量 104;迭代 31 卡② 净删两件投放(battery 1/保险 1)
|
||||
## → 稀有 7、总量 102(用户裁「净删」,禁把令牌转给别件保住总量)。已裁换代授权内的 provisional 投放
|
||||
## (卡文 T2「四级配额表重构」+Q3 单迭代全量),偏离 GDD 占位记录随代拍板追认单呈报;
|
||||
## 档位配额结构锚(四级直映射 70/25/3/0;
|
||||
## 稀有档投放三 kind=ap 3/rap 2/时沙 2
|
||||
## → 合计 稀有 7、总量 102(「净删」口径=出池件配额直接归零,禁转给别件保住总量)。provisional 投放
|
||||
## (「四级配额表重构」+Q3 单迭代全量),偏离 GDD 占位记录随追认单呈报;
|
||||
## validate() 硬断言;此后改总量=重裁非调参)。
|
||||
const TIER_QUOTAS := {"common": 70, "excellent": 25, "rare": 7, "legendary": 0}
|
||||
|
||||
# —— 通关部分回血(固定型,非末关通关兑付)——
|
||||
@export var clear_heal_pct: int = 20 # [0,100] knob——回复生效上限的百分比,封顶上限不回满
|
||||
|
||||
# —— 三选一候选数(2026-10-02 修复批 T2 魔法数结构化)——
|
||||
# —— 三选一候选数——
|
||||
@export var curio_candidate_count: int = 3 # [1,6] knob——令牌洗牌不放回抽取张数上界
|
||||
@export var shop_refresh_free: int = 0 # [0,9] knob(MC-1 provisional——进店免费刷新次数;基础 0=Q2 裁,天赋增益位预留)
|
||||
@export var shop_refresh_cost_base: int = 20 # [1,999] knob(MC-1 provisional——GP 刷新基价)
|
||||
@export var shop_refresh_cost_curve: float = 1.8 # [1.0,4.0] knob(MC-1 provisional——按已付费刷新数幂曲线,A 链同族)
|
||||
|
||||
# —— 令牌配额(kind × tier 计数;未列组合 = 0。迭代 20 T2 四级换代:旧 rare→excellent、
|
||||
# 旧 rarest→rare 物理承袭〔70/25/3 零偏离〕+凭证四 kind 占位 rare 档配额 0〔T4 落路由同批投放〕)——
|
||||
# —— 令牌配额(kind × tier 计数;未列组合 = 0。四级档名 common/excellent/rare/legendary
|
||||
# 承袭同一物理位序〔70/25/3 零偏离〕;资源凭证 kind 的 rare 档配额见各行现值)——
|
||||
@export var curio_quota_attack_common: int = 18 # [0,99]
|
||||
@export var curio_quota_attack_excellent: int = 5 # [0,99]
|
||||
@export var curio_quota_max_hp_common: int = 17 # [0,99]
|
||||
@@ -55,13 +55,13 @@ const TIER_QUOTAS := {"common": 70, "excellent": 25, "rare": 7, "legendary": 0}
|
||||
@export var curio_quota_armor_excellent: int = 5 # [0,99]
|
||||
@export var curio_quota_gp_common: int = 18 # [0,99]
|
||||
@export var curio_quota_gp_excellent: int = 5 # [0,99]
|
||||
@export var curio_quota_heal_excellent: int = 3 # [0,99]——恢复件(原 5;T6 挤占 2 席位予 refresh——用户 2026-10-05 裁方案B,总量 98 锚不变)
|
||||
@export var curio_quota_refresh_excellent: int = 2 # [0,99]——刷新次数件(T6 新入池资源类成员;provisional 归曲线调参批)
|
||||
@export var curio_quota_ap_rare: int = 3 # [0,99]——AP 件(稀有档;旧罕见档物理承袭)
|
||||
@export var curio_quota_rap_rare: int = 2 # [0,99]——储备金袋(T4 投放 provisional 归调参批)
|
||||
@export var curio_quota_time_sand_rare: int = 2 # [0,99]——时沙(T4 投放 provisional 归调参批)
|
||||
@export var curio_quota_heal_excellent: int = 3 # [0,99]——恢复件(refresh 挤占 2 席位,总量 98 锚不变)
|
||||
@export var curio_quota_refresh_excellent: int = 2 # [0,99]——刷新次数件(资源类成员;provisional 归曲线调参)
|
||||
@export var curio_quota_ap_rare: int = 3 # [0,99]——AP 件(稀有档)
|
||||
@export var curio_quota_rap_rare: int = 2 # [0,99]——储备金袋(投放 provisional 归调参)
|
||||
@export var curio_quota_time_sand_rare: int = 2 # [0,99]——时沙(投放 provisional 归调参)
|
||||
|
||||
# —— 令牌件值(小/大数值对——v2.2 追认 b 定稿:20/50、+1/+2、+1/+2、+5/+10、恢复+6、AP+1)——
|
||||
# —— 令牌件值(小/大数值对——v2.2 令牌池口径定稿:20/50、+1/+2、+1/+2、+5/+10、恢复+6、AP+1)——
|
||||
@export var curio_value_attack_common: int = 1 # [0,999] knob——小增伤
|
||||
@export var curio_value_attack_excellent: int = 2 # [0,999] knob——大增伤
|
||||
@export var curio_value_max_hp_common: int = 5 # [0,999] knob——小生命
|
||||
@@ -76,9 +76,9 @@ const TIER_QUOTAS := {"common": 70, "excellent": 25, "rare": 7, "legendary": 0}
|
||||
@export var curio_value_rap_rare: int = 3 # [0,99] knob(provisional)——储备金袋 RAP +N
|
||||
@export var curio_value_time_sand_rare: int = 2 # [0,99] knob(provisional)——时沙 下轮 AP +N
|
||||
|
||||
# —— 已拿上限 cap(迭代 20 T2 概率与上限解耦〔奇物.md 池结构案D〕——案②令牌池+提前退役:
|
||||
# —— 已拿上限 cap(概率与上限解耦〔奇物.md 池结构案D〕——案②令牌池+提前退役:
|
||||
# 概率位=档+令牌数〔牌库物理张数〕、上限=本字段〔拿了才计、刷出不计〕;cap 满后该 kind 从候选
|
||||
# 视图剔除〔提前退役〕。99=实质无上限占位,实数归调参批;专长 cap 在 feats.tres 行字段〕)——
|
||||
# 视图剔除〔提前退役〕。99=实质无上限占位,实数归调参;专长 cap 在 feats.tres 行字段〕)——
|
||||
@export var curio_cap_attack: int = 99 # [1,99]
|
||||
@export var curio_cap_max_hp: int = 99 # [1,99]
|
||||
@export var curio_cap_armor: int = 99 # [1,99]
|
||||
@@ -89,10 +89,10 @@ const TIER_QUOTAS := {"common": 70, "excellent": 25, "rare": 7, "legendary": 0}
|
||||
@export var curio_cap_rap: int = 99 # [1,99]
|
||||
@export var curio_cap_time_sand: int = 99 # [1,99]
|
||||
|
||||
# —— 专长获取门票(迭代 15 卡② T5——非 Boss 渠道的专长入口形态,奇物.md v2.7 渠道轴定位;
|
||||
# —— 专长获取门票(非 Boss 渠道的专长入口形态,奇物.md v2.7 渠道轴定位;
|
||||
# 四口径契约④:拿到即开不可囤积/罕见只在概率不在价格/一局限购一次/计步保底连续 5 展示未出
|
||||
# 第 6 必出(计数=展示次数、刷新重展计入、节点与商店共享、触发清零、限购满不再触发)。
|
||||
# 四参数全部 provisional 归曲线调参批;limit/pity_step 系口径定死值调参批可复议)——
|
||||
# 四参数全部 provisional 归曲线调参;limit/pity_step 系口径定死值,调参可复议)——
|
||||
@export var feat_ticket_chance: int = 20 # [0,100] knob(provisional)——每次非 Boss 渠道展示门票出现概率 %
|
||||
@export var feat_ticket_quota: int = 2 # [0,9] knob(provisional)——每局门票出现次数上限
|
||||
@export var feat_ticket_limit: int = 1 # [1,9] knob(口径定死=1)——一局限购(选中/买下)次数
|
||||
@@ -116,8 +116,8 @@ func curio_apply_route(kind: String) -> String:
|
||||
return String(CURIO_APPLY_ROUTES.get(kind, ""))
|
||||
|
||||
|
||||
## 令牌配额读数(未列 kind×tier 组合 = 0——迭代 20 T2 四级构成:属性四类 common/excellent、
|
||||
## heal/refresh excellent、ap rare、凭证四 kind rare 占位 0)
|
||||
## 令牌配额读数(未列 kind×tier 组合 = 0——四级构成:属性四类 common/excellent、
|
||||
## heal/refresh excellent、ap rare、凭证 kind rare 档)
|
||||
func curio_quota(kind: String, tier: String) -> int:
|
||||
match [kind, tier]:
|
||||
["attack", TIER_COMMON]: return curio_quota_attack_common
|
||||
@@ -155,7 +155,7 @@ func curio_value(kind: String, tier: String = TIER_COMMON) -> int:
|
||||
return 0
|
||||
|
||||
|
||||
## 已拿上限读数(迭代 20 T2 案②——kind 维度;专长 cap 走 feats.tres 行字段不走本面)
|
||||
## 已拿上限读数(案②——kind 维度;专长 cap 走 feats.tres 行字段不走本面)
|
||||
func curio_cap(kind: String) -> int:
|
||||
match kind:
|
||||
"attack": return curio_cap_attack
|
||||
@@ -170,8 +170,8 @@ func curio_cap(kind: String) -> int:
|
||||
return 99
|
||||
|
||||
|
||||
## Boss 专长三选一按档配比加权(迭代 20 T5——奇物.md v3.1 rarity 语义约束「候选按档配比或
|
||||
## 加权抽取」兑付面;权重 provisional 归调参批——普通打底/优秀期待/稀有稀缺/传说头奖形态)
|
||||
## Boss 专长三选一按档配比加权(奇物.md v3.1 rarity 语义约束「候选按档配比或
|
||||
## 加权抽取」兑付面;权重 provisional 归调参——普通打底/优秀期待/稀有稀缺/传说头奖形态)
|
||||
func boss_feat_weight(tier: String) -> int:
|
||||
match tier:
|
||||
TIER_COMMON: return 100
|
||||
|
||||
@@ -8,7 +8,7 @@ extends Resource
|
||||
const SOLVER_RULES := ["single", "single_subset"]
|
||||
const COST_BUDGET := 16384 # F2c provisional 量级护栏(禁墙钟逃生)
|
||||
const TARGET_ABS_FLOOR := 0.85 # 战斗结算 修订——target 域下限(P1 绝对锚)
|
||||
const BOARD_TYPE_VOCABULARY := ["dungeon", "underdark", "deep_underdark"] # 图类型词表 v1.1(2026-09-20 L3 内容批 +deep_underdark 灰矮人矿道;玩家侧显示名「地图类型」)
|
||||
const BOARD_TYPE_VOCABULARY := ["dungeon", "underdark", "deep_underdark"] # 图类型词表 v1.1(含 deep_underdark 灰矮人矿道;玩家侧显示名「地图类型」)
|
||||
|
||||
|
||||
func validate() -> Array:
|
||||
@@ -49,7 +49,7 @@ func validate() -> Array:
|
||||
var att: int = int(row.get("max_gen_attempts", 0))
|
||||
if att < 1 or att > 32:
|
||||
errs.append({"category": "domain", "message": "(%d,%d) max_gen_attempts 越域 [1,32]" % [L, D]})
|
||||
# 布雷数下限(R3)——零雷板拒绝(−9 = 出生区格数,常量单点 BoardData.SPAWN_AREA_CELLS——2026-10-02 修复批)
|
||||
# 布雷数下限(R3)——零雷板拒绝(−9 = 出生区格数,常量单点 BoardData.SPAWN_AREA_CELLS)
|
||||
var n_mines := int(round(d * float(w * h - BoardData.SPAWN_AREA_CELLS)))
|
||||
if n_mines < 1:
|
||||
errs.append({"category": "cross_field", "message": "(%d,%d) 布雷数下限违例 round(d×(W×H−9)) ≥ 1" % [L, D]})
|
||||
@@ -59,11 +59,11 @@ func validate() -> Array:
|
||||
# F2c 成本可行性
|
||||
if att * w * h > COST_BUDGET:
|
||||
errs.append({"category": "cross_field", "message": "(%d,%d) cost_feasible 违例 attempts×W×H ≤ %d" % [L, D, COST_BUDGET]})
|
||||
# 图类型词表 v1.0(2026-09-19 用户拍板定稿:dungeon=地牢基线 / underdark=幽暗地域;扩词随内容批走修订)
|
||||
# 图类型词表 v1.0(dungeon=地牢基线 / underdark=幽暗地域;扩词走本表修订)
|
||||
var bt: String = str(row.get("board_type", ""))
|
||||
if not BOARD_TYPE_VOCABULARY.has(bt):
|
||||
errs.append({"category": "cross_field", "message": "(%d,%d) board_type 越词表: %s" % [L, D, bt]})
|
||||
# 初始展开收窄批两参数(迭代14① T1——加载期校验违例致命;缺省 shave=1 现行投值 / cap=0 闸关防御口径)
|
||||
# 初始展开收窄两参数(加载期校验违例致命;缺省 shave=1 现行投值 / cap=0 闸关防御口径)
|
||||
var sr: int = int(row.get("shave_ring_radius", 1))
|
||||
if sr < 0 or sr > 2:
|
||||
errs.append({"category": "domain", "message": "(%d,%d) shave_ring_radius 越域 [0,2]: %d" % [L, D, sr]})
|
||||
|
||||
@@ -7,23 +7,23 @@ extends Resource
|
||||
|
||||
@export var rows: Array[Dictionary] = [] # Array[Dictionary]
|
||||
|
||||
# 护甲类型封闭枚举(攻击类型 × 护甲克制批——克制矩阵本体在 SettlementTable;
|
||||
# 2026-09-15 命名统一批:位置键 {none, a, b} → 语义键 {light, ward, heavy} = 轻甲 / 魔法护甲 / 重甲)
|
||||
# 护甲类型封闭枚举(克制矩阵本体在 SettlementTable;
|
||||
# 语义键 {light, ward, heavy} = 轻甲 / 魔法护甲 / 重甲)
|
||||
const ARMOR_TYPES := ["light", "ward", "heavy"]
|
||||
|
||||
# 攻击类型封闭枚举(迭代 24 N3 2026-10-13——敌方攻击类型轴开列,承接锻造工坊迁移面⑥;
|
||||
# 攻击类型封闭枚举(敌方攻击类型轴开列,承接锻造工坊迁移面⑥;
|
||||
# 克制矩阵点数制键前缀 = 本枚举:normal → light 0 / ward +1 / heavy −1;pierce/magic 为差异化
|
||||
# 预留档,差异化填值属新设计另立项。可省略 + 缺省 normal 向后兼容)
|
||||
const ATTACK_TYPES := ["normal", "pierce", "magic"]
|
||||
|
||||
# 实例化数量域(2026-10-02 修复批 T2 魔法数结构化——数量域 [1,3] 双落点收口单点:
|
||||
# 实例化数量域(域 [1,3] 双落点收口单点:
|
||||
# 本表 validate 与 单位容器 spawn 守卫共用;遭遇骰结果与行值 unit_count/unit_count_max 同域。
|
||||
# 2026-10-07 行集多行批:MAX 语义 = **单种数**(单行实例数上限),行集实例总数 ≤4 另由
|
||||
# MAX 语义 = **单种数**(单行实例数上限),行集实例总数 ≤4 另由
|
||||
# spawn 期守卫承担——骰后值加载期不可判,双闸分工见 敌人系统 v0.15 行集构成纪律③)
|
||||
const UNIT_COUNT_MIN := 1
|
||||
const UNIT_COUNT_MAX := 3
|
||||
|
||||
# 行集实例总数上限(2026-10-07 行集多行批 P1 甲——行集构成纪律③:各行开战骰后数量求和 ≤4;
|
||||
# 行集实例总数上限(行集构成纪律③:各行开战骰后数量求和 ≤4;
|
||||
# 「总数」= 实例数非行数〔Q9 二裁〕,行数不设上限。骰后值加载期不可判——守卫在 单位容器 spawn,
|
||||
# 常量单点在本表与数量域同位收口)
|
||||
const ROWSET_INSTANCE_MAX := 4
|
||||
@@ -66,7 +66,7 @@ func validate() -> Array:
|
||||
var atk: int = int(row.get("atk", -1))
|
||||
if atk < 0 or atk > 999:
|
||||
errs.append({"category": "domain", "message": "%s atk 越域 [0,999]" % eid})
|
||||
# atk_min/atk_max(可省略配对——2026-09-14 敌方攻击区间批):遭遇骰同流 spawn 期整战持久 roll;
|
||||
# atk_min/atk_max(可省略配对):遭遇骰同流 spawn 期整战持久 roll;
|
||||
# 中点锚 = 行动经济 条款①同款期望当量纪律(调区间必同步调 atk)
|
||||
var has_amin: bool = row.has("atk_min")
|
||||
var has_amax: bool = row.has("atk_max")
|
||||
@@ -79,7 +79,7 @@ func validate() -> Array:
|
||||
errs.append({"category": "domain", "message": "%s atk 区间越域 [0,999] 且 min≤max: %d..%d" % [eid, amin, amax]})
|
||||
if atk != int((amin + amax) / 2.0):
|
||||
errs.append({"category": "cross_field", "message": "%s atk(%d) ≠ 区间中点(%d)——中点恒等期望当量锚" % [eid, atk, (amin + amax) / 2]})
|
||||
# 池模板(敌人技能池批——单表结构校验;跨表构成纪律在 ConfigDB):
|
||||
# 池模板(单表结构校验;跨表构成纪律在 ConfigDB):
|
||||
# 非空池 + 条目 {skill_id 非空, weight > 0} + 池内 skill_id 不重复。
|
||||
# 普攻冷却恒 0(SkillTable schema 锁死)→ 可摇集合恒非空,"空池不可能"由此结构保证。
|
||||
var pool: Array[Variant] = row.get("pool", [])
|
||||
@@ -100,7 +100,7 @@ func validate() -> Array:
|
||||
var w: int = int(entry.get("weight", 0))
|
||||
if w <= 0 or w > 9999:
|
||||
errs.append({"category": "domain", "message": "%s pool 权重越域 (0,9999]: %d" % [eid, w]})
|
||||
# unlock_hp_pct(可省略——迭代14④ Boss 半血解锁技:缺省无字段=无门槛;域 [1,99] percent。
|
||||
# unlock_hp_pct(可省略——Boss 半血解锁技:缺省无字段=无门槛;域 [1,99] percent。
|
||||
# 投放纪律=仅 Boss 行〔enemy 表无 boss 编码位,硬闸须跨表查节点类型——不做硬闸,文档纪律登记〕)
|
||||
if entry.has("unlock_hp_pct"):
|
||||
var uhp: int = int(entry.get("unlock_hp_pct", 0))
|
||||
@@ -110,17 +110,17 @@ func validate() -> Array:
|
||||
var at: String = String(row.get("armor_type", "light"))
|
||||
if not ARMOR_TYPES.has(at):
|
||||
errs.append({"category": "enum", "message": "%s armor_type 未知枚举: %s(域 {light, ward, heavy})" % [eid, at]})
|
||||
# attack_type(迭代 24 N3——可省略,缺省 normal 向后兼容;写法沿用 armor_type 同款)
|
||||
# attack_type(可省略,缺省 normal 向后兼容;写法沿用 armor_type 同款)
|
||||
var att: String = String(row.get("attack_type", "normal"))
|
||||
if not ATTACK_TYPES.has(att):
|
||||
errs.append({"category": "enum", "message": "%s attack_type 未知枚举: %s(域 {normal, pierce, magic})" % [eid, att]})
|
||||
# armor_class(可省略——缺省 0 向后兼容;2026-09-16 护甲等级掷点批:防守方掷点判定阈值基数)
|
||||
# armor_class(可省略——缺省 0 向后兼容;防守方掷点判定阈值基数)
|
||||
var ac: int = int(row.get("armor_class", 0))
|
||||
if ac < 0 or ac > 999:
|
||||
errs.append({"category": "domain", "message": "%s armor_class 越域 [0,999]: %d" % [eid, ac]})
|
||||
# 行集构成纪律②(2026-10-07 行集多行批——行序即契约,敌人系统 v0.15):同坐标多行必须
|
||||
# 行集构成纪律②(行序即契约,敌人系统 v0.15):同坐标多行必须
|
||||
# 文件内相邻。迭代中记录上一行坐标;当前坐标曾出现过但 ≠ 上一行坐标 = 同坐标行被隔断,报错。
|
||||
# 原纪律「每行集恰一敌种」同批退役——同坐标多敌种混编合法;同坐标同敌种第二行仍由
|
||||
# 同坐标多敌种混编合法;同坐标同敌种第二行仍由
|
||||
# dup_key 拦截(键含 enemy_id)。错误类别沿用 rowset_rule(定位面不变)。
|
||||
var rk := "%d_%d" % [L, D]
|
||||
if rk != last_rk:
|
||||
@@ -135,6 +135,6 @@ func validate() -> Array:
|
||||
return errs
|
||||
|
||||
|
||||
# 进度键解析单点收口(2026-10-07 行集多行批 P2 甲——原 floor 语义 resolve_rowset 退役,全仓统走
|
||||
# ConfigDbResolver.resolve_enemy_rowset ceil 版行集数组:2026-09-18 用户拍板 ceil 保证偏难,
|
||||
# 双算法并存至本批销号〔状态记录 312 待裁项〕;原冒烟四调用点随批迁移,坐标全精确命中零行为差)。
|
||||
# 进度键解析单点收口(全仓统走
|
||||
# ConfigDbResolver.resolve_enemy_rowset ceil 版行集数组:ceil 保证偏难,
|
||||
# 禁双算法并存〔状态记录 312〕;坐标须全精确命中)。
|
||||
@@ -5,7 +5,7 @@
|
||||
class_name ForgeTable
|
||||
extends Resource
|
||||
|
||||
# 路线 id 词表(代拍板立项:甲路线 id=甲型值本身——ward=魔法护甲,3a 裁统一命名)
|
||||
# 路线 id 词表(甲路线 id=甲型值本身——ward=魔法护甲,统一命名)
|
||||
const WEAPON_IDS := ["sword", "spear", "staff"]
|
||||
const ARMOR_IDS := ["light", "heavy", "ward"]
|
||||
const ROUTE_IDS := ["sword", "spear", "staff", "light", "heavy", "ward"]
|
||||
@@ -104,7 +104,7 @@ func validate() -> Array:
|
||||
errs.append({"category": "cross_field", "message": "%s prereq_route 不得自指" % rid})
|
||||
elif kind_by_id.has(prereq) and String(kind_by_id[prereq]) != kind:
|
||||
errs.append({"category": "cross_field", "message": "%s 前置 %s 类型不符(武器←武器/甲←甲): %s vs %s" % [rid, prereq, kind, String(kind_by_id[prereq])]})
|
||||
# 初始持有恰两行且一武器一甲(长剑/轻甲——语义继承自已退役属性锻造行)
|
||||
# 初始持有恰两行且一武器一甲(长剑/轻甲)
|
||||
var init_weapons := 0
|
||||
var init_armors := 0
|
||||
for k in initial_ids.keys():
|
||||
|
||||
@@ -7,7 +7,7 @@ extends Resource
|
||||
|
||||
@export var max_hp: int = 50 # [1,999] knob(provisional,溯源 = 原型锚)
|
||||
@export var atk: int = 6 # [1,999] knob——基础攻击(溯源 = 概念原型 HERO_BASE_ATK,provisional 归 难度曲线与Boss);消费面 = 战斗结算·战斗段先手攻击
|
||||
@export var armor_class: int = 10 # [0,999] knob——防守方护甲等级基准(2026-09-16 护甲等级掷点批,暂定 10 归 难度曲线与Boss);消费面 = 战斗结算掷点判定阈值基数(ac_up 效果加算其上)
|
||||
@export var armor_class: int = 10 # [0,999] knob——防守方护甲等级基准(暂定 10 归 难度曲线与Boss);消费面 = 战斗结算掷点判定阈值基数(ac_up 效果加算其上)
|
||||
|
||||
|
||||
func validate() -> Array:
|
||||
|
||||
@@ -6,10 +6,10 @@ extends Resource
|
||||
|
||||
@export var rows: Array[Dictionary] = [] # Array[Dictionary]
|
||||
|
||||
const NODE_TYPES := ["battle", "reward", "rest", "boss", "shop"] # 商店房批 2026-09-21 四→五类(关卡节点地图 v1.1)
|
||||
const NODE_TYPES := ["battle", "reward", "rest", "boss", "shop"] # 五类(关卡节点地图 v1.1)
|
||||
|
||||
|
||||
# 行单点读取(2026-10-02 修复批 T5 表访问收口——LevelRunner._find_level_row 直扫收口;键 = level_index)
|
||||
# 行单点读取(表访问收口——LevelRunner._find_level_row 禁直扫;键 = level_index)
|
||||
func row_by_level_index(level_index: int) -> Dictionary:
|
||||
for row in rows:
|
||||
if int(row.level_index) == level_index:
|
||||
@@ -35,8 +35,8 @@ func validate() -> Array:
|
||||
var heal: int = int(row.get("rest_heal_pct", -1))
|
||||
if heal < 0 or heal > 100:
|
||||
errs.append({"category": "domain", "message": "level %d rest_heal_pct 越域 [0,100]" % L})
|
||||
# 模板池(关卡节点图生成 v0.4——M-3 迭代 5 T5):row.templates 非空 = 池形态,池内全模板加载期校验
|
||||
# (违规致命——不只抽中的那张);空 = 旧单图形态(row.nodes 直读,向后兼容零迁移)。
|
||||
# 模板池(关卡节点图生成 v0.4):row.templates 非空 = 池形态,池内全模板加载期校验
|
||||
# (违规致命——不只抽中的那张);空 = 单图形态(row.nodes 直读,向后兼容)。
|
||||
var node_lists: Array[Array] = [] # [ [上下文标签, nodes 数组], ... ]
|
||||
var templates: Array[Variant] = row.get("templates", [])
|
||||
if templates.is_empty():
|
||||
@@ -88,7 +88,7 @@ func _validate_level_nodes(L: int, ctx: String, nodes: Array) -> Array:
|
||||
# 图合法性(单链与图同一套算法——单链 = 出边恰一的退化形态,违规同样加载致命)
|
||||
for e in _validate_graph(L, nodes, is_graph, tag):
|
||||
errs.append(e)
|
||||
# U2 目检新规则(迭代 5 返工批 2026-09-29 两条,加载期同检):①单链前三位纯战斗(奖励/休整禁入前三)
|
||||
# U2 目检规则(两条,加载期同检):①单链前三位纯战斗(奖励/休整禁入前三)
|
||||
if not is_graph and nodes.size() >= 3:
|
||||
for i in 3:
|
||||
if String(nodes[i].get("type", "")) != "battle":
|
||||
@@ -127,9 +127,9 @@ func _validate_graph(L: int, nodes: Array, is_graph: bool, tag: String = "") ->
|
||||
errs.append({"category": "dangling_next", "message": "level %d%s 节点 %d 出边指向不存在编号: %d" % [L, tag, ids[i], tid]})
|
||||
elif tid <= ids[i]:
|
||||
errs.append({"category": "structural", "message": "level %d%s 连线 %d→%d 违反前进方向(目标编号须严格更大)" % [L, tag, ids[i], tid]})
|
||||
# ③b 出度上限(T17 用户拍板 2026-10-01「节点出度最大为 2」):单节点出边 >2 即加载致命。
|
||||
# 规则连带:出度 ≤2 + 入口唯一下 BFS 层宽上限 = 4(序列 1,2,4——原「层宽上限 5」与出度约束
|
||||
# 不相容,模板池随本检同批按层宽 ≤4 重建)。
|
||||
# ③b 出度上限(「节点出度最大为 2」):单节点出边 >2 即加载致命。
|
||||
# 规则连带:出度 ≤2 + 入口唯一下 BFS 层宽上限 = 4(序列 1,2,4——层宽上限 5 与出度约束
|
||||
# 不相容,模板池按层宽 ≤4 构造)。
|
||||
for i in nodes.size():
|
||||
if nexts[i].size() > 2:
|
||||
errs.append({"category": "fanout_violation", "message": "level %d%s 节点 %d 出度 %d > 2(出度上限拍板 2026-10-01)" % [L, tag, ids[i], nexts[i].size()]})
|
||||
@@ -177,7 +177,7 @@ func _validate_graph(L: int, nodes: Array, is_graph: bool, tag: String = "") ->
|
||||
for i in nodes.size():
|
||||
if not to_boss.has(i):
|
||||
errs.append({"category": "structural", "message": "level %d 节点 %d 沿连线不可达 Boss" % [L, ids[i]]})
|
||||
# ⑧ U2 目检新规则(迭代 5 返工批 2026-09-29):BFS 同深度层节点数上限 5(分支宽度天花板)
|
||||
# ⑧ U2 目检规则:BFS 同深度层节点数上限 5(分支宽度天花板)
|
||||
var layer_of := {}
|
||||
var bfs: Array[Variant] = [entries[0]]
|
||||
layer_of[entries[0]] = 0
|
||||
|
||||
@@ -7,9 +7,9 @@ extends Resource
|
||||
|
||||
@export var boss_keystone: Array[Dictionary] = [] # Array[Dictionary] 键 level_index
|
||||
@export var conversion: Array[Dictionary] = [] # Array[Dictionary] 键 attr
|
||||
@export var attune_refresh_upgrade: Array[Dictionary] = [] # Array[Dictionary] 键 level——Y 刷新升级行族(迭代 11 第①批 T4)
|
||||
@export var attune_refresh_upgrade: Array[Dictionary] = [] # Array[Dictionary] 键 level——Y 刷新升级行族
|
||||
|
||||
const FORGE_ATTRS := ["max_hp", "atk", "armor_class"] # 三属性轴词表(conversion 行键域——原 forge 词表名沿用作轴名)
|
||||
const FORGE_ATTRS := ["max_hp", "atk", "armor_class"] # 三属性轴词表(conversion 行键域=forge 词表名)
|
||||
|
||||
|
||||
func validate() -> Array:
|
||||
@@ -36,8 +36,8 @@ func validate() -> Array:
|
||||
if li_at != i + 1:
|
||||
errs.append({"category": "structural", "message": "boss_keystone level_index 不连续(须从 1 连续递增): 位 %d = %d" % [i + 1, li_at]})
|
||||
break
|
||||
# workshop_items 行族已退役(迭代 22 T2——解锁混合制换代,条件谓词挂 treasure_items 行内;
|
||||
# 校验段随批删除——秘宝解锁条件校验单点=TreasureItemTable.validate)
|
||||
# 无 workshop_items 行族(解锁混合制——条件谓词挂 treasure_items 行内;
|
||||
# 本表不校验秘宝解锁,单点=TreasureItemTable.validate)
|
||||
# conversion:attr 词表与 forge 一致 + 唯一 + 覆盖恰三 + 平移量域 [0,100]
|
||||
var seen_conv := {}
|
||||
for row in conversion:
|
||||
@@ -54,7 +54,7 @@ func validate() -> Array:
|
||||
for conv_key in FORGE_ATTRS:
|
||||
if not seen_conv.has(conv_key):
|
||||
errs.append({"category": "coverage", "message": "conversion 缺属性行: %s(三行与 forge 词表一致)" % conv_key})
|
||||
# attune_refresh_upgrade:level 唯一 + 连续从 1 + charges/cost 域(迭代 11 第①批 T4)
|
||||
# attune_refresh_upgrade:level 唯一 + 连续从 1 + charges/cost 域
|
||||
var seen_al := {}
|
||||
for row in attune_refresh_upgrade:
|
||||
var al: int = int(row.get("level", -1))
|
||||
|
||||
@@ -9,10 +9,10 @@ extends Resource
|
||||
|
||||
const VEINS := ["spine", "forge", "keystone", "attune"]
|
||||
const KINDS := ["stat", "rule", "unlock", "socket", "fork", "easter"]
|
||||
const CURRENCIES := ["keystone", "free"] # 树全钥石单币(2026-10-07 用户裁甲——原 gp 枚举值退役,字段保留为呈现元数据)
|
||||
const CURRENCIES := ["keystone", "free"] # 树全钥石单币(无 gp 枚举值,字段保留为呈现元数据)
|
||||
const STAT_KEYS := ["atk", "max_hp", "hit", "armor_class"]
|
||||
## 规则键封闭词表(零裸 flat 同族护栏——effects.rule 键出域=加载致命;六键=三组 Fork 双态真实接线,
|
||||
## 迭代 21 T1 扩签:posture 两键现役沿袭,sense/economy 四键随危险直觉/财富之道两组建效面新增)
|
||||
## posture 两键 + sense/economy 四键〔危险直觉/财富之道两组建效面〕)
|
||||
const RULE_KEYS := ["posture_first_strike", "posture_bulwark", "sense_sharp", "sense_guard", "economy_hoard", "economy_spend"]
|
||||
const KEY_WHITELIST := ["node_id", "display_name", "vein", "kind", "currency", "price", "cap",
|
||||
"requires", "requires_level", "effects", "effects_a", "effects_b", "name_a", "name_b", "fork_group", "note"]
|
||||
@@ -20,14 +20,14 @@ const KEY_WHITELIST := ["node_id", "display_name", "vein", "kind", "currency", "
|
||||
const SPINE_ROW_GATE := 4
|
||||
|
||||
## Fork 双态组词表(每组恰一节点——互斥由单态承载;营地零币切换承载面;
|
||||
## fg_mastery 克制专精整组砍除——2026-10-11 裁,类型表达归锻造工坊)
|
||||
## 无 fg_mastery 克制专精组——类型表达归锻造工坊)
|
||||
const FORK_GROUPS := ["fg_posture", "fg_sense", "fg_economy"]
|
||||
|
||||
|
||||
func validate() -> Array:
|
||||
var errs: Array[Variant] = []
|
||||
if rows.is_empty():
|
||||
return errs # 空表合法态(建表先行——内容随换代批落)
|
||||
return errs # 空表合法态(建表先行——内容后补)
|
||||
var seen := {}
|
||||
var fork_seen := {}
|
||||
for row in rows:
|
||||
@@ -118,7 +118,7 @@ func row_by_id(node_id: String) -> Dictionary:
|
||||
return {}
|
||||
|
||||
|
||||
## 行门槛(一线一节点:脊柱节点第 N 级可购需全树总等级 ≥ GATE×(N−1);原 row-gating 行号主语
|
||||
## 随层行退役平移到节点等级轴)
|
||||
## 行门槛(一线一节点:脊柱节点第 N 级可购需全树总等级 ≥ GATE×(N−1);
|
||||
## 门槛主语=节点等级轴)
|
||||
func level_gate(level: int) -> int:
|
||||
return SPINE_ROW_GATE * maxi(0, level - 1)
|
||||
@@ -6,7 +6,7 @@ extends Resource
|
||||
|
||||
@export var rows: Array[Dictionary] = [] # Array[Dictionary]
|
||||
|
||||
const CELL_TYPES := ["attack", "defense", "heal", "blast", "weaken", "pierce", "magic", "action", "gp", "treasure_item"] # 十型(迭代 7 T1 秘宝内容批扩值——registry 秘宝词表;aux 行 effect_id = item_id 双表校验键)
|
||||
const CELL_TYPES := ["attack", "defense", "heal", "blast", "weaken", "pierce", "magic", "action", "gp", "treasure_item"] # 十型(registry 秘宝词表;aux 行 effect_id = item_id 双表校验键)
|
||||
const TREASURE_ITEM_TYPE := "treasure_item" # aux 行标识(掉落投放行:挖开→战斗结算 grant 路由关卡域库存,不入增益容器)
|
||||
const POLARITIES := ["positive", "negative"]
|
||||
const POOL_TAGS := ["cell", "clear_default"] # cell = 格面资源单一池;clear_<biome> 命名空间预留拒绝
|
||||
@@ -39,11 +39,11 @@ func validate() -> Array:
|
||||
var atk_type: String = String(row.get("attack_type", ""))
|
||||
if atk_type != "" and not ["normal", "pierce", "magic"].has(atk_type):
|
||||
errs.append({"category": "enum", "message": "%s attack_type 出域 {normal, pierce, magic}: %s" % [rid, atk_type]})
|
||||
# aux 投放行专属校验(迭代 7 T1):极性恒正/加权入池/effect_id=item_id 非空(悬空在 config_db 跨表层
|
||||
# aux 投放行专属校验:极性恒正/加权入池/effect_id=item_id 非空(悬空在 config_db 跨表层
|
||||
# 对秘宝词表校验——reward_dangling_treasure)/无 power 无区间(数值语义不复用,道具效果读 TreasureItemTable)
|
||||
if ctype == TREASURE_ITEM_TYPE:
|
||||
# 负向行 = 危险格极性字典占位(雷安同池摇号下危险格可摇中 aux 身份——负路由零兑付兜底,
|
||||
# effect_id 空/power 0/weight 0 不入池;2026-09-29 T1)
|
||||
# effect_id 空/power 0/weight 0 不入池)
|
||||
if pol == "negative":
|
||||
if String(row.get("effect_id", "")) != "" or int(row.get("power", 0)) != 0 or int(row.get("weight", 0)) != 0:
|
||||
errs.append({"category": "cross_field", "message": "%s aux 负占位行仅允许 空effect_id/0power/0weight" % rid})
|
||||
|
||||
@@ -6,23 +6,23 @@ extends Resource
|
||||
|
||||
@export var equivalence_ref_damage: int = 12 # [1,999] knob——当量参考敞口 D_ref
|
||||
@export var p_perfect: int = 200 # {100,200} percent——完美排雷第二份兑付触发(载体域)
|
||||
@export var seal_hit_premium: int = 100 # [100,999] percent knob——封印命中兑付溢价 ×(1+(n−100)/100):125=×1.25(条款3 条件二封印溢价形态 a,2026-10-05 判式换代批 provisional);100=无溢价直通零扰动。只乘封印命中正向兑付(flat power/percent 幅度/gp 缓冲三形态),标空/挖格/清图不乘;雷安同池冻结不动
|
||||
@export var segment_order: PackedStringArray = [] # 结构冻结——恒等拍板段序校验违例致命
|
||||
@export var seal_hit_premium: int = 100 # [100,999] percent knob——封印命中兑付溢价 ×(1+(n−100)/100):125=×1.25(条款3 条件二封印溢价形态 a,provisional);100=无溢价直通零扰动。只乘封印命中正向兑付(flat power/percent 幅度/gp 缓冲三形态),标空/挖格/清图不乘;雷安同池冻结不动
|
||||
@export var segment_order: PackedStringArray = [] # 结构冻结——段序恒等校验违例致命
|
||||
@export var source_label_map: Dictionary[String, Variant] = {} # 结构冻结——source_class→文案键
|
||||
@export var effect_pool_map: Dictionary[String, Variant] = {} # 结构冻结——effect_id→pool(悬空加载致命)
|
||||
@export var pool_clamp_lo: int = -100 # 结构冻结
|
||||
@export var pool_clamp_hi: int = 100 # 结构冻结
|
||||
@export var round_direction: String = "floor" # 结构冻结——单一取整规则
|
||||
# ---- 攻击类型 × 护甲克制批 + 充能奖励定义(2026-09-12 用户拍板结构;2026-09-15 命名统一批 / 抽象分离批;数值 provisional)----
|
||||
# ---- 攻击类型 × 护甲克制 + 充能奖励定义(数值 provisional)----
|
||||
# 主角攻击 = 普通(atk×(1+attack_up),不吃克制)+ 穿刺 + 魔法(后两者由充能供给),三分量各自 floor 后相加。
|
||||
# 【两条抽象分离,勿焊死】攻击类型 = 伤害的属性轴,只决定查 armor_effectiveness 哪一行,定义里不含任何棋盘来源;
|
||||
# 充能 = 按攻击类型分桶的附加伤害储备,"谁往桶里放东西"属**奖励的定义**(开格/标雷只是奖励触发源,奖励内容可改)。
|
||||
# 原 damage_a_per_cell / damage_a_cap / damage_b_per_seal 与 charge_grants 充能奖励定义均退役(2026-09-19 充能退役批——攻击模型回归单分量)。
|
||||
# 无 damage_a_per_cell / damage_a_cap / damage_b_per_seal 与 charge_grants 字段(攻击模型=单分量)。
|
||||
@export var armor_effectiveness: Dictionary[String, Variant] = {} # 克制修正矩阵(加法点数 delta):{攻击类型 {normal|pierce|magic}: {护甲 {light|ward|heavy}: 点数 [-3,+3]}}
|
||||
# ---- 护甲等级掷点批(2026-09-16 用户拍板,数字即规范——registry settlement_table.ac_roll/ac_discount_tiers 同批定稿)----
|
||||
# ---- 护甲等级掷点(数字即规范——registry settlement_table.ac_roll/ac_discount_tiers 定稿)----
|
||||
# 判定式:掷 1d20,掷点 ≥ 防守方 AC + 攻击加成 → 全额;低掷按缺口(AC−掷点)查档打折,无 miss、无零收益档;
|
||||
# nat 最大面(crit_roll)= 暴击:该次基础攻击分量 ×2 且恒全额;任何伤害落地全局下限 damage_floor(≥1)——
|
||||
# 2026-09-20 用户拍板扩全来源(攻击双方/棋盘直伤/地形变异/反噬,取值域 [1,+∞) 无上限)。
|
||||
# 全来源(攻击双方/棋盘直伤/地形变异/反噬,取值域 [1,+∞) 无上限)。
|
||||
# 掷点只吃基础攻击分量(主角普通分量 / 敌方攻击拍与重击拍基线),穿刺/魔法充能与棋盘来源伤害不掷不折。
|
||||
# 微调旋钮 = 档位边界(roll_tier_bounds)与档位值(roll_tier_discounts),不动 AC 值(armor_class 归单位表)。
|
||||
@export var roll_faces: int = 20 # [2,1000] knob——掷点骰面(d20)
|
||||
@@ -31,8 +31,8 @@ extends Resource
|
||||
@export var roll_tier_discounts: Array[int] = [] # 档位值 percent 降序(与 bounds 同长;[75,50,25] = ×0.75/×0.5/×0.25)
|
||||
@export var crit_roll: int = 20 # [1,roll_faces] knob——nat 最大面 = 暴击触发掷点(恒全额)
|
||||
@export var crit_multiplier: int = 200 # [100,999] percent knob——暴击倍率(200 = ×2,只乘该次基础攻击分量)
|
||||
@export var damage_floor: int = 1 # [1,999] knob——任何伤害落地全局下限·全来源("被减伤归零 v=0"行语义随批退役)
|
||||
@export var armor_roll_stream: String = "armor_roll" # 掷点用途标签(rng_service PURPOSE_TAGS 成员——并流后不参与派生,仅作事件日志用途标记;2026-09-27 统一流裁决)
|
||||
@export var damage_floor: int = 1 # [1,999] knob——任何伤害落地全局下限·全来源(无"被减伤归零 v=0"语义)
|
||||
@export var armor_roll_stream: String = "armor_roll" # 掷点用途标签(rng_service PURPOSE_TAGS 成员——不参与派生,仅作事件日志用途标记)
|
||||
|
||||
const ARMOR_KEYS := ["light", "ward", "heavy"]
|
||||
const ATTACK_TYPE_KEYS := ["normal", "pierce", "magic"]
|
||||
|
||||
@@ -9,13 +9,13 @@ extends Resource
|
||||
@export var level_gp_bonus: Array[Dictionary] = [] # Array[Dictionary] 键 level_index
|
||||
@export var products: Array[Dictionary] = [] # Array[Dictionary] 键 product_id
|
||||
@export var price_level_bonus: Array[Dictionary] = [] # Array[Dictionary] 键 level_index
|
||||
@export var pool_shelf: Array[Dictionary] = [] # Array[Dictionary] 键 kind_id(迭代 11 第②批 T1)
|
||||
@export var pool_shelf: Array[Dictionary] = [] # Array[Dictionary] 键 kind_id
|
||||
@export var price_a: float = 0.0 # 进店已购次数爬价因子(provisional——0 = 未投值即致命域)
|
||||
@export var price_b: float = 0.0 # 跨关递增因子(provisional)
|
||||
|
||||
const GP_TIERS := ["normal", "elite", "boss"]
|
||||
const PRODUCT_ATTRS := ["atk", "max_hp", "armor_class"]
|
||||
const SHELF_KINDS := ["attack", "max_hp", "armor", "ap", "feat_ticket", "heal"] # 货架候选词表(四切边后种 + T5 门票行 + T6 heal 补回复〔默认原则:分类内成员都入该渠道池〕——奇物批迭代15②)
|
||||
const SHELF_KINDS := ["attack", "max_hp", "armor", "ap", "feat_ticket", "heal"] # 货架候选词表(四切边后种 + 门票行 + heal 补回复〔默认原则:分类内成员都入该渠道池〕)
|
||||
|
||||
|
||||
func validate() -> Array:
|
||||
@@ -83,7 +83,7 @@ func validate() -> Array:
|
||||
var inc: int = int(row.get("price_increment", -1))
|
||||
if inc < 0 or inc > 999:
|
||||
errs.append({"category": "domain", "message": "%s price_increment 越域 [0,999]: %d" % [pid, inc]})
|
||||
# pool_shelf:kind_id 词表封闭(四切边后种)+ 唯一 + display_name + base_price 域 [1,999](迭代 11 第②批 T1)
|
||||
# pool_shelf:kind_id 词表封闭(四切边后种)+ 唯一 + display_name + base_price 域 [1,999]
|
||||
var seen_kind := {}
|
||||
for row in pool_shelf:
|
||||
var kind: String = row.get("kind_id", "")
|
||||
|
||||
@@ -10,7 +10,7 @@ extends Resource
|
||||
# (特殊技内按权重、不受冷却限制——proposal §2.1 两道闸之二)。全局单旋钮(不分敌种)。
|
||||
@export var pity_threshold: int = 4
|
||||
|
||||
const SKILL_TYPES := ["basic", "damage", "buff", "debuff"] # 普通攻击 / 伤害 / 增益 / 妨害(封闭枚举——新效应词汇批 2026-09-19 由三扩四)
|
||||
const SKILL_TYPES := ["basic", "damage", "buff", "debuff"] # 普通攻击 / 伤害 / 增益 / 妨害(封闭枚举四型)
|
||||
const MULT_DOMAIN_MIN := 1.1 # damage_mult 域 [1.1, 16] provisional(proposal §2.2)
|
||||
const MULT_DOMAIN_MAX := 16.0
|
||||
const RIDER_MULT_MAX := 1.0 # rider_mult 域 (0, 1](必中附加分量——proposal-status-vocabulary §2;首发 0.75 锚 AC 折扣档最轻档)
|
||||
@@ -78,7 +78,7 @@ func validate() -> Array:
|
||||
var bd: int = int(row.get("buff_duration", 0))
|
||||
if bd < 1 or bd > 99:
|
||||
errs.append({"category": "domain", "message": "%s buff_duration 越域 [1,99]: %d" % [sid, bd]})
|
||||
# 妨害技(新效应词汇批 2026-09-19):0 伤害施放动作,对主角施加负面状态——
|
||||
# 妨害技:0 伤害施放动作,对主角施加负面状态——
|
||||
# 字段复用 buff_ref/buff_duration(落点由状态行 target_side 路由,ConfigDB side 守卫兜底);池纪律妨害 ≤1 在跨表通道
|
||||
if type == "debuff":
|
||||
if row.has("damage_mult"):
|
||||
|
||||
@@ -7,7 +7,7 @@ extends Resource
|
||||
@export var rows: Array[Dictionary] = [] # Array[Dictionary]
|
||||
|
||||
const TARGET_SIDES := ["hero", "enemy"]
|
||||
const UNITS := ["percent", "flat", "ac"] # 2026-09-16 护甲等级掷点批增 ac——AC 阈值轴效果(ac_up),非池百分比非定点值
|
||||
const UNITS := ["percent", "flat", "ac"] # ac——AC 阈值轴效果(ac_up),非池百分比非定点值
|
||||
const STACK_POLICIES := ["stack", "refresh", "independent"]
|
||||
|
||||
|
||||
|
||||
@@ -8,25 +8,25 @@ extends Resource
|
||||
|
||||
@export var rows: Array[Variant] = [] # Array[Dictionary] 键 item_id
|
||||
|
||||
# 计数键封闭枚举(原 AttunementGrowthTable.COUNTER_KEYS——迭代 21 T5 谓词表退役后就地内联;
|
||||
# 迭代 22 T1 换代:clear_count(旧工坊按关累计渠道)与 C6 通关口径(通关=打通整局冒险)不符,
|
||||
# 换名 adventure_cleared(生涯通关局数,save_manager 生涯计数域在役)并扩 vision_used(真视类工具
|
||||
# 累计使用——信标网络行为条件计数,本批新增;use_treasure 受理成功处累计、冒险收束导出入档);
|
||||
# 旧 clear_count 零行消费,换名安全)
|
||||
# 计数键封闭枚举(就地内联于本表,无独立谓词表;
|
||||
# 通关口径=C6(通关=打通整局冒险),键名 adventure_cleared
|
||||
# (生涯通关局数,save_manager 生涯计数域在役);扩 vision_used(真视类工具
|
||||
# 累计使用——信标网络行为条件计数;use_treasure 受理成功处累计、冒险收束导出入档);
|
||||
# 键域以本枚举为准)
|
||||
const UNLOCK_COUNTER_KEYS := ["adventure_cleared", "boss_kill_count", "vision_used"]
|
||||
|
||||
const DIMENSIONS := ["info", "risk", "action", "plan", "counter"]
|
||||
# 同调批扩 enum(迭代 11 第①批 T1)——新增九 kind 全部条件触发/机制修正类;mine_reveal/ap_grant 语义不变。
|
||||
# cell_removal = 爆破工兵包中立移除格(T7 原位换装时启用——T6 影响评估前置);mine_hit_halve 系固定
|
||||
# enum 九 kind 全部条件触发/机制修正类;mine_reveal/ap_grant 语义不变。
|
||||
# cell_removal = 爆破工兵包中立移除格;mine_hit_halve 系固定
|
||||
# 「减半」语义不消费 magnitude(反弹 N 由 reflect_damage 行 magnitude 承载)。
|
||||
# 内容数据驱动扩展批 T2 挂账:主动件 kind 词族换代(reveal_tiles/preview/grant_ap/remove_mines/
|
||||
# refresh_pick——效果引擎 ACTIVE_KINDS 同名闭集),被动件 effect_kinds 词族随 T4 秘宝迁移退役
|
||||
# (passive 行改 triggers 事件订阅形态);旧词保留共存至迁移完成。
|
||||
# 主动件 kind 词族(reveal_tiles/preview/grant_ap/remove_mines/
|
||||
# refresh_pick——效果引擎 ACTIVE_KINDS 同名闭集);被动件不走 effect_kinds 词族
|
||||
# (passive 行走 triggers 事件订阅形态);旧词保留共存至迁移完成。
|
||||
const EFFECT_KINDS := ["mine_reveal", "reward_preview", "ap_grant",
|
||||
"mine_hit_halve", "reflect_damage", "ap_rounds_front", "ap_charge_safe", "cell_removal",
|
||||
"reward_weight_bias", "bonus_refresh",
|
||||
"reveal_tiles", "preview", "grant_ap", "remove_mines", "refresh_pick",
|
||||
# 迭代 22 秘宝实装批 T1 扩签八型:
|
||||
# 以下八型:
|
||||
# 常时参数面六型(无事件订阅——开局/装板参数注入,注入面按装配集 kind 分发读取):
|
||||
# light_radius_bonus=微光半径加值〔阔视野带〕/rap_transfer_mult=停手转存率千分比〔储备罐 125=1.25〕/
|
||||
# dig_range_bonus=挖掘距离加值〔远程镐〕/seal_range_free=封印不限相邻〔信标网络〕/
|
||||
@@ -34,34 +34,34 @@ const EFFECT_KINDS := ["mine_reveal", "reward_preview", "ap_grant",
|
||||
# 主动分发两型:safe_dig=安全格直开(天眼——走普通挖格规则全额兑付,C5 裁)/mine_count_preview=危险格数量预告(巢穴预告)
|
||||
"light_radius_bonus", "rap_transfer_mult", "dig_range_bonus", "seal_range_free",
|
||||
"refresh_stock_grant", "shop_refresh_bonus", "safe_dig", "mine_count_preview"]
|
||||
const TARGETS := ["none", "area_3x3", "board_wide", "cell"] # 迭代 22 T1 扩 cell=单格真视(线香——≤3×3 窄域契约内)
|
||||
const TARGETS := ["none", "area_3x3", "board_wide", "cell"] # cell=单格真视(线香——≤3×3 窄域契约内)
|
||||
const USE_PHASES := ["active_dig"]
|
||||
const FORMS := ["", "passive", "active_charged"]
|
||||
# 档位词表(迭代 22 T1——槽位档位约束数据面:槽位可装 ≤ 槽位档位的件;词表沿 feats/adventure TIERS)
|
||||
# 档位词表(槽位档位约束数据面:槽位可装 ≤ 槽位档位的件;词表沿 feats/adventure TIERS)
|
||||
const TIERS := ["common", "excellent", "rare", "legendary"]
|
||||
# 档位序(迭代 22 T2——装配档位校验取数源:件档 rank ≤ 装入位序(格 1=普通格/格 2=优秀格/格 3=稀有格
|
||||
# 档位序(装配档位校验取数源:件档 rank ≤ 装入位序(格 1=普通格/格 2=优秀格/格 3=稀有格
|
||||
# 递增,向下兼容=高档格可装低档件);rank 越大档越高)
|
||||
const TIER_RANK := {"common": 1, "excellent": 2, "rare": 3, "legendary": 4}
|
||||
# 充能重置周期(迭代 22 T1 扩签——现役仅 adventure=每冒险;battle=每战重置〔线香〕/level=每关重置〔天眼〕)
|
||||
# 充能重置周期(现役仅 adventure=每冒险;battle=每战重置〔线香〕/level=每关重置〔天眼〕)
|
||||
const CHARGE_RESETS := ["adventure", "battle", "level"]
|
||||
# magnitude 消费面(≥1 校验):grant_ap 与同调 N 值 kind;其余 kind 活行 magnitude 恒 0
|
||||
const MAGNITUDE_KINDS := ["ap_grant", "reflect_damage", "ap_rounds_front",
|
||||
"ap_charge_safe", "reward_weight_bias", "bonus_refresh", "grant_ap",
|
||||
"light_radius_bonus", "rap_transfer_mult", "dig_range_bonus", "seal_range_free",
|
||||
"refresh_stock_grant", "shop_refresh_bonus"]
|
||||
# 真视类 kind(迭代 22 T1 护栏扩面——活行 target 必须 ∈ {area_3x3, cell}:真视窄域 ≤3×3 契约)
|
||||
# 真视类 kind(护栏:活行 target 必须 ∈ {area_3x3, cell}:真视窄域 ≤3×3 契约)
|
||||
const REVEAL_KINDS := ["mine_reveal", "reveal_tiles"]
|
||||
# 🔴 零裸 flat 结构护栏:行键白名单封闭(键出域=加载致命)——机制轴禁无条件属性加项的表结构承载
|
||||
const KEY_WHITELIST := ["item_id", "display_name", "dimension_tags", "effect_kinds", "target",
|
||||
"magnitude", "use_phase", "retired", "deferred", "form", "charges_per_adventure", "slot_cost",
|
||||
"unlock_requires", "effective_rounds", "charge_n", "triggers", "desc",
|
||||
"tier", "charge_reset", "unlock_keystone_cost"] # 迭代 22 T1 扩三键(档位/充能周期/钥石购买价)
|
||||
"tier", "charge_reset", "unlock_keystone_cost"] # 三键=档位/充能周期/钥石购买价
|
||||
|
||||
|
||||
func validate() -> Array:
|
||||
var errs: Array[Variant] = []
|
||||
if rows.is_empty():
|
||||
# 空表合法态(2026-09-17 用户拍板——切片期无道具内容;F2 覆盖检查仅对非空清单执行)
|
||||
# 空表合法态(切片期无道具内容;F2 覆盖检查仅对非空清单执行)
|
||||
return errs
|
||||
var seen := {}
|
||||
for row in rows:
|
||||
@@ -95,8 +95,8 @@ func validate() -> Array:
|
||||
for k in row.keys():
|
||||
if not KEY_WHITELIST.has(String(k)):
|
||||
errs.append({"category": "zero_flat", "message": "%s 行键出白名单: %s(KEY_WHITELIST 外键禁入)" % [iid, String(k)]})
|
||||
# triggers 事件订阅行(内容数据驱动扩展批 T2/T4——被动件换代形态;与 effect_kinds 互斥校验
|
||||
# 面=passive 行(换代单形态——效果行为只走订阅);active_charged 行两形合法并存
|
||||
# triggers 事件订阅行(被动件形态;与 effect_kinds 互斥校验
|
||||
# 面=passive 行(单形态——效果行为只走订阅);active_charged 行两形合法并存
|
||||
# (effect_kinds=主动分发通道 + triggers=受理闸订阅,掘进蓄能先例))
|
||||
if row.has("triggers"):
|
||||
if (row.get("effect_kinds", []) as Array).size() > 0 and String(row.get("form", "")) != "active_charged" and not bool(row.get("retired", false)):
|
||||
@@ -120,7 +120,7 @@ func validate() -> Array:
|
||||
errs.append({"category": "dup_key", "message": "%s effect_kind 重复: %s" % [iid, kind]})
|
||||
kind_seen[kind] = true
|
||||
# 承重护栏①真视窄域:真视类活行 target 必须 area_3x3 或 cell(一次最多 3×3,禁全图看雷;
|
||||
# 迭代 22 T1 护栏扩面——reveal_tiles 与新 cell 档纳入同护栏,线香单格=cell ⊂ 3×3 契约内)
|
||||
# reveal_tiles 与 cell 档纳入同护栏,线香单格=cell ⊂ 3×3 契约内)
|
||||
for rv in REVEAL_KINDS:
|
||||
if kind_seen.has(String(rv)) and not (target == "area_3x3" or target == "cell"):
|
||||
errs.append({"category": "load_bearing", "message": "%s 真视窄域护栏:%s 行 target 必须 area_3x3 或 cell,实得 %s(秘宝 GDD 结构冻结)" % [iid, String(rv), target]})
|
||||
@@ -143,7 +143,7 @@ func validate() -> Array:
|
||||
if form != "":
|
||||
if int(row.get("slot_cost", 1)) != 1:
|
||||
errs.append({"category": "load_bearing", "message": "%s 同调占位统一 1 槽(slot_cost 恒 1——底稿裁决 5,schema 强制)" % iid})
|
||||
# 档位必填(迭代 22 T1——槽位档位约束数据面:件档≤格档判定的取数源)
|
||||
# 档位必填(槽位档位约束数据面:件档≤格档判定的取数源)
|
||||
var tier: String = String(row.get("tier", ""))
|
||||
if not TIERS.has(tier):
|
||||
errs.append({"category": "domain", "message": "%s 同调行 tier 必填且 ∈ %s,实得 %s" % [iid, str(TIERS), tier]})
|
||||
@@ -153,7 +153,7 @@ func validate() -> Array:
|
||||
if form == "active_charged" and charges < 0:
|
||||
errs.append({"category": "domain", "message": "%s active_charged 行 charges_per_adventure 越域 ≥0,实得 %d" % [iid, charges]})
|
||||
if form == "active_charged" and charges == 0:
|
||||
# 充能 0=积点制/滑耗制合法态(迭代 22 T1——试炼镐转化获取,无固定充能上限):
|
||||
# 充能 0=积点制/滑耗制合法态(试炼镐转化获取,无固定充能上限):
|
||||
# 必须带使用闸——ap_charge_safe 滑耗(安全格积点)或 treasure_used+counter_spend 受理闸,二者缺一即孤儿行
|
||||
var has_gate := kind_seen.has("ap_charge_safe")
|
||||
if not has_gate and row.has("triggers"):
|
||||
@@ -163,13 +163,13 @@ func validate() -> Array:
|
||||
break
|
||||
if not has_gate:
|
||||
errs.append({"category": "cross_field", "message": "%s active_charged 行 charges=0(积点制)须带 ap_charge_safe 或 counter_spend 受理闸,实得二者皆无" % iid})
|
||||
# 充能重置周期(迭代 22 T1 扩签:adventure 缺省/battle 每战/level 每关;仅 active_charged 行可设)
|
||||
# 充能重置周期(adventure 缺省/battle 每战/level 每关;仅 active_charged 行可设)
|
||||
var creset: String = String(row.get("charge_reset", "adventure"))
|
||||
if not CHARGE_RESETS.has(creset):
|
||||
errs.append({"category": "enum", "message": "%s charge_reset 出域 %s: %s" % [iid, str(CHARGE_RESETS), creset]})
|
||||
if form != "active_charged" and row.has("charge_reset") and creset != "adventure":
|
||||
errs.append({"category": "cross_field", "message": "%s 非 active_charged 行 charge_reset 仅可缺省(adventure),实得 %s" % [iid, creset]})
|
||||
# 解锁混合制字段校验(迭代 22 T1 契约 6:钥石购买价与条件谓词互斥——每件单解锁方式,混合不做)
|
||||
# 解锁混合制字段校验(契约 6:钥石购买价与条件谓词互斥——每件单解锁方式,混合不做)
|
||||
var price: int = int(row.get("unlock_keystone_cost", 0))
|
||||
var ureq_count: int = (row.get("unlock_requires", []) as Array).size()
|
||||
if price != 0 and ureq_count > 0:
|
||||
@@ -206,7 +206,7 @@ func validate() -> Array:
|
||||
return errs
|
||||
|
||||
|
||||
# 同调行词表(form 非空)——装配覆盖层/效果接线/同调位上限的唯一取数口径(迭代 11 第①批 T1)
|
||||
# 同调行词表(form 非空)——装配覆盖层/效果接线/同调位上限的唯一取数口径
|
||||
func attunement_rows() -> Array:
|
||||
var out: Array[Variant] = []
|
||||
for row in rows:
|
||||
@@ -216,7 +216,7 @@ func attunement_rows() -> Array:
|
||||
|
||||
|
||||
# 过渡行词表(form 空 = 切片期过渡形态活行)——掉落投放/停点池/过渡覆盖面的取数口径;
|
||||
# 同调行永不入过渡渠道。T7 退役面切换后本口径随渠道消亡。
|
||||
# 同调行永不入过渡渠道。
|
||||
func transitional_rows() -> Array:
|
||||
var out: Array[Variant] = []
|
||||
for row in rows:
|
||||
|
||||
@@ -7,14 +7,14 @@ extends Resource
|
||||
|
||||
@export var rows: Array[Dictionary] = [] # Array[Dictionary]
|
||||
# 布置参数(表头字段;数量公式 = round(候选池格数 × percent_of_cells / 100) + (关卡序号 − 1) × per_level_bonus,
|
||||
# 连通片生长、单片 ≤ pack_max——2026-09-19 用户拍板:图规模百分比 × 关卡递增,全连通、单片不超 4;
|
||||
# 2026-09-21 用户拍板基数修正:百分比基数 = 候选池(安全格 − 初始展开区,即实际可挖格)而非总格数)
|
||||
# 连通片生长、单片 ≤ pack_max——图规模百分比 × 关卡递增,全连通、单片不超 4;
|
||||
# 百分比基数 = 候选池(安全格 − 初始展开区,即实际可挖格)而非总格数)
|
||||
@export var percent_of_cells: int = 5
|
||||
@export var per_level_bonus: int = 2
|
||||
@export var pack_max: int = 4
|
||||
|
||||
const SCOPES := ["region", "global"]
|
||||
# resource_face = 资源面偏斜(全局作用域变异首发批 2026-10-04——整图修摇号分布:生存族按 effect_value%
|
||||
# resource_face = 资源面偏斜(全局作用域变异——整图修摇号分布:生存族按 effect_value%
|
||||
# 下调、进攻族等比上浮重归一,不动任何格子数据;effect_value 语义随行域切换,不适用 flat_damage [1,9])
|
||||
const EFFECTS := ["none", "flat_damage", "hide_number", "resource_face"] # 效果词表:直伤 / 数字遮蔽 / 资源面偏斜(可扩充)
|
||||
const TRIGGERS := ["on_open", "on_number_shown", "always"] # 触发词表:被打开即触发 / 打开且显示数字时触发 / 板级恒在无触发时机(可扩充)
|
||||
@@ -46,7 +46,7 @@ func validate() -> Array:
|
||||
errs.append({"category": "enum", "message": "%s trigger 出域: %s" % [vid, row.get("trigger")]})
|
||||
if not PLACEMENTS.has(row.get("placement", "")):
|
||||
errs.append({"category": "enum", "message": "%s placement 出域: %s" % [vid, row.get("placement")]})
|
||||
# 跨字段互锁(加载期致命——枚举语义一致性随首批类型定稿批 2026-09-19 立闸):
|
||||
# 跨字段互锁(加载期致命——枚举语义一致性立闸):
|
||||
# 伤害型 = 挖开即伤 ⇒ 触发 on_open + 只落未开格(closed_only)+ 伤害量为加法核小整数
|
||||
if row.get("effect", "") == "flat_damage":
|
||||
if row.get("trigger", "") != "on_open":
|
||||
@@ -61,7 +61,7 @@ func validate() -> Array:
|
||||
errs.append({"category": "cross_field", "message": "%s hide_number ⇒ trigger 必为 on_number_shown" % vid})
|
||||
# 信息遮蔽型只允许落安全格(遮蔽对象是数字;危险格无数字可遮)——placement=any 时由读取方
|
||||
# 呈现面按 kind=safe 自然短路,此处不设布置约束
|
||||
# 全局作用域型互锁(2026-10-04 首发批——resource_face ⟺ scope=global 三条 + 新值域;
|
||||
# 全局作用域型互锁(resource_face ⟺ scope=global 三条 + 新值域;
|
||||
# 判定键=effect/scope 词值,既有六 region 行(flat_damage/hide_number)不入本组——零误伤)
|
||||
if row.get("effect", "") == "resource_face":
|
||||
if row.get("scope", "") != "global":
|
||||
|
||||
@@ -19,7 +19,7 @@ static func rest_line(healed: int, hp: int, max_hp: int) -> String:
|
||||
return "[color=#%s]恢复点:回复 %d HP(→ %d/%d)[/color]" % [UiKit.HEX_LOG_HEAL, healed, hp, max_hp]
|
||||
|
||||
|
||||
# 商店房行(GP+商店房实施批——到达即开店;购买/离开归玩家意图)
|
||||
# 商店房行(到达即开店;购买/离开归玩家意图)
|
||||
static func shop_line(gp: int) -> String:
|
||||
return "[color=#%s]商店房:GP %d——购买即时生效,离开后选下一跳[/color]" % [UiKit.HEX_LOG_GAIN, gp]
|
||||
|
||||
@@ -57,8 +57,8 @@ static func battle_end_text(result: String, level_end: bool) -> String:
|
||||
static func phase_text(round_index: int, battle_result: String, waiting: bool, boss_active: bool, boss_name: String) -> String:
|
||||
var boss_tag := " · Boss:%s" % boss_name if boss_active else ""
|
||||
if battle_result != "":
|
||||
# 打磨包 2026-09-29——判定文案两处措辞统一:相位行随结算判定行同词(战斗胜利/战斗失败,
|
||||
# 与 battle_end_text 同源措辞),「战斗结束」退役
|
||||
# 判定文案两处措辞统一:相位行随结算判定行同词(战斗胜利/战斗失败,
|
||||
# 与 battle_end_text 同源措辞),不用「战斗结束」
|
||||
return "第 %d 轮 · %s%s" % [round_index, "战斗胜利" if battle_result == "victory" else "战斗失败", boss_tag]
|
||||
if waiting:
|
||||
return "第 %d 轮 · 结算中%s" % [round_index, boss_tag]
|
||||
@@ -66,9 +66,9 @@ static func phase_text(round_index: int, battle_result: String, waiting: bool, b
|
||||
|
||||
|
||||
# Boss 名解析(runner Boss 节点 → enemy 表行名;缺省遗迹守卫)。
|
||||
# 行集多行后取**首行** display_name(2026-10-07 行集多行批——行序即契约的呈现面后果,
|
||||
# 行集多行时取**首行** display_name(行序即契约的呈现面后果,
|
||||
# Boss 行集混编护卫时 Boss 名显示可能为护卫名,知情代价登记 敌人系统 v0.15 / 出口报告;
|
||||
# 零内容期每坐标恰一行,行为零变化)
|
||||
# 每坐标恰一行时无此歧义)
|
||||
static func boss_name(runner, db) -> String:
|
||||
if runner != null:
|
||||
var node: Dictionary[Variant, Variant] = runner.current_node()
|
||||
@@ -79,7 +79,7 @@ static func boss_name(runner, db) -> String:
|
||||
return "遗迹守卫"
|
||||
|
||||
|
||||
# ---- 提升三选一文案族(迭代 10 奖励重构批——big_reward_text 随三件套退役)----
|
||||
# ---- 提升三选一文案族 ----
|
||||
|
||||
# 三选一停点头部行(回血反馈 + 选择引导;钥石增量行不上本面——单一展示面归战斗总结结算框,
|
||||
# level-clear.md v2 禁入清单口径)
|
||||
@@ -96,7 +96,7 @@ static func curio_pick_header(payload: Dictionary) -> String:
|
||||
|
||||
# 三选一选项卡文案(件名 + 档位 + 效果读数;值 = 冒险表行值,组合根喂入;TP0 令牌池——档位词
|
||||
# 对齐 v2.2 追认 b 口径:小/大前缀(小GP/大GP 族)、恢复/AP 不带档位词;道具件掷定前置时
|
||||
# treasure_line = 具体道具「名 · 效果摘要」行——池种退役后恒空走通用分支,机械惰性保留)
|
||||
# treasure_line = 具体道具「名 · 效果摘要」行——常空走通用分支)
|
||||
static func bonus_card_text(id: String, tier: String, value: int, treasure_line: String = "") -> String:
|
||||
var ts := ""
|
||||
if tier == "common":
|
||||
@@ -133,10 +133,10 @@ static func bonus_result_text(result: Dictionary) -> String:
|
||||
return "已选择:%s\n其余选项已丢弃(不可反悔)" % name
|
||||
|
||||
|
||||
# 特性卡片文案(迭代 12 T1;2026-10-04 呈现补强——第二行=效果摘要,用户反馈「不知道件是
|
||||
# 干嘛的」缺口承载。内容数据驱动扩展批 T3 文案表值化:摘要=数据行 desc 字段,零硬编码映射。
|
||||
# 2026-10-12 奇物呈现整改批:「件名 · 流派」族名字样摘除(内部分类禁上玩家可见面——新 29 行
|
||||
# 无 archetype,原 fallback 实显兜底词「流派」=僵尸文案),卡片=件名+效果摘要两行)
|
||||
# 特性卡片文案(第二行=效果摘要——须可读「不知道件是
|
||||
# 干嘛的」。文案表值化:摘要=数据行 desc 字段,零硬编码映射。
|
||||
# 「件名 · 流派」族名字样不上卡片(内部分类禁上玩家可见面——29 行
|
||||
# 表无 archetype,fallback 兜底词「流派」会实显僵尸文案),卡片=件名+效果摘要两行)
|
||||
static func feat_card_text(row: Dictionary) -> String:
|
||||
if row.is_empty() or String(row.get("feat_id", "")) == "":
|
||||
return "特性\n???"
|
||||
@@ -151,7 +151,7 @@ static func bonus_keystone_line(payload: Dictionary) -> String:
|
||||
return "钥石 +%d · 累计 %d" % [int(payload.get("keystone_gained", 0)), int(payload.get("keystone_total", 0))]
|
||||
|
||||
|
||||
# 奖励节点三选一头部行(迭代 10 追加批——与通关三选一共享同池;node-reward.md v2)
|
||||
# 奖励节点三选一头部行(与通关三选一共享同池;node-reward.md v2)
|
||||
static func node_curio_header() -> String:
|
||||
return "关卡奖励点\n从提升池抽三选一(与通关三选一同池同权重同上限):\n选择一件带走:"
|
||||
|
||||
@@ -168,7 +168,7 @@ static func adventure_summary_text(summary: Dictionary) -> String:
|
||||
int(summary.get("keystone_total", 0)), int(summary.get("master_seed", 0))]
|
||||
|
||||
|
||||
# 钥石入账明细文案(元进度实现批;迭代 10 改口径——钥石全源 = Boss 逐杀掉落,「大奖励」行退役;
|
||||
# 钥石入账明细文案(钥石全源 = Boss 逐杀掉落,无「大奖励」行;
|
||||
# name_lookup = item_id → 显示名投影,由组合根自 meta 表喂入)
|
||||
static func keystone_settle_text(detail: Dictionary, name_lookup: Dictionary = {}) -> String:
|
||||
var parts: Array[Variant] = []
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
class_name CampFlowContext
|
||||
extends RefCounted
|
||||
# 营地/商店投影的构造上下文(迭代 27 T4——审计 P1-5:控制器构造参数收拢为 context 对象,
|
||||
# 营地/商店投影的构造上下文(控制器构造参数收拢为 context 对象,
|
||||
# 原 13 参长签名收一处命名面——单测可直接 new() 本 context + 控制器获得独立实例化缝)。
|
||||
# 字段即原构造参数逐一同名迁移;依赖仅视图句柄 + 查询/编排 Callable(组合根注入)。
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
extends RefCounted
|
||||
|
||||
var db
|
||||
var _meta_get: Callable # () → 元进度账本现值(组合根 lambda——探针 isolate_save 换注面,迭代13 T4 实证)
|
||||
var _meta_get: Callable # () → 元进度账本现值(组合根 lambda——探针 isolate_save 换注面)
|
||||
var _save_get: Callable # () → 存档承载现值(同上——营地 recent 摘要读数面)
|
||||
var camp_overlay: CampOverlay
|
||||
var shop_overlay: ShopOverlay
|
||||
@@ -20,7 +20,7 @@ var _refresh_resume_button: Callable
|
||||
|
||||
|
||||
func _init(ctx: CampFlowContext) -> void:
|
||||
# 迭代 27 T4——构造参数收拢 context 对象(P1-5:13 参长签名收一处命名面;字段逐一同名迁移)
|
||||
# 构造参数收拢 context 对象(13 参长签名收一处命名面)
|
||||
db = ctx.db
|
||||
_meta_get = ctx.meta_get
|
||||
_save_get = ctx.save_get
|
||||
@@ -42,30 +42,30 @@ func open_camp() -> void:
|
||||
var meta = _meta_get.call()
|
||||
if meta == null:
|
||||
return
|
||||
meta.refresh_treasure_unlocks() # 迭代 22 T2——条件解锁刷新换代(旧工坊面退役)
|
||||
meta.refresh_treasure_unlocks() # 条件解锁刷新
|
||||
start_overlay.visible = false
|
||||
_open_modal.call(camp_overlay) # 模态栈单口(T3 收口批——登记层开合一律走栈)
|
||||
_open_modal.call(camp_overlay) # 模态栈单口(登记层开合一律走栈)
|
||||
camp_overlay.open_camp(camp_data())
|
||||
_refresh_presentations.call()
|
||||
|
||||
|
||||
func on_camp_closed() -> void:
|
||||
_close_modal.call(camp_overlay) # 模态栈单口(T3 收口批)
|
||||
_close_modal.call(camp_overlay) # 模态栈单口
|
||||
start_overlay.visible = true
|
||||
_refresh_resume_button.call() # 迭代 8 卡②——回菜单按断点在档态刷新
|
||||
_refresh_resume_button.call() # 回菜单按断点在档态刷新
|
||||
_refresh_presentations.call() # 关闭后刷新顶栏(工坊解锁可能变了余额侧读数面)
|
||||
|
||||
|
||||
# 营地快照投影(纯读——秘宝匣 12 件/同调装配/树面/锻造工坊两区六路线/余额/最近冒险;拼句归
|
||||
# CampOverlay,账面归 MetaProgression。属性锻造三行投影已删——迭代 21 T4 柜台退役;工坊六行换代
|
||||
# = 迭代 22 T2 秘宝匣;forge 键=迭代 23 T6 锻造工坊路线行板投影)
|
||||
# CampOverlay,账面归 MetaProgression。锻造三行投影不在本投影内;
|
||||
# workshop 键=秘宝匣 12 件;forge 键=锻造工坊路线行板投影)
|
||||
func camp_data() -> Dictionary:
|
||||
var meta = _meta_get.call()
|
||||
return {
|
||||
"balance": meta.keystone_balance() if meta != null else 0,
|
||||
"workshop": meta.treasure_shop_status() if meta != null else [], # 键名沿 workshop(视图消费键不变——行内容=秘宝匣 12 件解锁投影)
|
||||
"forge": meta.forge_shop_status() if meta != null else [], # 迭代 23 T6——锻造工坊两区六路线投影
|
||||
"attune": meta.attune_camp_status() if meta != null else {}, # 迭代 11 第①批 T4——同调装配面投影
|
||||
"forge": meta.forge_shop_status() if meta != null else [], # 锻造工坊两区六路线投影
|
||||
"attune": meta.attune_camp_status() if meta != null else {}, # 同调装配面投影
|
||||
"tree": meta.tree_camp_rows() if meta != null else [], # 树面投影(含 forge_workshop/ks_workshop 钥匙门判据行)
|
||||
"fork_groups": meta.tree_fork_groups() if meta != null else [],
|
||||
"recent": _save_get.call().recent_summaries() if _save_get.call() != null else [],
|
||||
@@ -76,7 +76,7 @@ func on_camp_buy(attr: String) -> void:
|
||||
var meta = _meta_get.call()
|
||||
if meta == null:
|
||||
return
|
||||
# 秘宝钥石购买(迭代 22 秘宝批 T4——秘宝匣柜台购买钮意图;写口唯一=buy_treasure_unlock,
|
||||
# 秘宝钥石购买(秘宝匣柜台购买钮意图;写口唯一=buy_treasure_unlock,
|
||||
# 余额闸/重复购/条件件误购三重拒绝零副作用)
|
||||
if attr != "attune_refresh":
|
||||
if not meta.buy_treasure_unlock(attr):
|
||||
@@ -85,8 +85,8 @@ func on_camp_buy(attr: String) -> void:
|
||||
camp_overlay.refresh_camp(camp_data())
|
||||
_refresh_presentations.call()
|
||||
return
|
||||
# Y 刷新升级行购买(迭代 11 第①批 T4——P1 甲;双入口之一 Q1 裁乙——树上 ks_refresh 同账;
|
||||
# 按钮禁用为常态防线,兜底拒绝可见 pattern 1。属性锻造面已退役——buy_forge 分支随批删除)
|
||||
# Y 刷新升级行购买(双入口之一——树上 ks_refresh 同账;
|
||||
# 按钮禁用为常态防线,兜底拒绝可见 pattern 1。本函数无属性锻造分支)
|
||||
if not meta.buy_attune_refresh():
|
||||
toast.show_toast("购买被拒", "warn")
|
||||
return
|
||||
@@ -94,7 +94,7 @@ func on_camp_buy(attr: String) -> void:
|
||||
_refresh_presentations.call()
|
||||
|
||||
|
||||
# 同调装配切换(迭代 11 第①批 T4——装配关系唯一写口经 MetaProgression.toggle_attunement;满位兜底拒绝可见)
|
||||
# 同调装配切换(装配关系唯一写口经 MetaProgression.toggle_attunement;满位兜底拒绝可见)
|
||||
func on_camp_attune_toggle(item_id: String) -> void:
|
||||
var meta = _meta_get.call()
|
||||
if meta == null:
|
||||
@@ -105,7 +105,7 @@ func on_camp_attune_toggle(item_id: String) -> void:
|
||||
camp_overlay.refresh_camp(camp_data())
|
||||
|
||||
|
||||
# 天赋树购买(迭代 12 T9——写口唯一归 MetaProgression.tree_buy/tree_buy_fork;Fork 节点购=激活侧)
|
||||
# 天赋树购买(写口唯一归 MetaProgression.tree_buy/tree_buy_fork;Fork 节点购=激活侧)
|
||||
func on_camp_tree_buy(node_id: String) -> void:
|
||||
var meta = _meta_get.call()
|
||||
if meta == null:
|
||||
@@ -118,7 +118,7 @@ func on_camp_tree_buy(node_id: String) -> void:
|
||||
_refresh_presentations.call()
|
||||
|
||||
|
||||
# A/B 免费切换(迭代 12 T8——同组换侧零币;未激活组走购买面)
|
||||
# A/B 免费切换(同组换侧零币;未激活组走购买面)
|
||||
func on_camp_fork_toggle(group: String, side: String) -> void:
|
||||
var meta = _meta_get.call()
|
||||
if meta == null:
|
||||
@@ -130,7 +130,7 @@ func on_camp_fork_toggle(group: String, side: String) -> void:
|
||||
_refresh_presentations.call()
|
||||
|
||||
|
||||
# ---- 锻造工坊三意图(迭代 23 T6——契约 9:写口唯一归 MetaProgression 锻造写口族;UI 只发意图)----
|
||||
# ---- 锻造工坊三意图(契约 9:写口唯一归 MetaProgression 锻造写口族;UI 只发意图)----
|
||||
# 路线购买(几何价;余额不足/未解锁/满级=核心拒绝零副作用,兜底拒绝可见 pattern 1)
|
||||
func on_camp_forge_buy(route_id: String) -> void:
|
||||
var meta = _meta_get.call()
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
class_name CurioFlowContext
|
||||
extends RefCounted
|
||||
# 三选一/替换子交互的构造上下文(迭代 27 T4——审计 P1-5:控制器构造参数收拢为 context 对象,
|
||||
# 三选一/替换子交互的构造上下文(控制器构造参数收拢为 context 对象,
|
||||
# 原 16 参长签名收一处命名面——单测可直接 new() 本 context + 控制器获得独立实例化缝)。
|
||||
# 字段即原构造参数逐一同名迁移;依赖仅视图句柄 + 查询/编排 Callable(组合根注入)。
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@ extends RefCounted
|
||||
const Narrator := preload("res://presentation/battle_narrator.gd")
|
||||
|
||||
var db
|
||||
var _meta_get: Callable # () → 元进度账本现值(组合根 lambda——探针 isolate_save 换注面,迭代13 T4 实证)
|
||||
var _meta_get: Callable # () → 元进度账本现值(组合根 lambda——探针 isolate_save 换注面)
|
||||
var curio_overlay: CurioPickOverlay
|
||||
var end_overlay: BattleEndOverlay
|
||||
var board_view: BoardView
|
||||
@@ -18,18 +18,18 @@ var _adventure_get: Callable # () → 冒险结构现值(组合根
|
||||
var _runner_get: Callable # () → 关卡推进器现值(组合根 lambda——reward_node 继续推进)
|
||||
var _adventure_mode_get: Callable # () → 冒险模式标记现值(组合根 lambda)
|
||||
var _open_modal: Callable # 模态栈单口(组合根)
|
||||
var _open_modal_after_playback: Callable # 演播后排窗单口(T17——组合根)
|
||||
var _open_modal_after_playback: Callable # 演播后排窗单口(组合根)
|
||||
var _close_modal: Callable
|
||||
var _refresh_presentations: Callable
|
||||
var _fatal_screen: Callable
|
||||
var _gp_summary: Callable # () → 战斗总结 GP 行文案(组合根 lambda——_last_gain_amount 取数面在根)
|
||||
|
||||
var _pending_curio := {} # 三选一停点载荷暂存(迭代 10——战斗总结结算确认后开三选一面;续玩停点复用)
|
||||
var _pending_curio := {} # 三选一停点载荷暂存(战斗总结结算确认后开三选一面;续玩停点复用)
|
||||
var _curio_context := "level_clear" # 三选一面上下文(level_clear=通关停点→推进下一关 / reward_node=奖励节点→回图选路)
|
||||
|
||||
|
||||
func _init(ctx: CurioFlowContext) -> void:
|
||||
# 迭代 27 T4——构造参数收拢 context 对象(P1-5:16 参长签名收一处命名面;字段逐一同名迁移)
|
||||
# 构造参数收拢 context 对象(16 参长签名收一处命名面)
|
||||
db = ctx.db
|
||||
_meta_get = ctx.meta_get
|
||||
curio_overlay = ctx.curio_overlay
|
||||
@@ -49,7 +49,7 @@ func _init(ctx: CurioFlowContext) -> void:
|
||||
|
||||
|
||||
# 停点在途判据(组合根 _on_battle_end_continue 分派面——战斗总结确认后开三选一)
|
||||
# 续玩停点恢复入口(true=已接手呈现,调用方中止后续分派)。空候选降级(2026-10-06 用户令):
|
||||
# 续玩停点恢复入口(true=已接手呈现,调用方中止后续分派)。空候选降级:
|
||||
# 候选 0 件不开选择面(空面无卡无钮=死面)——消化停点后按上下文直落后续推进(战斗总结面已
|
||||
# 给过玩家确认拍,三选一环节整体跳过)
|
||||
func resume_if_pending() -> bool:
|
||||
@@ -61,7 +61,7 @@ func resume_if_pending() -> bool:
|
||||
return true
|
||||
|
||||
|
||||
# 停点空候选跳过(2026-10-06 用户令——「选出来 0 个则直接跳过该环节」):分派与 on_continue
|
||||
# 停点空候选跳过(「选出来 0 个则直接跳过该环节」):分派与 on_continue
|
||||
# 同构(level_clear=推进下一关 / reward_node=兑付确认回图选路 / feat_ticket=只关面回店),
|
||||
# 差异仅零面可收——curio_overlay 未开不 close。core 停点经 skip_empty_curio 消化后推进
|
||||
#(continue_to_next_level 的「停点未兑付拒推进」守卫因此放行)。
|
||||
@@ -90,18 +90,18 @@ func on_presented(payload: Dictionary) -> void:
|
||||
_pending_curio = payload.duplicate(true)
|
||||
_curio_context = "feat_ticket"
|
||||
if (_pending_curio.get("candidates", []) as Array).is_empty():
|
||||
# 空候选防御(2026-10-06 用户令——零卡不开死面;core _redeem_feat_ticket 守卫外的末道)
|
||||
# 空候选防御(零卡不开死面;core _redeem_feat_ticket 守卫外的末道)
|
||||
_skip_empty_stop()
|
||||
return
|
||||
_open_modal.call(curio_overlay)
|
||||
curio_overlay.open_pick(Narrator.node_curio_header(), _curio_card_texts(payload.get("candidates", []) as Array))
|
||||
return
|
||||
if int(payload.get("aux_cleared", 0)) > 0:
|
||||
toast.show_toast("上关剩余 %d 件秘宝已随关卡结束清空" % int(payload.get("aux_cleared", 0)), "info") # 迭代 7 T4 清空反馈
|
||||
toast.show_toast("上关剩余 %d 件秘宝已随关卡结束清空" % int(payload.get("aux_cleared", 0)), "info") # 清空反馈
|
||||
_pending_curio = payload.duplicate(true)
|
||||
_curio_context = "level_clear"
|
||||
if bool(payload.get("refreshed", false)):
|
||||
# 刷新重发(2026-10-06):refreshed 标直落选择面——不再重开胜利总结面
|
||||
# 刷新重发:refreshed 标直落选择面——不再重开胜利总结面
|
||||
show_pick()
|
||||
return
|
||||
end_overlay.open_victory(Narrator.battle_end_text("victory", false), "继续",
|
||||
@@ -111,15 +111,15 @@ func on_presented(payload: Dictionary) -> void:
|
||||
_refresh_presentations.call()
|
||||
|
||||
|
||||
# 奖励节点三选一(迭代 10 追加批——与通关三选一共享同池同权重同上限;CurioPickOverlay 共用面)。
|
||||
# 原「秘宝投放渠道」三选一(迭代 7 过渡形态)随批退役;替换子交互随迭代 22 T5 拆除。
|
||||
# 奖励节点三选一(与通关三选一共享同池同权重同上限;CurioPickOverlay 共用面)。
|
||||
# 无替换子交互(同调装配不可变,见类头)。
|
||||
func present_node_curio() -> void:
|
||||
var adventure = _adventure_get.call()
|
||||
_curio_context = "reward_node"
|
||||
var node_draw: Dictionary[Variant, Variant] = adventure.node_curio_candidates()
|
||||
var node_cands: Array[Variant] = node_draw.get("candidates", []) as Array
|
||||
if node_cands.is_empty():
|
||||
# 空池降级(2026-10-06 用户令)——不开面、不挂节点停点(present_node_curio 未调即未挂),
|
||||
# 空池降级——不开面、不挂节点停点(present_node_curio 未调即未挂),
|
||||
# 兑付确认直落回图选路(auto-sim「node_entered 回调内立即 continue_after_node」先例)
|
||||
toast.show_toast("可用战利品为空——本次三选一跳过", "info")
|
||||
var runner = _runner_get.call()
|
||||
@@ -129,7 +129,7 @@ func present_node_curio() -> void:
|
||||
_open_modal.call(curio_overlay)
|
||||
curio_overlay.open_pick(Narrator.node_curio_header(), _curio_card_texts(node_cands))
|
||||
adventure.present_node_curio(node_cands)
|
||||
_feed_refresh_display(adventure) # 迭代 11 第①批 T5b + 2026-10-06 对齐商店——刷新面投喂单点
|
||||
_feed_refresh_display(adventure) # 刷新面投喂单点
|
||||
|
||||
|
||||
# 三选一面开出(战斗总结结算确认 / 续玩停点恢复)
|
||||
@@ -139,7 +139,7 @@ func show_pick() -> void:
|
||||
curio_overlay.open_pick(Narrator.curio_pick_header(payload), _curio_card_texts(payload.get("candidates", []) as Array))
|
||||
var adventure = _adventure_get.call()
|
||||
if adventure != null:
|
||||
_feed_refresh_display(adventure) # 迭代 11 第①批 T5b + 2026-10-06 对齐商店——刷新面投喂单点
|
||||
_feed_refresh_display(adventure) # 刷新面投喂单点
|
||||
_refresh_presentations.call()
|
||||
|
||||
|
||||
@@ -173,10 +173,10 @@ func _curio_card_texts(candidates: Array) -> Array:
|
||||
|
||||
|
||||
# 道具件卡片行(掷定前置——具体道具名 + 效果摘要;空 iid = 无预掷回退态显示通用文案)。
|
||||
# 内容数据驱动扩展批 T4 文案表值化:效果摘要=数据行 desc 字段直读(零 kind 硬编码映射——
|
||||
# 文案表值化:效果摘要=数据行 desc 字段直读(零 kind 硬编码映射——
|
||||
# 新件写行即有名有姓)
|
||||
|
||||
# 三选一刷新(迭代 11 第①批 T5b X/Y 双源 + 2026-10-06 用户令对齐商店 MC-1——免费位用尽转 GP 付费档:
|
||||
# 三选一刷新(X/Y 双源 + 对齐商店 MC-1——免费位用尽转 GP 付费档:
|
||||
# 同 shop_refresh_cost_base/curve 旋钮、同冒险钱包 runner.gp;通关停点经重发 curio_choice_presented 直落
|
||||
# 选择面,节点停点在此读在途态重 present——零自动推进契约不破,停点仍在途)
|
||||
func on_refresh() -> void:
|
||||
@@ -246,7 +246,7 @@ func on_continue() -> void:
|
||||
|
||||
|
||||
# 秘宝 item_id → 显示名投影(keystone_settle_text/替换子命名文案数据源——拼句归 Narrator);
|
||||
# 迭代 22 T2 数据源换代:旧工坊行族退役,改读 treasure 装配词表 12 件
|
||||
# 数据源=treasure 装配词表 12 件
|
||||
func item_name_lookup() -> Dictionary:
|
||||
var lookup := {}
|
||||
var meta = _meta_get.call()
|
||||
|
||||
@@ -26,7 +26,7 @@ func _init(db_ref, rng_ref, hero_override: Callable, seed_value: Callable) -> vo
|
||||
_seed_value = seed_value
|
||||
|
||||
|
||||
# 单场遭遇原语(2026-09-13 用户裁决:单场用户入口已删——本方法仅 ui_probe 单场段使用;
|
||||
# 单场遭遇原语(无单场用户入口——本方法仅 ui_probe 单场段使用;
|
||||
# 无延续、standalone 终局语义保留供探针战斗收尾)
|
||||
func start_single(level: int) -> bool:
|
||||
_level_mode = false
|
||||
|
||||
@@ -7,15 +7,15 @@ extends RefCounted
|
||||
|
||||
|
||||
# 每次结算行/轮次/节点变化/动作返回后调用(组合根全量重查装配)
|
||||
# 2026-09-26 C 区精简批:钱包 GP/钥石投喂退役(中/大循环资源迁出战斗屏——信息归属拍板);
|
||||
# HP/状态效果单投喂 B 区站桩(原 B/C 双投喂为重复展示——信息归属拍板单一展示面原则)
|
||||
# 钱包 GP/钥石不在本装配面投喂(中/大循环资源迁出战斗屏——信息归属单一展示面原则);
|
||||
# HP/状态效果单投喂 B 区站桩(B/C 双投喂属重复展示——单一展示面原则)
|
||||
static func refresh(stage: BattleStageView, info: HeroInfoPanelView, ap_bar: ApBarView,
|
||||
settlement, runner, level_active: bool, db, refresh_remaining: int) -> void:
|
||||
# 特性列表(v3.5 迭代 9 卡⑫ 2026-09-30——runner 统一加算区投影:商店购买与关卡大奖励
|
||||
# 特性列表(runner 统一加算区投影:商店购买与关卡大奖励
|
||||
# 共账本〔level_runner bonus{atk,max_hp,armor_class}〕;空加算=整节隐藏〔视图内空态纪律〕。
|
||||
# 呈现只读投影零核心触碰;行文格式=代拍板白名单项)
|
||||
# 呈现只读投影零核心触碰;行文格式=白名单项)
|
||||
if runner != null and level_active:
|
||||
var b: Dictionary[Variant, Variant] = runner.run_ledger.bonus # 迭代 27 T3——加算区住 RunLedger(P1-4 收口)
|
||||
var b: Dictionary[Variant, Variant] = runner.run_ledger.bonus # 加算区住 RunLedger
|
||||
var lines: Array[String] = []
|
||||
if int(b.get("atk", 0)) != 0:
|
||||
lines.append("攻击 %+d" % int(b.get("atk", 0)))
|
||||
@@ -26,18 +26,18 @@ static func refresh(stage: BattleStageView, info: HeroInfoPanelView, ap_bar: ApB
|
||||
info.set_level_bonuses(lines)
|
||||
else:
|
||||
info.set_level_bonuses([])
|
||||
# 特性持有投影(迭代 12 呈现补齐 2026-10-04——用户反馈「局内奖励选了什么看不到」:
|
||||
# 逐件行「件名 ×N」〔2026-10-12 整改换代,原逐流派聚合行退役〕;常驻可读+空态隐藏)
|
||||
# 特性持有投影(「局内奖励选了什么」须可读:
|
||||
# 逐件行「件名 ×N」;常驻可读+空态隐藏)
|
||||
if runner != null and level_active:
|
||||
info.set_feat_holdings(feat_holdings_lines(db, runner.feats_held()))
|
||||
else:
|
||||
info.set_feat_holdings([])
|
||||
if settlement == null:
|
||||
info.set_board_variants([]) # 战斗域角标出战斗即隐(首发批 2026-10-04——空态纪律)
|
||||
info.set_board_variants([]) # 战斗域角标出战斗即隐(空态纪律)
|
||||
_refresh_out_of_battle(stage, info, ap_bar, runner, level_active, db)
|
||||
return
|
||||
var s: Dictionary[Variant, Variant] = settlement.presentation_snapshot()
|
||||
# 地形变异常驻角标投影(全局作用域变异首发批 2026-10-04——板级变异快照 → 文案行;
|
||||
# 地形变异常驻角标投影(板级变异快照 → 文案行;
|
||||
# 空态隐藏=特性段先例;战斗全程常驻防中局遗忘)
|
||||
info.set_board_variants(board_variant_lines(db, s.get("board_variants", [])))
|
||||
var hero: Dictionary[Variant, Variant] = s.hero
|
||||
@@ -66,28 +66,28 @@ static func _refresh_hero(stage: BattleStageView, info: HeroInfoPanelView, s: Di
|
||||
var mod_text := ""
|
||||
if mod_total != 0:
|
||||
mod_text = "(%+d%% %s)" % [mod_total, "·".join(sources)]
|
||||
# B 区站桩 = HP 与状态效果唯一展示面(2026-09-26 信息归属拍板)——C 区不再重复投喂
|
||||
# B 区站桩 = HP 与状态效果唯一展示面(单一展示面原则)——C 区不重复投喂
|
||||
stage.hero_standee.set_hero(hp, max_hp)
|
||||
stage.hero_standee.set_effects(s.hero_effects)
|
||||
info.set_attack(atk, mod_text)
|
||||
# 我方 AC 唯一展示面 = C 区(2026-09-26 二轮拍板补入)——快照 armor_class 含战斗内 ac_up 折算
|
||||
# 我方 AC 唯一展示面 = C 区——快照 armor_class 含战斗内 ac_up 折算
|
||||
info.set_armor(int(hero.get("armor_class", 0)))
|
||||
|
||||
|
||||
# ---- 行动经济(AP 点阵 / RAP / 弃图刷新按钮态——RAP 读数 2026-09-26 自 C 区迁入本条) ----
|
||||
# ---- 行动经济(AP 点阵 / RAP / 弃图刷新按钮态——RAP 读数在本条) ----
|
||||
|
||||
static func _refresh_economy(ap_bar: ApBarView, s: Dictionary, db, refresh_remaining: int) -> void:
|
||||
ap_bar.set_ap(int(s.ap), int(db.action_economy.ap_per_round))
|
||||
ap_bar.set_rap(int(s.rap))
|
||||
var digging := String(s.phase) == ActionEconomy.PHASE_ACTIVE
|
||||
ap_bar.set_refresh_state(refresh_remaining,
|
||||
bool(s.can_refresh) and digging) # Waiting 代理已随 OQ12 退役——UI 仅挖掘期受理
|
||||
bool(s.can_refresh) and digging) # 无 Waiting 代理——UI 仅挖掘期受理
|
||||
ap_bar.set_stop_enabled(bool(s.hero.get("alive", false)) and digging)
|
||||
|
||||
|
||||
# 特性持有文案(迭代 12 呈现补齐——战斗 C 区与关卡选择页两处共用;空持有=空数组。
|
||||
# 2026-10-12 奇物呈现整改批:逐流派聚合行退役改逐件行「件名 ×N」——原聚合在新 29 行
|
||||
# (无 archetype 字段)下空键兜底实显「流派 ×N · 档一」僵尸文案,且流派档位机制已退役)
|
||||
# 特性持有文案(战斗 C 区与关卡选择页两处共用;空持有=空数组。
|
||||
# 行形态=逐件「件名 ×N」(非逐流派聚合)——表行
|
||||
# 无 archetype 字段,聚合形态会空键兜底实显「流派 ×N · 档一」僵尸文案;流派档位非现行机制)
|
||||
static func feat_holdings_lines(db, held: Array) -> Array[String]:
|
||||
var rlines: Array[String] = []
|
||||
if db == null or db.feats == null or (db.feats.rows as Array).is_empty() or held.is_empty():
|
||||
@@ -102,9 +102,9 @@ static func feat_holdings_lines(db, held: Array) -> Array[String]:
|
||||
return rlines
|
||||
|
||||
|
||||
# 地形变异常驻角标文案(全局作用域变异首发批 2026-10-04——板级变异 id → 文案行;
|
||||
# 地形变异常驻角标文案(板级变异 id → 文案行;
|
||||
# 空集 = 空数组=空态隐藏。三行制:变异名+两行效果说明(C 区 240px 宽防裁切)。
|
||||
# 文案 2026-10-04 用户人闸反馈常规化修订(原俚语表述退役)
|
||||
# 文案用常规表述,不用俚语
|
||||
static func board_variant_lines(db, ids: Array) -> Array[String]:
|
||||
var lines: Array[String] = []
|
||||
if db == null or db.variation == null:
|
||||
@@ -118,15 +118,15 @@ static func board_variant_lines(db, ids: Array) -> Array[String]:
|
||||
return lines
|
||||
|
||||
|
||||
# ---- 战斗收尾反馈与结算视觉同步(T3 HUD 单通道批 2026-10-02——组合根直改视图收敛入此) ----
|
||||
# ---- 战斗收尾反馈与结算视觉同步(组合根直改视图一律收敛入此) ----
|
||||
|
||||
# battle_over 收尾反馈:死亡兜底灰化 → 主角态回 idle → RAP 清零脉冲(调试收尾路)→ 状态全清可见反馈。
|
||||
# 调试/冒险两路共用;reset_rap=false = 冒险收束(RAP 清零由战斗末拍已毕,收束拍不再重放脉冲)。
|
||||
static func battle_end_feedback(stage: BattleStageView, ap_bar: ApBarView, reset_rap: bool) -> void:
|
||||
stage.gray_all_enemies() # 死亡兜底:站桩灰化残位(禁瞬消语义的战斗收尾面)
|
||||
stage.settle_hero_idle() # 主角态回 idle(迭代 6 T3 三态收尾)
|
||||
stage.settle_hero_idle() # 主角态回 idle(三态收尾)
|
||||
if reset_rap:
|
||||
ap_bar.set_rap(0) # RAP 清零脉冲 + 微文案(载体 2026-09-26 迁 D 区 AP 条)
|
||||
ap_bar.set_rap(0) # RAP 清零脉冲 + 微文案(载体=D 区 AP 条)
|
||||
stage.hero_standee.set_effects_cleared_feedback() # 状态全清可见反馈(增益状态×4④——载体迁 B 区站桩)
|
||||
|
||||
|
||||
@@ -152,7 +152,7 @@ static func _refresh_out_of_battle(stage: BattleStageView, info: HeroInfoPanelVi
|
||||
stage.hero_standee.set_hero(runner.hero_hp, runner.effective_max_hp())
|
||||
stage.hero_standee.set_effects([])
|
||||
stage.clear_enemies() # 节点间隙清敌方站桩残位(死亡瞬间的灰化呈现已在结算行驱动完成)
|
||||
ap_bar.set_rap(0) # battle_over RAP 清零反馈(行动经济 义务——载体 2026-09-26 迁 AP 条)
|
||||
ap_bar.set_rap(0) # battle_over RAP 清零反馈(行动经济 义务——载体=AP 条)
|
||||
|
||||
|
||||
# 三源拒绝可感知(排雷玩法 reject_reason/sub_reason → 玩家文案;pattern 1 双源拒绝反馈)
|
||||
@@ -181,7 +181,7 @@ static func outcome_text(outcome: Dictionary) -> String:
|
||||
return "动作被拒。"
|
||||
|
||||
|
||||
# ---- 调整板批量模拟报告装配(文案归 presenter——视图只 set_text;code-review 2026-09-12 批) ----
|
||||
# ---- 批量模拟报告装配(文案归 presenter——视图只 set_text) ----
|
||||
|
||||
static func tune_sim_report(n: int, atk_pct: int, hp_pct: int, wins: int, dmg_all: Array, hp_left_win: Array, hp_boss: Array, rounds_all: Array, death_nodes: Dictionary, base_hp: int) -> String:
|
||||
var lines: Array[String] = []
|
||||
|
||||
+216
-216
File diff suppressed because it is too large.
Load diff
@@ -1,6 +1,6 @@
|
||||
class_name ModalFlow
|
||||
extends RefCounted
|
||||
# 模态编排纯逻辑类(迭代 27 T4——审计 P1-5/P2-7:组合根可测化抽取 + 模态互斥域内聚)。
|
||||
# 模态编排纯逻辑类(组合根可测化 + 模态互斥域内聚)。
|
||||
# 依赖仅 ModalStack + 查询 Callable(审计处方原话)——零节点构建、零信号连接、零核心触碰
|
||||
#(同构:连接归组合根,本类只收注入)。可直接 new() 单测(V6)。
|
||||
# 职责三件:
|
||||
@@ -19,7 +19,7 @@ var start_visible_get: Callable # () -> bool——开始层可见(非模态
|
||||
var inter_visible_get: Callable # () -> bool——过场层可见(例外二)
|
||||
var settlement_get: Callable # () -> Variant——当前战斗编排器(null=战斗缺位例外三)
|
||||
var rows_empty_get: Callable # () -> bool——排播队列空(演播后排窗判据)
|
||||
var flush_rows: Callable # 开层前排空排播(卡⑦——明细带恒完整)
|
||||
var flush_rows: Callable # 开层前排空排播(明细带恒完整)
|
||||
var on_open_sync: Callable # 开侧同步钩子(状态角标+浮窗当局数据)
|
||||
var on_close_sync: Callable # 关侧同步钩子
|
||||
var replay_click: Callable # 演播完边沿无挂起时的锁期点击重放(main._replay_locked_click)
|
||||
@@ -38,7 +38,7 @@ func open(m: Control) -> void:
|
||||
var dm: Control = _deferred
|
||||
_deferred = null
|
||||
open(dm) # 原语义:先结挂起后继续开本层(不 return——两拍都走)
|
||||
flush_rows.call() # 卡⑦——任何覆盖层开启前排空排播队列(明细带恒完整,回看/停点面不缺行)
|
||||
flush_rows.call() # 任何覆盖层开启前排空排播队列(明细带恒完整,回看/停点面不缺行)
|
||||
stack.open(m)
|
||||
on_open_sync.call()
|
||||
|
||||
@@ -48,7 +48,7 @@ func close(m: Control) -> void:
|
||||
on_close_sync.call()
|
||||
|
||||
|
||||
# 演播后排窗(T17 用户令「伤害过程演完才切结算窗口」):排播队列在播=挂起,演播完边沿
|
||||
# 演播后排窗(伤害过程演完才切结算窗口):排播队列在播=挂起,演播完边沿
|
||||
# (pump_ended 派发)再开——旧路径前置 flush 会把击杀轮的伤害/死亡演出同帧倒掉。
|
||||
# 队列空=立即开(行为同旧路径)。
|
||||
func open_after_playback(m: Control) -> void:
|
||||
@@ -70,8 +70,8 @@ func pump_ended() -> void:
|
||||
replay_click.call()
|
||||
|
||||
|
||||
# 棋盘输入锁谓词(原 main._input_locked 随迁):模态互斥域(non_locking 层开态不计——迭代 18
|
||||
# 卡⑦ T2 防线)+ 三例外(开始层/过场层非模态全屏态 + 战斗缺位棋盘输入无载体)。
|
||||
# 棋盘输入锁谓词:模态互斥域(non_locking 层开态不计=调试浮窗防永久锁死的排除组)
|
||||
# + 三例外(开始层/过场层非模态全屏态 + 战斗缺位棋盘输入无载体)。
|
||||
func locks_input() -> bool:
|
||||
return stack.has_open_excluding_tags([ModalStackScript.TAG_NON_LOCKING]) \
|
||||
or bool(start_visible_get.call()) or bool(inter_visible_get.call()) or settlement_get.call() == null
|
||||
|
||||
@@ -6,10 +6,10 @@ extends RefCounted
|
||||
|
||||
var _list: Array[Control] = [] # 登记序 = z 遮挡序(低 → 高)
|
||||
var _occluded := {} # modal → 被其隐藏的上方层(关闭恢复)
|
||||
var _tags := {} # modal → 排除组标签集(迭代 27 T4——互斥域随层登记;P2-7 开态谓词收口)
|
||||
var _tags := {} # modal → 排除组标签集(互斥域随层登记)
|
||||
|
||||
# 排除组封闭词表(组合根登记用——新标签=改此处+评审,防调用点拼装回潮)
|
||||
const TAG_NON_LOCKING := "non_locking" # 开态不计入棋盘输入锁与角标置灰(调试浮窗——迭代 18 卡⑦ T2 防线:调试浮窗默认开×遮挡复活×has_open 组合=棋盘永久锁死)
|
||||
const TAG_NON_LOCKING := "non_locking" # 开态不计入棋盘输入锁与角标置灰(调试浮窗默认开×遮挡复活×has_open 组合=棋盘永久锁死)
|
||||
const TAG_SCREEN_STATE := "screen_state" # 常驻屏态(关卡地图屏——自身开态不计入本屏角标判据)
|
||||
|
||||
|
||||
@@ -38,7 +38,7 @@ func close(m: Control) -> void:
|
||||
_occluded.erase(m)
|
||||
|
||||
|
||||
# 开态查询(T3 模态收口批 2026-10-02——组合根输入锁唯一判据:登记册上是否有任一层处于开态;
|
||||
# 开态查询(组合根输入锁唯一判据:登记册上是否有任一层处于开态;
|
||||
# 可见性即开态,与 open/close 记账同源——组合根不再手工罗列各层)
|
||||
func has_open() -> bool:
|
||||
for m in _list:
|
||||
@@ -47,9 +47,9 @@ func has_open() -> bool:
|
||||
return false
|
||||
|
||||
|
||||
# 排除组开态查询(迭代 27 T4——P2-7 模态协议内聚:原「调用点排除式拼装」has_open_excluding/
|
||||
# has_open_excluding_any 退役,排除语义随层登记(register tags),查询按组名集合表达。
|
||||
# 只读查询零副作用——登记序/开合记账语义零改动;空标签集=全计(与旧 has_open 同形)。
|
||||
# 排除组开态查询(模态协议内聚:排除语义随层登记(register tags),
|
||||
# 查询按组名集合表达,禁在调用点拼装 has_open_excluding/has_open_excluding_any。
|
||||
# 只读查询零副作用——登记序/开合记账语义不变;空标签集=全计(与 has_open 同形)。
|
||||
func has_open_excluding_tags(exclude_tags: Array) -> bool:
|
||||
for m in _list:
|
||||
if is_instance_valid(m) and m.visible:
|
||||
|
||||
@@ -53,10 +53,10 @@ func _on_battle_finished_log(p: Dictionary) -> void:
|
||||
_append({
|
||||
"kind": "battle_end", "result": String(p.get("result", "")),
|
||||
"hero_hp": int(p.get("hero_hp", 0)),
|
||||
"gp_dropped": int(p.get("gp_dropped", 0))}) # 战斗掉落列(GP+商店房实施批 2026-09-21——验收取数面)
|
||||
"gp_dropped": int(p.get("gp_dropped", 0))}) # 战斗掉落列(验收取数面)
|
||||
|
||||
|
||||
# ---- GP 钱包行(GP+商店房实施批——到账/购买/清零事件;runner wallet_changed 投影经组合根转发)----
|
||||
# ---- GP 钱包行(到账/购买/清零事件;runner wallet_changed 投影经组合根转发)----
|
||||
|
||||
func log_wallet(event: String, gp: int, extra: Dictionary = {}) -> void:
|
||||
var line := {"kind": "wallet", "event": event, "gp": gp}
|
||||
@@ -76,7 +76,7 @@ func log_ux(ux_kind: String, payload: Dictionary) -> void:
|
||||
|
||||
func log_action(kind: String, cell: Vector2i, accepted: bool, extra: Dictionary = {}) -> void:
|
||||
_ux_inputs += 1 # 原始输入动作数(含被拒——P4 ≤2 护栏观测口径 = 原始输入)
|
||||
# 逐操作落盘(2026-09-12 用户要求:操作记录完整持久化——此前仅入环形缓冲、卡死诊断才可见)
|
||||
# 逐操作落盘(操作记录须完整持久化,禁只入环形缓冲)
|
||||
var line := {"kind": "action", "op": kind, "cell": [cell.x, cell.y], "accepted": accepted}
|
||||
for k in extra.keys():
|
||||
line[k] = extra[k]
|
||||
@@ -106,8 +106,8 @@ func log_node(node: Dictionary, info: Dictionary, node_index: int) -> void:
|
||||
elif t == "rest":
|
||||
line["healed"] = int(info.get("healed", 0))
|
||||
elif t == "shop":
|
||||
line["gp"] = int(info.get("gp", 0)) # 进店时点余额(GP+商店房实施批)
|
||||
# reward 节点:内容待拍板(格面资源批 2026-09-15 道具删除)——零载荷
|
||||
line["gp"] = int(info.get("gp", 0)) # 进店时点余额
|
||||
# reward 节点:零载荷(节点内容未在呈现面定义)
|
||||
_append(line)
|
||||
|
||||
|
||||
@@ -126,10 +126,10 @@ func log_level_end(payload: Dictionary) -> void:
|
||||
|
||||
func _on_step_row(row: Dictionary) -> void:
|
||||
_ring(_recent_steps, {"t": row.type, "src": row.source_class, "v": int(row.value), "r": int(row.round_index)})
|
||||
# 结算明细行逐行落盘(2026-09-12 用户要求——五段结算完整回放数据面);
|
||||
# 结算明细行逐行落盘(五段结算完整回放数据面);
|
||||
# 公共七字段之外的全部行字段(payload:roll/ac/tier/crit/normal/pierce/magic/baseline/rider/target/
|
||||
# effect_id/pool_value 等)随行平铺落盘(2026-09-17 数值分析工具批——无 payload 的旧日志反事实只能
|
||||
# 比例近似,新日志可精确重算)。
|
||||
# effect_id/pool_value 等)随行平铺落盘(无 payload 时反事实只能
|
||||
# 比例近似,带 payload 可精确重算)。
|
||||
var line := {"kind": "step", "type": row.type, "source": row.source_class,
|
||||
"cell": row.origin_cell, "round": int(row.round_index),
|
||||
"text": String(row.display_name), "value": int(row.value),
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
## 视图句柄与 refresh_request/pump_ended 回调由组合根注入——本类零信号连接。
|
||||
extends Node
|
||||
|
||||
const PACE_SEGMENT_GAP := 0.3 # 段间阶梯延迟(代拍板:白名单 0.2–0.4s 内取值,追认清单呈报)
|
||||
const PACE_SEGMENT_GAP := 0.3 # 段间阶梯延迟(取值须落白名单 0.2–0.4s 档位带内)
|
||||
const PACE_ROW_GAP := 0.22 # 段内间隔(三轮 #3 上调 0.06→0.22——逐条演出可感知,配合数字浮现/状态脉冲)
|
||||
|
||||
var _rows: Array[Variant] = [] # 待排播行(step_row 载荷原样入队;__round_line 标记=轮首分隔行)
|
||||
@@ -54,7 +54,7 @@ func flush() -> void:
|
||||
|
||||
|
||||
func _process(delta: float) -> void:
|
||||
# 排播泵(卡⑦——演出层帧驱动;队列空=零开销直返)
|
||||
# 排播泵(演出层帧驱动;队列空=零开销直返)
|
||||
if _rows.is_empty():
|
||||
_pace_wait = 0.0
|
||||
_prev_segment = ""
|
||||
@@ -79,7 +79,7 @@ func _process(delta: float) -> void:
|
||||
_prev_segment = seg
|
||||
|
||||
|
||||
# 行消费(排播队列的出队步 / 减动效快路共用):明细带入带+演出链+呈现刷新——与卡⑦前排位逐位一致
|
||||
# 行消费(排播队列的出队步 / 减动效快路共用):明细带入带+演出链+呈现刷新
|
||||
func _consume_row(row: Dictionary) -> void:
|
||||
if row.has("__round_line"):
|
||||
log_view.finish_settlement_round() # 上轮摘要行(明细带 pattern 3)随轮首分隔行落带
|
||||
@@ -94,14 +94,14 @@ func _consume_row(row: Dictionary) -> void:
|
||||
board_zone.fx.play_perfect_sweep()
|
||||
elif String(row.get("highlight", "")) == "seal_hit":
|
||||
board_zone.fx.play_seal_hit_projectile(true)
|
||||
# Boss 半血解锁技提示(迭代14④ T4——D8 呈现面:skill_unlock 行弹一次性技能名 toast,
|
||||
# Boss 半血解锁技提示(D8 呈现面:skill_unlock 行弹一次性技能名 toast,
|
||||
# 轻量非演出零动画资产;行文本 =「XX 解锁了 YY」,只报技能名不报数值——规格=敌人系统 意图呈现节)
|
||||
if String(row.get("highlight", "")) == "skill_unlock":
|
||||
board_zone.toast.show_toast(String(row.get("display_name", "")), "info")
|
||||
refresh_request.call()
|
||||
|
||||
|
||||
# 逐条结算标出(三轮 #3——用户裁决按结算顺序演出:先 Buff/Debuff 特效、再基础伤害数字;
|
||||
# 逐条结算标出(按结算顺序演出:先 Buff/Debuff 特效、再基础伤害数字;
|
||||
# 打中谁标谁、主角被打标主角;道具/奖励行逐条冒出)。目标解析失败兜底棋盘中心。
|
||||
func _play_settlement_mark(row: Dictionary) -> void:
|
||||
var mark_type := String(row.get("type", ""))
|
||||
@@ -135,7 +135,7 @@ func _play_cross_zone_fx(row: Dictionary) -> void:
|
||||
var target := String(row.get("target", ""))
|
||||
var crit := bool(row.get("crit", false))
|
||||
if target == "" or target == "hero":
|
||||
# 主角受击弹道(迭代 6 T5 拆「敌→主角零反馈」早退):⚔ 危险红飞向主角站桩;
|
||||
# 主角受击弹道(禁「敌→主角零反馈」早退):⚔ 危险红飞向主角站桩;
|
||||
# crit 只加重字号不加金(金=收益/胜利语义位,不用于敌方对主角输出)
|
||||
settlement_fx.play_projectile(from_v, stage.hero_standee_center(), "⚔",
|
||||
UiKit.RED_MINE, crit)
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
class_name RunScope
|
||||
extends Node
|
||||
# 一局会话状态承载(迭代 27 T1——P0-2「局生命周期物化」修复波)。
|
||||
# 一局会话状态承载(局生命周期单点)。
|
||||
# 会话 = 一次冒险(开新/续玩)或一次调试两层对局;组合根 _begin_session 创建本节点挂树、
|
||||
# 会话更替/终局整节点 queue_free()——旧手工复位清单(原 main._reset_run_scope 三项 +
|
||||
# _on_battle_end_continue 置空四件)随节点释放机制消亡,此后新增跨局字段零新增泄漏点
|
||||
@@ -16,7 +16,7 @@ var level_screen_active := false # 关卡独立屏激活状态(屏级互斥
|
||||
var boss_active := false # Boss 遭遇变体(阶段指示附 Boss 名)
|
||||
var was_settling := false # 结算相位上升沿检测(结算轮光带触发——演出链批)
|
||||
var hints_shown := {} # 一次性非模态提示去重(每 run 一次:chord/no_adjacent)
|
||||
var settlement_stop_timer := ArmedTimer.new() # 卡⑥ 结算框挂起计时(T3/D3 P4 乙——窗口 0=挂起到显式回落)
|
||||
var settlement_stop_timer := ArmedTimer.new() # 结算框挂起计时(窗口 0=挂起到显式回落)
|
||||
var pending_candidates: Array[Variant] = [] # 结算框挂起期的选路候选暂存(choices_presented 载荷)
|
||||
var locked_click := {} # 锁期点击缓存(T17 v3.1——最后一次;演播完边沿重放)
|
||||
var last_gain_amount := 0 # 最近一笔钱包入账(卡⑥ GP 增量取数面——wallet_changed gain 载荷)
|
||||
var last_gain_amount := 0 # 最近一笔钱包入账(GP 增量取数面——wallet_changed gain 载荷)
|
||||
@@ -1,6 +1,6 @@
|
||||
class_name AdventureScreen
|
||||
extends Control
|
||||
# 冒险屏壳(迭代 27 T1 场景拆分——审计 P0-1 第一步;内含关卡地图屏与战斗屏两容器)。
|
||||
# 冒险屏壳(内含关卡地图屏与战斗屏两容器)。
|
||||
# 两容器视图原样构建(build_battle_ui 归 ui_assembler 纪律不变、LevelMapScreen 类视图继续
|
||||
# 代码构建、零 .tscn 化改造——契约 2);战斗信号接线归组合根 main.gd(信号连接
|
||||
# 只在组合根)。树序语义:本屏壳挂 main 首子位(全模态之下)——屏内 level_screen 与
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
class_name CampScreen
|
||||
extends Control
|
||||
# 营地屏壳(迭代 27 T1 场景拆分——审计 P0-1 第一步)。
|
||||
# 营地屏壳(营地覆盖层的屏壳容器)。
|
||||
# 营地覆盖层视图原样构建(CampOverlay 类视图继续代码构建、零 .tscn 化改造——契约 2);
|
||||
# 购买/开合接线归组合根经 CampShopPresenter(信号连接只在组合根)。
|
||||
# 可独立实例化(F6/headless 挂载)——V2 探针断言载体。
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
class_name MenuScreen
|
||||
extends Control
|
||||
# 开始菜单屏壳(迭代 27 T1 场景拆分——审计 P0-1「场景作为组合单元/生命周期单元」第一步)。
|
||||
# 开始菜单屏壳(场景=组合单元与生命周期单元)。
|
||||
# 视图代码原样构建(节点构建归 ui_assembler.gd 纪律不变——本屏壳只持有与暴露句柄);
|
||||
# 按钮接线归组合根 main.gd(信号连接只在组合根)。
|
||||
# 可独立实例化(F6/headless 挂载)——V2「各新屏场景可独立实例化」探针断言载体。
|
||||
|
||||
@@ -14,7 +14,7 @@ var toast: ToastStack # 反馈面(board_zone.toast 注入)
|
||||
var board_view: BoardView
|
||||
var _settlement_get: Callable # () → 当前战斗结算现值(组合根 lambda)
|
||||
var _runner_get: Callable # () → 当前关卡推进器现值(组合根 lambda)
|
||||
var _use_selection := 0 # 选中句柄(迭代 7 T4——0=未选中;选中期棋盘点击=使用目标拾取)
|
||||
var _use_selection := 0 # 选中句柄(0=未选中;选中期棋盘点击=使用目标拾取)
|
||||
|
||||
|
||||
func _init(db_ref, meta_ref, info_panel_ref: HeroInfoPanelView, toast_ref: ToastStack,
|
||||
@@ -28,8 +28,8 @@ func _init(db_ref, meta_ref, info_panel_ref: HeroInfoPanelView, toast_ref: Toast
|
||||
_runner_get = runner_get
|
||||
|
||||
|
||||
# aux 解锁门控集组装(迭代 7 T5——拍板③甲:工坊解锁 ∩ 过渡活行词表;罗盘退役/失义件/同调行不入集
|
||||
# ——迭代 11 第①批 T1 切 treasure_transitional_rows 口径,同调行永不入过渡渠道)
|
||||
# aux 解锁门控集组装:工坊解锁 ∩ 过渡活行词表(treasure_transitional_rows 口径);
|
||||
# 罗盘/失义件/同调行不入集(同调行永不入过渡渠道)
|
||||
func unlocked_set() -> Dictionary:
|
||||
var out := {}
|
||||
if meta == null:
|
||||
@@ -47,7 +47,7 @@ func active_selection() -> int:
|
||||
|
||||
|
||||
# 槽点击(第一步):AP 类(target=none)点击即用(无目标);空间类进/出选中态(toggle 取消);
|
||||
# 自动兑付件展示行(handle<0)无使用动作——点击给说明提示(反哺批⑤)。
|
||||
# 自动兑付件展示行(handle<0)无使用动作——点击给说明提示。
|
||||
func on_slot_clicked(slot_handle: int) -> void:
|
||||
var settlement = _settlement_get.call()
|
||||
var runner = _runner_get.call()
|
||||
@@ -93,7 +93,7 @@ func attempt_use(handle: int, cell: Variant = null) -> void:
|
||||
_use_selection = 0
|
||||
refresh_slots()
|
||||
refresh_overlays()
|
||||
board_view.refresh() # 迭代 13 T8/D6——移除格即时落图(无移除态的件 diff 零变化幂等)
|
||||
board_view.refresh() # 移除格即时落图(无移除态的件 diff 零变化幂等)
|
||||
var arow: Dictionary[Variant, Variant] = db.treasure_item_row(String(v.get("item_id", "")))
|
||||
toast.show_toast("已使用 %s" % String(arow.get("display_name", "")), "info")
|
||||
|
||||
@@ -115,7 +115,7 @@ func inventory_projection() -> Array:
|
||||
for rec in runner.treasure_inventory.query_inventory(cb):
|
||||
var arow: Dictionary[Variant, Variant] = db.treasure_item_row(String(rec.get("item_id", "")))
|
||||
var dn := String(arow.get("display_name", ""))
|
||||
# 积点进度浮窗行(反哺批⑤——自动兑付件 C 区悬停 tooltip 补 N/M 进度;件 id 锁定=装配行唯一)
|
||||
# 积点进度浮窗行(自动兑付件 C 区悬停 tooltip 补 N/M 进度;件 id 锁定=装配行唯一)
|
||||
var note := ""
|
||||
if bool(cs.get("assembled", false)) and String(cs.get("item_id", "")) == String(rec.get("item_id", "")):
|
||||
note = "积点 %d/%d" % [int(cs.get("points", 0)), int(cs.get("need", 0))]
|
||||
@@ -129,7 +129,7 @@ func inventory_projection() -> Array:
|
||||
"usable": bool(rec.get("usable", true)),
|
||||
"note": note,
|
||||
})
|
||||
# 自动兑付件展示行(反哺批⑤补——charges=0 不入骑库存〔_reseed_attune_charges 跳过注入〕,
|
||||
# 自动兑付件展示行(charges=0 不入骑库存〔_reseed_attune_charges 跳过注入〕,
|
||||
# C 区四槽末尾补合成行承载悬停进度;handle=-1 非库存句柄:槽点击走说明 toast 分支不进两步交互)
|
||||
if bool(cs.get("assembled", false)) and not auto_seen:
|
||||
var adn := String(cs.get("display_name", ""))
|
||||
|
||||
@@ -28,7 +28,7 @@ func _autoplay():
|
||||
|
||||
|
||||
func run(n: int) -> void:
|
||||
# 共享日志会话(2026-09-12 用户要求:模拟运行与真人试玩同日志合规——操作/轮次/战斗/通关全量 JSONL;
|
||||
# 共享日志会话(模拟运行与真人试玩同日志合规——操作/轮次/战斗/通关全量 JSONL;
|
||||
# 一文件多局,run 行携带种子切分局次)
|
||||
var logger = PlaytestLogger.new()
|
||||
logger.ap_per_round = int(db.action_economy.ap_per_round)
|
||||
|
||||
@@ -5,15 +5,15 @@
|
||||
extends RefCounted
|
||||
|
||||
|
||||
# 战斗屏三区(B 战斗画面 / C·D 两列——A 区关卡进度条顶栏随 停点前移批 2026-09-23 退役:节点链
|
||||
# 进度由关卡画面本体(选路覆盖层整图)承载,28px 纵向空间拨给 C/D 底行 520→548;E 区道具面板
|
||||
# 已随可选类删除下线,2026-09-15 格面资源批)。
|
||||
# + 结算演出覆盖层(A–D 之上、模态之下——跨区弹道/光带载体,结算演出链批 2026-09-20)。
|
||||
# 战斗屏三区(B 战斗画面 / C·D 两列——节点链
|
||||
# 进度由关卡画面本体(选路覆盖层整图)承载,28px 纵向空间拨给 C/D 底行 520→548;
|
||||
# 无顶栏进度条、无道具面板区)。
|
||||
# + 结算演出覆盖层(A–D 之上、模态之下——跨区弹道/光带载体)。
|
||||
# 返回句柄字典:{stage, log_view, info_panel, board_zone, board_view, ap_bar, settlement_fx}
|
||||
# (gp_label/keystone_label 随 2026-09-26 信息归属拍板退役——GP/钥石迁出战斗屏,句柄不再透出)
|
||||
# (返回句柄不含 gp_label/keystone_label——GP/钥石不在战斗屏呈现)
|
||||
static func build_battle_ui(root: Control, db) -> Dictionary:
|
||||
# set_anchors_and_offsets_preset(非 set_anchors_preset):后者会把控件重置回最小内容尺寸
|
||||
# (实测 927×613——缩顶、棋盘 45px 格 ×10 行超出 D 区 333px 高被裁,2026-09-13 用户报告回归)
|
||||
# (实测 927×613——缩顶、棋盘 45px 格 ×10 行超出 D 区 333px 高被裁)
|
||||
root.set_anchors_and_offsets_preset(Control.PRESET_FULL_RECT)
|
||||
root.add_theme_constant_override("separation", 0)
|
||||
|
||||
@@ -21,7 +21,7 @@ static func build_battle_ui(root: Control, db) -> Dictionary:
|
||||
var stage := BattleStageView.new(db)
|
||||
root.add_child(stage)
|
||||
|
||||
# C/D 两列(548px——停点前移批 2026-09-23:原 A 区 28px 顶栏纵向空间并入)
|
||||
# C/D 两列(548px——含顶栏让出的 28px 纵向空间)
|
||||
var bottom := HBoxContainer.new()
|
||||
bottom.size_flags_vertical = Control.SIZE_EXPAND_FILL
|
||||
bottom.add_theme_constant_override("separation", 4)
|
||||
@@ -38,16 +38,16 @@ static func build_battle_ui(root: Control, db) -> Dictionary:
|
||||
var settlement_fx := SettlementFxView.new()
|
||||
root.add_child(settlement_fx)
|
||||
|
||||
# CJK 字体随包(2026-09-27 用户拍板 Fusion Pixel OFL):Control 侧默认主题字体走 project.godot
|
||||
# CJK 字体随包(Fusion Pixel OFL):Control 侧默认主题字体走 project.godot
|
||||
# gui/theme/custom_font;SubViewport 两世界不继承 Control 主题——显式注入挂点接线(C6 挂点链)。
|
||||
# 缺位守卫(load_font 返回 null = 引擎回退字体,与旧 Button 渲染同源,不炸)。
|
||||
# 缺位守卫(load_font 返回 null = 引擎回退字体,不炸)。
|
||||
var ui_font: Font = UiKit.load_font(UiKit.FONT_UI)
|
||||
if ui_font != null:
|
||||
stage.set_world_font(ui_font)
|
||||
board_zone.board_view.set_world_font(ui_font)
|
||||
|
||||
# GP/钥石余额位已随 2026-09-26 信息归属拍板退役(中/大循环资源迁出战斗屏——
|
||||
# GP 去向 = 关卡画面+战斗结算画面、钥石去向 = 结算画面+营地,呈现归对应 UX spec 批次)
|
||||
# GP/钥石余额位不在战斗屏(中/大循环资源迁出战斗屏——
|
||||
# GP 去向 = 关卡画面+战斗结算画面、钥石去向 = 结算画面+营地,呈现归对应 UX spec)
|
||||
return {
|
||||
"stage": stage,
|
||||
"log_view": stage.log_view,
|
||||
@@ -61,11 +61,11 @@ static func build_battle_ui(root: Control, db) -> Dictionary:
|
||||
|
||||
# 模态覆盖层组(z 序 = 登记序:图例 < 节点 < 终局 < 商店)。
|
||||
# 返回 {legend_overlay, node_overlay, end_overlay, shop_overlay};调用方按本序 addChild。
|
||||
# (调整板已随迭代 14.5 调试整合批退役——调试浮窗由组合根直构挂树序最末,见 main._build_ui;
|
||||
# 明细回看 overlay 已随 M9/P-C7 退役 2026-10-04——冒险中回看归「生涯」06 登记;
|
||||
# 营地覆盖层已随迭代 27 T1 场景拆分迁 camp_screen 屏壳——本组不再构建)
|
||||
# (本组不含调试浮窗——由组合根直构挂树序最末,见 main._build_ui;
|
||||
# 本组不含明细回看 overlay——冒险中回看归「生涯」页;
|
||||
# 本组不含营地覆盖层——在 camp_screen 屏壳构建)
|
||||
static func build_overlays(host: Control, db) -> Dictionary:
|
||||
var legend_overlay := LegendOverlay.new(int(db.action_economy.rap_refresh_cost)) # 打磨包 2026-09-29——RAP 行表值注入
|
||||
var legend_overlay := LegendOverlay.new(int(db.action_economy.rap_refresh_cost)) # RAP 行表值注入
|
||||
host.add_child(legend_overlay)
|
||||
|
||||
var node_overlay := NodeOverlay.new()
|
||||
@@ -74,7 +74,7 @@ static func build_overlays(host: Control, db) -> Dictionary:
|
||||
var end_overlay := BattleEndOverlay.new()
|
||||
host.add_child(end_overlay)
|
||||
|
||||
# 商店覆盖层(GP+商店房实施批 2026-09-21——商店房节点兑付呈现;按钮接线归组合根,装配器零信号连接)
|
||||
# 商店覆盖层(商店房节点兑付呈现;按钮接线归组合根,装配器零信号连接)
|
||||
var shop_overlay := ShopOverlay.new()
|
||||
host.add_child(shop_overlay)
|
||||
|
||||
@@ -87,23 +87,23 @@ static func build_overlays(host: Control, db) -> Dictionary:
|
||||
|
||||
|
||||
# 开始覆盖层(返回 {overlay, box, lvbtn, resumebtn, campbtn, careerbtn, settingsbtn, quitbtn}——按钮接线归组合根;
|
||||
# 迭代 14.5 调试整合批:随机种子/数值调整板/两层调试三散件撤出主按钮组,收进调试浮窗——
|
||||
# 2026-10-16 用户令:调试入口=UI 最顶层全局浮层角标(build_debug_badge,本层零调试入口)
|
||||
# UI 落码批 M2(2026-10-04):单列 VBox→双列(左操作 56% + 右舞台);按钮动词化+悬停解释
|
||||
#(解释全迁 tooltip,G5);lvbtn 升主操作钮(烛光黄 CANDLE);右列=立绘看台。**玩法速览牌已退役
|
||||
#(2026-10-06 用户令:界面禁玩法文字墙——教学内容归 Beta 帮助手册〔教学引导定稿批 P4 丙〕,历史文案在 git)**;
|
||||
# tooltip 主题归全局主题单点(迭代 25 2026-10-13——旧 root 挂程序化 tooltip 主题的做法退役)。
|
||||
# 返回键集不变,组合根接线零改)
|
||||
# 随机种子/数值调整板/两层调试三散件不在主按钮组,收进调试浮窗——
|
||||
# 调试入口=UI 最顶层全局浮层角标(build_debug_badge,本层零调试入口)
|
||||
# 布局=双列 VBox(左操作 56% + 右舞台);按钮动词化+悬停解释
|
||||
#(解释全迁 tooltip,G5);lvbtn 为主操作钮(烛光黄 CANDLE);右列=立绘看台。**界面禁玩法文字墙
|
||||
#(教学内容归 Beta 帮助手册〔教学引导定稿 P4 丙〕)**;本层无玩法速览牌;
|
||||
# tooltip 主题归全局主题单点(禁在 root 挂程序化 tooltip 主题)。
|
||||
# 返回键集如首行所列,接线归组合根)
|
||||
static func build_start_overlay(host: Control, db = null) -> Dictionary:
|
||||
var overlay := overlay_bg(host, UiKit.BG_BRIGHT)
|
||||
# 石纹弱质感+斜光带+暗角(UI 对齐批 T5 2026-10-06——demo .tilebg 0.14/.beams/.vignette 三层;
|
||||
# 旧满铺石纹不透退役。缺位不挂即回退平色)
|
||||
# 石纹弱质感+斜光带+暗角(demo .tilebg 0.14/.beams/.vignette 三层;
|
||||
# 石纹层用半透明贴图,非满铺不透。缺位不挂即回退平色)
|
||||
var bd := UiKit.backdrop_tile()
|
||||
if bd != null:
|
||||
overlay.add_child(bd)
|
||||
overlay.add_child(UiKit.beams_rect())
|
||||
overlay.add_child(UiKit.vignette_rect())
|
||||
# 双列骨架(M2):左=操作列 56% / 右=舞台+速览
|
||||
# 双列骨架:左=操作列 56% / 右=舞台(立绘看台)
|
||||
var cols := HBoxContainer.new()
|
||||
cols.set_anchors_preset(Control.PRESET_FULL_RECT)
|
||||
cols.add_theme_constant_override("separation", 32)
|
||||
@@ -124,14 +124,14 @@ static func build_start_overlay(host: Control, db = null) -> Dictionary:
|
||||
box.add_child(title)
|
||||
box.add_child(HSeparator.new())
|
||||
|
||||
# 组帽·冒险(M2 分组)
|
||||
# 组帽·冒险
|
||||
box.add_child(UiKit.label("── 冒 险 ──",UiKit.FS_CAPTION, false, UiKit.WARM_FAINT))
|
||||
# 唯一试玩入口 = 正式「开始冒险」(三关序列);单关测试直入口(L2/L3)退役(2026-09-23 用户拍板)
|
||||
var resumebtn := UiKit.primary_button("▶ 继续上局") # 迭代 8 卡②——有断点才亮(可见性与关数文案归组合根刷新);主操作钮=审计差距⑤对齐 demo
|
||||
# 唯一试玩入口 = 正式「开始冒险」(三关序列);无单关测试直入口
|
||||
var resumebtn := UiKit.primary_button("▶ 继续上局") # 有断点才亮(可见性与关数文案归组合根刷新);主操作钮对齐 demo
|
||||
resumebtn.tooltip_text = "自最近停点恢复"
|
||||
resumebtn.visible = false
|
||||
box.add_child(resumebtn)
|
||||
# 主操作语义对齐 demo(2026-10-06 审计差距⑤——有档时「继续上局」才是首动作=主黄钮,
|
||||
# 主操作语义对齐 demo(有档时「继续上局」才是首动作=主黄钮,
|
||||
# 「开始冒险」=常规石钮;tooltip 保留)
|
||||
var lvbtn := UiKit.button("▶ 开始冒险")
|
||||
lvbtn.tooltip_text = "三关连探:地精洞窟 → 幽暗地域 → 灰矮人矿城"
|
||||
@@ -139,30 +139,30 @@ static func build_start_overlay(host: Control, db = null) -> Dictionary:
|
||||
|
||||
# 组帽·营地与系统
|
||||
box.add_child(UiKit.label("── 营地与系统 ──",UiKit.FS_CAPTION, false, UiKit.WARM_FAINT))
|
||||
var campbtn := UiKit.verb_button("营地", UiKit.TEX_ICON_KEYSTONE, "永久强化:钥石换永久属性——跨局成长账本") # 元进度实现批——主菜单营地入口
|
||||
var campbtn := UiKit.verb_button("营地", UiKit.TEX_ICON_KEYSTONE, "永久强化:钥石换永久属性——跨局成长账本") # 主菜单营地入口
|
||||
box.add_child(campbtn)
|
||||
# 生涯页一级入口(迭代 18 卡④——与营地平级;页=局外只读跨局汇总,零跳转零购买)
|
||||
# 生涯页一级入口(与营地平级;页=局外只读跨局汇总,零跳转零购买)
|
||||
var careerbtn := UiKit.button("生涯")
|
||||
careerbtn.tooltip_text = "跨局生涯汇总:总局数 / 通关局数 / Boss 击杀 / 钥石余额 / 三柜台解锁概览(只读)"
|
||||
box.add_child(careerbtn)
|
||||
var settingsbtn := UiKit.button("设置") # 迭代 8 卡④——最小设置面入口
|
||||
var settingsbtn := UiKit.button("设置") # 最小设置面入口
|
||||
settingsbtn.tooltip_text = "音量 / 全屏 / 减动效"
|
||||
box.add_child(settingsbtn)
|
||||
# 二轮修改批 D1(2026-09-29 用户目检裁决):退出游戏与设置同层并行——只在一级菜单出现
|
||||
# 退出游戏与设置同层并行——只在一级菜单出现
|
||||
var quitbtn := UiKit.button("退出游戏")
|
||||
box.add_child(quitbtn)
|
||||
|
||||
# 迭代 14.5 调试整合批——调试入口收浮层角标;2026-10-16 用户令修正:boot 菜单优先不开
|
||||
# 调试入口收浮层角标;boot 菜单优先不开
|
||||
# 浮窗,调试入口=UI 最顶层全局浮层角标(build_debug_badge——LAYER_CONTRACT 末位、
|
||||
# ModalStack 登记外,任何 UI 不遮挡;对局中两屏角标形态随之退役,全局浮层全场景承载)
|
||||
# ModalStack 登记外,任何 UI 不遮挡;本层不设屏内调试角标,全局浮层全场景承载)
|
||||
|
||||
# 右列:立绘看台 + 玩法速览牌(M2——帮助全文保留表值驱动,仅容器化;内容含地形变异段不删)
|
||||
# 右列:立绘看台(三张立绘 + 标题底座;界面禁玩法文字墙,故无速览牌)
|
||||
var right := VBoxContainer.new()
|
||||
right.name = "start_right"
|
||||
right.size_flags_horizontal = Control.SIZE_EXPAND_FILL
|
||||
right.add_theme_constant_override("separation", 18)
|
||||
right.alignment = BoxContainer.ALIGNMENT_CENTER
|
||||
cols.add_child(right) # ui-struct-probe 批 T2:右列挂 HBox 第二列(原误挂 pad 与左列同矩形叠放——M2 双列失效根因)
|
||||
cols.add_child(right) # 右列挂 HBox 第二列(禁挂 pad——与左列同矩形叠放会破双列)
|
||||
var stage := VBoxContainer.new()
|
||||
stage.alignment = BoxContainer.ALIGNMENT_CENTER
|
||||
var foe_row := HBoxContainer.new()
|
||||
@@ -195,9 +195,9 @@ static func build_start_overlay(host: Control, db = null) -> Dictionary:
|
||||
"settingsbtn": settingsbtn, "quitbtn": quitbtn}
|
||||
|
||||
|
||||
# 调试浮层入口角标(2026-10-16 用户令:UI 最顶层浮层,任何 UI 不遮挡)——main 直接子层、
|
||||
# 调试浮层入口角标(UI 最顶层浮层,任何 UI 不遮挡)——main 直接子层、
|
||||
# LAYER_CONTRACT 末位(float 类:只落树序、不注册 ModalStack——open() 遮挡记账只动登记层,
|
||||
# 故永不被隐藏);右下角 16px(拇指区惯例,与旧三屏角标同域);可见性归组合根(_debug_entry
|
||||
# 故永不被隐藏);右下角 16px(拇指区惯例);可见性归组合根(_debug_entry
|
||||
# 门控落定后赋值);地图屏底栏 85px 常驻,offset 抬升本函数不处理(组合根 _sync_status_entry)。
|
||||
static func build_debug_badge(host: Control) -> Button:
|
||||
var badge := UiKit.button("调试", 0.0) # 角落小件豁免宽度基准(0=不设 min/seals——同状态角标)
|
||||
|
||||
@@ -59,7 +59,7 @@ func _init() -> void:
|
||||
spacer.size_flags_horizontal = Control.SIZE_EXPAND_FILL
|
||||
row.add_child(spacer)
|
||||
|
||||
# RAP 读数(2026-09-26 信息归属拍板自 C 区迁入):RAP 唯一消费口 = 弃图刷新——资源数字贴消费按钮
|
||||
# RAP 读数(自 C 区迁入):RAP 唯一消费口 = 弃图刷新——资源数字贴消费按钮
|
||||
# (就近原则);扣费文案「RAP−%d」已在刷新按钮副行,此处不重复代价提示(单一展示面原则)
|
||||
_rap_dots = HBoxContainer.new()
|
||||
_rap_dots.add_theme_constant_override("separation", 2)
|
||||
@@ -68,7 +68,7 @@ func _init() -> void:
|
||||
_rap_label = UiKit.label("RAP 0",UiKit.FS_HINT, true, UiKit.INK_LABEL) # 显式传色(UI 设计对齐批 N1-7——T0 实测对 PANEL_DATA 10.4:1 已合规,显式化仅为确定性加固,值不变)
|
||||
_rap_label.size_flags_vertical = Control.SIZE_SHRINK_CENTER
|
||||
row.add_child(_rap_label)
|
||||
_rap_feedback = UiKit.label("",UiKit.FS_CAPTION, false, UiKit.STONE_DEEP) # 迭代 26 T3 换墨——旧灰石弱化墨对 PANEL_DATA 2.9:1 越带,深石墨 5.6:1 在案
|
||||
_rap_feedback = UiKit.label("",UiKit.FS_CAPTION, false, UiKit.STONE_DEEP) # 深石墨 5.6:1(弱化墨对 PANEL_DATA 2.9:1 越带)
|
||||
_rap_feedback.size_flags_vertical = Control.SIZE_SHRINK_CENTER
|
||||
row.add_child(_rap_feedback)
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
class_name BattleEndOverlay
|
||||
extends Control
|
||||
|
||||
signal continue_pressed # M9:review_pressed 退役(P-C7——冒险中回看归「生涯」,06 登记)
|
||||
signal continue_pressed # 冒险中无回看入口——回看归「生涯」(06 登记)
|
||||
|
||||
const COL_GOLD := UiKit.GOLD
|
||||
const COL_DUSK := UiKit.DUSK # 暮蓝(全作唯一暮蓝档)
|
||||
@@ -13,7 +13,7 @@ const COL_WARM := UiKit.WARM
|
||||
var _dim: ColorRect
|
||||
var _seal: Panel
|
||||
var _verdict: Label
|
||||
var _rows_box: VBoxContainer # M9 三行账(GP/钥石/本关加成——demo ⑧两列对齐 2026-10-06 审计差距④;失败侧整行隐藏=P-C4 维持 OQ14)
|
||||
var _rows_box: VBoxContainer # 三行账(GP/钥石/本关加成——demo ⑧两列对齐;失败侧整行隐藏)
|
||||
var _gp_row: HBoxContainer
|
||||
var _gp_name: Label
|
||||
var _gp_val: Label
|
||||
@@ -23,16 +23,16 @@ var _ks_val: Label
|
||||
var _bonus_row_l: HBoxContainer
|
||||
var _bonus_name: Label
|
||||
var _bonus_val: Label
|
||||
var _cele: TextureRect # M9 庆祝立绘(idle 占位浮动——正式帧待资产批)
|
||||
var _cele: TextureRect # 庆祝立绘(idle 占位浮动)
|
||||
var _btn: Button
|
||||
var _board_rect := Rect2()
|
||||
|
||||
|
||||
func _init() -> void:
|
||||
# 二轮修改批 B(2026-09-29 用户目检裁决):半透明浮层文字不可读 → 实底石板皮面板形态
|
||||
# 半透明浮层文字不可读 → 实底石板皮面板形态
|
||||
#(与营地/设置/图例同族)。半透明氛围 dim 保留(胜利暖纱/失败暮蓝——open_* 就地换色),信息与
|
||||
# 按钮全部上板;金蜡封 = 面板头部静态徽记(原全屏坠落演出退役——落点被面板遮挡)。
|
||||
# 骨架经 UiKit.modal_shell(T4 收口批);_dim 引用摘出供 open_* 换氛围色。
|
||||
# 按钮全部上板;金蜡封 = 面板头部静态徽记(坠落演出落点被面板遮挡)。
|
||||
# 骨架经 UiKit.modal_shell;_dim 引用摘出供 open_* 换氛围色。
|
||||
var shell := UiKit.modal_shell(self, Color(COL_WARM, 0.0), Vector2(520, 0), 12, true,
|
||||
UiKit.panel_skinned(Vector4(16, 14, 16, 14)))
|
||||
_dim = shell.dim
|
||||
@@ -63,7 +63,7 @@ func _init() -> void:
|
||||
_verdict.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER
|
||||
_verdict.size_flags_vertical = Control.SIZE_SHRINK_CENTER
|
||||
header.add_child(_verdict)
|
||||
# M9 庆祝立绘(demo ⑧——idle 帧浮动占位;正式帧归资产批;减动效=静帧)
|
||||
# 庆祝立绘(demo ⑧——idle 帧浮动占位;减动效=静帧)
|
||||
_cele = TextureRect.new()
|
||||
var cele_tex := UiKit.load_tex(UiKit.TEX_HERO_IDLE)
|
||||
if cele_tex != null:
|
||||
@@ -81,7 +81,7 @@ func _init() -> void:
|
||||
box.add_child(_rows_box)
|
||||
# 两列对齐账行(审计差距④对齐 demo ⑧——项目名左/数值右,定宽 360)
|
||||
_gp_row = _ledger_row()
|
||||
_gp_name = _ledger_name(UiKit.FS_SUB, UiKit.GOLD_INK, true) # 尾裁微批a 2026-10-08——GOLD_DEEP 对结算板 2.67:1 越带,换深金墨 5.21:1
|
||||
_gp_name = _ledger_name(UiKit.FS_SUB, UiKit.GOLD_INK, true) # GOLD_DEEP 对结算板 2.67:1 越带,换深金墨 5.21:1
|
||||
_gp_val = _ledger_value(UiKit.FS_SUB, UiKit.GOLD_INK, true) # 同上
|
||||
_gp_row.add_child(_gp_name)
|
||||
_gp_row.add_child(_gp_val)
|
||||
@@ -101,9 +101,9 @@ func _init() -> void:
|
||||
_bonus_row_l.visible = false
|
||||
_rows_box.add_child(_bonus_row_l)
|
||||
box.add_child(HSeparator.new())
|
||||
# M9:明细回看钮退役(P-C7——冒险中回看归「生涯」)
|
||||
# 无明细回看钮——冒险中回看归「生涯」
|
||||
_btn = UiKit.primary_button("继续")
|
||||
_btn.size_flags_horizontal = Control.SIZE_SHRINK_CENTER # 审计差距④——紧凑钮(原全宽)
|
||||
_btn.size_flags_horizontal = Control.SIZE_SHRINK_CENTER # 紧凑钮
|
||||
_btn.custom_minimum_size.x = 150
|
||||
_btn.pressed.connect(_emit_continue_pressed)
|
||||
box.add_child(_btn)
|
||||
@@ -111,11 +111,11 @@ func _init() -> void:
|
||||
|
||||
|
||||
# 终局画面数据锚 = 战斗结算 判定行(verdict)——判定行文案由组合根装配传入。
|
||||
# gp_summary(OQ14 口径——胜利总结结算展示 GP 总量+增量):组合根装配「战利品 +增量 · 合计总量」
|
||||
# gp_summary(胜利总结结算展示 GP 总量+增量):组合根装配「战利品 +增量 · 合计总量」
|
||||
# 传入(入账次序 = LevelRunner 入账先于组合根 handler,runner.gp 即战后总量);空串 = 不显示(失败侧恒空)。
|
||||
func open_victory(verdict_line: String, continue_label: String, board_rect: Rect2, gp_summary: String = "",
|
||||
ks_summary: String = "", bonus_summary: String = "", gp_gain := -1, gp_total := -1) -> void:
|
||||
_dim.color = Color(UiKit.DIM_NIGHT, 0.62) # 审计差距④——全屏压暗对齐 demo(原 0.28 暖纱透底)
|
||||
_dim.color = Color(UiKit.DIM_NIGHT, 0.62) # 全屏压暗对齐 demo
|
||||
_verdict.text = verdict_line
|
||||
_seal.visible = true # 胜利徽记(面板头部静态)
|
||||
_seal.modulate = Color.WHITE
|
||||
@@ -133,7 +133,7 @@ func open_victory(verdict_line: String, continue_label: String, board_rect: Rect
|
||||
if UiKit.victory_fx_style == "dust":
|
||||
_play_victory_dust() # 尘金粒子(P-C5 两套并存——开关=调试浮窗,会话级)
|
||||
else:
|
||||
_play_crest_assemble() # 碎砖拼纹章(§2.4——样张 v2 冻结批 T3 终稿;默认)
|
||||
_play_crest_assemble() # 碎砖拼纹章(§2.4;默认)
|
||||
_play_seal_recall(0) # 胜利不放封印收回
|
||||
if gp_gain >= 0:
|
||||
_roll_gp(gp_gain, gp_total)
|
||||
@@ -141,14 +141,14 @@ func open_victory(verdict_line: String, continue_label: String, board_rect: Rect
|
||||
|
||||
func open_defeat(verdict_line: String, continue_label: String, board_rect: Rect2) -> void:
|
||||
# 明度不变的暮蓝色相氛围(全屏唯一暮蓝档——§2.5 最静一拍)
|
||||
_dim.color = Color(COL_DUSK, 0.6) # 审计差距④同族压暗(原 0.45)
|
||||
_dim.color = Color(COL_DUSK, 0.6) # 同族压暗
|
||||
_verdict.text = verdict_line
|
||||
_board_rect = board_rect
|
||||
visible = true
|
||||
_seal.visible = true
|
||||
_seal.modulate = Color(0.4, 0.4, 0.4) # M9:失败徽记灰化(demo ⑧;旧全隐藏退役)
|
||||
_seal.modulate = Color(0.4, 0.4, 0.4) # 失败徽记灰化(demo ⑧)
|
||||
_cele.visible = false
|
||||
_rows_box.visible = false # P-C4 维持 OQ14:失败侧不呈现账行
|
||||
_rows_box.visible = false # 失败侧不呈现账行
|
||||
_play_dust_sadle() # 坐砖掸灰(§2.5——终稿)
|
||||
_play_seal_recall(8)
|
||||
|
||||
@@ -187,7 +187,7 @@ func _ledger_value(size: int, col: Color, bold: bool) -> Label:
|
||||
|
||||
|
||||
# 拆名/值(组合根传入整串):gp_shape=固定名「战利品 GP」值取余串;其余行按「:」或「 · 」切
|
||||
#(全角冒号不入值列——T7 实机「:攻击 +1」缩进 Corrections);无分隔符=整串入值列、名空
|
||||
#(全角冒号不入值列——实机「:攻击 +1」缩进 Corrections);无分隔符=整串入值列、名空
|
||||
func _set_ledger_row(name_l: Label, val_l: Label, s: String, gp_shape := false) -> void:
|
||||
if gp_shape and s.begins_with("战利品 GP"):
|
||||
name_l.text = "战利品 GP"
|
||||
@@ -230,7 +230,7 @@ func _play_seal_recall(count: int) -> void:
|
||||
tw.tween_callback(dot.queue_free)
|
||||
|
||||
|
||||
# ---- 演出密度终稿(样张 v2 冻结批 T3——程序化 tween,确定性散布,零新帧资产) ----
|
||||
# ---- 演出密度(程序化 tween,确定性散布,零新帧资产) ----
|
||||
|
||||
var _crest_bricks: Array[ColorRect] = [] # 纹章碎砖驻留面(close 时回收)
|
||||
var _fx_crest_shards := 0
|
||||
@@ -277,7 +277,7 @@ func _play_dust_sadle() -> void:
|
||||
mtw.tween_callback(mote.queue_free)
|
||||
|
||||
|
||||
# M9 庆祝立绘浮动(idle 占位——UiKit.motion 守减动效;motion_scale<1 静帧)
|
||||
# 庆祝立绘浮动(idle 占位——UiKit.motion 守减动效;motion_scale<1 静帧)
|
||||
func _play_celebrate() -> void:
|
||||
if UiKit.motion_scale < 1.0:
|
||||
return
|
||||
@@ -287,7 +287,7 @@ func _play_celebrate() -> void:
|
||||
tw.tween_property(_cele, "position:y", _cele.position.y, UiKit.motion(0.6)).set_trans(Tween.TRANS_SINE).set_ease(Tween.EASE_IN_OUT)
|
||||
|
||||
|
||||
# M9 尘金粒子(P-C5 备选演出——金屑自面板上带飘落闪灭;确定性散布守 S2)
|
||||
# 尘金粒子(备选演出——金屑自面板上带飘落闪灭;确定性散布守 S2)
|
||||
func _play_victory_dust() -> void:
|
||||
_fx_dust_motes += 10
|
||||
var c := _fx_center()
|
||||
@@ -305,7 +305,7 @@ func _play_victory_dust() -> void:
|
||||
tw.tween_callback(mote.queue_free)
|
||||
|
||||
|
||||
# M9 GP 行数字滚动(demo ⑧——0.8s 三次缓出;减动效直出终值)
|
||||
# GP 行数字滚动(demo ⑧——0.8s 三次缓出;减动效直出终值)
|
||||
func _roll_gp(gp_gain: int, gp_total: int) -> void:
|
||||
if UiKit.motion_scale < 1.0:
|
||||
_gp_val.text = "+%d · 合计 %d" % [gp_gain, gp_total]
|
||||
|
||||
@@ -27,7 +27,7 @@ func _init(db_ref) -> void:
|
||||
add_child(vb)
|
||||
var head := HBoxContainer.new()
|
||||
vb.add_child(head)
|
||||
# 对比度修复(v3.5 批 B3 2026-09-30——明细条灰字对深灰底实测 2.63:1):暗底双色墨制=暖白族
|
||||
# 明细条灰字对深灰底实测 2.63:1 → 暗底双色墨制=暖白族
|
||||
_summary = UiKit.label("",UiKit.FS_BODY, false, UiKit.WARM)
|
||||
_summary.custom_minimum_size = Vector2(0, BAND_H_DIG - 4)
|
||||
_summary.clip_text = true
|
||||
@@ -101,7 +101,7 @@ func parsed_text() -> String:
|
||||
return _log.get_parsed_text()
|
||||
|
||||
|
||||
# M9(P-C7 裁):带点击回看入口退役——冒险中不给数据回看(归「生涯」06 登记);带保留单行摘要显示
|
||||
# 无点击回看入口——冒险中不给数据回看(归「生涯」06 登记);带保留单行摘要显示
|
||||
|
||||
|
||||
# ---- 聚合计数(未生效类行防逐行惩罚轰炸——pattern 3 聚合纪律) ----
|
||||
@@ -153,7 +153,7 @@ func _format_row(row: Dictionary, agg_count := -1) -> String:
|
||||
var suffix := "(%s)" % src if src != "" else ""
|
||||
match String(row.get("type", "")):
|
||||
"effect":
|
||||
# GP 型效果行(GP+商店房实施批 2026-09-21):「GP 袋 +3」兑付反馈——value = 本行 ±power,
|
||||
# GP 型效果行:「GP 袋 +3」兑付反馈——value = 本行 ±power,
|
||||
# escrow = 战斗内缓冲现值(入钱包在战斗末一次上抛——呈现语义与增益池分账)
|
||||
if String(row.get("effect_id", "")) in ["gp_gain", "gp_loss"]:
|
||||
var gp_col := UiKit.HEX_LOG_GAIN if v > 0 else UiKit.HEX_LOG_LOSS
|
||||
@@ -165,9 +165,8 @@ func _format_row(row: Dictionary, agg_count := -1) -> String:
|
||||
var body := "%s 单次%+d(池 %+d)%s" % [name_, v, pool, suffix]
|
||||
return prefix + (_gold_frame(body) if hl != "" else "[color=%s]%s[/color]" % [col, body])
|
||||
"damage":
|
||||
# 伤害行无行上拆行文案——基线+伏击与三分量两旧归因拆行已随伏击退役批(2026-09-20)
|
||||
# 与充能退役批(2026-09-19)出协议,行上归因读数只剩掷点档位随行
|
||||
# 护甲等级掷点留档(2026-09-16 掷点批——掷点读数与档位随行可读;吃掷点分量恒带,棋盘来源行不带)
|
||||
# 伤害行无行上拆行文案——行上归因读数只剩掷点档位随行
|
||||
# 护甲等级掷点留档(掷点读数与档位随行可读;吃掷点分量恒带,棋盘来源行不带)
|
||||
var roll_txt := ""
|
||||
if row.get("roll", null) != null:
|
||||
roll_txt = "〔掷 %d vs 护甲 %d" % [int(row.roll), int(row.ac)]
|
||||
@@ -177,8 +176,7 @@ func _format_row(row: Dictionary, agg_count := -1) -> String:
|
||||
roll_txt += " · ×%d%%" % int(row.tier)
|
||||
roll_txt += "〕"
|
||||
var kill := " ⚔击杀!" if row.get("killed", false) else ""
|
||||
# 二轮修改批 C(2026-09-29 用户目检——「每次结算标出的伤害数字不明显」):
|
||||
# 伤害值放大加色独立于行文(朱红大一号——数字即信息主角,非同字号色温微差)
|
||||
# 伤害数字不明显——伤害值放大加色独立于行文(朱红大一号——数字即信息主角,非同字号色温微差)
|
||||
var body := "%s [color=#%s][size=17]-%d[/size][/color]%s%s%s%s" % [name_, UiKit.HEX_LOG_DMG_NUM, v, roll_txt, tail, kill, suffix]
|
||||
var body_col := UiKit.HEX_LOG_DMG_KILL if row.get("killed", false) else (UiKit.HEX_LOG_ROUND if v == 0 else UiKit.HEX_LOG_DMG_BODY)
|
||||
if bool(row.get("crit", false)):
|
||||
@@ -194,7 +192,7 @@ func _format_row(row: Dictionary, agg_count := -1) -> String:
|
||||
var body := "✗ %s%s" % [name_, "(本板完美排雷已破——归因此封印)" if hl == "perfect_broken" else ""]
|
||||
return prefix + (_gold_frame(body) if hl == "perfect_broken" else "[color=#%s]%s[/color]" % [UiKit.HEX_LOG_ROUND, body])
|
||||
"cast":
|
||||
# 施放行(敌人技能池批——蓄力行 charge 改型):增益技 0 伤害施放,具名技能
|
||||
# 施放行:增益技 0 伤害施放,具名技能
|
||||
return prefix + "[color=#%s]✦ %s%s[/color]" % [UiKit.HEX_LOG_CAST, name_, suffix]
|
||||
"decay":
|
||||
return prefix + "[color=#%s]状态衰减 ×%d[/color]" % [UiKit.HEX_LOG_DECAY, v]
|
||||
|
||||
@@ -26,9 +26,8 @@ var world: Node2D # StageWorld(preload 脚本实例——Node2D 世
|
||||
|
||||
func _init(db_ref) -> void:
|
||||
custom_minimum_size = Vector2(0, 252)
|
||||
# 面板底(圣经 v3.5 批 B4 2026-09-30——§6.1 舞台正面目标落码:B 区=地窟看台,世界底层暗背景
|
||||
# =方向稿 render_battle_screen 取值(值单点收 UiKit.STAGE_BG——T4 裸色收口)——替换原浅石板皮
|
||||
# 〔实测 L0.027 旧深底与上半米黄割裂的 opposite 端——v3.5 定暖暗中档〕;描边=PANEL_BORDER 同方向稿)
|
||||
# 面板底(圣经 v3.5 §6.1 舞台正面目标:B 区=地窟看台,世界底层暗背景
|
||||
# =方向稿 render_battle_screen 取值(值单点收 UiKit.STAGE_BG);描边=PANEL_BORDER 同方向稿)
|
||||
var bg := Panel.new()
|
||||
var sb := UiKit.panel_flat(UiKit.STAGE_BG, UiKit.PANEL_BORDER, 0, 1)
|
||||
bg.add_theme_stylebox_override("panel", sb)
|
||||
@@ -114,8 +113,8 @@ func _init(db_ref) -> void:
|
||||
# 主角站桩本体(世界)+ 状态行伴随件(壳层)
|
||||
hero_standee = _STANDEE_BODY.new("hero")
|
||||
var hero_tex := UiKit.load_tex(UiKit.TEX_HERO_PORTRAIT)
|
||||
hero_standee.set_portrait_texture(hero_tex, true) # B 区立绘 48px 档·normal 态朝右(迭代 6 T3 换装=new-normal 源;素材缺位 null 回退字形「人」)
|
||||
# 主角三态注册(迭代 6 T3——attack/hit 缺位不注册=该态保持现纹理,缺位回退链延伸)
|
||||
hero_standee.set_portrait_texture(hero_tex, true) # B 区立绘 48px 档·normal 态朝右(素材缺位 null 回退字形「人」)
|
||||
# 主角三态注册:attack/hit 缺位不注册=该态保持现纹理,缺位回退链延伸
|
||||
hero_standee.register_hero_state("idle", hero_tex)
|
||||
hero_standee.register_hero_state("attack", UiKit.load_tex(UiKit.TEX_HERO_ATTACK))
|
||||
hero_standee.register_hero_state("hit", UiKit.load_tex(UiKit.TEX_HERO_HIT))
|
||||
@@ -139,7 +138,7 @@ func _draw_light_beams(c: Control) -> void:
|
||||
|
||||
|
||||
func _make_slot_companion(parent: Control) -> StatusSlotRow:
|
||||
# 状态效果唯一展示面(2026-09-26 C 区精简批迁入站桩)——compact 档伴随件,tooltip 全明细
|
||||
# 状态效果唯一展示面——compact 档伴随件,tooltip 全明细
|
||||
var comp := StatusSlotRow.new()
|
||||
comp.compact = true
|
||||
comp.alignment = BoxContainer.ALIGNMENT_CENTER
|
||||
@@ -268,7 +267,7 @@ func set_enemies(cards_data: Array) -> void:
|
||||
world.add_child(body)
|
||||
body.slot_row = _make_slot_companion(enemy_box)
|
||||
body.set_data(d)
|
||||
# 敌种立绘接线(仅重建拍装载一次;flip=false——八敌像素源核证朝左/正面(迭代 6 T1),
|
||||
# 敌种立绘接线(仅重建拍装载一次;flip=false——八敌像素源核证朝左/正面,
|
||||
# 呈现即朝主角无误;反向朝向源换代时翻转义务归素材批)
|
||||
body.set_portrait_texture(_enemy_portrait_tex(String(d.get("enemy_id", ""))), false)
|
||||
_cards.append(body)
|
||||
@@ -293,14 +292,14 @@ func set_round_text(text: String) -> void:
|
||||
|
||||
|
||||
# 结算行演出分发(占位级):伤害行 → 目标站桩闪白 / 击杀 → 灰化塌陷残位;
|
||||
# 主角三态切换(迭代 6 T3)——主角攻击行→attack 态(短驻自回)/ 主角受击行→hit 态
|
||||
# 主角三态切换:主角攻击行→attack 态(短驻自回)/ 主角受击行→hit 态
|
||||
func on_step_row_fx(row: Dictionary) -> void:
|
||||
var t := String(row.get("type", ""))
|
||||
if t != "damage":
|
||||
return
|
||||
var target := String(row.get("target", ""))
|
||||
if target == "hero" or target == "":
|
||||
# 主角受击演出(迭代 6 T5 拆早退——旧「敌→主角零反馈」):弹道到达拍对齐,
|
||||
# 主角受击演出:弹道到达拍对齐,
|
||||
# 受击态+闪白+打击火花+击杀塌陷与敌方路径同密度(演出对等——计划书 §1.4)
|
||||
hero_standee.set_hero_state("hit") # 受击态(短驻自回 idle)
|
||||
_schedule_arrival_fx(hero_standee, bool(row.get("killed", false)))
|
||||
@@ -358,7 +357,7 @@ func gray_all_enemies() -> void:
|
||||
c.mark_dead()
|
||||
|
||||
|
||||
# 结算收尾:主角态显式回 idle(迭代 6 T3——「结算回 idle」事件;0.35s 短驻自回的兜底)
|
||||
# 结算收尾:主角态显式回 idle(0.35s 短驻自回的兜底)
|
||||
func settle_hero_idle() -> void:
|
||||
hero_standee.set_hero_state("idle")
|
||||
|
||||
|
||||
@@ -13,14 +13,14 @@ var edge := 46.0: # 格边长(BoardView.set_cell_area 单点
|
||||
_glow_layer.setup(edge, glow_group)
|
||||
queue_redraw()
|
||||
var bg_sb: StyleBoxFlat = null # 格底 StyleBox(BoardView 每次 paint 重建;呼吸微光就地改边框)
|
||||
# ── 接触带微光绘制面(2026-09-26 批:可挖域微光自"墙格整框"改"英雄格周界空间侧"——art-bible §3.2 v2.8;
|
||||
# ── 接触带微光绘制面(可挖域微光画在英雄格周界空间侧——art-bible §3.2 v2.8;
|
||||
# 仅英雄格单元承载,BoardView.refresh 按可挖域写边/角码、_process 逐拍更新呼吸色)──
|
||||
var contact_bands: Array[int] = [] # 正交接触边码 0=N/1=S/2=W/3=E(该侧整条边)
|
||||
var contact_corners: Array[int] = [] # 斜向共享角码 0=NW/1=NE/2=SW/3=SE(角点+两侧各半条边)
|
||||
var contact_col := Color(0, 0, 0, 0) # 当前呼吸色(alpha 0 = 不画)
|
||||
var plate := false # 数字色牌开关(BoardView paint 按已开格 glyph 写入——原则二)
|
||||
var outline_sides: Array[int] = [] # 墙格地面朝向描边码 0=N/1=S/2=W/3=E(轮廓加强批——程序侧结构件)
|
||||
# ── 高墙感 D 投影档(2026-09-28 挑档落码——研究件四变体用户裁 D=C+地面投影,ITB 同款高度线索)──
|
||||
var outline_sides: Array[int] = [] # 墙格地面朝向描边码 0=N/1=S/2=W/3=E(程序侧结构件)
|
||||
# ── 高墙感 D 投影档(研究件四变体定 D=C+地面投影,ITB 同款高度线索)──
|
||||
# 南立面 = 墙格南缘内侧「口沿高光线 + 双段暗带」(邻地面格才画,墙群内聚);地面投影 = 北邻墙格
|
||||
# 在本已开格北缘投渐隐暗带。参数按研究件 64px 拼版实测比例换算(立面双段 0.16/0.25 格边、投影带
|
||||
# 0.22 格边;坐标推导留档 commit)。素材缺位程序化回退同样服役(结构归程序——art-bible §8.5)。
|
||||
@@ -36,7 +36,7 @@ var glyph := "": # 文字位——数字/雷/封印/人/?(
|
||||
set(v):
|
||||
glyph = v
|
||||
queue_redraw()
|
||||
var glyph_color := UiKit.INK: # 文字色(墨族/雷朱/封印朱/小人蓝)
|
||||
var glyph_color := UiKit.INK: # 文字色(墨族/雷朱/封印黑/小人蓝)
|
||||
set(v):
|
||||
glyph_color = v
|
||||
queue_redraw()
|
||||
@@ -46,7 +46,7 @@ var badge_glyph := "": # 奖励徽章文字(右下角金——封
|
||||
if _glow_layer != null and is_instance_valid(_glow_layer):
|
||||
_glow_layer.set_badge_mask(v != "")
|
||||
queue_redraw()
|
||||
var dot_visible := false: # BFS 路径预览点迹(中心 6×6 小人蓝——旧 ColorRect 等价)
|
||||
var dot_visible := false: # BFS 路径预览点迹(中心 6×6 小人蓝)
|
||||
set(v):
|
||||
dot_visible = v
|
||||
queue_redraw()
|
||||
@@ -59,7 +59,7 @@ var hl_color := UiKit.GOLD: # 高亮色(金目标/封印朱/禁入灰
|
||||
hl_color = v
|
||||
queue_redraw()
|
||||
var font: Font = null # 字体槽位(null = ThemeDB.fallback_font——BoardView.set_world_font 注入)
|
||||
var bevel := 0.0: # 顶面倒角强度(0=关——立体原型批 2026-09-24;档位 0/0.35/0.6/1.0)
|
||||
var bevel := 0.0: # 顶面倒角强度(0=关;档位 0/0.35/0.6/1.0)
|
||||
set(v):
|
||||
bevel = v
|
||||
queue_redraw()
|
||||
@@ -72,17 +72,17 @@ const FACADE_FAR_COL := Color(0, 0, 0, 0.82) # 立面远段黑罩(墙
|
||||
const PROJECTION_NEAR_COL := Color(0, 0, 0, 0.28) # 投影贴墙段(地面 66→46 实测 α≈0.28)
|
||||
const PROJECTION_MID_COL := Color(0, 0, 0, 0.16) # 投影中段(渐隐)
|
||||
const PROJECTION_EDGE_COL := Color(0, 0, 0, 0.06) # 投影远段(渐隐收边)
|
||||
# ── E 格高墙面(圣经 v3.5 冻结① 2026-09-30——地面格南邻墙→南部 30% 承接墙面:墙顶纹理原亮度
|
||||
# 直贴零暗档零阴影〔用户终裁「它们是同样颜色的」〕;墙脚阴线只画地面/墙面交界;(b)-(c) 交界
|
||||
# ── E 格高墙面(圣经 v3.5 冻结①——地面格南邻墙→南部 30% 承接墙面:墙顶纹理原亮度
|
||||
# 直贴零暗档零阴影;墙脚阴线只画地面/墙面交界;(b)-(c) 交界
|
||||
# 无边框由 BoardView 侧跳过墙格北向描边承载)──
|
||||
const E_FACADE_FRAC := 0.30 # 墙面承接高(格边比——用户指令 30%)
|
||||
# ── 素材槽位(本批全 null = 程序化占位服役;art-bible §8.2 格面 32×32 暂定档)──
|
||||
const E_FACADE_FRAC := 0.30 # 墙面承接高(格边比 30%)
|
||||
# ── 素材槽位(现全 null = 程序化占位服役;art-bible §8.2 格面 32×32 暂定档)──
|
||||
var tex_base: Texture2D = null # ① 砖面底
|
||||
var tex_mutation: Texture2D = null # ② 变异纹带
|
||||
var tex_glyph: Texture2D = null # ③ 数字
|
||||
var tex_badge: Texture2D = null # ③' 徽章
|
||||
var tex_seal: Texture2D = null: # ④ 封印(T17 修复:槽位可运行时换装〔封印〕——同族 facade/treasure_mark
|
||||
set(v): # 均 setter 触发重绘;旧普通 var 赋值不重绘=封印插上不显示)
|
||||
var tex_seal: Texture2D = null: # ④ 封印(槽位可运行时换装〔封印〕——同族 facade/treasure_mark
|
||||
set(v): # 均 setter 触发重绘(普通 var 赋值不重绘=封印插上不显示))
|
||||
if tex_seal != v:
|
||||
tex_seal = v
|
||||
queue_redraw()
|
||||
@@ -92,9 +92,9 @@ var facade_strip := false: # E 格开关(BoardView paint 按「已开
|
||||
facade_strip = v
|
||||
queue_redraw()
|
||||
|
||||
# 变异角标(迭代 6 T6——程序化,tex_mutation 纹带槽留给变异纹带六型自产批):
|
||||
# 变异角标(程序化,tex_mutation 纹带槽暂留):
|
||||
# "flat_damage" = 未开格伤害型变异,左下角危险红三角(开格有代价的策略纹理标记)
|
||||
# 叠加层标记(迭代 7 T4——秘宝板级叠加层的格面呈现;派生信息层零回写):
|
||||
# 叠加层标记(秘宝板级叠加层的格面呈现;派生信息层零回写):
|
||||
# "mine_reveal" = 真视格(危险红内缩框——「这格是雷」的确定决策标记,封印照常计费);
|
||||
# "reward_preview" 格不复用本字段——预读徽章走 badge_glyph 注入(board_view refresh 注入 REWARD_BADGE 字形)
|
||||
var treasure_mark := "":
|
||||
@@ -108,20 +108,20 @@ var mutation_mark := "":
|
||||
mutation_mark = v
|
||||
queue_redraw()
|
||||
|
||||
# ── 视野迷雾呈现态(迭代 11 第④批 T2/T3——纯呈现零逻辑;规格=art-bible §6.5 v3.5,正主=HUD框架「视野迷雾」节)──
|
||||
# ── 视野迷雾呈现态(纯呈现零逻辑;规格=art-bible §6.5 v3.5,正主=HUD框架「视野迷雾」节)──
|
||||
# 0=正常(灯光区内)/ 1=全黑(区外未开格——正主「区外未开格=全黑不渲染」不透明档)/ 2=记忆态(区外已开格灰显可读)
|
||||
var fog_state := 0:
|
||||
set(v):
|
||||
if fog_state != v:
|
||||
fog_state = v
|
||||
queue_redraw()
|
||||
var removed_dim := false: # 中立移除退色层(T8/D6——BoardView paint 按排雷玩法 is_removed 写入)
|
||||
var removed_dim := false: # 中立移除退色层(BoardView paint 按排雷玩法 is_removed 写入)
|
||||
set(v):
|
||||
removed_dim = v
|
||||
queue_redraw()
|
||||
# ── 未开格类型微光(迭代 30 卡①·Q1 丙案——英雄 8 向相邻未开格的组粒度类型提示)
|
||||
# ── 未开格类型微光(英雄 8 向相邻未开格的组粒度类型提示)
|
||||
# 作用集 = 可挖域 ∩ 身份可查格,由 BoardView 侧派生下发;本件只承接绘制与子件生命周期。
|
||||
# 静态(Q5 甲案):不呼吸、不入动效预算;相位门控(结算/道具窗口态隐藏)在 BoardView 侧。
|
||||
# 静态:不呼吸、不入动效预算;相位门控(结算/道具窗口态隐藏)在 BoardView 侧。
|
||||
var glow_group := 0:
|
||||
set(v):
|
||||
if glow_group != v:
|
||||
@@ -134,11 +134,11 @@ var _glow_layer: Node2D = null
|
||||
|
||||
func _draw() -> void:
|
||||
var rect := Rect2(Vector2.ZERO, Vector2(edge, edge))
|
||||
# ① 砖面底(占位 = StyleBoxFlat:格底色 + 暗化描边 + 3px 圆角——旧 Button normal 样式等价;
|
||||
# ① 砖面底(占位 = StyleBoxFlat:格底色 + 暗化描边 + 3px 圆角;
|
||||
# 呼吸微光/主角蓝框由 BoardView 改 bg_sb 后触发重绘)
|
||||
if tex_base != null:
|
||||
draw_texture_rect(tex_base, rect, false)
|
||||
# 轮廓加强(2026-09-26 用户反馈「轮廓线不明显」):墙格地面朝向边补 2px 深墨线(§8.5 结构归程序——
|
||||
# 轮廓加强(轮廓线不明显):墙格地面朝向边补 2px 深墨线(§8.5 结构归程序——
|
||||
# 素材 1px 墨线在 45/36px 非整数缩放下视觉衰减,程序线保底;墙-墙内界不画,保墙群内聚;
|
||||
# 坐标推导:各边内贴 2px 矩形,相邻边矩形全长相交=周界闭合)
|
||||
if not outline_sides.is_empty():
|
||||
@@ -155,7 +155,7 @@ func _draw() -> void:
|
||||
_draw_south_facade()
|
||||
_draw_north_projection()
|
||||
_draw_e_facade()
|
||||
# 瓦片态描边覆盖层(高墙瓦片接线批 2026-09-26):tex_base 在位时 BoardView 传入的 bg_sb
|
||||
# 瓦片态描边覆盖层:tex_base 在位时 BoardView 传入的 bg_sb
|
||||
# 已降级为「透明底 + 描边」载体——呼吸微光/主角蓝框画在瓦片之上(art-bible §1.2 叠层序
|
||||
# 不变:底 < 纹带 < 数字 < 封印 < 焦点高亮);素材缺位回退走 elif 程序化原样,零观感变化
|
||||
if bg_sb != null:
|
||||
@@ -204,7 +204,7 @@ func _draw() -> void:
|
||||
draw_style_box(bg_sb, rect)
|
||||
_draw_south_facade() # 平色回退路径——D 投影档两件同样服役(结构归程序)
|
||||
_draw_north_projection()
|
||||
# 顶面倒角(立体原型批:光源左上——上/左内沿受光 2px、下/右内沿背光 2px;内缩 2px 让
|
||||
# 顶面倒角(光源左上——上/左内沿受光 2px、下/右内沿背光 2px;内缩 2px 让
|
||||
# 呼吸微光/主角蓝边框保持最外沿可见;强度随档位)
|
||||
if bevel > 0.0:
|
||||
var base: Color = bg_sb.bg_color
|
||||
@@ -215,12 +215,12 @@ func _draw() -> void:
|
||||
draw_rect(Rect2(t, t, t, edge - t * 2.0), lit, true)
|
||||
draw_rect(Rect2(t, edge - t * 2.0, edge - t * 2.0, t), dark, true)
|
||||
draw_rect(Rect2(edge - t * 2.0, t, t, edge - t * 2.0), dark, true)
|
||||
# ② 变异纹带(占位批次无程序化纹带——槽位先行,终稿换入即用)
|
||||
# ② 变异纹带(无程序化纹带——槽位先行,贴图落位即用)
|
||||
if tex_mutation != null:
|
||||
draw_texture_rect(tex_mutation, rect, false)
|
||||
# ②' 变异角标(迭代 6 T6):左下角危险红三角(危险红=「开此格有代价」语义位,与雷族同源;
|
||||
# ②' 变异角标:左下角危险红三角(危险红=「开此格有代价」语义位,与雷族同源;
|
||||
# 尺寸随格边比例——灰色可辨辅助=红对砖面明度差,禁喧宾)
|
||||
# ②" 秘宝叠加层(迭代 7 T4):真视格=危险红内缩框(策略纹理——确定决策输入不喧宾)
|
||||
# ②" 秘宝叠加层:真视格=危险红内缩框(策略纹理——确定决策输入不喧宾)
|
||||
if treasure_mark == "mine_reveal":
|
||||
var inset := edge * 0.14
|
||||
draw_rect(Rect2(inset, inset, edge - inset * 2.0, edge - inset * 2.0),
|
||||
@@ -231,9 +231,9 @@ func _draw() -> void:
|
||||
draw_colored_polygon(mpts, Color(UiKit.RED_MINE, 0.85))
|
||||
# ③ 数字/徽章(数字 ≥ 格高 50% 墨族对格底 ≥4.5:1;徽章右下角金,遮蔽态 = "?")
|
||||
if glyph != "":
|
||||
# 数字色牌后备路径(圣经 v3.5 冻结④ 批 B2 2026-09-30:数字亮墨直画已开格底**退役必垫牌**;
|
||||
# 牌=后备件——雷/「?」维持牌载保读性(牌上深墨族 12:1),复杂格底〔物件纹样/装饰变体格〕
|
||||
# 判定随装饰瓦片自产批扩。牌尺寸 0.60/0.62 格边居中 = 样张 v2 冻结值;封印=未开格不垫牌)
|
||||
# 数字色牌后备路径(圣经 v3.5 冻结④:数字亮墨直画已开格底须垫牌;
|
||||
# 牌=后备件——雷/「?」维持牌载保读性(牌上深墨族 12:1);
|
||||
# 牌尺寸 0.60/0.62 格边居中;封印=未开格不垫牌)
|
||||
if plate:
|
||||
var pw := edge * 0.60
|
||||
var ph := edge * 0.62
|
||||
@@ -249,7 +249,7 @@ func _draw() -> void:
|
||||
draw_texture_rect(tex_badge, rect, false)
|
||||
else:
|
||||
_draw_badge(badge_glyph)
|
||||
# ④ 封印(现行视觉封印文字在 ③ 文字位承载——独立槽位留给终稿封印图)
|
||||
# ④ 封印(现行视觉封印文字在 ③ 文字位承载——独立槽位留给封印图)
|
||||
if tex_seal != null:
|
||||
draw_texture_rect(tex_seal, rect, false)
|
||||
# ⑤ 路径点迹 + 焦点高亮(悬停态覆盖层,不参与命中)
|
||||
@@ -257,15 +257,15 @@ func _draw() -> void:
|
||||
var ds := 6.0
|
||||
draw_rect(Rect2(Vector2((edge - ds) * 0.5, (edge - ds) * 0.5), Vector2(ds, ds)), UiKit.HERO_BLUE, true)
|
||||
if hl_visible:
|
||||
# 悬停高亮减淡(2026-09-26 用户反馈:金框太明显——绘制时 55% alpha,hl_color 属性与探针断言面不动)
|
||||
draw_rect(Rect2(1, 1, edge - 2.0, edge - 2.0), Color(hl_color, 0.55), false, 2.0) # 2px 内描边(旧 hl Panel 等价)
|
||||
# ⑥ 视野迷雾层(迭代 11 第④批 T2/T3——最顶遮罩,§6.5 落文):
|
||||
# 悬停高亮减淡(金框太明显——绘制时 55% alpha,hl_color 属性与探针断言面不动)
|
||||
draw_rect(Rect2(1, 1, edge - 2.0, edge - 2.0), Color(hl_color, 0.55), false, 2.0) # 2px 内描边
|
||||
# ⑥ 视野迷雾层(最顶遮罩,§6.5 落文):
|
||||
# 全黑=不透明纯黑盖全部叠层(含封印印/徽章/高亮——封印印记随格隐没,§6.5 显式推论);
|
||||
# 记忆态=记忆板素色压平(纹理免疫——色牌/封印印/徽章随板隐没)+数字记忆墨重画(数字可读硬指标 ≥4.5:1)
|
||||
if fog_state == 1:
|
||||
draw_rect(Rect2(Vector2.ZERO, Vector2(edge, edge)), Color(0, 0, 0, 1.0), true)
|
||||
elif fog_state == 2:
|
||||
# 记忆态换装(P10 乙 2026-10-06——demo 口径「地板纹理 + 灰罩 α.66」:原 #3A3A3A 平涂退役;
|
||||
# 记忆态换装(demo 口径「地板纹理 + 灰罩 α.66」:
|
||||
# 地板纹理原样直画 + FOG_MEMORY_PLATE α.66 灰罩压顶;素材零改动、格边长零改动。
|
||||
# 记忆墨 FOG_MEMORY_INK 对合成底实测 ≈6.7:1 ≥4.5:1(§7.2 硬指标;floor avg #453020 复算在册))
|
||||
if tex_base != null:
|
||||
@@ -273,7 +273,7 @@ func _draw() -> void:
|
||||
draw_rect(Rect2(Vector2.ZERO, Vector2(edge, edge)), Color(UiKit.FOG_MEMORY_PLATE, 0.66), true)
|
||||
if glyph != "":
|
||||
_draw_glyph(glyph, UiKit.FOG_MEMORY_INK, maxi(18, int(ceil(edge * 0.5)))) # 字号下限 18(§7.4 冻结公式同源)
|
||||
# ⑦ 中立移除退色层(迭代 13 T8/D6——P2 甲退色降档:暗幕压顶全层覆盖,暗淡记忆态同族呈现语义;
|
||||
# ⑦ 中立移除退色层(退色降档:暗幕压顶全层覆盖,暗淡记忆态同族呈现语义;
|
||||
# 顶层于迷雾之上——移除格不再参与迷雾语义。本实现移除集=未揭示危险格(无数字无封印印))
|
||||
if removed_dim:
|
||||
draw_rect(Rect2(Vector2.ZERO, Vector2(edge, edge)), Color(0.12, 0.10, 0.09, 0.62), true)
|
||||
@@ -290,7 +290,7 @@ func _draw_south_facade() -> void:
|
||||
var y_far := edge - far_h
|
||||
var y_near := y_far - near_h
|
||||
var y_hl := y_near - FACADE_HIGHLIGHT_PX
|
||||
draw_rect(Rect2(0.0, y_hl, edge, FACADE_HIGHLIGHT_PX), UiKit.STAND_STONE_RIM, true) # 口沿高光(UiKit.STAND_STONE_RIM 并档——同值单点,清扫批 2026-10-02)
|
||||
draw_rect(Rect2(0.0, y_hl, edge, FACADE_HIGHLIGHT_PX), UiKit.STAND_STONE_RIM, true) # 口沿高光(UiKit.STAND_STONE_RIM 并档——同值单点)
|
||||
draw_rect(Rect2(0.0, y_near, edge, near_h), FACADE_NEAR_COL, true)
|
||||
draw_rect(Rect2(0.0, y_far, edge, far_h), FACADE_FAR_COL, true)
|
||||
|
||||
@@ -306,10 +306,10 @@ func _draw_north_projection() -> void:
|
||||
draw_rect(Rect2(0.0, seg * 2.0, edge, seg), PROJECTION_EDGE_COL, true)
|
||||
|
||||
|
||||
# E 格高墙面(圣经 v3.5 冻结① 2026-09-30——方向稿 draw_facade 移植):本格已开且南邻为墙 →
|
||||
# E 格高墙面(圣经 v3.5 冻结①——方向稿 draw_facade 移植):本格已开且南邻为墙 →
|
||||
# 南部 30% 承接墙面。材质 = 墙顶 center 瓦片顶部 30% 区带**原亮度**直贴(零暗档零阴影);
|
||||
# 墙脚阴线只画地面/墙面交界(strip 顶缘 1px);墙面底缘(格界处)不画任何线——直接接南侧墙顶
|
||||
# (v3.4 用户裁定:白边导致两截)。仅瓦片态服役(tex_facade 缺位 = 平色回退原语义不动)。
|
||||
# (白边导致两截)。仅瓦片态服役(tex_facade 缺位 = 平色回退原语义不动)。
|
||||
func _draw_e_facade() -> void:
|
||||
if not facade_strip or tex_facade == null:
|
||||
return
|
||||
@@ -321,7 +321,7 @@ func _draw_e_facade() -> void:
|
||||
draw_rect(Rect2(0.0, y0, edge, 1.0), UiKit.E_FACADE_FOOT_COL, true)
|
||||
|
||||
|
||||
# 文字位绘制:水平居中 + 垂直居中(基线 = (格高 − 字高)/2 + 上伸部——旧 Button 居中对齐等价)
|
||||
# 文字位绘制:水平居中 + 垂直居中(基线 = (格高 − 字高)/2 + 上伸部)
|
||||
func _draw_glyph(g: String, col: Color, fs: int) -> void:
|
||||
var f := font if font != null else ThemeDB.fallback_font
|
||||
if f == null:
|
||||
@@ -332,7 +332,7 @@ func _draw_glyph(g: String, col: Color, fs: int) -> void:
|
||||
HORIZONTAL_ALIGNMENT_CENTER, edge, fs, col)
|
||||
|
||||
|
||||
# 微光子件生命周期(迭代 30 卡①):有组才建、组变即换、无组即撤——作用集 ≤8 格常驻,
|
||||
# 微光子件生命周期:有组才建、组变即换、无组即撤——作用集 ≤8 格常驻,
|
||||
# 其余格零节点零绘制成本。与父件 _draw() 分离的三条理由见本件头注(命令序列/重绘预算/材质挂点)。
|
||||
func _sync_glow_layer() -> void:
|
||||
if glow_group == 0:
|
||||
@@ -347,7 +347,7 @@ func _sync_glow_layer() -> void:
|
||||
_glow_layer.set_badge_mask(badge_glyph != "")
|
||||
|
||||
|
||||
# 徽章绘制:右下角金(旧 Label BOTTOM_RIGHT 锚点等价——文字贴角内缩 2–3px)
|
||||
# 徽章绘制:右下角金(文字贴角内缩 2–3px)
|
||||
func _draw_badge(g: String) -> void:
|
||||
var f := font if font != null else ThemeDB.fallback_font
|
||||
if f == null:
|
||||
|
||||
@@ -6,7 +6,7 @@ extends Control
|
||||
|
||||
const COL_GOLD := UiKit.GOLD
|
||||
const COL_BOSS := UiKit.BOSS_PURPLE # 紫赤 #8F2F6A
|
||||
const COL_SEAL := UiKit.SEAL_BLACK # 封印黑(封印化暴击弹道——封印朱→封印紫→封印黑 2026-09-28)
|
||||
const COL_SEAL := UiKit.SEAL_BLACK # 封印黑(封印化暴击弹道)
|
||||
const CONFETTI_COLS := [UiKit.GOLD, UiKit.GOLD_LIT, UiKit.HERO_BLUE, UiKit.WARM] # 装饰彩带不涉封印语义——原紫位改亮金(紫赤系 Boss 独占不入装饰)
|
||||
|
||||
var _banner: Label
|
||||
@@ -117,7 +117,7 @@ func play_seal_hit_projectile(heavy: bool) -> void:
|
||||
tw.tween_callback(shot.queue_free)
|
||||
|
||||
|
||||
# ---- 挖掘挥铲三帧(棋盘小人贴图批 2026-09-25——计划书 §3.C 解耦方案:铲归 fx 层、角色弹跳归
|
||||
# ---- 挖掘挥铲三帧(计划书 §3.C 解耦方案:铲归 fx 层、角色弹跳归
|
||||
# BoardView.play_hero_dig_bounce,两演出零共享属性) ----
|
||||
|
||||
const SHOVEL_FRAME_PX := 32.0 # 铲帧画布边长(缩放比与英雄帧同源)
|
||||
@@ -128,7 +128,7 @@ var _shovel_texs: Array[Texture2D] = [] # 惰性装载缓存(素材缺
|
||||
|
||||
|
||||
# 挖掘挥铲:hand = 英雄持握手位(BoardView.hero_hand_point 出口,fx 与棋盘同层同系)——
|
||||
# 以手为圆心、铲长为半径旋转(2026-09-25 用户裁决;三帧系素材侧预烤的旋转相位,握把锚 (18,12)
|
||||
# 以手为圆心、铲长为半径旋转(三帧系素材侧预烤的旋转相位,握把锚 (18,12)
|
||||
# 三帧同位,锚点钉死手位即成弧);dir = 挖掘方向单位格向量(素材铲头朝左——dir.x>0 翻转);
|
||||
# sprite_scale 与英雄精灵同档(整数 1×——原「共享边中点+12% 偏置」定位与 edge/32 缩放作废)。
|
||||
func play_shovel_swing(hand: Vector2, dir: Vector2i, sprite_scale: float) -> void:
|
||||
|
||||
@@ -14,59 +14,57 @@ const _BOARD_WORLD := preload("res://presentation/views/board_world.gd")
|
||||
const COL_BRICK := UiKit.BRICK # 未开——掩尘完整地砖(色板原语 #C9AF83)
|
||||
const COL_FLOOR := UiKit.FLOOR # 已开——裸土 #B39A78(碎裂裸露地面)
|
||||
const COL_DIGIT := UiKit.INK # 数字——墨族 #33261B(对格底 ≥4.5:1 硬指标)
|
||||
const COL_HERO := UiKit.HERO_BLUE # 小人边框——小人蓝 #2E6E8E
|
||||
const COL_SEAL := UiKit.SEAL_BLACK # 封印黑 #000000(封印朱→封印紫→封印黑,2026-09-28 用户拍板)
|
||||
const COL_HERO := UiKit.HERO_BLUE # 小人边框——小人蓝
|
||||
const COL_SEAL := UiKit.SEAL_BLACK # 封印黑 #000000
|
||||
const COL_GOLD := UiKit.GOLD # 奖励金 #F0A828
|
||||
const COL_CONTACT := UiKit.CONTACT_GLOW # 接触带微光基色(蓝族浅一档·近白蓝——§3.2 v2.8;旧绿相退役 2026-09-26)
|
||||
const COL_CONTACT := UiKit.CONTACT_GLOW # 接触带微光基色(蓝族浅一档·近白蓝——§3.2 v2.8)
|
||||
const COL_HL_TARGET := UiKit.GOLD # 悬停目标格边框(金)
|
||||
const REWARD_BADGE := { # 已开格格面资源身份 icon(cell_type 九型键;极性视觉归批 2)
|
||||
"attack": "攻", "defense": "守", "heal": "医", "blast": "爆", "weaken": "弱", "pierce": "穿", "magic": "魔", # 穿/魔 = 伤害道具批 2026-09-18
|
||||
"action": "行", # 行 = 行动点型(八型批 2026-09-20——ap_up/ap_drain 两行;徽章键漏网补齐,ui_click_probe 91 断言修复)
|
||||
"gp": "金", # 金 = GP 型(GP+商店房实施批 2026-09-21——gp_gain/gp_loss 两行;金色系 ~40° 相位,禁入带紫赤 300–345° 外,取色 UiKit.GOLD 徽标色单点)
|
||||
const REWARD_BADGE := { # 已开格格面资源身份 icon(cell_type 九型键)
|
||||
"attack": "攻", "defense": "守", "heal": "医", "blast": "爆", "weaken": "弱", "pierce": "穿", "magic": "魔",
|
||||
"action": "行", # 行 = 行动点型(ap_up/ap_drain 两行)
|
||||
"gp": "金", # 金 = GP 型(gp_gain/gp_loss 两行;金色系 ~40° 相位,禁入带紫赤 300–345° 外,取色 UiKit.GOLD 徽标色单点)
|
||||
}
|
||||
const BADGE_PLACEHOLDER := "?" # 未知图标键占位纹样(HUD框架:禁静默空图标)
|
||||
const CURSOR_DIG := Input.CURSOR_CROSS # 挖——镐形自绘纹理载体
|
||||
const CURSOR_SEAL := Input.CURSOR_POINTING_HAND # 封印——封印形自绘纹理载体
|
||||
const CURSOR_MOVE_SEM := Input.CURSOR_ARROW # 移动——箭头(命名避开 Control.CURSOR_MOVE 原生成员遮蔽)
|
||||
const CHORD_WINDOW_MS := 350
|
||||
const SEP := 2 # 格间距(旧 GridContainer v/h_separation 同值——格尺寸公式与命中换算共用)
|
||||
const SEP := 2 # 格间距(格尺寸公式与命中换算共用)
|
||||
|
||||
# 棋盘立体参数(2026-09-24 用户实机三档对比拍板「中档」定稿——原型 F9/按钮入口已退役;
|
||||
# §1.4 注记待 art-bible 并行批落籍后补写)
|
||||
# 棋盘立体参数
|
||||
const DEPTH_PX := 8.0 # 悬崖厚度(像素)
|
||||
const DEPTH_BEVEL := 0.6 # 顶面倒角强度
|
||||
# 高墙感 D 投影档(2026-09-28 挑档落码):地面投影带宽 = 格边比(研究件 64px 拼版实测 14/64≈0.22;
|
||||
# 高墙感 D 投影档:地面投影带宽 = 格边比(研究件 64px 拼版实测 14/64≈0.22;
|
||||
# 立面/投影色参数在 CellUnit 常量——绘制侧单点)
|
||||
const WALL_PROJECTION_FRAC := 0.22
|
||||
|
||||
# 英雄单位演出参数(棋盘小人贴图批 2026-09-25——计划书 §3.B/C 时长档)
|
||||
# 英雄单位演出参数(计划书 §3.B/C 时长档)
|
||||
const HERO_FRAME_PX := 32.0 # 英雄帧画布边长(素材 32×32 锁定——缩放比分母)
|
||||
const HERO_SPRITE_SCALE := 1.5 # 1.5× 档(2026-09-26 用户二裁:实机看 1× 偏小放大 1.5 倍——明示
|
||||
# 裁决压圣经 §8.2「禁非整数缩放」一般规则,§8.2 豁免注记归 art-bible
|
||||
# 批另落;头可越格上沿不裁剪)
|
||||
const HERO_GROUND_FRAC := 0.25 # 脚锚纵向落点 = 格底往上 25%(用户口径「脚踩格子 1/4 处·从下往上数」;
|
||||
# 头越上格沿造立体感。⚠ 参照系纪律:本值一律自底边起算——上一批用
|
||||
# 「自顶 75%」表述同线异名,措辞口径误导用户被纠,本批统一为自底边)
|
||||
const HERO_SPRITE_SCALE := 1.5 # 1.5× 档(实机 1× 偏小故放大 1.5 倍——本档为圣经 §8.2「禁非整数缩放」
|
||||
# 一般规则的显式豁免;头可越格上沿不裁剪)
|
||||
const HERO_GROUND_FRAC := 0.25 # 脚锚纵向落点 = 格底往上 25%(口径「脚踩格子 1/4 处·从下往上数」;
|
||||
# 头越上格沿造立体感。⚠ 参照系纪律:本值一律自底边起算,禁改用「自顶 75%」
|
||||
# 一类的同线异名表述)
|
||||
const HERO_MOVE_S := 0.14 # 位移时长(~140ms)
|
||||
const HERO_STEP_S := 0.18 # 走路帧周期 = 原地撞墙帧闪时长(~180ms)
|
||||
const HERO_DIG_S := 0.15 # 挖掘弹跳总时长(~150ms,半程压半程回)
|
||||
const HERO_BOUNCE := 0.82 # 弹跳压缩比(scale.y 1→0.82→1 脚底收敛)
|
||||
const HERO_SHADOW_PX := Vector2(32, 12) # 脚下椭圆影屏幕尺寸(Godot 组件合成——径向渐变纹理压扁,
|
||||
# 贴图零改动,2026-09-26 用户裁决走引擎侧)
|
||||
# 贴图零改动,走引擎侧)
|
||||
|
||||
# ── 高墙/地面瓦片纹理路径单点(棋盘高墙瓦片接线批 2026-09-26)──
|
||||
# 素材 PNG 不入 git(用户裁定)——一律 UiKit.load_tex(exists 守卫)装载,禁 preload;
|
||||
# ── 高墙/地面瓦片纹理路径单点 ──
|
||||
# 素材 PNG 不入 git——一律 UiKit.load_tex(exists 守卫)装载,禁 preload;
|
||||
# 缺位返回 null,整批任一缺位 = 弃用瓦片走程序化平色回退(BRICK/FLOOR,fresh checkout 冒烟不炸)。
|
||||
# 路径常量落本文件单点(ui_kit.gd 由并行批占用,需求卡片 §2.5 禁入)。词表 =
|
||||
# 路径常量落本文件单点。词表 =
|
||||
# design/research/board-tile-vocab-plan-a-2026-09-26.md(16 唯一墙瓦片 + 5 地面变体;
|
||||
# 东/东北/东南/end_e/in_ne/in_se 六镜像件运行时 flip_x 合成,不出文件——词表 §二)。
|
||||
# 换色批(T6 2026-09-28):路径按 board_type 后缀参数化——terrain_<件名>_<board_type>.png;
|
||||
# 路径按 board_type 后缀参数化——terrain_<件名>_<board_type>.png;
|
||||
# 词表 §一 16 唯一墙瓦片名单单点在本常量数组。underdark/deep_underdark 两套 =
|
||||
# tools/recolor_terrain_tiles.py 程序换色产(台账 164 换色遗留清偿);任一套缺位 → 整批回落
|
||||
# tools/recolor_terrain_tiles.py 程序换色产;任一套缺位 → 整批回落
|
||||
# dungeon 套(禁同板混渲染);dungeon 套亦缺位 → 程序化平色回退(fresh checkout 冒烟不炸)。
|
||||
const TILE_WALL_NAMES := ["wall_center", "wall_center_crack", "wall_center_speckle", "wall_n", "wall_s", "wall_w", "wall_nw", "wall_sw", "wall_hrun", "wall_vrun", "wall_end_n", "wall_end_s", "wall_end_w", "wall_lone", "wall_in_nw", "wall_in_sw"]
|
||||
const TILE_WALL_FMT := "res://assets/art/board/terrain_%s_%s.png" # %s=件名 %s=board_type
|
||||
const TILE_SEAL_MARK := "res://assets/art/board/seal_mark.png" # 封印印记纹理(迭代 9 卡⑬——独立可选件)
|
||||
const TILE_SEAL_MARK := "res://assets/art/board/seal_mark.png" # 封印印记纹理(独立可选件)
|
||||
const TILE_FLOOR_FMT := "res://assets/art/board/terrain_floor_%s_%d.png" # %s=board_type 1..%d 变体
|
||||
const TILE_FLOOR_VARIANTS := 5 # 地面瓦片变体数(词表 §一 floor_1..5)
|
||||
|
||||
@@ -79,8 +77,8 @@ var _board: BoardData = null
|
||||
var _minesweeper = null # 排雷玩法 只读查询引用(refresh 更新;null = 无战斗)
|
||||
var _identity_cb := Callable() # 地图战斗资源 身份解析回调(组合根注入只读面)——Vector2i → Dictionary/null
|
||||
var _mask_cb := Callable() # 地形变异 数字遮蔽回调(组合根注入只读面)——Vector2i → bool(true = 该格数字不显示)
|
||||
var _vmark_cb := Callable() # 地形变异 角标回调(迭代 6 T6 只读面)——Vector2i → String("flat_damage"=伤害型变异未开格;""=无)
|
||||
var _treasure_reveal := {} # 秘宝 真视集(迭代 7 T4——Vector2i → true;组合根 set_treasure_overlays 注入投影)
|
||||
var _vmark_cb := Callable() # 地形变异 角标回调(只读面)——Vector2i → String("flat_damage"=伤害型变异未开格;""=无)
|
||||
var _treasure_reveal := {} # 秘宝 真视集(Vector2i → true;组合根 set_treasure_overlays 注入投影)
|
||||
var _treasure_preview := {} # 秘宝 预读集(同上——未开格徽章呈现挂点)
|
||||
var _diggable := {} # 可挖域(8 向相邻未开格)——呼吸微光作用集
|
||||
var _glow: Dictionary[Vector2i, int] = {} # 未开格类型微光作用集(Vector2i → 组号;=可挖域∩身份可查)
|
||||
@@ -98,7 +96,7 @@ var _desat_mat: ShaderMaterial
|
||||
var _desat_tween: Tween
|
||||
var _cell := 46.0 # 当前格边长(set_cell_area 按容器尺寸重算——battle.md 格尺寸公式)
|
||||
|
||||
# ── 英雄单位精灵状态(棋盘小人贴图批 2026-09-25;素材 PNG 不入 git——load_tex 守卫装载,缺位=字形回退)──
|
||||
# ── 英雄单位精灵状态(素材 PNG 不入 git——load_tex 守卫装载,缺位=字形回退)──
|
||||
var _hero_tex_idle: Texture2D # 站立帧(null = 素材缺位——零精灵,字形「人」原路径)
|
||||
var _hero_tex_walk: Texture2D # 跨步帧
|
||||
var _hero_last := Vector2i(-99, -99) # 上拍英雄格(refresh diff 基准;哨兵 = 未落位/随 rebuild 复位)
|
||||
@@ -108,31 +106,29 @@ var _hero_move_tw: Tween
|
||||
var _hero_bump_tw: Tween
|
||||
var _hero_dig_tw: Tween
|
||||
|
||||
# ── 瓦片状态(高墙瓦片接线批 2026-09-26;素材缺位 = 全 false/空——程序化平色回退)──
|
||||
# ── 瓦片状态(素材缺位 = 全 false/空——程序化平色回退)──
|
||||
var _tiles := {} # 词表键 → Texture2D(镜像件已预翻转;装载见 _load_board_tiles)
|
||||
var _floor_tiles: Array[Texture2D] = [] # 地面瓦片五变体(下标 0..4 = floor_1..5)
|
||||
var _tiles_ok := false # false = 素材缺位/未装载——bg_sb 程序化平色原路径
|
||||
var _seal_tex: Texture2D = null # 封印印记纹理(缺盘=null=字形「封印」回退——独立可选件不连瓦片整批)
|
||||
var _tiles_board_type := "dungeon" # 当前装载瓦片套的 board_type(rebuild 按板切换——T6 换色批)
|
||||
var _tiles_board_type := "dungeon" # 当前装载瓦片套的 board_type(rebuild 按板切换)
|
||||
var _digit_dark_ink := true # 数字墨族按板自适应(true=深墨三档)。缺盘平色回退态 #B39A78 裸土
|
||||
# 为中明度底——深墨族 CR 4.96 达标、亮墨族仅 2.2,故缺盘默认深墨;瓦片装载后按实测分位数覆写
|
||||
var _digit_plate := false # 数字色牌垫底模式(双峰纹理板——直画两族皆不过门槛时落牌,
|
||||
# 素牌+深墨 12:1 纹理免疫;2026-10-01 渲染帧取证 underdark 系深墨直画隐形后启用)
|
||||
# 素牌+深墨 12:1 纹理免疫;渲染帧取证 underdark 系深墨直画隐形,故启用色牌)
|
||||
|
||||
# ---- 视野迷雾(迭代 11 第④批 T2/T4——纯呈现层;正主=HUD框架「视野迷雾」节 v0.13,渲染规格=art-bible §6.5 v3.5)----
|
||||
# ---- 视野迷雾(纯呈现层;正主=HUD框架「视野迷雾」节 v0.13,渲染规格=art-bible §6.5 v3.5)----
|
||||
const FOG_RADIUS := 8 # 菱形灯光区基准半径(曼哈顿距离 ≤R——与微光环「周围 8 格」同圈起步;半径基准值改动=非白名单须用户)
|
||||
var _fog_enabled := true # 迷雾开关(呈现态布尔——真实游戏恒 on;探针/夹具可 off=无迷雾基线,两态结算逐位一致断言面)
|
||||
var _fog_lantern := 0 # 视野加成现值(缺省 0=无加成基线零扰动;生产写入面=组合根每战装配链显式重算——main._sync_treasure_glow)
|
||||
|
||||
# 视野加成接线(迭代 22 秘宝批 T4 换代——旧提灯布尔桥 set_fog_lantern 与 FOG_LANTERN_BONUS 常量
|
||||
# 随 fog_lantern 行退役摘除〔V12〕;新主语=阔视野带 light_radius_bonus 装配参数(magnitude 4→半径
|
||||
# 8+4=12 格 provisional);类型微光不随视野扩〔微光锚=可挖域贴身 8 邻零改动〕沿迭代 14⑤ 拍板。
|
||||
# 组合根 main._sync_treasure_glow 每战首轮装配链显式重算调用;缺省 0=off 态与旧基线逐位一致。
|
||||
# 视野加成接线:light_radius_bonus 装配参数(magnitude 4→半径 8+4=12 格 provisional);类型微光
|
||||
# 不随视野扩〔微光锚=可挖域贴身 8 邻零改动〕。组合根 main._sync_treasure_glow 每战首轮装配链显式重算。
|
||||
func set_fog_glow_bonus(bonus: int) -> void:
|
||||
_fog_lantern = maxi(0, bonus)
|
||||
refresh() # 灯光区半径变了——全板重算雾态(refresh/notify_hero_move 既有重绘驱动复用)
|
||||
|
||||
var cell_size: float: # 只读属性(棋盘格边长对外契约出口——旧私有名 _cell 的契约化出口)
|
||||
var cell_size: float: # 只读属性(棋盘格边长对外契约出口)
|
||||
get:
|
||||
return _cell
|
||||
|
||||
@@ -149,7 +145,7 @@ func _init() -> void:
|
||||
_svp_container.mouse_filter = Control.MOUSE_FILTER_IGNORE # 输入归壳层 _gui_input(板级公式换算),不进 SubViewport
|
||||
_wrap.add_child(_svp_container)
|
||||
_viewport = SubViewport.new()
|
||||
_viewport.transparent_bg = true # 格间距露出 D 区底色(与旧 Button 网格间隙同观感)
|
||||
_viewport.transparent_bg = true # 格间距露出 D 区底色
|
||||
_svp_container.add_child(_viewport)
|
||||
_world = _BOARD_WORLD.new()
|
||||
_viewport.add_child(_world)
|
||||
@@ -158,7 +154,7 @@ func _init() -> void:
|
||||
_desat_mat.set_shader_parameter("strength", 0.0)
|
||||
_hero_tex_idle = UiKit.load_tex(UiKit.TEX_HERO_IDLE)
|
||||
_hero_tex_walk = UiKit.load_tex(UiKit.TEX_HERO_WALK)
|
||||
_load_board_tiles() # 高墙/地面瓦片整批守卫装载(缺位静默回退——英雄批同款纪律,禁刷屏)
|
||||
_load_board_tiles() # 高墙/地面瓦片整批守卫装载(缺位静默回退——英雄同款纪律,禁刷屏)
|
||||
|
||||
|
||||
func board() -> BoardData:
|
||||
@@ -171,7 +167,7 @@ func _ready() -> void:
|
||||
|
||||
|
||||
# 结算期/道具窗口态:D 区去饱和 + 金橙偏移锁输入(明度不变——art-bible §2);
|
||||
# 材质挂 BoardWorld 世界级单点(CellUnit use_parent_material 链下随世界灰化——语义与旧逐格挂材质一致)
|
||||
# 材质挂 BoardWorld 世界级单点(CellUnit use_parent_material 链下随世界灰化)
|
||||
func set_desaturated(on: bool) -> void:
|
||||
if _desat_tween != null and _desat_tween.is_valid():
|
||||
_desat_tween.kill()
|
||||
@@ -182,7 +178,7 @@ func set_desaturated(on: bool) -> void:
|
||||
_world.material = _desat_mat if on else null
|
||||
|
||||
|
||||
# 视野迷雾开关(迭代 11 第④批 T2——呈现态布尔:真实游戏恒 on;探针/夹具 off=无迷雾基线,
|
||||
# 视野迷雾开关(呈现态布尔:真实游戏恒 on;探针/夹具 off=无迷雾基线,
|
||||
# 迷雾开/关两态结算逐位一致断言面;切换即全板重算 fog_state)
|
||||
func set_fog_enabled(on: bool) -> void:
|
||||
_fog_enabled = on
|
||||
@@ -200,12 +196,12 @@ func set_variant_mask_resolver(cb: Callable) -> void:
|
||||
_mask_cb = cb
|
||||
|
||||
|
||||
# 组合根注入 地形变异 角标只读回调(迭代 6 T6——flat_damage 未开格视觉标记;变异是策略纹理非数值墙:
|
||||
# 组合根注入 地形变异 角标只读回调(flat_damage 未开格视觉标记;变异是策略纹理非数值墙:
|
||||
# 玩家可据此绕行/承担的决策输入。空 Callable = 全板无标记(缺位回退态)
|
||||
func set_variant_mark_resolver(cb: Callable) -> void:
|
||||
_vmark_cb = cb
|
||||
|
||||
# 秘宝叠加层投影注入(迭代 7 T4——组合根在 use 成功/三清后调;refresh 消费)
|
||||
# 秘宝叠加层投影注入(组合根在 use 成功/三清后调;refresh 消费)
|
||||
func set_treasure_overlays(reveal_cells: Array, preview_cells: Array) -> void:
|
||||
_treasure_reveal.clear()
|
||||
_treasure_preview.clear()
|
||||
@@ -216,8 +212,8 @@ func set_treasure_overlays(reveal_cells: Array, preview_cells: Array) -> void:
|
||||
refresh()
|
||||
|
||||
|
||||
# SubViewport 主题/字体显式注入挂点(Phase B 两舞台复用——SubViewport 不自动继承主场景 Control 主题;
|
||||
# 本项目无自定义主题,缺省 = 引擎回退字体,与旧 Button 渲染同源)
|
||||
# SubViewport 主题/字体显式注入挂点(SubViewport 不自动继承主场景 Control 主题;
|
||||
# 本项目无自定义主题,缺省 = 引擎回退字体)
|
||||
func set_world_font(f: Font) -> void:
|
||||
_world.board_font = f
|
||||
for c in _cells:
|
||||
@@ -226,8 +222,7 @@ func set_world_font(f: Font) -> void:
|
||||
|
||||
|
||||
# D 区可用面积(AP 条之外)→ 格边长 = floor((min(宽, 高) − 预留 − (N−1)×格间距) / N)(battle.md 格尺寸公式)
|
||||
# 2026-09-19 修复:格边长公式补算 (N−1)×2px 网格间距 + 垂直预留 24px——旧公式只算 N×cell,
|
||||
# L2 12×12 板网格实高溢出可用区 ~6px,下缘被 clip_contents 裁掉约 1/6 格高。
|
||||
# 公式须补算 (N−1)×2px 网格间距与垂直预留 24px,否则 12×12 板实高溢出可用区 ~6px,下缘被 clip_contents 裁掉。
|
||||
func set_cell_area(avail: Vector2) -> void:
|
||||
if _board == null or _board.width == 0:
|
||||
return
|
||||
@@ -265,7 +260,7 @@ func rebuild(board: BoardData, minesweeper: MinesweeperRules) -> void:
|
||||
_minesweeper = minesweeper
|
||||
for child in _world.get_children():
|
||||
child.queue_free()
|
||||
_world.hero = null # 英雄精灵随清场退役——下拍 refresh 经 ensure_hero 重建(首建即时落位)
|
||||
_world.hero = null # 英雄精灵随清场复位——下拍 refresh 经 ensure_hero 重建(首建即时落位)
|
||||
_cells.clear()
|
||||
_glow.clear()
|
||||
_glow_errors.clear()
|
||||
@@ -275,7 +270,7 @@ func rebuild(board: BoardData, minesweeper: MinesweeperRules) -> void:
|
||||
_hover = Vector2i(-9, -9)
|
||||
for c in _board.compute_r0():
|
||||
_r0[c] = true
|
||||
# 瓦片套随板图型切换(T6 换色批——board_gen 词表 v1.1 dungeon/underdark/deep_underdark;
|
||||
# 瓦片套随板图型切换(board_gen 词表 v1.1 dungeon/underdark/deep_underdark;
|
||||
# 同图型重复 rebuild 零重载,_tiles_board_type 守卫)
|
||||
var bt: String = _board.board_type if _board.board_type != "" else "dungeon"
|
||||
if bt != _tiles_board_type:
|
||||
@@ -288,11 +283,11 @@ func rebuild(board: BoardData, minesweeper: MinesweeperRules) -> void:
|
||||
u.cell = c
|
||||
u.edge = _cell
|
||||
u.position = Vector2(c) * stride
|
||||
u.bevel = DEPTH_BEVEL # 定稿倒角——新建格单元继承
|
||||
u.bevel = DEPTH_BEVEL # 新建格单元继承
|
||||
u.use_parent_material = true # 去饱和材质挂 BoardWorld——单元随世界材质灰化
|
||||
if _tiles_ok:
|
||||
# NEAREST 采样(2026-09-26 用户拍板:32×32 源 × 45/36px 格边非整数缩放接受档,
|
||||
# 与英雄小人 1.5× 豁免同级——圣经 §8.2 豁免注记归 art-bible 批补写);只在瓦片
|
||||
# NEAREST 采样(32×32 源 × 45/36px 格边非整数缩放接受档,
|
||||
# 与英雄小人 1.5× 豁免同级(圣经 §8.2);只在瓦片
|
||||
# 模式启用——程序化回退路径渲染零扰动(含格面文字采样不回退不变化)
|
||||
u.texture_filter = CanvasItem.TEXTURE_FILTER_NEAREST
|
||||
u.font = _world.board_font
|
||||
@@ -318,10 +313,10 @@ func refresh() -> void:
|
||||
_paint_cell(Vector2i(x, y), hero)
|
||||
_refresh_hover_after_paint()
|
||||
_apply_contact_bands()
|
||||
_update_hero(hero) # 英雄精灵 diff 驱动(棋盘小人贴图批 2026-09-25)
|
||||
_update_hero(hero) # 英雄精灵 diff 驱动
|
||||
|
||||
|
||||
# 未开格类型微光作用集(迭代 30 卡①):= 可挖域 ∩ 身份可查格 ∩ 灯光区内。
|
||||
# 未开格类型微光作用集:= 可挖域 ∩ 身份可查格 ∩ 灯光区内。
|
||||
# 组号 = cell_type 经 UiKit.glow_group_for_cell_type 九值封闭判定得出;清单外值 = 数据错,
|
||||
# 登记进 _glow_errors 由断言面显式暴露,禁静默空光(图标键错误处理同款纪律)。
|
||||
# 纯派生:只读 query_identity 回调,零玩法数据写入(🔴 契约 1)。
|
||||
@@ -350,8 +345,8 @@ func _recompute_glow(hero: Vector2i) -> void:
|
||||
_glow[n] = g
|
||||
|
||||
|
||||
# 未开格类型微光开关(迭代 30 卡① · Q4 丙案):呈现态布尔,生产写入面 = 组合根单点。
|
||||
# 不新增设置项——工程有闸、界面无感;off 态为基线保护面(结算逐位一致断言承接,契约 1/T2)。
|
||||
# 未开格类型微光开关:呈现态布尔,生产写入面 = 组合根单点。
|
||||
# 不新增设置项——工程有闸、界面无感;off 态为基线保护面(结算逐位一致断言承接)。
|
||||
func set_type_glow_enabled(on: bool) -> void:
|
||||
if _type_glow == on:
|
||||
return
|
||||
@@ -360,7 +355,7 @@ func set_type_glow_enabled(on: bool) -> void:
|
||||
refresh()
|
||||
|
||||
|
||||
# ---- 入场演出(battle.md Transitions「install 淡入 200ms + 初始 R0 展开涟漪」——2026-09-26 补实装)----
|
||||
# ---- 入场演出(battle.md Transitions「install 淡入 200ms + 初始 R0 展开涟漪」)----
|
||||
# 全板 modulate 淡入 200ms;R0 格按离板心距离逐格延迟显形(涟漪);纯视觉 alpha,零输入影响;
|
||||
# 减动效时长减半(motion);重入(弃图换图 rebuild)先杀旧 tween 防叠加
|
||||
func _install_entrance_fx() -> void:
|
||||
@@ -385,7 +380,7 @@ func _install_entrance_fx() -> void:
|
||||
_ripple_tws.append(tw)
|
||||
|
||||
|
||||
# ---- 英雄单位精灵(棋盘小人贴图批 2026-09-25——计划书 §3.A/B;格面五 texture 槽零改动) ----
|
||||
# ---- 英雄单位精灵(计划书 §3.A/B;格面五 texture 槽零改动) ----
|
||||
|
||||
# refresh diff 驱动:位置变化 = 位移演出(140ms Tween + 走路帧);零位移但有受理移动回执 = 只播帧
|
||||
# 不位移(撞墙语义——排雷玩法「目标=当前格」幂等受理)。素材缺位 = 零精灵,字形「人」回退路径。
|
||||
@@ -410,7 +405,7 @@ func _update_hero(hero: Vector2i) -> void:
|
||||
|
||||
|
||||
# 英雄落位(instant = 即位无演出——首建/格边长变更;否则 140ms 位移 + 走路帧起手、到位回站帧。
|
||||
# 帧权威单一化:Sprite2D 换纹理路线(计划书 §3.B 二选一之后者)——换纹理+flip_h 同拍写定即生效,
|
||||
# 帧权威单一化:Sprite2D 换纹理路线(计划书 §3.B)——换纹理+flip_h 同拍写定即生效,
|
||||
# 无 AnimatedSprite2D 的 play()/advance(0) 错向面)
|
||||
func _place_hero(from: Vector2i, to: Vector2i, instant: bool) -> void:
|
||||
var sp: Sprite2D = _world.hero
|
||||
@@ -478,17 +473,16 @@ func cell_origin(c: Vector2i) -> Vector2:
|
||||
return _svp_container.position + Vector2(c) * (_cell + float(SEP))
|
||||
|
||||
|
||||
# 英雄脚底锚点——**世界坐标**(精灵在 SubViewport 世界内:格原点 = 格×步距,禁带壳层居中偏移。
|
||||
# 2026-09-26 修正:上批误用壳层 cell_origin(含 _svp_container.position 居中偏移)定位世界内精灵,
|
||||
# 实测整体右偏 ~7 格+偏离格心——实机截图用户报缺陷)。
|
||||
# 纵向 = 格底往上 25% 线(脚踩格子 1/4 处——2026-09-26 用户裁决);横向格中线。身体向上延伸,
|
||||
# 头越上格沿不裁剪(立体感裁决);锚点与精灵缩放解耦(缩放只改身体向上长多少)。
|
||||
# 英雄脚底锚点——**世界坐标**(精灵在 SubViewport 世界内:格原点 = 格×步距,禁带壳层居中偏移——
|
||||
# cell_origin 含 _svp_container.position,世界内精灵定位误用会整体右偏 ~7 格+偏离格心)。
|
||||
# 纵向 = 格底往上 25% 线(脚踩格子 1/4 处);横向格中线。身体向上延伸,
|
||||
# 头越上格沿不裁剪(造立体感);锚点与精灵缩放解耦(缩放只改身体向上长多少)。
|
||||
func _hero_feet(c: Vector2i) -> Vector2:
|
||||
return Vector2(c) * (_cell + float(SEP)) + Vector2(_cell * 0.5, _cell * (1.0 - HERO_GROUND_FRAC))
|
||||
|
||||
|
||||
# 英雄持握手位(fx 同层坐标系 = 壳层坐标——fx 在 SubViewport 之外,须含 _svp_container.position
|
||||
# 居中偏移:世界脚底锚 + 壳层偏移即得)。手 = 盒底往上 1/4 盒高、水平居中(2026-09-25 用户裁决;
|
||||
# 居中偏移:世界脚底锚 + 壳层偏移即得)。手 = 盒底往上 1/4 盒高、水平居中(
|
||||
# 盒高随 HERO_SPRITE_SCALE——手位钉在身体上不随缩放漂移)
|
||||
func hero_hand_point() -> Vector2:
|
||||
return _svp_container.position + _hero_feet(_hero_last) \
|
||||
@@ -508,9 +502,8 @@ func click_cell(cell: Vector2i, button: int) -> void:
|
||||
_on_cell_input(cell, ev)
|
||||
|
||||
|
||||
# 板级公式单点(Node2D 迁移批 Decision 3):格坐标 = floor(板内局部坐标 / 格步长)
|
||||
#(格步长 = 格边长 + 间距;板外/负域返回 (-9,-9)。旧逐格 Button 命中被此函数取代;
|
||||
# 2px 格间隙落入左/上邻格——旧间隙为零命中死区,等价性偏差已登记迁移方案汇报面)
|
||||
# 板级公式单点:格坐标 = floor(板内局部坐标 / 格步长)
|
||||
#(格步长 = 格边长 + 间距;板外/负域返回 (-9,-9);2px 格间隙落入左/上邻格)
|
||||
func _cell_at(shell_pos: Vector2) -> Vector2i:
|
||||
if _board == null or _board.width == 0:
|
||||
return Vector2i(-9, -9)
|
||||
@@ -581,8 +574,8 @@ func _on_cell_input(c: Vector2i, ev: InputEvent) -> void:
|
||||
elif ev.button_index == MOUSE_BUTTON_RIGHT:
|
||||
if not _minesweeper.has_seal(c):
|
||||
action_requested.emit(c, "seal_place")
|
||||
# 封印 = 一次性承诺(封印不可逆批 2026-09-19):撤封印退役——封印格右键零动作;
|
||||
# 左键相邻封印格直接挖(四情况②④兑付改判,规则引擎放行)。
|
||||
# 封印 = 一次性承诺:右键对封印格零动作(无撤封印);
|
||||
# 左键相邻封印格直接挖(规则引擎放行)。
|
||||
|
||||
|
||||
# chord 肌肉记忆首遇锚:同格双左击快开尝试(350ms 窗)——每 run 一次提示由组合根去重
|
||||
@@ -629,11 +622,11 @@ func _apply_hover_visuals(c: Vector2i) -> void:
|
||||
_show_path_preview(c)
|
||||
elif adjacent:
|
||||
u.hl_visible = true
|
||||
u.hl_color = COL_HL_TARGET # 挖掘目标格边框(相邻封印格同此——左键直接挖,封印不可逆批)
|
||||
u.hl_color = COL_HL_TARGET # 挖掘目标格边框(相邻封印格同此——左键直接挖)
|
||||
_set_cursor_shape(c, CURSOR_DIG)
|
||||
elif seal:
|
||||
u.hl_visible = true
|
||||
u.hl_color = COL_SEAL # 已承诺封印标记(封印黑;无交互可供——撤封印已退役)
|
||||
u.hl_color = COL_SEAL # 已承诺封印标记(封印黑;无交互可供)
|
||||
_set_cursor_shape(c, Input.CURSOR_FORBIDDEN)
|
||||
else:
|
||||
u.hl_visible = true
|
||||
@@ -650,11 +643,11 @@ func _clear_hover_visuals(c: Vector2i) -> void:
|
||||
|
||||
|
||||
func _set_cursor_shape(c: Vector2i, shape: int) -> void:
|
||||
mouse_default_cursor_shape = shape # 壳级光标(悬停格语义决定——单格悬停恒一,等价旧逐格光标)
|
||||
mouse_default_cursor_shape = shape # 壳级光标(悬停格语义决定——单格悬停恒一)
|
||||
|
||||
|
||||
func _show_path_preview(c: Vector2i) -> void:
|
||||
# D3 合法性指示:悬停已开格 → BFS 路径蓝细线点迹(compute_move_path 只读面)
|
||||
# 合法性指示:悬停已开格 → BFS 路径蓝细线点迹(compute_move_path 只读面)
|
||||
if _minesweeper == null:
|
||||
return
|
||||
var path: Array[Variant] = _minesweeper.compute_move_path(c)
|
||||
@@ -675,7 +668,7 @@ func _update_badge(c: Vector2i) -> String:
|
||||
return ""
|
||||
if _r0.has(c):
|
||||
return "" # R₀ 初始展开格无奖励身份(地图战斗资源 资格域 = S∖R₀)
|
||||
var rec: Dictionary[Variant, Variant] = _minesweeper.reveal_record(c) # E2 收口:已开记录经查询面(禁直读 排雷玩法 状态字典)
|
||||
var rec: Dictionary[Variant, Variant] = _minesweeper.reveal_record(c) # 已开记录经查询面(禁直读 排雷玩法 状态字典)
|
||||
if rec.get("kind", "") != "safe":
|
||||
return ""
|
||||
var ident = _identity_cb.call(c)
|
||||
@@ -684,7 +677,7 @@ func _update_badge(c: Vector2i) -> String:
|
||||
return _badge_glyph(String(ident.get("cell_type", "")))
|
||||
|
||||
|
||||
# 奖励图标键解析(HUD框架:查不到的键立即断言 + ? 占位纹样 + 诊断;键集 = 格面资源 cell_type 九值枚举——GP+商店房实施批 2026-09-21 八→九)
|
||||
# 奖励图标键解析(HUD框架:查不到的键立即断言 + ? 占位纹样 + 诊断;键集 = 格面资源 cell_type 九值枚举)
|
||||
func _badge_glyph(category: String) -> String:
|
||||
if REWARD_BADGE.has(category):
|
||||
return REWARD_BADGE[category]
|
||||
@@ -695,7 +688,7 @@ func _badge_glyph(category: String) -> String:
|
||||
|
||||
# ---- 格态渲染(灰度截图可辨:完整覆尘 / 碎裂裸土 / 已开数字) ----
|
||||
|
||||
# 数字三档色温(按板自适应双族——v3.5 冻结④ 修订 2026-10-01):亮墨族 1-3 冷亮/4-5 暖白/6-8 金亮
|
||||
# 数字三档色温(按板自适应双族——v3.5 冻结④):亮墨族 1-3 冷亮/4-5 暖白/6-8 金亮
|
||||
#(dungeon 瓦片实测达标维持);深墨族 1-3 青墨/4-5 赭墨/6-8 锈墨(色温语义同构——underdark/
|
||||
# deep_underdark 亮白筋纹瓦片上亮墨对亮斑 CR 1.0–1.9 融字,深墨族 5.7–11.1;选族判据见 _digit_ink_mode)
|
||||
func _digit_ink(d: int) -> Color:
|
||||
@@ -718,20 +711,20 @@ func _paint_cell(c: Vector2i, hero: Vector2i) -> void:
|
||||
return
|
||||
var revealed: bool = _minesweeper.is_revealed(c)
|
||||
var seal: bool = _minesweeper.has_seal(c)
|
||||
var removed: bool = _minesweeper.is_removed(c) # 迭代 13 T8/D6——中立移除格(P2 甲退色降档)
|
||||
var removed: bool = _minesweeper.is_removed(c) # 中立移除格(退色降档)
|
||||
var is_hero: bool = c == hero
|
||||
var glyph := ""
|
||||
var bg: Color
|
||||
var digit_col := COL_DIGIT
|
||||
# 封印槽统一维护(T17 修复):封印格=封印印记纹理;其余格清 null(封印格挖开后残留在 tex_seal
|
||||
# 封印槽统一维护:封印格=封印印记纹理;其余格清 null(封印格挖开后残留在 tex_seal
|
||||
# 会让 _draw 无条件把封印印画在已开格上——tex_seal 绘制无 revealed 条件)。缺盘走字形回退。
|
||||
u.tex_seal = _seal_tex if (seal and not revealed and _seal_tex != null) else null
|
||||
if removed:
|
||||
revealed = false # 移除格固定未开呈现基座(退色暗幕层承载;危险格本无数字——P1 甲周边数字冻结)
|
||||
revealed = false # 移除格固定未开呈现基座(退色暗幕层承载;危险格本无数字,周边数字冻结)
|
||||
seal = false
|
||||
u.tex_seal = null
|
||||
if revealed:
|
||||
var rec: Dictionary[Variant, Variant] = _minesweeper.reveal_record(c) # E2 收口:已开记录经查询面
|
||||
var rec: Dictionary[Variant, Variant] = _minesweeper.reveal_record(c) # 已开记录经查询面
|
||||
if rec.get("kind", "") == "mine":
|
||||
bg = COL_FLOOR
|
||||
glyph = "雷"
|
||||
@@ -744,7 +737,7 @@ func _paint_cell(c: Vector2i, hero: Vector2i) -> void:
|
||||
glyph = "?" # 数字遮蔽(地形变异 hide_number——信息型:该格真实数字不显示)
|
||||
else:
|
||||
glyph = str(d)
|
||||
digit_col = _digit_ink(d) # 数字墨三档色温(样张 v2 冻结批 T4——§4.1)
|
||||
digit_col = _digit_ink(d) # 数字墨三档色温(§4.1)
|
||||
else:
|
||||
if seal:
|
||||
bg = COL_BRICK
|
||||
@@ -753,10 +746,10 @@ func _paint_cell(c: Vector2i, hero: Vector2i) -> void:
|
||||
digit_col = COL_SEAL
|
||||
else:
|
||||
bg = COL_BRICK
|
||||
# 瓦片模式(高墙接线批 2026-09-26):tex_base = 查表瓦片(未开=墙瓦片/已开=地面瓦片),
|
||||
# 瓦片模式:tex_base = 查表瓦片(未开=墙瓦片/已开=地面瓦片),
|
||||
# bg_sb 降级为透明描边载体——只承载呼吸微光/主角蓝框(CellUnit 瓦片态画在瓦片之上),
|
||||
# 程序化平色退役;瓦片分配=格坐标哈希,初始化落定后逐拍稳定(禁逐轮重掷——圣经原则三)
|
||||
# draw calls 批优化(2026-10-01):透明载体 sb 视觉贡献=零(透明底+零描边——不可见),但
|
||||
# 瓦片分配=格坐标哈希,初始化落定后逐拍稳定(禁逐轮重掷——圣经原则三)
|
||||
# draw calls 批优化:透明载体 sb 视觉贡献=零(透明底+零描边——不可见),但
|
||||
# StyleBoxFlat 多边形命令不可合批,每格打断棋盘批——实测 100 格满负荷帧 ~402 DC(A/B 探针
|
||||
# 阶梯:纹理统一 0 增益/去 glyph −23/去叠层 −23/去隐形 sb −402)。非英雄格 sb 直接置 null
|
||||
#(绘制路径零像素变化),仅英雄格保留蓝框载体。
|
||||
@@ -780,28 +773,27 @@ func _paint_cell(c: Vector2i, hero: Vector2i) -> void:
|
||||
u.bg_sb = sb
|
||||
u.glyph = glyph
|
||||
u.glyph_color = digit_col
|
||||
# 色牌后备(v3.5 批 B2——数字直画退牌;雷/「?」维持牌载保读性)+ 2026-10-01 v3.4:双峰纹理板
|
||||
# 色牌后备(数字直画退牌;雷/「?」维持牌载保读性)+ v3.4:双峰纹理板
|
||||
#(_digit_plate——underdark 系亮白筋+近黑斑并存的直画不可救纹理)数字落牌,牌上深墨 12:1
|
||||
var plate_digit := _digit_plate and glyph != "" and glyph != "雷" and glyph != "?" and glyph != "封" and glyph != "人"
|
||||
if plate_digit:
|
||||
digit_col = COL_DIGIT # 牌上深墨族(素牌 12:1——双峰纹理免疫)
|
||||
u.glyph_color = digit_col
|
||||
u.plate = revealed and (glyph == "雷" or glyph == "?" or plate_digit)
|
||||
u.outline_sides.assign([]) # typed Array[int](代码审查批 3 类型收紧——assign 保持引用语义)
|
||||
u.outline_sides.assign([]) # typed Array[int](assign 保持引用语义)
|
||||
u.facade_south = false
|
||||
u.proj_band = 0.0
|
||||
u.tex_facade = null
|
||||
u.facade_strip = false
|
||||
# 封印槽不在此重置(2026-10-02 修复批——T13 批的 paint 尾部 `u.tex_seal = null` 与 L683 的条件
|
||||
# 赋值构成双写踩踏:执行序 683→此处把封印纹理恒清 null=素材在位时封印不可见;L683 的 else 分支
|
||||
# 已是全路径清残留,尾部重置系冗余且有害,删除)
|
||||
# 封印槽不在此重置:尾部重置会与此上方条件赋值构成双写踩踏(把封印纹理恒清 null=素材在位时封印不可见);
|
||||
# 上方 else 分支已是全路径清残留,尾部重置系冗余且有害
|
||||
var south_n: Vector2i = c + Vector2i(0, 1)
|
||||
var north_n: Vector2i = c + Vector2i(0, -1)
|
||||
var south_in: bool = south_n.x >= 0 and south_n.y >= 0 and south_n.x < _board.width and south_n.y < _board.height
|
||||
var north_in: bool = north_n.x >= 0 and north_n.y >= 0 and north_n.x < _board.width and north_n.y < _board.height
|
||||
if not revealed:
|
||||
# 高墙感 D 投影档(2026-09-28 挑档落码):南邻地面格 → 墙格南缘立「口沿高光+双段暗带」立面
|
||||
#(邻地面才画——墙-墙内界保墙群内聚,轮廓加强批同口径);板外=墙不画
|
||||
# 高墙感 D 投影档:南邻地面格 → 墙格南缘立「口沿高光+双段暗带」立面
|
||||
#(邻地面才画——墙-墙内界保墙群内聚);板外=墙不画
|
||||
u.facade_south = south_in and _minesweeper.is_revealed(south_n)
|
||||
# 轮廓加强:墙格四邻中已开(地面)格朝向记描边码(板外=墙不描——保墙群外轮廓只在墙/地交界)
|
||||
for d in [Vector2i(0, -1), Vector2i(0, 1), Vector2i(-1, 0), Vector2i(1, 0)]:
|
||||
@@ -811,7 +803,7 @@ func _paint_cell(c: Vector2i, hero: Vector2i) -> void:
|
||||
if code == 1 and u.facade_south:
|
||||
continue # 南缘由立面件承载(高光+双段暗带)——2px 描边让位防三重叠黑
|
||||
if code == 0 and _tiles_ok:
|
||||
continue # v3.5 冻结① (b)-(c) 交界无边框:墙面直贴接墙顶(E 格墙面件在邻格承载)——北向描边退役(平色回退态保留描边)
|
||||
continue # v3.5 冻结① (b)-(c) 交界无边框:墙面直贴接墙顶(E 格墙面件在邻格承载)——北向描边不画(平色回退态保留描边)
|
||||
u.outline_sides.append(code)
|
||||
else:
|
||||
# 地面投影:北邻墙格在本已开格北缘投渐隐暗带(ITB 同款高度线索——带宽按格边比例常量)
|
||||
@@ -822,7 +814,7 @@ func _paint_cell(c: Vector2i, hero: Vector2i) -> void:
|
||||
if south_in and not _minesweeper.is_revealed(south_n) and _tiles_ok:
|
||||
u.facade_strip = true
|
||||
u.tex_facade = _tiles.get("wall_center")
|
||||
# ②( 秘宝叠加层(迭代 7 T4):真视格=红内缩框;预读格=奖励身份徽章(identity_resolver 既有
|
||||
# ②( 秘宝叠加层:真视格=红内缩框;预读格=奖励身份徽章(identity_resolver 既有
|
||||
# 只读面取字形——零数据复制,badge 位复用);已开格标记自然退位(refresh 逐拍重算)
|
||||
if not revealed:
|
||||
u.treasure_mark = "mine_reveal" if _treasure_reveal.has(c) else ""
|
||||
@@ -832,15 +824,15 @@ func _paint_cell(c: Vector2i, hero: Vector2i) -> void:
|
||||
glyph = String(REWARD_BADGE[String(pid.get("cell_type", ""))])
|
||||
digit_col = COL_GOLD
|
||||
u.badge_glyph = glyph
|
||||
# ②' 变异角标(迭代 6 T6):未开格带伤害型变异 → 危险红角标(已开=威胁已兑现标记退位);
|
||||
# 回调缺位 = 无标记(程序化角标,tex_mutation 纹带槽留给变异纹带自产批)
|
||||
# ②' 变异角标:未开格带伤害型变异 → 危险红角标(已开=威胁已兑现标记退位);
|
||||
# 回调缺位 = 无标记(程序化角标,tex_mutation 纹带槽暂留)
|
||||
if not revealed and _vmark_cb.is_valid():
|
||||
u.mutation_mark = String(_vmark_cb.call(c))
|
||||
else:
|
||||
u.mutation_mark = ""
|
||||
u.badge_glyph = _update_badge(c)
|
||||
u.glow_group = int(_glow.get(c, UiKit.GLOW_NONE))
|
||||
# 视野迷雾呈态(迭代 11 第④批 T2/T3):曼哈顿距离 > 半径 → 区外——未开格全黑(正主 :163 照抄)/
|
||||
# 视野迷雾呈态:曼哈顿距离 > 半径 → 区外——未开格全黑(正主照抄)/
|
||||
# 已开格记忆态(灰显可读);以英雄格为心逐格重算,refresh()/notify_hero_move() 既有重绘驱动=移动实时跟随。
|
||||
# 纯呈现零逻辑——格态/数字真值/清图判定零触碰(🔴 契约 1);结算期去饱和叠加互斥由 CellUnit 记忆态终态档承载。
|
||||
if _fog_enabled:
|
||||
@@ -854,7 +846,7 @@ func _paint_cell(c: Vector2i, hero: Vector2i) -> void:
|
||||
|
||||
|
||||
# ---- 可挖域接触带呼吸微光(挖掘期唯一在动元素——§3.2 v2.8 画法:光画英雄格周界空间侧,
|
||||
# 正交邻墙=整条边、斜向=共享角点+两侧各半条边;旧"墙格整框绿相"退役 2026-09-26 批) ----
|
||||
# 正交邻墙=整条边、斜向=共享角点+两侧各半条边) ----
|
||||
|
||||
func set_dig_glow_enabled(on: bool) -> void:
|
||||
_dig_glow = on
|
||||
@@ -865,7 +857,7 @@ func set_dig_glow_enabled(on: bool) -> void:
|
||||
_sync_glow_to_units() # 相位切换 → 类型微光作用集随之增减(只重绘变动格)
|
||||
|
||||
|
||||
# 类型微光作用集与格件同步(迭代 30 卡①):只重绘「上一拍有组/这一拍有组」的并集格,
|
||||
# 类型微光作用集与格件同步:只重绘「上一拍有组/这一拍有组」的并集格,
|
||||
# 其余格零触碰;与 refresh() 全板重绘互为补充(refresh 走全板,本函数走增量)。
|
||||
func _sync_glow_to_units() -> void:
|
||||
if _board == null or _minesweeper == null:
|
||||
@@ -932,15 +924,15 @@ func _reset_breath() -> void:
|
||||
_contact_unit.queue_redraw()
|
||||
|
||||
|
||||
# ---- 高墙/地面瓦片接线(2026-09-26 批;词表 = design/research/board-tile-vocab-plan-a-2026-09-26.md) ----
|
||||
# ---- 高墙/地面瓦片接线(词表 = design/research/board-tile-vocab-plan-a-2026-09-26.md) ----
|
||||
|
||||
# 瓦片整批守卫装载(_init 单次调用):21 枚全成才启用——任一缺位按整批缺位处理(禁同板混渲染;
|
||||
# fresh checkout 无素材目录 → 静默全回退)。缺位不 push_error——可选素材缺位是预期路径非故障
|
||||
# (英雄批 load_tex 纪律同款,防冒烟刷屏)。镜像件运行时 flip_x 合成,不出文件。
|
||||
# (英雄 load_tex 纪律同款,防冒烟刷屏)。镜像件运行时 flip_x 合成,不出文件。
|
||||
func _load_board_tiles(board_type: String = "dungeon") -> void:
|
||||
if board_type == "":
|
||||
board_type = "dungeon"
|
||||
# 16 墙瓦片装载循环节(词表 TILE_WALL_NAMES 单点——T4 杂并:手写 16 条字典项重复退役;
|
||||
# 16 墙瓦片装载循环节(词表 TILE_WALL_NAMES 单点;
|
||||
# 词表本身被 terrain_tile_sets_test 直读,保留)
|
||||
var t := {}
|
||||
for wall_name in TILE_WALL_NAMES:
|
||||
@@ -956,8 +948,8 @@ func _load_board_tiles(board_type: String = "dungeon") -> void:
|
||||
if tex == null:
|
||||
_request_dungeon_fallback(board_type)
|
||||
return
|
||||
# 地板归一(圣经 v3.5 冻结①/§1.4 样张 v3 记录——已开格底归一世界底层档 L≈0.10,带 0.08–0.14):
|
||||
# 方向稿 _v3_darken_floors 同式移植;现行瓦片 L≈0.036 低于带下沿故实为提亮;素材重产归资产批,
|
||||
# 地板归一(圣经 v3.5 冻结①/§1.4——已开格底归一世界底层档 L≈0.10,带 0.08–0.14):
|
||||
# 方向稿 _v3_darken_floors 同式移植;现行瓦片 L≈0.036 低于带下沿故实为提亮;
|
||||
# 本归一为程序代役(逐批装载时对 freshly-loaded 原图测光,幂等)。归一后逐像素明度顺路采集,
|
||||
# 驱动数字墨族按板自适应(_digit_ink_mode——均值分位同源同像素,禁二次解码)
|
||||
var lumas: Array[Variant] = []
|
||||
@@ -979,11 +971,11 @@ func _load_board_tiles(board_type: String = "dungeon") -> void:
|
||||
_floor_tiles = floors
|
||||
_tiles_board_type = board_type
|
||||
_tiles_ok = true
|
||||
# 封印印记纹理(迭代 9 卡⑬——独立可选件:缺盘=字形回退,不触发瓦片整批回落语义)
|
||||
# 封印印记纹理(独立可选件:缺盘=字形回退,不触发瓦片整批回落语义)
|
||||
_seal_tex = UiKit.load_tex(TILE_SEAL_MARK)
|
||||
|
||||
|
||||
# 整批回落(T6 换色批):请求套缺位 → 回落 dungeon 套(禁同板混渲染);dungeon 本身缺位 =
|
||||
# 整批回落:请求套缺位 → 回落 dungeon 套(禁同板混渲染);dungeon 本身缺位 =
|
||||
# 维持程序化平色回退原语义(_tiles_ok 恒 false,缺位不刷屏纪律不变)
|
||||
func _request_dungeon_fallback(requested: String) -> void:
|
||||
if requested != "dungeon":
|
||||
@@ -1000,7 +992,7 @@ static func _flip_tex(tex: Texture2D) -> Texture2D:
|
||||
return ImageTexture.create_from_image(img)
|
||||
|
||||
|
||||
# 地板瓦片明度归一(v3.5 批 B1——方向稿 _v3_darken_floors 同式移植):8×8 双线性重采样均值测光
|
||||
# 地板瓦片明度归一(方向稿 _v3_darken_floors 同式移植):8×8 双线性重采样均值测光
|
||||
# (sRGB→线性域,Rec.709 系数),增益 k=(目标/实测)^(1/2.2) 钳 [0.6,1.8],逐通道 sRGB 域线性拉伸。
|
||||
# 输入恒为 freshly-loaded 原图(load_tex 每次盘读)——幂等可重入;32×32 五变体开销可忽略
|
||||
static func _normalize_floor_luma(tex: Texture2D, target_l: float = 0.10, lumas_out = null) -> Texture2D:
|
||||
@@ -1033,17 +1025,17 @@ static func _srgb_lin(c: float) -> float:
|
||||
return c / 12.92 if c <= 0.04045 else pow((c + 0.055) / 1.055, 2.4)
|
||||
|
||||
|
||||
# ---- 数字墨族按板自适应(L2/L3 亮瓦融字缺陷修复 2026-10-01——甲案:墨色按关自适应+三关对比断言)----
|
||||
# ---- 数字墨族按板自适应(墨色按关自适应+三关对比断言)----
|
||||
|
||||
const DIGIT_INK_CR_P90 := 4.5 # 墨族对地板 p90 亮斑对比度门槛(失效模式=亮白筋纹冲字,WCAG 数字体同源)
|
||||
const DIGIT_INK_CR_MEAN_BRIGHT := 2.5 # 亮族对地板均值门槛(中调防吃字)
|
||||
const DIGIT_INK_CR_MEAN_DARK := 3.0 # 深族对地板均值门槛(大字 AA 级——2.4 的实测观感=隐形,v3.4 收紧)
|
||||
const DIGIT_INK_CR_MEAN_DARK := 3.0 # 深族对地板均值门槛(大字 AA 级——2.4 的实测观感=隐形)
|
||||
|
||||
# 归一后地板逐像素明度 → 选数字呈现模式。三态:亮族直画(暗纹理板)/深族直画(中暗板)/
|
||||
# 色牌垫底(双峰纹理板——近黑斑与近白筋并存时任何墨族直画必融一峰,2026-10-01 渲染帧取证
|
||||
# 色牌垫底(双峰纹理板——近黑斑与近白筋并存时任何墨族直画必融一峰,渲染帧取证
|
||||
# 实证 underdark 系深墨隐形,牌上深墨 12:1 纹理免疫)。家族最弱成员(亮族=金亮/深族=锈墨)
|
||||
# 对 p90 与均值双门槛;亮族过选亮族,否则深族(均值门 3.0),两族皆不过落牌。
|
||||
# 实测(2026-10-01):dungeon p90=0.121 亮族 4.79/5.80 ✓ 直画;underdark p90=0.406 亮族 ✗、
|
||||
# 实测:dungeon p90=0.121 亮族 4.79/5.80 ✓ 直画;underdark p90=0.406 亮族 ✗、
|
||||
# 深族 5.34/**2.41**(均值 <3.0 ✗)→ 落牌;deep_underdark 同→落牌
|
||||
static func _digit_ink_mode(lumas: Array) -> Dictionary:
|
||||
var sorted := lumas.duplicate()
|
||||
@@ -1092,7 +1084,7 @@ static func _cell_hash(c: Vector2i) -> int:
|
||||
return h ^ (h >> 16)
|
||||
|
||||
|
||||
# 四正交邻墙判定(词表 §三.1:板外视为墙;未开格=墙、已开格=地板——用户世界观口径「未开=墙、
|
||||
# 四正交邻墙判定(词表 §三.1:板外视为墙;未开格=墙、已开格=地板——世界观口径「未开=墙、
|
||||
# 已开=挖出的空间」)。唯一调用面 _paint_cell(refresh 对 _minesweeper 非空守卫在先)
|
||||
func _is_wall(c: Vector2i) -> bool:
|
||||
if c.x < 0 or c.y < 0 or c.x >= _board.width or c.y >= _board.height:
|
||||
@@ -1140,7 +1132,7 @@ func _wall_tile_for(c: Vector2i) -> Texture2D:
|
||||
|
||||
|
||||
# 四邻皆墙格:对角凹角优先 nw > ne > sw > se(词表 §三.2——双凹角不做专件,多对角地板回落
|
||||
# 首中单凹角件,残留 1px 缝为已裁决可接受限);无对角地板 → 中心三变体按格坐标哈希 60/20/20
|
||||
# 首中单凹角件,残留 1px 缝为可接受限);无对角地板 → 中心三变体按格坐标哈希 60/20/20
|
||||
func _wall_center_tile(c: Vector2i) -> Texture2D:
|
||||
if not _is_wall(c + Vector2i(-1, -1)):
|
||||
return _tiles["wall_in_nw"] # 西北对角是地板
|
||||
@@ -1182,7 +1174,7 @@ func _make_cursor_texture(glyph: String) -> ImageTexture:
|
||||
return ImageTexture.create_from_image(img)
|
||||
|
||||
|
||||
# ---- 探针只读出口(D2 改造 2026-10-04——_cells/_diggable 私有容器直入退役;形态仿 fx_probe 先例:只读快照) ----
|
||||
# ---- 探针只读出口(形态仿 fx_probe 先例:只读快照) ----
|
||||
|
||||
# 格件映射只读快照(Vector2i → CellUnit;键面同构——断言语义零变化;破私有面的只是容器字典,
|
||||
# 格件字段本为公有呈现面,快照值即原格件引用)
|
||||
@@ -1195,22 +1187,22 @@ func diggable_probe() -> Dictionary:
|
||||
return _diggable.duplicate()
|
||||
|
||||
|
||||
# 悬停格只读出口(case① 悬停到达断言消费面——_hover 私有域直入退役,D2 清单外同类项随批收口)
|
||||
# 悬停格只读出口(case① 悬停到达断言消费面)
|
||||
func hover_probe() -> Vector2i:
|
||||
return _hover
|
||||
|
||||
|
||||
# 迷雾参数只读快照(case⑮ 参数漂移断言消费面——_fog_lantern/_fog_enabled 私有域直入退役,D2 清单外同类项随批收口)
|
||||
# 迷雾参数只读快照(case⑮ 参数漂移断言消费面)
|
||||
func fog_params_probe() -> Dictionary:
|
||||
return {"lantern": _fog_lantern, "enabled": _fog_enabled}
|
||||
|
||||
|
||||
# 类型微光只读快照(迭代 30 卡① 断言消费面——开关态/作用集与组号/清单外 cell_type 登记)
|
||||
# 类型微光只读快照(断言消费面——开关态/作用集与组号/清单外 cell_type 登记)
|
||||
func type_glow_probe() -> Dictionary:
|
||||
return {"enabled": _type_glow, "cells": _glow.duplicate(), "errors": _glow_errors.duplicate()}
|
||||
|
||||
|
||||
# 格件全局矩形只读出口(迭代 30 卡① 像素侧取样坐标源——禁测试侧硬编码像素;私有 _cells 直入退役)
|
||||
# 格件全局矩形只读出口(像素侧取样坐标源——禁测试侧硬编码像素)
|
||||
# 坐标系:格件居于 SubViewport 内(_world 于原点),global_position 是**子视口画布**坐标;
|
||||
# 取证帧截的是窗口画面 ⇒ 必须经 SubViewportContainer 的全局矩形换算回窗口坐标
|
||||
# (stretch 恒 1:1,仍按 host/vp 比例换算保稳健——容器中心化由 CenterContainer 驱动)。
|
||||
|
||||
@@ -3,8 +3,8 @@
|
||||
## 无 class_name——使用方经 preload 引用。
|
||||
extends Node2D
|
||||
|
||||
var board_font: Font = null # 字体注入槽位(null = ThemeDB.fallback_font——与旧 Button 缺省主题字体同源)
|
||||
var hero: Sprite2D = null # 英雄单位精灵(棋盘小人贴图批 2026-09-25——移动实体独立于 CellUnit 格面
|
||||
var board_font: Font = null # 字体注入槽位(null = ThemeDB.fallback_font)
|
||||
var hero: Sprite2D = null # 英雄单位精灵(移动实体独立于 CellUnit 格面
|
||||
# 语义,计划书 §3.A;null = 未创建/随 rebuild 清场,BoardView.refresh 经
|
||||
# ensure_hero 重建。texture 缺位时 BoardView 不调本方法=字形「人」回退路径)
|
||||
|
||||
@@ -12,8 +12,8 @@ var hero: Sprite2D = null # 英雄单位精灵(棋盘小人贴图批 2026-
|
||||
# 英雄精灵惰性创建(幂等):脚底锚(offset 上移半高——scale.y 弹跳向脚底收敛不悬空)+ z_index=1
|
||||
# 压格面之上(挥铲特效在 SubViewport 之外恒更上层,满足「格面之上、特效之下」叠序);NEAREST =
|
||||
# SubViewport 缩放下像素清晰(默认 Linear 会糊);use_parent_material = 随世界去饱和材质灰化
|
||||
#(结算期与 CellUnit 同口径——基批漏挂,本批补齐)。返回值便于 BoardView 即时落位。
|
||||
# 脚下椭圆影(2026-09-26 用户裁决走 Godot 组件合成、贴图零改动):径向渐变纹理 ×黑色半透明
|
||||
#(结算期与 CellUnit 同口径)。返回值便于 BoardView 即时落位。
|
||||
# 脚下椭圆影(走 Godot 组件合成、贴图零改动):径向渐变纹理 ×黑色半透明
|
||||
# modulate 压扁,挂英雄子件(z_index=-1 相对回落格面之上、英雄之下——位移/翻转/弹跳随父件
|
||||
# 同步免费拿;尺寸换算归 BoardView.HERO_SHADOW_PX 常量)。纹理 filter 用 LINEAR 保渐变平滑
|
||||
#(NEAREST 会把渐变打回条带)。
|
||||
@@ -35,16 +35,15 @@ func ensure_hero(tex: Texture2D, shadow_px: Vector2, scale: float) -> Sprite2D:
|
||||
shadow.modulate = Color(0, 0, 0, 0.35)
|
||||
shadow.texture_filter = CanvasItem.TEXTURE_FILTER_LINEAR
|
||||
shadow.scale = shadow_px / (64.0 * scale) # 子件缩放叠父件 scale——目标屏幕尺寸 ÷ (纹理边 × 父档)
|
||||
shadow.position = Vector2.ZERO # 椭圆中心 = 脚锚点(贴脚——上批 (0,2) 下坠让影子与脚脱开,用户报缺陷)
|
||||
shadow.position = Vector2.ZERO # 椭圆中心 = 脚锚点(贴脚)
|
||||
shadow.z_index = -1 # 相对父件回落:格面(z=0 树序在先)之上、英雄本体之下
|
||||
shadow.use_parent_material = true # 随英雄链世界去饱和材质
|
||||
hero = Sprite2D.new()
|
||||
hero.texture = tex
|
||||
hero.texture_filter = CanvasItem.TEXTURE_FILTER_NEAREST
|
||||
hero.centered = true
|
||||
# 脚浮修正(2026-09-26,样张预筛发现+用户实机确认的登记缺陷——台账 169/175 移交):素材底部 7 行全透明
|
||||
#(内容底 = 源 row 24,自中心 16-7.5=8.5px),旧 offset -16 按纹理底=内容底假设成立时脚悬空 8.5×1.5≈12.75px;
|
||||
# 修正 offset.y = -8.5 → 内容底恰落 position(脚锚=格底往上 25% 线,台账 162 口径——头仍越上格沿)
|
||||
# 脚底对齐:素材底部 7 行全透明(内容底 = 源 row 24,自中心 16−7.5=8.5px),故 offset.y = −8.5
|
||||
# → 内容底恰落 position(脚锚=格底往上 25% 线——头仍越上格沿)
|
||||
hero.offset = Vector2(0.0, -8.5) # 纹理中心上移 8.5 源 px——position = 视觉脚底中点
|
||||
hero.z_index = 1
|
||||
hero.use_parent_material = true
|
||||
@@ -52,7 +51,7 @@ func ensure_hero(tex: Texture2D, shadow_px: Vector2, scale: float) -> Sprite2D:
|
||||
hero.add_child(shadow)
|
||||
return hero
|
||||
|
||||
# ── 立体悬崖(BoardView.set_cell_area / _update_world_size 单点驱动;厚度定稿 8px=中档 2026-09-24)──
|
||||
# ── 立体悬崖(BoardView.set_cell_area / _update_world_size 单点驱动;厚度 8px=中档)──
|
||||
var depth_px := 0.0: # 悬崖厚度(像素;0 = 平面)
|
||||
set(v):
|
||||
depth_px = v
|
||||
@@ -69,7 +68,7 @@ var cols := 0: # 列数(正面悬崖竖缝数 = cols−1)
|
||||
set(v):
|
||||
cols = v
|
||||
queue_redraw()
|
||||
var mat: Color = UiKit.BRICK # 材质锚色(掩尘 #C9AF83 与 UiKit.BRICK 同值——T4 裸色收口改引单点;侧面由此派生明暗,BoardView 传入可覆写)
|
||||
var mat: Color = UiKit.BRICK # 材质锚色(掩尘 #C9AF83 与 UiKit.BRICK 同值;侧面由此派生明暗,BoardView 传入可覆写)
|
||||
|
||||
|
||||
func _draw() -> void:
|
||||
|
||||
@@ -8,7 +8,7 @@ var board_view: BoardView
|
||||
var toast: ToastStack
|
||||
var fx: BoardFxView
|
||||
var status_badge: Button # 状态面板入口角标(迭代 17 卡③ P1 丁;连接归组合根)
|
||||
var _board_area: Control # resized 命名转发用(原 lambda 局部捕获——P1-3 转命名 2026-09-29)
|
||||
var _board_area: Control # resized 命名转发用(勿回退为 lambda 局部捕获)
|
||||
|
||||
signal action_requested(cell: Vector2i, kind: String)
|
||||
signal stop_pressed
|
||||
@@ -38,7 +38,7 @@ func _init() -> void:
|
||||
board_area.add_child(fx)
|
||||
toast = ToastStack.new()
|
||||
board_area.add_child(toast)
|
||||
# 状态角标(迭代 17 卡③ P1 丁——右下角常驻;2026-10-16 调试角标迁全局浮层后本屏只留状态):
|
||||
# 状态角标(右下角常驻;调试角标已迁全局浮层,本屏只留状态):
|
||||
# 17卡③同形制:紧凑钮豁免宽度基准+字号降档。可见性/置灰归组合根 _sync_status_entry
|
||||
#(其他模态开着=置灰不可点——排除式开态查询);信号连接归组合根。
|
||||
# 全局调试浮层角标常驻右下角 16px(main 直接子层、ModalStack 登记外——任何 UI 不遮挡),
|
||||
|
||||
@@ -7,14 +7,14 @@ extends Control
|
||||
|
||||
signal buy_requested(attr: String)
|
||||
signal closed
|
||||
signal attune_toggled(item_id: String) # 同调装配切换意图(迭代 11 第①批 T4——写口唯一归 MetaProgression.toggle_attunement)
|
||||
signal tree_buy_requested(node_id: String) # 天赋树购买意图(迭代 12 T9——写口唯一归 MetaProgression.tree_buy)
|
||||
signal attune_toggled(item_id: String) # 同调装配切换意图(写口唯一归 MetaProgression.toggle_attunement)
|
||||
signal tree_buy_requested(node_id: String) # 天赋树购买意图(写口唯一归 MetaProgression.tree_buy)
|
||||
signal tree_fork_toggled(group: String, side: String) # 双态切换意图(一节点双态——零币切态)
|
||||
signal forge_buy_requested(route_id: String) # 锻造路线购买意图(迭代 23 T6——写口唯一归 MetaProgression.forge_buy_level)
|
||||
signal forge_unlock_requested(route_id: String) # 锻造路线解锁意图(迭代 23 T6——写口唯一归 MetaProgression.forge_buy_unlock)
|
||||
signal forge_select_requested(route_id: String) # 锻造路线选中意图(迭代 23 T6——写口唯一归 MetaProgression.forge_select_weapon/armor)
|
||||
signal forge_buy_requested(route_id: String) # 锻造路线购买意图(写口唯一归 MetaProgression.forge_buy_level)
|
||||
signal forge_unlock_requested(route_id: String) # 锻造路线解锁意图(写口唯一归 MetaProgression.forge_buy_unlock)
|
||||
signal forge_select_requested(route_id: String) # 锻造路线选中意图(写口唯一归 MetaProgression.forge_select_weapon/armor)
|
||||
|
||||
# 方向稿取色(柜台头带深棕/行板暖米;双色墨=亮板配深墨)——值单点收 UiKit(T4 裸色收口批)
|
||||
# 方向稿取色(柜台头带深棕/行板暖米;双色墨=亮板配深墨)——值单点收 UiKit
|
||||
const HEADER_BAND_COL := UiKit.CAMP_HEADER_BAND
|
||||
const ROW_PLATE_COL := UiKit.CAMP_ROW_PLATE
|
||||
|
||||
@@ -23,28 +23,27 @@ var _tree_holder: VBoxContainer # 树淬炼柜台内容(主界面——
|
||||
var _tree_buy_btns: Array[Dictionary] = [] # [{node_id, btn}]——树购买/解锁钮登记面(open/refresh 重建时刷新;探针消费)
|
||||
var _tree_fork_btns: Array[Dictionary] = [] # [{group, side, btn}]——Fork 双态切换钮登记面(探针消费)
|
||||
var _forge_lock_l: Label # 锻造工坊柜台锁定态行(钥匙未点亮)
|
||||
var _forge_workbench: VBoxContainer # 锻造工坊工作间(钥匙点亮后展开——两区六路线行板,迭代 23 T6 实装)
|
||||
var _forge_workbench: VBoxContainer # 锻造工坊工作间(钥匙点亮后展开——两区六路线行板)
|
||||
var _forge_zone_weapon: VBoxContainer # 武器锻造区行板容器(三路线)
|
||||
var _forge_zone_armor: VBoxContainer # 护甲锻造区行板容器(三路线)
|
||||
var _forge_rows: Array[Dictionary] = [] # [{route_id, name_l, lvl_l, status_l, buy_btn, unlock_btn, select_btn, next_price, unlock_price}](open/refresh 重建;探针消费)
|
||||
var _ws_scroll: ScrollContainer # 秘宝匣柜台内容容器(钥匙点亮后可见)
|
||||
var _ws_lock_l: Label # 秘宝匣柜台锁定态行
|
||||
var _workshop_holder: VBoxContainer # 秘宝匣柜台行板(六行+同调装配段+Y 升级行——迭代 11 第①批 T4 结构随迁)
|
||||
var _workshop_holder: VBoxContainer # 秘宝匣柜台行板(六行+同调装配段+Y 升级行)
|
||||
var _workshop_rows: Array[Dictionary] = [] # [{item_id, progress_l, status_l}]
|
||||
var _attune_header_l: Label # 同调装配段(Slots 点亮呈现)
|
||||
var _attune_btns: Array[Dictionary] = [] # [{item_id, btn}]——装配/卸下切换钮(满位禁选随刷)
|
||||
var _attune_nodes: Array[Control] = [] # 段内节点册(open_camp 重建时定向清理,不扰工坊六行)
|
||||
var _refresh_l: Label # Y 刷新升级行(双入口之一——树上 ks_refresh 同账 Q1 裁乙)
|
||||
var _refresh_l: Label # Y 刷新升级行(双入口之一——树上 ks_refresh 同账)
|
||||
var _refresh_btn: Button
|
||||
var _recent_list: Label # 最近冒险(逐行拼接 label——只读账面,随 open_camp 重建)
|
||||
|
||||
|
||||
func _init() -> void:
|
||||
# 底=不透明暖亮档(2026-10-06 用户实测缺陷修——原形制=0.45 半透 dim+满铺石纹遮底,T5 石纹降 0.14
|
||||
# 后底下屏幕透出〔用户实机:对局中开营地见棋盘/战斗提示条〕;demo #v-camp=bg-bright 实底,
|
||||
# 底=不透明暖亮档(半透 dim 会透出底下棋盘/战斗提示条,故须实底;demo #v-camp=bg-bright 实底,
|
||||
# 遮蔽语义由组合根输入锁兜底)
|
||||
var dim := UiKit.modal_dim(self, UiKit.BG_BRIGHT)
|
||||
# 石纹弱质感+暗角(UI 对齐批 T5 2026-10-06——demo .tilebg 0.14/.vignette;缺位不挂即回退平色)
|
||||
# 石纹弱质感+暗角(demo .tilebg 0.14/.vignette;缺位不挂即回退平色)
|
||||
var bd := UiKit.backdrop_tile()
|
||||
if bd != null:
|
||||
dim.add_child(bd)
|
||||
@@ -73,17 +72,17 @@ func _init() -> void:
|
||||
plaque.add_child(plaque_col)
|
||||
var title := UiKit.label("营地 · 天赋树",UiKit.FS_TITLE, true, UiKit.INK_TITLE)
|
||||
plaque_col.add_child(title)
|
||||
var sub := UiKit.label("营地本身即树——钥石逐节点点亮,属性/规则/工作间都在树上",UiKit.FS_HINT, false, UiKit.STONE_DEEP) # 尾裁微批a 2026-10-08——STONE 对铭牌板 3.59:1 越带,换灰石深化 5.98:1
|
||||
var sub := UiKit.label("营地本身即树——钥石逐节点点亮,属性/规则/工作间都在树上",UiKit.FS_HINT, false, UiKit.STONE_DEEP) # STONE 对铭牌板 3.59:1 越带,换灰石深化 5.98:1
|
||||
plaque_col.add_child(sub)
|
||||
var spacer := Control.new()
|
||||
spacer.size_flags_horizontal = Control.SIZE_EXPAND_FILL
|
||||
top.add_child(spacer)
|
||||
# M3(UI 落码批):钥石徽章烛光黄底+深墨字(demo ②)。Q10 甲(2026-10-06):UiKit.keystone_badge 工厂单点
|
||||
# 钥石徽章烛光黄底+深墨字(demo ②)。UiKit.keystone_badge 工厂单点
|
||||
var badge := UiKit.keystone_badge("钥石 0")
|
||||
top.add_child(badge)
|
||||
_balance_label = badge.value_label
|
||||
|
||||
# ── 四柜台并排(A② 裁——迭代 21 T6 程序占位重排,视觉终稿归 HUD 营地视觉批)──
|
||||
# ── 四柜台并排(程序占位重排)──
|
||||
# 宽度不足横向滚动(四柜 300 min 宽 > 窄窗可用宽时横滚兜底;纵向撑满余高)
|
||||
var camp_scroll := ScrollContainer.new()
|
||||
camp_scroll.size_flags_vertical = Control.SIZE_EXPAND_FILL
|
||||
@@ -105,22 +104,22 @@ func _init() -> void:
|
||||
tree_scroll.add_child(_tree_holder)
|
||||
var tree_panel := _counter_panel("天赋树 · 淬炼", "", tree_scroll, 300)
|
||||
tree_panel.size_flags_horizontal = Control.SIZE_EXPAND_FILL
|
||||
tree_panel.size_flags_vertical = Control.SIZE_FILL # 迭代 26 T5③ 四栏等高:任一栏长高时四栏同步拉伸
|
||||
tree_panel.size_flags_vertical = Control.SIZE_FILL # 四栏等高:任一栏长高时四栏同步拉伸
|
||||
mid.add_child(tree_panel)
|
||||
|
||||
# 柜二·锻造工坊(钥匙门——forge_workshop 点亮前锁定态;点亮=工作间两区六路线行板,迭代 23 T6 实装)
|
||||
# 柜二·锻造工坊(钥匙门——forge_workshop 点亮前锁定态;点亮=工作间两区六路线行板)
|
||||
_forge_lock_l = UiKit.label("🔒 锻造工坊未开放\n\n点亮树上「锻造工坊」钥匙节点后展开工作间。\n(钥匙免费、无前置——树上直接点亮)",UiKit.FS_HINT, false, UiKit.STONE_DEEP)
|
||||
_forge_lock_l.size_flags_vertical = Control.SIZE_SHRINK_CENTER # 迭代 26 T5③ 锁文在等高栏内垂直居中
|
||||
_forge_lock_l.size_flags_vertical = Control.SIZE_SHRINK_CENTER # 锁文在等高栏内垂直居中
|
||||
_forge_lock_l.autowrap_mode = TextServer.AUTOWRAP_WORD_SMART
|
||||
_forge_lock_l.custom_minimum_size = Vector2(280, 0)
|
||||
_forge_workbench = VBoxContainer.new()
|
||||
_forge_workbench.add_theme_constant_override("separation", 6)
|
||||
_forge_workbench.visible = false # 默认锁定态(钥匙门未判前零行板——门开才建,迭代 23 T6 占位纪律延续)
|
||||
_forge_workbench.visible = false # 默认锁定态(钥匙门未判前零行板——门开才建)
|
||||
_forge_zone_weapon = VBoxContainer.new()
|
||||
_forge_zone_weapon.add_theme_constant_override("separation", 4)
|
||||
_forge_zone_armor = VBoxContainer.new()
|
||||
_forge_zone_armor.add_theme_constant_override("separation", 4)
|
||||
var wb_wh := UiKit.label("── 武器锻造区 ──",UiKit.FS_HINT, true, UiKit.GOLD_INK) # 尾裁微批a 同款深金墨(对柜台板 5.21:1)
|
||||
var wb_wh := UiKit.label("── 武器锻造区 ──",UiKit.FS_HINT, true, UiKit.GOLD_INK) # 同款深金墨(对柜台板 5.21:1)
|
||||
_forge_workbench.add_child(wb_wh)
|
||||
_forge_workbench.add_child(_forge_zone_weapon)
|
||||
var wb_ah := UiKit.label("── 护甲锻造区 ──",UiKit.FS_HINT, true, UiKit.GOLD_INK)
|
||||
@@ -128,17 +127,17 @@ func _init() -> void:
|
||||
_forge_workbench.add_child(_forge_zone_armor)
|
||||
var forge_holder := VBoxContainer.new()
|
||||
forge_holder.add_theme_constant_override("separation", 6)
|
||||
forge_holder.custom_minimum_size = Vector2(0, 336) # 迭代 26 T5③ 四栏等高(与树/秘宝匣 scroll 336 min 对齐——锁定态仅锁文也撑住栏高)
|
||||
forge_holder.custom_minimum_size = Vector2(0, 336) # 四栏等高(与树/秘宝匣 scroll 336 min 对齐——锁定态仅锁文也撑住栏高)
|
||||
forge_holder.add_child(_forge_lock_l)
|
||||
forge_holder.add_child(_forge_workbench)
|
||||
var forge_panel := _counter_panel("锻造工坊", UiKit.TEX_ICON_FORGE, forge_holder, 300)
|
||||
forge_panel.size_flags_horizontal = Control.SIZE_EXPAND_FILL
|
||||
forge_panel.size_flags_vertical = Control.SIZE_FILL # T5③ 四栏等高:任一栏长高时四栏同步拉伸
|
||||
forge_panel.size_flags_vertical = Control.SIZE_FILL # 四栏等高:任一栏长高时四栏同步拉伸
|
||||
mid.add_child(forge_panel)
|
||||
|
||||
# 柜三·秘宝匣(钥匙门——ks_workshop 点亮前锁定态;点亮=现工坊内容整体迁入:六行+同调装配段+Y 升级行)
|
||||
_ws_lock_l = UiKit.label("🔒 秘宝匣未开放\n\n点亮树上「秘宝匣」钥匙节点后展开。\n(前置=脊柱生命第 1 级)",UiKit.FS_HINT, false, UiKit.STONE_DEEP)
|
||||
_ws_lock_l.size_flags_vertical = Control.SIZE_SHRINK_CENTER # 迭代 26 T5③ 锁文在等高栏内垂直居中
|
||||
_ws_lock_l.size_flags_vertical = Control.SIZE_SHRINK_CENTER # 锁文在等高栏内垂直居中
|
||||
_ws_lock_l.autowrap_mode = TextServer.AUTOWRAP_WORD_SMART
|
||||
_ws_lock_l.custom_minimum_size = Vector2(280, 0)
|
||||
_ws_scroll = ScrollContainer.new()
|
||||
@@ -150,23 +149,23 @@ func _init() -> void:
|
||||
_ws_scroll.add_child(_workshop_holder)
|
||||
var ws_holder := VBoxContainer.new()
|
||||
ws_holder.add_theme_constant_override("separation", 6)
|
||||
ws_holder.custom_minimum_size = Vector2(0, 336) # 迭代 26 T5③ 四栏等高(锁定态 scroll 隐、仅锁文也撑住栏高)
|
||||
ws_holder.custom_minimum_size = Vector2(0, 336) # 四栏等高(锁定态 scroll 隐、仅锁文也撑住栏高)
|
||||
ws_holder.add_child(_ws_lock_l)
|
||||
ws_holder.add_child(_ws_scroll)
|
||||
var ws_panel := _counter_panel("秘宝匣", UiKit.TEX_ICON_WORKSHOP, ws_holder, 300)
|
||||
ws_panel.size_flags_horizontal = Control.SIZE_EXPAND_FILL
|
||||
ws_panel.size_flags_vertical = Control.SIZE_FILL # T5③ 四栏等高:任一栏长高时四栏同步拉伸
|
||||
ws_panel.size_flags_vertical = Control.SIZE_FILL # 四栏等高:任一栏长高时四栏同步拉伸
|
||||
mid.add_child(ws_panel)
|
||||
|
||||
# 柜四·奖励格图鉴(常开独立位——结构位预留,内容排 Alpha)
|
||||
var atlas_holder := VBoxContainer.new()
|
||||
atlas_holder.custom_minimum_size = Vector2(0, 336) # 迭代 26 T5③ 四栏等高(图鉴仅锁文也撑住栏高)
|
||||
atlas_holder.custom_minimum_size = Vector2(0, 336) # 四栏等高(图鉴仅锁文也撑住栏高)
|
||||
var atlas_panel := _counter_panel("奖励格图鉴", UiKit.TEX_ICON_ATLAS, atlas_holder, 300)
|
||||
atlas_panel.size_flags_horizontal = Control.SIZE_EXPAND_FILL
|
||||
atlas_panel.size_flags_vertical = Control.SIZE_FILL # T5③ 四栏等高:任一栏长高时四栏同步拉伸
|
||||
atlas_panel.size_flags_vertical = Control.SIZE_FILL # 四栏等高:任一栏长高时四栏同步拉伸
|
||||
mid.add_child(atlas_panel)
|
||||
var lock_l := UiKit.label(" —— 柜台结构位预留:内容排 Alpha(地图战斗资源差异化批)",UiKit.FS_HINT, false, UiKit.STONE)
|
||||
lock_l.size_flags_vertical = Control.SIZE_SHRINK_CENTER # 迭代 26 T5③ 锁文在等高栏内垂直居中
|
||||
lock_l.size_flags_vertical = Control.SIZE_SHRINK_CENTER # 锁文在等高栏内垂直居中
|
||||
lock_l.autowrap_mode = TextServer.AUTOWRAP_WORD_SMART
|
||||
lock_l.custom_minimum_size = Vector2(280, 0)
|
||||
atlas_holder.add_child(lock_l)
|
||||
@@ -206,7 +205,7 @@ func _counter_panel(title_text: String, icon_path: String, holder: Control, min_
|
||||
head.add_theme_constant_override("separation", 6)
|
||||
band.add_child(head)
|
||||
if icon_path != "":
|
||||
var ic := UiKit.icon_rect(icon_path, 32) # P6 甲②(2026-10-06):柜台头带 64 源 0.5×;缺位 null → 纯文本回退
|
||||
var ic := UiKit.icon_rect(icon_path, 32) # 柜台头带 64 源 0.5×;缺位 null → 纯文本回退
|
||||
if ic != null:
|
||||
head.add_child(ic)
|
||||
var l := UiKit.label(title_text,UiKit.FS_BODY, true, UiKit.WARM)
|
||||
@@ -217,7 +216,7 @@ func _counter_panel(title_text: String, icon_path: String, holder: Control, min_
|
||||
|
||||
# ---- 柜台行构建(open_camp 重建;行结构仿 ShopOverlay 商品卡——亮板双色墨制)----
|
||||
|
||||
# 天赋树段重建(迭代 21 T6——树淬炼柜台主界面:脉分组行板:节点名/等级/价/购买钮 + Fork 双态切换钮;
|
||||
# 天赋树段重建(树淬炼柜台主界面:脉分组行板:节点名/等级/价/购买钮 + Fork 双态切换钮;
|
||||
# 树数据 = 组合根 _camp_data().tree 投影 [{node_id, display_name, vein, kind, level, price, can_buy,
|
||||
# currency}...] + fork_groups [{group, node_id, active_side, owned, can_buy, price, sides:[{side, display_name, effects}]}])
|
||||
func _rebuild_tree(data: Dictionary) -> void:
|
||||
@@ -233,7 +232,7 @@ func _rebuild_tree(data: Dictionary) -> void:
|
||||
var vein := String(row.get("vein", ""))
|
||||
if vein != last_vein:
|
||||
last_vein = vein
|
||||
var vh := UiKit.label(String(vein_names.get(vein, vein)),UiKit.FS_HINT, true, UiKit.GOLD_INK) # 尾裁微批a 2026-10-08——GOLD_DEEP 对柜台板 2.67:1 越带,换深金墨 5.21:1
|
||||
var vh := UiKit.label(String(vein_names.get(vein, vein)),UiKit.FS_HINT, true, UiKit.GOLD_INK) # GOLD_DEEP 对柜台板 2.67:1 越带,换深金墨 5.21:1
|
||||
_tree_holder.add_child(vh)
|
||||
var hb := HBoxContainer.new()
|
||||
hb.add_theme_constant_override("separation", 6)
|
||||
@@ -249,7 +248,7 @@ func _rebuild_tree(data: Dictionary) -> void:
|
||||
hb.add_child(name_l)
|
||||
if String(row.get("kind", "")) == "fork":
|
||||
continue # Fork 行去行板化——由 fork_groups 段承载双态切换钮
|
||||
var btn := UiKit.button("购买 %d 钥石" % int(row.get("price", 0)), 0.0) # 柜台行板紧凑钮豁免宽度基准(树全钥石单币 2026-10-07 裁甲)
|
||||
var btn := UiKit.button("购买 %d 钥石" % int(row.get("price", 0)), 0.0) # 柜台行板紧凑钮豁免宽度基准
|
||||
btn.pressed.connect(func(): tree_buy_requested.emit(String(row.get("node_id", ""))))
|
||||
btn.disabled = not bool(row.get("can_buy", false))
|
||||
hb.add_child(btn)
|
||||
@@ -282,7 +281,7 @@ func _rebuild_tree(data: Dictionary) -> void:
|
||||
_tree_holder.add_child(fb)
|
||||
|
||||
|
||||
# 工作间钥匙门(迭代 21 T6——锻造工坊/秘宝匣柜台锁定态切换:节点点亮=展开、未点亮=锁定行;
|
||||
# 工作间钥匙门(锻造工坊/秘宝匣柜台锁定态切换:节点点亮=展开、未点亮=锁定行;
|
||||
# 图鉴常开无门槛。判据读树投影行 level——零核心访问,纯视图)
|
||||
func _refresh_key_gates(data: Dictionary) -> void:
|
||||
var lvl_by_id := {}
|
||||
@@ -296,7 +295,7 @@ func _refresh_key_gates(data: Dictionary) -> void:
|
||||
_ws_scroll.visible = ws_open
|
||||
|
||||
|
||||
# 锻造工坊行板重建(迭代 23 T6——两区六路线;open_camp/refresh_camp 调用。行结构仿秘宝匣行板:
|
||||
# 锻造工坊行板重建(两区六路线;open_camp/refresh_camp 调用。行结构仿秘宝匣行板:
|
||||
# 件名+等级+解锁态+购买/解锁/选中钮。UI 只发意图信号〔契约 9——零核心写调用、零 MetaProgression
|
||||
# 持有〕;钮禁用=常态防线(余额/未解锁/满级/已选中),兜底拒绝可见归 presenter pattern 1)
|
||||
func _rebuild_forge(data: Dictionary) -> void:
|
||||
@@ -305,7 +304,7 @@ func _rebuild_forge(data: Dictionary) -> void:
|
||||
c.queue_free()
|
||||
_forge_rows.clear()
|
||||
if not _forge_workbench.visible:
|
||||
return # 锁定态(钥匙未点亮):零行板零购买路径——占位纪律延续(2026-10-11 用户令)
|
||||
return # 锁定态(钥匙未点亮):零行板零购买路径——占位纪律延续
|
||||
var bal := int(data.get("balance", 0))
|
||||
for row in data.get("forge", []):
|
||||
var rid := String(row.get("route_id", ""))
|
||||
@@ -376,7 +375,7 @@ func _rebuild_forge(data: Dictionary) -> void:
|
||||
# 数据 = 组合根 _camp_data() 投影)
|
||||
func open_camp(data: Dictionary) -> void:
|
||||
_rebuild_workshop(data) # 段尾链 _rebuild_attune(同调装配段+Y 升级行随秘宝匣柜台内容)
|
||||
_rebuild_forge(data) # 迭代 23 T6——锻造工坊两区六路线行板
|
||||
_rebuild_forge(data) # 锻造工坊两区六路线行板
|
||||
_rebuild_recent(data)
|
||||
refresh_camp(data)
|
||||
visible = true
|
||||
@@ -389,7 +388,7 @@ func refresh_camp(data: Dictionary) -> void:
|
||||
_balance_label.text = "钥石 %d" % balance
|
||||
_rebuild_tree(data)
|
||||
_refresh_key_gates(data)
|
||||
_rebuild_forge(data) # 迭代 23 T6——行板随刷重建(购买/解锁/选中后等级/价/禁用态同步)
|
||||
_rebuild_forge(data) # 行板随刷重建(购买/解锁/选中后等级/价/禁用态同步)
|
||||
var ws_by_id := {}
|
||||
for row in data.get("workshop", []):
|
||||
ws_by_id[String(row.get("item_id", ""))] = row
|
||||
@@ -398,8 +397,7 @@ func refresh_camp(data: Dictionary) -> void:
|
||||
if row.is_empty():
|
||||
continue
|
||||
var unlocked := bool(row.get("unlocked", false))
|
||||
# 秘宝匣进度列(迭代 22 T2——解锁混合制三形态:钥石购买价/条件进度拼接/已入手;
|
||||
# 旧「累计通关 N/M 次」单一渠道文案随工坊行族退役)
|
||||
# 秘宝匣进度列(解锁混合制三形态:钥石购买价/条件进度拼接/已入手)
|
||||
var prog_text := ""
|
||||
var mode := String(row.get("mode", ""))
|
||||
if unlocked:
|
||||
@@ -414,19 +412,19 @@ func refresh_camp(data: Dictionary) -> void:
|
||||
parts.append("%s %d/%d" % [lbl, int(req.get("progress", 0)), int(req.get("threshold", 0))])
|
||||
prog_text = "、".join(parts)
|
||||
card.progress_l.text = prog_text
|
||||
# 状态色随卡⑭ 亮板双色墨制(深墨族替代暖白族——亮板上暖白不可读);失义/退役行随新目录退役
|
||||
# 状态色用亮板双色墨制(深墨族替代暖白族——亮板上暖白不可读)
|
||||
var st_text := "未解锁"
|
||||
var st_col: Color = UiKit.STONE
|
||||
if unlocked:
|
||||
st_text = "已解锁"
|
||||
st_col = UiKit.GOLD_INK # 尾裁微批a 2026-10-08——GOLD_DEEP 对行板 3.00:1 越带,换深金墨 5.84:1
|
||||
st_col = UiKit.GOLD_INK # GOLD_DEEP 对行板 3.00:1 越带,换深金墨 5.84:1
|
||||
card.status_l.text = st_text
|
||||
card.status_l.add_theme_color_override("font_color", st_col)
|
||||
card.status_l.tooltip_text = st_text # 截断兜底(状态列 EXPAND_FILL 吸余宽——悬停看全文)
|
||||
# 购买钮余额联动(迭代 22 秘宝批 T4——禁用态=余额不足,常态防线;pattern 1 兜底拒绝可见归 presenter)
|
||||
# 购买钮余额联动(禁用态=余额不足,常态防线;pattern 1 兜底拒绝可见归 presenter)
|
||||
if card.get("buy_btn") != null:
|
||||
card.buy_btn.disabled = balance < int(card.get("price", 0))
|
||||
# 同调装配段状态就地刷(迭代 11 第①批 T4——Slots 点亮/满位禁选/Y 升级行余额联动)
|
||||
# 同调装配段状态就地刷(Slots 点亮/满位禁选/Y 升级行余额联动)
|
||||
var att: Dictionary[Variant, Variant] = data.get("attune", {})
|
||||
if _attune_header_l != null:
|
||||
var slots_n := int(att.get("slots", 0))
|
||||
@@ -438,7 +436,7 @@ func refresh_camp(data: Dictionary) -> void:
|
||||
for card2 in _attune_btns:
|
||||
var on: bool = eq_set.has(String(card2.item_id))
|
||||
card2.btn.text = "卸下" if on else "装配"
|
||||
# 满位禁选+未解锁禁装(迭代 22 T4——已装件恒可卸下;unlocked 快照随 rebuild 刷新)
|
||||
# 满位禁选+未解锁禁装(已装件恒可卸下;unlocked 快照随 rebuild 刷新)
|
||||
card2.btn.disabled = (not on and eq.size() >= slots_n) or (not on and not bool(card2.get("unlocked", false)))
|
||||
if _refresh_l != null:
|
||||
var rf: Dictionary[Variant, Variant] = att.get("refresh", {})
|
||||
@@ -469,18 +467,18 @@ func _rebuild_workshop(data: Dictionary) -> void:
|
||||
name_l.text_overrun_behavior = TextServer.OVERRUN_TRIM_ELLIPSIS
|
||||
name_l.tooltip_text = String(row.get("desc", "")) # 件效果摘要悬停可见(分档呈现契约——槽位档位=名后缀)
|
||||
line.add_child(name_l)
|
||||
var progress_l := UiKit.label("",UiKit.FS_HINT, false, UiKit.STONE_DEEP) # 尾裁微批a 2026-10-08——同行价格墨同案
|
||||
var progress_l := UiKit.label("",UiKit.FS_HINT, false, UiKit.STONE_DEEP) # 同行价格墨同案
|
||||
progress_l.custom_minimum_size = Vector2(150, 0)
|
||||
progress_l.clip_text = true # 营地溢出修——文字全宽不撑柜
|
||||
progress_l.text_overrun_behavior = TextServer.OVERRUN_TRIM_ELLIPSIS
|
||||
line.add_child(progress_l)
|
||||
# 状态色随卡⑭ 亮板双色墨制(深墨族替代暖白族)——色随刷新态条件赋值,非机械两行式(T4 收口保留)
|
||||
# 状态色用亮板双色墨制(深墨族替代暖白族)——色随刷新态条件赋值,非机械两行式
|
||||
var status_l := UiKit.label("",UiKit.FS_HINT, true, UiKit.STONE)
|
||||
status_l.clip_text = true # 同上——「已解锁 · 能力封存中」不撑柜(余宽吸收者)
|
||||
status_l.text_overrun_behavior = TextServer.OVERRUN_TRIM_ELLIPSIS
|
||||
status_l.size_flags_horizontal = Control.SIZE_EXPAND_FILL
|
||||
line.add_child(status_l)
|
||||
# 钥石购买钮(迭代 22 秘宝批 T4——购买件且未解锁时在列;写口唯一=buy_treasure_unlock,
|
||||
# 钥石购买钮(购买件且未解锁时在列;写口唯一=buy_treasure_unlock,
|
||||
# 经 buy_requested(item_id) 信号走 presenter 分支;禁用态=余额不足)
|
||||
var buy_btn: Button = null
|
||||
if not bool(row.get("unlocked", false)) and String(row.get("mode", "")) == "keystone":
|
||||
@@ -492,7 +490,7 @@ func _rebuild_workshop(data: Dictionary) -> void:
|
||||
_rebuild_attune(data)
|
||||
|
||||
|
||||
# 同调装配段重建(迭代 11 第①批 T4——秘宝匣柜台尾部段〔迭代 21 T6 随柜台迁入〕;Slots 点亮+
|
||||
# 同调装配段重建(秘宝匣柜台尾部段;Slots 点亮+
|
||||
# 九件行板+Y 升级行,程序占位面)
|
||||
func _rebuild_attune(data: Dictionary) -> void:
|
||||
var att: Dictionary[Variant, Variant] = data.get("attune", {})
|
||||
@@ -511,7 +509,7 @@ func _rebuild_attune(data: Dictionary) -> void:
|
||||
var form_l := UiKit.label("被动" if String(row.get("form", "")) == "passive" else "主动·充能",UiKit.FS_CAPTION, false, UiKit.STONE)
|
||||
form_l.custom_minimum_size = Vector2(58, 0)
|
||||
line.add_child(form_l)
|
||||
# 档位标签(迭代 22 秘宝批 T4——槽位档位显示:件档=普通/优秀/稀有,装入须件档≤格档契约 2 的呈现半边)
|
||||
# 档位标签(槽位档位显示:件档=普通/优秀/稀有,装入须件档≤格档契约 2 的呈现半边)
|
||||
var tier_l := UiKit.label(String(UiKit.TREASURE_TIER_LABELS.get(String(row.get("tier", "")), "—")),UiKit.FS_CAPTION, false, UiKit.STONE)
|
||||
tier_l.custom_minimum_size = Vector2(36, 0)
|
||||
line.add_child(tier_l)
|
||||
|
||||
@@ -44,7 +44,7 @@ func _init() -> void:
|
||||
_boss_first_label = _row(box, "Boss 首杀")
|
||||
_boss_kills_label = _row(box, "Boss 总击杀")
|
||||
|
||||
# 组二·资源(钥石余额——终身计数域同段只读投影;账号成长态概览三投影已删——2026-10-12 用户裁)
|
||||
# 组二·资源(钥石余额——终身计数域同段只读投影;账号成长态概览三投影已退役)
|
||||
_section_head(box, "资源")
|
||||
var bal_row := HBoxContainer.new()
|
||||
bal_row.add_theme_constant_override("separation", 8)
|
||||
|
||||
@@ -37,10 +37,9 @@ func open(text: String, confirm_label: String, cancel_label: String = "取消")
|
||||
_text.text = text
|
||||
_confirm_btn.text = confirm_label
|
||||
_cancel_btn.text = cancel_label
|
||||
# z 序纪律(T3 收口批 2026-10-02):树序由组合根构建序保证——本弹窗在 main._build_ui 尾部
|
||||
# add_child(开始层/过场层之后 = 子级最末,登记序 = 树序 = 绘制序恒最上;原诊断转储角落钮随
|
||||
# 迭代 26 T5b 退役迁调试浮窗,此处不再经其之后),
|
||||
# 「每次打开 move_child 运行时自提」创可贴随之删除。
|
||||
# z 序纪律:树序由组合根构建序保证——本弹窗在 main._build_ui 尾部
|
||||
# add_child(开始层/过场层之后 = 子级最末,登记序 = 树序 = 绘制序恒最上)
|
||||
|
||||
visible = true
|
||||
|
||||
|
||||
|
||||
@@ -25,7 +25,7 @@ var _bcards: Array[Variant] = [] # M7 bcard 引用列(sele
|
||||
|
||||
|
||||
func _init() -> void:
|
||||
# T7(UI 设计对齐批 2026-10-06):全屏页形态(demo ⑥ #v-bonus——与商店 T6 同壳 page_shell,
|
||||
# 全屏页形态(demo ⑥ #v-bonus——与商店 T6 同壳 page_shell,
|
||||
# 根底暗档)——56px 通栏顶条(标题+刷新图标钮/角标)+ 居中入账信息行(GP 汇总行承载)+
|
||||
# bcard 三竖卡(D1 232×352)+ 底部确认主钮与免费选择注记。差异白名单=免费选择/✓章/无价签。
|
||||
# 状态机不变:pick→result。M7 同构裁决遗产保留。
|
||||
@@ -94,7 +94,7 @@ func open_pick(header: String, cards: Array) -> void:
|
||||
var title := lines[0]
|
||||
var desc := "\n".join(lines.slice(1)) if lines.size() > 1 else ""
|
||||
var idl := cid.to_lower()
|
||||
var band := UiKit.tg_band(idl) # 类型色带单点(N4c 2026-10-06——TG_* 五族同源判定;aoe/blast 归 TG_AOE)
|
||||
var band := UiKit.tg_band(idl) # 类型色带单点(TG_* 五族同源判定;aoe/blast 归 TG_AOE)
|
||||
# 类型脚注(T7 判官 checkpoint:demo .bcard .src 底部类型行——实机缺=卡下半空底;词=demo 同款)
|
||||
var card := UiKit.bcard(title, desc, band, _icon_for_id(idl), "", _type_label(idl))
|
||||
card.gui_input.connect(func(ev: InputEvent) -> void:
|
||||
@@ -112,7 +112,7 @@ func open_pick(header: String, cards: Array) -> void:
|
||||
visible = true
|
||||
|
||||
|
||||
# 三选一刷新呈现投喂(2026-10-06 用户令对齐商店 MC-1 同构——free_left=X/Y 免费位余量〔基础 0〕,
|
||||
# 三选一刷新呈现投喂(对齐商店 MC-1 同构——free_left=X/Y 免费位余量〔基础 0〕,
|
||||
# 免费位用尽转 GP 付费档:gp_cost=本次 GP 刷新价、can_afford=冒险钱包可付;钮常驻选择态,两源皆穷=禁用不隐藏)
|
||||
func set_refresh_display(free_left: int, gp_cost: int, can_afford: bool) -> void:
|
||||
_refresh_allowance = maxi(0, int(free_left))
|
||||
|
||||
@@ -7,10 +7,10 @@ extends Control
|
||||
signal apply_requested(atk_pct: int, hp_pct: int) # 应用并关闭——加成落表归组合根单一应用路径
|
||||
signal simulate_requested(count: int) # 批量模拟编排归组合根(表基线复位 + 单路径施加)
|
||||
signal debug_levels_requested # 两层调试对局入口(编排归 debug_session.gd)
|
||||
signal diagnose_requested # 诊断转储入口(迭代 26 T5b——原主画面角落钮迁入;卡死取证编排归组合根)
|
||||
signal diagnose_requested # 诊断转储入口(卡死取证编排归组合根)
|
||||
signal closed
|
||||
|
||||
var seed_edit: LineEdit # 组合根 _seed_value 唯一读取面(原开始覆盖层种子行迁入)
|
||||
var seed_edit: LineEdit # 组合根 _seed_value 唯一读取面
|
||||
|
||||
var _db
|
||||
var _base_atk := 0
|
||||
@@ -22,11 +22,11 @@ var _hp_val: Label
|
||||
var _eff: Label
|
||||
var _sim_count: SpinBox
|
||||
var _sim_btn: Button
|
||||
var _dbg_btn: Button # 两层调试入口钮(迭代 18 卡⑦——对局中禁用承载面)
|
||||
var _dbg_btn: Button # 两层调试入口钮(对局中禁用承载面)
|
||||
var _sim_out: RichTextLabel
|
||||
var _sim_running := false # 批量模拟进行中(is_running 判据——与对局中禁用态分离防互扰)
|
||||
var _ctx_locked := false # 对局中上下文锁定(组合根 _sync_debug_overlay 喂入——批量模拟/两层调试入口禁用)
|
||||
var _run_labels := {} # 当局数据六项标签(P4 甲——只读投影,组合根 update_run_data 喂入)
|
||||
var _run_labels := {} # 当局数据六项标签(只读投影,组合根 update_run_data 喂入)
|
||||
var _run_data := {} # 最近一次投喂快照(探针值对面 run_data_probe)
|
||||
|
||||
|
||||
@@ -69,7 +69,7 @@ func set_sim_count(n: int) -> void:
|
||||
|
||||
|
||||
func is_running() -> bool:
|
||||
return _sim_running # 批量模拟进行中(迭代 18 卡⑦——与对局中禁用态分离,置灰 ≠ 在跑)
|
||||
return _sim_running # 批量模拟进行中(与对局中禁用态分离,置灰 ≠ 在跑)
|
||||
|
||||
|
||||
func set_sim_running(running: bool) -> void:
|
||||
@@ -77,7 +77,7 @@ func set_sim_running(running: bool) -> void:
|
||||
_sync_sim_button()
|
||||
|
||||
|
||||
# 对局中上下文锁定(迭代 18 卡⑦ T2——组合根 _sync_debug_overlay 喂入):批量模拟与两层调试入口
|
||||
# 对局中上下文锁定(组合根 _sync_debug_overlay 喂入):批量模拟与两层调试入口
|
||||
# 在活跃对局并存态不开放(长程异步模拟/并发对局编排与活跃结算并存态破坏状态机)
|
||||
func set_context_locked(locked: bool) -> void:
|
||||
_ctx_locked = locked
|
||||
@@ -85,7 +85,7 @@ func set_context_locked(locked: bool) -> void:
|
||||
_sync_sim_button()
|
||||
|
||||
|
||||
# 当局数据只读投影喂入(T3 P4 甲六项——组合根 _debug_run_data 构造;纯标签重查零动效)
|
||||
# 当局数据只读投影喂入(六项——组合根 _debug_run_data 构造;纯标签重查零动效)
|
||||
func update_run_data(data: Dictionary) -> void:
|
||||
_run_data = data.duplicate()
|
||||
for key in _run_labels:
|
||||
@@ -170,9 +170,9 @@ func _build() -> void:
|
||||
list.add_theme_constant_override("separation", 8)
|
||||
scroll.add_child(list)
|
||||
|
||||
# 区块·当局数据(迭代 18 卡⑦ T3 P4 甲——六项只读投影:种子=本局种子唯一查看位〔adventure
|
||||
# 区块·当局数据(六项只读投影:种子=本局种子唯一查看位〔adventure
|
||||
# .master_seed 现值,非 _seed_value() 重算〕/关卡/节点/HP/GP/钥石;数据由组合根开窗与模态开合
|
||||
# 时 update_run_data 喂入;纯标签零信号连接零动效——17卡③只读契约)
|
||||
# 时 update_run_data 喂入;纯标签零信号连接零动效)
|
||||
var run_head := UiKit.label("当局数据(只读)",UiKit.FS_HINT, true, UiKit.WARM)
|
||||
list.add_child(run_head)
|
||||
var run_grid := GridContainer.new()
|
||||
@@ -189,18 +189,18 @@ func _build() -> void:
|
||||
run_grid.add_child(val)
|
||||
list.add_child(HSeparator.new())
|
||||
|
||||
# 区块⓪胜利演出风格(P-C5 两套并存——纹章拼合[默认]/尘金粒子;会话级不落盘)
|
||||
# 区块⓪胜利演出风格(两套并存——纹章拼合[默认]/尘金粒子;会话级不落盘)
|
||||
var fx_row := HBoxContainer.new()
|
||||
fx_row.add_theme_constant_override("separation", 10)
|
||||
list.add_child(fx_row)
|
||||
fx_row.add_child(UiKit.label("胜利演出",UiKit.FS_HINT, false, UiKit.WARM))
|
||||
var fx_btn := UiKit.button("纹章拼合" if UiKit.victory_fx_style == "crest" else "尘金粒子")
|
||||
fx_btn.tooltip_text = "两套演出并存——点击切换(会话级,不落盘)"
|
||||
fx_btn.pressed.connect(_on_fx_toggle.bind(fx_btn)) # 迭代 18 卡⑦——lambda 转命名方法(只读巡检白名单方法名精确可判)
|
||||
fx_btn.pressed.connect(_on_fx_toggle.bind(fx_btn)) # lambda 转命名方法(只读巡检白名单方法名精确可判)
|
||||
fx_row.add_child(fx_btn)
|
||||
|
||||
# 区块①随机种子(原开始覆盖层种子行迁入——种子三分判定归组合根 _seed_value 单点):
|
||||
# 默认空框(迭代 18 卡⑦ C1——空=随全局规则:首局恒 42/非首局随机;显式填入全场景优先)
|
||||
# 区块①随机种子(种子三分判定归组合根 _seed_value 单点):
|
||||
# 默认空框(空=随全局规则:首局恒 42/非首局随机;显式填入全场景优先)
|
||||
var seed_row := HBoxContainer.new()
|
||||
list.add_child(seed_row)
|
||||
seed_row.add_child(UiKit.label("随机种子",UiKit.FS_BODY, false))
|
||||
@@ -211,7 +211,7 @@ func _build() -> void:
|
||||
seed_row.add_child(seed_edit)
|
||||
list.add_child(HSeparator.new())
|
||||
|
||||
# 区块②角色数值调整板(原 TunePanel 内容原样吸收)
|
||||
# 区块②角色数值调整板
|
||||
var atk_row := _slider_row(list, "攻击加成")
|
||||
_atk = atk_row.slider
|
||||
_atk_val = atk_row.value_label
|
||||
@@ -252,14 +252,13 @@ func _build() -> void:
|
||||
list.add_child(_sim_out)
|
||||
list.add_child(HSeparator.new())
|
||||
|
||||
# 区块④两层调试对局入口(原开始覆盖层调试按钮迁入——HP 关内延续;迭代 18 卡⑦——对局中禁用)
|
||||
# 区块④两层调试对局入口(HP 关内延续;对局中禁用)
|
||||
_dbg_btn = UiKit.button("调试 · 连续关卡两层(HP 关内延续)")
|
||||
_dbg_btn.pressed.connect(_emit_debug_levels)
|
||||
list.add_child(_dbg_btn)
|
||||
|
||||
# 区块⑤诊断转储(迭代 26 T5b——原主画面左下角「诊断转储」常驻钮退役迁入:grilling Q8 裁甲,
|
||||
# 承迭代 18 卡⑦「主菜单三散件收一」纪律;dump_diagnosis 卡死取证功能保留〔删除案否〕,
|
||||
# 门控隐藏案否——本浮窗本就可全局可开,双轨无意义。纯视图边界:转储编排经信号外发归组合根)
|
||||
# 区块⑤诊断转储:dump_diagnosis 卡死取证功能保留;不设门控隐藏——本浮窗本就可全局可开,双轨无意义。
|
||||
# 纯视图边界:转储编排经信号外发归组合根。
|
||||
var diag_row := HBoxContainer.new()
|
||||
diag_row.add_theme_constant_override("separation", 10)
|
||||
list.add_child(diag_row)
|
||||
@@ -282,7 +281,7 @@ func _slider_row(box: VBoxContainer, title: String) -> Dictionary:
|
||||
s.min_value = 0
|
||||
s.max_value = 300
|
||||
s.step = 5
|
||||
s.custom_minimum_size = Vector2(320, 24) # 皮肤走全局主题(迭代 25:与设置屏音量滑轨同一构建方式,消双轨)
|
||||
s.custom_minimum_size = Vector2(320, 24) # 皮肤走全局主题(与设置屏音量滑轨同一构建方式,消双轨)
|
||||
row.add_child(s)
|
||||
var v := UiKit.label("+0%",UiKit.FS_BODY, true)
|
||||
v.custom_minimum_size = Vector2(70, 0)
|
||||
@@ -318,7 +317,7 @@ func _on_slider_value_changed(_value: float) -> void:
|
||||
_sync_labels()
|
||||
|
||||
|
||||
# 胜利演出切换(原 _build 内 lambda——迭代 18 卡⑦ 转命名方法,只读巡检白名单承载)
|
||||
# 胜利演出切换(命名方法,只读巡检白名单承载)
|
||||
func _on_fx_toggle(btn: Button) -> void:
|
||||
UiKit.victory_fx_style = "dust" if UiKit.victory_fx_style == "crest" else "crest"
|
||||
btn.text = "纹章拼合" if UiKit.victory_fx_style == "crest" else "尘金粒子"
|
||||
@@ -8,27 +8,27 @@ const COL_INK := UiKit.INK
|
||||
|
||||
var _atk_label: Label
|
||||
var _armor_label: Label
|
||||
signal treasure_slot_clicked(handle: int) # 迭代 7 T4——槽点击(组合根消费:AP 类即用/空间类进选中态)
|
||||
var item_slots: HBoxContainer # 秘宝槽(迭代 7 T4 接线——库存条常读+两步交互第一步)
|
||||
signal treasure_slot_clicked(handle: int) # 槽点击(组合根消费:AP 类即用/空间类进选中态)
|
||||
var item_slots: HBoxContainer # 秘宝槽(库存条常读+两步交互第一步)
|
||||
var _slot_panels: Array[Variant] = [] # 槽 PanelContainer 引用(选中高亮/置灰重刷)
|
||||
var _selected_handle := 0 # 选中态(0=无;组合根回写维持单源)
|
||||
var _slot_handles: Array[Variant] = [] # 槽位→handle 映射(set_treasure_inventory 刷新)
|
||||
var _curio_section: VBoxContainer # 特性(预留——空态隐藏,接线归大奖励/锻造呈现批)
|
||||
var _curio_section: VBoxContainer # 特性(预留——空态隐藏)
|
||||
var _curio_list: VBoxContainer
|
||||
var _feat_section: VBoxContainer # 特性持有段(迭代 12 呈现补齐——空态隐藏)
|
||||
var _feat_section: VBoxContainer # 特性持有段(空态隐藏)
|
||||
var _feat_list: VBoxContainer
|
||||
var _board_variant_section: VBoxContainer # 地形变异常驻角标(全局作用域变异首发批——空态隐藏)
|
||||
var _board_variant_section: VBoxContainer # 地形变异常驻角标(空态隐藏)
|
||||
var _board_variant_list: VBoxContainer
|
||||
|
||||
|
||||
func _init() -> void:
|
||||
custom_minimum_size = Vector2(240, 0)
|
||||
clip_contents = true
|
||||
# 主面板穿石板戏服(样张 v2 冻结批 T2——纹理本色;数据框仍素面 §7.1;缺位回退 flat)
|
||||
# 主面板穿石板戏服(纹理本色;数据框仍素面 §7.1;缺位回退 flat)
|
||||
var sb := UiKit.panel_skinned(Vector4(8, 6, 8, 6))
|
||||
add_theme_stylebox_override("panel", sb)
|
||||
var col := VBoxContainer.new()
|
||||
col.add_theme_constant_override("separation", 14) # 布局充实(2026-09-26 反馈③:区段间距放大)
|
||||
col.add_theme_constant_override("separation", 14) # 布局充实(区段间距放大)
|
||||
add_child(col)
|
||||
|
||||
col.add_child(UiKit.label("探险者",UiKit.FS_SUB, true))
|
||||
@@ -46,7 +46,7 @@ func _init() -> void:
|
||||
data_col.add_child(_armor_label)
|
||||
|
||||
# 秘宝槽(预留):随身战斗用道具的唯一展示面——空槽占位承诺格子在此,
|
||||
# 不预设交互(挖掘期指向交互归 E 区既有裁决);终稿样式/槽位数归秘宝批
|
||||
# 不预设交互(挖掘期指向交互归 E 区既有裁决)
|
||||
col.add_child(UiKit.label("秘宝",UiKit.FS_BODY, true))
|
||||
item_slots = HBoxContainer.new()
|
||||
item_slots.add_theme_constant_override("separation", 10)
|
||||
@@ -64,10 +64,8 @@ func _init() -> void:
|
||||
item_slots.add_child(slot)
|
||||
_slot_panels.append(slot)
|
||||
|
||||
# 特性持有段(迭代 12 呈现补齐 2026-10-04——三选一特性兑付后常驻可读:逐件行「件名 ×N」
|
||||
# 〔2026-10-12 整改换代,原逐流派聚合行退役〕;空态整节隐藏。用户试玩反馈「局内奖励选了
|
||||
# 什么看不到」缺口承载;2026-10-04 二次反馈上移——
|
||||
# 构筑核心信息优先级高于临时强化,且防 C 区下部裁切不可见)
|
||||
# 特性持有段(三选一特性兑付后常驻可读:逐件行「件名 ×N」;空态整节隐藏。
|
||||
# 上移承载——构筑核心信息优先级高于临时强化,且防 C 区下部裁切不可见)
|
||||
_feat_section = VBoxContainer.new()
|
||||
_feat_section.add_theme_constant_override("separation", 4)
|
||||
_feat_section.visible = false
|
||||
@@ -76,8 +74,8 @@ func _init() -> void:
|
||||
_feat_list = VBoxContainer.new()
|
||||
_feat_list.add_theme_constant_override("separation", 2)
|
||||
_feat_section.add_child(_feat_list)
|
||||
# 地形变异常驻角标(全局作用域变异首发批 2026-10-04——Boss 图整图型战斗全程可见防中局遗忘;
|
||||
# 空态整节隐藏=特性段先例;摆位/文案 provisional 归 HUD 框架批与本地化批)
|
||||
# 地形变异常驻角标(Boss 图整图型战斗全程可见防中局遗忘;
|
||||
# 空态整节隐藏=特性段先例;摆位/文案 provisional)
|
||||
_board_variant_section = VBoxContainer.new()
|
||||
_board_variant_section.add_theme_constant_override("separation", 4)
|
||||
_board_variant_section.visible = false
|
||||
@@ -104,11 +102,11 @@ func set_attack(atk: int, mod_text: String) -> void:
|
||||
|
||||
|
||||
func set_armor(ac: int) -> void:
|
||||
# 护甲 AC(2026-09-26 二轮拍板补入)——我方 AC 唯一展示面;掷点判定轴受击侧可读性
|
||||
# 护甲 AC——我方 AC 唯一展示面;掷点判定轴受击侧可读性
|
||||
_armor_label.text = "护甲 AC %d" % ac
|
||||
|
||||
|
||||
# 特性持有段(迭代 12 呈现补齐):lines = 逐件文案行(「疼痛记忆 ×2」式——2026-10-12 整改换代)——空数组 = 整节隐藏
|
||||
# 特性持有段:lines = 逐件文案行(「疼痛记忆 ×2」式)——空数组 = 整节隐藏
|
||||
func set_feat_holdings(lines: Array) -> void:
|
||||
for c2 in _feat_list.get_children():
|
||||
_feat_list.remove_child(c2)
|
||||
@@ -119,7 +117,7 @@ func set_feat_holdings(lines: Array) -> void:
|
||||
_feat_section.visible = not lines.is_empty()
|
||||
|
||||
|
||||
# 地形变异常驻角标(首发批 2026-10-04):lines = 文案行(板级变异 display_name+体感短语)——空数组 = 整节隐藏
|
||||
# 地形变异常驻角标:lines = 文案行(板级变异 display_name+体感短语)——空数组 = 整节隐藏
|
||||
func set_board_variants(lines: Array) -> void:
|
||||
for c3 in _board_variant_list.get_children():
|
||||
_board_variant_list.remove_child(c3)
|
||||
@@ -136,7 +134,7 @@ func set_level_bonuses(lines: Array) -> void:
|
||||
_curio_list.remove_child(c)
|
||||
c.queue_free()
|
||||
for line in lines:
|
||||
var l := UiKit.label(String(line),UiKit.FS_BODY, false, COL_INK) # 字号下限(v3.5 批 B3——正文 ≥16)
|
||||
var l := UiKit.label(String(line),UiKit.FS_BODY, false, COL_INK) # 字号下限(正文 ≥16)
|
||||
_curio_list.add_child(l)
|
||||
_curio_section.visible = not lines.is_empty()
|
||||
|
||||
@@ -150,14 +148,14 @@ func armor_text() -> String:
|
||||
|
||||
|
||||
func _data_line() -> Label:
|
||||
# 对比度修复(v3.5 批 B3 2026-09-30——非粗体 label 默认浅灰对米色数据框仅 1.31:1,
|
||||
# 实测=ui-redesign 对比报告 §一):显式主墨(对 PANEL_DATA ≈10:1)
|
||||
# 非粗体 label 默认浅灰对米色数据框仅 1.31:1(实测=ui-redesign 对比报告 §一):
|
||||
# 显式主墨(对 PANEL_DATA ≈10:1)
|
||||
var l := UiKit.label("",UiKit.FS_BODY, false, UiKit.INK)
|
||||
l.clip_text = true
|
||||
l.text_overrun_behavior = TextServer.OVERRUN_TRIM_ELLIPSIS
|
||||
return l
|
||||
|
||||
# ---- 秘宝库存条(迭代 7 T4——C 区四槽常读+两步交互第一步)----
|
||||
# ---- 秘宝库存条(C 区四槽常读+两步交互第一步)----
|
||||
# 投影行 = {handle, glyph, name, used, usable}(组合根组装:字形=道具名首字;数据源 runner 级库存只读面)。
|
||||
# 空槽=虚位;used=暗态「已用」保留占位(Used 不可逆——受理序展示语义);不可用=置灰(板级零信息);
|
||||
# selected=金框高亮(两步交互第一步的可见态)。tooltip=全名+维度语义。
|
||||
@@ -179,7 +177,7 @@ func set_treasure_inventory(items: Array, selected_handle: int) -> void:
|
||||
var used := bool(rec.get("used", false))
|
||||
var usable := bool(rec.get("usable", true))
|
||||
var selected := selected_handle != 0 and int(rec.get("handle", 0)) == selected_handle
|
||||
# M8(UI 落码批):槽内容=手绘像素图标(demo ⑦;glyph 字符→图标键映射,未映射/used/不可用回退字形+条件墨色——T4 分支保留)
|
||||
# 槽内容=手绘像素图标(demo ⑦;glyph 字符→图标键映射,未映射/used/不可用回退字形+条件墨色)
|
||||
var glyph_ch := String(rec.get("glyph", "?"))
|
||||
var icon_map := {"爆": UiKit.TEX_ICON_BLAST, "医": UiKit.TEX_ICON_MEDKIT,
|
||||
"铲": UiKit.TEX_ICON_SHOVEL, "袋": UiKit.TEX_ICON_COINBAG, "钥": UiKit.TEX_ICON_KEYSHARD,
|
||||
@@ -204,17 +202,17 @@ func set_treasure_inventory(items: Array, selected_handle: int) -> void:
|
||||
glyph.set_anchors_preset(Control.PRESET_FULL_RECT)
|
||||
glyph.mouse_filter = Control.MOUSE_FILTER_IGNORE
|
||||
if used:
|
||||
# 「已用」占位墨=灰石深化(UI 设计对齐批 N1-5 2026-10-06——旧 STONE_FAINT 对 PANEL_DATA 槽牌 2.7:1;
|
||||
# STONE_DEEP = 5.6:1;语义分档保持双墨,Q6 乙不新增通道)
|
||||
# 「已用」占位墨=灰石深化(STONE_FAINT 对 PANEL_DATA 槽牌 2.7:1;
|
||||
# STONE_DEEP = 5.6:1;语义分档保持双墨)
|
||||
glyph.add_theme_color_override("font_color", UiKit.STONE_DEEP)
|
||||
elif not usable:
|
||||
# 「不可用」置灰墨=标签深墨(N1-6——旧 STONE 对槽牌 3.4:1;INK_LABEL = 10.4:1,虚位/失效更强置灰语义)
|
||||
# 「不可用」置灰墨=标签深墨(STONE 对槽牌 3.4:1;INK_LABEL = 10.4:1,虚位/失效更强置灰语义)
|
||||
glyph.add_theme_color_override("font_color", UiKit.INK_LABEL)
|
||||
slot.add_child(glyph)
|
||||
var border_col: Color = UiKit.GOLD if selected else UiKit.PANEL_BORDER
|
||||
slot.add_theme_stylebox_override("panel",
|
||||
UiKit.panel_flat(UiKit.PANEL_DATA, border_col, 0, 1 if not selected else 2, Vector4(4, 4, 4, 4)))
|
||||
# note=积点进度行(反哺批⑤——自动兑付件悬停浮窗补「积点 N/M」,进度随挖格经组合根重查刷新)
|
||||
# note=积点进度行(自动兑付件悬停浮窗补「积点 N/M」,进度随挖格经组合根重查刷新)
|
||||
var note := String(rec.get("note", ""))
|
||||
slot.tooltip_text = "%s%s%s" % [String(rec.get("name", "")),
|
||||
"(%s)" % note if note != "" else "",
|
||||
|
||||
@@ -11,14 +11,14 @@ const ENTRIES := [
|
||||
["#000000", "封印", "封印印记(呈现名·不能拔)——轮末自动判定标中;插错可左键直接挖开(安全格零奖励、危险格吃挖雷惩罚)"],
|
||||
["#2e86de", "小人", "探险者——8 向相邻未开格常驻微光 = 可挖域"],
|
||||
["#F0A828", "金徽章", "已开格奖励身份 icon(封印之下、数值之上,不进数字区)"],
|
||||
["#F0A828", "九徽章解码", "攻=攻击 · 守=护甲 · 爆=爆裂 · 医=回复 · 弱=削弱 · 穿=穿刺 · 魔=魔法 · 行=行动点 · 金=GP(打磨包 2026-09-29 入列)"],
|
||||
["#F0A828", "九徽章解码", "攻=攻击 · 守=护甲 · 爆=爆裂 · 医=回复 · 弱=削弱 · 穿=穿刺 · 魔=魔法 · 行=行动点 · 金=GP"],
|
||||
["#33261B", "状态槽空态", "无状态效果时槽位隐藏,数据框上移补位——非异常"],
|
||||
]
|
||||
|
||||
var _panel: PanelContainer
|
||||
|
||||
|
||||
var _rap_cost := -1 # <0 = 不渲染 RAP 行(打磨包 2026-09-29——构造参数注表值,禁字面量)
|
||||
var _rap_cost := -1 # <0 = 不渲染 RAP 行(构造参数注表值,禁字面量)
|
||||
|
||||
|
||||
func _init(rap_cost: int = -1) -> void:
|
||||
@@ -44,7 +44,7 @@ func _init(rap_cost: int = -1) -> void:
|
||||
row.add_child(lab)
|
||||
box.add_child(row)
|
||||
box.add_child(HSeparator.new())
|
||||
# RAP 行(打磨包 2026-09-29——表值驱动,禁字面量:刷新代价读行动经济表)
|
||||
# RAP 行(表值驱动,禁字面量:刷新代价读行动经济表)
|
||||
if _rap_cost >= 0:
|
||||
var rap_row := HBoxContainer.new()
|
||||
rap_row.add_theme_constant_override("separation", 8)
|
||||
|
||||
@@ -7,29 +7,29 @@ extends Control
|
||||
signal route_chosen(node_id: int)
|
||||
signal abandon_requested
|
||||
|
||||
const COL_ROOM_DONE := UiKit.GOLD_DONE # 已到——暗金(样张 v1 冻结新值 #68562C——旧 GOLD_DIM 对双色墨均 <4.5:1,对比硬指标逼守;UiKit.GOLD_DIM 他用不动)
|
||||
const COL_ROOM_DONE := UiKit.GOLD_DONE # 已到——暗金 #68562C(GOLD_DIM 对双色墨均 <4.5:1,对比硬指标逼守此值;UiKit.GOLD_DIM 他用不动)
|
||||
const COL_ROOM_CURRENT := UiKit.GOLD # 当前——奖励金
|
||||
const COL_ROOM_FUTURE := UiKit.DARK_UPCOMING # 未达——暗
|
||||
const COL_PASSAGE := UiKit.STONE # 通道——石灰
|
||||
const COL_PASSAGE_LIT := UiKit.CANDLE # 候选通道/候选描边——烛光黄(M5:金色定岗只留当前位,G4;旧 GOLD_LIT 退役出本屏)
|
||||
const COL_BOSS_BORDER := UiKit.BOSS_PURPLE # Boss 边框——紫赤(色相独占本色;封印朱退役 2026-09-26)
|
||||
const COL_PASSAGE_LIT := UiKit.CANDLE # 候选通道/候选描边——烛光黄(金色定岗只留当前位)
|
||||
const COL_BOSS_BORDER := UiKit.BOSS_PURPLE # Boss 边框——紫赤(色相独占本色)
|
||||
const COL_ABANDON := UiKit.RED_MINE # 破坏性动作红(危险红族)
|
||||
const NODE_LABELS := {"battle": "战", "reward": "赏", "rest": "疗", "boss": "王", "shop": "商"} # 商 = GP+商店房实施批 2026-09-21(贴图缺位字形回退用——贴图在位=字形退役惯例)
|
||||
const NODE_TYPE_NAMES := {"battle": "战斗", "reward": "奖励", "rest": "休息", "boss": "Boss", "shop": "商店"} # M5 节点下方类型字(demo ④——三通道可读性;当前位显示「当前位置」)
|
||||
const NODE_LABELS := {"battle": "战", "reward": "赏", "rest": "疗", "boss": "王", "shop": "商"} # 贴图缺位字形回退用——贴图在位=字形退役惯例
|
||||
const NODE_TYPE_NAMES := {"battle": "战斗", "reward": "奖励", "rest": "休息", "boss": "Boss", "shop": "商店"} # 节点下方类型字(demo ④——三通道可读性;当前位显示「当前位置」)
|
||||
const TEX_NODE_ICONS := {"battle": UiKit.TEX_NODE_BATTLE, "reward": UiKit.TEX_NODE_REWARD,
|
||||
"rest": UiKit.TEX_NODE_REST, "boss": UiKit.TEX_NODE_BOSS, "shop": UiKit.TEX_NODE_SHOP}
|
||||
const NODE_ICON_PX := 20.0 # 节点图标边长(样张 v1 冻结——20px 烘焙 1:1 消费零缩放)
|
||||
const TITLE_ENTRY := "出发——选择首个节点" # 入口停点(布局决策 1——台账 143 遗留清偿)
|
||||
const NODE_ICON_PX := 20.0 # 节点图标边长(20px 烘焙 1:1 消费零缩放)
|
||||
const TITLE_ENTRY := "出发——选择首个节点" # 入口停点(布局决策 1)
|
||||
const TITLE_SINGLE := "继续前进" # 单链停点
|
||||
const TITLE_FORK := "选择下一节点" # 岔路停点
|
||||
const NODE_HALF := Vector2(36.5, 25) # 节点方块半宽高(73×50 = 52×36 的 1.4×——UI 落码批 M5「黑底吃掉」;层距/布局公式零改)
|
||||
const LAYER_BASE_X := 70 # 首层节点原点 x(画布拟合与布局公式共用——T4 杂并:双写退役)
|
||||
const NODE_HALF := Vector2(36.5, 25) # 节点方块半宽高(73×50 = 52×36 的 1.4×;层距/布局公式零改)
|
||||
const LAYER_BASE_X := 70 # 首层节点原点 x(画布拟合与布局公式共用)
|
||||
const LAYER_STEP_X := 122 # 层间横向步距(同上)
|
||||
const SLOT_BASE_Y := 52 # 槽位原点 y(同上)
|
||||
const SLOT_STEP_Y := 58 # 同层槽位纵向步距(同上)
|
||||
const HIT_PAD := 6.0 # 图上命中外扩(指宽容差)
|
||||
const CONFIRM_WINDOW_S := 3.0 # 放弃确认态窗口(秒——超时回落)
|
||||
const COL_HP_BAR_BACK := UiKit.HP_BAR_BACK # 迷你血条深底(样张 v1 冻结——对暗面板可辨;值单点收 UiKit)
|
||||
const COL_HP_BAR_BACK := UiKit.HP_BAR_BACK # 迷你血条深底(对暗面板可辨;值单点收 UiKit)
|
||||
|
||||
var _runner = null
|
||||
var _title: Label
|
||||
@@ -41,13 +41,13 @@ var _hp_bar: Control
|
||||
var _attr_value: Label
|
||||
var _bonus_row: HBoxContainer
|
||||
var _bonus_value: Label
|
||||
var _feat_value: Label # 特性持有行(迭代 12 呈现补齐——组合根投喂逐件文案〔2026-10-12 换代〕)
|
||||
var _feat_value: Label # 特性持有行(组合根投喂逐件文案)
|
||||
var _cand_ids: Array[int] = [] # 本停点候选 id 集(图上命中合法集)
|
||||
var _content_size := Vector2(620, 240) # 图内容包络(open_choices 按图拟合——画布 min 尺寸随它)
|
||||
var _hover_id := -1 # 悬停预承诺候选(-1 = 无)
|
||||
var abandon_btn: Button # 放弃入口(可见性归组合根 set_abandon_available——只在冒险模式开)
|
||||
var status_badge: Button # 状态面板入口角标(迭代 17 卡③ P1 丁——顶栏 GP 徽右侧;连接归组合根)
|
||||
var _abandon_timer := ArmedTimer.new() # 放弃确认窗(T3/D3 P4 乙——_process 帧驱动倒计时机制收口;窗口=CONFIRM_WINDOW_S)
|
||||
var status_badge: Button # 状态面板入口角标(顶栏 GP 徽右侧;连接归组合根)
|
||||
var _abandon_timer := ArmedTimer.new() # 放弃确认窗(_process 帧驱动倒计时机制收口;窗口=CONFIRM_WINDOW_S)
|
||||
var _node_icons := {} # 节点图标贴图(load_tex 守卫——缺位 null → 字形回退,冒烟/无素材检出安全)
|
||||
var _noise_rect: TextureRect # 屏底石纹(换层色板渐变承载面——null = 纯色回退无挂点)
|
||||
var _tint_level := 0 # 当前已应用 tint 的层号(0 = 未应用)
|
||||
@@ -55,11 +55,11 @@ var _tint_level := 0 # 当前已应用 tint 的层号(0 = 未
|
||||
|
||||
func _init() -> void:
|
||||
_abandon_timer.expired.connect(_reset_abandon_confirm)
|
||||
# 节点图标贴图装载(样张 v1 冻结形制——load_tex 守卫,缺位回退现行字形)
|
||||
# 节点图标贴图装载(load_tex 守卫,缺位回退现行字形)
|
||||
for t in TEX_NODE_ICONS:
|
||||
_node_icons[t] = UiKit.load_tex(TEX_NODE_ICONS[t])
|
||||
# 独立屏底(形态升格 2026-09-28——spec Layout;UI 对齐批 T5 2026-10-06:demo #v-map 暖棕竖向
|
||||
# 渐变(#4A3B2A 0%→#3A2E22 40%)+石纹弱质感 0.14+暗角聚焦;旧满铺石纹不透退役;底纹缺位回退纯色)
|
||||
# 独立屏底(spec Layout——demo #v-map 暖棕竖向
|
||||
# 渐变(#4A3B2A 0%→#3A2E22 40%)+石纹弱质感 0.14+暗角聚焦;底纹缺位回退纯色)
|
||||
var base := UiKit.gradient_base(UiKit.BG_MAP_TOP, UiKit.BG_ROOT)
|
||||
add_child(base)
|
||||
var tex := UiKit.backdrop_tile()
|
||||
@@ -74,8 +74,8 @@ func _init() -> void:
|
||||
box.add_theme_constant_override("separation", 10)
|
||||
center.add_child(box)
|
||||
# 顶栏语境(spec Layout:左关卡名 / 右 GP 常驻徽章——直角细线数据框;
|
||||
# T5 判官 checkpoint 对齐 demo ④:关卡名入框板(裸文本退役)+GP 徽章=烛光胶囊(同商店GP框)+
|
||||
# 左右两极排布(原居中悬置))
|
||||
# 对齐 demo ④:关卡名入框板+GP 徽章=烛光胶囊(同商店GP框)+
|
||||
# 左右两极排布)
|
||||
var top := HBoxContainer.new()
|
||||
top.add_theme_constant_override("separation", 12)
|
||||
top.size_flags_horizontal = Control.SIZE_EXPAND_FILL
|
||||
@@ -98,7 +98,7 @@ func _init() -> void:
|
||||
gp_box.add_child(gp_row)
|
||||
_gp_value = UiKit.label("",UiKit.FS_BODY, true, UiKit.INK_ON_GOLD)
|
||||
gp_row.add_child(_gp_value)
|
||||
# 状态面板入口角标(迭代 17 卡③ P1 丁——顶栏常驻;2026-10-16 调试角标迁全局浮层后本屏只留状态。
|
||||
# 状态面板入口角标(顶栏常驻;本屏只留状态——调试角标归全局浮层。
|
||||
# 置灰守卫归组合根 _sync_status_entry(其他模态开着=不可点;本屏自身的登记开态不计——has_open_excluding);
|
||||
# 信号连接归组合根(视图零连接))
|
||||
status_badge = UiKit.button("状态", 0.0)
|
||||
@@ -116,11 +116,11 @@ func _init() -> void:
|
||||
_canvas.draw.connect(_draw_graph.bind(_canvas))
|
||||
_canvas.gui_input.connect(_on_canvas_input)
|
||||
box.add_child(_canvas)
|
||||
# 底部状态栏(M5 合并单条:亮米面板一站读齐=头像+生命+属性+本关加成/流派件——demo ④;
|
||||
# 道具槽不摆=道具读数无关卡画面成文出口(读快照私域越权),随 runner 出口批补——台账 352 注记;
|
||||
# 底部状态栏(合并单条:亮米面板一站读齐=头像+生命+属性+本关加成/流派件——demo ④;
|
||||
# 道具槽不摆=道具读数无关卡画面成文出口(读快照私域越权);
|
||||
# _hp_value/_attr_value/_bonus_value/_feat_value 投喂键全部保留)
|
||||
# 底栏定高纪律(2026-10-06 审计差距①对齐 demo——单条 ~85px 紧凑亮条;任何子件不再能把栏撑爆:
|
||||
# 特性持有行限宽截断+tooltip 全文,旧「autowrap 无限宽→最小高度逐字竖排爆栏」形态根除)
|
||||
# 底栏定高纪律(对齐 demo——单条 ~85px 紧凑亮条;任何子件不再能把栏撑爆:
|
||||
# 特性持有行限宽截断+tooltip 全文)
|
||||
var bottom := PanelContainer.new()
|
||||
bottom.custom_minimum_size = Vector2(0, 85)
|
||||
bottom.size_flags_vertical = Control.SIZE_SHRINK_END
|
||||
@@ -132,7 +132,7 @@ func _init() -> void:
|
||||
bottom.add_child(strip)
|
||||
var face := TextureRect.new()
|
||||
var face_tex := UiKit.load_tex(UiKit.TEX_HERO_PORTRAIT)
|
||||
# P6 甲⑤(2026-10-06):定高 48px=1×、宽随源图比自适应(源 46×48 → 46×48;纵横异倍消除)
|
||||
# 定高 48px=1×、宽随源图比自适应(源 46×48 → 46×48;纵横异倍消除)
|
||||
var face_h := 48.0
|
||||
var face_w := face_h
|
||||
if face_tex != null:
|
||||
@@ -160,20 +160,20 @@ func _init() -> void:
|
||||
_attr_value = UiKit.label("",UiKit.FS_BODY, true, UiKit.INK_TITLE)
|
||||
attr_box.add_child(_attr_value)
|
||||
strip.add_child(_vdiv())
|
||||
# 本关加成框(零加成隐藏——spec 布局决策 4)+ 流派件持有行(迭代 12 呈现补齐——组合根投喂)
|
||||
# 本关加成框(零加成隐藏——spec 布局决策 4)+ 流派件持有行(组合根投喂)
|
||||
_bonus_row = HBoxContainer.new()
|
||||
strip.add_child(_bonus_row)
|
||||
var bonus_box := VBoxContainer.new()
|
||||
_bonus_row.add_child(bonus_box)
|
||||
var bonus_inner := VBoxContainer.new()
|
||||
bonus_box.add_child(bonus_inner)
|
||||
var bonus_cap := UiKit.label("特性",UiKit.FS_CAPTION, false, UiKit.INK_HINT) # 淡墨→深墨(亮板可读,审计差距①)
|
||||
var bonus_cap := UiKit.label("特性",UiKit.FS_CAPTION, false, UiKit.INK_HINT) # 深墨(亮板可读)
|
||||
bonus_inner.add_child(bonus_cap)
|
||||
_bonus_value = UiKit.label("",UiKit.FS_BODY, true, UiKit.INK_TITLE) # 淡墨→深墨(同上)
|
||||
bonus_inner.add_child(_bonus_value)
|
||||
# 特性持有行(迭代 12 呈现补齐 2026-10-04——用户反馈关卡选择页看不到局内奖励;组合根投喂)
|
||||
# 审计差距①修(2026-10-06):限宽单行截断+tooltip 全文(旧 autowrap 无限宽=爆栏元凶);
|
||||
# 墨色 WARM_SOFT→INK_HINT(暖白系对亮板 ≈1:1 不可读——§7.2 违例修复)
|
||||
# 特性持有行(组合根投喂)
|
||||
# 限宽单行截断+tooltip 全文;
|
||||
# 墨色 INK_HINT(暖白系对亮板 ≈1:1 不可读——§7.2)
|
||||
_feat_value = UiKit.label("",UiKit.FS_HINT, false, UiKit.INK_HINT)
|
||||
_feat_value.custom_minimum_size = Vector2(240, 0)
|
||||
_feat_value.clip_text = true
|
||||
@@ -182,14 +182,14 @@ func _init() -> void:
|
||||
var spacer := Control.new()
|
||||
spacer.size_flags_horizontal = Control.SIZE_EXPAND_FILL
|
||||
strip.add_child(spacer)
|
||||
abandon_btn = UiKit.danger_button("放弃", "", 90.0) # 审计差距①对齐 demo:小号危险钮 ~90×38(原 216 宽砖);打磨包 2026-09-29 文案 + M5 危险钮皮
|
||||
abandon_btn = UiKit.danger_button("放弃", "", 90.0) # 小号危险钮 ~90×38
|
||||
abandon_btn.pressed.connect(_on_abandon_pressed)
|
||||
abandon_btn.visible = false
|
||||
strip.add_child(abandon_btn)
|
||||
visible = false
|
||||
|
||||
|
||||
# 竖分隔线(M5 底栏分组线——亮面板上低对比细线)
|
||||
# 竖分隔线(底栏分组线——亮面板上低对比细线)
|
||||
static func _vdiv() -> ColorRect:
|
||||
var v := ColorRect.new()
|
||||
v.color = UiKit.MAP_VDIV
|
||||
@@ -197,12 +197,9 @@ static func _vdiv() -> ColorRect:
|
||||
return v
|
||||
|
||||
|
||||
# _status_box 已退役(M5 底栏合并单条——demo ④;本函数随旧三暗框退役删除,投喂键保留于新底栏)
|
||||
|
||||
|
||||
func _draw_hp_bar(bar: Control) -> void:
|
||||
# 迷你血条(深底 UiKit.HP_BAR_BACK #28221C + 小人蓝填充——M5 改蓝:与战斗站桩血条同语言,
|
||||
# 「蓝只说己方」§4.2;旧金填充退役=金只说收益定岗 G4)。比例 = hero_hp / effective_max_hp
|
||||
# 迷你血条(深底 UiKit.HP_BAR_BACK #28221C + 小人蓝填充——与战斗站桩血条同语言,
|
||||
# 「蓝只说己方」§4.2)。比例 = hero_hp / effective_max_hp
|
||||
if _runner == null:
|
||||
return
|
||||
var max_hp := maxi(1, _runner.effective_max_hp())
|
||||
@@ -214,7 +211,7 @@ func _draw_hp_bar(bar: Control) -> void:
|
||||
|
||||
|
||||
# 放弃入口可见性(组合根在冒险启动时开、默认关——语义对象 = 整局冒险)
|
||||
# 特性持有投影(迭代 12 呈现补齐——组合根投喂逐件文案〔2026-10-12 换代〕;空文案=行隐藏)
|
||||
# 特性持有投影(组合根投喂逐件文案;空文案=行隐藏)
|
||||
func set_feat_holdings(text: String) -> void:
|
||||
_feat_value.text = text
|
||||
_feat_value.tooltip_text = text # 截断全量兜底(限宽单行省略——悬停看全文)
|
||||
@@ -240,7 +237,7 @@ func _on_abandon_pressed() -> void:
|
||||
func _reset_abandon_confirm() -> void:
|
||||
_abandon_timer.disarm()
|
||||
abandon_btn.modulate = Color(1, 1, 1, 1)
|
||||
abandon_btn.text = "放弃" # 打磨包 2026-09-29——文案收敛 level-map 4 字上限(确认态转义面见 _on_abandon_pressed)
|
||||
abandon_btn.text = "放弃" # 文案收敛 level-map 4 字上限(确认态转义面见 _on_abandon_pressed)
|
||||
|
||||
|
||||
func _process(delta: float) -> void:
|
||||
@@ -269,7 +266,7 @@ func open_choices(cands: Array, runner: LevelRunner) -> void:
|
||||
if cands.size() > 1:
|
||||
_title.text = TITLE_FORK
|
||||
elif visited.is_empty():
|
||||
_title.text = TITLE_ENTRY # 入口停点(尚未走出第一步——台账 143 措辞矛盾清偿)
|
||||
_title.text = TITLE_ENTRY # 入口停点(尚未走出第一步)
|
||||
else:
|
||||
_title.text = TITLE_SINGLE
|
||||
_cand_ids = []
|
||||
@@ -287,7 +284,7 @@ func open_choices(cands: Array, runner: LevelRunner) -> void:
|
||||
|
||||
|
||||
# 画布随图自适应(level-map.md Layout:画布宽随层数、高随同层最大节点数——公式与 _layout 同源;
|
||||
# 2026-09-28 真鼠标点击 bug 根修:固定 620×240 画布下深层节点(L1 Boss 内容 x=802)落在 gui_input
|
||||
# 固定 620×240 画布下深层节点(Boss 内容 x=802)落在 gui_input
|
||||
# 命中区外——绘制无裁剪看得见、命中收不到事件。单链关卡层深=节点序,必须先拟合再命中。
|
||||
func _fit_canvas(nodes: Array) -> void:
|
||||
if nodes.is_empty():
|
||||
@@ -302,15 +299,15 @@ func _fit_canvas(nodes: Array) -> void:
|
||||
for k in slot.keys():
|
||||
maxslot = maxi(maxslot, int(slot[k]))
|
||||
_content_size = Vector2(LAYER_BASE_X + maxl * LAYER_STEP_X + NODE_HALF.x + 8.0,
|
||||
SLOT_BASE_Y + maxslot * SLOT_STEP_Y + NODE_HALF.y + 24.0) # +24 = 类型文字标签行(M5)
|
||||
_content_size = _content_size.max(Vector2(620, 256)) # 下限(M5 抬高容纳标签行)
|
||||
SLOT_BASE_Y + maxslot * SLOT_STEP_Y + NODE_HALF.y + 24.0) # +24 = 类型文字标签行
|
||||
_content_size = _content_size.max(Vector2(620, 256)) # 下限(抬高容纳标签行)
|
||||
_canvas.custom_minimum_size = _content_size
|
||||
|
||||
|
||||
# 换层色板渐变挂点(art-bible §2.7 下探过渡切片档——关卡结算确认→下一关→独立屏淡入链路上的
|
||||
# 色相层进预告:屏底世界侧随层进换挡,信息层恒定;tint 明度带归一≈1 只动色相/饱和)。
|
||||
# tint 候选方向 = art-bible §6.3 三关图类型色板方向(1 dungeon 暖石基准/2 underdark 冷蓝灰/3 deep 灰黑金属),
|
||||
# 终值归色板样张批——本批只落挂点(level-map.md States 换层进入行承载)。
|
||||
# 只落挂点(level-map.md States 换层进入行承载)。
|
||||
const BOARD_TINTS := {1: Color(1, 1, 1), 2: Color(0.84, 0.99, 1.28), 3: Color(0.97, 0.99, 1.09)}
|
||||
|
||||
|
||||
@@ -321,7 +318,7 @@ func _apply_level_tint(level_index: int) -> void:
|
||||
var target: Color = BOARD_TINTS.get(level_index, Color(1, 1, 1))
|
||||
var tw := create_tween()
|
||||
tw.tween_property(_noise_rect, "modulate",
|
||||
Color(target.r, target.g, target.b, UiKit.BACKDROP_TILE_ALPHA), UiKit.motion(0.45)) # 0.14 弱质感档随 tint 保持(T5)
|
||||
Color(target.r, target.g, target.b, UiKit.BACKDROP_TILE_ALPHA), UiKit.motion(0.45)) # 0.14 弱质感档随 tint 保持
|
||||
|
||||
|
||||
# 状态栏刷新(命令式纪律——组合根打开本屏/兑付确认回执后重查;零推送信号订阅)
|
||||
@@ -333,7 +330,7 @@ func _refresh_status() -> void:
|
||||
var max_hp: int = _runner.effective_max_hp()
|
||||
_hp_value.text = "HP %d/%d" % [int(_runner.hero_hp), int(max_hp)]
|
||||
_hp_bar.queue_redraw()
|
||||
var atk_bonus := int(_runner.run_ledger.bonus.get("atk", 0)) # 迭代 27 T3——加算区住 RunLedger
|
||||
var atk_bonus := int(_runner.run_ledger.bonus.get("atk", 0)) # 加算区住 RunLedger
|
||||
var ac_bonus := int(_runner.run_ledger.bonus.get("armor_class", 0))
|
||||
var atk_txt := "%d" % _runner.effective_atk()
|
||||
if atk_bonus > 0:
|
||||
@@ -390,7 +387,7 @@ func _candidate_at(local_pos: Vector2) -> int:
|
||||
|
||||
|
||||
func _submit_choice(nid: int) -> void:
|
||||
# 形态升格分支(2026-09-28 独立屏):战斗/Boss 节点 = 出屏切回战斗屏;奖励/恢复/商店节点 =
|
||||
# 独立屏分支:战斗/Boss 节点 = 出屏切回战斗屏;奖励/恢复/商店节点 =
|
||||
# 本屏保持激活——兑付覆盖层由节点事件在本屏域内弹出(模态栈遮挡恢复语义复用),兑付确认后回图刷新
|
||||
var node := _node_by_id(nid, _runner.nodes())
|
||||
var t := String(node.get("type", ""))
|
||||
@@ -430,7 +427,7 @@ func _draw_graph(canvas: Control) -> void:
|
||||
var visited: Array[Variant] = _runner.visited_ids()
|
||||
var cand_ids: Array[Variant] = _runner.next_candidate_ids()
|
||||
# 连线(通道)先行画(方块覆盖其上);悬停预承诺 = 所属通道再增亮(加粗)
|
||||
# T17 修复:选路停点(候选非空)非候选通道降透明——金岔路从结构网分离(干线共线重叠缺陷的
|
||||
# 选路停点(候选非空)非候选通道降透明——金岔路从结构网分离(干线共线重叠缺陷的
|
||||
# 视觉主因:不同源通道共用同一垂直干线,金边与石色边焊接成「整列可走」假象)
|
||||
var dim_passages: bool = not cand_ids.is_empty()
|
||||
for node in nodes:
|
||||
@@ -443,7 +440,7 @@ func _draw_graph(canvas: Control) -> void:
|
||||
var lit: bool = _id_of(node) == current_id and cand_ids.has(tid)
|
||||
var hovered: bool = lit and _hover_id == tid
|
||||
var col := COL_PASSAGE_LIT if lit else (Color(COL_PASSAGE, 0.35) if dim_passages else COL_PASSAGE)
|
||||
var width := 4.0 if hovered else (3.0 if lit else 2.0) # 样张 v1 冻结:2/3/4(原 1.5 取整)
|
||||
var width := 4.0 if hovered else (3.0 if lit else 2.0) # 2/3/4 三档
|
||||
_draw_passage(canvas, from, to, col, width)
|
||||
# 房间方块
|
||||
for node in nodes:
|
||||
@@ -458,14 +455,14 @@ func _draw_graph(canvas: Control) -> void:
|
||||
var rect := Rect2(p - NODE_HALF, NODE_HALF * 2.0)
|
||||
canvas.draw_rect(rect, col)
|
||||
if t == "boss":
|
||||
# Boss 描边 = 紫赤 2px(§4.2 色相独占)+ 外缘 1px 深墨环(灰度冗余通道——样张 v1 L2 逼守:
|
||||
# Boss 描边 = 紫赤 2px(§4.2 色相独占)+ 外缘 1px 深墨环(灰度冗余通道强制:
|
||||
# 紫赤灰度 72 对石纹噪底 65 灰度不可辨;灰度/色弱下 Boss 环经深墨环可辨)
|
||||
canvas.draw_rect(Rect2(rect.position - Vector2(3, 3), rect.size + Vector2(6, 6)), UiKit.INK_ROUTE, false, 1.0)
|
||||
canvas.draw_rect(rect, COL_BOSS_BORDER, false, 2.0)
|
||||
elif cand_ids.has(nid):
|
||||
# 悬停预承诺 = 亮金描边加粗(4px),常态候选 = 亮金描边 2px(spec States 悬停预承诺行)
|
||||
canvas.draw_rect(rect, COL_PASSAGE_LIT, false, 4.0 if _hover_id == nid else 2.0)
|
||||
# 节点图标(样张 v1 冻结形制:20px 烘焙 1:1 消费;双色墨制=乘法调制——当前金底深墨,暗底系 WARM 原色;
|
||||
# 节点图标(20px 烘焙 1:1 消费;双色墨制=乘法调制——当前金底深墨,暗底系 WARM 原色;
|
||||
# 贴图缺位 → 字形回退=贴图在位字形退役惯例)
|
||||
var icon: Texture2D = _node_icons.get(t)
|
||||
if icon != null:
|
||||
@@ -476,11 +473,11 @@ func _draw_graph(canvas: Control) -> void:
|
||||
var font := get_theme_default_font()
|
||||
var label := String(NODE_LABELS.get(t, "?"))
|
||||
canvas.draw_string(font, p + Vector2(-6, 2), label, HORIZONTAL_ALIGNMENT_LEFT, -1, 16, UiKit.INK_ROUTE)
|
||||
# 编号(右上角——与图标占位区零重叠,样张 v1 L7;双色墨随态)
|
||||
# 编号(右上角——与图标占位区零重叠;双色墨随态)
|
||||
var num_ink := UiKit.INK_ROUTE if col == COL_ROOM_CURRENT else UiKit.WARM
|
||||
canvas.draw_string(get_theme_default_font(), Vector2(rect.end.x - 16.0, rect.position.y + 8.0),
|
||||
str(nid), HORIZONTAL_ALIGNMENT_RIGHT, 14, 10, num_ink)
|
||||
# 类型文字标签(M5——demo ④:节点下方 11px;当前位=「当前位置」金、候选=类型名+「 · 可选」烛光黄、
|
||||
# 类型文字标签(demo ④:节点下方 11px;当前位=「当前位置」金、候选=类型名+「 · 可选」烛光黄、
|
||||
# 其余=类型名暖米弱化;文字+图标+形状三通道)
|
||||
var type_text := String(NODE_TYPE_NAMES.get(t, t))
|
||||
if nid == current_id:
|
||||
@@ -504,7 +501,7 @@ func _layout(nodes: Array, canvas: Control) -> Dictionary:
|
||||
return pos
|
||||
|
||||
|
||||
# ---- 探针只读出口(D2 改造 2026-10-04——_layout/_canvas 私有域直入退役;形态仿 fx_probe 先例:只读快照) ----
|
||||
# ---- 探针只读出口(形态仿 fx_probe 先例:只读快照) ----
|
||||
|
||||
# 选路布局只读快照(node → 局部位置;画布固定取现行 _canvas,调用侧零私有域触达)
|
||||
func layout_probe(nodes: Array) -> Dictionary:
|
||||
@@ -573,9 +570,9 @@ func _draw_passage(canvas: Control, from: Vector2, to: Vector2, col: Color, widt
|
||||
var b := to - Vector2(NODE_HALF.x, 0)
|
||||
if b.x < a.x:
|
||||
return
|
||||
# T17 修复:垂直干线 x 按源节点 y 偏移——不同源的边不再共线重叠(旧 mid_x=中点使相邻层
|
||||
# 全部通道焊成一条干系,「金岔路+整列可走」误读的直接几何根源)。同源双边仍共线(同出发
|
||||
# 点语义正确),跨源分离度 = 源行距 × 0.2(L2 行距 ~58px → 偏移 ~12px 可辨)。
|
||||
# 垂直干线 x 按源节点 y 偏移——不同源的边不再共线重叠(共线会把相邻层通道焊成一条干系,
|
||||
# 「金岔路+整列可走」误读的直接几何根源)。同源双边仍共线(同出发
|
||||
# 点语义正确),跨源分离度 = 源行距 × 0.2(行距 ~58px → 偏移 ~12px 可辨)。
|
||||
var mid_x := (a.x + b.x) / 2.0 + (a.y - b.y) * 0.2
|
||||
var pts := PackedVector2Array([a, Vector2(mid_x, a.y), Vector2(mid_x, b.y), b])
|
||||
canvas.draw_polyline(pts, col, width)
|
||||
@@ -9,10 +9,10 @@ signal closed
|
||||
var _store
|
||||
var _volume: HSlider
|
||||
var _volume_val: Label
|
||||
var _fs_btn: Button # M4 换 SwitchToggle 拨杆(仍 Button 子类——探针接缝保持)
|
||||
var _motion_btn: Button # M4 同上
|
||||
var _fs_st: Label # M4 状态字随行(开/关)
|
||||
var _motion_st: Label # M4 状态字(完整/快速)
|
||||
var _fs_btn: Button # SwitchToggle 拨杆(仍 Button 子类——探针接缝保持)
|
||||
var _motion_btn: Button # 同上
|
||||
var _fs_st: Label # 状态字随行(开/关)
|
||||
var _motion_st: Label # 状态字(完整/快速)
|
||||
|
||||
|
||||
func _init(store) -> void:
|
||||
@@ -29,11 +29,11 @@ func retarget(store: SettingsStore) -> void:
|
||||
|
||||
|
||||
func _build() -> void:
|
||||
# M4(UI 落码批):石板面板化(demo ③)+行牌化(标签+拨杆+状态字+后果说明行);
|
||||
# 设置项零增减(P-C6:只有主音量——demo 音效音量行系原型发明已撤);
|
||||
# 减动效与结算演出动画=同一设置(语义命名批合并),一行承载。
|
||||
# T8(UI 设计对齐批 2026-10-06):两列对齐(label 列定宽 140——demo .setrow label/控制两列)+
|
||||
# 后果说明内联随行(demo .why 同排)+返回钮紧凑化(demo .btn9 紧凑——原 216 宽砖)。
|
||||
# 石板面板化(demo ③)+行牌化(标签+拨杆+状态字+后果说明行);
|
||||
# 设置项零增减(只有主音量);
|
||||
# 减动效与结算演出动画=同一设置,一行承载。
|
||||
# 两列对齐(label 列定宽 140——demo .setrow label/控制两列)+
|
||||
# 后果说明内联随行(demo .why 同排)+返回钮紧凑化(demo .btn9 紧凑)。
|
||||
var shell: Dictionary[Variant, Variant] = UiKit.modal_shell(self, UiKit.BG_BRIGHT, Vector2(560, 0), 8, true,
|
||||
UiKit.panel_skinned(Vector4(20, 16, 20, 16)))
|
||||
var box: VBoxContainer = shell.box
|
||||
@@ -51,13 +51,13 @@ func _build() -> void:
|
||||
vol_row.add_theme_constant_override("separation", 14)
|
||||
box.add_child(vol_row)
|
||||
var vol_lab := UiKit.label("主音量",UiKit.FS_BODY, false, UiKit.INK_LABEL)
|
||||
vol_lab.custom_minimum_size = Vector2(140, 0) # T8 label 列定宽(两列对齐)
|
||||
vol_lab.custom_minimum_size = Vector2(140, 0) # label 列定宽(两列对齐)
|
||||
vol_row.add_child(vol_lab)
|
||||
_volume = HSlider.new()
|
||||
_volume.min_value = 0.0
|
||||
_volume.max_value = 100.0
|
||||
_volume.value = 100.0
|
||||
_volume.custom_minimum_size = Vector2(200, 24) # 几何/量程内联(迭代 25:样式滑杆工厂退役,皮肤走全局主题)
|
||||
_volume.custom_minimum_size = Vector2(200, 24) # 几何/量程内联(皮肤走全局主题)
|
||||
_volume.step = 1
|
||||
_volume.size_flags_horizontal = Control.SIZE_EXPAND_FILL
|
||||
_volume.value_changed.connect(_on_volume_changed)
|
||||
@@ -66,12 +66,12 @@ func _build() -> void:
|
||||
_volume_val.custom_minimum_size = Vector2(60, 0)
|
||||
vol_row.add_child(_volume_val)
|
||||
|
||||
# 全屏(拨杆形——M10 SwitchToggle;状态字随行)
|
||||
# 全屏(拨杆形——SwitchToggle;状态字随行)
|
||||
var fs_row := HBoxContainer.new()
|
||||
fs_row.add_theme_constant_override("separation", 14)
|
||||
box.add_child(fs_row)
|
||||
var fs_lab := UiKit.label("全屏",UiKit.FS_BODY, false, UiKit.INK_LABEL)
|
||||
fs_lab.custom_minimum_size = Vector2(140, 0) # T8 同上
|
||||
fs_lab.custom_minimum_size = Vector2(140, 0) # 同上
|
||||
fs_row.add_child(fs_lab)
|
||||
_fs_btn = UiKit.switch_toggle(false)
|
||||
_fs_btn.toggled.connect(_on_fullscreen_toggled)
|
||||
@@ -79,14 +79,14 @@ func _build() -> void:
|
||||
_fs_st = UiKit.label("关",UiKit.FS_HINT, true, UiKit.INK_LABEL)
|
||||
fs_row.add_child(_fs_st)
|
||||
|
||||
# 结算演出动画(=减动效实称——语义命名批 T17;拨杆 on=完整,off=快速时长减半。
|
||||
# T8 核证:reduced_motion 单设置同驱 UiKit.motion_scale 与结算演播——demo「减动效」行与
|
||||
# 本行系同一设置,不另立行(拆两行=一设置双行重复,违语义命名批裁决)
|
||||
# 结算演出动画(=减动效实称;拨杆 on=完整,off=快速时长减半。
|
||||
# reduced_motion 单设置同驱 UiKit.motion_scale 与结算演播——demo「减动效」行与
|
||||
# 本行系同一设置,不另立行(拆两行=一设置双行重复)
|
||||
var motion_row := HBoxContainer.new()
|
||||
motion_row.add_theme_constant_override("separation", 14)
|
||||
box.add_child(motion_row)
|
||||
var motion_lab := UiKit.label("结算演出动画",UiKit.FS_BODY, false, UiKit.INK_LABEL)
|
||||
motion_lab.custom_minimum_size = Vector2(140, 0) # T8 同上
|
||||
motion_lab.custom_minimum_size = Vector2(140, 0) # 同上
|
||||
motion_row.add_child(motion_lab)
|
||||
_motion_btn = UiKit.switch_toggle(true)
|
||||
_motion_btn.toggled.connect(_on_motion_toggled)
|
||||
@@ -94,14 +94,14 @@ func _build() -> void:
|
||||
_motion_st = UiKit.label("完整",UiKit.FS_HINT, true, UiKit.INK_LABEL)
|
||||
motion_row.add_child(_motion_st)
|
||||
var why := UiKit.label("快速 = 时长减半(运动敏感 / 演示用)",UiKit.FS_CAPTION, false, UiKit.INK_HINT)
|
||||
motion_row.add_child(why) # T8 内联随行(demo .why 同排;原独立行换排)
|
||||
motion_row.add_child(why) # 内联随行(demo .why 同排)
|
||||
|
||||
box.add_child(HSeparator.new())
|
||||
# 退出游戏已提级开始菜单(D1——设置面不再承载,用户裁决「退出只在一级菜单出现」)
|
||||
# 退出游戏归开始菜单——设置面不再承载(退出只在一级菜单出现)
|
||||
var close_row := HBoxContainer.new()
|
||||
close_row.alignment = BoxContainer.ALIGNMENT_CENTER
|
||||
box.add_child(close_row)
|
||||
var close_btn := UiKit.button("返回", 90.0) # T8 紧凑返回钮(demo 紧凑形;原 216 宽砖)
|
||||
var close_btn := UiKit.button("返回", 90.0) # 紧凑返回钮(demo 紧凑形)
|
||||
close_btn.pressed.connect(_emit_closed)
|
||||
close_row.add_child(close_btn)
|
||||
|
||||
@@ -115,7 +115,7 @@ func _sync() -> void:
|
||||
_sync_motion_text()
|
||||
|
||||
|
||||
# T17 修复:文案随状态即时刷新(旧 bug=切换后文案恒「开」,玩家无法得知当前态)+语义命名
|
||||
# 文案随状态即时刷新(否则切换后文案恒「开」,玩家无法得知当前态)+语义命名
|
||||
# (「减动效」玩家读不懂——实称「结算演出动画」速度档)+操作提示内联(所见即所得)。
|
||||
func _sync_motion_text() -> void:
|
||||
_motion_st.text = "快速" if _store.reduced_motion else "完整"
|
||||
@@ -135,10 +135,10 @@ func _on_fullscreen_toggled(on: bool) -> void:
|
||||
|
||||
|
||||
func _on_motion_toggled(on: bool) -> void:
|
||||
_store.reduced_motion = not on # M4 拨杆语义翻转:on=完整(off=快速)
|
||||
_store.reduced_motion = not on # 拨杆语义翻转:on=完整(off=快速)
|
||||
_store.apply()
|
||||
_store.save_settings()
|
||||
_sync_motion_text() # T17 修复:切换即时刷新文案(旧版漏刷新=状态文字恒「开」)
|
||||
_sync_motion_text() # 切换即时刷新文案(漏刷新会让状态文字恒「开」)
|
||||
|
||||
|
||||
func _emit_closed() -> void:
|
||||
|
||||
@@ -143,7 +143,7 @@ func play_round_banner(ri: int) -> void:
|
||||
tw.tween_callback(banner.queue_free)
|
||||
|
||||
|
||||
# 开战横幅——板级地形变异预告(全局作用域变异首发批 2026-10-04):board_variants 非空时开战
|
||||
# 开战横幅——板级地形变异预告:board_variants 非空时开战
|
||||
# 显示一行(文本=「地形变异 · 贫矿脉」式);减动效=静态驻留后即隐(轮横幅先例——行呈现不跳过)。
|
||||
# 文案/驻留参数 provisional 归本地化批;普通图恒不发射(组合根按快照 board_variants 空判)。
|
||||
func play_board_variant_banner(text: String) -> void:
|
||||
|
||||
@@ -21,7 +21,7 @@ var _shelf_rows: Array[Dictionary] = [] # [{kind_id, btn}]——pool 面卡片
|
||||
|
||||
|
||||
func _init() -> void:
|
||||
# T6(UI 设计对齐批 2026-10-06):全屏页形态(demo ⑤ #v-shop——06 在册 D2 商店全屏化/D1 竖卡
|
||||
# 全屏页形态(demo ⑤ #v-shop——06 在册 D2 商店全屏化/D1 竖卡
|
||||
# 232×352 落地)——暖亮底+石纹弱质感+暗角(UiKit.page_shell)+56px 通栏顶条(标题+刷新图标钮/
|
||||
# 角标+GP 框)+ 爬价规则行 + bcard 三竖卡居中 + 底部离开钮与池源注记。
|
||||
# M6 遗产:库存余量退役(关卡节点地图 v1.5-a 用户裁);刷新钮呈现位照摆(机制=MC-1 投喂)。
|
||||
@@ -56,7 +56,7 @@ func _init() -> void:
|
||||
box.add_child(_visit_label)
|
||||
_cards_box = VBoxContainer.new()
|
||||
_cards_box.size_flags_vertical = Control.SIZE_EXPAND_FILL
|
||||
_cards_box.alignment = BoxContainer.ALIGNMENT_CENTER # 迭代 26 T5③ 卡片区垂直居中(旧默认顶对齐=卡片堆上半、下半空)
|
||||
_cards_box.alignment = BoxContainer.ALIGNMENT_CENTER # 卡片区垂直居中
|
||||
_cards_box.add_theme_constant_override("separation", 8)
|
||||
box.add_child(_cards_box)
|
||||
var foot := HBoxContainer.new()
|
||||
@@ -118,7 +118,7 @@ func _build_card(p: Dictionary) -> Dictionary:
|
||||
var row := HBoxContainer.new()
|
||||
row.add_theme_constant_override("separation", 14)
|
||||
card.add_child(row)
|
||||
var name_l := UiKit.label(_card_title(p),UiKit.FS_BODY, true, UiKit.WARM_SOFT) # 尾裁微批a 2026-10-08——显式传墨:bold 缺省 INK_LABEL 压暗底行卡 1.12:1 越带,换暖白次级 9.53:1
|
||||
var name_l := UiKit.label(_card_title(p),UiKit.FS_BODY, true, UiKit.WARM_SOFT) # 显式传墨:bold 缺省 INK_LABEL 压暗底行卡 1.12:1 越带,换暖白次级 9.53:1
|
||||
name_l.custom_minimum_size = Vector2(190, 0)
|
||||
row.add_child(name_l)
|
||||
var price_l := UiKit.label("",UiKit.FS_HINT, false, UiKit.WARM_SOFT)
|
||||
@@ -193,7 +193,7 @@ func set_refresh_display(free_left: int, gp_cost: int, can_afford: bool) -> void
|
||||
|
||||
|
||||
# 货架卡(bcard 竖卡:类型色带+图标+名+件值描述+价签+购买钮——M6 同构卡;demo ⑤)
|
||||
# 库存余量退役=关卡节点地图 v1.5-a 用户裁(2026-10-04)——数据层 stock_left 仍投喂断点,仅呈现不摆
|
||||
# 库存余量不再呈现(关卡节点地图 v1.5-a)——数据层 stock_left 仍投喂断点
|
||||
func _build_shelf_card(holder: Control, c: Dictionary) -> Dictionary:
|
||||
var kind := String(c.get("kind_id", ""))
|
||||
var icon_key := UiKit.TEX_ICON_SWORD
|
||||
|
||||
@@ -5,5 +5,5 @@ extends Node2D
|
||||
|
||||
var world_font: Font = null # 字体注入槽位(null = ThemeDB.fallback_font——BattleStageView.set_world_font 注入)
|
||||
|
||||
# C6 headless 可验判据(2026-09-12 场景结构裁决):注入字体非空 + 中文字形 has_char + 采样文本宽度 > 0。
|
||||
# C6 headless 可验判据:注入字体非空 + 中文字形 has_char + 采样文本宽度 > 0。
|
||||
# 实际光栅观感归批末目检清单。
|
||||
@@ -6,13 +6,13 @@ extends Node2D
|
||||
|
||||
const CHARGE_SKILL_IDS := ["heavy"]
|
||||
# 护甲类型图标占位——形状+色相双通道(art-bible):轻甲=◇ 灰石中性 / 魔法护甲=◆ 小人蓝 /
|
||||
# 重甲=▣ 深金(避开 Boss 专属紫赤与妨害紫)。(自旧 StandeeView 平移)
|
||||
# 重甲=▣ 深金(避开 Boss 专属紫赤与妨害紫)。
|
||||
const ARMOR_ICONS := {"light": "◇", "ward": "◆", "heavy": "▣"}
|
||||
# 名行/底座纵向预算余量(UI 设计对齐批 N2 2026-10-06——旧名行基线 = 盒底−1px「声明值刚好=容器值」
|
||||
# 同型高危写法返修;≥8px 硬余量 + 字形下探预算 ⇒ 12;取值不属色板,不进 ui_kit)
|
||||
# 名行/底座纵向预算余量(盒底−1px「声明值刚好=容器值」系高危写法;
|
||||
# ≥8px 硬余量 + 字形下探预算 ⇒ 12;取值不属色板,不进 ui_kit)
|
||||
const STANDEE_FOOT_MARGIN := 12.0
|
||||
|
||||
var box := Vector2(150, 176) # 站桩盒尺寸(旧 Control custom_minimum_size;实际高随壳层 row 布局注入)
|
||||
var box := Vector2(150, 176) # 站桩盒尺寸(实际高随壳层 row 布局注入)
|
||||
var font: Font = null # 世界字体槽位(null = ThemeDB.fallback_font——world.world_font 注入)
|
||||
var slot_row: StatusSlotRow # 状态效果唯一展示面 = 壳层伴随件(挂 BattleStageView 壳层,本体进世界)
|
||||
|
||||
@@ -20,10 +20,10 @@ var _kind := "enemy"
|
||||
var _portrait_sprite: Sprite2D = null # 资产挂点(null = 未落位,字形在位)
|
||||
var _portrait_tex: Texture2D = null # 落位纹理引用(set_portrait_texture 装载;字形回退判据)
|
||||
var _portrait_flip := false
|
||||
# 主角三态切换(迭代 6 T3):state 名 → 纹理(battle_stage_view 组合点注册);缺态=保持现纹理
|
||||
# 主角三态切换:state 名 → 纹理(battle_stage_view 组合点注册);缺态=保持现纹理
|
||||
# (缺位回退链延伸——三态素材缺任一不炸不空)。敌站桩不注册不消费。
|
||||
var _hero_tex := {}
|
||||
enum HeroState { IDLE, RESET, ATTACK, HIT } # 主角三态+复位哨兵(迭代 27 T6——裸字符串 enum 化;外部 String 契约=register/set 形参零变化)
|
||||
enum HeroState { IDLE, RESET, ATTACK, HIT } # 主角三态+复位哨兵(外部 String 契约=register/set 形参零变化)
|
||||
const _HS_NAME := {HeroState.IDLE: "idle", HeroState.RESET: "reset", HeroState.ATTACK: "attack", HeroState.HIT: "hit"}
|
||||
const _HS_VALUE := {"idle": HeroState.IDLE, "reset": HeroState.RESET, "attack": HeroState.ATTACK, "hit": HeroState.HIT}
|
||||
var _hero_state: int = HeroState.IDLE
|
||||
@@ -32,11 +32,11 @@ var _order := 1
|
||||
var _intent_glyph := ""
|
||||
var _intent_col := Color.WHITE
|
||||
var _intent_visible := false
|
||||
var _intent_scale := 1.0: # 意图弹入动画参数(旧 tween scale 等价)
|
||||
var _intent_scale := 1.0: # 意图弹入动画参数
|
||||
set(v):
|
||||
_intent_scale = v
|
||||
queue_redraw()
|
||||
var _glyph_rot := 0.0: # 立绘前倾角(旧只转字形 Label——贴图态无此动画,等价平移)
|
||||
var _glyph_rot := 0.0: # 立绘前倾角(贴图态无此动画)
|
||||
set(v):
|
||||
_glyph_rot = v
|
||||
queue_redraw()
|
||||
@@ -45,7 +45,7 @@ var _enemy_id := ""
|
||||
var _armor := "light"
|
||||
var _dead := false
|
||||
var _hp_max := 1.0
|
||||
var _hp_cur := 0 # 血量数字显示面(HUD框架 验收「血量数字可读」——2026-09-26 用户点名补数)
|
||||
var _hp_cur := 0 # 血量数字显示面(HUD框架 验收「血量数字可读」)
|
||||
var hp_value := 1.0: # 血条值 0..1(直角递减——tween_property 目标,逐行刷新重启不累积)
|
||||
set(v):
|
||||
hp_value = clampf(v, 0.0, 1.0)
|
||||
@@ -66,21 +66,21 @@ func display_name() -> String:
|
||||
return _display_name
|
||||
|
||||
|
||||
# 血条存在性探针出口(T5 断言等价改写——旧 _hp_bar ProgressBar 退役,世界直角血条常在)
|
||||
# 血条存在性探针出口(世界直角血条常在)
|
||||
var has_world_hp_bar := true
|
||||
|
||||
|
||||
# 意图徽标探针出口(T5——旧 _intent_badge.visible/text 改判)
|
||||
# 意图徽标探针出口
|
||||
func intent_probe() -> Dictionary:
|
||||
return {"visible": _intent_visible, "text": _intent_glyph}
|
||||
|
||||
|
||||
# 名字行探针出口(T5——旧 _name_label/_armor_icon.text 改判)
|
||||
# 名字行探针出口
|
||||
func name_line_probe() -> Dictionary:
|
||||
return {"name": _display_name, "armor_icon": String(ARMOR_ICONS.get(_armor, "◇"))}
|
||||
|
||||
|
||||
# ---- setter 面(投影契约与旧 StandeeView 一致——presenter/组合根零改排) ----
|
||||
# ---- setter 面(presenter/组合根零改排) ----
|
||||
|
||||
# data = 战斗结算 presentation_snapshot enemies[] 投影:{order, display_name, hp, max_hp, enemy_id,
|
||||
# pending_skill/pending_type/pending_name, effects[](敌方 side 增益——各立绘同值呈现)}
|
||||
@@ -106,7 +106,7 @@ func set_hero(hp: int, max_hp: int) -> void:
|
||||
_revive() # 新战斗开场 HP 回正——主角死亡残位随之复活(敌侧 set_data 同款语义)
|
||||
|
||||
|
||||
# 血条短 tween 递减(直角递减占位——密度/缓动终稿归战斗表现批);逐行刷新时重启 tween 不累积
|
||||
# 血条短 tween 递减(直角递减占位);逐行刷新时重启 tween 不累积
|
||||
func _set_hp(hp: int) -> void:
|
||||
_hp_cur = clampi(hp, 0, int(_hp_max))
|
||||
var target := float(_hp_cur) / _hp_max
|
||||
@@ -161,22 +161,22 @@ func set_pending(ptype: String, pid: String) -> void:
|
||||
queue_redraw()
|
||||
return
|
||||
var glyph := "⚔" # 攻击系(damage)
|
||||
var col: Color = UiKit.RED_MINE # 攻击预告红(危险红族——封印朱退役 2026-09-26,预告语义归封印色独占〔现行封印黑〕)
|
||||
var col: Color = UiKit.RED_MINE # 攻击预告红(危险红族——预告语义归封印色独占〔现行封印黑〕)
|
||||
if ptype == "buff":
|
||||
glyph = "✦" # 增益系
|
||||
col = UiKit.HERO_BLUE_DEEP # 深档墨(N1-4——浅垫牌上 ≥4.5:1;原 HERO_BLUE 亮档对垫牌 2.5:1)
|
||||
col = UiKit.HERO_BLUE_DEEP # 深档墨(浅垫牌上 ≥4.5:1)
|
||||
elif ptype == "debuff":
|
||||
glyph = "☠" # 妨害系(新效应词汇批 2026-09-19)
|
||||
glyph = "☠" # 妨害系
|
||||
col = UiKit.DEBUFF_PURPLE
|
||||
if pid in CHARGE_SKILL_IDS:
|
||||
col = UiKit.GOLD_INK # 重击类蓄力档(深金墨 N1——浅垫牌上 ≥4.5:1;原 GOLD_DEEP 2.5:1)
|
||||
col = UiKit.GOLD_INK # 重击类蓄力档(深金墨——浅垫牌上 ≥4.5:1)
|
||||
_intent_glyph = glyph
|
||||
_intent_col = col
|
||||
_intent_visible = true
|
||||
_play_intent_motion()
|
||||
|
||||
|
||||
# 意图三档动作动画终稿(样张 v2 冻结批 T3——§4.6.4 三档:①普通攻击=零表示〔set_pending 早退〕
|
||||
# 意图三档动作动画(§4.6.4 三档:①普通攻击=零表示〔set_pending 早退〕
|
||||
# ②特殊准备=徽标弹入+二次弹跳(高举语义,运动手段 §2)③重击/硬化=缓推蓄力+定格微顿+回落;
|
||||
# 三档运动签名互异,灰度/剪影层可辨)。摇定时一次性短动画,随后静帧保持。
|
||||
func _play_intent_motion() -> void:
|
||||
@@ -194,7 +194,7 @@ func _play_intent_motion() -> void:
|
||||
tw.tween_property(self, "_intent_scale", 1.0, UiKit.motion(0.13))
|
||||
|
||||
|
||||
# 资产挂点(战斗表现样张批):tex 落位后字形隐退、纹理立绘顶位——零代码换装契约;tex=null 回退字形。
|
||||
# 资产挂点:tex 落位后字形隐退、纹理立绘顶位——零代码换装契约;tex=null 回退字形。
|
||||
# flip = 立绘朝向翻转(素材基准朝左时传 true 呈现朝右)。盒高 96px = 48px 立绘源整数 2×
|
||||
# (圣经 §8.2 禁非整数缩放)——源换代时随源重算。
|
||||
func set_portrait_texture(tex: Texture2D, flip: bool = false) -> void:
|
||||
@@ -208,7 +208,7 @@ func set_portrait_texture(tex: Texture2D, flip: bool = false) -> void:
|
||||
if _portrait_sprite == null:
|
||||
_portrait_sprite = Sprite2D.new()
|
||||
_portrait_sprite.centered = false
|
||||
_portrait_sprite.texture_filter = CanvasItem.TEXTURE_FILTER_NEAREST # 像素帧放大防糊(棋盘小人贴图批 2026-09-25)
|
||||
_portrait_sprite.texture_filter = CanvasItem.TEXTURE_FILTER_NEAREST # 像素帧放大防糊
|
||||
add_child(_portrait_sprite)
|
||||
_portrait_sprite.texture = tex
|
||||
_portrait_sprite.flip_h = flip
|
||||
@@ -217,7 +217,7 @@ func set_portrait_texture(tex: Texture2D, flip: bool = false) -> void:
|
||||
|
||||
|
||||
func _place_portrait() -> void:
|
||||
# 立绘盒 = (0,44)-(box.x, box.y-36)(旧 FULL_RECT offsets 等价);48 源 ×2 整数档居中
|
||||
# 立绘盒 = (0,44)-(box.x, box.y-36);48 源 ×2 整数档居中
|
||||
if _portrait_sprite == null or _portrait_tex == null:
|
||||
return
|
||||
var box_h := box.y - 44.0 - 36.0
|
||||
@@ -238,16 +238,16 @@ func mark_dead() -> void:
|
||||
tw.tween_property(self, "rotation_degrees", 0.0, UiKit.motion(0.08))
|
||||
_spawn_death_shards() # 碎裂退场(§4.6.4 语义硬规则:灰化+碎裂禁瞬间消失;§8.4 v2 冻结 5 块 0.45s)
|
||||
_refresh_portrait_glyph()
|
||||
_intent_visible = false # 死亡残位意图退位(旧同款)
|
||||
_intent_visible = false # 死亡残位意图退位
|
||||
_set_hp(0)
|
||||
if slot_row != null:
|
||||
slot_row.visible = false # 状态行随 mark_dead 同拍隐藏(批内代拍板②待追认)
|
||||
slot_row.visible = false # 状态行随 mark_dead 同拍隐藏
|
||||
|
||||
|
||||
# ---- 演出密度终稿(样张 v2 冻结批 T3——程序化 tween,零新帧资产) ----
|
||||
# ---- 演出密度(程序化 tween,零新帧资产) ----
|
||||
|
||||
const DEATH_SHARD_N := 5 # 碎块数(§8.4 冻结值)
|
||||
const HIT_SPARK_N := 6 # 命中火花数(迭代 6 T4 终稿——打击瞬间可读性一拍)
|
||||
const HIT_SPARK_N := 6 # 命中火花数(打击瞬间可读性一拍)
|
||||
var _death_shards := 0 # 探针出口计数(碎块节点回收后仍保留读数)
|
||||
var _hit_sparks := 0 # 探针出口计数(命中火花累计)
|
||||
var _flash_count := 0 # 探针出口计数(受击闪白触发次数——主角路由断言面)
|
||||
@@ -279,7 +279,7 @@ func fx_probe() -> Dictionary:
|
||||
return {"death_shards": _death_shards, "hit_sparks": _hit_sparks, "flash_count": _flash_count}
|
||||
|
||||
|
||||
# 命中瞬间打击特效(迭代 6 T4 终稿):立绘盒中心六枚短火花放射(程序化色块,确定性方向
|
||||
# 命中瞬间打击特效:立绘盒中心六枚短火花放射(程序化色块,确定性方向
|
||||
# i*60°——禁全局随机;亮金/暖白交替=物理打击语义,不占 危险红=雷 与 奖励金=收益 的语义位)。
|
||||
# 弹道到达拍由 battle_stage_view 调度(on_step_row_fx 延迟 0.33s≈弹道飞行)。
|
||||
func play_hit_impact() -> void:
|
||||
@@ -304,7 +304,7 @@ func is_dead() -> bool:
|
||||
return _dead
|
||||
|
||||
|
||||
# 受击闪白(结算演出占位——§2.2 受击反馈;强度时长复核迭代 6 T4=维持现行)
|
||||
# 受击闪白(结算演出占位——§2.2 受击反馈;强度时长维持现行)
|
||||
func flash_hit() -> void:
|
||||
_flash_count += 1
|
||||
if _hp_flash_tw != null and _hp_flash_tw.is_valid():
|
||||
@@ -332,8 +332,8 @@ func _refresh_portrait_glyph() -> void:
|
||||
|
||||
|
||||
func _portrait_glyph() -> String:
|
||||
# 字形回退已退役为纯缺位兜底(迭代 6 T2——八敌真图落位;旧三档体量字形 ▂/▆/█ 随退役,
|
||||
# 相对体量语义由 48px 档统一比例承载)。缺位态:主角「人」/死亡「✕」/其余「敌」。
|
||||
# 字形回退仅作缺位兜底(八敌真图落位;相对体量语义由 48px 档统一比例承载)。
|
||||
# 缺位态:主角「人」/死亡「✕」/其余「敌」。
|
||||
if _kind == "hero":
|
||||
return "人"
|
||||
if _dead:
|
||||
@@ -341,12 +341,12 @@ func _portrait_glyph() -> String:
|
||||
return "敌"
|
||||
|
||||
|
||||
# 立绘落位探针出口(迭代 6 T2——站桩纹理非空断言面;缺位回退链保持的对照面)
|
||||
# 立绘落位探针出口(站桩纹理非空断言面;缺位回退链保持的对照面)
|
||||
func portrait_probe() -> Dictionary:
|
||||
return {"has_tex": _portrait_sprite != null and _portrait_sprite.texture != null}
|
||||
|
||||
|
||||
# ---- 主角三态切换(迭代 6 T3——攻击演出→attack/受击→hit/结算回 idle;§8.4 帧数档挂点) ----
|
||||
# ---- 主角三态切换(攻击演出→attack/受击→hit/结算回 idle;§8.4 帧数档挂点) ----
|
||||
|
||||
# 组合点注册(battle_stage_view _init——UiKit 常量 load_tex,缺位 null 不注册=该态保持现纹理)
|
||||
func register_hero_state(state: String, tex: Texture2D) -> void:
|
||||
@@ -399,14 +399,14 @@ func _pedestal_rect() -> Rect2:
|
||||
|
||||
|
||||
# 绘制内容矩形(世界局部;ui_struct_probe ⑥standee「不出框+下余量≥8px」断言读数面——
|
||||
# 最低绘制元素 = 底座下缘;N2 纵向余量纪律 2026-10-06)
|
||||
# 最低绘制元素 = 底座下缘)
|
||||
func content_rect() -> Rect2:
|
||||
return Rect2(0.0, 0.0, box.x, _pedestal_rect().end.y)
|
||||
|
||||
|
||||
func _draw() -> void:
|
||||
var f := _efont()
|
||||
# 椭圆落影(圣经 v3.5 批 B4 2026-09-30——§6.1 地台落影压脚:方向稿椭圆 (0,0,0,70),压在地台
|
||||
# 椭圆落影(圣经 §6.1 地台落影压脚:方向稿椭圆 (0,0,0,70),压在地台
|
||||
# 上缘周围;24 段多边形近似椭圆,中心=台面上方 6px、横半径=台宽一半)
|
||||
var sh_center := Vector2(box.x * 0.5, box.y - 24.0)
|
||||
var sh_rx := (box.x - 36.0) * 0.5
|
||||
@@ -417,13 +417,13 @@ func _draw() -> void:
|
||||
pts.append(sh_center + Vector2(cos(ang) * sh_rx, sin(ang) * sh_ry))
|
||||
draw_colored_polygon(pts, Color(0, 0, 0, 0.27))
|
||||
# 石板地台(v3.5——方向稿取色:板身/描边/顶缘受光线 2px——取色单点收 UiKit.STAND_STONE 族
|
||||
# (T4 裸色收口);几何沿用现行底座位〔margin 26/高 10→方向稿 18/12——按方向稿对齐〕)
|
||||
# ;几何沿用现行底座位〔margin 18/高 12——按方向稿对齐〕)
|
||||
var ped := _pedestal_rect()
|
||||
draw_rect(ped, UiKit.STAND_STONE, true)
|
||||
draw_rect(ped, UiKit.STAND_STONE_EDGE, false, 1.0)
|
||||
draw_rect(Rect2(ped.position.x, ped.position.y, ped.size.x, 2.0), UiKit.STAND_STONE_RIM, true)
|
||||
|
||||
# 血条(直角递减):敌附顺位徽标(GOLD_BRIGHT ①②③——迭代 26 T3 换墨,旧 GOLD_DEEP 对舞台底 3.3:1 越带)——旧 top VBox 几何等价(左 8 / 右 -8)
|
||||
# 血条(直角递减):敌附顺位徽标(GOLD_BRIGHT ①②③);血条几何左 8 / 右 -8
|
||||
var bar_x := 8.0
|
||||
if _kind == "enemy":
|
||||
var order_txt := String("①②③"[clampi(_order - 1, 0, 2)])
|
||||
@@ -439,7 +439,7 @@ func _draw() -> void:
|
||||
# 数字=目标值即时刷新,条 tween 只管填充宽度;死残位 0/max 照常可读)
|
||||
var hp_txt := "%d/%d" % [_hp_cur, int(_hp_max)]
|
||||
var hts: Vector2 = f.get_string_size(hp_txt, HORIZONTAL_ALIGNMENT_LEFT, -1, 11)
|
||||
# 对比度垫牌(UI 设计对齐批 N1-1/N1-2 2026-10-06——白 α.95 直压蓝条 2.67:1 违 §7.2;
|
||||
# 对比度垫牌(白 α.95 直压蓝条 2.67:1 违 §7.2;
|
||||
# 中央垫 HP_BAR_BACK 深素牌 = 14.3:1;牌宽=字宽+8〔白名单取 8〕,高=条带全高;§4.1 数字色牌原则)
|
||||
draw_rect(Rect2(bar_x + (bar_w - hts.x) * 0.5 - 4.0, 2.0, hts.x + 8.0, 10.0), UiKit.HP_BAR_BACK, true)
|
||||
var hbase := Vector2(bar_x + (bar_w - hts.x) * 0.5, 2.0 + 5.0 + 4.0)
|
||||
@@ -451,9 +451,9 @@ func _draw() -> void:
|
||||
var prect := _portrait_rect()
|
||||
var glyph := _portrait_glyph()
|
||||
var center := prect.get_center()
|
||||
# 坐标系注记(2026-09-26 错位修复):draw_set_transform 原点已置于 center——draw_string 的
|
||||
# 坐标系注记:draw_set_transform 原点已置于 center——draw_string 的
|
||||
# pos 是**变换后的局部坐标**,须传相对 center 的偏移(-w/2 水平居中、+0.35h 基线下移);
|
||||
# 旧实现传 center-… = 双重偏移,字形画到盒外(实机敌人立绘区空白的真因,截图取证)
|
||||
# 传绝对 center 会成双重偏移、把字形画到盒外。
|
||||
draw_set_transform(center, deg_to_rad(_glyph_rot), Vector2.ONE)
|
||||
var ts := f.get_string_size(glyph, HORIZONTAL_ALIGNMENT_LEFT, -1, 42)
|
||||
f.draw_string(get_canvas_item(), Vector2(-ts.x * 0.5, ts.y * 0.35), glyph,
|
||||
@@ -465,7 +465,7 @@ func _draw() -> void:
|
||||
var icenter := Vector2(box.x * 0.5, 86.0)
|
||||
draw_set_transform(icenter, 0.0, Vector2(_intent_scale, _intent_scale))
|
||||
var its := f.get_string_size(_intent_glyph, HORIZONTAL_ALIGNMENT_LEFT, -1, 15)
|
||||
# 对比度垫牌(UI 设计对齐批 N1-3/N1-4 2026-10-06——四色墨直压立绘纹理/暗舞台底 1.5-3.3:1 违 §7.2
|
||||
# 对比度垫牌(四色墨直压立绘纹理/暗舞台底 1.5-3.3:1 违 §7.2
|
||||
# 「文字禁直压纹理底」;垫 PANEL_DATA 素牌后 atk 5.3/buf 4.8/debuff 4.7/charge 4.9 全 ≥4.5:1;
|
||||
# atk/debuff 零换墨、buf 换 HERO_BLUE_DEEP、charge 换 GOLD_INK;随弹入 transform 同步缩放)
|
||||
draw_rect(Rect2(-its.x * 0.5 - 4.0, -its.y * 0.75, its.x + 8.0, its.y * 1.1), UiKit.PANEL_DATA, true)
|
||||
@@ -473,19 +473,19 @@ func _draw() -> void:
|
||||
HORIZONTAL_ALIGNMENT_LEFT, -1, 15, _intent_col) # 局部坐标(同上注记——双重偏移修复)
|
||||
draw_set_transform(Vector2.ZERO, 0.0, Vector2.ONE)
|
||||
|
||||
# 底座下 护甲类型图标+名字 常驻行(敌 only——2026-09-26 用户终裁:AC 数值不展示,类型图标化)
|
||||
# 底座下 护甲类型图标+名字 常驻行(敌 only——AC 数值不展示,类型图标化)
|
||||
if _kind == "enemy":
|
||||
var armor_txt := String(ARMOR_ICONS.get(_armor, "◇"))
|
||||
var col: Color = UiKit.WARM_FAINT # 轻甲 = 暖白弱化(v3.5 暗底双色墨制——原灰石对暗底不可读)
|
||||
var col: Color = UiKit.WARM_FAINT # 轻甲 = 暖白弱化(v3.5 暗底双色墨制)
|
||||
match _armor:
|
||||
"ward":
|
||||
col = UiKit.HERO_BLUE_BRIGHT # 魔法护甲 = 小人蓝亮档(迭代 26 T3 换墨,旧 HERO_BLUE 对舞台底 3.2:1 越带)
|
||||
col = UiKit.HERO_BLUE_BRIGHT # 魔法护甲 = 小人蓝亮档
|
||||
"heavy":
|
||||
col = UiKit.GOLD_BRIGHT # 重甲 = 金族亮档(迭代 26 T3 换墨,旧 GOLD_DEEP 对舞台底 3.3:1 越带)
|
||||
col = UiKit.GOLD_BRIGHT # 重甲 = 金族亮档
|
||||
var name_w := f.get_string_size(_display_name, HORIZONTAL_ALIGNMENT_LEFT, -1, 12).x
|
||||
var armor_w := f.get_string_size(armor_txt, HORIZONTAL_ALIGNMENT_LEFT, -1, 12).x
|
||||
var total_w := armor_w + 3.0 + name_w
|
||||
var baseline := Vector2((box.x - total_w) * 0.5, box.y - 1.0 - STANDEE_FOOT_MARGIN) # N2 余量纪律(2026-10-06——旧值 box.y-1.0 离盒底 1px)
|
||||
var baseline := Vector2((box.x - total_w) * 0.5, box.y - 1.0 - STANDEE_FOOT_MARGIN) # 纵向余量纪律
|
||||
f.draw_string(get_canvas_item(), baseline, armor_txt, HORIZONTAL_ALIGNMENT_LEFT, -1, 12, col)
|
||||
f.draw_string(get_canvas_item(), baseline + Vector2(armor_w + 3.0, 0), _display_name,
|
||||
HORIZONTAL_ALIGNMENT_LEFT, -1, 12, UiKit.WARM) # v3.5 暗底双色墨制(原 INK_TITLE 深墨对暗底不可读)
|
||||
HORIZONTAL_ALIGNMENT_LEFT, -1, 12, UiKit.WARM) # v3.5 暗底双色墨制
|
||||
@@ -73,7 +73,7 @@ func open_status(data: Dictionary) -> void:
|
||||
|
||||
|
||||
# 道具工坊六行(解锁态文案与营地柜台同口径——退役/封存/已解锁/未解锁;进度=解锁判定量读数)
|
||||
# 秘宝匣 12 行只读概览(迭代 22 T2 换代——旧工坊六行退役;数据源=treasure_shop_status 投影,
|
||||
# 秘宝匣 12 行只读概览(数据源=treasure_shop_status 投影,
|
||||
# 解锁态文案与营地柜台同口径;进度列=解锁混合制三形态:钥石价/条件进度拼接/已入手)
|
||||
func _rebuild_workshop(data: Dictionary) -> void:
|
||||
for c in _workshop_holder.get_children():
|
||||
|
||||
@@ -5,12 +5,10 @@ class_name StatusSlotRow
|
||||
extends HBoxContainer
|
||||
|
||||
const MAX_SLOTS := 4
|
||||
# 纹理映射(2026-09-24 状态图标接入批):effect_id→定稿纹理,全局恒定禁换图(图标键契约 §4.6 的
|
||||
# 纹理映射:effect_id→定稿纹理,全局恒定禁换图(图标键契约 §4.6 的
|
||||
# 文件侧延伸——文件键 = status_<effect_id>.png,命名规范圣经 §8.3「对象键 = 数据表主键」)。
|
||||
# 状态表九行全映射(attack_up/ac_up/weaken/alert/enrage/off_guard/sapped/empower/fortify)
|
||||
# ——HUD框架 图标键契约"一键一枚专属纹样,查不到的键立即断言 + 占位 + 诊断"
|
||||
# (2026-09-15 格面资源批补 sapped;2026-09-16 护甲等级掷点批 defense_up → ac_up 换装;
|
||||
# 同日 敌人技能池批补 empower/fortify 敌方增益两键——敌方增益显示 = 敌卡状态行文本 + 本图标键词汇)。
|
||||
const EFFECT_ICON := {
|
||||
"attack_up": "res://assets/ui/icons/status_attack_up.png",
|
||||
"ac_up": "res://assets/ui/icons/status_ac_up.png",
|
||||
@@ -47,7 +45,7 @@ var _tip: PanelContainer
|
||||
var _tip_label: Label
|
||||
var _slots: Array[PanelContainer] = [] # 槽引用数组持有——tooltip _tip 悬停后入子节点表,按位序 get_child 会误取(BUG-0001)
|
||||
var _cleared_feedback := false
|
||||
var compact := false # B 区站桩迷你档(2026-09-26 C 区精简批):16px 图标+层数角标、无 meta 行——tooltip 义务随组件迁入站桩
|
||||
var compact := false # B 区站桩迷你档:16px 图标+层数角标、无 meta 行——tooltip 义务随组件迁入站桩
|
||||
|
||||
|
||||
func _init() -> void:
|
||||
@@ -73,8 +71,8 @@ func set_effects(effects: Array) -> void:
|
||||
_slots[i].visible = true
|
||||
|
||||
|
||||
# battle_over 反馈(增益状态×4④):逐槽化光收回 + 微灰化(下次 set_effects 恢复常态;
|
||||
# 2026-09-24 状态图标接入批:原字形 "✕ " 前缀随占位字形退役,灰化即清场反馈)
|
||||
# battle_over 反馈:逐槽化光收回 + 微灰化(下次 set_effects 恢复常态;
|
||||
# 灰化即清场反馈)
|
||||
func set_cleared_feedback() -> void:
|
||||
_cleared_feedback = true
|
||||
for slot in _slots:
|
||||
@@ -98,7 +96,7 @@ func _sync_slots(n: int) -> void:
|
||||
func _make_slot() -> PanelContainer:
|
||||
var slot := PanelContainer.new()
|
||||
if compact:
|
||||
# 槽底不透明(2026-09-26 用户反馈:图标显示不清晰——半透明底叠 B 区米色面板吃掉白图标对比度;
|
||||
# 槽底不透明(图标显示不清晰——半透明底叠 B 区米色面板吃掉白图标对比度;
|
||||
# 改深墨棕实底 = 白色纹样图标对比承载,槽界同时可辨)
|
||||
slot.add_theme_stylebox_override("panel",
|
||||
UiKit.panel_flat(UiKit.SLOT_BACKDROP, UiKit.PANEL_BORDER,UiKit.RADIUS_OBJECT, 1, Vector4(2, 1, 2, 1)))
|
||||
@@ -112,7 +110,7 @@ func _make_slot() -> PanelContainer:
|
||||
if compact:
|
||||
col.add_theme_constant_override("separation", 0)
|
||||
slot.add_child(col)
|
||||
# 定稿纹理图标(P6 甲③ 2026-10-06:32px 源 → 常规档 32px=1×;mini(站桩)档 16px=0.5×
|
||||
# 定稿纹理图标(32px 源 → 常规档 32px=1×;mini(站桩)档 16px=0.5×
|
||||
# 共用 §8.2 例外登记——线性过滤匹配抗锯齿矢量风源图)
|
||||
var icon := TextureRect.new()
|
||||
icon.custom_minimum_size = Vector2(32, 32) if not compact else Vector2(16, 16)
|
||||
@@ -184,7 +182,7 @@ func _on_slot_hover(slot: Control) -> void:
|
||||
|
||||
|
||||
# 图标键解析(HUD框架:查不到的键按错误处理——立即断言 + 占位 + 诊断,禁止静默显示空图标)
|
||||
# 装载走 UiKit.load_tex 守卫(呈现层素材 preload 红线=0,UI 设计对齐批 N3 2026-10-06——
|
||||
# 装载走 UiKit.load_tex 守卫(呈现层素材 preload 红线=0——
|
||||
# 素材缺位树上 preload 直接断言炸脚本;路径串表 + exists 守卫,缺位 null 走占位回退)
|
||||
func _icon_tex(effect_id: String) -> Texture2D:
|
||||
if EFFECT_ICON.has(effect_id):
|
||||
|
||||
@@ -7,7 +7,7 @@ extends Control
|
||||
const LIFE_SEC := 2.5
|
||||
const MAX_SLOTS := 3
|
||||
const COL_INFO := UiKit.INK
|
||||
const COL_WARN := UiKit.RED_MINE # 危险红族——拒绝/作废(封印朱退役 2026-09-26)
|
||||
const COL_WARN := UiKit.RED_MINE # 危险红族——拒绝/作废
|
||||
const COL_GOLD := UiKit.GOLD_DEEP # 奖励金族——提示/达成(深金档)
|
||||
|
||||
var _slot_box: VBoxContainer
|
||||
|
||||
+112
-116
@@ -3,14 +3,14 @@
|
||||
class_name UiKit
|
||||
extends RefCounted
|
||||
|
||||
# ── 色板(语义族;值 = 2026-09-20 收敛时现值) ──
|
||||
# ── 色板(语义族) ──
|
||||
const INK := Color8(51, 38, 27) # 墨族——正文/数字(对格底 ≥4.5:1)
|
||||
const INK_COLD := Color8(19, 46, 54) # 青墨 1-3 #132E36(数字墨三档·冷=稀疏——样张 v2 定稿 2026-09-28;亮瓦自适应深墨族档兼色牌后备路径)
|
||||
const INK_COLD := Color8(19, 46, 54) # 青墨 1-3 #132E36(数字墨三档·冷=稀疏;亮瓦自适应深墨族档兼色牌后备路径)
|
||||
const INK_OCHRE := Color8(60, 37, 12) # 赭墨 4-5 #3C250C(数字墨三档·中;亮瓦自适应深墨族档兼色牌后备路径)
|
||||
const INK_RUST := Color8(74, 30, 20) # 锈墨 6-8 #4A1E14(数字墨三档·暖=密集;亮瓦自适应深墨族档兼色牌后备路径)
|
||||
# ── 亮墨三档(圣经 v3.5 冻结④ 2026-09-30——数字直画已开格底:色温承袭密度语义,冷亮/暖白/金亮;
|
||||
# ── 亮墨三档(圣经 v3.5 冻结④——数字直画已开格底:色温承袭密度语义,冷亮/暖白/金亮;
|
||||
# 实测对比 6.0–6.3:1(A3a);金亮低饱和避奖励金语义;色牌后备组用深墨族上牌〔12:1〕。
|
||||
# 2026-10-01 修订:亮墨族限暗纹理板(dungeon),亮筋纹板按 _digit_ink_mode 自适应切深墨三档)──
|
||||
# 亮墨族限暗纹理板(dungeon),亮筋纹板按 _digit_ink_mode 自适应切深墨三档)──
|
||||
const INK_L_COLD := Color8(216, 236, 240) # 冷亮 1-3 #D8ECF0
|
||||
const INK_L_WARM := Color8(240, 226, 200) # 暖白 4-5 #F0E2C8(关卡屏 WARM 同族)
|
||||
const INK_L_GOLD := Color8(248, 227, 168) # 金亮 6-8 #F8E3A8
|
||||
@@ -19,41 +19,39 @@ const INK_TITLE := Color8(30, 26, 22) # 亮底标题墨
|
||||
const INK_ROUTE := Color8(24, 20, 16) # 节点图最深墨
|
||||
const INK_ON_GOLD := Color8(60, 44, 20) # 金底上的深墨
|
||||
|
||||
const PANEL_DATA := Color8(233, 217, 184) # 面板底·数据框(AP 条/状态槽)#E9D9B8(P7 乙 2026-10-06——demo 唯一来源对齐,Δ≤6/255)
|
||||
const PANEL_BORDER := Color8(120, 110, 88) # 面板细线框;兼熄灭点阵(DOT_OFF 别名)#786E58(P7 乙同上)
|
||||
const PANEL_DATA := Color8(233, 217, 184) # 面板底·数据框(AP 条/状态槽)#E9D9B8(demo 唯一来源对齐,Δ≤6/255)
|
||||
const PANEL_BORDER := Color8(120, 110, 88) # 面板细线框;兼熄灭点阵(DOT_OFF 别名)#786E58(demo 唯一来源对齐,Δ≤6/255)
|
||||
|
||||
const GOLD := Color8(240, 168, 40) # 奖励金
|
||||
const GOLD_DEEP := Color8(176, 122, 20) # 深金(金框描边/达意金)
|
||||
const GOLD_INK := Color8(116, 80, 11) # 深金墨(意图蓄力徽标墨——PANEL_DATA 垫牌上 4.9:1,UI 设计对齐批 N1 2026-10-06)
|
||||
const GOLD_BRIGHT := Color8(224, 184, 96) # 金族亮档(迭代 26 T3——敌顺位徽标①②③/重甲徽标对暗舞台底 6.5:1;值=收益金 e0b860 同门;GOLD_DEEP 语义岗〔standee:199 意图判据/board_view:35/level_map:237〕在役不动)
|
||||
const GOLD_INK := Color8(116, 80, 11) # 深金墨(意图蓄力徽标墨——PANEL_DATA 垫牌上 4.9:1)
|
||||
const GOLD_BRIGHT := Color8(224, 184, 96) # 金族亮档(敌顺位徽标①②③/重甲徽标对暗舞台底 6.5:1;值=收益金 e0b860 同门;GOLD_DEEP 语义岗〔standee:199 意图判据/board_view:35/level_map:237〕在役不动)
|
||||
const GOLD_LIT := Color8(230, 190, 90) # 亮金(候选高亮)
|
||||
const GOLD_DIM := Color8(140, 116, 60) # 暗金(已过节点)
|
||||
const GOLD_DONE := Color8(104, 86, 44) # 已过节点暗金·关卡画面呈现态(关卡画面样张 v1 冻结新值 #68562C——旧 GOLD_DIM #8C743C 对双色墨均 <4.5:1 对比硬指标逼守;GOLD_DIM 他用不动)
|
||||
const GOLD_DONE := Color8(104, 86, 44) # 已过节点暗金·关卡画面呈现态 #68562C(GOLD_DIM #8C743C 对双色墨均 <4.5:1,对比硬指标逼守此值;GOLD_DIM 他用不动)
|
||||
|
||||
const SEAL_BLACK := Color8(0, 0, 0) # 封印黑 #000000——封印(封印)宣告色(2026-09-28
|
||||
# 用户拍板封印朱→封印紫→封印黑:紫色与微光蓝族色相冲突,黑色无色相天然避开禁入带
|
||||
# 妨害紫 281° 各留 12-14° 间距;候选值样张批冻结;封印朱 #C63D22 退役)
|
||||
const SEAL_BLACK := Color8(0, 0, 0) # 封印黑 #000000——封印宣告色(紫色与微光蓝族色相冲突故弃;
|
||||
# 黑色无色相,天然避开禁入带——与妨害紫 281° 各留 12-14° 间距)
|
||||
const RED_DEEP := Color8(150, 60, 48) # 深朱(敌卡框/敌像)
|
||||
const RED_MINE := Color8(150, 44, 36) # 引爆雷字朱(已开危险格"雷"字)
|
||||
const RED_FORBID := Color8(120, 60, 50) # 禁止框朱
|
||||
const RED_ALERT := Color8(200, 60, 48) # 报错朱
|
||||
|
||||
const HERO_BLUE := Color8(46, 134, 222) # 小人蓝 #2E86DE(样张 v2 定稿 2026-09-28——圣经 §4.1 候选值冻结;旧 #2E6E8E 收敛期快照退役)
|
||||
const HERO_BLUE_DEEP := Color8(30, 90, 150) # 小人蓝同族深档(意图增益徽标墨——PANEL_DATA 垫牌上 4.8:1;命名同 CONTACT_GLOW_DEEP 规则,UI 设计对齐批 N1 2026-10-06)
|
||||
const HERO_BLUE_BRIGHT := Color8(126, 186, 240) # 小人蓝同族亮档(迭代 26 T3——魔法护甲徽标对暗舞台底 5.9:1;HERO_BLUE 语义岗〔board_view:35 小人边框/level_map:237〕在役不动)
|
||||
const HERO_BLUE := Color8(46, 134, 222) # 小人蓝 #2E86DE(圣经 §4.1 冻结值)
|
||||
const HERO_BLUE_DEEP := Color8(30, 90, 150) # 小人蓝同族深档(意图增益徽标墨——PANEL_DATA 垫牌上 4.8:1;命名同 CONTACT_GLOW_DEEP 规则)
|
||||
const HERO_BLUE_BRIGHT := Color8(126, 186, 240) # 小人蓝同族亮档(魔法护甲徽标对暗舞台底 5.9:1;HERO_BLUE 语义岗〔board_view:35 小人边框/level_map:237〕在役不动)
|
||||
const BOSS_PURPLE := Color8(143, 47, 106) # 紫赤(Boss 专属)
|
||||
const DEBUFF_PURPLE := Color8(122, 66, 148) # 妨害紫(意图 ☠)
|
||||
|
||||
# ── 类型色五族(UI 设计对齐批 N4c 2026-10-06——值 = demo --tg-* 冻结抄录〔需求卡片 §2.2 抄录表〕;
|
||||
# 前四族 = 实机现值搬运、第五族群伤 demo 值补族——实机原完全缺位,消费面 = tg_band() 同源判定)──
|
||||
# ── 类型色五族(值 = demo --tg-* 冻结抄录〔需求卡片 §2.2 抄录表〕;消费面 = tg_band() 同源判定)──
|
||||
const TG_BUFF := Color8(194, 178, 100) # 通用增益/默认带
|
||||
const TG_HEAL := Color8(102, 164, 87) # 回复
|
||||
const TG_MAGIC := Color8(138, 114, 196) # 魔法
|
||||
const TG_PIERCE := Color8(87, 160, 160) # 穿甲
|
||||
const TG_AOE := Color8(226, 86, 118) # 群伤/AOE #E25676(demo --tg-aoe)
|
||||
|
||||
# 类型色带分族判定(N4c/Q4 甲——三选一与商店货架同源同判:id/kind 小写子串匹配,默认=通用增益带。
|
||||
# E5 回滚 2026-10-06:审计实证 demo「工兵爆破」卡=紫带(magic 族)——blast 归回 TG_MAGIC,
|
||||
# 类型色带分族判定(三选一与商店货架同源同判:id/kind 小写子串匹配,默认=通用增益带。
|
||||
# demo「工兵爆破」卡=紫带(magic 族)——blast 归 TG_MAGIC,
|
||||
# TG_AOE 只接 aoe 键(spark_aoe 消费面保留);max_hp 含 hp 归回复族)
|
||||
static func tg_band(id_lower: String) -> Color:
|
||||
if id_lower.contains("heal") or id_lower.contains("hp"):
|
||||
@@ -66,24 +64,24 @@ static func tg_band(id_lower: String) -> Color:
|
||||
return TG_PIERCE
|
||||
return TG_BUFF
|
||||
|
||||
# ── 未开格类型微光五组(迭代 30 卡①——HUD框架「未开格类型微光」节实装面;Q3 甲案「不并入」卡顶带
|
||||
# ── 未开格类型微光五组(HUD框架「未开格类型微光」节实装面;「不并入」卡顶带
|
||||
# TG_* 两套色带两套归组并存,禁互为引用)──
|
||||
# 色相 = art-bible §4.2 样张 v2 冻结五组(既裁项:本批无重裁资格,只允许按明度档做亮度缩放)。
|
||||
# 🔴 冻结面稽核(2026-10-17 契约 5 停机呈报后用户裁定):逐组 HSV 对照冻结值——BUFF/HEAL/PIERCE/
|
||||
# MAGIC 四组 ΔH ≤0.13°(8bit 量化噪声)、ΔS ≤0.002,属纯 V 缩放;群伤组初版 #FF1A31 曾 ΔH 5.40°
|
||||
# (朝纯红移动、侵蚀冻结绯对危险红的刻意错峰),用户裁「退回色相保持域」→ 改 #A61D37(冻结
|
||||
# #FF2D55 的 V=0.65 缩放,H=348.57°/S=0.824 逐位不动)。五组现均满足「只动明度、不动色相」。
|
||||
# 明度档 = 本批 Q2 甲案 provisional 值——补齐 §4.2 从未定义的「各组一档明度互辨」数值
|
||||
# (色弱灰度互辨通道;复核转正归战斗一屏样张批)。
|
||||
# 🔴 生成口径(2026-10-17 原型帧实测订正):阶梯必须在**渲染合成空间**(sRGB/伽马空间)里排——
|
||||
# 色相 = art-bible §4.2 冻结五组(色相既裁不可重裁,只允许按明度档做亮度缩放)。
|
||||
# 🔴 冻结面稽核:逐组 HSV 对照冻结值——BUFF/HEAL/PIERCE/
|
||||
# MAGIC 四组 ΔH ≤0.13°(8bit 量化噪声)、ΔS ≤0.002,属纯 V 缩放;群伤组 #A61D37(冻结
|
||||
# #FF2D55 的 V=0.65 缩放,H=348.57°/S=0.824 逐位不动,保持绯色相避开危险红)。
|
||||
# 五组均满足「只动明度、不动色相」。
|
||||
# 明度档 = provisional 值——补齐 §4.2 从未定义的「各组一档明度互辨」数值
|
||||
# (色弱灰度互辨通道)。
|
||||
# 🔴 生成口径:阶梯必须在**渲染合成空间**(sRGB/伽马空间)里排——
|
||||
# 引擎 canvas 混合按帧缓冲编码(伽马)做线性插值,色弱玩家屏幕上看到的是合成值而非色值本身。
|
||||
# 首版按 CIE L*(线性)排档曾把群伤/魔法排反(绯红线性亮于紫罗兰,但伽马空间绯红 G+B 通道低,
|
||||
# 合成后反而更暗——实测最弱对 CR 仅 1.05)。本版五档按合成空间单调阶梯重排:以「带内均值 ≈
|
||||
# 按 CIE L*(线性)排档会把群伤/魔法排反(绯红线性亮于紫罗兰,但伽马空间绯红 G+B 通道低,
|
||||
# 合成后反而更暗——实测最弱对 CR 仅 1.05)。五档按合成空间单调阶梯排列:以「带内均值 ≈
|
||||
# 0.49×色值 + 0.51×瓦片」合成模型反解,相邻组模型 CR = 1.245/1.234/1.239/1.270(判据线
|
||||
# 1.20;模型域与实测的差 = shader 距离场权重非扁平混合,故互辨判据的终审在渲染取证探针实测,
|
||||
# L1 棋盘探针逐对读数见 reports/iter30-实现报告读数表)。
|
||||
# 明度档只许在 provisional 域内自调(L3 预授权③一轮),翻动色相 = 触 art-bible §4.2 既裁冻结面
|
||||
# = L3 停机呈报(2026-10-17 群伤组一案即此路径:停机呈报 → 用户裁 → 批内执行)。
|
||||
# 明度档只许在 provisional 域内自调(L3 预授权③一轮),翻动色相 = 触 art-bible §4.2 冻结面
|
||||
# = L3 停机呈报。
|
||||
const TG_GLOW_BUFF := Color8(247, 223, 67) # 通用增益·芥末黄(attack/defense/weaken/action/gp)
|
||||
const TG_GLOW_HEAL := Color8(104, 208, 66) # 治疗·黄绿(heal)
|
||||
const TG_GLOW_PIERCE := Color8(48, 166, 166) # 穿刺·青(pierce)
|
||||
@@ -135,7 +133,7 @@ static func glow_color_for_group(g: int) -> Color:
|
||||
return Color(0, 0, 0, 0)
|
||||
|
||||
|
||||
# ── 呈现散件裸色搬运(UI 设计对齐批 N4a 2026-10-06——原值搬运零观感;本节立后 ui_kit 之外
|
||||
# ── 呈现散件裸色搬运(本节立后 ui_kit 之外
|
||||
# 呈现层裸 Color8 = 0,守卫 = ui_contrast_probe 静态段)──
|
||||
const INK_HINT := Color8(110, 69, 52) # 提示/警示暖棕墨(设置警示文/地图底栏组标)
|
||||
const INK_ON_DEEP := Color8(246, 238, 218) # 深底标题亮墨(主菜单标题/营地柜台等级章字)
|
||||
@@ -146,61 +144,60 @@ const HINT_STRIP_EDGE := Color8(74, 62, 48) # 棋盘底提示条描边(半
|
||||
const RAP_DOT_CORE_WARM := Color8(248, 226, 168) # AP 点阵亮心(烛光黄族)
|
||||
const RAP_DOT_LIT_BLUE := Color8(105, 161, 232) # RAP 点阵亮面(小人蓝族)
|
||||
const RAP_DOT_CORE_BLUE := Color8(156, 194, 240) # RAP 点阵亮心(小人蓝族)
|
||||
const E_FACADE_FOOT_COL := Color8(52, 42, 32) # 墙脚阴线(方向稿同色——原 board_cell_unit 局部)
|
||||
const FOG_MEMORY_PLATE := Color8(58, 58, 58) # 记忆板 #3A3A3A(§6.5——原 board_cell_unit 局部)
|
||||
const FOG_MEMORY_INK := Color8(196, 196, 196) # 记忆墨 #C4C4C4(对记忆板 ≈6.3:1——原局部)
|
||||
const FOG_OUTLINE := Color8(30, 24, 18) # 雾面轮廓墨(原 board_cell_unit _draw 局部 oc)
|
||||
const ICON_TINT_ON_GOLD := Color(24.0 / 240.0, 20.0 / 226.0, 16.0 / 200.0) # 当前金底图标墨 = WARM 分量乘法调制(双色墨制——原 level_map_screen 局部)
|
||||
const E_FACADE_FOOT_COL := Color8(52, 42, 32) # 墙脚阴线(方向稿同色)
|
||||
const FOG_MEMORY_PLATE := Color8(58, 58, 58) # 记忆板 #3A3A3A(§6.5)
|
||||
const FOG_MEMORY_INK := Color8(196, 196, 196) # 记忆墨 #C4C4C4(对记忆板 ≈6.3:1)
|
||||
const FOG_OUTLINE := Color8(30, 24, 18) # 雾面轮廓墨
|
||||
const ICON_TINT_ON_GOLD := Color(24.0 / 240.0, 20.0 / 226.0, 16.0 / 200.0) # 当前金底图标墨 = WARM 分量乘法调制(双色墨制)
|
||||
|
||||
const DUSK := Color8(90, 108, 140) # 暮蓝(全作唯一暮蓝档——失败终局)
|
||||
const DIM_NIGHT := Color8(24, 18, 12) # 终局压暗底 #18120C(demo 结算仪式暗纱——胜利侧 dim 基色)
|
||||
|
||||
const WARM := Color8(240, 226, 200) # 暖白(暗底正文)
|
||||
const WARM_SOFT := Color8(232, 213, 168) # 暖白次级(说明文)#E8D5A8(M0 换代——灰米 #C8BCA6 退役,配色总纲「米色去灰」)
|
||||
const WARM_FAINT := Color8(205, 184, 140) # 暖白弱化(帮助文)#CDB88C(M0 换代,同上)
|
||||
const WARM_SOFT := Color8(232, 213, 168) # 暖白次级(说明文)#E8D5A8(配色总纲「米色去灰」)
|
||||
const WARM_FAINT := Color8(205, 184, 140) # 暖白弱化(帮助文)#CDB88C(配色总纲「米色去灰」)
|
||||
|
||||
const STONE := Color8(120, 110, 98) # 灰石(通道/箭头/失效)
|
||||
const STONE_FAINT := Color8(140, 124, 100) # 灰石弱化(占位提示文)
|
||||
const STONE_DEEP := Color8(85, 77, 65) # 灰石深化(「已用」槽位墨——PANEL_DATA 槽牌 5.6:1 / 石皮 5.0:1,UI 设计对齐批 N1 2026-10-06;旧 STONE_FAINT 对亮底双向 <4.5:1)
|
||||
const STONE_DEEP := Color8(85, 77, 65) # 灰石深化(「已用」槽位墨——PANEL_DATA 槽牌 5.6:1 / 石皮 5.0:1;STONE_FAINT 对亮底双向 <4.5:1,故另立此档)
|
||||
const DARK_UPCOMING := Color8(64, 58, 52) # 未达暗字(底/文同值)
|
||||
|
||||
const BRICK := Color8(201, 175, 131) # 棋盘·未开掩尘地砖 #C9AF83
|
||||
const FLOOR := Color8(179, 154, 120) # 棋盘·已开裸土 #B39A78
|
||||
const CONTACT_GLOW := Color8(214, 236, 255) # 可挖域接触带微光(蓝族浅一档·近白蓝——art-bible §3.2 v2.8 定稿;
|
||||
# 旧绿相 DIG_EDGE 随画法切换退役 2026-09-26 批)
|
||||
const CONTACT_GLOW := Color8(214, 236, 255) # 可挖域接触带微光(蓝族浅一档·近白蓝——art-bible §3.2 v2.8)
|
||||
const CONTACT_GLOW_DEEP := Color8(150, 190, 230) # 接触带呼吸暗端(lerp 目标)
|
||||
const DIGIT_PLATE := Color8(245, 234, 211) # 数字色牌(素色暖纸白直角牌——art-bible §4.1;数字一律垫牌,
|
||||
# 可读不依赖格底——原则二;2026-09-26 呈现修复批落码)
|
||||
# 可读不依赖格底——原则二)
|
||||
const DIGIT_PLATE_EDGE := Color8(210, 198, 172) # 色牌牌缘线
|
||||
const SLOT_BACKDROP := Color8(62, 54, 44) # B 区状态图标槽实底(深墨棕——白图标对比承载)#3E362C(P7 乙 2026-10-06 对齐)
|
||||
const SLOT_BACKDROP_SOFT := Color8(128, 106, 78) # 图标槽实底·柔和档(迭代 26 T5②——BCard 图标槽明度差降档:对米色卡面 3.7:1〔旧深档 8.7:1 过硬〕,白图标 5.1:1 ≥3:1 图形对比硬约束;B 区状态槽仍用深档 SLOT_BACKDROP)
|
||||
const SLOT_BACKDROP := Color8(62, 54, 44) # B 区状态图标槽实底(深墨棕——白图标对比承载)#3E362C
|
||||
const SLOT_BACKDROP_SOFT := Color8(128, 106, 78) # 图标槽实底·柔和档(BCard 图标槽明度差降档:对米色卡面 3.7:1〔深档 8.7:1 过硬〕,白图标 5.1:1 ≥3:1 图形对比硬约束;B 区状态槽仍用深档 SLOT_BACKDROP)
|
||||
|
||||
const BG_ROOT := Color8(58, 46, 34) # 屏幕根底 #3A2E22(UI 落码批 M0 2026-10-04——配色总纲「提暖提亮禁近黑」,旧近黑 #1E1914 退役)
|
||||
const BG_OVERLAY := Color8(54, 43, 32) # 模态遮罩底 #362B20(同批换代,旧 #1C1612 退役)
|
||||
const BG_BRIGHT := Color8(69, 55, 35) # 暖亮档底 #453723(M0 新增——标题/营地/商店等暖亮屏根底)
|
||||
const CANDLE := Color8(246, 198, 103) # 烛光黄 #F6C667(M0 新增——主操作色/候选节点/钥石徽章底;
|
||||
const BG_ROOT := Color8(58, 46, 34) # 屏幕根底 #3A2E22(配色总纲「提暖提亮禁近黑」)
|
||||
const BG_OVERLAY := Color8(54, 43, 32) # 模态遮罩底 #362B20
|
||||
const BG_BRIGHT := Color8(69, 55, 35) # 暖亮档底 #453723(标题/营地/商店等暖亮屏根底)
|
||||
const CANDLE := Color8(246, 198, 103) # 烛光黄 #F6C667(主操作色/候选节点/钥石徽章底;
|
||||
# 与 §4.1 奖励金 #F0A828 分轨——圣经 §7.2 琥珀双岗注记)
|
||||
const DIM := Color(0.08, 0.06, 0.05) # 遮罩基色(用时乘 alpha)
|
||||
const TOAST_BG := Color(0.94, 0.91, 0.84, 0.95) # toast 面底
|
||||
const TIP_BG := Color(0.16, 0.12, 0.09, 0.96) # tooltip 深底
|
||||
|
||||
# 呈现杂色档(T4 裸色收口批 2026-10-02——各视图裸 Color8 字面量单点化;值 = 收口时各来源行现值快照)
|
||||
# 呈现杂色档(各视图裸 Color8 字面量单点化)
|
||||
const STAGE_BG := Color8(62, 52, 42) # B 区舞台暗底(方向稿 render_battle_screen——battle_stage_view 面板底)
|
||||
const BEAM_LIGHT := Color8(255, 236, 200) # B 区顶光柱暖光(用时乘 alpha 渐隐——battle_stage_view 光柱层)
|
||||
const STAND_STONE := Color8(150, 130, 100) # 站桩石板地台·板身(standee_body 方向稿取色)
|
||||
const STAND_STONE_EDGE := Color8(90, 76, 58) # 站桩石板地台·描边
|
||||
const STAND_STONE_RIM := Color8(196, 172, 132) # 站桩石板地台·顶缘受光线(board_cell_unit 口沿高光已并档至此——清扫批 2026-10-02)
|
||||
const CAMP_HEADER_BAND := Color8(110, 69, 38) # 营地柜台头带深棕 #6E4526(4-camp.png 方向稿取色——P7 乙 2026-10-06 对齐 demo --wood-deep)
|
||||
const STAND_STONE_RIM := Color8(196, 172, 132) # 站桩石板地台·顶缘受光线
|
||||
const CAMP_HEADER_BAND := Color8(110, 69, 38) # 营地柜台头带深棕 #6E4526(4-camp.png 方向稿取色;对齐 demo --wood-deep)
|
||||
const CAMP_ROW_PLATE := Color8(240, 230, 206) # 营地行板暖米(同上)
|
||||
const CAMP_BADGE_PANEL := Color8(40, 32, 24) # 营地钥石徽暗底(同上)
|
||||
const HP_BAR_BACK := Color8(40, 34, 32) # 迷你血条深底 #282220(关卡画面样张 v1 冻结——P7 乙 2026-10-06 对齐;兼 N1 血数垫牌)
|
||||
const HP_BAR_BACK := Color8(40, 34, 32) # 迷你血条深底 #282220(兼血数垫牌)
|
||||
|
||||
# BBCode 十六进制(RichTextLabel 用;与上同值)
|
||||
const HEX_GOLD := "f0a828"
|
||||
const HEX_INK := "33261b"
|
||||
const HEX_ON_GOLD := "3a2a08"
|
||||
# 结算行/叙事行语义档(T4 收口批——battle_log_view 行格式化与 narrator 横幅行的裸 hex 单点化;
|
||||
# 名随呈现语义、值 = 2026-09 起各行现值快照,色值逐字节不变)
|
||||
# 结算行/叙事行语义档(battle_log_view 行格式化与 narrator 横幅行的裸 hex 单点化;
|
||||
# 名随呈现语义)
|
||||
const HEX_LOG_ROUND := "9a8a72" # 轮次前缀/零值行暖灰石
|
||||
const HEX_LOG_GAIN := "e0b860" # 收益金(GP 袋/商店行/败判定/节点横幅)
|
||||
const HEX_LOG_LOSS := "c47c7c" # 损失红(GP 流失/负效果)
|
||||
@@ -220,7 +217,7 @@ const DOT_OFF := PANEL_BORDER
|
||||
|
||||
|
||||
# ── 减动效(battle.md §Transitions 内建承诺:开启时时长减半;结算逐行兑现为信息载体降速不跳过)──
|
||||
# 唯一入口 = 设置面开关(迭代 8 卡④ 转正;切片期调整板开关已随其退役,调整板本体 14.5 收进调试浮窗)
|
||||
# 唯一入口 = 设置面开关(调整板本体在调试浮窗)
|
||||
static var motion_scale := 1.0 # 1.0 常态 / 0.5 减动效
|
||||
static var victory_fx_style := "crest" # 胜利演出风格(P-C5 两套并存——"crest"碎砖拼纹章[默认冻结件]/"dust"尘金粒子;切换=调试浮窗,会话级)
|
||||
|
||||
@@ -229,15 +226,15 @@ static func motion(dur: float) -> float:
|
||||
return dur * motion_scale
|
||||
|
||||
|
||||
# ── 单位/特效纹理路径单点(棋盘小人贴图批 2026-09-25)──
|
||||
# 素材 PNG 不入 git(用户裁定)——装载必须经 load_tex(exists 守卫),缺位返回 null 走各消费点
|
||||
# ── 单位/特效纹理路径单点 ──
|
||||
# 素材 PNG 不入 git——装载必须经 load_tex(exists 守卫),缺位返回 null 走各消费点
|
||||
# 的字形/无演出回退路径;禁 preload(素材缺位树上直接断言炸脚本,冒烟安全红线)
|
||||
const TEX_HERO_IDLE := "res://assets/art/character/character_hero_idle.png"
|
||||
const TEX_HERO_WALK := "res://assets/art/character/character_hero_walk.png"
|
||||
const TEX_HERO_PORTRAIT := "res://assets/art/character/character_hero_portrait.png" # B 区站桩立绘·normal 态(48px 档,迭代 6 T3 换装=new-normal 源)
|
||||
const TEX_HERO_ATTACK := "res://assets/art/character/character_hero_attack.png" # B 区站桩立绘·attack 态(迭代 6 T3 三态切换)
|
||||
const TEX_HERO_HIT := "res://assets/art/character/character_hero_hit.png" # B 区站桩立绘·hit 态(迭代 6 T3 三态切换)
|
||||
# 敌种立绘(B 区站桩 2026-09-26 槽接线批)——文件键 = 数据表主键 enemy_id(圣经 §8.3 命名规范),
|
||||
const TEX_HERO_PORTRAIT := "res://assets/art/character/character_hero_portrait.png" # B 区站桩立绘·normal 态(48px 档)
|
||||
const TEX_HERO_ATTACK := "res://assets/art/character/character_hero_attack.png" # B 区站桩立绘·attack 态(三态切换)
|
||||
const TEX_HERO_HIT := "res://assets/art/character/character_hero_hit.png" # B 区站桩立绘·hit 态(三态切换)
|
||||
# 敌种立绘(B 区站桩)——文件键 = 数据表主键 enemy_id(圣经 §8.3 命名规范),
|
||||
# 键集 = art-bible §4.6.4 八敌种;素材批入库前全部缺位(load_tex→null→字形回退),入库即显示零代码换装
|
||||
const TEX_ENEMY_GOBLIN := "res://assets/art/character/character_enemy_goblin.png" # 地精
|
||||
const TEX_ENEMY_BUGBEAR := "res://assets/art/character/character_enemy_bugbear.png" # 熊地精
|
||||
@@ -248,7 +245,7 @@ const TEX_ENEMY_DUERGAR := "res://assets/art/character/character_enemy_duergar.p
|
||||
const TEX_ENEMY_HOOK_HORROR := "res://assets/art/character/character_enemy_hook_horror.png" # 钩爪恐魔
|
||||
const TEX_ENEMY_DUERGAR_KING := "res://assets/art/character/character_enemy_duergar_king.png" # 灰矮人石君王
|
||||
|
||||
# 框材组纹理槽(样张 v2 冻结批 T2 2026-09-28——♻ 复用件量化改色入包,产物 = tools/quantize_ui_skin.py,
|
||||
# 框材组纹理槽(♻ 复用件量化改色入包,产物 = tools/quantize_ui_skin.py,
|
||||
# 全 CC0;缺位 = load_tex null → 程序化回退原路径,冒烟安全)
|
||||
const TEX_PANEL_STONE := "res://assets/art/ui/panel_stone.png" # 石板亮面板九宫格 48×48
|
||||
const TEX_BUTTON_STONE := "res://assets/art/ui/button_stone.png" # 按钮九宫格·常态(可按的砖)
|
||||
@@ -259,14 +256,14 @@ const TEX_ICON_KEYSTONE := "res://assets/art/ui/icon_keystone.png" #
|
||||
const TEX_ICON_FORGE := "res://assets/art/ui/icon_forge.png" # 柜台一·属性锻造
|
||||
const TEX_ICON_WORKSHOP := "res://assets/art/ui/icon_workshop.png" # 柜台二·道具工坊
|
||||
const TEX_ICON_ATLAS := "res://assets/art/ui/icon_atlas.png" # 柜台三·奖励格图鉴
|
||||
# 节点图标五键(关卡画面样张 v1 冻结批 M-2 迭代 4 T2——20px 烘焙 1:1 消费,WARM 单色;
|
||||
# 节点图标五键(20px 烘焙 1:1 消费,WARM 单色;
|
||||
# 对象键 = node_type 文件侧延伸 §8.3;缺位 = load_tex null → 字形回退)
|
||||
const TEX_NODE_BATTLE := "res://assets/art/ui/node_battle.png" # 节点图标·战斗房
|
||||
const TEX_NODE_REWARD := "res://assets/art/ui/node_reward.png" # 节点图标·奖励房
|
||||
const TEX_NODE_REST := "res://assets/art/ui/node_rest.png" # 节点图标·恢复房
|
||||
const TEX_NODE_BOSS := "res://assets/art/ui/node_boss.png" # 节点图标·Boss 房
|
||||
const TEX_NODE_SHOP := "res://assets/art/ui/node_shop.png" # 节点图标·商店房
|
||||
# 手绘功能图标十键(UI 落码批 M1 2026-10-04——20×20 像素 1:1/整数倍消费〔§8.2 甲类纪律〕;
|
||||
# 手绘功能图标十键(20×20 像素 1:1/整数倍消费〔§8.2 甲类纪律〕;
|
||||
# 源=demo 原型 src 盘上件搬运;缺位 = load_tex null → 字形回退不炸)
|
||||
const TEX_ICON_SWORD := "res://assets/art/ui/icon_sword.png" # 图标·攻击系(锻造行/攻击强化卡)
|
||||
const TEX_ICON_SHIELD := "res://assets/art/ui/icon_shield.png" # 图标·护甲系
|
||||
@@ -287,8 +284,8 @@ static func load_tex(path: String) -> Texture2D:
|
||||
return null
|
||||
|
||||
|
||||
# ── 屏底氛围层(UI 设计对齐批 T5 2026-10-06——demo .tilebg opacity .14/.vignette/.beams 对齐;
|
||||
# 旧「石纹全屏满铺不透」退役——demo 石纹只作弱质感,聚焦靠暗角)──
|
||||
# ── 屏底氛围层(demo .tilebg opacity .14/.vignette/.beams 对齐;
|
||||
# demo 石纹只作弱质感,聚焦靠暗角)──
|
||||
const BACKDROP_TILE_ALPHA := 0.14 # demo .tilebg{opacity:.14}
|
||||
const BG_MAP_TOP := Color8(74, 59, 42) # 地图屏渐变顶 #4A3B2A(demo #v-map 180deg 0%;低点 40% = BG_ROOT #3A2E22)
|
||||
|
||||
@@ -323,16 +320,16 @@ static func vignette_rect() -> TextureRect:
|
||||
var tr := TextureRect.new()
|
||||
tr.texture = t
|
||||
tr.stretch_mode = TextureRect.STRETCH_SCALE
|
||||
tr.texture_filter = CanvasItem.TEXTURE_FILTER_LINEAR # 平滑渐变件白名单(迭代 26 T2——全局 Nearest 下显式豁免)
|
||||
tr.texture_filter = CanvasItem.TEXTURE_FILTER_LINEAR # 平滑渐变件白名单(全局 Nearest 下显式豁免)
|
||||
tr.set_anchors_preset(Control.PRESET_FULL_RECT)
|
||||
tr.mouse_filter = Control.MOUSE_FILTER_IGNORE
|
||||
return tr
|
||||
|
||||
|
||||
# 斜光带(demo #v-start .beams——repeating-linear-gradient 102deg,暖白 5% 窄带 240-340/560px;
|
||||
# 程序纹理循环带,确定性生成零随机。迭代 26 T4 光柱硬边修(grilling Q7 裁=连续 alpha):
|
||||
# 带内 alpha 0→0.05→0 横向过渡(旧匀净+零过渡硬边梯形退役)+纵向衰减上亮下暗;
|
||||
# 纹理 filter=显式 LINEAR 白名单(与暗角/地图渐变同血统,见 T2)
|
||||
# 程序纹理循环带,确定性生成零随机。光柱采用连续 alpha:
|
||||
# 带内 alpha 0→0.05→0 横向过渡+纵向衰减上亮下暗;
|
||||
# 纹理 filter=显式 LINEAR 白名单(与暗角/地图渐变同血统)
|
||||
static func beams_rect() -> TextureRect:
|
||||
var img := Image.create(560, 560, false, Image.FORMAT_RGBA8)
|
||||
var ca := cos(0.2094) # 12°(102deg 带轴的垂直倾角)
|
||||
@@ -353,7 +350,7 @@ static func beams_rect() -> TextureRect:
|
||||
var tr := TextureRect.new()
|
||||
tr.texture = ImageTexture.create_from_image(img)
|
||||
tr.stretch_mode = TextureRect.STRETCH_TILE
|
||||
tr.texture_filter = CanvasItem.TEXTURE_FILTER_LINEAR # 平滑渐变件白名单(迭代 26 T2 Q7 裁——连续 alpha 与暗角/地图渐变同血统)
|
||||
tr.texture_filter = CanvasItem.TEXTURE_FILTER_LINEAR # 平滑渐变件白名单(连续 alpha 与暗角/地图渐变同血统)
|
||||
tr.set_anchors_preset(Control.PRESET_FULL_RECT)
|
||||
tr.mouse_filter = Control.MOUSE_FILTER_IGNORE
|
||||
return tr
|
||||
@@ -373,13 +370,13 @@ static func gradient_base(top: Color, low: Color, stop := 0.4) -> TextureRect:
|
||||
var tr := TextureRect.new()
|
||||
tr.texture = t
|
||||
tr.stretch_mode = TextureRect.STRETCH_SCALE
|
||||
tr.texture_filter = CanvasItem.TEXTURE_FILTER_LINEAR # 平滑渐变件白名单(迭代 26 T2——全局 Nearest 下显式豁免)
|
||||
tr.texture_filter = CanvasItem.TEXTURE_FILTER_LINEAR # 平滑渐变件白名单(全局 Nearest 下显式豁免)
|
||||
tr.set_anchors_preset(Control.PRESET_FULL_RECT)
|
||||
tr.mouse_filter = Control.MOUSE_FILTER_IGNORE
|
||||
return tr
|
||||
|
||||
|
||||
# CJK 随包字体(2026-09-27 用户拍板 Fusion Pixel OFL——CJK 覆盖+授权干净;命名遵 art-bible §8.3
|
||||
# CJK 随包字体(Fusion Pixel OFL——CJK 覆盖+授权干净;命名遵 art-bible §8.3
|
||||
# 全小写 snake_case 域前缀)。守卫装载同 load_tex 纪律:缺位返回 null(引擎回退字体接手,不炸)。
|
||||
const FONT_UI := "res://assets/fonts/ui_font_fusion_pixel_zh_hans.ttf"
|
||||
|
||||
@@ -390,8 +387,8 @@ static func load_font(path: String) -> Font:
|
||||
return null
|
||||
|
||||
|
||||
# ── 字号档位表(UI 设计对齐批 P3 甲 2026-10-06——WB G1 六档;全站 UiKit.label/字号 override 只许取
|
||||
# 本表,裸数字字号退役。归档映射:10/11/12→CAPTION、13/14→HINT、15/16→BODY、17/18/20→SUB、
|
||||
# ── 字号档位表(六档;全站 UiKit.label/字号 override 只许取
|
||||
# 本表。归档映射:10/11/12→CAPTION、13/14→HINT、15/16→BODY、17/18/20→SUB、
|
||||
# 21/22/24/26→TITLE、32/40→H1(就近归档);棋盘数字公式 maxi(18, ceil(格边×0.5)) 与站桩血数
|
||||
# 11px 条带内读数豁免不入表(art-bible §7.4)──
|
||||
const FS_CAPTION := 12 # 装饰 caption/分区小字/微计数(§7.4 豁免下限)
|
||||
@@ -401,7 +398,7 @@ const FS_SUB := 18 # 副标题/小组标题(=棋盘数字下限同值)
|
||||
const FS_TITLE := 24 # 屏标题/横幅/价格
|
||||
const FS_H1 := 34 # 主标题/演算暴击
|
||||
|
||||
# ── 秘宝解锁条件计数键 → 呈现标签(迭代 22 秘宝批 T4——秘宝匣柜台条件进度文案单点;键域=
|
||||
# ── 秘宝解锁条件计数键 → 呈现标签(秘宝匣柜台条件进度文案单点;键域=
|
||||
# TreasureItemTable.UNLOCK_COUNTER_KEYS 封闭枚举,键出域走「条件」兜底不炸)──
|
||||
const TREASURE_REQ_LABELS := {"adventure_cleared": "通关", "boss_kill_count": "Boss击杀", "vision_used": "真视使用"}
|
||||
# 秘宝档位 → 呈现名(契约 2 三档;legendary 传说空集合法不在柜)
|
||||
@@ -409,10 +406,10 @@ const TREASURE_TIER_LABELS := {"common": "普通", "excellent": "优秀", "rare"
|
||||
|
||||
# ── StyleBox 工厂 ──
|
||||
# margin = (左, 上, 右, 下);border_w = 0 即无边框(根底/遮罩用)
|
||||
# 圆角率单点(UI 设计对齐批 P5 甲 2026-10-06——容器/按钮全站唯一圆角值;数据框/数字牌/AP 点阵/准线/血条
|
||||
# =0 直角不变;底座椭圆 17/结算金印 200/状态脉冲环 100 圆形 = §7.1 明示例外保留。取值=代拍板 5,可推翻)
|
||||
# 圆角率单点(容器/按钮全站唯一圆角值;数据框/数字牌/AP 点阵/准线/血条
|
||||
# =0 直角不变;底座椭圆 17/结算金印 200/状态脉冲环 100 圆形 = §7.1 明示例外保留)
|
||||
const RADIUS_OBJECT := 5
|
||||
# 九宫格切片(P4 乙 2026-10-06——panel 石皮 8→16 与 demo .plate9 slice16 对齐;button 维持 8、仪式框维持 12)
|
||||
# 九宫格切片(panel 石皮 16 与 demo .plate9 slice16 对齐;button 维持 8、仪式框维持 12)
|
||||
const PANEL_SLICE := 16.0
|
||||
static func panel_flat(bg: Color, border: Color, corner: int = 0, border_w: int = 1, margin: Vector4 = Vector4.ZERO) -> StyleBoxFlat:
|
||||
var sb := StyleBoxFlat.new()
|
||||
@@ -427,8 +424,8 @@ static func panel_flat(bg: Color, border: Color, corner: int = 0, border_w: int
|
||||
return sb
|
||||
|
||||
|
||||
# color 缺省哨兵 = alpha<0(真实用色不取负 alpha)——传色即一次到位(旧「label() 后跟
|
||||
# add_theme_color_override」两行式收单行);省略时保持旧默认:bold=INK_LABEL / 非 bold=主题缺省
|
||||
# color 缺省哨兵 = alpha<0(真实用色不取负 alpha)——传色即一次到位;省略时缺省
|
||||
# bold=INK_LABEL / 非 bold=主题缺省
|
||||
static func label(text: String, size: int, bold: bool, color: Color = Color(-1, -1, -1, -1)) -> Label:
|
||||
var l := Label.new()
|
||||
l.text = text
|
||||
@@ -440,7 +437,7 @@ static func label(text: String, size: int, bold: bool, color: Color = Color(-1,
|
||||
return l
|
||||
|
||||
|
||||
# ── 模态壳工厂(T4 收口批 2026-10-02——各 overlay 手搓 dim+Center+VBox/Panel 骨架单点化)──
|
||||
# ── 模态壳工厂(各 overlay 手搓 dim+Center+VBox/Panel 骨架单点化)──
|
||||
# dim = 全屏半透明遮罩(全屏锚定纪律同既有各 overlay);center 全屏锚定居中;with_panel = 内容包
|
||||
# PanelContainer(panel_sb 缺省 = 主题默认板——settlement_review 同款);box = 内容列
|
||||
#(min_size/separation 参数化,min_size ZERO = 不设)。返回 Dictionary{dim, center, panel, box}
|
||||
@@ -475,8 +472,8 @@ static func modal_shell(parent: Control, dim_color: Color, min_size := Vector2.Z
|
||||
return {"dim": dim, "center": center, "panel": panel, "box": box}
|
||||
|
||||
|
||||
# ── 全屏页壳(UI 设计对齐批 T6/T7 2026-10-06——demo #v-shop/#v-bonus 全屏页形态落地:
|
||||
# 06 在册 D2 商店全屏化/D1 竖卡 232×352。暖底+石纹弱质感+暗角+56px 通栏顶条+内容列;
|
||||
# ── 全屏页壳(demo #v-shop/#v-bonus 全屏页形态落地:
|
||||
# 商店全屏化 + 竖卡 232×352。暖底+石纹弱质感+暗角+56px 通栏顶条+内容列;
|
||||
# 商店=暖亮底、三选一=根底,两屏同构差异仅底色与顶条工具件)──
|
||||
const PAGE_TOP_H := 56.0
|
||||
|
||||
@@ -518,9 +515,9 @@ static func scaled(base: int, pct: int) -> int:
|
||||
return maxi(1, int(round(float(base) * (100.0 + float(pct)) / 100.0)))
|
||||
|
||||
|
||||
# 宽度纪律(ui-struct-probe 批 T3——全站按钮最小宽度基准 216+SIZE_FILL:VBox 列内拉满列宽等宽,
|
||||
# 宽度纪律(全站按钮最小宽度基准 216+SIZE_FILL:VBox 列内拉满列宽等宽,
|
||||
# HBox 行内不抢余宽(EXPAND_FILL 会把行撑爆——营地底条 1616px 实证);角落小件/紧凑位传 0 豁免)
|
||||
# 钮高纪律(2026-10-06 用户令「对齐好、不要那么细」——标题屏主钮 26px 对常规钮 38px 不齐的治根):
|
||||
# 钮高纪律(统一全站钮高,消除标题屏主钮与常规钮不齐):
|
||||
# 全站常规钮统一最小高 40(主操作钮/危险钮经 button() 同高对齐,压扁形态从此不存在);
|
||||
# 豁免域与宽度纪律同款——min_w=0 紧凑位(HUD 顶条小钮/角标)不抬行高
|
||||
const BUTTON_MIN_H := 40.0
|
||||
@@ -535,7 +532,7 @@ static func button(text: String, min_w := 216.0) -> Button:
|
||||
return b
|
||||
|
||||
|
||||
# ── 框材纹理工厂(样张 v2 冻结批 T2——§7.1 容器穿戏服、数据素面;缺位回退 flat/主题默认) ──
|
||||
# ── 框材纹理工厂(§7.1 容器穿戏服、数据素面;缺位回退 flat/主题默认) ──
|
||||
# 九宫格 StyleBoxTexture(texture_margin 8px——quantize 脚本同值);tint 供消费点微调色调
|
||||
static func _tex_sb(tex: Texture2D, margin: float, tint := Color.WHITE) -> StyleBoxTexture:
|
||||
var sb := StyleBoxTexture.new()
|
||||
@@ -549,8 +546,8 @@ static func _tex_sb(tex: Texture2D, margin: float, tint := Color.WHITE) -> Style
|
||||
|
||||
|
||||
# 石板亮面板(B 区舞台/C 区主面板/图例——容器穿戏服;数据框仍 panel_flat 素面 §7.1)
|
||||
# Q1 丙(2026-10-06):content margin 低于切片的分量自动抬到切片——切片加大到 16 后石框纹理不吃内容
|
||||
# (T0 实查 9 消费面 7 处 margin<16;工厂单点 clamp,调用点零改)
|
||||
# content margin 低于切片的分量自动抬到切片——切片 16 后石框纹理不吃内容
|
||||
# (9 消费面 7 处 margin<16;工厂单点 clamp,调用点零改)
|
||||
static func panel_skinned(margin: Vector4 = Vector4.ZERO, tint := Color.WHITE) -> StyleBox:
|
||||
var m := Vector4(maxf(margin.x, PANEL_SLICE), maxf(margin.y, PANEL_SLICE),
|
||||
maxf(margin.z, PANEL_SLICE), maxf(margin.w, PANEL_SLICE))
|
||||
@@ -592,8 +589,7 @@ static func icon_rect(tex_path: String, px: float) -> TextureRect:
|
||||
return tr
|
||||
|
||||
|
||||
# 钥石徽(badge 工厂件——Q10 甲收编 2026-10-06:原营地顶栏钥石徽本地拼装〔camp_overlay_view〕
|
||||
# 参数化收编,消费方 = 营地覆盖层 + 状态面板钥石余额行(迭代 17 卡③)。形制 = 烛光黄底+木色描边
|
||||
# 钥石徽(badge 工厂件——消费方 = 营地覆盖层 + 状态面板钥石余额行。形制 = 烛光黄底+木色描边
|
||||
# +钥石图标(icon_rect 守卫装载,缺位=纯文本回退)+深墨字;零新素材零新色值——值全取现行 token)
|
||||
class KeystoneBadge:
|
||||
extends PanelContainer
|
||||
@@ -620,10 +616,10 @@ static func keystone_badge(text: String, icon_path := TEX_ICON_KEYSTONE, icon_px
|
||||
return KeystoneBadge.new(text, icon_path, icon_px)
|
||||
|
||||
|
||||
# 按钮皮·五态矩阵(迭代 26 T1——UI 交互与观感批;甲/乙案值=grilling 两轮八裁落文,逐实例 override 不动主题层)
|
||||
# 悬停=乙案:常态图+modulate 提亮(0.06 档与 primary_button 现行 lightened(0.06) 同纪律,零新素材);
|
||||
# 禁用=甲案:本族底+α0.45 幽灵化(与 font_disabled_color 同纪律),取代旧「disabled=按压图」错配;
|
||||
# 焦点=甲案:INK 2px 内缩 1px 环(与 font_focus_color=INK 同源;引擎 focus 样式盒叠加绘制,不遮钮底)
|
||||
# 按钮皮·五态矩阵(逐实例 override 不动主题层)
|
||||
# 悬停:常态图+modulate 提亮(0.06 档与 primary_button 现行 lightened(0.06) 同纪律,零新素材);
|
||||
# 禁用:本族底+α0.45 幽灵化(与 font_disabled_color 同纪律);
|
||||
# 焦点:INK 2px 内缩 1px 环(与 font_focus_color=INK 同源;引擎 focus 样式盒叠加绘制,不遮钮底)
|
||||
const BUTTON_HOVER_LIFT := Color(1.06, 1.06, 1.06, 1.0) # 悬停提亮档(modulate 乘数>1=中间调提亮,高光像素封顶不漂)
|
||||
const BUTTON_GHOST_ALPHA := 0.45 # 禁用幽灵化 α(与文字禁用同档)
|
||||
const BUTTON_FOCUS_RING_W := 2.0 # 焦点环宽 px
|
||||
@@ -659,10 +655,10 @@ static func _apply_button_skin(b: Button) -> void:
|
||||
b.add_theme_color_override("font_disabled_color", Color(INK, BUTTON_GHOST_ALPHA))
|
||||
|
||||
|
||||
# ── UI 落码批 M10 公共件(2026-10-04——bcard 竖卡工厂/拨杆开关/样式滑杆/动词按钮/tooltip 主题;
|
||||
# 消费方 = M2 标题/M3 营地/M4 设置/M6 商店/M7 三选一。单点纪律同 §7.1:新件只加不改旧件)──
|
||||
# ── 公共件(bcard 竖卡工厂/拨杆开关/样式滑杆/动词按钮/tooltip 主题;
|
||||
# 消费方 = 标题/营地/设置/商店/三选一屏。单点纪律同 §7.1:新件只加不改旧件)──
|
||||
|
||||
# 动词按钮(图标+动词+悬停说明三件套——M2 标题屏按钮组/通用;icon 缺位=纯文字不炸)
|
||||
# 动词按钮(图标+动词+悬停说明三件套——标题屏按钮组/通用;icon 缺位=纯文字不炸)
|
||||
static func verb_button(text: String, icon_path: String, tip := "") -> Button:
|
||||
var b := button(text)
|
||||
var tex := load_tex(icon_path)
|
||||
@@ -716,7 +712,7 @@ static func danger_button(text: String, tip := "", min_w := 216.0) -> Button:
|
||||
return b
|
||||
|
||||
|
||||
# 卡顶类型色带·Bayer 有序抖动(UI 设计对齐批 P8 甲 2026-10-06——bcard band 平涂 ColorRect 退役;
|
||||
# 卡顶类型色带·Bayer 有序抖动(
|
||||
# demo CSS 平滑渐变/blur 违反像素纪律,改 4×4 Bayer 有序抖动台阶:α 由深至 0,2px 元胞逐行降覆盖;
|
||||
# 纯静态绘制零动效(减动效无涉);Bayer 阶数=实现细节白名单项,取 4×4)
|
||||
class DitherBand:
|
||||
@@ -725,7 +721,7 @@ class DitherBand:
|
||||
|
||||
func _init(c: Color) -> void:
|
||||
band = c
|
||||
custom_minimum_size = Vector2(0, 8) # 8px = 4×4 Bayer 全周期(原平涂条 6px)
|
||||
custom_minimum_size = Vector2(0, 8) # 8px = 4×4 Bayer 全周期
|
||||
|
||||
func _draw() -> void:
|
||||
var bayer := [[0, 8, 2, 10], [12, 4, 14, 6], [3, 11, 1, 9], [15, 7, 13, 5]]
|
||||
@@ -739,7 +735,7 @@ class DitherBand:
|
||||
draw_rect(Rect2(float(rx) * cell, float(ry) * cell, cell, cell), band, true)
|
||||
|
||||
|
||||
# bcard 竖卡工厂(商店/三选一同构卡——M6/M7 共用;M7.1 差异仅=价签 vs ✓选择章)
|
||||
# bcard 竖卡工厂(商店/三选一同构卡——两屏共用;差异仅=价签 vs ✓选择章)
|
||||
# set_selected 经 mark/样式换装
|
||||
class BCard:
|
||||
extends PanelContainer
|
||||
@@ -749,30 +745,30 @@ class BCard:
|
||||
var desc_l: Label
|
||||
var price_l: Label
|
||||
var foot_l: Label
|
||||
var body: VBoxContainer # 内容列(消费方经 add_footer 插件到底注前——M6 购买钮挂位)
|
||||
var body: VBoxContainer # 内容列(消费方经 add_footer 插件到底注前——购买钮挂位)
|
||||
var _sb_normal: StyleBox
|
||||
var _sb_sel: StyleBox
|
||||
|
||||
func _init(title: String, desc: String, band: Color, icon_path: String, price := "", footnote := "") -> void:
|
||||
var tex := UiKit.load_tex(UiKit.TEX_PANEL_STONE)
|
||||
_sb_normal = UiKit.panel_skinned(Vector4(12, 10, 12, 10)) if tex != null else UiKit.panel_flat(PANEL_DATA, PANEL_BORDER,RADIUS_OBJECT, 1, Vector4(12, 10, 12, 10)) # 回退=数据板单点(P7 乙后同值,字面量退役)
|
||||
_sb_sel = UiKit.panel_flat(PANEL_DATA, Color8(138, 90, 52), RADIUS_OBJECT, 2, Vector4(12, 10, 12, 10)) # 选中底=数据板单点(P7 乙后同值)
|
||||
_sb_normal = UiKit.panel_skinned(Vector4(12, 10, 12, 10)) if tex != null else UiKit.panel_flat(PANEL_DATA, PANEL_BORDER,RADIUS_OBJECT, 1, Vector4(12, 10, 12, 10)) # 回退=数据板单点
|
||||
_sb_sel = UiKit.panel_flat(PANEL_DATA, Color8(138, 90, 52), RADIUS_OBJECT, 2, Vector4(12, 10, 12, 10)) # 选中底=数据板单点
|
||||
add_theme_stylebox_override("panel", _sb_normal)
|
||||
custom_minimum_size = Vector2(232, 352) # D1(UI 设计对齐批 T6 2026-10-06——demo .bcard 232×352;原 216×300)
|
||||
size_flags_vertical = Control.SIZE_SHRINK_CENTER # 行容器不纵向拉伸(卡恒 352 高居中——T7 实测 HBox fill 拉到 ~536 修)
|
||||
custom_minimum_size = Vector2(232, 352) # demo .bcard 232×352
|
||||
size_flags_vertical = Control.SIZE_SHRINK_CENTER # 行容器不纵向拉伸(卡恒 352 高居中——HBox fill 会拉到 ~536)
|
||||
var box := VBoxContainer.new()
|
||||
box.add_theme_constant_override("separation", 8)
|
||||
add_child(box)
|
||||
body = box
|
||||
var band_r := DitherBand.new(band) # P8 甲:卡顶类型色带=Bayer 有序抖动带(随各 TG_* 逐卡着色)
|
||||
var band_r := DitherBand.new(band) # 卡顶类型色带=Bayer 有序抖动带(随各 TG_* 逐卡着色)
|
||||
box.add_child(band_r)
|
||||
mark = UiKit.label("✓",FS_BODY, true, Color8(58, 42, 8))
|
||||
mark.horizontal_alignment = HORIZONTAL_ALIGNMENT_RIGHT
|
||||
mark.visible = false
|
||||
box.add_child(mark)
|
||||
# 图标槽(demo .ic 76×76 槽底+图标 60=20px 源 3 整数倍——P6 甲④ 2× 档升 3× 随 D1 对齐)
|
||||
# 图标槽(demo .ic 76×76 槽底+图标 60=20px 源 3 整数倍)
|
||||
var slot := PanelContainer.new()
|
||||
slot.add_theme_stylebox_override("panel", UiKit.panel_flat(SLOT_BACKDROP_SOFT, PANEL_BORDER, 0, 1)) # 迭代 26 T5② 明度差降档(柔和档;白图标 ≥3:1 硬约束在役)
|
||||
slot.add_theme_stylebox_override("panel", UiKit.panel_flat(SLOT_BACKDROP_SOFT, PANEL_BORDER, 0, 1)) # 明度差降档(柔和档;白图标 ≥3:1 硬约束在役)
|
||||
slot.custom_minimum_size = Vector2(76, 76)
|
||||
slot.size_flags_horizontal = Control.SIZE_SHRINK_CENTER
|
||||
box.add_child(slot)
|
||||
@@ -795,7 +791,7 @@ class BCard:
|
||||
var spacer := Control.new()
|
||||
spacer.size_flags_vertical = Control.SIZE_EXPAND_FILL
|
||||
box.add_child(spacer)
|
||||
price_l = UiKit.label(price,FS_TITLE, true, GOLD_INK) # 尾裁微批a 2026-10-08——GOLD 对货架板 1.46:1 越带,换深金墨 5.21:1(三选一价签恒空串隐藏零波及)
|
||||
price_l = UiKit.label(price,FS_TITLE, true, GOLD_INK) # GOLD 对货架板 1.46:1 越带,换深金墨 5.21:1(三选一价签恒空串隐藏零波及)
|
||||
price_l.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER
|
||||
price_l.visible = price != ""
|
||||
box.add_child(price_l)
|
||||
@@ -846,7 +842,7 @@ static func switch_toggle(initial: bool) -> SwitchToggle:
|
||||
return SwitchToggle.new(initial)
|
||||
|
||||
|
||||
# 样式滑杆/tooltip 主题两处局部做法已退役(迭代 25 UI 全局主题批 2026-10-13——词条迁
|
||||
# game/assets/theme/main_theme.tres 全局主题单点:滑轨 slider/grabber_area/grabber、
|
||||
# TooltipPanel/TooltipLabel;旧实现同批删除,禁新旧并存。消费方几何内联:
|
||||
# settings_overlay 音量滑轨 / debug_overlay _slider_row)
|
||||
# 样式滑杆/tooltip 主题词条单点迁
|
||||
# game/assets/theme/main_theme.tres 全局主题:滑轨 slider/grabber_area/grabber、
|
||||
# TooltipPanel/TooltipLabel;禁新旧并存。消费方几何内联:
|
||||
# settings_overlay 音量滑轨 / debug_overlay _slider_row
|
||||
@@ -1,5 +1,4 @@
|
||||
# VERTICAL SLICE - NOT FOR PRODUCTION
|
||||
# Date: 2026-09-21
|
||||
# 冒险全流程期望扫描(三关连探零加成基线批量试玩——run_adventure 的蒙特卡洛批量工具,
|
||||
# 补 level_monte_carlo 只测单关的空白面):固定 atk+0%/hp+0%(零天赋格加成,关间大奖励
|
||||
# 按冒险结构现行设计自动兑付),逐局独立种子,输出:
|
||||
@@ -34,23 +33,23 @@ func _initialize() -> void:
|
||||
var route := "min"
|
||||
if args.size() >= 4 and args[3] in ["min", "max"]:
|
||||
route = args[3]
|
||||
# 同调分层开关(迭代 11 第①批 T8——off=现行基线零装配零扰动;on=贪心装配画像,确定性同种子可复放)
|
||||
# 同调分层开关(off=现行基线零装配零扰动;on=贪心装配画像,确定性同种子可复放)
|
||||
var attune_mode := "off"
|
||||
if args.size() >= 5 and args[4] in ["off", "on"]:
|
||||
attune_mode = args[4]
|
||||
# 商店分层开关(迭代 11 第②批 T5——off=legacy 对照缺省零扰动;on=共享池货架面,终局池读数随行)
|
||||
# 商店分层开关(off=legacy 对照缺省零扰动;on=共享池货架面,终局池读数随行)
|
||||
var shop_mode := "off"
|
||||
if args.size() >= 6 and args[5] in ["off", "on"]:
|
||||
shop_mode = args[5]
|
||||
# 特性/树分层开关(迭代 12 T7/T10——off=基线;formed=成型画像开局注入五事件族 14 件成型集
|
||||
# 特性/树分层开关(off=基线;formed=成型画像开局注入五事件族 14 件成型集
|
||||
# 〔测量面专用 params.relics_seed——真实局规则改写型特性只走局内随机渠道〕;ab_a/ab_b=Fork posture 两侧
|
||||
# rule 键集——A/B 面两侧差 <~3pp 验收载体;rules=任意树规则键集注入〔逗号分隔 args[7]——迭代 21 V19 三组配对载体〕)
|
||||
# rule 键集——A/B 面两侧差 <~3pp 验收载体;rules=任意树规则键集注入〔逗号分隔 args[7]——V19 三组配对载体〕)
|
||||
var feat_mode := "off"
|
||||
if args.size() >= 7 and args[6] in ["off", "formed", "ab_a", "ab_b", "rules"]:
|
||||
feat_mode = args[6]
|
||||
var feat_profile := {}
|
||||
if feat_mode == "formed":
|
||||
# 迭代 20 T8 画像换代——旧 14 件随老 27 件退役;新画像=五事件族覆盖 14 件
|
||||
# 成型画像=五事件族覆盖 14 件
|
||||
# (开格 4/封印 3/踩雷 4/节奏 2/战斗 1——每族含跨事件账本件,账本件=攒层/计数面 MC 读数承载)
|
||||
feat_profile = {"mode": "on", "seed_ids": [
|
||||
"twin_pick", "geologist", "hard_hat", "frontier_miner",
|
||||
@@ -63,7 +62,7 @@ func _initialize() -> void:
|
||||
elif feat_mode == "ab_b":
|
||||
feat_profile = {"mode": "off", "tree_rules": ["posture_bulwark"]}
|
||||
elif feat_mode == "rules":
|
||||
feat_profile = {"mode": "off", "tree_rules": String(args[7] if args.size() >= 8 else "").split(",")} # 迭代 21 V19——任意键集配对注入
|
||||
feat_profile = {"mode": "off", "tree_rules": String(args[7] if args.size() >= 8 else "").split(",")} # V19——任意键集配对注入
|
||||
var db = load("res://core/config_db.gd").new()
|
||||
if not db.load_tables("res://data/tables", false):
|
||||
print("表加载失败")
|
||||
@@ -71,10 +70,10 @@ func _initialize() -> void:
|
||||
return
|
||||
var attune_profile := {}
|
||||
if attune_mode == "on":
|
||||
# 贪心画像:Slots=3(迭代 21 V14 换代——槽位判定源=树同调位节点点亮数,模拟画像=三节点
|
||||
# 全点亮常量;原「中间进度计数经谓词表求值」随 attunement_growth 谓词表退役);装配序 =
|
||||
# 贪心画像:Slots=3(槽位判定源=树同调位节点点亮数,模拟画像=三节点
|
||||
# 全点亮常量);装配序 =
|
||||
# 表序 passive 优先至 Slots-1 + 首个 active(slots=3 → [淬毒刃纹, 标中殉爆, 掘进蓄能],
|
||||
# provisional 画像归调参批);Y = 营地升级 Lv1 表值 1。autoplay 默认不刷——Y 仅注入容量,
|
||||
# provisional 画像归调参);Y = 营地升级 Lv1 表值 1。autoplay 默认不刷——Y 仅注入容量,
|
||||
# 刷新消费归玩家策略面(钓鱼归玩家)。
|
||||
var slots: int = 3
|
||||
var passives: Array = []
|
||||
@@ -126,7 +125,7 @@ func _initialize() -> void:
|
||||
"mine_dmg": int(st.get("mine_dmg", 0)),
|
||||
"taken": int(st["hero_dmg"]) + int(st.get("mine_dmg", 0)),
|
||||
"refreshes": int(st["refreshes"]), "choices": int(st["choices"]),
|
||||
# 封印经济五字段(迭代14②判式换代批 T1——η=seal_hit÷seal_placed、标空率=seal_miss÷seal_placed
|
||||
# 封印经济五字段(η=seal_hit÷seal_placed、标空率=seal_miss÷seal_placed
|
||||
# 派生归分析侧;gain/penalty=结算流水行值合计(原始行值口径,等价换算归判读侧——登记))
|
||||
"seal_placed": int(st.get("seal_placed", 0)), "seal_hit": int(st.get("seal_hit", 0)),
|
||||
"seal_miss": int(st.get("seal_miss", 0)),
|
||||
@@ -141,7 +140,7 @@ func _initialize() -> void:
|
||||
"wallet_count": (st["wallet_events"] as Array).size(),
|
||||
}
|
||||
if shop_mode == "on":
|
||||
# pool 面增量字段(迭代 11 第②批 T5——off 行零新增保对拍逐位干净)
|
||||
# pool 面增量字段(off 行零新增保对拍逐位干净)
|
||||
rec["shop_mode"] = "pool"
|
||||
rec["shop_buys"] = int(st.get("shop_buys", 0))
|
||||
rec["pool_stock"] = st.get("pool_stock", {})
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
# VERTICAL SLICE - NOT FOR PRODUCTION
|
||||
# Date: 2026-09-13(自动玩家策略单一实现)
|
||||
# 自动玩家策略集(headless 冒烟/扫描/转储共用的理性玩家近似):
|
||||
# choose_dig = 挖最近可挖格(先移到邻接已开格)。
|
||||
# 无 window_selection(道具窗口确定性选择——道具窗口已下线)。
|
||||
@@ -28,13 +27,13 @@ static func choose_dig(minesweeper) -> Dictionary:
|
||||
return {}
|
||||
|
||||
|
||||
# ==================== 普通策略档 v2·基础定式画像(2026-09-19 用户拍板——替代旧"无推理最近格"口径) ====================
|
||||
# ==================== 普通策略档 v2·基础定式画像 ====================
|
||||
# 扫雷推理分级(经典定式文献口径):新手级 = 单数字满足规则——「数字 = 邻雷未开数 ⇒ 全是雷(满足即标)」
|
||||
# 「数字 = 已封印数 ⇒ 余邻全安全(满足即开)」,教学一眼会、正常玩家两局即会的最低门槛判断;
|
||||
# 进阶级 = 联动定式(1-1 / 1-2 / 1-2-1 / 1-2-2-1 / 子集缩减)与概率分析——本切片归完美档 subset 推理承载,
|
||||
# 不入普通画像(新手不可靠掌握)。本档语义 = 只做新手级推理 + 无处可推时按既有口径盲挖边界格(新手冲动)+
|
||||
# 不主动刷新(新手无刷新策略意识)。测量定位:教会基础规则后的真实玩家下限近似(旧"乱点/最近格"口径测的是
|
||||
# 无认知玩家下限,对通关率带判读失真——2026-09-18/19 普通画像 0%/5% 越带下界与真人方向性结论矛盾)。
|
||||
# 不主动刷新(新手无刷新策略意识)。测量定位:教会基础规则后的真实玩家下限近似
|
||||
# (乱点/最近格口径测的是无认知玩家下限,对通关率带判读失真,不作普通画像)。
|
||||
|
||||
# 普通画像单步决策入口(每动作一查——挖掘揭示新数字后重推理):move/dig/seal/stop 四值(无 refresh)。
|
||||
static func novice_action(minesweeper) -> Dictionary:
|
||||
@@ -74,7 +73,7 @@ static func _deduce_single(minesweeper) -> Dictionary:
|
||||
return {"safe": _row_major(safe), "mines": _row_major(mines)}
|
||||
|
||||
|
||||
# ==================== 完美策略档(2026-09-14 用户拍板:OQ7 技能轴上界测量仪——只作测量,非玩法数值) ====================
|
||||
# ==================== 完美策略档(OQ7 技能轴上界测量仪——只作测量,非玩法数值) ====================
|
||||
# 策略语义:零盲挖(只挖已证安全格 ⇒ 构造保证零踩雷)· 已证危险格全封印(封印先于挖——封印 = 纯收益
|
||||
# 〔轮末 weaken + 魔法 首次充能〕且雷上封印不阻连锁)· 卡死且刷新资源可用 → 轮内刷新
|
||||
# (经 settlement.player_request_refresh 核心编排)· 否则停手(残 AP 1:1 转存 RAP——下轮刷新资源)。
|
||||
@@ -210,7 +209,7 @@ static func _row_major(cells: Dictionary) -> Array:
|
||||
return out
|
||||
|
||||
|
||||
# 卡死赌挖选格(普通档 v3.1 2026-10-01——模拟档位重构):概率启发近似真人「挑看起来最安全的一格」。
|
||||
# 卡死赌挖选格(普通档 v3.1):概率启发近似真人「挑看起来最安全的一格」。
|
||||
# 危险度 = 该格对每个邻接已证数字约束取(余雷数/未知数)比值中的最小值(= 任一邻数字视角下的
|
||||
# 最低雷占比——single 已证安全格在进入本函数前已被 perfect_action 消化,故卡死态比值恒 >0);
|
||||
# 同危险度行主序先到先得。纯查询面零 RNG(确定性保回放);寻路同 choose_dig(目标已开 8 邻
|
||||
|
||||
@@ -7,10 +7,10 @@
|
||||
# 护栏:headless fail-fast(shot_probe 同款);空纹理取证 fail——取证段失败=闸红,
|
||||
# 零字节/空纹理不允许静默落盘;board_meta.json 取样坐标落盘(棋盘视图全局矩形——
|
||||
# golden 前置裁剪坐标源,禁硬编码像素)。
|
||||
# 迭代 30 卡① T2-④ 双帧取证:每关抓 微光开(生产默认态·Q4 丙案)/关(golden 基线保护面)
|
||||
# 双帧取证:每关抓 微光开(生产默认态·Q4 丙案)/关(golden 基线保护面)
|
||||
# 两帧——① 开帧逐像素判定「非生效格零变化」(禁整格染色/禁越圈;生效格内只准落在内缘
|
||||
# 带 = cell÷8)② 每个生效格须有实绘像素(微光不空转)③ 关帧即 golden 对比帧(原名落盘,
|
||||
# 基线 2026-09-30 代不重制——契约 1 off 态基线)。隔离纪律 = 与 type_glow_shot_probe 同款:
|
||||
# 基线不重制——契约 1 off 态基线)。隔离纪律 = 与 type_glow_shot_probe 同款:
|
||||
# 前置 UiKit.motion_scale=0.5 冻结接触带呼吸相位(静态带色),两帧差异唯一定位在类型微光。
|
||||
extends Node
|
||||
|
||||
@@ -41,7 +41,7 @@ func _ready() -> void:
|
||||
push_error("[board_shot] 表加载失败")
|
||||
get_tree().quit(1)
|
||||
return
|
||||
# 双帧单一变量隔离(迭代 30 卡①):冻结接触带呼吸相位——motion_scale<1.0 ⇒ _apply_contact_bands
|
||||
# 双帧单一变量隔离:冻结接触带呼吸相位——motion_scale<1.0 ⇒ _apply_contact_bands
|
||||
# 施静态色(减动效停格路径)。必须在 rebuild 前置位(rebuild/set_dig_glow_enabled 按当时相位
|
||||
# 重施带色)。golden 侧影响:静态带色=呼吸周期中值,与基线捕获相的像素差最坏 ≈灰度24 于
|
||||
# 接触带像素(占比 <0.4%),≤2% 判据不受影响;停格同时令「非生效格零变化」可精确判定。
|
||||
@@ -77,17 +77,17 @@ func _ready() -> void:
|
||||
add_child(view)
|
||||
# 身份解析接线(生产同源——main.gd:251 set_identity_resolver(_identity_lookup)):必须在
|
||||
# rebuild / set_dig_glow_enabled 前接好,二者尾链 _recompute_glow 同步读身份建作用集。
|
||||
# 裸视图不接 = _identity_cb 无效 ⇒ info 恒 null ⇒ 作用集恒空(2026-10-17 首两次实跑
|
||||
# 三关 glow=0 根因——双帧比对空转、PASS 假绿)。现值经 _cur_settlement 成员读入
|
||||
# 裸视图不接 = _identity_cb 无效 ⇒ info 恒 null ⇒ 作用集恒空(三关 glow=0 根因——
|
||||
# 双帧比对空转、PASS 假绿)。现值经 _cur_settlement 成员读入
|
||||
# (仿生产 run_scope.settlement;bind 传参有 Callable 类型校验坑,见成员注)。
|
||||
view.set_identity_resolver(_probe_identity_lookup)
|
||||
view.rebuild(settlement.minesweeper.current_board(), settlement.minesweeper)
|
||||
# 进挖掘相位(生产态 = hud_presenter.set_dig_glow_enabled(not settling) 接线——裸视图默认
|
||||
# _dig_glow=false,接触带与类型微光同源门控都不渲染;golden 基线 2026-09-30 代即捕获于
|
||||
# _dig_glow=false,接触带与类型微光同源门控都不渲染;golden 基线即捕获于
|
||||
# 该裸态。双帧取证要求生产默认态,故显式开——两帧均含静态接触带(motion_scale=0.5 停格),
|
||||
# 差异唯一定位在类型微光开关)
|
||||
view.set_dig_glow_enabled(true)
|
||||
# 抓帧前等入场动画完结(迭代 19 卡① S2 观察项根因修复 2026-10-08):rebuild 有全板淡入
|
||||
# 抓帧前等入场动画完结(S2 观察项根因):rebuild 有全板淡入
|
||||
# motion(0.2)+R0 涟漪逐格延迟显形(motion(0.04×离板心距离)+motion(0.12),最远格 ≈0.56s),
|
||||
# 原制 6 个 process_frame 热机后 ≈0.1s=捕在涟漪中途——逐跑帧时序抖动使揭示 alpha 漂移,
|
||||
# golden L2/L3 跨 2% 容差线波动(实测 0.00-2.53%)。改时间制 1.2s(2×余量)=锚定动画完结态。
|
||||
@@ -151,7 +151,7 @@ func _probe_identity_lookup(c: Vector2i):
|
||||
return _cur_settlement.query_reward_identity(c)
|
||||
|
||||
|
||||
# 双帧逐像素比对(迭代 30 卡① T2-④-①②):所有变化像素必须落在生效格的内缘带内
|
||||
# 双帧逐像素比对:所有变化像素必须落在生效格的内缘带内
|
||||
# (de < cell÷8——带外含砖心零侵入=禁整格染色/禁越圈);每个生效格须有实绘像素(不空转)。
|
||||
func _check_dual_frames(tag: String, view, glow_cells: Dictionary, off_img: Image, on_img: Image) -> void:
|
||||
var board = view.board()
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
# VERTICAL SLICE - NOT FOR PRODUCTION
|
||||
# 定位:分析工具·非验收件——零断言取数工具,不出入任何闸。
|
||||
# Date: 2026-09-19
|
||||
# 棋盘板形统计探针(判式"实测板形"口径的数据源):
|
||||
# 对 board_gen.tres 每行 (L, D) 生成 N 张板,统计幸存雷数 |M| 与安全格数 |S|
|
||||
# 的均值 / 最小 / 最大("先布后删"后的落地值——名义值 density×(W×H−9) 不含删除段)。
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
# VERTICAL SLICE - NOT FOR PRODUCTION
|
||||
# Date: 2026-09-22
|
||||
# 棋盘口径校准(分析会话取数工具——冒险零加成批完成度分母实证):
|
||||
# 棋盘口径校准(分析会话取数工具——冒险零加成完成度分母实证):
|
||||
# 逐 board_gen 行真实生成 N 板,统计 实际雷数/安全格数/R₀ 初始展开占比——
|
||||
# 生成期「先布后删+可推理率重试」使实际雷数远低于表口径 round(d×(W×H−9)),
|
||||
# 日志侧只有清图板可反推 safe_total(未清图深度无样本),本工具给全网格精确分母。
|
||||
@@ -36,7 +35,7 @@ func _initialize() -> void:
|
||||
"mine_density": float(row.mine_density), "inferability_target": float(row.inferability_target),
|
||||
"solver_rules": String(row.solver_rules), "min_safe_count": int(row.min_safe_count),
|
||||
"max_gen_attempts": int(row.max_gen_attempts), "board_type": String(row.board_type),
|
||||
# 初始展开收窄批(迭代14①)两新参数透传(.get 缺省与生成器防御口径一致)
|
||||
# 初始展开收窄两参数透传(.get 缺省与生成器防御口径一致)
|
||||
"shave_ring_radius": int(row.get("shave_ring_radius", 1)),
|
||||
"r0_max_cap": int(row.get("r0_max_cap", 0)),
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# VERTICAL SLICE - NOT FOR PRODUCTION
|
||||
# 定位:诊断工具·非验收件——不出入任何闸(probe_gate/render_gate/run_smoke 全不涉)。
|
||||
# Date: 2026-09-13(playtest-2026-09-12 报告 Top1——零承伤根因分割探针)
|
||||
# playtest-2026-09-12 报告 Top1——零承伤根因分割探针
|
||||
# 检查面:H1 hero_defense 池仅 off_guard 正向行(×0 墙结构面不存在);
|
||||
# H2 Boss/敌攻击链健康——fresh 池下非蓄力拍 d_tick = max(下限, 池乘区(掷点档位基线))。
|
||||
# 快照盲区说明:settlement_snapshot 取在衰减段之后,时效 1 的 ac_up 当轮授予当轮衰减——
|
||||
@@ -106,7 +106,6 @@ func _run_boss(db, seed_val: int, dig: bool) -> Dictionary:
|
||||
|
||||
|
||||
# 逐轮:授予行汇总 → 重建战斗段时点池值(effect_pool_map 表驱动归属)→ 敌行动行逐行打印
|
||||
#(T6 收口批:原形参 verbose 为死参——打印行为无档位差异,随批删除)
|
||||
func _print_rounds(db, math, run: Dictionary) -> void:
|
||||
var groups := _round_groups(run.rows)
|
||||
var beats := _played_beats(run, db)
|
||||
@@ -192,7 +191,7 @@ func _analyze_round(db, math, rows: Array, beats: Dictionary) -> Dictionary:
|
||||
elif t == "damage" and String(row.get("source_class", "")) == "enemy_action":
|
||||
var baseline: int = int(row.get("baseline", 0))
|
||||
var attach: int = int(row.get("attach", 0))
|
||||
# 掷点口径重建(2026-09-16 护甲等级掷点批):基线按行留档档位打折(暴击 ×倍率),附加必中
|
||||
# 掷点口径重建:基线按行留档档位打折(暴击 ×倍率),附加必中
|
||||
var tier: int = int(row.get("tier", 100))
|
||||
var crit: bool = bool(row.get("crit", false))
|
||||
var base_factor: float = float(tier) * (float(db.settlement.crit_multiplier) / 100.0 if crit else 1.0)
|
||||
@@ -212,7 +211,7 @@ func _analyze_round(db, math, rows: Array, beats: Dictionary) -> Dictionary:
|
||||
return {"pd": pd, "pe": pe, "grants": grants, "lines": lines, "ticks": ticks}
|
||||
|
||||
|
||||
# 拍名重建(敌人技能池批):pending_skill 字段 = 该结算开轮摇定的动作 id(快照取在结算末尾)——
|
||||
# 拍名重建:pending_skill 字段 = 该结算开轮摇定的动作 id(快照取在结算末尾)——
|
||||
# round r 的拍 = 快照 r 的 enemies[0].pending_skill。快照 enemies 空时(敌死亡轮)回退 "?"——该轮敌已无行动行。
|
||||
func _played_beats(run: Dictionary, db) -> Dictionary:
|
||||
var beats := {}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
# VERTICAL SLICE - NOT FOR PRODUCTION
|
||||
# Date: 2026-09-13(playtest-2026-09-12 Q1 复核——"安全格减伤是否过量导致满血打完 Boss")
|
||||
# playtest-2026-09-12 Q1 复核——"安全格减伤是否过量导致满血打完 Boss"
|
||||
# 口径:逐结算轮统计 ac_up 授予层数(AC10→11→12)与该轮敌方实伤,
|
||||
# 分割"高 AC 轮"(叠满 = AC12,期望减免 28.75%)与基线轮(AC10,期望减免 21.25%)的承伤读数。
|
||||
# 运行:Godot_console.exe --headless --path . --script res://smoke/defense_scan.gd -- [N] [seed0]
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
# VERTICAL SLICE - NOT FOR PRODUCTION
|
||||
# Validation Question: 玩家能否无指导在一场完整战斗中经历「开战→挖掘规划→轮末结算逐行兑现→胜/负」并逐行归因(P2)
|
||||
# Date: 2026-09-10
|
||||
# 一次性检查脚本:打印一场完整战斗的结算步骤流 + 棋盘摘要(肉眼验收 P2 归因质量)。
|
||||
# 运行:Godot_console.exe --headless --path . --script res://smoke/dump_battle.gd
|
||||
extends SceneTree
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
# VERTICAL SLICE - NOT FOR PRODUCTION
|
||||
# Date: 2026-09-10
|
||||
# 一次性实验:不同 mine_density 下的 R₀ 占比 / 幸存雷数 / 残余挖掘量 / 推理率——为切片临时调参提供数据。
|
||||
extends SceneTree
|
||||
|
||||
|
||||
@@ -36,7 +36,7 @@ func _ready() -> void:
|
||||
if not sb.is_dead():
|
||||
problems.append("mark_dead 后 is_dead 应真")
|
||||
|
||||
# ①b 命中火花(迭代 6 T4 终稿——打击瞬间特效计数)
|
||||
# ①b 命中火花(打击瞬间特效计数)
|
||||
var sb2 = sb_script.new("enemy")
|
||||
sb2.box = Vector2(150, 176)
|
||||
add_child(sb2)
|
||||
@@ -77,7 +77,7 @@ func _ready() -> void:
|
||||
if n_fx < 2:
|
||||
problems.append("结算光带应含带体+前导缘线 ≥2 节点,实得 %d" % n_fx)
|
||||
|
||||
# ---- ④b 轮切换横幅双态(迭代 8 卡⑦——常态=淡入驻留消隐动画链;减动效=静态呈现零动画)----
|
||||
# ---- ④b 轮切换横幅双态(常态=淡入驻留消隐动画链;减动效=静态呈现零动画)----
|
||||
sfx.play_round_banner(3)
|
||||
await get_tree().process_frame
|
||||
var banner_nodes := 0
|
||||
@@ -104,7 +104,7 @@ func _ready() -> void:
|
||||
if int(sfx.probe_settlement_marks()) != 2:
|
||||
problems.append("逐条结算标出未落位(状态脉冲+数字浮现——三轮 #3),实得 %s" % sfx.probe_settlement_marks())
|
||||
|
||||
# ---- ⑤ 主角三态切换(迭代 6 T3——attack/hit 短驻自回 + 死亡复活态回 idle) ----
|
||||
# ---- ⑤ 主角三态切换(attack/hit 短驻自回 + 死亡复活态回 idle) ----
|
||||
var hero_sb = sb_script.new("hero")
|
||||
hero_sb.box = Vector2(150, 176)
|
||||
add_child(hero_sb)
|
||||
@@ -133,7 +133,7 @@ func _ready() -> void:
|
||||
if hero_sb.is_dead() or String(hero_sb.hero_state_probe().get("state", "")) != "idle":
|
||||
problems.append("主角死亡后 set_hero 复活失败(is_dead 应假+态回 idle)")
|
||||
|
||||
# ---- ⑥ 主角受击/死亡路由(迭代 6 T5——battle_stage 主角早退拆除的呈现链断言) ----
|
||||
# ---- ⑥ 主角受击/死亡路由(battle_stage 主角走完整呈现链的断言) ----
|
||||
var db2 = load("res://core/config_db.gd").new()
|
||||
if db2.load_tables("res://data/tables", false):
|
||||
var stage2 = BattleStageView.new(db2)
|
||||
|
||||
@@ -8,8 +8,8 @@ extends Node
|
||||
|
||||
const ProbeUiUtil := preload("res://smoke/probe_ui_util.gd") # T6 收口:树寻址/隔离样板单点
|
||||
|
||||
# ── 演出采样窗口口径(拍板点:headless/窗口化帧率不定,帧时刻以墙钟近似锚定;改窗口 = 改抓帧
|
||||
# 时刻观测面,非断言语义。窗口值与原字面量逐一同值——T6 收口批仅提具名。) ──
|
||||
# ── 演出采样窗口口径(headless/窗口化帧率不定,帧时刻以墙钟近似锚定;改窗口 = 改抓帧
|
||||
# 时刻观测面,非断言语义。窗口值与字面量同值——仅提为具名常量。) ──
|
||||
const T_DIG_FLIGHT := 0.12 # 挖格行到达/弹道在飞窗口(settle_batch 回执后停留)
|
||||
const T_SETTLE := 0.35 # 结算排播窗口(行到达后二连抓)
|
||||
const T_ROUND_TICK := 0.25 # 停手轮末(敌方攻击弹道/受击闪白 0.2-1.6s 流过)连抓 6 帧的采样步距
|
||||
@@ -29,7 +29,7 @@ func _ready() -> void:
|
||||
add_child(main)
|
||||
await get_tree().process_frame
|
||||
ProbeUiUtil.isolate_save(main, "fx_shot_probe", false, true, false)
|
||||
# 隔离档即时应用(迭代 19 卡① T1——真实档 fullscreen 态会污染抓帧尺寸〔shot_probe 同款修正〕)
|
||||
# 隔离档即时应用(真实档 fullscreen 态会污染抓帧尺寸〔shot_probe 同款口径〕)
|
||||
main.settings_store.apply()
|
||||
for i in 8:
|
||||
await get_tree().process_frame # UI 装配完成等足帧(canvas_item 空引用窗口)
|
||||
|
||||
@@ -72,7 +72,7 @@ func _check_one(label: String, main: Node) -> void:
|
||||
# 不遍历战斗屏宿主的全部后代。变异验证第二发证——向装配器注入一个全幅 ColorRect
|
||||
# (挂在 battle_root 的内层根下)时探针仍全绿:「未登记兄弟控件」这一缺陷类不在本探针
|
||||
# 拦截面内。要拦它得改为递归遍历全子树找重叠,那是另一套判据(且极易误报容器父子关系),
|
||||
# 归后续批按需立,不在本批范围。
|
||||
# 本探针不设此判据。
|
||||
var bv: Control = main.board_view
|
||||
if bv == null:
|
||||
_problems.append("[%s] board_view = null" % label)
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
# VERTICAL SLICE - NOT FOR PRODUCTION
|
||||
# Date: 2026-09-11
|
||||
# 日志合规:任何模拟运行与真人试玩同日志——操作记录 / 轮次状态 / 战斗结束 / 通关结束 全量 JSONL。
|
||||
# logger=null 时自建会话(每局一文件),批量调用方传共享 logger(log_run 局次标记切分)。
|
||||
# 关卡自动通关策略(理性玩家近似)——呈现层数值调整板的批量模拟与 headless 扫描
|
||||
@@ -22,15 +21,15 @@
|
||||
# 实现注:信号处理体全部为静态方法 + bind 传状态——GDScript 多行 lambda 内嵌 match/控制流的解析不可靠。
|
||||
extends RefCounted
|
||||
|
||||
const AutoPlayer := preload("res://smoke/auto_player.gd") # O4 收口:自动玩家策略单一实现
|
||||
const AutoPlayer := preload("res://smoke/auto_player.gd") # 自动玩家策略单一实现
|
||||
|
||||
# ── 普通档 v3 失误注入参数(2026-10-01 用户规格:踩雷期望=完美+2/板、每板少挖几个正确格)──
|
||||
# 标雷→误挖转化率:v3.2 实证 0.22 时普通档 0/100 全灭(首轮大量已证封印=保底命中,非真人失误
|
||||
# 形态——真人几乎不挖自己封印的格);降 0.10 = 偶发真手滑,主踩雷源=卡死赌挖(不确定性概率命中)
|
||||
# ── 普通档 v3 失误注入参数(踩雷期望=完美+2/板、每板少挖几个正确格)──
|
||||
# 标雷→误挖转化率:主踩雷源=卡死赌挖(不确定性概率命中),误挖只作偶发真手滑——
|
||||
# 真人几乎不挖自己封印的格(p 过高会让首轮已证封印变保底命中,非失误形态)
|
||||
const NORMAL_MINE_SLIP_P := 0.10
|
||||
# 安全挖跳过率:每板安全格 ~70-90 × p ≈ 3-5 次漏挖(信息与推进双损失)
|
||||
const NORMAL_SKIP_DIG_P := 0.05
|
||||
# 空闲提前赌挖率(v3.4):已证安全格在手时小概率先赌一格——完美系把不确定性攒到残局
|
||||
# 空闲提前赌挖率:已证安全格在手时小概率先赌一格——完美系把不确定性攒到残局
|
||||
# (残局 hazard→1=扫雷),真人早赌滚信息(低 hazard 期吃掉不确定性);p×~40 安全 AP/板 ≈ 4 次
|
||||
const NORMAL_IDLE_GAMBLE_P := 0.10
|
||||
|
||||
@@ -52,7 +51,7 @@ static func run_level(db, seed_val: int, level_index: int, atk_pct: int, hp_pct:
|
||||
"boss_depth": -1, "hp_at_boss": -1, "rows": [], "timeout": false,
|
||||
"log_path": "", "policy": policy, "refreshes": 0, "mine_hits": 0, "mine_dmg": 0,
|
||||
"visited_ids": [], "choices": 0, "last_battle_enemies": [],
|
||||
# 封印经济五字段(迭代14②判式换代批 T1——runsum 读数面;聚合自结算流水 source_class,
|
||||
# 封印经济五字段(runsum 读数面;聚合自结算流水 source_class,
|
||||
# 零新随机源零核心改动;η=seal_hit÷seal_placed 与标空率派生归分析侧)
|
||||
"seal_placed": 0, "seal_hit": 0, "seal_miss": 0, "seal_hit_gain": 0, "seal_miss_penalty": 0,
|
||||
"prng": _policy_rng(seed_val), "mine_slips": 0, "skip_digs": 0, "shop_buys": 0,
|
||||
@@ -81,9 +80,9 @@ static func run_level(db, seed_val: int, level_index: int, atk_pct: int, hp_pct:
|
||||
|
||||
|
||||
# 单关驱动段(run_level 抽取——零自动推进停点 + 战斗自动玩家;policy 经 st 入 _autoplay_battle)
|
||||
# 停点前移批 2026-09-23:入口停点(node_index 尚 -1 = 进节点前无 current_node)也经本分支驱动——
|
||||
# 读 next_candidate_ids 候选(入口单候选)→ pick_next_node,同一 _enter_node 与旧自动进入逐位
|
||||
# 等价(零 RNG 漂移);入口 pick 不计 st.choices(单候选强制选路,计数口径与旧测量面逐位同构)。
|
||||
# 入口停点(node_index 尚 -1 = 进节点前无 current_node)也经本分支驱动——
|
||||
# 读 next_candidate_ids 候选(入口单候选)→ pick_next_node,同一 _enter_node 等价(零 RNG 漂移);
|
||||
# 入口 pick 不计 st.choices(单候选强制选路,计数口径与测量面同构)。
|
||||
static func _drive_level(runner, st: Dictionary, logger, policy: String, route: String) -> void:
|
||||
st["policy"] = policy
|
||||
var guard_battles := 20
|
||||
@@ -103,11 +102,11 @@ static func _drive_level(runner, st: Dictionary, logger, policy: String, route:
|
||||
_autoplay_battle(runner, this_settlement, st, logger)
|
||||
|
||||
|
||||
# 三关连探冒险驱动(冒险结构实现批 2026-09-21)——AdventureRunner 编排:主种子恰一次
|
||||
# 三关连探冒险驱动——AdventureRunner 编排:主种子恰一次
|
||||
# (start_adventure 唯一 new_adventure 调用点)、大奖励立即确认、冒险终局置 result/summary。
|
||||
# 返回结构 = run_level 同族 + summary / wallet_events / encounter_max_hp_seq / reward_events /
|
||||
# visited_ids(全冒险节点行进序列——跨关累计,非末关 runner 的 visited 随实例更替,此处为唯一全量面)。
|
||||
# 2026-09-22 数值验证工具批:增 hero_override 尾参(永久强化 flat 注入透传——缺省 {} 与既有调用方
|
||||
# hero_override 尾参 = 永久强化 flat 注入透传(缺省 {} 与既有调用方
|
||||
# 逐位一致零行为变更)+ run 行携带该参数(分析侧按元进度档切分)+ 败退敌人名采集
|
||||
# (last_battle_enemies——末场战斗敌方显示名序列,失败局即死亡遭遇)。
|
||||
static func run_adventure(db, seed_val: int, atk_pct: int, hp_pct: int, collect_rows: bool = false,
|
||||
@@ -132,36 +131,36 @@ static func run_adventure(db, seed_val: int, atk_pct: int, hp_pct: int, collect_
|
||||
"log_path": "", "policy": policy, "refreshes": 0, "mine_hits": 0, "mine_dmg": 0,
|
||||
"visited_ids": [], "choices": 0, "last_battle_enemies": [],
|
||||
"summary": {}, "wallet_events": [], "encounter_max_hp_seq": [], "reward_events": [],
|
||||
"feat_mode": String(relic.get("mode", "off")), # 特性分层开关(迭代 12 T7——off=316-f 通用竞争/on=成型画像优先捕捞 relic)
|
||||
"feat_mode": String(relic.get("mode", "off")), # 特性分层开关(off=316-f 通用竞争/on=成型画像优先捕捞 relic)
|
||||
"shop_gp_seq": [], # 商店房到达余额序(逐关 1 次——GP 跨关延续验收取数面)
|
||||
# 封印经济五字段(迭代14②判式换代批 T1——与 run_level 同口径,见彼处注释)
|
||||
# 封印经济五字段(与 run_level 同口径,见彼处注释)
|
||||
"seal_placed": 0, "seal_hit": 0, "seal_miss": 0, "seal_hit_gain": 0, "seal_miss_penalty": 0,
|
||||
"prng": _policy_rng(seed_val), "mine_slips": 0, "skip_digs": 0, "shop_buys": 0,
|
||||
"adv": true, # v3 档位行为开关(选路评分/失误注入/商店购买只挂冒险测量面——单关夹具保持旧语义零扰动)
|
||||
"adv": true, # v3 档位行为开关(选路评分/失误注入/商店购买只挂冒险测量面——单关夹具语义不变)
|
||||
|
||||
"level_templates": [], # 逐关图模板名(关卡节点图生成 v0.4 分层出数)
|
||||
}
|
||||
adr.step_row.connect(_on_step_row.bind(adr, st, collect_rows))
|
||||
adr.node_entered.connect(_on_adventure_node_entered.bind(adr, st, logger))
|
||||
adr.wallet_changed.connect(_on_adventure_wallet.bind(st, logger))
|
||||
# 三选一停点驱动(迭代 10 奖励重构批——big_reward_paid 退役:确定性策略候选取首件、
|
||||
# 兑付后推进下一关——零随机流自开,同种子逐位可复放;满位替换策略随迭代 22 T5 拆除)
|
||||
# 三选一停点驱动(确定性策略候选取首件、
|
||||
# 兑付后推进下一关——零随机流自开,同种子逐位可复放;无满位替换策略)
|
||||
adr.curio_choice_presented.connect(_on_adventure_bonus.bind(st, adr))
|
||||
adr.curio_resolved.connect(_on_adventure_curio_resolved.bind(st, adr))
|
||||
adr.adventure_finished.connect(_on_adventure_finished.bind(st, logger))
|
||||
adr.set_hero_override(atk_pct, hp_pct)
|
||||
# 同调分层注入(迭代 11 第①批 T8——缺省 {} 与既有调用方逐位一致零行为变更)
|
||||
# 同调分层注入(缺省 {} 与既有调用方逐位一致零行为变更)
|
||||
var adv_params := {"hero_override": hero_override, "first_script": false}
|
||||
if not attune.is_empty():
|
||||
adv_params["attunement"] = attune.get("equipped", [])
|
||||
adv_params["attune_refresh_y"] = int(attune.get("refresh_y", 0))
|
||||
# 特性注入(迭代 12 T7——formed 画像=开局注入七流派各 2 件档一成型集;测量面专用)
|
||||
# 特性注入(formed 画像=开局注入七流派各 2 件档一成型集;测量面专用)
|
||||
if relic.has("seed_ids"):
|
||||
adv_params["relics_seed"] = relic.get("seed_ids", [])
|
||||
# 树规则键注入(迭代 12 T10——A/B 面读数载体:fork 激活侧 effects rule 键集透传)
|
||||
# 树规则键注入(A/B 面读数载体:fork 激活侧 effects rule 键集透传)
|
||||
if relic.has("tree_rules"):
|
||||
adv_params["tree_rules"] = relic.get("tree_rules", [])
|
||||
# 商店分层注入(迭代 11 第②批 T5——缺省 legacy 与既有调用方逐位一致零行为变更;pool=共享池货架面)
|
||||
# 商店分层注入(缺省 legacy 与既有调用方逐位一致零行为变更;pool=共享池货架面)
|
||||
var pool_shop: bool = String(shop.get("mode", "")) == "pool"
|
||||
if pool_shop:
|
||||
adv_params["shop_mode"] = "pool"
|
||||
@@ -181,10 +180,10 @@ static func run_adventure(db, seed_val: int, atk_pct: int, hp_pct: int, collect_
|
||||
if adr.runner() != null:
|
||||
adr.runner().restore_hero_base()
|
||||
if pool_shop:
|
||||
# 终局池读数(迭代 11 第②批 T5——摘要 additive 键 pool_stock:_settle 清账前快照,
|
||||
# 终局池读数(摘要 additive 键 pool_stock:_settle 清账前快照,
|
||||
# 商店买走 vs 奖励领走 vs 枯竭的终局面;事后读 adr.pool_stock 会被收束清账重置=满库假象)
|
||||
st["pool_stock"] = (st.get("summary", {}).get("pool_stock", {}) as Dictionary).duplicate(true)
|
||||
st["relics"] = (st.get("summary", {}).get("relics", []) as Array).duplicate() # 特性终局持有集(迭代 12 T1——摘要 additive 键清账前快照,同 pool_stock 模式)
|
||||
st["relics"] = (st.get("summary", {}).get("relics", []) as Array).duplicate() # 特性终局持有集(摘要 additive 键清账前快照,同 pool_stock 模式)
|
||||
st.log_path = logger.log_path()
|
||||
if own_session:
|
||||
logger.close_session()
|
||||
@@ -212,7 +211,7 @@ static func _on_adventure_node_entered(node: Dictionary, info: Dictionary, adr,
|
||||
st.boss_depth = int(node.get("node_depth", -1))
|
||||
st.hp_at_boss = int(adr.ledger["hero_hp"])
|
||||
elif t == "reward":
|
||||
# 奖励节点三选一(迭代 10 追加批——共享池确定性策略;TP0 起 316-f 判读:1 级最稀有、
|
||||
# 奖励节点三选一(共享池确定性策略;TP0 起 316-f 判读:1 级最稀有、
|
||||
# 同档并列取最左;替换放弃新件;兑付后 continue_after_node 回图选路)
|
||||
var ndraw: Dictionary = adr.node_curio_candidates()
|
||||
var ncands: Array = ndraw.get("candidates", []) as Array
|
||||
@@ -223,8 +222,8 @@ static func _on_adventure_node_entered(node: Dictionary, info: Dictionary, adr,
|
||||
adr.present_node_curio(ncands)
|
||||
var npick: Dictionary = _pick_token(ncands, String(st.get("feat_mode", "off")))
|
||||
if adr.apply_node_curio(String(npick.get("kind", "")), String(npick.get("tier", "")), String(npick.get("feat_id", ""))):
|
||||
# T5 门票拿到即开——候选已被替换为专长三选一,就地再选一轮(确定性策略同款 316-f);
|
||||
# 不作此分支则节点停点悬空→选路卡死(MC timeout 形态,2026-10-05 批内实测)
|
||||
# 门票拿到即开——候选已被替换为专长三选一,就地再选一轮(确定性策略同款 316-f);
|
||||
# 不作此分支则节点停点悬空→选路卡死(MC timeout 形态)
|
||||
if String(npick.get("kind", "")) == "feat_ticket":
|
||||
var fcands: Array = (adr.node_curio_pending_state().get("candidates", []) as Array)
|
||||
if not fcands.is_empty():
|
||||
@@ -239,9 +238,9 @@ static func _on_adventure_node_entered(node: Dictionary, info: Dictionary, adr,
|
||||
elif t == "shop":
|
||||
st.shop_gp_seq.append(int(info.get("gp", -1)))
|
||||
if String(st.get("shop_mode", "legacy")) == "pool":
|
||||
_autoplay_shop_pool(r, st) # 共享池货架贪心(迭代 11 第②批 T5——买一件=奖励面少一件的机会成本画像维持贪心语义)
|
||||
_autoplay_shop_pool(r, st) # 共享池货架贪心(买一件=奖励面少一件的机会成本,画像维持贪心语义)
|
||||
else:
|
||||
_autoplay_shop(r, st) # 贪心买齐(2026-10-01——旧零购离开退役:GP 局内死钱,真人实证两店全购)
|
||||
_autoplay_shop(r, st) # 贪心买齐(GP 局内死钱——真人实证两店全购)
|
||||
r.continue_after_node()
|
||||
if logger != null:
|
||||
logger.log_node(node, info, r.node_index)
|
||||
@@ -256,7 +255,7 @@ static func _on_adventure_wallet(event: String, gp_val: int, extra: Dictionary,
|
||||
logger.log_wallet(event, gp_val, extra)
|
||||
|
||||
|
||||
# 三选一停点驱动(迭代 10——确定性策略;TP0 起 316-f 判读:1 级最稀有、同档并列取最左;
|
||||
# 三选一停点驱动(确定性策略;TP0 起 316-f 判读:1 级最稀有、同档并列取最左;
|
||||
# 奖励事件随 reward_events 留档)
|
||||
static func _on_adventure_bonus(payload: Dictionary, st: Dictionary, adr) -> void:
|
||||
st.reward_events.append({"curio_pick": payload.duplicate(true)})
|
||||
@@ -269,9 +268,9 @@ static func _on_adventure_bonus(payload: Dictionary, st: Dictionary, adr) -> voi
|
||||
st.result = "advance_failed"
|
||||
|
||||
|
||||
# 三选一确定性选件(TP0 316-f——1 级优先度=最稀有〔迭代 20 四级:legendary>rare>excellent>common〕、2 级=最左;
|
||||
# 三选一确定性选件(TP0 316-f——1 级优先度=最稀有〔四级:legendary>rare>excellent>common〕、2 级=最左;
|
||||
# 候选并列同档取最左=稳定序保证。空候选/未知档位 = 回退最左防御口径。
|
||||
# 成型画像(迭代 12 T7——relic 模式 on:候选含 relic 令牌时优先在 relic 集内按 316-f 择一
|
||||
# 成型画像(relic 模式 on:候选含 relic 令牌时优先在 relic 集内按 316-f 择一
|
||||
# 〔贪心捕捞→特性入局→成型画像全流派覆盖——R3 乙单锚承载〕;off=relic 与属性加成型特性同规则竞争)
|
||||
static func _pick_token(candidates: Array, feat_mode: String = "off") -> Dictionary:
|
||||
var pool: Array = candidates
|
||||
@@ -314,7 +313,7 @@ static func _on_adventure_curio_resolved(result: Dictionary, st: Dictionary, adr
|
||||
st.result = "advance_failed"
|
||||
|
||||
|
||||
# 商店贪心买齐(2026-10-01 模拟档位重构):GP 局内死钱——买得起就买( affordable 集里挑最便宜的
|
||||
# 商店贪心买齐:GP 局内死钱——买得起就买( affordable 集里挑最便宜的
|
||||
# 反复买到达不上任何商品为止,guard 防死循环)。确定性(零随机流);购买出账/加算区入账全走
|
||||
# runner.buy_product 核心编排(与呈现层购买同一出账路径,wallet_changed 信号照常采集)。
|
||||
static func _autoplay_shop(r, st: Dictionary) -> void:
|
||||
@@ -336,7 +335,7 @@ static func _autoplay_shop(r, st: Dictionary) -> void:
|
||||
st.shop_buys = int(st.get("shop_buys", 0)) + 1
|
||||
|
||||
|
||||
# 商店共享池贪心(迭代 11 第②批 T5):货架三选一同构面贪心买齐——affordable 集挑最便宜反复买
|
||||
# 商店共享池贪心:货架三选一同构面贪心买齐——affordable 集挑最便宜反复买
|
||||
# (每次买后全重抽、货架随买随换,枯竭/不可负担即止,guard 防死循环)。购买走 runner.buy_pool_item
|
||||
# 核心编排(与呈现层购买同一出账+池消费路径,wallet_changed 信号照常采集)。确定性(零随机流)。
|
||||
static func _autoplay_shop_pool(r, st: Dictionary) -> void:
|
||||
@@ -366,7 +365,7 @@ static func _on_adventure_finished(summary: Dictionary, st: Dictionary, logger)
|
||||
logger.log_level_end(summary)
|
||||
|
||||
|
||||
# 策略流 RNG(2026-10-01 模拟档位重构):种子派生自局种子 × 黄金比率常数 + 偏移——与核心统一流
|
||||
# 策略流 RNG:种子派生自局种子 × 黄金比率常数 + 偏移——与核心统一流
|
||||
# (rng_service 单盐派生)零共享零扰动;逐局确定性保持(同种子 ⇒ 同决策序列 ⇒ 同回放)
|
||||
static func _policy_rng(seed_val: int) -> RandomNumberGenerator:
|
||||
var prng := RandomNumberGenerator.new()
|
||||
@@ -374,7 +373,7 @@ static func _policy_rng(seed_val: int) -> RandomNumberGenerator:
|
||||
return prng
|
||||
|
||||
|
||||
# 路径轴选路(2026-10-01 目标价值评分制——机械最小编号退役):候选按节点类型评分取最高,
|
||||
# 路径轴选路(目标价值评分制):候选按节点类型评分取最高,
|
||||
# 同分 tie-break 沿 route 方向(min=小编号/max=大编号——verifier 双窗轴保持)。
|
||||
# 评分语义(确定性,零随机流):rest=伤重高值(缺口比例×100)/shop=买得起高值(GP 局内死钱,
|
||||
# 真人实证两店全购)/reward=占位低值(MC 夹具解锁空集=零兑付直通)/boss=缓冲低值(有店有_rest
|
||||
@@ -399,7 +398,7 @@ static func _route_pick(runner, route: String, st: Dictionary) -> int:
|
||||
s = 10.0
|
||||
var cheapest := 1 << 30
|
||||
if String(st.get("shop_mode", "legacy")) == "pool":
|
||||
for c in runner.shop_shelf(): # pool 面:货架现价(迭代 11 第②批 T5)
|
||||
for c in runner.shop_shelf(): # pool 面:货架现价
|
||||
cheapest = mini(cheapest, int(c.get("price", 1 << 30)))
|
||||
else:
|
||||
for p in runner.shop_products():
|
||||
@@ -434,7 +433,7 @@ static func _on_step_row(row: Dictionary, _runner, st: Dictionary, collect_rows:
|
||||
_note_enemy(String(row.get("text", "")), st) # 施放行(毒爆挂毒等——伤害行可能缺席的死亡遭遇仍可归因)
|
||||
if row.source_class == "mine_hit":
|
||||
st.mine_hits += 1 # 踩雷证据行(负向兑付——完美档断言恒 0;盲挖惩罚链埋点)
|
||||
# 封印经济聚合(迭代14②判式换代批 T1——自结算流水聚合,零新随机源):
|
||||
# 封印经济聚合(自结算流水聚合,零新随机源):
|
||||
# ⚠ 行侧判据考证实录:结算段对存续增益实例每轮复发行 effect 行且复用实例 source_class
|
||||
#(battle_orchestrator 状态列举行)——source_class=seal_hit 的行 ≠ 标中事件。事件行唯一判据
|
||||
# = 授予行专带 highlight="seal_hit"(实测 87 行 == 受理封印 87 动作,1:1 精确);
|
||||
@@ -456,7 +455,7 @@ static func _on_step_row(row: Dictionary, _runner, st: Dictionary, collect_rows:
|
||||
st.rows.append(row)
|
||||
|
||||
|
||||
# 敌方显示名采集(数值验证工具批 2026-09-22):敌方行动/施放行文本首词 = 单位显示名
|
||||
# 敌方显示名采集:敌方行动/施放行文本首词 = 单位显示名
|
||||
#(攻击行「地精 撕咬(暴击)」、施放行「地精 施放 毒雾」——显示名不含空格,去重连续重复)
|
||||
static func _note_enemy(text: String, st: Dictionary) -> void:
|
||||
if text.is_empty():
|
||||
@@ -480,7 +479,7 @@ static func _on_node_entered(node: Dictionary, info: Dictionary, runner, st: Dic
|
||||
st.boss_depth = int(node.get("node_depth", -1))
|
||||
st.hp_at_boss = runner.hero_hp
|
||||
elif t == "reward":
|
||||
runner.continue_after_node() # 奖励点 = 内容待拍板直通节点(格面资源批 2026-09-15)
|
||||
runner.continue_after_node() # 奖励点 = 内容未定直通节点
|
||||
elif t == "rest":
|
||||
st.rest_healed = int(info.get("healed", 0))
|
||||
st.hp_before_rest = int(info.get("hp", 0)) - st.rest_healed
|
||||
@@ -503,17 +502,16 @@ static func _on_level_finished(p: Dictionary, st: Dictionary, logger) -> void:
|
||||
|
||||
|
||||
static func _autoplay_battle(runner, settlement, st: Dictionary, logger) -> void:
|
||||
# 策略档经 _decide_act 单点分派(T6 样板收口批 2026-10-02——原 _autoplay_battle/_autoplay_battle_perfect
|
||||
# 双循环逐行相同仅差 act 来源行,合并单循环;三档决策语义逐位不变,见 _decide_act)。
|
||||
# 普通策略档 v3(2026-10-01 用户重定义):完美策略 + 受控失误注入——v2「新手级单步推理+盲挖
|
||||
# 兜底」画像被真人实证推翻(零加成真人首通三关踩 23 雷 vs v2 普通档 L2 条件通关率 10.6%)。
|
||||
# 策略档经 _decide_act 单点分派(单循环承载三档——三档决策语义逐位不变,见 _decide_act)。
|
||||
# 普通策略档 v3:完美策略 + 受控失误注入(标定依据=真人实证:
|
||||
# 零加成真人首通三关踩 23 雷)。
|
||||
# v3 语义 = 会玩的普通玩家:推理与完美档同源(subset 解算/封印/卡死刷新),失误只注入两手——
|
||||
# 标雷误挖(NORMAL_MINE_SLIP_P)+ 安全挖跳过(NORMAL_SKIP_DIG_P)。连续拒绝 ≥3 = 编排违约防御。
|
||||
# 完美策略档(2026-09-14):单步决策循环——move/dig/seal 经 排雷玩法 settle_batch 提交,
|
||||
# 完美策略档:单步决策循环——move/dig/seal 经 排雷玩法 settle_batch 提交,
|
||||
# refresh 经 settlement.player_request_refresh(战斗结算 核心编排:行动经济 扣 RAP + 退役 + 当场换新板),
|
||||
# stop 出内环走统一停手提交。连续拒绝 ≥3 = 编排违约防御(fail-fast 语义,停手防死循环)。
|
||||
# 2026-10-01 上界修正:选路改目标价值评分(_route_pick)+ 商店贪心买齐(_autoplay_shop)——
|
||||
# 「完美」须是元游戏意义上的上界(会购物会补给),不再只是扫雷子游戏上界。
|
||||
# 上界口径:选路=目标价值评分(_route_pick)+ 商店贪心买齐(_autoplay_shop)——
|
||||
# 「完美」是元游戏意义上的上界(会购物会补给),不只扫雷子游戏上界。
|
||||
var round_guard := 60
|
||||
while st.result == "" and runner.current_battle() == settlement and settlement.battle_result == "" and round_guard > 0:
|
||||
round_guard -= 1
|
||||
@@ -543,7 +541,7 @@ static func _autoplay_battle(runner, settlement, st: Dictionary, logger) -> void
|
||||
if kind == "refresh":
|
||||
st.refreshes = int(st.refreshes) + 1
|
||||
elif kind == "seal":
|
||||
st.seal_placed = int(st.get("seal_placed", 0)) + 1 # 封印数(η 分母=全部封印数——迭代14② Q5 裁决;失误注入转化封印不计数=转化后实为挖)
|
||||
st.seal_placed = int(st.get("seal_placed", 0)) + 1 # 封印数(η 分母=全部封印数〔Q5〕;失误注入转化封印不计数=转化后实为挖)
|
||||
rejects = 0
|
||||
else:
|
||||
rejects += 1
|
||||
@@ -553,7 +551,7 @@ static func _autoplay_battle(runner, settlement, st: Dictionary, logger) -> void
|
||||
logger.log_action("stop", Vector2i(-1, -1), _accepted(out_s), {"round": start_round})
|
||||
|
||||
|
||||
# 普通档 v3 失误注入(2026-10-01 用户规格):三类——
|
||||
# 普通档 v3 失误注入:三类——
|
||||
# ①标雷误挖:把「封印已证雷」动作直接转成「挖该格」(真人手滑原型:知道是雷、位置点错,
|
||||
# 踩雷吃伤且该雷奖励报废);期望踩雷增量 ≈ 每板已证雷数 × NORMAL_MINE_SLIP_P(目标完美+2/板)
|
||||
# ②安全挖跳过:把「挖已证安全格」动作转成本轮停手(少挖几个正确的雷——信息与推进双损失)
|
||||
@@ -596,7 +594,7 @@ static func _gamble_action(plan: Dictionary, settlement) -> Dictionary:
|
||||
return {"kind": "move", "cell": plan.hero_cell}
|
||||
|
||||
|
||||
# 战斗决策单行分派(T6 双循环合并抽取——原两循环唯一差异行):
|
||||
# 战斗决策单行分派:
|
||||
# perfect 档 = 完美策略单步决策(上界口径);
|
||||
# normal 档 v3 = 完美+受控失误注入(冒险测量面 adv=true);单关机制夹具保持 v2 新手画像(adv 缺省)。
|
||||
# 决策函数纯读板面/编排面(零 RNG 消费——失误流独走 st.prng,见 _apply_normal_slips)。
|
||||
|
||||
Loaded 100 of 235 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user