Merge branch 'master' into iter38

# Conflicts:
#	game/README.md
#	game/tests/验证集清单.md
This commit is contained in:
DKChan committed 2026-10-26 15:29:55 +08:00
commit 416ca84aeb
85 files changed
+2939 -2054

No files matched your search

+7 -7
View File
@@ -47,7 +47,7 @@ static func debug_open_line(seed_val: int, level: int, level_mode: bool) -> Stri
# 调试战斗终局文案(level_end = 关内两层模式的关卡末)
static func battle_end_text(result: String, level_end: bool) -> String:
if level_end:
return "关卡通关!"
return "关卡完成!"
if result == "victory":
return "战斗胜利!敌人全灭。"
return "战斗失败……探险者倒下了。"
@@ -87,9 +87,9 @@ static func curio_pick_header(payload: Dictionary) -> String:
var lines: Array[String] = ["第 %d 层通过!" % int(payload.get("level_index", 0))]
var heal := int(payload.get("heal_gained", 0))
if heal > 0:
lines.append("通关回血 +%d(生命 %d → %d)" % [heal, int(payload.get("hp_before", 0)), int(payload.get("hp_after", 0))])
lines.append("关卡完成回血 +%d(生命 %d → %d)" % [heal, int(payload.get("hp_before", 0)), int(payload.get("hp_after", 0))])
else:
lines.append("生命已满——通关回血 0(合法态)")
lines.append("生命已满——关卡完成回血 0(合法态)")
lines.append("选择一件提升带走(另两件丢弃不可反悔):")
return "\n".join(lines)
@@ -151,19 +151,19 @@ static func bonus_keystone_line(payload: Dictionary) -> String:
return "钥石 +%d · 累计 %d" % [int(payload.get("keystone_gained", 0)), int(payload.get("keystone_total", 0))]
# 奖励节点三选一头部行(与通关三选一共享同库;渠道白名单分流——node-reward.md v2)
# 奖励节点三选一头部行(与关卡完成三选一共享同库;渠道白名单分流——node-reward.md v2)
static func node_curio_header() -> String:
return "关卡奖励点\n从冒险牌库抽三选一(属性值提升与补给;与通关专长三选一同库分流):\n选择一件带走:"
return "关卡奖励点\n从冒险牌库抽三选一(属性值提升与补给;与关卡完成专长三选一同库分流):\n选择一件带走:"
# 冒险结算屏文案(摘要五件事:打穿没有 / 通关几关 / 止步第几关 / 钥石合计 / 主种子)
# 冒险结算屏文案(摘要五件事:打穿没有 / 完成几关 / 止步第几关 / 钥石合计 / 主种子)
static func adventure_summary_text(summary: Dictionary) -> String:
var verdict := "冒险失败 · 未打穿"
if bool(summary.get("cleared", false)):
verdict = "冒险完成 · 已打穿"
elif String(summary.get("result", "")) == "abandoned":
verdict = "冒险放弃 · 未打穿"
return "%s\n通关 %d 关 · 止步第 %d 层 · 钥石合计 %d · 主种子 %d" % [
return "%s\n完成 %d 关 · 止步第 %d 层 · 钥石合计 %d · 主种子 %d" % [
verdict, int(summary.get("levels_cleared", 0)), int(summary.get("stopped_at", 0)),
int(summary.get("keystone_total", 0)), int(summary.get("master_seed", 0))]
@@ -25,7 +25,7 @@ var _fatal_screen: Callable
var _gp_summary: Callable # () → 战斗总结 GP 行文案(组合根 lambda——_last_gain_amount 取数面在根)
var _pending_curio := {} # 三选一停点载荷暂存(战斗总结结算确认后开三选一面;续玩停点复用)
var _curio_context := "level_clear" # 三选一面上下文(level_clear=通关停点→推进下一关 / reward_node=奖励节点→回图选路)
var _curio_context := "level_clear" # 三选一面上下文(level_clear=关卡完成停点→推进下一关 / reward_node=奖励节点→回图选路)
func _init(ctx: CurioFlowContext) -> void:
@@ -82,7 +82,7 @@ func _skip_empty_stop() -> bool:
return true
# 三选一停点(非末关通关恰一次):Boss 胜利先经战斗总结结算面(settlement-summary v1.1——钥石
# 三选一停点(非末关的关卡完成恰一次):Boss 胜利先经战斗总结结算面(settlement-summary v1.1——钥石
# 增量行在其上),确认后进三选一面(level-clear.md v2);续玩停点恢复同入口(零自动推进契约)
func on_presented(payload: Dictionary) -> void:
if String(payload.get("context", "")) == "feat_ticket":
@@ -111,7 +111,7 @@ func on_presented(payload: Dictionary) -> void:
_refresh_presentations.call()
# 奖励节点三选一(与通关三选一共享同池同权重同上限;CurioPickOverlay 共用面)。
# 奖励节点三选一(与关卡完成三选一共享同池同权重同上限;CurioPickOverlay 共用面)。
# 无替换子交互(同调装配不可变,见类头)。
func present_node_curio() -> void:
var adventure = _adventure_get.call()
@@ -177,7 +177,7 @@ func _curio_card_texts(candidates: Array) -> Array:
# 新件写行即有名有姓)
# 三选一刷新(X/Y 双源 + 对齐商店 MC-1——免费位用尽转 GP 付费档:
# 同 shop_refresh_cost_base/curve 旋钮、同冒险钱包 runner.gp;通关停点经重发 curio_choice_presented 直落
# 同 shop_refresh_cost_base/curve 旋钮、同冒险钱包 runner.gp;关卡完成停点经重发 curio_choice_presented 直落
# 选择面,节点停点在此读在途态重 present——零自动推进契约不破,停点仍在途)
func on_refresh() -> void:
var adventure = _adventure_get.call()
@@ -228,7 +228,7 @@ func on_resolved(result: Dictionary) -> void:
_refresh_presentations.call()
# 继续钮:收三选一面 → 上下文分派(通关停点=推进下一关 / 奖励节点=兑付确认回图选路——零自动推进)
# 继续钮:收三选一面 → 上下文分派(关卡完成停点=推进下一关 / 奖励节点=兑付确认回图选路——零自动推进)
func on_continue() -> void:
_close_modal.call(curio_overlay)
curio_overlay.close_pick()
+2 -2
View File
@@ -188,10 +188,10 @@ static func outcome_text(outcome: Dictionary) -> String:
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] = []
lines.append("[b]模拟 N=%d(atk+%d%% / hp+%d%%)[/b]" % [n, atk_pct, hp_pct])
lines.append("通关率 [color=#%s]%.0f%%[/color](%d/%d)" % [UiKit.HEX_LOG_WIN, 100.0 * float(wins) / float(maxi(1, n)), wins, n])
lines.append("关卡完成率 [color=#%s]%.0f%%[/color](%d/%d)" % [UiKit.HEX_LOG_WIN, 100.0 * float(wins) / float(maxi(1, n)), wins, n])
lines.append("整关承伤:均 %.1f · p90 %.0f(基础 HP %d)" % [_mean(dmg_all), _p90(dmg_all), base_hp])
if not hp_left_win.is_empty():
lines.append("通关剩余 HP 均 %.1f · 进 Boss HP 均 %.1f" % [_mean(hp_left_win), _mean(hp_boss)])
lines.append("关卡完成剩余 HP 均 %.1f · 进 Boss HP 均 %.1f" % [_mean(hp_left_win), _mean(hp_boss)])
lines.append("平均轮数 %.1f" % _mean(rounds_all))
if death_nodes.is_empty():
lines.append("败北节点:—")
+1 -1
View File
@@ -51,7 +51,7 @@ static func _on_runner_node(root: Variant, node: Dictionary, info: Dictionary) -
root._show_board_fx_for_node(t)
root.log_view.append_bb(root.Narrator.node_banner(root.run_scope.runner, node, t))
elif t == "reward":
# 奖励节点三选一(与通关三选一共享同池同权重同上限;CurioPickOverlay 共用面)。
# 奖励节点三选一(与关卡完成三选一共享同池同权重同上限;CurioPickOverlay 共用面)。
# 呈现编排归 curio_interact_controller.present_node_curio。
root._bonus.present_node_curio()
root.log_view.append_bb(root.Narrator.reward_line())
+1 -1
View File
@@ -35,7 +35,7 @@ func is_running() -> bool:
func run(n: int) -> void:
_running = true
# 共享日志会话(模拟运行与真人试玩同日志合规——操作/轮次/战斗/通关全量 JSONL;
# 共享日志会话(模拟运行与真人试玩同日志合规——操作/轮次/战斗/关卡完成全量 JSONL;
# 一文件多局,run 行携带种子切分局次)
var logger = PlaytestLogger.new()
logger.ap_per_round = int(db.action_economy.ap_per_round)
+1 -1
View File
@@ -542,7 +542,7 @@ func _rebuild_recent(data: Dictionary) -> void:
verdict = "完成"
elif String(s.get("result", "")) == "abandoned":
verdict = "放弃"
lines.append("最近冒险:%s · 通关 %d 关 · 钥石 +%d · 种子 %d" % [verdict,
lines.append("最近冒险:%s · 完成 %d 关 · 钥石 +%d · 种子 %d" % [verdict,
int(s.get("levels_cleared", 0)), int(s.get("keystone_total", 0)), int(s.get("master_seed", 0))])
_recent_list.text = " | ".join(lines)
+5 -5
View File
@@ -8,8 +8,8 @@ extends Control
signal closed
var _total_label: Label # 冒险总局数(开局即计含放弃与失败)
var _cleared_label: Label # 通关局数(三关全通才计 1)
var _clears_label: Label # 各关通关次数(一行三关——display_name ×次数)
var _cleared_label: Label # 冒险成功局数(三关全通才计 1)
var _clears_label: Label # 各关完成次数(一行三关——display_name ×次数)
var _boss_first_label: Label # Boss 首杀(已首杀关数/总关数)
var _boss_kills_label: Label # Boss 总击杀(含重复)
var _keystone_badge: UiKit.KeystoneBadge # 钥石余额(badge 工厂件——状态面板同形制)
@@ -33,14 +33,14 @@ func _init() -> void:
UiKit.FS_HINT, false, UiKit.WARM_SOFT)
box.add_child(hint)
# 组一·冒险战绩(终身计数域——存档 v6:总局数开局即计、通关局数三关全通才计)
# 组一·冒险战绩(终身计数域——存档 v6:总局数开局即计、冒险成功局数三关全通才计)
# 对比度口径:页底=暖亮档深棕(BG_BRIGHT),文字全用亮墨族 token(WARM 9.0:1 / WARM_SOFT 8.0:1 /
# CANDLE 7.2:1 / INK_L_GOLD 9.1:1 实测对 BG_BRIGHT 全过 §7.2 ≥4.5:1——深墨族〔INK_LABEL〕
# 压深棕底仅 ~1.3:1 属违例,禁用)
_section_head(box, "冒险战绩")
_total_label = _row(box, "冒险总局数")
_cleared_label = _row(box, "通关局数")
_clears_label = _row(box, "各关通关次数")
_cleared_label = _row(box, "冒险成功局数")
_clears_label = _row(box, "各关完成次数")
_boss_first_label = _row(box, "Boss 首杀")
_boss_kills_label = _row(box, "Boss 总击杀")
@@ -1,5 +1,5 @@
## VERTICAL SLICE - NOT FOR PRODUCTION
## 提升三选一覆盖层(模态):通关停点的三卡点选→确认→兑付反馈两态呈现。
## 提升三选一覆盖层(模态):关卡完成停点的三卡点选→确认→兑付反馈两态呈现。
## 文案拼装归 Narrator 经组合根喂入——本类零文案装配。
class_name CurioPickOverlay
extends Control
@@ -1,5 +1,5 @@
## VERTICAL SLICE - NOT FOR PRODUCTION
## 选档界面·全屏页:3 存档位(新档/进档/删档)+ 槽摘要三项(钥石余额/通关局数/冒险总局数)。
## 选档界面·全屏页:3 存档位(新档/进档/删档)+ 槽摘要三项(钥石余额/冒险成功局数/冒险总局数)。
## 结构树层级:选档 → 进档 → 营地(本面零存档内功能入口——禁营地/冒险/生涯跳转与购买/查看入口);
## 删档必经二次确认(确认弹窗与分派归组合根消费方编排——本面只发意图信号)。
## 槽数与槽态(空/有档)唯一来自组合根喂入数据(open_save_select 每开重建——组合根经卡① 窥视 API
@@ -15,7 +15,7 @@ signal load_slot_requested(slot: int)
signal delete_slot_requested(slot: int)
var _rows_box: VBoxContainer
var _summary_labels: Dictionary[int, Label] = {} # 槽号→摘要读数 label(通关/冒险两值——探针取值缝)
var _summary_labels: Dictionary[int, Label] = {} # 槽号→摘要读数 label(冒险成功/冒险两值——探针取值缝)
var _occupied_flags: Dictionary[int, bool] = {} # 槽号→有档态(操作集二分断言缝;判定源=喂入数据)
var _new_btns: Dictionary[int, Button] = {} # 槽号→新档钮(空/坏槽——操作集断言与点击链驱动缝)
var _load_btns: Dictionary[int, Button] = {} # 槽号→进档钮(有档槽)
@@ -84,7 +84,7 @@ func _build_slot_row(sd: Dictionary) -> void:
sp.size_flags_horizontal = Control.SIZE_EXPAND_FILL
if occupied:
row.add_child(UiKit.keystone_badge("钥石 %d" % int(sd.get("keystone", 0))))
var summary_l := UiKit.label("通关 %d 局 · 冒险 %d 局" % [int(sd.get("cleared", 0)), int(sd.get("total", 0))],
var summary_l := UiKit.label("冒险成功 %d 局 · 冒险 %d 局" % [int(sd.get("cleared", 0)), int(sd.get("total", 0))],
UiKit.FS_BODY, false, UiKit.CANDLE)
_summary_labels[slot] = summary_l # 摘要读数缝(禁词断言枚举 Label 全集时含此件)
row.add_child(summary_l)
+2 -2
View File
@@ -208,7 +208,7 @@ const HEX_LOG_DMG_BODY := "e0a050" # 伤害行正文橙
const HEX_LOG_VOIDED := "b08060" # 未生效行暗橙(⚠ 划线)
const HEX_LOG_CAST := "b09a72" # 施放行暖石(✦ 具名技能)
const HEX_LOG_DECAY := "8a9ab0" # 状态衰减蓝灰
const HEX_LOG_WIN := "80d080" # 胜利判定绿(模拟报告通关率同档)
const HEX_LOG_WIN := "80d080" # 胜利判定绿(模拟报告关卡完成率同档)
const HEX_ROUTE_NOTE := "d0c090" # 奖励点预告暗金(narrator 行)
const HEX_SEED_NOTE := "c8a860" # 种子/开场行暗金(narrator 行)
@@ -400,7 +400,7 @@ const FS_H1 := 34 # 主标题/演算暴击
# ── 秘宝解锁条件计数键 → 呈现标签(秘宝匣柜台条件进度文案单点;键域=
# TreasureItemTable.UNLOCK_COUNTER_KEYS 封闭枚举,键出域走「条件」兜底不炸)──
const TREASURE_REQ_LABELS := {"adventure_cleared": "通关", "boss_kill_count": "Boss击杀", "vision_used": "真视使用"}
const TREASURE_REQ_LABELS := {"adventure_cleared": "冒险成功", "boss_kill_count": "Boss击杀", "vision_used": "真视使用"}
# 秘宝档位 → 呈现名(契约 2 三档;legendary 传说空集合法不在柜)
const TREASURE_TIER_LABELS := {"common": "普通", "excellent": "优秀", "rare": "稀有", "legendary": "传说"}