diff --git a/Resources/Enemies/Base_Fairy.tres b/Resources/Enemies/Base_Fairy.tres index 6c75349d..c21afd16 100644 --- a/Resources/Enemies/Base_Fairy.tres +++ b/Resources/Enemies/Base_Fairy.tres @@ -43,7 +43,7 @@ metadata/_custom_type_script = "uid://cq65aed620ijo" script = ExtResource("8_241b0") EnemyName = &"Fairy" EnemyKey = &"FAIRY_BASE" -PrefabPath = &"res://Scenes/Actors/Fairy_New.tscn" +PrefabPath = &"uid://clieeuln36a7a" MaxHealth = 8.0 MovementSpeed = 30.0 Weapon = ExtResource("7_xkg5o") diff --git a/Resources/Enemies/Base_Fairy_Special.tres b/Resources/Enemies/Base_Fairy_Special.tres index 5e8bc029..2d741eb9 100644 --- a/Resources/Enemies/Base_Fairy_Special.tres +++ b/Resources/Enemies/Base_Fairy_Special.tres @@ -43,7 +43,7 @@ metadata/_custom_type_script = "uid://cq65aed620ijo" script = ExtResource("8_8fxhl") EnemyName = &"Special Fairy" EnemyKey = &"FAIRY_BASE_SPECIAL" -PrefabPath = &"res://Scenes/Actors/Fairy_Special_FSM.tscn" +PrefabPath = &"uid://bq4r28ikbmn0r" MaxHealth = 12.0 MovementSpeed = 30.0 Weapon = ExtResource("7_tf7s2") diff --git a/Resources/Enemies/Fairy_Guard.tres b/Resources/Enemies/Fairy_Guard.tres index 8bb28de2..42502a75 100644 --- a/Resources/Enemies/Fairy_Guard.tres +++ b/Resources/Enemies/Fairy_Guard.tres @@ -43,7 +43,7 @@ metadata/_custom_type_script = "uid://cq65aed620ijo" script = ExtResource("1_p31tv") EnemyName = &"Fairy Guard" EnemyKey = &"FAIRY_GUARD" -PrefabPath = &"res://Scenes/Actors/FairyGuard_New.tscn" +PrefabPath = &"uid://bb32f4p5e671j" MaxHealth = 10.0 MovementSpeed = 40.0 Weapon = ExtResource("7_xlxdc") diff --git a/Resources/Enemies/Thermathron.tres b/Resources/Enemies/Thermathron.tres index e1780426..2c2393c8 100644 --- a/Resources/Enemies/Thermathron.tres +++ b/Resources/Enemies/Thermathron.tres @@ -1,6 +1,5 @@ -[gd_resource type="Resource" script_class="EnemyResource" load_steps=4 format=3 uid="uid://cfdvg162u65sr"] +[gd_resource type="Resource" script_class="EnemyResource" load_steps=3 format=3 uid="uid://cfdvg162u65sr"] -[ext_resource type="Script" uid="uid://cq65aed620ijo" path="res://Scripts/Resources/Loot/LootDrop.cs" id="1_f3huq"] [ext_resource type="Resource" uid="uid://cdfmedtgp2rcn" path="res://Resources/Weapons/EnemyWeapon.tres" id="7_filx8"] [ext_resource type="Script" uid="uid://cd5o0ceb50jki" path="res://Scripts/Resources/EnemyResource.cs" id="8_x8scf"] @@ -8,11 +7,11 @@ script = ExtResource("8_x8scf") EnemyName = &"Robot 1" EnemyKey = &"ROBOT_1" -PrefabPath = &"res://Scenes/Actors/Thermathron.tscn" +PrefabPath = &"uid://dky13otbks8cm" MaxHealth = 16.0 MovementSpeed = 38.0 Weapon = ExtResource("7_filx8") -LootDrops = Array[ExtResource("1_f3huq")]([]) +LootDrops = [] MotivationReward = 4.0 PlayerDetectionRange = 90.0 ViewRange = 120.0 diff --git a/Scenes/Maps/Roguelike.tscn b/Scenes/Maps/Roguelike.tscn index cc08c8d5..fbf3b673 100644 --- a/Scenes/Maps/Roguelike.tscn +++ b/Scenes/Maps/Roguelike.tscn @@ -35,7 +35,6 @@ process_mode = 1 script = ExtResource("4_jtlua") Rooms = Array[Object]([ExtResource("5_gwtv6"), ExtResource("6_gwtv6"), ExtResource("7_wbqvu"), ExtResource("8_3fyis"), ExtResource("9_go1yg"), ExtResource("5_pfafs"), ExtResource("11_68lig"), ExtResource("12_83bvc"), ExtResource("13_y651a")]) DungeonLength = 12 -Seed = 1 [node name="CameraController" type="Camera2D" parent="."] process_mode = 1 diff --git a/Scenes/Maps/RogueliteMaps/Beginner1.tscn b/Scenes/Maps/RogueliteMaps/Beginner1.tscn index ec7c71fd..84e979ed 100644 --- a/Scenes/Maps/RogueliteMaps/Beginner1.tscn +++ b/Scenes/Maps/RogueliteMaps/Beginner1.tscn @@ -1,4 +1,4 @@ -[gd_scene load_steps=7 format=4 uid="uid://24wh7h2dbljf"] +[gd_scene load_steps=8 format=4 uid="uid://24wh7h2dbljf"] [ext_resource type="Script" uid="uid://b2j00riayxkit" path="res://Scripts/Controllers/RogueliteRoom.cs" id="1_vhsym"] [ext_resource type="Resource" uid="uid://dn3ai56rrxfnk" path="res://Resources/RogueliteMaps/Beginner1.tres" id="2_vhsym"] @@ -7,6 +7,9 @@ [ext_resource type="Script" uid="uid://krean0uywtms" path="res://Scripts/TilemapAvoidance.cs" id="4_pys6w"] [ext_resource type="Script" uid="uid://ddry5kjj3fr6c" path="res://Scripts/Controllers/DoorMarker.cs" id="5_mqiea"] +[sub_resource type="RectangleShape2D" id="RectangleShape2D_u3c1h"] +size = Vector2(272, 85) + [node name="Tilemaps" type="Node2D"] process_mode = 1 script = ExtResource("1_vhsym") @@ -57,3 +60,13 @@ Direction = 2 position = Vector2(8.1806, 87.7693) script = ExtResource("5_mqiea") Direction = 3 + +[node name="PlayerEnterDetector" type="Area2D" parent="."] +collision_layer = 0 +collision_mask = 2 + +[node name="CollisionShape2D" type="CollisionShape2D" parent="PlayerEnterDetector"] +position = Vector2(162, 94.5) +shape = SubResource("RectangleShape2D_u3c1h") + +[connection signal="area_entered" from="PlayerEnterDetector" to="." method="OnRoomEntered"] diff --git a/Scenes/Maps/RogueliteMaps/Rg2.tscn b/Scenes/Maps/RogueliteMaps/Rg2.tscn index 5a200c57..d3bbf8b4 100644 --- a/Scenes/Maps/RogueliteMaps/Rg2.tscn +++ b/Scenes/Maps/RogueliteMaps/Rg2.tscn @@ -1,4 +1,4 @@ -[gd_scene load_steps=9 format=4 uid="uid://dcxrdhq1yw5c7"] +[gd_scene load_steps=10 format=4 uid="uid://dcxrdhq1yw5c7"] [ext_resource type="Script" uid="uid://b2j00riayxkit" path="res://Scripts/Controllers/RogueliteRoom.cs" id="1_cak6m"] [ext_resource type="Resource" uid="uid://ly8l7asedjpx" path="res://Resources/RogueliteMaps/TestRGMap2.tres" id="2_cak6m"] @@ -15,6 +15,9 @@ outlines = Array[PackedVector2Array]([PackedVector2Array(17, 36, 14, 12, 47, 15, parsed_collision_mask = 353 source_geometry_mode = 1 +[sub_resource type="RectangleShape2D" id="RectangleShape2D_wtdf1"] +size = Vector2(272, 85) + [node name="Map" type="Node2D"] process_mode = 1 script = ExtResource("1_cak6m") @@ -81,3 +84,13 @@ Direction = 3 [node name="NavigationRegion2D" type="NavigationRegion2D" parent="."] navigation_polygon = SubResource("NavigationPolygon_f7qjl") + +[node name="PlayerEnterDetector" type="Area2D" parent="."] +collision_layer = 0 +collision_mask = 2 + +[node name="CollisionShape2D" type="CollisionShape2D" parent="PlayerEnterDetector"] +position = Vector2(162, 94.5) +shape = SubResource("RectangleShape2D_wtdf1") + +[connection signal="area_entered" from="PlayerEnterDetector" to="." method="OnRoomEntered"] diff --git a/Scripts/Components/FSM/Enemy/EnemyFSMProxy.cs b/Scripts/Components/FSM/Enemy/EnemyFSMProxy.cs index 5e4b39ed..ac07571c 100644 --- a/Scripts/Components/FSM/Enemy/EnemyFSMProxy.cs +++ b/Scripts/Components/FSM/Enemy/EnemyFSMProxy.cs @@ -23,6 +23,13 @@ public partial class EnemyFSMProxy : CharacterBody2D, IActivable [Export] public ActivationType ActivationType { get; private set; } = ActivationType.Toggle; + [Signal] public delegate void DeathEventHandler(EnemyFSMProxy enemy); + + public void TriggerDeath() + { + EmitSignalDeath(this); + } + public bool Activate(ActivationType activationType = ActivationType.Toggle) { switch (activationType) diff --git a/Scripts/Controllers/RogueliteRoom.cs b/Scripts/Controllers/RogueliteRoom.cs index 7c6491fd..974a4986 100644 --- a/Scripts/Controllers/RogueliteRoom.cs +++ b/Scripts/Controllers/RogueliteRoom.cs @@ -1,5 +1,7 @@ using System; +using System.Collections.Generic; using System.Linq; +using Cirno.Scripts.Components.FSM.Enemy; using Cirno.Scripts.Resources; using Cirno.Scripts.Resources.Roguelite; using Godot; @@ -16,7 +18,7 @@ public partial class RogueliteRoom : Node2D [Export] public PackedScene DoorPrefab { get; private set; } [Export] public PackedScene WallPrefab { get; private set; } - private static readonly Dictionary DirectionMap = new() + private static readonly Godot.Collections.Dictionary DirectionMap = new() { { "North", new Vector2I(0, -1) }, { "South", new Vector2I(0, 1) }, @@ -24,6 +26,10 @@ public partial class RogueliteRoom : Node2D { "West", new Vector2I(-1, 0) }, }; + private List _doors = []; + private List _connections = []; + private List _enemies = []; + private Array SpawnableEnemies => RoomResource.SpawnableEnemies; public RogueliteRoom Spawn() @@ -84,6 +90,15 @@ public partial class RogueliteRoom : Node2D door.State = DoorState.Closed; + _doors.Add(door); + + if (connection.FromDoor is null) connection.FromDoor = door; + else if (connection.ToDoor is null) connection.ToDoor = door; + else + GD.Print("Door connection was full"); + + _connections.Add(connection); + // var label = new Label(); // label.Text = $"Door Edge: {doorEdge}, {connection}"; // label.ZIndex = 10; @@ -100,15 +115,7 @@ public partial class RogueliteRoom : Node2D // // marker.AddChild(label); } - - // PackedScene prefab = hasConnection - // ? GD.Load("res://Prefabs/Door.tscn") - // : GD.Load("res://Prefabs/Wall.tscn"); - // - // var instance = prefab.Instantiate(); - // AddChild(instance); - // instance.GlobalPosition = ((Node2D)child).GlobalPosition; - // instance.Rotation = ((Node2D)child).GlobalRotation; + } } @@ -125,7 +132,59 @@ public partial class RogueliteRoom : Node2D var e = SpawnableEnemies[index]; var enemyScene = GD.Load(e.PrefabPath); - var spawnedEnemy = spawner.CreateChild(enemyScene); + var spawnedEnemy = spawner.CreateChild(enemyScene); + + _enemies.Add(spawnedEnemy); + + spawnedEnemy.Death += SpawnedEnemyOnDeath; } } + + private void SpawnedEnemyOnDeath(EnemyFSMProxy enemy) + { + enemy.Death -= SpawnedEnemyOnDeath; + _enemies.Remove(enemy); + + if (_enemies.Count == 0) + { + OpenDoors(); + } + } + + public void OpenDoors() + { + foreach (var connection in _connections) + { + connection.FromDoor?.Activate(ActivationType.Open); + connection.ToDoor?.Activate(ActivationType.Open); + } + } + + public void CloseDoors() + { + foreach (var connection in _connections) + { + connection.FromDoor?.Activate(ActivationType.Close); + connection.ToDoor?.Activate(ActivationType.Close); + } + } + + private void OnRoomEntered(Area2D area) + { + if (area is not InteractionController player) return; + + if (_enemies.Count <= 0) + { + OpenDoors(); + } + else + { + CloseDoors(); + } + } + + private void OnRoomExited(Area2D area) + { + if (area is not InteractionController player) return; + } } \ No newline at end of file diff --git a/Scripts/Controllers/RogueliteRoomManager.cs b/Scripts/Controllers/RogueliteRoomManager.cs index c42d300e..9dc3c46c 100644 --- a/Scripts/Controllers/RogueliteRoomManager.cs +++ b/Scripts/Controllers/RogueliteRoomManager.cs @@ -87,9 +87,9 @@ public partial class RogueliteRoomManager : Node2D Vector2I origin = Vector2I.Zero; var starterRoom = Rooms.Where(r => r.Type == RoomType.Starter).PickRandom(); - SpawnRoom(starterRoom, origin); + SpawnRoom(starterRoom, origin, out var spawnedBeginRoom); _mainPath.Add(origin); - + var randomRoomsList = RegularRooms.Shuffle(DungeonLength).ToList(); var currentPos = origin; @@ -120,7 +120,7 @@ public partial class RogueliteRoomManager : Node2D //var posWithOffset = nextPos + new Vector2I(offset, 0); - if (!SpawnRoom(roomToSpawn, nextPos)) + if (!SpawnRoom(roomToSpawn, nextPos, out var spawnedRoom)) { GD.PrintErr("Could not spawn room"); break; @@ -153,7 +153,7 @@ public partial class RogueliteRoomManager : Node2D var bossRoom = BossRooms.PickRandom(); - if (SpawnRoom(bossRoom, nextPos)) + if (SpawnRoom(bossRoom, nextPos, out var spawnedBossRoom)) { _mainPath.Add(nextPos); @@ -175,136 +175,17 @@ public partial class RogueliteRoomManager : Node2D room.HandleDoors((doorEdge, pos) => { //var neighborPos = room.GridPosition + pos; - return _connections.FirstOrDefault(x => (x.From == doorEdge && x.To == pos) || (x.From == pos && x.To == doorEdge)); + return _connections.FirstOrDefault(x => + (x.From == doorEdge && x.To == pos) || (x.From == pos && x.To == doorEdge)); //return _roomGrid.ContainsKey(neighborPos); }); } EmitSignalMapCreated(); + + //CallDeferred(MethodName.OpenStartDoorsDeferred, spawnedBeginRoom); } - private void GenerateDungeon() - { - Vector2I origin = Vector2I.Zero; - var starterRoom = Rooms.Where(r => r.Type == RoomType.Starter).PickRandom(); - SpawnRoom(starterRoom, origin); - _mainPath.Add(origin); - - var currentPos = origin; - - // 🔐 Reserve boss room position - var bossRoom = BossRooms.FirstOrDefault(); - Vector2I? bossTargetPos = FindValidBossRoomLocation(currentPos); - - if (bossRoom == null || bossTargetPos == null) - { - GD.PrintErr("Failed to place boss room!"); - return; - } - - // Generate path to boss room - while (currentPos != bossTargetPos.Value) - { - var nextPos = GetNextPosition(currentPos, bossTargetPos.Value); - if (!SpawnRoom(Rooms.Where(x => x.Type == RoomType.Regular).PickRandom(), nextPos)) break; - - _mainPath.Add(nextPos); - _connections.Add(new RoomConnection(currentPos, nextPos)); - currentPos = nextPos; - } - - // Place the boss room - if (SpawnRoom(bossRoom, bossTargetPos.Value)) - { - _mainPath.Add(bossTargetPos.Value); - _connections.Add(new RoomConnection(currentPos, bossTargetPos.Value)); - } - } - - // private void SpawnRoomsBinaryTree() - // { - // var directions = new List - // { - // new Vector2I(0, -1), // North - // new Vector2I(0, 1), // South - // new Vector2I(1, 0), // East - // new Vector2I(-1, 0), // West - // }; - // - // //var origin = Vector2I.Zero; - // var tileSize = new Vector2(16, 16); - // var gridRoomSizeInTiles = new Vector2(20, 10); - // - // var starterRooms = StarterRooms.ToList(); - // var regularRooms = RegularRooms.ToList(); - // var bossRooms = BossRooms.ToList(); - // var starterRoom = starterRooms[GD.RandRange(0, starterRooms.Count - 1)]; - // - // var spawnedStartRoom = SpawnRoom(starterRoom, SpawnOrigin); - // - // Vector2I currentPos = SpawnOrigin; - // - // //MarkRoom(SpawnOrigin, starterRoom.Size, starterRoom); - // - // for (int i = 0; i < DungeonLength - 2; i++) - // { - // var nextPos = GetNextPosition(currentPos); - // if (nextPos == currentPos) break; - // - // var room = regularRooms[GD.RandRange(0, regularRooms.Count - 1)]; - // if (!SpawnRoom(room, nextPos)) break; // Skip on overlap - // - // _mainPath.Add(nextPos); - // _connections.Add(new RoomConnection(currentPos, nextPos)); - // currentPos = nextPos; - // } - // - // // Place Boss Room - // - // var bossRoom = bossRooms[GD.RandRange(0, bossRooms.Count - 1)]; - // var bossPos = GetNextPosition(currentPos); - // if (!SpawnRoom(bossRoom, bossPos)) break; // Skip on overlap - // SpawnRoom(bossRoom, bossPos); - // _mainPath.Add(bossPos); - // _connections.Add(new RoomConnection(currentPos, bossPos)); - // - // // Place Shop and Secret Rooms - // //PlaceSpecialRoom(RoomType.Shop); - // //PlaceSpecialRoom(RoomType.Secret); - // - // // Optionally, add branches - // for (int i = 0; i < MaxBranches; i++) - // { - // var branchStart = _mainPath[GD.RandRange(0, _mainPath.Count -1)]; - // var branchLength = GD.RandRange(1, MaxBranchLength); - // //_random.Next(1, MaxBranchLength + 1); - // var branchPos = branchStart; - // - // for (int j = 0; j < branchLength; j++) - // { - // var nextPos = GetNextPosition(branchPos); - // if (_roomGrid.ContainsKey(nextPos)) break; - // - // var room = regularRooms[GD.RandRange(0,regularRooms.Count -1)]; - // - // SpawnRoom(room, nextPos); - // _connections.Add(new RoomConnection(branchPos, nextPos)); - // branchPos = nextPos; - // } - // } - // - // // Spawn doors and walls - // foreach (var roomEntry in _roomGrid) - // { - // var room = roomEntry.Value; - // room.HandleDoors(pos => - // { - // var neighborPos = room.GridPosition + pos; - // return _roomGrid.ContainsKey(neighborPos); - // }); - // } - // } - private Vector2I? FindValidBossRoomLocation(Vector2I from, int maxSteps = 10) { var directions = new List { Vector2I.Right, Vector2I.Left, Vector2I.Up, Vector2I.Down }; @@ -361,10 +242,13 @@ public partial class RogueliteRoomManager : Node2D GameManager.Instance.RebakeNavigation(); } - private bool SpawnRoom(RogueliteRoomResource room, Vector2I gridPos) + private bool SpawnRoom(RogueliteRoomResource room, Vector2I gridPos, out RogueliteRoom spawnedRoom) { if (!CanPlaceRoom(gridPos, room.Size)) + { + spawnedRoom = null; return false; + } var position = gridPos * TileSize * RoomSizeInTiles; @@ -379,6 +263,7 @@ public partial class RogueliteRoomManager : Node2D //SpawnRoom(room, origin + (room.Size * new Vector2(i, j) * tileSize)); spawnedScene.Spawn(); MarkRoom(gridPos, room.Size, spawnedScene); + spawnedRoom = spawnedScene; return true; } diff --git a/Scripts/Controllers/RoomConnection.cs b/Scripts/Controllers/RoomConnection.cs index 1b88498e..94b0578a 100644 --- a/Scripts/Controllers/RoomConnection.cs +++ b/Scripts/Controllers/RoomConnection.cs @@ -9,6 +9,9 @@ public class RoomConnection public bool IsLocked; public bool IsSecret; + public Door FromDoor { get; set; } + public Door ToDoor { get; set; } + public RoomConnection(Vector2I from, Vector2I to, bool isLocked = false, bool isSecret = false) { From = from;