Refactor Godot architecture into AppShell, modular menus and game domains

This commit is contained in:
enne2
2026-09-06 17:07:05 +02:00
parent a73fb4b55f
commit f4a9da6bc3
209 changed files with 2726 additions and 2072 deletions
+8
View File
@@ -0,0 +1,8 @@
class_name MenuAudio
extends Node
## Feedback sonori brevi dei menu, separati dal gameplay.
func play_tone(freq: float) -> void:
var stream := AudioFactory.create_tone(freq, 0.10, 0.16)
AudioFactory.play_once(self, stream, -4.0)
@@ -0,0 +1 @@
uid://cjmmmat76bpb5
+96
View File
@@ -0,0 +1,96 @@
class_name MenuUI
extends RefCounted
## Helper visuali comuni alle schermate P1/P2a/P3a.
const DISPLAY_FONT := preload("res://shared/assets/fonts/NotoSerifDisplay-CondensedExtraBold.ttf")
const MENU_SHADER := preload("res://menus/assets/shaders/ambient_drift.gdshader")
static func set_normalized_rect(control: Control, center: Vector2, normalized_size: Vector2) -> void:
var viewport_size := control.get_viewport().get_visible_rect().size
var control_size := viewport_size * normalized_size
control.position = viewport_size * center - control_size * 0.5
control.size = control_size
static func add_full_texture(parent: Control, texture: Texture2D, material: Material = null) -> TextureRect:
var image := TextureRect.new()
image.set_anchors_and_offsets_preset(Control.PRESET_FULL_RECT)
image.texture = texture
image.expand_mode = TextureRect.EXPAND_IGNORE_SIZE
image.stretch_mode = TextureRect.STRETCH_KEEP_ASPECT_COVERED
image.mouse_filter = Control.MOUSE_FILTER_IGNORE
image.material = material
parent.add_child(image)
return image
static func ambient_material(strength: float, speed: float) -> ShaderMaterial:
var material := ShaderMaterial.new()
material.shader = MENU_SHADER
material.set_shader_parameter("drift_strength", strength)
material.set_shader_parameter("drift_speed", speed)
return material
static func add_image(parent: Control, texture: Texture2D, center: Vector2, normalized_size: Vector2, material: Material = null) -> TextureRect:
var image := TextureRect.new()
image.texture = texture
image.expand_mode = TextureRect.EXPAND_IGNORE_SIZE
image.stretch_mode = TextureRect.STRETCH_KEEP_ASPECT_CENTERED
image.mouse_filter = Control.MOUSE_FILTER_IGNORE
image.material = material
parent.add_child(image)
set_normalized_rect(image, center, normalized_size)
return image
static func add_image_button(parent: Control, texture: Texture2D, center: Vector2, normalized_size: Vector2) -> TextureButton:
var button := TextureButton.new()
button.ignore_texture_size = true
button.stretch_mode = TextureButton.STRETCH_KEEP_ASPECT_CENTERED
button.texture_normal = texture
button.texture_hover = texture
button.texture_pressed = texture
button.mouse_default_cursor_shape = Control.CURSOR_POINTING_HAND
button.mouse_entered.connect(func(): button.modulate = Color(1.08, 1.08, 1.08, 1.0))
button.mouse_exited.connect(func(): button.modulate = Color.WHITE)
parent.add_child(button)
set_normalized_rect(button, center, normalized_size)
return button
static func add_hotspot(parent: Control, center: Vector2, normalized_size: Vector2) -> Button:
var button := Button.new()
button.flat = true
button.mouse_default_cursor_shape = Control.CURSOR_POINTING_HAND
parent.add_child(button)
set_normalized_rect(button, center, normalized_size)
return button
static func add_label(parent: Control, text: String, center: Vector2, normalized_size: Vector2, font_size: int) -> Label:
var label := Label.new()
label.text = text
label.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER
label.vertical_alignment = VERTICAL_ALIGNMENT_CENTER
label.add_theme_font_override("font", DISPLAY_FONT)
label.add_theme_font_size_override("font_size", font_size)
label.add_theme_color_override("font_color", Color("#171413"))
label.add_theme_color_override("font_outline_color", Color("#f4ecd9"))
label.add_theme_constant_override("outline_size", 2)
label.mouse_filter = Control.MOUSE_FILTER_IGNORE
parent.add_child(label)
set_normalized_rect(label, center, normalized_size)
return label
static func note_style() -> StyleBoxFlat:
var style := StyleBoxFlat.new()
style.bg_color = Color("#f2dfb7")
style.border_color = Color("#9b6738")
style.set_border_width_all(3)
style.set_corner_radius_all(12)
style.shadow_color = Color(0, 0, 0, 0.25)
style.shadow_size = 5
return style
+1
View File
@@ -0,0 +1 @@
uid://bvnmwlxoulgel
+122
View File
@@ -0,0 +1,122 @@
class_name MainMenuController
extends CanvasLayer
## Coordina navigazione e transizioni tra P1, P2a e P3a.
signal mode_requested
signal exit_requested
const HOME_SCENE := preload("res://menus/scenes/p1_home.tscn")
const JUGGLE_SCENE := preload("res://menus/scenes/p2a_juggle_menu.tscn")
const GYM_SCENE := preload("res://menus/scenes/p3a_gym_menu.tscn")
var screen_root: Control
var fade: ColorRect
var menu_audio: MenuAudio
var current_screen: Control
var _transitioning := false
var _capture_enabled := false
func _ready() -> void:
process_mode = Node.PROCESS_MODE_ALWAYS
layer = 20
_capture_enabled = OS.get_cmdline_user_args().has("--capture-menus")
_build_shell()
visible = false
func _build_shell() -> void:
var root := Control.new()
root.set_anchors_and_offsets_preset(Control.PRESET_FULL_RECT)
root.mouse_filter = Control.MOUSE_FILTER_STOP
add_child(root)
screen_root = Control.new()
screen_root.set_anchors_and_offsets_preset(Control.PRESET_FULL_RECT)
root.add_child(screen_root)
fade = ColorRect.new()
fade.set_anchors_and_offsets_preset(Control.PRESET_FULL_RECT)
fade.color = Color("#1d1720")
fade.modulate.a = 0.0
fade.mouse_filter = Control.MOUSE_FILTER_STOP
root.add_child(fade)
menu_audio = MenuAudio.new()
add_child(menu_audio)
func show_home() -> void:
_show_direct(HOME_SCENE, "p1")
func show_juggle() -> void:
_show_direct(JUGGLE_SCENE, "p2a")
func show_gym() -> void:
_show_direct(GYM_SCENE, "p3a")
func hide_menu() -> void:
visible = false
get_tree().paused = false
func _show_direct(scene: PackedScene, capture_label: String) -> void:
visible = true
get_tree().paused = true
_replace_screen(scene)
_capture_after_frame(capture_label)
func _navigate(scene: PackedScene, capture_label: String, focal: Control = null) -> void:
if _transitioning:
return
_transitioning = true
menu_audio.play_tone(660.0)
var tween := create_tween()
tween.set_pause_mode(Tween.TWEEN_PAUSE_PROCESS)
if focal:
focal.pivot_offset = focal.size * 0.5
tween.tween_property(focal, "scale", Vector2(1.08, 1.08), 0.16).set_trans(Tween.TRANS_BACK).set_ease(Tween.EASE_OUT)
tween.parallel().tween_property(fade, "modulate:a", 0.92, 0.24)
tween.tween_callback(func(): _replace_screen(scene))
tween.tween_property(fade, "modulate:a", 0.0, 0.30)
tween.tween_callback(func():
_transitioning = false
_capture_after_frame(capture_label)
)
func _replace_screen(scene: PackedScene) -> void:
if is_instance_valid(current_screen):
current_screen.queue_free()
current_screen = scene.instantiate()
screen_root.add_child(current_screen)
_bind_screen(current_screen)
func _bind_screen(screen: Control) -> void:
if screen is P1HomeScreen:
screen.juggle_requested.connect(func(focal: Control): _navigate(JUGGLE_SCENE, "p2a", focal))
screen.gym_requested.connect(func(focal: Control): _navigate(GYM_SCENE, "p3a", focal))
screen.exit_requested.connect(func():
menu_audio.play_tone(220.0)
exit_requested.emit()
)
elif screen is P2AJuggleMenuScreen:
screen.back_requested.connect(func(): _navigate(HOME_SCENE, "p1"))
screen.mode_requested.connect(func(): mode_requested.emit())
screen.feedback_requested.connect(menu_audio.play_tone)
elif screen is P3AGymMenuScreen:
screen.back_requested.connect(func(): _navigate(HOME_SCENE, "p1"))
screen.feedback_requested.connect(menu_audio.play_tone)
func _capture_after_frame(label: String) -> void:
if not _capture_enabled:
return
await RenderingServer.frame_post_draw
var image := get_viewport().get_texture().get_image()
image.save_png("/tmp/autgame_menu_%s.png" % label)
@@ -0,0 +1 @@
uid://ds0gavril7pmk
+88
View File
@@ -0,0 +1,88 @@
class_name ModeSelectDialog
extends CanvasLayer
## Logica del dialog Target/Infinita. La costruzione visuale è in ModeSelectView.
signal mode_chosen
signal back_requested
var target_button: Button
var infinite_button: Button
var zen_button: CheckButton
var play_button: Button
var back_button: Button
var card: Control
var _capture_enabled := false
func _ready() -> void:
process_mode = Node.PROCESS_MODE_ALWAYS
layer = 30
_capture_enabled = OS.get_cmdline_user_args().has("--capture-menus")
visible = false
var view := ModeSelectView.build(self)
card = view.card
target_button = view.target
infinite_button = view.infinite
zen_button = view.zen
play_button = view.play
back_button = view.back
view.group.pressed.connect(_on_mode_changed)
play_button.pressed.connect(_on_play)
back_button.pressed.connect(_on_back)
func show_menu() -> void:
if Settings.game_mode == 2:
infinite_button.button_pressed = true
target_button.button_pressed = false
zen_button.button_pressed = false
else:
target_button.button_pressed = true
infinite_button.button_pressed = false
zen_button.button_pressed = Settings.game_mode == 1
_on_mode_changed()
visible = true
get_tree().paused = true
_animate_in()
if _capture_enabled:
_capture_after_animation()
func hide_menu() -> void:
visible = false
get_tree().paused = false
func _on_mode_changed(_button: BaseButton = null) -> void:
zen_button.visible = target_button.button_pressed
func _on_back() -> void:
hide_menu()
back_requested.emit()
func _on_play() -> void:
Settings.game_mode = 2 if infinite_button.button_pressed else 1 if zen_button.button_pressed else 0
Settings.save_settings()
hide_menu()
mode_chosen.emit()
func _animate_in() -> void:
card.modulate.a = 0.0
card.scale = Vector2(0.93, 0.93)
card.pivot_offset = card.size * 0.5
play_button.modulate.a = 0.0
var tween := create_tween()
tween.set_pause_mode(Tween.TWEEN_PAUSE_PROCESS)
tween.set_parallel(true)
tween.tween_property(card, "modulate:a", 1.0, 0.22)
tween.tween_property(card, "scale", Vector2.ONE, 0.38).set_trans(Tween.TRANS_BACK).set_ease(Tween.EASE_OUT)
tween.tween_property(play_button, "modulate:a", 1.0, 0.3).set_delay(0.24)
func _capture_after_animation() -> void:
await get_tree().create_timer(0.7, true).timeout
await RenderingServer.frame_post_draw
get_viewport().get_texture().get_image().save_png("/tmp/autgame_menu_mode_dialog.png")
+1
View File
@@ -0,0 +1 @@
uid://c2g01qq8bfkvn
+151
View File
@@ -0,0 +1,151 @@
class_name ModeSelectView
extends RefCounted
## Costruzione visuale del dialog modalità; la logica resta nel controller.
static func build(owner: CanvasLayer) -> Dictionary:
var dim := ColorRect.new()
dim.color = Color(0.045, 0.018, 0.012, 0.77)
dim.set_anchors_preset(Control.PRESET_FULL_RECT)
dim.mouse_filter = Control.MOUSE_FILTER_STOP
owner.add_child(dim)
var center := CenterContainer.new()
center.set_anchors_preset(Control.PRESET_FULL_RECT)
owner.add_child(center)
var card := PanelContainer.new()
card.custom_minimum_size = Vector2(750, 550)
var frame := CircusTheme.flat_style(Color("#70411f"), Color("#edca69"), 6, 31)
frame.shadow_color = Color(0, 0, 0, 0.58)
frame.shadow_size = 22
frame.shadow_offset = Vector2(0, 9)
for side in [SIDE_LEFT, SIDE_RIGHT, SIDE_TOP, SIDE_BOTTOM]:
frame.set_content_margin(side, 10.0)
card.add_theme_stylebox_override("panel", frame)
center.add_child(card)
var wood := PanelContainer.new()
wood.add_theme_stylebox_override("panel", CircusTheme.wood_style())
card.add_child(wood)
var studs := BrassStuds.new()
studs.set_anchors_and_offsets_preset(Control.PRESET_FULL_RECT)
wood.add_child(studs)
var margin := MarginContainer.new()
margin.add_theme_constant_override("margin_left", 50)
margin.add_theme_constant_override("margin_right", 50)
margin.add_theme_constant_override("margin_top", 28)
margin.add_theme_constant_override("margin_bottom", 32)
wood.add_child(margin)
var content := VBoxContainer.new()
content.add_theme_constant_override("separation", 12)
content.alignment = BoxContainer.ALIGNMENT_CENTER
margin.add_child(content)
_add_header(content)
var buttons := _add_mode_buttons(content)
var zen := _add_zen(content)
var play := _add_play(content)
var back := _add_back(content)
return {"card": card, "target": buttons.target, "infinite": buttons.infinite, "group": buttons.group, "zen": zen, "play": play, "back": back}
static func _add_header(parent: VBoxContainer) -> void:
var bunting := CircusTrim.new()
bunting.custom_minimum_size = Vector2(0, 34)
parent.add_child(bunting)
var title := Label.new()
title.text = "Scegli la modalità"
title.add_theme_font_override("font", CircusTheme.FONT)
title.add_theme_font_size_override("font_size", 45)
title.add_theme_color_override("font_color", Color("#6d3419"))
title.add_theme_color_override("font_outline_color", Color("#f5d985"))
title.add_theme_constant_override("outline_size", 3)
title.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER
parent.add_child(title)
var subtitle := Label.new()
subtitle.text = "Come vuoi giocare?"
subtitle.add_theme_font_size_override("font_size", 18)
subtitle.add_theme_color_override("font_color", CircusTheme.INK)
subtitle.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER
parent.add_child(subtitle)
static func _add_mode_buttons(parent: VBoxContainer) -> Dictionary:
var row := HBoxContainer.new()
row.add_theme_constant_override("separation", 18)
row.alignment = BoxContainer.ALIGNMENT_CENTER
parent.add_child(row)
var group := ButtonGroup.new()
var target := _mode_button("🎯 TARGET", "Raggiungi il punteggio stabilito")
var infinite := _mode_button("♾ INFINITA", "A vite, senza limite di punteggio")
target.button_group = group
infinite.button_group = group
row.add_child(target)
row.add_child(infinite)
return {"target": target, "infinite": infinite, "group": group}
static func _mode_button(title: String, hint: String) -> Button:
var button := Button.new()
button.text = title + "\n" + hint
button.custom_minimum_size = Vector2(305, 100)
button.add_theme_font_override("font", CircusTheme.FONT)
button.add_theme_font_size_override("font_size", 20)
button.add_theme_color_override("font_color", CircusTheme.INK)
button.add_theme_color_override("font_pressed_color", Color("#fff5d6"))
button.toggle_mode = true
button.mouse_default_cursor_shape = Control.CURSOR_POINTING_HAND
button.add_theme_stylebox_override("normal", CircusTheme.button_style(Color("#ead7ac"), Color("#9b6b30"), 3))
button.add_theme_stylebox_override("hover", CircusTheme.button_style(Color("#f3dfad"), CircusTheme.BRASS_LIGHT, 4))
var selected := CircusTheme.button_style(Color("#a85b2c"), Color("#f4cf67"), 5)
button.add_theme_stylebox_override("pressed", selected)
button.add_theme_stylebox_override("hover_pressed", selected)
button.add_theme_stylebox_override("focus", selected)
return button
static func _add_zen(parent: VBoxContainer) -> CheckButton:
var center := CenterContainer.new()
center.custom_minimum_size = Vector2(0, 52)
parent.add_child(center)
var zen := CheckButton.new()
zen.text = " ZEN · senza countdown né decadimento"
zen.custom_minimum_size = Vector2(420, 46)
zen.add_theme_font_size_override("font_size", 17)
zen.add_theme_color_override("font_color", CircusTheme.INK)
zen.add_theme_stylebox_override("normal", CircusTheme.flat_style(Color("#ead6a8"), Color("#a77835"), 2, 12))
zen.add_theme_stylebox_override("hover", CircusTheme.flat_style(Color("#f2dfb2"), CircusTheme.BRASS, 3, 12))
zen.alignment = HORIZONTAL_ALIGNMENT_CENTER
center.add_child(zen)
return zen
static func _add_play(parent: VBoxContainer) -> Button:
var center := CenterContainer.new()
parent.add_child(center)
var button := Button.new()
button.text = "◆ GIOCA ◆"
button.custom_minimum_size = Vector2(380, 66)
button.add_theme_font_override("font", CircusTheme.FONT)
button.add_theme_font_size_override("font_size", 29)
button.add_theme_color_override("font_color", Color("#fff1c7"))
button.add_theme_stylebox_override("normal", CircusTheme.button_style(Color("#824720"), Color("#d9a947"), 5))
button.add_theme_stylebox_override("hover", CircusTheme.button_style(Color("#98562a"), CircusTheme.BRASS_LIGHT, 5))
button.add_theme_stylebox_override("pressed", CircusTheme.button_style(Color("#653318"), Color("#b7832e"), 4))
center.add_child(button)
return button
static func _add_back(parent: VBoxContainer) -> Button:
var center := CenterContainer.new()
center.custom_minimum_size = Vector2(0, 42)
parent.add_child(center)
var button := Button.new()
button.text = "← Indietro"
button.custom_minimum_size = Vector2(220, 42)
button.add_theme_font_override("font", CircusTheme.FONT)
button.add_theme_font_size_override("font_size", 18)
button.add_theme_color_override("font_color", CircusTheme.INK)
button.add_theme_stylebox_override("normal", CircusTheme.flat_style(Color("#ead7ac"), Color("#9b6b30"), 2, 10))
button.add_theme_stylebox_override("hover", CircusTheme.flat_style(Color("#f3dfad"), CircusTheme.BRASS_LIGHT, 3, 10))
center.add_child(button)
return button
+1
View File
@@ -0,0 +1 @@
uid://8xdnu5320org
+61
View File
@@ -0,0 +1,61 @@
class_name P1HomeScreen
extends Control
## P1: home con i due tendoni/percorsi.
signal juggle_requested(focal: Control)
signal gym_requested(focal: Control)
signal exit_requested
const HOME_BG := preload("res://menus/assets/common/1_sfondo.png")
const CLOUDS := preload("res://menus/assets/common/1_nuvole.png")
const BLUE_TENT := preload("res://menus/assets/p1/1_tendone_1.png")
const RED_TENT := preload("res://menus/assets/p1/1_tendone_2.png")
const BACK_BUTTON := preload("res://menus/assets/common/pulsante_indietro.png")
const SETTINGS_BUTTON := preload("res://menus/assets/common/pulsante_rotellina.png")
const CIRCUS_SIGN := preload("res://menus/assets/p1/upendi_circus_sign.svg")
func _ready() -> void:
set_anchors_and_offsets_preset(Control.PRESET_FULL_RECT)
_build()
func _build() -> void:
MenuUI.add_full_texture(self, HOME_BG)
MenuUI.add_full_texture(self, CLOUDS, MenuUI.ambient_material(1.0, 0.16))
_add_sign()
var blue := MenuUI.add_image_button(self, BLUE_TENT, Vector2(0.29, 0.64), Vector2(0.36, 0.59))
blue.material = MenuUI.ambient_material(0.65, 0.20)
blue.pressed.connect(func(): juggle_requested.emit(blue))
var red := MenuUI.add_image_button(self, RED_TENT, Vector2(0.71, 0.64), Vector2(0.36, 0.59))
red.material = MenuUI.ambient_material(0.65, 0.20)
red.pressed.connect(func(): gym_requested.emit(red))
_add_star(Vector2(0.28, 0.18), "1", func(): juggle_requested.emit(blue))
_add_star(Vector2(0.72, 0.18), "2", func(): gym_requested.emit(red))
MenuUI.add_label(self, "Tocca un tendone e scopri il gioco!", Vector2(0.50, 0.91), Vector2(0.55, 0.07), 24)
MenuUI.add_image(self, SETTINGS_BUTTON, Vector2(0.91, 0.13), Vector2(0.09, 0.14))
if not OS.has_feature("web"):
var exit_button := MenuUI.add_image_button(self, BACK_BUTTON, Vector2(0.075, 0.13), Vector2(0.09, 0.14))
exit_button.pressed.connect(func(): exit_requested.emit())
func _add_sign() -> void:
MenuUI.add_image(self, CIRCUS_SIGN, Vector2(0.50, 0.14), Vector2(0.33, 0.22))
var brand := MenuUI.add_label(self, "Upendi", Vector2(0.50, 0.125), Vector2(0.25, 0.08), 42)
brand.add_theme_color_override("font_color", Color("#f8f0d3"))
brand.add_theme_color_override("font_outline_color", Color("#283f49"))
brand.add_theme_constant_override("outline_size", 3)
var circus := MenuUI.add_label(self, "C I R C U S", Vector2(0.50, 0.175), Vector2(0.20, 0.05), 17)
circus.add_theme_color_override("font_color", Color("#f8f0d3"))
circus.add_theme_color_override("font_outline_color", Color("#283f49"))
func _add_star(center: Vector2, number: String, action: Callable) -> void:
var label := MenuUI.add_label(self, "★\n" + number, center, Vector2(0.08, 0.13), 27)
label.add_theme_color_override("font_color", Color("#f6d45f"))
label.add_theme_color_override("font_outline_color", Color("#4e3423"))
label.add_theme_constant_override("outline_size", 5)
var hotspot := MenuUI.add_hotspot(self, center, Vector2(0.10, 0.16))
hotspot.pressed.connect(func(): action.call())
@@ -0,0 +1 @@
uid://d3iwd73jo7uw
@@ -0,0 +1,70 @@
class_name P2AJuggleMenuScreen
extends Control
## P2a: configurazione visuale del percorso JuggleBoard.
## Il numero palline è deliberatamente locale al menu e non modifica il gioco.
signal back_requested
signal mode_requested
signal feedback_requested(frequency: float)
const BG := preload("res://menus/assets/p2a/2a_sfondo.png")
const CLOUDS := preload("res://menus/assets/common/1_nuvole.png")
const TENT := preload("res://menus/assets/p2a/2a_tendone.png")
const TITLE := preload("res://menus/assets/p2a/2a_titolo.png")
const LEFT_ARROW := preload("res://menus/assets/common/freccina_sx.png")
const RIGHT_ARROW := preload("res://menus/assets/common/freccina_dx.png")
const BACK_BUTTON := preload("res://menus/assets/common/pulsante_indietro.png")
const SETTINGS_BUTTON := preload("res://menus/assets/common/pulsante_rotellina.png")
const PLAY_BUTTON := preload("res://menus/assets/common/pulsante_gioca.png")
var balls_count := 5
var count_label: Label
var left_button: TextureButton
var right_button: TextureButton
func _ready() -> void:
set_anchors_and_offsets_preset(Control.PRESET_FULL_RECT)
_build()
func _build() -> void:
MenuUI.add_full_texture(self, BG)
MenuUI.add_full_texture(self, CLOUDS, MenuUI.ambient_material(1.0, 0.16))
MenuUI.add_image(self, TENT, Vector2(0.28, 0.59), Vector2(0.56, 0.78), MenuUI.ambient_material(0.60, 0.20))
MenuUI.add_image(self, TITLE, Vector2(0.68, 0.22), Vector2(0.34, 0.28))
var back := MenuUI.add_image_button(self, BACK_BUTTON, Vector2(0.075, 0.13), Vector2(0.09, 0.14))
back.pressed.connect(func(): back_requested.emit())
MenuUI.add_image(self, SETTINGS_BUTTON, Vector2(0.91, 0.13), Vector2(0.09, 0.14))
_build_selector()
var play := MenuUI.add_image_button(self, PLAY_BUTTON, Vector2(0.79, 0.83), Vector2(0.30, 0.115))
play.pressed.connect(func():
feedback_requested.emit(784.0)
mode_requested.emit()
)
func _build_selector() -> void:
MenuUI.add_label(self, "N° palline", Vector2(0.68, 0.57), Vector2(0.23, 0.06), 22)
left_button = MenuUI.add_image_button(self, LEFT_ARROW, Vector2(0.68, 0.67), Vector2(0.055, 0.075))
left_button.pressed.connect(func(): _change_balls(-1))
right_button = MenuUI.add_image_button(self, RIGHT_ARROW, Vector2(0.87, 0.67), Vector2(0.055, 0.075))
right_button.pressed.connect(func(): _change_balls(1))
count_label = MenuUI.add_label(self, str(balls_count), Vector2(0.775, 0.67), Vector2(0.10, 0.09), 32)
_update_arrow_state()
func _change_balls(delta: int) -> void:
balls_count = clampi(balls_count + delta, 1, 5)
count_label.text = str(balls_count)
feedback_requested.emit(440.0 + 45.0 * balls_count)
_update_arrow_state()
func _update_arrow_state() -> void:
left_button.disabled = balls_count <= 1
right_button.disabled = balls_count >= 5
left_button.modulate.a = 0.35 if left_button.disabled else 1.0
right_button.modulate.a = 0.35 if right_button.disabled else 1.0
@@ -0,0 +1 @@
uid://1mk8nvjs8r1g
@@ -0,0 +1,49 @@
class_name P3AGymMenuScreen
extends Control
## P3a: anteprima della futura Palestra di Giocoleria.
signal back_requested
signal feedback_requested(frequency: float)
const BG := preload("res://menus/assets/common/1_sfondo.png")
const CLOUDS := preload("res://menus/assets/common/1_nuvole.png")
const TENT := preload("res://menus/assets/p3a/3a_tendone.png")
const TITLE := preload("res://menus/assets/p3a/3a_titolo.png")
const BACK_BUTTON := preload("res://menus/assets/common/pulsante_indietro.png")
const SETTINGS_BUTTON := preload("res://menus/assets/common/pulsante_rotellina.png")
const PLAY_BUTTON := preload("res://menus/assets/common/pulsante_gioca.png")
func _ready() -> void:
set_anchors_and_offsets_preset(Control.PRESET_FULL_RECT)
_build()
func _build() -> void:
MenuUI.add_full_texture(self, BG)
MenuUI.add_full_texture(self, CLOUDS, MenuUI.ambient_material(1.0, 0.16))
MenuUI.add_image(self, TENT, Vector2(0.28, 0.59), Vector2(0.56, 0.78), MenuUI.ambient_material(0.60, 0.20))
MenuUI.add_image(self, TITLE, Vector2(0.68, 0.22), Vector2(0.34, 0.34))
var back := MenuUI.add_image_button(self, BACK_BUTTON, Vector2(0.075, 0.13), Vector2(0.09, 0.14))
back.pressed.connect(func(): back_requested.emit())
MenuUI.add_image(self, SETTINGS_BUTTON, Vector2(0.91, 0.13), Vector2(0.09, 0.14))
MenuUI.add_label(self, "N° giocolieri", Vector2(0.77, 0.57), Vector2(0.28, 0.06), 22)
MenuUI.add_label(self, "3", Vector2(0.88, 0.57), Vector2(0.08, 0.08), 30)
var play := MenuUI.add_image_button(self, PLAY_BUTTON, Vector2(0.79, 0.83), Vector2(0.30, 0.115))
play.pressed.connect(_show_coming_soon)
func _show_coming_soon() -> void:
feedback_requested.emit(260.0)
var note := MenuUI.add_label(self, "Presto disponibile", Vector2(0.79, 0.72), Vector2(0.28, 0.09), 24)
note.add_theme_color_override("font_color", Color("#4c2917"))
note.add_theme_stylebox_override("normal", MenuUI.note_style())
note.modulate.a = 0.0
var tween := create_tween()
tween.set_pause_mode(Tween.TWEEN_PAUSE_PROCESS)
tween.tween_property(note, "modulate:a", 1.0, 0.16)
tween.tween_interval(1.6)
tween.tween_property(note, "modulate:a", 0.0, 0.28)
tween.tween_callback(note.queue_free)
@@ -0,0 +1 @@
uid://y6t4mu5wh602