From d9f0b090848f7a4a695b98184c826d0f0d9514df Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sun, 25 Jan 2026 14:21:57 +0000 Subject: [PATCH 1/6] Initial plan From 1ee4e7e6ad0af72615be41fafbcbbaa433228b00 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sun, 25 Jan 2026 14:29:43 +0000 Subject: [PATCH 2/6] Add VolumeObject trigger target type with compilation support Co-authored-by: TrainWrack <120750885+TrainWrack@users.noreply.github.com> --- .../LevelData/Compilers/TombEngine/FloorData.cs | 12 ++++++++++++ .../Compilers/TombEngine/LevelCompilerTombEngine.cs | 5 +++++ .../TombLib/LevelData/Instances/TriggerInstance.cs | 3 ++- TombLib/TombLib/NG/NgParameterInfo.cs | 7 +++++++ TombLib/TombLib/NG/NgParameterRange.cs | 9 ++++++++- 5 files changed, 34 insertions(+), 2 deletions(-) diff --git a/TombLib/TombLib/LevelData/Compilers/TombEngine/FloorData.cs b/TombLib/TombLib/LevelData/Compilers/TombEngine/FloorData.cs index 6c01ed07b..98ee016a5 100644 --- a/TombLib/TombLib/LevelData/Compilers/TombEngine/FloorData.cs +++ b/TombLib/TombLib/LevelData/Compilers/TombEngine/FloorData.cs @@ -310,6 +310,16 @@ private List BuildTriggers(Room room, Sector sector, VectorInt2 pos) break; + case TriggerTargetType.VolumeObject: + if (!(trigger.Target is VolumeInstance)) + { + throw new Exception("A VolumeObject trigger must reference a volume instance! ('" + trigger + "')"); + } + + trigger2 = (ushort)(GetTriggerParameter(trigger.Target, trigger, _fdFunctionMask) | func); + result.Add(trigger2); + break; + default: throw new Exception("Unknown trigger target found '" + trigger + "'"); } @@ -347,6 +357,8 @@ private ushort GetTriggerParameter(ITriggerParameter parameter, TriggerInstance index = _sinkTable[(SinkInstance)parameter]; else if (parameter is FlybyCameraInstance) index = _flybyTable[(FlybyCameraInstance)parameter]; + else if (parameter is VolumeInstance) + index = _volumeTable[(VolumeInstance)parameter]; else if (parameter is StaticInstance) { StaticInstance @object = (StaticInstance)parameter; diff --git a/TombLib/TombLib/LevelData/Compilers/TombEngine/LevelCompilerTombEngine.cs b/TombLib/TombLib/LevelData/Compilers/TombEngine/LevelCompilerTombEngine.cs index 7953a7e47..7f4ab76f3 100644 --- a/TombLib/TombLib/LevelData/Compilers/TombEngine/LevelCompilerTombEngine.cs +++ b/TombLib/TombLib/LevelData/Compilers/TombEngine/LevelCompilerTombEngine.cs @@ -53,6 +53,7 @@ public sealed partial class LevelCompilerTombEngine : LevelCompiler private Dictionary _soundSourcesTable; private Dictionary _flybyTable; private Dictionary _staticsTable; + private Dictionary _volumeTable; // Collected game limits private Dictionary _limits; @@ -207,7 +208,9 @@ private void BuildCamerasAndSinks() _cameraTable = new Dictionary(new ReferenceEqualityComparer()); _sinkTable = new Dictionary(new ReferenceEqualityComparer()); _flybyTable = new Dictionary(new ReferenceEqualityComparer()); + _volumeTable = new Dictionary(new ReferenceEqualityComparer()); + int volumeID = 0; foreach (var room in _level.ExistingRooms) { foreach (var obj in room.Objects.OfType()) @@ -216,6 +219,8 @@ private void BuildCamerasAndSinks() _flybyTable.Add(obj, flybyID++); foreach (var obj in room.Objects.OfType()) _sinkTable.Add(obj, sinkID++); + foreach (var obj in room.Objects.OfType()) + _volumeTable.Add(obj, volumeID++); } } diff --git a/TombLib/TombLib/LevelData/Instances/TriggerInstance.cs b/TombLib/TombLib/LevelData/Instances/TriggerInstance.cs index 5f33d0e5e..edec915e8 100644 --- a/TombLib/TombLib/LevelData/Instances/TriggerInstance.cs +++ b/TombLib/TombLib/LevelData/Instances/TriggerInstance.cs @@ -45,7 +45,8 @@ public enum TriggerTargetType : ushort FmvNg = 14, TimerfieldNg = 15, VolumeEvent = 16, - GlobalEvent = 17 + GlobalEvent = 17, + VolumeObject = 18 } public interface ITriggerParameter : IEquatable diff --git a/TombLib/TombLib/NG/NgParameterInfo.cs b/TombLib/TombLib/NG/NgParameterInfo.cs index 56a35ef99..13751a6c9 100644 --- a/TombLib/TombLib/NG/NgParameterInfo.cs +++ b/TombLib/TombLib/NG/NgParameterInfo.cs @@ -82,6 +82,7 @@ public static IEnumerable GetTargetTypeRange(LevelSettings le { yield return TriggerTargetType.VolumeEvent; yield return TriggerTargetType.GlobalEvent; + yield return TriggerTargetType.VolumeObject; } } } @@ -177,6 +178,12 @@ public static NgParameterRange GetTargetRange(LevelSettings levelSettings, Trigg else return new NgParameterRange(NgParameterKind.AnyNumber); + case TriggerTargetType.VolumeObject: + if (levelSettings.GameVersion == TRVersion.Game.TombEngine) + return new NgParameterRange(NgParameterKind.VolumesInLevel); + else + return new NgParameterRange(NgParameterKind.AnyNumber); + default: return new NgParameterRange(NgParameterKind.AnyNumber); } diff --git a/TombLib/TombLib/NG/NgParameterRange.cs b/TombLib/TombLib/NG/NgParameterRange.cs index a68d9039c..1d8d921a8 100644 --- a/TombLib/TombLib/NG/NgParameterRange.cs +++ b/TombLib/TombLib/NG/NgParameterRange.cs @@ -36,7 +36,8 @@ public enum NgParameterKind VolumeEventTypes, GlobalEventSets, GlobalEventTypes, - PluginEnumeration + PluginEnumeration, + VolumesInLevel } public struct NgLinearParameter @@ -146,6 +147,7 @@ public bool IsObject case NgParameterKind.CamerasInLevel: case NgParameterKind.SinksInLevel: case NgParameterKind.FlybyCamerasInLevel: + case NgParameterKind.VolumesInLevel: return true; case NgParameterKind.Choice: foreach (var choice in Choices) @@ -207,6 +209,8 @@ public bool ParameterMatches(ITriggerParameter parameter, bool nullResult) return parameter is SinkInstance; case NgParameterKind.FlybyCamerasInLevel: return parameter is FlybyCameraInstance; + case NgParameterKind.VolumesInLevel: + return parameter is VolumeInstance; case NgParameterKind.Rooms255: return parameter is Room; case NgParameterKind.VolumeEventSets: @@ -295,6 +299,9 @@ public IEnumerable BuildList(Level level) case NgParameterKind.FlybyCamerasInLevel: return level.ExistingRooms.SelectMany(room => room.Objects).OfType(); + case NgParameterKind.VolumesInLevel: + return level.ExistingRooms.SelectMany(room => room.Objects).OfType(); + case NgParameterKind.Rooms255: return level.ExistingRooms; From 6e2e218c2f4e9488fa608361c098e994a90738ea Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sun, 25 Jan 2026 14:31:38 +0000 Subject: [PATCH 3/6] Add proper error handling for volume table lookup Co-authored-by: TrainWrack <120750885+TrainWrack@users.noreply.github.com> --- .../TombLib/LevelData/Compilers/TombEngine/FloorData.cs | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/TombLib/TombLib/LevelData/Compilers/TombEngine/FloorData.cs b/TombLib/TombLib/LevelData/Compilers/TombEngine/FloorData.cs index 98ee016a5..873642966 100644 --- a/TombLib/TombLib/LevelData/Compilers/TombEngine/FloorData.cs +++ b/TombLib/TombLib/LevelData/Compilers/TombEngine/FloorData.cs @@ -358,7 +358,14 @@ private ushort GetTriggerParameter(ITriggerParameter parameter, TriggerInstance else if (parameter is FlybyCameraInstance) index = _flybyTable[(FlybyCameraInstance)parameter]; else if (parameter is VolumeInstance) - index = _volumeTable[(VolumeInstance)parameter]; + { + VolumeInstance volume = (VolumeInstance)parameter; + if (!_volumeTable.TryGetValue(volume, out index)) + { + _progressReporter.ReportWarn("Trigger '" + triggerDiagnostic + "') referring to illegal volume '" + volume + "'."); + index = 0; + } + } else if (parameter is StaticInstance) { StaticInstance @object = (StaticInstance)parameter; From 1aacc73e03cc7b3335b2f97879b113a60261e328 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sun, 25 Jan 2026 14:33:15 +0000 Subject: [PATCH 4/6] Simplify VolumeObject trigger compilation by grouping with other simple triggers Co-authored-by: TrainWrack <120750885+TrainWrack@users.noreply.github.com> --- .../LevelData/Compilers/TombEngine/FloorData.cs | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) diff --git a/TombLib/TombLib/LevelData/Compilers/TombEngine/FloorData.cs b/TombLib/TombLib/LevelData/Compilers/TombEngine/FloorData.cs index 873642966..edbacc0fc 100644 --- a/TombLib/TombLib/LevelData/Compilers/TombEngine/FloorData.cs +++ b/TombLib/TombLib/LevelData/Compilers/TombEngine/FloorData.cs @@ -255,6 +255,7 @@ private List BuildTriggers(Room room, Sector sector, VectorInt2 pos) case TriggerTargetType.PlayAudio: case TriggerTargetType.FlipEffect: case TriggerTargetType.Secret: + case TriggerTargetType.VolumeObject: trigger2 = (ushort)(GetTriggerParameter(trigger.Target, trigger, _fdFunctionMask) | func); result.Add(trigger2); break; @@ -310,16 +311,6 @@ private List BuildTriggers(Room room, Sector sector, VectorInt2 pos) break; - case TriggerTargetType.VolumeObject: - if (!(trigger.Target is VolumeInstance)) - { - throw new Exception("A VolumeObject trigger must reference a volume instance! ('" + trigger + "')"); - } - - trigger2 = (ushort)(GetTriggerParameter(trigger.Target, trigger, _fdFunctionMask) | func); - result.Add(trigger2); - break; - default: throw new Exception("Unknown trigger target found '" + trigger + "'"); } From d3119d775a13190467ac9b0aee8f0febf10bce33 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sun, 25 Jan 2026 14:34:39 +0000 Subject: [PATCH 5/6] Fix typo in volume table warning message Co-authored-by: TrainWrack <120750885+TrainWrack@users.noreply.github.com> --- TombLib/TombLib/LevelData/Compilers/TombEngine/FloorData.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/TombLib/TombLib/LevelData/Compilers/TombEngine/FloorData.cs b/TombLib/TombLib/LevelData/Compilers/TombEngine/FloorData.cs index edbacc0fc..68f31e5b4 100644 --- a/TombLib/TombLib/LevelData/Compilers/TombEngine/FloorData.cs +++ b/TombLib/TombLib/LevelData/Compilers/TombEngine/FloorData.cs @@ -353,7 +353,7 @@ private ushort GetTriggerParameter(ITriggerParameter parameter, TriggerInstance VolumeInstance volume = (VolumeInstance)parameter; if (!_volumeTable.TryGetValue(volume, out index)) { - _progressReporter.ReportWarn("Trigger '" + triggerDiagnostic + "') referring to illegal volume '" + volume + "'."); + _progressReporter.ReportWarn("Trigger '" + triggerDiagnostic + "' referring to illegal volume '" + volume + "'."); index = 0; } } From bac6bb6d5431de590c0d9d5454846a1a0a5f18ae Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sun, 25 Jan 2026 14:35:55 +0000 Subject: [PATCH 6/6] Improve code style: move variable declaration and use pattern matching Co-authored-by: TrainWrack <120750885+TrainWrack@users.noreply.github.com> --- TombLib/TombLib/LevelData/Compilers/TombEngine/FloorData.cs | 3 +-- .../LevelData/Compilers/TombEngine/LevelCompilerTombEngine.cs | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/TombLib/TombLib/LevelData/Compilers/TombEngine/FloorData.cs b/TombLib/TombLib/LevelData/Compilers/TombEngine/FloorData.cs index 68f31e5b4..02e6b899a 100644 --- a/TombLib/TombLib/LevelData/Compilers/TombEngine/FloorData.cs +++ b/TombLib/TombLib/LevelData/Compilers/TombEngine/FloorData.cs @@ -348,9 +348,8 @@ private ushort GetTriggerParameter(ITriggerParameter parameter, TriggerInstance index = _sinkTable[(SinkInstance)parameter]; else if (parameter is FlybyCameraInstance) index = _flybyTable[(FlybyCameraInstance)parameter]; - else if (parameter is VolumeInstance) + else if (parameter is VolumeInstance volume) { - VolumeInstance volume = (VolumeInstance)parameter; if (!_volumeTable.TryGetValue(volume, out index)) { _progressReporter.ReportWarn("Trigger '" + triggerDiagnostic + "' referring to illegal volume '" + volume + "'."); diff --git a/TombLib/TombLib/LevelData/Compilers/TombEngine/LevelCompilerTombEngine.cs b/TombLib/TombLib/LevelData/Compilers/TombEngine/LevelCompilerTombEngine.cs index 7f4ab76f3..0ddb215e4 100644 --- a/TombLib/TombLib/LevelData/Compilers/TombEngine/LevelCompilerTombEngine.cs +++ b/TombLib/TombLib/LevelData/Compilers/TombEngine/LevelCompilerTombEngine.cs @@ -204,13 +204,13 @@ private void BuildCamerasAndSinks() int sinkID = 0; int camID = 0; int flybyID = 0; + int volumeID = 0; _cameraTable = new Dictionary(new ReferenceEqualityComparer()); _sinkTable = new Dictionary(new ReferenceEqualityComparer()); _flybyTable = new Dictionary(new ReferenceEqualityComparer()); _volumeTable = new Dictionary(new ReferenceEqualityComparer()); - int volumeID = 0; foreach (var room in _level.ExistingRooms) { foreach (var obj in room.Objects.OfType())