Skip to content

Commit

Permalink
Fix enemy-dependent secrets
Browse files Browse the repository at this point in the history
  • Loading branch information
lahm86 committed Jan 25, 2024
1 parent 2e7c6e0 commit b716097
Show file tree
Hide file tree
Showing 10 changed files with 90 additions and 14 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
## [Unreleased](https://github.com/LostArtefacts/TR-Rando/compare/V1.8.3...master) - xxxx-xx-xx
- fixed item locking logic so that secrets that rely on specific enemies will always be obtainable (#570)

## [V1.8.3](https://github.com/LostArtefacts/TR-Rando/compare/V1.8.2...V1.8.3) - 2024-01-21
- fixed incorrect items sometimes being allocated as secret rewards in Thames Wharf (#597)
Expand Down
4 changes: 3 additions & 1 deletion TRRandomizerCore/Editors/TR2RandoEditor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,7 @@ protected override void SaveImpl(AbstractTRScriptEditor scriptEditor, TRSaveMoni
if (Settings.DevelopmentMode)
{
(tr23ScriptEditor.Script as TR23Script).LevelSelectEnabled = true;
(tr23ScriptEditor.Script as TR23Script).DozyEnabled = true;
scriptEditor.SaveScript();
}

Expand Down Expand Up @@ -265,7 +266,8 @@ protected override void SaveImpl(AbstractTRScriptEditor scriptEditor, TRSaveMoni
BackupPath = backupDirectory,
SaveMonitor = monitor,
Settings = Settings,
TextureMonitor = textureMonitor
TextureMonitor = textureMonitor,
ItemFactory = itemFactory,
}.Randomize(Settings.EnemySeed);
}

Expand Down
5 changes: 5 additions & 0 deletions TRRandomizerCore/Helpers/ItemFactory.cs
Original file line number Diff line number Diff line change
Expand Up @@ -119,4 +119,9 @@ public bool IsItemLocked(string level, int itemIndex)
{
return _lockedItems.ContainsKey(level) && _lockedItems[level].Contains(itemIndex);
}

public List<int> GetLockedItems(string level)
{
return _lockedItems.ContainsKey(level) ? new(_lockedItems[level]) : new();
}
}
7 changes: 5 additions & 2 deletions TRRandomizerCore/Randomizers/Shared/SecretPicker.cs
Original file line number Diff line number Diff line change
Expand Up @@ -214,7 +214,7 @@ private void ResetEvaluators(int totalCount)
_distanceDivisor = Math.Max(_minDistanceDivisor, totalCount);
}

public void FinaliseSecretPool(IEnumerable<Location> usedLocations, string level)
public void FinaliseSecretPool(IEnumerable<Location> usedLocations, string level, Func<int, List<int>> lockedItemAction)
{
// Secrets in packs are permitted to enforce level state
if (usedLocations.Any(l => l.LevelState == LevelState.Mirrored))
Expand All @@ -229,7 +229,10 @@ public void FinaliseSecretPool(IEnumerable<Location> usedLocations, string level
foreach (Location location in usedLocations.Where(l => l.EntityIndex != -1))
{
// Indicates that a secret relies on another item to remain in its original position
ItemFactory.LockItem(level, location.EntityIndex);
foreach (int itemIndex in lockedItemAction(location.EntityIndex))
{
ItemFactory.LockItem(level, itemIndex);
}
}

#if DEBUG
Expand Down
2 changes: 1 addition & 1 deletion TRRandomizerCore/Randomizers/TR1/TR1SecretRandomizer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -475,7 +475,7 @@ private void RandomizeSecrets(TR1CombinedLevel level, List<TR1Type> pickupTypes,
floorData.WriteToLevel(level.Data);

AddDamageControl(level, pickedLocations);
_secretPicker.FinaliseSecretPool(pickedLocations, level.Name);
_secretPicker.FinaliseSecretPool(pickedLocations, level.Name, itemIndex => new() { itemIndex });
}

private void AddDamageControl(TR1CombinedLevel level, List<Location> locations)
Expand Down
21 changes: 17 additions & 4 deletions TRRandomizerCore/Randomizers/TR2/TR2EnemyRandomizer.cs
Original file line number Diff line number Diff line change
@@ -1,10 +1,7 @@
using System.Diagnostics;
using System.Numerics;
using TRFDControl;
using TRFDControl.FDEntryTypes;
using TRFDControl.Utilities;
using TRGE.Core;
using TRLevelControl;
using TRLevelControl.Helpers;
using TRLevelControl.Model;
using TRModelTransporter.Packing;
Expand All @@ -25,6 +22,7 @@ public class TR2EnemyRandomizer : BaseTR2Randomizer

internal int MaxPackingAttempts { get; set; }
internal TR2TextureMonitorBroker TextureMonitor { get; set; }
public ItemFactory<TR2Entity> ItemFactory { get; set; }

public TR2EnemyRandomizer()
{
Expand Down Expand Up @@ -229,6 +227,20 @@ private EnemyTransportCollection SelectCrossLevelEnemies(TR2CombinedLevel level,
}
}

// Some secrets may have locked enemies in place - we must retain those types
foreach (int itemIndex in ItemFactory.GetLockedItems(level.Name))
{
TR2Entity item = level.Data.Entities[itemIndex];
if (TR2TypeUtilities.IsEnemyType(item.TypeID))
{
List<TR2Type> family = TR2TypeUtilities.GetFamily(TR2TypeUtilities.GetAliasForLevel(level.Name, item.TypeID));
if (!newEntities.Any(family.Contains))
{
newEntities.Add(family[_generator.Next(0, family.Count)]);
}
}
}

// Get all other candidate supported enemies
List<TR2Type> allEnemies = TR2TypeUtilities.GetCandidateCrossLevelEnemies()
.FindAll(e => TR2EnemyUtilities.IsEnemySupported(level.Name, e, difficulty, Settings.ProtectMonks));
Expand Down Expand Up @@ -607,7 +619,8 @@ private void RandomizeEnemies(TR2CombinedLevel level, EnemyRandomizationCollecti
int enemyIndex = level.Data.Entities.IndexOf(currentEntity);

// If it's an existing enemy that has to remain in the same spot, skip it
if (TR2EnemyUtilities.IsEnemyRequired(level.Name, currentEntityType))
if (TR2EnemyUtilities.IsEnemyRequired(level.Name, currentEntityType)
|| ItemFactory.IsItemLocked(level.Name, enemyIndex))
{
continue;
}
Expand Down
20 changes: 19 additions & 1 deletion TRRandomizerCore/Randomizers/TR2/TR2SecretRandomizer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -146,7 +146,25 @@ private void RandomizeSecrets(TR2CombinedLevel level)
}

AddDamageControl(level, pickedLocations);
_secretPicker.FinaliseSecretPool(pickedLocations, level.Name);
_secretPicker.FinaliseSecretPool(pickedLocations, level.Name, itemIndex => GetDependentLockedItems(level, itemIndex));
}

private static List<int> GetDependentLockedItems(TR2CombinedLevel level, int itemIndex)
{
// We may be locking an enemy, so be sure to also lock their pickups.
List<int> items = new() { itemIndex };

if (TR2TypeUtilities.IsEnemyType(level.Data.Entities[itemIndex].TypeID))
{
Location enemyLocation = level.Data.Entities[itemIndex].GetLocation();
List<TR2Entity> pickups = level.Data.Entities
.FindAll(e => TR2TypeUtilities.IsAnyPickupType(e.TypeID))
.FindAll(e => e.GetLocation().IsEquivalent(enemyLocation));

items.AddRange(pickups.Select(p => level.Data.Entities.IndexOf(p)));
}

return items;
}

private void PlaceSecret(TR2Entity entity, TR2Type type, Location location)
Expand Down
18 changes: 17 additions & 1 deletion TRRandomizerCore/Randomizers/TR3/TR3EnemyRandomizer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -189,6 +189,20 @@ private EnemyTransportCollection SelectCrossLevelEnemies(TR3CombinedLevel level)
}
}

// Some secrets may have locked enemies in place - we must retain those types
foreach (int itemIndex in ItemFactory.GetLockedItems(level.Name))
{
TR3Entity item = level.Data.Entities[itemIndex];
if (TR3TypeUtilities.IsEnemyType(item.TypeID))
{
List<TR3Type> family = TR3TypeUtilities.GetFamily(TR3TypeUtilities.GetAliasForLevel(level.Name, item.TypeID));
if (!newEntities.Any(family.Contains))
{
newEntities.Add(family[_generator.Next(0, family.Count)]);
}
}
}

if (!Settings.DocileWillard || Settings.OneEnemyMode || Settings.IncludedEnemies.Count < newEntities.Capacity)
{
// Willie isn't excludable in his own right because supporting a Willie-only game is impossible
Expand Down Expand Up @@ -445,9 +459,11 @@ private void RandomizeEnemies(TR3CombinedLevel level, EnemyRandomizationCollecti
{
TR3Type currentEntityType = currentEntity.TypeID;
TR3Type newEntityType = currentEntityType;
int enemyIndex = level.Data.Entities.IndexOf(currentEntity);

// If it's an existing enemy that has to remain in the same spot, skip it
if (TR3EnemyUtilities.IsEnemyRequired(level.Name, currentEntityType))
if (TR3EnemyUtilities.IsEnemyRequired(level.Name, currentEntityType)
|| ItemFactory.IsItemLocked(level.Name, enemyIndex))
{
continue;
}
Expand Down
20 changes: 19 additions & 1 deletion TRRandomizerCore/Randomizers/TR3/TR3SecretRandomizer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -402,7 +402,25 @@ private void RandomizeSecrets(TR3CombinedLevel level, List<TR3Type> pickupTypes,
floorData.WriteToLevel(level.Data);

AddDamageControl(level, pickupTypes, pickedLocations);
_secretPicker.FinaliseSecretPool(pickedLocations, level.Name);
_secretPicker.FinaliseSecretPool(pickedLocations, level.Name, itemIndex => GetDependentLockedItems(level, itemIndex));
}

private static List<int> GetDependentLockedItems(TR3CombinedLevel level, int itemIndex)
{
// We may be locking an enemy, so be sure to also lock their pickups.
List<int> items = new() { itemIndex };

if (TR3TypeUtilities.IsEnemyType(level.Data.Entities[itemIndex].TypeID))
{
Location enemyLocation = level.Data.Entities[itemIndex].GetLocation();
List<TR3Entity> pickups = level.Data.Entities
.FindAll(e => TR3TypeUtilities.IsAnyPickupType(e.TypeID))
.FindAll(e => e.GetLocation().IsEquivalent(enemyLocation));

items.AddRange(pickups.Select(p => level.Data.Entities.IndexOf(p)));
}

return items;
}

private void AddDamageControl(TR3CombinedLevel level, List<TR3Type> pickupTypes, List<Location> locations)
Expand Down
6 changes: 3 additions & 3 deletions TRRandomizerCore/Resources/TR2/Locations/locations.json
Original file line number Diff line number Diff line change
Expand Up @@ -1145,7 +1145,7 @@
"Z": 22580,
"Room": 91,
"Difficulty": "Hard",
"EntityIndex": 79,
"EntityIndex": 78,
"PackID": "apel",
"RequiresGlitch": true
},
Expand Down Expand Up @@ -1597,7 +1597,7 @@
"X": 66560,
"Y": -840,
"Z": 25692,
"Room": 98,
"Room": 103,
"Difficulty": "Hard",
"PackID": "apel",
"RequiresGlitch": true
Expand Down Expand Up @@ -1635,7 +1635,7 @@
"X": 41877,
"Y": -3761,
"Z": 31643,
"Room": 130,
"Room": 135,
"Difficulty": "Hard",
"PackID": "apel",
"RequiresGlitch": true
Expand Down

0 comments on commit b716097

Please sign in to comment.