Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Selectable Enemies, Unconditional Bird Monsters and SFX Updates #328

Merged
merged 11 commits into from
Apr 26, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Binary file modified Deps/TRGE.Core.dll
Binary file not shown.
88 changes: 88 additions & 0 deletions TRLevelReader/Helpers/TR1BoxUtilities.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
using System;
using System.Collections.Generic;
using System.Linq;
using TRLevelReader.Model;
using TRLevelReader.Model.Base.Enums;

namespace TRLevelReader.Helpers
{
public static class TR1BoxUtilities
{
public static void DuplicateZone(TRLevel level, int boxIndex)
{
TRZoneGroup zoneGroup = level.Zones[boxIndex];
List<TRZoneGroup> zones = level.Zones.ToList();
zones.Add(new TRZoneGroup
{
NormalZone = zoneGroup.NormalZone.Clone(),
AlternateZone = zoneGroup.AlternateZone.Clone()
});
level.Zones = zones.ToArray();
}

public static TRZoneGroup[] ReadZones(uint numBoxes, ushort[] zoneData)
{
// Initialise the zone groups - one for every box.
TRZoneGroup[] zones = new TRZoneGroup[numBoxes];
for (int i = 0; i < zones.Length; i++)
{
zones[i] = new TRZoneGroup
{
NormalZone = new TRZone(),
AlternateZone = new TRZone()
};
}

// Build the zones, mapping the multidimensional ushort structures into the corresponding
// zone object values.
IEnumerable<FlipStatus> flipValues = Enum.GetValues(typeof(FlipStatus)).Cast<FlipStatus>();
IEnumerable<TRZones> zoneValues = Enum.GetValues(typeof(TRZones)).Cast<TRZones>();

int valueIndex = 0;
foreach (FlipStatus flip in flipValues)
{
foreach (TRZones zone in zoneValues)
{
for (int box = 0; box < zones.Length; box++)
{
zones[box][flip].GroundZones[zone] = zoneData[valueIndex++];
}
}

for (int box = 0; box < zones.Length; box++)
{
zones[box][flip].FlyZone = zoneData[valueIndex++];
}
}

return zones;
}

public static ushort[] FlattenZones(TRZoneGroup[] zoneGroups)
{
// Convert the zone objects back into a flat ushort list.
IEnumerable<FlipStatus> flipValues = Enum.GetValues(typeof(FlipStatus)).Cast<FlipStatus>();
IEnumerable<TRZones> zoneValues = Enum.GetValues(typeof(TRZones)).Cast<TRZones>();

List<ushort> zones = new List<ushort>();

foreach (FlipStatus flip in flipValues)
{
foreach (TRZones zone in zoneValues)
{
for (int box = 0; box < zoneGroups.Length; box++)
{
zones.Add(zoneGroups[box][flip].GroundZones[zone]);
}
}

for (int box = 0; box < zoneGroups.Length; box++)
{
zones.Add(zoneGroups[box][flip].FlyZone);
}
}

return zones.ToArray();
}
}
}
14 changes: 14 additions & 0 deletions TRLevelReader/Helpers/TR2EntityUtilities.cs
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,20 @@ public static List<TR2Entities> GetEntityFamily(TR2Entities entity)
return new List<TR2Entities> { entity };
}

public static List<TR2Entities> RemoveAliases(IEnumerable<TR2Entities> entities)
{
List<TR2Entities> ents = new List<TR2Entities>();
foreach (TR2Entities ent in entities)
{
TR2Entities normalisedEnt = TranslateEntityAlias(ent);
if (!ents.Contains(normalisedEnt))
{
ents.Add(normalisedEnt);
}
}
return ents;
}

public static List<TR2Entities> GetLaraTypes()
{
return new List<TR2Entities>
Expand Down
14 changes: 14 additions & 0 deletions TRLevelReader/Helpers/TR3EntityUtilities.cs
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,20 @@ public static TR3Entities GetAliasForLevel(string lvl, TR3Entities entity)
return entity;
}

public static List<TR3Entities> RemoveAliases(IEnumerable<TR3Entities> entities)
{
List<TR3Entities> ents = new List<TR3Entities>();
foreach (TR3Entities ent in entities)
{
TR3Entities normalisedEnt = TranslateEntityAlias(ent);
if (!ents.Contains(normalisedEnt))
{
ents.Add(normalisedEnt);
}
}
return ents;
}

public static List<TR3Entities> GetLaraTypes()
{
return new List<TR3Entities>
Expand Down
8 changes: 8 additions & 0 deletions TRLevelReader/Model/Base/Enums/TRZones.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
namespace TRLevelReader.Model.Base.Enums
{
public enum TRZones
{
Zone1 = 0,
Zone2 = 1
}
}
17 changes: 3 additions & 14 deletions TRLevelReader/Model/Base/TRLevel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using TRLevelReader.Helpers;
using TRLevelReader.Serialization;

namespace TRLevelReader.Model
Expand Down Expand Up @@ -220,19 +221,7 @@ public class TRLevel : BaseTRLevel, ISerializableCompact
/// </summary>
public ushort[] Overlaps { get; set; }

public ushort[] Zones { get; set; }

public ushort[] GroundZone { get; set; }

public ushort[] GroundZone2 { get; set; }

public ushort[] FlyZone { get; set; }

public ushort[] GroundZoneAlt { get; set; }

public ushort[] GroundZoneAlt2 { get; set; }

public ushort[] FlyZoneAlt { get; set; }
public TRZoneGroup[] Zones { get; set; }

/// <summary>
/// 4 bytes
Expand Down Expand Up @@ -371,7 +360,7 @@ public byte[] Serialize()
foreach (TRBox box in Boxes) { writer.Write(box.Serialize()); }
writer.Write(NumOverlaps);
foreach (ushort overlap in Overlaps) { writer.Write(overlap); }
foreach (ushort zone in Zones) { writer.Write(zone); }
foreach (ushort zone in TR1BoxUtilities.FlattenZones(Zones)) { writer.Write(zone); }
writer.Write(NumAnimatedTextures);
writer.Write((ushort)AnimatedTextures.Length);
foreach (TRAnimatedTexture texture in AnimatedTextures) { writer.Write(texture.Serialize()); }
Expand Down
14 changes: 13 additions & 1 deletion TRLevelReader/Model/Base/TRSoundDetails.cs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,19 @@ public class TRSoundDetails : ISerializableCompact
public ushort Chance { get; set; }

public ushort Characteristics { get; set; }
public int NumSounds => (Characteristics & 0x00FC) >> 2; // get bits 2-7

public int NumSounds
{
get
{
return (Characteristics & 0x00FC) >> 2; // get bits 2-7
}
set
{
Characteristics = (ushort)(Characteristics & ~(Characteristics & 0x00FC));
Characteristics |= (ushort)(value << 2);
}
}

public override string ToString()
{
Expand Down
49 changes: 28 additions & 21 deletions TRLevelReader/Model/Base/TRZone.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,43 +2,50 @@
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

using TRLevelReader.Model.Base.Enums;
using TRLevelReader.Serialization;

namespace TRLevelReader.Model
{
public class TRZone : ISerializableCompact
public class TRZone : ISerializableCompact, ICloneable
{
public ushort GroundZone1Normal { get; set; }

public ushort GroundZone2Normal { get; set; }

public ushort FlyZoneNormal { get; set; }

public ushort GroundZone1Alternate { get; set; }
public Dictionary<TRZones, ushort> GroundZones { get; set; }
public ushort FlyZone { get; set; }

public ushort GroundZone2Alternate { get; set; }

public ushort FlyZoneAlternate { get; set; }
public TRZone()
{
GroundZones = new Dictionary<TRZones, ushort>();
}

public byte[] Serialize()
{
using (MemoryStream stream = new MemoryStream())
{
using (BinaryWriter writer = new BinaryWriter(stream))
{
writer.Write(GroundZone1Normal);
writer.Write(GroundZone2Normal);
writer.Write(FlyZoneNormal);
writer.Write(GroundZone1Alternate);
writer.Write(GroundZone2Alternate);
writer.Write(FlyZoneAlternate);
foreach (ushort zone in GroundZones.Values)
{
writer.Write(zone);
}
writer.Write(FlyZone);
}

return stream.ToArray();
}
}

public TRZone Clone()
{
return new TRZone
{
GroundZones = GroundZones.ToDictionary(e => e.Key, e => e.Value),
FlyZone = FlyZone
};
}

object ICloneable.Clone()
{
return Clone();
}
}
}
}
25 changes: 25 additions & 0 deletions TRLevelReader/Model/Base/TRZoneGroup.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
using System.Collections.Generic;
using TRLevelReader.Model.Base.Enums;

namespace TRLevelReader.Model
{
public class TRZoneGroup : Dictionary<FlipStatus, TRZone>
{
/// <summary>
/// Zone values when flipmap is off.
/// </summary>
public TRZone NormalZone
{
get => this[FlipStatus.Off];
set => this[FlipStatus.Off] = value;
}
/// <summary>
/// Zone values when flipmap is on.
/// </summary>
public TRZone AlternateZone
{
get => this[FlipStatus.On];
set => this[FlipStatus.On] = value;
}
}
}
9 changes: 5 additions & 4 deletions TRLevelReader/TRLevelReader.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using TRLevelReader.Helpers;
using TRLevelReader.Model;

namespace TRLevelReader
Expand Down Expand Up @@ -284,12 +285,12 @@ public TRLevel ReadLevel(string Filename)
level.Overlaps[i] = reader.ReadUInt16();
}

level.Zones = new ushort[6 * level.NumBoxes];

for (int i = 0; i < level.Zones.Count(); i++)
ushort[] zoneData = new ushort[level.NumBoxes * 6];
for (int i = 0; i < zoneData.Length; i++)
{
level.Zones[i] = reader.ReadUInt16();
zoneData[i] = reader.ReadUInt16();
}
level.Zones = TR1BoxUtilities.ReadZones(level.NumBoxes, zoneData);

//Animated Textures - the data stores the total number of ushorts to read (NumAnimatedTextures)
//followed by a ushort to describe the number of actual texture group objects.
Expand Down
51 changes: 51 additions & 0 deletions TRLevelReaderUnitTests/TRLevel_UnitTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@
using TRFDControl.FDEntryTypes;
using TRFDControl.Utilities;
using System.Linq;
using TRLevelReader.Model.Base.Enums;
using TRLevelReader.Helpers;

namespace TRLevelReaderUnitTests
{
Expand Down Expand Up @@ -386,5 +388,54 @@ public void Floordata_ReadWrite_DefaultTest()
TR1LevelWriter writer = new TR1LevelWriter();
writer.WriteLevelToFile(lvl, "level10c_fdata.phd");
}

[TestMethod]
public void ModifyZonesTest()
{
TR1LevelReader reader = new TR1LevelReader();
TRLevel lvl = reader.ReadLevel("level1.phd");

// For every box, store the current zone. We use the serialized form
// for comparison.
Dictionary<int, byte[]> flipOffZones = new Dictionary<int, byte[]>();
Dictionary<int, byte[]> flipOnZones = new Dictionary<int, byte[]>();
for (int i = 0; i < lvl.NumBoxes; i++)
{
flipOffZones[i] = lvl.Zones[i][FlipStatus.Off].Serialize();
flipOnZones[i] = lvl.Zones[i][FlipStatus.On].Serialize();
}

// Add a new box
List<TRBox> boxes = lvl.Boxes.ToList();
boxes.Add(boxes[0]);
lvl.Boxes = boxes.ToArray();
lvl.NumBoxes++;

// Add a new zone for the box and store its serialized form for comparison
int newBoxIndex = (int)(lvl.NumBoxes - 1);
TR1BoxUtilities.DuplicateZone(lvl, 0);
flipOffZones[newBoxIndex] = lvl.Zones[newBoxIndex][FlipStatus.Off].Serialize();
flipOnZones[newBoxIndex] = lvl.Zones[newBoxIndex][FlipStatus.On].Serialize();

// Verify the number of zone ushorts matches what's expected for the box count
Assert.AreEqual(TR1BoxUtilities.FlattenZones(lvl.Zones).Length, (int)(6 * lvl.NumBoxes));

// Write and re-read the level
new TR1LevelWriter().WriteLevelToFile(lvl, "TEST.phd");
lvl = reader.ReadLevel("TEST.phd");

// Capture all of the zones again. Make sure the addition of the zone above didn't
// affect any of the others and that the addition itself matches after IO.
for (int i = 0; i < lvl.NumBoxes; i++)
{
byte[] flipOff = lvl.Zones[i][FlipStatus.Off].Serialize();
Assert.IsTrue(flipOffZones.ContainsKey(i));
CollectionAssert.AreEqual(flipOffZones[i], flipOff);

byte[] flipOn = lvl.Zones[i][FlipStatus.On].Serialize();
Assert.IsTrue(flipOnZones.ContainsKey(i));
CollectionAssert.AreEqual(flipOnZones[i], flipOn);
}
}
}
}
Loading