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