Explosion_der_Spiele/scripts/combat/interactable_npc.gd

114 lines
4.1 KiB
GDScript3
Raw Normal View History

@tool
extends Node3D
class_name InteractableNPC
## Billboard NPC with dialog key and optional quest hook.
## @tool: applies texture/height in the editor so NPCs are WYSIWYG (no quest logic).
signal interacted(npc: InteractableNPC)
@export var dialog_key: String = "dialog_hongkong_npc"
@export var texture_path: String = "res://assets/sprites/placeholders/npc.png":
set(v):
texture_path = v
if Engine.is_editor_hint() and is_node_ready():
_apply_billboard_look()
@export var billboard_height: float = 1.74:
set(v):
billboard_height = v
if Engine.is_editor_hint() and is_node_ready():
_apply_billboard_look()
@export var billboard_max_width: float = 2.2:
set(v):
billboard_max_width = v
if Engine.is_editor_hint() and is_node_ready():
_apply_billboard_look()
@export var can_interact: bool = true ## false = deco only (e.g. village house)
@export var give_item_id: String = ""
@export var give_once: bool = false ## Don't stack if player already has the item
@export var despawn_after_give: bool = false ## Remove world pickup after taking
@export var require_item_id: String = ""
@export var consume_required: bool = false
@export var quest_flag: String = ""
@export var achievement_id: String = ""
@export var on_success_goto: String = ""
@export var prompt_key: String = "" ## Optional override, e.g. prompt_press_button
@onready var _billboard: BillboardSprite = $BillboardSprite
func _ready() -> void:
_apply_billboard_look()
if Engine.is_editor_hint():
return
if can_interact:
add_to_group("interactable")
func _apply_billboard_look() -> void:
if _billboard == null:
_billboard = get_node_or_null("BillboardSprite") as BillboardSprite
if _billboard == null:
return
_billboard.max_width = billboard_max_width
_billboard.set_target_height(billboard_height)
if texture_path != "" and ResourceLoader.exists(texture_path):
var tex: Texture2D = load(texture_path)
if tex:
_billboard.set_texture(tex)
func on_interact(_player: Node) -> void:
if Engine.is_editor_hint() or not can_interact:
return
interacted.emit(self)
# World buttons / portals: go immediately (optional short bark via dialog_key)
var ui := get_node_or_null("/root/UIRoot")
if dialog_key != "" and on_success_goto == "":
if ui and ui.has_method("show_dialog_key"):
ui.show_dialog_key(dialog_key)
elif ui and ui.has_method("show_dialog"):
ui.show_dialog(Locale.tr_key(dialog_key), 3.5, dialog_key)
if require_item_id != "" and not Inventory.has_item(require_item_id):
return
if require_item_id != "" and consume_required:
Inventory.remove_item(require_item_id, 1)
if give_item_id != "":
if not (give_once and Inventory.has_item(give_item_id)):
Inventory.add_item(give_item_id, 1)
if despawn_after_give:
call_deferred("queue_free")
if quest_flag != "":
GameState.set_flag(quest_flag, true)
if quest_flag == "house_built":
GameState.house_built = true
if achievement_id != "":
Achievements.unlock(achievement_id)
if on_success_goto != "":
# Optional one-liner before leave (non-blocking bark if UI supports it)
if dialog_key != "" and ui and ui.has_method("show_dialog_key"):
ui.show_dialog_key(dialog_key, 1.2, false)
GameState.goto_scene(on_success_goto)
func get_interact_prompt() -> String:
if prompt_key != "":
return Locale.tr_key(prompt_key)
if texture_path.ends_with("button.png"):
return Locale.tr_key("prompt_press_button")
if texture_path.ends_with("tiramisu.png"):
return Locale.tr_key("prompt_eat_tiramisu")
if give_item_id != "":
var item_name := Locale.tr_key("item_" + give_item_id)
return "%s — %s" % [Locale.tr_key("prompt_pick_up"), item_name]
if on_success_goto != "":
return Locale.tr_key("prompt_enter")
if quest_flag == "house_built":
return Locale.tr_key("prompt_build")
if dialog_key != "":
var label := Locale.tr_key(dialog_key)
if label.length() > 42:
label = label.substr(0, 40) + "…"
return "%s — %s" % [Locale.tr_key("prompt_talk"), label]
return Locale.tr_key("prompt_use")
func set_interaction_highlight(enabled: bool, color: Color = Color(1.4, 1.35, 0.4)) -> void:
if _billboard and _billboard.has_method("set_highlight"):
_billboard.set_highlight(enabled, color)