2026-01-30 00:35:01 +08:00
|
|
|
class_name NotePool extends Node
|
|
|
|
|
|
|
|
|
|
@export_group("Start")
|
|
|
|
|
## Tap notes to spawn on [method _ready].
|
|
|
|
|
@export var tap_size: int = 30
|
|
|
|
|
|
|
|
|
|
## Hold notes to spawn on [method _ready].
|
|
|
|
|
@export var hold_size: int = 10
|
|
|
|
|
|
|
|
|
|
@export_group("Skin")
|
|
|
|
|
## [StringName] of the tap note skin to use.
|
|
|
|
|
@export var tap_skin: StringName = "default"
|
|
|
|
|
|
|
|
|
|
## [StringName] of the hold note skin to use.
|
|
|
|
|
@export var hold_skin: StringName = "default"
|
|
|
|
|
|
|
|
|
|
func get_note(type: NoteVisual.TYPE) -> NoteVisual:
|
2026-02-01 23:17:51 +08:00
|
|
|
match type:
|
|
|
|
|
NoteVisual.TYPE.TAP:
|
|
|
|
|
return get_tap()
|
|
|
|
|
NoteVisual.TYPE.HOLD:
|
|
|
|
|
return get_hold()
|
|
|
|
|
_:
|
|
|
|
|
return null
|
|
|
|
|
|
|
|
|
|
func return_note(note: NoteVisual) -> void:
|
|
|
|
|
if note is TapNote:
|
|
|
|
|
note.reset()
|
|
|
|
|
return_tap(note as TapNote)
|
|
|
|
|
elif note is HoldNote:
|
|
|
|
|
note.reset()
|
|
|
|
|
return_hold(note as HoldNote)
|
2026-01-30 00:35:01 +08:00
|
|
|
|
|
|
|
|
## Get a tap note. Instantiates one if no free nodes are available.
|
|
|
|
|
func get_tap() -> TapNote:
|
|
|
|
|
var tap: TapNote = _free_taps.pop_back()
|
|
|
|
|
if tap == null:
|
|
|
|
|
tap = _instantiate_tap()
|
|
|
|
|
_used_taps[tap] = true
|
|
|
|
|
return tap
|
|
|
|
|
|
|
|
|
|
## Return a tap note.
|
|
|
|
|
func return_tap(tap: TapNote) -> void:
|
|
|
|
|
var parent: Node = tap.get_parent()
|
|
|
|
|
if parent:
|
|
|
|
|
parent.remove_child(tap)
|
|
|
|
|
|
|
|
|
|
if not _used_taps.erase(tap):
|
|
|
|
|
push_warning(
|
|
|
|
|
"Returning tap note ", tap,
|
|
|
|
|
" that was not spawned by pool ", self, "."
|
|
|
|
|
)
|
|
|
|
|
_free_taps.append(tap)
|
|
|
|
|
|
|
|
|
|
## Get a hold note. Instantiates one if no free nodes are available.
|
|
|
|
|
func get_hold() -> HoldNote:
|
|
|
|
|
var hold: HoldNote = _free_holds.pop_back()
|
|
|
|
|
if hold == null:
|
|
|
|
|
hold = _instantiate_hold()
|
|
|
|
|
_used_holds[hold] = true
|
|
|
|
|
return hold
|
|
|
|
|
|
|
|
|
|
## Return a hold note.
|
|
|
|
|
func return_hold(hold: HoldNote) -> void:
|
|
|
|
|
var parent: Node = hold.get_parent()
|
|
|
|
|
if parent:
|
|
|
|
|
parent.remove_child(hold)
|
|
|
|
|
|
|
|
|
|
if not _used_holds.erase(hold):
|
|
|
|
|
push_warning(
|
|
|
|
|
"Returning hold note ", hold,
|
|
|
|
|
" that was not spawned by pool ", self, "."
|
|
|
|
|
)
|
|
|
|
|
_free_holds.append(hold)
|
|
|
|
|
|
|
|
|
|
## Spawn the given number of notes.
|
|
|
|
|
func reserve(taps: int, holds: int) -> void:
|
|
|
|
|
for i: int in range(taps):
|
|
|
|
|
_free_taps.append(_instantiate_tap())
|
|
|
|
|
|
|
|
|
|
for i: int in range(holds):
|
|
|
|
|
_free_holds.append(_instantiate_hold())
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# ========= IMPLEMENTATION ========= #
|
|
|
|
|
var _free_taps: Array[TapNote]
|
|
|
|
|
var _used_taps: Dictionary[TapNote, bool]
|
|
|
|
|
|
|
|
|
|
var _free_holds: Array[HoldNote]
|
|
|
|
|
var _used_holds: Dictionary[HoldNote, bool]
|
|
|
|
|
|
|
|
|
|
func _instantiate_tap() -> TapNote:
|
|
|
|
|
return NoteScenes.get_tap(tap_skin).instantiate() as TapNote
|
|
|
|
|
|
|
|
|
|
func _instantiate_hold() -> HoldNote:
|
|
|
|
|
return NoteScenes.get_tap(tap_skin).instantiate() as HoldNote
|