public void ShuffleTreasures(MT19337 rng) { DirectedGraph<byte> graph = new DirectedGraph<byte>(); var treasureBlob = Get(TreasureOffset, TreasureSize * TreasureCount); var usedIndices = Enumerable.Range(0, TreasureCount).Except(TreasureConditions.NotUsed).ToList(); var usedTreasures = usedIndices.Select(i => treasureBlob[i]).ToList(); bool tofrQuestItem; do { usedTreasures.Shuffle(rng); for (int i = 0; i < usedIndices.Count; i++) { treasureBlob[usedIndices[i]] = usedTreasures[i]; } // ToFR is only exitable using WARP or EXIT, so we don't want these items showing up there. // Especially not the TAIL, as that would make class change impossible. And the CROWN being // here could block a LOT of valuable loot if you don't have a WW or BW. tofrQuestItem = TreasureConditions.ToFR.Contains(treasureBlob.IndexOf((byte)QuestItems.Crown)) || TreasureConditions.ToFR.Contains(treasureBlob.IndexOf((byte)QuestItems.Tail)) || TreasureConditions.ToFR.Contains(treasureBlob.IndexOf((byte)QuestItems.Adamant)); if (tofrQuestItem) { continue; } var blockages = new List<Tuple<byte, int, List<int>>> { Tuple.Create((byte)QuestItems.Crown, treasureBlob.IndexOf((byte)QuestItems.Crown), TreasureConditions.CrownBlocked), Tuple.Create((byte)QuestItems.Tnt, treasureBlob.IndexOf((byte)QuestItems.Tnt), TreasureConditions.TntBlocked), Tuple.Create((byte)QuestItems.Ruby, treasureBlob.IndexOf((byte)QuestItems.Ruby), TreasureConditions.RubyBlocked), Tuple.Create((byte)QuestItems.Floater, treasureBlob.IndexOf((byte)QuestItems.Floater), TreasureConditions.FloaterBlocked), Tuple.Create((byte)QuestItems.Slab, treasureBlob.IndexOf((byte)QuestItems.Slab), TreasureConditions.SlabBlocked) }; graph = new DirectedGraph<byte>(); foreach (var blockage in blockages) { graph.AddNode(blockage.Item1); } foreach (var blocker in blockages) { foreach (var blockee in blockages) { if (blocker.Item3.Contains(blockee.Item2)) { graph.AddEdge(blockee.Item1, blocker.Item1); } } } } while (tofrQuestItem || graph.HasCycles()); Put(TreasureOffset, treasureBlob); }