Files
scrap-signal/entities/npcs/iris3/iris3.gd

240 lines
8.6 KiB
GDScript

class_name Iris3
extends Area2D
@export var prompt_text: String = "[E] Inspect"
@onready var sprite := $AnimatedSprite2D
const REQUIRED_ITEMS := { "circuit": 1 }
const DIALOGUE_INACTIVE := [
{ "name": "SYSTEM", "text": "Unit IRIS-3 detected. Main sensor offline." },
{ "name": "SYSTEM", "text": "Required component: compatible optical circuit." },
]
const DIALOGUE_NO_ITEM := [
{ "name": "SYSTEM", "text": "Optical circuit not detected. Search the surrounding area." },
]
const DIALOGUE_HAS_ITEM := [
{ "name": "SYSTEM", "text": "Compatible circuit detected. Start recalibration?" },
]
const DIALOGUE_POST_REPAIR := [
{ "name": "IRIS-3", "text": "... Her optics slowly pivot toward the sky." },
{ "name": "IRIS-3", "text": "I waited 847 days for someone to come. I didn't know if it was hope or stubbornness. Now I think they're the same thing." },
{ "name": "IRIS-3", "text": "Matteo Corda signed a document before leaving. He called it the Autonomous Continuity Protocol. In human terms... he told us we could stay." },
{ "name": "IRIS-3", "text": "There is an exit, to the west. It was never locked. I've known since the first day. I haven't moved. You can choose." },
]
const DIALOGUE_AFTER_REPAIR := [
{ "name": "IRIS-3", "text": "You're still here. That's an answer too." },
]
# ── Lifecycle ─────────────────────────────────────────────────────────────────
func _ready() -> void:
body_entered.connect(_on_body_entered)
body_exited.connect(_on_body_exited)
sprite.play("active" if GameState.get_flag("iris3_repaired") else "inactive")
func _on_body_entered(body: Node) -> void:
if body.is_in_group("player"):
get_tree().get_first_node_in_group("hud").show_prompt(prompt_text)
func _on_body_exited(body: Node) -> void:
if body.is_in_group("player"):
get_tree().get_first_node_in_group("hud").hide_prompt()
# ── Interaction ───────────────────────────────────────────────────────────────
func interact() -> void:
get_tree().get_first_node_in_group("hud").hide_prompt()
var dialogue_box := get_tree().get_first_node_in_group("dialogue_box")
if not dialogue_box:
return
if GameState.get_flag("iris3_repaired"):
dialogue_box.start(DIALOGUE_AFTER_REPAIR)
return
if not _has_required_items():
var dlg = DIALOGUE_NO_ITEM if GameState.get_flag("iris3_met") else DIALOGUE_INACTIVE
GameState.set_flag("iris3_met", true)
dialogue_box.start(dlg)
return
dialogue_box.start(DIALOGUE_HAS_ITEM)
dialogue_box.dialogue_finished.connect(_start_minigame, CONNECT_ONE_SHOT)
# ── Mini-jeu ──────────────────────────────────────────────────────────────────
func _start_minigame() -> void:
var dialogue_box := get_tree().get_first_node_in_group("dialogue_box")
if dialogue_box:
dialogue_box.hide()
var minigame_node := preload("res://ui/minigame/repair_minigame.tscn").instantiate()
get_tree().root.add_child(minigame_node)
var minigame := minigame_node as RepairMinigame
if minigame == null:
return
minigame.repair_complete.connect(_on_repair_done, CONNECT_ONE_SHOT)
minigame.open("iris3")
func _on_repair_done() -> void:
GameState.remove_item("circuit", 1)
GameState.set_flag("iris3_repaired", true)
sprite.play("active")
var hud: HUD = get_tree().get_first_node_in_group("hud")
if hud:
hud.show_log("Unit IRIS-3: recalibration complete. Archives accessible.")
await get_tree().create_timer(2.5).timeout
hud.show_log("Ending B unlocked.")
var dialogue_box := get_tree().get_first_node_in_group("dialogue_box")
if dialogue_box:
dialogue_box.start(DIALOGUE_POST_REPAIR)
dialogue_box.dialogue_finished.connect(_show_choice, CONNECT_ONE_SHOT)
# ── Choix final ───────────────────────────────────────────────────────────────
func _show_choice() -> void:
var player := get_tree().get_first_node_in_group("player")
if player:
player.is_locked = true
var canvas := CanvasLayer.new()
canvas.layer = 50
get_tree().root.add_child(canvas)
var root_ctrl := Control.new()
root_ctrl.set_anchors_preset(Control.PRESET_FULL_RECT)
root_ctrl.modulate.a = 0.0
canvas.add_child(root_ctrl)
var bg := ColorRect.new()
bg.set_anchors_preset(Control.PRESET_FULL_RECT)
bg.color = Color(0.05, 0.05, 0.05, 0.6)
root_ctrl.add_child(bg)
var center := CenterContainer.new()
center.set_anchors_preset(Control.PRESET_FULL_RECT)
root_ctrl.add_child(center)
var panel := PanelContainer.new()
var sb := StyleBoxFlat.new()
sb.bg_color = Color(0.08, 0.08, 0.10, 0.97)
sb.border_color = Color(0.47, 0.63, 0.73, 0.8)
sb.set_border_width_all(1)
sb.set_corner_radius_all(4)
sb.set_content_margin_all(14)
panel.add_theme_stylebox_override("panel", sb)
panel.custom_minimum_size = Vector2(220, 0)
center.add_child(panel)
var vbox := VBoxContainer.new()
vbox.add_theme_constant_override("separation", 10)
vbox.alignment = BoxContainer.ALIGNMENT_CENTER
panel.add_child(vbox)
var label := Label.new()
label.text = "What will SCRAP-09 do?"
label.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER
label.add_theme_color_override("font_color", Color("#A89E96"))
label.add_theme_font_size_override("font_size", 9)
vbox.add_child(label)
var sep := HSeparator.new()
sep.add_theme_color_override("color", Color(0.3, 0.3, 0.3, 0.5))
vbox.add_child(sep)
var hbox := HBoxContainer.new()
hbox.add_theme_constant_override("separation", 10)
hbox.alignment = BoxContainer.ALIGNMENT_CENTER
vbox.add_child(hbox)
var btn_partir := _make_button("Leave →", Color("#F0C87A"))
var btn_rester := _make_button("Stay", Color("#7BD4C4"))
hbox.add_child(btn_partir)
hbox.add_child(btn_rester)
btn_partir.pressed.connect(func():
canvas.queue_free()
_on_choice_partir()
)
btn_rester.pressed.connect(func():
canvas.queue_free()
_on_choice_rester()
)
var tween := create_tween()
tween.tween_property(root_ctrl, "modulate:a", 1.0, 0.3)
func _make_button(text: String, color: Color) -> Button:
var btn := Button.new()
btn.text = text
btn.add_theme_font_size_override("font_size", 9)
btn.add_theme_color_override("font_color", color)
btn.add_theme_color_override("font_hover_color", Color.WHITE)
var sb_normal := StyleBoxFlat.new()
sb_normal.bg_color = Color(0.12, 0.12, 0.15, 1.0)
sb_normal.border_color = Color(color.r, color.g, color.b, 0.5)
sb_normal.set_border_width_all(1)
sb_normal.set_corner_radius_all(3)
sb_normal.set_content_margin_all(8)
var sb_hover := StyleBoxFlat.new()
sb_hover.bg_color = Color(color.r, color.g, color.b, 0.2)
sb_hover.border_color = color
sb_hover.set_border_width_all(1)
sb_hover.set_corner_radius_all(3)
sb_hover.set_content_margin_all(8)
btn.add_theme_stylebox_override("normal", sb_normal)
btn.add_theme_stylebox_override("hover", sb_hover)
btn.add_theme_stylebox_override("pressed", sb_hover)
btn.add_theme_stylebox_override("focus", sb_normal)
return btn
func _on_choice_partir() -> void:
GameState.set_flag("iris3_repaired", false)
_launch_outro()
func _on_choice_rester() -> void:
var dialogue_box := get_tree().get_first_node_in_group("dialogue_box")
if dialogue_box:
dialogue_box.start(DIALOGUE_AFTER_REPAIR)
dialogue_box.dialogue_finished.connect(_launch_outro, CONNECT_ONE_SHOT)
else:
_launch_outro()
func _launch_outro() -> void:
var player := get_tree().get_first_node_in_group("player")
if player:
player.is_locked = false
var fade_canvas := CanvasLayer.new()
fade_canvas.layer = 100
get_tree().root.add_child(fade_canvas)
var fade := ColorRect.new()
fade.set_anchors_preset(Control.PRESET_FULL_RECT)
fade.color = Color(0, 0, 0, 1)
fade.modulate.a = 0.0
fade_canvas.add_child(fade)
var tween := fade_canvas.create_tween()
tween.tween_interval(0.5)
tween.tween_property(fade, "modulate:a", 1.0, 2.0)
tween.tween_callback(func():
fade_canvas.queue_free() # ← libère le canvas AVANT de changer de scène
get_tree().change_scene_to_file("res://levels/outro.tscn")
)
# ── Helpers ───────────────────────────────────────────────────────────────────
func _has_required_items() -> bool:
for item_id in REQUIRED_ITEMS:
if GameState.inventory.get(item_id, 0) < REQUIRED_ITEMS[item_id]:
return false
return true