388 lines
15 KiB
GDScript
388 lines
15 KiB
GDScript
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
|
|
signal sign_drop_started
|
|
signal sign_landed
|
|
signal sign_bounced
|
|
|
|
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_PANEL := preload("res://menus/assets/p1/upendi_circus_sign_panel.png")
|
|
const CIRCUS_SIGN_ROPES := preload("res://menus/assets/p1/upendi_circus_sign_ropes.png")
|
|
const PLAY_BUTTON := preload("res://menus/assets/common/pulsante_gioca.png")
|
|
|
|
var piedone: Control
|
|
var piedone_title: Label
|
|
var piedone_desc: Label
|
|
var piedone_play: TextureButton
|
|
var selected_tent: String = "" # "juggle" o "gym"
|
|
var piedone_tween: Tween
|
|
var blue_tent_control: Control
|
|
var red_tent_control: Control
|
|
|
|
# Riferimenti alle animazioni e shader per ciascun tendone
|
|
var blue_mat: ShaderMaterial
|
|
var red_mat: ShaderMaterial
|
|
|
|
var blue_curtain_dir: Array = [0]
|
|
var blue_curtain_timer: Timer
|
|
var blue_lights: TextureRect
|
|
|
|
var red_curtain_dir: Array = [0]
|
|
var red_curtain_timer: Timer
|
|
var red_lights: TextureRect
|
|
|
|
const CURTAIN_FRAMES := [
|
|
preload("res://menus/assets/p1/curtain_anim/curtain_00.png"),
|
|
preload("res://menus/assets/p1/curtain_anim/curtain_01.png"),
|
|
preload("res://menus/assets/p1/curtain_anim/curtain_02.png"),
|
|
preload("res://menus/assets/p1/curtain_anim/curtain_03.png"),
|
|
preload("res://menus/assets/p1/curtain_anim/curtain_04.png"),
|
|
preload("res://menus/assets/p1/curtain_anim/curtain_05.png"),
|
|
preload("res://menus/assets/p1/curtain_anim/curtain_06.png"),
|
|
]
|
|
|
|
const GARLAND_FRAMES := [
|
|
preload("res://menus/assets/p1/garland_anim/lights_00.png"),
|
|
preload("res://menus/assets/p1/garland_anim/lights_01.png"),
|
|
preload("res://menus/assets/p1/garland_anim/lights_02.png"),
|
|
preload("res://menus/assets/p1/garland_anim/lights_03.png"),
|
|
preload("res://menus/assets/p1/garland_anim/lights_04.png"),
|
|
preload("res://menus/assets/p1/garland_anim/lights_05.png"),
|
|
]
|
|
|
|
|
|
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()
|
|
|
|
# Costruzione dei due tendoni selezionabili al click (entrambi con luci e sipario)
|
|
var blue := _add_tent_button(BLUE_TENT, Vector2(0.29, 0.46), "juggle")
|
|
blue_tent_control = blue
|
|
blue.pressed.connect(func(): _select_tent("juggle"))
|
|
|
|
var red := _add_tent_button(RED_TENT, Vector2(0.71, 0.46), "gym")
|
|
red_tent_control = red
|
|
red.pressed.connect(func(): _select_tent("gym"))
|
|
|
|
_add_bottom_bar()
|
|
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, MenuUI.BACK_BUTTON_CENTER, MenuUI.BACK_BUTTON_SIZE)
|
|
exit_button.pressed.connect(func(): exit_requested.emit())
|
|
|
|
|
|
func _add_tent_button(texture: Texture2D, center: Vector2, tent_id: String) -> TextureButton:
|
|
var button := MenuUI.add_image_button(self, texture, center, Vector2(0.36, 0.59))
|
|
var mat := MenuUI.tent_material(0.0)
|
|
button.material = mat
|
|
button.tooltip_text = "Seleziona questo percorso"
|
|
|
|
if tent_id == "juggle":
|
|
blue_mat = mat
|
|
_setup_tent_interactive(button, center, "blue")
|
|
elif tent_id == "gym":
|
|
red_mat = mat
|
|
_setup_tent_interactive(button, center, "red")
|
|
|
|
return button
|
|
|
|
|
|
func _setup_tent_interactive(parent_button: TextureButton, center: Vector2, color_id: String) -> void:
|
|
# Sipario: parte chiuso (frame 0). Alla selezione si apre (0 -> 6).
|
|
# Alla deselezione si richiude al contrario (6 -> 0).
|
|
var curtain := TextureRect.new()
|
|
curtain.name = color_id.capitalize() + "Curtain"
|
|
curtain.expand_mode = TextureRect.EXPAND_IGNORE_SIZE
|
|
curtain.stretch_mode = TextureRect.STRETCH_KEEP_ASPECT_CENTERED
|
|
curtain.mouse_filter = Control.MOUSE_FILTER_IGNORE
|
|
curtain.texture = CURTAIN_FRAMES[0]
|
|
add_child(curtain)
|
|
MenuUI.set_normalized_rect(curtain, center, Vector2(0.36, 0.59))
|
|
|
|
var curtain_dir := [0] # 0 = fermo, 1 = apre, -1 = chiude
|
|
var curtain_timer := Timer.new()
|
|
curtain_timer.name = color_id.capitalize() + "CurtainTimer"
|
|
curtain_timer.wait_time = 1.0 / 12.5
|
|
curtain_timer.process_mode = Node.PROCESS_MODE_ALWAYS
|
|
var curtain_idx := [0]
|
|
curtain_timer.timeout.connect(func():
|
|
if curtain_dir[0] == 1:
|
|
if curtain_idx[0] < CURTAIN_FRAMES.size() - 1:
|
|
curtain_idx[0] += 1
|
|
curtain.texture = CURTAIN_FRAMES[curtain_idx[0]]
|
|
curtain.visible = true
|
|
else:
|
|
# Completamente aperto
|
|
curtain.texture = CURTAIN_FRAMES[curtain_idx[0]]
|
|
curtain_dir[0] = 0
|
|
curtain_timer.stop()
|
|
elif curtain_dir[0] == -1:
|
|
if curtain_idx[0] > 0:
|
|
curtain_idx[0] -= 1
|
|
curtain.texture = CURTAIN_FRAMES[curtain_idx[0]]
|
|
curtain.visible = true
|
|
else:
|
|
# Completamente chiuso
|
|
curtain.texture = CURTAIN_FRAMES[0]
|
|
curtain_dir[0] = 0
|
|
curtain_timer.stop()
|
|
)
|
|
add_child(curtain_timer)
|
|
|
|
# Lucine a inseguimento
|
|
var lights := TextureRect.new()
|
|
lights.name = color_id.capitalize() + "Garland"
|
|
lights.expand_mode = TextureRect.EXPAND_IGNORE_SIZE
|
|
lights.stretch_mode = TextureRect.STRETCH_KEEP_ASPECT_CENTERED
|
|
lights.mouse_filter = Control.MOUSE_FILTER_IGNORE
|
|
lights.texture = GARLAND_FRAMES[0]
|
|
lights.visible = false
|
|
add_child(lights)
|
|
MenuUI.set_normalized_rect(lights, center, Vector2(0.36, 0.59))
|
|
|
|
var lights_idx := [0]
|
|
var lights_timer := Timer.new()
|
|
lights_timer.name = color_id.capitalize() + "GarlandTimer"
|
|
lights_timer.wait_time = 1.0 / 12.5
|
|
lights_timer.process_mode = Node.PROCESS_MODE_ALWAYS
|
|
lights_timer.timeout.connect(func():
|
|
lights_idx[0] = (lights_idx[0] + 1) % GARLAND_FRAMES.size()
|
|
if lights.visible:
|
|
lights.texture = GARLAND_FRAMES[lights_idx[0]]
|
|
)
|
|
add_child(lights_timer)
|
|
lights_timer.start()
|
|
|
|
if color_id == "blue":
|
|
blue_curtain_dir = curtain_dir
|
|
blue_curtain_timer = curtain_timer
|
|
blue_lights = lights
|
|
elif color_id == "red":
|
|
red_curtain_dir = curtain_dir
|
|
red_curtain_timer = curtain_timer
|
|
red_lights = lights
|
|
|
|
|
|
func _select_tent(which: String) -> void:
|
|
if selected_tent == which:
|
|
return
|
|
selected_tent = which
|
|
|
|
# Aggiorna animazioni sui tendoni
|
|
if which == "juggle":
|
|
# Attiva tendone blu
|
|
if blue_mat:
|
|
blue_mat.set_shader_parameter("hover_strength", 1.0)
|
|
blue_mat.set_shader_parameter("garland_strength", 1.0)
|
|
if blue_curtain_dir and blue_curtain_timer:
|
|
blue_curtain_dir[0] = 1
|
|
blue_curtain_timer.start()
|
|
if blue_lights:
|
|
blue_lights.visible = true
|
|
|
|
# Spegne e richiude tendone rosso
|
|
if red_mat:
|
|
red_mat.set_shader_parameter("hover_strength", 0.0)
|
|
red_mat.set_shader_parameter("garland_strength", 0.0)
|
|
if red_curtain_dir and red_curtain_timer:
|
|
red_curtain_dir[0] = -1
|
|
red_curtain_timer.start()
|
|
if red_lights:
|
|
red_lights.visible = false
|
|
|
|
elif which == "gym":
|
|
# Attiva tendone rosso
|
|
if red_mat:
|
|
red_mat.set_shader_parameter("hover_strength", 1.0)
|
|
red_mat.set_shader_parameter("garland_strength", 1.0)
|
|
if red_curtain_dir and red_curtain_timer:
|
|
red_curtain_dir[0] = 1
|
|
red_curtain_timer.start()
|
|
if red_lights:
|
|
red_lights.visible = true
|
|
|
|
# Spegne e richiude tendone blu
|
|
if blue_mat:
|
|
blue_mat.set_shader_parameter("hover_strength", 0.0)
|
|
blue_mat.set_shader_parameter("garland_strength", 0.0)
|
|
if blue_curtain_dir and blue_curtain_timer:
|
|
blue_curtain_dir[0] = -1
|
|
blue_curtain_timer.start()
|
|
if blue_lights:
|
|
blue_lights.visible = false
|
|
|
|
# Transizione del piedone: scende e risale con le nuove info
|
|
_transition_bottom_bar(which)
|
|
|
|
|
|
func _transition_bottom_bar(which: String) -> void:
|
|
if not is_instance_valid(piedone):
|
|
return
|
|
|
|
var v_height := get_viewport_rect().size.y
|
|
var hidden_y := v_height * 0.29
|
|
var target_y := 0.0
|
|
|
|
if is_instance_valid(piedone_tween) and piedone_tween.is_running():
|
|
piedone_tween.kill()
|
|
|
|
piedone_tween = create_tween()
|
|
piedone_tween.set_pause_mode(Tween.TWEEN_PAUSE_PROCESS)
|
|
|
|
# Quando l'utente tocca un tendone, Piedone scende e si rialza aggiornato
|
|
piedone_tween.tween_property(piedone, "position:y", hidden_y, 0.25).set_trans(Tween.TRANS_QUAD).set_ease(Tween.EASE_IN)
|
|
piedone_tween.tween_callback(func(): _update_piedone_content(which))
|
|
piedone_tween.tween_property(piedone, "position:y", v_height * 0.025, 0.45).set_trans(Tween.TRANS_QUAD).set_ease(Tween.EASE_OUT)
|
|
piedone_tween.tween_property(piedone, "position:y", target_y, 0.25).set_trans(Tween.TRANS_BOUNCE).set_ease(Tween.EASE_OUT)
|
|
|
|
|
|
func _update_piedone_content(which: String) -> void:
|
|
piedone_play.visible = true
|
|
# Riposiziona il titolo sopra alla descrizione
|
|
MenuUI.set_normalized_rect(piedone_title, Vector2(0.38, 0.845), Vector2(0.52, 0.065))
|
|
piedone_title.horizontal_alignment = HORIZONTAL_ALIGNMENT_LEFT
|
|
|
|
if which == "juggle":
|
|
piedone_title.text = "JUGGLEBOARD"
|
|
piedone_desc.text = "Allena ritmo e coordinazione con le biglie e i lanci a bersaglio."
|
|
elif which == "gym":
|
|
piedone_title.text = "PALESTRA DI GIOCOLERIA"
|
|
piedone_desc.text = "Scopri gli attrezzi del circo: clave, palline, piatti e rola bola."
|
|
|
|
piedone_desc.visible = true
|
|
|
|
|
|
func _on_play_pressed() -> void:
|
|
if selected_tent == "juggle":
|
|
juggle_requested.emit(blue_tent_control)
|
|
elif selected_tent == "gym":
|
|
gym_requested.emit(red_tent_control)
|
|
|
|
|
|
func _add_bottom_bar() -> void:
|
|
# Piedone parte nascosto e sale all'avvio con testo centrale "Tocca uno dei tendoni"
|
|
piedone = Control.new()
|
|
piedone.name = "Piedone"
|
|
piedone.mouse_filter = Control.MOUSE_FILTER_IGNORE
|
|
piedone.set_anchors_and_offsets_preset(Control.PRESET_FULL_RECT)
|
|
add_child(piedone)
|
|
|
|
var bar := ColorRect.new()
|
|
bar.color = Color("#f1dfbd")
|
|
bar.mouse_filter = Control.MOUSE_FILTER_IGNORE
|
|
piedone.add_child(bar)
|
|
MenuUI.set_normalized_rect(bar, Vector2(0.50, 0.885), Vector2(1.0, 0.23))
|
|
|
|
for item in [[0.774, 3.0], [0.787, 2.0]]:
|
|
var rule := ColorRect.new()
|
|
rule.color = Color("#28201c")
|
|
rule.mouse_filter = Control.MOUSE_FILTER_IGNORE
|
|
piedone.add_child(rule)
|
|
MenuUI.set_normalized_rect(rule, Vector2(0.50, item[0]), Vector2(1.0, item[1] / 720.0))
|
|
|
|
# Titolo / invito iniziale: allineato armoniosamente nella colonna sinistra
|
|
piedone_title = MenuUI.add_label(piedone, "Tocca uno dei tendoni", Vector2(0.38, 0.885), Vector2(0.52, 0.09), 26)
|
|
piedone_title.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER
|
|
|
|
# Descrizione del gioco: posizionata direttamente sotto al titolo
|
|
piedone_desc = Label.new()
|
|
piedone_desc.text = ""
|
|
piedone_desc.horizontal_alignment = HORIZONTAL_ALIGNMENT_LEFT
|
|
piedone_desc.vertical_alignment = VERTICAL_ALIGNMENT_TOP
|
|
piedone_desc.autowrap_mode = TextServer.AUTOWRAP_WORD_SMART
|
|
piedone_desc.add_theme_font_override("font", MenuUI.DISPLAY_FONT)
|
|
piedone_desc.add_theme_font_size_override("font_size", 17)
|
|
piedone_desc.add_theme_color_override("font_color", Color("#423832"))
|
|
piedone_desc.mouse_filter = Control.MOUSE_FILTER_IGNORE
|
|
piedone_desc.visible = false
|
|
piedone.add_child(piedone_desc)
|
|
MenuUI.set_normalized_rect(piedone_desc, Vector2(0.38, 0.925), Vector2(0.52, 0.08))
|
|
|
|
# Pulsante GIOCA sul lato destro del piedone (coerente con i mockup P1 e P2a)
|
|
piedone_play = TextureButton.new()
|
|
piedone_play.ignore_texture_size = true
|
|
piedone_play.stretch_mode = TextureButton.STRETCH_KEEP_ASPECT_CENTERED
|
|
piedone_play.texture_normal = PLAY_BUTTON
|
|
piedone_play.texture_hover = PLAY_BUTTON
|
|
piedone_play.texture_pressed = PLAY_BUTTON
|
|
piedone_play.mouse_default_cursor_shape = Control.CURSOR_POINTING_HAND
|
|
piedone_play.mouse_entered.connect(func(): piedone_play.modulate = Color(1.08, 1.08, 1.08, 1.0))
|
|
piedone_play.mouse_exited.connect(func(): piedone_play.modulate = Color.WHITE)
|
|
piedone_play.pressed.connect(_on_play_pressed)
|
|
piedone_play.visible = false
|
|
piedone.add_child(piedone_play)
|
|
MenuUI.set_normalized_rect(piedone_play, Vector2(0.79, 0.885), Vector2(0.26, 0.14))
|
|
|
|
# Parte nascosto sotto lo schermo e si anima verso l'alto con rimbalzo elastico
|
|
var viewport_height := get_viewport_rect().size.y
|
|
piedone.position.y = viewport_height * 0.29
|
|
var intro_tween := create_tween()
|
|
intro_tween.set_pause_mode(Tween.TWEEN_PAUSE_PROCESS)
|
|
intro_tween.tween_property(piedone, "position:y", viewport_height * 0.025, 0.85).set_trans(Tween.TRANS_QUAD).set_ease(Tween.EASE_OUT)
|
|
intro_tween.tween_property(piedone, "position:y", 0.0, 0.42).set_trans(Tween.TRANS_BOUNCE).set_ease(Tween.EASE_OUT)
|
|
|
|
|
|
func _add_sign() -> void:
|
|
# Il pannello si muove, mentre le funi restano ancorate al bordo superiore.
|
|
# Separare i due livelli conserva la texture disegnata originale senza lasciare
|
|
# un vuoto quando il pannello supera verso il basso il punto di riposo.
|
|
var rig := Control.new()
|
|
rig.mouse_filter = Control.MOUSE_FILTER_IGNORE
|
|
add_child(rig)
|
|
MenuUI.set_normalized_rect(rig, Vector2(0.50, 0.17), Vector2(0.43, 0.34))
|
|
|
|
_add_sign_layer(rig, CIRCUS_SIGN_ROPES)
|
|
var panel := _add_sign_layer(rig, CIRCUS_SIGN_PANEL)
|
|
var sign_mat := MenuUI.sign_lights_material()
|
|
panel.material = sign_mat
|
|
var final_y := panel.position.y
|
|
panel.position.y = final_y - panel.size.y * 1.15
|
|
|
|
var tween := create_tween()
|
|
# Il menu viene messo in pausa dalla shell: l'ingresso deve continuare comunque.
|
|
tween.set_pause_mode(Tween.TWEEN_PAUSE_PROCESS)
|
|
# Discesa rapida, atterraggio evidente e rimbalzo elastico.
|
|
call_deferred("_emit_sign_drop_started")
|
|
tween.tween_property(panel, "position:y", final_y + panel.size.y * 0.11, 1.10).set_trans(Tween.TRANS_QUAD).set_ease(Tween.EASE_IN)
|
|
# L'impatto è sincronizzato con la massima estensione, prima del ritorno elastico.
|
|
tween.tween_callback(func(): sign_landed.emit())
|
|
tween.tween_property(panel, "position:y", final_y, 0.50).set_trans(Tween.TRANS_BOUNCE).set_ease(Tween.EASE_OUT)
|
|
tween.tween_callback(func():
|
|
sign_bounced.emit()
|
|
# Attiva le lucine colorate dell'insegna con una dolce dissolvenza solo a fine animazione
|
|
var lights_tween := create_tween()
|
|
lights_tween.set_pause_mode(Tween.TWEEN_PAUSE_PROCESS)
|
|
lights_tween.tween_method(func(val: float): sign_mat.set_shader_parameter("sign_lights_active", val), 0.0, 1.0, 0.6).set_trans(Tween.TRANS_SINE).set_ease(Tween.EASE_OUT)
|
|
)
|
|
|
|
|
|
func _add_sign_layer(parent: Control, texture: Texture2D) -> TextureRect:
|
|
var layer := TextureRect.new()
|
|
layer.texture = texture
|
|
layer.expand_mode = TextureRect.EXPAND_IGNORE_SIZE
|
|
layer.stretch_mode = TextureRect.STRETCH_KEEP_ASPECT_CENTERED
|
|
layer.mouse_filter = Control.MOUSE_FILTER_IGNORE
|
|
parent.add_child(layer)
|
|
layer.set_anchors_and_offsets_preset(Control.PRESET_FULL_RECT)
|
|
return layer
|
|
|
|
|
|
func _emit_sign_drop_started() -> void:
|
|
if is_inside_tree():
|
|
sign_drop_started.emit()
|