update!
This commit is contained in:
@@ -3,6 +3,7 @@ extends Node
|
||||
const ROOM_HEIGHT: float = 4.0
|
||||
const WALL_THICKNESS: float = 0.4
|
||||
const CORRIDOR_WIDTH: float = 4.0
|
||||
const DOOR_WIDTH: float = 4.5
|
||||
|
||||
var rng: RandomNumberGenerator
|
||||
var rooms: Array = []
|
||||
@@ -17,7 +18,7 @@ func generate(parent: Node3D, seed: int, scale_difficulty: float = 1.0) -> Dicti
|
||||
for i in range(room_count):
|
||||
var w: float = rng.randf_range(8.0, 14.0)
|
||||
var d: float = rng.randf_range(8.0, 14.0)
|
||||
rooms.append({"pos": pos, "size": Vector3(w, ROOM_HEIGHT, d)})
|
||||
rooms.append({"pos": pos, "size": Vector3(w, ROOM_HEIGHT, d), "openings": []})
|
||||
if i == room_count - 1:
|
||||
break
|
||||
var corridor_len: float = rng.randf_range(4.0, 8.0)
|
||||
@@ -26,23 +27,61 @@ func generate(parent: Node3D, seed: int, scale_difficulty: float = 1.0) -> Dicti
|
||||
if rng.randf() < 0.5:
|
||||
var rotate_left: bool = rng.randf() < 0.5
|
||||
dir = dir.rotated(Vector3.UP, PI * 0.5 * (1 if rotate_left else -1))
|
||||
_compute_openings()
|
||||
_build_geometry(parent)
|
||||
return {"rooms": rooms, "spawn": rooms[0].pos + Vector3(0, 1, 0), "boss": rooms[-1].pos}
|
||||
|
||||
func _compute_openings() -> void:
|
||||
for i in range(rooms.size() - 1):
|
||||
var rel: Vector3 = rooms[i + 1].pos - rooms[i].pos
|
||||
if abs(rel.x) > abs(rel.z):
|
||||
if rel.x > 0:
|
||||
rooms[i].openings.append("east")
|
||||
rooms[i + 1].openings.append("west")
|
||||
else:
|
||||
rooms[i].openings.append("west")
|
||||
rooms[i + 1].openings.append("east")
|
||||
else:
|
||||
if rel.z > 0:
|
||||
rooms[i].openings.append("south")
|
||||
rooms[i + 1].openings.append("north")
|
||||
else:
|
||||
rooms[i].openings.append("north")
|
||||
rooms[i + 1].openings.append("south")
|
||||
|
||||
func _build_geometry(parent: Node3D) -> void:
|
||||
for r in rooms:
|
||||
_build_room(parent, r.pos, r.size)
|
||||
_build_room(parent, r.pos, r.size, r.openings)
|
||||
for i in range(rooms.size() - 1):
|
||||
_build_corridor(parent, rooms[i].pos, rooms[i + 1].pos)
|
||||
|
||||
func _build_room(parent: Node3D, center: Vector3, size: Vector3) -> void:
|
||||
func _build_room(parent: Node3D, center: Vector3, size: Vector3, openings: Array) -> void:
|
||||
_add_floor(parent, center, Vector2(size.x, size.z))
|
||||
var hw: float = size.x * 0.5
|
||||
var hd: float = size.z * 0.5
|
||||
_add_wall(parent, center + Vector3(0, ROOM_HEIGHT * 0.5, -hd), Vector3(size.x, ROOM_HEIGHT, WALL_THICKNESS))
|
||||
_add_wall(parent, center + Vector3(0, ROOM_HEIGHT * 0.5, hd), Vector3(size.x, ROOM_HEIGHT, WALL_THICKNESS))
|
||||
_add_wall(parent, center + Vector3(-hw, ROOM_HEIGHT * 0.5, 0), Vector3(WALL_THICKNESS, ROOM_HEIGHT, size.z))
|
||||
_add_wall(parent, center + Vector3(hw, ROOM_HEIGHT * 0.5, 0), Vector3(WALL_THICKNESS, ROOM_HEIGHT, size.z))
|
||||
_add_wall_with_opening(parent, center + Vector3(0, ROOM_HEIGHT * 0.5, -hd), Vector3(size.x, ROOM_HEIGHT, WALL_THICKNESS), "north", openings, true)
|
||||
_add_wall_with_opening(parent, center + Vector3(0, ROOM_HEIGHT * 0.5, hd), Vector3(size.x, ROOM_HEIGHT, WALL_THICKNESS), "south", openings, true)
|
||||
_add_wall_with_opening(parent, center + Vector3(-hw, ROOM_HEIGHT * 0.5, 0), Vector3(WALL_THICKNESS, ROOM_HEIGHT, size.z), "west", openings, false)
|
||||
_add_wall_with_opening(parent, center + Vector3(hw, ROOM_HEIGHT * 0.5, 0), Vector3(WALL_THICKNESS, ROOM_HEIGHT, size.z), "east", openings, false)
|
||||
|
||||
func _add_wall_with_opening(parent: Node3D, center: Vector3, size: Vector3, side: String, openings: Array, axis_x: bool) -> void:
|
||||
if not openings.has(side):
|
||||
_add_wall(parent, center, size)
|
||||
return
|
||||
if axis_x:
|
||||
var seg_len: float = (size.x - DOOR_WIDTH) * 0.5
|
||||
if seg_len <= 0.1:
|
||||
return
|
||||
var seg_offset: float = DOOR_WIDTH * 0.5 + seg_len * 0.5
|
||||
_add_wall(parent, center + Vector3(-seg_offset, 0, 0), Vector3(seg_len, size.y, size.z))
|
||||
_add_wall(parent, center + Vector3(seg_offset, 0, 0), Vector3(seg_len, size.y, size.z))
|
||||
else:
|
||||
var seg_len: float = (size.z - DOOR_WIDTH) * 0.5
|
||||
if seg_len <= 0.1:
|
||||
return
|
||||
var seg_offset: float = DOOR_WIDTH * 0.5 + seg_len * 0.5
|
||||
_add_wall(parent, center + Vector3(0, 0, -seg_offset), Vector3(size.x, size.y, seg_len))
|
||||
_add_wall(parent, center + Vector3(0, 0, seg_offset), Vector3(size.x, size.y, seg_len))
|
||||
|
||||
func _build_corridor(parent: Node3D, from: Vector3, to: Vector3) -> void:
|
||||
var mid: Vector3 = (from + to) * 0.5
|
||||
|
||||
@@ -2,6 +2,7 @@ extends Node3D
|
||||
|
||||
const PLAYER_SCENE: PackedScene = preload("res://scenes/entities/player/player.tscn")
|
||||
const ENEMY_SCENE: PackedScene = preload("res://scenes/entities/enemy/enemy.tscn")
|
||||
const PORTAL_SCENE: PackedScene = preload("res://scenes/entities/portal/portal.tscn")
|
||||
const GENERATOR: GDScript = preload("res://scenes/dungeon/dungeon_generator.gd")
|
||||
|
||||
@onready var players_root: Node3D = $EntityRoot/Players
|
||||
@@ -45,9 +46,9 @@ func _populate_dungeon() -> void:
|
||||
var room: Dictionary = data.rooms[i]
|
||||
var n: int = 2 + (1 if GameState.dungeon_red else 0)
|
||||
for j in range(n):
|
||||
var off := Vector3(randf_range(-room.size.x * 0.3, room.size.x * 0.3), 0.5, randf_range(-room.size.z * 0.3, room.size.z * 0.3))
|
||||
var off := Vector3(randf_range(-room.size.x * 0.3, room.size.x * 0.3), 1.0, randf_range(-room.size.z * 0.3, room.size.z * 0.3))
|
||||
spawn_system.spawn_enemy_at(room.pos + off, GameState.dungeon_red, difficulty * 0.5)
|
||||
spawn_system.spawn_boss_at(data.boss + Vector3(0, 0.5, 0), difficulty)
|
||||
spawn_system.spawn_boss_at(data.boss + Vector3(0, 2.0, 0), difficulty)
|
||||
|
||||
func _spawn_player(peer_id: int) -> void:
|
||||
if players_root.get_node_or_null(str(peer_id)) != null:
|
||||
@@ -70,13 +71,21 @@ func _on_peer_disconnected(id: int) -> void:
|
||||
if node:
|
||||
node.queue_free()
|
||||
|
||||
func _on_boss_defeated(_b: Node) -> void:
|
||||
if Net.is_host():
|
||||
var t := get_tree().create_timer(2.0)
|
||||
t.timeout.connect(func():
|
||||
GameState.dungeon_seed = 0
|
||||
_return.rpc())
|
||||
func _on_boss_defeated(b: Node) -> void:
|
||||
if not Net.is_host():
|
||||
return
|
||||
var portal: StaticBody3D = PORTAL_SCENE.instantiate()
|
||||
portal.is_return = true
|
||||
portal.name = "ReturnPortal"
|
||||
var portals_root: Node3D = $EntityRoot/Portals
|
||||
portals_root.add_child(portal, true)
|
||||
var spawn_pos: Vector3 = (b as Node3D).global_position if b is Node3D else data.boss
|
||||
portal.global_position = spawn_pos + Vector3(0, 1, 0)
|
||||
EventBus.portal_spawned.emit(portal)
|
||||
var t := get_tree().create_timer(3.0)
|
||||
t.timeout.connect(_auto_return.bind(portal))
|
||||
|
||||
@rpc("authority", "reliable", "call_local")
|
||||
func _return() -> void:
|
||||
func _auto_return(portal: Node) -> void:
|
||||
if is_instance_valid(portal):
|
||||
portal.queue_free()
|
||||
GameState.change_scene(GameState.SCENE_WORLD)
|
||||
|
||||
@@ -11,6 +11,7 @@ extends StaticBody3D
|
||||
var spawn_timer: float = 0.0
|
||||
var spawned_count: int = 0
|
||||
var dead: bool = false
|
||||
var attacked: bool = false
|
||||
|
||||
func _enter_tree() -> void:
|
||||
set_multiplayer_authority(1)
|
||||
@@ -51,6 +52,8 @@ func _physics_process(delta: float) -> void:
|
||||
return
|
||||
if not multiplayer.is_server() and multiplayer.multiplayer_peer != null:
|
||||
return
|
||||
if not attacked:
|
||||
return
|
||||
spawn_timer = max(0.0, spawn_timer - delta)
|
||||
if spawn_timer <= 0.0 and spawned_count < int(Stats.get_stat(self, "spawn_count", 5)):
|
||||
var spawn_sys: Node = get_node_or_null("/root/World/Systems/SpawnSystem")
|
||||
@@ -62,6 +65,8 @@ func _physics_process(delta: float) -> void:
|
||||
func _on_health_changed(entity: Node, current: float, max: float) -> void:
|
||||
if entity != self:
|
||||
return
|
||||
if not attacked and current < max:
|
||||
attacked = true
|
||||
var ratio: float = clamp(current / max if max > 0 else 0.0, 0.0, 1.0)
|
||||
healthbar.scale.x = max(0.01, ratio * 2.0)
|
||||
|
||||
|
||||
@@ -37,7 +37,7 @@ func _ready() -> void:
|
||||
func _process(_delta: float) -> void:
|
||||
if nearby_player and nearby_player.is_multiplayer_authority():
|
||||
prompt.visible = true
|
||||
if Input.is_action_just_pressed("interact"):
|
||||
if Input.is_action_just_pressed("interact") and not nearby_player.ui_capturing:
|
||||
EventBus.dialog_opened.emit(nearby_player, self)
|
||||
else:
|
||||
prompt.visible = false
|
||||
|
||||
@@ -35,6 +35,7 @@ func _ready() -> void:
|
||||
if stats_resource == null:
|
||||
stats_resource = PlayerStats.new()
|
||||
Stats.register(self, stats_resource)
|
||||
Stats.restore_player(peer_id, self)
|
||||
Stats.set_stat(self, "role", role)
|
||||
name_label.text = Net.player_names.get(peer_id, "P%d" % peer_id)
|
||||
EventBus.entity_died.connect(_on_entity_died_clear_target)
|
||||
@@ -63,6 +64,7 @@ func _request_target(path_str: String) -> void:
|
||||
current_target = t
|
||||
|
||||
func _exit_tree() -> void:
|
||||
Stats.cache_player(peer_id, self)
|
||||
Stats.deregister(self)
|
||||
|
||||
func _input(event: InputEvent) -> void:
|
||||
@@ -85,6 +87,8 @@ func _unhandled_input(event: InputEvent) -> void:
|
||||
return
|
||||
if build_mode:
|
||||
return
|
||||
if ui_capturing:
|
||||
return
|
||||
if event.is_action_pressed("class_tank"):
|
||||
_request_role(GameState.ROLE_TANK)
|
||||
elif event.is_action_pressed("class_damage"):
|
||||
|
||||
@@ -2,6 +2,7 @@ extends StaticBody3D
|
||||
|
||||
@export var stats_resource: PortalStats
|
||||
@export var is_red: bool = false
|
||||
@export var is_return: bool = false
|
||||
|
||||
@onready var mesh: MeshInstance3D = $Mesh
|
||||
@onready var name_label: Label3D = $NameLabel
|
||||
@@ -22,7 +23,15 @@ func _ready() -> void:
|
||||
stats_resource.is_red = is_red
|
||||
Stats.register(self, stats_resource)
|
||||
enter_area.body_entered.connect(_on_body_entered)
|
||||
if is_red:
|
||||
if is_return:
|
||||
var mat: StandardMaterial3D = StandardMaterial3D.new()
|
||||
mat.albedo_color = Color(1.0, 0.85, 0.3)
|
||||
mat.emission_enabled = true
|
||||
mat.emission = Color(1.0, 0.8, 0.2)
|
||||
mat.emission_energy_multiplier = 1.2
|
||||
mesh.material_override = mat
|
||||
name_label.text = "Zurück"
|
||||
elif is_red:
|
||||
var mat: StandardMaterial3D = StandardMaterial3D.new()
|
||||
mat.albedo_color = Color(0.95, 0.2, 0.2)
|
||||
mat.emission_enabled = true
|
||||
@@ -45,7 +54,10 @@ func _on_body_entered(body: Node) -> void:
|
||||
return
|
||||
triggered = true
|
||||
EventBus.portal_entered.emit(self, body)
|
||||
_request_enter.rpc_id(1, is_red, global_position)
|
||||
if is_return:
|
||||
_request_return.rpc_id(1)
|
||||
else:
|
||||
_request_enter.rpc_id(1, is_red, global_position)
|
||||
|
||||
@rpc("any_peer", "reliable", "call_local")
|
||||
func _request_enter(red: bool, return_pos: Vector3) -> void:
|
||||
@@ -60,3 +72,13 @@ func _do_enter(seed: int, red: bool, return_pos: Vector3) -> void:
|
||||
GameState.dungeon_red = red
|
||||
GameState.portal_return_position = return_pos
|
||||
GameState.change_scene(GameState.SCENE_DUNGEON)
|
||||
|
||||
@rpc("any_peer", "reliable", "call_local")
|
||||
func _request_return() -> void:
|
||||
if not multiplayer.is_server() and multiplayer.multiplayer_peer != null:
|
||||
return
|
||||
_do_return.rpc()
|
||||
|
||||
@rpc("authority", "reliable", "call_local")
|
||||
func _do_return() -> void:
|
||||
GameState.change_scene(GameState.SCENE_WORLD)
|
||||
|
||||
@@ -84,7 +84,19 @@ func _process(_delta: float) -> void:
|
||||
_update_build_preview()
|
||||
_update_minimap()
|
||||
|
||||
func _is_typing() -> bool:
|
||||
var f := get_viewport().gui_get_focus_owner()
|
||||
return f is LineEdit or f is TextEdit
|
||||
|
||||
func _unhandled_input(event: InputEvent) -> void:
|
||||
if _is_typing():
|
||||
if event.is_action_pressed("pause"):
|
||||
var f := get_viewport().gui_get_focus_owner()
|
||||
if f is LineEdit:
|
||||
(f as LineEdit).release_focus()
|
||||
_capture_ui(_any_panel_visible())
|
||||
get_viewport().set_input_as_handled()
|
||||
return
|
||||
if event.is_action_pressed("inventory"):
|
||||
_toggle_panel(inventory_panel)
|
||||
_refresh_inventory()
|
||||
|
||||
@@ -180,9 +180,10 @@ layout_mode = 2
|
||||
unique_name_in_owner = true
|
||||
custom_minimum_size = Vector2(0, 140)
|
||||
layout_mode = 2
|
||||
size_flags_vertical = 3
|
||||
bbcode_enabled = true
|
||||
scroll_following = true
|
||||
fit_content = true
|
||||
scroll_active = true
|
||||
|
||||
[node name="ChatInput" type="LineEdit" parent="ChatPanel/VBox"]
|
||||
unique_name_in_owner = true
|
||||
@@ -309,9 +310,10 @@ theme_override_font_sizes/font_size = 22
|
||||
unique_name_in_owner = true
|
||||
custom_minimum_size = Vector2(0, 280)
|
||||
layout_mode = 2
|
||||
size_flags_vertical = 3
|
||||
bbcode_enabled = true
|
||||
scroll_following = true
|
||||
fit_content = true
|
||||
scroll_active = true
|
||||
|
||||
[node name="DialogInput" type="LineEdit" parent="DialogPanel/VBox"]
|
||||
unique_name_in_owner = true
|
||||
|
||||
Reference in New Issue
Block a user