Skip to content

Commit

Permalink
LostArtefacts#240 LostArtefacts#241 Enemy Environment Changes
Browse files Browse the repository at this point in the history
Adds basic environment changing support, mainly targeted at mods post-enemy randomization. A new group is available - ConditionalAll - each item in this set has a condition and so will only run if the condition is met (can define OnTrue and/or OnFalse). This currently fixes moves enemy 8 in Jungle if it's not suitable for the crawlspace and handles the handscanner area in HSC if there isn't a prisoner to open the trapdoor.

Environment rando will currently only apply All, ConditionalAll and NonPurist if defined. The UI options remain disabled.
  • Loading branch information
lahm86 committed Dec 7, 2021
1 parent ca18754 commit 7e8a20b
Show file tree
Hide file tree
Showing 11 changed files with 361 additions and 111 deletions.
18 changes: 8 additions & 10 deletions TREnvironmentEditor/EMEditorMapping.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ public class EMEditorMapping
public static readonly EMConverter Converter = new EMConverter();

public EMEditorSet All { get; set; }
public List<EMConditionalSingleEditorSet> ConditionalAll { get; set; }
public EMEditorSet NonPurist { get; set; }
public List<EMEditorSet> Any { get; set; }
public List<List<EMEditorSet>> AllWithin { get; set; }
Expand All @@ -22,6 +23,7 @@ public class EMEditorMapping
public EMEditorMapping()
{
All = new EMEditorSet();
ConditionalAll = new List<EMConditionalSingleEditorSet>();
NonPurist = new EMEditorSet();
Any = new List<EMEditorSet>();
AllWithin = new List<List<EMEditorSet>>();
Expand Down Expand Up @@ -51,6 +53,10 @@ public void AlternateTextures()
{
All.RemapTextures(AlternativeTextures);
}
if (ConditionalAll != null)
{
ConditionalAll.ForEach(s => s.RemapTextures(AlternativeTextures));
}
if (NonPurist != null)
{
NonPurist.RemapTextures(AlternativeTextures);
Expand All @@ -65,19 +71,11 @@ public void AlternateTextures()
}
if (ConditionalAllWithin != null)
{
foreach (EMConditionalEditorSet condSet in ConditionalAllWithin)
{
condSet.OnTrue.ForEach(s => s.RemapTextures(AlternativeTextures));
condSet.OnFalse.ForEach(s => s.RemapTextures(AlternativeTextures));
}
ConditionalAllWithin.ForEach(s => s.RemapTextures(AlternativeTextures));
}
if (OneOf != null)
{
foreach (EMEditorGroupedSet group in OneOf)
{
group.Leader.RemapTextures(AlternativeTextures);
group.Followers.ForEach(s => s.RemapTextures(AlternativeTextures));
}
OneOf.ForEach(s => s.RemapTextures(AlternativeTextures));
}
if (Mirrored != null)
{
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
using TRLevelReader.Model;
using System.Collections.Generic;
using TRLevelReader.Model;

namespace TREnvironmentEditor.Model.Conditions
{
public class EMEntityPropertyCondition : BaseEMCondition
{
public int EntityIndex { get; set; }
public short? EntityType { get; set; }
public List<short> EntityTypes { get; set; } // i.e. is the entity one of these types?
public bool? Invisible { get; set; }
public bool? ClearBody { get; set; }
public short? Intensity1 { get; set; }
Expand All @@ -29,6 +31,10 @@ private bool GetResult(TR2Entity entity)
{
result &= entity.TypeID == EntityType.Value;
}
if (EntityTypes != null)
{
result &= EntityTypes.Contains(entity.TypeID);
}
if (Invisible.HasValue)
{
result &= entity.Invisible == Invisible.Value;
Expand Down
12 changes: 12 additions & 0 deletions TREnvironmentEditor/Model/EMConditionalEditorSet.cs
Original file line number Diff line number Diff line change
Expand Up @@ -18,5 +18,17 @@ public List<EMEditorSet> GetApplicableSets(TR3Level level)
{
return Condition.GetResult(level) ? OnTrue : OnFalse;
}

public void RemapTextures(Dictionary<ushort, ushort> indexMap)
{
if (OnTrue != null)
{
OnTrue.ForEach( s => s.RemapTextures(indexMap));
}
if (OnFalse != null)
{
OnFalse.ForEach(s => s.RemapTextures(indexMap));
}
}
}
}
42 changes: 42 additions & 0 deletions TREnvironmentEditor/Model/EMConditionalSingleEditorSet.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
using System.Collections.Generic;
using TRLevelReader.Model;

namespace TREnvironmentEditor.Model
{
public class EMConditionalSingleEditorSet : ITextureModifier
{
public BaseEMCondition Condition { get; set; }
public EMEditorSet OnTrue { get; set; }
public EMEditorSet OnFalse { get; set; }

public void ApplyToLevel(TR2Level level, IEnumerable<EMType> excludedTypes = null)
{
EMEditorSet edits = Condition.GetResult(level) ? OnTrue : OnFalse;
if (edits != null)
{
edits.ApplyToLevel(level, excludedTypes);
}
}

public void ApplyToLevel(TR3Level level, IEnumerable<EMType> excludedTypes = null)
{
EMEditorSet edits = Condition.GetResult(level) ? OnTrue : OnFalse;
if (edits != null)
{
edits.ApplyToLevel(level, excludedTypes);
}
}

public void RemapTextures(Dictionary<ushort, ushort> indexMap)
{
if (OnTrue != null)
{
OnTrue.RemapTextures(indexMap);
}
if (OnFalse != null)
{
OnFalse.RemapTextures(indexMap);
}
}
}
}
8 changes: 7 additions & 1 deletion TREnvironmentEditor/Model/EMEditorGroupedSet.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@

namespace TREnvironmentEditor.Model
{
public class EMEditorGroupedSet
public class EMEditorGroupedSet : ITextureModifier
{
public EMEditorSet Leader { get; set; }
public List<EMEditorSet> Followers { get; set; }
Expand All @@ -25,5 +25,11 @@ public void ApplyToLevel(TR3Level level, EMEditorSet follower, IEnumerable<EMTyp
follower.ApplyToLevel(level, excludedTypes);
}
}

public void RemapTextures(Dictionary<ushort, ushort> indexMap)
{
Leader.RemapTextures(indexMap);
Followers.ForEach(s => s.RemapTextures(indexMap));
}
}
}
17 changes: 17 additions & 0 deletions TRRandomizerCore/Editors/TR3RandoEditor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,9 @@ protected override int GetSaveTarget(int numLevels)
target += numLevels * 3;
}

// Environment randomizer always runs
target += numLevels;

return target;
}

Expand Down Expand Up @@ -179,6 +182,20 @@ protected override void SaveImpl(AbstractTRScriptEditor scriptEditor, TRSaveMoni
}.Randomize(Settings.EnemySeed);
}

if (!monitor.IsCancelled)
{
monitor.FireSaveStateBeginning(TRSaveCategory.Custom, /*Settings.RandomizeEnvironment ? "Randomizing environment" : */"Applying default environment packs");
new TR3EnvironmentRandomizer
{
ScriptEditor = tr23ScriptEditor,
Levels = levels,
BasePath = wipDirectory,
SaveMonitor = monitor,
Settings = Settings,
TextureMonitor = textureMonitor
}.Randomize(Settings.EnvironmentSeed);
}

if (!monitor.IsCancelled && Settings.RandomizeAudio)
{
monitor.FireSaveStateBeginning(TRSaveCategory.Custom, "Randomizing audio tracks");
Expand Down
7 changes: 7 additions & 0 deletions TRRandomizerCore/Randomizers/TR2/TR2EnvironmentRandomizer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,13 @@ private void ApplyMappingToLevel(TR2CombinedLevel level, EMEditorMapping mapping
// These generally fix OG issues such as problems with box overlaps and
// textures.
mapping.All.ApplyToLevel(level.Data, emptyExclusions);

// Similar to All, but these mods will have conditions configured so may
// or may not apply.
foreach (EMConditionalSingleEditorSet mod in mapping.ConditionalAll)
{
mod.ApplyToLevel(level.Data, emptyExclusions);
}

if (!EnforcedModeOnly || !Settings.PuristMode)
{
Expand Down
13 changes: 2 additions & 11 deletions TRRandomizerCore/Randomizers/TR3/TR3EnemyRandomizer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -435,21 +435,12 @@ private void RandomizeEnemies(TR3CombinedLevel level, EnemyRandomizationCollecti
else if (level.Is(TR3LevelNames.HSC) && currentEntity.Room == 87 && newEntityType != TR3Entities.Prisoner)
{
// #271 The prisoner is needed here to activate the heavy trigger for the trapdoor. If we still have
// prisoners in the pool, ensure one is chosen, otherwise provide a workaround.
// prisoners in the pool, ensure one is chosen. If this isn't the case, environment rando will provide
// a workaround.
if (enemies.Available.Contains(TR3Entities.Prisoner))
{
newEntityType = TR3Entities.Prisoner;
}
else
{
// TODO: Once environment rando is fully implemented, convert this temporary stopgap into a
// conditional check as part of default environment rando.
EMEditorMapping mapping = EMEditorMapping.Get(GetResourcePath(@"TR3\Environment\" + level.Name + "-Environment.json"));
if (mapping != null)
{
mapping.All.ApplyToLevel(level.Data);
}
}
}

// Make sure to convert back to the actual type
Expand Down
157 changes: 151 additions & 6 deletions TRRandomizerCore/Randomizers/TR3/TR3EnvironmentRandomizer.cs
Original file line number Diff line number Diff line change
@@ -1,16 +1,161 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using TREnvironmentEditor;
using TREnvironmentEditor.Model;
using TREnvironmentEditor.Model.Types;
using TRGE.Core;
using TRLevelReader.Helpers;
using TRLevelReader.Model.Enums;
using TRRandomizerCore.Helpers;
using TRRandomizerCore.Levels;
using TRRandomizerCore.Textures;

namespace TRRandomizerCore.Randomizers.TR3
namespace TRRandomizerCore.Randomizers
{
public class TR3EnvironmentRandomizer : BaseTR3Randomizer
{
internal bool EnforcedModeOnly => true;//!Settings.RandomizeEnvironment;
internal TR3TextureMonitorBroker TextureMonitor { get; set; }

private List<EMType> _disallowedTypes;
private List<TR3ScriptedLevel> _levelsToMirror;

public override void Randomize(int seed)
{
throw new NotImplementedException();
_generator = new Random(seed);

_disallowedTypes = new List<EMType>();
if (!Settings.RandomizeWaterLevels)
{
_disallowedTypes.Add(EMType.Flood);
_disallowedTypes.Add(EMType.Drain);
}
if (!Settings.RandomizeSlotPositions)
{
_disallowedTypes.Add(EMType.MoveSlot);
_disallowedTypes.Add(EMType.SwapSlot);
}
if (!Settings.RandomizeLadders)
{
_disallowedTypes.Add(EMType.Ladder);
}

_levelsToMirror = Levels.RandomSelection(_generator, (int)Settings.MirroredLevelCount, exclusions: new HashSet<TR3ScriptedLevel>
{
Levels.Find(l => l.Is(TR3LevelNames.ASSAULT))
});

foreach (TR3ScriptedLevel lvl in Levels)
{
LoadLevelInstance(lvl);

RandomizeEnvironment(_levelInstance);

SaveLevelInstance();

if (!TriggerProgress())
{
break;
}
}
}

private void RandomizeEnvironment(TR3CombinedLevel level)
{
EMEditorMapping mapping = EMEditorMapping.Get(GetResourcePath(@"TR3\Environment\" + level.Name + "-Environment.json"));
if (mapping != null)
{
ApplyMappingToLevel(level, mapping);
}

if (!EnforcedModeOnly && (_levelsToMirror.Contains(level.Script) || (level.IsAssault && Settings.MirrorAssaultCourse)))
{
MirrorLevel(level, mapping);
}
}

private void ApplyMappingToLevel(TR3CombinedLevel level, EMEditorMapping mapping)
{
EMType[] emptyExclusions = new EMType[] { };

// Process enforced packs first. We do not pass disallowed types here.
// These generally fix OG issues such as problems with box overlaps and
// textures.
mapping.All.ApplyToLevel(level.Data, emptyExclusions);

// Similar to All, but these mods will have conditions configured so may
// or may not apply.
foreach (EMConditionalSingleEditorSet mod in mapping.ConditionalAll)
{
mod.ApplyToLevel(level.Data, emptyExclusions);
}

if (!EnforcedModeOnly || !Settings.PuristMode)
{
// Non-purist packs generally make return paths available.
// These are applied only if Purist mode is off or if Environment
// rando is on as a whole, because some other categories may rely
// on these changes having been made.
mapping.NonPurist.ApplyToLevel(level.Data, emptyExclusions);
}

if (EnforcedModeOnly)
{
return;
}

if (mapping.Any.Count > 0)
{
// Pick a random number of packs to apply, but at least 1
int packCount = _generator.Next(1, mapping.Any.Count + 1);
List<EMEditorSet> randomSet = mapping.Any.RandomSelection(_generator, packCount);
foreach (EMEditorSet mod in randomSet)
{
mod.ApplyToLevel(level.Data, _disallowedTypes);
}
}

// AllWithin means one from each set will be applied. Used for the likes of choosing a new
// keyhole position from a set.
foreach (List<EMEditorSet> modList in mapping.AllWithin)
{
EMEditorSet mod = modList[_generator.Next(0, modList.Count)];
mod.ApplyToLevel(level.Data, _disallowedTypes);
}

// ConditionalAllWithin is similar to above, but different sets of mods can be returned based
// on a given condition. For example, move a slot to a room, but only if a specific entity exists.
foreach (EMConditionalEditorSet conditionalSet in mapping.ConditionalAllWithin)
{
List<EMEditorSet> modList = conditionalSet.GetApplicableSets(level.Data);
EMEditorSet mod = modList[_generator.Next(0, modList.Count)];
mod.ApplyToLevel(level.Data, _disallowedTypes);
}

// OneOf is used for a leader-follower situation, but where only one follower from
// a group is wanted. An example is removing a ladder (the leader) and putting it in
// a different position, so the followers are the different positions from which we pick one.
foreach (EMEditorGroupedSet mod in mapping.OneOf)
{
EMEditorSet follower = mod.Followers[_generator.Next(0, mod.Followers.Count)];
mod.ApplyToLevel(level.Data, follower, _disallowedTypes);
}
}

private void MirrorLevel(TR3CombinedLevel level, EMEditorMapping mapping)
{
EMMirrorFunction mirrorer = new EMMirrorFunction();
mirrorer.ApplyToLevel(level.Data);

if (mapping != null)
{
// Process packs that need to be applied after mirroring.
mapping.Mirrored.ApplyToLevel(level.Data, new EMType[] { });
}

// Notify the texture monitor that this level has been flipped
TextureMonitor<TR3Entities> monitor = TextureMonitor.CreateMonitor(level.Name);
monitor.UseMirroring = true;
}
}
}
}
Loading

0 comments on commit 7e8a20b

Please sign in to comment.