class_name ComboWindowHud extends Control @export var slot_count := 4 const FRAME_TEXTURE := preload("res://assets/ui/combo/four_slot_judgement_frame.png") const FRAME_SIZE := Vector2(255.0, 51.0) const SLOT_SIZE := Vector2(50.0, 51.0) const SLOT_X := [0.0, 68.0, 137.0, 205.0] var frame: TextureRect var panels: Array[PanelContainer] = [] var labels: Array[Label] = [] var clear_tween: Tween func _ready() -> void: custom_minimum_size = FRAME_SIZE size = FRAME_SIZE _build_slots() var bus := _event_bus() bus.connect("combo_updated", refresh) bus.connect("combo_cleared", _on_combo_cleared) func refresh(inputs: Array) -> void: if labels.is_empty(): _build_slots() for index: int in range(labels.size()): var filled := index < inputs.size() labels[index].text = _slot_text(inputs[index]) if filled else "∅" labels[index].modulate = Color(1.0, 1.0, 1.0, 1.0 if filled else 0.32) panels[index].modulate = Color(1.0, 1.0, 1.0, 1.0 if filled else 0.72) if filled: _pulse_slot(panels[index]) func _on_combo_cleared(_reason: StringName) -> void: refresh([]) _flash_clear() func _build_slots() -> void: if not labels.is_empty(): return frame = TextureRect.new() frame.name = "SlotFrame" frame.texture = FRAME_TEXTURE frame.modulate = Color(1.0, 1.0, 1.0, 0.7) frame.mouse_filter = Control.MOUSE_FILTER_IGNORE frame.expand_mode = TextureRect.EXPAND_IGNORE_SIZE frame.stretch_mode = TextureRect.STRETCH_SCALE frame.set_anchors_preset(Control.PRESET_TOP_LEFT) frame.position = Vector2.ZERO frame.size = FRAME_SIZE add_child(frame) for index: int in range(slot_count): var panel := PanelContainer.new() panel.name = "Slot%d" % (index + 1) panel.position = Vector2(float(SLOT_X[index]), 0.0) panel.size = SLOT_SIZE panel.custom_minimum_size = SLOT_SIZE panel.pivot_offset = SLOT_SIZE * 0.5 panel.modulate = Color(1.0, 1.0, 1.0, 0.72) panel.add_theme_stylebox_override("panel", _make_slot_style()) var label := Label.new() label.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER label.vertical_alignment = VERTICAL_ALIGNMENT_CENTER label.text = "∅" label.size_flags_horizontal = Control.SIZE_EXPAND_FILL label.size_flags_vertical = Control.SIZE_EXPAND_FILL label.add_theme_color_override("font_color", Color(0.94, 0.98, 1.0, 1.0)) label.add_theme_color_override("font_shadow_color", Color(0.0, 0.0, 0.0, 0.9)) label.add_theme_constant_override("shadow_offset_x", 2) label.add_theme_constant_override("shadow_offset_y", 2) label.add_theme_font_size_override("font_size", 25) panel.add_child(label) add_child(panel) panels.append(panel) labels.append(label) func _slot_text(value: Variant) -> String: var symbol := StringName(str(value)) match symbol: &"SP": return "sp" &"Ø": return "∅" return str(symbol) func _pulse_slot(panel: PanelContainer) -> void: var tween := create_tween() panel.scale = Vector2(1.08, 1.08) tween.tween_property(panel, "scale", Vector2.ONE, 0.09) func _flash_clear() -> void: if clear_tween != null and clear_tween.is_valid(): clear_tween.kill() clear_tween = create_tween() clear_tween.set_parallel(true) for panel: PanelContainer in panels: panel.scale = Vector2(1.16, 1.16) panel.modulate = Color(1.0, 1.0, 1.0, 1.0) clear_tween.tween_property(panel, "scale", Vector2.ONE, 0.20) clear_tween.tween_property(panel, "modulate", Color(1.0, 1.0, 1.0, 0.72), 0.20) func _make_slot_style() -> StyleBoxFlat: var style := StyleBoxFlat.new() style.content_margin_left = 4.0 style.content_margin_top = 2.0 style.content_margin_right = 4.0 style.content_margin_bottom = 2.0 style.bg_color = Color.TRANSPARENT style.border_width_left = 0 style.border_width_top = 0 style.border_width_right = 0 style.border_width_bottom = 0 return style func _event_bus() -> Node: var root := get_tree().root var bus := root.get_node_or_null("EventBus") if bus == null: bus = load("res://autoload/event_bus.gd").new() bus.name = "EventBus" root.add_child(bus) return bus