Example #1
0
        public static void Run(FF1Rom rom, RandomNumberGenerator csharpRNG)
        {
            MapRequirements reqs = new MapRequirements
            {
                MapId = MapId.EarthCaveB1,
                Rom   = rom,
            };

            MapGenerator         generator = new MapGenerator();
            MapGeneratorStrategy strategy  = MapGeneratorStrategy.BSPTree;


            while (true)
            {
                var seed = new byte[8];
                csharpRNG.GetBytes(seed);
                MT19337     rng       = new MT19337(BitConverter.ToUInt32(seed, 0));
                CompleteMap waterfall = generator.Generate(rng, strategy, reqs);
                Console.WriteLine("Press a key to generate another one (X to quit)...");
                if (Console.ReadKey().Key == ConsoleKey.X)
                {
                    break;
                }
            }
        }
Example #2
0
        private IMapGeneratorEngine GetEngine(MapGeneratorStrategy strategy)
        {
            switch (strategy)
            {
            case MapGeneratorStrategy.Cellular:
                return(new CellularGenerator());

            case MapGeneratorStrategy.WaterfallClone:
                return(new WaterfallEngine());

            case MapGeneratorStrategy.Square:
                return(new RectilinearGenerator());

            case MapGeneratorStrategy.BSPTree:
                return(new BSPTreeEngine());

            default:
                return(null);
            }
        }
Example #3
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();
            EasterEggs();
            DynamicWindowColor();
            PermanentCaravan();
            ShiftEarthOrbDown();
            CastableItemTargeting();

            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;

            /*
             * flags.FreeAirship = true;
             * flags.ExperimentalFloorGeneration = true;
             * flags.DungeonEncounterRate = 0;
             */
            if (flags.ExperimentalFloorGeneration)
            {
                MapRequirements reqs = new MapRequirements
                {
                    MapId = MapId.Waterfall,
                    Rom   = this,
                };

                MapGenerator         generator = new MapGenerator();
                MapGeneratorStrategy 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;

                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;

                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;
            }

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

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

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

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

            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 = 4;

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

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

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

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

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

                        shopItemLocation = ShuffleShops(rng, flags.ImmediatePureAndSoftRequired, flags.RandomWares, excludeItemsFromRandomShops, flags.WorldWealth);
                    }

                    if (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 (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.WeaponPermissions)
            {
                ShuffleWeaponPermissions(rng);
            }

            if (flags.ArmorPermissions)
            {
                ShuffleArmorPermissions(rng);
            }

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

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

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

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

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



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

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

            // 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 (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.EarlyOrdeals)
            {
                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.FreeCanal)
            {
                EnableFreeCanal();
            }

            if (flags.HousesFillHp)
            {
                EnableHousesFillHp();
            }

            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.NPCSwatter)
            {
                EnableNPCSwatter();
            }

            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.BlackBeltMDEF)
            {
                RemakeStyleMasterMDEF();
            }

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

            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, 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, flags.ClampMinimumStatScale);
            }

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

            PartyComposition(rng, flags);

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

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

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

            SetProgressiveScaleMode(flags.ProgressiveScaleMode);

            // 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();
        }
Example #4
0
        public CompleteMap Generate(MT19337 rng, MapGeneratorStrategy strategy, MapRequirements reqs)
        {
            CompleteMap map = null;

            if (reqs.MapId == MapId.Waterfall)
            {
                reqs.Floor       = Tile.WaterfallRandomEncounters;
                reqs.InRoomFloor = Tile.WaterfallInside;
                reqs.FreeNPCs    = Enumerable.Range(1, 10);
                reqs.Rooms       = new List <RoomSpec>
                {
                    new RoomSpec
                    {
                        Tiledata = new byte[, ] {
                            { 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02 },
                            { 0x03, 0x79, 0x7A, 0x7B, 0x7C, 0x7D, 0x7E, 0x05 },
                            { 0x03, 0x46, 0x46, 0x46, 0x46, 0x46, 0x46, 0x05 },
                            { 0x06, 0x07, 0x48, 0x07, 0x07, 0x07, 0x07, 0x08 },
                            { 0x30, 0x30, 0x36, 0x30, 0x30, 0x30, 0x30, 0x30 },
                            { 0x49, 0x49, 0x3A, 0x49, 0x49, 0x49, 0x49, 0x49 }
                        },
                        NPCs = new List <NPC> {
                            new NPC {
                                Index = 0, Coord = (5, 2), InRoom = true, Stationary = false
                            }
                        },
                    }
                };
                reqs.Portals = new byte[] { (byte)Tile.WarpUp };

                IMapGeneratorEngine engine = GetEngine(strategy);
                int iterations             = 0;
                while (iterations < MAX_MAP_ITERATIONS && map == null)
                {
                    Console.WriteLine($"Generating {reqs.MapId} - iteration #{iterations}");
                    map = engine.Generate(rng, reqs);
                }

                if (map == null)
                {
                    throw new InsaneException($"Couldn't generate map using {strategy} after maximum {iterations} iterations.");
                }

                // add the reqs we used
                map.Requirements = reqs;
            }
            else if (reqs.MapId == MapId.EarthCaveB1)
            {
                reqs.Floor       = Tile.EarthCaveRandomEncounters;
                reqs.InRoomFloor = Tile.EarthCaveInside;
                reqs.OutOfBounds = Tile.EarthCaveOOB;
                reqs.Barrier     = Tile.EarthCaveRockA;
                reqs.FreeNPCs    = new int[] { };
                reqs.Rooms       = new List <RoomSpec> {
                };
                reqs.Portals     = new byte[] { (byte)Tile.WarpUp, 0x24 };
                reqs.Objects     = Enumerable.Range(0x42, 5).Select(x => (byte)x);
                reqs.Traps       = Enumerable.Repeat(0x1D, 3).Select(x => (byte)x);

                IMapGeneratorEngine engine = GetEngine(strategy);
                map = engine.Generate(rng, reqs);

                // add the reqs we used
                map.Requirements = reqs;
            }
            else if (reqs.MapId == MapId.EarthCaveB2)
            {
                reqs.Floor       = Tile.EarthCaveRandomEncounters;
                reqs.InRoomFloor = Tile.EarthCaveInside;
                reqs.OutOfBounds = Tile.EarthCaveOOB;
                reqs.Barrier     = Tile.EarthCaveRockA;
                reqs.FreeNPCs    = Enumerable.Range(0, 13);
                reqs.Rooms       = new List <RoomSpec> {
                };
                reqs.Portals     = new byte[] { (byte)Tile.WarpUp, 0x25 };
                reqs.Objects     = Enumerable.Range(0x47, 6).Select(x => (byte)x);
                reqs.Traps       = Enumerable.Range(0x1D, 1).Select(x => (byte)x);

                IMapGeneratorEngine engine = GetEngine(strategy);
                map = engine.Generate(rng, reqs);

                // add the reqs we used
                map.Requirements = reqs;
            }
            else
            {
                throw new ArgumentOutOfRangeException();
            }

            // Free NPC placement doesn't require the engine
            var locations = map.Map.Where(element => element.Tile == reqs.Floor).ToList();

            reqs.FreeNPCs.ToList().ForEach(npc =>
            {
                var location = locations.SpliceRandom(rng);
                reqs.Rom.MoveNpc(reqs.MapId, npc, location.X, location.Y, false, false);
            });

            if (Debugger.IsAttached)
            {
                Console.Write(map.AsText());
            }

            return(map);
        }