public void FloorData_InsertFDEntryWriteReadTest() { //Read Dragons Lair data TR2LevelReader reader = new TR2LevelReader(); TR2Level lvl = reader.ReadLevel("xian.tr2"); //Parse the floordata using FDControl FDControl fdataReader = new FDControl(); fdataReader.ParseFromLevel(lvl); //Add a music trigger to index 9 fdataReader.Entries[9].Add(new FDTriggerEntry { Setup = new FDSetup(FDFunctions.Trigger), TrigSetup = new FDTrigSetup(), TrigActionList = new List <FDActionListItem> { new FDActionListItem { TrigAction = FDTrigAction.PlaySoundtrack, Parameter = 40 } } }); //Write the data back fdataReader.WriteToLevel(lvl); //Save it and read it back in TR2LevelWriter writer = new TR2LevelWriter(); writer.WriteLevelToFile(lvl, "TEST.tr2"); lvl = reader.ReadLevel("TEST.tr2"); fdataReader = new FDControl(); fdataReader.ParseFromLevel(lvl); //Verify index 9 has two entries, that its first entry //does not have EndData set, but that its second does Assert.AreEqual(fdataReader.Entries[9].Count, 2); Assert.IsFalse(fdataReader.Entries[9][0].Setup.EndData); Assert.IsTrue(fdataReader.Entries[9][1].Setup.EndData); //Verify the trigger we added matches what we expect FDEntry entry = fdataReader.Entries[9][1]; Assert.IsTrue(entry is FDTriggerEntry); FDTriggerEntry triggerEntry = entry as FDTriggerEntry; Assert.IsTrue(triggerEntry.Setup.Function == (byte)FDFunctions.Trigger); Assert.IsTrue(triggerEntry.TrigActionList.Count == 1); Assert.IsTrue(triggerEntry.TrigActionList[0].TrigAction == FDTrigAction.PlaySoundtrack); Assert.IsTrue(triggerEntry.TrigActionList[0].Parameter == 40); }
public void FloorData_AppendFDActionListItemTest() { //Read Dragons Lair data TR2LevelReader reader = new TR2LevelReader(); TR2Level lvl = reader.ReadLevel("xian.tr2"); //Parse the floordata using FDControl FDControl fdataReader = new FDControl(); fdataReader.ParseFromLevel(lvl); //Add a music action to the trigger at index 13 FDTriggerEntry trigger = fdataReader.Entries[13][0] as FDTriggerEntry; Assert.AreEqual(trigger.TrigActionList.Count, 2); trigger.TrigActionList.Add(new FDActionListItem { TrigAction = FDTrigAction.PlaySoundtrack, Parameter = 40 }); //Write the data back fdataReader.WriteToLevel(lvl); //Save it and read it back in TR2LevelWriter writer = new TR2LevelWriter(); writer.WriteLevelToFile(lvl, "TEST.tr2"); lvl = reader.ReadLevel("TEST.tr2"); fdataReader = new FDControl(); fdataReader.ParseFromLevel(lvl); trigger = fdataReader.Entries[13][0] as FDTriggerEntry; // Verifying that the trigger has 3 items implicitly verifies that the Continue // flag was correctly changed on the previous last item and on the new item, // otherwise the parsing would have stopped at the second Assert.AreEqual(trigger.TrigActionList.Count, 3); Assert.IsTrue(trigger.TrigActionList[2].TrigAction == FDTrigAction.PlaySoundtrack); Assert.IsTrue(trigger.TrigActionList[2].Parameter == 40); }
public void FloorData_AppendFDActionListItemCamTest() { //Read Dragons Lair data TR2LevelReader reader = new TR2LevelReader(); TR2Level lvl = reader.ReadLevel("xian.tr2"); //Parse the floordata using FDControl FDControl fdataReader = new FDControl(); fdataReader.ParseFromLevel(lvl); //Add a music action to the trigger at index 6010 //This has a CamAction in its TrigList so this tests //that the Continue flag is correctly set FDTriggerEntry trigger = fdataReader.Entries[6010][1] as FDTriggerEntry; Assert.AreEqual(trigger.TrigActionList.Count, 2); Assert.IsNotNull(trigger.TrigActionList[1].CamAction); Assert.IsFalse(trigger.TrigActionList[1].CamAction.Continue); trigger.TrigActionList.Add(new FDActionListItem { TrigAction = FDTrigAction.PlaySoundtrack, Parameter = 40 }); //Write the data back fdataReader.WriteToLevel(lvl); //Check the CamAction has been updated Assert.AreEqual(trigger.TrigActionList.Count, 3); Assert.IsNotNull(trigger.TrigActionList[1].CamAction); Assert.IsTrue(trigger.TrigActionList[1].CamAction.Continue); //Check the music trigger has Continue set to false Assert.IsFalse(trigger.TrigActionList[2].Continue); }
public void FloorData_InsertFDTest() { //Read Dragons Lair data TR2LevelReader reader = new TR2LevelReader(); TR2Level lvl = reader.ReadLevel("xian.tr2"); //Parse the floordata using FDControl FDControl fdataReader = new FDControl(); fdataReader.ParseFromLevel(lvl); //Find a sector that currently has no floor data int room, roomSector = -1; for (room = 0; room < lvl.NumRooms; room++) { roomSector = lvl.Rooms[room].SectorList.ToList().FindIndex(s => s.FDIndex == 0); if (roomSector != -1) { break; } } if (roomSector == -1) { Assert.Fail("Could not locate a Room Sector that does not have floor data associated with it."); } TRRoomSector sector = lvl.Rooms[room].SectorList[roomSector]; // Create a slot in the FD for this sector fdataReader.CreateFloorData(sector); Assert.AreNotEqual(sector.FDIndex, 0, "Sector does not have FD allocated."); // Add a music trigger fdataReader.Entries[sector.FDIndex].Add(new FDTriggerEntry { Setup = new FDSetup(FDFunctions.Trigger), TrigSetup = new FDTrigSetup(), TrigActionList = new List <FDActionListItem> { new FDActionListItem { TrigAction = FDTrigAction.PlaySoundtrack, Parameter = 40 } } }); //Write the data back fdataReader.WriteToLevel(lvl); //Save it and read it back in TR2LevelWriter writer = new TR2LevelWriter(); writer.WriteLevelToFile(lvl, "TEST.tr2"); lvl = reader.ReadLevel("TEST.tr2"); //Reassign the sector sector = lvl.Rooms[room].SectorList[roomSector]; fdataReader = new FDControl(); fdataReader.ParseFromLevel(lvl); //Ensure the sector still has FD associated with it Assert.AreNotEqual(sector.FDIndex, 0, "Sector no longer has FD after write/read."); //Verify there is one entry for this sector Assert.AreEqual(fdataReader.Entries[sector.FDIndex].Count, 1); //Verify the trigger we added matches what we expect FDEntry entry = fdataReader.Entries[sector.FDIndex][0]; Assert.IsTrue(entry is FDTriggerEntry); FDTriggerEntry triggerEntry = entry as FDTriggerEntry; Assert.IsTrue(triggerEntry.Setup.Function == (byte)FDFunctions.Trigger); Assert.IsTrue(triggerEntry.TrigActionList.Count == 1); Assert.IsTrue(triggerEntry.TrigActionList[0].TrigAction == FDTrigAction.PlaySoundtrack); Assert.IsTrue(triggerEntry.TrigActionList[0].Parameter == 40); }
private void RandomizeSecretTracks(TR2Level level, FDControl floorData) { // Generate new triggers for secrets to allow different sounds for each one List <TRAudioTrack> secretTracks = _tracks[TRAudioCategory.Secret]; Dictionary <int, TR2Entity> secrets = GetSecretItems(level); foreach (int entityIndex in secrets.Keys) { TR2Entity secret = secrets[entityIndex]; TRRoomSector sector = FDUtilities.GetRoomSector(secret.X, secret.Y, secret.Z, secret.Room, level, floorData); if (sector.FDIndex == 0) { // The secret is positioned on a tile that currently has no FD, so create it floorData.CreateFloorData(sector); } List <FDEntry> entries = floorData.Entries[sector.FDIndex]; FDTriggerEntry existingTriggerEntry = entries.Find(e => e is FDTriggerEntry) as FDTriggerEntry; bool existingEntityPickup = false; if (existingTriggerEntry != null) { if (existingTriggerEntry.TrigType == FDTrigType.Pickup && existingTriggerEntry.TrigActionList[0].Parameter == entityIndex) { // GW gold secret (default location) already has a pickup trigger to spawn the // TRex (or whatever enemy) so we'll just append to that item list here existingEntityPickup = true; } else { // There is already a non-pickup trigger for this sector so while nothing is wrong with // adding a pickup trigger, the game ignores it. So in this instance, the sound that // plays will just be whatever is set in the script. continue; } } // Generate a new music action FDActionListItem musicAction = new FDActionListItem { TrigAction = FDTrigAction.PlaySoundtrack, Parameter = secretTracks[_generator.Next(0, secretTracks.Count)].ID }; // For GW default gold, just append it if (existingEntityPickup) { existingTriggerEntry.TrigActionList.Add(musicAction); } else { entries.Add(new FDTriggerEntry { // The values here are replicated from Trigger#112 (in trview) in GW. // The first action list must be the entity being picked up and so // remaining action list items are actioned on pick up. Setup = new FDSetup { Value = 1028 }, TrigSetup = new FDTrigSetup { Value = 15872 }, TrigActionList = new List <FDActionListItem> { new FDActionListItem { TrigAction = FDTrigAction.Object, Parameter = (ushort)entityIndex }, musicAction } }); } } }
public void ParseFromLevel(TR2Level lvl) { _entries = new SortedDictionary <int, List <FDEntry> >(); foreach (TR2Room room in lvl.Rooms) { foreach (TRRoomSector sector in room.SectorList) { //Index into FData is FDIndex ushort index = sector.FDIndex; //Index 0 is a dummy if (index == 0) { continue; } //List of floordata functions for the sector List <FDEntry> floordataFunctions = new List <FDEntry>(); while (true) { FDSetup data = new FDSetup() { Value = lvl.FloorData[index] }; switch ((FDFunctions)data.Function) { case FDFunctions.PortalSector: FDPortalEntry portal = new FDPortalEntry() { Setup = new FDSetup() { Value = lvl.FloorData[index] }, Room = lvl.FloorData[++index] }; floordataFunctions.Add(portal); break; case FDFunctions.FloorSlant: FDSlantEntry floorSlant = new FDSlantEntry() { Setup = new FDSetup() { Value = lvl.FloorData[index] }, SlantValue = lvl.FloorData[++index], Type = FDSlantEntryType.FloorSlant }; floordataFunctions.Add(floorSlant); break; case FDFunctions.CeilingSlant: FDSlantEntry ceilingSlant = new FDSlantEntry() { Setup = new FDSetup() { Value = lvl.FloorData[index] }, SlantValue = lvl.FloorData[++index], Type = FDSlantEntryType.CeilingSlant }; floordataFunctions.Add(ceilingSlant); break; case FDFunctions.Trigger: FDTriggerEntry trig = new FDTriggerEntry() { Setup = new FDSetup() { Value = lvl.FloorData[index] }, TrigSetup = new FDTrigSetup() { Value = lvl.FloorData[++index] } }; if (trig.TrigType == FDTrigType.Switch || trig.TrigType == FDTrigType.Key) { //First entry in action list is reference to switch/key entity for switch/key types. trig.SwitchOrKeyRef = lvl.FloorData[++index]; } //We don't know if there are any more yet. bool continueFDParse; //Parse trigactions do { //New trigger action FDActionListItem action = new FDActionListItem() { Value = lvl.FloorData[++index] }; continueFDParse = action.Continue; if (action.TrigAction == FDTrigAction.Camera) { //Camera trig actions have a special extra uint16... FDCameraAction camAction = new FDCameraAction() { Value = lvl.FloorData[++index] }; //store associated camera action action.CamAction = camAction; //Is there more? continueFDParse = camAction.Continue; } //add action trig.TrigActionList.Add(action); } while (index < lvl.NumFloorData && continueFDParse); floordataFunctions.Add(trig); break; case FDFunctions.KillLara: FDKillLaraEntry kill = new FDKillLaraEntry() { Setup = new FDSetup() { Value = lvl.FloorData[index] } }; floordataFunctions.Add(kill); break; case FDFunctions.ClimbableWalls: FDClimbEntry climb = new FDClimbEntry() { Setup = new FDSetup() { Value = lvl.FloorData[index] } }; floordataFunctions.Add(climb); break; case FDFunctions.FloorTriangulationNWSE_Solid: //TR3 Only - Ignore for now... break; case FDFunctions.FloorTriangulationNESW_Solid: //TR3 Only - Ignore for now... break; case FDFunctions.CeilingTriangulationNW_Solid: //TR3 Only - Ignore for now... break; case FDFunctions.CeilingTriangulationNE_Solid: //TR3 Only - Ignore for now... break; case FDFunctions.FloorTriangulationNWSE_SW: //TR3 Only - Ignore for now... break; case FDFunctions.FloorTriangulationNWSE_NE: //TR3 Only - Ignore for now... break; case FDFunctions.FloorTriangulationNESW_SW: //TR3 Only - Ignore for now... break; case FDFunctions.FloorTriangulationNESW_NW: //TR3 Only - Ignore for now... break; case FDFunctions.CeilingTriangulationNW_SW: //TR3 Only - Ignore for now... break; case FDFunctions.CeilingTriangulationNW_NE: //TR3 Only - Ignore for now... break; case FDFunctions.CeilingTriangulationNE_NW: //TR3 Only - Ignore for now... break; case FDFunctions.CeilingTriangulationNE_SE: //TR3 Only - Ignore for now... break; case FDFunctions.Monkeyswing: //TR3 Only - Ignore for now... break; case FDFunctions.DeferredTriggeringOrMinecartRotateLeft: //TR3 Only - Ignore for now... break; case FDFunctions.MechBeetleOrMinecartRotateRight: //TR3 Only - Ignore for now... break; default: break; } if (data.EndData) { //End data (from what I understand) means there is no further functions for this sector. //E.G. Sector 52 on Xian has a slant function and portal function. EndData is not set on //slant function, but is on portal function as there are no further functions. break; } else { //There are further functions for this sector - continue parsing. index++; } } //Store the sector index and all of its associated functions _entries.Add(sector.FDIndex, floordataFunctions); } } }