# VERTICAL SLICE - NOT FOR PRODUCTION # 实现 GDD:design/ux/battle.md(D 区演出层——清图金粒喷泉+彩纸 3–5s 占位、Boss 紫赤影子爬棋盘 # 一次性占位、完美排雷金光带+横幅占位、标中旗加重弹道占位;全部程序化色板原语,真实美术归 战斗表现)。 # Date: 2026-09-12 # 高内聚边界:纯演出——组合根按核心信号调用 play_*;本类零核心依赖、不自持游戏状态。 class_name BoardFxView extends Control const COL_GOLD := UiKit.GOLD const COL_BOSS := UiKit.BOSS_PURPLE # 紫赤 #8F2F6A const COL_FLAG := UiKit.FLAG_RED # 旗朱 const CONFETTI_COLS := [UiKit.GOLD, UiKit.FLAG_RED, UiKit.HERO_BLUE, UiKit.WARM] var _banner: Label func _init() -> void: mouse_filter = Control.MOUSE_FILTER_IGNORE set_anchors_preset(Control.PRESET_FULL_RECT) _banner = UiKit.label("", 26, true) _banner.add_theme_color_override("font_color", COL_GOLD) _banner.set_anchors_preset(Control.PRESET_CENTER_TOP) _banner.grow_horizontal = Control.GROW_DIRECTION_BOTH _banner.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER _banner.visible = false _banner.mouse_filter = Control.MOUSE_FILTER_IGNORE add_child(_banner) # 清图瞬间:金粒喷泉 + 彩纸 3–5s 即收(§2.3 占位) func play_clear_burst() -> void: var fountain := CPUParticles2D.new() fountain.position = size * 0.5 fountain.amount = 90 fountain.lifetime = UiKit.motion(1.6) fountain.one_shot = true fountain.explosiveness = 0.2 fountain.direction = Vector2(0, -1) fountain.spread = 55.0 fountain.initial_velocity_min = 320.0 fountain.initial_velocity_max = 560.0 fountain.gravity = Vector2(0, 700) fountain.scale_amount_min = 2.0 fountain.scale_amount_max = 4.5 fountain.color = COL_GOLD fountain.emitting = true add_child(fountain) var confetti := CPUParticles2D.new() confetti.position = Vector2(size.x * 0.5, -8.0) confetti.amount = 60 confetti.lifetime = UiKit.motion(3.2) confetti.one_shot = true confetti.explosiveness = 0.9 confetti.direction = Vector2(0, 1) confetti.spread = 75.0 confetti.initial_velocity_min = 90.0 confetti.initial_velocity_max = 200.0 confetti.gravity = Vector2(0, 140) confetti.scale_amount_min = 3.0 confetti.scale_amount_max = 5.0 confetti.color_ramp = _confetti_ramp() confetti.emitting = true add_child(confetti) var tw := create_tween() tw.tween_interval(UiKit.motion(3.5)) tw.tween_callback(func(): for n in [fountain, confetti]: if is_instance_valid(n): n.queue_free()) func _confetti_ramp() -> Gradient: var g := Gradient.new() g.set_color(0, CONFETTI_COLS[0]) g.set_color(1, CONFETTI_COLS[1]) return g # Boss 遭遇:紫赤影子爬过棋盘一次性占位演出(§2.6) func play_boss_shadow() -> void: var shadow := ColorRect.new() shadow.color = Color(COL_BOSS, 0.55) shadow.size = Vector2(size.x, 56.0) shadow.position = Vector2(0, -60.0) shadow.mouse_filter = Control.MOUSE_FILTER_IGNORE add_child(shadow) var tw := shadow.create_tween() tw.tween_property(shadow, "position:y", size.y, UiKit.motion(1.1)).set_trans(Tween.TRANS_SINE) tw.tween_property(shadow, "modulate:a", 0.0, UiKit.motion(0.5)) tw.tween_callback(shadow.queue_free) # 完美排雷:金光带加倍扫射 + 横幅占位(§2.2 最高档) func play_perfect_sweep() -> void: for i in 2: var band := ColorRect.new() band.color = Color(COL_GOLD, 0.35) band.size = Vector2(size.x, 14.0 + 10.0 * i) band.position = Vector2(-size.x, size.y * 0.3 + 40.0 * i) band.mouse_filter = Control.MOUSE_FILTER_IGNORE add_child(band) var tw := band.create_tween() tw.tween_property(band, "position:x", size.x + 8.0, UiKit.motion(0.5 + 0.15 * i)) tw.tween_callback(band.queue_free) _show_banner("完美排雷 ×2") # 标中旗:旗化暴击弹道加重占位(自棋盘上缘飞向 B 区敌人卡方向——越界即收) func play_flag_hit_projectile(heavy: bool) -> void: var shot := ColorRect.new() var w := 8.0 if heavy else 4.0 shot.color = COL_FLAG shot.size = Vector2(w, w * 2.2) shot.position = Vector2(size.x * 0.5, size.y * 0.7) shot.mouse_filter = Control.MOUSE_FILTER_IGNORE add_child(shot) var tw := shot.create_tween() tw.tween_property(shot, "position", Vector2(size.x * 0.75, -30.0), UiKit.motion(0.35)) tw.parallel().tween_property(shot, "modulate:a", 0.0, UiKit.motion(0.35)) tw.tween_callback(shot.queue_free) func _show_banner(text: String) -> void: _banner.text = text _banner.visible = true _banner.modulate.a = 0.0 var tw := create_tween() tw.tween_property(_banner, "modulate:a", 1.0, UiKit.motion(0.2)) tw.tween_interval(UiKit.motion(1.6)) tw.tween_property(_banner, "modulate:a", 0.0, UiKit.motion(0.4)) tw.tween_callback(func(): _banner.visible = false)