175 lines
5.5 KiB
GDScript
175 lines
5.5 KiB
GDScript
class_name RhythmTrack
|
|
extends Control
|
|
|
|
@onready var judgement_label: Label = $JudgementLabel
|
|
@onready var center_base: TextureRect = $CenterBase
|
|
@onready var center_flash: TextureRect = $CenterFlash
|
|
@onready var left_mover: TextureRect = $LeftMover
|
|
@onready var right_mover: TextureRect = $RightMover
|
|
@onready var chart_marker_container: Control = $ChartMarkerContainer
|
|
|
|
@export var bpm := 80.0
|
|
|
|
var chart_markers: Array[Control] = []
|
|
var track_center := Vector2.ZERO
|
|
var left_mover_start := Vector2.ZERO
|
|
var right_mover_start := Vector2.ZERO
|
|
var mover_size := Vector2.ZERO
|
|
var center_flash_size := Vector2.ZERO
|
|
var center_flash_color := Color.WHITE
|
|
var beat_flash := 0.0
|
|
var beat_age := 0.0
|
|
var feedback_flash := 0.0
|
|
|
|
|
|
func _ready() -> void:
|
|
_cache_rhythm_track_layout()
|
|
center_flash.modulate = Color(1.0, 1.0, 1.0, 0.0)
|
|
var bus := _event_bus()
|
|
bus.connect("beat_ticked", _on_beat_ticked)
|
|
bus.connect("action_judged", _on_action_judged)
|
|
bus.connect("chart_event_upcoming", _on_chart_event_upcoming)
|
|
bus.connect("chart_event_triggered", _on_chart_event_triggered)
|
|
|
|
|
|
func _process(delta: float) -> void:
|
|
var visual_delta := minf(delta, 1.0 / 30.0)
|
|
beat_age += delta
|
|
beat_flash = maxf(0.0, beat_flash - visual_delta * 8.0)
|
|
_update_movers()
|
|
if feedback_flash > 0.0:
|
|
feedback_flash = maxf(0.0, feedback_flash - visual_delta * 4.0)
|
|
judgement_label.scale = Vector2.ONE * (1.0 + feedback_flash * 0.18)
|
|
|
|
|
|
func _on_beat_ticked(_beat_index: int) -> void:
|
|
center_flash_color = Color.WHITE
|
|
beat_flash = 1.0
|
|
beat_age = 0.0
|
|
_update_movers()
|
|
|
|
|
|
func _on_action_judged(action_name: StringName, rating: Dictionary) -> void:
|
|
var diff := float(rating.get("diff", INF))
|
|
var color: Color = rating.get("color", Color("ff0055")) as Color
|
|
judgement_label.text = "%s %s %s" % [
|
|
str(action_name).to_upper(),
|
|
str(rating.get("label", "miss")).to_upper(),
|
|
_format_signed_ms(diff),
|
|
]
|
|
judgement_label.modulate = color
|
|
judgement_label.scale = Vector2(1.18, 1.18)
|
|
feedback_flash = 1.0
|
|
|
|
|
|
func _on_chart_event_upcoming(event: Resource, time_to_event: float) -> void:
|
|
var marker := Label.new()
|
|
marker.text = _chart_marker_text(event)
|
|
marker.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER
|
|
marker.vertical_alignment = VERTICAL_ALIGNMENT_CENTER
|
|
marker.add_theme_font_size_override("font_size", 14)
|
|
marker.add_theme_color_override("font_color", _chart_marker_color(event))
|
|
marker.custom_minimum_size = Vector2(54, 24)
|
|
marker.position = _chart_marker_position(time_to_event)
|
|
chart_marker_container.add_child(marker)
|
|
chart_markers.append(marker)
|
|
var tween := create_tween()
|
|
tween.tween_property(marker, "modulate:a", 0.25, maxf(0.1, time_to_event))
|
|
tween.tween_callback(marker.queue_free)
|
|
|
|
|
|
func _on_chart_event_triggered(event: Resource) -> void:
|
|
if StringName(str(event.get("event_type"))) == &"camera_pulse":
|
|
center_flash_color = Color(1.0, 0.84, 0.26, 1.0)
|
|
else:
|
|
center_flash_color = _chart_marker_color(event)
|
|
beat_flash = 1.0
|
|
_update_movers()
|
|
|
|
|
|
func _update_movers() -> void:
|
|
var seconds_per_beat := 60.0 / maxf(1.0, bpm)
|
|
var progress := clampf(beat_age / seconds_per_beat, 0.0, 1.0)
|
|
if beat_flash > 0.15:
|
|
progress = 1.0
|
|
|
|
_set_control_center(left_mover, left_mover_start.lerp(track_center, progress), mover_size)
|
|
_set_control_center(right_mover, right_mover_start.lerp(track_center, progress), mover_size)
|
|
_set_control_center(center_flash, track_center, center_flash_size)
|
|
center_flash.modulate = Color(center_flash_color.r, center_flash_color.g, center_flash_color.b, beat_flash)
|
|
|
|
|
|
func _cache_rhythm_track_layout() -> void:
|
|
track_center = _control_center(center_base)
|
|
left_mover_start = _control_center(left_mover)
|
|
right_mover_start = _control_center(right_mover)
|
|
mover_size = left_mover.size
|
|
center_flash_size = center_flash.size
|
|
|
|
|
|
func _control_center(control: Control) -> Vector2:
|
|
return Vector2(
|
|
(control.offset_left + control.offset_right) * 0.5,
|
|
(control.offset_top + control.offset_bottom) * 0.5
|
|
)
|
|
|
|
|
|
func _set_control_center(control: Control, center: Vector2, size: Vector2) -> void:
|
|
control.offset_left = center.x - size.x * 0.5
|
|
control.offset_top = center.y - size.y * 0.5
|
|
control.offset_right = center.x + size.x * 0.5
|
|
control.offset_bottom = center.y + size.y * 0.5
|
|
|
|
|
|
func _chart_marker_text(event: Resource) -> String:
|
|
match StringName(str(event.get("event_type"))):
|
|
&"show_accent_marker":
|
|
return "ACC"
|
|
&"enemy_prepare_attack":
|
|
return "WARN"
|
|
&"enemy_attack_active":
|
|
return "ATK"
|
|
&"enemy_recovery":
|
|
return "REC"
|
|
&"camera_pulse":
|
|
return "CAM"
|
|
return str(event.get("event_type")).to_upper()
|
|
|
|
|
|
func _chart_marker_color(event: Resource) -> Color:
|
|
match StringName(str(event.get("event_type"))):
|
|
&"show_accent_marker":
|
|
return Color("ffd84a")
|
|
&"enemy_prepare_attack":
|
|
return Color("ff7a33")
|
|
&"enemy_attack_active":
|
|
return Color("ff3355")
|
|
&"enemy_recovery":
|
|
return Color("8aa0ff")
|
|
&"camera_pulse":
|
|
return Color("ffffff")
|
|
return Color("00f2ff")
|
|
|
|
|
|
func _chart_marker_position(time_to_event: float) -> Vector2:
|
|
var seconds_per_beat := 60.0 / maxf(1.0, bpm)
|
|
var beat_distance := clampf(time_to_event / seconds_per_beat, 0.0, 4.0)
|
|
var x := track_center.x + beat_distance * 92.0
|
|
return Vector2(x - 27.0, track_center.y + 34.0)
|
|
|
|
|
|
func _format_signed_ms(seconds: float) -> String:
|
|
if is_inf(seconds):
|
|
return "-- ms"
|
|
return "%+.0f ms" % (seconds * 1000.0)
|
|
|
|
|
|
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
|