From de7dfdf9f7313f611d54487d20af362b410c9922 Mon Sep 17 00:00:00 2001 From: enne2 Date: Wed, 19 Aug 2026 00:17:35 +0200 Subject: [PATCH] Mode select dialog at startup (Target/Zen/Infinite), Zen only for Target, refined circus-wood look (codex) --- scripts/main.gd | 11 ++ scripts/mode_select.gd | 281 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 292 insertions(+) create mode 100644 scripts/mode_select.gd diff --git a/scripts/main.gd b/scripts/main.gd index 4b28116..1c04144 100644 --- a/scripts/main.gd +++ b/scripts/main.gd @@ -67,6 +67,7 @@ var sound_defeat: AudioStreamWAV var sound_lose_heart: AudioStreamWAV var hearts: Node2D var victory_layer: CanvasLayer +var mode_select: CanvasLayer var victory_shown := false var ball_sounds := {} # color_name -> AudioStreamWAV (tono pentatonico) var bgm: AudioStreamPlayer @@ -88,7 +89,12 @@ func _ready() -> void: victory_layer = load("res://scripts/victory_overlay.gd").new() victory_layer.reset_requested.connect(_reset_after_victory) add_child(victory_layer) + mode_select = load("res://scripts/mode_select.gd").new() + mode_select.mode_chosen.connect(_start_with_mode) + add_child(mode_select) _new_game() + # primo avvio: mostra il dialog di scelta modalità + mode_select.show_menu() func _process(delta: float) -> void: @@ -373,6 +379,11 @@ func _new_game() -> void: _update_hud() +func _start_with_mode() -> void: + # chiamato dal dialog di scelta modalità: avvia la partita con la modalità scelta + _new_game() + + func _handle_click(pos: Vector2) -> void: var clicked: Area2D = null var best := HIT_RADIUS diff --git a/scripts/mode_select.gd b/scripts/mode_select.gd new file mode 100644 index 0000000..3d47dea --- /dev/null +++ b/scripts/mode_select.gd @@ -0,0 +1,281 @@ +extends CanvasLayer +## Dialog di scelta modalità mostrato all'apertura del gioco: +## - Target (a punteggio) con opzione Zen (senza tempo) +## - Infinita (a vite / endless, ignora il punteggio) + +signal mode_chosen # emesso quando si preme GIOCA + +const DISPLAY_FONT := preload("res://assets/fonts/NotoSerifDisplay-CondensedExtraBold.ttf") +const WOOD_TEXTURE := preload("res://assets/wood_board_rounded.png") +const INK := Color("#482615") +const BRASS := Color("#d5a83f") +const BRASS_LIGHT := Color("#f6d77a") + +var target_btn: Button +var infinite_btn: Button +var zen_cb: CheckButton +var play_btn: Button +var _card: Control +var _group := ButtonGroup.new() + + +class CircusTrim extends Control: + func _ready() -> void: + mouse_filter = Control.MOUSE_FILTER_IGNORE + queue_redraw() + + func _draw() -> void: + var colors := [Color("#a9403e"), Color("#d5a83f"), Color("#397b76")] + var count := 11 + var span := 440.0 + var start_x := (size.x - span) * 0.5 + draw_line(Vector2(start_x - 10, 8), Vector2(start_x + span + 10, 8), Color("#63361f"), 3.0, true) + for i in count: + var x := start_x + i * (span / float(count - 1)) - 16.0 + var points := PackedVector2Array([Vector2(x, 7), Vector2(x + 32, 7), Vector2(x + 16, 31)]) + draw_colored_polygon(points, colors[i % colors.size()]) + draw_polyline(PackedVector2Array([points[0], points[1], points[2], points[0]]), Color("#6b341c"), 1.5, true) + + +class BrassStuds extends Control: + func _ready() -> void: + mouse_filter = Control.MOUSE_FILTER_IGNORE + queue_redraw() + + func _draw() -> void: + for p in [Vector2(22, 22), Vector2(size.x - 22, 22), Vector2(22, size.y - 22), Vector2(size.x - 22, size.y - 22)]: + draw_circle(p + Vector2(1.5, 2.5), 8.0, Color(0.12, 0.05, 0.02, 0.45)) + draw_circle(p, 8.0, Color("#b17b24")) + draw_circle(p - Vector2(2, 2), 4.5, BRASS_LIGHT) + draw_line(p - Vector2(3, 0), p + Vector2(3, 0), Color("#765018"), 1.5, true) + + +func _ready() -> void: + process_mode = Node.PROCESS_MODE_ALWAYS + visible = false + _build() + + +func _flat_style(color: Color, border: Color, width: int, radius: int) -> StyleBoxFlat: + var style := StyleBoxFlat.new() + style.bg_color = color + style.border_color = border + style.set_border_width_all(width) + style.set_corner_radius_all(radius) + return style + + +func _wood_style() -> StyleBoxTexture: + var style := StyleBoxTexture.new() + style.texture = WOOD_TEXTURE + style.texture_margin_left = 35.0 + style.texture_margin_right = 35.0 + style.texture_margin_top = 35.0 + style.texture_margin_bottom = 35.0 + return style + + +func _build() -> void: + 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 + add_child(dim) + + var center := CenterContainer.new() + center.set_anchors_preset(Control.PRESET_FULL_RECT) + add_child(center) + + _card = PanelContainer.new() + _card.custom_minimum_size = Vector2(750, 550) + var frame := _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) + frame.content_margin_left = 10 + frame.content_margin_right = 10 + frame.content_margin_top = 10 + frame.content_margin_bottom = 10 + _card.add_theme_stylebox_override("panel", frame) + center.add_child(_card) + + var wood := PanelContainer.new() + wood.add_theme_stylebox_override("panel", _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 vb := VBoxContainer.new() + vb.add_theme_constant_override("separation", 12) + vb.alignment = BoxContainer.ALIGNMENT_CENTER + margin.add_child(vb) + + var bunting := CircusTrim.new() + bunting.custom_minimum_size = Vector2(0, 34) + vb.add_child(bunting) + + var title := Label.new() + title.text = "Scegli la modalità" + title.add_theme_font_override("font", DISPLAY_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.add_theme_color_override("font_shadow_color", Color(0.25, 0.09, 0.02, 0.3)) + title.add_theme_constant_override("shadow_offset_x", 0) + title.add_theme_constant_override("shadow_offset_y", 3) + title.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER + vb.add_child(title) + + var sub := Label.new() + sub.text = "Come vuoi giocare?" + sub.add_theme_font_size_override("font_size", 18) + sub.add_theme_color_override("font_color", INK) + sub.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER + vb.add_child(sub) + + # Pulsanti modalità (comportamento radio). + var hb := HBoxContainer.new() + hb.add_theme_constant_override("separation", 18) + hb.alignment = BoxContainer.ALIGNMENT_CENTER + vb.add_child(hb) + + target_btn = _make_mode_button("🎯 TARGET", "Raggiungi il punteggio stabilito") + infinite_btn = _make_mode_button("♾ INFINITA", "A vite, senza limite di punteggio") + target_btn.button_group = _group + infinite_btn.button_group = _group + _group.pressed.connect(_on_mode_changed) + hb.add_child(target_btn) + hb.add_child(infinite_btn) + + # Checkbox Zen (solo per la modalità Target). + var zen_center := CenterContainer.new() + zen_center.custom_minimum_size = Vector2(0, 52) + vb.add_child(zen_center) + zen_cb = CheckButton.new() + zen_cb.text = " ZEN · senza countdown né decadimento" + zen_cb.custom_minimum_size = Vector2(420, 46) + zen_cb.add_theme_font_size_override("font_size", 17) + zen_cb.add_theme_color_override("font_color", INK) + zen_cb.add_theme_color_override("font_hover_color", Color("#67391d")) + zen_cb.add_theme_stylebox_override("normal", _flat_style(Color("#ead6a8"), Color("#a77835"), 2, 12)) + zen_cb.add_theme_stylebox_override("hover", _flat_style(Color("#f2dfb2"), BRASS, 3, 12)) + zen_cb.add_theme_stylebox_override("pressed", _flat_style(Color("#dfc58c"), Color("#966324"), 3, 12)) + zen_cb.button_pressed = false + zen_cb.alignment = HORIZONTAL_ALIGNMENT_CENTER + zen_cb.mouse_default_cursor_shape = Control.CURSOR_POINTING_HAND + zen_center.add_child(zen_cb) + + var play_center := CenterContainer.new() + vb.add_child(play_center) + play_btn = Button.new() + play_btn.text = "◆ GIOCA ◆" + play_btn.custom_minimum_size = Vector2(380, 66) + play_btn.add_theme_font_override("font", DISPLAY_FONT) + play_btn.add_theme_font_size_override("font_size", 29) + play_btn.add_theme_color_override("font_color", Color("#fff1c7")) + play_btn.add_theme_color_override("font_hover_color", Color.WHITE) + play_btn.add_theme_color_override("font_pressed_color", Color("#f8dc91")) + play_btn.add_theme_stylebox_override("normal", _button_style(Color("#824720"), Color("#d9a947"), 5)) + play_btn.add_theme_stylebox_override("hover", _button_style(Color("#98562a"), BRASS_LIGHT, 5)) + play_btn.add_theme_stylebox_override("pressed", _button_style(Color("#653318"), Color("#b7832e"), 4)) + play_btn.mouse_default_cursor_shape = Control.CURSOR_POINTING_HAND + play_btn.pressed.connect(_on_play) + play_center.add_child(play_btn) + + +func _button_style(color: Color, border: Color, width: int) -> StyleBoxFlat: + var style := _flat_style(color, border, width, 14) + style.shadow_color = Color(0.14, 0.05, 0.02, 0.5) + style.shadow_size = 6 + style.shadow_offset = Vector2(0, 4) + return style + + +func _make_mode_button(title_text: String, hint_text: String) -> Button: + var btn := Button.new() + btn.text = title_text + "\n" + hint_text + btn.custom_minimum_size = Vector2(305, 100) + btn.add_theme_font_override("font", DISPLAY_FONT) + btn.add_theme_font_size_override("font_size", 20) + btn.add_theme_color_override("font_color", INK) + btn.add_theme_color_override("font_hover_color", Color("#572b16")) + btn.add_theme_color_override("font_pressed_color", Color("#fff5d6")) + btn.add_theme_color_override("font_focus_color", Color("#fff5d6")) + btn.add_theme_constant_override("outline_size", 1) + btn.toggle_mode = true + btn.mouse_default_cursor_shape = Control.CURSOR_POINTING_HAND + var normal := _flat_style(Color("#ead7ac"), Color("#9b6b30"), 3, 14) + normal.shadow_color = Color(0.18, 0.07, 0.02, 0.35) + normal.shadow_size = 5 + normal.shadow_offset = Vector2(0, 3) + var hover := _flat_style(Color("#f3dfad"), BRASS_LIGHT, 4, 14) + hover.shadow_color = Color(0.72, 0.43, 0.08, 0.3) + hover.shadow_size = 8 + var selected := _flat_style(Color("#a85b2c"), Color("#f4cf67"), 5, 14) + selected.shadow_color = Color(0.95, 0.65, 0.18, 0.42) + selected.shadow_size = 10 + selected.shadow_offset = Vector2(0, 2) + btn.add_theme_stylebox_override("normal", normal) + btn.add_theme_stylebox_override("hover", hover) + btn.add_theme_stylebox_override("pressed", selected) + btn.add_theme_stylebox_override("hover_pressed", selected) + btn.add_theme_stylebox_override("focus", selected) + return btn + + +func show_menu() -> void: + # preseleziona dall'ultima scelta salvata + var mode: int = Settings.game_mode + if mode == 2: + infinite_btn.button_pressed = true + target_btn.button_pressed = false + else: + target_btn.button_pressed = true + infinite_btn.button_pressed = false + zen_cb.button_pressed = (mode == 1) + _on_mode_changed() + visible = true + get_tree().paused = true + _card.modulate.a = 0.0 + _card.scale = Vector2(0.93, 0.93) + _card.pivot_offset = _card.size * 0.5 + play_btn.modulate.a = 0.0 + var tw := create_tween() + tw.set_pause_mode(Tween.TWEEN_PAUSE_PROCESS) + tw.set_parallel(true) + tw.tween_property(_card, "modulate:a", 1.0, 0.22) + tw.tween_property(_card, "scale", Vector2.ONE, 0.38).set_trans(Tween.TRANS_BACK).set_ease(Tween.EASE_OUT) + tw.tween_property(play_btn, "modulate:a", 1.0, 0.3).set_delay(0.24) + + +func _on_mode_changed(_btn: BaseButton = null) -> void: + # lo Zen è disponibile SOLO per la modalità Target + zen_cb.visible = target_btn.button_pressed + + +func hide_menu() -> void: + visible = false + get_tree().paused = false + + +func _on_play() -> void: + # applica la scelta e salva (per ricordarla al prossimo avvio) + if infinite_btn.button_pressed: + Settings.game_mode = 2 + elif zen_cb.button_pressed: + Settings.game_mode = 1 + else: + Settings.game_mode = 0 + Settings.save_settings() + hide_menu() + mode_chosen.emit()