class_name Interactable extends Node3D @export var interaction: PackedScene = null var interaction_ui : Control = null @onready var view: Node3D = $View @onready var frame: Sprite3D = $Frame @onready var canvas_layer: CanvasLayer = $CanvasLayer var active : bool = true var shown : bool = false var hover : bool = false var collected : bool = false var tween: Tween = null func _ready() -> void: view.scale = Vector3.ZERO frame.modulate.a = 0 if interaction: interaction_ui = interaction.instantiate() as Control canvas_layer.add_child(interaction_ui) Scenes.player_enable.connect(_player_active) # TODO: do I have to clean this up? func _player_active(value: bool) -> void: active = value func _process(_delta: float) -> void: _process_hover() _process_billboard() func _process_billboard() -> void: if shown: var player_view := State.player_view look_at(player_view.global_position, Vector3.UP, true) func _process_hover() -> void: if active and hover and not shown: shown = true view.scale = Vector3.ZERO frame.modulate = Color.TRANSPARENT view.rotation.z = -PI*0.5 # Godot angle wrapping is ... something if tween: tween.kill() tween = create_tween().set_ease(Tween.EASE_OUT).set_trans(Tween.TRANS_BACK) tween.parallel().tween_property(view, "scale", Vector3.ONE, 1.0).set_delay(0.5) tween.parallel().tween_property(view, "rotation:z", 0, 0.8).set_delay(0.5) tween.parallel().tween_property(frame, "modulate:a", 1.0, 2.0) elif not hover and shown or shown and not active: shown = false if tween: tween.kill() tween = create_tween().set_ease(Tween.EASE_IN).set_trans(Tween.TRANS_BACK) tween.parallel().tween_property(view, "scale", Vector3.ZERO, 0.3) tween.parallel().tween_property(frame, "modulate:a", 0, 0.6) func _input(event: InputEvent) -> void: if not active or not hover or not shown: return print(event, event.is_action_pressed("ui_accept")) var clicked : bool = (event.is_action_pressed("ui_accept")) or (event is InputEventMouseButton and event.pressed) if hover and shown and clicked: collect_memento() func collect_memento() -> void: shown = false collected = true # Hide mouse and collapse other interactables BEFORE showing canvas get_tree().call_group("interactables", "collapse") # Show the CanvasLayer so the story is visible full-screen canvas_layer.show() if interaction_ui is StoryPlayable: Input.mouse_mode = Input.MOUSE_MODE_HIDDEN canvas_layer.show() Scenes.begin_sequence(interaction_ui.scene_id) # Play the story await interaction_ui.play() # Pick the cards var picker := State.room.get_node("%Picker") as CardPicker await picker.pick_cards(interaction_ui.scene_id, false) # Hide the CanvasLayer when done canvas_layer.hide() Scenes.end_sequence(interaction_ui.scene_id) # todo: maybe later? Input.mouse_mode = Input.MOUSE_MODE_CAPTURED Scenes.player_enable.emit(true) # TODO: this may not be our job?