Refactor Godot architecture into AppShell, modular menus and game domains
This commit is contained in:
@@ -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
|
||||
@@ -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
|
||||
@@ -0,0 +1 @@
|
||||
uid://bvnmwlxoulgel
|
||||
@@ -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
|
||||
@@ -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")
|
||||
@@ -0,0 +1 @@
|
||||
uid://c2g01qq8bfkvn
|
||||
@@ -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
|
||||
@@ -0,0 +1 @@
|
||||
uid://8xdnu5320org
|
||||
@@ -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
|
||||
Reference in New Issue
Block a user