private static void InitGetBottleList() { RomData.BottleList = new Dictionary <int, BottleCatchEntry>(); int f = RomUtils.GetFileIndexForWriting(BOTTLE_CATCH_TABLE); int baseaddr = BOTTLE_CATCH_TABLE - RomData.MMFileList[f].Addr; var fileData = RomData.MMFileList[f].Data; foreach (var getBottleItemIndex in ItemUtils.AllGetBottleItemIndices()) { int offset = getBottleItemIndex * 6 + baseaddr; RomData.BottleList[getBottleItemIndex] = new BottleCatchEntry { ItemGained = fileData[offset + 3], Index = fileData[offset + 4], Message = fileData[offset + 5] }; } }
private static void InitGetItemList() { RomData.GetItemList = new Dictionary <int, GetItemEntry>(); int f = RomUtils.GetFileIndexForWriting(GET_ITEM_TABLE); int baseaddr = GET_ITEM_TABLE - RomData.MMFileList[f].Addr; var fileData = RomData.MMFileList[f].Data; foreach (var getItemIndex in ItemUtils.AllGetItemIndices()) { int offset = (getItemIndex - 1) * 8 + baseaddr; RomData.GetItemList[getItemIndex] = new GetItemEntry { ItemGained = fileData[offset], Flag = fileData[offset + 1], Index = fileData[offset + 2], Type = fileData[offset + 3], Message = (short)((fileData[offset + 4] << 8) | fileData[offset + 5]), Object = (short)((fileData[offset + 6] << 8) | fileData[offset + 7]) }; } }
public static void WriteNewBottle(int location, int item) { System.Diagnostics.Debug.WriteLine($"Writing {Items.ITEM_NAMES[item]} --> {Items.ITEM_NAMES[location]}"); location = ItemUtils.SubtractItemOffset(location); item = ItemUtils.SubtractItemOffset(item); int f = RomUtils.GetFileIndexForWriting(BOTTLE_CATCH_TABLE); int baseaddr = BOTTLE_CATCH_TABLE - RomData.MMFileList[f].Addr; var fileData = RomData.MMFileList[f].Data; for (int i = 0; i < RomData.BottleIndices[location].Length; i++) { int offset = RomData.BottleIndices[location][i] * 6 + baseaddr; var newBottle = RomData.BottleList[item][0]; var data = new byte[] { newBottle.ItemGained, newBottle.Index, newBottle.Message, }; ReadWriteUtils.Arr_Insert(data, 0, data.Length, fileData, offset + 3); } }
public static List <MessageEntry> MakeGossipQuotes(RandomizedResult randomizedResult) { if (randomizedResult.Settings.GossipHintStyle == GossipHintStyle.Default) { return(new List <MessageEntry>()); } var randomizedItems = new List <ItemObject>(); var competitiveHints = new List <string>(); var itemsInRegions = new Dictionary <string, List <ItemObject> >(); foreach (var item in randomizedResult.ItemList) { if (item.NewLocation == null) { continue; } if (randomizedResult.Settings.ClearHints) { // skip free items if (ItemUtils.IsStartingLocation(item.NewLocation.Value)) { continue; } } if (!item.IsRandomized) { continue; } var itemName = item.Item.Name(); if (randomizedResult.Settings.GossipHintStyle != GossipHintStyle.Competitive && (itemName.Contains("Heart") || itemName.Contains("Rupee")) && (randomizedResult.Settings.ClearHints || randomizedResult.Random.Next(8) != 0)) { continue; } if (randomizedResult.Settings.GossipHintStyle == GossipHintStyle.Competitive) { var preventRegions = new List <string> { "The Moon", "Bottle Catch", "Misc" }; var itemRegion = item.NewLocation.Value.Region(); if (!string.IsNullOrWhiteSpace(itemRegion) && !preventRegions.Contains(itemRegion) && (randomizedResult.Settings.AddSongs || !ItemUtils.IsSong(item.Item))) { if (!itemsInRegions.ContainsKey(itemRegion)) { itemsInRegions[itemRegion] = new List <ItemObject>(); } itemsInRegions[itemRegion].Add(item); } if (!Gossip.GuaranteedLocationHints.Contains(item.NewLocation.Value)) { continue; } } randomizedItems.Add(item); } var unusedItems = randomizedItems.ToList(); if (randomizedResult.Settings.GossipHintStyle == GossipHintStyle.Competitive) { unusedItems.AddRange(randomizedItems); var requiredHints = new List <string>(); var nonRequiredHints = new List <string>(); foreach (var kvp in itemsInRegions) { bool regionHasRequiredItem; if (kvp.Value.Any(io => randomizedResult.ItemsRequiredForMoonAccess.Contains(io.Item))) { regionHasRequiredItem = true; } else if (!kvp.Value.Any(io => randomizedResult.AllItemsOnPathToMoon.Contains(io.Item))) { regionHasRequiredItem = false; } else { continue; } ushort soundEffectId = 0x690C; // grandma curious string start = Gossip.MessageStartSentences.Random(randomizedResult.Random); string sfx = $"{(char)((soundEffectId >> 8) & 0xFF)}{(char)(soundEffectId & 0xFF)}"; var locationMessage = kvp.Key; var mid = "is"; var itemMessage = regionHasRequiredItem ? "on the Way of the Hero" : "a foolish choice"; var list = regionHasRequiredItem ? requiredHints : nonRequiredHints; list.Add($"\x1E{sfx}{start} \x01{locationMessage}\x00 {mid} \x06{itemMessage}\x00...\xBF".Wrap(35, "\x11")); } var numberOfRequiredHints = 3; var numberOfNonRequiredHints = 2; for (var i = 0; i < numberOfRequiredHints; i++) { var chosen = requiredHints.RandomOrDefault(randomizedResult.Random); if (chosen != null) { requiredHints.Remove(chosen); competitiveHints.Add(chosen); competitiveHints.Add(chosen); } } for (var i = 0; i < numberOfNonRequiredHints; i++) { var chosen = nonRequiredHints.RandomOrDefault(randomizedResult.Random); if (chosen != null) { nonRequiredHints.Remove(chosen); competitiveHints.Add(chosen); competitiveHints.Add(chosen); } } } List <MessageEntry> finalHints = new List <MessageEntry>(); foreach (var gossipQuote in Enum.GetValues(typeof(GossipQuote)).Cast <GossipQuote>().OrderBy(gq => randomizedResult.Random.Next())) { var isMoonGossipStone = gossipQuote.ToString().StartsWith("Moon"); var restrictionAttributes = gossipQuote.GetAttributes <GossipRestrictAttribute>().ToList(); ItemObject item = null; var forceClear = false; while (item == null) { if (restrictionAttributes.Any() && (isMoonGossipStone || randomizedResult.Settings.GossipHintStyle == GossipHintStyle.Relevant)) { var chosen = restrictionAttributes.Random(randomizedResult.Random); var candidateItem = chosen.Type == GossipRestrictAttribute.RestrictionType.Item ? randomizedResult.ItemList.Single(io => io.Item == chosen.Item) : randomizedResult.ItemList.Single(io => io.NewLocation == chosen.Item); if (isMoonGossipStone || unusedItems.Contains(candidateItem)) { item = candidateItem; forceClear = chosen.ForceClear; } else { restrictionAttributes.Remove(chosen); } } else if (unusedItems.Any()) { item = unusedItems.Random(randomizedResult.Random); } else { break; } } if (!isMoonGossipStone) { unusedItems.Remove(item); } string messageText = null; if (item != null) { ushort soundEffectId = 0x690C; // grandma curious string itemName = null; string locationName = null; if (forceClear || randomizedResult.Settings.ClearHints) { itemName = item.Item.Name(); locationName = item.NewLocation.Value.Location(); } else { if (isMoonGossipStone || randomizedResult.Settings.GossipHintStyle == GossipHintStyle.Competitive || randomizedResult.Random.Next(100) >= 5) // 5% chance of fake/junk hint if it's not a moon gossip stone or competitive style { itemName = item.Item.ItemHints().Random(randomizedResult.Random); locationName = item.NewLocation.Value.LocationHints().Random(randomizedResult.Random); } else { if (randomizedResult.Random.Next(2) == 0) // 50% chance for fake hint. otherwise default to junk hint. { soundEffectId = 0x690A; // grandma laugh itemName = item.Item.ItemHints().Random(randomizedResult.Random); locationName = randomizedItems.Random(randomizedResult.Random).Item.LocationHints().Random(randomizedResult.Random); } } } if (itemName != null && locationName != null) { messageText = BuildGossipQuote(soundEffectId, locationName, itemName, randomizedResult.Random); } } if (messageText == null) { if (competitiveHints.Any()) { messageText = competitiveHints.Random(randomizedResult.Random); competitiveHints.Remove(messageText); } else { messageText = Gossip.JunkMessages.Random(randomizedResult.Random); } } finalHints.Add(new MessageEntry() { Id = (ushort)gossipQuote, Message = messageText, Header = MessageHeader.ToArray() }); } return(finalHints); }
public static void CreateSpoilerLog(RandomizedResult randomized, SettingsObject settings) { var itemList = randomized.ItemList .Where(io => !io.Item.IsFake()) .Select(u => new SpoilerItem(u, ItemUtils.IsRequired(u.Item, randomized), ItemUtils.IsImportant(u.Item, randomized))); var settingsString = settings.ToString(); var directory = Path.GetDirectoryName(settings.OutputROMFilename); var filename = $"{Path.GetFileNameWithoutExtension(settings.OutputROMFilename)}"; var plainTextRegex = new Regex("[^a-zA-Z0-9' .\\-]+"); Spoiler spoiler = new Spoiler() { Version = MainForm.AssemblyVersion.Substring(26), SettingsString = settingsString, Seed = settings.Seed, RandomizeDungeonEntrances = settings.RandomizeDungeonEntrances, ItemList = itemList.Where(u => !u.Item.IsFake()).ToList(), NewDestinationIndices = randomized.NewDestinationIndices, Logic = randomized.Logic, CustomItemListString = settings.UseCustomItemList ? settings.CustomItemListString : null, CustomStartingItemListString = settings.CustomStartingItemList.Any() ? settings.CustomStartingItemListString : null, CustomJunkLocationsString = settings.CustomJunkLocationsString, GossipHints = randomized.GossipQuotes?.ToDictionary(me => (GossipQuote)me.Id, (me) => { var message = me.Message.Substring(1); var soundEffect = message.Substring(0, 2); message = message.Substring(2); if (soundEffect == "\x69\x0C") { // real } else if (soundEffect == "\x69\x0A") { // fake message = "FAKE - " + message; } else { // junk message = "JUNK - " + message; } return(plainTextRegex.Replace(message.Replace("\x11", " "), "")); }), }; if (settings.GenerateHTMLLog) { filename += "_SpoilerLog.html"; using (StreamWriter newlog = new StreamWriter(Path.Combine(directory, filename))) { Templates.HtmlSpoiler htmlspoiler = new Templates.HtmlSpoiler(spoiler); newlog.Write(htmlspoiler.TransformText()); } } else { filename += "_SpoilerLog.txt"; CreateTextSpoilerLog(spoiler, Path.Combine(directory, filename)); } }
public static void WriteNewItem(int location, int item) { System.Diagnostics.Debug.WriteLine($"Writing {Items.ITEM_NAMES[item]} --> {Items.ITEM_NAMES[location]}"); bool isRepeatable = Items.REPEATABLE.Contains(item); bool isCycleRepeatable = Items.CYCLE_REPEATABLE.Contains(item); location = ItemUtils.SubtractItemOffset(location); item = ItemUtils.SubtractItemOffset(item); int f = RomUtils.GetFileIndexForWriting(GET_ITEM_TABLE); int baseaddr = GET_ITEM_TABLE - RomData.MMFileList[f].Addr; var getItemIndex = RomData.GetItemIndices[location]; if (location == Items.ItemGoldDust) { getItemIndex = 0x6A; // Place items intended for Gold Dust at the Goron Race Bottle location. } int offset = (getItemIndex - 1) * 8 + baseaddr; var newItem = RomData.GetItemList[item]; var fileData = RomData.MMFileList[f].Data; var data = new byte[] { newItem.ItemGained, newItem.Flag, newItem.Index, newItem.Type, (byte)(newItem.Message >> 8), (byte)(newItem.Message & 0xFF), (byte)(newItem.Object >> 8), (byte)(newItem.Object & 0xFF), }; ReadWriteUtils.Arr_Insert(data, 0, data.Length, fileData, offset); if (isCycleRepeatable) { ReadWriteUtils.WriteToROM(cycle_repeat, (ushort)getItemIndex); cycle_repeat += 2; } if (!isRepeatable) { SceneUtils.UpdateSceneFlagMask(getItemIndex); } if (item == Items.ItemBottleWitch) { ReadWriteUtils.WriteToROM(0xB49982, (ushort)getItemIndex); ReadWriteUtils.WriteToROM(0xC72B42, (ushort)getItemIndex); } if (item == Items.ItemBottleMadameAroma) { ReadWriteUtils.WriteToROM(0xB4999A, (ushort)getItemIndex); ReadWriteUtils.WriteToROM(0xC72B4E, (ushort)getItemIndex); } if (item == Items.ItemBottleAliens) { ReadWriteUtils.WriteToROM(0xB499A6, (ushort)getItemIndex); ReadWriteUtils.WriteToROM(0xC72B5A, (ushort)getItemIndex); } // Goron Race Bottle now rewards a plain Gold Dust, so this is unnecessary until a proper fix for Goron Dust is found. //if (NewItem == Items.ItemBottleGoronRace) //{ // WriteToROM(0xB499B2, (ushort)getItemIndex); // WriteToROM(0xC72B66, (ushort)getItemIndex); //} }
public static List <MessageEntry> MakeGossipQuotes(SettingsObject settings, List <ItemObject> items, Random random) { if (!settings.EnableGossipHints) { return(new List <MessageEntry>()); } var hints = new List <string>(); var GossipList = settings.ClearHints ? items .Where(io => !ItemUtils.IsFakeItem(io.ID)) .Select(io => new Gossip { SourceMessage = new string[] { Items.LOCATION_NAMES[io.ID] }, DestinationMessage = new string[] { Items.ITEM_NAMES[io.ID] }, }).ToList() : GetGossipList(); foreach (var item in items) { if (!item.ReplacesAnotherItem) { continue; } // Skip hints for vanilla bottle content if ((!settings.RandomizeBottleCatchContents) && ItemUtils.IsBottleCatchContent(item.ID)) { continue; } // Skip hints for vanilla shop items if ((!settings.AddShopItems) && ItemUtils.IsShopItem(item.ID)) { continue; } // Skip hints for vanilla dungeon items if (!settings.AddDungeonItems && ItemUtils.IsDungeonItem(item.ID)) { continue; } int sourceItemId = ItemUtils.SubtractItemOffset(item.ReplacesItemId); int toItemId = ItemUtils.SubtractItemOffset(item.ID); ushort soundEffectId = 0x690C; if (!settings.ClearHints) { // 5% chance of being fake bool isFake = random.Next(100) < 5; if (isFake) { sourceItemId = random.Next(GossipList.Count); soundEffectId = 0x690A; } } if (IsBadMessage(GossipList[toItemId].DestinationMessage[0]) && (settings.ClearHints || random.Next(8) != 0)) { continue; } int sourceMessageLength = GossipList[sourceItemId] .SourceMessage .Length; int destinationMessageLength = GossipList[toItemId] .DestinationMessage .Length; // Randomize messages string sourceMessage = GossipList[sourceItemId] .SourceMessage[random.Next(sourceMessageLength)]; string destinationMessage = GossipList[toItemId] .DestinationMessage[random.Next(destinationMessageLength)]; var quote = BuildGossipQuote(soundEffectId, sourceMessage, destinationMessage, random); hints.Add(quote); } if (!settings.ClearHints) { for (int i = 0; i < Gossip.JunkMessages.Count; i++) { hints.Add(Gossip.JunkMessages[i]); } } //trim the pool of messages List <MessageEntry> finalHints = new List <MessageEntry>(); for (ushort textId = GOSSIP_START_ID; textId < GOSSIP_END_ID; textId++) { if (GossipExclude.Contains(textId)) { continue; } int selectedIndex = random.Next(hints.Count); string selectedHint = hints[selectedIndex]; MessageEntry message = new MessageEntry() { Id = textId, Message = selectedHint, Header = MessageHeader.ToArray() }; finalHints.Add(message); hints.RemoveAt(selectedIndex); } return(finalHints); }
public static List <MessageEntry> MakeGossipQuotes(RandomizedResult randomizedResult) { if (randomizedResult.Settings.GossipHintStyle == GossipHintStyle.Default) { return(new List <MessageEntry>()); } var randomizedItems = new List <ItemObject>(); var competitiveHints = new List <string>(); var itemsInRegions = new Dictionary <Region, List <ItemObject> >(); foreach (var item in randomizedResult.ItemList) { if (item.NewLocation == null) { continue; } if (randomizedResult.Settings.ClearHints) { // skip free items if (ItemUtils.IsStartingLocation(item.NewLocation.Value)) { continue; } } if (!item.IsRandomized) { continue; } var itemName = item.Item.Name(); if (randomizedResult.Settings.GossipHintStyle != GossipHintStyle.Competitive && (itemName.Contains("Heart") || itemName.Contains("Rupee")) && (randomizedResult.Settings.ClearHints || randomizedResult.Random.Next(8) != 0)) { continue; } if (randomizedResult.Settings.GossipHintStyle == GossipHintStyle.Competitive) { var preventRegions = new List <Region> { Region.TheMoon, Region.BottleCatch, Region.Misc }; var itemRegion = item.NewLocation.Value.Region(); if (itemRegion.HasValue && !preventRegions.Contains(itemRegion.Value) && !randomizedResult.Settings.CustomJunkLocations.Contains(item.NewLocation.Value)) { if (!itemsInRegions.ContainsKey(itemRegion.Value)) { itemsInRegions[itemRegion.Value] = new List <ItemObject>(); } itemsInRegions[itemRegion.Value].Add(item); } var competitiveHintInfo = item.NewLocation.Value.GetAttribute <GossipCompetitiveHintAttribute>(); if (competitiveHintInfo == null) { continue; } if (randomizedResult.Settings.CustomJunkLocations.Contains(item.NewLocation.Value)) { continue; } if (competitiveHintInfo.Condition != null && competitiveHintInfo.Condition(randomizedResult.Settings)) { continue; } } randomizedItems.Add(item); } var unusedItems = randomizedItems.ToList(); if (randomizedResult.Settings.GossipHintStyle == GossipHintStyle.Competitive) { unusedItems.AddRange(randomizedItems); var requiredHints = new List <string>(); var nonRequiredHints = new List <string>(); foreach (var kvp in itemsInRegions) { var numberOfRequiredItems = kvp.Value.Count(io => ItemUtils.IsRequired(io.Item, randomizedResult)); var numberOfImportantItems = kvp.Value.Count(io => ItemUtils.IsImportant(io.Item, randomizedResult)); if (numberOfRequiredItems == 0 && numberOfImportantItems > 0) { continue; } ushort soundEffectId = 0x690C; // grandma curious string start = Gossip.MessageStartSentences.Random(randomizedResult.Random); string sfx = $"{(char)((soundEffectId >> 8) & 0xFF)}{(char)(soundEffectId & 0xFF)}"; var locationMessage = kvp.Key.Name(); //var mid = "is"; //var itemMessage = numberOfRequiredItems > 0 // ? "on the Way of the Hero" // : "a foolish choice"; List <string> list; char color; if (numberOfRequiredItems > 0) { list = requiredHints; color = TextCommands.ColorYellow; } else { list = nonRequiredHints; color = TextCommands.ColorSilver; } //list.Add($"\x1E{sfx}{start} \x01{locationMessage}\x00 {mid} \x06{itemMessage}\x00...\xBF".Wrap(35, "\x11")); var mid = "has"; list.Add($"\x1E{sfx}{start} {TextCommands.ColorRed}{locationMessage}{TextCommands.ColorWhite} {mid} {color}{NumberToWords(numberOfImportantItems)} important item{(numberOfImportantItems == 1 ? "" : "s")}{TextCommands.ColorWhite}...\xBF".Wrap(35, "\x11")); } var numberOfRequiredHints = 2; var numberOfNonRequiredHints = 3; for (var i = 0; i < numberOfRequiredHints; i++) { var chosen = requiredHints.RandomOrDefault(randomizedResult.Random); if (chosen != null) { requiredHints.Remove(chosen); competitiveHints.Add(chosen); competitiveHints.Add(chosen); } } for (var i = 0; i < numberOfNonRequiredHints; i++) { var chosen = nonRequiredHints.RandomOrDefault(randomizedResult.Random); if (chosen != null) { nonRequiredHints.Remove(chosen); competitiveHints.Add(chosen); competitiveHints.Add(chosen); } } } List <MessageEntry> finalHints = new List <MessageEntry>(); foreach (var gossipQuote in Enum.GetValues(typeof(GossipQuote)).Cast <GossipQuote>().OrderBy(gq => randomizedResult.Random.Next())) { string messageText = null; var isMoonGossipStone = gossipQuote.ToString().StartsWith("Moon"); if (!isMoonGossipStone && competitiveHints.Any()) { messageText = competitiveHints.Random(randomizedResult.Random); competitiveHints.Remove(messageText); } if (messageText == null) { var restrictionAttributes = gossipQuote.GetAttributes <GossipRestrictAttribute>().ToList(); ItemObject item = null; var forceClear = false; while (item == null) { if (restrictionAttributes.Any() && (isMoonGossipStone || randomizedResult.Settings.GossipHintStyle == GossipHintStyle.Relevant)) { var chosen = restrictionAttributes.Random(randomizedResult.Random); var candidateItem = chosen.Type == GossipRestrictAttribute.RestrictionType.Item ? randomizedResult.ItemList.Single(io => io.Item == chosen.Item) : randomizedResult.ItemList.Single(io => io.NewLocation == chosen.Item); if (isMoonGossipStone || unusedItems.Contains(candidateItem)) { item = candidateItem; forceClear = chosen.ForceClear; } else { restrictionAttributes.Remove(chosen); } } else if (unusedItems.Any()) { if (randomizedResult.Settings.GossipHintStyle == GossipHintStyle.Competitive) { item = unusedItems.FirstOrDefault(io => unusedItems.Count(x => x.Item == io.Item) == 1); if (item == null) { item = unusedItems.GroupBy(io => io.NewLocation.Value.GetAttribute <GossipCompetitiveHintAttribute>().Priority) .OrderByDescending(g => g.Key) .First() .ToList() .Random(randomizedResult.Random); } } else { item = unusedItems.Random(randomizedResult.Random); } } else { break; } } if (!isMoonGossipStone) { unusedItems.Remove(item); } if (item != null) { ushort soundEffectId = 0x690C; // grandma curious string itemName = null; string locationName = null; if (forceClear || randomizedResult.Settings.ClearHints) { itemName = item.Item.Name(); locationName = item.NewLocation.Value.Location(); } else { if (isMoonGossipStone || randomizedResult.Settings.GossipHintStyle == GossipHintStyle.Competitive || randomizedResult.Random.Next(100) >= 5) // 5% chance of fake/junk hint if it's not a moon gossip stone or competitive style { itemName = item.Item.ItemHints().Random(randomizedResult.Random); locationName = item.NewLocation.Value.LocationHints().Random(randomizedResult.Random); } else { if (randomizedResult.Random.Next(2) == 0) // 50% chance for fake hint. otherwise default to junk hint. { soundEffectId = 0x690A; // grandma laugh itemName = item.Item.ItemHints().Random(randomizedResult.Random); locationName = randomizedItems.Random(randomizedResult.Random).Item.LocationHints().Random(randomizedResult.Random); } } } if (itemName != null && locationName != null) { messageText = BuildGossipQuote(soundEffectId, locationName, itemName, randomizedResult.Random); } } } if (messageText == null) { messageText = Gossip.JunkMessages.Random(randomizedResult.Random); } finalHints.Add(new MessageEntry() { Id = (ushort)gossipQuote, Message = messageText, Header = MessageHeader.ToArray() }); } return(finalHints); }