Files
scrap-signal/ui/minigame/repair_minigame.gd
Kellan KOZUME e02fbeddc8 refactor(minigame): replace grid with wire-connecting minigame
- Replace GridContainer-based puzzle with drag-and-drop wire system
- Add draw_wires() and on_canvas_input() driven by WireCanvas node
- Shuffle right-side endpoints for varied puzzle layout
- Fix type inference errors (Dictionary values cast to Array/Color)
- Fix WireCanvas size_flags_vertical to SIZE_SHRINK_CENTER
- Set Panel custom_minimum_size and VBox to prevent layout overflow
- Title and StatusLabel now always visible
2026-04-26 03:25:02 -04:00

186 lines
5.9 KiB
GDScript

class_name RepairMinigame
extends CanvasLayer
signal repair_complete
const PUZZLES := {
"vrac7": {
"title": "RECONNECTION — MOTOR SYSTEM",
"colors": [Color("#f4c430"), Color("#5bc8f5"), Color("#f47c3c")]
},
"iris3": {
"title": "RECALIBRATION — OPTICAL SENSOR",
"colors": [Color("#f4c430"), Color("#5bc8f5")]
},
"scrap09": {
"title": "SELF-REPAIR — SCRAP-09",
"colors": [Color("#f4c430"), Color("#5bc8f5"), Color("#f47c3c")]
},
}
const DOT_RADIUS := 8.0
const WIRE_WIDTH := 4.0
var current_puzzle : Dictionary
var right_order : Array = []
var connections : Dictionary = {}
var dragging : int = -1
var drag_pos : Vector2
@onready var wire_canvas : Control = $Background/Panel/VBoxContainer/WireCanvas
@onready var title_lbl : Label = $Background/Panel/VBoxContainer/Title
@onready var status : Label = $Background/Panel/VBoxContainer/StatusLabel
func _ready() -> void:
var vbox = $Background/Panel/VBoxContainer
layer = 1000
wire_canvas.custom_minimum_size = Vector2(320, 120)
wire_canvas.size_flags_vertical = Control.SIZE_SHRINK_CENTER
vbox.size_flags_vertical = Control.SIZE_SHRINK_CENTER
func _unhandled_input(event: InputEvent) -> void:
if event.is_action_pressed("ui_cancel"):
_cancel()
func open(robot_id: String) -> void:
current_puzzle = PUZZLES.get(robot_id, {})
if current_puzzle.is_empty():
return
title_lbl.text = current_puzzle["title"] as String
connections.clear()
dragging = -1
var n: int = (current_puzzle["colors"] as Array).size() # ← FIX ligne 49
right_order = range(n)
right_order.shuffle()
_set_hud_visible(false)
_set_player_enabled(false)
_update_status()
wire_canvas.queue_redraw()
show()
# ── Drawing ────────────────────────────────────────────────────────────────────
func draw_wires(canvas: Control) -> void:
if current_puzzle.is_empty():
return
var colors := current_puzzle["colors"] as Array
var n: int = colors.size()
var w := canvas.size.x
var h := canvas.size.y
canvas.draw_line(Vector2(w * 0.5, 10), Vector2(w * 0.5, h - 10),
Color(1, 1, 1, 0.08), 1.0)
for left_idx in connections:
var right_slot: int = connections[left_idx]
var from := _left_pos(left_idx, n, w, h)
var to := _right_pos(right_slot, n, w, h)
canvas.draw_line(from, to, colors[left_idx], WIRE_WIDTH, true)
if dragging >= 0:
var from := _left_pos(dragging, n, w, h)
canvas.draw_line(from, drag_pos, (colors[dragging] as Color).lightened(0.3), WIRE_WIDTH, true)
for i in n:
var pos := _left_pos(i, n, w, h)
var c := colors[i] as Color
canvas.draw_circle(pos, DOT_RADIUS, c)
if not connections.has(i):
canvas.draw_arc(pos, DOT_RADIUS + 4, 0, TAU, 32, c.lightened(0.5), 2.0)
for i in n:
var pos := _right_pos(i, n, w, h)
var col_idx : int = right_order[i]
var c := colors[col_idx] as Color
canvas.draw_circle(pos, DOT_RADIUS, c)
if not connections.values().has(i):
canvas.draw_arc(pos, DOT_RADIUS + 4, 0, TAU, 32, c.lightened(0.5), 2.0)
# ── Input ─────────────────────────────────────────────────────────────────────
func on_canvas_input(event: InputEvent) -> void:
if current_puzzle.is_empty():
return
var colors := current_puzzle["colors"] as Array
var n: int = colors.size()
var w := wire_canvas.size.x
var h := wire_canvas.size.y
if event is InputEventMouseButton and event.button_index == MOUSE_BUTTON_LEFT:
if event.pressed:
for i in n:
if event.position.distance_to(_left_pos(i, n, w, h)) <= DOT_RADIUS + 6.0:
connections.erase(i)
dragging = i
drag_pos = event.position
_update_status()
wire_canvas.queue_redraw()
return
else:
if dragging >= 0:
for i in n:
if connections.values().has(i):
continue
if event.position.distance_to(_right_pos(i, n, w, h)) <= DOT_RADIUS + 6.0:
if right_order[i] == dragging:
connections[dragging] = i
_update_status()
_check_win()
dragging = -1
wire_canvas.queue_redraw()
elif event is InputEventMouseMotion and dragging >= 0:
drag_pos = event.position
wire_canvas.queue_redraw()
# ── Helpers ───────────────────────────────────────────────────────────────────
func _left_pos(i: int, n: int, w: float, h: float) -> Vector2:
return Vector2(DOT_RADIUS + 20.0, h / (n + 1) * (i + 1))
func _right_pos(i: int, n: int, w: float, h: float) -> Vector2:
return Vector2(w - DOT_RADIUS - 20.0, h / (n + 1) * (i + 1))
func _check_win() -> void:
if connections.size() == (current_puzzle["colors"] as Array).size():
await get_tree().create_timer(0.6).timeout
repair_complete.emit()
_close()
func _cancel() -> void:
dragging = -1
_close()
func _close() -> void:
_set_hud_visible(true)
_set_player_enabled(true)
hide()
func _update_status() -> void:
var total: int = (current_puzzle.get("colors", []) as Array).size()
var done := connections.size()
if done == total:
status.text = "REPAIR COMPLETE"
status.add_theme_color_override("font_color", Color("#6daa45"))
else:
status.text = "%d / %d connections established" % [done, total]
status.remove_theme_color_override("font_color")
func _set_hud_visible(v: bool) -> void:
var hud = get_tree().get_first_node_in_group("hud")
if hud:
hud.visible = v
func _set_player_enabled(enabled: bool) -> void:
var player = get_tree().get_first_node_in_group("player")
if player:
player.set_process(enabled)
player.set_physics_process(enabled)
player.set_process_unhandled_input(enabled)
player.is_locked = false
if not enabled and player is CharacterBody2D:
player.velocity = Vector2.ZERO