fix(iter37①): T2 呈现/组合根缺陷修——蓄力档分档·庆祝句柄·互斥四口·模态 fail-fast·站桩按 id 解析
- d2 蓄力档 `_intent_charge` 布尔分档(颜色相等判据退役,深浅金同值不再误判);d4 庆祝浮动 tween 句柄 kill 纪律(close 必杀) - d5/d6/d7/d8 互斥四口:调试钮判据并入 `_sync_sim_button` 单点、组合根两守卫补模拟态/冒险态、`is_running()` 运行态查询面 - d9 ModalStack open/close fail-fast+遮挡账合并非覆盖(重复 open 不再吞旧账);d12 target_id 产发(damage_controller 四处)+站桩两口 id 先名兜底;d17 await 后实例守卫;d18 参数类型注解 - 断言:standee_fx_sweep_test(新增,18 项)/modal_flow_test V6-5(5 项 wd9,V6-1~4 原样同绿)/damage_controller_test target_id/pacing_queue_test 排播 target_id/system_test wd6+wd7 - 代拍板:d18 卡片字面 `lumas_out: Array[Variant] = null` 在 Godot 4.7 解析报错(typed array 不可默认 null),改用 `Variant = null`——唯一合法注法,零行为变化,随代拍板追认单留痕
This commit is contained in:
1 parent
1b9736f065
commit
0529e1691c
19 files changed
+348
-21
No files matched your search
@@ -708,9 +708,9 @@ func _on_battle_end_continue() -> void:
|
||||
|
||||
|
||||
func _start_debug_levels() -> void:
|
||||
# 守卫:调试对局与活跃冒险/结算并存不开放——并发会清写
|
||||
# settlement/编排态=状态机破坏;对局中开窗是只读浮窗,不是开局前置
|
||||
if run_scope.is_adventure or run_scope.settlement != null:
|
||||
# 守卫:调试对局与活跃冒险/结算/批量模拟并存不开放——并发会清写
|
||||
# settlement/编排态=状态机破坏;模拟在跑时 reset_sliders 会作废测量读数(d5 置灰之外的信号绕过闸)
|
||||
if run_scope.is_adventure or run_scope.settlement != null or _tune_sim.is_running():
|
||||
return
|
||||
debug_overlay.reset_sliders()
|
||||
_close_modal(debug_overlay) # 浮窗内入口发起——随开随收(对局期不可再开:门禁在 _show_debug_overlay)
|
||||
@@ -1483,8 +1483,8 @@ func _on_tune_apply(atk_pct: int, hp_pct: int) -> void:
|
||||
|
||||
func _on_tune_simulate(n: int) -> void:
|
||||
# 批量模拟编排 = 控制器承接(tune_sim_controller.gd);E4 后无表态需复位——模拟走 runner 覆盖参数。
|
||||
# 对局中禁用兜底(长程异步模拟与活跃结算并存态不开放;探针 emit 绕过 disabled 时由此守住)
|
||||
if run_scope.settlement != null:
|
||||
# 对局中禁用兜底(长程异步模拟与活跃冒险/结算并存态不开放;探针 emit 绕过 disabled 时由此守住)
|
||||
if run_scope.settlement != null or run_scope.is_adventure:
|
||||
return
|
||||
_tune_sim.run(n)
|
||||
|
||||
|
||||
@@ -19,18 +19,30 @@ func register(m: Control, tags: Array = []) -> void:
|
||||
|
||||
|
||||
func open(m: Control) -> void:
|
||||
var hidden: Array[Control] = []
|
||||
var idx := _list.find(m)
|
||||
if idx < 0:
|
||||
push_error("ModalStack.open: 未登记层不可打开(register 先行)")
|
||||
return
|
||||
var hidden: Array[Control] = []
|
||||
for i in range(idx + 1, _list.size()):
|
||||
var higher: Control = _list[i]
|
||||
if is_instance_valid(higher) and higher.visible:
|
||||
higher.visible = false
|
||||
hidden.append(higher)
|
||||
_occluded[m] = hidden
|
||||
# 遮挡账合并非覆盖:同层重复 open 时上方已隐、hidden 为空,旧账须保留
|
||||
# (空表覆盖会让先前被隐层在本层 close 后永不恢复)
|
||||
var merged: Array = _occluded.get(m, []).duplicate()
|
||||
for h in hidden:
|
||||
if not merged.has(h):
|
||||
merged.append(h)
|
||||
_occluded[m] = merged
|
||||
m.visible = true
|
||||
|
||||
|
||||
func close(m: Control) -> void:
|
||||
if _list.find(m) < 0:
|
||||
push_error("ModalStack.close: 未登记层不可关闭(register 先行)")
|
||||
return
|
||||
m.visible = false
|
||||
for h in _occluded.get(m, []):
|
||||
if is_instance_valid(h):
|
||||
|
||||
@@ -108,11 +108,12 @@ func _play_settlement_mark(row: Dictionary) -> void:
|
||||
if mark_type != "effect" and mark_type != "damage" and mark_type != "heal":
|
||||
return
|
||||
var target := String(row.get("target", ""))
|
||||
var target_id := String(row.get("target_id", ""))
|
||||
var center: Vector2
|
||||
if target == "hero":
|
||||
center = stage.hero_standee_center()
|
||||
else:
|
||||
var v = stage.enemy_standee_center(target) if target != "" and target != "all_enemies" else null
|
||||
var v = stage.enemy_standee_center(target_id, target) if target != "" and target != "all_enemies" else null
|
||||
center = v if v != null else board_view.get_global_rect().get_center()
|
||||
match mark_type:
|
||||
"effect":
|
||||
@@ -133,6 +134,7 @@ func _play_cross_zone_fx(row: Dictionary) -> void:
|
||||
match String(row.get("type", "")):
|
||||
"damage":
|
||||
var target := String(row.get("target", ""))
|
||||
var target_id := String(row.get("target_id", ""))
|
||||
var crit := bool(row.get("crit", false))
|
||||
if target == "" or target == "hero":
|
||||
# 主角受击弹道(禁「敌→主角零反馈」早退):⚔ 危险红飞向主角站桩;
|
||||
@@ -140,7 +142,7 @@ func _play_cross_zone_fx(row: Dictionary) -> void:
|
||||
settlement_fx.play_projectile(from_v, stage.hero_standee_center(), "⚔",
|
||||
UiKit.RED_MINE, crit)
|
||||
return
|
||||
var to_v = stage.enemy_standee_center(target)
|
||||
var to_v = stage.enemy_standee_center(target_id, target)
|
||||
if to_v != null:
|
||||
settlement_fx.play_projectile(from_v, to_v, "⚔",
|
||||
UiKit.GOLD if crit else UiKit.RED_MINE, crit)
|
||||
|
||||
@@ -11,6 +11,7 @@ var db
|
||||
var panel # 调试浮窗 setter 面:atk_pct/hp_pct/set_sim_running/set_sim_progress/set_sim_report
|
||||
var _base_hp := 0 # 报告中的"基础 HP"口径注记值
|
||||
var _driver = null # 关卡自动策略驱动(调用方注入;null = 旧行为运行时 load 兜底)
|
||||
var _running := false # 运行态(run 全程 true——组合根互查守卫用,与面板置灰解耦)
|
||||
|
||||
|
||||
func _init(db_ref, panel_ref, base_hp: int, driver = null) -> void:
|
||||
@@ -27,7 +28,13 @@ func _autoplay():
|
||||
return load("res://sim/level_autoplay.gd") # 与 headless 扫描同口径的自动策略(P3 兜底路径)
|
||||
|
||||
|
||||
# 运行态查询出口(组合根互查守卫:模拟在跑期禁开调试对局/二次模拟)
|
||||
func is_running() -> bool:
|
||||
return _running
|
||||
|
||||
|
||||
func run(n: int) -> void:
|
||||
_running = true
|
||||
# 共享日志会话(模拟运行与真人试玩同日志合规——操作/轮次/战斗/通关全量 JSONL;
|
||||
# 一文件多局,run 行携带种子切分局次)
|
||||
var logger = PlaytestLogger.new()
|
||||
@@ -60,3 +67,4 @@ func run(n: int) -> void:
|
||||
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_running(false)
|
||||
_running = false
|
||||
@@ -24,6 +24,7 @@ var _bonus_row_l: HBoxContainer
|
||||
var _bonus_name: Label
|
||||
var _bonus_val: Label
|
||||
var _cele: TextureRect # 庆祝立绘(idle 占位浮动)
|
||||
var _celebrate_tw: Tween # 庆祝浮动循环句柄(每调先 kill,close 必杀——无限循环禁裸创建)
|
||||
var _btn: Button
|
||||
var _board_rect := Rect2()
|
||||
|
||||
@@ -158,6 +159,9 @@ func close() -> void:
|
||||
_seal.visible = false
|
||||
_seal.modulate = Color.WHITE
|
||||
_cele.visible = false
|
||||
if _celebrate_tw != null and _celebrate_tw.is_valid():
|
||||
_celebrate_tw.kill()
|
||||
_celebrate_tw = null
|
||||
_clear_crest_bricks()
|
||||
|
||||
|
||||
@@ -281,7 +285,10 @@ func _play_dust_sadle() -> void:
|
||||
func _play_celebrate() -> void:
|
||||
if UiKit.motion_scale < 1.0:
|
||||
return
|
||||
var tw := _cele.create_tween()
|
||||
if _celebrate_tw != null and _celebrate_tw.is_valid():
|
||||
_celebrate_tw.kill()
|
||||
_celebrate_tw = _cele.create_tween()
|
||||
var tw := _celebrate_tw
|
||||
tw.set_loops()
|
||||
tw.tween_property(_cele, "position:y", _cele.position.y - 4.0, UiKit.motion(0.6)).set_trans(Tween.TRANS_SINE).set_ease(Tween.EASE_IN_OUT)
|
||||
tw.tween_property(_cele, "position:y", _cele.position.y, UiKit.motion(0.6)).set_trans(Tween.TRANS_SINE).set_ease(Tween.EASE_IN_OUT)
|
||||
|
||||
@@ -305,7 +305,9 @@ func on_step_row_fx(row: Dictionary) -> void:
|
||||
_schedule_arrival_fx(hero_standee, bool(row.get("killed", false)))
|
||||
return
|
||||
hero_standee.set_hero_state("attack") # 我方攻击行——attack 态(短驻自回)
|
||||
var card: Node2D = _card_by_name(target)
|
||||
var card: Node2D = _card_by_id(String(row.get("target_id", "")))
|
||||
if card == null:
|
||||
card = _card_by_name(target) # 旧行无 target_id 时名兜底
|
||||
if card == null:
|
||||
return
|
||||
_schedule_arrival_fx(card, bool(row.get("killed", false)))
|
||||
@@ -338,9 +340,24 @@ func _card_by_name(display_name: String) -> Node2D:
|
||||
return null
|
||||
|
||||
|
||||
# 按敌单位 id 精确解析站桩(同名敌主键——空 id 恒不命中,由调用点名兜底)
|
||||
func _card_by_id(enemy_id: String) -> Node2D:
|
||||
if enemy_id == "":
|
||||
return null
|
||||
for c in _cards:
|
||||
if not is_instance_valid(c):
|
||||
continue
|
||||
if c.enemy_id() == enemy_id:
|
||||
return c
|
||||
return null
|
||||
|
||||
|
||||
# 跨区弹道目标坐标(结算演出链批——组合根传给 SettlementFxView 的全局锚点;壳层口径 C4)
|
||||
func enemy_standee_center(display_name: String):
|
||||
var card := _card_by_name(display_name)
|
||||
# 解析 id 先、display_name 兜底(旧行无 target_id)
|
||||
func enemy_standee_center(target_id: String, display_name: String) -> Variant:
|
||||
var card := _card_by_id(target_id)
|
||||
if card == null:
|
||||
card = _card_by_name(display_name)
|
||||
if card == null or not is_instance_valid(card):
|
||||
return null
|
||||
return world_to_shell(card.position + card.box * 0.5)
|
||||
|
||||
@@ -995,7 +995,7 @@ static func _flip_tex(tex: Texture2D) -> Texture2D:
|
||||
# 地板瓦片明度归一(方向稿 _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:
|
||||
static func _normalize_floor_luma(tex: Texture2D, target_l: float = 0.10, lumas_out: Variant = null) -> Texture2D:
|
||||
var img: Image = tex.get_image()
|
||||
if img.is_compressed():
|
||||
img.decompress()
|
||||
|
||||
@@ -81,7 +81,6 @@ func set_sim_running(running: bool) -> void:
|
||||
# 在活跃对局并存态不开放(长程异步模拟/并发对局编排与活跃结算并存态破坏状态机)
|
||||
func set_context_locked(locked: bool) -> void:
|
||||
_ctx_locked = locked
|
||||
_dbg_btn.disabled = _ctx_locked
|
||||
_sync_sim_button()
|
||||
|
||||
|
||||
@@ -97,9 +96,12 @@ func run_data_probe() -> Dictionary:
|
||||
return _run_data.duplicate()
|
||||
|
||||
|
||||
# 两钮同判据(模拟运行 / 对局上下文锁定对「模拟」「两层调试」两入口等价)——
|
||||
# 判据单点:两 setter 都经此同步,禁在调用点各写一份
|
||||
func _sync_sim_button() -> void:
|
||||
_sim_btn.disabled = _sim_running or _ctx_locked
|
||||
_sim_btn.text = "模拟中…" if _sim_running else "运行模拟"
|
||||
_dbg_btn.disabled = _sim_running or _ctx_locked
|
||||
|
||||
|
||||
func set_sim_progress(text: String) -> void:
|
||||
|
||||
@@ -92,6 +92,8 @@ func play_damage_number(pos: Vector2, text: String, col: Color, crit: bool = fal
|
||||
lab.z_index = 6
|
||||
add_child(lab)
|
||||
await get_tree().process_frame # 等一帧取实际尺寸(文本度量)
|
||||
if not is_instance_valid(lab):
|
||||
return # 等待帧间节点已释放(父层收起/清场)——守卫后立即退出,禁触已释放实例
|
||||
lab.position = pos - lab.size * 0.5
|
||||
var tw := lab.create_tween()
|
||||
tw.tween_property(lab, "position:y", lab.position.y - 40.0, UiKit.motion(1.1)).set_trans(Tween.TRANS_QUAD).set_ease(Tween.EASE_OUT)
|
||||
|
||||
@@ -31,6 +31,7 @@ var _state_revert_tw: Tween
|
||||
var _order := 1
|
||||
var _intent_glyph := ""
|
||||
var _intent_col := Color.WHITE
|
||||
var _intent_charge := false # 蓄力档(pid ∈ CHARGE_SKILL_IDS——意图三档 ③ 分支判据)
|
||||
var _intent_visible := false
|
||||
var _intent_scale := 1.0: # 意图弹入动画参数
|
||||
set(v):
|
||||
@@ -66,6 +67,11 @@ func display_name() -> String:
|
||||
return _display_name
|
||||
|
||||
|
||||
# 敌单位身份出口(d12 站桩解析主键——同名敌按 id 精确匹配;主角站桩恒空串)
|
||||
func enemy_id() -> String:
|
||||
return _enemy_id
|
||||
|
||||
|
||||
# 血条存在性探针出口(世界直角血条常在)
|
||||
var has_world_hp_bar := true
|
||||
|
||||
@@ -153,10 +159,12 @@ func set_order(i: int) -> void:
|
||||
# "" = 未摇定/死亡残位)。不显示技能名——类型级图标 + 一次性动作动画。
|
||||
func set_pending(ptype: String, pid: String) -> void:
|
||||
if _dead:
|
||||
_intent_charge = false
|
||||
_intent_visible = false
|
||||
queue_redraw()
|
||||
return
|
||||
if ptype == "" or ptype == "basic":
|
||||
_intent_charge = false
|
||||
_intent_visible = false # 普通攻击零表示(完全不可预测——§2.4)
|
||||
queue_redraw()
|
||||
return
|
||||
@@ -168,7 +176,8 @@ func set_pending(ptype: String, pid: String) -> void:
|
||||
elif ptype == "debuff":
|
||||
glyph = "☠" # 妨害系
|
||||
col = UiKit.DEBUFF_PURPLE
|
||||
if pid in CHARGE_SKILL_IDS:
|
||||
_intent_charge = pid in CHARGE_SKILL_IDS
|
||||
if _intent_charge:
|
||||
col = UiKit.GOLD_INK # 重击类蓄力档(深金墨——浅垫牌上 ≥4.5:1)
|
||||
_intent_glyph = glyph
|
||||
_intent_col = col
|
||||
@@ -185,7 +194,7 @@ func _play_intent_motion() -> void:
|
||||
tw.tween_property(self, "_intent_scale", 1.0, UiKit.motion(0.16))
|
||||
tw.parallel().tween_property(self, "_glyph_rot", 4.0, UiKit.motion(0.10))
|
||||
tw.tween_property(self, "_glyph_rot", 0.0, UiKit.motion(0.14))
|
||||
if _intent_col == UiKit.GOLD_DEEP:
|
||||
if _intent_charge:
|
||||
tw.tween_property(self, "_intent_scale", 1.22, UiKit.motion(0.28)).set_trans(Tween.TRANS_SINE) # ③蓄力缓推
|
||||
tw.tween_interval(UiKit.motion(0.40)) # 定格微顿
|
||||
tw.tween_property(self, "_intent_scale", 1.0, UiKit.motion(0.18))
|
||||
|
||||
@@ -25,7 +25,7 @@ const PANEL_BORDER := Color8(120, 110, 88) # 面板细线框;兼熄灭点
|
||||
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)
|
||||
const GOLD_BRIGHT := Color8(224, 184, 96) # 金族亮档(敌顺位徽标①②③/重甲徽标对暗舞台底 6.5:1;值=收益金 e0b860 同门;GOLD_DEEP 语义岗〔standee:199 意图判据/board_view:35/level_map:237〕在役不动)
|
||||
const GOLD_BRIGHT := Color8(224, 184, 96) # 金族亮档(敌顺位徽标①②③/重甲徽标对暗舞台底 6.5:1;值=收益金 e0b860 同门)
|
||||
const GOLD_LIT := Color8(230, 190, 90) # 亮金(候选高亮)
|
||||
const GOLD_DIM := Color8(140, 116, 60) # 暗金(已过节点)
|
||||
const GOLD_DONE := Color8(104, 86, 44) # 已过节点暗金·关卡画面呈现态 #68562C(GOLD_DIM #8C743C 对双色墨均 <4.5:1,对比硬指标逼守此值;GOLD_DIM 他用不动)
|
||||
|
||||
Reference in new issue
Block a user