129 lines
6.1 KiB
GDScript
129 lines
6.1 KiB
GDScript
## VERTICAL SLICE - NOT FOR PRODUCTION
|
||
## 站桩资产与演出态件(StandeeBody 拆件——单一变化原因=美术素材落位规则与演出编排设计):
|
||
## 站桩的资产挂点与一次性演出——立绘精灵落位与换装、主角三态注册与 0.35s 短驻回归、意图徽标弹入动画。
|
||
## 无 class_name(类名缓存面纪律)——经宿主 standee_body preload 引用(mo 实例);
|
||
## 两个属性 setter 经 _owner 回引触发宿主重绘语义逐字;tween 经 _owner 创建(同一 Node 生命绑定)。
|
||
extends RefCounted
|
||
|
||
enum HeroState { IDLE, RESET, ATTACK, HIT } # 主角三态+复位哨兵(外部 String 契约=register/set 形参零变化)
|
||
const _HS_NAME := {HeroState.IDLE: "idle", HeroState.RESET: "reset", HeroState.ATTACK: "attack", HeroState.HIT: "hit"}
|
||
const _HS_VALUE := {"idle": HeroState.IDLE, "reset": HeroState.RESET, "attack": HeroState.ATTACK, "hit": HeroState.HIT}
|
||
|
||
var _owner: Node2D = null
|
||
var _portrait_sprite: Sprite2D = null # 资产挂点(null = 未落位,字形在位)
|
||
var _portrait_tex: Texture2D = null # 落位纹理引用(set_portrait_texture 装载;字形回退判据)
|
||
# 主角三态切换:state 名 → 纹理(battle_stage_view 组合点注册);缺态=保持现纹理
|
||
# (缺位回退链延伸——三态素材缺任一不炸不空)。敌站桩不注册不消费。
|
||
var _hero_tex := {}
|
||
var _hero_state: int = 0
|
||
var _state_revert_tw: Tween
|
||
var _intent_glyph := ""
|
||
var _intent_col := Color.WHITE
|
||
var _intent_visible := false
|
||
var _intent_scale := 1.0: # 意图弹入动画参数
|
||
set(v):
|
||
_intent_scale = v
|
||
_owner.queue_redraw()
|
||
var _glyph_rot := 0.0: # 立绘前倾角(贴图态无此动画)
|
||
set(v):
|
||
_glyph_rot = v
|
||
_owner.queue_redraw()
|
||
|
||
|
||
func _init(owner: Node2D) -> void:
|
||
_owner = owner
|
||
|
||
|
||
# 资产挂点:tex 落位后字形隐退、纹理立绘顶位——零代码换装契约;tex=null 回退字形。
|
||
# flip = 立绘朝向翻转(素材基准朝左时传 true 呈现朝右)。盒高 96px = 48px 立绘源整数 2×
|
||
# (圣经 §8.2 禁非整数缩放)——源换代时随源重算。(box = 宿主冻结 var,由宿主传入)
|
||
func set_portrait_texture(tex: Texture2D, flip: bool, box: Vector2) -> void:
|
||
if tex == null:
|
||
if _portrait_sprite != null:
|
||
_portrait_sprite.visible = false
|
||
_owner.queue_redraw()
|
||
return
|
||
_portrait_tex = tex
|
||
if _portrait_sprite == null:
|
||
_portrait_sprite = Sprite2D.new()
|
||
_portrait_sprite.centered = false
|
||
_portrait_sprite.texture_filter = CanvasItem.TEXTURE_FILTER_NEAREST # 像素帧放大防糊
|
||
_owner.add_child(_portrait_sprite)
|
||
_portrait_sprite.texture = tex
|
||
_portrait_sprite.flip_h = flip
|
||
_portrait_sprite.visible = true
|
||
_place_portrait(box)
|
||
|
||
|
||
func _place_portrait(box: Vector2) -> void:
|
||
# 立绘盒 = (0,44)-(box.x, box.y-36);48 源 ×2 整数档居中
|
||
if _portrait_sprite == null or _portrait_tex == null:
|
||
return
|
||
var box_h := box.y - 44.0 - 36.0
|
||
var w := _portrait_tex.get_width() * 2.0
|
||
var h := _portrait_tex.get_height() * 2.0
|
||
_portrait_sprite.scale = Vector2(2.0, 2.0) # 整数 2×(C5)
|
||
_portrait_sprite.position = Vector2((box.x - w) * 0.5, 44.0 + (box_h - h) * 0.5)
|
||
|
||
|
||
# 组合点注册(battle_stage_view _init——UiKit 常量 load_tex,缺位 null 不注册=该态保持现纹理)
|
||
func register_hero_state(state: String, tex: Texture2D) -> void:
|
||
if tex != null:
|
||
_hero_tex[state] = tex
|
||
|
||
|
||
# 状态切换:现纹理换注册态(attack/hit);未注册态名或缺位 = 保持现纹理不炸。
|
||
# 非 idle 态自动短驻回归 idle(0.35s——演出一拍即回,结算收尾另有显式 idle 兜底)。
|
||
#(is_hero = 宿主 vitals._kind == "hero" 参判传入——三连早退条件序逐字)
|
||
func apply_hero_state(state: String, is_hero: bool) -> void:
|
||
var target: int = _HS_VALUE.get(state, -1)
|
||
if not is_hero or target == _hero_state:
|
||
return
|
||
if not _hero_tex.has(state) and state != "idle":
|
||
return
|
||
_hero_state = target
|
||
if _portrait_sprite != null and _hero_tex.has(state):
|
||
_portrait_sprite.texture = _hero_tex[state]
|
||
if _state_revert_tw != null and _state_revert_tw.is_valid():
|
||
_state_revert_tw.kill()
|
||
if state != "idle":
|
||
_state_revert_tw = _owner.create_tween()
|
||
_state_revert_tw.tween_interval(UiKit.motion(0.35))
|
||
_state_revert_tw.tween_callback(_settle_hero_idle)
|
||
|
||
|
||
func _settle_hero_idle() -> void:
|
||
_hero_state = HeroState.IDLE
|
||
if _portrait_sprite != null and _hero_tex.has("idle"):
|
||
_portrait_sprite.texture = _hero_tex["idle"]
|
||
|
||
|
||
# 复位强制绕同态早退两拍封装(原 _revive 内 hero 分支——先置 RESET 哨兵再回 idle)
|
||
func reset_state() -> void:
|
||
_hero_state = HeroState.RESET # 强制回 idle 态(绕过同态早退)
|
||
apply_hero_state("idle", true)
|
||
|
||
|
||
# 意图三档动作动画(§4.6.4 三档:①普通攻击=零表示〔set_pending 早退〕
|
||
# ②特殊准备=徽标弹入+二次弹跳(高举语义,运动手段 §2)③重击/硬化=缓推蓄力+定格微顿+回落;
|
||
# 三档运动签名互异,灰度/剪影层可辨)。摇定时一次性短动画,随后静帧保持。
|
||
#(charge = 宿主 _intent_charge 分档布尔参判传入)
|
||
func play_intent_motion(charge: bool) -> void:
|
||
var tw := _owner.create_tween()
|
||
tw.tween_property(self, "_intent_scale", 1.6, UiKit.motion(0.10))
|
||
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 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))
|
||
else:
|
||
tw.tween_property(self, "_intent_scale", 1.26, UiKit.motion(0.11)) # ②二次弹跳(高举)
|
||
tw.tween_property(self, "_intent_scale", 1.0, UiKit.motion(0.13))
|
||
|
||
|
||
# 三态探针出口(fx_probe——当前态名+注册态集合;宿主 hero_state_probe 读件投影,外形零变)
|
||
func state_probe() -> Dictionary:
|
||
return {"state": _HS_NAME.get(_hero_state, "idle"), "registered": _hero_tex.keys()}
|