diff --git a/data/levelGraphs/test.json b/data/levelGraphs/test.json index 556d872..4682698 100644 --- a/data/levelGraphs/test.json +++ b/data/levelGraphs/test.json @@ -5,30 +5,6 @@ "ToId": "4ae948ea-82b7-4b2d-bec2-19ed8a9d4c03", "FromPort": 0, "ToPort": 0 - }, - { - "FromId": "4ae948ea-82b7-4b2d-bec2-19ed8a9d4c03", - "ToId": "c604e1ef-f3e7-4189-a6f7-ab2bd23d5130", - "FromPort": 0, - "ToPort": 0 - }, - { - "FromId": "4ae948ea-82b7-4b2d-bec2-19ed8a9d4c03", - "ToId": "bb9da3f9-911f-465f-af2e-be1325f17d15", - "FromPort": 0, - "ToPort": 0 - }, - { - "FromId": "c604e1ef-f3e7-4189-a6f7-ab2bd23d5130", - "ToId": "c9e860b9-e001-4b9b-9e6d-adf132beb64f", - "FromPort": 0, - "ToPort": 0 - }, - { - "FromId": "bb9da3f9-911f-465f-af2e-be1325f17d15", - "ToId": "c9e860b9-e001-4b9b-9e6d-adf132beb64f", - "FromPort": 0, - "ToPort": 0 } ], "RoomNodeDataList": [ @@ -37,7 +13,7 @@ "Title": "起点房间", "Description": "测试的起点房间。", "RoomTemplateSet": [ - "res://prefab/roomTemplates/dungeon/initialRoom.tscn" + "res://prefab/roomTemplates/dungeon" ], "Tags": [ "StartingRoom" @@ -51,33 +27,6 @@ "res://prefab/roomTemplates/dungeon/utilityRoom.tscn" ], "Tags": null - }, - { - "Id": "c604e1ef-f3e7-4189-a6f7-ab2bd23d5130", - "Title": "普通房间1", - "Description": "测试使用。", - "RoomTemplateSet": [ - "res://prefab/roomTemplates/dungeon" - ], - "Tags": null - }, - { - "Id": "bb9da3f9-911f-465f-af2e-be1325f17d15", - "Title": "普通房间2", - "Description": "测试使用。", - "RoomTemplateSet": [ - "res://prefab/roomTemplates/dungeon" - ], - "Tags": null - }, - { - "Id": "c9e860b9-e001-4b9b-9e6d-adf132beb64f", - "Title": "Boos房间", - "Description": "关卡结束的地方。", - "RoomTemplateSet": [ - "res://prefab/roomTemplates/dungeon" - ], - "Tags": null } ] } \ No newline at end of file diff --git a/locals/Error.csv b/locals/Error.csv index add28cc..5fb7974 100644 --- a/locals/Error.csv +++ b/locals/Error.csv @@ -6,4 +6,5 @@ width_or_height_of_room_slot_must_be_1,房间槽的宽度或高度必须为1。, connected_room_timeout,连接房间超时。,Connecting the room timed out.,接続部屋はタイムアウトです。 projectiles_is_empty,未设置抛射体。,The projectile is not set.,射出体は設置されていません。 map_generator_missing_parameters,地图生成器缺少参数。,Map generator missing parameters.,マップジェネレータが不足しています。 -map_generator_attempts_to_parse_empty_layout_diagrams,地图生成器尝试解析空的布局图。,Map generator attempts to parse empty layout diagrams.,マップジェネレータは空のレイアウト図を解析しようとしています。 \ No newline at end of file +map_generator_attempts_to_parse_empty_layout_diagrams,地图生成器尝试解析空的布局图。,Map generator attempts to parse empty layout diagrams.,マップジェネレータは空のレイアウト図を解析しようとしています。 +map_generator_has_no_starting_room_data,地图生成器没有起点房间数据。,Map generator has no starting room data.,マップ生成器に起点部屋データはありません。 \ No newline at end of file diff --git a/locals/Error.en.translation b/locals/Error.en.translation index 262938c..64b0440 100644 Binary files a/locals/Error.en.translation and b/locals/Error.en.translation differ diff --git a/locals/Error.jp.translation b/locals/Error.jp.translation index 50361d6..1192517 100644 Binary files a/locals/Error.jp.translation and b/locals/Error.jp.translation differ diff --git a/locals/Error.zh.translation b/locals/Error.zh.translation index 6e08a5e..b15f408 100644 Binary files a/locals/Error.zh.translation and b/locals/Error.zh.translation differ diff --git a/scripts/loader/sceneLoader/GameSceneLoader.cs b/scripts/loader/sceneLoader/GameSceneLoader.cs index 2e6e057..3d4969b 100644 --- a/scripts/loader/sceneLoader/GameSceneLoader.cs +++ b/scripts/loader/sceneLoader/GameSceneLoader.cs @@ -4,6 +4,7 @@ using ColdMint.scripts.map; using ColdMint.scripts.map.LayoutParsingStrategy; using ColdMint.scripts.map.layoutStrategy; using ColdMint.scripts.map.RoomPlacer; +using ColdMint.scripts.utils; using Godot; namespace ColdMint.scripts.loader.sceneLoader; @@ -33,6 +34,7 @@ public partial class GameSceneLoader : SceneLoaderTemplate MapGenerator.LayoutStrategy = new TestLayoutStrategy(); MapGenerator.LayoutParsingStrategy = new SequenceLayoutParsingStrategy(); MapGenerator.RoomPlacementStrategy = new PatchworkRoomPlacementStrategy(); + MapGenerator.Seed = GuidUtils.GetGuid(); await MapGenerator.GenerateMap(); } } \ No newline at end of file diff --git a/scripts/map/LayoutParsingStrategy/ILayoutParsingStrategy.cs b/scripts/map/LayoutParsingStrategy/ILayoutParsingStrategy.cs index 78cee3f..483f7fe 100644 --- a/scripts/map/LayoutParsingStrategy/ILayoutParsingStrategy.cs +++ b/scripts/map/LayoutParsingStrategy/ILayoutParsingStrategy.cs @@ -15,7 +15,14 @@ public interface ILayoutParsingStrategy /// /// public void SetLevelGraph(LevelGraphEditorSaveData levelGraphEditorSaveData); - + + /// + /// Gets data for the start room node + /// 获取起始房间节点的数据 + /// + /// + public Task GetStartRoomNodeData(); + /// /// Gets the next room to place /// 获取下一个要放置的房间 @@ -29,7 +36,7 @@ public interface ILayoutParsingStrategy /// /// public Task GetNextParentNodeId(); - + /// /// Is there another room that needs to be placed /// 是否还有下一个需要放置的房间 diff --git a/scripts/map/LayoutParsingStrategy/SequenceLayoutParsingStrategy.cs b/scripts/map/LayoutParsingStrategy/SequenceLayoutParsingStrategy.cs index 73b9a2e..0d8a6f5 100644 --- a/scripts/map/LayoutParsingStrategy/SequenceLayoutParsingStrategy.cs +++ b/scripts/map/LayoutParsingStrategy/SequenceLayoutParsingStrategy.cs @@ -1,4 +1,5 @@ using System.Collections.Generic; +using System.Linq; using System.Threading.Tasks; using ColdMint.scripts.levelGraphEditor; @@ -23,19 +24,17 @@ public class SequenceLayoutParsingStrategy : ILayoutParsingStrategy //设置数据时,是否检查合法了 private bool _checkLegality; - //A special marker for the starting room. - //特殊标记,代表起始房间。 - private const int StartingRoomIndex = -1; //The connection index of the query //查询的连接索引 - private int _index = StartingRoomIndex; + private int _index; private int _maxIndex; private Dictionary _roomNodeDataDictionary = new Dictionary(); public void SetLevelGraph(LevelGraphEditorSaveData levelGraphEditorSaveData) { - _index = StartingRoomIndex; + _checkLegality = false; + _index = -1; _levelGraphEditorSaveData = levelGraphEditorSaveData; if (_levelGraphEditorSaveData.RoomNodeDataList == null || _levelGraphEditorSaveData.RoomNodeDataList.Count == 0) { @@ -51,7 +50,7 @@ public class SequenceLayoutParsingStrategy : ILayoutParsingStrategy } else { - _maxIndex = _levelGraphEditorSaveData.ConnectionDataList.Count - 1; + _maxIndex = _levelGraphEditorSaveData.ConnectionDataList.Count; } _roomNodeDataDictionary.Clear(); @@ -64,66 +63,34 @@ public class SequenceLayoutParsingStrategy : ILayoutParsingStrategy _roomNodeDataDictionary.Add(roomNodeData.Id, roomNodeData); } - - //Check that the first room is the starting room. - //检查首个房间是否为起始房间。 - var firstRoom = GetFirstRoom(_levelGraphEditorSaveData); - if (firstRoom == null) - { - return; - } - - _checkLegality = firstRoom.HasTag(Config.RoomDataTag.StartingRoom); + _checkLegality = true; } - /// - /// Get the first room - /// 获取第一个房间 - /// - /// - /// - private RoomNodeData? GetFirstRoom(LevelGraphEditorSaveData? levelGraphEditorSaveData) + public Task GetStartRoomNodeData() { - if (levelGraphEditorSaveData == null || levelGraphEditorSaveData.RoomNodeDataList == null || - levelGraphEditorSaveData.RoomNodeDataList.Count == 0) + if (_levelGraphEditorSaveData == null) { - return null; + return Task.FromResult(null); } - RoomNodeData? firstRoom = null; - if (levelGraphEditorSaveData.ConnectionDataList == null || - levelGraphEditorSaveData.ConnectionDataList.Count == 0) + if (_levelGraphEditorSaveData.RoomNodeDataList == null || _levelGraphEditorSaveData.RoomNodeDataList.Count == 0) { - //If there is no connection information, then fetch the first room in the RoomNodeDataList. - //如果没有连接信息,那么在RoomNodeDataList内取出第一个房间。 - firstRoom = levelGraphEditorSaveData.RoomNodeDataList[0]; - } - else - { - //If there is connection information, then fetch the first connected From room in the ConnectionDataList. - //如果有连接信息,那么在ConnectionDataList内取出第一个连接的From房间。 - var firstConnection = levelGraphEditorSaveData.ConnectionDataList[0]; - if (firstConnection.FromId == null) - { - return firstRoom; - } - - if (_roomNodeDataDictionary.TryGetValue(firstConnection.FromId, out var value)) - { - firstRoom = value; - } + //If there is no room data in the level map set. + //如果设置的关卡图内没有房间数据。 + return Task.FromResult(null); } - return firstRoom; + foreach (var roomNodeData in _levelGraphEditorSaveData.RoomNodeDataList.Where(roomNodeData => + roomNodeData.HasTag(Config.RoomDataTag.StartingRoom))) + { + return Task.FromResult(roomNodeData); + } + + return Task.FromResult(null); } public Task Next() { - if (_index == StartingRoomIndex) - { - return Task.FromResult(GetFirstRoom(_levelGraphEditorSaveData)); - } - var connectionData = GetIndexOfConnectionData(_index); if (connectionData == null) { @@ -167,13 +134,6 @@ public class SequenceLayoutParsingStrategy : ILayoutParsingStrategy public Task GetNextParentNodeId() { - if (_index == StartingRoomIndex) - { - //The start room will not have a parent node. - //起始房间不会有父节点。 - return Task.FromResult(null); - } - var connectionData = GetIndexOfConnectionData(_index); if (connectionData == null) { @@ -202,14 +162,7 @@ public class SequenceLayoutParsingStrategy : ILayoutParsingStrategy return Task.FromResult(false); } - if (_index == StartingRoomIndex) - { - //The start room is always considered to have the next room, in order to handle situations where levelGraphEditorSaveData has only room data and no connection data. - //起始房间始终被认为有下一个房间,这是为了处理levelGraphEditorSaveData仅有房间数据,没有连接数据的情况。 - _index++; - return Task.FromResult(true); - } - + _index++; return Task.FromResult(_index < _maxIndex); } } \ No newline at end of file diff --git a/scripts/map/MapGenerator.cs b/scripts/map/MapGenerator.cs index f78599b..54ec660 100644 --- a/scripts/map/MapGenerator.cs +++ b/scripts/map/MapGenerator.cs @@ -1,10 +1,13 @@ using System.Collections.Generic; using System.Threading.Tasks; using ColdMint.scripts.debug; +using ColdMint.scripts.levelGraphEditor; +using ColdMint.scripts.map.dateBean; using ColdMint.scripts.map.interfaces; using ColdMint.scripts.map.LayoutParsingStrategy; using ColdMint.scripts.map.layoutStrategy; using ColdMint.scripts.map.room; +using ColdMint.scripts.utils; using Godot; namespace ColdMint.scripts.map; @@ -37,6 +40,19 @@ public static class MapGenerator /// private static IRoomPlacementStrategy? _roomPlacementStrategy; + private static ulong _seed; + + /// + /// Set seed + /// 设置种子 + /// + public static string Seed + { + get => _seed.ToString(); + //If the player inputs integers, we seed them directly with the input values. If it is not an integer, the hash value is taken. + //如果玩家输入的是整数,那么我们直接用输入值作为种子。如果不是整数,则取哈希值。 + set => _seed = ulong.TryParse(value, out var result) ? result : HashCodeUtils.GetFixedHashCode(value); + } /// /// Layout diagram parsing policy @@ -95,12 +111,36 @@ public static class MapGenerator //Save the dictionary, put the ID in the room data, corresponding to the successful placement of the room. //保存字典,将房间数据内的ID,对应放置成功的房间。 var roomDictionary = new Dictionary(); + var randomNumberGenerator = new RandomNumberGenerator(); + randomNumberGenerator.Seed = _seed; + LogCat.Log("Seed:" + _seed); + var startRoomNodeData = await _layoutParsingStrategy.GetStartRoomNodeData(); + if (startRoomNodeData == null || string.IsNullOrEmpty(startRoomNodeData.Id)) + { + LogCat.LogError("map_generator_has_no_starting_room_data"); + return; + } + + var startingRoomPlacementData = + await _roomPlacementStrategy.CalculatePlacementDataForStartingRoom(randomNumberGenerator, + startRoomNodeData); + if (startingRoomPlacementData == null) + { + return; + } + + var placeSuccess = await PlaceRoomAndAddRecord(startRoomNodeData.Id, startingRoomPlacementData, roomDictionary); + if (!placeSuccess) + { + return; + } + while (await _layoutParsingStrategy.HasNext()) { //When a new room needs to be placed //当有新的房间需要放置时 var roomNodeData = await _layoutParsingStrategy.Next(); - if (roomNodeData == null) + if (roomNodeData == null || string.IsNullOrEmpty(roomNodeData.Id)) { continue; } @@ -118,23 +158,49 @@ public static class MapGenerator } var roomPlacementData = - await _roomPlacementStrategy.CalculateNewRoomPlacementData(parentRoomNode, roomNodeData); + await _roomPlacementStrategy.CalculateNewRoomPlacementData(randomNumberGenerator, parentRoomNode, + roomNodeData); if (roomPlacementData == null) { continue; } - if (!await _roomPlacementStrategy.PlaceRoom(_mapRoot, roomPlacementData)) - { - continue; - } - - if (!string.IsNullOrEmpty(roomNodeData.Id) && roomPlacementData.Room != null) - { - roomDictionary.Add(roomNodeData.Id, roomPlacementData.Room); - } + await PlaceRoomAndAddRecord(roomNodeData.Id, roomPlacementData, roomDictionary); } //All rooms have been placed. //所有房间已放置完毕。 } + + /// + /// Place rooms and add mappings + /// 放置房间,并增加映射 + /// + /// + /// + /// + /// + private static async Task PlaceRoomAndAddRecord(string roomNodeDataId, + RoomPlacementData roomPlacementData, Dictionary dictionary) + { + //The input parameters are incomplete. + //输入参数不全。 + if (_roomPlacementStrategy == null || _mapRoot == null || string.IsNullOrEmpty(roomNodeDataId) || + roomPlacementData.Room == null) + { + return false; + } + + if (dictionary.ContainsKey(roomNodeDataId)) + { + return false; + } + + if (!await _roomPlacementStrategy.PlaceRoom(_mapRoot, roomPlacementData)) + { + return false; + } + + dictionary.Add(roomNodeDataId, roomPlacementData.Room); + return true; + } } \ No newline at end of file diff --git a/scripts/map/interfaces/IRoomPlacementStrategy.cs b/scripts/map/interfaces/IRoomPlacementStrategy.cs index 4b48d53..c828611 100644 --- a/scripts/map/interfaces/IRoomPlacementStrategy.cs +++ b/scripts/map/interfaces/IRoomPlacementStrategy.cs @@ -40,5 +40,18 @@ public interface IRoomPlacementStrategy ///欲放置的新房间数据 /// /// - public Task CalculateNewRoomPlacementData(Room? parentRoomNode, RoomNodeData newRoomNodeData); + public Task CalculateNewRoomPlacementData(RandomNumberGenerator randomNumberGenerator, + Room? parentRoomNode, + RoomNodeData newRoomNodeData); + + + /// + /// Calculates the placement information for the starting room + /// 计算起始房间的放置信息 + /// + /// + /// + /// + public Task CalculatePlacementDataForStartingRoom( + RandomNumberGenerator randomNumberGenerator, RoomNodeData startRoomNodeData); } \ No newline at end of file diff --git a/scripts/map/room/RoomFactory.cs b/scripts/map/room/RoomFactory.cs index 8a47653..3c9d6f3 100644 --- a/scripts/map/room/RoomFactory.cs +++ b/scripts/map/room/RoomFactory.cs @@ -1,5 +1,7 @@ using System.Collections.Generic; +using System.IO; using Godot; +using FileAccess = Godot.FileAccess; namespace ColdMint.scripts.map.room; @@ -36,7 +38,7 @@ public static class RoomFactory { if (!dir.CurrentIsDir()) { - resList.Add(fileName); + resList.Add(Path.Join(roomTemplate, fileName)); } fileName = dir.GetNext(); diff --git a/scripts/map/roomPlacer/PatchworkRoomPlacementStrategy.cs b/scripts/map/roomPlacer/PatchworkRoomPlacementStrategy.cs index 502beb6..9b6e846 100644 --- a/scripts/map/roomPlacer/PatchworkRoomPlacementStrategy.cs +++ b/scripts/map/roomPlacer/PatchworkRoomPlacementStrategy.cs @@ -32,40 +32,55 @@ public class PatchworkRoomPlacementStrategy : IRoomPlacementStrategy } var rootNode = roomPlacementData.Room.RootNode; - rootNode.Reparent(mapRoot); + mapRoot.AddChild(rootNode); rootNode.Position = roomPlacementData.Position.Value; return Task.FromResult(true); } - public Task CalculateNewRoomPlacementData(Room? parentRoomNode, RoomNodeData newRoomNodeData) + public Task CalculateNewRoomPlacementData(RandomNumberGenerator randomNumberGenerator, + Room? parentRoomNode, + RoomNodeData newRoomNodeData) { if (newRoomNodeData.RoomTemplateSet == null || newRoomNodeData.RoomTemplateSet.Length == 0) { return Task.FromResult(null); } - var roomResArray = RoomFactory.RoomTemplateSetToRoomRes(newRoomNodeData.RoomTemplateSet); if (parentRoomNode == null) { - //No parent node is set, which we think is the starting room. - //没有设置父节点,我们认为是起点房间。 - //TODO:在这里兼容世界种子。 - var roomPlacementData = new RoomPlacementData - { - Room = RoomFactory.CreateRoom(roomResArray[0]), - Position = Vector2.Zero - }; - return Task.FromResult(roomPlacementData); - } - else - { - //TODO:在这里实现房间的放置策略。 return Task.FromResult(null); } + // var roomResArray = RoomFactory.RoomTemplateSetToRoomRes(newRoomNodeData.RoomTemplateSet); + //TODO:在这里实现房间的放置策略。 + return Task.FromResult(null); } + public Task CalculatePlacementDataForStartingRoom(RandomNumberGenerator randomNumberGenerator, + RoomNodeData startRoomNodeData) + { + if (startRoomNodeData.RoomTemplateSet == null || startRoomNodeData.RoomTemplateSet.Length == 0) + { + return Task.FromResult(null); + } + + var roomResArray = RoomFactory.RoomTemplateSetToRoomRes(startRoomNodeData.RoomTemplateSet); + if (roomResArray.Length == 0) + { + return Task.FromResult(null); + } + + var index = randomNumberGenerator.Randi() % roomResArray.Length; + var roomPlacementData = new RoomPlacementData + { + Room = RoomFactory.CreateRoom(roomResArray[index]), + Position = Vector2.Zero + }; + return Task.FromResult(roomPlacementData); + } + + private Task CalculatedPosition(Room mainRoom, Room newRoom, RoomSlot? mainRoomSlot, - RoomSlot? newRoomSlot,bool roomSlotOverlap) + RoomSlot? newRoomSlot, bool roomSlotOverlap) { if (mainRoom.RootNode == null || mainRoom.TileMap == null || newRoom.TileMap == null || mainRoomSlot == null || newRoomSlot == null) diff --git a/scripts/utils/HashCodeUtils.cs b/scripts/utils/HashCodeUtils.cs new file mode 100644 index 0000000..db479f6 --- /dev/null +++ b/scripts/utils/HashCodeUtils.cs @@ -0,0 +1,23 @@ +using System.Linq; + +namespace ColdMint.scripts.utils; + +public class HashCodeUtils +{ + /// + /// Gets the hash code for a string + /// 获取字符串的哈希码 + /// + /// + ///The input string returns a fixed hash code + ///输入的字符串,返回固定的哈希码 + /// + /// + public static uint GetFixedHashCode(string str) + { + unchecked + { + return str.Aggregate(2166136261, (current, c) => (current ^ c) * 16777619); + } + } +} \ No newline at end of file