Files
Kalulu/sources/minigames/parakeets/parakeets_minigame.gd
T
2025-06-23 08:53:19 +02:00

251 lines
7.2 KiB
GDScript

@tool
extends Minigame
enum State {
Locked,
Idle,
Selected1,
Selected2,
}
enum Audio {
Fly,
Happy,
Turn,
Win,
}
const AUDIO_STREAMS: Array[AudioStreamMP3] = [
preload("res://assets/minigames/parakeets/audio/parakeet_fly.mp3"),
preload("res://assets/minigames/parakeets/audio/parakeet_happy_short.mp3"),
preload("res://assets/minigames/parakeets/audio/parakeet_turn_over.mp3"),
preload("res://assets/minigames/parakeets/audio/parakeet_win.mp3"),
]
@export var fly_duration: float = 3.0
@onready var branches: Control = $GameRoot/TreeTrunk/Branches
@onready var possible_start_positions_parent: Control = $GameRoot/FlyFrom
@onready var parakeets_node: Control = $GameRoot/Parakeets
@onready var nest_position_1: Control = $GameRoot/TreeTrunk/Branches/TreeBranch5/TreeNestBack/TreeNestFront/Position1
@onready var nest_position_2: Control = $GameRoot/TreeTrunk/Branches/TreeBranch5/TreeNestBack/TreeNestFront/Position2
@onready var nest_positions: Array[Vector2] = [
nest_position_1.global_position,
nest_position_2.global_position
]
@onready var fly_away_position_1: Control = $GameRoot/FlyAway/Position1
@onready var fly_away_position_2: Control = $GameRoot/FlyAway/Position2
@onready var fly_away_positions: Array[Vector2] = [
fly_away_position_1.global_position,
fly_away_position_2.global_position
]
var possible_branch_positions: Array[Vector2] = []
var parakeets: Array[Parakeet] = []
var selected: Array[Parakeet] = []
var state: State = State.Locked
const DIFFICULTY_SETTINGS: Dictionary[int, Dictionary] = {
0: {"pairs_count": 2},
1: {"pairs_count": 3},
2: {"pairs_count": 4},
3: {"pairs_count": 5},
4: {"pairs_count": 6},
}
# Find and set the parameters of the minigame, like the number of lives or the victory conditions.
func _setup_minigame() -> void:
super._setup_minigame()
# Progression management
current_progression = 0
var max_difficulty: int = 0
for difficulty_key: int in DIFFICULTY_SETTINGS.keys():
if difficulty_key > max_difficulty:
max_difficulty = difficulty_key
if difficulty > max_difficulty:
difficulty = max_difficulty
var settings: Dictionary = DIFFICULTY_SETTINGS[difficulty]
var pairs_count: int = min(settings.pairs_count, stimuli.size())
var possible_start_positions: Array = possible_start_positions_parent.get_children()
possible_start_positions.shuffle()
var stimuli2: Array = stimuli.duplicate()
stimuli2.shuffle()
max_progression = pairs_count
var color: Parakeet.Colors = randi_range(0, Parakeet.Colors.size() - 1) as Parakeet.Colors
for index: int in pairs_count:
var new_parakeet1: Parakeet = Parakeet.instantiate()
var new_parakeet2: Parakeet = Parakeet.instantiate()
parakeets_node.add_child(new_parakeet1)
parakeets_node.add_child(new_parakeet2)
new_parakeet1.color = color
new_parakeet2.color = color
new_parakeet1.uppercase = true
new_parakeet2.uppercase = false
new_parakeet1.global_position = possible_start_positions[2 * index].global_position
new_parakeet2.global_position = possible_start_positions[2 * index + 1].global_position
parakeets.append_array([new_parakeet1, new_parakeet2])
var stimulus: Dictionary = stimuli2.pop_back()
new_parakeet1.stimulus = stimulus
new_parakeet2.stimulus = stimulus
new_parakeet1.pressed.connect(_on_parakeet_pressed.bind(new_parakeet1))
new_parakeet2.pressed.connect(_on_parakeet_pressed.bind(new_parakeet2))
# Find the stimuli and distractions of the minigame.
func _find_stimuli_and_distractions() -> void:
stimuli = Database.get_gps_for_lesson(lesson_nb, true)
func _start() -> void:
super()
var possible_positions: Array[Vector2] = []
for branch: Node in branches.get_children():
for child: Control in branch.get_children():
if "Position" in child.name:
possible_positions.append(child.global_position)
possible_positions.shuffle()
await _flying_arrival(possible_positions)
_present_parakeets()
func _on_parakeet_pressed(parakeet: Parakeet) -> void:
match state:
State.Selected2, State.Locked:
return
State.Selected1:
state = State.Locked
if parakeet in selected:
selected.erase(parakeet)
await _turn(parakeet, true)
state = State.Idle
else:
selected.append(parakeet)
await _turn(parakeet, false)
state = State.Selected2
if selected[0].stimulus.Grapheme == selected[1].stimulus.Grapheme:
_correct()
else:
_wrong()
State.Idle:
state = State.Locked
selected.append(parakeet)
await _turn(parakeet, false)
state = State.Selected1
func _correct() -> void:
if selected[0].global_position.x > selected[1].global_position.x:
var buffer: Parakeet = selected[0]
selected[0] = selected[1]
selected[1] = buffer
await _make_selected_happy()
current_progression += 1
await _fly_to(nest_positions)
await _make_selected_coo()
_fly_to(fly_away_positions)
state = State.Idle
selected.clear()
func _wrong() -> void:
await _make_selected_sad()
current_lives -= 1
for parakeet: Parakeet in selected:
parakeet.idle()
await _parakeets_to_front(selected)
await _present_parakeets()
selected.clear()
func _parakeets_to_front(exceptions: Array[Parakeet] = []) -> void:
var coroutine: Coroutine = Coroutine.new()
for parakeet: Parakeet in parakeets:
if parakeet in exceptions:
continue
coroutine.add_future(_turn.bind(parakeet, false))
await coroutine.join_all()
func _present_parakeets() -> void:
await get_tree().create_timer(3).timeout
var coroutine: Coroutine = Coroutine.new()
for parakeet: Parakeet in parakeets:
coroutine.add_future(_turn.bind(parakeet, true))
await coroutine.join_all()
state = State.Idle
func _make_selected_happy() -> void:
for parakeet: Parakeet in selected:
parakeet.right()
parakeet.happy()
audio_player.stream = AUDIO_STREAMS[Audio.Happy]
audio_player.play()
await audio_player.finished
func _make_selected_sad() -> void:
var coroutine: Coroutine = Coroutine.new()
for parakeet: Parakeet in selected:
parakeet.wrong()
coroutine.add_future(parakeet.sad)
await coroutine.join_all()
func _make_selected_coo() -> void:
for parakeet: Parakeet in selected:
parakeet.idle()
audio_player.stream = AUDIO_STREAMS[Audio.Win]
audio_player.play()
await audio_player.finished
func _fly_to(targets: Array[Vector2]) -> void:
var coroutine: Coroutine = Coroutine.new()
audio_player.stream = AUDIO_STREAMS[Audio.Fly]
audio_player.play()
coroutine.add_future(audio_player.finished)
coroutine.add_future(selected[0].fly_to.bind(targets[0], fly_duration))
coroutine.add_future(selected[1].fly_to.bind(targets[1], fly_duration))
await coroutine.join_all()
func _turn(parakeet: Parakeet, to_back: bool) -> void:
if not audio_player.playing:
audio_player.stream = AUDIO_STREAMS[Audio.Turn]
audio_player.play()
if to_back:
await parakeet.turn_to_back()
else:
await parakeet.turn_to_front()
func _flying_arrival(to: Array[Vector2]) -> void:
assert(parakeets.size() <= to.size(), "Some parakeets don't have a destination")
var coroutine: Coroutine = Coroutine.new()
audio_player.stream = AUDIO_STREAMS[Audio.Fly]
audio_player.play()
coroutine.add_future(audio_player.finished)
for index: int in parakeets.size():
coroutine.add_future(parakeets[index].fly_to.bind(to[index], fly_duration))
await coroutine.join_all()
for parakeet: Parakeet in parakeets:
parakeet.idle()