Option (a): cursor shape still changes (body orients to targets), but verb labels and interaction prompts are suppressed when insert_active is false. Amends D-056 and D-057 with resolution note. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
96 lines
2.9 KiB
GDScript
96 lines
2.9 KiB
GDScript
extends PanelContainer
|
|
|
|
# Interaction prompt — displays available interaction verb for nearby entity.
|
|
# Server-driven: shows when GameState.nearby_interactions is non-empty,
|
|
# hides when empty. No game logic — pure display layer.
|
|
#
|
|
# v0.1: Single-line "E - Talk" (first verb on nearest entity)
|
|
# v0.2: Will be replaced/extended with radial verb menu.
|
|
# Public interface: get_interaction_target(), get_selected_verb()
|
|
|
|
@onready var prompt_label: Label = $MarginContainer/PromptLabel
|
|
|
|
var _is_showing: bool = false
|
|
var _active_tween: Tween = null
|
|
var _current_target_id: int = -1
|
|
# OQ-07 (#522): when false, prompt is suppressed (z-layer 6 insert overlay only)
|
|
var _insert_active: bool = true
|
|
|
|
const FADE_IN: float = 0.15
|
|
const FADE_OUT: float = 0.15
|
|
|
|
func _ready() -> void:
|
|
modulate.a = 0.0
|
|
visible = false
|
|
_is_showing = false
|
|
|
|
func _process(_delta: float) -> void:
|
|
# OQ-07: insert off means no verb labels (z-layer 6 insert overlay suppressed)
|
|
if not _insert_active:
|
|
if _is_showing:
|
|
_hide_prompt()
|
|
return
|
|
var interactions: Array = GameState.nearby_interactions
|
|
if interactions.size() > 0:
|
|
_show_prompt(interactions[0])
|
|
elif _is_showing:
|
|
_hide_prompt()
|
|
|
|
func _show_prompt(interaction: Dictionary) -> void:
|
|
var target_id: int = interaction.get("entity_id", -1)
|
|
var verbs: Array = interaction.get("verbs", [])
|
|
|
|
if verbs.is_empty():
|
|
if _is_showing:
|
|
_hide_prompt()
|
|
return
|
|
|
|
# v0.1: pick first verb (sorted by priority from server)
|
|
var verb_label: String = verbs[0].get("label", "")
|
|
var display_text: String = "E - %s" % verb_label
|
|
|
|
if _current_target_id != target_id or prompt_label.text != display_text:
|
|
prompt_label.text = display_text
|
|
_current_target_id = target_id
|
|
|
|
if not _is_showing:
|
|
visible = true
|
|
_is_showing = true
|
|
if _active_tween and _active_tween.is_valid():
|
|
_active_tween.kill()
|
|
_active_tween = create_tween()
|
|
_active_tween.tween_property(self, "modulate:a", 1.0, FADE_IN)
|
|
|
|
func _hide_prompt() -> void:
|
|
if not _is_showing:
|
|
return
|
|
_is_showing = false
|
|
_current_target_id = -1
|
|
if _active_tween and _active_tween.is_valid():
|
|
_active_tween.kill()
|
|
_active_tween = create_tween()
|
|
_active_tween.tween_property(self, "modulate:a", 0.0, FADE_OUT)
|
|
_active_tween.tween_callback(func(): visible = false)
|
|
|
|
## Returns the current interaction target entity ID, or -1 if no interaction.
|
|
func get_interaction_target() -> int:
|
|
return _current_target_id
|
|
|
|
## OQ-07 (#522): insert off hides prompt (diegetic: no insert data on z-layer 6).
|
|
## Cursor shape changes still fire on cursor_renderer.gd.
|
|
func set_insert_active(active: bool) -> void:
|
|
_insert_active = active
|
|
if not active and _is_showing:
|
|
_hide_prompt()
|
|
|
|
|
|
## Returns the selected verb kind (v0.1: first verb on nearest, v0.2: radial selection).
|
|
func get_selected_verb() -> String:
|
|
var interactions: Array = GameState.nearby_interactions
|
|
if interactions.is_empty():
|
|
return ""
|
|
var verbs: Array = interactions[0].get("verbs", [])
|
|
if verbs.is_empty():
|
|
return ""
|
|
return verbs[0].get("kind", "")
|