132 lines
4.7 KiB
GDScript
132 lines
4.7 KiB
GDScript
# 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)
|