diff --git a/game/presentation/battle_narrator.gd b/game/presentation/battle_narrator.gd index d8874ef6..ee8671ca 100644 --- a/game/presentation/battle_narrator.gd +++ b/game/presentation/battle_narrator.gd @@ -98,31 +98,36 @@ static func curio_pick_header(payload: Dictionary) -> String: # 对齐 v2.2 追认 b 口径:小/大前缀(小GP/大GP 族)、恢复/AP 不带档位词;道具件掷定前置时 # treasure_line = 具体道具「名 · 效果摘要」行——常空走通用分支) static func bonus_card_text(id: String, tier: String, value: int, treasure_line: String = "") -> String: - var ts := "" - if tier == "common": - ts = " · 小" - elif tier == "rare": - ts = " · 大" - match id: - "attack": - return "攻击力提升%s\n攻击 +%d" % [ts, value] - "max_hp": - return "生命上限提升%s\n生命上限 +%d" % [ts, value] - "armor": - return "护甲等级提升%s\n护甲 +%d" % [ts, value] - "gp": - return "GP 袋%s\nGP +%d" % [ts, value] - "heal": - return "恢复药剂\n生命 +%d(当即回复)" % value - "treasure_item": - if treasure_line != "": - return "秘宝\n%s" % treasure_line - return "秘宝\n随机获得一件" - "ap": - return "行动点提升\n每战首轮 AP +%d" % value + if id == "treasure_item": + return _bonus_treasure_line(treasure_line) + var ts := _tier_suffix(tier) + var templates := {"attack": "攻击力提升%s\n攻击 +%d", "max_hp": "生命上限提升%s\n生命上限 +%d", + "armor": "护甲等级提升%s\n护甲 +%d", "gp": "GP 袋%s\nGP +%d"} + if templates.has(id): + return templates[id] % [ts, value] + if id == "heal": + return "恢复药剂\n生命 +%d(当即回复)" % value + if id == "ap": + return "行动点提升\n每战首轮 AP +%d" % value return id +## 档位尾注域(common=小/rare=大——旧载荷防御口径) +static func _tier_suffix(tier: String) -> String: + if tier == "common": + return " · 小" + if tier == "rare": + return " · 大" + return "" + + +## 秘宝卡文案域(treasure_line 空串=随机获得一件) +static func _bonus_treasure_line(treasure_line: String) -> String: + if treasure_line != "": + return "秘宝\n%s" % treasure_line + return "秘宝\n随机获得一件" + + # 三选一兑付反馈行(选中件效果 + 丢弃提示;tier 缺省普通档——旧载荷防御口径) static func bonus_result_text(result: Dictionary) -> String: var name := "" diff --git a/game/presentation/camp_flow_context.gd b/game/presentation/camp_flow_context.gd index e1f384f8..e8dd0b47 100644 --- a/game/presentation/camp_flow_context.gd +++ b/game/presentation/camp_flow_context.gd @@ -4,33 +4,16 @@ extends RefCounted # 原 13 参长签名收一处命名面——单测可直接 new() 本 context + 控制器获得独立实例化缝)。 # 字段即原构造参数逐一同名迁移;依赖仅视图句柄 + 查询/编排 Callable(组合根注入)。 +## 三束按域收拢(u21——同 u20 形;views=视图句柄/getters=查询 Callable 活引用/flow=编排 Callable): +## 原 12 参长签名收 4 参——字段即原构造参数逐一同名迁移,键面=消费方改址合同。 var db -var meta_get: Callable # () -> MetaProgressionCore(现值回调——组合根 lambda) -var save_get: Callable # () -> SaveManagerCore -var camp_overlay: CampOverlay -var shop_overlay: ShopOverlay -var toast: ToastStack -var start_overlay: Control -var logger # PlaytestLogger(试玩日志验收取数面) -var runner_get: Callable # () -> LevelRunner -var open_modal: Callable -var close_modal: Callable -var refresh_presentations: Callable +var views: Dictionary = {} # {camp_overlay, shop_overlay, toast, start_overlay, logger} +var getters: Dictionary = {} # {meta_get, save_get, runner_get} +var flow: Dictionary = {} # {open_modal, close_modal, refresh_presentations} -func _init(db_ref, meta_get_cb: Callable, save_get_cb: Callable, camp_overlay_ref: CampOverlay, - shop_overlay_ref: ShopOverlay, toast_ref: ToastStack, start_overlay_ref: Control, - logger_ref, runner_get_cb: Callable, open_modal_cb: Callable, close_modal_cb: Callable, - refresh_presentations_cb: Callable) -> void: +func _init(db_ref, views_bundle: Dictionary, getters_bundle: Dictionary, flow_bundle: Dictionary) -> void: db = db_ref - meta_get = meta_get_cb - save_get = save_get_cb - camp_overlay = camp_overlay_ref - shop_overlay = shop_overlay_ref - toast = toast_ref - start_overlay = start_overlay_ref - logger = logger_ref - runner_get = runner_get_cb - open_modal = open_modal_cb - close_modal = close_modal_cb - refresh_presentations = refresh_presentations_cb + views = views_bundle + getters = getters_bundle + flow = flow_bundle diff --git a/game/presentation/camp_shop_presenter.gd b/game/presentation/camp_shop_presenter.gd index fea7cb36..174ed80b 100644 --- a/game/presentation/camp_shop_presenter.gd +++ b/game/presentation/camp_shop_presenter.gd @@ -21,17 +21,17 @@ var _refresh_presentations: Callable func _init(ctx: CampFlowContext) -> void: # 构造参数收拢 context 对象(13 参长签名收一处命名面) db = ctx.db - _meta_get = ctx.meta_get - _save_get = ctx.save_get - camp_overlay = ctx.camp_overlay - shop_overlay = ctx.shop_overlay - toast = ctx.toast - start_overlay = ctx.start_overlay - logger = ctx.logger - _runner_get = ctx.runner_get - _open_modal = ctx.open_modal - _close_modal = ctx.close_modal - _refresh_presentations = ctx.refresh_presentations + _meta_get = ctx.getters.meta_get + _save_get = ctx.getters.save_get + camp_overlay = ctx.views.camp_overlay + shop_overlay = ctx.views.shop_overlay + toast = ctx.views.toast + start_overlay = ctx.views.start_overlay + logger = ctx.views.logger + _runner_get = ctx.getters.runner_get + _open_modal = ctx.flow.open_modal + _close_modal = ctx.flow.close_modal + _refresh_presentations = ctx.flow.refresh_presentations # 打开营地(主菜单入口——开合与开始层互斥仿设置面先例:开始覆盖层非模态且树序在后, diff --git a/game/presentation/curio_flow_context.gd b/game/presentation/curio_flow_context.gd index 480fb587..1e9ac5bf 100644 --- a/game/presentation/curio_flow_context.gd +++ b/game/presentation/curio_flow_context.gd @@ -4,42 +4,16 @@ extends RefCounted # 原 16 参长签名收一处命名面——单测可直接 new() 本 context + 控制器获得独立实例化缝)。 # 字段即原构造参数逐一同名迁移;依赖仅视图句柄 + 查询/编排 Callable(组合根注入)。 +## 三束按域收拢(u20——views=视图句柄/getters=查询 Callable 活引用〔禁快照〕/flow=编排 Callable): +## 原 16 参长签名收 4 参——字段即原构造参数逐一同名迁移,键面=消费方改址合同。 var db -var meta_get: Callable # () -> MetaProgressionCore(现值回调——组合根 lambda) -var curio_overlay: CurioPickOverlay -var end_overlay: BattleEndOverlay -var board_view: BoardView -var toast: ToastStack -var save_get: Callable # () -> SaveManagerCore -var adventure_get: Callable # () -> AdventureRunner(会话现值) -var runner_get: Callable # () -> LevelRunner -var adventure_mode_get: Callable # () -> bool -var open_modal: Callable # (Control) -> void -var open_modal_after_playback: Callable -var close_modal: Callable -var refresh_presentations: Callable -var fatal_screen: Callable -var gp_summary: Callable # (Dictionary) -> String +var views: Dictionary = {} # {curio_overlay, end_overlay, board_view, toast} +var getters: Dictionary = {} # {meta_get, save_get, adventure_get, runner_get, adventure_mode_get} +var flow: Dictionary = {} # {open_modal, open_modal_after_playback, close_modal, refresh_presentations, fatal_screen, gp_summary} -func _init(db_ref, meta_get_cb: Callable, curio_overlay_ref: CurioPickOverlay, end_overlay_ref: BattleEndOverlay, - board_view_ref: BoardView, toast_ref: ToastStack, save_get_cb: Callable, - adventure_get_cb: Callable, runner_get_cb: Callable, adventure_mode_get_cb: Callable, - open_modal_cb: Callable, open_modal_after_playback_cb: Callable, close_modal_cb: Callable, - refresh_presentations_cb: Callable, fatal_screen_cb: Callable, gp_summary_cb: Callable) -> void: +func _init(db_ref, views_bundle: Dictionary, getters_bundle: Dictionary, flow_bundle: Dictionary) -> void: db = db_ref - meta_get = meta_get_cb - curio_overlay = curio_overlay_ref - end_overlay = end_overlay_ref - board_view = board_view_ref - toast = toast_ref - save_get = save_get_cb - adventure_get = adventure_get_cb - runner_get = runner_get_cb - adventure_mode_get = adventure_mode_get_cb - open_modal = open_modal_cb - open_modal_after_playback = open_modal_after_playback_cb - close_modal = close_modal_cb - refresh_presentations = refresh_presentations_cb - fatal_screen = fatal_screen_cb - gp_summary = gp_summary_cb + views = views_bundle + getters = getters_bundle + flow = flow_bundle diff --git a/game/presentation/curio_interact_controller.gd b/game/presentation/curio_interact_controller.gd index f5cbf94f..c59ff1c1 100644 --- a/game/presentation/curio_interact_controller.gd +++ b/game/presentation/curio_interact_controller.gd @@ -8,21 +8,8 @@ extends RefCounted const Narrator := preload("res://presentation/battle_narrator.gd") var db -var _meta_get: Callable # () → 元进度账本现值(组合根 lambda——探针 isolate_save 换注面) -var curio_overlay: CurioPickOverlay -var end_overlay: BattleEndOverlay -var board_view: BoardView -var toast: ToastStack -var _save_get: Callable # () → 存档承载现值(同上——Attunement.slots 读数面) -var _adventure_get: Callable # () → 冒险结构现值(组合根 lambda——随局更替) -var _runner_get: Callable # () → 关卡推进器现值(组合根 lambda——reward_node 继续推进) -var _adventure_mode_get: Callable # () → 冒险模式标记现值(组合根 lambda) -var _open_modal: Callable # 模态栈单口(组合根) -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 取数面在根) +# u13 引用束注入:16 个 _init 抄写成员删除——正文读点改址 ctx 束(u20 三束键名; +# _pending_curio/_curio_context 纯内部态保持宿主) var _pending_curio := {} # 三选一停点载荷暂存(战斗总结结算确认后开三选一面;续玩停点复用) var _curio_context := "level_clear" # 三选一面上下文(level_clear=关卡完成停点→推进下一关 / reward_node=奖励节点→回图选路) @@ -31,21 +18,8 @@ var _curio_context := "level_clear" # 三选一面上下文(level_clear= func _init(ctx: CurioFlowContext) -> void: # 构造参数收拢 context 对象(16 参长签名收一处命名面) db = ctx.db - _meta_get = ctx.meta_get - curio_overlay = ctx.curio_overlay - end_overlay = ctx.end_overlay - board_view = ctx.board_view - toast = ctx.toast - _save_get = ctx.save_get - _adventure_get = ctx.adventure_get - _runner_get = ctx.runner_get - _adventure_mode_get = ctx.adventure_mode_get - _open_modal = ctx.open_modal - _open_modal_after_playback = ctx.open_modal_after_playback - _close_modal = ctx.close_modal - _refresh_presentations = ctx.refresh_presentations - _fatal_screen = ctx.fatal_screen - _gp_summary = ctx.gp_summary + ctx.flow.fatal_screen = ctx.fatal_screen + ctx.flow.gp_summary = ctx.gp_summary # 停点在途判据(组合根 _on_battle_end_continue 分派面——战斗总结确认后开三选一) @@ -66,17 +40,17 @@ func resume_if_pending() -> bool: # 差异仅零面可收——curio_overlay 未开不 close。core 停点经 skip_empty_curio 消化后推进 #(continue_to_next_level 的「停点未兑付拒推进」守卫因此放行)。 func _skip_empty_stop() -> bool: - toast.show_toast("可用战利品为空——本次三选一跳过", "info") + ctx.views.toast.show_toast("可用战利品为空——本次三选一跳过", "info") var adventure = _adventure_get.call() if adventure != null: adventure.stop().skip_empty_curio() # core 停点消化(其守卫=仅空候选放行) if _curio_context == "reward_node": - var runner = _runner_get.call() + var runner = ctx.getters.runner_get.call() if runner != null: runner.continue_after_node() - elif _curio_context == "level_clear" and bool(_adventure_mode_get.call()): + elif _curio_context == "level_clear" and bool(ctx.getters.adventure_mode_get.call()): if not adventure.continue_to_next_level(): - _fatal_screen.call("关卡推进失败——见输出日志") + ctx.flow.fatal_screen.call("关卡推进失败——见输出日志") # feat_ticket = 只消化停点回店(零推进——与 on_continue 同构) _pending_curio = {} return true @@ -93,22 +67,22 @@ func on_presented(payload: Dictionary) -> void: # 空候选防御(零卡不开死面;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)) + ctx.flow.open_modal.call(ctx.views.curio_overlay) + ctx.views.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") # 清空反馈 + ctx.views.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)): # 刷新重发:refreshed 标直落选择面——不再重开胜利总结面 show_pick() return - end_overlay.open_victory(Narrator.battle_end_text("victory", false), "继续", - board_view.get_global_rect(), - "%s\n%s" % [String(_gp_summary.call()), Narrator.bonus_keystone_line(payload)]) - _open_modal_after_playback.call(end_overlay) - _refresh_presentations.call() + ctx.views.end_overlay.open_victory(Narrator.battle_end_text("victory", false), "继续", + ctx.views.board_view.get_global_rect(), + "%s\n%s" % [String(ctx.flow.gp_summary.call()), Narrator.bonus_keystone_line(payload)]) + ctx.flow.open_modal_after_playback.call(ctx.views.end_overlay) + ctx.flow.refresh_presentations.call() # 奖励节点三选一(与关卡完成三选一共享同池同权重同上限;CurioPickOverlay 共用面)。 @@ -121,13 +95,13 @@ func present_node_curio() -> void: if node_cands.is_empty(): # 空池降级——不开面、不挂节点停点(present_node_curio 未调即未挂), # 兑付确认直落回图选路(auto-sim「node_entered 回调内立即 continue_after_node」先例) - toast.show_toast("可用战利品为空——本次三选一跳过", "info") - var runner = _runner_get.call() + ctx.views.toast.show_toast("可用战利品为空——本次三选一跳过", "info") + var runner = ctx.getters.runner_get.call() if runner != null: runner.continue_after_node() return - _open_modal.call(curio_overlay) - curio_overlay.open_pick(Narrator.node_curio_header(), _curio_card_texts(node_cands)) + ctx.flow.open_modal.call(ctx.views.curio_overlay) + ctx.views.curio_overlay.open_pick(Narrator.node_curio_header(), _curio_card_texts(node_cands)) adventure.stop().present_node_curio(node_cands) _feed_refresh_display(adventure) # 刷新面投喂单点 @@ -135,12 +109,12 @@ func present_node_curio() -> void: # 三选一面开出(战斗总结结算确认 / 续玩停点恢复) func show_pick() -> void: var payload: Dictionary[Variant, Variant] = _pending_curio - _open_modal.call(curio_overlay) - curio_overlay.open_pick(Narrator.curio_pick_header(payload), _curio_card_texts(payload.get("candidates", []) as Array)) + ctx.flow.open_modal.call(ctx.views.curio_overlay) + ctx.views.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) # 刷新面投喂单点 - _refresh_presentations.call() + ctx.flow.refresh_presentations.call() # 刷新呈现投喂单点(免费余量 X/Y + GP 付费价 + 冒险钱包可付——读数与商店 set_refresh_display 同构) @@ -148,7 +122,7 @@ func _feed_refresh_display(adventure: AdventureRunner) -> void: var runner = _runner_get.call() var gp := int(runner.gp) if runner != null else 0 var cost: int = adventure.refresh_bank().gp_cost() - curio_overlay.set_refresh_display(adventure.refresh_bank().free_left(), cost, gp >= cost) + ctx.views.curio_overlay.set_refresh_display(adventure.refresh_bank().free_left(), cost, gp >= cost) func _curio_card_texts(candidates: Array) -> Array: @@ -189,15 +163,15 @@ func on_refresh() -> void: # 免费位(X/Y)已穷——GP 付费档(先扣钱包后重抽,余额不足拒绝零副作用) var cost: int = adventure.refresh_bank().gp_cost() if runner == null or not runner.spend_gp(cost): - toast.show_toast("GP不足——刷新失败", "warn") + ctx.views.toast.show_toast("GP不足——刷新失败", "warn") return refreshed = adventure.stop().refresh_curio_paid() - _refresh_presentations.call() # 钱包变化上屏(对齐商店购买后刷新面) + ctx.flow.refresh_presentations.call() # 钱包变化上屏(对齐商店购买后刷新面) if not refreshed: return if _curio_context == "reward_node": var st: Dictionary[Variant, Variant] = adventure.stop().node_curio_pending_state() - curio_overlay.open_pick(Narrator.node_curio_header(), _curio_card_texts(st.get("candidates", []) as Array)) + ctx.views.curio_overlay.open_pick(Narrator.node_curio_header(), _curio_card_texts(st.get("candidates", []) as Array)) _feed_refresh_display(adventure) @@ -218,31 +192,31 @@ func on_pick_confirmed(item_id: String) -> void: # 后续兑付/确认仍走节点路径回图选路) if _curio_context == "reward_node" and kind == "feat_ticket": var st2: Dictionary[Variant, Variant] = adventure.stop().node_curio_pending_state() - curio_overlay.open_pick(Narrator.node_curio_header(), _curio_card_texts(st2.get("candidates", []) as Array)) + ctx.views.curio_overlay.open_pick(Narrator.node_curio_header(), _curio_card_texts(st2.get("candidates", []) as Array)) # 兑付反馈(curio_resolved 到达——结果行上屏,唯一出口 = 继续) func on_resolved(result: Dictionary) -> void: _pending_curio = {} - curio_overlay.show_result(Narrator.bonus_result_text(result)) - _refresh_presentations.call() + ctx.views.curio_overlay.show_result(Narrator.bonus_result_text(result)) + ctx.flow.refresh_presentations.call() # 继续钮:收三选一面 → 上下文分派(关卡完成停点=推进下一关 / 奖励节点=兑付确认回图选路——零自动推进) func on_continue() -> void: - _close_modal.call(curio_overlay) - curio_overlay.close_pick() + ctx.flow.close_modal.call(ctx.views.curio_overlay) + ctx.views.curio_overlay.close_pick() var adventure = _adventure_get.call() if _curio_context == "feat_ticket": return # T5 店内专长凭证面——专长兑付完成后关面回店(无推进/无回图) if _curio_context == "reward_node": - var runner = _runner_get.call() + var runner = ctx.getters.runner_get.call() if runner != null: runner.continue_after_node() return - if bool(_adventure_mode_get.call()) and adventure != null: + if bool(ctx.getters.adventure_mode_get.call()) and adventure != null: if not adventure.continue_to_next_level(): - _fatal_screen.call("关卡推进失败——见输出日志") + ctx.flow.fatal_screen.call("关卡推进失败——见输出日志") # 秘宝 item_id → 显示名投影(keystone_settle_text/替换子命名文案数据源——拼句归 Narrator); diff --git a/game/presentation/hud_presenter.gd b/game/presentation/hud_presenter.gd index 09e8219c..97426bbd 100644 --- a/game/presentation/hud_presenter.gd +++ b/game/presentation/hud_presenter.gd @@ -11,8 +11,13 @@ const Narrator := preload("res://presentation/battle_narrator.gd") # 每次结算行/轮次/节点变化/动作返回后调用(组合根全量重查装配) # 钱包 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: +# 运行态尾参收拢 ctx 束(u19——键 {settlement, runner, level_active, db, refresh_remaining}) +static func refresh(stage: BattleStageView, info: HeroInfoPanelView, ap_bar: ApBarView, ctx: Dictionary) -> void: + var settlement = ctx.get("settlement") + var runner = ctx.get("runner") + var level_active: bool = ctx.get("level_active", false) + var db = ctx.get("db") + var refresh_remaining: int = ctx.get("refresh_remaining", 0) # 特性列表(runner 统一加算区投影:商店购买与关卡大奖励 # 共账本〔level_runner bonus{atk,max_hp,armor_class}〕;空加算=整节隐藏〔视图内空态纪律〕。 # 呈现只读投影零核心触碰;行文格式=白名单项) @@ -36,7 +41,7 @@ static func refresh(stage: BattleStageView, info: HeroInfoPanelView, ap_bar: ApB info.set_feat_holdings([]) if settlement == null: info.set_board_variants([]) # 战斗域角标出战斗即隐(空态纪律) - _refresh_out_of_battle(stage, info, ap_bar, runner, level_active, db) + _refresh_out_of_battle(stage, info, ap_bar, ctx) return var s: Dictionary[Variant, Variant] = settlement.presentation_snapshot() # 地形变异常驻角标投影(整图变异快照 → 文案行; @@ -44,7 +49,7 @@ static func refresh(stage: BattleStageView, info: HeroInfoPanelView, ap_bar: ApB info.set_board_variants(board_variant_lines(db, s.get("board_variants", []))) var hero: Dictionary[Variant, Variant] = s.hero if hero.is_empty() or not bool(hero.get("alive", false)): - _refresh_out_of_battle(stage, info, ap_bar, runner, level_active, db) + _refresh_out_of_battle(stage, info, ap_bar, ctx) return _refresh_hero(stage, info, s) _refresh_economy(ap_bar, s, db, refresh_remaining) @@ -145,7 +150,10 @@ static func sync_settlement_visuals(stage: BattleStageView, board_view: BoardVie # 战斗外(节点间隙/关卡结束)——HP 读 runner 携带值 static func _refresh_out_of_battle(stage: BattleStageView, info: HeroInfoPanelView, - ap_bar: ApBarView, runner, level_active: bool, db) -> void: + ap_bar: ApBarView, ctx: Dictionary) -> void: + var runner = ctx.get("runner") + var level_active: bool = ctx.get("level_active", false) + var db = ctx.get("db") if not level_active: return if runner != null: @@ -185,7 +193,14 @@ static func outcome_text(outcome: Dictionary) -> String: # ---- 批量模拟报告装配(文案归 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: +## 统计束收拢(u19——stats 六键 {wins, dmg_all, hp_left_win, hp_boss, rounds_all, death_nodes}) +static func tune_sim_report(n: int, atk_pct: int, hp_pct: int, stats: Dictionary, base_hp: int) -> String: + var wins: int = stats.get("wins", 0) + var dmg_all: Array = stats.get("dmg_all", []) + var hp_left_win: Array = stats.get("hp_left_win", []) + var hp_boss: Array = stats.get("hp_boss", []) + var rounds_all: Array = stats.get("rounds_all", []) + var death_nodes: Dictionary = stats.get("death_nodes", {}) 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]) diff --git a/game/presentation/main.gd b/game/presentation/main.gd index b2cd87a4..744634d4 100644 --- a/game/presentation/main.gd +++ b/game/presentation/main.gd @@ -816,7 +816,7 @@ func _refresh_used_snapshot() -> int: func _refresh_presentations() -> void: # 每个核心信号/写调用返回后的全量重查装配(更新触发注记——无推送信号,命令式重查) var refresh_remaining: int = maxi(0, int(db.run.action_economy.refresh_cap_per_round) - _refresh_used_snapshot()) - HudPresenter.refresh(stage, info_panel, ap_bar, run_scope.settlement, run_scope.runner, run_scope.level_active, db, refresh_remaining) + HudPresenter.refresh(stage, info_panel, ap_bar, {"settlement": run_scope.settlement, "runner": run_scope.runner, "level_active": run_scope.level_active, "db": db, "refresh_remaining": refresh_remaining}) _treasure_ui.refresh_slots() # C 区秘宝槽重查(试炼镐积点进度随挖格刷新悬停浮窗) _sync_settlement_visuals() diff --git a/game/presentation/main_ui_build.gd b/game/presentation/main_ui_build.gd index badcc2bd..89cb145c 100644 --- a/game/presentation/main_ui_build.gd +++ b/game/presentation/main_ui_build.gd @@ -134,11 +134,17 @@ static func _build_curio(root: Variant) -> void: root.add_child(root.curio_overlay) # 提升三选一与替换子交互(本体在 curio_interact_controller.gd;本类只接线与回调注入, # 模态栈/演播后排窗/呈现刷新/fatal 经回调注入,adventure/runner 现值回调) - root._bonus = CurioInteractController.new(CurioFlowContext.new(root.db, func() -> Variant: return root.meta, root.curio_overlay, root._end_overlay, root.board_view, - root.board_zone.toast, func() -> Variant: return root.save_manager, func() -> Variant: return root.run_scope.adventure, - func() -> Variant: return root.run_scope.runner, func() -> Variant: return root.run_scope.is_adventure, - root._open_modal, root._open_modal_after_playback, root._close_modal, root._refresh_presentations, - root._fatal_screen, func() -> String: return root._gp_summary_text({"gp_dropped": root.run_scope.last_gain_amount}))) # 构造参数收拢 context + root._bonus = CurioInteractController.new(CurioFlowContext.new(root.db, + {"curio_overlay": root.curio_overlay, "end_overlay": root._end_overlay, + "board_view": root.board_view, "toast": root.board_zone.toast}, + {"meta_get": func() -> Variant: return root.meta, "save_get": func() -> Variant: return root.save_manager, + "adventure_get": func() -> Variant: return root.run_scope.adventure, + "runner_get": func() -> Variant: return root.run_scope.runner, + "adventure_mode_get": func() -> Variant: return root.run_scope.is_adventure}, + {"open_modal": root._open_modal, "open_modal_after_playback": root._open_modal_after_playback, + "close_modal": root._close_modal, "refresh_presentations": root._refresh_presentations, + "fatal_screen": root._fatal_screen, + "gp_summary": func() -> String: return root._gp_summary_text({"gp_dropped": root.run_scope.last_gain_amount}}))) # 构造参数收拢 context(u20 三束) root.curio_overlay.pick_confirmed.connect(root._bonus.on_pick_confirmed) root.curio_overlay.continue_pressed.connect(root._bonus.on_continue) root.curio_overlay.refresh_requested.connect(root._bonus.on_refresh) @@ -154,9 +160,13 @@ static func _build_menu_wiring(root: Variant) -> void: root.debug_badge.pressed.connect(root._on_debug_badge_pressed) # 营地/商店投影(本体在 camp_shop_presenter.gd;本类只接线、探针缝保缝转发; # 购买写调用唯一经 MetaProgression/LevelRunner core 单口——组合根 lambda 现值注入) - root._camp_pres = CampShopPresenter.new(CampFlowContext.new(root.db, func() -> Variant: return root.meta, func() -> Variant: return root.save_manager, root.camp_overlay, root.shop_overlay, - root.board_zone.toast, root._start_overlay, root.logger, func() -> Variant: return root.run_scope.runner, - root._open_modal, root._close_modal, root._refresh_presentations)) # 构造参数收拢 context + root._camp_pres = CampShopPresenter.new(CampFlowContext.new(root.db, + {"camp_overlay": root.camp_overlay, "shop_overlay": root.shop_overlay, + "toast": root.board_zone.toast, "start_overlay": root._start_overlay, "logger": root.logger}, + {"meta_get": func() -> Variant: return root.meta, "save_get": func() -> Variant: return root.save_manager, + "runner_get": func() -> Variant: return root.run_scope.runner}, + {"open_modal": root._open_modal, "close_modal": root._close_modal, + "refresh_presentations": root._refresh_presentations})) # 构造参数收拢 context(u21 三束) root.shop_overlay.buy_requested.connect(root._camp_pres.on_shop_buy) root.shop_overlay.refresh_requested.connect(root._camp_pres.on_shop_refresh) # MC-1 root.shop_overlay.leave_pressed.connect(root._camp_pres.on_shop_leave) diff --git a/game/presentation/playtest_logger.gd b/game/presentation/playtest_logger.gd index 1d8dce84..4182051f 100644 --- a/game/presentation/playtest_logger.gd +++ b/game/presentation/playtest_logger.gd @@ -157,8 +157,6 @@ func dump_diagnosis(context: Dictionary) -> String: if f != null: f.store_string(JSON.stringify(dump, " ")) f.close() - print("[playtest_logger] 诊断转储已写入: %s(项目日志目录见输出)" % path) - print(" [diag] %s" % JSON.stringify(context)) return path diff --git a/game/presentation/tune_sim_controller.gd b/game/presentation/tune_sim_controller.gd index bf089c07..5c998019 100644 --- a/game/presentation/tune_sim_controller.gd +++ b/game/presentation/tune_sim_controller.gd @@ -39,32 +39,30 @@ func run(n: int) -> void: # 一文件多局,run 行携带种子切分局次) var logger = PlaytestLogger.new() logger.ap_per_round = int(db.run.action_economy.ap_per_round) - var log_path: String = logger.start_session(SEED_BASE) - print("[tune_sim] 模拟日志: %s" % log_path) + logger.start_session(SEED_BASE) panel.set_sim_running(true) panel.set_sim_progress("模拟中:0/%d …" % n) - var wins := 0 - var dmg_all: Array[float] = [] - var hp_left_win: Array[float] = [] - var hp_boss: Array[float] = [] - var rounds_all: Array[int] = [] - var death_nodes := {} + var stats := {"wins": 0, "dmg_all": [], "hp_left_win": [], "hp_boss": [], "rounds_all": [], "death_nodes": {}} var autoplay = _autoplay() for i in range(n): - var st = autoplay.run_level(db, SEED_BASE + i, 1, panel.atk_pct(), panel.hp_pct(), false, logger) - dmg_all.append(st.hero_dmg) - rounds_all.append(st.rounds) - if st.result == "victory": - wins += 1 - hp_left_win.append(st.hp_left) - hp_boss.append(st.hp_at_boss) - elif st.result == "defeat": - var key := "%d.%s" % [st.defeat_node_index, st.defeat_node_type] - death_nodes[key] = int(death_nodes.get(key, 0)) + 1 + _run_one_level(autoplay, stats, i, panel, logger) panel.set_sim_progress("模拟中:%d/%d …" % [i + 1, n]) await panel.get_tree().process_frame logger.close_session() - panel.set_sim_report(HudPresenter.tune_sim_report(n, panel.atk_pct(), panel.hp_pct(), - wins, dmg_all, hp_left_win, hp_boss, rounds_all, death_nodes, _base_hp)) + panel.set_sim_report(HudPresenter.tune_sim_report(n, panel.atk_pct(), panel.hp_pct(), stats, _base_hp)) + + +## 单局模拟域(循环体提函数——结果四桶记账;u19 stats 六键同构承载) +func _run_one_level(autoplay, stats: Dictionary, i: int, panel, logger) -> void: + var st = autoplay.run_level(db, SEED_BASE + i, 1, panel.atk_pct(), panel.hp_pct(), false, logger) + (stats["dmg_all"] as Array).append(st.hero_dmg) + (stats["rounds_all"] as Array).append(st.rounds) + if st.result == "victory": + stats["wins"] = int(stats["wins"]) + 1 + (stats["hp_left_win"] as Array).append(st.hp_left) + (stats["hp_boss"] as Array).append(st.hp_at_boss) + elif st.result == "defeat": + var key := "%d.%s" % [st.defeat_node_index, st.defeat_node_type] + stats["death_nodes"][key] = int(stats["death_nodes"].get(key, 0)) + 1 panel.set_sim_running(false) _running = false