2026-09-14 18:36:02 +02:00
|
|
|
extends Node3D
|
2026-09-15 12:02:57 +02:00
|
|
|
## Glitschkönig: Reveal, Quirxel-Schüsse, Split, Flucht (DESIGN.md §12).
|
2026-09-14 19:08:00 +02:00
|
|
|
|
|
|
|
|
enum Phase { FULL, SPLIT, FLEE, DONE }
|
|
|
|
|
|
|
|
|
|
var _phase: Phase = Phase.FULL
|
|
|
|
|
var _revealed: bool = false
|
2026-09-15 12:02:57 +02:00
|
|
|
var _splits_done: int = 0
|
|
|
|
|
const SPLITS_MAX := 2
|
2026-09-14 18:36:02 +02:00
|
|
|
|
|
|
|
|
func _ready() -> void:
|
|
|
|
|
GameState.current_scene_id = "final_boss"
|
|
|
|
|
GameState.heal_full()
|
2026-09-14 19:08:00 +02:00
|
|
|
_revealed = bool(GameState.get_flag("glitsch_revealed", false))
|
|
|
|
|
if not _revealed:
|
|
|
|
|
_do_reveal()
|
|
|
|
|
var boss := get_node_or_null("Hongkong")
|
|
|
|
|
if boss:
|
2026-09-15 12:02:57 +02:00
|
|
|
boss.set("max_hp", 16)
|
|
|
|
|
boss.set("hp", 16)
|
|
|
|
|
boss.set("ai_enabled", true)
|
|
|
|
|
boss.set("move_speed", 1.8)
|
|
|
|
|
boss.set("attack_damage", 1)
|
|
|
|
|
boss.set("attack_cooldown", 1.4)
|
|
|
|
|
boss.set("death_cause_key", "death_default")
|
|
|
|
|
boss.set("billboard_height", SpriteFit.WORLD_BOSS_HEIGHT)
|
|
|
|
|
boss.set("shout_key", "dialog_hongkong_boss")
|
|
|
|
|
boss.set("shout_interval", 8.0)
|
2026-09-14 19:08:00 +02:00
|
|
|
if boss.has_signal("hit"):
|
|
|
|
|
boss.hit.connect(_on_boss_hit)
|
|
|
|
|
if boss.has_signal("defeated"):
|
|
|
|
|
boss.defeated.connect(_on_boss_defeated)
|
|
|
|
|
var t := Timer.new()
|
2026-09-15 12:02:57 +02:00
|
|
|
t.wait_time = 2.2
|
2026-09-14 19:08:00 +02:00
|
|
|
t.autostart = true
|
|
|
|
|
t.timeout.connect(_spawn_quirxel)
|
|
|
|
|
add_child(t)
|
|
|
|
|
|
|
|
|
|
func _do_reveal() -> void:
|
|
|
|
|
GameState.set_flag("glitsch_revealed", true)
|
|
|
|
|
Inventory.add_item("banana", 1)
|
|
|
|
|
Inventory.add_item("wartchen", 1)
|
|
|
|
|
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_reveal", 5.5)
|
|
|
|
|
await get_tree().create_timer(2.0).timeout
|
|
|
|
|
ui.show_dialog_key("dialog_boss_unclever", 3.5)
|
2026-09-14 19:08:00 +02:00
|
|
|
|
|
|
|
|
func _on_boss_hit(remaining: int) -> void:
|
2026-09-15 12:02:57 +02:00
|
|
|
var boss := get_node_or_null("Hongkong")
|
|
|
|
|
if remaining <= 10 and _splits_done < SPLITS_MAX and _phase != Phase.FLEE:
|
2026-09-14 19:08:00 +02:00
|
|
|
_phase = Phase.SPLIT
|
2026-09-15 12:02:57 +02:00
|
|
|
_splits_done += 1
|
2026-09-14 19:08:00 +02:00
|
|
|
_spawn_split_clone()
|
|
|
|
|
var ui := get_node_or_null("/root/UIRoot")
|
|
|
|
|
if ui and ui.has_method("show_dialog"):
|
|
|
|
|
ui.show_dialog("Er teilt sich wie Minecraft-Schleim!", 2.5)
|
2026-09-15 12:02:57 +02:00
|
|
|
if remaining <= 3 and _phase != Phase.DONE:
|
2026-09-14 19:08:00 +02:00
|
|
|
_phase = Phase.FLEE
|
2026-09-15 12:02:57 +02:00
|
|
|
if boss:
|
|
|
|
|
boss.set("flee_below_hp", 99)
|
|
|
|
|
boss.set("move_speed", 5.5)
|
|
|
|
|
boss.set("ai_enabled", true)
|
2026-09-14 19:08:00 +02:00
|
|
|
|
|
|
|
|
func _spawn_split_clone() -> void:
|
|
|
|
|
var scene: PackedScene = load("res://scenes/prefabs/enemy.tscn")
|
|
|
|
|
if scene == null:
|
|
|
|
|
return
|
|
|
|
|
var c: Node3D = scene.instantiate()
|
2026-09-15 12:02:57 +02:00
|
|
|
c.name = "GlitschSplit_%d" % _splits_done
|
2026-09-14 19:08:00 +02:00
|
|
|
add_child(c)
|
2026-09-15 12:02:57 +02:00
|
|
|
c.global_position = Vector3(randf_range(-4, 4), 0.1, -2)
|
|
|
|
|
c.set("max_hp", 5)
|
|
|
|
|
c.set("texture_path", "res://assets/sprites/placeholders/glitschkoenig.png")
|
|
|
|
|
c.set("ai_enabled", true)
|
|
|
|
|
c.set("move_speed", 3.2)
|
|
|
|
|
c.set("attack_damage", 1)
|
|
|
|
|
c.set("billboard_height", 1.2)
|
|
|
|
|
c.set("death_cause_key", "death_quirxel")
|
2026-09-14 19:08:00 +02:00
|
|
|
var bill = c.get_node_or_null("BillboardSprite")
|
|
|
|
|
if bill and bill.has_method("set_texture"):
|
2026-09-15 12:02:57 +02:00
|
|
|
var tex: Texture2D = load("res://assets/sprites/placeholders/glitschkoenig.png")
|
2026-09-14 19:08:00 +02:00
|
|
|
if tex:
|
|
|
|
|
bill.set_texture(tex)
|
|
|
|
|
|
|
|
|
|
func _spawn_quirxel() -> void:
|
|
|
|
|
if _phase == Phase.DONE or GameState.get_flag("hongkong_defeated", false):
|
|
|
|
|
return
|
2026-09-15 12:02:57 +02:00
|
|
|
var scene: PackedScene = load("res://scenes/prefabs/quirxel_projectile.tscn")
|
2026-09-14 19:08:00 +02:00
|
|
|
if scene == null:
|
|
|
|
|
return
|
2026-09-15 12:02:57 +02:00
|
|
|
var player := get_tree().get_first_node_in_group("player")
|
|
|
|
|
var boss := get_node_or_null("Hongkong")
|
|
|
|
|
var origin := Vector3(0, 0.8, -2)
|
|
|
|
|
if boss is Node3D:
|
|
|
|
|
origin = (boss as Node3D).global_position + Vector3(0, 0.9, 0)
|
|
|
|
|
var q: Node3D = scene.instantiate()
|
|
|
|
|
add_child(q)
|
|
|
|
|
q.global_position = origin + Vector3(randf_range(-1.5, 1.5), 0, randf_range(-1, 1))
|
|
|
|
|
var dir := Vector3(0, 0, 1)
|
|
|
|
|
if player is Node3D:
|
|
|
|
|
dir = ((player as Node3D).global_position + Vector3(0, 0.8, 0) - q.global_position).normalized()
|
|
|
|
|
q.set("velocity", dir * 7.5)
|
2026-09-14 19:08:00 +02:00
|
|
|
|
|
|
|
|
func _on_boss_defeated() -> void:
|
|
|
|
|
_phase = Phase.DONE
|
|
|
|
|
Achievements.unlock("beat_hongkong")
|
|
|
|
|
GameState.set_flag("hongkong_defeated", true)
|
2026-09-14 18:36:02 +02:00
|
|
|
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_hongkong_boss", 3.0)
|
2026-09-14 19:08:00 +02:00
|
|
|
await get_tree().create_timer(2.5).timeout
|
2026-09-15 12:02:57 +02:00
|
|
|
ui.show_dialog_key("dialog_bear_end", 5.0)
|
2026-09-14 19:08:00 +02:00
|
|
|
await get_tree().create_timer(3.5).timeout
|
|
|
|
|
GameState.goto_scene("end_screen")
|