Skip to content

Commit

Permalink
Add reward stack option (LostArtefacts#689)
Browse files Browse the repository at this point in the history
  • Loading branch information
lahm86 authored May 28, 2024
1 parent 6e30f2e commit af49c76
Show file tree
Hide file tree
Showing 27 changed files with 513 additions and 506 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@
- added support for TR1X V4 (#626)
- added Lara's assault course outfit in TR2 for outfit randomization (#672)
- added gun holsters to Lara's robe outfit in TR2 (#672)
- added an option to stack rewards with secrets in TR1 and TR3, rather than using reward rooms (#687)
- added separate secret audio for TR1 and TR3 when not using reward rooms (#687)
- fixed a key item softlock in Crash Site (#662)
- fixed incorrect item and mesh positions in Home Sweet Home when mirrored (#676)
- fixed uncontrolled SFX in gym/assault course levels not being linked to the correct setting (#684)
Expand Down
3 changes: 3 additions & 0 deletions TRRandomizerCore/Editors/RandomizerSettings.cs
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,7 @@ public class RandomizerSettings
public bool UseKillableClonePierres { get; set; }
public bool GlitchedSecrets { get; set; }
public bool GuaranteeSecrets { get; set; }
public TRSecretRewardMode SecretRewardMode { get; set; }
public bool UseRewardRoomCameras { get; set; }
public bool UseRandomSecretModels { get; set; }
public TRSecretCountMode SecretCountMode { get; set; }
Expand Down Expand Up @@ -197,6 +198,7 @@ public void ApplyConfig(Config config)
HardSecrets = config.GetBool(nameof(HardSecrets));
GlitchedSecrets = config.GetBool(nameof(GlitchedSecrets));
GuaranteeSecrets = config.GetBool(nameof(GuaranteeSecrets), true);
SecretRewardMode = (TRSecretRewardMode)config.GetEnum(nameof(SecretRewardMode), typeof(TRSecretRewardMode), TRSecretRewardMode.Room);
UseRewardRoomCameras = config.GetBool(nameof(UseRewardRoomCameras), true);
UseRandomSecretModels = config.GetBool(nameof(UseRandomSecretModels));
SecretCountMode = (TRSecretCountMode)config.GetEnum(nameof(SecretCountMode), typeof(TRSecretCountMode), TRSecretCountMode.Default);
Expand Down Expand Up @@ -364,6 +366,7 @@ public void StoreConfig(Config config)
config[nameof(HardSecrets)] = HardSecrets;
config[nameof(GlitchedSecrets)] = GlitchedSecrets;
config[nameof(GuaranteeSecrets)] = GuaranteeSecrets;
config[nameof(SecretRewardMode)] = SecretRewardMode;
config[nameof(UseRewardRoomCameras)] = UseRewardRoomCameras;
config[nameof(UseRandomSecretModels)] = UseRandomSecretModels;
config[nameof(SecretCountMode)] = SecretCountMode;
Expand Down
2 changes: 2 additions & 0 deletions TRRandomizerCore/Editors/TR1RemasteredEditor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
using TRLevelControl.Model;
using TRRandomizerCore.Helpers;
using TRRandomizerCore.Randomizers;
using TRRandomizerCore.Secrets;

namespace TRRandomizerCore.Editors;

Expand All @@ -19,6 +20,7 @@ protected override void ApplyConfig(Config config)
Settings.FixOGBugs = false;
Settings.ReplaceRequiredEnemies = false;
Settings.SwapEnemyAppearance = false;
Settings.SecretRewardMode = TRSecretRewardMode.Stack;
}

protected override int GetSaveTarget(int numLevels)
Expand Down
2 changes: 2 additions & 0 deletions TRRandomizerCore/Editors/TR3RemasteredEditor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
using TRLevelControl.Model;
using TRRandomizerCore.Helpers;
using TRRandomizerCore.Randomizers;
using TRRandomizerCore.Secrets;

namespace TRRandomizerCore.Editors;

Expand All @@ -19,6 +20,7 @@ protected override void ApplyConfig(Config config)
Settings.FixOGBugs = false;
Settings.ReplaceRequiredEnemies = false;
Settings.SwapEnemyAppearance = false;
Settings.SecretRewardMode = TRSecretRewardMode.Stack;
}

protected override int GetSaveTarget(int numLevels)
Expand Down
5 changes: 2 additions & 3 deletions TRRandomizerCore/Randomizers/Shared/AudioAllocator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -145,10 +145,9 @@ public void RandomizeSecretTracks(FDControl floorData, ushort defaultTrackID)
};

List<FDTriggerEntry> triggers = floorData.GetSecretTriggers(i);
foreach (FDTriggerEntry trigger in triggers)
foreach (FDTriggerEntry trigger in triggers.Where(t => t.Mask == TRConsts.FullMask))
{
FDActionItem currentMusicAction = trigger.Actions.Find(a => a.Action == FDTrigAction.PlaySoundtrack);
if (currentMusicAction == null)
if (!trigger.Actions.Any(a => a.Action == FDTrigAction.PlaySoundtrack))
{
trigger.Actions.Add(musicAction);
}
Expand Down
157 changes: 156 additions & 1 deletion TRRandomizerCore/Randomizers/Shared/SecretArtefactPlacer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,9 @@

namespace TRRandomizerCore.Randomizers;

public class SecretArtefactPlacer<T>
public class SecretArtefactPlacer<T, E>
where T : Enum
where E : TREntity<T>, new()
{
private static readonly string _invalidLocationMsg = "Cannot place a nonvalidated secret where a trigger already exists - {0} [X={1}, Y={2}, Z={3}, R={4}]";
private static readonly string _trapdoorLocationMsg = "Cannot place a secret on the same sector as a bridge/trapdoor - {0} [X={1}, Y={2}, Z={3}, R={4}]";
Expand All @@ -24,6 +25,7 @@ public class SecretArtefactPlacer<T>
private static readonly int _triggerEdgeLimit = (int)Math.Ceiling(TRConsts.Step4 / 10d); // Within ~10% of a tile edge, triggers will be copied into neighbours

public RandomizerSettings Settings { get; set; }
public ItemFactory<E> ItemFactory { get; set; }

private TRGameVersion _version;
private string _levelName;
Expand Down Expand Up @@ -356,4 +358,157 @@ private void CreateSecretTrigger(TRSecretPlacement<T> secret, short room, TRRoom

_floorData[sector.FDIndex].Add(trigger);
}

public TRSecretRoom<E> MakePlaceholderRewardRoom(TRGameVersion gameVersion, string levelName, int secretCount, List<E> allItems)
{
if (Settings.SecretRewardMode == TRSecretRewardMode.Stack && !Settings.DevelopmentMode)
{
return null;
}

TRSecretRoom<E> rewardRoom = null;
string mappingPath = $@"Resources\{gameVersion}\SecretMapping\{levelName}-SecretMapping.json";
if (File.Exists(mappingPath))
{
int requiredDoors = (int)Math.Ceiling((double)secretCount / TRConsts.MaskBits);
rewardRoom = new()
{
DoorIndices = new()
};

for (int i = 0; i < requiredDoors; i++)
{
E door = ItemFactory.CreateItem(levelName, allItems);
rewardRoom.DoorIndices.Add(allItems.IndexOf(door));
}
}

return rewardRoom;
}

public void CreateRewardRoom(string levelName,
TRSecretRoom<E> placeholder,
TRSecretRoom<E> finalRoom,
List<E> allItems,
List<TRCamera> allCameras,
T cameraTargetType,
List<int> rewardIndices,
FDControl floorData,
short roomIndex,
int secretCount,
Func<T, bool> isTrapdoor)
{
// Convert the temporary doors
finalRoom.DoorIndices = placeholder.DoorIndices;
for (int i = 0; i < finalRoom.DoorIndices.Count; i++)
{
int doorIndex = finalRoom.DoorIndices[i];
E door = finalRoom.Doors[i];
if (door.Room < 0)
{
door.Room = roomIndex;
}
allItems[doorIndex] = door;

if (isTrapdoor(door.TypeID))
{
TRRoomSector sector = _sectorGetter(door.GetFloorLocation(_sectorGetter));
if (sector.FDIndex == 0)
{
floorData.CreateFloorData(sector);
}

floorData[sector.FDIndex].Add(new FDTriggerEntry
{
TrigType = FDTrigType.Dummy,
Actions = new()
{
new()
{
Parameter = (short)doorIndex
}
}
});
}
}

// Spread the rewards out fairly evenly across each defined position in the new room.
int rewardPositionCount = finalRoom.RewardPositions.Count;
for (int i = 0; i < rewardIndices.Count; i++)
{
E item = allItems[rewardIndices[i]];
item.SetLocation(finalRoom.RewardPositions[i % rewardPositionCount]);
item.Room = roomIndex;
}

// #238 Make the required number of cameras. Because of the masks, we need
// a camera per counted secret otherwise it only shows once.
if (Settings.UseRewardRoomCameras && finalRoom.Cameras != null)
{
finalRoom.CameraIndices = new();
for (int i = 0; i < secretCount; i++)
{
finalRoom.CameraIndices.Add(allCameras.Count);
allCameras.Add(finalRoom.Cameras[i % finalRoom.Cameras.Count]);
}

short cameraTarget;
if (finalRoom.CameraTarget != null && ItemFactory.CanCreateItem(levelName, allItems))
{
E target = ItemFactory.CreateItem(levelName, allItems, finalRoom.CameraTarget);
target.TypeID = cameraTargetType;
cameraTarget = (short)allItems.IndexOf(target);
}
else
{
cameraTarget = (short)finalRoom.DoorIndices[0];
}

// Get each trigger created for each secret index and add the camera, provided
// there isn't any existing camera actions.
for (int i = 0; i < secretCount; i++)
{
List<FDTriggerEntry> secretTriggers = floorData.GetSecretTriggers(i);
foreach (FDTriggerEntry trigger in secretTriggers)
{
if (trigger.Actions.Find(a => a.Action == FDTrigAction.Camera) == null)
{
trigger.Actions.Add(new()
{
Action = FDTrigAction.Camera,
CamAction = new()
{
Timer = 4
},
Parameter = (short)finalRoom.CameraIndices[i]
});
trigger.Actions.Add(new()
{
Action = FDTrigAction.LookAtItem,
Parameter = cameraTarget
});
}
}
}
}
}

public void CreateRewardStacks(List<E> allItems, List<int> rewardIndices, FDControl floorData)
{
List<E> artefacts = allItems.FindAll(e => floorData.GetEntityTriggers(allItems.IndexOf(e))
.Any(t => t.TrigType == FDTrigType.Pickup && t.Actions.Any(a => a.Action == FDTrigAction.SecretFound)));
if (artefacts.Count == 0)
{
return;
}

List<E>[] rewardClusters = rewardIndices
.Select(i => allItems[i])
.Cluster(artefacts.Count);
for (int i = 0; i < artefacts.Count; i++)
{
Location location = artefacts[i].GetLocation();
rewardClusters[i].ForEach(r => r.SetLocation(location));
}
}
}
12 changes: 10 additions & 2 deletions TRRandomizerCore/Randomizers/TR1/Classic/TR1AudioRandomizer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -40,11 +40,19 @@ public override void Randomize(int seed)
}
}

TR1ScriptEditor script = (ScriptEditor as TR1ScriptEditor);
if (Settings.RandomizeWibble)
{
(ScriptEditor as TR1ScriptEditor).EnablePitchedSounds = true;
ScriptEditor.SaveScript();
script.EnablePitchedSounds = true;
}

if (Settings.SeparateSecretTracks && Settings.SecretRewardMode == Secrets.TRSecretRewardMode.Stack)
{
script.FixSecretsKillingMusic = false;
script.FixSpeechesKillingMusic = false;
}

ScriptEditor.SaveScript();
}

private void RandomizeSoundEffects(TR1CombinedLevel level)
Expand Down
Loading

0 comments on commit af49c76

Please sign in to comment.