Beispiel #1
0
        public static readonly List <int> UsedTreasureIndices = Enumerable.Range(0, 256).Except(UnusedTreasureIndices).ToList();        // This maps a compacted list back to the game's array, skipping the unused slots.

        public List <IRewardSource> ShuffleTreasures(MT19337 rng,
                                                     IItemPlacementFlags flags,
                                                     IncentiveData incentivesData,
                                                     ItemShopSlot caravanItemLocation,
                                                     Dictionary <MapLocation, List <MapChange> > mapLocationRequirements)
        {
            var vanillaNPCs = !flags.NPCItems && !flags.NPCFetchItems;

            if (!vanillaNPCs)
            {
                EnableBridgeShipCanalAnywhere();
                EnableNPCsGiveAnyItem();
                // This extends Vampire's routine to set a flag for Sarda, but it also clobers Sarda's routine
                if (!flags.EarlySarda)
                {
                    Put(0x393E1, Blob.FromHex("207F90A51160"));
                }
            }

            var treasureBlob = Get(TreasureOffset, TreasureSize * TreasureCount);
            var treasurePool = UsedTreasureIndices.Select(x => (Item)treasureBlob[x])
                               .Concat(ItemLists.AllNonTreasureChestItems).ToList();

            if (flags.ShardHunt)
            {
                treasurePool = treasurePool.Select(ShardHuntTreasureSelector).ToList();
                int shardsAdded = treasurePool.Count(item => item == Item.Shard);
                Debug.Assert(shardsAdded == TotalOrbsToInsert);
            }

            var placedItems =
                ItemPlacement.PlaceSaneItems(rng,
                                             flags,
                                             incentivesData,
                                             treasurePool,
                                             caravanItemLocation,
                                             mapLocationRequirements);

            if (flags.FreeBridge)
            {
                placedItems = placedItems.Select(x => x.Item != Item.Bridge ? x : ItemPlacement.NewItemPlacement(x, ReplacementItem)).ToList();
            }
            if (flags.FreeAirship)
            {
                placedItems = placedItems.Select(x => x.Item != Item.Floater ? x : ItemPlacement.NewItemPlacement(x, ReplacementItem)).ToList();
            }

            // Output the results to the ROM
            foreach (var item in placedItems.Where(x => !x.IsUnused && x.Address < 0x80000 && (!vanillaNPCs || x is TreasureChest)))
            {
                //Debug.WriteLine(item.SpoilerText);
                item.Put(this);
            }

            MoveShipToRewardSource(placedItems.Find(reward => reward.Item == Item.Ship));
            return(placedItems);
        }
Beispiel #2
0
        public static readonly List <int> UsedTreasureIndices = Enumerable.Range(0, 256).Except(UnusedTreasureIndices).ToList();        // This maps a compacted list back to the game's array, skipping the unused slots.

        public List <IRewardSource> ShuffleTreasures(MT19337 rng,
                                                     IItemPlacementFlags flags,
                                                     IncentiveData incentivesData,
                                                     ItemShopSlot caravanItemLocation,
                                                     OverworldMap overworldMap,
                                                     TeleportShuffle teleporters)
        {
            Dictionary <MapLocation, Tuple <List <MapChange>, AccessRequirement> > fullFloorRequirements = overworldMap.FullLocationRequirements;
            Dictionary <MapLocation, OverworldTeleportIndex> overridenOverworld = overworldMap.OverriddenOverworldLocations;

            var vanillaNPCs = !(flags.NPCItems ?? false) && !(flags.NPCFetchItems ?? false);

            if (!vanillaNPCs)
            {
                EnableBridgeShipCanalAnywhere();
                EnableNPCsGiveAnyItem();
                // This extends Vampire's routine to set a flag for Sarda, but it also clobers Sarda's routine
                if (!(bool)flags.EarlySarda)
                {
                    Put(0x393E1, Blob.FromHex("207F90A51160"));
                }
            }

            var treasureBlob = Get(TreasureOffset, TreasureSize * TreasureCount);
            var treasurePool = UsedTreasureIndices.Select(x => (Item)treasureBlob[x])
                               .Concat(ItemLists.AllNonTreasureChestItems).ToList();

            if (flags.ShardHunt)
            {
                treasurePool = treasurePool.Select(ShardHuntTreasureSelector).ToList();
                int shardsAdded = treasurePool.Count(item => item == Item.Shard);
                Debug.Assert(shardsAdded == TotalOrbsToInsert);
            }

            ItemPlacement placement   = ItemPlacement.Create(flags, incentivesData, treasurePool, caravanItemLocation, overworldMap);
            var           placedItems = placement.PlaceSaneItems(rng);

            // Output the results to the ROM
            foreach (var item in placedItems.Where(x => !x.IsUnused && x.Address < 0x80000 && (!vanillaNPCs || x is TreasureChest)))
            {
                //Debug.WriteLine(item.SpoilerText);
                item.Put(this);
            }
            // Move the ship someplace closer to where it really ends up.
            if (!(flags.FreeShip ?? false))
            {
                MapLocation shipLocation = placedItems.Find(reward => reward.Item == Item.Ship).MapLocation;
                if (overridenOverworld != null && overridenOverworld.TryGetValue(shipLocation, out var overworldIndex))
                {
                    shipLocation = teleporters.OverworldMapLocations.TryGetValue(overworldIndex, out var vanillaShipLocation) ? vanillaShipLocation : shipLocation;
                }
                MoveShipToRewardSource(shipLocation);
            }
            return(placedItems);
        }
Beispiel #3
0
        public static ItemPlacement Create(IItemPlacementFlags flags, IncentiveData incentivesData, List <Item> allTreasures, ItemShopSlot caravanItemLocation, OverworldMap overworldMap)
        {
            ItemPlacement placement;

            placement = new GuidedItemPlacement();

            placement._flags               = flags;
            placement._incentivesData      = incentivesData;
            placement._allTreasures        = allTreasures;
            placement._caravanItemLocation = caravanItemLocation;
            placement._overworldMap        = overworldMap;

            return(placement);
        }
        public static ItemPlacement Create(IItemPlacementFlags flags, IncentiveData incentivesData, List <Item> allTreasures, ItemShopSlot caravanItemLocation, OverworldMap overworldMap, ISanityChecker checker)
        {
            ItemPlacement placement;

            placement = flags.PredictivePlacement && checker is SanityCheckerV2 ? new PredictivePlacement() : new GuidedItemPlacement();

            placement._flags               = flags;
            placement._incentivesData      = incentivesData;
            placement._allTreasures        = allTreasures;
            placement._caravanItemLocation = caravanItemLocation;
            placement._overworldMap        = overworldMap;
            placement._checker             = checker;

            return(placement);
        }
        public ExtSpoiler(FF1Rom _rom, SanityCheckerV2 _checker, ShopData _shopData, ItemNames _itemsText, List <IRewardSource> _itemPlacement, OverworldMap _overworldMap, IncentiveData _incentivesData, GearPermissions _weaponPermissions, GearPermissions _armorPermissions, Flags _flags)
        {
            rom               = _rom;
            checker           = _checker;
            shopData          = _shopData;
            itemsText         = _itemsText;
            itemPlacement     = _itemPlacement;
            overworldMap      = _overworldMap;
            incentivesData    = _incentivesData;
            weaponPermissions = _weaponPermissions;
            armorPermissions  = _armorPermissions;
            flags             = _flags;

            logic       = new SCLogic(rom, checker.Main, itemPlacement, flags, false);
            weapons     = Weapon.LoadAllWeapons(rom, flags).ToList();
            armors      = Armor.LoadAllArmors(rom, flags).ToList();
            magicSpells = rom.GetSpells();
        }
Beispiel #6
0
        public static readonly List <int> UsedTreasureIndices = Enumerable.Range(0, 256).Except(UnusedTreasureIndices).ToList();        // This maps a compacted list back to the game's array, skipping the unused slots.

        public List <IRewardSource> ShuffleTreasures(MT19337 rng,
                                                     ITreasureShuffleFlags flags,
                                                     IncentiveData incentivesData,
                                                     ItemShopSlot caravanItemLocation,
                                                     Dictionary <MapLocation, List <MapChange> > mapLocationRequirements)
        {
            if (flags.NPCItems)
            {
                EnableBridgeShipCanalAnywhere();
                EnableNPCsGiveAnyItem();
                // This extends Vampire's routine to set a flag for Sarda, but it also clobers Sarda's routine
                if (!flags.EarlyRod)
                {
                    Put(0x393E1, Blob.FromHex("207F90A51160"));
                }
            }

            var treasureBlob = Get(TreasureOffset, TreasureSize * TreasureCount);
            var treasurePool = UsedTreasureIndices.Select(x => (Item)treasureBlob[x])
                               .Concat(ItemLists.AllNonTreasureChestItems).ToList();

            if (flags.OrbHunt)
            {
                treasurePool = treasurePool.Select(OrbHuntTreasureSelector).ToList();
                System.Diagnostics.Debug.Assert(treasurePool.Count(item => item == Item.Shard) == TotalOrbsToInsert);
            }

            var placedItems =
                ItemPlacement.PlaceSaneItems(rng,
                                             flags,
                                             incentivesData,
                                             treasurePool,
                                             caravanItemLocation,
                                             mapLocationRequirements);

            // Output the results tothe ROM
            foreach (var item in placedItems.Where(x => !x.IsUnused && x.Address < 0x80000 && !incentivesData.ForcedItemPlacements.Any(y => y.Address == x.Address)))
            {
                //Debug.WriteLine(item.SpoilerText);
                item.Put(this);
            }
            return(placedItems);
        }
Beispiel #7
0
        public static ItemPlacement Create(IItemPlacementFlags flags, IncentiveData incentivesData, List <Item> allTreasures, ItemShopSlot caravanItemLocation, OverworldMap overworldMap)
        {
            ItemPlacement placement;

            if (flags.AllowObsoleteVehicles)
            {
                placement = new RandomItemPlacement();
            }
            else
            {
                placement = new GuidedItemPlacement();
            };

            placement._flags               = flags;
            placement._incentivesData      = incentivesData;
            placement._allTreasures        = allTreasures;
            placement._caravanItemLocation = caravanItemLocation;
            placement._overworldMap        = overworldMap;

            return(placement);
        }
        public static List <IRewardSource> PlaceSaneItems(MT19337 rng,
                                                          IItemPlacementFlags flags,
                                                          IncentiveData incentivesData,
                                                          List <Item> allTreasures,
                                                          ItemShopSlot caravanItemLocation,
                                                          Dictionary <MapLocation, List <MapChange> > mapLocationRequirements)
        {
            long sanityCounter = 0;
            List <IRewardSource> placedItems;

            var canoeObsoletesBridge  = flags.MapCanalBridge && flags.MapConeriaDwarves;
            var canoeObsoletesShip    = flags.MapCanalBridge ? (flags.MapConeriaDwarves || flags.MapVolcanoIceRiver) : (flags.MapConeriaDwarves && flags.MapVolcanoIceRiver);
            var incentiveLocationPool = incentivesData.IncentiveLocations.ToList();
            var incentivePool         = incentivesData.IncentiveItems.Where(x => allTreasures.Contains(x)).ToList();
            var forcedItems           = incentivesData.ForcedItemPlacements.ToList();
            var keyLocations          = incentivesData.KeyLocations.ToList();
            var canoeLocations        = incentivesData.CanoeLocations.ToList();
            var bridgeLocations       = incentivesData.BridgeLocations.ToList();
            var shipLocations         = incentivesData.ShipLocations.ToList();
            var itemLocationPool      = incentivesData.AllValidItemLocations.ToList();
            var startingMapLocations  = mapLocationRequirements.Where(x => x.Value.Any(y => y == MapChange.None)).Select(x => x.Key);
            var earlyMapLocations     = mapLocationRequirements.Where(x => x.Value.Any(y => MapChange.Bridge.HasFlag(y))).Select(x => x.Key);

            var unincentivizedQuestItems =
                ItemLists.AllQuestItems
                .Where(x => !incentivePool.Contains(x) &&
                       allTreasures.Contains(x) &&
                       !forcedItems.Any(y => y.Item == x))
                .ToList();

            var treasurePool = allTreasures.ToList();

            foreach (var incentive in incentivePool)
            {
                treasurePool.Remove(incentive);
            }
            foreach (var placement in forcedItems)
            {
                treasurePool.Remove(placement.Item);
            }
            foreach (var questItem in unincentivizedQuestItems)
            {
                treasurePool.Remove(questItem);
            }

            var itemShopItem = Item.Bottle;

            while (treasurePool.Remove(Item.Shard))
            {
                unincentivizedQuestItems.Add(Item.Shard);
            }
            do
            {
                sanityCounter++;
                if (sanityCounter > 10000)
                {
                    throw new InvalidOperationException("Invalid flag set");
                }
                // 1. (Re)Initialize lists inside of loop
                placedItems = forcedItems.ToList();
                var incentives    = incentivePool.ToList();
                var nonincentives = unincentivizedQuestItems.ToList();
                incentives.Shuffle(rng);
                nonincentives.Shuffle(rng);

                if (flags.NPCItems)
                {
                    // 2. Place caravan item first because among incentive locations it has the smallest set of possible items
                    var validShopIncentives = incentives
                                              .Where(x => x > Item.None && x <= Item.Soft)
                                              .ToList();
                    if (validShopIncentives.Any() && incentiveLocationPool.Any(x => x.Address == ItemLocations.CaravanItemShop1.Address))
                    {
                        itemShopItem = validShopIncentives.PickRandom(rng);
                        incentives.Remove(itemShopItem);
                    }
                    else
                    {
                        itemShopItem = treasurePool.Concat(nonincentives).Where(x => x > Item.None && x <= Item.Soft).ToList().PickRandom(rng);
                        nonincentives.Remove(itemShopItem);
                    }
                    placedItems.Add(new ItemShopSlot(caravanItemLocation, itemShopItem));

                    // 3. Place key and canoe next because among incentive locations these have the greatest initial impact
                    if (incentives.Remove(Item.Key) || nonincentives.Remove(Item.Key))
                    {
                        placedItems.Add(NewItemPlacement(keyLocations.PickRandom(rng), Item.Key));
                    }
                    if (incentives.Remove(Item.Canoe) || nonincentives.Remove(Item.Canoe))
                    {
                        placedItems.Add(NewItemPlacement(canoeLocations.Where(x => !placedItems.Any(y => y.Address == x.Address)).ToList().PickRandom(rng), Item.Canoe));
                    }

                    var startingCanoeAvailable = placedItems.Any(x => x.Item == Item.Canoe && startingMapLocations.Contains(x.MapLocation));
                    var earlyCanoeAvailable    = placedItems.Any(x => x.Item == Item.Canoe && earlyMapLocations.Contains(x.MapLocation));
                    var earlyKeyAvailable      = placedItems.Any(x => x.Item == Item.Key && earlyMapLocations.Contains(x.MapLocation));

                    // 4. Place Bridge and Ship next since the valid location lists are so small, unless canoe is available and map edits are applied
                    if (!earlyCanoeAvailable || !canoeObsoletesShip)
                    {
                        var remainingShipLocations =
                            shipLocations
                            .Where(x => !placedItems.Any(y => y.Address == x.Address) &&
                                   (earlyKeyAvailable || !x.AccessRequirement.HasFlag(AccessRequirement.Key)))
                            .ToList();
                        if (!remainingShipLocations.Any())
                        {
                            continue;
                        }
                        if (incentives.Remove(Item.Ship) && remainingShipLocations.Any(x => incentiveLocationPool.Any(y => y.Address == x.Address)))
                        {
                            remainingShipLocations =
                                remainingShipLocations
                                .Where(x => incentiveLocationPool.Any(y => y.Address == x.Address))
                                .ToList();
                        }
                        nonincentives.Remove(Item.Ship);
                        placedItems.Add(NewItemPlacement(remainingShipLocations.PickRandom(rng), Item.Ship));
                    }

                    var startingShipAvailable = placedItems.Any(x => x.Item == Item.Ship && startingMapLocations.Contains(x.MapLocation));

                    if (!(startingCanoeAvailable && canoeObsoletesBridge) && !startingShipAvailable)
                    {
                        var startingKeyAvailable = earlyKeyAvailable && placedItems.Any(x => x.Item == Item.Key && startingMapLocations.Contains(x.MapLocation));

                        var remainingBridgeLocations =
                            bridgeLocations
                            .Where(x => !placedItems.Any(y => y.Address == x.Address) &&
                                   (startingKeyAvailable || !x.AccessRequirement.HasFlag(AccessRequirement.Key)))
                            .ToList();
                        if (!remainingBridgeLocations.Any())
                        {
                            continue;
                        }
                        if (incentives.Remove(Item.Bridge) && remainingBridgeLocations.Any(x => incentiveLocationPool.Any(y => y.Address == x.Address)))
                        {
                            remainingBridgeLocations =
                                remainingBridgeLocations
                                .Where(x => incentiveLocationPool.Any(y => y.Address == x.Address))
                                .ToList();
                        }
                        nonincentives.Remove(Item.Bridge);
                        placedItems.Add(NewItemPlacement(remainingBridgeLocations.PickRandom(rng), Item.Bridge));
                    }
                }

                // 5. Then place all incentive locations that don't have special logic
                var incentiveLocations = incentiveLocationPool.Where(x => !placedItems.Any(y => y.Address == x.Address) && x.Address != ItemLocations.CaravanItemShop1.Address).ToList();
                incentiveLocations.Shuffle(rng);
                foreach (var incentiveLocation in incentiveLocations)
                {
                    if (!incentives.Any())
                    {
                        break;
                    }
                    placedItems.Add(NewItemPlacement(incentiveLocation, incentives.SpliceRandom(rng)));
                }

                // 6. Then place remanining incentive items and unincentivized quest items in any other chest before ToFR
                var leftoverItems = incentives.Concat(nonincentives).ToList();
                leftoverItems.Remove(itemShopItem);
                leftoverItems.Shuffle(rng);
                var leftoverItemLocations =
                    itemLocationPool
                    .Where(x => !ItemLocations.ToFR.Any(y => y.Address == x.Address) &&
                           !x.IsUnused && !placedItems.Any(y => y.Address == x.Address))
                    .ToList();
                foreach (var leftoverItem in leftoverItems)
                {
                    placedItems.Add(NewItemPlacement(leftoverItemLocations.SpliceRandom(rng), leftoverItem));
                }

                // 7. Check sanity and loop if needed
            } while (!CheckSanity(placedItems, mapLocationRequirements, flags));

            // 8. Place all remaining unincentivized treasures or incentivized non-quest items that weren't placed
            var i = 0;

            itemLocationPool =
                itemLocationPool
                .Where(x => !x.IsUnused && !placedItems.Any(y => y.Address == x.Address))
                .ToList();
            foreach (var placedItem in placedItems)
            {
                incentivePool.Remove(placedItem.Item);
            }
            foreach (var unplacedIncentive in incentivePool)
            {
                treasurePool.Add(unplacedIncentive);
            }
            treasurePool.Shuffle(rng);
            itemLocationPool.Shuffle(rng);
            foreach (var remainingTreasure in itemLocationPool)
            {
                placedItems.Add(NewItemPlacement(remainingTreasure, treasurePool[i]));
                i++;
            }

            //Debug.WriteLine($"Sanity Check Fails: {sanityCounter}");
            return(placedItems);
        }
Beispiel #9
0
        public void Randomize(Blob seed, Flags flags, Preferences preferences)
        {
            MT19337 rng;

            using (SHA256 hasher = SHA256.Create())
            {
                Blob FlagsBlob    = Encoding.UTF8.GetBytes(Flags.EncodeFlagsText(flags));
                Blob SeedAndFlags = Blob.Concat(new Blob[] { FlagsBlob, seed });
                Blob hash         = hasher.ComputeHash(SeedAndFlags);
                rng = new MT19337(BitConverter.ToUInt32(hash, 0));
            }
            if (flags.TournamentSafe)
            {
                AssureSafe();
            }

            UpgradeToMMC3();
            MakeSpace();
            Bank1E();
            Bank1B();
            EasterEggs();
            DynamicWindowColor(preferences.MenuColor);
            PermanentCaravan();
            ShiftEarthOrbDown();
            CastableItemTargeting();
            FixEnemyPalettes();       // fixes a bug in the original game's programming that causes third enemy slot's palette to render incorrectly
            FixWarpBug();             // The warp bug must be fixed for magic level shuffle and spellcrafter
            SeparateUnrunnables();
            var talkroutines = new TalkRoutines();
            var npcdata      = new NPCdata(this);

            UpdateDialogs(npcdata);

            if (flags.TournamentSafe)
            {
                Put(0x3FFE3, Blob.FromHex("66696E616C2066616E74617379"));
            }

            flags = Flags.ConvertAllTriState(flags, rng);

            TeleportShuffle teleporters      = new TeleportShuffle();
            var             palettes         = OverworldMap.GeneratePalettes(Get(OverworldMap.MapPaletteOffset, MapCount * OverworldMap.MapPaletteSize).Chunk(OverworldMap.MapPaletteSize));
            var             overworldMap     = new OverworldMap(this, flags, palettes, teleporters);
            var             maps             = ReadMaps();
            var             shopItemLocation = ItemLocations.CaravanItemShop1;
            var             oldItemNames     = ReadText(ItemTextPointerOffset, ItemTextPointerBase, ItemTextPointerCount);

            if (flags.EFGWaterfall || flags.EFGEarth1 || flags.EFGEarth2)
            {
                MapRequirements      reqs;
                MapGeneratorStrategy strategy;
                MapGenerator         generator = new MapGenerator();
                if (flags.EFGWaterfall)
                {
                    reqs = new MapRequirements
                    {
                        MapId = MapId.Waterfall,
                        Rom   = this,
                    };
                    strategy = MapGeneratorStrategy.WaterfallClone;
                    CompleteMap waterfall = generator.Generate(rng, strategy, reqs);

                    // Should add more into the reqs so that this can be done inside the generator.
                    teleporters.Waterfall.SetEntrance(waterfall.Entrance);
                    overworldMap.PutOverworldTeleport(OverworldTeleportIndex.Waterfall, teleporters.Waterfall);
                    maps[(int)MapId.Waterfall] = waterfall.Map;
                }

                if (flags.EFGEarth1)
                {
                    reqs = new MapRequirements
                    {
                        MapId = MapId.EarthCaveB1,
                        Rom   = this,
                    };

                    strategy = MapGeneratorStrategy.Square;
                    var earthB1 = generator.Generate(rng, strategy, reqs);

                    // Should add more into the reqs so that this can be done inside the generator.
                    teleporters.EarthCave1.SetEntrance(earthB1.Entrance);
                    overworldMap.PutOverworldTeleport(OverworldTeleportIndex.EarthCave1, teleporters.EarthCave1);
                    maps[(int)MapId.EarthCaveB1] = earthB1.Map;
                }
                if (flags.EFGEarth2)
                {
                    reqs = new MapRequirements
                    {
                        MapId = MapId.EarthCaveB2,
                        Rom   = this,
                    };

                    strategy = MapGeneratorStrategy.Square;
                    var earthB2 = generator.Generate(rng, strategy, reqs);

                    // Should add more into the reqs so that this can be done inside the generator.
                    teleporters.EarthCave2.SetEntrance(earthB2.Entrance);
                    overworldMap.PutStandardTeleport(TeleportIndex.EarthCave2, teleporters.EarthCave2, OverworldTeleportIndex.EarthCave1);
                    maps[(int)MapId.EarthCaveB2] = earthB2.Map;
                }
            }

            var flippedMaps = new List <MapId>();

            if ((bool)flags.FlipDungeons)
            {
                flippedMaps = HorizontalFlipDungeons(rng, maps, teleporters, overworldMap);
            }

            if ((bool)flags.RandomizeFormationEnemizer)
            {
                DoEnemizer(rng, (bool)flags.RandomizeEnemizer, (bool)flags.RandomizeFormationEnemizer, flags.EnemizerDontMakeNewScripts);
            }

            if (preferences.ModernBattlefield)
            {
                EnableModernBattlefield();
            }

            if ((bool)flags.TitansTrove)
            {
                EnableTitansTrove(maps);
            }

            if ((bool)flags.LefeinShops)
            {
                EnableLefeinShops(maps);
            }

            // This has to be done before we shuffle spell levels.
            if (flags.SpellBugs)
            {
                FixSpellBugs();
            }

            //must be done before spells get shuffled around otherwise we'd be changing a spell that isnt lock
            if (flags.LockMode != LockHitMode.Vanilla)
            {
                ChangeLockMode(flags.LockMode);
            }

            if (flags.EnemySpellsTargetingAllies)
            {
                FixEnemyAOESpells();
            }

            if (flags.AllSpellLevelsForKnightNinja)
            {
                KnightNinjaChargesForAllLevels();
            }

            if (flags.BuffHealingSpells)
            {
                BuffHealingSpells();
            }

            UpdateMagicAutohitThreshold(rng, flags.MagicAutohitThreshold);

            if ((bool)flags.GenerateNewSpellbook)
            {
                CraftNewSpellbook(rng, (bool)flags.SpellcrafterMixSpells, flags.LockMode, (bool)flags.MagicLevels, (bool)flags.SpellcrafterRetainPermissions);
            }

            if ((bool)flags.MagisizeWeapons)
            {
                MagisizeWeapons(rng, (bool)flags.MagisizeWeaponsBalanced);
            }

            if ((bool)flags.ItemMagic)
            {
                ShuffleItemMagic(rng, (bool)flags.BalancedItemMagicShuffle);
            }

            if ((bool)flags.GuaranteedRuseItem)
            {
                CraftRuseItem();
            }

            if ((bool)flags.ShortToFR)
            {
                ShortenToFR(maps, (bool)flags.PreserveFiendRefights, (bool)flags.PreserveAllFiendRefights, rng);
            }

            if (((bool)flags.Treasures) && flags.ShardHunt && !flags.FreeOrbs)
            {
                EnableShardHunt(rng, talkroutines, flags.ShardCount);
            }

            if ((bool)flags.TransformFinalFormation && !flags.SpookyFlag)
            {
                TransformFinalFormation((FinalFormation)rng.Between(0, Enum.GetValues(typeof(FinalFormation)).Length - 1), flags.EvadeCap);
            }

            var maxRetries = 8;

            for (var i = 0; i < maxRetries; i++)
            {
                try
                {
                    overworldMap = new OverworldMap(this, flags, palettes, teleporters);
                    if (((bool)flags.Entrances || (bool)flags.Floors || (bool)flags.Towns) && ((bool)flags.Treasures) && ((bool)flags.NPCItems))
                    {
                        overworldMap.ShuffleEntrancesAndFloors(rng, flags);

                        // Disable the Princess Warp back to Castle Coneria
                        if ((bool)flags.Entrances || (bool)flags.Floors)
                        {
                            talkroutines.ReplaceChunk(newTalkRoutines.Talk_Princess1, Blob.FromHex("20CC90"), Blob.FromHex("EAEAEA"));
                        }
                    }

                    if ((bool)flags.Treasures && (bool)flags.ShuffleObjectiveNPCs)
                    {
                        overworldMap.ShuffleObjectiveNPCs(rng);
                    }

                    IncentiveData incentivesData = new IncentiveData(rng, flags, overworldMap, shopItemLocation);

                    if (((bool)flags.Shops))
                    {
                        var excludeItemsFromRandomShops = new List <Item>();
                        if ((bool)flags.Treasures)
                        {
                            excludeItemsFromRandomShops = incentivesData.ForcedItemPlacements.Select(x => x.Item).Concat(incentivesData.IncentiveItems).ToList();
                        }

                        if (!((bool)flags.RandomWaresIncludesSpecialGear))
                        {
                            excludeItemsFromRandomShops.AddRange(ItemLists.SpecialGear);
                            if ((bool)flags.GuaranteedRuseItem)
                            {
                                excludeItemsFromRandomShops.Add(Item.PowerRod);
                            }
                        }

                        if ((bool)flags.NoMasamune)
                        {
                            excludeItemsFromRandomShops.Add(Item.Masamune);
                        }

                        shopItemLocation = ShuffleShops(rng, (bool)flags.ImmediatePureAndSoftRequired, ((bool)flags.RandomWares), excludeItemsFromRandomShops, flags.WorldWealth);
                        incentivesData   = new IncentiveData(rng, flags, overworldMap, shopItemLocation);
                    }

                    if ((bool)flags.Treasures)
                    {
                        generatedPlacement = ShuffleTreasures(rng, flags, incentivesData, shopItemLocation, overworldMap, teleporters);
                    }
                    break;
                }
                catch (InsaneException e)
                {
                    Console.WriteLine(e.Message);
                    if (maxRetries > (i + 1))
                    {
                        continue;
                    }
                    throw new InvalidOperationException(e.Message);
                }
            }

            // Change Astos routine so item isn't lost in wall of text
            if ((bool)flags.NPCItems || (bool)flags.NPCFetchItems || (bool)flags.ShuffleAstos)
            {
                talkroutines.Replace(newTalkRoutines.Talk_Astos, Blob.FromHex("A674F005BD2060F027A5738561202096B020A572203D96A575200096A476207F90207392A5611820109F201896A9F060A57060"));
            }

            npcdata.UpdateItemPlacement(generatedPlacement);

            if ((bool)flags.MagicShopLocs)
            {
                ShuffleMagicLocations(rng);
            }

            if (((bool)flags.MagicShops))
            {
                ShuffleMagicShops(rng);
            }

            if (((bool)flags.MagicLevels))
            {
                ShuffleMagicLevels(rng, ((bool)flags.MagicPermissions), (bool)flags.MagicLevelsTiered, (bool)flags.MagicLevelsMixed, (bool)!flags.GenerateNewSpellbook);
            }

            new StartingInventory(rng, flags, this).SetStartingInventory();

            /*
             * if (flags.WeaponPermissions)
             * {
             *      ShuffleWeaponPermissions(rng);
             * }
             *
             * if (flags.ArmorPermissions)
             * {
             *      ShuffleArmorPermissions(rng);
             * }
             */

            if (flags.SaveGameWhenGameOver)
            {
                EnableSaveOnDeath(flags);
            }

            // Ordered before RNG shuffle. In the event that both flags are on, RNG shuffle depends on this.
            if (((bool)flags.FixMissingBattleRngEntry))
            {
                FixMissingBattleRngEntry();
            }

            if (((bool)flags.Rng))
            {
                ShuffleRng(rng);
            }

            if (((bool)flags.EnemyScripts))
            {
                ShuffleEnemyScripts(rng, (bool)flags.AllowUnsafePirates, (bool)!flags.BossScriptsOnly, ((bool)flags.EnemySkillsSpellsTiered || (bool)flags.ScaryImps), (bool)flags.ScaryImps);
            }

            if (((bool)flags.EnemySkillsSpells))
            {
                if ((bool)flags.EnemySkillsSpellsTiered && (bool)!flags.BossSkillsOnly)
                {
                    GenerateBalancedEnemyScripts(rng, (bool)flags.SwolePirates);
                    ShuffleEnemySkillsSpells(rng, false);
                }
                else
                {
                    ShuffleEnemySkillsSpells(rng, (bool)!flags.BossSkillsOnly);
                }
            }

            if (((bool)flags.EnemyStatusAttacks))
            {
                if (((bool)flags.RandomStatusAttacks))
                {
                    RandomEnemyStatusAttacks(rng, (bool)flags.AllowUnsafePirates, (bool)flags.DisableStunTouch);
                }
                else
                {
                    ShuffleEnemyStatusAttacks(rng, (bool)flags.AllowUnsafePirates);
                }
            }

            if (flags.Runnability == Runnability.Random)
            {
                flags.Runnability = (Runnability)Rng.Between(rng, 0, 3);
            }

            if (flags.Runnability == Runnability.AllRunnable)
            {
                CompletelyRunnable();
            }
            else if (flags.Runnability == Runnability.AllUnrunnable)
            {
                CompletelyUnrunnable();
            }
            else if (flags.Runnability == Runnability.Shuffle)
            {
                ShuffleUnrunnable(rng);
            }

            // Always on to supply the correct changes for WaitWhenUnrunnable
            AllowStrikeFirstAndSurprise(flags.WaitWhenUnrunnable, (bool)flags.UnrunnablesStrikeFirstAndSurprise);

            if (((bool)flags.EnemyFormationsSurprise))
            {
                ShuffleSurpriseBonus(rng);
            }

            // Put this before other encounter / trap tile edits.
            if ((bool)flags.AllowUnsafeMelmond)
            {
                EnableMelmondGhetto(flags.EnemizerEnabled);
            }

            // After unrunnable shuffle and before formation shuffle. Perfect!
            if (flags.WarMECHMode != WarMECHMode.Vanilla)
            {
                WarMECHNpc(flags.WarMECHMode, npcdata, rng, maps);
            }

            if (flags.WarMECHMode == WarMECHMode.Unleashed)
            {
                UnleashWarMECH();
            }

            if ((bool)flags.ClassAsNpcFiends || (bool)flags.ClassAsNpcKeyNPC)
            {
                ClassAsNPC(flags, talkroutines, npcdata, flippedMaps, rng);
            }

            if ((bool)flags.FiendShuffle)
            {
                FiendShuffle(rng);
            }

            if (flags.FormationShuffleMode != FormationShuffleMode.None && !flags.EnemizerEnabled)
            {
                ShuffleEnemyFormations(rng, flags.FormationShuffleMode);
            }

            if ((bool)flags.RemoveTrapTiles)
            {
                RemoveTrapTiles(flags.EnemizerEnabled);
            }

            if (((bool)flags.EnemyTrapTiles) && !flags.EnemizerEnabled)
            {
                ShuffleTrapTiles(rng, ((bool)flags.RandomTrapFormations));
            }

            if ((bool)flags.OrdealsPillars)
            {
                ShuffleOrdeals(rng, maps);
            }

            if (flags.SkyCastle4FMazeMode == SkyCastle4FMazeMode.Maze)
            {
                DoSkyCastle4FMaze(rng, maps);
            }
            else if (flags.SkyCastle4FMazeMode == SkyCastle4FMazeMode.Teleporters)
            {
                ShuffleSkyCastle4F(rng, maps);
            }

            if ((bool)flags.ConfusedOldMen)
            {
                EnableConfusedOldMen(rng);
            }

            if ((bool)flags.EarlyOrdeals)
            {
                EnableEarlyOrdeals();
            }

            if (flags.ChaosRush)
            {
                EnableChaosRush();
            }
            if ((bool)flags.EarlyKing)
            {
                EnableEarlyKing(npcdata);
            }

            if ((bool)flags.EarlySarda)
            {
                EnableEarlySarda(npcdata);
            }

            if ((bool)flags.EarlySage)
            {
                EnableEarlySage(npcdata);
            }

            if ((bool)flags.FreeBridge)
            {
                EnableFreeBridge();
            }

            if ((bool)flags.FreeAirship)
            {
                EnableFreeAirship();
            }

            if ((bool)flags.FreeShip)
            {
                EnableFreeShip();
            }

            if (flags.FreeOrbs)
            {
                EnableFreeOrbs();
            }

            if ((bool)flags.FreeCanal)
            {
                EnableFreeCanal((bool)flags.NPCItems, npcdata);
            }

            if ((bool)flags.FreeCanoe)
            {
                EnableFreeCanoe();
            }

            if ((bool)flags.FreeLute)
            {
                EnableFreeLute();
            }

            if ((bool)flags.FreeTail && !(bool)flags.NoTail)
            {
                EnableFreeTail();
            }

            if (flags.NoPartyShuffle)
            {
                DisablePartyShuffle();
            }

            if (flags.SpeedHacks)
            {
                EnableSpeedHacks();
            }

            if (flags.IdentifyTreasures)
            {
                EnableIdentifyTreasures();
            }

            if (flags.Dash)
            {
                EnableDash();
            }

            if (flags.BuyTen)
            {
                EnableBuyQuantity();
            }
            else if (flags.BuyTenOld)
            {
                EnableBuyTen();
            }

            if (flags.WaitWhenUnrunnable)
            {
                ChangeUnrunnableRunToWait();
            }

            if (flags.SpeedHacks && flags.EnableCritNumberDisplay)
            {
                EnableCritNumberDisplay();
            }

            if (flags.BattleMagicMenuWrapAround)
            {
                BattleMagicMenuWrapAround();
            }

            if (flags.NPCSwatter)
            {
                EnableNPCSwatter(npcdata);
            }

            if (flags.EasyMode)
            {
                EnableEasyMode();
            }

            if ((bool)flags.TrappedChests || (bool)flags.TCMasaGuardian || (bool)flags.TrappedShards)
            {
                MonsterInABox(rng, flags);
            }

            if (flags.HouseMPRestoration || flags.HousesFillHp)
            {
                FixHouse(flags.HouseMPRestoration, flags.HousesFillHp);
            }

            if (flags.BBCritRate)
            {
                DontDoubleBBCritRates();
            }

            if (flags.WeaponCritRate)
            {
                DoubleWeaponCritRates();
            }

            //needs to go after item magic, moved after double weapon crit to have more control over the actual number of crit gained.
            if ((bool)flags.RandomWeaponBonus)
            {
                RandomWeaponBonus(rng, flags.RandomWeaponBonusLow, flags.RandomWeaponBonusHigh, (bool)flags.RandomWeaponBonusExcludeMasa);
            }

            if ((bool)flags.RandomArmorBonus)
            {
                RandomArmorBonus(rng, flags.RandomArmorBonusLow, flags.RandomArmorBonusHigh);
            }

            if (flags.WeaponBonuses)
            {
                IncreaseWeaponBonus(flags.WeaponTypeBonusValue);
            }

            if (flags.WeaponStats)
            {
                FixWeaponStats();
            }

            if (flags.ChanceToRun)
            {
                FixChanceToRun();
            }

            if (flags.EnemyStatusAttackBug)
            {
                FixEnemyStatusAttackBug();
            }

            if (flags.BlackBeltAbsorb)
            {
                FixBBAbsorbBug();
            }

            if (flags.MDefMode != MDEFGrowthMode.None)
            {
                MDefChanges(flags.MDefMode);
            }

            if (flags.ThiefHitRate)
            {
                ThiefHitRate();
            }

            if (flags.ImproveTurnOrderRandomization)
            {
                ImproveTurnOrderRandomization(rng);
            }

            if (flags.FixHitChanceCap)
            {
                FixHitChanceCap();
            }

            if (flags.EnemyElementalResistancesBug)
            {
                FixEnemyElementalResistances();
            }

            if (preferences.FunEnemyNames && !flags.EnemizerEnabled)
            {
                FunEnemyNames(preferences.TeamSteak);
            }

            var itemText = ReadText(ItemTextPointerOffset, ItemTextPointerBase, ItemTextPointerCount);

            itemText[(int)Item.Ribbon] = itemText[(int)Item.Ribbon].Remove(7);

            if ((bool)flags.HintsVillage || (bool)flags.HintsDungeon)
            {
                if ((bool)flags.HintsDungeon)
                {
                    SetDungeonNPC(flippedMaps, rng);
                }

                NPCHints(rng, npcdata, flags, overworldMap);
            }

            if (flags.Etherizer && !flags.HouseMPRestoration && !flags.HousesFillHp)
            {
                Etherizer();
                itemText[(int)Item.Tent]  = "ETHR@p";
                itemText[(int)Item.Cabin] = "DRY@p ";
                itemText[(int)Item.House] = "XETH@p";
            }

            ExpGoldBoost(flags.ExpBonus, flags.ExpMultiplier);
            ScalePrices(flags, itemText, rng, ((bool)flags.ClampMinimumPriceScale), shopItemLocation);
            ScaleEncounterRate(flags.EncounterRate / 30.0, flags.DungeonEncounterRate / 30.0);

            overworldMap.ApplyMapEdits();
            WriteMaps(maps);

            WriteText(itemText, ItemTextPointerOffset, ItemTextPointerBase, ItemTextOffset, UnusedGoldItems);

            if ((bool)flags.SwolePirates)
            {
                EnableSwolePirates();
            }

            if (flags.EnemyScaleStatsHigh != 100 || flags.EnemyScaleStatsLow != 100 || ((bool)flags.SeparateEnemyHPScaling && (flags.EnemyScaleHpLow != 100 || flags.EnemyScaleHpHigh != 100)))
            {
                ScaleEnemyStats(rng, flags);
            }

            if (flags.BossScaleStatsHigh != 100 || flags.BossScaleStatsLow != 100 || ((bool)flags.SeparateBossHPScaling && (flags.BossScaleHpLow != 100 || flags.BossScaleHpHigh != 100)))
            {
                ScaleBossStats(rng, flags);
            }

            PartyComposition(rng, flags, preferences);

            if (((bool)flags.RecruitmentMode))
            {
                PubReplaceClinic(rng, flags);
            }

            if ((bool)flags.ChangeMaxMP)
            {
                SetMPMax(flags.RedMageMaxMP, flags.WhiteMageMaxMP, flags.BlackMageMaxMP, flags.KnightMaxMP, flags.NinjaMaxMP);
            }

            if ((bool)flags.ShuffleAstos)
            {
                ShuffleAstos(flags, npcdata, talkroutines, rng);
            }

            if ((bool)flags.EnablePoolParty)
            {
                EnablePoolParty(flags, rng);
            }

            if ((bool)flags.MapCanalBridge)
            {
                EnableCanalBridge();
            }

            if (flags.NoDanMode)
            {
                NoDanMode();
            }

            SetProgressiveScaleMode(flags);

            if ((bool)flags.RandomizeClass)
            {
                RandomizeClass(rng, flags, oldItemNames);
            }

            if ((bool)flags.EnableRandomPromotions)
            {
                EnableRandomPromotions(flags, rng);
            }

            if (flags.DisableTentSaving)
            {
                CannotSaveOnOverworld();
            }

            if (flags.DisableInnSaving)
            {
                CannotSaveAtInns();
            }

            if (flags.PacifistMode && !flags.SpookyFlag)
            {
                PacifistEnd(talkroutines, npcdata, (bool)flags.EnemyTrapTiles || flags.EnemizerEnabled);
            }

            if (flags.ShopInfo)
            {
                ShopUpgrade();
            }

            if (flags.SpookyFlag)
            {
                Spooky(talkroutines, npcdata, rng, flags);
            }

            if (flags.InventoryAutosort && !(preferences.RenounceAutosort))
            {
                EnableInventoryAutosort();
            }

            // We have to do "fun" stuff last because it alters the RNG state.
            // Back up Rng so that fun flags are uniform when different ones are selected
            uint funRngSeed = rng.Next();

            RollCredits(rng);

            if (preferences.DisableDamageTileFlicker)
            {
                DisableDamageTileFlicker();
            }

            if (preferences.ThirdBattlePalette)
            {
                UseVariablePaletteForCursorAndStone();
            }

            if (preferences.PaletteSwap && !flags.EnemizerEnabled)
            {
                rng = new MT19337(funRngSeed);
                PaletteSwap(rng);
            }

            if (preferences.TeamSteak && !(bool)flags.RandomizeEnemizer)
            {
                TeamSteak();
            }

            if (preferences.ChangeLute)
            {
                rng = new MT19337(funRngSeed);
                ChangeLute(rng);
            }

            rng = new MT19337(funRngSeed);

            HurrayDwarfFate(preferences.HurrayDwarfFate, npcdata, rng);

            if (preferences.Music != MusicShuffle.None)
            {
                rng = new MT19337(funRngSeed);
                ShuffleMusic(preferences.Music, rng);
            }

            if (preferences.DisableSpellCastFlash)
            {
                DisableSpellCastScreenFlash();
            }

            npcdata.WriteNPCdata(this);
            talkroutines.WriteRoutines(this);
            talkroutines.UpdateNPCRoutines(this, npcdata);

            WriteSeedAndFlags(seed.ToHex(), Flags.EncodeFlagsText(flags));
            ExtraTrackingAndInitCode(flags);
        }
Beispiel #10
0
        public void Randomize(Blob seed, Flags flags, Preferences preferences)
        {
            var rng = new MT19337(BitConverter.ToUInt32(seed, 0));

            // Spoilers => different rng immediately
            if (flags.Spoilers)
            {
                rng = new MT19337(rng.Next());
            }

            UpgradeToMMC3();
            MakeSpace();
            Bank1E();
            Bank1B();
            EasterEggs();
            DynamicWindowColor(preferences.MenuColor);
            PermanentCaravan();
            ShiftEarthOrbDown();
            CastableItemTargeting();
            flags = Flags.ConvertAllTriState(flags, rng);


            TeleportShuffle teleporters      = new TeleportShuffle();
            var             palettes         = OverworldMap.GeneratePalettes(Get(OverworldMap.MapPaletteOffset, MapCount * OverworldMap.MapPaletteSize).Chunk(OverworldMap.MapPaletteSize));
            var             overworldMap     = new OverworldMap(this, flags, palettes, teleporters);
            var             maps             = ReadMaps();
            var             shopItemLocation = ItemLocations.CaravanItemShop1;

#if DEBUG
            if (flags.ExperimentalFloorGeneration)
            {
                MapRequirements      reqs;
                MapGeneratorStrategy strategy;
                MapGenerator         generator = new MapGenerator();
                if (flags.EFGWaterfall)
                {
                    reqs = new MapRequirements
                    {
                        MapId = MapId.Waterfall,
                        Rom   = this,
                    };
                    strategy = MapGeneratorStrategy.WaterfallClone;
                    CompleteMap waterfall = generator.Generate(rng, strategy, reqs);

                    // Should add more into the reqs so that this can be done inside the generator.
                    teleporters.Waterfall.SetEntrance(waterfall.Entrance);
                    overworldMap.PutOverworldTeleport(OverworldTeleportIndex.Waterfall, teleporters.Waterfall);
                    maps[(int)MapId.Waterfall] = waterfall.Map;
                }

                if (flags.EFGEarth1)
                {
                    reqs = new MapRequirements
                    {
                        MapId = MapId.EarthCaveB1,
                        Rom   = this,
                    };

                    strategy = MapGeneratorStrategy.Square;
                    var earthB1 = generator.Generate(rng, strategy, reqs);

                    // Should add more into the reqs so that this can be done inside the generator.
                    teleporters.EarthCave1.SetEntrance(earthB1.Entrance);
                    overworldMap.PutOverworldTeleport(OverworldTeleportIndex.EarthCave1, teleporters.EarthCave1);
                    maps[(int)MapId.EarthCaveB1] = earthB1.Map;
                }
                if (flags.EFGEarth2)
                {
                    reqs = new MapRequirements
                    {
                        MapId = MapId.EarthCaveB2,
                        Rom   = this,
                    };

                    strategy = MapGeneratorStrategy.Square;
                    var earthB2 = generator.Generate(rng, strategy, reqs);

                    // Should add more into the reqs so that this can be done inside the generator.
                    teleporters.EarthCave2.SetEntrance(earthB2.Entrance);
                    overworldMap.PutStandardTeleport(TeleportIndex.EarthCave2, teleporters.EarthCave2, OverworldTeleportIndex.EarthCave1);
                    maps[(int)MapId.EarthCaveB2] = earthB2.Map;
                }
            }
#endif

            if (flags.RandomizeFormationEnemizer)
            {
                DoEnemizer(rng, false, flags.RandomizeFormationEnemizer, false);
            }

            if (preferences.ModernBattlefield)
            {
                EnableModernBattlefield();
            }

            if ((bool)flags.TitansTrove)
            {
                EnableTitansTrove(maps);
            }

            if ((bool)flags.LefeinShops)
            {
                EnableLefeinShops(maps);
            }

            // This has to be done before we shuffle spell levels.
            if (flags.SpellBugs)
            {
                FixSpellBugs();
            }

            if (flags.RebalanceSpells)
            {
                RebalanceSpells();
            }

            if (flags.EnemySpellsTargetingAllies)
            {
                FixEnemyAOESpells();
            }

            if ((bool)flags.ItemMagic)
            {
                ShuffleItemMagic(rng);
            }

            if ((bool)flags.ShortToFR)
            {
                ShortenToFR(maps, (bool)flags.PreserveFiendRefights, rng);
            }

            if (((bool)flags.Treasures) && flags.ShardHunt && !flags.FreeOrbs)
            {
                EnableShardHunt(rng, flags.ExtraShards ? rng.Between(24, 30) : 16, ((bool)flags.NPCItems));
            }

            if ((bool)flags.TransformFinalFormation)
            {
                TransformFinalFormation((FinalFormation)rng.Between(0, Enum.GetValues(typeof(FinalFormation)).Length - 1));
            }

            var maxRetries = 8;
            for (var i = 0; i < maxRetries; i++)
            {
                try
                {
                    overworldMap = new OverworldMap(this, flags, palettes, teleporters);
                    if (((bool)flags.Entrances || (bool)flags.Floors || (bool)flags.Towns) && ((bool)flags.Treasures) && ((bool)flags.NPCItems))
                    {
                        overworldMap.ShuffleEntrancesAndFloors(rng, flags);
                    }

                    if ((bool)flags.ShuffleObjectiveNPCs)
                    {
                        overworldMap.ShuffleObjectiveNPCs(rng);
                    }

                    IncentiveData incentivesData = new IncentiveData(rng, flags, overworldMap, shopItemLocation);

                    if (((bool)flags.Shops))
                    {
                        var excludeItemsFromRandomShops = new List <Item>();
                        if ((bool)flags.Treasures)
                        {
                            excludeItemsFromRandomShops = incentivesData.ForcedItemPlacements.Select(x => x.Item).Concat(incentivesData.IncentiveItems).ToList();
                        }

                        if (!((bool)flags.RandomWaresIncludesSpecialGear))
                        {
                            excludeItemsFromRandomShops.AddRange(ItemLists.SpecialGear);
                        }

                        shopItemLocation = ShuffleShops(rng, (bool)flags.ImmediatePureAndSoftRequired, ((bool)flags.RandomWares), excludeItemsFromRandomShops, flags.WorldWealth);
                        incentivesData   = new IncentiveData(rng, flags, overworldMap, shopItemLocation);
                    }

                    if ((bool)flags.Treasures)
                    {
                        ShuffleTreasures(rng, flags, incentivesData, shopItemLocation, overworldMap, teleporters);
                    }
                    break;
                }
                catch (InsaneException e)
                {
                    Console.WriteLine(e.Message);
                    if (maxRetries > (i + 1))
                    {
                        continue;
                    }
                    throw new InvalidOperationException(e.Message);
                }
            }

            if (((bool)flags.MagicShops))
            {
                ShuffleMagicShops(rng);
            }

            if (((bool)flags.MagicLevels))
            {
                FixWarpBug();                 // The warp bug only needs to be fixed if the magic levels are being shuffled
                ShuffleMagicLevels(rng, ((bool)flags.MagicPermissions));
            }

            /*
             * if (flags.WeaponPermissions)
             * {
             *      ShuffleWeaponPermissions(rng);
             * }
             *
             * if (flags.ArmorPermissions)
             * {
             *      ShuffleArmorPermissions(rng);
             * }
             */

            if (((bool)flags.Rng))
            {
                ShuffleRng(rng);
            }

            if (((bool)flags.EnemyScripts))
            {
                ShuffleEnemyScripts(rng, (bool)flags.AllowUnsafePirates);
            }

            if (((bool)flags.EnemySkillsSpells))
            {
                ShuffleEnemySkillsSpells(rng);
            }

            if (((bool)flags.EnemyStatusAttacks))
            {
                if (((bool)flags.RandomStatusAttacks))
                {
                    RandomEnemyStatusAttacks(rng, (bool)flags.AllowUnsafePirates);
                }
                else
                {
                    ShuffleEnemyStatusAttacks(rng, (bool)flags.AllowUnsafePirates);
                }
            }

            if (((bool)flags.EnemyFormationsUnrunnable))
            {
                if (((bool)flags.EverythingUnrunnable))
                {
                    CompletelyUnrunnable();
                }
                else
                {
                    ShuffleUnrunnable(rng);
                }
            }

            if (((bool)flags.UnrunnablesStrikeFirstAndSurprise))
            {
                AllowStrikeFirstAndSurprise();
            }


            if (((bool)flags.EnemyFormationsSurprise))
            {
                ShuffleSurpriseBonus(rng);
            }

            // Put this before other encounter / trap tile edits.
            if ((bool)flags.AllowUnsafeMelmond)
            {
                EnableMelmondGhetto(flags.RandomizeFormationEnemizer);
            }

            // After unrunnable shuffle and before formation shuffle. Perfect!
            if (flags.WarMECHMode != WarMECHMode.Vanilla)
            {
                WarMECHNpc(flags.WarMECHMode, rng, maps);
            }

            if (flags.WarMECHMode == WarMECHMode.Unleashed)
            {
                UnleashWarMECH();
            }

            if (flags.FiendShuffle)
            {
                FiendShuffle(rng);
            }

            if (flags.FormationShuffleMode != FormationShuffleModeEnum.None)
            {
                ShuffleEnemyFormations(rng, flags.FormationShuffleMode);
            }

            if (((bool)flags.EnemyTrapTiles))
            {
                ShuffleTrapTiles(rng, ((bool)flags.RandomTrapFormations));
            }

            if ((bool)flags.OrdealsPillars)
            {
                ShuffleOrdeals(rng, maps);
            }

            if (flags.SkyCastle4FMazeMode == SkyCastle4FMazeMode.Maze)
            {
                DoSkyCastle4FMaze(rng, maps);
            }
            else if (flags.SkyCastle4FMazeMode == SkyCastle4FMazeMode.Teleporters)
            {
                ShuffleSkyCastle4F(rng, maps);
            }

            if ((bool)flags.ConfusedOldMen)
            {
                EnableConfusedOldMen(rng);
            }

            if ((bool)flags.EarlyOrdeals)
            {
                EnableEarlyOrdeals();
            }

            if (flags.ChaosRush)
            {
                EnableChaosRush();
            }

            if ((bool)flags.EarlySarda && !((bool)flags.NPCItems))
            {
                EnableEarlySarda();
            }

            if ((bool)flags.EarlySage && !((bool)flags.NPCItems))
            {
                EnableEarlySage();
            }

            if ((bool)flags.FreeBridge)
            {
                EnableFreeBridge();
            }

            if ((bool)flags.FreeAirship)
            {
                EnableFreeAirship();
            }

            if ((bool)flags.FreeShip)
            {
                EnableFreeShip();
            }

            if (flags.FreeOrbs)
            {
                EnableFreeOrbs();
            }

            if ((bool)flags.FreeCanal)
            {
                EnableFreeCanal();
            }

            if ((bool)flags.FreeLute)
            {
                EnableFreeLute();
            }

            if (flags.NoPartyShuffle)
            {
                DisablePartyShuffle();
            }

            if (flags.SpeedHacks)
            {
                EnableSpeedHacks();
            }

            if (flags.IdentifyTreasures)
            {
                EnableIdentifyTreasures();
            }

            if (flags.Dash)
            {
                EnableDash();
            }

            if (flags.BuyTen)
            {
                EnableBuyTen();
            }

            if (flags.WaitWhenUnrunnable)
            {
                ChangeUnrunnableRunToWait();
            }

            if (flags.SpeedHacks && flags.EnableCritNumberDisplay)
            {
                EnableCritNumberDisplay();
            }

            if (flags.NPCSwatter)
            {
                EnableNPCSwatter();
            }

            if (flags.EasyMode)
            {
                EnableEasyMode();
            }

            if (flags.HouseMPRestoration || flags.HousesFillHp)
            {
                FixHouse(flags.HouseMPRestoration, flags.HousesFillHp);
            }

            if (flags.WeaponStats)
            {
                FixWeaponStats();
            }

            if (flags.ChanceToRun)
            {
                FixChanceToRun();
            }

            if (flags.EnemyStatusAttackBug)
            {
                FixEnemyStatusAttackBug();
            }

            if (flags.BlackBeltAbsorb)
            {
                FixBBAbsorbBug();
            }

            if (flags.MDefMode != MDefChangesEnum.None)
            {
                MDefChanges(flags.MDefMode);
            }

            if (flags.ThiefHitRate)
            {
                ThiefHitRate();
            }

            if (flags.ImproveTurnOrderRandomization)
            {
                ImproveTurnOrderRandomization(rng);
            }

            if (flags.FixHitChanceCap)
            {
                FixHitChanceCap();
            }

            if (flags.EnemyElementalResistancesBug)
            {
                FixEnemyElementalResistances();
            }

            if (preferences.FunEnemyNames)
            {
                FunEnemyNames(preferences.TeamSteak);
            }

            var itemText = ReadText(ItemTextPointerOffset, ItemTextPointerBase, ItemTextPointerCount);
            itemText[(int)Item.Ribbon].Trim();

            ExpGoldBoost(flags.ExpBonus, flags.ExpMultiplier);
            ScalePrices(flags, itemText, rng, ((bool)flags.ClampMinimumPriceScale), shopItemLocation);
            ScaleEncounterRate(flags.EncounterRate / 30.0, flags.DungeonEncounterRate / 30.0);

            overworldMap.ApplyMapEdits();
            WriteMaps(maps);

            WriteText(itemText, ItemTextPointerOffset, ItemTextPointerBase, ItemTextOffset, UnusedGoldItems);

            if (flags.EnemyScaleFactor > 1)
            {
                ScaleEnemyStats(flags.EnemyScaleFactor, flags.WrapStatOverflow, flags.IncludeMorale, rng, ((bool)flags.ClampMinimumStatScale));
            }

            if (flags.BossScaleFactor > 1)
            {
                ScaleBossStats(flags.BossScaleFactor, flags.WrapStatOverflow, flags.IncludeMorale, rng, ((bool)flags.ClampMinimumBossStatScale));
            }

            PartyComposition(rng, flags);

            if (((bool)flags.RecruitmentMode))
            {
                PubReplaceClinic(rng, flags);
            }

            if ((bool)flags.MapCanalBridge)
            {
                EnableCanalBridge();
            }

            if (flags.NoDanMode)
            {
                NoDanMode();
            }

            SetProgressiveScaleMode(flags.ProgressiveScaleMode);

            if (flags.DisableTentSaving)
            {
                CannotSaveOnOverworld();
            }

            if (flags.DisableInnSaving)
            {
                CannotSaveAtInns();
            }

            // We have to do "fun" stuff last because it alters the RNG state.
            RollCredits(rng);

            if (preferences.DisableDamageTileFlicker)
            {
                DisableDamageTileFlicker();
            }

            if (preferences.ThirdBattlePalette)
            {
                UseVariablePaletteForCursorAndStone();
            }

            if (preferences.PaletteSwap)
            {
                PaletteSwap(rng);
            }

            if (preferences.TeamSteak)
            {
                TeamSteak();
            }

            if (preferences.Music != MusicShuffle.None)
            {
                ShuffleMusic(preferences.Music, rng);
            }

            WriteSeedAndFlags(Version, seed.ToHex(), Flags.EncodeFlagsText(flags));
            ExtraTrackingAndInitCode();
        }
Beispiel #11
0
        public void Randomize(Blob seed, Flags flags)
        {
            var rng = new MT19337(BitConverter.ToUInt32(seed, 0));

            UpgradeToMMC3();
            EasterEggs();
            DynamicWindowColor();
            PermanentCaravan();
            var map = new OverworldMap(this, flags);
            var shopItemLocation = ItemLocations.CaravanItemShop1;

            if (flags.ModernBattlefield)
            {
                SetBattleUI(true);
            }

            if (flags.TitansTrove)
            {
                EnableTitansTrove();
            }

            // This has to be done before we shuffle spell levels.
            if (flags.SpellBugs)
            {
                FixSpellBugs();
            }

            if (flags.Shops)
            {
                shopItemLocation = ShuffleShops(rng, flags.EnemyStatusAttacks);
            }

            if (flags.Treasures || flags.NPCItems || flags.NPCFetchItems)
            {
                var incentivesData = new IncentiveData(rng, flags, map.MapLocationRequirements);
                ShuffleTreasures(rng, flags, incentivesData, shopItemLocation, map.MapLocationRequirements);
            }

            if (flags.MagicShops)
            {
                ShuffleMagicShops(rng);
            }

            if (flags.MagicLevels)
            {
                ShuffleMagicLevels(rng, flags.MagicPermissions);
            }

            if (flags.Rng)
            {
                ShuffleRng(rng);
            }

            if (flags.EnemyScripts)
            {
                ShuffleEnemyScripts(rng);
            }

            if (flags.EnemySkillsSpells)
            {
                ShuffleEnemySkillsSpells(rng);
            }

            if (flags.EnemyStatusAttacks)
            {
                ShuffleEnemyStatusAttacks(rng);
            }

            if (flags.OrdealsPillars)
            {
                ShuffleOrdeals(rng);
            }

            if (flags.CrownlessOrdeals)
            {
                EnableEarlyOrdeals();
            }

            if (flags.KeylessToFR)
            {
                EnableKeylessToFR();
            }

            if (flags.EarlySarda && !flags.NPCItems)
            {
                EnableEarlySarda();
            }

            if (flags.EarlySage && !flags.NPCItems)
            {
                EnableEarlySage();
            }

            if (flags.FreeBridge)
            {
                EnableFreeBridge();
            }

            if (flags.FreeAirship)
            {
                EnableFreeAirship();
            }

            if (flags.FreeOrbs)
            {
                EnableFreeOrbs();
            }

            if (flags.NoPartyShuffle)
            {
                DisablePartyShuffle();
            }

            if (flags.SpeedHacks)
            {
                EnableSpeedHacks();
            }

            if (flags.IdentifyTreasures)
            {
                EnableIdentifyTreasures();
            }

            if (flags.Dash)
            {
                EnableDash();
            }

            if (flags.BuyTen)
            {
                EnableBuyTen();
            }

            if (flags.EasyMode)
            {
                EnableEasyMode();
            }

            if (flags.HouseMPRestoration)
            {
                FixHouse();
            }

            if (flags.WeaponStats)
            {
                FixWeaponStats();
            }

            if (flags.ChanceToRun)
            {
                FixChanceToRun();
            }

            if (flags.EnemyStatusAttackBug)
            {
                FixEnemyStatusAttackBug();
            }

            if (flags.FunEnemyNames)
            {
                FunEnemyNames(flags.TeamSteak);
            }

            var itemText = ReadText(ItemTextPointerOffset, ItemTextPointerBase, ItemTextPointerCount);

            itemText[99] = FF1Text.TextToBytes("Ribbon ", useDTE: false);

            ExpGoldBoost(flags.ExpBonus, flags.ExpMultiplier);
            ScalePrices(flags.PriceScaleFactor, flags.ExpMultiplier, itemText, rng);

            map.ApplyMapEdits();

            WriteText(itemText, ItemTextPointerOffset, ItemTextPointerBase, ItemTextOffset, UnusedGoldItems);

            if (flags.EnemyScaleFactor > 1)
            {
                ScaleEnemyStats(flags.EnemyScaleFactor, rng);
            }

            if (flags.ForcedPartyMembers > 0)
            {
                PartyRandomize(rng, flags.ForcedPartyMembers);
            }

            if (flags.MapCanalBridge)
            {
                EnableCanalBridge();
            }

            // We have to do "fun" stuff last because it alters the RNG state.
            RollCredits(rng);

            if (flags.PaletteSwap)
            {
                PaletteSwap(rng);
            }

            if (flags.TeamSteak)
            {
                TeamSteak();
            }

            if (flags.Music != MusicShuffle.None)
            {
                ShuffleMusic(flags.Music, rng);
            }

            WriteSeedAndFlags(Version, seed.ToHex(), Flags.EncodeFlagsText(flags));
            ExtraTrackingAndInitCode();
        }
Beispiel #12
0
        public static List <IRewardSource> PlaceSaneItems(MT19337 rng,
                                                          IItemPlacementFlags flags,
                                                          IncentiveData incentivesData,
                                                          List <Item> allTreasures,
                                                          ItemShopSlot caravanItemLocation,
                                                          OverworldMap overworldMap)
        {
            Dictionary <MapLocation, Tuple <List <MapChange>, AccessRequirement> > fullLocationRequirements = overworldMap.FullLocationRequirements;
            Dictionary <MapLocation, OverworldTeleportIndex> overridenOverworld = overworldMap.OverriddenOverworldLocations;

            long sanityCounter = 0;
            List <IRewardSource> placedItems;

            var canoeObsoletesBridge    = flags.MapCanalBridge && flags.MapConeriaDwarves;
            var canoeObsoletesShip      = flags.MapCanalBridge ? (flags.MapConeriaDwarves || flags.MapVolcanoIceRiver) : (flags.MapConeriaDwarves && flags.MapVolcanoIceRiver);
            var incentiveLocationPool   = incentivesData.IncentiveLocations.ToList();
            var incentivePool           = incentivesData.IncentiveItems.Where(x => allTreasures.Contains(x)).ToList();
            var forcedItems             = incentivesData.ForcedItemPlacements.ToList();
            var keyLocations            = incentivesData.KeyLocations.ToList();
            var canoeLocations          = incentivesData.CanoeLocations.ToList();
            var bridgeLocations         = incentivesData.BridgeLocations.ToList();
            var shipLocations           = incentivesData.ShipLocations.ToList();
            var itemLocationPool        = incentivesData.AllValidItemLocations.ToList();
            var startingPotentialAccess = overworldMap.StartingPotentialAccess;
            var startingMapLocations    = AccessibleMapLocations(startingPotentialAccess, MapChange.None, fullLocationRequirements);
            var earlyMapLocations       = AccessibleMapLocations(startingPotentialAccess | AccessRequirement.Crystal, MapChange.Bridge, fullLocationRequirements);

            var unincentivizedQuestItems =
                ItemLists.AllQuestItems
                .Where(x => !incentivePool.Contains(x) &&
                       allTreasures.Contains(x) &&
                       !forcedItems.Any(y => y.Item == x))
                .ToList();

            var treasurePool = allTreasures.ToList();

            foreach (var incentive in incentivePool)
            {
                treasurePool.Remove(incentive);
            }
            foreach (var placement in forcedItems)
            {
                treasurePool.Remove(placement.Item);
            }
            foreach (var questItem in unincentivizedQuestItems)
            {
                treasurePool.Remove(questItem);
            }

            var itemShopItem = Item.Bottle;

            while (treasurePool.Remove(Item.Shard))
            {
                unincentivizedQuestItems.Add(Item.Shard);
            }

            // Cache the final unincentivized pool so we can reset it each iteration of the loop.
            IReadOnlyCollection <Item> cachedTreasurePool = new ReadOnlyCollection <Item>(treasurePool.ToList());

            do
            {
                sanityCounter++;
                if (sanityCounter > 500)
                {
                    throw new InsaneException("Sanity Counter exceeds 500 iterations!");
                }
                // 1. (Re)Initialize lists inside of loop
                placedItems = forcedItems.ToList();
                var incentives    = incentivePool.ToList();
                var nonincentives = unincentivizedQuestItems.ToList();
                treasurePool = cachedTreasurePool.ToList();
                incentives.Shuffle(rng);
                nonincentives.Shuffle(rng);

                if (flags.NPCItems)
                {
                    // 2. Place caravan item first because among incentive locations it has the smallest set of possible items
                    var validShopIncentives = incentives
                                              .Where(x => x > Item.None && x <= Item.Soft)
                                              .ToList();
                    if (validShopIncentives.Any() && incentiveLocationPool.Any(x => x.Address == ItemLocations.CaravanItemShop1.Address))
                    {
                        itemShopItem = validShopIncentives.PickRandom(rng);
                        incentives.Remove(itemShopItem);
                    }
                    else
                    {
                        itemShopItem = treasurePool.Concat(nonincentives).Where(x => x > Item.None && x <= Item.Soft && x != Item.Shard).ToList().PickRandom(rng);
                        if (!nonincentives.Remove(itemShopItem))
                        {
                            treasurePool.Remove(itemShopItem);
                        }
                    }
                    placedItems.Add(new ItemShopSlot(caravanItemLocation, itemShopItem));

                    // 3. Place key and canoe next because among incentive locations these have the greatest initial impact
                    if (incentives.Remove(Item.Key) || nonincentives.Remove(Item.Key))
                    {
                        placedItems.Add(NewItemPlacement(keyLocations.PickRandom(rng), Item.Key));
                    }
                    if (incentives.Remove(Item.Canoe) || nonincentives.Remove(Item.Canoe))
                    {
                        placedItems.Add(NewItemPlacement(canoeLocations.Where(x => !placedItems.Any(y => y.Address == x.Address)).ToList().PickRandom(rng), Item.Canoe));
                    }

                    var startingCanoeAvailable =
                        placedItems.Any(x => x.Item == Item.Canoe && startingMapLocations.Contains(x.MapLocation) &&
                                        startingMapLocations.Contains((x as MapObject)?.SecondLocation ?? MapLocation.StartingLocation));
                    var earlyCanoeAvailable =
                        placedItems.Any(x => x.Item == Item.Canoe && earlyMapLocations.Contains(x.MapLocation) &&
                                        earlyMapLocations.Contains((x as MapObject)?.SecondLocation ?? MapLocation.StartingLocation));
                    var earlyKeyAvailable =
                        placedItems.Any(x => x.Item == Item.Key && earlyMapLocations.Contains(x.MapLocation) &&
                                        earlyMapLocations.Contains((x as MapObject)?.SecondLocation ?? MapLocation.StartingLocation));

                    // 4. Place Bridge and Ship next since the valid location lists are so small, unless canoe is available and map edits are applied
                    if (!earlyCanoeAvailable || !canoeObsoletesShip)
                    {
                        var remainingShipLocations =
                            shipLocations
                            .Where(x => !placedItems.Any(y => y.Address == x.Address) &&
                                   (earlyKeyAvailable || !x.AccessRequirement.HasFlag(AccessRequirement.Key)))
                            .ToList();
                        if (!remainingShipLocations.Any())
                        {
                            continue;
                        }
                        if (incentives.Remove(Item.Ship) && remainingShipLocations.Any(x => incentiveLocationPool.Any(y => y.Address == x.Address)))
                        {
                            remainingShipLocations =
                                remainingShipLocations
                                .Where(x => incentiveLocationPool.Any(y => y.Address == x.Address))
                                .ToList();
                        }
                        nonincentives.Remove(Item.Ship);
                        placedItems.Add(NewItemPlacement(remainingShipLocations.PickRandom(rng), Item.Ship));
                    }

                    var startingShipAvailable =
                        placedItems.Any(x => x.Item == Item.Ship && startingMapLocations.Contains(x.MapLocation) &&
                                        startingMapLocations.Contains((x as MapObject)?.SecondLocation ?? MapLocation.StartingLocation));

                    if (!(startingCanoeAvailable && canoeObsoletesBridge) && !startingShipAvailable)
                    {
                        var startingKeyAvailable =
                            earlyKeyAvailable && placedItems.Any(x => x.Item == Item.Key && startingMapLocations.Contains(x.MapLocation) &&
                                                                 startingMapLocations.Contains((x as MapObject)?.SecondLocation ?? MapLocation.StartingLocation));

                        var remainingBridgeLocations =
                            bridgeLocations
                            .Where(x => !placedItems.Any(y => y.Address == x.Address) &&
                                   (startingKeyAvailable || !x.AccessRequirement.HasFlag(AccessRequirement.Key)))
                            .ToList();
                        if (!remainingBridgeLocations.Any())
                        {
                            continue;
                        }
                        if (incentives.Remove(Item.Bridge) && remainingBridgeLocations.Any(x => incentiveLocationPool.Any(y => y.Address == x.Address)))
                        {
                            remainingBridgeLocations =
                                remainingBridgeLocations
                                .Where(x => incentiveLocationPool.Any(y => y.Address == x.Address))
                                .ToList();
                        }
                        nonincentives.Remove(Item.Bridge);
                        placedItems.Add(NewItemPlacement(remainingBridgeLocations.PickRandom(rng), Item.Bridge));
                    }
                }

                // 5. Then place all incentive locations that don't have special logic
                var incentiveLocations = incentiveLocationPool.Where(x => !placedItems.Any(y => y.Address == x.Address) && x.Address != ItemLocations.CaravanItemShop1.Address).ToList();
                incentiveLocations.Shuffle(rng);
                foreach (var incentiveLocation in incentiveLocations)
                {
                    if (!incentives.Any())
                    {
                        break;
                    }
                    placedItems.Add(NewItemPlacement(incentiveLocation, incentives.SpliceRandom(rng)));
                }

                // 6. Then place remanining incentive items and unincentivized quest items in any other chest before ToFR
                var leftoverItems = incentives.Concat(nonincentives).ToList();
                leftoverItems.Shuffle(rng);
                var leftoverItemLocations =
                    itemLocationPool
                    .Where(x => !ItemLocations.ToFR.Any(y => y.Address == x.Address) &&
                           !x.IsUnused && !placedItems.Any(y => y.Address == x.Address))
                    .ToList();
                foreach (var leftoverItem in leftoverItems)
                {
                    placedItems.Add(NewItemPlacement(leftoverItemLocations.SpliceRandom(rng), leftoverItem));
                }

                // 7. Check sanity and loop if needed
            } while (!CheckSanity(placedItems, fullLocationRequirements, flags));

            if (Debugger.IsAttached)
            {
                Console.WriteLine($"ItemPlacement::PlaceSaneItems required {sanityCounter} iterations.");
                Console.WriteLine("");
                Console.WriteLine("Item     Entrance  ->  Floor  ->  Source                   Requirements");
                Console.WriteLine("------------------------------------------------------------------------------------------");

                var sorted = placedItems.Where(item => item.Item != Item.Shard).ToList();
                sorted.Sort((IRewardSource lhs, IRewardSource rhs) => lhs.Item.ToString().CompareTo(rhs.Item.ToString()));
                sorted.ForEach(item =>
                {
                    if (fullLocationRequirements.TryGetValue(item.MapLocation, out var flr))
                    {
                        var overworldLocation = item.MapLocation.ToString();
                        if (overridenOverworld != null && overridenOverworld.TryGetValue(item.MapLocation, out var overriden))
                        {
                            overworldLocation = overriden.ToString();
                        }

                        var itemStr = item.Item.ToString().PadRight(9);
                        var locStr  = $"{overworldLocation} -> {item.MapLocation} -> {item.Name} ".PadRight(50);
                        var changes = $"{String.Join(" | ", flr.Item1.Select(mapChange => mapChange.ToString()).ToArray())}";
                        var reqs    = flr.Item2.ToString().CompareTo("None") == 0 ? "" : $" AND {flr.Item2.ToString()}";
                        Console.WriteLine($"{itemStr}{locStr}{changes}{reqs}");
                    }
                });
            }

            // 8. Place all remaining unincentivized treasures or incentivized non-quest items that weren't placed
            itemLocationPool = itemLocationPool.Where(x => !x.IsUnused && !placedItems.Any(y => y.Address == x.Address)).ToList();
            foreach (var placedItem in placedItems)
            {
                incentivePool.Remove(placedItem.Item);
            }
            treasurePool.AddRange(incentivePool);

            Debug.Assert(treasurePool.Count() == itemLocationPool.Count());
            treasurePool.Shuffle(rng);
            itemLocationPool.Shuffle(rng);

            var leftovers = treasurePool.Zip(itemLocationPool, (treasure, location) => NewItemPlacement(location, treasure));

            placedItems.AddRange(leftovers);
            return(placedItems);
        }
Beispiel #13
0
        public static List <IRewardSource> PlaceSaneItems(MT19337 rng,
                                                          ITreasureShuffleFlags flags,
                                                          IncentiveData incentivesData,
                                                          List <Item> allTreasures,
                                                          ItemShopSlot caravanItemLocation,
                                                          Dictionary <MapLocation, List <MapChange> > mapLocationRequirements)
        {
            long sanityCounter = 0;
            List <IRewardSource> placedItems;

            var incentiveLocationPool = incentivesData.IncentiveLocations.ToList();
            var incentivePool         = incentivesData.IncentiveItems.ToList();
            var forcedItems           = incentivesData.ForcedItemPlacements.ToList();
            var bridgeLocations       = incentivesData.BridgeLocations.ToList();
            var shipLocations         = incentivesData.ShipLocations.ToList();
            var itemLocationPool      = incentivesData.AllValidItemLocations.ToList();

            var unincentivizedQuestItems =
                ItemLists.AllQuestItems
                .Where(x => !incentivePool.Contains(x) &&
                       x != Item.Ship && x != Item.Bridge && x != Item.Bottle &&
                       !forcedItems.Any(y => y.Item == x)).ToList();

            var treasurePool = allTreasures.ToList();

            treasurePool.Remove(Item.Bridge);
            treasurePool.Remove(Item.Ship);

            foreach (var incentive in incentivePool)
            {
                treasurePool.Remove(incentive);
            }
            foreach (var placement in forcedItems)
            {
                treasurePool.Remove(placement.Item);
            }
            foreach (var questItem in unincentivizedQuestItems)
            {
                treasurePool.Remove(questItem);
            }
            while (treasurePool.Remove(Item.Shard))
            {
                unincentivizedQuestItems.Add(Item.Shard);
            }
            do
            {
                sanityCounter++;
                if (sanityCounter > 10000)
                {
                    throw new InvalidOperationException("Invalid flag set");
                }
                // 1. (Re)Initialize lists inside of loop
                placedItems = forcedItems.ToList();
                var incentives = incentivePool.ToList();
                incentives.Shuffle(rng);

                if (flags.NPCItems)
                {
                    // 2. Place caravan item first because among incentive locations it has the smallest set of possible items
                    if (!placedItems.Any(x => x.Address == ItemLocations.CaravanItemShop1.Address))
                    {
                        var itemPick            = Item.Bottle;
                        var validShopIncentives = incentives
                                                  .Where(x => x > Item.None && x <= Item.Soft)
                                                  .ToList();
                        if (validShopIncentives.Any())
                        {
                            itemPick = validShopIncentives.PickRandom(rng);
                            incentives.Remove(itemPick);
                        }
                        else if (placedItems.Any(x => x.Item == Item.Bottle))
                        {
                            itemPick = ItemLists.AllConsumables.ToList().PickRandom(rng);
                        }
                        else
                        {
                            treasurePool.Remove(Item.Bottle);
                        }

                        placedItems.Add(new ItemShopSlot(caravanItemLocation, itemPick));
                    }

                    // 3. Place Bridge and Ship next since the valid location lists are so small
                    IRewardSource bridgePlacement = bridgeLocations.PickRandom(rng);
                    placedItems.Add(NewItemPlacement(bridgePlacement, Item.Bridge));

                    var shipPlacement =
                        shipLocations
                        .Where(x => x.Address != bridgePlacement.Address)
                        .ToList().PickRandom(rng);
                    placedItems.Add(NewItemPlacement(shipPlacement, Item.Ship));
                }

                // 4. Then place all incentive locations that don't have special logic
                foreach (var incentiveLocation in incentiveLocationPool.Where(x => !placedItems.Any(y => y.Address == x.Address)))
                {
                    if (!incentives.Any())
                    {
                        break;
                    }
                    placedItems.Add(NewItemPlacement(incentiveLocation, incentives.SpliceRandom(rng)));
                }

                // 5. Then place remanining incentive items and unincentivized quest items in any other chest before ToFR
                var leftoverItems = incentives.Concat(unincentivizedQuestItems).ToList();
                leftoverItems.Shuffle(rng);
                var leftoverItemLocations =
                    itemLocationPool
                    .Where(x => !ItemLocations.ToFR.Any(y => y.Address == x.Address) &&
                           !x.IsUnused && !placedItems.Any(y => y.Address == x.Address))
                    .ToList();
                foreach (var leftoverItem in leftoverItems)
                {
                    placedItems.Add(NewItemPlacement(leftoverItemLocations.SpliceRandom(rng), leftoverItem));
                }

                // 6. Check sanity and loop if needed
            } while (!CheckSanity(placedItems, flags, mapLocationRequirements));

            // 7. Place all remaining unincentivized treasures or incentivized non-quest items that weren't placed
            var i = 0;

            itemLocationPool =
                itemLocationPool
                .Where(x => !x.IsUnused && !placedItems.Any(y => y.Address == x.Address))
                .ToList();
            foreach (var placedItem in placedItems)
            {
                incentivePool.Remove(placedItem.Item);
            }
            foreach (var unplacedIncentive in incentivePool)
            {
                treasurePool.Add(unplacedIncentive);
            }
            treasurePool.Shuffle(rng);
            foreach (var remainingTreasure in itemLocationPool)
            {
                placedItems.Add(NewItemPlacement(remainingTreasure, treasurePool[i]));
                i++;
            }

            //Debug.WriteLine($"Sanity Check Fails: {sanityCounter}");
            return(placedItems);
        }
Beispiel #14
0
        public void Randomize(Blob seed, Flags flags)
        {
            var rng = new MT19337(BitConverter.ToUInt32(seed, 0));

            UpgradeToMMC3();
            MakeSpaceIn1F();
            EasterEggs();
            DynamicWindowColor();
            PermanentCaravan();
            ShiftEarthOrbDown();

            var overworldMap     = new OverworldMap(this, flags);
            var maps             = ReadMaps();
            var incentivesData   = new IncentiveData(rng, flags, overworldMap.MapLocationRequirements);
            var shopItemLocation = ItemLocations.CaravanItemShop1;

            if (flags.ModernBattlefield)
            {
                EnableModernBattlefield();
            }

            if (flags.TitansTrove)
            {
                EnableTitansTrove(maps);
            }

            // This has to be done before we shuffle spell levels.
            if (flags.SpellBugs)
            {
                FixSpellBugs();
            }

            if (flags.EnemySpellsTargetingAllies)
            {
                FixEnemyAOESpells();
            }

            if (flags.ItemMagic)
            {
                ShuffleItemMagic(rng);
            }

            if (flags.Shops)
            {
                var excludeItemsFromRandomShops = flags.Treasures
                                        ? incentivesData.ForcedItemPlacements.Select(x => x.Item).Concat(incentivesData.IncentiveItems).ToList()
                                        : new List <Item>();
                shopItemLocation = ShuffleShops(rng, flags.EnemyStatusAttacks, flags.RandomWares, excludeItemsFromRandomShops);
            }

            if (flags.Treasures || flags.NPCItems || flags.NPCFetchItems)
            {
                ShuffleTreasures(rng, flags, incentivesData, shopItemLocation, overworldMap.MapLocationRequirements);
            }

            if (flags.Treasures && flags.ShardHunt && !flags.ChaosRush)
            {
                EnableShardHunt(rng, flags.ExtraShards ? rng.Between(24, 30) : 16, maps, flags.NPCItems);
            }

            if (flags.TransformFinalFormation)
            {
                TransformFinalFormation((FinalFormation)rng.Between(0, Enum.GetValues(typeof(FinalFormation)).Length - 1));
            }

            if (flags.MagicShops)
            {
                ShuffleMagicShops(rng);
            }

            if (flags.MagicLevels)
            {
                FixWarpBug();                 // The warp bug only needs to be fixed if the magic levels are being shuffled
                ShuffleMagicLevels(rng, flags.MagicPermissions);
            }

            if (flags.Rng)
            {
                ShuffleRng(rng);
            }

            if (flags.EnemyScripts)
            {
                ShuffleEnemyScripts(rng, flags.AllowUnsafePirates);
            }

            if (flags.EnemySkillsSpells)
            {
                ShuffleEnemySkillsSpells(rng);
            }

            if (flags.EnemyStatusAttacks)
            {
                ShuffleEnemyStatusAttacks(rng, flags.AllowUnsafePirates);
            }

            if (flags.EnemyFormationsUnrunnable)
            {
                ShuffleUnrunnable(rng);
            }

            if (flags.EnemyFormationsSurprise)
            {
                ShuffleSurpriseBonus(rng);
            }

            if (flags.EnemyFormationsFrequency)
            {
                ShuffleEnemyFormations(rng);
            }

            if (flags.OrdealsPillars)
            {
                ShuffleOrdeals(rng, maps);
            }

            if (flags.SkyCastle4FTeleporters)
            {
                ShuffleSkyCastle4F(rng, maps);
            }

            if (flags.CrownlessOrdeals)
            {
                EnableEarlyOrdeals();
            }

            if (flags.ChaosRush)
            {
                EnableChaosRush();
            }

            if (flags.EarlySarda && !flags.NPCItems)
            {
                EnableEarlySarda();
            }

            if (flags.EarlySage && !flags.NPCItems)
            {
                EnableEarlySage();
            }

            if (flags.FreeBridge)
            {
                EnableFreeBridge();
            }

            if (flags.FreeAirship)
            {
                EnableFreeAirship();
            }

            if (flags.FreeOrbs)
            {
                EnableFreeOrbs();
            }

            if (flags.NoPartyShuffle)
            {
                DisablePartyShuffle();
            }

            if (flags.SpeedHacks)
            {
                EnableSpeedHacks();
            }

            if (flags.IdentifyTreasures)
            {
                EnableIdentifyTreasures();
            }

            if (flags.Dash)
            {
                EnableDash();
            }

            if (flags.BuyTen)
            {
                EnableBuyTen();
            }

            if (flags.WaitWhenUnrunnable)
            {
                ChangeUnrunnableRunToWait();
            }

            if (flags.EasyMode)
            {
                EnableEasyMode();
            }

            if (flags.HouseMPRestoration)
            {
                FixHouse();
            }

            if (flags.WeaponStats)
            {
                FixWeaponStats();
            }

            if (flags.ChanceToRun)
            {
                FixChanceToRun();
            }

            if (flags.EnemyStatusAttackBug)
            {
                FixEnemyStatusAttackBug();
            }

            if (flags.BlackBeltAbsorb)
            {
                FixBBAbsorbBug();
            }

            if (flags.ImproveTurnOrderRandomization)
            {
                ImproveTurnOrderRandomization(rng);
            }

            if (flags.EnemyElementalResistancesBug)
            {
                FixEnemyElementalResistances();
            }

            if (flags.FunEnemyNames)
            {
                FunEnemyNames(flags.TeamSteak);
            }

            var itemText = ReadText(ItemTextPointerOffset, ItemTextPointerBase, ItemTextPointerCount);

            FixVanillaRibbon(itemText);
            ExpGoldBoost(flags.ExpBonus, flags.ExpMultiplier);
            ScalePrices(flags.PriceScaleFactor, flags.ExpMultiplier, flags.VanillaStartingGold, itemText, rng);

            overworldMap.ApplyMapEdits();
            WriteMaps(maps);

            WriteText(itemText, ItemTextPointerOffset, ItemTextPointerBase, ItemTextOffset, UnusedGoldItems);

            if (flags.EnemyScaleFactor > 1)
            {
                ScaleEnemyStats(flags.EnemyScaleFactor, rng);
            }

            if (flags.ForcedPartyMembers > 0)
            {
                PartyRandomize(rng, flags.ForcedPartyMembers);
            }

            if (flags.MapCanalBridge)
            {
                EnableCanalBridge();
            }

            // We have to do "fun" stuff last because it alters the RNG state.
            RollCredits(rng);

            if (flags.PaletteSwap)
            {
                PaletteSwap(rng);
            }

            if (flags.TeamSteak)
            {
                TeamSteak();
            }

            if (flags.Music != MusicShuffle.None)
            {
                ShuffleMusic(flags.Music, rng);
            }

            WriteSeedAndFlags(Version, seed.ToHex(), Flags.EncodeFlagsText(flags));
            ExtraTrackingAndInitCode();
        }
Beispiel #15
0
        public Archipelago(FF1Rom _rom, List <IRewardSource> generatedPlacement, SanityCheckerV2 checker, ExpChests _expChests, IncentiveData _incentivesData, Flags _flags, Preferences _preferences)
        {
            rom            = _rom;
            expChests      = _expChests;
            incentivesData = _incentivesData;
            flags          = _flags;
            preferences    = _preferences;

            var kiPlacement = generatedPlacement.Where(r => ItemLists.AllQuestItems.Contains(r.Item) && r.Item != Item.Bridge).ToList();

            if (flags.ArchipelagoConsumables)
            {
                if (flags.ExtConsumablesEnabled)
                {
                    kiPlacement.AddRange(generatedPlacement.Where(r => r.Item >= Item.Tent && r.Item <= Item.Rapier));
                }
                else
                {
                    kiPlacement.AddRange(generatedPlacement.Where(r => r.Item >= Item.Tent && r.Item <= Item.Soft));
                }
            }

            if (flags.ArchipelagoShards)
            {
                kiPlacement.AddRange(generatedPlacement.Where(r => r.Item == Item.Shard));
            }

            if (flags.ArchipelagoGold)
            {
                kiPlacement.AddRange(generatedPlacement.Where(r => r.Item >= Item.Gold10 && r.Item < expChests.FirstExpItem));
            }

            switch (flags.ArchipelagoEquipment)
            {
            case ArchipelagoEquipment.Common:
                AddCommonEquipment(kiPlacement, generatedPlacement);
                AddRareEquipment(kiPlacement, generatedPlacement);
                AddLegendaryEquipment(kiPlacement, generatedPlacement);
                AddCasterEquipment(kiPlacement, generatedPlacement);
                break;

            case ArchipelagoEquipment.Rare:
                AddRareEquipment(kiPlacement, generatedPlacement);
                AddLegendaryEquipment(kiPlacement, generatedPlacement);
                AddCasterEquipment(kiPlacement, generatedPlacement);
                break;

            case ArchipelagoEquipment.Legendary:
                AddLegendaryEquipment(kiPlacement, generatedPlacement);
                AddCasterEquipment(kiPlacement, generatedPlacement);
                break;

            case ArchipelagoEquipment.CasterItems:
                AddCasterEquipment(kiPlacement, generatedPlacement);
                break;

            case ArchipelagoEquipment.Incentivized:
                AddIncentivizedEquipment(kiPlacement, generatedPlacement);
                break;
            }

            //Remove ToFr and distinct by address to remove duplicates
            kiPlacement = kiPlacement.Where(r => !ItemLocations.ToFR.Any(l => l.Address == r.Address)).GroupBy(r => r.Address).Select(g => g.First()).ToList();

            logic = new SCLogic(rom, checker.Main, kiPlacement, flags, true);
        }
Beispiel #16
0
        public void Randomize(Blob seed, Flags flags)
        {
            var rng = new MT19337(BitConverter.ToUInt32(seed, 0));

            UpgradeToMMC3();
            MakeSpaceIn1F();
            EasterEggs();
            DynamicWindowColor();
            PermanentCaravan();
            ShiftEarthOrbDown();

            var palettes         = OverworldMap.GeneratePalettes(Get(OverworldMap.MapPaletteOffset, MapCount * OverworldMap.MapPaletteSize).Chunk(OverworldMap.MapPaletteSize));
            var overworldMap     = new OverworldMap(this, flags, palettes);
            var maps             = ReadMaps();
            var shopItemLocation = ItemLocations.CaravanItemShop1;

            if (flags.ModernBattlefield)
            {
                EnableModernBattlefield();
            }

            if (flags.TitansTrove)
            {
                EnableTitansTrove(maps);
            }

            // This has to be done before we shuffle spell levels.
            if (flags.SpellBugs)
            {
                FixSpellBugs();
            }

            if (flags.EnemySpellsTargetingAllies)
            {
                FixEnemyAOESpells();
            }

            if (flags.ItemMagic)
            {
                ShuffleItemMagic(rng);
            }

            if (flags.ShortToFR)
            {
                ShortenToFR(maps, flags.PreserveFiendRefights, rng);
            }

            if (flags.Treasures && flags.ShardHunt && !flags.ChaosRush)
            {
                EnableShardHunt(rng, flags.ExtraShards ? rng.Between(24, 30) : 16, flags.NPCItems);
            }

            if (flags.TransformFinalFormation)
            {
                TransformFinalFormation((FinalFormation)rng.Between(0, Enum.GetValues(typeof(FinalFormation)).Length - 1));
            }

            var maxRetries = 500;

            for (var i = 0; i < maxRetries; i++)
            {
                try
                {
                    overworldMap = new OverworldMap(this, flags, palettes);
                    if ((flags.Entrances || flags.Floors || flags.Towns) && flags.Treasures && flags.NPCItems)
                    {
                        overworldMap.ShuffleEntrancesAndFloors(rng, flags);
                    }

                    var incentivesData = new IncentiveData(rng, flags, overworldMap);

                    if (flags.Shops)
                    {
                        var excludeItemsFromRandomShops = flags.Treasures
                                                        ? incentivesData.ForcedItemPlacements.Select(x => x.Item).Concat(incentivesData.IncentiveItems).ToList()
                                                        : new List <Item>();
                        shopItemLocation = ShuffleShops(rng, flags.EnemyStatusAttacks, flags.RandomWares, excludeItemsFromRandomShops);
                    }

                    if (flags.Treasures)
                    {
                        ShuffleTreasures(rng, flags, incentivesData, shopItemLocation, overworldMap);
                    }
                    break;
                }
                catch (InsaneException)
                {
                    Console.WriteLine("Insane seed. Retrying");
                    if (maxRetries > (i + 1))
                    {
                        continue;
                    }
                    throw new InvalidOperationException("Failed Sanity Check too many times");
                }
            }

            if (flags.MagicShops)
            {
                ShuffleMagicShops(rng);
            }

            if (flags.MagicLevels)
            {
                FixWarpBug();                 // The warp bug only needs to be fixed if the magic levels are being shuffled
                ShuffleMagicLevels(rng, flags.MagicPermissions);
            }

            if (flags.Rng)
            {
                ShuffleRng(rng);
            }

            if (flags.EnemyScripts)
            {
                ShuffleEnemyScripts(rng, flags.AllowUnsafePirates);
            }

            if (flags.EnemySkillsSpells)
            {
                ShuffleEnemySkillsSpells(rng);
            }

            if (flags.EnemyStatusAttacks)
            {
                ShuffleEnemyStatusAttacks(rng, flags.AllowUnsafePirates);
            }

            if (flags.EnemyFormationsUnrunnable)
            {
                ShuffleUnrunnable(rng);
            }

            if (flags.EnemyFormationsSurprise)
            {
                ShuffleSurpriseBonus(rng);
            }

            if (flags.EnemyFormationsFrequency)
            {
                ShuffleEnemyFormations(rng);
            }

            if (flags.EnemyTrapTiles)
            {
                ShuffleTrapTiles(rng, flags.RandomTrapFormations);
            }

            if (flags.OrdealsPillars)
            {
                ShuffleOrdeals(rng, maps);
            }

            if (flags.SkyCastle4FTeleporters)
            {
                ShuffleSkyCastle4F(rng, maps);
            }

            if (flags.ConfusedOldMen)
            {
                EnableConfusedOldMen(rng);
            }

            if (flags.CrownlessOrdeals)
            {
                EnableEarlyOrdeals();
            }

            if (flags.ChaosRush)
            {
                EnableChaosRush();
            }

            if (flags.EarlySarda && !flags.NPCItems)
            {
                EnableEarlySarda();
            }

            if (flags.EarlySage && !flags.NPCItems)
            {
                EnableEarlySage();
            }

            if (flags.FreeBridge)
            {
                EnableFreeBridge();
            }

            if (flags.FreeAirship)
            {
                EnableFreeAirship();
            }

            if (flags.FreeOrbs)
            {
                EnableFreeOrbs();
            }

            if (flags.NoPartyShuffle)
            {
                DisablePartyShuffle();
            }

            if (flags.SpeedHacks)
            {
                EnableSpeedHacks();
            }

            if (flags.IdentifyTreasures)
            {
                EnableIdentifyTreasures();
            }

            if (flags.Dash)
            {
                EnableDash();
            }

            if (flags.BuyTen)
            {
                EnableBuyTen();
            }

            if (flags.WaitWhenUnrunnable)
            {
                ChangeUnrunnableRunToWait();
            }

            if (flags.EnableCritNumberDisplay)
            {
                EnableCritNumberDisplay();
            }

            if (flags.EasyMode)
            {
                EnableEasyMode();
            }

            if (flags.HouseMPRestoration)
            {
                FixHouse();
            }

            if (flags.WeaponStats)
            {
                FixWeaponStats();
            }

            if (flags.ChanceToRun)
            {
                FixChanceToRun();
            }

            if (flags.EnemyStatusAttackBug)
            {
                FixEnemyStatusAttackBug();
            }

            if (flags.BlackBeltAbsorb)
            {
                FixBBAbsorbBug();
            }

            if (flags.ImproveTurnOrderRandomization)
            {
                ImproveTurnOrderRandomization(rng);
            }

            if (flags.EnemyElementalResistancesBug)
            {
                FixEnemyElementalResistances();
            }

            if (flags.FunEnemyNames)
            {
                FunEnemyNames(flags.TeamSteak);
            }

            var itemText = ReadText(ItemTextPointerOffset, ItemTextPointerBase, ItemTextPointerCount);

            // var dialogueText = ReadText(DialogueTextPointerOffset, DialogueTextPointerBase, DialogueTextPointerCount);
            FixVanillaRibbon(itemText);
            ExpGoldBoost(flags.ExpBonus, flags.ExpMultiplier);
            ScalePrices(flags, itemText, rng, shopItemLocation);
            ScaleEncounterRate(flags.EncounterRate / 30.0, flags.DungeonEncounterRate / 30.0);


            if (flags.WarMECHMode != WarMECHMode.Vanilla)
            {
                WarMECHNpc(flags.WarMECHMode, rng, maps);
            }

            overworldMap.ApplyMapEdits();
            WriteMaps(maps);

            WriteText(itemText, ItemTextPointerOffset, ItemTextPointerBase, ItemTextOffset, UnusedGoldItems);
            // WriteText(dialogueText, DialogueTextPointerOffset, DialogueTextPointerBase, DialogueTextOffset);

            if (flags.EnemyScaleFactor > 1)
            {
                ScaleEnemyStats(flags.EnemyScaleFactor, flags.WrapStatOverflow, flags.IncludeMorale, rng);
            }

            if (flags.BossScaleFactor > 1)
            {
                ScaleBossStats(flags.BossScaleFactor, flags.WrapStatOverflow, flags.IncludeMorale, rng);
            }

            if (flags.ForcedPartyMembers > 0)
            {
                PartyRandomize(rng, flags.ForcedPartyMembers);
            }

            if (flags.MapCanalBridge)
            {
                EnableCanalBridge();
            }

            SetProgressiveScaleMode(flags.ProgressiveScaleMode);

            // We have to do "fun" stuff last because it alters the RNG state.
            RollCredits(rng);

            if (flags.PaletteSwap)
            {
                PaletteSwap(rng);
            }

            if (flags.TeamSteak)
            {
                TeamSteak();
            }

            if (flags.Music != MusicShuffle.None)
            {
                ShuffleMusic(flags.Music, rng);
            }

            WriteSeedAndFlags(Version, seed.ToHex(), Flags.EncodeFlagsText(flags));
            ExtraTrackingAndInitCode();
        }