prototype vibe
This commit is contained in:
@@ -5,6 +5,9 @@
|
||||
[ext_resource type="PackedScene" path="res://scenes/enemy/enemy.tscn" id="enemy"]
|
||||
[ext_resource type="Resource" path="res://scenes/enemy/boss_stats.tres" id="boss_stats"]
|
||||
[ext_resource type="Script" path="res://systems/dungeon_system.gd" id="dungeon_system"]
|
||||
[ext_resource type="Script" path="res://scenes/dungeon/dungeon_manager.gd" id="dungeon_manager"]
|
||||
[ext_resource type="Script" path="res://systems/audio_system.gd" id="audio_system"]
|
||||
[ext_resource type="Script" path="res://systems/xp_system.gd" id="xp_system"]
|
||||
[ext_resource type="PackedScene" path="res://scenes/portal/gate.tscn" id="gate"]
|
||||
[ext_resource type="Script" path="res://systems/damage_system.gd" id="damage_system"]
|
||||
[ext_resource type="Script" path="res://systems/health_system.gd" id="health_system"]
|
||||
@@ -252,3 +255,12 @@ is_exit = true
|
||||
|
||||
[node name="DungeonSystem" type="Node" parent="Systems"]
|
||||
script = ExtResource("dungeon_system")
|
||||
|
||||
[node name="AudioSystem" type="Node" parent="Systems"]
|
||||
script = ExtResource("audio_system")
|
||||
|
||||
[node name="XpSystem" type="Node" parent="Systems"]
|
||||
script = ExtResource("xp_system")
|
||||
|
||||
[node name="DungeonManager" type="Node" parent="."]
|
||||
script = ExtResource("dungeon_manager")
|
||||
|
||||
16
scenes/dungeon/dungeon_manager.gd
Normal file
16
scenes/dungeon/dungeon_manager.gd
Normal file
@@ -0,0 +1,16 @@
|
||||
extends Node
|
||||
|
||||
func _ready() -> void:
|
||||
call_deferred("_scale_dungeon")
|
||||
|
||||
func _scale_dungeon() -> void:
|
||||
var variant_multiplier: float = 10.0 if GameState.last_dungeon_variant == 1 else 1.0
|
||||
var total_scale: float = PlayerData.level_scale * variant_multiplier
|
||||
var parent: Node = get_parent()
|
||||
for child in parent.get_children():
|
||||
if not child.is_in_group("enemies"):
|
||||
continue
|
||||
if child.is_in_group("boss"):
|
||||
BossData.apply_scale(child, total_scale)
|
||||
else:
|
||||
EnemyData.apply_scale(child, total_scale)
|
||||
1
scenes/dungeon/dungeon_manager.gd.uid
Normal file
1
scenes/dungeon/dungeon_manager.gd.uid
Normal file
@@ -0,0 +1 @@
|
||||
uid://bfkxrflfn5qx4
|
||||
@@ -3,16 +3,23 @@ extends CharacterBody3D
|
||||
@export var stats: EnemyStats
|
||||
|
||||
var gravity: float = ProjectSettings.get_setting("physics/3d/default_gravity")
|
||||
var spawn_scale: float = 1.0
|
||||
var hover_t: float = 0.0
|
||||
var mesh_base_y: float = 0.0
|
||||
|
||||
func _ready() -> void:
|
||||
add_to_group("enemies")
|
||||
if is_in_group("boss"):
|
||||
BossData.register(self, stats)
|
||||
BossData.register(self, stats, spawn_scale)
|
||||
BossData.set_stat(self, "spawn_position", global_position)
|
||||
else:
|
||||
EnemyData.register(self, stats)
|
||||
EnemyData.register(self, stats, spawn_scale)
|
||||
EnemyData.set_stat(self, "spawn_position", global_position)
|
||||
EventBus.entity_died.connect(_on_entity_died)
|
||||
var mesh: Node3D = get_node_or_null("Mesh")
|
||||
if mesh:
|
||||
mesh_base_y = mesh.position.y
|
||||
_apply_appearance(mesh)
|
||||
|
||||
func _exit_tree() -> void:
|
||||
if is_in_group("boss"):
|
||||
@@ -24,6 +31,27 @@ func _on_entity_died(entity: Node) -> void:
|
||||
if entity == self:
|
||||
queue_free()
|
||||
|
||||
func _process(delta: float) -> void:
|
||||
hover_t += delta
|
||||
var mesh: Node3D = get_node_or_null("Mesh")
|
||||
if mesh:
|
||||
mesh.position.y = mesh_base_y + sin(hover_t * 3.0) * 0.08
|
||||
|
||||
func _apply_appearance(mesh: Node3D) -> void:
|
||||
if mesh is MeshInstance3D:
|
||||
var mat := StandardMaterial3D.new()
|
||||
if is_in_group("boss"):
|
||||
mat.albedo_color = Color(0.6, 0.15, 0.7, 1)
|
||||
mat.emission_enabled = true
|
||||
mat.emission = Color(0.8, 0.2, 0.9, 1)
|
||||
mat.emission_energy_multiplier = 0.4
|
||||
else:
|
||||
mat.albedo_color = Color(0.7, 0.25, 0.25, 1)
|
||||
mat.emission_enabled = true
|
||||
mat.emission = Color(0.9, 0.3, 0.2, 1)
|
||||
mat.emission_energy_multiplier = 0.25
|
||||
(mesh as MeshInstance3D).material_override = mat
|
||||
|
||||
func _physics_process(delta: float) -> void:
|
||||
if not is_on_floor():
|
||||
velocity.y -= gravity * delta
|
||||
|
||||
21
scenes/menu/game_over_overlay.gd
Normal file
21
scenes/menu/game_over_overlay.gd
Normal file
@@ -0,0 +1,21 @@
|
||||
extends CanvasLayer
|
||||
|
||||
@onready var label: Label = $Center/VBox/Label
|
||||
@onready var button: Button = $Center/VBox/Button
|
||||
|
||||
func _ready() -> void:
|
||||
visible = false
|
||||
button.pressed.connect(_on_button)
|
||||
|
||||
func show_overlay(wave: int) -> void:
|
||||
label.text = "GAME OVER — Welle %d erreicht" % wave
|
||||
visible = true
|
||||
|
||||
func _on_button() -> void:
|
||||
GameState.reset()
|
||||
PlayerData.reset_run()
|
||||
EnemyData.entities.clear()
|
||||
BossData.entities.clear()
|
||||
PortalData.entities.clear()
|
||||
TavernData.entities.clear()
|
||||
get_tree().change_scene_to_file("res://scenes/menu/main_menu.tscn")
|
||||
1
scenes/menu/game_over_overlay.gd.uid
Normal file
1
scenes/menu/game_over_overlay.gd.uid
Normal file
@@ -0,0 +1 @@
|
||||
uid://dm00anoh5wtyu
|
||||
41
scenes/menu/game_over_overlay.tscn
Normal file
41
scenes/menu/game_over_overlay.tscn
Normal file
@@ -0,0 +1,41 @@
|
||||
[gd_scene format=3]
|
||||
|
||||
[ext_resource type="Script" path="res://scenes/menu/game_over_overlay.gd" id="1"]
|
||||
|
||||
[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_button"]
|
||||
bg_color = Color(0.2, 0.2, 0.25, 0.9)
|
||||
border_width_bottom = 2
|
||||
border_width_left = 2
|
||||
border_width_right = 2
|
||||
border_width_top = 2
|
||||
border_color = Color(0.7, 0.7, 0.7, 1)
|
||||
|
||||
[node name="GameOverOverlay" type="CanvasLayer"]
|
||||
layer = 10
|
||||
script = ExtResource("1")
|
||||
|
||||
[node name="Background" type="ColorRect" parent="."]
|
||||
anchors_preset = 15
|
||||
anchor_right = 1.0
|
||||
anchor_bottom = 1.0
|
||||
color = Color(0, 0, 0, 0.75)
|
||||
|
||||
[node name="Center" type="CenterContainer" parent="."]
|
||||
anchors_preset = 15
|
||||
anchor_right = 1.0
|
||||
anchor_bottom = 1.0
|
||||
|
||||
[node name="VBox" type="VBoxContainer" parent="Center"]
|
||||
custom_minimum_size = Vector2(400, 0)
|
||||
theme_override_constants/separation = 30
|
||||
|
||||
[node name="Label" type="Label" parent="Center/VBox"]
|
||||
text = "GAME OVER"
|
||||
horizontal_alignment = 1
|
||||
theme_override_font_sizes/font_size = 48
|
||||
theme_override_colors/font_color = Color(1, 0.3, 0.3, 1)
|
||||
|
||||
[node name="Button" type="Button" parent="Center/VBox"]
|
||||
custom_minimum_size = Vector2(0, 48)
|
||||
text = "Zurück zum Menü"
|
||||
theme_override_styles/normal = SubResource("StyleBoxFlat_button")
|
||||
32
scenes/menu/main_menu.gd
Normal file
32
scenes/menu/main_menu.gd
Normal file
@@ -0,0 +1,32 @@
|
||||
extends CanvasLayer
|
||||
|
||||
@onready var singleplayer_button: Button = $Center/VBox/SingleplayerButton
|
||||
@onready var host_button: Button = $Center/VBox/HostButton
|
||||
@onready var join_button: Button = $Center/VBox/JoinButton
|
||||
@onready var quit_button: Button = $Center/VBox/QuitButton
|
||||
|
||||
func _ready() -> void:
|
||||
singleplayer_button.pressed.connect(_on_singleplayer)
|
||||
host_button.pressed.connect(_on_host)
|
||||
join_button.pressed.connect(_on_join)
|
||||
quit_button.pressed.connect(_on_quit)
|
||||
host_button.disabled = true
|
||||
join_button.disabled = true
|
||||
|
||||
func _on_singleplayer() -> void:
|
||||
GameState.reset()
|
||||
PlayerData.reset_run()
|
||||
EnemyData.entities.clear()
|
||||
BossData.entities.clear()
|
||||
PortalData.entities.clear()
|
||||
TavernData.entities.clear()
|
||||
get_tree().change_scene_to_file("res://scenes/world/world.tscn")
|
||||
|
||||
func _on_host() -> void:
|
||||
pass
|
||||
|
||||
func _on_join() -> void:
|
||||
pass
|
||||
|
||||
func _on_quit() -> void:
|
||||
get_tree().quit()
|
||||
1
scenes/menu/main_menu.gd.uid
Normal file
1
scenes/menu/main_menu.gd.uid
Normal file
@@ -0,0 +1 @@
|
||||
uid://b4m6byh4k2mg7
|
||||
57
scenes/menu/main_menu.tscn
Normal file
57
scenes/menu/main_menu.tscn
Normal file
@@ -0,0 +1,57 @@
|
||||
[gd_scene format=3]
|
||||
|
||||
[ext_resource type="Script" path="res://scenes/menu/main_menu.gd" id="1"]
|
||||
|
||||
[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_button"]
|
||||
bg_color = Color(0.2, 0.2, 0.25, 0.9)
|
||||
border_width_bottom = 2
|
||||
border_width_left = 2
|
||||
border_width_right = 2
|
||||
border_width_top = 2
|
||||
border_color = Color(0.7, 0.7, 0.7, 1)
|
||||
|
||||
[node name="MainMenu" type="CanvasLayer"]
|
||||
script = ExtResource("1")
|
||||
|
||||
[node name="Background" type="ColorRect" parent="."]
|
||||
anchors_preset = 15
|
||||
anchor_right = 1.0
|
||||
anchor_bottom = 1.0
|
||||
color = Color(0.08, 0.08, 0.12, 1)
|
||||
|
||||
[node name="Center" type="CenterContainer" parent="."]
|
||||
anchors_preset = 15
|
||||
anchor_right = 1.0
|
||||
anchor_bottom = 1.0
|
||||
|
||||
[node name="VBox" type="VBoxContainer" parent="Center"]
|
||||
custom_minimum_size = Vector2(320, 0)
|
||||
theme_override_constants/separation = 20
|
||||
|
||||
[node name="Title" type="Label" parent="Center/VBox"]
|
||||
text = "MMO"
|
||||
horizontal_alignment = 1
|
||||
theme_override_font_sizes/font_size = 64
|
||||
|
||||
[node name="Spacer" type="Control" parent="Center/VBox"]
|
||||
custom_minimum_size = Vector2(0, 40)
|
||||
|
||||
[node name="SingleplayerButton" type="Button" parent="Center/VBox"]
|
||||
custom_minimum_size = Vector2(0, 48)
|
||||
text = "Singleplayer"
|
||||
theme_override_styles/normal = SubResource("StyleBoxFlat_button")
|
||||
|
||||
[node name="HostButton" type="Button" parent="Center/VBox"]
|
||||
custom_minimum_size = Vector2(0, 48)
|
||||
text = "Host (bald)"
|
||||
theme_override_styles/normal = SubResource("StyleBoxFlat_button")
|
||||
|
||||
[node name="JoinButton" type="Button" parent="Center/VBox"]
|
||||
custom_minimum_size = Vector2(0, 48)
|
||||
text = "Join (bald)"
|
||||
theme_override_styles/normal = SubResource("StyleBoxFlat_button")
|
||||
|
||||
[node name="QuitButton" type="Button" parent="Center/VBox"]
|
||||
custom_minimum_size = Vector2(0, 48)
|
||||
text = "Quit"
|
||||
theme_override_styles/normal = SubResource("StyleBoxFlat_button")
|
||||
@@ -1,6 +1,6 @@
|
||||
extends Node
|
||||
|
||||
const TARGET_RANGE := 20.0
|
||||
const TARGET_RANGE := 100.0
|
||||
|
||||
var mouse_press_pos: Vector2 = Vector2.ZERO
|
||||
|
||||
@@ -29,15 +29,28 @@ func _try_target_under_mouse(mouse_pos: Vector2) -> void:
|
||||
var result := space.intersect_ray(query)
|
||||
if result:
|
||||
var hit_target := result.collider.get_parent() as Node3D
|
||||
EventBus.target_requested.emit(player, hit_target)
|
||||
else:
|
||||
EventBus.target_requested.emit(player, null)
|
||||
if hit_target and hit_target.is_in_group("tavern"):
|
||||
EventBus.target_requested.emit(player, null)
|
||||
return
|
||||
if hit_target and (hit_target.is_in_group("enemies") or hit_target.is_in_group("portals")):
|
||||
EventBus.target_requested.emit(player, hit_target)
|
||||
return
|
||||
EventBus.target_requested.emit(player, null)
|
||||
|
||||
func _cycle_target() -> void:
|
||||
var targets := get_tree().get_nodes_in_group("enemies") + get_tree().get_nodes_in_group("portals")
|
||||
var enemies := get_tree().get_nodes_in_group("enemies")
|
||||
var portals := get_tree().get_nodes_in_group("portals")
|
||||
var targets: Array = []
|
||||
for e in enemies:
|
||||
if is_instance_valid(e):
|
||||
targets.append(e)
|
||||
for p in portals:
|
||||
if is_instance_valid(p):
|
||||
targets.append(p)
|
||||
if targets.is_empty():
|
||||
EventBus.target_requested.emit(player, null)
|
||||
return
|
||||
targets.sort_custom(func(a, b): return player.global_position.distance_squared_to(a.global_position) < player.global_position.distance_squared_to(b.global_position))
|
||||
var current: Node3D = PlayerData.target
|
||||
if current == null or current not in targets:
|
||||
EventBus.target_requested.emit(player, targets[0])
|
||||
|
||||
@@ -4,6 +4,7 @@ extends StaticBody3D
|
||||
@export var is_exit: bool = false
|
||||
|
||||
var active := false
|
||||
var dungeon_variant: int = 0
|
||||
|
||||
func _ready() -> void:
|
||||
if not is_exit:
|
||||
@@ -28,6 +29,7 @@ func _on_gate_area_body_entered(body: Node3D) -> void:
|
||||
PlayerData.returning_from_dungeon = true
|
||||
else:
|
||||
PlayerData.portal_position = global_position
|
||||
GameState.last_dungeon_variant = dungeon_variant
|
||||
call_deferred("_change_scene")
|
||||
|
||||
func _change_scene() -> void:
|
||||
|
||||
@@ -4,11 +4,34 @@ extends StaticBody3D
|
||||
|
||||
func _ready() -> void:
|
||||
add_to_group("portals")
|
||||
if stats.variant == PortalStats.Kind.RED:
|
||||
add_to_group("red_portal")
|
||||
_apply_appearance()
|
||||
PortalData.register(self, stats)
|
||||
|
||||
func _exit_tree() -> void:
|
||||
PortalData.deregister(self)
|
||||
|
||||
func _process(delta: float) -> void:
|
||||
var mesh: Node3D = get_node_or_null("Mesh")
|
||||
if mesh:
|
||||
mesh.rotate_y(delta * 1.5)
|
||||
|
||||
func _apply_appearance() -> void:
|
||||
var mesh: MeshInstance3D = get_node_or_null("Mesh")
|
||||
if not mesh:
|
||||
return
|
||||
var mat := StandardMaterial3D.new()
|
||||
mat.emission_enabled = true
|
||||
mat.emission_energy_multiplier = 0.8
|
||||
if stats.variant == PortalStats.Kind.RED:
|
||||
mat.albedo_color = Color(0.9, 0.1, 0.1, 1)
|
||||
mat.emission = Color(1.0, 0.25, 0.25, 1)
|
||||
else:
|
||||
mat.albedo_color = Color(0.4, 0.2, 0.85, 1)
|
||||
mat.emission = Color(0.6, 0.35, 1.0, 1)
|
||||
mesh.material_override = mat
|
||||
|
||||
func _on_detection_area_body_entered(body: Node3D) -> void:
|
||||
if body is CharacterBody3D and body.name == "Player":
|
||||
EventBus.portal_entered.emit(self, body)
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
extends BaseStats
|
||||
class_name PortalStats
|
||||
|
||||
enum Kind { NORMAL, RED }
|
||||
|
||||
@export var variant: Kind = Kind.NORMAL
|
||||
@export var spawn_count := 3
|
||||
@export var thresholds: Array[float] = [0.85, 0.70, 0.55, 0.40, 0.25, 0.10]
|
||||
|
||||
9
scenes/portal/red_portal_stats.tres
Normal file
9
scenes/portal/red_portal_stats.tres
Normal file
@@ -0,0 +1,9 @@
|
||||
[gd_resource type="Resource" script_class="PortalStats" format=3]
|
||||
|
||||
[ext_resource type="Script" path="res://scenes/portal/portal_stats.gd" id="1"]
|
||||
|
||||
[resource]
|
||||
script = ExtResource("1")
|
||||
variant = 1
|
||||
max_health = 5000.0
|
||||
spawn_count = 5
|
||||
10
scenes/tavern/init.gd
Normal file
10
scenes/tavern/init.gd
Normal file
@@ -0,0 +1,10 @@
|
||||
extends StaticBody3D
|
||||
|
||||
@export var stats: TavernStats
|
||||
|
||||
func _ready() -> void:
|
||||
add_to_group("tavern")
|
||||
TavernData.register(self, stats)
|
||||
|
||||
func _exit_tree() -> void:
|
||||
TavernData.deregister(self)
|
||||
1
scenes/tavern/init.gd.uid
Normal file
1
scenes/tavern/init.gd.uid
Normal file
@@ -0,0 +1 @@
|
||||
uid://dd103qxf2s5i5
|
||||
66
scenes/tavern/tavern.tscn
Normal file
66
scenes/tavern/tavern.tscn
Normal file
@@ -0,0 +1,66 @@
|
||||
[gd_scene format=3]
|
||||
|
||||
[ext_resource type="Script" path="res://scenes/tavern/init.gd" id="1"]
|
||||
[ext_resource type="Resource" path="res://scenes/tavern/tavern_stats.tres" id="2"]
|
||||
|
||||
[sub_resource type="BoxShape3D" id="BoxShape3D_tavern"]
|
||||
size = Vector3(5, 3, 5)
|
||||
|
||||
[sub_resource type="StandardMaterial3D" id="StandardMaterial3D_tavern"]
|
||||
albedo_color = Color(0.45, 0.3, 0.15, 1)
|
||||
|
||||
[sub_resource type="StandardMaterial3D" id="StandardMaterial3D_roof"]
|
||||
albedo_color = Color(0.3, 0.15, 0.08, 1)
|
||||
|
||||
[sub_resource type="BoxMesh" id="BoxMesh_tavern"]
|
||||
size = Vector3(5, 3, 5)
|
||||
material = SubResource("StandardMaterial3D_tavern")
|
||||
|
||||
[sub_resource type="PrismMesh" id="PrismMesh_roof"]
|
||||
size = Vector3(5.4, 2, 5.4)
|
||||
material = SubResource("StandardMaterial3D_roof")
|
||||
|
||||
[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_health_bg"]
|
||||
bg_color = Color(0.3, 0.1, 0.1, 1)
|
||||
|
||||
[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_health_fill"]
|
||||
bg_color = Color(0.9, 0.7, 0.2, 1)
|
||||
|
||||
[node name="Tavern" type="StaticBody3D"]
|
||||
script = ExtResource("1")
|
||||
stats = ExtResource("2")
|
||||
|
||||
[node name="CollisionShape3D" type="CollisionShape3D" parent="."]
|
||||
shape = SubResource("BoxShape3D_tavern")
|
||||
|
||||
[node name="Mesh" type="MeshInstance3D" parent="."]
|
||||
mesh = SubResource("BoxMesh_tavern")
|
||||
|
||||
[node name="Roof" type="MeshInstance3D" parent="."]
|
||||
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 2.5, 0)
|
||||
mesh = SubResource("PrismMesh_roof")
|
||||
|
||||
[node name="Healthbar" type="Sprite3D" parent="."]
|
||||
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 4, 0)
|
||||
billboard = 1
|
||||
pixel_size = 0.015
|
||||
|
||||
[node name="SubViewport" type="SubViewport" parent="Healthbar"]
|
||||
transparent_bg = true
|
||||
size = Vector2i(204, 25)
|
||||
|
||||
[node name="Border" type="ColorRect" parent="Healthbar/SubViewport"]
|
||||
offset_right = 204.0
|
||||
offset_bottom = 25.0
|
||||
color = Color(0.1, 0.1, 0.1, 1)
|
||||
|
||||
[node name="HealthBar" type="ProgressBar" parent="Healthbar/SubViewport"]
|
||||
offset_left = 2.0
|
||||
offset_top = 2.0
|
||||
offset_right = 202.0
|
||||
offset_bottom = 23.0
|
||||
theme_override_styles/background = SubResource("StyleBoxFlat_health_bg")
|
||||
theme_override_styles/fill = SubResource("StyleBoxFlat_health_fill")
|
||||
max_value = 5000.0
|
||||
value = 5000.0
|
||||
show_percentage = false
|
||||
2
scenes/tavern/tavern_stats.gd
Normal file
2
scenes/tavern/tavern_stats.gd
Normal file
@@ -0,0 +1,2 @@
|
||||
extends BaseStats
|
||||
class_name TavernStats
|
||||
1
scenes/tavern/tavern_stats.gd.uid
Normal file
1
scenes/tavern/tavern_stats.gd.uid
Normal file
@@ -0,0 +1 @@
|
||||
uid://duw4m3mhgmixk
|
||||
7
scenes/tavern/tavern_stats.tres
Normal file
7
scenes/tavern/tavern_stats.tres
Normal file
@@ -0,0 +1,7 @@
|
||||
[gd_resource type="Resource" script_class="TavernStats" format=3]
|
||||
|
||||
[ext_resource type="Script" path="res://scenes/tavern/tavern_stats.gd" id="1"]
|
||||
|
||||
[resource]
|
||||
script = ExtResource("1")
|
||||
max_health = 5000.0
|
||||
@@ -2,34 +2,55 @@ extends Node
|
||||
|
||||
const PORTAL_SCENE: PackedScene = preload("res://scenes/portal/portal.tscn")
|
||||
const GATE_SCENE: PackedScene = preload("res://scenes/portal/gate.tscn")
|
||||
const SPAWN_INTERVAL := 30.0
|
||||
const MAX_PORTALS := 3
|
||||
const RED_PORTAL_STATS: Resource = preload("res://scenes/portal/red_portal_stats.tres")
|
||||
const MAX_NORMAL_PORTALS := 3
|
||||
const MIN_DISTANCE := 20.0
|
||||
const MAX_DISTANCE := 40.0
|
||||
const RESPAWN_DELAY := 1.0
|
||||
|
||||
var portals: Array[Node] = []
|
||||
var timer := 0.0
|
||||
|
||||
func _ready() -> void:
|
||||
EventBus.portal_defeated.connect(_on_portal_defeated)
|
||||
EventBus.wave_started.connect(_on_wave_started)
|
||||
if PlayerData.portal_position != Vector3.ZERO and not PlayerData.dungeon_cleared:
|
||||
call_deferred("_restore_gate")
|
||||
else:
|
||||
if PlayerData.dungeon_cleared:
|
||||
PlayerData.clear_cache()
|
||||
call_deferred("_spawn_portal")
|
||||
call_deferred("_ensure_portals")
|
||||
|
||||
func _restore_gate() -> void:
|
||||
var gate: Node3D = GATE_SCENE.instantiate()
|
||||
get_parent().add_child(gate)
|
||||
gate.global_position = PlayerData.portal_position
|
||||
|
||||
func _process(delta: float) -> void:
|
||||
timer += delta
|
||||
if timer >= SPAWN_INTERVAL:
|
||||
timer = 0.0
|
||||
_cleanup_dead()
|
||||
if portals.size() < MAX_PORTALS:
|
||||
_spawn_portal()
|
||||
func _ensure_portals() -> void:
|
||||
_cleanup_dead()
|
||||
while portals.size() < MAX_NORMAL_PORTALS:
|
||||
_spawn_portal()
|
||||
|
||||
func _on_portal_defeated(portal: Node) -> void:
|
||||
if portal.is_in_group("red_portal"):
|
||||
return
|
||||
portals.erase(portal)
|
||||
await get_tree().create_timer(RESPAWN_DELAY).timeout
|
||||
_ensure_portals()
|
||||
|
||||
func _on_wave_started(_wave_number: int) -> void:
|
||||
_spawn_red_portal()
|
||||
|
||||
func _spawn_red_portal() -> void:
|
||||
for p in get_tree().get_nodes_in_group("red_portal"):
|
||||
if is_instance_valid(p):
|
||||
return
|
||||
var angle: float = randf() * TAU
|
||||
var distance: float = randf_range(MIN_DISTANCE, MAX_DISTANCE)
|
||||
var pos := Vector3(cos(angle) * distance, 0, sin(angle) * distance)
|
||||
var portal: Node3D = PORTAL_SCENE.instantiate()
|
||||
portal.stats = RED_PORTAL_STATS
|
||||
get_parent().add_child(portal)
|
||||
portal.global_position = pos
|
||||
|
||||
func _spawn_portal() -> void:
|
||||
var angle: float = randf() * TAU
|
||||
|
||||
@@ -23,7 +23,14 @@
|
||||
[ext_resource type="Script" path="res://systems/respawn_system.gd" id="respawn_system"]
|
||||
[ext_resource type="Script" path="res://systems/shield_system.gd" id="shield_system"]
|
||||
[ext_resource type="Script" path="res://systems/spawn_system.gd" id="spawn_system"]
|
||||
[ext_resource type="Script" path="res://systems/wave_system.gd" id="wave_system"]
|
||||
[ext_resource type="Script" path="res://systems/xp_system.gd" id="xp_system"]
|
||||
[ext_resource type="Script" path="res://systems/invasion_system.gd" id="invasion_system"]
|
||||
[ext_resource type="Script" path="res://systems/audio_system.gd" id="audio_system"]
|
||||
[ext_resource type="Script" path="res://scenes/world/world_manager.gd" id="world_manager"]
|
||||
[ext_resource type="PackedScene" path="res://scenes/menu/game_over_overlay.tscn" id="game_over_overlay"]
|
||||
[ext_resource type="PackedScene" path="res://scenes/hud/hud.tscn" id="hud"]
|
||||
[ext_resource type="PackedScene" path="res://scenes/tavern/tavern.tscn" id="tavern"]
|
||||
[ext_resource type="PackedScene" uid="uid://cdnkbt1f0db7e" path="res://scenes/player/player.tscn" id="player"]
|
||||
[ext_resource type="Script" path="res://scenes/world/portal_spawner.gd" id="portal_spawner"]
|
||||
[ext_resource type="Resource" uid="uid://cgxtn7dfs40bh" path="res://scenes/player/role/tank/set.tres" id="tank_set"]
|
||||
@@ -141,6 +148,18 @@ script = ExtResource("hud_system")
|
||||
[node name="NameplateSystem" type="Node" parent="Systems"]
|
||||
script = ExtResource("nameplate_system")
|
||||
|
||||
[node name="WaveSystem" type="Node" parent="Systems"]
|
||||
script = ExtResource("wave_system")
|
||||
|
||||
[node name="XpSystem" type="Node" parent="Systems"]
|
||||
script = ExtResource("xp_system")
|
||||
|
||||
[node name="InvasionSystem" type="Node" parent="Systems"]
|
||||
script = ExtResource("invasion_system")
|
||||
|
||||
[node name="AudioSystem" type="Node" parent="Systems"]
|
||||
script = ExtResource("audio_system")
|
||||
|
||||
[node name="NavigationRegion3D" type="NavigationRegion3D" parent="."]
|
||||
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0.0027503967, 0.014227867, 0.023231506)
|
||||
navigation_mesh = SubResource("NavigationMesh_1")
|
||||
@@ -157,15 +176,9 @@ shape = SubResource("WorldBoundaryShape3D_1")
|
||||
transform = Transform3D(1, 0, 0, 0, 0.707, 0.707, 0, -0.707, 0.707, 0, 10, 0)
|
||||
shadow_enabled = true
|
||||
|
||||
[node name="Taverne" type="StaticBody3D" parent="."]
|
||||
[node name="Tavern" parent="." instance=ExtResource("tavern")]
|
||||
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1.5, 0)
|
||||
|
||||
[node name="Mesh" type="MeshInstance3D" parent="Taverne"]
|
||||
mesh = SubResource("BoxMesh_tavern")
|
||||
|
||||
[node name="CollisionShape3D" type="CollisionShape3D" parent="Taverne"]
|
||||
shape = SubResource("BoxShape3D_tavern")
|
||||
|
||||
[node name="Player" parent="." instance=ExtResource("player")]
|
||||
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1, -5)
|
||||
|
||||
@@ -173,3 +186,8 @@ transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1, -5)
|
||||
|
||||
[node name="PortalSpawner" type="Node" parent="."]
|
||||
script = ExtResource("portal_spawner")
|
||||
|
||||
[node name="WorldManager" type="Node" parent="."]
|
||||
script = ExtResource("world_manager")
|
||||
|
||||
[node name="GameOverOverlay" parent="." instance=ExtResource("game_over_overlay")]
|
||||
|
||||
21
scenes/world/world_manager.gd
Normal file
21
scenes/world/world_manager.gd
Normal file
@@ -0,0 +1,21 @@
|
||||
extends Node
|
||||
|
||||
func _ready() -> void:
|
||||
EventBus.game_over.connect(_on_game_over)
|
||||
if GameState.force_return_to_world:
|
||||
call_deferred("_handle_force_return")
|
||||
|
||||
func _handle_force_return() -> void:
|
||||
GameState.force_return_to_world = false
|
||||
var player: Node3D = get_tree().get_first_node_in_group("player")
|
||||
var tavern: Node3D = get_tree().get_first_node_in_group("tavern")
|
||||
if player and tavern:
|
||||
player.global_position = tavern.global_position + Vector3(0, 1, -6)
|
||||
var invasion: Node = get_node_or_null("../Systems/InvasionSystem")
|
||||
if invasion:
|
||||
invasion.trigger()
|
||||
|
||||
func _on_game_over() -> void:
|
||||
var overlay: CanvasLayer = get_node_or_null("../GameOverOverlay")
|
||||
if overlay and overlay.has_method("show_overlay"):
|
||||
overlay.show_overlay(GameState.current_wave)
|
||||
1
scenes/world/world_manager.gd.uid
Normal file
1
scenes/world/world_manager.gd.uid
Normal file
@@ -0,0 +1 @@
|
||||
uid://cejlqodm01ob3
|
||||
Reference in New Issue
Block a user