Explosion_der_Spiele/scripts/levels/meadow_zone.gd
copilot 1cc8aa9b9d Add village 3D wiring, voice lines, and 1.74 m world scale.
Houses ~8.5 m; chimps capped ~0.85 m with safer axis fitting. Large GLB/PBR packs stay local until Git LFS (Forgejo HTTP 413).

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-09-15 12:02:57 +02:00

141 lines
4.6 KiB
GDScript

extends Node3D
## Wiese-Zone innerhalb der Dorf-Hauptwelt (kein eigener Scene-Teleport).
## Glitzos leben nur in meadow_aabb; außerhalb → Pixel-Sprühnebel.
@export var meadow_aabb: AABB = AABB(Vector3(20.0, -1.0, -18.0), Vector3(36.0, 8.0, 36.0))
var _glitzo_timer: float = 0.0
var _bear_saved: bool = false
var _first_glitzo_done: bool = false
func _ready() -> void:
_bear_saved = bool(GameState.get_flag("bear_saved", false))
_first_glitzo_done = Achievements.is_unlocked("first_glitzo")
var bear := get_node_or_null("Bear")
if bear and bear.has_signal("interacted"):
bear.interacted.connect(_on_bear)
var snake := get_node_or_null("SnakePickup")
if snake and snake.has_signal("interacted"):
snake.interacted.connect(_on_snake_pickup)
if Inventory.has_item("snake") or GameState.get_flag("snake_massaged", false):
if snake:
snake.queue_free()
if GameState.get_flag("snake_massaged", false):
_disable_mower()
func _process(delta: float) -> void:
## Only tick while player is near the meadow (cheap).
var player := get_tree().get_first_node_in_group("player")
if player is Node3D:
var pp: Vector3 = (player as Node3D).global_position
if not _near_meadow(pp):
_cull_glitzos_off_meadow()
return
_glitzo_timer += delta
if _glitzo_timer >= 3.2:
_glitzo_timer = 0.0
_spawn_glitzo()
_cull_glitzos_off_meadow()
func _near_meadow(p: Vector3) -> bool:
var expanded := meadow_aabb.grow(8.0)
return expanded.has_point(Vector3(p.x, 0.0, p.z))
func _cull_glitzos_off_meadow() -> void:
for child in get_children():
if child is Node3D and (child.name.begins_with("Glitzo") or str(child.get("texture_path")).ends_with("glitzo.png")):
var p: Vector3 = child.global_position
if not meadow_aabb.has_point(Vector3(p.x, 0.0, p.z)):
_pixel_spray(p)
child.queue_free()
func _pixel_spray(at: Vector3) -> void:
var scene: PackedScene = load("res://scenes/prefabs/quirxel_projectile.tscn")
if scene == null:
return
for i in range(5):
var q: Node3D = scene.instantiate()
get_parent().add_child(q)
q.global_position = at + Vector3(0, 0.4, 0)
var ang := randf() * TAU
q.set("velocity", Vector3(cos(ang), 0.5, sin(ang)) * randf_range(3.0, 6.0))
q.set("lifetime", 0.7)
q.set("hp", 99)
func _spawn_glitzo() -> void:
var scene: PackedScene = load("res://scenes/prefabs/enemy.tscn")
if scene == null:
return
var g: Node3D = scene.instantiate()
g.name = "GlitzoSpawn_%d" % Time.get_ticks_msec()
g.set("max_hp", 4)
g.set("texture_path", "res://assets/sprites/placeholders/glitzo.png")
g.set("ai_enabled", true)
g.set("glitzo_mode", true)
g.set("move_speed", 2.6)
g.set("attack_cooldown", 1.15)
g.set("attack_range", 1.1)
g.set("death_cause_key", "death_glitzo")
g.set("billboard_height", 1.0)
g.set("aggro_range", 22.0)
if not _first_glitzo_done:
g.set("achievement_on_defeat", "first_glitzo")
add_child(g)
## Spawn inside meadow AABB (local to world coords used by export).
var min_p := meadow_aabb.position
var max_p := meadow_aabb.position + meadow_aabb.size
g.global_position = Vector3(
randf_range(min_p.x + 4.0, max_p.x - 4.0),
0.1,
randf_range(min_p.z + 4.0, max_p.z - 4.0)
)
var bill: Node = g.get_node_or_null("BillboardSprite")
if bill and bill.has_method("set_texture"):
var tex: Texture2D = load("res://assets/sprites/placeholders/glitzo.png")
if tex:
bill.set_texture(tex)
if g.has_signal("defeated"):
g.defeated.connect(_on_glitzo_killed)
func _on_glitzo_killed() -> void:
_first_glitzo_done = true
func _on_bear(_npc: Node = null) -> void:
var ui := get_node_or_null("/root/UIRoot")
if _bear_saved:
if ui and ui.has_method("show_dialog"):
ui.show_dialog_key("dialog_bear_saved")
return
if Inventory.has_item("snake") and GameState.get_flag("snake_massaged", false):
_save_bear()
return
if Inventory.has_item("snake"):
if ui and ui.has_method("show_dialog"):
ui.show_dialog_key("dialog_villager_snake")
return
if ui and ui.has_method("show_dialog"):
ui.show_dialog_key("dialog_bear_help")
func _on_snake_pickup(_npc: Node = null) -> void:
var ui := get_node_or_null("/root/UIRoot")
if ui and ui.has_method("show_dialog"):
ui.show_dialog_key("dialog_snake_picked")
func _save_bear() -> void:
_bear_saved = true
GameState.set_flag("bear_saved", true)
if not Inventory.has_item("tooth"):
Inventory.add_item("tooth", 1)
_disable_mower()
var ui := get_node_or_null("/root/UIRoot")
if ui and ui.has_method("show_dialog"):
ui.show_dialog_key("dialog_bear_saved", 4.5)
func _disable_mower() -> void:
var mower := get_node_or_null("MowerHazard")
if mower:
mower.queue_free()
func notify_snake_massaged() -> void:
if not _bear_saved:
_save_bear()