Skip to content
6 changes: 5 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
## [Unreleased](https://github.com/LostArtefacts/TR-Rando/compare/V1.10.2...master) - xxxx-xx-xx
- added support for TR1X 4.14 (now the minimum version supported) (#803)
- added support TR2X (#821)
- added support for TR2X (#821)
- added support for The Golden Mask in TR2X and playing in combined mode (see Level Sequencing options) (#59)
- added Spanish translations for TR1 (#800)
- added an option to include extra pickups in certain levels in TR2X (#832)
Expand Down Expand Up @@ -29,6 +29,10 @@
- fixed an uncollectible secret in Natla's Mines (#849)
- fixed models all using the same highlight colour in wireframe mode (#852)
- fixed the Temple of Xian Dragon Seal room (default placement) sometimes being flipped on arrival, leading to potential softlocks (OG bug)
- fixed a glitchless secret in Bartoli's Hideout not always being obtainable without using glitches
- fixed Lara not being invisible in the Diving Area cutscene if she was invisible in the level itself
- fixed an error message during TR1 outfit randomization
- fixed the imported dragon room in Floating Islands using the wrong textures
- removed support for the dragon (TR2 Remastered only) in all levels except Dragon's Lair (may revisit)

## [V1.10.2](https://github.com/LostArtefacts/TR-Rando/compare/V1.10.1...V1.10.2) - 2024-12-06
Expand Down
Binary file modified Deps/TRGE.Core.dll
Binary file not shown.
1 change: 1 addition & 0 deletions TRDataControl/Environment/Model/EMType.cs
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,7 @@ public enum EMType
ImportNonGraphicsModel = 145,
CopySpriteSequence = 146,
RemoveStaticCollision = 147,
CloneType = 148,

// NOOP/Placeholder
NOOP = 1000
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
using TRLevelControl.Model;

namespace TRDataControl.Environment;

public class EMCloneTypeFunction : BaseEMFunction
{
public Dictionary<uint, uint> ModelMap { get; set; }
public Dictionary<uint, uint> SpriteMap { get; set; }

public override void ApplyToLevel(TR1Level level)
=> Clone(level.Models, level.Sprites);

public override void ApplyToLevel(TR2Level level)
=> Clone(level.Models, level.Sprites);

public override void ApplyToLevel(TR3Level level)
=> Clone(level.Models, level.Sprites);

private void Clone<T>(TRDictionary<T, TRModel> models, TRDictionary<T, TRSpriteSequence> sprites)
where T : Enum
{
Clone(models, ModelMap);
Clone(sprites, SpriteMap);
}

private static void Clone<T, V>(TRDictionary<T, V> levelMap, Dictionary<uint, uint> cloneMap)
where T : Enum
where V : class, ICloneable
{
if (cloneMap == null)
{
return;
}

foreach (var (baseId, targetId) in cloneMap)
{
var baseType = (T)(object)baseId;
var targetType = (T)(object)targetId;
if (levelMap.TryGetValue(baseType, out var value))
{
levelMap[targetType] = (V)value.Clone();
}
}
}
}
1 change: 1 addition & 0 deletions TRDataControl/Environment/Parsing/EMConverter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,7 @@ private object ReadEMType(JObject jo)
EMType.ImportNonGraphicsModel => JsonConvert.DeserializeObject<EMImportNonGraphicsModelFunction>(jo.ToString(), this),
EMType.CopySpriteSequence => JsonConvert.DeserializeObject<EMCopySpriteSequenceFunction>(jo.ToString(), this),
EMType.RemoveStaticCollision => JsonConvert.DeserializeObject<EMRemoveStaticCollisionFunction>(jo.ToString(), this),
EMType.CloneType => JsonConvert.DeserializeObject<EMCloneTypeFunction>(jo.ToString(), this),

// NOOP
EMType.NOOP => JsonConvert.DeserializeObject<EMPlaceholderFunction>(jo.ToString(), this),
Expand Down
7 changes: 7 additions & 0 deletions TRRandomizerCore/Processors/TR2/Tasks/TR2XFloorDataTask.cs
Original file line number Diff line number Diff line change
Expand Up @@ -41,5 +41,12 @@ public void Run(TR2CombinedLevel level)
{
level.Data.Cameras[7].Y -= TRConsts.Step2 + 16;
}

if (level.Is(TR2LevelNames.CHICKEN))
{
var sector = level.Data.Rooms[48].GetSector(5, 4, TRUnit.Sector);
var zone = level.Data.Boxes[sector.BoxIndex].Zone;
zone.FlipOnZone = zone.FlipOffZone.Clone();
}
}
}
10 changes: 6 additions & 4 deletions TRRandomizerCore/Randomizers/Shared/EnemyAllocator.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
using Newtonsoft.Json;
using TRLevelControl.Model;
using TRLevelControl.Model;
using TRRandomizerCore.Editors;
using TRRandomizerCore.Helpers;
using TRRandomizerCore.Utilities;
Expand All @@ -22,10 +21,13 @@ public EnemyAllocator(TRGameVersion version)
{
string relocFile = $"Resources/{version}/Locations/enemy_relocations.json";
_relocations = File.Exists(relocFile)
? JsonConvert.DeserializeObject<Dictionary<string, List<Location>>>(File.ReadAllText(relocFile))
: new();
? JsonUtils.ReadFile<Dictionary<string, List<Location>>>(relocFile)
: [];
RelocationsLoaded();
}

protected virtual void RelocationsLoaded() { }

public void Initialise()
{
_resultantEnemies = new();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ namespace TRRandomizerCore.Randomizers;
public class TR1OutfitRandomizer : BaseTR1Randomizer
{
private static readonly string _gymOutfitHash = "6523d69dbf1f0ab671f5f877afe6ff35";
private static readonly string _mauledOutfitHash = "ab52e600b2f4fa7edfd3e9a32d1a5582";
private static readonly Version _minBraidCutsceneVersion = new(2, 13, 0);
private static readonly TR1SFX[] _barefootSfxIDs = new TR1SFX[] { TR1SFX.LaraFeet, TR1SFX.LaraLand };
private static readonly double _mauledLaraChance = (double)1 / 3;
Expand Down Expand Up @@ -803,6 +804,11 @@ private void ConvertToMauledOutfit(TR1CombinedLevel level)
List<TRMesh> laraShotgun = level.Data.Models[TR1Type.LaraShotgunAnim_H]?.Meshes;
List<TRMesh> laraMisc = level.Data.Models[TR1Type.LaraMiscAnim_H]?.Meshes;

if (laraMisc == null || laraMisc.ComputeSkeletonHash() != _mauledOutfitHash)
{
return;
}

if (level.Is(TR1LevelNames.QUALOPEC_CUT))
{
// This model is completely different to all others, so just
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -281,10 +281,7 @@ internal TextureProcessor(TR1TextureRandomizer outer)
: base(outer)
{
_holders = new();
_landmarkImporter = new()
{
IsCommunityPatch = true
};
_landmarkImporter = new();
_wireframer = new();
}

Expand Down
25 changes: 19 additions & 6 deletions TRRandomizerCore/Randomizers/TR2/Classic/TR2EnemyRandomizer.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
using Newtonsoft.Json;
using TRDataControl;
using TRDataControl.Environment;
using TRLevelControl;
using TRLevelControl.Helpers;
using TRLevelControl.Model;
using TRRandomizerCore.Helpers;
Expand Down Expand Up @@ -184,14 +185,26 @@ private void AdjustFurnaceBlockRoom(TR2CombinedLevel level)
return;
}

var enemyTrigActions = level.Data.Entities
var enemies = level.Data.Entities
.Select((entity, idx) => (entity, idx))
.Where(x => x.entity.Room == 11 && TR2TypeUtilities.IsEnemyType(x.entity.TypeID))
.Select(x => new FDActionItem
.Where(x => x.entity.Room == 11 && TR2TypeUtilities.IsEnemyType(x.entity.TypeID));
foreach (var enemy in enemies.Select(x => x.entity))
{
var x = (enemy.X - level.Data.Rooms[11].Info.X) / TRConsts.Step4;
var z = (enemy.Z - level.Data.Rooms[11].Info.Z) / TRConsts.Step4;
enemy.Angle = (x, z) switch
{
Action = FDTrigAction.Object,
Parameter = (short)x.idx,
}).ToList();
(1, _) => -16384,
(_, 1) => -32768,
_ => 0,
};
}

var enemyTrigActions = enemies.Select(x => new FDActionItem
{
Action = FDTrigAction.Object,
Parameter = (short)x.idx,
}).ToList();
enemyTrigActions.Shuffle(_generator);
var actionGroups = enemyTrigActions.Split(4);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -169,6 +169,10 @@ private void FinalizeEnvironment(TR2CombinedLevel level)

if (mapping != null)
{
if (level.IsUKBox)
{
mapping.AlternateTextures();
}
mapping.SetCommunityPatch(ScriptEditor.Edition.IsCommunityPatch);

foreach (EMConditionalSingleEditorSet mod in mapping.ConditionalAll)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -143,7 +143,7 @@ internal class OutfitProcessor : AbstractProcessorThread<TR2OutfitRandomizer>
TR2Type.Lara, TR2Type.LaraPonytail_H, TR2Type.LaraFlareAnim_H,
TR2Type.LaraPistolAnim_H, TR2Type.LaraShotgunAnim_H, TR2Type.LaraAutoAnim_H,
TR2Type.LaraUziAnim_H, TR2Type.LaraM16Anim_H, TR2Type.LaraHarpoonAnim_H,
TR2Type.LaraGrenadeAnim_H, TR2Type.LaraMiscAnim_H
TR2Type.LaraGrenadeAnim_H, TR2Type.LaraMiscAnim_H, TR2Type.CutsceneActor3,
};

private readonly Dictionary<TR2CombinedLevel, TR2Type> _outfitAllocations;
Expand Down
49 changes: 49 additions & 0 deletions TRRandomizerCore/Randomizers/TR2/Shared/TR2EnemyAllocator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,15 @@ public class TR2EnemyAllocator : EnemyAllocator<TR2Type>
TR2LevelNames.MONASTERY,
};

private static readonly List<TR2Type> _furnaceBlockRoomRestrictedTypes =
[
.. TR2TypeUtilities.GetFamily(TR2Type.BirdMonsterOG),
.. TR2TypeUtilities.GetFamily(TR2Type.FlamethrowerGoonOG),
TR2Type.ShotgunGoon,
TR2Type.TRex,
TR2Type.XianGuardSpear,
];

public TR2EnemyAllocator()
: base(TRGameVersion.TR2) { }

Expand All @@ -47,6 +56,26 @@ protected override Dictionary<TR2Type, List<int>> GetRestrictedRooms(string leve
protected override bool IsOneShotType(TR2Type type)
=> type == TR2Type.MarcoBartoli;

protected override void RelocationsLoaded()
{
foreach (var locations in _relocations.Values)
{
var blackLocations = locations.FindAll(l => l.TargetType == (short)TR2Type.BlackMorayEel);
foreach (var blackEel in blackLocations)
{
var hasYellowEel = locations.Any(l => l.IsEquivalent(blackEel)
&& l.EntityIndex == blackEel.EntityIndex
&& l.TargetType == (short)TR2Type.YellowMorayEel);
if (!hasYellowEel)
{
var yellowEel = blackEel.Clone();
yellowEel.TargetType = (short)TR2Type.YellowMorayEel;
locations.Add(yellowEel);
}
}
}
}

public EnemyTransportCollection<TR2Type> SelectCrossLevelEnemies(string levelName, TR2Level level)
{
if (levelName == TR2LevelNames.ASSAULT)
Expand Down Expand Up @@ -483,6 +512,13 @@ public void RandomizeEnemies(string levelName, TR2Level level, EnemyRandomizatio
}
}

if (levelName == TR2LevelNames.FURNACE && currentEntity.Room == 11
&& difficulty == RandoDifficulty.Default)
{
newType = SelectFurnaceBlockRoomType(newType, enemyPool,
enemyEntities.FindAll(e => e.Room == 11 && e != currentEntity));
}

// If we are restricting count per level for this enemy and have reached that count, pick
// something else. This applies when we are restricting by in-level count, but not by room
// (e.g. Winston).
Expand Down Expand Up @@ -554,6 +590,19 @@ public void RandomizeEnemies(string levelName, TR2Level level, EnemyRandomizatio
}
}

private TR2Type SelectFurnaceBlockRoomType(TR2Type selectedType, List<TR2Type> pool, List<TR2Entity> neighbours)
{
var safePool = pool.Except(_furnaceBlockRoomRestrictedTypes).ToList();
if (!_furnaceBlockRoomRestrictedTypes.Contains(selectedType) || safePool.Count == 0)
{
return selectedType;
}

return neighbours.Any(e => e.TypeID == TR2TypeUtilities.TranslateAlias(selectedType))
? safePool.RandomItem(Generator)
: selectedType;
}

private void LimitSkidooEntities(string levelName, TR2Level level)
{
if (!Settings.IsRemastered)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3196,9 +3196,9 @@
"Z": 2048
},
{
"X": 4096,
"X": 5120,
"Y": 1280,
"Z": 2048
"Z": 3072
}
]
},
Expand Down Expand Up @@ -3240,12 +3240,12 @@
"Vertices": [
{
"X": 2048,
"Y": -3840,
"Y": -1792,
"Z": 1024
},
{
"X": 1024,
"Y": -3840,
"Y": -1792,
"Z": 1024
},
{
Expand All @@ -3269,12 +3269,12 @@
"Vertices": [
{
"X": 1024,
"Y": -3840,
"Y": -1792,
"Z": 3072
},
{
"X": 2048,
"Y": -3840,
"Y": -1792,
"Z": 3072
},
{
Expand Down Expand Up @@ -4002,7 +4002,7 @@
"Z": 90624,
"Room": -3
},
"Timer": 30
"Timer": 34
}
},
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,15 @@
"BackgroundIndex": 3
}
]
},
"Landmarks.GMSryBut": {
"0": [
{
"RoomNumber": 61,
"RectangleIndices": [ 7 ],
"BackgroundIndex": 5
}
]
}
},

Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,15 @@
{
"Landmarks": {
"Landmarks.NevByte": {
"0": [
{
"RoomNumber": 23,
"RectangleIndices": [ 76 ],
"BackgroundIndex": 44
}
]
}
},
"Static": {
"Lara.Bobble": [
{
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{
"Info": "https://www.twitch.tv/gmsrybut",
"VariantMap": {
"Default": [
"0, 0, 64, 64"
]
}
}
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{
"Info": "https://www.twitch.tv/nevbyte",
"VariantMap": {
"Default": [
"0, 0, 64, 64"
]
}
}
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Loading