From 7b9b65d80814076bca8df6256c791516fe597d6e Mon Sep 17 00:00:00 2001 From: lahm86 <33758420+lahm86@users.noreply.github.com> Date: Sun, 9 Jun 2024 18:20:03 +0100 Subject: [PATCH] Add TRR texture support (#703) Resolves #702. --- TRLevelControl/IO/TRLevelReader.cs | 10 + TRLevelControl/IO/TRLevelWriter.cs | 7 + TRLevelControl/Model/Common/TRVertex.cs | 20 + TRLevelControl/Model/TRG/TRGData.cs | 15 + TRLevelControl/Model/TRG/TRGMesh.cs | 8 + TRLevelControl/Model/TRG/TRGShader.cs | 11 + TRLevelControl/Model/TRG/TRGVertex.cs | 13 + TRLevelControl/TRGControlBase.cs | 187 ++++ TRLevelControlTests/Base/TestBase.cs | 20 + TRLevelControlTests/TR1/IOTests.cs | 7 + TRLevelControlTests/TR2/IOTests.cs | 7 + TRLevelControlTests/TR3/IOTests.cs | 7 + .../Editors/RandomizerSettings.cs | 9 + .../Editors/TR1RemasteredEditor.cs | 19 + .../Editors/TR2RemasteredEditor.cs | 19 + .../Editors/TR3RemasteredEditor.cs | 19 + TRRandomizerCore/Helpers/TextureMode.cs | 8 + TRRandomizerCore/Levels/TR1RCombinedLevel.cs | 1 + TRRandomizerCore/Levels/TR2RCombinedLevel.cs | 1 + TRRandomizerCore/Levels/TR3RCombinedLevel.cs | 1 + .../Processors/AbstractLevelProcessor.cs | 25 + .../Processors/TR1/TR1RLevelProcessor.cs | 11 + .../Processors/TR2/TR2RLevelProcessor.cs | 11 + .../Processors/TR3/TR3RLevelProcessor.cs | 11 + .../Randomizers/Shared/TextureAllocator.cs | 265 ++++++ .../TR1/Remastered/TR1RTextureRandomizer.cs | 34 + .../TR2/Remastered/TR2RTextureRandomizer.cs | 34 + .../TR3/Remastered/TR3RTextureRandomizer.cs | 34 + .../Resources/TR1/Textures/texinfo.json | 1 + .../Resources/TR2/Textures/texinfo.json | 1 + .../Resources/TR3/Textures/texinfo.json | 1 + TRRandomizerCore/TRRandomizerController.cs | 18 + TRRandomizerCore/TRRandomizerType.cs | 2 + TRRandomizerCore/TRVersionSupport.cs | 13 +- TRRandomizerCore/Textures/TRTexInfo.cs | 50 ++ TRRandomizerView/Controls/EditorControl.xaml | 3 +- TRRandomizerView/Model/ControllerOptions.cs | 75 +- TRRandomizerView/Windows/AdvancedWindow.xaml | 96 ++- .../Windows/AdvancedWindow.xaml.cs | 21 +- TextureExport/Program.cs | 34 +- TextureExport/TextureExport.csproj | 4 + TextureExport/Types/TRRExporter.cs | 814 ++++++++++++++++++ 42 files changed, 1909 insertions(+), 38 deletions(-) create mode 100644 TRLevelControl/Model/TRG/TRGData.cs create mode 100644 TRLevelControl/Model/TRG/TRGMesh.cs create mode 100644 TRLevelControl/Model/TRG/TRGShader.cs create mode 100644 TRLevelControl/Model/TRG/TRGVertex.cs create mode 100644 TRLevelControl/TRGControlBase.cs create mode 100644 TRRandomizerCore/Helpers/TextureMode.cs create mode 100644 TRRandomizerCore/Randomizers/Shared/TextureAllocator.cs create mode 100644 TRRandomizerCore/Randomizers/TR1/Remastered/TR1RTextureRandomizer.cs create mode 100644 TRRandomizerCore/Randomizers/TR2/Remastered/TR2RTextureRandomizer.cs create mode 100644 TRRandomizerCore/Randomizers/TR3/Remastered/TR3RTextureRandomizer.cs create mode 100644 TRRandomizerCore/Resources/TR1/Textures/texinfo.json create mode 100644 TRRandomizerCore/Resources/TR2/Textures/texinfo.json create mode 100644 TRRandomizerCore/Resources/TR3/Textures/texinfo.json create mode 100644 TRRandomizerCore/Textures/TRTexInfo.cs create mode 100644 TextureExport/Types/TRRExporter.cs diff --git a/TRLevelControl/IO/TRLevelReader.cs b/TRLevelControl/IO/TRLevelReader.cs index 1f5253628..df28696ae 100644 --- a/TRLevelControl/IO/TRLevelReader.cs +++ b/TRLevelControl/IO/TRLevelReader.cs @@ -506,6 +506,16 @@ public TRVertex ReadVertex(bool reverse = false) return vertex; } + public TRVertex8 ReadVertex8() + { + return new() + { + X = ReadByte(), + Y = ReadByte(), + Z = ReadByte() + }; + } + public TRVertex32 ReadVertex32() { return new() diff --git a/TRLevelControl/IO/TRLevelWriter.cs b/TRLevelControl/IO/TRLevelWriter.cs index 8ac6f8c05..23ce4b051 100644 --- a/TRLevelControl/IO/TRLevelWriter.cs +++ b/TRLevelControl/IO/TRLevelWriter.cs @@ -376,6 +376,13 @@ public void Write(TRVertex vertex, bool reverse = false) Write(reverse ? vertex.X : vertex.Z); } + public void Write(TRVertex8 vertex) + { + Write(vertex.X); + Write(vertex.Y); + Write(vertex.Z); + } + public void Write(TRVertex32 vertex) { Write(vertex.X); diff --git a/TRLevelControl/Model/Common/TRVertex.cs b/TRLevelControl/Model/Common/TRVertex.cs index 4d645a797..e7858faed 100644 --- a/TRLevelControl/Model/Common/TRVertex.cs +++ b/TRLevelControl/Model/Common/TRVertex.cs @@ -40,3 +40,23 @@ public TRVertex32 Clone() object ICloneable.Clone() => Clone(); } + +public class TRVertex8 : ICloneable +{ + public byte X { get; set; } + public byte Y { get; set; } + public byte Z { get; set; } + + public TRVertex8 Clone() + { + return new() + { + X = X, + Y = Y, + Z = Z + }; + } + + object ICloneable.Clone() + => Clone(); +} diff --git a/TRLevelControl/Model/TRG/TRGData.cs b/TRLevelControl/Model/TRG/TRGData.cs new file mode 100644 index 000000000..6ab4a6617 --- /dev/null +++ b/TRLevelControl/Model/TRG/TRGData.cs @@ -0,0 +1,15 @@ +namespace TRLevelControl.Model; + +public class TRGData +{ + public List AmbientCubes { get; set; } + public byte[] Unknown1 { get; set; } + public byte Unknown2 { get; set; } + public byte Unknown3 { get; set; } + public List Unknown4 { get; set; } + public List Unknown5 { get; set; } + public List Meshes { get; set; } + public List Textures { get; set; } + public List Indices { get; set; } + public List Vertices { get; set; } +} diff --git a/TRLevelControl/Model/TRG/TRGMesh.cs b/TRLevelControl/Model/TRG/TRGMesh.cs new file mode 100644 index 000000000..228937a72 --- /dev/null +++ b/TRLevelControl/Model/TRG/TRGMesh.cs @@ -0,0 +1,8 @@ +namespace TRLevelControl.Model; + +public class TRGMesh +{ + public List Shaders { get; set; } + public uint Unknown1 { get; set; } + public List Unknown2 { get; set; } +} diff --git a/TRLevelControl/Model/TRG/TRGShader.cs b/TRLevelControl/Model/TRG/TRGShader.cs new file mode 100644 index 000000000..5cd63d2cb --- /dev/null +++ b/TRLevelControl/Model/TRG/TRGShader.cs @@ -0,0 +1,11 @@ +namespace TRLevelControl.Model; + +public class TRGShader +{ + public uint Type { get; set; } + public uint Unknown1 { get; set; } + public uint Unknown2 { get; set; } + public uint Unknown3 { get; set; } + public uint Unknown4 { get; set; } + public Tuple[] IndexInfo { get; set; } +} diff --git a/TRLevelControl/Model/TRG/TRGVertex.cs b/TRLevelControl/Model/TRG/TRGVertex.cs new file mode 100644 index 000000000..3e31d06a9 --- /dev/null +++ b/TRLevelControl/Model/TRG/TRGVertex.cs @@ -0,0 +1,13 @@ +namespace TRLevelControl.Model; + +public class TRGVertex +{ + public TRVertex Vertex { get; set; } + public short Unknown { get; set; } + public TRVertex8 Normal { get; set; } + public byte Texture { get; set; } + public TRColour Ambient1 { get; set; } + public TRColour Ambient2 { get; set; } + public byte U { get; set; } + public byte V { get; set; } +} diff --git a/TRLevelControl/TRGControlBase.cs b/TRLevelControl/TRGControlBase.cs new file mode 100644 index 000000000..6eb8b8158 --- /dev/null +++ b/TRLevelControl/TRGControlBase.cs @@ -0,0 +1,187 @@ +using System.Diagnostics; +using TRLevelControl.Model; + +namespace TRLevelControl; + +public class TRGControlBase +{ + private const uint _trgMagic = 'T' | ('R' << 8) | ('G' << 16) | (3 << 24); + + public static TRGData Read(string filePath) + => Read(File.OpenRead(filePath)); + + public static TRGData Read(Stream stream) + { + using TRLevelReader reader = new(stream); + Debug.Assert(reader.ReadUInt32() == _trgMagic); + + TRGData data = new(); + + uint numAmbientCubes = reader.ReadUInt32(); + data.AmbientCubes = new(); + for (int i = 0; i < numAmbientCubes; i++) + { + TRColour4[] cube = new TRColour4[6]; + data.AmbientCubes.Add(cube); + for (int j = 0; j < cube.Length; j++) + { + cube[j] = new(reader.ReadUInt32()); + } + } + + data.Unknown1 = reader.ReadBytes(24); + data.Unknown2 = reader.ReadByte(); + data.Unknown3 = reader.ReadByte(); + + byte numUnknown4 = reader.ReadByte(); + byte numUnknown5 = reader.ReadByte(); + + data.Unknown4 = new(); + for (int i = 0; i < numUnknown4; i++) + { + data.Unknown4.Add(reader.ReadBytes(40)); + } + + data.Unknown5 = new(); + for (int i = 0; i < numUnknown5; i++) + { + data.Unknown5.Add(reader.ReadBytes(28)); + } + + uint numMeshes = reader.ReadUInt32(); + data.Meshes = new(); + for (int i = 0; i < numMeshes; i++) + { + TRGMesh mesh = new(); + data.Meshes.Add(mesh); + + uint numShaders = reader.ReadUInt32(); + mesh.Shaders = new(); + for (int j = 0; j < numShaders; j++) + { + TRGShader shader = new() + { + Type = reader.ReadUInt32(), + Unknown1 = reader.ReadUInt32(), + Unknown2 = reader.ReadUInt32(), + Unknown3 = reader.ReadUInt32(), + Unknown4 = reader.ReadUInt32(), + IndexInfo = new Tuple[3] + }; + mesh.Shaders.Add(shader); + + for (int k = 0; k < 3; k++) + { + shader.IndexInfo[k] = new(reader.ReadUInt32(), reader.ReadUInt32()); + } + } + + mesh.Unknown1 = reader.ReadUInt32(); + uint numUnknowns = reader.ReadUInt32(); + mesh.Unknown2 = new(); + for (int j = 0; j < numUnknowns; j++) + { + mesh.Unknown2.Add(reader.ReadUInt8s(28)); + } + } + + uint numTextures = reader.ReadUInt32(); + data.Textures = new(reader.ReadUInt16s(numTextures)); + + while (reader.BaseStream.Position % sizeof(uint) != 0) + { + reader.ReadByte(); + } + + uint numIndices = reader.ReadUInt32(); + uint numVertices = reader.ReadUInt32(); + data.Indices = new(reader.ReadUInt32s(numIndices)); + data.Vertices = new(); + for (int i = 0; i < numVertices; i++) + { + data.Vertices.Add(new() + { + Vertex = reader.ReadVertex(), + Unknown = reader.ReadInt16(), + Normal = reader.ReadVertex8(), + Texture = reader.ReadByte(), + Ambient1 = reader.ReadColour(), + U = reader.ReadByte(), + Ambient2 = reader.ReadColour(), + V = reader.ReadByte(), + }); + } + + Debug.Assert(reader.BaseStream.Position == reader.BaseStream.Length); + + return data; + } + + public static void Write(TRGData data, string filePath) + => Write(data, File.Create(filePath)); + + public static void Write(TRGData data, Stream outputStream) + { + using TRLevelWriter writer = new(outputStream); + writer.Write(_trgMagic); + + writer.Write((uint)data.AmbientCubes.Count); + writer.Write(data.AmbientCubes.SelectMany(c => c.Select(a => a.ToARGB()))); + + writer.Write(data.Unknown1); + writer.Write(data.Unknown2); + writer.Write(data.Unknown3); + + writer.Write((byte)data.Unknown4.Count); + writer.Write((byte)data.Unknown5.Count); + writer.Write(data.Unknown4.SelectMany(u => u)); + writer.Write(data.Unknown5.SelectMany(u => u)); + + writer.Write((uint)data.Meshes.Count); + foreach (TRGMesh mesh in data.Meshes) + { + writer.Write((uint)mesh.Shaders.Count); + foreach (TRGShader shader in mesh.Shaders) + { + writer.Write(shader.Type); + writer.Write(shader.Unknown1); + writer.Write(shader.Unknown2); + writer.Write(shader.Unknown3); + writer.Write(shader.Unknown4); + foreach (var (index, offset) in shader.IndexInfo) + { + writer.Write(index); + writer.Write(offset); + } + } + + writer.Write(mesh.Unknown1); + writer.Write((uint)mesh.Unknown2.Count); + writer.Write(mesh.Unknown2.SelectMany(u => u)); + } + + writer.Write((uint)data.Textures.Count); + writer.Write(data.Textures); + + while (writer.BaseStream.Position % sizeof(uint) != 0) + { + writer.Write((byte)0); + } + + writer.Write((uint)data.Indices.Count); + writer.Write((uint)data.Vertices.Count); + writer.Write(data.Indices); + + foreach (TRGVertex vertex in data.Vertices) + { + writer.Write(vertex.Vertex); + writer.Write(vertex.Unknown); + writer.Write(vertex.Normal); + writer.Write(vertex.Texture); + writer.Write(vertex.Ambient1); + writer.Write(vertex.U); + writer.Write(vertex.Ambient2); + writer.Write(vertex.V); + } + } +} diff --git a/TRLevelControlTests/Base/TestBase.cs b/TRLevelControlTests/Base/TestBase.cs index a67454fea..d196da213 100644 --- a/TRLevelControlTests/Base/TestBase.cs +++ b/TRLevelControlTests/Base/TestBase.cs @@ -257,6 +257,26 @@ public static void ReadWriteMAP(string levelName, TRGameVersion version) CollectionAssert.AreEqual(originalLines.Distinct().ToList(), outputLines); } + public static void ReadWriteTRG(string levelName, TRGameVersion version) + { + levelName = Path.GetFileNameWithoutExtension(levelName) + ".TRG"; + string pathI = GetReadPath(levelName, version, true); + + using FileStream dataStream = File.OpenRead(pathI); + using MemoryStream inputStream = new(); + using MemoryStream outputStream = new(); + + dataStream.CopyTo(inputStream); + byte[] inputData = inputStream.ToArray(); + inputStream.Position = 0; + + ObserverBase observer = new(); + TRGData data = TRGControlBase.Read(inputStream); + TRGControlBase.Write(data, outputStream); + + observer.TestOutput(inputData, outputStream.ToArray()); + } + public static IEnumerable GetLevelNames(IEnumerable names) { foreach (string lvl in names) diff --git a/TRLevelControlTests/TR1/IOTests.cs b/TRLevelControlTests/TR1/IOTests.cs index 0eed20f5b..3842ada7a 100644 --- a/TRLevelControlTests/TR1/IOTests.cs +++ b/TRLevelControlTests/TR1/IOTests.cs @@ -41,6 +41,13 @@ public void TestMAPReadWrite(string levelName) ReadWriteMAP(levelName, TRGameVersion.TR1); } + [TestMethod] + [DynamicData(nameof(GetAllLevels), DynamicDataSourceType.Method)] + public void TestTRGReadWrite(string levelName) + { + ReadWriteTRG(levelName, TRGameVersion.TR1); + } + [TestMethod] [DynamicData(nameof(GetAllLevels), DynamicDataSourceType.Method)] public void TestAgressiveFloorData(string levelName) diff --git a/TRLevelControlTests/TR2/IOTests.cs b/TRLevelControlTests/TR2/IOTests.cs index 29709b7c6..9791357b1 100644 --- a/TRLevelControlTests/TR2/IOTests.cs +++ b/TRLevelControlTests/TR2/IOTests.cs @@ -38,6 +38,13 @@ public void TestMAPReadWrite(string levelName) ReadWriteMAP(levelName, TRGameVersion.TR2); } + [TestMethod] + [DynamicData(nameof(GetAllLevels), DynamicDataSourceType.Method)] + public void TestTRGReadWrite(string levelName) + { + ReadWriteTRG(levelName, TRGameVersion.TR2); + } + [TestMethod] [DynamicData(nameof(GetAllLevels), DynamicDataSourceType.Method)] public void TestAgressiveFloorData(string levelName) diff --git a/TRLevelControlTests/TR3/IOTests.cs b/TRLevelControlTests/TR3/IOTests.cs index 17c562579..7cf005eb8 100644 --- a/TRLevelControlTests/TR3/IOTests.cs +++ b/TRLevelControlTests/TR3/IOTests.cs @@ -40,6 +40,13 @@ public void TestMAPReadWrite(string levelName) ReadWriteMAP(levelName, TRGameVersion.TR3); } + [TestMethod] + [DynamicData(nameof(GetAllLevels), DynamicDataSourceType.Method)] + public void TestTRGReadWrite(string levelName) + { + ReadWriteTRG(levelName, TRGameVersion.TR3); + } + [TestMethod] [DynamicData(nameof(GetAllLevels), DynamicDataSourceType.Method)] public void TestAgressiveFloorData(string levelName) diff --git a/TRRandomizerCore/Editors/RandomizerSettings.cs b/TRRandomizerCore/Editors/RandomizerSettings.cs index cf535f8a9..9fab42e13 100644 --- a/TRRandomizerCore/Editors/RandomizerSettings.cs +++ b/TRRandomizerCore/Editors/RandomizerSettings.cs @@ -50,6 +50,9 @@ public class RandomizerSettings public bool IncludeExtraPickups { get; set; } public bool DevelopmentMode { get; set; } public ItemDifficulty RandoItemDifficulty { get; set; } + public TextureMode TextureMode { get; set; } + public bool MatchTextureTypes { get; set; } + public bool MatchTextureItems { get; set; } public bool PersistTextureVariants { get; set; } public bool RandomizeWaterColour { get; set; } public bool RetainMainLevelTextures { get; set; } @@ -251,6 +254,9 @@ public void ApplyConfig(Config config) RandomizeTextures = config.GetBool(nameof(RandomizeTextures)); TextureSeed = config.GetInt(nameof(TextureSeed), defaultSeed); + TextureMode = (TextureMode)config.GetEnum(nameof(TextureMode), typeof(TextureMode), TextureMode.Game); + MatchTextureTypes = config.GetBool(nameof(MatchTextureTypes), true); + MatchTextureItems = config.GetBool(nameof(MatchTextureItems), true); PersistTextureVariants = config.GetBool(nameof(PersistTextureVariants)); RandomizeWaterColour = config.GetBool(nameof(RandomizeWaterColour), true); RetainMainLevelTextures = config.GetBool(nameof(RetainMainLevelTextures)); @@ -416,6 +422,9 @@ public void StoreConfig(Config config) config[nameof(RandomizeTextures)] = RandomizeTextures; config[nameof(TextureSeed)] = TextureSeed; + config[nameof(TextureMode)] = TextureMode; + config[nameof(MatchTextureTypes)] = MatchTextureTypes; + config[nameof(MatchTextureItems)] = MatchTextureItems; config[nameof(PersistTextureVariants)] = PersistTextureVariants; config[nameof(RandomizeWaterColour)] = RandomizeWaterColour; config[nameof(RetainMainLevelTextures)] = RetainMainLevelTextures; diff --git a/TRRandomizerCore/Editors/TR1RemasteredEditor.cs b/TRRandomizerCore/Editors/TR1RemasteredEditor.cs index 1864de2b9..689812afd 100644 --- a/TRRandomizerCore/Editors/TR1RemasteredEditor.cs +++ b/TRRandomizerCore/Editors/TR1RemasteredEditor.cs @@ -62,6 +62,11 @@ protected override int GetSaveTarget(int numLevels) target += numLevels; } + if (Settings.RandomizeTextures) + { + target += numLevels * 2; + } + // Environment randomizer always runs target += numLevels * 2; @@ -221,5 +226,19 @@ protected override void SaveImpl(AbstractTRScriptEditor scriptEditor, TRSaveMoni Settings = Settings }.Randomize(Settings.AudioSeed); } + + if (!monitor.IsCancelled && Settings.RandomizeTextures) + { + monitor.FireSaveStateBeginning(TRSaveCategory.Custom, "Randomizing textures"); + new TR1RTextureRandomizer + { + ScriptEditor = scriptEditor, + Levels = levels, + BasePath = wipDirectory, + BackupPath = backupDirectory, + SaveMonitor = monitor, + Settings = Settings, + }.Randomize(Settings.TextureSeed); + } } } diff --git a/TRRandomizerCore/Editors/TR2RemasteredEditor.cs b/TRRandomizerCore/Editors/TR2RemasteredEditor.cs index 19a07c03e..6a9500a66 100644 --- a/TRRandomizerCore/Editors/TR2RemasteredEditor.cs +++ b/TRRandomizerCore/Editors/TR2RemasteredEditor.cs @@ -55,6 +55,11 @@ protected override int GetSaveTarget(int numLevels) target += numLevels; } + if (Settings.RandomizeTextures) + { + target += numLevels * 2; + } + // Environment randomizer always runs target += numLevels * 2; @@ -201,5 +206,19 @@ protected override void SaveImpl(AbstractTRScriptEditor scriptEditor, TRSaveMoni Settings = Settings }.Randomize(Settings.AudioSeed); } + + if (!monitor.IsCancelled && Settings.RandomizeTextures) + { + monitor.FireSaveStateBeginning(TRSaveCategory.Custom, "Randomizing textures"); + new TR2RTextureRandomizer + { + ScriptEditor = scriptEditor, + Levels = levels, + BasePath = wipDirectory, + BackupPath = backupDirectory, + SaveMonitor = monitor, + Settings = Settings, + }.Randomize(Settings.TextureSeed); + } } } diff --git a/TRRandomizerCore/Editors/TR3RemasteredEditor.cs b/TRRandomizerCore/Editors/TR3RemasteredEditor.cs index f90366d3b..1154b421e 100644 --- a/TRRandomizerCore/Editors/TR3RemasteredEditor.cs +++ b/TRRandomizerCore/Editors/TR3RemasteredEditor.cs @@ -62,6 +62,11 @@ protected override int GetSaveTarget(int numLevels) target += numLevels; } + if (Settings.RandomizeTextures) + { + target += numLevels * 2; + } + // Environment randomizer always runs target += numLevels * 2; @@ -224,5 +229,19 @@ protected override void SaveImpl(AbstractTRScriptEditor scriptEditor, TRSaveMoni Settings = Settings }.Randomize(Settings.AudioSeed); } + + if (!monitor.IsCancelled && Settings.RandomizeTextures) + { + monitor.FireSaveStateBeginning(TRSaveCategory.Custom, "Randomizing textures"); + new TR3RTextureRandomizer + { + ScriptEditor = scriptEditor, + Levels = levels, + BasePath = wipDirectory, + BackupPath = backupDirectory, + SaveMonitor = monitor, + Settings = Settings, + }.Randomize(Settings.TextureSeed); + } } } diff --git a/TRRandomizerCore/Helpers/TextureMode.cs b/TRRandomizerCore/Helpers/TextureMode.cs new file mode 100644 index 000000000..1d3e93628 --- /dev/null +++ b/TRRandomizerCore/Helpers/TextureMode.cs @@ -0,0 +1,8 @@ +namespace TRRandomizerCore.Helpers; + +public enum TextureMode +{ + Game, + Level, + Random, +} diff --git a/TRRandomizerCore/Levels/TR1RCombinedLevel.cs b/TRRandomizerCore/Levels/TR1RCombinedLevel.cs index 50e200521..e7d07ef3e 100644 --- a/TRRandomizerCore/Levels/TR1RCombinedLevel.cs +++ b/TRRandomizerCore/Levels/TR1RCombinedLevel.cs @@ -19,4 +19,5 @@ public class TR1RCombinedLevel public bool IsAssault => Is(TR1LevelNames.ASSAULT); public TRDictionary PDPData { get; set; } public Dictionary MapData { get; set; } + public TRGData TRGData { get; set; } } diff --git a/TRRandomizerCore/Levels/TR2RCombinedLevel.cs b/TRRandomizerCore/Levels/TR2RCombinedLevel.cs index ddce8cb08..a7f8e7c4b 100644 --- a/TRRandomizerCore/Levels/TR2RCombinedLevel.cs +++ b/TRRandomizerCore/Levels/TR2RCombinedLevel.cs @@ -19,4 +19,5 @@ public class TR2RCombinedLevel public bool IsAssault => Is(TR2LevelNames.ASSAULT); public TRDictionary PDPData { get; set; } public Dictionary MapData { get; set; } + public TRGData TRGData { get; set; } } diff --git a/TRRandomizerCore/Levels/TR3RCombinedLevel.cs b/TRRandomizerCore/Levels/TR3RCombinedLevel.cs index 3b7788b10..1bb2c2b36 100644 --- a/TRRandomizerCore/Levels/TR3RCombinedLevel.cs +++ b/TRRandomizerCore/Levels/TR3RCombinedLevel.cs @@ -19,5 +19,6 @@ public class TR3RCombinedLevel public bool IsAssault => Is(TR3LevelNames.ASSAULT); public TRDictionary PDPData { get; set; } public Dictionary MapData { get; set; } + public TRGData TRGData { get; set; } public bool HasExposureMeter => Sequence == 16 || Sequence == 17; } diff --git a/TRRandomizerCore/Processors/AbstractLevelProcessor.cs b/TRRandomizerCore/Processors/AbstractLevelProcessor.cs index 312792473..eab979174 100644 --- a/TRRandomizerCore/Processors/AbstractLevelProcessor.cs +++ b/TRRandomizerCore/Processors/AbstractLevelProcessor.cs @@ -1,5 +1,7 @@ using System.Runtime.ExceptionServices; using TRGE.Core; +using TRLevelControl.Model; +using TRLevelControl; namespace TRRandomizerCore.Processors; @@ -50,6 +52,29 @@ protected void SaveLevelInstance() protected abstract void SaveLevel(C level); + public TRGData LoadTRGData(string name) + { + lock (_controlLock) + { + string fullPath = Path.Combine(BasePath, name); + return File.Exists(fullPath) ? TRGControlBase.Read(fullPath) : null; + } + } + + public void SaveTRGData(TRGData data, string name) + { + if (data == null) + { + return; + } + + lock (_controlLock) + { + string fullPath = Path.Combine(BasePath, name); + TRGControlBase.Write(data, fullPath); + } + } + protected void SaveScript() { lock (_controlLock) diff --git a/TRRandomizerCore/Processors/TR1/TR1RLevelProcessor.cs b/TRRandomizerCore/Processors/TR1/TR1RLevelProcessor.cs index d2cb879e2..6431b936a 100644 --- a/TRRandomizerCore/Processors/TR1/TR1RLevelProcessor.cs +++ b/TRRandomizerCore/Processors/TR1/TR1RLevelProcessor.cs @@ -70,11 +70,20 @@ protected override void ReloadLevelData(TR1RCombinedLevel level) level.Data = LoadLevelData(level.Name); level.PDPData = LoadPDPData(level.Script.PdpFileBaseName); level.MapData = LoadMapData(level.Script.MapFileBaseName); + if (level.TRGData != null) + { + level.TRGData = LoadTRGData(level.Script.TrgFileBaseName); + } + if (level.HasCutScene) { level.CutSceneLevel.Data = LoadLevelData(level.CutSceneLevel.Name); level.CutSceneLevel.PDPData = LoadPDPData(level.CutSceneLevel.Script.PdpFileBaseName); level.CutSceneLevel.MapData = LoadMapData(level.CutSceneLevel.Script.MapFileBaseName); + if (level.CutSceneLevel.TRGData != null) + { + level.CutSceneLevel.TRGData = LoadTRGData(level.CutSceneLevel.Script.TrgFileBaseName); + } } } @@ -83,11 +92,13 @@ protected override void SaveLevel(TR1RCombinedLevel level) SaveLevel(level.Data, level.Name); SavePDPData(level.PDPData, level.Script.PdpFileBaseName); SaveMapData(level.MapData, level.Script.MapFileBaseName); + SaveTRGData(level.TRGData, level.Script.TrgFileBaseName); if (level.HasCutScene) { SaveLevel(level.CutSceneLevel.Data, level.CutSceneLevel.Name); SavePDPData(level.CutSceneLevel.PDPData, level.CutSceneLevel.Script.PdpFileBaseName); SaveMapData(level.CutSceneLevel.MapData, level.CutSceneLevel.Script.MapFileBaseName); + SaveTRGData(level.CutSceneLevel.TRGData, level.CutSceneLevel.Script.TrgFileBaseName); } SaveScript(); diff --git a/TRRandomizerCore/Processors/TR2/TR2RLevelProcessor.cs b/TRRandomizerCore/Processors/TR2/TR2RLevelProcessor.cs index 68d4a3f3a..ffd9538ea 100644 --- a/TRRandomizerCore/Processors/TR2/TR2RLevelProcessor.cs +++ b/TRRandomizerCore/Processors/TR2/TR2RLevelProcessor.cs @@ -70,11 +70,20 @@ protected override void ReloadLevelData(TR2RCombinedLevel level) level.Data = LoadLevelData(level.Name); level.PDPData = LoadPDPData(level.Script.PdpFileBaseName); level.MapData = LoadMapData(level.Script.MapFileBaseName); + if (level.TRGData != null) + { + level.TRGData = LoadTRGData(level.Script.TrgFileBaseName); + } + if (level.HasCutScene) { level.CutSceneLevel.Data = LoadLevelData(level.CutSceneLevel.Name); level.CutSceneLevel.PDPData = LoadPDPData(level.CutSceneLevel.Script.PdpFileBaseName); level.CutSceneLevel.MapData = LoadMapData(level.CutSceneLevel.Script.MapFileBaseName); + if (level.CutSceneLevel.TRGData != null) + { + level.CutSceneLevel.TRGData = LoadTRGData(level.CutSceneLevel.Script.TrgFileBaseName); + } } } @@ -83,11 +92,13 @@ protected override void SaveLevel(TR2RCombinedLevel level) SaveLevel(level.Data, level.Name); SavePDPData(level.PDPData, level.Script.PdpFileBaseName); SaveMapData(level.MapData, level.Script.MapFileBaseName); + SaveTRGData(level.TRGData, level.Script.TrgFileBaseName); if (level.HasCutScene) { SaveLevel(level.CutSceneLevel.Data, level.CutSceneLevel.Name); SavePDPData(level.CutSceneLevel.PDPData, level.CutSceneLevel.Script.PdpFileBaseName); SaveMapData(level.CutSceneLevel.MapData, level.CutSceneLevel.Script.MapFileBaseName); + SaveTRGData(level.CutSceneLevel.TRGData, level.CutSceneLevel.Script.TrgFileBaseName); } SaveScript(); diff --git a/TRRandomizerCore/Processors/TR3/TR3RLevelProcessor.cs b/TRRandomizerCore/Processors/TR3/TR3RLevelProcessor.cs index b4c60783d..eb4fc41ca 100644 --- a/TRRandomizerCore/Processors/TR3/TR3RLevelProcessor.cs +++ b/TRRandomizerCore/Processors/TR3/TR3RLevelProcessor.cs @@ -70,11 +70,20 @@ protected override void ReloadLevelData(TR3RCombinedLevel level) level.Data = LoadLevelData(level.Name); level.PDPData = LoadPDPData(level.Script.PdpFileBaseName); level.MapData = LoadMapData(level.Script.MapFileBaseName); + if (level.TRGData != null) + { + level.TRGData = LoadTRGData(level.Script.TrgFileBaseName); + } + if (level.HasCutScene) { level.CutSceneLevel.Data = LoadLevelData(level.CutSceneLevel.Name); level.CutSceneLevel.PDPData = LoadPDPData(level.CutSceneLevel.Script.PdpFileBaseName); level.CutSceneLevel.MapData = LoadMapData(level.CutSceneLevel.Script.MapFileBaseName); + if (level.CutSceneLevel.TRGData != null) + { + level.CutSceneLevel.TRGData = LoadTRGData(level.CutSceneLevel.Script.TrgFileBaseName); + } } } @@ -83,11 +92,13 @@ protected override void SaveLevel(TR3RCombinedLevel level) SaveLevel(level.Data, level.Name); SavePDPData(level.PDPData, level.Script.PdpFileBaseName); SaveMapData(level.MapData, level.Script.MapFileBaseName); + SaveTRGData(level.TRGData, level.Script.TrgFileBaseName); if (level.HasCutScene) { SaveLevel(level.CutSceneLevel.Data, level.CutSceneLevel.Name); SavePDPData(level.CutSceneLevel.PDPData, level.CutSceneLevel.Script.PdpFileBaseName); SaveMapData(level.CutSceneLevel.MapData, level.CutSceneLevel.Script.MapFileBaseName); + SaveTRGData(level.CutSceneLevel.TRGData, level.CutSceneLevel.Script.TrgFileBaseName); } SaveScript(); diff --git a/TRRandomizerCore/Randomizers/Shared/TextureAllocator.cs b/TRRandomizerCore/Randomizers/Shared/TextureAllocator.cs new file mode 100644 index 000000000..7004b2932 --- /dev/null +++ b/TRRandomizerCore/Randomizers/Shared/TextureAllocator.cs @@ -0,0 +1,265 @@ +using Newtonsoft.Json; +using TRGE.Core; +using TRLevelControl.Model; +using TRRandomizerCore.Editors; +using TRRandomizerCore.Helpers; +using TRRandomizerCore.Textures; + +namespace TRRandomizerCore.Randomizers; + +public class TextureAllocator + where T : Enum + where R : Enum +{ + private readonly TRTexInfo _texInfo; + private readonly Dictionary _trgData; + private readonly Dictionary> _mapData; + + public Random Generator { get; set; } + public RandomizerSettings Settings { get; set; } + + public TextureAllocator(TRGameVersion version) + { + _texInfo = JsonConvert.DeserializeObject>(File.ReadAllText($@"Resources\{version}\Textures\texinfo.json")); + _trgData = new(); + _mapData = new(); + } + + public void LoadData(TRRScriptedLevel level, TRGData trgData, Dictionary mapData) + { + _trgData[level] = trgData; + _mapData[level] = mapData; + } + + public void Allocate(Func, bool> saveData) + { + Dictionary> textureCache = _trgData.ToDictionary(l => l.Key, l => l.Value.Textures.ToList()); + List allTextures = new(textureCache.Values.SelectMany(t => t).Distinct()); + List levels = new(_trgData.Keys); + List levelSwaps = new(); + + if (Settings.TextureMode == TextureMode.Game) + { + levelSwaps.AddRange(levels); + do + { + levelSwaps.Shuffle(Generator); + } + while (levelSwaps.Any(l => levels.IndexOf(l) == levelSwaps.IndexOf(l))); + } + + foreach (var (level, trgData) in _trgData) + { + List baseTextures = new(); + List newTextures = new(); + if (Settings.TextureMode == TextureMode.Level) + { + baseTextures.AddRange(trgData.Textures); + newTextures.AddRange(trgData.Textures); + } + else if (Settings.TextureMode == TextureMode.Game) + { + TRRScriptedLevel nextLevel = levelSwaps[levels.IndexOf(level)]; + baseTextures.AddRange(textureCache[nextLevel]); + newTextures = textureCache[nextLevel]; + + while (newTextures.Count < trgData.Textures.Count) + { + newTextures.Add(newTextures.RandomItem(Generator)); + } + } + else + { + baseTextures.AddRange(trgData.Textures); + newTextures.AddRange(allTextures.RandomSelection(Generator, trgData.Textures.Count)); + } + + newTextures.Shuffle(Generator); + + if (Settings.MatchTextureTypes) + { + for (int i = 0; i < trgData.Textures.Count; i++) + { + ushort originalTexture = trgData.Textures[i]; + ushort newTexture = newTextures[i]; + + // Try to match levers, ladders and windows/gates etc with similar from the import set. + // By default, ensure everything else is opaque. + if (!_texInfo.Categories.Keys.Any(c => SelectTexture(originalTexture, ref newTexture, c, baseTextures))) + { + while (!_texInfo.Categories[TRTexCategory.Opaque].Contains(newTexture)) + { + newTexture = newTextures.RandomItem(Generator); + } + } + + newTextures[i] = newTexture; + } + } + + // Effectively disable animated textures as it's unclear where these are defined so they can't be + // properly controlled yet. This does not affect water surfaces. + List animatedIndices = new(trgData.Textures + .Where(_texInfo.Animated.Contains) + .Select(t => trgData.Textures.IndexOf(t))); + if (animatedIndices.Count > 0) + { + ushort defaultTexture = newTextures[animatedIndices.RandomItem(Generator)]; + animatedIndices.ForEach(i => newTextures[i] = defaultTexture); + } + + Dictionary itemRemap = Settings.TextureMode == TextureMode.Game && Settings.MatchTextureItems + ? RemapItems(level, levelSwaps[levels.IndexOf(level)]) + : null; + + trgData.Textures.Clear(); + trgData.Textures.AddRange(newTextures); + if (!saveData(level, trgData, itemRemap)) + { + break; + } + } + } + + private bool SelectTexture(ushort originalTexture, ref ushort targetTexture, + TRTexCategory category, List baseTextures) + { + if (category == TRTexCategory.Opaque) + { + return false; + } + + SortedSet controlSet = _texInfo.Categories[category]; + if (!controlSet.Contains(originalTexture)) + { + return false; + } + + if (category == TRTexCategory.Fixed) + { + targetTexture = originalTexture; + } + else if (!baseTextures.Any(controlSet.Contains)) + { + targetTexture = _texInfo.Defaults[category]; + } + else + { + while (!controlSet.Contains(targetTexture)) + { + targetTexture = baseTextures.RandomItem(Generator); + } + } + + return true; + } + + private Dictionary RemapItems(TRRScriptedLevel level, TRRScriptedLevel otherLevel) + { + string levelFile = level.LevelFileBaseName.ToUpper(); + string otherLevelFile = otherLevel.LevelFileBaseName.ToUpper(); + + Dictionary> currentAreaMap = _texInfo.ItemFlags.FirstOrDefault(a => a.ContainsKey(levelFile)); + Dictionary> otherAreaMap = _texInfo.ItemFlags.FirstOrDefault(a => a.ContainsKey(otherLevelFile)); + if (currentAreaMap == null || otherAreaMap == null) + { + return null; + } + + Dictionary currentLevelMap = currentAreaMap[levelFile]; + Dictionary otherLevelMap = otherAreaMap[otherLevelFile]; + + Dictionary newMapping = new(_mapData[level]); + Dictionary otherMapping = _mapData[otherLevel]; + + List pairFlags = new() + { + TRItemFlags.PairA, + TRItemFlags.PairB, + TRItemFlags.PairC, + }; + + bool IsPaired(TRItemFlags flags) + => pairFlags.Any(f => flags.HasFlag(f)); + + bool IsDoor(TRItemFlags flags) + => flags.HasFlag(TRItemFlags.LeftDoor) || flags.HasFlag(TRItemFlags.RightDoor); + + bool TryMatch(T type, TRItemFlags flags, Dictionary otherLevelMap) + { + List otherTypes = new(); + foreach (var (otherType, otherFlags) in otherLevelMap) + { + if (flags == otherFlags) + { + otherTypes.Add(otherType); + } + } + + if (otherTypes.Count > 0) + { + T otherType = otherTypes.RandomItem(Generator); + if (otherMapping.ContainsKey(otherType)) + { + newMapping[type] = otherMapping[otherType]; + } + else + { + // The "first" models don't have mappings e.g. Vilcabamba pushblock, so we mimic that approach. + newMapping.Remove(type); + } + return true; + } + else if (IsDoor(flags) && !IsPaired(flags)) + { + // Applicable where normally paired doors are used in single instances, like Caves room 17. + return pairFlags.Any(pair => TryMatch(type, flags | pair, otherLevelMap)); + } + + return false; + } + + bool DoorMatch(T type, TRItemFlags flags, Dictionary otherLevelMap) + { + if (!IsDoor(flags) || !IsPaired(flags) || flags.HasFlag(TRItemFlags.FourClick)) + { + return false; + } + + pairFlags.ForEach(pair => flags &= ~pair); + return pairFlags.Any(pair => TryMatch(type, flags | pair, otherLevelMap)); + } + + foreach (var (type, flags) in currentLevelMap) + { + otherMapping = _mapData[otherLevel]; + if (!TryMatch(type, flags, otherLevelMap)) + { + if (DoorMatch(type, flags, otherLevelMap)) + { + continue; + } + + // The used level doesn't have an equivalent item, so check other area levels + // e.g. if using Folly, search Colly, Midas etc too. + foreach (var (siblingLevel, siblingMap) in otherAreaMap) + { + if (siblingMap == otherLevelMap) + { + continue; + } + + otherMapping = _mapData[_mapData.Keys.First(l => string.Equals(l.LevelFileBaseName, + siblingLevel, StringComparison.InvariantCultureIgnoreCase))]; + if (TryMatch(type, flags, siblingMap) + || DoorMatch(type, flags, siblingMap)) + { + break; + } + } + } + } + + return newMapping; + } +} diff --git a/TRRandomizerCore/Randomizers/TR1/Remastered/TR1RTextureRandomizer.cs b/TRRandomizerCore/Randomizers/TR1/Remastered/TR1RTextureRandomizer.cs new file mode 100644 index 000000000..4831bae34 --- /dev/null +++ b/TRRandomizerCore/Randomizers/TR1/Remastered/TR1RTextureRandomizer.cs @@ -0,0 +1,34 @@ +using TRGE.Core; +using TRLevelControl.Model; + +namespace TRRandomizerCore.Randomizers; + +public class TR1RTextureRandomizer : BaseTR1RRandomizer +{ + public override void Randomize(int seed) + { + TextureAllocator allocator = new(TRGameVersion.TR1) + { + Generator = new(seed), + Settings = Settings, + }; + + foreach (TRRScriptedLevel level in Levels) + { + TRGData trgData = LoadTRGData(level.TrgFileBaseName); + Dictionary mapData = LoadMapData(level.MapFileBaseName); + allocator.LoadData(level, trgData, mapData); + if (!TriggerProgress()) + { + return; + } + } + + allocator.Allocate((level, trgData, mapData) => + { + SaveMapData(mapData, level.MapFileBaseName); + SaveTRGData(trgData, level.TrgFileBaseName); + return TriggerProgress(); + }); + } +} diff --git a/TRRandomizerCore/Randomizers/TR2/Remastered/TR2RTextureRandomizer.cs b/TRRandomizerCore/Randomizers/TR2/Remastered/TR2RTextureRandomizer.cs new file mode 100644 index 000000000..23e9b9684 --- /dev/null +++ b/TRRandomizerCore/Randomizers/TR2/Remastered/TR2RTextureRandomizer.cs @@ -0,0 +1,34 @@ +using TRGE.Core; +using TRLevelControl.Model; + +namespace TRRandomizerCore.Randomizers; + +public class TR2RTextureRandomizer : BaseTR2RRandomizer +{ + public override void Randomize(int seed) + { + TextureAllocator allocator = new(TRGameVersion.TR2) + { + Generator = new(seed), + Settings = Settings, + }; + + foreach (TRRScriptedLevel level in Levels) + { + TRGData trgData = LoadTRGData(level.TrgFileBaseName); + Dictionary mapData = LoadMapData(level.MapFileBaseName); + allocator.LoadData(level, trgData, mapData); + if (!TriggerProgress()) + { + return; + } + } + + allocator.Allocate((level, trgData, mapData) => + { + SaveMapData(mapData, level.MapFileBaseName); + SaveTRGData(trgData, level.TrgFileBaseName); + return TriggerProgress(); + }); + } +} diff --git a/TRRandomizerCore/Randomizers/TR3/Remastered/TR3RTextureRandomizer.cs b/TRRandomizerCore/Randomizers/TR3/Remastered/TR3RTextureRandomizer.cs new file mode 100644 index 000000000..fbc37c50b --- /dev/null +++ b/TRRandomizerCore/Randomizers/TR3/Remastered/TR3RTextureRandomizer.cs @@ -0,0 +1,34 @@ +using TRGE.Core; +using TRLevelControl.Model; + +namespace TRRandomizerCore.Randomizers; + +public class TR3RTextureRandomizer : BaseTR3RRandomizer +{ + public override void Randomize(int seed) + { + TextureAllocator allocator = new(TRGameVersion.TR3) + { + Generator = new(seed), + Settings = Settings, + }; + + foreach (TRRScriptedLevel level in Levels) + { + TRGData trgData = LoadTRGData(level.TrgFileBaseName); + Dictionary mapData = LoadMapData(level.MapFileBaseName); + allocator.LoadData(level, trgData, mapData); + if (!TriggerProgress()) + { + return; + } + } + + allocator.Allocate((level, trgData, mapData) => + { + SaveMapData(mapData, level.MapFileBaseName); + SaveTRGData(trgData, level.TrgFileBaseName); + return TriggerProgress(); + }); + } +} diff --git a/TRRandomizerCore/Resources/TR1/Textures/texinfo.json b/TRRandomizerCore/Resources/TR1/Textures/texinfo.json new file mode 100644 index 000000000..1396131ba --- /dev/null +++ b/TRRandomizerCore/Resources/TR1/Textures/texinfo.json @@ -0,0 +1 @@ +{"Categories":{"Opaque":[15,17,18,19,20,24,25,27,28,30,31,32,34,36,37,38,39,40,41,42,43,44,45,46,47,48,49,52,53,54,55,57,63,65,66,67,68,69,70,72,73,74,81,82,84,86,87,88,91,92,93,94,95,96,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,121,126,127,128,129,130,131,132,133,134,135,136,137,138,140,143,144,145,146,148,149,150,151,152,153,154,155,156,163,164,165,167,168,173,174,177,178,179,181,182,183,184,185,186,187,188,189,190,191,192,193,194,195,196,197,198,200,207,208,210,211,212,214,215,216,217,218,221,230,231,232,233,234,235,237,238,239,241,242,243,244,245,249,250,251,252,253,256,257,258,261,262,263,264,265,266,267,268,269,271,272,273,274,275,276,277,278,279,280,281,283,284,285,286,287,288,289,290,292,293,296,297,301,302,303,304,305,307,309,310,311,312,313,314,315,316,320,324,325,326,327,328,329,331,332,335,336,337,338,340,341,342,343,345,346,347,349,350,351,352,353,354,357,359,360,361,362,363,364,365,366,367,368,369,370,371,372,373,374,377,378,379,380,381,382,383,384,385,386,387,388,389,391,392,393,394,395,396,397,401,407,408,409,411,412,413,414,415,416,417,418,419,422,423,424,425,426,427,428,429,430,431,432,433,434,435,438,439,440,441,442,445,446,449,450,451,452,453,455,456,457,459,460,461,463,464,465,466,467,468,469,470,471,472,474,475,476,478,479,480,482,484,485,486,487,488,489,490,491,492,493,494,496,498,499,500,501,502,503,504,505,506,507,510,511,513,514,515,516,519,520,521,522,524,525,526,527,530,532,533,534,535,537,541,542,543,561,562,563,564,565,566,567,568,569,572,573,575,576,577,578,579,580,581,583,584,585,586,587,595,596,597,598,599,600,602,603,604,605,606,607,608,609,610,621,622,623,624,625,626,627,630,631,632,633,635,637,640,641,642,643,645,646,648,650,653,658,660,661,662,663,665,666,668,669,671,672,673,674,676,679,680,681,682,683,684,685,686,688,689,690,691,692,693,694,696,697,698,699,700,701,702,703,704,706,707,709,710,711,714,715,716,717,718,719,721,722,724,725,727,728,729,730,731,732,733,734,735,736,737,738,739,859,1002,1008,1009,1010,1011,1012,1015,2049,2050,2051,2052,3016,3025,3026,3027,3029,3031,3032,3033,3034,3035,3036,3037,3038,3039,3040,3041,3044,3045,3046,3047,3048,3049,3050,3051,3052,4128,5060,5869,7002,7003,7004,7014,7021,7051,7052,7054,7055,7058,7060],"Fixed":[1,2,60,61,254,255,259,454,509,545,546,547,548,549,550,551,552,553,554,555,556,557,559,560,591,592,611,612,613,614,615,616,617,618,619,659,664,667,670,675,678,687,695,800,803,804,805,806,807,808,809,810,811,812,813,814,815,816,817,818,819,820,821,822,823,824,825,826,827,828,829,833,835,839,840,841,842,843,844,845,846,847,848,849,850,851,852,853,861,862,863,864,866,867,869,870,971,972,973,974,975,976,977,978,979,980,981,982,993,994,995,997,998,999,1006,1480,1496,1497,1498,1499,1500,1501,1502,1503,1504,1505,1506,1507,2000,2001,2002,2003,2004,2005,2006,2007,2008,2009,2010,2011,2013,2014,2015,2016,2017,2018,2019,2020,2021,2022,2023,2024,2025,2026,2027,2028,2029,2030,2031,2032,2033,2034,2035,2036,2037,2038,2039,2040,2041,2042,2043,2044,2045,2046,2047,2048,2053,2054,3015,3021,3023,3108,7068,7508,7509,7510,7511,9097],"Lever":[59,83,147,222,291,294,306,308,322,339,390,481,544,574,582,634,649,677,708,1003],"Ladder":[],"Transparent":[56,323,330,400,477,588,620,638,639,866]},"Animated":[554,558,571,590,651,652,654,655],"Defaults":{"Transparent":477,"Lever":34},"ItemFlags":[{"GYM.PHD":{}},{"LEVEL1.PHD":{"Door1":16777345,"Door2":16777346,"Door3":130,"Door4":17,"FallingBlock":512,"WallSwitch":4096,"DartEmitter":17039360,"Dart_H":33816576},"LEVEL2.PHD":{"Door1":17,"Door2":17,"Door4":17,"Door5":16777345,"Door6":16777346,"Trapdoor1":8,"FallingBlock":512,"PushBlock1":1024,"WallSwitch":4096,"UnderwaterSwitch":8192},"LEVEL3A.PHD":{"Door1":17,"WallSwitch":4096},"LEVEL3B.PHD":{"Door1":20,"Door2":20,"Door3":20,"Door4":320,"Door5":17,"Door6":17,"Door8":17,"FallingBlock":512,"PushBlock1":1024,"WallSwitch":4096,"RollingBall":131072}},{"LEVEL4.PHD":{"Door1":17,"Door2":132,"Door3":129,"Door4":17,"Door5":16777345,"Door6":16777346,"Door7":17,"FallingBlock":512,"PushBlock1":1024,"WallSwitch":4096,"UnderwaterSwitch":8192,"RollingBall":131072},"LEVEL5.PHD":{"Door1":33,"Door2":129,"Door5":17,"Door6":130,"Door7":132,"Door8":17,"PushBlock1":1024,"WallSwitch":4096,"RollingBall":131072},"LEVEL6.PHD":{"Door1":17,"Door2":17,"Door3":17,"Door4":17,"Door5":17,"Door6":130,"Door7":129,"FallingBlock":512,"PushBlock1":1024,"PushBlock2":1024,"SlammingDoor":2048,"WallSwitch":4096},"LEVEL7A.PHD":{"Door1":17,"Door2":17,"Door3":17,"Trapdoor1":8,"FallingBlock":512,"PushBlock1":1024,"PushBlock2":1024,"WallSwitch":4096,"UnderwaterSwitch":8192},"LEVEL7B.PHD":{"Door1":17,"Door2":33,"Door3":17,"Door4":129,"Door5":17,"FallingBlock":512,"PushBlock1":1024,"SlammingDoor":2048,"WallSwitch":4096,"UnderwaterSwitch":8192,"DartEmitter":17039360,"Dart_H":33816576}},{"LEVEL8A.PHD":{"Door1":16777233,"Door2":17,"Door3":16777234,"Door4":33554561,"Door5":33554562,"Trapdoor1":8,"PushBlock1":1024,"WallSwitch":4096,"UnderwaterSwitch":8192,"RollingBall":131072},"LEVEL8B.PHD":{"Door1":17,"Door2":17,"Door5":16777345,"Door6":16777346,"PushBlock1":1024,"WallSwitch":4096},"LEVEL8C.PHD":{"Door1":17,"Door3":129,"Door4":132,"Door5":16777345,"Door6":16777346,"PushBlock1":1024,"SlammingDoor":2048,"WallSwitch":4096,"UnderwaterSwitch":8192}},{"LEVEL10A.PHD":{"Door1":17,"Door2":17,"Door3":16777345,"Door4":16777346,"Trapdoor2":8,"FallingBlock":512,"PushBlock1":1024,"PushBlock2":1024,"PushBlock3":1024,"PushBlock4":1024,"WallSwitch":4096,"RollingBall":131072},"LEVEL10B.PHD":{"Door3":130,"Door4":17,"Door5":17,"Door6":17,"Door7":17,"Door8":17,"Trapdoor2":8,"PushBlock1":1024,"SlammingDoor":2048,"WallSwitch":4096,"UnderwaterSwitch":8192,"RollingBall":131072,"DartEmitter":17039360,"Dart_H":33816576},"LEVEL10C.PHD":{"Door1":17,"Door2":17,"FallingBlock":512,"PushBlock1":1024,"SlammingDoor":2048,"WallSwitch":4096,"RollingBall":131072,"DartEmitter":17039360,"Dart_H":33816576}}]} \ No newline at end of file diff --git a/TRRandomizerCore/Resources/TR2/Textures/texinfo.json b/TRRandomizerCore/Resources/TR2/Textures/texinfo.json new file mode 100644 index 000000000..583e26e68 --- /dev/null +++ b/TRRandomizerCore/Resources/TR2/Textures/texinfo.json @@ -0,0 +1 @@ +{"Categories":{"Opaque":[0,1,2,3,6,10,12,13,14,15,16,19,23,27,29,31,33,35,36,38,39,40,41,42,43,46,48,50,51,54,57,58,59,62,63,66,67,68,69,70,73,74,75,77,78,79,85,86,88,92,94,95,96,97,99,105,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,129,130,135,138,140,144,146,152,153,154,155,156,157,158,160,161,162,163,164,166,167,168,170,172,174,177,179,180,183,184,187,188,189,191,192,194,197,199,201,203,204,205,209,210,212,223,224,225,226,227,232,239,243,249,250,251,253,254,258,260,261,262,263,264,265,266,267,268,271,272,274,276,278,279,280,281,283,284,286,288,289,290,293,294,295,296,301,303,305,306,308,309,311,312,315,316,319,320,322,325,326,327,329,341,342,344,345,346,347,348,349,350,351,352,353,354,356,359,360,361,362,363,364,367,369,370,371,372,373,374,375,376,377,379,380,381,382,384,385,386,387,388,390,391,394,395,397,398,399,400,401,402,403,406,407,411,412,413,414,415,416,417,419,420,423,425,427,428,429,431,432,433,434,435,436,438,440,442,443,445,447,448,451,453,456,458,459,462,463,464,466,467,468,469,470,474,475,476,477,479,480,481,482,483,485,486,490,491,493,494,495,497,498,500,501,502,503,504,506,509,510,511,512,513,514,516,517,518,519,520,522,523,524,525,526,528,531,532,536,537,538,539,540,543,545,546,547,548,549,550,551,554,557,559,561,562,563,564,567,571,572,574,575,576,577,578,579,580,582,583,586,587,588,589,590,591,593,594,595,596,598,601,602,604,605,606,607,608,610,611,612,613,614,615,616,617,619,620,629,630,632,633,639,640,641,642,643,644,645,647,648,649,650,652,654,655,656,657,659,660,661,662,663,666,667,668,669,670,671,672,673,674,676,679,680,681,683,684,687,688,690,691,694,695,696,697,698,699,700,701,702,705,706,707,708,709,710,711,714,715,716,717,718,719,721,722,731,735,736,737,738,739,740,741,743,745,746,748,751,754,758,761,762,763,764,765,773,774,775,777,784,792,793,794,795,796,797,798,799,800,802,804,805,807,808,809,810,812,816,819,820,822,824,831,832,833,835,837,838,839,840,841,842,843,844,845,846,847,849,850,858,861,866,867,869,870,874,875,876,879,880,882,883,884,891,892,894,895,896,897,898,899,900,901,902,903,904,906,908,910,911,913,914,916,917,924,925,927,928,929,930,931,932,933,934,935,937,938,942,943,945,946,947,948,952,953,956,957,960,962,963,964,965,967,968,969,970,972,975,976,977,978,980,981,982,983,986,987,989,998,999,1000,1003,1004,1005,1006,1007,1010,1011,1012,1013,1028,1029,1030,1033,1035,1036,1037,1038,1039,1040,1042,1043,1044,1045,1046,1047,1048,1049,1050,1051,1052,1053,1054,1055,1056,1058,1059,1060,1061,1062,1063,1064,1065,1068,1070,1071,1072,1073,1074,1075,1076,1077,1087,1088,1089,1091,1095,1096,1097,1098,1099,1100,1101,1103,1104,1105,1106,1107,1108,1109,1110,1114,1115,1116,1117,1119,1120,1121,1122,1124,1125,1126,1127,1130,1131,1132,1133,1134,1136,1137,1138,1139,1140,1142,1143,1144,1145,1146,1147,1148,1149,1151,1152,1153,1154,1155,1156,1157,1158,1159,1160,1161,1163,1165,1167,1168,1169,1174,1175,1176,1177,1178,1179,1180,1181,1182,1183,1184,1185,1186,1187,1189,1190,1191,1192,1193,1194,1195,1196,1197,1198,1199,1200,1201,1203,1204,1205,1206,1207,1208,1209,1210,1211,1212,1213,1214,1216,1217,1220,1221,1222,1227,1228,1229,1232,1234,1235,1236,1238,1239,1240,1241,1242,1243,1244,1245,1246,1247,1248,1249,1250,1251,1252,1253,1254,1256,1258,1259,1263,1266,1267,1271,1272,1275,1276,1277,1279,1280,1281,1282,1283,1284,1285,1288,1290,1292,1293,1294,1295,1298,1299,1300,1301,1302,1303,1304,1305,1307,1309,1311,1312,1313,1314,1315,1316,1317,1323,1326,1327,1328,1329,1330,1331,1332,1335,1336,1337,1338,1341,1342,1343,1345,1346,1347,1349,1350,1352,1353,1354,1355,1356,1358,1360,1361,1362,1363,1364,1365,1366,1367,1370,1371,1372,1373,1375,1377,1379,1381,1383,1387,1388,1390,1391,1392,1393,1394,1395,1396,1397,1400,1401,1402,1403,1405,1406,1407,1408,1409,1410,1411,1412,1413,1414,1415,1416,1418,1421,1422,1423,1424,1425,1426,1427,1428,1431,1434,1435,1436,1438,1439,1440,1441,1442,1443,1444,1445,1446,1447,1448,1449,1450,1452,1453,1454,1456,1458,1459,1460,1461,1462,1463,1464,1465,1466,1467,1468,1469,1470,1473,1479,1535,1553,1561,2079,2080,2081,2082,2083,2084,2085,2086,2087,2088,2236,2238,3406,4216,5800,5807,5808,5809,5823,5838,5840,5841,5844,5866,5867,5868,5883,5885,5886,5888,5890,5891,5892,7003,7031,7032,7033,7034,7036,7041],"Fixed":[28,52,56,65,101,215,216,217,219,221,237,307,328,330,331,332,333,335,336,337,338,339,340,368,424,426,441,449,597,636,637,638,776,779,780,785,786,813,823,827,854,859,860,863,864,865,877,881,886,887,890,984,985,988,990,993,994,995,996,997,1008,1014,1015,1016,1017,1093,1094,1226,1230,1231,1268,1274,1340,1344,1374,1384,1385,1386,1471,1474,1477,1480,1483,1484,1485,1486,1487,1488,1489,1490,1491,1492,1493,1494,1495,1496,1497,1498,1499,1500,1501,1502,1503,1504,1505,1506,1507,1509,1510,1511,1512,1514,1515,1516,1517,1520,1521,1522,1523,1534,1539,1545,1549,1550,1551,1552,1563,1564,1566,1567,1568,1569,1570,1571,1574,1575,2000,2001,2002,2003,2004,2005,2006,2007,2008,2009,2010,2011,2013,2014,2015,2016,2017,2018,2019,2020,2021,2022,2023,2024,2025,2026,2027,2028,2029,2030,2031,2032,2033,2034,2035,2036,2037,2038,2039,2040,2041,2042,2043,2044,2045,2046,2047,2048,2049,2052,2053,2054,2055,2056,2057,2058,2059,2060,2061,2062,2063,2064,2077,2078,2089,2090,2091,2092,2093,2094,2095,2096,2097,2098,2099,2100,2200,2201,2202,2203,2215,2220,2224,2227,2231,2239,2240,2241,2242,2243,2244,2245,2246,2247,2248,2249,2250,2251,2252,2253,2254,2255,2256,2257,2258,2259,2260,2261,2262,2263,3025,3026,3027,3028,3029,3030,3031,3032,3033,3034,3035,3036,3037,3038,3039,3040,3041,3042,3043,3044,3045,3046,3047,3048,3049,3100,3101,3102,3103,3130,3131,3132,3133,3134,3135,3136,3137,3138,3139,3140,3141,3142,3143,3144,3145,3146,3147,3148,3149,3336,3337,3338,3339,3340,3341,3342,3343,3344,3345,3346,3347,3348,3349,3350,3351,3352,3353,3354,3355,3356,3357,3358,3359,3360,3361,3362,3363,3364,3365,3366,3367,3368,3369,3370,3371,3372,3373,3374,3375,3376,3377,3378,3379,3380,3381,3382,3383,3384,3385,3386,3387,3388,3389,3390,3391,3392,3393,3394,3395,3396,3397,3398,3399,3400,3401,3402,3403,3404,3405,3407,3450,3451,3452,3453,3454,3455,3456,3457,3458,3459,3460,3461,3462,3463,3464,3515,3516,3600,3602,3650,3700,3701,3702,3703,3704,3705,3706,3707,3708,3709,3710,3711,3720,3721,3722,3723,3724,3725,3726,3727,3728,3729,3730,3731,3732,3733,3734,3735,3736,3737,3738,3739,3740,3741,3742,3743,4005,4048,4101,4111,4118,4119,4120,4121,4196,4222,4261],"Lever":[132,211,252,452,521,635,665,749,868,878,955,1066,1202,1286],"Ladder":[24,133,134,200,227,264,273,366,461,465,533,600,651,750,767,825,828,836,991,1009,1057,1172,1233,1265,1270,1273,1318,1368,1369,1431,1451],"Transparent":[84,142,159,198,207,259,282,317,318,321,365,396,404,421,422,478,505,603,623,628,631,634,646,720,815,890,949,951,1069,1090,1141,1150,2070,2075,2206,2235,2237,3104,3105,3608]},"Animated":[375,401,402,403,413,414,415,416,654,721,722],"Defaults":{"Transparent":159,"Lever":1202,"Ladder":1172},"ItemFlags":[{"ASSAULT.TR2":{"Door1":17,"Door2":33,"Door3":33,"Door4":33,"BreakableWindow1":16384,"PushButtonSwitch":32768},"HOUSE.TR2":{"Door1":17,"Door2":33,"Door3":33,"Door4":33,"BreakableWindow1":16384,"PushButtonSwitch":32768}},{"WALL.TR2":{"Door1":34,"Door2":17,"Trapdoor1":8,"FallingBlock":512,"PushBlock1":1024,"WallSwitch":4096,"RollingBall":16908288,"TeethSpikesOrGlassShards":524288,"RollingSpindle":1048576},"BOAT.TR2":{"Door1":16777345,"Door2":16777346,"Door3":33554561,"Door4":17,"LiftingDoor2":33554562,"Door5":67108993,"LiftingDoor1":67108994,"LiftingDoor3":132,"BreakableWindow1":16384,"PushButtonSwitch":32768,"Trapdoor1":8,"WallSwitch":4096,"UnderwaterSwitch":8192},"VENICE.TR2":{"Door1":16777345,"Door2":16777346,"Door3":17,"Door4":17,"Door5":33554561,"LiftingDoor1":33554562,"LiftingDoor3":132,"BreakableWindow1":16384,"PushBlock1":1024,"PushBlock2":1024,"Trapdoor1":8,"Trapdoor2":8,"WallSwitch":4096,"UnderwaterSwitch":8192},"OPERA.TR2":{"Door1":16777282,"Door2":16777281,"Door3":17,"Door4":130,"Door5":34,"LiftingDoor1":17,"LiftingDoor2":20,"LiftingDoor3":132,"BreakableWindow1":16384,"PushButtonSwitch":32768,"PushBlock1":1024,"Trapdoor1":8,"UnderwaterSwitch":8192,"BouldersOrSnowballs":33685504,"TeethSpikesOrGlassShards":524288}},{"RIG.TR2":{"Door1":17,"Door3":17,"BreakableWindow1":16384,"PushButtonSwitch":32768,"PushBlock1":1024,"Trapdoor1":8,"Trapdoor2":8,"UnderwaterSwitch":8192,"RollingStorageDrums":67239936},"PLATFORM.TR2":{"Door1":17,"Door2":17,"Door3":17,"PushButtonSwitch":32768,"PushBlock1":1024,"Trapdoor1":8,"Trapdoor2":8,"UnderwaterSwitch":8192}},{"UNWATER.TR2":{"Door1":17,"Door2":17,"Door3":17,"PushBlock1":1024,"Trapdoor1":8,"UnderwaterSwitch":8192,"WallSwitch":4096},"KEEL.TR2":{"Door1":17,"Door3":16777250,"Door4":16777249,"Door5":17,"LiftingDoor1":17,"FallingBlock":512,"PushButtonSwitch":32768,"PushBlock2":1024,"PushBlock3":1024,"PushBlock4":1024,"Trapdoor1":8,"Trapdoor2":8,"UnderwaterSwitch":8192,"WallSwitch":4096,"RollingStorageDrums":67239936,"TeethSpikesOrGlassShards":524288},"LIVING.TR2":{"Door1":17,"Door2":16777250,"Door3":16777249,"FallingBlock":512,"PushButtonSwitch":32768,"PushBlock1":1024,"PushBlock2":1024,"Trapdoor1":8,"UnderwaterSwitch":8192,"WallSwitch":4096,"RollingStorageDrums":67239936,"TeethSpikesOrGlassShards":524288},"DECK.TR2":{"Door1":17,"Door4":17,"Door5":17,"FallingBlock":512,"PushButtonSwitch":32768,"PushBlock2":1024,"Trapdoor1":8,"UnderwaterSwitch":8192,"WallSwitch":4096,"TeethSpikesOrGlassShards":524288}},{"SKIDOO.TR2":{"Door1":17,"Door2":17,"BreakableWindow1":16384,"PushBlock1":1024,"Trapdoor1":136,"WallSwitch":4096,"BouldersOrSnowballs":33685504},"MONASTRY.TR2":{"Door1":16777345,"Door2":16777346,"Door3":17,"Door4":17,"Door5":17,"BreakableWindow1":16384,"BreakableWindow2":16384,"PushBlock1":1024,"Trapdoor1":8,"WallSwitch":4096,"RollingBall":16908288,"RollingSpindle":1048576},"CATACOMB.TR2":{"Door1":16777345,"Door2":16777346,"Door3":17,"LiftingDoor3":132,"FallingBlock":512,"PushBlock2":1024,"Trapdoor2":136,"WallSwitch":4096,"BouldersOrSnowballs":33685504,"TeethSpikesOrGlassShards":524288},"ICECAVE.TR2":{"Door1":16777345,"Door2":16777346,"Door3":17,"LiftingDoor3":132,"BreakableWindow2":16384,"BouncePad":65536,"PushBlock1":1024,"Trapdoor1":8,"WallSwitch":4096,"BouldersOrSnowballs":33685504,"TeethSpikesOrGlassShards":524288}},{"EMPRTOMB.TR2":{"Door1":16777345,"Door2":16777346,"Door3":17,"Door4":33554561,"Door5":33554562,"LiftingDoor1":17,"LiftingDoor2":20,"LiftingDoor3":132,"FallingBlock":512,"PushButtonSwitch":32768,"BouncePad":65536,"PushBlock2":1024,"Trapdoor1":8,"Trapdoor2":136,"UnderwaterSwitch":8192,"WallSwitch":4096,"RollingBall":16908288,"TeethSpikesOrGlassShards":524288,"RollingSpindle":1048576},"FLOATING.TR2":{"Door1":16777345,"Door2":16777346,"Door3":33554561,"Door4":33554562,"LiftingDoor1":132,"LiftingDoor2":132,"LiftingDoor3":132,"PushBlock1":1024,"Trapdoor1":136,"Trapdoor2":8,"WallSwitch":4096,"RollingBall":16908288,"TeethSpikesOrGlassShards":524288},"XIAN.TR2":{"Door3":16777345,"Door4":16777346,"Door5":132,"LiftingDoor1":132,"WallSwitch":4096}}]} \ No newline at end of file diff --git a/TRRandomizerCore/Resources/TR3/Textures/texinfo.json b/TRRandomizerCore/Resources/TR3/Textures/texinfo.json new file mode 100644 index 000000000..7778a91b3 --- /dev/null +++ b/TRRandomizerCore/Resources/TR3/Textures/texinfo.json @@ -0,0 +1 @@ +{"Categories":{"Opaque":[0,1,2,3,6,7,8,11,13,14,15,16,17,23,30,31,32,38,39,40,44,45,46,49,51,54,55,60,63,64,75,76,77,78,79,80,81,87,89,90,93,94,96,98,100,101,102,103,104,105,107,108,110,111,113,116,119,120,121,125,127,128,133,136,139,141,144,145,146,147,152,153,154,168,170,172,175,180,181,188,189,196,204,206,207,209,210,211,212,213,214,215,220,224,228,229,232,233,234,236,237,238,239,242,243,246,249,250,251,252,260,261,262,263,264,267,268,269,271,273,274,275,277,278,279,281,282,283,284,286,289,290,291,292,293,294,296,297,298,299,301,302,304,305,306,309,311,313,314,315,316,317,320,321,323,324,325,326,327,328,329,330,331,333,336,338,339,340,341,343,344,345,346,350,354,355,356,357,358,359,360,361,365,366,367,368,369,370,371,373,378,379,380,381,390,392,393,394,397,398,399,400,401,403,404,405,406,409,410,412,413,414,415,417,419,421,426,433,437,438,439,440,441,443,447,448,449,450,452,453,455,456,458,459,460,461,462,463,464,465,466,467,469,470,475,476,477,478,479,480,481,482,483,484,485,486,487,488,489,490,491,492,493,494,495,496,497,498,499,500,502,503,504,505,506,507,509,510,511,512,513,514,515,516,517,518,520,521,522,525,526,527,528,529,530,532,533,542,544,545,546,547,548,549,550,551,552,553,554,557,558,560,561,562,563,564,565,566,567,568,569,571,572,573,574,575,576,577,578,579,580,581,582,583,584,585,586,587,588,589,590,591,593,594,595,596,597,598,599,601,602,603,604,605,606,607,609,610,611,612,613,615,616,617,618,619,620,621,622,623,624,625,626,628,631,632,633,634,635,637,639,640,642,643,644,645,646,647,648,649,650,651,652,653,654,656,657,658,659,660,662,663,664,665,666,667,668,669,670,671,672,673,674,675,676,677,680,681,682,683,684,686,687,689,690,691,692,697,698,707,708,710,711,712,714,718,719,720,721,722,723,724,725,726,727,728,729,731,735,736,737,738,739,740,741,742,743,744,745,746,747,748,749,750,751,752,753,754,755,756,759,761,762,774,776,777,779,780,781,782,783,785,786,790,791,792,793,795,796,797,799,800,801,802,805,806,807,808,809,810,813,815,818,819,820,821,822,824,825,826,827,828,830,831,832,833,834,835,836,837,838,839,840,841,842,843,844,845,846,847,849,850,851,852,853,854,855,857,858,859,860,861,862,864,865,866,867,868,869,870,871,873,874,877,878,880,883,885,886,887,888,889,892,893,894,903,904,905,906,907,908,909,911,913,914,915,922,924,925,928,929,931,933,934,935,936,937,938,939,941,942,943,945,946,947,949,950,951,952,953,954,955,956,957,958,959,960,962,963,964,965,966,967,968,969,970,971,973,974,976,977,978,980,981,984,986,987,988,989,993,996,997,998,1000,1001,1003,1005,1010,1011,1012,1013,1014,1015,1019,1027,1029,1030,1031,1034,1035,1036,1037,1038,1039,1040,1042,1043,1044,1045,1046,1047,1049,1050,1055,1061,1062,1064,1065,1066,1067,1068,1069,1070,1071,1072,1073,1074,1075,1076,1078,1081,1082,1083,1084,1085,1088,1093,1094,1095,1096,1097,1098,1099,1100,1101,1104,1106,1108,1110,1112,1114,1115,1116,1117,1118,1119,1120,1121,1122,1123,1124,1125,1126,1127,1128,1129,1132,1135,1136,1137,1138,1139,1141,1144,1145,1146,1147,1148,1149,1150,1153,1155,1156,1157,1158,1159,1160,1161,1162,1163,1164,1165,1166,1167,1168,1169,1170,1171,1172,1174,1175,1180,1182,1183,1184,1185,1188,1192,1194,1195,1196,1197,1198,1199,1200,1201,1203,1206,1207,1209,1210,1212,1213,1214,1217,1220,1222,1223,1224,1225,1228,1229,1230,1231,1232,1233,1235,1236,1237,1238,1239,1240,1246,1248,1249,1250,1251,1252,1255,1257,1259,1261,1263,1264,1265,1266,1267,1268,1269,1270,1271,1273,1274,1275,1276,1279,1280,1281,1282,1283,1288,1293,1294,1295,1296,1297,1298,1299,1301,1302,1303,1304,1305,1306,1307,1308,1309,1310,1311,1312,1314,1315,1316,1317,1320,1321,1322,1323,1324,1325,1326,1327,1328,1329,1331,1333,1334,1335,1337,1338,1339,1340,1341,1343,1344,1346,1347,1349,1350,1351,1352,1355,1358,1360,1366,1369,1370,1371,1372,1373,1374,1377,1379,1381,1382,1383,1387,1389,1390,1391,1392,1393,1394,1395,1396,1397,1398,1399,1404,1407,1408,1409,1410,1412,1414,1415,1416,1418,1420,1421,1423,1424,1425,1426,1427,1429,1430,1431,1435,1436,1437,1438,1439,1440,1441,1442,1443,1444,1446,1449,1453,1456,1457,1458,1460,1461,1462,1463,1465,1469,1470,1471,1472,1473,1474,1475,1476,1477,1478,1479,1480,1481,1482,1483,1484,1485,1486,1487,1488,1489,1490,1492,1493,1494,1495,1496,1497,1498,1499,1501,1502,1503,1504,1505,1506,1507,1510,1511,1512,1513,1514,1515,1516,1517,1518,1519,1520,1521,1522,1523,1524,1525,1526,1527,1528,1529,1531,1532,1533,1534,1535,1536,1537,1538,1539,1540,1541,1542,1543,1544,1545,1546,1547,1548,1549,1550,1551,1552,1554,1556,1557,1558,1559,1560,1561,1562,1563,1564,1565,1566,1569,1570,1571,1575,1576,1580,1581,1584,1591,1592,1593,1594,1595,1596,1597,1598,1601,1602,1603,1605,1606,1607,1608,1609,1610,1611,1612,1615,1616,1617,1618,1620,1621,1622,1623,1625,1626,1627,1629,1630,1633,1634,1635,1636,1637,1639,1640,1641,1642,1643,1644,1645,1646,1647,1649,1650,1653,1654,1656,1658,1660,1663,1668,1669,1678,1679,1681,1682,1683,1685,1686,1688,1689,1690,1692,1693,1694,1697,1698,1701,1705,1707,1708,1711,1712,1713,1714,1715,1716,1718,1721,1723,1727,1729,1730,1732,1735,1736,1738,1741,1742,1744,1747,1748,1750,1751,1753,1754,1756,1757,1763,1764,1766,1767,1772,1774,1784,1786,1787,1788,1789,1790,1792,1793,1794,1795,1797,1798,1800,1801,1803,1804,1805,1807,1808,1810,1811,1812,1813,1820,1822,1824,1826,1827,1828,1829,1830,1832,1833,1834,1835,1837,1840,1841,1842,1843,1844,1845,1846,1849,1850,1851,1852,1853,1859,1861,1866,1867,1870,1873,1874,1875,1876,1878,1880,1881,1884,1885,1886,1887,1888,1891,1892,1894,1895,1898,1900,1901,1902,1999,2015,2018,2019,2020,2021,3155,3180,3181,3186,3188,3189,3328,3329,3550,3605,4009,4016,4070,4071,4500,4501,4502,4503,4504,4505,4506,4507,4509,4511,4512,4515,4516,5894,5895,5896,5897,5913],"Fixed":[25,26,27,28,29,68,150,151,156,157,171,174,203,205,208,226,227,230,231,235,247,280,285,287,308,388,389,395,396,402,407,408,425,536,537,538,539,540,688,702,772,910,930,994,1008,1113,1181,1189,1190,1191,1218,1219,1226,1332,1342,1345,1354,1356,1385,1386,1400,1401,1402,1403,1405,1406,1413,1445,1466,1567,1578,1579,1583,1585,1586,1587,1588,1589,1590,1614,1655,1657,1659,1665,1673,1675,1676,1684,1687,1691,1696,1699,1700,1703,1704,1706,1709,1854,1855,1856,1857,1858,1862,1864,1865,1871,1872,1877,1897,1899,2000,2001,2002,2003,2004,2005,2006,2007,2008,2009,2010,2011,2012,2013,2014,2017,2025,2026,2028,2029,2030,2031,2037,2038,2039,2040,2041,2042,2043,2044,2045,2046,2047,2048,2049,2050,2051,2052,2053,2054,2058,2059,2060,2061,2062,2063,2068,2102,2103,2104,2105,2106,2107,3000,3001,3002,3004,3005,3006,3007,3008,3009,3010,3011,3012,3013,3014,3015,3016,3017,3018,3019,3020,3021,3022,3023,3024,3025,3026,3027,3028,3029,3030,3031,3032,3033,3034,3035,3036,3037,3038,3039,3040,3041,3042,3043,3044,3045,3046,3047,3048,3049,3050,3051,3052,3053,3054,3055,3056,3057,3058,3059,3060,3061,3062,3063,3064,3065,3066,3067,3068,3069,3070,3071,3072,3073,3075,3076,3077,3078,3079,3080,3081,3082,3083,3084,3085,3086,3087,3088,3089,3090,3091,3092,3093,3094,3095,3096,3097,3098,3099,3100,3101,3102,3103,3104,3105,3106,3107,3108,3109,3110,3111,3112,3113,3114,3115,3116,3117,3118,3119,3120,3121,3122,3123,3124,3125,3126,3127,3128,3129,3130,3131,3132,3133,3134,3135,3136,3137,3138,3139,3140,3141,3142,3143,3144,3146,3147,3148,3149,3156,3157,3158,3159,3160,3161,3162,3163,3164,3165,3166,3167,3168,3169,3170,3171,3172,3173,3174,3175,3176,3177,3178,3179,3183,3185,3193,3194,3195,3196,3197,3198,3199,3200,3201,3202,3203,3204,3205,3206,3207,3208,3209,3210,3211,3212,3213,3214,3215,3326,3327,3331,3336,3337,3338,3339,3340,3341,3342,3343,3344,3345,3346,3347,3348,3349,3350,3351,3352,3353,3354,3355,3357,3400,3401,3402,3403,3407,3408,3409,3410,3411,3412,3413,3414,3415,3416,3417,3418,3419,3420,3421,3422,3423,3424,3425,3426,3427,3428,3429,3430,3433,3434,3435,3436,3437,3438,3439,3440,3441,3442,3443,3444,3445,3446,3450,3451,3452,3453,3454,3455,3456,3457,3458,3459,3460,3461,3462,3463,3464,3480,3481,3482,3483,3484,3485,3486,3487,3488,3489,3490,3491,3492,3493,3494,3495,3496,3497,3498,3500,3501,3502,3503,3504,3505,3506,3507,3508,3509,3510,3511,3512,3513,3514,3515,3516,3517,3518,3519,3520,3521,3522,3523,3524,3527,3530,3531,3532,3533,3534,3535,3536,3537,3538,3539,3540,3541,3542,3543,3544,3554,3700,3701,3702,3703,3704,3705,3706,3707,3708,3709,3710,3711,3712,3713,3950,3951,3952,3953,3954,3955,3956,3957,3958,3959,3960,3961,3962,3963,3964,3965,3966,3967,3968,3969,3970,3971,3972,3973,3974,3975,3976,3977,3978,3981,4000,4007,4008,4012,4013,4014,4015,4017,4018,4056,4059,4060,4061,4086,4140,4225,4300,4301,4302,4303,4304,4305,4306,4307,4312,4313,4314,4315,4316,4317,4318,4319,4320,4330,4331,4332,4333,4334,4335,4336,4337,4338,4339,4340,4341,4342,4343,4344,4345,4346,4347,4348,4349,4350,4351,6500,6501,6502,6503,6504,6505,6506,6507,6508,6509,6510,6511,6512,6513,6514,6515,6516,6517,6518,6519,6520,6521,6522,6523,6524,6525,6526,6527,6528,6529,6530,6531,6532,6533,6534,6535,6536,6537,6538,6539,6540,6541,6542,6543,6544,6545,6546,6547],"Lever":[92,195,423,1002,1111,1234,1254,1300,1375,1376,1722],"Ladder":[35,37,200,201,295,332,600,630,769,770,778,803,881,979,991,1004,1102,1177,1178,1553,1731,3525,4514],"Transparent":[84,88,97,99,112,114,140,158,159,161,176,177,178,179,183,184,198,199,266,270,272,310,318,388,411,420,468,519,531,541,608,695,700,757,759,784,787,788,789,794,811,829,848,879,891,926,927,982,995,1006,1007,1009,1016,1017,1026,1131,1202,1419,1452,1613,1619,3180,3600,3602]},"Animated":[374,375,376,377,378,379,380,381,806,807,808,809,818,819,820,821],"Defaults":{"Transparent":794,"Lever":1002,"Ladder":600},"ItemFlags":[{"HOUSE.TR2":{"Door1":17,"Door2":33,"Door3":33,"Door4":33,"Door5":33,"Door6":33,"Door7":33,"Door8":129,"PushButtonSwitch":32768,"PushableBlock1":1024}},{"JUNGLE.TR2":{"Door1":17,"Door8":132,"PushableBlock1":1024,"Trapdoor1":8,"WallSwitch":4096},"TEMPLE.TR2":{"Door1":16777345,"Door2":17,"Door3":16777346,"PushableBlock1":1024,"Trapdoor1":8,"UnderwaterSwitch":8192,"WallSwitch":4096},"QUADCHAS.TR2":{"Door1":16777345,"Door2":17,"Door3":16777346,"PushButtonSwitch":32768,"Trapdoor1":8},"TONYBOSS.TR2":{"PushableBlock1":1024}},{"SHORE.TR2":{"Door1":129,"Door4":17,"PushButtonSwitch":32768,"Trapdoor1":8},"CRASH.TR2":{"Door1":65,"Door2":17,"Trapdoor1":8,"Trapdoor2":8,"UnderwaterSwitch":8192,"WallSwitch":4096},"RAPIDS.TR2":{"Door1":129,"PushButtonSwitch":32768,"Trapdoor1":8},"TRIBOSS.TR2":{"Door1":17,"Door2":16777250,"Door3":16777249,"Door4":17,"PushButtonSwitch":32768,"PushableBlock1":1024,"WallSwitch":4096}},{"ROOFS.TR2":{"Door1":17,"PushButtonSwitch":32768,"PushableBlock1":1024,"PushableBlock2":1024,"Trapdoor1":8,"Trapdoor2":8,"UnderwaterSwitch":8192},"SEWER.TR2":{"Door2":17,"Door3":17,"Door4":17,"Door5":17,"Door6":17,"Door7":17,"FallingBlock":512,"PushButtonSwitch":32768,"PushableBlock1":1024,"Trapdoor1":8,"Trapdoor2":8},"TOWER.TR2":{"Door2":17,"Door3":17,"Door4":17,"Door7":16777250,"Door8":16777249,"FallingBlock":512,"PushButtonSwitch":32768,"PushableBlock1":1024,"PushableBlock2":1024,"Trapdoor1":8,"UnderwaterSwitch":8192},"OFFICE.TR2":{"Door1":17,"PushButtonSwitch":32768,"Trapdoor1":8},"STPAUL.TR2":{"Door1":17,"Door5":17,"FallingBlock":512,"PushButtonSwitch":32768,"Trapdoor1":8,"WallSwitch":4096}},{"NEVADA.TR2":{"Door1":17,"Door2":16777345,"Door3":16777346,"Door4":17,"PushableBlock1":1024,"Trapdoor1":8,"UnderwaterSwitch":8192},"COMPOUND.TR2":{"Door1":17,"Door2":16777250,"Door3":16777249,"Door4":17,"Door5":20,"Door6":20,"PushButtonSwitch":32768,"PushableBlock1":1024,"Trapdoor1":8,"UnderwaterSwitch":8192},"AREA51.TR2":{"Door1":17,"Door4":16777250,"Door5":16777249,"Door6":17,"PushButtonSwitch":32768,"Trapdoor1":8,"Trapdoor2":8}},{"ANTARC.TR2":{"Door3":20,"Door4":65,"PushButtonSwitch":32768,"Trapdoor1":8,"WallSwitch":4096},"MINES.TR2":{"Door1":17,"Door3":20,"PushButtonSwitch":32768},"CITY.TR2":{"Door1":16777345,"Door2":16777346,"Door3":20,"Door4":132,"Door6":17,"Door7":20,"PushButtonSwitch":32768,"PushableBlock1":1024,"Trapdoor1":8,"UnderwaterSwitch":8192,"WallSwitch":4096},"CHAMBER.TR2":{"Door1":17,"Door2":65,"PushButtonSwitch":32768}}]} \ No newline at end of file diff --git a/TRRandomizerCore/TRRandomizerController.cs b/TRRandomizerCore/TRRandomizerController.cs index 6d753f6a5..989d23e8f 100644 --- a/TRRandomizerCore/TRRandomizerController.cs +++ b/TRRandomizerCore/TRRandomizerController.cs @@ -924,6 +924,24 @@ public int TextureSeed set => LevelRandomizer.TextureSeed = value; } + public TextureMode TextureMode + { + get => LevelRandomizer.TextureMode; + set => LevelRandomizer.TextureMode = value; + } + + public bool MatchTextureTypes + { + get => LevelRandomizer.MatchTextureTypes; + set => LevelRandomizer.MatchTextureTypes = value; + } + + public bool MatchTextureItems + { + get => LevelRandomizer.MatchTextureItems; + set => LevelRandomizer.MatchTextureItems = value; + } + public bool PersistTextures { get => LevelRandomizer.PersistTextureVariants; diff --git a/TRRandomizerCore/TRRandomizerType.cs b/TRRandomizerCore/TRRandomizerType.cs index 59fbe600a..ed114f921 100644 --- a/TRRandomizerCore/TRRandomizerType.cs +++ b/TRRandomizerCore/TRRandomizerType.cs @@ -66,4 +66,6 @@ public enum TRRandomizerType GameMode, ItemDrops, LevelCount, + TextureSwap, + Wireframe, } diff --git a/TRRandomizerCore/TRVersionSupport.cs b/TRRandomizerCore/TRVersionSupport.cs index 08f37d8e9..92df3f6c8 100644 --- a/TRRandomizerCore/TRVersionSupport.cs +++ b/TRRandomizerCore/TRVersionSupport.cs @@ -55,6 +55,7 @@ internal class TRVersionSupport TRRandomizerType.Traps, TRRandomizerType.Unarmed, TRRandomizerType.WaterColour, + TRRandomizerType.Wireframe, }; private static readonly List _tr1RTypes = new() @@ -73,6 +74,8 @@ internal class TRVersionSupport TRRandomizerType.SFX, TRRandomizerType.StartPosition, TRRandomizerType.Text, + TRRandomizerType.Texture, + TRRandomizerType.TextureSwap, }; private static readonly List _tr2Types = new() @@ -116,7 +119,8 @@ internal class TRVersionSupport TRRandomizerType.Text, TRRandomizerType.Texture, TRRandomizerType.Unarmed, - TRRandomizerType.ItemSprite + TRRandomizerType.ItemSprite, + TRRandomizerType.Wireframe, }; private static readonly List _tr2RTypes = new() @@ -134,6 +138,8 @@ internal class TRVersionSupport TRRandomizerType.SFX, TRRandomizerType.StartPosition, TRRandomizerType.Text, + TRRandomizerType.Texture, + TRRandomizerType.TextureSwap, }; private static readonly List _tr3Types = new() @@ -169,7 +175,8 @@ internal class TRVersionSupport TRRandomizerType.Text, TRRandomizerType.Texture, TRRandomizerType.Unarmed, - TRRandomizerType.VFX + TRRandomizerType.VFX, + TRRandomizerType.Wireframe, }; private static readonly List _tr3MainTypes = new() @@ -192,6 +199,8 @@ internal class TRVersionSupport TRRandomizerType.SFX, TRRandomizerType.StartPosition, TRRandomizerType.Text, + TRRandomizerType.Texture, + TRRandomizerType.TextureSwap, }; private static readonly Dictionary _supportedTypes = new() diff --git a/TRRandomizerCore/Textures/TRTexInfo.cs b/TRRandomizerCore/Textures/TRTexInfo.cs new file mode 100644 index 000000000..12ded35d9 --- /dev/null +++ b/TRRandomizerCore/Textures/TRTexInfo.cs @@ -0,0 +1,50 @@ +namespace TRRandomizerCore.Textures; + +public class TRTexInfo + where T : Enum +{ + public Dictionary> Categories { get; set; } + public SortedSet Animated { get; set; } + public Dictionary Defaults { get; set; } + public List>> ItemFlags { get; set; } +} + +public enum TRTexCategory +{ + Opaque, + Fixed, + Lever, + Ladder, + Transparent, +} + +[Flags] +public enum TRItemFlags +{ + LeftDoor = 1 << 0, + RightDoor = 1 << 1, + LiftingDoor = 1 << 2, + Trapdoor = 1 << 3, + FourClick = 1 << 4, + FiveClick = 1 << 5, + SixClick = 1 << 6, + EightClick = 1 << 7, + Drawbridge = 1 << 8, + + FallingBlock = 1 << 9, + PushBlock = 1 << 10, + SlammingDoor = 1 << 11, + WallSwitch = 1 << 12, + UnderwaterSwitch = 1 << 13, + BreakableWindow = 1 << 14, + PushButton = 1 << 15, + Springboard = 1 << 16, + Boulder = 1 << 17, + Darts = 1 << 18, + Spikes = 1 << 19, + Spindle = 1 << 20, + + PairA = 1 << 24, + PairB = 1 << 25, + PairC = 1 << 26, +} diff --git a/TRRandomizerView/Controls/EditorControl.xaml b/TRRandomizerView/Controls/EditorControl.xaml index 4d084db34..3e2bbcd81 100644 --- a/TRRandomizerView/Controls/EditorControl.xaml +++ b/TRRandomizerView/Controls/EditorControl.xaml @@ -308,7 +308,8 @@ MainDescription="Customize the texture randomization." BoolItemsSource="{Binding Data.TextureBoolItemControls, Source={StaticResource proxy}}" HasBoolItems="True" - HasTextureOptions="True" + HasWireframeOptions="{Binding Data.IsWireframeTypeSupported, Source={StaticResource proxy}}" + HasTextureSwapOptions="{Binding Data.IsTextureSwapTypeSupported, Source={StaticResource proxy}}" ControllerProxy="{Binding Data, Source={StaticResource proxy}}"> diff --git a/TRRandomizerView/Model/ControllerOptions.cs b/TRRandomizerView/Model/ControllerOptions.cs index 8feb6a90c..22b28a14d 100644 --- a/TRRandomizerView/Model/ControllerOptions.cs +++ b/TRRandomizerView/Model/ControllerOptions.cs @@ -120,6 +120,10 @@ public class ControllerOptions : INotifyPropertyChanged private WeaponDifficulty _weaponDifficulty; private WeaponDifficulty[] _weaponDifficulties; + private BoolItemControlClass _matchTextureTypes, _matchTextureItems; + private TextureMode[] _textureModes; + private TextureMode _textureMode; + #region TR1X Sepcifics private bool _enableGameModes; @@ -1951,6 +1955,49 @@ public int TextureSeed } } + public BoolItemControlClass MatchTextureTypes + { + get => _matchTextureTypes; + set + { + _matchTextureTypes = value; + FirePropertyChanged(); + } + } + + public BoolItemControlClass MatchTextureItems + { + get => _matchTextureItems; + set + { + _matchTextureItems = value; + FirePropertyChanged(); + } + } + + public TextureMode[] TextureModes + { + get => _textureModes; + private set + { + _textureModes = value; + FirePropertyChanged(); + } + } + + public TextureMode TextureMode + { + get => _textureMode; + set + { + _textureMode = value; + FirePropertyChanged(); + + MatchTextureItems.IsActive = value == TextureMode.Game; + FirePropertyChanged(nameof(MatchTextureItems)); + } + } + public BoolItemControlClass PersistTextures { get => _persistTextures; @@ -3144,6 +3191,18 @@ public ControllerOptions() // Textures Binding randomizeTexturesBinding = new(nameof(RandomizeTextures)) { Source = this }; + MatchTextureTypes = new() + { + Title = "Match texture types", + Description = "Textures for such things as levers, windows and ladders will be matched where possible with those from the import set." + }; + BindingOperations.SetBinding(MatchTextureTypes, BoolItemControlClass.IsActiveProperty, randomizeTexturesBinding); + MatchTextureItems = new() + { + Title = "Match item types", + Description = "Movable items such as doors, levers and traps will be matched where possible with those from the import set." + }; + BindingOperations.SetBinding(MatchTextureItems, BoolItemControlClass.IsActiveProperty, randomizeTexturesBinding); PersistTextures = new BoolItemControlClass() { Title = "Use persistent textures", @@ -3395,7 +3454,7 @@ public ControllerOptions() }; TextureBoolItemControls = new() { - _persistTextures, _randomizeWaterColour, _retainLevelTextures, _retainLaraTextures, _retainEnemyTextures, _retainKeySpriteTextures, _retainSecretSpriteTextures + _matchTextureTypes, _matchTextureItems, _persistTextures, _randomizeWaterColour, _retainLevelTextures, _retainLaraTextures, _retainEnemyTextures, _retainKeySpriteTextures, _retainSecretSpriteTextures }; AudioBoolItemControls = new() { @@ -3512,6 +3571,11 @@ private void AdjustAvailableOptions() _randomizeTraps.IsAvailable = IsTrapsTypeSupported; _randomizeChallengeRooms.IsAvailable = IsChallengeRoomsTypeSupported; _hardEnvironmentMode.IsAvailable = IsHardEnvironmentTypeSupported; + + _matchTextureTypes.IsAvailable = IsTextureSwapTypeSupported; + _matchTextureItems.IsAvailable = IsTextureSwapTypeSupported; + _persistTextures.IsAvailable = !IsTextureSwapTypeSupported; + _retainLaraTextures.IsAvailable = !IsTextureSwapTypeSupported; } public void Load(TRRandomizerController controller) @@ -3655,6 +3719,10 @@ public void Load(TRRandomizerController controller) RandomizeTextures = _controller.RandomizeTextures; TextureSeed = _controller.TextureSeed; + TextureModes = Enum.GetValues(); + TextureMode = _controller.TextureMode; + MatchTextureTypes.Value = _controller.MatchTextureTypes; + MatchTextureItems.Value = _controller.MatchTextureItems; PersistTextures.Value = _controller.PersistTextures; RandomizeWaterColour.Value = _controller.RandomizeWaterColour; RetainMainLevelTextures.Value = _controller.RetainMainLevelTextures; @@ -3959,6 +4027,9 @@ public void Save() _controller.RandomizeTextures = RandomizeTextures; _controller.TextureSeed = TextureSeed; + _controller.TextureMode = TextureMode; + _controller.MatchTextureTypes = MatchTextureTypes.Value; + _controller.MatchTextureItems = MatchTextureItems.Value; _controller.PersistTextures = PersistTextures.Value; _controller.RandomizeWaterColour = RandomizeWaterColour.Value; _controller.RetainMainLevelTextures = RetainMainLevelTextures.Value; @@ -4137,6 +4208,8 @@ public void Unload() public bool IsExtraPickupsTypeSupported => IsRandomizationSupported(TRRandomizerType.ExtraPickups); public bool IsEnemyTypeSupported => IsRandomizationSupported(TRRandomizerType.Enemy); public bool IsTextureTypeSupported => IsRandomizationSupported(TRRandomizerType.Texture); + public bool IsWireframeTypeSupported => IsRandomizationSupported(TRRandomizerType.Wireframe); + public bool IsTextureSwapTypeSupported => IsRandomizationSupported(TRRandomizerType.TextureSwap); public bool IsStartPositionTypeSupported => IsRandomizationSupported(TRRandomizerType.StartPosition); public bool IsAudioTypeSupported => IsRandomizationSupported(TRRandomizerType.Audio); public bool IsAmbientTracksTypeSupported => IsRandomizationSupported(TRRandomizerType.AmbientTracks); diff --git a/TRRandomizerView/Windows/AdvancedWindow.xaml b/TRRandomizerView/Windows/AdvancedWindow.xaml index 213ff6d51..ec6834835 100644 --- a/TRRandomizerView/Windows/AdvancedWindow.xaml +++ b/TRRandomizerView/Windows/AdvancedWindow.xaml @@ -94,6 +94,7 @@ + @@ -148,8 +149,51 @@ + + + + + + + + + + + + + + + + + + + + + + + - @@ -207,7 +251,7 @@ - @@ -375,7 +419,7 @@ - @@ -422,7 +466,7 @@ - @@ -463,7 +507,7 @@ - @@ -502,7 +546,7 @@ - - - - - - - @@ -1000,7 +1044,7 @@ - - + @@ -1251,7 +1295,7 @@ - - @@ -1352,7 +1396,7 @@ - - - @@ -1615,7 +1659,7 @@ - SetValue(HasGlobeOptionsProperty, value); } - public bool HasTextureOptions + public bool HasWireframeOptions + { + get => (bool)GetValue(HasWireframeOptionsProperty); + set => SetValue(HasWireframeOptionsProperty, value); + } + + public bool HasTextureSwapOptions { - get => (bool)GetValue(HasTextureOptionsProperty); - set => SetValue(HasTextureOptionsProperty, value); + get => (bool)GetValue(HasTextureSwapOptionsProperty); + set => SetValue(HasTextureSwapOptionsProperty, value); } public bool HasAudioOptions diff --git a/TextureExport/Program.cs b/TextureExport/Program.cs index 6de626502..53c526bb4 100644 --- a/TextureExport/Program.cs +++ b/TextureExport/Program.cs @@ -9,7 +9,7 @@ class Program { enum Mode { - Png, Html, Segments, Faces, Boxes, Dependencies + Png, Html, Segments, Faces, Boxes, Dependencies, Dds, TexInfo, } static readonly TR1LevelControl _reader1 = new(); @@ -56,11 +56,37 @@ static void Main(string[] args) { mode = Mode.Dependencies; } + else if (arg == "dds") + { + if (args.Length < 3) + { + return; + } + mode = Mode.Dds; + } + else if (arg == "texinfo") + { + mode = Mode.TexInfo; + } } string levelType = args[0].ToLower(); - if (levelType.EndsWith(".phd")) + if (mode == Mode.Dds) + { + if (Enum.TryParse(levelType.ToUpper(), out TRGameVersion version)) + { + TRRExporter.Export(args[2], version); + } + } + else if (mode == Mode.TexInfo) + { + if (Enum.TryParse(levelType.ToUpper(), out TRGameVersion version)) + { + TRRExporter.GenerateCategories(version); + } + } + else if (levelType.EndsWith(".phd")) { ExportAllTextures(args[0], _reader1.Read(args[0]), mode); } @@ -236,7 +262,7 @@ static int[] GetRoomArgs() static void Usage() { Console.WriteLine(); - Console.WriteLine("Usage: TextureExport [tr1 | tr1g | tr2 | tr2g | tr3 | tr3g | *.phd | *.tr2] [png | html | segments | faces | boxes | depend]"); + Console.WriteLine("Usage: TextureExport [tr1 | tr1g | tr2 | tr2g | tr3 | tr3g | *.phd | *.tr2] [png | html | segments | faces | boxes | depend | dds | texinfo]"); Console.WriteLine(); Console.WriteLine("Target Levels"); @@ -257,6 +283,8 @@ static void Usage() Console.WriteLine("\tfaces - Create a new texture for every face in a room and mark its index."); Console.WriteLine("\tboxes - Similar to faces, but mark box extents for a list of rooms."); Console.WriteLine("\tdepend - Calculate which textures are shared between models and generate the JSON used in the main randomizer."); + Console.WriteLine("\tdds - Convert DDS files to PNG."); + Console.WriteLine("\ttexinfo - Generate TexInfo JSON files for texture randomization."); Console.WriteLine(); Console.WriteLine("Examples"); diff --git a/TextureExport/TextureExport.csproj b/TextureExport/TextureExport.csproj index e250d1813..9acc5591e 100644 --- a/TextureExport/TextureExport.csproj +++ b/TextureExport/TextureExport.csproj @@ -17,10 +17,14 @@ + PreserveNewest + + + \ No newline at end of file diff --git a/TextureExport/Types/TRRExporter.cs b/TextureExport/Types/TRRExporter.cs new file mode 100644 index 000000000..2b6077bf7 --- /dev/null +++ b/TextureExport/Types/TRRExporter.cs @@ -0,0 +1,814 @@ +using Newtonsoft.Json; +using System.Drawing; +using System.Drawing.Imaging; +using System.Runtime.InteropServices; +using TRLevelControl.Helpers; +using TRLevelControl.Model; +using TRRandomizerCore.Textures; + +namespace TextureExport.Types; + +public static class TRRExporter +{ + public static void Export(string ddsFolder, TRGameVersion version) + { + string dir = $@"{version}\DDS\PNG"; + Directory.CreateDirectory(dir); + + foreach (string ddsFile in Directory.GetFiles(ddsFolder)) + { + using Pfim.IImage image = Pfim.Pfimage.FromFile(ddsFile); + GCHandle handle = GCHandle.Alloc(image.Data, GCHandleType.Pinned); + try + { + IntPtr data = Marshal.UnsafeAddrOfPinnedArrayElement(image.Data, 0); + Bitmap bitmap = new(image.Width, image.Height, image.Stride, PixelFormat.Format32bppArgb, data); + bitmap.Save(Path.Combine(dir, Path.ChangeExtension(Path.GetFileName(ddsFile), ".png")), ImageFormat.Png); + } + finally + { + handle.Free(); + } + } + } + + public static void GenerateCategories(TRGameVersion version) + { + Dictionary> categories = new(); + Dictionary defaults = new(); + SortedSet animated; + + foreach (TRTexCategory category in Enum.GetValues()) + { + string dir = $@"{version}\DDS\Categories\{category}"; + if (!Directory.Exists(dir)) + { + continue; + } + + categories[category] = new(); + foreach (string img in Directory.GetFiles(dir)) + { + if (ushort.TryParse(Path.GetFileNameWithoutExtension(img), out ushort id)) + { + categories[category].Add(id); + } + } + } + + void WriteInfo(List>> itemFlags) + where T : Enum + { + TRTexInfo info = new() + { + Categories = categories, + Animated = animated, + Defaults = defaults, + ItemFlags = itemFlags + }; + + File.WriteAllText(Path.GetFullPath($@"..\..\..\TRRandomizerCore\Resources\{version}\Textures\texinfo.json"), JsonConvert.SerializeObject(info)); + } + + switch (version) + { + case TRGameVersion.TR1: + defaults[TRTexCategory.Transparent] = 477; + defaults[TRTexCategory.Lever] = 34; + animated = new() { 554, 558, 571, 590, 651, 652, 654, 655 }; + WriteInfo(_tr1ItemFlags); + break; + + case TRGameVersion.TR2: + defaults[TRTexCategory.Transparent] = 159; + defaults[TRTexCategory.Lever] = 1202; + defaults[TRTexCategory.Ladder] = 1172; + animated = new() { 375, 401, 402, 403, 413, 414, 415, 416, 654, 721, 722 }; + WriteInfo(_tr2ItemFlags); + break; + + case TRGameVersion.TR3: + defaults[TRTexCategory.Transparent] = 794; + defaults[TRTexCategory.Lever] = 1002; + defaults[TRTexCategory.Ladder] = 600; + animated = new() { 374, 375, 376, 377, 378, 379, 380, 381, 806, 807, 808, 809, 818, 819, 820, 821 }; + WriteInfo(_tr3ItemFlags); + break; + } + } + + private static readonly List>> _tr1ItemFlags = new() + { + new() + { + [TR1LevelNames.ASSAULT] = new(), + }, + new() + { + [TR1LevelNames.CAVES] = new() + { + [TR1Type.Door1] = TRItemFlags.LeftDoor | TRItemFlags.EightClick | TRItemFlags.PairA, + [TR1Type.Door2] = TRItemFlags.RightDoor | TRItemFlags.EightClick | TRItemFlags.PairA, + [TR1Type.Door3] = TRItemFlags.RightDoor | TRItemFlags.EightClick, + [TR1Type.Door4] = TRItemFlags.LeftDoor | TRItemFlags.FourClick, + [TR1Type.FallingBlock] = TRItemFlags.FallingBlock, + [TR1Type.WallSwitch] = TRItemFlags.WallSwitch, + [TR1Type.DartEmitter] = TRItemFlags.Darts | TRItemFlags.PairA, + [TR1Type.Dart_H] = TRItemFlags.Darts | TRItemFlags.PairB, + }, + [TR1LevelNames.VILCABAMBA] = new() + { + // Door3 omitted as its mesh vertices aren't regular + [TR1Type.Door1] = TRItemFlags.LeftDoor | TRItemFlags.FourClick, + [TR1Type.Door2] = TRItemFlags.LeftDoor | TRItemFlags.FourClick, + [TR1Type.Door4] = TRItemFlags.LeftDoor | TRItemFlags.FourClick, + [TR1Type.Door5] = TRItemFlags.LeftDoor | TRItemFlags.EightClick | TRItemFlags.PairA, + [TR1Type.Door6] = TRItemFlags.RightDoor | TRItemFlags.EightClick | TRItemFlags.PairA, + [TR1Type.Trapdoor1] = TRItemFlags.Trapdoor, + [TR1Type.FallingBlock] = TRItemFlags.FallingBlock, + [TR1Type.PushBlock1] = TRItemFlags.PushBlock, + [TR1Type.WallSwitch] = TRItemFlags.WallSwitch, + [TR1Type.UnderwaterSwitch] = TRItemFlags.UnderwaterSwitch, + }, + [TR1LevelNames.VALLEY] = new() + { + // Door6 omitted as it appears to be a dummy with a static mesh in its place + [TR1Type.Door1] = TRItemFlags.LeftDoor | TRItemFlags.FourClick, + [TR1Type.WallSwitch] = TRItemFlags.WallSwitch, + }, + [TR1LevelNames.QUALOPEC] = new() + { + [TR1Type.Door1] = TRItemFlags.LiftingDoor | TRItemFlags.FourClick, + [TR1Type.Door2] = TRItemFlags.LiftingDoor | TRItemFlags.FourClick, + [TR1Type.Door3] = TRItemFlags.LiftingDoor | TRItemFlags.FourClick, + [TR1Type.Door4] = TRItemFlags.Drawbridge | TRItemFlags.SixClick, + [TR1Type.Door5] = TRItemFlags.LeftDoor | TRItemFlags.FourClick, + [TR1Type.Door6] = TRItemFlags.LeftDoor | TRItemFlags.FourClick, + [TR1Type.Door8] = TRItemFlags.LeftDoor | TRItemFlags.FourClick, + [TR1Type.FallingBlock] = TRItemFlags.FallingBlock, + [TR1Type.PushBlock1] = TRItemFlags.PushBlock, + [TR1Type.WallSwitch] = TRItemFlags.WallSwitch, + [TR1Type.RollingBall] = TRItemFlags.Boulder, + }, + }, + new() + { + [TR1LevelNames.FOLLY] = new() + { + [TR1Type.Door1] = TRItemFlags.LeftDoor | TRItemFlags.FourClick, + [TR1Type.Door2] = TRItemFlags.LiftingDoor | TRItemFlags.EightClick, + [TR1Type.Door3] = TRItemFlags.LeftDoor | TRItemFlags.EightClick, + [TR1Type.Door4] = TRItemFlags.LeftDoor | TRItemFlags.FourClick, + [TR1Type.Door5] = TRItemFlags.LeftDoor | TRItemFlags.EightClick | TRItemFlags.PairA, + [TR1Type.Door6] = TRItemFlags.RightDoor | TRItemFlags.EightClick | TRItemFlags.PairA, + [TR1Type.Door7] = TRItemFlags.LeftDoor | TRItemFlags.FourClick, + [TR1Type.FallingBlock] = TRItemFlags.FallingBlock, + [TR1Type.PushBlock1] = TRItemFlags.PushBlock, + [TR1Type.WallSwitch] = TRItemFlags.WallSwitch, + [TR1Type.UnderwaterSwitch] = TRItemFlags.UnderwaterSwitch, + [TR1Type.RollingBall] = TRItemFlags.Boulder, + }, + [TR1LevelNames.COLOSSEUM] = new() + { + [TR1Type.Door1] = TRItemFlags.LeftDoor | TRItemFlags.FiveClick, + [TR1Type.Door2] = TRItemFlags.LeftDoor | TRItemFlags.EightClick, + [TR1Type.Door5] = TRItemFlags.LeftDoor | TRItemFlags.FourClick, + [TR1Type.Door6] = TRItemFlags.RightDoor | TRItemFlags.EightClick, + [TR1Type.Door7] = TRItemFlags.LiftingDoor | TRItemFlags.EightClick, + [TR1Type.Door8] = TRItemFlags.LeftDoor | TRItemFlags.FourClick, + [TR1Type.PushBlock1] = TRItemFlags.PushBlock, + [TR1Type.WallSwitch] = TRItemFlags.WallSwitch, + [TR1Type.RollingBall] = TRItemFlags.Boulder, + }, + [TR1LevelNames.MIDAS] = new() + { + [TR1Type.Door1] = TRItemFlags.LeftDoor | TRItemFlags.FourClick, + [TR1Type.Door2] = TRItemFlags.LeftDoor | TRItemFlags.FourClick, + [TR1Type.Door3] = TRItemFlags.LeftDoor | TRItemFlags.FourClick, + [TR1Type.Door4] = TRItemFlags.LeftDoor | TRItemFlags.FourClick, + [TR1Type.Door5] = TRItemFlags.LeftDoor | TRItemFlags.FourClick, + [TR1Type.Door6] = TRItemFlags.RightDoor | TRItemFlags.EightClick, + [TR1Type.Door7] = TRItemFlags.LeftDoor | TRItemFlags.EightClick, + [TR1Type.FallingBlock] = TRItemFlags.FallingBlock, + [TR1Type.PushBlock1] = TRItemFlags.PushBlock, + [TR1Type.PushBlock2] = TRItemFlags.PushBlock, + [TR1Type.SlammingDoor] = TRItemFlags.SlammingDoor, + [TR1Type.WallSwitch] = TRItemFlags.WallSwitch, + }, + [TR1LevelNames.CISTERN] = new() + { + [TR1Type.Door1] = TRItemFlags.LeftDoor | TRItemFlags.FourClick, + [TR1Type.Door2] = TRItemFlags.LeftDoor | TRItemFlags.FourClick, + [TR1Type.Door3] = TRItemFlags.LeftDoor | TRItemFlags.FourClick, + [TR1Type.Trapdoor1] = TRItemFlags.Trapdoor, + [TR1Type.FallingBlock] = TRItemFlags.FallingBlock, + [TR1Type.PushBlock1] = TRItemFlags.PushBlock, + [TR1Type.PushBlock2] = TRItemFlags.PushBlock, + [TR1Type.WallSwitch] = TRItemFlags.WallSwitch, + [TR1Type.UnderwaterSwitch] = TRItemFlags.UnderwaterSwitch, + }, + [TR1LevelNames.TIHOCAN] = new() + { + [TR1Type.Door1] = TRItemFlags.LeftDoor | TRItemFlags.FourClick, + [TR1Type.Door2] = TRItemFlags.LeftDoor | TRItemFlags.FiveClick, + [TR1Type.Door3] = TRItemFlags.LeftDoor | TRItemFlags.FourClick, + [TR1Type.Door4] = TRItemFlags.LeftDoor | TRItemFlags.EightClick, + [TR1Type.Door5] = TRItemFlags.LeftDoor | TRItemFlags.FourClick, + [TR1Type.FallingBlock] = TRItemFlags.FallingBlock, + [TR1Type.PushBlock1] = TRItemFlags.PushBlock, + [TR1Type.SlammingDoor] = TRItemFlags.SlammingDoor, + [TR1Type.WallSwitch] = TRItemFlags.WallSwitch, + [TR1Type.UnderwaterSwitch] = TRItemFlags.UnderwaterSwitch, + [TR1Type.DartEmitter] = TRItemFlags.Darts | TRItemFlags.PairA, + [TR1Type.Dart_H] = TRItemFlags.Darts | TRItemFlags.PairB, + }, + }, + new() + { + [TR1LevelNames.KHAMOON] = new() + { + [TR1Type.Door1] = TRItemFlags.LeftDoor | TRItemFlags.FourClick | TRItemFlags.PairA, + [TR1Type.Door2] = TRItemFlags.LeftDoor | TRItemFlags.FourClick, + [TR1Type.Door3] = TRItemFlags.RightDoor | TRItemFlags.FourClick | TRItemFlags.PairA, + [TR1Type.Door4] = TRItemFlags.LeftDoor | TRItemFlags.EightClick | TRItemFlags.PairB, + [TR1Type.Door5] = TRItemFlags.RightDoor | TRItemFlags.EightClick | TRItemFlags.PairB, + [TR1Type.Trapdoor1] = TRItemFlags.Trapdoor, + [TR1Type.PushBlock1] = TRItemFlags.PushBlock, + [TR1Type.WallSwitch] = TRItemFlags.WallSwitch, + [TR1Type.UnderwaterSwitch] = TRItemFlags.UnderwaterSwitch, + [TR1Type.RollingBall] = TRItemFlags.Boulder, + }, + [TR1LevelNames.OBELISK] = new() + { + [TR1Type.Door1] = TRItemFlags.LeftDoor | TRItemFlags.FourClick, + [TR1Type.Door2] = TRItemFlags.LeftDoor | TRItemFlags.FourClick, + [TR1Type.Door5] = TRItemFlags.LeftDoor | TRItemFlags.EightClick | TRItemFlags.PairA, + [TR1Type.Door6] = TRItemFlags.RightDoor | TRItemFlags.EightClick | TRItemFlags.PairA, + [TR1Type.PushBlock1] = TRItemFlags.PushBlock, + [TR1Type.WallSwitch] = TRItemFlags.WallSwitch, + }, + [TR1LevelNames.SANCTUARY] = new() + { + [TR1Type.Door1] = TRItemFlags.LeftDoor | TRItemFlags.FourClick, + [TR1Type.Door3] = TRItemFlags.LeftDoor | TRItemFlags.EightClick, + [TR1Type.Door4] = TRItemFlags.LiftingDoor | TRItemFlags.EightClick, + [TR1Type.Door5] = TRItemFlags.LeftDoor | TRItemFlags.EightClick | TRItemFlags.PairA, + [TR1Type.Door6] = TRItemFlags.RightDoor | TRItemFlags.EightClick | TRItemFlags.PairA, + [TR1Type.PushBlock1] = TRItemFlags.PushBlock, + [TR1Type.SlammingDoor] = TRItemFlags.SlammingDoor, + [TR1Type.WallSwitch] = TRItemFlags.WallSwitch, + [TR1Type.UnderwaterSwitch] = TRItemFlags.UnderwaterSwitch, + } + }, + new() + { + [TR1LevelNames.MINES] = new() + { + [TR1Type.Door1] = TRItemFlags.LeftDoor | TRItemFlags.FourClick, + [TR1Type.Door2] = TRItemFlags.LeftDoor | TRItemFlags.FourClick, + [TR1Type.Door3] = TRItemFlags.LeftDoor | TRItemFlags.EightClick | TRItemFlags.PairA, + [TR1Type.Door4] = TRItemFlags.RightDoor | TRItemFlags.EightClick | TRItemFlags.PairA, + [TR1Type.Trapdoor2] = TRItemFlags.Trapdoor, + [TR1Type.FallingBlock] = TRItemFlags.FallingBlock, + [TR1Type.PushBlock1] = TRItemFlags.PushBlock, + [TR1Type.PushBlock2] = TRItemFlags.PushBlock, + [TR1Type.PushBlock3] = TRItemFlags.PushBlock, + [TR1Type.PushBlock4] = TRItemFlags.PushBlock, + [TR1Type.WallSwitch] = TRItemFlags.WallSwitch, + [TR1Type.RollingBall] = TRItemFlags.Boulder, + }, + [TR1LevelNames.ATLANTIS] = new() + { + [TR1Type.Door3] = TRItemFlags.RightDoor | TRItemFlags.EightClick, + [TR1Type.Door4] = TRItemFlags.LeftDoor | TRItemFlags.FourClick, + [TR1Type.Door5] = TRItemFlags.LeftDoor | TRItemFlags.FourClick, + [TR1Type.Door6] = TRItemFlags.LeftDoor | TRItemFlags.FourClick, + [TR1Type.Door7] = TRItemFlags.LeftDoor | TRItemFlags.FourClick, + [TR1Type.Door8] = TRItemFlags.LeftDoor | TRItemFlags.FourClick, + [TR1Type.Trapdoor2] = TRItemFlags.Trapdoor, + [TR1Type.PushBlock1] = TRItemFlags.PushBlock, + [TR1Type.SlammingDoor] = TRItemFlags.SlammingDoor, + [TR1Type.WallSwitch] = TRItemFlags.WallSwitch, + [TR1Type.UnderwaterSwitch] = TRItemFlags.UnderwaterSwitch, + [TR1Type.RollingBall] = TRItemFlags.Boulder, + [TR1Type.DartEmitter] = TRItemFlags.Darts | TRItemFlags.PairA, + [TR1Type.Dart_H] = TRItemFlags.Darts | TRItemFlags.PairB, + }, + [TR1LevelNames.PYRAMID] = new() + { + [TR1Type.Door1] = TRItemFlags.LeftDoor | TRItemFlags.FourClick, + [TR1Type.Door2] = TRItemFlags.LeftDoor | TRItemFlags.FourClick, + [TR1Type.FallingBlock] = TRItemFlags.FallingBlock, + [TR1Type.PushBlock1] = TRItemFlags.PushBlock, + [TR1Type.SlammingDoor] = TRItemFlags.SlammingDoor, + [TR1Type.WallSwitch] = TRItemFlags.WallSwitch, + [TR1Type.RollingBall] = TRItemFlags.Boulder, + [TR1Type.DartEmitter] = TRItemFlags.Darts | TRItemFlags.PairA, + [TR1Type.Dart_H] = TRItemFlags.Darts | TRItemFlags.PairB, + } + }, + }; + + private static readonly List>> _tr2ItemFlags = new() + { + new() + { + [TR2LevelNames.ASSAULT] = new() + { + [TR2Type.Door1] = TRItemFlags.LeftDoor | TRItemFlags.FourClick, + [TR2Type.Door2] = TRItemFlags.LeftDoor | TRItemFlags.FiveClick, + [TR2Type.Door3] = TRItemFlags.LeftDoor | TRItemFlags.FiveClick, + [TR2Type.Door4] = TRItemFlags.LeftDoor | TRItemFlags.FiveClick, + [TR2Type.BreakableWindow1] = TRItemFlags.BreakableWindow, + [TR2Type.PushButtonSwitch] = TRItemFlags.PushButton, + }, + [TR2LevelNames.HOME] = new() + { + [TR2Type.Door1] = TRItemFlags.LeftDoor | TRItemFlags.FourClick, + [TR2Type.Door2] = TRItemFlags.LeftDoor | TRItemFlags.FiveClick, + [TR2Type.Door3] = TRItemFlags.LeftDoor | TRItemFlags.FiveClick, + [TR2Type.Door4] = TRItemFlags.LeftDoor | TRItemFlags.FiveClick, + [TR2Type.BreakableWindow1] = TRItemFlags.BreakableWindow, + [TR2Type.PushButtonSwitch] = TRItemFlags.PushButton, + }, + }, + new() + { + [TR2LevelNames.GW] = new() + { + [TR2Type.Door1] = TRItemFlags.RightDoor | TRItemFlags.FiveClick, + [TR2Type.Door2] = TRItemFlags.LeftDoor | TRItemFlags.FourClick, + [TR2Type.Trapdoor1] = TRItemFlags.Trapdoor, + [TR2Type.FallingBlock] = TRItemFlags.FallingBlock, + [TR2Type.PushBlock1] = TRItemFlags.PushBlock, + [TR2Type.WallSwitch] = TRItemFlags.WallSwitch, + [TR2Type.RollingBall] = TRItemFlags.Boulder | TRItemFlags.PairA, + [TR2Type.TeethSpikesOrGlassShards] = TRItemFlags.Spikes, + [TR2Type.RollingSpindle] = TRItemFlags.Spindle, + }, + [TR2LevelNames.VENICE] = new() + { + [TR2Type.Door1] = TRItemFlags.LeftDoor | TRItemFlags.EightClick | TRItemFlags.PairA, + [TR2Type.Door2] = TRItemFlags.RightDoor | TRItemFlags.EightClick | TRItemFlags.PairA, + [TR2Type.Door3] = TRItemFlags.LeftDoor | TRItemFlags.EightClick | TRItemFlags.PairB, + [TR2Type.Door4] = TRItemFlags.LeftDoor | TRItemFlags.FourClick, + [TR2Type.LiftingDoor2] = TRItemFlags.RightDoor | TRItemFlags.EightClick | TRItemFlags.PairB, + [TR2Type.Door5] = TRItemFlags.LeftDoor | TRItemFlags.EightClick | TRItemFlags.PairC, + [TR2Type.LiftingDoor1] = TRItemFlags.RightDoor | TRItemFlags.EightClick | TRItemFlags.PairC, + [TR2Type.LiftingDoor3] = TRItemFlags.LiftingDoor | TRItemFlags.EightClick, + [TR2Type.BreakableWindow1] = TRItemFlags.BreakableWindow, + [TR2Type.PushButtonSwitch] = TRItemFlags.PushButton, + [TR2Type.Trapdoor1] = TRItemFlags.Trapdoor, + [TR2Type.WallSwitch] = TRItemFlags.WallSwitch, + [TR2Type.UnderwaterSwitch] = TRItemFlags.UnderwaterSwitch, + }, + [TR2LevelNames.BARTOLI] = new() + { + [TR2Type.Door1] = TRItemFlags.LeftDoor | TRItemFlags.EightClick | TRItemFlags.PairA, + [TR2Type.Door2] = TRItemFlags.RightDoor | TRItemFlags.EightClick | TRItemFlags.PairA, + [TR2Type.Door3] = TRItemFlags.LeftDoor | TRItemFlags.FourClick, + [TR2Type.Door4] = TRItemFlags.LeftDoor | TRItemFlags.FourClick, + [TR2Type.Door5] = TRItemFlags.LeftDoor | TRItemFlags.EightClick | TRItemFlags.PairB, + [TR2Type.LiftingDoor1] = TRItemFlags.RightDoor | TRItemFlags.EightClick | TRItemFlags.PairB, + [TR2Type.LiftingDoor3] = TRItemFlags.LiftingDoor | TRItemFlags.EightClick, + [TR2Type.BreakableWindow1] = TRItemFlags.BreakableWindow, + [TR2Type.PushBlock1] = TRItemFlags.PushBlock, + [TR2Type.PushBlock2] = TRItemFlags.PushBlock, + [TR2Type.Trapdoor1] = TRItemFlags.Trapdoor, + [TR2Type.Trapdoor2] = TRItemFlags.Trapdoor, + [TR2Type.WallSwitch] = TRItemFlags.WallSwitch, + [TR2Type.UnderwaterSwitch] = TRItemFlags.UnderwaterSwitch, + }, + [TR2LevelNames.OPERA] = new() + { + [TR2Type.Door1] = TRItemFlags.RightDoor | TRItemFlags.SixClick | TRItemFlags.PairA, + [TR2Type.Door2] = TRItemFlags.LeftDoor | TRItemFlags.SixClick | TRItemFlags.PairA, + [TR2Type.Door3] = TRItemFlags.LeftDoor | TRItemFlags.FourClick, + [TR2Type.Door4] = TRItemFlags.RightDoor | TRItemFlags.EightClick, + [TR2Type.Door5] = TRItemFlags.RightDoor | TRItemFlags.FiveClick, + [TR2Type.LiftingDoor1] = TRItemFlags.LeftDoor | TRItemFlags.FourClick, + [TR2Type.LiftingDoor2] = TRItemFlags.LiftingDoor | TRItemFlags.FourClick, + [TR2Type.LiftingDoor3] = TRItemFlags.LiftingDoor | TRItemFlags.EightClick, + [TR2Type.BreakableWindow1] = TRItemFlags.BreakableWindow, + [TR2Type.PushButtonSwitch] = TRItemFlags.PushButton, + [TR2Type.PushBlock1] = TRItemFlags.PushBlock, + [TR2Type.Trapdoor1] = TRItemFlags.Trapdoor, + [TR2Type.UnderwaterSwitch] = TRItemFlags.UnderwaterSwitch, + [TR2Type.BouldersOrSnowballs] = TRItemFlags.Boulder | TRItemFlags.PairB, + [TR2Type.TeethSpikesOrGlassShards] = TRItemFlags.Spikes, + }, + }, + new() + { + [TR2LevelNames.RIG] = new() + { + // Door5 omitted as split; Door6 omitted as wheel door + [TR2Type.Door1] = TRItemFlags.LeftDoor | TRItemFlags.FourClick, + [TR2Type.Door3] = TRItemFlags.LeftDoor | TRItemFlags.FourClick, + [TR2Type.BreakableWindow1] = TRItemFlags.BreakableWindow, + [TR2Type.PushButtonSwitch] = TRItemFlags.PushButton, + [TR2Type.PushBlock1] = TRItemFlags.PushBlock, + [TR2Type.Trapdoor1] = TRItemFlags.Trapdoor, + [TR2Type.Trapdoor2] = TRItemFlags.Trapdoor, + [TR2Type.UnderwaterSwitch] = TRItemFlags.UnderwaterSwitch, + [TR2Type.RollingStorageDrums] = TRItemFlags.Boulder | TRItemFlags.PairC, + }, + [TR2LevelNames.DA] = new() + { + [TR2Type.Door1] = TRItemFlags.LeftDoor | TRItemFlags.FourClick, + [TR2Type.Door2] = TRItemFlags.LeftDoor | TRItemFlags.FourClick, + [TR2Type.Door3] = TRItemFlags.LeftDoor | TRItemFlags.FourClick, + [TR2Type.PushButtonSwitch] = TRItemFlags.PushButton, + [TR2Type.PushBlock1] = TRItemFlags.PushBlock, + [TR2Type.Trapdoor1] = TRItemFlags.Trapdoor, + [TR2Type.Trapdoor2] = TRItemFlags.Trapdoor, + [TR2Type.UnderwaterSwitch] = TRItemFlags.UnderwaterSwitch, + }, + }, + new() + { + [TR2LevelNames.FATHOMS] = new() + { + [TR2Type.Door1] = TRItemFlags.LeftDoor | TRItemFlags.FourClick, + [TR2Type.Door2] = TRItemFlags.LeftDoor | TRItemFlags.FourClick, + [TR2Type.Door3] = TRItemFlags.LeftDoor | TRItemFlags.FourClick, + [TR2Type.PushBlock1] = TRItemFlags.PushBlock, + [TR2Type.Trapdoor1] = TRItemFlags.Trapdoor, + [TR2Type.UnderwaterSwitch] = TRItemFlags.UnderwaterSwitch, + [TR2Type.WallSwitch] = TRItemFlags.WallSwitch, + }, + [TR2LevelNames.DORIA] = new() + { + // Door2 omitted (wheel door) + [TR2Type.Door1] = TRItemFlags.LeftDoor | TRItemFlags.FourClick, + [TR2Type.Door3] = TRItemFlags.RightDoor | TRItemFlags.FiveClick | TRItemFlags.PairA, + [TR2Type.Door4] = TRItemFlags.LeftDoor | TRItemFlags.FiveClick | TRItemFlags.PairA, + [TR2Type.Door5] = TRItemFlags.LeftDoor | TRItemFlags.FourClick, + [TR2Type.LiftingDoor1] = TRItemFlags.LeftDoor | TRItemFlags.FourClick, + [TR2Type.FallingBlock] = TRItemFlags.FallingBlock, + [TR2Type.PushButtonSwitch] = TRItemFlags.PushButton, + [TR2Type.PushBlock2] = TRItemFlags.PushBlock, + [TR2Type.PushBlock3] = TRItemFlags.PushBlock, + [TR2Type.PushBlock4] = TRItemFlags.PushBlock, + [TR2Type.Trapdoor1] = TRItemFlags.Trapdoor, + [TR2Type.Trapdoor2] = TRItemFlags.Trapdoor, + [TR2Type.UnderwaterSwitch] = TRItemFlags.UnderwaterSwitch, + [TR2Type.WallSwitch] = TRItemFlags.WallSwitch, + [TR2Type.RollingStorageDrums] = TRItemFlags.Boulder | TRItemFlags.PairC, + [TR2Type.TeethSpikesOrGlassShards] = TRItemFlags.Spikes, + }, + [TR2LevelNames.LQ] = new() + { + [TR2Type.Door1] = TRItemFlags.LeftDoor | TRItemFlags.FourClick, + [TR2Type.Door2] = TRItemFlags.RightDoor | TRItemFlags.FiveClick | TRItemFlags.PairA, + [TR2Type.Door3] = TRItemFlags.LeftDoor | TRItemFlags.FiveClick | TRItemFlags.PairA, + [TR2Type.FallingBlock] = TRItemFlags.FallingBlock, + [TR2Type.PushButtonSwitch] = TRItemFlags.PushButton, + [TR2Type.PushBlock1] = TRItemFlags.PushBlock, + [TR2Type.PushBlock2] = TRItemFlags.PushBlock, + [TR2Type.Trapdoor1] = TRItemFlags.Trapdoor, + [TR2Type.UnderwaterSwitch] = TRItemFlags.UnderwaterSwitch, + [TR2Type.WallSwitch] = TRItemFlags.WallSwitch, + [TR2Type.RollingStorageDrums] = TRItemFlags.Boulder | TRItemFlags.PairC, + [TR2Type.TeethSpikesOrGlassShards] = TRItemFlags.Spikes, + }, + [TR2LevelNames.DECK] = new() + { + [TR2Type.Door1] = TRItemFlags.LeftDoor | TRItemFlags.FourClick, + [TR2Type.Door4] = TRItemFlags.LeftDoor | TRItemFlags.FourClick, + [TR2Type.Door5] = TRItemFlags.LeftDoor | TRItemFlags.FourClick, + [TR2Type.FallingBlock] = TRItemFlags.FallingBlock, + [TR2Type.PushButtonSwitch] = TRItemFlags.PushButton, + [TR2Type.PushBlock2] = TRItemFlags.PushBlock, + [TR2Type.Trapdoor1] = TRItemFlags.Trapdoor, + [TR2Type.UnderwaterSwitch] = TRItemFlags.UnderwaterSwitch, + [TR2Type.WallSwitch] = TRItemFlags.WallSwitch, + [TR2Type.TeethSpikesOrGlassShards] = TRItemFlags.Spikes, + }, + }, + new() + { + [TR2LevelNames.TIBET] = new() + { + [TR2Type.Door1] = TRItemFlags.LeftDoor | TRItemFlags.FourClick, + [TR2Type.Door2] = TRItemFlags.LeftDoor | TRItemFlags.FourClick, + [TR2Type.BreakableWindow1] = TRItemFlags.BreakableWindow, + [TR2Type.PushBlock1] = TRItemFlags.PushBlock, + [TR2Type.Trapdoor1] = TRItemFlags.Trapdoor | TRItemFlags.EightClick, + [TR2Type.WallSwitch] = TRItemFlags.WallSwitch, + [TR2Type.BouldersOrSnowballs] = TRItemFlags.Boulder | TRItemFlags.PairB, + }, + [TR2LevelNames.MONASTERY] = new() + { + [TR2Type.Door1] = TRItemFlags.LeftDoor | TRItemFlags.EightClick | TRItemFlags.PairA, + [TR2Type.Door2] = TRItemFlags.RightDoor | TRItemFlags.EightClick | TRItemFlags.PairA, + [TR2Type.Door3] = TRItemFlags.LeftDoor | TRItemFlags.FourClick, + [TR2Type.Door4] = TRItemFlags.LeftDoor | TRItemFlags.FourClick, + [TR2Type.Door5] = TRItemFlags.LeftDoor | TRItemFlags.FourClick, + [TR2Type.BreakableWindow1] = TRItemFlags.BreakableWindow, + [TR2Type.BreakableWindow2] = TRItemFlags.BreakableWindow, + [TR2Type.PushBlock1] = TRItemFlags.PushBlock, + [TR2Type.Trapdoor1] = TRItemFlags.Trapdoor, + [TR2Type.WallSwitch] = TRItemFlags.WallSwitch, + [TR2Type.RollingBall] = TRItemFlags.Boulder | TRItemFlags.PairA, + [TR2Type.RollingSpindle] = TRItemFlags.Spindle, + }, + [TR2LevelNames.COT] = new() + { + [TR2Type.Door1] = TRItemFlags.LeftDoor | TRItemFlags.EightClick | TRItemFlags.PairA, + [TR2Type.Door2] = TRItemFlags.RightDoor | TRItemFlags.EightClick | TRItemFlags.PairA, + [TR2Type.Door3] = TRItemFlags.LeftDoor | TRItemFlags.FourClick, + [TR2Type.LiftingDoor3] = TRItemFlags.LiftingDoor | TRItemFlags.EightClick, + [TR2Type.FallingBlock] = TRItemFlags.FallingBlock, + [TR2Type.PushBlock2] = TRItemFlags.PushBlock, + [TR2Type.Trapdoor2] = TRItemFlags.Trapdoor | TRItemFlags.EightClick, + [TR2Type.WallSwitch] = TRItemFlags.WallSwitch, + [TR2Type.BouldersOrSnowballs] = TRItemFlags.Boulder | TRItemFlags.PairB, + [TR2Type.TeethSpikesOrGlassShards] = TRItemFlags.Spikes, + }, + [TR2LevelNames.CHICKEN] = new() + { + [TR2Type.Door1] = TRItemFlags.LeftDoor | TRItemFlags.EightClick | TRItemFlags.PairA, + [TR2Type.Door2] = TRItemFlags.RightDoor | TRItemFlags.EightClick | TRItemFlags.PairA, + [TR2Type.Door3] = TRItemFlags.LeftDoor | TRItemFlags.FourClick, + [TR2Type.LiftingDoor3] = TRItemFlags.LiftingDoor | TRItemFlags.EightClick, + [TR2Type.BreakableWindow2] = TRItemFlags.BreakableWindow, + [TR2Type.BouncePad] = TRItemFlags.Springboard, + [TR2Type.PushBlock1] = TRItemFlags.PushBlock, + [TR2Type.Trapdoor1] = TRItemFlags.Trapdoor, + [TR2Type.WallSwitch] = TRItemFlags.WallSwitch, + [TR2Type.BouldersOrSnowballs] = TRItemFlags.Boulder | TRItemFlags.PairB, + [TR2Type.TeethSpikesOrGlassShards] = TRItemFlags.Spikes, + }, + }, + new() + { + [TR2LevelNames.XIAN] = new() + { + [TR2Type.Door1] = TRItemFlags.LeftDoor | TRItemFlags.EightClick | TRItemFlags.PairA, + [TR2Type.Door2] = TRItemFlags.RightDoor | TRItemFlags.EightClick | TRItemFlags.PairA, + [TR2Type.Door3] = TRItemFlags.LeftDoor | TRItemFlags.FourClick, + [TR2Type.Door4] = TRItemFlags.LeftDoor | TRItemFlags.EightClick | TRItemFlags.PairB, + [TR2Type.Door5] = TRItemFlags.RightDoor | TRItemFlags.EightClick | TRItemFlags.PairB, + [TR2Type.LiftingDoor1] = TRItemFlags.LeftDoor | TRItemFlags.FourClick, + [TR2Type.LiftingDoor2] = TRItemFlags.LiftingDoor | TRItemFlags.FourClick, + [TR2Type.LiftingDoor3] = TRItemFlags.LiftingDoor | TRItemFlags.EightClick, + [TR2Type.FallingBlock] = TRItemFlags.FallingBlock, + [TR2Type.PushButtonSwitch] = TRItemFlags.PushButton, + [TR2Type.BouncePad] = TRItemFlags.Springboard, + [TR2Type.PushBlock2] = TRItemFlags.PushBlock, + [TR2Type.Trapdoor1] = TRItemFlags.Trapdoor, + [TR2Type.Trapdoor2] = TRItemFlags.Trapdoor | TRItemFlags.EightClick, + [TR2Type.UnderwaterSwitch] = TRItemFlags.UnderwaterSwitch, + [TR2Type.WallSwitch] = TRItemFlags.WallSwitch, + [TR2Type.RollingBall] = TRItemFlags.Boulder | TRItemFlags.PairA, + [TR2Type.TeethSpikesOrGlassShards] = TRItemFlags.Spikes, + [TR2Type.RollingSpindle] = TRItemFlags.Spindle, + }, + [TR2LevelNames.FLOATER] = new() + { + [TR2Type.Door1] = TRItemFlags.LeftDoor | TRItemFlags.EightClick | TRItemFlags.PairA, + [TR2Type.Door2] = TRItemFlags.RightDoor | TRItemFlags.EightClick | TRItemFlags.PairA, + [TR2Type.Door3] = TRItemFlags.LeftDoor | TRItemFlags.EightClick | TRItemFlags.PairB, + [TR2Type.Door4] = TRItemFlags.RightDoor | TRItemFlags.EightClick | TRItemFlags.PairB, + [TR2Type.LiftingDoor1] = TRItemFlags.LiftingDoor | TRItemFlags.EightClick, + [TR2Type.LiftingDoor2] = TRItemFlags.LiftingDoor | TRItemFlags.EightClick, + [TR2Type.LiftingDoor3] = TRItemFlags.LiftingDoor | TRItemFlags.EightClick, + [TR2Type.PushBlock1] = TRItemFlags.PushBlock, + [TR2Type.Trapdoor1] = TRItemFlags.Trapdoor | TRItemFlags.EightClick, + [TR2Type.Trapdoor2] = TRItemFlags.Trapdoor, + [TR2Type.WallSwitch] = TRItemFlags.WallSwitch, + [TR2Type.RollingBall] = TRItemFlags.Boulder | TRItemFlags.PairA, + [TR2Type.TeethSpikesOrGlassShards] = TRItemFlags.Spikes, + }, + [TR2LevelNames.LAIR] = new() + { + [TR2Type.Door3] = TRItemFlags.LeftDoor | TRItemFlags.EightClick | TRItemFlags.PairA, + [TR2Type.Door4] = TRItemFlags.RightDoor | TRItemFlags.EightClick | TRItemFlags.PairA, + [TR2Type.Door5] = TRItemFlags.LiftingDoor | TRItemFlags.EightClick, + [TR2Type.LiftingDoor1] = TRItemFlags.LiftingDoor | TRItemFlags.EightClick, + [TR2Type.WallSwitch] = TRItemFlags.WallSwitch, + }, + } + }; + + private static readonly List>> _tr3ItemFlags = new() + { + new() + { + [TR3LevelNames.ASSAULT] = new() + { + [TR3Type.Door1] = TRItemFlags.LeftDoor | TRItemFlags.FourClick, + [TR3Type.Door2] = TRItemFlags.LeftDoor | TRItemFlags.FiveClick, + [TR3Type.Door3] = TRItemFlags.LeftDoor | TRItemFlags.FiveClick, + [TR3Type.Door4] = TRItemFlags.LeftDoor | TRItemFlags.FiveClick, + [TR3Type.Door5] = TRItemFlags.LeftDoor | TRItemFlags.FiveClick, + [TR3Type.Door6] = TRItemFlags.LeftDoor | TRItemFlags.FiveClick, + [TR3Type.Door7] = TRItemFlags.LeftDoor | TRItemFlags.FiveClick, + [TR3Type.Door8] = TRItemFlags.LeftDoor | TRItemFlags.EightClick, + [TR3Type.PushButtonSwitch] = TRItemFlags.PushButton, + [TR3Type.PushableBlock1] = TRItemFlags.PushBlock, + }, + }, + new() + { + [TR3LevelNames.JUNGLE] = new() + { + [TR3Type.Door1] = TRItemFlags.LeftDoor | TRItemFlags.FourClick, + [TR3Type.Door8] = TRItemFlags.LiftingDoor | TRItemFlags.EightClick, + [TR3Type.PushableBlock1] = TRItemFlags.PushBlock, + [TR3Type.Trapdoor1] = TRItemFlags.Trapdoor, + [TR3Type.WallSwitch] = TRItemFlags.WallSwitch, + }, + [TR3LevelNames.RUINS] = new() + { + [TR3Type.Door1] = TRItemFlags.LeftDoor | TRItemFlags.EightClick | TRItemFlags.PairA, + [TR3Type.Door2] = TRItemFlags.LeftDoor | TRItemFlags.FourClick, + [TR3Type.Door3] = TRItemFlags.RightDoor | TRItemFlags.EightClick | TRItemFlags.PairA, + [TR3Type.PushableBlock1] = TRItemFlags.PushBlock, + [TR3Type.Trapdoor1] = TRItemFlags.Trapdoor, + [TR3Type.UnderwaterSwitch] = TRItemFlags.UnderwaterSwitch, + [TR3Type.WallSwitch] = TRItemFlags.WallSwitch, + }, + [TR3LevelNames.GANGES] = new() + { + [TR3Type.Door1] = TRItemFlags.LeftDoor | TRItemFlags.EightClick | TRItemFlags.PairA, + [TR3Type.Door2] = TRItemFlags.LeftDoor | TRItemFlags.FourClick, + [TR3Type.Door3] = TRItemFlags.RightDoor | TRItemFlags.EightClick | TRItemFlags.PairA, + [TR3Type.PushButtonSwitch] = TRItemFlags.PushButton, + [TR3Type.Trapdoor1] = TRItemFlags.Trapdoor, + }, + [TR3LevelNames.CAVES] = new() + { + [TR3Type.PushableBlock1] = TRItemFlags.PushBlock, + }, + }, + new() + { + [TR3LevelNames.COASTAL] = new() + { + [TR3Type.Door1] = TRItemFlags.LeftDoor | TRItemFlags.EightClick, + [TR3Type.Door4] = TRItemFlags.LeftDoor| TRItemFlags.FourClick, + [TR3Type.PushButtonSwitch] = TRItemFlags.PushButton, + [TR3Type.Trapdoor1] = TRItemFlags.Trapdoor, + }, + [TR3LevelNames.CRASH] = new() + { + [TR3Type.Door1] = TRItemFlags.LeftDoor | TRItemFlags.SixClick, + [TR3Type.Door2] = TRItemFlags.LeftDoor| TRItemFlags.FourClick, + [TR3Type.Trapdoor1] = TRItemFlags.Trapdoor, + [TR3Type.Trapdoor2] = TRItemFlags.Trapdoor, + [TR3Type.UnderwaterSwitch] = TRItemFlags.UnderwaterSwitch, + [TR3Type.WallSwitch] = TRItemFlags.WallSwitch, + }, + [TR3LevelNames.MADUBU] = new() + { + [TR3Type.Door1] = TRItemFlags.LeftDoor | TRItemFlags.EightClick, + [TR3Type.PushButtonSwitch] = TRItemFlags.PushButton, + [TR3Type.Trapdoor1] = TRItemFlags.Trapdoor, + }, + [TR3LevelNames.PUNA] = new() + { + [TR3Type.Door1] = TRItemFlags.LeftDoor | TRItemFlags.FourClick, + [TR3Type.Door2] = TRItemFlags.RightDoor | TRItemFlags.FiveClick | TRItemFlags.PairA, + [TR3Type.Door3] = TRItemFlags.LeftDoor | TRItemFlags.FiveClick | TRItemFlags.PairA, + [TR3Type.Door4] = TRItemFlags.LeftDoor | TRItemFlags.FourClick, + [TR3Type.PushButtonSwitch] = TRItemFlags.PushButton, + [TR3Type.PushableBlock1] = TRItemFlags.PushBlock, + [TR3Type.WallSwitch] = TRItemFlags.WallSwitch, + }, + }, + new() + { + [TR3LevelNames.THAMES] = new() + { + [TR3Type.Door1] = TRItemFlags.LeftDoor | TRItemFlags.FourClick, + [TR3Type.PushButtonSwitch] = TRItemFlags.PushButton, + [TR3Type.PushableBlock1] = TRItemFlags.PushBlock, + [TR3Type.PushableBlock2] = TRItemFlags.PushBlock, + [TR3Type.Trapdoor1] = TRItemFlags.Trapdoor, + [TR3Type.Trapdoor2] = TRItemFlags.Trapdoor, + [TR3Type.UnderwaterSwitch] = TRItemFlags.UnderwaterSwitch, + }, + [TR3LevelNames.ALDWYCH] = new() + { + // Door1 is mini-door with a static mesh, so ignored + [TR3Type.Door2] = TRItemFlags.LeftDoor | TRItemFlags.FourClick, + [TR3Type.Door3] = TRItemFlags.LeftDoor | TRItemFlags.FourClick, + [TR3Type.Door4] = TRItemFlags.LeftDoor | TRItemFlags.FourClick, + [TR3Type.Door5] = TRItemFlags.LeftDoor | TRItemFlags.FourClick, + [TR3Type.Door6] = TRItemFlags.LeftDoor | TRItemFlags.FourClick, + [TR3Type.Door7] = TRItemFlags.LeftDoor | TRItemFlags.FourClick, + [TR3Type.FallingBlock] = TRItemFlags.FallingBlock, + [TR3Type.PushButtonSwitch] = TRItemFlags.PushButton, + [TR3Type.PushableBlock1] = TRItemFlags.PushBlock, + [TR3Type.Trapdoor1] = TRItemFlags.Trapdoor, + [TR3Type.Trapdoor2] = TRItemFlags.Trapdoor, + }, + [TR3LevelNames.LUDS] = new() + { + [TR3Type.Door2] = TRItemFlags.LeftDoor | TRItemFlags.FourClick, + [TR3Type.Door3] = TRItemFlags.LeftDoor | TRItemFlags.FourClick, + [TR3Type.Door4] = TRItemFlags.LeftDoor | TRItemFlags.FourClick, + [TR3Type.Door7] = TRItemFlags.RightDoor | TRItemFlags.FiveClick | TRItemFlags.PairA, + [TR3Type.Door8] = TRItemFlags.LeftDoor | TRItemFlags.FiveClick | TRItemFlags.PairA, + [TR3Type.FallingBlock] = TRItemFlags.FallingBlock, + [TR3Type.PushButtonSwitch] = TRItemFlags.PushButton, + [TR3Type.PushableBlock1] = TRItemFlags.PushBlock, + [TR3Type.PushableBlock2] = TRItemFlags.PushBlock, + [TR3Type.Trapdoor1] = TRItemFlags.Trapdoor, + [TR3Type.UnderwaterSwitch] = TRItemFlags.UnderwaterSwitch, + }, + [TR3LevelNames.CITY] = new() + { + [TR3Type.Door1] = TRItemFlags.LeftDoor | TRItemFlags.FourClick, + [TR3Type.PushButtonSwitch] = TRItemFlags.PushButton, + [TR3Type.Trapdoor1] = TRItemFlags.Trapdoor, + }, + [TR3LevelNames.HALLOWS] = new() + { + [TR3Type.Door1] = TRItemFlags.LeftDoor | TRItemFlags.FourClick, + [TR3Type.Door5] = TRItemFlags.LeftDoor | TRItemFlags.FourClick, + [TR3Type.FallingBlock] = TRItemFlags.FallingBlock, + [TR3Type.PushButtonSwitch] = TRItemFlags.PushButton, + [TR3Type.Trapdoor1] = TRItemFlags.Trapdoor, + [TR3Type.WallSwitch] = TRItemFlags.WallSwitch, + }, + }, + new() + { + [TR3LevelNames.NEVADA] = new() + { + [TR3Type.Door1] = TRItemFlags.LeftDoor | TRItemFlags.FourClick, + [TR3Type.Door2] = TRItemFlags.LeftDoor | TRItemFlags.EightClick | TRItemFlags.PairA, + [TR3Type.Door3] = TRItemFlags.RightDoor | TRItemFlags.EightClick | TRItemFlags.PairA, + [TR3Type.Door4] = TRItemFlags.LeftDoor | TRItemFlags.FourClick, + [TR3Type.PushableBlock1] = TRItemFlags.PushBlock, + [TR3Type.Trapdoor1] = TRItemFlags.Trapdoor, + [TR3Type.UnderwaterSwitch] = TRItemFlags.UnderwaterSwitch, + }, + [TR3LevelNames.HSC] = new() + { + [TR3Type.Door1] = TRItemFlags.LeftDoor | TRItemFlags.FourClick, + [TR3Type.Door2] = TRItemFlags.RightDoor | TRItemFlags.FiveClick | TRItemFlags.PairA, + [TR3Type.Door3] = TRItemFlags.LeftDoor | TRItemFlags.FiveClick | TRItemFlags.PairA, + [TR3Type.Door4] = TRItemFlags.LeftDoor | TRItemFlags.FourClick, + [TR3Type.Door5] = TRItemFlags.LiftingDoor | TRItemFlags.FourClick, + [TR3Type.Door6] = TRItemFlags.LiftingDoor | TRItemFlags.FourClick, + [TR3Type.PushButtonSwitch] = TRItemFlags.PushButton, + [TR3Type.PushableBlock1] = TRItemFlags.PushBlock, + [TR3Type.Trapdoor1] = TRItemFlags.Trapdoor, + [TR3Type.UnderwaterSwitch] = TRItemFlags.UnderwaterSwitch, + }, + [TR3LevelNames.AREA51] = new() + { + // Doors 7 and 8 are non-standard so ignored + [TR3Type.Door1] = TRItemFlags.LeftDoor | TRItemFlags.FourClick, + [TR3Type.Door4] = TRItemFlags.RightDoor | TRItemFlags.FiveClick | TRItemFlags.PairA, + [TR3Type.Door5] = TRItemFlags.LeftDoor | TRItemFlags.FiveClick | TRItemFlags.PairA, + [TR3Type.Door6] = TRItemFlags.LeftDoor | TRItemFlags.FourClick, + [TR3Type.PushButtonSwitch] = TRItemFlags.PushButton, + [TR3Type.Trapdoor1] = TRItemFlags.Trapdoor, + [TR3Type.Trapdoor2] = TRItemFlags.Trapdoor, + }, + }, + new() + { + [TR3LevelNames.ANTARC] = new() + { + // Doors 1, 2 and 5 are mini-doors; door6 is a gauge + [TR3Type.Door3] = TRItemFlags.LiftingDoor | TRItemFlags.FourClick, + [TR3Type.Door4] = TRItemFlags.LeftDoor | TRItemFlags.SixClick, + [TR3Type.PushButtonSwitch] = TRItemFlags.PushButton, + [TR3Type.Trapdoor1] = TRItemFlags.Trapdoor, + [TR3Type.WallSwitch] = TRItemFlags.WallSwitch, + }, + [TR3LevelNames.RXTECH] = new() + { + [TR3Type.Door1] = TRItemFlags.LeftDoor | TRItemFlags.FourClick, + [TR3Type.Door3] = TRItemFlags.LiftingDoor | TRItemFlags.FourClick, + [TR3Type.PushButtonSwitch] = TRItemFlags.PushButton, + }, + [TR3LevelNames.TINNOS] = new() + { + [TR3Type.Door1] = TRItemFlags.LeftDoor | TRItemFlags.EightClick | TRItemFlags.PairA, + [TR3Type.Door2] = TRItemFlags.RightDoor | TRItemFlags.EightClick | TRItemFlags.PairA, + [TR3Type.Door3] = TRItemFlags.LiftingDoor | TRItemFlags.FourClick, + [TR3Type.Door4] = TRItemFlags.LiftingDoor | TRItemFlags.EightClick, + [TR3Type.Door6] = TRItemFlags.LeftDoor | TRItemFlags.FourClick, + [TR3Type.Door7] = TRItemFlags.LiftingDoor | TRItemFlags.FourClick, + [TR3Type.PushButtonSwitch] = TRItemFlags.PushButton, + [TR3Type.PushableBlock1] = TRItemFlags.PushBlock, + [TR3Type.Trapdoor1] = TRItemFlags.Trapdoor, + [TR3Type.UnderwaterSwitch] = TRItemFlags.UnderwaterSwitch, + [TR3Type.WallSwitch] = TRItemFlags.WallSwitch, + }, + [TR3LevelNames.WILLIE] = new() + { + [TR3Type.Door1] = TRItemFlags.LeftDoor | TRItemFlags.FourClick, + [TR3Type.Door2] = TRItemFlags.LeftDoor | TRItemFlags.SixClick, + [TR3Type.PushButtonSwitch] = TRItemFlags.PushButton, + }, + }, + }; +}