# World.gd
# ─────────────────────────────────────────────────────────────────────────────
# Spielwelt-Controller
#
# Verantwortlichkeiten:
#   • Overworld + Dungeon in einer Szene (kein Szenenwechsel)
#   • Prozeduraler Boden-Shader, Himmel
#   • Hauptmenü → Klassenauswahl → Spielinitialisierung
#   • Startausrüstung je nach Klasse
#   • Gegner-Setup, Loot-Weiterleitung, Respawn nach Tod
#   • Dungeon-Generierung und Portal-System
# ─────────────────────────────────────────────────────────────────────────────
extends Node3D

const ENEMY_SCENE = preload("res://enemy.tscn")
const MAIN_MENU = preload("res://main_menu.tscn")
const CLASS_SELECTION_MENU = preload("res://class_selection_menu.tscn")
const RESPAWN_TIME = 60.0

# Startausrüstung
const STARTER_SWORD = preload("res://equipment/iron_sword.tres")
const STARTER_STAFF = preload("res://equipment/wooden_staff.tres")
const STARTER_CHEST = preload("res://equipment/leather_chest.tres")
const STARTER_POTION = preload("res://consumables/small_hp_potion.tres")

# Loot Tables
const GOBLIN_LOOT = preload("res://loot_tables/goblin_loot.tres")

# Dungeon-Konstanten
const TILE_SIZE = 3.0
const WALL_HEIGHT = 6.0
const GRID_WIDTH = 60
const GRID_HEIGHT = 60
const MIN_ROOMS = 6
const MAX_ROOMS = 10
const MIN_ROOM_SIZE = 4
const MAX_ROOM_SIZE = 9

@onready var player = $Player
@onready var floor_mesh = $Boden/MeshInstance3D
@onready var gate_area = $DungeonGate/GateArea
@onready var gate_label = $DungeonGate/GateLabel

var in_dungeon: bool = false
var dungeon_level: int = 0  # 0 = Overworld, 1+ = Dungeon-Ebene
var overworld_nodes: Array = []  # Nodes die im Dungeon versteckt werden
var dungeon_container: Node3D = null
var dungeon_env: WorldEnvironment = null
var overworld_env: WorldEnvironment = null
var return_portal_area: Area3D = null
var return_portal_label: Label3D = null
var deeper_portal_area: Area3D = null
var deeper_portal_label: Label3D = null
var player_overworld_pos: Vector3 = Vector3.ZERO
var portal_choice_panel: PanelContainer = null
var overworld_env_resource: Environment = null

# Dungeon-Daten
var dungeon_grid: Array = []
var dungeon_rooms: Array = []

# Gespeicherte Dungeon-Ebenen (Level → {grid, rooms})
var saved_dungeons: Dictionary = {}

func _ready():
	_setup_floor_material()
	_setup_sky()
	# Hauptmenü anzeigen
	var main_menu = MAIN_MENU.instantiate()
	add_child(main_menu)
	main_menu.start_game.connect(_on_start_game)

func _setup_sky():
	var sky_mat = ProceduralSkyMaterial.new()
	sky_mat.sky_top_color        = Color(0.15, 0.35, 0.75)
	sky_mat.sky_horizon_color    = Color(0.55, 0.75, 1.0)
	sky_mat.ground_horizon_color = Color(0.35, 0.30, 0.25)
	sky_mat.ground_bottom_color  = Color(0.1,  0.1,  0.1)
	sky_mat.sun_angle_max        = 30.0
	sky_mat.sun_curve            = 0.15

	var sky = Sky.new()
	sky.sky_material = sky_mat

	var env = Environment.new()
	env.background_mode        = Environment.BG_SKY
	env.sky                    = sky
	env.ambient_light_source   = Environment.AMBIENT_SOURCE_SKY
	env.ambient_light_energy   = 0.6
	env.tonemap_mode           = Environment.TONE_MAPPER_FILMIC

	overworld_env = WorldEnvironment.new()
	overworld_env.name = "OverworldEnv"
	overworld_env.environment = env
	add_child(overworld_env)

func _setup_floor_material():
	var shader = Shader.new()
	shader.code = """
shader_type spatial;
uniform vec4 grass_color_a : source_color = vec4(0.18, 0.42, 0.12, 1.0);
uniform vec4 grass_color_b : source_color = vec4(0.22, 0.50, 0.15, 1.0);
uniform vec4 dirt_color : source_color = vec4(0.35, 0.25, 0.15, 1.0);
uniform float noise_scale : hint_range(0.01, 0.5) = 0.08;
uniform float dirt_threshold : hint_range(0.0, 1.0) = 0.72;

float hash(vec2 p) {
	return fract(sin(dot(p, vec2(127.1, 311.7))) * 43758.5453);
}

float noise(vec2 p) {
	vec2 i = floor(p);
	vec2 f = fract(p);
	f = f * f * (3.0 - 2.0 * f);
	float a = hash(i);
	float b = hash(i + vec2(1.0, 0.0));
	float c = hash(i + vec2(0.0, 1.0));
	float d = hash(i + vec2(1.0, 1.0));
	return mix(mix(a, b, f.x), mix(c, d, f.x), f.y);
}

void fragment() {
	vec3 world_pos = (INV_VIEW_MATRIX * vec4(VERTEX, 1.0)).xyz;
	vec2 uv = world_pos.xz * noise_scale;
	float n1 = noise(uv * 3.0);
	float n2 = noise(uv * 7.0 + vec2(50.0));
	float n3 = noise(uv * 15.0 + vec2(100.0));
	float combined = n1 * 0.5 + n2 * 0.35 + n3 * 0.15;
	vec3 grass = mix(grass_color_a.rgb, grass_color_b.rgb, n2);
	vec3 col = mix(grass, dirt_color.rgb, step(dirt_threshold, combined));
	ALBEDO = col;
	ROUGHNESS = 0.92;
	METALLIC = 0.0;
	NORMAL_MAP = vec3(n2 * 0.3, n3 * 0.3, 1.0);
}
"""
	var mat = ShaderMaterial.new()
	mat.shader = shader
	floor_mesh.material_override = mat

# ─────────────────────────────────────────────────────────────────────────────
# PROCESS
# ─────────────────────────────────────────────────────────────────────────────

func _process(_delta):
	if in_dungeon:
		_check_return_portal()
		_check_deeper_portal()
	else:
		_check_gate_proximity()

func _check_gate_proximity():
	if not player or not gate_area or not gate_label:
		return
	var dist = player.global_position.distance_to(gate_area.global_position)
	gate_label.visible = dist < 6.0
	if dist < 6.0 and Input.is_action_just_pressed("interact"):
		_enter_dungeon()

func _check_return_portal():
	if not player or not return_portal_area or not return_portal_label:
		return
	if portal_choice_panel and is_instance_valid(portal_choice_panel):
		return  # Menü ist offen
	var dist = player.global_position.distance_to(return_portal_area.global_position)
	return_portal_label.visible = dist < 6.0
	if dist < 6.0 and Input.is_action_just_pressed("interact"):
		if dungeon_level <= 1:
			_exit_dungeon()
		else:
			_show_portal_choice()

func _check_deeper_portal():
	if not player or not deeper_portal_area or not deeper_portal_label:
		return
	var dist = player.global_position.distance_to(deeper_portal_area.global_position)
	deeper_portal_label.visible = dist < 6.0
	if dist < 6.0 and Input.is_action_just_pressed("interact"):
		_go_deeper()

# ─────────────────────────────────────────────────────────────────────────────
# DUNGEON BETRETEN / VERLASSEN
# ─────────────────────────────────────────────────────────────────────────────

func _enter_dungeon():
	in_dungeon = true
	dungeon_level = 1
	player_overworld_pos = player.global_position

	# Overworld-Nodes verstecken
	_hide_overworld()

	# Ersten Dungeon generieren
	_generate_dungeon_level()

func _go_deeper():
	# Aktuelle Ebene speichern
	_save_current_dungeon()
	_clear_dungeon()
	dungeon_level += 1
	_generate_dungeon_level()

func _go_up():
	# Aktuelle Ebene speichern
	_save_current_dungeon()
	_clear_dungeon()
	dungeon_level -= 1
	_generate_dungeon_level()

func _save_current_dungeon():
	saved_dungeons[dungeon_level] = {
		"grid": dungeon_grid.duplicate(true),
		"rooms": dungeon_rooms.duplicate(true)
	}

func _show_portal_choice():
	get_tree().paused = true
	Input.set_mouse_mode(Input.MOUSE_MODE_VISIBLE)

	portal_choice_panel = PanelContainer.new()
	portal_choice_panel.process_mode = Node.PROCESS_MODE_ALWAYS

	var vbox = VBoxContainer.new()
	vbox.add_theme_constant_override("separation", 10)

	var title = Label.new()
	title.text = "Wohin möchtest du gehen?"
	title.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER
	vbox.add_child(title)

	var btn_up = Button.new()
	btn_up.text = "Eine Ebene höher (Ebene " + str(dungeon_level - 1) + ")"
	btn_up.pressed.connect(_on_portal_go_up)
	vbox.add_child(btn_up)

	var btn_out = Button.new()
	btn_out.text = "Zurück zur Oberwelt"
	btn_out.pressed.connect(_on_portal_exit)
	vbox.add_child(btn_out)

	var btn_cancel = Button.new()
	btn_cancel.text = "Abbrechen"
	btn_cancel.pressed.connect(_on_portal_cancel)
	vbox.add_child(btn_cancel)

	portal_choice_panel.add_child(vbox)
	player.hud.add_child(portal_choice_panel)
	portal_choice_panel.anchors_preset = Control.PRESET_CENTER
	portal_choice_panel.position -= portal_choice_panel.size / 2

func _on_portal_go_up():
	_close_portal_choice()
	_go_up()

func _on_portal_exit():
	_close_portal_choice()
	_exit_dungeon()

func _on_portal_cancel():
	_close_portal_choice()

func _close_portal_choice():
	if portal_choice_panel and is_instance_valid(portal_choice_panel):
		portal_choice_panel.queue_free()
		portal_choice_panel = null
	get_tree().paused = false

func _hide_overworld():
	overworld_nodes.clear()
	for child in get_children():
		if child == player:
			continue
		if child.name == "OverworldEnv":
			continue
		if child is Node3D or child is StaticBody3D:
			overworld_nodes.append(child)
			child.visible = false
			if child is StaticBody3D:
				child.process_mode = Node.PROCESS_MODE_DISABLED
	if overworld_env:
		overworld_env_resource = overworld_env.environment
		overworld_env.environment = null

func _generate_dungeon_level():
	dungeon_container = Node3D.new()
	dungeon_container.name = "DungeonContainer"
	add_child(dungeon_container)

	# Gespeichertes Level laden oder neu generieren
	if saved_dungeons.has(dungeon_level):
		dungeon_grid = saved_dungeons[dungeon_level]["grid"].duplicate(true)
		dungeon_rooms = saved_dungeons[dungeon_level]["rooms"].duplicate(true)
	else:
		_generate_dungeon()

	_build_dungeon_geometry()
	_build_dungeon_navmesh()

	# Edge-Connection-Margin erhöhen damit NavMesh-Zellen sich verbinden
	var map_rid = get_world_3d().navigation_map
	NavigationServer3D.map_set_edge_connection_margin(map_rid, 0.6)

	_place_return_portal()
	_place_deeper_portal()
	_setup_dungeon_lighting()
	_setup_dungeon_environment()

	# Spieler-Spawn: beim Hochgehen im letzten Raum, sonst im ersten
	var spawn_room: Rect2i
	if saved_dungeons.has(dungeon_level) and dungeon_level < _get_max_visited_level():
		# Von unten zurückgekommen → Spawn am Deeper-Portal (letzter Raum)
		spawn_room = dungeon_rooms[dungeon_rooms.size() - 1]
	else:
		# Neu betreten oder von oben → Spawn am Eingang (erster Raum)
		spawn_room = dungeon_rooms[0]

	var spawn = Vector3(
		(spawn_room.position.x + spawn_room.size.x / 2.0) * TILE_SIZE,
		0.5,
		(spawn_room.position.y + spawn_room.size.y / 2.0) * TILE_SIZE
	)
	player.global_position = spawn

	# Gegner im Dungeon spawnen
	_spawn_dungeon_enemies()

func _get_max_visited_level() -> int:
	var max_level = 0
	for key in saved_dungeons.keys():
		if key > max_level:
			max_level = key
	return max_level

func _clear_dungeon():
	if dungeon_container:
		dungeon_container.queue_free()
		dungeon_container = null
	if dungeon_env:
		dungeon_env.queue_free()
		dungeon_env = null
	return_portal_area = null
	return_portal_label = null
	deeper_portal_area = null
	deeper_portal_label = null
	dungeon_grid.clear()
	dungeon_rooms.clear()

func _exit_dungeon():
	in_dungeon = false
	dungeon_level = 0
	_clear_dungeon()
	saved_dungeons.clear()

	# Overworld-Nodes wieder zeigen
	for node in overworld_nodes:
		if is_instance_valid(node):
			node.visible = true
			if node is StaticBody3D:
				node.process_mode = Node.PROCESS_MODE_INHERIT
	overworld_nodes.clear()

	if overworld_env:
		overworld_env.environment = overworld_env_resource

	# Spieler zurück zur Overworld
	player.global_position = player_overworld_pos

# ─────────────────────────────────────────────────────────────────────────────
# DUNGEON-GENERIERUNG
# ─────────────────────────────────────────────────────────────────────────────

func _generate_dungeon():
	dungeon_grid.clear()
	dungeon_rooms.clear()

	for x in range(GRID_WIDTH):
		var col = []
		for y in range(GRID_HEIGHT):
			col.append(0)
		dungeon_grid.append(col)

	var attempts = 0
	var target_rooms = randi_range(MIN_ROOMS, MAX_ROOMS)
	while dungeon_rooms.size() < target_rooms and attempts < 200:
		attempts += 1
		var w = randi_range(MIN_ROOM_SIZE, MAX_ROOM_SIZE)
		var h = randi_range(MIN_ROOM_SIZE, MAX_ROOM_SIZE)
		var x = randi_range(2, GRID_WIDTH - w - 2)
		var y = randi_range(2, GRID_HEIGHT - h - 2)
		var new_room = Rect2i(x, y, w, h)
		if _dungeon_room_fits(new_room):
			dungeon_rooms.append(new_room)
			_carve_room(new_room)

	for i in range(dungeon_rooms.size() - 1):
		_carve_corridor(dungeon_rooms[i], dungeon_rooms[i + 1])

func _dungeon_room_fits(new_room: Rect2i) -> bool:
	var expanded = Rect2i(new_room.position - Vector2i(2, 2), new_room.size + Vector2i(4, 4))
	for room in dungeon_rooms:
		if expanded.intersects(room):
			return false
	return true

func _carve_room(room: Rect2i):
	for x in range(room.position.x, room.position.x + room.size.x):
		for y in range(room.position.y, room.position.y + room.size.y):
			dungeon_grid[x][y] = 1

func _carve_corridor(room_a: Rect2i, room_b: Rect2i):
	var ax = room_a.position.x + room_a.size.x / 2
	var ay = room_a.position.y + room_a.size.y / 2
	var bx = room_b.position.x + room_b.size.x / 2
	var by = room_b.position.y + room_b.size.y / 2

	var corridor_width = 2
	var start_x = min(ax, bx)
	var end_x = max(ax, bx)
	for x in range(start_x, end_x + 1):
		for w in range(corridor_width):
			if x >= 0 and x < GRID_WIDTH and ay + w >= 0 and ay + w < GRID_HEIGHT:
				dungeon_grid[x][ay + w] = 1

	var start_y = min(ay, by)
	var end_y = max(ay, by)
	for y in range(start_y, end_y + 1):
		for w in range(corridor_width):
			if bx + w >= 0 and bx + w < GRID_WIDTH and y >= 0 and y < GRID_HEIGHT:
				dungeon_grid[bx + w][y] = 1

func _build_dungeon_geometry():
	var floor_mat = StandardMaterial3D.new()
	floor_mat.albedo_color = Color(0.25, 0.22, 0.2)
	floor_mat.roughness = 0.9

	var wall_mat = StandardMaterial3D.new()
	wall_mat.albedo_color = Color(0.35, 0.3, 0.28)
	wall_mat.roughness = 0.95

	var ceiling_mat = StandardMaterial3D.new()
	ceiling_mat.albedo_color = Color(0.2, 0.18, 0.16)
	ceiling_mat.roughness = 0.95

	for x in range(GRID_WIDTH):
		for y in range(GRID_HEIGHT):
			if dungeon_grid[x][y] == 1:
				# Boden
				var floor_tile = CSGBox3D.new()
				floor_tile.size = Vector3(TILE_SIZE, 0.3, TILE_SIZE)
				floor_tile.position = Vector3(x * TILE_SIZE, -0.15, y * TILE_SIZE)
				floor_tile.material = floor_mat
				floor_tile.use_collision = true
				dungeon_container.add_child(floor_tile)

				# Decke
				var ceil_tile = CSGBox3D.new()
				ceil_tile.size = Vector3(TILE_SIZE, 0.3, TILE_SIZE)
				ceil_tile.position = Vector3(x * TILE_SIZE, WALL_HEIGHT + 0.15, y * TILE_SIZE)
				ceil_tile.material = ceiling_mat
				dungeon_container.add_child(ceil_tile)

				# Wände
				var directions = [
					Vector2i(-1, 0), Vector2i(1, 0),
					Vector2i(0, -1), Vector2i(0, 1)
				]
				for dir in directions:
					var nx = x + dir.x
					var ny = y + dir.y
					if nx < 0 or nx >= GRID_WIDTH or ny < 0 or ny >= GRID_HEIGHT or dungeon_grid[nx][ny] == 0:
						var wall = CSGBox3D.new()
						if dir.x != 0:
							wall.size = Vector3(0.3, WALL_HEIGHT, TILE_SIZE)
							wall.position = Vector3(
								x * TILE_SIZE + dir.x * TILE_SIZE / 2.0,
								WALL_HEIGHT / 2.0,
								y * TILE_SIZE
							)
						else:
							wall.size = Vector3(TILE_SIZE, WALL_HEIGHT, 0.3)
							wall.position = Vector3(
								x * TILE_SIZE,
								WALL_HEIGHT / 2.0,
								y * TILE_SIZE + dir.y * TILE_SIZE / 2.0
							)
						wall.material = wall_mat
						wall.use_collision = true
						dungeon_container.add_child(wall)

func _place_return_portal():
	var first = dungeon_rooms[0]
	var portal_pos = Vector3(
		(first.position.x + 1.0) * TILE_SIZE,
		0,
		(first.position.y + 1.0) * TILE_SIZE
	)

	var portal = Node3D.new()
	portal.name = "ReturnPortal"
	portal.position = portal_pos
	dungeon_container.add_child(portal)

	var portal_mat = StandardMaterial3D.new()
	portal_mat.albedo_color = Color(0.2, 0.4, 1.0)
	portal_mat.emission_enabled = true
	portal_mat.emission = Color(0.2, 0.4, 1.0)
	portal_mat.emission_energy_multiplier = 2.0

	# Portal-Ring
	var frame = CSGTorus3D.new()
	frame.inner_radius = 1.2
	frame.outer_radius = 1.6
	frame.ring_sides = 16
	frame.sides = 24
	frame.position = Vector3(0, 2.0, 0)
	frame.material = portal_mat
	portal.add_child(frame)

	# Portal-Fläche
	var surface = CSGCylinder3D.new()
	surface.radius = 1.2
	surface.height = 0.1
	surface.sides = 24
	surface.position = Vector3(0, 2.0, 0)
	surface.material = portal_mat
	portal.add_child(surface)

	# Trigger
	return_portal_area = Area3D.new()
	return_portal_area.name = "PortalArea"
	return_portal_area.position = Vector3(0, 1.5, 0)
	portal.add_child(return_portal_area)

	var col = CollisionShape3D.new()
	var shape = BoxShape3D.new()
	shape.size = Vector3(4, 4, 4)
	col.shape = shape
	return_portal_area.add_child(col)

	# Label
	return_portal_label = Label3D.new()
	return_portal_label.text = "Zurück zur Oberwelt [E]"
	return_portal_label.position = Vector3(0, 4.5, 0)
	return_portal_label.font_size = 48
	return_portal_label.modulate = Color(0.3, 0.6, 1.0)
	return_portal_label.billboard = BaseMaterial3D.BILLBOARD_ENABLED
	return_portal_label.visible = false
	portal.add_child(return_portal_label)

func _place_deeper_portal():
	var last = dungeon_rooms[dungeon_rooms.size() - 1]
	var portal_pos = Vector3(
		(last.position.x + last.size.x / 2.0) * TILE_SIZE,
		0,
		(last.position.y + last.size.y / 2.0) * TILE_SIZE
	)

	var portal = Node3D.new()
	portal.name = "DeeperPortal"
	portal.position = portal_pos
	dungeon_container.add_child(portal)

	var portal_mat = StandardMaterial3D.new()
	portal_mat.albedo_color = Color(1.0, 0.3, 0.1)
	portal_mat.emission_enabled = true
	portal_mat.emission = Color(1.0, 0.3, 0.1)
	portal_mat.emission_energy_multiplier = 2.0

	# Portal-Ring (rot/orange)
	var frame = CSGTorus3D.new()
	frame.inner_radius = 1.2
	frame.outer_radius = 1.6
	frame.ring_sides = 16
	frame.sides = 24
	frame.position = Vector3(0, 2.0, 0)
	frame.material = portal_mat
	portal.add_child(frame)

	# Portal-Fläche
	var surface = CSGCylinder3D.new()
	surface.radius = 1.2
	surface.height = 0.1
	surface.sides = 24
	surface.position = Vector3(0, 2.0, 0)
	surface.material = portal_mat
	portal.add_child(surface)

	# Trigger
	deeper_portal_area = Area3D.new()
	deeper_portal_area.name = "DeeperPortalArea"
	deeper_portal_area.position = Vector3(0, 1.5, 0)
	portal.add_child(deeper_portal_area)

	var col = CollisionShape3D.new()
	var shape = BoxShape3D.new()
	shape.size = Vector3(4, 4, 4)
	col.shape = shape
	deeper_portal_area.add_child(col)

	# Label
	deeper_portal_label = Label3D.new()
	deeper_portal_label.text = "Ebene " + str(dungeon_level + 1) + " betreten [E]"
	deeper_portal_label.position = Vector3(0, 4.5, 0)
	deeper_portal_label.font_size = 48
	deeper_portal_label.modulate = Color(1.0, 0.5, 0.2)
	deeper_portal_label.billboard = BaseMaterial3D.BILLBOARD_ENABLED
	deeper_portal_label.visible = false
	portal.add_child(deeper_portal_label)

func _setup_dungeon_lighting():
	# Licht in jedem Raum
	for room in dungeon_rooms:
		var center = Vector3(
			(room.position.x + room.size.x / 2.0) * TILE_SIZE,
			WALL_HEIGHT - 1.0,
			(room.position.y + room.size.y / 2.0) * TILE_SIZE
		)
		var light = OmniLight3D.new()
		light.position = center
		light.omni_range = 15.0
		light.light_energy = 1.2
		light.light_color = Color(1.0, 0.85, 0.6)
		light.shadow_enabled = true
		dungeon_container.add_child(light)

	# Korridor-Beleuchtung
	for i in range(dungeon_rooms.size() - 1):
		var room_a = dungeon_rooms[i]
		var room_b = dungeon_rooms[i + 1]
		var ax = (room_a.position.x + room_a.size.x / 2.0) * TILE_SIZE
		var ay = (room_a.position.y + room_a.size.y / 2.0) * TILE_SIZE
		var bx = (room_b.position.x + room_b.size.x / 2.0) * TILE_SIZE
		var by = (room_b.position.y + room_b.size.y / 2.0) * TILE_SIZE

		# Licht am L-Knick
		var corner_light = OmniLight3D.new()
		corner_light.position = Vector3(bx, WALL_HEIGHT - 1.0, ay)
		corner_light.omni_range = 12.0
		corner_light.light_energy = 1.0
		corner_light.light_color = Color(1.0, 0.8, 0.5)
		dungeon_container.add_child(corner_light)

		# Mitte horizontaler Gang
		var mid_h = OmniLight3D.new()
		mid_h.position = Vector3((ax + bx) / 2.0, WALL_HEIGHT - 1.0, ay)
		mid_h.omni_range = 10.0
		mid_h.light_energy = 0.8
		mid_h.light_color = Color(1.0, 0.8, 0.5)
		dungeon_container.add_child(mid_h)

		# Mitte vertikaler Gang
		var mid_v = OmniLight3D.new()
		mid_v.position = Vector3(bx, WALL_HEIGHT - 1.0, (ay + by) / 2.0)
		mid_v.omni_range = 10.0
		mid_v.light_energy = 0.8
		mid_v.light_color = Color(1.0, 0.8, 0.5)
		dungeon_container.add_child(mid_v)

func _setup_dungeon_environment():
	var env = Environment.new()
	env.background_mode = Environment.BG_COLOR
	env.background_color = Color(0.02, 0.02, 0.04)
	env.ambient_light_source = Environment.AMBIENT_SOURCE_COLOR
	env.ambient_light_color = Color(0.08, 0.07, 0.1)
	env.ambient_light_energy = 0.3
	env.tonemap_mode = Environment.TONE_MAPPER_FILMIC
	env.fog_enabled = true
	env.fog_light_color = Color(0.05, 0.05, 0.08)
	env.fog_density = 0.02

	dungeon_env = WorldEnvironment.new()
	dungeon_env.name = "DungeonEnv"
	dungeon_env.environment = env
	add_child(dungeon_env)

func _build_dungeon_navmesh():
	var nav_region = NavigationRegion3D.new()
	nav_region.name = "DungeonNavRegion"
	var nav_mesh = NavigationMesh.new()

	var half = TILE_SIZE / 2.0
	var margin = 0.5  # Abstand zu Wänden

	# Positions-basierte Vertex-Deduplizierung:
	# Gleiche Position → gleicher Index → NavMesh ist automatisch zusammenhängend
	var pos_to_idx: Dictionary = {}  # "x_z" String → vertex index
	var vertices = PackedVector3Array()
	var cell_indices: Array = []  # [{i0, i1, i2, i3}, ...]

	for x in range(GRID_WIDTH):
		for y in range(GRID_HEIGHT):
			if dungeon_grid[x][y] != 1:
				continue

			var wall_left  = (x <= 0 or dungeon_grid[x - 1][y] == 0)
			var wall_right = (x >= GRID_WIDTH - 1 or dungeon_grid[x + 1][y] == 0)
			var wall_top   = (y <= 0 or dungeon_grid[x][y - 1] == 0)
			var wall_bot   = (y >= GRID_HEIGHT - 1 or dungeon_grid[x][y + 1] == 0)

			var lx = x * TILE_SIZE - half + (margin if wall_left else 0.0)
			var rx = (x + 1) * TILE_SIZE - half - (margin if wall_right else 0.0)
			var tz = y * TILE_SIZE - half + (margin if wall_top else 0.0)
			var bz = (y + 1) * TILE_SIZE - half - (margin if wall_bot else 0.0)

			var corners = [
				Vector3(lx, 0.0, tz), Vector3(rx, 0.0, tz),
				Vector3(rx, 0.0, bz), Vector3(lx, 0.0, bz)
			]

			var indices = PackedInt32Array()
			for c in corners:
				var key = "%0.4f_%0.4f" % [c.x, c.z]
				if not pos_to_idx.has(key):
					pos_to_idx[key] = vertices.size()
					vertices.append(c)
				indices.append(pos_to_idx[key])
			cell_indices.append(indices)

	nav_mesh.vertices = vertices
	for ci in cell_indices:
		nav_mesh.add_polygon(ci)

	nav_region.navigation_mesh = nav_mesh
	dungeon_container.add_child(nav_region)

func _spawn_dungeon_enemies():
	for i in range(1, dungeon_rooms.size()):
		# Letzter Raum = Deeper-Portal, keine Gegner
		if i == dungeon_rooms.size() - 1:
			continue
		var room = dungeon_rooms[i]
		var center = Vector3(
			(room.position.x + room.size.x / 2.0) * TILE_SIZE,
			0.5,
			(room.position.y + room.size.y / 2.0) * TILE_SIZE
		)
		var enemy = ENEMY_SCENE.instantiate()
		dungeon_container.add_child(enemy)
		enemy.global_position = center
		# Gegner-Level = Dungeon-Ebene
		enemy.mob_level = dungeon_level
		_setup_enemy(enemy)

# ─────────────────────────────────────────────────────────────────────────────
# MENÜ & SPIELER-SETUP
# ─────────────────────────────────────────────────────────────────────────────

# Nach Hauptmenü: Klassenauswahl anzeigen
func _on_start_game():
	var menu = CLASS_SELECTION_MENU.instantiate()
	add_child(menu)
	menu.class_selected.connect(_on_class_selected)
	get_tree().paused = true

# Klasse ausgewählt: Spieler initialisieren
func _on_class_selected(character_class: CharacterClass):
	player.character_class = character_class

	# Skills klassenabhängig aufbauen
	player._init_class_skills()
	for i in range(9):
		player._refresh_action_slot(i)

	# Startausrüstung klassenabhängig
	if character_class.resource_type == CharacterClass.ResourceType.MANA:
		player.equip_item(STARTER_STAFF)
	else:
		player.equip_item(STARTER_SWORD)
	player.equip_item(STARTER_CHEST)

	player._calculate_stats()
	player.current_hp = player.max_hp
	var potion = STARTER_POTION.duplicate()
	potion.stack_size = 3
	player.inventory.add_item(potion)
	player.assign_to_action_bar(2, potion)
	player.current_resource = player.max_resource
	player.hud.update_health(player.current_hp, player.max_hp)
	player.hud.update_resource(player.current_resource, player.max_resource, player.get_resource_name())
	print("Klasse gewählt: ", character_class.class_name_de)

	# Jetzt Gegner initialisieren
	for child in get_children():
		if child.has_method("take_damage") and child != player:
			_setup_enemy(child)

# ─────────────────────────────────────────────────────────────────────────────
# GEGNER-SYSTEM
# ─────────────────────────────────────────────────────────────────────────────

func _setup_enemy(enemy):
	if enemy and player:
		enemy.target = player
		if enemy.loot_table == null:
			enemy.loot_table = GOBLIN_LOOT
		if not enemy.enemy_died.is_connected(_on_enemy_died):
			enemy.enemy_died.connect(_on_enemy_died)
		if not enemy.enemy_dropped_loot.is_connected(_on_enemy_dropped_loot):
			enemy.enemy_dropped_loot.connect(_on_enemy_dropped_loot)

func _on_enemy_dropped_loot(loot: Dictionary, world_pos: Vector3):
	if player:
		player.receive_loot(loot, world_pos)

func _on_enemy_died(spawn_position: Vector3, xp_reward: int):
	if player:
		player.gain_xp(xp_reward)
	print("Respawn in ", RESPAWN_TIME, " Sekunden...")
	await get_tree().create_timer(RESPAWN_TIME).timeout
	if in_dungeon and dungeon_container:
		var new_enemy = ENEMY_SCENE.instantiate()
		dungeon_container.add_child(new_enemy)
		new_enemy.global_position = spawn_position
		_setup_enemy(new_enemy)
	elif not in_dungeon:
		_spawn_enemy(spawn_position)

func _spawn_enemy(position: Vector3):
	var new_enemy = ENEMY_SCENE.instantiate()
	add_child(new_enemy)
	new_enemy.global_position = position
	_setup_enemy(new_enemy)
