예제 #1
0
        public static void CreateSpoilerLog(RandomizedResult randomized, GameplaySettings settings, OutputSettings outputSettings)
        {
            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(outputSettings.OutputROMFilename);
            var filename  = $"{Path.GetFileNameWithoutExtension(outputSettings.OutputROMFilename)}";

            var     plainTextRegex = new Regex("[^a-zA-Z0-9' .\\-]+");
            Spoiler spoiler        = new Spoiler()
            {
                Version                      = Randomizer.AssemblyVersion,
                SettingsString               = settingsString,
                Seed                         = randomized.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 (outputSettings.GenerateHTMLLog)
            {
                using (StreamWriter newlog = new StreamWriter(Path.Combine(directory, filename + "_Tracker.html")))
                {
                    Templates.HtmlSpoiler htmlspoiler = new Templates.HtmlSpoiler(spoiler);
                    newlog.Write(htmlspoiler.TransformText());
                }
            }

            if (outputSettings.GenerateSpoilerLog)
            {
                CreateTextSpoilerLog(spoiler, Path.Combine(directory, filename + "_SpoilerLog.txt"));
            }
        }
예제 #2
0
 public static bool IsRequired(Item item, RandomizedResult randomizedResult)
 {
     return(!item.Name().Contains("Heart") &&
            !IsStrayFairy(item) &&
            !IsSkulltulaToken(item) &&
            randomizedResult.ItemsRequiredForMoonAccess.Contains(item));
 }
예제 #3
0
 public static bool IsImportant(Item item, Item locationForImportance, RandomizedResult randomizedResult)
 {
     // todo maybe actually calculate if song of time is needed in the seed
     return(item == Item.SongTime || (
                !item.Name().Contains("Heart") &&
                item != Item.IceTrap &&
                randomizedResult.ImportantLocations.Contains(locationForImportance)
                ));
 }
예제 #4
0
        public static void CreateSpoilerLog(RandomizedResult randomized, Settings settings)
        {
            var itemList = randomized.ItemList
                           .Where(u => u.ReplacesAnotherItem)
                           .Select(u => new SpoilerItem(u)
            {
                ReplacedById = randomized.ItemList.Single(i => i.ReplacesItemId == u.ID).ID
            })
                           .ToList();
            var settingsString = settings.ToString();

            var directory = Path.GetDirectoryName(settings.OutputROMFilename);
            var filename  = $"{Path.GetFileNameWithoutExtension(settings.OutputROMFilename)}";

            Spoiler spoiler = new Spoiler()
            {
                Version                   = MainForm.AssemblyVersion.Substring(26),
                SettingsString            = settingsString,
                Seed                      = settings.Seed,
                RandomizeDungeonEntrances = settings.RandomizeDungeonEntrances,
                ItemList                  = itemList,
                NewDestinationIndices     = randomized.NewDestinationIndices,
                Logic                     = randomized.Logic
            };

            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));
            }
        }
예제 #5
0
 public static bool IsImportant(Item item, RandomizedResult randomizedResult)
 {
     return(!item.Name().Contains("Heart") && randomizedResult.ImportantItems.Contains(item));
 }
예제 #6
0
        public static string Process(Configuration configuration, int seed, IProgressReporter progressReporter)
        {
            var randomizer = new Randomizer(configuration.GameplaySettings, seed);
            RandomizedResult randomized = null;

            if (string.IsNullOrWhiteSpace(configuration.OutputSettings.InputPatchFilename))
            {
                try
                {
                    randomized = randomizer.Randomize(progressReporter);
                }
                catch (RandomizationException ex)
                {
                    string nl = Environment.NewLine;
                    return($"Error randomizing logic: {ex.Message}{nl}{nl}Please try a different seed");
                }
                catch (Exception ex)
                {
                    return(ex.Message);
                }

                if (configuration.OutputSettings.GenerateSpoilerLog &&
                    configuration.GameplaySettings.LogicMode != LogicMode.Vanilla)
                {
                    SpoilerUtils.CreateSpoilerLog(randomized, configuration.GameplaySettings, configuration.OutputSettings);
                }
            }

            if (configuration.OutputSettings.GenerateROM || configuration.OutputSettings.OutputVC || configuration.OutputSettings.GeneratePatch)
            {
                if (!RomUtils.ValidateROM(configuration.OutputSettings.InputROMFilename))
                {
                    return("Cannot verify input ROM is Majora's Mask (U).");
                }

                var builder = new Builder(randomized, configuration.CosmeticSettings);

                try
                {
                    builder.MakeROM(configuration.OutputSettings, progressReporter);
                }
                catch (PatchMagicException)
                {
                    return($"Error applying patch: Not a valid patch file");
                }
                catch (PatchVersionException ex)
                {
                    return($"Error applying patch: {ex.Message}");
                }
                catch (Exception ex)
                {
                    string nl = Environment.NewLine;
                    return($"Error building ROM: {ex.Message}{nl}{nl}Please contact the development team and provide them more information");
                }
            }

            //settings.InputPatchFilename = null;

            return(null);
            //return "Generation complete!";
        }
예제 #7
0
 public static bool IsImportant(Item item, Item locationForImportance, RandomizedResult randomizedResult)
 {
     return(!item.Name().Contains("Heart") &&
            item != Item.IceTrap &&
            randomizedResult.ImportantLocations?.Contains(locationForImportance) == true);
 }
예제 #8
0
        private static (string message, string clearMessage, List <ItemObject> combined) BuildItemHint(ItemObject item, RandomizedResult randomizedResult,
                                                                                                       GossipHintStyle hintStyle, List <ItemObject> itemsToCombineWith, List <ItemObject> hintableItems, Func <ItemObject, bool> shouldIndicateImportance, Random random)
        {
            ushort soundEffectId = 0x690C; // grandma curious
            var    itemNames     = new List <string>();
            var    locationNames = new List <string>();
            bool   hasOrder      = item.NewLocation.Value.HasAttribute <GossipCombineOrderAttribute>();
            var    combined      = new List <ItemObject>();

            combined.Add(item);

            var article    = randomizedResult.Settings.ProgressiveUpgrades && item.Item.HasAttribute <ProgressiveAttribute>() ? "a " : GetArticle(item.Item);
            var color      = TextCommands.ColorPink;
            var importance = "";

            if (randomizedResult.Settings.HintsIndicateImportance && shouldIndicateImportance?.Invoke(item) == true)
            {
                var locationForImportance = item.Item.MainLocation().HasValue ? item.Item : item.NewLocation.Value;
                var isRequired            = ItemUtils.IsRequired(item.Item, locationForImportance, randomizedResult, true);
                if (!ItemUtils.IsLogicallyJunk(item.Item))
                {
                    importance = isRequired ? " (required)" : " (not required)";
                }
                color = isRequired ? TextCommands.ColorYellow : TextCommands.ColorSilver;
            }
            itemNames.Add(article + color + item.Item.ProgressiveUpgradeName(randomizedResult.Settings.ProgressiveUpgrades) + TextCommands.ColorWhite + importance);
            locationNames.Add(item.NewLocation.Value.Location());
            if (hintStyle != GossipHintStyle.Relevant)
            {
                var gossipCombineAttribute = item.NewLocation.Value.GetAttribute <GossipCombineAttribute>();
                combined = itemsToCombineWith.Where(io => gossipCombineAttribute?.OtherItems.Contains(io.NewLocation.Value) == true).ToList();
                if (combined.Any())
                {
                    combined.Add(item);
                    combined = combined.OrderBy(io => io.NewLocation.Value.GetAttribute <GossipCombineOrderAttribute>()?.Order ?? random.Next()).ToList();
                    locationNames.Clear();
                    itemNames.Clear();
                    var combinedName = gossipCombineAttribute.CombinedName;
                    if (!string.IsNullOrWhiteSpace(combinedName))
                    {
                        locationNames.Add(combinedName);
                    }
                    else
                    {
                        locationNames.AddRange(combined.Select(io => io.NewLocation.Value.Location()));
                    }
                    itemNames.AddRange(combined.Select(io =>
                    {
                        article    = randomizedResult.Settings.ProgressiveUpgrades && io.Item.HasAttribute <ProgressiveAttribute>() ? "a " : GetArticle(io.Item);
                        color      = TextCommands.ColorPink;
                        importance = "";
                        if (randomizedResult.Settings.HintsIndicateImportance && shouldIndicateImportance?.Invoke(io) == true)
                        {
                            var locationForImportance = io.Item.MainLocation().HasValue ? io.Item : io.NewLocation.Value;
                            var isRequired            = ItemUtils.IsRequired(io.Item, locationForImportance, randomizedResult, true);
                            if (!ItemUtils.IsLogicallyJunk(io.Item))
                            {
                                importance = isRequired ? " (required)" : " (not required)";
                            }
                            color = isRequired ? TextCommands.ColorYellow : TextCommands.ColorSilver;
                        }
                        return(article + color + io.Item.ProgressiveUpgradeName(randomizedResult.Settings.ProgressiveUpgrades) + TextCommands.ColorWhite + importance);
                    }));
                }
                else
                {
                    combined.Add(item);
                }
            }
            string clearMessage = null;

            if (itemNames.Any() && locationNames.Any())
            {
                clearMessage = BuildGossipQuote(soundEffectId, locationNames, itemNames, hasOrder, random);
            }

            itemNames.Clear();
            locationNames.Clear();
            if (item.Mimic != null)
            {
                // If item has a mimic and not using clear hints, always use a fake hint.
                soundEffectId = 0x690A; // grandma laugh
                itemNames.Add(item.Mimic.Item.ItemHints().Random(random));
                locationNames.Add(item.NewLocation.Value.LocationHints().Random(random));
            }
            else if (hintStyle != GossipHintStyle.Random || random.Next(100) >= 5) // 5% chance of fake/junk hint if it's not a moon gossip stone or competitive style
            {
                itemNames.Add(item.Item.ItemHints().Random(random));
                locationNames.Add(item.NewLocation.Value.LocationHints().Random(random));
            }
            else
            {
                if (random.Next(2) == 0)    // 50% chance for fake hint. otherwise default to junk hint.
                {
                    soundEffectId = 0x690A; // grandma laugh
                    itemNames.Add(item.Item.ItemHints().Random(random));
                    locationNames.Add(hintableItems.Random(random).NewLocation.Value.LocationHints().Random(random));
                }
            }
            if (itemNames.Any())
            {
                itemNames[0] = $"{TextCommands.ColorPink}{itemNames[0]}{TextCommands.ColorWhite}";
            }

            string message = null;

            if (itemNames.Any() && locationNames.Any())
            {
                message = BuildGossipQuote(soundEffectId, locationNames, itemNames, hasOrder, random);
                //return (BuildGossipQuote(soundEffectId, locationNames, itemNames, hasOrder, random), combined);
            }

            if (message != null || clearMessage != null)
            {
                return(message, clearMessage, combined);
            }

            return(null, null, null);
        }
예제 #9
0
 public Builder(RandomizedResult randomized)
 {
     _randomized   = randomized;
     _settings     = randomized.Settings;
     _messageTable = new MessageTable();
 }
예제 #10
0
파일: Builder.cs 프로젝트: Bribz/mm-rando
 public Builder(RandomizedResult randomized)
 {
     _randomized = randomized;
     _settings   = randomized.Settings;
 }
예제 #11
0
 public static bool IsRequired(Item item, Item locationForImportance, RandomizedResult randomizedResult)
 {
     return(CanBeRequired(item) && randomizedResult.LocationsRequiredForMoonAccess.Contains(locationForImportance));
 }
예제 #12
0
        public static void CreateSpoilerLog(RandomizedResult randomized, GameplaySettings settings, OutputSettings outputSettings)
        {
            var itemList = randomized.ItemList
                           .Where(io => !io.Item.IsFake() && io.NewLocation.HasValue)
                           .Select(u => new SpoilerItem(u, ItemUtils.IsRequired(u.Item, randomized), ItemUtils.IsImportant(u.Item, randomized), settings.ProgressiveUpgrades));

            Dictionary <Item, Item> dungeonEntrances = new Dictionary <Item, Item>();

            if (settings.RandomizeDungeonEntrances)
            {
                var entrances = new List <Item>
                {
                    Item.AreaWoodFallTempleAccess,
                    Item.AreaSnowheadTempleAccess,
                    Item.AreaGreatBayTempleAccess,
                    Item.AreaInvertedStoneTowerTempleAccess,
                };
                foreach (var entrance in entrances.OrderBy(e => entrances.IndexOf(randomized.ItemList[e].NewLocation.Value)))
                {
                    dungeonEntrances.Add(randomized.ItemList[entrance].NewLocation.Value, entrance);
                }
            }
            var settingsString = settings.ToString();

            var directory = Path.GetDirectoryName(outputSettings.OutputROMFilename);
            var filename  = $"{Path.GetFileNameWithoutExtension(outputSettings.OutputROMFilename)}";

            var     plainTextRegex = new Regex("[^a-zA-Z0-9' .\\-]+");
            Spoiler spoiler        = new Spoiler()
            {
                Version          = Randomizer.AssemblyVersion,
                SettingsString   = settingsString,
                Seed             = randomized.Seed,
                DungeonEntrances = dungeonEntrances,
                ItemList         = itemList.ToList(),
                Logic            = randomized.Logic,
                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 (outputSettings.GenerateHTMLLog)
            {
                using (StreamWriter newlog = new StreamWriter(Path.Combine(directory, filename + "_Tracker.html")))
                {
                    Templates.HtmlSpoiler htmlspoiler = new Templates.HtmlSpoiler(spoiler);
                    newlog.Write(htmlspoiler.TransformText());
                }
            }

            if (outputSettings.GenerateSpoilerLog)
            {
                CreateTextSpoilerLog(spoiler, Path.Combine(directory, filename + "_SpoilerLog.txt"));
            }
        }
예제 #13
0
        public static string Process(Configuration configuration, int seed, IProgressReporter progressReporter)
        {
            var randomizer = new Randomizer(configuration.GameplaySettings, seed);
            RandomizedResult randomized = null;

            if (string.IsNullOrWhiteSpace(configuration.OutputSettings.InputPatchFilename))
            {
                try
                {
                    randomized = randomizer.Randomize(progressReporter);

                    if ((configuration.OutputSettings.GenerateSpoilerLog || configuration.OutputSettings.GenerateHTMLLog) &&
                        configuration.GameplaySettings.LogicMode != LogicMode.Vanilla)
                    {
                        SpoilerUtils.CreateSpoilerLog(randomized, configuration.GameplaySettings, configuration.OutputSettings);
                    }
                }
                catch (RandomizationException ex)
                {
                    string nl = Environment.NewLine;
                    return($"Error randomizing logic: {ex.Message}{nl}{nl}Please try a different seed");
                }
                catch (Exception ex)
                {
                    return(ex.Message);
                }
            }

            if (configuration.OutputSettings.GenerateROM || configuration.OutputSettings.OutputVC || configuration.OutputSettings.GeneratePatch)
            {
                if (!RomUtils.ValidateROM(configuration.OutputSettings.InputROMFilename))
                {
                    return("Cannot verify input ROM is Majora's Mask (U).");
                }
                if (configuration.OutputSettings.OutputVC && !Directory.Exists(Values.VCDirectory))
                {
                    return("Error: vc folder is missing and WiiVC wad creation was selected.\n\n"
                           + "If you did not extract the whole randomizer, you must extract the vc folder. If this is a beta release, copy the vc folder from the main release.");
                }

                var builder = new Builder(randomized, configuration.CosmeticSettings);

                try
                {
                    builder.MakeROM(configuration.OutputSettings, progressReporter);
                }
                catch (ROMOverflowException ex)
                {
                    return($"Error: {ex.Message}");
                }
                catch (PatchMagicException)
                {
                    return($"Error applying patch: Not a valid patch file");
                }
                catch (PatchVersionException ex)
                {
                    return($"Error applying patch: {ex.Message}");
                }
                catch (IOException ex)
                {
                    return(ex.Message);
                }
                catch (Exception ex)
                {
                    string nl = Environment.NewLine;
                    return($"Error building ROM: {ex.Message}{nl}{nl}Please contact the development team and provide them more information");
                }
            }

            //settings.InputPatchFilename = null;

            return(null);
            //return "Generation complete!";
        }
예제 #14
0
        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);
        }
예제 #15
0
        public static void CreateSpoilerLog(RandomizedResult randomized, GameplaySettings settings, OutputSettings outputSettings)
        {
            var itemList = randomized.ItemList
                           .Where(io => (io.IsRandomized && io.NewLocation.Value.Region().HasValue) || (io.Item.MainLocation().HasValue&& randomized.ItemList[io.Item.MainLocation().Value].IsRandomized))
                           .Select(io => new {
                ItemObject            = io.Item.MainLocation().HasValue ? randomized.ItemList.Find(x => x.NewLocation == io.Item.MainLocation().Value) : io,
                LocationForImportance = io.NewLocation ?? io.Item,
                Region = io.IsRandomized ? io.NewLocation.Value.Region().Value : io.Item.Region().Value,
            })
                           .Select(u => new SpoilerItem(
                                       u.ItemObject,
                                       u.Region,
                                       ItemUtils.IsRequired(u.ItemObject.Item, u.LocationForImportance, randomized),
                                       ItemUtils.IsImportant(u.ItemObject.Item, u.LocationForImportance, randomized),
                                       randomized.ImportantSongLocations.Contains(u.LocationForImportance),
                                       settings.ProgressiveUpgrades
                                       ));

            randomized.Logic.ForEach((il) =>
            {
                if (il.ItemId >= 0)
                {
                    var io        = randomized.ItemList[il.ItemId];
                    il.IsFakeItem = (io.Item.IsFake() && io.Item.Entrance() == null) || !io.IsRandomized;
                }
            });

            Dictionary <Item, Item> dungeonEntrances = new Dictionary <Item, Item>();

            if (settings.RandomizeDungeonEntrances)
            {
                var entrances = new List <Item>
                {
                    Item.AreaWoodFallTempleAccess,
                    Item.AreaSnowheadTempleAccess,
                    Item.AreaGreatBayTempleAccess,
                    Item.AreaInvertedStoneTowerTempleAccess,
                };
                foreach (var entrance in entrances.OrderBy(e => entrances.IndexOf(randomized.ItemList[e].NewLocation.Value)))
                {
                    dungeonEntrances.Add(randomized.ItemList[entrance].NewLocation.Value, entrance);
                }
            }
            var settingsString = settings.ToString();

            var directory = Path.GetDirectoryName(outputSettings.OutputROMFilename);
            var filename  = $"{Path.GetFileNameWithoutExtension(outputSettings.OutputROMFilename)}";

            var     plainTextRegex = new Regex("[^a-zA-Z0-9' .\\-]+");
            Spoiler spoiler        = new Spoiler()
            {
                Version          = Randomizer.AssemblyVersion,
                SettingsString   = settingsString,
                Seed             = randomized.Seed,
                DungeonEntrances = dungeonEntrances,
                ItemList         = itemList.ToList(),
                Logic            = randomized.Logic,
                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", " "), ""));
                }),
                MessageCosts = randomized.MessageCosts.Select((mc, i) =>
                {
                    if (!mc.HasValue)
                    {
                        return(((string, ushort)?)null);
                    }
                    var messageCost = MessageCost.MessageCosts[i];

                    var name = messageCost.Name;
                    if (string.IsNullOrWhiteSpace(name))
                    {
                        if (messageCost.LocationsAffected.Count > 0)
                        {
                            var location     = messageCost.LocationsAffected[0];
                            var mainLocation = location.MainLocation();
                            if (mainLocation.HasValue)
                            {
                                name = $"{mainLocation.Value.Location()} ({location.ToString().Replace(mainLocation.Value.ToString(), "")})";
                            }
                            else
                            {
                                name = location.Location();
                            }
                        }
                        else
                        {
                            name = $"Message Cost [{i}]";
                        }
                    }
                    return(name, mc.Value);
                }).Where(mc => mc != null).Select(mc => mc.Value).ToList(),
            };

            if (outputSettings.GenerateHTMLLog)
            {
                using (StreamWriter newlog = new StreamWriter(Path.Combine(directory, filename + "_Tracker.html")))
                {
                    Templates.HtmlSpoiler htmlspoiler = new Templates.HtmlSpoiler(spoiler);
                    newlog.Write(htmlspoiler.TransformText());
                }
            }

            if (outputSettings.GenerateSpoilerLog)
            {
                CreateTextSpoilerLog(spoiler, Path.Combine(directory, filename + "_SpoilerLog.txt"));
            }
        }
예제 #16
0
        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);
        }
예제 #17
0
        public static List <MessageEntry> MakeGossipQuotes(RandomizedResult randomizedResult)
        {
            if (randomizedResult.Settings.GossipHintStyle == GossipHintStyle.Default)
            {
                return(new List <MessageEntry>());
            }

            var random = new Random(randomizedResult.Seed);

            var randomizedItems = new List <ItemObject>();
            var hintableItems   = new List <ItemObject>();
            var itemsInRegions  = new Dictionary <Region, List <(ItemObject io, Item locationForImportance)> >();

            foreach (var io in randomizedResult.ItemList)
            {
                if ((!io.IsRandomized || !io.NewLocation.Value.Region().HasValue) && (!io.Item.MainLocation().HasValue || !randomizedResult.ItemList[io.Item.MainLocation().Value].IsRandomized))
                {
                    continue;
                }

                // TODO make this less hard-coded
                if (io.NewLocation == Item.UpgradeRoyalWallet)
                {
                    continue;
                }

                var item = io.Item.MainLocation().HasValue ? randomizedResult.ItemList.Find(x => x.NewLocation == io.Item.MainLocation().Value) : io;

                if (randomizedResult.Settings.ClearHints && !io.Item.MainLocation().HasValue)
                {
                    // skip free items
                    if (ItemUtils.IsStartingLocation(io.NewLocation.Value))
                    {
                        continue;
                    }
                }

                if (ItemUtils.IsRegionRestricted(randomizedResult.Settings, item.Item))
                {
                    continue;
                }

                randomizedItems.Add(item);

                if (randomizedResult.Settings.GossipHintStyle == GossipHintStyle.Random)
                {
                    if (ItemUtils.IsJunk(item.Item) && (randomizedResult.Settings.ClearHints || random.Next(8) != 0))
                    {
                        continue;
                    }
                }

                if (randomizedResult.Settings.GossipHintStyle == GossipHintStyle.Competitive)
                {
                    var preventRegions = new List <Region> {
                        Region.TheMoon, Region.BottleCatch, Region.Misc
                    };
                    var locationForImportance = io.Item.MainLocation().HasValue ? io.Item : io.NewLocation.Value;
                    var itemRegion            = locationForImportance.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, Item)>();
                        }
                        itemsInRegions[itemRegion.Value].Add((item, locationForImportance));
                    }

                    var competitiveHintInfo = item.NewLocation.Value.GetAttribute <GossipCompetitiveHintAttribute>();
                    if (competitiveHintInfo == null)
                    {
                        continue;
                    }

                    if (randomizedResult.Settings.CustomJunkLocations.Contains(io.NewLocation.Value))
                    {
                        randomizedItems.Remove(item);
                        continue;
                    }

                    if (competitiveHintInfo.Condition != null && !competitiveHintInfo.Condition(randomizedResult.Settings))
                    {
                        randomizedItems.Remove(item);
                        continue;
                    }
                }

                hintableItems.Add(item);
            }

            var unusedItems        = hintableItems.ToList();
            var itemsToCombineWith = new List <ItemObject>();
            var competitiveHints   = new List <(string message, List <GossipQuote> allowedGossipQuotes)>();

            if (randomizedResult.Settings.GossipHintStyle == GossipHintStyle.Competitive)
            {
                var gossipStoneRequirements = LogicUtils.GetGossipStoneRequirements(randomizedResult.ItemList, randomizedResult.Logic, randomizedResult.Settings);

                var totalUniqueGossipHints = Enum.GetValues(typeof(GossipQuote)).Cast <GossipQuote>().Count(gq => !gq.IsMoonGossipStone()) / 2;

                var numberOfRequiredHints     = randomizedResult.Settings.AddSongs ? 4 : 3;
                var numberOfNonRequiredHints  = 3;
                var maxNumberOfClockTownHints = 2;

                var numberOfLocationHints = totalUniqueGossipHints - numberOfRequiredHints - numberOfNonRequiredHints;
                unusedItems = hintableItems.GroupBy(io => io.NewLocation.Value.GetAttribute <GossipCompetitiveHintAttribute>().Priority)
                              .OrderByDescending(g => g.Key)
                              .Select(g => g.OrderBy(_ => random.Next()).AsEnumerable())
                              .Aggregate(Enumerable.Empty <ItemObject>(), (g1, g2) => g1.Concat(g2))
                              .Take(numberOfLocationHints)
                              .ToList();
                var combinedItems = unusedItems
                                    .SelectMany(io => io.NewLocation.Value.GetAttributes <GossipCombineAttribute>().Select(gca => gca.OtherItem))
                                    .Where(item => randomizedItems.Any(io => io.NewLocation == item))
                                    .Select(item => randomizedItems.Single(io => io.NewLocation == item))
                                    .Where(io => !unusedItems.Contains(io))
                ;
                itemsToCombineWith.AddRange(combinedItems);

                unusedItems.AddRange(unusedItems);

                foreach (var unusedItem in unusedItems)
                {
                    (var messageText, var combined) = BuildItemHint(
                        unusedItem,
                        randomizedResult.Settings.GossipHintStyle,
                        false,
                        randomizedResult.Settings.ClearHints,
                        false,
                        randomizedResult.Settings.ProgressiveUpgrades,
                        itemsToCombineWith,
                        hintableItems,
                        random
                        );

                    var allowedGossipQuotes = combined
                                              .Select(io => gossipStoneRequirements.Where(kvp => !kvp.Value.Contains(io.NewLocation.Value)).Select(kvp => kvp.Key))
                                              .Aggregate((list1, list2) => list1.Intersect(list2))
                                              .ToList();
                    competitiveHints.Add((messageText, allowedGossipQuotes));
                }

                var importantRegionCounts    = new Dictionary <Region, List <(ItemObject io, Item locationForImportance)> >();
                var nonImportantRegionCounts = new Dictionary <Region, List <(ItemObject, Item)> >();
                var songOnlyRegionCounts     = new Dictionary <Region, List <(ItemObject, Item)> >();
                var clockTownRegionCounts    = new Dictionary <Region, List <(ItemObject, Item)> >();
                foreach (var kvp in itemsInRegions)
                {
                    var requiredItems  = kvp.Value.Where(io => ItemUtils.IsRequired(io.io.Item, io.locationForImportance, randomizedResult) && !unusedItems.Contains(io.io) && !itemsToCombineWith.Contains(io.io)).ToList();
                    var importantItems = kvp.Value.Where(io => ItemUtils.IsImportant(io.io.Item, io.locationForImportance, randomizedResult)).ToList();

                    Dictionary <Region, List <(ItemObject, Item)> > dict;
                    if (requiredItems.Count == 0 && importantItems.Count > 0)
                    {
                        if (!randomizedResult.Settings.AddSongs && importantItems.All(io => ItemUtils.IsSong(io.io.Item) && !randomizedResult.ImportantSongLocations.Contains(io.locationForImportance)))
                        {
                            dict = songOnlyRegionCounts;
                        }
                        else
                        {
                            continue;
                        }
                    }
                    else if (requiredItems.Count == 0)
                    {
                        dict = nonImportantRegionCounts;
                    }
                    else if (Gossip.ClockTownRegions.Contains(kvp.Key))
                    {
                        dict = clockTownRegionCounts;
                    }
                    else
                    {
                        dict = importantRegionCounts;
                    }

                    dict[kvp.Key] = requiredItems;
                }

                var chosenClockTownRegions = 0;
                for (var i = 0; i < numberOfRequiredHints; i++)
                {
                    var regionCounts = importantRegionCounts.AsEnumerable();
                    if (chosenClockTownRegions < maxNumberOfClockTownHints)
                    {
                        regionCounts = regionCounts.Concat(clockTownRegionCounts);
                    }
                    if (!regionCounts.Any())
                    {
                        regionCounts = regionCounts.Concat(clockTownRegionCounts);
                    }
                    if (regionCounts.Any())
                    {
                        var chosen = regionCounts.ToList().Random(random);
                        var allowedGossipQuotes = chosen.Value
                                                  .Select(io => gossipStoneRequirements.Where(kvp => !kvp.Value.Contains(io.locationForImportance)).Select(kvp => kvp.Key))
                                                  .Aggregate((list1, list2) => list1.Intersect(list2))
                                                  .ToList();
                        competitiveHints.Add((BuildRegionHint(chosen, RegionHintType.SomeRequired, random), allowedGossipQuotes));
                        competitiveHints.Add((BuildRegionHint(chosen, RegionHintType.SomeRequired, random), allowedGossipQuotes));
                        if (clockTownRegionCounts.Remove(chosen.Key))
                        {
                            chosenClockTownRegions++;
                        }
                        else
                        {
                            importantRegionCounts.Remove(chosen.Key);
                        }
                    }
                }

                for (var i = 0; i < numberOfNonRequiredHints; i++)
                {
                    var regionCounts = nonImportantRegionCounts.AsEnumerable();
                    regionCounts = regionCounts.Concat(songOnlyRegionCounts);
                    if (regionCounts.Any())
                    {
                        var            chosen = regionCounts.ToList().Random(random);
                        RegionHintType regionHintType;
                        if (songOnlyRegionCounts.Remove(chosen.Key))
                        {
                            regionHintType = RegionHintType.OnlyImportantSong;
                        }
                        else
                        {
                            nonImportantRegionCounts.Remove(chosen.Key);
                            regionHintType = RegionHintType.NoneRequired;
                        }

                        competitiveHints.Add((BuildRegionHint(chosen, regionHintType, random), new List <GossipQuote>()));
                        competitiveHints.Add((BuildRegionHint(chosen, regionHintType, random), new List <GossipQuote>()));
                    }
                }

                unusedItems.Clear();
            }

            List <MessageEntry> finalHints = new List <MessageEntry>();

            while (competitiveHints.Any(ch => ch.allowedGossipQuotes.Count > 0))
            {
                var competitiveHint = competitiveHints
                                      .Where(ch => ch.allowedGossipQuotes.Count > 0)
                                      .OrderBy(ch => ch.allowedGossipQuotes.Count)
                                      .ThenBy(ch => random.Next())
                                      .First();

                var gossipQuote = competitiveHint.allowedGossipQuotes.Random(random);
                finalHints.Add(new MessageEntry()
                {
                    Id      = (ushort)gossipQuote,
                    Message = competitiveHint.message,
                    Header  = MessageHeader.ToArray()
                });
                competitiveHints.Remove(competitiveHint);
                foreach (var ch in competitiveHints)
                {
                    ch.allowedGossipQuotes.Remove(gossipQuote);
                }
            }

            foreach (var gossipQuote in Enum.GetValues <GossipQuote>().OrderBy(gq => random.Next()))
            {
                if (finalHints.Any(me => me.Id == (ushort)gossipQuote))
                {
                    continue;
                }

                string messageText       = null;
                var    isMoonGossipStone = gossipQuote.IsMoonGossipStone();
                if (!isMoonGossipStone && competitiveHints.Any())
                {
                    var competitiveHint = competitiveHints.Random(random);
                    messageText = competitiveHint.message;
                    competitiveHints.Remove(competitiveHint);
                }

                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(random);
                            var candidateItem = chosen.Type == GossipRestrictAttribute.RestrictionType.Item
                                ? randomizedResult.ItemList.Single(io => io.ID == (int)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.ID == io.ID) == 1);
                                if (item == null)
                                {
                                    item = unusedItems.Random(random);
                                }
                            }
                            else
                            {
                                item = unusedItems.Random(random);
                            }
                        }
                        else
                        {
                            break;
                        }
                    }

                    if (!isMoonGossipStone)
                    {
                        unusedItems.Remove(item);
                    }

                    if (item != null)
                    {
                        (var hint, var combined) = BuildItemHint(
                            item,
                            randomizedResult.Settings.GossipHintStyle,
                            forceClear,
                            randomizedResult.Settings.ClearHints,
                            isMoonGossipStone,
                            randomizedResult.Settings.ProgressiveUpgrades,
                            itemsToCombineWith,
                            hintableItems,
                            random
                            );
                        messageText = hint;
                    }
                }

                if (messageText == null)
                {
                    messageText = Gossip.JunkMessages.Random(random);
                }

                finalHints.Add(new MessageEntry()
                {
                    Id      = (ushort)gossipQuote,
                    Message = messageText,
                    Header  = MessageHeader.ToArray()
                });
            }

            return(finalHints);
        }
예제 #18
0
        public static List <MessageEntry> MakeGossipQuotes(RandomizedResult randomizedResult)
        {
            if (randomizedResult.Settings.GossipHintStyle == GossipHintStyle.Default)
            {
                return(new List <MessageEntry>());
            }

            var random = new Random(randomizedResult.Seed);

            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 || 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)
            {
                var totalUniqueGossipHints = Enum.GetValues(typeof(GossipQuote)).Cast <GossipQuote>().Count(gq => !gq.IsMoonGossipStone()) / 2;

                var numberOfRequiredHints     = randomizedResult.Settings.AddSongs ? 4 : 3;
                var numberOfNonRequiredHints  = 3;
                var maxNumberOfSongOnlyHints  = 3;
                var maxNumberOfClockTownHints = 2;

                var numberOfLocationHints = totalUniqueGossipHints - numberOfRequiredHints - numberOfNonRequiredHints;
                unusedItems = randomizedItems.GroupBy(io => io.NewLocation.Value.GetAttribute <GossipCompetitiveHintAttribute>().Priority)
                              .OrderByDescending(g => g.Key)
                              .Select(g => g.OrderBy(_ => random.Next()).AsEnumerable())
                              .Aggregate((g1, g2) => g1.Concat(g2))
                              .Take(numberOfLocationHints)
                              .ToList();

                unusedItems.AddRange(unusedItems);
                var importantRegionCounts    = new Dictionary <Region, int>();
                var nonImportantRegionCounts = new Dictionary <Region, int>();
                var songOnlyRegionCounts     = new Dictionary <Region, int>();
                var clockTownRegionCounts    = new Dictionary <Region, int>();
                foreach (var kvp in itemsInRegions)
                {
                    var numberOfRequiredItems  = kvp.Value.Count(io => ItemUtils.IsRequired(io.Item, randomizedResult) && !unusedItems.Contains(io));
                    var numberOfImportantItems = kvp.Value.Count(io => ItemUtils.IsImportant(io.Item, randomizedResult));

                    if (numberOfRequiredItems == 0 && numberOfImportantItems > 0)
                    {
                        continue;
                    }

                    Dictionary <Region, int> dict;
                    if (numberOfRequiredItems == 0)
                    {
                        dict = nonImportantRegionCounts;
                    }
                    else if (Gossip.ClockTownRegions.Contains(kvp.Key))
                    {
                        dict = clockTownRegionCounts;
                    }
                    else if (!randomizedResult.Settings.AddSongs && kvp.Value.Count(io => ItemUtils.IsRequired(io.Item, randomizedResult) && !ItemUtils.IsSong(io.Item) && !unusedItems.Contains(io)) == 0)
                    {
                        dict = songOnlyRegionCounts;
                    }
                    else
                    {
                        dict = importantRegionCounts;
                    }

                    dict[kvp.Key] = numberOfRequiredItems;
                }

                var chosenSongOnlyRegions  = 0;
                var chosenClockTownRegions = 0;
                for (var i = 0; i < numberOfRequiredHints; i++)
                {
                    var regionCounts = importantRegionCounts.AsEnumerable();
                    if (chosenClockTownRegions < maxNumberOfClockTownHints)
                    {
                        regionCounts = regionCounts.Concat(clockTownRegionCounts);
                    }
                    if (chosenSongOnlyRegions < maxNumberOfSongOnlyHints)
                    {
                        regionCounts = regionCounts.Concat(songOnlyRegionCounts);
                    }
                    if (!regionCounts.Any())
                    {
                        regionCounts = regionCounts.Concat(clockTownRegionCounts);
                        //}
                        //if (!regionCounts.Any())
                        //{
                        regionCounts = regionCounts.Concat(songOnlyRegionCounts);
                    }
                    if (regionCounts.Any())
                    {
                        var chosen = regionCounts.ToList().Random(random);
                        competitiveHints.Add(BuildRegionHint(chosen, random));
                        competitiveHints.Add(BuildRegionHint(chosen, random));
                        if (songOnlyRegionCounts.Remove(chosen.Key))
                        {
                            chosenSongOnlyRegions++;
                        }
                        else if (clockTownRegionCounts.Remove(chosen.Key))
                        {
                            chosenClockTownRegions++;
                        }
                        else
                        {
                            importantRegionCounts.Remove(chosen.Key);
                        }
                    }
                }

                for (var i = 0; i < numberOfNonRequiredHints; i++)
                {
                    if (nonImportantRegionCounts.Any())
                    {
                        var chosen = nonImportantRegionCounts.ToList().Random(random);
                        competitiveHints.Add(BuildRegionHint(chosen, random));
                        competitiveHints.Add(BuildRegionHint(chosen, random));
                        nonImportantRegionCounts.Remove(chosen.Key);
                    }
                }
            }

            List <MessageEntry> finalHints = new List <MessageEntry>();

            foreach (var gossipQuote in Enum.GetValues(typeof(GossipQuote)).Cast <GossipQuote>().OrderBy(gq => random.Next()))
            {
                string messageText       = null;
                var    isMoonGossipStone = gossipQuote.IsMoonGossipStone();
                if (!isMoonGossipStone && competitiveHints.Any())
                {
                    messageText = competitiveHints.Random(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(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.Random(random);
                                }
                            }
                            else
                            {
                                item = unusedItems.Random(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 || 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(random);
                                locationName = item.NewLocation.Value.LocationHints().Random(random);
                            }
                            else
                            {
                                if (random.Next(2) == 0)    // 50% chance for fake hint. otherwise default to junk hint.
                                {
                                    soundEffectId = 0x690A; // grandma laugh
                                    itemName      = item.Item.ItemHints().Random(random);
                                    locationName  = randomizedItems.Random(random).Item.LocationHints().Random(random);
                                }
                            }
                        }
                        if (itemName != null && locationName != null)
                        {
                            messageText = BuildGossipQuote(soundEffectId, locationName, itemName, random);
                        }
                    }
                }

                if (messageText == null)
                {
                    messageText = Gossip.JunkMessages.Random(random);
                }

                finalHints.Add(new MessageEntry()
                {
                    Id      = (ushort)gossipQuote,
                    Message = messageText,
                    Header  = MessageHeader.ToArray()
                });
            }

            return(finalHints);
        }
예제 #19
0
 public static bool IsRequired(Item item, Item locationForImportance, RandomizedResult randomizedResult, bool anythingCanBeRequired = false)
 {
     return((anythingCanBeRequired || CanBeRequired(item)) && randomizedResult.LocationsRequiredForMoonAccess?.Contains(locationForImportance) == true);
 }
예제 #20
0
        /// <summary>
        /// Try to perform randomization and make rom
        /// </summary>
        private void TryRandomize(BackgroundWorker worker, DoWorkEventArgs e)
        {
            if (!_settings.GenerateROM && !_settings.GenerateSpoilerLog && !_settings.GeneratePatch)
            {
                MessageBox.Show($"No output selected", "Error",
                                MessageBoxButtons.OK, MessageBoxIcon.Error);
                return;
            }

            RandomizedResult randomized;

            if (string.IsNullOrWhiteSpace(_settings.InputPatchFilename))
            {
                try
                {
                    randomized = _randomizer.Randomize(worker, e);
                }
                catch (InvalidDataException ex)
                {
                    MessageBox.Show(ex.Message, "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
                    return;
                }
                catch (Exception ex)
                {
                    string nl = Environment.NewLine;
                    MessageBox.Show($"Error randomizing logic: {ex.Message}{nl}{nl}Please try a different seed", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
                    return;
                }

                if (_settings.GenerateSpoilerLog &&
                    _settings.LogicMode != LogicMode.Vanilla)
                {
                    SpoilerUtils.CreateSpoilerLog(randomized, _settings);
                }
            }
            else
            {
                randomized = new RandomizedResult(_settings, null);
            }

            if (_settings.GenerateROM || _settings.GeneratePatch)
            {
                if (!ValidateInputFile())
                {
                    return;
                }

                if (!RomUtils.ValidateROM(_settings.InputROMFilename))
                {
                    MessageBox.Show("Cannot verify input ROM is Majora's Mask (U).",
                                    "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
                    return;
                }

                _builder = new Builder(randomized);

                try
                {
                    _builder.MakeROM(_settings.InputROMFilename, _settings.OutputROMFilename, worker);
                }
                catch (Exception ex)
                {
                    string nl = Environment.NewLine;
                    MessageBox.Show($"Error building ROM: {ex.Message}{nl}{nl}Please contact the development team and provide them more information", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
                    return;
                }
            }

            _settings.InputPatchFilename = null;

            MessageBox.Show("Generation complete!",
                            "Success", MessageBoxButtons.OK, MessageBoxIcon.None);
        }
예제 #21
0
        public static List <MessageEntry> MakeGossipQuotes(
            IEnumerable <GossipQuote> gossipQuotes, GossipHintStyle hintStyle, RandomizedResult randomizedResult,
            int numberOfRequiredHints, int numberOfNonRequiredHints, int maxNumberOfClockTownHints,
            List <Region> hintedRegions, List <ItemObject> hintedItems)
        {
            if (hintStyle == GossipHintStyle.Default)
            {
                return(new List <MessageEntry>());
            }

            var random = new Random(randomizedResult.Seed);

            var randomizedItems = new List <ItemObject>();
            var hintableItems   = new List <ItemObject>();
            var itemsInRegions  = new Dictionary <Region, List <(ItemObject io, Item locationForImportance)> >();

            foreach (var io in randomizedResult.ItemList)
            {
                if ((!io.IsRandomized || !io.NewLocation.Value.Region().HasValue) && (!io.Item.MainLocation().HasValue || !randomizedResult.ItemList[io.Item.MainLocation().Value].IsRandomized))
                {
                    continue;
                }

                // TODO make this less hard-coded
                if (io.NewLocation == Item.UpgradeRoyalWallet)
                {
                    continue;
                }

                var item = io.Item.MainLocation().HasValue ? randomizedResult.ItemList.Find(x => x.NewLocation == io.Item.MainLocation().Value) : io;

                if (!io.Item.MainLocation().HasValue)
                {
                    // skip free items
                    if (ItemUtils.IsStartingLocation(io.NewLocation.Value))
                    {
                        continue;
                    }
                }

                if (ItemUtils.IsRegionRestricted(randomizedResult.Settings, item.Item))
                {
                    continue;
                }

                randomizedItems.Add(item);

                if (hintStyle == GossipHintStyle.Competitive)
                {
                    var preventRegions = new List <Region> {
                        Region.TheMoon, Region.BottleCatch, Region.Misc
                    };
                    var locationForImportance = io.Item.MainLocation().HasValue ? io.Item : io.NewLocation.Value;
                    var itemRegion            = locationForImportance.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, Item)>();
                        }
                        itemsInRegions[itemRegion.Value].Add((item, locationForImportance));
                    }

                    if (hintedItems.Contains(item))
                    {
                        continue;
                    }

                    if (randomizedResult.Settings.OverrideHintPriorities != null)
                    {
                        if (!randomizedResult.Settings.OverrideHintPriorities.Any(items => items.Contains(item.NewLocation.Value)))
                        {
                            continue;
                        }
                    }
                    else
                    {
                        var competitiveHintInfo = item.NewLocation.Value.GetAttribute <GossipCompetitiveHintAttribute>();
                        if (competitiveHintInfo == null)
                        {
                            continue;
                        }

                        if (competitiveHintInfo.Condition != null && !competitiveHintInfo.Condition(randomizedResult.Settings))
                        {
                            randomizedItems.Remove(item);
                            continue;
                        }
                    }

                    if (randomizedResult.Settings.CustomJunkLocations.Contains(io.NewLocation.Value))
                    {
                        randomizedItems.Remove(item);
                        continue;
                    }
                }

                hintableItems.Add(item);
            }

            var unusedItems        = hintableItems.ToList();
            var itemsToCombineWith = new List <ItemObject>();
            var competitiveHints   = new List <(string message, string clearMessage, List <GossipQuote> allowedGossipQuotes)>();

            if (hintStyle == GossipHintStyle.Competitive)
            {
                var gossipStoneRequirements = LogicUtils.GetGossipStoneRequirements(gossipQuotes, randomizedResult.ItemList, randomizedResult.Logic, randomizedResult.Settings, randomizedResult.CheckedImportanceLocations);

                var totalUniqueGossipHints = gossipQuotes.Count() / 2;

                var numberOfLocationHints = totalUniqueGossipHints - numberOfRequiredHints - numberOfNonRequiredHints;

                Func <ItemObject, int> getPriority = randomizedResult.Settings.OverrideHintPriorities != null
                    ? (io) => randomizedResult.Settings.OverrideHintPriorities.FindIndex(locations => locations.Contains(io.NewLocation.Value))
                    : (io) => - io.NewLocation.Value.GetAttribute <GossipCompetitiveHintAttribute>().Priority;

                unusedItems = hintableItems.GroupBy(io => io.NewLocation.Value.GetAttribute <GossipCombineAttribute>()?.CombinedName ?? io.NewLocation.Value.ToString())
                              .Select(g => g.OrderBy(getPriority).First())
                              .GroupBy(getPriority)
                              .OrderBy(g => g.Key)
                              .Select(g => g.OrderBy(_ => random.Next()).AsEnumerable())
                              .Aggregate(Enumerable.Empty <ItemObject>(), (g1, g2) => g1.Concat(g2))
                              .Take(numberOfLocationHints)
                              .ToList();
                var combinedItems = hintableItems.Where(io => !unusedItems.Contains(io));
                itemsToCombineWith.AddRange(combinedItems);

                unusedItems.AddRange(unusedItems);

                Func <ItemObject, bool> shouldIndicatePriority = randomizedResult.Settings.OverrideHintPriorities != null && randomizedResult.Settings.OverrideImportanceIndicatorTiers != null
                    ? io => randomizedResult.Settings.OverrideImportanceIndicatorTiers.Contains(getPriority(io))
                    : io => true;

                foreach (var unusedItem in unusedItems)
                {
                    (var messageText, var clearMessageText, var combined) = BuildItemHint(
                        unusedItem,
                        randomizedResult,
                        hintStyle,
                        itemsToCombineWith,
                        hintableItems,
                        shouldIndicatePriority,
                        random
                        );

                    var allowedGossipQuotes = combined
                                              .Select(io => gossipStoneRequirements.Where(kvp => kvp.Value?.Contains(io.NewLocation.Value) == false).Select(kvp => kvp.Key))
                                              .Aggregate((list1, list2) => list1.Intersect(list2))
                                              .ToList();
                    competitiveHints.Add((messageText, clearMessageText, allowedGossipQuotes));

                    hintedItems.AddRange(combined);
                }

                var importantRegionCounts    = new Dictionary <Region, List <(ItemObject io, Item locationForImportance)> >();
                var nonImportantRegionCounts = new Dictionary <Region, List <(ItemObject, Item)> >();
                var songOnlyRegionCounts     = new Dictionary <Region, List <(ItemObject, Item)> >();
                var clockTownRegionCounts    = new Dictionary <Region, List <(ItemObject, Item)> >();
                foreach (var kvp in itemsInRegions)
                {
                    var requiredItems  = kvp.Value.Where(io => ItemUtils.IsRequired(io.io.Item, io.locationForImportance, randomizedResult) && !unusedItems.Contains(io.io) && !itemsToCombineWith.Contains(io.io)).ToList();
                    var importantItems = kvp.Value.Where(io => ItemUtils.IsImportant(io.io.Item, io.locationForImportance, randomizedResult)).ToList();

                    Dictionary <Region, List <(ItemObject, Item)> > dict;
                    if (requiredItems.Count == 0 && importantItems.Count > 0)
                    {
                        if (!randomizedResult.Settings.AddSongs && importantItems.All(io => ItemUtils.IsSong(io.io.Item)))
                        {
                            dict = songOnlyRegionCounts;
                        }
                        else
                        {
                            continue;
                        }
                    }
                    else if (requiredItems.Count == 0)
                    {
                        dict = nonImportantRegionCounts;
                    }
                    else if (Gossip.ClockTownRegions.Contains(kvp.Key))
                    {
                        dict = clockTownRegionCounts;
                    }
                    else
                    {
                        dict = importantRegionCounts;
                    }

                    dict[kvp.Key] = requiredItems;

                    if (!randomizedResult.Settings.AddSongs && requiredItems.Count > 0 && requiredItems.All(io => ItemUtils.IsSong(io.io.Item)) && importantItems.All(io => ItemUtils.IsSong(io.io.Item)))
                    {
                        songOnlyRegionCounts[kvp.Key] = requiredItems;
                    }
                }

                for (var i = 0; i < numberOfNonRequiredHints; i++)
                {
                    var regionCounts = nonImportantRegionCounts.AsEnumerable();
                    regionCounts = regionCounts.Concat(songOnlyRegionCounts);
                    regionCounts = regionCounts.Where(kvp => !hintedRegions.Contains(kvp.Key));
                    if (regionCounts.Any())
                    {
                        var            chosen = regionCounts.ToList().Random(random, kvp => itemsInRegions[kvp.Key].Count);
                        RegionHintType regionHintType;
                        if (songOnlyRegionCounts.Remove(chosen.Key))
                        {
                            regionHintType = RegionHintType.OnlyImportantSong;
                        }
                        else
                        {
                            nonImportantRegionCounts.Remove(chosen.Key);
                            regionHintType = RegionHintType.NoneRequired;
                        }

                        competitiveHints.Add((BuildRegionHint(chosen, regionHintType, random), null, new List <GossipQuote>()));
                        competitiveHints.Add((BuildRegionHint(chosen, regionHintType, random), null, new List <GossipQuote>()));

                        hintedRegions.Add(chosen.Key);
                    }
                }

                var chosenClockTownRegions = 0;
                for (var i = 0; i < numberOfRequiredHints; i++)
                {
                    var regionCounts = importantRegionCounts.AsEnumerable();
                    if (chosenClockTownRegions < maxNumberOfClockTownHints)
                    {
                        regionCounts = regionCounts.Concat(clockTownRegionCounts);
                    }
                    if (!regionCounts.Any())
                    {
                        regionCounts = regionCounts.Concat(clockTownRegionCounts);
                    }
                    regionCounts = regionCounts.Where(kvp => !hintedRegions.Contains(kvp.Key));
                    if (regionCounts.Any())
                    {
                        var chosen = regionCounts.ToList().Random(random);
                        var allowedGossipQuotes = chosen.Value
                                                  .Select(io => gossipStoneRequirements.Where(kvp => kvp.Value?.Contains(io.locationForImportance) == false).Select(kvp => kvp.Key))
                                                  .Aggregate((list1, list2) => list1.Intersect(list2))
                                                  .ToList();
                        competitiveHints.Add((BuildRegionHint(chosen, RegionHintType.SomeRequired, random), null, allowedGossipQuotes));
                        competitiveHints.Add((BuildRegionHint(chosen, RegionHintType.SomeRequired, random), null, allowedGossipQuotes));
                        if (clockTownRegionCounts.Remove(chosen.Key))
                        {
                            chosenClockTownRegions++;
                        }
                        else
                        {
                            importantRegionCounts.Remove(chosen.Key);
                        }

                        hintedRegions.Add(chosen.Key);
                    }
                }

                unusedItems.Clear();
            }

            List <MessageEntry> finalHints = new List <MessageEntry>();

            void addHint(GossipQuote gossipQuote, string message)
            {
                var header = MessageHeader.ToArray();

                if (gossipQuote.IsGaroHint())
                {
                    header[0] = 0;
                    header[1] = 1;
                    message   = message.Replace("\xBF", "\x19\xBF");
                }

                finalHints.Add(new MessageEntry()
                {
                    Id      = (ushort)gossipQuote,
                    Message = message,
                    Header  = header
                });
            }

            while (competitiveHints.Any(ch => ch.allowedGossipQuotes.Count > 0))
            {
                var competitiveHint = competitiveHints
                                      .Where(ch => ch.allowedGossipQuotes.Count > 0)
                                      .OrderBy(ch => ch.allowedGossipQuotes.Count)
                                      .ThenBy(ch => random.Next())
                                      .First();

                var gossipQuote       = competitiveHint.allowedGossipQuotes.Random(random);
                var clearHintsEnabled = gossipQuote.IsGaroHint() ? randomizedResult.Settings.ClearGaroHints : randomizedResult.Settings.ClearHints;
                addHint(gossipQuote, clearHintsEnabled && competitiveHint.clearMessage != null ? competitiveHint.clearMessage : competitiveHint.message);
                competitiveHints.Remove(competitiveHint);
                foreach (var ch in competitiveHints)
                {
                    ch.allowedGossipQuotes.Remove(gossipQuote);
                }
            }

            foreach (var gossipQuote in gossipQuotes.OrderBy(gq => random.Next()))
            {
                if (finalHints.Any(me => me.Id == (ushort)gossipQuote))
                {
                    continue;
                }

                string messageText       = null;
                var    isMoonGossipStone = gossipQuote.IsMoonGossipStone();
                var    clearHintsEnabled = gossipQuote.IsGaroHint() ? randomizedResult.Settings.ClearGaroHints : randomizedResult.Settings.ClearHints;
                if (competitiveHints.Any())
                {
                    var competitiveHint = competitiveHints.Random(random);
                    messageText = clearHintsEnabled && competitiveHint.clearMessage != null ? competitiveHint.clearMessage : competitiveHint.message;
                    competitiveHints.Remove(competitiveHint);
                }

                if (messageText == null)
                {
                    var        restrictionAttributes = gossipQuote.GetAttributes <GossipRestrictAttribute>().ToList();
                    ItemObject item       = null;
                    var        forceClear = false;
                    while (item == null)
                    {
                        if (restrictionAttributes.Any() && hintStyle == GossipHintStyle.Relevant)
                        {
                            var chosen        = restrictionAttributes.Random(random);
                            var candidateItem = chosen.Type == GossipRestrictAttribute.RestrictionType.Item
                                ? randomizedResult.ItemList.Single(io => io.ID == (int)chosen.Item)
                                : randomizedResult.ItemList.Single(io => io.NewLocation == chosen.Item);
                            if (unusedItems.Contains(candidateItem))
                            {
                                item       = candidateItem;
                                forceClear = chosen.ForceClear;
                            }
                            else
                            {
                                restrictionAttributes.Remove(chosen);
                            }
                        }
                        else if (unusedItems.Any())
                        {
                            if (hintStyle == GossipHintStyle.Competitive)
                            {
                                item = unusedItems.FirstOrDefault(io => unusedItems.Count(x => x.ID == io.ID) == 1);
                                if (item == null)
                                {
                                    item = unusedItems.Random(random);
                                }
                            }
                            else
                            {
                                item = unusedItems.Random(random);
                                if (ItemUtils.IsJunk(item.Item) && (clearHintsEnabled || random.Next(8) != 0))
                                {
                                    item = null;
                                }
                            }
                        }
                        else
                        {
                            break;
                        }
                    }

                    if (!isMoonGossipStone)
                    {
                        unusedItems.Remove(item);
                    }

                    if (item != null)
                    {
                        (var hint, var clearHint, var combined) = BuildItemHint(
                            item,
                            randomizedResult,
                            hintStyle,
                            itemsToCombineWith,
                            hintableItems,
                            null,
                            random
                            );
                        messageText = hint;
                        if (clearHint != null && clearHintsEnabled)
                        {
                            messageText = clearHint;
                        }
                    }
                }

                if (messageText == null)
                {
                    messageText = Gossip.JunkMessages.Random(random);
                }

                addHint(gossipQuote, messageText);
            }

            return(finalHints);
        }