Ejemplo n.º 1
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);
        }
Ejemplo n.º 2
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);
        }