Add target score setting + victory overlay (animation, victory audio, reset button)

This commit is contained in:
enne2
2026-08-18 18:45:21 +02:00
parent 94f05adf34
commit 44160adc1a
4 changed files with 144 additions and 8 deletions
+51 -8
View File
@@ -14,8 +14,8 @@ const VIEW := Vector2(1280, 720)
const LANE_TOP := 210.0
const LANE_GAP := 98.0
const BALL_RADIUS := 40.0
const HOME_X := 1210.0 # alloggiamento destro (a tutta larghezza)
const LEFT_X := 70.0 # alloggiamento sinistro (a tutta larghezza)
const HOME_X := 1180.0 # alloggiamento destro (dentro i bordi)
const LEFT_X := 100.0 # alloggiamento sinistro (dentro i bordi)
const HIT_RADIUS := BALL_RADIUS * 1.4 # area di tocco/clic (maggiorata)
# I valori di calibrazione (velocità, moltiplicatori, decay) sono gestiti dal
@@ -61,9 +61,13 @@ var sound_launch: AudioStreamWAV
var sound_error: AudioStreamWAV
var sound_timeout: AudioStreamWAV
var sound_milestone: AudioStreamWAV
var sound_victory: AudioStreamWAV
var victory_layer: CanvasLayer
var victory_shown := false
var ball_sounds := {} # color_name -> AudioStreamWAV (tono pentatonico)
var bgm: AudioStreamPlayer
var options_menu: CanvasLayer
var lock_menu: CanvasLayer
func _ready() -> void:
@@ -74,6 +78,12 @@ func _ready() -> void:
_setup_bgm()
options_menu = load("res://scripts/options_menu.gd").new()
add_child(options_menu)
lock_menu = load("res://scripts/lock_menu.gd").new()
lock_menu.unlocked.connect(_open_options)
add_child(lock_menu)
victory_layer = load("res://scripts/victory_overlay.gd").new()
victory_layer.reset_requested.connect(_reset_after_victory)
add_child(victory_layer)
_new_game()
@@ -106,17 +116,22 @@ func _unhandled_input(event: InputEvent) -> void:
_handle_click(pos)
func _request_options() -> void:
# Le Opzioni sono protette: prima la sequenza di sblocco (per adulti)
if lock_menu:
lock_menu.show_menu()
func _open_options() -> void:
if options_menu:
options_menu.show_menu()
func _toggle_options() -> void:
if options_menu:
if options_menu.visible:
options_menu.hide_menu()
else:
options_menu.show_menu()
if options_menu and options_menu.visible:
options_menu.hide_menu()
else:
_request_options()
# ---------------------------------------------------------------- costruzione
@@ -206,7 +221,7 @@ func _build_ui() -> void:
opt_hover.bg_color = Color("#ebcb96")
opts_btn.add_theme_stylebox_override("hover", opt_hover)
opts_btn.add_theme_stylebox_override("pressed", opt_style)
opts_btn.pressed.connect(_open_options)
opts_btn.pressed.connect(_request_options)
layer.add_child(opts_btn)
# feedback celebrativo alle milestone
@@ -239,6 +254,8 @@ func _init_audio() -> void:
sound_error = _create_tone(220.0, 0.15, 0.16) # "pop" basso e attenuato
sound_timeout = _create_tone(330.0, 0.2, 0.16) # tono dolce
sound_milestone = _create_soft_arpeggio([523.25, 659.25, 783.99, 1046.5], 0.12)
# audio di vittoria: arpeggio ascendente luminoso ma morbido (sensory-friendly)
sound_victory = _create_soft_arpeggio([523.25, 659.25, 783.99, 1046.5, 1318.51, 1567.98], 0.16)
# tono pentatonico per ogni pallina colorata
for cn in BALL_NOTES:
ball_sounds[cn] = _create_tone(BALL_NOTES[cn], 0.2, 0.3)
@@ -363,6 +380,7 @@ func _handle_click(pos: Vector2) -> void:
_pick_target()
_update_hud()
_check_milestone()
_check_victory()
else:
# tasto sbagliato: suono morbido, penalità gentile (combo -1) o reset
_play_sound(sound_error)
@@ -449,6 +467,31 @@ func _update_hud() -> void:
# ---------------------------------------------- feedback alle milestone
func _check_victory() -> void:
if not victory_shown and score >= Settings.target_score:
_on_victory()
func _on_victory() -> void:
if victory_shown:
return
victory_shown = true
# ferma la musica di sottofondo e riproduce l'audio di vittoria
bgm.stop()
_play_sound(sound_victory)
# congela il gioco e mostra l'overlay di vittoria (animazione inclusa)
get_tree().paused = true
victory_layer.show_victory(score)
func _reset_after_victory() -> void:
get_tree().paused = false
victory_shown = false
victory_layer.hide_victory()
bgm.play()
_new_game()
func _check_milestone() -> void:
if score < next_milestone:
return
+1
View File
@@ -53,6 +53,7 @@ func _build() -> void:
["min_time", "Tempo minimo (s)", 0.3, 3.0, 0.1],
["score_ramp", "Punteggio per velocità max", 100.0, 20000.0, 100.0],
["points_per_combo", "Punti per combo", 1.0, 50.0, 1.0],
["target_score", "Punteggio vittoria", 100.0, 50000.0, 100.0],
["max_combo", "Combo massimo", 1.0, 20.0, 1.0],
["combo_decay_threshold", "Soglia decay combo", 0.0, 30.0, 1.0],
["combo_decay_rate", "Rate decay combo", 0.0, 0.5, 0.01],
+4
View File
@@ -11,6 +11,7 @@ var start_time := 4.5 # tempo iniziale (s) tra un ordine e l'altro
var min_time := 0.8 # tempo minimo (velocità massima)
var score_ramp := 8000.0 # punteggio per difficoltà piena (velocità massima)
var points_per_combo := 1 # punti per ogni lancio corretto (lineare, default 1)
var target_score := 1000 # punteggio da raggiungere per la vittoria
var max_combo := 5.0 # combo massimo di default (configurabile)
var combo_decay_threshold := 0.0 # combo sopra cui inizia il decay (0 = da subito)
var combo_decay_rate := 0.15 # combo persi al secondo per unità sopra la soglia
@@ -38,6 +39,7 @@ func load_settings() -> void:
min_time = cfg.get_value("gameplay", "min_time", min_time)
score_ramp = cfg.get_value("gameplay", "score_ramp", score_ramp)
points_per_combo = cfg.get_value("gameplay", "points_per_combo", points_per_combo)
target_score = cfg.get_value("gameplay", "target_score", target_score)
max_combo = cfg.get_value("gameplay", "max_combo", max_combo)
combo_decay_threshold = cfg.get_value("gameplay", "combo_decay_threshold", combo_decay_threshold)
combo_decay_rate = cfg.get_value("gameplay", "combo_decay_rate", combo_decay_rate)
@@ -53,6 +55,7 @@ func save_settings() -> void:
cfg.set_value("gameplay", "min_time", min_time)
cfg.set_value("gameplay", "score_ramp", score_ramp)
cfg.set_value("gameplay", "points_per_combo", points_per_combo)
cfg.set_value("gameplay", "target_score", target_score)
cfg.set_value("gameplay", "max_combo", max_combo)
cfg.set_value("gameplay", "combo_decay_threshold", combo_decay_threshold)
cfg.set_value("gameplay", "combo_decay_rate", combo_decay_rate)
@@ -67,6 +70,7 @@ func reset_defaults() -> void:
min_time = 0.8
score_ramp = 8000.0
points_per_combo = 1
target_score = 1000
max_combo = 5.0
combo_decay_threshold = 0.0
combo_decay_rate = 0.15
+88
View File
@@ -0,0 +1,88 @@
extends CanvasLayer
## Overlay di vittoria: appare quando si raggiunge il punteggio target.
## Animazione di comparsa, mostra il punteggio e un pulsante per rigiocare.
signal reset_requested # emesso quando l'utente preme "Rigioca"
var title_label: Label
var score_label: Label
func _ready() -> void:
process_mode = Node.PROCESS_MODE_ALWAYS # funziona anche a gioco in pausa
visible = false
_build()
func _build() -> void:
var dim := ColorRect.new()
dim.color = Color(0.05, 0.02, 0.02, 0.72)
dim.set_anchors_preset(Control.PRESET_FULL_RECT)
dim.mouse_filter = Control.MOUSE_FILTER_STOP
add_child(dim)
var center := CenterContainer.new()
center.set_anchors_preset(Control.PRESET_FULL_RECT)
add_child(center)
var panel := PanelContainer.new()
center.add_child(panel)
var margin := MarginContainer.new()
margin.add_theme_constant_override("margin_left", 48)
margin.add_theme_constant_override("margin_right", 48)
margin.add_theme_constant_override("margin_top", 32)
margin.add_theme_constant_override("margin_bottom", 32)
panel.add_child(margin)
var vb := VBoxContainer.new()
vb.add_theme_constant_override("separation", 18)
vb.alignment = BoxContainer.ALIGNMENT_CENTER
margin.add_child(vb)
title_label = Label.new()
title_label.text = "VITTORIA!"
title_label.add_theme_font_size_override("font_size", 64)
title_label.add_theme_color_override("font_color", Color("#f6c445"))
title_label.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER
vb.add_child(title_label)
var subtitle := Label.new()
subtitle.text = "Hai raggiunto il punteggio target!"
subtitle.add_theme_font_size_override("font_size", 22)
subtitle.add_theme_color_override("font_color", Color(0.15, 0.1, 0.05))
subtitle.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER
vb.add_child(subtitle)
score_label = Label.new()
score_label.add_theme_font_size_override("font_size", 36)
score_label.add_theme_color_override("font_color", Color(0.1, 0.07, 0.04))
score_label.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER
vb.add_child(score_label)
var reset := Button.new()
reset.text = "🔄 Rigioca"
reset.custom_minimum_size = Vector2(220, 64)
reset.add_theme_font_size_override("font_size", 26)
reset.pressed.connect(func(): reset_requested.emit())
vb.add_child(reset)
func show_victory(score: int) -> void:
score_label.text = "Punteggio: %d" % score
visible = true
# animazione di comparsa chiara: il titolo "salta" e le label si fanno strada
title_label.pivot_offset = title_label.size * 0.5
title_label.scale = Vector2(0.4, 0.4)
title_label.modulate.a = 0.0
score_label.modulate.a = 0.0
var tw := create_tween()
tw.set_parallel(true)
tw.tween_property(title_label, "scale", Vector2(1.15, 1.15), 0.35) \
.set_trans(Tween.TRANS_BACK).set_ease(Tween.EASE_OUT)
tw.tween_property(title_label, "modulate:a", 1.0, 0.25)
tw.tween_property(score_label, "modulate:a", 1.0, 0.5).set_delay(0.25)
tw.chain().tween_property(title_label, "scale", Vector2(1.0, 1.0), 0.15)
func hide_victory() -> void:
visible = false