AppResidencia/seguir-la-linea/scripts/game.gd

217 lines
5.8 KiB
GDScript

extends Control
@export var bg_color = Color.SKY_BLUE
@export var shape_name := "linea"
@export var line_width := 60.0
@export var line_color = Color.GRAY
@export var shape_scale := 0.9
@export var resolution := 360
@export var tracker_radius := 100
@export var follow_threshold := 100.0
@onready var shape_line = $MarginContainer/VBoxContainer/Control/ShapeLine
@onready var shape_area = $MarginContainer/VBoxContainer/Control/ShapeArea
@onready var tracker = $MarginContainer/VBoxContainer/Control/Tracker
@onready var tracker_shape = $MarginContainer/VBoxContainer/Control/Tracker/CollisionShape2D
@onready var collision_polygon = $MarginContainer/VBoxContainer/Control/ShapeArea/CollisionPolygon2D
@onready var menu = $MarginContainer/VBoxContainer/HBoxContainer/MenuButton
@onready var exit_menu_confirm_exit_button = $ExitButtonWindow/VBoxContainer/HBoxContainer/ConfirmExitButton
@onready var exit_menu_dont_exit_button = $ExitButtonWindow/VBoxContainer/HBoxContainer/DontExitButton
@onready var audio_player = $AudioStreamPlayer2D
var shape_points := PackedVector2Array()
var progress: Array[bool] = []
var fail_count := 0
var last_frame_inside := true
var completed := false
var shapes: Array[Dictionary] = [
{"name": "linea"},
{"name": "circulo"},
{"name": "corazon"},
]
var shape_order = []
var ordered_shapes = []
var current_index = 0
func _ready():
RenderingServer.set_default_clear_color(bg_color)
audio_player.play()
tracker_shape.shape = CircleShape2D.new()
tracker_shape.shape.radius = tracker_radius
# Generar lista ordenada
for name in shape_order:
for shape in shapes:
if shape["name"] == name:
ordered_shapes.append(shape)
break
menu.get_popup().add_item("Reproducir sonido", 0)
menu.get_popup().add_item("Salir", 1)
menu.get_popup().connect("id_pressed", self._on_menu_item_selected)
exit_menu_confirm_exit_button.pressed.connect(_on_exit_menu_button_pressed.bind(true))
exit_menu_dont_exit_button.pressed.connect(_on_exit_menu_button_pressed.bind(false))
# Cargar primera forma
load_shape(ordered_shapes[current_index])
func _on_menu_item_selected(id):
match id:
0:
audio_player.play()
1:
$ExitButtonWindow.show()
$Blocker.visible = true
func _on_exit_menu_button_pressed(exit : bool):
if exit:
GameData.clear_active_games()
ScreenManager.go_back_to_previous_scene()
else:
$ExitButtonWindow.hide()
$Blocker.visible = false
func load_shape(shape_data: Dictionary):
var shape_name = shape_data["name"]
var center = get_rect().size / 2
var radius = calculate_scale()
shape_points = generate_shape_points(shape_name, center, radius)
draw_shape(shape_points)
collision_polygon.polygon = shape_points
fail_count = 0
completed = false
func _on_next_shape_pressed():
var shape_name = ordered_shapes[current_index]["name"]
if not completed:
GameData.remove_active_game(shape_name)
current_index += 1
if current_index < ordered_shapes.size():
load_shape(ordered_shapes[current_index])
else:
$EndOfShapesWindow.show()
$Blocker.visible = true
func _on_end_of_game_button():
ScreenManager.go_back_to_previous_scene()
func calculate_scale() -> float:
var size = get_rect().size
return min(size.x, size.y) * shape_scale / 2.0
func generate_shape_points(shape: String, center: Vector2, radius: float) -> PackedVector2Array:
var points := PackedVector2Array()
match shape:
"circulo":
for i in range(resolution + 1):
var angle = TAU * i / resolution
points.append(center + Vector2(cos(angle), sin(angle)) * radius)
"corazon":
for i in range(resolution + 1):
var t = TAU * i / resolution
var x = 16 * pow(sin(t), 3)
var y = 13 * cos(t) - 5 * cos(2 * t) - 2 * cos(3 * t) - cos(4 * t)
var point = Vector2(x, -y) * (radius / 17.0)
points.append(center + point)
"linea":
for i in range(resolution + 1):
var t = float(i) / resolution
points.append(center + Vector2(-radius + t * radius * 2, 0))
return points
func draw_shape(points: PackedVector2Array):
shape_line.clear_points()
progress.clear()
shape_line.width = line_width
completed = false
for point in points:
shape_line.add_point(point)
progress.append(false)
update_line_gradient()
func _process(_delta):
var pos = get_local_mouse_position()
tracker.position = pos
var is_inside = tracker.get_overlapping_areas().has(shape_area)
if not is_inside and last_frame_inside:
fail_count += 1
GameData.add_failure_to_shape(ordered_shapes[current_index -1]["name"])
print("❌ Fallo detectado. Total fallos:", fail_count)
last_frame_inside = is_inside
var closest_index := -1
var closest_dist := follow_threshold
for i in range(shape_points.size()):
var dist = pos.distance_to(shape_points[i])
if dist < closest_dist:
closest_index = i
closest_dist = dist
if closest_index != -1:
var window = 5
for offset in range(-window, window + 1):
var index = clamp(closest_index + offset, 0, shape_points.size() - 1)
progress[index] = true
update_line_gradient()
check_completion()
func update_line_gradient():
var grad := Gradient.new()
var colors := []
var offsets := []
for i in range(progress.size()):
var t = float(i) / float(progress.size() - 1)
offsets.append(t)
if progress[i]:
colors.append(Color.GREEN)
else:
colors.append(line_color)
grad.colors = colors
grad.offsets = offsets
shape_line.gradient = grad
func check_completion():
if not completed and progress.all(func(p): return p):
completed = true
await get_tree().create_timer(1.0).timeout # espera 1 segundo
# Llamar a end_game para registrar la forma completada
var shape_name = ordered_shapes[current_index]["name"]
await GameData.end_game(shape_name)
current_index += 1
if current_index < ordered_shapes.size():
load_shape(ordered_shapes[current_index])
else:
$EndOfShapesWindow.show()
$Blocker.visible = true