239 lines
7.6 KiB
C#
239 lines
7.6 KiB
C#
//
|
|
// This file is part of the Tremble package by Tiny Goose.
|
|
// Copyright (c) 2024-2026 TinyGoose Ltd., All Rights Reserved.
|
|
//
|
|
|
|
using System;
|
|
using System.Collections;
|
|
using System.Collections.Generic;
|
|
using System.IO;
|
|
using UnityEngine;
|
|
|
|
namespace TinyGoose.Tremble
|
|
{
|
|
public class FgdWriter
|
|
{
|
|
// -----------------------------------------------------------------------------------------------------------------------------
|
|
// State
|
|
// -----------------------------------------------------------------------------------------------------------------------------
|
|
private FgdClass m_WorldSpawn;
|
|
private readonly List<FgdClass> m_OtherClasses = new();
|
|
private readonly List<FgdClass> m_BaseClasses = new();
|
|
private readonly string m_VersionString;
|
|
private readonly float m_ImportScale;
|
|
|
|
public FgdWriter(string versionString, TrembleSyncSettings settings)
|
|
{
|
|
m_VersionString = versionString;
|
|
m_ImportScale = settings.ImportScale;
|
|
|
|
// Add base entities
|
|
AddMapBaseEntity();
|
|
AddMapPointBaseEntity();
|
|
AddMapPrefabBaseEntity();
|
|
}
|
|
|
|
public FgdClass WorldspawnClass
|
|
{
|
|
get => m_WorldSpawn;
|
|
set => m_WorldSpawn = value;
|
|
}
|
|
|
|
public IReadOnlyList<FgdClass> BaseClasses => m_BaseClasses;
|
|
public IReadOnlyList<FgdClass> Classes => m_OtherClasses;
|
|
|
|
public void AddClass(FgdClass entityClass)
|
|
{
|
|
if (entityClass.Name.Length > 31)
|
|
{
|
|
Debug.LogError($"Class '{entityClass.Name}' is too long - it cannot be longer than 31 characters." +
|
|
" Add a custom [PrefabEntity], [BrushEntity], or [PointEntity] attribute to this MonoBehaviour and set a shorter name!");
|
|
}
|
|
|
|
if (entityClass.Type == FgdClassType.Base)
|
|
{
|
|
int insertionIdx = m_BaseClasses.BinarySearch(entityClass, Comparer<FgdClass>.Create((c1, c2) => String.Compare(c1.Name, c2.Name, StringComparison.Ordinal)));
|
|
if (insertionIdx < 0)
|
|
{
|
|
m_BaseClasses.Add(entityClass);
|
|
}
|
|
else
|
|
{
|
|
m_BaseClasses.Insert(insertionIdx, entityClass);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
int insertionIdx = m_OtherClasses.BinarySearch(entityClass, Comparer<FgdClass>.Create((c1, c2) => String.Compare(c1.Name, c2.Name, StringComparison.Ordinal)));
|
|
if (insertionIdx < 0)
|
|
{
|
|
m_OtherClasses.Add(entityClass);
|
|
}
|
|
else
|
|
{
|
|
m_OtherClasses.Insert(insertionIdx, entityClass);
|
|
}
|
|
}
|
|
}
|
|
|
|
public void WriteFgd(StreamWriter stream, bool humanReadable = true)
|
|
{
|
|
if (humanReadable)
|
|
{
|
|
stream.WriteLine("// TrenchBroom game definition file (.fgd)");
|
|
stream.WriteLine($"// Generated by Tremble for Unity v{m_VersionString}");
|
|
stream.WriteLine($"// For: {Application.productName}. (c) {DateTime.UtcNow.Year} {Application.companyName}");
|
|
stream.WriteLine();
|
|
stream.WriteLine();
|
|
|
|
stream.WriteLine("// ----------------------------------------------------------------------------------------------------");
|
|
stream.WriteLine("//");
|
|
stream.WriteLine("// Base Classes");
|
|
stream.WriteLine("//");
|
|
stream.WriteLine("// ----------------------------------------------------------------------------------------------------");
|
|
}
|
|
|
|
// Base entities
|
|
foreach (FgdClass entity in m_BaseClasses)
|
|
{
|
|
if (humanReadable)
|
|
{
|
|
stream.WriteLine("// ----------------------------------------------------------------------------------------------------");
|
|
stream.WriteLine($"// Tremble Base Class '{entity.Name}'");
|
|
stream.WriteLine($"// {entity.Description}");
|
|
stream.WriteLine("// ----------------------------------------------------------------------------------------------------");
|
|
}
|
|
|
|
entity.WriteFgd(stream, m_ImportScale);
|
|
|
|
if (humanReadable)
|
|
{
|
|
stream.WriteLine();
|
|
stream.WriteLine();
|
|
}
|
|
}
|
|
|
|
if (m_WorldSpawn != null)
|
|
{
|
|
if (humanReadable)
|
|
{
|
|
stream.WriteLine();
|
|
stream.WriteLine();
|
|
stream.WriteLine();
|
|
stream.WriteLine("// ----------------------------------------------------------------------------------------------------");
|
|
stream.WriteLine("//");
|
|
stream.WriteLine("// worldspawn - World Root Properties");
|
|
stream.WriteLine("//");
|
|
stream.WriteLine("// ----------------------------------------------------------------------------------------------------");
|
|
}
|
|
|
|
m_WorldSpawn.WriteFgd(stream, m_ImportScale);
|
|
|
|
if (humanReadable)
|
|
{
|
|
stream.WriteLine();
|
|
stream.WriteLine();
|
|
}
|
|
}
|
|
|
|
if (humanReadable)
|
|
{
|
|
stream.WriteLine();
|
|
stream.WriteLine();
|
|
stream.WriteLine();
|
|
stream.WriteLine("// ----------------------------------------------------------------------------------------------------");
|
|
stream.WriteLine("//");
|
|
stream.WriteLine("// Game Entities");
|
|
stream.WriteLine("//");
|
|
stream.WriteLine("// ----------------------------------------------------------------------------------------------------");
|
|
stream.WriteLine();
|
|
}
|
|
|
|
foreach (FgdClass entity in m_OtherClasses)
|
|
{
|
|
if (humanReadable)
|
|
{
|
|
string type = entity.Type switch
|
|
{
|
|
FgdClassType.Base => nameof(ITrembleBaseClass),
|
|
FgdClassType.Point => "Point Entity",
|
|
FgdClassType.Brush => "Brush Entity",
|
|
_ => throw new ArgumentOutOfRangeException()
|
|
};
|
|
|
|
stream.WriteLine("// ----------------------------------------------------------------------------------------------------");
|
|
stream.WriteLine($"// {type}");
|
|
stream.WriteLine($"// {entity.Description}");
|
|
stream.WriteLine("// ----------------------------------------------------------------------------------------------------");
|
|
}
|
|
|
|
entity.WriteFgd(stream, m_ImportScale);
|
|
}
|
|
}
|
|
|
|
// -----------------------------------------------------------------------------------------------------------------------------
|
|
// Helpers
|
|
// -----------------------------------------------------------------------------------------------------------------------------
|
|
private void AddMapBaseEntity()
|
|
{
|
|
FgdClass mapBase = new(FgdClassType.Base, FgdConsts.CLASS_MAP_BASE, "The base class for all map entities");
|
|
mapBase.Description = "Base class for common map properties";
|
|
|
|
mapBase.AddField(new FgdTargetSourceField
|
|
{
|
|
Name = TrembleSyncSettings.Get().IdentityPropertyName,
|
|
Description = "The ID/Name of this entity",
|
|
DefaultValue = ""
|
|
});
|
|
|
|
mapBase.AddField(new FgdAnglesField
|
|
{
|
|
Name = FgdConsts.PROPERTY_ANGLES,
|
|
Description = "Rotation Angles",
|
|
DefaultValue = Vector3.zero
|
|
});
|
|
|
|
mapBase.AddField(new FgdVectorField
|
|
{
|
|
Name = FgdConsts.PROPERTY_ORIGIN,
|
|
Description = "Spawn position of this entity",
|
|
DefaultValue = Vector3.zero,
|
|
ConvertToQ3 = true,
|
|
ImportScale = m_ImportScale
|
|
});
|
|
|
|
AddClass(mapBase);
|
|
}
|
|
private void AddMapPointBaseEntity()
|
|
{
|
|
FgdClass mapBase = new(FgdClassType.Base, FgdConsts.CLASS_MAP_POINT_BASE, "The base class for all Point entities");
|
|
mapBase.Description = "Base class for common Point properties";
|
|
|
|
mapBase.AddField(new FgdTargetDestinationField
|
|
{
|
|
Name = FgdConsts.PROPERTY_PARENT,
|
|
Description = "Parent Transform",
|
|
DefaultValue = null
|
|
});
|
|
|
|
AddClass(mapBase);
|
|
}
|
|
|
|
private void AddMapPrefabBaseEntity()
|
|
{
|
|
FgdClass mapBase = new(FgdClassType.Base, FgdConsts.CLASS_MAP_PREFAB_BASE, "The base class for all Prefab entities");
|
|
mapBase.Description = "Base class for common Prefab properties";
|
|
|
|
mapBase.AddBaseClass(FgdConsts.CLASS_MAP_POINT_BASE);
|
|
|
|
mapBase.AddField(new FgdVectorField
|
|
{
|
|
Name = FgdConsts.PROPERTY_SCALE,
|
|
Description = "Prefab Scale",
|
|
DefaultValue = Vector3.one
|
|
});
|
|
|
|
AddClass(mapBase);
|
|
}
|
|
}
|
|
} |