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