diff --git a/TREnvironmentEditor/Model/Conditions/Models/EMModelExistsCondition.cs b/TREnvironmentEditor/Model/Conditions/Models/EMModelExistsCondition.cs index a5169fc8a..b20ebcef8 100644 --- a/TREnvironmentEditor/Model/Conditions/Models/EMModelExistsCondition.cs +++ b/TREnvironmentEditor/Model/Conditions/Models/EMModelExistsCondition.cs @@ -8,19 +8,16 @@ public class EMModelExistsCondition : BaseEMCondition protected override bool Evaluate(TR1Level level) { - TRModel model = level.Models.Find(m => m.ID == ModelID); - return model != null; + return level.Models.ContainsKey((TR1Type)ModelID); } protected override bool Evaluate(TR2Level level) { - TRModel model = level.Models.Find(m => m.ID == ModelID); - return model != null; + return level.Models.ContainsKey((TR2Type)ModelID); } protected override bool Evaluate(TR3Level level) { - TRModel model = level.Models.Find(m => m.ID == ModelID); - return model != null; + return level.Models.ContainsKey((TR3Type)ModelID); } } diff --git a/TREnvironmentEditor/Model/Conditions/Models/EMUnconditionalBirdCheck.cs b/TREnvironmentEditor/Model/Conditions/Models/EMUnconditionalBirdCheck.cs index 3699d2f73..cbb77280b 100644 --- a/TREnvironmentEditor/Model/Conditions/Models/EMUnconditionalBirdCheck.cs +++ b/TREnvironmentEditor/Model/Conditions/Models/EMUnconditionalBirdCheck.cs @@ -11,12 +11,8 @@ protected override bool Evaluate(TR1Level level) protected override bool Evaluate(TR2Level level) { - TRModel model = level.Models.Find(m => m.ID == (uint)TR2Type.BirdMonster); - if (model != null) - { - return model.Animations[20].FrameEnd == model.Animations[19].FrameEnd; - } - return false; + TRModel model = level.Models[TR2Type.BirdMonster]; + return model != null && model.Animations[20].FrameEnd == model.Animations[19].FrameEnd; } protected override bool Evaluate(TR3Level level) diff --git a/TREnvironmentEditor/Model/Types/Mirroring/EMMirrorFunction.cs b/TREnvironmentEditor/Model/Types/Mirroring/EMMirrorFunction.cs index 2d71d359a..231954cd0 100644 --- a/TREnvironmentEditor/Model/Types/Mirroring/EMMirrorFunction.cs +++ b/TREnvironmentEditor/Model/Types/Mirroring/EMMirrorFunction.cs @@ -1078,7 +1078,7 @@ private static void MirrorTextures(TR1Level level) // Models such as doors may use textures also used on walls, but // these models aren't mirrored so the texture will end up being // upside down. Rotate the relevant mesh faces. - MirrorDependentFaces(level.Models, textureReferences); + MirrorDependentFaces(level.Models.Values, textureReferences); } private static void MirrorTextures(TR2Level level) @@ -1247,19 +1247,14 @@ private static void MirrorObjectTextures(ISet textureReferences, List models, ISet textureReferences) { - foreach (TRModel model in models) + IEnumerable faces = models.SelectMany(m => m.Meshes) + .SelectMany(m => m.TexturedRectangles) + .Where(f => textureReferences.Contains(f.Texture)) + .Distinct(); + foreach (TRMeshFace face in faces) { - foreach (TRMesh mesh in model.Meshes) - { - foreach (TRMeshFace face in mesh.TexturedRectangles) - { - if (textureReferences.Contains(face.Texture)) - { - face.SwapVertices(0, 2); - face.SwapVertices(1, 3); - } - } - } + face.SwapVertices(0, 2); + face.SwapVertices(1, 3); } } } diff --git a/TREnvironmentEditor/Model/Types/Mirroring/EMMirrorModelFunction.cs b/TREnvironmentEditor/Model/Types/Mirroring/EMMirrorModelFunction.cs index 852a10fd1..5979de686 100644 --- a/TREnvironmentEditor/Model/Types/Mirroring/EMMirrorModelFunction.cs +++ b/TREnvironmentEditor/Model/Types/Mirroring/EMMirrorModelFunction.cs @@ -10,21 +10,24 @@ public class EMMirrorModelFunction : BaseEMFunction public override void ApplyToLevel(TR1Level level) { IEnumerable models = level.Models - .Where(m => ModelIDs.Contains(m.ID) && m.Meshes.Count == 1); + .Where(kvp => ModelIDs.Contains((uint)kvp.Key) && kvp.Value.Meshes.Count == 1) + .Select(kvp => kvp.Value); MirrorObjectTextures(MirrorMeshes(models), level.ObjectTextures); } public override void ApplyToLevel(TR2Level level) { IEnumerable models = level.Models - .Where(m => ModelIDs.Contains(m.ID) && m.Meshes.Count == 1); + .Where(kvp => ModelIDs.Contains((uint)kvp.Key) && kvp.Value.Meshes.Count == 1) + .Select(kvp => kvp.Value); MirrorObjectTextures(MirrorMeshes(models), level.ObjectTextures); } public override void ApplyToLevel(TR3Level level) { IEnumerable models = level.Models - .Where(m => ModelIDs.Contains(m.ID) && m.Meshes.Count == 1); + .Where(kvp => ModelIDs.Contains((uint)kvp.Key) && kvp.Value.Meshes.Count == 1) + .Select(kvp => kvp.Value); MirrorObjectTextures(MirrorMeshes(models), level.ObjectTextures); } diff --git a/TREnvironmentEditor/Model/Types/Models/EMConvertModelFunction.cs b/TREnvironmentEditor/Model/Types/Models/EMConvertModelFunction.cs index ab1556580..1f1171346 100644 --- a/TREnvironmentEditor/Model/Types/Models/EMConvertModelFunction.cs +++ b/TREnvironmentEditor/Model/Types/Models/EMConvertModelFunction.cs @@ -9,63 +9,35 @@ public class EMConvertModelFunction : BaseEMFunction public override void ApplyToLevel(TR1Level level) { - ConvertModel(level.Models); - UpdateModelEntities(level.Entities); + ConvertModel(level.Models, level.Entities); } public override void ApplyToLevel(TR2Level level) { - ConvertModel(level.Models); - UpdateModelEntities(level.Entities); + ConvertModel(level.Models, level.Entities); } public override void ApplyToLevel(TR3Level level) { - ConvertModel(level.Models); - UpdateModelEntities(level.Entities); + ConvertModel(level.Models, level.Entities); } - private void ConvertModel(List models) + private void ConvertModel(TRDictionary models, List entities) + where T : Enum + where E : TREntity { - if (models.Find(m => m.ID == NewModelID) == null) + T oldID = (T)(object)OldModelID; + T newID = (T)(object)NewModelID; + if (!models.ChangeKey(oldID, newID)) { - TRModel oldModel = models.Find(m => m.ID == OldModelID); - if (oldModel != null) - { - oldModel.ID = NewModelID; - } - } - } - - private void UpdateModelEntities(List entities) - { - foreach (TR1Entity entity in entities) - { - if (entity.TypeID == (TR1Type)OldModelID) - { - entity.TypeID = (TR1Type)NewModelID; - } + return; } - } - private void UpdateModelEntities(IEnumerable entities) - { - foreach (TR2Entity entity in entities) - { - if (entity.TypeID == (TR2Type)OldModelID) - { - entity.TypeID = (TR2Type)NewModelID; - } - } - } - - private void UpdateModelEntities(IEnumerable entities) - { - foreach (TR3Entity entity in entities) + foreach (E entity in entities) { - if (entity.TypeID == (TR3Type)OldModelID) + if (EqualityComparer.Default.Equals(entity.TypeID, oldID)) { - entity.TypeID = (TR3Type)NewModelID; + entity.TypeID = newID; } } } diff --git a/TREnvironmentEditor/Model/Types/Models/EMImportNonGraphicsModelFunction.cs b/TREnvironmentEditor/Model/Types/Models/EMImportNonGraphicsModelFunction.cs index db2b0115e..ea839dde1 100644 --- a/TREnvironmentEditor/Model/Types/Models/EMImportNonGraphicsModelFunction.cs +++ b/TREnvironmentEditor/Model/Types/Models/EMImportNonGraphicsModelFunction.cs @@ -10,8 +10,8 @@ public class EMImportNonGraphicsModelFunction : BaseEMFunction public override void ApplyToLevel(TR1Level level) { - List data = PrepareImportData(level.Models); - if (data.Count == 0) + IEnumerable data = PrepareImportData(level.Models); + if (!data.Any()) { return; } @@ -26,13 +26,13 @@ public override void ApplyToLevel(TR1Level level) }; importer.Import(); - RemapFaces(data, level.ObjectTextures.Count - 1, modelID => level.Models.Find(m => m.ID == modelID)); + RemapFaces(data, level.ObjectTextures.Count - 1, level.Models); } public override void ApplyToLevel(TR2Level level) { - List data = PrepareImportData(level.Models); - if (data.Count == 0) + IEnumerable data = PrepareImportData(level.Models); + if (!data.Any()) { return; } @@ -47,13 +47,13 @@ public override void ApplyToLevel(TR2Level level) }; importer.Import(); - RemapFaces(data, level.ObjectTextures.Count - 1, modelID => level.Models.Find(m => m.ID == modelID)); + RemapFaces(data, level.ObjectTextures.Count - 1, level.Models); } public override void ApplyToLevel(TR3Level level) { - List data = PrepareImportData(level.Models); - if (data.Count == 0) + IEnumerable data = PrepareImportData(level.Models); + if (!data.Any()) { return; } @@ -68,27 +68,21 @@ public override void ApplyToLevel(TR3Level level) }; importer.Import(); - RemapFaces(data, level.ObjectTextures.Count - 1, modelID => level.Models.Find(m => m.ID == modelID)); + RemapFaces(data, level.ObjectTextures.Count - 1, level.Models); } - private List PrepareImportData(List existingModels) + private IEnumerable PrepareImportData(SortedDictionary existingModels) + where T : Enum { - List importData = new(); - foreach (EMMeshTextureData data in Data) - { - if (existingModels.Find(m => m.ID == data.ModelID) == null) - { - importData.Add(data); - } - } - return importData; + return Data.Where(d => !existingModels.ContainsKey((T)(object)d.ModelID)); } - private static void RemapFaces(List data, int maximumTexture, Func modelAction) + private static void RemapFaces(IEnumerable data, int maximumTexture, SortedDictionary models) + where T : Enum { foreach (EMMeshTextureData textureData in data) { - TRModel model= modelAction.Invoke(textureData.ModelID); + TRModel model = models[(T)(object)textureData.ModelID]; foreach (TRMesh mesh in model.Meshes) { foreach (TRMeshFace face in mesh.ColouredTriangles) diff --git a/TRLevelControl/Build/TRModelBuilder.cs b/TRLevelControl/Build/TRModelBuilder.cs index b32779912..a7657dbcf 100644 --- a/TRLevelControl/Build/TRModelBuilder.cs +++ b/TRLevelControl/Build/TRModelBuilder.cs @@ -3,7 +3,8 @@ namespace TRLevelControl.Build; -public class TRModelBuilder +public class TRModelBuilder + where T : Enum { private static readonly ushort _tr5ModelPadding = 0xFFEF; @@ -29,7 +30,7 @@ public TRModelBuilder(TRGameVersion version, ITRLevelObserver observer = null) _observer = observer; } - public List ReadModelData(TRLevelReader reader, IMeshProvider meshProvider) + public TRDictionary ReadModelData(TRLevelReader reader, IMeshProvider meshProvider) { ReadAnimations(reader); ReadStateChanges(reader); @@ -39,18 +40,18 @@ public List ReadModelData(TRLevelReader reader, IMeshProvider meshProvi ReadFrames(reader); ReadModels(reader); - List models = new(); + TRDictionary models = new(); foreach (PlaceholderModel placeholder in _placeholderModels) { - models.Add(BuildModel(placeholder, meshProvider)); + models[(T)(object)placeholder.ID] = BuildModel(placeholder, meshProvider); } - TestTR5Changes(models); + TestTR5Changes(models.Values); return models; } - public void WriteModelData(TRLevelWriter writer, List models) + public void WriteModelData(TRLevelWriter writer, TRDictionary models) { _placeholderAnimations = new(); _placeholderChanges = new(); @@ -62,9 +63,9 @@ public void WriteModelData(TRLevelWriter writer, List models) _dispatchToAnimMap = new(); _dispatchFrameBase = new(); - foreach (TRModel model in models) + foreach (var (type, model) in models) { - DeconstructModel(model); + DeconstructModel(type, model); } RestoreTR5Extras(); @@ -214,11 +215,7 @@ private void ReadModels(TRLevelReader reader) private TRModel BuildModel(PlaceholderModel placeholder, IMeshProvider meshProvider) { - TRModel model = new() - { - // To be eliminated - ID = placeholder.ID, - }; + TRModel model = new(); // Everything has a dummy mesh tree, so load one less than the mesh count int treePointer = (int)placeholder.MeshTree / sizeof(int); @@ -517,7 +514,7 @@ private TRAnimFrame BuildFrame(ref int frameIndex, int numRotations) return frame; } - private void TestTR5Changes(List models) + private void TestTR5Changes(IEnumerable models) { if (_observer == null || _version != TRGameVersion.TR5) { @@ -534,11 +531,11 @@ private void TestTR5Changes(List models) } } - private void DeconstructModel(TRModel model) + private void DeconstructModel(T type, TRModel model) { PlaceholderModel placeholderModel = new() { - ID = model.ID, + ID = (uint)(object)type, Animation = model.Animations.Count == 0 ? TRConsts.NoAnimation : (ushort)_placeholderAnimations.Count, FrameOffset = (uint)_frames.Count * sizeof(short), NumMeshes = (ushort)model.Meshes.Count, @@ -755,12 +752,12 @@ private void RestoreTR5Extras() } } - private void WriteAnimations(TRLevelWriter writer, List models) + private void WriteAnimations(TRLevelWriter writer, TRDictionary models) { writer.Write((uint)_placeholderAnimations.Count); - foreach (TRModel model in models) + foreach (var (type, model) in models) { - PlaceholderModel placeholderModel = _placeholderModels.Find(m => m.ID == model.ID); + PlaceholderModel placeholderModel = _placeholderModels.Find(m => m.ID == (uint)(object)type); for (int i = 0; i < model.Animations.Count; i++) { @@ -857,15 +854,15 @@ private void WriteFrames(TRLevelWriter writer) writer.Write(_frames); } - private void WriteModels(TRLevelWriter writer, List models) + private void WriteModels(TRLevelWriter writer, TRDictionary models) { writer.Write((uint)models.Count); uint treePointer = 0; ushort startingMesh = 0; - foreach (TRModel model in models) + foreach (var (type, model) in models) { - PlaceholderModel placeholderModel = _placeholderModels.Find(m => m.ID == model.ID); + PlaceholderModel placeholderModel = _placeholderModels.Find(m => m.ID == (uint)(object)type); writer.Write(placeholderModel.ID); writer.Write(placeholderModel.NumMeshes); diff --git a/TRLevelControl/Control/TR1LevelControl.cs b/TRLevelControl/Control/TR1LevelControl.cs index 876446100..58f68f2ab 100644 --- a/TRLevelControl/Control/TR1LevelControl.cs +++ b/TRLevelControl/Control/TR1LevelControl.cs @@ -259,18 +259,18 @@ private void ReadMeshData(TRLevelReader reader) private void WriteMeshData(TRLevelWriter writer) { - _meshBuilder.WriteObjectMeshes(writer, _level.Models.SelectMany(m => m.Meshes), _level.StaticMeshes); + _meshBuilder.WriteObjectMeshes(writer, _level.Models.Values.SelectMany(m => m.Meshes), _level.StaticMeshes); } private void ReadModelData(TRLevelReader reader) { - TRModelBuilder builder = new(TRGameVersion.TR1, _observer); + TRModelBuilder builder = new(TRGameVersion.TR1, _observer); _level.Models = builder.ReadModelData(reader, _meshBuilder); } private void WriteModelData(TRLevelWriter writer) { - TRModelBuilder builder = new(TRGameVersion.TR1, _observer); + TRModelBuilder builder = new(TRGameVersion.TR1, _observer); builder.WriteModelData(writer, _level.Models); } diff --git a/TRLevelControl/Control/TR2LevelControl.cs b/TRLevelControl/Control/TR2LevelControl.cs index 61dd5fca0..081deebf2 100644 --- a/TRLevelControl/Control/TR2LevelControl.cs +++ b/TRLevelControl/Control/TR2LevelControl.cs @@ -270,18 +270,18 @@ private void ReadMeshData(TRLevelReader reader) private void WriteMeshData(TRLevelWriter writer) { - _meshBuilder.WriteObjectMeshes(writer, _level.Models.SelectMany(m => m.Meshes), _level.StaticMeshes); + _meshBuilder.WriteObjectMeshes(writer, _level.Models.Values.SelectMany(m => m.Meshes), _level.StaticMeshes); } private void ReadModelData(TRLevelReader reader) { - TRModelBuilder builder = new(TRGameVersion.TR2, _observer); + TRModelBuilder builder = new(TRGameVersion.TR2, _observer); _level.Models = builder.ReadModelData(reader, _meshBuilder); } private void WriteModelData(TRLevelWriter writer) { - TRModelBuilder builder = new(TRGameVersion.TR2, _observer); + TRModelBuilder builder = new(TRGameVersion.TR2, _observer); builder.WriteModelData(writer, _level.Models); } diff --git a/TRLevelControl/Control/TR3LevelControl.cs b/TRLevelControl/Control/TR3LevelControl.cs index 97b8ca18f..708f44c5f 100644 --- a/TRLevelControl/Control/TR3LevelControl.cs +++ b/TRLevelControl/Control/TR3LevelControl.cs @@ -276,18 +276,18 @@ private void ReadMeshData(TRLevelReader reader) private void WriteMeshData(TRLevelWriter writer) { - _meshBuilder.WriteObjectMeshes(writer, _level.Models.SelectMany(m => m.Meshes), _level.StaticMeshes); + _meshBuilder.WriteObjectMeshes(writer, _level.Models.Values.SelectMany(m => m.Meshes), _level.StaticMeshes); } private void ReadModelData(TRLevelReader reader) { - TRModelBuilder builder = new(TRGameVersion.TR3, _observer); + TRModelBuilder builder = new(TRGameVersion.TR3, _observer); _level.Models = builder.ReadModelData(reader, _meshBuilder); } private void WriteModelData(TRLevelWriter writer) { - TRModelBuilder builder = new(TRGameVersion.TR3, _observer); + TRModelBuilder builder = new(TRGameVersion.TR3, _observer); builder.WriteModelData(writer, _level.Models); } diff --git a/TRLevelControl/Control/TR4LevelControl.cs b/TRLevelControl/Control/TR4LevelControl.cs index 64e649dd9..7e1ffd3ce 100644 --- a/TRLevelControl/Control/TR4LevelControl.cs +++ b/TRLevelControl/Control/TR4LevelControl.cs @@ -185,18 +185,18 @@ private void ReadMeshData(TRLevelReader reader) private void WriteMeshData(TRLevelWriter writer) { - _meshBuilder.WriteObjectMeshes(writer, _level.Models.SelectMany(m => m.Meshes), _level.StaticMeshes); + _meshBuilder.WriteObjectMeshes(writer, _level.Models.Values.SelectMany(m => m.Meshes), _level.StaticMeshes); } private void ReadModelData(TRLevelReader reader) { - TRModelBuilder builder = new(TRGameVersion.TR4, _observer); + TRModelBuilder builder = new(TRGameVersion.TR4, _observer); _level.Models = builder.ReadModelData(reader, _meshBuilder); } private void WriteModelData(TRLevelWriter writer) { - TRModelBuilder builder = new(TRGameVersion.TR4, _observer); + TRModelBuilder builder = new(TRGameVersion.TR4, _observer); builder.WriteModelData(writer, _level.Models); } diff --git a/TRLevelControl/Control/TR5LevelControl.cs b/TRLevelControl/Control/TR5LevelControl.cs index 031640c52..54e885177 100644 --- a/TRLevelControl/Control/TR5LevelControl.cs +++ b/TRLevelControl/Control/TR5LevelControl.cs @@ -202,18 +202,18 @@ private void ReadMeshData(TRLevelReader reader) private void WriteMeshData(TRLevelWriter writer) { - _meshBuilder.WriteObjectMeshes(writer, _level.Models.SelectMany(m => m.Meshes), _level.StaticMeshes); + _meshBuilder.WriteObjectMeshes(writer, _level.Models.Values.SelectMany(m => m.Meshes), _level.StaticMeshes); } private void ReadModelData(TRLevelReader reader) { - TRModelBuilder builder = new(TRGameVersion.TR5, _observer); + TRModelBuilder builder = new(TRGameVersion.TR5, _observer); _level.Models = builder.ReadModelData(reader, _meshBuilder); } private void WriteModelData(TRLevelWriter writer) { - TRModelBuilder builder = new(TRGameVersion.TR5, _observer); + TRModelBuilder builder = new(TRGameVersion.TR5, _observer); builder.WriteModelData(writer, _level.Models); } diff --git a/TRLevelControl/Model/Common/TRDictionary.cs b/TRLevelControl/Model/Common/TRDictionary.cs new file mode 100644 index 000000000..8ff9436bb --- /dev/null +++ b/TRLevelControl/Model/Common/TRDictionary.cs @@ -0,0 +1,23 @@ +namespace TRLevelControl.Model; + +public class TRDictionary : SortedDictionary + where TValue : class +{ + public new TValue this[TKey key] + { + get => ContainsKey(key) ? base[key] : null; + set => base[key] = value; + } + + public bool ChangeKey(TKey oldKey, TKey newKey) + { + if (!TryGetValue(oldKey, out TValue value)) + { + return false; + } + + Remove(oldKey); + base[newKey] = value; + return true; + } +} diff --git a/TRLevelControl/Model/Common/TRModel.cs b/TRLevelControl/Model/Common/TRModel.cs index 762696523..93ddb00cf 100644 --- a/TRLevelControl/Model/Common/TRModel.cs +++ b/TRLevelControl/Model/Common/TRModel.cs @@ -5,7 +5,6 @@ public class TRModel : ICloneable public List Animations { get; set; } = new(); public List MeshTrees { get; set; } = new(); public List Meshes { get; set; } = new(); - public uint ID { get; set; } public TRModel Clone() { @@ -14,7 +13,6 @@ public TRModel Clone() Animations = new(Animations.Select(a => a.Clone())), MeshTrees = new(MeshTrees.Select(m => m.Clone())), Meshes = new(Meshes.Select(m => m.Clone())), - ID = ID, }; } diff --git a/TRLevelControl/Model/TR1/Enums/TR1Type.cs b/TRLevelControl/Model/TR1/Enums/TR1Type.cs index 54b94bcbd..93d670f2e 100644 --- a/TRLevelControl/Model/TR1/Enums/TR1Type.cs +++ b/TRLevelControl/Model/TR1/Enums/TR1Type.cs @@ -7,7 +7,7 @@ //_N are nullmeshes (no render/collision) //_H are helper entities (not placed) //_U are unused entities -public enum TR1Type +public enum TR1Type : uint { Lara = 0, LaraPistolAnim_H = 1, diff --git a/TRLevelControl/Model/TR1/TR1Level.cs b/TRLevelControl/Model/TR1/TR1Level.cs index c8c6beb33..6735302f8 100644 --- a/TRLevelControl/Model/TR1/TR1Level.cs +++ b/TRLevelControl/Model/TR1/TR1Level.cs @@ -5,7 +5,7 @@ public class TR1Level : TRLevelBase public List Images8 { get; set; } public List Rooms { get; set; } public List FloorData { get; set; } - public List Models { get; set; } + public TRDictionary Models { get; set; } public List StaticMeshes { get; set; } public List ObjectTextures { get; set; } public List SpriteTextures { get; set; } @@ -22,4 +22,8 @@ public class TR1Level : TRLevelBase public List CinematicFrames { get; set; } public byte[] DemoData { get; set; } public SortedDictionary SoundEffects { get; set; } + + public override IEnumerable DistinctMeshes => Models.Values.SelectMany(m => m.Meshes) + .Concat(StaticMeshes.Select(s => s.Mesh)) + .Distinct(); } diff --git a/TRLevelControl/Model/TR2/Enums/TR2Type.cs b/TRLevelControl/Model/TR2/Enums/TR2Type.cs index 11bd4f7f3..4009cac64 100644 --- a/TRLevelControl/Model/TR2/Enums/TR2Type.cs +++ b/TRLevelControl/Model/TR2/Enums/TR2Type.cs @@ -7,7 +7,7 @@ //_N are nullmeshes (no render/collision) //_H are helper entities (not placed) //_U are unused entities -public enum TR2Type +public enum TR2Type : uint { Lara = 0, LaraPistolAnim_H = 1, diff --git a/TRLevelControl/Model/TR2/TR2Level.cs b/TRLevelControl/Model/TR2/TR2Level.cs index 0c8e9d534..e52459b5c 100644 --- a/TRLevelControl/Model/TR2/TR2Level.cs +++ b/TRLevelControl/Model/TR2/TR2Level.cs @@ -8,7 +8,7 @@ public class TR2Level : TRLevelBase public List Images16 { get; set; } public List Rooms { get; set; } public List FloorData { get; set; } - public List Models { get; set; } + public TRDictionary Models { get; set; } public List StaticMeshes { get; set; } public List ObjectTextures { get; set; } public List SpriteTextures { get; set; } @@ -24,4 +24,8 @@ public class TR2Level : TRLevelBase public List CinematicFrames { get; set; } public byte[] DemoData { get; set; } public SortedDictionary SoundEffects { get; set; } + + public override IEnumerable DistinctMeshes => Models.Values.SelectMany(m => m.Meshes) + .Concat(StaticMeshes.Select(s => s.Mesh)) + .Distinct(); } diff --git a/TRLevelControl/Model/TR3/Enums/TR3Type.cs b/TRLevelControl/Model/TR3/Enums/TR3Type.cs index 44d119362..9c4705a8d 100644 --- a/TRLevelControl/Model/TR3/Enums/TR3Type.cs +++ b/TRLevelControl/Model/TR3/Enums/TR3Type.cs @@ -7,7 +7,7 @@ //_N are nullmeshes (no render/collision) - RED //_H are helper entities (not placed) - GREEN //_U are unused entities - PURPLE -public enum TR3Type +public enum TR3Type : uint { Lara = 0, LaraPistolAnimation_H = 1, diff --git a/TRLevelControl/Model/TR3/TR3Level.cs b/TRLevelControl/Model/TR3/TR3Level.cs index a74a79472..df69912c5 100644 --- a/TRLevelControl/Model/TR3/TR3Level.cs +++ b/TRLevelControl/Model/TR3/TR3Level.cs @@ -8,7 +8,7 @@ public class TR3Level : TRLevelBase public List Images16 { get; set; } public List Rooms { get; set; } public List FloorData { get; set; } - public List Models { get; set; } + public TRDictionary Models { get; set; } public List StaticMeshes { get; set; } public List SpriteTextures { get; set; } public List SpriteSequences { get; set; } @@ -24,4 +24,8 @@ public class TR3Level : TRLevelBase public List CinematicFrames { get; set; } public byte[] DemoData { get; set; } public SortedDictionary SoundEffects { get; set; } + + public override IEnumerable DistinctMeshes => Models.Values.SelectMany(m => m.Meshes) + .Concat(StaticMeshes.Select(s => s.Mesh)) + .Distinct(); } diff --git a/TRLevelControl/Model/TR4/Enums/TR4Type.cs b/TRLevelControl/Model/TR4/Enums/TR4Type.cs index ba9b189db..15f2dca4d 100644 --- a/TRLevelControl/Model/TR4/Enums/TR4Type.cs +++ b/TRLevelControl/Model/TR4/Enums/TR4Type.cs @@ -1,6 +1,6 @@ namespace TRLevelControl.Model; -public enum TR4Type +public enum TR4Type : uint { Lara = 0, LaraPistolAnim = 1, diff --git a/TRLevelControl/Model/TR4/TR4Level.cs b/TRLevelControl/Model/TR4/TR4Level.cs index 11fd24de1..a8d24da7f 100644 --- a/TRLevelControl/Model/TR4/TR4Level.cs +++ b/TRLevelControl/Model/TR4/TR4Level.cs @@ -5,7 +5,7 @@ public class TR4Level : TRLevelBase public TR4Textiles Images { get; set; } public List Rooms { get; set; } public List FloorData { get; set; } - public List Models { get; set; } + public TRDictionary Models { get; set; } public List StaticMeshes { get; set; } public List SpriteTextures { get; set; } public List SpriteSequences { get; set; } @@ -22,4 +22,8 @@ public class TR4Level : TRLevelBase public List AIEntities { get; set; } public byte[] DemoData { get; set; } public SortedDictionary SoundEffects { get; set; } + + public override IEnumerable DistinctMeshes => Models.Values.SelectMany(m => m.Meshes) + .Concat(StaticMeshes.Select(s => s.Mesh)) + .Distinct(); } diff --git a/TRLevelControl/Model/TR5/Enums/TR5Type.cs b/TRLevelControl/Model/TR5/Enums/TR5Type.cs index 5eb28f8d6..89db01100 100644 --- a/TRLevelControl/Model/TR5/Enums/TR5Type.cs +++ b/TRLevelControl/Model/TR5/Enums/TR5Type.cs @@ -1,6 +1,6 @@ namespace TRLevelControl.Model; -public enum TR5Type +public enum TR5Type : uint { Lara = 0, LaraPistolAnim = 1, diff --git a/TRLevelControl/Model/TR5/TR5Level.cs b/TRLevelControl/Model/TR5/TR5Level.cs index 69c7f61f1..5a4ce8946 100644 --- a/TRLevelControl/Model/TR5/TR5Level.cs +++ b/TRLevelControl/Model/TR5/TR5Level.cs @@ -7,7 +7,7 @@ public class TR5Level : TRLevelBase public ushort WeatherType { get; set; } public List Rooms { get; set; } public List FloorData { get; set; } - public List Models { get; set; } + public TRDictionary Models { get; set; } public List StaticMeshes { get; set; } public List SpriteTextures { get; set; } public List SpriteSequences { get; set; } @@ -24,4 +24,8 @@ public class TR5Level : TRLevelBase public List AIEntities { get; set; } public byte[] DemoData { get; set; } public SortedDictionary SoundEffects { get; set; } + + public override IEnumerable DistinctMeshes => Models.Values.SelectMany(m => m.Meshes) + .Concat(StaticMeshes.Select(s => s.Mesh)) + .Distinct(); } diff --git a/TRLevelControl/Model/TRLevelBase.cs b/TRLevelControl/Model/TRLevelBase.cs index 695a41d54..ed4cf1e4e 100644 --- a/TRLevelControl/Model/TRLevelBase.cs +++ b/TRLevelControl/Model/TRLevelBase.cs @@ -3,4 +3,5 @@ public abstract class TRLevelBase { public TRVersion Version { get; set; } + public abstract IEnumerable DistinctMeshes { get; } } diff --git a/TRLevelToolset/Controls/DataControls/TR/TRAnimationControl.cs b/TRLevelToolset/Controls/DataControls/TR/TRAnimationControl.cs index 303e90735..470559f14 100644 --- a/TRLevelToolset/Controls/DataControls/TR/TRAnimationControl.cs +++ b/TRLevelToolset/Controls/DataControls/TR/TRAnimationControl.cs @@ -10,12 +10,12 @@ public void Draw() { if (ImGui.TreeNodeEx("Animations Data", ImGuiTreeNodeFlags.OpenOnArrow)) { - ImGui.Text("Animations count: " + IOManager.CurrentLevelAsTR1?.Models.Sum(m => m.Animations.Count)); - ImGui.Text("State change count: " + IOManager.CurrentLevelAsTR1?.Models.Sum(m => m.Animations.Sum(a => a.Changes.Count))); - ImGui.Text("Animation dispatch count: " + IOManager.CurrentLevelAsTR1?.Models.Sum(m => m.Animations.Sum(a => a.Changes.Sum(c => c.Dispatches.Count)))); - ImGui.Text("Animation command count: " + IOManager.CurrentLevelAsTR1?.Models.Sum(m => m.Animations.Sum(a => a.Commands.Count))); - ImGui.Text("Mesh tree count: " + IOManager.CurrentLevelAsTR1?.Models.Sum(m => m.MeshTrees.Count)); - ImGui.Text("Total frames count: " + IOManager.CurrentLevelAsTR1?.Models.Sum(m => m.Animations.Sum(a => a.Frames.Count))); + ImGui.Text("Animations count: " + IOManager.CurrentLevelAsTR1?.Models.Values.Sum(m => m.Animations.Count)); + ImGui.Text("State change count: " + IOManager.CurrentLevelAsTR1?.Models.Values.Sum(m => m.Animations.Sum(a => a.Changes.Count))); + ImGui.Text("Animation dispatch count: " + IOManager.CurrentLevelAsTR1?.Models.Values.Sum(m => m.Animations.Sum(a => a.Changes.Sum(c => c.Dispatches.Count)))); + ImGui.Text("Animation command count: " + IOManager.CurrentLevelAsTR1?.Models.Values.Sum(m => m.Animations.Sum(a => a.Commands.Count))); + ImGui.Text("Mesh tree count: " + IOManager.CurrentLevelAsTR1?.Models.Values.Sum(m => m.MeshTrees.Count)); + ImGui.Text("Total frames count: " + IOManager.CurrentLevelAsTR1?.Models.Values.Sum(m => m.Animations.Sum(a => a.Frames.Count))); ImGui.TreePop(); } diff --git a/TRLevelToolset/Controls/DataControls/TR/TRMeshControl.cs b/TRLevelToolset/Controls/DataControls/TR/TRMeshControl.cs index c6692e1d3..b070429c5 100644 --- a/TRLevelToolset/Controls/DataControls/TR/TRMeshControl.cs +++ b/TRLevelToolset/Controls/DataControls/TR/TRMeshControl.cs @@ -10,8 +10,7 @@ public void Draw() { if (ImGui.TreeNodeEx("Mesh Data", ImGuiTreeNodeFlags.OpenOnArrow)) { - ImGui.Text("Mesh count: " + IOManager.CurrentLevelAsTR1?.Models.SelectMany(m => m.Meshes) - .Concat(IOManager.CurrentLevelAsTR1.StaticMeshes.Select(s => s.Mesh)).Count()); + ImGui.Text("Mesh count: " + IOManager.CurrentLevelAsTR1?.DistinctMeshes.Count()); ImGui.TreePop(); } diff --git a/TRModelTransporter/Handlers/ModelTransportHandler.cs b/TRModelTransporter/Handlers/ModelTransportHandler.cs index a28920870..f7557e1f8 100644 --- a/TRModelTransporter/Handlers/ModelTransportHandler.cs +++ b/TRModelTransporter/Handlers/ModelTransportHandler.cs @@ -8,69 +8,61 @@ public class ModelTransportHandler { public static void Export(TR1Level level, TR1ModelDefinition definition, TR1Type entity) { - definition.Model = GetTRModel(level.Models, (short)entity); + definition.Model = level.Models[entity]; } public static void Export(TR2Level level, TR2ModelDefinition definition, TR2Type entity) { - definition.Model = GetTRModel(level.Models, (short)entity); + definition.Model = level.Models[entity]; } public static void Export(TR3Level level, TR3ModelDefinition definition, TR3Type entity) { - definition.Model = GetTRModel(level.Models, (short)entity); - } - - private static TRModel GetTRModel(List models, short entityID) - { - TRModel model = models.Find(m => m.ID == entityID); - return model ?? throw new ArgumentException($"The model for {entityID} could not be found."); + definition.Model = level.Models[entity]; } public static void Import(TR1Level level, TR1ModelDefinition definition, Dictionary aliasPriority, IEnumerable laraDependants) { - int i = level.Models.FindIndex(m => m.ID == (short)definition.Entity); - if (i == -1) + if (!level.Models.ContainsKey(definition.Entity)) { - level.Models.Add(definition.Model); + level.Models[definition.Entity] = definition.Model; } else if (!aliasPriority.ContainsKey(definition.Entity) || aliasPriority[definition.Entity] == definition.Alias) { if (!definition.HasGraphics) { // The original mesh data may still be needed so don't overwrite - definition.Model.MeshTrees = level.Models[i].MeshTrees; - definition.Model.Meshes = level.Models[i].Meshes; + definition.Model.MeshTrees = level.Models[definition.Entity].MeshTrees; + definition.Model.Meshes = level.Models[definition.Entity].Meshes; } - level.Models[i] = definition.Model; + level.Models[definition.Entity] = definition.Model; } if (laraDependants != null) { if (definition.Entity == TR1Type.Lara) { - ReplaceLaraDependants(level.Models, definition.Model, laraDependants.Select(e => (short)e)); + ReplaceLaraDependants(level.Models, definition.Model, laraDependants); } - else if (laraDependants.Contains((TR1Type)definition.Model.ID)) + else if (laraDependants.Contains(definition.Entity)) { - ReplaceLaraDependants(level.Models, level.Models.Find(m => m.ID == (uint)TR1Type.Lara), new short[] { (short)definition.Model.ID }); + ReplaceLaraDependants(level.Models, level.Models[TR1Type.Lara], new TR1Type[] { definition.Entity }); } } } public static void Import(TR2Level level, TR2ModelDefinition definition, Dictionary aliasPriority, IEnumerable laraDependants) { - int i = level.Models.FindIndex(m => m.ID == (short)definition.Entity); - if (i == -1) + if (!level.Models.ContainsKey(definition.Entity)) { - level.Models.Add(definition.Model); + level.Models[definition.Entity] = definition.Model; } else if (!aliasPriority.ContainsKey(definition.Entity) || aliasPriority[definition.Entity] == definition.Alias) { // Replacement occurs for the likes of aliases taking the place of another // e.g. WhiteTiger replacing BengalTiger in GW, or if we have a specific // alias that should always have a higher priority than its peers. - level.Models[i] = definition.Model; + level.Models[definition.Entity] = definition.Model; } // If we have replaced Lara, we need to update models such as CameraTarget, FlameEmitter etc @@ -79,44 +71,44 @@ public static void Import(TR2Level level, TR2ModelDefinition definition, Diction // their starting mesh and mesh tree indices are just remapped to Lara's. if (definition.Entity == TR2Type.Lara && laraDependants != null) { - ReplaceLaraDependants(level.Models, definition.Model, laraDependants.Select(e => (short)e)); + ReplaceLaraDependants(level.Models, definition.Model, laraDependants); } } public static void Import(TR3Level level, TR3ModelDefinition definition, Dictionary aliasPriority, IEnumerable laraDependants, IEnumerable unsafeReplacements) { - int i = level.Models.FindIndex(m => m.ID == (short)definition.Entity); - if (i == -1) + if (!level.Models.ContainsKey(definition.Entity)) { - level.Models.Add(definition.Model); + level.Models[definition.Entity] = definition.Model; } else if (!aliasPriority.ContainsKey(definition.Entity) || aliasPriority[definition.Entity] == definition.Alias) { if (!unsafeReplacements.Contains(definition.Entity)) { - level.Models[i] = definition.Model; + level.Models[definition.Entity] = definition.Model; } else { // #234 Replacing Lara entirely can cause locking issues after pressing buttons or crouching // where she refuses to come out of her stance. TR3 seems bound to having Lara's animations start // at 0, so because these don't change per skin, we just replace the meshes and frames here. - level.Models[i].Meshes = definition.Model.Meshes; - level.Models[i].MeshTrees = definition.Model.MeshTrees; + level.Models[definition.Entity].Meshes = definition.Model.Meshes; + level.Models[definition.Entity].MeshTrees = definition.Model.MeshTrees; } } if (definition.Entity == TR3Type.Lara && laraDependants != null) { - ReplaceLaraDependants(level.Models, definition.Model, laraDependants.Select(e => (short)e)); + ReplaceLaraDependants(level.Models, definition.Model, laraDependants); } } - private static void ReplaceLaraDependants(List models, TRModel lara, IEnumerable entityIDs) + private static void ReplaceLaraDependants(SortedDictionary models, TRModel lara, IEnumerable entityIDs) + where T : Enum { - foreach (short dependant in entityIDs) + foreach (T dependant in entityIDs) { - TRModel dependentModel = models.Find(m => m.ID == dependant); + models.TryGetValue(dependant, out TRModel dependentModel); if (dependentModel != null) { Debug.Assert(dependentModel.Meshes.Count == 1); diff --git a/TRModelTransporter/Handlers/Textures/TR2/TR2TextureImportHandler.cs b/TRModelTransporter/Handlers/Textures/TR2/TR2TextureImportHandler.cs index 9c7c7de29..725507a3a 100644 --- a/TRModelTransporter/Handlers/Textures/TR2/TR2TextureImportHandler.cs +++ b/TRModelTransporter/Handlers/Textures/TR2/TR2TextureImportHandler.cs @@ -66,7 +66,7 @@ private void ApplyFlamePatch() if ( _definitions.ToList().FindIndex(d => flameEnemies.Contains(d.Entity)) != -1 || - _level.Models.FindIndex(m => flameEnemies.Contains((TR2Type)m.ID)) != -1 + _level.Models.Keys.Any(flameEnemies.Contains) ) { int blastSequence = _level.SpriteSequences.FindIndex(s => s.SpriteID == (int)TR2Type.FireBlast_S_H); diff --git a/TRModelTransporter/Helpers/TRModelExtensions.cs b/TRModelTransporter/Helpers/TRModelExtensions.cs index 3264ec6f6..b37dce410 100644 --- a/TRModelTransporter/Helpers/TRModelExtensions.cs +++ b/TRModelTransporter/Helpers/TRModelExtensions.cs @@ -132,7 +132,7 @@ public static void ReindexTextures(this TR2Level level, Dictionary ind return; } - foreach (TRMesh mesh in level.Models.SelectMany(m => m.Meshes).Concat(level.StaticMeshes.Select(s => s.Mesh))) + foreach (TRMesh mesh in level.DistinctMeshes) { foreach (TRMeshFace face in mesh.TexturedFaces) { @@ -190,7 +190,7 @@ public static void ReindexTextures(this TR3Level level, Dictionary ind return; } - foreach (TRMesh mesh in level.Models.SelectMany(m => m.Meshes).Concat(level.StaticMeshes.Select(s => s.Mesh))) + foreach (TRMesh mesh in level.DistinctMeshes) { foreach (TRMeshFace face in mesh.TexturedFaces) { diff --git a/TRModelTransporter/Model/AbstractTRModelDefinition.cs b/TRModelTransporter/Model/AbstractTRModelDefinition.cs index 62c3e0c69..4a1681cab 100644 --- a/TRModelTransporter/Model/AbstractTRModelDefinition.cs +++ b/TRModelTransporter/Model/AbstractTRModelDefinition.cs @@ -8,7 +8,7 @@ namespace TRModelTransporter.Model; public abstract class AbstractTRModelDefinition : IDisposable where E : Enum { [JsonIgnore] - public abstract E Entity { get; } + public E Entity { get; set; } [JsonIgnore] public E Alias { get; set; } [JsonIgnore] diff --git a/TRModelTransporter/Model/Definitions/TR1ModelDefinition.cs b/TRModelTransporter/Model/Definitions/TR1ModelDefinition.cs index d804635c5..0255e3a55 100644 --- a/TRModelTransporter/Model/Definitions/TR1ModelDefinition.cs +++ b/TRModelTransporter/Model/Definitions/TR1ModelDefinition.cs @@ -4,7 +4,6 @@ namespace TRModelTransporter.Model.Definitions; public class TR1ModelDefinition : AbstractTRModelDefinition { - public override TR1Type Entity => (TR1Type)Model.ID; public TRCinematicFrame[] CinematicFrames { get; set; } public Dictionary Colours { get; set; } public List Meshes { get; set; } diff --git a/TRModelTransporter/Model/Definitions/TR2ModelDefinition.cs b/TRModelTransporter/Model/Definitions/TR2ModelDefinition.cs index a33cb7ed0..ac62fa34f 100644 --- a/TRModelTransporter/Model/Definitions/TR2ModelDefinition.cs +++ b/TRModelTransporter/Model/Definitions/TR2ModelDefinition.cs @@ -4,7 +4,6 @@ namespace TRModelTransporter.Model.Definitions; public class TR2ModelDefinition : AbstractTRModelDefinition { - public override TR2Type Entity => (TR2Type)Model.ID; public TRCinematicFrame[] CinematicFrames { get; set; } public Dictionary Colours { get; set; } public List Meshes { get; set; } diff --git a/TRModelTransporter/Model/Definitions/TR3ModelDefinition.cs b/TRModelTransporter/Model/Definitions/TR3ModelDefinition.cs index 117d34b9c..98811ceb5 100644 --- a/TRModelTransporter/Model/Definitions/TR3ModelDefinition.cs +++ b/TRModelTransporter/Model/Definitions/TR3ModelDefinition.cs @@ -4,7 +4,6 @@ namespace TRModelTransporter.Model.Definitions; public class TR3ModelDefinition : AbstractTRModelDefinition { - public override TR3Type Entity => (TR3Type)Model.ID; public TRCinematicFrame[] CinematicFrames { get; set; } public Dictionary Colours { get; set; } public List Meshes { get; set; } diff --git a/TRModelTransporter/Model/Textures/RemapTypes/TR1TextureRemapGroup.cs b/TRModelTransporter/Model/Textures/RemapTypes/TR1TextureRemapGroup.cs index 2cb2c8042..17537c601 100644 --- a/TRModelTransporter/Model/Textures/RemapTypes/TR1TextureRemapGroup.cs +++ b/TRModelTransporter/Model/Textures/RemapTypes/TR1TextureRemapGroup.cs @@ -7,12 +7,7 @@ public class TR1TextureRemapGroup : AbstractTextureRemapGroup { protected override IEnumerable GetModelTypes(TR1Level level) { - List types = new(); - foreach (TRModel model in level.Models) - { - types.Add((TR1Type)model.ID); - } - return types; + return level.Models.Keys.ToList(); } protected override AbstractTexturePacker CreatePacker(TR1Level level) diff --git a/TRModelTransporter/Model/Textures/RemapTypes/TR2TextureRemapGroup.cs b/TRModelTransporter/Model/Textures/RemapTypes/TR2TextureRemapGroup.cs index 1fc77bf39..a450766bd 100644 --- a/TRModelTransporter/Model/Textures/RemapTypes/TR2TextureRemapGroup.cs +++ b/TRModelTransporter/Model/Textures/RemapTypes/TR2TextureRemapGroup.cs @@ -7,12 +7,7 @@ public class TR2TextureRemapGroup : AbstractTextureRemapGroup { protected override IEnumerable GetModelTypes(TR2Level level) { - List types = new(); - foreach (TRModel model in level.Models) - { - types.Add((TR2Type)model.ID); - } - return types; + return level.Models.Keys.ToList(); } protected override AbstractTexturePacker CreatePacker(TR2Level level) diff --git a/TRModelTransporter/Model/Textures/RemapTypes/TR3TextureRemapGroup.cs b/TRModelTransporter/Model/Textures/RemapTypes/TR3TextureRemapGroup.cs index 238112141..c4be475ac 100644 --- a/TRModelTransporter/Model/Textures/RemapTypes/TR3TextureRemapGroup.cs +++ b/TRModelTransporter/Model/Textures/RemapTypes/TR3TextureRemapGroup.cs @@ -7,12 +7,7 @@ public class TR3TextureRemapGroup : AbstractTextureRemapGroup { protected override IEnumerable GetModelTypes(TR3Level level) { - List types = new(); - foreach (TRModel model in level.Models) - { - types.Add((TR3Type)model.ID); - } - return types; + return level.Models.Keys.ToList(); } protected override AbstractTexturePacker CreatePacker(TR3Level level) diff --git a/TRModelTransporter/Packing/Types/TR1TexturePacker.cs b/TRModelTransporter/Packing/Types/TR1TexturePacker.cs index dba108d24..b419bcbb6 100644 --- a/TRModelTransporter/Packing/Types/TR1TexturePacker.cs +++ b/TRModelTransporter/Packing/Types/TR1TexturePacker.cs @@ -59,7 +59,7 @@ protected override List LoadSpriteTextures() protected override List GetModelMeshes(TR1Type modelEntity) { - return Level.Models.Find(m => m.ID == (uint)modelEntity)?.Meshes; + return Level.Models[modelEntity]?.Meshes; } protected override TRSpriteSequence GetSpriteSequence(TR1Type entity) @@ -69,12 +69,7 @@ protected override TRSpriteSequence GetSpriteSequence(TR1Type entity) protected override IEnumerable GetAllModelTypes() { - List modelIDs = new(); - foreach (TRModel model in Level.Models) - { - modelIDs.Add((TR1Type)model.ID); - } - return modelIDs; + return Level.Models.Keys.ToList(); } protected override void CreateImageSpace(int count) diff --git a/TRModelTransporter/Packing/Types/TR2TexturePacker.cs b/TRModelTransporter/Packing/Types/TR2TexturePacker.cs index 959e13589..fd6cf214b 100644 --- a/TRModelTransporter/Packing/Types/TR2TexturePacker.cs +++ b/TRModelTransporter/Packing/Types/TR2TexturePacker.cs @@ -56,7 +56,7 @@ protected override List LoadSpriteTextures() protected override List GetModelMeshes(TR2Type modelEntity) { - return Level.Models.Find(m => m.ID == (uint)modelEntity)?.Meshes; + return Level.Models[modelEntity]?.Meshes; } protected override TRSpriteSequence GetSpriteSequence(TR2Type entity) @@ -66,12 +66,7 @@ protected override TRSpriteSequence GetSpriteSequence(TR2Type entity) protected override IEnumerable GetAllModelTypes() { - List modelIDs = new(); - foreach (TRModel model in Level.Models) - { - modelIDs.Add((TR2Type)model.ID); - } - return modelIDs; + return Level.Models.Keys.ToList(); } protected override void CreateImageSpace(int count) diff --git a/TRModelTransporter/Packing/Types/TR3TexturePacker.cs b/TRModelTransporter/Packing/Types/TR3TexturePacker.cs index a87016805..db56bef22 100644 --- a/TRModelTransporter/Packing/Types/TR3TexturePacker.cs +++ b/TRModelTransporter/Packing/Types/TR3TexturePacker.cs @@ -56,7 +56,7 @@ protected override List LoadSpriteTextures() protected override List GetModelMeshes(TR3Type modelEntity) { - return Level.Models.Find(m => m.ID == (uint)modelEntity)?.Meshes; + return Level.Models[modelEntity]?.Meshes; } protected override TRSpriteSequence GetSpriteSequence(TR3Type entity) @@ -66,12 +66,7 @@ protected override TRSpriteSequence GetSpriteSequence(TR3Type entity) protected override IEnumerable GetAllModelTypes() { - List modelIDs = new(); - foreach (TRModel model in Level.Models) - { - modelIDs.Add((TR3Type)model.ID); - } - return modelIDs; + return Level.Models.Keys.ToList(); } protected override void CreateImageSpace(int count) diff --git a/TRModelTransporter/Transport/TR1/TR1ModelExporter.cs b/TRModelTransporter/Transport/TR1/TR1ModelExporter.cs index 3e9363c70..b9a0f5bef 100644 --- a/TRModelTransporter/Transport/TR1/TR1ModelExporter.cs +++ b/TRModelTransporter/Transport/TR1/TR1ModelExporter.cs @@ -96,7 +96,7 @@ protected override void ModelExportReady(TR1ModelDefinition definition) public static void AmendPierreGunshot(TR1Level level) { - TRModel model = level.Models.Find(m => m.ID == (uint)TR1Type.Pierre); + TRModel model = level.Models[TR1Type.Pierre]; // Get his shooting animation TRAnimation anim = model.Animations[10]; @@ -110,7 +110,7 @@ public static void AmendPierreGunshot(TR1Level level) public static void AmendPierreDeath(TR1Level level) { - TRModel model = level.Models.Find(m => m.ID == (uint)TR1Type.Pierre); + TRModel model = level.Models[TR1Type.Pierre]; // Get his death animation TRAnimation anim = model.Animations[12]; @@ -124,7 +124,7 @@ public static void AmendPierreDeath(TR1Level level) public static void AmendLarsonDeath(TR1Level level) { - TRModel model = level.Models.Find(m => m.ID == (uint)TR1Type.Larson); + TRModel model = level.Models[TR1Type.Larson]; // Get his death animation TRAnimation anim = model.Animations[15]; @@ -138,7 +138,7 @@ public static void AmendLarsonDeath(TR1Level level) public static void AmendSkaterBoyDeath(TR1Level level) { - TRModel model = level.Models.Find(m => m.ID == (uint)TR1Type.SkateboardKid); + TRModel model = level.Models[TR1Type.SkateboardKid]; // Get his death animation TRAnimation anim = model.Animations[13]; // Play the death sound on the 2nd frame (doesn't work on the 1st, which is OG). @@ -147,7 +147,7 @@ public static void AmendSkaterBoyDeath(TR1Level level) public static void AmendNatlaDeath(TR1Level level) { - TRModel model = level.Models.Find(m => m.ID == (uint)TR1Type.Natla); + TRModel model = level.Models[TR1Type.Natla]; // Get her death animation TRAnimation anim = model.Animations[13]; @@ -170,7 +170,7 @@ public static void AddMovingBlockSFX(TR1Level level) level.SoundEffects[TR1SFX.TrapdoorClose] = vilcabamba.SoundEffects[TR1SFX.TrapdoorClose]; } - TRModel model = level.Models.Find(m => m.ID == (uint)TR1Type.MovingBlock); + TRModel model = level.Models[TR1Type.MovingBlock]; for (int i = 2; i < 4; i++) { TRAnimation anim = model.Animations[i]; diff --git a/TRModelTransporter/Transport/TR1/TR1ModelImporter.cs b/TRModelTransporter/Transport/TR1/TR1ModelImporter.cs index 87bf63c0f..2e9dc518c 100644 --- a/TRModelTransporter/Transport/TR1/TR1ModelImporter.cs +++ b/TRModelTransporter/Transport/TR1/TR1ModelImporter.cs @@ -33,7 +33,7 @@ protected override AbstractTextureImportHandler GetExistingModelTypes() { - return Level.Models.Select(m => (TR1Type)m.ID).ToList(); + return Level.Models.Keys.ToList(); } protected override void Import(IEnumerable standardDefinitions, IEnumerable soundOnlyDefinitions) diff --git a/TRModelTransporter/Transport/TR2/TR2ModelImporter.cs b/TRModelTransporter/Transport/TR2/TR2ModelImporter.cs index d0d633fa3..22e51572e 100644 --- a/TRModelTransporter/Transport/TR2/TR2ModelImporter.cs +++ b/TRModelTransporter/Transport/TR2/TR2ModelImporter.cs @@ -21,7 +21,7 @@ protected override AbstractTextureImportHandler GetExistingModelTypes() { - return Level.Models.Select(m => (TR2Type)m.ID).ToList(); + return Level.Models.Keys.ToList(); } protected override void Import(IEnumerable standardDefinitions, IEnumerable soundOnlyDefinitions) diff --git a/TRModelTransporter/Transport/TR3/TR3ModelImporter.cs b/TRModelTransporter/Transport/TR3/TR3ModelImporter.cs index 40b03fa1d..f97b3ba38 100644 --- a/TRModelTransporter/Transport/TR3/TR3ModelImporter.cs +++ b/TRModelTransporter/Transport/TR3/TR3ModelImporter.cs @@ -22,7 +22,7 @@ protected override AbstractTextureImportHandler GetExistingModelTypes() { - return Level.Models.Select(m => (TR3Type)m.ID).ToList(); + return Level.Models.Keys.ToList(); } protected override void Import(IEnumerable standardDefinitions, IEnumerable soundOnlyDefinitions) diff --git a/TRRandomizerCore/Levels/TR1CombinedLevel.cs b/TRRandomizerCore/Levels/TR1CombinedLevel.cs index 267c0d502..1e3ab35be 100644 --- a/TRRandomizerCore/Levels/TR1CombinedLevel.cs +++ b/TRRandomizerCore/Levels/TR1CombinedLevel.cs @@ -73,9 +73,4 @@ public class TR1CombinedLevel /// Returns {Name}-Steam if IsSteamPyramid, otherwise just {Name}. /// public string JsonID => IsSteamPyramid ? Name + "-Steam" : Name; - - public void RemoveModel(TR1Type type) - { - Data.Models.RemoveAll(m => m.ID == (uint)type); - } } diff --git a/TRRandomizerCore/Levels/TR3CombinedLevel.cs b/TRRandomizerCore/Levels/TR3CombinedLevel.cs index 581114946..786e6ec2f 100644 --- a/TRRandomizerCore/Levels/TR3CombinedLevel.cs +++ b/TRRandomizerCore/Levels/TR3CombinedLevel.cs @@ -126,9 +126,4 @@ public TR3Adventure Adventure return TR3Adventure.India; } } - - public void RemoveModel(TR3Type type) - { - Data.Models.RemoveAll(m => m.ID == (uint)type); - } } diff --git a/TRRandomizerCore/Processors/TR2/TR2ModelAdjuster.cs b/TRRandomizerCore/Processors/TR2/TR2ModelAdjuster.cs index 4143a12f0..6afffefd5 100644 --- a/TRRandomizerCore/Processors/TR2/TR2ModelAdjuster.cs +++ b/TRRandomizerCore/Processors/TR2/TR2ModelAdjuster.cs @@ -46,11 +46,8 @@ private void AdjustInstanceModels() // of the old model, should also point to the new model type. foreach (TR2Type oldEntity in _modelRemap.Keys) { - TRModel model = _levelInstance.Data.Models.Find(m => m.ID == (short)oldEntity); - if (model != null) + if (_levelInstance.Data.Models.ChangeKey(oldEntity, _modelRemap[oldEntity])) { - model.ID = (uint)_modelRemap[oldEntity]; - List modelEntities = _levelInstance.Data.Entities.FindAll(e => e.TypeID == oldEntity); foreach (TR2Entity entity in modelEntities) { diff --git a/TRRandomizerCore/Processors/TR3/TR3SequenceProcessor.cs b/TRRandomizerCore/Processors/TR3/TR3SequenceProcessor.cs index 3318dba98..948403396 100644 --- a/TRRandomizerCore/Processors/TR3/TR3SequenceProcessor.cs +++ b/TRRandomizerCore/Processors/TR3/TR3SequenceProcessor.cs @@ -194,9 +194,8 @@ private void ImportUPV(TR3CombinedLevel level) // test here for the likes of Jungle. if (!Settings.RandomizeEnemies && level.Data.Entities.Any(e => e.TypeID == TR3Type.Monkey) - && level.Data.Models.Any(m => (TR3Type)m.ID == TR3Type.Tiger)) + && level.Data.Models.Remove(TR3Type.Tiger)) { - level.RemoveModel(TR3Type.Tiger); level.Data.Entities.Where(e => e.TypeID == TR3Type.Tiger) .ToList() .ForEach(e => e.TypeID = TR3Type.Monkey); @@ -249,7 +248,7 @@ private void ImportArtefactMenuModels(TR3CombinedLevel level) List imports = new(); foreach (TR3Type artefactMenuModel in TR3TypeUtilities.GetArtefactMenuModels()) { - if (level.Data.Models.Find(m => m.ID == (uint)artefactMenuModel) == null) + if (!level.Data.Models.ContainsKey(artefactMenuModel)) { imports.Add(artefactMenuModel); } @@ -275,10 +274,8 @@ private void AmendWillardBoss(TR3CombinedLevel level) foreach (TR3Type artefact in _artefactAssignment.Keys) { TR3Type replacement = _artefactAssignment[artefact]; - TRModel artefactModel = level.Data.Models.Find(m => m.ID == (uint)artefact); - TRModel replacementModel = artefactModel.Clone(); - replacementModel.ID = (uint)replacement; - level.Data.Models.Add(replacementModel); + TRModel artefactModel = level.Data.Models[artefact]; + level.Data.Models[replacement] = artefactModel.Clone(); level.Data.Entities .FindAll(e => e.TypeID == artefact) diff --git a/TRRandomizerCore/Randomizers/TR1/TR1EnemyRandomizer.cs b/TRRandomizerCore/Randomizers/TR1/TR1EnemyRandomizer.cs index 65e9f24d4..ed92cc27b 100644 --- a/TRRandomizerCore/Randomizers/TR1/TR1EnemyRandomizer.cs +++ b/TRRandomizerCore/Randomizers/TR1/TR1EnemyRandomizer.cs @@ -690,7 +690,7 @@ private void RandomizeEnemies(TR1CombinedLevel level, EnemyRandomizationCollecti if (Settings.AllowEmptyEggs) { // Add 1/4 chance of an empty egg, provided at least one spawn model is not available - IEnumerable allModels = level.Data.Models.Select(m => (TR1Type)m.ID); + List allModels = level.Data.Models.Keys.ToList(); // We can add Adam to make it possible for a dud spawn - he's not normally available for eggs because // of his own restrictions. @@ -835,7 +835,7 @@ private static int GetEntityCount(TR1CombinedLevel level, TR1Type entityType) else if (type == TR1Type.AdamEgg || type == TR1Type.AtlanteanEgg) { TR1Type eggType = TR1EnemyUtilities.CodeBitsToAtlantean(entity.CodeBits); - if (eggType == translatedType && level.Data.Models.Find(m => m.ID == (uint)eggType) != null) + if (eggType == translatedType && level.Data.Models.ContainsKey(eggType)) { count++; } @@ -866,24 +866,24 @@ private static bool IsEnemyInOrAboveWater(TR1Entity entity, TR1Level level, FDCo private static void AmendToQLarson(TR1CombinedLevel level) { - TRModel larsonModel = level.Data.Models.Find(m => m.ID == (uint)TR1Type.Larson); - if (larsonModel != null) - { - // Convert the Larson model into the Great Pyramid scion to allow ending the level. Larson will - // become a raptor to allow for normal randomization. Environment mods will handle the specifics here. - larsonModel.ID = (uint)TR1Type.ScionPiece3_S_P; - level.Data.Entities - .Where(e => e.TypeID == TR1Type.Larson) - .ToList() - .ForEach(e => e.TypeID = TR1Type.Raptor); - - // Make the scion invisible. - MeshEditor editor = new(); - foreach (TRMesh mesh in larsonModel.Meshes) - { - editor.Mesh = mesh; - editor.ClearAllPolygons(); - } + // Convert the Larson model into the Great Pyramid scion to allow ending the level. Larson will + // become a raptor to allow for normal randomization. Environment mods will handle the specifics here. + if (!level.Data.Models.ChangeKey(TR1Type.Larson, TR1Type.ScionPiece3_S_P)) + { + return; + } + + level.Data.Entities + .Where(e => e.TypeID == TR1Type.Larson) + .ToList() + .ForEach(e => e.TypeID = TR1Type.Raptor); + + // Make the scion invisible. + MeshEditor editor = new(); + foreach (TRMesh mesh in level.Data.Models[TR1Type.ScionPiece3_S_P].Meshes) + { + editor.Mesh = mesh; + editor.ClearAllPolygons(); } } @@ -891,7 +891,7 @@ private void AmendPyramidTorso(TR1CombinedLevel level) { // We want to keep Adam's egg, but simulate something else hatching. // In hard mode, two enemies take his place. - level.RemoveModel(TR1Type.Adam); + level.Data.Models.Remove(TR1Type.Adam); TR1Entity egg = level.Data.Entities.Find(e => e.TypeID == TR1Type.AdamEgg); TR1Entity lara = level.Data.Entities.Find(e => e.TypeID == TR1Type.Lara); @@ -937,13 +937,12 @@ private void AmendAtlanteanModels(TR1CombinedLevel level, EnemyRandomizationColl // If non-shooting grounded Atlanteans are present, we can just duplicate the model to make shooting Atlanteans if (enemies.Available.Any(TR1TypeUtilities.GetFamily(TR1Type.ShootingAtlantean_N).Contains)) { - TRModel shooter = level.Data.Models.Find(m => m.ID == (uint)TR1Type.ShootingAtlantean_N); - TRModel nonShooter = level.Data.Models.Find(m => m.ID == (uint)TR1Type.NonShootingAtlantean_N); + TRModel shooter = level.Data.Models[TR1Type.ShootingAtlantean_N]; + TRModel nonShooter = level.Data.Models[TR1Type.NonShootingAtlantean_N]; if (shooter == null && nonShooter != null) { shooter = nonShooter.Clone(); - shooter.ID = (uint)TR1Type.ShootingAtlantean_N; - level.Data.Models.Add(shooter); + level.Data.Models[TR1Type.ShootingAtlantean_N] = shooter; enemies.Available.Add(TR1Type.ShootingAtlantean_N); } } @@ -951,7 +950,7 @@ private void AmendAtlanteanModels(TR1CombinedLevel level, EnemyRandomizationColl // If we're using flying mummies, add a chance that they'll have proper wings if (enemies.Available.Contains(TR1Type.BandagedFlyer) && _generator.NextDouble() < 0.5) { - List meshes = level.Data.Models.Find(m => m.ID == (uint)TR1Type.FlyingAtlantean).Meshes; + List meshes = level.Data.Models[TR1Type.FlyingAtlantean].Meshes; ushort bandageTexture = meshes[1].TexturedRectangles[3].Texture; for (int i = 15; i < 21; i++) { @@ -1049,7 +1048,7 @@ private void AddUnarmedLevelAmmo(TR1CombinedLevel level) }; // Only include it if the model is present i.e. it's not an empty egg. - if (level.Data.Models.Find(m => (TR1Type)m.ID == resultantEnemy.TypeID) != null) + if (level.Data.Models.ContainsKey(resultantEnemy.TypeID)) { levelEnemies.Add(resultantEnemy); } @@ -1127,19 +1126,19 @@ private void RandomizeMeshes(TR1CombinedLevel level, List availableEnem [TR1Type.CassettePlayer_M_H] = 1 }; - List scion = level.Data.Models.Find(m => m.ID == (uint)TR1Type.ScionPiece4_S_P).Meshes; + List scion = level.Data.Models[TR1Type.ScionPiece4_S_P].Meshes; List replacementKeys = scionSwaps.Keys.ToList(); TR1Type replacement = replacementKeys[_generator.Next(0, replacementKeys.Count)]; - List replacementMeshes = level.Data.Models.Find(m => m.ID == (uint)replacement).Meshes; + List replacementMeshes = level.Data.Models[replacement].Meshes; int colRadius = scion[0].CollRadius; replacementMeshes[scionSwaps[replacement]].CopyInto(scion[0]); scion[0].CollRadius = colRadius; // Retain original as Lara may need to shoot it // Cutscene head swaps - List lara = level.Data.Models.Find(m => m.ID == (uint)TR1Type.CutsceneActor1).Meshes; - List natla = level.Data.Models.Find(m => m.ID == (uint)TR1Type.CutsceneActor3).Meshes; - List pierre = level.Data.Models.Find(m => m.ID == (uint)TR1Type.Pierre).Meshes; + List lara = level.CutSceneLevel.Data.Models[TR1Type.CutsceneActor1].Meshes; + List natla = level.CutSceneLevel.Data.Models[TR1Type.CutsceneActor3].Meshes; + List pierre = level.CutSceneLevel.Data.Models[TR1Type.Pierre].Meshes; switch (_generator.Next(0, 6)) { @@ -1174,24 +1173,24 @@ private void RandomizeMeshes(TR1CombinedLevel level, List availableEnem if (availableEnemies.Contains(TR1Type.Adam) && _generator.NextDouble() < 0.4) { // Replace Adam's head with a much larger version of Natla's, Larson's or normal/angry Lara's. - List adam = level.Data.Models.Find(m => m.ID == (uint)TR1Type.Adam).Meshes; + List adam = level.Data.Models[TR1Type.Adam].Meshes; TRMesh replacement; if (availableEnemies.Contains(TR1Type.Natla) && _generator.NextDouble() < 0.5) { - replacement = level.Data.Models.Find(m => m.ID == (uint)TR1Type.Natla).Meshes[2]; + replacement = level.Data.Models[TR1Type.Natla].Meshes[2]; } else if (availableEnemies.Contains(TR1Type.Larson) && _generator.NextDouble() < 0.5) { - replacement = level.Data.Models.Find(m => m.ID == (uint)TR1Type.Larson).Meshes[8]; + replacement = level.Data.Models[TR1Type.Larson].Meshes[8]; } else if (availableEnemies.Contains(TR1Type.Pierre) && _generator.NextDouble() < 0.5) { - replacement = level.Data.Models.Find(m => m.ID == (uint)TR1Type.Pierre).Meshes[8]; + replacement = level.Data.Models[TR1Type.Pierre].Meshes[8]; } else { TR1Type laraSwapType = _generator.NextDouble() < 0.5 ? TR1Type.LaraUziAnimation_H : TR1Type.Lara; - replacement = level.Data.Models.Find(m => m.ID == (uint)laraSwapType).Meshes[14]; + replacement = level.Data.Models[laraSwapType].Meshes[14]; } adam[3] = replacement.Clone(); @@ -1223,9 +1222,9 @@ private void RandomizeMeshes(TR1CombinedLevel level, List availableEnem if (availableEnemies.Contains(TR1Type.Pierre) && _generator.NextDouble() < 0.25) { // Replace Pierre's head with a slightly bigger version of Lara's (either angry Lara or normal Lara) - List pierre = level.Data.Models.Find(m => m.ID == (uint)TR1Type.Pierre).Meshes; - List lara = level.Data.Models.Find(m => m.ID == (uint)TR1Type.Lara).Meshes; - List laraUziAnim = level.Data.Models.Find(m => m.ID == (uint)TR1Type.LaraUziAnimation_H).Meshes; + List pierre = level.Data.Models[TR1Type.Pierre].Meshes; + List lara = level.Data.Models[TR1Type.Lara].Meshes; + List laraUziAnim = level.Data.Models[TR1Type.LaraUziAnimation_H].Meshes; pierre[8] = (_generator.NextDouble() < 0.5 ? laraUziAnim[14] : lara[14]).Clone(); foreach (TRVertex vertex in pierre[8].Vertices) @@ -1243,9 +1242,7 @@ private void FixEnemyAnimations(TR1CombinedLevel level) { // Model transport will handle these missing SFX by default, but we need to fix them in // the levels where these enemies already exist. - List entities = level.Data.Models.Select(m => (TR1Type)m.ID).ToList(); - - if (entities.Contains(TR1Type.Pierre) + if (level.Data.Models.ContainsKey(TR1Type.Pierre) && (level.Is(TR1LevelNames.FOLLY) || level.Is(TR1LevelNames.COLOSSEUM) || level.Is(TR1LevelNames.CISTERN) || level.Is(TR1LevelNames.TIHOCAN))) { TR1ModelExporter.AmendPierreGunshot(level.Data); @@ -1259,17 +1256,17 @@ private void FixEnemyAnimations(TR1CombinedLevel level) } } - if (entities.Contains(TR1Type.Larson) && level.Is(TR1LevelNames.SANCTUARY)) + if (level.Data.Models.ContainsKey(TR1Type.Larson) && level.Is(TR1LevelNames.SANCTUARY)) { TR1ModelExporter.AmendLarsonDeath(level.Data); } - if (entities.Contains(TR1Type.SkateboardKid) && level.Is(TR1LevelNames.MINES)) + if (level.Data.Models.ContainsKey(TR1Type.SkateboardKid) && level.Is(TR1LevelNames.MINES)) { TR1ModelExporter.AmendSkaterBoyDeath(level.Data); } - if (entities.Contains(TR1Type.Natla) && level.Is(TR1LevelNames.PYRAMID)) + if (level.Data.Models.ContainsKey(TR1Type.Natla) && level.Is(TR1LevelNames.PYRAMID)) { TR1ModelExporter.AmendNatlaDeath(level.Data); } @@ -1305,7 +1302,7 @@ private void CloneEnemies(TR1CombinedLevel level) TR1Entity adamEgg = level.Data.Entities.Find(e => e.TypeID == TR1Type.AdamEgg); if (adamEgg != null && TR1EnemyUtilities.CodeBitsToAtlantean(adamEgg.CodeBits) == TR1Type.Adam - && level.Data.Models.Find(m => m.ID == (uint)TR1Type.Adam) != null) + && level.Data.Models.ContainsKey(TR1Type.Adam)) { enemies.Add(adamEgg); } diff --git a/TRRandomizerCore/Randomizers/TR1/TR1OutfitRandomizer.cs b/TRRandomizerCore/Randomizers/TR1/TR1OutfitRandomizer.cs index d889483a0..9e9379f98 100644 --- a/TRRandomizerCore/Randomizers/TR1/TR1OutfitRandomizer.cs +++ b/TRRandomizerCore/Randomizers/TR1/TR1OutfitRandomizer.cs @@ -324,7 +324,7 @@ private void ImportBraid(TR1CombinedLevel level) } // Find the texture references for the plain parts of imported hair - List ponytailMeshes = level.Data.Models.Find(m => m.ID == (uint)TR1Type.LaraPonytail_H_U).Meshes; + List ponytailMeshes = level.Data.Models[TR1Type.LaraPonytail_H_U].Meshes; ushort plainHairQuad = ponytailMeshes[0].TexturedRectangles[0].Texture; ushort plainHairTri = ponytailMeshes[5].TexturedTriangles[0].Texture; @@ -340,7 +340,7 @@ private void ImportBraid(TR1CombinedLevel level) foreach (TR1Type laraType in headAmendments.Keys) { - List meshes = level.Data.Models.Find(m => m.ID == (uint)laraType)?.Meshes; + List meshes = level.Data.Models[laraType]?.Meshes; if (meshes == null || meshes.Count < 15) { continue; @@ -373,11 +373,11 @@ private void ImportBraid(TR1CombinedLevel level) private static void CreateGoldenBraid(TR1CombinedLevel level) { - TRModel model = level.Data.Models.Find(m => m.ID == (uint)TR1Type.LaraMiscAnim_H); + TRModel model = level.Data.Models[TR1Type.LaraMiscAnim_H]; TRMesh goldenHips = model.Meshes[0]; ushort goldPalette = goldenHips.ColouredRectangles[0].Texture; - TRModel ponytail = level.Data.Models.Find(m => m.ID == (uint)TR1Type.LaraPonytail_H_U); + TRModel ponytail = level.Data.Models[TR1Type.LaraPonytail_H_U]; ponytail.Meshes.AddRange(ponytail.Meshes.Select(m => MeshEditor.CloneMeshAsColoured(m, goldPalette))); ponytail.MeshTrees.AddRange(ponytail.MeshTrees); } @@ -387,7 +387,7 @@ private static void HideEntities(TR1CombinedLevel level, IEnumerable en MeshEditor editor = new(); foreach (TR1Type ent in entities) { - List meshes = level.Data.Models.Find(m => m.ID == (uint)ent)?.Meshes; + List meshes = level.Data.Models[ent]?.Meshes; if (meshes != null) { foreach (TRMesh mesh in meshes) @@ -438,7 +438,7 @@ private void AmendBackpack(TR1CombinedLevel level) // Make the backpack shallower so the braid doesn't smash into it foreach (TR1Type ent in laraEntities) { - TRMesh mesh = level.Data.Models.Find(m => m.ID == (uint)ent).Meshes[7]; + TRMesh mesh = level.Data.Models[ent].Meshes[7]; for (int i = 26; i < 30; i++) { mesh.Vertices[i].Z += 12; @@ -469,7 +469,7 @@ private bool CutsceneSupportsBraid(TR1CombinedLevel parentLevel) { // Lara's head may be Natla's or Pierre's, so only support the braid if // the mesh is the original. - TRMesh larasHead = parentLevel.CutSceneLevel.Data.Models.Find(m => m.ID == (uint)TR1Type.CutsceneActor1).Meshes[14]; + TRMesh larasHead = parentLevel.CutSceneLevel.Data.Models[TR1Type.CutsceneActor1].Meshes[14]; return larasHead.CollRadius == 68; } @@ -483,12 +483,12 @@ private void ConvertToGymOutfit(TR1CombinedLevel level) return; } - List lara = level.Data.Models.Find(m => m.ID == (uint)(level.IsCutScene ? TR1Type.CutsceneActor1 : TR1Type.Lara)).Meshes; - List laraPistol = level.Data.Models.Find(m => m.ID == (uint)TR1Type.LaraPistolAnim_H).Meshes; - List laraShotgun = level.Data.Models.Find(m => m.ID == (uint)TR1Type.LaraShotgunAnim_H).Meshes; - List laraMagnums = level.Data.Models.Find(m => m.ID == (uint)TR1Type.LaraMagnumAnim_H).Meshes; - List laraUzis = level.Data.Models.Find(m => m.ID == (uint)TR1Type.LaraUziAnimation_H).Meshes; - List laraMisc = level.Data.Models.Find(m => m.ID == (uint)TR1Type.LaraMiscAnim_H).Meshes; + List lara = level.Data.Models[(level.IsCutScene ? TR1Type.CutsceneActor1 : TR1Type.Lara)].Meshes; + List laraPistol = level.Data.Models[TR1Type.LaraPistolAnim_H].Meshes; + List laraShotgun = level.Data.Models[TR1Type.LaraShotgunAnim_H].Meshes; + List laraMagnums = level.Data.Models[TR1Type.LaraMagnumAnim_H].Meshes; + List laraUzis = level.Data.Models[TR1Type.LaraUziAnimation_H].Meshes; + List laraMisc = level.Data.Models[TR1Type.LaraMiscAnim_H].Meshes; // Basic meshes to take from LaraMiscAnim. We don't replace Lara's gloves // or thighs (at this stage - handled below with gun swaps). @@ -587,9 +587,9 @@ private void ConvertToPartialGymOutfit(TR1CombinedLevel level) return; } - List lara = level.Data.Models.Find(m => m.ID == (uint)(level.IsCutScene ? TR1Type.CutsceneActor1 : TR1Type.Lara)).Meshes; - List laraShotgun = level.Data.Models.Find(m => m.ID == (uint)TR1Type.LaraShotgunAnim_H).Meshes; - List laraMisc = level.Data.Models.Find(m => m.ID == (uint)TR1Type.LaraMiscAnim_H).Meshes; + List lara = level.Data.Models[(level.IsCutScene ? TR1Type.CutsceneActor1 : TR1Type.Lara)].Meshes; + List laraShotgun = level.Data.Models[TR1Type.LaraShotgunAnim_H].Meshes; + List laraMisc = level.Data.Models[TR1Type.LaraMiscAnim_H].Meshes; // Just the torso laraMisc[7].CopyInto(lara[7]); @@ -686,7 +686,7 @@ private bool ImportGymOutfit(TR1CombinedLevel level) return false; } - TRModel existingModel = level.Data.Models.Find(m => m.ID == (uint)TR1Type.LaraMiscAnim_H); + TRModel existingModel = level.Data.Models[TR1Type.LaraMiscAnim_H]; if (existingModel != null) { // If we already have the gym outfit available, we're done. @@ -718,7 +718,7 @@ private bool ImportGymOutfit(TR1CombinedLevel level) // e.g. for Adam death animation and scion pickups. if (existingModel != null) { - TRModel newModel = level.Data.Models.Find(m => m.ID == (uint)TR1Type.LaraMiscAnim_H); + TRModel newModel = level.Data.Models[TR1Type.LaraMiscAnim_H]; newModel.Animations = existingModel.Animations; } @@ -804,9 +804,9 @@ private static void CopyMeshParts(MeshCopyData data) private void ConvertToMauledOutfit(TR1CombinedLevel level) { - List lara = level.Data.Models.Find(m => m.ID == (uint)(level.IsCutScene ? TR1Type.CutsceneActor1 : TR1Type.Lara)).Meshes; - List laraShotgun = level.Data.Models.Find(m => m.ID == (uint)TR1Type.LaraShotgunAnim_H).Meshes; - List laraMisc = level.Data.Models.Find(m => m.ID == (uint)TR1Type.LaraMiscAnim_H).Meshes; + List lara = level.Data.Models[(level.IsCutScene ? TR1Type.CutsceneActor1 : TR1Type.Lara)].Meshes; + List laraShotgun = level.Data.Models[TR1Type.LaraShotgunAnim_H].Meshes; + List laraMisc = level.Data.Models[TR1Type.LaraMiscAnim_H].Meshes; if (level.Is(TR1LevelNames.QUALOPEC_CUT)) { @@ -862,7 +862,7 @@ private void ConvertToMauledOutfit(TR1CombinedLevel level) }; foreach (TR1Type gunAnimType in gunAnims) { - List meshes = level.Data.Models.Find(m => m.ID == (uint)gunAnimType)?.Meshes; + List meshes = level.Data.Models[gunAnimType]?.Meshes; if (meshes == null) continue; diff --git a/TRRandomizerCore/Randomizers/TR1/TR1SecretRandomizer.cs b/TRRandomizerCore/Randomizers/TR1/TR1SecretRandomizer.cs index 5226d335d..253bdb8c6 100644 --- a/TRRandomizerCore/Randomizers/TR1/TR1SecretRandomizer.cs +++ b/TRRandomizerCore/Randomizers/TR1/TR1SecretRandomizer.cs @@ -506,17 +506,17 @@ private static void SetPuzzleTypeName(TR1CombinedLevel level, TR1Type itemType, { if (TR1TypeUtilities.IsKeyType(itemType)) { - PopulateScriptStrings(itemType - TR1Type.Key1_S_P, level.Script.Keys, "K"); + PopulateScriptStrings((int)(itemType - TR1Type.Key1_S_P), level.Script.Keys, "K"); level.Script.Keys.Add(name); } else if (TR1TypeUtilities.IsPuzzleType(itemType)) { - PopulateScriptStrings(itemType - TR1Type.Puzzle1_S_P, level.Script.Puzzles, "P"); + PopulateScriptStrings((int)(itemType - TR1Type.Puzzle1_S_P), level.Script.Puzzles, "P"); level.Script.Puzzles.Add(name); } else if (TR1TypeUtilities.IsQuestType(itemType)) { - PopulateScriptStrings(itemType - TR1Type.Quest1_S_P, level.Script.Pickups, "Q"); + PopulateScriptStrings((int)(itemType - TR1Type.Quest1_S_P), level.Script.Pickups, "Q"); level.Script.Pickups.Add(name); } } @@ -835,7 +835,7 @@ protected override void StartImpl() // We exclude current puzzle/key items from the available switching pool. foreach (TR1Type puzzleType in _modelReplacements.Keys) { - if (level.Data.Models.Find(m => m.ID == (uint)puzzleType) == null) + if (!level.Data.Models.ContainsKey(puzzleType)) { allocation.AvailablePickupModels.Add(puzzleType); } @@ -875,15 +875,14 @@ protected override void ProcessImpl() TR1Type puzzleModelType = allocation.AvailablePickupModels.First(); TR1Type puzzlePickupType = _modelReplacements[puzzleModelType]; - TRModel puzzleModel = level.Data.Models.Find(m => m.ID == (uint)secretModelType); - puzzleModel.ID = (uint)puzzleModelType; + level.Data.Models.ChangeKey(secretModelType, puzzleModelType); level.Data.SpriteSequences.Find(s => s.SpriteID == (int)secretPickupType).SpriteID = (int)puzzlePickupType; if (secretModelType == TR1Type.SecretScion_M_H && _outer.Are3DPickupsEnabled()) { // TR1X embeds scions into the ground when they are puzzle/key types in 3D mode, // so we counteract that here to avoid uncollectable items. - TRMesh scionMesh = puzzleModel.Meshes[0]; + TRMesh scionMesh = level.Data.Models[puzzleModelType].Meshes[0]; foreach (TRVertex vertex in scionMesh.Vertices) { vertex.Y -= 90; diff --git a/TRRandomizerCore/Randomizers/TR1/TR1TextureRandomizer.cs b/TRRandomizerCore/Randomizers/TR1/TR1TextureRandomizer.cs index 78e4bce5f..bceedafce 100644 --- a/TRRandomizerCore/Randomizers/TR1/TR1TextureRandomizer.cs +++ b/TRRandomizerCore/Randomizers/TR1/TR1TextureRandomizer.cs @@ -17,7 +17,7 @@ public class TR1TextureRandomizer : BaseTR1Randomizer, ITextureVariantHandler private static readonly Color[] _wireframeColours = ColorUtilities.GetWireframeColours(); private readonly Dictionary _persistentVariants; - private readonly Dictionary _wireframeData; + private readonly Dictionary> _wireframeData; private readonly object _drawLock; private TR1TextureDatabase _textureDatabase; private Dictionary _textureOptions; @@ -31,7 +31,7 @@ public class TR1TextureRandomizer : BaseTR1Randomizer, ITextureVariantHandler public TR1TextureRandomizer() { _persistentVariants = new Dictionary(); - _wireframeData = JsonConvert.DeserializeObject>(ReadResource(@"TR1\Textures\wireframing.json")); + _wireframeData = JsonConvert.DeserializeObject>>(ReadResource(@"TR1\Textures\wireframing.json")); _drawLock = new object(); } @@ -172,14 +172,14 @@ private void ChooseWireframeLevels() } bool has3DPickups = (ScriptEditor as TR1ScriptEditor).Enable3dPickups; - foreach (WireframeData data in _wireframeData.Values.ToList()) + foreach (WireframeData data in _wireframeData.Values) { data.Has3DPickups = has3DPickups; data.HighlightTriggers = data.HighlightDeathTiles = Settings.ShowWireframeTriggers; data.SolidInteractables = Settings.UseSolidInteractableWireframing; foreach (SpecialTextureHandling special in data.SpecialTextures) { - List modes = WireframeData.GetDrawModes(special.Type); + List modes = WireframeData.GetDrawModes(special.Type); special.Mode = modes[_generator.Next(0, modes.Count)]; } } @@ -255,7 +255,7 @@ private bool IsSolidLaraLevel(TR1CombinedLevel lvl) (_solidLaraLevels.Contains(lvl.Script) || (lvl.IsCutScene && _solidLaraLevels.Contains(lvl.ParentLevel.Script))); } - private WireframeData GetWireframeData(TR1CombinedLevel lvl) + private WireframeData GetWireframeData(TR1CombinedLevel lvl) { return IsWireframeLevel(lvl) ? _wireframeData[lvl.Name] : null; } @@ -365,11 +365,11 @@ protected override void StartImpl() if (_outer.IsWireframeLevel(level)) { - WireframeData data = _outer.GetWireframeData(level); + WireframeData data = _outer.GetWireframeData(level); data.SolidEnemies = _outer.Settings.UseSolidEnemyWireframing; if (level.IsCutScene) { - WireframeData parentData = _outer.GetWireframeData(level.ParentLevel); + WireframeData parentData = _outer.GetWireframeData(level.ParentLevel); data.HighlightColour = parentData.HighlightColour; data.SolidLara = parentData.SolidLara; } @@ -401,9 +401,9 @@ protected override void StartImpl() if (_outer.Settings.UseDifferentWireframeColours) { - foreach (TRModel model in level.Data.Models) + foreach (TR1Type type in level.Data.Models.Keys) { - data.ModelColours[model.ID] = _outer.GetWireframeVariant(); + data.ModelColours[type] = _outer.GetWireframeVariant(); } } } diff --git a/TRRandomizerCore/Randomizers/TR2/TR2EnemyRandomizer.cs b/TRRandomizerCore/Randomizers/TR2/TR2EnemyRandomizer.cs index af04fec14..14354b6ce 100644 --- a/TRRandomizerCore/Randomizers/TR2/TR2EnemyRandomizer.cs +++ b/TRRandomizerCore/Randomizers/TR2/TR2EnemyRandomizer.cs @@ -487,26 +487,17 @@ private void RandomizeEnemiesNatively(TR2CombinedLevel level) }); } - private static void DisguiseEntity(TR2CombinedLevel level, TR2Type guiser, TR2Type targetEntity) + private static void DisguiseEntity(TR2CombinedLevel level, TR2Type guiser, TR2Type targetType) { - int existingIndex = level.Data.Models.FindIndex(m => m.ID == (short)guiser); - if (existingIndex != -1) - { - level.Data.Models.RemoveAt(existingIndex); - } - - TRModel disguiseAsModel = level.Data.Models.Find(m => m.ID == (short)targetEntity); - if (targetEntity == TR2Type.BirdMonster && level.Is(TR2LevelNames.CHICKEN)) + if (targetType == TR2Type.BirdMonster && level.Is(TR2LevelNames.CHICKEN)) { // We have to keep the original model for the boss, so in // this instance we just clone the model for the guiser - TRModel guiserModel = disguiseAsModel.Clone(); - guiserModel.ID = (uint)guiser; - level.Data.Models.Add(guiserModel); + level.Data.Models[guiser] = level.Data.Models[targetType].Clone(); } else { - disguiseAsModel.ID = (uint)guiser; + level.Data.Models.ChangeKey(targetType, guiser); } } @@ -926,10 +917,10 @@ private void RandomizeEnemyMeshes(TR2CombinedLevel level, EnemyRandomizationColl if (laraClones.Count > 0) { - TRModel laraModel = level.Data.Models.Find(m => m.ID == (uint)TR2Type.Lara); + TRModel laraModel = level.Data.Models[TR2Type.Lara]; foreach (TR2Type enemyType in laraClones) { - TRModel enemyModel = level.Data.Models.Find(m => m.ID == (uint)enemyType); + TRModel enemyModel = level.Data.Models[enemyType]; enemyModel.MeshTrees = laraModel.MeshTrees; enemyModel.Meshes = laraModel.Meshes; } @@ -943,8 +934,8 @@ private void RandomizeEnemyMeshes(TR2CombinedLevel level, EnemyRandomizationColl && _generator.Next(0, chance) == 0) { // Make Marco look and behave like Winston, until Lara gets too close - TRModel marcoModel = level.Data.Models.Find(m => m.ID == (uint)TR2Type.MarcoBartoli); - TRModel winnieModel = level.Data.Models.Find(m => m.ID == (uint)TR2Type.Winston); + TRModel marcoModel = level.Data.Models[TR2Type.MarcoBartoli]; + TRModel winnieModel = level.Data.Models[TR2Type.Winston]; marcoModel.Animations = winnieModel.Animations; marcoModel.MeshTrees = winnieModel.MeshTrees; marcoModel.Meshes = winnieModel.Meshes; @@ -964,7 +955,7 @@ private static void MakeChickensUnconditional(TR2CombinedLevel level) // #327 Trick the game into never reaching the final frame of the death animation. // This results in a very abrupt death but avoids the level ending. For Ice Palace, // environment modifications will be made to enforce an alternative ending. - TRModel model = level.Data.Models.Find(m => m.ID == (uint)TR2Type.BirdMonster); + TRModel model = level.Data.Models[TR2Type.BirdMonster]; if (model != null) { model.Animations[20].FrameEnd = model.Animations[19].FrameEnd; diff --git a/TRRandomizerCore/Randomizers/TR2/TR2OutfitRandomizer.cs b/TRRandomizerCore/Randomizers/TR2/TR2OutfitRandomizer.cs index 56d175894..72b058c78 100644 --- a/TRRandomizerCore/Randomizers/TR2/TR2OutfitRandomizer.cs +++ b/TRRandomizerCore/Randomizers/TR2/TR2OutfitRandomizer.cs @@ -229,8 +229,10 @@ protected override void ProcessImpl() private bool Import(TR2CombinedLevel level, TR2Type lara) { - TRModel laraModel = level.Data.Models.Find(m => m.ID == (uint)TR2Type.Lara); - List laraClones = level.Data.Models.FindAll(m => m.MeshTrees.FirstOrDefault() == laraModel.MeshTrees.FirstOrDefault() && m != laraModel); + TRModel laraModel = level.Data.Models[TR2Type.Lara]; + List laraClones = level.Data.Models.Values + .Where(m => m.MeshTrees.FirstOrDefault() == laraModel.MeshTrees.FirstOrDefault() && m != laraModel) + .ToList(); if (lara == TR2Type.LaraInvisible) { @@ -275,7 +277,7 @@ private bool Import(TR2CombinedLevel level, TR2Type lara) // #314 Any clones of Lara should copy her new style if (laraClones.Count > 0) { - laraModel = level.Data.Models.Find(m => m.ID == (uint)TR2Type.Lara); + laraModel = level.Data.Models[TR2Type.Lara]; foreach (TRModel model in laraClones) { model.MeshTrees = laraModel.MeshTrees; @@ -318,7 +320,7 @@ private static void HideEntities(TR2CombinedLevel level, IEnumerable en MeshEditor editor = new(); foreach (TR2Type ent in entities) { - List meshes = level.Data.Models.Find(m => m.ID == (uint)ent)?.Meshes; + List meshes = level.Data.Models[ent]?.Meshes; if (meshes != null) { foreach (TRMesh mesh in meshes) @@ -338,13 +340,13 @@ private static void HideEntities(TR2CombinedLevel level, IEnumerable en private void AdjustOutfit(TR2CombinedLevel level, TR2Type lara) { - TRModel laraModel = level.Data.Models.Find(m => m.ID == (uint)TR2Type.Lara); + TRModel laraModel = level.Data.Models[TR2Type.Lara]; if (level.Is(TR2LevelNames.HOME) && lara != TR2Type.LaraHome) { // This ensures that Lara's hips match the new outfit for the starting animation and shower cutscene, // otherwise the dressing gown hips are rendered, but the mesh is completely different for this, plus // its textures will have been removed. - TRModel laraMiscModel = level.Data.Models.Find(m => m.ID == (uint)TR2Type.LaraMiscAnim_H); + TRModel laraMiscModel = level.Data.Models[TR2Type.LaraMiscAnim_H]; laraModel.Meshes[0].CopyInto(laraMiscModel.Meshes[0]); } @@ -377,14 +379,14 @@ private void AdjustOutfit(TR2CombinedLevel level, TR2Type lara) // so we basically just retain the hand. MeshEditor editor = new() { - Mesh = level.Data.Models.Find(m => m.ID == (uint)TR2Type.LaraMiscAnim_H).Meshes[10] + Mesh = level.Data.Models[TR2Type.LaraMiscAnim_H].Meshes[10] }; editor.RemoveTexturedRectangleRange(6, 20); editor.ClearTexturedTriangles(); // And hide it from the inventory - foreach (TRMesh mesh in level.Data.Models.Find(m => m.ID == (uint)TR2Type.Puzzle1_M_H).Meshes) + foreach (TRMesh mesh in level.Data.Models[TR2Type.Puzzle1_M_H].Meshes) { editor.Mesh = mesh; editor.ClearTexturedRectangles(); @@ -400,8 +402,8 @@ private void AdjustOutfit(TR2CombinedLevel level, TR2Type lara) // into the diving suit, but model ID 99 is the one before. We always want the cutscene actor to // match DA, but this unfortunately means she'll leave the cutscene in the same outfit. She just // didn't like the look of any of the alternatives... - TRModel actorLara = level.Data.Models.Find(m => m.ID == (short)TR2Type.CutsceneActor3); - TRModel realLara = level.Data.Models.Find(m => m.ID == (short)TR2Type.Lara); + TRModel actorLara = level.Data.Models[TR2Type.CutsceneActor3]; + TRModel realLara = level.Data.Models[TR2Type.Lara]; actorLara.MeshTrees = realLara.MeshTrees; actorLara.Meshes = realLara.Meshes; diff --git a/TRRandomizerCore/Randomizers/TR2/TR2TextureRandomizer.cs b/TRRandomizerCore/Randomizers/TR2/TR2TextureRandomizer.cs index 9d76e5c70..a71217a30 100644 --- a/TRRandomizerCore/Randomizers/TR2/TR2TextureRandomizer.cs +++ b/TRRandomizerCore/Randomizers/TR2/TR2TextureRandomizer.cs @@ -17,7 +17,7 @@ public class TR2TextureRandomizer : BaseTR2Randomizer, ITextureVariantHandler private static readonly Color[] _wireframeColours = ColorUtilities.GetWireframeColours(); private readonly Dictionary _persistentVariants; - private readonly Dictionary _wireframeData; + private readonly Dictionary> _wireframeData; private readonly object _drawLock; private TR2TextureDatabase _textureDatabase; private Dictionary _textureOptions; @@ -31,7 +31,7 @@ public class TR2TextureRandomizer : BaseTR2Randomizer, ITextureVariantHandler public TR2TextureRandomizer() { _persistentVariants = new Dictionary(); - _wireframeData = JsonConvert.DeserializeObject>(ReadResource(@"TR2\Textures\wireframing.json")); + _wireframeData = JsonConvert.DeserializeObject>>(ReadResource(@"TR2\Textures\wireframing.json")); _drawLock = new object(); } @@ -171,7 +171,7 @@ private void ChooseWireframeLevels() _persistentWireColour = _wireframeColours[_generator.Next(0, _wireframeColours.Length)]; } - foreach (WireframeData data in _wireframeData.Values.ToList()) + foreach (WireframeData data in _wireframeData.Values) { data.HighlightLadders = Settings.UseWireframeLadders; data.HighlightTriggers = data.HighlightDeathTiles = Settings.ShowWireframeTriggers; @@ -248,7 +248,7 @@ private bool IsSolidLaraLevel(TR2CombinedLevel lvl) (_solidLaraLevels.Contains(lvl.Script) || (lvl.IsCutScene && _solidLaraLevels.Contains(lvl.ParentLevel.Script))); } - private WireframeData GetWireframeData(TR2CombinedLevel lvl) + private WireframeData GetWireframeData(TR2CombinedLevel lvl) { return IsWireframeLevel(lvl) ? _wireframeData[lvl.JsonID] : null; } @@ -314,11 +314,11 @@ protected override void StartImpl() if (_outer.IsWireframeLevel(level)) { - WireframeData data = _outer.GetWireframeData(level); + WireframeData data = _outer.GetWireframeData(level); data.SolidEnemies = _outer.Settings.UseSolidEnemyWireframing; if (level.IsCutScene) { - WireframeData parentData = _outer.GetWireframeData(level.ParentLevel); + WireframeData parentData = _outer.GetWireframeData(level.ParentLevel); data.HighlightColour = parentData.HighlightColour; data.SolidLara = parentData.SolidLara; } @@ -350,15 +350,15 @@ protected override void StartImpl() if (_outer.Settings.UseDifferentWireframeColours) { - foreach (TRModel model in level.Data.Models) + foreach (TR2Type type in level.Data.Models.Keys) { - data.ModelColours[model.ID] = _outer.GetWireframeVariant(); + data.ModelColours[type] = _outer.GetWireframeVariant(); } // Make sure the front and back of the dragon match - if (data.ModelColours.ContainsKey((uint)TR2Type.DragonFront_H)) + if (data.ModelColours.ContainsKey(TR2Type.DragonFront_H)) { - data.ModelColours[(uint)TR2Type.DragonBack_H] = data.ModelColours[(uint)TR2Type.DragonFront_H]; + data.ModelColours[TR2Type.DragonBack_H] = data.ModelColours[TR2Type.DragonFront_H]; } } } diff --git a/TRRandomizerCore/Randomizers/TR3/TR3EnemyRandomizer.cs b/TRRandomizerCore/Randomizers/TR3/TR3EnemyRandomizer.cs index 567f2139f..3cb504891 100644 --- a/TRRandomizerCore/Randomizers/TR3/TR3EnemyRandomizer.cs +++ b/TRRandomizerCore/Randomizers/TR3/TR3EnemyRandomizer.cs @@ -360,7 +360,7 @@ private void RandomizeEnemiesNatively(TR3CombinedLevel level) { TR3Type banishedType = _generator.NextDouble() < 0.5 ? TR3Type.Tiger : TR3Type.Monkey; availableEnemyTypes.Remove(banishedType); - level.RemoveModel(banishedType); + level.Data.Models.Remove(banishedType); } List droppableEnemies = TR3TypeUtilities.FilterDroppableEnemies(availableEnemyTypes, Settings.ProtectMonks); @@ -747,10 +747,9 @@ protected override void ProcessImpl() // Remove stale tiger model if present to avoid friendly monkeys causing vehicle crashes. if (level.HasVehicle - && enemies.EntitiesToImport.Contains(TR3Type.Monkey) - && level.Data.Models.Any(m => m.ID == (uint)TR3Type.Tiger)) + && enemies.EntitiesToImport.Contains(TR3Type.Monkey)) { - level.RemoveModel(TR3Type.Tiger); + level.Data.Models.Remove(TR3Type.Tiger); } } diff --git a/TRRandomizerCore/Randomizers/TR3/TR3OutfitRandomizer.cs b/TRRandomizerCore/Randomizers/TR3/TR3OutfitRandomizer.cs index 8421444c7..1f2ac013e 100644 --- a/TRRandomizerCore/Randomizers/TR3/TR3OutfitRandomizer.cs +++ b/TRRandomizerCore/Randomizers/TR3/TR3OutfitRandomizer.cs @@ -271,7 +271,7 @@ private static void HideEntities(TR3CombinedLevel level, IEnumerable en MeshEditor editor = new(); foreach (TR3Type ent in entities) { - List meshes = level.Data.Models.Find(m => m.ID == (uint)ent)?.Meshes; + List meshes = level.Data.Models[ent]?.Meshes; if (meshes != null) { foreach (TRMesh mesh in meshes) diff --git a/TRRandomizerCore/Randomizers/TR3/TR3SecretRandomizer.cs b/TRRandomizerCore/Randomizers/TR3/TR3SecretRandomizer.cs index 9b9eec4e0..81bbd5df1 100644 --- a/TRRandomizerCore/Randomizers/TR3/TR3SecretRandomizer.cs +++ b/TRRandomizerCore/Randomizers/TR3/TR3SecretRandomizer.cs @@ -452,7 +452,7 @@ private void AddDamageControl(TR3CombinedLevel level, List pickupTypes, foreach (TR3Type pickupType in artefacts.Keys) { TR3Type menuType = artefacts[pickupType]; - if (level.Data.Models.Find(m => m.ID == (uint)menuType) == null) + if (!level.Data.Models.ContainsKey(menuType)) { availablePickupType = pickupType; availableMenuType = menuType; @@ -464,10 +464,8 @@ private void AddDamageControl(TR3CombinedLevel level, List pickupTypes, { // We have a free slot, so duplicate a model TR3Type baseArtefact = pickupTypes[_generator.Next(0, pickupTypes.Count)]; - TRModel artefactMenuModel = level.Data.Models.Find(m => m.ID == (uint)artefacts[baseArtefact]); - TRModel availableModel = artefactMenuModel.Clone(); - availableModel.ID = (uint)availableMenuType; - level.Data.Models.Add(availableModel); + TRModel artefactMenuModel = level.Data.Models[artefacts[baseArtefact]]; + level.Data.Models[availableMenuType] = artefactMenuModel.Clone(); // Add a script name - pull from GamestringRando once translations completed SetPuzzleTypeName(level, availablePickupType, "Infinite Medi Packs"); @@ -486,15 +484,15 @@ private static void SetPuzzleTypeName(TR3CombinedLevel level, TR3Type itemType, { if (TR3TypeUtilities.IsKeyType(itemType)) { - level.Script.Keys[itemType - TR3Type.Key1_P] = name; + level.Script.Keys[(int)(itemType - TR3Type.Key1_P)] = name; } else if (TR3TypeUtilities.IsPuzzleType(itemType)) { - level.Script.Puzzles[itemType - TR3Type.Puzzle1_P] = name; + level.Script.Puzzles[(int)(itemType - TR3Type.Puzzle1_P)] = name; } else if (TR3TypeUtilities.IsQuestType(itemType)) { - level.Script.Pickups[itemType - TR3Type.Quest1_P] = name; + level.Script.Pickups[(int)(itemType - TR3Type.Quest1_P)] = name; } } @@ -761,32 +759,20 @@ protected override void StartImpl() // Special case for Crash Site, which is the only level that uses Quest1 (the swamp map). // We want to reallocate this as a key to allow us to reuse Quest1 on import. Amend the // models to become Key3 and update the script to match. - level.Data.Models.Find(m => m.ID == (uint)TR3Type.Quest1_P).ID = (uint)TR3Type.Key3_P; - level.Data.Models.Find(m => m.ID == (uint)TR3Type.Quest1_M_H).ID = (uint)TR3Type.Key3_M_H; + level.Data.Models.ChangeKey(TR3Type.Quest1_P, TR3Type.Key3_P); + level.Data.Models.ChangeKey(TR3Type.Quest1_M_H, TR3Type.Key3_M_H); level.Script.Keys[2] = level.Script.Pickups[0]; - level.Script.SetStartInventoryItems(new Dictionary + level.Script.SetStartInventoryItems(new() { [TR3Items.Key3] = 1 }); } - foreach (TR3Type puzzleType in _artefactReplacements.Keys) - { - if (level.Data.Models.Find(m => m.ID == (uint)puzzleType) == null) - { - allocation.AvailablePickupModels.Add(puzzleType); - } - } + allocation.AvailablePickupModels.AddRange(_artefactReplacements.Keys + .Where(a => !level.Data.Models.ContainsKey(a))); List artefactTypes = _artefactPickups.Keys.ToList(); - for (int i = artefactTypes.Count - 1; i >= 0; i--) - { - TR3Type artefactType = artefactTypes[i]; - if (level.Data.Models.Find(m => m.ID == (uint)artefactType) != null) - { - artefactTypes.RemoveAt(i); - } - } + artefactTypes.RemoveAll(a => level.Data.Models.ContainsKey(a)); // How many models do we actually need? int modelImportCount = Math.Min(_outer.Settings.DevelopmentMode ? _devModeSecretCount : level.Script.NumSecrets, allocation.AvailablePickupModels.Count); @@ -827,15 +813,12 @@ protected override void ProcessImpl() TR3Type puzzlePickupType = allocation.AvailablePickupModels.First(); TR3Type puzzleMenuType = _artefactReplacements[puzzlePickupType]; - level.Data.Models.Find(m => m.ID == (uint)artefactPickupType).ID = (uint)puzzlePickupType; + level.Data.Models.ChangeKey(artefactPickupType, puzzlePickupType); // #277 Most levels (beyond India) have the artefacts as menu models so we need // to duplicate the models instead of replacing them, otherwise the carried-over // artefacts from previous levels are invisible. - TRModel menuModel = level.Data.Models.Find(m => m.ID == (uint)artefactMenuType); - TRModel newModel = menuModel.Clone(); - newModel.ID = (uint)puzzleMenuType; - level.Data.Models.Add(newModel); + level.Data.Models[puzzleMenuType] = level.Data.Models[artefactMenuType].Clone(); // Remove this puzzle type from the available pool allocation.AvailablePickupModels.RemoveAt(0); diff --git a/TRRandomizerCore/Randomizers/TR3/TR3TextureRandomizer.cs b/TRRandomizerCore/Randomizers/TR3/TR3TextureRandomizer.cs index 3bcbd873b..15e560e95 100644 --- a/TRRandomizerCore/Randomizers/TR3/TR3TextureRandomizer.cs +++ b/TRRandomizerCore/Randomizers/TR3/TR3TextureRandomizer.cs @@ -17,7 +17,7 @@ public class TR3TextureRandomizer : BaseTR3Randomizer, ITextureVariantHandler private static readonly Color[] _wireframeColours = ColorUtilities.GetWireframeColours(); private readonly Dictionary _persistentVariants; - private readonly Dictionary _wireframeData; + private readonly Dictionary> _wireframeData; private readonly object _drawLock; private TR3TextureDatabase _textureDatabase; private Dictionary _textureOptions; @@ -31,7 +31,7 @@ public class TR3TextureRandomizer : BaseTR3Randomizer, ITextureVariantHandler public TR3TextureRandomizer() { _persistentVariants = new Dictionary(); - _wireframeData = JsonConvert.DeserializeObject>(ReadResource(@"TR3\Textures\wireframing.json")); + _wireframeData = JsonConvert.DeserializeObject>>(ReadResource(@"TR3\Textures\wireframing.json")); _drawLock = new object(); } @@ -170,14 +170,14 @@ private void ChooseWireframeLevels() _persistentWireColour = _wireframeColours[_generator.Next(0, _wireframeColours.Length)]; } - foreach (WireframeData data in _wireframeData.Values.ToList()) + foreach (WireframeData data in _wireframeData.Values) { data.HighlightLadders = Settings.UseWireframeLadders; data.HighlightTriggers = data.HighlightDeathTiles = Settings.ShowWireframeTriggers; data.SolidInteractables = Settings.UseSolidInteractableWireframing; foreach (SpecialTextureHandling special in data.SpecialTextures) { - List modes = WireframeData.GetDrawModes(special.Type); + List modes = WireframeData.GetDrawModes(special.Type); special.Mode = modes[_generator.Next(0, modes.Count)]; } } @@ -253,7 +253,7 @@ private bool IsSolidLaraLevel(TR3CombinedLevel lvl) (_solidLaraLevels.Contains(lvl.Script) || (lvl.IsCutScene && _solidLaraLevels.Contains(lvl.ParentLevel.Script))); } - private WireframeData GetWireframeData(TR3CombinedLevel lvl) + private WireframeData GetWireframeData(TR3CombinedLevel lvl) { if (IsWireframeLevel(lvl)) { @@ -320,11 +320,11 @@ protected override void StartImpl() if (_outer.IsWireframeLevel(level)) { - WireframeData data = _outer.GetWireframeData(level); + WireframeData data = _outer.GetWireframeData(level); data.SolidEnemies = _outer.Settings.UseSolidEnemyWireframing; if (level.IsCutScene) { - WireframeData parentData = _outer.GetWireframeData(level.ParentLevel); + WireframeData parentData = _outer.GetWireframeData(level.ParentLevel); data.HighlightColour = parentData.HighlightColour; data.SolidLara = parentData.SolidLara; } @@ -356,9 +356,9 @@ protected override void StartImpl() if (_outer.Settings.UseDifferentWireframeColours) { - foreach (TRModel model in level.Data.Models) + foreach (TR3Type type in level.Data.Models.Keys) { - data.ModelColours[model.ID] = _outer.GetWireframeVariant(); + data.ModelColours[type] = _outer.GetWireframeVariant(); } } } diff --git a/TRRandomizerCore/Textures/DynamicTextureBuilder.cs b/TRRandomizerCore/Textures/DynamicTextureBuilder.cs index b440fc60e..8b944ffdc 100644 --- a/TRRandomizerCore/Textures/DynamicTextureBuilder.cs +++ b/TRRandomizerCore/Textures/DynamicTextureBuilder.cs @@ -123,7 +123,7 @@ public DynamicTextureTarget Build(TR1CombinedLevel level) } else { - hips = level.Data.Models.Find(m => m.ID == (uint)TR1Type.Lara).Meshes[0]; + hips = level.Data.Models[TR1Type.Lara].Meshes[0]; if (level.Data.Entities.Any(e => e.TypeID == TR1Type.MidasHand_N)) { modelIDs.Add(TR1Type.LaraMiscAnim_H); @@ -135,19 +135,19 @@ public DynamicTextureTarget Build(TR1CombinedLevel level) // Lara will be partially re-textured. foreach (TR1Type modelID in modelIDs) { - TRModel model = level.Data.Models.Find(m => m.ID == (uint)modelID); + TRModel model = level.Data.Models[modelID]; if (model != null) { - AddModelTextures(level.Data, model, hips, defaultObjectTextures, modelMeshes); + AddModelTextures(level.Data, modelID, model, hips, defaultObjectTextures, modelMeshes); } } foreach (TR1Type modelID in _enemyIDs) { - TRModel model = level.Data.Models.Find(m => m.ID == (uint)modelID); + TRModel model = level.Data.Models[modelID]; if (model != null) { - AddModelTextures(level.Data, model, hips, enemyObjectTextures, modelMeshes); + AddModelTextures(level.Data, modelID, model, hips, enemyObjectTextures, modelMeshes); } } @@ -172,7 +172,7 @@ public DynamicTextureTarget Build(TR1CombinedLevel level) Dictionary keyItems = TR1TypeUtilities.GetKeyItemMap(); foreach (TR1Type pickupType in keyItems.Keys) { - TRModel model = level.Data.Models.Find(m => m.ID == (uint)keyItems[pickupType]); + TRModel model = level.Data.Models[keyItems[pickupType]]; if (model == null) { continue; @@ -185,14 +185,14 @@ public DynamicTextureTarget Build(TR1CombinedLevel level) TRRoomSector sector = FDUtilities.GetRoomSector(keyInstance.X, keyInstance.Y, keyInstance.Z, keyInstance.Room, level.Data, floorData); if (LocationUtilities.SectorContainsSecret(sector, floorData)) { - AddModelTextures(level.Data, model, hips, secretObjectTextures, modelMeshes); + AddModelTextures(level.Data, pickupType, model, hips, secretObjectTextures, modelMeshes); AddSpriteTextures(level.Data, pickupType, secretSpriteTextures); continue; } } // Otherwise it's a regular key item - AddModelTextures(level.Data, model, hips, keyItemObjectTextures, modelMeshes); + AddModelTextures(level.Data, pickupType, model, hips, keyItemObjectTextures, modelMeshes); AddSpriteTextures(level.Data, pickupType, keyItemSpriteTextures); } @@ -222,9 +222,8 @@ public DynamicTextureTarget Build(TR1CombinedLevel level) }; } - private void AddModelTextures(TR1Level level, TRModel model, TRMesh dummyMesh, ISet textures, ISet meshCollection) + private void AddModelTextures(TR1Level level, TR1Type modelID, TRModel model, TRMesh dummyMesh, ISet textures, ISet meshCollection) { - TR1Type modelID = (TR1Type)model.ID; if (TextureMonitor?.RemovedTextures?.Contains(modelID) ?? false) { // Don't include textures that may have been re-assigned to other imported models (e.g. enemies). @@ -238,7 +237,7 @@ private void AddModelTextures(TR1Level level, TRModel model, TRMesh dummyMesh, I return; } - if (modelID == TR1Type.Cowboy && level.Models.Find(m => m.ID == (uint)TR1Type.Cowboy).Meshes[2].TexturedRectangles.Count > 0) + if (modelID == TR1Type.Cowboy && level.Models[TR1Type.Cowboy].Meshes[2].TexturedRectangles.Count > 0) { // We only want to target LeoC's headless cowboy - in this case the cowboy is OG. return; diff --git a/TRRandomizerCore/Textures/Wireframing/AbstractTRWireframer.cs b/TRRandomizerCore/Textures/Wireframing/AbstractTRWireframer.cs index fcd987af4..7fc1621e4 100644 --- a/TRRandomizerCore/Textures/Wireframing/AbstractTRWireframer.cs +++ b/TRRandomizerCore/Textures/Wireframing/AbstractTRWireframer.cs @@ -12,7 +12,7 @@ namespace TRRandomizerCore.Textures; public abstract class AbstractTRWireframer where E : Enum - where L : class + where L : TRLevelBase { protected static readonly TRSize _nullSize = new(0, 0); protected static readonly int _ladderRungs = 4; @@ -29,7 +29,7 @@ public abstract class AbstractTRWireframer private Dictionary _meshFaces; private ISet _allTextures; - protected WireframeData _data; + protected WireframeData _data; protected virtual bool IsTextureExcluded(ushort texture) { @@ -41,7 +41,7 @@ protected virtual bool IsTextureOverriden(ushort texture) return _data.ForcedOverrides.Contains(texture); } - public void Apply(L level, WireframeData data) + public void Apply(L level, WireframeData data) { _roomFace3s = new(); _roomFace4s = new(); @@ -195,7 +195,7 @@ private void ScanRoomFace3s(L level, IEnumerable faces) private void ScanMeshes(L level) { - foreach (TRMesh mesh in GetLevelMeshes(level)) + foreach (TRMesh mesh in level.DistinctMeshes) { ScanMesh(level, mesh); } @@ -556,16 +556,16 @@ private void TidyModels(L level) int blackIndex = GetBlackPaletteIndex(level); // For most meshes, replace any colours with the default background - foreach (TRMesh mesh in GetLevelMeshes(level)) + foreach (TRMesh mesh in level.DistinctMeshes) { SetFace4Colours(mesh.ColouredRectangles, blackIndex); SetFace3Colours(mesh.ColouredTriangles, blackIndex); } ISet processedModelMeshes = new HashSet(); - foreach (TRModel model in GetModels(level)) + foreach (var (type, model) in GetModels(level)) { - if (IsSkybox(model)) + if (IsSkybox(type)) { // Solidify the skybox as it will become the backdrop for every room foreach (TRMesh mesh in model.Meshes) @@ -580,14 +580,14 @@ private void TidyModels(L level) } else if ( - (_data.SolidLara && IsLaraModel(model)) || - (_data.SolidEnemies && (IsEnemyModel(model) || _data.SolidModels.Contains(model.ID)) && !IsEnemyPlaceholderModel(model)) || - (_data.SolidInteractables && IsInteractableModel(model)) || - ShouldSolidifyModel(model) + (_data.SolidLara && IsLaraModel(type)) || + (_data.SolidEnemies && (IsEnemyModel(type) || _data.SolidModels.Contains(type)) && !IsEnemyPlaceholderModel(type)) || + (_data.SolidInteractables && IsInteractableModel(type)) || + ShouldSolidifyModel(type) ) { - int paletteIndex = ImportColour(level, !IsLaraModel(model) && _data.ModelColours.ContainsKey(model.ID) ? - _data.ModelColours[model.ID] : + int paletteIndex = ImportColour(level, !IsLaraModel(type) && _data.ModelColours.ContainsKey(type) ? + _data.ModelColours[type] : _data.HighlightColour); if (paletteIndex == -1) @@ -667,14 +667,13 @@ private void DeleteAnimatedTextures(L level) protected abstract List GetObjectTextures(L level); protected abstract int GetBlackPaletteIndex(L level); protected abstract int ImportColour(L level, Color c); - protected abstract List GetModels(L level); - protected abstract IEnumerable GetLevelMeshes(L level); - protected abstract bool IsSkybox(TRModel model); - protected abstract bool IsLaraModel(TRModel model); - protected abstract bool IsEnemyModel(TRModel model); - protected virtual bool IsEnemyPlaceholderModel(TRModel model) => false; - protected abstract bool IsInteractableModel(TRModel model); - protected virtual bool ShouldSolidifyModel(TRModel model) => false; + protected abstract TRDictionary GetModels(L level); + protected abstract bool IsSkybox(E type); + protected abstract bool IsLaraModel(E type); + protected abstract bool IsEnemyModel(E type); + protected virtual bool IsEnemyPlaceholderModel(E type) => false; + protected abstract bool IsInteractableModel(E type); + protected virtual bool ShouldSolidifyModel(E type) => false; protected abstract void SetSkyboxVisible(L level); protected abstract List GetAnimatedTextures(L level); diff --git a/TRRandomizerCore/Textures/Wireframing/TR1Wireframer.cs b/TRRandomizerCore/Textures/Wireframing/TR1Wireframer.cs index ac46e269a..99abd4c49 100644 --- a/TRRandomizerCore/Textures/Wireframing/TR1Wireframer.cs +++ b/TRRandomizerCore/Textures/Wireframing/TR1Wireframer.cs @@ -43,7 +43,7 @@ public class TR1Wireframer : AbstractTRWireframer TR1Type.Puzzle1_M_H, TR1Type.Puzzle2_M_H, TR1Type.Puzzle3_M_H, TR1Type.Puzzle4_M_H, TR1Type.Key1_M_H, TR1Type.Key2_M_H, TR1Type.Key3_M_H, TR1Type.Key4_M_H, TR1Type.Quest1_M_H, TR1Type.Quest2_M_H, - TR1Type.ScionPiece_M_H + TR1Type.ScionPiece_M_H, TR1Type.LeadBar_M_H }; public override bool Is8BitPalette => true; @@ -55,14 +55,13 @@ protected override AbstractTexturePacker CreatePacker(TR1Leve return _packer = new TR1TexturePacker(level); } - protected override bool IsSkybox(TRModel model) + protected override bool IsSkybox(TR1Type type) { return false; } - protected override bool IsInteractableModel(TRModel model) + protected override bool IsInteractableModel(TR1Type type) { - TR1Type type = (TR1Type)model.ID; return TR1TypeUtilities.IsSwitchType(type) || TR1TypeUtilities.IsKeyholeType(type) || TR1TypeUtilities.IsSlotType(type) @@ -71,9 +70,9 @@ protected override bool IsInteractableModel(TRModel model) || type == TR1Type.Compass_M_H; } - protected override bool ShouldSolidifyModel(TRModel model) + protected override bool ShouldSolidifyModel(TR1Type type) { - return _data.Has3DPickups && _pickupModels.Contains((TR1Type)model.ID); + return _data.Has3DPickups && _pickupModels.Contains(type); } protected override int GetBlackPaletteIndex(TR1Level level) @@ -86,12 +85,7 @@ protected override IEnumerable GetInvalidObjectTextureIndices(TR1Level leve return level.GetInvalidObjectTextureIndices(); } - protected override IEnumerable GetLevelMeshes(TR1Level level) - { - return level.Models.SelectMany(m => m.Meshes).Concat(level.StaticMeshes.Select(s => s.Mesh)); - } - - protected override List GetModels(TR1Level level) + protected override TRDictionary GetModels(TR1Level level) { return level.Models; } @@ -127,21 +121,19 @@ protected override int ImportColour(TR1Level level, Color c) return _packer.PaletteManager.AddPredefinedColour(c); } - protected override bool IsLaraModel(TRModel model) + protected override bool IsLaraModel(TR1Type type) { - return _laraEntities.Contains((TR1Type)model.ID); + return _laraEntities.Contains(type); } - protected override bool IsEnemyModel(TRModel model) + protected override bool IsEnemyModel(TR1Type type) { - TR1Type id = (TR1Type)model.ID; - return TR1TypeUtilities.IsEnemyType(id) || _additionalEnemyEntities.Contains(id); + return TR1TypeUtilities.IsEnemyType(type) || _additionalEnemyEntities.Contains(type); } - protected override bool IsEnemyPlaceholderModel(TRModel model) + protected override bool IsEnemyPlaceholderModel(TR1Type type) { - TR1Type id = (TR1Type)model.ID; - return _enemyPlaceholderEntities.Contains(id); + return _enemyPlaceholderEntities.Contains(type); } protected override void ResetPaletteTracking(TR1Level level) @@ -200,13 +192,13 @@ protected override Dictionary CreateSpecialSegments private TexturedTileSegment CreateMidasDoor(TR1Level level, Pen pen, ushort textureIndex, SpecialTextureMode mode) { - TRModel doorModel = FindDoorModel(level, textureIndex); - if (doorModel == null) + TR1Type doorType = FindDoorModel(level, textureIndex); + if (doorType == default) { return null; } - TR1Entity doorInstance = level.Entities.Find(e => e.TypeID == (TR1Type)doorModel.ID); + TR1Entity doorInstance = level.Entities.Find(e => e.TypeID == doorType); if (doorInstance == null) { return null; @@ -278,25 +270,24 @@ private TexturedTileSegment CreateMidasDoor(TR1Level level, Pen pen, ushort text return new TexturedTileSegment(texture, frame.Bitmap); } - private static TRModel FindDoorModel(TR1Level level, ushort textureIndex) + private static TR1Type FindDoorModel(TR1Level level, ushort textureIndex) { - foreach (TRModel model in level.Models) + foreach (var (type, model) in level.Models) { - TR1Type type = (TR1Type)model.ID; if (!TR1TypeUtilities.IsDoorType(type)) { continue; } - foreach (TRMesh mesh in level.Models.Find(m => m.ID == (uint)type).Meshes) + foreach (TRMesh mesh in model.Meshes) { if (mesh.TexturedRectangles.Any(f => f.Texture == textureIndex)) { - return model; + return type; } } } - return null; + return default; } } diff --git a/TRRandomizerCore/Textures/Wireframing/TR2Wireframer.cs b/TRRandomizerCore/Textures/Wireframing/TR2Wireframer.cs index da59060fa..379402669 100644 --- a/TRRandomizerCore/Textures/Wireframing/TR2Wireframer.cs +++ b/TRRandomizerCore/Textures/Wireframing/TR2Wireframer.cs @@ -34,14 +34,13 @@ protected override AbstractTexturePacker CreatePacker(TR2Leve return new TR2TexturePacker(level); } - protected override bool IsSkybox(TRModel model) + protected override bool IsSkybox(TR2Type type) { - return (TR2Type)model.ID == TR2Type.Skybox_H; + return type == TR2Type.Skybox_H; } - protected override bool IsInteractableModel(TRModel model) + protected override bool IsInteractableModel(TR2Type type) { - TR2Type type = (TR2Type)model.ID; return TR2TypeUtilities.IsSwitchType(type) || TR2TypeUtilities.IsKeyholeType(type) || TR2TypeUtilities.IsSlotType(type) @@ -58,12 +57,7 @@ protected override IEnumerable GetInvalidObjectTextureIndices(TR2Level leve return level.GetInvalidObjectTextureIndices(); } - protected override IEnumerable GetLevelMeshes(TR2Level level) - { - return level.Models.SelectMany(m => m.Meshes).Concat(level.StaticMeshes.Select(s => s.Mesh)); - } - - protected override List GetModels(TR2Level level) + protected override TRDictionary GetModels(TR2Level level) { return level.Models; } @@ -99,15 +93,14 @@ protected override int ImportColour(TR2Level level, Color c) return _paletteTracker.Import(c); } - protected override bool IsLaraModel(TRModel model) + protected override bool IsLaraModel(TR2Type type) { - return _laraEntities.Contains((TR2Type)model.ID); + return _laraEntities.Contains(type); } - protected override bool IsEnemyModel(TRModel model) + protected override bool IsEnemyModel(TR2Type type) { - TR2Type id = (TR2Type)model.ID; - return TR2TypeUtilities.IsEnemyType(id) || _additionalEnemyEntities.Contains(id); + return TR2TypeUtilities.IsEnemyType(type) || _additionalEnemyEntities.Contains(type); } protected override void ResetUnusedTextures(TR2Level level) diff --git a/TRRandomizerCore/Textures/Wireframing/TR3Wireframer.cs b/TRRandomizerCore/Textures/Wireframing/TR3Wireframer.cs index 86aa0ea32..5f5614bbc 100644 --- a/TRRandomizerCore/Textures/Wireframing/TR3Wireframer.cs +++ b/TRRandomizerCore/Textures/Wireframing/TR3Wireframer.cs @@ -34,9 +34,8 @@ protected override AbstractTexturePacker CreatePacker(TR3Leve return new TR3TexturePacker(level); } - protected override bool IsInteractableModel(TRModel model) + protected override bool IsInteractableModel(TR3Type type) { - TR3Type type = (TR3Type)model.ID; return TR3TypeUtilities.IsSwitchType(type) || TR3TypeUtilities.IsKeyholeType(type) || TR3TypeUtilities.IsSlotType(type) @@ -53,12 +52,7 @@ protected override IEnumerable GetInvalidObjectTextureIndices(TR3Level leve return level.GetInvalidObjectTextureIndices(); } - protected override IEnumerable GetLevelMeshes(TR3Level level) - { - return level.Models.SelectMany(m => m.Meshes).Concat(level.StaticMeshes.Select(s => s.Mesh)); - } - - protected override List GetModels(TR3Level level) + protected override TRDictionary GetModels(TR3Level level) { return level.Models; } @@ -94,25 +88,23 @@ protected override int ImportColour(TR3Level level, Color c) return _paletteTracker.Import(c); } - protected override bool IsLaraModel(TRModel model) + protected override bool IsLaraModel(TR3Type type) { - return _laraEntities.Contains((TR3Type)model.ID); + return _laraEntities.Contains(type); } - protected override bool IsEnemyModel(TRModel model) + protected override bool IsEnemyModel(TR3Type type) { - TR3Type id = (TR3Type)model.ID; - return TR3TypeUtilities.IsEnemyType(id) || _additionalEnemyEntities.Contains(id); + return TR3TypeUtilities.IsEnemyType(type) || _additionalEnemyEntities.Contains(type); } - protected override bool IsSkybox(TRModel model) + protected override bool IsSkybox(TR3Type type) { - return (TR3Type)model.ID == TR3Type.Skybox_H; + return type == TR3Type.Skybox_H; } - protected override bool ShouldSolidifyModel(TRModel model) + protected override bool ShouldSolidifyModel(TR3Type type) { - TR3Type type = (TR3Type)model.ID; return TR3TypeUtilities.IsAnyPickupType(type) || TR3TypeUtilities.IsCrystalPickup(type); } diff --git a/TRRandomizerCore/Textures/Wireframing/WireframeData.cs b/TRRandomizerCore/Textures/Wireframing/WireframeData.cs index ffe391712..d77192666 100644 --- a/TRRandomizerCore/Textures/Wireframing/WireframeData.cs +++ b/TRRandomizerCore/Textures/Wireframing/WireframeData.cs @@ -2,7 +2,8 @@ namespace TRRandomizerCore.Textures; -public class WireframeData +public class WireframeData + where T : Enum { /// /// Textures that will be retained, such as water surfaces. @@ -54,12 +55,12 @@ public class WireframeData /// /// Models that should also use solid textures if SolidEnemies is enabled (e.g. CutsceneActors) /// - public List SolidModels { get; set; } + public List SolidModels { get; set; } /// /// Allows different solid colours to be allocated per model. /// - public Dictionary ModelColours { get; set; } + public Dictionary ModelColours { get; set; } /// /// Where textures are shared within segments, and we want to exclude only parts, we "clip" out the rest. diff --git a/TRRandomizerCore/Utilities/TR1EnemyUtilities.cs b/TRRandomizerCore/Utilities/TR1EnemyUtilities.cs index b5a217ed8..86f49fd62 100644 --- a/TRRandomizerCore/Utilities/TR1EnemyUtilities.cs +++ b/TRRandomizerCore/Utilities/TR1EnemyUtilities.cs @@ -583,7 +583,7 @@ public static bool IsEmptyEgg(TR1Entity entity, TR1CombinedLevel level) } TR1Type type = CodeBitsToAtlantean(entity.CodeBits); - return level.Data.Models.Find(m => m.ID == (uint)type) == null; + return !level.Data.Models.ContainsKey(type); } public static bool CanDropItems(TR1Entity entity, TR1CombinedLevel level, FDControl floorData) diff --git a/TRTexture16Importer/Helpers/TRPalette16Control.cs b/TRTexture16Importer/Helpers/TRPalette16Control.cs index f56bb4f18..f7b58044f 100644 --- a/TRTexture16Importer/Helpers/TRPalette16Control.cs +++ b/TRTexture16Importer/Helpers/TRPalette16Control.cs @@ -9,10 +9,10 @@ public class TRPalette16Control private readonly Queue _freeIndices; public TRPalette16Control(TR2Level level) - : this(level.Palette16, level.Models.SelectMany(m => m.Meshes).Concat(level.StaticMeshes.Select(s => s.Mesh))) { } + : this(level.Palette16, level.DistinctMeshes) { } public TRPalette16Control(TR3Level level) - : this(level.Palette16, level.Models.SelectMany(m => m.Meshes).Concat(level.StaticMeshes.Select(s => s.Mesh))) { } + : this(level.Palette16, level.DistinctMeshes) { } public TRPalette16Control(List palette16, IEnumerable meshes) { diff --git a/TRTexture16Importer/Helpers/TRPalette8Control.cs b/TRTexture16Importer/Helpers/TRPalette8Control.cs index 1729791b0..78df60f99 100644 --- a/TRTexture16Importer/Helpers/TRPalette8Control.cs +++ b/TRTexture16Importer/Helpers/TRPalette8Control.cs @@ -63,24 +63,26 @@ public void MergeTiles() // Grab meshes we aren't interested in - but don't remove Lara's hips e.g. Atlantean spawns List ignoredMeshes = new(); - List laraMeshes = Level.Models.Find(m => m.ID == (uint)TR1Type.Lara)?.Meshes; + Level.Models.TryGetValue(TR1Type.Lara, out TRModel lara); foreach (TR1Type entity in ObsoleteModels) { - List meshes = Level.Models.Find(m => m.ID == (uint)entity)?.Meshes; - if (meshes != null) + Level.Models.TryGetValue(entity, out TRModel model); + if (model == null) { - foreach (TRMesh mesh in meshes) + continue; + } + + foreach (TRMesh mesh in model.Meshes) + { + if (lara == null || !lara.Meshes.Contains(mesh)) { - if (laraMeshes == null || !laraMeshes.Contains(mesh)) - { - ignoredMeshes.AddRange(meshes); - } + ignoredMeshes.AddRange(model.Meshes); } } } // Update all colours used in all meshes - foreach (TRMesh mesh in Level.Models.SelectMany(m => m.Meshes).Concat(Level.StaticMeshes.Select(s => s.Mesh))) + foreach (TRMesh mesh in Level.DistinctMeshes) { if (ignoredMeshes.Contains(mesh)) { @@ -158,7 +160,7 @@ public void MergePredefinedColours() } } - foreach (TRMesh mesh in Level.Models.SelectMany(m => m.Meshes).Concat(Level.StaticMeshes.Select(s => s.Mesh))) + foreach (TRMesh mesh in Level.DistinctMeshes) { foreach (TRMeshFace face in mesh.ColouredFaces) { diff --git a/TRTexture16Importer/Textures/Mapping/TR1TextureMapping.cs b/TRTexture16Importer/Textures/Mapping/TR1TextureMapping.cs index 6e7577ae3..edc91d418 100644 --- a/TRTexture16Importer/Textures/Mapping/TR1TextureMapping.cs +++ b/TRTexture16Importer/Textures/Mapping/TR1TextureMapping.cs @@ -46,7 +46,7 @@ protected override int ImportColour(Color colour) protected override List GetModelMeshes(TR1Type entity) { - return _level.Models.Find(m => m.ID == (uint)entity)?.Meshes; + return _level.Models.ContainsKey(entity) ? _level.Models[entity].Meshes : null; } protected override List GetSpriteSequences() diff --git a/TRTexture16Importer/Textures/Mapping/TR2TextureMapping.cs b/TRTexture16Importer/Textures/Mapping/TR2TextureMapping.cs index 8fa8bf83e..e08b9ea4c 100644 --- a/TRTexture16Importer/Textures/Mapping/TR2TextureMapping.cs +++ b/TRTexture16Importer/Textures/Mapping/TR2TextureMapping.cs @@ -42,7 +42,7 @@ protected override int ImportColour(Color colour) protected override List GetModelMeshes(TR2Type entity) { - return _level.Models.Find(m => m.ID == (uint)entity).Meshes; + return _level.Models.ContainsKey(entity) ? _level.Models[entity].Meshes : null; } protected override List GetSpriteSequences() diff --git a/TRTexture16Importer/Textures/Mapping/TR3TextureMapping.cs b/TRTexture16Importer/Textures/Mapping/TR3TextureMapping.cs index 8ac07ad05..bb2383378 100644 --- a/TRTexture16Importer/Textures/Mapping/TR3TextureMapping.cs +++ b/TRTexture16Importer/Textures/Mapping/TR3TextureMapping.cs @@ -43,7 +43,7 @@ protected override int ImportColour(Color colour) protected override List GetModelMeshes(TR3Type entity) { - return _level.Models.Find(m => m.ID == (uint)entity).Meshes; + return _level.Models.ContainsKey(entity) ? _level.Models[entity].Meshes : null; } protected override List GetSpriteSequences() diff --git a/TextureExport/Types/DependencyExporter.cs b/TextureExport/Types/DependencyExporter.cs index d3562d917..54d5315bd 100644 --- a/TextureExport/Types/DependencyExporter.cs +++ b/TextureExport/Types/DependencyExporter.cs @@ -9,9 +9,9 @@ public static class DependencyExporter public static void Export(TR1Level level, string lvl) { TR1TextureRemapGroup remapGroup = new(); - foreach (TRModel model in level.Models) + foreach (TR1Type type in level.Models.Keys) { - remapGroup.CalculateDependencies(level, (TR1Type)model.ID); + remapGroup.CalculateDependencies(level, type); } foreach (TextureDependency dependency in remapGroup.Dependencies) @@ -46,9 +46,9 @@ public static void Export(TR1Level level, string lvl) public static void Export(TR2Level level, string lvl) { TR2TextureRemapGroup remapGroup = new(); - foreach (TRModel model in level.Models) + foreach (TR2Type type in level.Models.Keys) { - remapGroup.CalculateDependencies(level, (TR2Type)model.ID); + remapGroup.CalculateDependencies(level, type); } string dir = @"TR2\Deduplication"; @@ -59,9 +59,9 @@ public static void Export(TR2Level level, string lvl) public static void Export(TR3Level level, string lvl) { TR3TextureRemapGroup remapGroup = new(); - foreach (TRModel model in level.Models) + foreach (TR3Type type in level.Models.Keys) { - remapGroup.CalculateDependencies(level, (TR3Type)model.ID); + remapGroup.CalculateDependencies(level, type); } remapGroup.Dependencies.Sort(delegate (TextureDependency d1, TextureDependency d2) diff --git a/TextureExport/Types/HtmlExporter.cs b/TextureExport/Types/HtmlExporter.cs index c102d70cf..d7a0e9256 100644 --- a/TextureExport/Types/HtmlExporter.cs +++ b/TextureExport/Types/HtmlExporter.cs @@ -43,7 +43,7 @@ public static void Export(TR2Level level, string lvlName) BuildLevelSelect(levelSel, lvlName, TR2LevelNames.AsOrderedList); StringBuilder skyboxInfo = new(); - Dictionary skyColours = GetSkyBoxColours(level.Models.Find(m => m.ID == (uint)TR2Type.Skybox_H)?.Meshes, level.Palette16); + Dictionary skyColours = GetSkyBoxColours(level.Models[TR2Type.Skybox_H]?.Meshes, level.Palette16); BuildSkyBox(skyboxInfo, skyColours); Write("TR2", lvlName, tiles, levelSel, skyboxInfo); @@ -59,7 +59,7 @@ public static void Export(TR3Level level, string lvlName) BuildLevelSelect(levelSel, lvlName, TR3LevelNames.AsOrderedList); StringBuilder skyboxInfo = new(); - Dictionary skyColours = GetSkyBoxColours(level.Models.Find(m => m.ID == (uint)TR3Type.Skybox_H)?.Meshes, level.Palette16); + Dictionary skyColours = GetSkyBoxColours(level.Models[TR3Type.Skybox_H]?.Meshes, level.Palette16); BuildSkyBox(skyboxInfo, skyColours); Write("TR3", lvlName, tiles, levelSel, skyboxInfo);