Files
gplan-2/game/presentation/views/settlement_fx_view.gd
T
DKChan 0529e1691c 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`——唯一合法注法,零行为变化,随代拍板追认单留痕
2026-10-25 03:49:05 +08:00

175 lines
8.7 KiB
GDScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
## VERTICAL SLICE - NOT FOR PRODUCTION
## 结算演出层:跨区弹道/光带等结算兑现演出载体(A–D 区之上、模态之下);棋盘区内演出归 BoardFxView。
## 组合根按结算信号调 play_* 传全局坐标;mouse_filter 全忽略不劫持输入。
class_name SettlementFxView
extends Control
func _init() -> void:
mouse_filter = Control.MOUSE_FILTER_IGNORE
set_anchors_preset(Control.PRESET_FULL_RECT)
# 结算轮光带:金橙半透明横带自棋盘上缘扫至下缘(一次 0.5s,自动回收)——
# "棋盘能量向战斗画面汇聚"的开轮语义(§2.2 色彩转金橙主导;区分于 board_fx 的完美封印金光带)。
# 终稿(样张 v2 冻结批 T3):加 2px 亮金前导缘线——扫带运动方向可读(兑现光带逐行横扫 §2.2)
func play_round_sweep(board_rect: Rect2) -> void:
if board_rect.size == Vector2.ZERO:
return
var band := ColorRect.new()
band.color = Color(UiKit.GOLD, 0.22)
band.mouse_filter = Control.MOUSE_FILTER_IGNORE
band.position = Vector2(board_rect.position.x, board_rect.position.y)
band.size = Vector2(board_rect.size.x, 34.0)
add_child(band)
var edge := ColorRect.new()
edge.color = Color(UiKit.GOLD_LIT, 0.7)
edge.mouse_filter = Control.MOUSE_FILTER_IGNORE
edge.size = Vector2(board_rect.size.x, 2.0)
edge.position = band.position
add_child(edge)
var tw := band.create_tween()
tw.tween_property(band, "position:y", board_rect.end.y - 34.0, UiKit.motion(0.5)).set_trans(Tween.TRANS_SINE)
tw.parallel().tween_property(band, "modulate:a", 0.0, UiKit.motion(0.5)).set_delay(UiKit.motion(0.15))
tw.tween_callback(band.queue_free)
var etw := edge.create_tween()
etw.tween_property(edge, "position:y", board_rect.end.y - 2.0, UiKit.motion(0.5)).set_trans(Tween.TRANS_SINE)
etw.parallel().tween_property(edge, "modulate:a", 0.0, UiKit.motion(0.2)).set_delay(UiKit.motion(0.3))
etw.tween_callback(edge.queue_free)
# 跨区弹道:glyph 字形自 from 飞向 to(0.35s + 轻微上抛弧线 + 终点淡出,自动回收)——
# 开格之因 → 站桩之果的因果可视承载(P2);crit 弹道由调用方传加重标记
func play_projectile(from: Vector2, to: Vector2, glyph: String, col: Color, crit: bool = false) -> void:
var shot := UiKit.label(glyph, UiKit.FS_TITLE if crit else UiKit.FS_BODY, true, col)
shot.mouse_filter = Control.MOUSE_FILTER_IGNORE
shot.size = Vector2(24, 24)
shot.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER
shot.vertical_alignment = VERTICAL_ALIGNMENT_CENTER
shot.position = from - shot.size * 0.5
shot.pivot_offset = shot.size * 0.5
shot.z_index = 5
add_child(shot)
var mid := (from + to) * 0.5 + Vector2(0, -36.0) # 轻微上抛弧线
var tw := shot.create_tween()
if crit:
tw.tween_property(shot, "scale", Vector2(1.5, 1.5), UiKit.motion(0.08))
tw.tween_property(shot, "position", mid, UiKit.motion(0.16)).set_trans(Tween.TRANS_SINE).set_ease(Tween.EASE_OUT)
tw.tween_property(shot, "position", to - shot.size * 0.5, UiKit.motion(0.19)).set_trans(Tween.TRANS_SINE).set_ease(Tween.EASE_IN)
tw.parallel().tween_property(shot, "modulate:a", 0.0, UiKit.motion(0.12)).set_delay(UiKit.motion(0.23))
tw.tween_callback(shot.queue_free)
# ---- 轮切换横幅(迭代 8 卡⑦——「第 N 轮」驻留 0.6s 后消隐进下一轮;白名单 0.5–0.8s 内取值)----
# 减动效=静态呈现(驻留后即隐,零位移/零透明度动画——行呈现不跳过同语义,卡⑦ AC③)。
const BANNER_DWELL := 0.6
var _fx_banners := 0 # 演出探针计数(fx_probe 断言面)
var _fx_damage_marks := 0 # 三轮 #3——伤害/治疗数字浮现计数
var _fx_status_pulses := 0 # 三轮 #3——增益/减益状态脉冲计数
# 演出探针出口(卡⑦——轮横幅发射计数)
func probe_banners() -> int:
return _fx_banners
# 演出探针出口(三轮 #3——逐条结算演出计数)
func probe_settlement_marks() -> int:
return _fx_damage_marks + _fx_status_pulses
# 伤害/治疗数字浮现(三轮 #3——用户裁决「打中谁就标出多少伤害/主角被打标出多少」):
# 站桩头顶浮现数字,上飘 34px 渐隐 0.7s(减动效=时长折半同演不跳过);crit 加大加重。
func play_damage_number(pos: Vector2, text: String, col: Color, crit: bool = false) -> void:
_fx_damage_marks += 1
# T17 修复(用户实机「完全没看到伤害数字」):26/32px 大字+1.1s 驻留——旧 20px/0.7s 在
# 结算排播节奏下视觉存在感不足;描边 4→6 同步加重(亮舞台底上保读数)。
var lab := UiKit.label(text, UiKit.FS_H1 if crit else UiKit.FS_TITLE, true, col)
lab.add_theme_color_override("font_outline_color", Color(0.1, 0.08, 0.06, 0.9))
lab.add_theme_constant_override("outline_size", 6)
lab.mouse_filter = Control.MOUSE_FILTER_IGNORE
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)
tw.parallel().tween_property(lab, "modulate:a", 0.0, UiKit.motion(0.45)).set_delay(UiKit.motion(0.65))
tw.tween_callback(lab.queue_free)
# 增益/减益状态脉冲(三轮 #3——用户裁决「先上 Buff/Debuff 特效,再显基础伤害」):
# 目标站桩处色环扩散淡出 0.5s(增益绿/减益红由调用方传色)。
func play_status_pulse(pos: Vector2, col: Color) -> void:
_fx_status_pulses += 1
var ring := Panel.new()
ring.mouse_filter = Control.MOUSE_FILTER_IGNORE
var sb := UiKit.panel_flat(Color(col, 0.0), col, 100, 3)
ring.add_theme_stylebox_override("panel", sb)
ring.size = Vector2(22, 22)
ring.position = pos - ring.size * 0.5
ring.z_index = 5
add_child(ring)
var tw := ring.create_tween()
tw.tween_property(ring, "scale", Vector2(2.4, 2.4), UiKit.motion(0.5)).set_trans(Tween.TRANS_QUAD).set_ease(Tween.EASE_OUT)
tw.parallel().tween_property(ring, "modulate:a", 0.0, UiKit.motion(0.5))
tw.tween_callback(ring.queue_free)
func play_round_banner(ri: int) -> void:
_fx_banners += 1
var banner := Panel.new()
banner.mouse_filter = Control.MOUSE_FILTER_IGNORE
banner.add_theme_stylebox_override("panel", UiKit.panel_flat(Color(UiKit.BG_OVERLAY, 0.88), UiKit.GOLD_DEEP, UiKit.RADIUS_OBJECT, 2))
var lab := UiKit.label("第 %d 轮" % ri, UiKit.FS_TITLE, true, UiKit.GOLD)
lab.set_anchors_and_offsets_preset(Control.PRESET_FULL_RECT)
lab.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER
lab.vertical_alignment = VERTICAL_ALIGNMENT_CENTER
banner.add_child(lab)
banner.size = Vector2(190, 46)
banner.position = Vector2((size.x - banner.size.x) * 0.5, size.y * 0.30) # 棋盘中带上缘——不压明细带
banner.z_index = 4
add_child(banner)
if UiKit.motion_scale < 1.0:
var st := get_tree().create_timer(BANNER_DWELL)
st.timeout.connect(banner.queue_free)
return
banner.modulate.a = 0.0
var tw := banner.create_tween()
tw.tween_property(banner, "modulate:a", 1.0, UiKit.motion(0.15))
tw.tween_interval(UiKit.motion(BANNER_DWELL))
tw.tween_property(banner, "modulate:a", 0.0, UiKit.motion(0.25))
tw.tween_callback(banner.queue_free)
# 开战横幅——整图地形变异预告:board_variants 非空时开战
# 显示一行(文本=「地形变异 · 贫矿脉」式);减动效=静态驻留后即隐(轮横幅先例——行呈现不跳过)。
# 文案/驻留参数 provisional 归本地化批;普通图恒不发射(组合根按快照 board_variants 空判)。
func play_board_variant_banner(text: String) -> void:
_fx_banners += 1
var banner := Panel.new()
banner.mouse_filter = Control.MOUSE_FILTER_IGNORE
banner.add_theme_stylebox_override("panel", UiKit.panel_flat(Color(UiKit.BG_OVERLAY, 0.88), UiKit.GOLD_DEEP, UiKit.RADIUS_OBJECT, 2))
var lab := UiKit.label(text, UiKit.FS_TITLE, true, UiKit.GOLD)
lab.set_anchors_and_offsets_preset(Control.PRESET_FULL_RECT)
lab.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER
lab.vertical_alignment = VERTICAL_ALIGNMENT_CENTER
banner.add_child(lab)
banner.size = Vector2(320, 46)
banner.position = Vector2((size.x - banner.size.x) * 0.5, size.y * 0.30 + 52.0) # 轮横幅下方叠行(首轮同刻两横幅不重叠)
banner.z_index = 4
add_child(banner)
if UiKit.motion_scale < 1.0:
var st := get_tree().create_timer(BANNER_DWELL)
st.timeout.connect(banner.queue_free)
return
banner.modulate.a = 0.0
var tw := banner.create_tween()
tw.tween_property(banner, "modulate:a", 1.0, UiKit.motion(0.15))
tw.tween_interval(UiKit.motion(BANNER_DWELL))
tw.tween_property(banner, "modulate:a", 0.0, UiKit.motion(0.25))
tw.tween_callback(banner.queue_free)