Files
gplan-2/game/presentation/views/settlement_fx_view.gd
T

153 lines
7.8 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
# 实现 GDD:design/ux/battle.md(§Transitions 结算逐行兑现——B 区因果演出链骨架:
# 结算轮光带自棋盘区横扫 → 伤害/治疗弹道自棋盘飞向敌我站桩 → 受击闪白与血条递减由站桩侧承接)。
# 位置语义:全屏覆盖层(A–D 区之上、模态之下),只做跨区飞行的演出载体——棋盘区内演出
# (清图金粒/完美排雷横幅/标中旗弹道/Boss 影子)仍归 board_fx_view。
# 高内聚边界:纯演出——组合根按结算信号调用 play_*,传全局坐标(组合根持有棋盘与站桩两域句柄);
# 本类零核心依赖、不自持游戏状态、mouse_filter 全忽略(不劫持输入)。
# 动画密度/缓动曲线归战斗表现样张批终稿——本批落"因果全程可见"骨架(规划兑现 P2 义务)。
# Date: 2026-09-20(结算演出链批)
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, 22 if crit else 16, true)
shot.add_theme_color_override("font_color", 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, 32 if crit else 26, true)
lab.add_theme_color_override("font_color", 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 # 等一帧取实际尺寸(文本度量)
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, 8, 2))
var lab := UiKit.label("第 %d 轮" % ri, 22, true)
lab.add_theme_color_override("font_color", 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)