Add map loading logic and spawn rate control

This commit is contained in:
2025-09-30 02:32:03 -07:00
parent 70d1871985
commit 6f582fcca1
43 changed files with 470 additions and 118 deletions

View File

@@ -1,16 +0,0 @@
namespace Zennysoft.Ma.Adapter;
public enum Floor
{
Overworld,
Altar,
BossFloorA,
BossFloorB,
GoddessOfGuidanceFloor,
VoidRoom,
FinalFloor,
Floor01,
Floor02,
Floor03,
Floor04,
Floor05,
}

View File

@@ -17,6 +17,4 @@ public partial record PlayerData
[Meta, Id("map_data")] [Meta, Id("map_data")]
public partial record MapData public partial record MapData
{ {
[Save("floor_list")]
public required Dictionary<Floor, string> FloorScenes { get; init; }
} }

View File

@@ -28,4 +28,8 @@
<ProjectReference Include="..\Zennysoft.Game.Godot.Implementation\Zennysoft.Game.Implementation.csproj" /> <ProjectReference Include="..\Zennysoft.Game.Godot.Implementation\Zennysoft.Game.Implementation.csproj" />
</ItemGroup> </ItemGroup>
<ItemGroup>
<Folder Include="Map\" />
</ItemGroup>
</Project> </Project>

View File

@@ -0,0 +1,68 @@
using Godot;
using Godot.Collections;
using System.Linq;
using Zennysoft.Game.Ma;
[Tool]
public partial class DungeonFloorLayout : LayoutType
{
[Export]
public DungeonFloorSetType SetType
{
get => _setType;
set
{
_setType = value;
LayoutWithSpawnRate = [];
NotifyPropertyListChanged();
}
}
[ExportToolButton("Populate Map Data")]
public Callable PopulateMapList => Callable.From(() => PopulateDictionary(SetType));
[Export]
public Dictionary<string, float> LayoutWithSpawnRate { get; private set; }
[Export]
public Dictionary<EnemyType, float> EnemySpawnRates { get; set; } = default!;
private string _floorPath = "res://src/map/dungeon/floors/";
private DungeonFloorSetType _setType;
private void PopulateDictionary(DungeonFloorSetType setType)
{
var floorPath = _floorPath;
var floorType = string.Empty;
if (setType == DungeonFloorSetType.SetA)
floorType = "SetAFloors";
else if (setType == DungeonFloorSetType.SetB)
floorType = "SetBFloors";
var pathToScenes = $"{floorPath}/{floorType}";
var files = DirAccess.GetFilesAt(pathToScenes).Where(x => x.EndsWith(".tscn"));
var newMaps = new Dictionary<string, float>();
foreach (var file in files)
{
if (LayoutWithSpawnRate.ContainsKey($"{floorType}/{file}"))
{
var spawnRate = LayoutWithSpawnRate.TryGetValue($"{floorType}/{file}", out var currentSpawnRate);
newMaps.Add($"{floorType}/{file}", currentSpawnRate);
}
else
newMaps.Add($"{floorType}/{file}", 1.0f);
}
LayoutWithSpawnRate = newMaps;
NotifyPropertyListChanged();
}
public enum DungeonFloorSetType
{
SetA,
SetB
}
}

View File

@@ -0,0 +1 @@
uid://ci7o3nn4mdo8o

View File

@@ -0,0 +1,22 @@
#if TOOLS
using Godot;
using Zennysoft.Game.Ma;
[Tool]
public partial class DungeonFloorLayoutNode : EditorPlugin
{
public override void _EnterTree()
{
// Initialization of the plugin goes here.
var script = GD.Load<Script>("res://addons/dungeon_floor_layout/DungeonFloorLayout.cs");
var texture = GD.Load<Texture2D>("res://addons/dungeon_floor_layout/icon_door.png");
AddCustomType(nameof(DungeonFloorLayout), nameof(LayoutType), script, texture);
}
public override void _ExitTree()
{
// Clean-up of the plugin goes here.
RemoveCustomType(nameof(DungeonFloorLayout));
}
}
#endif

View File

@@ -0,0 +1 @@
uid://f87ejxatyy2n

Binary file not shown.

After

Width:  |  Height:  |  Size: 161 B

View File

@@ -0,0 +1,34 @@
[remap]
importer="texture"
type="CompressedTexture2D"
uid="uid://cgd2d4fusp4pg"
path="res://.godot/imported/icon_door.png-437da3e7d1cb55961e6afceef56e9ea2.ctex"
metadata={
"vram_texture": false
}
[deps]
source_file="res://addons/dungeon_floor_layout/icon_door.png"
dest_files=["res://.godot/imported/icon_door.png-437da3e7d1cb55961e6afceef56e9ea2.ctex"]
[params]
compress/mode=0
compress/high_quality=false
compress/lossy_quality=0.7
compress/hdr_compression=1
compress/normal_map=0
compress/channel_pack=0
mipmaps/generate=false
mipmaps/limit=-1
roughness/mode=0
roughness/src_normal=""
process/fix_alpha_border=true
process/premult_alpha=false
process/normal_map_invert_y=false
process/hdr_as_srgb=false
process/hdr_clamp_exposure=false
process/size_limit=0
detect_3d/compress_to=0

View File

@@ -0,0 +1,7 @@
[plugin]
name="Dungeon Floor Layout"
description=""
author="Zenny"
version=""
script="DungeonFloorLayoutNode.cs"

View File

@@ -0,0 +1,25 @@
using Godot;
using Zennysoft.Game.Ma;
[Tool]
public partial class SpecialFloorLayout : LayoutType
{
[Export]
public SpecialFloorType FloorName { get; set; }
public override void _EnterTree()
{
base._EnterTree();
}
public enum SpecialFloorType
{
Overworld,
Altar,
BossFloorA,
BossFloorB,
GoddessOfGuidanceFloor,
TrueGoddessOfGuidanceFloor,
FinalFloor
}
}

View File

@@ -0,0 +1 @@
uid://cabvj6s31iucg

View File

@@ -0,0 +1,23 @@
#if TOOLS
using Godot;
using System;
using Zennysoft.Game.Ma;
[Tool]
public partial class SpecialFloorLayoutNode : EditorPlugin
{
public override void _EnterTree()
{
// Initialization of the plugin goes here.
var script = GD.Load<Script>("res://addons/special_floor_layout_node/SpecialFloorLayout.cs");
var texture = GD.Load<Texture2D>("res://addons/special_floor_layout_node/icon_door.png");
AddCustomType(nameof(SpecialFloorLayout), nameof(LayoutType), script, texture);
}
public override void _ExitTree()
{
// Clean-up of the plugin goes here.
RemoveCustomType(nameof(SpecialFloorLayout));
}
}
#endif

View File

@@ -0,0 +1 @@
uid://uy8mygg0jtd8

Binary file not shown.

After

Width:  |  Height:  |  Size: 161 B

View File

@@ -0,0 +1,34 @@
[remap]
importer="texture"
type="CompressedTexture2D"
uid="uid://pxevsja7e3s0"
path="res://.godot/imported/icon_door.png-d7e4ac87b8cdfac1c9f03b9aff4c7e79.ctex"
metadata={
"vram_texture": false
}
[deps]
source_file="res://addons/special_floor_layout_node/icon_door.png"
dest_files=["res://.godot/imported/icon_door.png-d7e4ac87b8cdfac1c9f03b9aff4c7e79.ctex"]
[params]
compress/mode=0
compress/high_quality=false
compress/lossy_quality=0.7
compress/hdr_compression=1
compress/normal_map=0
compress/channel_pack=0
mipmaps/generate=false
mipmaps/limit=-1
roughness/mode=0
roughness/src_normal=""
process/fix_alpha_border=true
process/premult_alpha=false
process/normal_map_invert_y=false
process/hdr_as_srgb=false
process/hdr_clamp_exposure=false
process/size_limit=0
detect_3d/compress_to=0

View File

@@ -0,0 +1,7 @@
[plugin]
name="Special Floor Layout Node"
description=""
author="Zenny"
version=""
script="SpecialFloorLayoutNode.cs"

View File

@@ -41,7 +41,7 @@ project/assembly_name="Ma"
[editor_plugins] [editor_plugins]
enabled=PackedStringArray("res://addons/dialogue_manager/plugin.cfg") enabled=PackedStringArray("res://addons/dialogue_manager/plugin.cfg", "res://addons/dungeon_floor_layout/plugin.cfg", "res://addons/special_floor_layout_node/plugin.cfg")
[file_customization] [file_customization]

View File

@@ -1,12 +0,0 @@
using Godot;
namespace Zennysoft.Game.Ma;
public partial class EnemyDatabase : Node
{
[Export]
public PackedScene[] EnemyList;
[Export]
public float[] SpawnRate;
}

View File

@@ -0,0 +1,21 @@
namespace Zennysoft.Game.Ma;
public enum EnemyType
{
Sproingy,
Michael,
FilthEater,
Sara,
Ballos,
Chariot,
Chinthe,
AmbassadorGreen,
AmbassadorRed,
AmbassadorSteel,
AgniDemon,
AqueousDemon,
EdenPillar,
Palan,
ShieldOfHeaven,
GoldSproingy
}

View File

@@ -0,0 +1 @@
uid://bwqjmol68w6pr

View File

@@ -0,0 +1,56 @@
using Godot;
using System;
namespace Zennysoft.Game.Ma;
public static class EnemyTypeToEnemyConverter
{
private static readonly string _folderPath = "res://src/enemy/enemy_types";
public static Enemy Convert(EnemyType enemyType)
{
switch (enemyType)
{
case EnemyType.Sproingy:
return InstantiateFromPath(@$"{_folderPath}/01. sproingy/Sproingy.tscn");
case EnemyType.Michael:
return InstantiateFromPath(@$"{_folderPath}/02. michael/Michael.tscn");
case EnemyType.FilthEater:
return InstantiateFromPath(@$"{_folderPath}/03. filth_eater/FilthEater.tscn");
case EnemyType.Sara:
return InstantiateFromPath(@$"{_folderPath}/04. sara/Sara.tscn");
case EnemyType.Ballos:
return InstantiateFromPath(@$"{_folderPath}/05. ballos/Ballos.tscn");
case EnemyType.Chariot:
return InstantiateFromPath(@$"{_folderPath}/06. chariot/Chariot.tscn");
case EnemyType.Chinthe:
return InstantiateFromPath(@$"{_folderPath}/07. chinthe/Chinthe.tscn");
case EnemyType.AmbassadorGreen:
return InstantiateFromPath(@$"{_folderPath}/08a. Ambassador/Ambassador.tscn");
case EnemyType.AmbassadorRed:
return InstantiateFromPath(@$"{_folderPath}/08b. Ambassador (red)/AmbassadorRed.tscn");
case EnemyType.AmbassadorSteel:
return InstantiateFromPath(@$"{_folderPath}/08c. Ambassador (steel)/AmbassadorSteel.tscn");
case EnemyType.AgniDemon:
return InstantiateFromPath(@$"{_folderPath}/09. Agni/AgniDemon.tscn");
case EnemyType.AqueousDemon:
return InstantiateFromPath(@$"{_folderPath}/9b. Aqueos Demon/AqueosDemon.tscn");
case EnemyType.EdenPillar:
return InstantiateFromPath(@$"{_folderPath}/10. Eden Pillar/Eden Pillar.tscn");
case EnemyType.Palan:
return InstantiateFromPath(@$"{_folderPath}/11. Palan/Palan.tscn");
case EnemyType.ShieldOfHeaven:
return InstantiateFromPath(@$"{_folderPath}/12. Shield of Heaven/ShieldModelView.tscn");
case EnemyType.GoldSproingy:
return InstantiateFromPath(@$"{_folderPath}/13. gold sproingy/GoldSproingy.tscn");
default:
throw new NotImplementedException("Not supported");
}
}
private static Enemy InstantiateFromPath(string scenePath)
{
var enemyScene = GD.Load<PackedScene>(scenePath);
return enemyScene.Instantiate<Enemy>();
}
}

View File

@@ -0,0 +1 @@
uid://ctspbxmdle4vt

View File

@@ -17,6 +17,8 @@ public partial class Game : Node3D, IGame
{ {
public override void _Notification(int what) => this.Notify(what); public override void _Notification(int what) => this.Notify(what);
IGame IProvide<IGame>.Value() => this;
IGameRepo IProvide<IGameRepo>.Value() => GameRepo; IGameRepo IProvide<IGameRepo>.Value() => GameRepo;
IPlayer IProvide<IPlayer>.Value() => _player; IPlayer IProvide<IPlayer>.Value() => _player;
@@ -97,7 +99,6 @@ public partial class Game : Node3D, IGame
}, },
MapData = new MapData() MapData = new MapData()
{ {
FloorScenes = []
}, },
RescuedItems = new RescuedItemDatabase() RescuedItems = new RescuedItemDatabase()
{ {

View File

@@ -8,7 +8,7 @@ using Godot;
using System.Threading.Tasks; using System.Threading.Tasks;
using Zennysoft.Ma.Adapter; using Zennysoft.Ma.Adapter;
public interface IGame : IProvide<IGameRepo>, IProvide<IPlayer>, IProvide<IMap>, IProvide<ISaveChunk<GameData>>, INode3D public interface IGame : IProvide<IGame>, IProvide<IGameRepo>, IProvide<IPlayer>, IProvide<IMap>, IProvide<ISaveChunk<GameData>>, INode3D
{ {
void LoadExistingGame(); void LoadExistingGame();

View File

@@ -15,8 +15,6 @@ public interface IMap : INode3D, IProvide<ISaveChunk<MapInfo>>
Task LoadFloor(string sceneName); Task LoadFloor(string sceneName);
ImmutableDictionary<Floor, string> FloorScenes { get; }
IDungeonFloor CurrentFloor { get; } IDungeonFloor CurrentFloor { get; }
Transform3D GetPlayerSpawnPosition(); Transform3D GetPlayerSpawnPosition();

View File

@@ -0,0 +1,49 @@
using Godot;
using System;
using System.Linq;
using static SpecialFloorLayout;
namespace Zennysoft.Game.Ma;
public static class LayoutToScenePathConverter
{
private static readonly string _folderPath = "res://src/map/dungeon/floors";
public static string Convert(LayoutType layoutType)
{
if (layoutType is SpecialFloorLayout specialFloor)
{
var path = $"{_folderPath}/Special Floors/";
var files = DirAccess.GetFilesAt(path);
switch (specialFloor.FloorName)
{
case SpecialFloorType.Overworld:
return path + files.Single(x => x.Contains("Overworld.tscn"));
case SpecialFloorType.Altar:
return path + files.Single(x => x.Contains("Altar.tscn"));
case SpecialFloorType.BossFloorA:
return path + files.Single(x => x.Contains("Boss Floor A.tscn"));
case SpecialFloorType.BossFloorB:
return path + files.Single(x => x.Contains("Boss Floor B.tscn"));
case SpecialFloorType.GoddessOfGuidanceFloor:
return path + files.Single(x => x.Contains("Goddess of Guidance's Room.tscn"));
case SpecialFloorType.TrueGoddessOfGuidanceFloor:
return path + files.Single(x => x.Contains("Goddess of Guidance's Room - True Form.tscn"));
case SpecialFloorType.FinalFloor:
return path + files.Single(x => x.Contains("Final Floor.tscn"));
default:
throw new NotImplementedException();
}
}
else if (layoutType is DungeonFloorLayout dungeonFloor)
{
var rng = new RandomNumberGenerator();
rng.Randomize();
var index = (int)rng.RandWeighted([.. dungeonFloor.LayoutWithSpawnRate.Values]);
var result = dungeonFloor.LayoutWithSpawnRate.ElementAt(index);
return _folderPath + "/" + result.Key;
}
throw new NotImplementedException();
}
}

View File

@@ -0,0 +1 @@
uid://d0yn4yu7264c8

View File

@@ -0,0 +1,7 @@
using Godot;
namespace Zennysoft.Game.Ma;
public abstract partial class LayoutType : Node
{
}

View File

@@ -0,0 +1 @@
uid://csb6g5a657010

View File

@@ -36,9 +36,7 @@ public partial class Map : Node3D, IMap
#endregion #endregion
[Export] [Export]
private Array<Floor> Floors { get; set; } = default!; private Array<LayoutType> Floors { get; set; } = default!;
public ImmutableDictionary<Floor, string> FloorScenes { get; private set; }
public IDungeonFloor CurrentFloor { get; private set; } public IDungeonFloor CurrentFloor { get; private set; }
@@ -57,14 +55,6 @@ public partial class Map : Node3D, IMap
public void InitializeMapData() public void InitializeMapData()
{ {
FloorScenes = ImmutableDictionary<Floor, string>.Empty
.Add(Floor.Overworld, _floorFilePath + "Special Floors/Overworld.tscn")
.Add(Floor.Altar, _floorFilePath + "Special Floors/Altar.tscn")
.Add(Floor.BossFloorA, _floorFilePath + "Special Floors/15. Boss Floor A.tscn")
.Add(Floor.BossFloorB, _floorFilePath + "Special Floors/34. Boss Floor B.tscn")
.Add(Floor.GoddessOfGuidanceFloor, _floorFilePath + "Special Floors/35. Goddess of Guidance's Room.tscn")
.Add(Floor.VoidRoom, _floorFilePath + "Set B/30. Void Room.tscn")
.Add(Floor.FinalFloor, _floorFilePath + "Special Floors/36. Final Floor.tscn");
CurrentFloorNumber.OnNext(0); CurrentFloorNumber.OnNext(0);
} }
@@ -79,9 +69,11 @@ public partial class Map : Node3D, IMap
public async Task LoadFloor() public async Task LoadFloor()
{ {
GetMapFiles(); var floor = GetChildren().OfType<LayoutType>().ElementAt(CurrentFloorNumber.Value);
FloorScenes.TryGetValue(Floors.First(), out var sceneToLoad); var sceneToLoad = LayoutToScenePathConverter.Convert(floor);
await LoadFloor(sceneToLoad); await LoadFloor(sceneToLoad);
if (CurrentFloor is DungeonFloor dungeonFloor && floor is DungeonFloorLayout dungeonFloorLayout)
dungeonFloor.SpawnEnemies(dungeonFloorLayout.EnemySpawnRates);
} }
public async Task LoadFloor(string sceneName) public async Task LoadFloor(string sceneName)
@@ -125,10 +117,4 @@ public partial class Map : Node3D, IMap
var transform = GetPlayerSpawnPosition(); var transform = GetPlayerSpawnPosition();
Player.TeleportPlayer(transform); Player.TeleportPlayer(transform);
} }
private string[] GetMapFiles()
{
var folderLocation = _floorFilePath + "Floor" + CurrentFloorNumber.Value.ToString("D2");
return DirAccess.GetFilesAt(folderLocation);
}
} }

View File

@@ -1,6 +1,8 @@
[gd_scene load_steps=6 format=3 uid="uid://by67pn7fdsg1m"] [gd_scene load_steps=8 format=3 uid="uid://by67pn7fdsg1m"]
[ext_resource type="Script" uid="uid://14e8mu48ed4" path="res://src/map/Map.cs" id="1_bw70o"] [ext_resource type="Script" uid="uid://14e8mu48ed4" path="res://src/map/Map.cs" id="1_bw70o"]
[ext_resource type="Script" uid="uid://cabvj6s31iucg" path="res://addons/special_floor_layout_node/SpecialFloorLayout.cs" id="2_00xd7"]
[ext_resource type="Script" uid="uid://ci7o3nn4mdo8o" path="res://addons/dungeon_floor_layout/DungeonFloorLayout.cs" id="3_v14r0"]
[sub_resource type="Animation" id="Animation_00xd7"] [sub_resource type="Animation" id="Animation_00xd7"]
length = 0.001 length = 0.001
@@ -54,9 +56,9 @@ _data = {
&"fade_out": SubResource("Animation_v14r0") &"fade_out": SubResource("Animation_v14r0")
} }
[node name="Map" type="Node3D"] [node name="Map" type="Node3D" node_paths=PackedStringArray("Floors")]
script = ExtResource("1_bw70o") script = ExtResource("1_bw70o")
Floors = Array[int]([3, 2, 0, 4, 1, 5, 6]) Floors = [3, 2, 0, 4, 1, 5, 6]
[node name="ColorRect" type="ColorRect" parent="."] [node name="ColorRect" type="ColorRect" parent="."]
anchors_preset = 15 anchors_preset = 15
@@ -71,3 +73,28 @@ unique_name_in_owner = true
libraries = { libraries = {
&"": SubResource("AnimationLibrary_00xd7") &"": SubResource("AnimationLibrary_00xd7")
} }
[node name="Overworld (Add SpecialFloorLayout node for special floors and pick the FloorName from the list)" type="Node" parent="."]
script = ExtResource("2_00xd7")
metadata/_custom_type_script = "uid://cabvj6s31iucg"
[node name="Altar (Arrange order of nodes to change default load order)" type="Node" parent="."]
script = ExtResource("2_00xd7")
FloorName = 1
metadata/_custom_type_script = "uid://cabvj6s31iucg"
[node name="Floor01 (Press Populate Map Data Button to load floor from SetAFloors folder)" type="Node" parent="."]
script = ExtResource("3_v14r0")
LayoutWithSpawnRate = Dictionary[String, float]({})
EnemySpawnRates = {
0: 1.0
}
metadata/_custom_type_script = "uid://ci7o3nn4mdo8o"
[node name="Floor02 (Add DungeonFloorLayout node for regular dungeon floors)" type="Node" parent="."]
script = ExtResource("3_v14r0")
LayoutWithSpawnRate = Dictionary[String, float]({})
EnemySpawnRates = {
0: 1.0
}
metadata/_custom_type_script = "uid://ci7o3nn4mdo8o"

View File

@@ -12,8 +12,6 @@ public partial class DungeonFloor : Node3D, IDungeonFloor
{ {
public override void _Notification(int what) => this.Notify(what); public override void _Notification(int what) => this.Notify(what);
[Node] public EnemyDatabase EnemyDatabase { get; set; } = default!;
private Transform3D _playerSpawnPoint; private Transform3D _playerSpawnPoint;
public ImmutableList<IDungeonRoom> Rooms { get; private set; } public ImmutableList<IDungeonRoom> Rooms { get; private set; }
@@ -25,12 +23,16 @@ public partial class DungeonFloor : Node3D, IDungeonFloor
Rooms = []; Rooms = [];
Rooms = FindAllDungeonRooms([.. GetChildren()], Rooms); Rooms = FindAllDungeonRooms([.. GetChildren()], Rooms);
_playerSpawnPoint = RandomizePlayerSpawnPoint(); _playerSpawnPoint = RandomizePlayerSpawnPoint();
var monsterRooms = Rooms.OfType<MonsterRoom>();
foreach (var room in monsterRooms)
room.SpawnEnemies(EnemyDatabase);
} }
public Transform3D GetPlayerSpawnPoint() => new Transform3D(_playerSpawnPoint.Basis, new Vector3(_playerSpawnPoint.Origin.X, -1.75f, _playerSpawnPoint.Origin.Z)); public void SpawnEnemies(Godot.Collections.Dictionary<EnemyType, float> enemyInfo)
{
var monsterRooms = Rooms.OfType<MonsterRoom>();
foreach (var room in monsterRooms)
room.SpawnEnemies(enemyInfo);
}
public Transform3D GetPlayerSpawnPoint() => new Transform3D(_playerSpawnPoint.Basis, new Vector3(_playerSpawnPoint.Origin.X, 0f, _playerSpawnPoint.Origin.Z));
private Transform3D RandomizePlayerSpawnPoint() private Transform3D RandomizePlayerSpawnPoint()
{ {

View File

@@ -11,8 +11,6 @@ public abstract partial class DungeonRoom : Node3D, IDungeonRoom
{ {
public override void _Notification(int what) => this.Notify(what); public override void _Notification(int what) => this.Notify(what);
[Node] public Marker3D PlayerSpawn { get; set; } = default!;
[Node] private MeshInstance3D _minimap { get; set; } = default!; [Node] private MeshInstance3D _minimap { get; set; } = default!;
public bool IsPlayerInRoom => _isPlayerInRoom; public bool IsPlayerInRoom => _isPlayerInRoom;
@@ -30,8 +28,8 @@ public abstract partial class DungeonRoom : Node3D, IDungeonRoom
public void Setup() public void Setup()
{ {
_enemiesInRoom = []; _enemiesInRoom = [];
_room.BodyEntered += Room_BodyEntered; //_room.BodyEntered += Room_BodyEntered;
_room.BodyExited += Room_BodyExited; //_room.BodyExited += Room_BodyExited;
} }
private void Room_BodyExited(Node3D body) private void Room_BodyExited(Node3D body)

View File

@@ -14,17 +14,19 @@ public partial class ExitRoom : DungeonRoom
[Node] private Area3D _exit { get; set; } = default!; [Node] private Area3D _exit { get; set; } = default!;
[Node] public Marker3D PlayerSpawn { get; set; } = default!;
public override void _Ready() public override void _Ready()
{ {
_exit.AreaEntered += Exit_AreaEntered; _exit.AreaEntered += Exit_AreaEntered;
} }
public void ExitReached() public void ExitReached()
=> Game.FloorExitReached(); => Game.FloorExitReached();
private void Exit_AreaEntered(Area3D area) private void Exit_AreaEntered(Area3D area)
{ {
if (area.GetOwner() is IPlayer) if (area.GetOwner() is IPlayer)
ExitReached(); ExitReached();
} }
} }

View File

@@ -5,8 +5,6 @@ using System.Collections.Immutable;
namespace Zennysoft.Game.Ma; namespace Zennysoft.Game.Ma;
public interface IDungeonRoom : INode3D public interface IDungeonRoom : INode3D
{ {
Marker3D PlayerSpawn { get; }
bool IsPlayerInRoom { get; } bool IsPlayerInRoom { get; }
bool PlayerDiscoveredRoom { get; } bool PlayerDiscoveredRoom { get; }

View File

@@ -15,51 +15,57 @@ public partial class MonsterRoom : DungeonRoom
[Node] public Node3D EnemySpawnPoints { get; set; } = default!; [Node] public Node3D EnemySpawnPoints { get; set; } = default!;
[Node] public Marker3D PlayerSpawn { get; set; } = default!;
[Node] public ItemDatabase ItemDatabase { get; set; } = default!; [Node] public ItemDatabase ItemDatabase { get; set; } = default!;
public override void _Ready() public override void _Ready()
{ {
SpawnItems(); SpawnItems();
} }
public void SpawnEnemies(EnemyDatabase enemyDatabase) public void SpawnEnemies(Godot.Collections.Dictionary<EnemyType, float> enemyInfo)
{ {
var rng = new RandomNumberGenerator(); var rng = new RandomNumberGenerator();
rng.Randomize(); rng.Randomize();
var enemySpawnPoints = EnemySpawnPoints.GetChildren(); var enemySpawnPoints = EnemySpawnPoints.GetChildren();
var numberOfEnemiesToSpawn = rng.RandiRange(1, enemySpawnPoints.Count); var numberOfEnemiesToSpawn = rng.RandiRange(1, enemySpawnPoints.Count);
foreach (var spawnPoint in enemySpawnPoints.Cast<Marker3D>()) foreach (var spawnPoint in enemySpawnPoints.Cast<Marker3D>())
{ {
if (numberOfEnemiesToSpawn <= 0) if (numberOfEnemiesToSpawn <= 0)
break; break;
numberOfEnemiesToSpawn--; numberOfEnemiesToSpawn--;
var enemy = enemyDatabase.EnemyList[rng.RandWeighted(enemyDatabase.SpawnRate)]; var index = rng.RandWeighted([.. enemyInfo.Values]);
var instantiatedEnemy = enemy.Instantiate<Enemy>(); var selectedEnemy = enemyInfo.ElementAt((int)index);
instantiatedEnemy.Position = new Vector3(spawnPoint.Position.X, 0, spawnPoint.Position.Z); var instantiatedEnemy = EnemyTypeToEnemyConverter.Convert(selectedEnemy.Key);
AddChild(instantiatedEnemy); instantiatedEnemy.Position = new Vector3(spawnPoint.Position.X, 1.75f, spawnPoint.Position.Z);
} AddChild(instantiatedEnemy);
}
} }
private void SpawnItems() private void SpawnItems()
{ {
var itemSpawnPoints = ItemSpawnPoints.GetChildren(); if (ItemSpawnPoints == null)
var rng = new RandomNumberGenerator(); return;
rng.Randomize();
var numberOfItemsToSpawn = rng.RandiRange(1, itemSpawnPoints.Count);
itemSpawnPoints.Shuffle();
var database = new ItemDatabase();
foreach (var spawnPoint in itemSpawnPoints.Cast<Marker3D>())
{
if (numberOfItemsToSpawn <= 0)
break;
numberOfItemsToSpawn--;
var selectedItem = database.PickItem<InventoryItem>(); var itemSpawnPoints = ItemSpawnPoints.GetChildren();
var duplicated = selectedItem.Duplicate((int)DuplicateFlags.UseInstantiation) as Node3D; var rng = new RandomNumberGenerator();
duplicated.Position = new Vector3(spawnPoint.Position.X, -1.5f, spawnPoint.Position.Z); rng.Randomize();
AddChild(duplicated); var numberOfItemsToSpawn = rng.RandiRange(1, itemSpawnPoints.Count);
} itemSpawnPoints.Shuffle();
var database = new ItemDatabase();
foreach (var spawnPoint in itemSpawnPoints.Cast<Marker3D>())
{
if (numberOfItemsToSpawn <= 0)
break;
numberOfItemsToSpawn--;
var selectedItem = database.PickItem<InventoryItem>();
var duplicated = selectedItem.Duplicate((int)DuplicateFlags.UseInstantiation) as Node3D;
duplicated.Position = new Vector3(spawnPoint.Position.X, -1.5f, spawnPoint.Position.Z);
AddChild(duplicated);
}
} }
} }

View File

@@ -1,8 +0,0 @@
[gd_resource type="Resource" script_class="MapInfo" load_steps=2 format=3 uid="uid://c0764jocyuon4"]
[ext_resource type="Script" uid="uid://bb4a0ijn0q2ou" path="res://src/map/MapInfo.cs" id="1_hrh8w"]
[resource]
script = ExtResource("1_hrh8w")
MapNameAndOdds = Dictionary[String, float]({})
metadata/_custom_type_script = "uid://bb4a0ijn0q2ou"

View File

@@ -1,10 +1,9 @@
[gd_scene load_steps=16 format=3 uid="uid://dpec2lbt83dhe"] [gd_scene load_steps=15 format=3 uid="uid://dpec2lbt83dhe"]
[ext_resource type="Script" uid="uid://dhollu4j3pynq" path="res://src/map/dungeon/code/MonsterRoom.cs" id="1_312b8"] [ext_resource type="Script" uid="uid://dhollu4j3pynq" path="res://src/map/dungeon/code/MonsterRoom.cs" id="1_312b8"]
[ext_resource type="PackedScene" uid="uid://bvpwkx6ag1yf4" path="res://src/map/dungeon/models/Area 1/Antechamber/A1-Antechamber.glb" id="2_kycn2"] [ext_resource type="PackedScene" uid="uid://bvpwkx6ag1yf4" path="res://src/map/dungeon/models/Area 1/Antechamber/A1-Antechamber.glb" id="2_kycn2"]
[ext_resource type="Texture2D" uid="uid://ncu0fsnqyede" path="res://src/minimap/textures/Room Maps/mi_antechamber.png" id="6_0ndak"] [ext_resource type="Texture2D" uid="uid://ncu0fsnqyede" path="res://src/minimap/textures/Room Maps/mi_antechamber.png" id="6_0ndak"]
[ext_resource type="PackedScene" uid="uid://twrj4wixcbu7" path="res://src/items/ItemDatabase.tscn" id="17_25wvm"] [ext_resource type="PackedScene" uid="uid://twrj4wixcbu7" path="res://src/items/ItemDatabase.tscn" id="17_25wvm"]
[ext_resource type="Texture2D" uid="uid://bkvegamuqdsdd" path="res://src/map/dungeon/corridors/Corridor A/CORRIDOR test_FLOOR1.jpg" id="17_jig7d"]
[ext_resource type="Texture2D" uid="uid://del2dfj3etokd" path="res://src/map/assets/Blocked Door A1 .png" id="20_le1vp"] [ext_resource type="Texture2D" uid="uid://del2dfj3etokd" path="res://src/map/assets/Blocked Door A1 .png" id="20_le1vp"]
[sub_resource type="BoxShape3D" id="BoxShape3D_phhs1"] [sub_resource type="BoxShape3D" id="BoxShape3D_phhs1"]
@@ -21,10 +20,8 @@ albedo_texture = ExtResource("20_le1vp")
texture_filter = 0 texture_filter = 0
[sub_resource type="StandardMaterial3D" id="StandardMaterial3D_b605t"] [sub_resource type="StandardMaterial3D" id="StandardMaterial3D_b605t"]
albedo_texture = ExtResource("17_jig7d")
[sub_resource type="StandardMaterial3D" id="StandardMaterial3D_cnaww"] [sub_resource type="StandardMaterial3D" id="StandardMaterial3D_cnaww"]
albedo_texture = ExtResource("17_jig7d")
[sub_resource type="BoxShape3D" id="BoxShape3D_e81mq"] [sub_resource type="BoxShape3D" id="BoxShape3D_e81mq"]
size = Vector3(20, 8, 16) size = Vector3(20, 8, 16)
@@ -169,7 +166,8 @@ shape = SubResource("BoxShape3D_e81mq")
[node name="Minimap" type="Node3D" parent="."] [node name="Minimap" type="Node3D" parent="."]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -0.61193, 0.859072, 0) transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -0.61193, 0.859072, 0)
[node name="mi_antechamber" type="MeshInstance3D" parent="Minimap"] [node name="Minimap" type="MeshInstance3D" parent="Minimap"]
unique_name_in_owner = true
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, -0.983959, 0) transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, -0.983959, 0)
layers = 2 layers = 2
mesh = SubResource("PlaneMesh_7ppra") mesh = SubResource("PlaneMesh_7ppra")

View File

@@ -111,3 +111,8 @@ popup/item_13/text = "Shield of Heaven"
popup/item_13/id = 13 popup/item_13/id = 13
popup/item_14/text = "Gold Sproingy" popup/item_14/text = "Gold Sproingy"
popup/item_14/id = 14 popup/item_14/id = 14
[node name="LoadNextFloorButton" type="Button" parent="MarginContainer/VBoxContainer/HBoxContainer/VFlowContainer"]
unique_name_in_owner = true
layout_mode = 2
text = "Load Next Floor"

View File

@@ -23,6 +23,8 @@ public partial class PauseDebugMenu : Control, IDebugMenu
[Node] public OptionButton SpawnEnemyDropDown { get; set; } = default!; [Node] public OptionButton SpawnEnemyDropDown { get; set; } = default!;
[Node] public Button LoadNextFloorButton { get; set; } = default!;
private readonly string _floorFilePath = @"res://src/map/dungeon/floors/"; private readonly string _floorFilePath = @"res://src/map/dungeon/floors/";
private readonly string _enemyFilePath = @"res://src/enemy/enemy_types"; private readonly string _enemyFilePath = @"res://src/enemy/enemy_types";
@@ -33,6 +35,8 @@ public partial class PauseDebugMenu : Control, IDebugMenu
public override void _Ready() public override void _Ready()
{ {
LoadNextFloorButton.Pressed += LoadNextFloorButton_Pressed;
_itemDatabase = new ItemDatabase(); _itemDatabase = new ItemDatabase();
_spawnableItems = _itemDatabase.Items; _spawnableItems = _itemDatabase.Items;