Skip to content

Commit

Permalink
LostArtefacts#397 TR1 OG SFX Fixes
Browse files Browse the repository at this point in the history
Restores the death animation SFX for Pierre, Larson, SkateboardKid and Natla.
  • Loading branch information
lahm86 committed Nov 12, 2022
1 parent ec8196b commit f9d7fd1
Show file tree
Hide file tree
Showing 8 changed files with 157 additions and 25 deletions.
7 changes: 7 additions & 0 deletions TRModelTransporter/Handlers/Sound/SoundUtilities.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,13 @@ namespace TRModelTransporter.Handlers
{
public static class SoundUtilities
{
public static void ImportLevelSound(TRLevel baseLevel, TRLevel sourceLevel, short[] soundIDs)
{
TR1PackedSound sound = BuildPackedSound(sourceLevel.SoundMap, sourceLevel.SoundDetails, sourceLevel.SampleIndices, sourceLevel.Samples, soundIDs);
new SoundUnpacker().Unpack(sound, baseLevel);
SoundUtilities.ResortSoundIndices(baseLevel);
}

public static TR1PackedSound BuildPackedSound(short[] soundMap, TRSoundDetails[] soundDetails, uint[] sampleIndices, byte[] wavSamples, short[] hardcodedSounds)
{
if (hardcodedSounds == null || hardcodedSounds.Length == 0)
Expand Down
2 changes: 2 additions & 0 deletions TRModelTransporter/Transport/AbstractTRModelExporter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@ public D Export(L level, E entity)
};
}

PreDefinitionCreation(level, entity);
D definition = CreateModelDefinition(level, entity);
ExportDependencies(definition);
ModelExportReady(definition);
Expand All @@ -84,6 +85,7 @@ public D Export(L level, E entity)
return definition;
}

protected virtual void PreDefinitionCreation(L level, E modelEntity) { }
protected abstract D CreateModelDefinition(L level, E modelEntity);
protected virtual void ModelExportReady(D definition) { }

Expand Down
107 changes: 107 additions & 0 deletions TRModelTransporter/Transport/TR1/TR1ModelExporter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,26 @@ protected override TR1ModelDefinition CreateModelDefinition(TRLevel level, TREnt
return definition;
}

protected override void PreDefinitionCreation(TRLevel level, TREntities modelEntity)
{
switch (modelEntity)
{
case TREntities.Pierre:
AmendPierreGunshot(level);
AmendPierreDeath(level);
break;
case TREntities.Larson:
AmendLarsonDeath(level);
break;
case TREntities.SkateboardKid:
AmendSkaterBoyDeath(level);
break;
case TREntities.Natla:
AmendNatlaDeath(level);
break;
}
}

protected override void ModelExportReady(TR1ModelDefinition definition)
{
switch (definition.Entity)
Expand All @@ -71,5 +91,92 @@ protected override void ModelExportReady(TR1ModelDefinition definition)
break;
}
}

public static void AmendPierreGunshot(TRLevel level)
{
TRModel model = Array.Find(level.Models, m => m.ID == (uint)TREntities.Pierre);
// Get his shooting animation
TRAnimation anim = level.Animations[model.Animation + 10];
List<TRAnimCommand> cmds = level.AnimCommands.ToList();
anim.AnimCommand = (ushort)cmds.Count;
anim.NumAnimCommands = 1;

// On the 2nd frame, play SFX 44 (magnums)
cmds.Add(new TRAnimCommand { Value = 5 });
cmds.Add(new TRAnimCommand { Value = (short)(anim.FrameStart + 1) });
cmds.Add(new TRAnimCommand { Value = 44 });

level.AnimCommands = cmds.ToArray();
level.NumAnimCommands = (uint)cmds.Count;
}

public static void AmendPierreDeath(TRLevel level)
{
TRModel model = Array.Find(level.Models, m => m.ID == (uint)TREntities.Pierre);
// Get his death animation
TRAnimation anim = level.Animations[model.Animation + 12];
anim.NumAnimCommands++;

List<TRAnimCommand> cmds = level.AnimCommands.ToList();
anim.AnimCommand = (ushort)cmds.Count;
cmds.Add(new TRAnimCommand { Value = 4 }); // Death

// On the 61st frame, play SFX 159 (death)
cmds.Add(new TRAnimCommand { Value = 5 });
cmds.Add(new TRAnimCommand { Value = (short)(anim.FrameStart + 60) });
cmds.Add(new TRAnimCommand { Value = 159 });

level.AnimCommands = cmds.ToArray();
level.NumAnimCommands = (uint)cmds.Count;
}

public static void AmendLarsonDeath(TRLevel level)
{
TRModel model = Array.Find(level.Models, m => m.ID == (uint)TREntities.Larson);
// Get his death animation
TRAnimation anim = level.Animations[model.Animation + 15];
anim.NumAnimCommands++;

List<TRAnimCommand> cmds = level.AnimCommands.ToList();
anim.AnimCommand = (ushort)cmds.Count;
cmds.Add(new TRAnimCommand { Value = 4 }); // Death

// On the 2nd frame, play SFX 158 (death)
cmds.Add(new TRAnimCommand { Value = 5 });
cmds.Add(new TRAnimCommand { Value = (short)(anim.FrameStart + 1) });
cmds.Add(new TRAnimCommand { Value = 158 });

level.AnimCommands = cmds.ToArray();
level.NumAnimCommands = (uint)cmds.Count;
}

public static void AmendSkaterBoyDeath(TRLevel level)
{
TRModel model = Array.Find(level.Models, m => m.ID == (uint)TREntities.SkateboardKid);
// Get his death animation
TRAnimation anim = level.Animations[model.Animation + 13];
// Play the death sound on the 2nd frame (doesn't work on the 1st, which is OG).
level.AnimCommands[anim.AnimCommand + 2].Value++;
}

public static void AmendNatlaDeath(TRLevel level)
{
TRModel model = Array.Find(level.Models, m => m.ID == (uint)TREntities.Natla);
// Get her death animation
TRAnimation anim = level.Animations[model.Animation + 13];
anim.NumAnimCommands++;

List<TRAnimCommand> cmds = level.AnimCommands.ToList();
anim.AnimCommand = (ushort)cmds.Count;
cmds.Add(new TRAnimCommand { Value = 4 }); // Death

// On the 5th frame, play SFX 160 (death)
cmds.Add(new TRAnimCommand { Value = 5 });
cmds.Add(new TRAnimCommand { Value = (short)(anim.FrameStart + 4) });
cmds.Add(new TRAnimCommand { Value = 160 });

level.AnimCommands = cmds.ToArray();
level.NumAnimCommands = (uint)cmds.Count;
}
}
}
58 changes: 37 additions & 21 deletions TRRandomizerCore/Randomizers/TR1/TR1EnemyRandomizer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,19 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Numerics;
using TRFDControl;
using TRFDControl.FDEntryTypes;
using TRFDControl.Utilities;
using TRGE.Core;
using TRLevelReader;
using TRLevelReader.Helpers;
using TRLevelReader.Model;
using TRLevelReader.Model.Enums;
using TRModelTransporter.Handlers;
using TRModelTransporter.Model.Sound;
using TRModelTransporter.Transport;
using TRRandomizerCore.Helpers;
using TRRandomizerCore.Levels;
Expand Down Expand Up @@ -716,11 +720,8 @@ private void RandomizeEnemies(TR1CombinedLevel level, EnemyRandomizationCollecti
level.Data.NumEntities++;
}

// Fix Pierre's silent guns
if (enemies.Available.Contains(TREntities.Pierre))
{
FixPierreGunshot(level);
}
// Fix missing OG animation SFX
FixEnemyAnimations(level);

// Add extra ammo based on this level's difficulty
if (Settings.CrossLevelEnemies && ScriptEditor.Edition.IsCommunityPatch && level.Script.RemovesWeapons)
Expand Down Expand Up @@ -1122,24 +1123,39 @@ private void RandomizeMeshes(TR1CombinedLevel level, List<TREntities> availableE
}
}

private void FixPierreGunshot(TR1CombinedLevel level)
private void FixEnemyAnimations(TR1CombinedLevel level)
{
TRModel pierre = Array.Find(level.Data.Models, m => m.ID == (uint)TREntities.Pierre);
if (pierre != null)
// Model transport will handle these missing SFX by default, but we need to fix them in
// the levels where these enemies already exist.
List<TREntities> entities = level.Data.Models.Select(m => (TREntities)m.ID).ToList();

if (entities.Contains(TREntities.Pierre)
&& (level.Is(TRLevelNames.FOLLY) || level.Is(TRLevelNames.COLOSSEUM) || level.Is(TRLevelNames.CISTERN) || level.Is(TRLevelNames.TIHOCAN)))
{
TR1ModelExporter.AmendPierreGunshot(level.Data);
TR1ModelExporter.AmendPierreDeath(level.Data);

// Non one-shot-Pierre levels won't have the death sound by default, so borrow it from ToT.
if (level.Data.SoundMap[159] == -1)
{
TRLevel tihocan = new TR1LevelReader().ReadLevel(Path.Combine(BackupPath, TRLevelNames.TIHOCAN));
SoundUtilities.ImportLevelSound(level.Data, tihocan, new short[] { 159 });
}
}

if (entities.Contains(TREntities.Larson) && level.Is(TRLevelNames.SANCTUARY))
{
TR1ModelExporter.AmendLarsonDeath(level.Data);
}

if (entities.Contains(TREntities.SkateboardKid) && level.Is(TRLevelNames.MINES))
{
TR1ModelExporter.AmendSkaterBoyDeath(level.Data);
}

if (entities.Contains(TREntities.Natla) && level.Is(TRLevelNames.PYRAMID))
{
// Get Pierre's shooting animation
TRAnimation anim = level.Data.Animations[pierre.Animation + 10];
List<TRAnimCommand> cmds = level.Data.AnimCommands.ToList();
anim.AnimCommand = (ushort)cmds.Count;
anim.NumAnimCommands = 1;

// On the second frame, play SFX 44 (magnums)
cmds.Add(new TRAnimCommand { Value = 5 });
cmds.Add(new TRAnimCommand { Value = (short)(anim.FrameStart + 1) });
cmds.Add(new TRAnimCommand { Value = 44 });

level.Data.AnimCommands = cmds.ToArray();
level.Data.NumAnimCommands = (uint)cmds.Count;
TR1ModelExporter.AmendNatlaDeath(level.Data);
}
}

Expand Down
2 changes: 1 addition & 1 deletion TRRandomizerCore/Resources/TR1/Models/Larson/Data.json

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion TRRandomizerCore/Resources/TR1/Models/Natla/Data.json

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion TRRandomizerCore/Resources/TR1/Models/Pierre/Data.json

Large diffs are not rendered by default.

Large diffs are not rendered by default.

0 comments on commit f9d7fd1

Please sign in to comment.