Door connections

This commit is contained in:
Marco 2025-04-16 18:18:52 +02:00
commit 700be1e207
11 changed files with 127 additions and 149 deletions

View file

@ -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")

View file

@ -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")

View file

@ -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")

View file

@ -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

View file

@ -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

View file

@ -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"]

View file

@ -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"]

View file

@ -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)

View file

@ -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<string, Vector2I> DirectionMap = new()
private static readonly Godot.Collections.Dictionary<string, Vector2I> 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<Door> _doors = [];
private List<RoomConnection> _connections = [];
private List<EnemyFSMProxy> _enemies = [];
private Array<EnemyResource> 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;
@ -101,14 +116,6 @@ public partial class RogueliteRoom : Node2D
// marker.AddChild(label);
}
// PackedScene prefab = hasConnection
// ? GD.Load<PackedScene>("res://Prefabs/Door.tscn")
// : GD.Load<PackedScene>("res://Prefabs/Wall.tscn");
//
// var instance = prefab.Instantiate<Node2D>();
// 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<PackedScene>(e.PrefabPath);
var spawnedEnemy = spawner.CreateChild<Node2D>(enemyScene);
var spawnedEnemy = spawner.CreateChild<EnemyFSMProxy>(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;
}
}

View file

@ -87,7 +87,7 @@ 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();
@ -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<Vector2I>
// {
// 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> { 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;
}

View file

@ -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;