Adds commentary to the jellyfish minigame. Reworks the jellyfish pressed interaction. Corrects the stimuli and distractions. Adds the highlight fx to the jellyfishes
This commit is contained in:
@@ -86,6 +86,9 @@ func _ready() -> void:
|
||||
minigame_ui.set_music_volume_slider(UserDataManager.get_music_volume())
|
||||
minigame_ui.set_voice_volume_slider(UserDataManager.get_voice_volume())
|
||||
minigame_ui.set_effects_volume_slider(UserDataManager.get_effects_volume())
|
||||
|
||||
# Stop the current music
|
||||
MusicManager.stop()
|
||||
|
||||
_reset_logs()
|
||||
_initialize()
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
@tool
|
||||
extends MarginContainer
|
||||
class_name Jellyfish
|
||||
|
||||
signal pressed()
|
||||
|
||||
@@ -27,22 +28,34 @@ const sizes: = [
|
||||
|
||||
@onready var animated_sprite: = %AnimatedSprite2D
|
||||
@onready var label: = %AutoSizeLabel.get_node("Label")
|
||||
@onready var highlight_fx: = $HighlightFX
|
||||
@onready var right_fx: = $RightFX
|
||||
@onready var wrong_fx: = $WrongFX
|
||||
|
||||
var stimulus: Dictionary :
|
||||
set(value):
|
||||
stimulus = value
|
||||
label.text = value.Grapheme
|
||||
if label:
|
||||
if value.has("Grapheme"):
|
||||
label.text = value.Grapheme
|
||||
else:
|
||||
label.text = ""
|
||||
|
||||
|
||||
|
||||
func _ready() -> void:
|
||||
stimulus = stimulus
|
||||
|
||||
var f = randf()
|
||||
color = Colors.Red if f < 0.7 else Colors.Green
|
||||
animated_sprite.play("idle")
|
||||
animated_sprite.frame = randi_range(0, animated_sprite.sprite_frames.get_frame_count("idle") - 1)
|
||||
|
||||
|
||||
func is_idle() -> bool:
|
||||
return animated_sprite.animation == "idle"
|
||||
|
||||
|
||||
func happy() -> void:
|
||||
animated_sprite.play("happy")
|
||||
|
||||
@@ -55,6 +68,10 @@ func hit() -> void:
|
||||
animated_sprite.play("hit")
|
||||
|
||||
|
||||
func highlight() -> void:
|
||||
highlight_fx.play()
|
||||
|
||||
|
||||
func right() -> void:
|
||||
right_fx.play()
|
||||
await right_fx.finished
|
||||
@@ -72,6 +89,6 @@ func _on_gui_input(event: InputEvent) -> void:
|
||||
|
||||
func delete() -> void:
|
||||
var tween: = create_tween()
|
||||
tween.tween_property(self, "modulate:a", 0, 1)
|
||||
tween.tween_property(self, "modulate:a", 0, 2)
|
||||
await tween.finished
|
||||
queue_free()
|
||||
|
||||
@@ -1,19 +1,25 @@
|
||||
[gd_scene load_steps=8 format=3 uid="uid://cpkwyypyikpfc"]
|
||||
[gd_scene load_steps=9 format=3 uid="uid://cpkwyypyikpfc"]
|
||||
|
||||
[ext_resource type="Script" path="res://sources/utils/sprite_control.gd" id="1_1b85n"]
|
||||
[ext_resource type="Script" path="res://sources/minigames/jellyfish/jellyfish.gd" id="1_06uwq"]
|
||||
[ext_resource type="PackedScene" uid="uid://cn2rw06pltyiu" path="res://sources/utils/fx/right.tscn" id="2_edt4e"]
|
||||
[ext_resource type="PackedScene" uid="uid://cge0uyn30tcpv" path="res://sources/utils/fx/highlight.tscn" id="3_5uhvd"]
|
||||
[ext_resource type="Script" path="res://sources/utils/percent_margin_container.gd" id="3_vllju"]
|
||||
[ext_resource type="PackedScene" uid="uid://bgio7muqck2bu" path="res://sources/utils/auto_size_label.tscn" id="4_26xiq"]
|
||||
[ext_resource type="SpriteFrames" uid="uid://dquynn3nx52o" path="res://sources/minigames/jellyfish/red_jellyfish_animations.tres" id="4_g5og8"]
|
||||
[ext_resource type="SpriteFrames" uid="uid://dquynn3nx52o" path="res://sources/minigames/jellyfish/red_jellyfish_animations.tres" id="4_d8s11"]
|
||||
[ext_resource type="PackedScene" uid="uid://dlmbxcgiv8tpr" path="res://sources/utils/fx/wrong.tscn" id="6_w1tlu"]
|
||||
|
||||
[node name="Jellyfish" type="MarginContainer"]
|
||||
custom_minimum_size = Vector2(449.43, 449.43)
|
||||
custom_minimum_size = Vector2(466.955, 466.955)
|
||||
offset_right = 200.0
|
||||
offset_bottom = 200.0
|
||||
script = ExtResource("1_06uwq")
|
||||
|
||||
[node name="HighlightFX" parent="." instance=ExtResource("3_5uhvd")]
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 4
|
||||
size_flags_vertical = 4
|
||||
|
||||
[node name="RightFX" parent="." instance=ExtResource("2_edt4e")]
|
||||
position = Vector2(284, 142.25)
|
||||
|
||||
@@ -25,9 +31,10 @@ sprites = [NodePath("AnimatedSprite2D")]
|
||||
|
||||
[node name="AnimatedSprite2D" type="AnimatedSprite2D" parent="SpriteControl"]
|
||||
unique_name_in_owner = true
|
||||
scale = Vector2(1e-05, 1e-05)
|
||||
sprite_frames = ExtResource("4_g5og8")
|
||||
scale = Vector2(2.50538, 2.50538)
|
||||
sprite_frames = ExtResource("4_d8s11")
|
||||
animation = &"idle"
|
||||
frame_progress = 0.106154
|
||||
centered = false
|
||||
|
||||
[node name="PercentMarginContainer" type="Container" parent="."]
|
||||
@@ -46,7 +53,7 @@ unique_name_in_owner = true
|
||||
layout_mode = 2
|
||||
|
||||
[node name="Label" parent="PercentMarginContainer/AspectRatioContainer/AutoSizeLabel" index="0"]
|
||||
text = "A"
|
||||
text = ""
|
||||
|
||||
[node name="WrongFX" parent="." instance=ExtResource("6_w1tlu")]
|
||||
position = Vector2(284, 142.25)
|
||||
|
||||
@@ -14,69 +14,99 @@ class DifficultySettings:
|
||||
|
||||
|
||||
var difficulty_settings: = {
|
||||
0: DifficultySettings.new(4, 0.6, 150),
|
||||
1: DifficultySettings.new(3, 0.5, 175),
|
||||
2: DifficultySettings.new(2, 0.4, 200),
|
||||
3: DifficultySettings.new(1, 0.3, 250),
|
||||
4: DifficultySettings.new(1, 0.2, 300),
|
||||
0: DifficultySettings.new(4, 0.75, 150),
|
||||
1: DifficultySettings.new(3, 0.66, 175),
|
||||
2: DifficultySettings.new(2, 0.33, 200),
|
||||
3: DifficultySettings.new(1, 0.25, 250),
|
||||
4: DifficultySettings.new(1, 0.25, 300),
|
||||
}
|
||||
|
||||
@export var lesson_nb: = 4
|
||||
@export var difficulty: = 0
|
||||
|
||||
var blocking_jellyfish: Array[Control] = []
|
||||
var blocking_jellyfish: Array[Jellyfish] = []
|
||||
|
||||
@onready var spawning_space: Control = %SpawningSpace
|
||||
@onready var spawn_timer: = $SpawnTimer
|
||||
|
||||
|
||||
# Find the stimuli and distractions of the minigame.
|
||||
func _find_stimuli_and_distractions() -> void:
|
||||
stimuli = Database.get_GP_for_lesson(lesson_nb, true)
|
||||
var all_distractions: = Database.get_GP_before_lesson(lesson_nb, true)
|
||||
all_distractions.shuffle()
|
||||
|
||||
var number_of_distractions: int = min((max_progression - 1) / 2, all_distractions.size())
|
||||
var current_distractions: = all_distractions.slice(0, number_of_distractions)
|
||||
# Gets the stimuli (only vowels for now) for the current lesson
|
||||
var current_lesson_stimuli = Database.get_vowels_for_lesson(lesson_nb)
|
||||
|
||||
stimuli.append_array(current_distractions)
|
||||
distractions = stimuli.duplicate()
|
||||
# Calculate the number of stimuli to add from this lesson (70% of the maximum progression)
|
||||
@warning_ignore("narrowing_conversion")
|
||||
var number_of_stimuli: int = max_progression * 0.7
|
||||
|
||||
# Adds the right number of stimuli from current lesson
|
||||
while stimuli.size() < number_of_stimuli:
|
||||
stimuli.append(current_lesson_stimuli[randi() % current_lesson_stimuli.size()])
|
||||
|
||||
# Gets other stimuli from previous errors or lessons
|
||||
var previous_lesson_stimuli = Database.get_vowels_before_lesson(lesson_nb)
|
||||
while stimuli.size() < max_progression:
|
||||
stimuli.append(previous_lesson_stimuli[randi() % previous_lesson_stimuli.size()])
|
||||
|
||||
# Shuffle the stimuli
|
||||
stimuli.shuffle()
|
||||
|
||||
# For each stimuli get the distractors
|
||||
var all_learned_stimuli = previous_lesson_stimuli
|
||||
all_learned_stimuli.append_array(current_lesson_stimuli)
|
||||
for stimulus in stimuli:
|
||||
var stimulus_distractors := []
|
||||
|
||||
for distractor in all_learned_stimuli:
|
||||
if stimulus.Grapheme != distractor.Grapheme and stimulus.Phoneme != distractor.Phoneme:
|
||||
stimulus_distractors.append(distractor)
|
||||
|
||||
# Adds fake distractors (allow to have empty jellyfishes) if there are less than 4 distractors
|
||||
while stimulus_distractors.size() < 4:
|
||||
stimulus_distractors.append({})
|
||||
|
||||
distractions.append(stimulus_distractors)
|
||||
|
||||
print(stimuli)
|
||||
print(distractions)
|
||||
|
||||
|
||||
# Launch the minigame
|
||||
func _start() -> void:
|
||||
super()
|
||||
|
||||
audio_player.stream = Database.get_audio_stream_for_phoneme(stimuli[current_progression % stimuli.size()].Phoneme)
|
||||
audio_player.play()
|
||||
_play_current_stimulus_phoneme()
|
||||
spawn_timer.start()
|
||||
|
||||
|
||||
func _on_spawn_timer_timeout() -> void:
|
||||
_spawn()
|
||||
spawn_timer.wait_time = _get_difficulty_settings().spawn_time
|
||||
|
||||
|
||||
func _process(delta: float) -> void:
|
||||
for jellyfish: Control in spawning_space.get_children():
|
||||
jellyfish.position.y -= _get_difficulty_settings().velocity * delta
|
||||
for jellyfish: Jellyfish in spawning_space.get_children():
|
||||
# Moves the jellyfish upward
|
||||
if jellyfish.is_idle():
|
||||
jellyfish.position.y -= _get_difficulty_settings().velocity * delta
|
||||
|
||||
# Handles the blocking array
|
||||
if jellyfish in blocking_jellyfish and jellyfish.position.y + jellyfish.size.y < spawning_space.size.y:
|
||||
blocking_jellyfish.erase(jellyfish)
|
||||
|
||||
# Destroy the jellyfish when it leaves the screen
|
||||
if jellyfish.position.y + jellyfish.size.y < 0:
|
||||
jellyfish.queue_free()
|
||||
|
||||
|
||||
func _get_difficulty_settings() -> DifficultySettings:
|
||||
return difficulty_settings[difficulty]
|
||||
func _highlight():
|
||||
# TODO Revoir, il faut highlight les méduses jusqu'à ce que la bonne réponse soit trouvée ? Ou pendant un certain temps ? Ou on laisse comme ça ?
|
||||
for jellyfish: Jellyfish in spawning_space.get_children():
|
||||
if jellyfish.stimulus and jellyfish.stimulus.Grapheme == _get_current_stimulus().Grapheme:
|
||||
jellyfish.highlight()
|
||||
|
||||
|
||||
func _spawn() -> void:
|
||||
var new_jellyfish: = jellyfish_scene.instantiate()
|
||||
spawning_space.add_child(new_jellyfish)
|
||||
var is_stimulus: = randf() < _get_difficulty_settings().stimuli_ratio
|
||||
new_jellyfish.stimulus = stimuli[current_progression % stimuli.size()] if is_stimulus else stimuli[randi() % stimuli.size()]
|
||||
# Instantiate a new jellyfish
|
||||
var new_jellyfish : Jellyfish = jellyfish_scene.instantiate()
|
||||
|
||||
# Check if there is enough space to spawn the jellyfish and find a spot
|
||||
var permitted_range: = 0
|
||||
var left_border: = 0
|
||||
# blocking jellyfish is supposed to be ordered
|
||||
# Blocking jellyfish is supposed to be ordered
|
||||
for blocking in blocking_jellyfish:
|
||||
permitted_range += max(0, blocking.position.x - left_border - new_jellyfish.size.x)
|
||||
left_border = blocking.position.x + blocking.size.x
|
||||
@@ -84,9 +114,19 @@ func _spawn() -> void:
|
||||
if permitted_range <= 0:
|
||||
new_jellyfish.queue_free()
|
||||
return
|
||||
|
||||
# Define if the jellyfish is a stimulus or a distraction
|
||||
var is_stimulus: = randf() < _get_difficulty_settings().stimuli_ratio
|
||||
if is_stimulus:
|
||||
new_jellyfish.stimulus = _get_current_stimulus()
|
||||
else:
|
||||
var current_distractors : Array = distractions[current_progression % distractions.size()]
|
||||
new_jellyfish.stimulus = current_distractors[randi() % current_distractors.size()]
|
||||
|
||||
# Randomize the spawn and adds the jellyfish into the scene
|
||||
var random_spawn: = randi_range(0, permitted_range - 1)
|
||||
left_border = 0
|
||||
# blocking jellyfish is supposed to be ordered
|
||||
# Blocking jellyfish is supposed to be ordered
|
||||
for blocking in blocking_jellyfish:
|
||||
var local_permitted_range = max(0, blocking.position.x - left_border - new_jellyfish.size.x)
|
||||
if local_permitted_range <= random_spawn:
|
||||
@@ -96,6 +136,8 @@ func _spawn() -> void:
|
||||
left_border = blocking.position.x + blocking.size.x
|
||||
new_jellyfish.position.x = left_border + random_spawn
|
||||
new_jellyfish.position.y = spawning_space.size.y
|
||||
|
||||
# Insert the new jellyfish on the right spot in the blocking array
|
||||
var found_ind: = -1
|
||||
for i in blocking_jellyfish.size():
|
||||
var blocking: = blocking_jellyfish[i]
|
||||
@@ -105,21 +147,65 @@ func _spawn() -> void:
|
||||
if found_ind < 0:
|
||||
found_ind = blocking_jellyfish.size()
|
||||
blocking_jellyfish.insert(found_ind, new_jellyfish)
|
||||
|
||||
# Connects the new jellyfish with the pressed signal
|
||||
new_jellyfish.pressed.connect(_on_jellyfish_pressed.bind(new_jellyfish))
|
||||
|
||||
# Adds the jellyfish into the scene
|
||||
spawning_space.add_child(new_jellyfish)
|
||||
|
||||
|
||||
func _on_jellyfish_pressed(jellyfish: Control) -> void:
|
||||
# already clicked
|
||||
_log_new_response(jellyfish.stimulus, stimuli[current_progression % stimuli.size()])
|
||||
if jellyfish.get_parent() != spawning_space:
|
||||
func _get_difficulty_settings() -> DifficultySettings:
|
||||
return difficulty_settings[difficulty]
|
||||
|
||||
|
||||
func _get_current_stimulus() -> Dictionary :
|
||||
if stimuli.size() == 0:
|
||||
return {}
|
||||
return stimuli[current_progression % stimuli.size()]
|
||||
|
||||
|
||||
# TODO Peut être à déplacer dans un script attaché à l'AudioStreamPlayer de BaseMinigame
|
||||
func _play_phoneme(phoneme : String) -> void:
|
||||
var phoneme_audiostream = Database.get_audio_stream_for_phoneme(phoneme) as AudioStream
|
||||
if not phoneme_audiostream:
|
||||
push_warning("AudioStream not found for phoneme " + phoneme)
|
||||
return
|
||||
var jellyfish_position: = jellyfish.global_position
|
||||
spawning_space.remove_child(jellyfish)
|
||||
spawning_space.add_sibling(jellyfish)
|
||||
jellyfish.global_position = jellyfish_position
|
||||
var is_right: = false
|
||||
if jellyfish.stimulus == stimuli[current_progression % stimuli.size()]:
|
||||
is_right = true
|
||||
|
||||
audio_player.stream = phoneme_audiostream
|
||||
audio_player.play()
|
||||
|
||||
if audio_player.playing:
|
||||
await audio_player.finished
|
||||
|
||||
|
||||
func _play_current_stimulus_phoneme()-> void:
|
||||
var current_stimulus: = _get_current_stimulus()
|
||||
if not current_stimulus or not current_stimulus.has("Phoneme"):
|
||||
return
|
||||
|
||||
await _play_phoneme(current_stimulus.Phoneme)
|
||||
|
||||
|
||||
# ------------ Connections ------------
|
||||
|
||||
|
||||
func _on_spawn_timer_timeout() -> void:
|
||||
_spawn()
|
||||
spawn_timer.wait_time = _get_difficulty_settings().spawn_time
|
||||
|
||||
|
||||
func _on_jellyfish_pressed(jellyfish: Jellyfish) -> void:
|
||||
|
||||
# Disconnect the jellyfish
|
||||
jellyfish.pressed.disconnect(_on_jellyfish_pressed)
|
||||
|
||||
# Log the answer
|
||||
_log_new_response(jellyfish.stimulus, _get_current_stimulus())
|
||||
|
||||
# Check if the stimulus is right
|
||||
var is_right: bool = jellyfish.stimulus.Grapheme == _get_current_stimulus().Grapheme
|
||||
if is_right:
|
||||
jellyfish.happy()
|
||||
jellyfish.right()
|
||||
current_progression += 1
|
||||
@@ -127,25 +213,30 @@ func _on_jellyfish_pressed(jellyfish: Control) -> void:
|
||||
jellyfish.hit()
|
||||
jellyfish.wrong()
|
||||
current_lives -= 1
|
||||
audio_player.stream = Database.get_audio_stream_for_phoneme(jellyfish.stimulus.Phoneme)
|
||||
audio_player.play()
|
||||
|
||||
await get_tree().create_timer(1).timeout
|
||||
jellyfish.delete()
|
||||
# Play the pressed jellyfish phoneme
|
||||
if jellyfish.stimulus and jellyfish.stimulus.Phoneme:
|
||||
await _play_phoneme(jellyfish.stimulus.Phoneme)
|
||||
|
||||
# Remove the jellyfish
|
||||
await jellyfish.delete()
|
||||
|
||||
# Handle the blocking array
|
||||
if jellyfish in blocking_jellyfish:
|
||||
blocking_jellyfish.erase(jellyfish)
|
||||
|
||||
# Replay the current stimulus
|
||||
if not is_right:
|
||||
_play_stimulus()
|
||||
_play_current_stimulus_phoneme()
|
||||
|
||||
|
||||
# ------------ UI Callbacks ------------
|
||||
|
||||
|
||||
func _on_current_progression_changed() -> void:
|
||||
if current_progression > 0:
|
||||
audio_player.stream = Database.get_audio_stream_for_phoneme(stimuli[current_progression % stimuli.size()].Phoneme)
|
||||
audio_player.play()
|
||||
_play_current_stimulus_phoneme()
|
||||
|
||||
|
||||
func _play_stimulus() -> void:
|
||||
audio_player.stream = Database.get_audio_stream_for_phoneme(stimuli[current_progression % stimuli.size()].Phoneme)
|
||||
audio_player.play()
|
||||
if audio_player.playing:
|
||||
await audio_player.finished
|
||||
await _play_current_stimulus_phoneme()
|
||||
|
||||
@@ -11,10 +11,10 @@
|
||||
|
||||
[node name="JellyfishMinigame" instance=ExtResource("1_hauef")]
|
||||
script = ExtResource("2_gnjlp")
|
||||
lesson_nb = 10
|
||||
difficulty = 0
|
||||
max_number_of_lives = 5
|
||||
max_progression = 8
|
||||
lesson_nb = 12
|
||||
difficulty = 4
|
||||
max_number_of_lives = 3
|
||||
max_progression = 9
|
||||
intro_kalulu_speech = ExtResource("3_6sswr")
|
||||
help_kalulu_speech = ExtResource("2_7li38")
|
||||
win_kalulu_speech = ExtResource("3_ar7nu")
|
||||
|
||||
@@ -101,6 +101,24 @@ func get_GP_before_and_for_lesson(lesson_nb: int, distinct: bool) -> Array:
|
||||
return db.query_result
|
||||
|
||||
|
||||
func get_vowels_for_lesson(lesson_nb: int) -> Array:
|
||||
var query: = "Select Grapheme, Phoneme, LessonNb FROM GPs
|
||||
INNER JOIN GPsInLessons ON GPsInLessons.GPID = GPs.ID
|
||||
INNER JOIN Lessons ON Lessons.ID = GPsInLessons.LessonID AND Lessons.LessonNb == ?
|
||||
WHERE Type=1"
|
||||
db.query_with_bindings(query, [lesson_nb])
|
||||
return db.query_result
|
||||
|
||||
|
||||
func get_vowels_before_lesson(lesson_nb: int) -> Array:
|
||||
var query: = "Select Grapheme, Phoneme, LessonNb FROM GPs
|
||||
INNER JOIN GPsInLessons ON GPsInLessons.GPID = GPs.ID
|
||||
INNER JOIN Lessons ON Lessons.ID = GPsInLessons.LessonID AND Lessons.LessonNb < ?
|
||||
WHERE Type=1"
|
||||
db.query_with_bindings(query, [lesson_nb])
|
||||
return db.query_result
|
||||
|
||||
|
||||
func get_GP_from_word(word: String) -> Array:
|
||||
db.query_with_bindings("SELECT Grapheme, Phoneme FROM Words INNER JOIN GPsInWords ON Words.ID = GPsInWords.WordID AND Words.Word=? INNER JOIN GPs WHERE GPS.ID = GPsInWords.GPID ORDER BY Position", [word])
|
||||
return db.query_result
|
||||
|
||||
@@ -1,17 +1,19 @@
|
||||
extends Node
|
||||
|
||||
@onready var music_player := $MusicPlayer
|
||||
@onready var music_player : AudioStreamPlayer = $MusicPlayer
|
||||
|
||||
const main_menu_music := preload("res://assets/menus/main/ui_title_card_music.mp3")
|
||||
|
||||
func _ready():
|
||||
music_player.stream = main_menu_music
|
||||
music_player.play()
|
||||
play(main_menu_music)
|
||||
|
||||
func _on_music_player_finished():
|
||||
music_player.stream_paused = false
|
||||
music_player.play()
|
||||
|
||||
func play_music(music : AudioStream):
|
||||
func play(music : AudioStream):
|
||||
music_player.stream = music
|
||||
music_player.play()
|
||||
|
||||
func stop():
|
||||
music_player.stop()
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
extends Node2D
|
||||
extends Control
|
||||
|
||||
@onready var animation_player: = $AnimationPlayer
|
||||
|
||||
|
||||
@@ -223,7 +223,9 @@ _data = {
|
||||
[sub_resource type="CanvasItemMaterial" id="CanvasItemMaterial_3qhub"]
|
||||
blend_mode = 1
|
||||
|
||||
[node name="HighlightFX" type="Node2D"]
|
||||
[node name="HighlightFX" type="Control"]
|
||||
layout_mode = 3
|
||||
anchors_preset = 0
|
||||
script = ExtResource("1_uj18s")
|
||||
|
||||
[node name="AnimationPlayer" type="AnimationPlayer" parent="."]
|
||||
|
||||
Reference in New Issue
Block a user