Newer
Older
DungeonShooting / DungeonShooting_Godot / src / framework / generator / DungeonRoomGenerator.cs
@小李xl 小李xl on 7 Apr 2023 8 KB 房间添加权重功能

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text.Json;
using Godot;

namespace Generator;

/// <summary>
/// 地牢房间数据生成器
/// </summary>
public static class DungeonRoomGenerator
{
	/// <summary>
	/// 根据名称在编辑器中创建地牢的预制房间, open 表示创建完成后是否在编辑器中打开这个房间
	/// </summary>
	public static bool CreateDungeonRoom(string groupName, string roomType, string roomName, bool open = false)
	{
		try
		{
			var path = GameConfig.RoomTileDir + groupName + "/" + roomType;
			if (!Directory.Exists(path))
			{
				Directory.CreateDirectory(path);
			}
			//创建场景资源
			var prefabFile = path + "/" + roomName + ".tscn";
			var prefabResPath = "res://" + prefabFile;
			if (!Directory.Exists(GameConfig.RoomTileDir))
			{
				Directory.CreateDirectory(GameConfig.RoomTileDir);
			}
			
			//加载脚本资源
			
			// 这段代码在 Godot4.0.1rc1中会报错, 应该是个bug
			// var scriptRes = GD.Load<CSharpScript>("res://src/framework/map/DungeonRoomTemplate.cs");
			// var tileMap = new TileMap();
			// tileMap.Name = roomName;
			// tileMap.SetScript(scriptRes);
			// var scene = new PackedScene();
			// scene.Pack(tileMap); //报错在这一行, 报的是访问了被销毁的资源 scriptRes
			// ResourceSaver.Save(scene, prefabResPath);
			
			//临时处理
			{
				var tscnCode = $"[gd_scene load_steps=2 format=3]\n" +
				               $"\n" +
				               $"[ext_resource type=\"Script\" path=\"res://src/framework/map/DungeonRoomTemplate.cs\" id=\"dungeonRoomTemplate\"]\n" +
				               $"\n" +
				               $"[node name=\"{roomName}\" type=\"TileMap\"]\n" +
				               $"format = 2\n" +
				               $"script = ExtResource(\"dungeonRoomTemplate\")\n";
				File.WriteAllText(prefabFile, tscnCode);
				//重新保存一遍, 以让编辑器生成资源Id
				var scene = GD.Load<PackedScene>(prefabResPath);
				ResourceSaver.Save(scene, prefabResPath);
			}

			//打包房间配置
			GenerateRoomConfig();
			
			//生成 UiManager_Methods.cs 代码
			UiManagerMethodsGenerator.Generate();
#if TOOLS
			//打开房间
			if (open)
			{
				Plugin.Plugin.Instance.GetEditorInterface().OpenSceneFromPath(prefabResPath);
			}   
#endif
		}
		catch (Exception e)
		{
			GD.PrintErr(e.ToString());
			return false;
		}
		
		return true;
	}
	
	/// <summary>
	/// 执行生成 RoomConfig.json 操作, 返回是否执行成功
	/// </summary>
    public static bool GenerateRoomConfig()
    {
	    try
	    {
		    //地图路径
		    var tileDir = GameConfig.RoomTileDir;
		    //地图描述数据路径
		    var tileDataDir = GameConfig.RoomTileDataDir;
		
		    var tileGroup = new DirectoryInfo(tileDir).GetDirectories();
		    var tileDataGroup = new DirectoryInfo(tileDataDir).GetDirectories();

		    //所有地图列表
		    var map = new Dictionary<string, FileInfo>();
		    
		    //地图场景
		    foreach (var groupDir in tileGroup)
		    {
			    var groupName = groupDir.Name;
			    var typeDirArray = groupDir.GetDirectories();
			    //遍历枚举, 获取指定路径文件
			    foreach (DungeonRoomType roomType in Enum.GetValues(typeof(DungeonRoomType)))
			    {
				    var typeName = DungeonRoomTemplate.DungeonRoomTypeToString(roomType);

				    //收集所有文件名称
				    var tempFileDataInfos = typeDirArray.FirstOrDefault(dirInfo => dirInfo.Name == typeName);
				    if (tempFileDataInfos != null)
				    {
					    foreach (var fileInfo in tempFileDataInfos.GetFiles())
					    {
						    if (fileInfo.Extension == ".tscn")
						    {
							    var pathInfo = new FileInfo(groupName, roomType, typeName, RemoveExtension(fileInfo.Name));
							    map.TryAdd(pathInfo.GetPath(), pathInfo);
						    }
					    }
				    }
			    }
		    }

		    //地图配置数据
		    foreach (var groupDir in tileDataGroup)
		    {
			    var groupName = groupDir.Name;
			    var typeDirArray = groupDir.GetDirectories();
			    //遍历枚举, 获取指定路径文件
			    foreach (DungeonRoomType roomType in Enum.GetValues(typeof(DungeonRoomType)))
			    {
				    var typeName = DungeonRoomTemplate.DungeonRoomTypeToString(roomType);

				    //收集所有文件名称
				    var tempFileDataInfos = typeDirArray.FirstOrDefault(dirInfo => dirInfo.Name == typeName);
				    if (tempFileDataInfos != null)
				    {
					    foreach (var fileInfo in tempFileDataInfos.GetFiles())
					    {
						    if (fileInfo.Extension == ".json")
						    {
							    var pathInfo = new FileInfo(groupName, roomType, typeName, RemoveExtension(fileInfo.Name));
							    map.TryAdd(pathInfo.GetPath(), pathInfo);
						    }
					    }
				    }
			    }
		    }

		    //剔除多余的 tile.json
		    foreach (var item in map)
		    {
			    var path = item.Key;
			    if (!File.Exists(tileDir + path + ".tscn"))
			    {
				    map.Remove(path);
				    var filePath = tileDataDir + path + ".json";
				    if (File.Exists(filePath))
				    {
					    GD.Print($"未找到'{tileDir + path}.tscn', 删除配置文件: {filePath}");
					    File.Delete(filePath);
				    }
			    }
		    }

		    //手动生成缺失的 tile.json
		    foreach (var item in map)
		    {
			    if (!File.Exists(tileDataDir + item.Key + ".json"))
			    {
				    var tscnName = tileDir + item.Key + ".tscn";
				    var packedScene = ResourceManager.Load<PackedScene>(tscnName, false);
				    if (packedScene != null)
				    {
					    var dungeonRoomTemplate = packedScene.Instantiate<DungeonRoomTemplate>();
					    var usedRect = dungeonRoomTemplate.GetUsedRect();
					    var dungeonTile = new DungeonTile(dungeonRoomTemplate);
					    dungeonTile.SetFloorAtlasCoords(new List<Vector2I>() { new Vector2I(0, 8) });
					    //计算导航网格
					    dungeonTile.GenerateNavigationPolygon(0);
					    var polygonData = dungeonTile.GetPolygonData();
					    
					    DungeonRoomTemplate.SaveConfig(new List<DoorAreaInfo>(), usedRect.Position, usedRect.Size, polygonData.ToList(),
						    item.Value.GroupName, item.Value.RoomType, item.Value.FileName, dungeonRoomTemplate.Weight);
					    dungeonRoomTemplate.QueueFree();
				    }
			    }
		    }

		    var roomGroupMap = new Dictionary<string, DungeonRoomGroup>();
		    //var list = new List<DungeonRoomSplit>();
		    //整合操作
		    foreach (var item in map)
		    {
			    var path = item.Key;
			    var configPath = tileDataDir + path + ".json";
			    var split = new DungeonRoomSplit();
			    split.ScenePath = ToResPath(tileDir + path + ".tscn");
			    split.ConfigPath = ToResPath(configPath);

			    if (!roomGroupMap.TryGetValue(item.Value.GroupName, out var group))
			    {
				    group = new DungeonRoomGroup();
				    group.GroupName = item.Value.GroupName;
				    roomGroupMap.Add(group.GroupName, group);
			    }
			    
			    group.GetRoomList(item.Value.RoomType).Add(split);
		    }

		    //写出配置
		    var config = new JsonSerializerOptions();
		    config.WriteIndented = true;
		    var text = JsonSerializer.Serialize(roomGroupMap, config);
		    File.WriteAllText(GameConfig.RoomTileConfigFile, text);

		    GD.Print("地牢房间配置, 重新打包完成!");
	    }
	    catch (Exception e)
	    {
		    GD.PrintErr(e.ToString());
		    return false;
	    }

	    return true;
    }

	private class FileInfo
	{
		public FileInfo(string groupName, DungeonRoomType roomType, string typeName, string fileName)
		{
			GroupName = groupName;
			RoomType = roomType;
			TypeName = typeName;
			FileName = fileName;
		}

		public string GroupName;
		public DungeonRoomType RoomType;
		public string TypeName;
		public string FileName;

		public string GetPath()
		{
			return GroupName + "/" + TypeName + "/" + FileName;
		}
	}
	
	private static string ToResPath(string path)
    {
	    var field = path.Replace("\\", "/");
	    return "res://" + field;
    }

    private static string RemoveExtension(string name)
    {
	    var index = name.LastIndexOf(".", StringComparison.Ordinal);
	    if (index >= 0)
	    {
		    return name.Substring(0, index);
	    }

	    return name;
    }
}