/// <summary> /// Unlock a specific door within an SRim file. /// </summary> /// <param name="paths">KPaths object for this game.</param> /// <param name="area">Name of the SRim file to modify.</param> /// <param name="label">Label of the door to unlock.</param> private static void UnlockDoorInFile(KPaths paths, string area, string label) { var areaFiles = paths.FilesInModules.Where(fi => fi.Name.Contains(LookupTable[area])); foreach (FileInfo fi in areaFiles) { // Skip any files that don't end in "s.rim". if (fi.Name[fi.Name.Length - 5] != 's') { continue; } RIM r = new RIM(fi.FullName); // Open what replaced this area. RIM.rFile rf = r.File_Table.FirstOrDefault(x => x.Label == label); GFF g = new GFF(rf.File_Data); // Grab the door out of the file. // Set fields related to opening and unlocking. (g.Top_Level.Fields.FirstOrDefault(x => x.Label == "KeyRequired") as GFF.BYTE).Value = 0; (g.Top_Level.Fields.FirstOrDefault(x => x.Label == "Locked") as GFF.BYTE).Value = 0; (g.Top_Level.Fields.FirstOrDefault(x => x.Label == "OpenLockDC") as GFF.BYTE).Value = 0; (g.Top_Level.Fields.FirstOrDefault(x => x.Label == "Plot") as GFF.BYTE).Value = 0; // Set fields related to bashing open. (g.Top_Level.Fields.FirstOrDefault(x => x.Label == "Hardness") as GFF.BYTE).Value = 0; (g.Top_Level.Fields.FirstOrDefault(x => x.Label == "HP") as GFF.SHORT).Value = 1; (g.Top_Level.Fields.FirstOrDefault(x => x.Label == "CurrentHP") as GFF.SHORT).Value = 1; (g.Top_Level.Fields.FirstOrDefault(x => x.Label == "Min1HP") as GFF.BYTE).Value = 0; // Write change(s) to file. rf.File_Data = g.ToRawData(); r.WriteToFile(fi.FullName); } }
/// <summary> /// Update warp coordinates that are in bad locations by default. /// </summary> /// <param name="paths">KPaths object for this game.</param> private static void FixWarpCoordinates(KPaths paths) { // Create a lookup for modules needing coordinate fix with their newly shuffled FileInfos. var shuffleFileLookup = new Dictionary <string, FileInfo>(); foreach (var key in Globals.FIXED_COORDINATES.Keys) { shuffleFileLookup.Add(key, paths.FilesInModules.FirstOrDefault(fi => fi.Name.Contains(LookupTable[key]))); } foreach (var kvp in shuffleFileLookup) { // Set up objects. RIM r = new RIM(kvp.Value.FullName); RIM.rFile rf = r.File_Table.FirstOrDefault(x => x.TypeID == (int)ResourceType.IFO); GFF g = new GFF(rf.File_Data); // Update coordinate data. (g.Top_Level.Fields.FirstOrDefault(x => x.Label == Properties.Resources.ModuleEntryX) as GFF.FLOAT).Value = Globals.FIXED_COORDINATES[kvp.Key].Item1; (g.Top_Level.Fields.FirstOrDefault(x => x.Label == Properties.Resources.ModuleEntryY) as GFF.FLOAT).Value = Globals.FIXED_COORDINATES[kvp.Key].Item2; (g.Top_Level.Fields.FirstOrDefault(x => x.Label == Properties.Resources.ModuleEntryZ) as GFF.FLOAT).Value = Globals.FIXED_COORDINATES[kvp.Key].Item3; // Write updated data to RIM file. rf.File_Data = g.ToRawData(); r.WriteToFile(kvp.Value.FullName); } }
//Randomize Dialogue static void shuffle_dialogue(KPaths paths, bool Entries, bool Replies, bool SoundMatching) { TLK t = new TLK(paths.dialog); foreach (FileInfo fi in paths.FilesInModules) { if (fi.Name[fi.Name.Length - 5] != 's') { continue; } RIM r = new RIM(fi.FullName); foreach (RIM.rFile RF in r.File_Table.Where(x => x.TypeID == (int)ResourceType.DLG)) { GFF g = new GFF(RF.File_Data); //Entries if (Entries) { foreach (GFF.STRUCT S in (g.Top_Level.Fields.Where(x => x.Label == "EntryList").FirstOrDefault() as GFF.LIST).Structs) { if ((S.Fields.Where(x => x.Label == "Text").FirstOrDefault() as GFF.CExoLocString).StringRef != -1) // Avoid overwriting dialogue end indicators, and animation nodes { int str_ref = 0; // Find valid string while (t.String_Data_Table[str_ref].SoundResRef == "" || t.String_Data_Table[str_ref].SoundResRef[0] == '_' || t.String_Data_Table[str_ref].SoundResRef.ToLower().Contains("comp")) //Ensure the string we have has a sound to go with it, starting with undescord means it's player dialogue, which doesn't have audio in this game { str_ref = Randomize.Rng.Next(TLK_STRING_COUNT); } if (!EntriesLookupTable.ContainsKey(fi.Name)) { EntriesLookupTable.Add(fi.Name, new Dictionary <string, List <Tuple <int, int, string, string, string, string> > >()); } if (!EntriesLookupTable[fi.Name].ContainsKey(RF.Label)) { EntriesLookupTable[fi.Name].Add(RF.Label, new List <Tuple <int, int, string, string, string, string> >()); } // Sound and Text Matching if (SoundMatching) { var text = S.Fields.Where(x => x.Label == "Text").FirstOrDefault() as GFF.CExoLocString; int textOrig = text.StringRef; int textRand = str_ref; text.StringRef = str_ref; string VORefOrig = ""; string VORefRand = ""; string SoundOrig = ""; string SoundRand = ""; try { var voResRef = S.Fields.Where(x => x.Label == "VO_ResRef").FirstOrDefault() as GFF.ResRef; VORefOrig = voResRef.Reference; VORefRand = t.String_Data_Table[str_ref].SoundResRef; voResRef.Reference = VORefRand; } catch { VORefOrig = ""; VORefRand = ""; } try { var sound = S.Fields.Where(x => x.Label == "Sound").FirstOrDefault() as GFF.ResRef; SoundOrig = sound.Reference; SoundRand = t.String_Data_Table[str_ref].SoundResRef; sound.Reference = SoundRand; } catch { SoundOrig = ""; SoundRand = ""; } // If both VO_ResRef and Sound Fail we ignore the entry EntriesLookupTable[fi.Name][RF.Label].Add(new Tuple <int, int, string, string, string, string>(textOrig, textRand, VORefOrig, VORefRand, SoundOrig, SoundRand)); } else { var text = S.Fields.Where(x => x.Label == "Text").FirstOrDefault() as GFF.CExoLocString; EntriesLookupTable[fi.Name][RF.Label].Add(new Tuple <int, int, string, string, string, string>(text.StringRef, str_ref, "", "", "", "")); text.StringRef = str_ref; } } } } //Replies if (Replies) { foreach (GFF.STRUCT S in (g.Top_Level.Fields.Where(x => x.Label == "ReplyList").FirstOrDefault() as GFF.LIST).Structs) { if ((S.Fields.Where(x => x.Label == "Text").FirstOrDefault() as GFF.CExoLocString).StringRef != -1) //Avoid overwriting dialogue end indicators, and animation nodes { if (!RepliesLookupTable.ContainsKey(fi.Name)) { RepliesLookupTable.Add(fi.Name, new Dictionary <string, List <Tuple <int, int> > >()); } if (!RepliesLookupTable[fi.Name].ContainsKey(RF.Label)) { RepliesLookupTable[fi.Name].Add(RF.Label, new List <Tuple <int, int> >()); } int str_ref = Randomize.Rng.Next(TLK_STRING_COUNT); while (t.String_Data_Table[str_ref].StringText == "") { str_ref = Randomize.Rng.Next(TLK_STRING_COUNT); } var text = S.Fields.Where(x => x.Label == "Text").FirstOrDefault() as GFF.CExoLocString; RepliesLookupTable[fi.Name][RF.Label].Add(new Tuple <int, int>(text.StringRef, str_ref)); text.StringRef = str_ref; } } } Array.Clear(RF.File_Data, 0, RF.File_Data.Length); RF.File_Data = g.ToRawData(); } r.WriteToFile(fi.FullName); } }
/// <summary> /// Unlock Leviathan Hangar option in the other two elevator access, and enables the use of the Hangar elevator. /// </summary> /// <param name="paths">KPaths object for this game.</param> private static void FixLeviathanElevators(KPaths paths) { var lev_files_a = paths.FilesInModules.Where(fi => fi.Name.Contains(LookupTable[AREA_LEV_PRISON])); var lev_files_b = paths.FilesInModules.Where(fi => fi.Name.Contains(LookupTable[AREA_LEV_COMMAND])); var lev_files_c = paths.FilesInModules.Where(fi => fi.Name.Contains(LookupTable[AREA_LEV_HANGAR])); // Prison Block Fix - Unlock option to visit Hangar. foreach (FileInfo fi in lev_files_a) { // Skip any files that don't end in "s.rim". if (fi.Name[fi.Name.Length - 5] != 's') { continue; } RIM r_lev = new RIM(fi.FullName); GFF g_lev = new GFF(r_lev.File_Table.FirstOrDefault(x => x.Label == LABEL_LEV_ELEVATOR_A).File_Data); // Change Entry connecting for bridge option Index to 3, which will transition to the command deck (((g_lev.Top_Level.Fields.FirstOrDefault(x => x.Label == "ReplyList") as GFF.LIST).Structs.FirstOrDefault(x => x.Struct_Type == 3).Fields.FirstOrDefault(x => x.Label == "EntriesList") as GFF.LIST).Structs.FirstOrDefault(x => x.Struct_Type == 0).Fields.FirstOrDefault(x => x.Label == "Index") as GFF.DWORD).Value = 3; // Sets the active reference for the hangar option to nothing, meaning there is no requirement to transition to the hangar (((g_lev.Top_Level.Fields.FirstOrDefault(x => x.Label == "ReplyList") as GFF.LIST).Structs.FirstOrDefault(x => x.Struct_Type == 1).Fields.FirstOrDefault(x => x.Label == "EntriesList") as GFF.LIST).Structs.FirstOrDefault(x => x.Struct_Type == 0).Fields.FirstOrDefault(x => x.Label == "Active") as GFF.ResRef).Reference = ""; r_lev.File_Table.FirstOrDefault(x => x.Label == LABEL_LEV_ELEVATOR_A).File_Data = g_lev.ToRawData(); r_lev.WriteToFile(fi.FullName); } // Command Deck Fix - Unlock option to visit Hangar. foreach (FileInfo fi in lev_files_b) { // Skip any files that don't end in "s.rim". if (fi.Name[fi.Name.Length - 5] != 's') { continue; } RIM r_lev = new RIM(fi.FullName); GFF g_lev = new GFF(r_lev.File_Table.FirstOrDefault(x => x.Label == LABEL_LEV_ELEVATOR_B).File_Data); // Sets the active reference for the hangar option to nothing, meaning there is no requirement to transition to the hangar (((g_lev.Top_Level.Fields.FirstOrDefault(x => x.Label == "ReplyList") as GFF.LIST).Structs.FirstOrDefault(x => x.Struct_Type == 1).Fields.FirstOrDefault(x => x.Label == "EntriesList") as GFF.LIST).Structs.FirstOrDefault(x => x.Struct_Type == 1).Fields.FirstOrDefault(x => x.Label == "Active") as GFF.ResRef).Reference = ""; r_lev.File_Table.FirstOrDefault(x => x.Label == LABEL_LEV_ELEVATOR_B).File_Data = g_lev.ToRawData(); r_lev.WriteToFile(fi.FullName); } // Hangar Fix - Enable the elevator so it can be used. foreach (FileInfo fi in lev_files_c) { // Skip any files that don't end in "s.rim". if (fi.Name[fi.Name.Length - 5] != 's') { continue; } RIM r_lev = new RIM(fi.FullName); // While I possess the ability to edit this file programmatically, due to the complexity I have opted to just load the modded file into resources. r_lev.File_Table.FirstOrDefault(x => x.Label == LABEL_LEV_ELEVATOR_C).File_Data = Properties.Resources.lev40_accntl_dlg; // Adding module transition scripts to RIM... // Prison Block r_lev.File_Table.Add(new RIM.rFile { TypeID = (int)ResourceType.NCS, Label = "k_plev_goto40aa", File_Data = Properties.Resources.k_plev_goto40aa }); // Command Deck r_lev.File_Table.Add(new RIM.rFile { TypeID = (int)ResourceType.NCS, Label = "k_plev_goto40ab", File_Data = Properties.Resources.k_plev_goto40ab }); r_lev.WriteToFile(fi.FullName); } }
public static void model_rando(KPaths paths) { foreach (FileInfo fi in paths.FilesInModules) { RIM r = new RIM(fi.FullName); // Doors if ((Properties.Settings.Default.RandomizeDoorModels & 1) > 0) { foreach (RIM.rFile rf in r.File_Table.Where(x => x.TypeID == (int)ResourceType.UTD)) { GFF g = new GFF(rf.File_Data); int temp = 0; if ((Properties.Settings.Default.RandomizeDoorModels & 4) > 0) // Broken Doors { temp = Randomize.Rng.Next(13, 64); // First 12 doors are open so this is easier } else { temp = Randomize.Rng.Next(0, 64); } // Airlock if ((Properties.Settings.Default.RandomizeDoorModels & 2) > 0 && (g.Field_Array.Where(x => x.Label == "LocName").FirstOrDefault().Field_Data as GFF.CExoLocString).StringRef == 21080) { continue; } g.Field_Array.Where(k => k.Label == "GenericType").FirstOrDefault().Field_Data = temp; g.Field_Array.Where(k => k.Label == "GenericType").FirstOrDefault().DataOrDataOffset = temp; MemoryStream ms = new MemoryStream(); rf.File_Data = g.ToRawData(); } } //Placeables if ((Properties.Settings.Default.RandomizePlaceModels & 1) > 0) { foreach (RIM.rFile rf in r.File_Table.Where(k => k.TypeID == 2044)) { GFF g = new GFF(rf.File_Data); int temp = Randomize.Rng.Next(0, 231); bool broken_satisfied = !((Properties.Settings.Default.RandomizePlaceModels & 4) > 0) || !Globals.BROKEN_PLACE.Contains(temp); //Always Satisfied if Broken omission disbaled bool large_satisfied = !((Properties.Settings.Default.RandomizePlaceModels & 2) > 0) || !Globals.LARGE_PLACE.Contains(temp); //Always satisifed if Large omission disabled while (!(broken_satisfied && large_satisfied)) { temp = Randomize.Rng.Next(0, 231); broken_satisfied = !((Properties.Settings.Default.RandomizePlaceModels & 4) > 0) || !Globals.BROKEN_PLACE.Contains(temp); //Always Satisfied if Broken omission disbaled large_satisfied = !((Properties.Settings.Default.RandomizePlaceModels & 2) > 0) || !Globals.LARGE_PLACE.Contains(temp); //Always satisifed if Large omission disabled } g.Field_Array.Where(k => k.Label == "Appearance").FirstOrDefault().Field_Data = temp; g.Field_Array.Where(k => k.Label == "Appearance").FirstOrDefault().DataOrDataOffset = temp; MemoryStream ms = new MemoryStream(); rf.File_Data = g.ToRawData(); } } //Characters if ((Properties.Settings.Default.RandomizeCharModels & 1) > 0) { foreach (RIM.rFile rf in r.File_Table.Where(k => k.TypeID == 2027)) { GFF g = new GFF(rf.File_Data); int temp = Randomize.Rng.Next(0, 508); bool broken_satisfied = !((Properties.Settings.Default.RandomizeCharModels & 4) > 0) || !Globals.BROKEN_CHARS.Contains(temp); //Always Satisfied if Broken omission disbaled bool large_satisfied = !((Properties.Settings.Default.RandomizeCharModels & 2) > 0) || !Globals.LARGE_CHARS.Contains(temp); //Always satisifed if Large omission disabled while (!(broken_satisfied && large_satisfied)) { temp = Randomize.Rng.Next(0, 508); broken_satisfied = !((Properties.Settings.Default.RandomizeCharModels & 4) > 0) || !Globals.BROKEN_CHARS.Contains(temp); //Always Satisfied if Broken omission disbaled large_satisfied = !((Properties.Settings.Default.RandomizeCharModels & 2) > 0) || !Globals.LARGE_CHARS.Contains(temp); //Always satisifed if Large omission disabled } g.Field_Array.Where(k => k.Label == "Appearance_Type").FirstOrDefault().Field_Data = temp; g.Field_Array.Where(k => k.Label == "Appearance_Type").FirstOrDefault().DataOrDataOffset = temp; MemoryStream ms = new MemoryStream(); rf.File_Data = g.ToRawData(); } } r.WriteToFile(fi.FullName); } }
// Populates and shuffles the the modules flagged to be randomized. Returns true if override files should be added. public static void Module_rando(KPaths paths) { // Set up the bound module collection if it hasn't been already. if (!Properties.Settings.Default.ModulesInitialized) { Globals.BoundModules.Clear(); foreach (string s in Globals.MODULES) { Globals.BoundModules.Add(new Globals.Mod_Entry(s, true)); } Properties.Settings.Default.ModulesInitialized = true; } //if (!Properties.Settings.Default.ModulePresetSelected) //{ // //Figure something out here //} // Split the Bound modules into their respective lists. List <string> ExcludedModules = Globals.BoundModules.Where(x => x.Omitted).Select(x => x.Name).ToList(); List <string> IncludedModules = Globals.BoundModules.Where(x => !x.Omitted).Select(x => x.Name).ToList(); // Shuffle the list of included modules. List <string> ShuffledModules = IncludedModules.ToList(); Randomize.FisherYatesShuffle(ShuffledModules); // Copy shuffled modules into the base directory. Dictionary <string, string> LookupTable = new Dictionary <string, string>(); // Create lookup table to find a given module's new "name". for (int i = 0; i < IncludedModules.Count; i++) { LookupTable.Add(IncludedModules[i], ShuffledModules[i]); File.Copy($"{paths.modules_backup}{IncludedModules[i]}.rim", $"{paths.modules}{ShuffledModules[i]}.rim", true); File.Copy($"{paths.modules_backup}{IncludedModules[i]}_s.rim", $"{paths.modules}{ShuffledModules[i]}_s.rim", true); File.Copy($"{paths.lips_backup}{IncludedModules[i]}_loc.mod", $"{paths.lips}{ShuffledModules[i]}_loc.mod", true); } // Copy excluded, untouched modules into the base directory. foreach (string name in ExcludedModules) { LookupTable.Add(name, name); File.Copy($"{paths.modules_backup}{name}.rim", $"{paths.modules}{name}.rim", true); File.Copy($"{paths.modules_backup}{name}_s.rim", $"{paths.modules}{name}_s.rim", true); File.Copy($"{paths.lips_backup}{name}_loc.mod", $"{paths.lips}{name}_loc.mod", true); } // Copy lips extras into the base directory. foreach (string name in Globals.lipXtras) { File.Copy($"{paths.lips_backup}{name}", $"{paths.lips}{name}", true); } // Write additional override files. string moduleSavePath = Path.Combine(paths.Override, TwoDA_MODULE_SAVE); ModuleExtras saveFileExtras = Properties.Settings.Default.ModuleExtrasValue & (ModuleExtras.SaveAllModules | ModuleExtras.SaveMiniGames | ModuleExtras.NoSaveDelete); //if (0 == (saveFileExtras ^ (ModuleExtras.Default))) //{ // // 0b000 - Milestone Delete (Default) // // Do nothing. //} if (0 == (saveFileExtras ^ (ModuleExtras.NoSaveDelete))) { // 0b001 - No Milestone Delete File.WriteAllBytes(moduleSavePath, Properties.Resources.NODELETE_modulesave); } if (0 == (saveFileExtras ^ (ModuleExtras.SaveMiniGames))) { // 0b010 - Include Minigames | Milestone Delete File.WriteAllBytes(moduleSavePath, Properties.Resources.MGINCLUDED_modulesave); } if (0 == (saveFileExtras ^ (ModuleExtras.NoSaveDelete | ModuleExtras.SaveMiniGames))) { // 0b011 - Include Minigames | No Milestone Delete File.WriteAllBytes(moduleSavePath, Properties.Resources.NODELETE_MGINCLUDED_modulesave); } if (0 == (saveFileExtras ^ (ModuleExtras.SaveAllModules)) || 0 == (saveFileExtras ^ (ModuleExtras.SaveMiniGames | ModuleExtras.SaveAllModules))) { // Treat both the same. // 0b100 - Include All Modules | Milestone Delete // 0b110 - Include All Modules | Include Minigames | Milestone Delete File.WriteAllBytes(moduleSavePath, Properties.Resources.ALLINCLUDED_modulesave); } if (0 == (saveFileExtras ^ (ModuleExtras.NoSaveDelete | ModuleExtras.SaveAllModules)) || 0 == (saveFileExtras ^ (ModuleExtras.NoSaveDelete | ModuleExtras.SaveMiniGames | ModuleExtras.SaveAllModules))) { // Treat both the same. // 0b101 - Include All Modules | No Milestone Delete // 0b111 - Include All Modules | Include Minigames | No Milestone Delete File.WriteAllBytes(moduleSavePath, Properties.Resources.NODELETE_ALLINCLUDED_modulesave); } if (Properties.Settings.Default.ModuleExtrasValue.HasFlag(ModuleExtras.FixDream)) { File.WriteAllBytes(Path.Combine(paths.Override, FIXED_DREAM_OVERRIDE), Properties.Resources.k_ren_visionland); } if (Properties.Settings.Default.ModuleExtrasValue.HasFlag(ModuleExtras.UnlockGalaxyMap)) { File.WriteAllBytes(Path.Combine(paths.Override, UNLOCK_MAP_OVERRIDE), Properties.Resources.k_pebn_galaxy); } // Fix warp coordinates. if (Properties.Settings.Default.ModuleExtrasValue.HasFlag(ModuleExtras.FixCoordinates)) { // Create a lookup for modules needing coordinate fix with their newly shuffled FileInfos. var shuffleFileLookup = new Dictionary <string, FileInfo>(); foreach (var key in Globals.FIXED_COORDINATES.Keys) { shuffleFileLookup.Add(key, paths.FilesInModules.FirstOrDefault(fi => fi.Name.Contains(LookupTable[key]))); } foreach (var kvp in shuffleFileLookup) { // Set up objects. RIM r = new RIM(kvp.Value.FullName); RIM.rFile rf = r.File_Table.Where(x => x.TypeID == (int)ResourceType.IFO).FirstOrDefault(); GFF g = new GFF(rf.File_Data); // Update coordinate data. g.Field_Array.Where(x => x.Label == Properties.Resources.ModuleEntryX).FirstOrDefault().DataOrDataOffset = Globals.FIXED_COORDINATES[kvp.Key].Item1; g.Field_Array.Where(x => x.Label == Properties.Resources.ModuleEntryY).FirstOrDefault().DataOrDataOffset = Globals.FIXED_COORDINATES[kvp.Key].Item2; g.Field_Array.Where(x => x.Label == Properties.Resources.ModuleEntryZ).FirstOrDefault().DataOrDataOffset = Globals.FIXED_COORDINATES[kvp.Key].Item3; // Write updated data to RIM file. rf.File_Data = g.ToRawData(); r.WriteToFile(kvp.Value.FullName); } } // Fixed Rakata riddle Man in Mind Prison. if (Properties.Settings.Default.ModuleExtrasValue.HasFlag(ModuleExtras.FixMindPrison)) { // Find the files associated with AREA_MYSTERY_BOX. var files = paths.FilesInModules.Where(fi => fi.Name.Contains(LookupTable[AREA_MYSTERY_BOX])); foreach (FileInfo fi in files) { // Skip any files that don't end in "s.rim". if (fi.Name[fi.Name.Length - 5] != 's') { continue; } // Check the RIM's File_Table for any rFiles labeled with LABEL_MIND_PRISON. RIM r = new RIM(fi.FullName); if (r.File_Table.Where(x => x.Label == LABEL_MIND_PRISON).Any()) { bool offadjust = false; foreach (RIM.rFile rf in r.File_Table) { // For the rFile with LABEL_MIND_PRISON, update the file data with the fix. if (rf.Label == LABEL_MIND_PRISON) { rf.File_Data = Properties.Resources.g_brakatan003; rf.DataSize += 192; offadjust = true; continue; } // For rFiles after LABEL_MIND_PRISON, add the additional data offset. if (offadjust) { rf.DataOffset += 192; } } // Write updated RIM data to file. r.WriteToFile(fi.FullName); } } } }
public static void model_rando(KPaths paths) { const int MAX_CHAR_INDEX = 509; const int MIN_DOOR_INDEX_BROKEN = 13; const int MAX_DOOR_INDEX = 65; const int MAX_PLAC_INDEX = 232; const string CHAR_2DA = "appearance"; const string DOOR_2DA = "genericdoors"; const string PLAC_2DA = "placeables"; const string COL_LABEL = "label"; LookupTable.Clear(); BIF bif = new BIF(Path.Combine(paths.data, "2da.bif")); KEY key = new KEY(paths.chitin); bif.AttachKey(key, "data\\2da.bif"); var charVRE = bif.VariableResourceTable.Where(x => x.ResRef == CHAR_2DA).FirstOrDefault(); var doorVRE = bif.VariableResourceTable.Where(x => x.ResRef == DOOR_2DA).FirstOrDefault(); var placVRE = bif.VariableResourceTable.Where(x => x.ResRef == PLAC_2DA).FirstOrDefault(); TwoDA char2DA = new TwoDA(charVRE.EntryData, charVRE.ResRef); TwoDA door2DA = new TwoDA(doorVRE.EntryData, doorVRE.ResRef); TwoDA plac2DA = new TwoDA(placVRE.EntryData, placVRE.ResRef); // Check if the floor panel fix is enabled. bool isFloorPanelActive = (Properties.Settings.Default.RandomizePlaceModels & 8) > 0; var catacombsFile = AREA_UNK_CATACOMBS; // Check if modules have been randomized. var moduleFiles = paths.FilesInModules.ToList(); if (ModuleRando.LookupTable.Any()) { // If randomized, ensure module files are processed in the same order every time. var sortedLookup = ModuleRando.LookupTable.OrderBy(kvp => kvp.Key); var newList = new List<FileInfo>(); foreach (var kvp in sortedLookup) { // Find the file that has replaced the catacombs for the floor panel fix. if (kvp.Key == AREA_UNK_CATACOMBS) catacombsFile = kvp.Value; // Add the files to modify to the new list in proper order. var filesToAdd = moduleFiles.Where(fi => fi.Name.Contains(kvp.Value)); newList.AddRange(filesToAdd); } moduleFiles = newList; } // Loop through each file and randomize the requested model types. foreach (FileInfo fi in moduleFiles) { RIM r = new RIM(fi.FullName); LookupTable.Add(fi.Name, new Dictionary<string, Dictionary<string, Tuple<int, string, int, string>>>()); // Doors if ((Properties.Settings.Default.RandomizeDoorModels & 1) > 0) { LookupTable[fi.Name].Add(DOOR, new Dictionary<string, Tuple<int, string, int, string>>()); foreach (RIM.rFile rf in r.File_Table.Where(x => x.TypeID == (int)ResourceType.UTD)) { GFF g = new GFF(rf.File_Data); int randAppear = 0; //The randomly generated Appearance ID //Generate the random appearacne values before ommitting airlock, to create more seed consistancy if ((Properties.Settings.Default.RandomizeDoorModels & 4) > 0) // Broken Doors { randAppear = Randomize.Rng.Next(MIN_DOOR_INDEX_BROKEN, MAX_DOOR_INDEX); // First 12 doors are open so this is easier } else { randAppear = Randomize.Rng.Next(0, MAX_DOOR_INDEX); } // Airlock if ((Properties.Settings.Default.RandomizeDoorModels & 2) > 0 && (g.Top_Level.Fields.Where(f => f.Label == LBL_LOC_NAME).FirstOrDefault() as GFF.CExoLocString).StringRef == 21080) { continue; } //Get Info from Door2DA for the Spoiler Log var field = g.Top_Level.Fields.Where(f => f.Label == LBL_GENERIC_TYPE).FirstOrDefault() as GFF.BYTE; int id = (int)field.Value; var label_old = door2DA.Data[COL_LABEL][id]; var label_new = door2DA.Data[COL_LABEL][randAppear]; LookupTable[fi.Name][DOOR].Add(rf.Label, new Tuple<int, string, int, string>(id, label_old, randAppear, label_new)); //Change the appearance value (g.Top_Level.Fields.Where(f => f.Label == LBL_GENERIC_TYPE).FirstOrDefault() as GFF.BYTE).Value = (byte)randAppear; rf.File_Data = g.ToRawData(); } } // Placeables if ((Properties.Settings.Default.RandomizePlaceModels & 1) > 0) { LookupTable[fi.Name].Add(PLACEABLE, new Dictionary<string, Tuple<int, string, int, string>>()); // Check if floor panels should be replaced with valid placeables. bool useValidFloorPanels = isFloorPanelActive && fi.Name.Contains(catacombsFile); foreach (RIM.rFile rf in r.File_Table.Where(k => k.TypeID == (int)ResourceType.UTP)) { GFF g = new GFF(rf.File_Data); // If this is a broken placeable, skip it. if (Globals.BROKEN_PLACE.Contains((int)(g.Top_Level.Fields.Where(f => f.Label == LBL_APPEARANCE).FirstOrDefault() as GFF.DWORD).Value)) { continue; } int randAppear = 0; // Randomly generate a valid replacement for the "Lights Out" panels. if (useValidFloorPanels && (rf.Label.StartsWith(LABEL_UNK_FLPNL) || rf.Label == LABEL_UNK_RESETPANEL)) { randAppear = Globals.PANEL_PLACE[Randomize.Rng.Next(0, Globals.PANEL_PLACE.Count)]; } else { // Generate a random appearance for this placeable. bool isBroken = false; bool isLarge = false; do { randAppear = Randomize.Rng.Next(0, MAX_PLAC_INDEX); isBroken = ((Properties.Settings.Default.RandomizePlaceModels & 4) > 0) && Globals.BROKEN_PLACE.Contains(randAppear); // Always Satisfied if Broken omission disbaled isLarge = ((Properties.Settings.Default.RandomizePlaceModels & 2) > 0) && Globals.LARGE_PLACE.Contains(randAppear); // Always satisifed if Large omission disabled } while (isBroken || isLarge); } var field = g.Top_Level.Fields.Where(f => f.Label == LBL_APPEARANCE).FirstOrDefault() as GFF.DWORD; int id = (int)field.Value; var label_old = plac2DA.Data[COL_LABEL][id]; var label_new = plac2DA.Data[COL_LABEL][randAppear]; LookupTable[fi.Name][PLACEABLE].Add(rf.Label, new Tuple<int, string, int, string>(id, label_old, randAppear, label_new)); // Change the appearance value. (g.Top_Level.Fields.Where(f => f.Label == LBL_APPEARANCE).FirstOrDefault() as GFF.DWORD).Value = (uint)randAppear; rf.File_Data = g.ToRawData(); } } // Characters if ((Properties.Settings.Default.RandomizeCharModels & 1) > 0) { LookupTable[fi.Name].Add(CHARACTER, new Dictionary<string, Tuple<int, string, int, string>>()); foreach (RIM.rFile rf in r.File_Table.Where(k => k.TypeID == (int)ResourceType.UTC)) { GFF g = new GFF(rf.File_Data); int randAppear = 0; bool isBroken = false; bool isLarge = false; do { randAppear = Randomize.Rng.Next(0, MAX_CHAR_INDEX); isBroken = ((Properties.Settings.Default.RandomizeCharModels & 4) > 0) && Globals.BROKEN_CHARS.Contains(randAppear); // Always Satisfied if Broken omission disabled isLarge = ((Properties.Settings.Default.RandomizeCharModels & 2) > 0) && Globals.LARGE_CHARS.Contains(randAppear); // Always satisifed if Large omission disabled } while (isBroken || isLarge); var field = g.Top_Level.Fields.Where(f => f.Label == LBL_APPEARANCE_TYPE).FirstOrDefault() as GFF.WORD; int id = (int)field.Value; var label_old = char2DA.Data[COL_LABEL][id]; var label_new = char2DA.Data[COL_LABEL][randAppear]; LookupTable[fi.Name][CHARACTER].Add(rf.Label, new Tuple<int, string, int, string>(id, label_old, randAppear, label_new)); (g.Top_Level.Fields.Where(f => f.Label == LBL_APPEARANCE_TYPE).FirstOrDefault() as GFF.WORD).Value = (ushort)randAppear; rf.File_Data = g.ToRawData(); } } r.WriteToFile(fi.FullName); } }