Skip to content

Commit

Permalink
LostArtefacts#310 Multiple Dragons and LostArtefacts#314 Enemy Meshes
Browse files Browse the repository at this point in the history
The dragon can now appear in every level. This is done by importing a suitable room into the levels that otherwise cannot support a dragon fight. Several updates are in place as a result of this.

Environment:
- EMRoomDefinition holds an exported room from another level along with any necessary floor data (slants for example, but not triggers).
- ImportRoom function implemented to read in a specific room ID from JSON and merge into the level. Other standard environment mods make portals, triggers etc. The imported room is retextured to suit the level.
- CopyRoom function implemented for TR2 (previously TR3 only).
- Fix to CopyRoom function to correctly set the size of newly added Boxes.
- CameraTrigger function updated to allow attaching to every trigger in a room and to allow reusing an existing camera index.
- RemoveTriggerAction function added to allow removing a specific entry from a trigger's action list.
- Trigger function updated to allow placing on every sector in a specific room.
- Various Marco placement amendments for better spawns.

Enemy Randomization:
- Unused Marco model removed from Floating Islands to free-up space.
- Enemy pool adjustment count based on whether or not Marco is included (for example in Bartoli's to increase likelihood of successful import).
- Final packing attempt when we are maximising dragon appearance will fall back to standard enemy pool rando to avoid original enemy pool from being selected.
- Docile bird monsters honoured when a level has a restricted pool of enemies that work with Marco (e.g. Barkhang).
- Adds a chance of Marco becoming Winston for LostArtefacts#314.
  • Loading branch information
lahm86 committed Apr 10, 2022
1 parent b60ab3d commit 1d9f7cf
Show file tree
Hide file tree
Showing 23 changed files with 23,273 additions and 42 deletions.
11 changes: 11 additions & 0 deletions TREnvironmentEditor/Helpers/EMRoomDefinition.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
using System.Collections.Generic;
using TRFDControl;

namespace TREnvironmentEditor.Helpers
{
public class EMRoomDefinition<R> where R : class
{
public R Room { get; set; }
public Dictionary<int, List<FDEntry>> FloorData { get; set; }
}
}
2 changes: 2 additions & 0 deletions TREnvironmentEditor/Model/EMType.cs
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ public enum EMType
AppendTriggerActionFunction = 68,
ConvertTrigger = 69,
KillLara = 70,
RemoveTriggerAction = 71,

// Portal types 81-100
VisibilityPortal = 81,
Expand All @@ -58,6 +59,7 @@ public enum EMType
ModifyOverlaps = 122,
CopyRoom = 123,
CopyVertexAttributes = 124,
ImportRoom = 125,

// Models
ImportModel = 141,
Expand Down
251 changes: 248 additions & 3 deletions TREnvironmentEditor/Model/Types/Rooms/EMCopyRoomFunction.cs
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,252 @@ public class EMCopyRoomFunction : BaseEMFunction

public override void ApplyToLevel(TR2Level level)
{
throw new NotImplementedException();
TR2Room baseRoom = level.Rooms[RoomIndex];

int xdiff = NewLocation.X - baseRoom.Info.X;
int ydiff = NewLocation.Y - baseRoom.Info.YBottom;
int zdiff = NewLocation.Z - baseRoom.Info.Z;

TR2Room newRoom = new TR2Room
{
AlternateRoom = -1,
AmbientIntensity = baseRoom.AmbientIntensity,
AmbientIntensity2 = baseRoom.AmbientIntensity2,
Flags = baseRoom.Flags,
Info = new TRRoomInfo
{
X = NewLocation.X,
YBottom = NewLocation.Y,
YTop = NewLocation.Y + (baseRoom.Info.YTop - baseRoom.Info.YBottom),
Z = NewLocation.Z
},
Lights = new TR2RoomLight[baseRoom.NumLights],
LightMode = baseRoom.LightMode,
NumDataWords = baseRoom.NumDataWords,
NumLights = baseRoom.NumLights,
NumPortals = 0,
NumStaticMeshes = baseRoom.NumStaticMeshes,
NumXSectors = baseRoom.NumXSectors,
NumZSectors = baseRoom.NumZSectors,
Portals = new TRRoomPortal[] { },
RoomData = new TR2RoomData
{
NumRectangles = baseRoom.RoomData.NumRectangles,
NumSprites = baseRoom.RoomData.NumSprites,
NumTriangles = baseRoom.RoomData.NumTriangles,
NumVertices = baseRoom.RoomData.NumVertices,
Rectangles = new TRFace4[baseRoom.RoomData.NumRectangles],
Sprites = new TRRoomSprite[baseRoom.RoomData.NumSprites],
Triangles = new TRFace3[baseRoom.RoomData.NumTriangles],
Vertices = new TR2RoomVertex[baseRoom.RoomData.NumVertices]
},
SectorList = new TRRoomSector[baseRoom.SectorList.Length],
StaticMeshes = new TR2RoomStaticMesh[baseRoom.NumStaticMeshes]
};

// Lights
for (int i = 0; i < newRoom.Lights.Length; i++)
{
newRoom.Lights[i] = new TR2RoomLight
{
Fade1 = baseRoom.Lights[i].Fade1,
Fade2 = baseRoom.Lights[i].Fade2,
Intensity1 = baseRoom.Lights[i].Intensity1,
Intensity2 = baseRoom.Lights[i].Intensity2,
X = baseRoom.Lights[i].X + xdiff,
Y = baseRoom.Lights[i].Y + ydiff,
Z = baseRoom.Lights[i].Z + zdiff
};
}

// Faces
for (int i = 0; i < newRoom.RoomData.NumRectangles; i++)
{
newRoom.RoomData.Rectangles[i] = new TRFace4
{
Texture = baseRoom.RoomData.Rectangles[i].Texture,
Vertices = new ushort[baseRoom.RoomData.Rectangles[i].Vertices.Length]
};
for (int j = 0; j < newRoom.RoomData.Rectangles[i].Vertices.Length; j++)
{
newRoom.RoomData.Rectangles[i].Vertices[j] = baseRoom.RoomData.Rectangles[i].Vertices[j];
}
}

for (int i = 0; i < newRoom.RoomData.NumTriangles; i++)
{
newRoom.RoomData.Triangles[i] = new TRFace3
{
Texture = baseRoom.RoomData.Triangles[i].Texture,
Vertices = new ushort[baseRoom.RoomData.Triangles[i].Vertices.Length]
};
for (int j = 0; j < newRoom.RoomData.Triangles[i].Vertices.Length; j++)
{
newRoom.RoomData.Triangles[i].Vertices[j] = baseRoom.RoomData.Triangles[i].Vertices[j];
}
}

// Vertices
for (int i = 0; i < newRoom.RoomData.Vertices.Length; i++)
{
newRoom.RoomData.Vertices[i] = new TR2RoomVertex
{
Attributes = baseRoom.RoomData.Vertices[i].Attributes,
Lighting = baseRoom.RoomData.Vertices[i].Lighting,
Lighting2 = baseRoom.RoomData.Vertices[i].Lighting2,
Vertex = new TRVertex
{
X = baseRoom.RoomData.Vertices[i].Vertex.X, // Room coords for X and Z
Y = (short)(baseRoom.RoomData.Vertices[i].Vertex.Y + ydiff),
Z = baseRoom.RoomData.Vertices[i].Vertex.Z
}
};
}

// Sprites
for (int i = 0; i < newRoom.RoomData.NumSprites; i++)
{
newRoom.RoomData.Sprites[i] = new TRRoomSprite
{
Texture = baseRoom.RoomData.Sprites[i].Texture,
Vertex = baseRoom.RoomData.Sprites[i].Vertex
};
}

// Static Meshes
for (int i = 0; i < newRoom.NumStaticMeshes; i++)
{
newRoom.StaticMeshes[i] = new TR2RoomStaticMesh
{
Intensity1 = baseRoom.StaticMeshes[i].Intensity1,
Intensity2 = baseRoom.StaticMeshes[i].Intensity2,
MeshID = baseRoom.StaticMeshes[i].MeshID,
Rotation = baseRoom.StaticMeshes[i].Rotation,
X = (uint)(baseRoom.StaticMeshes[i].X + xdiff),
Y = (uint)(baseRoom.StaticMeshes[i].Y + ydiff),
Z = (uint)(baseRoom.StaticMeshes[i].Z + zdiff)
};
}

// Boxes, zones and sectors
FDControl floorData = new FDControl();
floorData.ParseFromLevel(level);

TRRoomSector linkedSector = FDUtilities.GetRoomSector(LinkedLocation.X, LinkedLocation.Y, LinkedLocation.Z, LinkedLocation.Room, level, floorData);
ushort newBoxIndex = (ushort)level.NumBoxes;
int linkedBoxIndex = linkedSector.BoxIndex;

// Duplicate the zone for the new box and link the current box to the new room
TR2BoxUtilities.DuplicateZone(level, linkedBoxIndex);
TR2Box linkedBox = level.Boxes[linkedBoxIndex];
List<ushort> overlaps = TR2BoxUtilities.GetOverlaps(level, linkedBox);
overlaps.Add(newBoxIndex);
TR2BoxUtilities.UpdateOverlaps(level, linkedBox, overlaps);

// Make a new box for the new room
byte xmin = (byte)(newRoom.Info.X / SectorSize);
byte zmin = (byte)(newRoom.Info.Z / SectorSize);
byte xmax = (byte)(xmin + newRoom.NumXSectors);
byte zmax = (byte)(zmin + newRoom.NumZSectors);
TR2Box box = new TR2Box
{
XMin = xmin,
ZMin = zmin,
XMax = xmax,
ZMax = zmax,
TrueFloor = (short)newRoom.Info.YBottom
};
List<TR2Box> boxes = level.Boxes.ToList();
boxes.Add(box);
level.Boxes = boxes.ToArray();
level.NumBoxes++;

// Link the box to the room we're joining to
TR2BoxUtilities.UpdateOverlaps(level, box, new List<ushort> { (ushort)linkedBoxIndex });

for (int i = 0; i < newRoom.SectorList.Length; i++)
{
int sectorYDiff = 0;
ushort sectorBoxIndex = baseRoom.SectorList[i].BoxIndex;
// Only change the sector if it's not impenetrable
if (baseRoom.SectorList[i].Ceiling != _solidSector || baseRoom.SectorList[i].Floor != _solidSector)
{
sectorYDiff = ydiff / ClickSize;
sectorBoxIndex = newBoxIndex;
}

newRoom.SectorList[i] = new TRRoomSector
{
BoxIndex = sectorBoxIndex,
Ceiling = (sbyte)(baseRoom.SectorList[i].Ceiling + sectorYDiff),
FDIndex = 0, // Initialise to no FD
Floor = (sbyte)(baseRoom.SectorList[i].Floor + sectorYDiff),
RoomAbove = _noRoom,
RoomBelow = _noRoom
};

// Duplicate the FD too for everything except triggers. Track any portals
// so they can be blocked off.
if (baseRoom.SectorList[i].FDIndex != 0)
{
List<FDEntry> entries = floorData.Entries[baseRoom.SectorList[i].FDIndex];
List<FDEntry> newEntries = new List<FDEntry>();
foreach (FDEntry entry in entries)
{
switch ((FDFunctions)entry.Setup.Function)
{
case FDFunctions.PortalSector:
// This portal will no longer be valid in the new room's position,
// so block off the wall
newRoom.SectorList[i].Floor = newRoom.SectorList[i].Ceiling = _solidSector;
break;
case FDFunctions.FloorSlant:
FDSlantEntry slantEntry = entry as FDSlantEntry;
newEntries.Add(new FDSlantEntry()
{
Setup = new FDSetup() { Value = slantEntry.Setup.Value },
SlantValue = slantEntry.SlantValue,
Type = FDSlantEntryType.FloorSlant
});
break;
case FDFunctions.CeilingSlant:
FDSlantEntry ceilingSlant = entry as FDSlantEntry;
newEntries.Add(new FDSlantEntry()
{
Setup = new FDSetup() { Value = ceilingSlant.Setup.Value },
SlantValue = ceilingSlant.SlantValue,
Type = FDSlantEntryType.CeilingSlant
});
break;
case FDFunctions.KillLara:
newEntries.Add(new FDKillLaraEntry()
{
Setup = new FDSetup() { Value = entry.Setup.Value }
});
break;
case FDFunctions.ClimbableWalls:
newEntries.Add(new FDClimbEntry()
{
Setup = new FDSetup() { Value = entry.Setup.Value }
});
break;
}
}

if (newEntries.Count > 0)
{
floorData.CreateFloorData(newRoom.SectorList[i]);
floorData.Entries[newRoom.SectorList[i].FDIndex].AddRange(newEntries);
}
}
}

floorData.WriteToLevel(level);

List<TR2Room> rooms = level.Rooms.ToList();
rooms.Add(newRoom);
level.Rooms = rooms.ToArray();
level.NumRooms++;
}

public override void ApplyToLevel(TR3Level level)
Expand Down Expand Up @@ -174,8 +419,8 @@ public override void ApplyToLevel(TR3Level level)
// Make a new box for the new room
byte xmin = (byte)(newRoom.Info.X / SectorSize);
byte zmin = (byte)(newRoom.Info.Z / SectorSize);
byte xmax = (byte)((newRoom.Info.X / SectorSize) + (newRoom.Sectors.Length / newRoom.NumZSectors));
byte zmax = (byte)((newRoom.Info.Z / SectorSize) + (newRoom.Sectors.Length % newRoom.NumZSectors));
byte xmax = (byte)(xmin + newRoom.NumXSectors);
byte zmax = (byte)(zmin + newRoom.NumZSectors);
TR2Box box = new TR2Box
{
XMin = xmin,
Expand Down
Loading

0 comments on commit 1d9f7cf

Please sign in to comment.