Beispiel #1
0
        public void PacifistEnd(TalkRoutines talkroutines, NPCdata npcdata, bool extendedtraptiles)
        {
            // Remove ToFR Fiends tiles
            var tilesets = Get(TilesetDataOffset, TilesetDataCount * TilesetDataSize * TilesetCount).Chunk(TilesetDataSize).ToList();

            tilesets.ForEach(tile =>
            {
                if (IsBossTrapTile(tile))
                {
                    tile[1] = (byte)(extendedtraptiles ? 0x00 : 0x80);
                }
            });
            Put(TilesetDataOffset, tilesets.SelectMany(tileset => tileset.ToBytes()).ToArray());

            // Update Chaos script
            var Talk_Ending = talkroutines.Add(Blob.FromHex("4C38C9"));

            npcdata.SetRoutine((ObjectId)0x1A, (newTalkRoutines)Talk_Ending);

            //Update Fiends, Garland, Vampire, Astos and Bikke
            var battleJump = Blob.FromHex("2020B1");
            var mapreload  = Blob.FromHex("201896");

            talkroutines.ReplaceChunk(newTalkRoutines.Talk_fight, battleJump, Blob.FromHex("EAEAEA"));
            talkroutines.ReplaceChunk(newTalkRoutines.Talk_fight, mapreload, Blob.FromHex("EAEAEA"));
            talkroutines.ReplaceChunk(newTalkRoutines.Talk_Bikke, battleJump, Blob.FromHex("EAEAEA"));
            talkroutines.ReplaceChunk(newTalkRoutines.Talk_Bikke, mapreload, Blob.FromHex("EAEAEA"));
            talkroutines.ReplaceChunk(newTalkRoutines.Talk_Astos, battleJump, Blob.FromHex("EAEAEA"));
            talkroutines.ReplaceChunk(newTalkRoutines.Talk_Astos, mapreload, Blob.FromHex("EAEAEA"));
        }
Beispiel #2
0
        private void BlackOrbChecksShardsCountFor(int goal, TalkRoutines talkroutines)
        {
            // black orb typically checks for earth($6031) fire($6032) water ($6033) wind ($6034)
            // ShiftEarthOrbDown() creates a count at $6035, and this NPC talkroutine compares the $6035 value to goal
            talkroutines.Replace(newTalkRoutines.Talk_BlackOrb, Blob.FromHex($"AD3560C9{goal:X2}300CA0CA209690E67DE67DA57160A57260"));

            // make portal under Black Orb walkable
            Remove4OrbRequirementForToFRPortal();
        }
Beispiel #3
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 #4
0
        private List <String> BlackOrbRequiresSpecificOrbs(MT19337 rng, int goal, TalkRoutines talkroutines)
        {
            List <String> availableOrbs = new List <String> {
                "earth", "fire", "water", "wind"
            };
            List <String> requiredOrbs = new List <String>();

            // choose X random orbs for goal
            for (int i = 0; i < goal; i++)
            {
                // choose random orb from available
                int orb = rng.Between(0, availableOrbs.Count - 1);

                // add to required ; remove from available
                requiredOrbs.Add(availableOrbs[orb]);
                availableOrbs.RemoveAt(orb);
            }

            List <String> requiredOrbsClone = new List <String>(requiredOrbs);           // must send copy back for spoiler text

            // change Black Orb requirement for specific orbs

            // Talk_BlackOrb:                     AD 3260 2D 3360 2D 3460 2D 3160 F00CA0CA209690E67DE67DA57160A57260
            //                                      ^fire && watr && wind && erth^
            //
            // Example that needs just water orb: AD 3360 2D 3360 2D 3360 2D 3360 F00CA0CA209690E67DE67DA57160A57260
            //                                      ^watr && watr && watr && watr^

            StringBuilder asm = new StringBuilder();

            asm.Append("AD");
            for (int i = 0; i < 4; i++)             // substituting 4 comparisons
            {
                string orbName = requiredOrbs[0];
                switch (orbName)
                {
                case "earth":
                    asm.Append("31602D");                             // 6031 AND
                    break;

                case "fire":
                    asm.Append("32602D");                             // 6032 AND
                    break;

                case "water":
                    asm.Append("33602D");                             // 6033 AND
                    break;

                case "wind":
                    asm.Append("34602D");                             // 6034 AND
                    break;
                }
                if (requiredOrbs.Count > 1)
                {
                    requiredOrbs.RemoveAt(0);
                }
            }
            asm.Remove(24, 2);                                // removes unneeded trailing "2D" from appends above
            asm.Append("F00CA0CA209690E67DE67DA57160A57260"); // trailing asm from original talkroutine
            talkroutines.Replace(newTalkRoutines.Talk_BlackOrb, Blob.FromHex(asm.ToString()));

            // make portal under Black Orb walkable
            Remove4OrbRequirementForToFRPortal();

            return(requiredOrbsClone);
        }
Beispiel #5
0
        public void SetOrbRequirement(MT19337 rng, TalkRoutines talkroutines, int orbsRequiredCount, OrbsRequiredMode mode, bool spoilersEnabled)
        {
            int goal = 0;

            switch (orbsRequiredCount)
            {
            case 4: return;                     // do nothing

            case 3: goal = 3; break;

            case 2: goal = 2; break;

            case 1: goal = 1; break;

            case 0: { goal = 0; mode = OrbsRequiredMode.Any; }  break;                     // for 0 Orbs, force Any

            case 5: goal = rng.Between(1, 3); break;
            }

            Dictionary <int, String> updatedBlackOrbDialogue = new Dictionary <int, String>();
            String orbIntro = "The ORBS now cover";

            if (goal == 1)
            {
                orbIntro = "The ORB now covers";
            }
            else if (goal == 0)
            {
                orbIntro = "You now approach";
            }
            updatedBlackOrbDialogue.Add(0x21, $"{orbIntro}\nthe black ORB..\nTo take a step forward\nis to go back 2000 years\nin time.");

            if (mode.Equals(OrbsRequiredMode.Any))
            {
                // Orb Requirement is Any 3, Any 2, Any 1, or 0

                // Modify "shift earth orb down code" that normally assigns shard values 2 for earth / fire, and 4 for water / wind
                // (now assigns shard value of 1 for all orbs; AKA modded 0F_CE12_OrbRewards.asm)
                Put(0x7CE12, Blob.FromHex("A201A000F010A201A001D00AA201A002D004A201A003B93160D00FA901993160188A6D35608D3560E66C1860"));

                // Adjust Black Orb Behavior to check $6035 for goal "shards" (in this case, the orb count)
                BlackOrbChecksShardsCountFor(goal, talkroutines);

                if (spoilersEnabled && goal != 0)
                {
                    String total = "";
                    switch (goal)
                    {
                    case 1: total = "ONE"; break;

                    case 2: total = "TWO"; break;

                    case 3: total = "THREE"; break;
                    }
                    updatedBlackOrbDialogue.Add(0x22, $"The black ORB\nwhispers ominously..\nBring me {total}.");
                }
            }
            else
            {
                // Orb Requirement is Random 3, Random 2, or Random 1

                List <String> orbsNeeded = BlackOrbRequiresSpecificOrbs(rng, goal, talkroutines);

                if (spoilersEnabled)
                {
                    String hintLine1 = "";
                    String hintLine2 = "";

                    if (orbsNeeded.Count > 1)
                    {
                        hintLine1 = "swirls colors of";
                        for (int i = 0; i < orbsNeeded.Count; i++)
                        {
                            if (i < orbsNeeded.Count - 1)
                            {
                                hintLine2 += orbsNeeded[i].ToUpper();
                                if (orbsNeeded.Count == 3)
                                {
                                    hintLine2 += ", ";
                                }
                                else
                                {
                                    hintLine2 += " ";
                                };
                            }
                            else
                            {
                                hintLine2 += "and " + orbsNeeded[i].ToUpper() + ".";
                            }
                        }
                    }
                    else
                    {
                        hintLine1 = "swirls with the";
                        hintLine2 = "color of " + orbsNeeded[0].ToUpper() + ".";
                    }
                    updatedBlackOrbDialogue.Add(0x22, $"The black ORB\n{hintLine1}\n{hintLine2}");
                }
            }
            InsertDialogs(updatedBlackOrbDialogue);
        }
Beispiel #6
0
        public void EnableShardHunt(MT19337 rng, TalkRoutines talkroutines, ShardCount count)
        {
            int goal = 16;

            switch (count)
            {
            case ShardCount.Count16: goal = 16; break;

            case ShardCount.Count20: goal = 20; break;

            case ShardCount.Count24: goal = 24; break;

            case ShardCount.Count28: goal = 28; break;

            case ShardCount.Count32: goal = 32; break;

            case ShardCount.Count36: goal = 36; break;

            case ShardCount.Range16_24: goal = rng.Between(16, 24); break;

            case ShardCount.Range24_32: goal = rng.Between(24, 32); break;

            case ShardCount.Range16_36: goal = rng.Between(16, 36); break;
            }

            string shardName = ShardNames.PickRandom(rng);

            // Replace unused CANOE string and EarthOrb pointer with whatever we're calling the scavenged item.
            ItemsText[(int)Item.Shard] = shardName;

            // Replace the upper two tiles of the unlit orb with an empty and found shard.
            // These are at tile address $76 and $77 respectively.
            Put(0x37760, Blob.FromHex("001C22414141221CFFE3DDBEBEBEDDE3001C3E7F7F7F3E1CFFFFE3CFDFDFFFFF"));

            int ppu = 0x2043;

            ppu = ppu + (goal <= 24 ? 0x20 : 0x00);

            // Fancy shard drawing code, see 0E_B8D7_DrawShardBox.asm
            Put(0x3B87D, Blob.FromHex($"A9{ppu & 0xFF:X2}8511A9{(ppu & 0xFF00) >> 8:X2}8512A977A00048AD0220A5128D0620A51118692085118D0620900DAD0220E612A5128D0620A5118D062068A200CC3560D002A976C0{goal:X2}D001608D0720C8E8E006D0EB1890C1"));

            // Black Orb Override to check for shards rather than ORBs.
            BlackOrbChecksShardsCountFor(goal, talkroutines);

            // A little narrative overhaul.
            Blob intro = FF1Text.TextToStory(new string[]
            {
                "The Time Loop has reopened!", "",
                "The ORBS have been smashed!", "", "", "",
                $"The resulting {shardName}S were", "",
                "stolen and scattered around", "",
                "the world to distract while", "",
                "this new evil incubates....", "", "", "",
                "But The Light Warriors return!", "",
                $"They will need {goal} {shardName}S", "",
                "to restore the BLACK ORB and", "",
                "confront this new malevolence.",
            });

            System.Diagnostics.Debug.Assert(intro.Length <= 208);
            Put(0x37F20, intro);

            InsertDialogs(new Dictionary <int, string>()
            {
                { 0x21, $"The {shardName}S coalesce to\nrestore the Black ORB.\n\nBrave Light Warriors....\nDestroy the Evil within!" },                 // Black Orb Text
                { 0x2E, $"Ah, the Light Warriors!\n\nSo you have collected\nthe {shardName}S and restored\nthe BLACK ORB." },
                { 0x2F, "Thus you've travelled\n2000 years into the past\nto try to stop me?\n\nStep forward then,\nto your peril!" },
                { 0x30, "Oh, Light Warriors!\nSuch arrogant bravery.\n\nLet us see whom history\nremembers. En Garde!" },
            });
        }