2026-09-15 12:02:57 +02:00
|
|
|
@tool
|
2026-09-14 19:08:00 +02:00
|
|
|
extends Node3D
|
|
|
|
|
class_name CheeseSeller
|
|
|
|
|
## Places a cheese table and periodically launches moldy cheese at the player.
|
|
|
|
|
## DESIGN: stab the cheese (projectile), not the seller — seller dies of grief when cheese destroyed.
|
2026-09-15 12:02:57 +02:00
|
|
|
## @tool: billboard look only in editor (no cheese firing).
|
2026-09-14 19:08:00 +02:00
|
|
|
|
2026-09-15 12:02:57 +02:00
|
|
|
@export var texture_path: String = "res://assets/sprites/placeholders/cheese_seller.png":
|
|
|
|
|
set(v):
|
|
|
|
|
texture_path = v
|
|
|
|
|
if Engine.is_editor_hint() and is_node_ready():
|
|
|
|
|
_apply_billboard_look()
|
2026-09-14 19:08:00 +02:00
|
|
|
@export var fire_interval: float = 2.8
|
|
|
|
|
@export var cheese_speed: float = 7.0
|
|
|
|
|
|
|
|
|
|
var _alive: bool = true
|
|
|
|
|
var _timer: float = 0.0
|
|
|
|
|
var _grief: int = 0
|
|
|
|
|
|
|
|
|
|
@onready var _billboard: BillboardSprite = $BillboardSprite
|
|
|
|
|
|
|
|
|
|
func _ready() -> void:
|
2026-09-15 12:02:57 +02:00
|
|
|
_apply_billboard_look()
|
|
|
|
|
if Engine.is_editor_hint():
|
|
|
|
|
set_process(false)
|
|
|
|
|
return
|
2026-09-14 19:08:00 +02:00
|
|
|
add_to_group("interactable")
|
|
|
|
|
add_to_group("cheese_seller")
|
|
|
|
|
_timer = randf() * fire_interval
|
2026-09-15 12:02:57 +02:00
|
|
|
|
|
|
|
|
|
|
|
|
|
func _apply_billboard_look() -> void:
|
|
|
|
|
if _billboard == null:
|
|
|
|
|
_billboard = get_node_or_null("BillboardSprite") as BillboardSprite
|
|
|
|
|
if _billboard == null:
|
|
|
|
|
return
|
|
|
|
|
_billboard.set_target_height(SpriteFit.WORLD_PROP_HEIGHT)
|
|
|
|
|
if texture_path != "" and ResourceLoader.exists(texture_path):
|
2026-09-14 19:08:00 +02:00
|
|
|
var tex: Texture2D = load(texture_path)
|
|
|
|
|
if tex:
|
|
|
|
|
_billboard.set_texture(tex)
|
|
|
|
|
|
|
|
|
|
func _process(delta: float) -> void:
|
2026-09-15 12:02:57 +02:00
|
|
|
if Engine.is_editor_hint() or not _alive:
|
2026-09-14 19:08:00 +02:00
|
|
|
return
|
|
|
|
|
_timer -= delta
|
|
|
|
|
if _timer <= 0.0:
|
|
|
|
|
_timer = fire_interval
|
|
|
|
|
_fire()
|
|
|
|
|
|
|
|
|
|
func _fire() -> void:
|
|
|
|
|
var player := get_tree().get_first_node_in_group("player")
|
|
|
|
|
if player == null:
|
|
|
|
|
return
|
|
|
|
|
var proj_scene: PackedScene = load("res://scenes/prefabs/cheese_projectile.tscn")
|
|
|
|
|
if proj_scene == null:
|
|
|
|
|
return
|
|
|
|
|
var proj: Node3D = proj_scene.instantiate()
|
|
|
|
|
get_tree().current_scene.add_child(proj)
|
|
|
|
|
proj.global_position = global_position + Vector3(0, 0.8, 0)
|
|
|
|
|
var dir: Vector3 = (player.global_position + Vector3(0, 0.8, 0) - proj.global_position).normalized()
|
|
|
|
|
proj.set("velocity", dir * cheese_speed)
|
|
|
|
|
proj.add_to_group("cheese_projectile")
|
|
|
|
|
proj.add_to_group("enemy")
|
|
|
|
|
proj.set_meta("seller", self)
|
|
|
|
|
|
|
|
|
|
func notify_cheese_destroyed() -> void:
|
|
|
|
|
_grief += 1
|
|
|
|
|
if _grief >= 2:
|
|
|
|
|
_alive = false
|
|
|
|
|
var ui := get_node_or_null("/root/UIRoot")
|
|
|
|
|
if ui and ui.has_method("show_dialog"):
|
2026-09-15 12:02:57 +02:00
|
|
|
ui.show_dialog_key("dialog_cheese")
|
2026-09-14 19:08:00 +02:00
|
|
|
queue_free()
|
|
|
|
|
|
|
|
|
|
func on_interact(_player: Node) -> void:
|
|
|
|
|
var ui := get_node_or_null("/root/UIRoot")
|
|
|
|
|
if ui and ui.has_method("show_dialog"):
|
2026-09-15 12:02:57 +02:00
|
|
|
ui.show_dialog_key("dialog_cheese")
|
2026-09-14 19:08:00 +02:00
|
|
|
|
|
|
|
|
func take_hit(_amount: int = 1) -> void:
|
|
|
|
|
## Hitting the seller does nothing useful (DESIGN).
|
|
|
|
|
var ui := get_node_or_null("/root/UIRoot")
|
|
|
|
|
if ui and ui.has_method("show_dialog"):
|
2026-09-15 12:02:57 +02:00
|
|
|
ui.show_dialog_key("dialog_cheese")
|
|
|
|
|
|
|
|
|
|
func get_interact_prompt() -> String:
|
|
|
|
|
return "%s — %s" % [Locale.tr_key("prompt_talk"), Locale.tr_key("dialog_cheese")]
|
|
|
|
|
|
|
|
|
|
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)
|