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.events; using ColdMint.scripts.map.interfaces; using ColdMint.scripts.map.LayoutParsingStrategy; using ColdMint.scripts.map.layoutStrategy; using ColdMint.scripts.map.room; using ColdMint.scripts.serialization; using ColdMint.scripts.utils; using Godot; namespace ColdMint.scripts.map; /// /// Map generator /// 地图生成器 /// /// ///Responsible for the overall map generation process control ///负责地图的整体生成流程控制 /// public static class MapGenerator { /// /// Layout map selection strategy /// 布局图选择策略 /// private static ILayoutStrategy? _layoutStrategy; private static bool _running; /// /// Map root node /// 地图根节点 /// private static Node? _mapRoot; /// /// Room placement strategy /// 房间的放置策略 /// private static IRoomPlacementStrategy? _roomPlacementStrategy; private static ulong _seed; private static Dictionary? _roomInjectionProcessorsDictionary; /// /// Register the room injection processor /// 注册房间注入处理器 /// /// /// public static bool RegisterRoomInjectionProcessor(IRoomInjectionProcessor roomInjectionProcessor) { var key = roomInjectionProcessor.GetId(); if (_roomInjectionProcessorsDictionary == null) { _roomInjectionProcessorsDictionary = new Dictionary { { key, roomInjectionProcessor } }; return true; } return _roomInjectionProcessorsDictionary.TryAdd(key, roomInjectionProcessor); } /// /// Log out of the room injection processor /// 注销房间注入处理器 /// /// /// public static bool UnRegisterRoomInjectionProcessor(string id) { if (_roomInjectionProcessorsDictionary == null) { return false; } return _roomInjectionProcessorsDictionary.Remove(id); } /// /// 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 /// 布局图解析策略 /// private static ILayoutParsingStrategy? _layoutParsingStrategy; public static Node? MapRoot { get => _mapRoot; set => _mapRoot = value; } public static ILayoutParsingStrategy? LayoutParsingStrategy { get => _layoutParsingStrategy; set => _layoutParsingStrategy = value; } public static IRoomPlacementStrategy? RoomPlacementStrategy { get => _roomPlacementStrategy; set => _roomPlacementStrategy = value; } public static ILayoutStrategy? LayoutStrategy { get => _layoutStrategy; set => _layoutStrategy = value; } /// /// Generating a map /// 生成地图 /// public static async Task GenerateMap() { if (_running) { LogCat.LogWarning("map_generator_is_running"); return; } _running = true; EventBus.MapGenerationStartEvent?.Invoke(new MapGenerationStartEvent()); if (_layoutStrategy == null || _roomPlacementStrategy == null || _layoutParsingStrategy == null || _mapRoot == null) { LogCat.LogError("map_generator_missing_parameters"); _running = false; return; } if (GameSceneDepend.AiCharacterContainer != null) { NodeUtils.DeleteAllChild(GameSceneDepend.AiCharacterContainer); } NodeUtils.DeleteAllChild(_mapRoot); if (!await _roomPlacementStrategy.StartGeneration(_mapRoot)) { LogCat.LogError("room_placement_strategy_terminates_map_generation"); _running = false; return; } //Get the layout data //拿到布局图数据 var levelGraphEditorSaveData = await _layoutStrategy.GetLayout(); if (levelGraphEditorSaveData == null || levelGraphEditorSaveData.RoomNodeDataList == null || levelGraphEditorSaveData.RoomNodeDataList.Count == 0) { LogCat.LogError("map_generator_attempts_to_parse_empty_layout_diagrams"); _running = false; return; } _layoutParsingStrategy.SetLevelGraph(levelGraphEditorSaveData); //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; var startRoomNodeData = await _layoutParsingStrategy.GetStartRoomNodeData(); if (startRoomNodeData == null || string.IsNullOrEmpty(startRoomNodeData.Id)) { LogCat.LogError("map_generator_has_no_starting_room_data"); _running = false; return; } var startingRoomPlacementData = await _roomPlacementStrategy.CalculatePlacementDataForStartingRoom(randomNumberGenerator, startRoomNodeData); if (startingRoomPlacementData == null) { LogCat.LogError("start_room_placement_information_returns_empty"); _running = false; return; } var placeSuccess = await PlaceRoomAndAddRecord(startRoomNodeData.Id, startingRoomPlacementData, roomDictionary); if (!placeSuccess) { LogCat.LogError("start_room_placement_failed"); _running = false; return; } while (await _layoutParsingStrategy.HasNext()) { //When a new room needs to be placed //当有新的房间需要放置时 var roomNodeData = await _layoutParsingStrategy.Next(); if (roomNodeData == null || string.IsNullOrEmpty(roomNodeData.Id)) { LogCat.LogWarning("room_data_missing"); continue; } var roomInjectionProcessorData = roomNodeData.RoomInjectionProcessorData; //Whether room can be placed //是否可放置房间 var canBePlaced = true; if (_roomInjectionProcessorsDictionary != null && !string.IsNullOrEmpty(roomInjectionProcessorData)) { var roomInjectionProcessorDataArray = YamlSerialization.Deserialize(roomInjectionProcessorData); if (roomInjectionProcessorDataArray is { Length: > 0 }) { foreach (var injectionProcessorData in roomInjectionProcessorDataArray) { if (string.IsNullOrEmpty(injectionProcessorData.Id) || string.IsNullOrEmpty(injectionProcessorData.Config)) { //The data is incomplete, and the injectionProcessorData is ignored. //数据不全,忽略injectionProcessorData。 continue; } if (!_roomInjectionProcessorsDictionary.TryGetValue(injectionProcessorData.Id, out var roomInjectionProcessor)) { //If the room injection processor cannot be found, a print error occurs. //如果找不到房间注入处理器,那么打印错误。 LogCat.LogErrorWithFormat("room_injection_processor_does_not_exist", LogCat.LogLabel.Default, LogCat.UploadFormat, injectionProcessorData.Id); continue; } if (await roomInjectionProcessor.CanBePlaced(randomNumberGenerator, injectionProcessorData.Config)) continue; //If the room cannot be placed, then out of the loop. //如果此房间不能被放置,那么跳出循环。 canBePlaced = false; break; } } } if (!canBePlaced) { continue; } var nextParentNodeId = await _layoutParsingStrategy.GetNextParentNodeId(); Room? parentRoomNode = null; if (nextParentNodeId != null) { //If the new room has the parent's ID, then we pass the parent's room into the compute function. //如果新房间有父节点的ID,那么我们将父节点的房间传入到计算函数内。 if (roomDictionary.TryGetValue(nextParentNodeId, out var value)) { parentRoomNode = value; } } var roomPlacementData = await _roomPlacementStrategy.CalculateNewRoomPlacementData(randomNumberGenerator, parentRoomNode, roomNodeData); if (roomPlacementData == null) { LogCat.LogWithFormat("failed_to_calculate_the_room_location", LogCat.LogLabel.Default, LogCat.UploadFormat, roomNodeData.Id); continue; } if (await PlaceRoomAndAddRecord(roomNodeData.Id, roomPlacementData, roomDictionary)) { MarkRoomSlot(roomPlacementData); } } //Place barriers //放置屏障 foreach (var roomDictionaryValue in roomDictionary.Values) { PlaceBarrier(roomDictionaryValue); } //All rooms have been placed. //所有房间已放置完毕。 await _roomPlacementStrategy.GeneratedComplete(_mapRoot); _running = false; //Invoke the map generation completion event //调用地图生成完成事件 var eventObj = new MapGenerationCompleteEvent { RandomNumberGenerator = randomNumberGenerator, RoomDictionary = roomDictionary }; EventBus.MapGenerationCompleteEvent?.Invoke(eventObj); var aiCharacterGenerateEvent = new AiCharacterGenerateEvent { Tag = AiCharacterGenerateEvent.TagMapGenerationComplete }; EventBus.AiCharacterGenerateEvent?.Invoke(aiCharacterGenerateEvent); } /// /// Place barriers /// 放置屏障 /// /// /// private static void PlaceBarrier(Room? room) { if (room == null) { return; } var ground = room.GetTileMapLayer(Config.TileMapLayerName.Ground); var barrier = room.GetTileMapLayer(Config.TileMapLayerName.Barrier); if (ground == null || barrier == null) { return; } var roomSlots = room.RoomSlots; if (roomSlots == null || roomSlots.Length == 0) { return; } foreach (var roomSlot in roomSlots) { if (roomSlot == null) { continue; } if (roomSlot.Matched) { continue; } //Place the corresponding coordinate tiles of the barrier layer on the ground level. //将屏障层的对应坐标瓦片放到地面层。 CoordinateUtils.ForEachCell(roomSlot.StartPosition, roomSlot.EndPosition, i => { var cellSourceId = barrier.GetCellSourceId(i); if (cellSourceId == -1) { return; } ground.SetCell(i, cellSourceId, barrier.GetCellAtlasCoords(i), barrier.GetCellAlternativeTile(i)); }); } barrier.QueueFree(); } /// /// Mark the room slot as matched /// 将房间槽标记为已匹配 /// /// private static void MarkRoomSlot(RoomPlacementData roomPlacementData) { if (roomPlacementData.ParentRoomSlot != null) { roomPlacementData.ParentRoomSlot.Matched = true; } if (roomPlacementData.NewRoomSlot != null) { roomPlacementData.NewRoomSlot.Matched = true; } } /// /// 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.NewRoom == null) { return false; } if (dictionary.ContainsKey(roomNodeDataId)) { LogCat.LogWithFormat("place_existing_rooms", LogCat.LogLabel.Default, LogCat.UploadFormat, roomNodeDataId); return false; } if (!await _roomPlacementStrategy.PlaceRoom(_mapRoot, roomPlacementData)) { LogCat.LogWarningWithFormat("room_placement_failed", LogCat.LogLabel.Default, LogCat.UploadFormat, roomNodeDataId); return false; } dictionary.Add(roomNodeDataId, roomPlacementData.NewRoom); LogCat.LogWithFormat("room_placement_information", LogCat.LogLabel.Default, LogCat.UploadFormat, roomNodeDataId, roomPlacementData.Position.ToString()); return true; } }