Skip to content

Commit

Permalink
Expand on TRR file control (#679)
Browse files Browse the repository at this point in the history
Part of #614.
  • Loading branch information
lahm86 authored May 21, 2024
1 parent 38be4bd commit 201d2e9
Show file tree
Hide file tree
Showing 35 changed files with 1,885 additions and 48 deletions.
Binary file modified Deps/TRGE.Coord.dll
Binary file not shown.
Binary file modified Deps/TRGE.Core.dll
Binary file not shown.
26 changes: 22 additions & 4 deletions TRLevelControl/Build/TRModelBuilder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ public class TRModelBuilder<T>
private static readonly ushort _tr5ModelPadding = 0xFFEF;

private readonly TRGameVersion _version;
private readonly TRModelDataType _dataType;
private readonly ITRLevelObserver _observer;

private List<TRAnimation> _animations;
Expand All @@ -23,9 +24,10 @@ public class TRModelBuilder<T>
private Dictionary<TRAnimDispatch, short> _dispatchToAnimMap;
private Dictionary<TRAnimDispatch, short> _dispatchFrameBase;

public TRModelBuilder(TRGameVersion version, ITRLevelObserver observer = null)
public TRModelBuilder(TRGameVersion version, TRModelDataType dataType, ITRLevelObserver observer = null)
{
_version = version;
_dataType = dataType;
_observer = observer;
}

Expand Down Expand Up @@ -305,6 +307,19 @@ private TRAnimation BuildAnimation(PlaceholderModel placeholderModel, int animIn
}
}

if (_dataType == TRModelDataType.PDP && _version == TRGameVersion.TR1 && globalAnimIndex < _placeholderAnimations.Count - 1)
{
// TR1 PDP files have an extra frame in Lara's drop-twist-hang animation (46, modern controls).
// Frame start/end mean it's never used in-game, but we capture it for tests.
// Sanity check that the difference is exactly the size of a (TR1) frame.
PlaceholderAnimation nextAnimation = _placeholderAnimations[globalAnimIndex + 1];
int difference = ((int)nextAnimation.FrameOffset / sizeof(short)) - frameIndex;
if (difference == 10 + 2 * placeholderModel.NumMeshes)
{
animation.Frames.Add(BuildFrame(ref frameIndex, placeholderModel.NumMeshes));
}
}

return animation;
}

Expand Down Expand Up @@ -536,7 +551,8 @@ private void DeconstructModel(T type, TRModel model)
{
ID = (uint)(object)type,
Animation = model.Animations.Count == 0 ? TRConsts.NoAnimation : (ushort)_placeholderAnimations.Count,
FrameOffset = _observer == null && model.Animations.Count == 0 ? 0 : (uint)_frames.Count * sizeof(short),
FrameOffset = (_dataType == TRModelDataType.PDP || _observer == null)
&& model.Animations.Count == 0 ? 0 : (uint)_frames.Count * sizeof(short),
NumMeshes = (ushort)model.Meshes.Count,
};
_placeholderModels.Add(placeholderModel);
Expand Down Expand Up @@ -880,11 +896,13 @@ private void WriteModels(TRLevelWriter writer, TRDictionary<T, TRModel> models)
}
}

private static TRAngleMode GetMode(TRAnimFrameRotation rot)
private TRAngleMode GetMode(TRAnimFrameRotation rot)
{
if (rot.X == 0 && rot.Y == 0)
{
return TRAngleMode.Z;
// OG TR2+ levels (and TRR levels) use Z here, PDP uses X. Makes no difference
// in game, but keeps tests happy.
return _dataType == TRModelDataType.PDP && rot.Z == 0 ? TRAngleMode.X : TRAngleMode.Z;
}
if (rot.X == 0 && rot.Z == 0)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -151,13 +151,13 @@ private void WriteMeshData(TRLevelWriter writer)

private void ReadModelData(TRLevelReader reader)
{
TRModelBuilder<TR1Type> builder = new(TRGameVersion.TR1, _observer);
TRModelBuilder<TR1Type> builder = new(TRGameVersion.TR1, TRModelDataType.Level, _observer);
_level.Models = builder.ReadModelData(reader, _meshBuilder);
}

private void WriteModelData(TRLevelWriter writer)
{
TRModelBuilder<TR1Type> builder = new(TRGameVersion.TR1, _observer);
TRModelBuilder<TR1Type> builder = new(TRGameVersion.TR1, TRModelDataType.Level, _observer);
builder.WriteModelData(writer, _level.Models);
}

Expand Down
83 changes: 83 additions & 0 deletions TRLevelControl/Control/TR1/TR1MapControl.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
using TRLevelControl.Model;

namespace TRLevelControl;

public class TR1MapControl : TRMapControlBase<TR1Type, TR1RAlias>
{
protected override TR1Type ReadKey(string key)
=> _keyToTypeMap.ContainsKey(key) ? _keyToTypeMap[key] : default;

protected override string ConvertKey(TR1Type key)
=> _typeToKeyMap[key];

private static readonly Dictionary<string, TR1Type> _keyToTypeMap = new()
{
["DUMMY"] = default,
["BRIDGE_FLAT"] = TR1Type.BridgeFlat,
["BRIDGE_TILT1"] = TR1Type.BridgeTilt1,
["BRIDGE_TILT2"] = TR1Type.BridgeTilt2,
["DART_EMITTER"] = TR1Type.DartEmitter,
["DARTS"] = TR1Type.Dart_H,
["DOOR_TYPE1"] = TR1Type.Door1,
["DOOR_TYPE2"] = TR1Type.Door2,
["DOOR_TYPE3"] = TR1Type.Door3,
["DOOR_TYPE4"] = TR1Type.Door4,
["DOOR_TYPE5"] = TR1Type.Door5,
["DOOR_TYPE6"] = TR1Type.Door6,
["DOOR_TYPE7"] = TR1Type.Door7,
["DOOR_TYPE8"] = TR1Type.Door8,
["FALLING_BLOCK"] = TR1Type.FallingBlock,
["FALLING_CEILING1"] = TR1Type.FallingCeiling1,
["FALLING_CEILING2"] = TR1Type.FallingCeiling2,
["KEY_HOLE1"] = TR1Type.Keyhole1,
["KEY_HOLE2"] = TR1Type.Keyhole2,
["KEY_HOLE3"] = TR1Type.Keyhole3,
["KEY_HOLE4"] = TR1Type.Keyhole4,
["KEY_OPTION1"] = TR1Type.Key1_M_H,
["KEY_OPTION2"] = TR1Type.Key2_M_H,
["KEY_OPTION3"] = TR1Type.Key3_M_H,
["KEY_OPTION4"] = TR1Type.Key4_M_H,
["LARSON"] = TR1Type.Larson,
["LAVA_WEDGE"] = TR1Type.AtlanteanLava,
["LIGHTNING_EMITTER"] = TR1Type.ThorLightning,
["MOVABLE_BLOCK"] = TR1Type.PushBlock1,
["MOVABLE_BLOCK2"] = TR1Type.PushBlock2,
["MOVABLE_BLOCK3"] = TR1Type.PushBlock3,
["MOVABLE_BLOCK4"] = TR1Type.PushBlock4,
["MOVING_BAR"] = TR1Type.Barricade,
["NATLA"] = TR1Type.Natla,
["PASSPORT_OPTION"] = TR1Type.PassportOpen_M_H,
["PENDULUM"] = TR1Type.SwingingBlade,
["PLAYER_1"] = TR1Type.CutsceneActor1,
["PLAYER_2"] = TR1Type.CutsceneActor2,
["PLAYER_3"] = TR1Type.CutsceneActor3,
["PLAYER_4"] = TR1Type.CutsceneActor4,
["PODS"] = TR1Type.AtlanteanEgg,
["PUZZLE_DONE1"] = TR1Type.PuzzleDone1,
["PUZZLE_DONE2"] = TR1Type.PuzzleDone2,
["PUZZLE_DONE3"] = TR1Type.PuzzleDone3,
["PUZZLE_DONE4"] = TR1Type.PuzzleDone4,
["PUZZLE_HOLE1"] = TR1Type.PuzzleHole1,
["PUZZLE_HOLE2"] = TR1Type.PuzzleHole2,
["PUZZLE_HOLE3"] = TR1Type.PuzzleHole3,
["PUZZLE_HOLE4"] = TR1Type.PuzzleHole4,
["PUZZLE_OPTION1"] = TR1Type.Puzzle1_M_H,
["PUZZLE_OPTION2"] = TR1Type.Puzzle2_M_H,
["PUZZLE_OPTION3"] = TR1Type.Puzzle3_M_H,
["PUZZLE_OPTION4"] = TR1Type.Puzzle4_M_H,
["ROLLING_BALL"] = TR1Type.RollingBall,
["SWITCH_TYPE1"] = TR1Type.WallSwitch,
["SWITCH_TYPE2"] = TR1Type.UnderwaterSwitch,
["TEETH_TRAP"] = TR1Type.SlammingDoor,
["TRAPDOOR"] = TR1Type.Trapdoor1,
["TRAPDOOR2"] = TR1Type.Trapdoor2,
["WARRIOR3"] = TR1Type.NonShootingAtlantean_N,
};

private static readonly Dictionary<TR1Type, string> _typeToKeyMap;

static TR1MapControl()
{
_typeToKeyMap = _keyToTypeMap.ToDictionary(e => e.Value, e => e.Key);
}
}
13 changes: 13 additions & 0 deletions TRLevelControl/Control/TR1/TR1PDPControl.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
using TRLevelControl.Build;
using TRLevelControl.Model;

namespace TRLevelControl;

public class TR1PDPControl : TRPDPControlBase<TR1Type>
{
public TR1PDPControl(ITRLevelObserver observer = null)
: base(observer) { }

protected override TRModelBuilder<TR1Type> CreateBuilder()
=> new(TRGameVersion.TR1, TRModelDataType.PDP);
}
Original file line number Diff line number Diff line change
Expand Up @@ -168,13 +168,13 @@ private void WriteMeshData(TRLevelWriter writer)

private void ReadModelData(TRLevelReader reader)
{
TRModelBuilder<TR2Type> builder = new(TRGameVersion.TR2, _observer);
TRModelBuilder<TR2Type> builder = new(TRGameVersion.TR2, TRModelDataType.Level, _observer);
_level.Models = builder.ReadModelData(reader, _meshBuilder);
}

private void WriteModelData(TRLevelWriter writer)
{
TRModelBuilder<TR2Type> builder = new(TRGameVersion.TR2, _observer);
TRModelBuilder<TR2Type> builder = new(TRGameVersion.TR2, TRModelDataType.Level, _observer);
builder.WriteModelData(writer, _level.Models);
}

Expand Down
119 changes: 119 additions & 0 deletions TRLevelControl/Control/TR2/TR2MapControl.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
using TRLevelControl.Model;

namespace TRLevelControl;

public class TR2MapControl : TRMapControlBase<TR2Type, TR2RAlias>
{
protected override TR2Type ReadKey(string key)
=> _keyToTypeMap.ContainsKey(key) ? _keyToTypeMap[key] : default;

protected override string ConvertKey(TR2Type key)
=> _typeToKeyMap[key];

private static readonly Dictionary<string, TR2Type> _keyToTypeMap = new()
{
["DUMMY"] = default,
["ALARM_SOUND"] = TR2Type.DoorBell_N,
["AVALANCHE"] = TR2Type.BouldersOrSnowballs,
["BANDIT2"] = TR2Type.Mercenary2,
["BANDIT2B"] = TR2Type.Mercenary3,
["BANDIT3"] = TR2Type.Mercenary1,
["BARACUDDA"] = TR2Type.Barracuda,
["BIG_SPIDER"] = TR2Type.GiantSpider,
["BIG_YETI"] = TR2Type.BirdMonster,
["BLADE"] = TR2Type.WallMountedKnifeBlade,
["BRIDGE_FLAT"] = TR2Type.BridgeFlat,
["BRIDGE_TILT1"] = TR2Type.BridgeTilt1,
["BRIDGE_TILT2"] = TR2Type.BridgeTilt2,
["CEILING_SPIKES"] = TR2Type.SpikyCeiling,
["COPTER"] = TR2Type.Helicopter,
["CULT3"] = TR2Type.ShotgunGoon,
["DOOR_TYPE1"] = TR2Type.Door1,
["DOOR_TYPE2"] = TR2Type.Door2,
["DOOR_TYPE3"] = TR2Type.Door3,
["DOOR_TYPE4"] = TR2Type.Door4,
["DOOR_TYPE5"] = TR2Type.Door5,
["DOOR_TYPE6"] = TR2Type.LiftingDoor1,
["DOOR_TYPE7"] = TR2Type.LiftingDoor2,
["DOOR_TYPE8"] = TR2Type.LiftingDoor3,
["DRAW_BRIDGE"] = TR2Type.Drawbridge,
["EAGLE"] = TR2Type.Eagle,
["FALLING_BLOCK"] = TR2Type.FallingBlock,
["FALLING_CEILING1"] = TR2Type.FallingCeilingOrSandbag,
["HARPOON_BOLT"] = TR2Type.HarpoonProjectile_H,
["KEY_HOLE1"] = TR2Type.Keyhole1,
["KEY_HOLE2"] = TR2Type.Keyhole2,
["KEY_HOLE3"] = TR2Type.Keyhole3,
["KEY_HOLE4"] = TR2Type.Keyhole4,
["KEY_OPTION1"] = TR2Type.Key1_M_H,
["KEY_OPTION2"] = TR2Type.Key2_M_H,
["KEY_OPTION3"] = TR2Type.Key3_M_H,
["KEY_OPTION4"] = TR2Type.Key4_M_H,
["KILLER_STATUE"] = TR2Type.StatueWithKnifeBlade,
["MESHSWAP1"] = TR2Type.CutsceneActor1,
["MESHSWAP2"] = TR2Type.CutsceneActor2,
["MINI_COPTER"] = TR2Type.Helicopter2,
["MONK1"] = TR2Type.MonkWithLongStick,
["MONK2"] = TR2Type.MonkWithKnifeStick,
["MOVABLE_BLOCK"] = TR2Type.PushBlock1,
["MOVABLE_BLOCK2"] = TR2Type.PushBlock2,
["MOVABLE_BLOCK3"] = TR2Type.PushBlock3,
["MOVABLE_BLOCK4"] = TR2Type.PushBlock4,
["OILDRUMS"] = TR2Type.RollingStorageDrums,
["PASSPORT_OPTION"] = TR2Type.PassportOpen_M_H,
        ["PENDULUM"] = TR2Type.SandbagOrBallsack,
["PICKUP_OPTION1"] = TR2Type.Quest1_M_H,
["PICKUP_OPTION2"] = TR2Type.Quest2_M_H,
["PLAYER_1"] = TR2Type.CutsceneActor4,
["PLAYER_2"] = TR2Type.CutsceneActor5,
["PLAYER_3"] = TR2Type.CutsceneActor6,
["PLAYER_5"] = TR2Type.CutsceneActor8,
["PLAYER_6"] = TR2Type.CutsceneActor9,
["PLAYER_7"] = TR2Type.CutsceneActor10,
["PLAYER_8"] = TR2Type.CutsceneActor11,
["PLAYER4"] = TR2Type.CutsceneActor7,
["PUSH_SWITCH"] = TR2Type.PushButtonSwitch,
["PUZZLE_DONE1"] = TR2Type.PuzzleDone1,
["PUZZLE_DONE2"] = TR2Type.PuzzleDone2,
["PUZZLE_DONE3"] = TR2Type.PuzzleDone3,
["PUZZLE_DONE4"] = TR2Type.PuzzleDone4,
["PUZZLE_HOLE1"] = TR2Type.PuzzleHole1,
["PUZZLE_HOLE2"] = TR2Type.PuzzleHole2,
["PUZZLE_HOLE3"] = TR2Type.PuzzleHole3,
["PUZZLE_HOLE4"] = TR2Type.PuzzleHole4,
["PUZZLE_OPTION1"] = TR2Type.Puzzle1_M_H,
["PUZZLE_OPTION2"] = TR2Type.Puzzle2_M_H,
["PUZZLE_OPTION3"] = TR2Type.Puzzle3_M_H,
["PUZZLE_OPTION4"] = TR2Type.Puzzle4_M_H,
["ROCKET"] = TR2Type.GrenadeProjectile_H,
["ROLLING_BALL"] = TR2Type.RollingBall,
["SHARK"] = TR2Type.Shark,
["SKIDMAN"] = TR2Type.MercSnowmobDriver,
["SKIDOO"] = TR2Type.RedSnowmobile,
["SMASH_ICE"] = TR2Type.BreakableWindow2,
["SMASH_WINDOW"] = TR2Type.BreakableWindow1,
["SPIDER"] = TR2Type.Spider,
["SPIKE_WALL"] = TR2Type.SpikyWall,
["SPIKES"] = TR2Type.TeethSpikesOrGlassShards,
["SPINNING_BLADE"] = TR2Type.RollingSpindle,
["SPRING_BOARD"] = TR2Type.BouncePad,
["SWING_BOX"] = TR2Type.SwingingBoxOrBall,
["SWITCH_TYPE1"] = TR2Type.WallSwitch,
["SWITCH_TYPE2"] = TR2Type.UnderwaterSwitch,
["TEETH_TRAP"] = TR2Type.SlammingDoor,
["TIGER"] = TR2Type.TigerOrSnowLeopard,
["TRAPDOOR"] = TR2Type.Trapdoor1,
["TRAPDOOR2"] = TR2Type.Trapdoor2,
["WORKER1"] = TR2Type.Gunman1,
["WORKER3"] = TR2Type.StickWieldingGoon1,
["WORKER4"] = TR2Type.StickWieldingGoon2,
["YETI"] = TR2Type.Yeti,
};

private static readonly Dictionary<TR2Type, string> _typeToKeyMap;

static TR2MapControl()
{
_typeToKeyMap = _keyToTypeMap.ToDictionary(e => e.Value, e => e.Key);
}
}
13 changes: 13 additions & 0 deletions TRLevelControl/Control/TR2/TR2PDPControl.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
using TRLevelControl.Build;
using TRLevelControl.Model;

namespace TRLevelControl;

public class TR2PDPControl : TRPDPControlBase<TR2Type>
{
public TR2PDPControl(ITRLevelObserver observer = null)
: base(observer) { }

protected override TRModelBuilder<TR2Type> CreateBuilder()
=> new(TRGameVersion.TR2, TRModelDataType.PDP);
}
Original file line number Diff line number Diff line change
Expand Up @@ -168,13 +168,13 @@ private void WriteMeshData(TRLevelWriter writer)

private void ReadModelData(TRLevelReader reader)
{
TRModelBuilder<TR3Type> builder = new(TRGameVersion.TR3, _observer);
TRModelBuilder<TR3Type> builder = new(TRGameVersion.TR3, TRModelDataType.Level, _observer);
_level.Models = builder.ReadModelData(reader, _meshBuilder);
}

private void WriteModelData(TRLevelWriter writer)
{
TRModelBuilder<TR3Type> builder = new(TRGameVersion.TR3, _observer);
TRModelBuilder<TR3Type> builder = new(TRGameVersion.TR3, TRModelDataType.Level, _observer);
builder.WriteModelData(writer, _level.Models);
}

Expand Down
Loading

0 comments on commit 201d2e9

Please sign in to comment.