Skip to content

Commit

Permalink
LostArtefacts#241 TR3 Environment Randomization
Browse files Browse the repository at this point in the history
All environment functions are now available for TR3.
  • Loading branch information
lahm86 committed May 20, 2022
1 parent e26669d commit de0864b
Show file tree
Hide file tree
Showing 19 changed files with 776 additions and 85 deletions.
25 changes: 22 additions & 3 deletions TREnvironmentEditor/Model/BaseEMFunction.cs
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ public int CreateRoomVertex(TR2Room room, TRVertex vert, short lighting = 6574,
{
TR2RoomVertex v = new TR2RoomVertex
{
Attributes = 32784, // This stops it shimmering if viewed from underwater, should be configuratble
Attributes = 32784, // This stops it shimmering if viewed from underwater, should be configurable
Lighting = lighting,
Lighting2 = lighting2,
Vertex = vert
Expand All @@ -61,13 +61,32 @@ public int CreateRoomVertex(TR2Room room, TRVertex vert, short lighting = 6574,
return verts.Count - 1;
}

public int CreateRoomVertex(TR3Room room, TRVertex vert, short lighting = 6574, ushort colour = 6574, bool useCaustics = false, bool useWaveMovement = false)
{
TR3RoomVertex v = new TR3RoomVertex
{
Attributes = 32784,
Lighting = lighting,
Colour = colour,
UseCaustics = useCaustics,
UseWaveMovement = useWaveMovement,
Vertex = vert
};

List<TR3RoomVertex> verts = room.RoomData.Vertices.ToList();
verts.Add(v);
room.RoomData.Vertices = verts.ToArray();
room.RoomData.NumVertices++;
return verts.Count - 1;
}

/// <summary>
/// Gets the indices of rooms above or below the provided room.
/// </summary>
public ISet<byte> GetAdjacentRooms(TR2Room room, bool above)
public ISet<byte> GetAdjacentRooms(IEnumerable<TRRoomSector> sectors, bool above)
{
ISet<byte> rooms = new HashSet<byte>();
foreach (TRRoomSector sector in room.SectorList)
foreach (TRRoomSector sector in sectors)
{
byte roomNumber = above ? sector.RoomAbove : sector.RoomBelow;
if (roomNumber != 255)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System.Collections.Generic;
using System;
using System.Collections.Generic;
using TREnvironmentEditor.Helpers;
using TRFDControl;
using TRFDControl.FDEntryTypes;
Expand Down Expand Up @@ -38,9 +39,47 @@ protected void RepositionTriggerable(TR2Entity entity, TR2Level level)
List<FDTriggerEntry> currentTriggers = FDUtilities.GetEntityTriggers(control, EntityIndex);
FDUtilities.RemoveEntityTriggers(level, EntityIndex, control);

AmendTriggers(currentTriggers, control, delegate (EMLocation location)
{
return FDUtilities.GetRoomSector(location.X, location.Y, location.Z, data.ConvertRoom(location.Room), level, control);
});

control.WriteToLevel(level);
}

protected void RepositionTriggerable(TR2Entity entity, TR3Level level)
{
EMLevelData data = GetData(level);

entity.X = Location.X;
entity.Y = Location.Y;
entity.Z = Location.Z;
entity.Room = data.ConvertRoom(Location.Room);

if (TriggerLocations == null || TriggerLocations.Count == 0)
{
return;
}

FDControl control = new FDControl();
control.ParseFromLevel(level);

List<FDTriggerEntry> currentTriggers = FDUtilities.GetEntityTriggers(control, EntityIndex);
FDUtilities.RemoveEntityTriggers(level, EntityIndex, control);

AmendTriggers(currentTriggers, control, delegate (EMLocation location)
{
return FDUtilities.GetRoomSector(location.X, location.Y, location.Z, data.ConvertRoom(location.Room), level, control);
});

control.WriteToLevel(level);
}

private void AmendTriggers(List<FDTriggerEntry> currentTriggers, FDControl control, Func<EMLocation, TRRoomSector> sectorGetter)
{
foreach (EMLocation location in TriggerLocations)
{
TRRoomSector sector = FDUtilities.GetRoomSector(location.X, location.Y, location.Z, data.ConvertRoom(location.Room), level, control);
TRRoomSector sector = sectorGetter.Invoke(location);
// If there is no floor data create the FD to begin with.
if (sector.FDIndex == 0)
{
Expand Down Expand Up @@ -78,8 +117,6 @@ protected void RepositionTriggerable(TR2Entity entity, TR2Level level)
});
}
}

control.WriteToLevel(level);
}
}
}
33 changes: 29 additions & 4 deletions TREnvironmentEditor/Model/Types/Entities/EMConvertEnemyFunction.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
using System.Collections.Generic;
using System.Linq;
using TREnvironmentEditor.Helpers;
using TRLevelReader.Helpers;
using TRLevelReader.Model;
using TRLevelReader.Model.Enums;
Expand All @@ -10,7 +11,7 @@ public class EMConvertEnemyFunction : BaseEMFunction
{
public List<int> EntityIndices { get; set; }
public EnemyType NewEnemyType { get; set; }
public List<TR2Entities> Exclusions { get; set; }
public List<short> Exclusions { get; set; }

public override void ApplyToLevel(TR2Level level)
{
Expand All @@ -28,22 +29,46 @@ public override void ApplyToLevel(TR2Level level)

if (Exclusions != null && Exclusions.Count > 0)
{
potentialTypes.RemoveAll(e => Exclusions.Contains(e));
potentialTypes.RemoveAll(e => Exclusions.Contains((short)e));
}

TR2Entity enemyMatch = level.Entities.ToList().Find(e => potentialTypes.Contains((TR2Entities)e.TypeID));
if (enemyMatch != null)
{
EMLevelData data = GetData(level);
foreach (int index in EntityIndices)
{
level.Entities[index].TypeID = enemyMatch.TypeID;
level.Entities[data.ConvertEntity(index)].TypeID = enemyMatch.TypeID;
}
}
}

public override void ApplyToLevel(TR3Level level)
{
throw new System.NotImplementedException();
List<TR3Entities> potentialTypes = TR3EntityUtilities.GetFullListOfEnemies();
if (NewEnemyType == EnemyType.Land)
{
potentialTypes.RemoveAll(e => TR3EntityUtilities.IsWaterCreature(e));
}
else
{
potentialTypes.RemoveAll(e => !TR3EntityUtilities.IsWaterCreature(e));
}

if (Exclusions != null && Exclusions.Count > 0)
{
potentialTypes.RemoveAll(e => Exclusions.Contains((short)e));
}

TR2Entity enemyMatch = level.Entities.ToList().Find(e => potentialTypes.Contains((TR3Entities)e.TypeID));
if (enemyMatch != null)
{
EMLevelData data = GetData(level);
foreach (int index in EntityIndices)
{
level.Entities[data.ConvertEntity(index)].TypeID = enemyMatch.TypeID;
}
}
}
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
using TRLevelReader.Model;
using TRLevelReader.Model.Enums;
using TREnvironmentEditor.Helpers;
using TRLevelReader.Model;

namespace TREnvironmentEditor.Model.Types
{
Expand All @@ -10,12 +10,14 @@ public class EMConvertEntityFunction : BaseEMFunction

public override void ApplyToLevel(TR2Level level)
{
level.Entities[EntityIndex].TypeID = NewEntityType;
EMLevelData data = GetData(level);
level.Entities[data.ConvertEntity(EntityIndex)].TypeID = NewEntityType;
}

public override void ApplyToLevel(TR3Level level)
{
level.Entities[EntityIndex].TypeID = NewEntityType;
EMLevelData data = GetData(level);
level.Entities[data.ConvertEntity(EntityIndex)].TypeID = NewEntityType;
}
}
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using TRLevelReader.Model;
using TREnvironmentEditor.Helpers;
using TRLevelReader.Model;

namespace TREnvironmentEditor.Model.Types
{
Expand All @@ -13,12 +14,14 @@ public class EMModifyEntityFunction : BaseEMFunction

public override void ApplyToLevel(TR2Level level)
{
ModifyEntity(level.Entities[EntityIndex]);
EMLevelData data = GetData(level);
ModifyEntity(level.Entities[data.ConvertEntity(EntityIndex)]);
}

public override void ApplyToLevel(TR3Level level)
{
ModifyEntity(level.Entities[EntityIndex]);
EMLevelData data = GetData(level);
ModifyEntity(level.Entities[data.ConvertEntity(EntityIndex)]);
}

private void ModifyEntity(TR2Entity entity)
Expand Down
25 changes: 24 additions & 1 deletion TREnvironmentEditor/Model/Types/Entities/EMMoveEnemyFunction.cs
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,30 @@ public override void ApplyToLevel(TR2Level level)

public override void ApplyToLevel(TR3Level level)
{
throw new System.NotImplementedException();
TR2Entity enemy = level.Entities[EntityIndex];
TR3Entities enemyEntity = (TR3Entities)enemy.TypeID;
bool isWaterEnemy = TR3EntityUtilities.IsWaterCreature(enemyEntity);

// If the index doesn't point to an enemy or if we only want to move land creatures
// but the enemy is a water creature (and vice-versa), bail out.
if (!TR3EntityUtilities.IsEnemyType(enemyEntity) || (IfLandCreature && isWaterEnemy) || (!IfLandCreature && !isWaterEnemy))
{
return;
}

// If the level has water creatures available, and we want to switch it, do so.
if (AttemptWaterCreature)
{
TR2Entity waterEnemy = level.Entities.ToList().Find(e => TR3EntityUtilities.IsWaterCreature((TR3Entities)e.TypeID));
if (waterEnemy != null)
{
enemy.TypeID = waterEnemy.TypeID;
return;
}
}

// Otherwise, reposition the enemy and its triggers.
RepositionTriggerable(enemy, level);
}
}
}
46 changes: 34 additions & 12 deletions TREnvironmentEditor/Model/Types/Entities/EMMovePickupFunction.cs
Original file line number Diff line number Diff line change
@@ -1,16 +1,16 @@
using System.Collections.Generic;
using System;
using System.Collections.Generic;
using System.Linq;
using TREnvironmentEditor.Helpers;
using TRFDControl;
using TRFDControl.Utilities;
using TRLevelReader.Model;
using TRLevelReader.Model.Enums;

namespace TREnvironmentEditor.Model.Types
{
public class EMMovePickupFunction : BaseEMFunction
{
public List<TR2Entities> Types { get; set; }
public List<short> Types { get; set; }
public List<EMLocation> SectorLocations { get; set; }
public EMLocation TargetLocation { get; set; }
public bool MatchY { get; set; }
Expand All @@ -22,18 +22,38 @@ public override void ApplyToLevel(TR2Level level)
FDControl control = new FDControl();
control.ParseFromLevel(level);

MovePickups(level.Entities.ToList(), data, control, delegate (EMLocation location)
{
return FDUtilities.GetRoomSector(location.X, location.Y, location.Z, data.ConvertRoom(location.Room), level, control);
});
}

public override void ApplyToLevel(TR3Level level)
{
EMLevelData data = GetData(level);

FDControl control = new FDControl();
control.ParseFromLevel(level);

MovePickups(level.Entities.ToList(), data, control, delegate (EMLocation location)
{
return FDUtilities.GetRoomSector(location.X, location.Y, location.Z, data.ConvertRoom(location.Room), level, control);
});
}

private void MovePickups(List<TR2Entity> entities, EMLevelData data, FDControl control, Func<EMLocation, TRRoomSector> sectorGetter)
{
// Store the sectors we are interested in
Dictionary<TRRoomSector, EMLocation> sectors = new Dictionary<TRRoomSector, EMLocation>();
foreach (EMLocation location in SectorLocations)
{
TRRoomSector sector = FDUtilities.GetRoomSector(location.X, location.Y, location.Z, data.ConvertRoom(location.Room), level, control);
TRRoomSector sector = sectorGetter.Invoke(location);
sectors[sector] = location;
}

// Scan for each entity type and if it's found, find its sector location. If it matches
// any we are interested in, move the item to the new location. If we haven't defined a
// manual target location, the one used to locate the sector will be used.
List<TR2Entity> entities = level.Entities.ToList();
List<TR2Entity> matchingEntities;
if (Types == null || Types.Count == 0)
{
Expand All @@ -43,12 +63,19 @@ public override void ApplyToLevel(TR2Level level)
else
{
// Only look for the types we are interested in.
matchingEntities = entities.FindAll(e => Types.Contains((TR2Entities)e.TypeID));
matchingEntities = entities.FindAll(e => Types.Contains(e.TypeID));
}

foreach (TR2Entity match in matchingEntities)
{
TRRoomSector matchSector = FDUtilities.GetRoomSector(match.X, match.Y, match.Z, match.Room, level, control);
TRRoomSector matchSector = sectorGetter.Invoke(new EMLocation
{
X = match.X,
Y = match.Y,
Z = match.Z,
Room = match.Room
});

// MatchY means the defined sector location's Y val should be compared with the entity's Y val, for
// instances where an item may be in mid-air (i.e. underwater) and another may be on the floor below it.
if (sectors.ContainsKey(matchSector) && (!MatchY || sectors[matchSector].Y == match.Y))
Expand All @@ -61,10 +88,5 @@ public override void ApplyToLevel(TR2Level level)
}
}
}

public override void ApplyToLevel(TR3Level level)
{
throw new System.NotImplementedException();
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,8 @@ public override void ApplyToLevel(TR2Level level)

public override void ApplyToLevel(TR3Level level)
{
throw new System.NotImplementedException();
TR2Entity trap = level.Entities[EntityIndex];
RepositionTriggerable(trap, level);
}
}
}
Loading

0 comments on commit de0864b

Please sign in to comment.