/// <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); } }
/// <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> /// Unlock the doors requested by the user. /// </summary> /// <param name="paths">KPaths object for this game.</param> private static void UnlockDoors(KPaths paths) { var extrasValue = Properties.Settings.Default.ModuleExtrasValue; // Dantooine Ruins if (extrasValue.HasFlag(ModuleExtras.UnlockDanRuins)) { UnlockDoorInFile(paths, AREA_DAN_COURTYARD, LABEL_DANT_DOOR); } // Leviathan Elevators if (extrasValue.HasFlag(ModuleExtras.UnlockLevElev)) { FixLeviathanElevators(paths); } // Manaan Embassy Door to Submersible if (extrasValue.HasFlag(ModuleExtras.UnlockManSub)) { UnlockDoorInFile(paths, AREA_MAN_EAST_CENTRAL, LABEL_MAN_SUB_DOOR); } // Star Forge Door to Bastila if (extrasValue.HasFlag(ModuleExtras.UnlockStaBastila)) { UnlockDoorInFile(paths, AREA_STA_DECK3, LABEL_STA_BAST_DOOR); } // Lehon Temple Roof if (extrasValue.HasFlag(ModuleExtras.UnlockUnkSummit)) { UnlockDoorInFile(paths, AREA_UNK_SUMMIT, LABEL_UNK_DOOR); } }
/// <summary> /// Allow the Mystery Box and Mind Prison to be used more than once. /// </summary> /// <param name="paths">KPaths object for this game.</param> private static void FixMindPrison(KPaths paths) { // Allowing the Mystery Box to be accessed multiple times. ReplaceSRimFileData(paths, AREA_EBO_HAWK, LABEL_EBO_BOX, Properties.Resources.pebn_mystery); // Allowing Riddles to be done more than once. ReplaceSRimFileData(paths, AREA_EBO_BOX, LABEL_EBO_PRISON, Properties.Resources.g_brakatan003); }
/// <summary> /// Write special files to the override folder - save data, dream fix, galaxy map unlock, etc. /// </summary> /// <param name="paths">KPaths object for this game.</param> private static void WriteOverrideFiles(KPaths paths) { string moduleSavePath = Path.Combine(paths.Override, TwoDA_MODULE_SAVE); ModuleExtras saveFileExtras = Properties.Settings.Default.ModuleExtrasValue & (ModuleExtras.SaveAllModules | ModuleExtras.SaveMiniGames | ModuleExtras.NoSaveDelete); // Save Data File switch ((int)saveFileExtras) { default: // 0b000 - Milestone Delete (Default) // Do nothing. break; case (int)(ModuleExtras.NoSaveDelete): // 0b001 - No Milestone Delete File.WriteAllBytes(moduleSavePath, Properties.Resources.NODELETE_modulesave); break; case (int)(ModuleExtras.SaveMiniGames): // 0b010 - Save Minigames | Milestone Delete File.WriteAllBytes(moduleSavePath, Properties.Resources.MGINCLUDED_modulesave); break; case (int)(ModuleExtras.NoSaveDelete | ModuleExtras.SaveMiniGames): // 0b011 - Save Minigames | No Milestone Delete File.WriteAllBytes(moduleSavePath, Properties.Resources.NODELETE_MGINCLUDED_modulesave); break; case (int)(ModuleExtras.SaveAllModules): case (int)(ModuleExtras.SaveMiniGames | ModuleExtras.SaveAllModules): // Treat both the same. // 0b100 - Save All Modules | Milestone Delete // 0b110 - Save All Modules | Save Minigames | Milestone Delete File.WriteAllBytes(moduleSavePath, Properties.Resources.ALLINCLUDED_modulesave); break; case (int)(ModuleExtras.NoSaveDelete | ModuleExtras.SaveAllModules): case (int)(ModuleExtras.NoSaveDelete | ModuleExtras.SaveMiniGames | ModuleExtras.SaveAllModules): // Treat both the same. // 0b101 - Save All Modules | No Milestone Delete // 0b111 - Save All Modules | Save Minigames | No Milestone Delete File.WriteAllBytes(moduleSavePath, Properties.Resources.NODELETE_ALLINCLUDED_modulesave); break; } // Fix Dream File if (Properties.Settings.Default.ModuleExtrasValue.HasFlag(ModuleExtras.FixDream)) { File.WriteAllBytes(Path.Combine(paths.Override, FIXED_DREAM_OVERRIDE), Properties.Resources.k_ren_visionland); } // Unlock Galaxy Map File if (Properties.Settings.Default.ModuleExtrasValue.HasFlag(ModuleExtras.UnlockGalaxyMap)) { File.WriteAllBytes(Path.Combine(paths.Override, UNLOCK_MAP_OVERRIDE), Properties.Resources.k_pebn_galaxy); } }
public static void text_rando(KPaths paths) { var settings = Properties.Settings.Default; if (settings.TextSettingsValue.HasFlag(TextSettings.RandoFullTLK)) //Shuffle TLK first, so sound matching still works { shuffle_TLK(paths, settings.TextSettingsValue.HasFlag(TextSettings.MatchSimLengthStrings)); } if (settings.TextSettingsValue.HasFlag(TextSettings.RandoDialogEntries) || settings.TextSettingsValue.HasFlag(TextSettings.RandoDialogReplies)) { shuffle_dialogue(paths, settings.TextSettingsValue.HasFlag(TextSettings.RandoDialogEntries), settings.TextSettingsValue.HasFlag(TextSettings.RandoDialogReplies), settings.TextSettingsValue.HasFlag(TextSettings.MatchEntrySoundsWText)); } }
public static void Twoda_rando(KPaths paths) { BIF b = new BIF(Path.Combine(paths.data, "2da.bif")); KEY k = new KEY(paths.chitin_backup); b.AttachKey(k, "data\\2da.bif"); var filesInOverride = paths.FilesInOverride.ToList(); foreach (BIF.VariableResourceEntry VRE in b.VariableResourceTable.Where(x => Globals.Selected2DAs.Keys.Contains(x.ResRef))) { // Check to see if this table is already in the override directory. TwoDA t; if (filesInOverride.Any(fi => fi.Name == $"{VRE.ResRef}.2da")) { // Modify the existing table. t = new TwoDA(File.ReadAllBytes(filesInOverride.First(fi => fi.Name == $"{VRE.ResRef}.2da").FullName), VRE.ResRef); } else { // Fetch the table from the 2DA BIF file. t = new TwoDA(VRE.EntryData, VRE.ResRef); } if (!LookupTable.ContainsKey(VRE.ResRef)) { // Add 2DA to the table. LookupTable.Add(VRE.ResRef, new Dictionary <string, List <Tuple <string, string> > >()); } foreach (string col in Globals.Selected2DAs[VRE.ResRef]) { if (!LookupTable[VRE.ResRef].ContainsKey(col)) { // Add column to the table. LookupTable[VRE.ResRef].Add(col, new List <Tuple <string, string> >()); } var old = t.Data[col].ToList(); // Save list of old data. Randomize.FisherYatesShuffle(t.Data[col]); // Randomize 2DA column data. for (int i = 0; i < old.Count; i++) { // Add old and new data to the table. LookupTable[VRE.ResRef][col].Add(new Tuple <string, string>(old[i], t.Data[col][i])); } } t.WriteToDirectory(paths.Override); // Write new 2DA data to file. } }
//Randomize TLK static void shuffle_TLK(KPaths paths, bool LengthMatching) { TLK t = new TLK(paths.dialog); if (LengthMatching) { TLK t_ordered = new TLK(paths.dialog); t_ordered.String_Data_Table = t_ordered.String_Data_Table.OrderBy(x => x.StringText.Length).ToList(); for (int i = 0; i < t.String_Data_Table.Count; i++) { try { int index_offset = 0; while (index_offset == 0) { index_offset = Randomize.Rng.Next(-5, 5); } // Could get faster execution time by matching the strings by lenght instead of text, but then there would be bias towards strings earlier in each lenght bracket. var randomString = t_ordered.String_Data_Table[t_ordered.String_Data_Table.FindIndex(x => x.StringText == t.String_Data_Table[i].StringText) + index_offset]; TlkLookupTable.Add(i, new Tuple <string, string>(t.String_Data_Table[i].StringText, randomString.StringText)); t.String_Data_Table[i] = randomString; } catch (Exception ex) { if (ex is IndexOutOfRangeException || ex is ArgumentOutOfRangeException) { continue; //ignoring extreme cases } else { throw; } } } } else { TLK t_orig = new TLK(paths.dialog); Randomize.FisherYatesShuffle(t.String_Data_Table); for (int i = 0; i < t.String_Data_Table.Count; i++) { TlkLookupTable.Add(i, new Tuple <string, string>(t_orig.String_Data_Table[i].StringText, t.String_Data_Table[i].StringText)); } } t.WriteToFile(paths.dialog); }
/// <summary> /// Copy backup module files to the modules directory based on the current shuffle. /// </summary> /// <param name="paths">KPaths object for this game.</param> private static void WriteFilesToModulesDirectory(KPaths paths) { // Copy shuffled modules into the base directory. foreach (var name in LookupTable) { File.Copy($"{paths.modules_backup}{name.Key}.rim", $"{paths.modules}{name.Value}.rim", true); File.Copy($"{paths.modules_backup}{name.Key}_s.rim", $"{paths.modules}{name.Value}_s.rim", true); File.Copy($"{paths.lips_backup}{name.Key}_loc.mod", $"{paths.lips}{name.Value}_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); } }
public static void Twoda_rando(KPaths paths) { BIF b = new BIF(Path.Combine(paths.data, "2da.bif")); KEY k = new KEY(paths.chitin_backup); b.AttachKey(k, "data\\2da.bif"); foreach (BIF.VariableResourceEntry VRE in b.VariableResourceTable.Where(x => Globals.Selected2DAs.Keys.Contains(x.ResRef))) { TwoDA t = new TwoDA(VRE.EntryData, VRE.ResRef); foreach (string col in Globals.Selected2DAs[VRE.ResRef]) { Randomize.FisherYatesShuffle(t.Data[col]); } t.WriteToDirectory(paths.Override); } }
/// <summary> /// Replace file data 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 rFile to update.</param> /// <param name="rawData">File data to store in the rFile.</param> private static void ReplaceSRimFileData(KPaths paths, string area, string label, byte[] rawData) { // Find the files associated with this area. var area_files = paths.FilesInModules.Where(fi => fi.Name.Contains(LookupTable[area])); foreach (FileInfo file in area_files) { // Skip any files that don't end in "s.rim". if (file.Name[file.Name.Length - 5] != 's') { continue; } // Check the RIM's File_Table for any rFiles with the given label. RIM rim = new RIM(file.FullName); var rFiles = rim.File_Table.Where(x => x.Label == label); foreach (RIM.rFile rFile in rFiles) { rFile.File_Data = rawData; } rim.WriteToFile(file.FullName); } }
public static void CreateSpoilerLog(XLWorkbook workbook) { if (LookupTable.Count == 0) { return; } var ws = workbook.Worksheets.Add("Item"); var paths = new KPaths(Properties.Settings.Default.Kotor1Path); KEY k = new KEY(paths.chitin_backup); BIF b = new BIF(Path.Combine(paths.data, "templates.bif")); b.AttachKey(k, "data\\templates.bif"); var items = b.VariableResourceTable.Where(x => x.ResourceType == ResourceType.UTI); TLK t = new TLK(File.Exists(paths.dialog_backup) ? paths.dialog_backup : paths.dialog); int i = 1; ws.Cell(i, 1).Value = "Seed"; ws.Cell(i, 2).Value = Properties.Settings.Default.Seed; ws.Cell(i, 1).Style.Font.Bold = true; i++; Version version = typeof(StartForm).Assembly.GetName().Version; ws.Cell(i, 1).Value = "Version"; ws.Cell(i, 1).Style.Font.Bold = true; ws.Cell(i, 2).Value = $"v{version.Major}.{version.Minor}.{version.Build}"; ws.Cell(i, 2).Style.Alignment.Horizontal = XLAlignmentHorizontalValues.Right; i += 2; // Skip a row. // Item Randomization Settings ws.Cell(i, 1).Value = "Item Type"; ws.Cell(i, 2).Value = "Rando Level"; ws.Cell(i, 1).Style.Border.BottomBorder = XLBorderStyleValues.Thin; ws.Cell(i, 2).Style.Border.BottomBorder = XLBorderStyleValues.Thin; ws.Cell(i, 1).Style.Font.Bold = true; ws.Cell(i, 2).Style.Font.Bold = true; i++; var settings = new List <Tuple <string, string> >() { new Tuple <string, string>("Armbands", Properties.Settings.Default.RandomizeArmbands.ToString()), new Tuple <string, string>("Armor", Properties.Settings.Default.RandomizeArmor.ToString()), new Tuple <string, string>("Belts", Properties.Settings.Default.RandomizeBelts.ToString()), new Tuple <string, string>("Blasters", Properties.Settings.Default.RandomizeBlasters.ToString()), new Tuple <string, string>("Creature Hides", Properties.Settings.Default.RandomizeHides.ToString()), new Tuple <string, string>("Creature Weapons", Properties.Settings.Default.RandomizeCreature.ToString()), new Tuple <string, string>("Droid Equipment", Properties.Settings.Default.RandomizeDroid.ToString()), new Tuple <string, string>("Gauntlets", Properties.Settings.Default.RandomizeGloves.ToString()), new Tuple <string, string>("Grenades", Properties.Settings.Default.RandomizeGrenades.ToString()), new Tuple <string, string>("Implants", Properties.Settings.Default.RandomizeImplants.ToString()), new Tuple <string, string>("Lightsabers", Properties.Settings.Default.RandomizeLightsabers.ToString()), new Tuple <string, string>("Masks", Properties.Settings.Default.RandomizeMask.ToString()), new Tuple <string, string>("Melee Weapons", Properties.Settings.Default.RandomizeMelee.ToString()), new Tuple <string, string>("Mines", Properties.Settings.Default.RandomizeMines.ToString()), new Tuple <string, string>("Pazaak Cards", Properties.Settings.Default.RandomizePaz.ToString()), new Tuple <string, string>("Stims/Medpacs", Properties.Settings.Default.RandomizeStims.ToString()), new Tuple <string, string>("Upgrades/Crystals", Properties.Settings.Default.RandomizeUpgrade.ToString()), new Tuple <string, string>("Various", Properties.Settings.Default.RandomizeVarious.ToString()), }; foreach (var setting in settings) { ws.Cell(i, 1).Value = setting.Item1; ws.Cell(i, 2).Value = setting.Item2; ws.Cell(i, 1).Style.Font.Italic = true; i++; } i++; // Skip a row. // Omitted Items int iMax = i; i = 3; // Restart at the top of the settings list. ws.Cell(i, 4).Value = "Omitted Items"; ws.Cell(i, 4).Style.Alignment.Horizontal = XLAlignmentHorizontalValues.Center; ws.Cell(i, 4).Style.Font.Bold = true; ws.Range(i, 4, i, 5).Merge(); i++; ws.Cell(i, 4).Value = "ID"; ws.Cell(i, 5).Value = "Label"; ws.Cell(i, 4).Style.Border.BottomBorder = XLBorderStyleValues.Thin; ws.Cell(i, 5).Style.Border.BottomBorder = XLBorderStyleValues.Thin; ws.Cell(i, 4).Style.Font.Italic = true; ws.Cell(i, 5).Style.Font.Italic = true; i++; var sortedList = Globals.OmitItems.ToList(); sortedList.Sort(); foreach (var item in sortedList) { ws.Cell(i, 4).Value = item; var origItemName = ""; var origItemVre = items.FirstOrDefault(x => x.ResRef == item); if (origItemVre != null) { GFF origItem = new GFF(origItemVre.EntryData); if (origItem.Top_Level.Fields.FirstOrDefault(x => x.Label == "LocalizedName") is GFF.CExoLocString field) { origItemName = t.String_Data_Table[field.StringRef].StringText; } } ws.Cell(i, 5).Value = origItemName; i++; } // Handle variable length of omitted items list. if (iMax > i) { i = iMax; // Return to the bottom of the settings list. } else { i++; // Skip a row. } i++; // Skip an additional 2 rows. // Randomized Items ws.Cell(i, 1).Value = "Has Changed"; ws.Cell(i - 1, 2).Value = "Original (New Item)"; ws.Cell(i, 2).Value = "ID"; ws.Cell(i, 3).Value = "Label"; ws.Cell(i - 1, 4).Value = "Randomized (Old Item)"; ws.Cell(i, 4).Value = "ID"; ws.Cell(i, 5).Value = "Label"; ws.Cell(i, 1).Style.Alignment.Horizontal = XLAlignmentHorizontalValues.Center; ws.Cell(i - 1, 2).Style.Alignment.Horizontal = XLAlignmentHorizontalValues.Center; ws.Cell(i - 1, 4).Style.Alignment.Horizontal = XLAlignmentHorizontalValues.Center; ws.Cell(i, 1).Style.Border.BottomBorder = XLBorderStyleValues.Thin; ws.Cell(i, 2).Style.Border.BottomBorder = XLBorderStyleValues.Thin; ws.Cell(i, 3).Style.Border.BottomBorder = XLBorderStyleValues.Thin; ws.Cell(i, 4).Style.Border.BottomBorder = XLBorderStyleValues.Thin; ws.Cell(i, 5).Style.Border.BottomBorder = XLBorderStyleValues.Thin; ws.Cell(i - 1, 2).Style.Border.LeftBorder = XLBorderStyleValues.Thin; ws.Cell(i, 2).Style.Border.LeftBorder = XLBorderStyleValues.Thin; ws.Cell(i - 1, 4).Style.Border.LeftBorder = XLBorderStyleValues.Thin; ws.Cell(i, 4).Style.Border.LeftBorder = XLBorderStyleValues.Thin; ws.Cell(i - 1, 6).Style.Border.LeftBorder = XLBorderStyleValues.Thin; ws.Cell(i, 6).Style.Border.LeftBorder = XLBorderStyleValues.Thin; ws.Cell(i, 1).Style.Font.Bold = true; ws.Cell(i - 1, 2).Style.Font.Bold = true; ws.Cell(i, 2).Style.Font.Italic = true; ws.Cell(i, 3).Style.Font.Italic = true; ws.Cell(i - 1, 4).Style.Font.Bold = true; ws.Cell(i, 4).Style.Font.Italic = true; ws.Cell(i, 5).Style.Font.Italic = true; ws.Range(i - 1, 2, i - 1, 3).Merge(); ws.Range(i - 1, 4, i - 1, 5).Merge(); i++; var sortedLookup = LookupTable.OrderBy(tpl => tpl.Item1); foreach (var tpl in sortedLookup) { string origItemName = ""; string randItemName = ""; var omitted = Globals.OmitItems.Any(x => x == tpl.Item1); var changed = tpl.Item1 != tpl.Item2; // Has the shuffle changed this item? var origItemVre = items.FirstOrDefault(x => x.ResRef == tpl.Item1); if (origItemVre != null) { GFF origItem = new GFF(origItemVre.EntryData); if (origItem.Top_Level.Fields.FirstOrDefault(x => x.Label == "LocalizedName") is GFF.CExoLocString field) { origItemName = t.String_Data_Table[field.StringRef].StringText; } } if (changed) { var randItemVre = items.FirstOrDefault(x => x.ResRef == tpl.Item2); if (randItemVre != null) { GFF randItem = new GFF(randItemVre.EntryData); if (randItem.Top_Level.Fields.FirstOrDefault(x => x.Label == "LocalizedName") is GFF.CExoLocString field) { randItemName = t.String_Data_Table[field.StringRef].StringText; } } } else { randItemName = origItemName; } ws.Cell(i, 1).Value = omitted ? "OMITTED" : changed.ToString(); ws.Cell(i, 2).Style.Border.LeftBorder = XLBorderStyleValues.Thin; ws.Cell(i, 2).Value = tpl.Item1; ws.Cell(i, 3).Value = origItemName; ws.Cell(i, 4).Style.Border.LeftBorder = XLBorderStyleValues.Thin; ws.Cell(i, 4).Value = tpl.Item2; ws.Cell(i, 5).Value = randItemName; ws.Cell(i, 6).Style.Border.LeftBorder = XLBorderStyleValues.Thin; if (omitted) { // Center "OMITTED" text. ws.Cell(i, 1).Style.Alignment.Horizontal = XLAlignmentHorizontalValues.Center; } else { // Set color of "Has Changed" column. Booleans are automatically centered. if (changed) { ws.Cell(i, 1).Style.Font.FontColor = XLColor.Green; } else { ws.Cell(i, 1).Style.Font.FontColor = XLColor.Red; } } i++; } // Resize Columns ws.Column(1).AdjustToContents(); ws.Column(2).AdjustToContents(); ws.Column(3).AdjustToContents(); ws.Column(4).AdjustToContents(); ws.Column(5).AdjustToContents(); }
public static void sound_rando(KPaths paths) { var musicFiles = paths.FilesInMusicBackup; var soundFiles = paths.FilesInSoundsBackup; // Get file collections List <FileInfo> maxMusic = new List <FileInfo>(); List <FileInfo> maxSound = new List <FileInfo>(); // Area Music List <FileInfo> areaMusic = new List <FileInfo>(); foreach (var prefix in PrefixListAreaMusic) { areaMusic.AddRange(musicFiles.Where(f => f.Name.StartsWith(prefix, StringComparison.OrdinalIgnoreCase))); } switch ((RandomizationLevel)Properties.Settings.Default.RandomizeAreaMusic) { case RandomizationLevel.Max: maxMusic.AddRange(areaMusic); break; case RandomizationLevel.Type: Randomize.RandomizeFiles(areaMusic, paths.music); break; case RandomizationLevel.Subtype: case RandomizationLevel.None: default: break; } // Ambient Noise List <FileInfo> ambientNoiseMusic = new List <FileInfo>(); foreach (var prefix in PrefixListNoise) { ambientNoiseMusic.AddRange(musicFiles.Where(f => f.Name.StartsWith(prefix, StringComparison.OrdinalIgnoreCase))); } List <FileInfo> ambientNoiseSound = new List <FileInfo>(); foreach (var prefix in PrefixListNoise) { ambientNoiseSound.AddRange(soundFiles.Where(f => f.Name.StartsWith(prefix, StringComparison.OrdinalIgnoreCase))); } switch ((RandomizationLevel)Properties.Settings.Default.RandomizeAmbientNoise) { case RandomizationLevel.Max: maxMusic.AddRange(ambientNoiseMusic); maxSound.AddRange(ambientNoiseSound); break; case RandomizationLevel.Type: Randomize.RandomizeFiles(ambientNoiseMusic, paths.music); Randomize.RandomizeFiles(ambientNoiseSound, paths.sounds); break; case RandomizationLevel.Subtype: case RandomizationLevel.None: default: break; } // Battle Music List <FileInfo> battleMusic = new List <FileInfo>(musicFiles.Where(f => RegexBattleMusic.IsMatch(f.Name))); List <FileInfo> battleMusicEnd = new List <FileInfo>(soundFiles.Where(f => RegexBattleMusic.IsMatch(f.Name))); switch ((RandomizationLevel)Properties.Settings.Default.RandomizeBattleMusic) { case RandomizationLevel.Max: maxMusic.AddRange(battleMusic); maxSound.AddRange(battleMusicEnd); break; case RandomizationLevel.Type: Randomize.RandomizeFiles(battleMusic, paths.music); Randomize.RandomizeFiles(battleMusicEnd, paths.sounds); break; case RandomizationLevel.Subtype: case RandomizationLevel.None: default: break; } // Cutscene Noise List <FileInfo> cutsceneNoise = new List <FileInfo>(musicFiles.Where(f => RegexCutscene.IsMatch(f.Name))); cutsceneNoise.RemoveAll(f => f.Name.StartsWith("57.")); // Remove specific exception switch ((RandomizationLevel)Properties.Settings.Default.RandomizeCutsceneNoise) { case RandomizationLevel.Max: maxMusic.AddRange(cutsceneNoise); break; case RandomizationLevel.Type: Randomize.RandomizeFiles(cutsceneNoise, paths.music); break; case RandomizationLevel.Subtype: case RandomizationLevel.None: default: break; } // Check if NPC and Party Sounds are combined List <FileInfo> npcSounds = new List <FileInfo>(soundFiles.Where(f => RegexNPCSound.IsMatch(f.Name))); List <FileInfo> partySounds = new List <FileInfo>(soundFiles.Where(f => RegexPartySound.IsMatch(f.Name))); //if (MixNpcAndPartySounds) // Functionality Disabled //{ // npcSounds.AddRange(partySounds); //} //else { // Party Sounds (if not mixing) switch ((RandomizationLevel)Properties.Settings.Default.RandomizePartySounds) { case RandomizationLevel.Max: maxSound.AddRange(partySounds); break; case RandomizationLevel.Type: Randomize.RandomizeFiles(partySounds, paths.sounds); break; case RandomizationLevel.Subtype: RandomizeSoundActions(partySounds, paths.sounds); break; case RandomizationLevel.None: default: break; } } //// NPC Sounds (or both if mixing) // Functionality Disabled //switch (RandomizeNpcSounds) //{ // case RandomizationLevel.Max: // maxSound.AddRange(npcSounds); // break; // case RandomizationLevel.Type: // Randomize.RandomizeFiles(npcSounds, SoundsPath); // break; // case RandomizationLevel.Subtype: // RandomizeSoundActions(npcSounds, SoundsPath); // break; // case RandomizationLevel.None: // default: // break; //} // Max Randomizations if (maxMusic.Any()) { Randomize.RandomizeFiles(maxMusic, paths.music); } if (maxSound.Any()) { Randomize.RandomizeFiles(maxSound, paths.sounds); } }
//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); } }
public static void item_rando(KPaths paths) { // Prepare lists for new randomization. Max_Rando.Clear(); Type_Lists.Clear(); LookupTable.Clear(); // Load KEY file. KEY k = new KEY(paths.chitin); // Handle categories HandleCategory(k, ArmbandsRegs, Properties.Settings.Default.RandomizeArmbands); HandleCategory(k, ArmorRegs, Properties.Settings.Default.RandomizeArmor); HandleCategory(k, BeltsRegs, Properties.Settings.Default.RandomizeBelts); HandleCategory(k, BlastersRegs, Properties.Settings.Default.RandomizeBlasters); HandleCategory(k, HidesRegs, Properties.Settings.Default.RandomizeHides); HandleCategory(k, CreatureRegs, Properties.Settings.Default.RandomizeCreature); HandleCategory(k, DroidRegs, Properties.Settings.Default.RandomizeDroid); HandleCategory(k, GlovesRegs, Properties.Settings.Default.RandomizeGloves); HandleCategory(k, GrenadesRegs, Properties.Settings.Default.RandomizeGrenades); HandleCategory(k, ImplantsRegs, Properties.Settings.Default.RandomizeImplants); HandleCategory(k, LightsabersRegs, Properties.Settings.Default.RandomizeLightsabers); HandleCategory(k, MaskRegs, Properties.Settings.Default.RandomizeMask); HandleCategory(k, MeleeRegs, Properties.Settings.Default.RandomizeMelee); HandleCategory(k, MinesRegs, Properties.Settings.Default.RandomizeMines); HandleCategory(k, PazRegs, Properties.Settings.Default.RandomizePaz); HandleCategory(k, StimsRegs, Properties.Settings.Default.RandomizeStims); HandleCategory(k, UpgradeRegs, Properties.Settings.Default.RandomizeUpgrade); // Handle Various switch (Properties.Settings.Default.RandomizeVarious) { default: case RandomizationLevel.None: break; case RandomizationLevel.Type: List <string> type = new List <string>(k.KeyTable.Where(x => Matches_None(x.ResRef) && !Is_Forbidden(x.ResRef) && x.ResourceType == (short)ResourceType.UTI).Select(x => x.ResRef)); Type_Lists.Add(type); break; case RandomizationLevel.Max: Max_Rando.AddRange(k.KeyTable.Where(x => Matches_None(x.ResRef) && !Is_Forbidden(x.ResRef) && x.ResourceType == (short)ResourceType.UTI).Select(x => x.ResRef)); break; } // Omitted Items foreach (var item in Globals.OmitItems) { LookupTable.Add(new Tuple <string, string>(item, item)); } // Max Rando List <string> Max_Rando_Iterator = new List <string>(Max_Rando); Randomize.FisherYatesShuffle(Max_Rando); int j = 0; foreach (KEY.KeyEntry ke in k.KeyTable.Where(x => Max_Rando_Iterator.Contains(x.ResRef))) { LookupTable.Add(new Tuple <string, string>(ke.ResRef, Max_Rando[j])); ke.ResRef = Max_Rando[j]; j++; } // Type Rando foreach (List <string> li in Type_Lists) { List <string> type_copy = new List <string>(li); Randomize.FisherYatesShuffle(type_copy); j = 0; foreach (KEY.KeyEntry ke in k.KeyTable.Where(x => li.Contains(x.ResRef))) { LookupTable.Add(new Tuple <string, string>(ke.ResRef, type_copy[j])); ke.ResRef = type_copy[j]; j++; } } k.WriteToFile(paths.chitin); }
public static void sound_rando(KPaths paths) { // Prepare lists for new randomization. MusicLookupTable.Clear(); SoundLookupTable.Clear(); // Get file collections List <FileInfo> maxMusic = new List <FileInfo>(); List <FileInfo> maxSound = new List <FileInfo>(); List <FileInfo> musicFiles = new List <FileInfo>(); List <FileInfo> soundFiles = new List <FileInfo>(); if (Directory.Exists(paths.music_backup)) { musicFiles = paths.FilesInMusicBackup.ToList(); } if (Directory.Exists(paths.sounds_backup)) { soundFiles = paths.FilesInSoundsBackup.ToList(); } // Area Music List <FileInfo> areaMusic = new List <FileInfo>(); foreach (var prefix in PrefixListAreaMusic) { areaMusic.AddRange(musicFiles.Where(f => f.Name.StartsWith(prefix, StringComparison.OrdinalIgnoreCase))); } if (Properties.Settings.Default.RemoveDmcaMusic) { areaMusic.RemoveAll(f => DmcaAreaMusic.Contains(f.Name)); // Remove DMCA music from the area list. } switch (Properties.Settings.Default.RandomizeAreaMusic) { case RandomizationLevel.Max: maxMusic.AddRange(areaMusic); break; case RandomizationLevel.Type: var randList = Randomize.RandomizeFiles(areaMusic, paths.music); AddToMusicLookup(areaMusic, randList); break; case RandomizationLevel.Subtype: case RandomizationLevel.None: default: break; } // Ambient Noise List <FileInfo> ambientNoiseMusic = new List <FileInfo>(); foreach (var prefix in PrefixListNoise) { ambientNoiseMusic.AddRange(musicFiles.Where(f => f.Name.StartsWith(prefix, StringComparison.OrdinalIgnoreCase))); } List <FileInfo> ambientNoiseSound = new List <FileInfo>(); foreach (var prefix in PrefixListNoise) { ambientNoiseSound.AddRange(soundFiles.Where(f => f.Name.StartsWith(prefix, StringComparison.OrdinalIgnoreCase))); } switch (Properties.Settings.Default.RandomizeAmbientNoise) { case RandomizationLevel.Max: maxMusic.AddRange(ambientNoiseMusic); maxSound.AddRange(ambientNoiseSound); break; case RandomizationLevel.Type: var randList = Randomize.RandomizeFiles(ambientNoiseMusic, paths.music); AddToMusicLookup(ambientNoiseMusic, randList); randList = Randomize.RandomizeFiles(ambientNoiseSound, paths.sounds); AddToSoundLookup(ambientNoiseSound, randList); break; case RandomizationLevel.Subtype: case RandomizationLevel.None: default: break; } // Battle Music List <FileInfo> battleMusic = new List <FileInfo>(musicFiles.Where(f => RegexBattleMusic.IsMatch(f.Name))); List <FileInfo> battleMusicEnd = new List <FileInfo>(soundFiles.Where(f => RegexBattleMusic.IsMatch(f.Name))); switch (Properties.Settings.Default.RandomizeBattleMusic) { case RandomizationLevel.Max: maxMusic.AddRange(battleMusic); maxSound.AddRange(battleMusicEnd); break; case RandomizationLevel.Type: var randList = Randomize.RandomizeFiles(battleMusic, paths.music); AddToMusicLookup(battleMusic, randList); randList = Randomize.RandomizeFiles(battleMusicEnd, paths.sounds); AddToSoundLookup(battleMusicEnd, randList); break; case RandomizationLevel.Subtype: case RandomizationLevel.None: default: break; } // Cutscene Noise List <FileInfo> cutsceneNoise = new List <FileInfo>(musicFiles.Where(f => RegexCutscene.IsMatch(f.Name))); cutsceneNoise.RemoveAll(f => f.Name.StartsWith("57.")); // Remove specific exception switch (Properties.Settings.Default.RandomizeCutsceneNoise) { case RandomizationLevel.Max: maxMusic.AddRange(cutsceneNoise); break; case RandomizationLevel.Type: var randList = Randomize.RandomizeFiles(cutsceneNoise, paths.music); AddToMusicLookup(cutsceneNoise, randList); break; case RandomizationLevel.Subtype: case RandomizationLevel.None: default: break; } // Check if NPC and Party Sounds are combined List <FileInfo> npcSounds = new List <FileInfo>(soundFiles.Where(f => RegexNPCSound.IsMatch(f.Name))); List <FileInfo> partySounds = new List <FileInfo>(soundFiles.Where(f => RegexPartySound.IsMatch(f.Name))); //if (MixNpcAndPartySounds) // Functionality Disabled //{ // npcSounds.AddRange(partySounds); //} //else { // Party Sounds (if not mixing) switch (Properties.Settings.Default.RandomizePartySounds) { case RandomizationLevel.Max: maxSound.AddRange(partySounds); break; case RandomizationLevel.Type: var randList = Randomize.RandomizeFiles(partySounds, paths.sounds); AddToSoundLookup(partySounds, randList); break; case RandomizationLevel.Subtype: RandomizeSoundActions(partySounds, paths.sounds); break; case RandomizationLevel.None: default: break; } } //// NPC Sounds (or both if mixing) // Functionality Disabled //switch (RandomizeNpcSounds) //{ // case RandomizationLevel.Max: // maxSound.AddRange(npcSounds); // break; // case RandomizationLevel.Type: // Randomize.RandomizeFiles(npcSounds, SoundsPath); // break; // case RandomizationLevel.Subtype: // RandomizeSoundActions(npcSounds, SoundsPath); // break; // case RandomizationLevel.None: // default: // break; //} // Max Randomizations if (maxMusic.Any()) { var randList = Randomize.RandomizeFiles(maxMusic, paths.music); AddToMusicLookup(maxMusic, randList); } if (maxSound.Any()) { var randList = Randomize.RandomizeFiles(maxSound, paths.sounds); AddToSoundLookup(maxSound, randList); } // Overwrite DMCA music with alternatives if (Properties.Settings.Default.RemoveDmcaMusic) { var orig = new List <FileInfo>(); var rand = new List <FileInfo>(); foreach (var fi in musicFiles.Where(f => DmcaAreaMusic.Contains(f.Name))) { var replacement = areaMusic[Randomize.Rng.Next(areaMusic.Count)]; File.Copy(replacement.FullName, Path.Combine(paths.music, fi.Name), true); orig.Add(fi); rand.Add(replacement); } AddToMusicLookup(orig, rand); } }
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); } }
// 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 other_rando(KPaths paths) { // NameGen if (Properties.Settings.Default.RandomizeNameGen) { List <string> male_names = Properties.Settings.Default.FirstnamesM.Cast <string>().Select(x => x.Trim()).Where(x => x.Length > 2).ToList(); LTR ltr_male_names = new LTR(male_names); List <string> female_names = Properties.Settings.Default.FirstnamesF.Cast <string>().Select(x => x.Trim()).Where(x => x.Length > 2).ToList(); LTR ltr_female_names = new LTR(female_names); List <string> last_names = Properties.Settings.Default.Lastnames.Cast <string>().Select(x => x.Trim()).Where(x => x.Length > 2).ToList(); LTR ltr_last_names = new LTR(last_names); if (male_names.Any()) { ltr_male_names.WriteToFile(paths.Override + "humanm.ltr"); } if (female_names.Any()) { ltr_female_names.WriteToFile(paths.Override + "humanf.ltr"); } if (last_names.Any()) { ltr_last_names.WriteToFile(paths.Override + "humanl.ltr"); } } // Polymorph // Random NPC Pazaak Decks if (Properties.Settings.Default.RandomizePazaakDecks) { string ops = "+-*"; BIF b = new BIF(paths.data + "\\2da.bif"); KEY k = new KEY(paths.chitin_backup); b.AttachKey(k, "data\\2da.bif"); var resource = b.VariableResourceTable.Where(x => x.ResRef == PAZAAKDECKS_RESREF).FirstOrDefault(); if (resource == null) { throw new ArgumentOutOfRangeException($"The ResRef \"{PAZAAKDECKS_RESREF}\" could not be found."); } TwoDA t = new TwoDA(resource.EntryData, PAZAAKDECKS_RESREF); foreach (string c in t.Columns) { if (c == DECKNAME_COLUMN) { continue; } // [+-*][1-6] // "" + ops[Randomize.Rng.Next(0, 3)] + Convert.ToString(Randomize.Rng.Next(1, 7)); t.Data[c][0] = $"{ops[Randomize.Rng.Next(0, 3)]}{Convert.ToString(Randomize.Rng.Next(1, 7))}"; t.Data[c][1] = $"{ops[Randomize.Rng.Next(0, 3)]}{Convert.ToString(Randomize.Rng.Next(1, 7))}"; t.Data[c][2] = $"{ops[Randomize.Rng.Next(0, 3)]}{Convert.ToString(Randomize.Rng.Next(1, 7))}"; t.Data[c][3] = $"{ops[Randomize.Rng.Next(0, 3)]}{Convert.ToString(Randomize.Rng.Next(1, 7))}"; } t.WriteToDirectory(paths.Override); } }
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); } }
public static void texture_rando(KPaths paths) { // Prepare lists for new randomization. MaxRando.Clear(); TypeLists.Clear(); // Load in texture pack. string pack_name; switch (Properties.Settings.Default.TexturePack) { default: case TexturePack.HighQuality: pack_name = "\\swpc_tex_tpa.erf"; break; case TexturePack.MedQuality: pack_name = "\\swpc_tex_tpb.erf"; break; case TexturePack.LowQuality: pack_name = "\\swpc_tex_tpc.erf"; break; } ERF e = new ERF(paths.TexturePacks + pack_name); foreach (var key in e.Key_List) { if (!NameLookup.ContainsKey(key.ResID)) { NameLookup.Add(key.ResID, key.ResRef); } } // Handle categories. HandleCategory(e, RegexCubeMaps, Properties.Settings.Default.TextureRandomizeCubeMaps); HandleCategory(e, RegexCreatures, Properties.Settings.Default.TextureRandomizeCreatures); HandleCategory(e, RegexEffects, Properties.Settings.Default.TextureRandomizeEffects); HandleCategory(e, RegexItems, Properties.Settings.Default.TextureRandomizeItems); HandleCategory(e, RegexPlanetary, Properties.Settings.Default.TextureRandomizePlanetary); HandleCategory(e, RegexNPC, Properties.Settings.Default.TextureRandomizeNPC); HandleCategory(e, RegexPlayHeads, Properties.Settings.Default.TextureRandomizePlayHeads); HandleCategory(e, RegexPlayBodies, Properties.Settings.Default.TextureRandomizePlayBodies); HandleCategory(e, RegexPlaceables, Properties.Settings.Default.TextureRandomizePlaceables); HandleCategory(e, RegexParty, Properties.Settings.Default.TextureRandomizeParty); HandleCategory(e, RegexStunt, Properties.Settings.Default.TextureRandomizeStunt); HandleCategory(e, RegexVehicles, Properties.Settings.Default.TextureRandomizeVehicles); HandleCategory(e, RegexWeapons, Properties.Settings.Default.TextureRandomizeWeapons); // Handle other. switch (Properties.Settings.Default.TextureRandomizeOther) { default: case RandomizationLevel.None: break; // Do nothing. case RandomizationLevel.Type: List <int> type = new List <int>(e.Key_List.Where(x => Matches_None(x.ResRef) && !Is_Forbidden(x.ResRef)).Select(x => x.ResID)); TypeLists.Add(type); break; case RandomizationLevel.Max: MaxRando.AddRange(e.Key_List.Where(x => Matches_None(x.ResRef) && !Is_Forbidden(x.ResRef)).Select(x => x.ResID)); break; } // Max Rando. List <int> Max_Rando_Iterator = new List <int>(MaxRando); Randomize.FisherYatesShuffle(MaxRando); int j = 0; foreach (ERF.Key k in e.Key_List.Where(x => Max_Rando_Iterator.Contains(x.ResID))) { LookupTable.Add(k.ResID, MaxRando[j]); k.ResID = MaxRando[j]; j++; } // Type Rando. foreach (List <int> li in TypeLists) { List <int> type_copy = new List <int>(li); Randomize.FisherYatesShuffle(type_copy); j = 0; foreach (ERF.Key k in e.Key_List.Where(x => li.Contains(x.ResID))) { LookupTable.Add(k.ResID, type_copy[j]); k.ResID = type_copy[j]; j++; } } e.WriteToFile(paths.TexturePacks + pack_name); }
public static void other_rando(KPaths paths) { // NameGen if (Properties.Settings.Default.RandomizeNameGen) { List <string> male_names = Properties.Settings.Default.FirstnamesM.Cast <string>().Select(x => x.Trim()).Where(x => x.Length > 2).ToList(); LTR ltr_male_names = new LTR(male_names); List <string> female_names = Properties.Settings.Default.FirstnamesF.Cast <string>().Select(x => x.Trim()).Where(x => x.Length > 2).ToList(); LTR ltr_female_names = new LTR(female_names); List <string> last_names = Properties.Settings.Default.Lastnames.Cast <string>().Select(x => x.Trim()).Where(x => x.Length > 2).ToList(); LTR ltr_last_names = new LTR(last_names); if (male_names.Any()) { NameGenLookup.Add(NAME_GEN_MALE, male_names); ltr_male_names.WriteToFile(paths.Override + "humanm.ltr"); } if (female_names.Any()) { NameGenLookup.Add(NAME_GEN_FEMALE, female_names); ltr_female_names.WriteToFile(paths.Override + "humanf.ltr"); } if (last_names.Any()) { NameGenLookup.Add(NAME_GEN_LAST, last_names); ltr_last_names.WriteToFile(paths.Override + "humanl.ltr"); } } // Polymorph if (Properties.Settings.Default.PolymorphMode) { BIF b = new BIF(paths.data + "templates.bif"); KEY k = new KEY(paths.chitin); b.AttachKey(k, "data\\templates.bif"); foreach (var res in b.VariableResourceTable.Where(x => x.ResourceType == ResourceType.UTI)) { GFF g = new GFF(res.EntryData); int item_basetype = (g.Top_Level.Fields.Where(x => x.Label == "BaseItem").FirstOrDefault() as GFF.INT).Value; //ignore items that can't be equipped in the chest slot if ((item_basetype < 35 || item_basetype > 43) && (item_basetype < 66 || item_basetype > 68) && item_basetype != 85 && item_basetype != 89) { continue; } ushort rando_appearance = 0; while (Globals.BROKEN_CHARS.Contains((int)rando_appearance) || Globals.LARGE_CHARS.Contains((int)rando_appearance)) { rando_appearance = (ushort)Randomize.Rng.Next(508); } //STRUCT that gives an item the "disguise" property GFF.STRUCT disguise_prop = new GFF.STRUCT("", 0, new List <GFF.FIELD>() { new GFF.BYTE("ChanceAppear", 100), new GFF.BYTE("CostTable", 0), new GFF.WORD("CostValue", 0), new GFF.BYTE("Param1", 255), new GFF.BYTE("Param1Value", 0), new GFF.WORD("PropertyName", 59), //Disguise property new GFF.WORD("Subtype", rando_appearance), //The random appearance value (same values used in model rando) } ); //Adds the disguise property to the UTI's property list (g.Top_Level.Fields.Where(x => x.Label == "PropertiesList").FirstOrDefault() as GFF.LIST).Structs.Add(disguise_prop); PolymorphLookupTable.Add(res.ResRef, rando_appearance); g.WriteToFile(paths.Override + res.ResRef + ".uti"); } } // Random NPC Pazaak Decks if (Properties.Settings.Default.RandomizePazaakDecks) { string ops = "+-*"; BIF b = new BIF(paths.data + "2da.bif"); KEY k = new KEY(paths.chitin_backup); b.AttachKey(k, "data\\2da.bif"); var resource = b.VariableResourceTable.Where(x => x.ResRef == PAZAAKDECKS_RESREF).FirstOrDefault(); if (resource == null) { throw new ArgumentOutOfRangeException($"The ResRef \"{PAZAAKDECKS_RESREF}\" could not be found."); } TwoDA t = new TwoDA(resource.EntryData, PAZAAKDECKS_RESREF); foreach (string c in t.Columns) { if (c == DECKNAME_COLUMN) { NpcPazaakLookupTable.Add(c, new List <Tuple <string, string> >() { new Tuple <string, string>(t.Data[c][0], t.Data[c][0]), new Tuple <string, string>(t.Data[c][1], t.Data[c][1]), new Tuple <string, string>(t.Data[c][2], t.Data[c][2]), new Tuple <string, string>(t.Data[c][3], t.Data[c][3]), }); continue; } NpcPazaakLookupTable.Add(c, new List <Tuple <string, string> >()); // [+-*][1-6] // "" + ops[Randomize.Rng.Next(0, 3)] + Convert.ToString(Randomize.Rng.Next(1, 7)); var card0 = $"{ops[Randomize.Rng.Next(0, 3)]}{Convert.ToString(Randomize.Rng.Next(1, 7))}"; var card1 = $"{ops[Randomize.Rng.Next(0, 3)]}{Convert.ToString(Randomize.Rng.Next(1, 7))}"; var card2 = $"{ops[Randomize.Rng.Next(0, 3)]}{Convert.ToString(Randomize.Rng.Next(1, 7))}"; var card3 = $"{ops[Randomize.Rng.Next(0, 3)]}{Convert.ToString(Randomize.Rng.Next(1, 7))}"; NpcPazaakLookupTable[c].Add(new Tuple <string, string>(t.Data[c][0], card0)); NpcPazaakLookupTable[c].Add(new Tuple <string, string>(t.Data[c][1], card1)); NpcPazaakLookupTable[c].Add(new Tuple <string, string>(t.Data[c][2], card2)); NpcPazaakLookupTable[c].Add(new Tuple <string, string>(t.Data[c][3], card3)); t.Data[c][0] = card0; t.Data[c][1] = card1; t.Data[c][2] = card2; t.Data[c][3] = card3; } t.WriteToDirectory(paths.Override); } // Party Rando if (Properties.Settings.Default.RandomizePartyMembers) { BIF b = new BIF(paths.data + "templates.bif"); KEY k = new KEY(paths.chitin_backup); b.AttachKey(k, "data\\templates.bif"); foreach (var ID in Party_IDs) { // Find a creature that isn't this party member var charsList = b.VariableResourceTable.Where(x => x.ResourceType == ResourceType.UTC).ToList(); var character = charsList.First(x => x.ResRef == ID.Item2); BIF.VariableResourceEntry resource; GFF g; do { int randoIndex = Randomize.Rng.Next(charsList.Count); resource = charsList[randoIndex]; if (resource.ResRef != character.ResRef && !InvalidPartyMembers.Contains(resource.ResRef)) { // Potentially valid party member. Check against broken and large characters. g = new GFF(resource.EntryData); var appearance = g.Top_Level.Fields.First(x => x.Label == ModelRando.LBL_APPEARANCE_TYPE) as GFF.WORD; if (!Globals.BROKEN_CHARS.Contains(appearance.Value) && !Globals.LARGE_CHARS.Contains(appearance.Value)) { break; // Character is valid. } } // Character is invalid, broken, or large. Skip it. Console.WriteLine($"Skipped invalid party member ({resource.ResRef}) when randomizing {ID.Item2}."); }while (true); PartyLookupTable.Add(ID.Item3, resource.ResRef); GFF gOld = new GFF(character.EntryData); ushort portraitId = (gOld.Top_Level.Fields.Where(x => x.Label == "PortraitId").FirstOrDefault() as GFF.WORD)?.Value ?? 0; // Turns creature file into a playable companion replacing the current party member. (g.Top_Level.Fields.Where(x => x.Label == "Conversation").FirstOrDefault() as GFF.ResRef).Reference = ID.Item1; (g.Top_Level.Fields.Where(x => x.Label == "Tag").FirstOrDefault() as GFF.CExoString).CEString = ID.Item3; (g.Top_Level.Fields.Where(x => x.Label == "TemplateResRef").FirstOrDefault() as GFF.ResRef).Reference = ID.Item2; (g.Top_Level.Fields.Where(x => x.Label == "NoPermDeath").FirstOrDefault() as GFF.BYTE).Value = 1; (g.Top_Level.Fields.Where(x => x.Label == "FactionID").FirstOrDefault() as GFF.WORD).Value = 2; // Give new companion the old companion's portrait. if (g.Top_Level.Fields.Where(x => x.Label == "PortraitId").FirstOrDefault() is GFF.WORD field) { field.Value = portraitId; } // Henchmen script suite. (g.Top_Level.Fields.Where(x => x.Label == "ScriptHeartbeat").FirstOrDefault() as GFF.ResRef).Reference = "k_hen_heartbt01"; (g.Top_Level.Fields.Where(x => x.Label == "ScriptOnNotice").FirstOrDefault() as GFF.ResRef).Reference = "k_hen_percept01"; (g.Top_Level.Fields.Where(x => x.Label == "ScriptSpellAt").FirstOrDefault() as GFF.ResRef).Reference = ""; (g.Top_Level.Fields.Where(x => x.Label == "ScriptAttacked").FirstOrDefault() as GFF.ResRef).Reference = "k_hen_attacked01"; (g.Top_Level.Fields.Where(x => x.Label == "ScriptDamaged").FirstOrDefault() as GFF.ResRef).Reference = "k_hen_damage01"; (g.Top_Level.Fields.Where(x => x.Label == "ScriptDisturbed").FirstOrDefault() as GFF.ResRef).Reference = ""; (g.Top_Level.Fields.Where(x => x.Label == "ScriptEndRound").FirstOrDefault() as GFF.ResRef).Reference = "k_hen_combend01"; (g.Top_Level.Fields.Where(x => x.Label == "ScriptEndDialogu").FirstOrDefault() as GFF.ResRef).Reference = ""; (g.Top_Level.Fields.Where(x => x.Label == "ScriptDialogue").FirstOrDefault() as GFF.ResRef).Reference = "k_hen_dialogue01"; (g.Top_Level.Fields.Where(x => x.Label == "ScriptSpawn").FirstOrDefault() as GFF.ResRef).Reference = "k_hen_spawn01"; (g.Top_Level.Fields.Where(x => x.Label == "ScriptRested").FirstOrDefault() as GFF.ResRef).Reference = ""; (g.Top_Level.Fields.Where(x => x.Label == "ScriptDeath").FirstOrDefault() as GFF.ResRef).Reference = ""; (g.Top_Level.Fields.Where(x => x.Label == "ScriptOnBlocked").FirstOrDefault() as GFF.ResRef).Reference = "k_hen_blocked01"; (g.Top_Level.Fields.Where(x => x.Label == "ScriptUserDefine").FirstOrDefault() as GFF.ResRef).Reference = ""; // Add a Dummy Feat to prevent the feats menu from crashing. (g.Top_Level.Fields.Where(x => x.Label == "FeatList").FirstOrDefault() as GFF.LIST).Structs.Add(new GFF.STRUCT("", 1, new List <GFF.FIELD>() { new GFF.WORD("Feat", 27) })); // If they are a Jedi class, add a Power to prevent powers menu from crashing. var charClassList = g.Top_Level.Fields.First(x => x.Label == "ClassList") as GFF.LIST; foreach (var classStruct in charClassList.Structs) { var charClassValue = (classStruct.Fields.First(x => x.Label == "Class") as GFF.INT).Value; if (charClassValue == 3 || // Jedi Guardian charClassValue == 4 || // Jedi Consular charClassValue == 5) // Jedi Sentinel { // Build a power to add to the list. GFF.STRUCT affectMind = new GFF.STRUCT("", 3, new List <GFF.FIELD>() { new GFF.WORD("Spell", 6), new GFF.BYTE("SpellMetaMagic", 0), new GFF.BYTE("SpellFlags", 1), }); if (!(classStruct.Fields.FirstOrDefault(x => x.Label == "KnownList0") is GFF.LIST knownList)) { // KnownList0 doesn't exist. Create it and add Affect Mind. classStruct.Fields.Add(new GFF.LIST("KnownList0", new List <GFF.STRUCT>() { affectMind })); } else if (knownList.Structs.Count == 0) { // KnownList0 exists but is empty. Add affect Mind. knownList.Structs.Add(affectMind); } break; } } g.WriteToFile(paths.Override + ID.Item2 + ".utc"); } }
/// <summary> /// Populates and shuffles the the modules flagged to be randomized. Returns true if override files should be added. /// </summary> /// <param name="paths">KPaths object for this game.</param> public static void Module_rando(KPaths paths) { // Reset digraph reachability settings. Digraph.ResetSettings(); LookupTable.Clear(); // Split the Bound modules into their respective lists. bool reachable = false; int iterations = 0; // Only shuffle if there is more than 1 module in the shuffle. if (Globals.BoundModules.Count(x => !x.Omitted) > 1) { if (Properties.Settings.Default.UseRandoRules || Properties.Settings.Default.VerifyReachability) { System.Diagnostics.Stopwatch sw = new System.Diagnostics.Stopwatch(); sw.Start(); while (!reachable && iterations < MAX_ITERATIONS) { iterations++; Console.WriteLine($"Iteration {iterations}:"); CreateLookupTableShuffle(); Digraph.SetRandomizationLookup(LookupTable); if (Properties.Settings.Default.UseRandoRules) { // Skip to the next iteration if the rules are violated. if (AreRulesViolated()) { continue; } } if (Properties.Settings.Default.VerifyReachability) { Digraph.CheckReachability(); reachable = Digraph.IsGoalReachable(); } else { reachable = true; } } if (Properties.Settings.Default.VerifyReachability) { if (reachable) { var message = $"Reachable solution found after {iterations} shuffles. Time elapsed: {sw.Elapsed}"; Console.WriteLine(message); } else { // Throw an exception if not reachable. var message = $"No reachable solution found over {iterations} shuffles. Time elapsed: {sw.Elapsed}"; Console.WriteLine(message); throw new TimeoutException(message); } } //digraph.WriteReachableToConsole(); Console.WriteLine(); } else { CreateLookupTableShuffle(); } } else { CreateLookupTableNoShuffle(); } WriteFilesToModulesDirectory(paths); // Write additional override files (and unlock galaxy map). WriteOverrideFiles(paths); // Fix warp coordinates. if (Properties.Settings.Default.ModuleExtrasValue.HasFlag(ModuleExtras.FixCoordinates)) { FixWarpCoordinates(paths); } // Fixed Rakata riddle Man in Mind Prison. if (Properties.Settings.Default.ModuleExtrasValue.HasFlag(ModuleExtras.FixMindPrison)) { FixMindPrison(paths); } // Unlock locked doors or elevators. UnlockDoors(paths); // Vulkar Spice Lab Transition if (Properties.Settings.Default.ModuleExtrasValue.HasFlag(ModuleExtras.VulkarSpiceLZ)) { var vulk_files = paths.FilesInModules.Where(fi => fi.Name.Contains(LookupTable[AREA_TAR_VULK_BASE])); foreach (FileInfo fi in vulk_files) { // Skip any files that end in "s.rim". if (fi.Name[fi.Name.Length - 5] == 's') { continue; } RIM r_vul = new RIM(fi.FullName); r_vul.File_Table.FirstOrDefault(x => x.Label == LABEL_TAR_VULK_GIT && x.TypeID == (int)ResourceType.GIT).File_Data = Properties.Resources.m10aa; r_vul.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); } }