Untitled
Anonymous
plain_text
02/24/2026 12:38 AM
12.1 KB
9
Indexable
extends Node2D
@export var foxpost_portrait: Texture2D
@export var package_icon: Texture2D # reward icon shown in overlay (like Aurora token)
var dialogue_ui
var note_ui
var fade_rect
var item_get_overlay
var main_ref
@onready var player = $Player
@onready var box_eicon: CanvasItem = get_node_or_null("Box/EIcon")
@onready var exit_eicon: CanvasItem = get_node_or_null("ExitToHub/EIcon")
@onready var box = _get_interactable("Box")
@onready var exit_to_hub = _get_interactable("ExitToHub")
var has_reward := false
var interaction_locked := false
# Optional per-scene spawn markers (only if you add them)
var _spawn_from := "left"
@onready var spawn_left: Marker2D = get_node_or_null("SpawnLeft")
@onready var spawn_right: Marker2D = get_node_or_null("SpawnRight")
func set_spawn_from(side: String) -> void:
_spawn_from = side
func bind_ui(d, n, f, i, m) -> void:
dialogue_ui = d
note_ui = n
fade_rect = f
item_get_overlay = i
main_ref = m
func _ready() -> void:
await _apply_spawn_if_present()
has_reward = GameState.unlock_home
if has_reward:
if exit_eicon: exit_eicon.visible = true
if box_eicon: box_eicon.visible = false
else:
if exit_eicon: exit_eicon.visible = false
if box != null and box.has_signal("interacted"):
box.interacted.connect(_on_box_interacted)
else:
push_error("Foxpost: Box interactable not found. Name 'Box' + Interactable.gd (or child 'Interactable').")
if exit_to_hub != null and exit_to_hub.has_signal("interacted"):
exit_to_hub.interacted.connect(_on_exit_to_hub)
else:
push_error("Foxpost: ExitToHub interactable not found. Name 'ExitToHub' + Interactable.gd (or child 'Interactable').")
func _get_interactable(node_name: String) -> Node:
var n := get_node_or_null(node_name)
if n == null:
return null
if n.has_signal("interacted"):
return n
var child := n.get_node_or_null("Interactable")
if child != null and child.has_signal("interacted"):
return child
return null
func _apply_spawn_if_present() -> void:
if spawn_left == null or spawn_right == null:
return
var target: Marker2D = spawn_left if _spawn_from == "left" else spawn_right
if target == null:
return
player.set_can_move(false)
var offset := Vector2(-32, 0) if _spawn_from == "left" else Vector2(32, 0)
player.global_position = target.global_position + offset
var t := create_tween()
t.tween_property(player, "global_position", target.global_position, 0.35)\
.set_trans(Tween.TRANS_SINE)\
.set_ease(Tween.EASE_OUT)
await t.finished
player.set_can_move(true)
# -------------------------------------------------
# Choice connection helper (same as Aurora)
# -------------------------------------------------
func _connect_choice_once(handler: Callable) -> void:
if dialogue_ui == null:
push_error("Foxpost: dialogue_ui is null (bind_ui not called?)")
return
if dialogue_ui.choice_selected.is_connected(handler):
dialogue_ui.choice_selected.disconnect(handler)
dialogue_ui.choice_selected.connect(handler, CONNECT_ONE_SHOT)
# -------------------------------------------------
# Debounced "press E to close dialogue" helper
# - Lets typewriter finish (DialogueUI handles E for rushing)
# - Then waits for a FRESH E press to close
# -------------------------------------------------
func _close_after_fresh_e() -> void:
# Wait until typing ends
while dialogue_ui != null and dialogue_ui.is_typing():
await get_tree().process_frame
# Debounce: wait release (if player is holding interact)
while Input.is_action_pressed("interact"):
await get_tree().process_frame
# Fresh E press to close
while true:
await get_tree().process_frame
if Input.is_action_just_pressed("interact"):
if dialogue_ui != null:
dialogue_ui.hide_dialogue()
break
# Wait release to prevent instant retrigger
while Input.is_action_pressed("interact"):
await get_tree().process_frame
await get_tree().create_timer(0.05).timeout
# -------------------------------------------------
# Post-reward reinteraction line (Aurora-style)
# -------------------------------------------------
func _show_reinteraction_line(speaker: String, text: String, portrait: Texture2D) -> void:
if interaction_locked:
return
interaction_locked = true
player.set_can_move(false)
# Debounce the interact press that triggered it
await get_tree().process_frame
while Input.is_action_pressed("interact"):
await get_tree().process_frame
dialogue_ui.show_dialogue(speaker, text, portrait, [])
await _close_after_fresh_e()
player.set_can_move(true)
interaction_locked = false
# -------------------------------------------------
# Exit back to hub (only after reward)
# -------------------------------------------------
func _on_exit_to_hub() -> void:
if interaction_locked:
return
if not has_reward:
return
if exit_eicon:
exit_eicon.visible = false
if main_ref != null and main_ref.has_method("go_hub"):
main_ref.go_hub("left")
else:
push_error("Foxpost: main_ref.go_hub missing")
# -------------------------------------------------
# Interaction entry
# -------------------------------------------------
func _on_box_interacted() -> void:
if interaction_locked:
return
if has_reward:
await _show_reinteraction_line(
"Foxpost",
"Már kivetted a csomagod.",
foxpost_portrait
)
return
interaction_locked = true
player.set_can_move(false)
_stage_picker()
# -------------------------------------------------
# Picker -> wrong (E to close -> back) -> right (E to close -> reward -> E to finish)
# -------------------------------------------------
func _stage_picker() -> void:
dialogue_ui.show_dialogue(
"Foxpost",
"Melyik rekeszt nyitod?",
foxpost_portrait,
[
{"label": "21-es rekesz"},
{"label": "32-es rekesz"},
{"label": "53-as rekesz"},
{"label": "64-es rekesz"},
]
)
_connect_choice_once(_choice_picker)
func _choice_picker(i: int) -> void:
# Correct is index 2 (53-as)
if i == 2:
await _stage_right()
else:
await _stage_wrong()
func _stage_wrong() -> void:
dialogue_ui.show_dialogue(
"Foxpost",
"Sajnos ez nem az. Próbálj egy másikat.",
foxpost_portrait,
[]
)
# Player: E can finish typewriter; then fresh E closes; then return to picker
await _close_after_fresh_e()
_stage_picker()
func _stage_right() -> void:
dialogue_ui.show_dialogue(
"Foxpost",
"**A rekesz kinyílik...**\nVedd át a csomagod, már régóta vár rád.",
foxpost_portrait,
[]
)
# Player finishes typing with E; then fresh E closes the dialogue
await _close_after_fresh_e()
# Then grant reward overlay
await _grant_reward()
# After reward, require a fresh E release/press cycle before enabling movement
# (prevents instant re-trigger on the box)
while Input.is_action_pressed("interact"):
await get_tree().process_frame
await get_tree().create_timer(0.05).timeout
player.set_can_move(true)
interaction_locked = false
# -------------------------------------------------
# Reward: show overlay + unlock home
# -------------------------------------------------
func _grant_reward() -> void:
has_reward = true
GameState.unlock_home = true
await _play_item_get_sequence(package_icon)
if box_eicon:
box_eicon.visible = false
if exit_eicon:
exit_eicon.visible = true
# -------------------------------------------------
# Item get sequence (copied from Aurora)
# -------------------------------------------------
func _play_item_get_sequence(texture: Texture2D) -> void:
if fade_rect == null or item_get_overlay == null:
push_error("Foxpost: Missing fade_rect / item_get_overlay")
return
if texture == null:
push_error("Foxpost: Reward texture is null")
return
var icon := item_get_overlay.get_node("Center/ItemIcon") as TextureRect
if icon == null:
push_error("Foxpost: ItemGetOverlay missing Center/ItemIcon")
return
player.set_can_move(false)
fade_rect.visible = true
fade_rect.modulate.a = 0.0
item_get_overlay.visible = true
icon.visible = true
icon.texture = texture
icon.modulate = Color(1, 1, 1, 0)
icon.scale = Vector2(1.0, 1.0)
var t := create_tween()
t.set_pause_mode(Tween.TWEEN_PAUSE_PROCESS)
t.tween_property(fade_rect, "modulate:a", 1.0, 0.25).set_trans(Tween.TRANS_SINE).set_ease(Tween.EASE_OUT)
await t.finished
var t2 := create_tween()
t2.set_pause_mode(Tween.TWEEN_PAUSE_PROCESS)
t2.tween_property(icon, "modulate:a", 1.0, 0.18).set_trans(Tween.TRANS_SINE).set_ease(Tween.EASE_OUT)
t2.parallel().tween_property(icon, "scale", Vector2(1.12, 1.12), 0.18).set_trans(Tween.TRANS_SINE).set_ease(Tween.EASE_OUT)
t2.tween_property(icon, "scale", Vector2(1.0, 1.0), 0.18).set_trans(Tween.TRANS_SINE).set_ease(Tween.EASE_IN)
t2.tween_property(icon, "scale", Vector2(1.16, 1.16), 0.20).set_trans(Tween.TRANS_SINE).set_ease(Tween.EASE_OUT)
t2.tween_property(icon, "scale", Vector2(1.0, 1.0), 0.20).set_trans(Tween.TRANS_SINE).set_ease(Tween.EASE_IN)
t2.tween_interval(0.25)
t2.tween_property(icon, "modulate:a", 0.0, 0.20).set_trans(Tween.TRANS_SINE).set_ease(Tween.EASE_IN)
await t2.finished
icon.visible = false
item_get_overlay.visible = false
var t3 := create_tween()
t3.set_pause_mode(Tween.TWEEN_PAUSE_PROCESS)
t3.tween_property(fade_rect, "modulate:a", 0.0, 0.25).set_trans(Tween.TRANS_SINE).set_ease(Tween.EASE_IN)
await t3.finished
fade_rect.visible = falseEditor is loading...
Leave a Comment