Initial project sync

This commit is contained in:
wxm
2026-07-06 22:55:14 -07:00
commit 8cca00d0da
1066 changed files with 58585 additions and 0 deletions
+132
View File
@@ -0,0 +1,132 @@
class_name AttackBuffVisual
extends Node2D
const DEFAULT_FRAME_DIRECTORY := "res://assets/ui/buff_effect"
const DEFAULT_MAX_STACKS := 7
@export var frame_directory := DEFAULT_FRAME_DIRECTORY
@export var frames_per_second := 18.0
@export var center_offset := Vector2(0.0, -86.0)
@export var minimum_scale := 0.85
@export var maximum_scale := 1.85
@export var minimum_alpha := 0.28
@export var maximum_alpha := 0.95
var _stacks := 0
var _max_stacks := DEFAULT_MAX_STACKS
var _frame_index := 0
var _frame_elapsed := 0.0
var _pulse_elapsed := 0.0
var _frames: Array[Texture2D] = []
var _sprite: Sprite2D
func _ready() -> void:
z_index = 1
_sprite = Sprite2D.new()
_sprite.name = "BuffEffect"
_sprite.centered = true
_sprite.position = center_offset
_sprite.texture_filter = CanvasItem.TEXTURE_FILTER_NEAREST
_sprite.visible = false
add_child(_sprite)
_load_frames()
_connect_event_buses()
call_deferred("_connect_event_buses")
call_deferred("_sync_from_attack_buff_component")
set_attack_buff(0, DEFAULT_MAX_STACKS)
func _process(delta: float) -> void:
if _stacks <= 0 or _frames.is_empty():
return
var intensity := _intensity()
_pulse_elapsed = fmod(_pulse_elapsed + delta * lerpf(1.3, 2.8, intensity), TAU)
_frame_elapsed += delta
var frame_duration := 1.0 / maxf(1.0, frames_per_second * lerpf(0.85, 1.35, intensity))
while _frame_elapsed >= frame_duration:
_frame_elapsed -= frame_duration
_frame_index = (_frame_index + 1) % _frames.size()
_sprite.texture = _frames[_frame_index]
_apply_effect_state()
func set_attack_buff(stacks: int, max_stacks: int) -> void:
_max_stacks = maxi(1, max_stacks)
_stacks = clampi(stacks, 0, _max_stacks)
set_process(_stacks > 0 and not _frames.is_empty())
visible = _stacks > 0 and not _frames.is_empty()
if _sprite != null:
_sprite.visible = visible
if visible and _sprite.texture == null and not _frames.is_empty():
_sprite.texture = _frames[_frame_index]
_apply_effect_state()
func frame_count() -> int:
return _frames.size()
func _on_attack_buff_changed(stacks: int, max_stacks: int) -> void:
set_attack_buff(stacks, max_stacks)
func _load_frames() -> void:
_frames.clear()
var dir := DirAccess.open(frame_directory)
if dir == null:
return
# 导出包内贴图在目录列表里显示为 xxx.png.import / xxx.png.remap
# 还原原名后 load() 才能命中。编辑器里原图与 .import 同时在列,需去重。
var files := PackedStringArray()
dir.list_dir_begin()
var file_name := dir.get_next()
while not file_name.is_empty():
if not dir.current_is_dir():
var resource_name := file_name.trim_suffix(".import").trim_suffix(".remap")
if resource_name.begins_with("frame") and resource_name.ends_with(".png") and not files.has(resource_name):
files.append(resource_name)
file_name = dir.get_next()
dir.list_dir_end()
files.sort()
for path_name: String in files:
var texture := load("%s/%s" % [frame_directory, path_name]) as Texture2D
if texture != null:
_frames.append(texture)
if not _frames.is_empty() and _sprite != null:
_sprite.texture = _frames[0]
func _apply_effect_state() -> void:
if _sprite == null:
return
if _stacks <= 0 or _frames.is_empty():
_sprite.visible = false
return
var intensity := _intensity()
var pulse := 0.5 + 0.5 * sin(_pulse_elapsed)
_sprite.position = center_offset
_sprite.scale = Vector2.ONE * (lerpf(minimum_scale, maximum_scale, intensity) + pulse * lerpf(0.03, 0.18, intensity))
_sprite.modulate = Color(1.0, 1.0, 1.0, lerpf(minimum_alpha, maximum_alpha, intensity) * lerpf(0.78, 1.0, pulse))
_sprite.visible = true
func _sync_from_attack_buff_component() -> void:
var actor := get_parent()
if actor != null:
actor = actor.get_parent()
var component := actor.get_node_or_null("AttackBuffComponent") if actor != null else null
if component != null and component.has_method("attack_buff_stacks"):
set_attack_buff(int(component.call("attack_buff_stacks")), DEFAULT_MAX_STACKS)
func _connect_event_buses() -> void:
if not is_inside_tree():
return
for child: Node in get_tree().root.get_children():
if child.has_signal("attack_buff_changed") and not child.is_connected("attack_buff_changed", _on_attack_buff_changed):
child.connect("attack_buff_changed", _on_attack_buff_changed)
func _intensity() -> float:
return clampf(float(_stacks) / float(maxi(1, _max_stacks)), 0.0, 1.0)