From 7825b5ef0f09a877142ea1eb221e895bb60a0253 Mon Sep 17 00:00:00 2001 From: ElectroDeoxys Date: Mon, 27 Sep 2021 11:56:10 +0100 Subject: Split bank 6 --- src/data/duel/effect_commands.asm | 1619 --------- src/engine/auto_deck_machines.asm | 191 ++ src/engine/bank03.asm | 4 +- src/engine/bank06.asm | 5974 --------------------------------- src/engine/copy_card_name.asm | 152 + src/engine/duel/animations.asm | 354 ++ src/engine/duel/core.asm | 2 +- src/engine/duel/effect_commands.asm | 1619 +++++++++ src/engine/game_loop.asm | 2 +- src/engine/input_name.asm | 1417 ++++++++ src/engine/link/card_pop.asm | 399 +++ src/engine/link/ir_core.asm | 531 +++ src/engine/link/ir_functions.asm | 323 ++ src/engine/link/link_duel.asm | 179 + src/engine/link/printer.asm | 1124 +++++++ src/engine/menus/booster_pack.asm | 42 + src/engine/menus/common.asm | 8 +- src/engine/menus/glossary.asm | 221 ++ src/engine/menus/play_area.asm | 570 ++++ src/engine/menus/unknown.asm | 103 + src/engine/promotional_card.asm | 61 + src/engine/sprite_vblank.asm | 39 + src/engine/starter_deck.asm | 182 + src/engine/unused_save_validation.asm | 96 + src/layout.link | 15 +- src/main.asm | 45 +- src/sram.asm | 2 +- src/text/text1.asm | 2 +- src/text/text_offsets.asm | 2 +- 29 files changed, 7669 insertions(+), 7609 deletions(-) delete mode 100644 src/data/duel/effect_commands.asm create mode 100644 src/engine/auto_deck_machines.asm delete mode 100644 src/engine/bank06.asm create mode 100644 src/engine/copy_card_name.asm create mode 100644 src/engine/duel/animations.asm create mode 100644 src/engine/duel/effect_commands.asm create mode 100644 src/engine/input_name.asm create mode 100644 src/engine/link/card_pop.asm create mode 100644 src/engine/link/ir_core.asm create mode 100644 src/engine/link/ir_functions.asm create mode 100644 src/engine/link/link_duel.asm create mode 100644 src/engine/link/printer.asm create mode 100644 src/engine/menus/booster_pack.asm create mode 100644 src/engine/menus/glossary.asm create mode 100644 src/engine/menus/play_area.asm create mode 100644 src/engine/menus/unknown.asm create mode 100644 src/engine/promotional_card.asm create mode 100644 src/engine/sprite_vblank.asm create mode 100644 src/engine/starter_deck.asm create mode 100644 src/engine/unused_save_validation.asm diff --git a/src/data/duel/effect_commands.asm b/src/data/duel/effect_commands.asm deleted file mode 100644 index e96ef7e..0000000 --- a/src/data/duel/effect_commands.asm +++ /dev/null @@ -1,1619 +0,0 @@ -EffectCommands: ; 186f7 (6:46f7) -; Each attack has a two-byte effect pointer (attack's 7th param) that points to one of these structures. -; Similarly, trainer cards have a two-byte pointer (7th param) to one of these structures, which determines the card's function. -; Energy cards also point to one of these, but their data is just $00. -; db EFFECTCMDTYPE_* ($01 - $0a) -; dw Function -; ... -; db $00 - -; Commands are associated to a time or a scope (EFFECTCMDTYPE_*) that determines when their function is executed during the turn. -; - EFFECTCMDTYPE_INITIAL_EFFECT_1: Executed right after attack or trainer card is used. Bypasses Smokescreen and Sand Attack effects. -; - EFFECTCMDTYPE_INITIAL_EFFECT_2: Executed right after attack, Pokemon Power, or trainer card is used. -; - EFFECTCMDTYPE_DISCARD_ENERGY: For attacks or trainer cards that require putting one or more attached energy cards into the discard pile. -; - EFFECTCMDTYPE_REQUIRE_SELECTION: For attacks, Pokemon Powers, or trainer cards requiring the user to select a card (from e.g. play area screen or card list). -; - EFFECTCMDTYPE_BEFORE_DAMAGE: Effect command of an attack executed prior to the damage step. For trainer card or Pokemon Power, usually the main effect. -; - EFFECTCMDTYPE_AFTER_DAMAGE: Effect command executed after the damage step. -; - EFFECTCMDTYPE_AI_SWITCH_DEFENDING_PKMN: For attacks that may result in the defending Pokemon being switched out. Called only for AI-executed attacks. -; - EFFECTCMDTYPE_PKMN_POWER_TRIGGER: Pokemon Power effects that trigger the moment the Pokemon card is played. -; - EFFECTCMDTYPE_AI: Used for AI scoring. -; - EFFECTCMDTYPE_AI_SELECTION: When AI is required to select a card - -; Attacks that have an EFFECTCMDTYPE_REQUIRE_SELECTION also must have either an EFFECTCMDTYPE_AI_SWITCH_DEFENDING_PKMN or an -; EFFECTCMDTYPE_AI_SELECTION (for anything not involving switching the defending Pokemon), to handle selections involving the AI. - -; Similar attack effects of different Pokemon cards all point to a different command list, -; even though in some cases their commands and function pointers match. - -; Function name examples -; PoisonEffect ; generic effect shared by multiple attacks. -; Paralysis50PercentEffect ; -; KakunaStiffenEffect ; unique effect from an attack known by multiple cards. -; MetapodStiffenEffect ; -; AcidEffect ; unique effect from an attack known by a single card -; FoulOdorEffect ; -; SpitPoison_Poison50PercentEffect ; unique effect made of more than one command. -; SpitPoison_AIEffect ; - -EkansSpitPoisonEffectCommands: - dbw EFFECTCMDTYPE_BEFORE_DAMAGE, SpitPoison_Poison50PercentEffect - dbw EFFECTCMDTYPE_AI, SpitPoison_AIEffect - db $00 - -EkansWrapEffectCommands: - dbw EFFECTCMDTYPE_BEFORE_DAMAGE, Paralysis50PercentEffect - db $00 - -ArbokTerrorStrikeEffectCommands: - dbw EFFECTCMDTYPE_AFTER_DAMAGE, TerrorStrike_SwitchDefendingPokemon - dbw EFFECTCMDTYPE_REQUIRE_SELECTION, TerrorStrike_50PercentSelectSwitchPokemon - dbw EFFECTCMDTYPE_AI_SWITCH_DEFENDING_PKMN, TerrorStrike_50PercentSelectSwitchPokemon - db $00 - -ArbokPoisonFangEffectCommands: - dbw EFFECTCMDTYPE_BEFORE_DAMAGE, PoisonEffect - dbw EFFECTCMDTYPE_AI, PoisonFang_AIEffect - db $00 - -WeepinbellPoisonPowderEffectCommands: - dbw EFFECTCMDTYPE_BEFORE_DAMAGE, Poison50PercentEffect - dbw EFFECTCMDTYPE_AI, WeepinbellPoisonPowder_AIEffect - db $00 - -VictreebelLureEffectCommands: - dbw EFFECTCMDTYPE_INITIAL_EFFECT_1, VictreebelLure_AssertPokemonInBench - dbw EFFECTCMDTYPE_AFTER_DAMAGE, VictreebelLure_SwitchDefendingPokemon - dbw EFFECTCMDTYPE_REQUIRE_SELECTION, VictreebelLure_SelectSwitchPokemon - dbw EFFECTCMDTYPE_AI_SELECTION, VictreebelLure_GetBenchPokemonWithLowestHP - db $00 - -VictreebelAcidEffectCommands: - dbw EFFECTCMDTYPE_BEFORE_DAMAGE, AcidEffect - db $00 - -PinsirIronGripEffectCommands: - dbw EFFECTCMDTYPE_BEFORE_DAMAGE, Paralysis50PercentEffect - db $00 - -CaterpieStringShotEffectCommands: - dbw EFFECTCMDTYPE_BEFORE_DAMAGE, Paralysis50PercentEffect - db $00 - -GloomPoisonPowderEffectCommands: - dbw EFFECTCMDTYPE_BEFORE_DAMAGE, PoisonEffect - dbw EFFECTCMDTYPE_AI, GloomPoisonPowder_AIEffect - db $00 - -GloomFoulOdorEffectCommands: - dbw EFFECTCMDTYPE_BEFORE_DAMAGE, FoulOdorEffect - db $00 - -KakunaStiffenEffectCommands: - dbw EFFECTCMDTYPE_BEFORE_DAMAGE, KakunaStiffenEffect - db $00 - -KakunaPoisonPowderEffectCommands: - dbw EFFECTCMDTYPE_BEFORE_DAMAGE, Poison50PercentEffect - dbw EFFECTCMDTYPE_AI, KakunaPoisonPowder_AIEffect - db $00 - -GolbatLeechLifeEffectCommands: - dbw EFFECTCMDTYPE_AFTER_DAMAGE, GolbatLeechLifeEffect - db $00 - -VenonatStunSporeEffectCommands: - dbw EFFECTCMDTYPE_BEFORE_DAMAGE, Paralysis50PercentEffect - db $00 - -VenonatLeechLifeEffectCommands: - dbw EFFECTCMDTYPE_AFTER_DAMAGE, VenonatLeechLifeEffect - db $00 - -ScytherSwordsDanceEffectCommands: - dbw EFFECTCMDTYPE_BEFORE_DAMAGE, SwordsDanceEffect - db $00 - -ZubatSupersonicEffectCommands: - dbw EFFECTCMDTYPE_BEFORE_DAMAGE, ZubatSupersonicEffect - db $00 - -ZubatLeechLifeEffectCommands: - dbw EFFECTCMDTYPE_AFTER_DAMAGE, ZubatLeechLifeEffect - db $00 - -BeedrillTwineedleEffectCommands: - dbw EFFECTCMDTYPE_BEFORE_DAMAGE, Twineedle_MultiplierEffect - dbw EFFECTCMDTYPE_AI, Twineedle_AIEffect - db $00 - -BeedrillPoisonStingEffectCommands: - dbw EFFECTCMDTYPE_BEFORE_DAMAGE, Poison50PercentEffect - dbw EFFECTCMDTYPE_AI, BeedrillPoisonSting_AIEffect - db $00 - -ExeggcuteHypnosisEffectCommands: - dbw EFFECTCMDTYPE_BEFORE_DAMAGE, SleepEffect - db $00 - -ExeggcuteLeechSeedEffectCommands: - dbw EFFECTCMDTYPE_AFTER_DAMAGE, ExeggcuteLeechSeedEffect - db $00 - -KoffingFoulGasEffectCommands: - dbw EFFECTCMDTYPE_BEFORE_DAMAGE, FoulGas_PoisonOrConfusionEffect - dbw EFFECTCMDTYPE_AI, FoulGas_AIEffect - db $00 - -MetapodStiffenEffectCommands: - dbw EFFECTCMDTYPE_BEFORE_DAMAGE, MetapodStiffenEffect - db $00 - -MetapodStunSporeEffectCommands: - dbw EFFECTCMDTYPE_BEFORE_DAMAGE, Paralysis50PercentEffect - db $00 - -OddishStunSporeEffectCommands: - dbw EFFECTCMDTYPE_BEFORE_DAMAGE, Paralysis50PercentEffect - db $00 - -OddishSproutEffectCommands: - dbw EFFECTCMDTYPE_INITIAL_EFFECT_1, Sprout_CheckDeckAndPlayArea - dbw EFFECTCMDTYPE_AFTER_DAMAGE, Sprout_PutInPlayAreaEffect - dbw EFFECTCMDTYPE_REQUIRE_SELECTION, Sprout_PlayerSelectEffect - dbw EFFECTCMDTYPE_AI_SELECTION, Sprout_AISelectEffect - db $00 - -ExeggutorTeleportEffectCommands: - dbw EFFECTCMDTYPE_INITIAL_EFFECT_1, Teleport_CheckBench - dbw EFFECTCMDTYPE_AFTER_DAMAGE, Teleport_SwitchEffect - dbw EFFECTCMDTYPE_REQUIRE_SELECTION, Teleport_PlayerSelectEffect - dbw EFFECTCMDTYPE_AI_SELECTION, Teleport_AISelectEffect - db $00 - -ExeggutorBigEggsplosionEffectCommands: - dbw EFFECTCMDTYPE_BEFORE_DAMAGE, BigEggsplosion_MultiplierEffect - dbw EFFECTCMDTYPE_AI, BigEggsplosion_AIEffect - db $00 - -NidokingThrashEffectCommands: - dbw EFFECTCMDTYPE_BEFORE_DAMAGE, Thrash_ModifierEffect - dbw EFFECTCMDTYPE_AFTER_DAMAGE, Thrash_RecoilEffect - dbw EFFECTCMDTYPE_AI, Thrash_AIEffect - db $00 - -NidokingToxicEffectCommands: - dbw EFFECTCMDTYPE_BEFORE_DAMAGE, Toxic_DoublePoisonEffect - dbw EFFECTCMDTYPE_AI, Toxic_AIEffect - db $00 - -NidoqueenBoyfriendsEffectCommands: - dbw EFFECTCMDTYPE_BEFORE_DAMAGE, BoyfriendsEffect - db $00 - -NidoranFFurySwipesEffectCommands: - dbw EFFECTCMDTYPE_BEFORE_DAMAGE, NidoranFFurySwipes_MultiplierEffect - dbw EFFECTCMDTYPE_AI, NidoranFFurySwipes_AIEffect - db $00 - -NidoranFCallForFamilyEffectCommands: - dbw EFFECTCMDTYPE_INITIAL_EFFECT_1, NidoranFCallForFamily_CheckDeckAndPlayArea - dbw EFFECTCMDTYPE_AFTER_DAMAGE, NidoranFCallForFamily_PutInPlayAreaEffect - dbw EFFECTCMDTYPE_REQUIRE_SELECTION, NidoranFCallForFamily_PlayerSelectEffect - dbw EFFECTCMDTYPE_AI_SELECTION, NidoranFCallForFamily_AISelectEffect - db $00 - -NidoranMHornHazardEffectCommands: - dbw EFFECTCMDTYPE_BEFORE_DAMAGE, HornHazard_NoDamage50PercentEffect - dbw EFFECTCMDTYPE_AI, HornHazard_AIEffect - db $00 - -NidorinaSupersonicEffectCommands: - dbw EFFECTCMDTYPE_BEFORE_DAMAGE, NidorinaSupersonicEffect - db $00 - -NidorinaDoubleKickEffectCommands: - dbw EFFECTCMDTYPE_BEFORE_DAMAGE, NidorinaDoubleKick_MultiplierEffect - dbw EFFECTCMDTYPE_AI, NidorinaDoubleKick_AIEffect - db $00 - -NidorinoDoubleKickEffectCommands: - dbw EFFECTCMDTYPE_BEFORE_DAMAGE, NidorinoDoubleKick_MultiplierEffect - dbw EFFECTCMDTYPE_AI, NidorinoDoubleKick_AIEffect - db $00 - -ButterfreeWhirlwindEffectCommands: - dbw EFFECTCMDTYPE_AFTER_DAMAGE, ButterfreeWhirlwind_SwitchEffect - dbw EFFECTCMDTYPE_REQUIRE_SELECTION, ButterfreeWhirlwind_CheckBench - dbw EFFECTCMDTYPE_AI_SWITCH_DEFENDING_PKMN, ButterfreeWhirlwind_CheckBench - db $00 - -ButterfreeMegaDrainEffectCommands: - dbw EFFECTCMDTYPE_AFTER_DAMAGE, ButterfreeMegaDrainEffect - db $00 - -ParasSporeEffectCommands: - dbw EFFECTCMDTYPE_BEFORE_DAMAGE, SleepEffect - db $00 - -ParasectSporeEffectCommands: - dbw EFFECTCMDTYPE_BEFORE_DAMAGE, SleepEffect - db $00 - -WeedlePoisonStingEffectCommands: - dbw EFFECTCMDTYPE_BEFORE_DAMAGE, Poison50PercentEffect - dbw EFFECTCMDTYPE_AI, WeedlePoisonSting_AIEffect - db $00 - -IvysaurPoisonPowderEffectCommands: - dbw EFFECTCMDTYPE_BEFORE_DAMAGE, PoisonEffect - dbw EFFECTCMDTYPE_AI, IvysaurPoisonPowder_AIEffect - db $00 - -BulbasaurLeechSeedEffectCommands: - dbw EFFECTCMDTYPE_AFTER_DAMAGE, BulbasaurLeechSeedEffect - db $00 - -VenusaurEnergyTransEffectCommands: - dbw EFFECTCMDTYPE_INITIAL_EFFECT_2, EnergyTrans_CheckPlayArea - dbw EFFECTCMDTYPE_BEFORE_DAMAGE, EnergyTrans_TransferEffect - dbw EFFECTCMDTYPE_AFTER_DAMAGE, EnergyTrans_AIEffect - dbw EFFECTCMDTYPE_REQUIRE_SELECTION, EnergyTrans_PrintProcedure - db $00 - -GrimerNastyGooEffectCommands: - dbw EFFECTCMDTYPE_BEFORE_DAMAGE, Paralysis50PercentEffect - db $00 - -GrimerMinimizeEffectCommands: - dbw EFFECTCMDTYPE_BEFORE_DAMAGE, GrimerMinimizeEffect - db $00 - -MukToxicGasEffectCommands: - dbw EFFECTCMDTYPE_INITIAL_EFFECT_1, ToxicGasEffect - db $00 - -MukSludgeEffectCommands: - dbw EFFECTCMDTYPE_BEFORE_DAMAGE, Poison50PercentEffect - dbw EFFECTCMDTYPE_AI, Sludge_AIEffect - db $00 - -BellsproutCallForFamilyEffectCommands: - dbw EFFECTCMDTYPE_INITIAL_EFFECT_1, BellsproutCallForFamily_CheckDeckAndPlayArea - dbw EFFECTCMDTYPE_AFTER_DAMAGE, BellsproutCallForFamily_PutInPlayAreaEffect - dbw EFFECTCMDTYPE_REQUIRE_SELECTION, BellsproutCallForFamily_PlayerSelectEffect - dbw EFFECTCMDTYPE_AI_SELECTION, BellsproutCallForFamily_AISelectEffect - db $00 - -WeezingSmogEffectCommands: - dbw EFFECTCMDTYPE_BEFORE_DAMAGE, Poison50PercentEffect - dbw EFFECTCMDTYPE_AI, WeezingSmog_AIEffect - db $00 - -WeezingSelfdestructEffectCommands: - dbw EFFECTCMDTYPE_AFTER_DAMAGE, WeezingSelfdestructEffect - db $00 - -VenomothShiftEffectCommands: - dbw EFFECTCMDTYPE_INITIAL_EFFECT_2, Shift_OncePerTurnCheck - dbw EFFECTCMDTYPE_BEFORE_DAMAGE, Shift_ChangeColorEffect - dbw EFFECTCMDTYPE_REQUIRE_SELECTION, Shift_PlayerSelectEffect - db $00 - -VenomothVenomPowderEffectCommands: - dbw EFFECTCMDTYPE_BEFORE_DAMAGE, VenomPowder_PoisonConfusion50PercentEffect - dbw EFFECTCMDTYPE_AI, VenomPowder_AIEffect - db $00 - -TangelaBindEffectCommands: - dbw EFFECTCMDTYPE_BEFORE_DAMAGE, Paralysis50PercentEffect - db $00 - -TangelaPoisonPowderEffectCommands: - dbw EFFECTCMDTYPE_BEFORE_DAMAGE, PoisonEffect - dbw EFFECTCMDTYPE_AI, TangelaPoisonPowder_AIEffect - db $00 - -VileplumeHealEffectCommands: - dbw EFFECTCMDTYPE_INITIAL_EFFECT_2, Heal_OncePerTurnCheck - dbw EFFECTCMDTYPE_BEFORE_DAMAGE, Heal_RemoveDamageEffect - db $00 - -VileplumePetalDanceEffectCommands: - dbw EFFECTCMDTYPE_BEFORE_DAMAGE, PetalDance_MultiplierEffect - dbw EFFECTCMDTYPE_AI, PetalDance_AIEffect - db $00 - -TangelaStunSporeEffectCommands: - dbw EFFECTCMDTYPE_BEFORE_DAMAGE, Paralysis50PercentEffect - db $00 - -TangelaPoisonWhipEffectCommands: - dbw EFFECTCMDTYPE_BEFORE_DAMAGE, PoisonEffect - dbw EFFECTCMDTYPE_AI, PoisonWhip_AIEffect - db $00 - -VenusaurSolarPowerEffectCommands: - dbw EFFECTCMDTYPE_INITIAL_EFFECT_2, SolarPower_CheckUse - dbw EFFECTCMDTYPE_BEFORE_DAMAGE, SolarPower_RemoveStatusEffect - db $00 - -VenusaurMegaDrainEffectCommands: - dbw EFFECTCMDTYPE_AFTER_DAMAGE, VenusaurMegaDrainEffect - db $00 - -OmastarWaterGunEffectCommands: - dbw EFFECTCMDTYPE_BEFORE_DAMAGE, OmastarWaterGunEffect - dbw EFFECTCMDTYPE_AI, OmastarWaterGunEffect - db $00 - -OmastarSpikeCannonEffectCommands: - dbw EFFECTCMDTYPE_BEFORE_DAMAGE, OmastarSpikeCannon_MultiplierEffect - dbw EFFECTCMDTYPE_AI, OmastarSpikeCannon_AIEffect - db $00 - -OmanyteClairvoyanceEffectCommands: - dbw EFFECTCMDTYPE_INITIAL_EFFECT_1, ClairvoyanceEffect - db $00 - -OmanyteWaterGunEffectCommands: - dbw EFFECTCMDTYPE_BEFORE_DAMAGE, OmanyteWaterGunEffect - dbw EFFECTCMDTYPE_AI, OmanyteWaterGunEffect - db $00 - -WartortleWithdrawEffectCommands: - dbw EFFECTCMDTYPE_BEFORE_DAMAGE, WartortleWithdrawEffect - db $00 - -BlastoiseRainDanceEffectCommands: - dbw EFFECTCMDTYPE_INITIAL_EFFECT_1, RainDanceEffect - db $00 - -BlastoiseHydroPumpEffectCommands: - dbw EFFECTCMDTYPE_BEFORE_DAMAGE, HydroPumpEffect - dbw EFFECTCMDTYPE_AI, HydroPumpEffect - db $00 - -GyaradosBubblebeamEffectCommands: - dbw EFFECTCMDTYPE_BEFORE_DAMAGE, Paralysis50PercentEffect - db $00 - -KinglerFlailEffectCommands: - dbw EFFECTCMDTYPE_BEFORE_DAMAGE, KinglerFlail_HPCheck - dbw EFFECTCMDTYPE_AI, KinglerFlail_AIEffect - db $00 - -KrabbyCallForFamilyEffectCommands: - dbw EFFECTCMDTYPE_INITIAL_EFFECT_1, KrabbyCallForFamily_CheckDeckAndPlayArea - dbw EFFECTCMDTYPE_AFTER_DAMAGE, KrabbyCallForFamily_PutInPlayAreaEffect - dbw EFFECTCMDTYPE_REQUIRE_SELECTION, KrabbyCallForFamily_PlayerSelectEffect - dbw EFFECTCMDTYPE_AI_SELECTION, KrabbyCallForFamily_AISelectEffect - db $00 - -MagikarpFlailEffectCommands: - dbw EFFECTCMDTYPE_BEFORE_DAMAGE, MagikarpFlail_HPCheck - dbw EFFECTCMDTYPE_AI, MagikarpFlail_AIEffect - db $00 - -PsyduckHeadacheEffectCommands: - dbw EFFECTCMDTYPE_BEFORE_DAMAGE, HeadacheEffect - db $00 - -PsyduckFurySwipesEffectCommands: - dbw EFFECTCMDTYPE_BEFORE_DAMAGE, PsyduckFurySwipes_MultiplierEffect - dbw EFFECTCMDTYPE_AI, PsyduckFurySwipes_AIEffect - db $00 - -GolduckPsyshockEffectCommands: - dbw EFFECTCMDTYPE_BEFORE_DAMAGE, Paralysis50PercentEffect - db $00 - -GolduckHyperBeamEffectCommands: - dbw EFFECTCMDTYPE_AFTER_DAMAGE, GolduckHyperBeam_DiscardEffect - dbw EFFECTCMDTYPE_REQUIRE_SELECTION, GolduckHyperBeam_PlayerSelectEffect - dbw EFFECTCMDTYPE_AI_SELECTION, GolduckHyperBeam_AISelectEffect - db $00 - -SeadraWaterGunEffectCommands: - dbw EFFECTCMDTYPE_BEFORE_DAMAGE, SeadraWaterGunEffect - dbw EFFECTCMDTYPE_AI, SeadraWaterGunEffect - db $00 - -SeadraAgilityEffectCommands: - dbw EFFECTCMDTYPE_BEFORE_DAMAGE, SeadraAgilityEffect - db $00 - -ShellderSupersonicEffectCommands: - dbw EFFECTCMDTYPE_BEFORE_DAMAGE, ShellderSupersonicEffect - db $00 - -ShellderHideInShellEffectCommands: - dbw EFFECTCMDTYPE_BEFORE_DAMAGE, HideInShellEffect - db $00 - -VaporeonQuickAttackEffectCommands: - dbw EFFECTCMDTYPE_BEFORE_DAMAGE, VaporeonQuickAttack_DamageBoostEffect - dbw EFFECTCMDTYPE_AI, VaporeonQuickAttack_AIEffect - db $00 - -VaporeonWaterGunEffectCommands: - dbw EFFECTCMDTYPE_BEFORE_DAMAGE, VaporeonWaterGunEffect - dbw EFFECTCMDTYPE_AI, VaporeonWaterGunEffect - db $00 - -DewgongIceBeamEffectCommands: - dbw EFFECTCMDTYPE_BEFORE_DAMAGE, Paralysis50PercentEffect - db $00 - -StarmieRecoverEffectCommands: - dbw EFFECTCMDTYPE_INITIAL_EFFECT_1, StarmieRecover_CheckEnergyHP - dbw EFFECTCMDTYPE_INITIAL_EFFECT_2, StarmieRecover_PlayerSelectEffect - dbw EFFECTCMDTYPE_AFTER_DAMAGE, StarmieRecover_HealEffect - dbw EFFECTCMDTYPE_DISCARD_ENERGY, StarmieRecover_DiscardEffect - dbw EFFECTCMDTYPE_AI_SELECTION, StarmieRecover_AISelectEffect - db $00 - -StarmieStarFreezeEffectCommands: - dbw EFFECTCMDTYPE_BEFORE_DAMAGE, Paralysis50PercentEffect - db $00 - -SquirtleBubbleEffectCommands: - dbw EFFECTCMDTYPE_BEFORE_DAMAGE, Paralysis50PercentEffect - db $00 - -SquirtleWithdrawEffectCommands: - dbw EFFECTCMDTYPE_BEFORE_DAMAGE, SquirtleWithdrawEffect - db $00 - -HorseaSmokescreenEffectCommands: - dbw EFFECTCMDTYPE_BEFORE_DAMAGE, HorseaSmokescreenEffect - db $00 - -TentacruelSupersonicEffectCommands: - dbw EFFECTCMDTYPE_BEFORE_DAMAGE, TentacruelSupersonicEffect - db $00 - -TentacruelJellyfishStingEffectCommands: - dbw EFFECTCMDTYPE_BEFORE_DAMAGE, PoisonEffect - dbw EFFECTCMDTYPE_AI, JellyfishSting_AIEffect - db $00 - -PoliwhirlAmnesiaEffectCommands: - dbw EFFECTCMDTYPE_INITIAL_EFFECT_1, PoliwhirlAmnesia_CheckAttacks - dbw EFFECTCMDTYPE_INITIAL_EFFECT_2, PoliwhirlAmnesia_PlayerSelectEffect - dbw EFFECTCMDTYPE_BEFORE_DAMAGE, PoliwhirlAmnesia_DisableEffect - dbw EFFECTCMDTYPE_AI_SELECTION, PoliwhirlAmnesia_AISelectEffect - db $00 - -PoliwhirlDoubleslapEffectCommands: - dbw EFFECTCMDTYPE_BEFORE_DAMAGE, PoliwhirlDoubleslap_MultiplierEffect - dbw EFFECTCMDTYPE_AI, PoliwhirlDoubleslap_AIEffect - db $00 - -PoliwrathWaterGunEffectCommands: - dbw EFFECTCMDTYPE_BEFORE_DAMAGE, PoliwrathWaterGunEffect - dbw EFFECTCMDTYPE_AI, PoliwrathWaterGunEffect - db $00 - -PoliwrathWhirlpoolEffectCommands: - dbw EFFECTCMDTYPE_AFTER_DAMAGE, Whirlpool_DiscardEffect - dbw EFFECTCMDTYPE_REQUIRE_SELECTION, Whirlpool_PlayerSelectEffect - dbw EFFECTCMDTYPE_AI_SELECTION, Whirlpool_AISelectEffect - db $00 - -PoliwagWaterGunEffectCommands: - dbw EFFECTCMDTYPE_BEFORE_DAMAGE, PoliwagWaterGunEffect - dbw EFFECTCMDTYPE_AI, PoliwagWaterGunEffect - db $00 - -CloysterClampEffectCommands: - dbw EFFECTCMDTYPE_BEFORE_DAMAGE, ClampEffect - db $00 - -CloysterSpikeCannonEffectCommands: - dbw EFFECTCMDTYPE_BEFORE_DAMAGE, CloysterSpikeCannon_MultiplierEffect - dbw EFFECTCMDTYPE_AI, CloysterSpikeCannon_AIEffect - db $00 - -ArticunoFreezeDryEffectCommands: - dbw EFFECTCMDTYPE_BEFORE_DAMAGE, Paralysis50PercentEffect - db $00 - -ArticunoBlizzardEffectCommands: - dbw EFFECTCMDTYPE_BEFORE_DAMAGE, Blizzard_BenchDamage50PercentEffect - dbw EFFECTCMDTYPE_AFTER_DAMAGE, Blizzard_BenchDamageEffect - db $00 - -TentacoolCowardiceEffectCommands: - dbw EFFECTCMDTYPE_INITIAL_EFFECT_2, Cowardice_Check - dbw EFFECTCMDTYPE_BEFORE_DAMAGE, Cowardice_RemoveFromPlayAreaEffect - dbw EFFECTCMDTYPE_REQUIRE_SELECTION, Cowardice_PlayerSelectEffect - db $00 - -LaprasWaterGunEffectCommands: - dbw EFFECTCMDTYPE_BEFORE_DAMAGE, LaprasWaterGunEffect - dbw EFFECTCMDTYPE_AI, LaprasWaterGunEffect - db $00 - -LaprasConfuseRayEffectCommands: - dbw EFFECTCMDTYPE_BEFORE_DAMAGE, Confusion50PercentEffect - db $00 - -ArticunoQuickfreezeEffectCommands: - dbw EFFECTCMDTYPE_INITIAL_EFFECT_1, Quickfreeze_InitialEffect - dbw EFFECTCMDTYPE_PKMN_POWER_TRIGGER, Quickfreeze_Paralysis50PercentEffect - db $00 - -ArticunoIceBreathEffectCommands: - dbw EFFECTCMDTYPE_BEFORE_DAMAGE, IceBreath_ZeroDamage - dbw EFFECTCMDTYPE_AFTER_DAMAGE, IceBreath_RandomPokemonDamageEffect - db $00 - -VaporeonFocusEnergyEffectCommands: - dbw EFFECTCMDTYPE_BEFORE_DAMAGE, FocusEnergyEffect - db $00 - -ArcanineFlamethrowerEffectCommands: - dbw EFFECTCMDTYPE_INITIAL_EFFECT_1, ArcanineFlamethrower_CheckEnergy - dbw EFFECTCMDTYPE_INITIAL_EFFECT_2, ArcanineFlamethrower_PlayerSelectEffect - dbw EFFECTCMDTYPE_DISCARD_ENERGY, ArcanineFlamethrower_DiscardEffect - dbw EFFECTCMDTYPE_AI_SELECTION, ArcanineFlamethrower_AISelectEffect - db $00 - -ArcanineTakeDownEffectCommands: - dbw EFFECTCMDTYPE_AFTER_DAMAGE, TakeDownEffect - db $00 - -ArcanineQuickAttackEffectCommands: - dbw EFFECTCMDTYPE_BEFORE_DAMAGE, ArcanineQuickAttack_DamageBoostEffect - dbw EFFECTCMDTYPE_AI, ArcanineQuickAttack_AIEffect - db $00 - -ArcanineFlamesOfRageEffectCommands: - dbw EFFECTCMDTYPE_INITIAL_EFFECT_1, FlamesOfRage_CheckEnergy - dbw EFFECTCMDTYPE_INITIAL_EFFECT_2, FlamesOfRage_PlayerSelectEffect - dbw EFFECTCMDTYPE_BEFORE_DAMAGE, FlamesOfRage_DamageBoostEffect - dbw EFFECTCMDTYPE_DISCARD_ENERGY, FlamesOfRage_DiscardEffect - dbw EFFECTCMDTYPE_AI_SELECTION, FlamesOfRage_AISelectEffect - dbw EFFECTCMDTYPE_AI, FlamesOfRage_AIEffect - db $00 - -RapidashStompEffectCommands: - dbw EFFECTCMDTYPE_BEFORE_DAMAGE, RapidashStomp_DamageBoostEffect - dbw EFFECTCMDTYPE_AI, RapidashStomp_AIEffect - db $00 - -RapidashAgilityEffectCommands: - dbw EFFECTCMDTYPE_BEFORE_DAMAGE, RapidashAgilityEffect - db $00 - -NinetalesLureEffectCommands: - dbw EFFECTCMDTYPE_INITIAL_EFFECT_1, NinetalesLure_CheckBench - dbw EFFECTCMDTYPE_AFTER_DAMAGE, NinetalesLure_SwitchEffect - dbw EFFECTCMDTYPE_REQUIRE_SELECTION, NinetalesLure_PlayerSelectEffect - dbw EFFECTCMDTYPE_AI_SELECTION, NinetalesLure_AISelectEffect - db $00 - -NinetalesFireBlastEffectCommands: - dbw EFFECTCMDTYPE_INITIAL_EFFECT_1, FireBlast_CheckEnergy - dbw EFFECTCMDTYPE_INITIAL_EFFECT_2, FireBlast_PlayerSelectEffect - dbw EFFECTCMDTYPE_DISCARD_ENERGY, FireBlast_DiscardEffect - dbw EFFECTCMDTYPE_AI_SELECTION, FireBlast_AISelectEffect - db $00 - -CharmanderEmberEffectCommands: - dbw EFFECTCMDTYPE_INITIAL_EFFECT_1, Ember_CheckEnergy - dbw EFFECTCMDTYPE_INITIAL_EFFECT_2, Ember_PlayerSelectEffect - dbw EFFECTCMDTYPE_DISCARD_ENERGY, Ember_DiscardEffect - dbw EFFECTCMDTYPE_AI_SELECTION, Ember_AISelectEffect - db $00 - -MoltresWildfireEffectCommands: - dbw EFFECTCMDTYPE_INITIAL_EFFECT_1, Wildfire_CheckEnergy - dbw EFFECTCMDTYPE_INITIAL_EFFECT_2, Wildfire_PlayerSelectEffect - dbw EFFECTCMDTYPE_AFTER_DAMAGE, Wildfire_DiscardDeckEffect - dbw EFFECTCMDTYPE_DISCARD_ENERGY, Wildfire_DiscardEnergyEffect - dbw EFFECTCMDTYPE_AI_SELECTION, Wildfire_AISelectEffect - db $00 - -Moltres1DiveBombEffectCommands: - dbw EFFECTCMDTYPE_BEFORE_DAMAGE, Moltres1DiveBomb_Success50PercentEffect - dbw EFFECTCMDTYPE_AI, Moltres1DiveBomb_AIEffect - db $00 - -FlareonQuickAttackEffectCommands: - dbw EFFECTCMDTYPE_BEFORE_DAMAGE, FlareonQuickAttack_DamageBoostEffect - dbw EFFECTCMDTYPE_AI, FlareonQuickAttack_AIEffect - db $00 - -FlareonFlamethrowerEffectCommands: - dbw EFFECTCMDTYPE_INITIAL_EFFECT_1, FlareonFlamethrower_CheckEnergy - dbw EFFECTCMDTYPE_INITIAL_EFFECT_2, FlareonFlamethrower_PlayerSelectEffect - dbw EFFECTCMDTYPE_DISCARD_ENERGY, FlareonFlamethrower_DiscardEffect - dbw EFFECTCMDTYPE_AI_SELECTION, FlareonFlamethrower_AISelectEffect - db $00 - -MagmarFlamethrowerEffectCommands: - dbw EFFECTCMDTYPE_INITIAL_EFFECT_1, MagmarFlamethrower_CheckEnergy - dbw EFFECTCMDTYPE_INITIAL_EFFECT_2, MagmarFlamethrower_PlayerSelectEffect - dbw EFFECTCMDTYPE_DISCARD_ENERGY, MagmarFlamethrower_DiscardEffect - dbw EFFECTCMDTYPE_AI_SELECTION, MagmarFlamethrower_AISelectEffect - db $00 - -MagmarSmokescreenEffectCommands: - dbw EFFECTCMDTYPE_BEFORE_DAMAGE, MagmarSmokescreenEffect - db $00 - -MagmarSmogEffectCommands: - dbw EFFECTCMDTYPE_BEFORE_DAMAGE, Poison50PercentEffect - dbw EFFECTCMDTYPE_AI, MagmarSmog_AIEffect - db $00 - -CharmeleonFlamethrowerEffectCommands: - dbw EFFECTCMDTYPE_INITIAL_EFFECT_1, CharmeleonFlamethrower_CheckEnergy - dbw EFFECTCMDTYPE_INITIAL_EFFECT_2, CharmeleonFlamethrower_PlayerSelectEffect - dbw EFFECTCMDTYPE_DISCARD_ENERGY, CharmeleonFlamethrower_DiscardEffect - dbw EFFECTCMDTYPE_AI_SELECTION, CharmeleonFlamethrower_AISelectEffect - db $00 - -CharizardEnergyBurnEffectCommands: - dbw EFFECTCMDTYPE_INITIAL_EFFECT_1, EnergyBurnEffect - db $00 - -CharizardFireSpinEffectCommands: - dbw EFFECTCMDTYPE_INITIAL_EFFECT_1, FireSpin_CheckEnergy - dbw EFFECTCMDTYPE_INITIAL_EFFECT_2, FireSpin_PlayerSelectEffect - dbw EFFECTCMDTYPE_DISCARD_ENERGY, FireSpin_DiscardEffect - dbw EFFECTCMDTYPE_AI_SELECTION, FireSpin_AISelectEffect - db $00 - -VulpixConfuseRayEffectCommands: - dbw EFFECTCMDTYPE_BEFORE_DAMAGE, Confusion50PercentEffect - db $00 - -FlareonRageEffectCommands: - dbw EFFECTCMDTYPE_BEFORE_DAMAGE, FlareonRage_DamageBoostEffect - dbw EFFECTCMDTYPE_AI, FlareonRage_AIEffect - db $00 - -NinetalesMixUpEffectCommands: - dbw EFFECTCMDTYPE_AFTER_DAMAGE, MixUpEffect - db $00 - -NinetalesDancingEmbersEffectCommands: - dbw EFFECTCMDTYPE_BEFORE_DAMAGE, DancingEmbers_MultiplierEffect - dbw EFFECTCMDTYPE_AI, DancingEmbers_AIEffect - db $00 - -MoltresFiregiverEffectCommands: - dbw EFFECTCMDTYPE_INITIAL_EFFECT_1, Firegiver_InitialEffect - dbw EFFECTCMDTYPE_PKMN_POWER_TRIGGER, Firegiver_AddToHandEffect - db $00 - -Moltres2DiveBombEffectCommands: - dbw EFFECTCMDTYPE_BEFORE_DAMAGE, Moltres2DiveBomb_Success50PercentEffect - dbw EFFECTCMDTYPE_AI, Moltres2DiveBomb_AIEffect - db $00 - -AbraPsyshockEffectCommands: - dbw EFFECTCMDTYPE_BEFORE_DAMAGE, Paralysis50PercentEffect - db $00 - -GengarCurseEffectCommands: - dbw EFFECTCMDTYPE_INITIAL_EFFECT_2, Curse_CheckDamageAndBench - dbw EFFECTCMDTYPE_BEFORE_DAMAGE, Curse_TransferDamageEffect - dbw EFFECTCMDTYPE_REQUIRE_SELECTION, Curse_PlayerSelectEffect - db $00 - -GengarDarkMindEffectCommands: - dbw EFFECTCMDTYPE_AFTER_DAMAGE, GengarDarkMind_DamageBenchEffect - dbw EFFECTCMDTYPE_REQUIRE_SELECTION, GengarDarkMind_PlayerSelectEffect - dbw EFFECTCMDTYPE_AI_SELECTION, GengarDarkMind_AISelectEffect - db $00 - -GastlySleepingGasEffectCommands: - dbw EFFECTCMDTYPE_BEFORE_DAMAGE, SleepingGasEffect - db $00 - -GastlyDestinyBondEffectCommands: - dbw EFFECTCMDTYPE_INITIAL_EFFECT_1, DestinyBond_CheckEnergy - dbw EFFECTCMDTYPE_INITIAL_EFFECT_2, DestinyBond_PlayerSelectEffect - dbw EFFECTCMDTYPE_BEFORE_DAMAGE, DestinyBond_DestinyBondEffect - dbw EFFECTCMDTYPE_DISCARD_ENERGY, DestinyBond_DiscardEffect - dbw EFFECTCMDTYPE_AI_SELECTION, DestinyBond_AISelectEffect - db $00 - -GastlyLickEffectCommands: - dbw EFFECTCMDTYPE_BEFORE_DAMAGE, Paralysis50PercentEffect - db $00 - -GastlyEnergyConversionEffectCommands: - dbw EFFECTCMDTYPE_INITIAL_EFFECT_1, EnergyConversion_CheckEnergy - dbw EFFECTCMDTYPE_AFTER_DAMAGE, EnergyConversion_AddToHandEffect - dbw EFFECTCMDTYPE_REQUIRE_SELECTION, EnergyConversion_PlayerSelectEffect - dbw EFFECTCMDTYPE_AI_SELECTION, EnergyConversion_AISelectEffect - db $00 - -HaunterHypnosisEffectCommands: - dbw EFFECTCMDTYPE_BEFORE_DAMAGE, SleepEffect - db $00 - -HaunterDreamEaterEffectCommands: - dbw EFFECTCMDTYPE_INITIAL_EFFECT_1, DreamEaterEffect - db $00 - -HaunterTransparencyEffectCommands: - dbw EFFECTCMDTYPE_INITIAL_EFFECT_1, TransparencyEffect - db $00 - -HaunterNightmareEffectCommands: - dbw EFFECTCMDTYPE_BEFORE_DAMAGE, SleepEffect - db $00 - -HypnoProphecyEffectCommands: - dbw EFFECTCMDTYPE_INITIAL_EFFECT_1, Prophecy_CheckDeck - dbw EFFECTCMDTYPE_AFTER_DAMAGE, Prophecy_ReorderDeckEffect - dbw EFFECTCMDTYPE_REQUIRE_SELECTION, Prophecy_PlayerSelectEffect - dbw EFFECTCMDTYPE_AI_SELECTION, Prophecy_AISelectEffect - db $00 - -HypnoDarkMindEffectCommands: - dbw EFFECTCMDTYPE_AFTER_DAMAGE, HypnoDarkMind_DamageBenchEffect - dbw EFFECTCMDTYPE_REQUIRE_SELECTION, HypnoDarkMind_PlayerSelectEffect - dbw EFFECTCMDTYPE_AI_SELECTION, HypnoDarkMind_AISelectEffect - db $00 - -DrowzeeConfuseRayEffectCommands: - dbw EFFECTCMDTYPE_BEFORE_DAMAGE, Confusion50PercentEffect - db $00 - -MrMimeInvisibleWallEffectCommands: - dbw EFFECTCMDTYPE_INITIAL_EFFECT_1, InvisibleWallEffect - db $00 - -MrMimeMeditateEffectCommands: - dbw EFFECTCMDTYPE_BEFORE_DAMAGE, MrMimeMeditate_DamageBoostEffect - dbw EFFECTCMDTYPE_AI, MrMimeMeditate_AIEffect - db $00 - -AlakazamDamageSwapEffectCommands: - dbw EFFECTCMDTYPE_INITIAL_EFFECT_2, DamageSwap_CheckDamage - dbw EFFECTCMDTYPE_BEFORE_DAMAGE, DamageSwap_SelectAndSwapEffect - dbw EFFECTCMDTYPE_AFTER_DAMAGE, DamageSwap_SwapEffect - db $00 - -AlakazamConfuseRayEffectCommands: - dbw EFFECTCMDTYPE_BEFORE_DAMAGE, Confusion50PercentEffect - db $00 - -MewPsywaveEffectCommands: - dbw EFFECTCMDTYPE_BEFORE_DAMAGE, PsywaveEffect - db $00 - -MewDevolutionBeamEffectCommands: - dbw EFFECTCMDTYPE_INITIAL_EFFECT_1, DevolutionBeam_CheckPlayArea - dbw EFFECTCMDTYPE_INITIAL_EFFECT_2, DevolutionBeam_PlayerSelectEffect - dbw EFFECTCMDTYPE_BEFORE_DAMAGE, DevolutionBeam_LoadAnimation - dbw EFFECTCMDTYPE_AFTER_DAMAGE, DevolutionBeam_DevolveEffect - dbw EFFECTCMDTYPE_AI_SELECTION, DevolutionBeam_AISelectEffect - db $00 - -MewNeutralizingShieldEffectCommands: - dbw EFFECTCMDTYPE_INITIAL_EFFECT_1, NeutralizingShieldEffect - db $00 - -MewPsyshockEffectCommands: - dbw EFFECTCMDTYPE_BEFORE_DAMAGE, Paralysis50PercentEffect - db $00 - -MewtwoPsychicEffectCommands: - dbw EFFECTCMDTYPE_BEFORE_DAMAGE, Psychic_DamageBoostEffect - dbw EFFECTCMDTYPE_AI, Psychic_AIEffect - db $00 - -MewtwoBarrierEffectCommands: - dbw EFFECTCMDTYPE_INITIAL_EFFECT_1, Barrier_CheckEnergy - dbw EFFECTCMDTYPE_INITIAL_EFFECT_2, Barrier_PlayerSelectEffect - dbw EFFECTCMDTYPE_BEFORE_DAMAGE, Barrier_BarrierEffect - dbw EFFECTCMDTYPE_DISCARD_ENERGY, Barrier_DiscardEffect - dbw EFFECTCMDTYPE_AI_SELECTION, Barrier_AISelectEffect - db $00 - -Mewtwo3EnergyAbsorptionEffectCommands: - dbw EFFECTCMDTYPE_INITIAL_EFFECT_1, Mewtwo3EnergyAbsorption_CheckDiscardPile - dbw EFFECTCMDTYPE_AFTER_DAMAGE, Mewtwo3EnergyAbsorption_AddToHandEffect - dbw EFFECTCMDTYPE_REQUIRE_SELECTION, Mewtwo3EnergyAbsorption_PlayerSelectEffect - dbw EFFECTCMDTYPE_AI_SELECTION, Mewtwo3EnergyAbsorption_AISelectEffect - db $00 - -Mewtwo2EnergyAbsorptionEffectCommands: - dbw EFFECTCMDTYPE_INITIAL_EFFECT_1, Mewtwo2EnergyAbsorption_CheckDiscardPile - dbw EFFECTCMDTYPE_AFTER_DAMAGE, Mewtwo2EnergyAbsorption_AddToHandEffect - dbw EFFECTCMDTYPE_REQUIRE_SELECTION, Mewtwo2EnergyAbsorption_PlayerSelectEffect - dbw EFFECTCMDTYPE_AI_SELECTION, Mewtwo2EnergyAbsorption_AISelectEffect - db $00 - -SlowbroStrangeBehaviorEffectCommands: - dbw EFFECTCMDTYPE_INITIAL_EFFECT_2, StrangeBehavior_CheckDamage - dbw EFFECTCMDTYPE_BEFORE_DAMAGE, StrangeBehavior_SelectAndSwapEffect - dbw EFFECTCMDTYPE_AFTER_DAMAGE, StrangeBehavior_SwapEffect - db $00 - -SlowbroPsyshockEffectCommands: - dbw EFFECTCMDTYPE_BEFORE_DAMAGE, Paralysis50PercentEffect - db $00 - -SlowpokeSpacingOutEffectCommands: - dbw EFFECTCMDTYPE_INITIAL_EFFECT_1, SpacingOut_CheckDamage - dbw EFFECTCMDTYPE_BEFORE_DAMAGE, SpacingOut_Success50PercentEffect - dbw EFFECTCMDTYPE_AFTER_DAMAGE, SpacingOut_HealEffect - db $00 - -SlowpokeScavengeEffectCommands: - dbw EFFECTCMDTYPE_INITIAL_EFFECT_1, Scavenge_CheckDiscardPile - dbw EFFECTCMDTYPE_INITIAL_EFFECT_2, Scavenge_PlayerSelectEnergyEffect - dbw EFFECTCMDTYPE_AFTER_DAMAGE, Scavenge_AddToHandEffect - dbw EFFECTCMDTYPE_REQUIRE_SELECTION, Scavenge_PlayerSelectTrainerEffect - dbw EFFECTCMDTYPE_DISCARD_ENERGY, Scavenge_DiscardEffect - dbw EFFECTCMDTYPE_AI_SELECTION, Scavenge_AISelectEffect - db $00 - -SlowpokeAmnesiaEffectCommands: - dbw EFFECTCMDTYPE_INITIAL_EFFECT_1, SlowpokeAmnesia_CheckAttacks - dbw EFFECTCMDTYPE_INITIAL_EFFECT_2, SlowpokeAmnesia_PlayerSelectEffect - dbw EFFECTCMDTYPE_BEFORE_DAMAGE, SlowpokeAmnesia_DisableEffect - dbw EFFECTCMDTYPE_AI_SELECTION, SlowpokeAmnesia_AISelectEffect - db $00 - -KadabraRecoverEffectCommands: - dbw EFFECTCMDTYPE_INITIAL_EFFECT_1, KadabraRecover_CheckEnergyHP - dbw EFFECTCMDTYPE_INITIAL_EFFECT_2, KadabraRecover_PlayerSelectEffect - dbw EFFECTCMDTYPE_AFTER_DAMAGE, KadabraRecover_HealEffect - dbw EFFECTCMDTYPE_DISCARD_ENERGY, KadabraRecover_DiscardEffect - dbw EFFECTCMDTYPE_AI_SELECTION, KadabraRecover_AISelectEffect - db $00 - -JynxDoubleslapEffectCommands: - dbw EFFECTCMDTYPE_BEFORE_DAMAGE, JynxDoubleslap_MultiplierEffect - dbw EFFECTCMDTYPE_AI, JynxDoubleslap_AIEffect - db $00 - -JynxMeditateEffectCommands: - dbw EFFECTCMDTYPE_BEFORE_DAMAGE, JynxMeditate_DamageBoostEffect - dbw EFFECTCMDTYPE_AI, JynxMeditate_AIEffect - db $00 - -MewMysteryAttackEffectCommands: - dbw EFFECTCMDTYPE_BEFORE_DAMAGE, MysteryAttack_RandomEffect - dbw EFFECTCMDTYPE_AFTER_DAMAGE, MysteryAttack_RecoverEffect - dbw EFFECTCMDTYPE_AI, MysteryAttack_AIEffect - db $00 - -GeodudeStoneBarrageEffectCommands: - dbw EFFECTCMDTYPE_BEFORE_DAMAGE, StoneBarrage_MultiplierEffect - dbw EFFECTCMDTYPE_AI, StoneBarrage_AIEffect - db $00 - -OnixHardenEffectCommands: - dbw EFFECTCMDTYPE_BEFORE_DAMAGE, OnixHardenEffect - db $00 - -PrimeapeFurySwipesEffectCommands: - dbw EFFECTCMDTYPE_BEFORE_DAMAGE, PrimeapeFurySwipes_MultiplierEffect - dbw EFFECTCMDTYPE_AI, PrimeapeFurySwipes_AIEffect - db $00 - -PrimeapeTantrumEffectCommands: - dbw EFFECTCMDTYPE_BEFORE_DAMAGE, TantrumEffect - db $00 - -MachampStrikesBackEffectCommands: - dbw EFFECTCMDTYPE_INITIAL_EFFECT_1, StrikesBackEffect - db $00 - -KabutoKabutoArmorEffectCommands: - dbw EFFECTCMDTYPE_INITIAL_EFFECT_1, KabutoArmorEffect - db $00 - -KabutopsAbsorbEffectCommands: - dbw EFFECTCMDTYPE_AFTER_DAMAGE, AbsorbEffect - db $00 - -CuboneSnivelEffectCommands: - dbw EFFECTCMDTYPE_BEFORE_DAMAGE, SnivelEffect - db $00 - -CuboneRageEffectCommands: - dbw EFFECTCMDTYPE_BEFORE_DAMAGE, CuboneRage_DamageBoostEffect - dbw EFFECTCMDTYPE_AI, CuboneRage_AIEffect - db $00 - -MarowakBonemerangEffectCommands: - dbw EFFECTCMDTYPE_BEFORE_DAMAGE, Bonemerang_MultiplierEffect - dbw EFFECTCMDTYPE_AI, Bonemerang_AIEffect - db $00 - -MarowakCallforFriendEffectCommands: - dbw EFFECTCMDTYPE_INITIAL_EFFECT_1, MarowakCallForFamily_CheckDeckAndPlayArea - dbw EFFECTCMDTYPE_AFTER_DAMAGE, MarowakCallForFamily_PutInPlayAreaEffect - dbw EFFECTCMDTYPE_REQUIRE_SELECTION, MarowakCallForFamily_PlayerSelectEffect - dbw EFFECTCMDTYPE_AI_SELECTION, MarowakCallForFamily_AISelectEffect - db $00 - -MachokeKarateChopEffectCommands: - dbw EFFECTCMDTYPE_BEFORE_DAMAGE, KarateChop_DamageSubtractionEffect - dbw EFFECTCMDTYPE_AI, KarateChop_AIEffect - db $00 - -MachokeSubmissionEffectCommands: - dbw EFFECTCMDTYPE_AFTER_DAMAGE, SubmissionEffect - db $00 - -GolemSelfdestructEffectCommands: - dbw EFFECTCMDTYPE_AFTER_DAMAGE, GolemSelfdestructEffect - db $00 - -GravelerHardenEffectCommands: - dbw EFFECTCMDTYPE_BEFORE_DAMAGE, GravelerHardenEffect - db $00 - -RhydonRamEffectCommands: - dbw EFFECTCMDTYPE_AFTER_DAMAGE, Ram_RecoilSwitchEffect - dbw EFFECTCMDTYPE_REQUIRE_SELECTION, Ram_SelectSwitchEffect - dbw EFFECTCMDTYPE_AI_SWITCH_DEFENDING_PKMN, Ram_SelectSwitchEffect - db $00 - -RhyhornLeerEffectCommands: - dbw EFFECTCMDTYPE_BEFORE_DAMAGE, LeerEffect - db $00 - -HitmonleeStretchKickEffectCommands: - dbw EFFECTCMDTYPE_INITIAL_EFFECT_1, StretchKick_CheckBench - dbw EFFECTCMDTYPE_AFTER_DAMAGE, StretchKick_BenchDamageEffect - dbw EFFECTCMDTYPE_REQUIRE_SELECTION, StretchKick_PlayerSelectEffect - dbw EFFECTCMDTYPE_AI_SELECTION, StretchKick_AISelectEffect - db $00 - -SandshrewSandAttackEffectCommands: - dbw EFFECTCMDTYPE_BEFORE_DAMAGE, SandAttackEffect - db $00 - -SandslashFurySwipesEffectCommands: - dbw EFFECTCMDTYPE_BEFORE_DAMAGE, SandslashFurySwipes_MultiplierEffect - dbw EFFECTCMDTYPE_AI, SandslashFurySwipes_AIEffect - db $00 - -DugtrioEarthquakeEffectCommands: - dbw EFFECTCMDTYPE_AFTER_DAMAGE, EarthquakeEffect - db $00 - -AerodactylPrehistoricPowerEffectCommands: - dbw EFFECTCMDTYPE_INITIAL_EFFECT_1, PrehistoricPowerEffect - db $00 - -MankeyPeekEffectCommands: - dbw EFFECTCMDTYPE_INITIAL_EFFECT_2, Peek_OncePerTurnCheck - dbw EFFECTCMDTYPE_BEFORE_DAMAGE, Peek_SelectEffect - db $00 - -MarowakBoneAttackEffectCommands: - dbw EFFECTCMDTYPE_BEFORE_DAMAGE, BoneAttackEffect - db $00 - -MarowakWailEffectCommands: - dbw EFFECTCMDTYPE_INITIAL_EFFECT_1, Wail_BenchCheck - dbw EFFECTCMDTYPE_AFTER_DAMAGE, Wail_FillBenchEffect - db $00 - -ElectabuzzThundershockEffectCommands: - dbw EFFECTCMDTYPE_BEFORE_DAMAGE, Paralysis50PercentEffect - db $00 - -ElectabuzzThunderpunchEffectCommands: - dbw EFFECTCMDTYPE_BEFORE_DAMAGE, Thunderpunch_ModifierEffect - dbw EFFECTCMDTYPE_AFTER_DAMAGE, Thunderpunch_RecoilEffect - dbw EFFECTCMDTYPE_AI, Thunderpunch_AIEffect - db $00 - -ElectabuzzLightScreenEffectCommands: - dbw EFFECTCMDTYPE_BEFORE_DAMAGE, LightScreenEffect - db $00 - -ElectabuzzQuickAttackEffectCommands: - dbw EFFECTCMDTYPE_BEFORE_DAMAGE, ElectabuzzQuickAttack_DamageBoostEffect - dbw EFFECTCMDTYPE_AI, ElectabuzzQuickAttack_AIEffect - db $00 - -MagnemiteThunderWaveEffectCommands: - dbw EFFECTCMDTYPE_BEFORE_DAMAGE, Paralysis50PercentEffect - db $00 - -MagnemiteSelfdestructEffectCommands: - dbw EFFECTCMDTYPE_AFTER_DAMAGE, MagnemiteSelfdestructEffect - db $00 - -ZapdosThunderEffectCommands: - dbw EFFECTCMDTYPE_BEFORE_DAMAGE, ZapdosThunder_Recoil50PercentEffect - dbw EFFECTCMDTYPE_AFTER_DAMAGE, ZapdosThunder_RecoilEffect - db $00 - -ZapdosThunderboltEffectCommands: - dbw EFFECTCMDTYPE_BEFORE_DAMAGE, ThunderboltEffect - db $00 - -ZapdosThunderstormEffectCommands: - dbw EFFECTCMDTYPE_AFTER_DAMAGE, ThunderstormEffect - db $00 - -JolteonQuickAttackEffectCommands: - dbw EFFECTCMDTYPE_BEFORE_DAMAGE, JolteonQuickAttack_DamageBoostEffect - dbw EFFECTCMDTYPE_AI, JolteonQuickAttack_AIEffect - db $00 - -JolteonPinMissileEffectCommands: - dbw EFFECTCMDTYPE_BEFORE_DAMAGE, PinMissile_MultiplierEffect - dbw EFFECTCMDTYPE_AI, PinMissile_AIEffect - db $00 - -FlyingPikachuThundershockEffectCommands: - dbw EFFECTCMDTYPE_BEFORE_DAMAGE, Paralysis50PercentEffect - db $00 - -FlyingPikachuFlyEffectCommands: - dbw EFFECTCMDTYPE_BEFORE_DAMAGE, Fly_Success50PercentEffect - dbw EFFECTCMDTYPE_AI, Fly_AIEffect - db $00 - -PikachuThunderJoltEffectCommands: - dbw EFFECTCMDTYPE_BEFORE_DAMAGE, ThunderJolt_Recoil50PercentEffect - dbw EFFECTCMDTYPE_AFTER_DAMAGE, ThunderJolt_RecoilEffect - db $00 - -PikachuSparkEffectCommands: - dbw EFFECTCMDTYPE_AFTER_DAMAGE, Spark_BenchDamageEffect - dbw EFFECTCMDTYPE_REQUIRE_SELECTION, Spark_PlayerSelectEffect - dbw EFFECTCMDTYPE_AI_SELECTION, Spark_AISelectEffect - db $00 - -Pikachu3GrowlEffectCommands: - dbw EFFECTCMDTYPE_BEFORE_DAMAGE, Pikachu3GrowlEffect - db $00 - -Pikachu3ThundershockEffectCommands: - dbw EFFECTCMDTYPE_BEFORE_DAMAGE, Paralysis50PercentEffect - db $00 - -Pikachu4GrowlEffectCommands: - dbw EFFECTCMDTYPE_BEFORE_DAMAGE, Pikachu4GrowlEffect - db $00 - -Pikachu4ThundershockEffectCommands: - dbw EFFECTCMDTYPE_BEFORE_DAMAGE, Paralysis50PercentEffect - db $00 - -ElectrodeChainLightningEffectCommands: - dbw EFFECTCMDTYPE_AFTER_DAMAGE, ChainLightningEffect - db $00 - -RaichuAgilityEffectCommands: - dbw EFFECTCMDTYPE_BEFORE_DAMAGE, RaichuAgilityEffect - db $00 - -RaichuThunderEffectCommands: - dbw EFFECTCMDTYPE_BEFORE_DAMAGE, RaichuThunder_Recoil50PercentEffect - dbw EFFECTCMDTYPE_AFTER_DAMAGE, RaichuThunder_RecoilEffect - db $00 - -RaichuGigashockEffectCommands: - dbw EFFECTCMDTYPE_AFTER_DAMAGE, Gigashock_BenchDamageEffect - dbw EFFECTCMDTYPE_REQUIRE_SELECTION, Gigashock_PlayerSelectEffect - dbw EFFECTCMDTYPE_AI_SELECTION, Gigashock_AISelectEffect - db $00 - -MagnetonThunderWaveEffectCommands: - dbw EFFECTCMDTYPE_BEFORE_DAMAGE, Paralysis50PercentEffect - db $00 - -Magneton1SelfdestructEffectCommands: - dbw EFFECTCMDTYPE_AFTER_DAMAGE, Magneton1SelfdestructEffect - db $00 - -MagnetonSonicboomEffectCommands: - dbw EFFECTCMDTYPE_BEFORE_DAMAGE, MagnetonSonicboom_UnaffectedByColorEffect - dbw EFFECTCMDTYPE_AFTER_DAMAGE, MagnetonSonicboom_NullEffect - dbw EFFECTCMDTYPE_AI, MagnetonSonicboom_UnaffectedByColorEffect - db $00 - -Magneton2SelfdestructEffectCommands: - dbw EFFECTCMDTYPE_AFTER_DAMAGE, Magneton2SelfdestructEffect - db $00 - -ZapdosPealOfThunderEffectCommands: - dbw EFFECTCMDTYPE_INITIAL_EFFECT_1, PealOfThunder_InitialEffect - dbw EFFECTCMDTYPE_PKMN_POWER_TRIGGER, PealOfThunder_RandomlyDamageEffect - db $00 - -ZapdosBigThunderEffectCommands: - dbw EFFECTCMDTYPE_AFTER_DAMAGE, BigThunderEffect - db $00 - -MagnemiteMagneticStormEffectCommands: - dbw EFFECTCMDTYPE_AFTER_DAMAGE, MagneticStormEffect - db $00 - -ElectrodeSonicboomEffectCommands: - dbw EFFECTCMDTYPE_BEFORE_DAMAGE, ElectrodeSonicboom_UnaffectedByColorEffect - dbw EFFECTCMDTYPE_AFTER_DAMAGE, ElectrodeSonicboom_NullEffect - dbw EFFECTCMDTYPE_AI, ElectrodeSonicboom_UnaffectedByColorEffect - db $00 - -ElectrodeEnergySpikeEffectCommands: - dbw EFFECTCMDTYPE_INITIAL_EFFECT_1, EnergySpike_DeckCheck - dbw EFFECTCMDTYPE_AFTER_DAMAGE, EnergySpike_AttachEnergyEffect - dbw EFFECTCMDTYPE_REQUIRE_SELECTION, EnergySpike_PlayerSelectEffect - dbw EFFECTCMDTYPE_AI_SELECTION, EnergySpike_AISelectEffect - db $00 - -JolteonDoubleKickEffectCommands: - dbw EFFECTCMDTYPE_BEFORE_DAMAGE, JolteonDoubleKick_MultiplierEffect - dbw EFFECTCMDTYPE_AI, JolteonDoubleKick_AIEffect - db $00 - -JolteonStunNeedleEffectCommands: - dbw EFFECTCMDTYPE_BEFORE_DAMAGE, Paralysis50PercentEffect - db $00 - -EeveeTailWagEffectCommands: - dbw EFFECTCMDTYPE_BEFORE_DAMAGE, TailWagEffect - db $00 - -EeveeQuickAttackEffectCommands: - dbw EFFECTCMDTYPE_BEFORE_DAMAGE, EeveeQuickAttack_DamageBoostEffect - dbw EFFECTCMDTYPE_AI, EeveeQuickAttack_AIEffect - db $00 - -SpearowMirrorMoveEffectCommands: - dbw EFFECTCMDTYPE_INITIAL_EFFECT_1, SpearowMirrorMove_InitialEffect1 - dbw EFFECTCMDTYPE_INITIAL_EFFECT_2, SpearowMirrorMove_InitialEffect2 - dbw EFFECTCMDTYPE_BEFORE_DAMAGE, SpearowMirrorMove_BeforeDamage - dbw EFFECTCMDTYPE_AFTER_DAMAGE, SpearowMirrorMove_AfterDamage - dbw EFFECTCMDTYPE_REQUIRE_SELECTION, SpearowMirrorMove_PlayerSelection - dbw EFFECTCMDTYPE_AI_SELECTION, SpearowMirrorMove_AISelection - dbw EFFECTCMDTYPE_AI, SpearowMirrorMove_AIEffect - db $00 - -FearowAgilityEffectCommands: - dbw EFFECTCMDTYPE_BEFORE_DAMAGE, FearowAgilityEffect - db $00 - -DragoniteStepInEffectCommands: - dbw EFFECTCMDTYPE_INITIAL_EFFECT_2, StepIn_BenchCheck - dbw EFFECTCMDTYPE_BEFORE_DAMAGE, StepIn_SwitchEffect - db $00 - -Dragonite2SlamEffectCommands: - dbw EFFECTCMDTYPE_BEFORE_DAMAGE, Dragonite2Slam_MultiplierEffect - dbw EFFECTCMDTYPE_AI, Dragonite2Slam_AIEffect - db $00 - -SnorlaxThickSkinnedEffectCommands: - dbw EFFECTCMDTYPE_INITIAL_EFFECT_1, ThickSkinnedEffect - db $00 - -SnorlaxBodySlamEffectCommands: - dbw EFFECTCMDTYPE_BEFORE_DAMAGE, Paralysis50PercentEffect - db $00 - -FarfetchdLeekSlapEffectCommands: - dbw EFFECTCMDTYPE_INITIAL_EFFECT_1, LeekSlap_OncePerDuelCheck - dbw EFFECTCMDTYPE_BEFORE_DAMAGE, LeekSlap_NoDamage50PercentEffect - dbw EFFECTCMDTYPE_DISCARD_ENERGY, LeekSlap_SetUsedThisDuelFlag - dbw EFFECTCMDTYPE_AI, LeekSlap_AIEffect - db $00 - -KangaskhanFetchEffectCommands: - dbw EFFECTCMDTYPE_AFTER_DAMAGE, FetchEffect - db $00 - -KangaskhanCometPunchEffectCommands: - dbw EFFECTCMDTYPE_BEFORE_DAMAGE, CometPunch_MultiplierEffect - dbw EFFECTCMDTYPE_AI, CometPunch_AIEffect - db $00 - -TaurosStompEffectCommands: - dbw EFFECTCMDTYPE_BEFORE_DAMAGE, TaurosStomp_DamageBoostEffect - dbw EFFECTCMDTYPE_AI, TaurosStomp_AIEffect - db $00 - -TaurosRampageEffectCommands: - dbw EFFECTCMDTYPE_BEFORE_DAMAGE, Rampage_Confusion50PercentEffect - dbw EFFECTCMDTYPE_AI, Rampage_AIEffect - db $00 - -DoduoFuryAttackEffectCommands: - dbw EFFECTCMDTYPE_BEFORE_DAMAGE, FuryAttack_MultiplierEffect - dbw EFFECTCMDTYPE_AI, FuryAttack_AIEffect - db $00 - -DodrioRetreatAidEffectCommands: - dbw EFFECTCMDTYPE_INITIAL_EFFECT_1, RetreatAidEffect - db $00 - -DodrioRageEffectCommands: - dbw EFFECTCMDTYPE_BEFORE_DAMAGE, DodrioRage_DamageBoostEffect - dbw EFFECTCMDTYPE_AI, DodrioRage_AIEffect - db $00 - -MeowthPayDayEffectCommands: - dbw EFFECTCMDTYPE_AFTER_DAMAGE, PayDayEffect - db $00 - -DragonairSlamEffectCommands: - dbw EFFECTCMDTYPE_BEFORE_DAMAGE, DragonairSlam_MultiplierEffect - dbw EFFECTCMDTYPE_AI, DragonairSlam_AIEffect - db $00 - -DragonairHyperBeamEffectCommands: - dbw EFFECTCMDTYPE_AFTER_DAMAGE, DragonairHyperBeam_DiscardEffect - dbw EFFECTCMDTYPE_REQUIRE_SELECTION, DragonairHyperBeam_PlayerSelectEffect - dbw EFFECTCMDTYPE_AI_SELECTION, DragonairHyperBeam_AISelectEffect - db $00 - -ClefableMetronomeEffectCommands: - dbw EFFECTCMDTYPE_INITIAL_EFFECT_1, ClefableMetronome_CheckAttacks - dbw EFFECTCMDTYPE_INITIAL_EFFECT_2, ClefableMetronome_UseAttackEffect - dbw EFFECTCMDTYPE_AI_SELECTION, ClefableMetronome_AISelectEffect - db $00 - -ClefableMinimizeEffectCommands: - dbw EFFECTCMDTYPE_BEFORE_DAMAGE, ClefableMinimizeEffect - db $00 - -PidgeotHurricaneEffectCommands: - dbw EFFECTCMDTYPE_AFTER_DAMAGE, HurricaneEffect - db $00 - -PidgeottoWhirlwindEffectCommands: - dbw EFFECTCMDTYPE_AFTER_DAMAGE, PidgeottoWhirlwind_SwitchEffect - dbw EFFECTCMDTYPE_REQUIRE_SELECTION, PidgeottoWhirlwind_SelectEffect - dbw EFFECTCMDTYPE_AI_SWITCH_DEFENDING_PKMN, PidgeottoWhirlwind_SelectEffect - db $00 - -PidgeottoMirrorMoveEffectCommands: - dbw EFFECTCMDTYPE_INITIAL_EFFECT_1, PidgeottoMirrorMove_InitialEffect1 - dbw EFFECTCMDTYPE_INITIAL_EFFECT_2, PidgeottoMirrorMove_InitialEffect2 - dbw EFFECTCMDTYPE_BEFORE_DAMAGE, PidgeottoMirrorMove_BeforeDamage - dbw EFFECTCMDTYPE_AFTER_DAMAGE, PidgeottoMirrorMove_AfterDamage - dbw EFFECTCMDTYPE_REQUIRE_SELECTION, PidgeottoMirrorMove_PlayerSelection - dbw EFFECTCMDTYPE_AI_SELECTION, PidgeottoMirrorMove_AISelection - dbw EFFECTCMDTYPE_AI, PidgeottoMirrorMove_AIEffect - db $00 - -ClefairySingEffectCommands: - dbw EFFECTCMDTYPE_BEFORE_DAMAGE, SingEffect - db $00 - -ClefairyMetronomeEffectCommands: - dbw EFFECTCMDTYPE_INITIAL_EFFECT_1, ClefairyMetronome_CheckAttacks - dbw EFFECTCMDTYPE_INITIAL_EFFECT_2, ClefairyMetronome_UseAttackEffect - dbw EFFECTCMDTYPE_AI_SELECTION, ClefairyMetronome_AISelectEffect - db $00 - -WigglytuffLullabyEffectCommands: - dbw EFFECTCMDTYPE_BEFORE_DAMAGE, SleepEffect - db $00 - -WigglytuffDoTheWaveEffectCommands: - dbw EFFECTCMDTYPE_BEFORE_DAMAGE, DoTheWaveEffect - dbw EFFECTCMDTYPE_AI, DoTheWaveEffect - db $00 - -JigglypuffLullabyEffectCommands: - dbw EFFECTCMDTYPE_BEFORE_DAMAGE, SleepEffect - db $00 - -JigglypuffFirstAidEffectCommands: - dbw EFFECTCMDTYPE_INITIAL_EFFECT_1, FirstAid_DamageCheck - dbw EFFECTCMDTYPE_AFTER_DAMAGE, FirstAid_HealEffect - db $00 - -JigglypuffDoubleEdgeEffectCommands: - dbw EFFECTCMDTYPE_AFTER_DAMAGE, JigglypuffDoubleEdgeEffect - db $00 - -PersianPounceEffectCommands: - dbw EFFECTCMDTYPE_BEFORE_DAMAGE, PounceEffect - db $00 - -LickitungTongueWrapEffectCommands: - dbw EFFECTCMDTYPE_BEFORE_DAMAGE, Paralysis50PercentEffect - db $00 - -LickitungSupersonicEffectCommands: - dbw EFFECTCMDTYPE_BEFORE_DAMAGE, LickitungSupersonicEffect - db $00 - -PidgeyWhirlwindEffectCommands: - dbw EFFECTCMDTYPE_AFTER_DAMAGE, PidgeyWhirlwind_SwitchEffect - dbw EFFECTCMDTYPE_REQUIRE_SELECTION, PidgeyWhirlwind_SelectEffect - dbw EFFECTCMDTYPE_AI_SWITCH_DEFENDING_PKMN, PidgeyWhirlwind_SelectEffect - db $00 - -PorygonConversion1EffectCommands: - dbw EFFECTCMDTYPE_INITIAL_EFFECT_1, Conversion1_WeaknessCheck - dbw EFFECTCMDTYPE_INITIAL_EFFECT_2, Conversion1_PlayerSelectEffect - dbw EFFECTCMDTYPE_AFTER_DAMAGE, Conversion1_ChangeWeaknessEffect - dbw EFFECTCMDTYPE_AI_SELECTION, Conversion1_AISelectEffect - db $00 - -PorygonConversion2EffectCommands: - dbw EFFECTCMDTYPE_INITIAL_EFFECT_1, Conversion2_ResistanceCheck - dbw EFFECTCMDTYPE_INITIAL_EFFECT_2, Conversion2_PlayerSelectEffect - dbw EFFECTCMDTYPE_AFTER_DAMAGE, Conversion2_ChangeResistanceEffect - dbw EFFECTCMDTYPE_AI_SELECTION, Conversion2_AISelectEffect - db $00 - -ChanseyScrunchEffectCommands: - dbw EFFECTCMDTYPE_BEFORE_DAMAGE, ScrunchEffect - db $00 - -ChanseyDoubleEdgeEffectCommands: - dbw EFFECTCMDTYPE_AFTER_DAMAGE, ChanseyDoubleEdgeEffect - db $00 - -RaticateSuperFangEffectCommands: - dbw EFFECTCMDTYPE_BEFORE_DAMAGE, SuperFang_HalfHPEffect - dbw EFFECTCMDTYPE_AI, SuperFang_AIEffect - db $00 - -TrainerCardAsPokemonEffectCommands: - dbw EFFECTCMDTYPE_INITIAL_EFFECT_2, TrainerCardAsPokemon_BenchCheck - dbw EFFECTCMDTYPE_BEFORE_DAMAGE, TrainerCardAsPokemon_DiscardEffect - dbw EFFECTCMDTYPE_REQUIRE_SELECTION, TrainerCardAsPokemon_PlayerSelectSwitch - db $00 - -DragoniteHealingWindEffectCommands: - dbw EFFECTCMDTYPE_INITIAL_EFFECT_1, HealingWind_InitialEffect - dbw EFFECTCMDTYPE_PKMN_POWER_TRIGGER, HealingWind_PlayAreaHealEffect - db $00 - -Dragonite1SlamEffectCommands: - dbw EFFECTCMDTYPE_BEFORE_DAMAGE, Dragonite1Slam_MultiplierEffect - dbw EFFECTCMDTYPE_AI, Dragonite1Slam_AIEffect - db $00 - -MeowthCatPunchEffectCommands: - dbw EFFECTCMDTYPE_AFTER_DAMAGE, CatPunchEffect - db $00 - -DittoMorphEffectCommands: - dbw EFFECTCMDTYPE_AFTER_DAMAGE, MorphEffect - db $00 - -PidgeotSlicingWindEffectCommands: - dbw EFFECTCMDTYPE_AFTER_DAMAGE, SlicingWindEffect - db $00 - -PidgeotGaleEffectCommands: - dbw EFFECTCMDTYPE_BEFORE_DAMAGE, Gale_LoadAnimation - dbw EFFECTCMDTYPE_AFTER_DAMAGE, Gale_SwitchEffect - db $00 - -JigglypuffFriendshipSongEffectCommands: - dbw EFFECTCMDTYPE_INITIAL_EFFECT_1, FriendshipSong_BenchCheck - dbw EFFECTCMDTYPE_AFTER_DAMAGE, FriendshipSong_AddToBench50PercentEffect - db $00 - -JigglypuffExpandEffectCommands: - dbw EFFECTCMDTYPE_AFTER_DAMAGE, ExpandEffect - db $00 - -DoubleColorlessEnergyEffectCommands: - db $00 - -PsychicEnergyEffectCommands: - db $00 - -FightingEnergyEffectCommands: - db $00 - -LightningEnergyEffectCommands: - db $00 - -WaterEnergyEffectCommands: - db $00 - -FireEnergyEffectCommands: - db $00 - -GrassEnergyEffectCommands: - db $00 - -SuperPotionEffectCommands: - dbw EFFECTCMDTYPE_INITIAL_EFFECT_1, SuperPotion_DamageEnergyCheck - dbw EFFECTCMDTYPE_INITIAL_EFFECT_2, SuperPotion_PlayerSelectEffect - dbw EFFECTCMDTYPE_BEFORE_DAMAGE, SuperPotion_HealEffect - db $00 - -ImakuniEffectCommands: - dbw EFFECTCMDTYPE_BEFORE_DAMAGE, ImakuniEffect - db $00 - -EnergyRemovalEffectCommands: - dbw EFFECTCMDTYPE_INITIAL_EFFECT_1, EnergyRemoval_EnergyCheck - dbw EFFECTCMDTYPE_INITIAL_EFFECT_2, EnergyRemoval_PlayerSelection - dbw EFFECTCMDTYPE_BEFORE_DAMAGE, EnergyRemoval_DiscardEffect - dbw EFFECTCMDTYPE_AI_SELECTION, EnergyRemoval_AISelection - db $00 - -EnergyRetrievalEffectCommands: - dbw EFFECTCMDTYPE_INITIAL_EFFECT_1, EnergyRetrieval_HandEnergyCheck - dbw EFFECTCMDTYPE_INITIAL_EFFECT_2, EnergyRetrieval_PlayerHandSelection - dbw EFFECTCMDTYPE_BEFORE_DAMAGE, EnergyRetrieval_DiscardAndAddToHandEffect - dbw EFFECTCMDTYPE_REQUIRE_SELECTION, EnergyRetrieval_PlayerDiscardPileSelection - db $00 - -EnergySearchEffectCommands: - dbw EFFECTCMDTYPE_INITIAL_EFFECT_1, EnergySearch_DeckCheck - dbw EFFECTCMDTYPE_BEFORE_DAMAGE, EnergySearch_AddToHandEffect - dbw EFFECTCMDTYPE_REQUIRE_SELECTION, EnergySearch_PlayerSelection - db $00 - -ProfessorOakEffectCommands: - dbw EFFECTCMDTYPE_BEFORE_DAMAGE, ProfessorOakEffect - db $00 - -PotionEffectCommands: - dbw EFFECTCMDTYPE_INITIAL_EFFECT_1, Potion_DamageCheck - dbw EFFECTCMDTYPE_INITIAL_EFFECT_2, Potion_PlayerSelection - dbw EFFECTCMDTYPE_BEFORE_DAMAGE, Potion_HealEffect - db $00 - -GamblerEffectCommands: - dbw EFFECTCMDTYPE_BEFORE_DAMAGE, GamblerEffect - db $00 - -ItemFinderEffectCommands: - dbw EFFECTCMDTYPE_INITIAL_EFFECT_1, ItemFinder_HandDiscardPileCheck - dbw EFFECTCMDTYPE_INITIAL_EFFECT_2, ItemFinder_PlayerSelection - dbw EFFECTCMDTYPE_BEFORE_DAMAGE, ItemFinder_DiscardAddToHandEffect - db $00 - -DefenderEffectCommands: - dbw EFFECTCMDTYPE_INITIAL_EFFECT_2, Defender_PlayerSelection - dbw EFFECTCMDTYPE_BEFORE_DAMAGE, Defender_AttachDefenderEffect - db $00 - -MysteriousFossilEffectCommands: - dbw EFFECTCMDTYPE_INITIAL_EFFECT_1, MysteriousFossil_BenchCheck - dbw EFFECTCMDTYPE_BEFORE_DAMAGE, MysteriousFossil_PlaceInPlayAreaEffect - db $00 - -FullHealEffectCommands: - dbw EFFECTCMDTYPE_INITIAL_EFFECT_1, FullHeal_StatusCheck - dbw EFFECTCMDTYPE_BEFORE_DAMAGE, FullHeal_ClearStatusEffect - db $00 - -ImposterProfessorOakEffectCommands: - dbw EFFECTCMDTYPE_BEFORE_DAMAGE, ImposterProfessorOakEffect - db $00 - -ComputerSearchEffectCommands: - dbw EFFECTCMDTYPE_INITIAL_EFFECT_1, ComputerSearch_HandDeckCheck - dbw EFFECTCMDTYPE_INITIAL_EFFECT_2, ComputerSearch_PlayerDiscardHandSelection - dbw EFFECTCMDTYPE_BEFORE_DAMAGE, ComputerSearch_DiscardAddToHandEffect - dbw EFFECTCMDTYPE_REQUIRE_SELECTION, ComputerSearch_PlayerDeckSelection - db $00 - -ClefairyDollEffectCommands: - dbw EFFECTCMDTYPE_INITIAL_EFFECT_1, ClefairyDoll_BenchCheck - dbw EFFECTCMDTYPE_BEFORE_DAMAGE, ClefairyDoll_PlaceInPlayAreaEffect - db $00 - -MrFujiEffectCommands: - dbw EFFECTCMDTYPE_INITIAL_EFFECT_1, MrFuji_BenchCheck - dbw EFFECTCMDTYPE_INITIAL_EFFECT_2, MrFuji_PlayerSelection - dbw EFFECTCMDTYPE_BEFORE_DAMAGE, MrFuji_ReturnToDeckEffect - db $00 - -PlusPowerEffectCommands: - dbw EFFECTCMDTYPE_BEFORE_DAMAGE, PlusPowerEffect - db $00 - -SwitchEffectCommands: - dbw EFFECTCMDTYPE_INITIAL_EFFECT_1, Switch_BenchCheck - dbw EFFECTCMDTYPE_INITIAL_EFFECT_2, Switch_PlayerSelection - dbw EFFECTCMDTYPE_BEFORE_DAMAGE, Switch_SwitchEffect - db $00 - -PokemonCenterEffectCommands: - dbw EFFECTCMDTYPE_INITIAL_EFFECT_1, PokemonCenter_DamageCheck - dbw EFFECTCMDTYPE_BEFORE_DAMAGE, PokemonCenter_HealDiscardEnergyEffect - db $00 - -PokemonFluteEffectCommands: - dbw EFFECTCMDTYPE_INITIAL_EFFECT_1, PokemonFlute_BenchCheck - dbw EFFECTCMDTYPE_INITIAL_EFFECT_2, PokemonFlute_PlayerSelection - dbw EFFECTCMDTYPE_BEFORE_DAMAGE, PokemonFlute_PlaceInPlayAreaText - db $00 - -PokemonBreederEffectCommands: - dbw EFFECTCMDTYPE_INITIAL_EFFECT_1, PokemonBreeder_HandPlayAreaCheck - dbw EFFECTCMDTYPE_INITIAL_EFFECT_2, PokemonBreeder_PlayerSelection - dbw EFFECTCMDTYPE_BEFORE_DAMAGE, PokemonBreeder_EvolveEffect - db $00 - -ScoopUpEffectCommands: - dbw EFFECTCMDTYPE_INITIAL_EFFECT_1, ScoopUp_BenchCheck - dbw EFFECTCMDTYPE_INITIAL_EFFECT_2, ScoopUp_PlayerSelection - dbw EFFECTCMDTYPE_BEFORE_DAMAGE, ScoopUp_ReturnToHandEffect - db $00 - -PokemonTraderEffectCommands: - dbw EFFECTCMDTYPE_INITIAL_EFFECT_1, PokemonTrader_HandDeckCheck - dbw EFFECTCMDTYPE_INITIAL_EFFECT_2, PokemonTrader_PlayerHandSelection - dbw EFFECTCMDTYPE_BEFORE_DAMAGE, PokemonTrader_TradeCardsEffect - dbw EFFECTCMDTYPE_REQUIRE_SELECTION, PokemonTrader_PlayerDeckSelection - db $00 - -PokedexEffectCommands: - dbw EFFECTCMDTYPE_INITIAL_EFFECT_1, Pokedex_DeckCheck - dbw EFFECTCMDTYPE_BEFORE_DAMAGE, Pokedex_OrderDeckCardsEffect - dbw EFFECTCMDTYPE_REQUIRE_SELECTION, Pokedex_PlayerSelection - db $00 - -BillEffectCommands: - dbw EFFECTCMDTYPE_BEFORE_DAMAGE, BillEffect - db $00 - -LassEffectCommands: - dbw EFFECTCMDTYPE_BEFORE_DAMAGE, LassEffect - db $00 - -MaintenanceEffectCommands: - dbw EFFECTCMDTYPE_INITIAL_EFFECT_1, Maintenance_HandCheck - dbw EFFECTCMDTYPE_INITIAL_EFFECT_2, Maintenance_PlayerSelection - dbw EFFECTCMDTYPE_BEFORE_DAMAGE, Maintenance_ReturnToDeckAndDrawEffect - db $00 - -PokeBallEffectCommands: - dbw EFFECTCMDTYPE_INITIAL_EFFECT_1, PokeBall_DeckCheck - dbw EFFECTCMDTYPE_BEFORE_DAMAGE, PokeBall_AddToHandEffect - dbw EFFECTCMDTYPE_REQUIRE_SELECTION, PokeBall_PlayerSelection - db $00 - -RecycleEffectCommands: - dbw EFFECTCMDTYPE_INITIAL_EFFECT_1, Recycle_DiscardPileCheck - dbw EFFECTCMDTYPE_BEFORE_DAMAGE, Recycle_AddToHandEffect - dbw EFFECTCMDTYPE_REQUIRE_SELECTION, Recycle_PlayerSelection - db $00 - -ReviveEffectCommands: - dbw EFFECTCMDTYPE_INITIAL_EFFECT_1, Revive_BenchCheck - dbw EFFECTCMDTYPE_INITIAL_EFFECT_2, Revive_PlayerSelection - dbw EFFECTCMDTYPE_BEFORE_DAMAGE, Revive_PlaceInPlayAreaEffect - db $00 - -DevolutionSprayEffectCommands: - dbw EFFECTCMDTYPE_INITIAL_EFFECT_1, DevolutionSpray_PlayAreaEvolutionCheck - dbw EFFECTCMDTYPE_INITIAL_EFFECT_2, DevolutionSpray_PlayerSelection - dbw EFFECTCMDTYPE_BEFORE_DAMAGE, DevolutionSpray_DevolutionEffect - db $00 - -SuperEnergyRemovalEffectCommands: - dbw EFFECTCMDTYPE_INITIAL_EFFECT_1, SuperEnergyRemoval_EnergyCheck - dbw EFFECTCMDTYPE_INITIAL_EFFECT_2, SuperEnergyRemoval_PlayerSelection - dbw EFFECTCMDTYPE_BEFORE_DAMAGE, SuperEnergyRemoval_DiscardEffect - db $00 - -SuperEnergyRetrievalEffectCommands: - dbw EFFECTCMDTYPE_INITIAL_EFFECT_1, SuperEnergyRetrieval_HandEnergyCheck - dbw EFFECTCMDTYPE_INITIAL_EFFECT_2, SuperEnergyRetrieval_PlayerHandSelection - dbw EFFECTCMDTYPE_BEFORE_DAMAGE, SuperEnergyRetrieval_DiscardAndAddToHandEffect - dbw EFFECTCMDTYPE_REQUIRE_SELECTION, SuperEnergyRetrieval_PlayerDiscardPileSelection - db $00 - -GustOfWindEffectCommands: - dbw EFFECTCMDTYPE_INITIAL_EFFECT_1, GustOfWind_BenchCheck - dbw EFFECTCMDTYPE_INITIAL_EFFECT_2, GustOfWind_PlayerSelection - dbw EFFECTCMDTYPE_BEFORE_DAMAGE, GustOfWind_SwitchEffect - db $00 diff --git a/src/engine/auto_deck_machines.asm b/src/engine/auto_deck_machines.asm new file mode 100644 index 0000000..0f84cef --- /dev/null +++ b/src/engine/auto_deck_machines.asm @@ -0,0 +1,191 @@ +INCLUDE "data/auto_deck_card_lists.asm" +INCLUDE "data/auto_deck_machines.asm" + +; writes to sAutoDecks all the deck configurations +; from the Auto Deck Machine in wCurAutoDeckMachine +ReadAutoDeckConfiguration: + call EnableSRAM + ld a, [wCurAutoDeckMachine] + ld l, a + ld h, 6 * NUM_DECK_MACHINE_SLOTS + call HtimesL + ld bc, AutoDeckMachineEntries + add hl, bc + ld b, 0 +.loop_decks + call .GetPointerToSRAMAutoDeck + call .ReadDeckConfiguration + call .ReadDeckName + + ; store deck description text ID + push hl + ld de, wAutoDeckMachineTextDescriptions + ld h, b + ld l, 2 + call HtimesL + add hl, de + ld d, h + ld e, l + pop hl + ld a, [hli] + ld [de], a + inc de + ld a, [hli] + ld [de], a + inc b + ld a, b + cp NUM_DECK_MACHINE_SLOTS + jr nz, .loop_decks + call DisableSRAM + ret + +; outputs in de the saved deck with index b +.GetPointerToSRAMAutoDeck + push hl + ld l, b + ld h, DECK_STRUCT_SIZE + call HtimesL + ld de, sAutoDecks + add hl, de + ld d, h + ld e, l + pop hl + ret + +; writes the deck configuration in SRAM +; by reading the given deck card list +.ReadDeckConfiguration + push hl + push bc + push de + push de + ld e, [hl] + inc hl + ld d, [hl] + pop hl + ld bc, DECK_NAME_SIZE + add hl, bc +.loop_create_deck + ld a, [de] + inc de + ld b, a ; card count + or a + jr z, .done_create_deck + ld a, [de] + inc de + ld c, a ; card ID +.loop_card_count + ld [hl], c + inc hl + dec b + jr nz, .loop_card_count + jr .loop_create_deck +.done_create_deck + pop de + pop bc + pop hl + inc hl + inc hl + ret + +.ReadDeckName + push hl + push bc + push de + ld a, [hli] + ld h, [hl] + ld l, a + ld de, wDismantledDeckName + call CopyText + pop hl + ld de, wDismantledDeckName +.loop_copy_name + ld a, [de] + ld [hli], a + or a + jr z, .done_copy_name + inc de + jr .loop_copy_name +.done_copy_name + pop bc + pop hl + inc hl + inc hl + ret + +; tries out all combinations of dismantling the player's decks +; in order to build the deck in wSelectedDeckMachineEntry +; if none of the combinations work, return carry set +; otherwise, return in a which deck flags should be dismantled +CheckWhichDecksToDismantleToBuildSavedDeck: + xor a + ld [wDecksToBeDismantled], a + +; first check if it can be built by +; only dismantling a single deck + ld a, DECK_1 +.loop_single_built_decks + call .CheckIfCanBuild + ret nc + sla a ; next deck + cp (1 << NUM_DECKS) + jr z, .two_deck_combinations + jr .loop_single_built_decks + +.two_deck_combinations +; next check all two deck combinations + ld a, DECK_1 | DECK_2 + call .CheckIfCanBuild + ret nc + ld a, DECK_1 | DECK_3 + call .CheckIfCanBuild + ret nc + ld a, DECK_1 | DECK_4 + call .CheckIfCanBuild + ret nc + ld a, DECK_2 | DECK_3 + call .CheckIfCanBuild + ret nc + ld a, DECK_2 | DECK_4 + call .CheckIfCanBuild + ret nc + ld a, DECK_3 | DECK_4 + call .CheckIfCanBuild + ret nc + +; all but one deck combinations + ld a, $ff ^ DECK_4 +.loop_three_deck_combinations + call .CheckIfCanBuild + ret nc + sra a + cp $ff + jr z, .all_decks + jr .loop_three_deck_combinations + +.all_decks +; finally check if can be built by dismantling all decks + call .CheckIfCanBuild + ret nc + +; none of the combinations work + scf + ret + +; returns carry if wSelectedDeckMachineEntry cannot be built +; by dismantling the decks given by register a +; a = DECK_* flags +.CheckIfCanBuild + push af + ld hl, wSelectedDeckMachineEntry + ld b, [hl] + farcall CheckIfCanBuildSavedDeck + jr c, .cannot_build + pop af + ld [wDecksToBeDismantled], a + or a + ret +.cannot_build + pop af + scf + ret diff --git a/src/engine/bank03.asm b/src/engine/bank03.asm index 3c83056..7bd4b75 100644 --- a/src/engine/bank03.asm +++ b/src/engine/bank03.asm @@ -2379,7 +2379,7 @@ ScriptCommand_ShowCardReceivedScreen: ; cee2 (3:4ee2) farcall Func_10000 farcall FlashWhiteScreen pop af - bank1call Func_7594 + bank1call ShowPromotionalCardScreen call WhiteOutDMGPals call DoFrameIfLCDEnabled call ReturnToOverworldNoCallback @@ -3199,7 +3199,7 @@ ScriptCommand_nop: ; d3d1 (3:53d1) ScriptCommand_GiveStarterDeck: ; d3d4 (3:53d4) ld a, [wStarterDeckChoice] - bank1call Func_7576 + bank1call AddStarterDeck jp IncreaseScriptPointerBy1 Unknown_d3dd: ; d3dd (3:53dd) diff --git a/src/engine/bank06.asm b/src/engine/bank06.asm deleted file mode 100644 index fa181f3..0000000 --- a/src/engine/bank06.asm +++ /dev/null @@ -1,5974 +0,0 @@ -; copy the name and level of the card at wLoadedCard1 to wDefaultText -; a = length in number of tiles (the resulting string will be padded with spaces to match it) -_CopyCardNameAndLevel: ; 18000 (6:4000) - push bc - push de - ld [wCardNameLength], a - ld hl, wLoadedCard1Name - ld a, [hli] - ld h, [hl] - ld l, a - ld de, wDefaultText - push de - call CopyText ; copy card name to wDefaultText - pop hl - ld a, [hli] - cp TX_HALFWIDTH - jp z, _CopyCardNameAndLevel_HalfwidthText - -; the name doesn't start with TX_HALFWIDTH -; this doesn't appear to be ever the case (unless caller manipulates wLoadedCard1Name) - ld a, [wCardNameLength] - ld c, a - ld a, [wLoadedCard1Type] - cp TYPE_ENERGY - jr nc, .level_done ; jump if energy or trainer - ld a, [wLoadedCard1Level] - or a - jr z, .level_done - inc c - inc c - ld a, [wLoadedCard1Level] - cp 10 - jr c, .level_done - inc c ; second digit -.level_done - ld hl, wLoadedCard1Name - ld a, [hli] - ld h, [hl] - ld l, a - ld de, wDefaultText - push de - call CopyText - pop hl - push de - ld e, c - call GetTextLengthInTiles - add e - ld c, a - pop hl - push hl -.fill_loop - ld a, $70 - ld [hli], a - dec c - jr nz, .fill_loop - ld [hl], TX_END - pop hl - ld a, [wLoadedCard1Type] - cp TYPE_ENERGY - jr nc, .done - ld a, [wLoadedCard1Level] - or a - jr z, .done - ld a, TX_SYMBOL - ld [hli], a - ld [hl], SYM_Lv - inc hl - ld a, [wLoadedCard1Level] - cp 10 - jr c, .one_digit - ld [hl], TX_SYMBOL - inc hl - ld b, SYM_0 - 1 -.first_digit_loop - inc b - sub 10 - jr nc, .first_digit_loop - add 10 - ld [hl], b ; first digit - inc hl -.one_digit - ld [hl], TX_SYMBOL - inc hl - add SYM_0 - ld [hl], a ; last (or only) digit - inc hl -.done - pop de - pop bc - ret - -; the name starts with TX_HALFWIDTH -_CopyCardNameAndLevel_HalfwidthText: ; 18086 (6:4086) - ld a, [wCardNameLength] - inc a - add a - ld b, a - ld hl, wDefaultText -.find_end_text_loop - dec b - ld a, [hli] - or a ; TX_END - jr nz, .find_end_text_loop - dec hl - ld a, [wLoadedCard1Type] - cp TYPE_ENERGY - jr nc, .level_done - ld a, [wLoadedCard1Level] - or a - jr z, .level_done - ld c, a - ld a, " " - ld [hli], a - dec b - ld a, "L" - ld [hli], a - dec b - ld a, "v" - ld [hli], a - dec b - ld a, c - cp 10 - jr c, .got_level - push bc - ld b, "0" - 1 -.first_digit_loop - inc b - sub 10 - jr nc, .first_digit_loop - add 10 - ld [hl], b ; first digit - inc hl - pop bc - ld c, a - dec b -.got_level - ld a, c - add "0" - ld [hli], a ; last (or only) digit - dec b -.level_done - push hl - ld a, " " -.fill_spaces_loop - ld [hli], a - dec b - jr nz, .fill_spaces_loop - ld [hl], TX_END - pop hl - pop de - pop bc - ret - -; this function is called when the player is shown the "In Play Area" screen. -; it can be called with either the select button (DuelMenuShortcut_BothActivePokemon), -; or via the "In Play Area" item of the Check menu (DuelCheckMenu_InPlayArea) -OpenInPlayAreaScreen: ; 180d5 (6:40d5) - ld a, INPLAYAREA_PLAYER_ACTIVE - ld [wInPlayAreaCurPosition], a -.start - xor a - ld [wCheckMenuCursorBlinkCounter], a - farcall DrawInPlayAreaScreen - call EnableLCD - call IsClairvoyanceActive - jr c, .clairvoyance_on - - ld de, OpenInPlayAreaScreen_TransitionTable1 - jr .clairvoyance_off - -.clairvoyance_on - ld de, OpenInPlayAreaScreen_TransitionTable2 -.clairvoyance_off - ld hl, wMenuInputTablePointer - ld [hl], e - inc hl - ld [hl], d - ld a, [wInPlayAreaCurPosition] - call .print_associated_text -.on_frame - ld a, $01 - ld [wVBlankOAMCopyToggle], a - call DoFrame - - ldh a, [hDPadHeld] - and START - jr nz, .selection - - ; if this function's been called from 'select' button, - ; wInPlayAreaFromSelectButton is on. - ld a, [wInPlayAreaFromSelectButton] - or a - jr z, .handle_input ; if it's from the Check menu, jump. - - ldh a, [hDPadHeld] - and SELECT - jr nz, .skip_input - -.handle_input - ld a, [wInPlayAreaCurPosition] - ld [wInPlayAreaTemporaryPosition], a - call OpenInPlayAreaScreen_HandleInput - jr c, .pressed - - ld a, [wInPlayAreaCurPosition] - cp INPLAYAREA_PLAYER_PLAY_AREA - jp z, .show_turn_holder_play_area - cp INPLAYAREA_OPP_PLAY_AREA - jp z, .show_non_turn_holder_play_area - - ; check if the cursor moved. - ld hl, wInPlayAreaTemporaryPosition - cp [hl] - call nz, .print_associated_text - - jr .on_frame - -.pressed - cp -1 - jr nz, .selection - - ; pressed b button. - call ZeroObjectPositionsAndToggleOAMCopy_Bank6 - lb de, $38, $9f - call SetupText - scf - ret - -.skip_input - call ZeroObjectPositionsAndToggleOAMCopy_Bank6 - lb de, $38, $9f - call SetupText - or a - ret - -.selection ; pressed a button or start button. - call ZeroObjectPositionsAndToggleOAMCopy_Bank6 - lb de, $38, $9f - call SetupText - ld a, [wInPlayAreaCurPosition] - ld [wInPlayAreaPreservedPosition], a - ld hl, .jump_table - call JumpToFunctionInTable - ld a, [wInPlayAreaPreservedPosition] - ld [wInPlayAreaCurPosition], a - - jp .start - -.print_associated_text ; 18171 (6:4171) -; each position has a text associated to it, -; which is printed at the bottom of the screen - push af - lb de, 1, 17 - call InitTextPrinting - ldtx hl, EmptyLineText - call ProcessTextFromID - - ld hl, hffb0 - ld [hl], $01 - ldtx hl, HandText_2 - call ProcessTextFromID - - ld hl, hffb0 - ld [hl], $00 - lb de, 1, 17 - call InitTextPrinting - pop af - ld hl, OpenInPlayAreaScreen_TextTable - ld b, 0 - sla a - ld c, a - add hl, bc - - ; hl = OpenInPlayAreaScreen_TextTable + 2 * (wInPlayAreaCurPosition) - ld a, [hli] - ld h, [hl] - ld l, a - ld a, h - - ; jump ahead if entry does not contain null text (it's not active pokemon) - or a - jr nz, .print_hand_or_discard_pile - - ld a, l - ; bench slots have dummy text IDs assigned to them, which are never used. - ; these are secretly not text id's, but rather, 2-byte PLAY_AREA_BENCH_* constants - ; check if the value at register l is one of those, and jump ahead if not - cp PLAY_AREA_BENCH_5 + $01 - jr nc, .print_hand_or_discard_pile - -; if we make it here, we need to print a Pokemon card name. -; wInPlayAreaCurPosition determines which duelist -; and l contains the PLAY_AREA_* location of the card. - ld a, [wInPlayAreaCurPosition] - cp INPLAYAREA_PLAYER_HAND - jr nc, .opponent_side - - ld a, l - add DUELVARS_ARENA_CARD - call GetTurnDuelistVariable - cp -1 - ret z - - call GetCardIDFromDeckIndex - call LoadCardDataToBuffer1_FromCardID - jr .display_card_name - -.opponent_side - ld a, l - add DUELVARS_ARENA_CARD - call GetNonTurnDuelistVariable - cp -1 - ret z - - call SwapTurn - call GetCardIDFromDeckIndex - call LoadCardDataToBuffer1_FromCardID - call SwapTurn - -.display_card_name - ld a, 18 - call CopyCardNameAndLevel - ld hl, wDefaultText - call ProcessText - ret - -.print_hand_or_discard_pile -; if we make it here, cursor position is to Hand or Discard Pile -; so DuelistHandText_2 or DuelistDiscardPileText will be printed - - ld a, [wInPlayAreaCurPosition] - cp INPLAYAREA_OPP_ACTIVE - jr nc, .opp_side_print_hand_or_discard_pile - call PrintTextNoDelay - ret - -.opp_side_print_hand_or_discard_pile - call SwapTurn - call PrintTextNoDelay - call SwapTurn - ret - -.show_turn_holder_play_area - lb de, $38, $9f - call SetupText - ldh a, [hWhoseTurn] - push af - bank1call OpenTurnHolderPlayAreaScreen - pop af - ldh [hWhoseTurn], a - ld a, [wInPlayAreaPreservedPosition] - ld [wInPlayAreaCurPosition], a - jp .start - -.show_non_turn_holder_play_area - lb de, $38, $9f - call SetupText - ldh a, [hWhoseTurn] - push af - bank1call OpenNonTurnHolderPlayAreaScreen - pop af - ldh [hWhoseTurn], a - ld a, [wInPlayAreaPreservedPosition] - ld [wInPlayAreaCurPosition], a - jp .start - -.jump_table ; (6:4228) - dw OpenInPlayAreaScreen_TurnHolderPlayArea ; 0x00: INPLAYAREA_PLAYER_BENCH_1 - dw OpenInPlayAreaScreen_TurnHolderPlayArea ; 0x01: INPLAYAREA_PLAYER_BENCH_2 - dw OpenInPlayAreaScreen_TurnHolderPlayArea ; 0x02: INPLAYAREA_PLAYER_BENCH_3 - dw OpenInPlayAreaScreen_TurnHolderPlayArea ; 0x03: INPLAYAREA_PLAYER_BENCH_4 - dw OpenInPlayAreaScreen_TurnHolderPlayArea ; 0x04: INPLAYAREA_PLAYER_BENCH_5 - dw OpenInPlayAreaScreen_TurnHolderPlayArea ; 0x05: INPLAYAREA_PLAYER_ACTIVE - dw OpenInPlayAreaScreen_TurnHolderHand ; 0x06: INPLAYAREA_PLAYER_HAND - dw OpenInPlayAreaScreen_TurnHolderDiscardPile ; 0x07: INPLAYAREA_PLAYER_DISCARD_PILE - dw OpenInPlayAreaScreen_NonTurnHolderPlayArea ; 0x08: INPLAYAREA_OPP_ACTIVE - dw OpenInPlayAreaScreen_NonTurnHolderHand ; 0x09: INPLAYAREA_OPP_HAND - dw OpenInPlayAreaScreen_NonTurnHolderDiscardPile ; 0x0a: INPLAYAREA_OPP_DISCARD_PILE - dw OpenInPlayAreaScreen_NonTurnHolderPlayArea ; 0x0b: INPLAYAREA_OPP_BENCH_1 - dw OpenInPlayAreaScreen_NonTurnHolderPlayArea ; 0x0c: INPLAYAREA_OPP_BENCH_2 - dw OpenInPlayAreaScreen_NonTurnHolderPlayArea ; 0x0d: INPLAYAREA_OPP_BENCH_3 - dw OpenInPlayAreaScreen_NonTurnHolderPlayArea ; 0x0e: INPLAYAREA_OPP_BENCH_4 - dw OpenInPlayAreaScreen_NonTurnHolderPlayArea ; 0x0f: INPLAYAREA_OPP_BENCH_5 - -OpenInPlayAreaScreen_TurnHolderPlayArea: ; 18248 (6:4248) - ; wInPlayAreaCurPosition constants conveniently map to (PLAY_AREA_* constants - 1) - ; for bench locations. this mapping is taken for granted in the following code. - ld a, [wInPlayAreaCurPosition] - inc a - cp INPLAYAREA_PLAYER_ACTIVE + $01 - jr nz, .on_bench - xor a ; PLAY_AREA_ARENA -.on_bench - ld [wCurPlayAreaSlot], a - add DUELVARS_ARENA_CARD - call GetTurnDuelistVariable - cp -1 - ret z - call GetCardIDFromDeckIndex - call LoadCardDataToBuffer1_FromCardID - xor a - ld [wCurPlayAreaY], a - bank1call OpenCardPage_FromCheckPlayArea - ret - -OpenInPlayAreaScreen_NonTurnHolderPlayArea: ; 1826a (6:426a) - ld a, [wInPlayAreaCurPosition] - sub INPLAYAREA_OPP_ACTIVE - or a - jr z, .active - ; convert INPLAYAREA_OPP_BENCH_* constant to PLAY_AREA_BENCH_* constant - sub INPLAYAREA_OPP_BENCH_1 - INPLAYAREA_OPP_ACTIVE - PLAY_AREA_BENCH_1 -.active - ld [wCurPlayAreaSlot], a - add DUELVARS_ARENA_CARD - call GetNonTurnDuelistVariable - cp -1 - ret z - call SwapTurn - call GetCardIDFromDeckIndex - call LoadCardDataToBuffer1_FromCardID - xor a - ld [wCurPlayAreaY], a - bank1call OpenCardPage_FromCheckPlayArea - call SwapTurn - ret - -OpenInPlayAreaScreen_TurnHolderHand: ; 18293 (6:4293) - ldh a, [hWhoseTurn] - push af - bank1call OpenTurnHolderHandScreen_Simple - pop af - ldh [hWhoseTurn], a - ret - -OpenInPlayAreaScreen_NonTurnHolderHand: ; 1829d (6:429d) - ldh a, [hWhoseTurn] - push af - bank1call OpenNonTurnHolderHandScreen_Simple - pop af - ldh [hWhoseTurn], a - ret - -OpenInPlayAreaScreen_TurnHolderDiscardPile: ; 182a7 (6:42a7) - ldh a, [hWhoseTurn] - push af - bank1call OpenTurnHolderDiscardPileScreen - pop af - ldh [hWhoseTurn], a - ret - -OpenInPlayAreaScreen_NonTurnHolderDiscardPile: ; 182b1 (6:42b1) - ldh a, [hWhoseTurn] - push af - bank1call OpenNonTurnHolderDiscardPileScreen - pop af - ldh [hWhoseTurn], a - ret - -OpenInPlayAreaScreen_TextTable: -; note that for bench slots, the entries are -; PLAY_AREA_BENCH_* constants in practice - tx HandText ; INPLAYAREA_PLAYER_BENCH_1 - tx CheckText ; INPLAYAREA_PLAYER_BENCH_2 - tx AttackText ; INPLAYAREA_PLAYER_BENCH_3 - tx PKMNPowerText ; INPLAYAREA_PLAYER_BENCH_4 - tx DoneText ; INPLAYAREA_PLAYER_BENCH_5 - dw NULL ; INPLAYAREA_PLAYER_ACTIVE - tx DuelistHandText_2 ; INPLAYAREA_PLAYER_HAND - tx DuelistDiscardPileText ; INPLAYAREA_PLAYER_DISCARD_PILE - dw NULL ; INPLAYAREA_OPP_ACTIVE - tx DuelistHandText_2 ; INPLAYAREA_OPP_HAND - tx DuelistDiscardPileText ; INPLAYAREA_OPP_DISCARD_PILE - tx HandText ; INPLAYAREA_OPP_BENCH_1 - tx CheckText ; INPLAYAREA_OPP_BENCH_2 - tx AttackText ; INPLAYAREA_OPP_BENCH_3 - tx PKMNPowerText ; INPLAYAREA_OPP_BENCH_4 - tx DoneText ; INPLAYAREA_OPP_BENCH_5 - -in_play_area_cursor_transition: MACRO - cursor_transition \1, \2, \3, INPLAYAREA_\4, INPLAYAREA_\5, INPLAYAREA_\6, INPLAYAREA_\7 -ENDM - -; it's related to wMenuInputTablePointer. -; with this table, the cursor moves into the proper location by the input. -; note that the unit of the position is not a 8x8 tile. -OpenInPlayAreaScreen_TransitionTable1: - in_play_area_cursor_transition $18, $8c, $00, PLAYER_ACTIVE, PLAYER_PLAY_AREA, PLAYER_BENCH_2, PLAYER_BENCH_5 - in_play_area_cursor_transition $30, $8c, $00, PLAYER_ACTIVE, PLAYER_PLAY_AREA, PLAYER_BENCH_3, PLAYER_BENCH_1 - in_play_area_cursor_transition $48, $8c, $00, PLAYER_ACTIVE, PLAYER_PLAY_AREA, PLAYER_BENCH_4, PLAYER_BENCH_2 - in_play_area_cursor_transition $60, $8c, $00, PLAYER_ACTIVE, PLAYER_PLAY_AREA, PLAYER_BENCH_5, PLAYER_BENCH_3 - in_play_area_cursor_transition $78, $8c, $00, PLAYER_ACTIVE, PLAYER_PLAY_AREA, PLAYER_BENCH_1, PLAYER_BENCH_4 - in_play_area_cursor_transition $30, $6c, $00, OPP_ACTIVE, PLAYER_BENCH_1, PLAYER_DISCARD_PILE, PLAYER_DISCARD_PILE - in_play_area_cursor_transition $78, $80, $00, PLAYER_DISCARD_PILE, PLAYER_BENCH_1, PLAYER_ACTIVE, PLAYER_ACTIVE - in_play_area_cursor_transition $78, $70, $00, OPP_ACTIVE, PLAYER_HAND, PLAYER_ACTIVE, PLAYER_ACTIVE - in_play_area_cursor_transition $78, $34, 1 << OAM_X_FLIP, OPP_BENCH_1, PLAYER_ACTIVE, OPP_DISCARD_PILE, OPP_DISCARD_PILE - in_play_area_cursor_transition $30, $20, 1 << OAM_X_FLIP, OPP_BENCH_1, OPP_DISCARD_PILE, OPP_ACTIVE, OPP_ACTIVE - in_play_area_cursor_transition $30, $38, 1 << OAM_X_FLIP, OPP_BENCH_1, PLAYER_ACTIVE, OPP_ACTIVE, OPP_ACTIVE - in_play_area_cursor_transition $90, $14, 1 << OAM_X_FLIP, OPP_PLAY_AREA, OPP_ACTIVE, OPP_BENCH_5, OPP_BENCH_2 - in_play_area_cursor_transition $78, $14, 1 << OAM_X_FLIP, OPP_PLAY_AREA, OPP_ACTIVE, OPP_BENCH_1, OPP_BENCH_3 - in_play_area_cursor_transition $60, $14, 1 << OAM_X_FLIP, OPP_PLAY_AREA, OPP_ACTIVE, OPP_BENCH_2, OPP_BENCH_4 - in_play_area_cursor_transition $48, $14, 1 << OAM_X_FLIP, OPP_PLAY_AREA, OPP_ACTIVE, OPP_BENCH_3, OPP_BENCH_5 - in_play_area_cursor_transition $30, $14, 1 << OAM_X_FLIP, OPP_PLAY_AREA, OPP_ACTIVE, OPP_BENCH_4, OPP_BENCH_1 - -OpenInPlayAreaScreen_TransitionTable2: - in_play_area_cursor_transition $18, $8c, $00, PLAYER_ACTIVE, PLAYER_PLAY_AREA, PLAYER_BENCH_2, PLAYER_BENCH_5 - in_play_area_cursor_transition $30, $8c, $00, PLAYER_ACTIVE, PLAYER_PLAY_AREA, PLAYER_BENCH_3, PLAYER_BENCH_1 - in_play_area_cursor_transition $48, $8c, $00, PLAYER_ACTIVE, PLAYER_PLAY_AREA, PLAYER_BENCH_4, PLAYER_BENCH_2 - in_play_area_cursor_transition $60, $8c, $00, PLAYER_ACTIVE, PLAYER_PLAY_AREA, PLAYER_BENCH_5, PLAYER_BENCH_3 - in_play_area_cursor_transition $78, $8c, $00, PLAYER_ACTIVE, PLAYER_PLAY_AREA, PLAYER_BENCH_1, PLAYER_BENCH_4 - in_play_area_cursor_transition $30, $6c, $00, OPP_ACTIVE, PLAYER_BENCH_1, PLAYER_DISCARD_PILE, PLAYER_DISCARD_PILE - in_play_area_cursor_transition $78, $80, $00, PLAYER_DISCARD_PILE, PLAYER_BENCH_1, PLAYER_ACTIVE, PLAYER_ACTIVE - in_play_area_cursor_transition $78, $70, $00, OPP_ACTIVE, PLAYER_HAND, PLAYER_ACTIVE, PLAYER_ACTIVE - in_play_area_cursor_transition $78, $34, 1 << OAM_X_FLIP, OPP_BENCH_1, PLAYER_ACTIVE, OPP_DISCARD_PILE, OPP_DISCARD_PILE - in_play_area_cursor_transition $30, $20, 1 << OAM_X_FLIP, OPP_BENCH_1, OPP_DISCARD_PILE, OPP_ACTIVE, OPP_ACTIVE - in_play_area_cursor_transition $30, $38, 1 << OAM_X_FLIP, OPP_HAND, PLAYER_ACTIVE, OPP_ACTIVE, OPP_ACTIVE - in_play_area_cursor_transition $90, $14, 1 << OAM_X_FLIP, OPP_PLAY_AREA, OPP_ACTIVE, OPP_BENCH_5, OPP_BENCH_2 - in_play_area_cursor_transition $78, $14, 1 << OAM_X_FLIP, OPP_PLAY_AREA, OPP_ACTIVE, OPP_BENCH_1, OPP_BENCH_3 - in_play_area_cursor_transition $60, $14, 1 << OAM_X_FLIP, OPP_PLAY_AREA, OPP_ACTIVE, OPP_BENCH_2, OPP_BENCH_4 - in_play_area_cursor_transition $48, $14, 1 << OAM_X_FLIP, OPP_PLAY_AREA, OPP_ACTIVE, OPP_BENCH_3, OPP_BENCH_5 - in_play_area_cursor_transition $30, $14, 1 << OAM_X_FLIP, OPP_PLAY_AREA, OPP_ACTIVE, OPP_BENCH_4, OPP_BENCH_1 - -OpenInPlayAreaScreen_HandleInput: ; 183bb (6:43bb) - xor a - ld [wPlaysSfx], a - ld hl, wMenuInputTablePointer - ld e, [hl] - inc hl - ld d, [hl] - ld a, [wInPlayAreaCurPosition] - ld l, a - ld h, $07 - call HtimesL - add hl, de - - ldh a, [hDPadHeld] - or a - jp z, .check_button - - inc hl - inc hl - inc hl - - ; check d-pad - bit D_UP_F, a - jr z, .else_if_down - - ; up - ld a, [hl] - jr .process_dpad - -.else_if_down - inc hl - bit D_DOWN_F, a - jr z, .else_if_right - - ; down - ld a, [hl] - jr .process_dpad - -.else_if_right - inc hl - bit D_RIGHT_F, a - jr z, .else_if_left - - ; right - ld a, [hl] - jr .process_dpad - -.else_if_left - inc hl - bit D_LEFT_F, a - jr z, .check_button - - ; left - ld a, [hl] -.process_dpad - push af - ld a, [wInPlayAreaCurPosition] - ld [wInPlayAreaPreservedPosition], a - pop af - - ld [wInPlayAreaCurPosition], a - cp INPLAYAREA_PLAYER_ACTIVE - jr c, .player_area - cp INPLAYAREA_OPP_BENCH_1 - jr c, .next - cp INPLAYAREA_PLAYER_PLAY_AREA - jr c, .opponent_area - - jr .next - -.player_area - ld a, DUELVARS_NUMBER_OF_POKEMON_IN_PLAY_AREA - call GetTurnDuelistVariable - dec a - jr nz, .bench_pokemon_exists - - ; no pokemon in player's bench. - ; then move to player's play area. - ld a, INPLAYAREA_PLAYER_PLAY_AREA - ld [wInPlayAreaCurPosition], a - jr .next - -.bench_pokemon_exists - ld b, a - ld a, [wInPlayAreaCurPosition] - cp b - jr c, .next - - ; handle index overflow - ldh a, [hDPadHeld] - bit D_RIGHT_F, a - jr z, .on_left - - xor a - ld [wInPlayAreaCurPosition], a - jr .next - -.on_left - ld a, b - dec a - ld [wInPlayAreaCurPosition], a - jr .next - -.opponent_area - ld a, DUELVARS_NUMBER_OF_POKEMON_IN_PLAY_AREA - call GetNonTurnDuelistVariable - dec a - jr nz, .bench_pokemon_exists_2 - - ld a, INPLAYAREA_OPP_PLAY_AREA - ld [wInPlayAreaCurPosition], a - jr .next - -.bench_pokemon_exists_2 - ld b, a - ld a, [wInPlayAreaCurPosition] - sub INPLAYAREA_OPP_BENCH_1 - cp b - jr c, .next - - ldh a, [hDPadHeld] - bit D_LEFT_F, a - jr z, .on_right - - ld a, INPLAYAREA_OPP_BENCH_1 - ld [wInPlayAreaCurPosition], a - jr .next - -.on_right - ld a, b - add INPLAYAREA_OPP_DISCARD_PILE - ld [wInPlayAreaCurPosition], a -.next - ld a, $01 - ld [wPlaysSfx], a - xor a - ld [wCheckMenuCursorBlinkCounter], a -.check_button - ldh a, [hKeysPressed] - and A_BUTTON | B_BUTTON - jr z, .return - - and A_BUTTON - jr nz, .a_button - - ; pressed b button - ld a, -1 - farcall PlaySFXConfirmOrCancel - scf - ret - -.a_button - call .draw_cursor - ld a, $01 - farcall PlaySFXConfirmOrCancel - ld a, [wInPlayAreaCurPosition] - scf - ret - -.return - ld a, [wPlaysSfx] - or a - jr z, .skip_sfx - call PlaySFX -.skip_sfx - ld hl, wCheckMenuCursorBlinkCounter - ld a, [hl] - inc [hl] - and $10 - 1 - ret nz - - bit 4, [hl] ; = and $10 - jr nz, ZeroObjectPositionsAndToggleOAMCopy_Bank6 - -.draw_cursor ; 184a0 (6:44a0) - call ZeroObjectPositions - ld hl, wMenuInputTablePointer - ld e, [hl] - inc hl - ld d, [hl] - ld a, [wInPlayAreaCurPosition] - ld l, a - ld h, $07 - call HtimesL - add hl, de - - ld d, [hl] ; x position. - inc hl - ld e, [hl] ; y position. - inc hl - ld b, [hl] ; attribute. - ld c, $00 - call SetOneObjectAttributes - or a - ret - -ZeroObjectPositionsAndToggleOAMCopy_Bank6: ; 184bf (6:44bf) - call ZeroObjectPositions - ld a, $01 - ld [wVBlankOAMCopyToggle], a - ret - -OpenGlossaryScreen: ; 184c8 (6:44c8) - xor a - ld [wGlossaryPageNo], a - call .display_menu - - xor a - ld [wInPlayAreaCurPosition], a - ld de, OpenGlossaryScreen_TransitionTable ; this data is stored in bank 2. - ld hl, wMenuInputTablePointer - ld [hl], e - inc hl - ld [hl], d - ld a, $ff - ld [wDuelInitialPrizesUpperBitsSet], a - xor a - ld [wCheckMenuCursorBlinkCounter], a -.next - ld a, $01 - ld [wVBlankOAMCopyToggle], a - call DoFrame - ldh a, [hKeysPressed] - and SELECT - jr nz, .on_select - - farcall YourOrOppPlayAreaScreen_HandleInput - jr nc, .next - - cp -1 ; b button - jr nz, .check_button - - farcall ZeroObjectPositionsWithCopyToggleOn - ret - -.check_button - push af - farcall ZeroObjectPositionsWithCopyToggleOn - pop af - - cp $09 ; $09: next page or prev page - jr z, .change_page - - call .print_description - call .display_menu - xor a - ld [wCheckMenuCursorBlinkCounter], a - jr .next - -.on_select - ld a, $01 - farcall PlaySFXConfirmOrCancel -.change_page - ld a, [wGlossaryPageNo] - xor $01 ; swap page - ld [wGlossaryPageNo], a - call .print_menu - jr .next - -; display glossary menu. -.display_menu ; 1852b (6:452b) - xor a - ld [wTileMapFill], a - call ZeroObjectPositions - ld a, $01 - ld [wVBlankOAMCopyToggle], a - call DoFrame - call EmptyScreen - call Set_OBJ_8x8 - farcall LoadCursorTile - - lb de, 5, 0 - call InitTextPrinting - ldtx hl, PokemonCardGlossaryText - call ProcessTextFromID - call .print_menu - ldtx hl, ChooseWordAndPressAButtonText - call DrawWideTextBox_PrintText - ret - -; print texts in glossary menu. -.print_menu ; 1855a (6:455a) - ld hl, wDefaultText - - ld a, TX_SYMBOL - ld [hli], a - - ld a, [wGlossaryPageNo] - add SYM_1 - ld [hli], a - - ld a, TX_SYMBOL - ld [hli], a - - ld a, SYM_SLASH - ld [hli], a - - ld a, TX_SYMBOL - ld [hli], a - - ld a, SYM_2 - ld [hli], a - - ld [hl], TX_END - - lb de, 16, 1 - call InitTextPrinting - ld hl, wDefaultText - call ProcessText - - lb de, 1, 3 - call InitTextPrinting - ld a, [wGlossaryPageNo] - or a - jr nz, .page_two - - ldtx hl, GlossaryMenuPage1Text - jr .page_one - -.page_two - ldtx hl, GlossaryMenuPage2Text -.page_one - call ProcessTextFromID - ret - -; display glossary description. -.print_description ; 18598 (6:4598) - push af - xor a - ld [wTileMapFill], a - call EmptyScreen - lb de, 5, 0 - call InitTextPrinting - ldtx hl, PokemonCardGlossaryText - call ProcessTextFromID - lb de, 0, 4 - lb bc, 20, 14 - call DrawRegularTextBox - - ld a, [wGlossaryPageNo] - or a - jr nz, .back_page - - ld hl, GlossaryData1 - jr .front_page - -.back_page - ld hl, GlossaryData2 -.front_page - pop af - ; hl += (a + (a << 2)). - ; that is, - ; hl += (5 * a). - ld c, a - ld b, 0 - add hl, bc - sla a - sla a - ld c, a - add hl, bc - ld a, [hli] - push hl - ld d, a - ld e, $02 - call InitTextPrinting - ld a, [hli] - ld h, [hl] - ld l, a - call ProcessTextFromID - pop hl - lb de, 1, 5 - call InitTextPrinting - inc hl - inc hl - ld a, [hli] - ld h, [hl] - ld l, a - ld a, $01 - ld [wLineSeparation], a - call ProcessTextFromID - xor a - ld [wLineSeparation], a - call EnableLCD -.loop - call DoFrame - ldh a, [hKeysPressed] - and B_BUTTON - jr z, .loop - - ld a, -1 - farcall PlaySFXConfirmOrCancel - ret - -; unit: 5 bytes. -; [structure] -; horizontal align (1) / title text id (2) / desc. text id (2) -glossary_entry: MACRO - db \1 - tx \2 - tx \3 -ENDM - -GlossaryData1: - glossary_entry 7, AboutTheDeckText, DeckDescriptionText - glossary_entry 5, AboutTheDiscardPileText, DiscardPileDescriptionText - glossary_entry 7, AboutTheHandText, HandDescriptionText - glossary_entry 6, AboutTheArenaText, ArenaDescriptionText - glossary_entry 6, AboutTheBenchText, BenchDescriptionText - glossary_entry 4, AboutTheActivePokemonText, ActivePokemonDescriptionText - glossary_entry 5, AboutBenchPokemonText, BenchPokemonDescriptionText - glossary_entry 7, AboutPrizesText, PrizesDescriptionText - glossary_entry 5, AboutDamageCountersText, DamageCountersDescriptionText - -GlossaryData2: - glossary_entry 5, AboutEnergyCardsText, EnergyCardsDescriptionText - glossary_entry 5, AboutTrainerCardsText, TrainerCardsDescriptionText - glossary_entry 5, AboutBasicPokemonText, BasicPokemonDescriptionText - glossary_entry 5, AboutEvolutionCardsText, EvolutionCardsDescriptionText - glossary_entry 6, AboutAttackingText, AttackingDescriptionText - glossary_entry 5, AboutPokemonPowerText, PokemonPowerDescriptionText - glossary_entry 6, AboutWeaknessText, WeaknessDescriptionText - glossary_entry 6, AboutResistanceText, ResistanceDescriptionText - glossary_entry 6, AboutRetreatingText, RetreatingDescriptionText - -Func_18661: ; 18661 (6:4661) - xor a - ld [wPlaysSfx], a - ld a, [wCheckMenuCursorXPosition] - ld d, a - ld a, [wCheckMenuCursorYPosition] - ld e, a - ldh a, [hDPadHeld] - or a - jr z, .check_button -; check input from dpad - bit D_LEFT_F, a - jr nz, .left_or_right - bit D_RIGHT_F, a - jr z, .check_up_and_down -.left_or_right -; swap the lsb of x position value. - ld a, d - xor $1 - ld d, a - jr .cursor_moved - -.check_up_and_down - bit D_UP_F, a - jr nz, .up_or_down - bit D_DOWN_F, a - jr z, .check_button -.up_or_down - ld a, e - xor $1 - ld e, a -.cursor_moved - ld a, $1 - ld [wPlaysSfx], a - push de - call .draw_blank_cursor - pop de - ld a, d - ld [wCheckMenuCursorXPosition], a - ld a, e - ld [wCheckMenuCursorYPosition], a - xor a - ld [wCheckMenuCursorBlinkCounter], a -.check_button - ldh a, [hKeysPressed] - and A_BUTTON | B_BUTTON - jr z, .check_cursor_moved - and A_BUTTON - jr nz, .a_button - -; b button - ld a, -1 - call Func_190fb - scf - ret - -; a button -.a_button - call .draw_cursor - ld a, 1 - call Func_190fb - scf - ret - -.check_cursor_moved - ld a, [wPlaysSfx] - or a - jr z, .check_cursor_blink - call PlaySFX -.check_cursor_blink - ld hl, wCheckMenuCursorBlinkCounter - ld a, [hl] - inc [hl] - and %00001111 - ret nz - ld a, SYM_CURSOR_R - bit D_RIGHT_F, [hl] - jr z, .draw_tile -.draw_blank_cursor ; 186d4 (6:46d4) - ld a, SYM_SPACE -.draw_tile - ld e, a - ld a, 10 - ld l, a - ld a, [wCheckMenuCursorXPosition] - ld h, a - call HtimesL - ld a, l - add 1 - ld b, a - ld a, [wCheckMenuCursorYPosition] - sla a - add 14 - ld c, a - ld a, e - ; b = 11, c = y_pos * 2 + 14 - ; h = x_pos * 10, l = 10 - call WriteByteToBGMap0 - or a - ret -.draw_cursor ; 186f3 (6:46f3) - ld a, SYM_CURSOR_R - jr .draw_tile - -INCLUDE "data/duel/effect_commands.asm" - -; reads the animation commands from PointerTable_AttackAnimation -; of attack in wLoadedAttackAnimation and plays them -PlayAttackAnimationCommands: ; 18f9c (6:4f9c) - ld a, [wLoadedAttackAnimation] - or a - ret z - - ld l, a - ld h, 0 - add hl, hl - ld de, PointerTable_AttackAnimation - add hl, de - ld e, [hl] - inc hl - ld d, [hl] - - push de - ld hl, wce7e - ld a, [hl] - or a - jr nz, .read_command - ld [hl], $01 - call Func_3b21 - pop de - - push de - ld a, DUEL_ANIM_SCREEN_MAIN_SCENE - ld [wDuelAnimationScreen], a - ld a, $01 - ld [wd4b3], a - xor a - ld [wDuelAnimLocationParam], a - ld a, [de] - cp $04 - jr z, .read_command - ld a, DUEL_ANIM_150 - call PlayDuelAnimation -.read_command - pop de - ; fallthrough - -PlayAttackAnimationCommands_NextCommand: ; 18fd4 (6:4fd4) - ld a, [de] - inc de - ld hl, AnimationCommandPointerTable - jp JumpToFunctionInTable - -AnimationCommand_AnimEnd: ; 18fdc (6:4fdc) - ret - -AnimationCommand_AnimPlayer: ; 18fdd (6:4fdd) - ldh a, [hWhoseTurn] - ld [wDuelAnimDuelistSide], a - ld a, [wDuelType] - cp $00 - jr nz, AnimationCommand_AnimNormal - ld a, PLAYER_TURN - ld [wDuelAnimDuelistSide], a - jr AnimationCommand_AnimNormal - -AnimationCommand_AnimOpponent: ; 18ff0 (6:4ff0) - call SwapTurn - ldh a, [hWhoseTurn] - ld [wDuelAnimDuelistSide], a - call SwapTurn - ld a, [wDuelType] - cp $00 - jr nz, AnimationCommand_AnimNormal - ld a, OPPONENT_TURN - ld [wDuelAnimDuelistSide], a - jr AnimationCommand_AnimNormal - -AnimationCommand_AnimUnknown2: ; 19009 (6:5009) - ld a, [wce82] - and $7f - ld [wDuelAnimLocationParam], a - jr AnimationCommand_AnimNormal - -AnimationCommand_AnimEnd2: ; 19013 (6:5013) - ret - -AnimationCommand_AnimNormal: ; 19014 (6:5014) - ld a, [de] - inc de - cp DUEL_ANIM_SHOW_DAMAGE - jr z, .show_damage - cp DUEL_ANIM_SHAKE1 - jr z, .shake_1 - cp DUEL_ANIM_SHAKE2 - jr z, .shake_2 - cp DUEL_ANIM_SHAKE3 - jr z, .shake_3 - -.play_anim - call PlayDuelAnimation - jr PlayAttackAnimationCommands_NextCommand - -.show_damage - ld a, DUEL_ANIM_PRINT_DAMAGE - call PlayDuelAnimation - ld a, [wce81] - ld [wd4b3], a - - push de - ld hl, wce7f - ld de, wDuelAnimDamage - ld a, [hli] - ld [de], a - inc de - ld a, [hli] - ld [de], a - pop de - - ld a, $8c - call PlayDuelAnimation - ld a, [wDuelDisplayedScreen] - cp DUEL_MAIN_SCENE - jr nz, .skip_update_hud - ld a, DUEL_ANIM_UPDATE_HUD - call PlayDuelAnimation -.skip_update_hud - jp PlayAttackAnimationCommands_NextCommand - -; screen shake happens differently -; depending on whose turn it is -.shake_1 - ld c, DUEL_ANIM_SMALL_SHAKE_X - ld b, DUEL_ANIM_SMALL_SHAKE_Y - jr .check_duelist - -.shake_2 - ld c, DUEL_ANIM_BIG_SHAKE_X - ld b, DUEL_ANIM_BIG_SHAKE_Y - jr .check_duelist - -.shake_3 - ld c, DUEL_ANIM_SMALL_SHAKE_Y - ld b, DUEL_ANIM_SMALL_SHAKE_X - -.check_duelist - ldh a, [hWhoseTurn] - cp PLAYER_TURN - ld a, c - jr z, .play_anim - ld a, [wDuelType] - cp $00 - ld a, c - jr z, .play_anim - ld a, b - jr .play_anim - -AnimationCommand_AnimUnknown: ; 19079 (6:5079) - ld a, [de] - inc de - ld [wd4b3], a - ld a, [wce82] - ld [wDuelAnimLocationParam], a - call SetDuelAnimationScreen - ld a, DUEL_ANIM_150 - call PlayDuelAnimation - jp PlayAttackAnimationCommands_NextCommand - -AnimationCommandPointerTable: ; 1908f (6:508f) - dw AnimationCommand_AnimEnd ; anim_end - dw AnimationCommand_AnimNormal ; anim_normal - dw AnimationCommand_AnimPlayer ; anim_player - dw AnimationCommand_AnimOpponent ; anim_opponent - dw AnimationCommand_AnimUnknown ; anim_unknown - dw AnimationCommand_AnimUnknown2 ; anim_unknown2 - dw AnimationCommand_AnimEnd2 ; anim_end2 (unused) - -; sets wDuelAnimationScreen according to wd4b3 -; if wd4b3 == $01, set it to Main Scene -; if wd4b3 == $04, st it to Play Area scene -SetDuelAnimationScreen: ; 1909d (6:509d) - ld a, [wd4b3] - cp $04 - jr z, .set_play_area_screen - cp $01 - ret nz - ld a, DUEL_ANIM_SCREEN_MAIN_SCENE - ld [wDuelAnimationScreen], a - ret - -.set_play_area_screen - ld a, [wDuelAnimLocationParam] - ld l, a - ld a, [wWhoseTurn] - ld h, a - cp PLAYER_TURN - jr z, .player - -; opponent - ld a, [wDuelType] - cp $00 - jr z, .asm_50c6 - -; link duel or vs. AI - bit 7, l - jr z, .asm_50e2 - jr .asm_50d2 - -.asm_50c6 - bit 7, l - jr z, .asm_50da - jr .asm_50ea - -.player - bit 7, l - jr z, .asm_50d2 - jr .asm_50e2 - -.asm_50d2 - ld l, UNKNOWN_SCREEN_4 - ld h, PLAYER_TURN - ld a, DUEL_ANIM_SCREEN_PLAYER_PLAY_AREA - jr .ok -.asm_50da - ld l, UNKNOWN_SCREEN_4 - ld h, OPPONENT_TURN - ld a, DUEL_ANIM_SCREEN_PLAYER_PLAY_AREA - jr .ok -.asm_50e2 - ld l, UNKNOWN_SCREEN_5 - ld h, OPPONENT_TURN - ld a, DUEL_ANIM_SCREEN_OPP_PLAY_AREA - jr .ok -.asm_50ea - ld l, UNKNOWN_SCREEN_5 - ld h, PLAYER_TURN - ld a, DUEL_ANIM_SCREEN_OPP_PLAY_AREA - -.ok - ld [wDuelAnimationScreen], a - ret - -Func_190f4: ; 190f4 (6:50f4) - ld a, [wd4b3] - cp $04 - jr z, Func_1910f - ; fallthrough - -Func_190fb: ; 190fb (6:50fb) - cp $01 - jr nz, .asm_510e - ld a, DUEL_ANIM_SCREEN_MAIN_SCENE - ld [wDuelAnimationScreen], a - ld a, [wDuelDisplayedScreen] - cp $01 - jr z, .asm_510e - bank1call DrawDuelMainScene -.asm_510e - ret - -Func_1910f: ; 1910f (6:510f) - call SetDuelAnimationScreen - ld a, [wDuelDisplayedScreen] - cp l - jr z, .skip_change_screen - ld a, l - push af - ld l, PLAYER_TURN - ld a, [wDuelType] - cp $00 - jr nz, .asm_5127 - ld a, [wWhoseTurn] - ld l, a -.asm_5127 - call DrawYourOrOppPlayAreaScreen_Bank0 - pop af - ld [wDuelDisplayedScreen], a -.skip_change_screen - call DrawWideTextBox - ret - -; prints text related to the damage received -; by card stored in wTempNonTurnDuelistCardID -; takes into account type effectiveness -PrintDamageText: ; 19132 (6:5132) - push hl - push bc - push de - ld a, [wLoadedAttackAnimation] - cp ATK_ANIM_HEAL - jr z, .skip - cp ATK_ANIM_HEALING_WIND_PLAY_AREA - jr z, .skip - - ld a, [wTempNonTurnDuelistCardID] - ld e, a - ld d, $00 - call LoadCardDataToBuffer1_FromCardID - ld a, 18 - call CopyCardNameAndLevel - ld [hl], TX_END - ld hl, wTxRam2 - xor a - ld [hli], a - ld [hl], a - ld hl, wce7f - ld a, [hli] - ld h, [hl] - ld l, a - call GetDamageText - ld a, l - or h - call nz, DrawWideTextBox_PrintText -.skip - pop de - pop bc - pop hl - ret - -; returns in hl the text id associated with -; the damage in hl and its effectiveness -GetDamageText: ; 19168 (6:5168) - ld a, l - or h - jr z, .no_damage - call LoadTxRam3 - ld a, [wce81] - ldtx hl, AttackDamageText - and (1 << RESISTANCE) | (1 << WEAKNESS) - ret z ; not weak or resistant - ldtx hl, WeaknessMoreDamage2Text - cp (1 << RESISTANCE) | (1 << WEAKNESS) - ret z ; weak and resistant - and (1 << WEAKNESS) - ldtx hl, WeaknessMoreDamageText - ret nz ; weak - ldtx hl, ResistanceLessDamageText - ret ; resistant - -.no_damage - call CheckNoDamageOrEffect - ret c - ldtx hl, NoDamageText - ld a, [wce81] - and (1 << RESISTANCE) - ret z ; not resistant - ldtx hl, ResistanceNoDamageText - ret ; resistant - -UpdateMainSceneHUD: ; 19199 (6:5199) - ld a, [wDuelDisplayedScreen] - cp DUEL_MAIN_SCENE - ret nz - bank1call DrawDuelHUDs - ret - -Func_191a3: ; 191a3 (6:51a3) - ret - -INCLUDE "data/duel/animations/attack_animations.asm" - -; if carry flag is set, only delays -; if carry not set: -; - set rRP to $c1, wait; -; - set rRP to $c0, wait; -; - return -Func_19674: ; 19674 (6:5674) - jr c, .delay_once - ld [hl], $c1 - ld a, 5 - jr .loop_delay_1 ; jump to possibly to add more cycles? -.loop_delay_1 - dec a - jr nz, .loop_delay_1 - ld [hl], $c0 - ld a, 14 - jr .loop_delay_2 ; jump to possibly to add more cycles? -.loop_delay_2 - dec a - jr nz, .loop_delay_2 - ret - -.delay_once - ld a, 21 - jr .loop_delay_3 ; jump to possibly to add more cycles? -.loop_delay_3 - dec a - jr nz, .loop_delay_3 - nop - ret - -; input a = byte to transmit through IR -TransmitByteThroughIR: ; 19692 (6:5692) - push hl - ld hl, rRP - push de - push bc - ld b, a - scf ; carry set - call Func_19674 - or a ; carry not set - call Func_19674 - ld c, 8 - ld c, 8 ; number of input bits -.loop - ld a, $00 - rr b - call Func_19674 - dec c - jr nz, .loop - pop bc - pop de - pop hl - ldh a, [rJOYP] - bit 1, a ; P11 - jr z, ReturnZFlagUnsetAndCarryFlagSet - xor a ; return z set - ret - -; same as ReceiveByteThroughIR but -; returns $0 in a if there's an error in IR -ReceiveByteThroughIR_ZeroIfUnsuccessful: ; 196ba (6:56ba) - call ReceiveByteThroughIR - ret nc - xor a - ret - -; returns carry if there's some time out -; and output in register a of $ff -; otherwise returns in a some sequence of bits -; related to how rRP sets/unsets bit 1 -ReceiveByteThroughIR: ; 196c0 (6:56c0) - push de - push bc - push hl - -; waits for bit 1 in rRP to be unset -; up to $100 loops - ld b, 0 - ld hl, rRP -.wait_ir - bit 1, [hl] - jr z, .ok - dec b - jr nz, .wait_ir - ; looped around $100 times - ; return $ff and carry set - pop hl - pop bc - pop de - scf - ld a, $ff - ret - -.ok -; delay for some cycles - ld a, 15 -.loop_delay - dec a - jr nz, .loop_delay - -; loop for each bit - ld e, 8 -.loop - ld a, $01 - ; possibly delay cycles? - ld b, 9 - ld b, 9 - ld b, 9 - ld b, 9 - -; checks for bit 1 in rRP -; if in any of the checks it is unset, -; then a is set to 0 -; this is done a total of 9 times - bit 1, [hl] - jr nz, .asm_196ec - xor a -.asm_196ec - bit 1, [hl] - jr nz, .asm_196f1 - xor a -.asm_196f1 - dec b - jr nz, .asm_196ec - ; one bit received - rrca - rr d - dec e - jr nz, .loop - ld a, d ; has bits set for each "cycle" that bit 1 was not unset - pop hl - pop bc - pop de - or a - ret - -ReturnZFlagUnsetAndCarryFlagSet: ; 19700 (6:5700) - ld a, $ff - or a ; z not set - scf ; carry set - ret - -; called when expecting to transmit data -Func_19705: ; 19705 (6:5705) - ld hl, rRP -.asm_19708 - ldh a, [rJOYP] - bit 1, a - jr z, ReturnZFlagUnsetAndCarryFlagSet - ld a, $aa ; request - call TransmitByteThroughIR - push hl - pop hl - call ReceiveByteThroughIR_ZeroIfUnsuccessful - cp $33 ; acknowledge - jr nz, .asm_19708 - xor a - ret - -; called when expecting to receive data -Func_1971e: ; 1971e (6:571e) - ld hl, rRP -.asm_19721 - ldh a, [rJOYP] - bit 1, a - jr z, ReturnZFlagUnsetAndCarryFlagSet - call ReceiveByteThroughIR_ZeroIfUnsuccessful - cp $aa ; request - jr nz, .asm_19721 - ld a, $33 ; acknowledge - call TransmitByteThroughIR - xor a - ret - -ReturnZFlagUnsetAndCarryFlagSet2: ; 19735 (6:5735) - jp ReturnZFlagUnsetAndCarryFlagSet - -TransmitIRDataBuffer: ; 19738 (6:5738) - call Func_19705 - jr c, ReturnZFlagUnsetAndCarryFlagSet2 - ld a, $49 - call TransmitByteThroughIR - ld a, $52 - call TransmitByteThroughIR - ld hl, wIRDataBuffer - ld c, 8 - jr TransmitNBytesFromHLThroughIR - -ReceiveIRDataBuffer: ; 1974e (6:5738) - call Func_1971e - jr c, ReturnZFlagUnsetAndCarryFlagSet2 - call ReceiveByteThroughIR - cp $49 - jr nz, ReceiveIRDataBuffer - call ReceiveByteThroughIR - cp $52 - jr nz, ReceiveIRDataBuffer - ld hl, wIRDataBuffer - ld c, 8 - jr ReceiveNBytesToHLThroughIR - -; hl = start of data to transmit -; c = number of bytes to transmit -TransmitNBytesFromHLThroughIR: ; 19768 (6:5768) - ld b, $0 -.loop_data_bytes - ld a, b - add [hl] - ld b, a - ld a, [hli] - call TransmitByteThroughIR - jr c, .asm_1977c - dec c - jr nz, .loop_data_bytes - ld a, b - cpl - inc a - call TransmitByteThroughIR -.asm_1977c - ret - -; hl = address to write received data -; c = number of bytes to be received -ReceiveNBytesToHLThroughIR: ; 1977d (6:577d) - ld b, 0 -.loop_data_bytes - call ReceiveByteThroughIR - jr c, ReturnZFlagUnsetAndCarryFlagSet2 - ld [hli], a - add b - ld b, a - dec c - jr nz, .loop_data_bytes - call ReceiveByteThroughIR - add b - or a - jr nz, ReturnZFlagUnsetAndCarryFlagSet2 - ret - -; disables interrupts, and sets joypad and IR communication port -; switches to CGB normal speed -StartIRCommunications: ; 19792 (6:5792) - di - call SwitchToCGBNormalSpeed - ld a, P14 - ldh [rJOYP], a - ld a, $c0 - ldh [rRP], a - ret - -; reenables interrupts, and switches CGB back to double speed -CloseIRCommunications: ; 1979f (6:579f) - ld a, P14 | P15 - ldh [rJOYP], a -.wait_vblank_on - ldh a, [rSTAT] - and STAT_LCDC_STATUS - cp STAT_ON_VBLANK - jr z, .wait_vblank_on -.wait_vblank_off - ldh a, [rSTAT] - and STAT_LCDC_STATUS - cp STAT_ON_VBLANK - jr nz, .wait_vblank_off - call SwitchToCGBDoubleSpeed - ei - ret - -; set rRP to 0 -ClearRP: ; 197b8 (6:57b8) - ld a, $00 - ldh [rRP], a - ret - -; expects to receive a command (IRCMD_* constant) -; in wIRDataBuffer + 1, then calls the subroutine -; corresponding to that command -ExecuteReceivedIRCommands: ; 197bd (6:57bd) - call StartIRCommunications -.loop_commands - call ReceiveIRDataBuffer - jr c, .error - jr nz, .loop_commands - ld hl, wIRDataBuffer + 1 - ld a, [hl] - ld hl, .CmdPointerTable - cp NUM_IR_COMMANDS - jr nc, .loop_commands ; invalid command - call .JumpToCmdPointer ; execute command - jr .loop_commands -.error - call CloseIRCommunications - xor a - scf - ret - -.JumpToCmdPointer - add a ; *2 - add l - ld l, a - ld a, 0 - adc h - ld h, a - ld a, [hli] - ld h, [hl] - ld l, a -.jp_hl - jp hl - -.CmdPointerTable - dw .Close ; IRCMD_CLOSE - dw .ReturnWithoutClosing ; IRCMD_RETURN_WO_CLOSING - dw .TransmitData ; IRCMD_TRANSMIT_DATA - dw .ReceiveData ; IRCMD_RECEIVE_DATA - dw .CallFunction ; IRCMD_CALL_FUNCTION - -; closes the IR communications -; pops hl so that the sp points -; to the return address of ExecuteReceivedIRCommands -.Close - pop hl - call CloseIRCommunications - or a - ret - -; returns without closing the IR communications -; will continue the command loop -.ReturnWithoutClosing - or a - ret - -; receives an address and number of bytes -; and transmits starting at that address -.TransmitData - call Func_19705 - ret c - call LoadRegistersFromIRDataBuffer - jp TransmitNBytesFromHLThroughIR - -; receives an address and number of bytes -; and writes the data received to that address -.ReceiveData - call LoadRegistersFromIRDataBuffer - ld l, e - ld h, d - call ReceiveNBytesToHLThroughIR - jr c, .asm_19812 - sub b - call TransmitByteThroughIR -.asm_19812 - ret - -; receives an address to call, then stores -; the registers in the IR data buffer -.CallFunction - call LoadRegistersFromIRDataBuffer - call .jp_hl - call StoreRegistersInIRDataBuffer - ret - -; returns carry set if request sent was not acknowledged -TrySendIRRequest: ; 1981d (6:581d) - call StartIRCommunications - ld hl, rRP - ld c, 4 -.send_request - ld a, $aa ; request - push bc - call TransmitByteThroughIR - push bc - pop bc - call ReceiveByteThroughIR_ZeroIfUnsuccessful - pop bc - cp $33 ; acknowledgement - jr z, .received_ack - dec c - jr nz, .send_request - scf - jr .close - -.received_ack - xor a -.close - push af - call CloseIRCommunications - pop af - ret - -; returns carry set if request was not received -TryReceiveIRRequest: ; 19842 (6:5842) - call StartIRCommunications - ld hl, rRP -.wait_request - call ReceiveByteThroughIR_ZeroIfUnsuccessful - cp $aa ; request - jr z, .send_ack - ldh a, [rJOYP] - cpl - and P10 | P11 - jr z, .wait_request - scf - jr .close - -.send_ack - ld a, $33 ; acknowledgement - call TransmitByteThroughIR - xor a -.close - push af - call CloseIRCommunications - pop af - ret - -; sends request for other device to close current communication -RequestCloseIRCommunication: ; 19865 (6:5865) - call StartIRCommunications - ld a, IRCMD_CLOSE - ld [wIRDataBuffer + 1], a - call TransmitIRDataBuffer -; fallthrough - -; calls CloseIRCommunications while preserving af -SafelyCloseIRCommunications: ; 19870 (6:5870) - push af - call CloseIRCommunications - pop af - ret - -; sends a request for data to be transmitted -; from the other device -; hl = start of data to request to transmit -; de = address to write data received -; c = length of data -RequestDataTransmissionThroughIR: ; 19876 (6:5876) - ld a, IRCMD_TRANSMIT_DATA - call TransmitRegistersThroughIR - push de - push bc - call Func_1971e - pop bc - pop hl - jr c, SafelyCloseIRCommunications - call ReceiveNBytesToHLThroughIR - jr SafelyCloseIRCommunications - -; transmits data to be written in the other device -; hl = start of data to transmit -; de = address for other device to write data -; c = length of data -RequestDataReceivalThroughIR: ; 19889 (6:5889) - ld a, IRCMD_RECEIVE_DATA - call TransmitRegistersThroughIR - call TransmitNBytesFromHLThroughIR - jr c, SafelyCloseIRCommunications - call ReceiveByteThroughIR - jr c, SafelyCloseIRCommunications - add b - jr nz, .asm_1989e - xor a - jr SafelyCloseIRCommunications -.asm_1989e - call ReturnZFlagUnsetAndCarryFlagSet - jr SafelyCloseIRCommunications - -; first stores all the current registers in wIRDataBuffer -; then transmits it through IR -TransmitRegistersThroughIR: ; 198a3 (6:58a3) - push hl - push de - push bc - call StoreRegistersInIRDataBuffer - call StartIRCommunications - call TransmitIRDataBuffer - pop bc - pop de - pop hl - ret nc - inc sp - inc sp - jr SafelyCloseIRCommunications - -; stores af, hl, de and bc in wIRDataBuffer -StoreRegistersInIRDataBuffer: ; 198b7 (6:58b7) - push de - push hl - push af - ld hl, wIRDataBuffer - pop de - ld [hl], e ; <- f - inc hl - ld [hl], d ; <- a - inc hl - pop de - ld [hl], e ; <- l - inc hl - ld [hl], d ; <- h - inc hl - pop de - ld [hl], e ; <- e - inc hl - ld [hl], d ; <- d - inc hl - ld [hl], c ; <- c - inc hl - ld [hl], b ; <- b - ret - -; loads all the registers that were stored -; from StoreRegistersInIRDataBuffer -LoadRegistersFromIRDataBuffer: ; 198d0 (6:58d0) - ld hl, wIRDataBuffer - ld e, [hl] - inc hl - ld d, [hl] - inc hl - push de - ld e, [hl] - inc hl - ld d, [hl] - inc hl - push de - ld e, [hl] - inc hl - ld d, [hl] - inc hl - ld c, [hl] - inc hl - ld b, [hl] - pop hl - pop af - ret - -; empties screen and replaces -; wVBlankFunctionTrampoline with HandleAllSpriteAnimations -Func_198e7: ; 198e7 (6:58e7) - call EmptyScreen - call Set_OBJ_8x8 - call Func_3ca4 - lb de, $38, $7f - call SetupText - ld hl, wVBlankFunctionTrampoline + 1 - ld de, wVBlankFunctionTrampolineBackup - call BackupVBlankFunctionTrampoline - di - ld [hl], LOW(HandleAllSpriteAnimations) - inc hl - ld [hl], HIGH(HandleAllSpriteAnimations) - ei - ret - -; sets backup VBlank function as wVBlankFunctionTrampoline -RestoreVBlankFunction: ; 19907 (6:5907) - ld hl, wVBlankFunctionTrampolineBackup - ld de, wVBlankFunctionTrampoline + 1 - call BackupVBlankFunctionTrampoline - call Func_3ca4 - bank1call ZeroObjectPositionsAndToggleOAMCopy - ret - -; copies 2 bytes from hl to de while interrupts are disabled -; used to load or store wVBlankFunctionTrampoline -; to wVBlankFunctionTrampolineBackup -BackupVBlankFunctionTrampoline: ; 19917 (6:5917) - di - ld a, [hli] - ld [de], a - inc de - ld a, [hld] - ld [de], a - ei - ret - -Func_1991f: ; 1991f (6:591f) - add a - ld e, a - ld d, 0 - ld hl, .data - add hl, de - ld a, PLAYER_TURN - ldh [hWhoseTurn], a - ld a, [hli] - add $02 - push hl - ld hl, sDeck1Name - call CopyDeckNameAndCards - pop hl - call SwapTurn - ld a, [hli] - add $02 - call LoadDeck - call SwapTurn - call EnableSRAM - ld h, $a1 - ld de, wPlayerDeck - ld c, $3c -.asm_594c - ld a, [de] - inc de - ld l, a - res 7, [hl] - dec c - jr nz, .asm_594c - - ld h, $a1 - ld de, wOpponentDeck - ld c, $1e -.asm_595b - ld a, [de] - inc de - ld l, a - res 7, [hl] - inc [hl] - dec c - jr nz, .asm_595b - - call DisableSRAM - ret -.data - db $03, $04, $05, $06, $07, $08 - -; clears saved data (card Collection/saved decks/Card Pop! data/etc) -; then adds the starter decks as saved decks -; marks all cards in Collection as not owned -InitSaveData: ; 1996e (6:596e) -; clear card and deck save data - call EnableSRAM - ld a, PLAYER_TURN - ldh [hWhoseTurn], a - ld hl, sCardAndDeckSaveData - ld bc, sCardAndDeckSaveDataEnd - sCardAndDeckSaveData -.loop_clear - xor a - ld [hli], a - dec bc - ld a, c - or b - jr nz, .loop_clear - -; add the starter decks - ld a, CHARMANDER_AND_FRIENDS_DECK - ld hl, sSavedDeck1 - call CopyDeckNameAndCards - ld a, SQUIRTLE_AND_FRIENDS_DECK - ld hl, sSavedDeck2 - call CopyDeckNameAndCards - ld a, BULBASAUR_AND_FRIENDS_DECK - ld hl, sSavedDeck3 - call CopyDeckNameAndCards - -; marks all cards in Collection to not owned - call EnableSRAM - ld hl, sCardCollection - ld a, CARD_NOT_OWNED -.loop_collection - ld [hl], a - inc l - jr nz, .loop_collection - - ld hl, sCurrentDuel - xor a - ld [hli], a - ld [hli], a ; sCurrentDuelChecksum - ld [hl], a - -; clears Card Pop! names - ld hl, sCardPopNameList - ld c, CARDPOP_NAME_LIST_MAX_ELEMS -.loop_card_pop_names - ld [hl], $0 - ld de, NAME_BUFFER_LENGTH - add hl, de - dec c - jr nz, .loop_card_pop_names - -; saved configuration options - ld a, 2 - ld [sPrinterContrastLevel], a - ld a, $2 - ld [sTextSpeed], a - ld [wTextSpeed], a - -; miscellaneous data - xor a - ld [sAnimationsDisabled], a - ld [sSkipDelayAllowed], a - ld [s0a004], a - ld [sTotalCardPopsDone], a - ld [sReceivedLegendaryCards], a - farcall InitPromotionalCardAndDeckCounterSaveData - call DisableSRAM - ret - -; input: -; a = Deck ID -; hl = destination to copy -CopyDeckNameAndCards: ; 199e0 (6:59e0) - push de - push bc - push hl - call LoadDeck - jr c, .done - call .CopyDeckName - pop hl - call EnableSRAM - push hl - ld de, wDefaultText -.loop_write_name - ld a, [de] - inc de - ld [hli], a - or a - jr nz, .loop_write_name - pop hl - - push hl - ld de, DECK_NAME_SIZE - add hl, de - ld de, wPlayerDeck - ld c, DECK_SIZE -.loop_write_cards - ld a, [de] - inc de - ld [hli], a - dec c - jr nz, .loop_write_cards - call DisableSRAM - or a -.done - pop hl - pop bc - pop de - ret - -.CopyDeckName - ld hl, wDeckName - ld a, [hli] - ld h, [hl] - ld l, a - ld de, wDefaultText - call CopyText - ret - -; hl = text ID -LoadLinkConnectingScene: ; 19a1f (6:5a1f) - push hl - call Func_198e7 - ld a, SCENE_GAMEBOY_LINK_CONNECTING - lb bc, 0, 0 - call LoadScene - pop hl - call DrawWideTextBox_PrintText - call EnableLCD - ret - -; shows Link Not Connected scene -; then asks the player whether they want to try again -; if the player selects "no", return carry -; input: -; - hl = text ID -LoadLinkNotConnectedSceneAndAskWhetherToTryAgain: ; 19a33 (6:5a33) - push hl - call RestoreVBlankFunction - call Func_198e7 - ld a, SCENE_GAMEBOY_LINK_NOT_CONNECTED - lb bc, 0, 0 - call LoadScene - pop hl - call DrawWideTextBox_WaitForInput - ldtx hl, WouldYouLikeToTryAgainText - call YesOrNoMenuWithText_SetCursorToYes -; fallthrough - -ClearRPAndRestoreVBlankFunction: ; 19a4c (6:5a4c) - push af - call ClearRP - call RestoreVBlankFunction - pop af - ret - -; prepares IR communication parameter data -; a = a IRPARAM_* constant for the function of this connection -InitIRCommunications: ; 19a55 (6:5a55) - ld hl, wOwnIRCommunicationParams - ld [hl], a - inc hl - ld [hl], $50 - inc hl - ld [hl], $4b - inc hl - ld [hl], $31 - ld a, $ff - ld [wIRCommunicationErrorCode], a - ld a, PLAYER_TURN - ldh [hWhoseTurn], a -; clear wNameBuffer and wOpponentName - xor a - ld [wNameBuffer], a - ld hl, wOpponentName - ld [hli], a - ld [hl], a -; loads player's name from SRAM -; to wDefaultText - call EnableSRAM - ld hl, sPlayerName - ld de, wDefaultText - ld c, NAME_BUFFER_LENGTH -.loop - ld a, [hli] - ld [de], a - inc de - dec c - jr nz, .loop - call DisableSRAM - ret - -; returns carry if communication was unsuccessful -; if a = 0, then it was a communication error -; if a = 1, then operation was cancelled by the player -PrepareSendCardOrDeckConfigurationThroughIR: ; 19a89 (6:5a89) - call InitIRCommunications - -; pressing A button triggers request for IR communication -.loop_frame - call DoFrame - ldh a, [hKeysPressed] - bit B_BUTTON_F, a - jr nz, .b_btn - ldh a, [hKeysHeld] - bit A_BUTTON_F, a - jr z, .loop_frame -; a btn - call TrySendIRRequest - jr nc, .request_success - or a - jr z, .loop_frame - xor a - scf - ret - -.b_btn - ; cancelled by the player - ld a, $01 - scf - ret - -.request_success - call ExchangeIRCommunicationParameters - ret c - ld a, [wOtherIRCommunicationParams + 3] - cp $31 - jr nz, SetIRCommunicationErrorCode_Error - or a - ret - -; exchanges player names and IR communication parameters -; checks whether parameters for communication match -; and if they don't, an error is issued -ExchangeIRCommunicationParameters: ; 19ab7 (6:5ab7) - ld hl, wOwnIRCommunicationParams - ld de, wOtherIRCommunicationParams - ld c, 4 - call RequestDataTransmissionThroughIR - jr c, .error - ld hl, wOtherIRCommunicationParams + 1 - ld a, [hli] - cp $50 - jr nz, .error - ld a, [hli] - cp $4b - jr nz, .error - ld a, [wOwnIRCommunicationParams] - ld hl, wOtherIRCommunicationParams - cp [hl] ; do parameters match? - jr nz, SetIRCommunicationErrorCode_Error - -; receives wDefaultText from other device -; and writes it to wNameBuffer - ld hl, wDefaultText - ld de, wNameBuffer - ld c, NAME_BUFFER_LENGTH - call RequestDataTransmissionThroughIR - jr c, .error -; transmits wDefaultText to be -; written in wNameBuffer in the other device - ld hl, wDefaultText - ld de, wNameBuffer - ld c, NAME_BUFFER_LENGTH - call RequestDataReceivalThroughIR - jr c, .error - or a - ret - -.error - xor a - scf - ret - -SetIRCommunicationErrorCode_Error: ; 19af9 (6:5af9) - ld hl, wIRCommunicationErrorCode - ld [hl], $01 - ld de, wIRCommunicationErrorCode - ld c, 1 - call RequestDataReceivalThroughIR - call RequestCloseIRCommunication - ld a, $01 - scf - ret - -SetIRCommunicationErrorCode_NoError: ; 19b0d (6:5b0d) - ld hl, wOwnIRCommunicationParams - ld [hl], $00 - ld de, wIRCommunicationErrorCode - ld c, 1 - call RequestDataReceivalThroughIR - ret c - call RequestCloseIRCommunication - or a - ret - -; makes device receptive to receive data from other device -; to write in wDuelTempList (either list of cards or a deck configuration) -; returns carry if some error occurred -TryReceiveCardOrDeckConfigurationThroughIR: ; 19b20 (6:5b20) - call InitIRCommunications -.loop_receive_request - xor a - ld [wDuelTempList], a - call TryReceiveIRRequest - jr nc, .receive_data - bit 1, a - jr nz, .cancelled - jr .loop_receive_request -.receive_data - call ExecuteReceivedIRCommands - ld a, [wIRCommunicationErrorCode] - or a - ret z ; no error - xor a - scf - ret - -.cancelled - ld a, $01 - scf - ret - -; returns carry if card(s) wasn't successfully sent -_SendCard: ; 19b41 (6:5b41) - call StopMusic - ldtx hl, SendingACardText - call LoadLinkConnectingScene - ld a, IRPARAM_SEND_CARDS - call PrepareSendCardOrDeckConfigurationThroughIR - jr c, .fail - - ; send cards - xor a - ld [wDuelTempList + DECK_SIZE], a - ld hl, wDuelTempList - ld e, l - ld d, h - ld c, DECK_SIZE + 1 - call RequestDataReceivalThroughIR - jr c, .fail - call SetIRCommunicationErrorCode_NoError - jr c, .fail - call ExecuteReceivedIRCommands - jr c, .fail - ld a, [wOwnIRCommunicationParams + 1] - cp $4f - jr nz, .fail - call PlayCardPopSong - xor a - call ClearRPAndRestoreVBlankFunction - ret - -.fail - call PlayCardPopSong - ldtx hl, CardTransferWasntSuccessful1Text - call LoadLinkNotConnectedSceneAndAskWhetherToTryAgain - jr nc, _SendCard ; loop back and try again - ; failed - scf - ret - -PlayCardPopSong: ; 19b87 (6:5b87) - ld a, MUSIC_CARD_POP - jp PlaySong - -_ReceiveCard: ; 19b8c (6:5b8c) - call StopMusic - ldtx hl, ReceivingACardText - call LoadLinkConnectingScene - ld a, IRPARAM_SEND_CARDS - call TryReceiveCardOrDeckConfigurationThroughIR - ld a, $4f - ld [wOwnIRCommunicationParams + 1], a - ld hl, wOwnIRCommunicationParams - ld e, l - ld d, h - ld c, 4 - call RequestDataReceivalThroughIR - jr c, .fail - call RequestCloseIRCommunication - jr c, .fail - call PlayCardPopSong - or a - call ClearRPAndRestoreVBlankFunction - ret - -.fail - call PlayCardPopSong - ldtx hl, CardTransferWasntSuccessful2Text - call LoadLinkNotConnectedSceneAndAskWhetherToTryAgain - jr nc, _ReceiveCard - scf - ret - -_SendDeckConfiguration: ; 19bc5 (6:5bc5) - call StopMusic - ldtx hl, SendingADeckConfigurationText - call LoadLinkConnectingScene - ld a, IRPARAM_SEND_DECK - call PrepareSendCardOrDeckConfigurationThroughIR - jr c, .fail - ld hl, wDuelTempList - ld e, l - ld d, h - ld c, DECK_STRUCT_SIZE - call RequestDataReceivalThroughIR - jr c, .fail - call SetIRCommunicationErrorCode_NoError - jr c, .fail - call PlayCardPopSong - call ClearRPAndRestoreVBlankFunction - or a - ret - -.fail - call PlayCardPopSong - ldtx hl, DeckConfigurationTransferWasntSuccessful1Text - call LoadLinkNotConnectedSceneAndAskWhetherToTryAgain - jr nc, _SendDeckConfiguration - scf - ret - -_ReceiveDeckConfiguration: ; 19bfb (6:5bfb) - call StopMusic - ldtx hl, ReceivingDeckConfigurationText - call LoadLinkConnectingScene - ld a, IRPARAM_SEND_DECK - call TryReceiveCardOrDeckConfigurationThroughIR - jr c, .fail - call PlayCardPopSong - call ClearRPAndRestoreVBlankFunction - or a - ret - -.fail - call PlayCardPopSong - ldtx hl, DeckConfigurationTransferWasntSuccessful2Text - call LoadLinkNotConnectedSceneAndAskWhetherToTryAgain - jr nc, _ReceiveDeckConfiguration ; loop back and try again - scf - ret - -_DoCardPop: ; 19c20 (6:5c20) -; loads scene for Card Pop! screen -; then checks if console is SGB -; and issues an error message in case it is - call Func_198e7 - ld a,SCENE_CARD_POP - lb bc, 0, 0 - call LoadScene - ldtx hl, AreYouBothReadyToCardPopText - call PrintScrollableText_NoTextBoxLabel - call RestoreVBlankFunction - ldtx hl, CardPopCannotBePlayedWithTheGameBoyText - ld a, [wConsole] - cp CONSOLE_SGB - jr z, .error - -; initiate the communications - call PauseSong - call Func_198e7 - ld a, SCENE_GAMEBOY_LINK_CONNECTING - lb bc, 0, 0 - call LoadScene - ldtx hl, PositionGameBoyColorsAndPressAButtonText - call DrawWideTextBox_PrintText - call EnableLCD - call HandleCardPopCommunications - push af - push hl - call ClearRP - call RestoreVBlankFunction - pop hl - pop af - jr c, .error - -; show the received card detail page -; and play the corresponding song - ld a, [wLoadedCard1ID] - call AddCardToCollectionAndUpdateAlbumProgress - ld hl, wLoadedCard1Name - ld a, [hli] - ld h, [hl] - ld l, a - call LoadTxRam2 - ld a, PLAYER_TURN - ldh [hWhoseTurn], a - ld a, SFX_5D - call PlaySFX -.wait_sfx - call AssertSFXFinished - or a - jr nz, .wait_sfx - ld a, [wCardPopCardObtainSong] - call PlaySong - ldtx hl, ReceivedThroughCardPopText - bank1call _DisplayCardDetailScreen - call ResumeSong - lb de, $38, $9f - call SetupText - bank1call OpenCardPage_FromHand - ret - -.error -; show Card Pop! error scene -; and print text in hl - push hl - call ResumeSong - call Func_198e7 - ld a, SCENE_CARD_POP_ERROR - lb bc, 0, 0 - call LoadScene - pop hl - call PrintScrollableText_NoTextBoxLabel - call RestoreVBlankFunction - ret - -; handles all communications to the other device to do Card Pop! -; returns carry if Card Pop! is unsuccessful -; and returns in hl the corresponding error text ID -HandleCardPopCommunications: ; 19cb2 (6:5cb2) -; copy CardPopNameList from SRAM to WRAM - call EnableSRAM - ld hl, sCardPopNameList - ld de, wCardPopNameList - ld bc, CARDPOP_NAME_LIST_SIZE - call CopyDataHLtoDE - call DisableSRAM - - ld a, IRPARAM_CARD_POP - call InitIRCommunications -.asm_19cc9 - call TryReceiveIRRequest ; receive request - jr nc, .asm_19d05 - bit 1, a - jr nz, .fail - call TrySendIRRequest ; send request - jr c, .asm_19cc9 - -; do the player name search, then transmit the result - call ExchangeIRCommunicationParameters - jr c, .fail - ld hl, wCardPopNameList - ld de, wOtherPlayerCardPopNameList - ld c, 0 ; $100 bytes = CARDPOP_NAME_LIST_SIZE - call RequestDataTransmissionThroughIR - jr c, .fail - call LookUpNameInCardPopNameList - ld hl, wCardPopNameSearchResult - ld de, wCardPopNameSearchResult - ld c, 1 - call RequestDataReceivalThroughIR - jr c, .fail - call SetIRCommunicationErrorCode_NoError - jr c, .fail - call ExecuteReceivedIRCommands - jr c, .fail - jr .check_search_result - -.asm_19d05 - call ExecuteReceivedIRCommands - ld a, [wIRCommunicationErrorCode] - or a - jr nz, .fail - call RequestCloseIRCommunication - jr c, .fail - -.check_search_result - ld a, [wCardPopNameSearchResult] - or a - jr z, .success - ; not $00, means the name was found in the list - ldtx hl, CannotCardPopWithFriendPreviouslyPoppedWithText - scf - ret - -.success - call DecideCardToReceiveFromCardPop - -; increment number of times Card Pop! was done -; and write the other player's name to sCardPopNameList -; the spot where this is written in the list is derived -; from the lower nybble of sTotalCardPopsDone -; that means that after 16 Card Pop!, the older -; names start to get overwritten - call EnableSRAM - ld hl, sTotalCardPopsDone - ld a, [hl] - inc [hl] - and $0f - swap a ; *NAME_BUFFER_LENGTH - ld l, a - ld h, $0 - ld de, sCardPopNameList - add hl, de - ld de, wNameBuffer - ld c, NAME_BUFFER_LENGTH -.loop_write_name - ld a, [de] - inc de - ld [hli], a - dec c - jr nz, .loop_write_name - call DisableSRAM - or a - ret - -.fail - ldtx hl, ThePopWasntSuccessfulText - scf - ret - -; looks up the name in wNameBuffer in wCardPopNameList -; used to know whether this save file has done Card Pop! -; with the other player already -; returns carry and wCardPopNameSearchResult = $ff if the name was found; -; returns no carry and wCardPopNameSearchResult = $00 otherwise -LookUpNameInCardPopNameList: ; 19d49 (6:5d49) -; searches for other player's name in this game's name list - ld hl, wCardPopNameList - ld c, CARDPOP_NAME_LIST_MAX_ELEMS -.loop_own_card_pop_name_list - push hl - ld de, wNameBuffer - call .CompareNames - pop hl - jr nc, .found_name - ld de, NAME_BUFFER_LENGTH - add hl, de - dec c - jr nz, .loop_own_card_pop_name_list - -; name was not found in wCardPopNameList - -; searches for this player's name in the other game's name list -; this is useless since it discards the result from the name comparisons -; as a result this loop will always return no carry - call EnableSRAM - ld hl, wOtherPlayerCardPopNameList - ld c, CARDPOP_NAME_LIST_MAX_ELEMS -.loop_other_card_pop_name_list - push hl - ld de, sPlayerName - call .CompareNames ; discards result from comparison - pop hl - ld de, NAME_BUFFER_LENGTH - add hl, de - dec c - jr nz, .loop_other_card_pop_name_list - xor a - jr .no_carry - -.found_name - ld a, $ff - scf -.no_carry - call DisableSRAM - ld [wCardPopNameSearchResult], a ; $00 if name was not found, $ff otherwise - ret - -; compares names in hl and de -; if they are different, return carry -.CompareNames - ld b, NAME_BUFFER_LENGTH -.loop_chars - ld a, [de] - inc de - cp [hl] - jr nz, .not_same - inc hl - dec b - jr nz, .loop_chars - or a - ret -.not_same - scf - ret - -; loads in wLoadedCard1 a random card to be received -; this selection is done based on the rarity that is -; decided from the names of both participants -; the card will always be a Pokemon card that is not -; from a Promotional set, with the exception -; of Venusaur1 and Mew2 -; output: -; - e = card ID chosen -DecideCardToReceiveFromCardPop: ; 19d92 (6:5d92) - ld a, PLAYER_TURN - ldh [hWhoseTurn], a - call EnableSRAM - ld hl, sPlayerName - call CalculateNameHash - call DisableSRAM - push de - ld hl, wNameBuffer - call CalculateNameHash - pop bc - -; de = other player's name hash -; bc = this player's name hash - -; updates RNG values to subtraction of these two hashes - ld hl, wRNG1 - ld a, b - sub d - ld d, a ; b - d - ld [hli], a ; wRNG1 - ld a, c - sub e - ld e, a ; c - e - ld [hli], a ; wRNG2 - ld [hl], $0 ; wRNGCounter - -; depending on the values obtained from the hashes, -; determine which rarity card to give to the player -; along with the song to play with each rarity -; the probabilities of each possibility can be calculated -; as follows (given 2 random player names): -; 101/256 ~ 39% for Circle -; 90/256 ~ 35% for Diamond -; 63/256 ~ 25% for Star -; 1/256 ~ .4% for Venusaur1 or Mew2 - ld a, e - cp 5 - jr z, .venusaur1_or_mew2 - cp 64 - jr c, .star_rarity ; < 64 - cp 154 - jr c, .diamond_rarity ; < 154 - ; >= 154 - - ld a, MUSIC_BOOSTER_PACK - ld b, CIRCLE - jr .got_rarity -.diamond_rarity - ld a, MUSIC_BOOSTER_PACK - ld b, DIAMOND - jr .got_rarity -.star_rarity - ld a, MUSIC_MATCH_VICTORY - ld b, STAR -.got_rarity - ld [wCardPopCardObtainSong], a - ld a, b - call CreateCardPopCandidateList - ; shuffle candidates and pick first from list - call ShuffleCards - ld a, [hl] - ld e, a -.got_card_id - ld d, $0 - call LoadCardDataToBuffer1_FromCardID - ld a, e - ret - -.venusaur1_or_mew2 -; choose either Venusaur1 or Mew2 -; depending on whether the lower -; bit of d is unset or set, respectively - ld a, MUSIC_MEDAL - ld [wCardPopCardObtainSong], a - ld e, VENUSAUR1 - ld a, d - and $1 ; get lower bit - jr z, .got_card_id - ld e, MEW2 - jr .got_card_id - -; lists in wCardPopCardCandidates all cards that: -; - are Pokemon cards; -; - have the same rarity as input register a; -; - are not from Promotional set. -; input: -; - a = card rarity -; output: -; - a = number of candidates -CreateCardPopCandidateList: ; 19df7 (6:5df7) - ld hl, wPlayerDeck - push hl - push de - push bc - ld b, a - - lb de, 0, GRASS_ENERGY -.loop_card_ids - call LoadCardDataToBuffer1_FromCardID - jr c, .count ; no more card IDs - ld a, [wLoadedCard1Type] - and TYPE_ENERGY - jr nz, .next_card_id ; not Pokemon card - ld a, [wLoadedCard1Rarity] - cp b - jr nz, .next_card_id ; not equal rarity - ld a, [wLoadedCard1Set] - and $f0 - cp PROMOTIONAL - jr z, .next_card_id ; no promos - ld [hl], e - inc hl -.next_card_id - inc de - jr .loop_card_ids - -; count all the cards that were listed -; and return it in a -.count - ld [hl], $00 ; invalid card ID as end of list - ld hl, wPlayerDeck - ld c, -1 -.loop_count - inc c - ld a, [hli] - or a - jr nz, .loop_count - ld a, c - pop bc - pop de - pop hl - ret - -; creates a unique two-byte hash from the name given in hl -; the low byte is calculated by simply adding up all characters -; the high byte is calculated by xoring all characters together -; input: -; - hl = points to the start of the name buffer -; output: -; - de = hash -CalculateNameHash: ; 19e32 (6:5e32) - ld c, NAME_BUFFER_LENGTH - ld de, $0 -.loop - ld a, e - add [hl] - ld e, a - ld a, d - xor [hl] - ld d, a - inc hl - dec c - jr nz, .loop - ret - -; sends serial data to printer -; if there's an error in connection, -; show Printer Not Connected scene with error message -_PreparePrinterConnection: ; 19e42 (6:5e42) - ld bc, $0 - lb de, PRINTERPKT_DATA, $0 - call SendPrinterPacket - ret nc ; return if no error - - ld hl, wPrinterStatus - ld a, [hl] - or a - jr nz, .asm_19e55 - ld [hl], $ff -.asm_19e55 - ld a, [hl] - cp $ff - jr z, ShowPrinterIsNotConnected -; fallthrough - -; shows message on screen depending on wPrinterStatus -; also shows SCENE_GAMEBOY_PRINTER_NOT_CONNECTED. -HandlePrinterError: ; 19e5a (6:5e5a) - ld a, [wPrinterStatus] - cp $ff - jr z, .cable_or_printer_switch - or a - jr z, .interrupted - bit PRINTER_ERROR_BATTERIES_LOST_CHARGE, a - jr nz, .batteries_lost_charge - bit PRINTER_ERROR_CABLE_PRINTER_SWITCH, a - jr nz, .cable_or_printer_switch - bit PRINTER_ERROR_PAPER_JAMMED, a - jr nz, .jammed_printer - - ldtx hl, PrinterPacketErrorText - ld a, $04 - jr ShowPrinterConnectionErrorScene -.cable_or_printer_switch - ldtx hl, CheckCableOrPrinterSwitchText - ld a, $02 - jr ShowPrinterConnectionErrorScene -.jammed_printer - ldtx hl, PrinterPaperIsJammedText - ld a, $03 - jr ShowPrinterConnectionErrorScene -.batteries_lost_charge - ldtx hl, BatteriesHaveLostTheirChargeText - ld a, $01 - jr ShowPrinterConnectionErrorScene -.interrupted - ldtx hl, PrintingWasInterruptedText - call DrawWideTextBox_WaitForInput - scf - ret - -ShowPrinterIsNotConnected: ; 19e94 (6:5e94) - ldtx hl, PrinterIsNotConnectedText - ld a, $02 -; fallthrough - -; a = error code -; hl = text ID to print in text box -ShowPrinterConnectionErrorScene: ; 19e99 (6:5e99) - push hl - ; unnecessary loading TxRam, since the text data - ; already incorporate the error number - ld l, a - ld h, $00 - call LoadTxRam3 - - call Func_198e7 - ld a, SCENE_GAMEBOY_PRINTER_NOT_CONNECTED - lb bc, 0, 0 - call LoadScene - pop hl - call DrawWideTextBox_WaitForInput - call RestoreVBlankFunction - scf - ret - -; main card printer function -Func_19eb4: ; 19eb4 (6:5eb4) - ld e, a - ld d, $0 - call LoadCardDataToBuffer1_FromCardID - call Func_198e7 - ld a, SCENE_GAMEBOY_PRINTER_TRANSMITTING - lb bc, 0, 0 - call LoadScene - ld a, 20 - call CopyCardNameAndLevel - ld [hl], TX_END - ld hl, $0 - call LoadTxRam2 - ldtx hl, NowPrintingText - call DrawWideTextBox_PrintText - call EnableLCD - call PrepareForPrinterCommunications - call DrawTopCardInfoInSRAMGfxBuffer0 - call Func_19f87 - call DrawCardPicInSRAMGfxBuffer2 - call Func_19f99 - jr c, .error - call DrawBottomCardInfoInSRAMGfxBuffer0 - call Func_1a011 - jr c, .error - call RestoreVBlankFunction - call ResetPrinterCommunicationSettings - or a - ret -.error - call RestoreVBlankFunction - call ResetPrinterCommunicationSettings - jp HandlePrinterError - -DrawCardPicInSRAMGfxBuffer2: ; 19f05 (6:5f05) - ld hl, wLoadedCard1Gfx - ld a, [hli] - ld h, [hl] - ld l, a - ld de, sGfxBuffer2 - call Func_37a5 - ; draw card's picture in sGfxBuffer2 - ld a, $40 - lb hl, 12, 1 - lb de, 2, 68 - lb bc, 16, 12 - call FillRectangle - ret - -; writes the tiles necessary to draw -; the card's information in sGfxBuffer0 -; this includes card's type, lv, HP and attacks if Pokemon card -; or otherwise just the card's name and type symbol -DrawTopCardInfoInSRAMGfxBuffer0: ; 19f20 (6:5f20) - call Func_1a025 - call Func_212f - - ; draw empty text box frame - ld hl, sGfxBuffer0 - ld a, $34 - lb de, $30, $31 - ld b, 20 - call CopyLine - ld c, 15 -.loop_lines - xor a ; SYM_SPACE - lb de, $36, $37 - ld b, 20 - call CopyLine - dec c - jr nz, .loop_lines - - ; draw card type symbol - ld a, $38 - lb hl, 1, 2 - lb de, 1, 65 - lb bc, 2, 2 - call FillRectangle - ; print card's name - lb de, 4, 65 - ld hl, wLoadedCard1Name - call InitTextPrinting_ProcessTextFromPointerToID - -; prints card's type, lv, HP and attacks if it's a Pokemon card - ld a, [wLoadedCard1Type] - cp TYPE_ENERGY - jr nc, .skip_pokemon_data - inc a ; symbol corresponding to card's type (color) - lb bc, 18, 65 - call WriteByteToBGMap0 - ld a, SYM_Lv - lb bc, 11, 66 - call WriteByteToBGMap0 - ld a, [wLoadedCard1Level] - lb bc, 12, 66 - bank1call WriteTwoDigitNumberInTxSymbolFormat - ld a, SYM_HP - lb bc, 15, 66 - call WriteByteToBGMap0 - ld a, [wLoadedCard1EffectCommands] - inc b - bank1call WriteTwoByteNumberInTxSymbolFormat -.skip_pokemon_data - ret - -Func_19f87: ; 19f87 (6:5f87) - call TryInitPrinterCommunications - ret c - ld hl, sGfxBuffer0 - call Func_1a0cc - ret c - call Func_1a0cc - call Func_1a111 - ret - -Func_19f99: ; 19f99 (6:5f99) - call TryInitPrinterCommunications - ret c - ld hl, sGfxBuffer0 + $8 tiles - ld c, $06 -.asm_19fa2 - call Func_1a0cc - ret c - dec c - jr nz, .asm_19fa2 - call Func_1a111 - ret - -; writes the tiles necessary to draw -; the card's information in sGfxBuffer0 -; this includes card's Retreat cost, Weakness, Resistance, -; and attack if it's Pokemon card -; or otherwise just the card's description. -DrawBottomCardInfoInSRAMGfxBuffer0: ; 19fad (6:5fad) - call Func_1a025 - xor a - ld [wCardPageType], a - ld hl, sGfxBuffer0 - ld b, 20 - ld c, 9 -.loop_lines - xor a ; SYM_SPACE - lb de, $36, $37 - call CopyLine - dec c - jr nz, .loop_lines - ld a, $35 - lb de, $32, $33 - call CopyLine - - ld a, [wLoadedCard1Type] - cp TYPE_ENERGY - jr nc, .not_pkmn_card - ld hl, RetreatWeakResistData - call PlaceTextItems - ld c, 66 - bank1call DisplayCardPage_PokemonOverview.attacks - ld a, SYM_No - lb bc, 15, 72 - call WriteByteToBGMap0 - inc b - ld a, [wLoadedCard1PokedexNumber] - bank1call WriteTwoByteNumberInTxSymbolFormat - ret - -.not_pkmn_card - bank1call SetNoLineSeparation - lb de, 1, 66 - ld a, SYM_No - call InitTextPrintingInTextbox - ld hl, wLoadedCard1NonPokemonDescription - call ProcessTextFromPointerToID - bank1call SetOneLineSeparation - ret - -RetreatWeakResistData: ; 1a004 (6:6004) - textitem 1, 70, RetreatText - textitem 1, 71, WeaknessText - textitem 1, 72, ResistanceText - db $ff - -Func_1a011: ; 1a011 (6:6011) - call TryInitPrinterCommunications - ret c - ld hl, sGfxBuffer0 - ld c, $05 -.asm_1a01a - call Func_1a0cc - ret c - dec c - jr nz, .asm_1a01a - call Func_1a108 - ret - -; calls setup text and sets wTilePatternSelector -Func_1a025: ; 1a025 (6:6025) - lb de, $40, $bf - call SetupText - ld a, $a4 - ld [wTilePatternSelector], a - xor a - ld [wTilePatternSelectorCorrection], a - ret - -; switches to CGB normal speed, resets serial -; enables SRAM and switches to SRAM1 -; and clears sGfxBuffer0 -PrepareForPrinterCommunications: ; 1a035 (6:6035) - call SwitchToCGBNormalSpeed - call ResetSerial - ld a, $10 - ld [wce9b], a - call EnableSRAM - ld a, [sPrinterContrastLevel] - ld [wPrinterContrastLevel], a - call DisableSRAM - ldh a, [hBankSRAM] - ld [wce8f], a - ld a, BANK("SRAM1") - call BankswitchSRAM - call EnableSRAM -; fallthrough - -ClearPrinterGfxBuffer: ; 1a035 (6:6035) - ld hl, sGfxBuffer0 - ld bc, $400 -.loop - xor a - ld [hli], a - dec bc - ld a, c - or b - jr nz, .loop - xor a - ld [wce9f], a - ret - -; reverts settings changed by PrepareForPrinterCommunications -ResetPrinterCommunicationSettings: ; 1a06b (6:606b) - push af - call SwitchToCGBDoubleSpeed - ld a, [wce8f] - call BankswitchSRAM - call DisableSRAM - lb de, $30, $bf - call SetupText - pop af - ret - -; unreferenced -; send some bytes through serial -Func_1a080: ; 1a080 (6:6080) - ld bc, $0 - lb de, PRINTERPKT_NUL, $0 - jp SendPrinterPacket - -; tries initiating the communications for -; sending data to printer -; returns carry if operation was cancelled -; by pressing B button or serial transfer took long -TryInitPrinterCommunications: ; 1a089 (6:6089) - xor a - ld [wPrinterInitAttempts], a -.wait_input - call DoFrame - ldh a, [hKeysHeld] - and B_BUTTON - jr nz, .b_button - ld bc, $0 - lb de, PRINTERPKT_NUL, $0 - call SendPrinterPacket - jr c, .delay - and (1 << PRINTER_STATUS_BUSY) | (1 << PRINTER_STATUS_PRINTING) - jr nz, .wait_input - -.init - ld bc, $0 - lb de, PRINTERPKT_INIT, $0 - call SendPrinterPacket - jr nc, .no_carry - ld hl, wPrinterInitAttempts - inc [hl] - ld a, [hl] - cp 3 - jr c, .wait_input - ; time out - scf - ret -.no_carry - ret - -.b_button - xor a - ld [wPrinterStatus], a - scf - ret - -.delay - ld c, 10 -.delay_loop - call DoFrame - dec c - jr nz, .delay_loop - jr .init - -; loads tiles given by map in hl to sGfxBuffer5 -; copies first 20 tiles, then offsets by 2 tiles -; and copies another 20 -Func_1a0cc: ; 1a0cc (6:60cc) - push bc - ld de, sGfxBuffer5 - call .Copy20Tiles - call .Copy20Tiles - push hl - call CompressDataForPrinterSerialTransfer - call SendPrinterPacket - pop hl - pop bc - ret - -; copies 20 tiles given by hl to de -; then adds 2 tiles to hl -.Copy20Tiles ; 1a0e0 (6:60e0) - push hl - ld c, 20 -.loop_tiles - ld a, [hli] - call .CopyTile - dec c - jr nz, .loop_tiles - pop hl - ld bc, 2 tiles - add hl, bc - ret - -; copies a tile to de -; a = tile to get from sGfxBuffer1 -.CopyTile ; 1a0f0 (6:60f0) - push hl - push bc - ld l, a - ld h, $00 - add hl, hl - add hl, hl - add hl, hl - add hl, hl ; *TILE_SIZE - ld bc, sGfxBuffer1 - add hl, bc - ld c, TILE_SIZE -.loop_copy - ld a, [hli] - ld [de], a - inc de - dec c - jr nz, .loop_copy - pop bc - pop hl - ret - -Func_1a108: ; 1a108 (6:6108) - call GetPrinterContrastSerialData - push hl - lb hl, $3, $1 - jr SendPrinterInstructionPacket - -Func_1a111: ; 1a111 (6:6111) - call GetPrinterContrastSerialData - push hl - ld hl, wce9b - ld a, [hl] - ld [hl], $00 - ld h, a - ld l, $01 -; fallthrough - -SendPrinterInstructionPacket: ; 1a11e (6:611e) - push hl - ld bc, $0 - lb de, PRINTERPKT_DATA, $0 - call SendPrinterPacket - jr c, .asm_1a135 - ld hl, sp+$00 ; contrast level bytes - ld bc, $4 ; instruction packets are 4 bytes in size - lb de, PRINTERPKT_PRINT_INSTRUCTION, $0 - call SendPrinterPacket -.asm_1a135 - pop hl - pop hl - ret - -; returns in h and l the bytes -; to be sent through serial to the printer -; for the set contrast level -GetPrinterContrastSerialData: ; 1a138 (6:6138) - ld a, [wPrinterContrastLevel] - ld e, a - ld d, $00 - ld hl, .contrast_level_data - add hl, de - ld h, [hl] - ld l, $e4 - ret - -.contrast_level_data - db $00, $20, $40, $60, $7f - -; unreferenced -Func_1a14b: ; 1a14b (6:614b) - ld a, $01 - jr .asm_1a15d - ld a, $02 - jr .asm_1a15d - ld a, $03 - jr .asm_1a15d - ld a, $04 - jr .asm_1a15d - ld a, $05 -.asm_1a15d - ld [wce9d], a - scf - ret - -; a = saved deck index to print -_PrintDeckConfiguration: ; 1a162 (6:6162) -; copies selected deck from SRAM to wDuelTempList - call EnableSRAM - ld l, a - ld h, DECK_STRUCT_SIZE - call HtimesL - ld de, sSavedDeck1 - add hl, de - ld de, wDuelTempList - ld bc, DECK_STRUCT_SIZE - call CopyDataHLtoDE - call DisableSRAM - - call ShowPrinterTransmitting - call PrepareForPrinterCommunications - call Func_1a025 - call Func_212f - lb de, 0, 64 - lb bc, 20, 4 - call DrawRegularTextBoxDMG - lb de, 4, 66 - call InitTextPrinting - ld hl, wDuelTempList ; print deck name - call ProcessText - ldtx hl, DeckPrinterText - call ProcessTextFromID - - ld a, 5 - ld [wPrinterHorizontalOffset], a - ld hl, wPrinterTotalCardCount - xor a - ld [hli], a - ld [hl], a - ld [wPrintOnlyStarRarity], a - - ld hl, wCurDeckCards -.loop_cards - ld a, [hl] - or a - jr z, .asm_1a1d6 - ld e, a - ld d, $00 - call LoadCardDataToBuffer1_FromCardID - - ; find out this card's count - ld a, [hli] - ld b, a - ld c, 1 -.loop_card_count - cp [hl] - jr nz, .got_card_count - inc hl - inc c - jr .loop_card_count - -.got_card_count - ld a, c - ld [wPrinterCardCount], a - call LoadCardInfoForPrinter - call AddToPrinterGfxBuffer - jr c, .printer_error - jr .loop_cards - -.asm_1a1d6 - call SendCardListToPrinter - jr c, .printer_error - call ResetPrinterCommunicationSettings - call RestoreVBlankFunction - or a - ret - -.printer_error - call ResetPrinterCommunicationSettings - call RestoreVBlankFunction - jp HandlePrinterError - -SendCardListToPrinter: ; 1a1ec (6:61ec) - ld a, [wPrinterHorizontalOffset] - cp 1 - jr z, .skip_load_gfx - call LoadGfxBufferForPrinter - ret c -.skip_load_gfx - call TryInitPrinterCommunications - ret c - call Func_1a108 - ret -; 0z1a1ff - -; increases printer horizontal offset by 2 -AddToPrinterGfxBuffer: ; 1a1ff (6:61ff) - push hl - ld hl, wPrinterHorizontalOffset - inc [hl] - inc [hl] - ld a, [hl] - pop hl - ; return no carry if below 18 - cp 18 - ccf - ret nc - ; >= 18 -; fallthrough - -; copies Gfx to Gfx buffer and sends some serial data -; returns carry set if unsuccessful -LoadGfxBufferForPrinter: ; 1a20b (6:620b) - push hl - call TryInitPrinterCommunications - jr c, .set_carry - ld a, [wPrinterHorizontalOffset] - srl a - ld c, a - ld hl, sGfxBuffer0 -.loop_gfx_buffer - call Func_1a0cc - jr c, .set_carry - dec c - jr nz, .loop_gfx_buffer - call Func_1a111 - jr c, .set_carry - - call ClearPrinterGfxBuffer - ld a, 1 - ld [wPrinterHorizontalOffset], a - pop hl - or a - ret - -.set_carry - pop hl - scf - ret - -; load symbol, name, level and card count to buffer -LoadCardInfoForPrinter: ; 1a235 (6:6235) - push hl - ld a, [wPrinterHorizontalOffset] - or %1000000 - ld e, a - ld d, 3 - ld a, [wPrintOnlyStarRarity] - or a - jr nz, .skip_card_symbol - ld hl, wPrinterTotalCardCount - ld a, [hli] - or [hl] - call z, DrawCardSymbol -.skip_card_symbol - ld a, 14 - call CopyCardNameAndLevel - call InitTextPrinting - ld hl, wDefaultText - call ProcessText - ld a, [wPrinterHorizontalOffset] - or %1000000 - ld c, a - ld b, 16 - ld a, SYM_CROSS - call WriteByteToBGMap0 - inc b - ld a, [wPrinterCardCount] - bank1call WriteTwoDigitNumberInTxSymbolFormat - pop hl - ret - -_PrintCardList: ; 1a270 (6:6270) -; if Select button is held when printing card list -; only print cards with Star rarity (excluding Promotional cards) -; even if it's not marked as seen in the collection - ld e, FALSE - ldh a, [hKeysHeld] - and SELECT - jr z, .no_select - inc e ; TRUE -.no_select - ld a, e - ld [wPrintOnlyStarRarity], a - - call ShowPrinterTransmitting - call CreateTempCardCollection - ld de, wDefaultText - call CopyPlayerName - call PrepareForPrinterCommunications - call Func_1a025 - call Func_212f - - lb de, 0, 64 - lb bc, 20, 4 - call DrawRegularTextBoxDMG - ld a, PLAYER_TURN - ldh [hWhoseTurn], a - lb de, 2, 66 - call InitTextPrinting - ld hl, wDefaultText - call ProcessText - ldtx hl, AllCardsOwnedText - call ProcessTextFromID - ld a, [wPrintOnlyStarRarity] - or a - jr z, .asm_1a2c2 - ld a, TX_HALF2FULL - call ProcessSpecialTextCharacter - lb de, 3, 84 - call Func_22ca -.asm_1a2c2 - ld a, $ff - ld [wCurPrinterCardType], a - xor a - ld hl, wPrinterTotalCardCount - ld [hli], a - ld [hl], a - ld [wPrinterNumCardTypes], a - ld a, 5 - ld [wPrinterHorizontalOffset], a - - ld e, GRASS_ENERGY -.loop_cards - push de - ld d, $00 - call LoadCardDataToBuffer1_FromCardID - jr c, .done_card_loop - ld d, HIGH(wTempCardCollection) - ld a, [de] ; card ID count in collection - ld [wPrinterCardCount], a - call .LoadCardTypeEntry - jr c, .printer_error_pop_de - - ld a, [wPrintOnlyStarRarity] - or a - jr z, .all_owned_cards_mode - ld a, [wLoadedCard1Set] - and %11110000 - cp PROMOTIONAL - jr z, .next_card - ld a, [wLoadedCard1Rarity] - cp STAR - jr nz, .next_card - ; not Promotional, and Star rarity - ld hl, wPrinterCardCount - res CARD_NOT_OWNED_F, [hl] - jr .got_card_count - -.all_owned_cards_mode - ld a, [wPrinterCardCount] - or a - jr z, .next_card - cp CARD_NOT_OWNED - jr z, .next_card ; ignore not owned cards - -.got_card_count - ld a, [wPrinterCardCount] - and CARD_COUNT_MASK - ld c, a - - ; add to total card count - ld hl, wPrinterTotalCardCount - add [hl] - ld [hli], a - ld a, 0 - adc [hl] - ld [hl], a - - ; add to current card type count - ld hl, wPrinterCurCardTypeCount - ld a, c - add [hl] - ld [hli], a - ld a, 0 - adc [hl] - ld [hl], a - - ld hl, wPrinterNumCardTypes - inc [hl] - ld hl, wce98 - inc [hl] - call LoadCardInfoForPrinter - call AddToPrinterGfxBuffer - jr c, .printer_error_pop_de -.next_card - pop de - inc e - jr .loop_cards - -.printer_error_pop_de - pop de -.printer_error - call ResetPrinterCommunicationSettings - call RestoreVBlankFunction - jp HandlePrinterError - -.done_card_loop - pop de - ; add separator line - ld a, [wPrinterHorizontalOffset] - dec a - or $40 - ld c, a - ld b, 0 - call BCCoordToBGMap0Address - ld a, $35 - lb de, $35, $35 - ld b, 20 - call CopyLine - call AddToPrinterGfxBuffer - jr c, .printer_error - - ld hl, wPrinterTotalCardCount - ld c, [hl] - inc hl - ld b, [hl] - ldtx hl, TotalNumberOfCardsText - call .PrintTextWithNumber - jr c, .printer_error - ld a, [wPrintOnlyStarRarity] - or a - jr nz, .done - ld a, [wPrinterNumCardTypes] - ld c, a - ld b, 0 - ldtx hl, TypesOfCardsText - call .PrintTextWithNumber - jr c, .printer_error - -.done - call SendCardListToPrinter - jr c, .printer_error - call ResetPrinterCommunicationSettings - call RestoreVBlankFunction - or a - ret - -; prints text ID given in hl -; with decimal representation of -; the number given in bc -; hl = text ID -; bc = number -.PrintTextWithNumber - push bc - ld a, [wPrinterHorizontalOffset] - dec a - or $40 - ld e, a - ld d, 2 - call InitTextPrinting - call ProcessTextFromID - ld d, 14 - call InitTextPrinting - pop hl - call TwoByteNumberToTxSymbol_TrimLeadingZeros - ld hl, wStringBuffer - call ProcessText - call AddToPrinterGfxBuffer - ret - -; loads this card's type icon and text -; if it's a new card type that hasn't been printed yet -.LoadCardTypeEntry - ld a, [wLoadedCard1Type] - ld c, a - cp TYPE_ENERGY - jr c, .got_type ; jump if Pokemon card - ld c, $08 - cp TYPE_TRAINER - jr nc, .got_type ; jump if Trainer card - ld c, $07 -.got_type - ld hl, wCurPrinterCardType - ld a, [hl] - cp c - ret z ; already handled this card type - - ; show corresponding icon and text - ; for this new card type - ld a, c - ld [hl], a ; set it as current card type - add a - add c ; *3 - ld c, a - ld b, $00 - ld hl, .IconTextList - add hl, bc - ld a, [wPrinterHorizontalOffset] - dec a - or %1000000 - ld e, a - ld d, 1 - ld a, [hli] - push hl - lb bc, 2, 2 - lb hl, 1, 2 - call FillRectangle - pop hl - ld d, 3 - inc e - call InitTextPrinting - ld a, [hli] - ld h, [hl] - ld l, a - call ProcessTextFromID - - call AddToPrinterGfxBuffer - ld hl, wPrinterCurCardTypeCount - xor a - ld [hli], a - ld [hl], a - ld [wce98], a - ret - -.IconTextList - ; Fire - db $e0 ; icon tile - tx FirePokemonText - - ; Grass - db $e4 ; icon tile - tx GrassPokemonText - - ; Lightning - db $e8 ; icon tile - tx LightningPokemonText - - ; Water - db $ec ; icon tile - tx WaterPokemonText - - ; Fighting - db $f0 ; icon tile - tx FightingPokemonText - - ; Psychic - db $f4 ; icon tile - tx PsychicPokemonText - - ; Colorless - db $f8 ; icon tile - tx ColorlessPokemonText - - ; Energy - db $fc ; icon tile - tx EnergyCardText - - ; Trainer - db $dc ; icon tile - tx TrainerCardText - -ShowPrinterTransmitting: ; 1a420 (6:6420) - call Func_198e7 - ld a, SCENE_GAMEBOY_PRINTER_TRANSMITTING - lb bc, 0, 0 - call LoadScene - ldtx hl, NowPrintingPleaseWaitText - call DrawWideTextBox_PrintText - call EnableLCD - ret - -; compresses $28 tiles in sGfxBuffer5 -; and writes it in sGfxBuffer5 + $28 tiles. -; compressed data has 2 commands to instruct on how to decompress it. -; - a command byte with bit 7 not set, means to copy that many + 1 -; bytes that are following it literally. -; - a command byte with bit 7 set, means to copy the following byte -; that many times + 2 (after masking the top bit of command byte). -; returns in bc the size of the compressed data and -; in de the packet type data. -CompressDataForPrinterSerialTransfer: ; 1a435 (6:6435) - ld hl, sGfxBuffer5 - ld de, sGfxBuffer5 + $28 tiles - ld bc, $28 tiles -.loop_remaining_data - ld a, $ff - inc b - dec b - jr nz, .check_compression - ld a, c -.check_compression - push bc - push de - ld c, a - call CheckDataCompression - ld a, e - ld c, e - pop de - jr c, .copy_byte - ld a, c - ld b, c - dec a - ld [de], a ; number of bytes to copy literally - 1 - inc de -.copy_literal_sequence - ld a, [hli] - ld [de], a - inc de - dec c - jr nz, .copy_literal_sequence - ld c, b - jr .sub_added_bytes - -.copy_byte - ld a, c - dec a - dec a - or %10000000 ; set high bit - ld [de], a ; = (n times to copy - 2) | %10000000 - inc de - ld a, [hl] ; byte to copy n times - ld [de], a - inc de - ld b, $0 - add hl, bc - -.sub_added_bytes - ld a, c - cpl - inc a - pop bc - add c - ld c, a - ld a, $ff - adc b - ld b, a - or c - jr nz, .loop_remaining_data - - ld hl, $10000 - (sGfxBuffer5 + $28 tiles) - add hl, de ; gets the size of the compressed data - ld c, l - ld b, h - ld hl, sGfxBuffer5 + $28 tiles - lb de, PRINTERPKT_DATA, $1 - ret - -; checks whether the next byte sequence in hl, up to c bytes, can be compressed -; returns carry if the next sequence of bytes can be compressed, -; i.e. has at least 3 consecutive bytes with the same value. -; in that case, returns in e the number of consecutive -; same value bytes that were found. -; if there are no bytes with same value, then count as many bytes left -; as possible until either there are no more remaining data bytes, -; or until a sequence of 3 bytes with the same value are found. -; in that case, the number of bytes in this sequence is returned in e. -CheckDataCompression: ; 1a485 (6:6485) - push hl - ld e, c - ld a, c -; if number of remaining bytes is less than 4 -; then no point in compressing - cp 4 - jr c, .no_carry - -; check first if there are at least -; 3 consecutive bytes with the same value - ld b, c - ld a, [hli] - cp [hl] - inc hl - jr nz, .literal_copy ; not same - cp [hl] - inc hl - jr nz, .literal_copy ; not same - -; 3 consecutive bytes were found with same value -; keep track of how many consecutive bytes -; with the same value there are in e - dec c - dec c - dec c - ld e, 3 -.loop_same_value - cp [hl] - jr nz, .set_carry ; exit when a different byte is found - inc hl - inc e - dec c - jr z, .set_carry ; exit when there is no more remaining data - bit 5, e - ; exit if number of consecutive bytes >= $20 - jr z, .loop_same_value -.set_carry - pop hl - scf - ret - -.literal_copy -; consecutive bytes are not the same value -; count the number of bytes there are left -; until a sequence of 3 bytes with the same value is found - pop hl - push hl - ld c, b ; number of remaining bytes - ld e, 1 - ld a, [hli] - dec c - jr z, .no_carry ; exit if no more data -.reset_same_value_count - ld d, 2 ; number of consecutive same value bytes to exit -.next_byte - inc e - dec c - jr z, .no_carry - bit 7, e - jr nz, .no_carry ; exit if >= $80 - cp [hl] - jr z, .same_consecutive_value - ld a, [hli] - jr .reset_same_value_count -.no_carry - pop hl - or a - ret - -.same_consecutive_value - inc hl - dec d - jr nz, .next_byte - ; 3 consecutive bytes with same value found - ; discard the last 3 bytes in the sequence - dec e - dec e - dec e - jr .no_carry - -; sets up to start a link duel -; decides which device will pick the number of prizes -; then exchanges names and duels between the players -; and starts the main duel routine -_SetUpAndStartLinkDuel: ; 1a4cf (6:64cf) - ld hl, sp+$00 - ld a, l - ld [wDuelReturnAddress + 0], a - ld a, h - ld [wDuelReturnAddress + 1], a - call Func_198e7 - - ld a, SCENE_GAMEBOY_LINK_TRANSMITTING - lb bc, 0, 0 - call LoadScene - - bank1call LoadPlayerDeck - call SwitchToCGBNormalSpeed - bank1call DecideLinkDuelVariables - push af - call RestoreVBlankFunction - pop af - jp c, .error - - ld a, DUELIST_TYPE_PLAYER - ld [wPlayerDuelistType], a - ld a, DUELIST_TYPE_LINK_OPP - ld [wOpponentDuelistType], a - ld a, DUELTYPE_LINK - ld [wDuelType], a - - call EmptyScreen - ld a, [wSerialOp] - cp $29 - jr nz, .asm_1a540 - - ld a, PLAYER_TURN - ldh [hWhoseTurn], a - call .ExchangeNamesAndDecks - jr c, .error - lb de, 6, 2 - lb bc, 8, 6 - call DrawRegularTextBox - lb de, 7, 4 - call InitTextPrinting - ldtx hl, PrizesCardsText - call ProcessTextFromID - ldtx hl, ChooseTheNumberOfPrizesText - call DrawWideTextBox_PrintText - call EnableLCD - call .PickNumberOfPrizeCards - ld a, [wNPCDuelPrizes] - call SerialSend8Bytes - jr .prizes_decided - -.asm_1a540 - ld a, OPPONENT_TURN - ldh [hWhoseTurn], a - call .ExchangeNamesAndDecks - jr c, .error - ldtx hl, PleaseWaitDecidingNumberOfPrizesText - call DrawWideTextBox_PrintText - call EnableLCD - call SerialRecv8Bytes - ld [wNPCDuelPrizes], a - -.prizes_decided - call ExchangeRNG - ld a, LINK_OPP_PIC - ld [wOpponentPortrait], a - ldh a, [hWhoseTurn] - push af - call EmptyScreen - bank1call SetDefaultPalettes - ld a, SHUFFLE_DECK - ld [wDuelDisplayedScreen], a - bank1call DrawDuelistPortraitsAndNames - ld a, OPPONENT_TURN - ldh [hWhoseTurn], a - ld a, [wNPCDuelPrizes] - ld l, a - ld h, $00 - call LoadTxRam3 - ldtx hl, BeginAPrizeDuelWithText - call DrawWideTextBox_WaitForInput - pop af - ldh [hWhoseTurn], a - call ExchangeRNG - bank1call StartDuel_VSLinkOpp - call SwitchToCGBDoubleSpeed - ret - -.error - ld a, -1 - ld [wDuelResult], a - call Func_198e7 - - ld a, SCENE_GAMEBOY_LINK_NOT_CONNECTED - lb bc, 0, 0 - call LoadScene - - ldtx hl, TransmissionErrorText - call DrawWideTextBox_WaitForInput - call RestoreVBlankFunction - call ResetSerial - ret - -.ExchangeNamesAndDecks - ld de, wDefaultText - push de - call CopyPlayerName - pop hl - ld de, wNameBuffer - ld c, NAME_BUFFER_LENGTH - call SerialExchangeBytes - ret c - xor a - ld hl, wOpponentName - ld [hli], a - ld [hl], a - ld hl, wPlayerDeck - ld de, wOpponentDeck - ld c, DECK_SIZE - call SerialExchangeBytes - ret - -; handles player choice of number of prize cards -; pressing left/right makes it decrease/increase respectively -; selection is confirmed by pressing A button -.PickNumberOfPrizeCards - ld a, PRIZES_4 - ld [wNPCDuelPrizes], a - xor a - ld [wPrizeCardSelectionFrameCounter], a -.loop_input - call DoFrame - ld a, [wNPCDuelPrizes] - add SYM_0 - ld e, a - ; check frame counter so that it - ; either blinks or shows number - ld hl, wPrizeCardSelectionFrameCounter - ld a, [hl] - inc [hl] - and $10 - jr z, .no_blink - ld e, SYM_SPACE -.no_blink - ld a, e - lb bc, 9, 6 - call WriteByteToBGMap0 - - ldh a, [hDPadHeld] - ld b, a - ld a, [wNPCDuelPrizes] - bit D_LEFT_F, b - jr z, .check_d_right - dec a - cp PRIZES_2 - jr nc, .got_prize_count - ld a, PRIZES_6 ; wrap around to 6 - jr .got_prize_count - -.check_d_right - bit D_RIGHT_F, b - jr z, .check_a_btn - inc a - cp PRIZES_6 + 1 - jr c, .got_prize_count - ld a, PRIZES_2 -.got_prize_count - ld [wNPCDuelPrizes], a - xor a - ld [wPrizeCardSelectionFrameCounter], a - -.check_a_btn - bit A_BUTTON_F, b - jr z, .loop_input - ret - -Func_1a61f: ; 1a61f (6:661f) - push af - lb de, $38, $9f - call SetupText - pop af - or a - jr nz, .else - ld a, MOLTRES2 - call .legendary_card_text - ld a, ARTICUNO2 - call .legendary_card_text - ld a, ZAPDOS3 - call .legendary_card_text - ld a, DRAGONITE1 -.legendary_card_text - ldtx hl, ReceivedLegendaryCardText - jr .print_text -.else - ldtx hl, ReceivedCardText - cp VILEPLUME - jr z, .print_text - cp BLASTOISE - jr z, .print_text - ldtx hl, ReceivedPromotionalFlyingPikachuText - cp FLYING_PIKACHU - jr z, .print_text - ldtx hl, ReceivedPromotionalSurfingPikachuText - cp SURFING_PIKACHU1 - jr z, .print_text - cp SURFING_PIKACHU2 - jr z, .print_text - ldtx hl, ReceivedPromotionalCardText -.print_text - push hl - ld e, a - ld d, $0 - call LoadCardDataToBuffer1_FromCardID - call PauseSong - ld a, MUSIC_MEDAL - call PlaySong - ld hl, wLoadedCard1Name - ld a, [hli] - ld h, [hl] - ld l, a - bank1call LoadTxRam2 ; switch to bank 1, but call a home func - ld a, PLAYER_TURN - ldh [hWhoseTurn], a - pop hl - bank1call _DisplayCardDetailScreen -.loop - call AssertSongFinished - or a - jr nz, .loop - - call ResumeSong - bank1call OpenCardPage_FromHand - ret - -_OpenBoosterPack: ; 1a68d (6:668d) - ld a, PLAYER_TURN - ldh [hWhoseTurn], a - ld h, a - ld l, $00 -.asm_6694 - xor a - ld [hli], a - ld a, l - cp $3c - jr c, .asm_6694 - xor a - ld hl, wBoosterCardsDrawn - ld de, wDuelTempList - ld c, $00 -.asm_66a4 - ld a, [hli] - or a - jr z, .asm_66ae - ld a, c - ld [de], a - inc de - inc c - jr .asm_66a4 -.asm_66ae - ld a, $ff - ld [de], a - lb de, $38, $9f - call SetupText - bank1call InitAndDrawCardListScreenLayout - ldtx hl, ChooseTheCardYouWishToExamineText - ldtx de, BoosterPackText - bank1call SetCardListHeaderText - ld a, A_BUTTON | START - ld [wNoItemSelectionMenuKeys], a - bank1call DisplayCardList - ret - -CommentedOut_1a6cc: ; 1a6cc (6:66cc) - ret - -Func_1a6cd: ; 1a6cd (6:66cd) - ldh a, [hBankSRAM] - or a - ret nz - push hl - push de - push bc - ld hl, sCardCollection - ld bc, $0250 - ld a, [s0a000 + $b] - ld e, a -.asm_66de - ld a, [hli] - xor e - ld e, a - dec bc - ld a, c - or b - jr nz, .asm_66de - ld a, e - pop bc - pop de - pop hl - or a - ret z - xor a - ld [wTileMapFill], a - ld hl, wDoFrameFunction - ld [hli], a - ld [hl], a - ldh [hSCX], a - ldh [hSCY], a - bank1call ZeroObjectPositionsAndToggleOAMCopy - call EmptyScreen - call LoadSymbolsFont - bank1call SetDefaultPalettes - ld a, [wConsole] - cp $01 - jr nz, .asm_6719 - ld a, $e4 - ld [wOBP0], a - ld [wBGP], a - ld a, $01 - ld [wFlushPaletteFlags], a -.asm_6719 - lb de, $38, $9f - call SetupText - ld hl, $00a3 - bank1call DrawWholeScreenTextBox - ld a, $0a - ld [MBC3SRamEnable], a - xor a - ldh [hBankSRAM], a - ld [MBC3SRamBank], a - ld [MBC3RTC], a - ld [MBC3SRamEnable], a - jp Reset - ret - -Func_1a73a: ; 1a73a (6:673a) - ldh a, [hBankSRAM] - or a - ret nz - push hl - push de - push bc - ld hl, sCardCollection - ld bc, $0250 - ld e, $00 -.asm_6749 - ld a, [hli] - xor e - ld e, a - dec bc - ld a, c - or b - jr nz, .asm_6749 - ld a, $0a - ld [MBC3SRamEnable], a - ld a, e - ld [s0a00b], a - pop bc - pop de - pop hl - ret - -WhatIsYourNameData: ; 1a75e (6:675e) - textitem 1, 1, WhatIsYourNameText - db $ff -; [Deck1Data ~ Deck4Data] -; These are directed from around (2:4f05), -; without any bank description. -; That is, the developers hard-coded it. -_-;; -Deck1Data: ; 1a763 (6:6763) - textitem 2, 1, Deck1Text - textitem 14, 1, DeckText - db $ff -Deck2Data: ; 1a76c (6:676c) - textitem 2, 1, Deck2Text - textitem 14, 1, DeckText - db $ff -Deck3Data: ; 1a775 (6:6775) - textitem 2, 1, Deck3Text - textitem 14, 1, DeckText - db $ff -Deck4Data: ; 1a77e (6:677e) - textitem 2, 1, Deck4Text - textitem 14, 1, DeckText - db $ff - -; set each byte zero from hl for b bytes. -ClearMemory: ; 1a787 (6:6787) - push af - push bc - push hl - ld b, a - xor a -.loop - ld [hli], a - dec b - jr nz, .loop - pop hl - pop bc - pop af - ret - -; play different sfx by a. -; if a is 0xff play SFX_03 (usually following a B press), -; else play SFX_02 (usually following an A press). -PlayAcceptOrDeclineSFX: ; 1a794 (6:6794) - push af - inc a - jr z, .sfx_decline - ld a, SFX_02 - jr .sfx_accept -.sfx_decline - ld a, SFX_03 -.sfx_accept - call PlaySFX - pop af - ret - -; get player name from the user -; into hl -InputPlayerName: ; 1a7a3 (6:67a3) - ld e, l - ld d, h - ld a, MAX_PLAYER_NAME_LENGTH - ld hl, WhatIsYourNameData - lb bc, 12, 1 - call InitializeInputName - call Set_OBJ_8x8 - xor a - ld [wTileMapFill], a - call EmptyScreen - call ZeroObjectPositions - ld a, $01 - ld [wVBlankOAMCopyToggle], a - call LoadSymbolsFont - lb de, $38, $bf - call SetupText - call LoadTextCursorTile - ld a, $02 - ld [wd009], a - call DrawNamingScreenBG - xor a - ld [wNamingScreenCursorX], a - ld [wNamingScreenCursorY], a - ld a, $09 - ld [wNamingScreenNumColumns], a - ld a, $06 - ld [wNamingScreenKeyboardHeight], a - ld a, $0f - ld [wVisibleCursorTile], a - ld a, $00 - ld [wInvisibleCursorTile], a -.loop - ld a, $01 - ld [wVBlankOAMCopyToggle], a - call DoFrame - call UpdateRNGSources - ldh a, [hDPadHeld] - and START - jr z, .else - ; if pressed start button. - ld a, $01 - call PlayAcceptOrDeclineSFX - call Func_1aa07 - ld a, 6 - ld [wNamingScreenCursorX], a - ld a, 5 - ld [wNamingScreenCursorY], a - call Func_1aa23 - jr .loop -.else - call NamingScreen_CheckButtonState - jr nc, .loop ; if not pressed, go back to the loop. - cp $ff - jr z, .on_b_button - ; on A button. - call NamingScreen_ProcessInput - jr nc, .loop - ; if the player selected the end button, - ; end its naming. - call FinalizeInputName - ret -.on_b_button - ld a, [wNamingScreenBufferLength] - or a - jr z, .loop ; empty string? - ; erase one character. - ld e, a - ld d, 0 - ld hl, wNamingScreenBuffer - add hl, de - dec hl - dec hl - ld [hl], TX_END - ld hl, wNamingScreenBufferLength ; note that its unit is byte, not word. - dec [hl] - dec [hl] - call PrintPlayerNameFromInput - jr .loop - -; it's called when naming(either player's or deck's) starts. -; a: maximum length of name(depending on whether player's or deck's). -; bc: position of name. -; de: dest. pointer. -; hl: pointer to text item of the question. -InitializeInputName: ; 1a846 (6:6846) - ld [wNamingScreenBufferMaxLength], a - push hl - ld hl, wNamingScreenNamePosition - ld [hl], b - inc hl - ld [hl], c - pop hl - ld b, h - ld c, l - ; set the question string. - ld hl, wNamingScreenQuestionPointer - ld [hl], c - inc hl - ld [hl], b - ; set the destination buffer. - ld hl, wNamingScreenDestPointer - ld [hl], e - inc hl - ld [hl], d - ; clear the name buffer. - ld a, NAMING_SCREEN_BUFFER_LENGTH - ld hl, wNamingScreenBuffer - call ClearMemory - ld hl, wNamingScreenBuffer - ld a, [wNamingScreenBufferMaxLength] - ld b, a - inc b -.loop - ; copy data from de to hl - ; for b bytes. - ld a, [de] - inc de - ld [hli], a - dec b - jr nz, .loop - ld hl, wNamingScreenBuffer - call GetTextLengthInTiles - ld a, c - ld [wNamingScreenBufferLength], a - ret - -FinalizeInputName: ; 1a880 (6:6880) - ld hl, wNamingScreenDestPointer - ld e, [hl] - inc hl - ld d, [hl] - ld l, e - ld h, d - ld de, wNamingScreenBuffer - ld a, [wNamingScreenBufferMaxLength] - ld b, a - inc b - jr InitializeInputName.loop - -; draws the keyboard frame -; and the question if it exists. -DrawNamingScreenBG: ; 1a892 (6:6892) - call DrawTextboxForKeyboard - call PrintPlayerNameFromInput - ld hl, wNamingScreenQuestionPointer - ld c, [hl] - inc hl - ld a, [hl] - ld h, a - or c - jr z, .put_text_end - ; print the question string. - ; ex) "What is your name?" - ld l, c - call PlaceTextItems -.put_text_end - ; print "End". - ld hl, .data - call PlaceTextItems - ldtx hl, PlayerNameKeyboardText - lb de, 2, 4 - call InitTextPrinting - call ProcessTextFromID - call EnableLCD - ret -.data - textitem $0f, $10, EndText ; "End" - db $ff - -DrawTextboxForKeyboard: ; 1a8c1 (6:68c1) - lb de, 0, 3 ; x, y - lb bc, 20, 15 ; w, h - call DrawRegularTextBox - ret - -PrintPlayerNameFromInput: ; 1a8cb (6:68cb) - ld hl, wNamingScreenNamePosition - ld d, [hl] - inc hl - ld e, [hl] - push de - call InitTextPrinting - ld a, [wNamingScreenBufferMaxLength] - ld e, a - ld a, $14 - sub e - inc a - ld e, a - ld d, 0 - ; print the underbars - ; before print the input. - ld hl, .char_underbar - add hl, de - call ProcessText - pop de - call InitTextPrinting - ; print the input from the user. - ld hl, wNamingScreenBuffer - call ProcessText - ret -.char_underbar - db $56 -rept 10 - textfw3 "_" -endr - done - -; check if button pressed. -; if pressed, set the carry bit on. -NamingScreen_CheckButtonState: ; 1a908 (6:6908) - xor a - ld [wPlaysSfx], a - ldh a, [hDPadHeld] - or a - jp z, .no_press - ; detected any button press. - ld b, a - ld a, [wNamingScreenKeyboardHeight] - ld c, a - ld a, [wNamingScreenCursorX] - ld h, a - ld a, [wNamingScreenCursorY] - ld l, a - bit D_UP_F, b - jr z, .asm_692c - ; up - dec a - bit D_DOWN_F, a - jr z, .asm_69a7 - ld a, c - dec a - jr .asm_69a7 -.asm_692c - bit D_DOWN_F, b - jr z, .asm_6937 - ; down - inc a - cp c - jr c, .asm_69a7 - xor a - jr .asm_69a7 -.asm_6937 - ld a, [wNamingScreenNumColumns] - ld c, a - ld a, h - bit D_LEFT_F, b - jr z, .asm_6974 - ; left - ld d, a - ld a, $06 - cp l - ld a, d - jr nz, .asm_696b - push hl - push bc - push af - call GetCharInfoFromPos_Player - inc hl - inc hl - inc hl - inc hl - inc hl - ld a, [hl] - dec a - ld d, a - pop af - pop bc - pop hl - sub d - cp $ff - jr nz, .asm_6962 - ld a, c - sub $02 - jr .asm_69aa -.asm_6962 - cp $fe - jr nz, .asm_696b - ld a, c - sub $03 - jr .asm_69aa -.asm_696b - dec a - bit D_DOWN_F, a - jr z, .asm_69aa - ld a, c - dec a - jr .asm_69aa -.asm_6974 - bit D_RIGHT_F, b - jr z, .no_press - ld d, a - ld a, $06 - cp l - ld a, d - jr nz, .asm_6990 - push hl - push bc - push af - call GetCharInfoFromPos_Player - inc hl - inc hl - inc hl - inc hl - ld a, [hl] - dec a - ld d, a - pop af - pop bc - pop hl - add d -.asm_6990 - inc a - cp c - jr c, .asm_69aa - inc c - cp c - jr c, .asm_69a4 - inc c - cp c - jr c, .asm_69a0 - ld a, $02 - jr .asm_69aa -.asm_69a0 - ld a, $01 - jr .asm_69aa -.asm_69a4 - xor a - jr .asm_69aa -.asm_69a7 - ld l, a - jr .asm_69ab -.asm_69aa - ld h, a -.asm_69ab - push hl - call GetCharInfoFromPos_Player - inc hl - inc hl - inc hl - ld a, [wd009] - cp $02 - jr nz, .asm_69bb - inc hl - inc hl -.asm_69bb - ld d, [hl] - push de - call Func_1aa07 - pop de - pop hl - ld a, l - ld [wNamingScreenCursorY], a - ld a, h - ld [wNamingScreenCursorX], a - xor a - ld [wCheckMenuCursorBlinkCounter], a - ld a, $06 - cp d - jp z, NamingScreen_CheckButtonState - ld a, $01 - ld [wPlaysSfx], a -.no_press - ldh a, [hKeysPressed] - and A_BUTTON | B_BUTTON - jr z, .asm_69ef - and A_BUTTON - jr nz, .asm_69e5 - ld a, $ff -.asm_69e5 - call PlayAcceptOrDeclineSFX - push af - call Func_1aa23 - pop af - scf - ret -.asm_69ef - ld a, [wPlaysSfx] - or a - jr z, .asm_69f8 - call PlaySFX -.asm_69f8 - ld hl, wCheckMenuCursorBlinkCounter - ld a, [hl] - inc [hl] - and $0f - ret nz - ld a, [wVisibleCursorTile] - bit 4, [hl] - jr z, Func_1aa07.asm_6a0a - -Func_1aa07: ; 1aa07 (6:6a07) - ld a, [wInvisibleCursorTile] -.asm_6a0a - ld e, a - ld a, [wNamingScreenCursorX] - ld h, a - ld a, [wNamingScreenCursorY] - ld l, a - call GetCharInfoFromPos_Player - ld a, [hli] - ld c, a - ld b, [hl] - dec b - ld a, e - call Func_1aa28 - call WriteByteToBGMap0 - or a - ret - -Func_1aa23: ; 1aa23 (6:6a23) - ld a, [wVisibleCursorTile] - jr Func_1aa07.asm_6a0a - -Func_1aa28: ; 1aa28 (6:6a28) - push af - push bc - push de - push hl - push af - call ZeroObjectPositions - pop af - ld b, a - ld a, [wInvisibleCursorTile] - cp b - jr z, .asm_6a60 - ld a, [wNamingScreenBufferLength] - srl a - ld d, a - ld a, [wNamingScreenBufferMaxLength] - srl a - ld e, a - ld a, d - cp e - jr nz, .asm_6a49 - dec a -.asm_6a49 - ld hl, wNamingScreenNamePosition - add [hl] - ld d, a - ld h, $08 - ld l, d - call HtimesL - ld a, l - add $08 - ld d, a - ld e, $18 - ld bc, $0000 - call SetOneObjectAttributes -.asm_6a60 - pop hl - pop de - pop bc - pop af - ret - -; load, to the first tile of v0Tiles0, the graphics for the -; blinking black square used in name input screens. -; for inputting full width text. -LoadTextCursorTile: ; 1aa65 (6:6a65) - ld hl, v0Tiles0 + $00 tiles - ld de, .data - ld b, 0 -.loop - ld a, TILE_SIZE - cp b - ret z - inc b - ld a, [de] - inc de - ld [hli], a - jr .loop - -.data -rept TILE_SIZE - db $ff -endr - -; set the carry bit on, -; if "End" was selected. -NamingScreen_ProcessInput: ; 1aa87 (6:6a87) - ld a, [wNamingScreenCursorX] - ld h, a - ld a, [wNamingScreenCursorY] - ld l, a - call GetCharInfoFromPos_Player - inc hl - inc hl - ; load types into de. - ld e, [hl] - inc hl - ld a, [hli] - ld d, a - cp $09 - jp z, .on_end - cp $07 - jr nz, .asm_6ab8 - ld a, [wd009] - or a - jr nz, .asm_6aac - ld a, $01 - jp .asm_6ace -.asm_6aac - dec a - jr nz, .asm_6ab4 - ld a, $02 - jp .asm_6ace -.asm_6ab4 - xor a - jp .asm_6ace -.asm_6ab8 - cp $08 - jr nz, .asm_6ad6 - ld a, [wd009] - or a - jr nz, .asm_6ac6 - ld a, $02 - jr .asm_6ace -.asm_6ac6 - dec a - jr nz, .asm_6acc - xor a - jr .asm_6ace -.asm_6acc - ld a, $01 -.asm_6ace - ld [wd009], a - call DrawNamingScreenBG - or a - ret -.asm_6ad6 - ld a, [wd009] - cp $02 - jr z, .read_char - ldfw3 bc, "“" - ld a, d - cp b - jr nz, .asm_6af4 - ld a, e - cp c - jr nz, .asm_6af4 - push hl - ld hl, TransitionTable1 ; from 55th. - call TransformCharacter - pop hl - jr c, .nothing - jr .asm_6b09 -.asm_6af4 - ldfw3 bc, "º(2)" - ld a, d - cp b - jr nz, .asm_6b1d - ld a, e - cp c - jr nz, .asm_6b1d - push hl - ld hl, TransitionTable2 ; from 72th. - call TransformCharacter - pop hl - jr c, .nothing -.asm_6b09 - ld a, [wNamingScreenBufferLength] - dec a - dec a - ld [wNamingScreenBufferLength], a - ld hl, wNamingScreenBuffer - push de - ld d, 0 - ld e, a - add hl, de - pop de - ld a, [hl] - jr .asm_6b37 -.asm_6b1d - ld a, d - or a - jr nz, .asm_6b37 - ld a, [wd009] - or a - jr nz, .asm_6b2b - ld a, TX_HIRAGANA - jr .asm_6b37 -.asm_6b2b - ld a, TX_KATAKANA - jr .asm_6b37 -; read character code from info. to register. -; hl: pointer. -.read_char - ld e, [hl] - inc hl - ld a, [hl] ; a: first byte of the code. - or a - ; if 2 bytes code, jump. - jr nz, .asm_6b37 - ; if 1 byte code(ascii), - ; set first byte to $0e. - ld a, $0e -; on 2 bytes code. -.asm_6b37 - ld d, a ; de: character code. - ld hl, wNamingScreenBufferLength - ld a, [hl] - ld c, a - push hl - ld hl, wNamingScreenBufferMaxLength - cp [hl] - pop hl - jr nz, .asm_6b4c - ; if the buffer is full - ; just change the last character of it. - ld hl, wNamingScreenBuffer - dec hl - dec hl - jr .asm_6b51 -; increase name length before add the character. -.asm_6b4c - inc [hl] - inc [hl] - ld hl, wNamingScreenBuffer -; write 2 bytes character codes to the name buffer. -; de: 2 bytes character codes. -; hl: dest. -.asm_6b51 - ld b, 0 - add hl, bc - ld [hl], d - inc hl - ld [hl], e - inc hl - ld [hl], TX_END ; null terminator. - call PrintPlayerNameFromInput -.nothing - or a - ret -.on_end - scf - ret - -; this transforms the last japanese character -; in the name buffer into its dakuon shape or something. -; it seems to have been deprecated as the game was translated into english. -; but it can still be applied to english, such as upper-lower case transition. -; hl: info. pointer. -TransformCharacter: ; 1ab61 (6:6b61) - ld a, [wNamingScreenBufferLength] - or a - jr z, .return ; if the length is zero, just return. - dec a - dec a - push hl - ld hl, wNamingScreenBuffer - ld d, 0 - ld e, a - add hl, de - ld e, [hl] - inc hl - ld d, [hl] - ; de: last character in the buffer, - ; but byte-wise swapped. - ld a, TX_KATAKANA - cp e - jr nz, .hiragana - ; if it's katakana, - ; make it hiragana by decreasing its high byte. - dec e -.hiragana - pop hl -.loop - ld a, [hli] - or a - jr z, .return - cp d - jr nz, .next - ld a, [hl] - cp e - jr nz, .next - inc hl - ld e, [hl] - inc hl - ld d, [hl] - or a - ret -.next - inc hl - inc hl - inc hl - jr .loop -.return - scf - ret - -; given the position of the current cursor, -; it returns the pointer to the proper information. -; h: position x. -; l: position y. -GetCharInfoFromPos_Player: ; 1ab93 (6:6b93) - push de - ; (information index) = (x) * (height) + (y) - ; (height) = 0x05(Deck) or 0x06(Player) - ld e, l - ld d, h - ld a, [wNamingScreenKeyboardHeight] - ld l, a - call HtimesL - ld a, l - add e - ld hl, KeyboardData_Player - pop de - or a - ret z -.loop - inc hl - inc hl - inc hl - inc hl - inc hl - inc hl - dec a - jr nz, .loop - ret - -; a set of keyboard datum. -; unit: 6 bytes. -; structure: -; abs. y pos. (1) / abs. x pos. (1) / type 1 (1) / type 2 (1) / char. code (2) -; unused data contains its character code as zero. -kbitem: MACRO - db \1, \2, \3, \4 -if (_NARG == 5) - dw \5 -elif (\5 == TX_FULLWIDTH3) - dw (\5 << 8) | STRCAT("FW3_", \6) -else - dw (\5 << 8) | \6 -endc -ENDM - -KeyboardData_Player: ; 1abaf (6:6baf) - kbitem $04, $02, $11, $00, TX_FULLWIDTH3, "A" - kbitem $06, $02, $12, $00, TX_FULLWIDTH3, "J" - kbitem $08, $02, $13, $00, TX_FULLWIDTH3, "S" - kbitem $0a, $02, $14, $00, "o" - kbitem $0c, $02, $15, $00, "d" - kbitem $10, $0f, $01, $09, $0000 - - kbitem $04, $04, $16, $00, TX_FULLWIDTH3, "B" - kbitem $06, $04, $17, $00, TX_FULLWIDTH3, "K" - kbitem $08, $04, $18, $00, TX_FULLWIDTH3, "T" - kbitem $0a, $04, $19, $00, TX_FULLWIDTH3, "&" - kbitem $0c, $04, $1a, $00, "e" - kbitem $10, $0f, $01, $09, $0000 - - kbitem $04, $06, $1b, $00, TX_FULLWIDTH3, "C" - kbitem $06, $06, $1c, $00, TX_FULLWIDTH3, "L" - kbitem $08, $06, $1d, $00, TX_FULLWIDTH3, "U" - kbitem $0a, $06, $1e, $00, "j" - kbitem $0c, $06, $1f, $00, "f" - kbitem $10, $0f, $01, $09, $0000 - - kbitem $04, $08, $20, $00, TX_FULLWIDTH3, "D" - kbitem $06, $08, $21, $00, TX_FULLWIDTH3, "M" - kbitem $08, $08, $22, $00, TX_FULLWIDTH3, "V" - kbitem $0a, $08, $23, $00, "k" - kbitem $0c, $08, $24, $00, "g" - kbitem $10, $0f, $01, $09, $0000 - - kbitem $04, $0a, $25, $00, TX_FULLWIDTH3, "E" - kbitem $06, $0a, $26, $00, TX_FULLWIDTH3, "N" - kbitem $08, $0a, $27, $00, TX_FULLWIDTH3, "W" - kbitem $0a, $0a, $28, $00, "w" - kbitem $0c, $0a, $29, $00, "h" - kbitem $10, $0f, $01, $09, $0000 - - kbitem $04, $0c, $2a, $00, TX_FULLWIDTH3, "F" - kbitem $06, $0c, $2b, $00, TX_FULLWIDTH3, "O" - kbitem $08, $0c, $2c, $00, TX_FULLWIDTH3, "X" - kbitem $0a, $0c, $2d, $00, "`" - kbitem $0c, $0c, $2e, $00, "i" - kbitem $10, $0f, $01, $09, $0000 - - kbitem $04, $0e, $2f, $00, TX_FULLWIDTH3, "G" - kbitem $06, $0e, $30, $00, TX_FULLWIDTH3, "P" - kbitem $08, $0e, $31, $00, TX_FULLWIDTH3, "Y" - kbitem $0a, $0e, $32, $00, "a" - kbitem $0c, $0e, $33, $00, TX_SYMBOL, SYM_No - kbitem $10, $0f, $01, $09, $0000 - - kbitem $04, $10, $34, $00, TX_FULLWIDTH3, "H" - kbitem $06, $10, $35, $00, TX_FULLWIDTH3, "Q" - kbitem $08, $10, $36, $00, TX_FULLWIDTH3, "Z" - kbitem $0a, $10, $3c, $00, "b" - kbitem $0c, $10, $3d, $00, TX_SYMBOL, SYM_Lv - kbitem $10, $0f, $01, $09, $0000 - - kbitem $04, $12, $37, $00, TX_FULLWIDTH3, "I" - kbitem $06, $12, $38, $00, TX_FULLWIDTH3, "R" - kbitem $08, $12, $39, $00, "n" - kbitem $0a, $12, $3a, $00, "c" - kbitem $0c, $12, $3b, $00, "p" - kbitem $10, $0f, $01, $09, $0000 - kbitem $00, $00, $00, $00, $0000 - -; a set of transition datum. -; unit: 4 bytes. -; structure: -; previous char. code (2) / translated char. code (2) -; - the former char. code contains 0x0e in high byte. -; - the latter char. code contains only low byte. -TransitionTable1: - dw $0e16, $003e - dw $0e17, $003f - dw $0e18, $0040 - dw $0e19, $0041 - dw $0e1a, $0042 - dw $0e1b, $0043 - dw $0e1c, $0044 - dw $0e1d, $0045 - dw $0e1e, $0046 - dw $0e1f, $0047 - dw $0e20, $0048 - dw $0e21, $0049 - dw $0e22, $004a - dw $0e23, $004b - dw $0e24, $004c - dw $0e2a, $004d - dw $0e2b, $004e - dw $0e2c, $004f - dw $0e2d, $0050 - dw $0e2e, $0051 - dw $0e52, $004d - dw $0e53, $004e - dw $0e54, $004f - dw $0e55, $0050 - dw $0e56, $0051 - dw $0000 - -TransitionTable2: - dw $0e2a, $0052 - dw $0e2b, $0053 - dw $0e2c, $0054 - dw $0e2d, $0055 - dw $0e2e, $0056 - dw $0e4d, $0052 - dw $0e4e, $0053 - dw $0e4f, $0054 - dw $0e50, $0055 - dw $0e51, $0056 - dw $0000 - -; get deck name from the user into de. -; function description is similar to the player's. -; refer to 'InputPlayerName'. -InputDeckName: ; 1ad89 (6:6d89) - push af - ; check if the buffer is empty. - ld a, [de] - or a - jr nz, .not_empty - ; this buffer will contain half-width chars. - ld a, TX_HALFWIDTH - ld [de], a -.not_empty - pop af - inc a - call InitializeInputName - call Set_OBJ_8x8 - - xor a - ld [wTileMapFill], a - call EmptyScreen - call ZeroObjectPositions - - ld a, $01 - ld [wVBlankOAMCopyToggle], a - call LoadSymbolsFont - - lb de, $38, $bf - call SetupText - call LoadHalfWidthTextCursorTile - - xor a - ld [wd009], a - call Func_1ae99 - - xor a - ld [wNamingScreenCursorX], a - ld [wNamingScreenCursorY], a - - ld a, $09 - ld [wNamingScreenNumColumns], a - ld a, $07 - ld [wNamingScreenKeyboardHeight], a - ld a, $0f - ld [wVisibleCursorTile], a - ld a, $00 - ld [wInvisibleCursorTile], a -.loop - ld a, $01 - ld [wVBlankOAMCopyToggle], a - call DoFrame - - call UpdateRNGSources - - ldh a, [hDPadHeld] - and START - jr z, .on_start - - ld a, $01 - call PlayAcceptOrDeclineSFX - call Func_1afa1 - - ld a, 6 - ld [wNamingScreenCursorX], a - ld [wNamingScreenCursorY], a - call Func_1afbd - - jr .loop -.on_start - call Func_1aefb - jr nc, .loop - - cp $ff - jr z, .asm_6e1c - - call Func_1aec3 - jr nc, .loop - - call FinalizeInputName - - ld hl, wNamingScreenDestPointer - ld a, [hli] - ld h, [hl] - ld l, a - inc hl - - ld a, [hl] - or a - jr nz, .return - - dec hl - ld [hl], TX_END -.return - ret -.asm_6e1c - ld a, [wNamingScreenBufferLength] - cp $02 - jr c, .loop - - ld e, a - ld d, 0 - ld hl, wNamingScreenBuffer - add hl, de - dec hl - ld [hl], TX_END - - ld hl, wNamingScreenBufferLength - dec [hl] - call ProcessTextWithUnderbar - - jp .loop - -; load, to the first tile of v0Tiles0, the graphics for the -; blinking black square used in name input screens. -; for inputting half width text. -LoadHalfWidthTextCursorTile: ; 1ae37 (6:6e37) - ld hl, v0Tiles0 + $00 tiles - ld de, .data - ld b, 0 -.loop - ld a, TILE_SIZE - cp b - ret z - inc b - ld a, [de] - inc de - ld [hli], a - jr .loop - -.data -rept TILE_SIZE - db $f0 -endr - -; it's only for naming the deck. -ProcessTextWithUnderbar: ; 1ae59 (6:6e59) - ld hl, wNamingScreenNamePosition - ld d, [hl] - inc hl - ld e, [hl] - call InitTextPrinting - ld hl, .underbar_data - ld de, wDefaultText -.loop ; copy the underbar string. - ld a, [hli] - ld [de], a - inc de - or a - jr nz, .loop - - ld hl, wNamingScreenBuffer - ld de, wDefaultText -.loop2 ; copy the input from the user. - ld a, [hli] - or a - jr z, .print_name - ld [de], a - inc de - jr .loop2 -.print_name - ld hl, wDefaultText - call ProcessText - ret -.underbar_data - db TX_HALFWIDTH -rept MAX_DECK_NAME_LENGTH - db "_" -endr - db TX_END - -Func_1ae99: ; 1ae99 (6:6e99) - call DrawTextboxForKeyboard - call ProcessTextWithUnderbar - ld hl, wNamingScreenQuestionPointer - ld c, [hl] - inc hl - ld a, [hl] - ld h, a - or c - jr z, .print - ; print the question string. - ld l, c - call PlaceTextItems -.print - ; print "End" - ld hl, DrawNamingScreenBG.data - call PlaceTextItems - ; print the keyboard characters. - ldtx hl, DeckNameKeyboardText ; "A B C D..." - lb de, 2, 4 - call InitTextPrinting - call ProcessTextFromID - call EnableLCD - ret - -Func_1aec3: ; 1aec3 (6:6ec3) - ld a, [wNamingScreenCursorX] - ld h, a - ld a, [wNamingScreenCursorY] - ld l, a - call GetCharInfoFromPos_Deck - inc hl - inc hl - ld a, [hl] - cp $01 - jr nz, .asm_6ed7 - scf - ret -.asm_6ed7 - ld d, a - ld hl, wNamingScreenBufferLength - ld a, [hl] - ld c, a - push hl - ld hl, wNamingScreenBufferMaxLength - cp [hl] - pop hl - jr nz, .asm_6eeb - ld hl, wNamingScreenBuffer - dec hl - jr .asm_6eef -.asm_6eeb - inc [hl] - ld hl, wNamingScreenBuffer -.asm_6eef - ld b, 0 - add hl, bc - ld [hl], d - inc hl - ld [hl], TX_END - call ProcessTextWithUnderbar - or a - ret - -Func_1aefb: ; 1aefb (6:6efb) - xor a - ld [wPlaysSfx], a - ldh a, [hDPadHeld] - or a - jp z, .asm_6f73 - ld b, a - ld a, [wNamingScreenKeyboardHeight] - ld c, a - ld a, [wNamingScreenCursorX] - ld h, a - ld a, [wNamingScreenCursorY] - ld l, a - bit 6, b - jr z, .asm_6f1f - dec a - bit 7, a - jr z, .asm_6f4b - ld a, c - dec a - jr .asm_6f4b -.asm_6f1f - bit 7, b - jr z, .asm_6f2a - inc a - cp c - jr c, .asm_6f4b - xor a - jr .asm_6f4b -.asm_6f2a - cp $06 - jr z, .asm_6f73 - ld a, [wNamingScreenNumColumns] - ld c, a - ld a, h - bit 5, b - jr z, .asm_6f40 - dec a - bit 7, a - jr z, .asm_6f4e - ld a, c - dec a - jr .asm_6f4e -.asm_6f40 - bit 4, b - jr z, .asm_6f73 - inc a - cp c - jr c, .asm_6f4e - xor a - jr .asm_6f4e -.asm_6f4b - ld l, a - jr .asm_6f4f -.asm_6f4e - ld h, a -.asm_6f4f - push hl - call GetCharInfoFromPos_Deck - inc hl - inc hl - ld d, [hl] - push de - call Func_1afa1 - pop de - pop hl - ld a, l - ld [wNamingScreenCursorY], a - ld a, h - ld [wNamingScreenCursorX], a - xor a - ld [wCheckMenuCursorBlinkCounter], a - ld a, $02 - cp d - jp z, Func_1aefb - ld a, $01 - ld [wPlaysSfx], a -.asm_6f73 - ldh a, [hKeysPressed] - and $03 - jr z, .asm_6f89 - and $01 - jr nz, .asm_6f7f - ld a, $ff -.asm_6f7f - call PlayAcceptOrDeclineSFX - push af - call Func_1afbd - pop af - scf - ret -.asm_6f89 - ld a, [wPlaysSfx] - or a - jr z, .asm_6f92 - call PlaySFX -.asm_6f92 - ld hl, wCheckMenuCursorBlinkCounter - ld a, [hl] - inc [hl] - and $0f - ret nz - ld a, [wVisibleCursorTile] - bit 4, [hl] - jr z, Func_1afa1.asm_6fa4 - -Func_1afa1: ; 1afa1 (6:6fa1) - ld a, [wInvisibleCursorTile] -.asm_6fa4 - ld e, a - ld a, [wNamingScreenCursorX] - ld h, a - ld a, [wNamingScreenCursorY] - ld l, a - call GetCharInfoFromPos_Deck - ld a, [hli] - ld c, a - ld b, [hl] - dec b - ld a, e - call Func_1afc2 - call WriteByteToBGMap0 - or a - ret - -Func_1afbd: ; 1afbd (6:6fbd) - ld a, [wVisibleCursorTile] - jr Func_1afa1.asm_6fa4 - -Func_1afc2: ; 1afc2 (6:6fc2) - push af - push bc - push de - push hl - push af - call ZeroObjectPositions - pop af - ld b, a - ld a, [wInvisibleCursorTile] - cp b - jr z, .asm_6ffb - ld a, [wNamingScreenBufferLength] - ld d, a - ld a, [wNamingScreenBufferMaxLength] - ld e, a - ld a, d - cp e - jr nz, .asm_6fdf - dec a -.asm_6fdf - dec a - ld d, a - ld hl, wNamingScreenNamePosition - ld a, [hl] - sla a - add d - ld d, a - ld h, $04 - ld l, d - call HtimesL - ld a, l - add $08 - ld d, a - ld e, $18 - ld bc, $0000 - call SetOneObjectAttributes -.asm_6ffb - pop hl - pop de - pop bc - pop af - ret - -; given the cursor position, -; returns the character information which the cursor directs. -; it's similar to "GetCharInfoFromPos_Player", -; but the data structure is different in its unit size. -; its unit size is 3, and player's is 6. -; h: x -; l: y -GetCharInfoFromPos_Deck: ; 1b000 (6:7000) - push de - ld e, l - ld d, h - ld a, [wNamingScreenKeyboardHeight] - ld l, a - call HtimesL - ld a, l - add e - ; x * h + y - ld hl, KeyboardData_Deck - pop de - or a - ret z -.loop - inc hl - inc hl - inc hl - dec a - jr nz, .loop - ret - -KeyboardData_Deck: ; 1b019 (6:7019) - db $04, $02, "A" - db $06, $02, "J" - db $08, $02, "S" - db $0a, $02, "?" - db $0c, $02, "4" - db $0e, $02, $02 - db $10, $0f, $01 - - db $04, $04, "B" - db $06, $04, "K" - db $08, $04, "T" - db $0a, $04, "&" - db $0c, $04, "5" - db $0e, $04, $02 - db $10, $0f, $01 - - db $04, $06, "C" - db $06, $06, "L" - db $08, $06, "U" - db $0a, $06, "+" - db $0c, $06, "6" - db $0e, $06, $02 - db $10, $0f, $01 - - db $04, $08, "D" - db $06, $08, "M" - db $08, $08, "V" - db $0a, $08, "-" - db $0c, $08, "7" - db $0e, $08, $02 - db $10, $0f, $01 - - db $04, $0a, "E" - db $06, $0a, "N" - db $08, $0a, "W" - db $0a, $0a, "'" - db $0c, $0a, "8" - db $0e, $0a, $02 - db $10, $0f, $01 - - db $04, $0c, "F" - db $06, $0c, "O" - db $08, $0c, "X" - db $0a, $0c, "0" - db $0c, $0c, "9" - db $0e, $0c, $02 - db $10, $0f, $01 - - db $04, $0e, "G" - db $06, $0e, "P" - db $08, $0e, "Y" - db $0a, $0e, "1" - db $0c, $0e, " " - db $0e, $0e, $02 - db $10, $0f, $01 - - db $04, $10, "H" - db $06, $10, "Q" - db $08, $10, "Z" - db $0a, $10, "2" - db $0c, $10, " " - db $0e, $10, $02 - db $10, $0f, $01 - - db $04, $12, "I" - db $06, $12, "R" - db $08, $12, "!" - db $0a, $12, "3" - db $0c, $12, " " - db $0e, $12, $02 - db $10, $0f, $01 - - ds 4 ; empty - -INCLUDE "data/auto_deck_card_lists.asm" -INCLUDE "data/auto_deck_machines.asm" - -; writes to sAutoDecks all the deck configurations -; from the Auto Deck Machine in wCurAutoDeckMachine -ReadAutoDeckConfiguration: ; 1ba14 (6:7a14) - call EnableSRAM - ld a, [wCurAutoDeckMachine] - ld l, a - ld h, 6 * NUM_DECK_MACHINE_SLOTS - call HtimesL - ld bc, AutoDeckMachineEntries - add hl, bc - ld b, 0 -.loop_decks - call .GetPointerToSRAMAutoDeck - call .ReadDeckConfiguration - call .ReadDeckName - - ; store deck description text ID - push hl - ld de, wAutoDeckMachineTextDescriptions - ld h, b - ld l, 2 - call HtimesL - add hl, de - ld d, h - ld e, l - pop hl - ld a, [hli] - ld [de], a - inc de - ld a, [hli] - ld [de], a - inc b - ld a, b - cp NUM_DECK_MACHINE_SLOTS - jr nz, .loop_decks - call DisableSRAM - ret - -; outputs in de the saved deck with index b -.GetPointerToSRAMAutoDeck - push hl - ld l, b - ld h, DECK_STRUCT_SIZE - call HtimesL - ld de, sAutoDecks - add hl, de - ld d, h - ld e, l - pop hl - ret - -; writes the deck configuration in SRAM -; by reading the given deck card list -.ReadDeckConfiguration - push hl - push bc - push de - push de - ld e, [hl] - inc hl - ld d, [hl] - pop hl - ld bc, DECK_NAME_SIZE - add hl, bc -.loop_create_deck - ld a, [de] - inc de - ld b, a ; card count - or a - jr z, .done_create_deck - ld a, [de] - inc de - ld c, a ; card ID -.loop_card_count - ld [hl], c - inc hl - dec b - jr nz, .loop_card_count - jr .loop_create_deck -.done_create_deck - pop de - pop bc - pop hl - inc hl - inc hl - ret - -.ReadDeckName - push hl - push bc - push de - ld a, [hli] - ld h, [hl] - ld l, a - ld de, wDismantledDeckName - call CopyText - pop hl - ld de, wDismantledDeckName -.loop_copy_name - ld a, [de] - ld [hli], a - or a - jr z, .done_copy_name - inc de - jr .loop_copy_name -.done_copy_name - pop bc - pop hl - inc hl - inc hl - ret - -; tries out all combinations of dismantling the player's decks -; in order to build the deck in wSelectedDeckMachineEntry -; if none of the combinations work, return carry set -; otherwise, return in a which deck flags should be dismantled -CheckWhichDecksToDismantleToBuildSavedDeck: ; 1ba9a (6:7a9a) - xor a - ld [wDecksToBeDismantled], a - -; first check if it can be built by -; only dismantling a single deck - ld a, DECK_1 -.loop_single_built_decks - call .CheckIfCanBuild - ret nc - sla a ; next deck - cp (1 << NUM_DECKS) - jr z, .two_deck_combinations - jr .loop_single_built_decks - -.two_deck_combinations -; next check all two deck combinations - ld a, DECK_1 | DECK_2 - call .CheckIfCanBuild - ret nc - ld a, DECK_1 | DECK_3 - call .CheckIfCanBuild - ret nc - ld a, DECK_1 | DECK_4 - call .CheckIfCanBuild - ret nc - ld a, DECK_2 | DECK_3 - call .CheckIfCanBuild - ret nc - ld a, DECK_2 | DECK_4 - call .CheckIfCanBuild - ret nc - ld a, DECK_3 | DECK_4 - call .CheckIfCanBuild - ret nc - -; all but one deck combinations - ld a, $ff ^ DECK_4 -.loop_three_deck_combinations - call .CheckIfCanBuild - ret nc - sra a - cp $ff - jr z, .all_decks - jr .loop_three_deck_combinations - -.all_decks -; finally check if can be built by dismantling all decks - call .CheckIfCanBuild - ret nc - -; none of the combinations work - scf - ret - -; returns carry if wSelectedDeckMachineEntry cannot be built -; by dismantling the decks given by register a -; a = DECK_* flags -.CheckIfCanBuild - push af - ld hl, wSelectedDeckMachineEntry - ld b, [hl] - farcall CheckIfCanBuildSavedDeck - jr c, .cannot_build - pop af - ld [wDecksToBeDismantled], a - or a - ret -.cannot_build - pop af - scf - ret diff --git a/src/engine/copy_card_name.asm b/src/engine/copy_card_name.asm new file mode 100644 index 0000000..feb33ca --- /dev/null +++ b/src/engine/copy_card_name.asm @@ -0,0 +1,152 @@ +; copy the name and level of the card at wLoadedCard1 to wDefaultText +; a = length in number of tiles (the resulting string will be padded with spaces to match it) +_CopyCardNameAndLevel: + push bc + push de + ld [wCardNameLength], a + ld hl, wLoadedCard1Name + ld a, [hli] + ld h, [hl] + ld l, a + ld de, wDefaultText + push de + call CopyText ; copy card name to wDefaultText + pop hl + ld a, [hli] + cp TX_HALFWIDTH + jp z, _CopyCardNameAndLevel_HalfwidthText + +; the name doesn't start with TX_HALFWIDTH +; this doesn't appear to be ever the case (unless caller manipulates wLoadedCard1Name) + ld a, [wCardNameLength] + ld c, a + ld a, [wLoadedCard1Type] + cp TYPE_ENERGY + jr nc, .level_done ; jump if energy or trainer + ld a, [wLoadedCard1Level] + or a + jr z, .level_done + inc c + inc c + ld a, [wLoadedCard1Level] + cp 10 + jr c, .level_done + inc c ; second digit +.level_done + ld hl, wLoadedCard1Name + ld a, [hli] + ld h, [hl] + ld l, a + ld de, wDefaultText + push de + call CopyText + pop hl + push de + ld e, c + call GetTextLengthInTiles + add e + ld c, a + pop hl + push hl +.fill_loop + ld a, $70 + ld [hli], a + dec c + jr nz, .fill_loop + ld [hl], TX_END + pop hl + ld a, [wLoadedCard1Type] + cp TYPE_ENERGY + jr nc, .done + ld a, [wLoadedCard1Level] + or a + jr z, .done + ld a, TX_SYMBOL + ld [hli], a + ld [hl], SYM_Lv + inc hl + ld a, [wLoadedCard1Level] + cp 10 + jr c, .one_digit + ld [hl], TX_SYMBOL + inc hl + ld b, SYM_0 - 1 +.first_digit_loop + inc b + sub 10 + jr nc, .first_digit_loop + add 10 + ld [hl], b ; first digit + inc hl +.one_digit + ld [hl], TX_SYMBOL + inc hl + add SYM_0 + ld [hl], a ; last (or only) digit + inc hl +.done + pop de + pop bc + ret + +; the name starts with TX_HALFWIDTH +_CopyCardNameAndLevel_HalfwidthText: + ld a, [wCardNameLength] + inc a + add a + ld b, a + ld hl, wDefaultText +.find_end_text_loop + dec b + ld a, [hli] + or a ; TX_END + jr nz, .find_end_text_loop + dec hl + ld a, [wLoadedCard1Type] + cp TYPE_ENERGY + jr nc, .level_done + ld a, [wLoadedCard1Level] + or a + jr z, .level_done + ld c, a + ld a, " " + ld [hli], a + dec b + ld a, "L" + ld [hli], a + dec b + ld a, "v" + ld [hli], a + dec b + ld a, c + cp 10 + jr c, .got_level + push bc + ld b, "0" - 1 +.first_digit_loop + inc b + sub 10 + jr nc, .first_digit_loop + add 10 + ld [hl], b ; first digit + inc hl + pop bc + ld c, a + dec b +.got_level + ld a, c + add "0" + ld [hli], a ; last (or only) digit + dec b +.level_done + push hl + ld a, " " +.fill_spaces_loop + ld [hli], a + dec b + jr nz, .fill_spaces_loop + ld [hl], TX_END + pop hl + pop de + pop bc + ret diff --git a/src/engine/duel/animations.asm b/src/engine/duel/animations.asm new file mode 100644 index 0000000..46b1ea1 --- /dev/null +++ b/src/engine/duel/animations.asm @@ -0,0 +1,354 @@ +; reads the animation commands from PointerTable_AttackAnimation +; of attack in wLoadedAttackAnimation and plays them +PlayAttackAnimationCommands: + ld a, [wLoadedAttackAnimation] + or a + ret z + + ld l, a + ld h, 0 + add hl, hl + ld de, PointerTable_AttackAnimation + add hl, de + ld e, [hl] + inc hl + ld d, [hl] + + push de + ld hl, wce7e + ld a, [hl] + or a + jr nz, .read_command + ld [hl], $01 + call Func_3b21 + pop de + + push de + ld a, DUEL_ANIM_SCREEN_MAIN_SCENE + ld [wDuelAnimationScreen], a + ld a, $01 + ld [wd4b3], a + xor a + ld [wDuelAnimLocationParam], a + ld a, [de] + cp $04 + jr z, .read_command + ld a, DUEL_ANIM_150 + call PlayDuelAnimation +.read_command + pop de + ; fallthrough + +PlayAttackAnimationCommands_NextCommand: + ld a, [de] + inc de + ld hl, AnimationCommandPointerTable + jp JumpToFunctionInTable + +AnimationCommand_AnimEnd: + ret + +AnimationCommand_AnimPlayer: + ldh a, [hWhoseTurn] + ld [wDuelAnimDuelistSide], a + ld a, [wDuelType] + cp $00 + jr nz, AnimationCommand_AnimNormal + ld a, PLAYER_TURN + ld [wDuelAnimDuelistSide], a + jr AnimationCommand_AnimNormal + +AnimationCommand_AnimOpponent: + call SwapTurn + ldh a, [hWhoseTurn] + ld [wDuelAnimDuelistSide], a + call SwapTurn + ld a, [wDuelType] + cp $00 + jr nz, AnimationCommand_AnimNormal + ld a, OPPONENT_TURN + ld [wDuelAnimDuelistSide], a + jr AnimationCommand_AnimNormal + +AnimationCommand_AnimUnknown2: + ld a, [wce82] + and $7f + ld [wDuelAnimLocationParam], a + jr AnimationCommand_AnimNormal + +AnimationCommand_AnimEnd2: + ret + +AnimationCommand_AnimNormal: + ld a, [de] + inc de + cp DUEL_ANIM_SHOW_DAMAGE + jr z, .show_damage + cp DUEL_ANIM_SHAKE1 + jr z, .shake_1 + cp DUEL_ANIM_SHAKE2 + jr z, .shake_2 + cp DUEL_ANIM_SHAKE3 + jr z, .shake_3 + +.play_anim + call PlayDuelAnimation + jr PlayAttackAnimationCommands_NextCommand + +.show_damage + ld a, DUEL_ANIM_PRINT_DAMAGE + call PlayDuelAnimation + ld a, [wce81] + ld [wd4b3], a + + push de + ld hl, wce7f + ld de, wDuelAnimDamage + ld a, [hli] + ld [de], a + inc de + ld a, [hli] + ld [de], a + pop de + + ld a, $8c + call PlayDuelAnimation + ld a, [wDuelDisplayedScreen] + cp DUEL_MAIN_SCENE + jr nz, .skip_update_hud + ld a, DUEL_ANIM_UPDATE_HUD + call PlayDuelAnimation +.skip_update_hud + jp PlayAttackAnimationCommands_NextCommand + +; screen shake happens differently +; depending on whose turn it is +.shake_1 + ld c, DUEL_ANIM_SMALL_SHAKE_X + ld b, DUEL_ANIM_SMALL_SHAKE_Y + jr .check_duelist + +.shake_2 + ld c, DUEL_ANIM_BIG_SHAKE_X + ld b, DUEL_ANIM_BIG_SHAKE_Y + jr .check_duelist + +.shake_3 + ld c, DUEL_ANIM_SMALL_SHAKE_Y + ld b, DUEL_ANIM_SMALL_SHAKE_X + +.check_duelist + ldh a, [hWhoseTurn] + cp PLAYER_TURN + ld a, c + jr z, .play_anim + ld a, [wDuelType] + cp $00 + ld a, c + jr z, .play_anim + ld a, b + jr .play_anim + +AnimationCommand_AnimUnknown: + ld a, [de] + inc de + ld [wd4b3], a + ld a, [wce82] + ld [wDuelAnimLocationParam], a + call SetDuelAnimationScreen + ld a, DUEL_ANIM_150 + call PlayDuelAnimation + jp PlayAttackAnimationCommands_NextCommand + +AnimationCommandPointerTable: + dw AnimationCommand_AnimEnd ; anim_end + dw AnimationCommand_AnimNormal ; anim_normal + dw AnimationCommand_AnimPlayer ; anim_player + dw AnimationCommand_AnimOpponent ; anim_opponent + dw AnimationCommand_AnimUnknown ; anim_unknown + dw AnimationCommand_AnimUnknown2 ; anim_unknown2 + dw AnimationCommand_AnimEnd2 ; anim_end2 (unused) + +; sets wDuelAnimationScreen according to wd4b3 +; if wd4b3 == $01, set it to Main Scene +; if wd4b3 == $04, st it to Play Area scene +SetDuelAnimationScreen: + ld a, [wd4b3] + cp $04 + jr z, .set_play_area_screen + cp $01 + ret nz + ld a, DUEL_ANIM_SCREEN_MAIN_SCENE + ld [wDuelAnimationScreen], a + ret + +.set_play_area_screen + ld a, [wDuelAnimLocationParam] + ld l, a + ld a, [wWhoseTurn] + ld h, a + cp PLAYER_TURN + jr z, .player + +; opponent + ld a, [wDuelType] + cp $00 + jr z, .asm_50c6 + +; link duel or vs. AI + bit 7, l + jr z, .asm_50e2 + jr .asm_50d2 + +.asm_50c6 + bit 7, l + jr z, .asm_50da + jr .asm_50ea + +.player + bit 7, l + jr z, .asm_50d2 + jr .asm_50e2 + +.asm_50d2 + ld l, UNKNOWN_SCREEN_4 + ld h, PLAYER_TURN + ld a, DUEL_ANIM_SCREEN_PLAYER_PLAY_AREA + jr .ok +.asm_50da + ld l, UNKNOWN_SCREEN_4 + ld h, OPPONENT_TURN + ld a, DUEL_ANIM_SCREEN_PLAYER_PLAY_AREA + jr .ok +.asm_50e2 + ld l, UNKNOWN_SCREEN_5 + ld h, OPPONENT_TURN + ld a, DUEL_ANIM_SCREEN_OPP_PLAY_AREA + jr .ok +.asm_50ea + ld l, UNKNOWN_SCREEN_5 + ld h, PLAYER_TURN + ld a, DUEL_ANIM_SCREEN_OPP_PLAY_AREA + +.ok + ld [wDuelAnimationScreen], a + ret + +Func_190f4: + ld a, [wd4b3] + cp $04 + jr z, Func_1910f + ; fallthrough + +Func_190fb: + cp $01 + jr nz, .done + ld a, DUEL_ANIM_SCREEN_MAIN_SCENE + ld [wDuelAnimationScreen], a + ld a, [wDuelDisplayedScreen] + cp $01 + jr z, .done + bank1call DrawDuelMainScene +.done + ret + +Func_1910f: + call SetDuelAnimationScreen + ld a, [wDuelDisplayedScreen] + cp l + jr z, .skip_change_screen + ld a, l + push af + ld l, PLAYER_TURN + ld a, [wDuelType] + cp $00 + jr nz, .asm_5127 + ld a, [wWhoseTurn] + ld l, a +.asm_5127 + call DrawYourOrOppPlayAreaScreen_Bank0 + pop af + ld [wDuelDisplayedScreen], a +.skip_change_screen + call DrawWideTextBox + ret + +; prints text related to the damage received +; by card stored in wTempNonTurnDuelistCardID +; takes into account type effectiveness +PrintDamageText: + push hl + push bc + push de + ld a, [wLoadedAttackAnimation] + cp ATK_ANIM_HEAL + jr z, .skip + cp ATK_ANIM_HEALING_WIND_PLAY_AREA + jr z, .skip + + ld a, [wTempNonTurnDuelistCardID] + ld e, a + ld d, $00 + call LoadCardDataToBuffer1_FromCardID + ld a, 18 + call CopyCardNameAndLevel + ld [hl], TX_END + ld hl, wTxRam2 + xor a + ld [hli], a + ld [hl], a + ld hl, wce7f + ld a, [hli] + ld h, [hl] + ld l, a + call GetDamageText + ld a, l + or h + call nz, DrawWideTextBox_PrintText +.skip + pop de + pop bc + pop hl + ret + +; returns in hl the text id associated with +; the damage in hl and its effectiveness +GetDamageText: + ld a, l + or h + jr z, .no_damage + call LoadTxRam3 + ld a, [wce81] + ldtx hl, AttackDamageText + and (1 << RESISTANCE) | (1 << WEAKNESS) + ret z ; not weak or resistant + ldtx hl, WeaknessMoreDamage2Text + cp (1 << RESISTANCE) | (1 << WEAKNESS) + ret z ; weak and resistant + and (1 << WEAKNESS) + ldtx hl, WeaknessMoreDamageText + ret nz ; weak + ldtx hl, ResistanceLessDamageText + ret ; resistant + +.no_damage + call CheckNoDamageOrEffect + ret c + ldtx hl, NoDamageText + ld a, [wce81] + and (1 << RESISTANCE) + ret z ; not resistant + ldtx hl, ResistanceNoDamageText + ret ; resistant + +UpdateMainSceneHUD: + ld a, [wDuelDisplayedScreen] + cp DUEL_MAIN_SCENE + ret nz + bank1call DrawDuelHUDs + ret + +Func_191a3: + ret + +INCLUDE "data/duel/animations/attack_animations.asm" diff --git a/src/engine/duel/core.asm b/src/engine/duel/core.asm index 3b4b95c..f2f08c7 100644 --- a/src/engine/duel/core.asm +++ b/src/engine/duel/core.asm @@ -5983,7 +5983,7 @@ PrintUsedTrainerCardDescription: ; byte 0 is $01, bytes 1 and 2 are the checksum, byte 3 is [wDuelType] ; next $33a bytes come from DuelDataToSave SaveDuelData: - farcall CommentedOut_1a6cc + farcall StubbedUnusedSaveDataValidation ld de, sCurrentDuel ; fallthrough diff --git a/src/engine/duel/effect_commands.asm b/src/engine/duel/effect_commands.asm new file mode 100644 index 0000000..e96ef7e --- /dev/null +++ b/src/engine/duel/effect_commands.asm @@ -0,0 +1,1619 @@ +EffectCommands: ; 186f7 (6:46f7) +; Each attack has a two-byte effect pointer (attack's 7th param) that points to one of these structures. +; Similarly, trainer cards have a two-byte pointer (7th param) to one of these structures, which determines the card's function. +; Energy cards also point to one of these, but their data is just $00. +; db EFFECTCMDTYPE_* ($01 - $0a) +; dw Function +; ... +; db $00 + +; Commands are associated to a time or a scope (EFFECTCMDTYPE_*) that determines when their function is executed during the turn. +; - EFFECTCMDTYPE_INITIAL_EFFECT_1: Executed right after attack or trainer card is used. Bypasses Smokescreen and Sand Attack effects. +; - EFFECTCMDTYPE_INITIAL_EFFECT_2: Executed right after attack, Pokemon Power, or trainer card is used. +; - EFFECTCMDTYPE_DISCARD_ENERGY: For attacks or trainer cards that require putting one or more attached energy cards into the discard pile. +; - EFFECTCMDTYPE_REQUIRE_SELECTION: For attacks, Pokemon Powers, or trainer cards requiring the user to select a card (from e.g. play area screen or card list). +; - EFFECTCMDTYPE_BEFORE_DAMAGE: Effect command of an attack executed prior to the damage step. For trainer card or Pokemon Power, usually the main effect. +; - EFFECTCMDTYPE_AFTER_DAMAGE: Effect command executed after the damage step. +; - EFFECTCMDTYPE_AI_SWITCH_DEFENDING_PKMN: For attacks that may result in the defending Pokemon being switched out. Called only for AI-executed attacks. +; - EFFECTCMDTYPE_PKMN_POWER_TRIGGER: Pokemon Power effects that trigger the moment the Pokemon card is played. +; - EFFECTCMDTYPE_AI: Used for AI scoring. +; - EFFECTCMDTYPE_AI_SELECTION: When AI is required to select a card + +; Attacks that have an EFFECTCMDTYPE_REQUIRE_SELECTION also must have either an EFFECTCMDTYPE_AI_SWITCH_DEFENDING_PKMN or an +; EFFECTCMDTYPE_AI_SELECTION (for anything not involving switching the defending Pokemon), to handle selections involving the AI. + +; Similar attack effects of different Pokemon cards all point to a different command list, +; even though in some cases their commands and function pointers match. + +; Function name examples +; PoisonEffect ; generic effect shared by multiple attacks. +; Paralysis50PercentEffect ; +; KakunaStiffenEffect ; unique effect from an attack known by multiple cards. +; MetapodStiffenEffect ; +; AcidEffect ; unique effect from an attack known by a single card +; FoulOdorEffect ; +; SpitPoison_Poison50PercentEffect ; unique effect made of more than one command. +; SpitPoison_AIEffect ; + +EkansSpitPoisonEffectCommands: + dbw EFFECTCMDTYPE_BEFORE_DAMAGE, SpitPoison_Poison50PercentEffect + dbw EFFECTCMDTYPE_AI, SpitPoison_AIEffect + db $00 + +EkansWrapEffectCommands: + dbw EFFECTCMDTYPE_BEFORE_DAMAGE, Paralysis50PercentEffect + db $00 + +ArbokTerrorStrikeEffectCommands: + dbw EFFECTCMDTYPE_AFTER_DAMAGE, TerrorStrike_SwitchDefendingPokemon + dbw EFFECTCMDTYPE_REQUIRE_SELECTION, TerrorStrike_50PercentSelectSwitchPokemon + dbw EFFECTCMDTYPE_AI_SWITCH_DEFENDING_PKMN, TerrorStrike_50PercentSelectSwitchPokemon + db $00 + +ArbokPoisonFangEffectCommands: + dbw EFFECTCMDTYPE_BEFORE_DAMAGE, PoisonEffect + dbw EFFECTCMDTYPE_AI, PoisonFang_AIEffect + db $00 + +WeepinbellPoisonPowderEffectCommands: + dbw EFFECTCMDTYPE_BEFORE_DAMAGE, Poison50PercentEffect + dbw EFFECTCMDTYPE_AI, WeepinbellPoisonPowder_AIEffect + db $00 + +VictreebelLureEffectCommands: + dbw EFFECTCMDTYPE_INITIAL_EFFECT_1, VictreebelLure_AssertPokemonInBench + dbw EFFECTCMDTYPE_AFTER_DAMAGE, VictreebelLure_SwitchDefendingPokemon + dbw EFFECTCMDTYPE_REQUIRE_SELECTION, VictreebelLure_SelectSwitchPokemon + dbw EFFECTCMDTYPE_AI_SELECTION, VictreebelLure_GetBenchPokemonWithLowestHP + db $00 + +VictreebelAcidEffectCommands: + dbw EFFECTCMDTYPE_BEFORE_DAMAGE, AcidEffect + db $00 + +PinsirIronGripEffectCommands: + dbw EFFECTCMDTYPE_BEFORE_DAMAGE, Paralysis50PercentEffect + db $00 + +CaterpieStringShotEffectCommands: + dbw EFFECTCMDTYPE_BEFORE_DAMAGE, Paralysis50PercentEffect + db $00 + +GloomPoisonPowderEffectCommands: + dbw EFFECTCMDTYPE_BEFORE_DAMAGE, PoisonEffect + dbw EFFECTCMDTYPE_AI, GloomPoisonPowder_AIEffect + db $00 + +GloomFoulOdorEffectCommands: + dbw EFFECTCMDTYPE_BEFORE_DAMAGE, FoulOdorEffect + db $00 + +KakunaStiffenEffectCommands: + dbw EFFECTCMDTYPE_BEFORE_DAMAGE, KakunaStiffenEffect + db $00 + +KakunaPoisonPowderEffectCommands: + dbw EFFECTCMDTYPE_BEFORE_DAMAGE, Poison50PercentEffect + dbw EFFECTCMDTYPE_AI, KakunaPoisonPowder_AIEffect + db $00 + +GolbatLeechLifeEffectCommands: + dbw EFFECTCMDTYPE_AFTER_DAMAGE, GolbatLeechLifeEffect + db $00 + +VenonatStunSporeEffectCommands: + dbw EFFECTCMDTYPE_BEFORE_DAMAGE, Paralysis50PercentEffect + db $00 + +VenonatLeechLifeEffectCommands: + dbw EFFECTCMDTYPE_AFTER_DAMAGE, VenonatLeechLifeEffect + db $00 + +ScytherSwordsDanceEffectCommands: + dbw EFFECTCMDTYPE_BEFORE_DAMAGE, SwordsDanceEffect + db $00 + +ZubatSupersonicEffectCommands: + dbw EFFECTCMDTYPE_BEFORE_DAMAGE, ZubatSupersonicEffect + db $00 + +ZubatLeechLifeEffectCommands: + dbw EFFECTCMDTYPE_AFTER_DAMAGE, ZubatLeechLifeEffect + db $00 + +BeedrillTwineedleEffectCommands: + dbw EFFECTCMDTYPE_BEFORE_DAMAGE, Twineedle_MultiplierEffect + dbw EFFECTCMDTYPE_AI, Twineedle_AIEffect + db $00 + +BeedrillPoisonStingEffectCommands: + dbw EFFECTCMDTYPE_BEFORE_DAMAGE, Poison50PercentEffect + dbw EFFECTCMDTYPE_AI, BeedrillPoisonSting_AIEffect + db $00 + +ExeggcuteHypnosisEffectCommands: + dbw EFFECTCMDTYPE_BEFORE_DAMAGE, SleepEffect + db $00 + +ExeggcuteLeechSeedEffectCommands: + dbw EFFECTCMDTYPE_AFTER_DAMAGE, ExeggcuteLeechSeedEffect + db $00 + +KoffingFoulGasEffectCommands: + dbw EFFECTCMDTYPE_BEFORE_DAMAGE, FoulGas_PoisonOrConfusionEffect + dbw EFFECTCMDTYPE_AI, FoulGas_AIEffect + db $00 + +MetapodStiffenEffectCommands: + dbw EFFECTCMDTYPE_BEFORE_DAMAGE, MetapodStiffenEffect + db $00 + +MetapodStunSporeEffectCommands: + dbw EFFECTCMDTYPE_BEFORE_DAMAGE, Paralysis50PercentEffect + db $00 + +OddishStunSporeEffectCommands: + dbw EFFECTCMDTYPE_BEFORE_DAMAGE, Paralysis50PercentEffect + db $00 + +OddishSproutEffectCommands: + dbw EFFECTCMDTYPE_INITIAL_EFFECT_1, Sprout_CheckDeckAndPlayArea + dbw EFFECTCMDTYPE_AFTER_DAMAGE, Sprout_PutInPlayAreaEffect + dbw EFFECTCMDTYPE_REQUIRE_SELECTION, Sprout_PlayerSelectEffect + dbw EFFECTCMDTYPE_AI_SELECTION, Sprout_AISelectEffect + db $00 + +ExeggutorTeleportEffectCommands: + dbw EFFECTCMDTYPE_INITIAL_EFFECT_1, Teleport_CheckBench + dbw EFFECTCMDTYPE_AFTER_DAMAGE, Teleport_SwitchEffect + dbw EFFECTCMDTYPE_REQUIRE_SELECTION, Teleport_PlayerSelectEffect + dbw EFFECTCMDTYPE_AI_SELECTION, Teleport_AISelectEffect + db $00 + +ExeggutorBigEggsplosionEffectCommands: + dbw EFFECTCMDTYPE_BEFORE_DAMAGE, BigEggsplosion_MultiplierEffect + dbw EFFECTCMDTYPE_AI, BigEggsplosion_AIEffect + db $00 + +NidokingThrashEffectCommands: + dbw EFFECTCMDTYPE_BEFORE_DAMAGE, Thrash_ModifierEffect + dbw EFFECTCMDTYPE_AFTER_DAMAGE, Thrash_RecoilEffect + dbw EFFECTCMDTYPE_AI, Thrash_AIEffect + db $00 + +NidokingToxicEffectCommands: + dbw EFFECTCMDTYPE_BEFORE_DAMAGE, Toxic_DoublePoisonEffect + dbw EFFECTCMDTYPE_AI, Toxic_AIEffect + db $00 + +NidoqueenBoyfriendsEffectCommands: + dbw EFFECTCMDTYPE_BEFORE_DAMAGE, BoyfriendsEffect + db $00 + +NidoranFFurySwipesEffectCommands: + dbw EFFECTCMDTYPE_BEFORE_DAMAGE, NidoranFFurySwipes_MultiplierEffect + dbw EFFECTCMDTYPE_AI, NidoranFFurySwipes_AIEffect + db $00 + +NidoranFCallForFamilyEffectCommands: + dbw EFFECTCMDTYPE_INITIAL_EFFECT_1, NidoranFCallForFamily_CheckDeckAndPlayArea + dbw EFFECTCMDTYPE_AFTER_DAMAGE, NidoranFCallForFamily_PutInPlayAreaEffect + dbw EFFECTCMDTYPE_REQUIRE_SELECTION, NidoranFCallForFamily_PlayerSelectEffect + dbw EFFECTCMDTYPE_AI_SELECTION, NidoranFCallForFamily_AISelectEffect + db $00 + +NidoranMHornHazardEffectCommands: + dbw EFFECTCMDTYPE_BEFORE_DAMAGE, HornHazard_NoDamage50PercentEffect + dbw EFFECTCMDTYPE_AI, HornHazard_AIEffect + db $00 + +NidorinaSupersonicEffectCommands: + dbw EFFECTCMDTYPE_BEFORE_DAMAGE, NidorinaSupersonicEffect + db $00 + +NidorinaDoubleKickEffectCommands: + dbw EFFECTCMDTYPE_BEFORE_DAMAGE, NidorinaDoubleKick_MultiplierEffect + dbw EFFECTCMDTYPE_AI, NidorinaDoubleKick_AIEffect + db $00 + +NidorinoDoubleKickEffectCommands: + dbw EFFECTCMDTYPE_BEFORE_DAMAGE, NidorinoDoubleKick_MultiplierEffect + dbw EFFECTCMDTYPE_AI, NidorinoDoubleKick_AIEffect + db $00 + +ButterfreeWhirlwindEffectCommands: + dbw EFFECTCMDTYPE_AFTER_DAMAGE, ButterfreeWhirlwind_SwitchEffect + dbw EFFECTCMDTYPE_REQUIRE_SELECTION, ButterfreeWhirlwind_CheckBench + dbw EFFECTCMDTYPE_AI_SWITCH_DEFENDING_PKMN, ButterfreeWhirlwind_CheckBench + db $00 + +ButterfreeMegaDrainEffectCommands: + dbw EFFECTCMDTYPE_AFTER_DAMAGE, ButterfreeMegaDrainEffect + db $00 + +ParasSporeEffectCommands: + dbw EFFECTCMDTYPE_BEFORE_DAMAGE, SleepEffect + db $00 + +ParasectSporeEffectCommands: + dbw EFFECTCMDTYPE_BEFORE_DAMAGE, SleepEffect + db $00 + +WeedlePoisonStingEffectCommands: + dbw EFFECTCMDTYPE_BEFORE_DAMAGE, Poison50PercentEffect + dbw EFFECTCMDTYPE_AI, WeedlePoisonSting_AIEffect + db $00 + +IvysaurPoisonPowderEffectCommands: + dbw EFFECTCMDTYPE_BEFORE_DAMAGE, PoisonEffect + dbw EFFECTCMDTYPE_AI, IvysaurPoisonPowder_AIEffect + db $00 + +BulbasaurLeechSeedEffectCommands: + dbw EFFECTCMDTYPE_AFTER_DAMAGE, BulbasaurLeechSeedEffect + db $00 + +VenusaurEnergyTransEffectCommands: + dbw EFFECTCMDTYPE_INITIAL_EFFECT_2, EnergyTrans_CheckPlayArea + dbw EFFECTCMDTYPE_BEFORE_DAMAGE, EnergyTrans_TransferEffect + dbw EFFECTCMDTYPE_AFTER_DAMAGE, EnergyTrans_AIEffect + dbw EFFECTCMDTYPE_REQUIRE_SELECTION, EnergyTrans_PrintProcedure + db $00 + +GrimerNastyGooEffectCommands: + dbw EFFECTCMDTYPE_BEFORE_DAMAGE, Paralysis50PercentEffect + db $00 + +GrimerMinimizeEffectCommands: + dbw EFFECTCMDTYPE_BEFORE_DAMAGE, GrimerMinimizeEffect + db $00 + +MukToxicGasEffectCommands: + dbw EFFECTCMDTYPE_INITIAL_EFFECT_1, ToxicGasEffect + db $00 + +MukSludgeEffectCommands: + dbw EFFECTCMDTYPE_BEFORE_DAMAGE, Poison50PercentEffect + dbw EFFECTCMDTYPE_AI, Sludge_AIEffect + db $00 + +BellsproutCallForFamilyEffectCommands: + dbw EFFECTCMDTYPE_INITIAL_EFFECT_1, BellsproutCallForFamily_CheckDeckAndPlayArea + dbw EFFECTCMDTYPE_AFTER_DAMAGE, BellsproutCallForFamily_PutInPlayAreaEffect + dbw EFFECTCMDTYPE_REQUIRE_SELECTION, BellsproutCallForFamily_PlayerSelectEffect + dbw EFFECTCMDTYPE_AI_SELECTION, BellsproutCallForFamily_AISelectEffect + db $00 + +WeezingSmogEffectCommands: + dbw EFFECTCMDTYPE_BEFORE_DAMAGE, Poison50PercentEffect + dbw EFFECTCMDTYPE_AI, WeezingSmog_AIEffect + db $00 + +WeezingSelfdestructEffectCommands: + dbw EFFECTCMDTYPE_AFTER_DAMAGE, WeezingSelfdestructEffect + db $00 + +VenomothShiftEffectCommands: + dbw EFFECTCMDTYPE_INITIAL_EFFECT_2, Shift_OncePerTurnCheck + dbw EFFECTCMDTYPE_BEFORE_DAMAGE, Shift_ChangeColorEffect + dbw EFFECTCMDTYPE_REQUIRE_SELECTION, Shift_PlayerSelectEffect + db $00 + +VenomothVenomPowderEffectCommands: + dbw EFFECTCMDTYPE_BEFORE_DAMAGE, VenomPowder_PoisonConfusion50PercentEffect + dbw EFFECTCMDTYPE_AI, VenomPowder_AIEffect + db $00 + +TangelaBindEffectCommands: + dbw EFFECTCMDTYPE_BEFORE_DAMAGE, Paralysis50PercentEffect + db $00 + +TangelaPoisonPowderEffectCommands: + dbw EFFECTCMDTYPE_BEFORE_DAMAGE, PoisonEffect + dbw EFFECTCMDTYPE_AI, TangelaPoisonPowder_AIEffect + db $00 + +VileplumeHealEffectCommands: + dbw EFFECTCMDTYPE_INITIAL_EFFECT_2, Heal_OncePerTurnCheck + dbw EFFECTCMDTYPE_BEFORE_DAMAGE, Heal_RemoveDamageEffect + db $00 + +VileplumePetalDanceEffectCommands: + dbw EFFECTCMDTYPE_BEFORE_DAMAGE, PetalDance_MultiplierEffect + dbw EFFECTCMDTYPE_AI, PetalDance_AIEffect + db $00 + +TangelaStunSporeEffectCommands: + dbw EFFECTCMDTYPE_BEFORE_DAMAGE, Paralysis50PercentEffect + db $00 + +TangelaPoisonWhipEffectCommands: + dbw EFFECTCMDTYPE_BEFORE_DAMAGE, PoisonEffect + dbw EFFECTCMDTYPE_AI, PoisonWhip_AIEffect + db $00 + +VenusaurSolarPowerEffectCommands: + dbw EFFECTCMDTYPE_INITIAL_EFFECT_2, SolarPower_CheckUse + dbw EFFECTCMDTYPE_BEFORE_DAMAGE, SolarPower_RemoveStatusEffect + db $00 + +VenusaurMegaDrainEffectCommands: + dbw EFFECTCMDTYPE_AFTER_DAMAGE, VenusaurMegaDrainEffect + db $00 + +OmastarWaterGunEffectCommands: + dbw EFFECTCMDTYPE_BEFORE_DAMAGE, OmastarWaterGunEffect + dbw EFFECTCMDTYPE_AI, OmastarWaterGunEffect + db $00 + +OmastarSpikeCannonEffectCommands: + dbw EFFECTCMDTYPE_BEFORE_DAMAGE, OmastarSpikeCannon_MultiplierEffect + dbw EFFECTCMDTYPE_AI, OmastarSpikeCannon_AIEffect + db $00 + +OmanyteClairvoyanceEffectCommands: + dbw EFFECTCMDTYPE_INITIAL_EFFECT_1, ClairvoyanceEffect + db $00 + +OmanyteWaterGunEffectCommands: + dbw EFFECTCMDTYPE_BEFORE_DAMAGE, OmanyteWaterGunEffect + dbw EFFECTCMDTYPE_AI, OmanyteWaterGunEffect + db $00 + +WartortleWithdrawEffectCommands: + dbw EFFECTCMDTYPE_BEFORE_DAMAGE, WartortleWithdrawEffect + db $00 + +BlastoiseRainDanceEffectCommands: + dbw EFFECTCMDTYPE_INITIAL_EFFECT_1, RainDanceEffect + db $00 + +BlastoiseHydroPumpEffectCommands: + dbw EFFECTCMDTYPE_BEFORE_DAMAGE, HydroPumpEffect + dbw EFFECTCMDTYPE_AI, HydroPumpEffect + db $00 + +GyaradosBubblebeamEffectCommands: + dbw EFFECTCMDTYPE_BEFORE_DAMAGE, Paralysis50PercentEffect + db $00 + +KinglerFlailEffectCommands: + dbw EFFECTCMDTYPE_BEFORE_DAMAGE, KinglerFlail_HPCheck + dbw EFFECTCMDTYPE_AI, KinglerFlail_AIEffect + db $00 + +KrabbyCallForFamilyEffectCommands: + dbw EFFECTCMDTYPE_INITIAL_EFFECT_1, KrabbyCallForFamily_CheckDeckAndPlayArea + dbw EFFECTCMDTYPE_AFTER_DAMAGE, KrabbyCallForFamily_PutInPlayAreaEffect + dbw EFFECTCMDTYPE_REQUIRE_SELECTION, KrabbyCallForFamily_PlayerSelectEffect + dbw EFFECTCMDTYPE_AI_SELECTION, KrabbyCallForFamily_AISelectEffect + db $00 + +MagikarpFlailEffectCommands: + dbw EFFECTCMDTYPE_BEFORE_DAMAGE, MagikarpFlail_HPCheck + dbw EFFECTCMDTYPE_AI, MagikarpFlail_AIEffect + db $00 + +PsyduckHeadacheEffectCommands: + dbw EFFECTCMDTYPE_BEFORE_DAMAGE, HeadacheEffect + db $00 + +PsyduckFurySwipesEffectCommands: + dbw EFFECTCMDTYPE_BEFORE_DAMAGE, PsyduckFurySwipes_MultiplierEffect + dbw EFFECTCMDTYPE_AI, PsyduckFurySwipes_AIEffect + db $00 + +GolduckPsyshockEffectCommands: + dbw EFFECTCMDTYPE_BEFORE_DAMAGE, Paralysis50PercentEffect + db $00 + +GolduckHyperBeamEffectCommands: + dbw EFFECTCMDTYPE_AFTER_DAMAGE, GolduckHyperBeam_DiscardEffect + dbw EFFECTCMDTYPE_REQUIRE_SELECTION, GolduckHyperBeam_PlayerSelectEffect + dbw EFFECTCMDTYPE_AI_SELECTION, GolduckHyperBeam_AISelectEffect + db $00 + +SeadraWaterGunEffectCommands: + dbw EFFECTCMDTYPE_BEFORE_DAMAGE, SeadraWaterGunEffect + dbw EFFECTCMDTYPE_AI, SeadraWaterGunEffect + db $00 + +SeadraAgilityEffectCommands: + dbw EFFECTCMDTYPE_BEFORE_DAMAGE, SeadraAgilityEffect + db $00 + +ShellderSupersonicEffectCommands: + dbw EFFECTCMDTYPE_BEFORE_DAMAGE, ShellderSupersonicEffect + db $00 + +ShellderHideInShellEffectCommands: + dbw EFFECTCMDTYPE_BEFORE_DAMAGE, HideInShellEffect + db $00 + +VaporeonQuickAttackEffectCommands: + dbw EFFECTCMDTYPE_BEFORE_DAMAGE, VaporeonQuickAttack_DamageBoostEffect + dbw EFFECTCMDTYPE_AI, VaporeonQuickAttack_AIEffect + db $00 + +VaporeonWaterGunEffectCommands: + dbw EFFECTCMDTYPE_BEFORE_DAMAGE, VaporeonWaterGunEffect + dbw EFFECTCMDTYPE_AI, VaporeonWaterGunEffect + db $00 + +DewgongIceBeamEffectCommands: + dbw EFFECTCMDTYPE_BEFORE_DAMAGE, Paralysis50PercentEffect + db $00 + +StarmieRecoverEffectCommands: + dbw EFFECTCMDTYPE_INITIAL_EFFECT_1, StarmieRecover_CheckEnergyHP + dbw EFFECTCMDTYPE_INITIAL_EFFECT_2, StarmieRecover_PlayerSelectEffect + dbw EFFECTCMDTYPE_AFTER_DAMAGE, StarmieRecover_HealEffect + dbw EFFECTCMDTYPE_DISCARD_ENERGY, StarmieRecover_DiscardEffect + dbw EFFECTCMDTYPE_AI_SELECTION, StarmieRecover_AISelectEffect + db $00 + +StarmieStarFreezeEffectCommands: + dbw EFFECTCMDTYPE_BEFORE_DAMAGE, Paralysis50PercentEffect + db $00 + +SquirtleBubbleEffectCommands: + dbw EFFECTCMDTYPE_BEFORE_DAMAGE, Paralysis50PercentEffect + db $00 + +SquirtleWithdrawEffectCommands: + dbw EFFECTCMDTYPE_BEFORE_DAMAGE, SquirtleWithdrawEffect + db $00 + +HorseaSmokescreenEffectCommands: + dbw EFFECTCMDTYPE_BEFORE_DAMAGE, HorseaSmokescreenEffect + db $00 + +TentacruelSupersonicEffectCommands: + dbw EFFECTCMDTYPE_BEFORE_DAMAGE, TentacruelSupersonicEffect + db $00 + +TentacruelJellyfishStingEffectCommands: + dbw EFFECTCMDTYPE_BEFORE_DAMAGE, PoisonEffect + dbw EFFECTCMDTYPE_AI, JellyfishSting_AIEffect + db $00 + +PoliwhirlAmnesiaEffectCommands: + dbw EFFECTCMDTYPE_INITIAL_EFFECT_1, PoliwhirlAmnesia_CheckAttacks + dbw EFFECTCMDTYPE_INITIAL_EFFECT_2, PoliwhirlAmnesia_PlayerSelectEffect + dbw EFFECTCMDTYPE_BEFORE_DAMAGE, PoliwhirlAmnesia_DisableEffect + dbw EFFECTCMDTYPE_AI_SELECTION, PoliwhirlAmnesia_AISelectEffect + db $00 + +PoliwhirlDoubleslapEffectCommands: + dbw EFFECTCMDTYPE_BEFORE_DAMAGE, PoliwhirlDoubleslap_MultiplierEffect + dbw EFFECTCMDTYPE_AI, PoliwhirlDoubleslap_AIEffect + db $00 + +PoliwrathWaterGunEffectCommands: + dbw EFFECTCMDTYPE_BEFORE_DAMAGE, PoliwrathWaterGunEffect + dbw EFFECTCMDTYPE_AI, PoliwrathWaterGunEffect + db $00 + +PoliwrathWhirlpoolEffectCommands: + dbw EFFECTCMDTYPE_AFTER_DAMAGE, Whirlpool_DiscardEffect + dbw EFFECTCMDTYPE_REQUIRE_SELECTION, Whirlpool_PlayerSelectEffect + dbw EFFECTCMDTYPE_AI_SELECTION, Whirlpool_AISelectEffect + db $00 + +PoliwagWaterGunEffectCommands: + dbw EFFECTCMDTYPE_BEFORE_DAMAGE, PoliwagWaterGunEffect + dbw EFFECTCMDTYPE_AI, PoliwagWaterGunEffect + db $00 + +CloysterClampEffectCommands: + dbw EFFECTCMDTYPE_BEFORE_DAMAGE, ClampEffect + db $00 + +CloysterSpikeCannonEffectCommands: + dbw EFFECTCMDTYPE_BEFORE_DAMAGE, CloysterSpikeCannon_MultiplierEffect + dbw EFFECTCMDTYPE_AI, CloysterSpikeCannon_AIEffect + db $00 + +ArticunoFreezeDryEffectCommands: + dbw EFFECTCMDTYPE_BEFORE_DAMAGE, Paralysis50PercentEffect + db $00 + +ArticunoBlizzardEffectCommands: + dbw EFFECTCMDTYPE_BEFORE_DAMAGE, Blizzard_BenchDamage50PercentEffect + dbw EFFECTCMDTYPE_AFTER_DAMAGE, Blizzard_BenchDamageEffect + db $00 + +TentacoolCowardiceEffectCommands: + dbw EFFECTCMDTYPE_INITIAL_EFFECT_2, Cowardice_Check + dbw EFFECTCMDTYPE_BEFORE_DAMAGE, Cowardice_RemoveFromPlayAreaEffect + dbw EFFECTCMDTYPE_REQUIRE_SELECTION, Cowardice_PlayerSelectEffect + db $00 + +LaprasWaterGunEffectCommands: + dbw EFFECTCMDTYPE_BEFORE_DAMAGE, LaprasWaterGunEffect + dbw EFFECTCMDTYPE_AI, LaprasWaterGunEffect + db $00 + +LaprasConfuseRayEffectCommands: + dbw EFFECTCMDTYPE_BEFORE_DAMAGE, Confusion50PercentEffect + db $00 + +ArticunoQuickfreezeEffectCommands: + dbw EFFECTCMDTYPE_INITIAL_EFFECT_1, Quickfreeze_InitialEffect + dbw EFFECTCMDTYPE_PKMN_POWER_TRIGGER, Quickfreeze_Paralysis50PercentEffect + db $00 + +ArticunoIceBreathEffectCommands: + dbw EFFECTCMDTYPE_BEFORE_DAMAGE, IceBreath_ZeroDamage + dbw EFFECTCMDTYPE_AFTER_DAMAGE, IceBreath_RandomPokemonDamageEffect + db $00 + +VaporeonFocusEnergyEffectCommands: + dbw EFFECTCMDTYPE_BEFORE_DAMAGE, FocusEnergyEffect + db $00 + +ArcanineFlamethrowerEffectCommands: + dbw EFFECTCMDTYPE_INITIAL_EFFECT_1, ArcanineFlamethrower_CheckEnergy + dbw EFFECTCMDTYPE_INITIAL_EFFECT_2, ArcanineFlamethrower_PlayerSelectEffect + dbw EFFECTCMDTYPE_DISCARD_ENERGY, ArcanineFlamethrower_DiscardEffect + dbw EFFECTCMDTYPE_AI_SELECTION, ArcanineFlamethrower_AISelectEffect + db $00 + +ArcanineTakeDownEffectCommands: + dbw EFFECTCMDTYPE_AFTER_DAMAGE, TakeDownEffect + db $00 + +ArcanineQuickAttackEffectCommands: + dbw EFFECTCMDTYPE_BEFORE_DAMAGE, ArcanineQuickAttack_DamageBoostEffect + dbw EFFECTCMDTYPE_AI, ArcanineQuickAttack_AIEffect + db $00 + +ArcanineFlamesOfRageEffectCommands: + dbw EFFECTCMDTYPE_INITIAL_EFFECT_1, FlamesOfRage_CheckEnergy + dbw EFFECTCMDTYPE_INITIAL_EFFECT_2, FlamesOfRage_PlayerSelectEffect + dbw EFFECTCMDTYPE_BEFORE_DAMAGE, FlamesOfRage_DamageBoostEffect + dbw EFFECTCMDTYPE_DISCARD_ENERGY, FlamesOfRage_DiscardEffect + dbw EFFECTCMDTYPE_AI_SELECTION, FlamesOfRage_AISelectEffect + dbw EFFECTCMDTYPE_AI, FlamesOfRage_AIEffect + db $00 + +RapidashStompEffectCommands: + dbw EFFECTCMDTYPE_BEFORE_DAMAGE, RapidashStomp_DamageBoostEffect + dbw EFFECTCMDTYPE_AI, RapidashStomp_AIEffect + db $00 + +RapidashAgilityEffectCommands: + dbw EFFECTCMDTYPE_BEFORE_DAMAGE, RapidashAgilityEffect + db $00 + +NinetalesLureEffectCommands: + dbw EFFECTCMDTYPE_INITIAL_EFFECT_1, NinetalesLure_CheckBench + dbw EFFECTCMDTYPE_AFTER_DAMAGE, NinetalesLure_SwitchEffect + dbw EFFECTCMDTYPE_REQUIRE_SELECTION, NinetalesLure_PlayerSelectEffect + dbw EFFECTCMDTYPE_AI_SELECTION, NinetalesLure_AISelectEffect + db $00 + +NinetalesFireBlastEffectCommands: + dbw EFFECTCMDTYPE_INITIAL_EFFECT_1, FireBlast_CheckEnergy + dbw EFFECTCMDTYPE_INITIAL_EFFECT_2, FireBlast_PlayerSelectEffect + dbw EFFECTCMDTYPE_DISCARD_ENERGY, FireBlast_DiscardEffect + dbw EFFECTCMDTYPE_AI_SELECTION, FireBlast_AISelectEffect + db $00 + +CharmanderEmberEffectCommands: + dbw EFFECTCMDTYPE_INITIAL_EFFECT_1, Ember_CheckEnergy + dbw EFFECTCMDTYPE_INITIAL_EFFECT_2, Ember_PlayerSelectEffect + dbw EFFECTCMDTYPE_DISCARD_ENERGY, Ember_DiscardEffect + dbw EFFECTCMDTYPE_AI_SELECTION, Ember_AISelectEffect + db $00 + +MoltresWildfireEffectCommands: + dbw EFFECTCMDTYPE_INITIAL_EFFECT_1, Wildfire_CheckEnergy + dbw EFFECTCMDTYPE_INITIAL_EFFECT_2, Wildfire_PlayerSelectEffect + dbw EFFECTCMDTYPE_AFTER_DAMAGE, Wildfire_DiscardDeckEffect + dbw EFFECTCMDTYPE_DISCARD_ENERGY, Wildfire_DiscardEnergyEffect + dbw EFFECTCMDTYPE_AI_SELECTION, Wildfire_AISelectEffect + db $00 + +Moltres1DiveBombEffectCommands: + dbw EFFECTCMDTYPE_BEFORE_DAMAGE, Moltres1DiveBomb_Success50PercentEffect + dbw EFFECTCMDTYPE_AI, Moltres1DiveBomb_AIEffect + db $00 + +FlareonQuickAttackEffectCommands: + dbw EFFECTCMDTYPE_BEFORE_DAMAGE, FlareonQuickAttack_DamageBoostEffect + dbw EFFECTCMDTYPE_AI, FlareonQuickAttack_AIEffect + db $00 + +FlareonFlamethrowerEffectCommands: + dbw EFFECTCMDTYPE_INITIAL_EFFECT_1, FlareonFlamethrower_CheckEnergy + dbw EFFECTCMDTYPE_INITIAL_EFFECT_2, FlareonFlamethrower_PlayerSelectEffect + dbw EFFECTCMDTYPE_DISCARD_ENERGY, FlareonFlamethrower_DiscardEffect + dbw EFFECTCMDTYPE_AI_SELECTION, FlareonFlamethrower_AISelectEffect + db $00 + +MagmarFlamethrowerEffectCommands: + dbw EFFECTCMDTYPE_INITIAL_EFFECT_1, MagmarFlamethrower_CheckEnergy + dbw EFFECTCMDTYPE_INITIAL_EFFECT_2, MagmarFlamethrower_PlayerSelectEffect + dbw EFFECTCMDTYPE_DISCARD_ENERGY, MagmarFlamethrower_DiscardEffect + dbw EFFECTCMDTYPE_AI_SELECTION, MagmarFlamethrower_AISelectEffect + db $00 + +MagmarSmokescreenEffectCommands: + dbw EFFECTCMDTYPE_BEFORE_DAMAGE, MagmarSmokescreenEffect + db $00 + +MagmarSmogEffectCommands: + dbw EFFECTCMDTYPE_BEFORE_DAMAGE, Poison50PercentEffect + dbw EFFECTCMDTYPE_AI, MagmarSmog_AIEffect + db $00 + +CharmeleonFlamethrowerEffectCommands: + dbw EFFECTCMDTYPE_INITIAL_EFFECT_1, CharmeleonFlamethrower_CheckEnergy + dbw EFFECTCMDTYPE_INITIAL_EFFECT_2, CharmeleonFlamethrower_PlayerSelectEffect + dbw EFFECTCMDTYPE_DISCARD_ENERGY, CharmeleonFlamethrower_DiscardEffect + dbw EFFECTCMDTYPE_AI_SELECTION, CharmeleonFlamethrower_AISelectEffect + db $00 + +CharizardEnergyBurnEffectCommands: + dbw EFFECTCMDTYPE_INITIAL_EFFECT_1, EnergyBurnEffect + db $00 + +CharizardFireSpinEffectCommands: + dbw EFFECTCMDTYPE_INITIAL_EFFECT_1, FireSpin_CheckEnergy + dbw EFFECTCMDTYPE_INITIAL_EFFECT_2, FireSpin_PlayerSelectEffect + dbw EFFECTCMDTYPE_DISCARD_ENERGY, FireSpin_DiscardEffect + dbw EFFECTCMDTYPE_AI_SELECTION, FireSpin_AISelectEffect + db $00 + +VulpixConfuseRayEffectCommands: + dbw EFFECTCMDTYPE_BEFORE_DAMAGE, Confusion50PercentEffect + db $00 + +FlareonRageEffectCommands: + dbw EFFECTCMDTYPE_BEFORE_DAMAGE, FlareonRage_DamageBoostEffect + dbw EFFECTCMDTYPE_AI, FlareonRage_AIEffect + db $00 + +NinetalesMixUpEffectCommands: + dbw EFFECTCMDTYPE_AFTER_DAMAGE, MixUpEffect + db $00 + +NinetalesDancingEmbersEffectCommands: + dbw EFFECTCMDTYPE_BEFORE_DAMAGE, DancingEmbers_MultiplierEffect + dbw EFFECTCMDTYPE_AI, DancingEmbers_AIEffect + db $00 + +MoltresFiregiverEffectCommands: + dbw EFFECTCMDTYPE_INITIAL_EFFECT_1, Firegiver_InitialEffect + dbw EFFECTCMDTYPE_PKMN_POWER_TRIGGER, Firegiver_AddToHandEffect + db $00 + +Moltres2DiveBombEffectCommands: + dbw EFFECTCMDTYPE_BEFORE_DAMAGE, Moltres2DiveBomb_Success50PercentEffect + dbw EFFECTCMDTYPE_AI, Moltres2DiveBomb_AIEffect + db $00 + +AbraPsyshockEffectCommands: + dbw EFFECTCMDTYPE_BEFORE_DAMAGE, Paralysis50PercentEffect + db $00 + +GengarCurseEffectCommands: + dbw EFFECTCMDTYPE_INITIAL_EFFECT_2, Curse_CheckDamageAndBench + dbw EFFECTCMDTYPE_BEFORE_DAMAGE, Curse_TransferDamageEffect + dbw EFFECTCMDTYPE_REQUIRE_SELECTION, Curse_PlayerSelectEffect + db $00 + +GengarDarkMindEffectCommands: + dbw EFFECTCMDTYPE_AFTER_DAMAGE, GengarDarkMind_DamageBenchEffect + dbw EFFECTCMDTYPE_REQUIRE_SELECTION, GengarDarkMind_PlayerSelectEffect + dbw EFFECTCMDTYPE_AI_SELECTION, GengarDarkMind_AISelectEffect + db $00 + +GastlySleepingGasEffectCommands: + dbw EFFECTCMDTYPE_BEFORE_DAMAGE, SleepingGasEffect + db $00 + +GastlyDestinyBondEffectCommands: + dbw EFFECTCMDTYPE_INITIAL_EFFECT_1, DestinyBond_CheckEnergy + dbw EFFECTCMDTYPE_INITIAL_EFFECT_2, DestinyBond_PlayerSelectEffect + dbw EFFECTCMDTYPE_BEFORE_DAMAGE, DestinyBond_DestinyBondEffect + dbw EFFECTCMDTYPE_DISCARD_ENERGY, DestinyBond_DiscardEffect + dbw EFFECTCMDTYPE_AI_SELECTION, DestinyBond_AISelectEffect + db $00 + +GastlyLickEffectCommands: + dbw EFFECTCMDTYPE_BEFORE_DAMAGE, Paralysis50PercentEffect + db $00 + +GastlyEnergyConversionEffectCommands: + dbw EFFECTCMDTYPE_INITIAL_EFFECT_1, EnergyConversion_CheckEnergy + dbw EFFECTCMDTYPE_AFTER_DAMAGE, EnergyConversion_AddToHandEffect + dbw EFFECTCMDTYPE_REQUIRE_SELECTION, EnergyConversion_PlayerSelectEffect + dbw EFFECTCMDTYPE_AI_SELECTION, EnergyConversion_AISelectEffect + db $00 + +HaunterHypnosisEffectCommands: + dbw EFFECTCMDTYPE_BEFORE_DAMAGE, SleepEffect + db $00 + +HaunterDreamEaterEffectCommands: + dbw EFFECTCMDTYPE_INITIAL_EFFECT_1, DreamEaterEffect + db $00 + +HaunterTransparencyEffectCommands: + dbw EFFECTCMDTYPE_INITIAL_EFFECT_1, TransparencyEffect + db $00 + +HaunterNightmareEffectCommands: + dbw EFFECTCMDTYPE_BEFORE_DAMAGE, SleepEffect + db $00 + +HypnoProphecyEffectCommands: + dbw EFFECTCMDTYPE_INITIAL_EFFECT_1, Prophecy_CheckDeck + dbw EFFECTCMDTYPE_AFTER_DAMAGE, Prophecy_ReorderDeckEffect + dbw EFFECTCMDTYPE_REQUIRE_SELECTION, Prophecy_PlayerSelectEffect + dbw EFFECTCMDTYPE_AI_SELECTION, Prophecy_AISelectEffect + db $00 + +HypnoDarkMindEffectCommands: + dbw EFFECTCMDTYPE_AFTER_DAMAGE, HypnoDarkMind_DamageBenchEffect + dbw EFFECTCMDTYPE_REQUIRE_SELECTION, HypnoDarkMind_PlayerSelectEffect + dbw EFFECTCMDTYPE_AI_SELECTION, HypnoDarkMind_AISelectEffect + db $00 + +DrowzeeConfuseRayEffectCommands: + dbw EFFECTCMDTYPE_BEFORE_DAMAGE, Confusion50PercentEffect + db $00 + +MrMimeInvisibleWallEffectCommands: + dbw EFFECTCMDTYPE_INITIAL_EFFECT_1, InvisibleWallEffect + db $00 + +MrMimeMeditateEffectCommands: + dbw EFFECTCMDTYPE_BEFORE_DAMAGE, MrMimeMeditate_DamageBoostEffect + dbw EFFECTCMDTYPE_AI, MrMimeMeditate_AIEffect + db $00 + +AlakazamDamageSwapEffectCommands: + dbw EFFECTCMDTYPE_INITIAL_EFFECT_2, DamageSwap_CheckDamage + dbw EFFECTCMDTYPE_BEFORE_DAMAGE, DamageSwap_SelectAndSwapEffect + dbw EFFECTCMDTYPE_AFTER_DAMAGE, DamageSwap_SwapEffect + db $00 + +AlakazamConfuseRayEffectCommands: + dbw EFFECTCMDTYPE_BEFORE_DAMAGE, Confusion50PercentEffect + db $00 + +MewPsywaveEffectCommands: + dbw EFFECTCMDTYPE_BEFORE_DAMAGE, PsywaveEffect + db $00 + +MewDevolutionBeamEffectCommands: + dbw EFFECTCMDTYPE_INITIAL_EFFECT_1, DevolutionBeam_CheckPlayArea + dbw EFFECTCMDTYPE_INITIAL_EFFECT_2, DevolutionBeam_PlayerSelectEffect + dbw EFFECTCMDTYPE_BEFORE_DAMAGE, DevolutionBeam_LoadAnimation + dbw EFFECTCMDTYPE_AFTER_DAMAGE, DevolutionBeam_DevolveEffect + dbw EFFECTCMDTYPE_AI_SELECTION, DevolutionBeam_AISelectEffect + db $00 + +MewNeutralizingShieldEffectCommands: + dbw EFFECTCMDTYPE_INITIAL_EFFECT_1, NeutralizingShieldEffect + db $00 + +MewPsyshockEffectCommands: + dbw EFFECTCMDTYPE_BEFORE_DAMAGE, Paralysis50PercentEffect + db $00 + +MewtwoPsychicEffectCommands: + dbw EFFECTCMDTYPE_BEFORE_DAMAGE, Psychic_DamageBoostEffect + dbw EFFECTCMDTYPE_AI, Psychic_AIEffect + db $00 + +MewtwoBarrierEffectCommands: + dbw EFFECTCMDTYPE_INITIAL_EFFECT_1, Barrier_CheckEnergy + dbw EFFECTCMDTYPE_INITIAL_EFFECT_2, Barrier_PlayerSelectEffect + dbw EFFECTCMDTYPE_BEFORE_DAMAGE, Barrier_BarrierEffect + dbw EFFECTCMDTYPE_DISCARD_ENERGY, Barrier_DiscardEffect + dbw EFFECTCMDTYPE_AI_SELECTION, Barrier_AISelectEffect + db $00 + +Mewtwo3EnergyAbsorptionEffectCommands: + dbw EFFECTCMDTYPE_INITIAL_EFFECT_1, Mewtwo3EnergyAbsorption_CheckDiscardPile + dbw EFFECTCMDTYPE_AFTER_DAMAGE, Mewtwo3EnergyAbsorption_AddToHandEffect + dbw EFFECTCMDTYPE_REQUIRE_SELECTION, Mewtwo3EnergyAbsorption_PlayerSelectEffect + dbw EFFECTCMDTYPE_AI_SELECTION, Mewtwo3EnergyAbsorption_AISelectEffect + db $00 + +Mewtwo2EnergyAbsorptionEffectCommands: + dbw EFFECTCMDTYPE_INITIAL_EFFECT_1, Mewtwo2EnergyAbsorption_CheckDiscardPile + dbw EFFECTCMDTYPE_AFTER_DAMAGE, Mewtwo2EnergyAbsorption_AddToHandEffect + dbw EFFECTCMDTYPE_REQUIRE_SELECTION, Mewtwo2EnergyAbsorption_PlayerSelectEffect + dbw EFFECTCMDTYPE_AI_SELECTION, Mewtwo2EnergyAbsorption_AISelectEffect + db $00 + +SlowbroStrangeBehaviorEffectCommands: + dbw EFFECTCMDTYPE_INITIAL_EFFECT_2, StrangeBehavior_CheckDamage + dbw EFFECTCMDTYPE_BEFORE_DAMAGE, StrangeBehavior_SelectAndSwapEffect + dbw EFFECTCMDTYPE_AFTER_DAMAGE, StrangeBehavior_SwapEffect + db $00 + +SlowbroPsyshockEffectCommands: + dbw EFFECTCMDTYPE_BEFORE_DAMAGE, Paralysis50PercentEffect + db $00 + +SlowpokeSpacingOutEffectCommands: + dbw EFFECTCMDTYPE_INITIAL_EFFECT_1, SpacingOut_CheckDamage + dbw EFFECTCMDTYPE_BEFORE_DAMAGE, SpacingOut_Success50PercentEffect + dbw EFFECTCMDTYPE_AFTER_DAMAGE, SpacingOut_HealEffect + db $00 + +SlowpokeScavengeEffectCommands: + dbw EFFECTCMDTYPE_INITIAL_EFFECT_1, Scavenge_CheckDiscardPile + dbw EFFECTCMDTYPE_INITIAL_EFFECT_2, Scavenge_PlayerSelectEnergyEffect + dbw EFFECTCMDTYPE_AFTER_DAMAGE, Scavenge_AddToHandEffect + dbw EFFECTCMDTYPE_REQUIRE_SELECTION, Scavenge_PlayerSelectTrainerEffect + dbw EFFECTCMDTYPE_DISCARD_ENERGY, Scavenge_DiscardEffect + dbw EFFECTCMDTYPE_AI_SELECTION, Scavenge_AISelectEffect + db $00 + +SlowpokeAmnesiaEffectCommands: + dbw EFFECTCMDTYPE_INITIAL_EFFECT_1, SlowpokeAmnesia_CheckAttacks + dbw EFFECTCMDTYPE_INITIAL_EFFECT_2, SlowpokeAmnesia_PlayerSelectEffect + dbw EFFECTCMDTYPE_BEFORE_DAMAGE, SlowpokeAmnesia_DisableEffect + dbw EFFECTCMDTYPE_AI_SELECTION, SlowpokeAmnesia_AISelectEffect + db $00 + +KadabraRecoverEffectCommands: + dbw EFFECTCMDTYPE_INITIAL_EFFECT_1, KadabraRecover_CheckEnergyHP + dbw EFFECTCMDTYPE_INITIAL_EFFECT_2, KadabraRecover_PlayerSelectEffect + dbw EFFECTCMDTYPE_AFTER_DAMAGE, KadabraRecover_HealEffect + dbw EFFECTCMDTYPE_DISCARD_ENERGY, KadabraRecover_DiscardEffect + dbw EFFECTCMDTYPE_AI_SELECTION, KadabraRecover_AISelectEffect + db $00 + +JynxDoubleslapEffectCommands: + dbw EFFECTCMDTYPE_BEFORE_DAMAGE, JynxDoubleslap_MultiplierEffect + dbw EFFECTCMDTYPE_AI, JynxDoubleslap_AIEffect + db $00 + +JynxMeditateEffectCommands: + dbw EFFECTCMDTYPE_BEFORE_DAMAGE, JynxMeditate_DamageBoostEffect + dbw EFFECTCMDTYPE_AI, JynxMeditate_AIEffect + db $00 + +MewMysteryAttackEffectCommands: + dbw EFFECTCMDTYPE_BEFORE_DAMAGE, MysteryAttack_RandomEffect + dbw EFFECTCMDTYPE_AFTER_DAMAGE, MysteryAttack_RecoverEffect + dbw EFFECTCMDTYPE_AI, MysteryAttack_AIEffect + db $00 + +GeodudeStoneBarrageEffectCommands: + dbw EFFECTCMDTYPE_BEFORE_DAMAGE, StoneBarrage_MultiplierEffect + dbw EFFECTCMDTYPE_AI, StoneBarrage_AIEffect + db $00 + +OnixHardenEffectCommands: + dbw EFFECTCMDTYPE_BEFORE_DAMAGE, OnixHardenEffect + db $00 + +PrimeapeFurySwipesEffectCommands: + dbw EFFECTCMDTYPE_BEFORE_DAMAGE, PrimeapeFurySwipes_MultiplierEffect + dbw EFFECTCMDTYPE_AI, PrimeapeFurySwipes_AIEffect + db $00 + +PrimeapeTantrumEffectCommands: + dbw EFFECTCMDTYPE_BEFORE_DAMAGE, TantrumEffect + db $00 + +MachampStrikesBackEffectCommands: + dbw EFFECTCMDTYPE_INITIAL_EFFECT_1, StrikesBackEffect + db $00 + +KabutoKabutoArmorEffectCommands: + dbw EFFECTCMDTYPE_INITIAL_EFFECT_1, KabutoArmorEffect + db $00 + +KabutopsAbsorbEffectCommands: + dbw EFFECTCMDTYPE_AFTER_DAMAGE, AbsorbEffect + db $00 + +CuboneSnivelEffectCommands: + dbw EFFECTCMDTYPE_BEFORE_DAMAGE, SnivelEffect + db $00 + +CuboneRageEffectCommands: + dbw EFFECTCMDTYPE_BEFORE_DAMAGE, CuboneRage_DamageBoostEffect + dbw EFFECTCMDTYPE_AI, CuboneRage_AIEffect + db $00 + +MarowakBonemerangEffectCommands: + dbw EFFECTCMDTYPE_BEFORE_DAMAGE, Bonemerang_MultiplierEffect + dbw EFFECTCMDTYPE_AI, Bonemerang_AIEffect + db $00 + +MarowakCallforFriendEffectCommands: + dbw EFFECTCMDTYPE_INITIAL_EFFECT_1, MarowakCallForFamily_CheckDeckAndPlayArea + dbw EFFECTCMDTYPE_AFTER_DAMAGE, MarowakCallForFamily_PutInPlayAreaEffect + dbw EFFECTCMDTYPE_REQUIRE_SELECTION, MarowakCallForFamily_PlayerSelectEffect + dbw EFFECTCMDTYPE_AI_SELECTION, MarowakCallForFamily_AISelectEffect + db $00 + +MachokeKarateChopEffectCommands: + dbw EFFECTCMDTYPE_BEFORE_DAMAGE, KarateChop_DamageSubtractionEffect + dbw EFFECTCMDTYPE_AI, KarateChop_AIEffect + db $00 + +MachokeSubmissionEffectCommands: + dbw EFFECTCMDTYPE_AFTER_DAMAGE, SubmissionEffect + db $00 + +GolemSelfdestructEffectCommands: + dbw EFFECTCMDTYPE_AFTER_DAMAGE, GolemSelfdestructEffect + db $00 + +GravelerHardenEffectCommands: + dbw EFFECTCMDTYPE_BEFORE_DAMAGE, GravelerHardenEffect + db $00 + +RhydonRamEffectCommands: + dbw EFFECTCMDTYPE_AFTER_DAMAGE, Ram_RecoilSwitchEffect + dbw EFFECTCMDTYPE_REQUIRE_SELECTION, Ram_SelectSwitchEffect + dbw EFFECTCMDTYPE_AI_SWITCH_DEFENDING_PKMN, Ram_SelectSwitchEffect + db $00 + +RhyhornLeerEffectCommands: + dbw EFFECTCMDTYPE_BEFORE_DAMAGE, LeerEffect + db $00 + +HitmonleeStretchKickEffectCommands: + dbw EFFECTCMDTYPE_INITIAL_EFFECT_1, StretchKick_CheckBench + dbw EFFECTCMDTYPE_AFTER_DAMAGE, StretchKick_BenchDamageEffect + dbw EFFECTCMDTYPE_REQUIRE_SELECTION, StretchKick_PlayerSelectEffect + dbw EFFECTCMDTYPE_AI_SELECTION, StretchKick_AISelectEffect + db $00 + +SandshrewSandAttackEffectCommands: + dbw EFFECTCMDTYPE_BEFORE_DAMAGE, SandAttackEffect + db $00 + +SandslashFurySwipesEffectCommands: + dbw EFFECTCMDTYPE_BEFORE_DAMAGE, SandslashFurySwipes_MultiplierEffect + dbw EFFECTCMDTYPE_AI, SandslashFurySwipes_AIEffect + db $00 + +DugtrioEarthquakeEffectCommands: + dbw EFFECTCMDTYPE_AFTER_DAMAGE, EarthquakeEffect + db $00 + +AerodactylPrehistoricPowerEffectCommands: + dbw EFFECTCMDTYPE_INITIAL_EFFECT_1, PrehistoricPowerEffect + db $00 + +MankeyPeekEffectCommands: + dbw EFFECTCMDTYPE_INITIAL_EFFECT_2, Peek_OncePerTurnCheck + dbw EFFECTCMDTYPE_BEFORE_DAMAGE, Peek_SelectEffect + db $00 + +MarowakBoneAttackEffectCommands: + dbw EFFECTCMDTYPE_BEFORE_DAMAGE, BoneAttackEffect + db $00 + +MarowakWailEffectCommands: + dbw EFFECTCMDTYPE_INITIAL_EFFECT_1, Wail_BenchCheck + dbw EFFECTCMDTYPE_AFTER_DAMAGE, Wail_FillBenchEffect + db $00 + +ElectabuzzThundershockEffectCommands: + dbw EFFECTCMDTYPE_BEFORE_DAMAGE, Paralysis50PercentEffect + db $00 + +ElectabuzzThunderpunchEffectCommands: + dbw EFFECTCMDTYPE_BEFORE_DAMAGE, Thunderpunch_ModifierEffect + dbw EFFECTCMDTYPE_AFTER_DAMAGE, Thunderpunch_RecoilEffect + dbw EFFECTCMDTYPE_AI, Thunderpunch_AIEffect + db $00 + +ElectabuzzLightScreenEffectCommands: + dbw EFFECTCMDTYPE_BEFORE_DAMAGE, LightScreenEffect + db $00 + +ElectabuzzQuickAttackEffectCommands: + dbw EFFECTCMDTYPE_BEFORE_DAMAGE, ElectabuzzQuickAttack_DamageBoostEffect + dbw EFFECTCMDTYPE_AI, ElectabuzzQuickAttack_AIEffect + db $00 + +MagnemiteThunderWaveEffectCommands: + dbw EFFECTCMDTYPE_BEFORE_DAMAGE, Paralysis50PercentEffect + db $00 + +MagnemiteSelfdestructEffectCommands: + dbw EFFECTCMDTYPE_AFTER_DAMAGE, MagnemiteSelfdestructEffect + db $00 + +ZapdosThunderEffectCommands: + dbw EFFECTCMDTYPE_BEFORE_DAMAGE, ZapdosThunder_Recoil50PercentEffect + dbw EFFECTCMDTYPE_AFTER_DAMAGE, ZapdosThunder_RecoilEffect + db $00 + +ZapdosThunderboltEffectCommands: + dbw EFFECTCMDTYPE_BEFORE_DAMAGE, ThunderboltEffect + db $00 + +ZapdosThunderstormEffectCommands: + dbw EFFECTCMDTYPE_AFTER_DAMAGE, ThunderstormEffect + db $00 + +JolteonQuickAttackEffectCommands: + dbw EFFECTCMDTYPE_BEFORE_DAMAGE, JolteonQuickAttack_DamageBoostEffect + dbw EFFECTCMDTYPE_AI, JolteonQuickAttack_AIEffect + db $00 + +JolteonPinMissileEffectCommands: + dbw EFFECTCMDTYPE_BEFORE_DAMAGE, PinMissile_MultiplierEffect + dbw EFFECTCMDTYPE_AI, PinMissile_AIEffect + db $00 + +FlyingPikachuThundershockEffectCommands: + dbw EFFECTCMDTYPE_BEFORE_DAMAGE, Paralysis50PercentEffect + db $00 + +FlyingPikachuFlyEffectCommands: + dbw EFFECTCMDTYPE_BEFORE_DAMAGE, Fly_Success50PercentEffect + dbw EFFECTCMDTYPE_AI, Fly_AIEffect + db $00 + +PikachuThunderJoltEffectCommands: + dbw EFFECTCMDTYPE_BEFORE_DAMAGE, ThunderJolt_Recoil50PercentEffect + dbw EFFECTCMDTYPE_AFTER_DAMAGE, ThunderJolt_RecoilEffect + db $00 + +PikachuSparkEffectCommands: + dbw EFFECTCMDTYPE_AFTER_DAMAGE, Spark_BenchDamageEffect + dbw EFFECTCMDTYPE_REQUIRE_SELECTION, Spark_PlayerSelectEffect + dbw EFFECTCMDTYPE_AI_SELECTION, Spark_AISelectEffect + db $00 + +Pikachu3GrowlEffectCommands: + dbw EFFECTCMDTYPE_BEFORE_DAMAGE, Pikachu3GrowlEffect + db $00 + +Pikachu3ThundershockEffectCommands: + dbw EFFECTCMDTYPE_BEFORE_DAMAGE, Paralysis50PercentEffect + db $00 + +Pikachu4GrowlEffectCommands: + dbw EFFECTCMDTYPE_BEFORE_DAMAGE, Pikachu4GrowlEffect + db $00 + +Pikachu4ThundershockEffectCommands: + dbw EFFECTCMDTYPE_BEFORE_DAMAGE, Paralysis50PercentEffect + db $00 + +ElectrodeChainLightningEffectCommands: + dbw EFFECTCMDTYPE_AFTER_DAMAGE, ChainLightningEffect + db $00 + +RaichuAgilityEffectCommands: + dbw EFFECTCMDTYPE_BEFORE_DAMAGE, RaichuAgilityEffect + db $00 + +RaichuThunderEffectCommands: + dbw EFFECTCMDTYPE_BEFORE_DAMAGE, RaichuThunder_Recoil50PercentEffect + dbw EFFECTCMDTYPE_AFTER_DAMAGE, RaichuThunder_RecoilEffect + db $00 + +RaichuGigashockEffectCommands: + dbw EFFECTCMDTYPE_AFTER_DAMAGE, Gigashock_BenchDamageEffect + dbw EFFECTCMDTYPE_REQUIRE_SELECTION, Gigashock_PlayerSelectEffect + dbw EFFECTCMDTYPE_AI_SELECTION, Gigashock_AISelectEffect + db $00 + +MagnetonThunderWaveEffectCommands: + dbw EFFECTCMDTYPE_BEFORE_DAMAGE, Paralysis50PercentEffect + db $00 + +Magneton1SelfdestructEffectCommands: + dbw EFFECTCMDTYPE_AFTER_DAMAGE, Magneton1SelfdestructEffect + db $00 + +MagnetonSonicboomEffectCommands: + dbw EFFECTCMDTYPE_BEFORE_DAMAGE, MagnetonSonicboom_UnaffectedByColorEffect + dbw EFFECTCMDTYPE_AFTER_DAMAGE, MagnetonSonicboom_NullEffect + dbw EFFECTCMDTYPE_AI, MagnetonSonicboom_UnaffectedByColorEffect + db $00 + +Magneton2SelfdestructEffectCommands: + dbw EFFECTCMDTYPE_AFTER_DAMAGE, Magneton2SelfdestructEffect + db $00 + +ZapdosPealOfThunderEffectCommands: + dbw EFFECTCMDTYPE_INITIAL_EFFECT_1, PealOfThunder_InitialEffect + dbw EFFECTCMDTYPE_PKMN_POWER_TRIGGER, PealOfThunder_RandomlyDamageEffect + db $00 + +ZapdosBigThunderEffectCommands: + dbw EFFECTCMDTYPE_AFTER_DAMAGE, BigThunderEffect + db $00 + +MagnemiteMagneticStormEffectCommands: + dbw EFFECTCMDTYPE_AFTER_DAMAGE, MagneticStormEffect + db $00 + +ElectrodeSonicboomEffectCommands: + dbw EFFECTCMDTYPE_BEFORE_DAMAGE, ElectrodeSonicboom_UnaffectedByColorEffect + dbw EFFECTCMDTYPE_AFTER_DAMAGE, ElectrodeSonicboom_NullEffect + dbw EFFECTCMDTYPE_AI, ElectrodeSonicboom_UnaffectedByColorEffect + db $00 + +ElectrodeEnergySpikeEffectCommands: + dbw EFFECTCMDTYPE_INITIAL_EFFECT_1, EnergySpike_DeckCheck + dbw EFFECTCMDTYPE_AFTER_DAMAGE, EnergySpike_AttachEnergyEffect + dbw EFFECTCMDTYPE_REQUIRE_SELECTION, EnergySpike_PlayerSelectEffect + dbw EFFECTCMDTYPE_AI_SELECTION, EnergySpike_AISelectEffect + db $00 + +JolteonDoubleKickEffectCommands: + dbw EFFECTCMDTYPE_BEFORE_DAMAGE, JolteonDoubleKick_MultiplierEffect + dbw EFFECTCMDTYPE_AI, JolteonDoubleKick_AIEffect + db $00 + +JolteonStunNeedleEffectCommands: + dbw EFFECTCMDTYPE_BEFORE_DAMAGE, Paralysis50PercentEffect + db $00 + +EeveeTailWagEffectCommands: + dbw EFFECTCMDTYPE_BEFORE_DAMAGE, TailWagEffect + db $00 + +EeveeQuickAttackEffectCommands: + dbw EFFECTCMDTYPE_BEFORE_DAMAGE, EeveeQuickAttack_DamageBoostEffect + dbw EFFECTCMDTYPE_AI, EeveeQuickAttack_AIEffect + db $00 + +SpearowMirrorMoveEffectCommands: + dbw EFFECTCMDTYPE_INITIAL_EFFECT_1, SpearowMirrorMove_InitialEffect1 + dbw EFFECTCMDTYPE_INITIAL_EFFECT_2, SpearowMirrorMove_InitialEffect2 + dbw EFFECTCMDTYPE_BEFORE_DAMAGE, SpearowMirrorMove_BeforeDamage + dbw EFFECTCMDTYPE_AFTER_DAMAGE, SpearowMirrorMove_AfterDamage + dbw EFFECTCMDTYPE_REQUIRE_SELECTION, SpearowMirrorMove_PlayerSelection + dbw EFFECTCMDTYPE_AI_SELECTION, SpearowMirrorMove_AISelection + dbw EFFECTCMDTYPE_AI, SpearowMirrorMove_AIEffect + db $00 + +FearowAgilityEffectCommands: + dbw EFFECTCMDTYPE_BEFORE_DAMAGE, FearowAgilityEffect + db $00 + +DragoniteStepInEffectCommands: + dbw EFFECTCMDTYPE_INITIAL_EFFECT_2, StepIn_BenchCheck + dbw EFFECTCMDTYPE_BEFORE_DAMAGE, StepIn_SwitchEffect + db $00 + +Dragonite2SlamEffectCommands: + dbw EFFECTCMDTYPE_BEFORE_DAMAGE, Dragonite2Slam_MultiplierEffect + dbw EFFECTCMDTYPE_AI, Dragonite2Slam_AIEffect + db $00 + +SnorlaxThickSkinnedEffectCommands: + dbw EFFECTCMDTYPE_INITIAL_EFFECT_1, ThickSkinnedEffect + db $00 + +SnorlaxBodySlamEffectCommands: + dbw EFFECTCMDTYPE_BEFORE_DAMAGE, Paralysis50PercentEffect + db $00 + +FarfetchdLeekSlapEffectCommands: + dbw EFFECTCMDTYPE_INITIAL_EFFECT_1, LeekSlap_OncePerDuelCheck + dbw EFFECTCMDTYPE_BEFORE_DAMAGE, LeekSlap_NoDamage50PercentEffect + dbw EFFECTCMDTYPE_DISCARD_ENERGY, LeekSlap_SetUsedThisDuelFlag + dbw EFFECTCMDTYPE_AI, LeekSlap_AIEffect + db $00 + +KangaskhanFetchEffectCommands: + dbw EFFECTCMDTYPE_AFTER_DAMAGE, FetchEffect + db $00 + +KangaskhanCometPunchEffectCommands: + dbw EFFECTCMDTYPE_BEFORE_DAMAGE, CometPunch_MultiplierEffect + dbw EFFECTCMDTYPE_AI, CometPunch_AIEffect + db $00 + +TaurosStompEffectCommands: + dbw EFFECTCMDTYPE_BEFORE_DAMAGE, TaurosStomp_DamageBoostEffect + dbw EFFECTCMDTYPE_AI, TaurosStomp_AIEffect + db $00 + +TaurosRampageEffectCommands: + dbw EFFECTCMDTYPE_BEFORE_DAMAGE, Rampage_Confusion50PercentEffect + dbw EFFECTCMDTYPE_AI, Rampage_AIEffect + db $00 + +DoduoFuryAttackEffectCommands: + dbw EFFECTCMDTYPE_BEFORE_DAMAGE, FuryAttack_MultiplierEffect + dbw EFFECTCMDTYPE_AI, FuryAttack_AIEffect + db $00 + +DodrioRetreatAidEffectCommands: + dbw EFFECTCMDTYPE_INITIAL_EFFECT_1, RetreatAidEffect + db $00 + +DodrioRageEffectCommands: + dbw EFFECTCMDTYPE_BEFORE_DAMAGE, DodrioRage_DamageBoostEffect + dbw EFFECTCMDTYPE_AI, DodrioRage_AIEffect + db $00 + +MeowthPayDayEffectCommands: + dbw EFFECTCMDTYPE_AFTER_DAMAGE, PayDayEffect + db $00 + +DragonairSlamEffectCommands: + dbw EFFECTCMDTYPE_BEFORE_DAMAGE, DragonairSlam_MultiplierEffect + dbw EFFECTCMDTYPE_AI, DragonairSlam_AIEffect + db $00 + +DragonairHyperBeamEffectCommands: + dbw EFFECTCMDTYPE_AFTER_DAMAGE, DragonairHyperBeam_DiscardEffect + dbw EFFECTCMDTYPE_REQUIRE_SELECTION, DragonairHyperBeam_PlayerSelectEffect + dbw EFFECTCMDTYPE_AI_SELECTION, DragonairHyperBeam_AISelectEffect + db $00 + +ClefableMetronomeEffectCommands: + dbw EFFECTCMDTYPE_INITIAL_EFFECT_1, ClefableMetronome_CheckAttacks + dbw EFFECTCMDTYPE_INITIAL_EFFECT_2, ClefableMetronome_UseAttackEffect + dbw EFFECTCMDTYPE_AI_SELECTION, ClefableMetronome_AISelectEffect + db $00 + +ClefableMinimizeEffectCommands: + dbw EFFECTCMDTYPE_BEFORE_DAMAGE, ClefableMinimizeEffect + db $00 + +PidgeotHurricaneEffectCommands: + dbw EFFECTCMDTYPE_AFTER_DAMAGE, HurricaneEffect + db $00 + +PidgeottoWhirlwindEffectCommands: + dbw EFFECTCMDTYPE_AFTER_DAMAGE, PidgeottoWhirlwind_SwitchEffect + dbw EFFECTCMDTYPE_REQUIRE_SELECTION, PidgeottoWhirlwind_SelectEffect + dbw EFFECTCMDTYPE_AI_SWITCH_DEFENDING_PKMN, PidgeottoWhirlwind_SelectEffect + db $00 + +PidgeottoMirrorMoveEffectCommands: + dbw EFFECTCMDTYPE_INITIAL_EFFECT_1, PidgeottoMirrorMove_InitialEffect1 + dbw EFFECTCMDTYPE_INITIAL_EFFECT_2, PidgeottoMirrorMove_InitialEffect2 + dbw EFFECTCMDTYPE_BEFORE_DAMAGE, PidgeottoMirrorMove_BeforeDamage + dbw EFFECTCMDTYPE_AFTER_DAMAGE, PidgeottoMirrorMove_AfterDamage + dbw EFFECTCMDTYPE_REQUIRE_SELECTION, PidgeottoMirrorMove_PlayerSelection + dbw EFFECTCMDTYPE_AI_SELECTION, PidgeottoMirrorMove_AISelection + dbw EFFECTCMDTYPE_AI, PidgeottoMirrorMove_AIEffect + db $00 + +ClefairySingEffectCommands: + dbw EFFECTCMDTYPE_BEFORE_DAMAGE, SingEffect + db $00 + +ClefairyMetronomeEffectCommands: + dbw EFFECTCMDTYPE_INITIAL_EFFECT_1, ClefairyMetronome_CheckAttacks + dbw EFFECTCMDTYPE_INITIAL_EFFECT_2, ClefairyMetronome_UseAttackEffect + dbw EFFECTCMDTYPE_AI_SELECTION, ClefairyMetronome_AISelectEffect + db $00 + +WigglytuffLullabyEffectCommands: + dbw EFFECTCMDTYPE_BEFORE_DAMAGE, SleepEffect + db $00 + +WigglytuffDoTheWaveEffectCommands: + dbw EFFECTCMDTYPE_BEFORE_DAMAGE, DoTheWaveEffect + dbw EFFECTCMDTYPE_AI, DoTheWaveEffect + db $00 + +JigglypuffLullabyEffectCommands: + dbw EFFECTCMDTYPE_BEFORE_DAMAGE, SleepEffect + db $00 + +JigglypuffFirstAidEffectCommands: + dbw EFFECTCMDTYPE_INITIAL_EFFECT_1, FirstAid_DamageCheck + dbw EFFECTCMDTYPE_AFTER_DAMAGE, FirstAid_HealEffect + db $00 + +JigglypuffDoubleEdgeEffectCommands: + dbw EFFECTCMDTYPE_AFTER_DAMAGE, JigglypuffDoubleEdgeEffect + db $00 + +PersianPounceEffectCommands: + dbw EFFECTCMDTYPE_BEFORE_DAMAGE, PounceEffect + db $00 + +LickitungTongueWrapEffectCommands: + dbw EFFECTCMDTYPE_BEFORE_DAMAGE, Paralysis50PercentEffect + db $00 + +LickitungSupersonicEffectCommands: + dbw EFFECTCMDTYPE_BEFORE_DAMAGE, LickitungSupersonicEffect + db $00 + +PidgeyWhirlwindEffectCommands: + dbw EFFECTCMDTYPE_AFTER_DAMAGE, PidgeyWhirlwind_SwitchEffect + dbw EFFECTCMDTYPE_REQUIRE_SELECTION, PidgeyWhirlwind_SelectEffect + dbw EFFECTCMDTYPE_AI_SWITCH_DEFENDING_PKMN, PidgeyWhirlwind_SelectEffect + db $00 + +PorygonConversion1EffectCommands: + dbw EFFECTCMDTYPE_INITIAL_EFFECT_1, Conversion1_WeaknessCheck + dbw EFFECTCMDTYPE_INITIAL_EFFECT_2, Conversion1_PlayerSelectEffect + dbw EFFECTCMDTYPE_AFTER_DAMAGE, Conversion1_ChangeWeaknessEffect + dbw EFFECTCMDTYPE_AI_SELECTION, Conversion1_AISelectEffect + db $00 + +PorygonConversion2EffectCommands: + dbw EFFECTCMDTYPE_INITIAL_EFFECT_1, Conversion2_ResistanceCheck + dbw EFFECTCMDTYPE_INITIAL_EFFECT_2, Conversion2_PlayerSelectEffect + dbw EFFECTCMDTYPE_AFTER_DAMAGE, Conversion2_ChangeResistanceEffect + dbw EFFECTCMDTYPE_AI_SELECTION, Conversion2_AISelectEffect + db $00 + +ChanseyScrunchEffectCommands: + dbw EFFECTCMDTYPE_BEFORE_DAMAGE, ScrunchEffect + db $00 + +ChanseyDoubleEdgeEffectCommands: + dbw EFFECTCMDTYPE_AFTER_DAMAGE, ChanseyDoubleEdgeEffect + db $00 + +RaticateSuperFangEffectCommands: + dbw EFFECTCMDTYPE_BEFORE_DAMAGE, SuperFang_HalfHPEffect + dbw EFFECTCMDTYPE_AI, SuperFang_AIEffect + db $00 + +TrainerCardAsPokemonEffectCommands: + dbw EFFECTCMDTYPE_INITIAL_EFFECT_2, TrainerCardAsPokemon_BenchCheck + dbw EFFECTCMDTYPE_BEFORE_DAMAGE, TrainerCardAsPokemon_DiscardEffect + dbw EFFECTCMDTYPE_REQUIRE_SELECTION, TrainerCardAsPokemon_PlayerSelectSwitch + db $00 + +DragoniteHealingWindEffectCommands: + dbw EFFECTCMDTYPE_INITIAL_EFFECT_1, HealingWind_InitialEffect + dbw EFFECTCMDTYPE_PKMN_POWER_TRIGGER, HealingWind_PlayAreaHealEffect + db $00 + +Dragonite1SlamEffectCommands: + dbw EFFECTCMDTYPE_BEFORE_DAMAGE, Dragonite1Slam_MultiplierEffect + dbw EFFECTCMDTYPE_AI, Dragonite1Slam_AIEffect + db $00 + +MeowthCatPunchEffectCommands: + dbw EFFECTCMDTYPE_AFTER_DAMAGE, CatPunchEffect + db $00 + +DittoMorphEffectCommands: + dbw EFFECTCMDTYPE_AFTER_DAMAGE, MorphEffect + db $00 + +PidgeotSlicingWindEffectCommands: + dbw EFFECTCMDTYPE_AFTER_DAMAGE, SlicingWindEffect + db $00 + +PidgeotGaleEffectCommands: + dbw EFFECTCMDTYPE_BEFORE_DAMAGE, Gale_LoadAnimation + dbw EFFECTCMDTYPE_AFTER_DAMAGE, Gale_SwitchEffect + db $00 + +JigglypuffFriendshipSongEffectCommands: + dbw EFFECTCMDTYPE_INITIAL_EFFECT_1, FriendshipSong_BenchCheck + dbw EFFECTCMDTYPE_AFTER_DAMAGE, FriendshipSong_AddToBench50PercentEffect + db $00 + +JigglypuffExpandEffectCommands: + dbw EFFECTCMDTYPE_AFTER_DAMAGE, ExpandEffect + db $00 + +DoubleColorlessEnergyEffectCommands: + db $00 + +PsychicEnergyEffectCommands: + db $00 + +FightingEnergyEffectCommands: + db $00 + +LightningEnergyEffectCommands: + db $00 + +WaterEnergyEffectCommands: + db $00 + +FireEnergyEffectCommands: + db $00 + +GrassEnergyEffectCommands: + db $00 + +SuperPotionEffectCommands: + dbw EFFECTCMDTYPE_INITIAL_EFFECT_1, SuperPotion_DamageEnergyCheck + dbw EFFECTCMDTYPE_INITIAL_EFFECT_2, SuperPotion_PlayerSelectEffect + dbw EFFECTCMDTYPE_BEFORE_DAMAGE, SuperPotion_HealEffect + db $00 + +ImakuniEffectCommands: + dbw EFFECTCMDTYPE_BEFORE_DAMAGE, ImakuniEffect + db $00 + +EnergyRemovalEffectCommands: + dbw EFFECTCMDTYPE_INITIAL_EFFECT_1, EnergyRemoval_EnergyCheck + dbw EFFECTCMDTYPE_INITIAL_EFFECT_2, EnergyRemoval_PlayerSelection + dbw EFFECTCMDTYPE_BEFORE_DAMAGE, EnergyRemoval_DiscardEffect + dbw EFFECTCMDTYPE_AI_SELECTION, EnergyRemoval_AISelection + db $00 + +EnergyRetrievalEffectCommands: + dbw EFFECTCMDTYPE_INITIAL_EFFECT_1, EnergyRetrieval_HandEnergyCheck + dbw EFFECTCMDTYPE_INITIAL_EFFECT_2, EnergyRetrieval_PlayerHandSelection + dbw EFFECTCMDTYPE_BEFORE_DAMAGE, EnergyRetrieval_DiscardAndAddToHandEffect + dbw EFFECTCMDTYPE_REQUIRE_SELECTION, EnergyRetrieval_PlayerDiscardPileSelection + db $00 + +EnergySearchEffectCommands: + dbw EFFECTCMDTYPE_INITIAL_EFFECT_1, EnergySearch_DeckCheck + dbw EFFECTCMDTYPE_BEFORE_DAMAGE, EnergySearch_AddToHandEffect + dbw EFFECTCMDTYPE_REQUIRE_SELECTION, EnergySearch_PlayerSelection + db $00 + +ProfessorOakEffectCommands: + dbw EFFECTCMDTYPE_BEFORE_DAMAGE, ProfessorOakEffect + db $00 + +PotionEffectCommands: + dbw EFFECTCMDTYPE_INITIAL_EFFECT_1, Potion_DamageCheck + dbw EFFECTCMDTYPE_INITIAL_EFFECT_2, Potion_PlayerSelection + dbw EFFECTCMDTYPE_BEFORE_DAMAGE, Potion_HealEffect + db $00 + +GamblerEffectCommands: + dbw EFFECTCMDTYPE_BEFORE_DAMAGE, GamblerEffect + db $00 + +ItemFinderEffectCommands: + dbw EFFECTCMDTYPE_INITIAL_EFFECT_1, ItemFinder_HandDiscardPileCheck + dbw EFFECTCMDTYPE_INITIAL_EFFECT_2, ItemFinder_PlayerSelection + dbw EFFECTCMDTYPE_BEFORE_DAMAGE, ItemFinder_DiscardAddToHandEffect + db $00 + +DefenderEffectCommands: + dbw EFFECTCMDTYPE_INITIAL_EFFECT_2, Defender_PlayerSelection + dbw EFFECTCMDTYPE_BEFORE_DAMAGE, Defender_AttachDefenderEffect + db $00 + +MysteriousFossilEffectCommands: + dbw EFFECTCMDTYPE_INITIAL_EFFECT_1, MysteriousFossil_BenchCheck + dbw EFFECTCMDTYPE_BEFORE_DAMAGE, MysteriousFossil_PlaceInPlayAreaEffect + db $00 + +FullHealEffectCommands: + dbw EFFECTCMDTYPE_INITIAL_EFFECT_1, FullHeal_StatusCheck + dbw EFFECTCMDTYPE_BEFORE_DAMAGE, FullHeal_ClearStatusEffect + db $00 + +ImposterProfessorOakEffectCommands: + dbw EFFECTCMDTYPE_BEFORE_DAMAGE, ImposterProfessorOakEffect + db $00 + +ComputerSearchEffectCommands: + dbw EFFECTCMDTYPE_INITIAL_EFFECT_1, ComputerSearch_HandDeckCheck + dbw EFFECTCMDTYPE_INITIAL_EFFECT_2, ComputerSearch_PlayerDiscardHandSelection + dbw EFFECTCMDTYPE_BEFORE_DAMAGE, ComputerSearch_DiscardAddToHandEffect + dbw EFFECTCMDTYPE_REQUIRE_SELECTION, ComputerSearch_PlayerDeckSelection + db $00 + +ClefairyDollEffectCommands: + dbw EFFECTCMDTYPE_INITIAL_EFFECT_1, ClefairyDoll_BenchCheck + dbw EFFECTCMDTYPE_BEFORE_DAMAGE, ClefairyDoll_PlaceInPlayAreaEffect + db $00 + +MrFujiEffectCommands: + dbw EFFECTCMDTYPE_INITIAL_EFFECT_1, MrFuji_BenchCheck + dbw EFFECTCMDTYPE_INITIAL_EFFECT_2, MrFuji_PlayerSelection + dbw EFFECTCMDTYPE_BEFORE_DAMAGE, MrFuji_ReturnToDeckEffect + db $00 + +PlusPowerEffectCommands: + dbw EFFECTCMDTYPE_BEFORE_DAMAGE, PlusPowerEffect + db $00 + +SwitchEffectCommands: + dbw EFFECTCMDTYPE_INITIAL_EFFECT_1, Switch_BenchCheck + dbw EFFECTCMDTYPE_INITIAL_EFFECT_2, Switch_PlayerSelection + dbw EFFECTCMDTYPE_BEFORE_DAMAGE, Switch_SwitchEffect + db $00 + +PokemonCenterEffectCommands: + dbw EFFECTCMDTYPE_INITIAL_EFFECT_1, PokemonCenter_DamageCheck + dbw EFFECTCMDTYPE_BEFORE_DAMAGE, PokemonCenter_HealDiscardEnergyEffect + db $00 + +PokemonFluteEffectCommands: + dbw EFFECTCMDTYPE_INITIAL_EFFECT_1, PokemonFlute_BenchCheck + dbw EFFECTCMDTYPE_INITIAL_EFFECT_2, PokemonFlute_PlayerSelection + dbw EFFECTCMDTYPE_BEFORE_DAMAGE, PokemonFlute_PlaceInPlayAreaText + db $00 + +PokemonBreederEffectCommands: + dbw EFFECTCMDTYPE_INITIAL_EFFECT_1, PokemonBreeder_HandPlayAreaCheck + dbw EFFECTCMDTYPE_INITIAL_EFFECT_2, PokemonBreeder_PlayerSelection + dbw EFFECTCMDTYPE_BEFORE_DAMAGE, PokemonBreeder_EvolveEffect + db $00 + +ScoopUpEffectCommands: + dbw EFFECTCMDTYPE_INITIAL_EFFECT_1, ScoopUp_BenchCheck + dbw EFFECTCMDTYPE_INITIAL_EFFECT_2, ScoopUp_PlayerSelection + dbw EFFECTCMDTYPE_BEFORE_DAMAGE, ScoopUp_ReturnToHandEffect + db $00 + +PokemonTraderEffectCommands: + dbw EFFECTCMDTYPE_INITIAL_EFFECT_1, PokemonTrader_HandDeckCheck + dbw EFFECTCMDTYPE_INITIAL_EFFECT_2, PokemonTrader_PlayerHandSelection + dbw EFFECTCMDTYPE_BEFORE_DAMAGE, PokemonTrader_TradeCardsEffect + dbw EFFECTCMDTYPE_REQUIRE_SELECTION, PokemonTrader_PlayerDeckSelection + db $00 + +PokedexEffectCommands: + dbw EFFECTCMDTYPE_INITIAL_EFFECT_1, Pokedex_DeckCheck + dbw EFFECTCMDTYPE_BEFORE_DAMAGE, Pokedex_OrderDeckCardsEffect + dbw EFFECTCMDTYPE_REQUIRE_SELECTION, Pokedex_PlayerSelection + db $00 + +BillEffectCommands: + dbw EFFECTCMDTYPE_BEFORE_DAMAGE, BillEffect + db $00 + +LassEffectCommands: + dbw EFFECTCMDTYPE_BEFORE_DAMAGE, LassEffect + db $00 + +MaintenanceEffectCommands: + dbw EFFECTCMDTYPE_INITIAL_EFFECT_1, Maintenance_HandCheck + dbw EFFECTCMDTYPE_INITIAL_EFFECT_2, Maintenance_PlayerSelection + dbw EFFECTCMDTYPE_BEFORE_DAMAGE, Maintenance_ReturnToDeckAndDrawEffect + db $00 + +PokeBallEffectCommands: + dbw EFFECTCMDTYPE_INITIAL_EFFECT_1, PokeBall_DeckCheck + dbw EFFECTCMDTYPE_BEFORE_DAMAGE, PokeBall_AddToHandEffect + dbw EFFECTCMDTYPE_REQUIRE_SELECTION, PokeBall_PlayerSelection + db $00 + +RecycleEffectCommands: + dbw EFFECTCMDTYPE_INITIAL_EFFECT_1, Recycle_DiscardPileCheck + dbw EFFECTCMDTYPE_BEFORE_DAMAGE, Recycle_AddToHandEffect + dbw EFFECTCMDTYPE_REQUIRE_SELECTION, Recycle_PlayerSelection + db $00 + +ReviveEffectCommands: + dbw EFFECTCMDTYPE_INITIAL_EFFECT_1, Revive_BenchCheck + dbw EFFECTCMDTYPE_INITIAL_EFFECT_2, Revive_PlayerSelection + dbw EFFECTCMDTYPE_BEFORE_DAMAGE, Revive_PlaceInPlayAreaEffect + db $00 + +DevolutionSprayEffectCommands: + dbw EFFECTCMDTYPE_INITIAL_EFFECT_1, DevolutionSpray_PlayAreaEvolutionCheck + dbw EFFECTCMDTYPE_INITIAL_EFFECT_2, DevolutionSpray_PlayerSelection + dbw EFFECTCMDTYPE_BEFORE_DAMAGE, DevolutionSpray_DevolutionEffect + db $00 + +SuperEnergyRemovalEffectCommands: + dbw EFFECTCMDTYPE_INITIAL_EFFECT_1, SuperEnergyRemoval_EnergyCheck + dbw EFFECTCMDTYPE_INITIAL_EFFECT_2, SuperEnergyRemoval_PlayerSelection + dbw EFFECTCMDTYPE_BEFORE_DAMAGE, SuperEnergyRemoval_DiscardEffect + db $00 + +SuperEnergyRetrievalEffectCommands: + dbw EFFECTCMDTYPE_INITIAL_EFFECT_1, SuperEnergyRetrieval_HandEnergyCheck + dbw EFFECTCMDTYPE_INITIAL_EFFECT_2, SuperEnergyRetrieval_PlayerHandSelection + dbw EFFECTCMDTYPE_BEFORE_DAMAGE, SuperEnergyRetrieval_DiscardAndAddToHandEffect + dbw EFFECTCMDTYPE_REQUIRE_SELECTION, SuperEnergyRetrieval_PlayerDiscardPileSelection + db $00 + +GustOfWindEffectCommands: + dbw EFFECTCMDTYPE_INITIAL_EFFECT_1, GustOfWind_BenchCheck + dbw EFFECTCMDTYPE_INITIAL_EFFECT_2, GustOfWind_PlayerSelection + dbw EFFECTCMDTYPE_BEFORE_DAMAGE, GustOfWind_SwitchEffect + db $00 diff --git a/src/engine/game_loop.asm b/src/engine/game_loop.asm index c3745b6..d6e4fab 100644 --- a/src/engine/game_loop.asm +++ b/src/engine/game_loop.asm @@ -15,7 +15,7 @@ GameLoop: ld a, 1 ld [wUppercaseHalfWidthLetters], a ei - farcall CommentedOut_1a6cc + farcall StubbedUnusedSaveDataValidation ldh a, [hKeysHeld] cp A_BUTTON | B_BUTTON jr z, .ask_erase_backup_ram diff --git a/src/engine/input_name.asm b/src/engine/input_name.asm new file mode 100644 index 0000000..c9d222e --- /dev/null +++ b/src/engine/input_name.asm @@ -0,0 +1,1417 @@ +WhatIsYourNameData: + textitem 1, 1, WhatIsYourNameText + db $ff +; [Deck1Data ~ Deck4Data] +; These are directed from around (2:4f05), +; without any bank description. +; That is, the developers hard-coded it. -_-;; +Deck1Data: + textitem 2, 1, Deck1Text + textitem 14, 1, DeckText + db $ff +Deck2Data: + textitem 2, 1, Deck2Text + textitem 14, 1, DeckText + db $ff +Deck3Data: + textitem 2, 1, Deck3Text + textitem 14, 1, DeckText + db $ff +Deck4Data: + textitem 2, 1, Deck4Text + textitem 14, 1, DeckText + db $ff + +; set each byte zero from hl for b bytes. +ClearMemory: + push af + push bc + push hl + ld b, a + xor a +.loop + ld [hli], a + dec b + jr nz, .loop + pop hl + pop bc + pop af + ret + +; play different sfx by a. +; if a is 0xff play SFX_03 (usually following a B press), +; else play SFX_02 (usually following an A press). +PlayAcceptOrDeclineSFX: + push af + inc a + jr z, .sfx_decline + ld a, SFX_02 + jr .sfx_accept +.sfx_decline + ld a, SFX_03 +.sfx_accept + call PlaySFX + pop af + ret + +; get player name from the user +; into hl +InputPlayerName: + ld e, l + ld d, h + ld a, MAX_PLAYER_NAME_LENGTH + ld hl, WhatIsYourNameData + lb bc, 12, 1 + call InitializeInputName + call Set_OBJ_8x8 + xor a + ld [wTileMapFill], a + call EmptyScreen + call ZeroObjectPositions + ld a, $01 + ld [wVBlankOAMCopyToggle], a + call LoadSymbolsFont + lb de, $38, $bf + call SetupText + call LoadTextCursorTile + ld a, $02 + ld [wd009], a + call DrawNamingScreenBG + xor a + ld [wNamingScreenCursorX], a + ld [wNamingScreenCursorY], a + ld a, $09 + ld [wNamingScreenNumColumns], a + ld a, $06 + ld [wNamingScreenKeyboardHeight], a + ld a, $0f + ld [wVisibleCursorTile], a + ld a, $00 + ld [wInvisibleCursorTile], a +.loop + ld a, $01 + ld [wVBlankOAMCopyToggle], a + call DoFrame + call UpdateRNGSources + ldh a, [hDPadHeld] + and START + jr z, .else + ; if pressed start button. + ld a, $01 + call PlayAcceptOrDeclineSFX + call Func_1aa07 + ld a, 6 + ld [wNamingScreenCursorX], a + ld a, 5 + ld [wNamingScreenCursorY], a + call Func_1aa23 + jr .loop +.else + call NamingScreen_CheckButtonState + jr nc, .loop ; if not pressed, go back to the loop. + cp $ff + jr z, .on_b_button + ; on A button. + call NamingScreen_ProcessInput + jr nc, .loop + ; if the player selected the end button, + ; end its naming. + call FinalizeInputName + ret +.on_b_button + ld a, [wNamingScreenBufferLength] + or a + jr z, .loop ; empty string? + ; erase one character. + ld e, a + ld d, 0 + ld hl, wNamingScreenBuffer + add hl, de + dec hl + dec hl + ld [hl], TX_END + ld hl, wNamingScreenBufferLength ; note that its unit is byte, not word. + dec [hl] + dec [hl] + call PrintPlayerNameFromInput + jr .loop + +; it's called when naming(either player's or deck's) starts. +; a: maximum length of name(depending on whether player's or deck's). +; bc: position of name. +; de: dest. pointer. +; hl: pointer to text item of the question. +InitializeInputName: + ld [wNamingScreenBufferMaxLength], a + push hl + ld hl, wNamingScreenNamePosition + ld [hl], b + inc hl + ld [hl], c + pop hl + ld b, h + ld c, l + ; set the question string. + ld hl, wNamingScreenQuestionPointer + ld [hl], c + inc hl + ld [hl], b + ; set the destination buffer. + ld hl, wNamingScreenDestPointer + ld [hl], e + inc hl + ld [hl], d + ; clear the name buffer. + ld a, NAMING_SCREEN_BUFFER_LENGTH + ld hl, wNamingScreenBuffer + call ClearMemory + ld hl, wNamingScreenBuffer + ld a, [wNamingScreenBufferMaxLength] + ld b, a + inc b +.loop + ; copy data from de to hl + ; for b bytes. + ld a, [de] + inc de + ld [hli], a + dec b + jr nz, .loop + ld hl, wNamingScreenBuffer + call GetTextLengthInTiles + ld a, c + ld [wNamingScreenBufferLength], a + ret + +FinalizeInputName: + ld hl, wNamingScreenDestPointer + ld e, [hl] + inc hl + ld d, [hl] + ld l, e + ld h, d + ld de, wNamingScreenBuffer + ld a, [wNamingScreenBufferMaxLength] + ld b, a + inc b + jr InitializeInputName.loop + +; draws the keyboard frame +; and the question if it exists. +DrawNamingScreenBG: + call DrawTextboxForKeyboard + call PrintPlayerNameFromInput + ld hl, wNamingScreenQuestionPointer + ld c, [hl] + inc hl + ld a, [hl] + ld h, a + or c + jr z, .put_text_end + ; print the question string. + ; ex) "What is your name?" + ld l, c + call PlaceTextItems +.put_text_end + ; print "End". + ld hl, .data + call PlaceTextItems + ldtx hl, PlayerNameKeyboardText + lb de, 2, 4 + call InitTextPrinting + call ProcessTextFromID + call EnableLCD + ret +.data + textitem $0f, $10, EndText ; "End" + db $ff + +DrawTextboxForKeyboard: + lb de, 0, 3 ; x, y + lb bc, 20, 15 ; w, h + call DrawRegularTextBox + ret + +PrintPlayerNameFromInput: + ld hl, wNamingScreenNamePosition + ld d, [hl] + inc hl + ld e, [hl] + push de + call InitTextPrinting + ld a, [wNamingScreenBufferMaxLength] + ld e, a + ld a, $14 + sub e + inc a + ld e, a + ld d, 0 + ; print the underbars + ; before print the input. + ld hl, .char_underbar + add hl, de + call ProcessText + pop de + call InitTextPrinting + ; print the input from the user. + ld hl, wNamingScreenBuffer + call ProcessText + ret +.char_underbar + db $56 +rept 10 + textfw3 "_" +endr + done + +; check if button pressed. +; if pressed, set the carry bit on. +NamingScreen_CheckButtonState: + xor a + ld [wPlaysSfx], a + ldh a, [hDPadHeld] + or a + jp z, .no_press + ; detected any button press. + ld b, a + ld a, [wNamingScreenKeyboardHeight] + ld c, a + ld a, [wNamingScreenCursorX] + ld h, a + ld a, [wNamingScreenCursorY] + ld l, a + bit D_UP_F, b + jr z, .asm_692c + ; up + dec a + bit D_DOWN_F, a + jr z, .asm_69a7 + ld a, c + dec a + jr .asm_69a7 +.asm_692c + bit D_DOWN_F, b + jr z, .asm_6937 + ; down + inc a + cp c + jr c, .asm_69a7 + xor a + jr .asm_69a7 +.asm_6937 + ld a, [wNamingScreenNumColumns] + ld c, a + ld a, h + bit D_LEFT_F, b + jr z, .asm_6974 + ; left + ld d, a + ld a, $06 + cp l + ld a, d + jr nz, .asm_696b + push hl + push bc + push af + call GetCharInfoFromPos_Player + inc hl + inc hl + inc hl + inc hl + inc hl + ld a, [hl] + dec a + ld d, a + pop af + pop bc + pop hl + sub d + cp $ff + jr nz, .asm_6962 + ld a, c + sub $02 + jr .asm_69aa +.asm_6962 + cp $fe + jr nz, .asm_696b + ld a, c + sub $03 + jr .asm_69aa +.asm_696b + dec a + bit D_DOWN_F, a + jr z, .asm_69aa + ld a, c + dec a + jr .asm_69aa +.asm_6974 + bit D_RIGHT_F, b + jr z, .no_press + ld d, a + ld a, $06 + cp l + ld a, d + jr nz, .asm_6990 + push hl + push bc + push af + call GetCharInfoFromPos_Player + inc hl + inc hl + inc hl + inc hl + ld a, [hl] + dec a + ld d, a + pop af + pop bc + pop hl + add d +.asm_6990 + inc a + cp c + jr c, .asm_69aa + inc c + cp c + jr c, .asm_69a4 + inc c + cp c + jr c, .asm_69a0 + ld a, $02 + jr .asm_69aa +.asm_69a0 + ld a, $01 + jr .asm_69aa +.asm_69a4 + xor a + jr .asm_69aa +.asm_69a7 + ld l, a + jr .asm_69ab +.asm_69aa + ld h, a +.asm_69ab + push hl + call GetCharInfoFromPos_Player + inc hl + inc hl + inc hl + ld a, [wd009] + cp $02 + jr nz, .asm_69bb + inc hl + inc hl +.asm_69bb + ld d, [hl] + push de + call Func_1aa07 + pop de + pop hl + ld a, l + ld [wNamingScreenCursorY], a + ld a, h + ld [wNamingScreenCursorX], a + xor a + ld [wCheckMenuCursorBlinkCounter], a + ld a, $06 + cp d + jp z, NamingScreen_CheckButtonState + ld a, $01 + ld [wPlaysSfx], a +.no_press + ldh a, [hKeysPressed] + and A_BUTTON | B_BUTTON + jr z, .asm_69ef + and A_BUTTON + jr nz, .asm_69e5 + ld a, $ff +.asm_69e5 + call PlayAcceptOrDeclineSFX + push af + call Func_1aa23 + pop af + scf + ret +.asm_69ef + ld a, [wPlaysSfx] + or a + jr z, .asm_69f8 + call PlaySFX +.asm_69f8 + ld hl, wCheckMenuCursorBlinkCounter + ld a, [hl] + inc [hl] + and $0f + ret nz + ld a, [wVisibleCursorTile] + bit 4, [hl] + jr z, Func_1aa07.asm_6a0a + +Func_1aa07: + ld a, [wInvisibleCursorTile] +.asm_6a0a + ld e, a + ld a, [wNamingScreenCursorX] + ld h, a + ld a, [wNamingScreenCursorY] + ld l, a + call GetCharInfoFromPos_Player + ld a, [hli] + ld c, a + ld b, [hl] + dec b + ld a, e + call Func_1aa28 + call WriteByteToBGMap0 + or a + ret + +Func_1aa23: + ld a, [wVisibleCursorTile] + jr Func_1aa07.asm_6a0a + +Func_1aa28: + push af + push bc + push de + push hl + push af + call ZeroObjectPositions + pop af + ld b, a + ld a, [wInvisibleCursorTile] + cp b + jr z, .asm_6a60 + ld a, [wNamingScreenBufferLength] + srl a + ld d, a + ld a, [wNamingScreenBufferMaxLength] + srl a + ld e, a + ld a, d + cp e + jr nz, .asm_6a49 + dec a +.asm_6a49 + ld hl, wNamingScreenNamePosition + add [hl] + ld d, a + ld h, $08 + ld l, d + call HtimesL + ld a, l + add $08 + ld d, a + ld e, $18 + ld bc, $0000 + call SetOneObjectAttributes +.asm_6a60 + pop hl + pop de + pop bc + pop af + ret + +; load, to the first tile of v0Tiles0, the graphics for the +; blinking black square used in name input screens. +; for inputting full width text. +LoadTextCursorTile: + ld hl, v0Tiles0 + $00 tiles + ld de, .data + ld b, 0 +.loop + ld a, TILE_SIZE + cp b + ret z + inc b + ld a, [de] + inc de + ld [hli], a + jr .loop + +.data +rept TILE_SIZE + db $ff +endr + +; set the carry bit on, +; if "End" was selected. +NamingScreen_ProcessInput: + ld a, [wNamingScreenCursorX] + ld h, a + ld a, [wNamingScreenCursorY] + ld l, a + call GetCharInfoFromPos_Player + inc hl + inc hl + ; load types into de. + ld e, [hl] + inc hl + ld a, [hli] + ld d, a + cp $09 + jp z, .on_end + cp $07 + jr nz, .asm_6ab8 + ld a, [wd009] + or a + jr nz, .asm_6aac + ld a, $01 + jp .asm_6ace +.asm_6aac + dec a + jr nz, .asm_6ab4 + ld a, $02 + jp .asm_6ace +.asm_6ab4 + xor a + jp .asm_6ace +.asm_6ab8 + cp $08 + jr nz, .asm_6ad6 + ld a, [wd009] + or a + jr nz, .asm_6ac6 + ld a, $02 + jr .asm_6ace +.asm_6ac6 + dec a + jr nz, .asm_6acc + xor a + jr .asm_6ace +.asm_6acc + ld a, $01 +.asm_6ace + ld [wd009], a + call DrawNamingScreenBG + or a + ret +.asm_6ad6 + ld a, [wd009] + cp $02 + jr z, .read_char + ldfw3 bc, "“" + ld a, d + cp b + jr nz, .asm_6af4 + ld a, e + cp c + jr nz, .asm_6af4 + push hl + ld hl, TransitionTable1 ; from 55th. + call TransformCharacter + pop hl + jr c, .nothing + jr .asm_6b09 +.asm_6af4 + ldfw3 bc, "º(2)" + ld a, d + cp b + jr nz, .asm_6b1d + ld a, e + cp c + jr nz, .asm_6b1d + push hl + ld hl, TransitionTable2 ; from 72th. + call TransformCharacter + pop hl + jr c, .nothing +.asm_6b09 + ld a, [wNamingScreenBufferLength] + dec a + dec a + ld [wNamingScreenBufferLength], a + ld hl, wNamingScreenBuffer + push de + ld d, 0 + ld e, a + add hl, de + pop de + ld a, [hl] + jr .asm_6b37 +.asm_6b1d + ld a, d + or a + jr nz, .asm_6b37 + ld a, [wd009] + or a + jr nz, .asm_6b2b + ld a, TX_HIRAGANA + jr .asm_6b37 +.asm_6b2b + ld a, TX_KATAKANA + jr .asm_6b37 +; read character code from info. to register. +; hl: pointer. +.read_char + ld e, [hl] + inc hl + ld a, [hl] ; a: first byte of the code. + or a + ; if 2 bytes code, jump. + jr nz, .asm_6b37 + ; if 1 byte code(ascii), + ; set first byte to $0e. + ld a, $0e +; on 2 bytes code. +.asm_6b37 + ld d, a ; de: character code. + ld hl, wNamingScreenBufferLength + ld a, [hl] + ld c, a + push hl + ld hl, wNamingScreenBufferMaxLength + cp [hl] + pop hl + jr nz, .asm_6b4c + ; if the buffer is full + ; just change the last character of it. + ld hl, wNamingScreenBuffer + dec hl + dec hl + jr .asm_6b51 +; increase name length before add the character. +.asm_6b4c + inc [hl] + inc [hl] + ld hl, wNamingScreenBuffer +; write 2 bytes character codes to the name buffer. +; de: 2 bytes character codes. +; hl: dest. +.asm_6b51 + ld b, 0 + add hl, bc + ld [hl], d + inc hl + ld [hl], e + inc hl + ld [hl], TX_END ; null terminator. + call PrintPlayerNameFromInput +.nothing + or a + ret +.on_end + scf + ret + +; this transforms the last japanese character +; in the name buffer into its dakuon shape or something. +; it seems to have been deprecated as the game was translated into english. +; but it can still be applied to english, such as upper-lower case transition. +; hl: info. pointer. +TransformCharacter: + ld a, [wNamingScreenBufferLength] + or a + jr z, .return ; if the length is zero, just return. + dec a + dec a + push hl + ld hl, wNamingScreenBuffer + ld d, 0 + ld e, a + add hl, de + ld e, [hl] + inc hl + ld d, [hl] + ; de: last character in the buffer, + ; but byte-wise swapped. + ld a, TX_KATAKANA + cp e + jr nz, .hiragana + ; if it's katakana, + ; make it hiragana by decreasing its high byte. + dec e +.hiragana + pop hl +.loop + ld a, [hli] + or a + jr z, .return + cp d + jr nz, .next + ld a, [hl] + cp e + jr nz, .next + inc hl + ld e, [hl] + inc hl + ld d, [hl] + or a + ret +.next + inc hl + inc hl + inc hl + jr .loop +.return + scf + ret + +; given the position of the current cursor, +; it returns the pointer to the proper information. +; h: position x. +; l: position y. +GetCharInfoFromPos_Player: + push de + ; (information index) = (x) * (height) + (y) + ; (height) = 0x05(Deck) or 0x06(Player) + ld e, l + ld d, h + ld a, [wNamingScreenKeyboardHeight] + ld l, a + call HtimesL + ld a, l + add e + ld hl, KeyboardData_Player + pop de + or a + ret z +.loop + inc hl + inc hl + inc hl + inc hl + inc hl + inc hl + dec a + jr nz, .loop + ret + +; a set of keyboard datum. +; unit: 6 bytes. +; structure: +; abs. y pos. (1) / abs. x pos. (1) / type 1 (1) / type 2 (1) / char. code (2) +; unused data contains its character code as zero. +kbitem: MACRO + db \1, \2, \3, \4 +if (_NARG == 5) + dw \5 +elif (\5 == TX_FULLWIDTH3) + dw (\5 << 8) | STRCAT("FW3_", \6) +else + dw (\5 << 8) | \6 +endc +ENDM + +KeyboardData_Player: + kbitem $04, $02, $11, $00, TX_FULLWIDTH3, "A" + kbitem $06, $02, $12, $00, TX_FULLWIDTH3, "J" + kbitem $08, $02, $13, $00, TX_FULLWIDTH3, "S" + kbitem $0a, $02, $14, $00, "o" + kbitem $0c, $02, $15, $00, "d" + kbitem $10, $0f, $01, $09, $0000 + + kbitem $04, $04, $16, $00, TX_FULLWIDTH3, "B" + kbitem $06, $04, $17, $00, TX_FULLWIDTH3, "K" + kbitem $08, $04, $18, $00, TX_FULLWIDTH3, "T" + kbitem $0a, $04, $19, $00, TX_FULLWIDTH3, "&" + kbitem $0c, $04, $1a, $00, "e" + kbitem $10, $0f, $01, $09, $0000 + + kbitem $04, $06, $1b, $00, TX_FULLWIDTH3, "C" + kbitem $06, $06, $1c, $00, TX_FULLWIDTH3, "L" + kbitem $08, $06, $1d, $00, TX_FULLWIDTH3, "U" + kbitem $0a, $06, $1e, $00, "j" + kbitem $0c, $06, $1f, $00, "f" + kbitem $10, $0f, $01, $09, $0000 + + kbitem $04, $08, $20, $00, TX_FULLWIDTH3, "D" + kbitem $06, $08, $21, $00, TX_FULLWIDTH3, "M" + kbitem $08, $08, $22, $00, TX_FULLWIDTH3, "V" + kbitem $0a, $08, $23, $00, "k" + kbitem $0c, $08, $24, $00, "g" + kbitem $10, $0f, $01, $09, $0000 + + kbitem $04, $0a, $25, $00, TX_FULLWIDTH3, "E" + kbitem $06, $0a, $26, $00, TX_FULLWIDTH3, "N" + kbitem $08, $0a, $27, $00, TX_FULLWIDTH3, "W" + kbitem $0a, $0a, $28, $00, "w" + kbitem $0c, $0a, $29, $00, "h" + kbitem $10, $0f, $01, $09, $0000 + + kbitem $04, $0c, $2a, $00, TX_FULLWIDTH3, "F" + kbitem $06, $0c, $2b, $00, TX_FULLWIDTH3, "O" + kbitem $08, $0c, $2c, $00, TX_FULLWIDTH3, "X" + kbitem $0a, $0c, $2d, $00, "`" + kbitem $0c, $0c, $2e, $00, "i" + kbitem $10, $0f, $01, $09, $0000 + + kbitem $04, $0e, $2f, $00, TX_FULLWIDTH3, "G" + kbitem $06, $0e, $30, $00, TX_FULLWIDTH3, "P" + kbitem $08, $0e, $31, $00, TX_FULLWIDTH3, "Y" + kbitem $0a, $0e, $32, $00, "a" + kbitem $0c, $0e, $33, $00, TX_SYMBOL, SYM_No + kbitem $10, $0f, $01, $09, $0000 + + kbitem $04, $10, $34, $00, TX_FULLWIDTH3, "H" + kbitem $06, $10, $35, $00, TX_FULLWIDTH3, "Q" + kbitem $08, $10, $36, $00, TX_FULLWIDTH3, "Z" + kbitem $0a, $10, $3c, $00, "b" + kbitem $0c, $10, $3d, $00, TX_SYMBOL, SYM_Lv + kbitem $10, $0f, $01, $09, $0000 + + kbitem $04, $12, $37, $00, TX_FULLWIDTH3, "I" + kbitem $06, $12, $38, $00, TX_FULLWIDTH3, "R" + kbitem $08, $12, $39, $00, "n" + kbitem $0a, $12, $3a, $00, "c" + kbitem $0c, $12, $3b, $00, "p" + kbitem $10, $0f, $01, $09, $0000 + kbitem $00, $00, $00, $00, $0000 + +; a set of transition datum. +; unit: 4 bytes. +; structure: +; previous char. code (2) / translated char. code (2) +; - the former char. code contains 0x0e in high byte. +; - the latter char. code contains only low byte. +TransitionTable1: + dw $0e16, $003e + dw $0e17, $003f + dw $0e18, $0040 + dw $0e19, $0041 + dw $0e1a, $0042 + dw $0e1b, $0043 + dw $0e1c, $0044 + dw $0e1d, $0045 + dw $0e1e, $0046 + dw $0e1f, $0047 + dw $0e20, $0048 + dw $0e21, $0049 + dw $0e22, $004a + dw $0e23, $004b + dw $0e24, $004c + dw $0e2a, $004d + dw $0e2b, $004e + dw $0e2c, $004f + dw $0e2d, $0050 + dw $0e2e, $0051 + dw $0e52, $004d + dw $0e53, $004e + dw $0e54, $004f + dw $0e55, $0050 + dw $0e56, $0051 + dw $0000 + +TransitionTable2: + dw $0e2a, $0052 + dw $0e2b, $0053 + dw $0e2c, $0054 + dw $0e2d, $0055 + dw $0e2e, $0056 + dw $0e4d, $0052 + dw $0e4e, $0053 + dw $0e4f, $0054 + dw $0e50, $0055 + dw $0e51, $0056 + dw $0000 + +; get deck name from the user into de. +; function description is similar to the player's. +; refer to 'InputPlayerName'. +InputDeckName: + push af + ; check if the buffer is empty. + ld a, [de] + or a + jr nz, .not_empty + ; this buffer will contain half-width chars. + ld a, TX_HALFWIDTH + ld [de], a +.not_empty + pop af + inc a + call InitializeInputName + call Set_OBJ_8x8 + + xor a + ld [wTileMapFill], a + call EmptyScreen + call ZeroObjectPositions + + ld a, $01 + ld [wVBlankOAMCopyToggle], a + call LoadSymbolsFont + + lb de, $38, $bf + call SetupText + call LoadHalfWidthTextCursorTile + + xor a + ld [wd009], a + call Func_1ae99 + + xor a + ld [wNamingScreenCursorX], a + ld [wNamingScreenCursorY], a + + ld a, $09 + ld [wNamingScreenNumColumns], a + ld a, $07 + ld [wNamingScreenKeyboardHeight], a + ld a, $0f + ld [wVisibleCursorTile], a + ld a, $00 + ld [wInvisibleCursorTile], a +.loop + ld a, $01 + ld [wVBlankOAMCopyToggle], a + call DoFrame + + call UpdateRNGSources + + ldh a, [hDPadHeld] + and START + jr z, .on_start + + ld a, $01 + call PlayAcceptOrDeclineSFX + call Func_1afa1 + + ld a, 6 + ld [wNamingScreenCursorX], a + ld [wNamingScreenCursorY], a + call Func_1afbd + + jr .loop +.on_start + call Func_1aefb + jr nc, .loop + + cp $ff + jr z, .asm_6e1c + + call Func_1aec3 + jr nc, .loop + + call FinalizeInputName + + ld hl, wNamingScreenDestPointer + ld a, [hli] + ld h, [hl] + ld l, a + inc hl + + ld a, [hl] + or a + jr nz, .return + + dec hl + ld [hl], TX_END +.return + ret +.asm_6e1c + ld a, [wNamingScreenBufferLength] + cp $02 + jr c, .loop + + ld e, a + ld d, 0 + ld hl, wNamingScreenBuffer + add hl, de + dec hl + ld [hl], TX_END + + ld hl, wNamingScreenBufferLength + dec [hl] + call ProcessTextWithUnderbar + + jp .loop + +; load, to the first tile of v0Tiles0, the graphics for the +; blinking black square used in name input screens. +; for inputting half width text. +LoadHalfWidthTextCursorTile: + ld hl, v0Tiles0 + $00 tiles + ld de, .data + ld b, 0 +.loop + ld a, TILE_SIZE + cp b + ret z + inc b + ld a, [de] + inc de + ld [hli], a + jr .loop + +.data +rept TILE_SIZE + db $f0 +endr + +; it's only for naming the deck. +ProcessTextWithUnderbar: + ld hl, wNamingScreenNamePosition + ld d, [hl] + inc hl + ld e, [hl] + call InitTextPrinting + ld hl, .underbar_data + ld de, wDefaultText +.loop ; copy the underbar string. + ld a, [hli] + ld [de], a + inc de + or a + jr nz, .loop + + ld hl, wNamingScreenBuffer + ld de, wDefaultText +.loop2 ; copy the input from the user. + ld a, [hli] + or a + jr z, .print_name + ld [de], a + inc de + jr .loop2 +.print_name + ld hl, wDefaultText + call ProcessText + ret +.underbar_data + db TX_HALFWIDTH +rept MAX_DECK_NAME_LENGTH + db "_" +endr + db TX_END + +Func_1ae99: + call DrawTextboxForKeyboard + call ProcessTextWithUnderbar + ld hl, wNamingScreenQuestionPointer + ld c, [hl] + inc hl + ld a, [hl] + ld h, a + or c + jr z, .print + ; print the question string. + ld l, c + call PlaceTextItems +.print + ; print "End" + ld hl, DrawNamingScreenBG.data + call PlaceTextItems + ; print the keyboard characters. + ldtx hl, DeckNameKeyboardText ; "A B C D..." + lb de, 2, 4 + call InitTextPrinting + call ProcessTextFromID + call EnableLCD + ret + +Func_1aec3: + ld a, [wNamingScreenCursorX] + ld h, a + ld a, [wNamingScreenCursorY] + ld l, a + call GetCharInfoFromPos_Deck + inc hl + inc hl + ld a, [hl] + cp $01 + jr nz, .asm_6ed7 + scf + ret +.asm_6ed7 + ld d, a + ld hl, wNamingScreenBufferLength + ld a, [hl] + ld c, a + push hl + ld hl, wNamingScreenBufferMaxLength + cp [hl] + pop hl + jr nz, .asm_6eeb + ld hl, wNamingScreenBuffer + dec hl + jr .asm_6eef +.asm_6eeb + inc [hl] + ld hl, wNamingScreenBuffer +.asm_6eef + ld b, 0 + add hl, bc + ld [hl], d + inc hl + ld [hl], TX_END + call ProcessTextWithUnderbar + or a + ret + +Func_1aefb: + xor a + ld [wPlaysSfx], a + ldh a, [hDPadHeld] + or a + jp z, .asm_6f73 + ld b, a + ld a, [wNamingScreenKeyboardHeight] + ld c, a + ld a, [wNamingScreenCursorX] + ld h, a + ld a, [wNamingScreenCursorY] + ld l, a + bit 6, b + jr z, .asm_6f1f + dec a + bit 7, a + jr z, .asm_6f4b + ld a, c + dec a + jr .asm_6f4b +.asm_6f1f + bit 7, b + jr z, .asm_6f2a + inc a + cp c + jr c, .asm_6f4b + xor a + jr .asm_6f4b +.asm_6f2a + cp $06 + jr z, .asm_6f73 + ld a, [wNamingScreenNumColumns] + ld c, a + ld a, h + bit 5, b + jr z, .asm_6f40 + dec a + bit 7, a + jr z, .asm_6f4e + ld a, c + dec a + jr .asm_6f4e +.asm_6f40 + bit 4, b + jr z, .asm_6f73 + inc a + cp c + jr c, .asm_6f4e + xor a + jr .asm_6f4e +.asm_6f4b + ld l, a + jr .asm_6f4f +.asm_6f4e + ld h, a +.asm_6f4f + push hl + call GetCharInfoFromPos_Deck + inc hl + inc hl + ld d, [hl] + push de + call Func_1afa1 + pop de + pop hl + ld a, l + ld [wNamingScreenCursorY], a + ld a, h + ld [wNamingScreenCursorX], a + xor a + ld [wCheckMenuCursorBlinkCounter], a + ld a, $02 + cp d + jp z, Func_1aefb + ld a, $01 + ld [wPlaysSfx], a +.asm_6f73 + ldh a, [hKeysPressed] + and $03 + jr z, .asm_6f89 + and $01 + jr nz, .asm_6f7f + ld a, $ff +.asm_6f7f + call PlayAcceptOrDeclineSFX + push af + call Func_1afbd + pop af + scf + ret +.asm_6f89 + ld a, [wPlaysSfx] + or a + jr z, .asm_6f92 + call PlaySFX +.asm_6f92 + ld hl, wCheckMenuCursorBlinkCounter + ld a, [hl] + inc [hl] + and $0f + ret nz + ld a, [wVisibleCursorTile] + bit 4, [hl] + jr z, Func_1afa1.asm_6fa4 + +Func_1afa1: + ld a, [wInvisibleCursorTile] +.asm_6fa4 + ld e, a + ld a, [wNamingScreenCursorX] + ld h, a + ld a, [wNamingScreenCursorY] + ld l, a + call GetCharInfoFromPos_Deck + ld a, [hli] + ld c, a + ld b, [hl] + dec b + ld a, e + call Func_1afc2 + call WriteByteToBGMap0 + or a + ret + +Func_1afbd: + ld a, [wVisibleCursorTile] + jr Func_1afa1.asm_6fa4 + +Func_1afc2: + push af + push bc + push de + push hl + push af + call ZeroObjectPositions + pop af + ld b, a + ld a, [wInvisibleCursorTile] + cp b + jr z, .asm_6ffb + ld a, [wNamingScreenBufferLength] + ld d, a + ld a, [wNamingScreenBufferMaxLength] + ld e, a + ld a, d + cp e + jr nz, .asm_6fdf + dec a +.asm_6fdf + dec a + ld d, a + ld hl, wNamingScreenNamePosition + ld a, [hl] + sla a + add d + ld d, a + ld h, $04 + ld l, d + call HtimesL + ld a, l + add $08 + ld d, a + ld e, $18 + ld bc, $0000 + call SetOneObjectAttributes +.asm_6ffb + pop hl + pop de + pop bc + pop af + ret + +; given the cursor position, +; returns the character information which the cursor directs. +; it's similar to "GetCharInfoFromPos_Player", +; but the data structure is different in its unit size. +; its unit size is 3, and player's is 6. +; h: x +; l: y +GetCharInfoFromPos_Deck: + push de + ld e, l + ld d, h + ld a, [wNamingScreenKeyboardHeight] + ld l, a + call HtimesL + ld a, l + add e + ; x * h + y + ld hl, KeyboardData_Deck + pop de + or a + ret z +.loop + inc hl + inc hl + inc hl + dec a + jr nz, .loop + ret + +KeyboardData_Deck: + db $04, $02, "A" + db $06, $02, "J" + db $08, $02, "S" + db $0a, $02, "?" + db $0c, $02, "4" + db $0e, $02, $02 + db $10, $0f, $01 + + db $04, $04, "B" + db $06, $04, "K" + db $08, $04, "T" + db $0a, $04, "&" + db $0c, $04, "5" + db $0e, $04, $02 + db $10, $0f, $01 + + db $04, $06, "C" + db $06, $06, "L" + db $08, $06, "U" + db $0a, $06, "+" + db $0c, $06, "6" + db $0e, $06, $02 + db $10, $0f, $01 + + db $04, $08, "D" + db $06, $08, "M" + db $08, $08, "V" + db $0a, $08, "-" + db $0c, $08, "7" + db $0e, $08, $02 + db $10, $0f, $01 + + db $04, $0a, "E" + db $06, $0a, "N" + db $08, $0a, "W" + db $0a, $0a, "'" + db $0c, $0a, "8" + db $0e, $0a, $02 + db $10, $0f, $01 + + db $04, $0c, "F" + db $06, $0c, "O" + db $08, $0c, "X" + db $0a, $0c, "0" + db $0c, $0c, "9" + db $0e, $0c, $02 + db $10, $0f, $01 + + db $04, $0e, "G" + db $06, $0e, "P" + db $08, $0e, "Y" + db $0a, $0e, "1" + db $0c, $0e, " " + db $0e, $0e, $02 + db $10, $0f, $01 + + db $04, $10, "H" + db $06, $10, "Q" + db $08, $10, "Z" + db $0a, $10, "2" + db $0c, $10, " " + db $0e, $10, $02 + db $10, $0f, $01 + + db $04, $12, "I" + db $06, $12, "R" + db $08, $12, "!" + db $0a, $12, "3" + db $0c, $12, " " + db $0e, $12, $02 + db $10, $0f, $01 + + ds 4 ; empty diff --git a/src/engine/link/card_pop.asm b/src/engine/link/card_pop.asm new file mode 100644 index 0000000..5f809ef --- /dev/null +++ b/src/engine/link/card_pop.asm @@ -0,0 +1,399 @@ +_DoCardPop: +; loads scene for Card Pop! screen +; then checks if console is SGB +; and issues an error message in case it is + call SetSpriteAnimationsAsVBlankFunction + ld a,SCENE_CARD_POP + lb bc, 0, 0 + call LoadScene + ldtx hl, AreYouBothReadyToCardPopText + call PrintScrollableText_NoTextBoxLabel + call RestoreVBlankFunction + ldtx hl, CardPopCannotBePlayedWithTheGameBoyText + ld a, [wConsole] + cp CONSOLE_SGB + jr z, .error + +; initiate the communications + call PauseSong + call SetSpriteAnimationsAsVBlankFunction + ld a, SCENE_GAMEBOY_LINK_CONNECTING + lb bc, 0, 0 + call LoadScene + ldtx hl, PositionGameBoyColorsAndPressAButtonText + call DrawWideTextBox_PrintText + call EnableLCD + call HandleCardPopCommunications + push af + push hl + call ClearRP + call RestoreVBlankFunction + pop hl + pop af + jr c, .error + +; show the received card detail page +; and play the corresponding song + ld a, [wLoadedCard1ID] + call AddCardToCollectionAndUpdateAlbumProgress + ld hl, wLoadedCard1Name + ld a, [hli] + ld h, [hl] + ld l, a + call LoadTxRam2 + ld a, PLAYER_TURN + ldh [hWhoseTurn], a + ld a, SFX_5D + call PlaySFX +.wait_sfx + call AssertSFXFinished + or a + jr nz, .wait_sfx + ld a, [wCardPopCardObtainSong] + call PlaySong + ldtx hl, ReceivedThroughCardPopText + bank1call _DisplayCardDetailScreen + call ResumeSong + lb de, $38, $9f + call SetupText + bank1call OpenCardPage_FromHand + ret + +.error +; show Card Pop! error scene +; and print text in hl + push hl + call ResumeSong + call SetSpriteAnimationsAsVBlankFunction + ld a, SCENE_CARD_POP_ERROR + lb bc, 0, 0 + call LoadScene + pop hl + call PrintScrollableText_NoTextBoxLabel + call RestoreVBlankFunction + ret + +; handles all communications to the other device to do Card Pop! +; returns carry if Card Pop! is unsuccessful +; and returns in hl the corresponding error text ID +HandleCardPopCommunications: +; copy CardPopNameList from SRAM to WRAM + call EnableSRAM + ld hl, sCardPopNameList + ld de, wCardPopNameList + ld bc, CARDPOP_NAME_LIST_SIZE + call CopyDataHLtoDE + call DisableSRAM + + ld a, IRPARAM_CARD_POP + call InitIRCommunications +.asm_19cc9 + call TryReceiveIRRequest ; receive request + jr nc, .asm_19d05 + bit 1, a + jr nz, .fail + call TrySendIRRequest ; send request + jr c, .asm_19cc9 + +; do the player name search, then transmit the result + call ExchangeIRCommunicationParameters + jr c, .fail + ld hl, wCardPopNameList + ld de, wOtherPlayerCardPopNameList + ld c, 0 ; $100 bytes = CARDPOP_NAME_LIST_SIZE + call RequestDataTransmissionThroughIR + jr c, .fail + call LookUpNameInCardPopNameList + ld hl, wCardPopNameSearchResult + ld de, wCardPopNameSearchResult + ld c, 1 + call RequestDataReceivalThroughIR + jr c, .fail + call SetIRCommunicationErrorCode_NoError + jr c, .fail + call ExecuteReceivedIRCommands + jr c, .fail + jr .check_search_result + +.asm_19d05 + call ExecuteReceivedIRCommands + ld a, [wIRCommunicationErrorCode] + or a + jr nz, .fail + call RequestCloseIRCommunication + jr c, .fail + +.check_search_result + ld a, [wCardPopNameSearchResult] + or a + jr z, .success + ; not $00, means the name was found in the list + ldtx hl, CannotCardPopWithFriendPreviouslyPoppedWithText + scf + ret + +.success + call DecideCardToReceiveFromCardPop + +; increment number of times Card Pop! was done +; and write the other player's name to sCardPopNameList +; the spot where this is written in the list is derived +; from the lower nybble of sTotalCardPopsDone +; that means that after 16 Card Pop!, the older +; names start to get overwritten + call EnableSRAM + ld hl, sTotalCardPopsDone + ld a, [hl] + inc [hl] + and $0f + swap a ; *NAME_BUFFER_LENGTH + ld l, a + ld h, $0 + ld de, sCardPopNameList + add hl, de + ld de, wNameBuffer + ld c, NAME_BUFFER_LENGTH +.loop_write_name + ld a, [de] + inc de + ld [hli], a + dec c + jr nz, .loop_write_name + call DisableSRAM + or a + ret + +.fail + ldtx hl, ThePopWasntSuccessfulText + scf + ret + +; looks up the name in wNameBuffer in wCardPopNameList +; used to know whether this save file has done Card Pop! +; with the other player already +; returns carry and wCardPopNameSearchResult = $ff if the name was found; +; returns no carry and wCardPopNameSearchResult = $00 otherwise +LookUpNameInCardPopNameList: +; searches for other player's name in this game's name list + ld hl, wCardPopNameList + ld c, CARDPOP_NAME_LIST_MAX_ELEMS +.loop_own_card_pop_name_list + push hl + ld de, wNameBuffer + call .CompareNames + pop hl + jr nc, .found_name + ld de, NAME_BUFFER_LENGTH + add hl, de + dec c + jr nz, .loop_own_card_pop_name_list + +; name was not found in wCardPopNameList + +; searches for this player's name in the other game's name list +; this is useless since it discards the result from the name comparisons +; as a result this loop will always return no carry + call EnableSRAM + ld hl, wOtherPlayerCardPopNameList + ld c, CARDPOP_NAME_LIST_MAX_ELEMS +.loop_other_card_pop_name_list + push hl + ld de, sPlayerName + call .CompareNames ; discards result from comparison + pop hl + ld de, NAME_BUFFER_LENGTH + add hl, de + dec c + jr nz, .loop_other_card_pop_name_list + xor a + jr .no_carry + +.found_name + ld a, $ff + scf +.no_carry + call DisableSRAM + ld [wCardPopNameSearchResult], a ; $00 if name was not found, $ff otherwise + ret + +; compares names in hl and de +; if they are different, return carry +.CompareNames + ld b, NAME_BUFFER_LENGTH +.loop_chars + ld a, [de] + inc de + cp [hl] + jr nz, .not_same + inc hl + dec b + jr nz, .loop_chars + or a + ret +.not_same + scf + ret + +; loads in wLoadedCard1 a random card to be received +; this selection is done based on the rarity that is +; decided from the names of both participants +; the card will always be a Pokemon card that is not +; from a Promotional set, with the exception +; of Venusaur1 and Mew2 +; output: +; - e = card ID chosen +DecideCardToReceiveFromCardPop: + ld a, PLAYER_TURN + ldh [hWhoseTurn], a + call EnableSRAM + ld hl, sPlayerName + call CalculateNameHash + call DisableSRAM + push de + ld hl, wNameBuffer + call CalculateNameHash + pop bc + +; de = other player's name hash +; bc = this player's name hash + +; updates RNG values to subtraction of these two hashes + ld hl, wRNG1 + ld a, b + sub d + ld d, a ; b - d + ld [hli], a ; wRNG1 + ld a, c + sub e + ld e, a ; c - e + ld [hli], a ; wRNG2 + ld [hl], $0 ; wRNGCounter + +; depending on the values obtained from the hashes, +; determine which rarity card to give to the player +; along with the song to play with each rarity +; the probabilities of each possibility can be calculated +; as follows (given 2 random player names): +; 101/256 ~ 39% for Circle +; 90/256 ~ 35% for Diamond +; 63/256 ~ 25% for Star +; 1/256 ~ .4% for Venusaur1 or Mew2 + ld a, e + cp 5 + jr z, .venusaur1_or_mew2 + cp 64 + jr c, .star_rarity ; < 64 + cp 154 + jr c, .diamond_rarity ; < 154 + ; >= 154 + + ld a, MUSIC_BOOSTER_PACK + ld b, CIRCLE + jr .got_rarity +.diamond_rarity + ld a, MUSIC_BOOSTER_PACK + ld b, DIAMOND + jr .got_rarity +.star_rarity + ld a, MUSIC_MATCH_VICTORY + ld b, STAR +.got_rarity + ld [wCardPopCardObtainSong], a + ld a, b + call CreateCardPopCandidateList + ; shuffle candidates and pick first from list + call ShuffleCards + ld a, [hl] + ld e, a +.got_card_id + ld d, $0 + call LoadCardDataToBuffer1_FromCardID + ld a, e + ret + +.venusaur1_or_mew2 +; choose either Venusaur1 or Mew2 +; depending on whether the lower +; bit of d is unset or set, respectively + ld a, MUSIC_MEDAL + ld [wCardPopCardObtainSong], a + ld e, VENUSAUR1 + ld a, d + and $1 ; get lower bit + jr z, .got_card_id + ld e, MEW2 + jr .got_card_id + +; lists in wCardPopCardCandidates all cards that: +; - are Pokemon cards; +; - have the same rarity as input register a; +; - are not from Promotional set. +; input: +; - a = card rarity +; output: +; - a = number of candidates +CreateCardPopCandidateList: + ld hl, wPlayerDeck + push hl + push de + push bc + ld b, a + + lb de, 0, GRASS_ENERGY +.loop_card_ids + call LoadCardDataToBuffer1_FromCardID + jr c, .count ; no more card IDs + ld a, [wLoadedCard1Type] + and TYPE_ENERGY + jr nz, .next_card_id ; not Pokemon card + ld a, [wLoadedCard1Rarity] + cp b + jr nz, .next_card_id ; not equal rarity + ld a, [wLoadedCard1Set] + and $f0 + cp PROMOTIONAL + jr z, .next_card_id ; no promos + ld [hl], e + inc hl +.next_card_id + inc de + jr .loop_card_ids + +; count all the cards that were listed +; and return it in a +.count + ld [hl], $00 ; invalid card ID as end of list + ld hl, wPlayerDeck + ld c, -1 +.loop_count + inc c + ld a, [hli] + or a + jr nz, .loop_count + ld a, c + pop bc + pop de + pop hl + ret + +; creates a unique two-byte hash from the name given in hl +; the low byte is calculated by simply adding up all characters +; the high byte is calculated by xoring all characters together +; input: +; - hl = points to the start of the name buffer +; output: +; - de = hash +CalculateNameHash: + ld c, NAME_BUFFER_LENGTH + ld de, $0 +.loop + ld a, e + add [hl] + ld e, a + ld a, d + xor [hl] + ld d, a + inc hl + dec c + jr nz, .loop + ret diff --git a/src/engine/link/ir_core.asm b/src/engine/link/ir_core.asm new file mode 100644 index 0000000..ab9eaae --- /dev/null +++ b/src/engine/link/ir_core.asm @@ -0,0 +1,531 @@ +; if carry flag is set, only delays +; if carry not set: +; - set rRP to $c1, wait; +; - set rRP to $c0, wait; +; - return +Func_19674: + jr c, .delay_once + ld [hl], $c1 + ld a, 5 + jr .loop_delay_1 ; jump to possibly to add more cycles? +.loop_delay_1 + dec a + jr nz, .loop_delay_1 + ld [hl], $c0 + ld a, 14 + jr .loop_delay_2 ; jump to possibly to add more cycles? +.loop_delay_2 + dec a + jr nz, .loop_delay_2 + ret + +.delay_once + ld a, 21 + jr .loop_delay_3 ; jump to possibly to add more cycles? +.loop_delay_3 + dec a + jr nz, .loop_delay_3 + nop + ret + +; input a = byte to transmit through IR +TransmitByteThroughIR: + push hl + ld hl, rRP + push de + push bc + ld b, a + scf ; carry set + call Func_19674 + or a ; carry not set + call Func_19674 + ld c, 8 + ld c, 8 ; number of input bits +.loop + ld a, $00 + rr b + call Func_19674 + dec c + jr nz, .loop + pop bc + pop de + pop hl + ldh a, [rJOYP] + bit 1, a ; P11 + jr z, ReturnZFlagUnsetAndCarryFlagSet + xor a ; return z set + ret + +; same as ReceiveByteThroughIR but +; returns $0 in a if there's an error in IR +ReceiveByteThroughIR_ZeroIfUnsuccessful: + call ReceiveByteThroughIR + ret nc + xor a + ret + +; returns carry if there's some time out +; and output in register a of $ff +; otherwise returns in a some sequence of bits +; related to how rRP sets/unsets bit 1 +ReceiveByteThroughIR: + push de + push bc + push hl + +; waits for bit 1 in rRP to be unset +; up to $100 loops + ld b, 0 + ld hl, rRP +.wait_ir + bit 1, [hl] + jr z, .ok + dec b + jr nz, .wait_ir + ; looped around $100 times + ; return $ff and carry set + pop hl + pop bc + pop de + scf + ld a, $ff + ret + +.ok +; delay for some cycles + ld a, 15 +.loop_delay + dec a + jr nz, .loop_delay + +; loop for each bit + ld e, 8 +.loop + ld a, $01 + ; possibly delay cycles? + ld b, 9 + ld b, 9 + ld b, 9 + ld b, 9 + +; checks for bit 1 in rRP +; if in any of the checks it is unset, +; then a is set to 0 +; this is done a total of 9 times + bit 1, [hl] + jr nz, .asm_196ec + xor a +.asm_196ec + bit 1, [hl] + jr nz, .asm_196f1 + xor a +.asm_196f1 + dec b + jr nz, .asm_196ec + ; one bit received + rrca + rr d + dec e + jr nz, .loop + ld a, d ; has bits set for each "cycle" that bit 1 was not unset + pop hl + pop bc + pop de + or a + ret + +ReturnZFlagUnsetAndCarryFlagSet: + ld a, $ff + or a ; z not set + scf ; carry set + ret + +; called when expecting to transmit data +Func_19705: + ld hl, rRP +.asm_19708 + ldh a, [rJOYP] + bit 1, a + jr z, ReturnZFlagUnsetAndCarryFlagSet + ld a, $aa ; request + call TransmitByteThroughIR + push hl + pop hl + call ReceiveByteThroughIR_ZeroIfUnsuccessful + cp $33 ; acknowledge + jr nz, .asm_19708 + xor a + ret + +; called when expecting to receive data +Func_1971e: + ld hl, rRP +.asm_19721 + ldh a, [rJOYP] + bit 1, a + jr z, ReturnZFlagUnsetAndCarryFlagSet + call ReceiveByteThroughIR_ZeroIfUnsuccessful + cp $aa ; request + jr nz, .asm_19721 + ld a, $33 ; acknowledge + call TransmitByteThroughIR + xor a + ret + +ReturnZFlagUnsetAndCarryFlagSet2: + jp ReturnZFlagUnsetAndCarryFlagSet + +TransmitIRDataBuffer: + call Func_19705 + jr c, ReturnZFlagUnsetAndCarryFlagSet2 + ld a, $49 + call TransmitByteThroughIR + ld a, $52 + call TransmitByteThroughIR + ld hl, wIRDataBuffer + ld c, 8 + jr TransmitNBytesFromHLThroughIR + +ReceiveIRDataBuffer: + call Func_1971e + jr c, ReturnZFlagUnsetAndCarryFlagSet2 + call ReceiveByteThroughIR + cp $49 + jr nz, ReceiveIRDataBuffer + call ReceiveByteThroughIR + cp $52 + jr nz, ReceiveIRDataBuffer + ld hl, wIRDataBuffer + ld c, 8 + jr ReceiveNBytesToHLThroughIR + +; hl = start of data to transmit +; c = number of bytes to transmit +TransmitNBytesFromHLThroughIR: + ld b, $0 +.loop_data_bytes + ld a, b + add [hl] + ld b, a + ld a, [hli] + call TransmitByteThroughIR + jr c, .asm_1977c + dec c + jr nz, .loop_data_bytes + ld a, b + cpl + inc a + call TransmitByteThroughIR +.asm_1977c + ret + +; hl = address to write received data +; c = number of bytes to be received +ReceiveNBytesToHLThroughIR: + ld b, 0 +.loop_data_bytes + call ReceiveByteThroughIR + jr c, ReturnZFlagUnsetAndCarryFlagSet2 + ld [hli], a + add b + ld b, a + dec c + jr nz, .loop_data_bytes + call ReceiveByteThroughIR + add b + or a + jr nz, ReturnZFlagUnsetAndCarryFlagSet2 + ret + +; disables interrupts, and sets joypad and IR communication port +; switches to CGB normal speed +StartIRCommunications: + di + call SwitchToCGBNormalSpeed + ld a, P14 + ldh [rJOYP], a + ld a, $c0 + ldh [rRP], a + ret + +; reenables interrupts, and switches CGB back to double speed +CloseIRCommunications: + ld a, P14 | P15 + ldh [rJOYP], a +.wait_vblank_on + ldh a, [rSTAT] + and STAT_LCDC_STATUS + cp STAT_ON_VBLANK + jr z, .wait_vblank_on +.wait_vblank_off + ldh a, [rSTAT] + and STAT_LCDC_STATUS + cp STAT_ON_VBLANK + jr nz, .wait_vblank_off + call SwitchToCGBDoubleSpeed + ei + ret + +; set rRP to 0 +ClearRP: + ld a, $00 + ldh [rRP], a + ret + +; expects to receive a command (IRCMD_* constant) +; in wIRDataBuffer + 1, then calls the subroutine +; corresponding to that command +ExecuteReceivedIRCommands: + call StartIRCommunications +.loop_commands + call ReceiveIRDataBuffer + jr c, .error + jr nz, .loop_commands + ld hl, wIRDataBuffer + 1 + ld a, [hl] + ld hl, .CmdPointerTable + cp NUM_IR_COMMANDS + jr nc, .loop_commands ; invalid command + call .JumpToCmdPointer ; execute command + jr .loop_commands +.error + call CloseIRCommunications + xor a + scf + ret + +.JumpToCmdPointer + add a ; *2 + add l + ld l, a + ld a, 0 + adc h + ld h, a + ld a, [hli] + ld h, [hl] + ld l, a +.jp_hl + jp hl + +.CmdPointerTable + dw .Close ; IRCMD_CLOSE + dw .ReturnWithoutClosing ; IRCMD_RETURN_WO_CLOSING + dw .TransmitData ; IRCMD_TRANSMIT_DATA + dw .ReceiveData ; IRCMD_RECEIVE_DATA + dw .CallFunction ; IRCMD_CALL_FUNCTION + +; closes the IR communications +; pops hl so that the sp points +; to the return address of ExecuteReceivedIRCommands +.Close + pop hl + call CloseIRCommunications + or a + ret + +; returns without closing the IR communications +; will continue the command loop +.ReturnWithoutClosing + or a + ret + +; receives an address and number of bytes +; and transmits starting at that address +.TransmitData + call Func_19705 + ret c + call LoadRegistersFromIRDataBuffer + jp TransmitNBytesFromHLThroughIR + +; receives an address and number of bytes +; and writes the data received to that address +.ReceiveData + call LoadRegistersFromIRDataBuffer + ld l, e + ld h, d + call ReceiveNBytesToHLThroughIR + jr c, .asm_19812 + sub b + call TransmitByteThroughIR +.asm_19812 + ret + +; receives an address to call, then stores +; the registers in the IR data buffer +.CallFunction + call LoadRegistersFromIRDataBuffer + call .jp_hl + call StoreRegistersInIRDataBuffer + ret + +; returns carry set if request sent was not acknowledged +TrySendIRRequest: + call StartIRCommunications + ld hl, rRP + ld c, 4 +.send_request + ld a, $aa ; request + push bc + call TransmitByteThroughIR + push bc + pop bc + call ReceiveByteThroughIR_ZeroIfUnsuccessful + pop bc + cp $33 ; acknowledgement + jr z, .received_ack + dec c + jr nz, .send_request + scf + jr .close + +.received_ack + xor a +.close + push af + call CloseIRCommunications + pop af + ret + +; returns carry set if request was not received +TryReceiveIRRequest: + call StartIRCommunications + ld hl, rRP +.wait_request + call ReceiveByteThroughIR_ZeroIfUnsuccessful + cp $aa ; request + jr z, .send_ack + ldh a, [rJOYP] + cpl + and P10 | P11 + jr z, .wait_request + scf + jr .close + +.send_ack + ld a, $33 ; acknowledgement + call TransmitByteThroughIR + xor a +.close + push af + call CloseIRCommunications + pop af + ret + +; sends request for other device to close current communication +RequestCloseIRCommunication: + call StartIRCommunications + ld a, IRCMD_CLOSE + ld [wIRDataBuffer + 1], a + call TransmitIRDataBuffer +; fallthrough + +; calls CloseIRCommunications while preserving af +SafelyCloseIRCommunications: + push af + call CloseIRCommunications + pop af + ret + +; sends a request for data to be transmitted +; from the other device +; hl = start of data to request to transmit +; de = address to write data received +; c = length of data +RequestDataTransmissionThroughIR: + ld a, IRCMD_TRANSMIT_DATA + call TransmitRegistersThroughIR + push de + push bc + call Func_1971e + pop bc + pop hl + jr c, SafelyCloseIRCommunications + call ReceiveNBytesToHLThroughIR + jr SafelyCloseIRCommunications + +; transmits data to be written in the other device +; hl = start of data to transmit +; de = address for other device to write data +; c = length of data +RequestDataReceivalThroughIR: + ld a, IRCMD_RECEIVE_DATA + call TransmitRegistersThroughIR + call TransmitNBytesFromHLThroughIR + jr c, SafelyCloseIRCommunications + call ReceiveByteThroughIR + jr c, SafelyCloseIRCommunications + add b + jr nz, .asm_1989e + xor a + jr SafelyCloseIRCommunications +.asm_1989e + call ReturnZFlagUnsetAndCarryFlagSet + jr SafelyCloseIRCommunications + +; first stores all the current registers in wIRDataBuffer +; then transmits it through IR +TransmitRegistersThroughIR: + push hl + push de + push bc + call StoreRegistersInIRDataBuffer + call StartIRCommunications + call TransmitIRDataBuffer + pop bc + pop de + pop hl + ret nc + inc sp + inc sp + jr SafelyCloseIRCommunications + +; stores af, hl, de and bc in wIRDataBuffer +StoreRegistersInIRDataBuffer: + push de + push hl + push af + ld hl, wIRDataBuffer + pop de + ld [hl], e ; <- f + inc hl + ld [hl], d ; <- a + inc hl + pop de + ld [hl], e ; <- l + inc hl + ld [hl], d ; <- h + inc hl + pop de + ld [hl], e ; <- e + inc hl + ld [hl], d ; <- d + inc hl + ld [hl], c ; <- c + inc hl + ld [hl], b ; <- b + ret + +; loads all the registers that were stored +; from StoreRegistersInIRDataBuffer +LoadRegistersFromIRDataBuffer: + ld hl, wIRDataBuffer + ld e, [hl] + inc hl + ld d, [hl] + inc hl + push de + ld e, [hl] + inc hl + ld d, [hl] + inc hl + push de + ld e, [hl] + inc hl + ld d, [hl] + inc hl + ld c, [hl] + inc hl + ld b, [hl] + pop hl + pop af + ret diff --git a/src/engine/link/ir_functions.asm b/src/engine/link/ir_functions.asm new file mode 100644 index 0000000..140dccb --- /dev/null +++ b/src/engine/link/ir_functions.asm @@ -0,0 +1,323 @@ +; hl = text ID +LoadLinkConnectingScene: + push hl + call SetSpriteAnimationsAsVBlankFunction + ld a, SCENE_GAMEBOY_LINK_CONNECTING + lb bc, 0, 0 + call LoadScene + pop hl + call DrawWideTextBox_PrintText + call EnableLCD + ret + +; shows Link Not Connected scene +; then asks the player whether they want to try again +; if the player selects "no", return carry +; input: +; - hl = text ID +LoadLinkNotConnectedSceneAndAskWhetherToTryAgain: + push hl + call RestoreVBlankFunction + call SetSpriteAnimationsAsVBlankFunction + ld a, SCENE_GAMEBOY_LINK_NOT_CONNECTED + lb bc, 0, 0 + call LoadScene + pop hl + call DrawWideTextBox_WaitForInput + ldtx hl, WouldYouLikeToTryAgainText + call YesOrNoMenuWithText_SetCursorToYes +; fallthrough + +ClearRPAndRestoreVBlankFunction: + push af + call ClearRP + call RestoreVBlankFunction + pop af + ret + +; prepares IR communication parameter data +; a = a IRPARAM_* constant for the function of this connection +InitIRCommunications: + ld hl, wOwnIRCommunicationParams + ld [hl], a + inc hl + ld [hl], $50 + inc hl + ld [hl], $4b + inc hl + ld [hl], $31 + ld a, $ff + ld [wIRCommunicationErrorCode], a + ld a, PLAYER_TURN + ldh [hWhoseTurn], a +; clear wNameBuffer and wOpponentName + xor a + ld [wNameBuffer], a + ld hl, wOpponentName + ld [hli], a + ld [hl], a +; loads player's name from SRAM +; to wDefaultText + call EnableSRAM + ld hl, sPlayerName + ld de, wDefaultText + ld c, NAME_BUFFER_LENGTH +.loop + ld a, [hli] + ld [de], a + inc de + dec c + jr nz, .loop + call DisableSRAM + ret + +; returns carry if communication was unsuccessful +; if a = 0, then it was a communication error +; if a = 1, then operation was cancelled by the player +PrepareSendCardOrDeckConfigurationThroughIR: + call InitIRCommunications + +; pressing A button triggers request for IR communication +.loop_frame + call DoFrame + ldh a, [hKeysPressed] + bit B_BUTTON_F, a + jr nz, .b_btn + ldh a, [hKeysHeld] + bit A_BUTTON_F, a + jr z, .loop_frame +; a btn + call TrySendIRRequest + jr nc, .request_success + or a + jr z, .loop_frame + xor a + scf + ret + +.b_btn + ; cancelled by the player + ld a, $01 + scf + ret + +.request_success + call ExchangeIRCommunicationParameters + ret c + ld a, [wOtherIRCommunicationParams + 3] + cp $31 + jr nz, SetIRCommunicationErrorCode_Error + or a + ret + +; exchanges player names and IR communication parameters +; checks whether parameters for communication match +; and if they don't, an error is issued +ExchangeIRCommunicationParameters: + ld hl, wOwnIRCommunicationParams + ld de, wOtherIRCommunicationParams + ld c, 4 + call RequestDataTransmissionThroughIR + jr c, .error + ld hl, wOtherIRCommunicationParams + 1 + ld a, [hli] + cp $50 + jr nz, .error + ld a, [hli] + cp $4b + jr nz, .error + ld a, [wOwnIRCommunicationParams] + ld hl, wOtherIRCommunicationParams + cp [hl] ; do parameters match? + jr nz, SetIRCommunicationErrorCode_Error + +; receives wDefaultText from other device +; and writes it to wNameBuffer + ld hl, wDefaultText + ld de, wNameBuffer + ld c, NAME_BUFFER_LENGTH + call RequestDataTransmissionThroughIR + jr c, .error +; transmits wDefaultText to be +; written in wNameBuffer in the other device + ld hl, wDefaultText + ld de, wNameBuffer + ld c, NAME_BUFFER_LENGTH + call RequestDataReceivalThroughIR + jr c, .error + or a + ret + +.error + xor a + scf + ret + +SetIRCommunicationErrorCode_Error: + ld hl, wIRCommunicationErrorCode + ld [hl], $01 + ld de, wIRCommunicationErrorCode + ld c, 1 + call RequestDataReceivalThroughIR + call RequestCloseIRCommunication + ld a, $01 + scf + ret + +SetIRCommunicationErrorCode_NoError: + ld hl, wOwnIRCommunicationParams + ld [hl], $00 + ld de, wIRCommunicationErrorCode + ld c, 1 + call RequestDataReceivalThroughIR + ret c + call RequestCloseIRCommunication + or a + ret + +; makes device receptive to receive data from other device +; to write in wDuelTempList (either list of cards or a deck configuration) +; returns carry if some error occurred +TryReceiveCardOrDeckConfigurationThroughIR: + call InitIRCommunications +.loop_receive_request + xor a + ld [wDuelTempList], a + call TryReceiveIRRequest + jr nc, .receive_data + bit 1, a + jr nz, .cancelled + jr .loop_receive_request +.receive_data + call ExecuteReceivedIRCommands + ld a, [wIRCommunicationErrorCode] + or a + ret z ; no error + xor a + scf + ret + +.cancelled + ld a, $01 + scf + ret + +; returns carry if card(s) wasn't successfully sent +_SendCard: + call StopMusic + ldtx hl, SendingACardText + call LoadLinkConnectingScene + ld a, IRPARAM_SEND_CARDS + call PrepareSendCardOrDeckConfigurationThroughIR + jr c, .fail + + ; send cards + xor a + ld [wDuelTempList + DECK_SIZE], a + ld hl, wDuelTempList + ld e, l + ld d, h + ld c, DECK_SIZE + 1 + call RequestDataReceivalThroughIR + jr c, .fail + call SetIRCommunicationErrorCode_NoError + jr c, .fail + call ExecuteReceivedIRCommands + jr c, .fail + ld a, [wOwnIRCommunicationParams + 1] + cp $4f + jr nz, .fail + call PlayCardPopSong + xor a + call ClearRPAndRestoreVBlankFunction + ret + +.fail + call PlayCardPopSong + ldtx hl, CardTransferWasntSuccessful1Text + call LoadLinkNotConnectedSceneAndAskWhetherToTryAgain + jr nc, _SendCard ; loop back and try again + ; failed + scf + ret + +PlayCardPopSong: + ld a, MUSIC_CARD_POP + jp PlaySong + +_ReceiveCard: + call StopMusic + ldtx hl, ReceivingACardText + call LoadLinkConnectingScene + ld a, IRPARAM_SEND_CARDS + call TryReceiveCardOrDeckConfigurationThroughIR + ld a, $4f + ld [wOwnIRCommunicationParams + 1], a + ld hl, wOwnIRCommunicationParams + ld e, l + ld d, h + ld c, 4 + call RequestDataReceivalThroughIR + jr c, .fail + call RequestCloseIRCommunication + jr c, .fail + call PlayCardPopSong + or a + call ClearRPAndRestoreVBlankFunction + ret + +.fail + call PlayCardPopSong + ldtx hl, CardTransferWasntSuccessful2Text + call LoadLinkNotConnectedSceneAndAskWhetherToTryAgain + jr nc, _ReceiveCard + scf + ret + +_SendDeckConfiguration: + call StopMusic + ldtx hl, SendingADeckConfigurationText + call LoadLinkConnectingScene + ld a, IRPARAM_SEND_DECK + call PrepareSendCardOrDeckConfigurationThroughIR + jr c, .fail + ld hl, wDuelTempList + ld e, l + ld d, h + ld c, DECK_STRUCT_SIZE + call RequestDataReceivalThroughIR + jr c, .fail + call SetIRCommunicationErrorCode_NoError + jr c, .fail + call PlayCardPopSong + call ClearRPAndRestoreVBlankFunction + or a + ret + +.fail + call PlayCardPopSong + ldtx hl, DeckConfigurationTransferWasntSuccessful1Text + call LoadLinkNotConnectedSceneAndAskWhetherToTryAgain + jr nc, _SendDeckConfiguration + scf + ret + +_ReceiveDeckConfiguration: + call StopMusic + ldtx hl, ReceivingDeckConfigurationText + call LoadLinkConnectingScene + ld a, IRPARAM_SEND_DECK + call TryReceiveCardOrDeckConfigurationThroughIR + jr c, .fail + call PlayCardPopSong + call ClearRPAndRestoreVBlankFunction + or a + ret + +.fail + call PlayCardPopSong + ldtx hl, DeckConfigurationTransferWasntSuccessful2Text + call LoadLinkNotConnectedSceneAndAskWhetherToTryAgain + jr nc, _ReceiveDeckConfiguration ; loop back and try again + scf + ret diff --git a/src/engine/link/link_duel.asm b/src/engine/link/link_duel.asm new file mode 100644 index 0000000..fc484d0 --- /dev/null +++ b/src/engine/link/link_duel.asm @@ -0,0 +1,179 @@ +; sets up to start a link duel +; decides which device will pick the number of prizes +; then exchanges names and duels between the players +; and starts the main duel routine +_SetUpAndStartLinkDuel: + ld hl, sp+$00 + ld a, l + ld [wDuelReturnAddress + 0], a + ld a, h + ld [wDuelReturnAddress + 1], a + call SetSpriteAnimationsAsVBlankFunction + + ld a, SCENE_GAMEBOY_LINK_TRANSMITTING + lb bc, 0, 0 + call LoadScene + + bank1call LoadPlayerDeck + call SwitchToCGBNormalSpeed + bank1call DecideLinkDuelVariables + push af + call RestoreVBlankFunction + pop af + jp c, .error + + ld a, DUELIST_TYPE_PLAYER + ld [wPlayerDuelistType], a + ld a, DUELIST_TYPE_LINK_OPP + ld [wOpponentDuelistType], a + ld a, DUELTYPE_LINK + ld [wDuelType], a + + call EmptyScreen + ld a, [wSerialOp] + cp $29 + jr nz, .asm_1a540 + + ld a, PLAYER_TURN + ldh [hWhoseTurn], a + call .ExchangeNamesAndDecks + jr c, .error + lb de, 6, 2 + lb bc, 8, 6 + call DrawRegularTextBox + lb de, 7, 4 + call InitTextPrinting + ldtx hl, PrizesCardsText + call ProcessTextFromID + ldtx hl, ChooseTheNumberOfPrizesText + call DrawWideTextBox_PrintText + call EnableLCD + call .PickNumberOfPrizeCards + ld a, [wNPCDuelPrizes] + call SerialSend8Bytes + jr .prizes_decided + +.asm_1a540 + ld a, OPPONENT_TURN + ldh [hWhoseTurn], a + call .ExchangeNamesAndDecks + jr c, .error + ldtx hl, PleaseWaitDecidingNumberOfPrizesText + call DrawWideTextBox_PrintText + call EnableLCD + call SerialRecv8Bytes + ld [wNPCDuelPrizes], a + +.prizes_decided + call ExchangeRNG + ld a, LINK_OPP_PIC + ld [wOpponentPortrait], a + ldh a, [hWhoseTurn] + push af + call EmptyScreen + bank1call SetDefaultPalettes + ld a, SHUFFLE_DECK + ld [wDuelDisplayedScreen], a + bank1call DrawDuelistPortraitsAndNames + ld a, OPPONENT_TURN + ldh [hWhoseTurn], a + ld a, [wNPCDuelPrizes] + ld l, a + ld h, $00 + call LoadTxRam3 + ldtx hl, BeginAPrizeDuelWithText + call DrawWideTextBox_WaitForInput + pop af + ldh [hWhoseTurn], a + call ExchangeRNG + bank1call StartDuel_VSLinkOpp + call SwitchToCGBDoubleSpeed + ret + +.error + ld a, -1 + ld [wDuelResult], a + call SetSpriteAnimationsAsVBlankFunction + + ld a, SCENE_GAMEBOY_LINK_NOT_CONNECTED + lb bc, 0, 0 + call LoadScene + + ldtx hl, TransmissionErrorText + call DrawWideTextBox_WaitForInput + call RestoreVBlankFunction + call ResetSerial + ret + +.ExchangeNamesAndDecks + ld de, wDefaultText + push de + call CopyPlayerName + pop hl + ld de, wNameBuffer + ld c, NAME_BUFFER_LENGTH + call SerialExchangeBytes + ret c + xor a + ld hl, wOpponentName + ld [hli], a + ld [hl], a + ld hl, wPlayerDeck + ld de, wOpponentDeck + ld c, DECK_SIZE + call SerialExchangeBytes + ret + +; handles player choice of number of prize cards +; pressing left/right makes it decrease/increase respectively +; selection is confirmed by pressing A button +.PickNumberOfPrizeCards + ld a, PRIZES_4 + ld [wNPCDuelPrizes], a + xor a + ld [wPrizeCardSelectionFrameCounter], a +.loop_input + call DoFrame + ld a, [wNPCDuelPrizes] + add SYM_0 + ld e, a + ; check frame counter so that it + ; either blinks or shows number + ld hl, wPrizeCardSelectionFrameCounter + ld a, [hl] + inc [hl] + and $10 + jr z, .no_blink + ld e, SYM_SPACE +.no_blink + ld a, e + lb bc, 9, 6 + call WriteByteToBGMap0 + + ldh a, [hDPadHeld] + ld b, a + ld a, [wNPCDuelPrizes] + bit D_LEFT_F, b + jr z, .check_d_right + dec a + cp PRIZES_2 + jr nc, .got_prize_count + ld a, PRIZES_6 ; wrap around to 6 + jr .got_prize_count + +.check_d_right + bit D_RIGHT_F, b + jr z, .check_a_btn + inc a + cp PRIZES_6 + 1 + jr c, .got_prize_count + ld a, PRIZES_2 +.got_prize_count + ld [wNPCDuelPrizes], a + xor a + ld [wPrizeCardSelectionFrameCounter], a + +.check_a_btn + bit A_BUTTON_F, b + jr z, .loop_input + ret diff --git a/src/engine/link/printer.asm b/src/engine/link/printer.asm new file mode 100644 index 0000000..110fde4 --- /dev/null +++ b/src/engine/link/printer.asm @@ -0,0 +1,1124 @@ +; sends serial data to printer +; if there's an error in connection, +; show Printer Not Connected scene with error message +_PreparePrinterConnection: + ld bc, $0 + lb de, PRINTERPKT_DATA, $0 + call SendPrinterPacket + ret nc ; return if no error + + ld hl, wPrinterStatus + ld a, [hl] + or a + jr nz, .asm_19e55 + ld [hl], $ff +.asm_19e55 + ld a, [hl] + cp $ff + jr z, ShowPrinterIsNotConnected +; fallthrough + +; shows message on screen depending on wPrinterStatus +; also shows SCENE_GAMEBOY_PRINTER_NOT_CONNECTED. +HandlePrinterError: + ld a, [wPrinterStatus] + cp $ff + jr z, .cable_or_printer_switch + or a + jr z, .interrupted + bit PRINTER_ERROR_BATTERIES_LOST_CHARGE, a + jr nz, .batteries_lost_charge + bit PRINTER_ERROR_CABLE_PRINTER_SWITCH, a + jr nz, .cable_or_printer_switch + bit PRINTER_ERROR_PAPER_JAMMED, a + jr nz, .jammed_printer + + ldtx hl, PrinterPacketErrorText + ld a, $04 + jr ShowPrinterConnectionErrorScene +.cable_or_printer_switch + ldtx hl, CheckCableOrPrinterSwitchText + ld a, $02 + jr ShowPrinterConnectionErrorScene +.jammed_printer + ldtx hl, PrinterPaperIsJammedText + ld a, $03 + jr ShowPrinterConnectionErrorScene +.batteries_lost_charge + ldtx hl, BatteriesHaveLostTheirChargeText + ld a, $01 + jr ShowPrinterConnectionErrorScene +.interrupted + ldtx hl, PrintingWasInterruptedText + call DrawWideTextBox_WaitForInput + scf + ret + +ShowPrinterIsNotConnected: + ldtx hl, PrinterIsNotConnectedText + ld a, $02 +; fallthrough + +; a = error code +; hl = text ID to print in text box +ShowPrinterConnectionErrorScene: + push hl + ; unnecessary loading TxRam, since the text data + ; already incorporate the error number + ld l, a + ld h, $00 + call LoadTxRam3 + + call SetSpriteAnimationsAsVBlankFunction + ld a, SCENE_GAMEBOY_PRINTER_NOT_CONNECTED + lb bc, 0, 0 + call LoadScene + pop hl + call DrawWideTextBox_WaitForInput + call RestoreVBlankFunction + scf + ret + +; main card printer function +Func_19eb4: + ld e, a + ld d, $0 + call LoadCardDataToBuffer1_FromCardID + call SetSpriteAnimationsAsVBlankFunction + ld a, SCENE_GAMEBOY_PRINTER_TRANSMITTING + lb bc, 0, 0 + call LoadScene + ld a, 20 + call CopyCardNameAndLevel + ld [hl], TX_END + ld hl, $0 + call LoadTxRam2 + ldtx hl, NowPrintingText + call DrawWideTextBox_PrintText + call EnableLCD + call PrepareForPrinterCommunications + call DrawTopCardInfoInSRAMGfxBuffer0 + call Func_19f87 + call DrawCardPicInSRAMGfxBuffer2 + call Func_19f99 + jr c, .error + call DrawBottomCardInfoInSRAMGfxBuffer0 + call Func_1a011 + jr c, .error + call RestoreVBlankFunction + call ResetPrinterCommunicationSettings + or a + ret +.error + call RestoreVBlankFunction + call ResetPrinterCommunicationSettings + jp HandlePrinterError + +DrawCardPicInSRAMGfxBuffer2: + ld hl, wLoadedCard1Gfx + ld a, [hli] + ld h, [hl] + ld l, a + ld de, sGfxBuffer2 + call Func_37a5 + ; draw card's picture in sGfxBuffer2 + ld a, $40 + lb hl, 12, 1 + lb de, 2, 68 + lb bc, 16, 12 + call FillRectangle + ret + +; writes the tiles necessary to draw +; the card's information in sGfxBuffer0 +; this includes card's type, lv, HP and attacks if Pokemon card +; or otherwise just the card's name and type symbol +DrawTopCardInfoInSRAMGfxBuffer0: + call Func_1a025 + call Func_212f + + ; draw empty text box frame + ld hl, sGfxBuffer0 + ld a, $34 + lb de, $30, $31 + ld b, 20 + call CopyLine + ld c, 15 +.loop_lines + xor a ; SYM_SPACE + lb de, $36, $37 + ld b, 20 + call CopyLine + dec c + jr nz, .loop_lines + + ; draw card type symbol + ld a, $38 + lb hl, 1, 2 + lb de, 1, 65 + lb bc, 2, 2 + call FillRectangle + ; print card's name + lb de, 4, 65 + ld hl, wLoadedCard1Name + call InitTextPrinting_ProcessTextFromPointerToID + +; prints card's type, lv, HP and attacks if it's a Pokemon card + ld a, [wLoadedCard1Type] + cp TYPE_ENERGY + jr nc, .skip_pokemon_data + inc a ; symbol corresponding to card's type (color) + lb bc, 18, 65 + call WriteByteToBGMap0 + ld a, SYM_Lv + lb bc, 11, 66 + call WriteByteToBGMap0 + ld a, [wLoadedCard1Level] + lb bc, 12, 66 + bank1call WriteTwoDigitNumberInTxSymbolFormat + ld a, SYM_HP + lb bc, 15, 66 + call WriteByteToBGMap0 + ld a, [wLoadedCard1EffectCommands] + inc b + bank1call WriteTwoByteNumberInTxSymbolFormat +.skip_pokemon_data + ret + +Func_19f87: + call TryInitPrinterCommunications + ret c + ld hl, sGfxBuffer0 + call Func_1a0cc + ret c + call Func_1a0cc + call Func_1a111 + ret + +Func_19f99: + call TryInitPrinterCommunications + ret c + ld hl, sGfxBuffer0 + $8 tiles + ld c, $06 +.asm_19fa2 + call Func_1a0cc + ret c + dec c + jr nz, .asm_19fa2 + call Func_1a111 + ret + +; writes the tiles necessary to draw +; the card's information in sGfxBuffer0 +; this includes card's Retreat cost, Weakness, Resistance, +; and attack if it's Pokemon card +; or otherwise just the card's description. +DrawBottomCardInfoInSRAMGfxBuffer0: + call Func_1a025 + xor a + ld [wCardPageType], a + ld hl, sGfxBuffer0 + ld b, 20 + ld c, 9 +.loop_lines + xor a ; SYM_SPACE + lb de, $36, $37 + call CopyLine + dec c + jr nz, .loop_lines + ld a, $35 + lb de, $32, $33 + call CopyLine + + ld a, [wLoadedCard1Type] + cp TYPE_ENERGY + jr nc, .not_pkmn_card + ld hl, RetreatWeakResistData + call PlaceTextItems + ld c, 66 + bank1call DisplayCardPage_PokemonOverview.attacks + ld a, SYM_No + lb bc, 15, 72 + call WriteByteToBGMap0 + inc b + ld a, [wLoadedCard1PokedexNumber] + bank1call WriteTwoByteNumberInTxSymbolFormat + ret + +.not_pkmn_card + bank1call SetNoLineSeparation + lb de, 1, 66 + ld a, SYM_No + call InitTextPrintingInTextbox + ld hl, wLoadedCard1NonPokemonDescription + call ProcessTextFromPointerToID + bank1call SetOneLineSeparation + ret + +RetreatWeakResistData: + textitem 1, 70, RetreatText + textitem 1, 71, WeaknessText + textitem 1, 72, ResistanceText + db $ff + +Func_1a011: + call TryInitPrinterCommunications + ret c + ld hl, sGfxBuffer0 + ld c, $05 +.asm_1a01a + call Func_1a0cc + ret c + dec c + jr nz, .asm_1a01a + call Func_1a108 + ret + +; calls setup text and sets wTilePatternSelector +Func_1a025: + lb de, $40, $bf + call SetupText + ld a, $a4 + ld [wTilePatternSelector], a + xor a + ld [wTilePatternSelectorCorrection], a + ret + +; switches to CGB normal speed, resets serial +; enables SRAM and switches to SRAM1 +; and clears sGfxBuffer0 +PrepareForPrinterCommunications: + call SwitchToCGBNormalSpeed + call ResetSerial + ld a, $10 + ld [wce9b], a + call EnableSRAM + ld a, [sPrinterContrastLevel] + ld [wPrinterContrastLevel], a + call DisableSRAM + ldh a, [hBankSRAM] + ld [wce8f], a + ld a, BANK("SRAM1") + call BankswitchSRAM + call EnableSRAM +; fallthrough + +ClearPrinterGfxBuffer: + ld hl, sGfxBuffer0 + ld bc, $400 +.loop + xor a + ld [hli], a + dec bc + ld a, c + or b + jr nz, .loop + xor a + ld [wce9f], a + ret + +; reverts settings changed by PrepareForPrinterCommunications +ResetPrinterCommunicationSettings: + push af + call SwitchToCGBDoubleSpeed + ld a, [wce8f] + call BankswitchSRAM + call DisableSRAM + lb de, $30, $bf + call SetupText + pop af + ret + +; send some bytes through serial +Func_1a080: ; unreferenced + ld bc, $0 + lb de, PRINTERPKT_NUL, $0 + jp SendPrinterPacket + +; tries initiating the communications for +; sending data to printer +; returns carry if operation was cancelled +; by pressing B button or serial transfer took long +TryInitPrinterCommunications: + xor a + ld [wPrinterInitAttempts], a +.wait_input + call DoFrame + ldh a, [hKeysHeld] + and B_BUTTON + jr nz, .b_button + ld bc, $0 + lb de, PRINTERPKT_NUL, $0 + call SendPrinterPacket + jr c, .delay + and (1 << PRINTER_STATUS_BUSY) | (1 << PRINTER_STATUS_PRINTING) + jr nz, .wait_input + +.init + ld bc, $0 + lb de, PRINTERPKT_INIT, $0 + call SendPrinterPacket + jr nc, .no_carry + ld hl, wPrinterInitAttempts + inc [hl] + ld a, [hl] + cp 3 + jr c, .wait_input + ; time out + scf + ret +.no_carry + ret + +.b_button + xor a + ld [wPrinterStatus], a + scf + ret + +.delay + ld c, 10 +.delay_loop + call DoFrame + dec c + jr nz, .delay_loop + jr .init + +; loads tiles given by map in hl to sGfxBuffer5 +; copies first 20 tiles, then offsets by 2 tiles +; and copies another 20 +Func_1a0cc: + push bc + ld de, sGfxBuffer5 + call .Copy20Tiles + call .Copy20Tiles + push hl + call CompressDataForPrinterSerialTransfer + call SendPrinterPacket + pop hl + pop bc + ret + +; copies 20 tiles given by hl to de +; then adds 2 tiles to hl +.Copy20Tiles ; 1a0e0 (6:60e0) + push hl + ld c, 20 +.loop_tiles + ld a, [hli] + call .CopyTile + dec c + jr nz, .loop_tiles + pop hl + ld bc, 2 tiles + add hl, bc + ret + +; copies a tile to de +; a = tile to get from sGfxBuffer1 +.CopyTile ; 1a0f0 (6:60f0) + push hl + push bc + ld l, a + ld h, $00 + add hl, hl + add hl, hl + add hl, hl + add hl, hl ; *TILE_SIZE + ld bc, sGfxBuffer1 + add hl, bc + ld c, TILE_SIZE +.loop_copy + ld a, [hli] + ld [de], a + inc de + dec c + jr nz, .loop_copy + pop bc + pop hl + ret + +Func_1a108: + call GetPrinterContrastSerialData + push hl + lb hl, $3, $1 + jr SendPrinterInstructionPacket + +Func_1a111: + call GetPrinterContrastSerialData + push hl + ld hl, wce9b + ld a, [hl] + ld [hl], $00 + ld h, a + ld l, $01 +; fallthrough + +SendPrinterInstructionPacket: + push hl + ld bc, $0 + lb de, PRINTERPKT_DATA, $0 + call SendPrinterPacket + jr c, .asm_1a135 + ld hl, sp+$00 ; contrast level bytes + ld bc, $4 ; instruction packets are 4 bytes in size + lb de, PRINTERPKT_PRINT_INSTRUCTION, $0 + call SendPrinterPacket +.asm_1a135 + pop hl + pop hl + ret + +; returns in h and l the bytes +; to be sent through serial to the printer +; for the set contrast level +GetPrinterContrastSerialData: + ld a, [wPrinterContrastLevel] + ld e, a + ld d, $00 + ld hl, .contrast_level_data + add hl, de + ld h, [hl] + ld l, $e4 + ret + +.contrast_level_data + db $00, $20, $40, $60, $7f + +Func_1a14b: ; unreferenced + ld a, $01 + jr .asm_1a15d + ld a, $02 + jr .asm_1a15d + ld a, $03 + jr .asm_1a15d + ld a, $04 + jr .asm_1a15d + ld a, $05 +.asm_1a15d + ld [wce9d], a + scf + ret + +; a = saved deck index to print +_PrintDeckConfiguration: +; copies selected deck from SRAM to wDuelTempList + call EnableSRAM + ld l, a + ld h, DECK_STRUCT_SIZE + call HtimesL + ld de, sSavedDeck1 + add hl, de + ld de, wDuelTempList + ld bc, DECK_STRUCT_SIZE + call CopyDataHLtoDE + call DisableSRAM + + call ShowPrinterTransmitting + call PrepareForPrinterCommunications + call Func_1a025 + call Func_212f + lb de, 0, 64 + lb bc, 20, 4 + call DrawRegularTextBoxDMG + lb de, 4, 66 + call InitTextPrinting + ld hl, wDuelTempList ; print deck name + call ProcessText + ldtx hl, DeckPrinterText + call ProcessTextFromID + + ld a, 5 + ld [wPrinterHorizontalOffset], a + ld hl, wPrinterTotalCardCount + xor a + ld [hli], a + ld [hl], a + ld [wPrintOnlyStarRarity], a + + ld hl, wCurDeckCards +.loop_cards + ld a, [hl] + or a + jr z, .asm_1a1d6 + ld e, a + ld d, $00 + call LoadCardDataToBuffer1_FromCardID + + ; find out this card's count + ld a, [hli] + ld b, a + ld c, 1 +.loop_card_count + cp [hl] + jr nz, .got_card_count + inc hl + inc c + jr .loop_card_count + +.got_card_count + ld a, c + ld [wPrinterCardCount], a + call LoadCardInfoForPrinter + call AddToPrinterGfxBuffer + jr c, .printer_error + jr .loop_cards + +.asm_1a1d6 + call SendCardListToPrinter + jr c, .printer_error + call ResetPrinterCommunicationSettings + call RestoreVBlankFunction + or a + ret + +.printer_error + call ResetPrinterCommunicationSettings + call RestoreVBlankFunction + jp HandlePrinterError + +SendCardListToPrinter: + ld a, [wPrinterHorizontalOffset] + cp 1 + jr z, .skip_load_gfx + call LoadGfxBufferForPrinter + ret c +.skip_load_gfx + call TryInitPrinterCommunications + ret c + call Func_1a108 + ret +; 0z1a1ff + +; increases printer horizontal offset by 2 +AddToPrinterGfxBuffer: + push hl + ld hl, wPrinterHorizontalOffset + inc [hl] + inc [hl] + ld a, [hl] + pop hl + ; return no carry if below 18 + cp 18 + ccf + ret nc + ; >= 18 +; fallthrough + +; copies Gfx to Gfx buffer and sends some serial data +; returns carry set if unsuccessful +LoadGfxBufferForPrinter: + push hl + call TryInitPrinterCommunications + jr c, .set_carry + ld a, [wPrinterHorizontalOffset] + srl a + ld c, a + ld hl, sGfxBuffer0 +.loop_gfx_buffer + call Func_1a0cc + jr c, .set_carry + dec c + jr nz, .loop_gfx_buffer + call Func_1a111 + jr c, .set_carry + + call ClearPrinterGfxBuffer + ld a, 1 + ld [wPrinterHorizontalOffset], a + pop hl + or a + ret + +.set_carry + pop hl + scf + ret + +; load symbol, name, level and card count to buffer +LoadCardInfoForPrinter: + push hl + ld a, [wPrinterHorizontalOffset] + or %1000000 + ld e, a + ld d, 3 + ld a, [wPrintOnlyStarRarity] + or a + jr nz, .skip_card_symbol + ld hl, wPrinterTotalCardCount + ld a, [hli] + or [hl] + call z, DrawCardSymbol +.skip_card_symbol + ld a, 14 + call CopyCardNameAndLevel + call InitTextPrinting + ld hl, wDefaultText + call ProcessText + ld a, [wPrinterHorizontalOffset] + or %1000000 + ld c, a + ld b, 16 + ld a, SYM_CROSS + call WriteByteToBGMap0 + inc b + ld a, [wPrinterCardCount] + bank1call WriteTwoDigitNumberInTxSymbolFormat + pop hl + ret + +_PrintCardList: +; if Select button is held when printing card list +; only print cards with Star rarity (excluding Promotional cards) +; even if it's not marked as seen in the collection + ld e, FALSE + ldh a, [hKeysHeld] + and SELECT + jr z, .no_select + inc e ; TRUE +.no_select + ld a, e + ld [wPrintOnlyStarRarity], a + + call ShowPrinterTransmitting + call CreateTempCardCollection + ld de, wDefaultText + call CopyPlayerName + call PrepareForPrinterCommunications + call Func_1a025 + call Func_212f + + lb de, 0, 64 + lb bc, 20, 4 + call DrawRegularTextBoxDMG + ld a, PLAYER_TURN + ldh [hWhoseTurn], a + lb de, 2, 66 + call InitTextPrinting + ld hl, wDefaultText + call ProcessText + ldtx hl, AllCardsOwnedText + call ProcessTextFromID + ld a, [wPrintOnlyStarRarity] + or a + jr z, .asm_1a2c2 + ld a, TX_HALF2FULL + call ProcessSpecialTextCharacter + lb de, 3, 84 + call Func_22ca +.asm_1a2c2 + ld a, $ff + ld [wCurPrinterCardType], a + xor a + ld hl, wPrinterTotalCardCount + ld [hli], a + ld [hl], a + ld [wPrinterNumCardTypes], a + ld a, 5 + ld [wPrinterHorizontalOffset], a + + ld e, GRASS_ENERGY +.loop_cards + push de + ld d, $00 + call LoadCardDataToBuffer1_FromCardID + jr c, .done_card_loop + ld d, HIGH(wTempCardCollection) + ld a, [de] ; card ID count in collection + ld [wPrinterCardCount], a + call .LoadCardTypeEntry + jr c, .printer_error_pop_de + + ld a, [wPrintOnlyStarRarity] + or a + jr z, .all_owned_cards_mode + ld a, [wLoadedCard1Set] + and %11110000 + cp PROMOTIONAL + jr z, .next_card + ld a, [wLoadedCard1Rarity] + cp STAR + jr nz, .next_card + ; not Promotional, and Star rarity + ld hl, wPrinterCardCount + res CARD_NOT_OWNED_F, [hl] + jr .got_card_count + +.all_owned_cards_mode + ld a, [wPrinterCardCount] + or a + jr z, .next_card + cp CARD_NOT_OWNED + jr z, .next_card ; ignore not owned cards + +.got_card_count + ld a, [wPrinterCardCount] + and CARD_COUNT_MASK + ld c, a + + ; add to total card count + ld hl, wPrinterTotalCardCount + add [hl] + ld [hli], a + ld a, 0 + adc [hl] + ld [hl], a + + ; add to current card type count + ld hl, wPrinterCurCardTypeCount + ld a, c + add [hl] + ld [hli], a + ld a, 0 + adc [hl] + ld [hl], a + + ld hl, wPrinterNumCardTypes + inc [hl] + ld hl, wce98 + inc [hl] + call LoadCardInfoForPrinter + call AddToPrinterGfxBuffer + jr c, .printer_error_pop_de +.next_card + pop de + inc e + jr .loop_cards + +.printer_error_pop_de + pop de +.printer_error + call ResetPrinterCommunicationSettings + call RestoreVBlankFunction + jp HandlePrinterError + +.done_card_loop + pop de + ; add separator line + ld a, [wPrinterHorizontalOffset] + dec a + or $40 + ld c, a + ld b, 0 + call BCCoordToBGMap0Address + ld a, $35 + lb de, $35, $35 + ld b, 20 + call CopyLine + call AddToPrinterGfxBuffer + jr c, .printer_error + + ld hl, wPrinterTotalCardCount + ld c, [hl] + inc hl + ld b, [hl] + ldtx hl, TotalNumberOfCardsText + call .PrintTextWithNumber + jr c, .printer_error + ld a, [wPrintOnlyStarRarity] + or a + jr nz, .done + ld a, [wPrinterNumCardTypes] + ld c, a + ld b, 0 + ldtx hl, TypesOfCardsText + call .PrintTextWithNumber + jr c, .printer_error + +.done + call SendCardListToPrinter + jr c, .printer_error + call ResetPrinterCommunicationSettings + call RestoreVBlankFunction + or a + ret + +; prints text ID given in hl +; with decimal representation of +; the number given in bc +; hl = text ID +; bc = number +.PrintTextWithNumber + push bc + ld a, [wPrinterHorizontalOffset] + dec a + or $40 + ld e, a + ld d, 2 + call InitTextPrinting + call ProcessTextFromID + ld d, 14 + call InitTextPrinting + pop hl + call TwoByteNumberToTxSymbol_TrimLeadingZeros + ld hl, wStringBuffer + call ProcessText + call AddToPrinterGfxBuffer + ret + +; loads this card's type icon and text +; if it's a new card type that hasn't been printed yet +.LoadCardTypeEntry + ld a, [wLoadedCard1Type] + ld c, a + cp TYPE_ENERGY + jr c, .got_type ; jump if Pokemon card + ld c, $08 + cp TYPE_TRAINER + jr nc, .got_type ; jump if Trainer card + ld c, $07 +.got_type + ld hl, wCurPrinterCardType + ld a, [hl] + cp c + ret z ; already handled this card type + + ; show corresponding icon and text + ; for this new card type + ld a, c + ld [hl], a ; set it as current card type + add a + add c ; *3 + ld c, a + ld b, $00 + ld hl, .IconTextList + add hl, bc + ld a, [wPrinterHorizontalOffset] + dec a + or %1000000 + ld e, a + ld d, 1 + ld a, [hli] + push hl + lb bc, 2, 2 + lb hl, 1, 2 + call FillRectangle + pop hl + ld d, 3 + inc e + call InitTextPrinting + ld a, [hli] + ld h, [hl] + ld l, a + call ProcessTextFromID + + call AddToPrinterGfxBuffer + ld hl, wPrinterCurCardTypeCount + xor a + ld [hli], a + ld [hl], a + ld [wce98], a + ret + +.IconTextList + ; Fire + db $e0 ; icon tile + tx FirePokemonText + + ; Grass + db $e4 ; icon tile + tx GrassPokemonText + + ; Lightning + db $e8 ; icon tile + tx LightningPokemonText + + ; Water + db $ec ; icon tile + tx WaterPokemonText + + ; Fighting + db $f0 ; icon tile + tx FightingPokemonText + + ; Psychic + db $f4 ; icon tile + tx PsychicPokemonText + + ; Colorless + db $f8 ; icon tile + tx ColorlessPokemonText + + ; Energy + db $fc ; icon tile + tx EnergyCardText + + ; Trainer + db $dc ; icon tile + tx TrainerCardText + +ShowPrinterTransmitting: + call SetSpriteAnimationsAsVBlankFunction + ld a, SCENE_GAMEBOY_PRINTER_TRANSMITTING + lb bc, 0, 0 + call LoadScene + ldtx hl, NowPrintingPleaseWaitText + call DrawWideTextBox_PrintText + call EnableLCD + ret + +; compresses $28 tiles in sGfxBuffer5 +; and writes it in sGfxBuffer5 + $28 tiles. +; compressed data has 2 commands to instruct on how to decompress it. +; - a command byte with bit 7 not set, means to copy that many + 1 +; bytes that are following it literally. +; - a command byte with bit 7 set, means to copy the following byte +; that many times + 2 (after masking the top bit of command byte). +; returns in bc the size of the compressed data and +; in de the packet type data. +CompressDataForPrinterSerialTransfer: + ld hl, sGfxBuffer5 + ld de, sGfxBuffer5 + $28 tiles + ld bc, $28 tiles +.loop_remaining_data + ld a, $ff + inc b + dec b + jr nz, .check_compression + ld a, c +.check_compression + push bc + push de + ld c, a + call CheckDataCompression + ld a, e + ld c, e + pop de + jr c, .copy_byte + ld a, c + ld b, c + dec a + ld [de], a ; number of bytes to copy literally - 1 + inc de +.copy_literal_sequence + ld a, [hli] + ld [de], a + inc de + dec c + jr nz, .copy_literal_sequence + ld c, b + jr .sub_added_bytes + +.copy_byte + ld a, c + dec a + dec a + or %10000000 ; set high bit + ld [de], a ; = (n times to copy - 2) | %10000000 + inc de + ld a, [hl] ; byte to copy n times + ld [de], a + inc de + ld b, $0 + add hl, bc + +.sub_added_bytes + ld a, c + cpl + inc a + pop bc + add c + ld c, a + ld a, $ff + adc b + ld b, a + or c + jr nz, .loop_remaining_data + + ld hl, $10000 - (sGfxBuffer5 + $28 tiles) + add hl, de ; gets the size of the compressed data + ld c, l + ld b, h + ld hl, sGfxBuffer5 + $28 tiles + lb de, PRINTERPKT_DATA, $1 + ret + +; checks whether the next byte sequence in hl, up to c bytes, can be compressed +; returns carry if the next sequence of bytes can be compressed, +; i.e. has at least 3 consecutive bytes with the same value. +; in that case, returns in e the number of consecutive +; same value bytes that were found. +; if there are no bytes with same value, then count as many bytes left +; as possible until either there are no more remaining data bytes, +; or until a sequence of 3 bytes with the same value are found. +; in that case, the number of bytes in this sequence is returned in e. +CheckDataCompression: + push hl + ld e, c + ld a, c +; if number of remaining bytes is less than 4 +; then no point in compressing + cp 4 + jr c, .no_carry + +; check first if there are at least +; 3 consecutive bytes with the same value + ld b, c + ld a, [hli] + cp [hl] + inc hl + jr nz, .literal_copy ; not same + cp [hl] + inc hl + jr nz, .literal_copy ; not same + +; 3 consecutive bytes were found with same value +; keep track of how many consecutive bytes +; with the same value there are in e + dec c + dec c + dec c + ld e, 3 +.loop_same_value + cp [hl] + jr nz, .set_carry ; exit when a different byte is found + inc hl + inc e + dec c + jr z, .set_carry ; exit when there is no more remaining data + bit 5, e + ; exit if number of consecutive bytes >= $20 + jr z, .loop_same_value +.set_carry + pop hl + scf + ret + +.literal_copy +; consecutive bytes are not the same value +; count the number of bytes there are left +; until a sequence of 3 bytes with the same value is found + pop hl + push hl + ld c, b ; number of remaining bytes + ld e, 1 + ld a, [hli] + dec c + jr z, .no_carry ; exit if no more data +.reset_same_value_count + ld d, 2 ; number of consecutive same value bytes to exit +.next_byte + inc e + dec c + jr z, .no_carry + bit 7, e + jr nz, .no_carry ; exit if >= $80 + cp [hl] + jr z, .same_consecutive_value + ld a, [hli] + jr .reset_same_value_count +.no_carry + pop hl + or a + ret + +.same_consecutive_value + inc hl + dec d + jr nz, .next_byte + ; 3 consecutive bytes with same value found + ; discard the last 3 bytes in the sequence + dec e + dec e + dec e + jr .no_carry diff --git a/src/engine/menus/booster_pack.asm b/src/engine/menus/booster_pack.asm new file mode 100644 index 0000000..934b730 --- /dev/null +++ b/src/engine/menus/booster_pack.asm @@ -0,0 +1,42 @@ +_OpenBoosterPack: + ld a, PLAYER_TURN + ldh [hWhoseTurn], a +; clears DECK_SIZE bytes starting from wPlayerDuelVariables + ld h, a + ld l, $00 +.loop_clear + xor a + ld [hli], a + ld a, l + cp DECK_SIZE + jr c, .loop_clear + +; fills wDuelTempList with 0, 1, 2, 3, ... +; up to the number of cards received in Boster Pack + xor a + ld hl, wBoosterCardsDrawn + ld de, wDuelTempList + ld c, $00 +.loop_index_sequence + ld a, [hli] + or a + jr z, .done_index_sequence + ld a, c + ld [de], a + inc de + inc c + jr .loop_index_sequence +.done_index_sequence + ld a, $ff ; terminator byte + ld [de], a + + lb de, $38, $9f + call SetupText + bank1call InitAndDrawCardListScreenLayout + ldtx hl, ChooseTheCardYouWishToExamineText + ldtx de, BoosterPackText + bank1call SetCardListHeaderText + ld a, A_BUTTON | START + ld [wNoItemSelectionMenuKeys], a + bank1call DisplayCardList + ret diff --git a/src/engine/menus/common.asm b/src/engine/menus/common.asm index 069d168..60ad0a9 100644 --- a/src/engine/menus/common.asm +++ b/src/engine/menus/common.asm @@ -19,8 +19,8 @@ DoCardPop: farcall _DoCardPop ret -Func_7576: - farcall Func_1991f +AddStarterDeck: + farcall _AddStarterDeck ret PreparePrinterConnection: @@ -43,8 +43,8 @@ SetUpAndStartLinkDuel: farcall _SetUpAndStartLinkDuel ret -Func_7594: - farcall Func_1a61f +ShowPromotionalCardScreen: + farcall _ShowPromotionalCardScreen ret OpenBoosterPack: diff --git a/src/engine/menus/glossary.asm b/src/engine/menus/glossary.asm new file mode 100644 index 0000000..78f44dd --- /dev/null +++ b/src/engine/menus/glossary.asm @@ -0,0 +1,221 @@ +OpenGlossaryScreen: + xor a + ld [wGlossaryPageNo], a + call .display_menu + + xor a + ld [wInPlayAreaCurPosition], a + ld de, OpenGlossaryScreen_TransitionTable ; this data is stored in bank 2. + ld hl, wMenuInputTablePointer + ld [hl], e + inc hl + ld [hl], d + ld a, $ff + ld [wDuelInitialPrizesUpperBitsSet], a + xor a + ld [wCheckMenuCursorBlinkCounter], a +.next + ld a, $01 + ld [wVBlankOAMCopyToggle], a + call DoFrame + ldh a, [hKeysPressed] + and SELECT + jr nz, .on_select + + farcall YourOrOppPlayAreaScreen_HandleInput + jr nc, .next + + cp -1 ; b button + jr nz, .check_button + + farcall ZeroObjectPositionsWithCopyToggleOn + ret + +.check_button + push af + farcall ZeroObjectPositionsWithCopyToggleOn + pop af + + cp $09 ; $09: next page or prev page + jr z, .change_page + + call .print_description + call .display_menu + xor a + ld [wCheckMenuCursorBlinkCounter], a + jr .next + +.on_select + ld a, $01 + farcall PlaySFXConfirmOrCancel +.change_page + ld a, [wGlossaryPageNo] + xor $01 ; swap page + ld [wGlossaryPageNo], a + call .print_menu + jr .next + +; display glossary menu. +.display_menu ; 1852b (6:452b) + xor a + ld [wTileMapFill], a + call ZeroObjectPositions + ld a, $01 + ld [wVBlankOAMCopyToggle], a + call DoFrame + call EmptyScreen + call Set_OBJ_8x8 + farcall LoadCursorTile + + lb de, 5, 0 + call InitTextPrinting + ldtx hl, PokemonCardGlossaryText + call ProcessTextFromID + call .print_menu + ldtx hl, ChooseWordAndPressAButtonText + call DrawWideTextBox_PrintText + ret + +; print texts in glossary menu. +.print_menu ; 1855a (6:455a) + ld hl, wDefaultText + + ld a, TX_SYMBOL + ld [hli], a + + ld a, [wGlossaryPageNo] + add SYM_1 + ld [hli], a + + ld a, TX_SYMBOL + ld [hli], a + + ld a, SYM_SLASH + ld [hli], a + + ld a, TX_SYMBOL + ld [hli], a + + ld a, SYM_2 + ld [hli], a + + ld [hl], TX_END + + lb de, 16, 1 + call InitTextPrinting + ld hl, wDefaultText + call ProcessText + + lb de, 1, 3 + call InitTextPrinting + ld a, [wGlossaryPageNo] + or a + jr nz, .page_two + + ldtx hl, GlossaryMenuPage1Text + jr .page_one + +.page_two + ldtx hl, GlossaryMenuPage2Text +.page_one + call ProcessTextFromID + ret + +; display glossary description. +.print_description ; 18598 (6:4598) + push af + xor a + ld [wTileMapFill], a + call EmptyScreen + lb de, 5, 0 + call InitTextPrinting + ldtx hl, PokemonCardGlossaryText + call ProcessTextFromID + lb de, 0, 4 + lb bc, 20, 14 + call DrawRegularTextBox + + ld a, [wGlossaryPageNo] + or a + jr nz, .back_page + + ld hl, GlossaryData1 + jr .front_page + +.back_page + ld hl, GlossaryData2 +.front_page + pop af + ; hl += (a + (a << 2)). + ; that is, + ; hl += (5 * a). + ld c, a + ld b, 0 + add hl, bc + sla a + sla a + ld c, a + add hl, bc + ld a, [hli] + push hl + ld d, a + ld e, $02 + call InitTextPrinting + ld a, [hli] + ld h, [hl] + ld l, a + call ProcessTextFromID + pop hl + lb de, 1, 5 + call InitTextPrinting + inc hl + inc hl + ld a, [hli] + ld h, [hl] + ld l, a + ld a, $01 + ld [wLineSeparation], a + call ProcessTextFromID + xor a + ld [wLineSeparation], a + call EnableLCD +.loop + call DoFrame + ldh a, [hKeysPressed] + and B_BUTTON + jr z, .loop + + ld a, -1 + farcall PlaySFXConfirmOrCancel + ret + +; unit: 5 bytes. +; [structure] +; horizontal align (1) / title text id (2) / desc. text id (2) +glossary_entry: MACRO + db \1 + tx \2 + tx \3 +ENDM + +GlossaryData1: + glossary_entry 7, AboutTheDeckText, DeckDescriptionText + glossary_entry 5, AboutTheDiscardPileText, DiscardPileDescriptionText + glossary_entry 7, AboutTheHandText, HandDescriptionText + glossary_entry 6, AboutTheArenaText, ArenaDescriptionText + glossary_entry 6, AboutTheBenchText, BenchDescriptionText + glossary_entry 4, AboutTheActivePokemonText, ActivePokemonDescriptionText + glossary_entry 5, AboutBenchPokemonText, BenchPokemonDescriptionText + glossary_entry 7, AboutPrizesText, PrizesDescriptionText + glossary_entry 5, AboutDamageCountersText, DamageCountersDescriptionText + +GlossaryData2: + glossary_entry 5, AboutEnergyCardsText, EnergyCardsDescriptionText + glossary_entry 5, AboutTrainerCardsText, TrainerCardsDescriptionText + glossary_entry 5, AboutBasicPokemonText, BasicPokemonDescriptionText + glossary_entry 5, AboutEvolutionCardsText, EvolutionCardsDescriptionText + glossary_entry 6, AboutAttackingText, AttackingDescriptionText + glossary_entry 5, AboutPokemonPowerText, PokemonPowerDescriptionText + glossary_entry 6, AboutWeaknessText, WeaknessDescriptionText + glossary_entry 6, AboutResistanceText, ResistanceDescriptionText + glossary_entry 6, AboutRetreatingText, RetreatingDescriptionText diff --git a/src/engine/menus/play_area.asm b/src/engine/menus/play_area.asm new file mode 100644 index 0000000..047d24f --- /dev/null +++ b/src/engine/menus/play_area.asm @@ -0,0 +1,570 @@ +; this function is called when the player is shown the "In Play Area" screen. +; it can be called with either the select button (DuelMenuShortcut_BothActivePokemon), +; or via the "In Play Area" item of the Check menu (DuelCheckMenu_InPlayArea) +OpenInPlayAreaScreen: + ld a, INPLAYAREA_PLAYER_ACTIVE + ld [wInPlayAreaCurPosition], a +.start + xor a + ld [wCheckMenuCursorBlinkCounter], a + farcall DrawInPlayAreaScreen + call EnableLCD + call IsClairvoyanceActive + jr c, .clairvoyance_on + + ld de, OpenInPlayAreaScreen_TransitionTable1 + jr .clairvoyance_off + +.clairvoyance_on + ld de, OpenInPlayAreaScreen_TransitionTable2 +.clairvoyance_off + ld hl, wMenuInputTablePointer + ld [hl], e + inc hl + ld [hl], d + ld a, [wInPlayAreaCurPosition] + call .print_associated_text +.on_frame + ld a, $01 + ld [wVBlankOAMCopyToggle], a + call DoFrame + + ldh a, [hDPadHeld] + and START + jr nz, .selection + + ; if this function's been called from 'select' button, + ; wInPlayAreaFromSelectButton is on. + ld a, [wInPlayAreaFromSelectButton] + or a + jr z, .handle_input ; if it's from the Check menu, jump. + + ldh a, [hDPadHeld] + and SELECT + jr nz, .skip_input + +.handle_input + ld a, [wInPlayAreaCurPosition] + ld [wInPlayAreaTemporaryPosition], a + call OpenInPlayAreaScreen_HandleInput + jr c, .pressed + + ld a, [wInPlayAreaCurPosition] + cp INPLAYAREA_PLAYER_PLAY_AREA + jp z, .show_turn_holder_play_area + cp INPLAYAREA_OPP_PLAY_AREA + jp z, .show_non_turn_holder_play_area + + ; check if the cursor moved. + ld hl, wInPlayAreaTemporaryPosition + cp [hl] + call nz, .print_associated_text + + jr .on_frame + +.pressed + cp -1 + jr nz, .selection + + ; pressed b button. + call ZeroObjectPositionsAndToggleOAMCopy_Bank6 + lb de, $38, $9f + call SetupText + scf + ret + +.skip_input + call ZeroObjectPositionsAndToggleOAMCopy_Bank6 + lb de, $38, $9f + call SetupText + or a + ret + +.selection ; pressed a button or start button. + call ZeroObjectPositionsAndToggleOAMCopy_Bank6 + lb de, $38, $9f + call SetupText + ld a, [wInPlayAreaCurPosition] + ld [wInPlayAreaPreservedPosition], a + ld hl, .jump_table + call JumpToFunctionInTable + ld a, [wInPlayAreaPreservedPosition] + ld [wInPlayAreaCurPosition], a + + jp .start + +.print_associated_text ; 18171 (6:4171) +; each position has a text associated to it, +; which is printed at the bottom of the screen + push af + lb de, 1, 17 + call InitTextPrinting + ldtx hl, EmptyLineText + call ProcessTextFromID + + ld hl, hffb0 + ld [hl], $01 + ldtx hl, HandText_2 + call ProcessTextFromID + + ld hl, hffb0 + ld [hl], $00 + lb de, 1, 17 + call InitTextPrinting + pop af + ld hl, OpenInPlayAreaScreen_TextTable + ld b, 0 + sla a + ld c, a + add hl, bc + + ; hl = OpenInPlayAreaScreen_TextTable + 2 * (wInPlayAreaCurPosition) + ld a, [hli] + ld h, [hl] + ld l, a + ld a, h + + ; jump ahead if entry does not contain null text (it's not active pokemon) + or a + jr nz, .print_hand_or_discard_pile + + ld a, l + ; bench slots have dummy text IDs assigned to them, which are never used. + ; these are secretly not text id's, but rather, 2-byte PLAY_AREA_BENCH_* constants + ; check if the value at register l is one of those, and jump ahead if not + cp PLAY_AREA_BENCH_5 + $01 + jr nc, .print_hand_or_discard_pile + +; if we make it here, we need to print a Pokemon card name. +; wInPlayAreaCurPosition determines which duelist +; and l contains the PLAY_AREA_* location of the card. + ld a, [wInPlayAreaCurPosition] + cp INPLAYAREA_PLAYER_HAND + jr nc, .opponent_side + + ld a, l + add DUELVARS_ARENA_CARD + call GetTurnDuelistVariable + cp -1 + ret z + + call GetCardIDFromDeckIndex + call LoadCardDataToBuffer1_FromCardID + jr .display_card_name + +.opponent_side + ld a, l + add DUELVARS_ARENA_CARD + call GetNonTurnDuelistVariable + cp -1 + ret z + + call SwapTurn + call GetCardIDFromDeckIndex + call LoadCardDataToBuffer1_FromCardID + call SwapTurn + +.display_card_name + ld a, 18 + call CopyCardNameAndLevel + ld hl, wDefaultText + call ProcessText + ret + +.print_hand_or_discard_pile +; if we make it here, cursor position is to Hand or Discard Pile +; so DuelistHandText_2 or DuelistDiscardPileText will be printed + + ld a, [wInPlayAreaCurPosition] + cp INPLAYAREA_OPP_ACTIVE + jr nc, .opp_side_print_hand_or_discard_pile + call PrintTextNoDelay + ret + +.opp_side_print_hand_or_discard_pile + call SwapTurn + call PrintTextNoDelay + call SwapTurn + ret + +.show_turn_holder_play_area + lb de, $38, $9f + call SetupText + ldh a, [hWhoseTurn] + push af + bank1call OpenTurnHolderPlayAreaScreen + pop af + ldh [hWhoseTurn], a + ld a, [wInPlayAreaPreservedPosition] + ld [wInPlayAreaCurPosition], a + jp .start + +.show_non_turn_holder_play_area + lb de, $38, $9f + call SetupText + ldh a, [hWhoseTurn] + push af + bank1call OpenNonTurnHolderPlayAreaScreen + pop af + ldh [hWhoseTurn], a + ld a, [wInPlayAreaPreservedPosition] + ld [wInPlayAreaCurPosition], a + jp .start + +.jump_table ; (6:4228) + dw OpenInPlayAreaScreen_TurnHolderPlayArea ; 0x00: INPLAYAREA_PLAYER_BENCH_1 + dw OpenInPlayAreaScreen_TurnHolderPlayArea ; 0x01: INPLAYAREA_PLAYER_BENCH_2 + dw OpenInPlayAreaScreen_TurnHolderPlayArea ; 0x02: INPLAYAREA_PLAYER_BENCH_3 + dw OpenInPlayAreaScreen_TurnHolderPlayArea ; 0x03: INPLAYAREA_PLAYER_BENCH_4 + dw OpenInPlayAreaScreen_TurnHolderPlayArea ; 0x04: INPLAYAREA_PLAYER_BENCH_5 + dw OpenInPlayAreaScreen_TurnHolderPlayArea ; 0x05: INPLAYAREA_PLAYER_ACTIVE + dw OpenInPlayAreaScreen_TurnHolderHand ; 0x06: INPLAYAREA_PLAYER_HAND + dw OpenInPlayAreaScreen_TurnHolderDiscardPile ; 0x07: INPLAYAREA_PLAYER_DISCARD_PILE + dw OpenInPlayAreaScreen_NonTurnHolderPlayArea ; 0x08: INPLAYAREA_OPP_ACTIVE + dw OpenInPlayAreaScreen_NonTurnHolderHand ; 0x09: INPLAYAREA_OPP_HAND + dw OpenInPlayAreaScreen_NonTurnHolderDiscardPile ; 0x0a: INPLAYAREA_OPP_DISCARD_PILE + dw OpenInPlayAreaScreen_NonTurnHolderPlayArea ; 0x0b: INPLAYAREA_OPP_BENCH_1 + dw OpenInPlayAreaScreen_NonTurnHolderPlayArea ; 0x0c: INPLAYAREA_OPP_BENCH_2 + dw OpenInPlayAreaScreen_NonTurnHolderPlayArea ; 0x0d: INPLAYAREA_OPP_BENCH_3 + dw OpenInPlayAreaScreen_NonTurnHolderPlayArea ; 0x0e: INPLAYAREA_OPP_BENCH_4 + dw OpenInPlayAreaScreen_NonTurnHolderPlayArea ; 0x0f: INPLAYAREA_OPP_BENCH_5 + +OpenInPlayAreaScreen_TurnHolderPlayArea: + ; wInPlayAreaCurPosition constants conveniently map to (PLAY_AREA_* constants - 1) + ; for bench locations. this mapping is taken for granted in the following code. + ld a, [wInPlayAreaCurPosition] + inc a + cp INPLAYAREA_PLAYER_ACTIVE + $01 + jr nz, .on_bench + xor a ; PLAY_AREA_ARENA +.on_bench + ld [wCurPlayAreaSlot], a + add DUELVARS_ARENA_CARD + call GetTurnDuelistVariable + cp -1 + ret z + call GetCardIDFromDeckIndex + call LoadCardDataToBuffer1_FromCardID + xor a + ld [wCurPlayAreaY], a + bank1call OpenCardPage_FromCheckPlayArea + ret + +OpenInPlayAreaScreen_NonTurnHolderPlayArea: + ld a, [wInPlayAreaCurPosition] + sub INPLAYAREA_OPP_ACTIVE + or a + jr z, .active + ; convert INPLAYAREA_OPP_BENCH_* constant to PLAY_AREA_BENCH_* constant + sub INPLAYAREA_OPP_BENCH_1 - INPLAYAREA_OPP_ACTIVE - PLAY_AREA_BENCH_1 +.active + ld [wCurPlayAreaSlot], a + add DUELVARS_ARENA_CARD + call GetNonTurnDuelistVariable + cp -1 + ret z + call SwapTurn + call GetCardIDFromDeckIndex + call LoadCardDataToBuffer1_FromCardID + xor a + ld [wCurPlayAreaY], a + bank1call OpenCardPage_FromCheckPlayArea + call SwapTurn + ret + +OpenInPlayAreaScreen_TurnHolderHand: + ldh a, [hWhoseTurn] + push af + bank1call OpenTurnHolderHandScreen_Simple + pop af + ldh [hWhoseTurn], a + ret + +OpenInPlayAreaScreen_NonTurnHolderHand: + ldh a, [hWhoseTurn] + push af + bank1call OpenNonTurnHolderHandScreen_Simple + pop af + ldh [hWhoseTurn], a + ret + +OpenInPlayAreaScreen_TurnHolderDiscardPile: + ldh a, [hWhoseTurn] + push af + bank1call OpenTurnHolderDiscardPileScreen + pop af + ldh [hWhoseTurn], a + ret + +OpenInPlayAreaScreen_NonTurnHolderDiscardPile: + ldh a, [hWhoseTurn] + push af + bank1call OpenNonTurnHolderDiscardPileScreen + pop af + ldh [hWhoseTurn], a + ret + +OpenInPlayAreaScreen_TextTable: +; note that for bench slots, the entries are +; PLAY_AREA_BENCH_* constants in practice + tx HandText ; INPLAYAREA_PLAYER_BENCH_1 + tx CheckText ; INPLAYAREA_PLAYER_BENCH_2 + tx AttackText ; INPLAYAREA_PLAYER_BENCH_3 + tx PKMNPowerText ; INPLAYAREA_PLAYER_BENCH_4 + tx DoneText ; INPLAYAREA_PLAYER_BENCH_5 + dw NULL ; INPLAYAREA_PLAYER_ACTIVE + tx DuelistHandText_2 ; INPLAYAREA_PLAYER_HAND + tx DuelistDiscardPileText ; INPLAYAREA_PLAYER_DISCARD_PILE + dw NULL ; INPLAYAREA_OPP_ACTIVE + tx DuelistHandText_2 ; INPLAYAREA_OPP_HAND + tx DuelistDiscardPileText ; INPLAYAREA_OPP_DISCARD_PILE + tx HandText ; INPLAYAREA_OPP_BENCH_1 + tx CheckText ; INPLAYAREA_OPP_BENCH_2 + tx AttackText ; INPLAYAREA_OPP_BENCH_3 + tx PKMNPowerText ; INPLAYAREA_OPP_BENCH_4 + tx DoneText ; INPLAYAREA_OPP_BENCH_5 + +in_play_area_cursor_transition: MACRO + cursor_transition \1, \2, \3, INPLAYAREA_\4, INPLAYAREA_\5, INPLAYAREA_\6, INPLAYAREA_\7 +ENDM + +; it's related to wMenuInputTablePointer. +; with this table, the cursor moves into the proper location by the input. +; note that the unit of the position is not a 8x8 tile. +OpenInPlayAreaScreen_TransitionTable1: + in_play_area_cursor_transition $18, $8c, $00, PLAYER_ACTIVE, PLAYER_PLAY_AREA, PLAYER_BENCH_2, PLAYER_BENCH_5 + in_play_area_cursor_transition $30, $8c, $00, PLAYER_ACTIVE, PLAYER_PLAY_AREA, PLAYER_BENCH_3, PLAYER_BENCH_1 + in_play_area_cursor_transition $48, $8c, $00, PLAYER_ACTIVE, PLAYER_PLAY_AREA, PLAYER_BENCH_4, PLAYER_BENCH_2 + in_play_area_cursor_transition $60, $8c, $00, PLAYER_ACTIVE, PLAYER_PLAY_AREA, PLAYER_BENCH_5, PLAYER_BENCH_3 + in_play_area_cursor_transition $78, $8c, $00, PLAYER_ACTIVE, PLAYER_PLAY_AREA, PLAYER_BENCH_1, PLAYER_BENCH_4 + in_play_area_cursor_transition $30, $6c, $00, OPP_ACTIVE, PLAYER_BENCH_1, PLAYER_DISCARD_PILE, PLAYER_DISCARD_PILE + in_play_area_cursor_transition $78, $80, $00, PLAYER_DISCARD_PILE, PLAYER_BENCH_1, PLAYER_ACTIVE, PLAYER_ACTIVE + in_play_area_cursor_transition $78, $70, $00, OPP_ACTIVE, PLAYER_HAND, PLAYER_ACTIVE, PLAYER_ACTIVE + in_play_area_cursor_transition $78, $34, 1 << OAM_X_FLIP, OPP_BENCH_1, PLAYER_ACTIVE, OPP_DISCARD_PILE, OPP_DISCARD_PILE + in_play_area_cursor_transition $30, $20, 1 << OAM_X_FLIP, OPP_BENCH_1, OPP_DISCARD_PILE, OPP_ACTIVE, OPP_ACTIVE + in_play_area_cursor_transition $30, $38, 1 << OAM_X_FLIP, OPP_BENCH_1, PLAYER_ACTIVE, OPP_ACTIVE, OPP_ACTIVE + in_play_area_cursor_transition $90, $14, 1 << OAM_X_FLIP, OPP_PLAY_AREA, OPP_ACTIVE, OPP_BENCH_5, OPP_BENCH_2 + in_play_area_cursor_transition $78, $14, 1 << OAM_X_FLIP, OPP_PLAY_AREA, OPP_ACTIVE, OPP_BENCH_1, OPP_BENCH_3 + in_play_area_cursor_transition $60, $14, 1 << OAM_X_FLIP, OPP_PLAY_AREA, OPP_ACTIVE, OPP_BENCH_2, OPP_BENCH_4 + in_play_area_cursor_transition $48, $14, 1 << OAM_X_FLIP, OPP_PLAY_AREA, OPP_ACTIVE, OPP_BENCH_3, OPP_BENCH_5 + in_play_area_cursor_transition $30, $14, 1 << OAM_X_FLIP, OPP_PLAY_AREA, OPP_ACTIVE, OPP_BENCH_4, OPP_BENCH_1 + +OpenInPlayAreaScreen_TransitionTable2: + in_play_area_cursor_transition $18, $8c, $00, PLAYER_ACTIVE, PLAYER_PLAY_AREA, PLAYER_BENCH_2, PLAYER_BENCH_5 + in_play_area_cursor_transition $30, $8c, $00, PLAYER_ACTIVE, PLAYER_PLAY_AREA, PLAYER_BENCH_3, PLAYER_BENCH_1 + in_play_area_cursor_transition $48, $8c, $00, PLAYER_ACTIVE, PLAYER_PLAY_AREA, PLAYER_BENCH_4, PLAYER_BENCH_2 + in_play_area_cursor_transition $60, $8c, $00, PLAYER_ACTIVE, PLAYER_PLAY_AREA, PLAYER_BENCH_5, PLAYER_BENCH_3 + in_play_area_cursor_transition $78, $8c, $00, PLAYER_ACTIVE, PLAYER_PLAY_AREA, PLAYER_BENCH_1, PLAYER_BENCH_4 + in_play_area_cursor_transition $30, $6c, $00, OPP_ACTIVE, PLAYER_BENCH_1, PLAYER_DISCARD_PILE, PLAYER_DISCARD_PILE + in_play_area_cursor_transition $78, $80, $00, PLAYER_DISCARD_PILE, PLAYER_BENCH_1, PLAYER_ACTIVE, PLAYER_ACTIVE + in_play_area_cursor_transition $78, $70, $00, OPP_ACTIVE, PLAYER_HAND, PLAYER_ACTIVE, PLAYER_ACTIVE + in_play_area_cursor_transition $78, $34, 1 << OAM_X_FLIP, OPP_BENCH_1, PLAYER_ACTIVE, OPP_DISCARD_PILE, OPP_DISCARD_PILE + in_play_area_cursor_transition $30, $20, 1 << OAM_X_FLIP, OPP_BENCH_1, OPP_DISCARD_PILE, OPP_ACTIVE, OPP_ACTIVE + in_play_area_cursor_transition $30, $38, 1 << OAM_X_FLIP, OPP_HAND, PLAYER_ACTIVE, OPP_ACTIVE, OPP_ACTIVE + in_play_area_cursor_transition $90, $14, 1 << OAM_X_FLIP, OPP_PLAY_AREA, OPP_ACTIVE, OPP_BENCH_5, OPP_BENCH_2 + in_play_area_cursor_transition $78, $14, 1 << OAM_X_FLIP, OPP_PLAY_AREA, OPP_ACTIVE, OPP_BENCH_1, OPP_BENCH_3 + in_play_area_cursor_transition $60, $14, 1 << OAM_X_FLIP, OPP_PLAY_AREA, OPP_ACTIVE, OPP_BENCH_2, OPP_BENCH_4 + in_play_area_cursor_transition $48, $14, 1 << OAM_X_FLIP, OPP_PLAY_AREA, OPP_ACTIVE, OPP_BENCH_3, OPP_BENCH_5 + in_play_area_cursor_transition $30, $14, 1 << OAM_X_FLIP, OPP_PLAY_AREA, OPP_ACTIVE, OPP_BENCH_4, OPP_BENCH_1 + +OpenInPlayAreaScreen_HandleInput: + xor a + ld [wPlaysSfx], a + ld hl, wMenuInputTablePointer + ld e, [hl] + inc hl + ld d, [hl] + ld a, [wInPlayAreaCurPosition] + ld l, a + ld h, $07 + call HtimesL + add hl, de + + ldh a, [hDPadHeld] + or a + jp z, .check_button + + inc hl + inc hl + inc hl + + ; check d-pad + bit D_UP_F, a + jr z, .else_if_down + + ; up + ld a, [hl] + jr .process_dpad + +.else_if_down + inc hl + bit D_DOWN_F, a + jr z, .else_if_right + + ; down + ld a, [hl] + jr .process_dpad + +.else_if_right + inc hl + bit D_RIGHT_F, a + jr z, .else_if_left + + ; right + ld a, [hl] + jr .process_dpad + +.else_if_left + inc hl + bit D_LEFT_F, a + jr z, .check_button + + ; left + ld a, [hl] +.process_dpad + push af + ld a, [wInPlayAreaCurPosition] + ld [wInPlayAreaPreservedPosition], a + pop af + + ld [wInPlayAreaCurPosition], a + cp INPLAYAREA_PLAYER_ACTIVE + jr c, .player_area + cp INPLAYAREA_OPP_BENCH_1 + jr c, .next + cp INPLAYAREA_PLAYER_PLAY_AREA + jr c, .opponent_area + + jr .next + +.player_area + ld a, DUELVARS_NUMBER_OF_POKEMON_IN_PLAY_AREA + call GetTurnDuelistVariable + dec a + jr nz, .bench_pokemon_exists + + ; no pokemon in player's bench. + ; then move to player's play area. + ld a, INPLAYAREA_PLAYER_PLAY_AREA + ld [wInPlayAreaCurPosition], a + jr .next + +.bench_pokemon_exists + ld b, a + ld a, [wInPlayAreaCurPosition] + cp b + jr c, .next + + ; handle index overflow + ldh a, [hDPadHeld] + bit D_RIGHT_F, a + jr z, .on_left + + xor a + ld [wInPlayAreaCurPosition], a + jr .next + +.on_left + ld a, b + dec a + ld [wInPlayAreaCurPosition], a + jr .next + +.opponent_area + ld a, DUELVARS_NUMBER_OF_POKEMON_IN_PLAY_AREA + call GetNonTurnDuelistVariable + dec a + jr nz, .bench_pokemon_exists_2 + + ld a, INPLAYAREA_OPP_PLAY_AREA + ld [wInPlayAreaCurPosition], a + jr .next + +.bench_pokemon_exists_2 + ld b, a + ld a, [wInPlayAreaCurPosition] + sub INPLAYAREA_OPP_BENCH_1 + cp b + jr c, .next + + ldh a, [hDPadHeld] + bit D_LEFT_F, a + jr z, .on_right + + ld a, INPLAYAREA_OPP_BENCH_1 + ld [wInPlayAreaCurPosition], a + jr .next + +.on_right + ld a, b + add INPLAYAREA_OPP_DISCARD_PILE + ld [wInPlayAreaCurPosition], a +.next + ld a, $01 + ld [wPlaysSfx], a + xor a + ld [wCheckMenuCursorBlinkCounter], a +.check_button + ldh a, [hKeysPressed] + and A_BUTTON | B_BUTTON + jr z, .return + + and A_BUTTON + jr nz, .a_button + + ; pressed b button + ld a, -1 + farcall PlaySFXConfirmOrCancel + scf + ret + +.a_button + call .draw_cursor + ld a, $01 + farcall PlaySFXConfirmOrCancel + ld a, [wInPlayAreaCurPosition] + scf + ret + +.return + ld a, [wPlaysSfx] + or a + jr z, .skip_sfx + call PlaySFX +.skip_sfx + ld hl, wCheckMenuCursorBlinkCounter + ld a, [hl] + inc [hl] + and $10 - 1 + ret nz + + bit 4, [hl] ; = and $10 + jr nz, ZeroObjectPositionsAndToggleOAMCopy_Bank6 + +.draw_cursor ; 184a0 (6:44a0) + call ZeroObjectPositions + ld hl, wMenuInputTablePointer + ld e, [hl] + inc hl + ld d, [hl] + ld a, [wInPlayAreaCurPosition] + ld l, a + ld h, $07 + call HtimesL + add hl, de + + ld d, [hl] ; x position. + inc hl + ld e, [hl] ; y position. + inc hl + ld b, [hl] ; attribute. + ld c, $00 + call SetOneObjectAttributes + or a + ret + +ZeroObjectPositionsAndToggleOAMCopy_Bank6: + call ZeroObjectPositions + ld a, $01 + ld [wVBlankOAMCopyToggle], a + ret diff --git a/src/engine/menus/unknown.asm b/src/engine/menus/unknown.asm new file mode 100644 index 0000000..c6f04fa --- /dev/null +++ b/src/engine/menus/unknown.asm @@ -0,0 +1,103 @@ +Func_18661: ; unreferenced + xor a + ld [wPlaysSfx], a + ld a, [wCheckMenuCursorXPosition] + ld d, a + ld a, [wCheckMenuCursorYPosition] + ld e, a + ldh a, [hDPadHeld] + or a + jr z, .check_button +; check input from dpad + bit D_LEFT_F, a + jr nz, .left_or_right + bit D_RIGHT_F, a + jr z, .check_up_and_down +.left_or_right +; swap the lsb of x position value. + ld a, d + xor $1 + ld d, a + jr .cursor_moved + +.check_up_and_down + bit D_UP_F, a + jr nz, .up_or_down + bit D_DOWN_F, a + jr z, .check_button +.up_or_down + ld a, e + xor $1 + ld e, a +.cursor_moved + ld a, $1 + ld [wPlaysSfx], a + push de + call .draw_blank_cursor + pop de + ld a, d + ld [wCheckMenuCursorXPosition], a + ld a, e + ld [wCheckMenuCursorYPosition], a + xor a + ld [wCheckMenuCursorBlinkCounter], a +.check_button + ldh a, [hKeysPressed] + and A_BUTTON | B_BUTTON + jr z, .check_cursor_moved + and A_BUTTON + jr nz, .a_button + +; b button + ld a, -1 + call Func_190fb + scf + ret + +; a button +.a_button + call .draw_cursor + ld a, 1 + call Func_190fb + scf + ret + +.check_cursor_moved + ld a, [wPlaysSfx] + or a + jr z, .check_cursor_blink + call PlaySFX +.check_cursor_blink + ld hl, wCheckMenuCursorBlinkCounter + ld a, [hl] + inc [hl] + and %00001111 + ret nz + ld a, SYM_CURSOR_R + bit D_RIGHT_F, [hl] + jr z, .draw_tile +.draw_blank_cursor ; 186d4 (6:46d4) + ld a, SYM_SPACE +.draw_tile + ld e, a + ld a, 10 + ld l, a + ld a, [wCheckMenuCursorXPosition] + ld h, a + call HtimesL + ld a, l + add 1 + ld b, a + ld a, [wCheckMenuCursorYPosition] + sla a + add 14 + ld c, a + ld a, e + ; b = 11, c = y_pos * 2 + 14 + ; h = x_pos * 10, l = 10 + call WriteByteToBGMap0 + or a + ret +.draw_cursor ; 186f3 (6:46f3) + ld a, SYM_CURSOR_R + jr .draw_tile diff --git a/src/engine/promotional_card.asm b/src/engine/promotional_card.asm new file mode 100644 index 0000000..bb3250a --- /dev/null +++ b/src/engine/promotional_card.asm @@ -0,0 +1,61 @@ +; shows screen with the promotional card and received text +; depending on input a +; if $0 = Legendary Molters, Articuno, Zapdos and Dragonite cards +; otherwise, a card ID +_ShowPromotionalCardScreen: + push af + lb de, $38, $9f + call SetupText + pop af + or a + jr nz, .else + ld a, MOLTRES2 + call .legendary_card_text + ld a, ARTICUNO2 + call .legendary_card_text + ld a, ZAPDOS3 + call .legendary_card_text + ld a, DRAGONITE1 +.legendary_card_text + ldtx hl, ReceivedLegendaryCardText + jr .print_text +.else + ldtx hl, ReceivedCardText + cp VILEPLUME + jr z, .print_text + cp BLASTOISE + jr z, .print_text + ldtx hl, ReceivedPromotionalFlyingPikachuText + cp FLYING_PIKACHU + jr z, .print_text + ldtx hl, ReceivedPromotionalSurfingPikachuText + cp SURFING_PIKACHU1 + jr z, .print_text + cp SURFING_PIKACHU2 + jr z, .print_text + ldtx hl, ReceivedPromotionalCardText +.print_text + push hl + ld e, a + ld d, $0 + call LoadCardDataToBuffer1_FromCardID + call PauseSong + ld a, MUSIC_MEDAL + call PlaySong + ld hl, wLoadedCard1Name + ld a, [hli] + ld h, [hl] + ld l, a + bank1call LoadTxRam2 ; switch to bank 1, but call a home func + ld a, PLAYER_TURN + ldh [hWhoseTurn], a + pop hl + bank1call _DisplayCardDetailScreen +.loop + call AssertSongFinished + or a + jr nz, .loop + + call ResumeSong + bank1call OpenCardPage_FromHand + ret diff --git a/src/engine/sprite_vblank.asm b/src/engine/sprite_vblank.asm new file mode 100644 index 0000000..5f099a1 --- /dev/null +++ b/src/engine/sprite_vblank.asm @@ -0,0 +1,39 @@ +; empties screen and replaces +; wVBlankFunctionTrampoline with HandleAllSpriteAnimations +SetSpriteAnimationsAsVBlankFunction: + call EmptyScreen + call Set_OBJ_8x8 + call Func_3ca4 + lb de, $38, $7f + call SetupText + ld hl, wVBlankFunctionTrampoline + 1 + ld de, wVBlankFunctionTrampolineBackup + call BackupVBlankFunctionTrampoline + di + ld [hl], LOW(HandleAllSpriteAnimations) + inc hl + ld [hl], HIGH(HandleAllSpriteAnimations) + ei + ret + +; sets backup VBlank function as wVBlankFunctionTrampoline +RestoreVBlankFunction: + ld hl, wVBlankFunctionTrampolineBackup + ld de, wVBlankFunctionTrampoline + 1 + call BackupVBlankFunctionTrampoline + call Func_3ca4 + bank1call ZeroObjectPositionsAndToggleOAMCopy + ret + +; copies 2 bytes from hl to de while interrupts are disabled +; used to load or store wVBlankFunctionTrampoline +; to wVBlankFunctionTrampolineBackup +BackupVBlankFunctionTrampoline: + di + ld a, [hli] + ld [de], a + inc de + ld a, [hld] + ld [de], a + ei + ret diff --git a/src/engine/starter_deck.asm b/src/engine/starter_deck.asm new file mode 100644 index 0000000..0500b4a --- /dev/null +++ b/src/engine/starter_deck.asm @@ -0,0 +1,182 @@ +; adds the chosen starter deck to the player's first deck configuration +; and also adds to the collection its corresponding extra cards +; input: +; - a = starter deck chosen +; $0 = Charmander +; $1 = Squirtle +; $2 = Bulbasaur +_AddStarterDeck: + add a + ld e, a + ld d, 0 + ld hl, .StarterCardIDs + add hl, de + ld a, PLAYER_TURN + ldh [hWhoseTurn], a + ld a, [hli] ; main deck + add 2 + push hl + ld hl, sDeck1 + call CopyDeckNameAndCards + pop hl + call SwapTurn + ld a, [hli] ; extra deck + add 2 + call LoadDeck + call SwapTurn + +; wPlayerDeck = main starter deck +; wOpponentDeck = extra cards + call EnableSRAM + ld h, HIGH(sCardCollection) + ld de, wPlayerDeck + ld c, DECK_SIZE +.loop_main_cards + ld a, [de] + inc de + ld l, a + res CARD_NOT_OWNED_F, [hl] + dec c + jr nz, .loop_main_cards + + ld h, HIGH(sCardCollection) + ld de, wOpponentDeck + ld c, 30 ; number of extra cards +.loop_extra_cards + ld a, [de] + inc de + ld l, a + res CARD_NOT_OWNED_F, [hl] + inc [hl] + dec c + jr nz, .loop_extra_cards + call DisableSRAM + ret + +.StarterCardIDs + ; main deck, extra cards + db CHARMANDER_AND_FRIENDS_DECK_ID, CHARMANDER_EXTRA_DECK_ID + db SQUIRTLE_AND_FRIENDS_DECK_ID, SQUIRTLE_EXTRA_DECK_ID + db BULBASAUR_AND_FRIENDS_DECK_ID, BULBASAUR_EXTRA_DECK_ID + +; clears saved data (card Collection/saved decks/Card Pop! data/etc) +; then adds the starter decks as saved decks +; marks all cards in Collection as not owned +InitSaveData: +; clear card and deck save data + call EnableSRAM + ld a, PLAYER_TURN + ldh [hWhoseTurn], a + ld hl, sCardAndDeckSaveData + ld bc, sCardAndDeckSaveDataEnd - sCardAndDeckSaveData +.loop_clear + xor a + ld [hli], a + dec bc + ld a, c + or b + jr nz, .loop_clear + +; add the starter decks + ld a, CHARMANDER_AND_FRIENDS_DECK + ld hl, sSavedDeck1 + call CopyDeckNameAndCards + ld a, SQUIRTLE_AND_FRIENDS_DECK + ld hl, sSavedDeck2 + call CopyDeckNameAndCards + ld a, BULBASAUR_AND_FRIENDS_DECK + ld hl, sSavedDeck3 + call CopyDeckNameAndCards + +; marks all cards in Collection to not owned + call EnableSRAM + ld hl, sCardCollection + ld a, CARD_NOT_OWNED +.loop_collection + ld [hl], a + inc l + jr nz, .loop_collection + + ld hl, sCurrentDuel + xor a + ld [hli], a + ld [hli], a ; sCurrentDuelChecksum + ld [hl], a + +; clears Card Pop! names + ld hl, sCardPopNameList + ld c, CARDPOP_NAME_LIST_MAX_ELEMS +.loop_card_pop_names + ld [hl], $0 + ld de, NAME_BUFFER_LENGTH + add hl, de + dec c + jr nz, .loop_card_pop_names + +; saved configuration options + ld a, 2 + ld [sPrinterContrastLevel], a + ld a, $2 + ld [sTextSpeed], a + ld [wTextSpeed], a + +; miscellaneous data + xor a + ld [sAnimationsDisabled], a + ld [sSkipDelayAllowed], a + ld [s0a004], a + ld [sTotalCardPopsDone], a + ld [sReceivedLegendaryCards], a + farcall InitPromotionalCardAndDeckCounterSaveData + call DisableSRAM + ret + +; input: +; a = Deck ID +; hl = destination to copy +CopyDeckNameAndCards: + push de + push bc + push hl + call LoadDeck + jr c, .done + call .CopyDeckName + pop hl + call EnableSRAM + push hl + ld de, wDefaultText +.loop_write_name + ld a, [de] + inc de + ld [hli], a + or a + jr nz, .loop_write_name + pop hl + + push hl + ld de, DECK_NAME_SIZE + add hl, de + ld de, wPlayerDeck + ld c, DECK_SIZE +.loop_write_cards + ld a, [de] + inc de + ld [hli], a + dec c + jr nz, .loop_write_cards + call DisableSRAM + or a +.done + pop hl + pop bc + pop de + ret + +.CopyDeckName + ld hl, wDeckName + ld a, [hli] + ld h, [hl] + ld l, a + ld de, wDefaultText + call CopyText + ret diff --git a/src/engine/unused_save_validation.asm b/src/engine/unused_save_validation.asm new file mode 100644 index 0000000..9a30d38 --- /dev/null +++ b/src/engine/unused_save_validation.asm @@ -0,0 +1,96 @@ +; this is a commented out routine for save data validation +; sUnusedSaveDataValidationByte would be used to store some validation byte +; and xor'd with $250 bytes in SRAM starting from sCardCollection +; if the result wasn't 0, then it would mean there was +; some save corrpution and an error message would pop up +StubbedUnusedSaveDataValidation: + ret + +UnusedSaveDataValidation: ; unreferenced + ldh a, [hBankSRAM] + or a + ret nz + + push hl + push de + push bc + ld hl, sCardCollection + ld bc, $250 + ld a, [sUnusedSaveDataValidationByte] + ld e, a +.loop_xor + ld a, [hli] + xor e + ld e, a + dec bc + ld a, c + or b + jr nz, .loop_xor + ld a, e + pop bc + pop de + pop hl + or a + ret z + + xor a + ld [wTileMapFill], a + ld hl, wDoFrameFunction + ld [hli], a + ld [hl], a + ldh [hSCX], a + ldh [hSCY], a + bank1call ZeroObjectPositionsAndToggleOAMCopy + call EmptyScreen + call LoadSymbolsFont + bank1call SetDefaultPalettes + ld a, [wConsole] + cp CONSOLE_SGB + jr nz, .not_sgb + ld a, $e4 + ld [wOBP0], a + ld [wBGP], a + ld a, $01 + ld [wFlushPaletteFlags], a +.not_sgb + lb de, $38, $9f + call SetupText + ldtx hl, YourDataWasDestroyedSomehowText + bank1call DrawWholeScreenTextBox + ld a, SRAM_ENABLE + ld [MBC3SRamEnable], a + xor a + ldh [hBankSRAM], a + ld [MBC3SRamBank], a + ld [MBC3RTC], a + ld [MBC3SRamEnable], a + jp Reset + + ret + +UnusedCalculateSaveDataValidationByte: ; unreferenced + ldh a, [hBankSRAM] + or a + ret nz + push hl + push de + push bc + ld hl, sCardCollection + ld bc, $250 + ld e, $00 +.loop_xor + ld a, [hli] + xor e + ld e, a + dec bc + ld a, c + or b + jr nz, .loop_xor + ld a, SRAM_ENABLE + ld [MBC3SRamEnable], a + ld a, e + ld [sUnusedSaveDataValidationByte], a + pop bc + pop de + pop hl + ret diff --git a/src/layout.link b/src/layout.link index f2d8f2b..e731983 100644 --- a/src/layout.link +++ b/src/layout.link @@ -36,7 +36,7 @@ ROMX $01 "Duel Core" "Menus Common" ROMX $02 - "Menus" + "Menus 1" ROMX $03 "Bank 3" ROMX $04 @@ -44,7 +44,18 @@ ROMX $04 ROMX $05 "AI Logic 1" ROMX $06 - "Bank 6" + "Menus 2" + "Effect Commands" + "Animations Engine" + "IR Communications Core" + "Sprite Animations VBlank" + "Starter Deck" + "Link Functions" + "Promotional Card" + "Booster Pack Menu" + "Unused Save Validation" + "Input Name" + "Auto Deck Machines" ROMX $07 "Bank 7" "Credits Sequence" diff --git a/src/main.asm b/src/main.asm index 66be305..55df597 100644 --- a/src/main.asm +++ b/src/main.asm @@ -12,7 +12,7 @@ INCLUDE "engine/duel/core.asm" SECTION "Menus Common", ROMX INCLUDE "engine/menus/common.asm" -SECTION "Menus", ROMX +SECTION "Menus 1", ROMX INCLUDE "engine/menus/duel.asm" INCLUDE "engine/menus/deck_selection.asm" INCLUDE "engine/menus/deck_check.asm" @@ -31,8 +31,47 @@ SECTION "AI Logic 1", ROMX INCLUDE "data/deck_ai_pointers.asm" INCLUDE "engine/ai/core.asm" -SECTION "Bank 6", ROMX -INCLUDE "engine/bank06.asm" +SECTION "Menus 2", ROMX +INCLUDE "engine/copy_card_name.asm" +INCLUDE "engine/menus/play_area.asm" +INCLUDE "engine/menus/glossary.asm" +INCLUDE "engine/menus/unknown.asm" + +SECTION "Effect Commands", ROMX +INCLUDE "engine/duel/effect_commands.asm" + +SECTION "Animations Engine", ROMX +INCLUDE "engine/duel/animations.asm" + +SECTION "IR Communications Core", ROMX +INCLUDE "engine/link/ir_core.asm" + +SECTION "Sprite Animations VBlank", ROMX +INCLUDE "engine/sprite_vblank.asm" + +SECTION "Starter Deck", ROMX +INCLUDE "engine/starter_deck.asm" + +SECTION "Link Functions", ROMX +INCLUDE "engine/link/ir_functions.asm" +INCLUDE "engine/link/card_pop.asm" +INCLUDE "engine/link/printer.asm" +INCLUDE "engine/link/link_duel.asm" + +SECTION "Promotional Card", ROMX +INCLUDE "engine/promotional_card.asm" + +SECTION "Booster Pack Menu", ROMX +INCLUDE "engine/menus/booster_pack.asm" + +SECTION "Unused Save Validation", ROMX +INCLUDE "engine/unused_save_validation.asm" + +SECTION "Input Name", ROMX +INCLUDE "engine/input_name.asm" + +SECTION "Auto Deck Machines", ROMX +INCLUDE "engine/auto_deck_machines.asm" SECTION "Bank 7", ROMX INCLUDE "engine/bank07.asm" diff --git a/src/sram.asm b/src/sram.asm index 6aa8234..8cffb3f 100644 --- a/src/sram.asm +++ b/src/sram.asm @@ -29,7 +29,7 @@ sSkipDelayAllowed:: ; a009 ds $1 sReceivedLegendaryCards:: ; a00a ds $1 -s0a00b:: ; a00b +sUnusedSaveDataValidationByte:: ; a00b ds $1 s0a00c:: ; a00c ds $4 diff --git a/src/text/text1.asm b/src/text/text1.asm index e6192ee..39bd82d 100644 --- a/src/text/text1.asm +++ b/src/text/text1.asm @@ -763,7 +763,7 @@ ResetBackUpRamText: ; 372a9 (d:72a9) text "Reset Back Up RAM?" done -Text00a3: ; 372bd (d:72bd) +YourDataWasDestroyedSomehowText: ; 372bd (d:72bd) text "Your Data was destroyed" line "somehow." line "" diff --git a/src/text/text_offsets.asm b/src/text/text_offsets.asm index 0d212c2..ff38daa 100644 --- a/src/text/text_offsets.asm +++ b/src/text/text_offsets.asm @@ -164,7 +164,7 @@ TextOffsets:: ; 34000 (d:4000) textpointer Text00a0 ; 0x00a0 textpointer Text00a1 ; 0x00a1 textpointer ResetBackUpRamText ; 0x00a2 - textpointer Text00a3 ; 0x00a3 + textpointer YourDataWasDestroyedSomehowText ; 0x00a3 textpointer NoCardsInHandText ; 0x00a4 textpointer TheDiscardPileHasNoCardsText ; 0x00a5 textpointer PlayerDiscardPileText ; 0x00a6 -- cgit v1.2.3 From a53828331cf4c86f7d14e3b178eaa18817b514a3 Mon Sep 17 00:00:00 2001 From: ElectroDeoxys Date: Mon, 27 Sep 2021 12:25:56 +0100 Subject: Split part of bank 7 --- src/data/sequences/credits.asm | 399 +++++ src/data/sequences/credits_sequence.asm | 399 ----- src/data/sequences/intro.asm | 47 + src/data/sequences/opening_sequence.asm | 47 - src/engine/bank04.asm | 4 +- src/engine/bank07.asm | 1861 +------------------- src/engine/credits.asm | 189 ++ src/engine/duel/animations.asm | 354 ---- src/engine/duel/animations/commands.asm | 354 ++++ src/engine/duel/animations/core.asm | 661 +++++++ src/engine/duel/animations/screen_effects.asm | 286 +++ src/engine/intro.asm | 114 ++ src/engine/menus/start.asm | 418 +++++ src/engine/sequences/credits_sequence_commands.asm | 2 +- src/engine/sequences/intro_sequence_commands.asm | 392 +++++ src/engine/sequences/opening_sequence_commands.asm | 325 ---- src/engine/unused_copyright.asm | 26 + src/home/lcd.asm | 2 +- src/home/map.asm | 2 +- src/layout.link | 6 +- src/macros/intro_sequence.asm | 63 + src/macros/opening_sequence.asm | 63 - src/main.asm | 22 +- src/wram.asm | 4 +- 24 files changed, 3024 insertions(+), 3016 deletions(-) create mode 100644 src/data/sequences/credits.asm delete mode 100644 src/data/sequences/credits_sequence.asm create mode 100644 src/data/sequences/intro.asm delete mode 100644 src/data/sequences/opening_sequence.asm create mode 100644 src/engine/credits.asm delete mode 100644 src/engine/duel/animations.asm create mode 100644 src/engine/duel/animations/commands.asm create mode 100644 src/engine/duel/animations/core.asm create mode 100644 src/engine/duel/animations/screen_effects.asm create mode 100644 src/engine/intro.asm create mode 100644 src/engine/menus/start.asm create mode 100644 src/engine/sequences/intro_sequence_commands.asm delete mode 100644 src/engine/sequences/opening_sequence_commands.asm create mode 100644 src/engine/unused_copyright.asm create mode 100644 src/macros/intro_sequence.asm delete mode 100644 src/macros/opening_sequence.asm diff --git a/src/data/sequences/credits.asm b/src/data/sequences/credits.asm new file mode 100644 index 0000000..c802b42 --- /dev/null +++ b/src/data/sequences/credits.asm @@ -0,0 +1,399 @@ +INCLUDE "macros/credits_sequence.asm" + +CreditsSequence: ; 1daef (7:5aef) + credits_seq_disable_lcd + credits_seq_load_ow_map 0, 0, OVERWORLD_MAP + credits_seq_init_volcano_sprite + credits_seq_init_overlay 0, 0, 144, 0 + credits_seq_print_text_box 2, 1, OverworldMapPokemonDomeText + credits_seq_print_text 0, 0, PokemonTradingCardGameStaffText + credits_seq_fade_in + credits_seq_wait 60 + credits_seq_transform_overlay 0, 32, 144, 0 + credits_seq_wait 225 + credits_seq_transform_overlay 0, 0, 144, 0 + credits_seq_fade_out + + credits_seq_load_ow_map 0, 0, MASON_LABORATORY + credits_seq_load_npc 14, 6, SOUTH, NPC_DRMASON + credits_seq_load_npc 4, 14, EAST, NPC_SAM + credits_seq_load_npc 6, 4, SOUTH, NPC_TECH5 + credits_seq_init_overlay 0, 0, 144, 0 + credits_seq_draw_rectangle 0, 3 + credits_seq_print_text 0, 0, ProducersText + credits_seq_fade_in + credits_seq_wait 60 + credits_seq_transform_overlay 0, 24, 144, 0 + credits_seq_transform_overlay 0, 24, 104, 40 + credits_seq_wait 225 + credits_seq_transform_overlay 0, 24, 144, 0 + credits_seq_transform_overlay 0, 0, 144, 0 + credits_seq_fade_out + + credits_seq_load_ow_map 0, 0, DECK_MACHINE_ROOM + credits_seq_load_npc 6, 8, SOUTH, NPC_TECH6 + credits_seq_load_npc 6, 22, WEST, NPC_TECH7 + credits_seq_load_npc 10, 18, WEST, NPC_TECH8 + credits_seq_load_npc 12, 12, WEST, NPC_AARON + credits_seq_init_overlay 0, 0, 144, 0 + credits_seq_draw_rectangle 0, 7 + credits_seq_print_text 0, 0, DirectorText + credits_seq_fade_in + credits_seq_wait 60 + credits_seq_transform_overlay 0, 24, 144, 0 + credits_seq_transform_overlay 0, 24, 120, 24 + credits_seq_wait 225 + credits_seq_transform_overlay 0, 24, 144, 0 + credits_seq_transform_overlay 0, 0, 144, 0 + credits_seq_fade_out + + credits_seq_load_club_map 0 + credits_seq_init_overlay 0, 0, 144, 0 + credits_seq_draw_rectangle 0, 5 + credits_seq_print_text 0, 0, ProgrammersText + credits_seq_fade_in + credits_seq_wait 60 + credits_seq_transform_overlay 0, 24, 144, 0 + credits_seq_transform_overlay 0, 24, 104, 40 + credits_seq_wait 225 + credits_seq_transform_overlay 0, 24, 144, 0 + credits_seq_transform_overlay 0, 0, 144, 0 + credits_seq_fade_out + + credits_seq_load_club_map 1 + credits_seq_init_overlay 0, 0, 144, 0 + credits_seq_draw_rectangle 0, 7 + credits_seq_print_text 0, 0, GBGraphicDesigners1Text + credits_seq_fade_in + credits_seq_wait 60 + credits_seq_transform_overlay 0, 24, 144, 0 + credits_seq_transform_overlay 0, 24, 104, 40 + credits_seq_wait 225 + credits_seq_transform_overlay 0, 24, 144, 0 + credits_seq_draw_rectangle 4, 3 + credits_seq_print_text 0, 4, GBGraphicDesigners2Text + credits_seq_transform_overlay 0, 24, 104, 40 + credits_seq_wait 225 + credits_seq_transform_overlay 0, 24, 144, 0 + credits_seq_draw_rectangle 4, 3 + credits_seq_print_text 0, 4, GBGraphicDesigners3Text + credits_seq_transform_overlay 0, 24, 104, 40 + credits_seq_wait 225 + credits_seq_transform_overlay 0, 24, 144, 0 + credits_seq_transform_overlay 0, 0, 144, 0 + credits_seq_fade_out + + credits_seq_load_club_map 2 + credits_seq_init_overlay 0, 0, 144, 0 + credits_seq_draw_rectangle 0, 7 + credits_seq_print_text 0, 0, MusicText + credits_seq_fade_in + credits_seq_wait 60 + credits_seq_transform_overlay 0, 24, 144, 0 + credits_seq_transform_overlay 0, 24, 120, 24 + credits_seq_wait 225 + credits_seq_transform_overlay 0, 24, 144, 0 + credits_seq_transform_overlay 0, 0, 144, 0 + credits_seq_draw_rectangle 0, 5 + credits_seq_print_text 0, 0, SoundEffectsText + credits_seq_transform_overlay 0, 24, 144, 0 + credits_seq_transform_overlay 0, 24, 120, 24 + credits_seq_wait 225 + credits_seq_transform_overlay 0, 24, 144, 0 + credits_seq_transform_overlay 0, 0, 144, 0 + credits_seq_fade_out + + credits_seq_load_club_map 3 + credits_seq_init_overlay 0, 0, 144, 0 + credits_seq_draw_rectangle 0, 7 + credits_seq_print_text 0, 0, SoundDirectorText + credits_seq_fade_in + credits_seq_wait 60 + credits_seq_transform_overlay 0, 24, 144, 0 + credits_seq_transform_overlay 0, 24, 120, 24 + credits_seq_wait 225 + credits_seq_transform_overlay 0, 24, 144, 0 + credits_seq_transform_overlay 0, 0, 144, 0 + credits_seq_draw_rectangle 0, 5 + credits_seq_print_text 0, 0, SoundSystemSupportText + credits_seq_transform_overlay 0, 24, 144, 0 + credits_seq_transform_overlay 0, 24, 112, 32 + credits_seq_wait 225 + credits_seq_transform_overlay 0, 24, 144, 0 + credits_seq_transform_overlay 0, 0, 144, 0 + credits_seq_fade_out + + credits_seq_load_booster 6, 3, SCENE_CHARIZARD_INTRO + credits_seq_init_overlay 0, 0, 144, 0 + credits_seq_draw_rectangle 0, 6 + credits_seq_print_text 0, 0, CardGameCreator1Text + credits_seq_fade_in + credits_seq_wait 60 + credits_seq_transform_overlay 0, 24, 144, 0 + credits_seq_transform_overlay 0, 24, 120, 24 + credits_seq_wait 225 + credits_seq_transform_overlay 0, 24, 144, 0 + credits_seq_transform_overlay 0, 0, 144, 0 + credits_seq_fade_out + + credits_seq_load_booster 6, 3, SCENE_SCYTHER_INTRO + credits_seq_init_overlay 0, 0, 144, 0 + credits_seq_draw_rectangle 0, 5 + credits_seq_print_text 0, 0, CardGameCreator2Text + credits_seq_fade_in + credits_seq_wait 60 + credits_seq_transform_overlay 0, 24, 144, 0 + credits_seq_transform_overlay 0, 24, 120, 24 + credits_seq_wait 225 + credits_seq_transform_overlay 0, 24, 144, 0 + credits_seq_transform_overlay 0, 0, 144, 0 + credits_seq_fade_out + + credits_seq_load_booster 6, 3, SCENE_AERODACTYL_INTRO + credits_seq_init_overlay 0, 0, 144, 0 + credits_seq_draw_rectangle 0, 5 + credits_seq_print_text 0, 0, CardGameCreator3Text + credits_seq_fade_in + credits_seq_wait 60 + credits_seq_transform_overlay 0, 24, 144, 0 + credits_seq_transform_overlay 0, 24, 120, 24 + credits_seq_wait 225 + credits_seq_transform_overlay 0, 24, 144, 0 + credits_seq_transform_overlay 0, 0, 144, 0 + credits_seq_fade_out + + credits_seq_load_ow_map 0, 0, ISHIHARAS_HOUSE + credits_seq_load_npc 8, 8, SOUTH, NPC_ISHIHARA + credits_seq_init_overlay 0, 0, 144, 0 + credits_seq_draw_rectangle 0, 8 + credits_seq_print_text 0, 0, CardIllustrators1Text + credits_seq_fade_in + credits_seq_wait 60 + credits_seq_transform_overlay 0, 24, 144, 0 + credits_seq_transform_overlay 0, 24, 96, 48 + credits_seq_wait 225 + credits_seq_transform_overlay 0, 24, 144, 0 + credits_seq_draw_rectangle 4, 4 + credits_seq_print_text 0, 4, CardIllustrators2Text + credits_seq_transform_overlay 0, 24, 96, 48 + credits_seq_wait 225 + credits_seq_transform_overlay 0, 24, 144, 0 + credits_seq_transform_overlay 0, 0, 144, 0 + credits_seq_fade_out + + credits_seq_load_ow_map 16, 8, LIGHTNING_CLUB_LOBBY + credits_seq_load_npc 6, 4, SOUTH, NPC_CLERK10 + credits_seq_load_npc 10, 4, SOUTH, NPC_GIFT_CENTER_CLERK + credits_seq_load_npc 18, 16, WEST, NPC_CHAP2 + credits_seq_load_npc 18, 2, NORTH, NPC_IMAKUNI + credits_seq_load_npc 8, 12, SOUTH, NPC_LASS4 + credits_seq_load_npc 20, 8, SOUTH, NPC_HOOD1 + credits_seq_init_overlay 0, 0, 144, 0 + credits_seq_draw_rectangle 0, 8 + credits_seq_print_text 0, 0, SpecialAppearances1Text + credits_seq_fade_in + credits_seq_wait 60 + credits_seq_transform_overlay 0, 24, 144, 0 + credits_seq_transform_overlay 0, 24, 112, 32 + credits_seq_wait 225 + credits_seq_transform_overlay 0, 24, 144, 0 + credits_seq_draw_rectangle 4, 4 + credits_seq_print_text 0, 4, SpecialAppearances2Text + credits_seq_transform_overlay 0, 24, 112, 32 + credits_seq_wait 225 + credits_seq_transform_overlay 0, 24, 144, 0 + credits_seq_transform_overlay 0, 0, 144, 0 + credits_seq_fade_out + + credits_seq_load_ow_map 48, 0, CHALLENGE_HALL + credits_seq_load_npc 14, 4, SOUTH, NPC_HOST + credits_seq_load_npc 18, 8, WEST, NPC_RONALD1 + credits_seq_load_npc 12, 8, EAST, NPC_PLAYER_CREDITS + credits_seq_init_overlay 0, 0, 144, 0 + credits_seq_draw_rectangle 0, 7 + credits_seq_print_text 0, 0, USCoordination1Text + credits_seq_fade_in + credits_seq_wait 60 + credits_seq_transform_overlay 0, 32, 144, 0 + credits_seq_transform_overlay 0, 32, 112, 32 + credits_seq_wait 225 + credits_seq_transform_overlay 0, 32, 144, 0 + credits_seq_draw_rectangle 4, 4 + credits_seq_print_text 0, 5, USCoordination2Text + credits_seq_transform_overlay 0, 32, 112, 32 + credits_seq_wait 225 + credits_seq_transform_overlay 0, 32, 144, 0 + credits_seq_transform_overlay 0, 0, 144, 0 + credits_seq_draw_rectangle 0, 7 + credits_seq_print_text 0, 0, USCoordination3Text + credits_seq_transform_overlay 0, 40, 144, 0 + credits_seq_transform_overlay 0, 40, 112, 32 + credits_seq_wait 225 + credits_seq_transform_overlay 0, 40, 144, 0 + credits_seq_draw_rectangle 6, 4 + credits_seq_print_text 0, 6, USCoordination4Text + credits_seq_transform_overlay 0, 40, 104, 40 + credits_seq_wait 225 + credits_seq_transform_overlay 0, 40, 144, 0 + credits_seq_transform_overlay 0, 0, 144, 0 + credits_seq_fade_out + + credits_seq_load_booster 6, 3, SCENE_COLOSSEUM_BOOSTER + credits_seq_init_overlay 0, 0, 144, 0 + credits_seq_draw_rectangle 0, 8 + credits_seq_print_text 0, 0, TranslationDraftText + credits_seq_fade_in + credits_seq_wait 60 + credits_seq_transform_overlay 0, 24, 144, 0 + credits_seq_transform_overlay 0, 24, 120, 24 + credits_seq_wait 225 + credits_seq_transform_overlay 0, 24, 144, 0 + credits_seq_transform_overlay 0, 0, 144, 0 + credits_seq_fade_out + + credits_seq_load_booster 6, 3, SCENE_EVOLUTION_BOOSTER + credits_seq_init_overlay 0, 0, 144, 0 + credits_seq_draw_rectangle 0, 6 + credits_seq_print_text 0, 0, MasteringText + credits_seq_fade_in + credits_seq_wait 60 + credits_seq_transform_overlay 0, 24, 144, 0 + credits_seq_transform_overlay 0, 24, 120, 24 + credits_seq_wait 225 + credits_seq_transform_overlay 0, 24, 144, 0 + credits_seq_transform_overlay 0, 0, 144, 0 + credits_seq_fade_out + + credits_seq_load_booster 6, 3, SCENE_MYSTERY_BOOSTER + credits_seq_init_overlay 0, 0, 144, 0 + credits_seq_draw_rectangle 0, 6 + credits_seq_print_text 0, 0, ManualCreationText + credits_seq_fade_in + credits_seq_wait 60 + credits_seq_transform_overlay 0, 24, 144, 0 + credits_seq_transform_overlay 0, 24, 120, 24 + credits_seq_wait 225 + credits_seq_transform_overlay 0, 24, 144, 0 + credits_seq_transform_overlay 0, 0, 144, 0 + credits_seq_fade_out + + credits_seq_load_booster 6, 3, SCENE_LABORATORY_BOOSTER + credits_seq_init_overlay 0, 0, 144, 0 + credits_seq_draw_rectangle 0, 6 + credits_seq_print_text 0, 0, ManualIllustrationsText + credits_seq_fade_in + credits_seq_wait 60 + credits_seq_transform_overlay 0, 24, 144, 0 + credits_seq_transform_overlay 0, 24, 120, 24 + credits_seq_wait 225 + credits_seq_transform_overlay 0, 24, 144, 0 + credits_seq_transform_overlay 0, 0, 144, 0 + credits_seq_fade_out + + credits_seq_load_club_map 4 + credits_seq_init_overlay 0, 0, 144, 0 + credits_seq_draw_rectangle 0, 7 + credits_seq_print_text 0, 0, PokemonOriginalStoryText + credits_seq_fade_in + credits_seq_wait 60 + credits_seq_transform_overlay 0, 24, 144, 0 + credits_seq_transform_overlay 0, 24, 120, 24 + credits_seq_wait 225 + credits_seq_transform_overlay 0, 24, 144, 0 + credits_seq_transform_overlay 0, 0, 144, 0 + credits_seq_draw_rectangle 0, 7 + credits_seq_print_text 0, 0, CreatedInCooperationWithText + credits_seq_wait 60 + credits_seq_transform_overlay 0, 24, 144, 0 + credits_seq_transform_overlay 0, 24, 104, 40 + credits_seq_wait 225 + credits_seq_transform_overlay 0, 24, 144, 0 + credits_seq_transform_overlay 0, 0, 144, 0 + credits_seq_fade_out + + credits_seq_load_club_map 5 + credits_seq_init_overlay 0, 0, 144, 0 + credits_seq_draw_rectangle 0, 7 + credits_seq_print_text 0, 0, WithCooperation1Text + credits_seq_fade_in + credits_seq_wait 60 + credits_seq_transform_overlay 0, 24, 144, 0 + credits_seq_transform_overlay 0, 24, 104, 40 + credits_seq_wait 225 + credits_seq_transform_overlay 0, 24, 144, 0 + credits_seq_draw_rectangle 4, 5 + credits_seq_print_text 0, 4, WithCooperation2Text + credits_seq_transform_overlay 0, 24, 96, 48 + credits_seq_wait 225 + credits_seq_transform_overlay 0, 24, 144, 0 + credits_seq_draw_rectangle 4, 4 + credits_seq_print_text 0, 4, WithCooperation3Text + credits_seq_transform_overlay 0, 24, 96, 48 + credits_seq_wait 225 + credits_seq_transform_overlay 0, 24, 144, 0 + credits_seq_transform_overlay 0, 0, 144, 0 + credits_seq_fade_out + + credits_seq_load_club_map 6 + credits_seq_init_overlay 0, 0, 144, 0 + credits_seq_draw_rectangle 0, 8 + credits_seq_print_text 0, 0, ProjectManagerText + credits_seq_fade_in + credits_seq_wait 60 + credits_seq_transform_overlay 0, 24, 144, 0 + credits_seq_transform_overlay 0, 24, 120, 24 + credits_seq_wait 225 + credits_seq_transform_overlay 0, 24, 144, 0 + credits_seq_transform_overlay 0, 0, 144, 0 + credits_seq_fade_out + + credits_seq_load_club_map 7 + credits_seq_init_overlay 0, 0, 144, 0 + credits_seq_draw_rectangle 0, 7 + credits_seq_print_text 0, 0, SupervisorText + credits_seq_fade_in + credits_seq_wait 60 + credits_seq_transform_overlay 0, 24, 144, 0 + credits_seq_transform_overlay 0, 24, 104, 40 + credits_seq_wait 225 + credits_seq_transform_overlay 0, 24, 144, 0 + credits_seq_transform_overlay 0, 0, 144, 0 + credits_seq_fade_out + + credits_seq_load_club_map 8 + credits_seq_init_overlay 0, 0, 144, 0 + credits_seq_draw_rectangle 0, 7 + credits_seq_print_text 0, 0, ExecutiveProducerText + credits_seq_fade_in + credits_seq_wait 60 + credits_seq_transform_overlay 0, 24, 144, 0 + credits_seq_transform_overlay 0, 24, 104, 40 + credits_seq_wait 225 + credits_seq_transform_overlay 0, 24, 144, 0 + credits_seq_transform_overlay 0, 0, 144, 0 + credits_seq_fade_out + + credits_seq_load_ow_map 16, 16, HALL_OF_HONOR + credits_seq_load_npc 10, 8, NORTH, NPC_LEGENDARY_CARD_TOP_LEFT + credits_seq_load_npc 12, 8, NORTH, NPC_LEGENDARY_CARD_TOP_RIGHT + credits_seq_load_npc 8, 10, NORTH, NPC_LEGENDARY_CARD_LEFT_SPARK + credits_seq_load_npc 10, 10, NORTH, NPC_LEGENDARY_CARD_BOTTOM_LEFT + credits_seq_load_npc 12, 10, NORTH, NPC_LEGENDARY_CARD_BOTTOM_RIGHT + credits_seq_load_npc 14, 10, NORTH, NPC_LEGENDARY_CARD_RIGHT_SPARK + credits_seq_init_overlay 0, 0, 144, 0 + credits_seq_draw_rectangle 0, 7 + credits_seq_print_text 0, 0, CreatedByText + credits_seq_fade_in + credits_seq_wait 60 + credits_seq_transform_overlay 0, 24, 144, 0 + credits_seq_transform_overlay 0, 24, 104, 40 + credits_seq_wait 225 + credits_seq_transform_overlay 0, 24, 144, 0 + credits_seq_transform_overlay 0, 0, 144, 0 + credits_seq_fade_out + + credits_seq_load_scene 0, 0, SCENE_COMPANIES + credits_seq_init_overlay 0, 0, 144, 0 + credits_seq_fade_in + credits_seq_wait 225 + credits_seq_end diff --git a/src/data/sequences/credits_sequence.asm b/src/data/sequences/credits_sequence.asm deleted file mode 100644 index c802b42..0000000 --- a/src/data/sequences/credits_sequence.asm +++ /dev/null @@ -1,399 +0,0 @@ -INCLUDE "macros/credits_sequence.asm" - -CreditsSequence: ; 1daef (7:5aef) - credits_seq_disable_lcd - credits_seq_load_ow_map 0, 0, OVERWORLD_MAP - credits_seq_init_volcano_sprite - credits_seq_init_overlay 0, 0, 144, 0 - credits_seq_print_text_box 2, 1, OverworldMapPokemonDomeText - credits_seq_print_text 0, 0, PokemonTradingCardGameStaffText - credits_seq_fade_in - credits_seq_wait 60 - credits_seq_transform_overlay 0, 32, 144, 0 - credits_seq_wait 225 - credits_seq_transform_overlay 0, 0, 144, 0 - credits_seq_fade_out - - credits_seq_load_ow_map 0, 0, MASON_LABORATORY - credits_seq_load_npc 14, 6, SOUTH, NPC_DRMASON - credits_seq_load_npc 4, 14, EAST, NPC_SAM - credits_seq_load_npc 6, 4, SOUTH, NPC_TECH5 - credits_seq_init_overlay 0, 0, 144, 0 - credits_seq_draw_rectangle 0, 3 - credits_seq_print_text 0, 0, ProducersText - credits_seq_fade_in - credits_seq_wait 60 - credits_seq_transform_overlay 0, 24, 144, 0 - credits_seq_transform_overlay 0, 24, 104, 40 - credits_seq_wait 225 - credits_seq_transform_overlay 0, 24, 144, 0 - credits_seq_transform_overlay 0, 0, 144, 0 - credits_seq_fade_out - - credits_seq_load_ow_map 0, 0, DECK_MACHINE_ROOM - credits_seq_load_npc 6, 8, SOUTH, NPC_TECH6 - credits_seq_load_npc 6, 22, WEST, NPC_TECH7 - credits_seq_load_npc 10, 18, WEST, NPC_TECH8 - credits_seq_load_npc 12, 12, WEST, NPC_AARON - credits_seq_init_overlay 0, 0, 144, 0 - credits_seq_draw_rectangle 0, 7 - credits_seq_print_text 0, 0, DirectorText - credits_seq_fade_in - credits_seq_wait 60 - credits_seq_transform_overlay 0, 24, 144, 0 - credits_seq_transform_overlay 0, 24, 120, 24 - credits_seq_wait 225 - credits_seq_transform_overlay 0, 24, 144, 0 - credits_seq_transform_overlay 0, 0, 144, 0 - credits_seq_fade_out - - credits_seq_load_club_map 0 - credits_seq_init_overlay 0, 0, 144, 0 - credits_seq_draw_rectangle 0, 5 - credits_seq_print_text 0, 0, ProgrammersText - credits_seq_fade_in - credits_seq_wait 60 - credits_seq_transform_overlay 0, 24, 144, 0 - credits_seq_transform_overlay 0, 24, 104, 40 - credits_seq_wait 225 - credits_seq_transform_overlay 0, 24, 144, 0 - credits_seq_transform_overlay 0, 0, 144, 0 - credits_seq_fade_out - - credits_seq_load_club_map 1 - credits_seq_init_overlay 0, 0, 144, 0 - credits_seq_draw_rectangle 0, 7 - credits_seq_print_text 0, 0, GBGraphicDesigners1Text - credits_seq_fade_in - credits_seq_wait 60 - credits_seq_transform_overlay 0, 24, 144, 0 - credits_seq_transform_overlay 0, 24, 104, 40 - credits_seq_wait 225 - credits_seq_transform_overlay 0, 24, 144, 0 - credits_seq_draw_rectangle 4, 3 - credits_seq_print_text 0, 4, GBGraphicDesigners2Text - credits_seq_transform_overlay 0, 24, 104, 40 - credits_seq_wait 225 - credits_seq_transform_overlay 0, 24, 144, 0 - credits_seq_draw_rectangle 4, 3 - credits_seq_print_text 0, 4, GBGraphicDesigners3Text - credits_seq_transform_overlay 0, 24, 104, 40 - credits_seq_wait 225 - credits_seq_transform_overlay 0, 24, 144, 0 - credits_seq_transform_overlay 0, 0, 144, 0 - credits_seq_fade_out - - credits_seq_load_club_map 2 - credits_seq_init_overlay 0, 0, 144, 0 - credits_seq_draw_rectangle 0, 7 - credits_seq_print_text 0, 0, MusicText - credits_seq_fade_in - credits_seq_wait 60 - credits_seq_transform_overlay 0, 24, 144, 0 - credits_seq_transform_overlay 0, 24, 120, 24 - credits_seq_wait 225 - credits_seq_transform_overlay 0, 24, 144, 0 - credits_seq_transform_overlay 0, 0, 144, 0 - credits_seq_draw_rectangle 0, 5 - credits_seq_print_text 0, 0, SoundEffectsText - credits_seq_transform_overlay 0, 24, 144, 0 - credits_seq_transform_overlay 0, 24, 120, 24 - credits_seq_wait 225 - credits_seq_transform_overlay 0, 24, 144, 0 - credits_seq_transform_overlay 0, 0, 144, 0 - credits_seq_fade_out - - credits_seq_load_club_map 3 - credits_seq_init_overlay 0, 0, 144, 0 - credits_seq_draw_rectangle 0, 7 - credits_seq_print_text 0, 0, SoundDirectorText - credits_seq_fade_in - credits_seq_wait 60 - credits_seq_transform_overlay 0, 24, 144, 0 - credits_seq_transform_overlay 0, 24, 120, 24 - credits_seq_wait 225 - credits_seq_transform_overlay 0, 24, 144, 0 - credits_seq_transform_overlay 0, 0, 144, 0 - credits_seq_draw_rectangle 0, 5 - credits_seq_print_text 0, 0, SoundSystemSupportText - credits_seq_transform_overlay 0, 24, 144, 0 - credits_seq_transform_overlay 0, 24, 112, 32 - credits_seq_wait 225 - credits_seq_transform_overlay 0, 24, 144, 0 - credits_seq_transform_overlay 0, 0, 144, 0 - credits_seq_fade_out - - credits_seq_load_booster 6, 3, SCENE_CHARIZARD_INTRO - credits_seq_init_overlay 0, 0, 144, 0 - credits_seq_draw_rectangle 0, 6 - credits_seq_print_text 0, 0, CardGameCreator1Text - credits_seq_fade_in - credits_seq_wait 60 - credits_seq_transform_overlay 0, 24, 144, 0 - credits_seq_transform_overlay 0, 24, 120, 24 - credits_seq_wait 225 - credits_seq_transform_overlay 0, 24, 144, 0 - credits_seq_transform_overlay 0, 0, 144, 0 - credits_seq_fade_out - - credits_seq_load_booster 6, 3, SCENE_SCYTHER_INTRO - credits_seq_init_overlay 0, 0, 144, 0 - credits_seq_draw_rectangle 0, 5 - credits_seq_print_text 0, 0, CardGameCreator2Text - credits_seq_fade_in - credits_seq_wait 60 - credits_seq_transform_overlay 0, 24, 144, 0 - credits_seq_transform_overlay 0, 24, 120, 24 - credits_seq_wait 225 - credits_seq_transform_overlay 0, 24, 144, 0 - credits_seq_transform_overlay 0, 0, 144, 0 - credits_seq_fade_out - - credits_seq_load_booster 6, 3, SCENE_AERODACTYL_INTRO - credits_seq_init_overlay 0, 0, 144, 0 - credits_seq_draw_rectangle 0, 5 - credits_seq_print_text 0, 0, CardGameCreator3Text - credits_seq_fade_in - credits_seq_wait 60 - credits_seq_transform_overlay 0, 24, 144, 0 - credits_seq_transform_overlay 0, 24, 120, 24 - credits_seq_wait 225 - credits_seq_transform_overlay 0, 24, 144, 0 - credits_seq_transform_overlay 0, 0, 144, 0 - credits_seq_fade_out - - credits_seq_load_ow_map 0, 0, ISHIHARAS_HOUSE - credits_seq_load_npc 8, 8, SOUTH, NPC_ISHIHARA - credits_seq_init_overlay 0, 0, 144, 0 - credits_seq_draw_rectangle 0, 8 - credits_seq_print_text 0, 0, CardIllustrators1Text - credits_seq_fade_in - credits_seq_wait 60 - credits_seq_transform_overlay 0, 24, 144, 0 - credits_seq_transform_overlay 0, 24, 96, 48 - credits_seq_wait 225 - credits_seq_transform_overlay 0, 24, 144, 0 - credits_seq_draw_rectangle 4, 4 - credits_seq_print_text 0, 4, CardIllustrators2Text - credits_seq_transform_overlay 0, 24, 96, 48 - credits_seq_wait 225 - credits_seq_transform_overlay 0, 24, 144, 0 - credits_seq_transform_overlay 0, 0, 144, 0 - credits_seq_fade_out - - credits_seq_load_ow_map 16, 8, LIGHTNING_CLUB_LOBBY - credits_seq_load_npc 6, 4, SOUTH, NPC_CLERK10 - credits_seq_load_npc 10, 4, SOUTH, NPC_GIFT_CENTER_CLERK - credits_seq_load_npc 18, 16, WEST, NPC_CHAP2 - credits_seq_load_npc 18, 2, NORTH, NPC_IMAKUNI - credits_seq_load_npc 8, 12, SOUTH, NPC_LASS4 - credits_seq_load_npc 20, 8, SOUTH, NPC_HOOD1 - credits_seq_init_overlay 0, 0, 144, 0 - credits_seq_draw_rectangle 0, 8 - credits_seq_print_text 0, 0, SpecialAppearances1Text - credits_seq_fade_in - credits_seq_wait 60 - credits_seq_transform_overlay 0, 24, 144, 0 - credits_seq_transform_overlay 0, 24, 112, 32 - credits_seq_wait 225 - credits_seq_transform_overlay 0, 24, 144, 0 - credits_seq_draw_rectangle 4, 4 - credits_seq_print_text 0, 4, SpecialAppearances2Text - credits_seq_transform_overlay 0, 24, 112, 32 - credits_seq_wait 225 - credits_seq_transform_overlay 0, 24, 144, 0 - credits_seq_transform_overlay 0, 0, 144, 0 - credits_seq_fade_out - - credits_seq_load_ow_map 48, 0, CHALLENGE_HALL - credits_seq_load_npc 14, 4, SOUTH, NPC_HOST - credits_seq_load_npc 18, 8, WEST, NPC_RONALD1 - credits_seq_load_npc 12, 8, EAST, NPC_PLAYER_CREDITS - credits_seq_init_overlay 0, 0, 144, 0 - credits_seq_draw_rectangle 0, 7 - credits_seq_print_text 0, 0, USCoordination1Text - credits_seq_fade_in - credits_seq_wait 60 - credits_seq_transform_overlay 0, 32, 144, 0 - credits_seq_transform_overlay 0, 32, 112, 32 - credits_seq_wait 225 - credits_seq_transform_overlay 0, 32, 144, 0 - credits_seq_draw_rectangle 4, 4 - credits_seq_print_text 0, 5, USCoordination2Text - credits_seq_transform_overlay 0, 32, 112, 32 - credits_seq_wait 225 - credits_seq_transform_overlay 0, 32, 144, 0 - credits_seq_transform_overlay 0, 0, 144, 0 - credits_seq_draw_rectangle 0, 7 - credits_seq_print_text 0, 0, USCoordination3Text - credits_seq_transform_overlay 0, 40, 144, 0 - credits_seq_transform_overlay 0, 40, 112, 32 - credits_seq_wait 225 - credits_seq_transform_overlay 0, 40, 144, 0 - credits_seq_draw_rectangle 6, 4 - credits_seq_print_text 0, 6, USCoordination4Text - credits_seq_transform_overlay 0, 40, 104, 40 - credits_seq_wait 225 - credits_seq_transform_overlay 0, 40, 144, 0 - credits_seq_transform_overlay 0, 0, 144, 0 - credits_seq_fade_out - - credits_seq_load_booster 6, 3, SCENE_COLOSSEUM_BOOSTER - credits_seq_init_overlay 0, 0, 144, 0 - credits_seq_draw_rectangle 0, 8 - credits_seq_print_text 0, 0, TranslationDraftText - credits_seq_fade_in - credits_seq_wait 60 - credits_seq_transform_overlay 0, 24, 144, 0 - credits_seq_transform_overlay 0, 24, 120, 24 - credits_seq_wait 225 - credits_seq_transform_overlay 0, 24, 144, 0 - credits_seq_transform_overlay 0, 0, 144, 0 - credits_seq_fade_out - - credits_seq_load_booster 6, 3, SCENE_EVOLUTION_BOOSTER - credits_seq_init_overlay 0, 0, 144, 0 - credits_seq_draw_rectangle 0, 6 - credits_seq_print_text 0, 0, MasteringText - credits_seq_fade_in - credits_seq_wait 60 - credits_seq_transform_overlay 0, 24, 144, 0 - credits_seq_transform_overlay 0, 24, 120, 24 - credits_seq_wait 225 - credits_seq_transform_overlay 0, 24, 144, 0 - credits_seq_transform_overlay 0, 0, 144, 0 - credits_seq_fade_out - - credits_seq_load_booster 6, 3, SCENE_MYSTERY_BOOSTER - credits_seq_init_overlay 0, 0, 144, 0 - credits_seq_draw_rectangle 0, 6 - credits_seq_print_text 0, 0, ManualCreationText - credits_seq_fade_in - credits_seq_wait 60 - credits_seq_transform_overlay 0, 24, 144, 0 - credits_seq_transform_overlay 0, 24, 120, 24 - credits_seq_wait 225 - credits_seq_transform_overlay 0, 24, 144, 0 - credits_seq_transform_overlay 0, 0, 144, 0 - credits_seq_fade_out - - credits_seq_load_booster 6, 3, SCENE_LABORATORY_BOOSTER - credits_seq_init_overlay 0, 0, 144, 0 - credits_seq_draw_rectangle 0, 6 - credits_seq_print_text 0, 0, ManualIllustrationsText - credits_seq_fade_in - credits_seq_wait 60 - credits_seq_transform_overlay 0, 24, 144, 0 - credits_seq_transform_overlay 0, 24, 120, 24 - credits_seq_wait 225 - credits_seq_transform_overlay 0, 24, 144, 0 - credits_seq_transform_overlay 0, 0, 144, 0 - credits_seq_fade_out - - credits_seq_load_club_map 4 - credits_seq_init_overlay 0, 0, 144, 0 - credits_seq_draw_rectangle 0, 7 - credits_seq_print_text 0, 0, PokemonOriginalStoryText - credits_seq_fade_in - credits_seq_wait 60 - credits_seq_transform_overlay 0, 24, 144, 0 - credits_seq_transform_overlay 0, 24, 120, 24 - credits_seq_wait 225 - credits_seq_transform_overlay 0, 24, 144, 0 - credits_seq_transform_overlay 0, 0, 144, 0 - credits_seq_draw_rectangle 0, 7 - credits_seq_print_text 0, 0, CreatedInCooperationWithText - credits_seq_wait 60 - credits_seq_transform_overlay 0, 24, 144, 0 - credits_seq_transform_overlay 0, 24, 104, 40 - credits_seq_wait 225 - credits_seq_transform_overlay 0, 24, 144, 0 - credits_seq_transform_overlay 0, 0, 144, 0 - credits_seq_fade_out - - credits_seq_load_club_map 5 - credits_seq_init_overlay 0, 0, 144, 0 - credits_seq_draw_rectangle 0, 7 - credits_seq_print_text 0, 0, WithCooperation1Text - credits_seq_fade_in - credits_seq_wait 60 - credits_seq_transform_overlay 0, 24, 144, 0 - credits_seq_transform_overlay 0, 24, 104, 40 - credits_seq_wait 225 - credits_seq_transform_overlay 0, 24, 144, 0 - credits_seq_draw_rectangle 4, 5 - credits_seq_print_text 0, 4, WithCooperation2Text - credits_seq_transform_overlay 0, 24, 96, 48 - credits_seq_wait 225 - credits_seq_transform_overlay 0, 24, 144, 0 - credits_seq_draw_rectangle 4, 4 - credits_seq_print_text 0, 4, WithCooperation3Text - credits_seq_transform_overlay 0, 24, 96, 48 - credits_seq_wait 225 - credits_seq_transform_overlay 0, 24, 144, 0 - credits_seq_transform_overlay 0, 0, 144, 0 - credits_seq_fade_out - - credits_seq_load_club_map 6 - credits_seq_init_overlay 0, 0, 144, 0 - credits_seq_draw_rectangle 0, 8 - credits_seq_print_text 0, 0, ProjectManagerText - credits_seq_fade_in - credits_seq_wait 60 - credits_seq_transform_overlay 0, 24, 144, 0 - credits_seq_transform_overlay 0, 24, 120, 24 - credits_seq_wait 225 - credits_seq_transform_overlay 0, 24, 144, 0 - credits_seq_transform_overlay 0, 0, 144, 0 - credits_seq_fade_out - - credits_seq_load_club_map 7 - credits_seq_init_overlay 0, 0, 144, 0 - credits_seq_draw_rectangle 0, 7 - credits_seq_print_text 0, 0, SupervisorText - credits_seq_fade_in - credits_seq_wait 60 - credits_seq_transform_overlay 0, 24, 144, 0 - credits_seq_transform_overlay 0, 24, 104, 40 - credits_seq_wait 225 - credits_seq_transform_overlay 0, 24, 144, 0 - credits_seq_transform_overlay 0, 0, 144, 0 - credits_seq_fade_out - - credits_seq_load_club_map 8 - credits_seq_init_overlay 0, 0, 144, 0 - credits_seq_draw_rectangle 0, 7 - credits_seq_print_text 0, 0, ExecutiveProducerText - credits_seq_fade_in - credits_seq_wait 60 - credits_seq_transform_overlay 0, 24, 144, 0 - credits_seq_transform_overlay 0, 24, 104, 40 - credits_seq_wait 225 - credits_seq_transform_overlay 0, 24, 144, 0 - credits_seq_transform_overlay 0, 0, 144, 0 - credits_seq_fade_out - - credits_seq_load_ow_map 16, 16, HALL_OF_HONOR - credits_seq_load_npc 10, 8, NORTH, NPC_LEGENDARY_CARD_TOP_LEFT - credits_seq_load_npc 12, 8, NORTH, NPC_LEGENDARY_CARD_TOP_RIGHT - credits_seq_load_npc 8, 10, NORTH, NPC_LEGENDARY_CARD_LEFT_SPARK - credits_seq_load_npc 10, 10, NORTH, NPC_LEGENDARY_CARD_BOTTOM_LEFT - credits_seq_load_npc 12, 10, NORTH, NPC_LEGENDARY_CARD_BOTTOM_RIGHT - credits_seq_load_npc 14, 10, NORTH, NPC_LEGENDARY_CARD_RIGHT_SPARK - credits_seq_init_overlay 0, 0, 144, 0 - credits_seq_draw_rectangle 0, 7 - credits_seq_print_text 0, 0, CreatedByText - credits_seq_fade_in - credits_seq_wait 60 - credits_seq_transform_overlay 0, 24, 144, 0 - credits_seq_transform_overlay 0, 24, 104, 40 - credits_seq_wait 225 - credits_seq_transform_overlay 0, 24, 144, 0 - credits_seq_transform_overlay 0, 0, 144, 0 - credits_seq_fade_out - - credits_seq_load_scene 0, 0, SCENE_COMPANIES - credits_seq_init_overlay 0, 0, 144, 0 - credits_seq_fade_in - credits_seq_wait 225 - credits_seq_end diff --git a/src/data/sequences/intro.asm b/src/data/sequences/intro.asm new file mode 100644 index 0000000..04ea67a --- /dev/null +++ b/src/data/sequences/intro.asm @@ -0,0 +1,47 @@ +INCLUDE "macros/intro_sequence.asm" + +IntroSequence: ; 1d59d (7:559d) + intro_seq_load_charizard_scene + intro_seq_play_sfx SFX_58 + intro_seq_set_orbs_coordinates OpeningOrbCoordinates_CharizardScene + intro_seq_set_orbs_animations OpeningOrbAnimations_CharizardScene + intro_seq_wait 44 + intro_seq_fade_in + intro_seq_wait 44 + intro_seq_fade_out + intro_seq_wait 30 + + intro_seq_load_scyther_scene + intro_seq_play_sfx SFX_58 + intro_seq_set_orbs_coordinates OpeningOrbCoordinates_ScytherScene + intro_seq_set_orbs_animations OpeningOrbAnimations_ScytherScene + intro_seq_wait 44 + intro_seq_fade_in + intro_seq_wait 44 + intro_seq_fade_out + intro_seq_wait 30 + + intro_seq_load_aerodactyl_scene + intro_seq_play_sfx SFX_59 + intro_seq_set_orbs_coordinates OpeningOrbCoordinates_AerodactylScene + intro_seq_set_orbs_animations OpeningOrbAnimations_AerodactylScene + intro_seq_wait 44 + intro_seq_fade_in + intro_seq_wait 100 + intro_seq_fade_out + intro_seq_wait 60 + + intro_seq_load_title_screen_scene + intro_seq_play_sfx SFX_5A + intro_seq_set_orbs_coordinates OpeningOrbCoordinates_InitialTitleScreen + intro_seq_set_orbs_animations OpeningOrbAnimations_InitialTitleScreen + intro_seq_wait_orbs_animation + intro_seq_fade_in + intro_seq_wait 16 + intro_seq_play_sfx SFX_5B + intro_seq_set_orbs_coordinates OpeningOrbCoordinates_InTitleScreen + intro_seq_set_orbs_animations OpeningOrbAnimations_InTitleScreen + intro_seq_wait_sfx + intro_seq_play_title_screen_music + intro_seq_wait 60 + intro_seq_end diff --git a/src/data/sequences/opening_sequence.asm b/src/data/sequences/opening_sequence.asm deleted file mode 100644 index a2a02ba..0000000 --- a/src/data/sequences/opening_sequence.asm +++ /dev/null @@ -1,47 +0,0 @@ -INCLUDE "macros/opening_sequence.asm" - -OpeningSequence: ; 1d59d (7:559d) - opening_seq_load_charizard_scene - opening_seq_play_sfx SFX_58 - opening_seq_set_orbs_coordinates OpeningOrbCoordinates_CharizardScene - opening_seq_set_orbs_animations OpeningOrbAnimations_CharizardScene - opening_seq_wait 44 - opening_seq_fade_in - opening_seq_wait 44 - opening_seq_fade_out - opening_seq_wait 30 - - opening_seq_load_scyther_scene - opening_seq_play_sfx SFX_58 - opening_seq_set_orbs_coordinates OpeningOrbCoordinates_ScytherScene - opening_seq_set_orbs_animations OpeningOrbAnimations_ScytherScene - opening_seq_wait 44 - opening_seq_fade_in - opening_seq_wait 44 - opening_seq_fade_out - opening_seq_wait 30 - - opening_seq_load_aerodactyl_scene - opening_seq_play_sfx SFX_59 - opening_seq_set_orbs_coordinates OpeningOrbCoordinates_AerodactylScene - opening_seq_set_orbs_animations OpeningOrbAnimations_AerodactylScene - opening_seq_wait 44 - opening_seq_fade_in - opening_seq_wait 100 - opening_seq_fade_out - opening_seq_wait 60 - - opening_seq_load_title_screen_scene - opening_seq_play_sfx SFX_5A - opening_seq_set_orbs_coordinates OpeningOrbCoordinates_InitialTitleScreen - opening_seq_set_orbs_animations OpeningOrbAnimations_InitialTitleScreen - opening_seq_wait_orbs_animation - opening_seq_fade_in - opening_seq_wait 16 - opening_seq_play_sfx SFX_5B - opening_seq_set_orbs_coordinates OpeningOrbCoordinates_InTitleScreen - opening_seq_set_orbs_animations OpeningOrbAnimations_InTitleScreen - opening_seq_wait_sfx - opening_seq_play_title_screen_music - opening_seq_wait 60 - opening_seq_end diff --git a/src/engine/bank04.asm b/src/engine/bank04.asm index 93a3ed3..940df79 100644 --- a/src/engine/bank04.asm +++ b/src/engine/bank04.asm @@ -5255,7 +5255,7 @@ Unknown_127fb: ; 127fb (4:67fb) db BOOSTER_ENERGY_LIGHTNING_FIRE DebugCredits: ; 12800 (4:6800) - farcall Credits_1d6ad + farcall PlayCreditsSequence scf ret @@ -5337,7 +5337,7 @@ Func_12871: ; 12871 (4:6871) ldh [hSCY], a ldh [hWX], a ldh [hWY], a - call Set_WD_off + call SetWDOff ret Func_1288c: ; 1288c (4:688c) diff --git a/src/engine/bank07.asm b/src/engine/bank07.asm index d0172e5..93ef008 100644 --- a/src/engine/bank07.asm +++ b/src/engine/bank07.asm @@ -1,24 +1,24 @@ -Func_1c000: ; 1c000 (7:4000) - jp Set_WD_off +JumpSetWDOff: + jp SetWDOff -; unreferenced debug function +; debug function ; prints player's coordinates by pressing B ; and draws palettes by pressing A -Func_1c003: ; 1c003 (7:4003) +Func_1c003: ; unreferenced ld a, [wCurMap] or a - jr z, Func_1c000 + jr z, JumpSetWDOff ld a, [wOverworldMode] cp OWMODE_START_SCRIPT - jr nc, Func_1c000 + jr nc, JumpSetWDOff ldh a, [hKeysHeld] ld b, a and A_BUTTON | B_BUTTON cp b - jr nz, Func_1c000 + jr nz, JumpSetWDOff and B_BUTTON - jr z, Func_1c000 + jr z, JumpSetWDOff ld bc, $20 ld a, [wPlayerXCoord] @@ -49,7 +49,7 @@ Func_1c003: ; 1c003 (7:4003) call Set_WD_on ret -Func_1c056: ; 1c056 (7:4056) +Func_1c056: push hl push bc push de @@ -100,7 +100,7 @@ Func_1c056: ; 1c056 (7:4056) INCLUDE "data/warps.asm" ; loads data from the map header of wCurMap -LoadMapHeader: ; 1c33b (7:433b) +LoadMapHeader: push hl push bc push de @@ -143,7 +143,7 @@ LoadMapHeader: ; 1c33b (7:433b) INCLUDE "data/map_headers.asm" -ClearNPCs: ; 1c440 (7:4440) +ClearNPCs: push hl push bc ld hl, wLoadedNPCs @@ -159,7 +159,7 @@ ClearNPCs: ; 1c440 (7:4440) pop hl ret -GetNPCDirection: ; 1c455 (7:4455) +GetNPCDirection: push hl ld a, [wLoadedNPCTempIndex] ld l, LOADED_NPC_DIRECTION @@ -171,7 +171,7 @@ GetNPCDirection: ; 1c455 (7:4455) ; sets new position to active NPC ; and updates its tile permissions ; bc = new coords -SetNPCPosition: ; 1c461 (7:4461) +SetNPCPosition: push hl push bc call UpdateNPCsTilePermission @@ -186,7 +186,7 @@ SetNPCPosition: ; 1c461 (7:4461) pop hl ret -GetNPCPosition: ; 1c477 (7:4477) +GetNPCPosition: push hl ld a, [wLoadedNPCTempIndex] ld l, LOADED_NPC_COORD_X @@ -198,7 +198,7 @@ GetNPCPosition: ; 1c477 (7:4477) ret ; Loads NPC Sprite Data -LoadNPC: ; 1c485 (7:4485) +LoadNPC: push hl push bc push de @@ -265,7 +265,7 @@ LoadNPC: ; 1c485 (7:4485) ret ; returns carry if input NPC ID in register a is Ronald -CheckIfNPCIsRonald: ; 1c4fa (7:44fa) +CheckIfNPCIsRonald: cp NPC_RONALD1 jr z, .set_carry cp NPC_RONALD2 @@ -278,7 +278,7 @@ CheckIfNPCIsRonald: ; 1c4fa (7:44fa) scf ret -UnloadNPC: ; 1c50a (7:450a) +UnloadNPC: push hl call UpdateNPCsTilePermission ld a, [wLoadedNPCTempIndex] @@ -303,7 +303,7 @@ UnloadNPC: ; 1c50a (7:450a) pop hl ret -Func_1c52e: ; 1c52e (7:452e) +Func_1c52e: push hl push af ld a, [wLoadedNPCTempIndex] @@ -315,7 +315,7 @@ Func_1c52e: ; 1c52e (7:452e) pop hl ret -Func_1c53f: ; 1c53f (7:453f) +Func_1c53f: push hl push bc ld a, [wLoadedNPCTempIndex] @@ -332,7 +332,7 @@ Func_1c53f: ; 1c53f (7:453f) pop hl ret -Func_1c557: ; 1c557 (7:4557) +Func_1c557: push bc ld c, a ld a, [wLoadedNPCTempIndex] @@ -357,7 +357,7 @@ Func_1c557: ; 1c557 (7:4557) ret ; a = NPC animation -SetNPCAnimation: ; 1c57b (7:457b) +SetNPCAnimation: push hl push bc push af @@ -371,7 +371,7 @@ SetNPCAnimation: ; 1c57b (7:457b) pop hl ret -UpdateNPCAnimation: ; 1c58e (7:458e) +UpdateNPCAnimation: push hl push bc ld a, [wWhichSprite] @@ -404,7 +404,7 @@ UpdateNPCAnimation: ; 1c58e (7:458e) ; give it a random initial value ; this makes it so that all NPCs are out of phase ; when they are loaded into a map -ApplyRandomCountToNPCAnim: ; 1c5b9 (7:45b9) +ApplyRandomCountToNPCAnim: push hl push bc ld a, [wWhichSprite] @@ -438,7 +438,7 @@ ApplyRandomCountToNPCAnim: ; 1c5b9 (7:45b9) ; sets the loaded NPC's direction ; to the direction that is in LOADED_NPC_DIRECTION_BACKUP -Func_1c5e9: ; 1c5e9 (7:45e9) +Func_1c5e9: push hl push bc ld a, [wLoadedNPCTempIndex] @@ -454,7 +454,7 @@ Func_1c5e9: ; 1c5e9 (7:45e9) ret ; a = new direction -SetNPCDirection: ; 1c5ff (7:45ff) +SetNPCDirection: push hl push af ld a, [wLoadedNPCTempIndex] @@ -466,7 +466,7 @@ SetNPCDirection: ; 1c5ff (7:45ff) pop hl ret -HandleAllNPCMovement: ; 1c610 (7:4610) +HandleAllNPCMovement: push hl push bc push de @@ -527,7 +527,7 @@ HandleAllNPCMovement: ; 1c610 (7:4610) pop hl ret -UpdateNPCSpritePosition: ; 1c665 (7:4665) +UpdateNPCSpritePosition: push hl push bc push de @@ -623,7 +623,7 @@ UpdateNPCSpritePosition: ; 1c665 (7:4665) ; ands wIsAnNPCMoving with the current ; NPC's NPC_FLAG_MOVING_F -UpdateIsAnNPCMovingFlag: ; 1c6d3 (7:46d3) +UpdateIsAnNPCMovingFlag: push hl push bc ld bc, LOADED_NPC_FLAGS @@ -635,7 +635,7 @@ UpdateIsAnNPCMovingFlag: ; 1c6d3 (7:46d3) pop hl ret -SetNPCsTilePermission: ; 1c6e3 (7:46e3) +SetNPCsTilePermission: push hl push bc ld a, [wLoadedNPCTempIndex] @@ -650,7 +650,7 @@ SetNPCsTilePermission: ; 1c6e3 (7:46e3) pop hl ret -SetAllNPCTilePermissions: ; 1c6f8 (7:46f8) +SetAllNPCTilePermissions: push hl push bc push de @@ -675,7 +675,7 @@ SetAllNPCTilePermissions: ; 1c6f8 (7:46f8) pop hl ret -UpdateNPCsTilePermission: ; 1c719 (7:4719) +UpdateNPCsTilePermission: push hl push bc ld a, [wLoadedNPCTempIndex] @@ -691,7 +691,7 @@ UpdateNPCsTilePermission: ; 1c719 (7:4719) ret ; Find NPC at coords b (x) c (y) -FindNPCAtLocation: ; 1c72e (7:472e) +FindNPCAtLocation: push hl push bc push de @@ -742,7 +742,7 @@ FindNPCAtLocation: ; 1c72e (7:472e) ; Probably needs a new name. Loads data for NPC that the next Script is for ; Sets direction, Loads Image data for it, loads name, and more -SetNewScriptNPC: ; 1c768 (7:4768) +SetNewScriptNPC: push hl ld a, [wLoadedNPCTempIndex] ld l, LOADED_NPC_DIRECTION @@ -760,7 +760,7 @@ SetNewScriptNPC: ; 1c768 (7:4768) pop hl ret -StartNPCMovement: ; 1c78d (7:478d) +StartNPCMovement: push hl ; set NPC as moving ld a, [wLoadedNPCTempIndex] @@ -825,7 +825,7 @@ StartNPCMovement: ; 1c78d (7:478d) ret ; returns nz if there is an NPC currently moving -CheckIsAnNPCMoving: ; 1c7de (7:47de) +CheckIsAnNPCMoving: ld a, [wIsAnNPCMoving] and NPC_FLAG_MOVING ret @@ -833,7 +833,7 @@ CheckIsAnNPCMoving: ; 1c7de (7:47de) ; while the NPC is moving, increment its movement step by 1 ; once it reaches a value greater than 16, update ; its tile permission and its position and start next movement -UpdateNPCMovementStep: ; 1c7e4 (7:47e4) +UpdateNPCMovementStep: push hl push bc push de @@ -861,7 +861,7 @@ UpdateNPCMovementStep: ; 1c7e4 (7:47e4) pop hl ret -UpdateNPCPosition: ; 1c80d (7:480d) +UpdateNPCPosition: push hl push bc ld a, [wLoadedNPCTempIndex] @@ -888,7 +888,7 @@ UpdateNPCPosition: ; 1c80d (7:480d) pop hl ret -ClearMasterBeatenList: ; 1c82e (7:482e) +ClearMasterBeatenList: push hl push bc ld c, $a @@ -904,7 +904,7 @@ ClearMasterBeatenList: ; 1c82e (7:482e) ; writes Master in register a to ; first empty slot in wMastersBeatenList -AddMasterBeatenToList: ; 1c83d (7:483d) +AddMasterBeatenToList: push hl push bc ld b, a @@ -933,7 +933,7 @@ AddMasterBeatenToList: ; 1c83d (7:483d) ; iterates all masters and attempts to ; add each of them to wMastersBeatenList -AddAllMastersToMastersBeatenList: ; 1c858 (7:4858) +AddAllMastersToMastersBeatenList: ld a, $01 .loop push af @@ -944,13 +944,13 @@ AddAllMastersToMastersBeatenList: ; 1c858 (7:4858) jr c, .loop ret -Func_1c865: ; 1c865 (7:4865) +Func_1c865: ret -; unreferenced debug function +; debug function ; adjusts hSCX and hSCY by using the arrow keys ; pressing B makes it scroll faster -Func_1c866: ; 1c866 (7:4866) +Func_1c866: ; unreferenced ldh a, [hKeysHeld] and B_BUTTON call nz, .asm_1c86d ; executes following part twice @@ -982,9 +982,8 @@ Func_1c866: ; 1c866 (7:4866) ldh [hSCY], a ret -; unreferenced ; sets some flags on a given sprite -Func_1c890: ; 1c890 (7:4890) +Func_1c890: ; unreferenced ld a, [wVBlankCounter] and %111111 ret nz @@ -1012,1775 +1011,3 @@ Func_1c890: ; 1c890 (7:4890) set SPRITE_ANIM_FLAG_SPEED, [hl] .asm_1c8bb ret - -Func_1c8bc: ; 1c8bc (7:48bc) - push hl - push bc - call Set_OBJ_8x8 - ld a, LOW(Func_3ba2) - ld [wDoFrameFunction], a - ld a, HIGH(Func_3ba2) - ld [wDoFrameFunction + 1], a - ld a, $ff - ld hl, wAnimationQueue - ld c, ANIMATION_QUEUE_LENGTH -.fill_queue - ld [hli], a - dec c - jr nz, .fill_queue - ld [wd42a], a - ld [wd4c0], a - xor a - ld [wDuelAnimBufferCurPos], a - ld [wDuelAnimBufferSize], a - ld [wd4b3], a - call DefaultScreenAnimationUpdate - call Func_3ca0 - pop bc - pop hl - ret - -PlayLoadedDuelAnimation: ; 1c8ef (7:48ef) - ld a, [wDoFrameFunction + 0] - cp LOW(Func_3ba2) - jr nz, .error - ld a, [wDoFrameFunction + 1] - cp HIGH(Func_3ba2) - jr z, .okay -.error - debug_nop - ret - -.okay - ld a, [wTempAnimation] - ld [wd4bf], a - cp DUEL_SPECIAL_ANIMS - jp nc, Func_1cb5e - - push hl - push bc - push de - call GetAnimationData -; hl: pointer - - ld a, [wAnimationsDisabled] - or a - jr z, .check_to_play_sfx - ; animations are disabled - push hl - ld bc, ANIM_SPRITE_ANIM_FLAGS - add hl, bc - ld a, [hl] - ; if flag is set, play animation anyway - and (1 << SPRITE_ANIM_FLAG_UNSKIPPABLE) - pop hl - jr z, .return - -.check_to_play_sfx - push hl - ld bc, ANIM_SOUND_FX_ID - add hl, bc - ld a, [hl] - pop hl - or a - jr z, .calc_addr - call PlaySFX - -.calc_addr -; this data field is always $00, -; so this calculation is unnecessary -; seems like there was supposed to be -; more than 1 function to handle animation - push hl - ld bc, ANIM_HANDLER_FUNCTION - add hl, bc - ld a, [hl] - rlca - add LOW(.address) ; $48 - ld l, a ; LO - ld a, HIGH(.address) ; $49 - adc 0 - ld h, a ; HI -; hl: pointer - ld a, [hli] - ld b, [hl] - ld c, a - pop hl - - call CallBC -.return - pop de - pop bc - pop hl - ret - -.address - dw .handler_func - -.handler_func ; 1c94a (7:494a) -; if any of ANIM_SPRITE_ID, ANIM_PALETTE_ID and ANIM_SPRITE_ANIM_ID -; are 0, then return - ld e, l - ld d, h - ld c, ANIM_SPRITE_ANIM_ID + 1 -.loop - ld a, [de] - or a - jr z, .return_with_carry - inc de - dec c - jr nz, .loop - - ld a, [hli] ; ANIM_SPRITE_ID - farcall CreateSpriteAndAnimBufferEntry - ld a, [wWhichSprite] - ld [wAnimationQueue], a ; push an animation to the queue - - xor a - ld [wVRAMTileOffset], a - ld [wd4cb], a - - ld a, [hli] ; ANIM_PALETTE_ID - farcall LoadPaletteData - ld a, [hli] ; ANIM_SPRITE_ANIM_ID - - push af - ld a, [hli] ; ANIM_SPRITE_ANIM_FLAGS - ld [wAnimFlags], a - call LoadAnimCoordsAndFlags - pop af - - farcall StartNewSpriteAnimation - or a - jr .done - -.return_with_carry - scf -.done - ret - -; loads the correct coordinates/flags for -; sprite animation in wAnimationQueue -LoadAnimCoordsAndFlags: ; 1c980 (7:4980) - push hl - push bc - ld a, [wAnimationQueue] - ld c, SPRITE_ANIM_ATTRIBUTES - call GetSpriteAnimBufferProperty_SpriteInA - call GetAnimCoordsAndFlags - - push af - and (1 << SPRITE_ANIM_FLAG_6) | (1 << SPRITE_ANIM_FLAG_5) - or [hl] - ld [hli], a - ld a, b - ld [hli], a ; SPRITE_ANIM_COORD_X - ld [hl], c ; SPRITE_ANIM_COORD_Y - pop af - - ld bc, SPRITE_ANIM_FLAGS - SPRITE_ANIM_COORD_Y - add hl, bc - ld c, a ; useless - and (1 << SPRITE_ANIM_FLAG_Y_SUBTRACT) | (1 << SPRITE_ANIM_FLAG_X_SUBTRACT) - or [hl] - ld [hl], a - pop bc - pop hl - ret - -; outputs x and y coordinates for the sprite animation -; taking into account who the turn duelist is. -; also returns in a the allowed animation flags of -; the configuration that is selected. -; output: -; a = anim flags -; b = x coordinate -; c = y coordinate -GetAnimCoordsAndFlags: ; 1c9a2 (7:49a2) - push hl - ld c, 0 - ld a, [wAnimFlags] - and (1 << SPRITE_ANIM_FLAG_SPEED) - jr nz, .calc_addr - - ld a, [wDuelAnimationScreen] - add a ; 2 * [wDuelAnimationScreen] - ld c, a - add a ; 4 * [wDuelAnimationScreen] - add c ; 6 * [wDuelAnimationScreen] - add a ; 12 * [wDuelAnimationScreen] - ld c, a - - ld a, [wDuelAnimDuelistSide] - cp PLAYER_TURN - jr z, .player_side -; opponent side - ld a, 6 - add c - ld c, a -.player_side - ld a, [wDuelAnimLocationParam] - add c ; a = [wDuelAnimLocationParam] + c - ld c, a - ld b, 0 - ld hl, AnimationCoordinatesIndex - add hl, bc - ld c, [hl] - -.calc_addr - ld a, c - add a ; a = c * 2 - add c ; a = c * 3 - ld c, a - ld b, 0 - ld hl, AnimationCoordinates - add hl, bc - ld b, [hl] ; x coord - inc hl - ld c, [hl] ; y coord - inc hl - ld a, [wAnimFlags] - and [hl] ; flags - pop hl - ret - -AnimationCoordinatesIndex: -; animations in the Duel Main Scene - db $01, $01, $01, $01, $01, $01 ; player - db $02, $02, $02, $02, $02, $02 ; opponent - -; animations in the Player's Play Area, for each Play Area Pokemon - db $03, $04, $05, $06, $07, $08 ; player - db $03, $04, $05, $06, $07, $08 ; opponent - -; animations in the Opponent's Play Area, for each Play Area Pokemon - db $09, $0a, $0b, $0c, $0d, $0e ; player - db $09, $0a, $0b, $0c, $0d, $0e ; opponent - -anim_coords: MACRO - db \1 - db \2 - db \3 -ENDM - -AnimationCoordinates: -; x coord, y coord, animation flags - anim_coords 88, 88, (1 << SPRITE_ANIM_FLAG_3) - -; animations in the Duel Main Scene - anim_coords 40, 80, $00 - anim_coords 136, 48, (1 << SPRITE_ANIM_FLAG_6) | (1 << SPRITE_ANIM_FLAG_5) | (1 << SPRITE_ANIM_FLAG_Y_SUBTRACT) | (1 << SPRITE_ANIM_FLAG_X_SUBTRACT) - -; animations in the Player's Play Area, for each Play Area Pokemon - anim_coords 88, 72, $00 - anim_coords 24, 96, $00 - anim_coords 56, 96, $00 - anim_coords 88, 96, $00 - anim_coords 120, 96, $00 - anim_coords 152, 96, $00 - -; animations in the Opponent's Play Area, for each Play Area Pokemon - anim_coords 88, 80, $00 - anim_coords 152, 40, $00 - anim_coords 120, 40, $00 - anim_coords 88, 40, $00 - anim_coords 56, 40, $00 - anim_coords 24, 40, $00 - -; appends to end of wDuelAnimBuffer -; the current duel animation -LoadDuelAnimationToBuffer: ; 1ca31 (7:4a31) - push hl - push bc - ld a, [wDuelAnimBufferCurPos] - ld b, a - ld hl, wDuelAnimBufferSize - ld a, [hl] - ld c, a - add DUEL_ANIM_STRUCT_SIZE - and %01111111 - cp b - jp z, .skip - ld [hl], a - - ld b, $00 - ld hl, wDuelAnimBuffer - add hl, bc - ld a, [wTempAnimation] - ld [hli], a - ld a, [wDuelAnimationScreen] - ld [hli], a - ld a, [wDuelAnimDuelistSide] - ld [hli], a - ld a, [wDuelAnimLocationParam] - ld [hli], a - ld a, [wDuelAnimDamage] - ld [hli], a - ld a, [wDuelAnimDamage + 1] - ld [hli], a - ld a, [wd4b3] - ld [hli], a - ld a, [wDuelAnimReturnBank] - ld [hl], a - -.skip - pop bc - pop hl - ret - -; loads the animations from wDuelAnimBuffer -; in ascending order, starting at wDuelAnimBufferCurPos -PlayBufferedDuelAnimations: ; 1ca6e (7:4a6e) - push hl - push bc -.next_duel_anim - ld a, [wDuelAnimBufferSize] - ld b, a - ld a, [wDuelAnimBufferCurPos] - cp b - jr z, .skip - - ld c, a - add DUEL_ANIM_STRUCT_SIZE - and %01111111 - ld [wDuelAnimBufferCurPos], a - - ld b, $00 - ld hl, wDuelAnimBuffer - add hl, bc - ld a, [hli] - ld [wTempAnimation], a - ld a, [hli] - ld [wDuelAnimationScreen], a - ld a, [hli] - ld [wDuelAnimDuelistSide], a - ld a, [hli] - ld [wDuelAnimLocationParam], a - ld a, [hli] - ld [wDuelAnimDamage], a - ld a, [hli] - ld [wDuelAnimDamage + 1], a - ld a, [hli] - ld [wd4b3], a - ld a, [hl] - ld [wDuelAnimReturnBank], a - - call PlayLoadedDuelAnimation - call CheckAnyAnimationPlaying - jr nc, .next_duel_anim - -.skip - pop bc - pop hl - ret - -; gets data from Animations for anim ID in a -; outputs the pointer to the data in hl -GetAnimationData: ; 1cab3 (7:4ab3) - push bc - ld a, [wTempAnimation] - ld l, a - ld h, 0 - add hl, hl ; hl = anim * 2 - ld b, h - ld c, l - add hl, hl ; hl = anim * 4 - add hl, bc ; hl = anim * 6 - ld bc, Animations - add hl, bc - pop bc - ret - -Func_1cac5: ; 1cac5 (7:4ac5) - ld a, [wd42a] - cp $ff - jr nz, .asm_1cb03 - - ld a, [wd4c0] - or a - jr z, .asm_1cafb - cp $80 - jr z, .asm_1cb11 - ld hl, wAnimationQueue - ld c, ANIMATION_QUEUE_LENGTH -.loop_queue - push af - push bc - ld a, [hl] - cp $ff - jr z, .next - ld [wWhichSprite], a - farcall GetSpriteAnimCounter - cp $ff - jr nz, .next - farcall DisableCurSpriteAnim - ld a, $ff - ld [hl], a - -.next - pop bc - pop af - and [hl] - inc hl - dec c - jr nz, .loop_queue - -.asm_1cafb - cp $ff - jr nz, .skip_play_anims - call PlayBufferedDuelAnimations -.skip_play_anims - ret - -.asm_1cb03 - ld hl, wScreenAnimUpdatePtr - ld a, [hli] - ld h, [hl] - ld l, a - call CallHL2 - ld a, [wd42a] - jr .asm_1cafb - -.asm_1cb11 - ld a, $ff - ld [wd4c0], a - jr .asm_1cafb - -Func_1cb18: ; 1cb18 (7:4b18) - push hl - push bc - push de - - ; if Func_3ba2 is not set as - ; wDoFrameFunction, quit and set carry - ld a, [wDoFrameFunction] - cp LOW(Func_3ba2) - jr nz, .carry - ld a, [wDoFrameFunction + 1] - cp HIGH(Func_3ba2) - jr nz, .carry - - ld a, $ff - ld [wd4c0], a - ld a, [wd42a] - cp $ff - call nz, DoScreenAnimationUpdate - -; clear all queued animations -; and disable their sprite anims - ld hl, wAnimationQueue - ld c, ANIMATION_QUEUE_LENGTH -.loop_queue - push bc - ld a, [hl] - cp $ff - jr z, .next_queued - ld [wWhichSprite], a - farcall DisableCurSpriteAnim - ld a, $ff - ld [hl], a -.next_queued - pop bc - inc hl - dec c - jr nz, .loop_queue - - xor a - ld [wDuelAnimBufferCurPos], a - ld [wDuelAnimBufferSize], a -.done - pop de - pop bc - pop hl - ret -.carry - scf - jr .done - -Func_1cb5e: ; 1cb5e (7:4b5e) - cp $96 - jp nc, Func_1ce03 - cp $8c - jp nz, InitScreenAnimation - jr .asm_1cb6a ; redundant -.asm_1cb6a - ld a, [wDuelAnimDamage + 1] - cp $03 - jr nz, .asm_1cb76 - ld a, [wDuelAnimDamage] - cp $e8 -.asm_1cb76 - ret nc - - xor a - ld [wd4b8], a - ld [wVRAMTileOffset], a - ld [wd4cb], a - - ld a, PALETTE_37 - farcall LoadPaletteData - call Func_1cba6 - - ld hl, wd4b3 - bit 0, [hl] - call nz, Func_1cc3e - - ld a, $12 - ld [wd4b8], a - bit 1, [hl] - call nz, Func_1cc4e - - bit 2, [hl] - call nz, Func_1cc66 - - xor a - ld [wd4b3], a - ret - -Func_1cba6: ; 1cba6 (7:4ba6) - call Func_1cc03 - xor a - ld [wd4b7], a - - ld hl, wd4b4 - ld de, wAnimationQueue + 1 -.asm_1cbb3 - push hl - push de - ld a, [hl] - or a - jr z, .asm_1cbbc - call Func_1cbcc - -.asm_1cbbc - pop de - pop hl - inc hl - inc de - ld a, [wd4b7] - inc a - ld [wd4b7], a - cp $03 - jr c, .asm_1cbb3 - ret - -Func_1cbcc: ; 1cbcc (7:4bcc) - push af - ld a, SPRITE_DUEL_4 - farcall CreateSpriteAndAnimBufferEntry - ld a, [wWhichSprite] - ld [de], a - ld a, (1 << SPRITE_ANIM_FLAG_UNSKIPPABLE) - ld [wAnimFlags], a - ld c, SPRITE_ANIM_COORD_X - call GetSpriteAnimBufferProperty - call GetAnimCoordsAndFlags - - ld a, [wd4b7] - add LOW(Unknown_1cbfd) - ld e, a - ld a, HIGH(Unknown_1cbfd) - adc 0 - ld d, a - ld a, [de] - add b - - ld [hli], a ; SPRITE_ANIM_COORD_X - ld [hl], c ; SPRITE_ANIM_COORD_Y - - ld a, [wd4b8] - ld c, a - pop af - farcall Func_12ac9 - ret - -Unknown_1cbfd: ; 1cbfd (7:4bfd) - db -$10, -$8, $0, $8, -$8, -$10 - -Func_1cc03: ; 1cc03 (7:4c03) - ld a, [wDuelAnimDamage] - ld l, a - ld a, [wDuelAnimDamage + 1] - ld h, a - - ld de, wd4b4 - ld bc, -100 - call .Func_1cc2f - ld bc, -10 - call .Func_1cc2f - - ld a, l - add $4f - ld [de], a - ld hl, wd4b4 - ld c, 2 -.asm_1cc23 - ld a, [hl] - cp $4f - jr nz, .asm_1cc2e - ld [hl], $00 - inc hl - dec c - jr nz, .asm_1cc23 -.asm_1cc2e - ret - -.Func_1cc2f - ld a, $4e -.loop - inc a - add hl, bc - jr c, .loop - - ld [de], a - inc de - ld a, l - sub c - ld l, a - ld a, h - sbc b - ld h, a - ret - -Func_1cc3e: ; 1cc3e (7:4c3e) - push hl - ld a, $03 - ld [wd4b7], a - ld de, wAnimationQueue + 4 - ld a, SPRITE_ANIM_91 - call Func_1cbcc - pop hl - ret - -Func_1cc4e: ; 1cc4e (7:4c4e) - push hl - ld a, $04 - ld [wd4b7], a - ld de, wAnimationQueue + 5 - ld a, SPRITE_ANIM_90 - call Func_1cbcc - ld a, [wd4b8] - add $12 - ld [wd4b8], a - pop hl - ret - -Func_1cc66: ; 1cc66 (7:4c66) - push hl - ld a, $05 - ld [wd4b7], a - ld de, wAnimationQueue + 6 - ld a, SPRITE_ANIM_89 - call Func_1cbcc - pop hl - ret - -; initializes a screen animation from wTempAnimation -; loads a function pointer for updating a frame -; and initializes the duration of the animation. -InitScreenAnimation: ; 1cc76 (7:4c76) - ld a, [wAnimationsDisabled] - or a - jr nz, .skip - ld a, [wTempAnimation] - ld [wd42a], a - sub DUEL_SCREEN_ANIMS - add a - add a - ld c, a - ld b, $00 - ld hl, Data_1cc9f - add hl, bc - ld a, [hli] - ld [wScreenAnimUpdatePtr], a - ld c, a - ld a, [hli] - ld [wScreenAnimUpdatePtr + 1], a - ld b, a - ld a, [hl] - ld [wScreenAnimDuration], a - call CallBC -.skip - ret - -; for the following animations, these functions -; are run with the corresponding duration. -; this duration decides different effects, -; depending on which function runs -; and is decreased by one each time. -; when it is down to 0, the animation is done. - -screen_effect: MACRO - dw \1 ; function pointer - db \2 ; duration - db $00 ; padding -ENDM - -Data_1cc9f: ; 1cc9f (7:4c9f) -; function pointer, duration - screen_effect ShakeScreenX_Small, 24 ; DUEL_ANIM_SMALL_SHAKE_X - screen_effect ShakeScreenX_Big, 32 ; DUEL_ANIM_BIG_SHAKE_X - screen_effect ShakeScreenY_Small, 24 ; DUEL_ANIM_SMALL_SHAKE_Y - screen_effect ShakeScreenY_Big, 32 ; DUEL_ANIM_BIG_SHAKE_Y - screen_effect WhiteFlashScreen, 8 ; DUEL_ANIM_FLASH - screen_effect DistortScreen, 63 ; DUEL_ANIM_DISTORT - -; checks if screen animation duration is over -; and if so, loads the default update function -LoadDefaultScreenAnimationUpdateWhenFinished: ; 1ccb7 (7:4cb7) - ld a, [wScreenAnimDuration] - or a - ret nz - ; fallthrough - -; function called for the screen animation update when it is over -DefaultScreenAnimationUpdate: ; 1ccbc (7:4cbc) - ld a, $ff - ld [wd42a], a - call DisableInt_LYCoincidence - xor a - ldh [hSCX], a - ldh [rSCX], a - ldh [hSCY], a - ld hl, wScreenAnimUpdatePtr - ld [hl], LOW(DefaultScreenAnimationUpdate) - inc hl - ld [hl], HIGH(DefaultScreenAnimationUpdate) - ret - -; runs the screen update function set in wScreenAnimUpdatePtr -DoScreenAnimationUpdate: ; 1ccd4 (7:4cd4) - ld a, 1 - ld [wScreenAnimDuration], a - ld hl, wScreenAnimUpdatePtr - ld a, [hli] - ld h, [hl] - ld l, a - call CallHL2 - jr DefaultScreenAnimationUpdate - -ShakeScreenX_Small: ; 1cce4 (7:4ce4) - ld hl, SmallShakeOffsets - jr ShakeScreenX - -ShakeScreenX_Big: ; 1cce9 (7:4ce9) - ld hl, BigShakeOffsets - jr ShakeScreenX - -ShakeScreenX: ; 1ccee (7:4cee) - ld a, l - ld [wd4bc], a - ld a, h - ld [wd4bc + 1], a - - ld hl, wScreenAnimUpdatePtr - ld [hl], LOW(.update) - inc hl - ld [hl], HIGH(.update) - ret - -.update - call DecrementScreenAnimDuration - call UpdateShakeOffset - jp nc, LoadDefaultScreenAnimationUpdateWhenFinished - ldh a, [hSCX] - add [hl] - ldh [hSCX], a - jp LoadDefaultScreenAnimationUpdateWhenFinished - -ShakeScreenY_Small: ; 1cd10 (7:4d10) - ld hl, SmallShakeOffsets - jr ShakeScreenY - -ShakeScreenY_Big: ; 1cd15 (7:4d15) - ld hl, BigShakeOffsets - jr ShakeScreenY - -ShakeScreenY: ; 1cd1a (7:4d1a) - ld a, l - ld [wd4bc], a - ld a, h - ld [wd4bc + 1], a - ld hl, wScreenAnimUpdatePtr - ld [hl], LOW(.update) - inc hl - ld [hl], HIGH(.update) - ret - -.update - call DecrementScreenAnimDuration - call UpdateShakeOffset - jp nc, LoadDefaultScreenAnimationUpdateWhenFinished - ldh a, [hSCY] - add [hl] - ldh [hSCY], a - jp LoadDefaultScreenAnimationUpdateWhenFinished - -; get the displacement of the current frame -; depending on the value of wScreenAnimDuration -; returns carry if displacement was updated -UpdateShakeOffset: ; 1cd3c (7:4d3c) - ld hl, wd4bc - ld a, [hli] - ld h, [hl] - ld l, a - ld a, [wScreenAnimDuration] - cp [hl] - ret nc - inc hl - push hl - inc hl - ld a, l - ld [wd4bc], a - ld a, h - ld [wd4bc + 1], a - pop hl - scf - ret - -SmallShakeOffsets: ; 1cd55 (7:4d55) - db 21, 2 - db 17, -2 - db 13, 2 - db 9, -2 - db 5, 1 - db 1, -1 - -BigShakeOffsets: ; 1cd61 (7:4d61) - db 29, 4 - db 25, -4 - db 21, 4 - db 17, -4 - db 13, 3 - db 9, -3 - db 5, 2 - db 1, -2 - -DecrementScreenAnimDuration: ; 1cd71 (7:4d71) - ld hl, wScreenAnimDuration - dec [hl] - ret - -WhiteFlashScreen: ; 1cd76 (7:4d76) - ld hl, wScreenAnimUpdatePtr - ld [hl], LOW(.update) - inc hl - ld [hl], HIGH(.update) - ld a, [wBGP] - ld [wd4bc], a - ; backup the current background pals - ld hl, wBackgroundPalettesCGB - ld de, wTempBackgroundPalettesCGB - ld bc, 8 palettes - call CopyDataHLtoDE_SaveRegisters - ld de, PALRGB_WHITE - ld hl, wBackgroundPalettesCGB - ld bc, (8 palettes) / 2 - call FillMemoryWithDE - xor a - call SetBGP - call FlushAllPalettes - -.update - call DecrementScreenAnimDuration - ld a, [wScreenAnimDuration] - or a - ret nz - ; retrieve the previous background pals - ld hl, wTempBackgroundPalettesCGB - ld de, wBackgroundPalettesCGB - ld bc, 8 palettes - call CopyDataHLtoDE_SaveRegisters - ld a, [wd4bc] - call SetBGP - call FlushAllPalettes - jp DefaultScreenAnimationUpdate - -DistortScreen: ; 1cdc3 (7:4dc3) - ld hl, wScreenAnimUpdatePtr - ld [hl], LOW(.update) - inc hl - ld [hl], HIGH(.update) - xor a - ld [wApplyBGScroll], a - ld hl, wLCDCFunctionTrampoline + 1 - ld [hl], LOW(ApplyBackgroundScroll) - inc hl - ld [hl], HIGH(ApplyBackgroundScroll) - ld a, 1 - ld [wBGScrollMod], a - call EnableInt_LYCoincidence - -.update - ld a, [wScreenAnimDuration] - srl a - srl a - srl a - and %00000111 - ld c, a - ld b, $00 - ld hl, .BGScrollModData - add hl, bc - ld a, [hl] - ld [wBGScrollMod], a - call DecrementScreenAnimDuration - jp LoadDefaultScreenAnimationUpdateWhenFinished - -; each value is applied for 8 "ticks" of wScreenAnimDuration -; starting from the last and running backwards -.BGScrollModData - db 4, 3, 2, 1, 1, 1, 1, 2 - -Func_1ce03: ; 1ce03 (7:4e03) - cp DUEL_ANIM_158 - jr z, .asm_1ce17 - sub $96 - add a - ld c, a - ld b, $00 - ld hl, .pointer_table - add hl, bc - ld a, [hli] - ld h, [hl] - ld l, a - jp Func_3bb5 - -.asm_1ce17 - ld a, [wDuelAnimDamage] - ld l, a - ld a, [wDuelAnimDamage + 1] - ld h, a - jp Func_3bb5 - -.pointer_table - dw Func_190f4 ; DUEL_ANIM_150 - dw PrintDamageText ; DUEL_ANIM_PRINT_DAMAGE - dw UpdateMainSceneHUD ; DUEL_ANIM_UPDATE_HUD - dw Func_191a3 ; DUEL_ANIM_153 - dw Func_191a3 ; DUEL_ANIM_154 - dw Func_191a3 ; DUEL_ANIM_155 - dw Func_191a3 ; DUEL_ANIM_156 - dw Func_191a3 ; DUEL_ANIM_157 - -INCLUDE "data/duel/animations/duel_animations.asm" - -; plays the Opening sequence, and handles player selection -; in the Title Screen and Start Menu -HandleTitleScreen: ; 1d078 (7:5078) -; if last selected item in Start Menu is 0 (Card Pop!) -; then skip straight to the Start Menu -; this makes it so that returning from Card Pop! -; doesn't play the Opening sequence - ld a, [wLastSelectedStartMenuItem] - or a - jr z, .start_menu - -.play_opening - ld a, MUSIC_STOP - call PlaySong - call Func_3ca0 - call PlayOpeningSequence - call LoadTitleScreenSprites - - xor a - ld [wd635], a - ld a, $3c - ld [wTitleScreenIgnoreInputCounter], a -.loop - call DoFrameIfLCDEnabled - call UpdateRNGSources - call AnimateRandomTitleScreenOrb - ld hl, wd635 - inc [hl] - call AssertSongFinished - or a - jr nz, .song_playing - ; reset back to the opening sequence - farcall Func_10ab4 - jr .play_opening - -.song_playing - ; should we ignore user input? - ld hl, wTitleScreenIgnoreInputCounter - ld a, [hl] - or a - jr z, .check_keys - ; ignore input, decrement the counter - dec [hl] - jr .loop - -.check_keys - ldh a, [hKeysPressed] - and A_BUTTON | START - jr z, .loop - ld a, SFX_02 - call PlaySFX - farcall Func_10ab4 - -.start_menu - call CheckIfHasSaveData - call HandleStartMenu - -; new game - ld a, [wStartMenuChoice] - cp START_MENU_NEW_GAME - jr nz, .continue_from_diary - call DeleteSaveDataForNewGame - jr c, HandleTitleScreen - jr .card_pop -.continue_from_diary - ld a, [wStartMenuChoice] - cp START_MENU_CONTINUE_FROM_DIARY - jr nz, .card_pop - call AskToContinueFromDiaryWithDuelData - jr c, HandleTitleScreen -.card_pop - ld a, [wStartMenuChoice] - cp START_MENU_CARD_POP - jr nz, .continue_duel - call ShowCardPopCGBDisclaimer - jr c, HandleTitleScreen -.continue_duel - call ResetDoFrameFunction - call Func_3ca0 - ret - -; updates wHasSaveData and wHasDuelSaveData -; depending on whether the save data is valid or not -CheckIfHasSaveData: ; 1d0fa (7:50fa) - farcall ValidateBackupGeneralSaveData - ld a, TRUE - jr c, .no_error - ld a, FALSE -.no_error - ld [wHasSaveData], a - cp $00 ; or a - jr z, .write_has_duel_data - bank1call ValidateSavedNonLinkDuelData - ld a, TRUE - jr nc, .write_has_duel_data - ld a, FALSE -.write_has_duel_data - ld [wHasDuelSaveData], a - farcall ValidateBackupGeneralSaveData - ret - -; handles printing the Start Menu -; and getting player input and choice -HandleStartMenu: ; 1d11c (7:511c) - ld a, MUSIC_PC_MAIN_MENU - call PlaySong - call DisableLCD - farcall Func_10000 - lb de, $30, $8f - call SetupText - call Func_3ca0 - xor a - ld [wLineSeparation], a - call .DrawPlayerPortrait - call .SetStartMenuParams - - ld a, $ff - ld [wTitleScreenIgnoreInputCounter], a - ld a, [wLastSelectedStartMenuItem] - cp $4 - jr c, .init_menu - ld a, [wHasSaveData] - or a - jr z, .init_menu - ld a, 1 ; start at second menu option -.init_menu - ld hl, wStartMenuParams - farcall InitAndPrintPauseMenu - farcall FlashWhiteScreen - -.wait_input - call DoFrameIfLCDEnabled - call UpdateRNGSources - call HandleMenuInput - push af - call PrintStartMenuDescriptionText - pop af - jr nc, .wait_input - ldh a, [hCurMenuItem] - cp e - jr nz, .wait_input - - ld [wLastSelectedStartMenuItem], a - ld a, [wHasSaveData] - or a - jr nz, .no_adjustment - ; New Game is 3rd option - ; but when there's no save data, - ; it's the 1st in menu list, so adjust it - inc e - inc e -.no_adjustment - ld a, e - ld [wStartMenuChoice], a - ret - -.SetStartMenuParams - ld hl, .StartMenuParams - ld de, wStartMenuParams - ld bc, .StartMenuParamsEnd - .StartMenuParams - call CopyDataHLtoDE - - ld e, 0 - ld a, [wHasSaveData] - or a - jr z, .get_text_id ; New Game - inc e - ld a, 2 - call .AddItems - ld a, [wHasDuelSaveData] - or a - jr z, .get_text_id ; Continue From Diary - inc e - ld a, 1 - call .AddItems - ; Continue Duel - -.get_text_id - sla e - ld d, $00 - ld hl, .StartMenuTextIDs - add hl, de - ; set text ID as Start Menu param - ld a, [hli] - ld [wStartMenuParams + 6], a - ld a, [hl] - ld [wStartMenuParams + 7], a - ret - -; adds c items to start menu list -; this means adding 2 units per item to the text box height -; and adding to the number of items -.AddItems - push bc - ld c, a - ; number of items in menu - ld a, [wStartMenuParams + 12] - add c - ld [wStartMenuParams + 12], a - ; height of text box - sla c - ld a, [wStartMenuParams + 3] - add c - ld [wStartMenuParams + 3], a - pop bc - ret - -.StartMenuParams - db 0, 0 ; start menu coords - db 14, 4 ; start menu text box dimensions - - db 2, 2 ; text alignment for InitTextPrinting - tx NewGameText - db $ff - - db 1, 2 ; cursor x, cursor y - db 2 ; y displacement between items - db 1 ; number of items - db SYM_CURSOR_R ; cursor tile number - db SYM_SPACE ; tile behind cursor - dw NULL ; function pointer if non-0 -.StartMenuParamsEnd - -.StartMenuTextIDs - tx NewGameText - tx CardPopContinueDiaryNewGameText - tx CardPopContinueDiaryNewGameContinueDuelText - -.DrawPlayerPortrait - lb bc, 14, 1 - farcall $4, DrawPlayerPortrait - ret - -; prints the description for the current selected item -; in the Start Menu in the text box -PrintStartMenuDescriptionText: ; 1d1e9 (7:51e9) - push hl - push bc - push de - ; don't print if it's already showing - ld a, [wCurMenuItem] - ld e, a - ld a, [wCurHighlightedStartMenuItem] - cp e - jr z, .skip - ld a, [wHasSaveData] - or a - jr nz, .has_data - ; New Game option is 3rd element - ; in function table, so add 2 - inc e - inc e -.has_data - - ld a, e - push af - lb de, 0, 10 - lb bc, 20, 8 - call DrawRegularTextBox - pop af - ld hl, .StartMenuDescriptionFunctionTable - call JumpToFunctionInTable -.skip - ld a, [wCurMenuItem] - ld [wCurHighlightedStartMenuItem], a - pop de - pop bc - pop hl - ret - -.StartMenuDescriptionFunctionTable - dw .CardPop - dw .ContinueFromDiary - dw .NewGame - dw .ContinueDuel - -.CardPop - lb de, 1, 12 - call InitTextPrinting - ldtx hl, WhenYouCardPopWithFriendText - call PrintTextNoDelay - ret - -.ContinueDuel - lb de, 1, 12 - call InitTextPrinting - ldtx hl, TheGameWillContinueFromThePointInTheDuelText - call PrintTextNoDelay - ret - -.NewGame - lb de, 1, 12 - call InitTextPrinting - ldtx hl, StartANewGameText - call PrintTextNoDelay - ret - -.ContinueFromDiary - ; get OW map name - ld a, [wCurOverworldMap] - add a - ld c, a - ld b, $00 - ld hl, OverworldMapNames - add hl, bc - ld a, [hli] - ld [wTxRam2 + 0], a - ld a, [hl] - ld [wTxRam2 + 1], a - - ; get medal count - ld a, [wMedalCount] - ld [wTxRam3 + 0], a - xor a - ld [wTxRam3 + 1], a - - ; print text - lb de, 1, 10 - call InitTextPrinting - ldtx hl, ContinueFromDiarySummaryText - call PrintTextNoDelay - - ld a, [wTotalNumCardsCollected] - ld d, a - ld a, [wTotalNumCardsToCollect] - ld e, a - ld bc, $90e - farcall Func_1024f - ld bc, $a10 - farcall Func_101df - ret - -; asks the player whether it's okay to delete -; the save data in order to create a new one -; if player answers "yes", delete it -DeleteSaveDataForNewGame: ; 1d289 (7:5289) -; exit if there no save data - ld a, [wHasSaveData] - or a - ret z - - call DisableLCD - farcall Func_10000 - call Func_3ca0 - farcall FlashWhiteScreen - call DoFrameIfLCDEnabled - ldtx hl, SavedDataAlreadyExistsText - call PrintScrollableText_NoTextBoxLabel - ldtx hl, OKToDeleteTheDataText - call YesOrNoMenuWithText - ret c ; quit if chose "no" - farcall InvalidateSaveData - ldtx hl, AllDataWasDeletedText - call PrintScrollableText_NoTextBoxLabel - or a - ret - -; asks the player if the game should resume -; from diary even though there is Duel save data -; returns carry if "no" was selected -AskToContinueFromDiaryWithDuelData: ; 1d2b8 (7:52b8) -; return if there's no duel save data - ld a, [wHasDuelSaveData] - or a - ret z - - call DisableLCD - farcall Func_10000 - call Func_3ca0 - farcall FlashWhiteScreen - call DoFrameIfLCDEnabled - ldtx hl, DataExistsWhenPowerWasTurnedOFFDuringDuelText - call PrintScrollableText_NoTextBoxLabel - ldtx hl, ContinueFromDiaryText - call YesOrNoMenuWithText - ret c - or a - ret - -; shows disclaimer for Card Pop! -; in case player is not playing in CGB -; return carry if disclaimer was shown -ShowCardPopCGBDisclaimer: ; 1d2dd (7:52dd) -; return if playing in CGB - ld a, [wConsole] - cp CONSOLE_CGB - ret z - - lb de, 0, 10 - lb bc, 20, 8 - call DrawRegularTextBox - lb de, 1,12 - call InitTextPrinting - ldtx hl, YouCanAccessCardPopOnlyWithGameBoyColorsText - call PrintTextNoDelay - lb bc, SYM_CURSOR_D, SYM_BOX_BOTTOM - lb de, 18, 17 - call SetCursorParametersForTextBox - call WaitForButtonAorB - scf - ret - -DrawPlayerPortraitAndPrintNewGameText: ; 1d306 (7:5306) - call DisableLCD - farcall Func_10a9b - farcall Func_10000 - call Func_3ca0 - ld hl, HandleAllSpriteAnimations - call SetDoFrameFunction - lb bc, 7, 3 - farcall $4, DrawPlayerPortrait - farcall Func_10af9 - call DoFrameIfLCDEnabled - ldtx hl, IsCrazyAboutPokemonAndPokemonCardCollectingText - call PrintScrollableText_NoTextBoxLabel - call ResetDoFrameFunction - call Func_3ca0 - ret - -PlayOpeningSequence: ; 1d335 (7:5335) - call DisableLCD - farcall Func_10a9b - farcall Func_10000 - call Func_3ca0 - ld hl, HandleAllSpriteAnimations - call SetDoFrameFunction - call LoadTitleScreenSprites - - ld a, LOW(OpeningSequence) - ld [wSequenceCmdPtr + 0], a - ld a, HIGH(OpeningSequence) - ld [wSequenceCmdPtr + 1], a - - xor a - ld [wd317], a - ld [wOpeningSequencePalsNeedUpdate], a - ld [wSequenceDelay], a - farcall FlashWhiteScreen - -.loop_cmds - call DoFrameIfLCDEnabled - call UpdateRNGSources - ldh a, [hKeysPressed] - and A_BUTTON | START - jr nz, .jump_to_title_screen - ld a, [wOpeningSequencePalsNeedUpdate] - or a - jr z, .no_pal_update - farcall Func_10d74 -.no_pal_update - call ExecuteOpeningSequenceCmd - ld a, [wSequenceDelay] - cp $ff - jr nz, .loop_cmds - jr .asm_1d39f - -.jump_to_title_screen - call AssertSongFinished - or a - jr nz, .asm_1d39f - call DisableLCD - ld a, MUSIC_TITLESCREEN - call PlaySong - lb bc, 0, 0 - ld a, SCENE_TITLE_SCREEN - call LoadScene - call OpeningSequenceEmptyFunc -.asm_1d39f - call Func_3ca0 - call .ShowPressStart - call EnableLCD - ret - -.ShowPressStart - ld a, SPRITE_PRESS_START - farcall CreateSpriteAndAnimBufferEntry - ld c, SPRITE_ANIM_COORD_X - call GetSpriteAnimBufferProperty - ld a, 48 - ld [hli], a ; x - ld a, 112 - ld [hl], a ; y - ld c, SPRITE_ANIM_190 - ld a, [wConsole] - cp CONSOLE_CGB - jr nz, .asm_1d3c5 - ld c, SPRITE_ANIM_191 -.asm_1d3c5 - ld a, c - ld bc, 60 - farcall Func_12ac9 - ret - -LoadTitleScreenSprites: ; 1d3ce (7:53ce) - xor a - ld [wd4ca], a - ld [wd4cb], a - ld a, PALETTE_30 - farcall LoadPaletteData - - ld bc, 0 - ld de, wTitleScreenSprites -.loop_load_sprites - push bc - push de - ld hl, .TitleScreenSpriteList - add hl, bc - ld a, [hl] - farcall CreateSpriteAndAnimBufferEntry - ld a, [wWhichSprite] - ld [de], a - call GetFirstSpriteAnimBufferProperty - inc hl - ld a, [hl] ; SPRITE_ANIM_ATTRIBUTES - or c - ld [hl], a - pop de - pop bc - inc de - inc c - ld a, c - cp $7 - jr c, .loop_load_sprites - ret - -.TitleScreenSpriteList - db SPRITE_GRASS - db SPRITE_FIRE - db SPRITE_WATER - db SPRITE_COLORLESS - db SPRITE_LIGHTNING - db SPRITE_PSYCHIC - db SPRITE_FIGHTING - -; TODO place in main.asm when possible -INCLUDE "engine/sequences/opening_sequence_commands.asm" -INCLUDE "data/sequences/opening_sequence.asm" - -; once every 63 frames randomly choose an orb sprite -; to animate, i.e. circle around the screen -AnimateRandomTitleScreenOrb: ; 1d614 (7:5614) - ld a, [wConsole] - cp CONSOLE_CGB - call z, .UpdateSpriteAttributes - ld a, [wd635] - and 63 - ret nz ; don't pick an orb now - -.pick_orb - ld a, $7 - call Random - ld c, a - ld b, $00 - ld hl, wTitleScreenSprites - add hl, bc - ld a, [hl] - ld [wWhichSprite], a - farcall GetSpriteAnimCounter - cp $ff - jr nz, .pick_orb - - ld c, SPRITE_ANIM_ATTRIBUTES - call GetSpriteAnimBufferProperty - ld a, [wConsole] - cp CONSOLE_CGB - jr nz, .set_coords - set SPRITE_ANIM_FLAG_UNSKIPPABLE, [hl] - -.set_coords - inc hl - ld a, 248 - ld [hli], a ; SPRITE_ANIM_COORD_X - ld a, 14 - ld [hl], a ; SPRITE_ANIM_COORD_Y - ld a, [wConsole] - cp CONSOLE_CGB - ld a, SPRITE_ANIM_215 - jr nz, .start_anim - ld a, SPRITE_ANIM_216 -.start_anim - farcall StartSpriteAnimation - ret - -.UpdateSpriteAttributes - ld c, $7 - ld de, wTitleScreenSprites -.loop_orbs - push bc - ld a, [de] - ld [wWhichSprite], a - ld c, SPRITE_ANIM_COORD_X - call GetSpriteAnimBufferProperty - ld a, [hld] - cp 152 - jr nz, .skip - res SPRITE_ANIM_FLAG_UNSKIPPABLE, [hl] -.skip - pop bc - inc de - dec c - jr nz, .loop_orbs - ret - -; unreferenced -; shows Copyright information for 300 frames -; or until Start button is pressed -Func_1d67b: ; 1d67b (7:567b) - call DisableLCD - farcall Func_10a9b - farcall Func_10000 - ld bc, $0 - ld a, SCENE_COPYRIGHT - call LoadScene - farcall Func_10af9 - ld bc, 300 -.loop_frame - push bc - call DoFrameIfLCDEnabled - call UpdateRNGSources - pop bc - ldh a, [hKeysPressed] - and START - jr nz, .exit - dec bc - ld a, b - or c - jr nz, .loop_frame -.exit - farcall Func_10ab4 - ret - -Credits_1d6ad: ; 1d6ad (7:56ad) - ld a, MUSIC_STOP - call PlaySong - call Func_1d705 - call AddAllMastersToMastersBeatenList - xor a - ld [wOWMapEvents + 1], a - ld a, MUSIC_CREDITS - call PlaySong - farcall FlashWhiteScreen - call SetCreditsSequenceCmdPtr -.asm_1d6c8 - call DoFrameIfLCDEnabled - call Func_1d765 - call ExecuteCreditsSequenceCmd - ld a, [wSequenceDelay] - cp $ff - jr nz, .asm_1d6c8 - call WaitForSongToFinish - ld a, $8 - farcall Func_12863 - ld a, MUSIC_STOP - call PlaySong - farcall Func_10ab4 - call Func_3ca4 - call Set_WD_off - call Func_1d758 - call EnableLCD - call DoFrameIfLCDEnabled - call DisableLCD - ld hl, wLCDC - set 1, [hl] - call ResetDoFrameFunction - ret - -Func_1d705: ; 1d705 (7:5705) - call DisableLCD - farcall Func_10a9b - call Func_3ca0 - farcall Func_10000 - call Func_1d7ee - ld hl, Func_3e31 - call SetDoFrameFunction - call .Func_1d720 ; can be fallthrough - ret - -.Func_1d720 - ld a, $91 - ld [wd647], a - ld [wd649], a - ld a, $01 - ld [wd648], a - ld [wd64a], a - call Func_1d765 - call Set_WD_on - call .Func_1d73a ; can bee fallthrough - ret - -.Func_1d73a - push hl - di - xor a - ld [wd657], a - ld hl, wLCDCFunctionTrampoline + 1 - ld [hl], LOW(Func_3e44) - inc hl - ld [hl], HIGH(Func_3e44) - ei - - ld hl, rSTAT - set STAT_LYC, [hl] - xor a - ldh [rLYC], a - ld hl, rIE - set INT_LCD_STAT, [hl] - pop hl - ret - -Func_1d758: ; 1d758 (7:5758) - push hl - ld hl, rSTAT - res STAT_LYC, [hl] - ld hl, rIE - res INT_LCD_STAT, [hl] - pop hl - ret - -Func_1d765: ; 1d765 (7:5765) - push hl - push bc - push de - xor a - ldh [hWY], a - - ld hl, wd659 - ld de, wd65f - ld a, [wd648] - or a - jr nz, .asm_1d785 - ld a, $a7 - ldh [hWX], a - ld [hli], a - push hl - ld hl, wLCDC - set 1, [hl] - pop hl - jr .asm_1d7e2 - -.asm_1d785 - ld a, [wd647] - or a - jr z, .asm_1d79e - dec a - ld [de], a - inc de - ld a, $a7 - ldh [hWX], a - ld [hli], a - push hl - ld hl, wLCDC - set 1, [hl] - pop hl - ld a, $07 - jr .asm_1d7a9 - -.asm_1d79e - ld a, $07 - ldh [hWX], a - push hl - ld hl, wLCDC - res 1, [hl] - pop hl -.asm_1d7a9 - ld [hli], a - ld a, [wd647] - dec a - ld c, a - ld a, [wd648] - add c - ld c, a - ld a, [wd649] - dec a - cp c - jr c, .asm_1d7d4 - jr z, .asm_1d7d4 - ld a, c - ld [de], a - inc de - push af - ld a, $a7 - ld [hli], a - pop bc - ld a, [wd64a] - or a - jr z, .asm_1d7e2 - ld a, [wd649] - dec a - ld [de], a - inc de - ld a, $07 - ld [hli], a - -.asm_1d7d4 - ld a, [wd649] - dec a - ld c, a - ld a, [wd64a] - add c - ld [de], a - inc de - ld a, $a7 - ld [hli], a -.asm_1d7e2 - ld a, $ff - ld [de], a - ld a, $01 - ld [wd665], a - pop de - pop bc - pop hl - ret - -Func_1d7ee: ; 1d7ee (7:57ee) - xor a - lb de, 0, 32 - lb bc, 20, 18 - lb hl, 0, 0 - call FillRectangle - ret diff --git a/src/engine/credits.asm b/src/engine/credits.asm new file mode 100644 index 0000000..d2da5d8 --- /dev/null +++ b/src/engine/credits.asm @@ -0,0 +1,189 @@ +PlayCreditsSequence: + ld a, MUSIC_STOP + call PlaySong + call Func_1d705 + call AddAllMastersToMastersBeatenList + xor a + ld [wOWMapEvents + 1], a + ld a, MUSIC_CREDITS + call PlaySong + farcall FlashWhiteScreen + call SetCreditsSequenceCmdPtr +.asm_1d6c8 + call DoFrameIfLCDEnabled + call Func_1d765 + call ExecuteCreditsSequenceCmd + ld a, [wSequenceDelay] + cp $ff + jr nz, .asm_1d6c8 + call WaitForSongToFinish + ld a, $8 + farcall Func_12863 + ld a, MUSIC_STOP + call PlaySong + farcall Func_10ab4 + call Func_3ca4 + call SetWDOff + call Func_1d758 + call EnableLCD + call DoFrameIfLCDEnabled + call DisableLCD + ld hl, wLCDC + set 1, [hl] + call ResetDoFrameFunction + ret + +Func_1d705: + call DisableLCD + farcall Func_10a9b + call Func_3ca0 + farcall Func_10000 + call Func_1d7ee + ld hl, Func_3e31 + call SetDoFrameFunction + call .Func_1d720 ; can be fallthrough + ret + +.Func_1d720 + ld a, $91 + ld [wd647], a + ld [wd649], a + ld a, $01 + ld [wd648], a + ld [wd64a], a + call Func_1d765 + call Set_WD_on + call .Func_1d73a ; can be fallthrough + ret + +.Func_1d73a + push hl + di + xor a + ld [wd657], a + ld hl, wLCDCFunctionTrampoline + 1 + ld [hl], LOW(Func_3e44) + inc hl + ld [hl], HIGH(Func_3e44) + ei + + ld hl, rSTAT + set STAT_LYC, [hl] + xor a + ldh [rLYC], a + ld hl, rIE + set INT_LCD_STAT, [hl] + pop hl + ret + +Func_1d758: + push hl + ld hl, rSTAT + res STAT_LYC, [hl] + ld hl, rIE + res INT_LCD_STAT, [hl] + pop hl + ret + +Func_1d765: + push hl + push bc + push de + xor a + ldh [hWY], a + + ld hl, wd659 + ld de, wd65f + ld a, [wd648] + or a + jr nz, .asm_1d785 + ld a, $a7 + ldh [hWX], a + ld [hli], a + push hl + ld hl, wLCDC + set 1, [hl] + pop hl + jr .asm_1d7e2 + +.asm_1d785 + ld a, [wd647] + or a + jr z, .asm_1d79e + dec a + ld [de], a + inc de + ld a, $a7 + ldh [hWX], a + ld [hli], a + push hl + ld hl, wLCDC + set 1, [hl] + pop hl + ld a, $07 + jr .asm_1d7a9 + +.asm_1d79e + ld a, $07 + ldh [hWX], a + push hl + ld hl, wLCDC + res 1, [hl] + pop hl +.asm_1d7a9 + ld [hli], a + ld a, [wd647] + dec a + ld c, a + ld a, [wd648] + add c + ld c, a + ld a, [wd649] + dec a + cp c + jr c, .asm_1d7d4 + jr z, .asm_1d7d4 + ld a, c + ld [de], a + inc de + push af + ld a, $a7 + ld [hli], a + pop bc + ld a, [wd64a] + or a + jr z, .asm_1d7e2 + ld a, [wd649] + dec a + ld [de], a + inc de + ld a, $07 + ld [hli], a + +.asm_1d7d4 + ld a, [wd649] + dec a + ld c, a + ld a, [wd64a] + add c + ld [de], a + inc de + ld a, $a7 + ld [hli], a +.asm_1d7e2 + ld a, $ff + ld [de], a + ld a, $01 + ld [wd665], a + pop de + pop bc + pop hl + ret + +Func_1d7ee: + xor a + lb de, 0, 32 + lb bc, 20, 18 + lb hl, 0, 0 + call FillRectangle + ret diff --git a/src/engine/duel/animations.asm b/src/engine/duel/animations.asm deleted file mode 100644 index 46b1ea1..0000000 --- a/src/engine/duel/animations.asm +++ /dev/null @@ -1,354 +0,0 @@ -; reads the animation commands from PointerTable_AttackAnimation -; of attack in wLoadedAttackAnimation and plays them -PlayAttackAnimationCommands: - ld a, [wLoadedAttackAnimation] - or a - ret z - - ld l, a - ld h, 0 - add hl, hl - ld de, PointerTable_AttackAnimation - add hl, de - ld e, [hl] - inc hl - ld d, [hl] - - push de - ld hl, wce7e - ld a, [hl] - or a - jr nz, .read_command - ld [hl], $01 - call Func_3b21 - pop de - - push de - ld a, DUEL_ANIM_SCREEN_MAIN_SCENE - ld [wDuelAnimationScreen], a - ld a, $01 - ld [wd4b3], a - xor a - ld [wDuelAnimLocationParam], a - ld a, [de] - cp $04 - jr z, .read_command - ld a, DUEL_ANIM_150 - call PlayDuelAnimation -.read_command - pop de - ; fallthrough - -PlayAttackAnimationCommands_NextCommand: - ld a, [de] - inc de - ld hl, AnimationCommandPointerTable - jp JumpToFunctionInTable - -AnimationCommand_AnimEnd: - ret - -AnimationCommand_AnimPlayer: - ldh a, [hWhoseTurn] - ld [wDuelAnimDuelistSide], a - ld a, [wDuelType] - cp $00 - jr nz, AnimationCommand_AnimNormal - ld a, PLAYER_TURN - ld [wDuelAnimDuelistSide], a - jr AnimationCommand_AnimNormal - -AnimationCommand_AnimOpponent: - call SwapTurn - ldh a, [hWhoseTurn] - ld [wDuelAnimDuelistSide], a - call SwapTurn - ld a, [wDuelType] - cp $00 - jr nz, AnimationCommand_AnimNormal - ld a, OPPONENT_TURN - ld [wDuelAnimDuelistSide], a - jr AnimationCommand_AnimNormal - -AnimationCommand_AnimUnknown2: - ld a, [wce82] - and $7f - ld [wDuelAnimLocationParam], a - jr AnimationCommand_AnimNormal - -AnimationCommand_AnimEnd2: - ret - -AnimationCommand_AnimNormal: - ld a, [de] - inc de - cp DUEL_ANIM_SHOW_DAMAGE - jr z, .show_damage - cp DUEL_ANIM_SHAKE1 - jr z, .shake_1 - cp DUEL_ANIM_SHAKE2 - jr z, .shake_2 - cp DUEL_ANIM_SHAKE3 - jr z, .shake_3 - -.play_anim - call PlayDuelAnimation - jr PlayAttackAnimationCommands_NextCommand - -.show_damage - ld a, DUEL_ANIM_PRINT_DAMAGE - call PlayDuelAnimation - ld a, [wce81] - ld [wd4b3], a - - push de - ld hl, wce7f - ld de, wDuelAnimDamage - ld a, [hli] - ld [de], a - inc de - ld a, [hli] - ld [de], a - pop de - - ld a, $8c - call PlayDuelAnimation - ld a, [wDuelDisplayedScreen] - cp DUEL_MAIN_SCENE - jr nz, .skip_update_hud - ld a, DUEL_ANIM_UPDATE_HUD - call PlayDuelAnimation -.skip_update_hud - jp PlayAttackAnimationCommands_NextCommand - -; screen shake happens differently -; depending on whose turn it is -.shake_1 - ld c, DUEL_ANIM_SMALL_SHAKE_X - ld b, DUEL_ANIM_SMALL_SHAKE_Y - jr .check_duelist - -.shake_2 - ld c, DUEL_ANIM_BIG_SHAKE_X - ld b, DUEL_ANIM_BIG_SHAKE_Y - jr .check_duelist - -.shake_3 - ld c, DUEL_ANIM_SMALL_SHAKE_Y - ld b, DUEL_ANIM_SMALL_SHAKE_X - -.check_duelist - ldh a, [hWhoseTurn] - cp PLAYER_TURN - ld a, c - jr z, .play_anim - ld a, [wDuelType] - cp $00 - ld a, c - jr z, .play_anim - ld a, b - jr .play_anim - -AnimationCommand_AnimUnknown: - ld a, [de] - inc de - ld [wd4b3], a - ld a, [wce82] - ld [wDuelAnimLocationParam], a - call SetDuelAnimationScreen - ld a, DUEL_ANIM_150 - call PlayDuelAnimation - jp PlayAttackAnimationCommands_NextCommand - -AnimationCommandPointerTable: - dw AnimationCommand_AnimEnd ; anim_end - dw AnimationCommand_AnimNormal ; anim_normal - dw AnimationCommand_AnimPlayer ; anim_player - dw AnimationCommand_AnimOpponent ; anim_opponent - dw AnimationCommand_AnimUnknown ; anim_unknown - dw AnimationCommand_AnimUnknown2 ; anim_unknown2 - dw AnimationCommand_AnimEnd2 ; anim_end2 (unused) - -; sets wDuelAnimationScreen according to wd4b3 -; if wd4b3 == $01, set it to Main Scene -; if wd4b3 == $04, st it to Play Area scene -SetDuelAnimationScreen: - ld a, [wd4b3] - cp $04 - jr z, .set_play_area_screen - cp $01 - ret nz - ld a, DUEL_ANIM_SCREEN_MAIN_SCENE - ld [wDuelAnimationScreen], a - ret - -.set_play_area_screen - ld a, [wDuelAnimLocationParam] - ld l, a - ld a, [wWhoseTurn] - ld h, a - cp PLAYER_TURN - jr z, .player - -; opponent - ld a, [wDuelType] - cp $00 - jr z, .asm_50c6 - -; link duel or vs. AI - bit 7, l - jr z, .asm_50e2 - jr .asm_50d2 - -.asm_50c6 - bit 7, l - jr z, .asm_50da - jr .asm_50ea - -.player - bit 7, l - jr z, .asm_50d2 - jr .asm_50e2 - -.asm_50d2 - ld l, UNKNOWN_SCREEN_4 - ld h, PLAYER_TURN - ld a, DUEL_ANIM_SCREEN_PLAYER_PLAY_AREA - jr .ok -.asm_50da - ld l, UNKNOWN_SCREEN_4 - ld h, OPPONENT_TURN - ld a, DUEL_ANIM_SCREEN_PLAYER_PLAY_AREA - jr .ok -.asm_50e2 - ld l, UNKNOWN_SCREEN_5 - ld h, OPPONENT_TURN - ld a, DUEL_ANIM_SCREEN_OPP_PLAY_AREA - jr .ok -.asm_50ea - ld l, UNKNOWN_SCREEN_5 - ld h, PLAYER_TURN - ld a, DUEL_ANIM_SCREEN_OPP_PLAY_AREA - -.ok - ld [wDuelAnimationScreen], a - ret - -Func_190f4: - ld a, [wd4b3] - cp $04 - jr z, Func_1910f - ; fallthrough - -Func_190fb: - cp $01 - jr nz, .done - ld a, DUEL_ANIM_SCREEN_MAIN_SCENE - ld [wDuelAnimationScreen], a - ld a, [wDuelDisplayedScreen] - cp $01 - jr z, .done - bank1call DrawDuelMainScene -.done - ret - -Func_1910f: - call SetDuelAnimationScreen - ld a, [wDuelDisplayedScreen] - cp l - jr z, .skip_change_screen - ld a, l - push af - ld l, PLAYER_TURN - ld a, [wDuelType] - cp $00 - jr nz, .asm_5127 - ld a, [wWhoseTurn] - ld l, a -.asm_5127 - call DrawYourOrOppPlayAreaScreen_Bank0 - pop af - ld [wDuelDisplayedScreen], a -.skip_change_screen - call DrawWideTextBox - ret - -; prints text related to the damage received -; by card stored in wTempNonTurnDuelistCardID -; takes into account type effectiveness -PrintDamageText: - push hl - push bc - push de - ld a, [wLoadedAttackAnimation] - cp ATK_ANIM_HEAL - jr z, .skip - cp ATK_ANIM_HEALING_WIND_PLAY_AREA - jr z, .skip - - ld a, [wTempNonTurnDuelistCardID] - ld e, a - ld d, $00 - call LoadCardDataToBuffer1_FromCardID - ld a, 18 - call CopyCardNameAndLevel - ld [hl], TX_END - ld hl, wTxRam2 - xor a - ld [hli], a - ld [hl], a - ld hl, wce7f - ld a, [hli] - ld h, [hl] - ld l, a - call GetDamageText - ld a, l - or h - call nz, DrawWideTextBox_PrintText -.skip - pop de - pop bc - pop hl - ret - -; returns in hl the text id associated with -; the damage in hl and its effectiveness -GetDamageText: - ld a, l - or h - jr z, .no_damage - call LoadTxRam3 - ld a, [wce81] - ldtx hl, AttackDamageText - and (1 << RESISTANCE) | (1 << WEAKNESS) - ret z ; not weak or resistant - ldtx hl, WeaknessMoreDamage2Text - cp (1 << RESISTANCE) | (1 << WEAKNESS) - ret z ; weak and resistant - and (1 << WEAKNESS) - ldtx hl, WeaknessMoreDamageText - ret nz ; weak - ldtx hl, ResistanceLessDamageText - ret ; resistant - -.no_damage - call CheckNoDamageOrEffect - ret c - ldtx hl, NoDamageText - ld a, [wce81] - and (1 << RESISTANCE) - ret z ; not resistant - ldtx hl, ResistanceNoDamageText - ret ; resistant - -UpdateMainSceneHUD: - ld a, [wDuelDisplayedScreen] - cp DUEL_MAIN_SCENE - ret nz - bank1call DrawDuelHUDs - ret - -Func_191a3: - ret - -INCLUDE "data/duel/animations/attack_animations.asm" diff --git a/src/engine/duel/animations/commands.asm b/src/engine/duel/animations/commands.asm new file mode 100644 index 0000000..46b1ea1 --- /dev/null +++ b/src/engine/duel/animations/commands.asm @@ -0,0 +1,354 @@ +; reads the animation commands from PointerTable_AttackAnimation +; of attack in wLoadedAttackAnimation and plays them +PlayAttackAnimationCommands: + ld a, [wLoadedAttackAnimation] + or a + ret z + + ld l, a + ld h, 0 + add hl, hl + ld de, PointerTable_AttackAnimation + add hl, de + ld e, [hl] + inc hl + ld d, [hl] + + push de + ld hl, wce7e + ld a, [hl] + or a + jr nz, .read_command + ld [hl], $01 + call Func_3b21 + pop de + + push de + ld a, DUEL_ANIM_SCREEN_MAIN_SCENE + ld [wDuelAnimationScreen], a + ld a, $01 + ld [wd4b3], a + xor a + ld [wDuelAnimLocationParam], a + ld a, [de] + cp $04 + jr z, .read_command + ld a, DUEL_ANIM_150 + call PlayDuelAnimation +.read_command + pop de + ; fallthrough + +PlayAttackAnimationCommands_NextCommand: + ld a, [de] + inc de + ld hl, AnimationCommandPointerTable + jp JumpToFunctionInTable + +AnimationCommand_AnimEnd: + ret + +AnimationCommand_AnimPlayer: + ldh a, [hWhoseTurn] + ld [wDuelAnimDuelistSide], a + ld a, [wDuelType] + cp $00 + jr nz, AnimationCommand_AnimNormal + ld a, PLAYER_TURN + ld [wDuelAnimDuelistSide], a + jr AnimationCommand_AnimNormal + +AnimationCommand_AnimOpponent: + call SwapTurn + ldh a, [hWhoseTurn] + ld [wDuelAnimDuelistSide], a + call SwapTurn + ld a, [wDuelType] + cp $00 + jr nz, AnimationCommand_AnimNormal + ld a, OPPONENT_TURN + ld [wDuelAnimDuelistSide], a + jr AnimationCommand_AnimNormal + +AnimationCommand_AnimUnknown2: + ld a, [wce82] + and $7f + ld [wDuelAnimLocationParam], a + jr AnimationCommand_AnimNormal + +AnimationCommand_AnimEnd2: + ret + +AnimationCommand_AnimNormal: + ld a, [de] + inc de + cp DUEL_ANIM_SHOW_DAMAGE + jr z, .show_damage + cp DUEL_ANIM_SHAKE1 + jr z, .shake_1 + cp DUEL_ANIM_SHAKE2 + jr z, .shake_2 + cp DUEL_ANIM_SHAKE3 + jr z, .shake_3 + +.play_anim + call PlayDuelAnimation + jr PlayAttackAnimationCommands_NextCommand + +.show_damage + ld a, DUEL_ANIM_PRINT_DAMAGE + call PlayDuelAnimation + ld a, [wce81] + ld [wd4b3], a + + push de + ld hl, wce7f + ld de, wDuelAnimDamage + ld a, [hli] + ld [de], a + inc de + ld a, [hli] + ld [de], a + pop de + + ld a, $8c + call PlayDuelAnimation + ld a, [wDuelDisplayedScreen] + cp DUEL_MAIN_SCENE + jr nz, .skip_update_hud + ld a, DUEL_ANIM_UPDATE_HUD + call PlayDuelAnimation +.skip_update_hud + jp PlayAttackAnimationCommands_NextCommand + +; screen shake happens differently +; depending on whose turn it is +.shake_1 + ld c, DUEL_ANIM_SMALL_SHAKE_X + ld b, DUEL_ANIM_SMALL_SHAKE_Y + jr .check_duelist + +.shake_2 + ld c, DUEL_ANIM_BIG_SHAKE_X + ld b, DUEL_ANIM_BIG_SHAKE_Y + jr .check_duelist + +.shake_3 + ld c, DUEL_ANIM_SMALL_SHAKE_Y + ld b, DUEL_ANIM_SMALL_SHAKE_X + +.check_duelist + ldh a, [hWhoseTurn] + cp PLAYER_TURN + ld a, c + jr z, .play_anim + ld a, [wDuelType] + cp $00 + ld a, c + jr z, .play_anim + ld a, b + jr .play_anim + +AnimationCommand_AnimUnknown: + ld a, [de] + inc de + ld [wd4b3], a + ld a, [wce82] + ld [wDuelAnimLocationParam], a + call SetDuelAnimationScreen + ld a, DUEL_ANIM_150 + call PlayDuelAnimation + jp PlayAttackAnimationCommands_NextCommand + +AnimationCommandPointerTable: + dw AnimationCommand_AnimEnd ; anim_end + dw AnimationCommand_AnimNormal ; anim_normal + dw AnimationCommand_AnimPlayer ; anim_player + dw AnimationCommand_AnimOpponent ; anim_opponent + dw AnimationCommand_AnimUnknown ; anim_unknown + dw AnimationCommand_AnimUnknown2 ; anim_unknown2 + dw AnimationCommand_AnimEnd2 ; anim_end2 (unused) + +; sets wDuelAnimationScreen according to wd4b3 +; if wd4b3 == $01, set it to Main Scene +; if wd4b3 == $04, st it to Play Area scene +SetDuelAnimationScreen: + ld a, [wd4b3] + cp $04 + jr z, .set_play_area_screen + cp $01 + ret nz + ld a, DUEL_ANIM_SCREEN_MAIN_SCENE + ld [wDuelAnimationScreen], a + ret + +.set_play_area_screen + ld a, [wDuelAnimLocationParam] + ld l, a + ld a, [wWhoseTurn] + ld h, a + cp PLAYER_TURN + jr z, .player + +; opponent + ld a, [wDuelType] + cp $00 + jr z, .asm_50c6 + +; link duel or vs. AI + bit 7, l + jr z, .asm_50e2 + jr .asm_50d2 + +.asm_50c6 + bit 7, l + jr z, .asm_50da + jr .asm_50ea + +.player + bit 7, l + jr z, .asm_50d2 + jr .asm_50e2 + +.asm_50d2 + ld l, UNKNOWN_SCREEN_4 + ld h, PLAYER_TURN + ld a, DUEL_ANIM_SCREEN_PLAYER_PLAY_AREA + jr .ok +.asm_50da + ld l, UNKNOWN_SCREEN_4 + ld h, OPPONENT_TURN + ld a, DUEL_ANIM_SCREEN_PLAYER_PLAY_AREA + jr .ok +.asm_50e2 + ld l, UNKNOWN_SCREEN_5 + ld h, OPPONENT_TURN + ld a, DUEL_ANIM_SCREEN_OPP_PLAY_AREA + jr .ok +.asm_50ea + ld l, UNKNOWN_SCREEN_5 + ld h, PLAYER_TURN + ld a, DUEL_ANIM_SCREEN_OPP_PLAY_AREA + +.ok + ld [wDuelAnimationScreen], a + ret + +Func_190f4: + ld a, [wd4b3] + cp $04 + jr z, Func_1910f + ; fallthrough + +Func_190fb: + cp $01 + jr nz, .done + ld a, DUEL_ANIM_SCREEN_MAIN_SCENE + ld [wDuelAnimationScreen], a + ld a, [wDuelDisplayedScreen] + cp $01 + jr z, .done + bank1call DrawDuelMainScene +.done + ret + +Func_1910f: + call SetDuelAnimationScreen + ld a, [wDuelDisplayedScreen] + cp l + jr z, .skip_change_screen + ld a, l + push af + ld l, PLAYER_TURN + ld a, [wDuelType] + cp $00 + jr nz, .asm_5127 + ld a, [wWhoseTurn] + ld l, a +.asm_5127 + call DrawYourOrOppPlayAreaScreen_Bank0 + pop af + ld [wDuelDisplayedScreen], a +.skip_change_screen + call DrawWideTextBox + ret + +; prints text related to the damage received +; by card stored in wTempNonTurnDuelistCardID +; takes into account type effectiveness +PrintDamageText: + push hl + push bc + push de + ld a, [wLoadedAttackAnimation] + cp ATK_ANIM_HEAL + jr z, .skip + cp ATK_ANIM_HEALING_WIND_PLAY_AREA + jr z, .skip + + ld a, [wTempNonTurnDuelistCardID] + ld e, a + ld d, $00 + call LoadCardDataToBuffer1_FromCardID + ld a, 18 + call CopyCardNameAndLevel + ld [hl], TX_END + ld hl, wTxRam2 + xor a + ld [hli], a + ld [hl], a + ld hl, wce7f + ld a, [hli] + ld h, [hl] + ld l, a + call GetDamageText + ld a, l + or h + call nz, DrawWideTextBox_PrintText +.skip + pop de + pop bc + pop hl + ret + +; returns in hl the text id associated with +; the damage in hl and its effectiveness +GetDamageText: + ld a, l + or h + jr z, .no_damage + call LoadTxRam3 + ld a, [wce81] + ldtx hl, AttackDamageText + and (1 << RESISTANCE) | (1 << WEAKNESS) + ret z ; not weak or resistant + ldtx hl, WeaknessMoreDamage2Text + cp (1 << RESISTANCE) | (1 << WEAKNESS) + ret z ; weak and resistant + and (1 << WEAKNESS) + ldtx hl, WeaknessMoreDamageText + ret nz ; weak + ldtx hl, ResistanceLessDamageText + ret ; resistant + +.no_damage + call CheckNoDamageOrEffect + ret c + ldtx hl, NoDamageText + ld a, [wce81] + and (1 << RESISTANCE) + ret z ; not resistant + ldtx hl, ResistanceNoDamageText + ret ; resistant + +UpdateMainSceneHUD: + ld a, [wDuelDisplayedScreen] + cp DUEL_MAIN_SCENE + ret nz + bank1call DrawDuelHUDs + ret + +Func_191a3: + ret + +INCLUDE "data/duel/animations/attack_animations.asm" diff --git a/src/engine/duel/animations/core.asm b/src/engine/duel/animations/core.asm new file mode 100644 index 0000000..5da9a0a --- /dev/null +++ b/src/engine/duel/animations/core.asm @@ -0,0 +1,661 @@ +Func_1c8bc: + push hl + push bc + call Set_OBJ_8x8 + ld a, LOW(Func_3ba2) + ld [wDoFrameFunction], a + ld a, HIGH(Func_3ba2) + ld [wDoFrameFunction + 1], a + ld a, $ff + ld hl, wAnimationQueue + ld c, ANIMATION_QUEUE_LENGTH +.fill_queue + ld [hli], a + dec c + jr nz, .fill_queue + ld [wd42a], a + ld [wd4c0], a + xor a + ld [wDuelAnimBufferCurPos], a + ld [wDuelAnimBufferSize], a + ld [wd4b3], a + call DefaultScreenAnimationUpdate + call Func_3ca0 + pop bc + pop hl + ret + +PlayLoadedDuelAnimation: + ld a, [wDoFrameFunction + 0] + cp LOW(Func_3ba2) + jr nz, .error + ld a, [wDoFrameFunction + 1] + cp HIGH(Func_3ba2) + jr z, .okay +.error + debug_nop + ret + +.okay + ld a, [wTempAnimation] + ld [wd4bf], a + cp DUEL_SPECIAL_ANIMS + jp nc, Func_1cb5e + + push hl + push bc + push de + call GetAnimationData +; hl: pointer + + ld a, [wAnimationsDisabled] + or a + jr z, .check_to_play_sfx + ; animations are disabled + push hl + ld bc, ANIM_SPRITE_ANIM_FLAGS + add hl, bc + ld a, [hl] + ; if flag is set, play animation anyway + and (1 << SPRITE_ANIM_FLAG_UNSKIPPABLE) + pop hl + jr z, .return + +.check_to_play_sfx + push hl + ld bc, ANIM_SOUND_FX_ID + add hl, bc + ld a, [hl] + pop hl + or a + jr z, .calc_addr + call PlaySFX + +.calc_addr +; this data field is always $00, +; so this calculation is unnecessary +; seems like there was supposed to be +; more than 1 function to handle animation + push hl + ld bc, ANIM_HANDLER_FUNCTION + add hl, bc + ld a, [hl] + rlca + add LOW(.address) ; $48 + ld l, a ; LO + ld a, HIGH(.address) ; $49 + adc 0 + ld h, a ; HI +; hl: pointer + ld a, [hli] + ld b, [hl] + ld c, a + pop hl + + call CallBC +.return + pop de + pop bc + pop hl + ret + +.address + dw .handler_func + +.handler_func ; 1c94a (7:494a) +; if any of ANIM_SPRITE_ID, ANIM_PALETTE_ID and ANIM_SPRITE_ANIM_ID +; are 0, then return + ld e, l + ld d, h + ld c, ANIM_SPRITE_ANIM_ID + 1 +.loop + ld a, [de] + or a + jr z, .return_with_carry + inc de + dec c + jr nz, .loop + + ld a, [hli] ; ANIM_SPRITE_ID + farcall CreateSpriteAndAnimBufferEntry + ld a, [wWhichSprite] + ld [wAnimationQueue], a ; push an animation to the queue + + xor a + ld [wVRAMTileOffset], a + ld [wd4cb], a + + ld a, [hli] ; ANIM_PALETTE_ID + farcall LoadPaletteData + ld a, [hli] ; ANIM_SPRITE_ANIM_ID + + push af + ld a, [hli] ; ANIM_SPRITE_ANIM_FLAGS + ld [wAnimFlags], a + call LoadAnimCoordsAndFlags + pop af + + farcall StartNewSpriteAnimation + or a + jr .done + +.return_with_carry + scf +.done + ret + +; loads the correct coordinates/flags for +; sprite animation in wAnimationQueue +LoadAnimCoordsAndFlags: + push hl + push bc + ld a, [wAnimationQueue] + ld c, SPRITE_ANIM_ATTRIBUTES + call GetSpriteAnimBufferProperty_SpriteInA + call GetAnimCoordsAndFlags + + push af + and (1 << SPRITE_ANIM_FLAG_6) | (1 << SPRITE_ANIM_FLAG_5) + or [hl] + ld [hli], a + ld a, b + ld [hli], a ; SPRITE_ANIM_COORD_X + ld [hl], c ; SPRITE_ANIM_COORD_Y + pop af + + ld bc, SPRITE_ANIM_FLAGS - SPRITE_ANIM_COORD_Y + add hl, bc + ld c, a ; useless + and (1 << SPRITE_ANIM_FLAG_Y_SUBTRACT) | (1 << SPRITE_ANIM_FLAG_X_SUBTRACT) + or [hl] + ld [hl], a + pop bc + pop hl + ret + +; outputs x and y coordinates for the sprite animation +; taking into account who the turn duelist is. +; also returns in a the allowed animation flags of +; the configuration that is selected. +; output: +; a = anim flags +; b = x coordinate +; c = y coordinate +GetAnimCoordsAndFlags: + push hl + ld c, 0 + ld a, [wAnimFlags] + and (1 << SPRITE_ANIM_FLAG_SPEED) + jr nz, .calc_addr + + ld a, [wDuelAnimationScreen] + add a ; 2 * [wDuelAnimationScreen] + ld c, a + add a ; 4 * [wDuelAnimationScreen] + add c ; 6 * [wDuelAnimationScreen] + add a ; 12 * [wDuelAnimationScreen] + ld c, a + + ld a, [wDuelAnimDuelistSide] + cp PLAYER_TURN + jr z, .player_side +; opponent side + ld a, 6 + add c + ld c, a +.player_side + ld a, [wDuelAnimLocationParam] + add c ; a = [wDuelAnimLocationParam] + c + ld c, a + ld b, 0 + ld hl, AnimationCoordinatesIndex + add hl, bc + ld c, [hl] + +.calc_addr + ld a, c + add a ; a = c * 2 + add c ; a = c * 3 + ld c, a + ld b, 0 + ld hl, AnimationCoordinates + add hl, bc + ld b, [hl] ; x coord + inc hl + ld c, [hl] ; y coord + inc hl + ld a, [wAnimFlags] + and [hl] ; flags + pop hl + ret + +AnimationCoordinatesIndex: +; animations in the Duel Main Scene + db $01, $01, $01, $01, $01, $01 ; player + db $02, $02, $02, $02, $02, $02 ; opponent + +; animations in the Player's Play Area, for each Play Area Pokemon + db $03, $04, $05, $06, $07, $08 ; player + db $03, $04, $05, $06, $07, $08 ; opponent + +; animations in the Opponent's Play Area, for each Play Area Pokemon + db $09, $0a, $0b, $0c, $0d, $0e ; player + db $09, $0a, $0b, $0c, $0d, $0e ; opponent + +anim_coords: MACRO + db \1 + db \2 + db \3 +ENDM + +AnimationCoordinates: +; x coord, y coord, animation flags + anim_coords 88, 88, (1 << SPRITE_ANIM_FLAG_3) + +; animations in the Duel Main Scene + anim_coords 40, 80, $00 + anim_coords 136, 48, (1 << SPRITE_ANIM_FLAG_6) | (1 << SPRITE_ANIM_FLAG_5) | (1 << SPRITE_ANIM_FLAG_Y_SUBTRACT) | (1 << SPRITE_ANIM_FLAG_X_SUBTRACT) + +; animations in the Player's Play Area, for each Play Area Pokemon + anim_coords 88, 72, $00 + anim_coords 24, 96, $00 + anim_coords 56, 96, $00 + anim_coords 88, 96, $00 + anim_coords 120, 96, $00 + anim_coords 152, 96, $00 + +; animations in the Opponent's Play Area, for each Play Area Pokemon + anim_coords 88, 80, $00 + anim_coords 152, 40, $00 + anim_coords 120, 40, $00 + anim_coords 88, 40, $00 + anim_coords 56, 40, $00 + anim_coords 24, 40, $00 + +; appends to end of wDuelAnimBuffer +; the current duel animation +LoadDuelAnimationToBuffer: + push hl + push bc + ld a, [wDuelAnimBufferCurPos] + ld b, a + ld hl, wDuelAnimBufferSize + ld a, [hl] + ld c, a + add DUEL_ANIM_STRUCT_SIZE + and %01111111 + cp b + jp z, .skip + ld [hl], a + + ld b, $00 + ld hl, wDuelAnimBuffer + add hl, bc + ld a, [wTempAnimation] + ld [hli], a + ld a, [wDuelAnimationScreen] + ld [hli], a + ld a, [wDuelAnimDuelistSide] + ld [hli], a + ld a, [wDuelAnimLocationParam] + ld [hli], a + ld a, [wDuelAnimDamage] + ld [hli], a + ld a, [wDuelAnimDamage + 1] + ld [hli], a + ld a, [wd4b3] + ld [hli], a + ld a, [wDuelAnimReturnBank] + ld [hl], a + +.skip + pop bc + pop hl + ret + +; loads the animations from wDuelAnimBuffer +; in ascending order, starting at wDuelAnimBufferCurPos +PlayBufferedDuelAnimations: + push hl + push bc +.next_duel_anim + ld a, [wDuelAnimBufferSize] + ld b, a + ld a, [wDuelAnimBufferCurPos] + cp b + jr z, .skip + + ld c, a + add DUEL_ANIM_STRUCT_SIZE + and %01111111 + ld [wDuelAnimBufferCurPos], a + + ld b, $00 + ld hl, wDuelAnimBuffer + add hl, bc + ld a, [hli] + ld [wTempAnimation], a + ld a, [hli] + ld [wDuelAnimationScreen], a + ld a, [hli] + ld [wDuelAnimDuelistSide], a + ld a, [hli] + ld [wDuelAnimLocationParam], a + ld a, [hli] + ld [wDuelAnimDamage], a + ld a, [hli] + ld [wDuelAnimDamage + 1], a + ld a, [hli] + ld [wd4b3], a + ld a, [hl] + ld [wDuelAnimReturnBank], a + + call PlayLoadedDuelAnimation + call CheckAnyAnimationPlaying + jr nc, .next_duel_anim + +.skip + pop bc + pop hl + ret + +; gets data from Animations for anim ID in a +; outputs the pointer to the data in hl +GetAnimationData: + push bc + ld a, [wTempAnimation] + ld l, a + ld h, 0 + add hl, hl ; hl = anim * 2 + ld b, h + ld c, l + add hl, hl ; hl = anim * 4 + add hl, bc ; hl = anim * 6 + ld bc, Animations + add hl, bc + pop bc + ret + +Func_1cac5: + ld a, [wd42a] + cp $ff + jr nz, .asm_1cb03 + + ld a, [wd4c0] + or a + jr z, .asm_1cafb + cp $80 + jr z, .asm_1cb11 + ld hl, wAnimationQueue + ld c, ANIMATION_QUEUE_LENGTH +.loop_queue + push af + push bc + ld a, [hl] + cp $ff + jr z, .next + ld [wWhichSprite], a + farcall GetSpriteAnimCounter + cp $ff + jr nz, .next + farcall DisableCurSpriteAnim + ld a, $ff + ld [hl], a + +.next + pop bc + pop af + and [hl] + inc hl + dec c + jr nz, .loop_queue + +.asm_1cafb + cp $ff + jr nz, .skip_play_anims + call PlayBufferedDuelAnimations +.skip_play_anims + ret + +.asm_1cb03 + ld hl, wScreenAnimUpdatePtr + ld a, [hli] + ld h, [hl] + ld l, a + call CallHL2 + ld a, [wd42a] + jr .asm_1cafb + +.asm_1cb11 + ld a, $ff + ld [wd4c0], a + jr .asm_1cafb + +Func_1cb18: + push hl + push bc + push de + + ; if Func_3ba2 is not set as + ; wDoFrameFunction, quit and set carry + ld a, [wDoFrameFunction] + cp LOW(Func_3ba2) + jr nz, .carry + ld a, [wDoFrameFunction + 1] + cp HIGH(Func_3ba2) + jr nz, .carry + + ld a, $ff + ld [wd4c0], a + ld a, [wd42a] + cp $ff + call nz, DoScreenAnimationUpdate + +; clear all queued animations +; and disable their sprite anims + ld hl, wAnimationQueue + ld c, ANIMATION_QUEUE_LENGTH +.loop_queue + push bc + ld a, [hl] + cp $ff + jr z, .next_queued + ld [wWhichSprite], a + farcall DisableCurSpriteAnim + ld a, $ff + ld [hl], a +.next_queued + pop bc + inc hl + dec c + jr nz, .loop_queue + + xor a + ld [wDuelAnimBufferCurPos], a + ld [wDuelAnimBufferSize], a +.done + pop de + pop bc + pop hl + ret +.carry + scf + jr .done + +Func_1cb5e: + cp $96 + jp nc, Func_1ce03 + cp $8c + jp nz, InitScreenAnimation + jr .asm_1cb6a ; redundant +.asm_1cb6a + ld a, [wDuelAnimDamage + 1] + cp $03 + jr nz, .asm_1cb76 + ld a, [wDuelAnimDamage] + cp $e8 +.asm_1cb76 + ret nc + + xor a + ld [wd4b8], a + ld [wVRAMTileOffset], a + ld [wd4cb], a + + ld a, PALETTE_37 + farcall LoadPaletteData + call Func_1cba6 + + ld hl, wd4b3 + bit 0, [hl] + call nz, Func_1cc3e + + ld a, $12 + ld [wd4b8], a + bit 1, [hl] + call nz, Func_1cc4e + + bit 2, [hl] + call nz, Func_1cc66 + + xor a + ld [wd4b3], a + ret + +Func_1cba6: + call Func_1cc03 + xor a + ld [wd4b7], a + + ld hl, wd4b4 + ld de, wAnimationQueue + 1 +.asm_1cbb3 + push hl + push de + ld a, [hl] + or a + jr z, .asm_1cbbc + call Func_1cbcc + +.asm_1cbbc + pop de + pop hl + inc hl + inc de + ld a, [wd4b7] + inc a + ld [wd4b7], a + cp $03 + jr c, .asm_1cbb3 + ret + +Func_1cbcc: + push af + ld a, SPRITE_DUEL_4 + farcall CreateSpriteAndAnimBufferEntry + ld a, [wWhichSprite] + ld [de], a + ld a, (1 << SPRITE_ANIM_FLAG_UNSKIPPABLE) + ld [wAnimFlags], a + ld c, SPRITE_ANIM_COORD_X + call GetSpriteAnimBufferProperty + call GetAnimCoordsAndFlags + + ld a, [wd4b7] + add LOW(Unknown_1cbfd) + ld e, a + ld a, HIGH(Unknown_1cbfd) + adc 0 + ld d, a + ld a, [de] + add b + + ld [hli], a ; SPRITE_ANIM_COORD_X + ld [hl], c ; SPRITE_ANIM_COORD_Y + + ld a, [wd4b8] + ld c, a + pop af + farcall Func_12ac9 + ret + +Unknown_1cbfd: + db -$10, -$8, $0, $8, -$8, -$10 + +Func_1cc03: + ld a, [wDuelAnimDamage] + ld l, a + ld a, [wDuelAnimDamage + 1] + ld h, a + + ld de, wd4b4 + ld bc, -100 + call .Func_1cc2f + ld bc, -10 + call .Func_1cc2f + + ld a, l + add $4f + ld [de], a + ld hl, wd4b4 + ld c, 2 +.asm_1cc23 + ld a, [hl] + cp $4f + jr nz, .asm_1cc2e + ld [hl], $00 + inc hl + dec c + jr nz, .asm_1cc23 +.asm_1cc2e + ret + +.Func_1cc2f + ld a, $4e +.loop + inc a + add hl, bc + jr c, .loop + + ld [de], a + inc de + ld a, l + sub c + ld l, a + ld a, h + sbc b + ld h, a + ret + +Func_1cc3e: + push hl + ld a, $03 + ld [wd4b7], a + ld de, wAnimationQueue + 4 + ld a, SPRITE_ANIM_91 + call Func_1cbcc + pop hl + ret + +Func_1cc4e: + push hl + ld a, $04 + ld [wd4b7], a + ld de, wAnimationQueue + 5 + ld a, SPRITE_ANIM_90 + call Func_1cbcc + ld a, [wd4b8] + add $12 + ld [wd4b8], a + pop hl + ret + +Func_1cc66: + push hl + ld a, $05 + ld [wd4b7], a + ld de, wAnimationQueue + 6 + ld a, SPRITE_ANIM_89 + call Func_1cbcc + pop hl + ret diff --git a/src/engine/duel/animations/screen_effects.asm b/src/engine/duel/animations/screen_effects.asm new file mode 100644 index 0000000..9058071 --- /dev/null +++ b/src/engine/duel/animations/screen_effects.asm @@ -0,0 +1,286 @@ +; initializes a screen animation from wTempAnimation +; loads a function pointer for updating a frame +; and initializes the duration of the animation. +InitScreenAnimation: + ld a, [wAnimationsDisabled] + or a + jr nz, .skip + ld a, [wTempAnimation] + ld [wd42a], a + sub DUEL_SCREEN_ANIMS + add a + add a + ld c, a + ld b, $00 + ld hl, Data_1cc9f + add hl, bc + ld a, [hli] + ld [wScreenAnimUpdatePtr], a + ld c, a + ld a, [hli] + ld [wScreenAnimUpdatePtr + 1], a + ld b, a + ld a, [hl] + ld [wScreenAnimDuration], a + call CallBC +.skip + ret + +; for the following animations, these functions +; are run with the corresponding duration. +; this duration decides different effects, +; depending on which function runs +; and is decreased by one each time. +; when it is down to 0, the animation is done. + +screen_effect: MACRO + dw \1 ; function pointer + db \2 ; duration + db $00 ; padding +ENDM + +Data_1cc9f: +; function pointer, duration + screen_effect ShakeScreenX_Small, 24 ; DUEL_ANIM_SMALL_SHAKE_X + screen_effect ShakeScreenX_Big, 32 ; DUEL_ANIM_BIG_SHAKE_X + screen_effect ShakeScreenY_Small, 24 ; DUEL_ANIM_SMALL_SHAKE_Y + screen_effect ShakeScreenY_Big, 32 ; DUEL_ANIM_BIG_SHAKE_Y + screen_effect WhiteFlashScreen, 8 ; DUEL_ANIM_FLASH + screen_effect DistortScreen, 63 ; DUEL_ANIM_DISTORT + +; checks if screen animation duration is over +; and if so, loads the default update function +LoadDefaultScreenAnimationUpdateWhenFinished: + ld a, [wScreenAnimDuration] + or a + ret nz + ; fallthrough + +; function called for the screen animation update when it is over +DefaultScreenAnimationUpdate: + ld a, $ff + ld [wd42a], a + call DisableInt_LYCoincidence + xor a + ldh [hSCX], a + ldh [rSCX], a + ldh [hSCY], a + ld hl, wScreenAnimUpdatePtr + ld [hl], LOW(DefaultScreenAnimationUpdate) + inc hl + ld [hl], HIGH(DefaultScreenAnimationUpdate) + ret + +; runs the screen update function set in wScreenAnimUpdatePtr +DoScreenAnimationUpdate: + ld a, 1 + ld [wScreenAnimDuration], a + ld hl, wScreenAnimUpdatePtr + ld a, [hli] + ld h, [hl] + ld l, a + call CallHL2 + jr DefaultScreenAnimationUpdate + +ShakeScreenX_Small: + ld hl, SmallShakeOffsets + jr ShakeScreenX + +ShakeScreenX_Big: + ld hl, BigShakeOffsets + jr ShakeScreenX + +ShakeScreenX: + ld a, l + ld [wd4bc], a + ld a, h + ld [wd4bc + 1], a + + ld hl, wScreenAnimUpdatePtr + ld [hl], LOW(.update) + inc hl + ld [hl], HIGH(.update) + ret + +.update + call DecrementScreenAnimDuration + call UpdateShakeOffset + jp nc, LoadDefaultScreenAnimationUpdateWhenFinished + ldh a, [hSCX] + add [hl] + ldh [hSCX], a + jp LoadDefaultScreenAnimationUpdateWhenFinished + +ShakeScreenY_Small: + ld hl, SmallShakeOffsets + jr ShakeScreenY + +ShakeScreenY_Big: + ld hl, BigShakeOffsets + jr ShakeScreenY + +ShakeScreenY: + ld a, l + ld [wd4bc], a + ld a, h + ld [wd4bc + 1], a + ld hl, wScreenAnimUpdatePtr + ld [hl], LOW(.update) + inc hl + ld [hl], HIGH(.update) + ret + +.update + call DecrementScreenAnimDuration + call UpdateShakeOffset + jp nc, LoadDefaultScreenAnimationUpdateWhenFinished + ldh a, [hSCY] + add [hl] + ldh [hSCY], a + jp LoadDefaultScreenAnimationUpdateWhenFinished + +; get the displacement of the current frame +; depending on the value of wScreenAnimDuration +; returns carry if displacement was updated +UpdateShakeOffset: + ld hl, wd4bc + ld a, [hli] + ld h, [hl] + ld l, a + ld a, [wScreenAnimDuration] + cp [hl] + ret nc + inc hl + push hl + inc hl + ld a, l + ld [wd4bc], a + ld a, h + ld [wd4bc + 1], a + pop hl + scf + ret + +SmallShakeOffsets: + db 21, 2 + db 17, -2 + db 13, 2 + db 9, -2 + db 5, 1 + db 1, -1 + +BigShakeOffsets: + db 29, 4 + db 25, -4 + db 21, 4 + db 17, -4 + db 13, 3 + db 9, -3 + db 5, 2 + db 1, -2 + +DecrementScreenAnimDuration: + ld hl, wScreenAnimDuration + dec [hl] + ret + +WhiteFlashScreen: + ld hl, wScreenAnimUpdatePtr + ld [hl], LOW(.update) + inc hl + ld [hl], HIGH(.update) + ld a, [wBGP] + ld [wd4bc], a + ; backup the current background pals + ld hl, wBackgroundPalettesCGB + ld de, wTempBackgroundPalettesCGB + ld bc, 8 palettes + call CopyDataHLtoDE_SaveRegisters + ld de, PALRGB_WHITE + ld hl, wBackgroundPalettesCGB + ld bc, (8 palettes) / 2 + call FillMemoryWithDE + xor a + call SetBGP + call FlushAllPalettes + +.update + call DecrementScreenAnimDuration + ld a, [wScreenAnimDuration] + or a + ret nz + ; retrieve the previous background pals + ld hl, wTempBackgroundPalettesCGB + ld de, wBackgroundPalettesCGB + ld bc, 8 palettes + call CopyDataHLtoDE_SaveRegisters + ld a, [wd4bc] + call SetBGP + call FlushAllPalettes + jp DefaultScreenAnimationUpdate + +DistortScreen: + ld hl, wScreenAnimUpdatePtr + ld [hl], LOW(.update) + inc hl + ld [hl], HIGH(.update) + xor a + ld [wApplyBGScroll], a + ld hl, wLCDCFunctionTrampoline + 1 + ld [hl], LOW(ApplyBackgroundScroll) + inc hl + ld [hl], HIGH(ApplyBackgroundScroll) + ld a, 1 + ld [wBGScrollMod], a + call EnableInt_LYCoincidence + +.update + ld a, [wScreenAnimDuration] + srl a + srl a + srl a + and %00000111 + ld c, a + ld b, $00 + ld hl, .BGScrollModData + add hl, bc + ld a, [hl] + ld [wBGScrollMod], a + call DecrementScreenAnimDuration + jp LoadDefaultScreenAnimationUpdateWhenFinished + +; each value is applied for 8 "ticks" of wScreenAnimDuration +; starting from the last and running backwards +.BGScrollModData + db 4, 3, 2, 1, 1, 1, 1, 2 + +Func_1ce03: + cp DUEL_ANIM_158 + jr z, .asm_1ce17 + sub $96 + add a + ld c, a + ld b, $00 + ld hl, .pointer_table + add hl, bc + ld a, [hli] + ld h, [hl] + ld l, a + jp Func_3bb5 + +.asm_1ce17 + ld a, [wDuelAnimDamage] + ld l, a + ld a, [wDuelAnimDamage + 1] + ld h, a + jp Func_3bb5 + +.pointer_table + dw Func_190f4 ; DUEL_ANIM_150 + dw PrintDamageText ; DUEL_ANIM_PRINT_DAMAGE + dw UpdateMainSceneHUD ; DUEL_ANIM_UPDATE_HUD + dw Func_191a3 ; DUEL_ANIM_153 + dw Func_191a3 ; DUEL_ANIM_154 + dw Func_191a3 ; DUEL_ANIM_155 + dw Func_191a3 ; DUEL_ANIM_156 + dw Func_191a3 ; DUEL_ANIM_157 diff --git a/src/engine/intro.asm b/src/engine/intro.asm new file mode 100644 index 0000000..8a16b3a --- /dev/null +++ b/src/engine/intro.asm @@ -0,0 +1,114 @@ +PlayIntroSequence: + call DisableLCD + farcall Func_10a9b + farcall Func_10000 + call Func_3ca0 + ld hl, HandleAllSpriteAnimations + call SetDoFrameFunction + call LoadTitleScreenSprites + + ld a, LOW(IntroSequence) + ld [wSequenceCmdPtr + 0], a + ld a, HIGH(IntroSequence) + ld [wSequenceCmdPtr + 1], a + + xor a + ld [wd317], a + ld [wIntroSequencePalsNeedUpdate], a + ld [wSequenceDelay], a + farcall FlashWhiteScreen + +.loop_cmds + call DoFrameIfLCDEnabled + call UpdateRNGSources + ldh a, [hKeysPressed] + and A_BUTTON | START + jr nz, .jump_to_title_screen + ld a, [wIntroSequencePalsNeedUpdate] + or a + jr z, .no_pal_update + farcall Func_10d74 +.no_pal_update + call ExecuteIntroSequenceCmd + ld a, [wSequenceDelay] + cp $ff + jr nz, .loop_cmds + jr .asm_1d39f + +.jump_to_title_screen + call AssertSongFinished + or a + jr nz, .asm_1d39f + call DisableLCD + ld a, MUSIC_TITLESCREEN + call PlaySong + lb bc, 0, 0 + ld a, SCENE_TITLE_SCREEN + call LoadScene + call IntroSequenceEmptyFunc +.asm_1d39f + call Func_3ca0 + call .ShowPressStart + call EnableLCD + ret + +.ShowPressStart + ld a, SPRITE_PRESS_START + farcall CreateSpriteAndAnimBufferEntry + ld c, SPRITE_ANIM_COORD_X + call GetSpriteAnimBufferProperty + ld a, 48 + ld [hli], a ; x + ld a, 112 + ld [hl], a ; y + ld c, SPRITE_ANIM_190 + ld a, [wConsole] + cp CONSOLE_CGB + jr nz, .asm_1d3c5 + ld c, SPRITE_ANIM_191 +.asm_1d3c5 + ld a, c + ld bc, 60 + farcall Func_12ac9 + ret + +LoadTitleScreenSprites: + xor a + ld [wd4ca], a + ld [wd4cb], a + ld a, PALETTE_30 + farcall LoadPaletteData + + ld bc, 0 + ld de, wTitleScreenSprites +.loop_load_sprites + push bc + push de + ld hl, .TitleScreenSpriteList + add hl, bc + ld a, [hl] + farcall CreateSpriteAndAnimBufferEntry + ld a, [wWhichSprite] + ld [de], a + call GetFirstSpriteAnimBufferProperty + inc hl + ld a, [hl] ; SPRITE_ANIM_ATTRIBUTES + or c + ld [hl], a + pop de + pop bc + inc de + inc c + ld a, c + cp $7 + jr c, .loop_load_sprites + ret + +.TitleScreenSpriteList + db SPRITE_GRASS + db SPRITE_FIRE + db SPRITE_WATER + db SPRITE_COLORLESS + db SPRITE_LIGHTNING + db SPRITE_PSYCHIC + db SPRITE_FIGHTING diff --git a/src/engine/menus/start.asm b/src/engine/menus/start.asm new file mode 100644 index 0000000..4d46d4a --- /dev/null +++ b/src/engine/menus/start.asm @@ -0,0 +1,418 @@ +; plays the Opening sequence, and handles player selection +; in the Title Screen and Start Menu +HandleTitleScreen: +; if last selected item in Start Menu is 0 (Card Pop!) +; then skip straight to the Start Menu +; this makes it so that returning from Card Pop! +; doesn't play the Opening sequence + ld a, [wLastSelectedStartMenuItem] + or a + jr z, .start_menu + +.play_opening + ld a, MUSIC_STOP + call PlaySong + call Func_3ca0 + call PlayIntroSequence + call LoadTitleScreenSprites + + xor a + ld [wd635], a + ld a, $3c + ld [wTitleScreenIgnoreInputCounter], a +.loop + call DoFrameIfLCDEnabled + call UpdateRNGSources + call AnimateRandomTitleScreenOrb + ld hl, wd635 + inc [hl] + call AssertSongFinished + or a + jr nz, .song_playing + ; reset back to the opening sequence + farcall Func_10ab4 + jr .play_opening + +.song_playing + ; should we ignore user input? + ld hl, wTitleScreenIgnoreInputCounter + ld a, [hl] + or a + jr z, .check_keys + ; ignore input, decrement the counter + dec [hl] + jr .loop + +.check_keys + ldh a, [hKeysPressed] + and A_BUTTON | START + jr z, .loop + ld a, SFX_02 + call PlaySFX + farcall Func_10ab4 + +.start_menu + call CheckIfHasSaveData + call HandleStartMenu + +; new game + ld a, [wStartMenuChoice] + cp START_MENU_NEW_GAME + jr nz, .continue_from_diary + call DeleteSaveDataForNewGame + jr c, HandleTitleScreen + jr .card_pop +.continue_from_diary + ld a, [wStartMenuChoice] + cp START_MENU_CONTINUE_FROM_DIARY + jr nz, .card_pop + call AskToContinueFromDiaryWithDuelData + jr c, HandleTitleScreen +.card_pop + ld a, [wStartMenuChoice] + cp START_MENU_CARD_POP + jr nz, .continue_duel + call ShowCardPopCGBDisclaimer + jr c, HandleTitleScreen +.continue_duel + call ResetDoFrameFunction + call Func_3ca0 + ret + +; updates wHasSaveData and wHasDuelSaveData +; depending on whether the save data is valid or not +CheckIfHasSaveData: + farcall ValidateBackupGeneralSaveData + ld a, TRUE + jr c, .no_error + ld a, FALSE +.no_error + ld [wHasSaveData], a + cp $00 ; or a + jr z, .write_has_duel_data + bank1call ValidateSavedNonLinkDuelData + ld a, TRUE + jr nc, .write_has_duel_data + ld a, FALSE +.write_has_duel_data + ld [wHasDuelSaveData], a + farcall ValidateBackupGeneralSaveData + ret + +; handles printing the Start Menu +; and getting player input and choice +HandleStartMenu: + ld a, MUSIC_PC_MAIN_MENU + call PlaySong + call DisableLCD + farcall Func_10000 + lb de, $30, $8f + call SetupText + call Func_3ca0 + xor a + ld [wLineSeparation], a + call .DrawPlayerPortrait + call .SetStartMenuParams + + ld a, $ff + ld [wTitleScreenIgnoreInputCounter], a + ld a, [wLastSelectedStartMenuItem] + cp $4 + jr c, .init_menu + ld a, [wHasSaveData] + or a + jr z, .init_menu + ld a, 1 ; start at second menu option +.init_menu + ld hl, wStartMenuParams + farcall InitAndPrintPauseMenu + farcall FlashWhiteScreen + +.wait_input + call DoFrameIfLCDEnabled + call UpdateRNGSources + call HandleMenuInput + push af + call PrintStartMenuDescriptionText + pop af + jr nc, .wait_input + ldh a, [hCurMenuItem] + cp e + jr nz, .wait_input + + ld [wLastSelectedStartMenuItem], a + ld a, [wHasSaveData] + or a + jr nz, .no_adjustment + ; New Game is 3rd option + ; but when there's no save data, + ; it's the 1st in menu list, so adjust it + inc e + inc e +.no_adjustment + ld a, e + ld [wStartMenuChoice], a + ret + +.SetStartMenuParams + ld hl, .StartMenuParams + ld de, wStartMenuParams + ld bc, .StartMenuParamsEnd - .StartMenuParams + call CopyDataHLtoDE + + ld e, 0 + ld a, [wHasSaveData] + or a + jr z, .get_text_id ; New Game + inc e + ld a, 2 + call .AddItems + ld a, [wHasDuelSaveData] + or a + jr z, .get_text_id ; Continue From Diary + inc e + ld a, 1 + call .AddItems + ; Continue Duel + +.get_text_id + sla e + ld d, $00 + ld hl, .StartMenuTextIDs + add hl, de + ; set text ID as Start Menu param + ld a, [hli] + ld [wStartMenuParams + 6], a + ld a, [hl] + ld [wStartMenuParams + 7], a + ret + +; adds c items to start menu list +; this means adding 2 units per item to the text box height +; and adding to the number of items +.AddItems + push bc + ld c, a + ; number of items in menu + ld a, [wStartMenuParams + 12] + add c + ld [wStartMenuParams + 12], a + ; height of text box + sla c + ld a, [wStartMenuParams + 3] + add c + ld [wStartMenuParams + 3], a + pop bc + ret + +.StartMenuParams + db 0, 0 ; start menu coords + db 14, 4 ; start menu text box dimensions + + db 2, 2 ; text alignment for InitTextPrinting + tx NewGameText + db $ff + + db 1, 2 ; cursor x, cursor y + db 2 ; y displacement between items + db 1 ; number of items + db SYM_CURSOR_R ; cursor tile number + db SYM_SPACE ; tile behind cursor + dw NULL ; function pointer if non-0 +.StartMenuParamsEnd + +.StartMenuTextIDs + tx NewGameText + tx CardPopContinueDiaryNewGameText + tx CardPopContinueDiaryNewGameContinueDuelText + +.DrawPlayerPortrait + lb bc, 14, 1 + farcall $4, DrawPlayerPortrait + ret + +; prints the description for the current selected item +; in the Start Menu in the text box +PrintStartMenuDescriptionText: + push hl + push bc + push de + ; don't print if it's already showing + ld a, [wCurMenuItem] + ld e, a + ld a, [wCurHighlightedStartMenuItem] + cp e + jr z, .skip + ld a, [wHasSaveData] + or a + jr nz, .has_data + ; New Game option is 3rd element + ; in function table, so add 2 + inc e + inc e +.has_data + + ld a, e + push af + lb de, 0, 10 + lb bc, 20, 8 + call DrawRegularTextBox + pop af + ld hl, .StartMenuDescriptionFunctionTable + call JumpToFunctionInTable +.skip + ld a, [wCurMenuItem] + ld [wCurHighlightedStartMenuItem], a + pop de + pop bc + pop hl + ret + +.StartMenuDescriptionFunctionTable + dw .CardPop + dw .ContinueFromDiary + dw .NewGame + dw .ContinueDuel + +.CardPop + lb de, 1, 12 + call InitTextPrinting + ldtx hl, WhenYouCardPopWithFriendText + call PrintTextNoDelay + ret + +.ContinueDuel + lb de, 1, 12 + call InitTextPrinting + ldtx hl, TheGameWillContinueFromThePointInTheDuelText + call PrintTextNoDelay + ret + +.NewGame + lb de, 1, 12 + call InitTextPrinting + ldtx hl, StartANewGameText + call PrintTextNoDelay + ret + +.ContinueFromDiary + ; get OW map name + ld a, [wCurOverworldMap] + add a + ld c, a + ld b, $00 + ld hl, OverworldMapNames + add hl, bc + ld a, [hli] + ld [wTxRam2 + 0], a + ld a, [hl] + ld [wTxRam2 + 1], a + + ; get medal count + ld a, [wMedalCount] + ld [wTxRam3 + 0], a + xor a + ld [wTxRam3 + 1], a + + ; print text + lb de, 1, 10 + call InitTextPrinting + ldtx hl, ContinueFromDiarySummaryText + call PrintTextNoDelay + + ld a, [wTotalNumCardsCollected] + ld d, a + ld a, [wTotalNumCardsToCollect] + ld e, a + ld bc, $90e + farcall Func_1024f + ld bc, $a10 + farcall Func_101df + ret + +; asks the player whether it's okay to delete +; the save data in order to create a new one +; if player answers "yes", delete it +DeleteSaveDataForNewGame: +; exit if there no save data + ld a, [wHasSaveData] + or a + ret z + + call DisableLCD + farcall Func_10000 + call Func_3ca0 + farcall FlashWhiteScreen + call DoFrameIfLCDEnabled + ldtx hl, SavedDataAlreadyExistsText + call PrintScrollableText_NoTextBoxLabel + ldtx hl, OKToDeleteTheDataText + call YesOrNoMenuWithText + ret c ; quit if chose "no" + farcall InvalidateSaveData + ldtx hl, AllDataWasDeletedText + call PrintScrollableText_NoTextBoxLabel + or a + ret + +; asks the player if the game should resume +; from diary even though there is Duel save data +; returns carry if "no" was selected +AskToContinueFromDiaryWithDuelData: +; return if there's no duel save data + ld a, [wHasDuelSaveData] + or a + ret z + + call DisableLCD + farcall Func_10000 + call Func_3ca0 + farcall FlashWhiteScreen + call DoFrameIfLCDEnabled + ldtx hl, DataExistsWhenPowerWasTurnedOFFDuringDuelText + call PrintScrollableText_NoTextBoxLabel + ldtx hl, ContinueFromDiaryText + call YesOrNoMenuWithText + ret c + or a + ret + +; shows disclaimer for Card Pop! +; in case player is not playing in CGB +; return carry if disclaimer was shown +ShowCardPopCGBDisclaimer: +; return if playing in CGB + ld a, [wConsole] + cp CONSOLE_CGB + ret z + + lb de, 0, 10 + lb bc, 20, 8 + call DrawRegularTextBox + lb de, 1,12 + call InitTextPrinting + ldtx hl, YouCanAccessCardPopOnlyWithGameBoyColorsText + call PrintTextNoDelay + lb bc, SYM_CURSOR_D, SYM_BOX_BOTTOM + lb de, 18, 17 + call SetCursorParametersForTextBox + call WaitForButtonAorB + scf + ret + +DrawPlayerPortraitAndPrintNewGameText: + call DisableLCD + farcall Func_10a9b + farcall Func_10000 + call Func_3ca0 + ld hl, HandleAllSpriteAnimations + call SetDoFrameFunction + lb bc, 7, 3 + farcall $4, DrawPlayerPortrait + farcall Func_10af9 + call DoFrameIfLCDEnabled + ldtx hl, IsCrazyAboutPokemonAndPokemonCardCollectingText + call PrintScrollableText_NoTextBoxLabel + call ResetDoFrameFunction + call Func_3ca0 + ret diff --git a/src/engine/sequences/credits_sequence_commands.asm b/src/engine/sequences/credits_sequence_commands.asm index 0fde871..263ecff 100644 --- a/src/engine/sequences/credits_sequence_commands.asm +++ b/src/engine/sequences/credits_sequence_commands.asm @@ -310,7 +310,7 @@ CreditsSequenceCmd_FadeOut: ; 1d9ee (7:59ee) call EnableLCD call DoFrameIfLCDEnabled call DisableLCD - call Set_WD_off + call SetWDOff jp AdvanceCreditsSequenceCmdPtrBy2 CreditsSequenceCmd_DrawRectangle: ; 1da04 (7:5a04) diff --git a/src/engine/sequences/intro_sequence_commands.asm b/src/engine/sequences/intro_sequence_commands.asm new file mode 100644 index 0000000..451ec0b --- /dev/null +++ b/src/engine/sequences/intro_sequence_commands.asm @@ -0,0 +1,392 @@ +ExecuteIntroSequenceCmd: ; 1d408 (7:5408) + ld a, [wSequenceDelay] + or a + jr z, .call_function + cp $ff + ret z ; sequence ended + + dec a ; still waiting + ld [wSequenceDelay], a + ret + +.call_function + ld a, [wSequenceCmdPtr + 0] + ld l, a + ld a, [wSequenceCmdPtr + 1] + ld h, a + ld a, [hli] + ld e, a + ld a, [hli] + ld d, a + ld a, [hli] + ld c, a + ld a, [hli] + ld b, a + ld l, e + ld h, d + call CallHL2 + jr c, ExecuteIntroSequenceCmd + ret + +AdvanceIntroSequenceCmdPtrBy2: ; 1d42e (7:542e) + ld a, 2 + jr AdvanceIntroSequenceCmdPtr + +AdvanceIntroSequenceCmdPtrBy3: ; 1d432 (7:5432) + ld a, 3 + jr AdvanceIntroSequenceCmdPtr + +AdvanceIntroSequenceCmdPtrBy4: ; 1d436 (7:5436) + ld a, 4 +; fallthrough + +AdvanceIntroSequenceCmdPtr: ; 1d438 (7:5438) + push hl + ld hl, wSequenceCmdPtr + add [hl] + ld [hli], a + ld a, [hl] + adc 0 + ld [hl], a + pop hl + ret + +IntroSequenceCmd_WaitOrbsAnimation: ; 1d444 (7:5444) + ld c, $7 + ld de, wTitleScreenSprites +.loop + ld a, [de] + ld [wWhichSprite], a + farcall GetSpriteAnimCounter + cp $ff + jr nz, .no_carry + inc de + dec c + jr nz, .loop + call AdvanceIntroSequenceCmdPtrBy2 + scf + ret + +.no_carry + or a + ret + +IntroSequenceCmd_Wait: ; 1d460 (7:5460) + ld a, c + ld [wSequenceDelay], a + call AdvanceIntroSequenceCmdPtrBy3 + scf + ret + +IntroSequenceCmd_SetOrbsAnimations: ; 1d469 (7:5469) + ld l, c + ld h, b + + ld c, $7 + ld de, wTitleScreenSprites +.loop + push bc + push de + ld a, [de] + ld [wWhichSprite], a + ld a, [hli] + farcall StartSpriteAnimation + pop de + pop bc + inc de + dec c + jr nz, .loop + + call AdvanceIntroSequenceCmdPtrBy4 + scf + ret + +IntroSequenceCmd_SetOrbsCoordinates: ; 1d486 (7:5486) + ld l, c + ld h, b + + ld c, $7 + ld de, wTitleScreenSprites +.loop + push bc + push de + ld a, [de] + ld [wWhichSprite], a + push hl + ld c, SPRITE_ANIM_COORD_X + call GetSpriteAnimBufferProperty + ld e, l + ld d, h + pop hl + ld a, [hli] + add 8 + ld [de], a ; x + inc de + ld a, [hli] + add 16 + ld [de], a ; y + pop de + pop bc + inc de + dec c + jr nz, .loop + + call AdvanceIntroSequenceCmdPtrBy4 + scf + ret + +OpeningOrbAnimations_CharizardScene: ; 1d4b0 (7:54b0) + db SPRITE_ANIM_192 ; GRASS + db SPRITE_ANIM_193 ; FIRE + db SPRITE_ANIM_193 ; WATER + db SPRITE_ANIM_192 ; COLORLESS + db SPRITE_ANIM_193 ; LIGHTNING + db SPRITE_ANIM_192 ; PSYCHIC + db SPRITE_ANIM_193 ; FIGHTING + +OpeningOrbCoordinates_CharizardScene: ; 1d4b7 (7:54b7) + ; x coord, y coord + db 240, 28 ; GRASS + db 160, 120 ; FIRE + db 160, 8 ; WATER + db 240, 64 ; COLORLESS + db 160, 84 ; LIGHTNING + db 240, 100 ; PSYCHIC + db 160, 44 ; FIGHTING + +OpeningOrbAnimations_ScytherScene: ; 1d4c5 (7:54c5) + db SPRITE_ANIM_193 ; GRASS + db SPRITE_ANIM_192 ; FIRE + db SPRITE_ANIM_192 ; WATER + db SPRITE_ANIM_193 ; COLORLESS + db SPRITE_ANIM_192 ; LIGHTNING + db SPRITE_ANIM_193 ; PSYCHIC + db SPRITE_ANIM_192 ; FIGHTING + +OpeningOrbCoordinates_ScytherScene: ; 1d4cc (7:54cc) + ; x coord, y coord + db 160, 28 ; GRASS + db 240, 120 ; FIRE + db 240, 8 ; WATER + db 160, 64 ; COLORLESS + db 240, 84 ; LIGHTNING + db 160, 100 ; PSYCHIC + db 240, 44 ; FIGHTING + +OpeningOrbAnimations_AerodactylScene: ; 1d4da (7:54da) + db SPRITE_ANIM_194 ; GRASS + db SPRITE_ANIM_197 ; FIRE + db SPRITE_ANIM_200 ; WATER + db SPRITE_ANIM_203 ; COLORLESS + db SPRITE_ANIM_206 ; LIGHTNING + db SPRITE_ANIM_209 ; PSYCHIC + db SPRITE_ANIM_212 ; FIGHTING + +OpeningOrbCoordinates_AerodactylScene: ; 1d4e1 (7:54e1) + ; x coord, y coord + db 240, 32 ; GRASS + db 160, 112 ; FIRE + db 160, 16 ; WATER + db 240, 64 ; COLORLESS + db 160, 80 ; LIGHTNING + db 240, 96 ; PSYCHIC + db 160, 48 ; FIGHTING + +OpeningOrbAnimations_InitialTitleScreen: ; 1d4ef (7:54ef) + db SPRITE_ANIM_195 ; GRASS + db SPRITE_ANIM_198 ; FIRE + db SPRITE_ANIM_201 ; WATER + db SPRITE_ANIM_204 ; COLORLESS + db SPRITE_ANIM_207 ; LIGHTNING + db SPRITE_ANIM_210 ; PSYCHIC + db SPRITE_ANIM_213 ; FIGHTING + +OpeningOrbCoordinates_InitialTitleScreen: ; 1d4f6 (7:54f6) + ; x coord, y coord + db 112, 144 ; GRASS + db 12, 144 ; FIRE + db 32, 144 ; WATER + db 92, 144 ; COLORLESS + db 52, 144 ; LIGHTNING + db 132, 144 ; PSYCHIC + db 72, 144 ; FIGHTING + +OpeningOrbAnimations_InTitleScreen: ; 1d504 (7:5504) + db SPRITE_ANIM_196 ; GRASS + db SPRITE_ANIM_199 ; FIRE + db SPRITE_ANIM_202 ; WATER + db SPRITE_ANIM_205 ; COLORLESS + db SPRITE_ANIM_208 ; LIGHTNING + db SPRITE_ANIM_211 ; PSYCHIC + db SPRITE_ANIM_214 ; FIGHTING + +OpeningOrbCoordinates_InTitleScreen: ; 1d50b (7:550b) + ; x coord, y coord + db 112, 76 ; GRASS + db 0, 28 ; FIRE + db 32, 76 ; WATER + db 92, 252 ; COLORLESS + db 52, 252 ; LIGHTNING + db 144, 28 ; PSYCHIC + db 72, 76 ; FIGHTING + +IntroSequenceCmd_PlayTitleScreenMusic: ; 1d519 (7:5519) + ld a, MUSIC_TITLESCREEN + call PlaySong + call AdvanceIntroSequenceCmdPtrBy2 + scf + ret + +IntroSequenceCmd_WaitSFX: ; 1d523 (7:5523) + call AssertSFXFinished + or a + jr nz, .no_carry + call AdvanceIntroSequenceCmdPtrBy2 + scf + ret + +.no_carry + or a + ret + +IntroSequenceCmd_PlaySFX: ; 1d530 (7:5530) + ld a, c + call PlaySFX + call AdvanceIntroSequenceCmdPtrBy3 + scf + ret + +IntroSequenceCmd_FadeIn: ; 1d539 (7:5539) + ld a, TRUE + ld [wIntroSequencePalsNeedUpdate], a + call AdvanceIntroSequenceCmdPtrBy2 + scf + ret + +IntroSequenceCmd_FadeOut: ; 1d543 (7:5543) + farcall Func_10d50 + ld a, TRUE + ld [wIntroSequencePalsNeedUpdate], a + call AdvanceIntroSequenceCmdPtrBy2 + scf + ret + +IntroSequenceCmd_LoadCharizardScene: ; 1d551 (7:5551) + lb bc, 6, 3 + ld a, SCENE_CHARIZARD_INTRO + jr LoadOpeningSceneAndUpdateSGBBorder + +IntroSequenceCmd_LoadScytherScene: ; 1d558 (7:5558) + lb bc, 6, 3 + ld a, SCENE_SCYTHER_INTRO + jr LoadOpeningSceneAndUpdateSGBBorder + +IntroSequenceCmd_LoadAerodactylScene: ; 1d55f (7:555f) + lb bc, 6, 3 + ld a, SCENE_AERODACTYL_INTRO +; fallthrough + +LoadOpeningSceneAndUpdateSGBBorder: ; 1d564 (7:5564) + call LoadOpeningScene + ld l, %001010 + lb bc, 0, 0 + lb de, 20, 18 + farcall Func_70498 + scf + ret + +IntroSequenceCmd_LoadTitleScreenScene: ; 1d575 (7:5575) + lb bc, 0, 0 + ld a, SCENE_TITLE_SCREEN + call LoadOpeningScene + call IntroSequenceEmptyFunc + scf + ret + +; a = scene ID +; bc = coordinates for scene +LoadOpeningScene: ; 1d582 (7:5582) + push af + push bc + call DisableLCD + pop bc + pop af + + farcall _LoadScene ; TODO change func name? + farcall Func_10d17 + + xor a + ld [wIntroSequencePalsNeedUpdate], a + call AdvanceIntroSequenceCmdPtrBy2 + call EnableLCD + ret + +IntroSequenceEmptyFunc: ; 1d59c (7:559c) + ret + +INCLUDE "data/sequences/intro.asm" + +; once every 63 frames randomly choose an orb sprite +; to animate, i.e. circle around the screen +AnimateRandomTitleScreenOrb: + ld a, [wConsole] + cp CONSOLE_CGB + call z, .UpdateSpriteAttributes + ld a, [wd635] + and 63 + ret nz ; don't pick an orb now + +.pick_orb + ld a, $7 + call Random + ld c, a + ld b, $00 + ld hl, wTitleScreenSprites + add hl, bc + ld a, [hl] + ld [wWhichSprite], a + farcall GetSpriteAnimCounter + cp $ff + jr nz, .pick_orb + + ld c, SPRITE_ANIM_ATTRIBUTES + call GetSpriteAnimBufferProperty + ld a, [wConsole] + cp CONSOLE_CGB + jr nz, .set_coords + set SPRITE_ANIM_FLAG_UNSKIPPABLE, [hl] + +.set_coords + inc hl + ld a, 248 + ld [hli], a ; SPRITE_ANIM_COORD_X + ld a, 14 + ld [hl], a ; SPRITE_ANIM_COORD_Y + ld a, [wConsole] + cp CONSOLE_CGB + ld a, SPRITE_ANIM_215 + jr nz, .start_anim + ld a, SPRITE_ANIM_216 +.start_anim + farcall StartSpriteAnimation + ret + +.UpdateSpriteAttributes + ld c, $7 + ld de, wTitleScreenSprites +.loop_orbs + push bc + ld a, [de] + ld [wWhichSprite], a + ld c, SPRITE_ANIM_COORD_X + call GetSpriteAnimBufferProperty + ld a, [hld] + cp 152 + jr nz, .skip + res SPRITE_ANIM_FLAG_UNSKIPPABLE, [hl] +.skip + pop bc + inc de + dec c + jr nz, .loop_orbs + ret diff --git a/src/engine/sequences/opening_sequence_commands.asm b/src/engine/sequences/opening_sequence_commands.asm deleted file mode 100644 index 446daa2..0000000 --- a/src/engine/sequences/opening_sequence_commands.asm +++ /dev/null @@ -1,325 +0,0 @@ -ExecuteOpeningSequenceCmd: ; 1d408 (7:5408) - ld a, [wSequenceDelay] - or a - jr z, .call_function - cp $ff - ret z ; sequence ended - - dec a ; still waiting - ld [wSequenceDelay], a - ret - -.call_function - ld a, [wSequenceCmdPtr + 0] - ld l, a - ld a, [wSequenceCmdPtr + 1] - ld h, a - ld a, [hli] - ld e, a - ld a, [hli] - ld d, a - ld a, [hli] - ld c, a - ld a, [hli] - ld b, a - ld l, e - ld h, d - call CallHL2 - jr c, ExecuteOpeningSequenceCmd - ret - -AdvanceOpeningSequenceCmdPtrBy2: ; 1d42e (7:542e) - ld a, 2 - jr AdvanceOpeningSequenceCmdPtr - -AdvanceOpeningSequenceCmdPtrBy3: ; 1d432 (7:5432) - ld a, 3 - jr AdvanceOpeningSequenceCmdPtr - -AdvanceOpeningSequenceCmdPtrBy4: ; 1d436 (7:5436) - ld a, 4 -; fallthrough - -AdvanceOpeningSequenceCmdPtr: ; 1d438 (7:5438) - push hl - ld hl, wSequenceCmdPtr - add [hl] - ld [hli], a - ld a, [hl] - adc 0 - ld [hl], a - pop hl - ret - -OpeningSequenceCmd_WaitOrbsAnimation: ; 1d444 (7:5444) - ld c, $7 - ld de, wTitleScreenSprites -.loop - ld a, [de] - ld [wWhichSprite], a - farcall GetSpriteAnimCounter - cp $ff - jr nz, .no_carry - inc de - dec c - jr nz, .loop - call AdvanceOpeningSequenceCmdPtrBy2 - scf - ret - -.no_carry - or a - ret - -OpeningSequenceCmd_Wait: ; 1d460 (7:5460) - ld a, c - ld [wSequenceDelay], a - call AdvanceOpeningSequenceCmdPtrBy3 - scf - ret - -OpeningSequenceCmd_SetOrbsAnimations: ; 1d469 (7:5469) - ld l, c - ld h, b - - ld c, $7 - ld de, wTitleScreenSprites -.loop - push bc - push de - ld a, [de] - ld [wWhichSprite], a - ld a, [hli] - farcall StartSpriteAnimation - pop de - pop bc - inc de - dec c - jr nz, .loop - - call AdvanceOpeningSequenceCmdPtrBy4 - scf - ret - -OpeningSequenceCmd_SetOrbsCoordinates: ; 1d486 (7:5486) - ld l, c - ld h, b - - ld c, $7 - ld de, wTitleScreenSprites -.loop - push bc - push de - ld a, [de] - ld [wWhichSprite], a - push hl - ld c, SPRITE_ANIM_COORD_X - call GetSpriteAnimBufferProperty - ld e, l - ld d, h - pop hl - ld a, [hli] - add 8 - ld [de], a ; x - inc de - ld a, [hli] - add 16 - ld [de], a ; y - pop de - pop bc - inc de - dec c - jr nz, .loop - - call AdvanceOpeningSequenceCmdPtrBy4 - scf - ret - -OpeningOrbAnimations_CharizardScene: ; 1d4b0 (7:54b0) - db SPRITE_ANIM_192 ; GRASS - db SPRITE_ANIM_193 ; FIRE - db SPRITE_ANIM_193 ; WATER - db SPRITE_ANIM_192 ; COLORLESS - db SPRITE_ANIM_193 ; LIGHTNING - db SPRITE_ANIM_192 ; PSYCHIC - db SPRITE_ANIM_193 ; FIGHTING - -OpeningOrbCoordinates_CharizardScene: ; 1d4b7 (7:54b7) - ; x coord, y coord - db 240, 28 ; GRASS - db 160, 120 ; FIRE - db 160, 8 ; WATER - db 240, 64 ; COLORLESS - db 160, 84 ; LIGHTNING - db 240, 100 ; PSYCHIC - db 160, 44 ; FIGHTING - -OpeningOrbAnimations_ScytherScene: ; 1d4c5 (7:54c5) - db SPRITE_ANIM_193 ; GRASS - db SPRITE_ANIM_192 ; FIRE - db SPRITE_ANIM_192 ; WATER - db SPRITE_ANIM_193 ; COLORLESS - db SPRITE_ANIM_192 ; LIGHTNING - db SPRITE_ANIM_193 ; PSYCHIC - db SPRITE_ANIM_192 ; FIGHTING - -OpeningOrbCoordinates_ScytherScene: ; 1d4cc (7:54cc) - ; x coord, y coord - db 160, 28 ; GRASS - db 240, 120 ; FIRE - db 240, 8 ; WATER - db 160, 64 ; COLORLESS - db 240, 84 ; LIGHTNING - db 160, 100 ; PSYCHIC - db 240, 44 ; FIGHTING - -OpeningOrbAnimations_AerodactylScene: ; 1d4da (7:54da) - db SPRITE_ANIM_194 ; GRASS - db SPRITE_ANIM_197 ; FIRE - db SPRITE_ANIM_200 ; WATER - db SPRITE_ANIM_203 ; COLORLESS - db SPRITE_ANIM_206 ; LIGHTNING - db SPRITE_ANIM_209 ; PSYCHIC - db SPRITE_ANIM_212 ; FIGHTING - -OpeningOrbCoordinates_AerodactylScene: ; 1d4e1 (7:54e1) - ; x coord, y coord - db 240, 32 ; GRASS - db 160, 112 ; FIRE - db 160, 16 ; WATER - db 240, 64 ; COLORLESS - db 160, 80 ; LIGHTNING - db 240, 96 ; PSYCHIC - db 160, 48 ; FIGHTING - -OpeningOrbAnimations_InitialTitleScreen: ; 1d4ef (7:54ef) - db SPRITE_ANIM_195 ; GRASS - db SPRITE_ANIM_198 ; FIRE - db SPRITE_ANIM_201 ; WATER - db SPRITE_ANIM_204 ; COLORLESS - db SPRITE_ANIM_207 ; LIGHTNING - db SPRITE_ANIM_210 ; PSYCHIC - db SPRITE_ANIM_213 ; FIGHTING - -OpeningOrbCoordinates_InitialTitleScreen: ; 1d4f6 (7:54f6) - ; x coord, y coord - db 112, 144 ; GRASS - db 12, 144 ; FIRE - db 32, 144 ; WATER - db 92, 144 ; COLORLESS - db 52, 144 ; LIGHTNING - db 132, 144 ; PSYCHIC - db 72, 144 ; FIGHTING - -OpeningOrbAnimations_InTitleScreen: ; 1d504 (7:5504) - db SPRITE_ANIM_196 ; GRASS - db SPRITE_ANIM_199 ; FIRE - db SPRITE_ANIM_202 ; WATER - db SPRITE_ANIM_205 ; COLORLESS - db SPRITE_ANIM_208 ; LIGHTNING - db SPRITE_ANIM_211 ; PSYCHIC - db SPRITE_ANIM_214 ; FIGHTING - -OpeningOrbCoordinates_InTitleScreen: ; 1d50b (7:550b) - ; x coord, y coord - db 112, 76 ; GRASS - db 0, 28 ; FIRE - db 32, 76 ; WATER - db 92, 252 ; COLORLESS - db 52, 252 ; LIGHTNING - db 144, 28 ; PSYCHIC - db 72, 76 ; FIGHTING - -OpeningSequenceCmd_PlayTitleScreenMusic: ; 1d519 (7:5519) - ld a, MUSIC_TITLESCREEN - call PlaySong - call AdvanceOpeningSequenceCmdPtrBy2 - scf - ret - -OpeningSequenceCmd_WaitSFX: ; 1d523 (7:5523) - call AssertSFXFinished - or a - jr nz, .no_carry - call AdvanceOpeningSequenceCmdPtrBy2 - scf - ret - -.no_carry - or a - ret - -OpeningSequenceCmd_PlaySFX: ; 1d530 (7:5530) - ld a, c - call PlaySFX - call AdvanceOpeningSequenceCmdPtrBy3 - scf - ret - -OpeningSequenceCmd_FadeIn: ; 1d539 (7:5539) - ld a, TRUE - ld [wOpeningSequencePalsNeedUpdate], a - call AdvanceOpeningSequenceCmdPtrBy2 - scf - ret - -OpeningSequenceCmd_FadeOut: ; 1d543 (7:5543) - farcall Func_10d50 - ld a, TRUE - ld [wOpeningSequencePalsNeedUpdate], a - call AdvanceOpeningSequenceCmdPtrBy2 - scf - ret - -OpeningSequenceCmd_LoadCharizardScene: ; 1d551 (7:5551) - lb bc, 6, 3 - ld a, SCENE_CHARIZARD_INTRO - jr LoadOpeningSceneAndUpdateSGBBorder - -OpeningSequenceCmd_LoadScytherScene: ; 1d558 (7:5558) - lb bc, 6, 3 - ld a, SCENE_SCYTHER_INTRO - jr LoadOpeningSceneAndUpdateSGBBorder - -OpeningSequenceCmd_LoadAerodactylScene: ; 1d55f (7:555f) - lb bc, 6, 3 - ld a, SCENE_AERODACTYL_INTRO -; fallthrough - -LoadOpeningSceneAndUpdateSGBBorder: ; 1d564 (7:5564) - call LoadOpeningScene - ld l, %001010 - lb bc, 0, 0 - lb de, 20, 18 - farcall Func_70498 - scf - ret - -OpeningSequenceCmd_LoadTitleScreenScene: ; 1d575 (7:5575) - lb bc, 0, 0 - ld a, SCENE_TITLE_SCREEN - call LoadOpeningScene - call OpeningSequenceEmptyFunc - scf - ret - -; a = scene ID -; bc = coordinates for scene -LoadOpeningScene: ; 1d582 (7:5582) - push af - push bc - call DisableLCD - pop bc - pop af - - farcall _LoadScene ; TODO change func name? - farcall Func_10d17 - - xor a - ld [wOpeningSequencePalsNeedUpdate], a - call AdvanceOpeningSequenceCmdPtrBy2 - call EnableLCD - ret - -OpeningSequenceEmptyFunc: ; 1d59c (7:559c) - ret diff --git a/src/engine/unused_copyright.asm b/src/engine/unused_copyright.asm new file mode 100644 index 0000000..31a489b --- /dev/null +++ b/src/engine/unused_copyright.asm @@ -0,0 +1,26 @@ +; shows Copyright information for 300 frames +; or until Start button is pressed +UnusedCopyrightScreen: ; unreferenced + call DisableLCD + farcall Func_10a9b + farcall Func_10000 + ld bc, $0 + ld a, SCENE_COPYRIGHT + call LoadScene + farcall Func_10af9 + ld bc, 300 +.loop_frame + push bc + call DoFrameIfLCDEnabled + call UpdateRNGSources + pop bc + ldh a, [hKeysPressed] + and START + jr nz, .exit + dec bc + ld a, b + or c + jr nz, .loop_frame +.exit + farcall Func_10ab4 + ret diff --git a/src/home/lcd.asm b/src/home/lcd.asm index 5e8fc5d..5d82c72 100644 --- a/src/home/lcd.asm +++ b/src/home/lcd.asm @@ -76,7 +76,7 @@ Set_WD_on: ret ; set Window Display off -Set_WD_off: +SetWDOff: ld a, [wLCDC] and LCDC_WINOFF ld [wLCDC], a diff --git a/src/home/map.asm b/src/home/map.asm index b00d456..4609b46 100644 --- a/src/home/map.asm +++ b/src/home/map.asm @@ -141,7 +141,7 @@ GameEvent_ContinueDuel: ret GameEvent_Credits: - farcall Credits_1d6ad + farcall PlayCreditsSequence or a ret diff --git a/src/layout.link b/src/layout.link index e731983..40cf56c 100644 --- a/src/layout.link +++ b/src/layout.link @@ -46,7 +46,7 @@ ROMX $05 ROMX $06 "Menus 2" "Effect Commands" - "Animations Engine" + "Animation Commands" "IR Communications Core" "Sprite Animations VBlank" "Starter Deck" @@ -58,6 +58,10 @@ ROMX $06 "Auto Deck Machines" ROMX $07 "Bank 7" + "Duel Animations" + "Start Menu" + "Intro Sequence" + "Unused Copyright" "Credits Sequence" "Booster Packs" ROMX $08 diff --git a/src/macros/intro_sequence.asm b/src/macros/intro_sequence.asm new file mode 100644 index 0000000..8c92b26 --- /dev/null +++ b/src/macros/intro_sequence.asm @@ -0,0 +1,63 @@ +intro_seq_wait_orbs_animation: MACRO + dw IntroSequenceCmd_WaitOrbsAnimation +ENDM + +; argument = frames to delay +intro_seq_wait: MACRO + dw IntroSequenceCmd_Wait + db \1 +ENDM + +; argument = list of animations to set +intro_seq_set_orbs_animations: MACRO + dw IntroSequenceCmd_SetOrbsAnimations + dw \1 +ENDM + +; argument = list of coordinates to set +intro_seq_set_orbs_coordinates: MACRO + dw IntroSequenceCmd_SetOrbsCoordinates + dw \1 +ENDM + +intro_seq_play_title_screen_music: MACRO + dw IntroSequenceCmd_PlayTitleScreenMusic +ENDM + +intro_seq_wait_sfx: MACRO + dw IntroSequenceCmd_WaitSFX +ENDM + +; argument = SFX to play +intro_seq_play_sfx: MACRO + dw IntroSequenceCmd_PlaySFX + db \1 +ENDM + +intro_seq_fade_in: MACRO + dw IntroSequenceCmd_FadeIn +ENDM + +intro_seq_fade_out: MACRO + dw IntroSequenceCmd_FadeOut +ENDM + +intro_seq_load_charizard_scene: MACRO + dw IntroSequenceCmd_LoadCharizardScene +ENDM + +intro_seq_load_scyther_scene: MACRO + dw IntroSequenceCmd_LoadScytherScene +ENDM + +intro_seq_load_aerodactyl_scene: MACRO + dw IntroSequenceCmd_LoadAerodactylScene +ENDM + +intro_seq_load_title_screen_scene: MACRO + dw IntroSequenceCmd_LoadTitleScreenScene +ENDM + +intro_seq_end: MACRO + intro_seq_wait $ff +ENDM diff --git a/src/macros/opening_sequence.asm b/src/macros/opening_sequence.asm deleted file mode 100644 index b864c3c..0000000 --- a/src/macros/opening_sequence.asm +++ /dev/null @@ -1,63 +0,0 @@ -opening_seq_wait_orbs_animation: MACRO - dw OpeningSequenceCmd_WaitOrbsAnimation -ENDM - -; argument = frames to delay -opening_seq_wait: MACRO - dw OpeningSequenceCmd_Wait - db \1 -ENDM - -; argument = list of animations to set -opening_seq_set_orbs_animations: MACRO - dw OpeningSequenceCmd_SetOrbsAnimations - dw \1 -ENDM - -; argument = list of coordinates to set -opening_seq_set_orbs_coordinates: MACRO - dw OpeningSequenceCmd_SetOrbsCoordinates - dw \1 -ENDM - -opening_seq_play_title_screen_music: MACRO - dw OpeningSequenceCmd_PlayTitleScreenMusic -ENDM - -opening_seq_wait_sfx: MACRO - dw OpeningSequenceCmd_WaitSFX -ENDM - -; argument = SFX to play -opening_seq_play_sfx: MACRO - dw OpeningSequenceCmd_PlaySFX - db \1 -ENDM - -opening_seq_fade_in: MACRO - dw OpeningSequenceCmd_FadeIn -ENDM - -opening_seq_fade_out: MACRO - dw OpeningSequenceCmd_FadeOut -ENDM - -opening_seq_load_charizard_scene: MACRO - dw OpeningSequenceCmd_LoadCharizardScene -ENDM - -opening_seq_load_scyther_scene: MACRO - dw OpeningSequenceCmd_LoadScytherScene -ENDM - -opening_seq_load_aerodactyl_scene: MACRO - dw OpeningSequenceCmd_LoadAerodactylScene -ENDM - -opening_seq_load_title_screen_scene: MACRO - dw OpeningSequenceCmd_LoadTitleScreenScene -ENDM - -opening_seq_end: MACRO - opening_seq_wait $ff -ENDM diff --git a/src/main.asm b/src/main.asm index 55df597..f759ba1 100644 --- a/src/main.asm +++ b/src/main.asm @@ -40,8 +40,8 @@ INCLUDE "engine/menus/unknown.asm" SECTION "Effect Commands", ROMX INCLUDE "engine/duel/effect_commands.asm" -SECTION "Animations Engine", ROMX -INCLUDE "engine/duel/animations.asm" +SECTION "Animation Commands", ROMX +INCLUDE "engine/duel/animations/commands.asm" SECTION "IR Communications Core", ROMX INCLUDE "engine/link/ir_core.asm" @@ -76,9 +76,25 @@ INCLUDE "engine/auto_deck_machines.asm" SECTION "Bank 7", ROMX INCLUDE "engine/bank07.asm" +SECTION "Duel Animations", ROMX +INCLUDE "engine/duel/animations/core.asm" +INCLUDE "engine/duel/animations/screen_effects.asm" +INCLUDE "data/duel/animations/duel_animations.asm" + +SECTION "Start Menu", ROMX +INCLUDE "engine/menus/start.asm" + +SECTION "Intro Sequence", ROMX +INCLUDE "engine/intro.asm" +INCLUDE "engine/sequences/intro_sequence_commands.asm" + +SECTION "Unused Copyright", ROMX +INCLUDE "engine/unused_copyright.asm" + SECTION "Credits Sequence", ROMX +INCLUDE "engine/credits.asm" INCLUDE "engine/sequences/credits_sequence_commands.asm" -INCLUDE "data/sequences/credits_sequence.asm" +INCLUDE "data/sequences/credits.asm" SECTION "Booster Packs", ROMX INCLUDE "engine/booster_packs.asm" diff --git a/src/wram.asm b/src/wram.asm index 4ee7c73..0807481 100644 --- a/src/wram.asm +++ b/src/wram.asm @@ -3113,7 +3113,7 @@ wTitleScreenSprites:: ; d629 ds $1 ; pointer to commands used by opening and credits sequence -; (see OpeningSequence and CreditsSequence) +; (see IntroSequence and CreditsSequence) wSequenceCmdPtr:: ; d631 ds $2 @@ -3123,7 +3123,7 @@ wSequenceCmdPtr:: ; d631 wSequenceDelay:: ; d633 ds $1 -wOpeningSequencePalsNeedUpdate:: ; d634 +wIntroSequencePalsNeedUpdate:: ; d634 ds $1 wd635:: ; d635 -- cgit v1.2.3 From a360facb6e51cf4f39e6c544dcddb86c79713a07 Mon Sep 17 00:00:00 2001 From: ElectroDeoxys Date: Tue, 28 Sep 2021 15:32:05 +0100 Subject: Split graphics data from bank 20 --- src/data/maps/tilemaps.asm | 371 ++++++++++ src/engine/bank20.asm | 1205 ++------------------------------- src/engine/gfx/gfx_table_pointers.asm | 6 + src/engine/gfx/palettes.asm | 170 +++++ src/engine/gfx/sprite_animations.asm | 224 ++++++ src/engine/gfx/sprites.asm | 122 ++++ src/engine/gfx/tilemaps.asm | 114 ++++ src/engine/gfx/tilesets.asm | 95 +++ src/gfx.asm | 1026 ++++++++++++++-------------- src/layout.link | 1 + src/main.asm | 9 + 11 files changed, 1671 insertions(+), 1672 deletions(-) create mode 100644 src/data/maps/tilemaps.asm create mode 100644 src/engine/gfx/gfx_table_pointers.asm create mode 100644 src/engine/gfx/palettes.asm create mode 100644 src/engine/gfx/sprite_animations.asm create mode 100644 src/engine/gfx/sprites.asm create mode 100644 src/engine/gfx/tilemaps.asm create mode 100644 src/engine/gfx/tilesets.asm diff --git a/src/data/maps/tilemaps.asm b/src/data/maps/tilemaps.asm new file mode 100644 index 0000000..bf66636 --- /dev/null +++ b/src/data/maps/tilemaps.asm @@ -0,0 +1,371 @@ +OverworldMapTilemap:: + db $14 ; width + db $12 ; height + dw NULL + db FALSE ; cgb mode + INCBIN "data/maps/tiles/overworld_map.bin" + +OverworldMapCGBTilemap:: + db $14 ; width + db $12 ; height + dw NULL + db TRUE ; cgb mode + INCBIN "data/maps/tiles/overworld_map_cgb.bin" + +MasonLaboratoryTilemap:: + db $1c ; width + db $1e ; height + dw MasonLaboratoryPermissions + db FALSE ; cgb mode + INCBIN "data/maps/tiles/mason_laboratory.bin" +MasonLaboratoryPermissions: + INCBIN "data/maps/permissions/mason_laboratory.bin" + +MasonLaboratoryCGBTilemap:: + db $1c ; width + db $1e ; height + dw MasonLaboratoryCGBPermissions + db TRUE ; cgb mode + INCBIN "data/maps/tiles/mason_laboratory_cgb.bin" +MasonLaboratoryCGBPermissions: + INCBIN "data/maps/permissions/mason_laboratory_cgb.bin" + +ChallengeMachineMapEventTilemap:: + db $04 ; width + db $06 ; height + dw ChallengeMachineMapEventPermissions + db FALSE ; cgb mode + INCBIN "data/maps/tiles/challenge_machine_map_event.bin" +ChallengeMachineMapEventPermissions: + INCBIN "data/maps/permissions/challenge_machine_map_event.bin" + +ChallengeMachineMapEventCGBTilemap:: + db $04 ; width + db $06 ; height + dw ChallengeMachineMapEventCGBPermissions + db TRUE ; cgb mode + INCBIN "data/maps/tiles/challenge_machine_map_event_cgb.bin" +ChallengeMachineMapEventCGBPermissions: + INCBIN "data/maps/permissions/challenge_machine_map_event_cgb.bin" + +DeckMachineRoomTilemap:: + db $18 ; width + db $1e ; height + dw DeckMachineRoomPermissions + db FALSE ; cgb mode + INCBIN "data/maps/tiles/deck_machine_room.bin" +DeckMachineRoomPermissions: + INCBIN "data/maps/permissions/deck_machine_room.bin" + +DeckMachineRoomCGBTilemap:: + db $18 ; width + db $1e ; height + dw DeckMachineRoomCGBPermissions + db TRUE ; cgb mode + INCBIN "data/maps/tiles/deck_machine_room_cgb.bin" +DeckMachineRoomCGBPermissions: + INCBIN "data/maps/permissions/deck_machine_room_cgb.bin" + +DeckMachineMapEventTilemap:: + db $04 ; width + db $01 ; height + dw DeckMachineMapEventPermissions + db FALSE ; cgb mode + INCBIN "data/maps/tiles/deck_machine_map_event.bin" +DeckMachineMapEventPermissions: + INCBIN "data/maps/permissions/deck_machine_map_event.bin" + +DeckMachineMapEventCGBTilemap:: + db $04 ; width + db $01 ; height + dw DeckMachineMapEventCGBPermissions + db TRUE ; cgb mode + INCBIN "data/maps/tiles/deck_machine_map_event_cgb.bin" +DeckMachineMapEventCGBPermissions: + INCBIN "data/maps/permissions/deck_machine_map_event_cgb.bin" + +IshiharaTilemap:: + db $14 ; width + db $18 ; height + dw IshiharaPermissions + db FALSE ; cgb mode + INCBIN "data/maps/tiles/ishihara.bin" +IshiharaPermissions: + INCBIN "data/maps/permissions/ishihara.bin" + +IshiharaCGBTilemap:: + db $14 ; width + db $18 ; height + dw IshiharaCGBPermissions + db TRUE ; cgb mode + INCBIN "data/maps/tiles/ishihara_cgb.bin" +IshiharaCGBPermissions: + INCBIN "data/maps/permissions/ishihara_cgb.bin" + +FightingClubEntranceTilemap:: + db $14 ; width + db $12 ; height + dw FightingClubEntrancePermissions + db FALSE ; cgb mode + INCBIN "data/maps/tiles/fighting_club_entrance.bin" +FightingClubEntrancePermissions: + INCBIN "data/maps/permissions/fighting_club_entrance.bin" + +FightingClubEntranceCGBTilemap:: + db $14 ; width + db $12 ; height + dw FightingClubEntranceCGBPermissions + db TRUE ; cgb mode + INCBIN "data/maps/tiles/fighting_club_entrance_cgb.bin" +FightingClubEntranceCGBPermissions: + INCBIN "data/maps/permissions/fighting_club_entrance_cgb.bin" + +RockClubEntranceTilemap:: + db $14 ; width + db $12 ; height + dw RockClubEntrancePermissions + db FALSE ; cgb mode + INCBIN "data/maps/tiles/rock_club_entrance.bin" +RockClubEntrancePermissions: + INCBIN "data/maps/permissions/rock_club_entrance.bin" + +RockClubEntranceCGBTilemap:: + db $14 ; width + db $12 ; height + dw RockClubEntranceCGBPermissions + db TRUE ; cgb mode + INCBIN "data/maps/tiles/rock_club_entrance_cgb.bin" +RockClubEntranceCGBPermissions: + INCBIN "data/maps/permissions/rock_club_entrance_cgb.bin" + +WaterClubEntranceTilemap:: + db $14 ; width + db $12 ; height + dw WaterClubEntrancePermissions + db FALSE ; cgb mode + INCBIN "data/maps/tiles/water_club_entrance.bin" +WaterClubEntrancePermissions: + INCBIN "data/maps/permissions/water_club_entrance.bin" + +WaterClubEntranceCGBTilemap:: + db $14 ; width + db $12 ; height + dw WaterClubEntranceCGBPermissions + db TRUE ; cgb mode + INCBIN "data/maps/tiles/water_club_entrance_cgb.bin" +WaterClubEntranceCGBPermissions: + INCBIN "data/maps/permissions/water_club_entrance_cgb.bin" + +LightningClubEntranceTilemap:: + db $14 ; width + db $12 ; height + dw LightningClubEntrancePermissions + db FALSE ; cgb mode + INCBIN "data/maps/tiles/lightning_club_entrance.bin" +LightningClubEntrancePermissions: + INCBIN "data/maps/permissions/lightning_club_entrance.bin" + +LightningClubEntranceCGBTilemap:: + db $14 ; width + db $12 ; height + dw LightningClubEntranceCGBPermissions + db TRUE ; cgb mode + INCBIN "data/maps/tiles/lightning_club_entrance_cgb.bin" +LightningClubEntranceCGBPermissions: + INCBIN "data/maps/permissions/lightning_club_entrance_cgb.bin" + +GrassClubEntranceTilemap:: + db $14 ; width + db $12 ; height + dw GrassClubEntrancePermissions + db FALSE ; cgb mode + INCBIN "data/maps/tiles/grass_club_entrance.bin" +GrassClubEntrancePermissions: + INCBIN "data/maps/permissions/grass_club_entrance.bin" + +GrassClubEntranceCGBTilemap:: + db $14 ; width + db $12 ; height + dw GrassClubEntranceCGBPermissions + db TRUE ; cgb mode + INCBIN "data/maps/tiles/grass_club_entrance_cgb.bin" +GrassClubEntranceCGBPermissions: + INCBIN "data/maps/permissions/grass_club_entrance_cgb.bin" + +PsychicClubEntranceTilemap:: + db $14 ; width + db $12 ; height + dw PsychicClubEntrancePermissions + db FALSE ; cgb mode + INCBIN "data/maps/tiles/psychic_club_entrance.bin" +PsychicClubEntrancePermissions: + INCBIN "data/maps/permissions/psychic_club_entrance.bin" + +PsychicClubEntranceCGBTilemap:: + db $14 ; width + db $12 ; height + dw PsychicClubEntranceCGBPermissions + db TRUE ; cgb mode + INCBIN "data/maps/tiles/psychic_club_entrance_cgb.bin" +PsychicClubEntranceCGBPermissions: + INCBIN "data/maps/permissions/psychic_club_entrance_cgb.bin" + +ScienceClubEntranceTilemap:: + db $14 ; width + db $12 ; height + dw ScienceClubEntrancePermissions + db FALSE ; cgb mode + INCBIN "data/maps/tiles/science_club_entrance.bin" +ScienceClubEntrancePermissions: + INCBIN "data/maps/permissions/science_club_entrance.bin" + +ScienceClubEntranceCGBTilemap:: + db $14 ; width + db $12 ; height + dw ScienceClubEntranceCGBPermissions + db TRUE ; cgb mode + INCBIN "data/maps/tiles/science_club_entrance_cgb.bin" +ScienceClubEntranceCGBPermissions: + INCBIN "data/maps/permissions/science_club_entrance_cgb.bin" + +FireClubEntranceTilemap:: + db $14 ; width + db $12 ; height + dw FireClubEntrancePermissions + db FALSE ; cgb mode + INCBIN "data/maps/tiles/fire_club_entrance.bin" +FireClubEntrancePermissions: + INCBIN "data/maps/permissions/fire_club_entrance.bin" + +FireClubEntranceCGBTilemap:: + db $14 ; width + db $12 ; height + dw FireClubEntranceCGBPermissions + db TRUE ; cgb mode + INCBIN "data/maps/tiles/fire_club_entrance_cgb.bin" +FireClubEntranceCGBPermissions: + INCBIN "data/maps/permissions/fire_club_entrance_cgb.bin" + +ChallengeHallEntranceTilemap:: + db $14 ; width + db $12 ; height + dw ChallengeHallEntrancePermissions + db FALSE ; cgb mode + INCBIN "data/maps/tiles/challenge_hall_entrance.bin" +ChallengeHallEntrancePermissions: + INCBIN "data/maps/permissions/challenge_hall_entrance.bin" + +ChallengeHallEntranceCGBTilemap:: + db $14 ; width + db $12 ; height + dw ChallengeHallEntranceCGBPermissions + db TRUE ; cgb mode + INCBIN "data/maps/tiles/challenge_hall_entrance_cgb.bin" +ChallengeHallEntranceCGBPermissions: + INCBIN "data/maps/permissions/challenge_hall_entrance_cgb.bin" + +ClubLobbyTilemap:: + db $1c ; width + db $1a ; height + dw ClubLobbyPermissions + db FALSE ; cgb mode + INCBIN "data/maps/tiles/club_lobby.bin" +ClubLobbyPermissions: + INCBIN "data/maps/permissions/club_lobby.bin" + +ClubLobbyCGBTilemap:: + db $1c ; width + db $1a ; height + dw ClubLobbyCGBPermissions + db TRUE ; cgb mode + INCBIN "data/maps/tiles/club_lobby_cgb.bin" +ClubLobbyCGBPermissions: + INCBIN "data/maps/permissions/club_lobby_cgb.bin" + +FightingClubTilemap:: + db $18 ; width + db $12 ; height + dw FightingClubPermissions + db FALSE ; cgb mode + INCBIN "data/maps/tiles/fighting_club.bin" +FightingClubPermissions: + INCBIN "data/maps/permissions/fighting_club.bin" + +FightingClubCGBTilemap:: + db $18 ; width + db $12 ; height + dw FightingClubCGBPermissions + db TRUE ; cgb mode + INCBIN "data/maps/tiles/fighting_club_cgb.bin" +FightingClubCGBPermissions: + INCBIN "data/maps/permissions/fighting_club_cgb.bin" + +RockClubTilemap:: + db $1c ; width + db $1e ; height + dw RockClubPermissions + db FALSE ; cgb mode + INCBIN "data/maps/tiles/rock_club.bin" +RockClubPermissions: + INCBIN "data/maps/permissions/rock_club.bin" + +RockClubCGBTilemap:: + db $1c ; width + db $1e ; height + dw RockClubCGBPermissions + db TRUE ; cgb mode + INCBIN "data/maps/tiles/rock_club_cgb.bin" +RockClubCGBPermissions: + INCBIN "data/maps/permissions/rock_club_cgb.bin" + +PokemonDomeDoorMapEventTilemap:: + db $04 ; width + db $03 ; height + dw PokemonDomeDoorMapEventPermissions + db FALSE ; cgb mode + INCBIN "data/maps/tiles/pokemon_dome_door_map_event.bin" +PokemonDomeDoorMapEventPermissions: + INCBIN "data/maps/permissions/pokemon_dome_door_map_event.bin" + +PokemonDomeDoorMapEventCGBTilemap:: + db $04 ; width + db $03 ; height + dw PokemonDomeDoorMapEventCGBPermissions + db TRUE ; cgb mode + INCBIN "data/maps/tiles/pokemon_dome_door_map_event_cgb.bin" +PokemonDomeDoorMapEventCGBPermissions: + INCBIN "data/maps/permissions/pokemon_dome_door_map_event_cgb.bin" + +HallOfHonorDoorMapEventTilemap:: + db $04 ; width + db $03 ; height + dw HallOfHonorDoorMapEventPermissions + db FALSE ; cgb mode + INCBIN "data/maps/tiles/hall_of_honor_door_map_event.bin" +HallOfHonorDoorMapEventPermissions: + INCBIN "data/maps/permissions/hall_of_honor_door_map_event.bin" + +HallOfHonorDoorMapEventCGBTilemap:: + db $04 ; width + db $03 ; height + dw HallOfHonorDoorMapEventCGBPermissions + db TRUE ; cgb mode + INCBIN "data/maps/tiles/hall_of_honor_door_map_event_cgb.bin" +HallOfHonorDoorMapEventCGBPermissions: + INCBIN "data/maps/permissions/hall_of_honor_door_map_event_cgb.bin" + +GrassMedalTilemap:: + db $03 ; width + db $03 ; height + dw NULL + db TRUE ; cgb mode + INCBIN "data/maps/tiles/grass_medal.bin" + +AnimData1:: + frame_table AnimFrameTable0 + frame_data 3, 16, 0, 0 + frame_data 4, 16, 0, 0 + frame_data 0, 0, 0, 0 + +Palette110:: + db $00, $00 diff --git a/src/engine/bank20.asm b/src/engine/bank20.asm index a8c03cc..bc71842 100644 --- a/src/engine/bank20.asm +++ b/src/engine/bank20.asm @@ -2,7 +2,7 @@ ; according to its Map Header configurations ; if it's the Overworld Map, also prints the map name ; and sets up the volcano animation -LoadMapGfxAndPermissions: ; 80000 (20:4000) +LoadMapGfxAndPermissions: call ClearSRAMBGMaps xor a ld [wTextBoxFrameType], a @@ -20,7 +20,7 @@ LoadMapGfxAndPermissions: ; 80000 (20:4000) ; reloads the map tiles and permissions ; after a textbox has been closed -ReloadMapAfterTextClose: ; 80028 (20:4028) +ReloadMapAfterTextClose: call ClearSRAMBGMaps lb bc, 0, 0 call LoadTilemap_ToSRAM @@ -29,7 +29,7 @@ ReloadMapAfterTextClose: ; 80028 (20:4028) farcall Func_c3ee ret -LoadMapTilesAndPals: ; 8003d (20:403d) +LoadMapTilesAndPals: farcall LoadMapHeader farcall SetSGB2AndSGB3MapPalette lb bc, 0, 0 @@ -58,14 +58,14 @@ LoadMapTilesAndPals: ; 8003d (20:403d) ; loads the BG map corresponding to wCurTilemap to SRAM ; bc = starting coordinates -LoadTilemap_ToSRAM: ; 80077 (20:4077) +LoadTilemap_ToSRAM: ld a, TRUE ld [wWriteBGMapToSRAM], a jr LoadTilemap ; loads the BG map corresponding to wCurTilemap to VRAM ; bc = starting coordinates -LoadTilemap_ToVRAM: ; 8007e (20:407e) +LoadTilemap_ToVRAM: xor a ; FALSE ld [wWriteBGMapToSRAM], a ; fallthrough @@ -74,7 +74,7 @@ LoadTilemap_ToVRAM: ; 8007e (20:407e) ; either loads them in VRAM or SRAM, ; depending on wWriteBGMapToSRAM ; bc = starting coordinates -LoadTilemap: ; 80082 (20:4082) +LoadTilemap: push hl push bc push de @@ -213,7 +213,7 @@ LoadTilemap: ; 80082 (20:4082) pop hl ret -Func_80148: ; 80148 (20:4148) +Func_80148: ld a, [wd291] or a ret z @@ -255,7 +255,7 @@ Func_80148: ; 80148 (20:4148) ; if SRAM is the target address to copy, ; copies data to sGfxBuffer0 or sGfxBuffer1 ; for VRAM0 or VRAM1 respectively -CopyBGDataToVRAMOrSRAM: ; 8016e (20:416e) +CopyBGDataToVRAMOrSRAM: ld a, [wWriteBGMapToSRAM] or a jp z, SafeCopyDataHLtoDE @@ -296,7 +296,7 @@ CopyBGDataToVRAMOrSRAM: ; 8016e (20:416e) ; safely copies $20 bytes at a time ; sGfxBuffer0 -> v0BGMap0 ; sGfxBuffer1 -> v0BGMap1 (if in CGB) -SafelyCopyBGMapFromSRAMToVRAM: ; 801a1 (20:41a1) +SafelyCopyBGMapFromSRAMToVRAM: push hl push bc push de @@ -348,7 +348,7 @@ SafelyCopyBGMapFromSRAMToVRAM: ; 801a1 (20:41a1) ret ; clears sGfxBuffer0 and sGfxBuffer1 -ClearSRAMBGMaps: ; 801f1 (20:41f1) +ClearSRAMBGMaps: push hl push bc ldh a, [hBankSRAM] @@ -368,10 +368,10 @@ ClearSRAMBGMaps: ; 801f1 (20:41f1) ; l - map data offset (0,2,4,6,8 for banks 0,1,2,3,4) ; a - map index (inside of the given bank) -GetMapDataPointer: ; 8020f (20:420f) +GetMapDataPointer: push bc push af - ld bc, MapDataPointers + ld bc, GfxTablePointers ld h, $0 add hl, bc ld c, [hl] @@ -389,18 +389,17 @@ GetMapDataPointer: ; 8020f (20:420f) ret ; Loads a pointer from [hl] to wTempPointer. Adds the graphics bank offset ($20) -LoadGraphicsPointerFromHL: ; 80229 (20:4229) +LoadGraphicsPointerFromHL: ld a, [hli] ld [wTempPointer], a ld a, [hli] ld [wTempPointer + 1], a ld a, [hli] - add BANK(MapDataPointers) + add BANK(GfxTablePointers) ld [wTempPointerBank], a ret -; unreferenced? -Func_80238: ; 80238 (20:4238) +Func_80238: ; unreferenced push hl ld l, $2 ; Tilesets ld a, [wCurTileset] @@ -423,7 +422,7 @@ Func_80238: ; 80238 (20:4238) ; a = sprite index within the data map ; output: ; a = number of tiles in sprite -Func_8025b: ; 8025b (20:425b) +Func_8025b: push hl ld l, $4 ; Sprites call GetMapDataPointer @@ -440,17 +439,17 @@ Func_8025b: ; 8025b (20:425b) ; loads graphics data pointed by wTempPointer in wTempPointerBank ; to the VRAM bank according to wd4cb, in address pointed by wVRAMPointer -LoadGfxDataFromTempPointerToVRAMBank: ; 80274 (20:4274) +LoadGfxDataFromTempPointerToVRAMBank: call GetTileOffsetPointerAndSwitchVRAM jr LoadGfxDataFromTempPointer -LoadGfxDataFromTempPointerToVRAMBank_Tiles0ToTiles2: ; 80279 (20:4279) +LoadGfxDataFromTempPointerToVRAMBank_Tiles0ToTiles2: call GetTileOffsetPointerAndSwitchVRAM_Tiles0ToTiles2 ; fallthrough ; loads graphics data pointed by wTempPointer in wTempPointerBank ; to wVRAMPointer -LoadGfxDataFromTempPointer: ; 8027c (20:427c) +LoadGfxDataFromTempPointer: push hl push bc push de @@ -478,7 +477,7 @@ LoadGfxDataFromTempPointer: ; 8027c (20:427c) ; convert wVRAMTileOffset to address in VRAM ; and stores it in wVRAMPointer ; switches VRAM according to wd4cb -GetTileOffsetPointerAndSwitchVRAM: ; 8029f (20:429f) +GetTileOffsetPointerAndSwitchVRAM: ; address of the tile offset is wVRAMTileOffset * $10 + $8000 ld a, [wVRAMTileOffset] swap a @@ -502,7 +501,7 @@ GetTileOffsetPointerAndSwitchVRAM: ; 8029f (20:429f) ; switches VRAM according to wd4cb ; then changes wVRAMPointer such that ; addresses to Tiles0 is changed to Tiles2 -GetTileOffsetPointerAndSwitchVRAM_Tiles0ToTiles2: ; 802bb (20:42bb) +GetTileOffsetPointerAndSwitchVRAM_Tiles0ToTiles2: ld a, [wVRAMTileOffset] push af xor $80 ; toggle top bit @@ -516,7 +515,7 @@ GetTileOffsetPointerAndSwitchVRAM_Tiles0ToTiles2: ; 802bb (20:42bb) ret ; loads tileset gfx to VRAM corresponding to wCurTileset -LoadTilesetGfx: ; 802d4 (20:42d4) +LoadTilesetGfx: push hl ld l, $02 ; Tilesets ld a, [wCurTileset] @@ -679,7 +678,7 @@ LoadTilesetGfx: ; 802d4 (20:42d4) ret ; gets pointer to BG map with ID from wCurTilemap -Func_803b9: ; 803b9 (20:43b9) +Func_803b9: ld l, $00 ; Tilemaps ld a, [wCurTilemap] call GetMapDataPointer @@ -691,7 +690,7 @@ Func_803b9: ; 803b9 (20:43b9) ; sets BGP in wLoadedPalData (if any) ; then loads the rest of the palette data ; a = palette index to load -SetBGPAndLoadedPal: ; 803c9 (20:43c9) +SetBGPAndLoadedPal: push hl push bc push de @@ -724,7 +723,7 @@ SetBGPAndLoadedPal: ; 803c9 (20:43c9) ; b = palette index ; c = palette size ; hl = palette data to copy -LoadPaletteDataFromHL: ; 803ec (20:43ec) +LoadPaletteDataFromHL: push hl push bc push de @@ -768,7 +767,7 @@ LoadPaletteDataFromHL: ; 803ec (20:43ec) ret ; loads palette index a -LoadPaletteData: ; 80418 (20:4418) +LoadPaletteData: push hl push bc push de @@ -824,7 +823,7 @@ LoadPaletteData: ; 80418 (20:4418) ret ; copies palette data of index in a to wLoadedPalData -LoadPaletteDataToBuffer: ; 80456 (20:4456) +LoadPaletteDataToBuffer: push hl push bc push de @@ -853,14 +852,14 @@ LoadPaletteDataToBuffer: ; 80456 (20:4456) pop hl ret -ClearNumLoadedFramesetSubgroups: ; 8047b (20:447b) +ClearNumLoadedFramesetSubgroups: xor a ld [wNumLoadedFramesetSubgroups], a ret ; for the current map, process the animation ; data of its corresponding OW tiles -DoMapOWFrame: ; 80480 (20:4480) +DoMapOWFrame: push hl push bc ld a, [wCurMap] @@ -887,7 +886,7 @@ DoMapOWFrame: ; 80480 (20:4480) ret ; processes the OW frameset pointed by hl -ProcessOWFrameset: ; 804a2 (20:44a2) +ProcessOWFrameset: push hl push bc ld a, l @@ -920,7 +919,7 @@ ProcessOWFrameset: ; 804a2 (20:44a2) ; for each of the loaded frameset subgroups ; load their tiles and advance their durations -DoLoadedFramesetSubgroupsFrame: ; 804d8 (20:44d8) +DoLoadedFramesetSubgroupsFrame: ld a, [wNumLoadedFramesetSubgroups] or a ret z @@ -941,7 +940,7 @@ DoLoadedFramesetSubgroupsFrame: ; 804d8 (20:44d8) ; from subgroup in register c, get ; from OW frameset in hl its corresponding ; data offset and duration -GetOWFramesetSubgroupData: ; 804f3 (20:44f3) +GetOWFramesetSubgroupData: push hl push bc push hl @@ -965,7 +964,7 @@ GetOWFramesetSubgroupData: ; 804f3 (20:44f3) ; if wCurOWFrameDuration == 0, processes next frame for OW map ; by loading the tiles corresponding to current frame ; if wCurOWFrameDuration != 0, then simply decrements it and returns -LoadOWFrameTiles: ; 8050c (20:450c) +LoadOWFrameTiles: ld a, [wCurOWFrameDuration] or a jr z, .next_frame @@ -1083,7 +1082,7 @@ LoadOWFrameTiles: ; 8050c (20:450c) ret ; fills wOWFramesetSubgroups with $ff -ClearOWFramesetSubgroups: ; 8059a (20:459a) +ClearOWFramesetSubgroups: push hl push bc ld hl, wOWFramesetSubgroups @@ -1100,7 +1099,7 @@ ClearOWFramesetSubgroups: ; 8059a (20:459a) ; copies wOWFramesetSubgroups + 2*c ; to wCurOWFrameDataOffset and wCurOWFrameDuration ; also returns its current duration -LoadOWFramesetSubgroup: ; 805aa (20:45aa) +LoadOWFramesetSubgroup: push hl push bc ld hl, wOWFramesetSubgroups @@ -1119,7 +1118,7 @@ LoadOWFramesetSubgroup: ; 805aa (20:45aa) ; copies wCurOWFrameDataOffset and wCurOWFrameDuration ; to wOWFramesetSubgroups + 2*c -StoreOWFramesetSubgroup: ; 805c1 (20:45c1) +StoreOWFramesetSubgroup: push hl push bc ld hl, wOWFramesetSubgroups @@ -1137,7 +1136,7 @@ StoreOWFramesetSubgroup: ; 805c1 (20:45c1) INCLUDE "data/map_ow_framesets.asm" ; clears wOWMapEvents -Func_80b7a: ; 80b7a (20:4b7a) +Func_80b7a: push hl push bc ld c, $b @@ -1152,7 +1151,7 @@ Func_80b7a: ; 80b7a (20:4b7a) ret ; a = MAP_EVENT_* constant -Func_80b89: ; 80b89 (20:4b89) +Func_80b89: push hl push bc push af @@ -1173,14 +1172,14 @@ Func_80b89: ; 80b89 (20:4b89) pop hl ret -Func_80ba4: ; 80ba4 (20:4ba4) +Func_80ba4: push af xor a ld [wWriteBGMapToSRAM], a pop af ; fallthrough -Func_80baa: ; 80baa (20:4baa) +Func_80baa: push hl push bc push de @@ -1294,10 +1293,9 @@ Func_80baa: ; 80baa (20:4baa) .ChallengeMachine db $0a, $00, TILEMAP_CHALLENGE_MACHINE_MAP_EVENT, TILEMAP_CHALLENGE_MACHINE_MAP_EVENT_CGB - ret ; unreferenced stray ret? + ret ; stray ret -; unreferenced? -Func_80c64: ; 80c64 (20:4c64) +Func_80c64: ; unreferenced ld a, [wLineSeparation] push af ld a, $01 ; no line separator @@ -1346,9 +1344,8 @@ Func_80c64: ; 80c64 (20:4c64) db SYM_SPACE ; tile behind cursor dw NULL ; function pointer if non-0 -; unreferenced? ; fills Tiles0 with random bytes -Func_80cc3: ; 80cc3 (20:4cc3) +Func_80cc3: ; unreferenced call DisableLCD ld hl, v0Tiles0 ld bc, $800 @@ -1361,12 +1358,12 @@ Func_80cc3: ; 80cc3 (20:4cc3) jr nz, .loop ret -Func_80cd6: ; 80cd6 (20:4cd6) +Func_80cd6: ret ; seems to be used to look at each OW NPC sprites ; with functions to rotate NPC and animate them -Func_80cd7: ; 80cd7 (20:4cd7) +Func_80cd7: call DisableLCD call EmptyScreen call Func_3ca4 @@ -1557,1118 +1554,8 @@ Func_80cd7: ; 80cd7 (20:4cd7) db SPRITE_OW_GRANNY, SPRITE_ANIM_LIGHT_NPC_UP, SPRITE_ANIM_YELLOW_NPC_UP ; $2b db SPRITE_OW_AMY, SPRITE_ANIM_SGB_AMY_LAYING, SPRITE_ANIM_CGB_AMY_LAYING ; $2c -SpriteNullAnimationPointer: ; 80e5a (20:4e5a) +SpriteNullAnimationPointer: dw SpriteNullAnimationFrame SpriteNullAnimationFrame: db 0 - -; might be closer to "screen specific data" than map data -MapDataPointers: ; 80e5d (20:4e5d) - dw Tilemaps - dw Tilesets - dw Sprites - dw SpriteAnimations - dw Palettes - -; \1 = pointer -; \2 = tileset -tilemap: MACRO - dwb \1, BANK(\1) - BANK(Tilemaps) - db \2 -ENDM - -Tilemaps: ; 80e67 (20:4e67) - tilemap OverworldMapTilemap, TILESET_OVERWORLD_MAP ; TILEMAP_OVERWORLD_MAP - tilemap OverworldMapCGBTilemap, TILESET_OVERWORLD_MAP ; TILEMAP_OVERWORLD_MAP_CGB - tilemap MasonLaboratoryTilemap, TILESET_MASON_LABORATORY ; TILEMAP_MASON_LABORATORY - tilemap MasonLaboratoryCGBTilemap, TILESET_MASON_LABORATORY ; TILEMAP_MASON_LABORATORY_CGB - tilemap ChallengeMachineMapEventTilemap, TILESET_MASON_LABORATORY ; TILEMAP_CHALLENGE_MACHINE_MAP_EVENT - tilemap ChallengeMachineMapEventCGBTilemap,TILESET_MASON_LABORATORY ; TILEMAP_CHALLENGE_MACHINE_MAP_EVENT_CGB - tilemap DeckMachineRoomTilemap, TILESET_MASON_LABORATORY ; TILEMAP_DECK_MACHINE_ROOM - tilemap DeckMachineRoomCGBTilemap, TILESET_MASON_LABORATORY ; TILEMAP_DECK_MACHINE_ROOM_CGB - tilemap DeckMachineMapEventTilemap, TILESET_MASON_LABORATORY ; TILEMAP_DECK_MACHINE_MAP_EVENT - tilemap DeckMachineMapEventCGBTilemap, TILESET_MASON_LABORATORY ; TILEMAP_DECK_MACHINE_MAP_EVENT_CGB - tilemap IshiharaTilemap, TILESET_ISHIHARA ; TILEMAP_ISHIHARA - tilemap IshiharaCGBTilemap, TILESET_ISHIHARA ; TILEMAP_ISHIHARA_CGB - tilemap FightingClubEntranceTilemap, TILESET_CLUB_ENTRANCE ; TILEMAP_FIGHTING_CLUB_ENTRANCE - tilemap FightingClubEntranceCGBTilemap, TILESET_CLUB_ENTRANCE ; TILEMAP_FIGHTING_CLUB_ENTRANCE_CGB - tilemap RockClubEntranceTilemap, TILESET_CLUB_ENTRANCE ; TILEMAP_ROCK_CLUB_ENTRANCE - tilemap RockClubEntranceCGBTilemap, TILESET_CLUB_ENTRANCE ; TILEMAP_ROCK_CLUB_ENTRANCE_CGB - tilemap WaterClubEntranceTilemap, TILESET_CLUB_ENTRANCE ; TILEMAP_WATER_CLUB_ENTRANCE - tilemap WaterClubEntranceCGBTilemap, TILESET_CLUB_ENTRANCE ; TILEMAP_WATER_CLUB_ENTRANCE_CGB - tilemap LightningClubEntranceTilemap, TILESET_CLUB_ENTRANCE ; TILEMAP_LIGHTNING_CLUB_ENTRANCE - tilemap LightningClubEntranceCGBTilemap, TILESET_CLUB_ENTRANCE ; TILEMAP_LIGHTNING_CLUB_ENTRANCE_CGB - tilemap GrassClubEntranceTilemap, TILESET_CLUB_ENTRANCE ; TILEMAP_GRASS_CLUB_ENTRANCE - tilemap GrassClubEntranceCGBTilemap, TILESET_CLUB_ENTRANCE ; TILEMAP_GRASS_CLUB_ENTRANCE_CGB - tilemap PsychicClubEntranceTilemap, TILESET_CLUB_ENTRANCE ; TILEMAP_PSYCHIC_CLUB_ENTRANCE - tilemap PsychicClubEntranceCGBTilemap, TILESET_CLUB_ENTRANCE ; TILEMAP_PSYCHIC_CLUB_ENTRANCE_CGB - tilemap ScienceClubEntranceTilemap, TILESET_CLUB_ENTRANCE ; TILEMAP_SCIENCE_CLUB_ENTRANCE - tilemap ScienceClubEntranceCGBTilemap, TILESET_CLUB_ENTRANCE ; TILEMAP_SCIENCE_CLUB_ENTRANCE_CGB - tilemap FireClubEntranceTilemap, TILESET_CLUB_ENTRANCE ; TILEMAP_FIRE_CLUB_ENTRANCE - tilemap FireClubEntranceCGBTilemap, TILESET_CLUB_ENTRANCE ; TILEMAP_FIRE_CLUB_ENTRANCE_CGB - tilemap ChallengeHallEntranceTilemap, TILESET_CLUB_ENTRANCE ; TILEMAP_CHALLENGE_HALL_ENTRANCE - tilemap ChallengeHallEntranceCGBTilemap, TILESET_CLUB_ENTRANCE ; TILEMAP_CHALLENGE_HALL_ENTRANCE_CGB - tilemap ClubLobbyTilemap, TILESET_CLUB_LOBBY ; TILEMAP_CLUB_LOBBY - tilemap ClubLobbyCGBTilemap, TILESET_CLUB_LOBBY ; TILEMAP_CLUB_LOBBY_CGB - tilemap FightingClubTilemap, TILESET_FIGHTING_CLUB ; TILEMAP_FIGHTING_CLUB - tilemap FightingClubCGBTilemap, TILESET_FIGHTING_CLUB ; TILEMAP_FIGHTING_CLUB_CGB - tilemap RockClubTilemap, TILESET_ROCK_CLUB ; TILEMAP_ROCK_CLUB - tilemap RockClubCGBTilemap, TILESET_ROCK_CLUB ; TILEMAP_ROCK_CLUB_CGB - tilemap WaterClubTilemap, TILESET_WATER_CLUB ; TILEMAP_WATER_CLUB - tilemap WaterClubCGBTilemap, TILESET_WATER_CLUB ; TILEMAP_WATER_CLUB_CGB - tilemap LightningClubTilemap, TILESET_LIGHTNING_CLUB ; TILEMAP_LIGHTNING_CLUB - tilemap LightningClubCGBTilemap, TILESET_LIGHTNING_CLUB ; TILEMAP_LIGHTNING_CLUB_CGB - tilemap GrassClubTilemap, TILESET_GRASS_CLUB ; TILEMAP_GRASS_CLUB - tilemap GrassClubCGBTilemap, TILESET_GRASS_CLUB ; TILEMAP_GRASS_CLUB_CGB - tilemap PsychicClubTilemap, TILESET_PSYCHIC_CLUB ; TILEMAP_PSYCHIC_CLUB - tilemap PsychicClubCGBTilemap, TILESET_PSYCHIC_CLUB ; TILEMAP_PSYCHIC_CLUB_CGB - tilemap ScienceClubTilemap, TILESET_SCIENCE_CLUB ; TILEMAP_SCIENCE_CLUB - tilemap ScienceClubCGBTilemap, TILESET_SCIENCE_CLUB ; TILEMAP_SCIENCE_CLUB_CGB - tilemap FireClubTilemap, TILESET_FIRE_CLUB ; TILEMAP_FIRE_CLUB - tilemap FireClubCGBTilemap, TILESET_FIRE_CLUB ; TILEMAP_FIRE_CLUB_CGB - tilemap ChallengeHallTilemap, TILESET_CHALLENGE_HALL ; TILEMAP_CHALLENGE_HALL - tilemap ChallengeHallCGBTilemap, TILESET_CHALLENGE_HALL ; TILEMAP_CHALLENGE_HALL_CGB - tilemap PokemonDomeEntranceTilemap, TILESET_POKEMON_DOME_ENTRANCE ; TILEMAP_POKEMON_DOME_ENTRANCE - tilemap PokemonDomeEntranceCGBTilemap, TILESET_POKEMON_DOME_ENTRANCE ; TILEMAP_POKEMON_DOME_ENTRANCE_CGB - tilemap PokemonDomeDoorMapEventTilemap, TILESET_POKEMON_DOME_ENTRANCE ; TILEMAP_POKEMON_DOME_DOOR_MAP_EVENT - tilemap PokemonDomeDoorMapEventCGBTilemap, TILESET_POKEMON_DOME_ENTRANCE ; TILEMAP_POKEMON_DOME_DOOR_MAP_EVENT_CGB - tilemap PokemonDomeTilemap, TILESET_POKEMON_DOME ; TILEMAP_POKEMON_DOME - tilemap PokemonDomeCGBTilemap, TILESET_POKEMON_DOME ; TILEMAP_POKEMON_DOME_CGB - tilemap HallOfHonorDoorMapEventTilemap, TILESET_POKEMON_DOME ; TILEMAP_HALL_OF_HONOR_DOOR_MAP_EVENT - tilemap HallOfHonorDoorMapEventCGBTilemap, TILESET_POKEMON_DOME ; TILEMAP_HALL_OF_HONOR_DOOR_MAP_EVENT_CGB - tilemap HallOfHonorTilemap, TILESET_HALL_OF_HONOR ; TILEMAP_HALL_OF_HONOR - tilemap HallOfHonorCGBTilemap, TILESET_HALL_OF_HONOR ; TILEMAP_HALL_OF_HONOR_CGB - tilemap CardPopCGBTilemap, TILESET_CARD_POP ; TILEMAP_CARD_POP_CGB - tilemap CardPopTilemap, TILESET_CARD_POP ; TILEMAP_CARD_POP - tilemap GrassMedalTilemap, TILESET_MEDAL ; TILEMAP_GRASS_MEDAL - tilemap ScienceMedalTilemap, TILESET_MEDAL ; TILEMAP_SCIENCE_MEDAL - tilemap FireMedalTilemap, TILESET_MEDAL ; TILEMAP_FIRE_MEDAL - tilemap WaterMedalTilemap, TILESET_MEDAL ; TILEMAP_WATER_MEDAL - tilemap LightningMedalTilemap, TILESET_MEDAL ; TILEMAP_LIGHTNING_MEDAL - tilemap FightingMedalTilemap, TILESET_MEDAL ; TILEMAP_FIGHTING_MEDAL - tilemap RockMedalTilemap, TILESET_MEDAL ; TILEMAP_ROCK_MEDAL - tilemap PsychicMedalTilemap, TILESET_MEDAL ; TILEMAP_PSYCHIC_MEDAL - tilemap GameBoyLinkCGBTilemap, TILESET_GAMEBOY_LINK ; TILEMAP_GAMEBOY_LINK_CGB - tilemap GameBoyLinkTilemap, TILESET_GAMEBOY_LINK ; TILEMAP_GAMEBOY_LINK - tilemap GameBoyLinkConnectingCGBTilemap, TILESET_GAMEBOY_LINK ; TILEMAP_GAMEBOY_LINK_CONNECTING_CGB - tilemap GameBoyLinkConnectingTilemap, TILESET_GAMEBOY_LINK ; TILEMAP_GAMEBOY_LINK_CONNECTING - tilemap GameBoyPrinterCGBTilemap, TILESET_GAMEBOY_PRINTER ; TILEMAP_GAMEBOY_PRINTER_CGB - tilemap GameBoyPrinterTilemap, TILESET_GAMEBOY_PRINTER ; TILEMAP_GAMEBOY_PRINTER - tilemap ColosseumTilemap, TILESET_COLOSSEUM_1 ; TILEMAP_COLOSSEUM - tilemap ColosseumCGBTilemap, TILESET_COLOSSEUM_2 ; TILEMAP_COLOSSEUM_CGB - tilemap EvolutionTilemap, TILESET_EVOLUTION_1 ; TILEMAP_EVOLUTION - tilemap EvolutionCGBTilemap, TILESET_EVOLUTION_2 ; TILEMAP_EVOLUTION_CGB - tilemap MysteryTilemap, TILESET_MYSTERY_1 ; TILEMAP_MYSTERY - tilemap MysteryCGBTilemap, TILESET_MYSTERY_2 ; TILEMAP_MYSTERY_CGB - tilemap LaboratoryTilemap, TILESET_LABORATORY_1 ; TILEMAP_LABORATORY - tilemap LaboratoryCGBTilemap, TILESET_LABORATORY_2 ; TILEMAP_LABORATORY_CGB - tilemap CharizardIntroTilemap, TILESET_CHARIZARD_INTRO_1 ; TILEMAP_CHARIZARD_INTRO - tilemap CharizardIntroCGBTilemap, TILESET_CHARIZARD_INTRO_2 ; TILEMAP_CHARIZARD_INTRO_CGB - tilemap ScytherIntroTilemap, TILESET_SCYTHER_INTRO_1 ; TILEMAP_SCYTHER_INTRO - tilemap ScytherIntroCGBTilemap, TILESET_SCYTHER_INTRO_2 ; TILEMAP_SCYTHER_INTRO_CGB - tilemap AerodactylIntroTilemap, TILESET_AERODACTYL_INTRO_1 ; TILEMAP_AERODACTYL_INTRO - tilemap AerodactylIntroCGBTilemap, TILESET_AERODACTYL_INTRO_2 ; TILEMAP_AERODACTYL_INTRO_CGB - tilemap JapaneseTitleScreenTilemap, TILESET_JAPANESE_TITLE_SCREEN ; TILEMAP_JAPANESE_TITLE_SCREEN - tilemap JapaneseTitleScreenCGBTilemap, TILESET_JAPANESE_TITLE_SCREEN_CGB ; TILEMAP_JAPANESE_TITLE_SCREEN_CGB - tilemap SolidTiles1Tilemap, TILESET_SOLID_TILES_1 ; TILEMAP_SOLID_TILES_1 - tilemap SolidTiles2Tilemap, TILESET_SOLID_TILES_1 ; TILEMAP_SOLID_TILES_2 - tilemap SolidTiles3Tilemap, TILESET_SOLID_TILES_1 ; TILEMAP_SOLID_TILES_3 - tilemap JapaneseTitleScreen2Tilemap, TILESET_JAPANESE_TITLE_SCREEN_2 ; TILEMAP_JAPANESE_TITLE_SCREEN_2 - tilemap JapaneseTitleScreen2CGBTilemap, TILESET_JAPANESE_TITLE_SCREEN_2_CGB ; TILEMAP_JAPANESE_TITLE_SCREEN_2_CGB - tilemap SolidTiles4Tilemap, TILESET_SOLID_TILES_2 ; TILEMAP_SOLID_TILES_4 - tilemap PlayerTilemap, TILESET_PLAYER ; TILEMAP_PLAYER - tilemap OpponentTilemap, TILESET_RONALD ; TILEMAP_OPPONENT - tilemap TitleScreenTilemap, TILESET_TITLE_SCREEN ; TILEMAP_TITLE_SCREEN - tilemap TitleScreenCGBTilemap, TILESET_TITLE_SCREEN_CGB ; TILEMAP_TITLE_SCREEN_CGB - tilemap CopyrightTilemap, TILESET_COPYRIGHT ; TILEMAP_COPYRIGHT - tilemap CopyrightCGBTilemap, TILESET_COPYRIGHT ; TILEMAP_COPYRIGHT_CGB - tilemap NintendoTilemap, TILESET_NINTENDO ; TILEMAP_NINTENDO - tilemap CompaniesTilemap, TILESET_COMPANIES ; TILEMAP_COMPANIES - -; \1 = pointer -; \2 = number of tiles -tileset: MACRO - dwb \1, BANK(\1) - BANK(Tilesets) - db \2 -ENDM - -Tilesets: ; 8100f (20:500f) - tileset OverworldMapTiles, 193 ; TILESET_OVERWORLD_MAP - tileset MasonLaboratoryTilesetGfx, 151 ; TILESET_MASON_LABORATORY - tileset IshiharaTilesetGfx, 77 ; TILESET_ISHIHARA - tileset ClubEntranceTilesetGfx, 129 ; TILESET_CLUB_ENTRANCE - tileset ClubLobbyTilesetGfx, 120 ; TILESET_CLUB_LOBBY - tileset FightingClubTilesetGfx, 99 ; TILESET_FIGHTING_CLUB - tileset RockClubTilesetGfx, 60 ; TILESET_ROCK_CLUB - tileset WaterClubTilesetGfx, 161 ; TILESET_WATER_CLUB - tileset LightningClubTilesetGfx, 131 ; TILESET_LIGHTNING_CLUB - tileset GrassClubTilesetGfx, 87 ; TILESET_GRASS_CLUB - tileset PsychicClubTilesetGfx, 58 ; TILESET_PSYCHIC_CLUB - tileset ScienceClubTilesetGfx, 82 ; TILESET_SCIENCE_CLUB - tileset FireClubTilesetGfx, 87 ; TILESET_FIRE_CLUB - tileset ChallengeHallTilesetGfx, 157 ; TILESET_CHALLENGE_HALL - tileset PokemonDomeEntranceTilesetGfx, 78 ; TILESET_POKEMON_DOME_ENTRANCE - tileset PokemonDomeTilesetGfx, 207 ; TILESET_POKEMON_DOME - tileset HallOfHonorTilesetGfx, 121 ; TILESET_HALL_OF_HONOR - tileset CardPopGfx, 189 ; TILESET_CARD_POP - tileset MedalGfx, 72 ; TILESET_MEDAL - tileset GameBoyLinkGfx, 109 ; TILESET_GAMEBOY_LINK - tileset GameBoyPrinterGfx, 93 ; TILESET_GAMEBOY_PRINTER - tileset Colosseum1Gfx, 96 ; TILESET_COLOSSEUM_1 - tileset Colosseum2Gfx, 86 ; TILESET_COLOSSEUM_2 - tileset Evolution1Gfx, 96 ; TILESET_EVOLUTION_1 - tileset Evolution2Gfx, 86 ; TILESET_EVOLUTION_2 - tileset Mystery1Gfx, 96 ; TILESET_MYSTERY_1 - tileset Mystery2Gfx, 86 ; TILESET_MYSTERY_2 - tileset Laboratory1Gfx, 96 ; TILESET_LABORATORY_1 - tileset Laboratory2Gfx, 86 ; TILESET_LABORATORY_2 - tileset CharizardIntro1Gfx, 96 ; TILESET_CHARIZARD_INTRO_1 - tileset CharizardIntro2Gfx, 96 ; TILESET_CHARIZARD_INTRO_2 - tileset ScytherIntro1Gfx, 96 ; TILESET_SCYTHER_INTRO_1 - tileset ScytherIntro2Gfx, 96 ; TILESET_SCYTHER_INTRO_2 - tileset AerodactylIntro1Gfx, 96 ; TILESET_AERODACTYL_INTRO_1 - tileset AerodactylIntro2Gfx, 96 ; TILESET_AERODACTYL_INTRO_2 - tileset JapaneseTitleScreenGfx, 97 ; TILESET_JAPANESE_TITLE_SCREEN - tileset JapaneseTitleScreenCGBGfx, 97 ; TILESET_JAPANESE_TITLE_SCREEN_CGB - tileset SolidTiles1, 4 ; TILESET_SOLID_TILES_1 - tileset JapaneseTitleScreen2Gfx, 244 ; TILESET_JAPANESE_TITLE_SCREEN_2 - tileset JapaneseTitleScreen2CGBGfx, 59 ; TILESET_JAPANESE_TITLE_SCREEN_2_CGB - tileset SolidTiles2, 4 ; TILESET_SOLID_TILES_2 - tileset PlayerGfx, 36 ; TILESET_PLAYER - tileset RonaldGfx, 36 ; TILESET_RONALD - tileset TitleScreenGfx, 220 ; TILESET_TITLE_SCREEN - tileset TitleScreenCGBGfx, 212 ; TILESET_TITLE_SCREEN_CGB - tileset CopyrightGfx, 36 ; TILESET_COPYRIGHT - tileset NintendoGfx, 24 ; TILESET_NINTENDO - tileset CompaniesGfx, 49 ; TILESET_COMPANIES - tileset SamGfx, 36 ; TILESET_SAM - tileset ImakuniGfx, 36 ; TILESET_IMAKUNI - tileset NikkiGfx, 36 ; TILESET_NIKKI - tileset RickGfx, 36 ; TILESET_RICK - tileset KenGfx, 36 ; TILESET_KEN - tileset AmyGfx, 36 ; TILESET_AMY - tileset IsaacGfx, 36 ; TILESET_ISAAC - tileset MitchGfx, 36 ; TILESET_MITCH - tileset GeneGfx, 36 ; TILESET_GENE - tileset MurrayGfx, 36 ; TILESET_MURRAY - tileset CourtneyGfx, 36 ; TILESET_COURTNEY - tileset SteveGfx, 36 ; TILESET_STEVE - tileset JackGfx, 36 ; TILESET_JACK - tileset RodGfx, 36 ; TILESET_ROD - tileset JosephGfx, 36 ; TILESET_JOSEPH - tileset DavidGfx, 36 ; TILESET_DAVID - tileset ErikGfx, 36 ; TILESET_ERIK - tileset JohnGfx, 36 ; TILESET_JOHN - tileset AdamGfx, 36 ; TILESET_ADAM - tileset JonathanGfx, 36 ; TILESET_JONATHAN - tileset JoshuaGfx, 36 ; TILESET_JOSHUA - tileset NicholasGfx, 36 ; TILESET_NICHOLAS - tileset BrandonGfx, 36 ; TILESET_BRANDON - tileset MatthewGfx, 36 ; TILESET_MATTHEW - tileset RyanGfx, 36 ; TILESET_RYAN - tileset AndrewGfx, 36 ; TILESET_ANDREW - tileset ChrisGfx, 36 ; TILESET_CHRIS - tileset MichaelGfx, 36 ; TILESET_MICHAEL - tileset DanielGfx, 36 ; TILESET_DANIEL - tileset RobertGfx, 36 ; TILESET_ROBERT - tileset BrittanyGfx, 36 ; TILESET_BRITTANY - tileset KristinGfx, 36 ; TILESET_KRISTIN - tileset HeatherGfx, 36 ; TILESET_HEATHER - tileset SaraGfx, 36 ; TILESET_SARA - tileset AmandaGfx, 36 ; TILESET_AMANDA - tileset JenniferGfx, 36 ; TILESET_JENNIFER - tileset JessicaGfx, 36 ; TILESET_JESSICA - tileset StephanieGfx, 36 ; TILESET_STEPHANIE - tileset AaronGfx, 36 ; TILESET_AARON - -; \1 = gfx pointer -; \2 = number of tiles -gfx_pointer: MACRO - dwb \1, BANK(\1) - BANK(Sprites) - db \2 -ENDM - -Sprites: ; 8116b (20:516b) - gfx_pointer OWPlayerGfx, $14 ; SPRITE_OW_PLAYER - gfx_pointer OWRonaldGfx, $14 ; SPRITE_OW_RONALD - gfx_pointer OWDrMasonGfx, $14 ; SPRITE_OW_DRMASON - gfx_pointer OWIshiharaGfx, $14 ; SPRITE_OW_ISHIHARA - gfx_pointer OWImakuniGfx, $14 ; SPRITE_OW_IMAKUNI - gfx_pointer OWNikkiGfx, $14 ; SPRITE_OW_NIKKI - gfx_pointer OWRickGfx, $14 ; SPRITE_OW_RICK - gfx_pointer OWKenGfx, $14 ; SPRITE_OW_KEN - gfx_pointer OWAmyGfx, $1b ; SPRITE_OW_AMY - gfx_pointer OWIsaacGfx, $14 ; SPRITE_OW_ISAAC - gfx_pointer OWMitchGfx, $14 ; SPRITE_OW_MITCH - gfx_pointer OWGeneGfx, $14 ; SPRITE_OW_GENE - gfx_pointer OWMurrayGfx, $14 ; SPRITE_OW_MURRAY - gfx_pointer OWCourtneyGfx, $14 ; SPRITE_OW_COURTNEY - gfx_pointer OWSteveGfx, $14 ; SPRITE_OW_STEVE - gfx_pointer OWJackGfx, $14 ; SPRITE_OW_JACK - gfx_pointer OWRodGfx, $14 ; SPRITE_OW_ROD - gfx_pointer OWBoyGfx, $14 ; SPRITE_OW_BOY - gfx_pointer OWLadGfx, $14 ; SPRITE_OW_LAD - gfx_pointer OWSpecsGfx, $14 ; SPRITE_OW_SPECS - gfx_pointer OWButchGfx, $14 ; SPRITE_OW_BUTCH - gfx_pointer OWManiaGfx, $14 ; SPRITE_OW_MANIA - gfx_pointer OWJoshuaGfx, $14 ; SPRITE_OW_JOSHUA - gfx_pointer OWHoodGfx, $14 ; SPRITE_OW_HOOD - gfx_pointer OWTechGfx, $14 ; SPRITE_OW_TECH - gfx_pointer OWChapGfx, $14 ; SPRITE_OW_CHAP - gfx_pointer OWManGfx, $14 ; SPRITE_OW_MAN - gfx_pointer OWPappyGfx, $14 ; SPRITE_OW_PAPPY - gfx_pointer OWGirlGfx, $14 ; SPRITE_OW_GIRL - gfx_pointer OWLass1Gfx, $14 ; SPRITE_OW_LASS1 - gfx_pointer OWLass2Gfx, $14 ; SPRITE_OW_LASS2 - gfx_pointer OWLass3Gfx, $14 ; SPRITE_OW_LASS3 - gfx_pointer OWSwimmerGfx, $14 ; SPRITE_OW_SWIMMER - gfx_pointer OWClerkGfx, $08 ; SPRITE_OW_CLERK - gfx_pointer OWGalGfx, $14 ; SPRITE_OW_GAL - gfx_pointer OWWomanGfx, $14 ; SPRITE_OW_WOMAN - gfx_pointer OWGrannyGfx, $14 ; SPRITE_OW_GRANNY - gfx_pointer OverworldMapOAMGfx, $08 ; SPRITE_OW_MAP_OAM - gfx_pointer Duel0Gfx, $16 ; SPRITE_DUEL_0 - gfx_pointer Duel63Gfx, $0a ; SPRITE_DUEL_63 - gfx_pointer DuelGlowGfx, $0b ; SPRITE_DUEL_GLOW - gfx_pointer Duel1Gfx, $06 ; SPRITE_DUEL_1 - gfx_pointer Duel2Gfx, $08 ; SPRITE_DUEL_2 - gfx_pointer Duel55Gfx, $02 ; SPRITE_DUEL_55 - gfx_pointer Duel58Gfx, $04 ; SPRITE_DUEL_58 - gfx_pointer Duel3Gfx, $09 ; SPRITE_DUEL_3 - gfx_pointer Duel4Gfx, $12 ; SPRITE_DUEL_4 - gfx_pointer Duel5Gfx, $09 ; SPRITE_DUEL_5 - gfx_pointer Duel6Gfx, $11 ; SPRITE_DUEL_6 - gfx_pointer Duel59Gfx, $03 ; SPRITE_DUEL_59 - gfx_pointer Duel7Gfx, $2d ; SPRITE_DUEL_7 - gfx_pointer Duel8Gfx, $0d ; SPRITE_DUEL_8 - gfx_pointer Duel9Gfx, $1c ; SPRITE_DUEL_9 - gfx_pointer Duel10Gfx, $4c ; SPRITE_DUEL_10 - gfx_pointer Duel61Gfx, $03 ; SPRITE_DUEL_61 - gfx_pointer Duel11Gfx, $1b ; SPRITE_DUEL_11 - gfx_pointer Duel12Gfx, $07 ; SPRITE_DUEL_12 - gfx_pointer Duel13Gfx, $0c ; SPRITE_DUEL_13 - gfx_pointer Duel62Gfx, $01 ; SPRITE_DUEL_62 - gfx_pointer Duel14Gfx, $22 ; SPRITE_DUEL_14 - gfx_pointer Duel15Gfx, $20 ; SPRITE_DUEL_15 - gfx_pointer Duel16Gfx, $0a ; SPRITE_DUEL_16 - gfx_pointer Duel17Gfx, $25 ; SPRITE_DUEL_17 - gfx_pointer Duel18Gfx, $18 ; SPRITE_DUEL_18 - gfx_pointer Duel19Gfx, $1b ; SPRITE_DUEL_19 - gfx_pointer Duel20Gfx, $08 ; SPRITE_DUEL_20 - gfx_pointer Duel21Gfx, $0d ; SPRITE_DUEL_21 - gfx_pointer Duel22Gfx, $22 ; SPRITE_DUEL_22 - gfx_pointer Duel23Gfx, $0c ; SPRITE_DUEL_23 - gfx_pointer Duel24Gfx, $25 ; SPRITE_DUEL_24 - gfx_pointer Duel25Gfx, $22 ; SPRITE_DUEL_25 - gfx_pointer Duel26Gfx, $0c ; SPRITE_DUEL_26 - gfx_pointer Duel27Gfx, $4c ; SPRITE_DUEL_27 - gfx_pointer Duel28Gfx, $08 ; SPRITE_DUEL_28 - gfx_pointer Duel29Gfx, $07 ; SPRITE_DUEL_29 - gfx_pointer Duel56Gfx, $01 ; SPRITE_DUEL_56 - gfx_pointer Duel30Gfx, $1a ; SPRITE_DUEL_30 - gfx_pointer Duel31Gfx, $0a ; SPRITE_DUEL_31 - gfx_pointer Duel32Gfx, $2e ; SPRITE_DUEL_32 - gfx_pointer Duel33Gfx, $08 ; SPRITE_DUEL_33 - gfx_pointer Duel34Gfx, $07 ; SPRITE_DUEL_34 - gfx_pointer Duel35Gfx, $1c ; SPRITE_DUEL_35 - gfx_pointer Duel66Gfx, $04 ; SPRITE_DUEL_66 - gfx_pointer Duel36Gfx, $08 ; SPRITE_DUEL_36 - gfx_pointer Duel37Gfx, $0b ; SPRITE_DUEL_37 - gfx_pointer Duel57Gfx, $01 ; SPRITE_DUEL_57 - gfx_pointer Duel38Gfx, $1c ; SPRITE_DUEL_38 - gfx_pointer Duel39Gfx, $16 ; SPRITE_DUEL_39 - gfx_pointer Duel40Gfx, $10 ; SPRITE_DUEL_40 - gfx_pointer Duel41Gfx, $0f ; SPRITE_DUEL_41 - gfx_pointer Duel42Gfx, $07 ; SPRITE_DUEL_42 - gfx_pointer Duel43Gfx, $0a ; SPRITE_DUEL_43 - gfx_pointer Duel44Gfx, $09 ; SPRITE_DUEL_44 - gfx_pointer Duel60Gfx, $02 ; SPRITE_DUEL_60 - gfx_pointer Duel64Gfx, $02 ; SPRITE_DUEL_64 - gfx_pointer Duel45Gfx, $03 ; SPRITE_DUEL_45 - gfx_pointer Duel46Gfx, $08 ; SPRITE_DUEL_46 - gfx_pointer Duel47Gfx, $0f ; SPRITE_DUEL_47 - gfx_pointer Duel48Gfx, $03 ; SPRITE_DUEL_48 - gfx_pointer Duel49Gfx, $05 ; SPRITE_DUEL_49 - gfx_pointer Duel50Gfx, $17 ; SPRITE_DUEL_50 - gfx_pointer Duel51Gfx, $36 ; SPRITE_DUEL_WON_LOST_DRAW - gfx_pointer Duel52Gfx, $0b ; SPRITE_DUEL_52 - gfx_pointer Duel53Gfx, $06 ; SPRITE_DUEL_53 - gfx_pointer Duel54Gfx, $16 ; SPRITE_DUEL_54 - gfx_pointer BoosterPackOAMGfx, $20 ; SPRITE_BOOSTER_PACK_OAM - gfx_pointer PressStartGfx, $14 ; SPRITE_PRESS_START - gfx_pointer GrassGfx, $04 ; SPRITE_GRASS - gfx_pointer FireGfx, $04 ; SPRITE_FIRE - gfx_pointer WaterGfx, $04 ; SPRITE_WATER - gfx_pointer ColorlessGfx, $04 ; SPRITE_COLORLESS - gfx_pointer LightningGfx, $04 ; SPRITE_LIGHTNING - gfx_pointer PsychicGfx, $04 ; SPRITE_PSYCHIC - gfx_pointer FightingGfx, $04 ; SPRITE_FIGHTING - -; \1 = anim data pointer -anim_data_pointer: MACRO - dwb \1, BANK(\1) - BANK(SpriteAnimations) - db $00 ; unused (padding?) -ENDM - -SpriteAnimations: ; 81333 (20:5333) - anim_data_pointer AnimData0 ; SPRITE_ANIM_LIGHT_NPC_UP - anim_data_pointer AnimData1 ; SPRITE_ANIM_LIGHT_NPC_RIGHT - anim_data_pointer AnimData2 ; SPRITE_ANIM_LIGHT_NPC_DOWN - anim_data_pointer AnimData3 ; SPRITE_ANIM_LIGHT_NPC_LEFT - anim_data_pointer AnimData4 ; SPRITE_ANIM_DARK_NPC_UP - anim_data_pointer AnimData5 ; SPRITE_ANIM_DARK_NPC_RIGHT - anim_data_pointer AnimData6 ; SPRITE_ANIM_DARK_NPC_DOWN - anim_data_pointer AnimData7 ; SPRITE_ANIM_DARK_NPC_LEFT - anim_data_pointer AnimData8 ; SPRITE_ANIM_SGB_AMY_LAYING - anim_data_pointer AnimData9 ; SPRITE_ANIM_SGB_AMY_STAND - anim_data_pointer AnimData10 ; SPRITE_ANIM_SGB_CLERK_NPC_UP - anim_data_pointer AnimData11 ; SPRITE_ANIM_SGB_CLERK_NPC_RIGHT - anim_data_pointer AnimData12 ; SPRITE_ANIM_SGB_CLERK_NPC_DOWN - anim_data_pointer AnimData13 ; SPRITE_ANIM_SGB_CLERK_NPC_LEFT - anim_data_pointer AnimData14 ; SPRITE_ANIM_BLUE_NPC_UP - anim_data_pointer AnimData15 ; SPRITE_ANIM_BLUE_NPC_RIGHT - anim_data_pointer AnimData16 ; SPRITE_ANIM_BLUE_NPC_DOWN - anim_data_pointer AnimData17 ; SPRITE_ANIM_BLUE_NPC_LEFT - anim_data_pointer AnimData18 ; SPRITE_ANIM_PINK_NPC_UP - anim_data_pointer AnimData19 ; SPRITE_ANIM_PINK_NPC_RIGHT - anim_data_pointer AnimData20 ; SPRITE_ANIM_PINK_NPC_DOWN - anim_data_pointer AnimData21 ; SPRITE_ANIM_PINK_NPC_LEFT - anim_data_pointer AnimData22 ; SPRITE_ANIM_YELLOW_NPC_UP - anim_data_pointer AnimData23 ; SPRITE_ANIM_YELLOW_NPC_RIGHT - anim_data_pointer AnimData24 ; SPRITE_ANIM_YELLOW_NPC_DOWN - anim_data_pointer AnimData25 ; SPRITE_ANIM_YELLOW_NPC_LEFT - anim_data_pointer AnimData26 ; SPRITE_ANIM_GREEN_NPC_UP - anim_data_pointer AnimData27 ; SPRITE_ANIM_GREEN_NPC_RIGHT - anim_data_pointer AnimData28 ; SPRITE_ANIM_GREEN_NPC_DOWN - anim_data_pointer AnimData29 ; SPRITE_ANIM_GREEN_NPC_LEFT - anim_data_pointer AnimData30 ; SPRITE_ANIM_RED_NPC_UP - anim_data_pointer AnimData31 ; SPRITE_ANIM_RED_NPC_RIGHT - anim_data_pointer AnimData32 ; SPRITE_ANIM_RED_NPC_DOWN - anim_data_pointer AnimData33 ; SPRITE_ANIM_RED_NPC_LEFT - anim_data_pointer AnimData34 ; SPRITE_ANIM_PURPLE_NPC_UP - anim_data_pointer AnimData35 ; SPRITE_ANIM_PURPLE_NPC_RIGHT - anim_data_pointer AnimData36 ; SPRITE_ANIM_PURPLE_NPC_DOWN - anim_data_pointer AnimData37 ; SPRITE_ANIM_PURPLE_NPC_LEFT - anim_data_pointer AnimData38 ; SPRITE_ANIM_WHITE_NPC_UP - anim_data_pointer AnimData39 ; SPRITE_ANIM_WHITE_NPC_RIGHT - anim_data_pointer AnimData40 ; SPRITE_ANIM_WHITE_NPC_DOWN - anim_data_pointer AnimData41 ; SPRITE_ANIM_WHITE_NPC_LEFT - anim_data_pointer AnimData42 ; SPRITE_ANIM_INDIGO_NPC_UP - anim_data_pointer AnimData43 ; SPRITE_ANIM_INDIGO_NPC_RIGHT - anim_data_pointer AnimData44 ; SPRITE_ANIM_INDIGO_NPC_DOWN - anim_data_pointer AnimData45 ; SPRITE_ANIM_INDIGO_NPC_LEFT - anim_data_pointer AnimData46 ; SPRITE_ANIM_CGB_AMY_LAYING - anim_data_pointer AnimData47 ; SPRITE_ANIM_CGB_AMY_STAND - anim_data_pointer AnimData48 ; SPRITE_ANIM_CGB_CLERK_NPC_UP - anim_data_pointer AnimData49 ; SPRITE_ANIM_CGB_CLERK_NPC_RIGHT - anim_data_pointer AnimData50 ; SPRITE_ANIM_CGB_CLERK_NPC_DOWN - anim_data_pointer AnimData51 ; SPRITE_ANIM_CGB_CLERK_NPC_LEFT - anim_data_pointer AnimData52 ; SPRITE_ANIM_SGB_VOLCANO_SMOKE - anim_data_pointer AnimData53 ; SPRITE_ANIM_SGB_OWMAP_CURSOR - anim_data_pointer AnimData54 ; SPRITE_ANIM_SGB_OWMAP_CURSOR_FAST - anim_data_pointer AnimData55 ; SPRITE_ANIM_CGB_VOLCANO_SMOKE - anim_data_pointer AnimData56 ; SPRITE_ANIM_CGB_OWMAP_CURSOR - anim_data_pointer AnimData57 ; SPRITE_ANIM_CGB_OWMAP_CURSOR_FAST - anim_data_pointer AnimData58 ; SPRITE_ANIM_TORCH - anim_data_pointer AnimData59 ; SPRITE_ANIM_SGB_CARD_TOP_LEFT - anim_data_pointer AnimData60 ; SPRITE_ANIM_SGB_CARD_TOP_RIGHT - anim_data_pointer AnimData61 ; SPRITE_ANIM_SGB_CARD_LEFT_SPARK - anim_data_pointer AnimData62 ; SPRITE_ANIM_SGB_CARD_BOTTOM_LEFT - anim_data_pointer AnimData63 ; SPRITE_ANIM_SGB_CARD_BOTTOM_RIGHT - anim_data_pointer AnimData64 ; SPRITE_ANIM_SGB_CARD_RIGHT_SPARK - anim_data_pointer AnimData65 ; SPRITE_ANIM_CGB_CARD_TOP_LEFT - anim_data_pointer AnimData66 ; SPRITE_ANIM_CGB_CARD_TOP_RIGHT - anim_data_pointer AnimData67 ; SPRITE_ANIM_CGB_CARD_LEFT_SPARK - anim_data_pointer AnimData68 ; SPRITE_ANIM_CGB_CARD_BOTTOM_LEFT - anim_data_pointer AnimData69 ; SPRITE_ANIM_CGB_CARD_BOTTOM_RIGHT - anim_data_pointer AnimData70 ; SPRITE_ANIM_CGB_CARD_RIGHT_SPARK - anim_data_pointer AnimData71 ; SPRITE_ANIM_71 - anim_data_pointer AnimData72 ; SPRITE_ANIM_72 - anim_data_pointer AnimData73 ; SPRITE_ANIM_73 - anim_data_pointer AnimData74 ; SPRITE_ANIM_74 - anim_data_pointer AnimData75 ; SPRITE_ANIM_75 - anim_data_pointer AnimData76 ; SPRITE_ANIM_76 - anim_data_pointer AnimData77 ; SPRITE_ANIM_77 - anim_data_pointer AnimData78 ; SPRITE_ANIM_78 - anim_data_pointer AnimData79 ; SPRITE_ANIM_79 - anim_data_pointer AnimData80 ; SPRITE_ANIM_80 - anim_data_pointer AnimData81 ; SPRITE_ANIM_81 - anim_data_pointer AnimData82 ; SPRITE_ANIM_82 - anim_data_pointer AnimData83 ; SPRITE_ANIM_83 - anim_data_pointer AnimData84 ; SPRITE_ANIM_84 - anim_data_pointer AnimData85 ; SPRITE_ANIM_85 - anim_data_pointer AnimData86 ; SPRITE_ANIM_86 - anim_data_pointer AnimData87 ; SPRITE_ANIM_87 - anim_data_pointer AnimData88 ; SPRITE_ANIM_88 - anim_data_pointer AnimData89 ; SPRITE_ANIM_89 - anim_data_pointer AnimData90 ; SPRITE_ANIM_90 - anim_data_pointer AnimData91 ; SPRITE_ANIM_91 - anim_data_pointer AnimData92 ; SPRITE_ANIM_92 - anim_data_pointer AnimData93 ; SPRITE_ANIM_93 - anim_data_pointer AnimData94 ; SPRITE_ANIM_94 - anim_data_pointer AnimData95 ; SPRITE_ANIM_95 - anim_data_pointer AnimData96 ; SPRITE_ANIM_96 - anim_data_pointer AnimData97 ; SPRITE_ANIM_97 - anim_data_pointer AnimData98 ; SPRITE_ANIM_98 - anim_data_pointer AnimData99 ; SPRITE_ANIM_99 - anim_data_pointer AnimData100 ; SPRITE_ANIM_100 - anim_data_pointer AnimData101 ; SPRITE_ANIM_101 - anim_data_pointer AnimData102 ; SPRITE_ANIM_102 - anim_data_pointer AnimData103 ; SPRITE_ANIM_103 - anim_data_pointer AnimData104 ; SPRITE_ANIM_104 - anim_data_pointer AnimData105 ; SPRITE_ANIM_105 - anim_data_pointer AnimData106 ; SPRITE_ANIM_106 - anim_data_pointer AnimData107 ; SPRITE_ANIM_107 - anim_data_pointer AnimData108 ; SPRITE_ANIM_108 - anim_data_pointer AnimData109 ; SPRITE_ANIM_109 - anim_data_pointer AnimData110 ; SPRITE_ANIM_110 - anim_data_pointer AnimData111 ; SPRITE_ANIM_111 - anim_data_pointer AnimData112 ; SPRITE_ANIM_112 - anim_data_pointer AnimData113 ; SPRITE_ANIM_113 - anim_data_pointer AnimData114 ; SPRITE_ANIM_114 - anim_data_pointer AnimData115 ; SPRITE_ANIM_115 - anim_data_pointer AnimData116 ; SPRITE_ANIM_116 - anim_data_pointer AnimData117 ; SPRITE_ANIM_117 - anim_data_pointer AnimData118 ; SPRITE_ANIM_118 - anim_data_pointer AnimData119 ; SPRITE_ANIM_119 - anim_data_pointer AnimData120 ; SPRITE_ANIM_120 - anim_data_pointer AnimData121 ; SPRITE_ANIM_121 - anim_data_pointer AnimData122 ; SPRITE_ANIM_122 - anim_data_pointer AnimData123 ; SPRITE_ANIM_123 - anim_data_pointer AnimData124 ; SPRITE_ANIM_124 - anim_data_pointer AnimData125 ; SPRITE_ANIM_125 - anim_data_pointer AnimData126 ; SPRITE_ANIM_126 - anim_data_pointer AnimData127 ; SPRITE_ANIM_127 - anim_data_pointer AnimData128 ; SPRITE_ANIM_128 - anim_data_pointer AnimData129 ; SPRITE_ANIM_129 - anim_data_pointer AnimData130 ; SPRITE_ANIM_130 - anim_data_pointer AnimData131 ; SPRITE_ANIM_131 - anim_data_pointer AnimData132 ; SPRITE_ANIM_132 - anim_data_pointer AnimData133 ; SPRITE_ANIM_133 - anim_data_pointer AnimData134 ; SPRITE_ANIM_134 - anim_data_pointer AnimData135 ; SPRITE_ANIM_135 - anim_data_pointer AnimData136 ; SPRITE_ANIM_136 - anim_data_pointer AnimData137 ; SPRITE_ANIM_137 - anim_data_pointer AnimData138 ; SPRITE_ANIM_138 - anim_data_pointer AnimData139 ; SPRITE_ANIM_139 - anim_data_pointer AnimData140 ; SPRITE_ANIM_140 - anim_data_pointer AnimData141 ; SPRITE_ANIM_141 - anim_data_pointer AnimData142 ; SPRITE_ANIM_142 - anim_data_pointer AnimData143 ; SPRITE_ANIM_143 - anim_data_pointer AnimData144 ; SPRITE_ANIM_144 - anim_data_pointer AnimData145 ; SPRITE_ANIM_145 - anim_data_pointer AnimData146 ; SPRITE_ANIM_146 - anim_data_pointer AnimData147 ; SPRITE_ANIM_147 - anim_data_pointer AnimData148 ; SPRITE_ANIM_148 - anim_data_pointer AnimData149 ; SPRITE_ANIM_149 - anim_data_pointer AnimData150 ; SPRITE_ANIM_150 - anim_data_pointer AnimData151 ; SPRITE_ANIM_151 - anim_data_pointer AnimData152 ; SPRITE_ANIM_152 - anim_data_pointer AnimData153 ; SPRITE_ANIM_153 - anim_data_pointer AnimData154 ; SPRITE_ANIM_154 - anim_data_pointer AnimData155 ; SPRITE_ANIM_155 - anim_data_pointer AnimData156 ; SPRITE_ANIM_156 - anim_data_pointer AnimData157 ; SPRITE_ANIM_157 - anim_data_pointer AnimData158 ; SPRITE_ANIM_158 - anim_data_pointer AnimData159 ; SPRITE_ANIM_159 - anim_data_pointer AnimData160 ; SPRITE_ANIM_160 - anim_data_pointer AnimData161 ; SPRITE_ANIM_161 - anim_data_pointer AnimData162 ; SPRITE_ANIM_162 - anim_data_pointer AnimData163 ; SPRITE_ANIM_163 - anim_data_pointer AnimData164 ; SPRITE_ANIM_164 - anim_data_pointer AnimData165 ; SPRITE_ANIM_165 - anim_data_pointer AnimData166 ; SPRITE_ANIM_166 - anim_data_pointer AnimData167 ; SPRITE_ANIM_167 - anim_data_pointer AnimData168 ; SPRITE_ANIM_168 - anim_data_pointer AnimData169 ; SPRITE_ANIM_169 - anim_data_pointer AnimData170 ; SPRITE_ANIM_170 - anim_data_pointer AnimData171 ; SPRITE_ANIM_171 - anim_data_pointer AnimData172 ; SPRITE_ANIM_172 - anim_data_pointer AnimData173 ; SPRITE_ANIM_173 - anim_data_pointer AnimData174 ; SPRITE_ANIM_174 - anim_data_pointer AnimData175 ; SPRITE_ANIM_175 - anim_data_pointer AnimData176 ; SPRITE_ANIM_176 - anim_data_pointer AnimData177 ; SPRITE_ANIM_177 - anim_data_pointer AnimData178 ; SPRITE_ANIM_178 - anim_data_pointer AnimData179 ; SPRITE_ANIM_179 - anim_data_pointer AnimData180 ; SPRITE_ANIM_180 - anim_data_pointer AnimData181 ; SPRITE_ANIM_181 - anim_data_pointer AnimData182 ; SPRITE_ANIM_182 - anim_data_pointer AnimData183 ; SPRITE_ANIM_183 - anim_data_pointer AnimData184 ; SPRITE_ANIM_184 - anim_data_pointer AnimData185 ; SPRITE_ANIM_185 - anim_data_pointer AnimData186 ; SPRITE_ANIM_186 - anim_data_pointer AnimData187 ; SPRITE_ANIM_187 - anim_data_pointer AnimData188 ; SPRITE_ANIM_188 - anim_data_pointer AnimData189 ; SPRITE_ANIM_189 - anim_data_pointer AnimData190 ; SPRITE_ANIM_190 - anim_data_pointer AnimData191 ; SPRITE_ANIM_191 - anim_data_pointer AnimData192 ; SPRITE_ANIM_192 - anim_data_pointer AnimData193 ; SPRITE_ANIM_193 - anim_data_pointer AnimData194 ; SPRITE_ANIM_194 - anim_data_pointer AnimData195 ; SPRITE_ANIM_195 - anim_data_pointer AnimData196 ; SPRITE_ANIM_196 - anim_data_pointer AnimData197 ; SPRITE_ANIM_197 - anim_data_pointer AnimData198 ; SPRITE_ANIM_198 - anim_data_pointer AnimData199 ; SPRITE_ANIM_199 - anim_data_pointer AnimData200 ; SPRITE_ANIM_200 - anim_data_pointer AnimData201 ; SPRITE_ANIM_201 - anim_data_pointer AnimData202 ; SPRITE_ANIM_202 - anim_data_pointer AnimData203 ; SPRITE_ANIM_203 - anim_data_pointer AnimData204 ; SPRITE_ANIM_204 - anim_data_pointer AnimData205 ; SPRITE_ANIM_205 - anim_data_pointer AnimData206 ; SPRITE_ANIM_206 - anim_data_pointer AnimData207 ; SPRITE_ANIM_207 - anim_data_pointer AnimData208 ; SPRITE_ANIM_208 - anim_data_pointer AnimData209 ; SPRITE_ANIM_209 - anim_data_pointer AnimData210 ; SPRITE_ANIM_210 - anim_data_pointer AnimData211 ; SPRITE_ANIM_211 - anim_data_pointer AnimData212 ; SPRITE_ANIM_212 - anim_data_pointer AnimData213 ; SPRITE_ANIM_213 - anim_data_pointer AnimData214 ; SPRITE_ANIM_214 - anim_data_pointer AnimData215 ; SPRITE_ANIM_215 - anim_data_pointer AnimData216 ; SPRITE_ANIM_216 - -; \1 = palette pointer -; \2 = number of palettes -; \3 = number of OBJ colors -palette_pointer: MACRO - dwb \1, BANK(\1) - BANK(Palettes) - db (\2 << 4) + \3 -ENDM - -Palettes: ; 81697 (20:5697) - palette_pointer Palette0, 8, 1 ; PALETTE_0 - palette_pointer Palette1, 8, 0 ; PALETTE_1 - palette_pointer Palette2, 8, 0 ; PALETTE_2 - palette_pointer Palette3, 8, 0 ; PALETTE_3 - palette_pointer Palette4, 8, 0 ; PALETTE_4 - palette_pointer Palette5, 8, 0 ; PALETTE_5 - palette_pointer Palette6, 8, 0 ; PALETTE_6 - palette_pointer Palette7, 8, 0 ; PALETTE_7 - palette_pointer Palette8, 8, 0 ; PALETTE_8 - palette_pointer Palette9, 8, 0 ; PALETTE_9 - palette_pointer Palette10, 8, 0 ; PALETTE_10 - palette_pointer Palette11, 8, 0 ; PALETTE_11 - palette_pointer Palette12, 8, 0 ; PALETTE_12 - palette_pointer Palette13, 8, 0 ; PALETTE_13 - palette_pointer Palette14, 8, 0 ; PALETTE_14 - palette_pointer Palette15, 8, 0 ; PALETTE_15 - palette_pointer Palette16, 8, 0 ; PALETTE_16 - palette_pointer Palette17, 8, 0 ; PALETTE_17 - palette_pointer Palette18, 8, 0 ; PALETTE_18 - palette_pointer Palette19, 8, 0 ; PALETTE_19 - palette_pointer Palette20, 8, 0 ; PALETTE_20 - palette_pointer Palette21, 8, 0 ; PALETTE_21 - palette_pointer Palette22, 8, 0 ; PALETTE_22 - palette_pointer Palette23, 8, 0 ; PALETTE_23 - palette_pointer Palette24, 8, 0 ; PALETTE_24 - palette_pointer Palette25, 8, 0 ; PALETTE_25 - palette_pointer Palette26, 8, 0 ; PALETTE_26 - palette_pointer Palette27, 8, 0 ; PALETTE_27 - palette_pointer Palette28, 8, 0 ; PALETTE_28 - palette_pointer Palette29, 8, 2 ; PALETTE_29 - palette_pointer Palette30, 8, 2 ; PALETTE_30 - palette_pointer Palette31, 1, 1 ; PALETTE_31 - palette_pointer Palette32, 1, 1 ; PALETTE_32 - palette_pointer Palette33, 1, 1 ; PALETTE_33 - palette_pointer Palette34, 1, 1 ; PALETTE_34 - palette_pointer Palette35, 1, 1 ; PALETTE_35 - palette_pointer Palette36, 1, 1 ; PALETTE_36 - palette_pointer Palette37, 1, 1 ; PALETTE_37 - palette_pointer Palette38, 1, 1 ; PALETTE_38 - palette_pointer Palette39, 1, 1 ; PALETTE_39 - palette_pointer Palette40, 1, 1 ; PALETTE_40 - palette_pointer Palette41, 1, 1 ; PALETTE_41 - palette_pointer Palette42, 1, 1 ; PALETTE_42 - palette_pointer Palette43, 1, 1 ; PALETTE_43 - palette_pointer Palette44, 1, 1 ; PALETTE_44 - palette_pointer Palette45, 1, 1 ; PALETTE_45 - palette_pointer Palette46, 1, 1 ; PALETTE_46 - palette_pointer Palette47, 1, 1 ; PALETTE_47 - palette_pointer Palette48, 1, 1 ; PALETTE_48 - palette_pointer Palette49, 1, 1 ; PALETTE_49 - palette_pointer Palette50, 1, 1 ; PALETTE_50 - palette_pointer Palette51, 1, 1 ; PALETTE_51 - palette_pointer Palette52, 1, 1 ; PALETTE_52 - palette_pointer Palette53, 1, 1 ; PALETTE_53 - palette_pointer Palette54, 1, 1 ; PALETTE_54 - palette_pointer Palette55, 1, 1 ; PALETTE_55 - palette_pointer Palette56, 1, 1 ; PALETTE_56 - palette_pointer Palette57, 1, 1 ; PALETTE_57 - palette_pointer Palette58, 1, 1 ; PALETTE_58 - palette_pointer Palette59, 1, 1 ; PALETTE_59 - palette_pointer Palette60, 1, 1 ; PALETTE_60 - palette_pointer Palette61, 1, 1 ; PALETTE_61 - palette_pointer Palette62, 1, 1 ; PALETTE_62 - palette_pointer Palette63, 1, 1 ; PALETTE_63 - palette_pointer Palette64, 1, 1 ; PALETTE_64 - palette_pointer Palette65, 1, 1 ; PALETTE_65 - palette_pointer Palette66, 1, 1 ; PALETTE_66 - palette_pointer Palette67, 1, 1 ; PALETTE_67 - palette_pointer Palette68, 1, 1 ; PALETTE_68 - palette_pointer Palette69, 1, 1 ; PALETTE_69 - palette_pointer Palette70, 1, 1 ; PALETTE_70 - palette_pointer Palette71, 1, 1 ; PALETTE_71 - palette_pointer Palette72, 1, 1 ; PALETTE_72 - palette_pointer Palette73, 1, 1 ; PALETTE_73 - palette_pointer Palette74, 1, 1 ; PALETTE_74 - palette_pointer Palette75, 1, 1 ; PALETTE_75 - palette_pointer Palette76, 1, 1 ; PALETTE_76 - palette_pointer Palette77, 1, 1 ; PALETTE_77 - palette_pointer Palette78, 1, 1 ; PALETTE_78 - palette_pointer Palette79, 1, 1 ; PALETTE_79 - palette_pointer Palette80, 1, 1 ; PALETTE_80 - palette_pointer Palette81, 1, 1 ; PALETTE_81 - palette_pointer Palette82, 1, 1 ; PALETTE_82 - palette_pointer Palette83, 1, 1 ; PALETTE_83 - palette_pointer Palette84, 1, 1 ; PALETTE_84 - palette_pointer Palette85, 1, 1 ; PALETTE_85 - palette_pointer Palette86, 1, 1 ; PALETTE_86 - palette_pointer Palette87, 1, 1 ; PALETTE_87 - palette_pointer Palette88, 1, 1 ; PALETTE_88 - palette_pointer Palette89, 1, 1 ; PALETTE_89 - palette_pointer Palette90, 1, 1 ; PALETTE_90 - palette_pointer Palette91, 1, 1 ; PALETTE_91 - palette_pointer Palette92, 1, 1 ; PALETTE_92 - palette_pointer Palette93, 1, 1 ; PALETTE_93 - palette_pointer Palette94, 8, 0 ; PALETTE_94 - palette_pointer Palette95, 8, 0 ; PALETTE_95 - palette_pointer Palette96, 8, 0 ; PALETTE_96 - palette_pointer Palette97, 8, 0 ; PALETTE_97 - palette_pointer Palette98, 8, 0 ; PALETTE_98 - palette_pointer Palette99, 8, 0 ; PALETTE_99 - palette_pointer Palette100, 8, 0 ; PALETTE_100 - palette_pointer Palette101, 7, 0 ; PALETTE_101 - palette_pointer Palette102, 7, 0 ; PALETTE_102 - palette_pointer Palette103, 7, 0 ; PALETTE_103 - palette_pointer Palette104, 7, 0 ; PALETTE_104 - palette_pointer Palette105, 7, 0 ; PALETTE_105 - palette_pointer Palette106, 7, 0 ; PALETTE_106 - palette_pointer Palette107, 7, 0 ; PALETTE_107 - palette_pointer Palette108, 0, 1 ; PALETTE_108 - palette_pointer Palette109, 0, 1 ; PALETTE_109 - palette_pointer Palette110, 0, 0 ; PALETTE_110 - palette_pointer Palette111, 8, 1 ; PALETTE_111 - palette_pointer Palette112, 8, 1 ; PALETTE_112 - palette_pointer Palette113, 8, 1 ; PALETTE_113 - palette_pointer Palette114, 4, 2 ; PALETTE_114 - palette_pointer Palette115, 4, 2 ; PALETTE_115 - palette_pointer Palette116, 4, 2 ; PALETTE_116 - palette_pointer Palette117, 1, 0 ; PALETTE_117 - palette_pointer Palette118, 6, 0 ; PALETTE_118 - palette_pointer Palette119, 1, 0 ; PALETTE_119 - palette_pointer Palette120, 1, 0 ; PALETTE_120 - palette_pointer Palette121, 1, 0 ; PALETTE_121 - palette_pointer Palette122, 1, 0 ; PALETTE_122 - palette_pointer Palette123, 1, 0 ; PALETTE_123 - palette_pointer Palette124, 1, 0 ; PALETTE_124 - palette_pointer Palette125, 1, 0 ; PALETTE_125 - palette_pointer Palette126, 1, 0 ; PALETTE_126 - palette_pointer Palette127, 1, 0 ; PALETTE_127 - palette_pointer Palette128, 1, 0 ; PALETTE_128 - palette_pointer Palette129, 1, 0 ; PALETTE_129 - palette_pointer Palette130, 1, 0 ; PALETTE_130 - palette_pointer Palette131, 1, 0 ; PALETTE_131 - palette_pointer Palette132, 1, 0 ; PALETTE_132 - palette_pointer Palette133, 1, 0 ; PALETTE_133 - palette_pointer Palette134, 1, 0 ; PALETTE_134 - palette_pointer Palette135, 1, 0 ; PALETTE_135 - palette_pointer Palette136, 1, 0 ; PALETTE_136 - palette_pointer Palette137, 1, 0 ; PALETTE_137 - palette_pointer Palette138, 1, 0 ; PALETTE_138 - palette_pointer Palette139, 1, 0 ; PALETTE_139 - palette_pointer Palette140, 1, 0 ; PALETTE_140 - palette_pointer Palette141, 1, 0 ; PALETTE_141 - palette_pointer Palette142, 1, 0 ; PALETTE_142 - palette_pointer Palette143, 1, 0 ; PALETTE_143 - palette_pointer Palette144, 1, 0 ; PALETTE_144 - palette_pointer Palette145, 1, 0 ; PALETTE_145 - palette_pointer Palette146, 1, 0 ; PALETTE_146 - palette_pointer Palette147, 1, 0 ; PALETTE_147 - palette_pointer Palette148, 1, 0 ; PALETTE_148 - palette_pointer Palette149, 1, 0 ; PALETTE_149 - palette_pointer Palette150, 1, 0 ; PALETTE_150 - palette_pointer Palette151, 1, 0 ; PALETTE_151 - palette_pointer Palette152, 1, 0 ; PALETTE_152 - palette_pointer Palette153, 1, 0 ; PALETTE_153 - palette_pointer Palette154, 1, 0 ; PALETTE_154 - palette_pointer Palette155, 1, 0 ; PALETTE_155 - palette_pointer Palette156, 1, 0 ; PALETTE_156 - palette_pointer Palette157, 1, 0 ; PALETTE_157 - palette_pointer Palette158, 1, 0 ; PALETTE_158 - palette_pointer Palette159, 1, 0 ; PALETTE_159 - palette_pointer Palette160, 1, 0 ; PALETTE_160 - -OverworldMapTilemap:: ; 8191b (20:591b) - db $14 ; width - db $12 ; height - dw NULL - db FALSE ; cgb mode - INCBIN "data/maps/tiles/overworld_map.bin" - -OverworldMapCGBTilemap:: ; 81a22 (20:5a22) - db $14 ; width - db $12 ; height - dw NULL - db TRUE ; cgb mode - INCBIN "data/maps/tiles/overworld_map_cgb.bin" - -MasonLaboratoryTilemap:: ; 81c13 (20:5c13) - db $1c ; width - db $1e ; height - dw MasonLaboratoryPermissions - db FALSE ; cgb mode - INCBIN "data/maps/tiles/mason_laboratory.bin" -MasonLaboratoryPermissions: - INCBIN "data/maps/permissions/mason_laboratory.bin" - -MasonLaboratoryCGBTilemap:: ; 81d2e (20:5d2e) - db $1c ; width - db $1e ; height - dw MasonLaboratoryCGBPermissions - db TRUE ; cgb mode - INCBIN "data/maps/tiles/mason_laboratory_cgb.bin" -MasonLaboratoryCGBPermissions: - INCBIN "data/maps/permissions/mason_laboratory_cgb.bin" - -ChallengeMachineMapEventTilemap:: ; 81ed1 (20:5ed1) - db $04 ; width - db $06 ; height - dw ChallengeMachineMapEventPermissions - db FALSE ; cgb mode - INCBIN "data/maps/tiles/challenge_machine_map_event.bin" -ChallengeMachineMapEventPermissions: - INCBIN "data/maps/permissions/challenge_machine_map_event.bin" - -ChallengeMachineMapEventCGBTilemap:: ; 81ef5 (20:5ef5) - db $04 ; width - db $06 ; height - dw ChallengeMachineMapEventCGBPermissions - db TRUE ; cgb mode - INCBIN "data/maps/tiles/challenge_machine_map_event_cgb.bin" -ChallengeMachineMapEventCGBPermissions: - INCBIN "data/maps/permissions/challenge_machine_map_event_cgb.bin" - -DeckMachineRoomTilemap:: ; 81f26 (20:5f26) - db $18 ; width - db $1e ; height - dw DeckMachineRoomPermissions - db FALSE ; cgb mode - INCBIN "data/maps/tiles/deck_machine_room.bin" -DeckMachineRoomPermissions: - INCBIN "data/maps/permissions/deck_machine_room.bin" - -DeckMachineRoomCGBTilemap:: ; 81feb (20:5feb) - db $18 ; width - db $1e ; height - dw DeckMachineRoomCGBPermissions - db TRUE ; cgb mode - INCBIN "data/maps/tiles/deck_machine_room_cgb.bin" -DeckMachineRoomCGBPermissions: - INCBIN "data/maps/permissions/deck_machine_room_cgb.bin" - -DeckMachineMapEventTilemap:: ; 82143 (20:6143) - db $04 ; width - db $01 ; height - dw DeckMachineMapEventPermissions - db FALSE ; cgb mode - INCBIN "data/maps/tiles/deck_machine_map_event.bin" -DeckMachineMapEventPermissions: - INCBIN "data/maps/permissions/deck_machine_map_event.bin" - -DeckMachineMapEventCGBTilemap:: ; 82150 (20:6150) - db $04 ; width - db $01 ; height - dw DeckMachineMapEventCGBPermissions - db TRUE ; cgb mode - INCBIN "data/maps/tiles/deck_machine_map_event_cgb.bin" -DeckMachineMapEventCGBPermissions: - INCBIN "data/maps/permissions/deck_machine_map_event_cgb.bin" - -IshiharaTilemap:: ; 82160 (20:6160) - db $14 ; width - db $18 ; height - dw IshiharaPermissions - db FALSE ; cgb mode - INCBIN "data/maps/tiles/ishihara.bin" -IshiharaPermissions: - INCBIN "data/maps/permissions/ishihara.bin" - -IshiharaCGBTilemap:: ; 82222 (20:6222) - db $14 ; width - db $18 ; height - dw IshiharaCGBPermissions - db TRUE ; cgb mode - INCBIN "data/maps/tiles/ishihara_cgb.bin" -IshiharaCGBPermissions: - INCBIN "data/maps/permissions/ishihara_cgb.bin" - -FightingClubEntranceTilemap:: ; 82336 (20:6336) - db $14 ; width - db $12 ; height - dw FightingClubEntrancePermissions - db FALSE ; cgb mode - INCBIN "data/maps/tiles/fighting_club_entrance.bin" -FightingClubEntrancePermissions: - INCBIN "data/maps/permissions/fighting_club_entrance.bin" - -FightingClubEntranceCGBTilemap:: ; 82400 (20:6400) - db $14 ; width - db $12 ; height - dw FightingClubEntranceCGBPermissions - db TRUE ; cgb mode - INCBIN "data/maps/tiles/fighting_club_entrance_cgb.bin" -FightingClubEntranceCGBPermissions: - INCBIN "data/maps/permissions/fighting_club_entrance_cgb.bin" - -RockClubEntranceTilemap:: ; 8251d (20:651d) - db $14 ; width - db $12 ; height - dw RockClubEntrancePermissions - db FALSE ; cgb mode - INCBIN "data/maps/tiles/rock_club_entrance.bin" -RockClubEntrancePermissions: - INCBIN "data/maps/permissions/rock_club_entrance.bin" - -RockClubEntranceCGBTilemap:: ; 825e7 (20:65e7) - db $14 ; width - db $12 ; height - dw RockClubEntranceCGBPermissions - db TRUE ; cgb mode - INCBIN "data/maps/tiles/rock_club_entrance_cgb.bin" -RockClubEntranceCGBPermissions: - INCBIN "data/maps/permissions/rock_club_entrance_cgb.bin" - -WaterClubEntranceTilemap:: ; 82704 (20:6704) - db $14 ; width - db $12 ; height - dw WaterClubEntrancePermissions - db FALSE ; cgb mode - INCBIN "data/maps/tiles/water_club_entrance.bin" -WaterClubEntrancePermissions: - INCBIN "data/maps/permissions/water_club_entrance.bin" - -WaterClubEntranceCGBTilemap:: ; 827ce (20:67ce) - db $14 ; width - db $12 ; height - dw WaterClubEntranceCGBPermissions - db TRUE ; cgb mode - INCBIN "data/maps/tiles/water_club_entrance_cgb.bin" -WaterClubEntranceCGBPermissions: - INCBIN "data/maps/permissions/water_club_entrance_cgb.bin" - -LightningClubEntranceTilemap:: ; 828eb (20:68eb) - db $14 ; width - db $12 ; height - dw LightningClubEntrancePermissions - db FALSE ; cgb mode - INCBIN "data/maps/tiles/lightning_club_entrance.bin" -LightningClubEntrancePermissions: - INCBIN "data/maps/permissions/lightning_club_entrance.bin" - -LightningClubEntranceCGBTilemap:: ; 829b5 (20:69b5) - db $14 ; width - db $12 ; height - dw LightningClubEntranceCGBPermissions - db TRUE ; cgb mode - INCBIN "data/maps/tiles/lightning_club_entrance_cgb.bin" -LightningClubEntranceCGBPermissions: - INCBIN "data/maps/permissions/lightning_club_entrance_cgb.bin" - -GrassClubEntranceTilemap:: ; 82ad2 (20:6ad2) - db $14 ; width - db $12 ; height - dw GrassClubEntrancePermissions - db FALSE ; cgb mode - INCBIN "data/maps/tiles/grass_club_entrance.bin" -GrassClubEntrancePermissions: - INCBIN "data/maps/permissions/grass_club_entrance.bin" - -GrassClubEntranceCGBTilemap:: ; 82b9c (20:6b9c) - db $14 ; width - db $12 ; height - dw GrassClubEntranceCGBPermissions - db TRUE ; cgb mode - INCBIN "data/maps/tiles/grass_club_entrance_cgb.bin" -GrassClubEntranceCGBPermissions: - INCBIN "data/maps/permissions/grass_club_entrance_cgb.bin" - -PsychicClubEntranceTilemap:: ; 82cb9 (20:6cb9) - db $14 ; width - db $12 ; height - dw PsychicClubEntrancePermissions - db FALSE ; cgb mode - INCBIN "data/maps/tiles/psychic_club_entrance.bin" -PsychicClubEntrancePermissions: - INCBIN "data/maps/permissions/psychic_club_entrance.bin" - -PsychicClubEntranceCGBTilemap:: ; 82d83 (20:6d83) - db $14 ; width - db $12 ; height - dw PsychicClubEntranceCGBPermissions - db TRUE ; cgb mode - INCBIN "data/maps/tiles/psychic_club_entrance_cgb.bin" -PsychicClubEntranceCGBPermissions: - INCBIN "data/maps/permissions/psychic_club_entrance_cgb.bin" - -ScienceClubEntranceTilemap:: ; 82ea0 (20:6ea0) - db $14 ; width - db $12 ; height - dw ScienceClubEntrancePermissions - db FALSE ; cgb mode - INCBIN "data/maps/tiles/science_club_entrance.bin" -ScienceClubEntrancePermissions: - INCBIN "data/maps/permissions/science_club_entrance.bin" - -ScienceClubEntranceCGBTilemap:: ; 82f6a (20:6f6a) - db $14 ; width - db $12 ; height - dw ScienceClubEntranceCGBPermissions - db TRUE ; cgb mode - INCBIN "data/maps/tiles/science_club_entrance_cgb.bin" -ScienceClubEntranceCGBPermissions: - INCBIN "data/maps/permissions/science_club_entrance_cgb.bin" - -FireClubEntranceTilemap:: ; 83087 (20:7087) - db $14 ; width - db $12 ; height - dw FireClubEntrancePermissions - db FALSE ; cgb mode - INCBIN "data/maps/tiles/fire_club_entrance.bin" -FireClubEntrancePermissions: - INCBIN "data/maps/permissions/fire_club_entrance.bin" - -FireClubEntranceCGBTilemap:: ; 83151 (20:7151) - db $14 ; width - db $12 ; height - dw FireClubEntranceCGBPermissions - db TRUE ; cgb mode - INCBIN "data/maps/tiles/fire_club_entrance_cgb.bin" -FireClubEntranceCGBPermissions: - INCBIN "data/maps/permissions/fire_club_entrance_cgb.bin" - -ChallengeHallEntranceTilemap:: ; 8326e (20:726e) - db $14 ; width - db $12 ; height - dw ChallengeHallEntrancePermissions - db FALSE ; cgb mode - INCBIN "data/maps/tiles/challenge_hall_entrance.bin" -ChallengeHallEntrancePermissions: - INCBIN "data/maps/permissions/challenge_hall_entrance.bin" - -ChallengeHallEntranceCGBTilemap:: ; 83321 (20:7321) - db $14 ; width - db $12 ; height - dw ChallengeHallEntranceCGBPermissions - db TRUE ; cgb mode - INCBIN "data/maps/tiles/challenge_hall_entrance_cgb.bin" -ChallengeHallEntranceCGBPermissions: - INCBIN "data/maps/permissions/challenge_hall_entrance_cgb.bin" - -ClubLobbyTilemap:: ; 83424 (20:7424) - db $1c ; width - db $1a ; height - dw ClubLobbyPermissions - db FALSE ; cgb mode - INCBIN "data/maps/tiles/club_lobby.bin" -ClubLobbyPermissions: - INCBIN "data/maps/permissions/club_lobby.bin" - -ClubLobbyCGBTilemap:: ; 83545 (20:7545) - db $1c ; width - db $1a ; height - dw ClubLobbyCGBPermissions - db TRUE ; cgb mode - INCBIN "data/maps/tiles/club_lobby_cgb.bin" -ClubLobbyCGBPermissions: - INCBIN "data/maps/permissions/club_lobby_cgb.bin" - -FightingClubTilemap:: ; 836db (20:76db) - db $18 ; width - db $12 ; height - dw FightingClubPermissions - db FALSE ; cgb mode - INCBIN "data/maps/tiles/fighting_club.bin" -FightingClubPermissions: - INCBIN "data/maps/permissions/fighting_club.bin" - -FightingClubCGBTilemap:: ; 8378c (20:778c) - db $18 ; width - db $12 ; height - dw FightingClubCGBPermissions - db TRUE ; cgb mode - INCBIN "data/maps/tiles/fighting_club_cgb.bin" -FightingClubCGBPermissions: - INCBIN "data/maps/permissions/fighting_club_cgb.bin" - -RockClubTilemap:: ; 8388d (20:788d) - db $1c ; width - db $1e ; height - dw RockClubPermissions - db FALSE ; cgb mode - INCBIN "data/maps/tiles/rock_club.bin" -RockClubPermissions: - INCBIN "data/maps/permissions/rock_club.bin" - -RockClubCGBTilemap:: ; 839d6 (20:79d6) - db $1c ; width - db $1e ; height - dw RockClubCGBPermissions - db TRUE ; cgb mode - INCBIN "data/maps/tiles/rock_club_cgb.bin" -RockClubCGBPermissions: - INCBIN "data/maps/permissions/rock_club_cgb.bin" - -PokemonDomeDoorMapEventTilemap:: ; 83bf1 (20:7bf1) - db $04 ; width - db $03 ; height - dw PokemonDomeDoorMapEventPermissions - db FALSE ; cgb mode - INCBIN "data/maps/tiles/pokemon_dome_door_map_event.bin" -PokemonDomeDoorMapEventPermissions: - INCBIN "data/maps/permissions/pokemon_dome_door_map_event.bin" - -PokemonDomeDoorMapEventCGBTilemap:: ; 83c03 (20:7c03) - db $04 ; width - db $03 ; height - dw PokemonDomeDoorMapEventCGBPermissions - db TRUE ; cgb mode - INCBIN "data/maps/tiles/pokemon_dome_door_map_event_cgb.bin" -PokemonDomeDoorMapEventCGBPermissions: - INCBIN "data/maps/permissions/pokemon_dome_door_map_event_cgb.bin" - -HallOfHonorDoorMapEventTilemap:: ; 83c1a (20:7c1a) - db $04 ; width - db $03 ; height - dw HallOfHonorDoorMapEventPermissions - db FALSE ; cgb mode - INCBIN "data/maps/tiles/hall_of_honor_door_map_event.bin" -HallOfHonorDoorMapEventPermissions: - INCBIN "data/maps/permissions/hall_of_honor_door_map_event.bin" - -HallOfHonorDoorMapEventCGBTilemap:: ; 83c26 (20:7c26) - db $04 ; width - db $03 ; height - dw HallOfHonorDoorMapEventCGBPermissions - db TRUE ; cgb mode - INCBIN "data/maps/tiles/hall_of_honor_door_map_event_cgb.bin" -HallOfHonorDoorMapEventCGBPermissions: - INCBIN "data/maps/permissions/hall_of_honor_door_map_event_cgb.bin" - -GrassMedalTilemap:: ; 83c36 (20:7c36) - db $03 ; width - db $03 ; height - dw NULL - db TRUE ; cgb mode - INCBIN "data/maps/tiles/grass_medal.bin" - -AnimData1:: ; 83c4c (20:7c4c) - frame_table AnimFrameTable0 - frame_data 3, 16, 0, 0 - frame_data 4, 16, 0, 0 - frame_data 0, 0, 0, 0 - -Palette110:: ; 83c5b (20:7c5b) - db $00, $00 diff --git a/src/engine/gfx/gfx_table_pointers.asm b/src/engine/gfx/gfx_table_pointers.asm new file mode 100644 index 0000000..370ed15 --- /dev/null +++ b/src/engine/gfx/gfx_table_pointers.asm @@ -0,0 +1,6 @@ +GfxTablePointers: + dw Tilemaps + dw Tilesets + dw Sprites + dw SpriteAnimations + dw Palettes diff --git a/src/engine/gfx/palettes.asm b/src/engine/gfx/palettes.asm new file mode 100644 index 0000000..8a0d71a --- /dev/null +++ b/src/engine/gfx/palettes.asm @@ -0,0 +1,170 @@ +; \1 = palette pointer +; \2 = number of palettes +; \3 = number of OBJ colors +palette_pointer: MACRO + dwb \1, BANK(\1) - BANK(Palettes) + db (\2 << 4) + \3 +ENDM + +Palettes: + palette_pointer Palette0, 8, 1 ; PALETTE_0 + palette_pointer Palette1, 8, 0 ; PALETTE_1 + palette_pointer Palette2, 8, 0 ; PALETTE_2 + palette_pointer Palette3, 8, 0 ; PALETTE_3 + palette_pointer Palette4, 8, 0 ; PALETTE_4 + palette_pointer Palette5, 8, 0 ; PALETTE_5 + palette_pointer Palette6, 8, 0 ; PALETTE_6 + palette_pointer Palette7, 8, 0 ; PALETTE_7 + palette_pointer Palette8, 8, 0 ; PALETTE_8 + palette_pointer Palette9, 8, 0 ; PALETTE_9 + palette_pointer Palette10, 8, 0 ; PALETTE_10 + palette_pointer Palette11, 8, 0 ; PALETTE_11 + palette_pointer Palette12, 8, 0 ; PALETTE_12 + palette_pointer Palette13, 8, 0 ; PALETTE_13 + palette_pointer Palette14, 8, 0 ; PALETTE_14 + palette_pointer Palette15, 8, 0 ; PALETTE_15 + palette_pointer Palette16, 8, 0 ; PALETTE_16 + palette_pointer Palette17, 8, 0 ; PALETTE_17 + palette_pointer Palette18, 8, 0 ; PALETTE_18 + palette_pointer Palette19, 8, 0 ; PALETTE_19 + palette_pointer Palette20, 8, 0 ; PALETTE_20 + palette_pointer Palette21, 8, 0 ; PALETTE_21 + palette_pointer Palette22, 8, 0 ; PALETTE_22 + palette_pointer Palette23, 8, 0 ; PALETTE_23 + palette_pointer Palette24, 8, 0 ; PALETTE_24 + palette_pointer Palette25, 8, 0 ; PALETTE_25 + palette_pointer Palette26, 8, 0 ; PALETTE_26 + palette_pointer Palette27, 8, 0 ; PALETTE_27 + palette_pointer Palette28, 8, 0 ; PALETTE_28 + palette_pointer Palette29, 8, 2 ; PALETTE_29 + palette_pointer Palette30, 8, 2 ; PALETTE_30 + palette_pointer Palette31, 1, 1 ; PALETTE_31 + palette_pointer Palette32, 1, 1 ; PALETTE_32 + palette_pointer Palette33, 1, 1 ; PALETTE_33 + palette_pointer Palette34, 1, 1 ; PALETTE_34 + palette_pointer Palette35, 1, 1 ; PALETTE_35 + palette_pointer Palette36, 1, 1 ; PALETTE_36 + palette_pointer Palette37, 1, 1 ; PALETTE_37 + palette_pointer Palette38, 1, 1 ; PALETTE_38 + palette_pointer Palette39, 1, 1 ; PALETTE_39 + palette_pointer Palette40, 1, 1 ; PALETTE_40 + palette_pointer Palette41, 1, 1 ; PALETTE_41 + palette_pointer Palette42, 1, 1 ; PALETTE_42 + palette_pointer Palette43, 1, 1 ; PALETTE_43 + palette_pointer Palette44, 1, 1 ; PALETTE_44 + palette_pointer Palette45, 1, 1 ; PALETTE_45 + palette_pointer Palette46, 1, 1 ; PALETTE_46 + palette_pointer Palette47, 1, 1 ; PALETTE_47 + palette_pointer Palette48, 1, 1 ; PALETTE_48 + palette_pointer Palette49, 1, 1 ; PALETTE_49 + palette_pointer Palette50, 1, 1 ; PALETTE_50 + palette_pointer Palette51, 1, 1 ; PALETTE_51 + palette_pointer Palette52, 1, 1 ; PALETTE_52 + palette_pointer Palette53, 1, 1 ; PALETTE_53 + palette_pointer Palette54, 1, 1 ; PALETTE_54 + palette_pointer Palette55, 1, 1 ; PALETTE_55 + palette_pointer Palette56, 1, 1 ; PALETTE_56 + palette_pointer Palette57, 1, 1 ; PALETTE_57 + palette_pointer Palette58, 1, 1 ; PALETTE_58 + palette_pointer Palette59, 1, 1 ; PALETTE_59 + palette_pointer Palette60, 1, 1 ; PALETTE_60 + palette_pointer Palette61, 1, 1 ; PALETTE_61 + palette_pointer Palette62, 1, 1 ; PALETTE_62 + palette_pointer Palette63, 1, 1 ; PALETTE_63 + palette_pointer Palette64, 1, 1 ; PALETTE_64 + palette_pointer Palette65, 1, 1 ; PALETTE_65 + palette_pointer Palette66, 1, 1 ; PALETTE_66 + palette_pointer Palette67, 1, 1 ; PALETTE_67 + palette_pointer Palette68, 1, 1 ; PALETTE_68 + palette_pointer Palette69, 1, 1 ; PALETTE_69 + palette_pointer Palette70, 1, 1 ; PALETTE_70 + palette_pointer Palette71, 1, 1 ; PALETTE_71 + palette_pointer Palette72, 1, 1 ; PALETTE_72 + palette_pointer Palette73, 1, 1 ; PALETTE_73 + palette_pointer Palette74, 1, 1 ; PALETTE_74 + palette_pointer Palette75, 1, 1 ; PALETTE_75 + palette_pointer Palette76, 1, 1 ; PALETTE_76 + palette_pointer Palette77, 1, 1 ; PALETTE_77 + palette_pointer Palette78, 1, 1 ; PALETTE_78 + palette_pointer Palette79, 1, 1 ; PALETTE_79 + palette_pointer Palette80, 1, 1 ; PALETTE_80 + palette_pointer Palette81, 1, 1 ; PALETTE_81 + palette_pointer Palette82, 1, 1 ; PALETTE_82 + palette_pointer Palette83, 1, 1 ; PALETTE_83 + palette_pointer Palette84, 1, 1 ; PALETTE_84 + palette_pointer Palette85, 1, 1 ; PALETTE_85 + palette_pointer Palette86, 1, 1 ; PALETTE_86 + palette_pointer Palette87, 1, 1 ; PALETTE_87 + palette_pointer Palette88, 1, 1 ; PALETTE_88 + palette_pointer Palette89, 1, 1 ; PALETTE_89 + palette_pointer Palette90, 1, 1 ; PALETTE_90 + palette_pointer Palette91, 1, 1 ; PALETTE_91 + palette_pointer Palette92, 1, 1 ; PALETTE_92 + palette_pointer Palette93, 1, 1 ; PALETTE_93 + palette_pointer Palette94, 8, 0 ; PALETTE_94 + palette_pointer Palette95, 8, 0 ; PALETTE_95 + palette_pointer Palette96, 8, 0 ; PALETTE_96 + palette_pointer Palette97, 8, 0 ; PALETTE_97 + palette_pointer Palette98, 8, 0 ; PALETTE_98 + palette_pointer Palette99, 8, 0 ; PALETTE_99 + palette_pointer Palette100, 8, 0 ; PALETTE_100 + palette_pointer Palette101, 7, 0 ; PALETTE_101 + palette_pointer Palette102, 7, 0 ; PALETTE_102 + palette_pointer Palette103, 7, 0 ; PALETTE_103 + palette_pointer Palette104, 7, 0 ; PALETTE_104 + palette_pointer Palette105, 7, 0 ; PALETTE_105 + palette_pointer Palette106, 7, 0 ; PALETTE_106 + palette_pointer Palette107, 7, 0 ; PALETTE_107 + palette_pointer Palette108, 0, 1 ; PALETTE_108 + palette_pointer Palette109, 0, 1 ; PALETTE_109 + palette_pointer Palette110, 0, 0 ; PALETTE_110 + palette_pointer Palette111, 8, 1 ; PALETTE_111 + palette_pointer Palette112, 8, 1 ; PALETTE_112 + palette_pointer Palette113, 8, 1 ; PALETTE_113 + palette_pointer Palette114, 4, 2 ; PALETTE_114 + palette_pointer Palette115, 4, 2 ; PALETTE_115 + palette_pointer Palette116, 4, 2 ; PALETTE_116 + palette_pointer Palette117, 1, 0 ; PALETTE_117 + palette_pointer Palette118, 6, 0 ; PALETTE_118 + palette_pointer Palette119, 1, 0 ; PALETTE_119 + palette_pointer Palette120, 1, 0 ; PALETTE_120 + palette_pointer Palette121, 1, 0 ; PALETTE_121 + palette_pointer Palette122, 1, 0 ; PALETTE_122 + palette_pointer Palette123, 1, 0 ; PALETTE_123 + palette_pointer Palette124, 1, 0 ; PALETTE_124 + palette_pointer Palette125, 1, 0 ; PALETTE_125 + palette_pointer Palette126, 1, 0 ; PALETTE_126 + palette_pointer Palette127, 1, 0 ; PALETTE_127 + palette_pointer Palette128, 1, 0 ; PALETTE_128 + palette_pointer Palette129, 1, 0 ; PALETTE_129 + palette_pointer Palette130, 1, 0 ; PALETTE_130 + palette_pointer Palette131, 1, 0 ; PALETTE_131 + palette_pointer Palette132, 1, 0 ; PALETTE_132 + palette_pointer Palette133, 1, 0 ; PALETTE_133 + palette_pointer Palette134, 1, 0 ; PALETTE_134 + palette_pointer Palette135, 1, 0 ; PALETTE_135 + palette_pointer Palette136, 1, 0 ; PALETTE_136 + palette_pointer Palette137, 1, 0 ; PALETTE_137 + palette_pointer Palette138, 1, 0 ; PALETTE_138 + palette_pointer Palette139, 1, 0 ; PALETTE_139 + palette_pointer Palette140, 1, 0 ; PALETTE_140 + palette_pointer Palette141, 1, 0 ; PALETTE_141 + palette_pointer Palette142, 1, 0 ; PALETTE_142 + palette_pointer Palette143, 1, 0 ; PALETTE_143 + palette_pointer Palette144, 1, 0 ; PALETTE_144 + palette_pointer Palette145, 1, 0 ; PALETTE_145 + palette_pointer Palette146, 1, 0 ; PALETTE_146 + palette_pointer Palette147, 1, 0 ; PALETTE_147 + palette_pointer Palette148, 1, 0 ; PALETTE_148 + palette_pointer Palette149, 1, 0 ; PALETTE_149 + palette_pointer Palette150, 1, 0 ; PALETTE_150 + palette_pointer Palette151, 1, 0 ; PALETTE_151 + palette_pointer Palette152, 1, 0 ; PALETTE_152 + palette_pointer Palette153, 1, 0 ; PALETTE_153 + palette_pointer Palette154, 1, 0 ; PALETTE_154 + palette_pointer Palette155, 1, 0 ; PALETTE_155 + palette_pointer Palette156, 1, 0 ; PALETTE_156 + palette_pointer Palette157, 1, 0 ; PALETTE_157 + palette_pointer Palette158, 1, 0 ; PALETTE_158 + palette_pointer Palette159, 1, 0 ; PALETTE_159 + palette_pointer Palette160, 1, 0 ; PALETTE_160 diff --git a/src/engine/gfx/sprite_animations.asm b/src/engine/gfx/sprite_animations.asm new file mode 100644 index 0000000..7c432c6 --- /dev/null +++ b/src/engine/gfx/sprite_animations.asm @@ -0,0 +1,224 @@ +; \1 = anim data pointer +anim_data_pointer: MACRO + dwb \1, BANK(\1) - BANK(SpriteAnimations) + db $00 ; unused (padding?) +ENDM + +SpriteAnimations: + anim_data_pointer AnimData0 ; SPRITE_ANIM_LIGHT_NPC_UP + anim_data_pointer AnimData1 ; SPRITE_ANIM_LIGHT_NPC_RIGHT + anim_data_pointer AnimData2 ; SPRITE_ANIM_LIGHT_NPC_DOWN + anim_data_pointer AnimData3 ; SPRITE_ANIM_LIGHT_NPC_LEFT + anim_data_pointer AnimData4 ; SPRITE_ANIM_DARK_NPC_UP + anim_data_pointer AnimData5 ; SPRITE_ANIM_DARK_NPC_RIGHT + anim_data_pointer AnimData6 ; SPRITE_ANIM_DARK_NPC_DOWN + anim_data_pointer AnimData7 ; SPRITE_ANIM_DARK_NPC_LEFT + anim_data_pointer AnimData8 ; SPRITE_ANIM_SGB_AMY_LAYING + anim_data_pointer AnimData9 ; SPRITE_ANIM_SGB_AMY_STAND + anim_data_pointer AnimData10 ; SPRITE_ANIM_SGB_CLERK_NPC_UP + anim_data_pointer AnimData11 ; SPRITE_ANIM_SGB_CLERK_NPC_RIGHT + anim_data_pointer AnimData12 ; SPRITE_ANIM_SGB_CLERK_NPC_DOWN + anim_data_pointer AnimData13 ; SPRITE_ANIM_SGB_CLERK_NPC_LEFT + anim_data_pointer AnimData14 ; SPRITE_ANIM_BLUE_NPC_UP + anim_data_pointer AnimData15 ; SPRITE_ANIM_BLUE_NPC_RIGHT + anim_data_pointer AnimData16 ; SPRITE_ANIM_BLUE_NPC_DOWN + anim_data_pointer AnimData17 ; SPRITE_ANIM_BLUE_NPC_LEFT + anim_data_pointer AnimData18 ; SPRITE_ANIM_PINK_NPC_UP + anim_data_pointer AnimData19 ; SPRITE_ANIM_PINK_NPC_RIGHT + anim_data_pointer AnimData20 ; SPRITE_ANIM_PINK_NPC_DOWN + anim_data_pointer AnimData21 ; SPRITE_ANIM_PINK_NPC_LEFT + anim_data_pointer AnimData22 ; SPRITE_ANIM_YELLOW_NPC_UP + anim_data_pointer AnimData23 ; SPRITE_ANIM_YELLOW_NPC_RIGHT + anim_data_pointer AnimData24 ; SPRITE_ANIM_YELLOW_NPC_DOWN + anim_data_pointer AnimData25 ; SPRITE_ANIM_YELLOW_NPC_LEFT + anim_data_pointer AnimData26 ; SPRITE_ANIM_GREEN_NPC_UP + anim_data_pointer AnimData27 ; SPRITE_ANIM_GREEN_NPC_RIGHT + anim_data_pointer AnimData28 ; SPRITE_ANIM_GREEN_NPC_DOWN + anim_data_pointer AnimData29 ; SPRITE_ANIM_GREEN_NPC_LEFT + anim_data_pointer AnimData30 ; SPRITE_ANIM_RED_NPC_UP + anim_data_pointer AnimData31 ; SPRITE_ANIM_RED_NPC_RIGHT + anim_data_pointer AnimData32 ; SPRITE_ANIM_RED_NPC_DOWN + anim_data_pointer AnimData33 ; SPRITE_ANIM_RED_NPC_LEFT + anim_data_pointer AnimData34 ; SPRITE_ANIM_PURPLE_NPC_UP + anim_data_pointer AnimData35 ; SPRITE_ANIM_PURPLE_NPC_RIGHT + anim_data_pointer AnimData36 ; SPRITE_ANIM_PURPLE_NPC_DOWN + anim_data_pointer AnimData37 ; SPRITE_ANIM_PURPLE_NPC_LEFT + anim_data_pointer AnimData38 ; SPRITE_ANIM_WHITE_NPC_UP + anim_data_pointer AnimData39 ; SPRITE_ANIM_WHITE_NPC_RIGHT + anim_data_pointer AnimData40 ; SPRITE_ANIM_WHITE_NPC_DOWN + anim_data_pointer AnimData41 ; SPRITE_ANIM_WHITE_NPC_LEFT + anim_data_pointer AnimData42 ; SPRITE_ANIM_INDIGO_NPC_UP + anim_data_pointer AnimData43 ; SPRITE_ANIM_INDIGO_NPC_RIGHT + anim_data_pointer AnimData44 ; SPRITE_ANIM_INDIGO_NPC_DOWN + anim_data_pointer AnimData45 ; SPRITE_ANIM_INDIGO_NPC_LEFT + anim_data_pointer AnimData46 ; SPRITE_ANIM_CGB_AMY_LAYING + anim_data_pointer AnimData47 ; SPRITE_ANIM_CGB_AMY_STAND + anim_data_pointer AnimData48 ; SPRITE_ANIM_CGB_CLERK_NPC_UP + anim_data_pointer AnimData49 ; SPRITE_ANIM_CGB_CLERK_NPC_RIGHT + anim_data_pointer AnimData50 ; SPRITE_ANIM_CGB_CLERK_NPC_DOWN + anim_data_pointer AnimData51 ; SPRITE_ANIM_CGB_CLERK_NPC_LEFT + anim_data_pointer AnimData52 ; SPRITE_ANIM_SGB_VOLCANO_SMOKE + anim_data_pointer AnimData53 ; SPRITE_ANIM_SGB_OWMAP_CURSOR + anim_data_pointer AnimData54 ; SPRITE_ANIM_SGB_OWMAP_CURSOR_FAST + anim_data_pointer AnimData55 ; SPRITE_ANIM_CGB_VOLCANO_SMOKE + anim_data_pointer AnimData56 ; SPRITE_ANIM_CGB_OWMAP_CURSOR + anim_data_pointer AnimData57 ; SPRITE_ANIM_CGB_OWMAP_CURSOR_FAST + anim_data_pointer AnimData58 ; SPRITE_ANIM_TORCH + anim_data_pointer AnimData59 ; SPRITE_ANIM_SGB_CARD_TOP_LEFT + anim_data_pointer AnimData60 ; SPRITE_ANIM_SGB_CARD_TOP_RIGHT + anim_data_pointer AnimData61 ; SPRITE_ANIM_SGB_CARD_LEFT_SPARK + anim_data_pointer AnimData62 ; SPRITE_ANIM_SGB_CARD_BOTTOM_LEFT + anim_data_pointer AnimData63 ; SPRITE_ANIM_SGB_CARD_BOTTOM_RIGHT + anim_data_pointer AnimData64 ; SPRITE_ANIM_SGB_CARD_RIGHT_SPARK + anim_data_pointer AnimData65 ; SPRITE_ANIM_CGB_CARD_TOP_LEFT + anim_data_pointer AnimData66 ; SPRITE_ANIM_CGB_CARD_TOP_RIGHT + anim_data_pointer AnimData67 ; SPRITE_ANIM_CGB_CARD_LEFT_SPARK + anim_data_pointer AnimData68 ; SPRITE_ANIM_CGB_CARD_BOTTOM_LEFT + anim_data_pointer AnimData69 ; SPRITE_ANIM_CGB_CARD_BOTTOM_RIGHT + anim_data_pointer AnimData70 ; SPRITE_ANIM_CGB_CARD_RIGHT_SPARK + anim_data_pointer AnimData71 ; SPRITE_ANIM_71 + anim_data_pointer AnimData72 ; SPRITE_ANIM_72 + anim_data_pointer AnimData73 ; SPRITE_ANIM_73 + anim_data_pointer AnimData74 ; SPRITE_ANIM_74 + anim_data_pointer AnimData75 ; SPRITE_ANIM_75 + anim_data_pointer AnimData76 ; SPRITE_ANIM_76 + anim_data_pointer AnimData77 ; SPRITE_ANIM_77 + anim_data_pointer AnimData78 ; SPRITE_ANIM_78 + anim_data_pointer AnimData79 ; SPRITE_ANIM_79 + anim_data_pointer AnimData80 ; SPRITE_ANIM_80 + anim_data_pointer AnimData81 ; SPRITE_ANIM_81 + anim_data_pointer AnimData82 ; SPRITE_ANIM_82 + anim_data_pointer AnimData83 ; SPRITE_ANIM_83 + anim_data_pointer AnimData84 ; SPRITE_ANIM_84 + anim_data_pointer AnimData85 ; SPRITE_ANIM_85 + anim_data_pointer AnimData86 ; SPRITE_ANIM_86 + anim_data_pointer AnimData87 ; SPRITE_ANIM_87 + anim_data_pointer AnimData88 ; SPRITE_ANIM_88 + anim_data_pointer AnimData89 ; SPRITE_ANIM_89 + anim_data_pointer AnimData90 ; SPRITE_ANIM_90 + anim_data_pointer AnimData91 ; SPRITE_ANIM_91 + anim_data_pointer AnimData92 ; SPRITE_ANIM_92 + anim_data_pointer AnimData93 ; SPRITE_ANIM_93 + anim_data_pointer AnimData94 ; SPRITE_ANIM_94 + anim_data_pointer AnimData95 ; SPRITE_ANIM_95 + anim_data_pointer AnimData96 ; SPRITE_ANIM_96 + anim_data_pointer AnimData97 ; SPRITE_ANIM_97 + anim_data_pointer AnimData98 ; SPRITE_ANIM_98 + anim_data_pointer AnimData99 ; SPRITE_ANIM_99 + anim_data_pointer AnimData100 ; SPRITE_ANIM_100 + anim_data_pointer AnimData101 ; SPRITE_ANIM_101 + anim_data_pointer AnimData102 ; SPRITE_ANIM_102 + anim_data_pointer AnimData103 ; SPRITE_ANIM_103 + anim_data_pointer AnimData104 ; SPRITE_ANIM_104 + anim_data_pointer AnimData105 ; SPRITE_ANIM_105 + anim_data_pointer AnimData106 ; SPRITE_ANIM_106 + anim_data_pointer AnimData107 ; SPRITE_ANIM_107 + anim_data_pointer AnimData108 ; SPRITE_ANIM_108 + anim_data_pointer AnimData109 ; SPRITE_ANIM_109 + anim_data_pointer AnimData110 ; SPRITE_ANIM_110 + anim_data_pointer AnimData111 ; SPRITE_ANIM_111 + anim_data_pointer AnimData112 ; SPRITE_ANIM_112 + anim_data_pointer AnimData113 ; SPRITE_ANIM_113 + anim_data_pointer AnimData114 ; SPRITE_ANIM_114 + anim_data_pointer AnimData115 ; SPRITE_ANIM_115 + anim_data_pointer AnimData116 ; SPRITE_ANIM_116 + anim_data_pointer AnimData117 ; SPRITE_ANIM_117 + anim_data_pointer AnimData118 ; SPRITE_ANIM_118 + anim_data_pointer AnimData119 ; SPRITE_ANIM_119 + anim_data_pointer AnimData120 ; SPRITE_ANIM_120 + anim_data_pointer AnimData121 ; SPRITE_ANIM_121 + anim_data_pointer AnimData122 ; SPRITE_ANIM_122 + anim_data_pointer AnimData123 ; SPRITE_ANIM_123 + anim_data_pointer AnimData124 ; SPRITE_ANIM_124 + anim_data_pointer AnimData125 ; SPRITE_ANIM_125 + anim_data_pointer AnimData126 ; SPRITE_ANIM_126 + anim_data_pointer AnimData127 ; SPRITE_ANIM_127 + anim_data_pointer AnimData128 ; SPRITE_ANIM_128 + anim_data_pointer AnimData129 ; SPRITE_ANIM_129 + anim_data_pointer AnimData130 ; SPRITE_ANIM_130 + anim_data_pointer AnimData131 ; SPRITE_ANIM_131 + anim_data_pointer AnimData132 ; SPRITE_ANIM_132 + anim_data_pointer AnimData133 ; SPRITE_ANIM_133 + anim_data_pointer AnimData134 ; SPRITE_ANIM_134 + anim_data_pointer AnimData135 ; SPRITE_ANIM_135 + anim_data_pointer AnimData136 ; SPRITE_ANIM_136 + anim_data_pointer AnimData137 ; SPRITE_ANIM_137 + anim_data_pointer AnimData138 ; SPRITE_ANIM_138 + anim_data_pointer AnimData139 ; SPRITE_ANIM_139 + anim_data_pointer AnimData140 ; SPRITE_ANIM_140 + anim_data_pointer AnimData141 ; SPRITE_ANIM_141 + anim_data_pointer AnimData142 ; SPRITE_ANIM_142 + anim_data_pointer AnimData143 ; SPRITE_ANIM_143 + anim_data_pointer AnimData144 ; SPRITE_ANIM_144 + anim_data_pointer AnimData145 ; SPRITE_ANIM_145 + anim_data_pointer AnimData146 ; SPRITE_ANIM_146 + anim_data_pointer AnimData147 ; SPRITE_ANIM_147 + anim_data_pointer AnimData148 ; SPRITE_ANIM_148 + anim_data_pointer AnimData149 ; SPRITE_ANIM_149 + anim_data_pointer AnimData150 ; SPRITE_ANIM_150 + anim_data_pointer AnimData151 ; SPRITE_ANIM_151 + anim_data_pointer AnimData152 ; SPRITE_ANIM_152 + anim_data_pointer AnimData153 ; SPRITE_ANIM_153 + anim_data_pointer AnimData154 ; SPRITE_ANIM_154 + anim_data_pointer AnimData155 ; SPRITE_ANIM_155 + anim_data_pointer AnimData156 ; SPRITE_ANIM_156 + anim_data_pointer AnimData157 ; SPRITE_ANIM_157 + anim_data_pointer AnimData158 ; SPRITE_ANIM_158 + anim_data_pointer AnimData159 ; SPRITE_ANIM_159 + anim_data_pointer AnimData160 ; SPRITE_ANIM_160 + anim_data_pointer AnimData161 ; SPRITE_ANIM_161 + anim_data_pointer AnimData162 ; SPRITE_ANIM_162 + anim_data_pointer AnimData163 ; SPRITE_ANIM_163 + anim_data_pointer AnimData164 ; SPRITE_ANIM_164 + anim_data_pointer AnimData165 ; SPRITE_ANIM_165 + anim_data_pointer AnimData166 ; SPRITE_ANIM_166 + anim_data_pointer AnimData167 ; SPRITE_ANIM_167 + anim_data_pointer AnimData168 ; SPRITE_ANIM_168 + anim_data_pointer AnimData169 ; SPRITE_ANIM_169 + anim_data_pointer AnimData170 ; SPRITE_ANIM_170 + anim_data_pointer AnimData171 ; SPRITE_ANIM_171 + anim_data_pointer AnimData172 ; SPRITE_ANIM_172 + anim_data_pointer AnimData173 ; SPRITE_ANIM_173 + anim_data_pointer AnimData174 ; SPRITE_ANIM_174 + anim_data_pointer AnimData175 ; SPRITE_ANIM_175 + anim_data_pointer AnimData176 ; SPRITE_ANIM_176 + anim_data_pointer AnimData177 ; SPRITE_ANIM_177 + anim_data_pointer AnimData178 ; SPRITE_ANIM_178 + anim_data_pointer AnimData179 ; SPRITE_ANIM_179 + anim_data_pointer AnimData180 ; SPRITE_ANIM_180 + anim_data_pointer AnimData181 ; SPRITE_ANIM_181 + anim_data_pointer AnimData182 ; SPRITE_ANIM_182 + anim_data_pointer AnimData183 ; SPRITE_ANIM_183 + anim_data_pointer AnimData184 ; SPRITE_ANIM_184 + anim_data_pointer AnimData185 ; SPRITE_ANIM_185 + anim_data_pointer AnimData186 ; SPRITE_ANIM_186 + anim_data_pointer AnimData187 ; SPRITE_ANIM_187 + anim_data_pointer AnimData188 ; SPRITE_ANIM_188 + anim_data_pointer AnimData189 ; SPRITE_ANIM_189 + anim_data_pointer AnimData190 ; SPRITE_ANIM_190 + anim_data_pointer AnimData191 ; SPRITE_ANIM_191 + anim_data_pointer AnimData192 ; SPRITE_ANIM_192 + anim_data_pointer AnimData193 ; SPRITE_ANIM_193 + anim_data_pointer AnimData194 ; SPRITE_ANIM_194 + anim_data_pointer AnimData195 ; SPRITE_ANIM_195 + anim_data_pointer AnimData196 ; SPRITE_ANIM_196 + anim_data_pointer AnimData197 ; SPRITE_ANIM_197 + anim_data_pointer AnimData198 ; SPRITE_ANIM_198 + anim_data_pointer AnimData199 ; SPRITE_ANIM_199 + anim_data_pointer AnimData200 ; SPRITE_ANIM_200 + anim_data_pointer AnimData201 ; SPRITE_ANIM_201 + anim_data_pointer AnimData202 ; SPRITE_ANIM_202 + anim_data_pointer AnimData203 ; SPRITE_ANIM_203 + anim_data_pointer AnimData204 ; SPRITE_ANIM_204 + anim_data_pointer AnimData205 ; SPRITE_ANIM_205 + anim_data_pointer AnimData206 ; SPRITE_ANIM_206 + anim_data_pointer AnimData207 ; SPRITE_ANIM_207 + anim_data_pointer AnimData208 ; SPRITE_ANIM_208 + anim_data_pointer AnimData209 ; SPRITE_ANIM_209 + anim_data_pointer AnimData210 ; SPRITE_ANIM_210 + anim_data_pointer AnimData211 ; SPRITE_ANIM_211 + anim_data_pointer AnimData212 ; SPRITE_ANIM_212 + anim_data_pointer AnimData213 ; SPRITE_ANIM_213 + anim_data_pointer AnimData214 ; SPRITE_ANIM_214 + anim_data_pointer AnimData215 ; SPRITE_ANIM_215 + anim_data_pointer AnimData216 ; SPRITE_ANIM_216 diff --git a/src/engine/gfx/sprites.asm b/src/engine/gfx/sprites.asm new file mode 100644 index 0000000..c2aa277 --- /dev/null +++ b/src/engine/gfx/sprites.asm @@ -0,0 +1,122 @@ +; \1 = gfx pointer +; \2 = number of tiles +gfx_pointer: MACRO + dwb \1, BANK(\1) - BANK(Sprites) + db \2 +ENDM + +Sprites: + gfx_pointer OWPlayerGfx, $14 ; SPRITE_OW_PLAYER + gfx_pointer OWRonaldGfx, $14 ; SPRITE_OW_RONALD + gfx_pointer OWDrMasonGfx, $14 ; SPRITE_OW_DRMASON + gfx_pointer OWIshiharaGfx, $14 ; SPRITE_OW_ISHIHARA + gfx_pointer OWImakuniGfx, $14 ; SPRITE_OW_IMAKUNI + gfx_pointer OWNikkiGfx, $14 ; SPRITE_OW_NIKKI + gfx_pointer OWRickGfx, $14 ; SPRITE_OW_RICK + gfx_pointer OWKenGfx, $14 ; SPRITE_OW_KEN + gfx_pointer OWAmyGfx, $1b ; SPRITE_OW_AMY + gfx_pointer OWIsaacGfx, $14 ; SPRITE_OW_ISAAC + gfx_pointer OWMitchGfx, $14 ; SPRITE_OW_MITCH + gfx_pointer OWGeneGfx, $14 ; SPRITE_OW_GENE + gfx_pointer OWMurrayGfx, $14 ; SPRITE_OW_MURRAY + gfx_pointer OWCourtneyGfx, $14 ; SPRITE_OW_COURTNEY + gfx_pointer OWSteveGfx, $14 ; SPRITE_OW_STEVE + gfx_pointer OWJackGfx, $14 ; SPRITE_OW_JACK + gfx_pointer OWRodGfx, $14 ; SPRITE_OW_ROD + gfx_pointer OWBoyGfx, $14 ; SPRITE_OW_BOY + gfx_pointer OWLadGfx, $14 ; SPRITE_OW_LAD + gfx_pointer OWSpecsGfx, $14 ; SPRITE_OW_SPECS + gfx_pointer OWButchGfx, $14 ; SPRITE_OW_BUTCH + gfx_pointer OWManiaGfx, $14 ; SPRITE_OW_MANIA + gfx_pointer OWJoshuaGfx, $14 ; SPRITE_OW_JOSHUA + gfx_pointer OWHoodGfx, $14 ; SPRITE_OW_HOOD + gfx_pointer OWTechGfx, $14 ; SPRITE_OW_TECH + gfx_pointer OWChapGfx, $14 ; SPRITE_OW_CHAP + gfx_pointer OWManGfx, $14 ; SPRITE_OW_MAN + gfx_pointer OWPappyGfx, $14 ; SPRITE_OW_PAPPY + gfx_pointer OWGirlGfx, $14 ; SPRITE_OW_GIRL + gfx_pointer OWLass1Gfx, $14 ; SPRITE_OW_LASS1 + gfx_pointer OWLass2Gfx, $14 ; SPRITE_OW_LASS2 + gfx_pointer OWLass3Gfx, $14 ; SPRITE_OW_LASS3 + gfx_pointer OWSwimmerGfx, $14 ; SPRITE_OW_SWIMMER + gfx_pointer OWClerkGfx, $08 ; SPRITE_OW_CLERK + gfx_pointer OWGalGfx, $14 ; SPRITE_OW_GAL + gfx_pointer OWWomanGfx, $14 ; SPRITE_OW_WOMAN + gfx_pointer OWGrannyGfx, $14 ; SPRITE_OW_GRANNY + gfx_pointer OverworldMapOAMGfx, $08 ; SPRITE_OW_MAP_OAM + gfx_pointer Duel0Gfx, $16 ; SPRITE_DUEL_0 + gfx_pointer Duel63Gfx, $0a ; SPRITE_DUEL_63 + gfx_pointer DuelGlowGfx, $0b ; SPRITE_DUEL_GLOW + gfx_pointer Duel1Gfx, $06 ; SPRITE_DUEL_1 + gfx_pointer Duel2Gfx, $08 ; SPRITE_DUEL_2 + gfx_pointer Duel55Gfx, $02 ; SPRITE_DUEL_55 + gfx_pointer Duel58Gfx, $04 ; SPRITE_DUEL_58 + gfx_pointer Duel3Gfx, $09 ; SPRITE_DUEL_3 + gfx_pointer Duel4Gfx, $12 ; SPRITE_DUEL_4 + gfx_pointer Duel5Gfx, $09 ; SPRITE_DUEL_5 + gfx_pointer Duel6Gfx, $11 ; SPRITE_DUEL_6 + gfx_pointer Duel59Gfx, $03 ; SPRITE_DUEL_59 + gfx_pointer Duel7Gfx, $2d ; SPRITE_DUEL_7 + gfx_pointer Duel8Gfx, $0d ; SPRITE_DUEL_8 + gfx_pointer Duel9Gfx, $1c ; SPRITE_DUEL_9 + gfx_pointer Duel10Gfx, $4c ; SPRITE_DUEL_10 + gfx_pointer Duel61Gfx, $03 ; SPRITE_DUEL_61 + gfx_pointer Duel11Gfx, $1b ; SPRITE_DUEL_11 + gfx_pointer Duel12Gfx, $07 ; SPRITE_DUEL_12 + gfx_pointer Duel13Gfx, $0c ; SPRITE_DUEL_13 + gfx_pointer Duel62Gfx, $01 ; SPRITE_DUEL_62 + gfx_pointer Duel14Gfx, $22 ; SPRITE_DUEL_14 + gfx_pointer Duel15Gfx, $20 ; SPRITE_DUEL_15 + gfx_pointer Duel16Gfx, $0a ; SPRITE_DUEL_16 + gfx_pointer Duel17Gfx, $25 ; SPRITE_DUEL_17 + gfx_pointer Duel18Gfx, $18 ; SPRITE_DUEL_18 + gfx_pointer Duel19Gfx, $1b ; SPRITE_DUEL_19 + gfx_pointer Duel20Gfx, $08 ; SPRITE_DUEL_20 + gfx_pointer Duel21Gfx, $0d ; SPRITE_DUEL_21 + gfx_pointer Duel22Gfx, $22 ; SPRITE_DUEL_22 + gfx_pointer Duel23Gfx, $0c ; SPRITE_DUEL_23 + gfx_pointer Duel24Gfx, $25 ; SPRITE_DUEL_24 + gfx_pointer Duel25Gfx, $22 ; SPRITE_DUEL_25 + gfx_pointer Duel26Gfx, $0c ; SPRITE_DUEL_26 + gfx_pointer Duel27Gfx, $4c ; SPRITE_DUEL_27 + gfx_pointer Duel28Gfx, $08 ; SPRITE_DUEL_28 + gfx_pointer Duel29Gfx, $07 ; SPRITE_DUEL_29 + gfx_pointer Duel56Gfx, $01 ; SPRITE_DUEL_56 + gfx_pointer Duel30Gfx, $1a ; SPRITE_DUEL_30 + gfx_pointer Duel31Gfx, $0a ; SPRITE_DUEL_31 + gfx_pointer Duel32Gfx, $2e ; SPRITE_DUEL_32 + gfx_pointer Duel33Gfx, $08 ; SPRITE_DUEL_33 + gfx_pointer Duel34Gfx, $07 ; SPRITE_DUEL_34 + gfx_pointer Duel35Gfx, $1c ; SPRITE_DUEL_35 + gfx_pointer Duel66Gfx, $04 ; SPRITE_DUEL_66 + gfx_pointer Duel36Gfx, $08 ; SPRITE_DUEL_36 + gfx_pointer Duel37Gfx, $0b ; SPRITE_DUEL_37 + gfx_pointer Duel57Gfx, $01 ; SPRITE_DUEL_57 + gfx_pointer Duel38Gfx, $1c ; SPRITE_DUEL_38 + gfx_pointer Duel39Gfx, $16 ; SPRITE_DUEL_39 + gfx_pointer Duel40Gfx, $10 ; SPRITE_DUEL_40 + gfx_pointer Duel41Gfx, $0f ; SPRITE_DUEL_41 + gfx_pointer Duel42Gfx, $07 ; SPRITE_DUEL_42 + gfx_pointer Duel43Gfx, $0a ; SPRITE_DUEL_43 + gfx_pointer Duel44Gfx, $09 ; SPRITE_DUEL_44 + gfx_pointer Duel60Gfx, $02 ; SPRITE_DUEL_60 + gfx_pointer Duel64Gfx, $02 ; SPRITE_DUEL_64 + gfx_pointer Duel45Gfx, $03 ; SPRITE_DUEL_45 + gfx_pointer Duel46Gfx, $08 ; SPRITE_DUEL_46 + gfx_pointer Duel47Gfx, $0f ; SPRITE_DUEL_47 + gfx_pointer Duel48Gfx, $03 ; SPRITE_DUEL_48 + gfx_pointer Duel49Gfx, $05 ; SPRITE_DUEL_49 + gfx_pointer Duel50Gfx, $17 ; SPRITE_DUEL_50 + gfx_pointer Duel51Gfx, $36 ; SPRITE_DUEL_WON_LOST_DRAW + gfx_pointer Duel52Gfx, $0b ; SPRITE_DUEL_52 + gfx_pointer Duel53Gfx, $06 ; SPRITE_DUEL_53 + gfx_pointer Duel54Gfx, $16 ; SPRITE_DUEL_54 + gfx_pointer BoosterPackOAMGfx, $20 ; SPRITE_BOOSTER_PACK_OAM + gfx_pointer PressStartGfx, $14 ; SPRITE_PRESS_START + gfx_pointer GrassGfx, $04 ; SPRITE_GRASS + gfx_pointer FireGfx, $04 ; SPRITE_FIRE + gfx_pointer WaterGfx, $04 ; SPRITE_WATER + gfx_pointer ColorlessGfx, $04 ; SPRITE_COLORLESS + gfx_pointer LightningGfx, $04 ; SPRITE_LIGHTNING + gfx_pointer PsychicGfx, $04 ; SPRITE_PSYCHIC + gfx_pointer FightingGfx, $04 ; SPRITE_FIGHTING diff --git a/src/engine/gfx/tilemaps.asm b/src/engine/gfx/tilemaps.asm new file mode 100644 index 0000000..216cca1 --- /dev/null +++ b/src/engine/gfx/tilemaps.asm @@ -0,0 +1,114 @@ +; \1 = pointer +; \2 = tileset +tilemap: MACRO + dwb \1, BANK(\1) - BANK(Tilemaps) + db \2 +ENDM + +Tilemaps: + tilemap OverworldMapTilemap, TILESET_OVERWORLD_MAP ; TILEMAP_OVERWORLD_MAP + tilemap OverworldMapCGBTilemap, TILESET_OVERWORLD_MAP ; TILEMAP_OVERWORLD_MAP_CGB + tilemap MasonLaboratoryTilemap, TILESET_MASON_LABORATORY ; TILEMAP_MASON_LABORATORY + tilemap MasonLaboratoryCGBTilemap, TILESET_MASON_LABORATORY ; TILEMAP_MASON_LABORATORY_CGB + tilemap ChallengeMachineMapEventTilemap, TILESET_MASON_LABORATORY ; TILEMAP_CHALLENGE_MACHINE_MAP_EVENT + tilemap ChallengeMachineMapEventCGBTilemap,TILESET_MASON_LABORATORY ; TILEMAP_CHALLENGE_MACHINE_MAP_EVENT_CGB + tilemap DeckMachineRoomTilemap, TILESET_MASON_LABORATORY ; TILEMAP_DECK_MACHINE_ROOM + tilemap DeckMachineRoomCGBTilemap, TILESET_MASON_LABORATORY ; TILEMAP_DECK_MACHINE_ROOM_CGB + tilemap DeckMachineMapEventTilemap, TILESET_MASON_LABORATORY ; TILEMAP_DECK_MACHINE_MAP_EVENT + tilemap DeckMachineMapEventCGBTilemap, TILESET_MASON_LABORATORY ; TILEMAP_DECK_MACHINE_MAP_EVENT_CGB + tilemap IshiharaTilemap, TILESET_ISHIHARA ; TILEMAP_ISHIHARA + tilemap IshiharaCGBTilemap, TILESET_ISHIHARA ; TILEMAP_ISHIHARA_CGB + tilemap FightingClubEntranceTilemap, TILESET_CLUB_ENTRANCE ; TILEMAP_FIGHTING_CLUB_ENTRANCE + tilemap FightingClubEntranceCGBTilemap, TILESET_CLUB_ENTRANCE ; TILEMAP_FIGHTING_CLUB_ENTRANCE_CGB + tilemap RockClubEntranceTilemap, TILESET_CLUB_ENTRANCE ; TILEMAP_ROCK_CLUB_ENTRANCE + tilemap RockClubEntranceCGBTilemap, TILESET_CLUB_ENTRANCE ; TILEMAP_ROCK_CLUB_ENTRANCE_CGB + tilemap WaterClubEntranceTilemap, TILESET_CLUB_ENTRANCE ; TILEMAP_WATER_CLUB_ENTRANCE + tilemap WaterClubEntranceCGBTilemap, TILESET_CLUB_ENTRANCE ; TILEMAP_WATER_CLUB_ENTRANCE_CGB + tilemap LightningClubEntranceTilemap, TILESET_CLUB_ENTRANCE ; TILEMAP_LIGHTNING_CLUB_ENTRANCE + tilemap LightningClubEntranceCGBTilemap, TILESET_CLUB_ENTRANCE ; TILEMAP_LIGHTNING_CLUB_ENTRANCE_CGB + tilemap GrassClubEntranceTilemap, TILESET_CLUB_ENTRANCE ; TILEMAP_GRASS_CLUB_ENTRANCE + tilemap GrassClubEntranceCGBTilemap, TILESET_CLUB_ENTRANCE ; TILEMAP_GRASS_CLUB_ENTRANCE_CGB + tilemap PsychicClubEntranceTilemap, TILESET_CLUB_ENTRANCE ; TILEMAP_PSYCHIC_CLUB_ENTRANCE + tilemap PsychicClubEntranceCGBTilemap, TILESET_CLUB_ENTRANCE ; TILEMAP_PSYCHIC_CLUB_ENTRANCE_CGB + tilemap ScienceClubEntranceTilemap, TILESET_CLUB_ENTRANCE ; TILEMAP_SCIENCE_CLUB_ENTRANCE + tilemap ScienceClubEntranceCGBTilemap, TILESET_CLUB_ENTRANCE ; TILEMAP_SCIENCE_CLUB_ENTRANCE_CGB + tilemap FireClubEntranceTilemap, TILESET_CLUB_ENTRANCE ; TILEMAP_FIRE_CLUB_ENTRANCE + tilemap FireClubEntranceCGBTilemap, TILESET_CLUB_ENTRANCE ; TILEMAP_FIRE_CLUB_ENTRANCE_CGB + tilemap ChallengeHallEntranceTilemap, TILESET_CLUB_ENTRANCE ; TILEMAP_CHALLENGE_HALL_ENTRANCE + tilemap ChallengeHallEntranceCGBTilemap, TILESET_CLUB_ENTRANCE ; TILEMAP_CHALLENGE_HALL_ENTRANCE_CGB + tilemap ClubLobbyTilemap, TILESET_CLUB_LOBBY ; TILEMAP_CLUB_LOBBY + tilemap ClubLobbyCGBTilemap, TILESET_CLUB_LOBBY ; TILEMAP_CLUB_LOBBY_CGB + tilemap FightingClubTilemap, TILESET_FIGHTING_CLUB ; TILEMAP_FIGHTING_CLUB + tilemap FightingClubCGBTilemap, TILESET_FIGHTING_CLUB ; TILEMAP_FIGHTING_CLUB_CGB + tilemap RockClubTilemap, TILESET_ROCK_CLUB ; TILEMAP_ROCK_CLUB + tilemap RockClubCGBTilemap, TILESET_ROCK_CLUB ; TILEMAP_ROCK_CLUB_CGB + tilemap WaterClubTilemap, TILESET_WATER_CLUB ; TILEMAP_WATER_CLUB + tilemap WaterClubCGBTilemap, TILESET_WATER_CLUB ; TILEMAP_WATER_CLUB_CGB + tilemap LightningClubTilemap, TILESET_LIGHTNING_CLUB ; TILEMAP_LIGHTNING_CLUB + tilemap LightningClubCGBTilemap, TILESET_LIGHTNING_CLUB ; TILEMAP_LIGHTNING_CLUB_CGB + tilemap GrassClubTilemap, TILESET_GRASS_CLUB ; TILEMAP_GRASS_CLUB + tilemap GrassClubCGBTilemap, TILESET_GRASS_CLUB ; TILEMAP_GRASS_CLUB_CGB + tilemap PsychicClubTilemap, TILESET_PSYCHIC_CLUB ; TILEMAP_PSYCHIC_CLUB + tilemap PsychicClubCGBTilemap, TILESET_PSYCHIC_CLUB ; TILEMAP_PSYCHIC_CLUB_CGB + tilemap ScienceClubTilemap, TILESET_SCIENCE_CLUB ; TILEMAP_SCIENCE_CLUB + tilemap ScienceClubCGBTilemap, TILESET_SCIENCE_CLUB ; TILEMAP_SCIENCE_CLUB_CGB + tilemap FireClubTilemap, TILESET_FIRE_CLUB ; TILEMAP_FIRE_CLUB + tilemap FireClubCGBTilemap, TILESET_FIRE_CLUB ; TILEMAP_FIRE_CLUB_CGB + tilemap ChallengeHallTilemap, TILESET_CHALLENGE_HALL ; TILEMAP_CHALLENGE_HALL + tilemap ChallengeHallCGBTilemap, TILESET_CHALLENGE_HALL ; TILEMAP_CHALLENGE_HALL_CGB + tilemap PokemonDomeEntranceTilemap, TILESET_POKEMON_DOME_ENTRANCE ; TILEMAP_POKEMON_DOME_ENTRANCE + tilemap PokemonDomeEntranceCGBTilemap, TILESET_POKEMON_DOME_ENTRANCE ; TILEMAP_POKEMON_DOME_ENTRANCE_CGB + tilemap PokemonDomeDoorMapEventTilemap, TILESET_POKEMON_DOME_ENTRANCE ; TILEMAP_POKEMON_DOME_DOOR_MAP_EVENT + tilemap PokemonDomeDoorMapEventCGBTilemap, TILESET_POKEMON_DOME_ENTRANCE ; TILEMAP_POKEMON_DOME_DOOR_MAP_EVENT_CGB + tilemap PokemonDomeTilemap, TILESET_POKEMON_DOME ; TILEMAP_POKEMON_DOME + tilemap PokemonDomeCGBTilemap, TILESET_POKEMON_DOME ; TILEMAP_POKEMON_DOME_CGB + tilemap HallOfHonorDoorMapEventTilemap, TILESET_POKEMON_DOME ; TILEMAP_HALL_OF_HONOR_DOOR_MAP_EVENT + tilemap HallOfHonorDoorMapEventCGBTilemap, TILESET_POKEMON_DOME ; TILEMAP_HALL_OF_HONOR_DOOR_MAP_EVENT_CGB + tilemap HallOfHonorTilemap, TILESET_HALL_OF_HONOR ; TILEMAP_HALL_OF_HONOR + tilemap HallOfHonorCGBTilemap, TILESET_HALL_OF_HONOR ; TILEMAP_HALL_OF_HONOR_CGB + tilemap CardPopCGBTilemap, TILESET_CARD_POP ; TILEMAP_CARD_POP_CGB + tilemap CardPopTilemap, TILESET_CARD_POP ; TILEMAP_CARD_POP + tilemap GrassMedalTilemap, TILESET_MEDAL ; TILEMAP_GRASS_MEDAL + tilemap ScienceMedalTilemap, TILESET_MEDAL ; TILEMAP_SCIENCE_MEDAL + tilemap FireMedalTilemap, TILESET_MEDAL ; TILEMAP_FIRE_MEDAL + tilemap WaterMedalTilemap, TILESET_MEDAL ; TILEMAP_WATER_MEDAL + tilemap LightningMedalTilemap, TILESET_MEDAL ; TILEMAP_LIGHTNING_MEDAL + tilemap FightingMedalTilemap, TILESET_MEDAL ; TILEMAP_FIGHTING_MEDAL + tilemap RockMedalTilemap, TILESET_MEDAL ; TILEMAP_ROCK_MEDAL + tilemap PsychicMedalTilemap, TILESET_MEDAL ; TILEMAP_PSYCHIC_MEDAL + tilemap GameBoyLinkCGBTilemap, TILESET_GAMEBOY_LINK ; TILEMAP_GAMEBOY_LINK_CGB + tilemap GameBoyLinkTilemap, TILESET_GAMEBOY_LINK ; TILEMAP_GAMEBOY_LINK + tilemap GameBoyLinkConnectingCGBTilemap, TILESET_GAMEBOY_LINK ; TILEMAP_GAMEBOY_LINK_CONNECTING_CGB + tilemap GameBoyLinkConnectingTilemap, TILESET_GAMEBOY_LINK ; TILEMAP_GAMEBOY_LINK_CONNECTING + tilemap GameBoyPrinterCGBTilemap, TILESET_GAMEBOY_PRINTER ; TILEMAP_GAMEBOY_PRINTER_CGB + tilemap GameBoyPrinterTilemap, TILESET_GAMEBOY_PRINTER ; TILEMAP_GAMEBOY_PRINTER + tilemap ColosseumTilemap, TILESET_COLOSSEUM_1 ; TILEMAP_COLOSSEUM + tilemap ColosseumCGBTilemap, TILESET_COLOSSEUM_2 ; TILEMAP_COLOSSEUM_CGB + tilemap EvolutionTilemap, TILESET_EVOLUTION_1 ; TILEMAP_EVOLUTION + tilemap EvolutionCGBTilemap, TILESET_EVOLUTION_2 ; TILEMAP_EVOLUTION_CGB + tilemap MysteryTilemap, TILESET_MYSTERY_1 ; TILEMAP_MYSTERY + tilemap MysteryCGBTilemap, TILESET_MYSTERY_2 ; TILEMAP_MYSTERY_CGB + tilemap LaboratoryTilemap, TILESET_LABORATORY_1 ; TILEMAP_LABORATORY + tilemap LaboratoryCGBTilemap, TILESET_LABORATORY_2 ; TILEMAP_LABORATORY_CGB + tilemap CharizardIntroTilemap, TILESET_CHARIZARD_INTRO_1 ; TILEMAP_CHARIZARD_INTRO + tilemap CharizardIntroCGBTilemap, TILESET_CHARIZARD_INTRO_2 ; TILEMAP_CHARIZARD_INTRO_CGB + tilemap ScytherIntroTilemap, TILESET_SCYTHER_INTRO_1 ; TILEMAP_SCYTHER_INTRO + tilemap ScytherIntroCGBTilemap, TILESET_SCYTHER_INTRO_2 ; TILEMAP_SCYTHER_INTRO_CGB + tilemap AerodactylIntroTilemap, TILESET_AERODACTYL_INTRO_1 ; TILEMAP_AERODACTYL_INTRO + tilemap AerodactylIntroCGBTilemap, TILESET_AERODACTYL_INTRO_2 ; TILEMAP_AERODACTYL_INTRO_CGB + tilemap JapaneseTitleScreenTilemap, TILESET_JAPANESE_TITLE_SCREEN ; TILEMAP_JAPANESE_TITLE_SCREEN + tilemap JapaneseTitleScreenCGBTilemap, TILESET_JAPANESE_TITLE_SCREEN_CGB ; TILEMAP_JAPANESE_TITLE_SCREEN_CGB + tilemap SolidTiles1Tilemap, TILESET_SOLID_TILES_1 ; TILEMAP_SOLID_TILES_1 + tilemap SolidTiles2Tilemap, TILESET_SOLID_TILES_1 ; TILEMAP_SOLID_TILES_2 + tilemap SolidTiles3Tilemap, TILESET_SOLID_TILES_1 ; TILEMAP_SOLID_TILES_3 + tilemap JapaneseTitleScreen2Tilemap, TILESET_JAPANESE_TITLE_SCREEN_2 ; TILEMAP_JAPANESE_TITLE_SCREEN_2 + tilemap JapaneseTitleScreen2CGBTilemap, TILESET_JAPANESE_TITLE_SCREEN_2_CGB ; TILEMAP_JAPANESE_TITLE_SCREEN_2_CGB + tilemap SolidTiles4Tilemap, TILESET_SOLID_TILES_2 ; TILEMAP_SOLID_TILES_4 + tilemap PlayerTilemap, TILESET_PLAYER ; TILEMAP_PLAYER + tilemap OpponentTilemap, TILESET_RONALD ; TILEMAP_OPPONENT + tilemap TitleScreenTilemap, TILESET_TITLE_SCREEN ; TILEMAP_TITLE_SCREEN + tilemap TitleScreenCGBTilemap, TILESET_TITLE_SCREEN_CGB ; TILEMAP_TITLE_SCREEN_CGB + tilemap CopyrightTilemap, TILESET_COPYRIGHT ; TILEMAP_COPYRIGHT + tilemap CopyrightCGBTilemap, TILESET_COPYRIGHT ; TILEMAP_COPYRIGHT_CGB + tilemap NintendoTilemap, TILESET_NINTENDO ; TILEMAP_NINTENDO + tilemap CompaniesTilemap, TILESET_COMPANIES ; TILEMAP_COMPANIES diff --git a/src/engine/gfx/tilesets.asm b/src/engine/gfx/tilesets.asm new file mode 100644 index 0000000..0b4f0af --- /dev/null +++ b/src/engine/gfx/tilesets.asm @@ -0,0 +1,95 @@ +; \1 = pointer +; \2 = number of tiles +tileset: MACRO + dwb \1, BANK(\1) - BANK(Tilesets) + db \2 +ENDM + +Tilesets: + tileset OverworldMapTiles, 193 ; TILESET_OVERWORLD_MAP + tileset MasonLaboratoryTilesetGfx, 151 ; TILESET_MASON_LABORATORY + tileset IshiharaTilesetGfx, 77 ; TILESET_ISHIHARA + tileset ClubEntranceTilesetGfx, 129 ; TILESET_CLUB_ENTRANCE + tileset ClubLobbyTilesetGfx, 120 ; TILESET_CLUB_LOBBY + tileset FightingClubTilesetGfx, 99 ; TILESET_FIGHTING_CLUB + tileset RockClubTilesetGfx, 60 ; TILESET_ROCK_CLUB + tileset WaterClubTilesetGfx, 161 ; TILESET_WATER_CLUB + tileset LightningClubTilesetGfx, 131 ; TILESET_LIGHTNING_CLUB + tileset GrassClubTilesetGfx, 87 ; TILESET_GRASS_CLUB + tileset PsychicClubTilesetGfx, 58 ; TILESET_PSYCHIC_CLUB + tileset ScienceClubTilesetGfx, 82 ; TILESET_SCIENCE_CLUB + tileset FireClubTilesetGfx, 87 ; TILESET_FIRE_CLUB + tileset ChallengeHallTilesetGfx, 157 ; TILESET_CHALLENGE_HALL + tileset PokemonDomeEntranceTilesetGfx, 78 ; TILESET_POKEMON_DOME_ENTRANCE + tileset PokemonDomeTilesetGfx, 207 ; TILESET_POKEMON_DOME + tileset HallOfHonorTilesetGfx, 121 ; TILESET_HALL_OF_HONOR + tileset CardPopGfx, 189 ; TILESET_CARD_POP + tileset MedalGfx, 72 ; TILESET_MEDAL + tileset GameBoyLinkGfx, 109 ; TILESET_GAMEBOY_LINK + tileset GameBoyPrinterGfx, 93 ; TILESET_GAMEBOY_PRINTER + tileset Colosseum1Gfx, 96 ; TILESET_COLOSSEUM_1 + tileset Colosseum2Gfx, 86 ; TILESET_COLOSSEUM_2 + tileset Evolution1Gfx, 96 ; TILESET_EVOLUTION_1 + tileset Evolution2Gfx, 86 ; TILESET_EVOLUTION_2 + tileset Mystery1Gfx, 96 ; TILESET_MYSTERY_1 + tileset Mystery2Gfx, 86 ; TILESET_MYSTERY_2 + tileset Laboratory1Gfx, 96 ; TILESET_LABORATORY_1 + tileset Laboratory2Gfx, 86 ; TILESET_LABORATORY_2 + tileset CharizardIntro1Gfx, 96 ; TILESET_CHARIZARD_INTRO_1 + tileset CharizardIntro2Gfx, 96 ; TILESET_CHARIZARD_INTRO_2 + tileset ScytherIntro1Gfx, 96 ; TILESET_SCYTHER_INTRO_1 + tileset ScytherIntro2Gfx, 96 ; TILESET_SCYTHER_INTRO_2 + tileset AerodactylIntro1Gfx, 96 ; TILESET_AERODACTYL_INTRO_1 + tileset AerodactylIntro2Gfx, 96 ; TILESET_AERODACTYL_INTRO_2 + tileset JapaneseTitleScreenGfx, 97 ; TILESET_JAPANESE_TITLE_SCREEN + tileset JapaneseTitleScreenCGBGfx, 97 ; TILESET_JAPANESE_TITLE_SCREEN_CGB + tileset SolidTiles1, 4 ; TILESET_SOLID_TILES_1 + tileset JapaneseTitleScreen2Gfx, 244 ; TILESET_JAPANESE_TITLE_SCREEN_2 + tileset JapaneseTitleScreen2CGBGfx, 59 ; TILESET_JAPANESE_TITLE_SCREEN_2_CGB + tileset SolidTiles2, 4 ; TILESET_SOLID_TILES_2 + tileset PlayerGfx, 36 ; TILESET_PLAYER + tileset RonaldGfx, 36 ; TILESET_RONALD + tileset TitleScreenGfx, 220 ; TILESET_TITLE_SCREEN + tileset TitleScreenCGBGfx, 212 ; TILESET_TITLE_SCREEN_CGB + tileset CopyrightGfx, 36 ; TILESET_COPYRIGHT + tileset NintendoGfx, 24 ; TILESET_NINTENDO + tileset CompaniesGfx, 49 ; TILESET_COMPANIES + tileset SamGfx, 36 ; TILESET_SAM + tileset ImakuniGfx, 36 ; TILESET_IMAKUNI + tileset NikkiGfx, 36 ; TILESET_NIKKI + tileset RickGfx, 36 ; TILESET_RICK + tileset KenGfx, 36 ; TILESET_KEN + tileset AmyGfx, 36 ; TILESET_AMY + tileset IsaacGfx, 36 ; TILESET_ISAAC + tileset MitchGfx, 36 ; TILESET_MITCH + tileset GeneGfx, 36 ; TILESET_GENE + tileset MurrayGfx, 36 ; TILESET_MURRAY + tileset CourtneyGfx, 36 ; TILESET_COURTNEY + tileset SteveGfx, 36 ; TILESET_STEVE + tileset JackGfx, 36 ; TILESET_JACK + tileset RodGfx, 36 ; TILESET_ROD + tileset JosephGfx, 36 ; TILESET_JOSEPH + tileset DavidGfx, 36 ; TILESET_DAVID + tileset ErikGfx, 36 ; TILESET_ERIK + tileset JohnGfx, 36 ; TILESET_JOHN + tileset AdamGfx, 36 ; TILESET_ADAM + tileset JonathanGfx, 36 ; TILESET_JONATHAN + tileset JoshuaGfx, 36 ; TILESET_JOSHUA + tileset NicholasGfx, 36 ; TILESET_NICHOLAS + tileset BrandonGfx, 36 ; TILESET_BRANDON + tileset MatthewGfx, 36 ; TILESET_MATTHEW + tileset RyanGfx, 36 ; TILESET_RYAN + tileset AndrewGfx, 36 ; TILESET_ANDREW + tileset ChrisGfx, 36 ; TILESET_CHRIS + tileset MichaelGfx, 36 ; TILESET_MICHAEL + tileset DanielGfx, 36 ; TILESET_DANIEL + tileset RobertGfx, 36 ; TILESET_ROBERT + tileset BrittanyGfx, 36 ; TILESET_BRITTANY + tileset KristinGfx, 36 ; TILESET_KRISTIN + tileset HeatherGfx, 36 ; TILESET_HEATHER + tileset SaraGfx, 36 ; TILESET_SARA + tileset AmandaGfx, 36 ; TILESET_AMANDA + tileset JenniferGfx, 36 ; TILESET_JENNIFER + tileset JessicaGfx, 36 ; TILESET_JESSICA + tileset StephanieGfx, 36 ; TILESET_STEPHANIE + tileset AaronGfx, 36 ; TILESET_AARON diff --git a/src/gfx.asm b/src/gfx.asm index 6df6203..1b96723 100644 --- a/src/gfx.asm +++ b/src/gfx.asm @@ -5,7 +5,7 @@ SECTION "Gfx 1", ROMX Fonts:: -FullWidthFonts:: ; 74000 (1d:4000) +FullWidthFonts:: INCBIN "gfx/fonts/full_width/0_0_katakana.1bpp" INCBIN "gfx/fonts/full_width/0_1_hiragana.1bpp" INCBIN "gfx/fonts/full_width/0_2_digits_kanji1.1bpp" @@ -14,36 +14,36 @@ INCBIN "gfx/fonts/full_width/2_kanji3.1bpp" INCBIN "gfx/fonts/full_width/3.1bpp" INCBIN "gfx/fonts/full_width/4.1bpp" -HalfWidthFont:: ; 76668 (1d:6668) +HalfWidthFont:: INCBIN "gfx/fonts/half_width.1bpp" -SymbolsFont:: ; 76968 (1d:6968) +SymbolsFont:: INCBIN "gfx/fonts/symbols.2bpp" DuelGraphics:: -DuelCardHeaderGraphics:: ; 76ce8 (1d:6ce8) +DuelCardHeaderGraphics:: INCBIN "gfx/duel/card_headers.2bpp" -DuelDmgSgbSymbolGraphics:: ; 76fe8 (1d:6fe8) +DuelDmgSgbSymbolGraphics:: INCBIN "gfx/duel/dmg_sgb_symbols.2bpp" -DuelCgbSymbolGraphics:: ; 777f8 (1d:77f8) +DuelCgbSymbolGraphics:: INCBIN "gfx/duel/cgb_symbols.2bpp", $0, $808 SECTION "Gfx 2", ROMX INCBIN "gfx/duel/cgb_symbols.2bpp", $808, $8 -DuelOtherGraphics:: ; 78008 (1e:4008) +DuelOtherGraphics:: INCBIN "gfx/duel/other.2bpp" -DuelBoxMessages:: ; 78318 (1e:4318) +DuelBoxMessages:: INCBIN "gfx/duel/box_messages.2bpp" SECTION "Gfx 3", ROMX -WaterClubTilemap:: ; 84000 (21:4000) +WaterClubTilemap:: db $1c ; width db $20 ; height dw WaterClubPermissions @@ -52,7 +52,7 @@ WaterClubTilemap:: ; 84000 (21:4000) WaterClubPermissions: INCBIN "data/maps/permissions/water_club.bin" -WaterClubCGBTilemap:: ; 84188 (21:4188) +WaterClubCGBTilemap:: db $1c ; width db $20 ; height dw WaterClubCGBPermissions @@ -61,7 +61,7 @@ WaterClubCGBTilemap:: ; 84188 (21:4188) WaterClubCGBPermissions: INCBIN "data/maps/permissions/water_club_cgb.bin" -LightningClubTilemap:: ; 843bb (21:43bb) +LightningClubTilemap:: db $1c ; width db $20 ; height dw LightningClubPermissions @@ -70,7 +70,7 @@ LightningClubTilemap:: ; 843bb (21:43bb) LightningClubPermissions: INCBIN "data/maps/permissions/lightning_club.bin" -LightningClubCGBTilemap:: ; 84533 (21:4533) +LightningClubCGBTilemap:: db $1c ; width db $20 ; height dw LightningClubCGBPermissions @@ -79,7 +79,7 @@ LightningClubCGBTilemap:: ; 84533 (21:4533) LightningClubCGBPermissions: INCBIN "data/maps/permissions/lightning_club_cgb.bin" -GrassClubTilemap:: ; 8472e (21:472e) +GrassClubTilemap:: db $1c ; width db $20 ; height dw GrassClubPermissions @@ -88,7 +88,7 @@ GrassClubTilemap:: ; 8472e (21:472e) GrassClubPermissions: INCBIN "data/maps/permissions/grass_club.bin" -GrassClubCGBTilemap:: ; 848d8 (21:48d8) +GrassClubCGBTilemap:: db $1c ; width db $20 ; height dw GrassClubCGBPermissions @@ -97,7 +97,7 @@ GrassClubCGBTilemap:: ; 848d8 (21:48d8) GrassClubCGBPermissions: INCBIN "data/maps/permissions/grass_club_cgb.bin" -PsychicClubTilemap:: ; 84b73 (21:4b73) +PsychicClubTilemap:: db $1c ; width db $1c ; height dw PsychicClubPermissions @@ -106,7 +106,7 @@ PsychicClubTilemap:: ; 84b73 (21:4b73) PsychicClubPermissions: INCBIN "data/maps/permissions/psychic_club.bin" -PsychicClubCGBTilemap:: ; 84c6f (21:4c6f) +PsychicClubCGBTilemap:: db $1c ; width db $1c ; height dw PsychicClubCGBPermissions @@ -115,7 +115,7 @@ PsychicClubCGBTilemap:: ; 84c6f (21:4c6f) PsychicClubCGBPermissions: INCBIN "data/maps/permissions/psychic_club_cgb.bin" -ScienceClubTilemap:: ; 84dfe (21:4dfe) +ScienceClubTilemap:: db $1c ; width db $20 ; height dw ScienceClubPermissions @@ -124,7 +124,7 @@ ScienceClubTilemap:: ; 84dfe (21:4dfe) ScienceClubPermissions: INCBIN "data/maps/permissions/science_club.bin" -ScienceClubCGBTilemap:: ; 84f1d (21:4f1d) +ScienceClubCGBTilemap:: db $1c ; width db $20 ; height dw ScienceClubCGBPermissions @@ -133,7 +133,7 @@ ScienceClubCGBTilemap:: ; 84f1d (21:4f1d) ScienceClubCGBPermissions: INCBIN "data/maps/permissions/science_club_cgb.bin" -FireClubTilemap:: ; 850b6 (21:50b6) +FireClubTilemap:: db $1c ; width db $20 ; height dw FireClubPermissions @@ -142,7 +142,7 @@ FireClubTilemap:: ; 850b6 (21:50b6) FireClubPermissions: INCBIN "data/maps/permissions/fire_club.bin" -FireClubCGBTilemap:: ; 85191 (21:5191) +FireClubCGBTilemap:: db $1c ; width db $20 ; height dw FireClubCGBPermissions @@ -151,7 +151,7 @@ FireClubCGBTilemap:: ; 85191 (21:5191) FireClubCGBPermissions: INCBIN "data/maps/permissions/fire_club_cgb.bin" -ChallengeHallTilemap:: ; 85315 (21:5315) +ChallengeHallTilemap:: db $20 ; width db $20 ; height dw ChallengeHallPermissions @@ -160,7 +160,7 @@ ChallengeHallTilemap:: ; 85315 (21:5315) ChallengeHallPermissions: INCBIN "data/maps/permissions/challenge_hall.bin" -ChallengeHallCGBTilemap:: ; 854b3 (21:54b3) +ChallengeHallCGBTilemap:: db $20 ; width db $20 ; height dw ChallengeHallCGBPermissions @@ -169,7 +169,7 @@ ChallengeHallCGBTilemap:: ; 854b3 (21:54b3) ChallengeHallCGBPermissions: INCBIN "data/maps/permissions/challenge_hall_cgb.bin" -PokemonDomeEntranceTilemap:: ; 8570a (21:570a) +PokemonDomeEntranceTilemap:: db $20 ; width db $12 ; height dw PokemonDomeEntrancePermissions @@ -178,7 +178,7 @@ PokemonDomeEntranceTilemap:: ; 8570a (21:570a) PokemonDomeEntrancePermissions: INCBIN "data/maps/permissions/pokemon_dome_entrance.bin" -PokemonDomeEntranceCGBTilemap:: ; 857ce (21:57ce) +PokemonDomeEntranceCGBTilemap:: db $20 ; width db $12 ; height dw PokemonDomeEntranceCGBPermissions @@ -187,7 +187,7 @@ PokemonDomeEntranceCGBTilemap:: ; 857ce (21:57ce) PokemonDomeEntranceCGBPermissions: INCBIN "data/maps/permissions/pokemon_dome_entrance_cgb.bin" -PokemonDomeTilemap:: ; 858ef (21:58ef) +PokemonDomeTilemap:: db $20 ; width db $20 ; height dw PokemonDomePermissions @@ -196,7 +196,7 @@ PokemonDomeTilemap:: ; 858ef (21:58ef) PokemonDomePermissions: INCBIN "data/maps/permissions/pokemon_dome.bin" -PokemonDomeCGBTilemap:: ; 85a79 (21:5a79) +PokemonDomeCGBTilemap:: db $20 ; width db $20 ; height dw PokemonDomeCGBPermissions @@ -205,7 +205,7 @@ PokemonDomeCGBTilemap:: ; 85a79 (21:5a79) PokemonDomeCGBPermissions: INCBIN "data/maps/permissions/pokemon_dome_cgb.bin" -HallOfHonorTilemap:: ; 85ce2 (21:5ce2) +HallOfHonorTilemap:: db $18 ; width db $1a ; height dw HallOfHonorPermissions @@ -214,7 +214,7 @@ HallOfHonorTilemap:: ; 85ce2 (21:5ce2) HallOfHonorPermissions: INCBIN "data/maps/permissions/hall_of_honor.bin" -HallOfHonorCGBTilemap:: ; 85df4 (21:5df4) +HallOfHonorCGBTilemap:: db $18 ; width db $1a ; height dw HallOfHonorCGBPermissions @@ -223,393 +223,393 @@ HallOfHonorCGBTilemap:: ; 85df4 (21:5df4) HallOfHonorCGBPermissions: INCBIN "data/maps/permissions/hall_of_honor_cgb.bin" -CardPopCGBTilemap:: ; 85f7c (21:5f7c) +CardPopCGBTilemap:: db $14 ; width db $12 ; height dw NULL db TRUE ; cgb mode INCBIN "data/maps/tiles/card_pop_cgb.bin" -CardPopTilemap:: ; 8607f (21:607f) +CardPopTilemap:: db $14 ; width db $12 ; height dw NULL db TRUE ; cgb mode INCBIN "data/maps/tiles/card_pop.bin" -ScienceMedalTilemap:: ; 8617d (21:617d) +ScienceMedalTilemap:: db $03 ; width db $03 ; height dw NULL db TRUE ; cgb mode INCBIN "data/maps/tiles/science_medal.bin" -FireMedalTilemap:: ; 86193 (21:6193) +FireMedalTilemap:: db $03 ; width db $03 ; height dw NULL db TRUE ; cgb mode INCBIN "data/maps/tiles/fire_medal.bin" -WaterMedalTilemap:: ; 861a9 (21:61a9) +WaterMedalTilemap:: db $03 ; width db $03 ; height dw NULL db TRUE ; cgb mode INCBIN "data/maps/tiles/water_medal.bin" -LightningMedalTilemap:: ; 861bf (21:61bf) +LightningMedalTilemap:: db $03 ; width db $03 ; height dw NULL db TRUE ; cgb mode INCBIN "data/maps/tiles/lightning_medal.bin" -FightingMedalTilemap:: ; 861d5 (21:61d5) +FightingMedalTilemap:: db $03 ; width db $03 ; height dw NULL db TRUE ; cgb mode INCBIN "data/maps/tiles/fighting_medal.bin" -RockMedalTilemap:: ; 861eb (21:61eb) +RockMedalTilemap:: db $03 ; width db $03 ; height dw NULL db TRUE ; cgb mode INCBIN "data/maps/tiles/rock_medal.bin" -PsychicMedalTilemap:: ; 86201 (21:6201) +PsychicMedalTilemap:: db $03 ; width db $03 ; height dw NULL db TRUE ; cgb mode INCBIN "data/maps/tiles/psychic_medal.bin" -GameBoyLinkCGBTilemap:: ; 86217 (21:6217) +GameBoyLinkCGBTilemap:: db $14 ; width db $12 ; height dw NULL db TRUE ; cgb mode INCBIN "data/maps/tiles/gameboy_link_cgb.bin" -GameBoyLinkTilemap:: ; 862da (21:62da) +GameBoyLinkTilemap:: db $14 ; width db $12 ; height dw NULL db FALSE ; cgb mode INCBIN "data/maps/tiles/gameboy_link.bin" -GameBoyLinkConnectingCGBTilemap:: ; 86364 (21:6364) +GameBoyLinkConnectingCGBTilemap:: db $14 ; width db $12 ; height dw NULL db TRUE ; cgb mode INCBIN "data/maps/tiles/gameboy_link_connecting_cgb.bin" -GameBoyLinkConnectingTilemap:: ; 86443 (21:6443) +GameBoyLinkConnectingTilemap:: db $14 ; width db $12 ; height dw NULL db FALSE ; cgb mode INCBIN "data/maps/tiles/gameboy_link_connecting.bin" -GameBoyPrinterCGBTilemap:: ; 864df (21:64df) +GameBoyPrinterCGBTilemap:: db $14 ; width db $12 ; height dw NULL db TRUE ; cgb mode INCBIN "data/maps/tiles/gameboy_printer_cgb.bin" -GameBoyPrinterTilemap:: ; 865b5 (21:65b5) +GameBoyPrinterTilemap:: db $14 ; width db $12 ; height dw NULL db FALSE ; cgb mode INCBIN "data/maps/tiles/gameboy_printer.bin" -ColosseumTilemap:: ; 86647 (21:6647) +ColosseumTilemap:: db $08 ; width db $0c ; height dw NULL db FALSE ; cgb mode INCBIN "data/maps/tiles/colosseum.bin" -ColosseumCGBTilemap:: ; 866b8 (21:66b8) +ColosseumCGBTilemap:: db $08 ; width db $0c ; height dw NULL db TRUE ; cgb mode INCBIN "data/maps/tiles/colosseum_cgb.bin" -EvolutionTilemap:: ; 8673e (21:673e) +EvolutionTilemap:: db $08 ; width db $0c ; height dw NULL db FALSE ; cgb mode INCBIN "data/maps/tiles/evolution.bin" -EvolutionCGBTilemap:: ; 867af (21:67af) +EvolutionCGBTilemap:: db $08 ; width db $0c ; height dw NULL db TRUE ; cgb mode INCBIN "data/maps/tiles/evolution_cgb.bin" -MysteryTilemap:: ; 86833 (21:6833) +MysteryTilemap:: db $08 ; width db $0c ; height dw NULL db FALSE ; cgb mode INCBIN "data/maps/tiles/mystery.bin" -MysteryCGBTilemap:: ; 868a4 (21:68a4) +MysteryCGBTilemap:: db $08 ; width db $0c ; height dw NULL db TRUE ; cgb mode INCBIN "data/maps/tiles/mystery_cgb.bin" -LaboratoryTilemap:: ; 86925 (21:6925) +LaboratoryTilemap:: db $08 ; width db $0c ; height dw NULL db FALSE ; cgb mode INCBIN "data/maps/tiles/laboratory.bin" -LaboratoryCGBTilemap:: ; 86996 (21:6996) +LaboratoryCGBTilemap:: db $08 ; width db $0c ; height dw NULL db TRUE ; cgb mode INCBIN "data/maps/tiles/laboratory_cgb.bin" -CharizardIntroTilemap:: ; 86a14 (21:6a14) +CharizardIntroTilemap:: db $08 ; width db $0c ; height dw NULL db FALSE ; cgb mode INCBIN "data/maps/tiles/charizard_intro.bin" -CharizardIntroCGBTilemap:: ; 86a85 (21:6a85) +CharizardIntroCGBTilemap:: db $08 ; width db $0c ; height dw NULL db TRUE ; cgb mode INCBIN "data/maps/tiles/charizard_intro_cgb.bin" -ScytherIntroTilemap:: ; 86b28 (21:6b28) +ScytherIntroTilemap:: db $08 ; width db $0c ; height dw NULL db FALSE ; cgb mode INCBIN "data/maps/tiles/scyther_intro.bin" -ScytherIntroCGBTilemap:: ; 86b99 (21:6b99) +ScytherIntroCGBTilemap:: db $08 ; width db $0c ; height dw NULL db TRUE ; cgb mode INCBIN "data/maps/tiles/scyther_intro_cgb.bin" -AerodactylIntroTilemap:: ; 86c34 (21:6c34) +AerodactylIntroTilemap:: db $08 ; width db $0c ; height dw NULL db FALSE ; cgb mode INCBIN "data/maps/tiles/aerodactyl_intro.bin" -AerodactylIntroCGBTilemap:: ; 86ca5 (21:6ca5) +AerodactylIntroCGBTilemap:: db $08 ; width db $0c ; height dw NULL db TRUE ; cgb mode INCBIN "data/maps/tiles/aerodactyl_intro_cgb.bin" -JapaneseTitleScreenTilemap:: ; 86d37 (21:6d37) +JapaneseTitleScreenTilemap:: db $14 ; width db $12 ; height dw NULL db FALSE ; cgb mode INCBIN "data/maps/tiles/japanese_title_screen.bin" -JapaneseTitleScreenCGBTilemap:: ; 86dcc (21:6dcc) +JapaneseTitleScreenCGBTilemap:: db $14 ; width db $12 ; height dw NULL db TRUE ; cgb mode INCBIN "data/maps/tiles/japanese_title_screen_cgb.bin" -SolidTiles1Tilemap:: ; 86e8a (21:6e8a) +SolidTiles1Tilemap:: db $14 ; width db $12 ; height dw NULL db TRUE ; cgb mode INCBIN "data/maps/tiles/solid_tiles_1.bin" -SolidTiles2Tilemap:: ; 86f18 (21:6f18) +SolidTiles2Tilemap:: db $14 ; width db $12 ; height dw NULL db TRUE ; cgb mode INCBIN "data/maps/tiles/solid_tiles_2.bin" -SolidTiles3Tilemap:: ; 86fc0 (21:6fc0) +SolidTiles3Tilemap:: db $14 ; width db $12 ; height dw NULL db TRUE ; cgb mode INCBIN "data/maps/tiles/solid_tiles_3.bin" -JapaneseTitleScreen2Tilemap:: ; 8704f (21:704f) +JapaneseTitleScreen2Tilemap:: db $14 ; width db $12 ; height dw NULL db FALSE ; cgb mode INCBIN "data/maps/tiles/japanese_title_screen_2.bin" -JapaneseTitleScreen2CGBTilemap:: ; 871a5 (21:71a5) +JapaneseTitleScreen2CGBTilemap:: db $14 ; width db $12 ; height dw NULL db TRUE ; cgb mode INCBIN "data/maps/tiles/japanese_title_screen_2_cgb.bin" -SolidTiles4Tilemap:: ; 87397 (21:7397) +SolidTiles4Tilemap:: db $08 ; width db $04 ; height dw NULL db TRUE ; cgb mode INCBIN "data/maps/tiles/solid_tiles_4.bin" -PlayerTilemap:: ; 873b7 (21:73b7) +PlayerTilemap:: db $06 ; width db $06 ; height dw NULL db FALSE ; cgb mode INCBIN "data/maps/tiles/player.bin" -OpponentTilemap:: ; 873e5 (21:73e5) +OpponentTilemap:: db $06 ; width db $06 ; height dw NULL db FALSE ; cgb mode INCBIN "data/maps/tiles/opponent.bin" -TitleScreenTilemap:: ; 87413 (21:7413) +TitleScreenTilemap:: db $14 ; width db $12 ; height dw NULL db FALSE ; cgb mode INCBIN "data/maps/tiles/title_screen.bin" -TitleScreenCGBTilemap:: ; 87538 (21:7538) +TitleScreenCGBTilemap:: db $14 ; width db $12 ; height dw NULL db TRUE ; cgb mode INCBIN "data/maps/tiles/title_screen_cgb.bin" -CopyrightTilemap:: ; 8769f (21:769f) +CopyrightTilemap:: db $14 ; width db $12 ; height dw NULL db FALSE ; cgb mode INCBIN "data/maps/tiles/copyright.bin" -CopyrightCGBTilemap:: ; 876f6 (21:76f6) +CopyrightCGBTilemap:: db $14 ; width db $12 ; height dw NULL db TRUE ; cgb mode INCBIN "data/maps/tiles/copyright_cgb.bin" -NintendoTilemap:: ; 8777c (21:777c) +NintendoTilemap:: db $14 ; width db $12 ; height dw NULL db FALSE ; cgb mode INCBIN "data/maps/tiles/nintendo.bin" -CompaniesTilemap:: ; 877c4 (21:77c4) +CompaniesTilemap:: db $14 ; width db $12 ; height dw NULL db FALSE ; cgb mode INCBIN "data/maps/tiles/companies.bin" -IshiharaTilesetGfx:: ; 87828 (21:7828) +IshiharaTilesetGfx:: dw 77 INCBIN "gfx/tilesets/ishihara.2bpp" -SolidTiles1:: ; 87cfa (21:7cfa) +SolidTiles1:: dw 4 INCBIN "gfx/solid_tiles.2bpp" -SolidTiles2:: ; 87d3c (21:7d3c) +SolidTiles2:: dw 4 INCBIN "gfx/solid_tiles.2bpp" -PlayerGfx:: ; 87d7e (21:7d7e) +PlayerGfx:: dw 36 INCBIN "gfx/duelists/player.2bpp" -Duel55Gfx:: ; 87fc0 (21:7fc0) +Duel55Gfx:: dw $2 INCBIN "gfx/duel/anims/55.2bpp" -Duel56Gfx:: ; 87fe2 (21:7fe2) +Duel56Gfx:: dw $1 INCBIN "gfx/duel/anims/56.2bpp" -AnimData12:: ; 87ff4 (21:7ff4) +AnimData12:: frame_table AnimFrameTable3 frame_data 2, 8, 0, 0 frame_data 0, 0, 0, 0 SECTION "Gfx 4", ROMX -OverworldMapTiles:: ; 88000 (22:4000) +OverworldMapTiles:: dw 193 INCBIN "gfx/overworld_map.2bpp" -MasonLaboratoryTilesetGfx:: ; 88c12 (22:4c12) +MasonLaboratoryTilesetGfx:: dw 151 INCBIN "gfx/tilesets/masonlaboratory.2bpp" -ClubEntranceTilesetGfx:: ; 89584 (22:5584) +ClubEntranceTilesetGfx:: dw 129 INCBIN "gfx/tilesets/clubentrance.2bpp" -ClubLobbyTilesetGfx:: ; 89d96 (22:5d96) +ClubLobbyTilesetGfx:: dw 120 INCBIN "gfx/tilesets/clublobby.2bpp" -FightingClubTilesetGfx:: ; 8a518 (22:6518) +FightingClubTilesetGfx:: dw 99 INCBIN "gfx/tilesets/fightingclub.2bpp" -RockClubTilesetGfx:: ; 8ab4a (22:6b4a) +RockClubTilesetGfx:: dw 60 INCBIN "gfx/tilesets/rockclub.2bpp" -WaterClubTilesetGfx:: ; 8af0c (22:6f0c) +WaterClubTilesetGfx:: dw 161 INCBIN "gfx/tilesets/waterclub.2bpp" -GrassClubTilesetGfx:: ; 8b91e (22:791e) +GrassClubTilesetGfx:: dw 87 INCBIN "gfx/tilesets/grassclub.2bpp" -OWPlayerGfx:: ; 8be90 (22:7e90) +OWPlayerGfx:: dw $14 INCBIN "gfx/overworld_sprites/player.2bpp" -Duel57Gfx:: ; 8bfd2 (22:7fd2) +Duel57Gfx:: dw $1 INCBIN "gfx/duel/anims/57.2bpp" -AnimData2:: ; 8bfe4 (22:7fe4) +AnimData2:: frame_table AnimFrameTable0 frame_data 5, 16, 0, 0 frame_data 6, 16, 0, 0 @@ -617,45 +617,45 @@ AnimData2:: ; 8bfe4 (22:7fe4) frame_data 6, 16, 0, 0 frame_data 0, 0, 0, 0 -Palette109:: ; 8bffb (22:7ffb) +Palette109:: db 1, %11100100 db 0 SECTION "Gfx 5", ROMX -LightningClubTilesetGfx:: ; 8c000 (23:4000) +LightningClubTilesetGfx:: dw 131 INCBIN "gfx/tilesets/lightningclub.2bpp" -PsychicClubTilesetGfx:: ; 8c832 (23:4832) +PsychicClubTilesetGfx:: dw 58 INCBIN "gfx/tilesets/psychicclub.2bpp" -ScienceClubTilesetGfx:: ; 8cbd4 (23:4bd4) +ScienceClubTilesetGfx:: dw 82 INCBIN "gfx/tilesets/scienceclub.2bpp" -FireClubTilesetGfx:: ; 8d0f6 (23:50f6) +FireClubTilesetGfx:: dw 87 INCBIN "gfx/tilesets/fireclub.2bpp" -ChallengeHallTilesetGfx:: ; 8d668 (23:5668) +ChallengeHallTilesetGfx:: dw 157 INCBIN "gfx/tilesets/challengehall.2bpp" -PokemonDomeEntranceTilesetGfx:: ; 8e03a (23:603a) +PokemonDomeEntranceTilesetGfx:: dw 78 INCBIN "gfx/tilesets/pokemondomeentrance.2bpp" -PokemonDomeTilesetGfx:: ; 8e51c (23:651c) +PokemonDomeTilesetGfx:: dw 207 INCBIN "gfx/tilesets/pokemondome.2bpp" -HallOfHonorTilesetGfx:: ; 8f20e (23:720e) +HallOfHonorTilesetGfx:: dw 121 INCBIN "gfx/tilesets/hallofhonor.2bpp" -MedalGfx:: ; 8f9a0 (23:79a0) +MedalGfx:: dw 72 INCBIN "gfx/medals.2bpp", $0, $c0 INCBIN "gfx/medals.2bpp", $240, $30 @@ -676,134 +676,134 @@ MedalGfx:: ; 8f9a0 (23:79a0) INCBIN "gfx/medals.2bpp", $420, $30 INCBIN "gfx/medals.2bpp", $3f0, $20 -NintendoGfx:: ; 8fe22 (23:7e22) +NintendoGfx:: dw 24 INCBIN "gfx/nintendo.2bpp" -Duel58Gfx:: ; 8ffa4 (23:7fa4) +Duel58Gfx:: dw $4 INCBIN "gfx/duel/anims/58.2bpp" -AnimData3:: ; 8ffe6 (23:7fe6) +AnimData3:: frame_table AnimFrameTable0 frame_data 8, 16, 0, 0 frame_data 9, 16, 0, 0 frame_data 0, 0, 0, 0 -AnimData11:: ; 8fff5 (23:7ff5) +AnimData11:: frame_table AnimFrameTable3 frame_data 1, 8, 0, 0 frame_data 0, 0, 0, 0 SECTION "Gfx 6", ROMX -CardPopGfx:: ; 90000 (24:4000) +CardPopGfx:: dw 189 INCBIN "gfx/link/cardpop.2bpp" -GameBoyLinkGfx:: ; 90bd2 (24:4bd2) +GameBoyLinkGfx:: dw 109 INCBIN "gfx/link/gameboylink.2bpp" -GameBoyPrinterGfx:: ; 912a4 (24:52a4) +GameBoyPrinterGfx:: dw 93 INCBIN "gfx/link/gameboyprinter.2bpp" -Colosseum1Gfx:: ; 91876 (24:5876) +Colosseum1Gfx:: dw 96 INCBIN "gfx/booster_packs/colosseum1.2bpp" -Colosseum2Gfx:: ; 91e78 (24:5e78) +Colosseum2Gfx:: dw 86 INCBIN "gfx/booster_packs/colosseum2.2bpp" -Evolution1Gfx:: ; 923da (24:63da) +Evolution1Gfx:: dw 96 INCBIN "gfx/booster_packs/evolution1.2bpp" -Evolution2Gfx:: ; 929dc (24:69dc) +Evolution2Gfx:: dw 86 INCBIN "gfx/booster_packs/evolution2.2bpp" -Mystery1Gfx:: ; 92f3e (24:6f3e) +Mystery1Gfx:: dw 96 INCBIN "gfx/booster_packs/mystery1.2bpp" -Mystery2Gfx:: ; 93540 (24:7540) +Mystery2Gfx:: dw 86 INCBIN "gfx/booster_packs/mystery2.2bpp" -RonaldGfx:: ; 93aa2 (24:7aa2) +RonaldGfx:: dw 36 INCBIN "gfx/duelists/ronald.2bpp" -CopyrightGfx:: ; 93ce4 (24:7ce4) +CopyrightGfx:: dw 36 INCBIN "gfx/copyright.2bpp" -OWClerkGfx:: ; 93f26 (24:7f26) +OWClerkGfx:: dw $8 INCBIN "gfx/overworld_sprites/clerk.2bpp" -Duel59Gfx:: ; 93fa8 (24:7fa8) +Duel59Gfx:: dw $3 INCBIN "gfx/duel/anims/59.2bpp" -Duel60Gfx:: ; 93fda (24:7fda) +Duel60Gfx:: dw $2 INCBIN "gfx/duel/anims/60.2bpp" SECTION "Gfx 7", ROMX -Laboratory1Gfx:: ; 94000 (25:4000) +Laboratory1Gfx:: dw 96 INCBIN "gfx/booster_packs/laboratory1.2bpp" -Laboratory2Gfx:: ; 94602 (25:4602) +Laboratory2Gfx:: dw 86 INCBIN "gfx/booster_packs/laboratory2.2bpp" -CharizardIntro1Gfx:: ; 94b64 (25:4b64) +CharizardIntro1Gfx:: dw 96 INCBIN "gfx/titlescreen/booster_packs/charizardintro1.2bpp" -CharizardIntro2Gfx:: ; 95166 (25:5166) +CharizardIntro2Gfx:: dw 96 INCBIN "gfx/titlescreen/booster_packs/charizardintro2.2bpp" -ScytherIntro1Gfx:: ; 95768 (25:5768) +ScytherIntro1Gfx:: dw 96 INCBIN "gfx/titlescreen/booster_packs/scytherintro1.2bpp" -ScytherIntro2Gfx:: ; 95d6a (25:5d6a) +ScytherIntro2Gfx:: dw 96 INCBIN "gfx/titlescreen/booster_packs/scytherintro2.2bpp" -AerodactylIntro1Gfx:: ; 9636c (25:636c) +AerodactylIntro1Gfx:: dw 96 INCBIN "gfx/titlescreen/booster_packs/aerodactylintro1.2bpp" -AerodactylIntro2Gfx:: ; 9696e (25:696e) +AerodactylIntro2Gfx:: dw 96 INCBIN "gfx/titlescreen/booster_packs/aerodactylintro2.2bpp" -JapaneseTitleScreenGfx:: ; 96f70 (25:6f70) +JapaneseTitleScreenGfx:: dw 97 INCBIN "gfx/titlescreen/japanese_title_screen.2bpp" -JapaneseTitleScreenCGBGfx:: ; 97582 (25:7582) +JapaneseTitleScreenCGBGfx:: dw 97 INCBIN "gfx/titlescreen/japanese_title_screen_cgb.2bpp" -CompaniesGfx:: ; 97b94 (25:7b94) +CompaniesGfx:: dw 49 INCBIN "gfx/companies.2bpp" -OWRonaldGfx:: ; 97ea6 (25:7ea6) +OWRonaldGfx:: dw $14 INCBIN "gfx/overworld_sprites/ronald.2bpp" -AnimData5:: ; 97fe8 (25:7fe8) +AnimData5:: frame_table AnimFrameTable1 frame_data 3, 16, 0, 0 frame_data 4, 16, 0, 0 @@ -811,335 +811,335 @@ AnimData5:: ; 97fe8 (25:7fe8) SECTION "Gfx 8", ROMX -JapaneseTitleScreen2Gfx:: ; 98000 (26:4000) +JapaneseTitleScreen2Gfx:: dw 244 INCBIN "gfx/titlescreen/japanese_title_screen_2.2bpp" -JapaneseTitleScreen2CGBGfx:: ; 98f42 (26:4f42) +JapaneseTitleScreen2CGBGfx:: dw 315 INCBIN "gfx/titlescreen/japanese_title_screen_2_cgb.2bpp" -TitleScreenGfx:: ; 9a2f4 (26:62f4) +TitleScreenGfx:: dw 220 INCBIN "gfx/titlescreen/title_screen.2bpp" -TitleScreenCGBGfx:: ; 9b0b6 (26:70b6) +TitleScreenCGBGfx:: dw 212 INCBIN "gfx/titlescreen/title_screen_cgb.2bpp" -OWDrMasonGfx:: ; 9bdf8 (26:7df8) +OWDrMasonGfx:: dw $14 INCBIN "gfx/overworld_sprites/doctormason.2bpp" -OverworldMapOAMGfx:: ; 9bf3a (26:7f3a) +OverworldMapOAMGfx:: dw $8 INCBIN "gfx/overworld_map_oam.2bpp" -Duel61Gfx:: ; 9bfbc (26:7fbc) +Duel61Gfx:: dw $3 INCBIN "gfx/duel/anims/61.2bpp" -Duel62Gfx:: ; 9bfee (26:7fee) +Duel62Gfx:: dw $1 INCBIN "gfx/duel/anims/62.2bpp" SECTION "Gfx 9", ROMX -SamGfx:: ; 9c000 (27:4000) +SamGfx:: dw 36 INCBIN "gfx/duelists/sam.2bpp" -ImakuniGfx:: ; 9c242 (27:4242) +ImakuniGfx:: dw 36 INCBIN "gfx/duelists/imakuni.2bpp" -NikkiGfx:: ; 9c484 (27:4484) +NikkiGfx:: dw 36 INCBIN "gfx/duelists/nikki.2bpp" -RickGfx:: ; 9c6c6 (27:46c6) +RickGfx:: dw 36 INCBIN "gfx/duelists/rick.2bpp" -KenGfx:: ; 9c908 (27:4908) +KenGfx:: dw 36 INCBIN "gfx/duelists/ken.2bpp" -AmyGfx:: ; 9cb4a (27:4b4a) +AmyGfx:: dw 36 INCBIN "gfx/duelists/amy.2bpp" -IsaacGfx:: ; 9cd8c (27:4d8c) +IsaacGfx:: dw 36 INCBIN "gfx/duelists/isaac.2bpp" -MitchGfx:: ; 9cfce (27:4fce) +MitchGfx:: dw 36 INCBIN "gfx/duelists/mitch.2bpp" -GeneGfx:: ; 9d210 (27:5210) +GeneGfx:: dw 36 INCBIN "gfx/duelists/gene.2bpp" -MurrayGfx:: ; 9d452 (27:5452) +MurrayGfx:: dw 36 INCBIN "gfx/duelists/murray.2bpp" -CourtneyGfx:: ; 9d694 (27:5694) +CourtneyGfx:: dw 36 INCBIN "gfx/duelists/courtney.2bpp" -SteveGfx:: ; 9d8d6 (27:58d6) +SteveGfx:: dw 36 INCBIN "gfx/duelists/steve.2bpp" -JackGfx:: ; 9db18 (27:5b18) +JackGfx:: dw 36 INCBIN "gfx/duelists/jack.2bpp" -RodGfx:: ; 9dd5a (27:5d5a) +RodGfx:: dw 36 INCBIN "gfx/duelists/rod.2bpp" -JosephGfx:: ; 9df9c (27:5f9c) +JosephGfx:: dw 36 INCBIN "gfx/duelists/joseph.2bpp" -DavidGfx:: ; 9e1de (27:61de) +DavidGfx:: dw 36 INCBIN "gfx/duelists/david.2bpp" -ErikGfx:: ; 9e420 (27:6420) +ErikGfx:: dw 36 INCBIN "gfx/duelists/erik.2bpp" -JohnGfx:: ; 9e662 (27:6662) +JohnGfx:: dw 36 INCBIN "gfx/duelists/john.2bpp" -AdamGfx:: ; 9e8a4 (27:68a4) +AdamGfx:: dw 36 INCBIN "gfx/duelists/adam.2bpp" -JonathanGfx:: ; 9eae6 (27:6ae6) +JonathanGfx:: dw 36 INCBIN "gfx/duelists/jonathan.2bpp" -JoshuaGfx:: ; 9ed28 (27:6d28) +JoshuaGfx:: dw 36 INCBIN "gfx/duelists/joshua.2bpp" -NicholasGfx:: ; 9ef6a (27:6f6a) +NicholasGfx:: dw 36 INCBIN "gfx/duelists/nicholas.2bpp" -BrandonGfx:: ; 9f1ac (27:71ac) +BrandonGfx:: dw 36 INCBIN "gfx/duelists/brandon.2bpp" -MatthewGfx:: ; 9f3ee (27:73ee) +MatthewGfx:: dw 36 INCBIN "gfx/duelists/matthew.2bpp" -RyanGfx:: ; 9f630 (27:7630) +RyanGfx:: dw 36 INCBIN "gfx/duelists/ryan.2bpp" -AndrewGfx:: ; 9f872 (27:7872) +AndrewGfx:: dw 36 INCBIN "gfx/duelists/andrew.2bpp" -ChrisGfx:: ; 9fab4 (27:7ab4) +ChrisGfx:: dw 36 INCBIN "gfx/duelists/chris.2bpp" -MichaelGfx:: ; 9fcf6 (27:7cf6) +MichaelGfx:: dw 36 INCBIN "gfx/duelists/michael.2bpp" -Duel63Gfx:: ; 9ff38 (27:7f38) +Duel63Gfx:: dw $a INCBIN "gfx/duel/anims/63.2bpp" -Duel64Gfx:: ; 9ffda (27:7fda) +Duel64Gfx:: dw $2 INCBIN "gfx/duel/anims/64.2bpp" SECTION "Gfx 10", ROMX -DanielGfx:: ; a0000 (28:4000) +DanielGfx:: dw 36 INCBIN "gfx/duelists/daniel.2bpp" -RobertGfx:: ; a0242 (28:4242) +RobertGfx:: dw 36 INCBIN "gfx/duelists/robert.2bpp" -BrittanyGfx:: ; a0484 (28:4484) +BrittanyGfx:: dw 36 INCBIN "gfx/duelists/brittany.2bpp" -KristinGfx:: ; a06c6 (28:46c6) +KristinGfx:: dw 36 INCBIN "gfx/duelists/kristin.2bpp" -HeatherGfx:: ; a0908 (28:4908) +HeatherGfx:: dw 36 INCBIN "gfx/duelists/heather.2bpp" -SaraGfx:: ; a0b4a (28:4b4a) +SaraGfx:: dw 36 INCBIN "gfx/duelists/sara.2bpp" -AmandaGfx:: ; a0d8c (28:4d8c) +AmandaGfx:: dw 36 INCBIN "gfx/duelists/amanda.2bpp" -JenniferGfx:: ; a0fce (28:4fce) +JenniferGfx:: dw 36 INCBIN "gfx/duelists/jennifer.2bpp" -JessicaGfx:: ; a1210 (28:5210) +JessicaGfx:: dw 36 INCBIN "gfx/duelists/jessica.2bpp" -StephanieGfx:: ; a1452 (28:5452) +StephanieGfx:: dw 36 INCBIN "gfx/duelists/stephanie.2bpp" -AaronGfx:: ; a1694 (28:5694) +AaronGfx:: dw 36 INCBIN "gfx/duelists/aaron.2bpp" -OWIshiharaGfx:: ; a18d6 (28:58d6) +OWIshiharaGfx:: dw $14 INCBIN "gfx/overworld_sprites/ishihara.2bpp" -OWImakuniGfx:: ; a1a18 (28:5a18) +OWImakuniGfx:: dw $14 INCBIN "gfx/overworld_sprites/imakuni.2bpp" -OWNikkiGfx:: ; a1b5a (28:5b5a) +OWNikkiGfx:: dw $14 INCBIN "gfx/overworld_sprites/nikki.2bpp" -OWRickGfx:: ; a1c9c (28:5c9c) +OWRickGfx:: dw $14 INCBIN "gfx/overworld_sprites/rick.2bpp" -OWKenGfx:: ; a1dde (28:5dde) +OWKenGfx:: dw $14 INCBIN "gfx/overworld_sprites/ken.2bpp" -OWAmyGfx:: ; a1f20 (28:5f20) +OWAmyGfx:: dw $1b INCBIN "gfx/overworld_sprites/amy.2bpp" -OWIsaacGfx:: ; a20d2 (28:60d2) +OWIsaacGfx:: dw $14 INCBIN "gfx/overworld_sprites/isaac.2bpp" -OWMitchGfx:: ; a2214 (28:6214) +OWMitchGfx:: dw $14 INCBIN "gfx/overworld_sprites/mitch.2bpp" -OWGeneGfx:: ; a2356 (28:6356) +OWGeneGfx:: dw $14 INCBIN "gfx/overworld_sprites/gene.2bpp" -OWMurrayGfx:: ; a2498 (28:6498) +OWMurrayGfx:: dw $14 INCBIN "gfx/overworld_sprites/murray.2bpp" -OWCourtneyGfx:: ; a25da (28:65da) +OWCourtneyGfx:: dw $14 INCBIN "gfx/overworld_sprites/courtney.2bpp" -OWSteveGfx:: ; a271c (28:671c) +OWSteveGfx:: dw $14 INCBIN "gfx/overworld_sprites/steve.2bpp" -OWJackGfx:: ; a285e (28:685e) +OWJackGfx:: dw $14 INCBIN "gfx/overworld_sprites/jack.2bpp" -OWRodGfx:: ; a29a0 (28:69a0) +OWRodGfx:: dw $14 INCBIN "gfx/overworld_sprites/rod.2bpp" -OWBoyGfx:: ; a2ae2 (28:6ae2) +OWBoyGfx:: dw $14 INCBIN "gfx/overworld_sprites/youngster.2bpp" -OWLadGfx:: ; a2c24 (28:6c24) +OWLadGfx:: dw $14 INCBIN "gfx/overworld_sprites/lad.2bpp" -OWSpecsGfx:: ; a2d66 (28:6d66) +OWSpecsGfx:: dw $14 INCBIN "gfx/overworld_sprites/specs.2bpp" -OWButchGfx:: ; a2ea8 (28:6ea8) +OWButchGfx:: dw $14 INCBIN "gfx/overworld_sprites/butch.2bpp" -OWManiaGfx:: ; a2fea (28:6fea) +OWManiaGfx:: dw $14 INCBIN "gfx/overworld_sprites/mania.2bpp" -OWJoshuaGfx:: ; a312c (28:712c) +OWJoshuaGfx:: dw $14 INCBIN "gfx/overworld_sprites/joshua.2bpp" -OWHoodGfx:: ; a326e (28:726e) +OWHoodGfx:: dw $14 INCBIN "gfx/overworld_sprites/hood.2bpp" -OWTechGfx:: ; a33b0 (28:73b0) +OWTechGfx:: dw $14 INCBIN "gfx/overworld_sprites/tech.2bpp" -OWChapGfx:: ; a34f2 (28:74f2) +OWChapGfx:: dw $14 INCBIN "gfx/overworld_sprites/chap.2bpp" -OWManGfx:: ; a3634 (28:7634) +OWManGfx:: dw $14 INCBIN "gfx/overworld_sprites/man.2bpp" -OWPappyGfx:: ; a3776 (28:7776) +OWPappyGfx:: dw $14 INCBIN "gfx/overworld_sprites/pappy.2bpp" -OWGirlGfx:: ; a38b8 (28:78b8) +OWGirlGfx:: dw $14 INCBIN "gfx/overworld_sprites/girl.2bpp" -OWLass1Gfx:: ; a39fa (28:79fa) +OWLass1Gfx:: dw $14 INCBIN "gfx/overworld_sprites/lass1.2bpp" -OWLass2Gfx:: ; a3b3c (28:7b3c) +OWLass2Gfx:: dw $14 INCBIN "gfx/overworld_sprites/lass2.2bpp" -OWLass3Gfx:: ; a3c7e (28:7c7e) +OWLass3Gfx:: dw $14 INCBIN "gfx/overworld_sprites/lass3.2bpp" -OWSwimmerGfx:: ; a3dc0 (28:7dc0) +OWSwimmerGfx:: dw $14 INCBIN "gfx/overworld_sprites/swimmer.2bpp" -DuelGlowGfx:: ; a3f02 (28:7f02) +DuelGlowGfx:: dw $b INCBIN "gfx/duel/anims/glow.2bpp" -Duel66Gfx:: ; a3fb4 (28:7fb4) +Duel66Gfx:: dw $4 INCBIN "gfx/duel/anims/66.2bpp" -Palette117:: ; a3ff6 (28:7ff6) +Palette117:: db 0 db 1 @@ -1150,203 +1150,203 @@ Palette117:: ; a3ff6 (28:7ff6) SECTION "Gfx 11", ROMX -OWGalGfx:: ; a4000 (29:4000) +OWGalGfx:: dw $14 INCBIN "gfx/overworld_sprites/gal.2bpp" -OWWomanGfx:: ; a4142 (29:4142) +OWWomanGfx:: dw $14 INCBIN "gfx/overworld_sprites/woman.2bpp" -OWGrannyGfx:: ; a4284 (29:4284) +OWGrannyGfx:: dw $14 INCBIN "gfx/overworld_sprites/granny.2bpp" -Duel0Gfx:: ; a43c6 (29:43c6) +Duel0Gfx:: dw $16 INCBIN "gfx/duel/anims/0.2bpp" -Duel1Gfx:: ; a4528 (29:4528) +Duel1Gfx:: dw $06 INCBIN "gfx/duel/anims/1.2bpp" -Duel2Gfx:: ; a458a (29:458a) +Duel2Gfx:: dw $08 INCBIN "gfx/duel/anims/2.2bpp" -Duel3Gfx:: ; a460c (29:460c) +Duel3Gfx:: dw $09 INCBIN "gfx/duel/anims/3.2bpp" -Duel4Gfx:: ; a469e (29:469e) +Duel4Gfx:: dw $12 INCBIN "gfx/duel/anims/4.2bpp" -Duel5Gfx:: ; a47c0 (29:47c0) +Duel5Gfx:: dw $09 INCBIN "gfx/duel/anims/5.2bpp" -Duel6Gfx:: ; a4852 (29:4852) +Duel6Gfx:: dw $11 INCBIN "gfx/duel/anims/6.2bpp" -Duel7Gfx:: ; a4964 (29:4964) +Duel7Gfx:: dw $2d INCBIN "gfx/duel/anims/7.2bpp" -Duel8Gfx:: ; a4c36 (29:4c36) +Duel8Gfx:: dw $0d INCBIN "gfx/duel/anims/8.2bpp" -Duel9Gfx:: ; a4d08 (29:4d08) +Duel9Gfx:: dw $1c INCBIN "gfx/duel/anims/9.2bpp" -Duel10Gfx:: ; a4eca (29:4eca) +Duel10Gfx:: dw $4c INCBIN "gfx/duel/anims/10.2bpp" -Duel11Gfx:: ; a538c (29:538c) +Duel11Gfx:: dw $1b INCBIN "gfx/duel/anims/11.2bpp" -Duel12Gfx:: ; a553e (29:553e) +Duel12Gfx:: dw $07 INCBIN "gfx/duel/anims/12.2bpp" -Duel13Gfx:: ; a55b0 (29:55b0) +Duel13Gfx:: dw $0c INCBIN "gfx/duel/anims/13.2bpp" -Duel14Gfx:: ; a5672 (29:5672) +Duel14Gfx:: dw $22 INCBIN "gfx/duel/anims/14.2bpp" -Duel15Gfx:: ; a5894 (29:5894) +Duel15Gfx:: dw $20 INCBIN "gfx/duel/anims/15.2bpp" -Duel16Gfx:: ; a5a96 (29:5a96) +Duel16Gfx:: dw $0a INCBIN "gfx/duel/anims/16.2bpp" -Duel17Gfx:: ; a5b38 (29:5b38) +Duel17Gfx:: dw $25 INCBIN "gfx/duel/anims/17.2bpp" -Duel18Gfx:: ; a5d8a (29:5d8a) +Duel18Gfx:: dw $18 INCBIN "gfx/duel/anims/18.2bpp" -Duel19Gfx:: ; a5f0c (29:5f0c) +Duel19Gfx:: dw $1b INCBIN "gfx/duel/anims/19.2bpp" -Duel20Gfx:: ; a60be (29:60be) +Duel20Gfx:: dw $08 INCBIN "gfx/duel/anims/20.2bpp" -Duel21Gfx:: ; a6140 (29:6140) +Duel21Gfx:: dw $0d INCBIN "gfx/duel/anims/21.2bpp" -Duel22Gfx:: ; a6212 (29:6212) +Duel22Gfx:: dw $22 INCBIN "gfx/duel/anims/22.2bpp" -Duel23Gfx:: ; a6434 (29:6434) +Duel23Gfx:: dw $0c INCBIN "gfx/duel/anims/23.2bpp" -Duel24Gfx:: ; a64f6 (29:64f6) +Duel24Gfx:: dw $25 INCBIN "gfx/duel/anims/24.2bpp" -Duel25Gfx:: ; a6748 (29:6748) +Duel25Gfx:: dw $22 INCBIN "gfx/duel/anims/25.2bpp" -Duel26Gfx:: ; a696a (29:696a) +Duel26Gfx:: dw $0c INCBIN "gfx/duel/anims/26.2bpp" -Duel27Gfx:: ; a6a2c (29:6a2c) +Duel27Gfx:: dw $4c INCBIN "gfx/duel/anims/27.2bpp" -Duel28Gfx:: ; a6eee (29:6eee) +Duel28Gfx:: dw $08 INCBIN "gfx/duel/anims/28.2bpp" -Duel29Gfx:: ; a6f70 (29:6f70) +Duel29Gfx:: dw $07 INCBIN "gfx/duel/anims/29.2bpp" -Duel30Gfx:: ; a6fe2 (29:6fe2) +Duel30Gfx:: dw $1a INCBIN "gfx/duel/anims/30.2bpp" -Duel31Gfx:: ; a7184 (29:7184) +Duel31Gfx:: dw $0a INCBIN "gfx/duel/anims/31.2bpp" -Duel32Gfx:: ; a7226 (29:7226) +Duel32Gfx:: dw $2e INCBIN "gfx/duel/anims/32.2bpp" -Duel33Gfx:: ; a7508 (29:7508) +Duel33Gfx:: dw $08 INCBIN "gfx/duel/anims/33.2bpp" -Duel34Gfx:: ; a758a (29:758a) +Duel34Gfx:: dw $07 INCBIN "gfx/duel/anims/34.2bpp" -Duel35Gfx:: ; a75fc (29:75fc) +Duel35Gfx:: dw $1c INCBIN "gfx/duel/anims/35.2bpp" -Duel36Gfx:: ; a77be (29:77be) +Duel36Gfx:: dw $08 INCBIN "gfx/duel/anims/36.2bpp" -Duel37Gfx:: ; a7840 (29:7840) +Duel37Gfx:: dw $0b INCBIN "gfx/duel/anims/37.2bpp" -Duel38Gfx:: ; a78f2 (29:78f2) +Duel38Gfx:: dw $1c INCBIN "gfx/duel/anims/38.2bpp" -Duel39Gfx:: ; a7ab4 (29:7ab4) +Duel39Gfx:: dw $16 INCBIN "gfx/duel/anims/39.2bpp" -Duel40Gfx:: ; a7c16 (29:7c16) +Duel40Gfx:: dw $10 INCBIN "gfx/duel/anims/40.2bpp" -Duel41Gfx:: ; a7d18 (29:7d18) +Duel41Gfx:: dw $0f INCBIN "gfx/duel/anims/41.2bpp" -Duel42Gfx:: ; a7e0a (29:7e0a) +Duel42Gfx:: dw $07 INCBIN "gfx/duel/anims/42.2bpp" -Duel43Gfx:: ; a7e7c (29:7e7c) +Duel43Gfx:: dw $0a INCBIN "gfx/duel/anims/43.2bpp" -Duel44Gfx:: ; a7f1e (29:7f1e) +Duel44Gfx:: dw $09 INCBIN "gfx/duel/anims/44.2bpp" -Duel45Gfx:: ; a7fb0 (29:7fb0) +Duel45Gfx:: dw $03 INCBIN "gfx/duel/anims/45.2bpp" -AnimData6:: ; a7fe2 (29:7fe2) +AnimData6:: frame_table AnimFrameTable1 frame_data 5, 16, 0, 0 frame_data 6, 16, 0, 0 @@ -1356,75 +1356,75 @@ AnimData6:: ; a7fe2 (29:7fe2) SECTION "Gfx 12", ROMX -Duel46Gfx:: ; a8000 (2a:4000) +Duel46Gfx:: dw $08 INCBIN "gfx/duel/anims/46.2bpp" -Duel47Gfx:: ; a8082 (2a:4082) +Duel47Gfx:: dw $0f INCBIN "gfx/duel/anims/47.2bpp" -Duel48Gfx:: ; a8174 (2a:4174) +Duel48Gfx:: dw $03 INCBIN "gfx/duel/anims/48.2bpp" -Duel49Gfx:: ; a81a6 (2a:41a6) +Duel49Gfx:: dw $05 INCBIN "gfx/duel/anims/49.2bpp" -Duel50Gfx:: ; a81f8 (2a:41f8) +Duel50Gfx:: dw $17 INCBIN "gfx/duel/anims/50.2bpp" -Duel51Gfx:: ; a836a (2a:436a) +Duel51Gfx:: dw $36 INCBIN "gfx/duel/anims/51.2bpp" -Duel52Gfx:: ; a86cc (2a:46cc) +Duel52Gfx:: dw $0b INCBIN "gfx/duel/anims/52.2bpp" -Duel53Gfx:: ; a877e (2a:477e) +Duel53Gfx:: dw $06 INCBIN "gfx/duel/anims/53.2bpp" -Duel54Gfx:: ; a87e0 (2a:47e0) +Duel54Gfx:: dw $16 INCBIN "gfx/duel/anims/54.2bpp" -BoosterPackOAMGfx:: ; a8942 (2a:4942) +BoosterPackOAMGfx:: dw $20 INCBIN "gfx/booster_packs/oam.2bpp" -PressStartGfx:: ; a8b44 (2a:4b44) +PressStartGfx:: dw $14 INCBIN "gfx/titlescreen/press_start.2bpp" -GrassGfx:: ; a8c86 (2a:4c86) +GrassGfx:: dw $04 INCBIN "gfx/titlescreen/energies/grass.2bpp" -FireGfx:: ; a8cc8 (2a:4cc8) +FireGfx:: dw $04 INCBIN "gfx/titlescreen/energies/fire.2bpp" -WaterGfx:: ; a8d0a (2a:4d0a) +WaterGfx:: dw $04 INCBIN "gfx/titlescreen/energies/water.2bpp" -ColorlessGfx:: ; a8d4c (2a:4d4c) +ColorlessGfx:: dw $04 INCBIN "gfx/titlescreen/energies/colorless.2bpp" -LightningGfx:: ; a8d8e (2a:4d8e) +LightningGfx:: dw $04 INCBIN "gfx/titlescreen/energies/lightning.2bpp" -PsychicGfx:: ; a8dd0 (2a:4dd0) +PsychicGfx:: dw $04 INCBIN "gfx/titlescreen/energies/psychic.2bpp" -FightingGfx:: ; a8e12 (2a:4e12) +FightingGfx:: dw $04 INCBIN "gfx/titlescreen/energies/fighting.2bpp" @@ -1437,7 +1437,7 @@ SECTION "Anims 2", ROMX SECTION "Anims 3", ROMX INCLUDE "data/duel/animations/anims3.asm" -Palette31:: ; b3feb (2c:7feb) +Palette31:: db 1, %11010010 db 1 @@ -1446,7 +1446,7 @@ Palette31:: ; b3feb (2c:7feb) rgb 31, 24, 6 rgb 11, 3, 0 -Palette119:: ; b3ff6 (2c:7ff6) +Palette119:: db 0 db 1 @@ -1466,89 +1466,89 @@ SECTION "Palettes2", ROMX SECTION "Card Gfx 1", ROMX -CardGraphics:: ; c4000 (31:4000) +CardGraphics:: -GrassEnergyCardGfx:: ; c4000 (31:4000) +GrassEnergyCardGfx:: INCBIN "gfx/cards/grassenergy.2bpp" INCBIN "gfx/cards/grassenergy.pal" -FireEnergyCardGfx:: ; c4308 (31:4308) +FireEnergyCardGfx:: INCBIN "gfx/cards/fireenergy.2bpp" INCBIN "gfx/cards/fireenergy.pal" -WaterEnergyCardGfx:: ; c4610 (31:4610) +WaterEnergyCardGfx:: INCBIN "gfx/cards/waterenergy.2bpp" INCBIN "gfx/cards/waterenergy.pal" -LightningEnergyCardGfx:: ; c4918 (31:4918) +LightningEnergyCardGfx:: INCBIN "gfx/cards/lightningenergy.2bpp" INCBIN "gfx/cards/lightningenergy.pal" -FightingEnergyCardGfx:: ; c4c20 (31:4c20) +FightingEnergyCardGfx:: INCBIN "gfx/cards/fightingenergy.2bpp" INCBIN "gfx/cards/fightingenergy.pal" -PsychicEnergyCardGfx:: ; c4f28 (31:4f28) +PsychicEnergyCardGfx:: INCBIN "gfx/cards/psychicenergy.2bpp" INCBIN "gfx/cards/psychicenergy.pal" -DoubleColorlessEnergyCardGfx:: ; c5230 (31:5230) +DoubleColorlessEnergyCardGfx:: INCBIN "gfx/cards/doublecolorlessenergy.2bpp" INCBIN "gfx/cards/doublecolorlessenergy.pal" -BulbasaurCardGfx:: ; c5538 (31:5538) +BulbasaurCardGfx:: INCBIN "gfx/cards/bulbasaur.2bpp" INCBIN "gfx/cards/bulbasaur.pal" -IvysaurCardGfx:: ; c5840 (31:5840) +IvysaurCardGfx:: INCBIN "gfx/cards/ivysaur.2bpp" INCBIN "gfx/cards/ivysaur.pal" -Venusaur1CardGfx:: ; c5b48 (31:5b48) +Venusaur1CardGfx:: INCBIN "gfx/cards/venusaur1.2bpp" INCBIN "gfx/cards/venusaur1.pal" -Venusaur2CardGfx:: ; c5e50 (31:5e50) +Venusaur2CardGfx:: INCBIN "gfx/cards/venusaur2.2bpp" INCBIN "gfx/cards/venusaur2.pal" -CaterpieCardGfx:: ; c6158 (31:6158) +CaterpieCardGfx:: INCBIN "gfx/cards/caterpie.2bpp" INCBIN "gfx/cards/caterpie.pal" -MetapodCardGfx:: ; c6460 (31:6460) +MetapodCardGfx:: INCBIN "gfx/cards/metapod.2bpp" INCBIN "gfx/cards/metapod.pal" -ButterfreeCardGfx:: ; c6768 (31:6768) +ButterfreeCardGfx:: INCBIN "gfx/cards/butterfree.2bpp" INCBIN "gfx/cards/butterfree.pal" -WeedleCardGfx:: ; c6a70 (31:6a70) +WeedleCardGfx:: INCBIN "gfx/cards/weedle.2bpp" INCBIN "gfx/cards/weedle.pal" -KakunaCardGfx:: ; c6d78 (31:6d78) +KakunaCardGfx:: INCBIN "gfx/cards/kakuna.2bpp" INCBIN "gfx/cards/kakuna.pal" -BeedrillCardGfx:: ; c7080 (31:7080) +BeedrillCardGfx:: INCBIN "gfx/cards/beedrill.2bpp" INCBIN "gfx/cards/beedrill.pal" -EkansCardGfx:: ; c7388 (31:7388) +EkansCardGfx:: INCBIN "gfx/cards/ekans.2bpp" INCBIN "gfx/cards/ekans.pal" -ArbokCardGfx:: ; c7690 (31:7690) +ArbokCardGfx:: INCBIN "gfx/cards/arbok.2bpp" INCBIN "gfx/cards/arbok.pal" -NidoranFCardGfx:: ; c7998 (31:7998) +NidoranFCardGfx:: INCBIN "gfx/cards/nidoranf.2bpp" INCBIN "gfx/cards/nidoranf.pal" -NidorinaCardGfx:: ; c7ca0 (31:7ca0) +NidorinaCardGfx:: INCBIN "gfx/cards/nidorina.2bpp" INCBIN "gfx/cards/nidorina.pal" @@ -1556,87 +1556,87 @@ NidorinaCardGfx:: ; c7ca0 (31:7ca0) SECTION "Card Gfx 2", ROMX -NidoqueenCardGfx:: ; c8000 (32:4000) +NidoqueenCardGfx:: INCBIN "gfx/cards/nidoqueen.2bpp" INCBIN "gfx/cards/nidoqueen.pal" -NidoranMCardGfx:: ; c8308 (32:4308) +NidoranMCardGfx:: INCBIN "gfx/cards/nidoranm.2bpp" INCBIN "gfx/cards/nidoranm.pal" -NidorinoCardGfx:: ; c8610 (32:4610) +NidorinoCardGfx:: INCBIN "gfx/cards/nidorino.2bpp" INCBIN "gfx/cards/nidorino.pal" -NidokingCardGfx:: ; c8918 (32:4918) +NidokingCardGfx:: INCBIN "gfx/cards/nidoking.2bpp" INCBIN "gfx/cards/nidoking.pal" -ZubatCardGfx:: ; c8c20 (32:4c20) +ZubatCardGfx:: INCBIN "gfx/cards/zubat.2bpp" INCBIN "gfx/cards/zubat.pal" -GolbatCardGfx:: ; c8f28 (32:4f28) +GolbatCardGfx:: INCBIN "gfx/cards/golbat.2bpp" INCBIN "gfx/cards/golbat.pal" -OddishCardGfx:: ; c9230 (32:5230) +OddishCardGfx:: INCBIN "gfx/cards/oddish.2bpp" INCBIN "gfx/cards/oddish.pal" -GloomCardGfx:: ; c9538 (32:5538) +GloomCardGfx:: INCBIN "gfx/cards/gloom.2bpp" INCBIN "gfx/cards/gloom.pal" -VileplumeCardGfx:: ; c9840 (32:5840) +VileplumeCardGfx:: INCBIN "gfx/cards/vileplume.2bpp" INCBIN "gfx/cards/vileplume.pal" -ParasCardGfx:: ; c9b48 (32:5b48) +ParasCardGfx:: INCBIN "gfx/cards/paras.2bpp" INCBIN "gfx/cards/paras.pal" -ParasectCardGfx:: ; c9e50 (32:5e50) +ParasectCardGfx:: INCBIN "gfx/cards/parasect.2bpp" INCBIN "gfx/cards/parasect.pal" -VenonatCardGfx:: ; ca158 (32:6158) +VenonatCardGfx:: INCBIN "gfx/cards/venonat.2bpp" INCBIN "gfx/cards/venonat.pal" -VenomothCardGfx:: ; ca460 (32:6460) +VenomothCardGfx:: INCBIN "gfx/cards/venomoth.2bpp" INCBIN "gfx/cards/venomoth.pal" -BellsproutCardGfx:: ; ca768 (32:6768) +BellsproutCardGfx:: INCBIN "gfx/cards/bellsprout.2bpp" INCBIN "gfx/cards/bellsprout.pal" -WeepinbellCardGfx:: ; caa70 (32:6a70) +WeepinbellCardGfx:: INCBIN "gfx/cards/weepinbell.2bpp" INCBIN "gfx/cards/weepinbell.pal" -VictreebelCardGfx:: ; cad78 (32:6d78) +VictreebelCardGfx:: INCBIN "gfx/cards/victreebel.2bpp" INCBIN "gfx/cards/victreebel.pal" -GrimerCardGfx:: ; cb080 (32:7080) +GrimerCardGfx:: INCBIN "gfx/cards/grimer.2bpp" INCBIN "gfx/cards/grimer.pal" -MukCardGfx:: ; cb388 (32:7388) +MukCardGfx:: INCBIN "gfx/cards/muk.2bpp" INCBIN "gfx/cards/muk.pal" -ExeggcuteCardGfx:: ; cb690 (32:7690) +ExeggcuteCardGfx:: INCBIN "gfx/cards/exeggcute.2bpp" INCBIN "gfx/cards/exeggcute.pal" -ExeggutorCardGfx:: ; cb998 (32:7998) +ExeggutorCardGfx:: INCBIN "gfx/cards/exeggutor.2bpp" INCBIN "gfx/cards/exeggutor.pal" -KoffingCardGfx:: ; cbca0 (32:7ca0) +KoffingCardGfx:: INCBIN "gfx/cards/koffing.2bpp" INCBIN "gfx/cards/koffing.pal" @@ -1644,87 +1644,87 @@ KoffingCardGfx:: ; cbca0 (32:7ca0) SECTION "Card Gfx 3", ROMX -WeezingCardGfx:: ; cc000 (33:4000) +WeezingCardGfx:: INCBIN "gfx/cards/weezing.2bpp" INCBIN "gfx/cards/weezing.pal" -Tangela1CardGfx:: ; cc308 (33:4308) +Tangela1CardGfx:: INCBIN "gfx/cards/tangela1.2bpp" INCBIN "gfx/cards/tangela1.pal" -Tangela2CardGfx:: ; cc610 (33:4610) +Tangela2CardGfx:: INCBIN "gfx/cards/tangela2.2bpp" INCBIN "gfx/cards/tangela2.pal" -ScytherCardGfx:: ; cc918 (33:4918) +ScytherCardGfx:: INCBIN "gfx/cards/scyther.2bpp" INCBIN "gfx/cards/scyther.pal" -PinsirCardGfx:: ; ccc20 (33:4c20) +PinsirCardGfx:: INCBIN "gfx/cards/pinsir.2bpp" INCBIN "gfx/cards/pinsir.pal" -CharmanderCardGfx:: ; ccf28 (33:4f28) +CharmanderCardGfx:: INCBIN "gfx/cards/charmander.2bpp" INCBIN "gfx/cards/charmander.pal" -CharmeleonCardGfx:: ; cd230 (33:5230) +CharmeleonCardGfx:: INCBIN "gfx/cards/charmeleon.2bpp" INCBIN "gfx/cards/charmeleon.pal" -CharizardCardGfx:: ; cd538 (33:5538) +CharizardCardGfx:: INCBIN "gfx/cards/charizard.2bpp" INCBIN "gfx/cards/charizard.pal" -VulpixCardGfx:: ; cd840 (33:5840) +VulpixCardGfx:: INCBIN "gfx/cards/vulpix.2bpp" INCBIN "gfx/cards/vulpix.pal" -Ninetales1CardGfx:: ; cdb48 (33:5b48) +Ninetales1CardGfx:: INCBIN "gfx/cards/ninetales1.2bpp" INCBIN "gfx/cards/ninetales1.pal" -Ninetales2CardGfx:: ; cde50 (33:5e50) +Ninetales2CardGfx:: INCBIN "gfx/cards/ninetales2.2bpp" INCBIN "gfx/cards/ninetales2.pal" -GrowlitheCardGfx:: ; ce158 (33:6158) +GrowlitheCardGfx:: INCBIN "gfx/cards/growlithe.2bpp" INCBIN "gfx/cards/growlithe.pal" -Arcanine1CardGfx:: ; ce460 (33:6460) +Arcanine1CardGfx:: INCBIN "gfx/cards/arcanine1.2bpp" INCBIN "gfx/cards/arcanine1.pal" -Arcanine2CardGfx:: ; ce768 (33:6768) +Arcanine2CardGfx:: INCBIN "gfx/cards/arcanine2.2bpp" INCBIN "gfx/cards/arcanine2.pal" -PonytaCardGfx:: ; cea70 (33:6a70) +PonytaCardGfx:: INCBIN "gfx/cards/ponyta.2bpp" INCBIN "gfx/cards/ponyta.pal" -RapidashCardGfx:: ; ced78 (33:6d78) +RapidashCardGfx:: INCBIN "gfx/cards/rapidash.2bpp" INCBIN "gfx/cards/rapidash.pal" -Magmar1CardGfx:: ; cf080 (33:7080) +Magmar1CardGfx:: INCBIN "gfx/cards/magmar1.2bpp" INCBIN "gfx/cards/magmar1.pal" -Magmar2CardGfx:: ; cf388 (33:7388) +Magmar2CardGfx:: INCBIN "gfx/cards/magmar2.2bpp" INCBIN "gfx/cards/magmar2.pal" -Flareon1CardGfx:: ; cf690 (33:7690) +Flareon1CardGfx:: INCBIN "gfx/cards/flareon1.2bpp" INCBIN "gfx/cards/flareon1.pal" -Flareon2CardGfx:: ; cf998 (33:7998) +Flareon2CardGfx:: INCBIN "gfx/cards/flareon2.2bpp" INCBIN "gfx/cards/flareon2.pal" -Moltres1CardGfx:: ; cfca0 (33:7ca0) +Moltres1CardGfx:: INCBIN "gfx/cards/moltres1.2bpp" INCBIN "gfx/cards/moltres1.pal" @@ -1732,87 +1732,87 @@ Moltres1CardGfx:: ; cfca0 (33:7ca0) SECTION "Card Gfx 4", ROMX -Moltres2CardGfx:: ; d0000 (34:4000) +Moltres2CardGfx:: INCBIN "gfx/cards/moltres2.2bpp" INCBIN "gfx/cards/moltres2.pal" -SquirtleCardGfx:: ; d0308 (34:4308) +SquirtleCardGfx:: INCBIN "gfx/cards/squirtle.2bpp" INCBIN "gfx/cards/squirtle.pal" -WartortleCardGfx:: ; d0610 (34:4610) +WartortleCardGfx:: INCBIN "gfx/cards/wartortle.2bpp" INCBIN "gfx/cards/wartortle.pal" -BlastoiseCardGfx:: ; d0918 (34:4918) +BlastoiseCardGfx:: INCBIN "gfx/cards/blastoise.2bpp" INCBIN "gfx/cards/blastoise.pal" -PsyduckCardGfx:: ; d0c20 (34:4c20) +PsyduckCardGfx:: INCBIN "gfx/cards/psyduck.2bpp" INCBIN "gfx/cards/psyduck.pal" -GolduckCardGfx:: ; d0f28 (34:4f28) +GolduckCardGfx:: INCBIN "gfx/cards/golduck.2bpp" INCBIN "gfx/cards/golduck.pal" -PoliwagCardGfx:: ; d1230 (34:5230) +PoliwagCardGfx:: INCBIN "gfx/cards/poliwag.2bpp" INCBIN "gfx/cards/poliwag.pal" -PoliwhirlCardGfx:: ; d1538 (34:5538) +PoliwhirlCardGfx:: INCBIN "gfx/cards/poliwhirl.2bpp" INCBIN "gfx/cards/poliwhirl.pal" -PoliwrathCardGfx:: ; d1840 (34:5840) +PoliwrathCardGfx:: INCBIN "gfx/cards/poliwrath.2bpp" INCBIN "gfx/cards/poliwrath.pal" -TentacoolCardGfx:: ; d1b48 (34:5b48) +TentacoolCardGfx:: INCBIN "gfx/cards/tentacool.2bpp" INCBIN "gfx/cards/tentacool.pal" -TentacruelCardGfx:: ; d1e50 (34:5e50) +TentacruelCardGfx:: INCBIN "gfx/cards/tentacruel.2bpp" INCBIN "gfx/cards/tentacruel.pal" -SeelCardGfx:: ; d2158 (34:6158) +SeelCardGfx:: INCBIN "gfx/cards/seel.2bpp" INCBIN "gfx/cards/seel.pal" -DewgongCardGfx:: ; d2460 (34:6460) +DewgongCardGfx:: INCBIN "gfx/cards/dewgong.2bpp" INCBIN "gfx/cards/dewgong.pal" -ShellderCardGfx:: ; d2768 (34:6768) +ShellderCardGfx:: INCBIN "gfx/cards/shellder.2bpp" INCBIN "gfx/cards/shellder.pal" -CloysterCardGfx:: ; d2a70 (34:6a70) +CloysterCardGfx:: INCBIN "gfx/cards/cloyster.2bpp" INCBIN "gfx/cards/cloyster.pal" -KrabbyCardGfx:: ; d2d78 (34:6d78) +KrabbyCardGfx:: INCBIN "gfx/cards/krabby.2bpp" INCBIN "gfx/cards/krabby.pal" -KinglerCardGfx:: ; d3080 (34:7080) +KinglerCardGfx:: INCBIN "gfx/cards/kingler.2bpp" INCBIN "gfx/cards/kingler.pal" -HorseaCardGfx:: ; d3388 (34:7388) +HorseaCardGfx:: INCBIN "gfx/cards/horsea.2bpp" INCBIN "gfx/cards/horsea.pal" -SeadraCardGfx:: ; d3690 (34:7690) +SeadraCardGfx:: INCBIN "gfx/cards/seadra.2bpp" INCBIN "gfx/cards/seadra.pal" -GoldeenCardGfx:: ; d3998 (34:7998) +GoldeenCardGfx:: INCBIN "gfx/cards/goldeen.2bpp" INCBIN "gfx/cards/goldeen.pal" -SeakingCardGfx:: ; d3ca0 (34:7ca0) +SeakingCardGfx:: INCBIN "gfx/cards/seaking.2bpp" INCBIN "gfx/cards/seaking.pal" @@ -1820,87 +1820,87 @@ SeakingCardGfx:: ; d3ca0 (34:7ca0) SECTION "Card Gfx 5", ROMX -StaryuCardGfx:: ; d4000 (35:4000) +StaryuCardGfx:: INCBIN "gfx/cards/staryu.2bpp" INCBIN "gfx/cards/staryu.pal" -StarmieCardGfx:: ; d4308 (35:4308) +StarmieCardGfx:: INCBIN "gfx/cards/starmie.2bpp" INCBIN "gfx/cards/starmie.pal" -MagikarpCardGfx:: ; d4610 (35:4610) +MagikarpCardGfx:: INCBIN "gfx/cards/magikarp.2bpp" INCBIN "gfx/cards/magikarp.pal" -GyaradosCardGfx:: ; d4918 (35:4918) +GyaradosCardGfx:: INCBIN "gfx/cards/gyarados.2bpp" INCBIN "gfx/cards/gyarados.pal" -LaprasCardGfx:: ; d4c20 (35:4c20) +LaprasCardGfx:: INCBIN "gfx/cards/lapras.2bpp" INCBIN "gfx/cards/lapras.pal" -Vaporeon1CardGfx:: ; d4f28 (35:4f28) +Vaporeon1CardGfx:: INCBIN "gfx/cards/vaporeon1.2bpp" INCBIN "gfx/cards/vaporeon1.pal" -Vaporeon2CardGfx:: ; d5230 (35:5230) +Vaporeon2CardGfx:: INCBIN "gfx/cards/vaporeon2.2bpp" INCBIN "gfx/cards/vaporeon2.pal" -OmanyteCardGfx:: ; d5538 (35:5538) +OmanyteCardGfx:: INCBIN "gfx/cards/omanyte.2bpp" INCBIN "gfx/cards/omanyte.pal" -OmastarCardGfx:: ; d5840 (35:5840) +OmastarCardGfx:: INCBIN "gfx/cards/omastar.2bpp" INCBIN "gfx/cards/omastar.pal" -Articuno1CardGfx:: ; d5b48 (35:5b48) +Articuno1CardGfx:: INCBIN "gfx/cards/articuno1.2bpp" INCBIN "gfx/cards/articuno1.pal" -Articuno2CardGfx:: ; d5e50 (35:5e50) +Articuno2CardGfx:: INCBIN "gfx/cards/articuno2.2bpp" INCBIN "gfx/cards/articuno2.pal" -Pikachu1CardGfx:: ; d6158 (35:6158) +Pikachu1CardGfx:: INCBIN "gfx/cards/pikachu1.2bpp" INCBIN "gfx/cards/pikachu1.pal" -Pikachu2CardGfx:: ; d6460 (35:6460) +Pikachu2CardGfx:: INCBIN "gfx/cards/pikachu2.2bpp" INCBIN "gfx/cards/pikachu2.pal" -Pikachu3CardGfx:: ; d6768 (35:6768) +Pikachu3CardGfx:: INCBIN "gfx/cards/pikachu3.2bpp" INCBIN "gfx/cards/pikachu3.pal" -Pikachu4CardGfx:: ; d6a70 (35:6a70) +Pikachu4CardGfx:: INCBIN "gfx/cards/pikachu4.2bpp" INCBIN "gfx/cards/pikachu4.pal" -FlyingPikachuCardGfx:: ; d6d78 (35:6d78) +FlyingPikachuCardGfx:: INCBIN "gfx/cards/flyingpikachu.2bpp" INCBIN "gfx/cards/flyingpikachu.pal" -SurfingPikachu1CardGfx:: ; d7080 (35:7080) +SurfingPikachu1CardGfx:: INCBIN "gfx/cards/surfingpikachu1.2bpp" INCBIN "gfx/cards/surfingpikachu1.pal" -SurfingPikachu2CardGfx:: ; d7388 (35:7388) +SurfingPikachu2CardGfx:: INCBIN "gfx/cards/surfingpikachu2.2bpp" INCBIN "gfx/cards/surfingpikachu2.pal" -Raichu1CardGfx:: ; d7690 (35:7690) +Raichu1CardGfx:: INCBIN "gfx/cards/raichu1.2bpp" INCBIN "gfx/cards/raichu1.pal" -Raichu2CardGfx:: ; d7998 (35:7998) +Raichu2CardGfx:: INCBIN "gfx/cards/raichu2.2bpp" INCBIN "gfx/cards/raichu2.pal" -Magnemite1CardGfx:: ; d7ca0 (35:7ca0) +Magnemite1CardGfx:: INCBIN "gfx/cards/magnemite1.2bpp" INCBIN "gfx/cards/magnemite1.pal" @@ -1908,87 +1908,87 @@ Magnemite1CardGfx:: ; d7ca0 (35:7ca0) SECTION "Card Gfx 6", ROMX -Magnemite2CardGfx:: ; d8000 (36:4000) +Magnemite2CardGfx:: INCBIN "gfx/cards/magnemite2.2bpp" INCBIN "gfx/cards/magnemite2.pal" -Magneton1CardGfx:: ; d8308 (36:4308) +Magneton1CardGfx:: INCBIN "gfx/cards/magneton1.2bpp" INCBIN "gfx/cards/magneton1.pal" -Magneton2CardGfx:: ; d8610 (36:4610) +Magneton2CardGfx:: INCBIN "gfx/cards/magneton2.2bpp" INCBIN "gfx/cards/magneton2.pal" -VoltorbCardGfx:: ; d8918 (36:4918) +VoltorbCardGfx:: INCBIN "gfx/cards/voltorb.2bpp" INCBIN "gfx/cards/voltorb.pal" -Electrode1CardGfx:: ; d8c20 (36:4c20) +Electrode1CardGfx:: INCBIN "gfx/cards/electrode1.2bpp" INCBIN "gfx/cards/electrode1.pal" -Electrode2CardGfx:: ; d8f28 (36:4f28) +Electrode2CardGfx:: INCBIN "gfx/cards/electrode2.2bpp" INCBIN "gfx/cards/electrode2.pal" -Electabuzz1CardGfx:: ; d9230 (36:5230) +Electabuzz1CardGfx:: INCBIN "gfx/cards/electabuzz1.2bpp" INCBIN "gfx/cards/electabuzz1.pal" -Electabuzz2CardGfx:: ; d9538 (36:5538) +Electabuzz2CardGfx:: INCBIN "gfx/cards/electabuzz2.2bpp" INCBIN "gfx/cards/electabuzz2.pal" -Jolteon1CardGfx:: ; d9840 (36:5840) +Jolteon1CardGfx:: INCBIN "gfx/cards/jolteon1.2bpp" INCBIN "gfx/cards/jolteon1.pal" -Jolteon2CardGfx:: ; d9b48 (36:5b48) +Jolteon2CardGfx:: INCBIN "gfx/cards/jolteon2.2bpp" INCBIN "gfx/cards/jolteon2.pal" -Zapdos1CardGfx:: ; d9e50 (36:5e50) +Zapdos1CardGfx:: INCBIN "gfx/cards/zapdos1.2bpp" INCBIN "gfx/cards/zapdos1.pal" -Zapdos2CardGfx:: ; da158 (36:6158) +Zapdos2CardGfx:: INCBIN "gfx/cards/zapdos2.2bpp" INCBIN "gfx/cards/zapdos2.pal" -Zapdos3CardGfx:: ; da460 (36:6460) +Zapdos3CardGfx:: INCBIN "gfx/cards/zapdos3.2bpp" INCBIN "gfx/cards/zapdos3.pal" -SandshrewCardGfx:: ; da768 (36:6768) +SandshrewCardGfx:: INCBIN "gfx/cards/sandshrew.2bpp" INCBIN "gfx/cards/sandshrew.pal" -SandslashCardGfx:: ; daa70 (36:6a70) +SandslashCardGfx:: INCBIN "gfx/cards/sandslash.2bpp" INCBIN "gfx/cards/sandslash.pal" -DiglettCardGfx:: ; dad78 (36:6d78) +DiglettCardGfx:: INCBIN "gfx/cards/diglett.2bpp" INCBIN "gfx/cards/diglett.pal" -DugtrioCardGfx:: ; db080 (36:7080) +DugtrioCardGfx:: INCBIN "gfx/cards/dugtrio.2bpp" INCBIN "gfx/cards/dugtrio.pal" -MankeyCardGfx:: ; db388 (36:7388) +MankeyCardGfx:: INCBIN "gfx/cards/mankey.2bpp" INCBIN "gfx/cards/mankey.pal" -PrimeapeCardGfx:: ; db690 (36:7690) +PrimeapeCardGfx:: INCBIN "gfx/cards/primeape.2bpp" INCBIN "gfx/cards/primeape.pal" -MachopCardGfx:: ; db998 (36:7998) +MachopCardGfx:: INCBIN "gfx/cards/machop.2bpp" INCBIN "gfx/cards/machop.pal" -MachokeCardGfx:: ; dbca0 (36:7ca0) +MachokeCardGfx:: INCBIN "gfx/cards/machoke.2bpp" INCBIN "gfx/cards/machoke.pal" @@ -1996,87 +1996,87 @@ MachokeCardGfx:: ; dbca0 (36:7ca0) SECTION "Card Gfx 7", ROMX -MachampCardGfx:: ; dc000 (37:4000) +MachampCardGfx:: INCBIN "gfx/cards/machamp.2bpp" INCBIN "gfx/cards/machamp.pal" -GeodudeCardGfx:: ; dc308 (37:4308) +GeodudeCardGfx:: INCBIN "gfx/cards/geodude.2bpp" INCBIN "gfx/cards/geodude.pal" -GravelerCardGfx:: ; dc610 (37:4610) +GravelerCardGfx:: INCBIN "gfx/cards/graveler.2bpp" INCBIN "gfx/cards/graveler.pal" -GolemCardGfx:: ; dc918 (37:4918) +GolemCardGfx:: INCBIN "gfx/cards/golem.2bpp" INCBIN "gfx/cards/golem.pal" -OnixCardGfx:: ; dcc20 (37:4c20) +OnixCardGfx:: INCBIN "gfx/cards/onix.2bpp" INCBIN "gfx/cards/onix.pal" -CuboneCardGfx:: ; dcf28 (37:4f28) +CuboneCardGfx:: INCBIN "gfx/cards/cubone.2bpp" INCBIN "gfx/cards/cubone.pal" -Marowak1CardGfx:: ; dd230 (37:5230) +Marowak1CardGfx:: INCBIN "gfx/cards/marowak1.2bpp" INCBIN "gfx/cards/marowak1.pal" -Marowak2CardGfx:: ; dd538 (37:5538) +Marowak2CardGfx:: INCBIN "gfx/cards/marowak2.2bpp" INCBIN "gfx/cards/marowak2.pal" -HitmonleeCardGfx:: ; dd840 (37:5840) +HitmonleeCardGfx:: INCBIN "gfx/cards/hitmonlee.2bpp" INCBIN "gfx/cards/hitmonlee.pal" -HitmonchanCardGfx:: ; ddb48 (37:5b48) +HitmonchanCardGfx:: INCBIN "gfx/cards/hitmonchan.2bpp" INCBIN "gfx/cards/hitmonchan.pal" -RhyhornCardGfx:: ; dde50 (37:5e50) +RhyhornCardGfx:: INCBIN "gfx/cards/rhyhorn.2bpp" INCBIN "gfx/cards/rhyhorn.pal" -RhydonCardGfx:: ; de158 (37:6158) +RhydonCardGfx:: INCBIN "gfx/cards/rhydon.2bpp" INCBIN "gfx/cards/rhydon.pal" -KabutoCardGfx:: ; de460 (37:6460) +KabutoCardGfx:: INCBIN "gfx/cards/kabuto.2bpp" INCBIN "gfx/cards/kabuto.pal" -KabutopsCardGfx:: ; de768 (37:6768) +KabutopsCardGfx:: INCBIN "gfx/cards/kabutops.2bpp" INCBIN "gfx/cards/kabutops.pal" -AerodactylCardGfx:: ; dea70 (37:6a70) +AerodactylCardGfx:: INCBIN "gfx/cards/aerodactyl.2bpp" INCBIN "gfx/cards/aerodactyl.pal" -AbraCardGfx:: ; ded78 (37:6d78) +AbraCardGfx:: INCBIN "gfx/cards/abra.2bpp" INCBIN "gfx/cards/abra.pal" -KadabraCardGfx:: ; df080 (37:7080) +KadabraCardGfx:: INCBIN "gfx/cards/kadabra.2bpp" INCBIN "gfx/cards/kadabra.pal" -AlakazamCardGfx:: ; df388 (37:7388) +AlakazamCardGfx:: INCBIN "gfx/cards/alakazam.2bpp" INCBIN "gfx/cards/alakazam.pal" -Slowpoke1CardGfx:: ; df690 (37:7690) +Slowpoke1CardGfx:: INCBIN "gfx/cards/slowpoke1.2bpp" INCBIN "gfx/cards/slowpoke1.pal" -Slowpoke2CardGfx:: ; df998 (37:7998) +Slowpoke2CardGfx:: INCBIN "gfx/cards/slowpoke2.2bpp" INCBIN "gfx/cards/slowpoke2.pal" -SlowbroCardGfx:: ; dfca0 (37:7ca0) +SlowbroCardGfx:: INCBIN "gfx/cards/slowbro.2bpp" INCBIN "gfx/cards/slowbro.pal" @@ -2084,87 +2084,87 @@ SlowbroCardGfx:: ; dfca0 (37:7ca0) SECTION "Card Gfx 8", ROMX -Gastly1CardGfx:: ; e0000 (38:4000) +Gastly1CardGfx:: INCBIN "gfx/cards/gastly1.2bpp" INCBIN "gfx/cards/gastly1.pal" -Gastly2CardGfx:: ; e0308 (38:4308) +Gastly2CardGfx:: INCBIN "gfx/cards/gastly2.2bpp" INCBIN "gfx/cards/gastly2.pal" -Haunter1CardGfx:: ; e0610 (38:4610) +Haunter1CardGfx:: INCBIN "gfx/cards/haunter1.2bpp" INCBIN "gfx/cards/haunter1.pal" -Haunter2CardGfx:: ; e0918 (38:4918) +Haunter2CardGfx:: INCBIN "gfx/cards/haunter2.2bpp" INCBIN "gfx/cards/haunter2.pal" -GengarCardGfx:: ; e0c20 (38:4c20) +GengarCardGfx:: INCBIN "gfx/cards/gengar.2bpp" INCBIN "gfx/cards/gengar.pal" -DrowzeeCardGfx:: ; e0f28 (38:4f28) +DrowzeeCardGfx:: INCBIN "gfx/cards/drowzee.2bpp" INCBIN "gfx/cards/drowzee.pal" -HypnoCardGfx:: ; e1230 (38:5230) +HypnoCardGfx:: INCBIN "gfx/cards/hypno.2bpp" INCBIN "gfx/cards/hypno.pal" -MrMimeCardGfx:: ; e1538 (38:5538) +MrMimeCardGfx:: INCBIN "gfx/cards/mrmime.2bpp" INCBIN "gfx/cards/mrmime.pal" -JynxCardGfx:: ; e1840 (38:5840) +JynxCardGfx:: INCBIN "gfx/cards/jynx.2bpp" INCBIN "gfx/cards/jynx.pal" -Mewtwo1CardGfx:: ; e1b48 (38:5b48) +Mewtwo1CardGfx:: INCBIN "gfx/cards/mewtwo1.2bpp" INCBIN "gfx/cards/mewtwo1.pal" -Mewtwo2CardGfx:: ; e1e50 (38:5e50) +Mewtwo2CardGfx:: INCBIN "gfx/cards/mewtwo2.2bpp" INCBIN "gfx/cards/mewtwo2.pal" -Mewtwo3CardGfx:: ; e2158 (38:6158) +Mewtwo3CardGfx:: INCBIN "gfx/cards/mewtwo3.2bpp" INCBIN "gfx/cards/mewtwo3.pal" -Mew1CardGfx:: ; e2460 (38:6460) +Mew1CardGfx:: INCBIN "gfx/cards/mew1.2bpp" INCBIN "gfx/cards/mew1.pal" -Mew2CardGfx:: ; e2768 (38:6768) +Mew2CardGfx:: INCBIN "gfx/cards/mew2.2bpp" INCBIN "gfx/cards/mew2.pal" -Mew3CardGfx:: ; e2a70 (38:6a70) +Mew3CardGfx:: INCBIN "gfx/cards/mew3.2bpp" INCBIN "gfx/cards/mew3.pal" -PidgeyCardGfx:: ; e2d78 (38:6d78) +PidgeyCardGfx:: INCBIN "gfx/cards/pidgey.2bpp" INCBIN "gfx/cards/pidgey.pal" -PidgeottoCardGfx:: ; e3080 (38:7080) +PidgeottoCardGfx:: INCBIN "gfx/cards/pidgeotto.2bpp" INCBIN "gfx/cards/pidgeotto.pal" -Pidgeot1CardGfx:: ; e3388 (38:7388) +Pidgeot1CardGfx:: INCBIN "gfx/cards/pidgeot1.2bpp" INCBIN "gfx/cards/pidgeot1.pal" -Pidgeot2CardGfx:: ; e3690 (38:7690) +Pidgeot2CardGfx:: INCBIN "gfx/cards/pidgeot2.2bpp" INCBIN "gfx/cards/pidgeot2.pal" -RattataCardGfx:: ; e3998 (38:7998) +RattataCardGfx:: INCBIN "gfx/cards/rattata.2bpp" INCBIN "gfx/cards/rattata.pal" -RaticateCardGfx:: ; e3ca0 (38:7ca0) +RaticateCardGfx:: INCBIN "gfx/cards/raticate.2bpp" INCBIN "gfx/cards/raticate.pal" @@ -2172,87 +2172,87 @@ RaticateCardGfx:: ; e3ca0 (38:7ca0) SECTION "Card Gfx 9", ROMX -SpearowCardGfx:: ; e4000 (39:4000) +SpearowCardGfx:: INCBIN "gfx/cards/spearow.2bpp" INCBIN "gfx/cards/spearow.pal" -FearowCardGfx:: ; e4308 (39:4308) +FearowCardGfx:: INCBIN "gfx/cards/fearow.2bpp" INCBIN "gfx/cards/fearow.pal" -ClefairyCardGfx:: ; e4610 (39:4610) +ClefairyCardGfx:: INCBIN "gfx/cards/clefairy.2bpp" INCBIN "gfx/cards/clefairy.pal" -ClefableCardGfx:: ; e4918 (39:4918) +ClefableCardGfx:: INCBIN "gfx/cards/clefable.2bpp" INCBIN "gfx/cards/clefable.pal" -Jigglypuff1CardGfx:: ; e4c20 (39:4c20) +Jigglypuff1CardGfx:: INCBIN "gfx/cards/jigglypuff1.2bpp" INCBIN "gfx/cards/jigglypuff1.pal" -Jigglypuff2CardGfx:: ; e4f28 (39:4f28) +Jigglypuff2CardGfx:: INCBIN "gfx/cards/jigglypuff2.2bpp" INCBIN "gfx/cards/jigglypuff2.pal" -Jigglypuff3CardGfx:: ; e5230 (39:5230) +Jigglypuff3CardGfx:: INCBIN "gfx/cards/jigglypuff3.2bpp" INCBIN "gfx/cards/jigglypuff3.pal" -WigglytuffCardGfx:: ; e5538 (39:5538) +WigglytuffCardGfx:: INCBIN "gfx/cards/wigglytuff.2bpp" INCBIN "gfx/cards/wigglytuff.pal" -Meowth1CardGfx:: ; e5840 (39:5840) +Meowth1CardGfx:: INCBIN "gfx/cards/meowth1.2bpp" INCBIN "gfx/cards/meowth1.pal" -Meowth2CardGfx:: ; e5b48 (39:5b48) +Meowth2CardGfx:: INCBIN "gfx/cards/meowth2.2bpp" INCBIN "gfx/cards/meowth2.pal" -PersianCardGfx:: ; e5e50 (39:5e50) +PersianCardGfx:: INCBIN "gfx/cards/persian.2bpp" INCBIN "gfx/cards/persian.pal" -FarfetchdCardGfx:: ; e6158 (39:6158) +FarfetchdCardGfx:: INCBIN "gfx/cards/farfetchd.2bpp" INCBIN "gfx/cards/farfetchd.pal" -DoduoCardGfx:: ; e6460 (39:6460) +DoduoCardGfx:: INCBIN "gfx/cards/doduo.2bpp" INCBIN "gfx/cards/doduo.pal" -DodrioCardGfx:: ; e6768 (39:6768) +DodrioCardGfx:: INCBIN "gfx/cards/dodrio.2bpp" INCBIN "gfx/cards/dodrio.pal" -LickitungCardGfx:: ; e6a70 (39:6a70) +LickitungCardGfx:: INCBIN "gfx/cards/lickitung.2bpp" INCBIN "gfx/cards/lickitung.pal" -ChanseyCardGfx:: ; e6d78 (39:6d78) +ChanseyCardGfx:: INCBIN "gfx/cards/chansey.2bpp" INCBIN "gfx/cards/chansey.pal" -KangaskhanCardGfx:: ; e7080 (39:7080) +KangaskhanCardGfx:: INCBIN "gfx/cards/kangaskhan.2bpp" INCBIN "gfx/cards/kangaskhan.pal" -TaurosCardGfx:: ; e7388 (39:7388) +TaurosCardGfx:: INCBIN "gfx/cards/tauros.2bpp" INCBIN "gfx/cards/tauros.pal" -DittoCardGfx:: ; e7690 (39:7690) +DittoCardGfx:: INCBIN "gfx/cards/ditto.2bpp" INCBIN "gfx/cards/ditto.pal" -EeveeCardGfx:: ; e7998 (39:7998) +EeveeCardGfx:: INCBIN "gfx/cards/eevee.2bpp" INCBIN "gfx/cards/eevee.pal" -PorygonCardGfx:: ; e7ca0 (39:7ca0) +PorygonCardGfx:: INCBIN "gfx/cards/porygon.2bpp" INCBIN "gfx/cards/porygon.pal" @@ -2260,87 +2260,87 @@ PorygonCardGfx:: ; e7ca0 (39:7ca0) SECTION "Card Gfx 10", ROMX -SnorlaxCardGfx:: ; e8000 (3a:4000) +SnorlaxCardGfx:: INCBIN "gfx/cards/snorlax.2bpp" INCBIN "gfx/cards/snorlax.pal" -DratiniCardGfx:: ; e8308 (3a:4308) +DratiniCardGfx:: INCBIN "gfx/cards/dratini.2bpp" INCBIN "gfx/cards/dratini.pal" -DragonairCardGfx:: ; e8610 (3a:4610) +DragonairCardGfx:: INCBIN "gfx/cards/dragonair.2bpp" INCBIN "gfx/cards/dragonair.pal" -Dragonite1CardGfx:: ; e8918 (3a:4918) +Dragonite1CardGfx:: INCBIN "gfx/cards/dragonite1.2bpp" INCBIN "gfx/cards/dragonite1.pal" -Dragonite2CardGfx:: ; e8c20 (3a:4c20) +Dragonite2CardGfx:: INCBIN "gfx/cards/dragonite2.2bpp" INCBIN "gfx/cards/dragonite2.pal" -ProfessorOakCardGfx:: ; e8f28 (3a:4f28) +ProfessorOakCardGfx:: INCBIN "gfx/cards/professoroak.2bpp" INCBIN "gfx/cards/professoroak.pal" -ImposterProfessorOakCardGfx:: ; e9230 (3a:5230) +ImposterProfessorOakCardGfx:: INCBIN "gfx/cards/imposterprofessoroak.2bpp" INCBIN "gfx/cards/imposterprofessoroak.pal" -BillCardGfx:: ; e9538 (3a:5538) +BillCardGfx:: INCBIN "gfx/cards/bill.2bpp" INCBIN "gfx/cards/bill.pal" -MrFujiCardGfx:: ; e9840 (3a:5840) +MrFujiCardGfx:: INCBIN "gfx/cards/mrfuji.2bpp" INCBIN "gfx/cards/mrfuji.pal" -LassCardGfx:: ; e9b48 (3a:5b48) +LassCardGfx:: INCBIN "gfx/cards/lass.2bpp" INCBIN "gfx/cards/lass.pal" -ImakuniCardGfx:: ; e9e50 (3a:5e50) +ImakuniCardGfx:: INCBIN "gfx/cards/imakuni.2bpp" INCBIN "gfx/cards/imakuni.pal" -PokemonTraderCardGfx:: ; ea158 (3a:6158) +PokemonTraderCardGfx:: INCBIN "gfx/cards/pokemontrader.2bpp" INCBIN "gfx/cards/pokemontrader.pal" -PokemonBreederCardGfx:: ; ea460 (3a:6460) +PokemonBreederCardGfx:: INCBIN "gfx/cards/pokemonbreeder.2bpp" INCBIN "gfx/cards/pokemonbreeder.pal" -ClefairyDollCardGfx:: ; ea768 (3a:6768) +ClefairyDollCardGfx:: INCBIN "gfx/cards/clefairydoll.2bpp" INCBIN "gfx/cards/clefairydoll.pal" -MysteriousFossilCardGfx:: ; eaa70 (3a:6a70) +MysteriousFossilCardGfx:: INCBIN "gfx/cards/mysteriousfossil.2bpp" INCBIN "gfx/cards/mysteriousfossil.pal" -EnergyRetrievalCardGfx:: ; ead78 (3a:6d78) +EnergyRetrievalCardGfx:: INCBIN "gfx/cards/energyretrieval.2bpp" INCBIN "gfx/cards/energyretrieval.pal" -SuperEnergyRetrievalCardGfx:: ; eb080 (3a:7080) +SuperEnergyRetrievalCardGfx:: INCBIN "gfx/cards/superenergyretrieval.2bpp" INCBIN "gfx/cards/superenergyretrieval.pal" -EnergySearchCardGfx:: ; eb388 (3a:7388) +EnergySearchCardGfx:: INCBIN "gfx/cards/energysearch.2bpp" INCBIN "gfx/cards/energysearch.pal" -EnergyRemovalCardGfx:: ; eb690 (3a:7690) +EnergyRemovalCardGfx:: INCBIN "gfx/cards/energyremoval.2bpp" INCBIN "gfx/cards/energyremoval.pal" -SuperEnergyRemovalCardGfx:: ; eb998 (3a:7998) +SuperEnergyRemovalCardGfx:: INCBIN "gfx/cards/superenergyremoval.2bpp" INCBIN "gfx/cards/superenergyremoval.pal" -SwitchCardGfx:: ; ebca0 (3a:7ca0) +SwitchCardGfx:: INCBIN "gfx/cards/switch.2bpp" INCBIN "gfx/cards/switch.pal" @@ -2348,74 +2348,74 @@ SwitchCardGfx:: ; ebca0 (3a:7ca0) SECTION "Card Gfx 11", ROMX -PokemonCenterCardGfx:: ; ec000 (3b:4000) +PokemonCenterCardGfx:: INCBIN "gfx/cards/pokemoncenter.2bpp" INCBIN "gfx/cards/pokemoncenter.pal" -PokeBallCardGfx:: ; ec308 (3b:4308) +PokeBallCardGfx:: INCBIN "gfx/cards/pokeball.2bpp" INCBIN "gfx/cards/pokeball.pal" -ScoopUpCardGfx:: ; ec610 (3b:4610) +ScoopUpCardGfx:: INCBIN "gfx/cards/scoopup.2bpp" INCBIN "gfx/cards/scoopup.pal" -ComputerSearchCardGfx:: ; ec918 (3b:4918) +ComputerSearchCardGfx:: INCBIN "gfx/cards/computersearch.2bpp" INCBIN "gfx/cards/computersearch.pal" -PokedexCardGfx:: ; ecc20 (3b:4c20) +PokedexCardGfx:: INCBIN "gfx/cards/pokedex.2bpp" INCBIN "gfx/cards/pokedex.pal" -PlusPowerCardGfx:: ; ecf28 (3b:4f28) +PlusPowerCardGfx:: INCBIN "gfx/cards/pluspower.2bpp" INCBIN "gfx/cards/pluspower.pal" -DefenderCardGfx:: ; ed230 (3b:5230) +DefenderCardGfx:: INCBIN "gfx/cards/defender.2bpp" INCBIN "gfx/cards/defender.pal" -ItemFinderCardGfx:: ; ed538 (3b:5538) +ItemFinderCardGfx:: INCBIN "gfx/cards/itemfinder.2bpp" INCBIN "gfx/cards/itemfinder.pal" -GustOfWindCardGfx:: ; ed840 (3b:5840) +GustOfWindCardGfx:: INCBIN "gfx/cards/gustofwind.2bpp" INCBIN "gfx/cards/gustofwind.pal" -DevolutionSprayCardGfx:: ; edb48 (3b:5b48) +DevolutionSprayCardGfx:: INCBIN "gfx/cards/devolutionspray.2bpp" INCBIN "gfx/cards/devolutionspray.pal" -PotionCardGfx:: ; ede50 (3b:5e50) +PotionCardGfx:: INCBIN "gfx/cards/potion.2bpp" INCBIN "gfx/cards/potion.pal" -SuperPotionCardGfx:: ; ee158 (3b:6158) +SuperPotionCardGfx:: INCBIN "gfx/cards/superpotion.2bpp" INCBIN "gfx/cards/superpotion.pal" -FullHealCardGfx:: ; ee460 (3b:6460) +FullHealCardGfx:: INCBIN "gfx/cards/fullheal.2bpp" INCBIN "gfx/cards/fullheal.pal" -ReviveCardGfx:: ; ee768 (3b:6768) +ReviveCardGfx:: INCBIN "gfx/cards/revive.2bpp" INCBIN "gfx/cards/revive.pal" -MaintenanceCardGfx:: ; eea70 (3b:6a70) +MaintenanceCardGfx:: INCBIN "gfx/cards/maintenance.2bpp" INCBIN "gfx/cards/maintenance.pal" -PokemonFluteCardGfx:: ; eed78 (3b:6d78) +PokemonFluteCardGfx:: INCBIN "gfx/cards/pokemonflute.2bpp" INCBIN "gfx/cards/pokemonflute.pal" -GamblerCardGfx:: ; ef080 (3b:7080) +GamblerCardGfx:: INCBIN "gfx/cards/gambler.2bpp" INCBIN "gfx/cards/gambler.pal" -RecycleCardGfx:: ; ef388 (3b:7388) +RecycleCardGfx:: INCBIN "gfx/cards/recycle.2bpp" INCBIN "gfx/cards/recycle.pal" diff --git a/src/layout.link b/src/layout.link index 40cf56c..46ef66d 100644 --- a/src/layout.link +++ b/src/layout.link @@ -105,6 +105,7 @@ ROMX $1e "Gfx 2" ROMX $20 "Bank 20" + "Gfx" ROMX $21 "Gfx 3" ROMX $22 diff --git a/src/main.asm b/src/main.asm index f759ba1..b08bb10 100644 --- a/src/main.asm +++ b/src/main.asm @@ -118,3 +118,12 @@ INCLUDE "engine/bank1c.asm" SECTION "Bank 20", ROMX INCLUDE "engine/bank20.asm" + +SECTION "Gfx", ROMX +INCLUDE "engine/gfx/gfx_table_pointers.asm" +INCLUDE "engine/gfx/tilemaps.asm" +INCLUDE "engine/gfx/tilesets.asm" +INCLUDE "engine/gfx/sprites.asm" +INCLUDE "engine/gfx/sprite_animations.asm" +INCLUDE "engine/gfx/palettes.asm" +INCLUDE "data/maps/tilemaps.asm" -- cgit v1.2.3 From b0487fa979d0b5f3241e9e0659dd4b29a7e38f18 Mon Sep 17 00:00:00 2001 From: ElectroDeoxys Date: Tue, 28 Sep 2021 15:33:10 +0100 Subject: Bank 1c -> SGB --- src/engine/bank1c.asm | 1022 ------------------------------------------------- src/engine/sgb.asm | 1022 +++++++++++++++++++++++++++++++++++++++++++++++++ src/layout.link | 2 +- src/main.asm | 4 +- 4 files changed, 1025 insertions(+), 1025 deletions(-) delete mode 100644 src/engine/bank1c.asm create mode 100644 src/engine/sgb.asm diff --git a/src/engine/bank1c.asm b/src/engine/bank1c.asm deleted file mode 100644 index d32928a..0000000 --- a/src/engine/bank1c.asm +++ /dev/null @@ -1,1022 +0,0 @@ -SetMainSGBBorder: ; 70000 (1c:4000) - ld a, [wConsole] - cp CONSOLE_SGB - ret nz ; exit if not SGB - ld b, $1 - ld a, EVENT_RECEIVED_LEGENDARY_CARDS - farcall GetEventValue - or a - jr z, .asm_70013 - ld b, $2 -.asm_70013 - ld a, b - call SetSGBBorder - ret - -SetIntroSGBBorder: ; 70018 (1c:4018) - ld a, [wConsole] - cp CONSOLE_SGB - ret nz ; exit if not SGB - ld a, $0 - call SetSGBBorder - ret - -AtrcEnPacket_Disable: ; 70024 (1c:4024) - sgb ATRC_EN, 1 ; sgb_command, length - db 1 - ds $0e - -; disable Controller Set-up Screen -IconEnPacket: ; 70034 (1c:4034) - sgb ICON_EN, 3 ; sgb_command, length - db $01 - ds $0e - -; sets SGB border corresponding with value in register a -; $0 = intro -; $1 = medals (gold) -; $2 = medals (blue) -; $3 = debug -SetSGBBorder: ; 70044 (1c:4044) - push hl - push bc - add a ; *2 - ld c, a - add a ; *4 - add c ; *6 - ld c, a - ld b, $0 - ld hl, .SGBBorders - add hl, bc - call DecompressAndSendSGBBorder - pop bc - pop hl - ret - -.SGBBorders - ; tiles, pals (?), map (?) - dw SGBBorderIntroGfxPointers, SGBData_BorderIntro3, SGBData_BorderIntro4 - dw SGBBorderMedalsGfxPointers, SGBData_BorderMedals3, SGBData_BorderMedals5 - dw SGBBorderMedalsGfxPointers, SGBData_BorderMedals4, SGBData_BorderMedals5 - dw SGBBorderDebugGfxPointers, SGBData_BorderDebug3, SGBData_BorderDebug4 - -; forces SGB border intro -; unreferenced? -Func_7006f: ; 7006f (1c:406f) - ld a, [wConsole] - cp CONSOLE_SGB - ret nz ; exit if not SGB - ld de, SGBData_BorderIntro3 - ld hl, SGBData_BorderIntro4 - call SetMainSGBBorderPalsAndMap - call Func_701c0 - ret - -DecompressAndSendSGBBorder: ; 70082 (1c:4082) - ld a, [wConsole] - cp CONSOLE_SGB - ret nz ; exit if not SGB - push hl - push bc - ld a, [hli] - push hl - ld h, [hl] - ld l, a - call Func_700a3 - pop hl - inc hl - - ld a, [hli] - ld e, a - ld a, [hli] - ld d, a - ld a, [hli] - ld h, [hl] - ld l, a - call SetMainSGBBorderPalsAndMap - call Func_701c0 - pop bc - pop hl - ret - -Func_700a3: ; 700a3 (1c:40a3) - push hl - push bc - push de - push hl - call Func_70136 - pop hl - - push hl - ld a, [hli] - ld h, [hl] - ld l, a - ld de, v0Tiles1 - call DecompressSGBData - call PrepareBGMapForSendingSGBBorder - ld hl, ChrTrnPacket_BGTiles1 - call SendSGBBorder - pop hl - - ld de, 2 - add hl, de - ld a, [hli] - ld h, [hl] - ld l, a - ld a, [hli] - or [hl] - jr z, .asm_700da - call Func_70136 - dec hl - ld de, v0Tiles1 - call DecompressSGBData - ld hl, ChrTrnPacket_BGTiles2 - call SendSGBBorder -.asm_700da - pop de - pop bc - pop hl - ret - -; CHR_TRN: tiles $00-$7F, BG (border) tiles (from SNES $000-$FFF) -ChrTrnPacket_BGTiles1: ; 700de (1c:40de) - sgb CHR_TRN, 1 ; sgb_command, length - db 0 - ds $0e - -; CHR_TRN: tiles $80-$FF, BG (border) tiles (from SNES $000-$FFF) -ChrTrnPacket_BGTiles2: ; 700ee (1c:40ee) - sgb CHR_TRN, 1 ; sgb_command, length - db 1 - ds $0e - -; de = pals -; hl = map -SetMainSGBBorderPalsAndMap: ; 700fe (1c:40fe) - push hl - push bc - push de - push hl - push de - push hl ; input hl - call Func_70136 - pop hl - ld de, v0Tiles1 - call DecompressSGBData - - pop hl ; input de - ld de, v0Tiles2 - call DecompressSGBData - call PrepareBGMapForSendingSGBBorder - - pop hl ; input hl - call FillSGBBorderMedalSlots - ld hl, PctTrnPacket - call SendSGBBorder - pop de - pop bc - pop hl - ret - -; PCT_TRN: read tile map & palette data into VRAM (from SNES $000-$87F) -PctTrnPacket: ; 70126 (1c:4126) - sgb PCT_TRN, 1 ; sgb_command, length - ds $0f - -Func_70136: ; 70136 (1c:4136) - push hl - push bc - push de - ldh a, [hSCX] - ld [wd41d], a - ldh a, [hSCY] - ld [wd41e], a - ld a, [wBGP] - ld [wd41f], a - ld a, [wLCDC] - ld [wd420], a - - di - ld hl, MaskEnPacket_Freeze_Bank1c - call SendSGB - call DisableLCD - ld a, [wLCDC] - and LCDC_BGENABLE | LCDC_WINSELECT - or LCDC_BGON - ld [wLCDC], a - ld a, %11100100 - ldh [rBGP], a - call SetBGP - - xor a - ldh [hSCX], a - ldh [rSCX], a - ldh [hSCY], a - ldh [rSCY], a - pop de - pop bc - pop hl - ret - -SendSGBBorder: ; 70177 (1c:4177) - push hl - push bc - push de - push hl - call EnableLCD - pop hl - call SendSGB - ld a, [wd41d] - ldh [hSCX], a - ld a, [wd41e] - ldh [hSCY], a - ld a, [wd41f] - call SetBGP - ld a, [wd420] - ld [wLCDC], a - call DisableLCD - ei - pop de - pop bc - pop hl - ret - -; MASK_EN on -MaskEnPacket_Freeze_Bank1c: ; 701a0 (1c:41a0) - sgb MASK_EN, 1 ; sgb_command, length - db MASK_EN_FREEZE_SCREEN - ds $0e - -; MASK_EN off -MaskEnPacket_Cancel_Bank1c: ; 701b0 (1c:41b0) - sgb MASK_EN, 1 ; sgb_command, length - db MASK_EN_CANCEL_MASK - ds $0e - -Func_701c0: ; 701c0 (1c:41c0) - push hl - push bc - call DisableLCD - xor a - ld c, $10 - ld hl, v0Tiles2 -.asm_701cb - ld [hli], a - dec c - jr nz, .asm_701cb - ld a, [wTileMapFill] - push af - xor a - ld [wTileMapFill], a - call EmptyScreen - pop af - ld [wTileMapFill], a - di - ld hl, MaskEnPacket_Cancel_Bank1c - call SendSGB - ei - pop bc - pop hl - ret - -; decompresses data pointed by hl to de -DecompressSGBData: ; 701e9 (1c:41e9) - ld a, [hli] - ld c, a - ld a, [hli] - ld b, a - or c - ret z - push de - push bc - ld e, l - ld d, h - ld b, HIGH(wDecompressionSecondaryBuffer) - call InitDataDecompression - pop bc - pop de - call DecompressData - ret - -; fills a 20x13 rectangle in v0BGMap0 -; with values ascending bytes starting at $80 -PrepareBGMapForSendingSGBBorder: ; 701fe (1c:41fe) - ld hl, v0BGMap0 - ld de, $000c - ld a, $80 - ld c, $d -.asm_70208 - ld b, SCREEN_WIDTH -.asm_7020a - ld [hli], a - inc a - dec b - jr nz, .asm_7020a - add hl, de - dec c - jr nz, .asm_70208 - ret - -; iterates all the medals obtained by the player -; and fills the corresponding medal slot in the SGB border -FillSGBBorderMedalSlots: ; 70214 (1c:4214) -; exit if not SGBData_BorderMedals5 - ld a, l - cp LOW(SGBData_BorderMedals5) - ret nz - ld a, h - cp HIGH(SGBData_BorderMedals5) - ret nz - - ld hl, .SGBBorderMedalTiles - ld a, EVENT_MEDAL_FLAGS - farcall GetEventValue - ld c, NUM_MEDALS -.loop_medals - push bc - push hl - push af - bit 7, a - jr z, .next_medal - - ld c, 3 * 3 -.loop_tiles - push bc - ld e, [hl] - inc hl - ld d, [hl] - inc hl - ld a, [hli] - ld [de], a - inc de - ld a, [hli] - ld [de], a - pop bc - dec c - jr nz, .loop_tiles - -.next_medal - pop af - rlca - pop hl - ld bc, 3 * 3 * 4 - add hl, bc - pop bc - dec c - jr nz, .loop_medals - ret - -border_medal_tile: MACRO - dw \1 ; VRAM address - db \2 ; tile - db \3 ; pal (?) -ENDM - -.SGBBorderMedalTiles -; GRASS_MEDAL - border_medal_tile v0Tiles1 + $182, $3f, $10 - border_medal_tile v0Tiles1 + $184, $40, $10 - border_medal_tile v0Tiles1 + $186, $41, $10 - border_medal_tile v0Tiles1 + $1c2, $42, $10 - border_medal_tile v0Tiles1 + $1c4, $43, $10 - border_medal_tile v0Tiles1 + $1c6, $44, $10 - border_medal_tile v0Tiles1 + $202, $45, $10 - border_medal_tile v0Tiles1 + $204, $46, $10 - border_medal_tile v0Tiles1 + $206, $47, $10 - -; SCIENCE_MEDAL - border_medal_tile v0Tiles1 + $282, $48, $10 - border_medal_tile v0Tiles1 + $284, $49, $10 - border_medal_tile v0Tiles1 + $286, $4a, $10 - border_medal_tile v0Tiles1 + $2c2, $4b, $10 - border_medal_tile v0Tiles1 + $2c4, $4c, $10 - border_medal_tile v0Tiles1 + $2c6, $4d, $10 - border_medal_tile v0Tiles1 + $302, $4e, $10 - border_medal_tile v0Tiles1 + $304, $4f, $10 - border_medal_tile v0Tiles1 + $306, $50, $10 - -; FIRE_MEDAL - border_medal_tile v0Tiles1 + $382, $51, $10 - border_medal_tile v0Tiles1 + $384, $52, $10 - border_medal_tile v0Tiles1 + $386, $53, $10 - border_medal_tile v0Tiles1 + $3c2, $54, $10 - border_medal_tile v0Tiles1 + $3c4, $55, $10 - border_medal_tile v0Tiles1 + $3c6, $56, $10 - border_medal_tile v0Tiles1 + $402, $57, $10 - border_medal_tile v0Tiles1 + $404, $58, $10 - border_medal_tile v0Tiles1 + $406, $59, $10 - -; WATER_MEDAL - border_medal_tile v0Tiles1 + $482, $5a, $10 - border_medal_tile v0Tiles1 + $484, $5b, $10 - border_medal_tile v0Tiles1 + $486, $5c, $10 - border_medal_tile v0Tiles1 + $4c2, $5d, $10 - border_medal_tile v0Tiles1 + $4c4, $5e, $10 - border_medal_tile v0Tiles1 + $4c6, $5f, $10 - border_medal_tile v0Tiles1 + $502, $60, $10 - border_medal_tile v0Tiles1 + $504, $61, $10 - border_medal_tile v0Tiles1 + $506, $62, $10 - -; LIGHTNING_MEDAL - border_medal_tile v0Tiles1 + $1b8, $63, $10 - border_medal_tile v0Tiles1 + $1ba, $64, $10 - border_medal_tile v0Tiles1 + $1bc, $65, $10 - border_medal_tile v0Tiles1 + $1f8, $66, $10 - border_medal_tile v0Tiles1 + $1fa, $67, $10 - border_medal_tile v0Tiles1 + $1fc, $68, $10 - border_medal_tile v0Tiles1 + $238, $69, $10 - border_medal_tile v0Tiles1 + $23a, $6a, $10 - border_medal_tile v0Tiles1 + $23c, $6b, $10 - -; PSYCHIC_MEDAL - border_medal_tile v0Tiles1 + $2b8, $7e, $10 - border_medal_tile v0Tiles1 + $2ba, $7f, $10 - border_medal_tile v0Tiles1 + $2bc, $80, $10 - border_medal_tile v0Tiles1 + $2f8, $81, $10 - border_medal_tile v0Tiles1 + $2fa, $82, $10 - border_medal_tile v0Tiles1 + $2fc, $83, $10 - border_medal_tile v0Tiles1 + $338, $84, $10 - border_medal_tile v0Tiles1 + $33a, $85, $10 - border_medal_tile v0Tiles1 + $33c, $86, $10 - -; ROCK_MEDAL - border_medal_tile v0Tiles1 + $3b8, $75, $10 - border_medal_tile v0Tiles1 + $3ba, $76, $10 - border_medal_tile v0Tiles1 + $3bc, $77, $10 - border_medal_tile v0Tiles1 + $3f8, $78, $10 - border_medal_tile v0Tiles1 + $3fa, $79, $10 - border_medal_tile v0Tiles1 + $3fc, $7a, $10 - border_medal_tile v0Tiles1 + $438, $7b, $10 - border_medal_tile v0Tiles1 + $43a, $7c, $10 - border_medal_tile v0Tiles1 + $43c, $7d, $10 - -; FIGHTING_MEDAL - border_medal_tile v0Tiles1 + $4b8, $6c, $10 - border_medal_tile v0Tiles1 + $4ba, $6d, $10 - border_medal_tile v0Tiles1 + $4bc, $6e, $10 - border_medal_tile v0Tiles1 + $4f8, $6f, $10 - border_medal_tile v0Tiles1 + $4fa, $70, $10 - border_medal_tile v0Tiles1 + $4fc, $71, $10 - border_medal_tile v0Tiles1 + $538, $72, $10 - border_medal_tile v0Tiles1 + $53a, $73, $10 - border_medal_tile v0Tiles1 + $53c, $74, $10 - -; decompresses palette data depending on wCurMapSGBPals -; then sends it as SGB packet -SetSGB2AndSGB3MapPalette: ; 7036a (1c:436a) - ld a, [wConsole] - cp CONSOLE_SGB - ret nz ; return if not SGB - ld a, [wCurMapSGBPals] - or a - ret z ; not valid - - push hl - push bc - push de - ld a, [wCurMapSGBPals] - add a - ld c, a - ld b, $0 - ld hl, .pal_data_pointers - add hl, bc - ld a, [hli] - ld h, [hl] - ld l, a - call DecompressSGBPalette - - ; load palettes to wTempSGBPacket - ld hl, wDecompressionBuffer - ld de, wTempSGBPacket + 1 ; PAL Packet color #0 (PAL23's SGB2) - ld bc, 8 ; pal size - call CopyDataHLtoDE - ld hl, wDecompressionBuffer + 34 - ld de, wTempSGBPacket + 9 ; PAL Packet color #4 (PAL23's SGB3) - ld bc, 6 - call CopyDataHLtoDE - - xor a - ld [wTempSGBPacket + 15], a - ld hl, wTempSGBPacket - ld a, PAL01 << 3 + 1 - ld [hl], a - call Func_704c7 - call SendSGB - pop de - pop bc - pop hl - ret - -.pal_data_pointers - dw SGBData_MapPals1 ; unused - dw SGBData_MapPals1 ; MAP_SGB_PALS_1 - dw SGBData_MapPals2 ; MAP_SGB_PALS_2 - dw SGBData_MapPals3 ; MAP_SGB_PALS_3 - dw SGBData_MapPals4 ; MAP_SGB_PALS_4 - dw SGBData_MapPals5 ; MAP_SGB_PALS_5 - dw SGBData_MapPals6 ; MAP_SGB_PALS_6 - dw SGBData_MapPals7 ; MAP_SGB_PALS_7 - dw SGBData_MapPals8 ; MAP_SGB_PALS_8 - dw SGBData_MapPals9 ; MAP_SGB_PALS_9 - dw SGBData_MapPals10 ; MAP_SGB_PALS_10 - -Func_703cb: ; 703cb (1c:43cb) - ld a, [wConsole] - cp CONSOLE_SGB - ret nz - push hl - push bc - push de - call DecompressSGBPalette - ld hl, wDecompressionBuffer - ld de, wTempSGBPacket + $1 - ld bc, $8 ; palette 2, color 0-3 - call CopyDataHLtoDE - ld hl, wDecompressionBuffer + $22 - ld de, wTempSGBPacket + $9 - ld bc, $6 ; palette 3, color 1-3 - call CopyDataHLtoDE - xor a - ld [wTempSGBPacket + $f], a - ld hl, wTempSGBPacket - ld a, PAL23 << 3 + 1 - ld [hl], a - call Func_704c7 - call SendSGB - pop de - pop bc - pop hl - ret - -DecompressSGBPalette: ; 70403 (1c:4403) - push hl - push bc - push de - ld c, [hl] - inc hl - ld b, [hl] - inc hl - push bc - ld e, l - ld d, h - ld b, HIGH(wDecompressionSecondaryBuffer) - call InitDataDecompression - pop bc - ld de, wDecompressionBuffer - call DecompressData - pop de - pop bc - pop hl - ret - -; sends an SGB packet related with palettes -Func_7041d: ; 7041d (1c:441d) - ld a, [wConsole] - cp CONSOLE_SGB - ret nz ; exit if not SGB - - push hl - push bc - push de - push bc - push hl - push hl - ld hl, SGBData_PlayerPortraitPals - call DecompressSGBPalette - ld hl, wLoadedPalData - ld de, wTempSGBPacket + $1 - ld bc, $8 - call CopyDataHLtoDE - - pop hl - call DecompressSGBPalette - ld hl, wLoadedPalData + 2 - ld de, wTempSGBPacket + $9 - ld bc, $6 - call CopyDataHLtoDE - - xor a - ld [wTempSGBPacket + $f], a - ld hl, wTempSGBPacket - ld a, $09 - ld [hl], a - call Func_704c7 - call SendSGB - - pop hl - ld c, $0f - ld a, l - cp LOW(SGBData_PlayerPortraitPals) - jr nz, .asm_7046a - ld a, h - cp HIGH(SGBData_PlayerPortraitPals) - jr nz, .asm_7046a - ld c, $0a - -.asm_7046a - ld a, c - ld [wTempSGBPacket + $3], a - pop bc - ld hl, wTempSGBPacket - push hl - ld a, $21 - ld [hli], a - ld a, $01 - ld [hli], a - ld a, $01 - ld [hli], a - inc hl - ld a, b - ld [hli], a - ld a, c - ld [hli], a - ld a, $05 - add b - ld [hli], a - ld a, $05 - add c - ld [hli], a - xor a - ld [wTempSGBPacket + $e], a - ld [wTempSGBPacket + $f], a - pop hl - call SendSGB - pop de - pop bc - pop hl - ret - -; send an ATTR_BLK SGB packet -; input: -; b = x1 (left) -; c = y1 (upper) -; d = block width -; e = block height -; l = %00xxyyzz, palette number for: outside block, block border, inside block -Func_70498: ; 70498 (1c:4498) - ld a, [wConsole] - cp CONSOLE_SGB - ret nz - push hl - push bc - push de - ld a, l - ld [wTempSGBPacket + 3], a ; Color Palette Designation - ld hl, wTempSGBPacket - push hl - ld a, ATTR_BLK << 3 + 1 - ld [hli], a ; packet command and length - ld a, 1 - ld [hli], a ; 1 data set - ld a, ATTR_BLK_CTRL_INSIDE - ld [hli], a ; control code - inc hl - ld a, b - ld [hli], a ; x1 - ld a, c - ld [hli], a ; y1 - ld a, d - dec a - add b - ld [hli], a ; x2 - ld a, e - dec a - add c - ld [hli], a ; y2 - pop hl - call SendSGB - pop de - pop bc - pop hl - ret - -; set color 0 to default white rgb(28, 28, 24) -; input: -; hl = pointer to start of SGB packet -Func_704c7: ; 704c7 (1c:44c7) - push af - push hl - inc hl - ld a, LOW(24 << 10 | 28 << 5 | 28) - ld [hli], a - ld a, HIGH(24 << 10 | 28 << 5 | 28) - ld [hl], a - pop hl - pop af - ret - -SGBData_BorderDebug4: ; 704d3 (1c:44d3) - dw $800 ; length - INCBIN "data/sgb_data/border_debug_4.bin" - -SGBData_BorderIntro4: ; 706dd (1c:46dd) - dw $800 ; length - INCBIN "data/sgb_data/border_intro_4.bin" - -SGBData_BorderMedals5: ; 709dc (1c:49dc) - dw $800 ; length - INCBIN "data/sgb_data/border_medals_5.bin" - -SGBBorderDebugGfxPointers: ; 70b96 (1c:4b96) - dw SGBData_BorderDebug1 - dw SGBData_BorderDebug2 - -SGBData_BorderDebug1: ; 70b9a (1c:45b9a) - dw $1000 ; length - INCBIN "data/sgb_data/border_debug_1.bin" - -SGBData_BorderDebug2: ; 71359 (1c:5359) - dw $a0 ; length - INCBIN "data/sgb_data/border_debug_2.bin" - -SGBBorderIntroGfxPointers: ; 713a9 (1c:53a9) - dw SGBData_BorderIntro1 - dw SGBData_BorderIntro2 - -SGBData_BorderIntro1: ; 713ad (1c:53ad) - dw $1000 ; length - INCBIN "data/sgb_data/border_intro_1.bin" - -SGBData_BorderIntro2: ; 71ec0 (1c:5ec0) - dw $4e0 ; length - INCBIN "data/sgb_data/border_intro_2.bin" - -SGBBorderMedalsGfxPointers: ; 72273 (1c:6273) - dw SGBData_BorderMedals1 - dw SGBData_BorderMedals2 - -SGBData_BorderMedals1: ; 72277 (1c:5277) - dw $1000 ; length - INCBIN "data/sgb_data/border_medals_1.bin" - -SGBData_BorderMedals2: ; 72fe4 (1c:5fe4) - dw $100 ; length - INCBIN "data/sgb_data/border_medals_2.bin" - -SGBData_BorderDebug3: ; 730de (1c:70de) - dw $60 ; length - INCBIN "data/sgb_data/border_debug_3.bin" - -SGBData_BorderIntro3: ; 73146 (1c:7146) - dw $60 ; length - INCBIN "data/sgb_data/border_intro_3.bin" - -SGBData_BorderMedals3: ; 7319a (1c:719a) - dw $60 ; length - INCBIN "data/sgb_data/border_medals_3.bin" - -SGBData_BorderMedals4: ; 731e5 (1c:71e5) - dw $60 ; length - INCBIN "data/sgb_data/border_medals_4.bin" - -SGBData_MapPals1: ; 7322f (1c:722f) - dw $20 ; length - INCBIN "data/sgb_data/map_pals_1.bin" - -SGBData_MapPals2: ; 73253 (1c:7253) - dw $20 ; length - INCBIN "data/sgb_data/map_pals_2.bin" - -SGBData_MapPals3: ; 73277 (1c:7277) - dw $20 ; length - INCBIN "data/sgb_data/map_pals_3.bin" - -SGBData_MapPals4: ; 7329a (1c:729a) - dw $20 ; length - INCBIN "data/sgb_data/map_pals_4.bin" - -SGBData_MapPals5: ; 732bd (1c:72bd) - dw $20 ; length - INCBIN "data/sgb_data/map_pals_5.bin" - -SGBData_MapPals6: ; 732e0 (1c:72e0) - dw $20 ; length - INCBIN "data/sgb_data/map_pals_6.bin" - -SGBData_MapPals7: ; 73304 (1c:7304) - dw $20 ; length - INCBIN "data/sgb_data/map_pals_7.bin" - -SGBData_MapPals8: ; 73328 (1c:7328) - dw $20 ; length - INCBIN "data/sgb_data/map_pals_8.bin" - -SGBData_MapPals9: ; 7334b (1c:734b) - dw $20 ; length - INCBIN "data/sgb_data/map_pals_9.bin" - -SGBData_MapPals10: ; 7336f (1c:736f) - dw $20 ; length - INCBIN "data/sgb_data/map_pals_10.bin" - -SGBData_CharizardIntro: ; 73393 (1c:7393) - dw $20 ; length - INCBIN "data/sgb_data/charizard_intro_pals.bin" - -SGBData_ScytherIntro: ; 733b8 (1c:73b8) - dw $20 ; length - INCBIN "data/sgb_data/scyther_intro_pals.bin" - -SGBData_AerodactylIntro: ; 733dd (1c:73dd) - dw $20 ; length - INCBIN "data/sgb_data/aerodactyl_intro_pals.bin" - -SGBData_ColosseumBooster: ; 73402 (1c:7402) - dw $20 ; length - INCBIN "data/sgb_data/colosseum_booster_pals.bin" - -SGBData_EvolutionBooster: ; 73427 (1c:7427) - dw $20 ; length - INCBIN "data/sgb_data/evolution_booster_pals.bin" - -SGBData_MysteryBooster: ; 7344c (1c:744c) - dw $20 ; length - INCBIN "data/sgb_data/mystery_booster_pals.bin" - -SGBData_LaboratoryBooster: ; 73471 (1c:7471) - dw $20 ; length - INCBIN "data/sgb_data/laboratory_booster_pals.bin" - -SGBData_PlayerPortraitPals: ; 73496 (1c:7496) - dw $20 ; length - INCBIN "data/sgb_data/player_pals.bin" - -SGBData_LinkOpponentPortraitPals: ; 734bb (1c:74bb) - dw $20 ; length - INCBIN "data/sgb_data/link_opponent_pals.bin" - -SGBData_RonaldPortraitPals: ; 734e0 (1c:74e0) - dw $20 ; length - INCBIN "data/sgb_data/ronald_pals.bin" - -SGBData_SamPortraitPals: ; 73505 (1c:7505) - dw $20 ; length - INCBIN "data/sgb_data/sam_pals.bin" - -SGBData_ImakuniPortraitPals: ; 7352a (1c:752a) - dw $20 ; length - INCBIN "data/sgb_data/imakuni_pals.bin" - -SGBData_NikkiPortraitPals: ; 7354f (1c:754f) - dw $20 ; length - INCBIN "data/sgb_data/nikki_pals.bin" - -SGBData_RickPortraitPals: ; 73574 (1c:7574) - dw $20 ; length - INCBIN "data/sgb_data/rick_pals.bin" - -SGBData_KenPortraitPals: ; 73599 (1c:7599) - dw $20 ; length - INCBIN "data/sgb_data/ken_pals.bin" - -SGBData_AmyPortraitPals: ; 735be (1c:75be) - dw $20 ; length - INCBIN "data/sgb_data/amy_pals.bin" - -SGBData_IsaacPortraitPals: ; 735e3 (1c:75e3) - dw $20 ; length - INCBIN "data/sgb_data/isaac_pals.bin" - -SGBData_MitchPortraitPals: ; 73608 (1c:7608) - dw $20 ; length - INCBIN "data/sgb_data/mitch_pals.bin" - -SGBData_GenePortraitPals: ; 7362d (1c:762d) - dw $20 ; length - INCBIN "data/sgb_data/gene_pals.bin" - -SGBData_MurrayPortraitPals: ; 73652 (1c:7652) - dw $20 ; length - INCBIN "data/sgb_data/murray_pals.bin" - -SGBData_CourtneyPortraitPals: ; 73677 (1c:7677) - dw $20 ; length - INCBIN "data/sgb_data/courtney_pals.bin" - -SGBData_StevePortraitPals: ; 7369c (1c:769c) - dw $20 ; length - INCBIN "data/sgb_data/steve_pals.bin" - -SGBData_JackPortraitPals: ; 736c1 (1c:76c1) - dw $20 ; length - INCBIN "data/sgb_data/jack_pals.bin" - -SGBData_RodPortraitPals: ; 736e6 (1c:76e6) - dw $20 ; length - INCBIN "data/sgb_data/rod_pals.bin" - -SGBData_JosephPortraitPals: ; 7370b (1c:770b) - dw $20 ; length - INCBIN "data/sgb_data/joseph_pals.bin" - -SGBData_DavidPortraitPals: ; 73730 (1c:7730) - dw $20 ; length - INCBIN "data/sgb_data/david_pals.bin" - -SGBData_ErikPortraitPals: ; 73755 (1c:7755) - dw $20 ; length - INCBIN "data/sgb_data/erik_pals.bin" - -SGBData_JohnPortraitPals: ; 7377a (1c:777a) - dw $20 ; length - INCBIN "data/sgb_data/john_pals.bin" - -SGBData_AdamPortraitPals: ; 7379f (1c:779f) - dw $20 ; length - INCBIN "data/sgb_data/adam_pals.bin" - -SGBData_JonathanPortraitPals: ; 737c4 (1c:77c4) - dw $20 ; length - INCBIN "data/sgb_data/jonathan_pals.bin" - -SGBData_JoshuaPortraitPals: ; 737e9 (1c:77e9) - dw $20 ; length - INCBIN "data/sgb_data/joshua_pals.bin" - -SGBData_NicholasPortraitPals: ; 7380e (1c:780e) - dw $20 ; length - INCBIN "data/sgb_data/nicholas_pals.bin" - -SGBData_BrandonPortraitPals: ; 73833 (1c:7833) - dw $20 ; length - INCBIN "data/sgb_data/brandon_pals.bin" - -SGBData_MatthewPortraitPals: ; 73858 (1c:7858) - dw $20 ; length - INCBIN "data/sgb_data/matthew_pals.bin" - -SGBData_RyanPortraitPals: ; 7387d (1c:787d) - dw $20 ; length - INCBIN "data/sgb_data/ryan_pals.bin" - -SGBData_AndrewPortraitPals: ; 738a2 (1c:78a2) - dw $20 ; length - INCBIN "data/sgb_data/andrew_pals.bin" - -SGBData_ChrisPortraitPals: ; 738c7 (1c:78c7) - dw $20 ; length - INCBIN "data/sgb_data/chris_pals.bin" - -SGBData_MichaelPortraitPals: ; 738ec (1c:78ec) - dw $20 ; length - INCBIN "data/sgb_data/michael_pals.bin" - -SGBData_DanielPortraitPals: ; 73911 (1c:7911) - dw $20 ; length - INCBIN "data/sgb_data/daniel_pals.bin" - -SGBData_RobertPortraitPals: ; 73936 (1c:7936) - dw $20 ; length - INCBIN "data/sgb_data/robert_pals.bin" - -SGBData_BrittanyPortraitPals: ; 7395b (1c:795b) - dw $20 ; length - INCBIN "data/sgb_data/brittany_pals.bin" - -SGBData_KristinPortraitPals: ; 73980 (1c:7980) - dw $20 ; length - INCBIN "data/sgb_data/kristin_pals.bin" - -SGBData_HeatherPortraitPals: ; 739a5 (1c:79a5) - dw $20 ; length - INCBIN "data/sgb_data/heather_pals.bin" - -SGBData_SaraPortraitPals: ; 739ca (1c:79ca) - dw $20 ; length - INCBIN "data/sgb_data/sara_pals.bin" - -SGBData_AmandaPortraitPals: ; 739ef (1c:79ef) - dw $20 ; length - INCBIN "data/sgb_data/amanda_pals.bin" - -SGBData_JenniferPortraitPals: ; 73a14 (1c:7a14) - dw $20 ; length - INCBIN "data/sgb_data/jennifer_pals.bin" - -SGBData_JessicaPortraitPals: ; 73a39 (1c:7a39) - dw $20 ; length - INCBIN "data/sgb_data/jessica_pals.bin" - -SGBData_StephaniePortraitPals: ; 73a5e (1c:7a5e) - dw $20 ; length - INCBIN "data/sgb_data/stephanie_pals.bin" - -SGBData_AaronPortraitPals: ; 73a83 (1c:7a83) - dw $20 ; length - INCBIN "data/sgb_data/aaron_pals.bin" - -SGBData_GameBoyLink: ; 73aa8 (1c:7aa8) - dw $40 ; length - INCBIN "data/sgb_data/gameboy_link_pals.bin" - -SGBData_CardPop: ; 73ad8 (1c:7ad8) - dw $40 ; length - INCBIN "data/sgb_data/card_pop_pals.bin" - -SGBData_GameBoyPrinter: ; 73b05 (1c:7b05) - dw $40 ; length - INCBIN "data/sgb_data/gameboy_printer_pals.bin" - -SGBData_TitleScreen: ; 73b33 (1c:7b33) - dw $40 ; length - INCBIN "data/sgb_data/title_screen_pals.bin" diff --git a/src/engine/sgb.asm b/src/engine/sgb.asm new file mode 100644 index 0000000..288dc45 --- /dev/null +++ b/src/engine/sgb.asm @@ -0,0 +1,1022 @@ +SetMainSGBBorder: + ld a, [wConsole] + cp CONSOLE_SGB + ret nz ; exit if not SGB + ld b, $1 + ld a, EVENT_RECEIVED_LEGENDARY_CARDS + farcall GetEventValue + or a + jr z, .asm_70013 + ld b, $2 +.asm_70013 + ld a, b + call SetSGBBorder + ret + +SetIntroSGBBorder: + ld a, [wConsole] + cp CONSOLE_SGB + ret nz ; exit if not SGB + ld a, $0 + call SetSGBBorder + ret + +AtrcEnPacket_Disable: + sgb ATRC_EN, 1 ; sgb_command, length + db 1 + ds $0e + +; disable Controller Set-up Screen +IconEnPacket: + sgb ICON_EN, 3 ; sgb_command, length + db $01 + ds $0e + +; sets SGB border corresponding with value in register a +; $0 = intro +; $1 = medals (gold) +; $2 = medals (blue) +; $3 = debug +SetSGBBorder: + push hl + push bc + add a ; *2 + ld c, a + add a ; *4 + add c ; *6 + ld c, a + ld b, $0 + ld hl, .SGBBorders + add hl, bc + call DecompressAndSendSGBBorder + pop bc + pop hl + ret + +.SGBBorders + ; tiles, pals (?), map (?) + dw SGBBorderIntroGfxPointers, SGBData_BorderIntro3, SGBData_BorderIntro4 + dw SGBBorderMedalsGfxPointers, SGBData_BorderMedals3, SGBData_BorderMedals5 + dw SGBBorderMedalsGfxPointers, SGBData_BorderMedals4, SGBData_BorderMedals5 + dw SGBBorderDebugGfxPointers, SGBData_BorderDebug3, SGBData_BorderDebug4 + +; forces SGB border intro +; unreferenced? +Func_7006f: + ld a, [wConsole] + cp CONSOLE_SGB + ret nz ; exit if not SGB + ld de, SGBData_BorderIntro3 + ld hl, SGBData_BorderIntro4 + call SetMainSGBBorderPalsAndMap + call Func_701c0 + ret + +DecompressAndSendSGBBorder: + ld a, [wConsole] + cp CONSOLE_SGB + ret nz ; exit if not SGB + push hl + push bc + ld a, [hli] + push hl + ld h, [hl] + ld l, a + call Func_700a3 + pop hl + inc hl + + ld a, [hli] + ld e, a + ld a, [hli] + ld d, a + ld a, [hli] + ld h, [hl] + ld l, a + call SetMainSGBBorderPalsAndMap + call Func_701c0 + pop bc + pop hl + ret + +Func_700a3: + push hl + push bc + push de + push hl + call Func_70136 + pop hl + + push hl + ld a, [hli] + ld h, [hl] + ld l, a + ld de, v0Tiles1 + call DecompressSGBData + call PrepareBGMapForSendingSGBBorder + ld hl, ChrTrnPacket_BGTiles1 + call SendSGBBorder + pop hl + + ld de, 2 + add hl, de + ld a, [hli] + ld h, [hl] + ld l, a + ld a, [hli] + or [hl] + jr z, .asm_700da + call Func_70136 + dec hl + ld de, v0Tiles1 + call DecompressSGBData + ld hl, ChrTrnPacket_BGTiles2 + call SendSGBBorder +.asm_700da + pop de + pop bc + pop hl + ret + +; CHR_TRN: tiles $00-$7F, BG (border) tiles (from SNES $000-$FFF) +ChrTrnPacket_BGTiles1: + sgb CHR_TRN, 1 ; sgb_command, length + db 0 + ds $0e + +; CHR_TRN: tiles $80-$FF, BG (border) tiles (from SNES $000-$FFF) +ChrTrnPacket_BGTiles2: + sgb CHR_TRN, 1 ; sgb_command, length + db 1 + ds $0e + +; de = pals +; hl = map +SetMainSGBBorderPalsAndMap: + push hl + push bc + push de + push hl + push de + push hl ; input hl + call Func_70136 + pop hl + ld de, v0Tiles1 + call DecompressSGBData + + pop hl ; input de + ld de, v0Tiles2 + call DecompressSGBData + call PrepareBGMapForSendingSGBBorder + + pop hl ; input hl + call FillSGBBorderMedalSlots + ld hl, PctTrnPacket + call SendSGBBorder + pop de + pop bc + pop hl + ret + +; PCT_TRN: read tile map & palette data into VRAM (from SNES $000-$87F) +PctTrnPacket: + sgb PCT_TRN, 1 ; sgb_command, length + ds $0f + +Func_70136: + push hl + push bc + push de + ldh a, [hSCX] + ld [wd41d], a + ldh a, [hSCY] + ld [wd41e], a + ld a, [wBGP] + ld [wd41f], a + ld a, [wLCDC] + ld [wd420], a + + di + ld hl, MaskEnPacket_Freeze_Bank1c + call SendSGB + call DisableLCD + ld a, [wLCDC] + and LCDC_BGENABLE | LCDC_WINSELECT + or LCDC_BGON + ld [wLCDC], a + ld a, %11100100 + ldh [rBGP], a + call SetBGP + + xor a + ldh [hSCX], a + ldh [rSCX], a + ldh [hSCY], a + ldh [rSCY], a + pop de + pop bc + pop hl + ret + +SendSGBBorder: + push hl + push bc + push de + push hl + call EnableLCD + pop hl + call SendSGB + ld a, [wd41d] + ldh [hSCX], a + ld a, [wd41e] + ldh [hSCY], a + ld a, [wd41f] + call SetBGP + ld a, [wd420] + ld [wLCDC], a + call DisableLCD + ei + pop de + pop bc + pop hl + ret + +; MASK_EN on +MaskEnPacket_Freeze_Bank1c: + sgb MASK_EN, 1 ; sgb_command, length + db MASK_EN_FREEZE_SCREEN + ds $0e + +; MASK_EN off +MaskEnPacket_Cancel_Bank1c: + sgb MASK_EN, 1 ; sgb_command, length + db MASK_EN_CANCEL_MASK + ds $0e + +Func_701c0: + push hl + push bc + call DisableLCD + xor a + ld c, $10 + ld hl, v0Tiles2 +.asm_701cb + ld [hli], a + dec c + jr nz, .asm_701cb + ld a, [wTileMapFill] + push af + xor a + ld [wTileMapFill], a + call EmptyScreen + pop af + ld [wTileMapFill], a + di + ld hl, MaskEnPacket_Cancel_Bank1c + call SendSGB + ei + pop bc + pop hl + ret + +; decompresses data pointed by hl to de +DecompressSGBData: + ld a, [hli] + ld c, a + ld a, [hli] + ld b, a + or c + ret z + push de + push bc + ld e, l + ld d, h + ld b, HIGH(wDecompressionSecondaryBuffer) + call InitDataDecompression + pop bc + pop de + call DecompressData + ret + +; fills a 20x13 rectangle in v0BGMap0 +; with values ascending bytes starting at $80 +PrepareBGMapForSendingSGBBorder: + ld hl, v0BGMap0 + ld de, $000c + ld a, $80 + ld c, $d +.asm_70208 + ld b, SCREEN_WIDTH +.asm_7020a + ld [hli], a + inc a + dec b + jr nz, .asm_7020a + add hl, de + dec c + jr nz, .asm_70208 + ret + +; iterates all the medals obtained by the player +; and fills the corresponding medal slot in the SGB border +FillSGBBorderMedalSlots: +; exit if not SGBData_BorderMedals5 + ld a, l + cp LOW(SGBData_BorderMedals5) + ret nz + ld a, h + cp HIGH(SGBData_BorderMedals5) + ret nz + + ld hl, .SGBBorderMedalTiles + ld a, EVENT_MEDAL_FLAGS + farcall GetEventValue + ld c, NUM_MEDALS +.loop_medals + push bc + push hl + push af + bit 7, a + jr z, .next_medal + + ld c, 3 * 3 +.loop_tiles + push bc + ld e, [hl] + inc hl + ld d, [hl] + inc hl + ld a, [hli] + ld [de], a + inc de + ld a, [hli] + ld [de], a + pop bc + dec c + jr nz, .loop_tiles + +.next_medal + pop af + rlca + pop hl + ld bc, 3 * 3 * 4 + add hl, bc + pop bc + dec c + jr nz, .loop_medals + ret + +border_medal_tile: MACRO + dw \1 ; VRAM address + db \2 ; tile + db \3 ; pal (?) +ENDM + +.SGBBorderMedalTiles +; GRASS_MEDAL + border_medal_tile v0Tiles1 + $182, $3f, $10 + border_medal_tile v0Tiles1 + $184, $40, $10 + border_medal_tile v0Tiles1 + $186, $41, $10 + border_medal_tile v0Tiles1 + $1c2, $42, $10 + border_medal_tile v0Tiles1 + $1c4, $43, $10 + border_medal_tile v0Tiles1 + $1c6, $44, $10 + border_medal_tile v0Tiles1 + $202, $45, $10 + border_medal_tile v0Tiles1 + $204, $46, $10 + border_medal_tile v0Tiles1 + $206, $47, $10 + +; SCIENCE_MEDAL + border_medal_tile v0Tiles1 + $282, $48, $10 + border_medal_tile v0Tiles1 + $284, $49, $10 + border_medal_tile v0Tiles1 + $286, $4a, $10 + border_medal_tile v0Tiles1 + $2c2, $4b, $10 + border_medal_tile v0Tiles1 + $2c4, $4c, $10 + border_medal_tile v0Tiles1 + $2c6, $4d, $10 + border_medal_tile v0Tiles1 + $302, $4e, $10 + border_medal_tile v0Tiles1 + $304, $4f, $10 + border_medal_tile v0Tiles1 + $306, $50, $10 + +; FIRE_MEDAL + border_medal_tile v0Tiles1 + $382, $51, $10 + border_medal_tile v0Tiles1 + $384, $52, $10 + border_medal_tile v0Tiles1 + $386, $53, $10 + border_medal_tile v0Tiles1 + $3c2, $54, $10 + border_medal_tile v0Tiles1 + $3c4, $55, $10 + border_medal_tile v0Tiles1 + $3c6, $56, $10 + border_medal_tile v0Tiles1 + $402, $57, $10 + border_medal_tile v0Tiles1 + $404, $58, $10 + border_medal_tile v0Tiles1 + $406, $59, $10 + +; WATER_MEDAL + border_medal_tile v0Tiles1 + $482, $5a, $10 + border_medal_tile v0Tiles1 + $484, $5b, $10 + border_medal_tile v0Tiles1 + $486, $5c, $10 + border_medal_tile v0Tiles1 + $4c2, $5d, $10 + border_medal_tile v0Tiles1 + $4c4, $5e, $10 + border_medal_tile v0Tiles1 + $4c6, $5f, $10 + border_medal_tile v0Tiles1 + $502, $60, $10 + border_medal_tile v0Tiles1 + $504, $61, $10 + border_medal_tile v0Tiles1 + $506, $62, $10 + +; LIGHTNING_MEDAL + border_medal_tile v0Tiles1 + $1b8, $63, $10 + border_medal_tile v0Tiles1 + $1ba, $64, $10 + border_medal_tile v0Tiles1 + $1bc, $65, $10 + border_medal_tile v0Tiles1 + $1f8, $66, $10 + border_medal_tile v0Tiles1 + $1fa, $67, $10 + border_medal_tile v0Tiles1 + $1fc, $68, $10 + border_medal_tile v0Tiles1 + $238, $69, $10 + border_medal_tile v0Tiles1 + $23a, $6a, $10 + border_medal_tile v0Tiles1 + $23c, $6b, $10 + +; PSYCHIC_MEDAL + border_medal_tile v0Tiles1 + $2b8, $7e, $10 + border_medal_tile v0Tiles1 + $2ba, $7f, $10 + border_medal_tile v0Tiles1 + $2bc, $80, $10 + border_medal_tile v0Tiles1 + $2f8, $81, $10 + border_medal_tile v0Tiles1 + $2fa, $82, $10 + border_medal_tile v0Tiles1 + $2fc, $83, $10 + border_medal_tile v0Tiles1 + $338, $84, $10 + border_medal_tile v0Tiles1 + $33a, $85, $10 + border_medal_tile v0Tiles1 + $33c, $86, $10 + +; ROCK_MEDAL + border_medal_tile v0Tiles1 + $3b8, $75, $10 + border_medal_tile v0Tiles1 + $3ba, $76, $10 + border_medal_tile v0Tiles1 + $3bc, $77, $10 + border_medal_tile v0Tiles1 + $3f8, $78, $10 + border_medal_tile v0Tiles1 + $3fa, $79, $10 + border_medal_tile v0Tiles1 + $3fc, $7a, $10 + border_medal_tile v0Tiles1 + $438, $7b, $10 + border_medal_tile v0Tiles1 + $43a, $7c, $10 + border_medal_tile v0Tiles1 + $43c, $7d, $10 + +; FIGHTING_MEDAL + border_medal_tile v0Tiles1 + $4b8, $6c, $10 + border_medal_tile v0Tiles1 + $4ba, $6d, $10 + border_medal_tile v0Tiles1 + $4bc, $6e, $10 + border_medal_tile v0Tiles1 + $4f8, $6f, $10 + border_medal_tile v0Tiles1 + $4fa, $70, $10 + border_medal_tile v0Tiles1 + $4fc, $71, $10 + border_medal_tile v0Tiles1 + $538, $72, $10 + border_medal_tile v0Tiles1 + $53a, $73, $10 + border_medal_tile v0Tiles1 + $53c, $74, $10 + +; decompresses palette data depending on wCurMapSGBPals +; then sends it as SGB packet +SetSGB2AndSGB3MapPalette: + ld a, [wConsole] + cp CONSOLE_SGB + ret nz ; return if not SGB + ld a, [wCurMapSGBPals] + or a + ret z ; not valid + + push hl + push bc + push de + ld a, [wCurMapSGBPals] + add a + ld c, a + ld b, $0 + ld hl, .pal_data_pointers + add hl, bc + ld a, [hli] + ld h, [hl] + ld l, a + call DecompressSGBPalette + + ; load palettes to wTempSGBPacket + ld hl, wDecompressionBuffer + ld de, wTempSGBPacket + 1 ; PAL Packet color #0 (PAL23's SGB2) + ld bc, 8 ; pal size + call CopyDataHLtoDE + ld hl, wDecompressionBuffer + 34 + ld de, wTempSGBPacket + 9 ; PAL Packet color #4 (PAL23's SGB3) + ld bc, 6 + call CopyDataHLtoDE + + xor a + ld [wTempSGBPacket + 15], a + ld hl, wTempSGBPacket + ld a, PAL01 << 3 + 1 + ld [hl], a + call Func_704c7 + call SendSGB + pop de + pop bc + pop hl + ret + +.pal_data_pointers + dw SGBData_MapPals1 ; unused + dw SGBData_MapPals1 ; MAP_SGB_PALS_1 + dw SGBData_MapPals2 ; MAP_SGB_PALS_2 + dw SGBData_MapPals3 ; MAP_SGB_PALS_3 + dw SGBData_MapPals4 ; MAP_SGB_PALS_4 + dw SGBData_MapPals5 ; MAP_SGB_PALS_5 + dw SGBData_MapPals6 ; MAP_SGB_PALS_6 + dw SGBData_MapPals7 ; MAP_SGB_PALS_7 + dw SGBData_MapPals8 ; MAP_SGB_PALS_8 + dw SGBData_MapPals9 ; MAP_SGB_PALS_9 + dw SGBData_MapPals10 ; MAP_SGB_PALS_10 + +Func_703cb: + ld a, [wConsole] + cp CONSOLE_SGB + ret nz + push hl + push bc + push de + call DecompressSGBPalette + ld hl, wDecompressionBuffer + ld de, wTempSGBPacket + $1 + ld bc, $8 ; palette 2, color 0-3 + call CopyDataHLtoDE + ld hl, wDecompressionBuffer + $22 + ld de, wTempSGBPacket + $9 + ld bc, $6 ; palette 3, color 1-3 + call CopyDataHLtoDE + xor a + ld [wTempSGBPacket + $f], a + ld hl, wTempSGBPacket + ld a, PAL23 << 3 + 1 + ld [hl], a + call Func_704c7 + call SendSGB + pop de + pop bc + pop hl + ret + +DecompressSGBPalette: + push hl + push bc + push de + ld c, [hl] + inc hl + ld b, [hl] + inc hl + push bc + ld e, l + ld d, h + ld b, HIGH(wDecompressionSecondaryBuffer) + call InitDataDecompression + pop bc + ld de, wDecompressionBuffer + call DecompressData + pop de + pop bc + pop hl + ret + +; sends an SGB packet related with palettes +Func_7041d: + ld a, [wConsole] + cp CONSOLE_SGB + ret nz ; exit if not SGB + + push hl + push bc + push de + push bc + push hl + push hl + ld hl, SGBData_PlayerPortraitPals + call DecompressSGBPalette + ld hl, wLoadedPalData + ld de, wTempSGBPacket + $1 + ld bc, $8 + call CopyDataHLtoDE + + pop hl + call DecompressSGBPalette + ld hl, wLoadedPalData + 2 + ld de, wTempSGBPacket + $9 + ld bc, $6 + call CopyDataHLtoDE + + xor a + ld [wTempSGBPacket + $f], a + ld hl, wTempSGBPacket + ld a, $09 + ld [hl], a + call Func_704c7 + call SendSGB + + pop hl + ld c, $0f + ld a, l + cp LOW(SGBData_PlayerPortraitPals) + jr nz, .asm_7046a + ld a, h + cp HIGH(SGBData_PlayerPortraitPals) + jr nz, .asm_7046a + ld c, $0a + +.asm_7046a + ld a, c + ld [wTempSGBPacket + $3], a + pop bc + ld hl, wTempSGBPacket + push hl + ld a, $21 + ld [hli], a + ld a, $01 + ld [hli], a + ld a, $01 + ld [hli], a + inc hl + ld a, b + ld [hli], a + ld a, c + ld [hli], a + ld a, $05 + add b + ld [hli], a + ld a, $05 + add c + ld [hli], a + xor a + ld [wTempSGBPacket + $e], a + ld [wTempSGBPacket + $f], a + pop hl + call SendSGB + pop de + pop bc + pop hl + ret + +; send an ATTR_BLK SGB packet +; input: +; b = x1 (left) +; c = y1 (upper) +; d = block width +; e = block height +; l = %00xxyyzz, palette number for: outside block, block border, inside block +Func_70498: + ld a, [wConsole] + cp CONSOLE_SGB + ret nz + push hl + push bc + push de + ld a, l + ld [wTempSGBPacket + 3], a ; Color Palette Designation + ld hl, wTempSGBPacket + push hl + ld a, ATTR_BLK << 3 + 1 + ld [hli], a ; packet command and length + ld a, 1 + ld [hli], a ; 1 data set + ld a, ATTR_BLK_CTRL_INSIDE + ld [hli], a ; control code + inc hl + ld a, b + ld [hli], a ; x1 + ld a, c + ld [hli], a ; y1 + ld a, d + dec a + add b + ld [hli], a ; x2 + ld a, e + dec a + add c + ld [hli], a ; y2 + pop hl + call SendSGB + pop de + pop bc + pop hl + ret + +; set color 0 to default white rgb(28, 28, 24) +; input: +; hl = pointer to start of SGB packet +Func_704c7: + push af + push hl + inc hl + ld a, LOW(24 << 10 | 28 << 5 | 28) + ld [hli], a + ld a, HIGH(24 << 10 | 28 << 5 | 28) + ld [hl], a + pop hl + pop af + ret + +SGBData_BorderDebug4: + dw $800 ; length + INCBIN "data/sgb_data/border_debug_4.bin" + +SGBData_BorderIntro4: + dw $800 ; length + INCBIN "data/sgb_data/border_intro_4.bin" + +SGBData_BorderMedals5: + dw $800 ; length + INCBIN "data/sgb_data/border_medals_5.bin" + +SGBBorderDebugGfxPointers: + dw SGBData_BorderDebug1 + dw SGBData_BorderDebug2 + +SGBData_BorderDebug1: + dw $1000 ; length + INCBIN "data/sgb_data/border_debug_1.bin" + +SGBData_BorderDebug2: + dw $a0 ; length + INCBIN "data/sgb_data/border_debug_2.bin" + +SGBBorderIntroGfxPointers: + dw SGBData_BorderIntro1 + dw SGBData_BorderIntro2 + +SGBData_BorderIntro1: + dw $1000 ; length + INCBIN "data/sgb_data/border_intro_1.bin" + +SGBData_BorderIntro2: + dw $4e0 ; length + INCBIN "data/sgb_data/border_intro_2.bin" + +SGBBorderMedalsGfxPointers: + dw SGBData_BorderMedals1 + dw SGBData_BorderMedals2 + +SGBData_BorderMedals1: + dw $1000 ; length + INCBIN "data/sgb_data/border_medals_1.bin" + +SGBData_BorderMedals2: + dw $100 ; length + INCBIN "data/sgb_data/border_medals_2.bin" + +SGBData_BorderDebug3: + dw $60 ; length + INCBIN "data/sgb_data/border_debug_3.bin" + +SGBData_BorderIntro3: + dw $60 ; length + INCBIN "data/sgb_data/border_intro_3.bin" + +SGBData_BorderMedals3: + dw $60 ; length + INCBIN "data/sgb_data/border_medals_3.bin" + +SGBData_BorderMedals4: + dw $60 ; length + INCBIN "data/sgb_data/border_medals_4.bin" + +SGBData_MapPals1: + dw $20 ; length + INCBIN "data/sgb_data/map_pals_1.bin" + +SGBData_MapPals2: + dw $20 ; length + INCBIN "data/sgb_data/map_pals_2.bin" + +SGBData_MapPals3: + dw $20 ; length + INCBIN "data/sgb_data/map_pals_3.bin" + +SGBData_MapPals4: + dw $20 ; length + INCBIN "data/sgb_data/map_pals_4.bin" + +SGBData_MapPals5: + dw $20 ; length + INCBIN "data/sgb_data/map_pals_5.bin" + +SGBData_MapPals6: + dw $20 ; length + INCBIN "data/sgb_data/map_pals_6.bin" + +SGBData_MapPals7: + dw $20 ; length + INCBIN "data/sgb_data/map_pals_7.bin" + +SGBData_MapPals8: + dw $20 ; length + INCBIN "data/sgb_data/map_pals_8.bin" + +SGBData_MapPals9: + dw $20 ; length + INCBIN "data/sgb_data/map_pals_9.bin" + +SGBData_MapPals10: + dw $20 ; length + INCBIN "data/sgb_data/map_pals_10.bin" + +SGBData_CharizardIntro: + dw $20 ; length + INCBIN "data/sgb_data/charizard_intro_pals.bin" + +SGBData_ScytherIntro: + dw $20 ; length + INCBIN "data/sgb_data/scyther_intro_pals.bin" + +SGBData_AerodactylIntro: + dw $20 ; length + INCBIN "data/sgb_data/aerodactyl_intro_pals.bin" + +SGBData_ColosseumBooster: + dw $20 ; length + INCBIN "data/sgb_data/colosseum_booster_pals.bin" + +SGBData_EvolutionBooster: + dw $20 ; length + INCBIN "data/sgb_data/evolution_booster_pals.bin" + +SGBData_MysteryBooster: + dw $20 ; length + INCBIN "data/sgb_data/mystery_booster_pals.bin" + +SGBData_LaboratoryBooster: + dw $20 ; length + INCBIN "data/sgb_data/laboratory_booster_pals.bin" + +SGBData_PlayerPortraitPals: + dw $20 ; length + INCBIN "data/sgb_data/player_pals.bin" + +SGBData_LinkOpponentPortraitPals: + dw $20 ; length + INCBIN "data/sgb_data/link_opponent_pals.bin" + +SGBData_RonaldPortraitPals: + dw $20 ; length + INCBIN "data/sgb_data/ronald_pals.bin" + +SGBData_SamPortraitPals: + dw $20 ; length + INCBIN "data/sgb_data/sam_pals.bin" + +SGBData_ImakuniPortraitPals: + dw $20 ; length + INCBIN "data/sgb_data/imakuni_pals.bin" + +SGBData_NikkiPortraitPals: + dw $20 ; length + INCBIN "data/sgb_data/nikki_pals.bin" + +SGBData_RickPortraitPals: + dw $20 ; length + INCBIN "data/sgb_data/rick_pals.bin" + +SGBData_KenPortraitPals: + dw $20 ; length + INCBIN "data/sgb_data/ken_pals.bin" + +SGBData_AmyPortraitPals: + dw $20 ; length + INCBIN "data/sgb_data/amy_pals.bin" + +SGBData_IsaacPortraitPals: + dw $20 ; length + INCBIN "data/sgb_data/isaac_pals.bin" + +SGBData_MitchPortraitPals: + dw $20 ; length + INCBIN "data/sgb_data/mitch_pals.bin" + +SGBData_GenePortraitPals: + dw $20 ; length + INCBIN "data/sgb_data/gene_pals.bin" + +SGBData_MurrayPortraitPals: + dw $20 ; length + INCBIN "data/sgb_data/murray_pals.bin" + +SGBData_CourtneyPortraitPals: + dw $20 ; length + INCBIN "data/sgb_data/courtney_pals.bin" + +SGBData_StevePortraitPals: + dw $20 ; length + INCBIN "data/sgb_data/steve_pals.bin" + +SGBData_JackPortraitPals: + dw $20 ; length + INCBIN "data/sgb_data/jack_pals.bin" + +SGBData_RodPortraitPals: + dw $20 ; length + INCBIN "data/sgb_data/rod_pals.bin" + +SGBData_JosephPortraitPals: + dw $20 ; length + INCBIN "data/sgb_data/joseph_pals.bin" + +SGBData_DavidPortraitPals: + dw $20 ; length + INCBIN "data/sgb_data/david_pals.bin" + +SGBData_ErikPortraitPals: + dw $20 ; length + INCBIN "data/sgb_data/erik_pals.bin" + +SGBData_JohnPortraitPals: + dw $20 ; length + INCBIN "data/sgb_data/john_pals.bin" + +SGBData_AdamPortraitPals: + dw $20 ; length + INCBIN "data/sgb_data/adam_pals.bin" + +SGBData_JonathanPortraitPals: + dw $20 ; length + INCBIN "data/sgb_data/jonathan_pals.bin" + +SGBData_JoshuaPortraitPals: + dw $20 ; length + INCBIN "data/sgb_data/joshua_pals.bin" + +SGBData_NicholasPortraitPals: + dw $20 ; length + INCBIN "data/sgb_data/nicholas_pals.bin" + +SGBData_BrandonPortraitPals: + dw $20 ; length + INCBIN "data/sgb_data/brandon_pals.bin" + +SGBData_MatthewPortraitPals: + dw $20 ; length + INCBIN "data/sgb_data/matthew_pals.bin" + +SGBData_RyanPortraitPals: + dw $20 ; length + INCBIN "data/sgb_data/ryan_pals.bin" + +SGBData_AndrewPortraitPals: + dw $20 ; length + INCBIN "data/sgb_data/andrew_pals.bin" + +SGBData_ChrisPortraitPals: + dw $20 ; length + INCBIN "data/sgb_data/chris_pals.bin" + +SGBData_MichaelPortraitPals: + dw $20 ; length + INCBIN "data/sgb_data/michael_pals.bin" + +SGBData_DanielPortraitPals: + dw $20 ; length + INCBIN "data/sgb_data/daniel_pals.bin" + +SGBData_RobertPortraitPals: + dw $20 ; length + INCBIN "data/sgb_data/robert_pals.bin" + +SGBData_BrittanyPortraitPals: + dw $20 ; length + INCBIN "data/sgb_data/brittany_pals.bin" + +SGBData_KristinPortraitPals: + dw $20 ; length + INCBIN "data/sgb_data/kristin_pals.bin" + +SGBData_HeatherPortraitPals: + dw $20 ; length + INCBIN "data/sgb_data/heather_pals.bin" + +SGBData_SaraPortraitPals: + dw $20 ; length + INCBIN "data/sgb_data/sara_pals.bin" + +SGBData_AmandaPortraitPals: + dw $20 ; length + INCBIN "data/sgb_data/amanda_pals.bin" + +SGBData_JenniferPortraitPals: + dw $20 ; length + INCBIN "data/sgb_data/jennifer_pals.bin" + +SGBData_JessicaPortraitPals: + dw $20 ; length + INCBIN "data/sgb_data/jessica_pals.bin" + +SGBData_StephaniePortraitPals: + dw $20 ; length + INCBIN "data/sgb_data/stephanie_pals.bin" + +SGBData_AaronPortraitPals: + dw $20 ; length + INCBIN "data/sgb_data/aaron_pals.bin" + +SGBData_GameBoyLink: + dw $40 ; length + INCBIN "data/sgb_data/gameboy_link_pals.bin" + +SGBData_CardPop: + dw $40 ; length + INCBIN "data/sgb_data/card_pop_pals.bin" + +SGBData_GameBoyPrinter: + dw $40 ; length + INCBIN "data/sgb_data/gameboy_printer_pals.bin" + +SGBData_TitleScreen: + dw $40 ; length + INCBIN "data/sgb_data/title_screen_pals.bin" diff --git a/src/layout.link b/src/layout.link index 46ef66d..73f98f3 100644 --- a/src/layout.link +++ b/src/layout.link @@ -98,7 +98,7 @@ ROMX $18 ROMX $19 "Text 13" ROMX $1c - "Bank 1C" + "SGB" ROMX $1d "Gfx 1" ROMX $1e diff --git a/src/main.asm b/src/main.asm index b08bb10..e1d4c34 100644 --- a/src/main.asm +++ b/src/main.asm @@ -113,8 +113,8 @@ INCLUDE "data/decks.asm" SECTION "Cards", ROMX INCLUDE "data/cards.asm" -SECTION "Bank 1C", ROMX -INCLUDE "engine/bank1c.asm" +SECTION "SGB", ROMX +INCLUDE "engine/sgb.asm" SECTION "Bank 20", ROMX INCLUDE "engine/bank20.asm" -- cgit v1.2.3 From bae47106f18266d2c3b97b6d954b91d9b16c0ccf Mon Sep 17 00:00:00 2001 From: ElectroDeoxys Date: Wed, 29 Sep 2021 09:02:02 +0100 Subject: Reorganise some folders in engine/ --- src/engine/ai/attacks.asm | 721 -- src/engine/ai/boss_deck_set_up.asm | 167 - src/engine/ai/common.asm | 970 -- src/engine/ai/core.asm | 2770 ----- src/engine/ai/damage_calculation.asm | 450 - src/engine/ai/deck_ai.asm | 82 - src/engine/ai/decks/fire_charge.asm | 80 - src/engine/ai/decks/first_strike.asm | 76 - src/engine/ai/decks/flower_power.asm | 75 - src/engine/ai/decks/general.asm | 194 - src/engine/ai/decks/general_no_retreat.asm | 140 - src/engine/ai/decks/go_go_rain_dance.asm | 79 - src/engine/ai/decks/im_ronald.asm | 80 - src/engine/ai/decks/invincible_ronald.asm | 78 - src/engine/ai/decks/legendary_articuno.asm | 209 - src/engine/ai/decks/legendary_dragonite.asm | 166 - src/engine/ai/decks/legendary_moltres.asm | 176 - src/engine/ai/decks/legendary_ronald.asm | 203 - src/engine/ai/decks/legendary_zapdos.asm | 153 - src/engine/ai/decks/powerful_ronald.asm | 92 - src/engine/ai/decks/rock_crusher.asm | 74 - src/engine/ai/decks/sams_practice.asm | 205 - src/engine/ai/decks/strange_psyshock.asm | 81 - src/engine/ai/decks/unreferenced.asm | 42 - src/engine/ai/decks/wonders_of_science.asm | 77 - src/engine/ai/decks/zapping_selfdestruct.asm | 75 - src/engine/ai/energy.asm | 1048 -- src/engine/ai/hand_pokemon.asm | 627 -- src/engine/ai/init.asm | 98 - src/engine/ai/pkmn_powers.asm | 1228 --- src/engine/ai/retreat.asm | 1009 -- src/engine/ai/special_attacks.asm | 481 - src/engine/ai/trainer_cards.asm | 6073 ----------- src/engine/duel/ai/attacks.asm | 721 ++ src/engine/duel/ai/boss_deck_set_up.asm | 167 + src/engine/duel/ai/common.asm | 970 ++ src/engine/duel/ai/core.asm | 2770 +++++ src/engine/duel/ai/damage_calculation.asm | 450 + src/engine/duel/ai/deck_ai.asm | 82 + src/engine/duel/ai/decks/fire_charge.asm | 80 + src/engine/duel/ai/decks/first_strike.asm | 76 + src/engine/duel/ai/decks/flower_power.asm | 75 + src/engine/duel/ai/decks/general.asm | 194 + src/engine/duel/ai/decks/general_no_retreat.asm | 140 + src/engine/duel/ai/decks/go_go_rain_dance.asm | 79 + src/engine/duel/ai/decks/im_ronald.asm | 80 + src/engine/duel/ai/decks/invincible_ronald.asm | 78 + src/engine/duel/ai/decks/legendary_articuno.asm | 209 + src/engine/duel/ai/decks/legendary_dragonite.asm | 166 + src/engine/duel/ai/decks/legendary_moltres.asm | 176 + src/engine/duel/ai/decks/legendary_ronald.asm | 203 + src/engine/duel/ai/decks/legendary_zapdos.asm | 153 + src/engine/duel/ai/decks/powerful_ronald.asm | 92 + src/engine/duel/ai/decks/rock_crusher.asm | 74 + src/engine/duel/ai/decks/sams_practice.asm | 205 + src/engine/duel/ai/decks/strange_psyshock.asm | 81 + src/engine/duel/ai/decks/unreferenced.asm | 42 + src/engine/duel/ai/decks/wonders_of_science.asm | 77 + src/engine/duel/ai/decks/zapping_selfdestruct.asm | 75 + src/engine/duel/ai/energy.asm | 1048 ++ src/engine/duel/ai/hand_pokemon.asm | 627 ++ src/engine/duel/ai/init.asm | 98 + src/engine/duel/ai/pkmn_powers.asm | 1228 +++ src/engine/duel/ai/retreat.asm | 1009 ++ src/engine/duel/ai/special_attacks.asm | 481 + src/engine/duel/ai/trainer_cards.asm | 6073 +++++++++++ src/engine/duel/effect_functions.asm | 11194 ++++++++++++++++++++ src/engine/effect_functions.asm | 11194 -------------------- src/engine/gfx/sprite_vblank.asm | 39 + src/engine/sprite_vblank.asm | 39 - src/home/ai.asm | 2 +- src/main.asm | 12 +- 72 files changed, 29319 insertions(+), 29319 deletions(-) delete mode 100644 src/engine/ai/attacks.asm delete mode 100644 src/engine/ai/boss_deck_set_up.asm delete mode 100644 src/engine/ai/common.asm delete mode 100644 src/engine/ai/core.asm delete mode 100644 src/engine/ai/damage_calculation.asm delete mode 100644 src/engine/ai/deck_ai.asm delete mode 100644 src/engine/ai/decks/fire_charge.asm delete mode 100644 src/engine/ai/decks/first_strike.asm delete mode 100644 src/engine/ai/decks/flower_power.asm delete mode 100644 src/engine/ai/decks/general.asm delete mode 100644 src/engine/ai/decks/general_no_retreat.asm delete mode 100644 src/engine/ai/decks/go_go_rain_dance.asm delete mode 100644 src/engine/ai/decks/im_ronald.asm delete mode 100644 src/engine/ai/decks/invincible_ronald.asm delete mode 100644 src/engine/ai/decks/legendary_articuno.asm delete mode 100644 src/engine/ai/decks/legendary_dragonite.asm delete mode 100644 src/engine/ai/decks/legendary_moltres.asm delete mode 100644 src/engine/ai/decks/legendary_ronald.asm delete mode 100644 src/engine/ai/decks/legendary_zapdos.asm delete mode 100644 src/engine/ai/decks/powerful_ronald.asm delete mode 100644 src/engine/ai/decks/rock_crusher.asm delete mode 100644 src/engine/ai/decks/sams_practice.asm delete mode 100644 src/engine/ai/decks/strange_psyshock.asm delete mode 100644 src/engine/ai/decks/unreferenced.asm delete mode 100644 src/engine/ai/decks/wonders_of_science.asm delete mode 100644 src/engine/ai/decks/zapping_selfdestruct.asm delete mode 100644 src/engine/ai/energy.asm delete mode 100644 src/engine/ai/hand_pokemon.asm delete mode 100644 src/engine/ai/init.asm delete mode 100644 src/engine/ai/pkmn_powers.asm delete mode 100644 src/engine/ai/retreat.asm delete mode 100644 src/engine/ai/special_attacks.asm delete mode 100644 src/engine/ai/trainer_cards.asm create mode 100644 src/engine/duel/ai/attacks.asm create mode 100644 src/engine/duel/ai/boss_deck_set_up.asm create mode 100644 src/engine/duel/ai/common.asm create mode 100644 src/engine/duel/ai/core.asm create mode 100644 src/engine/duel/ai/damage_calculation.asm create mode 100644 src/engine/duel/ai/deck_ai.asm create mode 100644 src/engine/duel/ai/decks/fire_charge.asm create mode 100644 src/engine/duel/ai/decks/first_strike.asm create mode 100644 src/engine/duel/ai/decks/flower_power.asm create mode 100644 src/engine/duel/ai/decks/general.asm create mode 100644 src/engine/duel/ai/decks/general_no_retreat.asm create mode 100644 src/engine/duel/ai/decks/go_go_rain_dance.asm create mode 100644 src/engine/duel/ai/decks/im_ronald.asm create mode 100644 src/engine/duel/ai/decks/invincible_ronald.asm create mode 100644 src/engine/duel/ai/decks/legendary_articuno.asm create mode 100644 src/engine/duel/ai/decks/legendary_dragonite.asm create mode 100644 src/engine/duel/ai/decks/legendary_moltres.asm create mode 100644 src/engine/duel/ai/decks/legendary_ronald.asm create mode 100644 src/engine/duel/ai/decks/legendary_zapdos.asm create mode 100644 src/engine/duel/ai/decks/powerful_ronald.asm create mode 100644 src/engine/duel/ai/decks/rock_crusher.asm create mode 100644 src/engine/duel/ai/decks/sams_practice.asm create mode 100644 src/engine/duel/ai/decks/strange_psyshock.asm create mode 100644 src/engine/duel/ai/decks/unreferenced.asm create mode 100644 src/engine/duel/ai/decks/wonders_of_science.asm create mode 100644 src/engine/duel/ai/decks/zapping_selfdestruct.asm create mode 100644 src/engine/duel/ai/energy.asm create mode 100644 src/engine/duel/ai/hand_pokemon.asm create mode 100644 src/engine/duel/ai/init.asm create mode 100644 src/engine/duel/ai/pkmn_powers.asm create mode 100644 src/engine/duel/ai/retreat.asm create mode 100644 src/engine/duel/ai/special_attacks.asm create mode 100644 src/engine/duel/ai/trainer_cards.asm create mode 100644 src/engine/duel/effect_functions.asm delete mode 100644 src/engine/effect_functions.asm create mode 100644 src/engine/gfx/sprite_vblank.asm delete mode 100644 src/engine/sprite_vblank.asm diff --git a/src/engine/ai/attacks.asm b/src/engine/ai/attacks.asm deleted file mode 100644 index 69ae2e1..0000000 --- a/src/engine/ai/attacks.asm +++ /dev/null @@ -1,721 +0,0 @@ -; have AI choose an attack to use, but do not execute it. -; return carry if an attack is chosen. -AIProcessButDontUseAttack: ; 169ca (5:69ca) - ld a, $01 - ld [wAIExecuteProcessedAttack], a - -; backup wPlayAreaAIScore in wTempPlayAreaAIScore. - ld de, wTempPlayAreaAIScore - ld hl, wPlayAreaAIScore - ld b, MAX_PLAY_AREA_POKEMON -.loop - ld a, [hli] - ld [de], a - inc de - dec b - jr nz, .loop - -; copies wAIScore to wTempAIScore - ld a, [wAIScore] - ld [de], a - jr AIProcessAttacks - -; copies wTempPlayAreaAIScore to wPlayAreaAIScore -; and loads wAIScore with value in wTempAIScore. -; identical to RetrievePlayAreaAIScoreFromBackup1. -RetrievePlayAreaAIScoreFromBackup2: ; 169e3 (5:69e3) - push af - ld de, wPlayAreaAIScore - ld hl, wTempPlayAreaAIScore - ld b, MAX_PLAY_AREA_POKEMON -.loop - ld a, [hli] - ld [de], a - inc de - dec b - jr nz, .loop - - ld a, [hl] - ld [wAIScore], a - pop af - ret - -; have AI choose and execute an attack. -; return carry if an attack was chosen and attempted. -AIProcessAndTryToUseAttack: ; 169f8 (5:69f8) - xor a - ld [wAIExecuteProcessedAttack], a - ; fallthrough - -; checks which of the Active card's attacks for AI to use. -; If any of the attacks has enough AI score to be used, -; AI will use it if wAIExecuteProcessedAttack is 0. -; in either case, return carry if an attack is chosen to be used. -AIProcessAttacks: ; 169fc (5:69fc) -; if AI used Pluspower, load its attack index - ld a, [wPreviousAIFlags] - and AI_FLAG_USED_PLUSPOWER - jr z, .no_pluspower - ld a, [wAIPluspowerAttack] - ld [wSelectedAttack], a - jr .attack_chosen - -.no_pluspower -; if Player is running Mewtwo1 mill deck, -; skip attack if Barrier counter is 0. - ld a, [wAIBarrierFlagCounter] - cp AI_MEWTWO_MILL + 0 - jp z, .dont_attack - -; determine AI score of both attacks. - xor a ; FIRST_ATTACK_OR_PKMN_POWER - call GetAIScoreOfAttack - ld a, [wAIScore] - ld [wFirstAttackAIScore], a - ld a, SECOND_ATTACK - call GetAIScoreOfAttack - -; compare both attack scores - ld c, SECOND_ATTACK - ld a, [wFirstAttackAIScore] - ld b, a - ld a, [wAIScore] - cp b - jr nc, .check_score - ; first attack has higher score - dec c - ld a, b - -; c holds the attack index chosen by AI, -; and a holds its AI score. -; first check if chosen attack has at least minimum score. -; then check if first attack is better than second attack -; in case the second one was chosen. -.check_score - cp $50 ; minimum score to use attack - jr c, .dont_attack - ; enough score, proceed - - ld a, c - ld [wSelectedAttack], a - or a - jr z, .attack_chosen - call CheckWhetherToSwitchToFirstAttack - -.attack_chosen -; check whether to execute the attack chosen - ld a, [wAIExecuteProcessedAttack] - or a - jr z, .execute - -; set carry and reset Play Area AI score -; to the previous values. - scf - jp RetrievePlayAreaAIScoreFromBackup2 - -.execute - ld a, AI_TRAINER_CARD_PHASE_14 - call AIProcessHandTrainerCards - -; load this attack's damage output against -; the current Defending Pokemon. - xor a ; PLAY_AREA_ARENA - ldh [hTempPlayAreaLocation_ff9d], a - ld a, [wSelectedAttack] - call EstimateDamage_VersusDefendingCard - ld a, [wDamage] - - or a - jr z, .check_damage_bench - ; if damage is not 0, fallthrough - -.can_damage - xor a - ld [wcdb4], a - jr .use_attack - -.check_damage_bench -; check if it can otherwise damage player's bench - ld a, ATTACK_FLAG1_ADDRESS | DAMAGE_TO_OPPONENT_BENCH_F - call CheckLoadedAttackFlag - jr c, .can_damage - -; cannot damage either Defending Pokemon or Bench - ld hl, wcdb4 - inc [hl] - -; return carry if attack is chosen -; and AI tries to use it. -.use_attack - ld a, TRUE - ld [wAITriedAttack], a - call AITryUseAttack - scf - ret - -.dont_attack - ld a, [wAIExecuteProcessedAttack] - or a - jr z, .failed_to_use - -; reset Play Area AI score -; to the previous values. - jp RetrievePlayAreaAIScoreFromBackup2 - -; return no carry if no viable attack. -.failed_to_use - ld hl, wcdb4 - inc [hl] - or a - ret - -; determines the AI score of attack index in a -; of card in Play Area location hTempPlayAreaLocation_ff9d. -GetAIScoreOfAttack: ; 16a86 (5:6a86) -; initialize AI score. - ld [wSelectedAttack], a - ld a, $50 - ld [wAIScore], a - - xor a - ldh [hTempPlayAreaLocation_ff9d], a - call CheckIfSelectedAttackIsUnusable - jr nc, .usable - -; return zero AI score. -.unusable - xor a - ld [wAIScore], a - jp .done - -; load arena card IDs -.usable - xor a - ld [wAICannotDamage], a - ld a, DUELVARS_ARENA_CARD - call GetTurnDuelistVariable - call GetCardIDFromDeckIndex - ld a, e - ld [wTempTurnDuelistCardID], a - call SwapTurn - ld a, DUELVARS_ARENA_CARD - call GetTurnDuelistVariable - call GetCardIDFromDeckIndex - ld a, e - ld [wTempNonTurnDuelistCardID], a - -; handle the case where the player has No Damage substatus. -; in the case the player does, check if this attack -; has a residual effect, or if it can damage the opposing bench. -; If none of those are true, render the attack unusable. -; also if it's a PKMN power, consider it unusable as well. - bank1call HandleNoDamageOrEffectSubstatus - call SwapTurn - jr nc, .check_if_can_ko - - ; player is under No Damage substatus - ld a, $01 - ld [wAICannotDamage], a - ld a, [wSelectedAttack] - call EstimateDamage_VersusDefendingCard - ld a, [wLoadedAttackCategory] - cp POKEMON_POWER - jr z, .unusable - and RESIDUAL - jr nz, .check_if_can_ko - ld a, ATTACK_FLAG1_ADDRESS | DAMAGE_TO_OPPONENT_BENCH_F - call CheckLoadedAttackFlag - jr nc, .unusable - -; calculate damage to player to check if attack can KO. -; encourage attack if it's able to KO. -.check_if_can_ko - ld a, [wSelectedAttack] - call EstimateDamage_VersusDefendingCard - ld a, DUELVARS_ARENA_CARD_HP - call GetNonTurnDuelistVariable - ld hl, wDamage - sub [hl] - jr c, .can_ko - jr z, .can_ko - jr .check_damage -.can_ko - ld a, 20 - call AddToAIScore - -; raise AI score by the number of damage counters that this attack deals. -; if no damage is dealt, subtract AI score. in case wDamage is zero -; but wMaxDamage is not, then encourage attack afterwards. -; otherwise, if wMaxDamage is also zero, check for damage against -; player's bench, and encourage attack in case there is. -.check_damage - xor a - ld [wAIAttackIsNonDamaging], a - ld a, [wDamage] - ld [wTempAI], a - or a - jr z, .no_damage - call CalculateByteTensDigit - call AddToAIScore - jr .check_recoil -.no_damage - ld a, $01 - ld [wAIAttackIsNonDamaging], a - call SubFromAIScore - ld a, [wAIMaxDamage] - or a - jr z, .no_max_damage - ld a, 2 - call AddToAIScore - xor a - ld [wAIAttackIsNonDamaging], a -.no_max_damage - ld a, ATTACK_FLAG1_ADDRESS | DAMAGE_TO_OPPONENT_BENCH_F - call CheckLoadedAttackFlag - jr nc, .check_recoil - ld a, 2 - call AddToAIScore - -; handle recoil attacks (low and high recoil). -.check_recoil - ld a, ATTACK_FLAG1_ADDRESS | LOW_RECOIL_F - call CheckLoadedAttackFlag - jr c, .is_recoil - ld a, ATTACK_FLAG1_ADDRESS | HIGH_RECOIL_F - call CheckLoadedAttackFlag - jp nc, .check_defending_can_ko -.is_recoil - ; sub from AI score number of damage counters - ; that attack deals to itself. - ld a, [wLoadedAttackEffectParam] - or a - jp z, .check_defending_can_ko - ld [wDamage], a - call ApplyDamageModifiers_DamageToSelf - ld a, e - call CalculateByteTensDigit - call SubFromAIScore - - push de - ld a, ATTACK_FLAG1_ADDRESS | HIGH_RECOIL_F - call CheckLoadedAttackFlag - pop de - jr c, .high_recoil - - ; if LOW_RECOIL KOs self, decrease AI score - ld a, DUELVARS_ARENA_CARD_HP - call GetTurnDuelistVariable - cp e - jr c, .kos_self - jp nz, .check_defending_can_ko -.kos_self - ld a, 10 - call SubFromAIScore - -.high_recoil - ; dismiss this attack if no benched Pokémon - ld a, DUELVARS_NUMBER_OF_POKEMON_IN_PLAY_AREA - call GetTurnDuelistVariable - cp 2 - jr c, .dismiss_high_recoil_atk - ; has benched Pokémon - -; here the AI handles high recoil attacks differently -; depending on what deck it's playing. - ld a, [wOpponentDeckID] - cp ROCK_CRUSHER_DECK_ID - jr z, .rock_crusher_deck - cp ZAPPING_SELFDESTRUCT_DECK_ID - jr z, .zapping_selfdestruct_deck - cp BOOM_BOOM_SELFDESTRUCT_DECK_ID - jr z, .encourage_high_recoil_atk - ; Boom Boom Selfdestruct deck always encourages - cp POWER_GENERATOR_DECK_ID - jr nz, .high_recoil_generic_checks - ; Power Generator deck always dismisses - -.dismiss_high_recoil_atk - xor a - ld [wAIScore], a - jp .done - -.encourage_high_recoil_atk - ld a, 20 - call AddToAIScore - jp .done - -; Zapping Selfdestruct deck only uses this attack -; if number of cards in deck >= 30 and -; HP of active card is < half max HP. -.zapping_selfdestruct_deck - ld a, DUELVARS_NUMBER_OF_CARDS_NOT_IN_DECK - call GetTurnDuelistVariable - cp 31 - jr nc, .high_recoil_generic_checks - ld e, PLAY_AREA_ARENA - call GetCardDamageAndMaxHP - sla a - cp c - jr c, .high_recoil_generic_checks - ld b, 0 - ld a, DUELVARS_ARENA_CARD - call GetTurnDuelistVariable - call GetCardIDFromDeckIndex - ld a, e - cp MAGNEMITE1 - jr z, .magnemite1 - ld b, 10 ; bench damage -.magnemite1 - ld a, 10 - add b - ld b, a ; 20 bench damage if not Magnemite1 - -; if this attack causes player to win the duel by -; knocking out own Pokémon, dismiss attack. - ld a, 1 ; count active Pokémon as KO'd - call .check_if_kos_bench - jr c, .dismiss_high_recoil_atk - jr .encourage_high_recoil_atk - -; Rock Crusher Deck only uses this attack if -; prize count is below 4 and attack wins (or potentially draws) the duel, -; (i.e. at least gets KOs equal to prize cards left). -.rock_crusher_deck - call CountPrizes - cp 4 - jr nc, .dismiss_high_recoil_atk - ; prize count < 4 - ld b, 20 ; damage dealt to bench - call SwapTurn - xor a - call .check_if_kos_bench - call SwapTurn - jr c, .encourage_high_recoil_atk - -; generic checks for all other deck IDs. -; encourage attack if it wins (or potentially draws) the duel, -; (i.e. at least gets KOs equal to prize cards left). -; dismiss it if it causes the player to win. -.high_recoil_generic_checks - ld a, DUELVARS_ARENA_CARD - call GetTurnDuelistVariable - call GetCardIDFromDeckIndex - ld a, e - cp CHANSEY - jr z, .chansey - cp MAGNEMITE1 - jr z, .magnemite1_or_weezing - cp WEEZING - jr z, .magnemite1_or_weezing - ld b, 20 ; bench damage - jr .check_bench_kos -.magnemite1_or_weezing - ld b, 10 ; bench damage - jr .check_bench_kos -.chansey - ld b, 0 ; no bench damage - -.check_bench_kos - push bc - call SwapTurn - xor a - call .check_if_kos_bench - call SwapTurn - pop bc - jr c, .wins_the_duel - push de - ld a, 1 - call .check_if_kos_bench - pop bc - jr nc, .count_own_ko_bench - -; attack causes player to draw all prize cards - xor a - ld [wAIScore], a - jp .done - -; attack causes CPU to draw all prize cards -.wins_the_duel - ld a, 20 - call AddToAIScore - jp .done - -; subtract from AI score number of own benched Pokémon KO'd -.count_own_ko_bench - push bc - ld a, d - or a - jr z, .count_player_ko_bench - dec a - call SubFromAIScore - -; add to AI score number of player benched Pokémon KO'd -.count_player_ko_bench - pop bc - ld a, b - call AddToAIScore - jr .check_defending_can_ko - -; local function that gets called to determine damage to -; benched Pokémon caused by a HIGH_RECOIL attack. -; return carry if using attack causes number of benched Pokémon KOs -; equal to or larger than remaining prize cards. -; this function is independent on duelist turn, so whatever -; turn it is when this is called, it's that duelist's -; bench/prize cards that get checked. -; input: -; a = initial number of KO's beside benched Pokémon, -; so that if the active Pokémon is KO'd by the attack, -; this counts towards the prize cards collected -; b = damage dealt to bench Pokémon -.check_if_kos_bench - ld d, a - ld a, DUELVARS_BENCH - call GetTurnDuelistVariable - ld e, PLAY_AREA_ARENA -.loop - inc e - ld a, [hli] - cp $ff - jr z, .exit_loop - ld a, e - add DUELVARS_ARENA_CARD_HP - push hl - call GetTurnDuelistVariable - pop hl - cp b - jr z, .increase_count - jr nc, .loop -.increase_count - ; increase d if damage dealt KOs - inc d - jr .loop -.exit_loop - push de - call SwapTurn - call CountPrizes - call SwapTurn - pop de - cp d - jp c, .set_carry - jp z, .set_carry - or a - ret -.set_carry - scf - ret - -; if defending card can KO, encourage attack -; unless attack is non-damaging. -.check_defending_can_ko - ld a, [wSelectedAttack] - push af - call CheckIfDefendingPokemonCanKnockOut - pop bc - ld a, b - ld [wSelectedAttack], a - jr nc, .check_discard - ld a, 5 - call AddToAIScore - ld a, [wAIAttackIsNonDamaging] - or a - jr z, .check_discard - ld a, 5 - call SubFromAIScore - -; subtract from AI score if this attack requires -; discarding any energy cards. -.check_discard - ld a, [wSelectedAttack] - ld e, a - ld a, DUELVARS_ARENA_CARD - call GetTurnDuelistVariable - ld d, a - call CopyAttackDataAndDamage_FromDeckIndex - ld a, ATTACK_FLAG2_ADDRESS | DISCARD_ENERGY_F - call CheckLoadedAttackFlag - jr nc, .asm_16ca6 - ld a, 1 - call SubFromAIScore - ld a, [wLoadedAttackEffectParam] - call SubFromAIScore - -.asm_16ca6 - ld a, ATTACK_FLAG2_ADDRESS | FLAG_2_BIT_6_F - call CheckLoadedAttackFlag - jr nc, .check_nullify_flag - ld a, [wLoadedAttackEffectParam] - call AddToAIScore - -; encourage attack if it has a nullify or weaken attack effect. -.check_nullify_flag - ld a, ATTACK_FLAG2_ADDRESS | NULLIFY_OR_WEAKEN_ATTACK_F - call CheckLoadedAttackFlag - jr nc, .check_draw_flag - ld a, 1 - call AddToAIScore - -; encourage attack if it has an effect to draw a card. -.check_draw_flag - ld a, ATTACK_FLAG1_ADDRESS | DRAW_CARD_F - call CheckLoadedAttackFlag - jr nc, .check_heal_flag - ld a, 1 - call AddToAIScore - -.check_heal_flag - ld a, ATTACK_FLAG2_ADDRESS | HEAL_USER_F - call CheckLoadedAttackFlag - jr nc, .check_status_effect - ld a, [wLoadedAttackEffectParam] - cp 1 - jr z, .tally_heal_score - ld a, [wTempAI] - call CalculateByteTensDigit - ld b, a - ld a, [wLoadedAttackEffectParam] - cp 3 - jr z, .asm_16cec - srl b - jr nc, .asm_16cec - inc b -.asm_16cec - ld a, DUELVARS_ARENA_CARD_HP - call GetTurnDuelistVariable - call CalculateByteTensDigit - cp b - jr c, .tally_heal_score - ld a, b -.tally_heal_score - push af - ld e, PLAY_AREA_ARENA - call GetCardDamageAndMaxHP - call CalculateByteTensDigit - pop bc - cp b ; wLoadedAttackEffectParam - jr c, .add_heal_score - ld a, b -.add_heal_score - call AddToAIScore - -.check_status_effect - ld a, DUELVARS_ARENA_CARD - call GetNonTurnDuelistVariable - call SwapTurn - call GetCardIDFromDeckIndex - call SwapTurn - ld a, e - ; skip if player has Snorlax - cp SNORLAX - jp z, .handle_special_atks - - ld a, DUELVARS_ARENA_CARD_STATUS - call GetNonTurnDuelistVariable - ld [wTempAI], a - -; encourage a poison inflicting attack if opposing Pokémon -; isn't (doubly) poisoned already. -; if opposing Pokémon is only poisoned and not double poisoned, -; and this attack has FLAG_2_BIT_6 set, discourage it -; (possibly to make Nidoking's Toxic attack less likely to be chosen -; if the other Pokémon is poisoned.) - ld a, ATTACK_FLAG1_ADDRESS | INFLICT_POISON_F - call CheckLoadedAttackFlag - jr nc, .check_sleep - ld a, [wTempAI] - and DOUBLE_POISONED - jr z, .add_poison_score - and $40 ; only double poisoned? - jr z, .check_sleep - ld a, ATTACK_FLAG2_ADDRESS | FLAG_2_BIT_6_F - call CheckLoadedAttackFlag - jr nc, .check_sleep - ld a, 2 - call SubFromAIScore - jr .check_sleep -.add_poison_score - ld a, 2 - call AddToAIScore - -; encourage sleep-inducing attack if other Pokémon isn't asleep. -.check_sleep - ld a, ATTACK_FLAG1_ADDRESS | INFLICT_SLEEP_F - call CheckLoadedAttackFlag - jr nc, .check_paralysis - ld a, [wTempAI] - and CNF_SLP_PRZ - cp ASLEEP - jr z, .check_paralysis - ld a, 1 - call AddToAIScore - -; encourage paralysis-inducing attack if other Pokémon isn't asleep. -; otherwise, if other Pokémon is asleep, discourage attack. -.check_paralysis - ld a, ATTACK_FLAG1_ADDRESS | INFLICT_PARALYSIS_F - call CheckLoadedAttackFlag - jr nc, .check_confusion - ld a, [wTempAI] - and CNF_SLP_PRZ - cp ASLEEP - jr z, .sub_prz_score - ld a, 1 - call AddToAIScore - jr .check_confusion -.sub_prz_score - ld a, 1 - call SubFromAIScore - -; encourage confuse-inducing attack if other Pokémon isn't asleep -; or confused already. -; otherwise, if other Pokémon is asleep or confused, -; discourage attack instead. -.check_confusion - ld a, ATTACK_FLAG1_ADDRESS | INFLICT_CONFUSION_F - call CheckLoadedAttackFlag - jr nc, .check_if_confused - ld a, [wTempAI] - and CNF_SLP_PRZ - cp ASLEEP - jr z, .sub_cnf_score - ld a, [wTempAI] - and CNF_SLP_PRZ - cp CONFUSED - jr z, .check_if_confused - ld a, 1 - call AddToAIScore - jr .check_if_confused -.sub_cnf_score - ld a, 1 - call SubFromAIScore - -; if this Pokémon is confused, subtract from score. -.check_if_confused - ld a, DUELVARS_ARENA_CARD_STATUS - call GetTurnDuelistVariable - and CNF_SLP_PRZ - cp CONFUSED - jr nz, .handle_special_atks - ld a, 1 - call SubFromAIScore - -; SPECIAL_AI_HANDLING marks attacks that the AI handles individually. -; each attack has its own checks and modifies AI score accordingly. -.handle_special_atks - ld a, ATTACK_FLAG3_ADDRESS | SPECIAL_AI_HANDLING_F - call CheckLoadedAttackFlag - jr nc, .done - call HandleSpecialAIAttacks - cp $80 - jr c, .negative_score - sub $80 - call AddToAIScore - jr .done -.negative_score - ld b, a - ld a, $80 - sub b - call SubFromAIScore - -.done - ret diff --git a/src/engine/ai/boss_deck_set_up.asm b/src/engine/ai/boss_deck_set_up.asm deleted file mode 100644 index ebcd2ea..0000000 --- a/src/engine/ai/boss_deck_set_up.asm +++ /dev/null @@ -1,167 +0,0 @@ -; sets up the initial hand of boss deck. -; always draws at least 2 Basic Pokemon cards and 2 Energy cards. -; also sets up so that the next cards to be drawn have -; some minimum number of Basic Pokemon and Energy cards. -SetUpBossStartingHandAndDeck: ; 172af (5:72af) -; shuffle all hand cards in deck - ld a, DUELVARS_HAND - call GetTurnDuelistVariable - ld b, STARTING_HAND_SIZE -.loop_hand - ld a, [hl] - call RemoveCardFromHand - call ReturnCardToDeck - dec b - jr nz, .loop_hand - jr .count_energy_basic - -.shuffle_deck - call ShuffleDeck - -; count number of Energy and basic Pokemon cards -; in the first STARTING_HAND_SIZE in deck. -.count_energy_basic - xor a - ld [wce06], a - ld [wce08], a - - ld a, DUELVARS_DECK_CARDS - call GetTurnDuelistVariable - ld b, STARTING_HAND_SIZE -.loop_deck_1 - ld a, [hli] - push bc - call LoadCardDataToBuffer1_FromDeckIndex - pop bc - ld a, [wLoadedCard1Type] - cp TYPE_ENERGY - jr c, .pokemon_card_1 - cp TYPE_TRAINER - jr z, .next_card_deck_1 - -; energy card - ld a, [wce08] - inc a - ld [wce08], a - jr .next_card_deck_1 - -.pokemon_card_1 - ld a, [wLoadedCard1Stage] - or a - jr nz, .next_card_deck_1 ; not basic - ld a, [wce06] - inc a - ld [wce06], a - -.next_card_deck_1 - dec b - jr nz, .loop_deck_1 - -; tally the number of Energy and basic Pokemon cards -; and if any of them is smaller than 2, re-shuffle deck. - ld a, [wce06] - cp 2 - jr c, .shuffle_deck - ld a, [wce08] - cp 2 - jr c, .shuffle_deck - -; now check the following 6 cards (prize cards). -; re-shuffle deck if any of these cards is listed in wAICardListAvoidPrize. - ld b, 6 -.check_card_ids - ld a, [hli] - push bc - call .CheckIfIDIsInList - pop bc - jr c, .shuffle_deck - dec b - jr nz, .check_card_ids - -; finally, check 6 cards after that. -; if Energy or Basic Pokemon counter is below 4 -; (counting with the ones found in the initial hand) -; then re-shuffle deck. - ld b, 6 -.loop_deck_2 - ld a, [hli] - push bc - call LoadCardDataToBuffer1_FromDeckIndex - pop bc - ld a, [wLoadedCard1Type] - cp TYPE_ENERGY - jr c, .pokemon_card_2 - cp TYPE_TRAINER - jr z, .next_card_deck_2 - -; energy card - ld a, [wce08] - inc a - ld [wce08], a - jr .next_card_deck_2 - -.pokemon_card_2 - ld a, [wLoadedCard1Stage] - or a - jr nz, .next_card_deck_2 - ld a, [wce06] - inc a - ld [wce06], a - -.next_card_deck_2 - dec b - jr nz, .loop_deck_2 - - ld a, [wce06] - cp 4 - jp c, .shuffle_deck - ld a, [wce08] - cp 4 - jp c, .shuffle_deck - -; draw new set of hand cards - ld a, DUELVARS_DECK_CARDS - call GetTurnDuelistVariable - ld b, STARTING_HAND_SIZE -.draw_loop - ld a, [hli] - call SearchCardInDeckAndAddToHand - call AddCardToHand - dec b - jr nz, .draw_loop - ret - -; expectation: return carry if card ID corresponding -; to the input deck index is listed in wAICardListAvoidPrize; -; reality: always returns no carry because when checking terminating -; byte in wAICardListAvoidPrize ($00), it wrongfully uses 'cp a' instead of 'or a', -; so it always ends up returning in the first item in list. -; input: -; - a = deck index of card to check -.CheckIfIDIsInList ; 17366 (5:7366) - ld b, a - ld a, [wAICardListAvoidPrize + 1] - or a - ret z ; null - push hl - ld h, a - ld a, [wAICardListAvoidPrize] - ld l, a - - ld a, b - call GetCardIDFromDeckIndex -.loop_id_list - ld a, [hli] - cp a ; bug, should be 'or a' - jr z, .false - cp e - jr nz, .loop_id_list - -; true - pop hl - scf - ret -.false - pop hl - or a - ret diff --git a/src/engine/ai/common.asm b/src/engine/ai/common.asm deleted file mode 100644 index d4f1da4..0000000 --- a/src/engine/ai/common.asm +++ /dev/null @@ -1,970 +0,0 @@ -; runs through Player's whole deck and -; sets carry if there's any Pokemon other -; than Mewtwo1. -CheckIfPlayerHasPokemonOtherThanMewtwo1: ; 227a9 (8:67a9) - call SwapTurn - ld e, 0 -.loop_deck - ld a, e - push de - call LoadCardDataToBuffer2_FromDeckIndex - pop de - ld a, [wLoadedCard2Type] - cp TYPE_ENERGY - jp nc, .next ; can be a jr - ld a, [wLoadedCard2ID] - cp MEWTWO1 - jr nz, .not_mewtwo1 -.next - inc e - ld a, DECK_SIZE - cp e - jr nz, .loop_deck - -; no carry - call SwapTurn - or a - ret - -.not_mewtwo1 - call SwapTurn - scf - ret - -; returns no carry if, given the Player is using a Mewtwo1 mill deck, -; the AI already has a Bench fully set up, in which case it -; will process some Trainer cards in hand (namely Energy Removals). -; this is used to check whether to skip some normal AI routines -; this turn and jump right to the attacking phase. -HandleAIAntiMewtwoDeckStrategy: ; 227d3 (8:67d3) -; return carry if Player is not playing Mewtwo1 mill deck - ld a, [wAIBarrierFlagCounter] - bit AI_MEWTWO_MILL_F, a - jr z, .set_carry - -; else, check if there's been less than 2 turns -; without the Player using Barrier. - cp AI_MEWTWO_MILL + 2 - jr c, .count_bench - -; if there has been, reset wAIBarrierFlagCounter -; and return carry. - xor a - ld [wAIBarrierFlagCounter], a - jr .set_carry - -; else, check number of Pokemon that are set up in Bench -; if less than 4, return carry. -.count_bench - farcall CountNumberOfSetUpBenchPokemon - cp 4 - jr c, .set_carry - -; if there's at least 4 Pokemon in the Bench set up, -; process Trainer hand cards of AI_TRAINER_CARD_PHASE_05 - ld a, AI_TRAINER_CARD_PHASE_05 - farcall AIProcessHandTrainerCards - or a - ret - -.set_carry - scf - ret - -; lists in wDuelTempList all the basic energy cards -; in card location of a. -; outputs in a number of cards found. -; returns carry if none were found. -; input: -; a = CARD_LOCATION_* to look -; output: -; a = number of cards found -FindBasicEnergyCardsInLocation: ; 227f6 (8:67f6) - ld [wTempAI], a - lb de, 0, 0 - ld hl, wDuelTempList - -; d = number of basic energy cards found -; e = current card in deck -; loop entire deck -.loop - ld a, DUELVARS_CARD_LOCATIONS - add e - push hl - call GetTurnDuelistVariable - ld hl, wTempAI - cp [hl] - pop hl - jr nz, .next_card - -; is in the card location we're looking for - ld a, e - push de - push hl - call GetCardIDFromDeckIndex - pop hl - ld a, e - pop de - cp DOUBLE_COLORLESS_ENERGY - ; only basic energy cards - ; will set carry here - jr nc, .next_card - -; is a basic energy card -; add this card to the TempList - ld a, e - ld [hli], a - inc d -.next_card - inc e - ld a, DECK_SIZE - cp e - jr nz, .loop - -; check if any were found - ld a, d - or a - jr z, .set_carry - -; some were found, add the termination byte on TempList - ld a, $ff - ld [hl], a - ld a, d - ret - -.set_carry - scf - ret - -; returns in a the card index of energy card -; attached to Pokémon in Play Area location a, -; that is to be discarded by the AI for an effect. -; outputs $ff is none was found. -; input: -; a = PLAY_AREA_* constant of card -; output: -; a = deck index of attached energy card chosen -AIPickEnergyCardToDiscard: ; 2282e (8:682e) -; load Pokémon's attached energy cards. - ldh [hTempPlayAreaLocation_ff9d], a - call CreateArenaOrBenchEnergyCardList - ldh a, [hTempPlayAreaLocation_ff9d] - ld e, a - call GetPlayAreaCardAttachedEnergies - ld a, [wTotalAttachedEnergies] - or a - jr z, .no_energy - -; load card's ID and type. - ldh a, [hTempPlayAreaLocation_ff9d] - ld b, a - ld a, DUELVARS_ARENA_CARD - add b - call GetTurnDuelistVariable - call GetCardIDFromDeckIndex - ld a, e - ld [wTempCardID], a - call LoadCardDataToBuffer1_FromCardID - ld a, [wLoadedCard1Type] - or TYPE_ENERGY - ld [wTempCardType], a - -; find a card that is not useful. -; if none is found, just return the first energy card attached. - ld hl, wDuelTempList -.loop - ld a, [hl] - cp $ff - jr z, .not_found - farcall CheckIfEnergyIsUseful - jr nc, .found - inc hl - jr .loop - -.found - ld a, [hl] - ret -.not_found - ld hl, wDuelTempList - ld a, [hl] - ret -.no_energy - ld a, $ff - ret - -; returns in a the deck index of an energy card attached to card -; in player's Play Area location a to remove. -; prioritizes double colorless energy, then any useful energy, -; then defaults to the first energy card attached if neither -; of those are found. -; returns $ff in a if there are no energy cards attached. -; input: -; a = Play Area location to check -; output: -; a = deck index of attached energy card -PickAttachedEnergyCardToRemove: ; 22875 (8:6875) -; construct energy list and check if there are any energy cards attached - ldh [hTempPlayAreaLocation_ff9d], a - call CreateArenaOrBenchEnergyCardList - ldh a, [hTempPlayAreaLocation_ff9d] - ld e, a - call GetPlayAreaCardAttachedEnergies - ld a, [wTotalAttachedEnergies] - or a - jr z, .no_energy - -; load card data and store its type - ldh a, [hTempPlayAreaLocation_ff9d] - ld b, a - ld a, DUELVARS_ARENA_CARD - add b - call GetTurnDuelistVariable - call GetCardIDFromDeckIndex - ld a, e - ld [wTempCardID], a - call LoadCardDataToBuffer1_FromCardID - ld a, [wLoadedCard1Type] - or TYPE_ENERGY - ld [wTempCardType], a - -; first look for any double colorless energy - ld hl, wDuelTempList -.loop_1 - ld a, [hl] - cp $ff - jr z, .check_useful - push hl - call GetCardIDFromDeckIndex - ld a, e - cp DOUBLE_COLORLESS_ENERGY - pop hl - jr z, .found - inc hl - jr .loop_1 - -; then look for any energy cards that are useful -.check_useful - ld hl, wDuelTempList -.loop_2 - ld a, [hl] - cp $ff - jr z, .default - farcall CheckIfEnergyIsUseful - jr c, .found - inc hl - jr .loop_2 - -; return the energy card that was found -.found - ld a, [hl] - ret - -; if none were found with the above criteria, -; just return the first option -.default - ld hl, wDuelTempList - ld a, [hl] - ret - -; return $ff if no energy cards attached -.no_energy - ld a, $ff - ret - -; stores in wTempAI and wCurCardCanAttack the deck indices -; of energy cards attached to card in Play Area location a. -; prioritizes double colorless energy, then any useful energy, -; then defaults to the first two energy cards attached if neither -; of those are found. -; returns $ff in a if there are no energy cards attached. -; input: -; a = Play Area location to check -; output: -; [wTempAI] = deck index of attached energy card -; [wCurCardCanAttack] = deck index of attached energy card -PickTwoAttachedEnergyCards: ; 228d1 (8:68d1) - ldh [hTempPlayAreaLocation_ff9d], a - call CreateArenaOrBenchEnergyCardList - ldh a, [hTempPlayAreaLocation_ff9d] - ld e, a - farcall CountNumberOfEnergyCardsAttached - cp 2 - jp c, .not_enough - -; load card data and store its type - ldh a, [hTempPlayAreaLocation_ff9d] - ld b, a - ld a, DUELVARS_ARENA_CARD - add b - call GetTurnDuelistVariable - call GetCardIDFromDeckIndex - ld a, e - ld [wTempCardID], a - call LoadCardDataToBuffer1_FromCardID - ld a, [wLoadedCard1Type] - or TYPE_ENERGY - ld [wTempCardType], a - ld a, $ff - ld [wTempAI], a - ld [wCurCardCanAttack], a - -; first look for any double colorless energy - ld hl, wDuelTempList -.loop_1 - ld a, [hl] - cp $ff - jr z, .check_useful - push hl - call GetCardIDFromDeckIndex - ld a, e - cp DOUBLE_COLORLESS_ENERGY - pop hl - jr z, .found_double_colorless - inc hl - jr .loop_1 -.found_double_colorless - ld a, [wTempAI] - cp $ff - jr nz, .already_chosen_1 - ld a, [hli] - ld [wTempAI], a - jr .loop_1 -.already_chosen_1 - ld a, [hl] - ld [wCurCardCanAttack], a - jr .done - -; then look for any energy cards that are useful -.check_useful - ld hl, wDuelTempList -.loop_2 - ld a, [hl] - cp $ff - jr z, .default - farcall CheckIfEnergyIsUseful - jr c, .found_useful - inc hl - jr .loop_2 -.found_useful - ld a, [wTempAI] - cp $ff - jr nz, .already_chosen_2 - ld a, [hli] - ld [wTempAI], a - jr .loop_2 -.already_chosen_2 - ld a, [hl] - ld [wCurCardCanAttack], a - jr .done - -; if none were found with the above criteria, -; just return the first 2 options -.default - ld hl, wDuelTempList - ld a, [wTempAI] - cp $ff - jr nz, .pick_one_card - -; pick 2 cards - ld a, [hli] - ld [wTempAI], a - ld a, [hl] - ld [wCurCardCanAttack], a - jr .done -.pick_one_card - ld a, [wTempAI] - ld b, a -.loop_3 - ld a, [hli] - cp b - jr z, .loop_3 ; already picked - ld [wCurCardCanAttack], a - -.done - ld a, [wCurCardCanAttack] - ld b, a - ld a, [wTempAI] - ret - -; return $ff if no energy cards attached -.not_enough - ld a, $ff - ret - -; copies $ff terminated buffer from hl to de -CopyBuffer: ; 2297b (8:697b) - ld a, [hli] - ld [de], a - cp $ff - ret z - inc de - jr CopyBuffer - -; zeroes a bytes starting at hl -ClearMemory_Bank8: ; 22983 (8:6983) - push af - push bc - push hl - ld b, a - xor a -.loop - ld [hli], a - dec b - jr nz, .loop - pop hl - pop bc - pop af - ret - -; counts number of energy cards found in hand -; and outputs result in a -; sets carry if none are found -; output: -; a = number of energy cards found -CountOppEnergyCardsInHand: ; 22990 (8:6990) - farcall CreateEnergyCardListFromHand - ret c - ld b, -1 - ld hl, wDuelTempList -.loop - inc b - ld a, [hli] - cp $ff - jr nz, .loop - ld a, b - or a - ret - -; converts HP in a to number of equivalent damage counters -; input: -; a = HP -; output: -; a = number of damage counters -ConvertHPToCounters: ; 229a3 (8:69a3) - push bc - ld c, 0 -.loop - sub 10 - jr c, .carry - inc c - jr .loop -.carry - ld a, c - pop bc - ret - -; calculates floor(hl / 10) -CalculateWordTensDigit: ; 229b0 (8:69b0) - push bc - push de - lb bc, $ff, -10 - lb de, $ff, -1 -.asm_229b8 - inc de - add hl, bc - jr c, .asm_229b8 - ld h, d - ld l, e - pop de - pop bc - ret - -; returns in a division of b by a -CalculateBDividedByA_Bank8: ; 229c1 (8:69c1) - push bc - ld c, a - ld a, b - ld b, c - ld c, 0 -.loop - sub b - jr c, .done - inc c - jr .loop -.done - ld a, c - pop bc - ret - -; returns in a the deck index of the first -; instance of card with ID equal to the ID in e -; in card location a. -; returns carry if found. -; input: -; a = CARD_LOCATION_* -; e = card ID to look for -LookForCardIDInLocation: ; 229d0 (8:69d0) - ld b, a - ld c, e - lb de, $00, 0 ; d is never used -.loop - ld a, DUELVARS_CARD_LOCATIONS - add e - call GetTurnDuelistVariable - cp b - jr nz, .next - ld a, e - push de - call GetCardIDFromDeckIndex - ld a, e - pop de - cp c - jr z, .found -.next - inc e - ld a, DECK_SIZE - cp e - jr nz, .loop - -; not found - or a - ret -.found - ld a, e - scf - ret - -; return carry if card ID loaded in a is found in hand -; and outputs in a the deck index of that card -; input: -; a = card ID -; output: -; a = card deck index, if found -; carry set if found -LookForCardIDInHandList_Bank8: ; 229f3 (8:69f3) - ld [wTempCardIDToLook], a - call CreateHandCardList - ld hl, wDuelTempList - -.loop - ld a, [hli] - cp $ff - ret z - - ldh [hTempCardIndex_ff98], a - call LoadCardDataToBuffer1_FromDeckIndex - ld b, a - ld a, [wTempCardIDToLook] - cp b - jr nz, .loop - - ldh a, [hTempCardIndex_ff98] - scf - ret - -; searches in deck for card ID 1 in a, and -; if found, searches in Hand/Play Area for card ID 2 in b, and -; if found, searches for card ID 1 in Hand/Play Area, and -; if none found, return carry and output deck index -; of the card ID 1 in deck. -; input: -; a = card ID 1 -; b = card ID 2 -; output: -; a = index of card ID 1 in deck -LookForCardIDInDeck_GivenCardIDInHandAndPlayArea: ; 22a10 (8:6a10) -; store a in wCurCardCanAttack -; and b in wTempAI - ld c, a - ld a, b - ld [wTempAI], a - ld a, c - ld [wCurCardCanAttack], a - -; look for the card ID 1 in deck - ld e, a - ld a, CARD_LOCATION_DECK - call LookForCardIDInLocation - ret nc - -; was found, store its deck index in memory - ld [wTempAIPokemonCard], a - -; look for the card ID 2 -; in Hand and Play Area, return if not found. - ld a, [wTempAI] - call LookForCardIDInHandAndPlayArea - ret nc - -; look for the card ID 1 in the Hand and Play Area -; if any card is found, return no carry. - ld a, [wCurCardCanAttack] - call LookForCardIDInHandAndPlayArea - jr c, .no_carry -; none found - - ld a, [wTempAIPokemonCard] - scf - ret - -.no_carry - or a - ret - -; returns carry if card ID in a -; is found in Play Area or in hand -; input: -; a = card ID -LookForCardIDInHandAndPlayArea: ; 22a39 (8:6a39) - ld b, a - push bc - call LookForCardIDInHandList_Bank8 - pop bc - ret c - - ld a, b - ld b, PLAY_AREA_ARENA - call LookForCardIDInPlayArea_Bank8 - ret c - or a - ret - -; searches in deck for card ID 1 in a, and -; if found, searches in Hand Area for card ID 2 in b, and -; if found, searches for card ID 1 in Hand/Play Area, and -; if none found, return carry and output deck index -; of the card ID 1 in deck. -; input: -; a = card ID 1 -; b = card ID 2 -; output: -; a = index of card ID 1 in deck -LookForCardIDInDeck_GivenCardIDInHand: ; 22a49 (8:6a49) -; store a in wCurCardCanAttack -; and b in wTempAI - ld c, a - ld a, b - ld [wTempAI], a - ld a, c - ld [wCurCardCanAttack], a - -; look for the card ID 1 in deck - ld e, a - ld a, CARD_LOCATION_DECK - call LookForCardIDInLocation - ret nc - -; was found, store its deck index in memory - ld [wTempAIPokemonCard], a - -; look for the card ID 2 in hand, return if not found. - ld a, [wTempAI] - call LookForCardIDInHandList_Bank8 - ret nc - -; look for the card ID 1 in the Hand and Play Area -; if any card is found, return no carry. - ld a, [wCurCardCanAttack] - call LookForCardIDInHandAndPlayArea - jr c, .no_carry -; none found - - ld a, [wTempAIPokemonCard] - scf - ret - -.no_carry - or a - ret - -; returns carry if card ID in a -; is found in Play Area, starting with -; location in b -; input: -; a = card ID -; b = PLAY_AREA_* to start with -; output: -; a = PLAY_AREA_* of found card -; carry set if found -LookForCardIDInPlayArea_Bank8: ; 22a72 (8:6a72) - ld [wTempCardIDToLook], a -.loop - ld a, DUELVARS_ARENA_CARD - add b - call GetTurnDuelistVariable - cp $ff - ret z - - call LoadCardDataToBuffer1_FromDeckIndex - ld c, a - ld a, [wTempCardIDToLook] - cp c - jr z, .is_same - - inc b - ld a, MAX_PLAY_AREA_POKEMON - cp b - jr nz, .loop - ld b, $ff - or a - ret - -.is_same - ld a, b - scf - ret - -; runs through list avoiding card in e. -; removes first card in list not equal to e -; and that has a type allowed to be removed, in d. -; returns carry if successful in finding a card. -; input: -; d = type of card allowed to be removed -; ($00 = Trainer, $01 = Pokemon, $02 = Energy) -; e = card deck index to avoid removing -; output: -; a = card index of removed card -RemoveFromListDifferentCardOfGivenType: ; 22a95 (8:6a95) - push hl - push de - push bc - call CountCardsInDuelTempList - call ShuffleCards - -; loop list until a card with -; deck index different from e is found. -.loop_list - ld a, [hli] - cp $ff - jr z, .no_carry - cp e - jr z, .loop_list - -; get this card's type - ldh [hTempCardIndex_ff98], a - push de - call GetCardIDFromDeckIndex - call GetCardType - pop de - cp TYPE_ENERGY - jr c, .pkmn_card - cp TYPE_TRAINER - jr nz, .energy - -; only remove from list specific type. - -; trainer - ld a, d - or a - jr nz, .loop_list - jr .remove_card -.energy - ld a, d - cp $02 - jr nz, .loop_list - jr .remove_card -.pkmn_card - ld a, d - cp $01 - jr nz, .loop_list - ; fallthrough - -.remove_card - ld d, h - ld e, l - dec hl -.loop_remove - ld a, [de] - inc de - ld [hli], a - cp $ff - jr nz, .loop_remove - -; success - ldh a, [hTempCardIndex_ff98] - pop bc - pop de - pop hl - scf - ret -.no_carry - pop bc - pop de - pop hl - or a - ret - -; used in Pokemon Trader checks to look for a specific -; card in the deck to trade with a card in hand that -; has a card ID different from e. -; returns carry if successful. -; input: -; a = card ID 1 -; e = card ID 2 -; output: -; a = deck index of card ID 1 found in deck -; e = deck index of Pokemon card in hand different than card ID 2 -LookForCardIDToTradeWithDifferentHandCard: ; 22ae0 (8:6ae0) - ld hl, wCurCardCanAttack - ld [hl], e - ld [wTempAI], a - -; if card ID 1 is in hand, return no carry. - call LookForCardIDInHandList_Bank8 - jr c, .no_carry - -; if card ID 1 is not in deck, return no carry. - ld a, [wTempAI] - ld e, a - ld a, CARD_LOCATION_DECK - call LookForCardIDInLocation - jr nc, .no_carry - -; store its deck index - ld [wTempAI], a - -; look in hand for Pokemon card ID that -; is different from card ID 2. - ld a, [wCurCardCanAttack] - ld c, a - call CreateHandCardList - ld hl, wDuelTempList - -.loop_hand - ld a, [hli] - cp $ff - jr z, .no_carry - ld b, a - call LoadCardDataToBuffer1_FromDeckIndex - cp c - jr z, .loop_hand - ld a, [wLoadedCard1Type] - cp TYPE_ENERGY - jr nc, .loop_hand - -; found, output deck index of card ID 1 in deck -; and deck index of card found in hand, and set carry - ld e, b - ld a, [wTempAI] - scf - ret - -.no_carry - or a - ret - -; returns carry if at least one card in the hand -; has the card ID of input. Outputs its index. -; input: -; a = card ID to look for -; output: -; a = deck index of card in hand found -CheckIfHasCardIDInHand: ; 22b1f (8:6b1f) - ld [wTempCardIDToLook], a - call CreateHandCardList - ld hl, wDuelTempList - ld c, 0 - -.loop_hand - ld a, [hli] - cp $ff - ret z - ldh [hTempCardIndex_ff98], a - call LoadCardDataToBuffer1_FromDeckIndex - ld b, a - ld a, [wTempCardIDToLook] - cp b - jr nz, .loop_hand - ld a, c - or a - jr nz, .set_carry - inc c - jr nz, .loop_hand - -.set_carry - ldh a, [hTempCardIndex_ff98] - scf - ret - -; outputs in a total number of Pokemon cards in hand -; plus Pokemon in Turn Duelist's Play Area. -CountPokemonCardsInHandAndInPlayArea: ; 22b45 (8:6b45) - ld a, DUELVARS_NUMBER_OF_POKEMON_IN_PLAY_AREA - call GetTurnDuelistVariable - ld [wTempAI], a - call CreateHandCardList - ld hl, wDuelTempList -.loop_hand - ld a, [hli] - cp $ff - jr z, .done - call GetCardIDFromDeckIndex - call GetCardType - cp TYPE_ENERGY - jr nc, .loop_hand - ld a, [wTempAI] - inc a - ld [wTempAI], a - jr .loop_hand -.done - ld a, [wTempAI] - ret - -; returns carry if a duplicate Pokemon card is found in hand. -; outputs in a the deck index of one of them. -FindDuplicatePokemonCards: ; 22b6f (8:6b6f) - ld a, $ff - ld [wTempAI], a - call CreateHandCardList - ld hl, wDuelTempList - push hl - -.loop_hand_outer - pop hl - ld a, [hli] - cp $ff - jr z, .done - call GetCardIDFromDeckIndex - ld b, e - push hl - -.loop_hand_inner - ld a, [hli] - cp $ff - jr z, .loop_hand_outer - ld c, a - call GetCardIDFromDeckIndex - ld a, e - cp b - jr nz, .loop_hand_inner - -; found two cards with same ID, -; if they are Pokemon cards, store its deck index. - push bc - call GetCardType - pop bc - cp TYPE_ENERGY - jr nc, .loop_hand_outer - ld a, c - ld [wTempAI], a - ; for some reason loop still continues - ; even though if some other duplicate - ; cards are found, it overwrites the result. - jr .loop_hand_outer - -.done - ld a, [wTempAI] - cp $ff - jr z, .no_carry - -; found - scf - ret -.no_carry - or a - ret - -; return carry flag if attack is not high recoil. -AICheckIfAttackIsHighRecoil: ; 22bad (8:6bad) - farcall AIProcessButDontUseAttack - ret nc - ld a, [wSelectedAttack] - ld e, a - ld a, DUELVARS_ARENA_CARD - call GetTurnDuelistVariable - ld d, a - call CopyAttackDataAndDamage_FromDeckIndex - ld a, ATTACK_FLAG1_ADDRESS | HIGH_RECOIL_F - call CheckLoadedAttackFlag - ccf - ret diff --git a/src/engine/ai/core.asm b/src/engine/ai/core.asm deleted file mode 100644 index f182375..0000000 --- a/src/engine/ai/core.asm +++ /dev/null @@ -1,2770 +0,0 @@ -INCLUDE "engine/ai/decks/unreferenced.asm" - -; returns carry if damage dealt from any of -; a card's attacks KOs defending Pokémon -; outputs index of the attack that KOs -; input: -; [hTempPlayAreaLocation_ff9d] = location of attacking card to consider -; output: -; [wSelectedAttack] = attack index that KOs -CheckIfAnyAttackKnocksOutDefendingCard: ; 140ae (5:40ae) - xor a ; first attack - call CheckIfAttackKnocksOutDefendingCard - ret c - ld a, SECOND_ATTACK -; fallthrough - -CheckIfAttackKnocksOutDefendingCard: ; 140b5 (5:40b5) - call EstimateDamage_VersusDefendingCard - ld a, DUELVARS_ARENA_CARD_HP - call GetNonTurnDuelistVariable - ld hl, wDamage - sub [hl] - ret c - ret nz - scf - ret - -; returns carry if any of the defending Pokémon's attacks -; brings card at hTempPlayAreaLocation_ff9d down -; to exactly 0 HP. -; outputs that attack index in wSelectedAttack. -CheckIfAnyDefendingPokemonAttackDealsSameDamageAsHP: ; 140c5 (5:40c5) - xor a ; FIRST_ATTACK_OR_PKMN_POWER - call .check_damage - ret c - ld a, SECOND_ATTACK - -.check_damage - call EstimateDamage_FromDefendingPokemon - ldh a, [hTempPlayAreaLocation_ff9d] - add DUELVARS_ARENA_CARD_HP - call GetTurnDuelistVariable - ld hl, wDamage - sub [hl] - jr z, .true - ret -.true - scf - ret - -; checks AI scores for all benched Pokémon -; returns the location of the card with highest score -; in a and [hTempPlayAreaLocation_ff9d] -FindHighestBenchScore: ; 140df (5:40df) - ld a, DUELVARS_NUMBER_OF_POKEMON_IN_PLAY_AREA - call GetTurnDuelistVariable - ld b, a - ld c, 0 - ld e, c - ld d, c - ld hl, wPlayAreaAIScore + 1 - jp .next - -.loop - ld a, [hli] - cp e - jr c, .next - ld e, a - ld d, c -.next - inc c - dec b - jr nz, .loop - - ld a, d - ldh [hTempPlayAreaLocation_ff9d], a - or a - ret - -; adds a to wAIScore -; if there's overflow, it's capped at $ff -; output: -; a = a + wAIScore (capped at $ff) -AddToAIScore: ; 140fe (5:40fe) - push hl - ld hl, wAIScore - add [hl] - jr nc, .no_cap - ld a, $ff -.no_cap - ld [hl], a - pop hl - ret - -; subs a from wAIScore -; if there's underflow, it's capped at $00 -SubFromAIScore: ; 1410a (5:410a) - push hl - push de - ld e, a - ld hl, wAIScore - ld a, [hl] - or a - jr z, .done - sub e - ld [hl], a - jr nc, .done - ld [hl], $00 -.done - pop de - pop hl - ret - -; loads defending Pokémon's weakness/resistance -; and the number of prize cards in both sides -LoadDefendingPokemonColorWRAndPrizeCards: ; 1411d (5:411d) - call SwapTurn - call GetArenaCardColor - call TranslateColorToWR - ld [wAIPlayerColor], a - call GetArenaCardWeakness - ld [wAIPlayerWeakness], a - call GetArenaCardResistance - ld [wAIPlayerResistance], a - call CountPrizes - ld [wAIPlayerPrizeCount], a - call SwapTurn - call CountPrizes - ld [wAIOpponentPrizeCount], a - ret - -; called when AI has chosen its attack. -; executes all effects and damage. -; handles AI choosing parameters for certain attacks as well. -AITryUseAttack: ; 14145 (5:4145) - ld a, [wSelectedAttack] - ldh [hTemp_ffa0], a - ld e, a - ld a, DUELVARS_ARENA_CARD - call GetTurnDuelistVariable - ldh [hTempCardIndex_ff9f], a - ld d, a - call CopyAttackDataAndDamage_FromDeckIndex - ld a, OPPACTION_BEGIN_ATTACK - bank1call AIMakeDecision - ret c - - call AISelectSpecialAttackParameters - jr c, .use_attack - ld a, EFFECTCMDTYPE_AI_SELECTION - call TryExecuteEffectCommandFunction - -.use_attack - ld a, [wSelectedAttack] - ld e, a - ld a, DUELVARS_ARENA_CARD - call GetTurnDuelistVariable - ld d, a - call CopyAttackDataAndDamage_FromDeckIndex - ld a, OPPACTION_USE_ATTACK - bank1call AIMakeDecision - ret c - - ld a, EFFECTCMDTYPE_AI_SWITCH_DEFENDING_PKMN - call TryExecuteEffectCommandFunction - ld a, OPPACTION_ATTACK_ANIM_AND_DAMAGE - bank1call AIMakeDecision - ret - -; return carry if any of the following is satisfied: -; - deck index in a corresponds to a double colorless energy card; -; - card type in wTempCardType is colorless; -; - card ID in wTempCardID is a Pokémon card that has -; attacks that require energy other than its color and -; the deck index in a corresponds to that energy type; -; - card ID is Eevee and a corresponds to an energy type -; of water, fire or lightning; -; - type of card in register a is the same as wTempCardType. -; used for knowing if a given energy card can be discarded -; from a given Pokémon card -; input: -; a = energy card attached to Pokémon to check -; [wTempCardType] = TYPE_ENERGY_* of given Pokémon -; [wTempCardID] = card index of Pokémon card to check -CheckIfEnergyIsUseful: ; 14184 (5:4184) - push de - call GetCardIDFromDeckIndex - ld a, e - cp DOUBLE_COLORLESS_ENERGY - jr z, .set_carry - ld a, [wTempCardType] - cp TYPE_ENERGY_DOUBLE_COLORLESS - jr z, .set_carry - ld a, [wTempCardID] - - ld d, PSYCHIC_ENERGY - cp EXEGGCUTE - jr z, .check_energy - cp EXEGGUTOR - jr z, .check_energy - cp PSYDUCK - jr z, .check_energy - cp GOLDUCK - jr z, .check_energy - - ld d, WATER_ENERGY - cp SURFING_PIKACHU1 - jr z, .check_energy - cp SURFING_PIKACHU2 - jr z, .check_energy - - cp EEVEE - jr nz, .check_type - ld a, e - cp WATER_ENERGY - jr z, .set_carry - cp FIRE_ENERGY - jr z, .set_carry - cp LIGHTNING_ENERGY - jr z, .set_carry - -.check_type - ld d, $00 ; unnecessary? - call GetCardType - ld d, a - ld a, [wTempCardType] - cp d - jr z, .set_carry - pop de - or a - ret - -.check_energy - ld a, d - cp e - jr nz, .check_type -.set_carry - pop de - scf - ret - -; pick a random Pokemon in the bench. -; output: -; - a = PLAY_AREA_* of Bench Pokemon picked. -PickRandomBenchPokemon: ; 141da (5:41da) - ld a, DUELVARS_NUMBER_OF_POKEMON_IN_PLAY_AREA - call GetTurnDuelistVariable - dec a - call Random - inc a - ret - -AIPickPrizeCards: ; 141e5 (5:41e5) - ld a, [wNumberPrizeCardsToTake] - ld b, a -.loop - call .PickPrizeCard - ld a, DUELVARS_PRIZES - call GetTurnDuelistVariable - or a - jr z, .done - dec b - jr nz, .loop -.done - ret - -; picks a prize card at random -; and adds it to the hand. -.PickPrizeCard: ; 141f8 (5:41f8) - ld a, DUELVARS_PRIZES - call GetTurnDuelistVariable - push hl - ld c, a - -; choose a random prize card until -; one is found that isn't taken already. -.loop_pick_prize - ld a, 6 - call Random - ld e, a - ld d, $00 - ld hl, .prize_flags - add hl, de - ld a, [hl] - and c - jr z, .loop_pick_prize ; no prize - -; prize card was found -; remove this prize from wOpponentPrizes - ld a, [hl] - pop hl - cpl - and [hl] - ld [hl], a - -; add this prize card to the hand - ld a, e - add DUELVARS_PRIZE_CARDS - call GetTurnDuelistVariable - call AddCardToHand - ret - -.prize_flags ; 1421e (5:421e) - db $1 << 0 - db $1 << 1 - db $1 << 2 - db $1 << 3 - db $1 << 4 - db $1 << 5 - db $1 << 6 - db $1 << 7 - -; routine for AI to play all Basic cards from its hand -; in the beginning of the Duel. -AIPlayInitialBasicCards: ; 14226 (5:4226) - call CreateHandCardList - ld hl, wDuelTempList -.check_for_next_card - ld a, [hli] - ldh [hTempCardIndex_ff98], a - cp $ff - ret z ; return when done - - call LoadCardDataToBuffer1_FromDeckIndex - ld a, [wLoadedCard1Type] - cp TYPE_ENERGY - jr nc, .check_for_next_card ; skip if not Pokemon card - ld a, [wLoadedCard1Stage] - or a - jr nz, .check_for_next_card ; skip if not Basic Stage - -; play Basic card from hand - push hl - ldh a, [hTempCardIndex_ff98] - call PutHandPokemonCardInPlayArea - pop hl - jr .check_for_next_card - -; returns carry if Pokémon at hTempPlayAreaLocation_ff9d -; can't use an attack or if that selected attack doesn't have enough energy -; input: -; [hTempPlayAreaLocation_ff9d] = location of Pokémon card -; [wSelectedAttack] = selected attack to examine -CheckIfSelectedAttackIsUnusable: ; 1424b (5:424b) - ldh a, [hTempPlayAreaLocation_ff9d] - or a - jr nz, .bench - - bank1call HandleCantAttackSubstatus - ret c - bank1call CheckIfActiveCardParalyzedOrAsleep - ret c - - ld a, DUELVARS_ARENA_CARD - call GetTurnDuelistVariable - ld d, a - ld a, [wSelectedAttack] - ld e, a - call CopyAttackDataAndDamage_FromDeckIndex - call HandleAmnesiaSubstatus - ret c - ld a, EFFECTCMDTYPE_INITIAL_EFFECT_1 - call TryExecuteEffectCommandFunction - ret c - -.bench - call CheckEnergyNeededForAttack - ret c ; can't be used - ld a, ATTACK_FLAG2_ADDRESS | FLAG_2_BIT_5_F - call CheckLoadedAttackFlag - ret - -; load selected attack from Pokémon in hTempPlayAreaLocation_ff9d -; and checks if there is enough energy to execute the selected attack -; input: -; [hTempPlayAreaLocation_ff9d] = location of Pokémon card -; [wSelectedAttack] = selected attack to examine -; output: -; b = basic energy still needed -; c = colorless energy still needed -; e = output of ConvertColorToEnergyCardID, or $0 if not an attack -; carry set if no attack -; OR if it's a Pokémon Power -; OR if not enough energy for attack -CheckEnergyNeededForAttack: ; 14279 (5:4279) - ldh a, [hTempPlayAreaLocation_ff9d] - add DUELVARS_ARENA_CARD - call GetTurnDuelistVariable - ld d, a - ld a, [wSelectedAttack] - ld e, a - call CopyAttackDataAndDamage_FromDeckIndex - ld hl, wLoadedAttackName - ld a, [hli] - or [hl] - jr z, .no_attack - ld a, [wLoadedAttackCategory] - cp POKEMON_POWER - jr nz, .is_attack -.no_attack - lb bc, 0, 0 - ld e, c - scf - ret - -.is_attack - ldh a, [hTempPlayAreaLocation_ff9d] - ld e, a - call GetPlayAreaCardAttachedEnergies - bank1call HandleEnergyBurn - - xor a - ld [wTempLoadedAttackEnergyCost], a - ld [wTempLoadedAttackEnergyNeededAmount], a - ld [wTempLoadedAttackEnergyNeededType], a - - ld hl, wAttachedEnergies - ld de, wLoadedAttackEnergyCost - ld b, 0 - ld c, (NUM_TYPES / 2) - 1 - -.loop - ; check all basic energy cards except colorless - ld a, [de] - swap a - call CheckIfEnoughParticularAttachedEnergy - ld a, [de] - call CheckIfEnoughParticularAttachedEnergy - inc de - dec c - jr nz, .loop - -; running CheckIfEnoughParticularAttachedEnergy back to back like this -; overwrites the results of a previous call of this function, -; however, no attack in the game has energy requirements for two -; different energy types (excluding colorless), so this routine -; will always just return the result for one type of basic energy, -; while all others will necessarily have an energy cost of 0 -; if attacks are added to the game with energy requirements of -; two different basic energy types, then this routine only accounts -; for the type with the highest index - - ; colorless - ld a, [de] - swap a - and %00001111 - ld b, a ; colorless energy still needed - ld a, [wTempLoadedAttackEnergyCost] - ld hl, wTempLoadedAttackEnergyNeededAmount - sub [hl] - ld c, a ; basic energy still needed - ld a, [wTotalAttachedEnergies] - sub c - sub b - jr c, .not_enough - - ld a, [wTempLoadedAttackEnergyNeededAmount] - or a - ret z - -; being here means the energy cost isn't satisfied, -; including with colorless energy - xor a -.not_enough - cpl - inc a - ld c, a ; colorless energy still needed - ld a, [wTempLoadedAttackEnergyNeededAmount] - ld b, a ; basic energy still needed - ld a, [wTempLoadedAttackEnergyNeededType] - call ConvertColorToEnergyCardID - ld e, a - ld d, 0 - scf - ret - -; takes as input the energy cost of an attack for a -; particular energy, stored in the lower nibble of a -; if the attack costs some amount of this energy, the lower nibble of a != 0, -; and this amount is stored in wTempLoadedAttackEnergyCost -; sets carry flag if not enough energy of this type attached -; input: -; a = this energy cost of attack (lower nibble) -; [hl] = attached energy -; output: -; carry set if not enough of this energy type attached -CheckIfEnoughParticularAttachedEnergy: ; 142f4 (5:42f4) - and %00001111 - jr nz, .check -.has_enough - inc hl - inc b - or a - ret - -.check - ld [wTempLoadedAttackEnergyCost], a - sub [hl] - jr z, .has_enough - jr c, .has_enough - - ; not enough energy - ld [wTempLoadedAttackEnergyNeededAmount], a - ld a, b - ld [wTempLoadedAttackEnergyNeededType], a - inc hl - inc b - scf - ret - -; input: -; a = energy type -; output: -; a = energy card ID -ConvertColorToEnergyCardID: ; 1430f (5:430f) - push hl - push de - ld e, a - ld d, 0 - ld hl, .card_id - add hl, de - ld a, [hl] - pop de - pop hl - ret - -.card_id - db FIRE_ENERGY - db GRASS_ENERGY - db LIGHTNING_ENERGY - db WATER_ENERGY - db FIGHTING_ENERGY - db PSYCHIC_ENERGY - db DOUBLE_COLORLESS_ENERGY - -; returns carry if loaded attack effect has -; an "initial effect 2" or "require selection" command type -; unreferenced -Func_14323: ; 14323 (5:4323) - ld hl, wLoadedAttackEffectCommands - ld a, [hli] - ld h, [hl] - ld l, a - ld a, EFFECTCMDTYPE_INITIAL_EFFECT_2 - push hl - call CheckMatchingCommand - pop hl - jr nc, .set_carry - ld a, EFFECTCMDTYPE_REQUIRE_SELECTION - call CheckMatchingCommand - jr nc, .set_carry - or a - ret -.set_carry - scf - ret - -; return carry depending on card index in a: -; - if energy card, return carry if no energy card has been played yet -; - if basic Pokémon card, return carry if there's space in bench -; - if evolution card, return carry if there's a Pokémon -; in Play Area it can evolve -; - if trainer card, return carry if it can be used -; input: -; a = card index to check -CheckIfCardCanBePlayed: ; 1433d (5:433d) - ldh [hTempCardIndex_ff9f], a - call LoadCardDataToBuffer1_FromDeckIndex - ld a, [wLoadedCard1Type] - cp TYPE_ENERGY - jr c, .pokemon_card - cp TYPE_TRAINER - jr z, .trainer_card - -; energy card - ld a, [wAlreadyPlayedEnergy] - or a - ret z - scf - ret - -.pokemon_card - ld a, [wLoadedCard1Stage] - or a - jr nz, .evolution_card - ld a, DUELVARS_NUMBER_OF_POKEMON_IN_PLAY_AREA - call GetTurnDuelistVariable - cp MAX_PLAY_AREA_POKEMON - ccf - ret - -.evolution_card - bank1call IsPrehistoricPowerActive - ret c - ld a, DUELVARS_NUMBER_OF_POKEMON_IN_PLAY_AREA - call GetTurnDuelistVariable - ld c, a - ld b, 0 -.loop - push bc - ld e, b - ldh a, [hTempCardIndex_ff9f] - ld d, a - call CheckIfCanEvolveInto - pop bc - ret nc - inc b - dec c - jr nz, .loop - scf - ret - -.trainer_card - bank1call CheckCantUseTrainerDueToHeadache - ret c - call LoadNonPokemonCardEffectCommands - ld a, EFFECTCMDTYPE_INITIAL_EFFECT_1 - call TryExecuteEffectCommandFunction - ret - -; loads all the energy cards -; in hand in wDuelTempList -; return carry if no energy cards found -CreateEnergyCardListFromHand: ; 1438c (5:438c) - push hl - push de - push bc - ld de, wDuelTempList - ld b, a - ld a, DUELVARS_NUMBER_OF_CARDS_IN_HAND - call GetTurnDuelistVariable - ld c, a - inc c - ld l, LOW(wOpponentHand) - jr .decrease - -.loop - ld a, [hli] - push de - call GetCardIDFromDeckIndex - call GetCardType - pop de - and TYPE_ENERGY - jr z, .decrease - dec hl - ld a, [hli] - ld [de], a - inc de -.decrease - dec c - jr nz, .loop - - ld a, $ff - ld [de], a - pop bc - pop de - pop hl - ld a, [wDuelTempList] - cp $ff - ccf - ret - -; looks for card ID in hand and -; sets carry if a card wasn't found -; as opposed to LookForCardIDInHandList_Bank5 -; this function doesn't create a list -; and preserves hl, de and bc -; input: -; a = card ID -; output: -; a = card deck index, if found -; carry set if NOT found -LookForCardIDInHand: ; 143bf (5:43bf) - push hl - push de - push bc - ld b, a - ld a, DUELVARS_NUMBER_OF_CARDS_IN_HAND - call GetTurnDuelistVariable - ld c, a - inc c - ld l, DUELVARS_HAND - jr .next - -.loop - ld a, [hli] - call GetCardIDFromDeckIndex - ld a, e - cp b - jr z, .no_carry -.next - dec c - jr nz, .loop - - pop bc - pop de - pop hl - scf - ret - -.no_carry - dec hl - ld a, [hl] - pop bc - pop de - pop hl - or a - ret - -INCLUDE "engine/ai/damage_calculation.asm" - -AIProcessHandTrainerCards: ; 14663 (5:4663) - farcall _AIProcessHandTrainerCards - ret - -INCLUDE "engine/ai/deck_ai.asm" - -; return carry if card ID loaded in a is found in hand -; and outputs in a the deck index of that card -; as opposed to LookForCardIDInHand, this function -; creates a list in wDuelTempList -; input: -; a = card ID -; output: -; a = card deck index, if found -; carry set if found -LookForCardIDInHandList_Bank5: ; 155d2 (5:55d2) - ld [wTempCardIDToLook], a - call CreateHandCardList - ld hl, wDuelTempList - -.loop - ld a, [hli] - cp $ff - ret z - ldh [hTempCardIndex_ff98], a - call LoadCardDataToBuffer1_FromDeckIndex - ld b, a - ld a, [wTempCardIDToLook] - cp b - jr nz, .loop - - ldh a, [hTempCardIndex_ff98] - scf - ret - -; returns carry if card ID in a -; is found in Play Area, starting with -; location in b -; input: -; a = card ID -; b = PLAY_AREA_* to start with -; output: -; a = PLAY_AREA_* of found card -; carry set if found -LookForCardIDInPlayArea_Bank5: ; 155ef (5:55ef) - ld [wTempCardIDToLook], a - -.loop - ld a, DUELVARS_ARENA_CARD - add b - call GetTurnDuelistVariable - cp $ff - ret z - call LoadCardDataToBuffer1_FromDeckIndex - ld c, a - ld a, [wTempCardIDToLook] - cp c - jr z, .found - inc b - ld a, MAX_PLAY_AREA_POKEMON - cp b - jr nz, .loop - - ld b, $ff - or a - ret -.found - ld a, b - scf - ret - -; check if energy card ID in e is in AI hand and, -; if so, attaches it to card ID in d in Play Area. -; input: -; e = Energy card ID -; d = Pokemon card ID -AIAttachEnergyInHandToCardInPlayArea: ; 15612 (5:5612) - ld a, e - push de - call LookForCardIDInHandList_Bank5 - pop de - ret nc ; not in hand - ld b, PLAY_AREA_ARENA - -.attach - ld e, a - ld a, d - call LookForCardIDInPlayArea_Bank5 - ldh [hTempPlayAreaLocation_ffa1], a - ld a, e - ldh [hTemp_ffa0], a - ld a, OPPACTION_PLAY_ENERGY - bank1call AIMakeDecision - ret - -; same as AIAttachEnergyInHandToCardInPlayArea but -; only look for card ID in the Bench. -AIAttachEnergyInHandToCardInBench: ; 1562b (5:562b) - ld a, e - push de - call LookForCardIDInHandList_Bank5 - pop de - ret nc - ld b, PLAY_AREA_BENCH_1 - jr AIAttachEnergyInHandToCardInPlayArea.attach - -INCLUDE "engine/ai/init.asm" - -; load selected attack from Pokémon in hTempPlayAreaLocation_ff9d, -; gets an energy card to discard and subsequently -; check if there is enough energy to execute the selected attack -; after removing that attached energy card. -; input: -; [hTempPlayAreaLocation_ff9d] = location of Pokémon card -; [wSelectedAttack] = selected attack to examine -; output: -; b = basic energy still needed -; c = colorless energy still needed -; e = output of ConvertColorToEnergyCardID, or $0 if not an attack -; carry set if no attack -; OR if it's a Pokémon Power -; OR if not enough energy for attack -CheckEnergyNeededForAttackAfterDiscard: ; 156c3 (5:56c3) - ldh a, [hTempPlayAreaLocation_ff9d] - add DUELVARS_ARENA_CARD - call GetTurnDuelistVariable - ld d, a - ld a, [wSelectedAttack] - ld e, a - call CopyAttackDataAndDamage_FromDeckIndex - ld hl, wLoadedAttackName - ld a, [hli] - or [hl] - jr z, .no_attack - ld a, [wLoadedAttackCategory] - cp POKEMON_POWER - jr nz, .is_attack -.no_attack - lb bc, 0, 0 - ld e, c - scf - ret - -.is_attack - ldh a, [hTempPlayAreaLocation_ff9d] - farcall AIPickEnergyCardToDiscard - call LoadCardDataToBuffer1_FromDeckIndex - cp DOUBLE_COLORLESS_ENERGY - jr z, .colorless - -; color energy -; decrease respective attached energy by 1. - ld hl, wAttachedEnergies - dec a - ld c, a - ld b, $00 - add hl, bc - dec [hl] - ld hl, wTotalAttachedEnergies - dec [hl] - jr .asm_1570c -; decrease attached colorless by 2. -.colorless - ld hl, wAttachedEnergies + COLORLESS - dec [hl] - dec [hl] - ld hl, wTotalAttachedEnergies - dec [hl] - dec [hl] - -.asm_1570c - bank1call HandleEnergyBurn - xor a - ld [wTempLoadedAttackEnergyCost], a - ld [wTempLoadedAttackEnergyNeededAmount], a - ld [wTempLoadedAttackEnergyNeededType], a - ld hl, wAttachedEnergies - ld de, wLoadedAttackEnergyCost - ld b, 0 - ld c, (NUM_TYPES / 2) - 1 -.loop - ; check all basic energy cards except colorless - ld a, [de] - swap a - call CheckIfEnoughParticularAttachedEnergy - ld a, [de] - call CheckIfEnoughParticularAttachedEnergy - inc de - dec c - jr nz, .loop - - ld a, [de] - swap a - and $0f - ld b, a ; colorless energy still needed - ld a, [wTempLoadedAttackEnergyCost] - ld hl, wTempLoadedAttackEnergyNeededAmount - sub [hl] - ld c, a ; basic energy still needed - ld a, [wTotalAttachedEnergies] - sub c - sub b - jr c, .not_enough_energy - - ld a, [wTempLoadedAttackEnergyNeededAmount] - or a - ret z - -; being here means the energy cost isn't satisfied, -; including with colorless energy - xor a -.not_enough_energy - cpl - inc a - ld c, a ; colorless energy still needed - ld a, [wTempLoadedAttackEnergyNeededAmount] - ld b, a ; basic energy still needed - ld a, [wTempLoadedAttackEnergyNeededType] - call ConvertColorToEnergyCardID - ld e, a - ld d, 0 - scf - ret - -; zeroes a bytes starting at hl -ClearMemory_Bank5: ; 1575e (5:575e) - push af - push bc - push hl - ld b, a - xor a -.clear_loop - ld [hli], a - dec b - jr nz, .clear_loop - pop hl - pop bc - pop af - ret - -; returns in a the tens digit of value in a -CalculateByteTensDigit: ; 1576b (5:576b) - push bc - ld c, 0 -.loop - sub 10 - jr c, .done - inc c - jr .loop -.done - ld a, c - pop bc - ret - -; returns in a the result of -; dividing b by a, rounded down -; input: -; a = divisor -; b = dividend -CalculateBDividedByA_Bank5: ; 15778 (5:5778) - push bc - ld c, a - ld a, b - ld b, c - ld c, 0 -.loop - sub b - jr c, .done - inc c - jr .loop -.done - ld a, c - pop bc - ret - -; returns in a the number of energy cards attached -; to Pokémon in location held by e -; this assumes that colorless are paired so -; that one colorless energy card provides 2 colorless energy -; input: -; e = location to check, i.e. PLAY_AREA_* -; output: -; a = number of energy cards attached -CountNumberOfEnergyCardsAttached: ; 15787 (5:5787) - call GetPlayAreaCardAttachedEnergies - ld a, [wTotalAttachedEnergies] - or a - ret z - - xor a - push hl - push bc - ld b, NUM_COLORED_TYPES - ld hl, wAttachedEnergies -; sum all the attached energies -.loop - add [hl] - inc hl - dec b - jr nz, .loop - - ld b, [hl] - srl b -; counts colorless ad halves it - add b - pop bc - pop hl - ret - -; returns carry if any card with ID in e is found -; in card location in a -; input: -; a = card location to look in; -; e = card ID to look for. -; output: -; a = deck index of card found, if any -CheckIfAnyCardIDinLocation: ; 157a3 (5:57a3) - ld b, a - ld c, e - lb de, 0, 0 -.loop - ld a, DUELVARS_CARD_LOCATIONS - add e - call GetTurnDuelistVariable - cp b - jr nz, .next - ld a, e - push de - call GetCardIDFromDeckIndex - ld a, e - pop de - cp c - jr z, .set_carry -.next - inc e - ld a, DECK_SIZE - cp e - jr nz, .loop - or a - ret -.set_carry - ld a, e - scf - ret - -; counts total number of energy cards in opponent's hand -; plus all the cards attached in Turn Duelist's Play Area. -; output: -; a and wTempAI = total number of energy cards. -CountOppEnergyCardsInHandAndAttached: ; 157c6 (5:57c6) - xor a - ld [wTempAI], a - call CreateEnergyCardListFromHand - jr c, .attached - -; counts number of energy cards in hand - ld b, -1 - ld hl, wDuelTempList -.loop_hand - inc b - ld a, [hli] - cp $ff - jr nz, .loop_hand - ld a, b - ld [wTempAI], a - -; counts number of energy cards -; that are attached in Play Area -.attached - ld a, DUELVARS_NUMBER_OF_POKEMON_IN_PLAY_AREA - call GetTurnDuelistVariable - ld d, a - ld e, PLAY_AREA_ARENA -.loop_play_area - call CountNumberOfEnergyCardsAttached - ld hl, wTempAI - add [hl] - ld [hl], a - inc e - dec d - jr nz, .loop_play_area - ret - -; returns carry if any card with ID in e is found -; in the list that is pointed by hl. -; if one is found, it is removed from the list. -; input: -; e = card ID to look for. -; hl = list to look in -RemoveCardIDInList: ; 157f3 (5:57f3) - push hl - push de - push bc - ld c, e - -.loop_1 - ld a, [hli] - cp $ff - jr z, .no_carry - - ldh [hTempCardIndex_ff98], a - call GetCardIDFromDeckIndex - ld a, c - cp e - jr nz, .loop_1 - -; found - ld d, h - ld e, l - dec hl - -; remove this index from the list -; and reposition the rest of the list ahead. -.loop_2 - ld a, [de] - inc de - ld [hli], a - cp $ff - jr nz, .loop_2 - - ldh a, [hTempCardIndex_ff98] - pop bc - pop de - pop hl - scf - ret - -.no_carry - pop bc - pop de - pop hl - or a - ret - -; play Pokemon cards from the hand to set the starting -; Play Area of Boss decks. -; each Boss deck has two ID lists in order of preference. -; one list is for the Arena card is the other is for the Bench cards. -; if Arena card could not be set (due to hand not having any card in its list) -; or if list is null, return carry and do not play any cards. -TrySetUpBossStartingPlayArea: ; 1581b (5:581b) - ld de, wAICardListArenaPriority - ld a, d - or a - jr z, .set_carry ; return if null - -; pick Arena card - call CreateHandCardList - ld hl, wDuelTempList - ld de, wAICardListArenaPriority - call .PlayPokemonCardInOrder - ret c - -; play Pokemon cards to Bench until there are -; a maximum of 3 cards in Play Area. -.loop - ld de, wAICardListBenchPriority - call .PlayPokemonCardInOrder - jr c, .done - cp 3 - jr c, .loop - -.done - or a - ret -.set_carry - scf - ret - -; runs through input card ID list in de. -; plays to Play Area first card that is found in hand. -; returns carry if none of the cards in the list are found. -; returns number of Pokemon in Play Area in a. -.PlayPokemonCardInOrder ; 1583f (5:583f) - ld a, [de] - ld c, a - inc de - ld a, [de] - ld d, a - ld e, c - -; go in order of the list in de and -; add first card that matches ID. -; returns carry if hand doesn't have any card in list. -.loop_id_list - ld a, [de] - inc de - or a - jr z, .not_found - push de - ld e, a - call RemoveCardIDInList - pop de - jr nc, .loop_id_list - - ; play this card to Play Area and return - push hl - call PutHandPokemonCardInPlayArea - pop hl - or a - ret - -.not_found - scf - ret - -; expects a $00-terminated list of 3-byte data with the following: -; - non-zero value (anything but $1 is ignored) -; - card ID to look for in Play Area -; - number of energy cards -; returns carry if a card ID is found in bench with at least the -; listed number of energy cards -; unreferenced -Func_1585b: ; 1585b (5:585b) - ld a, [hli] - or a - jr z, .no_carry - dec a - jr nz, .next_1 - ld a, [hli] - ld b, PLAY_AREA_BENCH_1 - push hl - call LookForCardIDInPlayArea_Bank5 - jr nc, .next_2 - ld e, a - push de - call CountNumberOfEnergyCardsAttached - pop de - pop hl - ld b, [hl] - cp b - jr nc, .set_carry - inc hl - jr Func_1585b - -.next_1 - inc hl - inc hl - jr Func_1585b - -.next_2 - pop hl - inc hl - jr Func_1585b - -.no_carry - or a - ret - -.set_carry - ld a, e - scf - ret - -; expects a $00-terminated list of 3-byte data with the following: -; - non-zero value -; - card ID -; - number of energy cards -; goes through the given list and if a card with a listed ID is found -; with less than the number of energy cards corresponding to its entry -; then have AI try to play an energy card from the hand to it -; unreferenced -Func_15886: ; 15886 (5:5886) - push hl - call CreateEnergyCardListFromHand - pop hl - ret c ; quit if no energy cards in hand - -.loop_energy_cards - ld a, [hli] - or a - ret z ; done - ld a, [hli] - ld b, PLAY_AREA_ARENA - push hl - call LookForCardIDInPlayArea_Bank5 - jr nc, .next ; skip if not found in Play Area - ld e, a - push de - call CountNumberOfEnergyCardsAttached - pop de - pop hl - cp [hl] - inc hl - jr nc, .loop_energy_cards - ld a, e - ldh [hTempPlayAreaLocation_ff9d], a - push hl - call AITryToPlayEnergyCard - pop hl - ret c - jr .loop_energy_cards -.next - pop hl - inc hl - jr .loop_energy_cards - -INCLUDE "engine/ai/retreat.asm" - -; Copy cards from wDuelTempList in hl to wHandTempList in de -CopyHandCardList: ; 15ea6 (5:5ea6) - ld a, [hli] - ld [de], a - cp $ff - ret z - inc de - jr CopyHandCardList - -INCLUDE "engine/ai/hand_pokemon.asm" - -; check if player's active Pokémon is Mr Mime -; if it isn't, set carry -; if it is, check if Pokémon at a -; can damage it, and if it can, set carry -; input: -; a = location of Pokémon card -CheckDamageToMrMime: ; 16270 (5:6270) - push af - ld a, DUELVARS_ARENA_CARD - call GetNonTurnDuelistVariable - call SwapTurn - call GetCardIDFromDeckIndex - call SwapTurn - ld a, e - cp MR_MIME - pop bc - jr nz, .set_carry - ld a, b - call CheckIfCanDamageDefendingPokemon - jr c, .set_carry - or a - ret -.set_carry - scf - ret - -; returns carry if arena card -; can knock out defending Pokémon -CheckIfActiveCardCanKnockOut: ; 1628f (5:628f) - xor a - ldh [hTempPlayAreaLocation_ff9d], a - call CheckIfAnyAttackKnocksOutDefendingCard - jr nc, .fail - call CheckIfSelectedAttackIsUnusable - jp c, .fail - scf - ret - -.fail - or a - ret - -; outputs carry if any of the active Pokémon attacks -; can be used and are not residual -CheckIfActivePokemonCanUseAnyNonResidualAttack: ; 162a1 (5:62a1) - xor a ; active card - ldh [hTempPlayAreaLocation_ff9d], a -; first atk - ld [wSelectedAttack], a - call CheckIfSelectedAttackIsUnusable - jr c, .next_atk - ld a, [wLoadedAttackCategory] - and RESIDUAL - jr z, .ok - -.next_atk -; second atk - ld a, $01 - ld [wSelectedAttack], a - call CheckIfSelectedAttackIsUnusable - jr c, .fail - ld a, [wLoadedAttackCategory] - and RESIDUAL - jr z, .ok -.fail - or a - ret - -.ok - scf - ret - -; looks for energy card(s) in hand depending on -; what is needed for selected card, for both attacks -; - if one basic energy is required, look for that energy; -; - if one colorless is required, create a list at wDuelTempList -; of all energy cards; -; - if two colorless are required, look for double colorless; -; return carry if successful in finding card -; input: -; [hTempPlayAreaLocation_ff9d] = location of Pokémon card -LookForEnergyNeededInHand: ; 162c8 (5:62c8) - xor a ; first attack - ld [wSelectedAttack], a - call CheckEnergyNeededForAttack - ld a, b - add c - cp 1 - jr z, .one_energy - cp 2 - jr nz, .second_attack - ld a, c - cp 2 - jr z, .two_colorless - -.second_attack - ld a, SECOND_ATTACK - ld [wSelectedAttack], a - call CheckEnergyNeededForAttack - ld a, b - add c - cp 1 - jr z, .one_energy - cp 2 - jr nz, .no_carry - ld a, c - cp 2 - jr z, .two_colorless -.no_carry - or a - ret - -.one_energy - ld a, b - or a - jr z, .one_colorless - ld a, e - call LookForCardIDInHandList_Bank5 - ret c - jr .no_carry - -.one_colorless - call CreateEnergyCardListFromHand - jr c, .no_carry - scf - ret - -.two_colorless - ld a, DOUBLE_COLORLESS_ENERGY - call LookForCardIDInHandList_Bank5 - ret c - jr .no_carry - -; looks for energy card(s) in hand depending on -; what is needed for selected card and attack -; - if one basic energy is required, look for that energy; -; - if one colorless is required, create a list at wDuelTempList -; of all energy cards; -; - if two colorless are required, look for double colorless; -; return carry if successful in finding card -; input: -; [hTempPlayAreaLocation_ff9d] = location of Pokémon card -; [wSelectedAttack] = selected attack to examine -LookForEnergyNeededForAttackInHand: ; 16311 (5:6311) - call CheckEnergyNeededForAttack - ld a, b - add c - cp 1 - jr z, .one_energy - cp 2 - jr nz, .done - ld a, c - cp 2 - jr z, .two_colorless -.done - or a - ret - -.one_energy - ld a, b - or a - jr z, .one_colorless - ld a, e - call LookForCardIDInHandList_Bank5 - ret c - jr .done - -.one_colorless - call CreateEnergyCardListFromHand - jr c, .done - scf - ret - -.two_colorless - ld a, DOUBLE_COLORLESS_ENERGY - call LookForCardIDInHandList_Bank5 - ret c - jr .done - -; goes through $00 terminated list pointed -; by wAICardListPlayFromHandPriority and compares it to each card in hand. -; Sorts the hand in wDuelTempList so that the found card IDs -; are in the same order as the list pointed by de. -SortTempHandByIDList: ; 1633f (5:633f) - ld a, [wAICardListPlayFromHandPriority+1] - or a - ret z ; return if list is empty - -; start going down the ID list - ld d, a - ld a, [wAICardListPlayFromHandPriority] - ld e, a - ld c, 0 -.loop_list_id -; get this item's ID -; if $00, list has ended - ld a, [de] - or a - ret z ; return when list is over - inc de - ld hl, wDuelTempList - ld b, 0 - add hl, bc - ld b, a - -; search in the hand card list -.next_hand_card - ld a, [hl] - ldh [hTempCardIndex_ff98], a - cp -1 - jr z, .loop_list_id - push bc - push de - call GetCardIDFromDeckIndex - ld a, e - pop de - pop bc - cp b - jr nz, .not_same - -; found -; swap this hand card with the spot -; in hand corresponding to c - push bc - push hl - ld b, 0 - ld hl, wDuelTempList - add hl, bc - ld b, [hl] - ldh a, [hTempCardIndex_ff98] - ld [hl], a - pop hl - ld [hl], b - pop bc - inc c -.not_same - inc hl - jr .next_hand_card - -; looks for energy card(s) in list at wDuelTempList -; depending on energy flags set in a -; return carry if successful in finding card -; input: -; a = energy flags needed -CheckEnergyFlagsNeededInList: ; 1637b (5:637b) - ld e, a - ld hl, wDuelTempList -.next_card - ld a, [hli] - cp $ff - jr z, .no_carry - push de - call GetCardIDFromDeckIndex - ld a, e - pop de - -; fire - cp FIRE_ENERGY - jr nz, .grass - ld a, FIRE_F - jr .check_energy -.grass - cp GRASS_ENERGY - jr nz, .lightning - ld a, GRASS_F - jr .check_energy -.lightning - cp LIGHTNING_ENERGY - jr nz, .water - ld a, LIGHTNING_F - jr .check_energy -.water - cp WATER_ENERGY - jr nz, .fighting - ld a, WATER_F - jr .check_energy -.fighting - cp FIGHTING_ENERGY - jr nz, .psychic - ld a, FIGHTING_F - jr .check_energy -.psychic - cp PSYCHIC_ENERGY - jr nz, .colorless - ld a, PSYCHIC_F - jr .check_energy -.colorless - cp DOUBLE_COLORLESS_ENERGY - jr nz, .next_card - ld a, COLORLESS_F - -; if energy card matches required energy, return carry -.check_energy - ld d, e - and e - ld e, d - jr z, .next_card - scf - ret -.no_carry - or a - ret - -; returns in a the energy cost of both attacks from card index in a -; represented by energy flags -; i.e. each bit represents a different energy type cost -; if any colorless energy is required, all bits are set -; input: -; a = card index -; output: -; a = bits of each energy requirement -GetAttacksEnergyCostBits: ; 163c9 (5:63c9) - call LoadCardDataToBuffer2_FromDeckIndex - ld hl, wLoadedCard2Atk1EnergyCost - call GetEnergyCostBits - ld b, a - - push bc - ld hl, wLoadedCard2Atk2EnergyCost - call GetEnergyCostBits - pop bc - or b - ret - -; returns in a the energy cost of an attack in [hl] -; represented by energy flags -; i.e. each bit represents a different energy type cost -; if any colorless energy is required, all bits are set -; input: -; [hl] = Loaded card attack energy cost -; output: -; a = bits of each energy requirement -GetEnergyCostBits: ; 163dd (5:63dd) - ld c, $00 - ld a, [hli] - ld b, a - -; fire - and $f0 - jr z, .grass - ld c, FIRE_F -.grass - ld a, b - and $0f - jr z, .lightning - ld a, GRASS_F - or c - ld c, a -.lightning - ld a, [hli] - ld b, a - and $f0 - jr z, .water - ld a, LIGHTNING_F - or c - ld c, a -.water - ld a, b - and $0f - jr z, .fighting - ld a, WATER_F - or c - ld c, a -.fighting - ld a, [hli] - ld b, a - and $f0 - jr z, .psychic - ld a, FIGHTING_F - or c - ld c, a -.psychic - ld a, b - and $0f - jr z, .colorless - ld a, PSYCHIC_F - or c - ld c, a -.colorless - ld a, [hli] - ld b, a - and $f0 - jr z, .done - ld a, %11111111 - or c ; unnecessary - ld c, a -.done - ld a, c - ret - -; set carry flag if any card in -; wDuelTempList evolves card index in a -; if found, the evolution card index is returned in a -; input: -; a = card index to check evolution -; output: -; a = card index of evolution found -CheckForEvolutionInList: ; 16422 (5:6422) - ld b, a - ld a, DUELVARS_ARENA_CARD - call GetTurnDuelistVariable - - push af - ld [hl], b - ld hl, wDuelTempList -.loop - ld a, [hli] - cp $ff - jr z, .no_carry - ld d, a - ld e, PLAY_AREA_ARENA - push de - push hl - call CheckIfCanEvolveInto - pop hl - pop de - jr c, .loop - - ld a, DUELVARS_ARENA_CARD - call GetTurnDuelistVariable - pop af - ld [hl], a - ld a, d - scf - ret - -.no_carry - ld a, DUELVARS_ARENA_CARD - call GetTurnDuelistVariable - pop af - ld [hl], a - or a - ret - -; set carry if it finds an evolution for -; the card index in a in the deck -; if found, return that evolution card index in a -; input: -; a = card index to check evolution -; output: -; a = card index of evolution found -CheckForEvolutionInDeck: ; 16451 (5:6451) - ld b, a - ld a, DUELVARS_ARENA_CARD - call GetTurnDuelistVariable - - push af - ld [hl], b - ld e, 0 -.loop - ld a, DUELVARS_CARD_LOCATIONS - add e - call GetTurnDuelistVariable - cp CARD_LOCATION_DECK - jr nz, .not_in_deck - push de - ld d, e - ld e, PLAY_AREA_ARENA - call CheckIfCanEvolveInto - pop de - jr nc, .set_carry - -; exit when it gets to the prize cards -.not_in_deck - inc e - ld a, DUELVARS_PRIZE_CARDS - cp e - jr nz, .loop - - ld a, DUELVARS_ARENA_CARD - call GetTurnDuelistVariable - pop af - ld [hl], a - or a - ret - -.set_carry - ld a, DUELVARS_ARENA_CARD - call GetTurnDuelistVariable - pop af - ld [hl], a - ld a, e - scf - ret - -INCLUDE "engine/ai/energy.asm" - -INCLUDE "engine/ai/attacks.asm" - -INCLUDE "engine/ai/special_attacks.asm" - -; checks in other Play Area for non-basic cards. -; afterwards, that card is checked for damage, -; and if the damage counters it has is greater than or equal -; to the max HP of the card stage below it, -; return carry and that card's Play Area location in a. -; output: -; a = card location of Pokémon card, if found; -; carry set if such a card is found. -LookForCardThatIsKnockedOutOnDevolution: ; 17080 (5:7080) - ldh a, [hTempPlayAreaLocation_ff9d] - push af - call SwapTurn - ld a, DUELVARS_NUMBER_OF_POKEMON_IN_PLAY_AREA - call GetTurnDuelistVariable - ld b, a - ld c, PLAY_AREA_ARENA - -.loop - ld a, c - ldh [hTempPlayAreaLocation_ff9d], a - push bc - bank1call GetCardOneStageBelow - pop bc - jr c, .next - ; is not a basic card - ; compare its HP with current damage - ld a, d - push bc - call LoadCardDataToBuffer2_FromDeckIndex - pop bc - ld a, [wLoadedCard2HP] - ld [wTempAI], a - ld e, c - push bc - call GetCardDamageAndMaxHP - pop bc - ld e, a - ld a, [wTempAI] - cp e - jr c, .set_carry - jr z, .set_carry -.next - inc c - ld a, c - cp b - jr nz, .loop - - call SwapTurn - pop af - ldh [hTempPlayAreaLocation_ff9d], a - or a - ret - -.set_carry - call SwapTurn - pop af - ldh [hTempPlayAreaLocation_ff9d], a - ld a, c - scf - ret - -; returns carry if the following conditions are met: -; - arena card HP >= half max HP -; - arena card Unknown2's 4 bit is not set or -; is set but there's no evolution of card in hand/deck -; - arena card can use second attack -CheckIfArenaCardIsAtHalfHPCanEvolveAndUseSecondAttack: ; 170c9 (5:70c9) - ld a, DUELVARS_ARENA_CARD - call GetTurnDuelistVariable - ld d, a - push de - call LoadCardDataToBuffer1_FromDeckIndex - ld a, DUELVARS_ARENA_CARD_HP - call GetTurnDuelistVariable - ld d, a - ld a, [wLoadedCard1HP] - rrca - cp d - pop de - jr nc, .no_carry - - ld a, [wLoadedCard1Unknown2] - and %00010000 - jr z, .check_second_attack - ld a, d - call CheckCardEvolutionInHandOrDeck - jr c, .no_carry - -.check_second_attack - xor a ; active card - ldh [hTempPlayAreaLocation_ff9d], a - ld a, SECOND_ATTACK - ld [wSelectedAttack], a - push hl - call CheckIfSelectedAttackIsUnusable - pop hl - jr c, .no_carry - scf - ret -.no_carry - or a - ret - -; count Pokemon in the Bench that -; meet the following conditions: -; - card HP > half max HP -; - card Unknown2's 4 bit is not set or -; is set but there's no evolution of card in hand/deck -; - card can use second attack -; Outputs the number of Pokémon in bench -; that meet these requirements in a -; and returns carry if at least one is found -CountNumberOfSetUpBenchPokemon: ; 17101 (5:7101) - ldh a, [hTempPlayAreaLocation_ff9d] - ld d, a - ld a, [wSelectedAttack] - ld e, a - push de - ld a, DUELVARS_BENCH - call GetTurnDuelistVariable - lb bc, 0, 0 - push hl - -.next - inc c - pop hl - ld a, [hli] - push hl - cp $ff - jr z, .done - - ld d, a - push de - push bc - call LoadCardDataToBuffer1_FromDeckIndex - pop bc - -; compares card's current HP with max HP - ld a, c - add DUELVARS_ARENA_CARD_HP - call GetTurnDuelistVariable - ld d, a - ld a, [wLoadedCard1HP] - rrca - -; a = max HP / 2 -; d = current HP -; jumps if (current HP) <= (max HP / 2) - cp d - pop de - jr nc, .next - - ld a, [wLoadedCard1Unknown2] - and $10 - jr z, .check_second_attack - - ld a, d - push bc - call CheckCardEvolutionInHandOrDeck - pop bc - jr c, .next - -.check_second_attack - ld a, c - ldh [hTempPlayAreaLocation_ff9d], a - ld a, SECOND_ATTACK - ld [wSelectedAttack], a - push bc - push hl - call CheckIfSelectedAttackIsUnusable - pop hl - pop bc - jr c, .next - inc b - jr .next - -.done - pop hl - pop de - ld a, e - ld [wSelectedAttack], a - ld a, d - ldh [hTempPlayAreaLocation_ff9d], a - ld a, b - or a - ret z - scf - ret - -; handles AI logic to determine some selections regarding certain attacks, -; if any of these attacks were chosen to be used. -; returns carry if selection was successful, -; and no carry if unable to make one. -; outputs in hTempPlayAreaLocation_ffa1 the chosen parameter. -AISelectSpecialAttackParameters: ; 17161 (5:7161) - ld a, [wSelectedAttack] - push af - call .SelectAttackParameters - pop bc - ld a, b - ld [wSelectedAttack], a - ret - -.SelectAttackParameters: ; 1716e (5:716e) - ld a, DUELVARS_ARENA_CARD - call GetTurnDuelistVariable - call GetCardIDFromDeckIndex - ld a, e - cp MEW3 - jr z, .DevolutionBeam - cp MEWTWO3 - jr z, .EnergyAbsorption - cp MEWTWO2 - jr z, .EnergyAbsorption - cp EXEGGUTOR - jr z, .Teleport - cp ELECTRODE1 - jr z, .EnergySpike - ; fallthrough - -.no_carry - or a - ret - -.DevolutionBeam -; in case selected attack is Devolution Beam -; store in hTempPlayAreaLocation_ffa1 -; the location of card to select to devolve - ld a, [wSelectedAttack] - or a - jp z, .no_carry ; can be jr - - ld a, $01 - ldh [hTemp_ffa0], a - call LookForCardThatIsKnockedOutOnDevolution - ldh [hTempPlayAreaLocation_ffa1], a - -.set_carry_1 - scf - ret - -.EnergyAbsorption -; in case selected attack is Energy Absorption -; make list from energy cards in Discard Pile - ld a, [wSelectedAttack] - or a - jp nz, .no_carry ; can be jr - - ld a, $ff - ldh [hTempPlayAreaLocation_ffa1], a - ldh [hTempRetreatCostCards], a - -; search for Psychic energy cards in Discard Pile - ld e, PSYCHIC_ENERGY - ld a, CARD_LOCATION_DISCARD_PILE - call CheckIfAnyCardIDinLocation - ldh [hTemp_ffa0], a - farcall CreateEnergyCardListFromDiscardPile_AllEnergy - -; find any energy card different from -; the one found by CheckIfAnyCardIDinLocation. -; since using this attack requires a Psychic energy card, -; and another one is in hTemp_ffa0, -; then any other energy card would account -; for the Energy Cost of Psyburn. - ld hl, wDuelTempList -.loop_energy_cards - ld a, [hli] - cp $ff - jr z, .set_carry_2 - ld b, a - ldh a, [hTemp_ffa0] - cp b - jr z, .loop_energy_cards ; same card, keep looking - -; store the deck index of energy card found - ld a, b - ldh [hTempPlayAreaLocation_ffa1], a - ; fallthrough - -.set_carry_2 - scf - ret - -.Teleport -; in case selected attack is Teleport -; decide Bench card to switch to. - ld a, [wSelectedAttack] - or a - jp nz, .no_carry ; can be jr - call AIDecideBenchPokemonToSwitchTo - jr c, .no_carry - ldh [hTemp_ffa0], a - scf - ret - -.EnergySpike -; in case selected attack is Energy Spike -; decide basic energy card to fetch from Deck. - ld a, [wSelectedAttack] - or a - jp z, .no_carry ; can be jr - - ld a, CARD_LOCATION_DECK - ld e, LIGHTNING_ENERGY - -; if none were found in Deck, return carry... - call CheckIfAnyCardIDinLocation - ldh [hTemp_ffa0], a - jp nc, .no_carry ; can be jr - -; ...else find a suitable Play Area Pokemon to -; attach the energy card to. - call AIProcessButDontPlayEnergy_SkipEvolution - jp nc, .no_carry ; can be jr - ldh a, [hTempPlayAreaLocation_ff9d] - ldh [hTempPlayAreaLocation_ffa1], a - scf - ret - -; return carry if Pokémon at play area location -; in hTempPlayAreaLocation_ff9d does not have -; energy required for the attack index in wSelectedAttack -; or has exactly the same amount of energy needed -; input: -; [hTempPlayAreaLocation_ff9d] = play area location -; [wSelectedAttack] = attack index to check -; output: -; a = number of extra energy cards attached -CheckIfNoSurplusEnergyForAttack: ; 171fb (5:71fb) - ldh a, [hTempPlayAreaLocation_ff9d] - add DUELVARS_ARENA_CARD - call GetTurnDuelistVariable - ld d, a - ld a, [wSelectedAttack] - ld e, a - call CopyAttackDataAndDamage_FromDeckIndex - ld hl, wLoadedAttackName - ld a, [hli] - or [hl] - jr z, .not_attack - ld a, [wLoadedAttackCategory] - cp POKEMON_POWER - jr nz, .is_attack -.not_attack - scf - ret - -.is_attack - ldh a, [hTempPlayAreaLocation_ff9d] - ld e, a - call GetPlayAreaCardAttachedEnergies - bank1call HandleEnergyBurn - xor a - ld [wTempLoadedAttackEnergyCost], a - ld [wTempLoadedAttackEnergyNeededAmount], a - ld [wTempLoadedAttackEnergyNeededType], a - ld hl, wAttachedEnergies - ld de, wLoadedAttackEnergyCost - ld b, 0 - ld c, (NUM_TYPES / 2) - 1 -.loop - ; check all basic energy cards except colorless - ld a, [de] - swap a - call CalculateParticularAttachedEnergyNeeded - ld a, [de] - call CalculateParticularAttachedEnergyNeeded - inc de - dec c - jr nz, .loop - - ; colorless - ld a, [de] - swap a - and %00001111 - ld b, a - ld hl, wTempLoadedAttackEnergyCost - ld a, [wTotalAttachedEnergies] - sub [hl] - sub b - ret c ; return if not enough energy - - or a - ret nz ; return if surplus energy - - ; exactly the amount of energy needed - scf - ret - -; takes as input the energy cost of an attack for a -; particular energy, stored in the lower nibble of a -; if the attack costs some amount of this energy, the lower nibble of a != 0, -; and this amount is stored in wTempLoadedAttackEnergyCost -; also adds the amount of energy still needed -; to wTempLoadedAttackEnergyNeededAmount -; input: -; a = this energy cost of attack (lower nibble) -; [hl] = attached energy -; output: -; carry set if not enough of this energy type attached -CalculateParticularAttachedEnergyNeeded: ; 17258 (5:7258) - and %00001111 - jr nz, .check -.done - inc hl - inc b - ret - -.check - ld [wTempLoadedAttackEnergyCost], a - sub [hl] - jr z, .done - jr nc, .done - push bc - ld a, [wTempLoadedAttackEnergyCost] - ld b, a - ld a, [hl] - sub b - pop bc - ld [wTempLoadedAttackEnergyNeededAmount], a - jr .done - -; return carry if there is a card that -; can evolve a Pokémon in hand or deck. -; input: -; a = deck index of card to check; -; output: -; a = deck index of evolution in hand, if found; -; carry set if there's a card in hand that can evolve. -CheckCardEvolutionInHandOrDeck: ; 17274 (5:7274) - ld b, a - ld a, DUELVARS_ARENA_CARD - call GetTurnDuelistVariable - push af - ld [hl], b - ld e, 0 - -.loop - ld a, DUELVARS_CARD_LOCATIONS - add e - call GetTurnDuelistVariable - cp CARD_LOCATION_DECK - jr z, .deck_or_hand - cp CARD_LOCATION_HAND - jr nz, .next -.deck_or_hand - push de - ld d, e - ld e, PLAY_AREA_ARENA - call CheckIfCanEvolveInto - pop de - jr nc, .set_carry -.next - inc e - ld a, DECK_SIZE - cp e - jr nz, .loop - - ld a, DUELVARS_ARENA_CARD - call GetTurnDuelistVariable - pop af - ld [hl], a - or a - ret - -.set_carry - ld a, DUELVARS_ARENA_CARD - call GetTurnDuelistVariable - pop af - ld [hl], a - ld a, e - scf - ret - -INCLUDE "engine/ai/boss_deck_set_up.asm" - -; returns carry if Pokemon at PLAY_AREA* in a -; can damage defending Pokémon with any of its attacks -; input: -; a = location of card to check -CheckIfCanDamageDefendingPokemon: ; 17383 (5:7383) - ldh [hTempPlayAreaLocation_ff9d], a - xor a ; first attack - ld [wSelectedAttack], a - call CheckIfSelectedAttackIsUnusable - jr c, .second_attack - xor a - call EstimateDamage_VersusDefendingCard - ld a, [wDamage] - or a - jr nz, .set_carry - -.second_attack - ld a, SECOND_ATTACK - ld [wSelectedAttack], a - call CheckIfSelectedAttackIsUnusable - jr c, .no_carry - ld a, $01 - call EstimateDamage_VersusDefendingCard - ld a, [wDamage] - or a - jr nz, .set_carry - -.no_carry - or a - ret -.set_carry - scf - ret - -; checks if defending Pokémon can knock out -; card at hTempPlayAreaLocation_ff9d with any of its attacks -; and if so, stores the damage to wce00 and wce01 -; sets carry if any on the attacks knocks out -; also outputs the largest damage dealt in a -; input: -; [hTempPlayAreaLocation_ff9d] = location of card to check -; output: -; a = largest damage of both attacks -; carry set if can knock out -CheckIfDefendingPokemonCanKnockOut: ; 173b1 (5:73b1) - xor a ; first attack - ld [wce00], a - ld [wce01], a - call CheckIfDefendingPokemonCanKnockOutWithAttack - jr nc, .second_attack - ld a, [wDamage] - ld [wce00], a - -.second_attack - ld a, SECOND_ATTACK - call CheckIfDefendingPokemonCanKnockOutWithAttack - jr nc, .return_if_neither_kos - ld a, [wDamage] - ld [wce01], a - jr .compare - -.return_if_neither_kos - ld a, [wce00] - or a - ret z - -.compare - ld a, [wce00] - ld b, a - ld a, [wce01] - cp b - jr nc, .set_carry - ld a, b -.set_carry - scf - ret - -; return carry if defending Pokémon can knock out -; card at hTempPlayAreaLocation_ff9d -; input: -; a = attack index -; [hTempPlayAreaLocation_ff9d] = location of card to check -CheckIfDefendingPokemonCanKnockOutWithAttack: ; 173e4 (5:73e4) - ld [wSelectedAttack], a - ldh a, [hTempPlayAreaLocation_ff9d] - push af - xor a - ldh [hTempPlayAreaLocation_ff9d], a - call SwapTurn - call CheckIfSelectedAttackIsUnusable - call SwapTurn - pop bc - ld a, b - ldh [hTempPlayAreaLocation_ff9d], a - jr c, .done - -; player's active Pokémon can use attack - ld a, [wSelectedAttack] - call EstimateDamage_FromDefendingPokemon - ldh a, [hTempPlayAreaLocation_ff9d] - add DUELVARS_ARENA_CARD_HP - call GetTurnDuelistVariable - ld hl, wDamage - sub [hl] - jr z, .set_carry - ret - -.set_carry - scf - ret - -.done - or a - ret - -; sets carry if Opponent's deck ID -; is between LEGENDARY_MOLTRES_DECK_ID (inclusive) -; and MUSCLES_FOR_BRAINS_DECK_ID (exclusive) -; these are the decks for Grandmaster/Club Master/Ronald -CheckIfOpponentHasBossDeckID: ; 17414 (5:7414) - push af - ld a, [wOpponentDeckID] - cp LEGENDARY_MOLTRES_DECK_ID - jr c, .no_carry - cp MUSCLES_FOR_BRAINS_DECK_ID - jr nc, .no_carry - pop af - scf - ret - -.no_carry - pop af - or a - ret - -; sets carry if not a boss fight -; and if hasn't received legendary cards yet -CheckIfNotABossDeckID: ; 17426 (5:7426) - call EnableSRAM - ld a, [sReceivedLegendaryCards] - call DisableSRAM - or a - jr nz, .no_carry - call CheckIfOpponentHasBossDeckID - jr nc, .set_carry -.no_carry - or a - ret - -.set_carry - scf - ret - -; probability to return carry: -; - 50% if deck AI is playing is on the list; -; - 25% for all other decks; -; - 0% for boss decks. -; used for certain decks to randomly choose -; not to play Trainer card or use PKMN Power -AIChooseRandomlyNotToDoAction: ; 1743b (5:743b) -; boss decks always use Trainer cards. - push hl - push de - call CheckIfNotABossDeckID - jr c, .check_deck - pop de - pop hl - ret - -.check_deck - ld a, [wOpponentDeckID] - cp MUSCLES_FOR_BRAINS_DECK_ID - jr z, .carry_50_percent - cp BLISTERING_POKEMON_DECK_ID - jr z, .carry_50_percent - cp WATERFRONT_POKEMON_DECK_ID - jr z, .carry_50_percent - cp BOOM_BOOM_SELFDESTRUCT_DECK_ID - jr z, .carry_50_percent - cp KALEIDOSCOPE_DECK_ID - jr z, .carry_50_percent - cp RESHUFFLE_DECK_ID - jr z, .carry_50_percent - -; carry 25 percent - ld a, 4 - call Random - cp 1 - pop de - pop hl - ret - -.carry_50_percent - ld a, 4 - call Random - cp 2 - pop de - pop hl - ret - -; checks if any bench Pokémon has same ID -; as input, and sets carry if it has more than -; half health and can use its second attack -; input: -; a = card ID to check for -; output: -; carry set if the above requirements are met -CheckForBenchIDAtHalfHPAndCanUseSecondAttack: ; 17474 (5:7474) - ld [wcdf9], a - ldh a, [hTempPlayAreaLocation_ff9d] - ld d, a - ld a, [wSelectedAttack] - ld e, a - push de - ld a, DUELVARS_ARENA_CARD - call GetTurnDuelistVariable - lb bc, 0, PLAY_AREA_ARENA - push hl - -.loop - inc c - pop hl - ld a, [hli] - push hl - cp $ff - jr z, .done - ld d, a - push de - push bc - call LoadCardDataToBuffer1_FromDeckIndex - pop bc - ld a, c - add DUELVARS_ARENA_CARD_HP - call GetTurnDuelistVariable - ld d, a - ld a, [wLoadedCard1HP] - rrca - cp d - pop de - jr nc, .loop - ; half max HP < current HP - ld a, [wLoadedCard1ID] - ld hl, wcdf9 - cp [hl] - jr nz, .loop - - ld a, c - ldh [hTempPlayAreaLocation_ff9d], a - ld a, SECOND_ATTACK - ld [wSelectedAttack], a - push bc - call CheckIfSelectedAttackIsUnusable - pop bc - jr c, .loop - inc b -.done - pop hl - pop de - ld a, e - ld [wSelectedAttack], a - ld a, d - ldh [hTempPlayAreaLocation_ff9d], a - ld a, b - or a - ret z - scf - ret - -; add 5 to wPlayAreaEnergyAIScore AI score corresponding to all cards -; in bench that have same ID as register a -; input: -; a = card ID to look for -RaiseAIScoreToAllMatchingIDsInBench: ; 174cd (5:74cd) - ld d, a - ld a, DUELVARS_BENCH - call GetTurnDuelistVariable - ld e, 0 -.loop - inc e - ld a, [hli] - cp $ff - ret z - push de - call GetCardIDFromDeckIndex - ld a, e - pop de - cp d - jr nz, .loop - ld c, e - ld b, $00 - push hl - ld hl, wPlayAreaEnergyAIScore - add hl, bc - ld a, 5 - add [hl] - ld [hl], a - pop hl - jr .loop - -; goes through each play area Pokémon, and -; for all cards of the same ID, determine which -; card has highest value calculated from Func_17583 -; the card with highest value gets increased wPlayAreaEnergyAIScore -; while all others get decreased wPlayAreaEnergyAIScore -Func_174f2: ; 174f2 (5:74f2) - ld a, MAX_PLAY_AREA_POKEMON - ld hl, wcdfa - call ClearMemory_Bank5 - ld a, DUELVARS_BENCH - call GetTurnDuelistVariable - ld e, 0 - -.loop_play_area - push hl - ld a, MAX_PLAY_AREA_POKEMON - ld hl, wcdea - call ClearMemory_Bank5 - pop hl - inc e - ld a, [hli] - cp $ff - ret z - - ld [wcdf9], a - push de - push hl - -; checks wcdfa + play area location in e -; if != 0, go to next in play area - ld d, $00 - ld hl, wcdfa - add hl, de - ld a, [hl] - or a - pop hl - pop de - jr nz, .loop_play_area - -; loads wcdf9 with card ID -; and call Func_17583 - push de - ld a, [wcdf9] - call GetCardIDFromDeckIndex - ld a, e - ld [wcdf9], a - pop de - push hl - push de - call Func_17583 - -; check play area Pokémon ahead -; if there is a card with the same ID, -; call Func_17583 for it as well -.loop_1 - inc e - ld a, [hli] - cp $ff - jr z, .check_if_repeated_id - push de - call GetCardIDFromDeckIndex - ld a, [wcdf9] - cp e - pop de - jr nz, .loop_1 - call Func_17583 - jr .loop_1 - -; if there are more than 1 of the same ID -; in play area, iterate bench backwards -; and determines which card has highest -; score in wcdea -.check_if_repeated_id - call Func_175a8 - jr c, .next - lb bc, 0, 0 - ld hl, wcdea + MAX_BENCH_POKEMON - ld d, MAX_PLAY_AREA_POKEMON -.loop_2 - dec d - jr z, .asm_17560 - ld a, [hld] - cp b - jr c, .loop_2 - ld b, a - ld c, d - jr .loop_2 - -; c = play area location of highest score -; decrease wPlayAreaEnergyAIScore score for all cards with same ID -; except for the one with highest score -; increase wPlayAreaEnergyAIScore score for card with highest ID -.asm_17560 - ld hl, wPlayAreaEnergyAIScore - ld de, wcdea - ld b, PLAY_AREA_ARENA -.loop_3 - ld a, c - cp b - jr z, .card_with_highest - ld a, [de] - or a - jr z, .check_next -; decrease score - dec [hl] - jr .check_next -.card_with_highest -; increase score - inc [hl] -.check_next - inc b - ld a, MAX_PLAY_AREA_POKEMON - cp b - jr z, .next - inc de - inc hl - jr .loop_3 - -.next - pop de - pop hl - jp .loop_play_area - -; loads wcdea + play area location in e -; with energy * 2 + $80 - floor(dam / 10) -; loads wcdfa + play area location in e -; with $01 -Func_17583: ; 17583 (5:7583) - push hl - push de - call GetCardDamageAndMaxHP - call CalculateByteTensDigit - ld b, a - push bc - call CountNumberOfEnergyCardsAttached - pop bc - sla a - add $80 - sub b - pop de - push de - ld d, $00 - ld hl, wcdea - add hl, de - ld [hl], a - ld hl, wcdfa - add hl, de - ld [hl], $01 - pop de - pop hl - ret - -; counts how many play area locations in wcdea -; are != 0, and outputs result in a -; also returns carry if result is < 2 -Func_175a8: ; 175a8 (5:75a8) - ld hl, wcdea - ld d, $00 - ld e, MAX_PLAY_AREA_POKEMON + 1 -.loop - dec e - jr z, .done - ld a, [hli] - or a - jr z, .loop - inc d - jr .loop -.done - ld a, d - cp 2 - ret - -; handle how AI scores giving out Energy Cards -; when using Legendary Articuno deck -HandleLegendaryArticunoEnergyScoring: ; 175bd (5:75bd) - ld a, [wOpponentDeckID] - cp LEGENDARY_ARTICUNO_DECK_ID - jr z, .articuno_deck - ret -.articuno_deck - call ScoreLegendaryArticunoCards - ret diff --git a/src/engine/ai/damage_calculation.asm b/src/engine/ai/damage_calculation.asm deleted file mode 100644 index 97c24b6..0000000 --- a/src/engine/ai/damage_calculation.asm +++ /dev/null @@ -1,450 +0,0 @@ -; stores in wDamage, wAIMinDamage and wAIMaxDamage the calculated damage -; done to the defending Pokémon by a given card and attack -; input: -; a = attack index to take into account -; [hTempPlayAreaLocation_ff9d] = location of attacking card to consider -EstimateDamage_VersusDefendingCard: ; 143e5 (5:43e5) - ld [wSelectedAttack], a - ld e, a - ldh a, [hTempPlayAreaLocation_ff9d] - add DUELVARS_ARENA_CARD - call GetTurnDuelistVariable - ld d, a - call CopyAttackDataAndDamage_FromDeckIndex - ld a, [wLoadedAttackCategory] - cp POKEMON_POWER - jr nz, .is_attack - -; is a Pokémon Power -; set wDamage, wAIMinDamage and wAIMaxDamage to zero - ld hl, wDamage - xor a - ld [hli], a - ld [hl], a - ld [wAIMinDamage], a - ld [wAIMaxDamage], a - ld e, a - ld d, a - ret - -.is_attack -; set wAIMinDamage and wAIMaxDamage to damage of attack -; these values take into account the range of damage -; that the attack can span (e.g. min and max number of hits) - ld a, [wDamage] - ld [wAIMinDamage], a - ld [wAIMaxDamage], a - ld a, EFFECTCMDTYPE_AI - call TryExecuteEffectCommandFunction - ld a, [wAIMinDamage] - ld hl, wAIMaxDamage - or [hl] - jr nz, .calculation - ld a, [wDamage] - ld [wAIMinDamage], a - ld [wAIMaxDamage], a - -.calculation -; if temp. location is active, damage calculation can be done directly... - ldh a, [hTempPlayAreaLocation_ff9d] - or a - jr z, CalculateDamage_VersusDefendingPokemon - -; ...otherwise substatuses need to be temporarily reset to account -; for the switching, to obtain the right damage calculation... - ; reset substatus1 - ld a, DUELVARS_ARENA_CARD_SUBSTATUS1 - call GetTurnDuelistVariable - push af - push hl - ld [hl], $00 - ; reset substatus2 - ld l, DUELVARS_ARENA_CARD_SUBSTATUS2 - ld a, [hl] - push af - push hl - ld [hl], $00 - ; reset changed resistance - ld l, DUELVARS_ARENA_CARD_CHANGED_RESISTANCE - ld a, [hl] - push af - push hl - ld [hl], $00 - call CalculateDamage_VersusDefendingPokemon -; ...and subsequently recovered to continue the duel normally - pop hl - pop af - ld [hl], a - pop hl - pop af - ld [hl], a - pop hl - pop af - ld [hl], a - ret - -; calculates the damage that will be dealt to the player's active card -; using the card that is located in hTempPlayAreaLocation_ff9d -; taking into account weakness/resistance/pluspowers/defenders/etc -; and outputs the result capped at a max of $ff -; input: -; [wAIMinDamage] = base damage -; [wAIMaxDamage] = base damage -; [wDamage] = base damage -; [hTempPlayAreaLocation_ff9d] = turn holder's card location as the attacker -CalculateDamage_VersusDefendingPokemon: ; 14453 (5:4453) - ld hl, wAIMinDamage - call _CalculateDamage_VersusDefendingPokemon - ld hl, wAIMaxDamage - call _CalculateDamage_VersusDefendingPokemon - ld hl, wDamage -; fallthrough - -_CalculateDamage_VersusDefendingPokemon: ; 14462 (5:4462) - ld e, [hl] - ld d, $00 - push hl - - ; load this card's data - ldh a, [hTempPlayAreaLocation_ff9d] - add DUELVARS_ARENA_CARD - call GetTurnDuelistVariable - call LoadCardDataToBuffer2_FromDeckIndex - ld a, [wLoadedCard2ID] - ld [wTempTurnDuelistCardID], a - - ; load player's arena card data - call SwapTurn - ld a, DUELVARS_ARENA_CARD - call GetTurnDuelistVariable - call LoadCardDataToBuffer2_FromDeckIndex - ld a, [wLoadedCard2ID] - ld [wTempNonTurnDuelistCardID], a - call SwapTurn - - push de - call HandleNoDamageOrEffectSubstatus - pop de - jr nc, .vulnerable - ; invulnerable to damage - ld de, $0 - jr .done -.vulnerable - ldh a, [hTempPlayAreaLocation_ff9d] - or a - call z, HandleDoubleDamageSubstatus - ; skips the weak/res checks if unaffected. - bit UNAFFECTED_BY_WEAKNESS_RESISTANCE_F, d - res UNAFFECTED_BY_WEAKNESS_RESISTANCE_F, d - jr nz, .not_resistant - -; handle weakness - ldh a, [hTempPlayAreaLocation_ff9d] - call GetPlayAreaCardColor - call TranslateColorToWR - ld b, a - call SwapTurn - call GetArenaCardWeakness - call SwapTurn - and b - jr z, .not_weak - ; double de - sla e - rl d - -.not_weak -; handle resistance - call SwapTurn - call GetArenaCardResistance - call SwapTurn - and b - jr z, .not_resistant - ld hl, -30 - add hl, de - ld e, l - ld d, h - -.not_resistant - ; apply pluspower and defender boosts - ldh a, [hTempPlayAreaLocation_ff9d] - add CARD_LOCATION_ARENA - ld b, a - call ApplyAttachedPluspower - call SwapTurn - ld b, CARD_LOCATION_ARENA - call ApplyAttachedDefender - call HandleDamageReduction - ; test if de underflowed - bit 7, d - jr z, .no_underflow - ld de, $0 - -.no_underflow - ld a, DUELVARS_ARENA_CARD_STATUS - call GetTurnDuelistVariable - and DOUBLE_POISONED - jr z, .not_poisoned - ld c, 20 - and DOUBLE_POISONED & (POISONED ^ $ff) - jr nz, .add_poison - ld c, 10 -.add_poison - ld a, c - add e - ld e, a - ld a, $00 - adc d - ld d, a -.not_poisoned - call SwapTurn - -.done - pop hl - ld [hl], e - ld a, d - or a - ret z - ; cap damage - ld a, $ff - ld [hl], a - ret - -; stores in wDamage, wAIMinDamage and wAIMaxDamage the calculated damage -; done to the Pokémon at hTempPlayAreaLocation_ff9d -; by the defending Pokémon, using the attack index at a -; input: -; a = attack index -; [hTempPlayAreaLocation_ff9d] = location of card to calculate -; damage as the receiver -EstimateDamage_FromDefendingPokemon: ; 1450b (5:450b) - call SwapTurn - ld [wSelectedAttack], a - ld e, a - ld a, DUELVARS_ARENA_CARD - call GetTurnDuelistVariable - ld d, a - call CopyAttackDataAndDamage_FromDeckIndex - call SwapTurn - ld a, [wLoadedAttackCategory] - cp POKEMON_POWER - jr nz, .is_attack - -; is a Pokémon Power -; set wDamage, wAIMinDamage and wAIMaxDamage to zero - ld hl, wDamage - xor a - ld [hli], a - ld [hl], a - ld [wAIMinDamage], a - ld [wAIMaxDamage], a - ld e, a - ld d, a - ret - -.is_attack -; set wAIMinDamage and wAIMaxDamage to damage of attack -; these values take into account the range of damage -; that the attack can span (e.g. min and max number of hits) - ld a, [wDamage] - ld [wAIMinDamage], a - ld [wAIMaxDamage], a - call SwapTurn - ldh a, [hTempPlayAreaLocation_ff9d] - push af - xor a - ldh [hTempPlayAreaLocation_ff9d], a - ld a, EFFECTCMDTYPE_AI - call TryExecuteEffectCommandFunction - pop af - ldh [hTempPlayAreaLocation_ff9d], a - call SwapTurn - ld a, [wAIMinDamage] - ld hl, wAIMaxDamage - or [hl] - jr nz, .calculation - ld a, [wDamage] - ld [wAIMinDamage], a - ld [wAIMaxDamage], a - -.calculation -; if temp. location is active, damage calculation can be done directly... - ldh a, [hTempPlayAreaLocation_ff9d] - or a - jr z, CalculateDamage_FromDefendingPokemon - -; ...otherwise substatuses need to be temporarily reset to account -; for the switching, to obtain the right damage calculation... - ld a, DUELVARS_ARENA_CARD_SUBSTATUS1 - call GetTurnDuelistVariable - push af - push hl - ld [hl], $00 - ; reset substatus2 - ld l, DUELVARS_ARENA_CARD_SUBSTATUS2 - ld a, [hl] - push af - push hl - ld [hl], $00 - ; reset changed resistance - ld l, DUELVARS_ARENA_CARD_CHANGED_RESISTANCE - ld a, [hl] - push af - push hl - ld [hl], $00 - call CalculateDamage_FromDefendingPokemon -; ...and subsequently recovered to continue the duel normally - pop hl - pop af - ld [hl], a - pop hl - pop af - ld [hl], a - pop hl - pop af - ld [hl], a - ret - -; similar to CalculateDamage_VersusDefendingPokemon but reversed, -; calculating damage of the defending Pokémon versus -; the card located in hTempPlayAreaLocation_ff9d -; taking into account weakness/resistance/pluspowers/defenders/etc -; and poison damage for two turns -; and outputs the result capped at a max of $ff -; input: -; [wAIMinDamage] = base damage -; [wAIMaxDamage] = base damage -; [wDamage] = base damage -; [hTempPlayAreaLocation_ff9d] = location of card to calculate -; damage as the receiver -CalculateDamage_FromDefendingPokemon: ; 1458c (5:458c) - ld hl, wAIMinDamage - call .CalculateDamage - ld hl, wAIMaxDamage - call .CalculateDamage - ld hl, wDamage - ; fallthrough - -.CalculateDamage ; 1459b (5:459b) - ld e, [hl] - ld d, $00 - push hl - - ; load player active card's data - call SwapTurn - ld a, DUELVARS_ARENA_CARD - call GetTurnDuelistVariable - call LoadCardDataToBuffer2_FromDeckIndex - ld a, [wLoadedCard2ID] - ld [wTempTurnDuelistCardID], a - call SwapTurn - - ; load opponent's card data - ldh a, [hTempPlayAreaLocation_ff9d] - add DUELVARS_ARENA_CARD - call GetTurnDuelistVariable - call LoadCardDataToBuffer2_FromDeckIndex - ld a, [wLoadedCard2ID] - ld [wTempNonTurnDuelistCardID], a - - call SwapTurn - call HandleDoubleDamageSubstatus - bit UNAFFECTED_BY_WEAKNESS_RESISTANCE_F, d - res UNAFFECTED_BY_WEAKNESS_RESISTANCE_F, d - jr nz, .not_resistant - -; handle weakness - call GetArenaCardColor - call TranslateColorToWR - ld b, a - call SwapTurn - ldh a, [hTempPlayAreaLocation_ff9d] - or a - jr nz, .bench_weak - ld a, DUELVARS_ARENA_CARD_CHANGED_WEAKNESS - call GetTurnDuelistVariable - or a - jr nz, .unchanged_weak - -.bench_weak - ldh a, [hTempPlayAreaLocation_ff9d] - add DUELVARS_ARENA_CARD - call GetTurnDuelistVariable - call LoadCardDataToBuffer2_FromDeckIndex - ld a, [wLoadedCard2Weakness] -.unchanged_weak - and b - jr z, .not_weak - ; double de - sla e - rl d - -.not_weak -; handle resistance - ldh a, [hTempPlayAreaLocation_ff9d] - or a - jr nz, .bench_res - ld a, DUELVARS_ARENA_CARD_CHANGED_RESISTANCE - call GetTurnDuelistVariable - or a - jr nz, .unchanged_res - -.bench_res - ldh a, [hTempPlayAreaLocation_ff9d] - add DUELVARS_ARENA_CARD - call GetTurnDuelistVariable - call LoadCardDataToBuffer2_FromDeckIndex - ld a, [wLoadedCard2Resistance] -.unchanged_res - and b - jr z, .not_resistant - ld hl, -30 - add hl, de - ld e, l - ld d, h - -.not_resistant - ; apply pluspower and defender boosts - call SwapTurn - ld b, CARD_LOCATION_ARENA - call ApplyAttachedPluspower - call SwapTurn - ldh a, [hTempPlayAreaLocation_ff9d] - add CARD_LOCATION_ARENA - ld b, a - call ApplyAttachedDefender - ldh a, [hTempPlayAreaLocation_ff9d] - or a - call z, HandleDamageReduction - bit 7, d - jr z, .no_underflow - ld de, $0 - -.no_underflow - ldh a, [hTempPlayAreaLocation_ff9d] - or a - jr nz, .done - ld a, DUELVARS_ARENA_CARD_STATUS - call GetTurnDuelistVariable - and DOUBLE_POISONED - jr z, .done - ld c, 40 - and DOUBLE_POISONED & (POISONED ^ $ff) - jr nz, .add_poison - ld c, 20 -.add_poison - ld a, c - add e - ld e, a - ld a, $00 - adc d - ld d, a - -.done - pop hl - ld [hl], e - ld a, d - or a - ret z - ld a, $ff - ld [hl], a - ret diff --git a/src/engine/ai/deck_ai.asm b/src/engine/ai/deck_ai.asm deleted file mode 100644 index c330418..0000000 --- a/src/engine/ai/deck_ai.asm +++ /dev/null @@ -1,82 +0,0 @@ -; AI card retreat score bonus -; when the AI retreat routine runs through the Bench to choose -; a Pokemon to switch to, it looks up in this list and if -; a card ID matches, applies a retreat score bonus to this card. -; positive (negative) means more (less) likely to switch to this card. -ai_retreat: MACRO - db \1 ; card ID - db $80 + \2 ; retreat score (ranges between -128 and 127) -ENDM - -; AI card energy attach score bonus -; when the AI energy attachment routine runs through the Play Area to choose -; a Pokemon to attach an energy card, it looks up in this list and if -; a card ID matches, skips this card if the maximum number of energy -; cards attached has been reached. If it hasn't been reached, additionally -; applies a positive (or negative) AI score to attach energy to this card. -ai_energy: MACRO - db \1 ; card ID - db \2 ; maximum number of attached cards - db $80 + \3 ; energy score (ranges between -128 and 127) -ENDM - -; stores in WRAM pointer to data in argument -; e.g. store_list_pointer wSomeListPointer, SomeData -store_list_pointer: MACRO - ld hl, \1 - ld de, \2 - ld [hl], e - inc hl - ld [hl], d -ENDM - -; deck AIs are specialized to work on a given deck ID. -; they decide what happens during a turn, what Pokemon cards -; to pick during the start of the duel, etc. -; the different scenarios these are used are listed in AIACTION_* constants. -; each of these have a pointer table with the following structure: -; dw .do_turn : never called; -; -; dw .do_turn : called to handle the main turn logic, from the beginning -; of the turn up to the attack (or lack thereof); -; -; dw .start_duel : called at the start of the duel to initialize some -; variables and optionally set up CPU hand and deck; -; -; dw .forced_switch : logic to determine what Pokemon to pick when there's -; an effect that forces AI to switch to Bench card; -; -; dw .ko_switch : logic for picking which card to use after a KO; -; -; dw .take_prize : logic to decide which prize card to pick. - -; optionally, decks can also declare card lists that will add -; more specialized logic during various generic AI routines, -; and read during the .start_duel routines. -; the pointers to these lists are stored in memory: -; wAICardListAvoidPrize : list of cards to avoid being placed as prize; -; wAICardListArenaPriority : priority list of Arena card at duel start; -; wAICardListBenchPriority : priority list of Bench cards at duel start; -; wAICardListPlayFromHandPriority : priority list of cards to play from hand; -; wAICardListRetreatBonus : scores given to certain cards for retreat; -; wAICardListEnergyBonus : max number of energy cards and card scores. - -INCLUDE "engine/ai/decks/general.asm" -INCLUDE "engine/ai/decks/sams_practice.asm" -INCLUDE "engine/ai/decks/general_no_retreat.asm" -INCLUDE "engine/ai/decks/legendary_moltres.asm" -INCLUDE "engine/ai/decks/legendary_zapdos.asm" -INCLUDE "engine/ai/decks/legendary_articuno.asm" -INCLUDE "engine/ai/decks/legendary_dragonite.asm" -INCLUDE "engine/ai/decks/first_strike.asm" -INCLUDE "engine/ai/decks/rock_crusher.asm" -INCLUDE "engine/ai/decks/go_go_rain_dance.asm" -INCLUDE "engine/ai/decks/zapping_selfdestruct.asm" -INCLUDE "engine/ai/decks/flower_power.asm" -INCLUDE "engine/ai/decks/strange_psyshock.asm" -INCLUDE "engine/ai/decks/wonders_of_science.asm" -INCLUDE "engine/ai/decks/fire_charge.asm" -INCLUDE "engine/ai/decks/im_ronald.asm" -INCLUDE "engine/ai/decks/powerful_ronald.asm" -INCLUDE "engine/ai/decks/invincible_ronald.asm" -INCLUDE "engine/ai/decks/legendary_ronald.asm" diff --git a/src/engine/ai/decks/fire_charge.asm b/src/engine/ai/decks/fire_charge.asm deleted file mode 100644 index f5b347b..0000000 --- a/src/engine/ai/decks/fire_charge.asm +++ /dev/null @@ -1,80 +0,0 @@ -AIActionTable_FireCharge: ; 15232 (5:5232) - dw .do_turn ; unused - dw .do_turn - dw .start_duel - dw .forced_switch - dw .ko_switch - dw .take_prize - -.do_turn ; 1523e (5:523e) - call AIMainTurnLogic - ret - -.start_duel ; 15242 (5:5242) - call InitAIDuelVars - call .store_list_pointers - call SetUpBossStartingHandAndDeck - call TrySetUpBossStartingPlayArea - ret nc - call AIPlayInitialBasicCards - ret - -.forced_switch ; 15253 (5:5253) - call AIDecideBenchPokemonToSwitchTo - ret - -.ko_switch ; 15257 (5:5257) - call AIDecideBenchPokemonToSwitchTo - ret - -.take_prize ; 1525b (5:525b) - call AIPickPrizeCards - ret - -.list_arena ; 1525f (5:525f) - db JIGGLYPUFF3 - db CHANSEY - db TAUROS - db MAGMAR1 - db JIGGLYPUFF1 - db GROWLITHE - db $00 - -.list_bench ; 15266 (5:5266) - db JIGGLYPUFF3 - db CHANSEY - db GROWLITHE - db MAGMAR1 - db JIGGLYPUFF1 - db TAUROS - db $00 - -.list_retreat ; 1526e (5:526e) - ai_retreat JIGGLYPUFF1, -1 - ai_retreat CHANSEY, -1 - ai_retreat GROWLITHE, -1 - db $00 - -.list_energy ; 15274 (5:5274) - ai_energy GROWLITHE, 3, +0 - ai_energy ARCANINE2, 4, +0 - ai_energy MAGMAR1, 3, +0 - ai_energy JIGGLYPUFF1, 3, +0 - ai_energy JIGGLYPUFF3, 2, +0 - ai_energy WIGGLYTUFF, 3, +0 - ai_energy CHANSEY, 4, +0 - ai_energy TAUROS, 3, +0 - db $00 - -.list_prize ; 1528d (5:528d) - db GAMBLER - db $00 - -.store_list_pointers ; 1528f (5:528f) - store_list_pointer wAICardListAvoidPrize, .list_prize - store_list_pointer wAICardListArenaPriority, .list_arena - store_list_pointer wAICardListBenchPriority, .list_bench - store_list_pointer wAICardListPlayFromHandPriority, .list_bench - ; missing store_list_pointer wAICardListRetreatBonus, .list_retreat - store_list_pointer wAICardListEnergyBonus, .list_energy - ret diff --git a/src/engine/ai/decks/first_strike.asm b/src/engine/ai/decks/first_strike.asm deleted file mode 100644 index 2e636e1..0000000 --- a/src/engine/ai/decks/first_strike.asm +++ /dev/null @@ -1,76 +0,0 @@ -AIActionTable_FirstStrike: ; 14e89 (5:4e89) - dw .do_turn ; unused - dw .do_turn - dw .start_duel - dw .forced_switch - dw .ko_switch - dw .take_prize - -.do_turn ; 14e95 (5:4e95) - call AIMainTurnLogic - ret - -.start_duel ; 14e99 (5:4e99) - call InitAIDuelVars - call .store_list_pointers - call SetUpBossStartingHandAndDeck - call TrySetUpBossStartingPlayArea - ret nc - call AIPlayInitialBasicCards - ret - -.forced_switch ; 14eaa (5:4eaa) - call AIDecideBenchPokemonToSwitchTo - ret - -.ko_switch ; 14eae (5:4eae) - call AIDecideBenchPokemonToSwitchTo - ret - -.take_prize ; 14eb2 (5:4eb2) - call AIPickPrizeCards - ret - -.list_arena ; 14eb6 (5:1eb6) - db HITMONCHAN - db MACHOP - db HITMONLEE - db MANKEY - db $00 - -.list_bench ; 14ebb (5:1ebb) - db MACHOP - db HITMONLEE - db HITMONCHAN - db MANKEY - db $00 - -.list_retreat ; 14ec0 (5:1ec0) - ai_retreat MACHOP, -1 - ai_retreat MACHOKE, -1 - ai_retreat MANKEY, -2 - db $00 - -.list_energy ; 14ec7 (5:1ec7) - ai_energy MACHOP, 3, +0 - ai_energy MACHOKE, 4, +0 - ai_energy MACHAMP, 4, -1 - ai_energy HITMONCHAN, 3, +0 - ai_energy HITMONLEE, 3, +0 - ai_energy MANKEY, 2, -1 - ai_energy PRIMEAPE, 3, -1 - db $00 - -.list_prize ; 14edd (5:1edd) - db HITMONLEE - db HITMONCHAN - db $00 - -.store_list_pointers ; 14ee0 (5:4ee0) - store_list_pointer wAICardListAvoidPrize, .list_prize - store_list_pointer wAICardListArenaPriority, .list_arena - store_list_pointer wAICardListBenchPriority, .list_bench - store_list_pointer wAICardListPlayFromHandPriority, .list_bench - ; missing store_list_pointer wAICardListRetreatBonus, .list_retreat - store_list_pointer wAICardListEnergyBonus, .list_energy - ret diff --git a/src/engine/ai/decks/flower_power.asm b/src/engine/ai/decks/flower_power.asm deleted file mode 100644 index 4d423a3..0000000 --- a/src/engine/ai/decks/flower_power.asm +++ /dev/null @@ -1,75 +0,0 @@ -AIActionTable_FlowerPower: ; 1509b (5:509b) - dw .do_turn ; unused - dw .do_turn - dw .start_duel - dw .forced_switch - dw .ko_switch - dw .take_prize - -.do_turn ; 150a7 (5:50a7) - call AIMainTurnLogic - ret - -.start_duel ; 150ab (5:50ab) - call InitAIDuelVars - call .store_list_pointers - call SetUpBossStartingHandAndDeck - call TrySetUpBossStartingPlayArea - ret nc - call AIPlayInitialBasicCards - ret - -.forced_switch ; 150bc (5:50bc) - call AIDecideBenchPokemonToSwitchTo - ret - -.ko_switch ; 150c0 (5:50c0) - call AIDecideBenchPokemonToSwitchTo - ret - -.take_prize ; 150c4 (5:50c4) - call AIPickPrizeCards - ret - -.list_arena ; 150c8 (5:50c8) - db ODDISH - db EXEGGCUTE - db BULBASAUR - db $00 - -.list_bench ; 150cc (5:50cc) - db BULBASAUR - db EXEGGCUTE - db ODDISH - db $00 - -.list_retreat ; 150cf (5:50cf) - ai_retreat GLOOM, -2 - ai_retreat VILEPLUME, -2 - ai_retreat BULBASAUR, -2 - ai_retreat IVYSAUR, -2 - db $00 - -.list_energy ; 150d9 (5:50d9) - ai_energy BULBASAUR, 3, +0 - ai_energy IVYSAUR, 4, +0 - ai_energy VENUSAUR2, 4, +0 - ai_energy ODDISH, 2, +0 - ai_energy GLOOM, 3, -1 - ai_energy VILEPLUME, 3, -1 - ai_energy EXEGGCUTE, 3, +0 - ai_energy EXEGGUTOR, 22, +0 - db $00 - -.list_prize ; 150f2 (5:50f2) - db VENUSAUR2 - db $00 - -.store_list_pointers ; 150f4 (5:50f4) - store_list_pointer wAICardListAvoidPrize, .list_prize - store_list_pointer wAICardListArenaPriority, .list_arena - store_list_pointer wAICardListBenchPriority, .list_bench - store_list_pointer wAICardListPlayFromHandPriority, .list_bench - ; missing store_list_pointer wAICardListRetreatBonus, .list_retreat - store_list_pointer wAICardListEnergyBonus, .list_energy - ret diff --git a/src/engine/ai/decks/general.asm b/src/engine/ai/decks/general.asm deleted file mode 100644 index 039e101..0000000 --- a/src/engine/ai/decks/general.asm +++ /dev/null @@ -1,194 +0,0 @@ -; AI logic used by general decks -AIActionTable_GeneralDecks: ; 14668 (05:4668) - dw .do_turn ; unused - dw .do_turn - dw .start_duel - dw .forced_switch - dw .ko_switch - dw .take_prize - -.do_turn ; 14674 (5:4674) - call AIMainTurnLogic - ret - -.start_duel ; 14678 (5:4678) - call InitAIDuelVars - call AIPlayInitialBasicCards - ret - -.forced_switch ; 1467f (5:467f) - call AIDecideBenchPokemonToSwitchTo - ret - -.ko_switch ; 14683 (5:4683) - call AIDecideBenchPokemonToSwitchTo - ret - -.take_prize: ; 14687 (5:4687) - call AIPickPrizeCards - ret - -; handle AI routines for a whole turn -AIMainTurnLogic: ; 1468b (5:468b) -; initialize variables - call InitAITurnVars - ld a, AI_TRAINER_CARD_PHASE_01 - call AIProcessHandTrainerCards - farcall HandleAIAntiMewtwoDeckStrategy - jp nc, .try_attack -; handle Pkmn Powers - farcall HandleAIGoGoRainDanceEnergy - farcall HandleAIDamageSwap - farcall HandleAIPkmnPowers - ret c ; return if turn ended - farcall HandleAICowardice -; process Trainer cards -; phase 2 through 4. - ld a, AI_TRAINER_CARD_PHASE_02 - call AIProcessHandTrainerCards - ld a, AI_TRAINER_CARD_PHASE_03 - call AIProcessHandTrainerCards - ld a, AI_TRAINER_CARD_PHASE_04 - call AIProcessHandTrainerCards -; play Pokemon from hand - call AIDecidePlayPokemonCard - ret c ; return if turn ended -; process Trainer cards -; phase 5 through 12. - ld a, AI_TRAINER_CARD_PHASE_05 - call AIProcessHandTrainerCards - ld a, AI_TRAINER_CARD_PHASE_06 - call AIProcessHandTrainerCards - ld a, AI_TRAINER_CARD_PHASE_07 - call AIProcessHandTrainerCards - ld a, AI_TRAINER_CARD_PHASE_08 - call AIProcessHandTrainerCards - call AIProcessRetreat - ld a, AI_TRAINER_CARD_PHASE_10 - call AIProcessHandTrainerCards - ld a, AI_TRAINER_CARD_PHASE_11 - call AIProcessHandTrainerCards - ld a, AI_TRAINER_CARD_PHASE_12 - call AIProcessHandTrainerCards -; play Energy card if possible - ld a, [wAlreadyPlayedEnergy] - or a - jr nz, .skip_energy_attach_1 - call AIProcessAndTryToPlayEnergy -.skip_energy_attach_1 -; play Pokemon from hand again - call AIDecidePlayPokemonCard -; handle Pkmn Powers again - farcall HandleAIDamageSwap - farcall HandleAIPkmnPowers - ret c ; return if turn ended - farcall HandleAIGoGoRainDanceEnergy - ld a, AI_ENERGY_TRANS_ATTACK - farcall HandleAIEnergyTrans -; process Trainer cards phases 13 and 15 - ld a, AI_TRAINER_CARD_PHASE_13 - call AIProcessHandTrainerCards - ld a, AI_TRAINER_CARD_PHASE_15 - call AIProcessHandTrainerCards -; if used Professor Oak, process new hand -; if not, then proceed to attack. - ld a, [wPreviousAIFlags] - and AI_FLAG_USED_PROFESSOR_OAK - jr z, .try_attack - ld a, AI_TRAINER_CARD_PHASE_01 - call AIProcessHandTrainerCards - ld a, AI_TRAINER_CARD_PHASE_02 - call AIProcessHandTrainerCards - ld a, AI_TRAINER_CARD_PHASE_03 - call AIProcessHandTrainerCards - ld a, AI_TRAINER_CARD_PHASE_04 - call AIProcessHandTrainerCards - call AIDecidePlayPokemonCard - ret c ; return if turn ended - ld a, AI_TRAINER_CARD_PHASE_05 - call AIProcessHandTrainerCards - ld a, AI_TRAINER_CARD_PHASE_06 - call AIProcessHandTrainerCards - ld a, AI_TRAINER_CARD_PHASE_07 - call AIProcessHandTrainerCards - ld a, AI_TRAINER_CARD_PHASE_08 - call AIProcessHandTrainerCards - call AIProcessRetreat - ld a, AI_TRAINER_CARD_PHASE_10 - call AIProcessHandTrainerCards - ld a, AI_TRAINER_CARD_PHASE_11 - call AIProcessHandTrainerCards - ld a, AI_TRAINER_CARD_PHASE_12 - call AIProcessHandTrainerCards - ld a, [wAlreadyPlayedEnergy] - or a - jr nz, .skip_energy_attach_2 - call AIProcessAndTryToPlayEnergy -.skip_energy_attach_2 - call AIDecidePlayPokemonCard - farcall HandleAIDamageSwap - farcall HandleAIPkmnPowers - ret c ; return if turn ended - farcall HandleAIGoGoRainDanceEnergy - ld a, AI_ENERGY_TRANS_ATTACK - farcall HandleAIEnergyTrans - ld a, AI_TRAINER_CARD_PHASE_13 - call AIProcessHandTrainerCards - ; skip AI_TRAINER_CARD_PHASE_15 -.try_attack - ld a, AI_ENERGY_TRANS_TO_BENCH - farcall HandleAIEnergyTrans -; attack if possible, if not, -; finish turn without attacking. - call AIProcessAndTryToUseAttack - ret c ; return if AI attacked - ld a, OPPACTION_FINISH_NO_ATTACK - bank1call AIMakeDecision - ret - -; handles AI retreating logic -AIProcessRetreat: ; 14786 (5:4786) - ld a, [wAIRetreatedThisTurn] - or a - ret nz ; return, already retreated this turn - - call AIDecideWhetherToRetreat - ret nc ; return if not retreating - - call AIDecideBenchPokemonToSwitchTo - ret c ; return if no Bench Pokemon - -; store Play Area to retreat to and -; set wAIRetreatedThisTurn to true - ld [wAIPlayAreaCardToSwitch], a - ld a, $01 - ld [wAIRetreatedThisTurn], a - -; if AI can use Switch from hand, use it instead... - ld a, AI_TRAINER_CARD_PHASE_09 - call AIProcessHandTrainerCards - ld a, [wPreviousAIFlags] - and AI_FLAG_USED_SWITCH - jr nz, .used_switch -; ... else try retreating normally. - ld a, [wAIPlayAreaCardToSwitch] - call AITryToRetreat - ret - -.used_switch -; if AI used switch, unset its AI flag - ld a, [wPreviousAIFlags] - and ~AI_FLAG_USED_SWITCH ; clear Switch flag - ld [wPreviousAIFlags], a - -; bug, this doesn't make sense being here, since at this point -; Switch Trainer card was already used to retreat the Pokemon. -; what the routine will do is just transfer Energy cards to -; the Arena Pokemon for the purpose of retreating, and -; then not actually retreat, resulting in unusual behaviour. -; this would only work placed right after the AI checks whether -; they have Switch card in hand to use and doesn't have one. -; (and probably that was the original intention.) - ld a, AI_ENERGY_TRANS_RETREAT ; retreat - farcall HandleAIEnergyTrans - ret diff --git a/src/engine/ai/decks/general_no_retreat.asm b/src/engine/ai/decks/general_no_retreat.asm deleted file mode 100644 index 20d84e3..0000000 --- a/src/engine/ai/decks/general_no_retreat.asm +++ /dev/null @@ -1,140 +0,0 @@ -; acts just like a general deck AI except never retreats -AIActionTable_GeneralNoRetreat: ; 148dc (5:48dc) - dw .do_turn ; unused - dw .do_turn - dw .start_duel - dw .forced_switch - dw .ko_switch - dw .take_prize - -.do_turn ; 148e8 (5:48e8) - call AIDoTurn_GeneralNoRetreat - ret - -.start_duel ; 148ec (5:48ec) - call InitAIDuelVars - call AIPlayInitialBasicCards - ret - -.forced_switch ; 148f3 (5:48f3) - call AIDecideBenchPokemonToSwitchTo - ret - -.ko_switch ; 148f7 (5:48f7) - call AIDecideBenchPokemonToSwitchTo - ret - -.take_prize ; 148fb (5:48fb) - call AIPickPrizeCards - ret - -AIDoTurn_GeneralNoRetreat: ; 148ff (5:48ff) -; initialize variables - call InitAITurnVars - ld a, AI_TRAINER_CARD_PHASE_01 - call AIProcessHandTrainerCards - farcall HandleAIAntiMewtwoDeckStrategy - jp nc, .try_attack -; handle Pkmn Powers - farcall HandleAIGoGoRainDanceEnergy - farcall HandleAIDamageSwap - farcall HandleAIPkmnPowers - ret c ; return if turn ended - farcall HandleAICowardice -; process Trainer cards -; phase 2 through 4. - ld a, AI_TRAINER_CARD_PHASE_02 - call AIProcessHandTrainerCards - ld a, AI_TRAINER_CARD_PHASE_03 - call AIProcessHandTrainerCards - ld a, AI_TRAINER_CARD_PHASE_04 - call AIProcessHandTrainerCards -; play Pokemon from hand - call AIDecidePlayPokemonCard - ret c ; return if turn ended -; process Trainer cards -; phase 5 through 12. - ld a, AI_TRAINER_CARD_PHASE_05 - call AIProcessHandTrainerCards - ld a, AI_TRAINER_CARD_PHASE_06 - call AIProcessHandTrainerCards - ld a, AI_TRAINER_CARD_PHASE_07 - call AIProcessHandTrainerCards - ld a, AI_TRAINER_CARD_PHASE_08 - call AIProcessHandTrainerCards - ld a, AI_TRAINER_CARD_PHASE_10 - call AIProcessHandTrainerCards - ld a, AI_TRAINER_CARD_PHASE_11 - call AIProcessHandTrainerCards - ld a, AI_TRAINER_CARD_PHASE_12 - call AIProcessHandTrainerCards -; play Energy card if possible - ld a, [wAlreadyPlayedEnergy] - or a - jr nz, .skip_energy_attach_1 - call AIProcessAndTryToPlayEnergy -.skip_energy_attach_1 -; play Pokemon from hand again - call AIDecidePlayPokemonCard -; handle Pkmn Powers again - farcall HandleAIDamageSwap - farcall HandleAIPkmnPowers - ret c ; return if turn ended - farcall HandleAIGoGoRainDanceEnergy - ld a, AI_ENERGY_TRANS_ATTACK - farcall HandleAIEnergyTrans -; process Trainer cards phases 13 and 15 - ld a, AI_TRAINER_CARD_PHASE_13 - call AIProcessHandTrainerCards - ld a, AI_TRAINER_CARD_PHASE_15 - call AIProcessHandTrainerCards -; if used Professor Oak, process new hand -; if not, then proceed to attack. - ld a, [wPreviousAIFlags] - and AI_FLAG_USED_PROFESSOR_OAK - jr z, .try_attack - ld a, AI_TRAINER_CARD_PHASE_01 - call AIProcessHandTrainerCards - ld a, AI_TRAINER_CARD_PHASE_02 - call AIProcessHandTrainerCards - ld a, AI_TRAINER_CARD_PHASE_03 - call AIProcessHandTrainerCards - ld a, AI_TRAINER_CARD_PHASE_04 - call AIProcessHandTrainerCards - call AIDecidePlayPokemonCard - ret c ; return if turn ended - ld a, AI_TRAINER_CARD_PHASE_05 - call AIProcessHandTrainerCards - ld a, AI_TRAINER_CARD_PHASE_06 - call AIProcessHandTrainerCards - ld a, AI_TRAINER_CARD_PHASE_07 - call AIProcessHandTrainerCards - ld a, AI_TRAINER_CARD_PHASE_08 - call AIProcessHandTrainerCards - ld a, AI_TRAINER_CARD_PHASE_10 - call AIProcessHandTrainerCards - ld a, AI_TRAINER_CARD_PHASE_11 - call AIProcessHandTrainerCards - ld a, AI_TRAINER_CARD_PHASE_12 - call AIProcessHandTrainerCards - ld a, [wAlreadyPlayedEnergy] - or a - jr nz, .skip_energy_attach_2 - call AIProcessAndTryToPlayEnergy -.skip_energy_attach_2 - call AIDecidePlayPokemonCard - farcall HandleAIDamageSwap - farcall HandleAIPkmnPowers - ret c ; return if turn ended - farcall HandleAIGoGoRainDanceEnergy - ld a, AI_TRAINER_CARD_PHASE_13 - call AIProcessHandTrainerCards - ; skip AI_TRAINER_CARD_PHASE_15 -.try_attack -; attack if possible, if not, -; finish turn without attacking. - call AIProcessAndTryToUseAttack - ret c ; return if turn ended - ld a, OPPACTION_FINISH_NO_ATTACK - bank1call AIMakeDecision - ret diff --git a/src/engine/ai/decks/go_go_rain_dance.asm b/src/engine/ai/decks/go_go_rain_dance.asm deleted file mode 100644 index 23547e2..0000000 --- a/src/engine/ai/decks/go_go_rain_dance.asm +++ /dev/null @@ -1,79 +0,0 @@ -AIActionTable_GoGoRainDance: ; 14f8f (5:4f8f) - dw .do_turn ; unused - dw .do_turn - dw .start_duel - dw .forced_switch - dw .ko_switch - dw .take_prize - -.do_turn ; 14f9b (5:4f9b) - call AIMainTurnLogic - ret - -.start_duel ; 14f9f (5:4f9f) - call InitAIDuelVars - call .store_list_pointers - call SetUpBossStartingHandAndDeck - call TrySetUpBossStartingPlayArea - ret nc - call AIPlayInitialBasicCards - ret - -.forced_switch ; 14fb0 (5:4fb0) - call AIDecideBenchPokemonToSwitchTo - ret - -.ko_switch ; 14fb4 (5:4fb4) - call AIDecideBenchPokemonToSwitchTo - ret - -.take_prize ; 14fb8 (5:4fb8) - call AIPickPrizeCards - ret - -.list_arena ; 14fbc (5:4fbc) - db LAPRAS - db HORSEA - db GOLDEEN - db SQUIRTLE - db $00 - -.list_bench ; 14fc1 (5:4fc1) - db SQUIRTLE - db HORSEA - db GOLDEEN - db LAPRAS - db $00 - -.list_retreat ; 14fc6 (5:4fc6) - ai_retreat SQUIRTLE, -3 - ai_retreat WARTORTLE, -2 - ai_retreat HORSEA, -1 - db $00 - -.list_energy ; 14fcd (5:4fcd) - ai_energy SQUIRTLE, 2, +0 - ai_energy WARTORTLE, 3, +0 - ai_energy BLASTOISE, 5, +0 - ai_energy GOLDEEN, 1, +0 - ai_energy SEAKING, 2, +0 - ai_energy HORSEA, 2, +0 - ai_energy SEADRA, 3, +0 - ai_energy LAPRAS, 3, +0 - db $00 - -.list_prize ; 14fe6 (5:4fe6) - db GAMBLER - db ENERGY_RETRIEVAL - db SUPER_ENERGY_RETRIEVAL - db BLASTOISE - db $00 - -.store_list_pointers ; 14feb (5:4feb) - store_list_pointer wAICardListAvoidPrize, .list_prize - store_list_pointer wAICardListArenaPriority, .list_arena - store_list_pointer wAICardListBenchPriority, .list_bench - store_list_pointer wAICardListPlayFromHandPriority, .list_bench - ; missing store_list_pointer wAICardListRetreatBonus, .list_retreat - store_list_pointer wAICardListEnergyBonus, .list_energy - ret diff --git a/src/engine/ai/decks/im_ronald.asm b/src/engine/ai/decks/im_ronald.asm deleted file mode 100644 index b002d83..0000000 --- a/src/engine/ai/decks/im_ronald.asm +++ /dev/null @@ -1,80 +0,0 @@ -AIActionTable_ImRonald: ; 152bd (5:52bd) - dw .do_turn ; unused - dw .do_turn - dw .start_duel - dw .forced_switch - dw .ko_switch - dw .take_prize - -.do_turn ; 152c9 (5:52c9) - call AIMainTurnLogic - ret - -.start_duel ; 152cd (5:52cd) - call InitAIDuelVars - call .store_list_pointers - call SetUpBossStartingHandAndDeck - call TrySetUpBossStartingPlayArea - ret nc - call AIPlayInitialBasicCards - ret - -.forced_switch ; 152de (5:52de) - call AIDecideBenchPokemonToSwitchTo - ret - -.ko_switch ; 152e2 (5:52e2) - call AIDecideBenchPokemonToSwitchTo - ret - -.take_prize ; 152e6 (5:52e6) - call AIPickPrizeCards - ret - -.list_arena ; 152ea (5:52ea) - db LAPRAS - db SEEL - db CHARMANDER - db CUBONE - db SQUIRTLE - db GROWLITHE - db $00 - -.list_bench ; 152f1 (5:52f1) - db CHARMANDER - db SQUIRTLE - db SEEL - db CUBONE - db GROWLITHE - db LAPRAS - db $00 - -.list_retreat ; 152f8 (5:52f8) - db $00 - -.list_energy ; 152f9 (5:52f9) - ai_energy CHARMANDER, 3, +0 - ai_energy CHARMELEON, 5, +0 - ai_energy GROWLITHE, 2, +0 - ai_energy ARCANINE2, 4, +0 - ai_energy SQUIRTLE, 2, +0 - ai_energy WARTORTLE, 3, +0 - ai_energy SEEL, 3, +0 - ai_energy DEWGONG, 4, +0 - ai_energy LAPRAS, 3, +0 - ai_energy CUBONE, 3, +0 - ai_energy MAROWAK1, 3, +0 - db $00 - -.list_prize ; 1531b (5:531b) - db LAPRAS - db $00 - -.store_list_pointers ; 1531d (5:531d) - store_list_pointer wAICardListAvoidPrize, .list_prize - store_list_pointer wAICardListArenaPriority, .list_arena - store_list_pointer wAICardListBenchPriority, .list_bench - store_list_pointer wAICardListPlayFromHandPriority, .list_bench - ; missing store_list_pointer wAICardListRetreatBonus, .list_retreat - store_list_pointer wAICardListEnergyBonus, .list_energy - ret diff --git a/src/engine/ai/decks/invincible_ronald.asm b/src/engine/ai/decks/invincible_ronald.asm deleted file mode 100644 index 463560b..0000000 --- a/src/engine/ai/decks/invincible_ronald.asm +++ /dev/null @@ -1,78 +0,0 @@ -AIActionTable_InvincibleRonald: ; 153e8 (5:53e8) - dw .do_turn ; unused - dw .do_turn - dw .start_duel - dw .forced_switch - dw .ko_switch - dw .take_prize - -.do_turn ; 153f4 (5:53f4) - call AIMainTurnLogic - ret - -.start_duel ; 153f8 (5:53f8) - call InitAIDuelVars - call .store_list_pointers - call SetUpBossStartingHandAndDeck - call TrySetUpBossStartingPlayArea - ret nc - call AIPlayInitialBasicCards - ret - -.forced_switch ; 15409 (5:5409) - call AIDecideBenchPokemonToSwitchTo - ret - -.ko_switch ; 1540d (5:540d) - call AIDecideBenchPokemonToSwitchTo - ret - -.take_prize ; 15411 (5:5411) - call AIPickPrizeCards - ret - -.list_arena ; 15415 (5:5415) - db KANGASKHAN - db MAGMAR2 - db CHANSEY - db GEODUDE - db SCYTHER - db GRIMER - db $00 - -.list_bench ; 1541c (5:541c) - db GRIMER - db SCYTHER - db GEODUDE - db CHANSEY - db MAGMAR2 - db KANGASKHAN - db $00 - -.list_retreat ; 15423 (5:5423) - ai_retreat GRIMER, -1 - db $00 - -.list_energy ; 15426 (5:5426) - ai_energy GRIMER, 1, -1 - ai_energy MUK, 3, -1 - ai_energy SCYTHER, 4, +1 - ai_energy MAGMAR2, 2, +0 - ai_energy GEODUDE, 2, +0 - ai_energy GRAVELER, 3, +0 - ai_energy CHANSEY, 4, +0 - ai_energy KANGASKHAN, 4, -1 - db $00 - -.list_prize ; 1543f (5:543f) - db GAMBLER - db $00 - -.store_list_pointers ; 15441 (5:5441) - store_list_pointer wAICardListAvoidPrize, .list_prize - store_list_pointer wAICardListArenaPriority, .list_arena - store_list_pointer wAICardListBenchPriority, .list_bench - store_list_pointer wAICardListPlayFromHandPriority, .list_bench - ; missing store_list_pointer wAICardListRetreatBonus, .list_retreat - store_list_pointer wAICardListEnergyBonus, .list_energy - ret diff --git a/src/engine/ai/decks/legendary_articuno.asm b/src/engine/ai/decks/legendary_articuno.asm deleted file mode 100644 index 6409330..0000000 --- a/src/engine/ai/decks/legendary_articuno.asm +++ /dev/null @@ -1,209 +0,0 @@ -AIActionTable_LegendaryArticuno: ; 14c0b (5:4c0b) - dw .do_turn ; unused - dw .do_turn - dw .start_duel - dw .forced_switch - dw .ko_switch - dw .take_prize - -.do_turn ; 14c17 (5:4c17) - call AIDoTurn_LegendaryArticuno - ret - -.start_duel ; 14c1b (5:4c1b) - call InitAIDuelVars - call .store_list_pointers - call SetUpBossStartingHandAndDeck - call TrySetUpBossStartingPlayArea - ret nc - call AIPlayInitialBasicCards - ret - -.forced_switch ; 14c2c (5:4c2c) - call AIDecideBenchPokemonToSwitchTo - ret - -.ko_switch ; 14c30 (5:4c30) - call AIDecideBenchPokemonToSwitchTo - ret - -.take_prize ; 14c34 (5:4c34) - call AIPickPrizeCards - ret - -.list_arena ; 14c38 (5:4c38) - db CHANSEY - db LAPRAS - db DITTO - db SEEL - db ARTICUNO1 - db ARTICUNO2 - db $00 - -.list_bench ; 14c3f (5:4c3f) - db ARTICUNO1 - db SEEL - db LAPRAS - db CHANSEY - db DITTO - db $00 - -.list_retreat ; 14c45 (5:4c45) - ai_retreat SEEL, -3 - ai_retreat DITTO, -3 - db $00 - -.list_energy ; 14c4a (5:4c4a) - ai_energy SEEL, 3, +1 - ai_energy DEWGONG, 4, +0 - ai_energy LAPRAS, 3, +0 - ai_energy ARTICUNO1, 4, +1 - ai_energy ARTICUNO2, 3, +0 - ai_energy CHANSEY, 0, -8 - ai_energy DITTO, 3, +0 - db $00 - -.list_prize ; 14c60 (5:4c60) - db GAMBLER - db ARTICUNO2 - db $00 - -.store_list_pointers ; 14c63 (5:4c63) - store_list_pointer wAICardListAvoidPrize, .list_prize - store_list_pointer wAICardListArenaPriority, .list_arena - store_list_pointer wAICardListBenchPriority, .list_bench - store_list_pointer wAICardListPlayFromHandPriority, .list_bench - ; missing store_list_pointer wAICardListRetreatBonus, .list_retreat - store_list_pointer wAICardListEnergyBonus, .list_energy - ret - -; this routine handles how Legendary Articuno -; prioritizes playing energy cards to each Pokémon. -; first, it makes sure that all Lapras have at least -; 3 energy cards before moving on to Articuno, -; and then to Dewgong and Seel -ScoreLegendaryArticunoCards: ; 14c91 (5:4c91) - call SwapTurn - call CountPrizes - call SwapTurn - cp 3 - ret c - -; player prizes >= 3 -; if Lapras has more than half HP and -; can use second attack, check next for Articuno -; otherwise, check if Articuno or Dewgong -; have more than half HP and can use second attack -; and if so, the next Pokémon to check is Lapras - ld a, LAPRAS - call CheckForBenchIDAtHalfHPAndCanUseSecondAttack - jr c, .articuno - ld a, ARTICUNO1 - call CheckForBenchIDAtHalfHPAndCanUseSecondAttack - jr c, .lapras - ld a, DEWGONG - call CheckForBenchIDAtHalfHPAndCanUseSecondAttack - jr c, .lapras - jr .articuno - -; the following routines check for certain card IDs in bench -; and call RaiseAIScoreToAllMatchingIDsInBench if these are found. -; for Lapras, an additional check is made to its -; attached energy count, which skips calling the routine -; if this count is >= 3 -.lapras - ld a, LAPRAS - ld b, PLAY_AREA_BENCH_1 - call LookForCardIDInPlayArea_Bank5 - jr nc, .articuno - ld e, a - call CountNumberOfEnergyCardsAttached - cp 3 - jr nc, .articuno - ld a, LAPRAS - call RaiseAIScoreToAllMatchingIDsInBench - ret - -.articuno - ld a, ARTICUNO1 - ld b, PLAY_AREA_BENCH_1 - call LookForCardIDInPlayArea_Bank5 - jr nc, .dewgong - ld a, ARTICUNO1 - call RaiseAIScoreToAllMatchingIDsInBench - ret - -.dewgong - ld a, DEWGONG - ld b, PLAY_AREA_BENCH_1 - call LookForCardIDInPlayArea_Bank5 - jr nc, .seel - ld a, DEWGONG - call RaiseAIScoreToAllMatchingIDsInBench - ret - -.seel - ld a, SEEL - ld b, PLAY_AREA_BENCH_1 - call LookForCardIDInPlayArea_Bank5 - ret nc - ld a, SEEL - call RaiseAIScoreToAllMatchingIDsInBench - ret - -AIDoTurn_LegendaryArticuno: ; 14cf7 (5:4cf7) -; initialize variables - call InitAITurnVars - ld a, AI_TRAINER_CARD_PHASE_01 - call AIProcessHandTrainerCards - farcall HandleAIAntiMewtwoDeckStrategy - jp nc, .try_attack -; process Trainer cards - ld a, AI_TRAINER_CARD_PHASE_02 - call AIProcessHandTrainerCards -; play Pokemon from hand - call AIDecidePlayPokemonCard - ret c ; return if turn ended - call AIProcessRetreat - ld a, AI_TRAINER_CARD_PHASE_10 - call AIProcessHandTrainerCards -; play Energy card if possible - ld a, [wAlreadyPlayedEnergy] - or a - jr nz, .skip_energy_attach_1 - call AIProcessAndTryToPlayEnergy -.skip_energy_attach_1 -; play Pokemon from hand again - call AIDecidePlayPokemonCard -; process Trainer cards phases 13 and 15 - ld a, AI_TRAINER_CARD_PHASE_13 - call AIProcessHandTrainerCards - ld a, AI_TRAINER_CARD_PHASE_15 - call AIProcessHandTrainerCards -; if used Professor Oak, process new hand - ld a, [wPreviousAIFlags] - and AI_FLAG_USED_PROFESSOR_OAK - jr z, .try_attack - ld a, AI_TRAINER_CARD_PHASE_01 - call AIProcessHandTrainerCards - ld a, AI_TRAINER_CARD_PHASE_02 - call AIProcessHandTrainerCards - call AIDecidePlayPokemonCard - ret c ; return if turn ended - call AIProcessRetreat - ld a, AI_TRAINER_CARD_PHASE_10 - call AIProcessHandTrainerCards - ld a, [wAlreadyPlayedEnergy] - or a - jr nz, .skip_energy_attach_2 - call AIProcessAndTryToPlayEnergy -.skip_energy_attach_2 - call AIDecidePlayPokemonCard -.try_attack -; attack if possible, if not, -; finish turn without attacking. - call AIProcessAndTryToUseAttack - ret c ; return if turn ended - ld a, OPPACTION_FINISH_NO_ATTACK - bank1call AIMakeDecision - ret diff --git a/src/engine/ai/decks/legendary_dragonite.asm b/src/engine/ai/decks/legendary_dragonite.asm deleted file mode 100644 index 597f72c..0000000 --- a/src/engine/ai/decks/legendary_dragonite.asm +++ /dev/null @@ -1,166 +0,0 @@ -AIActionTable_LegendaryDragonite: ; 14d60 (05:4d60) - dw .do_turn ; unused - dw .do_turn - dw .start_duel - dw .forced_switch - dw .ko_switch - dw .take_prize - -.do_turn ; 14d6c (5:4d6c) - call AIDoTurn_LegendaryDragonite - ret - -.start_duel ; 14d70 (5:4d70) - call InitAIDuelVars - call .store_list_pointers - call SetUpBossStartingHandAndDeck - call TrySetUpBossStartingPlayArea - ret nc - call AIPlayInitialBasicCards - ret - -.forced_switch ; 14d81 (5:4d81) - call AIDecideBenchPokemonToSwitchTo - ret - -.ko_switch ; 14d85 (5:4d85) - call AIDecideBenchPokemonToSwitchTo - ret - -.take_prize ; 14d89 (5:4d89) - call AIPickPrizeCards - ret - -.list_arena ; 14d8d (5:4d8d) - db KANGASKHAN - db LAPRAS - db CHARMANDER - db DRATINI - db MAGIKARP - db $00 - -.list_bench ; 14d93 (5:4d93) - db CHARMANDER - db MAGIKARP - db DRATINI - db LAPRAS - db KANGASKHAN - db $00 - -.list_retreat ; 14d99 (5:4d99) - ai_retreat CHARMANDER, -1 - ai_retreat MAGIKARP, -5 - db $00 - -.list_energy ; 14d9e (5:4d9e) - ai_energy CHARMANDER, 3, +1 - ai_energy CHARMELEON, 4, +1 - ai_energy CHARIZARD, 5, +0 - ai_energy MAGIKARP, 3, +1 - ai_energy GYARADOS, 4, -1 - ai_energy DRATINI, 2, +0 - ai_energy DRAGONAIR, 4, +0 - ai_energy DRAGONITE1, 3, -1 - ai_energy KANGASKHAN, 2, -2 - ai_energy LAPRAS, 3, +0 - db $00 - -.list_prize ; 14dbd (5:4dbd) - db GAMBLER - db DRAGONITE1 - db KANGASKHAN - db $00 - -.store_list_pointers ; 14dc1 (5:4dc1) - store_list_pointer wAICardListAvoidPrize, .list_prize - store_list_pointer wAICardListArenaPriority, .list_arena - store_list_pointer wAICardListBenchPriority, .list_bench - store_list_pointer wAICardListPlayFromHandPriority, .list_bench - ; missing store_list_pointer wAICardListRetreatBonus, .list_retreat - store_list_pointer wAICardListEnergyBonus, .list_energy - ret - -AIDoTurn_LegendaryDragonite: ; 14def (5:4def) -; initialize variables - call InitAITurnVars - ld a, AI_TRAINER_CARD_PHASE_01 - call AIProcessHandTrainerCards - farcall HandleAIAntiMewtwoDeckStrategy - jp nc, .try_attack -; process Trainer cards - ld a, AI_TRAINER_CARD_PHASE_02 - call AIProcessHandTrainerCards -; play Pokemon from hand - call AIDecidePlayPokemonCard - ret c ; return if turn ended - ld a, AI_TRAINER_CARD_PHASE_07 - call AIProcessHandTrainerCards - call AIProcessRetreat - ld a, AI_TRAINER_CARD_PHASE_10 - call AIProcessHandTrainerCards - ld a, AI_TRAINER_CARD_PHASE_11 - call AIProcessHandTrainerCards -; play Energy card if possible - ld a, [wAlreadyPlayedEnergy] - or a - jr nz, .skip_energy_attach_1 - -; if Arena card is Kangaskhan and doesn't -; have Energy cards attached, try attaching from hand. -; otherwise run normal AI energy attach routine. - ld a, DUELVARS_ARENA_CARD - call GetTurnDuelistVariable - call GetCardIDFromDeckIndex - ld a, KANGASKHAN - cp e - jr nz, .attach_normally - call CreateEnergyCardListFromHand - jr c, .skip_energy_attach_1 - ld e, PLAY_AREA_ARENA - call CountNumberOfEnergyCardsAttached - or a - jr nz, .attach_normally - xor a - ldh [hTempPlayAreaLocation_ff9d], a - call AITryToPlayEnergyCard - jr c, .skip_energy_attach_1 -.attach_normally - call AIProcessAndTryToPlayEnergy - -.skip_energy_attach_1 -; play Pokemon from hand again - call AIDecidePlayPokemonCard - ld a, AI_TRAINER_CARD_PHASE_15 - call AIProcessHandTrainerCards -; if used Professor Oak, process new hand -; if not, then proceed to attack. - ld a, [wPreviousAIFlags] - and AI_FLAG_USED_PROFESSOR_OAK - jr z, .try_attack - ld a, AI_TRAINER_CARD_PHASE_01 - call AIProcessHandTrainerCards - ld a, AI_TRAINER_CARD_PHASE_02 - call AIProcessHandTrainerCards - call AIDecidePlayPokemonCard - ret c ; return if turn ended - ld a, AI_TRAINER_CARD_PHASE_07 - call AIProcessHandTrainerCards - call AIProcessRetreat - ld a, AI_TRAINER_CARD_PHASE_10 - call AIProcessHandTrainerCards - ld a, AI_TRAINER_CARD_PHASE_11 - call AIProcessHandTrainerCards - ld a, [wAlreadyPlayedEnergy] - or a - jr nz, .skip_energy_attach_2 - call AIProcessAndTryToPlayEnergy -.skip_energy_attach_2 - call AIDecidePlayPokemonCard -.try_attack -; attack if possible, if not, -; finish turn without attacking. - call AIProcessAndTryToUseAttack - ret c ; return if turn ended - ld a, OPPACTION_FINISH_NO_ATTACK - bank1call AIMakeDecision - ret diff --git a/src/engine/ai/decks/legendary_moltres.asm b/src/engine/ai/decks/legendary_moltres.asm deleted file mode 100644 index c2a3882..0000000 --- a/src/engine/ai/decks/legendary_moltres.asm +++ /dev/null @@ -1,176 +0,0 @@ -AIActionTable_LegendaryMoltres: ; 149e8 (05:49e8) - dw .do_turn ; unused - dw .do_turn - dw .start_duel - dw .forced_switch - dw .ko_switch - dw .take_prize - -.do_turn ; 149f4 (5:49f4) - call AIDoTurn_LegendaryMoltres - ret - -.start_duel ; 149f8 (5:49f8) - call InitAIDuelVars - call .store_list_pointers - call SetUpBossStartingHandAndDeck - call TrySetUpBossStartingPlayArea - ret nc ; Play Area set up was successful - call AIPlayInitialBasicCards - ret - -.forced_switch ; 14a09 (5:4a09) - call AIDecideBenchPokemonToSwitchTo - ret - -.ko_switch ; 14a0d (5:4a0d) - call AIDecideBenchPokemonToSwitchTo - ret - -.take_prize ; 14a11 (5:4a11) - call AIPickPrizeCards - ret - -.list_arena ; 14a15 (5:4a15) - db MAGMAR2 - db GROWLITHE - db VULPIX - db MAGMAR1 - db MOLTRES1 - db MOLTRES2 - db $00 - -.list_bench ; 14a1c (5:4a1c) - db MOLTRES1 - db VULPIX - db GROWLITHE - db MAGMAR2 - db MAGMAR1 - db $00 - -.list_play_hand ; 14a22 (5:4a22) - db MOLTRES2 - db MOLTRES1 - db VULPIX - db GROWLITHE - db MAGMAR2 - db MAGMAR1 - db $00 - -.list_retreat ; 14a29 (5:4a29) - ai_retreat GROWLITHE, -5 - ai_retreat VULPIX, -5 - db $00 - -.list_energy ; 14a2e (5:4a2e) - ai_energy VULPIX, 3, +0 - ai_energy NINETALES2, 3, +1 - ai_energy GROWLITHE, 3, +1 - ai_energy ARCANINE2, 4, +1 - ai_energy MAGMAR1, 4, -1 - ai_energy MAGMAR2, 1, -1 - ai_energy MOLTRES2, 3, +2 - ai_energy MOLTRES1, 4, +2 - db $00 - -.list_prize ; 14a47 (5:4a47) - db ENERGY_REMOVAL - db MOLTRES2 - db $00 - -.store_list_pointers ; 14a4a (5:4a4a) - store_list_pointer wAICardListAvoidPrize, .list_prize - store_list_pointer wAICardListArenaPriority, .list_arena - store_list_pointer wAICardListBenchPriority, .list_bench - store_list_pointer wAICardListPlayFromHandPriority, .list_play_hand - store_list_pointer wAICardListRetreatBonus, .list_retreat - store_list_pointer wAICardListEnergyBonus, .list_energy - ret - -AIDoTurn_LegendaryMoltres: ; 14a81 (5:4a81) -; initialize variables - call InitAITurnVars - farcall HandleAIAntiMewtwoDeckStrategy - jp nc, .try_attack -; process Trainer cards -; phase 2 through 4. - ld a, AI_TRAINER_CARD_PHASE_02 - call AIProcessHandTrainerCards - ld a, AI_TRAINER_CARD_PHASE_04 - call AIProcessHandTrainerCards - -; check if AI can play Moltres2 -; from hand and if so, play it. - ld a, DUELVARS_NUMBER_OF_POKEMON_IN_PLAY_AREA - call GetTurnDuelistVariable - cp MAX_PLAY_AREA_POKEMON - jr nc, .skip_moltres ; skip if bench is full - ld a, DUELVARS_NUMBER_OF_CARDS_NOT_IN_DECK - call GetTurnDuelistVariable - cp DECK_SIZE - 9 - jr nc, .skip_moltres ; skip if cards in deck <= 9 - ld a, MUK - call CountPokemonIDInBothPlayAreas - jr c, .skip_moltres ; skip if Muk in play - ld a, MOLTRES2 - call LookForCardIDInHandList_Bank5 - jr nc, .skip_moltres ; skip if no Moltres2 in hand - ldh [hTemp_ffa0], a - ld a, OPPACTION_PLAY_BASIC_PKMN - bank1call AIMakeDecision - -.skip_moltres -; play Pokemon from hand - call AIDecidePlayPokemonCard - ret c ; return if turn ended -; process Trainer cards - ld a, AI_TRAINER_CARD_PHASE_05 - call AIProcessHandTrainerCards - call AIProcessRetreat - ld a, AI_TRAINER_CARD_PHASE_10 - call AIProcessHandTrainerCards - ld a, AI_TRAINER_CARD_PHASE_11 - call AIProcessHandTrainerCards -; play Energy card if possible - ld a, [wAlreadyPlayedEnergy] - or a - jr nz, .skip_attach_energy - -; if Magmar2 is the Arena card and has no energy attached, -; try attaching an energy card to it from the hand. -; otherwise, run normal AI energy attach routine. - ld a, DUELVARS_ARENA_CARD - call GetTurnDuelistVariable - call GetCardIDFromDeckIndex - ld a, MAGMAR2 - cp e - jr nz, .attach_normally - ; Magmar2 is the Arena card - call CreateEnergyCardListFromHand - jr c, .skip_attach_energy - ld e, PLAY_AREA_ARENA - call CountNumberOfEnergyCardsAttached - or a - jr nz, .attach_normally - xor a ; PLAY_AREA_ARENA - ldh [hTempPlayAreaLocation_ff9d], a - call AITryToPlayEnergyCard - jr c, .skip_attach_energy - -.attach_normally -; play Energy card if possible - call AIProcessAndTryToPlayEnergy -.skip_attach_energy -; try playing Pokemon cards from hand again - call AIDecidePlayPokemonCard - ld a, AI_TRAINER_CARD_PHASE_13 - call AIProcessHandTrainerCards - -.try_attack -; attack if possible, if not, -; finish turn without attacking. - call AIProcessAndTryToUseAttack - ret c - ld a, OPPACTION_FINISH_NO_ATTACK - bank1call AIMakeDecision - ret diff --git a/src/engine/ai/decks/legendary_ronald.asm b/src/engine/ai/decks/legendary_ronald.asm deleted file mode 100644 index 3356838..0000000 --- a/src/engine/ai/decks/legendary_ronald.asm +++ /dev/null @@ -1,203 +0,0 @@ -AIActionTable_LegendaryRonald: ; 1546f (5:546f) - dw .do_turn ; unused - dw .do_turn - dw .start_duel - dw .forced_switch - dw .ko_switch - dw .take_prize - -.do_turn ; 1547b (5:547b) - call AIDoTurn_LegendaryRonald - ret - -.start_duel ; 1547f (5:547f) - call InitAIDuelVars - call .store_list_pointers - call SetUpBossStartingHandAndDeck - call TrySetUpBossStartingPlayArea - ret nc - call AIPlayInitialBasicCards - ret - -.forced_switch ; 15490 (5:5490) - call AIDecideBenchPokemonToSwitchTo - ret - -.ko_switch ; 15494 (5:5494) - call AIDecideBenchPokemonToSwitchTo - ret - -.take_prize ; 15498 (5:5498) - call AIPickPrizeCards - ret - -.list_arena ; 1549c (5:549c) - db KANGASKHAN - db DRATINI - db EEVEE - db ZAPDOS3 - db ARTICUNO2 - db MOLTRES2 - db $00 - -.list_bench ; 154a3 (5:54a3) - db KANGASKHAN - db DRATINI - db EEVEE - db $00 - -.list_play_hand ; 154a7 (5:54a7) - db MOLTRES2 - db ZAPDOS3 - db KANGASKHAN - db DRATINI - db EEVEE - db ARTICUNO2 - db $00 - -.list_retreat ; 154ae (5:54ae) - ai_retreat EEVEE, -2 - db $00 - -.list_energy ; 154b1 (5:54b1) - ai_energy FLAREON1, 3, +0 - ai_energy MOLTRES2, 3, +0 - ai_energy VAPOREON1, 3, +0 - ai_energy ARTICUNO2, 0, -8 - ai_energy JOLTEON1, 4, +0 - ai_energy ZAPDOS3, 0, -8 - ai_energy KANGASKHAN, 4, -1 - ai_energy EEVEE, 3, +0 - ai_energy DRATINI, 3, +0 - ai_energy DRAGONAIR, 4, +0 - ai_energy DRAGONITE1, 3, +0 - db $00 - -.list_prize ; 154d3 (5:54d3) - db MOLTRES2 - db ARTICUNO2 - db ZAPDOS3 - db DRAGONITE1 - db GAMBLER - db $00 - -.store_list_pointers ; 154d9 (5:54d9) - store_list_pointer wAICardListAvoidPrize, .list_prize - store_list_pointer wAICardListArenaPriority, .list_arena - store_list_pointer wAICardListBenchPriority, .list_bench - store_list_pointer wAICardListPlayFromHandPriority, .list_play_hand - ; missing store_list_pointer wAICardListRetreatBonus, .list_retreat - store_list_pointer wAICardListEnergyBonus, .list_energy - ret - -AIDoTurn_LegendaryRonald: ; 15507 (5:5507) -; initialize variables - call InitAITurnVars -; process Trainer cards - ld a, AI_TRAINER_CARD_PHASE_01 - call AIProcessHandTrainerCards - ld a, AI_TRAINER_CARD_PHASE_02 - call AIProcessHandTrainerCards - ld a, AI_TRAINER_CARD_PHASE_04 - call AIProcessHandTrainerCards - -; check if AI can play Moltres2 -; from hand and if so, play it. - ld a, DUELVARS_NUMBER_OF_POKEMON_IN_PLAY_AREA - call GetTurnDuelistVariable - cp MAX_PLAY_AREA_POKEMON - jr nc, .skip_moltres_1 ; skip if bench is full - ld a, DUELVARS_NUMBER_OF_CARDS_NOT_IN_DECK - call GetTurnDuelistVariable - cp DECK_SIZE - 9 - jr nc, .skip_moltres_1 ; skip if cards in deck <= 9 - ld a, MUK - call CountPokemonIDInBothPlayAreas - jr c, .skip_moltres_1 ; skip if Muk in play - ld a, MOLTRES2 - call LookForCardIDInHandList_Bank5 - jr nc, .skip_moltres_1 ; skip if no Moltres2 in hand - ldh [hTemp_ffa0], a - ld a, OPPACTION_PLAY_BASIC_PKMN - bank1call AIMakeDecision - -.skip_moltres_1 -; play Pokemon from hand - call AIDecidePlayPokemonCard - ret c ; return if turn ended -; process Trainer cards - ld a, AI_TRAINER_CARD_PHASE_05 - call AIProcessHandTrainerCards - ld a, AI_TRAINER_CARD_PHASE_07 - call AIProcessHandTrainerCards - call AIProcessRetreat - ld a, AI_TRAINER_CARD_PHASE_10 - call AIProcessHandTrainerCards -; play Energy card if possible - ld a, [wAlreadyPlayedEnergy] - or a - jr nz, .skip_attach_energy_1 - call AIProcessAndTryToPlayEnergy -.skip_attach_energy_1 -; try playing Pokemon cards from hand again - call AIDecidePlayPokemonCard - ret c ; return if turn ended - ld a, AI_TRAINER_CARD_PHASE_15 -; if used Professor Oak, process new hand -; if not, then proceed to attack. - call AIProcessHandTrainerCards - ld a, [wPreviousAIFlags] - and AI_FLAG_USED_PROFESSOR_OAK - jr z, .try_attack - ld a, AI_TRAINER_CARD_PHASE_01 - call AIProcessHandTrainerCards - ld a, AI_TRAINER_CARD_PHASE_02 - call AIProcessHandTrainerCards - ld a, AI_TRAINER_CARD_PHASE_04 - call AIProcessHandTrainerCards - -; check if AI can play Moltres2 -; from hand and if so, play it. - ld a, DUELVARS_NUMBER_OF_POKEMON_IN_PLAY_AREA - call GetTurnDuelistVariable - cp MAX_PLAY_AREA_POKEMON - jr nc, .skip_moltres_2 ; skip if bench is full - ld a, DUELVARS_NUMBER_OF_CARDS_NOT_IN_DECK - call GetTurnDuelistVariable - cp DECK_SIZE - 9 - jr nc, .skip_moltres_2 ; skip if cards in deck <= 9 - ld a, MUK - call CountPokemonIDInBothPlayAreas - jr c, .skip_moltres_2 ; skip if Muk in play - ld a, MOLTRES2 - call LookForCardIDInHandList_Bank5 - jr nc, .skip_moltres_2 ; skip if no Moltres2 in hand - ldh [hTemp_ffa0], a - ld a, OPPACTION_PLAY_BASIC_PKMN - bank1call AIMakeDecision - -.skip_moltres_2 - call AIDecidePlayPokemonCard - ret c ; return if turn ended - ld a, AI_TRAINER_CARD_PHASE_05 - call AIProcessHandTrainerCards - ld a, AI_TRAINER_CARD_PHASE_07 - call AIProcessHandTrainerCards - call AIProcessRetreat - ld a, AI_TRAINER_CARD_PHASE_10 - call AIProcessHandTrainerCards - ld a, [wAlreadyPlayedEnergy] - or a - jr nz, .skip_attach_energy_2 - call AIProcessAndTryToPlayEnergy -.skip_attach_energy_2 - call AIDecidePlayPokemonCard - ret c ; return if turn ended -.try_attack -; attack if possible, if not, -; finish turn without attacking. - call AIProcessAndTryToUseAttack - ret c ; return if turn ended - ld a, OPPACTION_FINISH_NO_ATTACK - bank1call AIMakeDecision - ret diff --git a/src/engine/ai/decks/legendary_zapdos.asm b/src/engine/ai/decks/legendary_zapdos.asm deleted file mode 100644 index cc99f0c..0000000 --- a/src/engine/ai/decks/legendary_zapdos.asm +++ /dev/null @@ -1,153 +0,0 @@ -AIActionTable_LegendaryZapdos: ; 14b0f (05:4b0f) - dw .do_turn ; unused - dw .do_turn - dw .start_duel - dw .forced_switch - dw .ko_switch - dw .take_prize - -.do_turn ; 14b1b (5:4b1b) - call AIDoTurn_LegendaryZapdos - ret - -.start_duel ; 14b1f (5:4b1f) - call InitAIDuelVars - call .store_list_pointers - call SetUpBossStartingHandAndDeck - call TrySetUpBossStartingPlayArea - ret nc - call AIPlayInitialBasicCards - ret - -.forced_switch ; 14b30 (5:4b30) - call AIDecideBenchPokemonToSwitchTo - ret - -.ko_switch ; 14b34 (5:4b34) - call AIDecideBenchPokemonToSwitchTo - ret - -.take_prize ; 14b38 (5:4b38) - call AIPickPrizeCards - ret - -.list_arena ; 14b3c (5:4b3c) - db ELECTABUZZ2 - db VOLTORB - db EEVEE - db ZAPDOS1 - db ZAPDOS2 - db ZAPDOS3 - db $00 - -.list_bench ; 14b43 (5:4b43) - db ZAPDOS2 - db ZAPDOS1 - db EEVEE - db VOLTORB - db ELECTABUZZ2 - db $00 - -.list_retreat ; 14b49 (5:4b49) - ai_retreat EEVEE, -5 - ai_retreat VOLTORB, -5 - ai_retreat ELECTABUZZ2, -5 - db $00 - -.list_energy ; 14b50 (5:4b50) - ai_energy VOLTORB, 1, -1 - ai_energy ELECTRODE1, 3, +0 - ai_energy ELECTABUZZ2, 2, -1 - ai_energy JOLTEON2, 3, +1 - ai_energy ZAPDOS1, 4, +2 - ai_energy ZAPDOS2, 4, +2 - ai_energy ZAPDOS3, 3, +1 - ai_energy EEVEE, 3, +0 - db $00 - -.list_prize ; 14b69 (5:4b69) - db GAMBLER - db ZAPDOS3 - db $00 - -.store_list_pointers ; 14b6c (5:4b6c) - store_list_pointer wAICardListAvoidPrize, .list_prize - store_list_pointer wAICardListArenaPriority, .list_arena - store_list_pointer wAICardListBenchPriority, .list_bench - store_list_pointer wAICardListPlayFromHandPriority, .list_bench - ; missing store_list_pointer wAICardListRetreatBonus, .list_retreat - store_list_pointer wAICardListEnergyBonus, .list_energy - ret - -AIDoTurn_LegendaryZapdos: ; 14b9a (5:4b9a) -; initialize variables - call InitAITurnVars - farcall HandleAIAntiMewtwoDeckStrategy - jp nc, .try_attack -; process Trainer cards - ld a, AI_TRAINER_CARD_PHASE_01 - call AIProcessHandTrainerCards - ld a, AI_TRAINER_CARD_PHASE_04 - call AIProcessHandTrainerCards -; play Pokemon from hand - call AIDecidePlayPokemonCard - ret c ; return if turn ended - ld a, AI_TRAINER_CARD_PHASE_07 - call AIProcessHandTrainerCards - call AIProcessRetreat - ld a, AI_TRAINER_CARD_PHASE_10 - call AIProcessHandTrainerCards -; play Energy card if possible. - ld a, [wAlreadyPlayedEnergy] - or a - jr nz, .skip_energy_attach - -; if Arena card is Voltorb and there's Electrode1 in hand, -; or if it's Electabuzz, try attaching Energy card -; to the Arena card if it doesn't have any energy attached. -; Otherwise if Energy card is not needed, -; go through normal AI energy attach routine. - ld a, DUELVARS_ARENA_CARD - call GetTurnDuelistVariable - call GetCardIDFromDeckIndex - ld a, VOLTORB - cp e - jr nz, .check_electabuzz - ld a, ELECTRODE1 - call LookForCardIDInHandList_Bank5 - jr nc, .attach_normally - jr .voltorb_or_electabuzz -.check_electabuzz - ld a, ELECTABUZZ2 - cp e - jr nz, .attach_normally - -.voltorb_or_electabuzz - call CreateEnergyCardListFromHand - jr c, .skip_energy_attach - ld e, PLAY_AREA_ARENA - call CountNumberOfEnergyCardsAttached - or a - jr nz, .attach_normally - xor a ; PLAY_AREA_ARENA - ldh [hTempPlayAreaLocation_ff9d], a - call AITryToPlayEnergyCard - jr c, .skip_energy_attach - -.attach_normally - call AIProcessAndTryToPlayEnergy - -.skip_energy_attach -; play Pokemon from hand again - call AIDecidePlayPokemonCard - ret c ; return if turn ended - ld a, AI_TRAINER_CARD_PHASE_13 - call AIProcessHandTrainerCards -.try_attack -; attack if possible, if not, -; finish turn without attacking. - call AIProcessAndTryToUseAttack - ret c ; return if turn ended - ld a, OPPACTION_FINISH_NO_ATTACK - bank1call AIMakeDecision - ret diff --git a/src/engine/ai/decks/powerful_ronald.asm b/src/engine/ai/decks/powerful_ronald.asm deleted file mode 100644 index 096fbea..0000000 --- a/src/engine/ai/decks/powerful_ronald.asm +++ /dev/null @@ -1,92 +0,0 @@ -AIActionTable_PowerfulRonald: ; 1534b (5:534b) - dw .do_turn ; unused - dw .do_turn - dw .start_duel - dw .forced_switch - dw .ko_switch - dw .take_prize - -.do_turn ; 15357 (5:5357) - call AIMainTurnLogic - ret - -.start_duel ; 1535b (5:535b) - call InitAIDuelVars - call .store_list_pointers - call SetUpBossStartingHandAndDeck - call TrySetUpBossStartingPlayArea - ret nc - call AIPlayInitialBasicCards - ret - -.forced_switch ; 1536c (5:536c) - call AIDecideBenchPokemonToSwitchTo - ret - -.ko_switch ; 15370 (5:5370) - call AIDecideBenchPokemonToSwitchTo - ret - -.take_prize ; 15374 (5:5374) - call AIPickPrizeCards - ret - -.list_arena ; 15378 (5:5378) - db KANGASKHAN - db ELECTABUZZ2 - db HITMONCHAN - db MR_MIME - db LICKITUNG - db HITMONLEE - db TAUROS - db JYNX - db MEWTWO1 - db DODUO - db $00 - -.list_bench ; 15383 (5:5383) - db KANGASKHAN - db HITMONLEE - db HITMONCHAN - db TAUROS - db DODUO - db JYNX - db MEWTWO1 - db ELECTABUZZ2 - db MR_MIME - db LICKITUNG - db $00 - -.list_retreat ; 1538e (5:538e) - ai_retreat KANGASKHAN, -1 - ai_retreat DODUO, -1 - ai_retreat DODRIO, -1 - db $00 - -.list_energy ; 15395 (5:5395) - ai_energy ELECTABUZZ2, 2, +1 - ai_energy HITMONLEE, 3, +1 - ai_energy HITMONCHAN, 3, +1 - ai_energy MR_MIME, 2, +0 - ai_energy JYNX, 3, +0 - ai_energy MEWTWO1, 2, +0 - ai_energy DODUO, 3, -1 - ai_energy DODRIO, 3, -1 - ai_energy LICKITUNG, 2, +0 - ai_energy KANGASKHAN, 4, -1 - ai_energy TAUROS, 3, +0 - db $00 - -.list_prize ; 153b7 (5:53b7) - db GAMBLER - db ENERGY_REMOVAL - db $00 - -.store_list_pointers ; 153ba (5:53ba) - store_list_pointer wAICardListAvoidPrize, .list_prize - store_list_pointer wAICardListArenaPriority, .list_arena - store_list_pointer wAICardListBenchPriority, .list_bench - store_list_pointer wAICardListPlayFromHandPriority, .list_bench - ; missing store_list_pointer wAICardListRetreatBonus, .list_retreat - store_list_pointer wAICardListEnergyBonus, .list_energy - ret diff --git a/src/engine/ai/decks/rock_crusher.asm b/src/engine/ai/decks/rock_crusher.asm deleted file mode 100644 index 41a50fa..0000000 --- a/src/engine/ai/decks/rock_crusher.asm +++ /dev/null @@ -1,74 +0,0 @@ -AIActionTable_RockCrusher: ; 14f0e (5:4f0e) - dw .do_turn ; unused - dw .do_turn - dw .start_duel - dw .forced_switch - dw .ko_switch - dw .take_prize - -.do_turn ; 14f1a (5:4f1a) - call AIMainTurnLogic - ret - -.start_duel ; 14f1e (5:4f1e) - call InitAIDuelVars - call .store_list_pointers - call SetUpBossStartingHandAndDeck - call TrySetUpBossStartingPlayArea - ret nc - call AIPlayInitialBasicCards - ret - -.forced_switch ; 14f2f (5:4f2f) - call AIDecideBenchPokemonToSwitchTo - ret - -.ko_switch ; 14f33 (5:4f33) - call AIDecideBenchPokemonToSwitchTo - ret - -.take_prize ; 14f37 (5:4f37) - call AIPickPrizeCards - ret - -.list_arena ; 14f3b (5:4f3b) - db RHYHORN - db ONIX - db GEODUDE - db DIGLETT - db $00 - -.list_bench ; 14f40 (5:4f40) - db DIGLETT - db GEODUDE - db RHYHORN - db ONIX - db $00 - -.list_retreat ; 14f45 (5:4f45) - ai_retreat DIGLETT, -1 - db $00 - -.list_energy ; 14f48 (5:4f48) - ai_energy DIGLETT, 3, +1 - ai_energy DUGTRIO, 4, +0 - ai_energy GEODUDE, 2, +1 - ai_energy GRAVELER, 3, +0 - ai_energy GOLEM, 4, +0 - ai_energy ONIX, 2, -1 - ai_energy RHYHORN, 3, +0 - db $00 - -.list_prize ; 14f5e (5:4f5e) - db ENERGY_REMOVAL - db RHYHORN - db $00 - -.store_list_pointers ; 14f61 (5:4f61) - store_list_pointer wAICardListAvoidPrize, .list_prize - store_list_pointer wAICardListArenaPriority, .list_arena - store_list_pointer wAICardListBenchPriority, .list_bench - store_list_pointer wAICardListPlayFromHandPriority, .list_bench - ; missing store_list_pointer wAICardListRetreatBonus, .list_retreat - store_list_pointer wAICardListEnergyBonus, .list_energy - ret diff --git a/src/engine/ai/decks/sams_practice.asm b/src/engine/ai/decks/sams_practice.asm deleted file mode 100644 index dddce61..0000000 --- a/src/engine/ai/decks/sams_practice.asm +++ /dev/null @@ -1,205 +0,0 @@ -; AI for Sam's practice duel, which handles his scripted actions. -; will act as a normal duelist AI after turn 7. -AIActionTable_SamPractice: ; 147bd (05:47bd) - dw .do_turn ; unused - dw .do_turn - dw .start_duel - dw .forced_switch - dw .ko_switch - dw .take_prize - -.do_turn ; 147c9 (5:47c9) - call IsAIPracticeScriptedTurn - jr nc, .scripted_1 -; not scripted, use AI main turn logic - call AIMainTurnLogic - ret -.scripted_1 ; use scripted actions instead - call AIPerformScriptedTurn - ret - -.start_duel ; 147d6 (5:47d6) - call SetSamsStartingPlayArea - ret - -.forced_switch ; 147da (5:47da) - call IsAIPracticeScriptedTurn - jr nc, .scripted_2 - call AIDecideBenchPokemonToSwitchTo - ret -.scripted_2 - call PickRandomBenchPokemon - ret - -.ko_switch: ; 147e7 (5:47e7) - call IsAIPracticeScriptedTurn - jr nc, .scripted_3 - call AIDecideBenchPokemonToSwitchTo - ret -.scripted_3 - call GetPlayAreaLocationOfRaticateOrRattata - ret - -.take_prize: ; 147f4 (5:47f4) - call AIPickPrizeCards - ret - -; returns carry if number of turns -; the AI has taken >= 7. -; used to know whether AI Sam is still -; doing scripted turns. -IsAIPracticeScriptedTurn: ; 147f8 (5:47f8) - ld a, [wDuelTurns] - srl a - cp 7 - ccf - ret - -; places one Machop from the hand to the Play Area -; and sets the number of prizes to 2. -SetSamsStartingPlayArea: ; 14801 (5:4801) - call CreateHandCardList - ld hl, wDuelTempList -.loop_hand - ld a, [hli] - ldh [hTempCardIndex_ff98], a - cp $ff - ret z - call LoadCardDataToBuffer1_FromDeckIndex - cp MACHOP - jr nz, .loop_hand - ldh a, [hTempCardIndex_ff98] - call PutHandPokemonCardInPlayArea - ld a, 2 - ld [wDuelInitialPrizes], a - ret - -; outputs in a Play Area location of Raticate or Rattata -; in the Bench. If neither is found, just output PLAY_AREA_BENCH_1. -GetPlayAreaLocationOfRaticateOrRattata: ; 1481f (5:481f) - ld a, RATICATE - ld b, PLAY_AREA_BENCH_1 - call LookForCardIDInPlayArea_Bank5 - cp $ff - jr nz, .found - ld a, RATTATA - ld b, PLAY_AREA_BENCH_1 - call LookForCardIDInPlayArea_Bank5 - cp $ff - jr nz, .found - ld a, PLAY_AREA_BENCH_1 -.found - ldh [hTempPlayAreaLocation_ff9d], a - ret - -; has AI execute some scripted actions depending on Duel turn. -AIPerformScriptedTurn: ; 1483a (5:483a) - ld a, [wDuelTurns] - srl a - ld hl, .scripted_actions_list - call JumpToFunctionInTable - -; always attack with Arena card's first attack. -; if it's unusable end turn without attacking. - xor a - ldh [hTempPlayAreaLocation_ff9d], a - ld [wSelectedAttack], a - call CheckIfSelectedAttackIsUnusable - jr c, .unusable - call AITryUseAttack - ret - -.unusable - ld a, OPPACTION_FINISH_NO_ATTACK - bank1call AIMakeDecision - ret - -.scripted_actions_list ; 1485a (05:485a) - dw .turn_1 - dw .turn_2 - dw .turn_3 - dw .turn_4 - dw .turn_5 - dw .turn_6 - dw .turn_7 - -.turn_1 ; 14868 (5:4868) - ld d, MACHOP - ld e, FIGHTING_ENERGY - call AIAttachEnergyInHandToCardInPlayArea - ret - -.turn_2 ; 14870 (5:4870) - ld a, RATTATA - call LookForCardIDInHandList_Bank5 - ldh [hTemp_ffa0], a - ld a, OPPACTION_PLAY_BASIC_PKMN - bank1call AIMakeDecision - ld d, RATTATA - ld e, FIGHTING_ENERGY - call AIAttachEnergyInHandToCardInPlayArea - ret - -.turn_3 ; 14884 (5:4884) - ld a, RATTATA - ld b, PLAY_AREA_ARENA - call LookForCardIDInPlayArea_Bank5 - ldh [hTempPlayAreaLocation_ffa1], a - ld a, RATICATE - call LookForCardIDInHandList_Bank5 - ldh [hTemp_ffa0], a - ld a, OPPACTION_EVOLVE_PKMN - bank1call AIMakeDecision - ld d, RATICATE - ld e, LIGHTNING_ENERGY - call AIAttachEnergyInHandToCardInPlayArea - ret - -.turn_4 ; 148a1 (5:48a1) - ld d, RATICATE - ld e, LIGHTNING_ENERGY - call AIAttachEnergyInHandToCardInPlayArea - ret - -.turn_5 ; 148a9 (5:48a9) - ld a, MACHOP - call LookForCardIDInHandList_Bank5 - ldh [hTemp_ffa0], a - ld a, OPPACTION_PLAY_BASIC_PKMN - bank1call AIMakeDecision - ld d, MACHOP - ld e, FIGHTING_ENERGY - call AIAttachEnergyInHandToCardInBench - -; this is a bug, it's attempting to compare a card ID with a deck index. -; the intention was to change the card to switch to depending on whether -; the first Machop was KO'd at this point in the Duel or not. -; because of the buggy comparison, this will always jump the -; 'inc a' instruction and switch to PLAY_AREA_BENCH_1. -; in a normal Practice Duel following Dr. Mason's instructions, -; this will always lead to the AI correctly switching Raticate with Machop, -; but in case of a "Free" Duel where the first Machop is not KO'd, -; the intention was to switch to PLAY_AREA_BENCH_2 instead. -; but due to 'inc a' always being skipped, it will switch to Raticate. - ld a, DUELVARS_ARENA_CARD - call GetTurnDuelistVariable - cp MACHOP ; wrong - ld a, PLAY_AREA_BENCH_1 - jr nz, .retreat - inc a ; PLAY_AREA_BENCH_2 - -.retreat - call AITryToRetreat - ret - -.turn_6 ; 148cc (5:48cc) - ld d, MACHOP - ld e, FIGHTING_ENERGY - call AIAttachEnergyInHandToCardInPlayArea - ret - -.turn_7 ; 148d4 (5:48d4) - ld d, MACHOP - ld e, FIGHTING_ENERGY - call AIAttachEnergyInHandToCardInPlayArea - ret diff --git a/src/engine/ai/decks/strange_psyshock.asm b/src/engine/ai/decks/strange_psyshock.asm deleted file mode 100644 index ef378b0..0000000 --- a/src/engine/ai/decks/strange_psyshock.asm +++ /dev/null @@ -1,81 +0,0 @@ -AIActionTable_StrangePsyshock: ; 15122 (5:5122) - dw .do_turn ; unused - dw .do_turn - dw .start_duel - dw .forced_switch - dw .ko_switch - dw .take_prize - -.do_turn ; 1512e (5:512e) - call AIMainTurnLogic - ret - -.start_duel ; 15132 (5:5132) - call InitAIDuelVars - call .store_list_pointers - call SetUpBossStartingHandAndDeck - call TrySetUpBossStartingPlayArea - ret nc - call AIPlayInitialBasicCards - ret - -.forced_switch ; 15143 (5:5143) - call AIDecideBenchPokemonToSwitchTo - ret - -.ko_switch ; 15147 (5:5147) - call AIDecideBenchPokemonToSwitchTo - ret - -.take_prize ; 1514b (5:514b) - call AIPickPrizeCards - ret - -.list_arena ; 1514f (5:514f) - db KANGASKHAN - db CHANSEY - db SNORLAX - db MR_MIME - db ABRA - db $00 - -.list_bench ; 15155 (5:5155) - db ABRA - db MR_MIME - db KANGASKHAN - db SNORLAX - db CHANSEY - db $00 - -.list_retreat ; 1515b (5:515b) - ai_retreat ABRA, -3 - ai_retreat SNORLAX, -3 - ai_retreat KANGASKHAN, -1 - ai_retreat CHANSEY, -1 - db $00 - -.list_energy ; 15164 (5:5164) - ai_energy ABRA, 3, +1 - ai_energy KADABRA, 3, +0 - ai_energy ALAKAZAM, 3, +0 - ai_energy MR_MIME, 2, +0 - ai_energy CHANSEY, 2, -2 - ai_energy KANGASKHAN, 4, -2 - ai_energy SNORLAX, 0, -8 - db $00 - -.list_prize ; 1517a (5:517a) - db GAMBLER - db MR_MIME - db ALAKAZAM - db SWITCH - db $00 - -.store_list_pointers ; 1517f (5:517f) - store_list_pointer wAICardListAvoidPrize, .list_prize - store_list_pointer wAICardListArenaPriority, .list_arena - store_list_pointer wAICardListBenchPriority, .list_bench - store_list_pointer wAICardListPlayFromHandPriority, .list_bench - ; missing store_list_pointer wAICardListRetreatBonus, .list_retreat - store_list_pointer wAICardListEnergyBonus, .list_energy - ret diff --git a/src/engine/ai/decks/unreferenced.asm b/src/engine/ai/decks/unreferenced.asm deleted file mode 100644 index 8722a27..0000000 --- a/src/engine/ai/decks/unreferenced.asm +++ /dev/null @@ -1,42 +0,0 @@ -AIActionTable_Unreferenced: ; 1406a (5:406a) - dw $406c - dw .do_turn - dw .do_turn - dw .star_duel - dw .forced_switch - dw .ko_switch - dw .take_prize - -.do_turn - call AIDecidePlayPokemonCard - call AIDecideWhetherToRetreat - jr nc, .try_attack - call AIDecideBenchPokemonToSwitchTo - call AITryToRetreat - call AIDecideWhetherToRetreat - jr nc, .try_attack - call AIDecideBenchPokemonToSwitchTo - call AITryToRetreat -.try_attack - call AIProcessAndTryToPlayEnergy - call AIProcessAndTryToUseAttack - ret c - ld a, OPPACTION_FINISH_NO_ATTACK - bank1call AIMakeDecision - ret - -.star_duel - call AIPlayInitialBasicCards - ret - -.forced_switch - call AIDecideBenchPokemonToSwitchTo - ret - -.ko_switch - call AIDecideBenchPokemonToSwitchTo - ret - -.take_prize - call AIPickPrizeCards - ret diff --git a/src/engine/ai/decks/wonders_of_science.asm b/src/engine/ai/decks/wonders_of_science.asm deleted file mode 100644 index 706a7e6..0000000 --- a/src/engine/ai/decks/wonders_of_science.asm +++ /dev/null @@ -1,77 +0,0 @@ -AIActionTable_WondersOfScience: ; 151ad (5:51ad) - dw .do_turn ; unused - dw .do_turn - dw .start_duel - dw .forced_switch - dw .ko_switch - dw .take_prize - -.do_turn ; 151b9 (5:51b9) - call AIMainTurnLogic - ret - -.start_duel ; 151bd (5:51bd) - call InitAIDuelVars - call .store_list_pointers - call SetUpBossStartingHandAndDeck - call TrySetUpBossStartingPlayArea - ret nc - call AIPlayInitialBasicCards - ret - -.forced_switch ; 151ce (5:51ce) - call AIDecideBenchPokemonToSwitchTo - ret - -.ko_switch ; 151d2 (5:51d2) - call AIDecideBenchPokemonToSwitchTo - ret - -.take_prize ; 151d6 (5:51d6) - call AIPickPrizeCards - ret - -.list_arena ; 151da (5:51da) - db MEWTWO1 - db MEWTWO3 - db MEWTWO2 - db GRIMER - db KOFFING - db PORYGON - db $00 - -.list_bench ; 151e1 (5:51e1) - db GRIMER - db KOFFING - db MEWTWO3 - db MEWTWO2 - db MEWTWO1 - db PORYGON - db $00 - -.list_retreat ; 151e8 (5:51e8) - db $00 - -.list_energy ; 151e9 (5:51e9) - ai_energy GRIMER, 3, +0 - ai_energy MUK, 4, +0 - ai_energy KOFFING, 2, +0 - ai_energy WEEZING, 3, +0 - ai_energy MEWTWO1, 2, -1 - ai_energy MEWTWO3, 2, -1 - ai_energy MEWTWO2, 2, -1 - ai_energy PORYGON, 2, -1 - db $00 - -.list_prize ; 15202 (5:5202) - db MUK - db $00 - -.store_list_pointers ; 15204 (5:5204) - store_list_pointer wAICardListAvoidPrize, .list_prize - store_list_pointer wAICardListArenaPriority, .list_arena - store_list_pointer wAICardListBenchPriority, .list_bench - store_list_pointer wAICardListPlayFromHandPriority, .list_bench - ; missing store_list_pointer wAICardListRetreatBonus, .list_retreat - store_list_pointer wAICardListEnergyBonus, .list_energy - ret diff --git a/src/engine/ai/decks/zapping_selfdestruct.asm b/src/engine/ai/decks/zapping_selfdestruct.asm deleted file mode 100644 index da5e7c6..0000000 --- a/src/engine/ai/decks/zapping_selfdestruct.asm +++ /dev/null @@ -1,75 +0,0 @@ -AIActionTable_ZappingSelfdestruct: ; 15019 (5:5019) - dw .do_turn ; unused - dw .do_turn - dw .start_duel - dw .forced_switch - dw .ko_switch - dw .take_prize - -.do_turn ; 15025 (5:5025) - call AIMainTurnLogic - ret - -.start_duel ; 15029 (5:5029) - call InitAIDuelVars - call .store_list_pointers - call SetUpBossStartingHandAndDeck - call TrySetUpBossStartingPlayArea - ret nc - call AIPlayInitialBasicCards - ret - -.forced_switch ; 1503a (5:503a) - call AIDecideBenchPokemonToSwitchTo - ret - -.ko_switch ; 1503e (5:503e) - call AIDecideBenchPokemonToSwitchTo - ret - -.take_prize ; 15042 (5:5042) - call AIPickPrizeCards - ret - -.list_arena ; 15046 (5:5046) - db KANGASKHAN - db ELECTABUZZ2 - db TAUROS - db MAGNEMITE1 - db VOLTORB - db $00 - -.list_bench ; 1504c (5:504c) - db MAGNEMITE1 - db VOLTORB - db ELECTABUZZ2 - db TAUROS - db KANGASKHAN - db $00 - -.list_retreat ; 15052 (5:5052) - ai_retreat VOLTORB, -1 - db $00 - -.list_energy ; 15055 (5:5055) - ai_energy MAGNEMITE1, 3, +1 - ai_energy MAGNETON1, 4, +0 - ai_energy VOLTORB, 3, +1 - ai_energy ELECTRODE1, 3, +0 - ai_energy ELECTABUZZ2, 1, +0 - ai_energy KANGASKHAN, 2, -2 - ai_energy TAUROS, 3, +0 - db $00 - -.list_prize ; 1506b (5:506b) - db KANGASKHAN - db $00 - -.store_list_pointers ; 1506d (5:506d) - store_list_pointer wAICardListAvoidPrize, .list_prize - store_list_pointer wAICardListArenaPriority, .list_arena - store_list_pointer wAICardListBenchPriority, .list_bench - store_list_pointer wAICardListPlayFromHandPriority, .list_bench - ; missing store_list_pointer wAICardListRetreatBonus, .list_retreat - store_list_pointer wAICardListEnergyBonus, .list_energy - ret diff --git a/src/engine/ai/energy.asm b/src/engine/ai/energy.asm deleted file mode 100644 index ce8c037..0000000 --- a/src/engine/ai/energy.asm +++ /dev/null @@ -1,1048 +0,0 @@ -; processes AI energy card playing logic -; with AI_ENERGY_FLAG_DONT_PLAY flag on -; unreferenced -Func_16488: ; 16488 (5:6488) - ld a, AI_ENERGY_FLAG_DONT_PLAY - ld [wAIEnergyAttachLogicFlags], a - ld de, wTempPlayAreaAIScore - ld hl, wPlayAreaAIScore - ld b, MAX_PLAY_AREA_POKEMON -.loop - ld a, [hli] - ld [de], a - inc de - dec b - jr nz, .loop - ld a, [wAIScore] - ld [de], a - jr AIProcessAndTryToPlayEnergy.has_logic_flags - -; have AI choose an energy card to play, but do not play it. -; does not consider whether the cards have evolutions to be played. -; return carry if an energy card is chosen to use in any Play Area card, -; and if so, return its Play Area location in hTempPlayAreaLocation_ff9d. -AIProcessButDontPlayEnergy_SkipEvolution: ; 164a1 (5:64a1) - ld a, AI_ENERGY_FLAG_DONT_PLAY | AI_ENERGY_FLAG_SKIP_EVOLUTION - ld [wAIEnergyAttachLogicFlags], a - -; backup wPlayAreaAIScore in wTempPlayAreaAIScore. - ld de, wTempPlayAreaAIScore - ld hl, wPlayAreaAIScore - ld b, MAX_PLAY_AREA_POKEMON -.loop - ld a, [hli] - ld [de], a - inc de - dec b - jr nz, .loop - - ld a, [wAIScore] - ld [de], a - - jr AIProcessEnergyCards - -; have AI choose an energy card to play, but do not play it. -; does not consider whether the cards have evolutions to be played. -; return carry if an energy card is chosen to use in any Bench card, -; and if so, return its Play Area location in hTempPlayAreaLocation_ff9d. -AIProcessButDontPlayEnergy_SkipEvolutionAndArena: ; 164ba (5:64ba) - ld a, AI_ENERGY_FLAG_DONT_PLAY | AI_ENERGY_FLAG_SKIP_EVOLUTION | AI_ENERGY_FLAG_SKIP_ARENA_CARD - ld [wAIEnergyAttachLogicFlags], a - -; backup wPlayAreaAIScore in wTempPlayAreaAIScore. - ld de, wTempPlayAreaAIScore - ld hl, wPlayAreaAIScore - ld b, MAX_PLAY_AREA_POKEMON -.loop - ld a, [hli] - ld [de], a - inc de - dec b - jr nz, .loop - - ld a, [wAIScore] - ld [de], a - - jr AIProcessEnergyCards - -; copies wTempPlayAreaAIScore to wPlayAreaAIScore -; and loads wAIScore with value in wTempAIScore. -; identical to RetrievePlayAreaAIScoreFromBackup2. -RetrievePlayAreaAIScoreFromBackup1: ; 164d3 (5:64d3) - push af - ld de, wPlayAreaAIScore - ld hl, wTempPlayAreaAIScore - ld b, MAX_PLAY_AREA_POKEMON -.loop - ld a, [hli] - ld [de], a - inc de - dec b - jr nz, .loop - ld a, [hl] - ld [wAIScore], a - pop af - ret - -; have AI decide whether to play energy card from hand -; and determine which card is best to attach it. -AIProcessAndTryToPlayEnergy: ; 164e8 (5:64e8) - xor a - ld [wAIEnergyAttachLogicFlags], a - -.has_logic_flags - call CreateEnergyCardListFromHand - jr nc, AIProcessEnergyCards - -; no energy - ld a, [wAIEnergyAttachLogicFlags] - or a - jr z, .exit - jp RetrievePlayAreaAIScoreFromBackup1 -.exit - or a - ret - -; have AI decide whether to play energy card -; and determine which card is best to attach it. -AIProcessEnergyCards: ; 164fc (5:64fc) -; initialize Play Area AI score - ld a, $80 - ld b, MAX_PLAY_AREA_POKEMON - ld hl, wPlayAreaEnergyAIScore -.loop - ld [hli], a - dec b - jr nz, .loop - -; Legendary Articuno Deck has its own energy card logic - call HandleLegendaryArticunoEnergyScoring - -; start the main Play Area loop - ld b, PLAY_AREA_ARENA - ld a, DUELVARS_NUMBER_OF_POKEMON_IN_PLAY_AREA - call GetTurnDuelistVariable - ld c, a - -.loop_play_area - push bc - ld a, b - ldh [hTempPlayAreaLocation_ff9d], a - ld a, $80 - ld [wAIScore], a - ld a, $ff - ld [wTempAI], a - ld a, [wAIEnergyAttachLogicFlags] - and AI_ENERGY_FLAG_SKIP_EVOLUTION - jr nz, .check_venusaur - -; check if energy needed is found in hand -; and if there's an evolution in hand or deck -; and if so, add to AI score - call CreateHandCardList - ldh a, [hTempPlayAreaLocation_ff9d] - add DUELVARS_ARENA_CARD - call GetTurnDuelistVariable - ld [wCurCardCanAttack], a - call GetAttacksEnergyCostBits - ld hl, wDuelTempList - call CheckEnergyFlagsNeededInList - jp nc, .store_score - ld a, [wCurCardCanAttack] - call CheckForEvolutionInList - jr nc, .no_evolution_in_hand - ld [wTempAI], a ; store evolution card found - ld a, 2 - call AddToAIScore - jr .check_venusaur - -.no_evolution_in_hand - ld a, [wCurCardCanAttack] - call CheckForEvolutionInDeck - jr nc, .check_venusaur - ld a, 1 - call AddToAIScore - -; if there's no Muk in any Play Area -; and there's Venusaur2 in own Play Area, -; add to AI score -.check_venusaur - ld a, MUK - call CountPokemonIDInBothPlayAreas - jr c, .check_if_active - ld a, VENUSAUR2 - call CountPokemonIDInPlayArea - jr nc, .check_if_active - ld a, 1 - call AddToAIScore - -.check_if_active - ldh a, [hTempPlayAreaLocation_ff9d] - or a - jr nz, .bench - -; arena - ld a, [wAIBarrierFlagCounter] - bit AI_MEWTWO_MILL_F, a - jr z, .add_to_score - -; subtract from score instead -; if Player is running Mewtwo1 mill deck. - ld a, 5 - call SubFromAIScore - jr .check_defending_can_ko - -.add_to_score - ld a, 4 - call AddToAIScore - -; lower AI score if poison/double poison -; will KO Pokémon between turns -; or if the defending Pokémon can KO - ld a, DUELVARS_ARENA_CARD_HP - call GetTurnDuelistVariable - call CalculateByteTensDigit - cp 3 - jr nc, .check_defending_can_ko - ; hp < 30 - cp 2 - jr z, .has_20_hp - ; hp = 10 - ld a, DUELVARS_ARENA_CARD_STATUS - call GetTurnDuelistVariable - and POISONED - jr z, .check_defending_can_ko - jr .poison_will_ko -.has_20_hp - ld a, DUELVARS_ARENA_CARD_STATUS - call GetTurnDuelistVariable - and DOUBLE_POISONED - jr z, .check_defending_can_ko -.poison_will_ko - ld a, 10 - call SubFromAIScore - jr .check_bench -.check_defending_can_ko - call CheckIfDefendingPokemonCanKnockOut - jr nc, .ai_score_bonus - ld a, 10 - call SubFromAIScore - -; if either poison will KO or defending Pokémon can KO, -; check if there are bench Pokémon, -; if there are not, add AI score -.check_bench - ld a, DUELVARS_NUMBER_OF_POKEMON_IN_PLAY_AREA - call GetTurnDuelistVariable - dec a - jr nz, .ai_score_bonus - ld a, 6 - call AddToAIScore - jr .ai_score_bonus - -; lower AI score by 3 - (bench HP)/10 -; if bench HP < 30 -.bench - add DUELVARS_ARENA_CARD_HP - call GetTurnDuelistVariable - call CalculateByteTensDigit - cp 3 - jr nc, .ai_score_bonus -; hp < 30 - ld b, a - ld a, 3 - sub b - call SubFromAIScore - -; check list in wAICardListEnergyBonus -.ai_score_bonus - ld a, [wAICardListEnergyBonus + 1] - or a - jr z, .check_boss_deck ; is null - ld h, a - ld a, [wAICardListEnergyBonus] - ld l, a - - push hl - ldh a, [hTempPlayAreaLocation_ff9d] - add DUELVARS_ARENA_CARD - call GetTurnDuelistVariable - call GetCardIDFromDeckIndex - pop hl - -.loop_id_list - ld a, [hli] - or a - jr z, .check_boss_deck - cp e - jr nz, .next_id - - ; number of attached energy cards - ld a, [hli] - ld d, a - push de - ldh a, [hTempPlayAreaLocation_ff9d] - ld e, a - call GetPlayAreaCardAttachedEnergies - ld a, [wTotalAttachedEnergies] - pop de - cp d - jr c, .check_id_score - ; already reached target number of energy cards - ld a, 10 - call SubFromAIScore - jr .store_score - -.check_id_score - ld a, [hli] - cp $80 - jr c, .decrease_score_1 - sub $80 - call AddToAIScore - jr .check_boss_deck - -.decrease_score_1 - ld d, a - ld a, $80 - sub d - call SubFromAIScore - jr .check_boss_deck - -.next_id - inc hl - inc hl - jr .loop_id_list - -; if it's a boss deck, call Func_174f2 -; and apply to the AI score the values -; determined for this card -.check_boss_deck - call CheckIfNotABossDeckID - jr c, .skip_boss_deck - - call Func_174f2 - ldh a, [hTempPlayAreaLocation_ff9d] - ld c, a - ld b, $00 - ld hl, wPlayAreaEnergyAIScore - add hl, bc - ld a, [hl] - cp $80 - jr c, .decrease_score_2 - sub $80 - call AddToAIScore - jr .skip_boss_deck - -.decrease_score_2 - ld b, a - ld a, $80 - sub b - call SubFromAIScore - -.skip_boss_deck - ld a, 1 - call AddToAIScore - -; add AI score for both attacks, -; according to their energy requirements. - xor a ; first attack - call DetermineAIScoreOfAttackEnergyRequirement - ld a, SECOND_ATTACK - call DetermineAIScoreOfAttackEnergyRequirement - -; store bench score for this card. -.store_score - ldh a, [hTempPlayAreaLocation_ff9d] - ld c, a - ld b, $00 - ld hl, wPlayAreaAIScore - add hl, bc - ld a, [wAIScore] - ld [hl], a - pop bc - inc b - dec c - jp nz, .loop_play_area - -; the Play Area loop is over and the score -; for each card has been calculated. -; now to determine the highest score. - call FindPlayAreaCardWithHighestAIScore - jp nc, .not_found - - ld a, [wAIEnergyAttachLogicFlags] - or a - jr z, .play_card - scf - jp RetrievePlayAreaAIScoreFromBackup1 - -.play_card - call CreateEnergyCardListFromHand - jp AITryToPlayEnergyCard - -.not_found: ; 1668a (5:668a) - ld a, [wAIEnergyAttachLogicFlags] - or a - jr z, .no_carry - jp RetrievePlayAreaAIScoreFromBackup1 -.no_carry - or a - ret - -; checks score related to selected attack, -; in order to determine whether to play energy card. -; the AI score is increased/decreased accordingly. -; input: -; [wSelectedAttack] = attack to check. -DetermineAIScoreOfAttackEnergyRequirement: ; 16695 (5:6695) - ld [wSelectedAttack], a - call CheckEnergyNeededForAttack - jp c, .not_enough_energy - ld a, ATTACK_FLAG2_ADDRESS | ATTACHED_ENERGY_BOOST_F - call CheckLoadedAttackFlag - jr c, .attached_energy_boost - ld a, ATTACK_FLAG2_ADDRESS | DISCARD_ENERGY_F - call CheckLoadedAttackFlag - jr c, .discard_energy - jp .check_evolution - -.attached_energy_boost - ld a, [wLoadedAttackEffectParam] - cp MAX_ENERGY_BOOST_IS_LIMITED - jr z, .check_surplus_energy - - ; is MAX_ENERGY_BOOST_IS_NOT_LIMITED, - ; which is equal to 3, add to score. - call AddToAIScore - jp .check_evolution - -.check_surplus_energy - call CheckIfNoSurplusEnergyForAttack - jr c, .asm_166cd - cp 3 ; check how much surplus energy - jr c, .asm_166cd - -.asm_166c5 - ld a, 5 - call SubFromAIScore - jp .check_evolution - -.asm_166cd - ld a, 2 - call AddToAIScore - -; check whether attack has ATTACHED_ENERGY_BOOST flag -; and add to AI score if attaching another energy -; will KO defending Pokémon. -; add more to score if this is currently active Pokémon. - ld a, ATTACK_FLAG2_ADDRESS | ATTACHED_ENERGY_BOOST_F - call CheckLoadedAttackFlag - jp nc, .check_evolution - ld a, [wSelectedAttack] - call EstimateDamage_VersusDefendingCard - ld a, DUELVARS_ARENA_CARD_HP - call GetNonTurnDuelistVariable - ld hl, wDamage - sub [hl] - jp c, .check_evolution - jp z, .check_evolution - ld a, [wDamage] - add 10 ; boost gained by attaching another energy card - ld b, a - ld a, DUELVARS_ARENA_CARD_HP - call GetNonTurnDuelistVariable - sub b - jr c, .attaching_kos_player - jr nz, .check_evolution - -.attaching_kos_player - ld a, 20 - call AddToAIScore - ldh a, [hTempPlayAreaLocation_ff9d] - or a - jr nz, .check_evolution - ld a, 10 - call AddToAIScore - jr .check_evolution - -; checks if there is surplus energy for attack -; that discards attached energy card. -; if current card is Zapdos2, don't add to score. -; if there is no surplus energy, encourage playing energy. -.discard_energy - ld a, [wLoadedCard1ID] - cp ZAPDOS2 - jr z, .check_evolution - call CheckIfNoSurplusEnergyForAttack - jr c, .asm_166cd - jr .asm_166c5 - -.not_enough_energy - ld a, ATTACK_FLAG2_ADDRESS | FLAG_2_BIT_5_F - call CheckLoadedAttackFlag - jr nc, .check_color_needed - ld a, 5 - call SubFromAIScore - -; if the energy card color needed is in hand, increase AI score. -; if a colorless card is needed, increase AI score. -.check_color_needed - ld a, b - or a - jr z, .check_colorless_needed - ld a, e - call LookForCardIDInHand - jr c, .check_colorless_needed - ld a, 4 - call AddToAIScore - jr .check_total_needed -.check_colorless_needed - ld a, c - or a - jr z, .check_evolution - ld a, 3 - call AddToAIScore - -; if only one energy card is needed for attack, -; encourage playing energy card. -.check_total_needed - ld a, b - add c - dec a - jr nz, .check_evolution - ld a, 3 - call AddToAIScore - -; if the attack KOs player and this is the active card, add to AI score. - ldh a, [hTempPlayAreaLocation_ff9d] - or a - jr nz, .check_evolution - ld a, [wSelectedAttack] - call EstimateDamage_VersusDefendingCard - ld a, DUELVARS_ARENA_CARD_HP - call GetNonTurnDuelistVariable - ld hl, wDamage - sub [hl] - jr z, .atk_kos_defending - jr nc, .check_evolution -.atk_kos_defending - ld a, 20 - call AddToAIScore - -; this is possibly a bug. -; this is an identical check as above to test whether this card is active. -; in case it is active, the score gets added 10 more points, -; in addition to the 20 points already added above. -; what was probably intended was to add 20 points -; plus 10 in case it is the Arena card. - ldh a, [hTempPlayAreaLocation_ff9d] - or a - jr nz, .check_evolution - ld a, 10 - call AddToAIScore - -.check_evolution - ld a, [wTempAI] ; evolution in hand - cp $ff - ret z - -; temporarily replace this card with evolution in hand. - ld b, a - ldh a, [hTempPlayAreaLocation_ff9d] - add DUELVARS_ARENA_CARD - call GetTurnDuelistVariable - push af - ld [hl], b - -; check for energy still needed for evolution to attack. -; if FLAG_2_BIT_5 is not set, check what color is needed. -; if the energy card color needed is in hand, increase AI score. -; if a colorless card is needed, increase AI score. - call CheckEnergyNeededForAttack - jr nc, .done - ld a, ATTACK_FLAG2_ADDRESS | FLAG_2_BIT_5_F - call CheckLoadedAttackFlag - jr c, .done - ld a, b - or a - jr z, .check_colorless_needed_evo - ld a, e - call LookForCardIDInHand - jr c, .check_colorless_needed_evo - ld a, 2 - call AddToAIScore - jr .done -.check_colorless_needed_evo - ld a, c - or a - jr z, .done - ld a, 1 - call AddToAIScore - -; recover the original card in the Play Area location. -.done - ldh a, [hTempPlayAreaLocation_ff9d] - add DUELVARS_ARENA_CARD - call GetTurnDuelistVariable - pop af - ld [hl], a - ret - -; returns in hTempPlayAreaLocation_ff9d the Play Area location -; of the card with the highest Play Area AI score, unless -; the highest score is below $85. -; if it succeeds in return a card location, set carry. -; if AI_ENERGY_FLAG_SKIP_ARENA_CARD is set in wAIEnergyAttachLogicFlags -; doesn't include the Arena card and there's no minimum score. -FindPlayAreaCardWithHighestAIScore: ; 167b5 (5:67b5) - ld a, [wAIEnergyAttachLogicFlags] - and AI_ENERGY_FLAG_SKIP_ARENA_CARD - jr nz, .only_bench - - ld a, DUELVARS_NUMBER_OF_POKEMON_IN_PLAY_AREA - call GetTurnDuelistVariable - ld b, a - ld c, PLAY_AREA_ARENA - ld e, c - ld d, c - ld hl, wPlayAreaAIScore -; find highest Play Area AI score. -.loop_1 - ld a, [hli] - cp e - jr c, .next_1 - jr z, .next_1 - ld e, a ; overwrite highest score found - ld d, c ; overwrite Play Area of highest score -.next_1 - inc c - dec b - jr nz, .loop_1 - -; if highest AI score is below $85, return no carry. -; else, store Play Area location and return carry. - ld a, e - cp $85 - jr c, .not_enough_score - ld a, d - ldh [hTempPlayAreaLocation_ff9d], a - scf - ret -.not_enough_score - or a - ret - -; same as above but only check bench Pokémon scores. -.only_bench - ld a, DUELVARS_NUMBER_OF_POKEMON_IN_PLAY_AREA - call GetTurnDuelistVariable - dec a - jr z, .no_carry - - ld b, a - ld e, 0 - ld c, PLAY_AREA_BENCH_1 - ld d, c - ld hl, wPlayAreaAIScore + 1 -.loop_2 - ld a, [hli] - cp e - jr c, .next_2 - jr z, .next_2 - ld e, a ; overwrite highest score found - ld d, c ; overwrite Play Area of highest score -.next_2 - inc c - dec b - jr nz, .loop_2 - -; in this case, there is no minimum threshold AI score. - ld a, d - ldh [hTempPlayAreaLocation_ff9d], a - scf - ret -.no_carry - or a - ret - -; returns carry if there's an evolution card -; that can evolve card in hTempPlayAreaLocation_ff9d, -; and that card needs energy to use wSelectedAttack. -CheckIfEvolutionNeedsEnergyForAttack: ; 16805 (5:6805) - call CreateHandCardList - ldh a, [hTempPlayAreaLocation_ff9d] - add DUELVARS_ARENA_CARD - call GetTurnDuelistVariable - call CheckCardEvolutionInHandOrDeck - jr c, .has_evolution - or a - ret - -.has_evolution - ld b, a - ldh a, [hTempPlayAreaLocation_ff9d] - add DUELVARS_ARENA_CARD - call GetTurnDuelistVariable - push af - ld [hl], b - call CheckEnergyNeededForAttack - jr c, .not_enough_energy - ldh a, [hTempPlayAreaLocation_ff9d] - add DUELVARS_ARENA_CARD - call GetTurnDuelistVariable - pop af - ld [hl], a - or a - ret - -.not_enough_energy - ldh a, [hTempPlayAreaLocation_ff9d] - add DUELVARS_ARENA_CARD - call GetTurnDuelistVariable - pop af - ld [hl], a - scf - ret - -; returns in e the card ID of the energy required for -; the Discard/Energy Boost attack loaded in wSelectedAttack. -; if it's Zapdos2's Thunderbolt attack, return no carry. -; if it's Charizard's Fire Spin or Exeggutor's Big Eggsplosion -; attack, don't return energy card ID, but set carry. -; output: -; b = 1 if needs color energy, 0 otherwise; -; c = 1 if only needs colorless energy, 0 otherwise; -; carry set if not Zapdos2's Thunderbolt attack. -GetEnergyCardForDiscardOrEnergyBoostAttack: ; 1683b (5:683b) -; load card ID and check selected attack index. - ldh a, [hTempPlayAreaLocation_ff9d] - add DUELVARS_ARENA_CARD - call GetTurnDuelistVariable - call LoadCardDataToBuffer2_FromDeckIndex - ld b, a - ld a, [wSelectedAttack] - or a - jr z, .first_attack - -; check if second attack is Zapdos2's Thunderbolt, -; Charizard's Fire Spin or Exeggutor's Big Eggsplosion, -; for these to be treated differently. -; for both attacks, load its energy cost. - ld a, b - cp ZAPDOS2 - jr z, .zapdos2 - cp CHARIZARD - jr z, .charizard_or_exeggutor - cp EXEGGUTOR - jr z, .charizard_or_exeggutor - ld hl, wLoadedCard2Atk2EnergyCost - jr .fire -.first_attack - ld hl, wLoadedCard2Atk1EnergyCost - -; check which energy color the attack requires, -; and load in e the card ID of corresponding energy card, -; then return carry flag set. -.fire - ld a, [hli] - ld b, a - and $f0 - jr z, .grass - ld e, FIRE_ENERGY - jr .set_carry -.grass - ld a, b - and $0f - jr z, .lightning - ld e, GRASS_ENERGY - jr .set_carry -.lightning - ld a, [hli] - ld b, a - and $f0 - jr z, .water - ld e, LIGHTNING_ENERGY - jr .set_carry -.water - ld a, b - and $0f - jr z, .fighting - ld e, WATER_ENERGY - jr .set_carry -.fighting - ld a, [hli] - ld b, a - and $f0 - jr z, .psychic - ld e, FIGHTING_ENERGY - jr .set_carry -.psychic - ld e, PSYCHIC_ENERGY - -.set_carry - lb bc, $01, $00 - scf - ret - -; for Zapdos2's Thunderbolt attack, return with no carry. -.zapdos2 - or a - ret - -; Charizard's Fire Spin and Exeggutor's Big Eggsplosion, -; return carry. -.charizard_or_exeggutor - lb bc, $00, $01 - scf - ret - -; called after the AI has decided which card to attach -; energy from hand. AI does checks to determine whether -; this card needs more energy or not, and chooses the -; right energy card to play. If the card is played, -; return with carry flag set. -AITryToPlayEnergyCard: ; 1689f (5:689f) -; check if energy cards are still needed for attacks. -; if first attack doesn't need, test for the second attack. - xor a - ld [wTempAI], a - ld [wSelectedAttack], a - call CheckEnergyNeededForAttack - jr nc, .second_attack - ld a, b - or a - jr nz, .check_deck - ld a, c - or a - jr nz, .check_deck - -.second_attack - ld a, SECOND_ATTACK - ld [wSelectedAttack], a - call CheckEnergyNeededForAttack - jr nc, .check_discard_or_energy_boost - ld a, b - or a - jr nz, .check_deck - ld a, c - or a - jr nz, .check_deck - -; neither attack needs energy cards to be used. -; check whether these attacks can be given -; extra energy cards for their effects. -.check_discard_or_energy_boost - ld a, $01 - ld [wTempAI], a - -; for both attacks, check if it has the effect of -; discarding energy cards or attached energy boost. - xor a ; FIRST_ATTACK_OR_PKMN_POWER - ld [wSelectedAttack], a - call CheckEnergyNeededForAttack - ld a, ATTACK_FLAG2_ADDRESS | ATTACHED_ENERGY_BOOST_F - call CheckLoadedAttackFlag - jr c, .energy_boost_or_discard_energy - ld a, ATTACK_FLAG2_ADDRESS | DISCARD_ENERGY_F - call CheckLoadedAttackFlag - jr c, .energy_boost_or_discard_energy - - ld a, SECOND_ATTACK - ld [wSelectedAttack], a - call CheckEnergyNeededForAttack - ld a, ATTACK_FLAG2_ADDRESS | ATTACHED_ENERGY_BOOST_F - call CheckLoadedAttackFlag - jr c, .energy_boost_or_discard_energy - ld a, ATTACK_FLAG2_ADDRESS | DISCARD_ENERGY_F - call CheckLoadedAttackFlag - jr c, .energy_boost_or_discard_energy - -; if none of the attacks have those flags, do an additional -; check to ascertain whether evolution card needs energy -; to use second attack. Return if all these checks fail. - call CheckIfEvolutionNeedsEnergyForAttack - ret nc - call CreateEnergyCardListFromHand - jr .check_deck - -; for attacks that discard energy or get boost for -; additional energy cards, get the energy card ID required by attack. -; if it's Zapdos2's Thunderbolt attack, return. -.energy_boost_or_discard_energy - call GetEnergyCardForDiscardOrEnergyBoostAttack - ret nc - -; some decks allow basic Pokémon to be given double colorless -; in anticipation for evolution, so play card if that is the case. -.check_deck - call CheckSpecificDecksToAttachDoubleColorless - jr c, .play_energy_card - - ld a, b - or a - jr z, .colorless_energy - -; in this case, Pokémon needs a specific basic energy card. -; look for basic energy card needed in hand and play it. - ld a, e - call LookForCardIDInHand - ldh [hTemp_ffa0], a - jr nc, .play_energy_card - -; in this case Pokémon just needs colorless (any basic energy card). -; if active card, check if it needs 2 colorless. -; if it does (and also doesn't additionally need a color energy), -; look for double colorless card in hand and play it if found. -.colorless_energy - ldh a, [hTempPlayAreaLocation_ff9d] - or a - jr nz, .look_for_any_energy - ld a, c - or a - jr z, .check_if_done - cp 2 - jr nz, .look_for_any_energy - - ; needs two colorless - ld hl, wDuelTempList -.loop_1 - ld a, [hli] - cp $ff - jr z, .look_for_any_energy - ldh [hTemp_ffa0], a - call GetCardIDFromDeckIndex - ld a, e - cp DOUBLE_COLORLESS_ENERGY - jr nz, .loop_1 - jr .play_energy_card - -; otherwise, look for any card and play it. -; if it's a boss deck, only play double colorless in this situation. -.look_for_any_energy - ld hl, wDuelTempList - call CountCardsInDuelTempList - call ShuffleCards -.loop_2 - ld a, [hli] - cp $ff - jr z, .check_if_done - call CheckIfOpponentHasBossDeckID - jr nc, .load_card - push af - call GetCardIDFromDeckIndex - ld a, e - cp DOUBLE_COLORLESS_ENERGY - pop bc - jr z, .loop_2 - ld a, b -.load_card - ldh [hTemp_ffa0], a - -; plays energy card loaded in hTemp_ffa0 and sets carry flag. -.play_energy_card - ldh a, [hTempPlayAreaLocation_ff9d] - ldh [hTempPlayAreaLocation_ffa1], a - ld a, OPPACTION_PLAY_ENERGY - bank1call AIMakeDecision - scf - ret - -; wTempAI is 1 if the attack had a Discard/Energy Boost effect, -; and 0 otherwise. If 1, then return. If not one, check if -; there is still a second attack to check. -.check_if_done - ld a, [wTempAI] - or a - jr z, .check_first_attack - ret -.check_first_attack - ld a, [wSelectedAttack] - or a - jp z, .second_attack - ret - -; check if playing certain decks so that AI can decide whether to play -; double colorless to some specific cards. -; these are cards that do not need double colorless to any of their attacks -; but are required by their evolutions. -; return carry if there's a double colorless in hand to attach -; and it's one of the card IDs from these decks. -; output: -; [hTemp_ffa0] = card index of double colorless in hand; -; carry set if can play energy card. -CheckSpecificDecksToAttachDoubleColorless: ; 1696e (5:696e) - push bc - push de - push hl - -; check if AI is playing any of the applicable decks. - ld a, [wOpponentDeckID] - cp LEGENDARY_DRAGONITE_DECK_ID - jr z, .legendary_dragonite_deck - cp FIRE_CHARGE_DECK_ID - jr z, .fire_charge_deck - cp LEGENDARY_RONALD_DECK_ID - jr z, .legendary_ronald_deck - -.no_carry - pop hl - pop de - pop bc - or a - ret - -; if playing Legendary Dragonite deck, -; check for Charmander and Dratini. -.legendary_dragonite_deck - call .get_id - cp CHARMANDER - jr z, .check_colorless_attached - cp DRATINI - jr z, .check_colorless_attached - jr .no_carry - -; if playing Fire Charge deck, -; check for Growlithe. -.fire_charge_deck - call .get_id - cp GROWLITHE - jr z, .check_colorless_attached - jr .no_carry - -; if playing Legendary Ronald deck, -; check for Dratini. -.legendary_ronald_deck - call .get_id - cp DRATINI - jr z, .check_colorless_attached - jr .no_carry - -; check if card has any colorless energy cards attached, -; and if there are any, return no carry. -.check_colorless_attached - ldh a, [hTempPlayAreaLocation_ff9d] - ld e, a - call GetPlayAreaCardAttachedEnergies - ld a, [wAttachedEnergies + COLORLESS] - or a - jr nz, .no_carry - -; card has no colorless energy, so look for double colorless -; in hand and if found, return carry and its card index. - ld a, DOUBLE_COLORLESS_ENERGY - call LookForCardIDInHand - jr c, .no_carry - ldh [hTemp_ffa0], a - pop hl - pop de - pop bc - scf - ret - -.get_id: - ldh a, [hTempPlayAreaLocation_ff9d] - add DUELVARS_ARENA_CARD - call GetTurnDuelistVariable - call GetCardIDFromDeckIndex - ld a, e - ret diff --git a/src/engine/ai/hand_pokemon.asm b/src/engine/ai/hand_pokemon.asm deleted file mode 100644 index 27a4176..0000000 --- a/src/engine/ai/hand_pokemon.asm +++ /dev/null @@ -1,627 +0,0 @@ -; determine whether AI plays -; basic cards from hand -AIDecidePlayPokemonCard: ; 15eae (5:5eae) - call CreateHandCardList - call SortTempHandByIDList - ld hl, wDuelTempList - ld de, wHandTempList - call CopyHandCardList - ld hl, wHandTempList - -.next_hand_card - ld a, [hli] - cp $ff - jp z, AIDecideEvolution - - ld [wTempAIPokemonCard], a - push hl - call LoadCardDataToBuffer1_FromDeckIndex - ld a, [wLoadedCard1Type] - cp TYPE_ENERGY - jr nc, .skip - ; skip non-pokemon cards - - ld a, [wLoadedCard1Stage] - or a - jr nz, .skip - ; skip non-basic pokemon - - ld a, 130 - ld [wAIScore], a - call AIDecidePlayLegendaryBirds - -; if Play Area has more than 4 Pokémon, decrease AI score -; else, increase AI score - ld a, DUELVARS_NUMBER_OF_POKEMON_IN_PLAY_AREA - call GetTurnDuelistVariable - cp 4 - jr c, .has_4_or_fewer - ld a, 20 - call SubFromAIScore - jr .check_defending_can_ko -.has_4_or_fewer - ld a, 50 - call AddToAIScore - -; if defending Pokémon can KO active card, increase AI score -.check_defending_can_ko - xor a - ldh [hTempPlayAreaLocation_ff9d], a - call CheckIfDefendingPokemonCanKnockOut - jr nc, .check_energy_cards - ld a, 20 - call AddToAIScore - -; if energy cards are found in hand -; for this card's attacks, raise AI score -.check_energy_cards - ld a, [wTempAIPokemonCard] - call GetAttacksEnergyCostBits - call CheckEnergyFlagsNeededInList - jr nc, .check_evolution_hand - ld a, 20 - call AddToAIScore - -; if evolution card is found in hand -; for this card, raise AI score -.check_evolution_hand - ld a, [wTempAIPokemonCard] - call CheckForEvolutionInList - jr nc, .check_evolution_deck - ld a, 20 - call AddToAIScore - -; if evolution card is found in deck -; for this card, raise AI score -.check_evolution_deck - ld a, [wTempAIPokemonCard] - call CheckForEvolutionInDeck - jr nc, .check_score - ld a, 10 - call AddToAIScore - -; if AI score is >= 180, play card from hand -.check_score - ld a, [wAIScore] - cp 180 - jr c, .skip - ld a, [wTempAIPokemonCard] - ldh [hTemp_ffa0], a - call CheckIfCardCanBePlayed - jr c, .skip - ld a, OPPACTION_PLAY_BASIC_PKMN - bank1call AIMakeDecision - jr c, .done -.skip - pop hl - jp .next_hand_card -.done - pop hl - ret - -; determine whether AI evolves -; Pokémon in the Play Area -AIDecideEvolution: ; 15f4c (5:5f4c) - call CreateHandCardList - ld hl, wDuelTempList - ld de, wHandTempList - call CopyHandCardList - ld hl, wHandTempList - -.next_hand_card - ld a, [hli] - cp $ff - jp z, .done - ld [wTempAIPokemonCard], a - -; check if Prehistoric Power is active -; and if so, skip to next card in hand - push hl - call IsPrehistoricPowerActive - jp c, .done_hand_card - -; load evolution data to buffer1 -; skip if it's not a Pokémon card -; and if it's a basic stage card - ld a, [wTempAIPokemonCard] - call LoadCardDataToBuffer1_FromDeckIndex - ld a, [wLoadedCard1Type] - cp TYPE_ENERGY - jp nc, .done_hand_card - ld a, [wLoadedCard1Stage] - or a - jp z, .done_hand_card - -; start looping Pokémon in Play Area -; to find a card to evolve - ld a, DUELVARS_NUMBER_OF_POKEMON_IN_PLAY_AREA - call GetTurnDuelistVariable - ld c, a - ld b, 0 -.next_bench_pokemon - push bc - ld e, b - ld a, [wTempAIPokemonCard] - ld d, a - call CheckIfCanEvolveInto - pop bc - push bc - jp c, .done_bench_pokemon - -; store this Play Area location in wTempAI -; and initialize the AI score - ld a, b - ld [wTempAI], a - ldh [hTempPlayAreaLocation_ff9d], a - ld a, $80 - ld [wAIScore], a - call AIDecideSpecialEvolutions - -; check if the card can use any attacks -; and if any of those attacks can KO - xor a - ld [wSelectedAttack], a - call CheckIfSelectedAttackIsUnusable - jr nc, .can_attack - ld a, $01 - ld [wSelectedAttack], a - call CheckIfSelectedAttackIsUnusable - jr c, .cant_attack_or_ko -.can_attack - ld a, $01 - ld [wCurCardCanAttack], a - call CheckIfAnyAttackKnocksOutDefendingCard - jr nc, .check_evolution_attacks - call CheckIfSelectedAttackIsUnusable - jr c, .check_evolution_attacks - ld a, $01 - ld [wCurCardCanKO], a - jr .check_evolution_attacks -.cant_attack_or_ko - xor a - ld [wCurCardCanAttack], a - ld [wCurCardCanKO], a - -; check evolution to see if it can use any of its attacks: -; if it can, raise AI score; -; if it can't, decrease AI score and if an energy card that is needed -; can be played from the hand, raise AI score. -.check_evolution_attacks - ldh a, [hTempPlayAreaLocation_ff9d] - add DUELVARS_ARENA_CARD - call GetTurnDuelistVariable - push af - ld a, [wTempAIPokemonCard] - ld [hl], a - xor a - ld [wSelectedAttack], a - call CheckIfSelectedAttackIsUnusable - jr nc, .evolution_can_attack - ld a, $01 - ld [wSelectedAttack], a - call CheckIfSelectedAttackIsUnusable - jr c, .evolution_cant_attack -.evolution_can_attack - ld a, 5 - call AddToAIScore - jr .check_evolution_ko -.evolution_cant_attack - ld a, [wCurCardCanAttack] - or a - jr z, .check_evolution_ko - ld a, 2 - call SubFromAIScore - ld a, [wAlreadyPlayedEnergy] - or a - jr nz, .check_evolution_ko - call LookForEnergyNeededInHand - jr nc, .check_evolution_ko - ld a, 7 - call AddToAIScore - -; if it's an active card: -; if evolution can't KO but the current card can, lower AI score; -; if evolution can KO as well, raise AI score. -.check_evolution_ko - ld a, [wCurCardCanAttack] - or a - jr z, .check_defending_can_ko_evolution - ld a, [wTempAI] - or a - jr nz, .check_defending_can_ko_evolution - call CheckIfAnyAttackKnocksOutDefendingCard - jr nc, .evolution_cant_ko - call CheckIfSelectedAttackIsUnusable - jr c, .evolution_cant_ko - ld a, 5 - call AddToAIScore - jr .check_defending_can_ko_evolution -.evolution_cant_ko - ld a, [wCurCardCanKO] - or a - jr z, .check_defending_can_ko_evolution - ld a, 20 - call SubFromAIScore - -; if defending Pokémon can KO evolution, lower AI score -.check_defending_can_ko_evolution - ld a, [wTempAI] - or a - jr nz, .check_mr_mime - xor a - ldh [hTempPlayAreaLocation_ff9d], a - call CheckIfDefendingPokemonCanKnockOut - jr nc, .check_mr_mime - ld a, 5 - call SubFromAIScore - -; if evolution can't damage player's Mr Mime, lower AI score -.check_mr_mime - ld a, [wTempAI] - call CheckDamageToMrMime - jr c, .check_defending_can_ko - ld a, 20 - call SubFromAIScore - -; if defending Pokémon can KO current card, raise AI score -.check_defending_can_ko - ld a, [wTempAI] - add DUELVARS_ARENA_CARD - call GetTurnDuelistVariable - pop af - ld [hl], a - ld a, [wTempAI] - or a - jr nz, .check_2nd_stage_hand - xor a - ldh [hTempPlayAreaLocation_ff9d], a - call CheckIfDefendingPokemonCanKnockOut - jr nc, .check_status - ld a, 5 - call AddToAIScore - -; if current card has a status condition, raise AI score -.check_status - ld a, DUELVARS_ARENA_CARD_STATUS - call GetTurnDuelistVariable - or a - jr z, .check_2nd_stage_hand - ld a, 4 - call AddToAIScore - -; if hand has 2nd stage card to evolve evolution card, raise AI score -.check_2nd_stage_hand - ld a, [wTempAIPokemonCard] - call CheckForEvolutionInList - jr nc, .check_2nd_stage_deck - ld a, 2 - call AddToAIScore - jr .check_damage - -; if deck has 2nd stage card to evolve evolution card, raise AI score -.check_2nd_stage_deck - ld a, [wTempAIPokemonCard] - call CheckForEvolutionInDeck - jr nc, .check_damage - ld a, 1 - call AddToAIScore - -; decrease AI score proportional to damage -; AI score -= floor(Damage / 40) -.check_damage - ld a, [wTempAI] - ld e, a - call GetCardDamageAndMaxHP - or a - jr z, .check_mysterious_fossil - srl a - srl a - call CalculateByteTensDigit - call SubFromAIScore - -; if is Mysterious Fossil or -; wLoadedCard1Unknown2 is set to $02, -; raise AI score -.check_mysterious_fossil - ld a, [wTempAI] - add DUELVARS_ARENA_CARD - call GetTurnDuelistVariable - call LoadCardDataToBuffer1_FromDeckIndex - ld a, [wLoadedCard1ID] - cp MYSTERIOUS_FOSSIL - jr z, .mysterious_fossil - ld a, [wLoadedCard1Unknown2] - cp $02 - jr nz, .pikachu_deck - ld a, 2 - call AddToAIScore - jr .pikachu_deck - -.mysterious_fossil - ld a, 5 - call AddToAIScore - -; in Pikachu Deck, decrease AI score for evolving Pikachu -.pikachu_deck - ld a, [wOpponentDeckID] - cp PIKACHU_DECK_ID - jr nz, .check_score - ld a, [wLoadedCard1ID] - cp PIKACHU1 - jr z, .pikachu - cp PIKACHU2 - jr z, .pikachu - cp PIKACHU3 - jr z, .pikachu - cp PIKACHU4 - jr nz, .check_score -.pikachu - ld a, 3 - call SubFromAIScore - -; if AI score >= 133, go through with the evolution -.check_score - ld a, [wAIScore] - cp 133 - jr c, .done_bench_pokemon - ld a, [wTempAI] - ldh [hTempPlayAreaLocation_ffa1], a - ld a, [wTempAIPokemonCard] - ldh [hTemp_ffa0], a - ld a, OPPACTION_EVOLVE_PKMN - bank1call AIMakeDecision - pop bc - jr .done_hand_card - -.done_bench_pokemon - pop bc - inc b - dec c - jp nz, .next_bench_pokemon -.done_hand_card - pop hl - jp .next_hand_card -.done - or a - ret - -; determine AI score for evolving -; Charmeleon, Magikarp, Dragonair and Grimer -; in certain decks -AIDecideSpecialEvolutions: ; 16120 (5:6120) -; check if deck applies - ld a, [wOpponentDeckID] - cp LEGENDARY_DRAGONITE_DECK_ID - jr z, .legendary_dragonite - cp INVINCIBLE_RONALD_DECK_ID - jr z, .invincible_ronald - cp LEGENDARY_RONALD_DECK_ID - jr z, .legendary_ronald - ret - -.legendary_dragonite - ld a, [wLoadedCard2ID] - cp CHARMELEON - jr z, .charmeleon - cp MAGIKARP - jr z, .magikarp - cp DRAGONAIR - jr z, .dragonair - ret - -; check if number of energy cards attached to Charmeleon are at least 3 -; and if adding the energy cards in hand makes at least 6 energy cards -.charmeleon - ldh a, [hTempPlayAreaLocation_ff9d] - ld e, a - call CountNumberOfEnergyCardsAttached - cp 3 - jr c, .not_enough_energy - push af - farcall CountOppEnergyCardsInHand - pop bc - add b - cp 6 - jr c, .not_enough_energy - ld a, 3 - call AddToAIScore - ret -.not_enough_energy - ld a, 10 - call SubFromAIScore - ret - -; check if Magikarp is not the active card -; and has at least 2 energy cards attached -.magikarp - ldh a, [hTempPlayAreaLocation_ff9d] - or a ; active card - ret z - ld e, a - call CountNumberOfEnergyCardsAttached - cp 2 - ret c - ld a, 3 - call AddToAIScore - ret - -.invincible_ronald - ld a, [wLoadedCard2ID] - cp GRIMER - jr z, .grimer - ret - -; check if Grimer is not active card -.grimer - ldh a, [hTempPlayAreaLocation_ff9d] - or a ; active card - ret z - ld a, 10 - call AddToAIScore - ret - -.legendary_ronald - ld a, [wLoadedCard2ID] - cp DRAGONAIR - jr z, .dragonair - ret - -.dragonair - ldh a, [hTempPlayAreaLocation_ff9d] - or a ; active card - jr z, .is_active - -; if Dragonair is benched, check all Pokémon in Play Area -; and sum all the damage in HP of all cards -; if this result is >= 70, check if there's -; a Muk in any duelist's Play Area - ld a, DUELVARS_NUMBER_OF_POKEMON_IN_PLAY_AREA - call GetTurnDuelistVariable - ld b, a - ld c, 0 -.loop - dec b - ld e, b - push bc - call GetCardDamageAndMaxHP - pop bc - add c - ld c, a - ld a, b - or a - jr nz, .loop - ld a, 70 - cp c - jr c, .check_muk -.lower_score - ld a, 10 - call SubFromAIScore - ret - -; if there's no Muk, raise score -.check_muk - ld a, MUK - call CountPokemonIDInBothPlayAreas - jr c, .lower_score - ld a, 10 - call AddToAIScore - ret - -; if Dragonair is active, check its damage in HP -; if this result is >= 50, -; and if at least 3 energy cards attached, -; check if there's a Muk in any duelist's Play Area -.is_active - ld e, 0 - call GetCardDamageAndMaxHP - cp 50 - jr c, .lower_score - ld e, PLAY_AREA_ARENA - call GetPlayAreaCardAttachedEnergies - ld a, [wTotalAttachedEnergies] - cp 3 - jr c, .lower_score - jr .check_muk - -; determine AI score for the legendary cards -; Moltres, Zapdos and Articuno -AIDecidePlayLegendaryBirds: ; 161d5 (5:61d5) -; check if deck applies - ld a, [wOpponentDeckID] - cp LEGENDARY_ZAPDOS_DECK_ID - jr z, .begin - cp LEGENDARY_ARTICUNO_DECK_ID - jr z, .begin - cp LEGENDARY_RONALD_DECK_ID - jr z, .begin - ret - -; check if card applies -.begin - ld a, [wLoadedCard1ID] - cp ARTICUNO2 - jr z, .articuno - cp MOLTRES2 - jr z, .moltres - cp ZAPDOS3 - jr z, .zapdos - ret - -.articuno - ; exit if not enough Pokemon in Play Area - ld a, DUELVARS_NUMBER_OF_POKEMON_IN_PLAY_AREA - call GetTurnDuelistVariable - cp 2 - ret c - - call CheckIfActiveCardCanKnockOut - jr c, .subtract - call CheckIfActivePokemonCanUseAnyNonResidualAttack - jr nc, .subtract - call AIDecideWhetherToRetreat - jr c, .subtract - - ; checks for player's active card status - ld a, DUELVARS_ARENA_CARD_STATUS - call GetNonTurnDuelistVariable - and CNF_SLP_PRZ - or a - jr nz, .subtract - - ; checks for player's Pokemon Power - call SwapTurn - ld a, DUELVARS_ARENA_CARD - call GetTurnDuelistVariable - ld d, a - ld e, $00 - call CopyAttackDataAndDamage_FromDeckIndex - call SwapTurn - ld a, [wLoadedAttackCategory] - cp POKEMON_POWER - jr z, .check_muk_and_snorlax - - ; return if no space on the bench - ld a, DUELVARS_NUMBER_OF_POKEMON_IN_PLAY_AREA - call GetTurnDuelistVariable - cp MAX_BENCH_POKEMON - jr c, .check_muk_and_snorlax - ret - -.check_muk_and_snorlax - ; checks for Muk in both Play Areas - ld a, MUK - call CountPokemonIDInBothPlayAreas - jr c, .subtract - ; checks if player's active card is Snorlax - ld a, DUELVARS_ARENA_CARD - call GetNonTurnDuelistVariable - call SwapTurn - call GetCardIDFromDeckIndex - call SwapTurn - ld a, e - cp SNORLAX - jr z, .subtract - -; add - ld a, 70 - call AddToAIScore - ret -.subtract - ld a, 100 - call SubFromAIScore - ret - -.moltres - ; checks if there's enough cards in deck - ld a, DUELVARS_NUMBER_OF_CARDS_NOT_IN_DECK - call GetTurnDuelistVariable - cp 56 ; max number of cards not in deck to activate - jr nc, .subtract - ret - -.zapdos - ; checks for Muk in both Play Areas - ld a, MUK - call CountPokemonIDInBothPlayAreas - jr c, .subtract - ret diff --git a/src/engine/ai/init.asm b/src/engine/ai/init.asm deleted file mode 100644 index 33132cf..0000000 --- a/src/engine/ai/init.asm +++ /dev/null @@ -1,98 +0,0 @@ -InitAIDuelVars: ; 15636 (5:5636) - ld a, wAIDuelVarsEnd - wAIDuelVars - ld hl, wAIDuelVars - call ClearMemory_Bank5 - ld a, 5 - ld [wAIPokedexCounter], a - ld a, $ff - ld [wAIPeekedPrizes], a - ret - -; initializes some variables and sets value of wAIBarrierFlagCounter. -; if Player uses Barrier 3 times in a row, AI checks if Player's deck -; has only Mewtwo1 Pokemon cards (running a Mewtwo1 mill deck). -InitAITurnVars: ; 15649 (5:5649) -; increase Pokedex counter by 1 - ld a, [wAIPokedexCounter] - inc a - ld [wAIPokedexCounter], a - - xor a - ld [wPreviousAIFlags], a - ld [wAITriedAttack], a - ld [wcddc], a - ld [wAIRetreatedThisTurn], a - -; checks if the Player used an attack last turn -; and if it was the second attack of their card. - ld a, [wPlayerAttackingAttackIndex] - cp $ff - jr z, .check_flag - or a - jr z, .check_flag - ld a, [wPlayerAttackingCardIndex] - cp $ff - jr z, .check_flag - -; if the card is Mewtwo1, it means the Player -; used its second attack, Barrier. - call SwapTurn - call GetCardIDFromDeckIndex - call SwapTurn - ld a, e - cp MEWTWO1 - jr nz, .check_flag - ; Player used Barrier last turn - -; check if flag was already set, if so, -; reset wAIBarrierFlagCounter to $80. - ld a, [wAIBarrierFlagCounter] - bit AI_MEWTWO_MILL_F, a - jr nz, .set_flag - -; if not, increase it by 1 and check if it exceeds 2. - inc a - ld [wAIBarrierFlagCounter], a - cp 3 - jr c, .done - -; this means that the Player used Barrier -; at least 3 turns in a row. -; check if Player is running Mewtwo1-only deck, -; if so, set wAIBarrierFlagCounter flag. - ld a, DUELVARS_ARENA_CARD - call GetNonTurnDuelistVariable - call SwapTurn - call GetCardIDFromDeckIndex - call SwapTurn - ld a, e - cp MEWTWO1 - jr nz, .reset_1 - farcall CheckIfPlayerHasPokemonOtherThanMewtwo1 - jr nc, .set_flag -.reset_1 -; reset wAIBarrierFlagCounter - xor a - ld [wAIBarrierFlagCounter], a - jr .done - -.set_flag - ld a, AI_MEWTWO_MILL - ld [wAIBarrierFlagCounter], a - jr .done - -.check_flag -; increase counter by 1 if flag is set - ld a, [wAIBarrierFlagCounter] - bit AI_MEWTWO_MILL_F, a - jr z, .reset_2 - inc a - ld [wAIBarrierFlagCounter], a - jr .done - -.reset_2 -; reset wAIBarrierFlagCounter - xor a - ld [wAIBarrierFlagCounter], a -.done - ret diff --git a/src/engine/ai/pkmn_powers.asm b/src/engine/ai/pkmn_powers.asm deleted file mode 100644 index 8ae629a..0000000 --- a/src/engine/ai/pkmn_powers.asm +++ /dev/null @@ -1,1228 +0,0 @@ -; handle AI routines for Energy Trans. -; uses AI_ENERGY_TRANS_* constants as input: -; - AI_ENERGY_TRANS_RETREAT: transfers enough Grass Energy cards to -; Arena Pokemon for it to be able to pay the Retreat Cost; -; - AI_ENERGY_TRANS_ATTACK: transfers enough Grass Energy cards to -; Arena Pokemon for it to be able to use its second attack; -; - AI_ENERGY_TRANS_TO_BENCH: transfers all Grass Energy cards from -; Arena Pokemon to Bench in case Arena card will be KO'd. -HandleAIEnergyTrans: ; 2219b (8:619b) - ld [wce06], a - -; choose to randomly return - farcall AIChooseRandomlyNotToDoAction - ret c - - ld a, DUELVARS_NUMBER_OF_POKEMON_IN_PLAY_AREA - call GetTurnDuelistVariable - dec a - ret z ; return if no Bench cards - - ld a, VENUSAUR2 - call CountPokemonIDInPlayArea - ret nc ; return if no Venusaur2 found in own Play Area - - ld a, MUK - call CountPokemonIDInBothPlayAreas - ret c ; return if Muk found in any Play Area - - ld a, [wce06] - cp AI_ENERGY_TRANS_RETREAT - jr z, .check_retreat - - cp AI_ENERGY_TRANS_TO_BENCH - jp z, AIEnergyTransTransferEnergyToBench - - ; AI_ENERGY_TRANS_ATTACK - call .CheckEnoughGrassEnergyCardsForAttack - ret nc - jr .TransferEnergyToArena - -.check_retreat - call .CheckEnoughGrassEnergyCardsForRetreatCost - ret nc - -; use Energy Trans to transfer number of Grass energy cards -; equal to input a from the Bench to the Arena card. -.TransferEnergyToArena - ld [wAINumberOfEnergyTransCards], a - -; look for Venusaur2 in Play Area -; so that its PKMN Power can be used. - ld a, DUELVARS_NUMBER_OF_POKEMON_IN_PLAY_AREA - call GetTurnDuelistVariable - dec a - ld b, a -.loop_play_area - ld a, DUELVARS_ARENA_CARD - add b - call GetTurnDuelistVariable - ldh [hTempCardIndex_ff9f], a - call GetCardIDFromDeckIndex - ld a, e - cp VENUSAUR2 - jr z, .use_pkmn_power - - ld a, b - or a - ret z ; return when finished Play Area loop - - dec b - jr .loop_play_area - -; use Energy Trans Pkmn Power -.use_pkmn_power - ld a, b - ldh [hTemp_ffa0], a - ld a, OPPACTION_USE_PKMN_POWER - bank1call AIMakeDecision - ld a, OPPACTION_EXECUTE_PKMN_POWER_EFFECT - bank1call AIMakeDecision - - xor a ; PLAY_AREA_ARENA - ldh [hAIEnergyTransPlayAreaLocation], a - ld a, [wAINumberOfEnergyTransCards] - ld d, a - -; look for Grass energy cards that -; are currently attached to a Bench card. - ld e, 0 -.loop_deck_locations - ld a, DUELVARS_CARD_LOCATIONS - add e - call GetTurnDuelistVariable - and %00011111 - cp CARD_LOCATION_BENCH_1 - jr c, .next_card - - and %00001111 - ldh [hTempPlayAreaLocation_ffa1], a - - ld a, e - push de - call GetCardIDFromDeckIndex - ld a, e - pop de - cp GRASS_ENERGY - jr nz, .next_card - - ; store the deck index of energy card - ld a, e - ldh [hAIEnergyTransEnergyCard], a - - push de - ld d, 30 -.small_delay_loop - call DoFrame - dec d - jr nz, .small_delay_loop - - ld a, OPPACTION_6B15 - bank1call AIMakeDecision - pop de - dec d - jr z, .done_transfer - -.next_card - inc e - ld a, DECK_SIZE - cp e - jr nz, .loop_deck_locations - -; transfer is done, perform delay -; and return to main scene. -.done_transfer - ld d, 60 -.big_delay_loop - call DoFrame - dec d - jr nz, .big_delay_loop - ld a, OPPACTION_DUEL_MAIN_SCENE - bank1call AIMakeDecision - ret - -; checks if the Arena card needs energy for its second attack, -; and if it does, return carry if transferring Grass energy from Bench -; would be enough to use it. Outputs number of energy cards needed in a. -.CheckEnoughGrassEnergyCardsForAttack ; 22246 (8:6246) - ld a, DUELVARS_ARENA_CARD - call GetTurnDuelistVariable - call GetCardIDFromDeckIndex - ld a, e - cp EXEGGUTOR - jr z, .is_exeggutor - - xor a ; PLAY_AREA_ARENA - ldh [hTempPlayAreaLocation_ff9d], a - ld a, SECOND_ATTACK - ld [wSelectedAttack], a - farcall CheckEnergyNeededForAttack - jr nc, .attack_false ; return if no energy needed - -; check if colorless energy is needed... - ld a, c - or a - jr nz, .count_if_enough - -; ...otherwise check if basic energy card is needed -; and it's grass energy. - ld a, b - or a - jr z, .attack_false - ld a, e - cp GRASS_ENERGY - jr nz, .attack_false - ld c, b - jr .count_if_enough - -.attack_false - or a - ret - -.count_if_enough -; if there's enough Grass energy cards in Bench -; to satisfy the attack energy cost, return carry. - push bc - call .CountGrassEnergyInBench - pop bc - cp c - jr c, .attack_false - ld a, c - scf - ret - -.is_exeggutor -; in case it's Exeggutor in Arena, return carry -; if there are any Grass energy cards in Bench. - call .CountGrassEnergyInBench - or a - jr z, .attack_false - - scf - ret - -; outputs in a the number of Grass energy cards -; currently attached to Bench cards. -.CountGrassEnergyInBench ; 22286 (8:6286) - lb de, 0, 0 -.count_loop - ld a, DUELVARS_CARD_LOCATIONS - add e - call GetTurnDuelistVariable - and %00011111 - cp CARD_LOCATION_BENCH_1 - jr c, .count_next - -; is in bench - ld a, e - push de - call GetCardIDFromDeckIndex - ld a, e - pop de - cp GRASS_ENERGY - jr nz, .count_next - inc d -.count_next - inc e - ld a, DECK_SIZE - cp e - jr nz, .count_loop - ld a, d - ret - -; returns carry if there are enough Grass energy cards in Bench -; to satisfy the retreat cost of the Arena card. -; if so, output the number of energy cards still needed in a. -.CheckEnoughGrassEnergyCardsForRetreatCost ; 222a9 (8:62a9) - xor a ; PLAY_AREA_ARENA - ldh [hTempPlayAreaLocation_ff9d], a - call GetPlayAreaCardRetreatCost - ld b, a - ld e, PLAY_AREA_ARENA - farcall CountNumberOfEnergyCardsAttached - cp b - jr nc, .retreat_false ; return if enough to retreat - -; see if there's enough Grass energy cards -; in the Bench to satisfy retreat cost - ld c, a - ld a, b - sub c - ld c, a - push bc - call .CountGrassEnergyInBench - pop bc - cp c - jr c, .retreat_false ; return if less cards than needed - -; output number of cards needed to retreat - ld a, c - scf - ret -.retreat_false - or a - ret - -; AI logic to determine whether to use Energy Trans Pkmn Power -; to transfer energy cards attached from the Arena Pokemon to -; some card in the Bench. -AIEnergyTransTransferEnergyToBench: ; 222ca (8:62ca) - xor a ; PLAY_AREA_ARENA - ldh [hTempPlayAreaLocation_ff9d], a - farcall CheckIfDefendingPokemonCanKnockOut - ret nc ; return if Defending can't KO - -; processes attacks and see if any attack would be used by AI. -; if so, return. - farcall AIProcessButDontUseAttack - ret c - -; return if Arena card has no Grass energy cards attached. - ld e, PLAY_AREA_ARENA - call GetPlayAreaCardAttachedEnergies - ld a, [wAttachedEnergies + GRASS] - or a - ret z - -; if no energy card attachment is needed, return. - farcall AIProcessButDontPlayEnergy_SkipEvolutionAndArena - ret nc - -; AI decided that an energy card is needed -; so look for Venusaur2 in Play Area -; so that its PKMN Power can be used. - ld a, DUELVARS_NUMBER_OF_POKEMON_IN_PLAY_AREA - call GetTurnDuelistVariable - dec a - ld b, a -.loop_play_area - ld a, DUELVARS_ARENA_CARD - add b - call GetTurnDuelistVariable - ldh [hTempCardIndex_ff9f], a - ld [wAIVenusaur2DeckIndex], a - call GetCardIDFromDeckIndex - ld a, e - cp VENUSAUR2 - jr z, .use_pkmn_power - - ld a, b - or a - ret z ; return when Play Area loop is ended - - dec b - jr .loop_play_area - -; use Energy Trans Pkmn Power -.use_pkmn_power - ld a, b - ldh [hTemp_ffa0], a - ld [wAIVenusaur2PlayAreaLocation], a - ld a, OPPACTION_USE_PKMN_POWER - bank1call AIMakeDecision - ld a, OPPACTION_EXECUTE_PKMN_POWER_EFFECT - bank1call AIMakeDecision - -; loop for each energy cards that are going to be transferred. -.loop_energy - xor a - ldh [hTempPlayAreaLocation_ffa1], a - ld a, [wAIVenusaur2PlayAreaLocation] - ldh [hTemp_ffa0], a - - ; returns when Arena card has no Grass energy cards attached. - ld e, PLAY_AREA_ARENA - call GetPlayAreaCardAttachedEnergies - ld a, [wAttachedEnergies + GRASS] - or a - jr z, .done_transfer - -; look for Grass energy cards that -; are currently attached to Arena card. - ld e, 0 -.loop_deck_locations - ld a, DUELVARS_CARD_LOCATIONS - add e - call GetTurnDuelistVariable - cp CARD_LOCATION_ARENA - jr nz, .next_card - - ld a, e - push de - call GetCardIDFromDeckIndex - ld a, e - pop de - cp GRASS_ENERGY - jr nz, .next_card - - ; store the deck index of energy card - ld a, e - ldh [hAIEnergyTransEnergyCard], a - jr .transfer - -.next_card - inc e - ld a, DECK_SIZE - cp e - jr nz, .loop_deck_locations - jr .done_transfer - -.transfer -; get the Bench card location to transfer Grass energy card to. - farcall AIProcessButDontPlayEnergy_SkipEvolutionAndArena - jr nc, .done_transfer - ldh a, [hTempPlayAreaLocation_ff9d] - ldh [hAIEnergyTransPlayAreaLocation], a - - ld d, 30 -.small_delay_loop - call DoFrame - dec d - jr nz, .small_delay_loop - - ld a, [wAIVenusaur2DeckIndex] - ldh [hTempCardIndex_ff9f], a - ld d, a - ld e, FIRST_ATTACK_OR_PKMN_POWER - call CopyAttackDataAndDamage_FromDeckIndex - ld a, OPPACTION_6B15 - bank1call AIMakeDecision - jr .loop_energy - -; transfer is done, perform delay -; and return to main scene. -.done_transfer - ld d, 60 -.big_delay_loop - call DoFrame - dec d - jr nz, .big_delay_loop - ld a, OPPACTION_DUEL_MAIN_SCENE - bank1call AIMakeDecision - ret - -; handles AI logic for using some Pkmn Powers. -; Pkmn Powers handled here are: -; - Heal; -; - Shift; -; - Peek; -; - Strange Behavior; -; - Curse. -; returns carry if turn ended. -HandleAIPkmnPowers: ; 2237f (8:637f) - ld a, MUK - call CountPokemonIDInBothPlayAreas - ccf - ret nc ; return no carry if Muk is in play - - farcall AIChooseRandomlyNotToDoAction - ccf - ret nc ; return no carry if AI randomly decides to - - ld a, DUELVARS_NUMBER_OF_POKEMON_IN_PLAY_AREA - call GetTurnDuelistVariable - ld b, a - ld c, PLAY_AREA_ARENA - ld a, DUELVARS_ARENA_CARD_STATUS - call GetTurnDuelistVariable - and CNF_SLP_PRZ - jr nz, .next_2 - -.loop_play_area - ld a, DUELVARS_ARENA_CARD - add c - call GetTurnDuelistVariable - ld [wce08], a - - push af - push bc - ld d, a - ld a, c - ldh [hTempPlayAreaLocation_ff9d], a - ld e, FIRST_ATTACK_OR_PKMN_POWER - call CopyAttackDataAndDamage_FromDeckIndex - ld a, [wLoadedAttackCategory] - cp POKEMON_POWER - jr z, .execute_effect - pop bc - jr .next_3 - -.execute_effect - ld a, EFFECTCMDTYPE_INITIAL_EFFECT_2 - bank1call TryExecuteEffectCommandFunction - pop bc - jr c, .next_3 - -; TryExecuteEffectCommandFunction was successful, -; so check what Pkmn Power this is through card's ID. - pop af - call GetCardIDFromDeckIndex - ld a, e - push bc - -; check heal - cp VILEPLUME - jr nz, .check_shift - call HandleAIHeal - jr .next_1 -.check_shift - cp VENOMOTH - jr nz, .check_peek - call HandleAIShift - jr .next_1 -.check_peek - cp MANKEY - jr nz, .check_strange_behavior - call HandleAIPeek - jr .next_1 -.check_strange_behavior - cp SLOWBRO - jr nz, .check_curse - call HandleAIStrangeBehavior - jr .next_1 -.check_curse - cp GENGAR - jr nz, .next_1 - call z, HandleAICurse - jr c, .done - -.next_1 - pop bc -.next_2 - inc c - ld a, c - cp b - jr nz, .loop_play_area - ret - -.next_3 - pop af - jr .next_2 - -.done - pop bc - ret - -; checks whether AI uses Heal on Pokemon in Play Area. -; input: -; c = Play Area location (PLAY_AREA_*) of Vileplume. -HandleAIHeal: ; 22402 (8:6402) - ld a, c - ldh [hTemp_ffa0], a - call .CheckHealTarget - ret nc ; return if no target to heal - push af - ld a, [wce08] - ldh [hTempCardIndex_ff9f], a - ld a, OPPACTION_USE_PKMN_POWER - bank1call AIMakeDecision - pop af - ldh [hPlayAreaEffectTarget], a - ld a, OPPACTION_EXECUTE_PKMN_POWER_EFFECT - bank1call AIMakeDecision - ld a, OPPACTION_DUEL_MAIN_SCENE - bank1call AIMakeDecision - ret - -; finds a target suitable for AI to use Heal on. -; only heals Arena card if the Defending Pokemon -; cannot KO it after Heal is used. -; returns carry if target was found and outputs -; in a the Play Area location of that card. -.CheckHealTarget ; 22422 (8:6422) -; check if Arena card has any damage counters, -; if not, check Bench instead. - ld e, PLAY_AREA_ARENA - call GetCardDamageAndMaxHP - or a - jr z, .check_bench - - xor a ; PLAY_AREA_ARENA - ldh [hTempPlayAreaLocation_ff9d], a - farcall CheckIfDefendingPokemonCanKnockOut - jr nc, .set_carry ; return carry if can't KO - ld d, a - ld a, DUELVARS_ARENA_CARD_HP - call GetTurnDuelistVariable - ld h, a - ld e, PLAY_AREA_ARENA - call GetCardDamageAndMaxHP - ; this seems useless since it was already - ; checked that Arena card has damage, - ; so card damage is at least 10. - cp 10 + 1 - jr c, .check_remaining - ld a, 10 - ; a = min(10, CardDamage) - -; checks if Defending Pokemon can still KO -; if Heal is used on this card. -; if Heal prevents KO, return carry. -.check_remaining - ld l, a - ld a, h ; load remaining HP - add l ; add 1 counter to account for heal - sub d ; subtract damage of strongest opponent attack - jr c, .check_bench - jr z, .check_bench - -.set_carry - xor a ; PLAY_AREA_ARENA - scf - ret - -; check Bench for Pokemon with damage counters -; and find the one with the most damage. -.check_bench - ld a, DUELVARS_NUMBER_OF_POKEMON_IN_PLAY_AREA - call GetTurnDuelistVariable - ld d, a - lb bc, 0, 0 - ld e, PLAY_AREA_BENCH_1 -.loop_bench - ld a, e - cp d - jr z, .done - push bc - call GetCardDamageAndMaxHP - pop bc - cp b - jr c, .next_bench - jr z, .next_bench - ld b, a ; store this damage - ld c, e ; store this Play Area location -.next_bench - inc e - jr .loop_bench - -; check if a Pokemon with damage counters was found -; in the Bench and, if so, return carry. -.done - ld a, c - or a - jr z, .not_found -; found - scf - ret -.not_found - or a - ret - -; checks whether AI uses Shift. -; input: -; c = Play Area location (PLAY_AREA_*) of Venomoth -HandleAIShift: ; 22476 (8:6476) - ld a, c - or a - ret nz ; return if Venomoth is not Arena card - - ldh [hTemp_ffa0], a - call GetArenaCardColor - call TranslateColorToWR - ld b, a - call SwapTurn - call GetArenaCardWeakness - ld [wAIDefendingPokemonWeakness], a - call SwapTurn - or a - ret z ; return if Defending Pokemon has no weakness - and b - ret nz ; return if Venomoth is already Defending card's weakness type - -; check whether there's a card in play with -; the same color as the Player's card weakness - call .CheckWhetherTurnDuelistHasColor - jr c, .found - call SwapTurn - call .CheckWhetherTurnDuelistHasColor - call SwapTurn - ret nc ; return if no color found - -.found - ld a, [wce08] - ldh [hTempCardIndex_ff9f], a - ld a, OPPACTION_USE_PKMN_POWER - bank1call AIMakeDecision - -; converts WR_* to appropriate color - ld a, [wAIDefendingPokemonWeakness] - ld b, 0 -.loop_color - bit 7, a - jr nz, .done - inc b - rlca - jr .loop_color - -; use Pkmn Power effect -.done - ld a, b - ldh [hAIPkmnPowerEffectParam], a - ld a, OPPACTION_EXECUTE_PKMN_POWER_EFFECT - bank1call AIMakeDecision - ld a, OPPACTION_DUEL_MAIN_SCENE - bank1call AIMakeDecision - ret - -; returns carry if turn Duelist has a Pokemon -; with same color as wAIDefendingPokemonWeakness. -.CheckWhetherTurnDuelistHasColor ; 224c6 (8:64c6) - ld a, [wAIDefendingPokemonWeakness] - ld b, a - ld a, DUELVARS_ARENA_CARD - call GetTurnDuelistVariable -.loop_play_area - ld a, [hli] - cp $ff - jr z, .false - push bc - call GetCardIDFromDeckIndex - call GetCardType - ; in case this is a Mysterious Fossil or Clefairy Doll card, - ; AI might read the type of the card incorrectly here. - ; uncomment the following lines to account for this - ; cp TYPE_TRAINER - ; jr nz, .not_trainer - ; pop bc - ; jr .loop_play_area -; .not_trainer - call TranslateColorToWR - pop bc - and b - jr z, .loop_play_area -; true - scf - ret -.false - or a - ret - -; checks whether AI uses Peek. -; input: -; c = Play Area location (PLAY_AREA_*) of Mankey. -HandleAIPeek: ; 224e6 (8:64e6) - ld a, c - ldh [hTemp_ffa0], a - ld a, 50 - call Random - cp 3 - ret nc ; return 47 out of 50 times - -; choose what to use Peek on at random - ld a, 3 - call Random - or a - jr z, .check_ai_prizes - cp 2 - jr c, .check_player_hand - -; check Player's Deck - ld a, DUELVARS_NUMBER_OF_CARDS_NOT_IN_DECK - call GetNonTurnDuelistVariable - cp DECK_SIZE - 1 - ret nc ; return if Player has one or no cards in Deck - ld a, AI_PEEK_TARGET_DECK - jr .use_peek - -.check_ai_prizes - ld a, DUELVARS_PRIZES - call GetTurnDuelistVariable - ld hl, wAIPeekedPrizes - and [hl] - ld [hl], a - or a - ret z ; return if no prizes - - ld c, a - ld b, $1 - ld d, 0 -.loop_prizes - ld a, c - and b - jr nz, .found_prize - sla b - inc d - jr .loop_prizes -.found_prize -; remove this prize's flag from the prize list -; and use Peek on first one in list (lowest bit set) - ld a, c - sub b - ld [hl], a - ld a, AI_PEEK_TARGET_PRIZE - add d - jr .use_peek - -.check_player_hand - call SwapTurn - call CreateHandCardList - call SwapTurn - or a - ret z ; return if no cards in Hand -; shuffle list and pick the first entry to Peek - ld hl, wDuelTempList - call CountCardsInDuelTempList - call ShuffleCards - ld a, [wDuelTempList] - or AI_PEEK_TARGET_HAND - -.use_peek - push af - ld a, [wce08] - ldh [hTempCardIndex_ff9f], a - ld a, OPPACTION_USE_PKMN_POWER - bank1call AIMakeDecision - pop af - ldh [hAIPkmnPowerEffectParam], a - ld a, OPPACTION_EXECUTE_PKMN_POWER_EFFECT - bank1call AIMakeDecision - ld a, OPPACTION_DUEL_MAIN_SCENE - bank1call AIMakeDecision - ret - -; checks whether AI uses Strange Behavior. -; input: -; c = Play Area location (PLAY_AREA_*) of Slowbro. -HandleAIStrangeBehavior: ; 2255d (8:655d) - ld a, c - or a - ret z ; return if Slowbro is Arena card - - ldh [hTemp_ffa0], a - ld e, PLAY_AREA_ARENA - call GetCardDamageAndMaxHP - or a - ret z ; return if Arena card has no damage counters - - ld [wce06], a - ldh a, [hTemp_ffa0] - add DUELVARS_ARENA_CARD_HP - call GetTurnDuelistVariable - sub 10 - ret z ; return if Slowbro has only 10 HP remaining - -; if Slowbro can't receive all damage counters, -; only transfer remaining HP - 10 damage - ld hl, wce06 - cp [hl] - jr c, .use_strange_behavior - ld a, [hl] ; can receive all damage counters - -.use_strange_behavior - push af - ld a, [wce08] - ldh [hTempCardIndex_ff9f], a - ld a, OPPACTION_USE_PKMN_POWER - bank1call AIMakeDecision - xor a - ldh [hAIPkmnPowerEffectParam], a - ld a, OPPACTION_EXECUTE_PKMN_POWER_EFFECT - bank1call AIMakeDecision - pop af - -; loop counters chosen to transfer and use Pkmn Power - call ConvertHPToCounters - ld e, a -.loop_counters - ld d, 30 -.small_delay_loop - call DoFrame - dec d - jr nz, .small_delay_loop - push de - ld a, OPPACTION_6B15 - bank1call AIMakeDecision - pop de - dec e - jr nz, .loop_counters - -; return to main scene - ld d, 60 -.big_delay_loop - call DoFrame - dec d - jr nz, .big_delay_loop - ld a, OPPACTION_DUEL_MAIN_SCENE - bank1call AIMakeDecision - ret - -; checks whether AI uses Curse. -; input: -; c = Play Area location (PLAY_AREA_*) of Gengar. -HandleAICurse: ; 225b5 (8:65b5) - ld a, c - ldh [hTemp_ffa0], a - -; loop Player's Play Area and checks their damage. -; finds the card with lowest remaining HP and -; stores its HP and its Play Area location - ld a, DUELVARS_NUMBER_OF_POKEMON_IN_PLAY_AREA - call GetNonTurnDuelistVariable - ld d, a - ld e, PLAY_AREA_ARENA - lb bc, 0, $ff - ld h, PLAY_AREA_ARENA - call SwapTurn -.loop_play_area_1 - push bc - call GetCardDamageAndMaxHP - pop bc - or a - jr z, .next_1 - - inc b - ld a, e - add DUELVARS_ARENA_CARD_HP - push hl - call GetTurnDuelistVariable - pop hl - cp c - jr nc, .next_1 - ; lower HP than one stored - ld c, a ; store this HP - ld h, e ; store this Play Area location - -.next_1 - inc e - ld a, e - cp d - jr nz, .loop_play_area_1 ; reached end of Play Area - - ld a, 1 - cp b - jr nc, .failed ; return if less than 2 cards with damage - -; card in Play Area with lowest HP remaining was found. -; look for another card to take damage counter from. - ld a, h - ldh [hTempRetreatCostCards], a - ld b, a - ld a, 10 - cp c - jr z, .hp_10_remaining - ; if has more than 10 HP remaining, - ; skip Arena card in choosing which - ; card to take damage counter from. - ld e, PLAY_AREA_BENCH_1 - jr .second_card - -.hp_10_remaining - ; if Curse can KO, then include - ; Player's Arena card to take - ; damage counter from. - ld e, PLAY_AREA_ARENA - -.second_card - ld a, DUELVARS_NUMBER_OF_POKEMON_IN_PLAY_AREA - call GetTurnDuelistVariable - ld d, a -.loop_play_area_2 - ld a, e - cp b - jr z, .next_2 ; skip same Pokemon card - push bc - call GetCardDamageAndMaxHP - pop bc - jr nz, .use_curse ; has damage counters, choose this card -.next_2 - inc e - ld a, e - cp d - jr nz, .loop_play_area_2 - -.failed - call SwapTurn - or a - ret - -.use_curse - ld a, e - ldh [hAIPkmnPowerEffectParam], a - call SwapTurn - ld a, [wce08] - ldh [hTempCardIndex_ff9f], a - ld a, OPPACTION_USE_PKMN_POWER - bank1call AIMakeDecision - ld a, OPPACTION_EXECUTE_PKMN_POWER_EFFECT - bank1call AIMakeDecision - ld a, OPPACTION_DUEL_MAIN_SCENE - bank1call AIMakeDecision - ret - -; handles AI logic for Cowardice -HandleAICowardice: ; 2262d (8:662d) - ld a, MUK - call CountPokemonIDInBothPlayAreas - ret c ; return if there's Muk in play - - farcall AIChooseRandomlyNotToDoAction - ret c ; randomly return - - ld a, DUELVARS_NUMBER_OF_POKEMON_IN_PLAY_AREA - call GetTurnDuelistVariable - cp 1 - ret z ; return if only one Pokemon in Play Area - - ld b, a - ld c, PLAY_AREA_ARENA - ld a, DUELVARS_ARENA_CARD_STATUS - call GetTurnDuelistVariable - and CNF_SLP_PRZ - jr nz, .next -.loop - ld a, DUELVARS_ARENA_CARD - add c - call GetTurnDuelistVariable - ld [wce08], a - call GetCardIDFromDeckIndex - ld a, e - push bc - cp TENTACOOL - call z, .CheckWhetherToUseCowardice - pop bc - jr nc, .next - - dec b ; subtract 1 from number of Pokemon in Play Area - ld a, 1 - cp b - ret z ; return if no longer has Bench Pokemon - ld c, PLAY_AREA_ARENA ; reset back to Arena - jr .loop - -.next - inc c - ld a, c - cp b - jr nz, .loop - ret - -; checks whether AI uses Cowardice. -; return carry if Pkmn Power was used. -; input: -; c = Play Area location (PLAY_AREA_*) of Tentacool. -.CheckWhetherToUseCowardice ; 22671 (8:6671) - ld a, c - ldh [hTemp_ffa0], a - ld e, a - call GetCardDamageAndMaxHP -.asm_22678 - or a - ret z ; return if has no damage counters - - ldh a, [hTemp_ffa0] - or a - jr nz, .is_benched - - ; this part is buggy if AIDecideBenchPokemonToSwitchTo returns carry - ; but since this was already checked beforehand, this never happens. - ; so jr c, .asm_22678 can be safely removed. - farcall AIDecideBenchPokemonToSwitchTo - jr c, .asm_22678 ; bug, this jumps in the middle of damage checking - jr .use_cowardice -.is_benched - ld a, $ff -.use_cowardice - push af - ld a, [wce08] - ldh [hTempCardIndex_ff9f], a - ld a, OPPACTION_USE_PKMN_POWER - bank1call AIMakeDecision - pop af - ldh [hAIPkmnPowerEffectParam], a - ld a, OPPACTION_EXECUTE_PKMN_POWER_EFFECT - bank1call AIMakeDecision - ld a, OPPACTION_DUEL_MAIN_SCENE - bank1call AIMakeDecision - scf - ret - -; AI logic for Damage Swap to transfer damage from Arena card -; to a card in Bench with more than 10 HP remaining -; and with no energy cards attached. -HandleAIDamageSwap: ; 226a3 (8:66a3) - ld a, DUELVARS_NUMBER_OF_POKEMON_IN_PLAY_AREA - call GetTurnDuelistVariable - dec a - ret z ; return if no Bench Pokemon - - farcall AIChooseRandomlyNotToDoAction - ret c - - ld a, ALAKAZAM - call CountPokemonIDInPlayArea - ret nc ; return if no Alakazam - ld a, MUK - call CountPokemonIDInBothPlayAreas - ret c ; return if there's Muk in play - -; only take damage off certain cards in Arena - ld a, DUELVARS_ARENA_CARD - call GetTurnDuelistVariable - call GetCardIDFromDeckIndex - ld a, e - cp ALAKAZAM - jr z, .ok - cp KADABRA - jr z, .ok - cp ABRA - jr z, .ok - cp MR_MIME - ret nz - -.ok - ld e, PLAY_AREA_ARENA - call GetCardDamageAndMaxHP - or a - ret z ; return if no damage - - call ConvertHPToCounters - ld [wce06], a - ld a, ALAKAZAM - ld b, PLAY_AREA_BENCH_1 - farcall LookForCardIDInPlayArea_Bank5 - jr c, .is_in_bench - -; Alakazam is Arena card - xor a -.is_in_bench - ld [wce08], a - call .CheckForDamageSwapTargetInBench - ret c ; return if not found - -; use Damage Swap - ld a, [wce08] - add DUELVARS_ARENA_CARD - call GetTurnDuelistVariable - ldh [hTempCardIndex_ff9f], a - ld a, [wce08] - ldh [hTemp_ffa0], a - ld a, OPPACTION_USE_PKMN_POWER - bank1call AIMakeDecision - ld a, OPPACTION_EXECUTE_PKMN_POWER_EFFECT - bank1call AIMakeDecision - - ld a, [wce06] - ld e, a -.loop_damage - ld d, 30 -.small_delay_loop - call DoFrame - dec d - jr nz, .small_delay_loop - - push de - call .CheckForDamageSwapTargetInBench - jr c, .no_more_target - - ldh [hTempRetreatCostCards], a - xor a ; PLAY_AREA_ARENA - ldh [hAIPkmnPowerEffectParam], a - ld a, OPPACTION_6B15 - bank1call AIMakeDecision - pop de - dec e - jr nz, .loop_damage - -.done -; return to main scene - ld d, 60 -.big_delay_loop - call DoFrame - dec d - jr nz, .big_delay_loop - ld a, OPPACTION_DUEL_MAIN_SCENE - bank1call AIMakeDecision - ret - -.no_more_target - pop de - jr .done - -; looks for a target in the bench to receive damage counters. -; returns carry if one is found, and outputs remaining HP in a. -.CheckForDamageSwapTargetInBench ; 2273c (8:673c) - ld a, DUELVARS_NUMBER_OF_POKEMON_IN_PLAY_AREA - call GetTurnDuelistVariable - ld b, a - ld c, PLAY_AREA_BENCH_1 - lb de, $ff, $ff - -; look for candidates in bench to get the damage counters -; only target specific card IDs. -.loop_bench - ld a, c - add DUELVARS_ARENA_CARD - call GetTurnDuelistVariable - push de - call GetCardIDFromDeckIndex - ld a, e - pop de - cp CHANSEY - jr z, .found_candidate - cp KANGASKHAN - jr z, .found_candidate - cp SNORLAX - jr z, .found_candidate - cp MR_MIME - jr z, .found_candidate - -.next_play_area - inc c - ld a, c - cp b - jr nz, .loop_bench - -; done - ld a, e - cp $ff - jr nz, .no_carry - ld a, d - cp $ff - jr z, .set_carry -.no_carry - or a - ret - -.found_candidate -; found a potential candidate to receive damage counters - ld a, DUELVARS_ARENA_CARD_HP - add c - call GetTurnDuelistVariable - cp 20 - jr c, .next_play_area ; ignore cards with only 10 HP left - - ld d, c ; store damage - push de - push bc - ld e, c - farcall CountNumberOfEnergyCardsAttached - pop bc - pop de - or a - jr nz, .next_play_area ; ignore cards with attached energy - ld e, c ; store deck index - jr .next_play_area - -.set_carry - scf - ret - -; handles AI logic for attaching energy cards -; in Go Go Rain Dance deck. -HandleAIGoGoRainDanceEnergy: ; 22790 (8:6790) - ld a, [wOpponentDeckID] - cp GO_GO_RAIN_DANCE_DECK_ID - ret nz ; return if not Go Go Rain Dance deck - - ld a, BLASTOISE - call CountPokemonIDInPlayArea - ret nc ; return if no Blastoise - ld a, MUK - call CountPokemonIDInBothPlayAreas - ret c ; return if there's Muk in play - -; play all the energy cards that is needed. -.loop - farcall AIProcessAndTryToPlayEnergy - jr c, .loop - ret diff --git a/src/engine/ai/retreat.asm b/src/engine/ai/retreat.asm deleted file mode 100644 index 768a48b..0000000 --- a/src/engine/ai/retreat.asm +++ /dev/null @@ -1,1009 +0,0 @@ -; determine AI score for retreating -; return carry if AI decides to retreat -AIDecideWhetherToRetreat: ; 158b2 (5:58b2) - ld a, [wGotHeadsFromConfusionCheckDuringRetreat] - or a - jp nz, .no_carry - xor a - ld [wAIPlayEnergyCardForRetreat], a - call LoadDefendingPokemonColorWRAndPrizeCards - ld a, $80 ; initial retreat score - ld [wAIScore], a - ld a, [wcdb4] - or a - jr z, .check_status - srl a - srl a - sla a - call AddToAIScore - -.check_status - ld a, DUELVARS_ARENA_CARD_STATUS - call GetTurnDuelistVariable - or a - jr z, .check_ko_1 ; no status - and DOUBLE_POISONED - jr z, .check_cnf ; no poison - ld a, 2 - call AddToAIScore -.check_cnf - ld a, [hl] - and CNF_SLP_PRZ - cp CONFUSED - jr nz, .check_ko_1 - ld a, 1 - call AddToAIScore - -.check_ko_1 - xor a - ldh [hTempPlayAreaLocation_ff9d], a - call CheckIfAnyAttackKnocksOutDefendingCard - jr nc, .active_cant_ko_1 - call CheckIfSelectedAttackIsUnusable - jp nc, .active_cant_use_atk - call LookForEnergyNeededForAttackInHand - jr nc, .active_cant_ko_1 - -.active_cant_use_atk - ld a, 5 - call SubFromAIScore - ld a, [wAIOpponentPrizeCount] - cp 2 - jr nc, .active_cant_ko_1 - ld a, 35 - call SubFromAIScore - -.active_cant_ko_1 - call CheckIfDefendingPokemonCanKnockOut - jr nc, .defending_cant_ko - ld a, 2 - call AddToAIScore - - call CheckIfNotABossDeckID - jr c, .check_resistance_1 - ld a, [wAIPlayerPrizeCount] - cp 2 - jr nc, .check_prize_count - ld a, $01 - ld [wAIPlayEnergyCardForRetreat], a - -.defending_cant_ko - call CheckIfNotABossDeckID - jr c, .check_resistance_1 - ld a, [wAIPlayerPrizeCount] - cp 2 - jr nc, .check_prize_count - ld a, 2 - call AddToAIScore - -.check_prize_count - ld a, [wAIOpponentPrizeCount] - cp 2 - jr nc, .check_resistance_1 - ld a, 2 - call SubFromAIScore - -.check_resistance_1 - call GetArenaCardColor - call TranslateColorToWR - ld b, a - ld a, [wAIPlayerResistance] - and b - jr z, .check_weakness_1 - ld a, 1 - call AddToAIScore - -; check bench for Pokémon that -; the defending card is not resistant to -; if one is found, skip SubFromAIScore - ld a, [wAIPlayerResistance] - ld b, a - ld a, DUELVARS_BENCH - call GetTurnDuelistVariable -.loop_resistance_1 - ld a, [hli] - cp $ff - jr z, .exit_loop_resistance_1 - call LoadCardDataToBuffer1_FromDeckIndex - ld a, [wLoadedCard1Type] - call TranslateColorToWR - and b - jr nz, .loop_resistance_1 - jr .check_weakness_1 -.exit_loop_resistance_1 - ld a, 2 - call SubFromAIScore - -.check_weakness_1 - ld a, [wAIPlayerColor] - ld b, a - call GetArenaCardWeakness - and b - jr z, .check_resistance_2 - ld a, 2 - call AddToAIScore - -; check bench for Pokémon that -; is not weak to defending Pokémon -; if one is found, skip SubFromAIScore - ld a, [wAIPlayerColor] - ld b, a - ld a, DUELVARS_BENCH - call GetTurnDuelistVariable -.loop_weakness_1 - ld a, [hli] - cp $ff - jr z, .exit_loop_weakness_1 - call LoadCardDataToBuffer1_FromDeckIndex - ld a, [wLoadedCard1Weakness] - and b - jr nz, .loop_weakness_1 - jr .check_resistance_2 -.exit_loop_weakness_1 - ld a, 3 - call SubFromAIScore - -.check_resistance_2 - ld a, [wAIPlayerColor] - ld b, a - call GetArenaCardResistance - and b - jr z, .check_weakness_2 - ld a, 3 - call SubFromAIScore - -; check bench for Pokémon that -; is the defending Pokémon's weakness -; if none is found, skip AddToAIScore -.check_weakness_2 - ld a, [wAIPlayerWeakness] - ld b, a - ld a, DUELVARS_BENCH - call GetTurnDuelistVariable - ld e, $00 -.loop_weakness_2 - inc e - ld a, [hli] - cp $ff - jr z, .check_resistance_3 - push de - call LoadCardDataToBuffer1_FromDeckIndex - ld a, [wLoadedCard1Type] - call TranslateColorToWR - pop de - and b - jr z, .loop_weakness_2 - ld a, 2 - call AddToAIScore - - push de - ld a, DUELVARS_ARENA_CARD - call GetTurnDuelistVariable - call GetCardIDFromDeckIndex - ld a, e - pop de - cp PORYGON - jr nz, .check_weakness_3 - -; handle Porygon - ld a, e - call CheckIfCanDamageDefendingPokemon - jr nc, .check_weakness_3 - ld a, 10 - call AddToAIScore - jr .check_resistance_3 - -.check_weakness_3 - call GetArenaCardColor - call TranslateColorToWR - ld b, a - ld a, [wAIPlayerWeakness] - and b - jr z, .check_resistance_3 - ld a, 3 - call SubFromAIScore - -; check bench for Pokémon that -; is resistant to defending Pokémon -; if none is found, skip AddToAIScore -.check_resistance_3 - ld a, [wAIPlayerColor] - ld b, a - ld a, DUELVARS_BENCH - call GetTurnDuelistVariable -.loop_resistance_2 - ld a, [hli] - cp $ff - jr z, .check_ko_2 - call LoadCardDataToBuffer1_FromDeckIndex - ld a, [wLoadedCard1Resistance] - and b - jr z, .loop_resistance_2 - ld a, 1 - call AddToAIScore - -; check bench for Pokémon that -; can KO defending Pokémon -; if none is found, skip AddToAIScore -.check_ko_2 - ld a, DUELVARS_BENCH - call GetTurnDuelistVariable - ld c, 0 -.loop_ko_1 - inc c - ld a, [hli] - cp $ff - jr z, .check_defending_id - ld a, c - ldh [hTempPlayAreaLocation_ff9d], a - push hl - push bc - call CheckIfAnyAttackKnocksOutDefendingCard - jr nc, .no_ko - call CheckIfSelectedAttackIsUnusable - jr nc, .success - call LookForEnergyNeededForAttackInHand - jr c, .success -.no_ko - pop bc - pop hl - jr .loop_ko_1 -.success - pop bc - pop hl - ld a, 2 - call AddToAIScore - -; a bench Pokémon was found that can KO -; if this is a boss deck and it's at last prize card -; if arena Pokémon cannot KO, add to AI score -; and set wAIPlayEnergyCardForRetreat to $01 - - ld a, [wAIOpponentPrizeCount] - cp 2 - jr nc, .check_defending_id - call CheckIfNotABossDeckID - jr c, .check_defending_id - - xor a - ldh [hTempPlayAreaLocation_ff9d], a - call CheckIfAnyAttackKnocksOutDefendingCard - jr nc, .active_cant_ko_2 - call CheckIfSelectedAttackIsUnusable - jp nc, .check_defending_id -.active_cant_ko_2 - ld a, 40 - call AddToAIScore - ld a, $01 - ld [wAIPlayEnergyCardForRetreat], a - -.check_defending_id - ld a, DUELVARS_ARENA_CARD - call GetNonTurnDuelistVariable - call SwapTurn - call GetCardIDFromDeckIndex - call SwapTurn - ld a, e - cp MR_MIME - jr z, .mr_mime_or_hitmonlee - cp HITMONLEE ; ?? - jr nz, .check_retreat_cost - -; check bench if there's any Pokémon -; that can damage defending Pokémon -; this is done because of Mr. Mime's PKMN PWR -; but why Hitmonlee ($87) as well? -.mr_mime_or_hitmonlee - xor a - call CheckIfCanDamageDefendingPokemon - jr c, .check_retreat_cost - ld a, DUELVARS_BENCH - call GetTurnDuelistVariable - ld c, 0 -.loop_damage - inc c - ld a, [hli] - cp $ff - jr z, .check_retreat_cost - ld a, c - push hl - push bc - call CheckIfCanDamageDefendingPokemon - jr c, .can_damage - pop bc - pop hl - jr .loop_damage -.can_damage - pop bc - pop hl - ld a, 5 - call AddToAIScore - ld a, $01 - ld [wAIPlayEnergyCardForRetreat], a - -; subtract from wAIScore if retreat cost is larger than 1 -; then check if any cards have at least half HP, -; are final evolutions and can use second attack in the bench -; and adds to wAIScore if the active Pokémon doesn't meet -; these conditions -.check_retreat_cost - xor a - ldh [hTempPlayAreaLocation_ff9d], a - call GetPlayAreaCardRetreatCost - cp 2 - jr c, .one_or_none - cp 3 - jr nc, .three_or_more - ; exactly two - ld a, 1 - call SubFromAIScore - jr .one_or_none - -.three_or_more - ld a, 2 - call SubFromAIScore - -.one_or_none - call CheckIfArenaCardIsAtHalfHPCanEvolveAndUseSecondAttack - jr c, .check_defending_can_ko - call CountNumberOfSetUpBenchPokemon - cp 2 - jr c, .check_defending_can_ko - call AddToAIScore - -; check bench for Pokémon that -; the defending Pokémon can't knock out -; if none is found, skip SubFromAIScore -.check_defending_can_ko - ld a, DUELVARS_BENCH - call GetTurnDuelistVariable - ld e, 0 -.loop_ko_2 - inc e - ld a, [hli] - cp $ff - jr z, .exit_loop_ko - push de - push hl - call LoadCardDataToBuffer2_FromDeckIndex - ld a, [wLoadedCard2ID] - pop hl - pop de - cp MYSTERIOUS_FOSSIL - jr z, .loop_ko_2 - cp CLEFAIRY_DOLL - jr z, .loop_ko_2 - ld a, e - ldh [hTempPlayAreaLocation_ff9d], a - push de - push hl - call CheckIfDefendingPokemonCanKnockOut - pop hl - pop de - jr c, .loop_ko_2 - jr .check_active_id -.exit_loop_ko - ld a, 20 - call SubFromAIScore - -.check_active_id - ld a, DUELVARS_ARENA_CARD - call GetTurnDuelistVariable - call GetCardIDFromDeckIndex - ld a, e - cp MYSTERIOUS_FOSSIL - jr z, .mysterious_fossil_or_clefairy_doll - cp CLEFAIRY_DOLL - jr z, .mysterious_fossil_or_clefairy_doll - -; if wAIScore is at least 131, set carry - ld a, [wAIScore] - cp 131 - jr nc, .set_carry -.no_carry - or a - ret -.set_carry - scf - ret - -; set carry regardless if active card is -; either Mysterious Fossil or Clefairy Doll -; and there's a bench Pokémon who is not KO'd -; by defending Pokémon and can damage it -.mysterious_fossil_or_clefairy_doll - ld e, 0 -.loop_ko_3 - inc e - ld a, e - add DUELVARS_ARENA_CARD - call GetTurnDuelistVariable - cp $ff - jr z, .no_carry - ld a, e - ldh [hTempPlayAreaLocation_ff9d], a - push de - call CheckIfDefendingPokemonCanKnockOut - pop de - jr c, .loop_ko_3 - ld a, e - push de - call CheckIfCanDamageDefendingPokemon - pop de - jr nc, .loop_ko_3 - jr .set_carry - -; if player's turn and loaded attack is not a Pokémon Power OR -; if opponent's turn and wAITriedAttack == 0 -; set wcdda's bit 7 flag -Func_15b54: ; 15b54 (5:5b54) - xor a - ld [wcdda], a - ld a, [wWhoseTurn] - cp OPPONENT_TURN - jr z, .opponent - -; player - ld a, [wLoadedAttackCategory] - cp POKEMON_POWER - ret z - jr .set_flag - -.opponent - ld a, [wAITriedAttack] - or a - ret nz - -.set_flag - ld a, %10000000 - ld [wcdda], a - ret - -; calculates AI score for bench Pokémon -; returns in a and [hTempPlayAreaLocation_ff9d] the -; Play Area location of best card to switch to. -; returns carry if no Bench Pokemon. -AIDecideBenchPokemonToSwitchTo: ; 15b72 (5:5b72) - xor a - ldh [hTempPlayAreaLocation_ff9d], a - ld a, DUELVARS_NUMBER_OF_POKEMON_IN_PLAY_AREA - call GetTurnDuelistVariable - cp 2 - ret c - -; has at least 2 Pokémon in Play Area - call Func_15b54 - call LoadDefendingPokemonColorWRAndPrizeCards - ld a, 50 - ld [wAIScore], a - ld a, DUELVARS_NUMBER_OF_POKEMON_IN_PLAY_AREA - call GetTurnDuelistVariable - ld b, a - ld c, PLAY_AREA_ARENA - push bc - jp .store_score - -.next_bench - push bc - ld a, c - ldh [hTempPlayAreaLocation_ff9d], a - ld a, 50 - ld [wAIScore], a - -; check if card can KO defending Pokémon -; if it can, raise AI score -; if on last prize card, raise AI score again - call CheckIfAnyAttackKnocksOutDefendingCard - jr nc, .check_can_use_atks - call CheckIfSelectedAttackIsUnusable - jr c, .check_can_use_atks - ld a, 10 - call AddToAIScore - ld a, [wcdda] - or %00000001 - ld [wcdda], a - call CountPrizes - cp 2 - jp nc, .check_defending_weak - ld a, 10 - call AddToAIScore - -; calculates damage of both attacks -; to raise AI score accordingly -.check_can_use_atks - xor a - ld [wSelectedAttack], a - call CheckIfSelectedAttackIsUnusable - call nc, .HandleAttackDamageScore - ld a, $01 - ld [wSelectedAttack], a - call CheckIfSelectedAttackIsUnusable - call nc, .HandleAttackDamageScore - jr .check_energy_card - -; adds to AI score depending on amount of damage -; it can inflict to the defending Pokémon -; AI score += floor(Damage / 10) + 1 -.HandleAttackDamageScore - ld a, [wSelectedAttack] - call EstimateDamage_VersusDefendingCard - ld a, [wDamage] - call CalculateByteTensDigit - inc a - call AddToAIScore - ret - -; if an energy card that is needed is found in hand -; calculate damage of the move and raise AI score -; AI score += floor(Damage / 20) -.check_energy_card - call LookForEnergyNeededInHand - jr nc, .check_attached_energy - ld a, [wSelectedAttack] - call EstimateDamage_VersusDefendingCard - ld a, [wDamage] - call CalculateByteTensDigit - srl a - call AddToAIScore - -; if no energies attached to card, lower AI score -.check_attached_energy - ldh a, [hTempPlayAreaLocation_ff9d] - ld e, a - call GetPlayAreaCardAttachedEnergies - ld a, [wTotalAttachedEnergies] - or a - jr nz, .check_mr_mime - ld a, 1 - call SubFromAIScore - -; if can damage Mr Mime, raise AI score -.check_mr_mime - ld a, DUELVARS_ARENA_CARD - call GetNonTurnDuelistVariable - call SwapTurn - call LoadCardDataToBuffer2_FromDeckIndex - call SwapTurn - cp MR_MIME - jr nz, .check_defending_weak - xor a - call EstimateDamage_VersusDefendingCard - ld a, [wDamage] - or a - jr nz, .can_damage - ld a, $01 - call EstimateDamage_VersusDefendingCard - ld a, [wDamage] - or a - jr z, .check_defending_weak -.can_damage - ld a, 5 - call AddToAIScore - -; if defending card is weak to this card, raise AI score -.check_defending_weak - ldh a, [hTempPlayAreaLocation_ff9d] - add DUELVARS_ARENA_CARD - call GetTurnDuelistVariable - call LoadCardDataToBuffer1_FromDeckIndex - ld a, [wLoadedCard1Type] - call TranslateColorToWR - ld c, a - ld hl, wAIPlayerWeakness - and [hl] - jr z, .check_defending_resist - ld a, 3 - call AddToAIScore - -; if defending card is resistant to this card, lower AI score -.check_defending_resist - ld a, c - ld hl, wAIPlayerResistance - and [hl] - jr z, .check_resistance - ld a, 2 - call SubFromAIScore - -; if this card is resistant to defending Pokémon, raise AI score -.check_resistance - ld a, [wAIPlayerColor] - ld hl, wLoadedCard1Resistance - and [hl] - jr z, .check_weakness - ld a, 2 - call AddToAIScore - -; if this card is weak to defending Pokémon, lower AI score -.check_weakness - ld a, [wAIPlayerColor] - ld hl, wLoadedCard1Weakness - and [hl] - jr z, .check_retreat_cost - ld a, 3 - call SubFromAIScore - -; if this card's retreat cost < 2, raise AI score -; if this card's retreat cost > 2, lower AI score -.check_retreat_cost - call GetPlayAreaCardRetreatCost - cp 2 - jr c, .one_or_none - jr z, .check_player_prize_count - ld a, 1 - call SubFromAIScore - jr .check_player_prize_count -.one_or_none - ld a, 1 - call AddToAIScore - -; if wcdda != $81 -; if defending Pokémon can KO this card -; if player is not at last prize card, lower 3 from AI score -; if player is at last prize card, lower 10 from AI score -.check_player_prize_count - ld a, [wcdda] - cp %10000000 | %00000001 - jr z, .check_hp - call CheckIfDefendingPokemonCanKnockOut - jr nc, .check_hp - ld e, 3 - ld a, [wAIPlayerPrizeCount] - cp 1 - jr nz, .lower_score_1 - ld e, 10 -.lower_score_1 - ld a, e - call SubFromAIScore - -; if this card's HP is 0, make AI score 0 -.check_hp - ldh a, [hTempPlayAreaLocation_ff9d] - add DUELVARS_ARENA_CARD_HP - call GetTurnDuelistVariable - or a - jr nz, .add_hp_score - ld [wAIScore], a - jr .store_score - -; AI score += floor(HP/40) -.add_hp_score - ld b, a - ld a, 4 - call CalculateBDividedByA_Bank5 - call CalculateByteTensDigit - call AddToAIScore - -; raise AI score if -; - is a Mr Mime OR -; - is a Mew1 and defending card is not basic stage - ldh a, [hTempPlayAreaLocation_ff9d] - add DUELVARS_ARENA_CARD - call GetTurnDuelistVariable - call LoadCardDataToBuffer1_FromDeckIndex - cp MR_MIME - jr z, .raise_score - cp MEW1 - jr nz, .asm_15cf0 - ld a, DUELVARS_ARENA_CARD - call GetNonTurnDuelistVariable - call LoadCardDataToBuffer2_FromDeckIndex - ld a, [wLoadedCard2Stage] - or a - jr z, .asm_15cf0 -.raise_score - ld a, 5 - call AddToAIScore - -; if wLoadedCard1Unknown2 == $01, lower AI score -.asm_15cf0 - ld a, [wLoadedCard1Unknown2] - cp $01 - jr nz, .mysterious_fossil_or_clefairy_doll - ld a, 2 - call SubFromAIScore - -; if card is Mysterious Fossil or Clefairy Doll, -; lower AI score -.mysterious_fossil_or_clefairy_doll - ld a, [wLoadedCard1ID] - cp MYSTERIOUS_FOSSIL - jr z, .lower_score_2 - cp CLEFAIRY_DOLL - jr nz, .ai_score_bonus -.lower_score_2 - ld a, 10 - call SubFromAIScore - -.ai_score_bonus - ld b, a - ld a, [wAICardListRetreatBonus + 1] - or a - jr z, .store_score - ld h, a - ld a, [wAICardListRetreatBonus] - ld l, a - -.loop_ids - ld a, [hli] - or a - jr z, .store_score ; list is over - cp b - jr nz, .next_id - ld a, [hl] - cp $80 - jr c, .subtract_score - sub $80 - call AddToAIScore - jr .next_id -.subtract_score - ld c, a - ld a, $80 - sub c - call SubFromAIScore -.next_id - inc hl - jr .loop_ids - -.store_score - ldh a, [hTempPlayAreaLocation_ff9d] - ld c, a - ld b, $00 - ld hl, wPlayAreaAIScore - add hl, bc - ld a, [wAIScore] - ld [hl], a - pop bc - inc c - dec b - jp nz, .next_bench - -; done - xor a - ld [wcdb4], a - jp FindHighestBenchScore - -; handles AI action of retreating Arena Pokémon -; and chooses which energy cards to discard. -; if card can't discard, return carry. -; in case it's Clefairy Doll or Mysterious Fossil, -; handle its effect to discard itself instead of retreating. -; input: -; - a = Play Area location (PLAY_AREA_*) of card to retreat to. -AITryToRetreat: ; 15d4f (5:5d4f) - push af - ld a, [wAIPlayEnergyCardForRetreat] - or a - jr z, .check_id - -; AI is allowed to play an energy card -; from the hand in order to provide -; the necessary energy for retreat cost - -; check status - ld a, DUELVARS_ARENA_CARD_STATUS - call GetTurnDuelistVariable - and CNF_SLP_PRZ - cp ASLEEP - jp z, .check_id - cp PARALYZED - jp z, .check_id - -; if an energy card hasn't been played yet, -; checks if the Pokémon needs just one more energy to retreat -; if it does, check if there are any energy cards in hand -; and if there are, play that energy card - ld a, [wAlreadyPlayedEnergy] - or a - jr nz, .check_id - ld e, PLAY_AREA_ARENA - call CountNumberOfEnergyCardsAttached - push af - xor a - ldh [hTempPlayAreaLocation_ff9d], a - call GetPlayAreaCardRetreatCost - pop bc - cp b - jr c, .check_id - jr z, .check_id - ; energy attached < retreat cost - sub b - cp 1 - jr nz, .check_id - call CreateEnergyCardListFromHand - jr c, .check_id - ld a, [wDuelTempList] - ldh [hTemp_ffa0], a - xor a - ldh [hTempPlayAreaLocation_ffa1], a - ld a, OPPACTION_PLAY_ENERGY - bank1call AIMakeDecision - -.check_id - ld a, DUELVARS_ARENA_CARD - call GetTurnDuelistVariable - call GetCardIDFromDeckIndex - ld a, e - cp MYSTERIOUS_FOSSIL - jp z, .mysterious_fossil_or_clefairy_doll - cp CLEFAIRY_DOLL - jp z, .mysterious_fossil_or_clefairy_doll - -; if card is Asleep or Paralyzed, set carry and exit -; else, load the status in hTemp_ffa0 - pop af - ldh [hTempPlayAreaLocation_ffa1], a - ld a, DUELVARS_ARENA_CARD_STATUS - call GetTurnDuelistVariable - ld b, a - and CNF_SLP_PRZ - cp ASLEEP - jp z, .set_carry - cp PARALYZED - jp z, .set_carry - ld a, b - ldh [hTemp_ffa0], a - ld a, $ff - ldh [hTempRetreatCostCards], a - -; check energy required to retreat -; if the cost is 0, retreat right away - xor a - ldh [hTempPlayAreaLocation_ff9d], a - call GetPlayAreaCardRetreatCost - ld [wTempCardRetreatCost], a - or a - jp z, .retreat - -; if cost > 0 and number of energy cards attached == cost -; discard them all - xor a - call CreateArenaOrBenchEnergyCardList - ld e, PLAY_AREA_ARENA - call GetPlayAreaCardAttachedEnergies - ld a, [wTotalAttachedEnergies] - ld c, a - ld a, [wTempCardRetreatCost] - cp c - jr nz, .choose_energy_discard - - ld hl, hTempRetreatCostCards - ld de, wDuelTempList -.loop_1 - ld a, [de] - inc de - ld [hli], a - cp $ff - jr nz, .loop_1 - jp .retreat - -; if cost > 0 and number of energy cards attached > cost -; choose energy cards to discard according to color -.choose_energy_discard - ld a, DUELVARS_ARENA_CARD - call GetTurnDuelistVariable - call GetCardIDFromDeckIndex - ld a, e - ld [wTempCardID], a - call LoadCardDataToBuffer1_FromCardID - ld a, [wLoadedCard1Type] - or TYPE_ENERGY - ld [wTempCardType], a - ld a, [wTempCardRetreatCost] - ld c, a - -; first, look for and discard double colorless energy -; if retreat cost is >= 2 - ld hl, wDuelTempList - ld de, hTempRetreatCostCards -.loop_2 - ld a, c - cp 2 - jr c, .energy_not_same_color - ld a, [hli] - cp $ff - jr z, .energy_not_same_color - ld [de], a - push de - call GetCardIDFromDeckIndex - ld a, e - pop de - cp DOUBLE_COLORLESS_ENERGY - jr nz, .loop_2 - ld a, [de] - call RemoveCardFromDuelTempList - dec hl - inc de - dec c - dec c - jr nz, .loop_2 - jr .end_retreat_list - -; second, shuffle attached cards and discard energy cards -; that are not of the same type as the Pokémon -; the exception for this are cards that are needed for -; some attacks but are not of the same color as the Pokémon -; (i.e. Psyduck's Headache attack) -; and energy cards attached to Eevee corresponding to a -; color of any of its evolutions (water, fire, lightning) -.energy_not_same_color - ld hl, wDuelTempList - call CountCardsInDuelTempList - call ShuffleCards -.loop_3 - ld a, [hli] - cp $ff - jr z, .any_energy - ld [de], a - call CheckIfEnergyIsUseful - jr c, .loop_3 - ld a, [de] - call RemoveCardFromDuelTempList - dec hl - inc de - dec c - jr nz, .loop_3 - jr .end_retreat_list - -; third, discard any card until -; cost requirement is met -.any_energy - ld hl, wDuelTempList -.loop_4 - ld a, [hli] - cp $ff - jr z, .set_carry - ld [de], a - inc de - push de - call GetCardIDFromDeckIndex - ld a, e - pop de - cp DOUBLE_COLORLESS_ENERGY - jr nz, .not_double_colorless - dec c - jr z, .end_retreat_list -.not_double_colorless - dec c - jr nz, .loop_4 - -.end_retreat_list - ld a, $ff - ld [de], a - -.retreat - ld a, OPPACTION_ATTEMPT_RETREAT - bank1call AIMakeDecision - or a - ret -.set_carry - scf - ret - -; handle Mysterious Fossil and Clefairy Doll -; if there are bench Pokémon, use effect to discard card -; this is equivalent to using its Pokémon Power -.mysterious_fossil_or_clefairy_doll - ld a, DUELVARS_NUMBER_OF_POKEMON_IN_PLAY_AREA - call GetTurnDuelistVariable - cp 2 - jr nc, .has_bench - ; doesn't have any bench - pop af - jr .set_carry - -.has_bench - ld a, DUELVARS_ARENA_CARD - call GetTurnDuelistVariable - ldh [hTempCardIndex_ff9f], a - xor a - ldh [hTemp_ffa0], a - ld a, OPPACTION_USE_PKMN_POWER - bank1call AIMakeDecision - pop af - ldh [hAIPkmnPowerEffectParam], a - ld a, OPPACTION_EXECUTE_PKMN_POWER_EFFECT - bank1call AIMakeDecision - ld a, OPPACTION_DUEL_MAIN_SCENE - bank1call AIMakeDecision - or a - ret diff --git a/src/engine/ai/special_attacks.asm b/src/engine/ai/special_attacks.asm deleted file mode 100644 index 770324e..0000000 --- a/src/engine/ai/special_attacks.asm +++ /dev/null @@ -1,481 +0,0 @@ -; this function handles attacks with the SPECIAL_AI_HANDLING set, -; and makes specific checks in each of these attacks -; to either return a positive score (value above $80) -; or a negative score (value below $80). -; input: -; hTempPlayAreaLocation_ff9d = location of card with attack. -HandleSpecialAIAttacks: ; 16dcd (5:6dcd) - ldh a, [hTempPlayAreaLocation_ff9d] - add DUELVARS_ARENA_CARD - call GetTurnDuelistVariable - call GetCardIDFromDeckIndex - ld a, e - - cp NIDORANF - jr z, .NidoranFCallForFamily - cp ODDISH - jr z, .CallForFamily - cp BELLSPROUT - jr z, .CallForFamily - cp EXEGGUTOR - jp z, .Teleport - cp SCYTHER - jp z, .SwordsDanceAndFocusEnergy - cp KRABBY - jr z, .CallForFamily - cp VAPOREON1 - jp z, .SwordsDanceAndFocusEnergy - cp ELECTRODE2 - jp z, .ChainLightning - cp MAROWAK1 - jr z, .CallForFriend - cp MEW3 - jp z, .DevolutionBeam - cp JIGGLYPUFF2 - jp z, .FriendshipSong - cp PORYGON - jp z, .Conversion - cp MEWTWO3 - jp z, .EnergyAbsorption - cp MEWTWO2 - jp z, .EnergyAbsorption - cp NINETALES2 - jp z, .MixUp - cp ZAPDOS3 - jp z, .BigThunder - cp KANGASKHAN - jp z, .Fetch - cp DUGTRIO - jp z, .Earthquake - cp ELECTRODE1 - jp z, .EnergySpike - cp GOLDUCK - jp z, .HyperBeam - cp DRAGONAIR - jp z, .HyperBeam - -; return zero score. -.zero_score - xor a - ret - -; if any of card ID in a is found in deck, -; return a score of $80 + slots available in bench. -.CallForFamily: ; 16e3e (5:6e3e) - ld a, CARD_LOCATION_DECK - call CheckIfAnyCardIDinLocation - jr nc, .zero_score - ld a, DUELVARS_NUMBER_OF_POKEMON_IN_PLAY_AREA - call GetTurnDuelistVariable - cp MAX_BENCH_POKEMON - jr nc, .zero_score - ld b, a - ld a, MAX_BENCH_POKEMON - sub b - add $80 - ret - -; if any of NidoranM or NidoranF is found in deck, -; return a score of $80 + slots available in bench. -.NidoranFCallForFamily: ; 16e55 (5:6e55) - ld e, NIDORANM - ld a, CARD_LOCATION_DECK - call CheckIfAnyCardIDinLocation - jr c, .found_nidoran - ld e, NIDORANF - ld a, CARD_LOCATION_DECK - call CheckIfAnyCardIDinLocation - jr nc, .zero_score -.found_nidoran - ld a, DUELVARS_NUMBER_OF_POKEMON_IN_PLAY_AREA - call GetTurnDuelistVariable - cp MAX_PLAY_AREA_POKEMON - jr nc, .zero_score - ld b, a - ld a, MAX_PLAY_AREA_POKEMON - sub b - add $80 - ret - -; checks for certain card IDs of Fighting color in deck. -; if any of them are found, return a score of -; $80 + slots available in bench. -.CallForFriend: ; 16e77 (5:6e77) - ld e, GEODUDE - ld a, CARD_LOCATION_DECK - call CheckIfAnyCardIDinLocation - jr c, .found_fighting_card - ld e, ONIX - ld a, CARD_LOCATION_DECK - call CheckIfAnyCardIDinLocation - jr c, .found_fighting_card - ld e, CUBONE - ld a, CARD_LOCATION_DECK - call CheckIfAnyCardIDinLocation - jr c, .found_fighting_card - ld e, RHYHORN - ld a, CARD_LOCATION_DECK - call CheckIfAnyCardIDinLocation - jr c, .found_fighting_card - jr .zero_score -.found_fighting_card - ld a, DUELVARS_NUMBER_OF_POKEMON_IN_PLAY_AREA - call GetTurnDuelistVariable - cp MAX_BENCH_POKEMON - jr nc, .zero_score - ld b, a - ld a, MAX_BENCH_POKEMON - sub b - add $80 - ret - -; if any basic cards are found in deck, -; return a score of $80 + slots available in bench. -.FriendshipSong: ; 16ead (5:6ead) - call CheckIfAnyBasicPokemonInDeck - jr nc, .zero_score - ld a, DUELVARS_NUMBER_OF_POKEMON_IN_PLAY_AREA - call GetTurnDuelistVariable - cp MAX_PLAY_AREA_POKEMON - jr nc, .zero_score - ld b, a - ld a, MAX_PLAY_AREA_POKEMON - sub b - add $80 - ret - -; if AI decides to retreat, return a score of $80 + 10. -.Teleport: ; 16ec2 (5:6ec2) - call AIDecideWhetherToRetreat - jp nc, .zero_score - ld a, $8a - ret - -; tests for the following conditions: -; - player is under No Damage substatus; -; - second attack is unusable; -; - second attack deals no damage; -; if any are true, returns score of $80 + 5. -.SwordsDanceAndFocusEnergy: ; 16ecb (5:6ecb) - ld a, [wAICannotDamage] - or a - jr nz, .swords_dance_focus_energy_success - ld a, SECOND_ATTACK - ld [wSelectedAttack], a - call CheckIfSelectedAttackIsUnusable - jr c, .swords_dance_focus_energy_success - ld a, SECOND_ATTACK - call EstimateDamage_VersusDefendingCard - ld a, [wDamage] - or a - jp nz, .zero_score -.swords_dance_focus_energy_success - ld a, $85 - ret - -; checks player's active card color, then -; loops through bench looking for a Pokémon -; with that same color. -; if none are found, returns score of $80 + 2. -.ChainLightning: ; 16eea (5:6eea) - call SwapTurn - call GetArenaCardColor - call SwapTurn - ld b, a - ld a, DUELVARS_BENCH - call GetTurnDuelistVariable -.loop_chain_lightning_bench - ld a, [hli] - cp $ff - jr z, .chain_lightning_success - push bc - call GetCardIDFromDeckIndex - call GetCardType - pop bc - cp b - jr nz, .loop_chain_lightning_bench - jp .zero_score -.chain_lightning_success - ld a, $82 - ret - -.DevolutionBeam: ; 16f0f (5:6f0f) - call LookForCardThatIsKnockedOutOnDevolution - jp nc, .zero_score - ld a, $85 - ret - -; first checks if card is confused, and if so return 0. -; then checks number of Pokémon in bench that are viable to use: -; - if that number is < 2 and this attack is Conversion 1 OR -; - if that number is >= 2 and this attack is Conversion 2 -; then return score of $80 + 2. -; otherwise return score of $80 + 1. -.Conversion: ; 16f18 (5:6f18) - ld a, DUELVARS_ARENA_CARD_STATUS - call GetTurnDuelistVariable - and CNF_SLP_PRZ - cp CONFUSED - jp z, .zero_score - - ld a, [wSelectedAttack] - or a - jr nz, .conversion_2 - -; conversion 1 - call CountNumberOfSetUpBenchPokemon - cp 2 - jr c, .low_conversion_score - ld a, $82 - ret - -.conversion_2 - call CountNumberOfSetUpBenchPokemon - cp 2 - jr nc, .low_conversion_score - ld a, $82 - ret - -.low_conversion_score - ld a, $81 - ret - -; if any Psychic Energy is found in the Discard Pile, -; return a score of $80 + 2. -.EnergyAbsorption: ; 16f41 (5:6f41) - ld e, PSYCHIC_ENERGY - ld a, CARD_LOCATION_DISCARD_PILE - call CheckIfAnyCardIDinLocation - jp nc, .zero_score - ld a, $82 - ret - -; if player has cards in hand, AI calls Random: -; - 1/3 chance to encourage attack regardless; -; - 1/3 chance to dismiss attack regardless; -; - 1/3 change to make some checks to player's hand. -; AI tallies number of basic cards in hand, and if this -; number is >= 2, encourage attack. -; otherwise, if it finds an evolution card in hand that -; can evolve a card in player's deck, encourage. -; if encouraged, returns a score of $80 + 3. -.MixUp: ; 16f4e (5:6f4e) - ld a, DUELVARS_NUMBER_OF_CARDS_IN_HAND - call GetNonTurnDuelistVariable - or a - ret z - - ld a, 3 - call Random - or a - jr z, .encourage_mix_up - dec a - ret z - call SwapTurn - call CreateHandCardList - call SwapTurn - or a - ret z ; return if no hand cards (again) - ld a, DUELVARS_NUMBER_OF_POKEMON_IN_PLAY_AREA - call GetNonTurnDuelistVariable - cp 3 - jr nc, .mix_up_check_play_area - - ld hl, wDuelTempList - ld b, 0 -.loop_mix_up_hand - ld a, [hli] - cp $ff - jr z, .tally_basic_cards - push bc - call SwapTurn - call LoadCardDataToBuffer2_FromDeckIndex - call SwapTurn - pop bc - ld a, [wLoadedCard2Type] - cp TYPE_ENERGY - jr nc, .loop_mix_up_hand - ld a, [wLoadedCard2Stage] - or a - jr nz, .loop_mix_up_hand - ; is a basic Pokémon card - inc b - jr .loop_mix_up_hand -.tally_basic_cards - ld a, b - cp 2 - jr nc, .encourage_mix_up - -; less than 2 basic cards in hand -.mix_up_check_play_area - ld a, DUELVARS_ARENA_CARD - call GetNonTurnDuelistVariable -.loop_mix_up_play_area - ld a, [hli] - cp $ff - jp z, .zero_score - push hl - call SwapTurn - call CheckForEvolutionInList - call SwapTurn - pop hl - jr nc, .loop_mix_up_play_area - -.encourage_mix_up - ld a, $83 - ret - -; return score of $80 + 3. -.BigThunder: ; 16fb8 (5:6fb8) - ld a, $83 - ret - -; dismiss attack if cards in deck <= 20. -; otherwise return a score of $80 + 0. -.Fetch: ; 16fbb (5:6fbb) - ld a, DUELVARS_NUMBER_OF_CARDS_NOT_IN_DECK - call GetTurnDuelistVariable - cp 41 - jp nc, .zero_score - ld a, $80 - ret - -; dismiss attack if number of own benched cards which would -; be KOd is greater than or equal to the number -; of prize cards left for player. -.Earthquake: ; 16fc8 (5:6fc8) - ld a, DUELVARS_BENCH - call GetTurnDuelistVariable - - lb de, 0, 0 -.loop_earthquake - inc e - ld a, [hli] - cp $ff - jr z, .count_prizes - ld a, e - add DUELVARS_ARENA_CARD_HP - call GetTurnDuelistVariable - cp 20 - jr nc, .loop_earthquake - inc d - jr .loop_earthquake - -.count_prizes - push de - call CountPrizes - pop de - cp d - jp c, .zero_score - jp z, .zero_score - ld a, $80 - ret - -; if there's any lightning energy cards in deck, -; return a score of $80 + 3. -.EnergySpike: ; 16ff2 (5:6ff2) - ld a, CARD_LOCATION_DECK - ld e, LIGHTNING_ENERGY - call CheckIfAnyCardIDinLocation - jp nc, .zero_score - call AIProcessButDontPlayEnergy_SkipEvolution - jp nc, .zero_score - ld a, $83 - ret - -; only incentivize attack if player's active card, -; has any energy cards attached, and if so, -; return a score of $80 + 3. -.HyperBeam: ; 17005 (5:7005) - call SwapTurn - ld e, PLAY_AREA_ARENA - call CountNumberOfEnergyCardsAttached - call SwapTurn - or a - jr z, .hyper_beam_neutral - ld a, $83 - ret -.hyper_beam_neutral - ld a, $80 - ret - -; called when second attack is determined by AI to have -; more AI score than the first attack, so that it checks -; whether the first attack is a better alternative. -CheckWhetherToSwitchToFirstAttack: ; 17019 (5:7019) -; this checks whether the first attack is also viable -; (has more than minimum score to be used) - ld a, [wFirstAttackAIScore] - cp $50 - jr c, .keep_second_attack - -; first attack has more than minimum score to be used. -; check if second attack can KO. -; in case it can't, the AI keeps it as the attack to be used. -; (possibly due to the assumption that if the -; second attack cannot KO, the first attack can't KO as well.) - xor a - ldh [hTempPlayAreaLocation_ff9d], a - call EstimateDamage_VersusDefendingCard - ld a, DUELVARS_ARENA_CARD_HP - call GetNonTurnDuelistVariable - ld hl, wDamage - sub [hl] - jr z, .check_flag - jr nc, .keep_second_attack - -; second attack can ko, check its flag. -; in case its effect is to heal user or nullify/weaken damage -; next turn, keep second attack as the option. -; otherwise switch to the first attack. -.check_flag - ld a, DUELVARS_ARENA_CARD - call GetTurnDuelistVariable - ld d, a - ld e, SECOND_ATTACK - call CopyAttackDataAndDamage_FromDeckIndex - ld a, ATTACK_FLAG2_ADDRESS | HEAL_USER_F - call CheckLoadedAttackFlag - jr c, .keep_second_attack - ld a, ATTACK_FLAG2_ADDRESS | NULLIFY_OR_WEAKEN_ATTACK_F - call CheckLoadedAttackFlag - jr c, .keep_second_attack -; switch to first attack - xor a - ld [wSelectedAttack], a - ret -.keep_second_attack - ld a, $01 - ld [wSelectedAttack], a - ret - -; returns carry if there are -; any basic Pokémon cards in deck. -CheckIfAnyBasicPokemonInDeck: ; 17057 (5:7057) - ld e, 0 -.loop - ld a, DUELVARS_CARD_LOCATIONS - add e - call GetTurnDuelistVariable - cp CARD_LOCATION_DECK - jr nz, .next - push de - ld a, e - call LoadCardDataToBuffer2_FromDeckIndex - pop de - ld a, [wLoadedCard2Type] - cp TYPE_ENERGY - jr nc, .next - ld a, [wLoadedCard2Stage] - or a - jr z, .set_carry -.next - inc e - ld a, DECK_SIZE - cp e - jr nz, .loop - or a - ret -.set_carry - scf - ret diff --git a/src/engine/ai/trainer_cards.asm b/src/engine/ai/trainer_cards.asm deleted file mode 100644 index 4bee001..0000000 --- a/src/engine/ai/trainer_cards.asm +++ /dev/null @@ -1,6073 +0,0 @@ -INCLUDE "data/duel/ai_trainer_card_logic.asm" - -_AIProcessHandTrainerCards: ; 200e5 (8:40e5) - ld [wAITrainerCardPhase], a -; create hand list in wDuelTempList and wTempHandCardList. - call CreateHandCardList - ld hl, wDuelTempList - ld de, wTempHandCardList - call CopyBuffer - ld hl, wTempHandCardList - -.loop_hand - ld a, [hli] - ld [wAITrainerCardToPlay], a - cp $ff - ret z - - push hl - ld a, [wAITrainerCardPhase] - ld d, a - ld hl, AITrainerCardLogic -.loop_data - xor a - ld [wCurrentAIFlags], a - ld a, [hli] - cp $ff - jp z, .pop_hl - -; compare input to first byte in data and continue if equal. - cp d - jp nz, .inc_hl_by_5 - - ld a, [hli] - ld [wce17], a - ld a, [wAITrainerCardToPlay] - call LoadCardDataToBuffer1_FromDeckIndex - - cp SWITCH - jr nz, .skip_switch_check - - ld b, a - ld a, [wPreviousAIFlags] - and AI_FLAG_USED_SWITCH - jr nz, .inc_hl_by_4 - ld a, b - -.skip_switch_check -; compare hand card to second byte in data and continue if equal. - ld b, a - ld a, [wce17] - cp b - jr nz, .inc_hl_by_4 - -; found Trainer card - push hl - push de - ld a, [wAITrainerCardToPlay] - ldh [hTempCardIndex_ff9f], a - -; if Headache effects prevent playing card -; move on to the next item in list. - bank1call CheckCantUseTrainerDueToHeadache - jp c, .next_in_data - - call LoadNonPokemonCardEffectCommands - ld a, EFFECTCMDTYPE_INITIAL_EFFECT_1 - call TryExecuteEffectCommandFunction - jp c, .next_in_data - -; AI can randomly choose not to play card. - farcall AIChooseRandomlyNotToDoAction - jr c, .next_in_data - -; call routine to decide whether to play Trainer card - pop de - pop hl - push hl - call CallIndirect - pop hl - jr nc, .inc_hl_by_4 - -; routine returned carry, which means -; this card should be played. - inc hl - inc hl - ld [wAITrainerCardParameter], a - -; show Play Trainer Card screen - push de - push hl - ld a, [wAITrainerCardToPlay] - ldh [hTempCardIndex_ff9f], a - ld a, OPPACTION_PLAY_TRAINER - bank1call AIMakeDecision - pop hl - pop de - jr c, .inc_hl_by_2 - -; execute the effects of the Trainer card - push hl - call CallIndirect - pop hl - - inc hl - inc hl - ld a, [wPreviousAIFlags] - ld b, a - ld a, [wCurrentAIFlags] - or b - ld [wPreviousAIFlags], a - pop hl - and AI_FLAG_MODIFIED_HAND - jp z, .loop_hand - -; the hand was modified during the Trainer effect -; so it needs to be re-listed again and -; looped from the top. - call CreateHandCardList - ld hl, wDuelTempList - ld de, wTempHandCardList - call CopyBuffer - ld hl, wTempHandCardList -; clear the AI_FLAG_MODIFIED_HAND flag - ld a, [wPreviousAIFlags] - and ~AI_FLAG_MODIFIED_HAND - ld [wPreviousAIFlags], a - jp .loop_hand - -.inc_hl_by_5 - inc hl -.inc_hl_by_4 - inc hl - inc hl -.inc_hl_by_2 - inc hl - inc hl - jp .loop_data - -.next_in_data - pop de - pop hl - inc hl - inc hl - inc hl - inc hl - jp .loop_data - -.pop_hl - pop hl - jp .loop_hand - -; makes AI use Potion card. -AIPlay_Potion: ; 201b5 (8:41b5) - ld a, [wAITrainerCardToPlay] - ldh [hTempCardIndex_ff9f], a - ld a, [wAITrainerCardParameter] - ldh [hTemp_ffa0], a - ld e, a - call GetCardDamageAndMaxHP - cp 20 - jr c, .play_card - ld a, 20 -.play_card - ldh [hTempPlayAreaLocation_ffa1], a - ld a, OPPACTION_EXECUTE_TRAINER_EFFECTS - bank1call AIMakeDecision - ret - -; if AI doesn't decide to retreat this card, -; check if defending Pokémon can KO active card -; next turn after using Potion. -; if it cannot, return carry. -; also take into account whether attack is high recoil. -AIDecide_Potion1: ; 201d1 (8:41d1) - farcall AIDecideWhetherToRetreat - jr c, .no_carry - call AICheckIfAttackIsHighRecoil - jr c, .no_carry - xor a ; active card - ldh [hTempPlayAreaLocation_ff9d], a - farcall CheckIfDefendingPokemonCanKnockOut - jr nc, .no_carry - ld d, a - - ld a, DUELVARS_ARENA_CARD_HP - call GetTurnDuelistVariable - ld h, a - ld e, PLAY_AREA_ARENA - call GetCardDamageAndMaxHP - cp 20 + 1 ; if damage <= 20 - jr c, .calculate_hp - ld a, 20 ; amount of Potion HP healing - -; if damage done by defending Pokémon next turn will still -; KO this card after healing, return no carry. -.calculate_hp - ld l, a - ld a, h - add l - sub d - jr c, .no_carry - jr z, .no_carry - -; return carry. - xor a - scf - ret -.no_carry - or a - ret - -; finds a card in Play Area to use Potion on. -; output: -; a = card to use Potion on; -; carry set if Potion should be used. -AIDecide_Potion2: ; 20204 (8:4204) - xor a - ldh [hTempPlayAreaLocation_ff9d], a - farcall CheckIfDefendingPokemonCanKnockOut - jr nc, .start_from_active -; can KO - ld d, a - ld a, DUELVARS_ARENA_CARD_HP - call GetTurnDuelistVariable - ld h, a - ld e, PLAY_AREA_ARENA - call GetCardDamageAndMaxHP - cp 20 + 1 ; if damage <= 20 - jr c, .calculate_hp - ld a, 20 -; return if using healing prevents KO. -.calculate_hp - ld l, a - ld a, h - add l - sub d - jr c, .count_prizes - jr z, .count_prizes - or a - ret - -; using Potion on active card does not prevent a KO. -; if player is at last prize, start loop with active card. -; otherwise start loop at first bench Pokémon. -.count_prizes - call SwapTurn - call CountPrizes - call SwapTurn - dec a - jr z, .start_from_active - ld e, PLAY_AREA_BENCH_1 - jr .loop - -; find Play Area Pokémon with more than 10 damage. -; skip Pokémon if it has a BOOST_IF_TAKEN_DAMAGE attack. -.start_from_active - ld e, PLAY_AREA_ARENA -.loop - ld a, DUELVARS_ARENA_CARD - add e - call GetTurnDuelistVariable - cp $ff - ret z - call .check_boost_if_taken_damage - jr c, .has_boost_damage - call GetCardDamageAndMaxHP - cp 20 ; if damage >= 20 - jr nc, .found -.has_boost_damage - inc e - jr .loop - -; a card was found, now to check if it's active or benched. -.found - ld a, e - or a - jr z, .active_card - -; bench card - push de - call SwapTurn - call CountPrizes - call SwapTurn - dec a - or a - jr z, .check_random - ld a, 10 - call Random - cp 3 -; 7/10 chance of returning carry. -.check_random - pop de - jr c, .no_carry - ld a, e - scf - ret - -; return carry for active card if not High Recoil. -.active_card - push de - call AICheckIfAttackIsHighRecoil - pop de - jr c, .no_carry - ld a, e - scf - ret -.no_carry - or a - ret - -; return carry if either of the attacks are usable -; and have the BOOST_IF_TAKEN_DAMAGE effect. -.check_boost_if_taken_damage ; 2027e (8:427e) - push de - xor a ; FIRST_ATTACK_OR_PKMN_POWER - ld [wSelectedAttack], a - farcall CheckIfSelectedAttackIsUnusable - jr c, .second_attack - ld a, ATTACK_FLAG3_ADDRESS | BOOST_IF_TAKEN_DAMAGE_F - call CheckLoadedAttackFlag - jr c, .set_carry -.second_attack - ld a, SECOND_ATTACK - ld [wSelectedAttack], a - farcall CheckIfSelectedAttackIsUnusable - jr c, .false - ld a, ATTACK_FLAG3_ADDRESS | BOOST_IF_TAKEN_DAMAGE_F - call CheckLoadedAttackFlag - jr c, .set_carry -.false - pop de - or a - ret -.set_carry - pop de - scf - ret - -; makes AI use Super Potion card. -AIPlay_SuperPotion: ; 202a8 (8:42a8) - ld a, [wAITrainerCardToPlay] - ldh [hTempCardIndex_ff9f], a - ld a, [wAITrainerCardParameter] - ldh [hTempPlayAreaLocation_ffa1], a - call AIPickEnergyCardToDiscard - ldh [hTemp_ffa0], a - ld a, [wAITrainerCardParameter] - ld e, a - call GetCardDamageAndMaxHP - cp 40 - jr c, .play_card - ld a, 40 -.play_card - ldh [hTempRetreatCostCards], a - ld a, OPPACTION_EXECUTE_TRAINER_EFFECTS - bank1call AIMakeDecision - ret - -; if AI doesn't decide to retreat this card and card has -; any energy cards attached, check if defending Pokémon can KO -; active card next turn after using Super Potion. -; if it cannot, return carry. -; also take into account whether attack is high recoil. -AIDecide_SuperPotion1: ; 202cc (8:42cc) - farcall AIDecideWhetherToRetreat - jr c, .no_carry - call AICheckIfAttackIsHighRecoil - jr c, .no_carry - xor a - ldh [hTempPlayAreaLocation_ff9d], a - ld e, a - call .check_attached_energy - ret nc - farcall CheckIfDefendingPokemonCanKnockOut - jr nc, .no_carry - - ld d, a - ld d, a - ld a, DUELVARS_ARENA_CARD_HP - call GetTurnDuelistVariable - ld h, a - ld e, PLAY_AREA_ARENA - call GetCardDamageAndMaxHP - cp 40 + 1 ; if damage < 40 - jr c, .calculate_hp - ld a, 40 -.calculate_hp - ld l, a - ld a, h - add l - sub d - jr c, .no_carry - jr z, .no_carry - -; return carry - ld a, e - scf - ret -.no_carry - or a - ret - -; returns carry if card has energies attached. -.check_attached_energy ; 20305 (8:4305) - call GetPlayAreaCardAttachedEnergies - ld a, [wTotalAttachedEnergies] - or a - ret z - scf - ret - -; finds a card in Play Area to use Super Potion on. -; output: -; a = card to use Super Potion on; -; carry set if Super Potion should be used. -AIDecide_SuperPotion2: ; 2030f (8:430f) - xor a - ldh [hTempPlayAreaLocation_ff9d], a - farcall CheckIfDefendingPokemonCanKnockOut - jr nc, .start_from_active -; can KO - ld d, a - ld a, DUELVARS_ARENA_CARD_HP - call GetTurnDuelistVariable - ld h, a - ld e, PLAY_AREA_ARENA - call GetCardDamageAndMaxHP - cp 40 + 1 ; if damage < 40 - jr c, .calculate_hp - ld a, 40 -; return if using healing prevents KO. -.calculate_hp - ld l, a - ld a, h - add l - sub d - jr c, .count_prizes - jr z, .count_prizes - or a - ret - -; using Super Potion on active card does not prevent a KO. -; if player is at last prize, start loop with active card. -; otherwise start loop at first bench Pokémon. -.count_prizes - call SwapTurn - call CountPrizes - call SwapTurn - dec a - jr z, .start_from_active - ld e, PLAY_AREA_BENCH_1 - jr .loop - -; find Play Area Pokémon with more than 30 damage. -; skip Pokémon if it doesn't have any energy attached, -; has a BOOST_IF_TAKEN_DAMAGE attack, -; or if discarding makes any attack of its attacks unusable. -.start_from_active - ld e, PLAY_AREA_ARENA -.loop - ld a, DUELVARS_ARENA_CARD - add e - call GetTurnDuelistVariable - cp $ff - ret z - ld d, a - call .check_attached_energy - jr nc, .next - call .check_boost_if_taken_damage - jr c, .next - call .check_energy_cost - jr c, .next - call GetCardDamageAndMaxHP - cp 40 ; if damage >= 40 - jr nc, .found -.next - inc e - jr .loop - -; a card was found, now to check if it's active or benched. -.found - ld a, e - or a - jr z, .active_card - -; bench card - push de - call SwapTurn - call CountPrizes - call SwapTurn - dec a - or a - jr z, .check_random - ld a, 10 - call Random - cp 3 -; 7/10 chance of returning carry. -.check_random - pop de - jr c, .no_carry - ld a, e - scf - ret - -; return carry for active card if not Hgh Recoil. -.active_card - push de - call AICheckIfAttackIsHighRecoil - pop de - jr c, .no_carry - ld a, e - scf - ret -.no_carry - or a - ret - -; returns carry if card has energies attached. -.check_attached_energy ; 20394 (8:4394) - call GetPlayAreaCardAttachedEnergies - ld a, [wTotalAttachedEnergies] - or a - ret z - scf - ret - -; return carry if either of the attacks are usable -; and have the BOOST_IF_TAKEN_DAMAGE effect. -.check_boost_if_taken_damage ; 2039e (8:439e) - push de - xor a ; FIRST_ATTACK_OR_PKMN_POWER - ld [wSelectedAttack], a - farcall CheckIfSelectedAttackIsUnusable - jr c, .second_attack_1 - ld a, ATTACK_FLAG3_ADDRESS | BOOST_IF_TAKEN_DAMAGE_F - call CheckLoadedAttackFlag - jr c, .true_1 -.second_attack_1 - ld a, SECOND_ATTACK - ld [wSelectedAttack], a - farcall CheckIfSelectedAttackIsUnusable - jr c, .false_1 - ld a, ATTACK_FLAG3_ADDRESS | BOOST_IF_TAKEN_DAMAGE_F - call CheckLoadedAttackFlag - jr c, .true_1 -.false_1 - pop de - or a - ret -.true_1 - pop de - scf - ret - -; returns carry if discarding energy card renders any attack unusable, -; given that they have enough energy to be used before discarding. -.check_energy_cost ; 203c8 (8:43c8) - push de - xor a ; FIRST_ATTACK_OR_PKMN_POWER - ld [wSelectedAttack], a - ld a, e - ldh [hTempPlayAreaLocation_ff9d], a - farcall CheckEnergyNeededForAttack - jr c, .second_attack_2 - farcall CheckEnergyNeededForAttackAfterDiscard - jr c, .true_2 - -.second_attack_2 - pop de - push de - ld a, SECOND_ATTACK - ld [wSelectedAttack], a - ld a, e - ldh [hTempPlayAreaLocation_ff9d], a - farcall CheckEnergyNeededForAttack - jr c, .false_2 - farcall CheckEnergyNeededForAttackAfterDiscard - jr c, .true_2 - -.false_2 - pop de - or a - ret -.true_2 - pop de - scf - ret - -AIPlay_Defender: ; 203f8 (8:43f8) - ld a, [wAITrainerCardToPlay] - ldh [hTempCardIndex_ff9f], a - xor a - ldh [hTemp_ffa0], a - ld a, OPPACTION_EXECUTE_TRAINER_EFFECTS - bank1call AIMakeDecision - ret - -; returns carry if using Defender can prevent a KO -; by the defending Pokémon. -; this takes into account both attacks and whether they're useable. -AIDecide_Defender1: ; 20406 (8:4406) - xor a ; PLAY_AREA_ARENA - ldh [hTempPlayAreaLocation_ff9d], a - farcall CheckIfAnyAttackKnocksOutDefendingCard - jr nc, .cannot_ko - farcall CheckIfSelectedAttackIsUnusable - jr nc, .no_carry - farcall LookForEnergyNeededForAttackInHand - jr c, .no_carry - -.cannot_ko -; check if any of the defending Pokémon's attacks deal -; damage exactly equal to current HP, and if so, -; only continue if that attack is useable. - farcall CheckIfAnyDefendingPokemonAttackDealsSameDamageAsHP - jr nc, .no_carry - call SwapTurn - farcall CheckIfSelectedAttackIsUnusable - call SwapTurn - jr c, .no_carry - - ld a, [wSelectedAttack] - farcall EstimateDamage_FromDefendingPokemon - ld a, [wDamage] - ld [wce06], a - ld d, a - -; load in a the attack that was not selected, -; and check if it is useable. - ld a, [wSelectedAttack] - ld b, a - ld a, $01 - sub b - ld [wSelectedAttack], a - push de - call SwapTurn - farcall CheckIfSelectedAttackIsUnusable - call SwapTurn - pop de - jr c, .switch_back - -; the other attack is useable. -; compare its damage to the selected attack. - ld a, [wSelectedAttack] - push de - farcall EstimateDamage_FromDefendingPokemon - pop de - ld a, [wDamage] - cp d - jr nc, .subtract - -; in case the non-selected attack is useable -; and deals less damage than the selected attack, -; switch back to the other attack. -.switch_back - ld a, [wSelectedAttack] - ld b, a - ld a, $01 - sub b - ld [wSelectedAttack], a - ld a, [wce06] - ld [wDamage], a - -; now the selected attack is the one that deals -; the most damage of the two (and is useable). -; if subtracting damage by using Defender -; still prevents a KO, return carry. -.subtract - ld a, [wDamage] - sub 20 - ld d, a - ld a, DUELVARS_ARENA_CARD_HP - call GetTurnDuelistVariable - sub d - jr c, .no_carry - jr z, .no_carry - scf - ret -.no_carry - or a - ret - -; return carry if using Defender prevents Pokémon -; from being knocked out by an attack with recoil. -AIDecide_Defender2: ; 20486 (8:4486) - ld a, ATTACK_FLAG1_ADDRESS | HIGH_RECOIL_F - call CheckLoadedAttackFlag - jr c, .recoil - ld a, ATTACK_FLAG1_ADDRESS | LOW_RECOIL_F - call CheckLoadedAttackFlag - jr c, .recoil - or a - ret - -.recoil - ld a, DUELVARS_ARENA_CARD - call GetTurnDuelistVariable - call LoadCardDataToBuffer2_FromDeckIndex - ld a, [wSelectedAttack] - or a - jr nz, .second_attack -; first attack - ld a, [wLoadedCard2Atk1EffectParam] - jr .check_weak -.second_attack - ld a, [wLoadedCard2Atk2EffectParam] - -; double recoil damage if card is weak to its own color. -.check_weak - ld d, a - push de - call GetArenaCardColor - call TranslateColorToWR - ld b, a - call GetArenaCardWeakness - and b - pop de - jr z, .check_resist - sla d - -; subtract 30 from recoil damage if card resists its own color. -; if this yields a negative number, return no carry. -.check_resist - push de - call GetArenaCardColor - call TranslateColorToWR - ld b, a - call GetArenaCardResistance - and b - pop de - jr z, .subtract - ld a, d - sub 30 - jr c, .no_carry - ld d, a - -; subtract damage prevented by Defender. -; if damage still knocks out card, return no carry. -; if damage does not knock out, return carry. -.subtract - ld a, d - or a - jr z, .no_carry - sub 20 - ld d, a - ld a, DUELVARS_ARENA_CARD_HP - call GetTurnDuelistVariable - sub d - jr c, .no_carry - jr z, .no_carry - scf - ret -.no_carry - or a - ret - -AIPlay_Pluspower: ; 204e8 (8:44e8) - ld a, [wCurrentAIFlags] - or AI_FLAG_USED_PLUSPOWER - ld [wCurrentAIFlags], a - ld a, [wAITrainerCardParameter] - ld [wAIPluspowerAttack], a - ld a, [wAITrainerCardToPlay] - ldh [hTempCardIndex_ff9f], a - ld a, OPPACTION_EXECUTE_TRAINER_EFFECTS - bank1call AIMakeDecision - ret - -; returns carry if using a Pluspower can KO defending Pokémon -; if active card cannot KO without the boost. -; outputs in a the attack to use. -AIDecide_Pluspower1: ; 20501 (8:4501) -; this is mistakenly duplicated - xor a - ldh [hTempPlayAreaLocation_ff9d], a - xor a - ldh [hTempPlayAreaLocation_ff9d], a - -; continue if no attack can knock out. -; if there's an attack that can, only continue -; if it's unusable and there's no card in hand -; to fulfill its energy cost. - farcall CheckIfAnyAttackKnocksOutDefendingCard - jr nc, .cannot_ko - farcall CheckIfSelectedAttackIsUnusable - jr nc, .no_carry - farcall LookForEnergyNeededForAttackInHand - jr c, .no_carry - -; cannot use an attack that knocks out. -.cannot_ko -; get active Pokémon's info. - ld a, DUELVARS_ARENA_CARD - call GetTurnDuelistVariable - call GetCardIDFromDeckIndex - ld a, e - ld [wTempTurnDuelistCardID], a - -; get defending Pokémon's info and check -; its No Damage or Effect substatus. -; if substatus is active, return. - call SwapTurn - ld a, DUELVARS_ARENA_CARD - call GetTurnDuelistVariable - call GetCardIDFromDeckIndex - ld a, e - ld [wTempNonTurnDuelistCardID], a - bank1call HandleNoDamageOrEffectSubstatus - call SwapTurn - jr c, .no_carry - -; check both attacks and decide which one -; can KO with Pluspower boost. -; if neither can KO, return no carry. - xor a ; FIRST_ATTACK_OR_PKMN_POWER - ld [wSelectedAttack], a - call .check_ko_with_pluspower - jr c, .kos_with_pluspower_1 - ld a, SECOND_ATTACK - ld [wSelectedAttack], a - call .check_ko_with_pluspower - jr c, .kos_with_pluspower_2 - -.no_carry - or a - ret - -; first attack can KO with Pluspower. -.kos_with_pluspower_1 - call .check_mr_mime - jr nc, .no_carry - xor a ; FIRST_ATTACK_OR_PKMN_POWER - scf - ret -; second attack can KO with Pluspower. -.kos_with_pluspower_2 - call .check_mr_mime - jr nc, .no_carry - ld a, SECOND_ATTACK - scf - ret - -; return carry if attack is useable and KOs -; defending Pokémon with Pluspower boost. -.check_ko_with_pluspower ; 20562 (8:4562) - farcall CheckIfSelectedAttackIsUnusable - jr c, .unusable - ld a, [wSelectedAttack] - farcall EstimateDamage_VersusDefendingCard - ld a, DUELVARS_ARENA_CARD_HP - call GetNonTurnDuelistVariable - ld b, a - ld hl, wDamage - sub [hl] - jr c, .no_carry - jr z, .no_carry - ld a, [hl] - add 10 ; add Pluspower boost - ld c, a - ld a, b - sub c - ret c ; return carry if damage > HP left - ret nz ; does not KO - scf - ret ; KOs with Pluspower boost -.unusable - or a - ret - -; returns carry if Pluspower boost does -; not exceed 30 damage when facing Mr. Mime. -.check_mr_mime ; 20589 (8:4589) - ld a, [wDamage] - add 10 ; add Pluspower boost - cp 30 ; no danger in preventing damage - ret c - call SwapTurn - ld a, DUELVARS_ARENA_CARD - call GetTurnDuelistVariable - call GetCardIDFromDeckIndex - call SwapTurn - ld a, e - cp MR_MIME - ret z -; damage is >= 30 but not Mr. Mime - scf - ret - -; returns carry 7/10 of the time -; if selected attack is useable, can't KO without Pluspower boost -; can damage Mr. Mime even with Pluspower boost -; and has a minimum damage > 0. -; outputs in a the attack to use. -AIDecide_Pluspower2: ; 205a5 (8:45a5) - xor a - ldh [hTempPlayAreaLocation_ff9d], a - call .check_can_ko - jr nc, .no_carry - call .check_random - jr nc, .no_carry - call .check_mr_mime - jr nc, .no_carry - scf - ret -.no_carry - or a - ret - -; returns carry if Pluspower boost does -; not exceed 30 damage when facing Mr. Mime. -.check_mr_mime ; 205bb (8:45bb) - ld a, [wDamage] - add 10 ; add Pluspower boost - cp 30 ; no danger in preventing damage - ret c - call SwapTurn - ld a, DUELVARS_ARENA_CARD - call GetTurnDuelistVariable - call GetCardIDFromDeckIndex - call SwapTurn - ld a, e - cp MR_MIME - ret z -; damage is >= 30 but not Mr. Mime - scf - ret - -; return carry if attack is useable but cannot KO. -.check_can_ko ; 205d7 (8:45d7) - farcall CheckIfSelectedAttackIsUnusable - jr c, .unusable - ld a, [wSelectedAttack] - farcall EstimateDamage_VersusDefendingCard - ld a, DUELVARS_ARENA_CARD_HP - call GetNonTurnDuelistVariable - ld b, a - ld hl, wDamage - sub [hl] - jr c, .no_carry - jr z, .no_carry -; can't KO. - scf - ret -.unusable - or a - ret - -; return carry 7/10 of the time if -; attack is useable and minimum damage > 0. -.check_random ; 205f6 (8:45f6) - farcall CheckIfSelectedAttackIsUnusable - jr c, .unusable - ld a, [wSelectedAttack] - farcall EstimateDamage_VersusDefendingCard - ld a, [wAIMinDamage] - cp 10 - jr c, .unusable - ld a, 10 - call Random - cp 3 - ret - -AIPlay_Switch: ; 20612 (8:4612) - ld a, [wCurrentAIFlags] - or AI_FLAG_USED_SWITCH - ld [wCurrentAIFlags], a - ld a, [wAITrainerCardToPlay] - ldh [hTempCardIndex_ff9f], a - ld a, [wAITrainerCardParameter] - ldh [hTemp_ffa0], a - ld a, OPPACTION_EXECUTE_TRAINER_EFFECTS - bank1call AIMakeDecision - xor a - ld [wcdb4], a - ret - -; returns carry if the active card has less energy cards -; than the retreat cost and if AI can't play an energy -; card from the hand to fulfill the cost -AIDecide_Switch: ; 2062e (8:462e) -; check if AI can already play an energy card from hand to retreat - ld a, [wAIPlayEnergyCardForRetreat] - or a - jr z, .check_cost_amount - -; can't play energy card from hand to retreat -; compare number of energy cards attached to retreat cost - xor a ; PLAY_AREA_ARENA - ldh [hTempPlayAreaLocation_ff9d], a - call GetPlayAreaCardRetreatCost - push af - ld e, PLAY_AREA_ARENA - farcall CountNumberOfEnergyCardsAttached - ld b, a - pop af - sub b - ; jump if cards attached > retreat cost - jr c, .check_cost_amount - cp 2 - ; jump if retreat cost is 2 more energy cards - ; than the number of cards attached - jr nc, .switch - -.check_cost_amount - xor a ; PLAY_AREA_ARENA - ldh [hTempPlayAreaLocation_ff9d], a - call GetPlayAreaCardRetreatCost - cp 3 - ; jump if retreat cost >= 3 - jr nc, .switch - - push af - ld e, PLAY_AREA_ARENA - farcall CountNumberOfEnergyCardsAttached - pop bc - cp b - ; jump if energy cards attached < retreat cost - jr c, .switch - ret - -.switch - farcall AIDecideBenchPokemonToSwitchTo - ccf - ret - -AIPlay_GustOfWind: ; 20666 (8:4666) - ld a, [wCurrentAIFlags] - or AI_FLAG_USED_GUST_OF_WIND - ld [wCurrentAIFlags], a - ld a, [wAITrainerCardToPlay] - ldh [hTempCardIndex_ff9f], a - ld a, [wAITrainerCardParameter] - ldh [hTemp_ffa0], a - ld a, OPPACTION_EXECUTE_TRAINER_EFFECTS - bank1call AIMakeDecision - ret - -AIDecide_GustOfWind: ; 2067e (8:467e) - ld a, DUELVARS_NUMBER_OF_POKEMON_IN_PLAY_AREA - call GetNonTurnDuelistVariable - dec a - or a - ret z ; no bench cards - -; if used Gust Of Wind already, -; do not use it again. - ld a, [wPreviousAIFlags] - and AI_FLAG_USED_GUST_OF_WIND - ret nz - - farcall CheckIfActivePokemonCanUseAnyNonResidualAttack - ret nc ; no non-residual attack can be used - - xor a ; PLAY_AREA_ARENA - ldh [hTempPlayAreaLocation_ff9d], a - farcall CheckIfAnyAttackKnocksOutDefendingCard - jr nc, .check_id ; if can't KO - farcall CheckIfSelectedAttackIsUnusable - jr nc, .no_carry ; if KO attack is useable - farcall LookForEnergyNeededForAttackInHand - jr c, .no_carry ; if energy card is in hand - -.check_id - ; skip if current active card is MEW3 or MEWTWO1 - ld a, DUELVARS_ARENA_CARD - call GetTurnDuelistVariable - call GetCardIDFromDeckIndex - ld a, e - cp MEW3 - jr z, .no_carry - cp MEWTWO1 - jr z, .no_carry - - call .FindBenchCardToKnockOut - ret c - - xor a ; PLAY_AREA_ARENA - ldh [hTempPlayAreaLocation_ff9d], a - call .CheckIfNoAttackDealsDamage - jr c, .check_bench_energy - - ; skip if current arena card's color is - ; the defending card's weakness - call GetArenaCardColor - call TranslateColorToWR - ld b, a - call SwapTurn - call GetArenaCardWeakness - call SwapTurn - and b - jr nz, .no_carry - -; check weakness - call .FindBenchCardWithWeakness - ret nc ; no bench card weak to arena card - scf - ret ; found bench card weak to arena card - -.no_carry - or a - ret - -; being here means AI's arena card cannot damage player's arena card - -; first check if there is a card in player's bench that -; has no attached energy cards and that the AI can damage -.check_bench_energy - ; return carry if there's a bench card with weakness - call .FindBenchCardWithWeakness - ret c - - ld a, DUELVARS_NUMBER_OF_POKEMON_IN_PLAY_AREA - call GetNonTurnDuelistVariable - ld d, a - ld e, PLAY_AREA_ARENA -; loop through bench and check attached energy cards -.loop_1 - inc e - dec d - jr z, .check_bench_hp - call SwapTurn - call GetPlayAreaCardAttachedEnergies - call SwapTurn - ld a, [wTotalAttachedEnergies] - or a - jr nz, .loop_1 ; skip if has energy attached - call .CheckIfCanDamageBenchedCard - jr nc, .loop_1 - ld a, e - scf - ret - -.check_bench_hp - ld a, $ff - ld [wce06], a - xor a - ld [wce08], a - ld e, a - ld a, DUELVARS_NUMBER_OF_POKEMON_IN_PLAY_AREA - call GetNonTurnDuelistVariable - ld d, a - -; find bench card with least amount of available HP -.loop_2 - inc e - dec d - jr z, .check_found - ld a, e - add DUELVARS_ARENA_CARD_HP - call GetNonTurnDuelistVariable - ld b, a - ld a, [wce06] - inc b - cp b - jr c, .loop_2 - call .CheckIfCanDamageBenchedCard - jr nc, .loop_2 - dec b - ld a, b - ld [wce06], a - ld a, e - ld [wce08], a - jr .loop_2 - -.check_found - ld a, [wce08] - or a - jr z, .no_carry -; a card was found - -.set_carry - scf - ret - -.check_can_damage - push bc - push hl - xor a ; PLAY_AREA_ARENA - farcall CheckIfCanDamageDefendingPokemon - pop hl - pop bc - jr nc, .loop_3 - ld a, c - scf - ret - -; returns carry if any of the player's -; benched cards is weak to color in b -; and has a way to damage it -.FindBenchCardWithWeakness ; 2074d (8:474d) - ld a, DUELVARS_BENCH - call GetNonTurnDuelistVariable - ld c, PLAY_AREA_ARENA -.loop_3 - inc c - ld a, [hli] - cp $ff - jr z, .no_carry - call SwapTurn - call LoadCardDataToBuffer1_FromDeckIndex - call SwapTurn - ld a, [wLoadedCard1Weakness] - and b - jr nz, .check_can_damage - jr .loop_3 - -; returns carry if neither attack can deal damage -.CheckIfNoAttackDealsDamage ; 2076b (8:476b) - xor a ; FIRST_ATTACK_OR_PKMN_POWER - ld [wSelectedAttack], a - call .CheckIfAttackDealsNoDamage - jr c, .second_attack - ret -.second_attack - ld a, SECOND_ATTACK - ld [wSelectedAttack], a - call .CheckIfAttackDealsNoDamage - jr c, .true - ret -.true - scf - ret - -; returns carry if attack is Pokemon Power -; or otherwise doesn't deal any damage -.CheckIfAttackDealsNoDamage ; 20782 (8:4782) - ld a, [wSelectedAttack] - ld e, a - ld a, DUELVARS_ARENA_CARD - call GetTurnDuelistVariable - ld d, a - call CopyAttackDataAndDamage_FromDeckIndex - ld a, [wLoadedAttackCategory] - - ; skip if attack is a Power or has 0 damage - cp POKEMON_POWER - jr z, .no_damage - ld a, [wDamage] - or a - ret z - - ; check damage against defending card - ld a, [wSelectedAttack] - farcall EstimateDamage_VersusDefendingCard - ld a, [wAIMaxDamage] - or a - ret nz - -.no_damage - scf - ret - -; returns carry if there is a player's bench card that -; the opponent's current active card can KO -.FindBenchCardToKnockOut ; 207a9 (8:47a9) - ld a, DUELVARS_BENCH - call GetNonTurnDuelistVariable - ld e, PLAY_AREA_BENCH_1 - -.loop_4 - ld a, [hli] - cp $ff - ret z - -; overwrite the player's active card and its HP -; with the current bench card that is being checked - push hl - push de - ld b, a - ld a, DUELVARS_ARENA_CARD - call GetNonTurnDuelistVariable - push af - ld [hl], b - ld a, e - add DUELVARS_ARENA_CARD_HP - call GetNonTurnDuelistVariable - ld b, a - ld a, DUELVARS_ARENA_CARD_HP - call GetNonTurnDuelistVariable - push af - ld [hl], b - - xor a ; PLAY_AREA_ARENA - ldh [hTempPlayAreaLocation_ff9d], a - call .CheckIfAnyAttackKnocksOut - jr nc, .next - farcall CheckIfSelectedAttackIsUnusable - jr nc, .found - farcall LookForEnergyNeededForAttackInHand - jr c, .found - -; the following two local routines can be condensed into one -; since they both revert the player's arena card -.next - ld a, DUELVARS_ARENA_CARD_HP - call GetNonTurnDuelistVariable - pop af - ld [hl], a - ld a, DUELVARS_ARENA_CARD - call GetNonTurnDuelistVariable - pop af - ld [hl], a - pop de - inc e - pop hl - jr .loop_4 - -; revert player's arena card and set carry -.found - ld a, DUELVARS_ARENA_CARD_HP - call GetNonTurnDuelistVariable - pop af - ld [hl], a - ld a, DUELVARS_ARENA_CARD - call GetNonTurnDuelistVariable - pop af - ld [hl], a - pop de - ld a, e - pop hl - scf - ret - -; returns carry if any of arena card's attacks -; KOs player card in location stored in e -.CheckIfAnyAttackKnocksOut ; 20806 (8:4806) - xor a ; FIRST_ATTACK_OR_PKMN_POWER - call .CheckIfAttackKnocksOut - ret c - ld a, SECOND_ATTACK - -; returns carry if attack KOs player card -; in location stored in e -.CheckIfAttackKnocksOut - push de - farcall EstimateDamage_VersusDefendingCard - pop de - ld a, DUELVARS_ARENA_CARD_HP - add e - call GetNonTurnDuelistVariable - ld hl, wDamage - sub [hl] - ret c - ret nz - scf - ret - -; returns carry if opponent's arena card can damage -; this benched card if it were switched with -; the player's arena card -.CheckIfCanDamageBenchedCard ; 20821 (8:4821) - push bc - push de - push hl - - ; overwrite arena card data - ld a, e - add DUELVARS_ARENA_CARD - call GetNonTurnDuelistVariable - ld b, a - ld a, DUELVARS_ARENA_CARD - call GetNonTurnDuelistVariable - push af - ld [hl], b - - ; overwrite arena card HP - ld a, e - add DUELVARS_ARENA_CARD_HP - call GetNonTurnDuelistVariable - ld b, a - ld a, DUELVARS_ARENA_CARD_HP - call GetNonTurnDuelistVariable - push af - ld [hl], b - - xor a ; PLAY_AREA_ARENA - farcall CheckIfCanDamageDefendingPokemon - jr c, .can_damage - -; the following two local routines can be condensed into one -; since they both revert the player's arena card - -; can't damage - ld a, DUELVARS_ARENA_CARD_HP - call GetNonTurnDuelistVariable - pop af - ld [hl], a - ld a, DUELVARS_ARENA_CARD - call GetNonTurnDuelistVariable - pop af - ld [hl], a - pop hl - pop de - pop bc - or a - ret - -.can_damage - ld a, DUELVARS_ARENA_CARD_HP - call GetNonTurnDuelistVariable - pop af - ld [hl], a - ld a, DUELVARS_ARENA_CARD - call GetNonTurnDuelistVariable - pop af - ld [hl], a - pop hl - pop de - pop bc - scf - ret - -AIPlay_Bill: ; 2086d (8:486d) - ld a, [wAITrainerCardToPlay] - ldh [hTempCardIndex_ff9f], a - ld a, OPPACTION_EXECUTE_TRAINER_EFFECTS - bank1call AIMakeDecision - ret - -; return carry if cards in deck > 9 -AIDecide_Bill: ; 20878 (8:4878) - ld a, DUELVARS_NUMBER_OF_CARDS_NOT_IN_DECK - call GetTurnDuelistVariable - cp DECK_SIZE - 9 - ret - -AIPlay_EnergyRemoval: ; 20880 (8:4880) - ld a, [wAITrainerCardToPlay] - ldh [hTempCardIndex_ff9f], a - ld a, [wAITrainerCardParameter] - ldh [hTemp_ffa0], a - ld a, [wce1a] - ldh [hTempPlayAreaLocation_ffa1], a - ld a, OPPACTION_EXECUTE_TRAINER_EFFECTS - bank1call AIMakeDecision - ret - -; picks an energy card in the player's Play Area to remove -AIDecide_EnergyRemoval: ; 20895 (8:4895) -; check if the current active card can KO player's card -; if it's possible to KO, then do not consider the player's -; active card to remove its attached energy - xor a ; PLAY_AREA_ARENA - ldh [hTempPlayAreaLocation_ff9d], a - farcall CheckIfAnyAttackKnocksOutDefendingCard - jr nc, .cannot_ko - farcall CheckIfSelectedAttackIsUnusable - jr nc, .can_ko - farcall LookForEnergyNeededForAttackInHand - jr nc, .cannot_ko - -.can_ko - ; start checking from the bench - ld a, PLAY_AREA_BENCH_1 - ld [wce0f], a - jr .check_bench_energy -.cannot_ko - ; start checking from the arena card - xor a ; PLAY_AREA_ARENA - ld [wce0f], a - -; loop each card and check if it has enough energy to use any attack -; if it does, then proceed to pick an energy card to remove -.check_bench_energy - call SwapTurn - ld a, [wce0f] - ld e, a -.loop_1 - ld a, DUELVARS_ARENA_CARD - add e - call GetTurnDuelistVariable - cp $ff - jr z, .default - - ld d, a - call .CheckIfCardHasEnergyAttached - jr nc, .next_1 - call .CheckIfNotEnoughEnergyToAttack - jr nc, .pick_energy ; jump if enough energy to attack -.next_1 - inc e - jr .loop_1 - -.pick_energy -; a play area card was picked to remove energy -; store the picked energy card to remove in wce1a -; and set carry - ld a, e - push af - call PickAttachedEnergyCardToRemove - ld [wce1a], a - pop af - call SwapTurn - scf - ret - -; if no card in player's Play Area was found with enough energy -; to attack, just pick an energy card from player's active card -; (in case the AI cannot KO it this turn) -.default - ld a, [wce0f] - or a - jr nz, .check_bench_damage ; not active card - call .CheckIfCardHasEnergyAttached - jr c, .pick_energy - -; lastly, check what attack on player's Play Area is highest damaging -; and pick an energy card attached to that Pokemon to remove -.check_bench_damage - xor a - ld [wce06], a - ld [wce08], a - - ld e, PLAY_AREA_BENCH_1 -.loop_2 - ld a, DUELVARS_ARENA_CARD - add e - call GetTurnDuelistVariable - cp $ff - jr z, .found_damage - - ld d, a - call .CheckIfCardHasEnergyAttached - jr nc, .next_2 - call .FindHighestDamagingAttack -.next_2 - inc e - jr .loop_2 - -.found_damage - ld a, [wce08] - or a - jr z, .no_carry ; skip if none found - ld e, a - jr .pick_energy -.no_carry - call SwapTurn - or a - ret - -; returns carry if this card has any energy cards attached -.CheckIfCardHasEnergyAttached ; 2091a (8:491a) - call GetPlayAreaCardAttachedEnergies - ld a, [wTotalAttachedEnergies] - or a - ret z - scf - ret - -; returns carry if this card does not -; have enough energy for either of its attacks -.CheckIfNotEnoughEnergyToAttack ; 20924 (8:4924) - push de - xor a ; FIRST_ATTACK_OR_PKMN_POWER - ld [wSelectedAttack], a - ld a, e - ldh [hTempPlayAreaLocation_ff9d], a - farcall CheckEnergyNeededForAttack - jr nc, .enough_energy - pop de - - push de - ld a, SECOND_ATTACK - ld [wSelectedAttack], a - ld a, e - ldh [hTempPlayAreaLocation_ff9d], a - farcall CheckEnergyNeededForAttack - jr nc, .check_surplus - pop de - -; neither attack has enough energy - scf - ret - -.enough_energy - pop de - or a - ret - -; first attack doesn't have enough energy (or is just a Pokemon Power) -; but second attack has enough energy to be used -; check if there's surplus energy for attack and, if so, return carry -.check_surplus - farcall CheckIfNoSurplusEnergyForAttack - pop de - ccf - ret - -; stores in wce06 the highest damaging attack -; for the card in play area location in e -; and stores this card's location in wce08 -.FindHighestDamagingAttack ; 2094f (8:494f) - push de - ld a, e - ldh [hTempPlayAreaLocation_ff9d], a - - xor a ; FIRST_ATTACK_OR_PKMN_POWER - farcall EstimateDamage_VersusDefendingCard - ld a, [wDamage] - or a - jr z, .skip_1 - ld e, a - ld a, [wce06] - cp e - jr nc, .skip_1 - ld a, e - ld [wce06], a ; store this damage value - pop de - ld a, e - ld [wce08], a ; store this location - jr .second_attack - -.skip_1 - pop de - -.second_attack - push de - ld a, e - ldh [hTempPlayAreaLocation_ff9d], a - - ld a, SECOND_ATTACK - farcall EstimateDamage_VersusDefendingCard - ld a, [wDamage] - or a - jr z, .skip_2 - ld e, a - ld a, [wce06] - cp e - jr nc, .skip_2 - ld a, e - ld [wce06], a ; store this damage value - pop de - ld a, e - ld [wce08], a ; store this location - ret -.skip_2 - pop de - ret - -AIPlay_SuperEnergyRemoval: ; 20994 (8:4994) - ld a, [wAITrainerCardToPlay] - ldh [hTempCardIndex_ff9f], a - ld a, [wAITrainerCardParameter] - ldh [hTemp_ffa0], a - ld a, [wce1a] - ldh [hTempPlayAreaLocation_ffa1], a - ld a, [wce1b] - ldh [hTempRetreatCostCards], a - ld a, [wce1c] - ldh [hTempRetreatCostCards + 1], a - ld a, [wce1d] - ldh [hTempRetreatCostCards + 2], a - ld a, $ff - ldh [hTempRetreatCostCards + 3], a - ld a, OPPACTION_EXECUTE_TRAINER_EFFECTS - bank1call AIMakeDecision - ret - -; picks two energy cards in the player's Play Area to remove -AIDecide_SuperEnergyRemoval: ; 209bc (8:49bc) - ld e, PLAY_AREA_BENCH_1 -.loop_1 -; first find an Arena card with a color energy card -; to discard for card effect -; return immediately if no Arena cards - ld a, DUELVARS_ARENA_CARD - add e - call GetTurnDuelistVariable - cp $ff - jr z, .exit - - ld d, a - push de - call .LookForNonDoubleColorless - pop de - jr c, .not_double_colorless - inc e - jr .loop_1 - -; returns carry if an energy card other than double colorless -; is found attached to the card in play area location e -.LookForNonDoubleColorless - ld a, e - call CreateArenaOrBenchEnergyCardList - ld hl, wDuelTempList -.loop_2 - ld a, [hli] - cp $ff - ret z - call LoadCardDataToBuffer1_FromDeckIndex - cp DOUBLE_COLORLESS_ENERGY - ; any basic energy card - ; will set carry flag here - jr nc, .loop_2 - ret - -.exit - or a - ret - -; card in Play Area location e was found with -; a basic energy card -.not_double_colorless - ld a, e - ld [wce0f], a - -; check if the current active card can KO player's card -; if it's possible to KO, then do not consider the player's -; active card to remove its attached energy - xor a ; PLAY_AREA_ARENA - ldh [hTempPlayAreaLocation_ff9d], a - farcall CheckIfAnyAttackKnocksOutDefendingCard - jr nc, .cannot_ko - farcall CheckIfSelectedAttackIsUnusable - jr nc, .can_ko - farcall LookForEnergyNeededForAttackInHand - jr nc, .cannot_ko - -.can_ko - ; start checking from the bench - call SwapTurn - ld e, PLAY_AREA_BENCH_1 - jr .loop_3 -.cannot_ko - ; start checking from the arena card - call SwapTurn - ld e, PLAY_AREA_ARENA - -; loop each card and check if it has enough energy to use any attack -; if it does, then proceed to pick energy cards to remove -.loop_3 - ld a, DUELVARS_ARENA_CARD - add e - call GetTurnDuelistVariable - cp $ff - jr z, .no_carry - - ld d, a - call .CheckIfFewerThanTwoEnergyCards - jr c, .next_1 - call .CheckIfNotEnoughEnergyToAttack - jr nc, .found_card ; jump if enough energy to attack -.next_1 - inc e - jr .loop_3 - -.found_card -; a play area card was picked to remove energy -; if this is not the Arena Card, then check -; entire bench to pick the highest damage - ld a, e - or a - jr nz, .check_bench_damage - -; store the picked energy card to remove in wce1a -; and set carry -.pick_energy - ld [wce1b], a - call PickTwoAttachedEnergyCards - ld [wce1c], a - ld a, b - ld [wce1d], a - call SwapTurn - ld a, [wce0f] - push af - call AIPickEnergyCardToDiscard - ld [wce1a], a - pop af - scf - ret - -; check what attack on player's Play Area is highest damaging -; and pick an energy card attached to that Pokemon to remove -.check_bench_damage - xor a - ld [wce06], a - ld [wce08], a - - ld e, PLAY_AREA_BENCH_1 -.loop_4 - ld a, DUELVARS_ARENA_CARD - add e - call GetTurnDuelistVariable - cp $ff - jr z, .found_damage - - ld d, a - call .CheckIfFewerThanTwoEnergyCards - jr c, .next_2 - call .CheckIfNotEnoughEnergyToAttack - jr c, .next_2 - call .FindHighestDamagingAttack -.next_2 - inc e - jr .loop_4 - -.found_damage - ld a, [wce08] - or a - jr z, .no_carry - jr .pick_energy -.no_carry - call SwapTurn - or a - ret - -; returns carry if the number of energy cards attached -; is fewer than 2, or if all energy combined yields -; fewer than 2 energy -.CheckIfFewerThanTwoEnergyCards ; 20a77 (8:4a77) - call GetPlayAreaCardAttachedEnergies - ld a, [wTotalAttachedEnergies] - cp 2 - ret c ; return if fewer than 2 attached cards - -; count all energy attached -; i.e. colored energy card = 1 -; and double colorless energy card = 2 - xor a - ld b, NUM_COLORED_TYPES - ld hl, wAttachedEnergies -.loop_5 - add [hl] - inc hl - dec b - jr nz, .loop_5 - ld b, [hl] - srl b - add b - cp 2 - ret - -; returns carry if this card does not -; have enough energy for either of its attacks -.CheckIfNotEnoughEnergyToAttack ; 20a92 (8:4a92) - push de - xor a ; FIRST_ATTACK_OR_PKMN_POWER - ld [wSelectedAttack], a - ld a, e - ldh [hTempPlayAreaLocation_ff9d], a - farcall CheckEnergyNeededForAttack - jr nc, .enough_energy - pop de - - push de - ld a, SECOND_ATTACK - ld [wSelectedAttack], a - ld a, e - ldh [hTempPlayAreaLocation_ff9d], a - farcall CheckEnergyNeededForAttack - jr nc, .check_surplus - pop de - -; neither attack has enough energy - scf - ret - -.enough_energy - pop de - or a - ret - -; first attack doesn't have enough energy (or is just a Pokemon Power) -; but second attack has enough energy to be used -; check if there's surplus energy for attack and, if so, -; return carry if this surplus energy is at least 2 -.check_surplus - farcall CheckIfNoSurplusEnergyForAttack - cp 2 - jr c, .enough_energy - pop de - scf - ret - -; stores in wce06 the highest damaging attack -; for the card in play area location in e -; and stores this card's location in wce08 -.FindHighestDamagingAttack ; 20ac1 (8:4ac1) - push de - ld a, e - ldh [hTempPlayAreaLocation_ff9d], a - - xor a ; FIRST_ATTACK_OR_PKMN_POWER - farcall EstimateDamage_VersusDefendingCard - ld a, [wDamage] - or a - jr z, .skip_1 - ld e, a - ld a, [wce06] - cp e - jr nc, .skip_1 - ld a, e - ld [wce06], a ; store this damage value - pop de - ld a, e - ld [wce08], a ; store this location - jr .second_attack - -.skip_1 - pop de - -.second_attack - push de - ld a, e - ldh [hTempPlayAreaLocation_ff9d], a - - ld a, SECOND_ATTACK - farcall EstimateDamage_VersusDefendingCard - ld a, [wDamage] - or a - jr z, .skip_2 - ld e, a - ld a, [wce06] - cp e - jr nc, .skip_2 - ld a, e - ld [wce06], a ; store this damage value - pop de - ld a, e - ld [wce08], a ; store this location - ret -.skip_2 - pop de - ret - -AIPlay_PokemonBreeder: ; 20b06 (8:4b06) - ld a, [wAITrainerCardToPlay] - ldh [hTempCardIndex_ff9f], a - ld a, [wAITrainerCardParameter] - ldh [hTempPlayAreaLocation_ffa1], a - ld a, [wce1a] - ldh [hTemp_ffa0], a - ld a, OPPACTION_EXECUTE_TRAINER_EFFECTS - bank1call AIMakeDecision - ret - -AIDecide_PokemonBreeder: ; 20b1b (8:4b1b) - call IsPrehistoricPowerActive - jp c, .done - - ld a, 7 - ld hl, wce08 - call ClearMemory_Bank8 - - xor a - ld [wce06], a - call CreateHandCardList - ld hl, wDuelTempList - -.loop_hand_1 - ld a, [hli] - cp $ff - jr z, .not_found_in_hand - -; check if card in hand is any of the following -; stage 2 Pokemon cards - ld d, a - call LoadCardDataToBuffer1_FromDeckIndex - cp VENUSAUR1 - jr z, .found - cp VENUSAUR2 - jr z, .found - cp BLASTOISE - jr z, .found - cp VILEPLUME - jr z, .found - cp ALAKAZAM - jr z, .found - cp GENGAR - jr nz, .loop_hand_1 - -.found - ld a, DUELVARS_NUMBER_OF_POKEMON_IN_PLAY_AREA - push hl - call GetTurnDuelistVariable - pop hl - ld c, a - ld e, PLAY_AREA_ARENA - -; check Play Area for card that can evolve into -; the picked stage 2 Pokemon -.loop_play_area_1 - push hl - push bc - push de - call CheckIfCanEvolveInto_BasicToStage2 - pop de - call nc, .can_evolve - pop bc - pop hl - inc e - dec c - jr nz, .loop_play_area_1 - jr .loop_hand_1 - -.can_evolve - ld a, DUELVARS_ARENA_CARD_HP - add e - call GetTurnDuelistVariable - call ConvertHPToCounters - swap a - ld b, a - -; count number of energy cards attached and keep -; the lowest 4 bits (capped at $0f) - call GetPlayAreaCardAttachedEnergies - ld a, [wTotalAttachedEnergies] - cp $10 - jr c, .not_maxed_out - ld a, %00001111 -.not_maxed_out - or b - -; 4 high bits of a = HP counters Pokemon still has -; 4 low bits of a = number of energy cards attached - -; store this score in wce08 + PLAY_AREA* - ld hl, wce08 - ld c, e - ld b, $00 - add hl, bc - ld [hl], a - -; store the deck index of stage 2 Pokemon in wce0f + PLAY_AREA* - ld hl, wce0f - add hl, bc - ld [hl], d - -; increase wce06 by one - ld hl, wce06 - inc [hl] - ret - -.not_found_in_hand - ld a, [wce06] - or a - jr z, .check_evolution_and_dragonite - -; an evolution has been found before - xor a - ld [wce06], a - ld a, DUELVARS_NUMBER_OF_POKEMON_IN_PLAY_AREA - call GetTurnDuelistVariable - ld c, a - ld e, $00 - ld d, $00 - -; find highest score in wce08 -.loop_score_1 - ld hl, wce08 - add hl, de - ld a, [wce06] - cp [hl] - jr nc, .not_higher - -; store this score to wce06 - ld a, [hl] - ld [wce06], a -; store this PLay Area location to wce07 - ld a, e - ld [wce07], a - -.not_higher - inc e - dec c - jr nz, .loop_score_1 - -; store the deck index of the stage 2 card -; that has been decided in wce1a, -; return the Play Area location of card -; to evolve in a and return carry - ld a, [wce07] - ld e, a - ld hl, wce0f - add hl, de - ld a, [hl] - ld [wce1a], a - ld a, [wce07] - scf - ret - -.check_evolution_and_dragonite - ld a, 7 - ld hl, wce08 - call ClearMemory_Bank8 - - xor a - ld [wce06], a - call CreateHandCardList - ld hl, wDuelTempList - push hl - -.loop_hand_2 - pop hl - ld a, [hli] - cp $ff - jr z, .check_evolution_found - - push hl - ld d, a - ld a, DUELVARS_NUMBER_OF_POKEMON_IN_PLAY_AREA - call GetTurnDuelistVariable - ld c, a - ld e, PLAY_AREA_ARENA - -.loop_play_area_2 -; check if evolution is possible - push bc - push de - call CheckIfCanEvolveInto_BasicToStage2 - pop de - call nc, .HandleDragonite1Evolution - call nc, .can_evolve - -; not possible to evolve or returned carry -; when handling Dragonite1 evolution - pop bc - inc e - dec c - jr nz, .loop_play_area_2 - jr .loop_hand_2 - -.check_evolution_found - ld a, [wce06] - or a - jr nz, .evolution_was_found -; no evolution was found before - or a - ret - -.evolution_was_found - xor a - ld [wce06], a - ld a, $ff - ld [wce07], a - - ld a, DUELVARS_NUMBER_OF_POKEMON_IN_PLAY_AREA - call GetTurnDuelistVariable - ld c, a - ld e, $00 - ld d, $00 - -; find highest score in wce08 with at least -; 2 energy cards attached -.loop_score_2 - ld hl, wce08 - add hl, de - ld a, [wce06] - cp [hl] - jr nc, .next_score - -; take the lower 4 bits (total energy cards) -; and skip if less than 2 - ld a, [hl] - ld b, a - and %00001111 - cp 2 - jr c, .next_score - -; has at least 2 energy cards -; store the score in wce06 - ld a, b - ld [wce06], a -; store this PLay Area location to wce07 - ld a, e - ld [wce07], a - -.next_score - inc e - dec c - jr nz, .loop_score_2 - - ld a, [wce07] - cp $ff - jr z, .done - -; a card to evolve was found -; store the deck index of the stage 2 card -; that has been decided in wce1a, -; return the Play Area location of card -; to evolve in a and return carry - ld e, a - ld hl, wce0f - add hl, de - ld a, [hl] - ld [wce1a], a - ld a, [wce07] - scf - ret - -.done - or a - ret - -; return carry if card is evolving to Dragonite1 and if -; - the card that is evolving is not Arena card and -; number of damage counters in Play Area is under 8; -; - the card that is evolving is Arena card and has under 5 -; damage counters or has less than 3 energy cards attached. -.HandleDragonite1Evolution ; 20c5c (8:4c5c) - push af - push bc - push de - push hl - push de - -; check card ID - ld a, d - call GetCardIDFromDeckIndex - ld a, e - pop de - cp DRAGONITE1 - jr nz, .no_carry - -; check card Play Area location - ld a, e - or a - jr z, .active_card_dragonite - -; the card that is evolving is not active card - ld a, DUELVARS_NUMBER_OF_POKEMON_IN_PLAY_AREA - call GetTurnDuelistVariable - ld b, a - ld c, 0 - -; count damage counters in Play Area -.loop_play_area_damage - dec b - ld e, b - push bc - call GetCardDamageAndMaxHP - pop bc - call ConvertHPToCounters - add c - ld c, a - - ld a, b - or a - jr nz, .loop_play_area_damage - -; compare number of total damage counters -; with 7, if less or equal to that, set carry - ld a, 7 - cp c - jr c, .no_carry - jr .set_carry - -.active_card_dragonite -; the card that is evolving is active card -; compare number of this card's damage counters -; with 5, if less than that, set carry - ld e, PLAY_AREA_ARENA - call GetCardDamageAndMaxHP - cp 5 - jr c, .set_carry - -; compare number of this card's attached energy cards -; with 3, if less than that, set carry - ld e, PLAY_AREA_ARENA - farcall CountNumberOfEnergyCardsAttached - cp 3 - jr c, .set_carry - jr .no_carry - -.no_carry - pop hl - pop de - pop bc - pop af - ret - -.set_carry - pop hl - pop de - pop bc - pop af - scf - ret - -AIPlay_ProfessorOak: ; 20cae (8:4cae) - ld a, [wCurrentAIFlags] - or AI_FLAG_USED_PROFESSOR_OAK | AI_FLAG_MODIFIED_HAND - ld [wCurrentAIFlags], a - ld a, [wAITrainerCardToPlay] - ldh [hTempCardIndex_ff9f], a - ld a, OPPACTION_EXECUTE_TRAINER_EFFECTS - bank1call AIMakeDecision - ret - -; sets carry if AI determines a score of playing -; Professor Oak is over a certain threshold. -AIDecide_ProfessorOak: ; 20cc1 (8:4cc1) -; return if cards in deck <= 6 - ld a, DUELVARS_NUMBER_OF_CARDS_NOT_IN_DECK - call GetTurnDuelistVariable - cp DECK_SIZE - 6 - ret nc - - ld a, [wOpponentDeckID] - cp LEGENDARY_ARTICUNO_DECK_ID - jp z, .HandleLegendaryArticunoDeck - cp EXCAVATION_DECK_ID - jp z, .HandleExcavationDeck - cp WONDERS_OF_SCIENCE_DECK_ID - jp z, .HandleWondersOfScienceDeck - -; return if cards in deck <= 14 -.check_cards_deck - ld a, [hl] - cp DECK_SIZE - 14 - ret nc - -; initialize score - ld a, $1e - ld [wce06], a - -; check number of cards in hand -.check_cards_hand - ld a, DUELVARS_NUMBER_OF_CARDS_IN_HAND - call GetTurnDuelistVariable - cp 4 - jr nc, .more_than_3_cards - -; less than 4 cards in hand - ld a, [wce06] - add $32 - ld [wce06], a - jr .check_energy_cards - -.more_than_3_cards - cp 9 - jr c, .check_energy_cards - -; more than 8 cards - ld a, [wce06] - sub $1e - ld [wce06], a - -.check_energy_cards - farcall CreateEnergyCardListFromHand - jr nc, .handle_blastoise - -; no energy cards in hand - ld a, [wce06] - add $28 - ld [wce06], a - -.handle_blastoise - ld a, MUK - call CountPokemonIDInBothPlayAreas - jr c, .check_hand - -; no Muk in Play Area - ld a, BLASTOISE - call CountPokemonIDInPlayArea - jr nc, .check_hand - -; at least one Blastoise in AI Play Area - ld a, WATER_ENERGY - farcall LookForCardIDInHand - jr nc, .check_hand - -; no Water energy in hand - ld a, [wce06] - add $0a - ld [wce06], a - -; this part seems buggy -; the AI loops through all the cards in hand and checks -; if any of them is not a Pokemon card and has Basic stage. -; it seems like the intention was that if there was -; any Basic Pokemon still in hand, the AI would add to the score. -.check_hand - call CreateHandCardList - ld hl, wDuelTempList -.loop_hand - ld a, [hli] - cp $ff - jr z, .check_evolution - - call LoadCardDataToBuffer1_FromDeckIndex - ld a, [wLoadedCard1Type] - cp TYPE_ENERGY - jr c, .loop_hand ; bug, should be jr nc - - ld a, [wLoadedCard1Stage] - or a - jr nz, .loop_hand - - ld a, [wce06] - add $0a - ld [wce06], a - -.check_evolution - xor a - ld [wce0f], a - ld [wce0f + 1], a - - ld a, DUELVARS_NUMBER_OF_POKEMON_IN_PLAY_AREA - call GetTurnDuelistVariable - ld d, a - ld e, PLAY_AREA_ARENA - -.loop_play_area - push de - call .LookForEvolution - pop de - jr nc, .not_in_hand - -; there's a card in hand that can evolve - ld a, $01 - ld [wce0f], a - -.not_in_hand -; check if a card that can evolve was found at all -; if not, go to the next card in the Play Area - ld a, [wce08] - cp $01 - jr nz, .next_play_area - -; if it was found, set wce0f + 1 to $01 - ld a, $01 - ld [wce0f + 1], a - -.next_play_area - inc e - dec d - jr nz, .loop_play_area - -; if a card was found that evolves... - ld a, [wce0f + 1] - or a - jr z, .check_score - -; ...but that card is not in the hand... - ld a, [wce0f] - or a - jr nz, .check_score - -; ...add to the score - ld a, [wce06] - add $0a - ld [wce06], a - -; only return carry if score > $3c -.check_score - ld a, [wce06] - ld b, $3c - cp b - jr nc, .set_carry - or a - ret - -.set_carry - scf - ret - -; return carry if there's a card in the hand that -; can evolve the card in Play Area location in e. -; sets wce08 to $01 if any card is found that can -; evolve regardless of card location. -.LookForEvolution ; 20d9d (8:4d9d) - xor a - ld [wce08], a - ld d, 0 - -; loop through the whole deck to check if there's -; a card that can evolve this Pokemon. -.loop_deck_evolution - push de - call CheckIfCanEvolveInto - pop de - jr nc, .can_evolve -.evolution_not_in_hand - inc d - ld a, DECK_SIZE - cp d - jr nz, .loop_deck_evolution - - or a - ret - -; a card was found that can evolve, set wce08 to $01 -; and if the card is in the hand, return carry. -; otherwise resume looping through deck. -.can_evolve - ld a, $01 - ld [wce08], a - ld a, DUELVARS_CARD_LOCATIONS - add d - call GetTurnDuelistVariable - cp CARD_LOCATION_HAND - jr nz, .evolution_not_in_hand - - scf - ret - -; handles Legendary Articuno Deck AI logic. -.HandleLegendaryArticunoDeck ; 20dc3 (8:4dc3) - ld a, DUELVARS_NUMBER_OF_POKEMON_IN_PLAY_AREA - call GetTurnDuelistVariable - cp 3 - jr nc, .check_playable_cards - -; has less than 3 Pokemon in Play Area. - push af - call CreateHandCardList - pop af - ld d, a - ld e, PLAY_AREA_ARENA - -; if no cards in hand evolve cards in Play Area, -; returns carry. -.loop_play_area_articuno - ld a, DUELVARS_ARENA_CARD - add e - - push de - call GetTurnDuelistVariable - farcall CheckForEvolutionInList - pop de - jr c, .check_playable_cards - - inc e - ld a, d - cp e - jr nz, .loop_play_area_articuno - -.set_carry_articuno - scf - ret - -; if there are more than 3 energy cards in hand, -; return no carry, otherwise check for playable cards. -.check_playable_cards - call CountOppEnergyCardsInHand - cp 4 - jr nc, .no_carry_articuno - -; remove both Professor Oak cards from list -; before checking for playable cards - call CreateHandCardList - ld hl, wDuelTempList - ld e, PROFESSOR_OAK - farcall RemoveCardIDInList - ld e, PROFESSOR_OAK - farcall RemoveCardIDInList - -; look in hand for cards that can be played. -; if a card that cannot be played is found, return no carry. -; otherwise return carry. -.loop_hand_articuno - ld a, [hli] - cp $ff - jr z, .set_carry_articuno - push hl - farcall CheckIfCardCanBePlayed - pop hl - jr c, .loop_hand_articuno - -.no_carry_articuno - or a - ret - -; handles Excavation deck AI logic. -; sets score depending on whether there's no -; Mysterious Fossil in play and in hand. -.HandleExcavationDeck ; 20e11 (8:4e11) -; return no carry if cards in deck < 15 - ld a, [hl] - cp 46 - ret nc - -; look for Mysterious Fossil - ld a, MYSTERIOUS_FOSSIL - call LookForCardIDInHandAndPlayArea - jr c, .found_mysterious_fossil - ld a, $50 - ld [wce06], a - jp .check_cards_hand -.found_mysterious_fossil - ld a, $1e - ld [wce06], a - jp .check_cards_hand - -; handles Wonders of Science AI logic. -; if there's either Grimer or Muk in hand, -; do not play Professor Oak. -.HandleWondersOfScienceDeck ; 20e2c (8:4e2c) - ld a, GRIMER - call LookForCardIDInHandList_Bank8 - jr c, .found_grimer_or_muk - ld a, MUK - call LookForCardIDInHandList_Bank8 - jr c, .found_grimer_or_muk - - ld a, DUELVARS_NUMBER_OF_CARDS_NOT_IN_DECK - call GetTurnDuelistVariable - jp .check_cards_deck - -.found_grimer_or_muk - or a - ret - -AIPlay_EnergyRetrieval: ; 20e44 (8:4e44) - ld a, [wCurrentAIFlags] - or AI_FLAG_MODIFIED_HAND - ld [wCurrentAIFlags], a - ld a, [wAITrainerCardToPlay] - ldh [hTempCardIndex_ff9f], a - ld a, [wAITrainerCardParameter] - ldh [hTemp_ffa0], a - ld a, [wce1a] - ldh [hTempPlayAreaLocation_ffa1], a - ld a, [wce1b] - ldh [hTempRetreatCostCards], a - cp $ff - jr z, .asm_20e68 - ld a, $ff - ldh [$ffa3], a -.asm_20e68 - ld a, OPPACTION_EXECUTE_TRAINER_EFFECTS - bank1call AIMakeDecision - ret - -; checks whether AI can play Energy Retrieval and -; picks the energy cards from the discard pile, -; and duplicate cards in hand to discard. -AIDecide_EnergyRetrieval: ; 20e6e (8:4e6e) -; return no carry if no cards in hand - farcall CreateEnergyCardListFromHand - jp nc, .no_carry - -; handle Go Go Rain Dance deck -; return no carry if there's no Muk card in play and -; if there's no Blastoise card in Play Area -; if there's a Muk in play, continue as normal - ld a, [wOpponentDeckID] - cp GO_GO_RAIN_DANCE_DECK_ID - jr nz, .start - ld a, MUK - call CountPokemonIDInBothPlayAreas - jr c, .start - ld a, BLASTOISE - call CountPokemonIDInPlayArea - jp nc, .no_carry - -.start -; find duplicate cards in hand - call CreateHandCardList - ld hl, wDuelTempList - call FindDuplicateCards - jp c, .no_carry - - ld [wce06], a - ld a, CARD_LOCATION_DISCARD_PILE - call FindBasicEnergyCardsInLocation - jp c, .no_carry - -; some basic energy cards were found in Discard Pile - ld a, $ff - ld [wce1a], a - ld [wce1b], a - ld [wce1c], a - - ld a, DUELVARS_NUMBER_OF_POKEMON_IN_PLAY_AREA - call GetTurnDuelistVariable - ld d, a - ld e, PLAY_AREA_ARENA - -; first check if there are useful energy cards in the list -; and choose them for retrieval first -.loop_play_area - ld a, DUELVARS_ARENA_CARD - add e - push de - -; load this card's ID in wTempCardID -; and this card's Type in wTempCardType - call GetTurnDuelistVariable - call GetCardIDFromDeckIndex - ld a, e - ld [wTempCardID], a - call LoadCardDataToBuffer1_FromCardID - pop de - ld a, [wLoadedCard1Type] - or TYPE_ENERGY - ld [wTempCardType], a - -; loop the energy cards in the Discard Pile -; and check if they are useful for this Pokemon - ld hl, wDuelTempList -.loop_energy_cards_1 - ld a, [hli] - cp $ff - jr z, .next_play_area - - ld b, a - push hl - farcall CheckIfEnergyIsUseful - pop hl - jr nc, .loop_energy_cards_1 - - ld a, [wce1a] - cp $ff - jr nz, .second_energy_1 - -; check if there were already chosen cards, -; if this is the second chosen card, return carry - -; first energy card found - ld a, b - ld [wce1a], a - call RemoveCardFromList - jr .next_play_area -.second_energy_1 - ld a, b - ld [wce1b], a - jr .set_carry - -.next_play_area - inc e - dec d - jr nz, .loop_play_area - -; next, if there are still energy cards left to choose, -; loop through the energy cards again and select -; them in order. - ld hl, wDuelTempList -.loop_energy_cards_2 - ld a, [hli] - cp $ff - jr z, .check_chosen - ld b, a - ld a, [wce1a] - cp $ff - jr nz, .second_energy_2 - ld a, b - ld [wce1a], a - call RemoveCardFromList - jr .loop_energy_cards_2 - -.second_energy_2 - ld a, b - ld [wce1b], a - jr .set_carry - -; will set carry if at least one has been chosen -.check_chosen - ld a, [wce1a] - cp $ff - jr nz, .set_carry -.no_carry - or a - ret - -.set_carry - ld a, [wce06] - scf - ret - -; remove an element from the list -; and shortens it accordingly -; input: -; hl = pointer to element after the one to remove -RemoveCardFromList: ; 20f27 (8:4f27) - push de - ld d, h - ld e, l - dec hl - push hl -.loop_remove - ld a, [de] - ld [hli], a - cp $ff - jr z, .done_remove - inc de - jr .loop_remove -.done_remove - pop hl - pop de - ret - -; finds duplicates in card list in hl. -; if a duplicate of Pokemon cards are found, return in -; a the deck index of the second one. -; otherwise, if a duplicate of non-Pokemon cards are found -; return in a the deck index of the second one. -; if no duplicates found, return carry. -; input: -; hl = list to look in -; output: -; a = deck index of duplicate card -FindDuplicateCards: ; 20f38 (8:4f38) - ld a, $ff - ld [wce0f], a - ld [wce0f + 1], a - push hl - -.loop_outer -; get ID of current card - pop hl - ld a, [hli] - cp $ff - jr z, .check_found - call GetCardIDFromDeckIndex - ld b, e - push hl - -; loop the rest of the list to find -; another card with the same ID -.loop_inner - ld a, [hli] - cp $ff - jr z, .loop_outer - ld c, a - call GetCardIDFromDeckIndex - ld a, e - cp b - jr nz, .loop_inner - -; found two cards with same ID - push bc - call GetCardType - pop bc - cp TYPE_ENERGY - jr c, .not_energy - -; they are energy or trainer cards -; loads wce0f+1 with this card deck index - ld a, c - ld [wce0f + 1], a - jr .loop_outer - -.not_energy -; they are Pokemon cards -; loads wce0f with this card deck index - ld a, c - ld [wce0f], a - jr .loop_outer - -.check_found - ld a, [wce0f] - cp $ff - jr nz, .no_carry - ld a, [wce0f + 1] - cp $ff - jr nz, .no_carry - -; only set carry if duplicate cards were not found - scf - ret - -.no_carry -; two cards with the same ID were found -; of either Pokemon or Non-Pokemon cards - or a - ret - -AIPlay_SuperEnergyRetrieval: ; 20f80 (8:4f80) - ld a, [wCurrentAIFlags] - or AI_FLAG_MODIFIED_HAND - ld [wCurrentAIFlags], a - ld a, [wAITrainerCardToPlay] - ldh [hTempCardIndex_ff9f], a - ld a, [wAITrainerCardParameter] - ldh [hTemp_ffa0], a - ld a, [wce1a] - ldh [hTempPlayAreaLocation_ffa1], a - ld a, [wce1b] - ldh [hTempRetreatCostCards], a - ld a, [wce1c] - ldh [$ffa3], a - cp $ff - jr z, .asm_20fbb - ld a, [wce1d] - ldh [$ffa4], a - cp $ff - jr z, .asm_20fbb - ld a, [wce1e] - ldh [$ffa5], a - cp $ff - jr z, .asm_20fbb - ld a, $ff - ldh [$ffa6], a -.asm_20fbb - ld a, OPPACTION_EXECUTE_TRAINER_EFFECTS - bank1call AIMakeDecision - ret - -AIDecide_SuperEnergyRetrieval: ; 20fc1 (8:4fc1) -; return no carry if no cards in hand - farcall CreateEnergyCardListFromHand - jp nc, .no_carry - -; handle Go Go Rain Dance deck -; return no carry if there's no Muk card in play and -; if there's no Blastoise card in Play Area -; if there's a Muk in play, continue as normal - ld a, [wOpponentDeckID] - cp GO_GO_RAIN_DANCE_DECK_ID - jr nz, .start - ld a, MUK - call CountPokemonIDInBothPlayAreas - jr c, .start - ld a, BLASTOISE - call CountPokemonIDInPlayArea - jp nc, .no_carry - -.start -; find duplicate cards in hand - call CreateHandCardList - ld hl, wDuelTempList - call FindDuplicateCards - jp c, .no_carry - -; remove the duplicate card in hand -; and run the hand check again - ld [wce06], a - ld hl, wDuelTempList - call FindAndRemoveCardFromList - call FindDuplicateCards - jp c, .no_carry - - ld [wce08], a - ld a, CARD_LOCATION_DISCARD_PILE - call FindBasicEnergyCardsInLocation - jp c, .no_carry - -; some basic energy cards were found in Discard Pile - ld a, $ff - ld [wce1b], a - ld [wce1c], a - ld [wce1d], a - ld [wce1e], a - ld [wce1f], a - - ld a, DUELVARS_NUMBER_OF_POKEMON_IN_PLAY_AREA - call GetTurnDuelistVariable - ld d, a - ld e, PLAY_AREA_ARENA - -; first check if there are useful energy cards in the list -; and choose them for retrieval first -.loop_play_area - ld a, DUELVARS_ARENA_CARD - add e - push de - -; load this card's ID in wTempCardID -; and this card's Type in wTempCardType - call GetTurnDuelistVariable - call GetCardIDFromDeckIndex - ld a, e - ld [wTempCardID], a - call LoadCardDataToBuffer1_FromCardID - pop de - ld a, [wLoadedCard1Type] - or TYPE_ENERGY - ld [wTempCardType], a - -; loop the energy cards in the Discard Pile -; and check if they are useful for this Pokemon - ld hl, wDuelTempList -.loop_energy_cards_1 - ld a, [hli] - cp $ff - jr z, .next_play_area - - ld b, a - push hl - farcall CheckIfEnergyIsUseful - pop hl - jr nc, .loop_energy_cards_1 - -; first energy - ld a, [wce1b] - cp $ff - jr nz, .second_energy_1 - ld a, b - ld [wce1b], a - call RemoveCardFromList - jr .next_play_area - -.second_energy_1 - ld a, [wce1c] - cp $ff - jr nz, .third_energy_1 - ld a, b - ld [wce1c], a - call RemoveCardFromList - jr .next_play_area - -.third_energy_1 - ld a, [wce1d] - cp $ff - jr nz, .fourth_energy_1 - ld a, b - ld [wce1d], a - call RemoveCardFromList - jr .next_play_area - -.fourth_energy_1 - ld a, b - ld [wce1e], a - jr .set_carry - -.next_play_area - inc e - dec d - jr nz, .loop_play_area - -; next, if there are still energy cards left to choose, -; loop through the energy cards again and select -; them in order. - ld hl, wDuelTempList -.loop_energy_cards_2 - ld a, [hli] - cp $ff - jr z, .check_chosen - ld b, a - ld a, [wce1b] - cp $ff - jr nz, .second_energy_2 - ld a, b - -; first energy - ld [wce1b], a - call RemoveCardFromList - jr .loop_energy_cards_2 - -.second_energy_2 - ld a, [wce1c] - cp $ff - jr nz, .third_energy_2 - ld a, b - ld [wce1c], a - call RemoveCardFromList - jr .loop_energy_cards_2 - -.third_energy_2 - ld a, [wce1d] - cp $ff - jr nz, .fourth_energy - ld a, b - ld [wce1d], a - call RemoveCardFromList - jr .loop_energy_cards_2 - -.fourth_energy - ld a, b - ld [wce1e], a - jr .set_carry - -; will set carry if at least one has been chosen -.check_chosen - ld a, [wce1b] - cp $ff - jr nz, .set_carry - -.no_carry - or a - ret -.set_carry - ld a, [wce08] - ld [wce1a], a - ld a, [wce06] - scf - ret - -; finds the card with deck index a in list hl, -; and removes it from the list. -; the card HAS to exist in the list, since this -; routine does not check for the terminating byte $ff! -; input: -; a = card deck index to look -; hl = pointer to list of cards -FindAndRemoveCardFromList: ; 210d5 (8:50d5) - push hl - ld b, a -.loop_duplicate - ld a, [hli] - cp b - jr nz, .loop_duplicate - call RemoveCardFromList - pop hl - ret - -AIPlay_PokemonCenter: ; 210e0 (8:50e0) - ld a, [wAITrainerCardToPlay] - ldh [hTempCardIndex_ff9f], a - ld a, OPPACTION_EXECUTE_TRAINER_EFFECTS - bank1call AIMakeDecision - ret - -AIDecide_PokemonCenter: ; 210eb (8:50eb) - xor a - ldh [hTempPlayAreaLocation_ff9d], a - -; return if active Pokemon can KO player's card. - farcall CheckIfAnyAttackKnocksOutDefendingCard - jr nc, .start - farcall CheckIfSelectedAttackIsUnusable - jr nc, .no_carry - farcall LookForEnergyNeededForAttackInHand - jr c, .no_carry - -.start - xor a - ld [wce06], a - ld [wce08], a - ld [wce0f], a - - ld a, DUELVARS_NUMBER_OF_POKEMON_IN_PLAY_AREA - call GetTurnDuelistVariable - ld d, a - ld e, PLAY_AREA_ARENA - -.loop_play_area - ld a, DUELVARS_ARENA_CARD - add e - push de - call GetTurnDuelistVariable - call LoadCardDataToBuffer1_FromDeckIndex - ld a, e ; useless instruction - pop de - -; get this Pokemon's current HP in number of counters -; and add it to the total. - ld a, [wLoadedCard1HP] - call ConvertHPToCounters - ld b, a - ld a, [wce06] - add b - ld [wce06], a - -; get this Pokemon's current damage counters -; and add it to the total. - call GetCardDamageAndMaxHP - call ConvertHPToCounters - ld b, a - ld a, [wce08] - add b - ld [wce08], a - -; get this Pokemon's number of attached energy cards -; and add it to the total. -; if there's overflow, return no carry. - call GetPlayAreaCardAttachedEnergies - ld a, [wTotalAttachedEnergies] - ld b, a - ld a, [wce0f] - add b - jr c, .no_carry - ld [wce0f], a - - inc e - dec d - jr nz, .loop_play_area - -; if (number of damage counters / 2) < (total energy cards attached) -; return no carry. - ld a, [wce08] - srl a - ld hl, wce0f - cp [hl] - jr c, .no_carry - -; if (number of HP counters * 6 / 10) >= (number of damage counters) -; return no carry. - ld a, [wce06] - ld l, a - ld h, 6 - call HtimesL - call CalculateWordTensDigit - ld a, l - ld hl, wce08 - cp [hl] - jr nc, .no_carry - - scf - ret - -.no_carry - or a - ret - -AIPlay_ImposterProfessorOak: ; 21170 (8:5170) - ld a, [wAITrainerCardToPlay] - ldh [hTempCardIndex_ff9f], a - ld a, OPPACTION_EXECUTE_TRAINER_EFFECTS - bank1call AIMakeDecision - ret - -; sets carry depending on player's number of cards -; in deck in in hand. -AIDecide_ImposterProfessorOak: ; 2117b (8:517b) - ld a, DUELVARS_NUMBER_OF_CARDS_NOT_IN_DECK - call GetNonTurnDuelistVariable - cp DECK_SIZE - 14 - jr c, .more_than_14_cards - -; if player has less than 14 cards in deck, only -; set carry if number of cards in their hands < 6 - ld a, DUELVARS_NUMBER_OF_CARDS_IN_HAND - call GetNonTurnDuelistVariable - cp 6 - jr c, .set_carry -.no_carry - or a - ret - -; if player has more than 14 cards in deck, only -; set carry if number of cards in their hands >= 9 -.more_than_14_cards - ld a, DUELVARS_NUMBER_OF_CARDS_IN_HAND - call GetNonTurnDuelistVariable - cp 9 - jr c, .no_carry -.set_carry - scf - ret - -AIPlay_EnergySearch: ; 2119a (8:519a) - ld a, [wAITrainerCardToPlay] - ldh [hTempCardIndex_ff9f], a - ld a, [wAITrainerCardParameter] - ldh [hTemp_ffa0], a - ld a, OPPACTION_EXECUTE_TRAINER_EFFECTS - bank1call AIMakeDecision - ret - -; AI checks for playing Energy Search -AIDecide_EnergySearch: ; 211aa (8:51aa) - farcall CreateEnergyCardListFromHand - jr c, .start - call .CheckForUsefulEnergyCards - jr c, .start - -; there are energy cards in hand and at least -; one of them is useful to a card in Play Area -.no_carry - or a - ret - -.start - ld a, [wOpponentDeckID] - cp HEATED_BATTLE_DECK_ID - jr z, .heated_battle - cp WONDERS_OF_SCIENCE_DECK_ID - jr z, .wonders_of_science - -; if no energy cards in deck, return no carry - ld a, CARD_LOCATION_DECK - call FindBasicEnergyCardsInLocation - jr c, .no_carry - -; if any of the energy cards in deck is useful -; return carry right away... - call .CheckForUsefulEnergyCards - jr c, .no_useful - scf - ret - -; ...otherwise save the list in a before return carry. -.no_useful - ld a, [wDuelTempList] - scf - ret - -; Heated Battle deck only searches for Fire and Lightning -; if they are found to be useful to some card in Play Area -.heated_battle - ld a, CARD_LOCATION_DECK - call FindBasicEnergyCardsInLocation - jr c, .no_carry - call .CheckUsefulFireOrLightningEnergy - jr c, .no_carry - scf - ret - -; this subroutine has a bug. -; it was supposed to use the .CheckUsefulGrassEnergy subroutine -; but uses .CheckUsefulFireOrLightningEnergy instead. -.wonders_of_science - ld a, CARD_LOCATION_DECK - call FindBasicEnergyCardsInLocation - jr c, .no_carry - call .CheckUsefulFireOrLightningEnergy - jr c, .no_carry - scf - ret - -; return carry if cards in wDuelTempList are not -; useful to any of the Play Area Pokemon -.CheckForUsefulEnergyCards ; 211f1 (8:51f1) - ld a, DUELVARS_NUMBER_OF_POKEMON_IN_PLAY_AREA - call GetTurnDuelistVariable - ld d, a - ld e, PLAY_AREA_ARENA - -.loop_play_area_1 - ld a, DUELVARS_ARENA_CARD - add e - push de - call GetTurnDuelistVariable - -; store ID and type of card - call GetCardIDFromDeckIndex - ld a, e - ld [wTempCardID], a - call LoadCardDataToBuffer1_FromCardID - pop de - ld a, [wLoadedCard1Type] - or TYPE_ENERGY - ld [wTempCardType], a - -; look in list for a useful energy, -; is any is found return no carry. - ld hl, wDuelTempList -.loop_energy_1 - ld a, [hli] - cp $ff - jr z, .none_found - ld b, a - push hl - farcall CheckIfEnergyIsUseful - pop hl - jr nc, .loop_energy_1 - - ld a, b - or a - ret - -.none_found - inc e - ld a, e - cp d - jr nz, .loop_play_area_1 - - scf - ret - -; checks whether there are useful energies -; only for Fire and Lightning type Pokemon cards -; in Play Area. If none found, return carry. -.CheckUsefulFireOrLightningEnergy ; 2122e (8:522e) - ld a, DUELVARS_NUMBER_OF_POKEMON_IN_PLAY_AREA - call GetTurnDuelistVariable - ld d, a - ld e, PLAY_AREA_ARENA - -.loop_play_area_2 - ld a, DUELVARS_ARENA_CARD - add e - push de - call GetTurnDuelistVariable - -; get card's ID and Type - call GetCardIDFromDeckIndex - ld a, e - ld [wTempCardID], a - call LoadCardDataToBuffer1_FromCardID - pop de - ld a, [wLoadedCard1Type] - or TYPE_ENERGY - -; only do check if the Pokemon's type -; is either Fire or Lightning - cp TYPE_ENERGY_FIRE - jr z, .fire_or_lightning - cp TYPE_ENERGY_LIGHTNING - jr nz, .next_play_area - -; loop each energy card in list -.fire_or_lightning - ld [wTempCardType], a - ld hl, wDuelTempList -.loop_energy_2 - ld a, [hli] - cp $ff - jr z, .next_play_area - -; if this energy card is useful, -; return no carry. - ld b, a - push hl - farcall CheckIfEnergyIsUseful - pop hl - jr nc, .loop_energy_2 - - ld a, b - or a - ret - -.next_play_area - inc e - ld a, e - cp d - jr nz, .loop_play_area_2 - -; no card was found to be useful -; for Fire/Lightning type Pokemon card. - scf - ret - -; checks whether there are useful energies -; only for Grass type Pokemon cards -; in Play Area. If none found, return carry. -.CheckUsefulGrassEnergy ; 21273 (8:5273) -; unreferenced - ld a, DUELVARS_NUMBER_OF_POKEMON_IN_PLAY_AREA - call GetTurnDuelistVariable - ld d, a - ld e, PLAY_AREA_ARENA - -.loop_play_area_3 - ld a, DUELVARS_ARENA_CARD - add e - push de - call GetTurnDuelistVariable - -; get card's ID and Type - call GetCardIDFromDeckIndex - ld a, e - ld [wTempCardID], a - call LoadCardDataToBuffer1_FromCardID - pop de - ld a, [wLoadedCard1Type] - or TYPE_ENERGY - -; only do check if the Pokemon's type is Grass - cp TYPE_ENERGY_GRASS - jr nz, .next_play_area_3 - -; loop each energy card in list - ld [wTempCardType], a - ld hl, wDuelTempList -.loop_energy_3 - ld a, [hli] - cp $ff - jr z, .next_play_area_3 - -; if this energy card is useful, -; return no carry. - ld b, a - push hl - farcall CheckIfEnergyIsUseful - pop hl - jr nc, .loop_energy_3 - - ld a, b - or a - ret - -.next_play_area_3 - inc e - ld a, e - cp d - jr nz, .loop_play_area_3 - -; no card was found to be useful -; for Grass type Pokemon card. - scf - ret - -AIPlay_Pokedex: ; 212b4 (8:52b4) - ld a, [wAITrainerCardToPlay] - ldh [hTempCardIndex_ff9f], a - ld a, [wce1a] - ldh [hTemp_ffa0], a - ld a, [wce1b] - ldh [hTempPlayAreaLocation_ffa1], a - ld a, [wce1c] - ldh [hTempRetreatCostCards], a - ld a, [wce1d] - ldh [$ffa3], a - ld a, [wce1e] - ldh [$ffa4], a - ld a, $ff - ldh [$ffa5], a - ld a, OPPACTION_EXECUTE_TRAINER_EFFECTS - bank1call AIMakeDecision - ret - -AIDecide_Pokedex: ; 212dc (8:52dc) - ld a, [wAIPokedexCounter] - cp 5 + 1 - jr c, .no_carry ; return if counter hasn't reached 6 yet - -; return no carry if number of cards in deck <= 4 - ld a, DUELVARS_NUMBER_OF_CARDS_NOT_IN_DECK - call GetTurnDuelistVariable - cp DECK_SIZE - 4 - jr nc, .no_carry - -; has a 3 in 10 chance of actually playing card - ld a, 10 - call Random - cp 3 - jr c, .pick_cards - -.no_carry - or a - ret - -.pick_cards -; the following comparison is disregarded -; the Wonders of Science deck was probably intended -; to use PickPokedexCards_Unreferenced instead - ld a, [wOpponentDeckID] - cp WONDERS_OF_SCIENCE_DECK_ID - jp PickPokedexCards ; bug, should be jp nz - -; picks order of the cards in deck from the effects of Pokedex. -; prioritizes Pokemon cards, then Trainer cards, then energy cards. -; stores the resulting order in wce1a. -PickPokedexCards_Unreferenced: ; 212ff (8:52ff) -; unreferenced - xor a - ld [wAIPokedexCounter], a ; reset counter - - ld a, DUELVARS_NUMBER_OF_CARDS_NOT_IN_DECK - call GetTurnDuelistVariable - add DUELVARS_DECK_CARDS - ld l, a - lb de, $00, $00 - ld b, 5 - -; run through 5 of the remaining cards in deck -.next_card - ld a, [hli] - ld c, a - call .GetCardType - -; load this card's deck index and type in memory -; wce08 = card types -; wce0f = card deck indices - push hl - ld hl, wce08 - add hl, de - ld [hl], a - ld hl, wce0f - add hl, de - ld [hl], c - pop hl - - inc e - dec b - jr nz, .next_card - -; terminate the wce08 list - ld a, $ff - ld [wce08 + 5], a - - ld de, wce1a - -; find Pokemon - ld hl, wce08 - ld c, -1 - ld b, $00 - -; run through the stored cards -; and look for any Pokemon cards. -.loop_pokemon - inc c - ld a, [hli] - cp $ff - jr z, .find_trainers - cp TYPE_ENERGY - jr nc, .loop_pokemon -; found a Pokemon card -; store it in wce1a list - push hl - ld hl, wce0f - add hl, bc - ld a, [hl] - pop hl - ld [de], a - inc de - jr .loop_pokemon - -; run through the stored cards -; and look for any Trainer cards. -.find_trainers - ld hl, wce08 - ld c, -1 - ld b, $00 - -.loop_trainers - inc c - ld a, [hli] - cp $ff - jr z, .find_energy - cp TYPE_TRAINER - jr nz, .loop_trainers -; found a Trainer card -; store it in wce1a list - push hl - ld hl, wce0f - add hl, bc - ld a, [hl] - pop hl - ld [de], a - inc de - jr .loop_trainers - -.find_energy - ld hl, wce08 - ld c, -1 - ld b, $00 - -; run through the stored cards -; and look for any energy cards. -.loop_energy - inc c - ld a, [hli] - cp $ff - jr z, .done - and TYPE_ENERGY - jr z, .loop_energy -; found an energy card -; store it in wce1a list - push hl - ld hl, wce0f - add hl, bc - ld a, [hl] - pop hl - ld [de], a - inc de - jr .loop_energy - -.done - scf - ret - -.GetCardType ; 21383 (8:5383) - push bc - push de - call GetCardIDFromDeckIndex - call GetCardType - pop de - pop bc - ret - -; picks order of the cards in deck from the effects of Pokedex. -; prioritizes energy cards, then Pokemon cards, then Trainer cards. -; stores the resulting order in wce1a. -PickPokedexCards: ; 2138e (8:538e) - xor a - ld [wAIPokedexCounter], a ; reset counter ; reset counter - - ld a, DUELVARS_NUMBER_OF_CARDS_NOT_IN_DECK - call GetTurnDuelistVariable - add DUELVARS_DECK_CARDS - ld l, a - lb de, $00, $00 - ld b, 5 - -; run through 5 of the remaining cards in deck -.next_card - ld a, [hli] - ld c, a - call .GetCardType - -; load this card's deck index and type in memory -; wce08 = card types -; wce0f = card deck indices - push hl - ld hl, wce08 - add hl, de - ld [hl], a - ld hl, wce0f - add hl, de - ld [hl], c - pop hl - - inc e - dec b - jr nz, .next_card - -; terminate the wce08 list - ld a, $ff - ld [wce08 + 5], a - - ld de, wce1a - -; find energy - ld hl, wce08 - ld c, -1 - ld b, $00 - -; run through the stored cards -; and look for any energy cards. -.loop_energy - inc c - ld a, [hli] - cp $ff - jr z, .find_pokemon - and TYPE_ENERGY - jr z, .loop_energy -; found an energy card -; store it in wce1a list - push hl - ld hl, wce0f - add hl, bc - ld a, [hl] - pop hl - ld [de], a - inc de - jr .loop_energy - -.find_pokemon - ld hl, wce08 - ld c, -1 - ld b, $00 - -; run through the stored cards -; and look for any Pokemon cards. -.loop_pokemon - inc c - ld a, [hli] - cp $ff - jr z, .find_trainers - cp TYPE_ENERGY - jr nc, .loop_pokemon -; found a Pokemon card -; store it in wce1a list - push hl - ld hl, wce0f - add hl, bc - ld a, [hl] - pop hl - ld [de], a - inc de - jr .loop_pokemon - -; run through the stored cards -; and look for any Trainer cards. -.find_trainers - ld hl, wce08 - ld c, -1 - ld b, $00 - -.loop_trainers - inc c - ld a, [hli] - cp $ff - jr z, .done - cp TYPE_TRAINER - jr nz, .loop_trainers -; found a Trainer card -; store it in wce1a list - push hl - ld hl, wce0f - add hl, bc - ld a, [hl] - pop hl - ld [de], a - inc de - jr .loop_trainers - -.done - scf - ret - -.GetCardType ; 21412 (8:5412) - push bc - push de - call GetCardIDFromDeckIndex - call GetCardType - pop de - pop bc - ret - -AIPlay_FullHeal: ; 2141d (8:541d) - ld a, [wAITrainerCardToPlay] - ldh [hTempCardIndex_ff9f], a - ld a, OPPACTION_EXECUTE_TRAINER_EFFECTS - bank1call AIMakeDecision - ret - -AIDecide_FullHeal: ; 21428 (8:5428) - ld a, DUELVARS_ARENA_CARD_STATUS - call GetTurnDuelistVariable - -; skip if no status on arena card - or a ; NO_STATUS - jr z, .no_carry - - and CNF_SLP_PRZ - cp PARALYZED - jr z, .paralyzed - cp ASLEEP - jr z, .asleep - cp CONFUSED - jr z, .confused - ; if either PSN or DBLPSN, fallthrough - -.set_carry - scf - ret - -.asleep -; set carry if any of the following -; cards are in the Play Area. - ld a, GASTLY1 - ld b, PLAY_AREA_ARENA - call LookForCardIDInPlayArea_Bank8 - jr c, .set_carry - ld a, GASTLY2 - ld b, PLAY_AREA_ARENA - call LookForCardIDInPlayArea_Bank8 - jr c, .set_carry - ld a, HAUNTER2 - ld b, PLAY_AREA_ARENA - call LookForCardIDInPlayArea_Bank8 - jr c, .set_carry - -; otherwise fallthrough - -.paralyzed -; if Scoop Up is in hand and decided to be played, skip. - ld a, SCOOP_UP - call LookForCardIDInHandList_Bank8 - jr nc, .no_scoop_up_prz - call AIDecide_ScoopUp - jr c, .no_carry - -.no_scoop_up_prz -; if card can damage defending Pokemon... - xor a ; PLAY_AREA_ARENA - farcall CheckIfCanDamageDefendingPokemon - jr nc, .no_carry -; ...and can play an energy card to retreat, set carry. - ld a, [wAIPlayEnergyCardForRetreat] - or a - jr nz, .set_carry - -; if not, check whether it's a card it would rather retreat, -; and if it isn't, set carry. - farcall AIDecideWhetherToRetreat - jr nc, .set_carry - -.no_carry - or a - ret - -.confused -; if Scoop Up is in hand and decided to be played, skip. - ld a, SCOOP_UP - call LookForCardIDInHandList_Bank8 - jr nc, .no_scoop_up_cnf - call AIDecide_ScoopUp - jr c, .no_carry - -.no_scoop_up_cnf -; if card can damage defending Pokemon... - xor a ; PLAY_AREA_ARENA - farcall CheckIfCanDamageDefendingPokemon - jr nc, .no_carry -; ...and can play an energy card to retreat, set carry. - ld a, [wAIPlayEnergyCardForRetreat] - or a - jr nz, .set_carry -; if not, return no carry. - jr .no_carry - -AIPlay_MrFuji: ; 21497 (8:5497) - ld a, [wAITrainerCardToPlay] - ldh [hTempCardIndex_ff9f], a - ld a, [wAITrainerCardParameter] - ldh [hTemp_ffa0], a - ld a, OPPACTION_EXECUTE_TRAINER_EFFECTS - bank1call AIMakeDecision - ret - -; AI logic for playing Mr Fuji -AIDecide_MrFuji: ; 214a7 (8:54a7) - ld a, $ff - ld [wce06], a - ld [wce08], a - -; if just one Pokemon in Play Area, skip. - ld a, DUELVARS_NUMBER_OF_POKEMON_IN_PLAY_AREA - call GetTurnDuelistVariable - cp 1 - ret z - - dec a - ld d, a - ld e, PLAY_AREA_BENCH_1 - -; find a Pokemon in the bench that has damage counters. -.loop_bench - ld a, DUELVARS_ARENA_CARD - add e - push de - call GetTurnDuelistVariable - call LoadCardDataToBuffer1_FromDeckIndex - pop de - - ld a, [wLoadedCard1HP] - ld b, a - - ; skip if zero damage counters - call GetCardDamageAndMaxHP - call ConvertHPToCounters - or a - jr z, .next - -; a = damage counters -; b = hp left - call CalculateBDividedByA_Bank8 - cp 20 - jr nc, .next - -; here, HP left in counters is less than twice -; the number of damage counters, that is: -; HP < 1/3 max HP - -; if value is less than the one found before, store this one. - ld hl, wce08 - cp [hl] - jr nc, .next - ld [hl], a - ld a, e - ld [wce06], a -.next - inc e - dec d - jr nz, .loop_bench - - ld a, [wce06] - cp $ff - ret z - - scf - ret - -AIPlay_ScoopUp: ; 214f1 (8:54f1) - ld a, [wAITrainerCardToPlay] - ldh [hTempCardIndex_ff9f], a - ld a, [wAITrainerCardParameter] - ldh [hTemp_ffa0], a - ld a, [wce1a] - ldh [hTempPlayAreaLocation_ffa1], a - ld a, OPPACTION_EXECUTE_TRAINER_EFFECTS - bank1call AIMakeDecision - ret - -AIDecide_ScoopUp: ; 21506 (8:5506) - xor a - ldh [hTempPlayAreaLocation_ff9d], a - -; if only one Pokemon in Play Area, skip. - ld a, DUELVARS_NUMBER_OF_POKEMON_IN_PLAY_AREA - call GetTurnDuelistVariable - cp 2 - jr c, .no_carry - -; handle some decks differently - ld a, [wOpponentDeckID] - cp LEGENDARY_ARTICUNO_DECK_ID - jr z, .HandleLegendaryArticuno - cp LEGENDARY_RONALD_DECK_ID - jp z, .HandleLegendaryRonald - -; if can't KO defending Pokemon, check if defending Pokemon -; can KO this card. If so, then continue. -; If not, return no carry. - -; if it can KO the defending Pokemon this turn, -; return no carry. - farcall CheckIfAnyAttackKnocksOutDefendingCard - jr nc, .cannot_ko - farcall CheckIfSelectedAttackIsUnusable - jr nc, .no_carry - farcall LookForEnergyNeededForAttackInHand - jr c, .no_carry - -.cannot_ko - ld a, DUELVARS_ARENA_CARD_STATUS - call GetTurnDuelistVariable - and CNF_SLP_PRZ - cp PARALYZED - jr z, .cannot_retreat - cp ASLEEP - jr z, .cannot_retreat - -; doesn't have a status that prevents retreat. -; so check if it has enough energy to retreat. -; if not, return no carry. - xor a - ldh [hTempPlayAreaLocation_ff9d], a - call GetPlayAreaCardRetreatCost - ld b, a - ld e, PLAY_AREA_ARENA - farcall CountNumberOfEnergyCardsAttached - cp b - jr c, .cannot_retreat - -.no_carry - or a - ret - -.cannot_retreat -; store damage and total HP left - ld a, DUELVARS_ARENA_CARD - call GetTurnDuelistVariable - call LoadCardDataToBuffer1_FromDeckIndex - ld a, [wLoadedCard1HP] - call ConvertHPToCounters - ld d, a - -; skip if card has no damage counters. - ld e, PLAY_AREA_ARENA - call GetCardDamageAndMaxHP - or a - jr z, .no_carry - -; if (total damage / total HP counters) < 7 -; return carry. -; (this corresponds to damage counters -; being under 70% of the max HP) - ld b, a - ld a, d - call CalculateBDividedByA_Bank8 - cp 7 - jr c, .no_carry - -; store Pokemon to switch to in wce1a and set carry. -.decide_switch - farcall AIDecideBenchPokemonToSwitchTo - jr c, .no_carry - ld [wce1a], a - xor a - scf - ret - -; this deck will use Scoop Up on a benched Articuno2. -; it checks if the defending Pokemon is a Snorlax, -; but interestingly does not check for Muk in both Play Areas. -; will also use Scoop Up on -.HandleLegendaryArticuno -; if less than 3 Play Area Pokemon cards, skip. - ld a, DUELVARS_NUMBER_OF_POKEMON_IN_PLAY_AREA - call GetTurnDuelistVariable - cp 3 - jr c, .no_carry - -; look for Articuno2 in bench - ld a, ARTICUNO2 - ld b, PLAY_AREA_BENCH_1 - call LookForCardIDInPlayArea_Bank8 - jr c, .articuno_bench - -; check Arena card - ld a, DUELVARS_ARENA_CARD - call GetTurnDuelistVariable - call GetCardIDFromDeckIndex - ld a, e - cp ARTICUNO2 - jr z, .articuno_or_chansey - cp CHANSEY - jr nz, .no_carry - -; here either Articuno2 or Chansey -; is the Arena Card. -.articuno_or_chansey -; if can't KO defending Pokemon, check if defending Pokemon -; can KO this card. If so, then continue. -; If not, return no carry. - -; if it can KO the defending Pokemon this turn, -; return no carry. - farcall CheckIfAnyAttackKnocksOutDefendingCard - jr nc, .check_ko - farcall CheckIfSelectedAttackIsUnusable - jr nc, .no_carry - farcall LookForEnergyNeededForAttackInHand - jr c, .no_carry -.check_ko - farcall CheckIfDefendingPokemonCanKnockOut - jr nc, .no_carry - jr .decide_switch - -.articuno_bench -; skip if the defending card is Snorlax - push af - ld a, DUELVARS_ARENA_CARD - call GetNonTurnDuelistVariable - call SwapTurn - call GetCardIDFromDeckIndex - call SwapTurn - ld a, e - cp SNORLAX - pop bc - jr z, .no_carry - -; check attached energy cards. -; if it has any, return no carry. - ld a, b -.check_attached_energy - ld e, a - push af - farcall CountNumberOfEnergyCardsAttached - or a - pop bc - ld a, b - jr z, .no_energy - jp .no_carry - -.no_energy -; has decided to Scoop Up benched card, -; store $ff as the Pokemon card to switch to -; because there's no need to switch. - push af - ld a, $ff - ld [wce1a], a - pop af - scf - ret - -; this deck will use Scoop Up on a benched Articuno2, Zapdos3 or Moltres2. -; interestingly, does not check for Muk in both Play Areas. -.HandleLegendaryRonald ; 215e7 (8:55e7) -; if less than 3 Play Area Pokemon cards, skip. - ld a, DUELVARS_NUMBER_OF_POKEMON_IN_PLAY_AREA - call GetTurnDuelistVariable - cp 3 - jp c, .no_carry - - ld a, ARTICUNO2 - ld b, PLAY_AREA_BENCH_1 - call LookForCardIDInPlayArea_Bank8 - jr c, .articuno_bench - ld a, ZAPDOS3 - ld b, PLAY_AREA_BENCH_1 - call LookForCardIDInPlayArea_Bank8 - jr c, .check_attached_energy - ld a, MOLTRES2 - ld b, PLAY_AREA_BENCH_1 - call LookForCardIDInPlayArea_Bank8 - jr c, .check_attached_energy - jp .no_carry - -AIPlay_Maintenance: ; 2160f (8:560f) - ld a, [wCurrentAIFlags] - or AI_FLAG_MODIFIED_HAND - ld [wCurrentAIFlags], a - ld a, [wAITrainerCardToPlay] - ldh [hTempCardIndex_ff9f], a - ld a, [wce1a] - ldh [hTemp_ffa0], a - ld a, [wce1b] - ldh [hTempPlayAreaLocation_ffa1], a - ld a, OPPACTION_EXECUTE_TRAINER_EFFECTS - bank1call AIMakeDecision - ret - -; AI logic for playing Maintenance -AIDecide_Maintenance: ; 2162c (8:562c) -; Imakuni? has his own thing - ld a, [wOpponentDeckID] - cp IMAKUNI_DECK_ID - jr z, .imakuni - -; skip if number of cars in hand < 4. - ld a, DUELVARS_NUMBER_OF_CARDS_IN_HAND - call GetTurnDuelistVariable - cp 4 - jr c, .no_carry - -; list out all the hand cards and remove -; wAITrainerCardToPlay from list.Then find any duplicate cards. - call CreateHandCardList - ld hl, wDuelTempList - ld a, [wAITrainerCardToPlay] - call FindAndRemoveCardFromList -; if duplicates are not found, return no carry. - call FindDuplicateCards - jp c, .no_carry - -; store the first duplicate card and remove it from the list. -; run duplicate check again. - ld [wce1a], a - ld hl, wDuelTempList - call FindAndRemoveCardFromList -; if duplicates are not found, return no carry. - call FindDuplicateCards - jp c, .no_carry - -; store the second duplicate card and return carry. - ld [wce1b], a - scf - ret - -.no_carry - or a - ret - -.imakuni -; has a 2 in 10 chance of not skipping. - ld a, 10 - call Random - cp 2 - jr nc, .no_carry - -; skip if number of cards in hand < 3. - ld a, DUELVARS_NUMBER_OF_CARDS_IN_HAND - call GetTurnDuelistVariable - cp 3 - jr c, .no_carry - -; shuffle hand cards - call CreateHandCardList - ld hl, wDuelTempList - call CountCardsInDuelTempList - call ShuffleCards - -; go through each card and find -; cards that are different from wAITrainerCardToPlay. -; if found, add those cards to wce1a and wce1a+1. - ld a, [wAITrainerCardToPlay] - ld b, a - ld c, 2 - ld de, wce1a - -.loop - ld a, [hli] - cp $ff - jr z, .no_carry - cp b - jr z, .loop - ld [de], a - inc de - dec c - jr nz, .loop - -; two cards were found, return carry. - scf - ret - -AIPlay_Recycle: ; 2169a (8:569a) - ld a, [wAITrainerCardToPlay] - ldh [hTempCardIndex_ff9f], a - ldtx de, TrainerCardSuccessCheckText - bank1call TossCoin - jr nc, .asm_216ae - ld a, [wAITrainerCardParameter] - ldh [hTemp_ffa0], a - jr .asm_216b2 -.asm_216ae - ld a, $ff - ldh [hTemp_ffa0], a -.asm_216b2 - ld a, OPPACTION_EXECUTE_TRAINER_EFFECTS - bank1call AIMakeDecision - ret - -; lists cards to look for in the Discard Pile. -; has priorities for Ghost Deck, and a "default" priority list -; (which is the Fire Charge deck, since it's the only other -; deck that runs a Recycle card in it.) -AIDecide_Recycle: ; 216b8 (8:56b8) -; no use checking if no cards in Discard Pile - call CreateDiscardPileCardList - jr c, .no_carry - - ld a, $ff - ld [wce08], a - ld [wce08 + 1], a - ld [wce08 + 2], a - ld [wce08 + 3], a - ld [wce08 + 4], a - -; handle Ghost deck differently - ld hl, wDuelTempList - ld a, [wOpponentDeckID] - cp GHOST_DECK_ID - jr z, .loop_2 - -; priority list for Fire Charge deck -.loop_1 - ld a, [hli] - cp $ff - jr z, .done - - ld b, a - call LoadCardDataToBuffer1_FromDeckIndex - -; double colorless - cp DOUBLE_COLORLESS_ENERGY - jr nz, .chansey - ld a, b - ld [wce08], a - jr .loop_1 - -.chansey - cp CHANSEY - jr nz, .tauros - ld a, b - ld [wce08 + 1], a - jr .loop_1 - -.tauros - cp TAUROS - jr nz, .jigglypuff - ld a, b - ld [wce08 + 2], a - jr .loop_1 - -.jigglypuff - cp JIGGLYPUFF1 - jr nz, .loop_1 - ld a, b - ld [wce08 + 3], a - jr .loop_1 - -; loop through wce08 and set carry -; on the first that was found in Discard Pile. -; if none were found, return no carry. -.done - ld hl, wce08 - ld b, 5 -.loop_found - ld a, [hli] - cp $ff - jr nz, .set_carry - dec b - jr nz, .loop_found -.no_carry - or a - ret -.set_carry - scf - ret - -; priority list for Ghost deck -.loop_2 - ld a, [hli] - cp $ff - jr z, .done - - ld b, a - call LoadCardDataToBuffer1_FromDeckIndex - -; gastly2 - cp GASTLY2 - jr nz, .gastly1 - ld a, b - ld [wce08], a - jr .loop_2 - -.gastly1 - cp GASTLY1 - jr nz, .zubat - ld a, b - ld [wce08 + 1], a - jr .loop_2 - -.zubat - cp ZUBAT - jr nz, .ditto - ld a, b - ld [wce08 + 2], a - jr .loop_2 - -.ditto - cp DITTO - jr nz, .meowth - ld a, b - ld [wce08 + 3], a - jr .loop_2 - -.meowth - cp MEOWTH2 - jr nz, .loop_2 - ld a, b - ld [wce08 + 4], a - jr .loop_2 - -AIPlay_Lass: ; 21755 (8:5755) - ld a, [wCurrentAIFlags] - or AI_FLAG_MODIFIED_HAND - ld [wCurrentAIFlags], a - ld a, [wAITrainerCardToPlay] - ldh [hTempCardIndex_ff9f], a - ld a, OPPACTION_EXECUTE_TRAINER_EFFECTS - bank1call AIMakeDecision - ret - -AIDecide_Lass: ; 21768 (8:5768) -; skip if player has less than 7 cards in hand - ld a, DUELVARS_NUMBER_OF_CARDS_IN_HAND - call GetNonTurnDuelistVariable - cp 7 - jr c, .no_carry - -; look for Trainer cards in hand (except for Lass) -; if any is found, return no carry. -; otherwise, return carry. - call CreateHandCardList - ld hl, wDuelTempList -.loop - ld a, [hli] - cp $ff - jr z, .set_carry - ld b, a - call LoadCardDataToBuffer1_FromDeckIndex - cp LASS - jr z, .loop - ld a, [wLoadedCard1Type] - cp TYPE_TRAINER - jr nz, .loop -.no_carry - or a - ret -.set_carry - scf - ret - -AIPlay_ItemFinder: ; 2178f (8:578f) - ld a, [wCurrentAIFlags] - or AI_FLAG_MODIFIED_HAND - ld [wCurrentAIFlags], a - ld a, [wAITrainerCardToPlay] - ldh [hTempCardIndex_ff9f], a - ld a, [wce1a] - ldh [hTemp_ffa0], a - ld a, [wce1b] - ldh [hTempPlayAreaLocation_ffa1], a - ld a, [wAITrainerCardParameter] - ldh [hTempRetreatCostCards], a - ld a, OPPACTION_EXECUTE_TRAINER_EFFECTS - bank1call AIMakeDecision - ret - -; checks whether there's Energy Removal in Discard Pile. -; if so, find duplicate cards in hand to discard -; that are not Mr Mime and Pokemon Trader cards. -; this logic is suitable only for Strange Psyshock deck. -AIDecide_ItemFinder: ; 217b1 (8:57b1) -; skip if no Discard Pile. - call CreateDiscardPileCardList - jr c, .no_carry - -; look for Energy Removal in Discard Pile - ld hl, wDuelTempList -.loop_discard_pile - ld a, [hli] - cp $ff - jr z, .no_carry - ld b, a - call LoadCardDataToBuffer1_FromDeckIndex - cp ENERGY_REMOVAL - jr nz, .loop_discard_pile -; found, store this deck index - ld a, b - ld [wce06], a - -; before looking for cards to discard in hand, -; remove any Mr Mime and Pokemon Trader cards. -; this way these are guaranteed to not be discarded. - call CreateHandCardList - ld hl, wDuelTempList -.loop_hand - ld a, [hli] - cp $ff - jr z, .choose_discard - ld b, a - call LoadCardDataToBuffer1_FromDeckIndex - cp MR_MIME - jr nz, .pkmn_trader - call RemoveCardFromList - jr .loop_hand -.pkmn_trader - cp POKEMON_TRADER - jr nz, .loop_hand - call RemoveCardFromList - jr .loop_hand - -; choose cards to discard from hand. -.choose_discard - ld hl, wDuelTempList - -; do not discard wAITrainerCardToPlay - ld a, [wAITrainerCardToPlay] - call FindAndRemoveCardFromList -; find any duplicates, if not found, return no carry. - call FindDuplicateCards - jp c, .no_carry - -; store the duplicate found in wce1a and -; remove it from the hand list. - ld [wce1a], a - ld hl, wDuelTempList - call FindAndRemoveCardFromList -; find duplicates again, if not found, return no carry. - call FindDuplicateCards - jp c, .no_carry - -; store the duplicate found in wce1b. -; output the card to be recovered from the Discard Pile. - ld [wce1b], a - ld a, [wce06] - scf - ret - -.no_carry - or a - ret - -AIPlay_Imakuni: ; 21813 (8:5813) - ld a, [wAITrainerCardToPlay] - ldh [hTempCardIndex_ff9f], a - ld a, OPPACTION_EXECUTE_TRAINER_EFFECTS - bank1call AIMakeDecision - ret - -; only sets carry if Active card is not confused. -AIDecide_Imakuni: ; 2181e (8:581e) - ld a, DUELVARS_ARENA_CARD_STATUS - call GetTurnDuelistVariable - and CNF_SLP_PRZ - cp CONFUSED - jr z, .confused - scf - ret -.confused - or a - ret - -AIPlay_Gambler: ; 2182d (8:582d) - ld a, [wCurrentAIFlags] - or AI_FLAG_MODIFIED_HAND - ld [wCurrentAIFlags], a - ld a, [wOpponentDeckID] - cp IMAKUNI_DECK_ID - jr z, .asm_2186a - ld hl, wRNG1 - ld a, [hli] - ld [wce06], a - ld a, [hli] - ld [wce08], a - ld a, [hl] - ld [wce0f], a - ld a, $50 - ld [hld], a - ld [hld], a - ld [hl], a - ld a, [wAITrainerCardToPlay] - ldh [hTempCardIndex_ff9f], a - ld a, OPPACTION_EXECUTE_TRAINER_EFFECTS - bank1call AIMakeDecision - ld hl, wRNG1 - ld a, [wce06] - ld [hli], a - ld a, [wce08] - ld [hli], a - ld a, [wce0f] - ld [hl], a - ret -.asm_2186a - ld a, [wAITrainerCardToPlay] - ldh [hTempCardIndex_ff9f], a - ld a, OPPACTION_EXECUTE_TRAINER_EFFECTS - bank1call AIMakeDecision - ret - -; checks whether to play Gambler. -; aside from Imakuni?, all other opponents only -; play this card if Player is running Mewtwo1-only deck. -AIDecide_Gambler: ; 21875 (8:5875) -; Imakuni? has his own routine - ld a, [wOpponentDeckID] - cp IMAKUNI_DECK_ID - jr z, .imakuni - -; check if flag is set for Player using Mewtwo1 only deck - ld a, [wAIBarrierFlagCounter] - and AI_MEWTWO_MILL - jr z, .no_carry - -; set carry if number of cards in deck <= 4. -; this is done to counteract the deck out strategy -; of Mewtwo1 deck, by replenishing the deck with hand cards. - ld a, DUELVARS_NUMBER_OF_CARDS_NOT_IN_DECK - call GetTurnDuelistVariable - cp DECK_SIZE - 4 - jr nc, .set_carry -.no_carry - or a - ret - -.imakuni -; has a 2 in 10 chance of returning carry - ld a, 10 - call Random - cp 2 - jr nc, .no_carry -.set_carry - scf - ret - -AIPlay_Revive: ; 21899 (8:5899) - ld a, [wAITrainerCardToPlay] - ldh [hTempCardIndex_ff9f], a - ld a, [wAITrainerCardParameter] - ldh [hTemp_ffa0], a - ld a, OPPACTION_EXECUTE_TRAINER_EFFECTS - bank1call AIMakeDecision - ret - -; checks certain cards in Discard Pile to use Revive on. -; suitable for Muscle For Brains deck only. -AIDecide_Revive: ; 218a9 (8:58a9) -; skip if no cards in Discard Pile - call CreateDiscardPileCardList - jr c, .no_carry - -; skip if number of Pokemon cards in Play Area >= 4 - ld a, DUELVARS_NUMBER_OF_POKEMON_IN_PLAY_AREA - call GetTurnDuelistVariable - cp 4 - jr nc, .no_carry - -; look in Discard Pile for specific cards. - ld hl, wDuelTempList -.loop_discard_pile - ld a, [hli] - cp $ff - jr z, .no_carry - ld b, a - call LoadCardDataToBuffer1_FromDeckIndex - -; these checks have a bug. -; it works fine for Hitmonchan and Hitmonlee, -; but in case it's a Tauros card, the routine will fallthrough -; into the Kangaskhan check. since it will never be equal to Kangaskhan, -; it will fallthrough into the set carry branch. -; in case it's a Kangaskhan card, the check will fail in the Tauros check -; and jump back into the loop. so just by accident the Tauros check works, -; but Kangaskhan will never be correctly checked because of this. - cp HITMONCHAN - jr z, .set_carry - cp HITMONLEE - jr z, .set_carry - cp TAUROS - jr nz, .loop_discard_pile ; bug, these two lines should be swapped - cp KANGASKHAN - jr z, .set_carry ; bug, these two lines should be swapped - -.set_carry - ld a, b - scf - ret -.no_carry - or a - ret - -AIPlay_PokemonFlute: ; 218d8 (8:58d8) - ld a, [wAITrainerCardToPlay] - ldh [hTempCardIndex_ff9f], a - ld a, [wAITrainerCardParameter] - ldh [hTemp_ffa0], a - ld a, OPPACTION_EXECUTE_TRAINER_EFFECTS - bank1call AIMakeDecision - ret - -AIDecide_PokemonFlute: ; 218e8 (8:58e8) -; if player has no Discard Pile, skip. - call SwapTurn - call CreateDiscardPileCardList - call SwapTurn - jr c, .no_carry - -; if player's Play Area is already full, skip. - ld a, DUELVARS_NUMBER_OF_POKEMON_IN_PLAY_AREA - call GetNonTurnDuelistVariable - cp MAX_PLAY_AREA_POKEMON - jr nc, .no_carry - - ld a, [wOpponentDeckID] - cp IMAKUNI_DECK_ID - jr z, .imakuni - - ld a, $ff - ld [wce06], a - ld [wce08], a - -; find Basic stage Pokemon with lowest HP in Discard Pile - ld hl, wDuelTempList -.loop_1 - ld a, [hli] - cp $ff - jr z, .done - - ld b, a - call SwapTurn - call LoadCardDataToBuffer1_FromDeckIndex - call SwapTurn -; skip this card if it's not Pokemon card - ld a, [wLoadedCard1Type] - cp TYPE_ENERGY - jr nc, .loop_1 -; skip this card if it's not Basic Stage - ld a, [wLoadedCard1Stage] - or a ; BASIC - jr nz, .loop_1 - -; compare this HP with one stored - ld a, [wLoadedCard1HP] - push hl - ld hl, wce06 - cp [hl] - pop hl - jr nc, .loop_1 -; if lower, store this one - ld [wce06], a - ld a, b - ld [wce08], a - jr .loop_1 - -.done -; if lowest HP found >= 50, return no carry - ld a, [wce06] - cp 50 - jr nc, .no_carry -; otherwise output its deck index in a and set carry. - ld a, [wce08] - scf - ret -.no_carry - or a - ret - -.imakuni -; has 2 in 10 chance of not skipping - ld a, 10 - call Random - cp 2 - jr nc, .no_carry - -; look for any Basic Pokemon card - ld hl, wDuelTempList -.loop_2 - ld a, [hli] - cp $ff - jr z, .no_carry - ld b, a - call SwapTurn - call LoadCardDataToBuffer1_FromDeckIndex - call SwapTurn - ld a, [wLoadedCard1Type] - cp TYPE_ENERGY - jr nc, .loop_2 - ld a, [wLoadedCard1Stage] - or a ; BASIC - jr nz, .loop_2 - -; a Basic stage Pokemon was found, return carry - ld a, b - scf - ret - -AIPlay_ClefairyDollOrMysteriousFossil: ; 21977 (8:5977) - ld a, [wAITrainerCardToPlay] - ldh [hTempCardIndex_ff9f], a - ld a, OPPACTION_EXECUTE_TRAINER_EFFECTS - bank1call AIMakeDecision - ret - -; AI logic for playing Clefairy Doll -AIDecide_ClefairyDollOrMysteriousFossil: ; 21982 (8:5982) -; if has max number of Play Area Pokemon, skip - ld a, DUELVARS_NUMBER_OF_POKEMON_IN_PLAY_AREA - call GetTurnDuelistVariable - cp MAX_PLAY_AREA_POKEMON - jr nc, .no_carry - -; store number of Play Area Pokemon cards - ld [wce06], a - -; if the Arena card is Wigglytuff, return carry - ld a, DUELVARS_ARENA_CARD - call GetTurnDuelistVariable - call GetCardIDFromDeckIndex - ld a, e - cp WIGGLYTUFF - jr z, .set_carry - -; if number of Play Area Pokemon >= 4, return no carry - ld a, [wce06] - cp 4 - jr nc, .no_carry - -.set_carry - scf - ret -.no_carry - or a - ret - -AIPlay_Pokeball: ; 219a6 (8:59a6) - ld a, [wAITrainerCardToPlay] - ldh [hTempCardIndex_ff9f], a - ldtx de, TrainerCardSuccessCheckText - bank1call TossCoin - ldh [hTemp_ffa0], a - jr nc, .asm_219bc - ld a, [wAITrainerCardParameter] - ldh [hTempPlayAreaLocation_ffa1], a - jr .asm_219c0 -.asm_219bc - ld a, $ff - ldh [hTempPlayAreaLocation_ffa1], a -.asm_219c0 - ld a, OPPACTION_EXECUTE_TRAINER_EFFECTS - bank1call AIMakeDecision - ret - -AIDecide_Pokeball: ; 219c6 (8:59c6) -; go to the routines associated with deck ID - ld a, [wOpponentDeckID] - cp FIRE_CHARGE_DECK_ID - jr z, .fire_charge - cp HARD_POKEMON_DECK_ID - jr z, .hard_pokemon - cp PIKACHU_DECK_ID - jr z, .pikachu - cp ETCETERA_DECK_ID - jr z, .etcetera - cp LOVELY_NIDORAN_DECK_ID - jp z, .lovely_nidoran - or a - ret - -; this deck runs a deck check for specific -; card IDs in order of decreasing priority -.fire_charge - ld e, CHANSEY - ld a, CARD_LOCATION_DECK - call LookForCardIDInLocation - ret c - ld e, TAUROS - ld a, CARD_LOCATION_DECK - call LookForCardIDInLocation - ret c - ld e, JIGGLYPUFF1 - ld a, CARD_LOCATION_DECK - call LookForCardIDInLocation - ret c - ret - -; this deck runs a deck check for specific -; card IDs in order of decreasing priority -.hard_pokemon - ld e, RHYHORN - ld a, CARD_LOCATION_DECK - call LookForCardIDInLocation - ret c - ld e, RHYDON - ld a, CARD_LOCATION_DECK - call LookForCardIDInLocation - ret c - ld e, ONIX - ld a, CARD_LOCATION_DECK - call LookForCardIDInLocation - ret c - ret - -; this deck runs a deck check for specific -; card IDs in order of decreasing priority -.pikachu - ld e, PIKACHU2 - ld a, CARD_LOCATION_DECK - call LookForCardIDInLocation - ret c - ld e, PIKACHU3 - ld a, CARD_LOCATION_DECK - call LookForCardIDInLocation - ret c - ld e, PIKACHU4 - ld a, CARD_LOCATION_DECK - call LookForCardIDInLocation - ret c - ld e, PIKACHU1 - ld a, CARD_LOCATION_DECK - call LookForCardIDInLocation - ret c - ld e, FLYING_PIKACHU - ld a, CARD_LOCATION_DECK - call LookForCardIDInLocation - ret c - ret - -; this deck runs a deck check for specific -; card IDs in order of decreasing priority -; given a specific energy card in hand. -; also it avoids redundancy, so if it already -; has that card ID in the hand, it is skipped. -.etcetera -; fire - ld a, FIRE_ENERGY - call LookForCardIDInHandList_Bank8 - jr nc, .lightning - ld a, CHARMANDER - call LookForCardIDInHandList_Bank8 - jr c, .lightning - ld a, MAGMAR2 - call LookForCardIDInHandList_Bank8 - jr c, .lightning - ld e, CHARMANDER - ld a, CARD_LOCATION_DECK - call LookForCardIDInLocation - ret c - ld e, MAGMAR2 - ld a, CARD_LOCATION_DECK - call LookForCardIDInLocation - ret c - -.lightning - ld a, LIGHTNING_ENERGY - call LookForCardIDInHandList_Bank8 - jr nc, .fighting - ld a, PIKACHU1 - call LookForCardIDInHandList_Bank8 - jr c, .fighting - ld a, MAGNEMITE1 - call LookForCardIDInHandList_Bank8 - jr c, .fighting - ld e, PIKACHU1 - ld a, CARD_LOCATION_DECK - call LookForCardIDInLocation - ret c - ld e, MAGNEMITE1 - ld a, CARD_LOCATION_DECK - call LookForCardIDInLocation - ret c - -.fighting - ld a, FIGHTING_ENERGY - call LookForCardIDInHandList_Bank8 - jr nc, .psychic - ld a, DIGLETT - call LookForCardIDInHandList_Bank8 - jr c, .psychic - ld a, MACHOP - call LookForCardIDInHandList_Bank8 - jr c, .psychic - ld e, DIGLETT - ld a, CARD_LOCATION_DECK - call LookForCardIDInLocation - ret c - ld e, MACHOP - ld a, CARD_LOCATION_DECK - call LookForCardIDInLocation - ret c - -.psychic - ld a, PSYCHIC_ENERGY - call LookForCardIDInHandList_Bank8 - jr nc, .done_etcetera - ld a, GASTLY1 - call LookForCardIDInHandList_Bank8 - jr c, .done_etcetera - ld a, JYNX - call LookForCardIDInHandList_Bank8 - jr c, .done_etcetera - ld e, GASTLY1 - ld a, CARD_LOCATION_DECK - call LookForCardIDInLocation - ret c - ld e, JYNX - ld a, CARD_LOCATION_DECK - call LookForCardIDInLocation - ret c -.done_etcetera - or a - ret - -; this deck looks for card evolutions if -; its pre-evolution is in hand or in Play Area. -; if none of these are found, it looks for pre-evolutions -; of cards it has in hand. -; it does this for both the NidoranM (first) -; and NidoranF (second) families. -.lovely_nidoran - ld b, NIDORANM - ld a, NIDORINO - call LookForCardIDInDeck_GivenCardIDInHandAndPlayArea - ret c - ld b, NIDORINO - ld a, NIDOKING - call LookForCardIDInDeck_GivenCardIDInHandAndPlayArea - ret c - ld a, NIDORANM - ld b, NIDORINO - call LookForCardIDInDeck_GivenCardIDInHand - ret c - ld a, NIDORINO - ld b, NIDOKING - call LookForCardIDInDeck_GivenCardIDInHand - ret c - ld b, NIDORANF - ld a, NIDORINA - call LookForCardIDInDeck_GivenCardIDInHandAndPlayArea - ret c - ld b, NIDORINA - ld a, NIDOQUEEN - call LookForCardIDInDeck_GivenCardIDInHandAndPlayArea - ret c - ld a, NIDORANF - ld b, NIDORINA - call LookForCardIDInDeck_GivenCardIDInHand - ret c - ld a, NIDORINA - ld b, NIDOQUEEN - call LookForCardIDInDeck_GivenCardIDInHand - ret c - ret - -AIPlay_ComputerSearch: ; 21b12 (8:5b12) - ld a, [wCurrentAIFlags] - or AI_FLAG_MODIFIED_HAND - ld [wCurrentAIFlags], a - ld a, [wAITrainerCardToPlay] - ldh [hTempCardIndex_ff9f], a - ld a, [wAITrainerCardParameter] - ldh [hTempRetreatCostCards], a - ld a, [wce1a] - ldh [hTemp_ffa0], a - ld a, [wce1b] - ldh [hTempPlayAreaLocation_ffa1], a - ld a, OPPACTION_EXECUTE_TRAINER_EFFECTS - bank1call AIMakeDecision - ret - -; checks what Deck ID AI is playing and handle -; them in their own routine. -AIDecide_ComputerSearch: ; 21b34 (8:5b34) -; skip if number of cards in hand < 3 - ld a, DUELVARS_NUMBER_OF_CARDS_IN_HAND - call GetTurnDuelistVariable - cp 3 - jr c, .no_carry - - ld a, [wOpponentDeckID] - cp ROCK_CRUSHER_DECK_ID - jr z, AIDecide_ComputerSearch_RockCrusher - cp WONDERS_OF_SCIENCE_DECK_ID - jp z, AIDecide_ComputerSearch_WondersOfScience - cp FIRE_CHARGE_DECK_ID - jp z, AIDecide_ComputerSearch_FireCharge - cp ANGER_DECK_ID - jp z, AIDecide_ComputerSearch_Anger - -.no_carry - or a - ret - -AIDecide_ComputerSearch_RockCrusher: ; 21b55 (8:5b55) -; if number of cards in hand is equal to 3, -; target Professor Oak in deck - ld a, DUELVARS_NUMBER_OF_CARDS_IN_HAND - call GetTurnDuelistVariable - cp 3 - jr nz, .graveler - - ld e, PROFESSOR_OAK - ld a, CARD_LOCATION_DECK - call LookForCardIDInLocation - jr c, .find_discard_cards_1 - ; no Professor Oak in deck, fallthrough - -.no_carry - or a - ret - -.find_discard_cards_1 - ld [wce06], a - ld a, $ff - ld [wce1a], a - ld [wce1b], a - - call CreateHandCardList - ld hl, wDuelTempList - ld de, wce1a -.loop_hand_1 - ld a, [hli] - cp $ff - jr z, .check_discard_cards - - ld c, a - call LoadCardDataToBuffer1_FromDeckIndex - -; if any of the following cards are in the hand, -; return no carry. - cp PROFESSOR_OAK - jr z, .no_carry - cp FIGHTING_ENERGY - jr z, .no_carry - cp DOUBLE_COLORLESS_ENERGY - jr z, .no_carry - cp DIGLETT - jr z, .no_carry - cp GEODUDE - jr z, .no_carry - cp ONIX - jr z, .no_carry - cp RHYHORN - jr z, .no_carry - -; if it's same as wAITrainerCardToPlay, skip this card. - ld a, [wAITrainerCardToPlay] - ld b, a - ld a, c - cp b - jr z, .loop_hand_1 - -; store this card index in memory - ld [de], a - inc de - jr .loop_hand_1 - -.check_discard_cards -; check if two cards were found -; if so, output in a the deck index -; of Professor Oak card found in deck and set carry. - ld a, [wce1b] - cp $ff - jr z, .no_carry - ld a, [wce06] - scf - ret - -; more than 3 cards in hand, so look for -; specific evolution cards. - -; checks if there is a Graveler card in the deck to target. -; if so, check if there's Geodude in hand or Play Area, -; and if there's no Graveler card in hand, proceed. -; also removes Geodude from hand list so that it is not discarded. -.graveler - ld e, GRAVELER - ld a, CARD_LOCATION_DECK - call LookForCardIDInLocation - jr nc, .golem - ld [wce06], a - ld a, GEODUDE - call LookForCardIDInHandAndPlayArea - jr nc, .golem - ld a, GRAVELER - call LookForCardIDInHandList_Bank8 - jr c, .golem - call CreateHandCardList - ld hl, wDuelTempList - ld e, GEODUDE - farcall RemoveCardIDInList - jr .find_discard_cards_2 - -; checks if there is a Golem card in the deck to target. -; if so, check if there's Graveler in Play Area, -; and if there's no Golem card in hand, proceed. -.golem - ld e, GOLEM - ld a, CARD_LOCATION_DECK - call LookForCardIDInLocation - jr nc, .dugtrio - ld [wce06], a - ld a, GRAVELER - call LookForCardIDInPlayArea_Bank8 - jr nc, .dugtrio - ld a, GOLEM - call LookForCardIDInHandList_Bank8 - jr c, .dugtrio - call CreateHandCardList - ld hl, wDuelTempList - jr .find_discard_cards_2 - -; checks if there is a Dugtrio card in the deck to target. -; if so, check if there's Diglett in Play Area, -; and if there's no Dugtrio card in hand, proceed. -.dugtrio - ld e, DUGTRIO - ld a, CARD_LOCATION_DECK - call LookForCardIDInLocation - jp nc, .no_carry - ld [wce06], a - ld a, DIGLETT - call LookForCardIDInPlayArea_Bank8 - jp nc, .no_carry - ld a, DUGTRIO - call LookForCardIDInHandList_Bank8 - jp c, .no_carry - call CreateHandCardList - ld hl, wDuelTempList - jr .find_discard_cards_2 - -.find_discard_cards_2 - ld a, $ff - ld [wce1a], a - ld [wce1b], a - - ld bc, wce1a - ld d, $00 ; start considering Trainer cards only - -; stores wAITrainerCardToPlay in e so that -; all routines ignore it for the discard effects. - ld a, [wAITrainerCardToPlay] - ld e, a - -; this loop will store in wce1a cards to discard from hand. -; at the start it will only consider Trainer cards, -; then if there are still needed to discard, -; move on to Pokemon cards, and finally to Energy cards. -.loop_hand_2 - call RemoveFromListDifferentCardOfGivenType - jr c, .found - inc d ; move on to next type (Pokemon, then Energy) - ld a, $03 - cp d - jp z, .no_carry ; no more types to look - jr .loop_hand_2 -.found -; store this card in memory, -; and if there's still one more card to search for, -; jump back into the loop. - ld [bc], a - inc bc - ld a, [wce1b] - cp $ff - jr z, .loop_hand_2 - -; output in a Computer Search target and set carry. - ld a, [wce06] - scf - ret - -AIDecide_ComputerSearch_WondersOfScience: ; 21c56 (8:5c56) -; if number of cards in hand < 5, target Professor Oak in deck - ld a, DUELVARS_NUMBER_OF_CARDS_IN_HAND - call GetTurnDuelistVariable - cp 5 - jr nc, .look_in_hand - -; target Professor Oak for Computer Search - ld e, PROFESSOR_OAK - ld a, CARD_LOCATION_DECK - call LookForCardIDInLocation - jp nc, .look_in_hand ; can be a jr - ld [wce06], a - jr .find_discard_cards - -; Professor Oak not in deck, move on to -; look for other cards instead. -; if Grimer or Muk are not in hand, -; check whether to use Computer Search on them. -.look_in_hand - ld a, GRIMER - call LookForCardIDInHandList_Bank8 - jr nc, .target_grimer - ld a, MUK - call LookForCardIDInHandList_Bank8 - jr nc, .target_muk - -.no_carry - or a - ret - -; first check Grimer -; if in deck, check cards to discard. -.target_grimer - ld e, GRIMER - ld a, CARD_LOCATION_DECK - call LookForCardIDInLocation - jp nc, .no_carry ; can be a jr - ld [wce06], a - jr .find_discard_cards - -; first check Muk -; if in deck, check cards to discard. -.target_muk - ld e, MUK - ld a, CARD_LOCATION_DECK - call LookForCardIDInLocation - jp nc, .no_carry ; can be a jr - ld [wce06], a - -; only discard Trainer cards from hand. -; if there are less than 2 Trainer cards to discard, -; then return with no carry. -; else, store the cards to discard and the -; target card deck index, and return carry. -.find_discard_cards - call CreateHandCardList - ld hl, wDuelTempList - ld d, $00 ; first consider Trainer cards - -; ignore wAITrainerCardToPlay for the discard effects. - ld a, [wAITrainerCardToPlay] - ld e, a - call RemoveFromListDifferentCardOfGivenType - jr nc, .no_carry - ld [wce1a], a - call RemoveFromListDifferentCardOfGivenType - jr nc, .no_carry - ld [wce1b], a - ld a, [wce06] - scf - ret - -AIDecide_ComputerSearch_FireCharge: ; 21cbb (8:5cbb) -; pick target card in deck from highest to lowest priority. -; if not found in hand, go to corresponding branch. - ld a, CHANSEY - call LookForCardIDInHandList_Bank8 - jr nc, .chansey - ld a, TAUROS - call LookForCardIDInHandList_Bank8 - jr nc, .tauros - ld a, JIGGLYPUFF1 - call LookForCardIDInHandList_Bank8 - jr nc, .jigglypuff - ; fallthrough - -.no_carry - or a - ret - -; for each card targeted, check if it's in deck and, -; if not, then return no carry. -; else, look for cards to discard. -.chansey - ld e, CHANSEY - ld a, CARD_LOCATION_DECK - call LookForCardIDInLocation - jp nc, .no_carry - ld [wce06], a - jr .find_discard_cards -.tauros - ld e, TAUROS - ld a, CARD_LOCATION_DECK - call LookForCardIDInLocation - jp nc, .no_carry - ld [wce06], a - jr .find_discard_cards -.jigglypuff - ld e, JIGGLYPUFF1 - ld a, CARD_LOCATION_DECK - call LookForCardIDInLocation - jp nc, .no_carry - ld [wce06], a - -; only discard Trainer cards from hand. -; if there are less than 2 Trainer cards to discard, -; then return with no carry. -; else, store the cards to discard and the -; target card deck index, and return carry. -.find_discard_cards - call CreateHandCardList - ld hl, wDuelTempList - ld d, $00 ; first consider Trainer cards - -; ignore wAITrainerCardToPlay for the discard effects. - ld a, [wAITrainerCardToPlay] - ld e, a - call RemoveFromListDifferentCardOfGivenType - jr nc, .no_carry - ld [wce1a], a - call RemoveFromListDifferentCardOfGivenType - jr nc, .no_carry - ld [wce1b], a - ld a, [wce06] - scf - ret - -AIDecide_ComputerSearch_Anger: ; 21d1e (8:5d1e) -; for each of the following cards, -; first run a check if there's a pre-evolution in -; Play Area or in the hand. If there is, choose it as target. -; otherwise, check if the evolution card is in -; hand and if so, choose it as target instead. - ld b, RATTATA - ld a, RATICATE - call LookForCardIDInDeck_GivenCardIDInHandAndPlayArea - jr c, .find_discard_cards - ld a, RATTATA - ld b, RATICATE - call LookForCardIDInDeck_GivenCardIDInHand - jr c, .find_discard_cards - ld b, GROWLITHE - ld a, ARCANINE1 - call LookForCardIDInDeck_GivenCardIDInHandAndPlayArea - jr c, .find_discard_cards - ld a, GROWLITHE - ld b, ARCANINE1 - call LookForCardIDInDeck_GivenCardIDInHand - jr c, .find_discard_cards - ld b, DODUO - ld a, DODRIO - call LookForCardIDInDeck_GivenCardIDInHandAndPlayArea - jr c, .find_discard_cards - ld a, DODUO - ld b, DODRIO - call LookForCardIDInDeck_GivenCardIDInHand - jr c, .find_discard_cards - ; fallthrough - -.no_carry - or a - ret - -; only discard Trainer cards from hand. -; if there are less than 2 Trainer cards to discard, -; then return with no carry. -; else, store the cards to discard and the -; target card deck index, and return carry. -.find_discard_cards - ld [wce06], a - call CreateHandCardList - ld hl, wDuelTempList - ld d, $00 ; first consider Trainer cards - -; ignore wAITrainerCardToPlay for the discard effects. - ld a, [wAITrainerCardToPlay] - ld e, a - call RemoveFromListDifferentCardOfGivenType - jr nc, .no_carry - ld [wce1a], a - call RemoveFromListDifferentCardOfGivenType - jr nc, .no_carry - ld [wce1b], a - ld a, [wce06] - scf - ret - -AIPlay_PokemonTrader: ; 21d7a (8:5d7a) - ld a, [wAITrainerCardToPlay] - ldh [hTempCardIndex_ff9f], a - ld a, [wAITrainerCardParameter] - ldh [hTemp_ffa0], a - ld a, [wce1a] - ldh [hTempPlayAreaLocation_ffa1], a - ld a, OPPACTION_EXECUTE_TRAINER_EFFECTS - bank1call AIMakeDecision - ret - -AIDecide_PokemonTrader: ; 21d8f (8:5d8f) -; each deck has their own routine for picking -; what Pokemon to look for. - ld a, [wOpponentDeckID] - cp LEGENDARY_MOLTRES_DECK_ID - jr z, AIDecide_PokemonTrader_LegendaryMoltres - cp LEGENDARY_ARTICUNO_DECK_ID - jr z, AIDecide_PokemonTrader_LegendaryArticuno - cp LEGENDARY_DRAGONITE_DECK_ID - jp z, AIDecide_PokemonTrader_LegendaryDragonite - cp LEGENDARY_RONALD_DECK_ID - jp z, AIDecide_PokemonTrader_LegendaryRonald - cp BLISTERING_POKEMON_DECK_ID - jp z, AIDecide_PokemonTrader_BlisteringPokemon - cp SOUND_OF_THE_WAVES_DECK_ID - jp z, AIDecide_PokemonTrader_SoundOfTheWaves - cp POWER_GENERATOR_DECK_ID - jp z, AIDecide_PokemonTrader_PowerGenerator - cp FLOWER_GARDEN_DECK_ID - jp z, AIDecide_PokemonTrader_FlowerGarden - cp STRANGE_POWER_DECK_ID - jp z, AIDecide_PokemonTrader_StrangePower - cp FLAMETHROWER_DECK_ID - jp z, AIDecide_PokemonTrader_Flamethrower - or a - ret - -AIDecide_PokemonTrader_LegendaryMoltres: ; 21dc4 (8:5dc4) -; look for Moltres2 card in deck to trade with a -; card in hand different from Moltres1. - ld a, MOLTRES2 - ld e, MOLTRES1 - call LookForCardIDToTradeWithDifferentHandCard - jr nc, .no_carry -; success - ld [wce1a], a - ld a, e - scf - ret -.no_carry - or a - ret - -AIDecide_PokemonTrader_LegendaryArticuno: ; 21dd5 (8:5dd5) -; if has none of these cards in Hand or Play Area, proceed - ld a, ARTICUNO1 - call LookForCardIDInHandAndPlayArea - jr c, .no_carry - ld a, LAPRAS - call LookForCardIDInHandAndPlayArea - jr c, .no_carry - -; if doesn't have Seel in Hand or Play Area, -; look for it in the deck. -; otherwise, look for Dewgong instead. - ld a, SEEL - call LookForCardIDInHandAndPlayArea - jr c, .dewgong - - ld e, SEEL - ld a, CARD_LOCATION_DECK - call LookForCardIDInLocation - jr nc, .dewgong - ld [wce1a], a - jr .check_hand - -.dewgong - ld a, DEWGONG - call LookForCardIDInHandAndPlayArea - jr c, .no_carry - ld e, DEWGONG - ld a, CARD_LOCATION_DECK - call LookForCardIDInLocation - jr nc, .no_carry - ld [wce1a], a - -; a Seel or Dewgong was found in deck, -; check hand for card to trade for -.check_hand - ld a, CHANSEY - call CheckIfHasCardIDInHand - jr c, .set_carry - ld a, DITTO - call CheckIfHasCardIDInHand - jr c, .set_carry - ld a, ARTICUNO2 - call CheckIfHasCardIDInHand - jr c, .set_carry - ; doesn't have any of the cards in hand - -.no_carry - or a - ret - -.set_carry - scf - ret - -AIDecide_PokemonTrader_LegendaryDragonite: ; 21e24 (8:5e24) -; if has less than 5 cards of energy -; and of Pokemon in hand/Play Area, -; target a Kangaskhan in deck. - farcall CountOppEnergyCardsInHandAndAttached - cp 5 - jr c, .kangaskhan - call CountPokemonCardsInHandAndInPlayArea - cp 5 - jr c, .kangaskhan - ; total number of energy cards >= 5 - ; total number of Pokemon cards >= 5 - -; for each of the following cards, -; first run a check if there's a pre-evolution in -; Play Area or in the hand. If there is, choose it as target. -; otherwise, check if the evolution card is in -; hand and if so, choose it as target instead. - ld b, MAGIKARP - ld a, GYARADOS - call LookForCardIDInDeck_GivenCardIDInHandAndPlayArea - jr c, .choose_hand - ld a, MAGIKARP - ld b, GYARADOS - call LookForCardIDInDeck_GivenCardIDInHand - jr c, .choose_hand - ld b, DRATINI - ld a, DRAGONAIR - call LookForCardIDInDeck_GivenCardIDInHandAndPlayArea - jr c, .choose_hand - ld b, DRAGONAIR - ld a, DRAGONITE1 - call LookForCardIDInDeck_GivenCardIDInHandAndPlayArea - jr c, .choose_hand - ld a, DRATINI - ld b, DRAGONAIR - call LookForCardIDInDeck_GivenCardIDInHand - jr c, .choose_hand - ld a, DRAGONAIR - ld b, DRAGONITE1 - call LookForCardIDInDeck_GivenCardIDInHand - jr c, .choose_hand - ld b, CHARMANDER - ld a, CHARMELEON - call LookForCardIDInDeck_GivenCardIDInHandAndPlayArea - jr c, .choose_hand - ld b, CHARMELEON - ld a, CHARIZARD - call LookForCardIDInDeck_GivenCardIDInHandAndPlayArea - jr c, .choose_hand - ld a, CHARMANDER - ld b, CHARMELEON - call LookForCardIDInDeck_GivenCardIDInHand - jr c, .choose_hand - ld a, CHARMELEON - ld b, CHARIZARD - call LookForCardIDInDeck_GivenCardIDInHand - jr c, .choose_hand - jr .no_carry - -.kangaskhan - ld e, KANGASKHAN - ld a, CARD_LOCATION_DECK - call LookForCardIDInLocation - jr nc, .no_carry - -; card was found as target in deck, -; look for card in hand to trade with -.choose_hand - ld [wce1a], a - ld a, DRAGONAIR - call CheckIfHasCardIDInHand - jr c, .set_carry - ld a, CHARMELEON - call CheckIfHasCardIDInHand - jr c, .set_carry - ld a, GYARADOS - call CheckIfHasCardIDInHand - jr c, .set_carry - ld a, MAGIKARP - call CheckIfHasCardIDInHand - jr c, .set_carry - ld a, CHARMANDER - call CheckIfHasCardIDInHand - jr c, .set_carry - ld a, DRATINI - call CheckIfHasCardIDInHand - jr c, .set_carry - ; non found - -.no_carry - or a - ret -.set_carry - scf - ret - -AIDecide_PokemonTrader_LegendaryRonald: ; 21ec9 (8:5ec9) -; for each of the following cards, -; first run a check if there's a pre-evolution in -; Play Area or in the hand. If there is, choose it as target. -; otherwise, check if the evolution card is in -; hand and if so, choose it as target instead. - ld b, EEVEE - ld a, FLAREON1 - call LookForCardIDInDeck_GivenCardIDInHandAndPlayArea - jr c, .choose_hand - ld b, EEVEE - ld a, VAPOREON1 - call LookForCardIDInDeck_GivenCardIDInHandAndPlayArea - jr c, .choose_hand - ld b, EEVEE - ld a, JOLTEON1 - call LookForCardIDInDeck_GivenCardIDInHandAndPlayArea - jr c, .choose_hand - ld a, EEVEE - ld b, FLAREON1 - call LookForCardIDInDeck_GivenCardIDInHand - jr c, .choose_hand - ld a, EEVEE - ld b, VAPOREON1 - call LookForCardIDInDeck_GivenCardIDInHand - jr c, .choose_hand - ld a, EEVEE - ld b, JOLTEON1 - call LookForCardIDInDeck_GivenCardIDInHand - jr c, .choose_hand - ld b, DRATINI - ld a, DRAGONAIR - call LookForCardIDInDeck_GivenCardIDInHandAndPlayArea - jr c, .choose_hand - ld b, DRAGONAIR - ld a, DRAGONITE1 - call LookForCardIDInDeck_GivenCardIDInHandAndPlayArea - jr c, .choose_hand - ld a, DRATINI - ld b, DRAGONAIR - call LookForCardIDInDeck_GivenCardIDInHand - jr c, .choose_hand - ld a, DRAGONAIR - ld b, DRAGONITE1 - call LookForCardIDInDeck_GivenCardIDInHand - jr c, .choose_hand - jr .no_carry - -; card was found as target in deck, -; look for card in hand to trade with -.choose_hand - ld [wce1a], a - ld a, ZAPDOS3 - call LookForCardIDInHandList_Bank8 - jr c, .set_carry - ld a, ARTICUNO2 - call LookForCardIDInHandList_Bank8 - jr c, .set_carry - ld a, MOLTRES2 - call LookForCardIDInHandList_Bank8 - jr c, .set_carry - ; none found - -.no_carry - or a - ret -.set_carry - scf - ret - -AIDecide_PokemonTrader_BlisteringPokemon: ; 21f41 (8:5f41) -; for each of the following cards, -; first run a check if there's a pre-evolution in -; Play Area or in the hand. If there is, choose it as target. -; otherwise, check if the evolution card is in -; hand and if so, choose it as target instead. - ld b, RHYHORN - ld a, RHYDON - call LookForCardIDInDeck_GivenCardIDInHandAndPlayArea - jr c, .find_duplicates - ld a, RHYHORN - ld b, RHYDON - call LookForCardIDInDeck_GivenCardIDInHand - jr c, .find_duplicates - ld b, CUBONE - ld a, MAROWAK1 - call LookForCardIDInDeck_GivenCardIDInHandAndPlayArea - jr c, .find_duplicates - ld a, CUBONE - ld b, MAROWAK1 - call LookForCardIDInDeck_GivenCardIDInHand - jr c, .find_duplicates - ld b, PONYTA - ld a, RAPIDASH - call LookForCardIDInDeck_GivenCardIDInHandAndPlayArea - jr c, .find_duplicates - ld a, PONYTA - ld b, RAPIDASH - call LookForCardIDInDeck_GivenCardIDInHand - jr c, .find_duplicates - jr .no_carry - -; a card in deck was found to look for, -; check if there are duplicates in hand to trade with. -.find_duplicates - ld [wce1a], a - call FindDuplicatePokemonCards - jr c, .set_carry -.no_carry - or a - ret -.set_carry - scf - ret - -AIDecide_PokemonTrader_SoundOfTheWaves: ; 21f85 (8:5f85) -; for each of the following cards, -; first run a check if there's a pre-evolution in -; Play Area or in the hand. If there is, choose it as target. -; otherwise, check if the evolution card is in -; hand and if so, choose it as target instead. - ld b, SEEL - ld a, DEWGONG - call LookForCardIDInDeck_GivenCardIDInHandAndPlayArea - jr c, .choose_hand - ld a, SEEL - ld b, DEWGONG - call LookForCardIDInDeck_GivenCardIDInHand - jr c, .choose_hand - ld b, KRABBY - ld a, KINGLER - call LookForCardIDInDeck_GivenCardIDInHandAndPlayArea - jr c, .choose_hand - ld a, KRABBY - ld b, KINGLER - call LookForCardIDInDeck_GivenCardIDInHand - jr c, .choose_hand - ld b, SHELLDER - ld a, CLOYSTER - call LookForCardIDInDeck_GivenCardIDInHandAndPlayArea - jr c, .choose_hand - ld a, SHELLDER - ld b, CLOYSTER - call LookForCardIDInDeck_GivenCardIDInHand - jr c, .choose_hand - ld b, HORSEA - ld a, SEADRA - call LookForCardIDInDeck_GivenCardIDInHandAndPlayArea - jr c, .choose_hand - ld a, HORSEA - ld b, SEADRA - call LookForCardIDInDeck_GivenCardIDInHand - jr c, .choose_hand - ld b, TENTACOOL - ld a, TENTACRUEL - call LookForCardIDInDeck_GivenCardIDInHandAndPlayArea - jr c, .choose_hand - ld a, TENTACOOL - ld b, TENTACRUEL - call LookForCardIDInDeck_GivenCardIDInHand - jr c, .choose_hand - jr .no_carry - -; card was found as target in deck, -; look for card in hand to trade with -.choose_hand - ld [wce1a], a - ld a, SEEL - call CheckIfHasCardIDInHand - jr c, .set_carry - ld a, KRABBY - call CheckIfHasCardIDInHand - jr c, .set_carry - ld a, HORSEA - call CheckIfHasCardIDInHand - jr c, .set_carry - ld a, SHELLDER - call CheckIfHasCardIDInHand - jr c, .set_carry - ld a, TENTACOOL - call CheckIfHasCardIDInHand - jr c, .set_carry - ; none found - -.no_carry - or a - ret -.set_carry - scf - ret - -AIDecide_PokemonTrader_PowerGenerator: ; 2200b (8:600b) -; for each of the following cards, -; first run a check if there's a pre-evolution in -; Play Area or in the hand. If there is, choose it as target. -; otherwise, check if the evolution card is in -; hand and if so, choose it as target instead. - ld b, PIKACHU2 - ld a, RAICHU1 - call LookForCardIDInDeck_GivenCardIDInHandAndPlayArea - jp c, .find_duplicates - ld b, PIKACHU1 - ld a, RAICHU1 - call LookForCardIDInDeck_GivenCardIDInHandAndPlayArea - jr c, .find_duplicates - ld a, PIKACHU2 - ld b, RAICHU1 - call LookForCardIDInDeck_GivenCardIDInHand - jr c, .find_duplicates - ld a, PIKACHU1 - ld b, RAICHU1 - call LookForCardIDInDeck_GivenCardIDInHand - jr c, .find_duplicates - ld b, VOLTORB - ld a, ELECTRODE2 - call LookForCardIDInDeck_GivenCardIDInHandAndPlayArea - jr c, .find_duplicates - ld b, VOLTORB - ld a, ELECTRODE1 - call LookForCardIDInDeck_GivenCardIDInHandAndPlayArea - jr c, .find_duplicates - ld a, VOLTORB - ld b, ELECTRODE2 - call LookForCardIDInDeck_GivenCardIDInHand - jr c, .find_duplicates - ld a, VOLTORB - ld b, ELECTRODE1 - call LookForCardIDInDeck_GivenCardIDInHand - jr c, .find_duplicates - ld b, MAGNEMITE1 - ld a, MAGNETON2 - call LookForCardIDInDeck_GivenCardIDInHandAndPlayArea - jr c, .find_duplicates - ld b, MAGNEMITE2 - ld a, MAGNETON2 - call LookForCardIDInDeck_GivenCardIDInHandAndPlayArea - jr c, .find_duplicates - ld b, MAGNEMITE1 - ld a, MAGNETON1 - call LookForCardIDInDeck_GivenCardIDInHandAndPlayArea - jr c, .find_duplicates - ld b, MAGNEMITE2 - ld a, MAGNETON1 - call LookForCardIDInDeck_GivenCardIDInHandAndPlayArea - jr c, .find_duplicates - ld a, MAGNEMITE2 - ld b, MAGNETON2 - call LookForCardIDInDeck_GivenCardIDInHand - jr c, .find_duplicates - ld a, MAGNEMITE1 - ld b, MAGNETON2 - call LookForCardIDInDeck_GivenCardIDInHand - jr c, .find_duplicates - ld a, MAGNEMITE2 - ld b, MAGNETON1 - call LookForCardIDInDeck_GivenCardIDInHand - jr c, .find_duplicates - ld a, MAGNEMITE1 - ld b, MAGNETON1 - call LookForCardIDInDeck_GivenCardIDInHand - jr c, .find_duplicates - ; bug, missing jr .no_carry - -; since this last check falls through regardless of result, -; register a might hold an invalid deck index, -; which might lead to hilarious results like Brandon -; trading a Pikachu with a Grass Energy from the deck. -; however, since it's deep in a tower of conditionals, -; reaching here is extremely unlikely. - -; a card in deck was found to look for, -; check if there are duplicates in hand to trade with. -.find_duplicates - ld [wce1a], a - call FindDuplicatePokemonCards - jr c, .set_carry - or a - ret -.set_carry - scf - ret - -AIDecide_PokemonTrader_FlowerGarden: ; 220a8 (8:60a8) -; for each of the following cards, -; first run a check if there's a pre-evolution in -; Play Area or in the hand. If there is, choose it as target. -; otherwise, check if the evolution card is in -; hand and if so, choose it as target instead. - ld b, BULBASAUR - ld a, IVYSAUR - call LookForCardIDInDeck_GivenCardIDInHandAndPlayArea - jr c, .find_duplicates - ld b, IVYSAUR - ld a, VENUSAUR2 - call LookForCardIDInDeck_GivenCardIDInHandAndPlayArea - jr c, .find_duplicates - ld a, BULBASAUR - ld b, IVYSAUR - call LookForCardIDInDeck_GivenCardIDInHand - jr c, .find_duplicates - ld a, IVYSAUR - ld b, VENUSAUR2 - call LookForCardIDInDeck_GivenCardIDInHand - jr c, .find_duplicates - ld b, BELLSPROUT - ld a, WEEPINBELL - call LookForCardIDInDeck_GivenCardIDInHandAndPlayArea - jr c, .find_duplicates - ld b, WEEPINBELL - ld a, VICTREEBEL - call LookForCardIDInDeck_GivenCardIDInHandAndPlayArea - jr c, .find_duplicates - ld a, BELLSPROUT - ld b, WEEPINBELL - call LookForCardIDInDeck_GivenCardIDInHand - jr c, .find_duplicates - ld a, WEEPINBELL - ld b, VICTREEBEL - call LookForCardIDInDeck_GivenCardIDInHand - jr c, .find_duplicates - ld b, ODDISH - ld a, GLOOM - call LookForCardIDInDeck_GivenCardIDInHandAndPlayArea - jr c, .find_duplicates - ld b, GLOOM - ld a, VILEPLUME - call LookForCardIDInDeck_GivenCardIDInHandAndPlayArea - jr c, .find_duplicates - ld a, ODDISH - ld b, GLOOM - call LookForCardIDInDeck_GivenCardIDInHand - jr c, .find_duplicates - ld a, GLOOM - ld b, VILEPLUME - call LookForCardIDInDeck_GivenCardIDInHand - jr c, .find_duplicates - jr .no_carry - -; a card in deck was found to look for, -; check if there are duplicates in hand to trade with. -.find_duplicates - ld [wce1a], a - call FindDuplicatePokemonCards - jr c, .found -.no_carry - or a - ret -.found - scf - ret - -AIDecide_PokemonTrader_StrangePower: ; 22122 (8:6122) -; looks for a Pokemon in hand to trade with Mr Mime in deck. -; inputting Mr Mime in register e for the function is redundant -; since it already checks whether a Mr Mime exists in the hand. - ld a, MR_MIME - ld e, MR_MIME - call LookForCardIDToTradeWithDifferentHandCard - jr nc, .no_carry -; found - ld [wce1a], a - ld a, e - scf - ret -.no_carry - or a - ret - -AIDecide_PokemonTrader_Flamethrower: ; 22133 (8:6133) -; for each of the following cards, -; first run a check if there's a pre-evolution in -; Play Area or in the hand. If there is, choose it as target. -; otherwise, check if the evolution card is in -; hand and if so, choose it as target instead. - ld b, CHARMANDER - ld a, CHARMELEON - call LookForCardIDInDeck_GivenCardIDInHandAndPlayArea - jr c, .find_duplicates - ld b, CHARMELEON - ld a, CHARIZARD - call LookForCardIDInDeck_GivenCardIDInHandAndPlayArea - jr c, .find_duplicates - ld a, CHARMANDER - ld b, CHARMELEON - call LookForCardIDInDeck_GivenCardIDInHand - jr c, .find_duplicates - ld a, CHARMELEON - ld b, CHARIZARD - call LookForCardIDInDeck_GivenCardIDInHand - jr c, .find_duplicates - ld b, VULPIX - ld a, NINETALES1 - call LookForCardIDInDeck_GivenCardIDInHandAndPlayArea - jr c, .find_duplicates - ld a, VULPIX - ld b, NINETALES1 - call LookForCardIDInDeck_GivenCardIDInHand - jr c, .find_duplicates - ld b, GROWLITHE - ld a, ARCANINE2 - call LookForCardIDInDeck_GivenCardIDInHandAndPlayArea - jr c, .find_duplicates - ld a, GROWLITHE - ld b, ARCANINE2 - call LookForCardIDInDeck_GivenCardIDInHand - jr c, .find_duplicates - ld b, EEVEE - ld a, FLAREON2 - call LookForCardIDInDeck_GivenCardIDInHandAndPlayArea - jr c, .find_duplicates - ld a, EEVEE - ld b, FLAREON2 - call LookForCardIDInDeck_GivenCardIDInHand - jr c, .find_duplicates - jr .no_carry - -; a card in deck was found to look for, -; check if there are duplicates in hand to trade with. -.find_duplicates - ld [wce1a], a - call FindDuplicatePokemonCards - jr c, .set_carry -.no_carry - or a - ret -.set_carry - scf - ret diff --git a/src/engine/duel/ai/attacks.asm b/src/engine/duel/ai/attacks.asm new file mode 100644 index 0000000..69ae2e1 --- /dev/null +++ b/src/engine/duel/ai/attacks.asm @@ -0,0 +1,721 @@ +; have AI choose an attack to use, but do not execute it. +; return carry if an attack is chosen. +AIProcessButDontUseAttack: ; 169ca (5:69ca) + ld a, $01 + ld [wAIExecuteProcessedAttack], a + +; backup wPlayAreaAIScore in wTempPlayAreaAIScore. + ld de, wTempPlayAreaAIScore + ld hl, wPlayAreaAIScore + ld b, MAX_PLAY_AREA_POKEMON +.loop + ld a, [hli] + ld [de], a + inc de + dec b + jr nz, .loop + +; copies wAIScore to wTempAIScore + ld a, [wAIScore] + ld [de], a + jr AIProcessAttacks + +; copies wTempPlayAreaAIScore to wPlayAreaAIScore +; and loads wAIScore with value in wTempAIScore. +; identical to RetrievePlayAreaAIScoreFromBackup1. +RetrievePlayAreaAIScoreFromBackup2: ; 169e3 (5:69e3) + push af + ld de, wPlayAreaAIScore + ld hl, wTempPlayAreaAIScore + ld b, MAX_PLAY_AREA_POKEMON +.loop + ld a, [hli] + ld [de], a + inc de + dec b + jr nz, .loop + + ld a, [hl] + ld [wAIScore], a + pop af + ret + +; have AI choose and execute an attack. +; return carry if an attack was chosen and attempted. +AIProcessAndTryToUseAttack: ; 169f8 (5:69f8) + xor a + ld [wAIExecuteProcessedAttack], a + ; fallthrough + +; checks which of the Active card's attacks for AI to use. +; If any of the attacks has enough AI score to be used, +; AI will use it if wAIExecuteProcessedAttack is 0. +; in either case, return carry if an attack is chosen to be used. +AIProcessAttacks: ; 169fc (5:69fc) +; if AI used Pluspower, load its attack index + ld a, [wPreviousAIFlags] + and AI_FLAG_USED_PLUSPOWER + jr z, .no_pluspower + ld a, [wAIPluspowerAttack] + ld [wSelectedAttack], a + jr .attack_chosen + +.no_pluspower +; if Player is running Mewtwo1 mill deck, +; skip attack if Barrier counter is 0. + ld a, [wAIBarrierFlagCounter] + cp AI_MEWTWO_MILL + 0 + jp z, .dont_attack + +; determine AI score of both attacks. + xor a ; FIRST_ATTACK_OR_PKMN_POWER + call GetAIScoreOfAttack + ld a, [wAIScore] + ld [wFirstAttackAIScore], a + ld a, SECOND_ATTACK + call GetAIScoreOfAttack + +; compare both attack scores + ld c, SECOND_ATTACK + ld a, [wFirstAttackAIScore] + ld b, a + ld a, [wAIScore] + cp b + jr nc, .check_score + ; first attack has higher score + dec c + ld a, b + +; c holds the attack index chosen by AI, +; and a holds its AI score. +; first check if chosen attack has at least minimum score. +; then check if first attack is better than second attack +; in case the second one was chosen. +.check_score + cp $50 ; minimum score to use attack + jr c, .dont_attack + ; enough score, proceed + + ld a, c + ld [wSelectedAttack], a + or a + jr z, .attack_chosen + call CheckWhetherToSwitchToFirstAttack + +.attack_chosen +; check whether to execute the attack chosen + ld a, [wAIExecuteProcessedAttack] + or a + jr z, .execute + +; set carry and reset Play Area AI score +; to the previous values. + scf + jp RetrievePlayAreaAIScoreFromBackup2 + +.execute + ld a, AI_TRAINER_CARD_PHASE_14 + call AIProcessHandTrainerCards + +; load this attack's damage output against +; the current Defending Pokemon. + xor a ; PLAY_AREA_ARENA + ldh [hTempPlayAreaLocation_ff9d], a + ld a, [wSelectedAttack] + call EstimateDamage_VersusDefendingCard + ld a, [wDamage] + + or a + jr z, .check_damage_bench + ; if damage is not 0, fallthrough + +.can_damage + xor a + ld [wcdb4], a + jr .use_attack + +.check_damage_bench +; check if it can otherwise damage player's bench + ld a, ATTACK_FLAG1_ADDRESS | DAMAGE_TO_OPPONENT_BENCH_F + call CheckLoadedAttackFlag + jr c, .can_damage + +; cannot damage either Defending Pokemon or Bench + ld hl, wcdb4 + inc [hl] + +; return carry if attack is chosen +; and AI tries to use it. +.use_attack + ld a, TRUE + ld [wAITriedAttack], a + call AITryUseAttack + scf + ret + +.dont_attack + ld a, [wAIExecuteProcessedAttack] + or a + jr z, .failed_to_use + +; reset Play Area AI score +; to the previous values. + jp RetrievePlayAreaAIScoreFromBackup2 + +; return no carry if no viable attack. +.failed_to_use + ld hl, wcdb4 + inc [hl] + or a + ret + +; determines the AI score of attack index in a +; of card in Play Area location hTempPlayAreaLocation_ff9d. +GetAIScoreOfAttack: ; 16a86 (5:6a86) +; initialize AI score. + ld [wSelectedAttack], a + ld a, $50 + ld [wAIScore], a + + xor a + ldh [hTempPlayAreaLocation_ff9d], a + call CheckIfSelectedAttackIsUnusable + jr nc, .usable + +; return zero AI score. +.unusable + xor a + ld [wAIScore], a + jp .done + +; load arena card IDs +.usable + xor a + ld [wAICannotDamage], a + ld a, DUELVARS_ARENA_CARD + call GetTurnDuelistVariable + call GetCardIDFromDeckIndex + ld a, e + ld [wTempTurnDuelistCardID], a + call SwapTurn + ld a, DUELVARS_ARENA_CARD + call GetTurnDuelistVariable + call GetCardIDFromDeckIndex + ld a, e + ld [wTempNonTurnDuelistCardID], a + +; handle the case where the player has No Damage substatus. +; in the case the player does, check if this attack +; has a residual effect, or if it can damage the opposing bench. +; If none of those are true, render the attack unusable. +; also if it's a PKMN power, consider it unusable as well. + bank1call HandleNoDamageOrEffectSubstatus + call SwapTurn + jr nc, .check_if_can_ko + + ; player is under No Damage substatus + ld a, $01 + ld [wAICannotDamage], a + ld a, [wSelectedAttack] + call EstimateDamage_VersusDefendingCard + ld a, [wLoadedAttackCategory] + cp POKEMON_POWER + jr z, .unusable + and RESIDUAL + jr nz, .check_if_can_ko + ld a, ATTACK_FLAG1_ADDRESS | DAMAGE_TO_OPPONENT_BENCH_F + call CheckLoadedAttackFlag + jr nc, .unusable + +; calculate damage to player to check if attack can KO. +; encourage attack if it's able to KO. +.check_if_can_ko + ld a, [wSelectedAttack] + call EstimateDamage_VersusDefendingCard + ld a, DUELVARS_ARENA_CARD_HP + call GetNonTurnDuelistVariable + ld hl, wDamage + sub [hl] + jr c, .can_ko + jr z, .can_ko + jr .check_damage +.can_ko + ld a, 20 + call AddToAIScore + +; raise AI score by the number of damage counters that this attack deals. +; if no damage is dealt, subtract AI score. in case wDamage is zero +; but wMaxDamage is not, then encourage attack afterwards. +; otherwise, if wMaxDamage is also zero, check for damage against +; player's bench, and encourage attack in case there is. +.check_damage + xor a + ld [wAIAttackIsNonDamaging], a + ld a, [wDamage] + ld [wTempAI], a + or a + jr z, .no_damage + call CalculateByteTensDigit + call AddToAIScore + jr .check_recoil +.no_damage + ld a, $01 + ld [wAIAttackIsNonDamaging], a + call SubFromAIScore + ld a, [wAIMaxDamage] + or a + jr z, .no_max_damage + ld a, 2 + call AddToAIScore + xor a + ld [wAIAttackIsNonDamaging], a +.no_max_damage + ld a, ATTACK_FLAG1_ADDRESS | DAMAGE_TO_OPPONENT_BENCH_F + call CheckLoadedAttackFlag + jr nc, .check_recoil + ld a, 2 + call AddToAIScore + +; handle recoil attacks (low and high recoil). +.check_recoil + ld a, ATTACK_FLAG1_ADDRESS | LOW_RECOIL_F + call CheckLoadedAttackFlag + jr c, .is_recoil + ld a, ATTACK_FLAG1_ADDRESS | HIGH_RECOIL_F + call CheckLoadedAttackFlag + jp nc, .check_defending_can_ko +.is_recoil + ; sub from AI score number of damage counters + ; that attack deals to itself. + ld a, [wLoadedAttackEffectParam] + or a + jp z, .check_defending_can_ko + ld [wDamage], a + call ApplyDamageModifiers_DamageToSelf + ld a, e + call CalculateByteTensDigit + call SubFromAIScore + + push de + ld a, ATTACK_FLAG1_ADDRESS | HIGH_RECOIL_F + call CheckLoadedAttackFlag + pop de + jr c, .high_recoil + + ; if LOW_RECOIL KOs self, decrease AI score + ld a, DUELVARS_ARENA_CARD_HP + call GetTurnDuelistVariable + cp e + jr c, .kos_self + jp nz, .check_defending_can_ko +.kos_self + ld a, 10 + call SubFromAIScore + +.high_recoil + ; dismiss this attack if no benched Pokémon + ld a, DUELVARS_NUMBER_OF_POKEMON_IN_PLAY_AREA + call GetTurnDuelistVariable + cp 2 + jr c, .dismiss_high_recoil_atk + ; has benched Pokémon + +; here the AI handles high recoil attacks differently +; depending on what deck it's playing. + ld a, [wOpponentDeckID] + cp ROCK_CRUSHER_DECK_ID + jr z, .rock_crusher_deck + cp ZAPPING_SELFDESTRUCT_DECK_ID + jr z, .zapping_selfdestruct_deck + cp BOOM_BOOM_SELFDESTRUCT_DECK_ID + jr z, .encourage_high_recoil_atk + ; Boom Boom Selfdestruct deck always encourages + cp POWER_GENERATOR_DECK_ID + jr nz, .high_recoil_generic_checks + ; Power Generator deck always dismisses + +.dismiss_high_recoil_atk + xor a + ld [wAIScore], a + jp .done + +.encourage_high_recoil_atk + ld a, 20 + call AddToAIScore + jp .done + +; Zapping Selfdestruct deck only uses this attack +; if number of cards in deck >= 30 and +; HP of active card is < half max HP. +.zapping_selfdestruct_deck + ld a, DUELVARS_NUMBER_OF_CARDS_NOT_IN_DECK + call GetTurnDuelistVariable + cp 31 + jr nc, .high_recoil_generic_checks + ld e, PLAY_AREA_ARENA + call GetCardDamageAndMaxHP + sla a + cp c + jr c, .high_recoil_generic_checks + ld b, 0 + ld a, DUELVARS_ARENA_CARD + call GetTurnDuelistVariable + call GetCardIDFromDeckIndex + ld a, e + cp MAGNEMITE1 + jr z, .magnemite1 + ld b, 10 ; bench damage +.magnemite1 + ld a, 10 + add b + ld b, a ; 20 bench damage if not Magnemite1 + +; if this attack causes player to win the duel by +; knocking out own Pokémon, dismiss attack. + ld a, 1 ; count active Pokémon as KO'd + call .check_if_kos_bench + jr c, .dismiss_high_recoil_atk + jr .encourage_high_recoil_atk + +; Rock Crusher Deck only uses this attack if +; prize count is below 4 and attack wins (or potentially draws) the duel, +; (i.e. at least gets KOs equal to prize cards left). +.rock_crusher_deck + call CountPrizes + cp 4 + jr nc, .dismiss_high_recoil_atk + ; prize count < 4 + ld b, 20 ; damage dealt to bench + call SwapTurn + xor a + call .check_if_kos_bench + call SwapTurn + jr c, .encourage_high_recoil_atk + +; generic checks for all other deck IDs. +; encourage attack if it wins (or potentially draws) the duel, +; (i.e. at least gets KOs equal to prize cards left). +; dismiss it if it causes the player to win. +.high_recoil_generic_checks + ld a, DUELVARS_ARENA_CARD + call GetTurnDuelistVariable + call GetCardIDFromDeckIndex + ld a, e + cp CHANSEY + jr z, .chansey + cp MAGNEMITE1 + jr z, .magnemite1_or_weezing + cp WEEZING + jr z, .magnemite1_or_weezing + ld b, 20 ; bench damage + jr .check_bench_kos +.magnemite1_or_weezing + ld b, 10 ; bench damage + jr .check_bench_kos +.chansey + ld b, 0 ; no bench damage + +.check_bench_kos + push bc + call SwapTurn + xor a + call .check_if_kos_bench + call SwapTurn + pop bc + jr c, .wins_the_duel + push de + ld a, 1 + call .check_if_kos_bench + pop bc + jr nc, .count_own_ko_bench + +; attack causes player to draw all prize cards + xor a + ld [wAIScore], a + jp .done + +; attack causes CPU to draw all prize cards +.wins_the_duel + ld a, 20 + call AddToAIScore + jp .done + +; subtract from AI score number of own benched Pokémon KO'd +.count_own_ko_bench + push bc + ld a, d + or a + jr z, .count_player_ko_bench + dec a + call SubFromAIScore + +; add to AI score number of player benched Pokémon KO'd +.count_player_ko_bench + pop bc + ld a, b + call AddToAIScore + jr .check_defending_can_ko + +; local function that gets called to determine damage to +; benched Pokémon caused by a HIGH_RECOIL attack. +; return carry if using attack causes number of benched Pokémon KOs +; equal to or larger than remaining prize cards. +; this function is independent on duelist turn, so whatever +; turn it is when this is called, it's that duelist's +; bench/prize cards that get checked. +; input: +; a = initial number of KO's beside benched Pokémon, +; so that if the active Pokémon is KO'd by the attack, +; this counts towards the prize cards collected +; b = damage dealt to bench Pokémon +.check_if_kos_bench + ld d, a + ld a, DUELVARS_BENCH + call GetTurnDuelistVariable + ld e, PLAY_AREA_ARENA +.loop + inc e + ld a, [hli] + cp $ff + jr z, .exit_loop + ld a, e + add DUELVARS_ARENA_CARD_HP + push hl + call GetTurnDuelistVariable + pop hl + cp b + jr z, .increase_count + jr nc, .loop +.increase_count + ; increase d if damage dealt KOs + inc d + jr .loop +.exit_loop + push de + call SwapTurn + call CountPrizes + call SwapTurn + pop de + cp d + jp c, .set_carry + jp z, .set_carry + or a + ret +.set_carry + scf + ret + +; if defending card can KO, encourage attack +; unless attack is non-damaging. +.check_defending_can_ko + ld a, [wSelectedAttack] + push af + call CheckIfDefendingPokemonCanKnockOut + pop bc + ld a, b + ld [wSelectedAttack], a + jr nc, .check_discard + ld a, 5 + call AddToAIScore + ld a, [wAIAttackIsNonDamaging] + or a + jr z, .check_discard + ld a, 5 + call SubFromAIScore + +; subtract from AI score if this attack requires +; discarding any energy cards. +.check_discard + ld a, [wSelectedAttack] + ld e, a + ld a, DUELVARS_ARENA_CARD + call GetTurnDuelistVariable + ld d, a + call CopyAttackDataAndDamage_FromDeckIndex + ld a, ATTACK_FLAG2_ADDRESS | DISCARD_ENERGY_F + call CheckLoadedAttackFlag + jr nc, .asm_16ca6 + ld a, 1 + call SubFromAIScore + ld a, [wLoadedAttackEffectParam] + call SubFromAIScore + +.asm_16ca6 + ld a, ATTACK_FLAG2_ADDRESS | FLAG_2_BIT_6_F + call CheckLoadedAttackFlag + jr nc, .check_nullify_flag + ld a, [wLoadedAttackEffectParam] + call AddToAIScore + +; encourage attack if it has a nullify or weaken attack effect. +.check_nullify_flag + ld a, ATTACK_FLAG2_ADDRESS | NULLIFY_OR_WEAKEN_ATTACK_F + call CheckLoadedAttackFlag + jr nc, .check_draw_flag + ld a, 1 + call AddToAIScore + +; encourage attack if it has an effect to draw a card. +.check_draw_flag + ld a, ATTACK_FLAG1_ADDRESS | DRAW_CARD_F + call CheckLoadedAttackFlag + jr nc, .check_heal_flag + ld a, 1 + call AddToAIScore + +.check_heal_flag + ld a, ATTACK_FLAG2_ADDRESS | HEAL_USER_F + call CheckLoadedAttackFlag + jr nc, .check_status_effect + ld a, [wLoadedAttackEffectParam] + cp 1 + jr z, .tally_heal_score + ld a, [wTempAI] + call CalculateByteTensDigit + ld b, a + ld a, [wLoadedAttackEffectParam] + cp 3 + jr z, .asm_16cec + srl b + jr nc, .asm_16cec + inc b +.asm_16cec + ld a, DUELVARS_ARENA_CARD_HP + call GetTurnDuelistVariable + call CalculateByteTensDigit + cp b + jr c, .tally_heal_score + ld a, b +.tally_heal_score + push af + ld e, PLAY_AREA_ARENA + call GetCardDamageAndMaxHP + call CalculateByteTensDigit + pop bc + cp b ; wLoadedAttackEffectParam + jr c, .add_heal_score + ld a, b +.add_heal_score + call AddToAIScore + +.check_status_effect + ld a, DUELVARS_ARENA_CARD + call GetNonTurnDuelistVariable + call SwapTurn + call GetCardIDFromDeckIndex + call SwapTurn + ld a, e + ; skip if player has Snorlax + cp SNORLAX + jp z, .handle_special_atks + + ld a, DUELVARS_ARENA_CARD_STATUS + call GetNonTurnDuelistVariable + ld [wTempAI], a + +; encourage a poison inflicting attack if opposing Pokémon +; isn't (doubly) poisoned already. +; if opposing Pokémon is only poisoned and not double poisoned, +; and this attack has FLAG_2_BIT_6 set, discourage it +; (possibly to make Nidoking's Toxic attack less likely to be chosen +; if the other Pokémon is poisoned.) + ld a, ATTACK_FLAG1_ADDRESS | INFLICT_POISON_F + call CheckLoadedAttackFlag + jr nc, .check_sleep + ld a, [wTempAI] + and DOUBLE_POISONED + jr z, .add_poison_score + and $40 ; only double poisoned? + jr z, .check_sleep + ld a, ATTACK_FLAG2_ADDRESS | FLAG_2_BIT_6_F + call CheckLoadedAttackFlag + jr nc, .check_sleep + ld a, 2 + call SubFromAIScore + jr .check_sleep +.add_poison_score + ld a, 2 + call AddToAIScore + +; encourage sleep-inducing attack if other Pokémon isn't asleep. +.check_sleep + ld a, ATTACK_FLAG1_ADDRESS | INFLICT_SLEEP_F + call CheckLoadedAttackFlag + jr nc, .check_paralysis + ld a, [wTempAI] + and CNF_SLP_PRZ + cp ASLEEP + jr z, .check_paralysis + ld a, 1 + call AddToAIScore + +; encourage paralysis-inducing attack if other Pokémon isn't asleep. +; otherwise, if other Pokémon is asleep, discourage attack. +.check_paralysis + ld a, ATTACK_FLAG1_ADDRESS | INFLICT_PARALYSIS_F + call CheckLoadedAttackFlag + jr nc, .check_confusion + ld a, [wTempAI] + and CNF_SLP_PRZ + cp ASLEEP + jr z, .sub_prz_score + ld a, 1 + call AddToAIScore + jr .check_confusion +.sub_prz_score + ld a, 1 + call SubFromAIScore + +; encourage confuse-inducing attack if other Pokémon isn't asleep +; or confused already. +; otherwise, if other Pokémon is asleep or confused, +; discourage attack instead. +.check_confusion + ld a, ATTACK_FLAG1_ADDRESS | INFLICT_CONFUSION_F + call CheckLoadedAttackFlag + jr nc, .check_if_confused + ld a, [wTempAI] + and CNF_SLP_PRZ + cp ASLEEP + jr z, .sub_cnf_score + ld a, [wTempAI] + and CNF_SLP_PRZ + cp CONFUSED + jr z, .check_if_confused + ld a, 1 + call AddToAIScore + jr .check_if_confused +.sub_cnf_score + ld a, 1 + call SubFromAIScore + +; if this Pokémon is confused, subtract from score. +.check_if_confused + ld a, DUELVARS_ARENA_CARD_STATUS + call GetTurnDuelistVariable + and CNF_SLP_PRZ + cp CONFUSED + jr nz, .handle_special_atks + ld a, 1 + call SubFromAIScore + +; SPECIAL_AI_HANDLING marks attacks that the AI handles individually. +; each attack has its own checks and modifies AI score accordingly. +.handle_special_atks + ld a, ATTACK_FLAG3_ADDRESS | SPECIAL_AI_HANDLING_F + call CheckLoadedAttackFlag + jr nc, .done + call HandleSpecialAIAttacks + cp $80 + jr c, .negative_score + sub $80 + call AddToAIScore + jr .done +.negative_score + ld b, a + ld a, $80 + sub b + call SubFromAIScore + +.done + ret diff --git a/src/engine/duel/ai/boss_deck_set_up.asm b/src/engine/duel/ai/boss_deck_set_up.asm new file mode 100644 index 0000000..ebcd2ea --- /dev/null +++ b/src/engine/duel/ai/boss_deck_set_up.asm @@ -0,0 +1,167 @@ +; sets up the initial hand of boss deck. +; always draws at least 2 Basic Pokemon cards and 2 Energy cards. +; also sets up so that the next cards to be drawn have +; some minimum number of Basic Pokemon and Energy cards. +SetUpBossStartingHandAndDeck: ; 172af (5:72af) +; shuffle all hand cards in deck + ld a, DUELVARS_HAND + call GetTurnDuelistVariable + ld b, STARTING_HAND_SIZE +.loop_hand + ld a, [hl] + call RemoveCardFromHand + call ReturnCardToDeck + dec b + jr nz, .loop_hand + jr .count_energy_basic + +.shuffle_deck + call ShuffleDeck + +; count number of Energy and basic Pokemon cards +; in the first STARTING_HAND_SIZE in deck. +.count_energy_basic + xor a + ld [wce06], a + ld [wce08], a + + ld a, DUELVARS_DECK_CARDS + call GetTurnDuelistVariable + ld b, STARTING_HAND_SIZE +.loop_deck_1 + ld a, [hli] + push bc + call LoadCardDataToBuffer1_FromDeckIndex + pop bc + ld a, [wLoadedCard1Type] + cp TYPE_ENERGY + jr c, .pokemon_card_1 + cp TYPE_TRAINER + jr z, .next_card_deck_1 + +; energy card + ld a, [wce08] + inc a + ld [wce08], a + jr .next_card_deck_1 + +.pokemon_card_1 + ld a, [wLoadedCard1Stage] + or a + jr nz, .next_card_deck_1 ; not basic + ld a, [wce06] + inc a + ld [wce06], a + +.next_card_deck_1 + dec b + jr nz, .loop_deck_1 + +; tally the number of Energy and basic Pokemon cards +; and if any of them is smaller than 2, re-shuffle deck. + ld a, [wce06] + cp 2 + jr c, .shuffle_deck + ld a, [wce08] + cp 2 + jr c, .shuffle_deck + +; now check the following 6 cards (prize cards). +; re-shuffle deck if any of these cards is listed in wAICardListAvoidPrize. + ld b, 6 +.check_card_ids + ld a, [hli] + push bc + call .CheckIfIDIsInList + pop bc + jr c, .shuffle_deck + dec b + jr nz, .check_card_ids + +; finally, check 6 cards after that. +; if Energy or Basic Pokemon counter is below 4 +; (counting with the ones found in the initial hand) +; then re-shuffle deck. + ld b, 6 +.loop_deck_2 + ld a, [hli] + push bc + call LoadCardDataToBuffer1_FromDeckIndex + pop bc + ld a, [wLoadedCard1Type] + cp TYPE_ENERGY + jr c, .pokemon_card_2 + cp TYPE_TRAINER + jr z, .next_card_deck_2 + +; energy card + ld a, [wce08] + inc a + ld [wce08], a + jr .next_card_deck_2 + +.pokemon_card_2 + ld a, [wLoadedCard1Stage] + or a + jr nz, .next_card_deck_2 + ld a, [wce06] + inc a + ld [wce06], a + +.next_card_deck_2 + dec b + jr nz, .loop_deck_2 + + ld a, [wce06] + cp 4 + jp c, .shuffle_deck + ld a, [wce08] + cp 4 + jp c, .shuffle_deck + +; draw new set of hand cards + ld a, DUELVARS_DECK_CARDS + call GetTurnDuelistVariable + ld b, STARTING_HAND_SIZE +.draw_loop + ld a, [hli] + call SearchCardInDeckAndAddToHand + call AddCardToHand + dec b + jr nz, .draw_loop + ret + +; expectation: return carry if card ID corresponding +; to the input deck index is listed in wAICardListAvoidPrize; +; reality: always returns no carry because when checking terminating +; byte in wAICardListAvoidPrize ($00), it wrongfully uses 'cp a' instead of 'or a', +; so it always ends up returning in the first item in list. +; input: +; - a = deck index of card to check +.CheckIfIDIsInList ; 17366 (5:7366) + ld b, a + ld a, [wAICardListAvoidPrize + 1] + or a + ret z ; null + push hl + ld h, a + ld a, [wAICardListAvoidPrize] + ld l, a + + ld a, b + call GetCardIDFromDeckIndex +.loop_id_list + ld a, [hli] + cp a ; bug, should be 'or a' + jr z, .false + cp e + jr nz, .loop_id_list + +; true + pop hl + scf + ret +.false + pop hl + or a + ret diff --git a/src/engine/duel/ai/common.asm b/src/engine/duel/ai/common.asm new file mode 100644 index 0000000..d4f1da4 --- /dev/null +++ b/src/engine/duel/ai/common.asm @@ -0,0 +1,970 @@ +; runs through Player's whole deck and +; sets carry if there's any Pokemon other +; than Mewtwo1. +CheckIfPlayerHasPokemonOtherThanMewtwo1: ; 227a9 (8:67a9) + call SwapTurn + ld e, 0 +.loop_deck + ld a, e + push de + call LoadCardDataToBuffer2_FromDeckIndex + pop de + ld a, [wLoadedCard2Type] + cp TYPE_ENERGY + jp nc, .next ; can be a jr + ld a, [wLoadedCard2ID] + cp MEWTWO1 + jr nz, .not_mewtwo1 +.next + inc e + ld a, DECK_SIZE + cp e + jr nz, .loop_deck + +; no carry + call SwapTurn + or a + ret + +.not_mewtwo1 + call SwapTurn + scf + ret + +; returns no carry if, given the Player is using a Mewtwo1 mill deck, +; the AI already has a Bench fully set up, in which case it +; will process some Trainer cards in hand (namely Energy Removals). +; this is used to check whether to skip some normal AI routines +; this turn and jump right to the attacking phase. +HandleAIAntiMewtwoDeckStrategy: ; 227d3 (8:67d3) +; return carry if Player is not playing Mewtwo1 mill deck + ld a, [wAIBarrierFlagCounter] + bit AI_MEWTWO_MILL_F, a + jr z, .set_carry + +; else, check if there's been less than 2 turns +; without the Player using Barrier. + cp AI_MEWTWO_MILL + 2 + jr c, .count_bench + +; if there has been, reset wAIBarrierFlagCounter +; and return carry. + xor a + ld [wAIBarrierFlagCounter], a + jr .set_carry + +; else, check number of Pokemon that are set up in Bench +; if less than 4, return carry. +.count_bench + farcall CountNumberOfSetUpBenchPokemon + cp 4 + jr c, .set_carry + +; if there's at least 4 Pokemon in the Bench set up, +; process Trainer hand cards of AI_TRAINER_CARD_PHASE_05 + ld a, AI_TRAINER_CARD_PHASE_05 + farcall AIProcessHandTrainerCards + or a + ret + +.set_carry + scf + ret + +; lists in wDuelTempList all the basic energy cards +; in card location of a. +; outputs in a number of cards found. +; returns carry if none were found. +; input: +; a = CARD_LOCATION_* to look +; output: +; a = number of cards found +FindBasicEnergyCardsInLocation: ; 227f6 (8:67f6) + ld [wTempAI], a + lb de, 0, 0 + ld hl, wDuelTempList + +; d = number of basic energy cards found +; e = current card in deck +; loop entire deck +.loop + ld a, DUELVARS_CARD_LOCATIONS + add e + push hl + call GetTurnDuelistVariable + ld hl, wTempAI + cp [hl] + pop hl + jr nz, .next_card + +; is in the card location we're looking for + ld a, e + push de + push hl + call GetCardIDFromDeckIndex + pop hl + ld a, e + pop de + cp DOUBLE_COLORLESS_ENERGY + ; only basic energy cards + ; will set carry here + jr nc, .next_card + +; is a basic energy card +; add this card to the TempList + ld a, e + ld [hli], a + inc d +.next_card + inc e + ld a, DECK_SIZE + cp e + jr nz, .loop + +; check if any were found + ld a, d + or a + jr z, .set_carry + +; some were found, add the termination byte on TempList + ld a, $ff + ld [hl], a + ld a, d + ret + +.set_carry + scf + ret + +; returns in a the card index of energy card +; attached to Pokémon in Play Area location a, +; that is to be discarded by the AI for an effect. +; outputs $ff is none was found. +; input: +; a = PLAY_AREA_* constant of card +; output: +; a = deck index of attached energy card chosen +AIPickEnergyCardToDiscard: ; 2282e (8:682e) +; load Pokémon's attached energy cards. + ldh [hTempPlayAreaLocation_ff9d], a + call CreateArenaOrBenchEnergyCardList + ldh a, [hTempPlayAreaLocation_ff9d] + ld e, a + call GetPlayAreaCardAttachedEnergies + ld a, [wTotalAttachedEnergies] + or a + jr z, .no_energy + +; load card's ID and type. + ldh a, [hTempPlayAreaLocation_ff9d] + ld b, a + ld a, DUELVARS_ARENA_CARD + add b + call GetTurnDuelistVariable + call GetCardIDFromDeckIndex + ld a, e + ld [wTempCardID], a + call LoadCardDataToBuffer1_FromCardID + ld a, [wLoadedCard1Type] + or TYPE_ENERGY + ld [wTempCardType], a + +; find a card that is not useful. +; if none is found, just return the first energy card attached. + ld hl, wDuelTempList +.loop + ld a, [hl] + cp $ff + jr z, .not_found + farcall CheckIfEnergyIsUseful + jr nc, .found + inc hl + jr .loop + +.found + ld a, [hl] + ret +.not_found + ld hl, wDuelTempList + ld a, [hl] + ret +.no_energy + ld a, $ff + ret + +; returns in a the deck index of an energy card attached to card +; in player's Play Area location a to remove. +; prioritizes double colorless energy, then any useful energy, +; then defaults to the first energy card attached if neither +; of those are found. +; returns $ff in a if there are no energy cards attached. +; input: +; a = Play Area location to check +; output: +; a = deck index of attached energy card +PickAttachedEnergyCardToRemove: ; 22875 (8:6875) +; construct energy list and check if there are any energy cards attached + ldh [hTempPlayAreaLocation_ff9d], a + call CreateArenaOrBenchEnergyCardList + ldh a, [hTempPlayAreaLocation_ff9d] + ld e, a + call GetPlayAreaCardAttachedEnergies + ld a, [wTotalAttachedEnergies] + or a + jr z, .no_energy + +; load card data and store its type + ldh a, [hTempPlayAreaLocation_ff9d] + ld b, a + ld a, DUELVARS_ARENA_CARD + add b + call GetTurnDuelistVariable + call GetCardIDFromDeckIndex + ld a, e + ld [wTempCardID], a + call LoadCardDataToBuffer1_FromCardID + ld a, [wLoadedCard1Type] + or TYPE_ENERGY + ld [wTempCardType], a + +; first look for any double colorless energy + ld hl, wDuelTempList +.loop_1 + ld a, [hl] + cp $ff + jr z, .check_useful + push hl + call GetCardIDFromDeckIndex + ld a, e + cp DOUBLE_COLORLESS_ENERGY + pop hl + jr z, .found + inc hl + jr .loop_1 + +; then look for any energy cards that are useful +.check_useful + ld hl, wDuelTempList +.loop_2 + ld a, [hl] + cp $ff + jr z, .default + farcall CheckIfEnergyIsUseful + jr c, .found + inc hl + jr .loop_2 + +; return the energy card that was found +.found + ld a, [hl] + ret + +; if none were found with the above criteria, +; just return the first option +.default + ld hl, wDuelTempList + ld a, [hl] + ret + +; return $ff if no energy cards attached +.no_energy + ld a, $ff + ret + +; stores in wTempAI and wCurCardCanAttack the deck indices +; of energy cards attached to card in Play Area location a. +; prioritizes double colorless energy, then any useful energy, +; then defaults to the first two energy cards attached if neither +; of those are found. +; returns $ff in a if there are no energy cards attached. +; input: +; a = Play Area location to check +; output: +; [wTempAI] = deck index of attached energy card +; [wCurCardCanAttack] = deck index of attached energy card +PickTwoAttachedEnergyCards: ; 228d1 (8:68d1) + ldh [hTempPlayAreaLocation_ff9d], a + call CreateArenaOrBenchEnergyCardList + ldh a, [hTempPlayAreaLocation_ff9d] + ld e, a + farcall CountNumberOfEnergyCardsAttached + cp 2 + jp c, .not_enough + +; load card data and store its type + ldh a, [hTempPlayAreaLocation_ff9d] + ld b, a + ld a, DUELVARS_ARENA_CARD + add b + call GetTurnDuelistVariable + call GetCardIDFromDeckIndex + ld a, e + ld [wTempCardID], a + call LoadCardDataToBuffer1_FromCardID + ld a, [wLoadedCard1Type] + or TYPE_ENERGY + ld [wTempCardType], a + ld a, $ff + ld [wTempAI], a + ld [wCurCardCanAttack], a + +; first look for any double colorless energy + ld hl, wDuelTempList +.loop_1 + ld a, [hl] + cp $ff + jr z, .check_useful + push hl + call GetCardIDFromDeckIndex + ld a, e + cp DOUBLE_COLORLESS_ENERGY + pop hl + jr z, .found_double_colorless + inc hl + jr .loop_1 +.found_double_colorless + ld a, [wTempAI] + cp $ff + jr nz, .already_chosen_1 + ld a, [hli] + ld [wTempAI], a + jr .loop_1 +.already_chosen_1 + ld a, [hl] + ld [wCurCardCanAttack], a + jr .done + +; then look for any energy cards that are useful +.check_useful + ld hl, wDuelTempList +.loop_2 + ld a, [hl] + cp $ff + jr z, .default + farcall CheckIfEnergyIsUseful + jr c, .found_useful + inc hl + jr .loop_2 +.found_useful + ld a, [wTempAI] + cp $ff + jr nz, .already_chosen_2 + ld a, [hli] + ld [wTempAI], a + jr .loop_2 +.already_chosen_2 + ld a, [hl] + ld [wCurCardCanAttack], a + jr .done + +; if none were found with the above criteria, +; just return the first 2 options +.default + ld hl, wDuelTempList + ld a, [wTempAI] + cp $ff + jr nz, .pick_one_card + +; pick 2 cards + ld a, [hli] + ld [wTempAI], a + ld a, [hl] + ld [wCurCardCanAttack], a + jr .done +.pick_one_card + ld a, [wTempAI] + ld b, a +.loop_3 + ld a, [hli] + cp b + jr z, .loop_3 ; already picked + ld [wCurCardCanAttack], a + +.done + ld a, [wCurCardCanAttack] + ld b, a + ld a, [wTempAI] + ret + +; return $ff if no energy cards attached +.not_enough + ld a, $ff + ret + +; copies $ff terminated buffer from hl to de +CopyBuffer: ; 2297b (8:697b) + ld a, [hli] + ld [de], a + cp $ff + ret z + inc de + jr CopyBuffer + +; zeroes a bytes starting at hl +ClearMemory_Bank8: ; 22983 (8:6983) + push af + push bc + push hl + ld b, a + xor a +.loop + ld [hli], a + dec b + jr nz, .loop + pop hl + pop bc + pop af + ret + +; counts number of energy cards found in hand +; and outputs result in a +; sets carry if none are found +; output: +; a = number of energy cards found +CountOppEnergyCardsInHand: ; 22990 (8:6990) + farcall CreateEnergyCardListFromHand + ret c + ld b, -1 + ld hl, wDuelTempList +.loop + inc b + ld a, [hli] + cp $ff + jr nz, .loop + ld a, b + or a + ret + +; converts HP in a to number of equivalent damage counters +; input: +; a = HP +; output: +; a = number of damage counters +ConvertHPToCounters: ; 229a3 (8:69a3) + push bc + ld c, 0 +.loop + sub 10 + jr c, .carry + inc c + jr .loop +.carry + ld a, c + pop bc + ret + +; calculates floor(hl / 10) +CalculateWordTensDigit: ; 229b0 (8:69b0) + push bc + push de + lb bc, $ff, -10 + lb de, $ff, -1 +.asm_229b8 + inc de + add hl, bc + jr c, .asm_229b8 + ld h, d + ld l, e + pop de + pop bc + ret + +; returns in a division of b by a +CalculateBDividedByA_Bank8: ; 229c1 (8:69c1) + push bc + ld c, a + ld a, b + ld b, c + ld c, 0 +.loop + sub b + jr c, .done + inc c + jr .loop +.done + ld a, c + pop bc + ret + +; returns in a the deck index of the first +; instance of card with ID equal to the ID in e +; in card location a. +; returns carry if found. +; input: +; a = CARD_LOCATION_* +; e = card ID to look for +LookForCardIDInLocation: ; 229d0 (8:69d0) + ld b, a + ld c, e + lb de, $00, 0 ; d is never used +.loop + ld a, DUELVARS_CARD_LOCATIONS + add e + call GetTurnDuelistVariable + cp b + jr nz, .next + ld a, e + push de + call GetCardIDFromDeckIndex + ld a, e + pop de + cp c + jr z, .found +.next + inc e + ld a, DECK_SIZE + cp e + jr nz, .loop + +; not found + or a + ret +.found + ld a, e + scf + ret + +; return carry if card ID loaded in a is found in hand +; and outputs in a the deck index of that card +; input: +; a = card ID +; output: +; a = card deck index, if found +; carry set if found +LookForCardIDInHandList_Bank8: ; 229f3 (8:69f3) + ld [wTempCardIDToLook], a + call CreateHandCardList + ld hl, wDuelTempList + +.loop + ld a, [hli] + cp $ff + ret z + + ldh [hTempCardIndex_ff98], a + call LoadCardDataToBuffer1_FromDeckIndex + ld b, a + ld a, [wTempCardIDToLook] + cp b + jr nz, .loop + + ldh a, [hTempCardIndex_ff98] + scf + ret + +; searches in deck for card ID 1 in a, and +; if found, searches in Hand/Play Area for card ID 2 in b, and +; if found, searches for card ID 1 in Hand/Play Area, and +; if none found, return carry and output deck index +; of the card ID 1 in deck. +; input: +; a = card ID 1 +; b = card ID 2 +; output: +; a = index of card ID 1 in deck +LookForCardIDInDeck_GivenCardIDInHandAndPlayArea: ; 22a10 (8:6a10) +; store a in wCurCardCanAttack +; and b in wTempAI + ld c, a + ld a, b + ld [wTempAI], a + ld a, c + ld [wCurCardCanAttack], a + +; look for the card ID 1 in deck + ld e, a + ld a, CARD_LOCATION_DECK + call LookForCardIDInLocation + ret nc + +; was found, store its deck index in memory + ld [wTempAIPokemonCard], a + +; look for the card ID 2 +; in Hand and Play Area, return if not found. + ld a, [wTempAI] + call LookForCardIDInHandAndPlayArea + ret nc + +; look for the card ID 1 in the Hand and Play Area +; if any card is found, return no carry. + ld a, [wCurCardCanAttack] + call LookForCardIDInHandAndPlayArea + jr c, .no_carry +; none found + + ld a, [wTempAIPokemonCard] + scf + ret + +.no_carry + or a + ret + +; returns carry if card ID in a +; is found in Play Area or in hand +; input: +; a = card ID +LookForCardIDInHandAndPlayArea: ; 22a39 (8:6a39) + ld b, a + push bc + call LookForCardIDInHandList_Bank8 + pop bc + ret c + + ld a, b + ld b, PLAY_AREA_ARENA + call LookForCardIDInPlayArea_Bank8 + ret c + or a + ret + +; searches in deck for card ID 1 in a, and +; if found, searches in Hand Area for card ID 2 in b, and +; if found, searches for card ID 1 in Hand/Play Area, and +; if none found, return carry and output deck index +; of the card ID 1 in deck. +; input: +; a = card ID 1 +; b = card ID 2 +; output: +; a = index of card ID 1 in deck +LookForCardIDInDeck_GivenCardIDInHand: ; 22a49 (8:6a49) +; store a in wCurCardCanAttack +; and b in wTempAI + ld c, a + ld a, b + ld [wTempAI], a + ld a, c + ld [wCurCardCanAttack], a + +; look for the card ID 1 in deck + ld e, a + ld a, CARD_LOCATION_DECK + call LookForCardIDInLocation + ret nc + +; was found, store its deck index in memory + ld [wTempAIPokemonCard], a + +; look for the card ID 2 in hand, return if not found. + ld a, [wTempAI] + call LookForCardIDInHandList_Bank8 + ret nc + +; look for the card ID 1 in the Hand and Play Area +; if any card is found, return no carry. + ld a, [wCurCardCanAttack] + call LookForCardIDInHandAndPlayArea + jr c, .no_carry +; none found + + ld a, [wTempAIPokemonCard] + scf + ret + +.no_carry + or a + ret + +; returns carry if card ID in a +; is found in Play Area, starting with +; location in b +; input: +; a = card ID +; b = PLAY_AREA_* to start with +; output: +; a = PLAY_AREA_* of found card +; carry set if found +LookForCardIDInPlayArea_Bank8: ; 22a72 (8:6a72) + ld [wTempCardIDToLook], a +.loop + ld a, DUELVARS_ARENA_CARD + add b + call GetTurnDuelistVariable + cp $ff + ret z + + call LoadCardDataToBuffer1_FromDeckIndex + ld c, a + ld a, [wTempCardIDToLook] + cp c + jr z, .is_same + + inc b + ld a, MAX_PLAY_AREA_POKEMON + cp b + jr nz, .loop + ld b, $ff + or a + ret + +.is_same + ld a, b + scf + ret + +; runs through list avoiding card in e. +; removes first card in list not equal to e +; and that has a type allowed to be removed, in d. +; returns carry if successful in finding a card. +; input: +; d = type of card allowed to be removed +; ($00 = Trainer, $01 = Pokemon, $02 = Energy) +; e = card deck index to avoid removing +; output: +; a = card index of removed card +RemoveFromListDifferentCardOfGivenType: ; 22a95 (8:6a95) + push hl + push de + push bc + call CountCardsInDuelTempList + call ShuffleCards + +; loop list until a card with +; deck index different from e is found. +.loop_list + ld a, [hli] + cp $ff + jr z, .no_carry + cp e + jr z, .loop_list + +; get this card's type + ldh [hTempCardIndex_ff98], a + push de + call GetCardIDFromDeckIndex + call GetCardType + pop de + cp TYPE_ENERGY + jr c, .pkmn_card + cp TYPE_TRAINER + jr nz, .energy + +; only remove from list specific type. + +; trainer + ld a, d + or a + jr nz, .loop_list + jr .remove_card +.energy + ld a, d + cp $02 + jr nz, .loop_list + jr .remove_card +.pkmn_card + ld a, d + cp $01 + jr nz, .loop_list + ; fallthrough + +.remove_card + ld d, h + ld e, l + dec hl +.loop_remove + ld a, [de] + inc de + ld [hli], a + cp $ff + jr nz, .loop_remove + +; success + ldh a, [hTempCardIndex_ff98] + pop bc + pop de + pop hl + scf + ret +.no_carry + pop bc + pop de + pop hl + or a + ret + +; used in Pokemon Trader checks to look for a specific +; card in the deck to trade with a card in hand that +; has a card ID different from e. +; returns carry if successful. +; input: +; a = card ID 1 +; e = card ID 2 +; output: +; a = deck index of card ID 1 found in deck +; e = deck index of Pokemon card in hand different than card ID 2 +LookForCardIDToTradeWithDifferentHandCard: ; 22ae0 (8:6ae0) + ld hl, wCurCardCanAttack + ld [hl], e + ld [wTempAI], a + +; if card ID 1 is in hand, return no carry. + call LookForCardIDInHandList_Bank8 + jr c, .no_carry + +; if card ID 1 is not in deck, return no carry. + ld a, [wTempAI] + ld e, a + ld a, CARD_LOCATION_DECK + call LookForCardIDInLocation + jr nc, .no_carry + +; store its deck index + ld [wTempAI], a + +; look in hand for Pokemon card ID that +; is different from card ID 2. + ld a, [wCurCardCanAttack] + ld c, a + call CreateHandCardList + ld hl, wDuelTempList + +.loop_hand + ld a, [hli] + cp $ff + jr z, .no_carry + ld b, a + call LoadCardDataToBuffer1_FromDeckIndex + cp c + jr z, .loop_hand + ld a, [wLoadedCard1Type] + cp TYPE_ENERGY + jr nc, .loop_hand + +; found, output deck index of card ID 1 in deck +; and deck index of card found in hand, and set carry + ld e, b + ld a, [wTempAI] + scf + ret + +.no_carry + or a + ret + +; returns carry if at least one card in the hand +; has the card ID of input. Outputs its index. +; input: +; a = card ID to look for +; output: +; a = deck index of card in hand found +CheckIfHasCardIDInHand: ; 22b1f (8:6b1f) + ld [wTempCardIDToLook], a + call CreateHandCardList + ld hl, wDuelTempList + ld c, 0 + +.loop_hand + ld a, [hli] + cp $ff + ret z + ldh [hTempCardIndex_ff98], a + call LoadCardDataToBuffer1_FromDeckIndex + ld b, a + ld a, [wTempCardIDToLook] + cp b + jr nz, .loop_hand + ld a, c + or a + jr nz, .set_carry + inc c + jr nz, .loop_hand + +.set_carry + ldh a, [hTempCardIndex_ff98] + scf + ret + +; outputs in a total number of Pokemon cards in hand +; plus Pokemon in Turn Duelist's Play Area. +CountPokemonCardsInHandAndInPlayArea: ; 22b45 (8:6b45) + ld a, DUELVARS_NUMBER_OF_POKEMON_IN_PLAY_AREA + call GetTurnDuelistVariable + ld [wTempAI], a + call CreateHandCardList + ld hl, wDuelTempList +.loop_hand + ld a, [hli] + cp $ff + jr z, .done + call GetCardIDFromDeckIndex + call GetCardType + cp TYPE_ENERGY + jr nc, .loop_hand + ld a, [wTempAI] + inc a + ld [wTempAI], a + jr .loop_hand +.done + ld a, [wTempAI] + ret + +; returns carry if a duplicate Pokemon card is found in hand. +; outputs in a the deck index of one of them. +FindDuplicatePokemonCards: ; 22b6f (8:6b6f) + ld a, $ff + ld [wTempAI], a + call CreateHandCardList + ld hl, wDuelTempList + push hl + +.loop_hand_outer + pop hl + ld a, [hli] + cp $ff + jr z, .done + call GetCardIDFromDeckIndex + ld b, e + push hl + +.loop_hand_inner + ld a, [hli] + cp $ff + jr z, .loop_hand_outer + ld c, a + call GetCardIDFromDeckIndex + ld a, e + cp b + jr nz, .loop_hand_inner + +; found two cards with same ID, +; if they are Pokemon cards, store its deck index. + push bc + call GetCardType + pop bc + cp TYPE_ENERGY + jr nc, .loop_hand_outer + ld a, c + ld [wTempAI], a + ; for some reason loop still continues + ; even though if some other duplicate + ; cards are found, it overwrites the result. + jr .loop_hand_outer + +.done + ld a, [wTempAI] + cp $ff + jr z, .no_carry + +; found + scf + ret +.no_carry + or a + ret + +; return carry flag if attack is not high recoil. +AICheckIfAttackIsHighRecoil: ; 22bad (8:6bad) + farcall AIProcessButDontUseAttack + ret nc + ld a, [wSelectedAttack] + ld e, a + ld a, DUELVARS_ARENA_CARD + call GetTurnDuelistVariable + ld d, a + call CopyAttackDataAndDamage_FromDeckIndex + ld a, ATTACK_FLAG1_ADDRESS | HIGH_RECOIL_F + call CheckLoadedAttackFlag + ccf + ret diff --git a/src/engine/duel/ai/core.asm b/src/engine/duel/ai/core.asm new file mode 100644 index 0000000..9604322 --- /dev/null +++ b/src/engine/duel/ai/core.asm @@ -0,0 +1,2770 @@ +INCLUDE "engine/duel/ai/decks/unreferenced.asm" + +; returns carry if damage dealt from any of +; a card's attacks KOs defending Pokémon +; outputs index of the attack that KOs +; input: +; [hTempPlayAreaLocation_ff9d] = location of attacking card to consider +; output: +; [wSelectedAttack] = attack index that KOs +CheckIfAnyAttackKnocksOutDefendingCard: ; 140ae (5:40ae) + xor a ; first attack + call CheckIfAttackKnocksOutDefendingCard + ret c + ld a, SECOND_ATTACK +; fallthrough + +CheckIfAttackKnocksOutDefendingCard: ; 140b5 (5:40b5) + call EstimateDamage_VersusDefendingCard + ld a, DUELVARS_ARENA_CARD_HP + call GetNonTurnDuelistVariable + ld hl, wDamage + sub [hl] + ret c + ret nz + scf + ret + +; returns carry if any of the defending Pokémon's attacks +; brings card at hTempPlayAreaLocation_ff9d down +; to exactly 0 HP. +; outputs that attack index in wSelectedAttack. +CheckIfAnyDefendingPokemonAttackDealsSameDamageAsHP: ; 140c5 (5:40c5) + xor a ; FIRST_ATTACK_OR_PKMN_POWER + call .check_damage + ret c + ld a, SECOND_ATTACK + +.check_damage + call EstimateDamage_FromDefendingPokemon + ldh a, [hTempPlayAreaLocation_ff9d] + add DUELVARS_ARENA_CARD_HP + call GetTurnDuelistVariable + ld hl, wDamage + sub [hl] + jr z, .true + ret +.true + scf + ret + +; checks AI scores for all benched Pokémon +; returns the location of the card with highest score +; in a and [hTempPlayAreaLocation_ff9d] +FindHighestBenchScore: ; 140df (5:40df) + ld a, DUELVARS_NUMBER_OF_POKEMON_IN_PLAY_AREA + call GetTurnDuelistVariable + ld b, a + ld c, 0 + ld e, c + ld d, c + ld hl, wPlayAreaAIScore + 1 + jp .next + +.loop + ld a, [hli] + cp e + jr c, .next + ld e, a + ld d, c +.next + inc c + dec b + jr nz, .loop + + ld a, d + ldh [hTempPlayAreaLocation_ff9d], a + or a + ret + +; adds a to wAIScore +; if there's overflow, it's capped at $ff +; output: +; a = a + wAIScore (capped at $ff) +AddToAIScore: ; 140fe (5:40fe) + push hl + ld hl, wAIScore + add [hl] + jr nc, .no_cap + ld a, $ff +.no_cap + ld [hl], a + pop hl + ret + +; subs a from wAIScore +; if there's underflow, it's capped at $00 +SubFromAIScore: ; 1410a (5:410a) + push hl + push de + ld e, a + ld hl, wAIScore + ld a, [hl] + or a + jr z, .done + sub e + ld [hl], a + jr nc, .done + ld [hl], $00 +.done + pop de + pop hl + ret + +; loads defending Pokémon's weakness/resistance +; and the number of prize cards in both sides +LoadDefendingPokemonColorWRAndPrizeCards: ; 1411d (5:411d) + call SwapTurn + call GetArenaCardColor + call TranslateColorToWR + ld [wAIPlayerColor], a + call GetArenaCardWeakness + ld [wAIPlayerWeakness], a + call GetArenaCardResistance + ld [wAIPlayerResistance], a + call CountPrizes + ld [wAIPlayerPrizeCount], a + call SwapTurn + call CountPrizes + ld [wAIOpponentPrizeCount], a + ret + +; called when AI has chosen its attack. +; executes all effects and damage. +; handles AI choosing parameters for certain attacks as well. +AITryUseAttack: ; 14145 (5:4145) + ld a, [wSelectedAttack] + ldh [hTemp_ffa0], a + ld e, a + ld a, DUELVARS_ARENA_CARD + call GetTurnDuelistVariable + ldh [hTempCardIndex_ff9f], a + ld d, a + call CopyAttackDataAndDamage_FromDeckIndex + ld a, OPPACTION_BEGIN_ATTACK + bank1call AIMakeDecision + ret c + + call AISelectSpecialAttackParameters + jr c, .use_attack + ld a, EFFECTCMDTYPE_AI_SELECTION + call TryExecuteEffectCommandFunction + +.use_attack + ld a, [wSelectedAttack] + ld e, a + ld a, DUELVARS_ARENA_CARD + call GetTurnDuelistVariable + ld d, a + call CopyAttackDataAndDamage_FromDeckIndex + ld a, OPPACTION_USE_ATTACK + bank1call AIMakeDecision + ret c + + ld a, EFFECTCMDTYPE_AI_SWITCH_DEFENDING_PKMN + call TryExecuteEffectCommandFunction + ld a, OPPACTION_ATTACK_ANIM_AND_DAMAGE + bank1call AIMakeDecision + ret + +; return carry if any of the following is satisfied: +; - deck index in a corresponds to a double colorless energy card; +; - card type in wTempCardType is colorless; +; - card ID in wTempCardID is a Pokémon card that has +; attacks that require energy other than its color and +; the deck index in a corresponds to that energy type; +; - card ID is Eevee and a corresponds to an energy type +; of water, fire or lightning; +; - type of card in register a is the same as wTempCardType. +; used for knowing if a given energy card can be discarded +; from a given Pokémon card +; input: +; a = energy card attached to Pokémon to check +; [wTempCardType] = TYPE_ENERGY_* of given Pokémon +; [wTempCardID] = card index of Pokémon card to check +CheckIfEnergyIsUseful: ; 14184 (5:4184) + push de + call GetCardIDFromDeckIndex + ld a, e + cp DOUBLE_COLORLESS_ENERGY + jr z, .set_carry + ld a, [wTempCardType] + cp TYPE_ENERGY_DOUBLE_COLORLESS + jr z, .set_carry + ld a, [wTempCardID] + + ld d, PSYCHIC_ENERGY + cp EXEGGCUTE + jr z, .check_energy + cp EXEGGUTOR + jr z, .check_energy + cp PSYDUCK + jr z, .check_energy + cp GOLDUCK + jr z, .check_energy + + ld d, WATER_ENERGY + cp SURFING_PIKACHU1 + jr z, .check_energy + cp SURFING_PIKACHU2 + jr z, .check_energy + + cp EEVEE + jr nz, .check_type + ld a, e + cp WATER_ENERGY + jr z, .set_carry + cp FIRE_ENERGY + jr z, .set_carry + cp LIGHTNING_ENERGY + jr z, .set_carry + +.check_type + ld d, $00 ; unnecessary? + call GetCardType + ld d, a + ld a, [wTempCardType] + cp d + jr z, .set_carry + pop de + or a + ret + +.check_energy + ld a, d + cp e + jr nz, .check_type +.set_carry + pop de + scf + ret + +; pick a random Pokemon in the bench. +; output: +; - a = PLAY_AREA_* of Bench Pokemon picked. +PickRandomBenchPokemon: ; 141da (5:41da) + ld a, DUELVARS_NUMBER_OF_POKEMON_IN_PLAY_AREA + call GetTurnDuelistVariable + dec a + call Random + inc a + ret + +AIPickPrizeCards: ; 141e5 (5:41e5) + ld a, [wNumberPrizeCardsToTake] + ld b, a +.loop + call .PickPrizeCard + ld a, DUELVARS_PRIZES + call GetTurnDuelistVariable + or a + jr z, .done + dec b + jr nz, .loop +.done + ret + +; picks a prize card at random +; and adds it to the hand. +.PickPrizeCard: ; 141f8 (5:41f8) + ld a, DUELVARS_PRIZES + call GetTurnDuelistVariable + push hl + ld c, a + +; choose a random prize card until +; one is found that isn't taken already. +.loop_pick_prize + ld a, 6 + call Random + ld e, a + ld d, $00 + ld hl, .prize_flags + add hl, de + ld a, [hl] + and c + jr z, .loop_pick_prize ; no prize + +; prize card was found +; remove this prize from wOpponentPrizes + ld a, [hl] + pop hl + cpl + and [hl] + ld [hl], a + +; add this prize card to the hand + ld a, e + add DUELVARS_PRIZE_CARDS + call GetTurnDuelistVariable + call AddCardToHand + ret + +.prize_flags ; 1421e (5:421e) + db $1 << 0 + db $1 << 1 + db $1 << 2 + db $1 << 3 + db $1 << 4 + db $1 << 5 + db $1 << 6 + db $1 << 7 + +; routine for AI to play all Basic cards from its hand +; in the beginning of the Duel. +AIPlayInitialBasicCards: ; 14226 (5:4226) + call CreateHandCardList + ld hl, wDuelTempList +.check_for_next_card + ld a, [hli] + ldh [hTempCardIndex_ff98], a + cp $ff + ret z ; return when done + + call LoadCardDataToBuffer1_FromDeckIndex + ld a, [wLoadedCard1Type] + cp TYPE_ENERGY + jr nc, .check_for_next_card ; skip if not Pokemon card + ld a, [wLoadedCard1Stage] + or a + jr nz, .check_for_next_card ; skip if not Basic Stage + +; play Basic card from hand + push hl + ldh a, [hTempCardIndex_ff98] + call PutHandPokemonCardInPlayArea + pop hl + jr .check_for_next_card + +; returns carry if Pokémon at hTempPlayAreaLocation_ff9d +; can't use an attack or if that selected attack doesn't have enough energy +; input: +; [hTempPlayAreaLocation_ff9d] = location of Pokémon card +; [wSelectedAttack] = selected attack to examine +CheckIfSelectedAttackIsUnusable: ; 1424b (5:424b) + ldh a, [hTempPlayAreaLocation_ff9d] + or a + jr nz, .bench + + bank1call HandleCantAttackSubstatus + ret c + bank1call CheckIfActiveCardParalyzedOrAsleep + ret c + + ld a, DUELVARS_ARENA_CARD + call GetTurnDuelistVariable + ld d, a + ld a, [wSelectedAttack] + ld e, a + call CopyAttackDataAndDamage_FromDeckIndex + call HandleAmnesiaSubstatus + ret c + ld a, EFFECTCMDTYPE_INITIAL_EFFECT_1 + call TryExecuteEffectCommandFunction + ret c + +.bench + call CheckEnergyNeededForAttack + ret c ; can't be used + ld a, ATTACK_FLAG2_ADDRESS | FLAG_2_BIT_5_F + call CheckLoadedAttackFlag + ret + +; load selected attack from Pokémon in hTempPlayAreaLocation_ff9d +; and checks if there is enough energy to execute the selected attack +; input: +; [hTempPlayAreaLocation_ff9d] = location of Pokémon card +; [wSelectedAttack] = selected attack to examine +; output: +; b = basic energy still needed +; c = colorless energy still needed +; e = output of ConvertColorToEnergyCardID, or $0 if not an attack +; carry set if no attack +; OR if it's a Pokémon Power +; OR if not enough energy for attack +CheckEnergyNeededForAttack: ; 14279 (5:4279) + ldh a, [hTempPlayAreaLocation_ff9d] + add DUELVARS_ARENA_CARD + call GetTurnDuelistVariable + ld d, a + ld a, [wSelectedAttack] + ld e, a + call CopyAttackDataAndDamage_FromDeckIndex + ld hl, wLoadedAttackName + ld a, [hli] + or [hl] + jr z, .no_attack + ld a, [wLoadedAttackCategory] + cp POKEMON_POWER + jr nz, .is_attack +.no_attack + lb bc, 0, 0 + ld e, c + scf + ret + +.is_attack + ldh a, [hTempPlayAreaLocation_ff9d] + ld e, a + call GetPlayAreaCardAttachedEnergies + bank1call HandleEnergyBurn + + xor a + ld [wTempLoadedAttackEnergyCost], a + ld [wTempLoadedAttackEnergyNeededAmount], a + ld [wTempLoadedAttackEnergyNeededType], a + + ld hl, wAttachedEnergies + ld de, wLoadedAttackEnergyCost + ld b, 0 + ld c, (NUM_TYPES / 2) - 1 + +.loop + ; check all basic energy cards except colorless + ld a, [de] + swap a + call CheckIfEnoughParticularAttachedEnergy + ld a, [de] + call CheckIfEnoughParticularAttachedEnergy + inc de + dec c + jr nz, .loop + +; running CheckIfEnoughParticularAttachedEnergy back to back like this +; overwrites the results of a previous call of this function, +; however, no attack in the game has energy requirements for two +; different energy types (excluding colorless), so this routine +; will always just return the result for one type of basic energy, +; while all others will necessarily have an energy cost of 0 +; if attacks are added to the game with energy requirements of +; two different basic energy types, then this routine only accounts +; for the type with the highest index + + ; colorless + ld a, [de] + swap a + and %00001111 + ld b, a ; colorless energy still needed + ld a, [wTempLoadedAttackEnergyCost] + ld hl, wTempLoadedAttackEnergyNeededAmount + sub [hl] + ld c, a ; basic energy still needed + ld a, [wTotalAttachedEnergies] + sub c + sub b + jr c, .not_enough + + ld a, [wTempLoadedAttackEnergyNeededAmount] + or a + ret z + +; being here means the energy cost isn't satisfied, +; including with colorless energy + xor a +.not_enough + cpl + inc a + ld c, a ; colorless energy still needed + ld a, [wTempLoadedAttackEnergyNeededAmount] + ld b, a ; basic energy still needed + ld a, [wTempLoadedAttackEnergyNeededType] + call ConvertColorToEnergyCardID + ld e, a + ld d, 0 + scf + ret + +; takes as input the energy cost of an attack for a +; particular energy, stored in the lower nibble of a +; if the attack costs some amount of this energy, the lower nibble of a != 0, +; and this amount is stored in wTempLoadedAttackEnergyCost +; sets carry flag if not enough energy of this type attached +; input: +; a = this energy cost of attack (lower nibble) +; [hl] = attached energy +; output: +; carry set if not enough of this energy type attached +CheckIfEnoughParticularAttachedEnergy: ; 142f4 (5:42f4) + and %00001111 + jr nz, .check +.has_enough + inc hl + inc b + or a + ret + +.check + ld [wTempLoadedAttackEnergyCost], a + sub [hl] + jr z, .has_enough + jr c, .has_enough + + ; not enough energy + ld [wTempLoadedAttackEnergyNeededAmount], a + ld a, b + ld [wTempLoadedAttackEnergyNeededType], a + inc hl + inc b + scf + ret + +; input: +; a = energy type +; output: +; a = energy card ID +ConvertColorToEnergyCardID: ; 1430f (5:430f) + push hl + push de + ld e, a + ld d, 0 + ld hl, .card_id + add hl, de + ld a, [hl] + pop de + pop hl + ret + +.card_id + db FIRE_ENERGY + db GRASS_ENERGY + db LIGHTNING_ENERGY + db WATER_ENERGY + db FIGHTING_ENERGY + db PSYCHIC_ENERGY + db DOUBLE_COLORLESS_ENERGY + +; returns carry if loaded attack effect has +; an "initial effect 2" or "require selection" command type +; unreferenced +Func_14323: ; 14323 (5:4323) + ld hl, wLoadedAttackEffectCommands + ld a, [hli] + ld h, [hl] + ld l, a + ld a, EFFECTCMDTYPE_INITIAL_EFFECT_2 + push hl + call CheckMatchingCommand + pop hl + jr nc, .set_carry + ld a, EFFECTCMDTYPE_REQUIRE_SELECTION + call CheckMatchingCommand + jr nc, .set_carry + or a + ret +.set_carry + scf + ret + +; return carry depending on card index in a: +; - if energy card, return carry if no energy card has been played yet +; - if basic Pokémon card, return carry if there's space in bench +; - if evolution card, return carry if there's a Pokémon +; in Play Area it can evolve +; - if trainer card, return carry if it can be used +; input: +; a = card index to check +CheckIfCardCanBePlayed: ; 1433d (5:433d) + ldh [hTempCardIndex_ff9f], a + call LoadCardDataToBuffer1_FromDeckIndex + ld a, [wLoadedCard1Type] + cp TYPE_ENERGY + jr c, .pokemon_card + cp TYPE_TRAINER + jr z, .trainer_card + +; energy card + ld a, [wAlreadyPlayedEnergy] + or a + ret z + scf + ret + +.pokemon_card + ld a, [wLoadedCard1Stage] + or a + jr nz, .evolution_card + ld a, DUELVARS_NUMBER_OF_POKEMON_IN_PLAY_AREA + call GetTurnDuelistVariable + cp MAX_PLAY_AREA_POKEMON + ccf + ret + +.evolution_card + bank1call IsPrehistoricPowerActive + ret c + ld a, DUELVARS_NUMBER_OF_POKEMON_IN_PLAY_AREA + call GetTurnDuelistVariable + ld c, a + ld b, 0 +.loop + push bc + ld e, b + ldh a, [hTempCardIndex_ff9f] + ld d, a + call CheckIfCanEvolveInto + pop bc + ret nc + inc b + dec c + jr nz, .loop + scf + ret + +.trainer_card + bank1call CheckCantUseTrainerDueToHeadache + ret c + call LoadNonPokemonCardEffectCommands + ld a, EFFECTCMDTYPE_INITIAL_EFFECT_1 + call TryExecuteEffectCommandFunction + ret + +; loads all the energy cards +; in hand in wDuelTempList +; return carry if no energy cards found +CreateEnergyCardListFromHand: ; 1438c (5:438c) + push hl + push de + push bc + ld de, wDuelTempList + ld b, a + ld a, DUELVARS_NUMBER_OF_CARDS_IN_HAND + call GetTurnDuelistVariable + ld c, a + inc c + ld l, LOW(wOpponentHand) + jr .decrease + +.loop + ld a, [hli] + push de + call GetCardIDFromDeckIndex + call GetCardType + pop de + and TYPE_ENERGY + jr z, .decrease + dec hl + ld a, [hli] + ld [de], a + inc de +.decrease + dec c + jr nz, .loop + + ld a, $ff + ld [de], a + pop bc + pop de + pop hl + ld a, [wDuelTempList] + cp $ff + ccf + ret + +; looks for card ID in hand and +; sets carry if a card wasn't found +; as opposed to LookForCardIDInHandList_Bank5 +; this function doesn't create a list +; and preserves hl, de and bc +; input: +; a = card ID +; output: +; a = card deck index, if found +; carry set if NOT found +LookForCardIDInHand: ; 143bf (5:43bf) + push hl + push de + push bc + ld b, a + ld a, DUELVARS_NUMBER_OF_CARDS_IN_HAND + call GetTurnDuelistVariable + ld c, a + inc c + ld l, DUELVARS_HAND + jr .next + +.loop + ld a, [hli] + call GetCardIDFromDeckIndex + ld a, e + cp b + jr z, .no_carry +.next + dec c + jr nz, .loop + + pop bc + pop de + pop hl + scf + ret + +.no_carry + dec hl + ld a, [hl] + pop bc + pop de + pop hl + or a + ret + +INCLUDE "engine/duel/ai/damage_calculation.asm" + +AIProcessHandTrainerCards: ; 14663 (5:4663) + farcall _AIProcessHandTrainerCards + ret + +INCLUDE "engine/duel/ai/deck_ai.asm" + +; return carry if card ID loaded in a is found in hand +; and outputs in a the deck index of that card +; as opposed to LookForCardIDInHand, this function +; creates a list in wDuelTempList +; input: +; a = card ID +; output: +; a = card deck index, if found +; carry set if found +LookForCardIDInHandList_Bank5: ; 155d2 (5:55d2) + ld [wTempCardIDToLook], a + call CreateHandCardList + ld hl, wDuelTempList + +.loop + ld a, [hli] + cp $ff + ret z + ldh [hTempCardIndex_ff98], a + call LoadCardDataToBuffer1_FromDeckIndex + ld b, a + ld a, [wTempCardIDToLook] + cp b + jr nz, .loop + + ldh a, [hTempCardIndex_ff98] + scf + ret + +; returns carry if card ID in a +; is found in Play Area, starting with +; location in b +; input: +; a = card ID +; b = PLAY_AREA_* to start with +; output: +; a = PLAY_AREA_* of found card +; carry set if found +LookForCardIDInPlayArea_Bank5: ; 155ef (5:55ef) + ld [wTempCardIDToLook], a + +.loop + ld a, DUELVARS_ARENA_CARD + add b + call GetTurnDuelistVariable + cp $ff + ret z + call LoadCardDataToBuffer1_FromDeckIndex + ld c, a + ld a, [wTempCardIDToLook] + cp c + jr z, .found + inc b + ld a, MAX_PLAY_AREA_POKEMON + cp b + jr nz, .loop + + ld b, $ff + or a + ret +.found + ld a, b + scf + ret + +; check if energy card ID in e is in AI hand and, +; if so, attaches it to card ID in d in Play Area. +; input: +; e = Energy card ID +; d = Pokemon card ID +AIAttachEnergyInHandToCardInPlayArea: ; 15612 (5:5612) + ld a, e + push de + call LookForCardIDInHandList_Bank5 + pop de + ret nc ; not in hand + ld b, PLAY_AREA_ARENA + +.attach + ld e, a + ld a, d + call LookForCardIDInPlayArea_Bank5 + ldh [hTempPlayAreaLocation_ffa1], a + ld a, e + ldh [hTemp_ffa0], a + ld a, OPPACTION_PLAY_ENERGY + bank1call AIMakeDecision + ret + +; same as AIAttachEnergyInHandToCardInPlayArea but +; only look for card ID in the Bench. +AIAttachEnergyInHandToCardInBench: ; 1562b (5:562b) + ld a, e + push de + call LookForCardIDInHandList_Bank5 + pop de + ret nc + ld b, PLAY_AREA_BENCH_1 + jr AIAttachEnergyInHandToCardInPlayArea.attach + +INCLUDE "engine/duel/ai/init.asm" + +; load selected attack from Pokémon in hTempPlayAreaLocation_ff9d, +; gets an energy card to discard and subsequently +; check if there is enough energy to execute the selected attack +; after removing that attached energy card. +; input: +; [hTempPlayAreaLocation_ff9d] = location of Pokémon card +; [wSelectedAttack] = selected attack to examine +; output: +; b = basic energy still needed +; c = colorless energy still needed +; e = output of ConvertColorToEnergyCardID, or $0 if not an attack +; carry set if no attack +; OR if it's a Pokémon Power +; OR if not enough energy for attack +CheckEnergyNeededForAttackAfterDiscard: ; 156c3 (5:56c3) + ldh a, [hTempPlayAreaLocation_ff9d] + add DUELVARS_ARENA_CARD + call GetTurnDuelistVariable + ld d, a + ld a, [wSelectedAttack] + ld e, a + call CopyAttackDataAndDamage_FromDeckIndex + ld hl, wLoadedAttackName + ld a, [hli] + or [hl] + jr z, .no_attack + ld a, [wLoadedAttackCategory] + cp POKEMON_POWER + jr nz, .is_attack +.no_attack + lb bc, 0, 0 + ld e, c + scf + ret + +.is_attack + ldh a, [hTempPlayAreaLocation_ff9d] + farcall AIPickEnergyCardToDiscard + call LoadCardDataToBuffer1_FromDeckIndex + cp DOUBLE_COLORLESS_ENERGY + jr z, .colorless + +; color energy +; decrease respective attached energy by 1. + ld hl, wAttachedEnergies + dec a + ld c, a + ld b, $00 + add hl, bc + dec [hl] + ld hl, wTotalAttachedEnergies + dec [hl] + jr .asm_1570c +; decrease attached colorless by 2. +.colorless + ld hl, wAttachedEnergies + COLORLESS + dec [hl] + dec [hl] + ld hl, wTotalAttachedEnergies + dec [hl] + dec [hl] + +.asm_1570c + bank1call HandleEnergyBurn + xor a + ld [wTempLoadedAttackEnergyCost], a + ld [wTempLoadedAttackEnergyNeededAmount], a + ld [wTempLoadedAttackEnergyNeededType], a + ld hl, wAttachedEnergies + ld de, wLoadedAttackEnergyCost + ld b, 0 + ld c, (NUM_TYPES / 2) - 1 +.loop + ; check all basic energy cards except colorless + ld a, [de] + swap a + call CheckIfEnoughParticularAttachedEnergy + ld a, [de] + call CheckIfEnoughParticularAttachedEnergy + inc de + dec c + jr nz, .loop + + ld a, [de] + swap a + and $0f + ld b, a ; colorless energy still needed + ld a, [wTempLoadedAttackEnergyCost] + ld hl, wTempLoadedAttackEnergyNeededAmount + sub [hl] + ld c, a ; basic energy still needed + ld a, [wTotalAttachedEnergies] + sub c + sub b + jr c, .not_enough_energy + + ld a, [wTempLoadedAttackEnergyNeededAmount] + or a + ret z + +; being here means the energy cost isn't satisfied, +; including with colorless energy + xor a +.not_enough_energy + cpl + inc a + ld c, a ; colorless energy still needed + ld a, [wTempLoadedAttackEnergyNeededAmount] + ld b, a ; basic energy still needed + ld a, [wTempLoadedAttackEnergyNeededType] + call ConvertColorToEnergyCardID + ld e, a + ld d, 0 + scf + ret + +; zeroes a bytes starting at hl +ClearMemory_Bank5: ; 1575e (5:575e) + push af + push bc + push hl + ld b, a + xor a +.clear_loop + ld [hli], a + dec b + jr nz, .clear_loop + pop hl + pop bc + pop af + ret + +; returns in a the tens digit of value in a +CalculateByteTensDigit: ; 1576b (5:576b) + push bc + ld c, 0 +.loop + sub 10 + jr c, .done + inc c + jr .loop +.done + ld a, c + pop bc + ret + +; returns in a the result of +; dividing b by a, rounded down +; input: +; a = divisor +; b = dividend +CalculateBDividedByA_Bank5: ; 15778 (5:5778) + push bc + ld c, a + ld a, b + ld b, c + ld c, 0 +.loop + sub b + jr c, .done + inc c + jr .loop +.done + ld a, c + pop bc + ret + +; returns in a the number of energy cards attached +; to Pokémon in location held by e +; this assumes that colorless are paired so +; that one colorless energy card provides 2 colorless energy +; input: +; e = location to check, i.e. PLAY_AREA_* +; output: +; a = number of energy cards attached +CountNumberOfEnergyCardsAttached: ; 15787 (5:5787) + call GetPlayAreaCardAttachedEnergies + ld a, [wTotalAttachedEnergies] + or a + ret z + + xor a + push hl + push bc + ld b, NUM_COLORED_TYPES + ld hl, wAttachedEnergies +; sum all the attached energies +.loop + add [hl] + inc hl + dec b + jr nz, .loop + + ld b, [hl] + srl b +; counts colorless ad halves it + add b + pop bc + pop hl + ret + +; returns carry if any card with ID in e is found +; in card location in a +; input: +; a = card location to look in; +; e = card ID to look for. +; output: +; a = deck index of card found, if any +CheckIfAnyCardIDinLocation: ; 157a3 (5:57a3) + ld b, a + ld c, e + lb de, 0, 0 +.loop + ld a, DUELVARS_CARD_LOCATIONS + add e + call GetTurnDuelistVariable + cp b + jr nz, .next + ld a, e + push de + call GetCardIDFromDeckIndex + ld a, e + pop de + cp c + jr z, .set_carry +.next + inc e + ld a, DECK_SIZE + cp e + jr nz, .loop + or a + ret +.set_carry + ld a, e + scf + ret + +; counts total number of energy cards in opponent's hand +; plus all the cards attached in Turn Duelist's Play Area. +; output: +; a and wTempAI = total number of energy cards. +CountOppEnergyCardsInHandAndAttached: ; 157c6 (5:57c6) + xor a + ld [wTempAI], a + call CreateEnergyCardListFromHand + jr c, .attached + +; counts number of energy cards in hand + ld b, -1 + ld hl, wDuelTempList +.loop_hand + inc b + ld a, [hli] + cp $ff + jr nz, .loop_hand + ld a, b + ld [wTempAI], a + +; counts number of energy cards +; that are attached in Play Area +.attached + ld a, DUELVARS_NUMBER_OF_POKEMON_IN_PLAY_AREA + call GetTurnDuelistVariable + ld d, a + ld e, PLAY_AREA_ARENA +.loop_play_area + call CountNumberOfEnergyCardsAttached + ld hl, wTempAI + add [hl] + ld [hl], a + inc e + dec d + jr nz, .loop_play_area + ret + +; returns carry if any card with ID in e is found +; in the list that is pointed by hl. +; if one is found, it is removed from the list. +; input: +; e = card ID to look for. +; hl = list to look in +RemoveCardIDInList: ; 157f3 (5:57f3) + push hl + push de + push bc + ld c, e + +.loop_1 + ld a, [hli] + cp $ff + jr z, .no_carry + + ldh [hTempCardIndex_ff98], a + call GetCardIDFromDeckIndex + ld a, c + cp e + jr nz, .loop_1 + +; found + ld d, h + ld e, l + dec hl + +; remove this index from the list +; and reposition the rest of the list ahead. +.loop_2 + ld a, [de] + inc de + ld [hli], a + cp $ff + jr nz, .loop_2 + + ldh a, [hTempCardIndex_ff98] + pop bc + pop de + pop hl + scf + ret + +.no_carry + pop bc + pop de + pop hl + or a + ret + +; play Pokemon cards from the hand to set the starting +; Play Area of Boss decks. +; each Boss deck has two ID lists in order of preference. +; one list is for the Arena card is the other is for the Bench cards. +; if Arena card could not be set (due to hand not having any card in its list) +; or if list is null, return carry and do not play any cards. +TrySetUpBossStartingPlayArea: ; 1581b (5:581b) + ld de, wAICardListArenaPriority + ld a, d + or a + jr z, .set_carry ; return if null + +; pick Arena card + call CreateHandCardList + ld hl, wDuelTempList + ld de, wAICardListArenaPriority + call .PlayPokemonCardInOrder + ret c + +; play Pokemon cards to Bench until there are +; a maximum of 3 cards in Play Area. +.loop + ld de, wAICardListBenchPriority + call .PlayPokemonCardInOrder + jr c, .done + cp 3 + jr c, .loop + +.done + or a + ret +.set_carry + scf + ret + +; runs through input card ID list in de. +; plays to Play Area first card that is found in hand. +; returns carry if none of the cards in the list are found. +; returns number of Pokemon in Play Area in a. +.PlayPokemonCardInOrder ; 1583f (5:583f) + ld a, [de] + ld c, a + inc de + ld a, [de] + ld d, a + ld e, c + +; go in order of the list in de and +; add first card that matches ID. +; returns carry if hand doesn't have any card in list. +.loop_id_list + ld a, [de] + inc de + or a + jr z, .not_found + push de + ld e, a + call RemoveCardIDInList + pop de + jr nc, .loop_id_list + + ; play this card to Play Area and return + push hl + call PutHandPokemonCardInPlayArea + pop hl + or a + ret + +.not_found + scf + ret + +; expects a $00-terminated list of 3-byte data with the following: +; - non-zero value (anything but $1 is ignored) +; - card ID to look for in Play Area +; - number of energy cards +; returns carry if a card ID is found in bench with at least the +; listed number of energy cards +; unreferenced +Func_1585b: ; 1585b (5:585b) + ld a, [hli] + or a + jr z, .no_carry + dec a + jr nz, .next_1 + ld a, [hli] + ld b, PLAY_AREA_BENCH_1 + push hl + call LookForCardIDInPlayArea_Bank5 + jr nc, .next_2 + ld e, a + push de + call CountNumberOfEnergyCardsAttached + pop de + pop hl + ld b, [hl] + cp b + jr nc, .set_carry + inc hl + jr Func_1585b + +.next_1 + inc hl + inc hl + jr Func_1585b + +.next_2 + pop hl + inc hl + jr Func_1585b + +.no_carry + or a + ret + +.set_carry + ld a, e + scf + ret + +; expects a $00-terminated list of 3-byte data with the following: +; - non-zero value +; - card ID +; - number of energy cards +; goes through the given list and if a card with a listed ID is found +; with less than the number of energy cards corresponding to its entry +; then have AI try to play an energy card from the hand to it +; unreferenced +Func_15886: ; 15886 (5:5886) + push hl + call CreateEnergyCardListFromHand + pop hl + ret c ; quit if no energy cards in hand + +.loop_energy_cards + ld a, [hli] + or a + ret z ; done + ld a, [hli] + ld b, PLAY_AREA_ARENA + push hl + call LookForCardIDInPlayArea_Bank5 + jr nc, .next ; skip if not found in Play Area + ld e, a + push de + call CountNumberOfEnergyCardsAttached + pop de + pop hl + cp [hl] + inc hl + jr nc, .loop_energy_cards + ld a, e + ldh [hTempPlayAreaLocation_ff9d], a + push hl + call AITryToPlayEnergyCard + pop hl + ret c + jr .loop_energy_cards +.next + pop hl + inc hl + jr .loop_energy_cards + +INCLUDE "engine/duel/ai/retreat.asm" + +; Copy cards from wDuelTempList in hl to wHandTempList in de +CopyHandCardList: ; 15ea6 (5:5ea6) + ld a, [hli] + ld [de], a + cp $ff + ret z + inc de + jr CopyHandCardList + +INCLUDE "engine/duel/ai/hand_pokemon.asm" + +; check if player's active Pokémon is Mr Mime +; if it isn't, set carry +; if it is, check if Pokémon at a +; can damage it, and if it can, set carry +; input: +; a = location of Pokémon card +CheckDamageToMrMime: ; 16270 (5:6270) + push af + ld a, DUELVARS_ARENA_CARD + call GetNonTurnDuelistVariable + call SwapTurn + call GetCardIDFromDeckIndex + call SwapTurn + ld a, e + cp MR_MIME + pop bc + jr nz, .set_carry + ld a, b + call CheckIfCanDamageDefendingPokemon + jr c, .set_carry + or a + ret +.set_carry + scf + ret + +; returns carry if arena card +; can knock out defending Pokémon +CheckIfActiveCardCanKnockOut: ; 1628f (5:628f) + xor a + ldh [hTempPlayAreaLocation_ff9d], a + call CheckIfAnyAttackKnocksOutDefendingCard + jr nc, .fail + call CheckIfSelectedAttackIsUnusable + jp c, .fail + scf + ret + +.fail + or a + ret + +; outputs carry if any of the active Pokémon attacks +; can be used and are not residual +CheckIfActivePokemonCanUseAnyNonResidualAttack: ; 162a1 (5:62a1) + xor a ; active card + ldh [hTempPlayAreaLocation_ff9d], a +; first atk + ld [wSelectedAttack], a + call CheckIfSelectedAttackIsUnusable + jr c, .next_atk + ld a, [wLoadedAttackCategory] + and RESIDUAL + jr z, .ok + +.next_atk +; second atk + ld a, $01 + ld [wSelectedAttack], a + call CheckIfSelectedAttackIsUnusable + jr c, .fail + ld a, [wLoadedAttackCategory] + and RESIDUAL + jr z, .ok +.fail + or a + ret + +.ok + scf + ret + +; looks for energy card(s) in hand depending on +; what is needed for selected card, for both attacks +; - if one basic energy is required, look for that energy; +; - if one colorless is required, create a list at wDuelTempList +; of all energy cards; +; - if two colorless are required, look for double colorless; +; return carry if successful in finding card +; input: +; [hTempPlayAreaLocation_ff9d] = location of Pokémon card +LookForEnergyNeededInHand: ; 162c8 (5:62c8) + xor a ; first attack + ld [wSelectedAttack], a + call CheckEnergyNeededForAttack + ld a, b + add c + cp 1 + jr z, .one_energy + cp 2 + jr nz, .second_attack + ld a, c + cp 2 + jr z, .two_colorless + +.second_attack + ld a, SECOND_ATTACK + ld [wSelectedAttack], a + call CheckEnergyNeededForAttack + ld a, b + add c + cp 1 + jr z, .one_energy + cp 2 + jr nz, .no_carry + ld a, c + cp 2 + jr z, .two_colorless +.no_carry + or a + ret + +.one_energy + ld a, b + or a + jr z, .one_colorless + ld a, e + call LookForCardIDInHandList_Bank5 + ret c + jr .no_carry + +.one_colorless + call CreateEnergyCardListFromHand + jr c, .no_carry + scf + ret + +.two_colorless + ld a, DOUBLE_COLORLESS_ENERGY + call LookForCardIDInHandList_Bank5 + ret c + jr .no_carry + +; looks for energy card(s) in hand depending on +; what is needed for selected card and attack +; - if one basic energy is required, look for that energy; +; - if one colorless is required, create a list at wDuelTempList +; of all energy cards; +; - if two colorless are required, look for double colorless; +; return carry if successful in finding card +; input: +; [hTempPlayAreaLocation_ff9d] = location of Pokémon card +; [wSelectedAttack] = selected attack to examine +LookForEnergyNeededForAttackInHand: ; 16311 (5:6311) + call CheckEnergyNeededForAttack + ld a, b + add c + cp 1 + jr z, .one_energy + cp 2 + jr nz, .done + ld a, c + cp 2 + jr z, .two_colorless +.done + or a + ret + +.one_energy + ld a, b + or a + jr z, .one_colorless + ld a, e + call LookForCardIDInHandList_Bank5 + ret c + jr .done + +.one_colorless + call CreateEnergyCardListFromHand + jr c, .done + scf + ret + +.two_colorless + ld a, DOUBLE_COLORLESS_ENERGY + call LookForCardIDInHandList_Bank5 + ret c + jr .done + +; goes through $00 terminated list pointed +; by wAICardListPlayFromHandPriority and compares it to each card in hand. +; Sorts the hand in wDuelTempList so that the found card IDs +; are in the same order as the list pointed by de. +SortTempHandByIDList: ; 1633f (5:633f) + ld a, [wAICardListPlayFromHandPriority+1] + or a + ret z ; return if list is empty + +; start going down the ID list + ld d, a + ld a, [wAICardListPlayFromHandPriority] + ld e, a + ld c, 0 +.loop_list_id +; get this item's ID +; if $00, list has ended + ld a, [de] + or a + ret z ; return when list is over + inc de + ld hl, wDuelTempList + ld b, 0 + add hl, bc + ld b, a + +; search in the hand card list +.next_hand_card + ld a, [hl] + ldh [hTempCardIndex_ff98], a + cp -1 + jr z, .loop_list_id + push bc + push de + call GetCardIDFromDeckIndex + ld a, e + pop de + pop bc + cp b + jr nz, .not_same + +; found +; swap this hand card with the spot +; in hand corresponding to c + push bc + push hl + ld b, 0 + ld hl, wDuelTempList + add hl, bc + ld b, [hl] + ldh a, [hTempCardIndex_ff98] + ld [hl], a + pop hl + ld [hl], b + pop bc + inc c +.not_same + inc hl + jr .next_hand_card + +; looks for energy card(s) in list at wDuelTempList +; depending on energy flags set in a +; return carry if successful in finding card +; input: +; a = energy flags needed +CheckEnergyFlagsNeededInList: ; 1637b (5:637b) + ld e, a + ld hl, wDuelTempList +.next_card + ld a, [hli] + cp $ff + jr z, .no_carry + push de + call GetCardIDFromDeckIndex + ld a, e + pop de + +; fire + cp FIRE_ENERGY + jr nz, .grass + ld a, FIRE_F + jr .check_energy +.grass + cp GRASS_ENERGY + jr nz, .lightning + ld a, GRASS_F + jr .check_energy +.lightning + cp LIGHTNING_ENERGY + jr nz, .water + ld a, LIGHTNING_F + jr .check_energy +.water + cp WATER_ENERGY + jr nz, .fighting + ld a, WATER_F + jr .check_energy +.fighting + cp FIGHTING_ENERGY + jr nz, .psychic + ld a, FIGHTING_F + jr .check_energy +.psychic + cp PSYCHIC_ENERGY + jr nz, .colorless + ld a, PSYCHIC_F + jr .check_energy +.colorless + cp DOUBLE_COLORLESS_ENERGY + jr nz, .next_card + ld a, COLORLESS_F + +; if energy card matches required energy, return carry +.check_energy + ld d, e + and e + ld e, d + jr z, .next_card + scf + ret +.no_carry + or a + ret + +; returns in a the energy cost of both attacks from card index in a +; represented by energy flags +; i.e. each bit represents a different energy type cost +; if any colorless energy is required, all bits are set +; input: +; a = card index +; output: +; a = bits of each energy requirement +GetAttacksEnergyCostBits: ; 163c9 (5:63c9) + call LoadCardDataToBuffer2_FromDeckIndex + ld hl, wLoadedCard2Atk1EnergyCost + call GetEnergyCostBits + ld b, a + + push bc + ld hl, wLoadedCard2Atk2EnergyCost + call GetEnergyCostBits + pop bc + or b + ret + +; returns in a the energy cost of an attack in [hl] +; represented by energy flags +; i.e. each bit represents a different energy type cost +; if any colorless energy is required, all bits are set +; input: +; [hl] = Loaded card attack energy cost +; output: +; a = bits of each energy requirement +GetEnergyCostBits: ; 163dd (5:63dd) + ld c, $00 + ld a, [hli] + ld b, a + +; fire + and $f0 + jr z, .grass + ld c, FIRE_F +.grass + ld a, b + and $0f + jr z, .lightning + ld a, GRASS_F + or c + ld c, a +.lightning + ld a, [hli] + ld b, a + and $f0 + jr z, .water + ld a, LIGHTNING_F + or c + ld c, a +.water + ld a, b + and $0f + jr z, .fighting + ld a, WATER_F + or c + ld c, a +.fighting + ld a, [hli] + ld b, a + and $f0 + jr z, .psychic + ld a, FIGHTING_F + or c + ld c, a +.psychic + ld a, b + and $0f + jr z, .colorless + ld a, PSYCHIC_F + or c + ld c, a +.colorless + ld a, [hli] + ld b, a + and $f0 + jr z, .done + ld a, %11111111 + or c ; unnecessary + ld c, a +.done + ld a, c + ret + +; set carry flag if any card in +; wDuelTempList evolves card index in a +; if found, the evolution card index is returned in a +; input: +; a = card index to check evolution +; output: +; a = card index of evolution found +CheckForEvolutionInList: ; 16422 (5:6422) + ld b, a + ld a, DUELVARS_ARENA_CARD + call GetTurnDuelistVariable + + push af + ld [hl], b + ld hl, wDuelTempList +.loop + ld a, [hli] + cp $ff + jr z, .no_carry + ld d, a + ld e, PLAY_AREA_ARENA + push de + push hl + call CheckIfCanEvolveInto + pop hl + pop de + jr c, .loop + + ld a, DUELVARS_ARENA_CARD + call GetTurnDuelistVariable + pop af + ld [hl], a + ld a, d + scf + ret + +.no_carry + ld a, DUELVARS_ARENA_CARD + call GetTurnDuelistVariable + pop af + ld [hl], a + or a + ret + +; set carry if it finds an evolution for +; the card index in a in the deck +; if found, return that evolution card index in a +; input: +; a = card index to check evolution +; output: +; a = card index of evolution found +CheckForEvolutionInDeck: ; 16451 (5:6451) + ld b, a + ld a, DUELVARS_ARENA_CARD + call GetTurnDuelistVariable + + push af + ld [hl], b + ld e, 0 +.loop + ld a, DUELVARS_CARD_LOCATIONS + add e + call GetTurnDuelistVariable + cp CARD_LOCATION_DECK + jr nz, .not_in_deck + push de + ld d, e + ld e, PLAY_AREA_ARENA + call CheckIfCanEvolveInto + pop de + jr nc, .set_carry + +; exit when it gets to the prize cards +.not_in_deck + inc e + ld a, DUELVARS_PRIZE_CARDS + cp e + jr nz, .loop + + ld a, DUELVARS_ARENA_CARD + call GetTurnDuelistVariable + pop af + ld [hl], a + or a + ret + +.set_carry + ld a, DUELVARS_ARENA_CARD + call GetTurnDuelistVariable + pop af + ld [hl], a + ld a, e + scf + ret + +INCLUDE "engine/duel/ai/energy.asm" + +INCLUDE "engine/duel/ai/attacks.asm" + +INCLUDE "engine/duel/ai/special_attacks.asm" + +; checks in other Play Area for non-basic cards. +; afterwards, that card is checked for damage, +; and if the damage counters it has is greater than or equal +; to the max HP of the card stage below it, +; return carry and that card's Play Area location in a. +; output: +; a = card location of Pokémon card, if found; +; carry set if such a card is found. +LookForCardThatIsKnockedOutOnDevolution: ; 17080 (5:7080) + ldh a, [hTempPlayAreaLocation_ff9d] + push af + call SwapTurn + ld a, DUELVARS_NUMBER_OF_POKEMON_IN_PLAY_AREA + call GetTurnDuelistVariable + ld b, a + ld c, PLAY_AREA_ARENA + +.loop + ld a, c + ldh [hTempPlayAreaLocation_ff9d], a + push bc + bank1call GetCardOneStageBelow + pop bc + jr c, .next + ; is not a basic card + ; compare its HP with current damage + ld a, d + push bc + call LoadCardDataToBuffer2_FromDeckIndex + pop bc + ld a, [wLoadedCard2HP] + ld [wTempAI], a + ld e, c + push bc + call GetCardDamageAndMaxHP + pop bc + ld e, a + ld a, [wTempAI] + cp e + jr c, .set_carry + jr z, .set_carry +.next + inc c + ld a, c + cp b + jr nz, .loop + + call SwapTurn + pop af + ldh [hTempPlayAreaLocation_ff9d], a + or a + ret + +.set_carry + call SwapTurn + pop af + ldh [hTempPlayAreaLocation_ff9d], a + ld a, c + scf + ret + +; returns carry if the following conditions are met: +; - arena card HP >= half max HP +; - arena card Unknown2's 4 bit is not set or +; is set but there's no evolution of card in hand/deck +; - arena card can use second attack +CheckIfArenaCardIsAtHalfHPCanEvolveAndUseSecondAttack: ; 170c9 (5:70c9) + ld a, DUELVARS_ARENA_CARD + call GetTurnDuelistVariable + ld d, a + push de + call LoadCardDataToBuffer1_FromDeckIndex + ld a, DUELVARS_ARENA_CARD_HP + call GetTurnDuelistVariable + ld d, a + ld a, [wLoadedCard1HP] + rrca + cp d + pop de + jr nc, .no_carry + + ld a, [wLoadedCard1Unknown2] + and %00010000 + jr z, .check_second_attack + ld a, d + call CheckCardEvolutionInHandOrDeck + jr c, .no_carry + +.check_second_attack + xor a ; active card + ldh [hTempPlayAreaLocation_ff9d], a + ld a, SECOND_ATTACK + ld [wSelectedAttack], a + push hl + call CheckIfSelectedAttackIsUnusable + pop hl + jr c, .no_carry + scf + ret +.no_carry + or a + ret + +; count Pokemon in the Bench that +; meet the following conditions: +; - card HP > half max HP +; - card Unknown2's 4 bit is not set or +; is set but there's no evolution of card in hand/deck +; - card can use second attack +; Outputs the number of Pokémon in bench +; that meet these requirements in a +; and returns carry if at least one is found +CountNumberOfSetUpBenchPokemon: ; 17101 (5:7101) + ldh a, [hTempPlayAreaLocation_ff9d] + ld d, a + ld a, [wSelectedAttack] + ld e, a + push de + ld a, DUELVARS_BENCH + call GetTurnDuelistVariable + lb bc, 0, 0 + push hl + +.next + inc c + pop hl + ld a, [hli] + push hl + cp $ff + jr z, .done + + ld d, a + push de + push bc + call LoadCardDataToBuffer1_FromDeckIndex + pop bc + +; compares card's current HP with max HP + ld a, c + add DUELVARS_ARENA_CARD_HP + call GetTurnDuelistVariable + ld d, a + ld a, [wLoadedCard1HP] + rrca + +; a = max HP / 2 +; d = current HP +; jumps if (current HP) <= (max HP / 2) + cp d + pop de + jr nc, .next + + ld a, [wLoadedCard1Unknown2] + and $10 + jr z, .check_second_attack + + ld a, d + push bc + call CheckCardEvolutionInHandOrDeck + pop bc + jr c, .next + +.check_second_attack + ld a, c + ldh [hTempPlayAreaLocation_ff9d], a + ld a, SECOND_ATTACK + ld [wSelectedAttack], a + push bc + push hl + call CheckIfSelectedAttackIsUnusable + pop hl + pop bc + jr c, .next + inc b + jr .next + +.done + pop hl + pop de + ld a, e + ld [wSelectedAttack], a + ld a, d + ldh [hTempPlayAreaLocation_ff9d], a + ld a, b + or a + ret z + scf + ret + +; handles AI logic to determine some selections regarding certain attacks, +; if any of these attacks were chosen to be used. +; returns carry if selection was successful, +; and no carry if unable to make one. +; outputs in hTempPlayAreaLocation_ffa1 the chosen parameter. +AISelectSpecialAttackParameters: ; 17161 (5:7161) + ld a, [wSelectedAttack] + push af + call .SelectAttackParameters + pop bc + ld a, b + ld [wSelectedAttack], a + ret + +.SelectAttackParameters: ; 1716e (5:716e) + ld a, DUELVARS_ARENA_CARD + call GetTurnDuelistVariable + call GetCardIDFromDeckIndex + ld a, e + cp MEW3 + jr z, .DevolutionBeam + cp MEWTWO3 + jr z, .EnergyAbsorption + cp MEWTWO2 + jr z, .EnergyAbsorption + cp EXEGGUTOR + jr z, .Teleport + cp ELECTRODE1 + jr z, .EnergySpike + ; fallthrough + +.no_carry + or a + ret + +.DevolutionBeam +; in case selected attack is Devolution Beam +; store in hTempPlayAreaLocation_ffa1 +; the location of card to select to devolve + ld a, [wSelectedAttack] + or a + jp z, .no_carry ; can be jr + + ld a, $01 + ldh [hTemp_ffa0], a + call LookForCardThatIsKnockedOutOnDevolution + ldh [hTempPlayAreaLocation_ffa1], a + +.set_carry_1 + scf + ret + +.EnergyAbsorption +; in case selected attack is Energy Absorption +; make list from energy cards in Discard Pile + ld a, [wSelectedAttack] + or a + jp nz, .no_carry ; can be jr + + ld a, $ff + ldh [hTempPlayAreaLocation_ffa1], a + ldh [hTempRetreatCostCards], a + +; search for Psychic energy cards in Discard Pile + ld e, PSYCHIC_ENERGY + ld a, CARD_LOCATION_DISCARD_PILE + call CheckIfAnyCardIDinLocation + ldh [hTemp_ffa0], a + farcall CreateEnergyCardListFromDiscardPile_AllEnergy + +; find any energy card different from +; the one found by CheckIfAnyCardIDinLocation. +; since using this attack requires a Psychic energy card, +; and another one is in hTemp_ffa0, +; then any other energy card would account +; for the Energy Cost of Psyburn. + ld hl, wDuelTempList +.loop_energy_cards + ld a, [hli] + cp $ff + jr z, .set_carry_2 + ld b, a + ldh a, [hTemp_ffa0] + cp b + jr z, .loop_energy_cards ; same card, keep looking + +; store the deck index of energy card found + ld a, b + ldh [hTempPlayAreaLocation_ffa1], a + ; fallthrough + +.set_carry_2 + scf + ret + +.Teleport +; in case selected attack is Teleport +; decide Bench card to switch to. + ld a, [wSelectedAttack] + or a + jp nz, .no_carry ; can be jr + call AIDecideBenchPokemonToSwitchTo + jr c, .no_carry + ldh [hTemp_ffa0], a + scf + ret + +.EnergySpike +; in case selected attack is Energy Spike +; decide basic energy card to fetch from Deck. + ld a, [wSelectedAttack] + or a + jp z, .no_carry ; can be jr + + ld a, CARD_LOCATION_DECK + ld e, LIGHTNING_ENERGY + +; if none were found in Deck, return carry... + call CheckIfAnyCardIDinLocation + ldh [hTemp_ffa0], a + jp nc, .no_carry ; can be jr + +; ...else find a suitable Play Area Pokemon to +; attach the energy card to. + call AIProcessButDontPlayEnergy_SkipEvolution + jp nc, .no_carry ; can be jr + ldh a, [hTempPlayAreaLocation_ff9d] + ldh [hTempPlayAreaLocation_ffa1], a + scf + ret + +; return carry if Pokémon at play area location +; in hTempPlayAreaLocation_ff9d does not have +; energy required for the attack index in wSelectedAttack +; or has exactly the same amount of energy needed +; input: +; [hTempPlayAreaLocation_ff9d] = play area location +; [wSelectedAttack] = attack index to check +; output: +; a = number of extra energy cards attached +CheckIfNoSurplusEnergyForAttack: ; 171fb (5:71fb) + ldh a, [hTempPlayAreaLocation_ff9d] + add DUELVARS_ARENA_CARD + call GetTurnDuelistVariable + ld d, a + ld a, [wSelectedAttack] + ld e, a + call CopyAttackDataAndDamage_FromDeckIndex + ld hl, wLoadedAttackName + ld a, [hli] + or [hl] + jr z, .not_attack + ld a, [wLoadedAttackCategory] + cp POKEMON_POWER + jr nz, .is_attack +.not_attack + scf + ret + +.is_attack + ldh a, [hTempPlayAreaLocation_ff9d] + ld e, a + call GetPlayAreaCardAttachedEnergies + bank1call HandleEnergyBurn + xor a + ld [wTempLoadedAttackEnergyCost], a + ld [wTempLoadedAttackEnergyNeededAmount], a + ld [wTempLoadedAttackEnergyNeededType], a + ld hl, wAttachedEnergies + ld de, wLoadedAttackEnergyCost + ld b, 0 + ld c, (NUM_TYPES / 2) - 1 +.loop + ; check all basic energy cards except colorless + ld a, [de] + swap a + call CalculateParticularAttachedEnergyNeeded + ld a, [de] + call CalculateParticularAttachedEnergyNeeded + inc de + dec c + jr nz, .loop + + ; colorless + ld a, [de] + swap a + and %00001111 + ld b, a + ld hl, wTempLoadedAttackEnergyCost + ld a, [wTotalAttachedEnergies] + sub [hl] + sub b + ret c ; return if not enough energy + + or a + ret nz ; return if surplus energy + + ; exactly the amount of energy needed + scf + ret + +; takes as input the energy cost of an attack for a +; particular energy, stored in the lower nibble of a +; if the attack costs some amount of this energy, the lower nibble of a != 0, +; and this amount is stored in wTempLoadedAttackEnergyCost +; also adds the amount of energy still needed +; to wTempLoadedAttackEnergyNeededAmount +; input: +; a = this energy cost of attack (lower nibble) +; [hl] = attached energy +; output: +; carry set if not enough of this energy type attached +CalculateParticularAttachedEnergyNeeded: ; 17258 (5:7258) + and %00001111 + jr nz, .check +.done + inc hl + inc b + ret + +.check + ld [wTempLoadedAttackEnergyCost], a + sub [hl] + jr z, .done + jr nc, .done + push bc + ld a, [wTempLoadedAttackEnergyCost] + ld b, a + ld a, [hl] + sub b + pop bc + ld [wTempLoadedAttackEnergyNeededAmount], a + jr .done + +; return carry if there is a card that +; can evolve a Pokémon in hand or deck. +; input: +; a = deck index of card to check; +; output: +; a = deck index of evolution in hand, if found; +; carry set if there's a card in hand that can evolve. +CheckCardEvolutionInHandOrDeck: ; 17274 (5:7274) + ld b, a + ld a, DUELVARS_ARENA_CARD + call GetTurnDuelistVariable + push af + ld [hl], b + ld e, 0 + +.loop + ld a, DUELVARS_CARD_LOCATIONS + add e + call GetTurnDuelistVariable + cp CARD_LOCATION_DECK + jr z, .deck_or_hand + cp CARD_LOCATION_HAND + jr nz, .next +.deck_or_hand + push de + ld d, e + ld e, PLAY_AREA_ARENA + call CheckIfCanEvolveInto + pop de + jr nc, .set_carry +.next + inc e + ld a, DECK_SIZE + cp e + jr nz, .loop + + ld a, DUELVARS_ARENA_CARD + call GetTurnDuelistVariable + pop af + ld [hl], a + or a + ret + +.set_carry + ld a, DUELVARS_ARENA_CARD + call GetTurnDuelistVariable + pop af + ld [hl], a + ld a, e + scf + ret + +INCLUDE "engine/duel/ai/boss_deck_set_up.asm" + +; returns carry if Pokemon at PLAY_AREA* in a +; can damage defending Pokémon with any of its attacks +; input: +; a = location of card to check +CheckIfCanDamageDefendingPokemon: ; 17383 (5:7383) + ldh [hTempPlayAreaLocation_ff9d], a + xor a ; first attack + ld [wSelectedAttack], a + call CheckIfSelectedAttackIsUnusable + jr c, .second_attack + xor a + call EstimateDamage_VersusDefendingCard + ld a, [wDamage] + or a + jr nz, .set_carry + +.second_attack + ld a, SECOND_ATTACK + ld [wSelectedAttack], a + call CheckIfSelectedAttackIsUnusable + jr c, .no_carry + ld a, $01 + call EstimateDamage_VersusDefendingCard + ld a, [wDamage] + or a + jr nz, .set_carry + +.no_carry + or a + ret +.set_carry + scf + ret + +; checks if defending Pokémon can knock out +; card at hTempPlayAreaLocation_ff9d with any of its attacks +; and if so, stores the damage to wce00 and wce01 +; sets carry if any on the attacks knocks out +; also outputs the largest damage dealt in a +; input: +; [hTempPlayAreaLocation_ff9d] = location of card to check +; output: +; a = largest damage of both attacks +; carry set if can knock out +CheckIfDefendingPokemonCanKnockOut: ; 173b1 (5:73b1) + xor a ; first attack + ld [wce00], a + ld [wce01], a + call CheckIfDefendingPokemonCanKnockOutWithAttack + jr nc, .second_attack + ld a, [wDamage] + ld [wce00], a + +.second_attack + ld a, SECOND_ATTACK + call CheckIfDefendingPokemonCanKnockOutWithAttack + jr nc, .return_if_neither_kos + ld a, [wDamage] + ld [wce01], a + jr .compare + +.return_if_neither_kos + ld a, [wce00] + or a + ret z + +.compare + ld a, [wce00] + ld b, a + ld a, [wce01] + cp b + jr nc, .set_carry + ld a, b +.set_carry + scf + ret + +; return carry if defending Pokémon can knock out +; card at hTempPlayAreaLocation_ff9d +; input: +; a = attack index +; [hTempPlayAreaLocation_ff9d] = location of card to check +CheckIfDefendingPokemonCanKnockOutWithAttack: ; 173e4 (5:73e4) + ld [wSelectedAttack], a + ldh a, [hTempPlayAreaLocation_ff9d] + push af + xor a + ldh [hTempPlayAreaLocation_ff9d], a + call SwapTurn + call CheckIfSelectedAttackIsUnusable + call SwapTurn + pop bc + ld a, b + ldh [hTempPlayAreaLocation_ff9d], a + jr c, .done + +; player's active Pokémon can use attack + ld a, [wSelectedAttack] + call EstimateDamage_FromDefendingPokemon + ldh a, [hTempPlayAreaLocation_ff9d] + add DUELVARS_ARENA_CARD_HP + call GetTurnDuelistVariable + ld hl, wDamage + sub [hl] + jr z, .set_carry + ret + +.set_carry + scf + ret + +.done + or a + ret + +; sets carry if Opponent's deck ID +; is between LEGENDARY_MOLTRES_DECK_ID (inclusive) +; and MUSCLES_FOR_BRAINS_DECK_ID (exclusive) +; these are the decks for Grandmaster/Club Master/Ronald +CheckIfOpponentHasBossDeckID: ; 17414 (5:7414) + push af + ld a, [wOpponentDeckID] + cp LEGENDARY_MOLTRES_DECK_ID + jr c, .no_carry + cp MUSCLES_FOR_BRAINS_DECK_ID + jr nc, .no_carry + pop af + scf + ret + +.no_carry + pop af + or a + ret + +; sets carry if not a boss fight +; and if hasn't received legendary cards yet +CheckIfNotABossDeckID: ; 17426 (5:7426) + call EnableSRAM + ld a, [sReceivedLegendaryCards] + call DisableSRAM + or a + jr nz, .no_carry + call CheckIfOpponentHasBossDeckID + jr nc, .set_carry +.no_carry + or a + ret + +.set_carry + scf + ret + +; probability to return carry: +; - 50% if deck AI is playing is on the list; +; - 25% for all other decks; +; - 0% for boss decks. +; used for certain decks to randomly choose +; not to play Trainer card or use PKMN Power +AIChooseRandomlyNotToDoAction: ; 1743b (5:743b) +; boss decks always use Trainer cards. + push hl + push de + call CheckIfNotABossDeckID + jr c, .check_deck + pop de + pop hl + ret + +.check_deck + ld a, [wOpponentDeckID] + cp MUSCLES_FOR_BRAINS_DECK_ID + jr z, .carry_50_percent + cp BLISTERING_POKEMON_DECK_ID + jr z, .carry_50_percent + cp WATERFRONT_POKEMON_DECK_ID + jr z, .carry_50_percent + cp BOOM_BOOM_SELFDESTRUCT_DECK_ID + jr z, .carry_50_percent + cp KALEIDOSCOPE_DECK_ID + jr z, .carry_50_percent + cp RESHUFFLE_DECK_ID + jr z, .carry_50_percent + +; carry 25 percent + ld a, 4 + call Random + cp 1 + pop de + pop hl + ret + +.carry_50_percent + ld a, 4 + call Random + cp 2 + pop de + pop hl + ret + +; checks if any bench Pokémon has same ID +; as input, and sets carry if it has more than +; half health and can use its second attack +; input: +; a = card ID to check for +; output: +; carry set if the above requirements are met +CheckForBenchIDAtHalfHPAndCanUseSecondAttack: ; 17474 (5:7474) + ld [wcdf9], a + ldh a, [hTempPlayAreaLocation_ff9d] + ld d, a + ld a, [wSelectedAttack] + ld e, a + push de + ld a, DUELVARS_ARENA_CARD + call GetTurnDuelistVariable + lb bc, 0, PLAY_AREA_ARENA + push hl + +.loop + inc c + pop hl + ld a, [hli] + push hl + cp $ff + jr z, .done + ld d, a + push de + push bc + call LoadCardDataToBuffer1_FromDeckIndex + pop bc + ld a, c + add DUELVARS_ARENA_CARD_HP + call GetTurnDuelistVariable + ld d, a + ld a, [wLoadedCard1HP] + rrca + cp d + pop de + jr nc, .loop + ; half max HP < current HP + ld a, [wLoadedCard1ID] + ld hl, wcdf9 + cp [hl] + jr nz, .loop + + ld a, c + ldh [hTempPlayAreaLocation_ff9d], a + ld a, SECOND_ATTACK + ld [wSelectedAttack], a + push bc + call CheckIfSelectedAttackIsUnusable + pop bc + jr c, .loop + inc b +.done + pop hl + pop de + ld a, e + ld [wSelectedAttack], a + ld a, d + ldh [hTempPlayAreaLocation_ff9d], a + ld a, b + or a + ret z + scf + ret + +; add 5 to wPlayAreaEnergyAIScore AI score corresponding to all cards +; in bench that have same ID as register a +; input: +; a = card ID to look for +RaiseAIScoreToAllMatchingIDsInBench: ; 174cd (5:74cd) + ld d, a + ld a, DUELVARS_BENCH + call GetTurnDuelistVariable + ld e, 0 +.loop + inc e + ld a, [hli] + cp $ff + ret z + push de + call GetCardIDFromDeckIndex + ld a, e + pop de + cp d + jr nz, .loop + ld c, e + ld b, $00 + push hl + ld hl, wPlayAreaEnergyAIScore + add hl, bc + ld a, 5 + add [hl] + ld [hl], a + pop hl + jr .loop + +; goes through each play area Pokémon, and +; for all cards of the same ID, determine which +; card has highest value calculated from Func_17583 +; the card with highest value gets increased wPlayAreaEnergyAIScore +; while all others get decreased wPlayAreaEnergyAIScore +Func_174f2: ; 174f2 (5:74f2) + ld a, MAX_PLAY_AREA_POKEMON + ld hl, wcdfa + call ClearMemory_Bank5 + ld a, DUELVARS_BENCH + call GetTurnDuelistVariable + ld e, 0 + +.loop_play_area + push hl + ld a, MAX_PLAY_AREA_POKEMON + ld hl, wcdea + call ClearMemory_Bank5 + pop hl + inc e + ld a, [hli] + cp $ff + ret z + + ld [wcdf9], a + push de + push hl + +; checks wcdfa + play area location in e +; if != 0, go to next in play area + ld d, $00 + ld hl, wcdfa + add hl, de + ld a, [hl] + or a + pop hl + pop de + jr nz, .loop_play_area + +; loads wcdf9 with card ID +; and call Func_17583 + push de + ld a, [wcdf9] + call GetCardIDFromDeckIndex + ld a, e + ld [wcdf9], a + pop de + push hl + push de + call Func_17583 + +; check play area Pokémon ahead +; if there is a card with the same ID, +; call Func_17583 for it as well +.loop_1 + inc e + ld a, [hli] + cp $ff + jr z, .check_if_repeated_id + push de + call GetCardIDFromDeckIndex + ld a, [wcdf9] + cp e + pop de + jr nz, .loop_1 + call Func_17583 + jr .loop_1 + +; if there are more than 1 of the same ID +; in play area, iterate bench backwards +; and determines which card has highest +; score in wcdea +.check_if_repeated_id + call Func_175a8 + jr c, .next + lb bc, 0, 0 + ld hl, wcdea + MAX_BENCH_POKEMON + ld d, MAX_PLAY_AREA_POKEMON +.loop_2 + dec d + jr z, .asm_17560 + ld a, [hld] + cp b + jr c, .loop_2 + ld b, a + ld c, d + jr .loop_2 + +; c = play area location of highest score +; decrease wPlayAreaEnergyAIScore score for all cards with same ID +; except for the one with highest score +; increase wPlayAreaEnergyAIScore score for card with highest ID +.asm_17560 + ld hl, wPlayAreaEnergyAIScore + ld de, wcdea + ld b, PLAY_AREA_ARENA +.loop_3 + ld a, c + cp b + jr z, .card_with_highest + ld a, [de] + or a + jr z, .check_next +; decrease score + dec [hl] + jr .check_next +.card_with_highest +; increase score + inc [hl] +.check_next + inc b + ld a, MAX_PLAY_AREA_POKEMON + cp b + jr z, .next + inc de + inc hl + jr .loop_3 + +.next + pop de + pop hl + jp .loop_play_area + +; loads wcdea + play area location in e +; with energy * 2 + $80 - floor(dam / 10) +; loads wcdfa + play area location in e +; with $01 +Func_17583: ; 17583 (5:7583) + push hl + push de + call GetCardDamageAndMaxHP + call CalculateByteTensDigit + ld b, a + push bc + call CountNumberOfEnergyCardsAttached + pop bc + sla a + add $80 + sub b + pop de + push de + ld d, $00 + ld hl, wcdea + add hl, de + ld [hl], a + ld hl, wcdfa + add hl, de + ld [hl], $01 + pop de + pop hl + ret + +; counts how many play area locations in wcdea +; are != 0, and outputs result in a +; also returns carry if result is < 2 +Func_175a8: ; 175a8 (5:75a8) + ld hl, wcdea + ld d, $00 + ld e, MAX_PLAY_AREA_POKEMON + 1 +.loop + dec e + jr z, .done + ld a, [hli] + or a + jr z, .loop + inc d + jr .loop +.done + ld a, d + cp 2 + ret + +; handle how AI scores giving out Energy Cards +; when using Legendary Articuno deck +HandleLegendaryArticunoEnergyScoring: ; 175bd (5:75bd) + ld a, [wOpponentDeckID] + cp LEGENDARY_ARTICUNO_DECK_ID + jr z, .articuno_deck + ret +.articuno_deck + call ScoreLegendaryArticunoCards + ret diff --git a/src/engine/duel/ai/damage_calculation.asm b/src/engine/duel/ai/damage_calculation.asm new file mode 100644 index 0000000..97c24b6 --- /dev/null +++ b/src/engine/duel/ai/damage_calculation.asm @@ -0,0 +1,450 @@ +; stores in wDamage, wAIMinDamage and wAIMaxDamage the calculated damage +; done to the defending Pokémon by a given card and attack +; input: +; a = attack index to take into account +; [hTempPlayAreaLocation_ff9d] = location of attacking card to consider +EstimateDamage_VersusDefendingCard: ; 143e5 (5:43e5) + ld [wSelectedAttack], a + ld e, a + ldh a, [hTempPlayAreaLocation_ff9d] + add DUELVARS_ARENA_CARD + call GetTurnDuelistVariable + ld d, a + call CopyAttackDataAndDamage_FromDeckIndex + ld a, [wLoadedAttackCategory] + cp POKEMON_POWER + jr nz, .is_attack + +; is a Pokémon Power +; set wDamage, wAIMinDamage and wAIMaxDamage to zero + ld hl, wDamage + xor a + ld [hli], a + ld [hl], a + ld [wAIMinDamage], a + ld [wAIMaxDamage], a + ld e, a + ld d, a + ret + +.is_attack +; set wAIMinDamage and wAIMaxDamage to damage of attack +; these values take into account the range of damage +; that the attack can span (e.g. min and max number of hits) + ld a, [wDamage] + ld [wAIMinDamage], a + ld [wAIMaxDamage], a + ld a, EFFECTCMDTYPE_AI + call TryExecuteEffectCommandFunction + ld a, [wAIMinDamage] + ld hl, wAIMaxDamage + or [hl] + jr nz, .calculation + ld a, [wDamage] + ld [wAIMinDamage], a + ld [wAIMaxDamage], a + +.calculation +; if temp. location is active, damage calculation can be done directly... + ldh a, [hTempPlayAreaLocation_ff9d] + or a + jr z, CalculateDamage_VersusDefendingPokemon + +; ...otherwise substatuses need to be temporarily reset to account +; for the switching, to obtain the right damage calculation... + ; reset substatus1 + ld a, DUELVARS_ARENA_CARD_SUBSTATUS1 + call GetTurnDuelistVariable + push af + push hl + ld [hl], $00 + ; reset substatus2 + ld l, DUELVARS_ARENA_CARD_SUBSTATUS2 + ld a, [hl] + push af + push hl + ld [hl], $00 + ; reset changed resistance + ld l, DUELVARS_ARENA_CARD_CHANGED_RESISTANCE + ld a, [hl] + push af + push hl + ld [hl], $00 + call CalculateDamage_VersusDefendingPokemon +; ...and subsequently recovered to continue the duel normally + pop hl + pop af + ld [hl], a + pop hl + pop af + ld [hl], a + pop hl + pop af + ld [hl], a + ret + +; calculates the damage that will be dealt to the player's active card +; using the card that is located in hTempPlayAreaLocation_ff9d +; taking into account weakness/resistance/pluspowers/defenders/etc +; and outputs the result capped at a max of $ff +; input: +; [wAIMinDamage] = base damage +; [wAIMaxDamage] = base damage +; [wDamage] = base damage +; [hTempPlayAreaLocation_ff9d] = turn holder's card location as the attacker +CalculateDamage_VersusDefendingPokemon: ; 14453 (5:4453) + ld hl, wAIMinDamage + call _CalculateDamage_VersusDefendingPokemon + ld hl, wAIMaxDamage + call _CalculateDamage_VersusDefendingPokemon + ld hl, wDamage +; fallthrough + +_CalculateDamage_VersusDefendingPokemon: ; 14462 (5:4462) + ld e, [hl] + ld d, $00 + push hl + + ; load this card's data + ldh a, [hTempPlayAreaLocation_ff9d] + add DUELVARS_ARENA_CARD + call GetTurnDuelistVariable + call LoadCardDataToBuffer2_FromDeckIndex + ld a, [wLoadedCard2ID] + ld [wTempTurnDuelistCardID], a + + ; load player's arena card data + call SwapTurn + ld a, DUELVARS_ARENA_CARD + call GetTurnDuelistVariable + call LoadCardDataToBuffer2_FromDeckIndex + ld a, [wLoadedCard2ID] + ld [wTempNonTurnDuelistCardID], a + call SwapTurn + + push de + call HandleNoDamageOrEffectSubstatus + pop de + jr nc, .vulnerable + ; invulnerable to damage + ld de, $0 + jr .done +.vulnerable + ldh a, [hTempPlayAreaLocation_ff9d] + or a + call z, HandleDoubleDamageSubstatus + ; skips the weak/res checks if unaffected. + bit UNAFFECTED_BY_WEAKNESS_RESISTANCE_F, d + res UNAFFECTED_BY_WEAKNESS_RESISTANCE_F, d + jr nz, .not_resistant + +; handle weakness + ldh a, [hTempPlayAreaLocation_ff9d] + call GetPlayAreaCardColor + call TranslateColorToWR + ld b, a + call SwapTurn + call GetArenaCardWeakness + call SwapTurn + and b + jr z, .not_weak + ; double de + sla e + rl d + +.not_weak +; handle resistance + call SwapTurn + call GetArenaCardResistance + call SwapTurn + and b + jr z, .not_resistant + ld hl, -30 + add hl, de + ld e, l + ld d, h + +.not_resistant + ; apply pluspower and defender boosts + ldh a, [hTempPlayAreaLocation_ff9d] + add CARD_LOCATION_ARENA + ld b, a + call ApplyAttachedPluspower + call SwapTurn + ld b, CARD_LOCATION_ARENA + call ApplyAttachedDefender + call HandleDamageReduction + ; test if de underflowed + bit 7, d + jr z, .no_underflow + ld de, $0 + +.no_underflow + ld a, DUELVARS_ARENA_CARD_STATUS + call GetTurnDuelistVariable + and DOUBLE_POISONED + jr z, .not_poisoned + ld c, 20 + and DOUBLE_POISONED & (POISONED ^ $ff) + jr nz, .add_poison + ld c, 10 +.add_poison + ld a, c + add e + ld e, a + ld a, $00 + adc d + ld d, a +.not_poisoned + call SwapTurn + +.done + pop hl + ld [hl], e + ld a, d + or a + ret z + ; cap damage + ld a, $ff + ld [hl], a + ret + +; stores in wDamage, wAIMinDamage and wAIMaxDamage the calculated damage +; done to the Pokémon at hTempPlayAreaLocation_ff9d +; by the defending Pokémon, using the attack index at a +; input: +; a = attack index +; [hTempPlayAreaLocation_ff9d] = location of card to calculate +; damage as the receiver +EstimateDamage_FromDefendingPokemon: ; 1450b (5:450b) + call SwapTurn + ld [wSelectedAttack], a + ld e, a + ld a, DUELVARS_ARENA_CARD + call GetTurnDuelistVariable + ld d, a + call CopyAttackDataAndDamage_FromDeckIndex + call SwapTurn + ld a, [wLoadedAttackCategory] + cp POKEMON_POWER + jr nz, .is_attack + +; is a Pokémon Power +; set wDamage, wAIMinDamage and wAIMaxDamage to zero + ld hl, wDamage + xor a + ld [hli], a + ld [hl], a + ld [wAIMinDamage], a + ld [wAIMaxDamage], a + ld e, a + ld d, a + ret + +.is_attack +; set wAIMinDamage and wAIMaxDamage to damage of attack +; these values take into account the range of damage +; that the attack can span (e.g. min and max number of hits) + ld a, [wDamage] + ld [wAIMinDamage], a + ld [wAIMaxDamage], a + call SwapTurn + ldh a, [hTempPlayAreaLocation_ff9d] + push af + xor a + ldh [hTempPlayAreaLocation_ff9d], a + ld a, EFFECTCMDTYPE_AI + call TryExecuteEffectCommandFunction + pop af + ldh [hTempPlayAreaLocation_ff9d], a + call SwapTurn + ld a, [wAIMinDamage] + ld hl, wAIMaxDamage + or [hl] + jr nz, .calculation + ld a, [wDamage] + ld [wAIMinDamage], a + ld [wAIMaxDamage], a + +.calculation +; if temp. location is active, damage calculation can be done directly... + ldh a, [hTempPlayAreaLocation_ff9d] + or a + jr z, CalculateDamage_FromDefendingPokemon + +; ...otherwise substatuses need to be temporarily reset to account +; for the switching, to obtain the right damage calculation... + ld a, DUELVARS_ARENA_CARD_SUBSTATUS1 + call GetTurnDuelistVariable + push af + push hl + ld [hl], $00 + ; reset substatus2 + ld l, DUELVARS_ARENA_CARD_SUBSTATUS2 + ld a, [hl] + push af + push hl + ld [hl], $00 + ; reset changed resistance + ld l, DUELVARS_ARENA_CARD_CHANGED_RESISTANCE + ld a, [hl] + push af + push hl + ld [hl], $00 + call CalculateDamage_FromDefendingPokemon +; ...and subsequently recovered to continue the duel normally + pop hl + pop af + ld [hl], a + pop hl + pop af + ld [hl], a + pop hl + pop af + ld [hl], a + ret + +; similar to CalculateDamage_VersusDefendingPokemon but reversed, +; calculating damage of the defending Pokémon versus +; the card located in hTempPlayAreaLocation_ff9d +; taking into account weakness/resistance/pluspowers/defenders/etc +; and poison damage for two turns +; and outputs the result capped at a max of $ff +; input: +; [wAIMinDamage] = base damage +; [wAIMaxDamage] = base damage +; [wDamage] = base damage +; [hTempPlayAreaLocation_ff9d] = location of card to calculate +; damage as the receiver +CalculateDamage_FromDefendingPokemon: ; 1458c (5:458c) + ld hl, wAIMinDamage + call .CalculateDamage + ld hl, wAIMaxDamage + call .CalculateDamage + ld hl, wDamage + ; fallthrough + +.CalculateDamage ; 1459b (5:459b) + ld e, [hl] + ld d, $00 + push hl + + ; load player active card's data + call SwapTurn + ld a, DUELVARS_ARENA_CARD + call GetTurnDuelistVariable + call LoadCardDataToBuffer2_FromDeckIndex + ld a, [wLoadedCard2ID] + ld [wTempTurnDuelistCardID], a + call SwapTurn + + ; load opponent's card data + ldh a, [hTempPlayAreaLocation_ff9d] + add DUELVARS_ARENA_CARD + call GetTurnDuelistVariable + call LoadCardDataToBuffer2_FromDeckIndex + ld a, [wLoadedCard2ID] + ld [wTempNonTurnDuelistCardID], a + + call SwapTurn + call HandleDoubleDamageSubstatus + bit UNAFFECTED_BY_WEAKNESS_RESISTANCE_F, d + res UNAFFECTED_BY_WEAKNESS_RESISTANCE_F, d + jr nz, .not_resistant + +; handle weakness + call GetArenaCardColor + call TranslateColorToWR + ld b, a + call SwapTurn + ldh a, [hTempPlayAreaLocation_ff9d] + or a + jr nz, .bench_weak + ld a, DUELVARS_ARENA_CARD_CHANGED_WEAKNESS + call GetTurnDuelistVariable + or a + jr nz, .unchanged_weak + +.bench_weak + ldh a, [hTempPlayAreaLocation_ff9d] + add DUELVARS_ARENA_CARD + call GetTurnDuelistVariable + call LoadCardDataToBuffer2_FromDeckIndex + ld a, [wLoadedCard2Weakness] +.unchanged_weak + and b + jr z, .not_weak + ; double de + sla e + rl d + +.not_weak +; handle resistance + ldh a, [hTempPlayAreaLocation_ff9d] + or a + jr nz, .bench_res + ld a, DUELVARS_ARENA_CARD_CHANGED_RESISTANCE + call GetTurnDuelistVariable + or a + jr nz, .unchanged_res + +.bench_res + ldh a, [hTempPlayAreaLocation_ff9d] + add DUELVARS_ARENA_CARD + call GetTurnDuelistVariable + call LoadCardDataToBuffer2_FromDeckIndex + ld a, [wLoadedCard2Resistance] +.unchanged_res + and b + jr z, .not_resistant + ld hl, -30 + add hl, de + ld e, l + ld d, h + +.not_resistant + ; apply pluspower and defender boosts + call SwapTurn + ld b, CARD_LOCATION_ARENA + call ApplyAttachedPluspower + call SwapTurn + ldh a, [hTempPlayAreaLocation_ff9d] + add CARD_LOCATION_ARENA + ld b, a + call ApplyAttachedDefender + ldh a, [hTempPlayAreaLocation_ff9d] + or a + call z, HandleDamageReduction + bit 7, d + jr z, .no_underflow + ld de, $0 + +.no_underflow + ldh a, [hTempPlayAreaLocation_ff9d] + or a + jr nz, .done + ld a, DUELVARS_ARENA_CARD_STATUS + call GetTurnDuelistVariable + and DOUBLE_POISONED + jr z, .done + ld c, 40 + and DOUBLE_POISONED & (POISONED ^ $ff) + jr nz, .add_poison + ld c, 20 +.add_poison + ld a, c + add e + ld e, a + ld a, $00 + adc d + ld d, a + +.done + pop hl + ld [hl], e + ld a, d + or a + ret z + ld a, $ff + ld [hl], a + ret diff --git a/src/engine/duel/ai/deck_ai.asm b/src/engine/duel/ai/deck_ai.asm new file mode 100644 index 0000000..b46de01 --- /dev/null +++ b/src/engine/duel/ai/deck_ai.asm @@ -0,0 +1,82 @@ +; AI card retreat score bonus +; when the AI retreat routine runs through the Bench to choose +; a Pokemon to switch to, it looks up in this list and if +; a card ID matches, applies a retreat score bonus to this card. +; positive (negative) means more (less) likely to switch to this card. +ai_retreat: MACRO + db \1 ; card ID + db $80 + \2 ; retreat score (ranges between -128 and 127) +ENDM + +; AI card energy attach score bonus +; when the AI energy attachment routine runs through the Play Area to choose +; a Pokemon to attach an energy card, it looks up in this list and if +; a card ID matches, skips this card if the maximum number of energy +; cards attached has been reached. If it hasn't been reached, additionally +; applies a positive (or negative) AI score to attach energy to this card. +ai_energy: MACRO + db \1 ; card ID + db \2 ; maximum number of attached cards + db $80 + \3 ; energy score (ranges between -128 and 127) +ENDM + +; stores in WRAM pointer to data in argument +; e.g. store_list_pointer wSomeListPointer, SomeData +store_list_pointer: MACRO + ld hl, \1 + ld de, \2 + ld [hl], e + inc hl + ld [hl], d +ENDM + +; deck AIs are specialized to work on a given deck ID. +; they decide what happens during a turn, what Pokemon cards +; to pick during the start of the duel, etc. +; the different scenarios these are used are listed in AIACTION_* constants. +; each of these have a pointer table with the following structure: +; dw .do_turn : never called; +; +; dw .do_turn : called to handle the main turn logic, from the beginning +; of the turn up to the attack (or lack thereof); +; +; dw .start_duel : called at the start of the duel to initialize some +; variables and optionally set up CPU hand and deck; +; +; dw .forced_switch : logic to determine what Pokemon to pick when there's +; an effect that forces AI to switch to Bench card; +; +; dw .ko_switch : logic for picking which card to use after a KO; +; +; dw .take_prize : logic to decide which prize card to pick. + +; optionally, decks can also declare card lists that will add +; more specialized logic during various generic AI routines, +; and read during the .start_duel routines. +; the pointers to these lists are stored in memory: +; wAICardListAvoidPrize : list of cards to avoid being placed as prize; +; wAICardListArenaPriority : priority list of Arena card at duel start; +; wAICardListBenchPriority : priority list of Bench cards at duel start; +; wAICardListPlayFromHandPriority : priority list of cards to play from hand; +; wAICardListRetreatBonus : scores given to certain cards for retreat; +; wAICardListEnergyBonus : max number of energy cards and card scores. + +INCLUDE "engine/duel/ai/decks/general.asm" +INCLUDE "engine/duel/ai/decks/sams_practice.asm" +INCLUDE "engine/duel/ai/decks/general_no_retreat.asm" +INCLUDE "engine/duel/ai/decks/legendary_moltres.asm" +INCLUDE "engine/duel/ai/decks/legendary_zapdos.asm" +INCLUDE "engine/duel/ai/decks/legendary_articuno.asm" +INCLUDE "engine/duel/ai/decks/legendary_dragonite.asm" +INCLUDE "engine/duel/ai/decks/first_strike.asm" +INCLUDE "engine/duel/ai/decks/rock_crusher.asm" +INCLUDE "engine/duel/ai/decks/go_go_rain_dance.asm" +INCLUDE "engine/duel/ai/decks/zapping_selfdestruct.asm" +INCLUDE "engine/duel/ai/decks/flower_power.asm" +INCLUDE "engine/duel/ai/decks/strange_psyshock.asm" +INCLUDE "engine/duel/ai/decks/wonders_of_science.asm" +INCLUDE "engine/duel/ai/decks/fire_charge.asm" +INCLUDE "engine/duel/ai/decks/im_ronald.asm" +INCLUDE "engine/duel/ai/decks/powerful_ronald.asm" +INCLUDE "engine/duel/ai/decks/invincible_ronald.asm" +INCLUDE "engine/duel/ai/decks/legendary_ronald.asm" diff --git a/src/engine/duel/ai/decks/fire_charge.asm b/src/engine/duel/ai/decks/fire_charge.asm new file mode 100644 index 0000000..f5b347b --- /dev/null +++ b/src/engine/duel/ai/decks/fire_charge.asm @@ -0,0 +1,80 @@ +AIActionTable_FireCharge: ; 15232 (5:5232) + dw .do_turn ; unused + dw .do_turn + dw .start_duel + dw .forced_switch + dw .ko_switch + dw .take_prize + +.do_turn ; 1523e (5:523e) + call AIMainTurnLogic + ret + +.start_duel ; 15242 (5:5242) + call InitAIDuelVars + call .store_list_pointers + call SetUpBossStartingHandAndDeck + call TrySetUpBossStartingPlayArea + ret nc + call AIPlayInitialBasicCards + ret + +.forced_switch ; 15253 (5:5253) + call AIDecideBenchPokemonToSwitchTo + ret + +.ko_switch ; 15257 (5:5257) + call AIDecideBenchPokemonToSwitchTo + ret + +.take_prize ; 1525b (5:525b) + call AIPickPrizeCards + ret + +.list_arena ; 1525f (5:525f) + db JIGGLYPUFF3 + db CHANSEY + db TAUROS + db MAGMAR1 + db JIGGLYPUFF1 + db GROWLITHE + db $00 + +.list_bench ; 15266 (5:5266) + db JIGGLYPUFF3 + db CHANSEY + db GROWLITHE + db MAGMAR1 + db JIGGLYPUFF1 + db TAUROS + db $00 + +.list_retreat ; 1526e (5:526e) + ai_retreat JIGGLYPUFF1, -1 + ai_retreat CHANSEY, -1 + ai_retreat GROWLITHE, -1 + db $00 + +.list_energy ; 15274 (5:5274) + ai_energy GROWLITHE, 3, +0 + ai_energy ARCANINE2, 4, +0 + ai_energy MAGMAR1, 3, +0 + ai_energy JIGGLYPUFF1, 3, +0 + ai_energy JIGGLYPUFF3, 2, +0 + ai_energy WIGGLYTUFF, 3, +0 + ai_energy CHANSEY, 4, +0 + ai_energy TAUROS, 3, +0 + db $00 + +.list_prize ; 1528d (5:528d) + db GAMBLER + db $00 + +.store_list_pointers ; 1528f (5:528f) + store_list_pointer wAICardListAvoidPrize, .list_prize + store_list_pointer wAICardListArenaPriority, .list_arena + store_list_pointer wAICardListBenchPriority, .list_bench + store_list_pointer wAICardListPlayFromHandPriority, .list_bench + ; missing store_list_pointer wAICardListRetreatBonus, .list_retreat + store_list_pointer wAICardListEnergyBonus, .list_energy + ret diff --git a/src/engine/duel/ai/decks/first_strike.asm b/src/engine/duel/ai/decks/first_strike.asm new file mode 100644 index 0000000..2e636e1 --- /dev/null +++ b/src/engine/duel/ai/decks/first_strike.asm @@ -0,0 +1,76 @@ +AIActionTable_FirstStrike: ; 14e89 (5:4e89) + dw .do_turn ; unused + dw .do_turn + dw .start_duel + dw .forced_switch + dw .ko_switch + dw .take_prize + +.do_turn ; 14e95 (5:4e95) + call AIMainTurnLogic + ret + +.start_duel ; 14e99 (5:4e99) + call InitAIDuelVars + call .store_list_pointers + call SetUpBossStartingHandAndDeck + call TrySetUpBossStartingPlayArea + ret nc + call AIPlayInitialBasicCards + ret + +.forced_switch ; 14eaa (5:4eaa) + call AIDecideBenchPokemonToSwitchTo + ret + +.ko_switch ; 14eae (5:4eae) + call AIDecideBenchPokemonToSwitchTo + ret + +.take_prize ; 14eb2 (5:4eb2) + call AIPickPrizeCards + ret + +.list_arena ; 14eb6 (5:1eb6) + db HITMONCHAN + db MACHOP + db HITMONLEE + db MANKEY + db $00 + +.list_bench ; 14ebb (5:1ebb) + db MACHOP + db HITMONLEE + db HITMONCHAN + db MANKEY + db $00 + +.list_retreat ; 14ec0 (5:1ec0) + ai_retreat MACHOP, -1 + ai_retreat MACHOKE, -1 + ai_retreat MANKEY, -2 + db $00 + +.list_energy ; 14ec7 (5:1ec7) + ai_energy MACHOP, 3, +0 + ai_energy MACHOKE, 4, +0 + ai_energy MACHAMP, 4, -1 + ai_energy HITMONCHAN, 3, +0 + ai_energy HITMONLEE, 3, +0 + ai_energy MANKEY, 2, -1 + ai_energy PRIMEAPE, 3, -1 + db $00 + +.list_prize ; 14edd (5:1edd) + db HITMONLEE + db HITMONCHAN + db $00 + +.store_list_pointers ; 14ee0 (5:4ee0) + store_list_pointer wAICardListAvoidPrize, .list_prize + store_list_pointer wAICardListArenaPriority, .list_arena + store_list_pointer wAICardListBenchPriority, .list_bench + store_list_pointer wAICardListPlayFromHandPriority, .list_bench + ; missing store_list_pointer wAICardListRetreatBonus, .list_retreat + store_list_pointer wAICardListEnergyBonus, .list_energy + ret diff --git a/src/engine/duel/ai/decks/flower_power.asm b/src/engine/duel/ai/decks/flower_power.asm new file mode 100644 index 0000000..4d423a3 --- /dev/null +++ b/src/engine/duel/ai/decks/flower_power.asm @@ -0,0 +1,75 @@ +AIActionTable_FlowerPower: ; 1509b (5:509b) + dw .do_turn ; unused + dw .do_turn + dw .start_duel + dw .forced_switch + dw .ko_switch + dw .take_prize + +.do_turn ; 150a7 (5:50a7) + call AIMainTurnLogic + ret + +.start_duel ; 150ab (5:50ab) + call InitAIDuelVars + call .store_list_pointers + call SetUpBossStartingHandAndDeck + call TrySetUpBossStartingPlayArea + ret nc + call AIPlayInitialBasicCards + ret + +.forced_switch ; 150bc (5:50bc) + call AIDecideBenchPokemonToSwitchTo + ret + +.ko_switch ; 150c0 (5:50c0) + call AIDecideBenchPokemonToSwitchTo + ret + +.take_prize ; 150c4 (5:50c4) + call AIPickPrizeCards + ret + +.list_arena ; 150c8 (5:50c8) + db ODDISH + db EXEGGCUTE + db BULBASAUR + db $00 + +.list_bench ; 150cc (5:50cc) + db BULBASAUR + db EXEGGCUTE + db ODDISH + db $00 + +.list_retreat ; 150cf (5:50cf) + ai_retreat GLOOM, -2 + ai_retreat VILEPLUME, -2 + ai_retreat BULBASAUR, -2 + ai_retreat IVYSAUR, -2 + db $00 + +.list_energy ; 150d9 (5:50d9) + ai_energy BULBASAUR, 3, +0 + ai_energy IVYSAUR, 4, +0 + ai_energy VENUSAUR2, 4, +0 + ai_energy ODDISH, 2, +0 + ai_energy GLOOM, 3, -1 + ai_energy VILEPLUME, 3, -1 + ai_energy EXEGGCUTE, 3, +0 + ai_energy EXEGGUTOR, 22, +0 + db $00 + +.list_prize ; 150f2 (5:50f2) + db VENUSAUR2 + db $00 + +.store_list_pointers ; 150f4 (5:50f4) + store_list_pointer wAICardListAvoidPrize, .list_prize + store_list_pointer wAICardListArenaPriority, .list_arena + store_list_pointer wAICardListBenchPriority, .list_bench + store_list_pointer wAICardListPlayFromHandPriority, .list_bench + ; missing store_list_pointer wAICardListRetreatBonus, .list_retreat + store_list_pointer wAICardListEnergyBonus, .list_energy + ret diff --git a/src/engine/duel/ai/decks/general.asm b/src/engine/duel/ai/decks/general.asm new file mode 100644 index 0000000..039e101 --- /dev/null +++ b/src/engine/duel/ai/decks/general.asm @@ -0,0 +1,194 @@ +; AI logic used by general decks +AIActionTable_GeneralDecks: ; 14668 (05:4668) + dw .do_turn ; unused + dw .do_turn + dw .start_duel + dw .forced_switch + dw .ko_switch + dw .take_prize + +.do_turn ; 14674 (5:4674) + call AIMainTurnLogic + ret + +.start_duel ; 14678 (5:4678) + call InitAIDuelVars + call AIPlayInitialBasicCards + ret + +.forced_switch ; 1467f (5:467f) + call AIDecideBenchPokemonToSwitchTo + ret + +.ko_switch ; 14683 (5:4683) + call AIDecideBenchPokemonToSwitchTo + ret + +.take_prize: ; 14687 (5:4687) + call AIPickPrizeCards + ret + +; handle AI routines for a whole turn +AIMainTurnLogic: ; 1468b (5:468b) +; initialize variables + call InitAITurnVars + ld a, AI_TRAINER_CARD_PHASE_01 + call AIProcessHandTrainerCards + farcall HandleAIAntiMewtwoDeckStrategy + jp nc, .try_attack +; handle Pkmn Powers + farcall HandleAIGoGoRainDanceEnergy + farcall HandleAIDamageSwap + farcall HandleAIPkmnPowers + ret c ; return if turn ended + farcall HandleAICowardice +; process Trainer cards +; phase 2 through 4. + ld a, AI_TRAINER_CARD_PHASE_02 + call AIProcessHandTrainerCards + ld a, AI_TRAINER_CARD_PHASE_03 + call AIProcessHandTrainerCards + ld a, AI_TRAINER_CARD_PHASE_04 + call AIProcessHandTrainerCards +; play Pokemon from hand + call AIDecidePlayPokemonCard + ret c ; return if turn ended +; process Trainer cards +; phase 5 through 12. + ld a, AI_TRAINER_CARD_PHASE_05 + call AIProcessHandTrainerCards + ld a, AI_TRAINER_CARD_PHASE_06 + call AIProcessHandTrainerCards + ld a, AI_TRAINER_CARD_PHASE_07 + call AIProcessHandTrainerCards + ld a, AI_TRAINER_CARD_PHASE_08 + call AIProcessHandTrainerCards + call AIProcessRetreat + ld a, AI_TRAINER_CARD_PHASE_10 + call AIProcessHandTrainerCards + ld a, AI_TRAINER_CARD_PHASE_11 + call AIProcessHandTrainerCards + ld a, AI_TRAINER_CARD_PHASE_12 + call AIProcessHandTrainerCards +; play Energy card if possible + ld a, [wAlreadyPlayedEnergy] + or a + jr nz, .skip_energy_attach_1 + call AIProcessAndTryToPlayEnergy +.skip_energy_attach_1 +; play Pokemon from hand again + call AIDecidePlayPokemonCard +; handle Pkmn Powers again + farcall HandleAIDamageSwap + farcall HandleAIPkmnPowers + ret c ; return if turn ended + farcall HandleAIGoGoRainDanceEnergy + ld a, AI_ENERGY_TRANS_ATTACK + farcall HandleAIEnergyTrans +; process Trainer cards phases 13 and 15 + ld a, AI_TRAINER_CARD_PHASE_13 + call AIProcessHandTrainerCards + ld a, AI_TRAINER_CARD_PHASE_15 + call AIProcessHandTrainerCards +; if used Professor Oak, process new hand +; if not, then proceed to attack. + ld a, [wPreviousAIFlags] + and AI_FLAG_USED_PROFESSOR_OAK + jr z, .try_attack + ld a, AI_TRAINER_CARD_PHASE_01 + call AIProcessHandTrainerCards + ld a, AI_TRAINER_CARD_PHASE_02 + call AIProcessHandTrainerCards + ld a, AI_TRAINER_CARD_PHASE_03 + call AIProcessHandTrainerCards + ld a, AI_TRAINER_CARD_PHASE_04 + call AIProcessHandTrainerCards + call AIDecidePlayPokemonCard + ret c ; return if turn ended + ld a, AI_TRAINER_CARD_PHASE_05 + call AIProcessHandTrainerCards + ld a, AI_TRAINER_CARD_PHASE_06 + call AIProcessHandTrainerCards + ld a, AI_TRAINER_CARD_PHASE_07 + call AIProcessHandTrainerCards + ld a, AI_TRAINER_CARD_PHASE_08 + call AIProcessHandTrainerCards + call AIProcessRetreat + ld a, AI_TRAINER_CARD_PHASE_10 + call AIProcessHandTrainerCards + ld a, AI_TRAINER_CARD_PHASE_11 + call AIProcessHandTrainerCards + ld a, AI_TRAINER_CARD_PHASE_12 + call AIProcessHandTrainerCards + ld a, [wAlreadyPlayedEnergy] + or a + jr nz, .skip_energy_attach_2 + call AIProcessAndTryToPlayEnergy +.skip_energy_attach_2 + call AIDecidePlayPokemonCard + farcall HandleAIDamageSwap + farcall HandleAIPkmnPowers + ret c ; return if turn ended + farcall HandleAIGoGoRainDanceEnergy + ld a, AI_ENERGY_TRANS_ATTACK + farcall HandleAIEnergyTrans + ld a, AI_TRAINER_CARD_PHASE_13 + call AIProcessHandTrainerCards + ; skip AI_TRAINER_CARD_PHASE_15 +.try_attack + ld a, AI_ENERGY_TRANS_TO_BENCH + farcall HandleAIEnergyTrans +; attack if possible, if not, +; finish turn without attacking. + call AIProcessAndTryToUseAttack + ret c ; return if AI attacked + ld a, OPPACTION_FINISH_NO_ATTACK + bank1call AIMakeDecision + ret + +; handles AI retreating logic +AIProcessRetreat: ; 14786 (5:4786) + ld a, [wAIRetreatedThisTurn] + or a + ret nz ; return, already retreated this turn + + call AIDecideWhetherToRetreat + ret nc ; return if not retreating + + call AIDecideBenchPokemonToSwitchTo + ret c ; return if no Bench Pokemon + +; store Play Area to retreat to and +; set wAIRetreatedThisTurn to true + ld [wAIPlayAreaCardToSwitch], a + ld a, $01 + ld [wAIRetreatedThisTurn], a + +; if AI can use Switch from hand, use it instead... + ld a, AI_TRAINER_CARD_PHASE_09 + call AIProcessHandTrainerCards + ld a, [wPreviousAIFlags] + and AI_FLAG_USED_SWITCH + jr nz, .used_switch +; ... else try retreating normally. + ld a, [wAIPlayAreaCardToSwitch] + call AITryToRetreat + ret + +.used_switch +; if AI used switch, unset its AI flag + ld a, [wPreviousAIFlags] + and ~AI_FLAG_USED_SWITCH ; clear Switch flag + ld [wPreviousAIFlags], a + +; bug, this doesn't make sense being here, since at this point +; Switch Trainer card was already used to retreat the Pokemon. +; what the routine will do is just transfer Energy cards to +; the Arena Pokemon for the purpose of retreating, and +; then not actually retreat, resulting in unusual behaviour. +; this would only work placed right after the AI checks whether +; they have Switch card in hand to use and doesn't have one. +; (and probably that was the original intention.) + ld a, AI_ENERGY_TRANS_RETREAT ; retreat + farcall HandleAIEnergyTrans + ret diff --git a/src/engine/duel/ai/decks/general_no_retreat.asm b/src/engine/duel/ai/decks/general_no_retreat.asm new file mode 100644 index 0000000..20d84e3 --- /dev/null +++ b/src/engine/duel/ai/decks/general_no_retreat.asm @@ -0,0 +1,140 @@ +; acts just like a general deck AI except never retreats +AIActionTable_GeneralNoRetreat: ; 148dc (5:48dc) + dw .do_turn ; unused + dw .do_turn + dw .start_duel + dw .forced_switch + dw .ko_switch + dw .take_prize + +.do_turn ; 148e8 (5:48e8) + call AIDoTurn_GeneralNoRetreat + ret + +.start_duel ; 148ec (5:48ec) + call InitAIDuelVars + call AIPlayInitialBasicCards + ret + +.forced_switch ; 148f3 (5:48f3) + call AIDecideBenchPokemonToSwitchTo + ret + +.ko_switch ; 148f7 (5:48f7) + call AIDecideBenchPokemonToSwitchTo + ret + +.take_prize ; 148fb (5:48fb) + call AIPickPrizeCards + ret + +AIDoTurn_GeneralNoRetreat: ; 148ff (5:48ff) +; initialize variables + call InitAITurnVars + ld a, AI_TRAINER_CARD_PHASE_01 + call AIProcessHandTrainerCards + farcall HandleAIAntiMewtwoDeckStrategy + jp nc, .try_attack +; handle Pkmn Powers + farcall HandleAIGoGoRainDanceEnergy + farcall HandleAIDamageSwap + farcall HandleAIPkmnPowers + ret c ; return if turn ended + farcall HandleAICowardice +; process Trainer cards +; phase 2 through 4. + ld a, AI_TRAINER_CARD_PHASE_02 + call AIProcessHandTrainerCards + ld a, AI_TRAINER_CARD_PHASE_03 + call AIProcessHandTrainerCards + ld a, AI_TRAINER_CARD_PHASE_04 + call AIProcessHandTrainerCards +; play Pokemon from hand + call AIDecidePlayPokemonCard + ret c ; return if turn ended +; process Trainer cards +; phase 5 through 12. + ld a, AI_TRAINER_CARD_PHASE_05 + call AIProcessHandTrainerCards + ld a, AI_TRAINER_CARD_PHASE_06 + call AIProcessHandTrainerCards + ld a, AI_TRAINER_CARD_PHASE_07 + call AIProcessHandTrainerCards + ld a, AI_TRAINER_CARD_PHASE_08 + call AIProcessHandTrainerCards + ld a, AI_TRAINER_CARD_PHASE_10 + call AIProcessHandTrainerCards + ld a, AI_TRAINER_CARD_PHASE_11 + call AIProcessHandTrainerCards + ld a, AI_TRAINER_CARD_PHASE_12 + call AIProcessHandTrainerCards +; play Energy card if possible + ld a, [wAlreadyPlayedEnergy] + or a + jr nz, .skip_energy_attach_1 + call AIProcessAndTryToPlayEnergy +.skip_energy_attach_1 +; play Pokemon from hand again + call AIDecidePlayPokemonCard +; handle Pkmn Powers again + farcall HandleAIDamageSwap + farcall HandleAIPkmnPowers + ret c ; return if turn ended + farcall HandleAIGoGoRainDanceEnergy + ld a, AI_ENERGY_TRANS_ATTACK + farcall HandleAIEnergyTrans +; process Trainer cards phases 13 and 15 + ld a, AI_TRAINER_CARD_PHASE_13 + call AIProcessHandTrainerCards + ld a, AI_TRAINER_CARD_PHASE_15 + call AIProcessHandTrainerCards +; if used Professor Oak, process new hand +; if not, then proceed to attack. + ld a, [wPreviousAIFlags] + and AI_FLAG_USED_PROFESSOR_OAK + jr z, .try_attack + ld a, AI_TRAINER_CARD_PHASE_01 + call AIProcessHandTrainerCards + ld a, AI_TRAINER_CARD_PHASE_02 + call AIProcessHandTrainerCards + ld a, AI_TRAINER_CARD_PHASE_03 + call AIProcessHandTrainerCards + ld a, AI_TRAINER_CARD_PHASE_04 + call AIProcessHandTrainerCards + call AIDecidePlayPokemonCard + ret c ; return if turn ended + ld a, AI_TRAINER_CARD_PHASE_05 + call AIProcessHandTrainerCards + ld a, AI_TRAINER_CARD_PHASE_06 + call AIProcessHandTrainerCards + ld a, AI_TRAINER_CARD_PHASE_07 + call AIProcessHandTrainerCards + ld a, AI_TRAINER_CARD_PHASE_08 + call AIProcessHandTrainerCards + ld a, AI_TRAINER_CARD_PHASE_10 + call AIProcessHandTrainerCards + ld a, AI_TRAINER_CARD_PHASE_11 + call AIProcessHandTrainerCards + ld a, AI_TRAINER_CARD_PHASE_12 + call AIProcessHandTrainerCards + ld a, [wAlreadyPlayedEnergy] + or a + jr nz, .skip_energy_attach_2 + call AIProcessAndTryToPlayEnergy +.skip_energy_attach_2 + call AIDecidePlayPokemonCard + farcall HandleAIDamageSwap + farcall HandleAIPkmnPowers + ret c ; return if turn ended + farcall HandleAIGoGoRainDanceEnergy + ld a, AI_TRAINER_CARD_PHASE_13 + call AIProcessHandTrainerCards + ; skip AI_TRAINER_CARD_PHASE_15 +.try_attack +; attack if possible, if not, +; finish turn without attacking. + call AIProcessAndTryToUseAttack + ret c ; return if turn ended + ld a, OPPACTION_FINISH_NO_ATTACK + bank1call AIMakeDecision + ret diff --git a/src/engine/duel/ai/decks/go_go_rain_dance.asm b/src/engine/duel/ai/decks/go_go_rain_dance.asm new file mode 100644 index 0000000..23547e2 --- /dev/null +++ b/src/engine/duel/ai/decks/go_go_rain_dance.asm @@ -0,0 +1,79 @@ +AIActionTable_GoGoRainDance: ; 14f8f (5:4f8f) + dw .do_turn ; unused + dw .do_turn + dw .start_duel + dw .forced_switch + dw .ko_switch + dw .take_prize + +.do_turn ; 14f9b (5:4f9b) + call AIMainTurnLogic + ret + +.start_duel ; 14f9f (5:4f9f) + call InitAIDuelVars + call .store_list_pointers + call SetUpBossStartingHandAndDeck + call TrySetUpBossStartingPlayArea + ret nc + call AIPlayInitialBasicCards + ret + +.forced_switch ; 14fb0 (5:4fb0) + call AIDecideBenchPokemonToSwitchTo + ret + +.ko_switch ; 14fb4 (5:4fb4) + call AIDecideBenchPokemonToSwitchTo + ret + +.take_prize ; 14fb8 (5:4fb8) + call AIPickPrizeCards + ret + +.list_arena ; 14fbc (5:4fbc) + db LAPRAS + db HORSEA + db GOLDEEN + db SQUIRTLE + db $00 + +.list_bench ; 14fc1 (5:4fc1) + db SQUIRTLE + db HORSEA + db GOLDEEN + db LAPRAS + db $00 + +.list_retreat ; 14fc6 (5:4fc6) + ai_retreat SQUIRTLE, -3 + ai_retreat WARTORTLE, -2 + ai_retreat HORSEA, -1 + db $00 + +.list_energy ; 14fcd (5:4fcd) + ai_energy SQUIRTLE, 2, +0 + ai_energy WARTORTLE, 3, +0 + ai_energy BLASTOISE, 5, +0 + ai_energy GOLDEEN, 1, +0 + ai_energy SEAKING, 2, +0 + ai_energy HORSEA, 2, +0 + ai_energy SEADRA, 3, +0 + ai_energy LAPRAS, 3, +0 + db $00 + +.list_prize ; 14fe6 (5:4fe6) + db GAMBLER + db ENERGY_RETRIEVAL + db SUPER_ENERGY_RETRIEVAL + db BLASTOISE + db $00 + +.store_list_pointers ; 14feb (5:4feb) + store_list_pointer wAICardListAvoidPrize, .list_prize + store_list_pointer wAICardListArenaPriority, .list_arena + store_list_pointer wAICardListBenchPriority, .list_bench + store_list_pointer wAICardListPlayFromHandPriority, .list_bench + ; missing store_list_pointer wAICardListRetreatBonus, .list_retreat + store_list_pointer wAICardListEnergyBonus, .list_energy + ret diff --git a/src/engine/duel/ai/decks/im_ronald.asm b/src/engine/duel/ai/decks/im_ronald.asm new file mode 100644 index 0000000..b002d83 --- /dev/null +++ b/src/engine/duel/ai/decks/im_ronald.asm @@ -0,0 +1,80 @@ +AIActionTable_ImRonald: ; 152bd (5:52bd) + dw .do_turn ; unused + dw .do_turn + dw .start_duel + dw .forced_switch + dw .ko_switch + dw .take_prize + +.do_turn ; 152c9 (5:52c9) + call AIMainTurnLogic + ret + +.start_duel ; 152cd (5:52cd) + call InitAIDuelVars + call .store_list_pointers + call SetUpBossStartingHandAndDeck + call TrySetUpBossStartingPlayArea + ret nc + call AIPlayInitialBasicCards + ret + +.forced_switch ; 152de (5:52de) + call AIDecideBenchPokemonToSwitchTo + ret + +.ko_switch ; 152e2 (5:52e2) + call AIDecideBenchPokemonToSwitchTo + ret + +.take_prize ; 152e6 (5:52e6) + call AIPickPrizeCards + ret + +.list_arena ; 152ea (5:52ea) + db LAPRAS + db SEEL + db CHARMANDER + db CUBONE + db SQUIRTLE + db GROWLITHE + db $00 + +.list_bench ; 152f1 (5:52f1) + db CHARMANDER + db SQUIRTLE + db SEEL + db CUBONE + db GROWLITHE + db LAPRAS + db $00 + +.list_retreat ; 152f8 (5:52f8) + db $00 + +.list_energy ; 152f9 (5:52f9) + ai_energy CHARMANDER, 3, +0 + ai_energy CHARMELEON, 5, +0 + ai_energy GROWLITHE, 2, +0 + ai_energy ARCANINE2, 4, +0 + ai_energy SQUIRTLE, 2, +0 + ai_energy WARTORTLE, 3, +0 + ai_energy SEEL, 3, +0 + ai_energy DEWGONG, 4, +0 + ai_energy LAPRAS, 3, +0 + ai_energy CUBONE, 3, +0 + ai_energy MAROWAK1, 3, +0 + db $00 + +.list_prize ; 1531b (5:531b) + db LAPRAS + db $00 + +.store_list_pointers ; 1531d (5:531d) + store_list_pointer wAICardListAvoidPrize, .list_prize + store_list_pointer wAICardListArenaPriority, .list_arena + store_list_pointer wAICardListBenchPriority, .list_bench + store_list_pointer wAICardListPlayFromHandPriority, .list_bench + ; missing store_list_pointer wAICardListRetreatBonus, .list_retreat + store_list_pointer wAICardListEnergyBonus, .list_energy + ret diff --git a/src/engine/duel/ai/decks/invincible_ronald.asm b/src/engine/duel/ai/decks/invincible_ronald.asm new file mode 100644 index 0000000..463560b --- /dev/null +++ b/src/engine/duel/ai/decks/invincible_ronald.asm @@ -0,0 +1,78 @@ +AIActionTable_InvincibleRonald: ; 153e8 (5:53e8) + dw .do_turn ; unused + dw .do_turn + dw .start_duel + dw .forced_switch + dw .ko_switch + dw .take_prize + +.do_turn ; 153f4 (5:53f4) + call AIMainTurnLogic + ret + +.start_duel ; 153f8 (5:53f8) + call InitAIDuelVars + call .store_list_pointers + call SetUpBossStartingHandAndDeck + call TrySetUpBossStartingPlayArea + ret nc + call AIPlayInitialBasicCards + ret + +.forced_switch ; 15409 (5:5409) + call AIDecideBenchPokemonToSwitchTo + ret + +.ko_switch ; 1540d (5:540d) + call AIDecideBenchPokemonToSwitchTo + ret + +.take_prize ; 15411 (5:5411) + call AIPickPrizeCards + ret + +.list_arena ; 15415 (5:5415) + db KANGASKHAN + db MAGMAR2 + db CHANSEY + db GEODUDE + db SCYTHER + db GRIMER + db $00 + +.list_bench ; 1541c (5:541c) + db GRIMER + db SCYTHER + db GEODUDE + db CHANSEY + db MAGMAR2 + db KANGASKHAN + db $00 + +.list_retreat ; 15423 (5:5423) + ai_retreat GRIMER, -1 + db $00 + +.list_energy ; 15426 (5:5426) + ai_energy GRIMER, 1, -1 + ai_energy MUK, 3, -1 + ai_energy SCYTHER, 4, +1 + ai_energy MAGMAR2, 2, +0 + ai_energy GEODUDE, 2, +0 + ai_energy GRAVELER, 3, +0 + ai_energy CHANSEY, 4, +0 + ai_energy KANGASKHAN, 4, -1 + db $00 + +.list_prize ; 1543f (5:543f) + db GAMBLER + db $00 + +.store_list_pointers ; 15441 (5:5441) + store_list_pointer wAICardListAvoidPrize, .list_prize + store_list_pointer wAICardListArenaPriority, .list_arena + store_list_pointer wAICardListBenchPriority, .list_bench + store_list_pointer wAICardListPlayFromHandPriority, .list_bench + ; missing store_list_pointer wAICardListRetreatBonus, .list_retreat + store_list_pointer wAICardListEnergyBonus, .list_energy + ret diff --git a/src/engine/duel/ai/decks/legendary_articuno.asm b/src/engine/duel/ai/decks/legendary_articuno.asm new file mode 100644 index 0000000..6409330 --- /dev/null +++ b/src/engine/duel/ai/decks/legendary_articuno.asm @@ -0,0 +1,209 @@ +AIActionTable_LegendaryArticuno: ; 14c0b (5:4c0b) + dw .do_turn ; unused + dw .do_turn + dw .start_duel + dw .forced_switch + dw .ko_switch + dw .take_prize + +.do_turn ; 14c17 (5:4c17) + call AIDoTurn_LegendaryArticuno + ret + +.start_duel ; 14c1b (5:4c1b) + call InitAIDuelVars + call .store_list_pointers + call SetUpBossStartingHandAndDeck + call TrySetUpBossStartingPlayArea + ret nc + call AIPlayInitialBasicCards + ret + +.forced_switch ; 14c2c (5:4c2c) + call AIDecideBenchPokemonToSwitchTo + ret + +.ko_switch ; 14c30 (5:4c30) + call AIDecideBenchPokemonToSwitchTo + ret + +.take_prize ; 14c34 (5:4c34) + call AIPickPrizeCards + ret + +.list_arena ; 14c38 (5:4c38) + db CHANSEY + db LAPRAS + db DITTO + db SEEL + db ARTICUNO1 + db ARTICUNO2 + db $00 + +.list_bench ; 14c3f (5:4c3f) + db ARTICUNO1 + db SEEL + db LAPRAS + db CHANSEY + db DITTO + db $00 + +.list_retreat ; 14c45 (5:4c45) + ai_retreat SEEL, -3 + ai_retreat DITTO, -3 + db $00 + +.list_energy ; 14c4a (5:4c4a) + ai_energy SEEL, 3, +1 + ai_energy DEWGONG, 4, +0 + ai_energy LAPRAS, 3, +0 + ai_energy ARTICUNO1, 4, +1 + ai_energy ARTICUNO2, 3, +0 + ai_energy CHANSEY, 0, -8 + ai_energy DITTO, 3, +0 + db $00 + +.list_prize ; 14c60 (5:4c60) + db GAMBLER + db ARTICUNO2 + db $00 + +.store_list_pointers ; 14c63 (5:4c63) + store_list_pointer wAICardListAvoidPrize, .list_prize + store_list_pointer wAICardListArenaPriority, .list_arena + store_list_pointer wAICardListBenchPriority, .list_bench + store_list_pointer wAICardListPlayFromHandPriority, .list_bench + ; missing store_list_pointer wAICardListRetreatBonus, .list_retreat + store_list_pointer wAICardListEnergyBonus, .list_energy + ret + +; this routine handles how Legendary Articuno +; prioritizes playing energy cards to each Pokémon. +; first, it makes sure that all Lapras have at least +; 3 energy cards before moving on to Articuno, +; and then to Dewgong and Seel +ScoreLegendaryArticunoCards: ; 14c91 (5:4c91) + call SwapTurn + call CountPrizes + call SwapTurn + cp 3 + ret c + +; player prizes >= 3 +; if Lapras has more than half HP and +; can use second attack, check next for Articuno +; otherwise, check if Articuno or Dewgong +; have more than half HP and can use second attack +; and if so, the next Pokémon to check is Lapras + ld a, LAPRAS + call CheckForBenchIDAtHalfHPAndCanUseSecondAttack + jr c, .articuno + ld a, ARTICUNO1 + call CheckForBenchIDAtHalfHPAndCanUseSecondAttack + jr c, .lapras + ld a, DEWGONG + call CheckForBenchIDAtHalfHPAndCanUseSecondAttack + jr c, .lapras + jr .articuno + +; the following routines check for certain card IDs in bench +; and call RaiseAIScoreToAllMatchingIDsInBench if these are found. +; for Lapras, an additional check is made to its +; attached energy count, which skips calling the routine +; if this count is >= 3 +.lapras + ld a, LAPRAS + ld b, PLAY_AREA_BENCH_1 + call LookForCardIDInPlayArea_Bank5 + jr nc, .articuno + ld e, a + call CountNumberOfEnergyCardsAttached + cp 3 + jr nc, .articuno + ld a, LAPRAS + call RaiseAIScoreToAllMatchingIDsInBench + ret + +.articuno + ld a, ARTICUNO1 + ld b, PLAY_AREA_BENCH_1 + call LookForCardIDInPlayArea_Bank5 + jr nc, .dewgong + ld a, ARTICUNO1 + call RaiseAIScoreToAllMatchingIDsInBench + ret + +.dewgong + ld a, DEWGONG + ld b, PLAY_AREA_BENCH_1 + call LookForCardIDInPlayArea_Bank5 + jr nc, .seel + ld a, DEWGONG + call RaiseAIScoreToAllMatchingIDsInBench + ret + +.seel + ld a, SEEL + ld b, PLAY_AREA_BENCH_1 + call LookForCardIDInPlayArea_Bank5 + ret nc + ld a, SEEL + call RaiseAIScoreToAllMatchingIDsInBench + ret + +AIDoTurn_LegendaryArticuno: ; 14cf7 (5:4cf7) +; initialize variables + call InitAITurnVars + ld a, AI_TRAINER_CARD_PHASE_01 + call AIProcessHandTrainerCards + farcall HandleAIAntiMewtwoDeckStrategy + jp nc, .try_attack +; process Trainer cards + ld a, AI_TRAINER_CARD_PHASE_02 + call AIProcessHandTrainerCards +; play Pokemon from hand + call AIDecidePlayPokemonCard + ret c ; return if turn ended + call AIProcessRetreat + ld a, AI_TRAINER_CARD_PHASE_10 + call AIProcessHandTrainerCards +; play Energy card if possible + ld a, [wAlreadyPlayedEnergy] + or a + jr nz, .skip_energy_attach_1 + call AIProcessAndTryToPlayEnergy +.skip_energy_attach_1 +; play Pokemon from hand again + call AIDecidePlayPokemonCard +; process Trainer cards phases 13 and 15 + ld a, AI_TRAINER_CARD_PHASE_13 + call AIProcessHandTrainerCards + ld a, AI_TRAINER_CARD_PHASE_15 + call AIProcessHandTrainerCards +; if used Professor Oak, process new hand + ld a, [wPreviousAIFlags] + and AI_FLAG_USED_PROFESSOR_OAK + jr z, .try_attack + ld a, AI_TRAINER_CARD_PHASE_01 + call AIProcessHandTrainerCards + ld a, AI_TRAINER_CARD_PHASE_02 + call AIProcessHandTrainerCards + call AIDecidePlayPokemonCard + ret c ; return if turn ended + call AIProcessRetreat + ld a, AI_TRAINER_CARD_PHASE_10 + call AIProcessHandTrainerCards + ld a, [wAlreadyPlayedEnergy] + or a + jr nz, .skip_energy_attach_2 + call AIProcessAndTryToPlayEnergy +.skip_energy_attach_2 + call AIDecidePlayPokemonCard +.try_attack +; attack if possible, if not, +; finish turn without attacking. + call AIProcessAndTryToUseAttack + ret c ; return if turn ended + ld a, OPPACTION_FINISH_NO_ATTACK + bank1call AIMakeDecision + ret diff --git a/src/engine/duel/ai/decks/legendary_dragonite.asm b/src/engine/duel/ai/decks/legendary_dragonite.asm new file mode 100644 index 0000000..597f72c --- /dev/null +++ b/src/engine/duel/ai/decks/legendary_dragonite.asm @@ -0,0 +1,166 @@ +AIActionTable_LegendaryDragonite: ; 14d60 (05:4d60) + dw .do_turn ; unused + dw .do_turn + dw .start_duel + dw .forced_switch + dw .ko_switch + dw .take_prize + +.do_turn ; 14d6c (5:4d6c) + call AIDoTurn_LegendaryDragonite + ret + +.start_duel ; 14d70 (5:4d70) + call InitAIDuelVars + call .store_list_pointers + call SetUpBossStartingHandAndDeck + call TrySetUpBossStartingPlayArea + ret nc + call AIPlayInitialBasicCards + ret + +.forced_switch ; 14d81 (5:4d81) + call AIDecideBenchPokemonToSwitchTo + ret + +.ko_switch ; 14d85 (5:4d85) + call AIDecideBenchPokemonToSwitchTo + ret + +.take_prize ; 14d89 (5:4d89) + call AIPickPrizeCards + ret + +.list_arena ; 14d8d (5:4d8d) + db KANGASKHAN + db LAPRAS + db CHARMANDER + db DRATINI + db MAGIKARP + db $00 + +.list_bench ; 14d93 (5:4d93) + db CHARMANDER + db MAGIKARP + db DRATINI + db LAPRAS + db KANGASKHAN + db $00 + +.list_retreat ; 14d99 (5:4d99) + ai_retreat CHARMANDER, -1 + ai_retreat MAGIKARP, -5 + db $00 + +.list_energy ; 14d9e (5:4d9e) + ai_energy CHARMANDER, 3, +1 + ai_energy CHARMELEON, 4, +1 + ai_energy CHARIZARD, 5, +0 + ai_energy MAGIKARP, 3, +1 + ai_energy GYARADOS, 4, -1 + ai_energy DRATINI, 2, +0 + ai_energy DRAGONAIR, 4, +0 + ai_energy DRAGONITE1, 3, -1 + ai_energy KANGASKHAN, 2, -2 + ai_energy LAPRAS, 3, +0 + db $00 + +.list_prize ; 14dbd (5:4dbd) + db GAMBLER + db DRAGONITE1 + db KANGASKHAN + db $00 + +.store_list_pointers ; 14dc1 (5:4dc1) + store_list_pointer wAICardListAvoidPrize, .list_prize + store_list_pointer wAICardListArenaPriority, .list_arena + store_list_pointer wAICardListBenchPriority, .list_bench + store_list_pointer wAICardListPlayFromHandPriority, .list_bench + ; missing store_list_pointer wAICardListRetreatBonus, .list_retreat + store_list_pointer wAICardListEnergyBonus, .list_energy + ret + +AIDoTurn_LegendaryDragonite: ; 14def (5:4def) +; initialize variables + call InitAITurnVars + ld a, AI_TRAINER_CARD_PHASE_01 + call AIProcessHandTrainerCards + farcall HandleAIAntiMewtwoDeckStrategy + jp nc, .try_attack +; process Trainer cards + ld a, AI_TRAINER_CARD_PHASE_02 + call AIProcessHandTrainerCards +; play Pokemon from hand + call AIDecidePlayPokemonCard + ret c ; return if turn ended + ld a, AI_TRAINER_CARD_PHASE_07 + call AIProcessHandTrainerCards + call AIProcessRetreat + ld a, AI_TRAINER_CARD_PHASE_10 + call AIProcessHandTrainerCards + ld a, AI_TRAINER_CARD_PHASE_11 + call AIProcessHandTrainerCards +; play Energy card if possible + ld a, [wAlreadyPlayedEnergy] + or a + jr nz, .skip_energy_attach_1 + +; if Arena card is Kangaskhan and doesn't +; have Energy cards attached, try attaching from hand. +; otherwise run normal AI energy attach routine. + ld a, DUELVARS_ARENA_CARD + call GetTurnDuelistVariable + call GetCardIDFromDeckIndex + ld a, KANGASKHAN + cp e + jr nz, .attach_normally + call CreateEnergyCardListFromHand + jr c, .skip_energy_attach_1 + ld e, PLAY_AREA_ARENA + call CountNumberOfEnergyCardsAttached + or a + jr nz, .attach_normally + xor a + ldh [hTempPlayAreaLocation_ff9d], a + call AITryToPlayEnergyCard + jr c, .skip_energy_attach_1 +.attach_normally + call AIProcessAndTryToPlayEnergy + +.skip_energy_attach_1 +; play Pokemon from hand again + call AIDecidePlayPokemonCard + ld a, AI_TRAINER_CARD_PHASE_15 + call AIProcessHandTrainerCards +; if used Professor Oak, process new hand +; if not, then proceed to attack. + ld a, [wPreviousAIFlags] + and AI_FLAG_USED_PROFESSOR_OAK + jr z, .try_attack + ld a, AI_TRAINER_CARD_PHASE_01 + call AIProcessHandTrainerCards + ld a, AI_TRAINER_CARD_PHASE_02 + call AIProcessHandTrainerCards + call AIDecidePlayPokemonCard + ret c ; return if turn ended + ld a, AI_TRAINER_CARD_PHASE_07 + call AIProcessHandTrainerCards + call AIProcessRetreat + ld a, AI_TRAINER_CARD_PHASE_10 + call AIProcessHandTrainerCards + ld a, AI_TRAINER_CARD_PHASE_11 + call AIProcessHandTrainerCards + ld a, [wAlreadyPlayedEnergy] + or a + jr nz, .skip_energy_attach_2 + call AIProcessAndTryToPlayEnergy +.skip_energy_attach_2 + call AIDecidePlayPokemonCard +.try_attack +; attack if possible, if not, +; finish turn without attacking. + call AIProcessAndTryToUseAttack + ret c ; return if turn ended + ld a, OPPACTION_FINISH_NO_ATTACK + bank1call AIMakeDecision + ret diff --git a/src/engine/duel/ai/decks/legendary_moltres.asm b/src/engine/duel/ai/decks/legendary_moltres.asm new file mode 100644 index 0000000..c2a3882 --- /dev/null +++ b/src/engine/duel/ai/decks/legendary_moltres.asm @@ -0,0 +1,176 @@ +AIActionTable_LegendaryMoltres: ; 149e8 (05:49e8) + dw .do_turn ; unused + dw .do_turn + dw .start_duel + dw .forced_switch + dw .ko_switch + dw .take_prize + +.do_turn ; 149f4 (5:49f4) + call AIDoTurn_LegendaryMoltres + ret + +.start_duel ; 149f8 (5:49f8) + call InitAIDuelVars + call .store_list_pointers + call SetUpBossStartingHandAndDeck + call TrySetUpBossStartingPlayArea + ret nc ; Play Area set up was successful + call AIPlayInitialBasicCards + ret + +.forced_switch ; 14a09 (5:4a09) + call AIDecideBenchPokemonToSwitchTo + ret + +.ko_switch ; 14a0d (5:4a0d) + call AIDecideBenchPokemonToSwitchTo + ret + +.take_prize ; 14a11 (5:4a11) + call AIPickPrizeCards + ret + +.list_arena ; 14a15 (5:4a15) + db MAGMAR2 + db GROWLITHE + db VULPIX + db MAGMAR1 + db MOLTRES1 + db MOLTRES2 + db $00 + +.list_bench ; 14a1c (5:4a1c) + db MOLTRES1 + db VULPIX + db GROWLITHE + db MAGMAR2 + db MAGMAR1 + db $00 + +.list_play_hand ; 14a22 (5:4a22) + db MOLTRES2 + db MOLTRES1 + db VULPIX + db GROWLITHE + db MAGMAR2 + db MAGMAR1 + db $00 + +.list_retreat ; 14a29 (5:4a29) + ai_retreat GROWLITHE, -5 + ai_retreat VULPIX, -5 + db $00 + +.list_energy ; 14a2e (5:4a2e) + ai_energy VULPIX, 3, +0 + ai_energy NINETALES2, 3, +1 + ai_energy GROWLITHE, 3, +1 + ai_energy ARCANINE2, 4, +1 + ai_energy MAGMAR1, 4, -1 + ai_energy MAGMAR2, 1, -1 + ai_energy MOLTRES2, 3, +2 + ai_energy MOLTRES1, 4, +2 + db $00 + +.list_prize ; 14a47 (5:4a47) + db ENERGY_REMOVAL + db MOLTRES2 + db $00 + +.store_list_pointers ; 14a4a (5:4a4a) + store_list_pointer wAICardListAvoidPrize, .list_prize + store_list_pointer wAICardListArenaPriority, .list_arena + store_list_pointer wAICardListBenchPriority, .list_bench + store_list_pointer wAICardListPlayFromHandPriority, .list_play_hand + store_list_pointer wAICardListRetreatBonus, .list_retreat + store_list_pointer wAICardListEnergyBonus, .list_energy + ret + +AIDoTurn_LegendaryMoltres: ; 14a81 (5:4a81) +; initialize variables + call InitAITurnVars + farcall HandleAIAntiMewtwoDeckStrategy + jp nc, .try_attack +; process Trainer cards +; phase 2 through 4. + ld a, AI_TRAINER_CARD_PHASE_02 + call AIProcessHandTrainerCards + ld a, AI_TRAINER_CARD_PHASE_04 + call AIProcessHandTrainerCards + +; check if AI can play Moltres2 +; from hand and if so, play it. + ld a, DUELVARS_NUMBER_OF_POKEMON_IN_PLAY_AREA + call GetTurnDuelistVariable + cp MAX_PLAY_AREA_POKEMON + jr nc, .skip_moltres ; skip if bench is full + ld a, DUELVARS_NUMBER_OF_CARDS_NOT_IN_DECK + call GetTurnDuelistVariable + cp DECK_SIZE - 9 + jr nc, .skip_moltres ; skip if cards in deck <= 9 + ld a, MUK + call CountPokemonIDInBothPlayAreas + jr c, .skip_moltres ; skip if Muk in play + ld a, MOLTRES2 + call LookForCardIDInHandList_Bank5 + jr nc, .skip_moltres ; skip if no Moltres2 in hand + ldh [hTemp_ffa0], a + ld a, OPPACTION_PLAY_BASIC_PKMN + bank1call AIMakeDecision + +.skip_moltres +; play Pokemon from hand + call AIDecidePlayPokemonCard + ret c ; return if turn ended +; process Trainer cards + ld a, AI_TRAINER_CARD_PHASE_05 + call AIProcessHandTrainerCards + call AIProcessRetreat + ld a, AI_TRAINER_CARD_PHASE_10 + call AIProcessHandTrainerCards + ld a, AI_TRAINER_CARD_PHASE_11 + call AIProcessHandTrainerCards +; play Energy card if possible + ld a, [wAlreadyPlayedEnergy] + or a + jr nz, .skip_attach_energy + +; if Magmar2 is the Arena card and has no energy attached, +; try attaching an energy card to it from the hand. +; otherwise, run normal AI energy attach routine. + ld a, DUELVARS_ARENA_CARD + call GetTurnDuelistVariable + call GetCardIDFromDeckIndex + ld a, MAGMAR2 + cp e + jr nz, .attach_normally + ; Magmar2 is the Arena card + call CreateEnergyCardListFromHand + jr c, .skip_attach_energy + ld e, PLAY_AREA_ARENA + call CountNumberOfEnergyCardsAttached + or a + jr nz, .attach_normally + xor a ; PLAY_AREA_ARENA + ldh [hTempPlayAreaLocation_ff9d], a + call AITryToPlayEnergyCard + jr c, .skip_attach_energy + +.attach_normally +; play Energy card if possible + call AIProcessAndTryToPlayEnergy +.skip_attach_energy +; try playing Pokemon cards from hand again + call AIDecidePlayPokemonCard + ld a, AI_TRAINER_CARD_PHASE_13 + call AIProcessHandTrainerCards + +.try_attack +; attack if possible, if not, +; finish turn without attacking. + call AIProcessAndTryToUseAttack + ret c + ld a, OPPACTION_FINISH_NO_ATTACK + bank1call AIMakeDecision + ret diff --git a/src/engine/duel/ai/decks/legendary_ronald.asm b/src/engine/duel/ai/decks/legendary_ronald.asm new file mode 100644 index 0000000..3356838 --- /dev/null +++ b/src/engine/duel/ai/decks/legendary_ronald.asm @@ -0,0 +1,203 @@ +AIActionTable_LegendaryRonald: ; 1546f (5:546f) + dw .do_turn ; unused + dw .do_turn + dw .start_duel + dw .forced_switch + dw .ko_switch + dw .take_prize + +.do_turn ; 1547b (5:547b) + call AIDoTurn_LegendaryRonald + ret + +.start_duel ; 1547f (5:547f) + call InitAIDuelVars + call .store_list_pointers + call SetUpBossStartingHandAndDeck + call TrySetUpBossStartingPlayArea + ret nc + call AIPlayInitialBasicCards + ret + +.forced_switch ; 15490 (5:5490) + call AIDecideBenchPokemonToSwitchTo + ret + +.ko_switch ; 15494 (5:5494) + call AIDecideBenchPokemonToSwitchTo + ret + +.take_prize ; 15498 (5:5498) + call AIPickPrizeCards + ret + +.list_arena ; 1549c (5:549c) + db KANGASKHAN + db DRATINI + db EEVEE + db ZAPDOS3 + db ARTICUNO2 + db MOLTRES2 + db $00 + +.list_bench ; 154a3 (5:54a3) + db KANGASKHAN + db DRATINI + db EEVEE + db $00 + +.list_play_hand ; 154a7 (5:54a7) + db MOLTRES2 + db ZAPDOS3 + db KANGASKHAN + db DRATINI + db EEVEE + db ARTICUNO2 + db $00 + +.list_retreat ; 154ae (5:54ae) + ai_retreat EEVEE, -2 + db $00 + +.list_energy ; 154b1 (5:54b1) + ai_energy FLAREON1, 3, +0 + ai_energy MOLTRES2, 3, +0 + ai_energy VAPOREON1, 3, +0 + ai_energy ARTICUNO2, 0, -8 + ai_energy JOLTEON1, 4, +0 + ai_energy ZAPDOS3, 0, -8 + ai_energy KANGASKHAN, 4, -1 + ai_energy EEVEE, 3, +0 + ai_energy DRATINI, 3, +0 + ai_energy DRAGONAIR, 4, +0 + ai_energy DRAGONITE1, 3, +0 + db $00 + +.list_prize ; 154d3 (5:54d3) + db MOLTRES2 + db ARTICUNO2 + db ZAPDOS3 + db DRAGONITE1 + db GAMBLER + db $00 + +.store_list_pointers ; 154d9 (5:54d9) + store_list_pointer wAICardListAvoidPrize, .list_prize + store_list_pointer wAICardListArenaPriority, .list_arena + store_list_pointer wAICardListBenchPriority, .list_bench + store_list_pointer wAICardListPlayFromHandPriority, .list_play_hand + ; missing store_list_pointer wAICardListRetreatBonus, .list_retreat + store_list_pointer wAICardListEnergyBonus, .list_energy + ret + +AIDoTurn_LegendaryRonald: ; 15507 (5:5507) +; initialize variables + call InitAITurnVars +; process Trainer cards + ld a, AI_TRAINER_CARD_PHASE_01 + call AIProcessHandTrainerCards + ld a, AI_TRAINER_CARD_PHASE_02 + call AIProcessHandTrainerCards + ld a, AI_TRAINER_CARD_PHASE_04 + call AIProcessHandTrainerCards + +; check if AI can play Moltres2 +; from hand and if so, play it. + ld a, DUELVARS_NUMBER_OF_POKEMON_IN_PLAY_AREA + call GetTurnDuelistVariable + cp MAX_PLAY_AREA_POKEMON + jr nc, .skip_moltres_1 ; skip if bench is full + ld a, DUELVARS_NUMBER_OF_CARDS_NOT_IN_DECK + call GetTurnDuelistVariable + cp DECK_SIZE - 9 + jr nc, .skip_moltres_1 ; skip if cards in deck <= 9 + ld a, MUK + call CountPokemonIDInBothPlayAreas + jr c, .skip_moltres_1 ; skip if Muk in play + ld a, MOLTRES2 + call LookForCardIDInHandList_Bank5 + jr nc, .skip_moltres_1 ; skip if no Moltres2 in hand + ldh [hTemp_ffa0], a + ld a, OPPACTION_PLAY_BASIC_PKMN + bank1call AIMakeDecision + +.skip_moltres_1 +; play Pokemon from hand + call AIDecidePlayPokemonCard + ret c ; return if turn ended +; process Trainer cards + ld a, AI_TRAINER_CARD_PHASE_05 + call AIProcessHandTrainerCards + ld a, AI_TRAINER_CARD_PHASE_07 + call AIProcessHandTrainerCards + call AIProcessRetreat + ld a, AI_TRAINER_CARD_PHASE_10 + call AIProcessHandTrainerCards +; play Energy card if possible + ld a, [wAlreadyPlayedEnergy] + or a + jr nz, .skip_attach_energy_1 + call AIProcessAndTryToPlayEnergy +.skip_attach_energy_1 +; try playing Pokemon cards from hand again + call AIDecidePlayPokemonCard + ret c ; return if turn ended + ld a, AI_TRAINER_CARD_PHASE_15 +; if used Professor Oak, process new hand +; if not, then proceed to attack. + call AIProcessHandTrainerCards + ld a, [wPreviousAIFlags] + and AI_FLAG_USED_PROFESSOR_OAK + jr z, .try_attack + ld a, AI_TRAINER_CARD_PHASE_01 + call AIProcessHandTrainerCards + ld a, AI_TRAINER_CARD_PHASE_02 + call AIProcessHandTrainerCards + ld a, AI_TRAINER_CARD_PHASE_04 + call AIProcessHandTrainerCards + +; check if AI can play Moltres2 +; from hand and if so, play it. + ld a, DUELVARS_NUMBER_OF_POKEMON_IN_PLAY_AREA + call GetTurnDuelistVariable + cp MAX_PLAY_AREA_POKEMON + jr nc, .skip_moltres_2 ; skip if bench is full + ld a, DUELVARS_NUMBER_OF_CARDS_NOT_IN_DECK + call GetTurnDuelistVariable + cp DECK_SIZE - 9 + jr nc, .skip_moltres_2 ; skip if cards in deck <= 9 + ld a, MUK + call CountPokemonIDInBothPlayAreas + jr c, .skip_moltres_2 ; skip if Muk in play + ld a, MOLTRES2 + call LookForCardIDInHandList_Bank5 + jr nc, .skip_moltres_2 ; skip if no Moltres2 in hand + ldh [hTemp_ffa0], a + ld a, OPPACTION_PLAY_BASIC_PKMN + bank1call AIMakeDecision + +.skip_moltres_2 + call AIDecidePlayPokemonCard + ret c ; return if turn ended + ld a, AI_TRAINER_CARD_PHASE_05 + call AIProcessHandTrainerCards + ld a, AI_TRAINER_CARD_PHASE_07 + call AIProcessHandTrainerCards + call AIProcessRetreat + ld a, AI_TRAINER_CARD_PHASE_10 + call AIProcessHandTrainerCards + ld a, [wAlreadyPlayedEnergy] + or a + jr nz, .skip_attach_energy_2 + call AIProcessAndTryToPlayEnergy +.skip_attach_energy_2 + call AIDecidePlayPokemonCard + ret c ; return if turn ended +.try_attack +; attack if possible, if not, +; finish turn without attacking. + call AIProcessAndTryToUseAttack + ret c ; return if turn ended + ld a, OPPACTION_FINISH_NO_ATTACK + bank1call AIMakeDecision + ret diff --git a/src/engine/duel/ai/decks/legendary_zapdos.asm b/src/engine/duel/ai/decks/legendary_zapdos.asm new file mode 100644 index 0000000..cc99f0c --- /dev/null +++ b/src/engine/duel/ai/decks/legendary_zapdos.asm @@ -0,0 +1,153 @@ +AIActionTable_LegendaryZapdos: ; 14b0f (05:4b0f) + dw .do_turn ; unused + dw .do_turn + dw .start_duel + dw .forced_switch + dw .ko_switch + dw .take_prize + +.do_turn ; 14b1b (5:4b1b) + call AIDoTurn_LegendaryZapdos + ret + +.start_duel ; 14b1f (5:4b1f) + call InitAIDuelVars + call .store_list_pointers + call SetUpBossStartingHandAndDeck + call TrySetUpBossStartingPlayArea + ret nc + call AIPlayInitialBasicCards + ret + +.forced_switch ; 14b30 (5:4b30) + call AIDecideBenchPokemonToSwitchTo + ret + +.ko_switch ; 14b34 (5:4b34) + call AIDecideBenchPokemonToSwitchTo + ret + +.take_prize ; 14b38 (5:4b38) + call AIPickPrizeCards + ret + +.list_arena ; 14b3c (5:4b3c) + db ELECTABUZZ2 + db VOLTORB + db EEVEE + db ZAPDOS1 + db ZAPDOS2 + db ZAPDOS3 + db $00 + +.list_bench ; 14b43 (5:4b43) + db ZAPDOS2 + db ZAPDOS1 + db EEVEE + db VOLTORB + db ELECTABUZZ2 + db $00 + +.list_retreat ; 14b49 (5:4b49) + ai_retreat EEVEE, -5 + ai_retreat VOLTORB, -5 + ai_retreat ELECTABUZZ2, -5 + db $00 + +.list_energy ; 14b50 (5:4b50) + ai_energy VOLTORB, 1, -1 + ai_energy ELECTRODE1, 3, +0 + ai_energy ELECTABUZZ2, 2, -1 + ai_energy JOLTEON2, 3, +1 + ai_energy ZAPDOS1, 4, +2 + ai_energy ZAPDOS2, 4, +2 + ai_energy ZAPDOS3, 3, +1 + ai_energy EEVEE, 3, +0 + db $00 + +.list_prize ; 14b69 (5:4b69) + db GAMBLER + db ZAPDOS3 + db $00 + +.store_list_pointers ; 14b6c (5:4b6c) + store_list_pointer wAICardListAvoidPrize, .list_prize + store_list_pointer wAICardListArenaPriority, .list_arena + store_list_pointer wAICardListBenchPriority, .list_bench + store_list_pointer wAICardListPlayFromHandPriority, .list_bench + ; missing store_list_pointer wAICardListRetreatBonus, .list_retreat + store_list_pointer wAICardListEnergyBonus, .list_energy + ret + +AIDoTurn_LegendaryZapdos: ; 14b9a (5:4b9a) +; initialize variables + call InitAITurnVars + farcall HandleAIAntiMewtwoDeckStrategy + jp nc, .try_attack +; process Trainer cards + ld a, AI_TRAINER_CARD_PHASE_01 + call AIProcessHandTrainerCards + ld a, AI_TRAINER_CARD_PHASE_04 + call AIProcessHandTrainerCards +; play Pokemon from hand + call AIDecidePlayPokemonCard + ret c ; return if turn ended + ld a, AI_TRAINER_CARD_PHASE_07 + call AIProcessHandTrainerCards + call AIProcessRetreat + ld a, AI_TRAINER_CARD_PHASE_10 + call AIProcessHandTrainerCards +; play Energy card if possible. + ld a, [wAlreadyPlayedEnergy] + or a + jr nz, .skip_energy_attach + +; if Arena card is Voltorb and there's Electrode1 in hand, +; or if it's Electabuzz, try attaching Energy card +; to the Arena card if it doesn't have any energy attached. +; Otherwise if Energy card is not needed, +; go through normal AI energy attach routine. + ld a, DUELVARS_ARENA_CARD + call GetTurnDuelistVariable + call GetCardIDFromDeckIndex + ld a, VOLTORB + cp e + jr nz, .check_electabuzz + ld a, ELECTRODE1 + call LookForCardIDInHandList_Bank5 + jr nc, .attach_normally + jr .voltorb_or_electabuzz +.check_electabuzz + ld a, ELECTABUZZ2 + cp e + jr nz, .attach_normally + +.voltorb_or_electabuzz + call CreateEnergyCardListFromHand + jr c, .skip_energy_attach + ld e, PLAY_AREA_ARENA + call CountNumberOfEnergyCardsAttached + or a + jr nz, .attach_normally + xor a ; PLAY_AREA_ARENA + ldh [hTempPlayAreaLocation_ff9d], a + call AITryToPlayEnergyCard + jr c, .skip_energy_attach + +.attach_normally + call AIProcessAndTryToPlayEnergy + +.skip_energy_attach +; play Pokemon from hand again + call AIDecidePlayPokemonCard + ret c ; return if turn ended + ld a, AI_TRAINER_CARD_PHASE_13 + call AIProcessHandTrainerCards +.try_attack +; attack if possible, if not, +; finish turn without attacking. + call AIProcessAndTryToUseAttack + ret c ; return if turn ended + ld a, OPPACTION_FINISH_NO_ATTACK + bank1call AIMakeDecision + ret diff --git a/src/engine/duel/ai/decks/powerful_ronald.asm b/src/engine/duel/ai/decks/powerful_ronald.asm new file mode 100644 index 0000000..096fbea --- /dev/null +++ b/src/engine/duel/ai/decks/powerful_ronald.asm @@ -0,0 +1,92 @@ +AIActionTable_PowerfulRonald: ; 1534b (5:534b) + dw .do_turn ; unused + dw .do_turn + dw .start_duel + dw .forced_switch + dw .ko_switch + dw .take_prize + +.do_turn ; 15357 (5:5357) + call AIMainTurnLogic + ret + +.start_duel ; 1535b (5:535b) + call InitAIDuelVars + call .store_list_pointers + call SetUpBossStartingHandAndDeck + call TrySetUpBossStartingPlayArea + ret nc + call AIPlayInitialBasicCards + ret + +.forced_switch ; 1536c (5:536c) + call AIDecideBenchPokemonToSwitchTo + ret + +.ko_switch ; 15370 (5:5370) + call AIDecideBenchPokemonToSwitchTo + ret + +.take_prize ; 15374 (5:5374) + call AIPickPrizeCards + ret + +.list_arena ; 15378 (5:5378) + db KANGASKHAN + db ELECTABUZZ2 + db HITMONCHAN + db MR_MIME + db LICKITUNG + db HITMONLEE + db TAUROS + db JYNX + db MEWTWO1 + db DODUO + db $00 + +.list_bench ; 15383 (5:5383) + db KANGASKHAN + db HITMONLEE + db HITMONCHAN + db TAUROS + db DODUO + db JYNX + db MEWTWO1 + db ELECTABUZZ2 + db MR_MIME + db LICKITUNG + db $00 + +.list_retreat ; 1538e (5:538e) + ai_retreat KANGASKHAN, -1 + ai_retreat DODUO, -1 + ai_retreat DODRIO, -1 + db $00 + +.list_energy ; 15395 (5:5395) + ai_energy ELECTABUZZ2, 2, +1 + ai_energy HITMONLEE, 3, +1 + ai_energy HITMONCHAN, 3, +1 + ai_energy MR_MIME, 2, +0 + ai_energy JYNX, 3, +0 + ai_energy MEWTWO1, 2, +0 + ai_energy DODUO, 3, -1 + ai_energy DODRIO, 3, -1 + ai_energy LICKITUNG, 2, +0 + ai_energy KANGASKHAN, 4, -1 + ai_energy TAUROS, 3, +0 + db $00 + +.list_prize ; 153b7 (5:53b7) + db GAMBLER + db ENERGY_REMOVAL + db $00 + +.store_list_pointers ; 153ba (5:53ba) + store_list_pointer wAICardListAvoidPrize, .list_prize + store_list_pointer wAICardListArenaPriority, .list_arena + store_list_pointer wAICardListBenchPriority, .list_bench + store_list_pointer wAICardListPlayFromHandPriority, .list_bench + ; missing store_list_pointer wAICardListRetreatBonus, .list_retreat + store_list_pointer wAICardListEnergyBonus, .list_energy + ret diff --git a/src/engine/duel/ai/decks/rock_crusher.asm b/src/engine/duel/ai/decks/rock_crusher.asm new file mode 100644 index 0000000..41a50fa --- /dev/null +++ b/src/engine/duel/ai/decks/rock_crusher.asm @@ -0,0 +1,74 @@ +AIActionTable_RockCrusher: ; 14f0e (5:4f0e) + dw .do_turn ; unused + dw .do_turn + dw .start_duel + dw .forced_switch + dw .ko_switch + dw .take_prize + +.do_turn ; 14f1a (5:4f1a) + call AIMainTurnLogic + ret + +.start_duel ; 14f1e (5:4f1e) + call InitAIDuelVars + call .store_list_pointers + call SetUpBossStartingHandAndDeck + call TrySetUpBossStartingPlayArea + ret nc + call AIPlayInitialBasicCards + ret + +.forced_switch ; 14f2f (5:4f2f) + call AIDecideBenchPokemonToSwitchTo + ret + +.ko_switch ; 14f33 (5:4f33) + call AIDecideBenchPokemonToSwitchTo + ret + +.take_prize ; 14f37 (5:4f37) + call AIPickPrizeCards + ret + +.list_arena ; 14f3b (5:4f3b) + db RHYHORN + db ONIX + db GEODUDE + db DIGLETT + db $00 + +.list_bench ; 14f40 (5:4f40) + db DIGLETT + db GEODUDE + db RHYHORN + db ONIX + db $00 + +.list_retreat ; 14f45 (5:4f45) + ai_retreat DIGLETT, -1 + db $00 + +.list_energy ; 14f48 (5:4f48) + ai_energy DIGLETT, 3, +1 + ai_energy DUGTRIO, 4, +0 + ai_energy GEODUDE, 2, +1 + ai_energy GRAVELER, 3, +0 + ai_energy GOLEM, 4, +0 + ai_energy ONIX, 2, -1 + ai_energy RHYHORN, 3, +0 + db $00 + +.list_prize ; 14f5e (5:4f5e) + db ENERGY_REMOVAL + db RHYHORN + db $00 + +.store_list_pointers ; 14f61 (5:4f61) + store_list_pointer wAICardListAvoidPrize, .list_prize + store_list_pointer wAICardListArenaPriority, .list_arena + store_list_pointer wAICardListBenchPriority, .list_bench + store_list_pointer wAICardListPlayFromHandPriority, .list_bench + ; missing store_list_pointer wAICardListRetreatBonus, .list_retreat + store_list_pointer wAICardListEnergyBonus, .list_energy + ret diff --git a/src/engine/duel/ai/decks/sams_practice.asm b/src/engine/duel/ai/decks/sams_practice.asm new file mode 100644 index 0000000..dddce61 --- /dev/null +++ b/src/engine/duel/ai/decks/sams_practice.asm @@ -0,0 +1,205 @@ +; AI for Sam's practice duel, which handles his scripted actions. +; will act as a normal duelist AI after turn 7. +AIActionTable_SamPractice: ; 147bd (05:47bd) + dw .do_turn ; unused + dw .do_turn + dw .start_duel + dw .forced_switch + dw .ko_switch + dw .take_prize + +.do_turn ; 147c9 (5:47c9) + call IsAIPracticeScriptedTurn + jr nc, .scripted_1 +; not scripted, use AI main turn logic + call AIMainTurnLogic + ret +.scripted_1 ; use scripted actions instead + call AIPerformScriptedTurn + ret + +.start_duel ; 147d6 (5:47d6) + call SetSamsStartingPlayArea + ret + +.forced_switch ; 147da (5:47da) + call IsAIPracticeScriptedTurn + jr nc, .scripted_2 + call AIDecideBenchPokemonToSwitchTo + ret +.scripted_2 + call PickRandomBenchPokemon + ret + +.ko_switch: ; 147e7 (5:47e7) + call IsAIPracticeScriptedTurn + jr nc, .scripted_3 + call AIDecideBenchPokemonToSwitchTo + ret +.scripted_3 + call GetPlayAreaLocationOfRaticateOrRattata + ret + +.take_prize: ; 147f4 (5:47f4) + call AIPickPrizeCards + ret + +; returns carry if number of turns +; the AI has taken >= 7. +; used to know whether AI Sam is still +; doing scripted turns. +IsAIPracticeScriptedTurn: ; 147f8 (5:47f8) + ld a, [wDuelTurns] + srl a + cp 7 + ccf + ret + +; places one Machop from the hand to the Play Area +; and sets the number of prizes to 2. +SetSamsStartingPlayArea: ; 14801 (5:4801) + call CreateHandCardList + ld hl, wDuelTempList +.loop_hand + ld a, [hli] + ldh [hTempCardIndex_ff98], a + cp $ff + ret z + call LoadCardDataToBuffer1_FromDeckIndex + cp MACHOP + jr nz, .loop_hand + ldh a, [hTempCardIndex_ff98] + call PutHandPokemonCardInPlayArea + ld a, 2 + ld [wDuelInitialPrizes], a + ret + +; outputs in a Play Area location of Raticate or Rattata +; in the Bench. If neither is found, just output PLAY_AREA_BENCH_1. +GetPlayAreaLocationOfRaticateOrRattata: ; 1481f (5:481f) + ld a, RATICATE + ld b, PLAY_AREA_BENCH_1 + call LookForCardIDInPlayArea_Bank5 + cp $ff + jr nz, .found + ld a, RATTATA + ld b, PLAY_AREA_BENCH_1 + call LookForCardIDInPlayArea_Bank5 + cp $ff + jr nz, .found + ld a, PLAY_AREA_BENCH_1 +.found + ldh [hTempPlayAreaLocation_ff9d], a + ret + +; has AI execute some scripted actions depending on Duel turn. +AIPerformScriptedTurn: ; 1483a (5:483a) + ld a, [wDuelTurns] + srl a + ld hl, .scripted_actions_list + call JumpToFunctionInTable + +; always attack with Arena card's first attack. +; if it's unusable end turn without attacking. + xor a + ldh [hTempPlayAreaLocation_ff9d], a + ld [wSelectedAttack], a + call CheckIfSelectedAttackIsUnusable + jr c, .unusable + call AITryUseAttack + ret + +.unusable + ld a, OPPACTION_FINISH_NO_ATTACK + bank1call AIMakeDecision + ret + +.scripted_actions_list ; 1485a (05:485a) + dw .turn_1 + dw .turn_2 + dw .turn_3 + dw .turn_4 + dw .turn_5 + dw .turn_6 + dw .turn_7 + +.turn_1 ; 14868 (5:4868) + ld d, MACHOP + ld e, FIGHTING_ENERGY + call AIAttachEnergyInHandToCardInPlayArea + ret + +.turn_2 ; 14870 (5:4870) + ld a, RATTATA + call LookForCardIDInHandList_Bank5 + ldh [hTemp_ffa0], a + ld a, OPPACTION_PLAY_BASIC_PKMN + bank1call AIMakeDecision + ld d, RATTATA + ld e, FIGHTING_ENERGY + call AIAttachEnergyInHandToCardInPlayArea + ret + +.turn_3 ; 14884 (5:4884) + ld a, RATTATA + ld b, PLAY_AREA_ARENA + call LookForCardIDInPlayArea_Bank5 + ldh [hTempPlayAreaLocation_ffa1], a + ld a, RATICATE + call LookForCardIDInHandList_Bank5 + ldh [hTemp_ffa0], a + ld a, OPPACTION_EVOLVE_PKMN + bank1call AIMakeDecision + ld d, RATICATE + ld e, LIGHTNING_ENERGY + call AIAttachEnergyInHandToCardInPlayArea + ret + +.turn_4 ; 148a1 (5:48a1) + ld d, RATICATE + ld e, LIGHTNING_ENERGY + call AIAttachEnergyInHandToCardInPlayArea + ret + +.turn_5 ; 148a9 (5:48a9) + ld a, MACHOP + call LookForCardIDInHandList_Bank5 + ldh [hTemp_ffa0], a + ld a, OPPACTION_PLAY_BASIC_PKMN + bank1call AIMakeDecision + ld d, MACHOP + ld e, FIGHTING_ENERGY + call AIAttachEnergyInHandToCardInBench + +; this is a bug, it's attempting to compare a card ID with a deck index. +; the intention was to change the card to switch to depending on whether +; the first Machop was KO'd at this point in the Duel or not. +; because of the buggy comparison, this will always jump the +; 'inc a' instruction and switch to PLAY_AREA_BENCH_1. +; in a normal Practice Duel following Dr. Mason's instructions, +; this will always lead to the AI correctly switching Raticate with Machop, +; but in case of a "Free" Duel where the first Machop is not KO'd, +; the intention was to switch to PLAY_AREA_BENCH_2 instead. +; but due to 'inc a' always being skipped, it will switch to Raticate. + ld a, DUELVARS_ARENA_CARD + call GetTurnDuelistVariable + cp MACHOP ; wrong + ld a, PLAY_AREA_BENCH_1 + jr nz, .retreat + inc a ; PLAY_AREA_BENCH_2 + +.retreat + call AITryToRetreat + ret + +.turn_6 ; 148cc (5:48cc) + ld d, MACHOP + ld e, FIGHTING_ENERGY + call AIAttachEnergyInHandToCardInPlayArea + ret + +.turn_7 ; 148d4 (5:48d4) + ld d, MACHOP + ld e, FIGHTING_ENERGY + call AIAttachEnergyInHandToCardInPlayArea + ret diff --git a/src/engine/duel/ai/decks/strange_psyshock.asm b/src/engine/duel/ai/decks/strange_psyshock.asm new file mode 100644 index 0000000..ef378b0 --- /dev/null +++ b/src/engine/duel/ai/decks/strange_psyshock.asm @@ -0,0 +1,81 @@ +AIActionTable_StrangePsyshock: ; 15122 (5:5122) + dw .do_turn ; unused + dw .do_turn + dw .start_duel + dw .forced_switch + dw .ko_switch + dw .take_prize + +.do_turn ; 1512e (5:512e) + call AIMainTurnLogic + ret + +.start_duel ; 15132 (5:5132) + call InitAIDuelVars + call .store_list_pointers + call SetUpBossStartingHandAndDeck + call TrySetUpBossStartingPlayArea + ret nc + call AIPlayInitialBasicCards + ret + +.forced_switch ; 15143 (5:5143) + call AIDecideBenchPokemonToSwitchTo + ret + +.ko_switch ; 15147 (5:5147) + call AIDecideBenchPokemonToSwitchTo + ret + +.take_prize ; 1514b (5:514b) + call AIPickPrizeCards + ret + +.list_arena ; 1514f (5:514f) + db KANGASKHAN + db CHANSEY + db SNORLAX + db MR_MIME + db ABRA + db $00 + +.list_bench ; 15155 (5:5155) + db ABRA + db MR_MIME + db KANGASKHAN + db SNORLAX + db CHANSEY + db $00 + +.list_retreat ; 1515b (5:515b) + ai_retreat ABRA, -3 + ai_retreat SNORLAX, -3 + ai_retreat KANGASKHAN, -1 + ai_retreat CHANSEY, -1 + db $00 + +.list_energy ; 15164 (5:5164) + ai_energy ABRA, 3, +1 + ai_energy KADABRA, 3, +0 + ai_energy ALAKAZAM, 3, +0 + ai_energy MR_MIME, 2, +0 + ai_energy CHANSEY, 2, -2 + ai_energy KANGASKHAN, 4, -2 + ai_energy SNORLAX, 0, -8 + db $00 + +.list_prize ; 1517a (5:517a) + db GAMBLER + db MR_MIME + db ALAKAZAM + db SWITCH + db $00 + +.store_list_pointers ; 1517f (5:517f) + store_list_pointer wAICardListAvoidPrize, .list_prize + store_list_pointer wAICardListArenaPriority, .list_arena + store_list_pointer wAICardListBenchPriority, .list_bench + store_list_pointer wAICardListPlayFromHandPriority, .list_bench + ; missing store_list_pointer wAICardListRetreatBonus, .list_retreat + store_list_pointer wAICardListEnergyBonus, .list_energy + ret diff --git a/src/engine/duel/ai/decks/unreferenced.asm b/src/engine/duel/ai/decks/unreferenced.asm new file mode 100644 index 0000000..8722a27 --- /dev/null +++ b/src/engine/duel/ai/decks/unreferenced.asm @@ -0,0 +1,42 @@ +AIActionTable_Unreferenced: ; 1406a (5:406a) + dw $406c + dw .do_turn + dw .do_turn + dw .star_duel + dw .forced_switch + dw .ko_switch + dw .take_prize + +.do_turn + call AIDecidePlayPokemonCard + call AIDecideWhetherToRetreat + jr nc, .try_attack + call AIDecideBenchPokemonToSwitchTo + call AITryToRetreat + call AIDecideWhetherToRetreat + jr nc, .try_attack + call AIDecideBenchPokemonToSwitchTo + call AITryToRetreat +.try_attack + call AIProcessAndTryToPlayEnergy + call AIProcessAndTryToUseAttack + ret c + ld a, OPPACTION_FINISH_NO_ATTACK + bank1call AIMakeDecision + ret + +.star_duel + call AIPlayInitialBasicCards + ret + +.forced_switch + call AIDecideBenchPokemonToSwitchTo + ret + +.ko_switch + call AIDecideBenchPokemonToSwitchTo + ret + +.take_prize + call AIPickPrizeCards + ret diff --git a/src/engine/duel/ai/decks/wonders_of_science.asm b/src/engine/duel/ai/decks/wonders_of_science.asm new file mode 100644 index 0000000..706a7e6 --- /dev/null +++ b/src/engine/duel/ai/decks/wonders_of_science.asm @@ -0,0 +1,77 @@ +AIActionTable_WondersOfScience: ; 151ad (5:51ad) + dw .do_turn ; unused + dw .do_turn + dw .start_duel + dw .forced_switch + dw .ko_switch + dw .take_prize + +.do_turn ; 151b9 (5:51b9) + call AIMainTurnLogic + ret + +.start_duel ; 151bd (5:51bd) + call InitAIDuelVars + call .store_list_pointers + call SetUpBossStartingHandAndDeck + call TrySetUpBossStartingPlayArea + ret nc + call AIPlayInitialBasicCards + ret + +.forced_switch ; 151ce (5:51ce) + call AIDecideBenchPokemonToSwitchTo + ret + +.ko_switch ; 151d2 (5:51d2) + call AIDecideBenchPokemonToSwitchTo + ret + +.take_prize ; 151d6 (5:51d6) + call AIPickPrizeCards + ret + +.list_arena ; 151da (5:51da) + db MEWTWO1 + db MEWTWO3 + db MEWTWO2 + db GRIMER + db KOFFING + db PORYGON + db $00 + +.list_bench ; 151e1 (5:51e1) + db GRIMER + db KOFFING + db MEWTWO3 + db MEWTWO2 + db MEWTWO1 + db PORYGON + db $00 + +.list_retreat ; 151e8 (5:51e8) + db $00 + +.list_energy ; 151e9 (5:51e9) + ai_energy GRIMER, 3, +0 + ai_energy MUK, 4, +0 + ai_energy KOFFING, 2, +0 + ai_energy WEEZING, 3, +0 + ai_energy MEWTWO1, 2, -1 + ai_energy MEWTWO3, 2, -1 + ai_energy MEWTWO2, 2, -1 + ai_energy PORYGON, 2, -1 + db $00 + +.list_prize ; 15202 (5:5202) + db MUK + db $00 + +.store_list_pointers ; 15204 (5:5204) + store_list_pointer wAICardListAvoidPrize, .list_prize + store_list_pointer wAICardListArenaPriority, .list_arena + store_list_pointer wAICardListBenchPriority, .list_bench + store_list_pointer wAICardListPlayFromHandPriority, .list_bench + ; missing store_list_pointer wAICardListRetreatBonus, .list_retreat + store_list_pointer wAICardListEnergyBonus, .list_energy + ret diff --git a/src/engine/duel/ai/decks/zapping_selfdestruct.asm b/src/engine/duel/ai/decks/zapping_selfdestruct.asm new file mode 100644 index 0000000..da5e7c6 --- /dev/null +++ b/src/engine/duel/ai/decks/zapping_selfdestruct.asm @@ -0,0 +1,75 @@ +AIActionTable_ZappingSelfdestruct: ; 15019 (5:5019) + dw .do_turn ; unused + dw .do_turn + dw .start_duel + dw .forced_switch + dw .ko_switch + dw .take_prize + +.do_turn ; 15025 (5:5025) + call AIMainTurnLogic + ret + +.start_duel ; 15029 (5:5029) + call InitAIDuelVars + call .store_list_pointers + call SetUpBossStartingHandAndDeck + call TrySetUpBossStartingPlayArea + ret nc + call AIPlayInitialBasicCards + ret + +.forced_switch ; 1503a (5:503a) + call AIDecideBenchPokemonToSwitchTo + ret + +.ko_switch ; 1503e (5:503e) + call AIDecideBenchPokemonToSwitchTo + ret + +.take_prize ; 15042 (5:5042) + call AIPickPrizeCards + ret + +.list_arena ; 15046 (5:5046) + db KANGASKHAN + db ELECTABUZZ2 + db TAUROS + db MAGNEMITE1 + db VOLTORB + db $00 + +.list_bench ; 1504c (5:504c) + db MAGNEMITE1 + db VOLTORB + db ELECTABUZZ2 + db TAUROS + db KANGASKHAN + db $00 + +.list_retreat ; 15052 (5:5052) + ai_retreat VOLTORB, -1 + db $00 + +.list_energy ; 15055 (5:5055) + ai_energy MAGNEMITE1, 3, +1 + ai_energy MAGNETON1, 4, +0 + ai_energy VOLTORB, 3, +1 + ai_energy ELECTRODE1, 3, +0 + ai_energy ELECTABUZZ2, 1, +0 + ai_energy KANGASKHAN, 2, -2 + ai_energy TAUROS, 3, +0 + db $00 + +.list_prize ; 1506b (5:506b) + db KANGASKHAN + db $00 + +.store_list_pointers ; 1506d (5:506d) + store_list_pointer wAICardListAvoidPrize, .list_prize + store_list_pointer wAICardListArenaPriority, .list_arena + store_list_pointer wAICardListBenchPriority, .list_bench + store_list_pointer wAICardListPlayFromHandPriority, .list_bench + ; missing store_list_pointer wAICardListRetreatBonus, .list_retreat + store_list_pointer wAICardListEnergyBonus, .list_energy + ret diff --git a/src/engine/duel/ai/energy.asm b/src/engine/duel/ai/energy.asm new file mode 100644 index 0000000..ce8c037 --- /dev/null +++ b/src/engine/duel/ai/energy.asm @@ -0,0 +1,1048 @@ +; processes AI energy card playing logic +; with AI_ENERGY_FLAG_DONT_PLAY flag on +; unreferenced +Func_16488: ; 16488 (5:6488) + ld a, AI_ENERGY_FLAG_DONT_PLAY + ld [wAIEnergyAttachLogicFlags], a + ld de, wTempPlayAreaAIScore + ld hl, wPlayAreaAIScore + ld b, MAX_PLAY_AREA_POKEMON +.loop + ld a, [hli] + ld [de], a + inc de + dec b + jr nz, .loop + ld a, [wAIScore] + ld [de], a + jr AIProcessAndTryToPlayEnergy.has_logic_flags + +; have AI choose an energy card to play, but do not play it. +; does not consider whether the cards have evolutions to be played. +; return carry if an energy card is chosen to use in any Play Area card, +; and if so, return its Play Area location in hTempPlayAreaLocation_ff9d. +AIProcessButDontPlayEnergy_SkipEvolution: ; 164a1 (5:64a1) + ld a, AI_ENERGY_FLAG_DONT_PLAY | AI_ENERGY_FLAG_SKIP_EVOLUTION + ld [wAIEnergyAttachLogicFlags], a + +; backup wPlayAreaAIScore in wTempPlayAreaAIScore. + ld de, wTempPlayAreaAIScore + ld hl, wPlayAreaAIScore + ld b, MAX_PLAY_AREA_POKEMON +.loop + ld a, [hli] + ld [de], a + inc de + dec b + jr nz, .loop + + ld a, [wAIScore] + ld [de], a + + jr AIProcessEnergyCards + +; have AI choose an energy card to play, but do not play it. +; does not consider whether the cards have evolutions to be played. +; return carry if an energy card is chosen to use in any Bench card, +; and if so, return its Play Area location in hTempPlayAreaLocation_ff9d. +AIProcessButDontPlayEnergy_SkipEvolutionAndArena: ; 164ba (5:64ba) + ld a, AI_ENERGY_FLAG_DONT_PLAY | AI_ENERGY_FLAG_SKIP_EVOLUTION | AI_ENERGY_FLAG_SKIP_ARENA_CARD + ld [wAIEnergyAttachLogicFlags], a + +; backup wPlayAreaAIScore in wTempPlayAreaAIScore. + ld de, wTempPlayAreaAIScore + ld hl, wPlayAreaAIScore + ld b, MAX_PLAY_AREA_POKEMON +.loop + ld a, [hli] + ld [de], a + inc de + dec b + jr nz, .loop + + ld a, [wAIScore] + ld [de], a + + jr AIProcessEnergyCards + +; copies wTempPlayAreaAIScore to wPlayAreaAIScore +; and loads wAIScore with value in wTempAIScore. +; identical to RetrievePlayAreaAIScoreFromBackup2. +RetrievePlayAreaAIScoreFromBackup1: ; 164d3 (5:64d3) + push af + ld de, wPlayAreaAIScore + ld hl, wTempPlayAreaAIScore + ld b, MAX_PLAY_AREA_POKEMON +.loop + ld a, [hli] + ld [de], a + inc de + dec b + jr nz, .loop + ld a, [hl] + ld [wAIScore], a + pop af + ret + +; have AI decide whether to play energy card from hand +; and determine which card is best to attach it. +AIProcessAndTryToPlayEnergy: ; 164e8 (5:64e8) + xor a + ld [wAIEnergyAttachLogicFlags], a + +.has_logic_flags + call CreateEnergyCardListFromHand + jr nc, AIProcessEnergyCards + +; no energy + ld a, [wAIEnergyAttachLogicFlags] + or a + jr z, .exit + jp RetrievePlayAreaAIScoreFromBackup1 +.exit + or a + ret + +; have AI decide whether to play energy card +; and determine which card is best to attach it. +AIProcessEnergyCards: ; 164fc (5:64fc) +; initialize Play Area AI score + ld a, $80 + ld b, MAX_PLAY_AREA_POKEMON + ld hl, wPlayAreaEnergyAIScore +.loop + ld [hli], a + dec b + jr nz, .loop + +; Legendary Articuno Deck has its own energy card logic + call HandleLegendaryArticunoEnergyScoring + +; start the main Play Area loop + ld b, PLAY_AREA_ARENA + ld a, DUELVARS_NUMBER_OF_POKEMON_IN_PLAY_AREA + call GetTurnDuelistVariable + ld c, a + +.loop_play_area + push bc + ld a, b + ldh [hTempPlayAreaLocation_ff9d], a + ld a, $80 + ld [wAIScore], a + ld a, $ff + ld [wTempAI], a + ld a, [wAIEnergyAttachLogicFlags] + and AI_ENERGY_FLAG_SKIP_EVOLUTION + jr nz, .check_venusaur + +; check if energy needed is found in hand +; and if there's an evolution in hand or deck +; and if so, add to AI score + call CreateHandCardList + ldh a, [hTempPlayAreaLocation_ff9d] + add DUELVARS_ARENA_CARD + call GetTurnDuelistVariable + ld [wCurCardCanAttack], a + call GetAttacksEnergyCostBits + ld hl, wDuelTempList + call CheckEnergyFlagsNeededInList + jp nc, .store_score + ld a, [wCurCardCanAttack] + call CheckForEvolutionInList + jr nc, .no_evolution_in_hand + ld [wTempAI], a ; store evolution card found + ld a, 2 + call AddToAIScore + jr .check_venusaur + +.no_evolution_in_hand + ld a, [wCurCardCanAttack] + call CheckForEvolutionInDeck + jr nc, .check_venusaur + ld a, 1 + call AddToAIScore + +; if there's no Muk in any Play Area +; and there's Venusaur2 in own Play Area, +; add to AI score +.check_venusaur + ld a, MUK + call CountPokemonIDInBothPlayAreas + jr c, .check_if_active + ld a, VENUSAUR2 + call CountPokemonIDInPlayArea + jr nc, .check_if_active + ld a, 1 + call AddToAIScore + +.check_if_active + ldh a, [hTempPlayAreaLocation_ff9d] + or a + jr nz, .bench + +; arena + ld a, [wAIBarrierFlagCounter] + bit AI_MEWTWO_MILL_F, a + jr z, .add_to_score + +; subtract from score instead +; if Player is running Mewtwo1 mill deck. + ld a, 5 + call SubFromAIScore + jr .check_defending_can_ko + +.add_to_score + ld a, 4 + call AddToAIScore + +; lower AI score if poison/double poison +; will KO Pokémon between turns +; or if the defending Pokémon can KO + ld a, DUELVARS_ARENA_CARD_HP + call GetTurnDuelistVariable + call CalculateByteTensDigit + cp 3 + jr nc, .check_defending_can_ko + ; hp < 30 + cp 2 + jr z, .has_20_hp + ; hp = 10 + ld a, DUELVARS_ARENA_CARD_STATUS + call GetTurnDuelistVariable + and POISONED + jr z, .check_defending_can_ko + jr .poison_will_ko +.has_20_hp + ld a, DUELVARS_ARENA_CARD_STATUS + call GetTurnDuelistVariable + and DOUBLE_POISONED + jr z, .check_defending_can_ko +.poison_will_ko + ld a, 10 + call SubFromAIScore + jr .check_bench +.check_defending_can_ko + call CheckIfDefendingPokemonCanKnockOut + jr nc, .ai_score_bonus + ld a, 10 + call SubFromAIScore + +; if either poison will KO or defending Pokémon can KO, +; check if there are bench Pokémon, +; if there are not, add AI score +.check_bench + ld a, DUELVARS_NUMBER_OF_POKEMON_IN_PLAY_AREA + call GetTurnDuelistVariable + dec a + jr nz, .ai_score_bonus + ld a, 6 + call AddToAIScore + jr .ai_score_bonus + +; lower AI score by 3 - (bench HP)/10 +; if bench HP < 30 +.bench + add DUELVARS_ARENA_CARD_HP + call GetTurnDuelistVariable + call CalculateByteTensDigit + cp 3 + jr nc, .ai_score_bonus +; hp < 30 + ld b, a + ld a, 3 + sub b + call SubFromAIScore + +; check list in wAICardListEnergyBonus +.ai_score_bonus + ld a, [wAICardListEnergyBonus + 1] + or a + jr z, .check_boss_deck ; is null + ld h, a + ld a, [wAICardListEnergyBonus] + ld l, a + + push hl + ldh a, [hTempPlayAreaLocation_ff9d] + add DUELVARS_ARENA_CARD + call GetTurnDuelistVariable + call GetCardIDFromDeckIndex + pop hl + +.loop_id_list + ld a, [hli] + or a + jr z, .check_boss_deck + cp e + jr nz, .next_id + + ; number of attached energy cards + ld a, [hli] + ld d, a + push de + ldh a, [hTempPlayAreaLocation_ff9d] + ld e, a + call GetPlayAreaCardAttachedEnergies + ld a, [wTotalAttachedEnergies] + pop de + cp d + jr c, .check_id_score + ; already reached target number of energy cards + ld a, 10 + call SubFromAIScore + jr .store_score + +.check_id_score + ld a, [hli] + cp $80 + jr c, .decrease_score_1 + sub $80 + call AddToAIScore + jr .check_boss_deck + +.decrease_score_1 + ld d, a + ld a, $80 + sub d + call SubFromAIScore + jr .check_boss_deck + +.next_id + inc hl + inc hl + jr .loop_id_list + +; if it's a boss deck, call Func_174f2 +; and apply to the AI score the values +; determined for this card +.check_boss_deck + call CheckIfNotABossDeckID + jr c, .skip_boss_deck + + call Func_174f2 + ldh a, [hTempPlayAreaLocation_ff9d] + ld c, a + ld b, $00 + ld hl, wPlayAreaEnergyAIScore + add hl, bc + ld a, [hl] + cp $80 + jr c, .decrease_score_2 + sub $80 + call AddToAIScore + jr .skip_boss_deck + +.decrease_score_2 + ld b, a + ld a, $80 + sub b + call SubFromAIScore + +.skip_boss_deck + ld a, 1 + call AddToAIScore + +; add AI score for both attacks, +; according to their energy requirements. + xor a ; first attack + call DetermineAIScoreOfAttackEnergyRequirement + ld a, SECOND_ATTACK + call DetermineAIScoreOfAttackEnergyRequirement + +; store bench score for this card. +.store_score + ldh a, [hTempPlayAreaLocation_ff9d] + ld c, a + ld b, $00 + ld hl, wPlayAreaAIScore + add hl, bc + ld a, [wAIScore] + ld [hl], a + pop bc + inc b + dec c + jp nz, .loop_play_area + +; the Play Area loop is over and the score +; for each card has been calculated. +; now to determine the highest score. + call FindPlayAreaCardWithHighestAIScore + jp nc, .not_found + + ld a, [wAIEnergyAttachLogicFlags] + or a + jr z, .play_card + scf + jp RetrievePlayAreaAIScoreFromBackup1 + +.play_card + call CreateEnergyCardListFromHand + jp AITryToPlayEnergyCard + +.not_found: ; 1668a (5:668a) + ld a, [wAIEnergyAttachLogicFlags] + or a + jr z, .no_carry + jp RetrievePlayAreaAIScoreFromBackup1 +.no_carry + or a + ret + +; checks score related to selected attack, +; in order to determine whether to play energy card. +; the AI score is increased/decreased accordingly. +; input: +; [wSelectedAttack] = attack to check. +DetermineAIScoreOfAttackEnergyRequirement: ; 16695 (5:6695) + ld [wSelectedAttack], a + call CheckEnergyNeededForAttack + jp c, .not_enough_energy + ld a, ATTACK_FLAG2_ADDRESS | ATTACHED_ENERGY_BOOST_F + call CheckLoadedAttackFlag + jr c, .attached_energy_boost + ld a, ATTACK_FLAG2_ADDRESS | DISCARD_ENERGY_F + call CheckLoadedAttackFlag + jr c, .discard_energy + jp .check_evolution + +.attached_energy_boost + ld a, [wLoadedAttackEffectParam] + cp MAX_ENERGY_BOOST_IS_LIMITED + jr z, .check_surplus_energy + + ; is MAX_ENERGY_BOOST_IS_NOT_LIMITED, + ; which is equal to 3, add to score. + call AddToAIScore + jp .check_evolution + +.check_surplus_energy + call CheckIfNoSurplusEnergyForAttack + jr c, .asm_166cd + cp 3 ; check how much surplus energy + jr c, .asm_166cd + +.asm_166c5 + ld a, 5 + call SubFromAIScore + jp .check_evolution + +.asm_166cd + ld a, 2 + call AddToAIScore + +; check whether attack has ATTACHED_ENERGY_BOOST flag +; and add to AI score if attaching another energy +; will KO defending Pokémon. +; add more to score if this is currently active Pokémon. + ld a, ATTACK_FLAG2_ADDRESS | ATTACHED_ENERGY_BOOST_F + call CheckLoadedAttackFlag + jp nc, .check_evolution + ld a, [wSelectedAttack] + call EstimateDamage_VersusDefendingCard + ld a, DUELVARS_ARENA_CARD_HP + call GetNonTurnDuelistVariable + ld hl, wDamage + sub [hl] + jp c, .check_evolution + jp z, .check_evolution + ld a, [wDamage] + add 10 ; boost gained by attaching another energy card + ld b, a + ld a, DUELVARS_ARENA_CARD_HP + call GetNonTurnDuelistVariable + sub b + jr c, .attaching_kos_player + jr nz, .check_evolution + +.attaching_kos_player + ld a, 20 + call AddToAIScore + ldh a, [hTempPlayAreaLocation_ff9d] + or a + jr nz, .check_evolution + ld a, 10 + call AddToAIScore + jr .check_evolution + +; checks if there is surplus energy for attack +; that discards attached energy card. +; if current card is Zapdos2, don't add to score. +; if there is no surplus energy, encourage playing energy. +.discard_energy + ld a, [wLoadedCard1ID] + cp ZAPDOS2 + jr z, .check_evolution + call CheckIfNoSurplusEnergyForAttack + jr c, .asm_166cd + jr .asm_166c5 + +.not_enough_energy + ld a, ATTACK_FLAG2_ADDRESS | FLAG_2_BIT_5_F + call CheckLoadedAttackFlag + jr nc, .check_color_needed + ld a, 5 + call SubFromAIScore + +; if the energy card color needed is in hand, increase AI score. +; if a colorless card is needed, increase AI score. +.check_color_needed + ld a, b + or a + jr z, .check_colorless_needed + ld a, e + call LookForCardIDInHand + jr c, .check_colorless_needed + ld a, 4 + call AddToAIScore + jr .check_total_needed +.check_colorless_needed + ld a, c + or a + jr z, .check_evolution + ld a, 3 + call AddToAIScore + +; if only one energy card is needed for attack, +; encourage playing energy card. +.check_total_needed + ld a, b + add c + dec a + jr nz, .check_evolution + ld a, 3 + call AddToAIScore + +; if the attack KOs player and this is the active card, add to AI score. + ldh a, [hTempPlayAreaLocation_ff9d] + or a + jr nz, .check_evolution + ld a, [wSelectedAttack] + call EstimateDamage_VersusDefendingCard + ld a, DUELVARS_ARENA_CARD_HP + call GetNonTurnDuelistVariable + ld hl, wDamage + sub [hl] + jr z, .atk_kos_defending + jr nc, .check_evolution +.atk_kos_defending + ld a, 20 + call AddToAIScore + +; this is possibly a bug. +; this is an identical check as above to test whether this card is active. +; in case it is active, the score gets added 10 more points, +; in addition to the 20 points already added above. +; what was probably intended was to add 20 points +; plus 10 in case it is the Arena card. + ldh a, [hTempPlayAreaLocation_ff9d] + or a + jr nz, .check_evolution + ld a, 10 + call AddToAIScore + +.check_evolution + ld a, [wTempAI] ; evolution in hand + cp $ff + ret z + +; temporarily replace this card with evolution in hand. + ld b, a + ldh a, [hTempPlayAreaLocation_ff9d] + add DUELVARS_ARENA_CARD + call GetTurnDuelistVariable + push af + ld [hl], b + +; check for energy still needed for evolution to attack. +; if FLAG_2_BIT_5 is not set, check what color is needed. +; if the energy card color needed is in hand, increase AI score. +; if a colorless card is needed, increase AI score. + call CheckEnergyNeededForAttack + jr nc, .done + ld a, ATTACK_FLAG2_ADDRESS | FLAG_2_BIT_5_F + call CheckLoadedAttackFlag + jr c, .done + ld a, b + or a + jr z, .check_colorless_needed_evo + ld a, e + call LookForCardIDInHand + jr c, .check_colorless_needed_evo + ld a, 2 + call AddToAIScore + jr .done +.check_colorless_needed_evo + ld a, c + or a + jr z, .done + ld a, 1 + call AddToAIScore + +; recover the original card in the Play Area location. +.done + ldh a, [hTempPlayAreaLocation_ff9d] + add DUELVARS_ARENA_CARD + call GetTurnDuelistVariable + pop af + ld [hl], a + ret + +; returns in hTempPlayAreaLocation_ff9d the Play Area location +; of the card with the highest Play Area AI score, unless +; the highest score is below $85. +; if it succeeds in return a card location, set carry. +; if AI_ENERGY_FLAG_SKIP_ARENA_CARD is set in wAIEnergyAttachLogicFlags +; doesn't include the Arena card and there's no minimum score. +FindPlayAreaCardWithHighestAIScore: ; 167b5 (5:67b5) + ld a, [wAIEnergyAttachLogicFlags] + and AI_ENERGY_FLAG_SKIP_ARENA_CARD + jr nz, .only_bench + + ld a, DUELVARS_NUMBER_OF_POKEMON_IN_PLAY_AREA + call GetTurnDuelistVariable + ld b, a + ld c, PLAY_AREA_ARENA + ld e, c + ld d, c + ld hl, wPlayAreaAIScore +; find highest Play Area AI score. +.loop_1 + ld a, [hli] + cp e + jr c, .next_1 + jr z, .next_1 + ld e, a ; overwrite highest score found + ld d, c ; overwrite Play Area of highest score +.next_1 + inc c + dec b + jr nz, .loop_1 + +; if highest AI score is below $85, return no carry. +; else, store Play Area location and return carry. + ld a, e + cp $85 + jr c, .not_enough_score + ld a, d + ldh [hTempPlayAreaLocation_ff9d], a + scf + ret +.not_enough_score + or a + ret + +; same as above but only check bench Pokémon scores. +.only_bench + ld a, DUELVARS_NUMBER_OF_POKEMON_IN_PLAY_AREA + call GetTurnDuelistVariable + dec a + jr z, .no_carry + + ld b, a + ld e, 0 + ld c, PLAY_AREA_BENCH_1 + ld d, c + ld hl, wPlayAreaAIScore + 1 +.loop_2 + ld a, [hli] + cp e + jr c, .next_2 + jr z, .next_2 + ld e, a ; overwrite highest score found + ld d, c ; overwrite Play Area of highest score +.next_2 + inc c + dec b + jr nz, .loop_2 + +; in this case, there is no minimum threshold AI score. + ld a, d + ldh [hTempPlayAreaLocation_ff9d], a + scf + ret +.no_carry + or a + ret + +; returns carry if there's an evolution card +; that can evolve card in hTempPlayAreaLocation_ff9d, +; and that card needs energy to use wSelectedAttack. +CheckIfEvolutionNeedsEnergyForAttack: ; 16805 (5:6805) + call CreateHandCardList + ldh a, [hTempPlayAreaLocation_ff9d] + add DUELVARS_ARENA_CARD + call GetTurnDuelistVariable + call CheckCardEvolutionInHandOrDeck + jr c, .has_evolution + or a + ret + +.has_evolution + ld b, a + ldh a, [hTempPlayAreaLocation_ff9d] + add DUELVARS_ARENA_CARD + call GetTurnDuelistVariable + push af + ld [hl], b + call CheckEnergyNeededForAttack + jr c, .not_enough_energy + ldh a, [hTempPlayAreaLocation_ff9d] + add DUELVARS_ARENA_CARD + call GetTurnDuelistVariable + pop af + ld [hl], a + or a + ret + +.not_enough_energy + ldh a, [hTempPlayAreaLocation_ff9d] + add DUELVARS_ARENA_CARD + call GetTurnDuelistVariable + pop af + ld [hl], a + scf + ret + +; returns in e the card ID of the energy required for +; the Discard/Energy Boost attack loaded in wSelectedAttack. +; if it's Zapdos2's Thunderbolt attack, return no carry. +; if it's Charizard's Fire Spin or Exeggutor's Big Eggsplosion +; attack, don't return energy card ID, but set carry. +; output: +; b = 1 if needs color energy, 0 otherwise; +; c = 1 if only needs colorless energy, 0 otherwise; +; carry set if not Zapdos2's Thunderbolt attack. +GetEnergyCardForDiscardOrEnergyBoostAttack: ; 1683b (5:683b) +; load card ID and check selected attack index. + ldh a, [hTempPlayAreaLocation_ff9d] + add DUELVARS_ARENA_CARD + call GetTurnDuelistVariable + call LoadCardDataToBuffer2_FromDeckIndex + ld b, a + ld a, [wSelectedAttack] + or a + jr z, .first_attack + +; check if second attack is Zapdos2's Thunderbolt, +; Charizard's Fire Spin or Exeggutor's Big Eggsplosion, +; for these to be treated differently. +; for both attacks, load its energy cost. + ld a, b + cp ZAPDOS2 + jr z, .zapdos2 + cp CHARIZARD + jr z, .charizard_or_exeggutor + cp EXEGGUTOR + jr z, .charizard_or_exeggutor + ld hl, wLoadedCard2Atk2EnergyCost + jr .fire +.first_attack + ld hl, wLoadedCard2Atk1EnergyCost + +; check which energy color the attack requires, +; and load in e the card ID of corresponding energy card, +; then return carry flag set. +.fire + ld a, [hli] + ld b, a + and $f0 + jr z, .grass + ld e, FIRE_ENERGY + jr .set_carry +.grass + ld a, b + and $0f + jr z, .lightning + ld e, GRASS_ENERGY + jr .set_carry +.lightning + ld a, [hli] + ld b, a + and $f0 + jr z, .water + ld e, LIGHTNING_ENERGY + jr .set_carry +.water + ld a, b + and $0f + jr z, .fighting + ld e, WATER_ENERGY + jr .set_carry +.fighting + ld a, [hli] + ld b, a + and $f0 + jr z, .psychic + ld e, FIGHTING_ENERGY + jr .set_carry +.psychic + ld e, PSYCHIC_ENERGY + +.set_carry + lb bc, $01, $00 + scf + ret + +; for Zapdos2's Thunderbolt attack, return with no carry. +.zapdos2 + or a + ret + +; Charizard's Fire Spin and Exeggutor's Big Eggsplosion, +; return carry. +.charizard_or_exeggutor + lb bc, $00, $01 + scf + ret + +; called after the AI has decided which card to attach +; energy from hand. AI does checks to determine whether +; this card needs more energy or not, and chooses the +; right energy card to play. If the card is played, +; return with carry flag set. +AITryToPlayEnergyCard: ; 1689f (5:689f) +; check if energy cards are still needed for attacks. +; if first attack doesn't need, test for the second attack. + xor a + ld [wTempAI], a + ld [wSelectedAttack], a + call CheckEnergyNeededForAttack + jr nc, .second_attack + ld a, b + or a + jr nz, .check_deck + ld a, c + or a + jr nz, .check_deck + +.second_attack + ld a, SECOND_ATTACK + ld [wSelectedAttack], a + call CheckEnergyNeededForAttack + jr nc, .check_discard_or_energy_boost + ld a, b + or a + jr nz, .check_deck + ld a, c + or a + jr nz, .check_deck + +; neither attack needs energy cards to be used. +; check whether these attacks can be given +; extra energy cards for their effects. +.check_discard_or_energy_boost + ld a, $01 + ld [wTempAI], a + +; for both attacks, check if it has the effect of +; discarding energy cards or attached energy boost. + xor a ; FIRST_ATTACK_OR_PKMN_POWER + ld [wSelectedAttack], a + call CheckEnergyNeededForAttack + ld a, ATTACK_FLAG2_ADDRESS | ATTACHED_ENERGY_BOOST_F + call CheckLoadedAttackFlag + jr c, .energy_boost_or_discard_energy + ld a, ATTACK_FLAG2_ADDRESS | DISCARD_ENERGY_F + call CheckLoadedAttackFlag + jr c, .energy_boost_or_discard_energy + + ld a, SECOND_ATTACK + ld [wSelectedAttack], a + call CheckEnergyNeededForAttack + ld a, ATTACK_FLAG2_ADDRESS | ATTACHED_ENERGY_BOOST_F + call CheckLoadedAttackFlag + jr c, .energy_boost_or_discard_energy + ld a, ATTACK_FLAG2_ADDRESS | DISCARD_ENERGY_F + call CheckLoadedAttackFlag + jr c, .energy_boost_or_discard_energy + +; if none of the attacks have those flags, do an additional +; check to ascertain whether evolution card needs energy +; to use second attack. Return if all these checks fail. + call CheckIfEvolutionNeedsEnergyForAttack + ret nc + call CreateEnergyCardListFromHand + jr .check_deck + +; for attacks that discard energy or get boost for +; additional energy cards, get the energy card ID required by attack. +; if it's Zapdos2's Thunderbolt attack, return. +.energy_boost_or_discard_energy + call GetEnergyCardForDiscardOrEnergyBoostAttack + ret nc + +; some decks allow basic Pokémon to be given double colorless +; in anticipation for evolution, so play card if that is the case. +.check_deck + call CheckSpecificDecksToAttachDoubleColorless + jr c, .play_energy_card + + ld a, b + or a + jr z, .colorless_energy + +; in this case, Pokémon needs a specific basic energy card. +; look for basic energy card needed in hand and play it. + ld a, e + call LookForCardIDInHand + ldh [hTemp_ffa0], a + jr nc, .play_energy_card + +; in this case Pokémon just needs colorless (any basic energy card). +; if active card, check if it needs 2 colorless. +; if it does (and also doesn't additionally need a color energy), +; look for double colorless card in hand and play it if found. +.colorless_energy + ldh a, [hTempPlayAreaLocation_ff9d] + or a + jr nz, .look_for_any_energy + ld a, c + or a + jr z, .check_if_done + cp 2 + jr nz, .look_for_any_energy + + ; needs two colorless + ld hl, wDuelTempList +.loop_1 + ld a, [hli] + cp $ff + jr z, .look_for_any_energy + ldh [hTemp_ffa0], a + call GetCardIDFromDeckIndex + ld a, e + cp DOUBLE_COLORLESS_ENERGY + jr nz, .loop_1 + jr .play_energy_card + +; otherwise, look for any card and play it. +; if it's a boss deck, only play double colorless in this situation. +.look_for_any_energy + ld hl, wDuelTempList + call CountCardsInDuelTempList + call ShuffleCards +.loop_2 + ld a, [hli] + cp $ff + jr z, .check_if_done + call CheckIfOpponentHasBossDeckID + jr nc, .load_card + push af + call GetCardIDFromDeckIndex + ld a, e + cp DOUBLE_COLORLESS_ENERGY + pop bc + jr z, .loop_2 + ld a, b +.load_card + ldh [hTemp_ffa0], a + +; plays energy card loaded in hTemp_ffa0 and sets carry flag. +.play_energy_card + ldh a, [hTempPlayAreaLocation_ff9d] + ldh [hTempPlayAreaLocation_ffa1], a + ld a, OPPACTION_PLAY_ENERGY + bank1call AIMakeDecision + scf + ret + +; wTempAI is 1 if the attack had a Discard/Energy Boost effect, +; and 0 otherwise. If 1, then return. If not one, check if +; there is still a second attack to check. +.check_if_done + ld a, [wTempAI] + or a + jr z, .check_first_attack + ret +.check_first_attack + ld a, [wSelectedAttack] + or a + jp z, .second_attack + ret + +; check if playing certain decks so that AI can decide whether to play +; double colorless to some specific cards. +; these are cards that do not need double colorless to any of their attacks +; but are required by their evolutions. +; return carry if there's a double colorless in hand to attach +; and it's one of the card IDs from these decks. +; output: +; [hTemp_ffa0] = card index of double colorless in hand; +; carry set if can play energy card. +CheckSpecificDecksToAttachDoubleColorless: ; 1696e (5:696e) + push bc + push de + push hl + +; check if AI is playing any of the applicable decks. + ld a, [wOpponentDeckID] + cp LEGENDARY_DRAGONITE_DECK_ID + jr z, .legendary_dragonite_deck + cp FIRE_CHARGE_DECK_ID + jr z, .fire_charge_deck + cp LEGENDARY_RONALD_DECK_ID + jr z, .legendary_ronald_deck + +.no_carry + pop hl + pop de + pop bc + or a + ret + +; if playing Legendary Dragonite deck, +; check for Charmander and Dratini. +.legendary_dragonite_deck + call .get_id + cp CHARMANDER + jr z, .check_colorless_attached + cp DRATINI + jr z, .check_colorless_attached + jr .no_carry + +; if playing Fire Charge deck, +; check for Growlithe. +.fire_charge_deck + call .get_id + cp GROWLITHE + jr z, .check_colorless_attached + jr .no_carry + +; if playing Legendary Ronald deck, +; check for Dratini. +.legendary_ronald_deck + call .get_id + cp DRATINI + jr z, .check_colorless_attached + jr .no_carry + +; check if card has any colorless energy cards attached, +; and if there are any, return no carry. +.check_colorless_attached + ldh a, [hTempPlayAreaLocation_ff9d] + ld e, a + call GetPlayAreaCardAttachedEnergies + ld a, [wAttachedEnergies + COLORLESS] + or a + jr nz, .no_carry + +; card has no colorless energy, so look for double colorless +; in hand and if found, return carry and its card index. + ld a, DOUBLE_COLORLESS_ENERGY + call LookForCardIDInHand + jr c, .no_carry + ldh [hTemp_ffa0], a + pop hl + pop de + pop bc + scf + ret + +.get_id: + ldh a, [hTempPlayAreaLocation_ff9d] + add DUELVARS_ARENA_CARD + call GetTurnDuelistVariable + call GetCardIDFromDeckIndex + ld a, e + ret diff --git a/src/engine/duel/ai/hand_pokemon.asm b/src/engine/duel/ai/hand_pokemon.asm new file mode 100644 index 0000000..27a4176 --- /dev/null +++ b/src/engine/duel/ai/hand_pokemon.asm @@ -0,0 +1,627 @@ +; determine whether AI plays +; basic cards from hand +AIDecidePlayPokemonCard: ; 15eae (5:5eae) + call CreateHandCardList + call SortTempHandByIDList + ld hl, wDuelTempList + ld de, wHandTempList + call CopyHandCardList + ld hl, wHandTempList + +.next_hand_card + ld a, [hli] + cp $ff + jp z, AIDecideEvolution + + ld [wTempAIPokemonCard], a + push hl + call LoadCardDataToBuffer1_FromDeckIndex + ld a, [wLoadedCard1Type] + cp TYPE_ENERGY + jr nc, .skip + ; skip non-pokemon cards + + ld a, [wLoadedCard1Stage] + or a + jr nz, .skip + ; skip non-basic pokemon + + ld a, 130 + ld [wAIScore], a + call AIDecidePlayLegendaryBirds + +; if Play Area has more than 4 Pokémon, decrease AI score +; else, increase AI score + ld a, DUELVARS_NUMBER_OF_POKEMON_IN_PLAY_AREA + call GetTurnDuelistVariable + cp 4 + jr c, .has_4_or_fewer + ld a, 20 + call SubFromAIScore + jr .check_defending_can_ko +.has_4_or_fewer + ld a, 50 + call AddToAIScore + +; if defending Pokémon can KO active card, increase AI score +.check_defending_can_ko + xor a + ldh [hTempPlayAreaLocation_ff9d], a + call CheckIfDefendingPokemonCanKnockOut + jr nc, .check_energy_cards + ld a, 20 + call AddToAIScore + +; if energy cards are found in hand +; for this card's attacks, raise AI score +.check_energy_cards + ld a, [wTempAIPokemonCard] + call GetAttacksEnergyCostBits + call CheckEnergyFlagsNeededInList + jr nc, .check_evolution_hand + ld a, 20 + call AddToAIScore + +; if evolution card is found in hand +; for this card, raise AI score +.check_evolution_hand + ld a, [wTempAIPokemonCard] + call CheckForEvolutionInList + jr nc, .check_evolution_deck + ld a, 20 + call AddToAIScore + +; if evolution card is found in deck +; for this card, raise AI score +.check_evolution_deck + ld a, [wTempAIPokemonCard] + call CheckForEvolutionInDeck + jr nc, .check_score + ld a, 10 + call AddToAIScore + +; if AI score is >= 180, play card from hand +.check_score + ld a, [wAIScore] + cp 180 + jr c, .skip + ld a, [wTempAIPokemonCard] + ldh [hTemp_ffa0], a + call CheckIfCardCanBePlayed + jr c, .skip + ld a, OPPACTION_PLAY_BASIC_PKMN + bank1call AIMakeDecision + jr c, .done +.skip + pop hl + jp .next_hand_card +.done + pop hl + ret + +; determine whether AI evolves +; Pokémon in the Play Area +AIDecideEvolution: ; 15f4c (5:5f4c) + call CreateHandCardList + ld hl, wDuelTempList + ld de, wHandTempList + call CopyHandCardList + ld hl, wHandTempList + +.next_hand_card + ld a, [hli] + cp $ff + jp z, .done + ld [wTempAIPokemonCard], a + +; check if Prehistoric Power is active +; and if so, skip to next card in hand + push hl + call IsPrehistoricPowerActive + jp c, .done_hand_card + +; load evolution data to buffer1 +; skip if it's not a Pokémon card +; and if it's a basic stage card + ld a, [wTempAIPokemonCard] + call LoadCardDataToBuffer1_FromDeckIndex + ld a, [wLoadedCard1Type] + cp TYPE_ENERGY + jp nc, .done_hand_card + ld a, [wLoadedCard1Stage] + or a + jp z, .done_hand_card + +; start looping Pokémon in Play Area +; to find a card to evolve + ld a, DUELVARS_NUMBER_OF_POKEMON_IN_PLAY_AREA + call GetTurnDuelistVariable + ld c, a + ld b, 0 +.next_bench_pokemon + push bc + ld e, b + ld a, [wTempAIPokemonCard] + ld d, a + call CheckIfCanEvolveInto + pop bc + push bc + jp c, .done_bench_pokemon + +; store this Play Area location in wTempAI +; and initialize the AI score + ld a, b + ld [wTempAI], a + ldh [hTempPlayAreaLocation_ff9d], a + ld a, $80 + ld [wAIScore], a + call AIDecideSpecialEvolutions + +; check if the card can use any attacks +; and if any of those attacks can KO + xor a + ld [wSelectedAttack], a + call CheckIfSelectedAttackIsUnusable + jr nc, .can_attack + ld a, $01 + ld [wSelectedAttack], a + call CheckIfSelectedAttackIsUnusable + jr c, .cant_attack_or_ko +.can_attack + ld a, $01 + ld [wCurCardCanAttack], a + call CheckIfAnyAttackKnocksOutDefendingCard + jr nc, .check_evolution_attacks + call CheckIfSelectedAttackIsUnusable + jr c, .check_evolution_attacks + ld a, $01 + ld [wCurCardCanKO], a + jr .check_evolution_attacks +.cant_attack_or_ko + xor a + ld [wCurCardCanAttack], a + ld [wCurCardCanKO], a + +; check evolution to see if it can use any of its attacks: +; if it can, raise AI score; +; if it can't, decrease AI score and if an energy card that is needed +; can be played from the hand, raise AI score. +.check_evolution_attacks + ldh a, [hTempPlayAreaLocation_ff9d] + add DUELVARS_ARENA_CARD + call GetTurnDuelistVariable + push af + ld a, [wTempAIPokemonCard] + ld [hl], a + xor a + ld [wSelectedAttack], a + call CheckIfSelectedAttackIsUnusable + jr nc, .evolution_can_attack + ld a, $01 + ld [wSelectedAttack], a + call CheckIfSelectedAttackIsUnusable + jr c, .evolution_cant_attack +.evolution_can_attack + ld a, 5 + call AddToAIScore + jr .check_evolution_ko +.evolution_cant_attack + ld a, [wCurCardCanAttack] + or a + jr z, .check_evolution_ko + ld a, 2 + call SubFromAIScore + ld a, [wAlreadyPlayedEnergy] + or a + jr nz, .check_evolution_ko + call LookForEnergyNeededInHand + jr nc, .check_evolution_ko + ld a, 7 + call AddToAIScore + +; if it's an active card: +; if evolution can't KO but the current card can, lower AI score; +; if evolution can KO as well, raise AI score. +.check_evolution_ko + ld a, [wCurCardCanAttack] + or a + jr z, .check_defending_can_ko_evolution + ld a, [wTempAI] + or a + jr nz, .check_defending_can_ko_evolution + call CheckIfAnyAttackKnocksOutDefendingCard + jr nc, .evolution_cant_ko + call CheckIfSelectedAttackIsUnusable + jr c, .evolution_cant_ko + ld a, 5 + call AddToAIScore + jr .check_defending_can_ko_evolution +.evolution_cant_ko + ld a, [wCurCardCanKO] + or a + jr z, .check_defending_can_ko_evolution + ld a, 20 + call SubFromAIScore + +; if defending Pokémon can KO evolution, lower AI score +.check_defending_can_ko_evolution + ld a, [wTempAI] + or a + jr nz, .check_mr_mime + xor a + ldh [hTempPlayAreaLocation_ff9d], a + call CheckIfDefendingPokemonCanKnockOut + jr nc, .check_mr_mime + ld a, 5 + call SubFromAIScore + +; if evolution can't damage player's Mr Mime, lower AI score +.check_mr_mime + ld a, [wTempAI] + call CheckDamageToMrMime + jr c, .check_defending_can_ko + ld a, 20 + call SubFromAIScore + +; if defending Pokémon can KO current card, raise AI score +.check_defending_can_ko + ld a, [wTempAI] + add DUELVARS_ARENA_CARD + call GetTurnDuelistVariable + pop af + ld [hl], a + ld a, [wTempAI] + or a + jr nz, .check_2nd_stage_hand + xor a + ldh [hTempPlayAreaLocation_ff9d], a + call CheckIfDefendingPokemonCanKnockOut + jr nc, .check_status + ld a, 5 + call AddToAIScore + +; if current card has a status condition, raise AI score +.check_status + ld a, DUELVARS_ARENA_CARD_STATUS + call GetTurnDuelistVariable + or a + jr z, .check_2nd_stage_hand + ld a, 4 + call AddToAIScore + +; if hand has 2nd stage card to evolve evolution card, raise AI score +.check_2nd_stage_hand + ld a, [wTempAIPokemonCard] + call CheckForEvolutionInList + jr nc, .check_2nd_stage_deck + ld a, 2 + call AddToAIScore + jr .check_damage + +; if deck has 2nd stage card to evolve evolution card, raise AI score +.check_2nd_stage_deck + ld a, [wTempAIPokemonCard] + call CheckForEvolutionInDeck + jr nc, .check_damage + ld a, 1 + call AddToAIScore + +; decrease AI score proportional to damage +; AI score -= floor(Damage / 40) +.check_damage + ld a, [wTempAI] + ld e, a + call GetCardDamageAndMaxHP + or a + jr z, .check_mysterious_fossil + srl a + srl a + call CalculateByteTensDigit + call SubFromAIScore + +; if is Mysterious Fossil or +; wLoadedCard1Unknown2 is set to $02, +; raise AI score +.check_mysterious_fossil + ld a, [wTempAI] + add DUELVARS_ARENA_CARD + call GetTurnDuelistVariable + call LoadCardDataToBuffer1_FromDeckIndex + ld a, [wLoadedCard1ID] + cp MYSTERIOUS_FOSSIL + jr z, .mysterious_fossil + ld a, [wLoadedCard1Unknown2] + cp $02 + jr nz, .pikachu_deck + ld a, 2 + call AddToAIScore + jr .pikachu_deck + +.mysterious_fossil + ld a, 5 + call AddToAIScore + +; in Pikachu Deck, decrease AI score for evolving Pikachu +.pikachu_deck + ld a, [wOpponentDeckID] + cp PIKACHU_DECK_ID + jr nz, .check_score + ld a, [wLoadedCard1ID] + cp PIKACHU1 + jr z, .pikachu + cp PIKACHU2 + jr z, .pikachu + cp PIKACHU3 + jr z, .pikachu + cp PIKACHU4 + jr nz, .check_score +.pikachu + ld a, 3 + call SubFromAIScore + +; if AI score >= 133, go through with the evolution +.check_score + ld a, [wAIScore] + cp 133 + jr c, .done_bench_pokemon + ld a, [wTempAI] + ldh [hTempPlayAreaLocation_ffa1], a + ld a, [wTempAIPokemonCard] + ldh [hTemp_ffa0], a + ld a, OPPACTION_EVOLVE_PKMN + bank1call AIMakeDecision + pop bc + jr .done_hand_card + +.done_bench_pokemon + pop bc + inc b + dec c + jp nz, .next_bench_pokemon +.done_hand_card + pop hl + jp .next_hand_card +.done + or a + ret + +; determine AI score for evolving +; Charmeleon, Magikarp, Dragonair and Grimer +; in certain decks +AIDecideSpecialEvolutions: ; 16120 (5:6120) +; check if deck applies + ld a, [wOpponentDeckID] + cp LEGENDARY_DRAGONITE_DECK_ID + jr z, .legendary_dragonite + cp INVINCIBLE_RONALD_DECK_ID + jr z, .invincible_ronald + cp LEGENDARY_RONALD_DECK_ID + jr z, .legendary_ronald + ret + +.legendary_dragonite + ld a, [wLoadedCard2ID] + cp CHARMELEON + jr z, .charmeleon + cp MAGIKARP + jr z, .magikarp + cp DRAGONAIR + jr z, .dragonair + ret + +; check if number of energy cards attached to Charmeleon are at least 3 +; and if adding the energy cards in hand makes at least 6 energy cards +.charmeleon + ldh a, [hTempPlayAreaLocation_ff9d] + ld e, a + call CountNumberOfEnergyCardsAttached + cp 3 + jr c, .not_enough_energy + push af + farcall CountOppEnergyCardsInHand + pop bc + add b + cp 6 + jr c, .not_enough_energy + ld a, 3 + call AddToAIScore + ret +.not_enough_energy + ld a, 10 + call SubFromAIScore + ret + +; check if Magikarp is not the active card +; and has at least 2 energy cards attached +.magikarp + ldh a, [hTempPlayAreaLocation_ff9d] + or a ; active card + ret z + ld e, a + call CountNumberOfEnergyCardsAttached + cp 2 + ret c + ld a, 3 + call AddToAIScore + ret + +.invincible_ronald + ld a, [wLoadedCard2ID] + cp GRIMER + jr z, .grimer + ret + +; check if Grimer is not active card +.grimer + ldh a, [hTempPlayAreaLocation_ff9d] + or a ; active card + ret z + ld a, 10 + call AddToAIScore + ret + +.legendary_ronald + ld a, [wLoadedCard2ID] + cp DRAGONAIR + jr z, .dragonair + ret + +.dragonair + ldh a, [hTempPlayAreaLocation_ff9d] + or a ; active card + jr z, .is_active + +; if Dragonair is benched, check all Pokémon in Play Area +; and sum all the damage in HP of all cards +; if this result is >= 70, check if there's +; a Muk in any duelist's Play Area + ld a, DUELVARS_NUMBER_OF_POKEMON_IN_PLAY_AREA + call GetTurnDuelistVariable + ld b, a + ld c, 0 +.loop + dec b + ld e, b + push bc + call GetCardDamageAndMaxHP + pop bc + add c + ld c, a + ld a, b + or a + jr nz, .loop + ld a, 70 + cp c + jr c, .check_muk +.lower_score + ld a, 10 + call SubFromAIScore + ret + +; if there's no Muk, raise score +.check_muk + ld a, MUK + call CountPokemonIDInBothPlayAreas + jr c, .lower_score + ld a, 10 + call AddToAIScore + ret + +; if Dragonair is active, check its damage in HP +; if this result is >= 50, +; and if at least 3 energy cards attached, +; check if there's a Muk in any duelist's Play Area +.is_active + ld e, 0 + call GetCardDamageAndMaxHP + cp 50 + jr c, .lower_score + ld e, PLAY_AREA_ARENA + call GetPlayAreaCardAttachedEnergies + ld a, [wTotalAttachedEnergies] + cp 3 + jr c, .lower_score + jr .check_muk + +; determine AI score for the legendary cards +; Moltres, Zapdos and Articuno +AIDecidePlayLegendaryBirds: ; 161d5 (5:61d5) +; check if deck applies + ld a, [wOpponentDeckID] + cp LEGENDARY_ZAPDOS_DECK_ID + jr z, .begin + cp LEGENDARY_ARTICUNO_DECK_ID + jr z, .begin + cp LEGENDARY_RONALD_DECK_ID + jr z, .begin + ret + +; check if card applies +.begin + ld a, [wLoadedCard1ID] + cp ARTICUNO2 + jr z, .articuno + cp MOLTRES2 + jr z, .moltres + cp ZAPDOS3 + jr z, .zapdos + ret + +.articuno + ; exit if not enough Pokemon in Play Area + ld a, DUELVARS_NUMBER_OF_POKEMON_IN_PLAY_AREA + call GetTurnDuelistVariable + cp 2 + ret c + + call CheckIfActiveCardCanKnockOut + jr c, .subtract + call CheckIfActivePokemonCanUseAnyNonResidualAttack + jr nc, .subtract + call AIDecideWhetherToRetreat + jr c, .subtract + + ; checks for player's active card status + ld a, DUELVARS_ARENA_CARD_STATUS + call GetNonTurnDuelistVariable + and CNF_SLP_PRZ + or a + jr nz, .subtract + + ; checks for player's Pokemon Power + call SwapTurn + ld a, DUELVARS_ARENA_CARD + call GetTurnDuelistVariable + ld d, a + ld e, $00 + call CopyAttackDataAndDamage_FromDeckIndex + call SwapTurn + ld a, [wLoadedAttackCategory] + cp POKEMON_POWER + jr z, .check_muk_and_snorlax + + ; return if no space on the bench + ld a, DUELVARS_NUMBER_OF_POKEMON_IN_PLAY_AREA + call GetTurnDuelistVariable + cp MAX_BENCH_POKEMON + jr c, .check_muk_and_snorlax + ret + +.check_muk_and_snorlax + ; checks for Muk in both Play Areas + ld a, MUK + call CountPokemonIDInBothPlayAreas + jr c, .subtract + ; checks if player's active card is Snorlax + ld a, DUELVARS_ARENA_CARD + call GetNonTurnDuelistVariable + call SwapTurn + call GetCardIDFromDeckIndex + call SwapTurn + ld a, e + cp SNORLAX + jr z, .subtract + +; add + ld a, 70 + call AddToAIScore + ret +.subtract + ld a, 100 + call SubFromAIScore + ret + +.moltres + ; checks if there's enough cards in deck + ld a, DUELVARS_NUMBER_OF_CARDS_NOT_IN_DECK + call GetTurnDuelistVariable + cp 56 ; max number of cards not in deck to activate + jr nc, .subtract + ret + +.zapdos + ; checks for Muk in both Play Areas + ld a, MUK + call CountPokemonIDInBothPlayAreas + jr c, .subtract + ret diff --git a/src/engine/duel/ai/init.asm b/src/engine/duel/ai/init.asm new file mode 100644 index 0000000..33132cf --- /dev/null +++ b/src/engine/duel/ai/init.asm @@ -0,0 +1,98 @@ +InitAIDuelVars: ; 15636 (5:5636) + ld a, wAIDuelVarsEnd - wAIDuelVars + ld hl, wAIDuelVars + call ClearMemory_Bank5 + ld a, 5 + ld [wAIPokedexCounter], a + ld a, $ff + ld [wAIPeekedPrizes], a + ret + +; initializes some variables and sets value of wAIBarrierFlagCounter. +; if Player uses Barrier 3 times in a row, AI checks if Player's deck +; has only Mewtwo1 Pokemon cards (running a Mewtwo1 mill deck). +InitAITurnVars: ; 15649 (5:5649) +; increase Pokedex counter by 1 + ld a, [wAIPokedexCounter] + inc a + ld [wAIPokedexCounter], a + + xor a + ld [wPreviousAIFlags], a + ld [wAITriedAttack], a + ld [wcddc], a + ld [wAIRetreatedThisTurn], a + +; checks if the Player used an attack last turn +; and if it was the second attack of their card. + ld a, [wPlayerAttackingAttackIndex] + cp $ff + jr z, .check_flag + or a + jr z, .check_flag + ld a, [wPlayerAttackingCardIndex] + cp $ff + jr z, .check_flag + +; if the card is Mewtwo1, it means the Player +; used its second attack, Barrier. + call SwapTurn + call GetCardIDFromDeckIndex + call SwapTurn + ld a, e + cp MEWTWO1 + jr nz, .check_flag + ; Player used Barrier last turn + +; check if flag was already set, if so, +; reset wAIBarrierFlagCounter to $80. + ld a, [wAIBarrierFlagCounter] + bit AI_MEWTWO_MILL_F, a + jr nz, .set_flag + +; if not, increase it by 1 and check if it exceeds 2. + inc a + ld [wAIBarrierFlagCounter], a + cp 3 + jr c, .done + +; this means that the Player used Barrier +; at least 3 turns in a row. +; check if Player is running Mewtwo1-only deck, +; if so, set wAIBarrierFlagCounter flag. + ld a, DUELVARS_ARENA_CARD + call GetNonTurnDuelistVariable + call SwapTurn + call GetCardIDFromDeckIndex + call SwapTurn + ld a, e + cp MEWTWO1 + jr nz, .reset_1 + farcall CheckIfPlayerHasPokemonOtherThanMewtwo1 + jr nc, .set_flag +.reset_1 +; reset wAIBarrierFlagCounter + xor a + ld [wAIBarrierFlagCounter], a + jr .done + +.set_flag + ld a, AI_MEWTWO_MILL + ld [wAIBarrierFlagCounter], a + jr .done + +.check_flag +; increase counter by 1 if flag is set + ld a, [wAIBarrierFlagCounter] + bit AI_MEWTWO_MILL_F, a + jr z, .reset_2 + inc a + ld [wAIBarrierFlagCounter], a + jr .done + +.reset_2 +; reset wAIBarrierFlagCounter + xor a + ld [wAIBarrierFlagCounter], a +.done + ret diff --git a/src/engine/duel/ai/pkmn_powers.asm b/src/engine/duel/ai/pkmn_powers.asm new file mode 100644 index 0000000..8ae629a --- /dev/null +++ b/src/engine/duel/ai/pkmn_powers.asm @@ -0,0 +1,1228 @@ +; handle AI routines for Energy Trans. +; uses AI_ENERGY_TRANS_* constants as input: +; - AI_ENERGY_TRANS_RETREAT: transfers enough Grass Energy cards to +; Arena Pokemon for it to be able to pay the Retreat Cost; +; - AI_ENERGY_TRANS_ATTACK: transfers enough Grass Energy cards to +; Arena Pokemon for it to be able to use its second attack; +; - AI_ENERGY_TRANS_TO_BENCH: transfers all Grass Energy cards from +; Arena Pokemon to Bench in case Arena card will be KO'd. +HandleAIEnergyTrans: ; 2219b (8:619b) + ld [wce06], a + +; choose to randomly return + farcall AIChooseRandomlyNotToDoAction + ret c + + ld a, DUELVARS_NUMBER_OF_POKEMON_IN_PLAY_AREA + call GetTurnDuelistVariable + dec a + ret z ; return if no Bench cards + + ld a, VENUSAUR2 + call CountPokemonIDInPlayArea + ret nc ; return if no Venusaur2 found in own Play Area + + ld a, MUK + call CountPokemonIDInBothPlayAreas + ret c ; return if Muk found in any Play Area + + ld a, [wce06] + cp AI_ENERGY_TRANS_RETREAT + jr z, .check_retreat + + cp AI_ENERGY_TRANS_TO_BENCH + jp z, AIEnergyTransTransferEnergyToBench + + ; AI_ENERGY_TRANS_ATTACK + call .CheckEnoughGrassEnergyCardsForAttack + ret nc + jr .TransferEnergyToArena + +.check_retreat + call .CheckEnoughGrassEnergyCardsForRetreatCost + ret nc + +; use Energy Trans to transfer number of Grass energy cards +; equal to input a from the Bench to the Arena card. +.TransferEnergyToArena + ld [wAINumberOfEnergyTransCards], a + +; look for Venusaur2 in Play Area +; so that its PKMN Power can be used. + ld a, DUELVARS_NUMBER_OF_POKEMON_IN_PLAY_AREA + call GetTurnDuelistVariable + dec a + ld b, a +.loop_play_area + ld a, DUELVARS_ARENA_CARD + add b + call GetTurnDuelistVariable + ldh [hTempCardIndex_ff9f], a + call GetCardIDFromDeckIndex + ld a, e + cp VENUSAUR2 + jr z, .use_pkmn_power + + ld a, b + or a + ret z ; return when finished Play Area loop + + dec b + jr .loop_play_area + +; use Energy Trans Pkmn Power +.use_pkmn_power + ld a, b + ldh [hTemp_ffa0], a + ld a, OPPACTION_USE_PKMN_POWER + bank1call AIMakeDecision + ld a, OPPACTION_EXECUTE_PKMN_POWER_EFFECT + bank1call AIMakeDecision + + xor a ; PLAY_AREA_ARENA + ldh [hAIEnergyTransPlayAreaLocation], a + ld a, [wAINumberOfEnergyTransCards] + ld d, a + +; look for Grass energy cards that +; are currently attached to a Bench card. + ld e, 0 +.loop_deck_locations + ld a, DUELVARS_CARD_LOCATIONS + add e + call GetTurnDuelistVariable + and %00011111 + cp CARD_LOCATION_BENCH_1 + jr c, .next_card + + and %00001111 + ldh [hTempPlayAreaLocation_ffa1], a + + ld a, e + push de + call GetCardIDFromDeckIndex + ld a, e + pop de + cp GRASS_ENERGY + jr nz, .next_card + + ; store the deck index of energy card + ld a, e + ldh [hAIEnergyTransEnergyCard], a + + push de + ld d, 30 +.small_delay_loop + call DoFrame + dec d + jr nz, .small_delay_loop + + ld a, OPPACTION_6B15 + bank1call AIMakeDecision + pop de + dec d + jr z, .done_transfer + +.next_card + inc e + ld a, DECK_SIZE + cp e + jr nz, .loop_deck_locations + +; transfer is done, perform delay +; and return to main scene. +.done_transfer + ld d, 60 +.big_delay_loop + call DoFrame + dec d + jr nz, .big_delay_loop + ld a, OPPACTION_DUEL_MAIN_SCENE + bank1call AIMakeDecision + ret + +; checks if the Arena card needs energy for its second attack, +; and if it does, return carry if transferring Grass energy from Bench +; would be enough to use it. Outputs number of energy cards needed in a. +.CheckEnoughGrassEnergyCardsForAttack ; 22246 (8:6246) + ld a, DUELVARS_ARENA_CARD + call GetTurnDuelistVariable + call GetCardIDFromDeckIndex + ld a, e + cp EXEGGUTOR + jr z, .is_exeggutor + + xor a ; PLAY_AREA_ARENA + ldh [hTempPlayAreaLocation_ff9d], a + ld a, SECOND_ATTACK + ld [wSelectedAttack], a + farcall CheckEnergyNeededForAttack + jr nc, .attack_false ; return if no energy needed + +; check if colorless energy is needed... + ld a, c + or a + jr nz, .count_if_enough + +; ...otherwise check if basic energy card is needed +; and it's grass energy. + ld a, b + or a + jr z, .attack_false + ld a, e + cp GRASS_ENERGY + jr nz, .attack_false + ld c, b + jr .count_if_enough + +.attack_false + or a + ret + +.count_if_enough +; if there's enough Grass energy cards in Bench +; to satisfy the attack energy cost, return carry. + push bc + call .CountGrassEnergyInBench + pop bc + cp c + jr c, .attack_false + ld a, c + scf + ret + +.is_exeggutor +; in case it's Exeggutor in Arena, return carry +; if there are any Grass energy cards in Bench. + call .CountGrassEnergyInBench + or a + jr z, .attack_false + + scf + ret + +; outputs in a the number of Grass energy cards +; currently attached to Bench cards. +.CountGrassEnergyInBench ; 22286 (8:6286) + lb de, 0, 0 +.count_loop + ld a, DUELVARS_CARD_LOCATIONS + add e + call GetTurnDuelistVariable + and %00011111 + cp CARD_LOCATION_BENCH_1 + jr c, .count_next + +; is in bench + ld a, e + push de + call GetCardIDFromDeckIndex + ld a, e + pop de + cp GRASS_ENERGY + jr nz, .count_next + inc d +.count_next + inc e + ld a, DECK_SIZE + cp e + jr nz, .count_loop + ld a, d + ret + +; returns carry if there are enough Grass energy cards in Bench +; to satisfy the retreat cost of the Arena card. +; if so, output the number of energy cards still needed in a. +.CheckEnoughGrassEnergyCardsForRetreatCost ; 222a9 (8:62a9) + xor a ; PLAY_AREA_ARENA + ldh [hTempPlayAreaLocation_ff9d], a + call GetPlayAreaCardRetreatCost + ld b, a + ld e, PLAY_AREA_ARENA + farcall CountNumberOfEnergyCardsAttached + cp b + jr nc, .retreat_false ; return if enough to retreat + +; see if there's enough Grass energy cards +; in the Bench to satisfy retreat cost + ld c, a + ld a, b + sub c + ld c, a + push bc + call .CountGrassEnergyInBench + pop bc + cp c + jr c, .retreat_false ; return if less cards than needed + +; output number of cards needed to retreat + ld a, c + scf + ret +.retreat_false + or a + ret + +; AI logic to determine whether to use Energy Trans Pkmn Power +; to transfer energy cards attached from the Arena Pokemon to +; some card in the Bench. +AIEnergyTransTransferEnergyToBench: ; 222ca (8:62ca) + xor a ; PLAY_AREA_ARENA + ldh [hTempPlayAreaLocation_ff9d], a + farcall CheckIfDefendingPokemonCanKnockOut + ret nc ; return if Defending can't KO + +; processes attacks and see if any attack would be used by AI. +; if so, return. + farcall AIProcessButDontUseAttack + ret c + +; return if Arena card has no Grass energy cards attached. + ld e, PLAY_AREA_ARENA + call GetPlayAreaCardAttachedEnergies + ld a, [wAttachedEnergies + GRASS] + or a + ret z + +; if no energy card attachment is needed, return. + farcall AIProcessButDontPlayEnergy_SkipEvolutionAndArena + ret nc + +; AI decided that an energy card is needed +; so look for Venusaur2 in Play Area +; so that its PKMN Power can be used. + ld a, DUELVARS_NUMBER_OF_POKEMON_IN_PLAY_AREA + call GetTurnDuelistVariable + dec a + ld b, a +.loop_play_area + ld a, DUELVARS_ARENA_CARD + add b + call GetTurnDuelistVariable + ldh [hTempCardIndex_ff9f], a + ld [wAIVenusaur2DeckIndex], a + call GetCardIDFromDeckIndex + ld a, e + cp VENUSAUR2 + jr z, .use_pkmn_power + + ld a, b + or a + ret z ; return when Play Area loop is ended + + dec b + jr .loop_play_area + +; use Energy Trans Pkmn Power +.use_pkmn_power + ld a, b + ldh [hTemp_ffa0], a + ld [wAIVenusaur2PlayAreaLocation], a + ld a, OPPACTION_USE_PKMN_POWER + bank1call AIMakeDecision + ld a, OPPACTION_EXECUTE_PKMN_POWER_EFFECT + bank1call AIMakeDecision + +; loop for each energy cards that are going to be transferred. +.loop_energy + xor a + ldh [hTempPlayAreaLocation_ffa1], a + ld a, [wAIVenusaur2PlayAreaLocation] + ldh [hTemp_ffa0], a + + ; returns when Arena card has no Grass energy cards attached. + ld e, PLAY_AREA_ARENA + call GetPlayAreaCardAttachedEnergies + ld a, [wAttachedEnergies + GRASS] + or a + jr z, .done_transfer + +; look for Grass energy cards that +; are currently attached to Arena card. + ld e, 0 +.loop_deck_locations + ld a, DUELVARS_CARD_LOCATIONS + add e + call GetTurnDuelistVariable + cp CARD_LOCATION_ARENA + jr nz, .next_card + + ld a, e + push de + call GetCardIDFromDeckIndex + ld a, e + pop de + cp GRASS_ENERGY + jr nz, .next_card + + ; store the deck index of energy card + ld a, e + ldh [hAIEnergyTransEnergyCard], a + jr .transfer + +.next_card + inc e + ld a, DECK_SIZE + cp e + jr nz, .loop_deck_locations + jr .done_transfer + +.transfer +; get the Bench card location to transfer Grass energy card to. + farcall AIProcessButDontPlayEnergy_SkipEvolutionAndArena + jr nc, .done_transfer + ldh a, [hTempPlayAreaLocation_ff9d] + ldh [hAIEnergyTransPlayAreaLocation], a + + ld d, 30 +.small_delay_loop + call DoFrame + dec d + jr nz, .small_delay_loop + + ld a, [wAIVenusaur2DeckIndex] + ldh [hTempCardIndex_ff9f], a + ld d, a + ld e, FIRST_ATTACK_OR_PKMN_POWER + call CopyAttackDataAndDamage_FromDeckIndex + ld a, OPPACTION_6B15 + bank1call AIMakeDecision + jr .loop_energy + +; transfer is done, perform delay +; and return to main scene. +.done_transfer + ld d, 60 +.big_delay_loop + call DoFrame + dec d + jr nz, .big_delay_loop + ld a, OPPACTION_DUEL_MAIN_SCENE + bank1call AIMakeDecision + ret + +; handles AI logic for using some Pkmn Powers. +; Pkmn Powers handled here are: +; - Heal; +; - Shift; +; - Peek; +; - Strange Behavior; +; - Curse. +; returns carry if turn ended. +HandleAIPkmnPowers: ; 2237f (8:637f) + ld a, MUK + call CountPokemonIDInBothPlayAreas + ccf + ret nc ; return no carry if Muk is in play + + farcall AIChooseRandomlyNotToDoAction + ccf + ret nc ; return no carry if AI randomly decides to + + ld a, DUELVARS_NUMBER_OF_POKEMON_IN_PLAY_AREA + call GetTurnDuelistVariable + ld b, a + ld c, PLAY_AREA_ARENA + ld a, DUELVARS_ARENA_CARD_STATUS + call GetTurnDuelistVariable + and CNF_SLP_PRZ + jr nz, .next_2 + +.loop_play_area + ld a, DUELVARS_ARENA_CARD + add c + call GetTurnDuelistVariable + ld [wce08], a + + push af + push bc + ld d, a + ld a, c + ldh [hTempPlayAreaLocation_ff9d], a + ld e, FIRST_ATTACK_OR_PKMN_POWER + call CopyAttackDataAndDamage_FromDeckIndex + ld a, [wLoadedAttackCategory] + cp POKEMON_POWER + jr z, .execute_effect + pop bc + jr .next_3 + +.execute_effect + ld a, EFFECTCMDTYPE_INITIAL_EFFECT_2 + bank1call TryExecuteEffectCommandFunction + pop bc + jr c, .next_3 + +; TryExecuteEffectCommandFunction was successful, +; so check what Pkmn Power this is through card's ID. + pop af + call GetCardIDFromDeckIndex + ld a, e + push bc + +; check heal + cp VILEPLUME + jr nz, .check_shift + call HandleAIHeal + jr .next_1 +.check_shift + cp VENOMOTH + jr nz, .check_peek + call HandleAIShift + jr .next_1 +.check_peek + cp MANKEY + jr nz, .check_strange_behavior + call HandleAIPeek + jr .next_1 +.check_strange_behavior + cp SLOWBRO + jr nz, .check_curse + call HandleAIStrangeBehavior + jr .next_1 +.check_curse + cp GENGAR + jr nz, .next_1 + call z, HandleAICurse + jr c, .done + +.next_1 + pop bc +.next_2 + inc c + ld a, c + cp b + jr nz, .loop_play_area + ret + +.next_3 + pop af + jr .next_2 + +.done + pop bc + ret + +; checks whether AI uses Heal on Pokemon in Play Area. +; input: +; c = Play Area location (PLAY_AREA_*) of Vileplume. +HandleAIHeal: ; 22402 (8:6402) + ld a, c + ldh [hTemp_ffa0], a + call .CheckHealTarget + ret nc ; return if no target to heal + push af + ld a, [wce08] + ldh [hTempCardIndex_ff9f], a + ld a, OPPACTION_USE_PKMN_POWER + bank1call AIMakeDecision + pop af + ldh [hPlayAreaEffectTarget], a + ld a, OPPACTION_EXECUTE_PKMN_POWER_EFFECT + bank1call AIMakeDecision + ld a, OPPACTION_DUEL_MAIN_SCENE + bank1call AIMakeDecision + ret + +; finds a target suitable for AI to use Heal on. +; only heals Arena card if the Defending Pokemon +; cannot KO it after Heal is used. +; returns carry if target was found and outputs +; in a the Play Area location of that card. +.CheckHealTarget ; 22422 (8:6422) +; check if Arena card has any damage counters, +; if not, check Bench instead. + ld e, PLAY_AREA_ARENA + call GetCardDamageAndMaxHP + or a + jr z, .check_bench + + xor a ; PLAY_AREA_ARENA + ldh [hTempPlayAreaLocation_ff9d], a + farcall CheckIfDefendingPokemonCanKnockOut + jr nc, .set_carry ; return carry if can't KO + ld d, a + ld a, DUELVARS_ARENA_CARD_HP + call GetTurnDuelistVariable + ld h, a + ld e, PLAY_AREA_ARENA + call GetCardDamageAndMaxHP + ; this seems useless since it was already + ; checked that Arena card has damage, + ; so card damage is at least 10. + cp 10 + 1 + jr c, .check_remaining + ld a, 10 + ; a = min(10, CardDamage) + +; checks if Defending Pokemon can still KO +; if Heal is used on this card. +; if Heal prevents KO, return carry. +.check_remaining + ld l, a + ld a, h ; load remaining HP + add l ; add 1 counter to account for heal + sub d ; subtract damage of strongest opponent attack + jr c, .check_bench + jr z, .check_bench + +.set_carry + xor a ; PLAY_AREA_ARENA + scf + ret + +; check Bench for Pokemon with damage counters +; and find the one with the most damage. +.check_bench + ld a, DUELVARS_NUMBER_OF_POKEMON_IN_PLAY_AREA + call GetTurnDuelistVariable + ld d, a + lb bc, 0, 0 + ld e, PLAY_AREA_BENCH_1 +.loop_bench + ld a, e + cp d + jr z, .done + push bc + call GetCardDamageAndMaxHP + pop bc + cp b + jr c, .next_bench + jr z, .next_bench + ld b, a ; store this damage + ld c, e ; store this Play Area location +.next_bench + inc e + jr .loop_bench + +; check if a Pokemon with damage counters was found +; in the Bench and, if so, return carry. +.done + ld a, c + or a + jr z, .not_found +; found + scf + ret +.not_found + or a + ret + +; checks whether AI uses Shift. +; input: +; c = Play Area location (PLAY_AREA_*) of Venomoth +HandleAIShift: ; 22476 (8:6476) + ld a, c + or a + ret nz ; return if Venomoth is not Arena card + + ldh [hTemp_ffa0], a + call GetArenaCardColor + call TranslateColorToWR + ld b, a + call SwapTurn + call GetArenaCardWeakness + ld [wAIDefendingPokemonWeakness], a + call SwapTurn + or a + ret z ; return if Defending Pokemon has no weakness + and b + ret nz ; return if Venomoth is already Defending card's weakness type + +; check whether there's a card in play with +; the same color as the Player's card weakness + call .CheckWhetherTurnDuelistHasColor + jr c, .found + call SwapTurn + call .CheckWhetherTurnDuelistHasColor + call SwapTurn + ret nc ; return if no color found + +.found + ld a, [wce08] + ldh [hTempCardIndex_ff9f], a + ld a, OPPACTION_USE_PKMN_POWER + bank1call AIMakeDecision + +; converts WR_* to appropriate color + ld a, [wAIDefendingPokemonWeakness] + ld b, 0 +.loop_color + bit 7, a + jr nz, .done + inc b + rlca + jr .loop_color + +; use Pkmn Power effect +.done + ld a, b + ldh [hAIPkmnPowerEffectParam], a + ld a, OPPACTION_EXECUTE_PKMN_POWER_EFFECT + bank1call AIMakeDecision + ld a, OPPACTION_DUEL_MAIN_SCENE + bank1call AIMakeDecision + ret + +; returns carry if turn Duelist has a Pokemon +; with same color as wAIDefendingPokemonWeakness. +.CheckWhetherTurnDuelistHasColor ; 224c6 (8:64c6) + ld a, [wAIDefendingPokemonWeakness] + ld b, a + ld a, DUELVARS_ARENA_CARD + call GetTurnDuelistVariable +.loop_play_area + ld a, [hli] + cp $ff + jr z, .false + push bc + call GetCardIDFromDeckIndex + call GetCardType + ; in case this is a Mysterious Fossil or Clefairy Doll card, + ; AI might read the type of the card incorrectly here. + ; uncomment the following lines to account for this + ; cp TYPE_TRAINER + ; jr nz, .not_trainer + ; pop bc + ; jr .loop_play_area +; .not_trainer + call TranslateColorToWR + pop bc + and b + jr z, .loop_play_area +; true + scf + ret +.false + or a + ret + +; checks whether AI uses Peek. +; input: +; c = Play Area location (PLAY_AREA_*) of Mankey. +HandleAIPeek: ; 224e6 (8:64e6) + ld a, c + ldh [hTemp_ffa0], a + ld a, 50 + call Random + cp 3 + ret nc ; return 47 out of 50 times + +; choose what to use Peek on at random + ld a, 3 + call Random + or a + jr z, .check_ai_prizes + cp 2 + jr c, .check_player_hand + +; check Player's Deck + ld a, DUELVARS_NUMBER_OF_CARDS_NOT_IN_DECK + call GetNonTurnDuelistVariable + cp DECK_SIZE - 1 + ret nc ; return if Player has one or no cards in Deck + ld a, AI_PEEK_TARGET_DECK + jr .use_peek + +.check_ai_prizes + ld a, DUELVARS_PRIZES + call GetTurnDuelistVariable + ld hl, wAIPeekedPrizes + and [hl] + ld [hl], a + or a + ret z ; return if no prizes + + ld c, a + ld b, $1 + ld d, 0 +.loop_prizes + ld a, c + and b + jr nz, .found_prize + sla b + inc d + jr .loop_prizes +.found_prize +; remove this prize's flag from the prize list +; and use Peek on first one in list (lowest bit set) + ld a, c + sub b + ld [hl], a + ld a, AI_PEEK_TARGET_PRIZE + add d + jr .use_peek + +.check_player_hand + call SwapTurn + call CreateHandCardList + call SwapTurn + or a + ret z ; return if no cards in Hand +; shuffle list and pick the first entry to Peek + ld hl, wDuelTempList + call CountCardsInDuelTempList + call ShuffleCards + ld a, [wDuelTempList] + or AI_PEEK_TARGET_HAND + +.use_peek + push af + ld a, [wce08] + ldh [hTempCardIndex_ff9f], a + ld a, OPPACTION_USE_PKMN_POWER + bank1call AIMakeDecision + pop af + ldh [hAIPkmnPowerEffectParam], a + ld a, OPPACTION_EXECUTE_PKMN_POWER_EFFECT + bank1call AIMakeDecision + ld a, OPPACTION_DUEL_MAIN_SCENE + bank1call AIMakeDecision + ret + +; checks whether AI uses Strange Behavior. +; input: +; c = Play Area location (PLAY_AREA_*) of Slowbro. +HandleAIStrangeBehavior: ; 2255d (8:655d) + ld a, c + or a + ret z ; return if Slowbro is Arena card + + ldh [hTemp_ffa0], a + ld e, PLAY_AREA_ARENA + call GetCardDamageAndMaxHP + or a + ret z ; return if Arena card has no damage counters + + ld [wce06], a + ldh a, [hTemp_ffa0] + add DUELVARS_ARENA_CARD_HP + call GetTurnDuelistVariable + sub 10 + ret z ; return if Slowbro has only 10 HP remaining + +; if Slowbro can't receive all damage counters, +; only transfer remaining HP - 10 damage + ld hl, wce06 + cp [hl] + jr c, .use_strange_behavior + ld a, [hl] ; can receive all damage counters + +.use_strange_behavior + push af + ld a, [wce08] + ldh [hTempCardIndex_ff9f], a + ld a, OPPACTION_USE_PKMN_POWER + bank1call AIMakeDecision + xor a + ldh [hAIPkmnPowerEffectParam], a + ld a, OPPACTION_EXECUTE_PKMN_POWER_EFFECT + bank1call AIMakeDecision + pop af + +; loop counters chosen to transfer and use Pkmn Power + call ConvertHPToCounters + ld e, a +.loop_counters + ld d, 30 +.small_delay_loop + call DoFrame + dec d + jr nz, .small_delay_loop + push de + ld a, OPPACTION_6B15 + bank1call AIMakeDecision + pop de + dec e + jr nz, .loop_counters + +; return to main scene + ld d, 60 +.big_delay_loop + call DoFrame + dec d + jr nz, .big_delay_loop + ld a, OPPACTION_DUEL_MAIN_SCENE + bank1call AIMakeDecision + ret + +; checks whether AI uses Curse. +; input: +; c = Play Area location (PLAY_AREA_*) of Gengar. +HandleAICurse: ; 225b5 (8:65b5) + ld a, c + ldh [hTemp_ffa0], a + +; loop Player's Play Area and checks their damage. +; finds the card with lowest remaining HP and +; stores its HP and its Play Area location + ld a, DUELVARS_NUMBER_OF_POKEMON_IN_PLAY_AREA + call GetNonTurnDuelistVariable + ld d, a + ld e, PLAY_AREA_ARENA + lb bc, 0, $ff + ld h, PLAY_AREA_ARENA + call SwapTurn +.loop_play_area_1 + push bc + call GetCardDamageAndMaxHP + pop bc + or a + jr z, .next_1 + + inc b + ld a, e + add DUELVARS_ARENA_CARD_HP + push hl + call GetTurnDuelistVariable + pop hl + cp c + jr nc, .next_1 + ; lower HP than one stored + ld c, a ; store this HP + ld h, e ; store this Play Area location + +.next_1 + inc e + ld a, e + cp d + jr nz, .loop_play_area_1 ; reached end of Play Area + + ld a, 1 + cp b + jr nc, .failed ; return if less than 2 cards with damage + +; card in Play Area with lowest HP remaining was found. +; look for another card to take damage counter from. + ld a, h + ldh [hTempRetreatCostCards], a + ld b, a + ld a, 10 + cp c + jr z, .hp_10_remaining + ; if has more than 10 HP remaining, + ; skip Arena card in choosing which + ; card to take damage counter from. + ld e, PLAY_AREA_BENCH_1 + jr .second_card + +.hp_10_remaining + ; if Curse can KO, then include + ; Player's Arena card to take + ; damage counter from. + ld e, PLAY_AREA_ARENA + +.second_card + ld a, DUELVARS_NUMBER_OF_POKEMON_IN_PLAY_AREA + call GetTurnDuelistVariable + ld d, a +.loop_play_area_2 + ld a, e + cp b + jr z, .next_2 ; skip same Pokemon card + push bc + call GetCardDamageAndMaxHP + pop bc + jr nz, .use_curse ; has damage counters, choose this card +.next_2 + inc e + ld a, e + cp d + jr nz, .loop_play_area_2 + +.failed + call SwapTurn + or a + ret + +.use_curse + ld a, e + ldh [hAIPkmnPowerEffectParam], a + call SwapTurn + ld a, [wce08] + ldh [hTempCardIndex_ff9f], a + ld a, OPPACTION_USE_PKMN_POWER + bank1call AIMakeDecision + ld a, OPPACTION_EXECUTE_PKMN_POWER_EFFECT + bank1call AIMakeDecision + ld a, OPPACTION_DUEL_MAIN_SCENE + bank1call AIMakeDecision + ret + +; handles AI logic for Cowardice +HandleAICowardice: ; 2262d (8:662d) + ld a, MUK + call CountPokemonIDInBothPlayAreas + ret c ; return if there's Muk in play + + farcall AIChooseRandomlyNotToDoAction + ret c ; randomly return + + ld a, DUELVARS_NUMBER_OF_POKEMON_IN_PLAY_AREA + call GetTurnDuelistVariable + cp 1 + ret z ; return if only one Pokemon in Play Area + + ld b, a + ld c, PLAY_AREA_ARENA + ld a, DUELVARS_ARENA_CARD_STATUS + call GetTurnDuelistVariable + and CNF_SLP_PRZ + jr nz, .next +.loop + ld a, DUELVARS_ARENA_CARD + add c + call GetTurnDuelistVariable + ld [wce08], a + call GetCardIDFromDeckIndex + ld a, e + push bc + cp TENTACOOL + call z, .CheckWhetherToUseCowardice + pop bc + jr nc, .next + + dec b ; subtract 1 from number of Pokemon in Play Area + ld a, 1 + cp b + ret z ; return if no longer has Bench Pokemon + ld c, PLAY_AREA_ARENA ; reset back to Arena + jr .loop + +.next + inc c + ld a, c + cp b + jr nz, .loop + ret + +; checks whether AI uses Cowardice. +; return carry if Pkmn Power was used. +; input: +; c = Play Area location (PLAY_AREA_*) of Tentacool. +.CheckWhetherToUseCowardice ; 22671 (8:6671) + ld a, c + ldh [hTemp_ffa0], a + ld e, a + call GetCardDamageAndMaxHP +.asm_22678 + or a + ret z ; return if has no damage counters + + ldh a, [hTemp_ffa0] + or a + jr nz, .is_benched + + ; this part is buggy if AIDecideBenchPokemonToSwitchTo returns carry + ; but since this was already checked beforehand, this never happens. + ; so jr c, .asm_22678 can be safely removed. + farcall AIDecideBenchPokemonToSwitchTo + jr c, .asm_22678 ; bug, this jumps in the middle of damage checking + jr .use_cowardice +.is_benched + ld a, $ff +.use_cowardice + push af + ld a, [wce08] + ldh [hTempCardIndex_ff9f], a + ld a, OPPACTION_USE_PKMN_POWER + bank1call AIMakeDecision + pop af + ldh [hAIPkmnPowerEffectParam], a + ld a, OPPACTION_EXECUTE_PKMN_POWER_EFFECT + bank1call AIMakeDecision + ld a, OPPACTION_DUEL_MAIN_SCENE + bank1call AIMakeDecision + scf + ret + +; AI logic for Damage Swap to transfer damage from Arena card +; to a card in Bench with more than 10 HP remaining +; and with no energy cards attached. +HandleAIDamageSwap: ; 226a3 (8:66a3) + ld a, DUELVARS_NUMBER_OF_POKEMON_IN_PLAY_AREA + call GetTurnDuelistVariable + dec a + ret z ; return if no Bench Pokemon + + farcall AIChooseRandomlyNotToDoAction + ret c + + ld a, ALAKAZAM + call CountPokemonIDInPlayArea + ret nc ; return if no Alakazam + ld a, MUK + call CountPokemonIDInBothPlayAreas + ret c ; return if there's Muk in play + +; only take damage off certain cards in Arena + ld a, DUELVARS_ARENA_CARD + call GetTurnDuelistVariable + call GetCardIDFromDeckIndex + ld a, e + cp ALAKAZAM + jr z, .ok + cp KADABRA + jr z, .ok + cp ABRA + jr z, .ok + cp MR_MIME + ret nz + +.ok + ld e, PLAY_AREA_ARENA + call GetCardDamageAndMaxHP + or a + ret z ; return if no damage + + call ConvertHPToCounters + ld [wce06], a + ld a, ALAKAZAM + ld b, PLAY_AREA_BENCH_1 + farcall LookForCardIDInPlayArea_Bank5 + jr c, .is_in_bench + +; Alakazam is Arena card + xor a +.is_in_bench + ld [wce08], a + call .CheckForDamageSwapTargetInBench + ret c ; return if not found + +; use Damage Swap + ld a, [wce08] + add DUELVARS_ARENA_CARD + call GetTurnDuelistVariable + ldh [hTempCardIndex_ff9f], a + ld a, [wce08] + ldh [hTemp_ffa0], a + ld a, OPPACTION_USE_PKMN_POWER + bank1call AIMakeDecision + ld a, OPPACTION_EXECUTE_PKMN_POWER_EFFECT + bank1call AIMakeDecision + + ld a, [wce06] + ld e, a +.loop_damage + ld d, 30 +.small_delay_loop + call DoFrame + dec d + jr nz, .small_delay_loop + + push de + call .CheckForDamageSwapTargetInBench + jr c, .no_more_target + + ldh [hTempRetreatCostCards], a + xor a ; PLAY_AREA_ARENA + ldh [hAIPkmnPowerEffectParam], a + ld a, OPPACTION_6B15 + bank1call AIMakeDecision + pop de + dec e + jr nz, .loop_damage + +.done +; return to main scene + ld d, 60 +.big_delay_loop + call DoFrame + dec d + jr nz, .big_delay_loop + ld a, OPPACTION_DUEL_MAIN_SCENE + bank1call AIMakeDecision + ret + +.no_more_target + pop de + jr .done + +; looks for a target in the bench to receive damage counters. +; returns carry if one is found, and outputs remaining HP in a. +.CheckForDamageSwapTargetInBench ; 2273c (8:673c) + ld a, DUELVARS_NUMBER_OF_POKEMON_IN_PLAY_AREA + call GetTurnDuelistVariable + ld b, a + ld c, PLAY_AREA_BENCH_1 + lb de, $ff, $ff + +; look for candidates in bench to get the damage counters +; only target specific card IDs. +.loop_bench + ld a, c + add DUELVARS_ARENA_CARD + call GetTurnDuelistVariable + push de + call GetCardIDFromDeckIndex + ld a, e + pop de + cp CHANSEY + jr z, .found_candidate + cp KANGASKHAN + jr z, .found_candidate + cp SNORLAX + jr z, .found_candidate + cp MR_MIME + jr z, .found_candidate + +.next_play_area + inc c + ld a, c + cp b + jr nz, .loop_bench + +; done + ld a, e + cp $ff + jr nz, .no_carry + ld a, d + cp $ff + jr z, .set_carry +.no_carry + or a + ret + +.found_candidate +; found a potential candidate to receive damage counters + ld a, DUELVARS_ARENA_CARD_HP + add c + call GetTurnDuelistVariable + cp 20 + jr c, .next_play_area ; ignore cards with only 10 HP left + + ld d, c ; store damage + push de + push bc + ld e, c + farcall CountNumberOfEnergyCardsAttached + pop bc + pop de + or a + jr nz, .next_play_area ; ignore cards with attached energy + ld e, c ; store deck index + jr .next_play_area + +.set_carry + scf + ret + +; handles AI logic for attaching energy cards +; in Go Go Rain Dance deck. +HandleAIGoGoRainDanceEnergy: ; 22790 (8:6790) + ld a, [wOpponentDeckID] + cp GO_GO_RAIN_DANCE_DECK_ID + ret nz ; return if not Go Go Rain Dance deck + + ld a, BLASTOISE + call CountPokemonIDInPlayArea + ret nc ; return if no Blastoise + ld a, MUK + call CountPokemonIDInBothPlayAreas + ret c ; return if there's Muk in play + +; play all the energy cards that is needed. +.loop + farcall AIProcessAndTryToPlayEnergy + jr c, .loop + ret diff --git a/src/engine/duel/ai/retreat.asm b/src/engine/duel/ai/retreat.asm new file mode 100644 index 0000000..768a48b --- /dev/null +++ b/src/engine/duel/ai/retreat.asm @@ -0,0 +1,1009 @@ +; determine AI score for retreating +; return carry if AI decides to retreat +AIDecideWhetherToRetreat: ; 158b2 (5:58b2) + ld a, [wGotHeadsFromConfusionCheckDuringRetreat] + or a + jp nz, .no_carry + xor a + ld [wAIPlayEnergyCardForRetreat], a + call LoadDefendingPokemonColorWRAndPrizeCards + ld a, $80 ; initial retreat score + ld [wAIScore], a + ld a, [wcdb4] + or a + jr z, .check_status + srl a + srl a + sla a + call AddToAIScore + +.check_status + ld a, DUELVARS_ARENA_CARD_STATUS + call GetTurnDuelistVariable + or a + jr z, .check_ko_1 ; no status + and DOUBLE_POISONED + jr z, .check_cnf ; no poison + ld a, 2 + call AddToAIScore +.check_cnf + ld a, [hl] + and CNF_SLP_PRZ + cp CONFUSED + jr nz, .check_ko_1 + ld a, 1 + call AddToAIScore + +.check_ko_1 + xor a + ldh [hTempPlayAreaLocation_ff9d], a + call CheckIfAnyAttackKnocksOutDefendingCard + jr nc, .active_cant_ko_1 + call CheckIfSelectedAttackIsUnusable + jp nc, .active_cant_use_atk + call LookForEnergyNeededForAttackInHand + jr nc, .active_cant_ko_1 + +.active_cant_use_atk + ld a, 5 + call SubFromAIScore + ld a, [wAIOpponentPrizeCount] + cp 2 + jr nc, .active_cant_ko_1 + ld a, 35 + call SubFromAIScore + +.active_cant_ko_1 + call CheckIfDefendingPokemonCanKnockOut + jr nc, .defending_cant_ko + ld a, 2 + call AddToAIScore + + call CheckIfNotABossDeckID + jr c, .check_resistance_1 + ld a, [wAIPlayerPrizeCount] + cp 2 + jr nc, .check_prize_count + ld a, $01 + ld [wAIPlayEnergyCardForRetreat], a + +.defending_cant_ko + call CheckIfNotABossDeckID + jr c, .check_resistance_1 + ld a, [wAIPlayerPrizeCount] + cp 2 + jr nc, .check_prize_count + ld a, 2 + call AddToAIScore + +.check_prize_count + ld a, [wAIOpponentPrizeCount] + cp 2 + jr nc, .check_resistance_1 + ld a, 2 + call SubFromAIScore + +.check_resistance_1 + call GetArenaCardColor + call TranslateColorToWR + ld b, a + ld a, [wAIPlayerResistance] + and b + jr z, .check_weakness_1 + ld a, 1 + call AddToAIScore + +; check bench for Pokémon that +; the defending card is not resistant to +; if one is found, skip SubFromAIScore + ld a, [wAIPlayerResistance] + ld b, a + ld a, DUELVARS_BENCH + call GetTurnDuelistVariable +.loop_resistance_1 + ld a, [hli] + cp $ff + jr z, .exit_loop_resistance_1 + call LoadCardDataToBuffer1_FromDeckIndex + ld a, [wLoadedCard1Type] + call TranslateColorToWR + and b + jr nz, .loop_resistance_1 + jr .check_weakness_1 +.exit_loop_resistance_1 + ld a, 2 + call SubFromAIScore + +.check_weakness_1 + ld a, [wAIPlayerColor] + ld b, a + call GetArenaCardWeakness + and b + jr z, .check_resistance_2 + ld a, 2 + call AddToAIScore + +; check bench for Pokémon that +; is not weak to defending Pokémon +; if one is found, skip SubFromAIScore + ld a, [wAIPlayerColor] + ld b, a + ld a, DUELVARS_BENCH + call GetTurnDuelistVariable +.loop_weakness_1 + ld a, [hli] + cp $ff + jr z, .exit_loop_weakness_1 + call LoadCardDataToBuffer1_FromDeckIndex + ld a, [wLoadedCard1Weakness] + and b + jr nz, .loop_weakness_1 + jr .check_resistance_2 +.exit_loop_weakness_1 + ld a, 3 + call SubFromAIScore + +.check_resistance_2 + ld a, [wAIPlayerColor] + ld b, a + call GetArenaCardResistance + and b + jr z, .check_weakness_2 + ld a, 3 + call SubFromAIScore + +; check bench for Pokémon that +; is the defending Pokémon's weakness +; if none is found, skip AddToAIScore +.check_weakness_2 + ld a, [wAIPlayerWeakness] + ld b, a + ld a, DUELVARS_BENCH + call GetTurnDuelistVariable + ld e, $00 +.loop_weakness_2 + inc e + ld a, [hli] + cp $ff + jr z, .check_resistance_3 + push de + call LoadCardDataToBuffer1_FromDeckIndex + ld a, [wLoadedCard1Type] + call TranslateColorToWR + pop de + and b + jr z, .loop_weakness_2 + ld a, 2 + call AddToAIScore + + push de + ld a, DUELVARS_ARENA_CARD + call GetTurnDuelistVariable + call GetCardIDFromDeckIndex + ld a, e + pop de + cp PORYGON + jr nz, .check_weakness_3 + +; handle Porygon + ld a, e + call CheckIfCanDamageDefendingPokemon + jr nc, .check_weakness_3 + ld a, 10 + call AddToAIScore + jr .check_resistance_3 + +.check_weakness_3 + call GetArenaCardColor + call TranslateColorToWR + ld b, a + ld a, [wAIPlayerWeakness] + and b + jr z, .check_resistance_3 + ld a, 3 + call SubFromAIScore + +; check bench for Pokémon that +; is resistant to defending Pokémon +; if none is found, skip AddToAIScore +.check_resistance_3 + ld a, [wAIPlayerColor] + ld b, a + ld a, DUELVARS_BENCH + call GetTurnDuelistVariable +.loop_resistance_2 + ld a, [hli] + cp $ff + jr z, .check_ko_2 + call LoadCardDataToBuffer1_FromDeckIndex + ld a, [wLoadedCard1Resistance] + and b + jr z, .loop_resistance_2 + ld a, 1 + call AddToAIScore + +; check bench for Pokémon that +; can KO defending Pokémon +; if none is found, skip AddToAIScore +.check_ko_2 + ld a, DUELVARS_BENCH + call GetTurnDuelistVariable + ld c, 0 +.loop_ko_1 + inc c + ld a, [hli] + cp $ff + jr z, .check_defending_id + ld a, c + ldh [hTempPlayAreaLocation_ff9d], a + push hl + push bc + call CheckIfAnyAttackKnocksOutDefendingCard + jr nc, .no_ko + call CheckIfSelectedAttackIsUnusable + jr nc, .success + call LookForEnergyNeededForAttackInHand + jr c, .success +.no_ko + pop bc + pop hl + jr .loop_ko_1 +.success + pop bc + pop hl + ld a, 2 + call AddToAIScore + +; a bench Pokémon was found that can KO +; if this is a boss deck and it's at last prize card +; if arena Pokémon cannot KO, add to AI score +; and set wAIPlayEnergyCardForRetreat to $01 + + ld a, [wAIOpponentPrizeCount] + cp 2 + jr nc, .check_defending_id + call CheckIfNotABossDeckID + jr c, .check_defending_id + + xor a + ldh [hTempPlayAreaLocation_ff9d], a + call CheckIfAnyAttackKnocksOutDefendingCard + jr nc, .active_cant_ko_2 + call CheckIfSelectedAttackIsUnusable + jp nc, .check_defending_id +.active_cant_ko_2 + ld a, 40 + call AddToAIScore + ld a, $01 + ld [wAIPlayEnergyCardForRetreat], a + +.check_defending_id + ld a, DUELVARS_ARENA_CARD + call GetNonTurnDuelistVariable + call SwapTurn + call GetCardIDFromDeckIndex + call SwapTurn + ld a, e + cp MR_MIME + jr z, .mr_mime_or_hitmonlee + cp HITMONLEE ; ?? + jr nz, .check_retreat_cost + +; check bench if there's any Pokémon +; that can damage defending Pokémon +; this is done because of Mr. Mime's PKMN PWR +; but why Hitmonlee ($87) as well? +.mr_mime_or_hitmonlee + xor a + call CheckIfCanDamageDefendingPokemon + jr c, .check_retreat_cost + ld a, DUELVARS_BENCH + call GetTurnDuelistVariable + ld c, 0 +.loop_damage + inc c + ld a, [hli] + cp $ff + jr z, .check_retreat_cost + ld a, c + push hl + push bc + call CheckIfCanDamageDefendingPokemon + jr c, .can_damage + pop bc + pop hl + jr .loop_damage +.can_damage + pop bc + pop hl + ld a, 5 + call AddToAIScore + ld a, $01 + ld [wAIPlayEnergyCardForRetreat], a + +; subtract from wAIScore if retreat cost is larger than 1 +; then check if any cards have at least half HP, +; are final evolutions and can use second attack in the bench +; and adds to wAIScore if the active Pokémon doesn't meet +; these conditions +.check_retreat_cost + xor a + ldh [hTempPlayAreaLocation_ff9d], a + call GetPlayAreaCardRetreatCost + cp 2 + jr c, .one_or_none + cp 3 + jr nc, .three_or_more + ; exactly two + ld a, 1 + call SubFromAIScore + jr .one_or_none + +.three_or_more + ld a, 2 + call SubFromAIScore + +.one_or_none + call CheckIfArenaCardIsAtHalfHPCanEvolveAndUseSecondAttack + jr c, .check_defending_can_ko + call CountNumberOfSetUpBenchPokemon + cp 2 + jr c, .check_defending_can_ko + call AddToAIScore + +; check bench for Pokémon that +; the defending Pokémon can't knock out +; if none is found, skip SubFromAIScore +.check_defending_can_ko + ld a, DUELVARS_BENCH + call GetTurnDuelistVariable + ld e, 0 +.loop_ko_2 + inc e + ld a, [hli] + cp $ff + jr z, .exit_loop_ko + push de + push hl + call LoadCardDataToBuffer2_FromDeckIndex + ld a, [wLoadedCard2ID] + pop hl + pop de + cp MYSTERIOUS_FOSSIL + jr z, .loop_ko_2 + cp CLEFAIRY_DOLL + jr z, .loop_ko_2 + ld a, e + ldh [hTempPlayAreaLocation_ff9d], a + push de + push hl + call CheckIfDefendingPokemonCanKnockOut + pop hl + pop de + jr c, .loop_ko_2 + jr .check_active_id +.exit_loop_ko + ld a, 20 + call SubFromAIScore + +.check_active_id + ld a, DUELVARS_ARENA_CARD + call GetTurnDuelistVariable + call GetCardIDFromDeckIndex + ld a, e + cp MYSTERIOUS_FOSSIL + jr z, .mysterious_fossil_or_clefairy_doll + cp CLEFAIRY_DOLL + jr z, .mysterious_fossil_or_clefairy_doll + +; if wAIScore is at least 131, set carry + ld a, [wAIScore] + cp 131 + jr nc, .set_carry +.no_carry + or a + ret +.set_carry + scf + ret + +; set carry regardless if active card is +; either Mysterious Fossil or Clefairy Doll +; and there's a bench Pokémon who is not KO'd +; by defending Pokémon and can damage it +.mysterious_fossil_or_clefairy_doll + ld e, 0 +.loop_ko_3 + inc e + ld a, e + add DUELVARS_ARENA_CARD + call GetTurnDuelistVariable + cp $ff + jr z, .no_carry + ld a, e + ldh [hTempPlayAreaLocation_ff9d], a + push de + call CheckIfDefendingPokemonCanKnockOut + pop de + jr c, .loop_ko_3 + ld a, e + push de + call CheckIfCanDamageDefendingPokemon + pop de + jr nc, .loop_ko_3 + jr .set_carry + +; if player's turn and loaded attack is not a Pokémon Power OR +; if opponent's turn and wAITriedAttack == 0 +; set wcdda's bit 7 flag +Func_15b54: ; 15b54 (5:5b54) + xor a + ld [wcdda], a + ld a, [wWhoseTurn] + cp OPPONENT_TURN + jr z, .opponent + +; player + ld a, [wLoadedAttackCategory] + cp POKEMON_POWER + ret z + jr .set_flag + +.opponent + ld a, [wAITriedAttack] + or a + ret nz + +.set_flag + ld a, %10000000 + ld [wcdda], a + ret + +; calculates AI score for bench Pokémon +; returns in a and [hTempPlayAreaLocation_ff9d] the +; Play Area location of best card to switch to. +; returns carry if no Bench Pokemon. +AIDecideBenchPokemonToSwitchTo: ; 15b72 (5:5b72) + xor a + ldh [hTempPlayAreaLocation_ff9d], a + ld a, DUELVARS_NUMBER_OF_POKEMON_IN_PLAY_AREA + call GetTurnDuelistVariable + cp 2 + ret c + +; has at least 2 Pokémon in Play Area + call Func_15b54 + call LoadDefendingPokemonColorWRAndPrizeCards + ld a, 50 + ld [wAIScore], a + ld a, DUELVARS_NUMBER_OF_POKEMON_IN_PLAY_AREA + call GetTurnDuelistVariable + ld b, a + ld c, PLAY_AREA_ARENA + push bc + jp .store_score + +.next_bench + push bc + ld a, c + ldh [hTempPlayAreaLocation_ff9d], a + ld a, 50 + ld [wAIScore], a + +; check if card can KO defending Pokémon +; if it can, raise AI score +; if on last prize card, raise AI score again + call CheckIfAnyAttackKnocksOutDefendingCard + jr nc, .check_can_use_atks + call CheckIfSelectedAttackIsUnusable + jr c, .check_can_use_atks + ld a, 10 + call AddToAIScore + ld a, [wcdda] + or %00000001 + ld [wcdda], a + call CountPrizes + cp 2 + jp nc, .check_defending_weak + ld a, 10 + call AddToAIScore + +; calculates damage of both attacks +; to raise AI score accordingly +.check_can_use_atks + xor a + ld [wSelectedAttack], a + call CheckIfSelectedAttackIsUnusable + call nc, .HandleAttackDamageScore + ld a, $01 + ld [wSelectedAttack], a + call CheckIfSelectedAttackIsUnusable + call nc, .HandleAttackDamageScore + jr .check_energy_card + +; adds to AI score depending on amount of damage +; it can inflict to the defending Pokémon +; AI score += floor(Damage / 10) + 1 +.HandleAttackDamageScore + ld a, [wSelectedAttack] + call EstimateDamage_VersusDefendingCard + ld a, [wDamage] + call CalculateByteTensDigit + inc a + call AddToAIScore + ret + +; if an energy card that is needed is found in hand +; calculate damage of the move and raise AI score +; AI score += floor(Damage / 20) +.check_energy_card + call LookForEnergyNeededInHand + jr nc, .check_attached_energy + ld a, [wSelectedAttack] + call EstimateDamage_VersusDefendingCard + ld a, [wDamage] + call CalculateByteTensDigit + srl a + call AddToAIScore + +; if no energies attached to card, lower AI score +.check_attached_energy + ldh a, [hTempPlayAreaLocation_ff9d] + ld e, a + call GetPlayAreaCardAttachedEnergies + ld a, [wTotalAttachedEnergies] + or a + jr nz, .check_mr_mime + ld a, 1 + call SubFromAIScore + +; if can damage Mr Mime, raise AI score +.check_mr_mime + ld a, DUELVARS_ARENA_CARD + call GetNonTurnDuelistVariable + call SwapTurn + call LoadCardDataToBuffer2_FromDeckIndex + call SwapTurn + cp MR_MIME + jr nz, .check_defending_weak + xor a + call EstimateDamage_VersusDefendingCard + ld a, [wDamage] + or a + jr nz, .can_damage + ld a, $01 + call EstimateDamage_VersusDefendingCard + ld a, [wDamage] + or a + jr z, .check_defending_weak +.can_damage + ld a, 5 + call AddToAIScore + +; if defending card is weak to this card, raise AI score +.check_defending_weak + ldh a, [hTempPlayAreaLocation_ff9d] + add DUELVARS_ARENA_CARD + call GetTurnDuelistVariable + call LoadCardDataToBuffer1_FromDeckIndex + ld a, [wLoadedCard1Type] + call TranslateColorToWR + ld c, a + ld hl, wAIPlayerWeakness + and [hl] + jr z, .check_defending_resist + ld a, 3 + call AddToAIScore + +; if defending card is resistant to this card, lower AI score +.check_defending_resist + ld a, c + ld hl, wAIPlayerResistance + and [hl] + jr z, .check_resistance + ld a, 2 + call SubFromAIScore + +; if this card is resistant to defending Pokémon, raise AI score +.check_resistance + ld a, [wAIPlayerColor] + ld hl, wLoadedCard1Resistance + and [hl] + jr z, .check_weakness + ld a, 2 + call AddToAIScore + +; if this card is weak to defending Pokémon, lower AI score +.check_weakness + ld a, [wAIPlayerColor] + ld hl, wLoadedCard1Weakness + and [hl] + jr z, .check_retreat_cost + ld a, 3 + call SubFromAIScore + +; if this card's retreat cost < 2, raise AI score +; if this card's retreat cost > 2, lower AI score +.check_retreat_cost + call GetPlayAreaCardRetreatCost + cp 2 + jr c, .one_or_none + jr z, .check_player_prize_count + ld a, 1 + call SubFromAIScore + jr .check_player_prize_count +.one_or_none + ld a, 1 + call AddToAIScore + +; if wcdda != $81 +; if defending Pokémon can KO this card +; if player is not at last prize card, lower 3 from AI score +; if player is at last prize card, lower 10 from AI score +.check_player_prize_count + ld a, [wcdda] + cp %10000000 | %00000001 + jr z, .check_hp + call CheckIfDefendingPokemonCanKnockOut + jr nc, .check_hp + ld e, 3 + ld a, [wAIPlayerPrizeCount] + cp 1 + jr nz, .lower_score_1 + ld e, 10 +.lower_score_1 + ld a, e + call SubFromAIScore + +; if this card's HP is 0, make AI score 0 +.check_hp + ldh a, [hTempPlayAreaLocation_ff9d] + add DUELVARS_ARENA_CARD_HP + call GetTurnDuelistVariable + or a + jr nz, .add_hp_score + ld [wAIScore], a + jr .store_score + +; AI score += floor(HP/40) +.add_hp_score + ld b, a + ld a, 4 + call CalculateBDividedByA_Bank5 + call CalculateByteTensDigit + call AddToAIScore + +; raise AI score if +; - is a Mr Mime OR +; - is a Mew1 and defending card is not basic stage + ldh a, [hTempPlayAreaLocation_ff9d] + add DUELVARS_ARENA_CARD + call GetTurnDuelistVariable + call LoadCardDataToBuffer1_FromDeckIndex + cp MR_MIME + jr z, .raise_score + cp MEW1 + jr nz, .asm_15cf0 + ld a, DUELVARS_ARENA_CARD + call GetNonTurnDuelistVariable + call LoadCardDataToBuffer2_FromDeckIndex + ld a, [wLoadedCard2Stage] + or a + jr z, .asm_15cf0 +.raise_score + ld a, 5 + call AddToAIScore + +; if wLoadedCard1Unknown2 == $01, lower AI score +.asm_15cf0 + ld a, [wLoadedCard1Unknown2] + cp $01 + jr nz, .mysterious_fossil_or_clefairy_doll + ld a, 2 + call SubFromAIScore + +; if card is Mysterious Fossil or Clefairy Doll, +; lower AI score +.mysterious_fossil_or_clefairy_doll + ld a, [wLoadedCard1ID] + cp MYSTERIOUS_FOSSIL + jr z, .lower_score_2 + cp CLEFAIRY_DOLL + jr nz, .ai_score_bonus +.lower_score_2 + ld a, 10 + call SubFromAIScore + +.ai_score_bonus + ld b, a + ld a, [wAICardListRetreatBonus + 1] + or a + jr z, .store_score + ld h, a + ld a, [wAICardListRetreatBonus] + ld l, a + +.loop_ids + ld a, [hli] + or a + jr z, .store_score ; list is over + cp b + jr nz, .next_id + ld a, [hl] + cp $80 + jr c, .subtract_score + sub $80 + call AddToAIScore + jr .next_id +.subtract_score + ld c, a + ld a, $80 + sub c + call SubFromAIScore +.next_id + inc hl + jr .loop_ids + +.store_score + ldh a, [hTempPlayAreaLocation_ff9d] + ld c, a + ld b, $00 + ld hl, wPlayAreaAIScore + add hl, bc + ld a, [wAIScore] + ld [hl], a + pop bc + inc c + dec b + jp nz, .next_bench + +; done + xor a + ld [wcdb4], a + jp FindHighestBenchScore + +; handles AI action of retreating Arena Pokémon +; and chooses which energy cards to discard. +; if card can't discard, return carry. +; in case it's Clefairy Doll or Mysterious Fossil, +; handle its effect to discard itself instead of retreating. +; input: +; - a = Play Area location (PLAY_AREA_*) of card to retreat to. +AITryToRetreat: ; 15d4f (5:5d4f) + push af + ld a, [wAIPlayEnergyCardForRetreat] + or a + jr z, .check_id + +; AI is allowed to play an energy card +; from the hand in order to provide +; the necessary energy for retreat cost + +; check status + ld a, DUELVARS_ARENA_CARD_STATUS + call GetTurnDuelistVariable + and CNF_SLP_PRZ + cp ASLEEP + jp z, .check_id + cp PARALYZED + jp z, .check_id + +; if an energy card hasn't been played yet, +; checks if the Pokémon needs just one more energy to retreat +; if it does, check if there are any energy cards in hand +; and if there are, play that energy card + ld a, [wAlreadyPlayedEnergy] + or a + jr nz, .check_id + ld e, PLAY_AREA_ARENA + call CountNumberOfEnergyCardsAttached + push af + xor a + ldh [hTempPlayAreaLocation_ff9d], a + call GetPlayAreaCardRetreatCost + pop bc + cp b + jr c, .check_id + jr z, .check_id + ; energy attached < retreat cost + sub b + cp 1 + jr nz, .check_id + call CreateEnergyCardListFromHand + jr c, .check_id + ld a, [wDuelTempList] + ldh [hTemp_ffa0], a + xor a + ldh [hTempPlayAreaLocation_ffa1], a + ld a, OPPACTION_PLAY_ENERGY + bank1call AIMakeDecision + +.check_id + ld a, DUELVARS_ARENA_CARD + call GetTurnDuelistVariable + call GetCardIDFromDeckIndex + ld a, e + cp MYSTERIOUS_FOSSIL + jp z, .mysterious_fossil_or_clefairy_doll + cp CLEFAIRY_DOLL + jp z, .mysterious_fossil_or_clefairy_doll + +; if card is Asleep or Paralyzed, set carry and exit +; else, load the status in hTemp_ffa0 + pop af + ldh [hTempPlayAreaLocation_ffa1], a + ld a, DUELVARS_ARENA_CARD_STATUS + call GetTurnDuelistVariable + ld b, a + and CNF_SLP_PRZ + cp ASLEEP + jp z, .set_carry + cp PARALYZED + jp z, .set_carry + ld a, b + ldh [hTemp_ffa0], a + ld a, $ff + ldh [hTempRetreatCostCards], a + +; check energy required to retreat +; if the cost is 0, retreat right away + xor a + ldh [hTempPlayAreaLocation_ff9d], a + call GetPlayAreaCardRetreatCost + ld [wTempCardRetreatCost], a + or a + jp z, .retreat + +; if cost > 0 and number of energy cards attached == cost +; discard them all + xor a + call CreateArenaOrBenchEnergyCardList + ld e, PLAY_AREA_ARENA + call GetPlayAreaCardAttachedEnergies + ld a, [wTotalAttachedEnergies] + ld c, a + ld a, [wTempCardRetreatCost] + cp c + jr nz, .choose_energy_discard + + ld hl, hTempRetreatCostCards + ld de, wDuelTempList +.loop_1 + ld a, [de] + inc de + ld [hli], a + cp $ff + jr nz, .loop_1 + jp .retreat + +; if cost > 0 and number of energy cards attached > cost +; choose energy cards to discard according to color +.choose_energy_discard + ld a, DUELVARS_ARENA_CARD + call GetTurnDuelistVariable + call GetCardIDFromDeckIndex + ld a, e + ld [wTempCardID], a + call LoadCardDataToBuffer1_FromCardID + ld a, [wLoadedCard1Type] + or TYPE_ENERGY + ld [wTempCardType], a + ld a, [wTempCardRetreatCost] + ld c, a + +; first, look for and discard double colorless energy +; if retreat cost is >= 2 + ld hl, wDuelTempList + ld de, hTempRetreatCostCards +.loop_2 + ld a, c + cp 2 + jr c, .energy_not_same_color + ld a, [hli] + cp $ff + jr z, .energy_not_same_color + ld [de], a + push de + call GetCardIDFromDeckIndex + ld a, e + pop de + cp DOUBLE_COLORLESS_ENERGY + jr nz, .loop_2 + ld a, [de] + call RemoveCardFromDuelTempList + dec hl + inc de + dec c + dec c + jr nz, .loop_2 + jr .end_retreat_list + +; second, shuffle attached cards and discard energy cards +; that are not of the same type as the Pokémon +; the exception for this are cards that are needed for +; some attacks but are not of the same color as the Pokémon +; (i.e. Psyduck's Headache attack) +; and energy cards attached to Eevee corresponding to a +; color of any of its evolutions (water, fire, lightning) +.energy_not_same_color + ld hl, wDuelTempList + call CountCardsInDuelTempList + call ShuffleCards +.loop_3 + ld a, [hli] + cp $ff + jr z, .any_energy + ld [de], a + call CheckIfEnergyIsUseful + jr c, .loop_3 + ld a, [de] + call RemoveCardFromDuelTempList + dec hl + inc de + dec c + jr nz, .loop_3 + jr .end_retreat_list + +; third, discard any card until +; cost requirement is met +.any_energy + ld hl, wDuelTempList +.loop_4 + ld a, [hli] + cp $ff + jr z, .set_carry + ld [de], a + inc de + push de + call GetCardIDFromDeckIndex + ld a, e + pop de + cp DOUBLE_COLORLESS_ENERGY + jr nz, .not_double_colorless + dec c + jr z, .end_retreat_list +.not_double_colorless + dec c + jr nz, .loop_4 + +.end_retreat_list + ld a, $ff + ld [de], a + +.retreat + ld a, OPPACTION_ATTEMPT_RETREAT + bank1call AIMakeDecision + or a + ret +.set_carry + scf + ret + +; handle Mysterious Fossil and Clefairy Doll +; if there are bench Pokémon, use effect to discard card +; this is equivalent to using its Pokémon Power +.mysterious_fossil_or_clefairy_doll + ld a, DUELVARS_NUMBER_OF_POKEMON_IN_PLAY_AREA + call GetTurnDuelistVariable + cp 2 + jr nc, .has_bench + ; doesn't have any bench + pop af + jr .set_carry + +.has_bench + ld a, DUELVARS_ARENA_CARD + call GetTurnDuelistVariable + ldh [hTempCardIndex_ff9f], a + xor a + ldh [hTemp_ffa0], a + ld a, OPPACTION_USE_PKMN_POWER + bank1call AIMakeDecision + pop af + ldh [hAIPkmnPowerEffectParam], a + ld a, OPPACTION_EXECUTE_PKMN_POWER_EFFECT + bank1call AIMakeDecision + ld a, OPPACTION_DUEL_MAIN_SCENE + bank1call AIMakeDecision + or a + ret diff --git a/src/engine/duel/ai/special_attacks.asm b/src/engine/duel/ai/special_attacks.asm new file mode 100644 index 0000000..770324e --- /dev/null +++ b/src/engine/duel/ai/special_attacks.asm @@ -0,0 +1,481 @@ +; this function handles attacks with the SPECIAL_AI_HANDLING set, +; and makes specific checks in each of these attacks +; to either return a positive score (value above $80) +; or a negative score (value below $80). +; input: +; hTempPlayAreaLocation_ff9d = location of card with attack. +HandleSpecialAIAttacks: ; 16dcd (5:6dcd) + ldh a, [hTempPlayAreaLocation_ff9d] + add DUELVARS_ARENA_CARD + call GetTurnDuelistVariable + call GetCardIDFromDeckIndex + ld a, e + + cp NIDORANF + jr z, .NidoranFCallForFamily + cp ODDISH + jr z, .CallForFamily + cp BELLSPROUT + jr z, .CallForFamily + cp EXEGGUTOR + jp z, .Teleport + cp SCYTHER + jp z, .SwordsDanceAndFocusEnergy + cp KRABBY + jr z, .CallForFamily + cp VAPOREON1 + jp z, .SwordsDanceAndFocusEnergy + cp ELECTRODE2 + jp z, .ChainLightning + cp MAROWAK1 + jr z, .CallForFriend + cp MEW3 + jp z, .DevolutionBeam + cp JIGGLYPUFF2 + jp z, .FriendshipSong + cp PORYGON + jp z, .Conversion + cp MEWTWO3 + jp z, .EnergyAbsorption + cp MEWTWO2 + jp z, .EnergyAbsorption + cp NINETALES2 + jp z, .MixUp + cp ZAPDOS3 + jp z, .BigThunder + cp KANGASKHAN + jp z, .Fetch + cp DUGTRIO + jp z, .Earthquake + cp ELECTRODE1 + jp z, .EnergySpike + cp GOLDUCK + jp z, .HyperBeam + cp DRAGONAIR + jp z, .HyperBeam + +; return zero score. +.zero_score + xor a + ret + +; if any of card ID in a is found in deck, +; return a score of $80 + slots available in bench. +.CallForFamily: ; 16e3e (5:6e3e) + ld a, CARD_LOCATION_DECK + call CheckIfAnyCardIDinLocation + jr nc, .zero_score + ld a, DUELVARS_NUMBER_OF_POKEMON_IN_PLAY_AREA + call GetTurnDuelistVariable + cp MAX_BENCH_POKEMON + jr nc, .zero_score + ld b, a + ld a, MAX_BENCH_POKEMON + sub b + add $80 + ret + +; if any of NidoranM or NidoranF is found in deck, +; return a score of $80 + slots available in bench. +.NidoranFCallForFamily: ; 16e55 (5:6e55) + ld e, NIDORANM + ld a, CARD_LOCATION_DECK + call CheckIfAnyCardIDinLocation + jr c, .found_nidoran + ld e, NIDORANF + ld a, CARD_LOCATION_DECK + call CheckIfAnyCardIDinLocation + jr nc, .zero_score +.found_nidoran + ld a, DUELVARS_NUMBER_OF_POKEMON_IN_PLAY_AREA + call GetTurnDuelistVariable + cp MAX_PLAY_AREA_POKEMON + jr nc, .zero_score + ld b, a + ld a, MAX_PLAY_AREA_POKEMON + sub b + add $80 + ret + +; checks for certain card IDs of Fighting color in deck. +; if any of them are found, return a score of +; $80 + slots available in bench. +.CallForFriend: ; 16e77 (5:6e77) + ld e, GEODUDE + ld a, CARD_LOCATION_DECK + call CheckIfAnyCardIDinLocation + jr c, .found_fighting_card + ld e, ONIX + ld a, CARD_LOCATION_DECK + call CheckIfAnyCardIDinLocation + jr c, .found_fighting_card + ld e, CUBONE + ld a, CARD_LOCATION_DECK + call CheckIfAnyCardIDinLocation + jr c, .found_fighting_card + ld e, RHYHORN + ld a, CARD_LOCATION_DECK + call CheckIfAnyCardIDinLocation + jr c, .found_fighting_card + jr .zero_score +.found_fighting_card + ld a, DUELVARS_NUMBER_OF_POKEMON_IN_PLAY_AREA + call GetTurnDuelistVariable + cp MAX_BENCH_POKEMON + jr nc, .zero_score + ld b, a + ld a, MAX_BENCH_POKEMON + sub b + add $80 + ret + +; if any basic cards are found in deck, +; return a score of $80 + slots available in bench. +.FriendshipSong: ; 16ead (5:6ead) + call CheckIfAnyBasicPokemonInDeck + jr nc, .zero_score + ld a, DUELVARS_NUMBER_OF_POKEMON_IN_PLAY_AREA + call GetTurnDuelistVariable + cp MAX_PLAY_AREA_POKEMON + jr nc, .zero_score + ld b, a + ld a, MAX_PLAY_AREA_POKEMON + sub b + add $80 + ret + +; if AI decides to retreat, return a score of $80 + 10. +.Teleport: ; 16ec2 (5:6ec2) + call AIDecideWhetherToRetreat + jp nc, .zero_score + ld a, $8a + ret + +; tests for the following conditions: +; - player is under No Damage substatus; +; - second attack is unusable; +; - second attack deals no damage; +; if any are true, returns score of $80 + 5. +.SwordsDanceAndFocusEnergy: ; 16ecb (5:6ecb) + ld a, [wAICannotDamage] + or a + jr nz, .swords_dance_focus_energy_success + ld a, SECOND_ATTACK + ld [wSelectedAttack], a + call CheckIfSelectedAttackIsUnusable + jr c, .swords_dance_focus_energy_success + ld a, SECOND_ATTACK + call EstimateDamage_VersusDefendingCard + ld a, [wDamage] + or a + jp nz, .zero_score +.swords_dance_focus_energy_success + ld a, $85 + ret + +; checks player's active card color, then +; loops through bench looking for a Pokémon +; with that same color. +; if none are found, returns score of $80 + 2. +.ChainLightning: ; 16eea (5:6eea) + call SwapTurn + call GetArenaCardColor + call SwapTurn + ld b, a + ld a, DUELVARS_BENCH + call GetTurnDuelistVariable +.loop_chain_lightning_bench + ld a, [hli] + cp $ff + jr z, .chain_lightning_success + push bc + call GetCardIDFromDeckIndex + call GetCardType + pop bc + cp b + jr nz, .loop_chain_lightning_bench + jp .zero_score +.chain_lightning_success + ld a, $82 + ret + +.DevolutionBeam: ; 16f0f (5:6f0f) + call LookForCardThatIsKnockedOutOnDevolution + jp nc, .zero_score + ld a, $85 + ret + +; first checks if card is confused, and if so return 0. +; then checks number of Pokémon in bench that are viable to use: +; - if that number is < 2 and this attack is Conversion 1 OR +; - if that number is >= 2 and this attack is Conversion 2 +; then return score of $80 + 2. +; otherwise return score of $80 + 1. +.Conversion: ; 16f18 (5:6f18) + ld a, DUELVARS_ARENA_CARD_STATUS + call GetTurnDuelistVariable + and CNF_SLP_PRZ + cp CONFUSED + jp z, .zero_score + + ld a, [wSelectedAttack] + or a + jr nz, .conversion_2 + +; conversion 1 + call CountNumberOfSetUpBenchPokemon + cp 2 + jr c, .low_conversion_score + ld a, $82 + ret + +.conversion_2 + call CountNumberOfSetUpBenchPokemon + cp 2 + jr nc, .low_conversion_score + ld a, $82 + ret + +.low_conversion_score + ld a, $81 + ret + +; if any Psychic Energy is found in the Discard Pile, +; return a score of $80 + 2. +.EnergyAbsorption: ; 16f41 (5:6f41) + ld e, PSYCHIC_ENERGY + ld a, CARD_LOCATION_DISCARD_PILE + call CheckIfAnyCardIDinLocation + jp nc, .zero_score + ld a, $82 + ret + +; if player has cards in hand, AI calls Random: +; - 1/3 chance to encourage attack regardless; +; - 1/3 chance to dismiss attack regardless; +; - 1/3 change to make some checks to player's hand. +; AI tallies number of basic cards in hand, and if this +; number is >= 2, encourage attack. +; otherwise, if it finds an evolution card in hand that +; can evolve a card in player's deck, encourage. +; if encouraged, returns a score of $80 + 3. +.MixUp: ; 16f4e (5:6f4e) + ld a, DUELVARS_NUMBER_OF_CARDS_IN_HAND + call GetNonTurnDuelistVariable + or a + ret z + + ld a, 3 + call Random + or a + jr z, .encourage_mix_up + dec a + ret z + call SwapTurn + call CreateHandCardList + call SwapTurn + or a + ret z ; return if no hand cards (again) + ld a, DUELVARS_NUMBER_OF_POKEMON_IN_PLAY_AREA + call GetNonTurnDuelistVariable + cp 3 + jr nc, .mix_up_check_play_area + + ld hl, wDuelTempList + ld b, 0 +.loop_mix_up_hand + ld a, [hli] + cp $ff + jr z, .tally_basic_cards + push bc + call SwapTurn + call LoadCardDataToBuffer2_FromDeckIndex + call SwapTurn + pop bc + ld a, [wLoadedCard2Type] + cp TYPE_ENERGY + jr nc, .loop_mix_up_hand + ld a, [wLoadedCard2Stage] + or a + jr nz, .loop_mix_up_hand + ; is a basic Pokémon card + inc b + jr .loop_mix_up_hand +.tally_basic_cards + ld a, b + cp 2 + jr nc, .encourage_mix_up + +; less than 2 basic cards in hand +.mix_up_check_play_area + ld a, DUELVARS_ARENA_CARD + call GetNonTurnDuelistVariable +.loop_mix_up_play_area + ld a, [hli] + cp $ff + jp z, .zero_score + push hl + call SwapTurn + call CheckForEvolutionInList + call SwapTurn + pop hl + jr nc, .loop_mix_up_play_area + +.encourage_mix_up + ld a, $83 + ret + +; return score of $80 + 3. +.BigThunder: ; 16fb8 (5:6fb8) + ld a, $83 + ret + +; dismiss attack if cards in deck <= 20. +; otherwise return a score of $80 + 0. +.Fetch: ; 16fbb (5:6fbb) + ld a, DUELVARS_NUMBER_OF_CARDS_NOT_IN_DECK + call GetTurnDuelistVariable + cp 41 + jp nc, .zero_score + ld a, $80 + ret + +; dismiss attack if number of own benched cards which would +; be KOd is greater than or equal to the number +; of prize cards left for player. +.Earthquake: ; 16fc8 (5:6fc8) + ld a, DUELVARS_BENCH + call GetTurnDuelistVariable + + lb de, 0, 0 +.loop_earthquake + inc e + ld a, [hli] + cp $ff + jr z, .count_prizes + ld a, e + add DUELVARS_ARENA_CARD_HP + call GetTurnDuelistVariable + cp 20 + jr nc, .loop_earthquake + inc d + jr .loop_earthquake + +.count_prizes + push de + call CountPrizes + pop de + cp d + jp c, .zero_score + jp z, .zero_score + ld a, $80 + ret + +; if there's any lightning energy cards in deck, +; return a score of $80 + 3. +.EnergySpike: ; 16ff2 (5:6ff2) + ld a, CARD_LOCATION_DECK + ld e, LIGHTNING_ENERGY + call CheckIfAnyCardIDinLocation + jp nc, .zero_score + call AIProcessButDontPlayEnergy_SkipEvolution + jp nc, .zero_score + ld a, $83 + ret + +; only incentivize attack if player's active card, +; has any energy cards attached, and if so, +; return a score of $80 + 3. +.HyperBeam: ; 17005 (5:7005) + call SwapTurn + ld e, PLAY_AREA_ARENA + call CountNumberOfEnergyCardsAttached + call SwapTurn + or a + jr z, .hyper_beam_neutral + ld a, $83 + ret +.hyper_beam_neutral + ld a, $80 + ret + +; called when second attack is determined by AI to have +; more AI score than the first attack, so that it checks +; whether the first attack is a better alternative. +CheckWhetherToSwitchToFirstAttack: ; 17019 (5:7019) +; this checks whether the first attack is also viable +; (has more than minimum score to be used) + ld a, [wFirstAttackAIScore] + cp $50 + jr c, .keep_second_attack + +; first attack has more than minimum score to be used. +; check if second attack can KO. +; in case it can't, the AI keeps it as the attack to be used. +; (possibly due to the assumption that if the +; second attack cannot KO, the first attack can't KO as well.) + xor a + ldh [hTempPlayAreaLocation_ff9d], a + call EstimateDamage_VersusDefendingCard + ld a, DUELVARS_ARENA_CARD_HP + call GetNonTurnDuelistVariable + ld hl, wDamage + sub [hl] + jr z, .check_flag + jr nc, .keep_second_attack + +; second attack can ko, check its flag. +; in case its effect is to heal user or nullify/weaken damage +; next turn, keep second attack as the option. +; otherwise switch to the first attack. +.check_flag + ld a, DUELVARS_ARENA_CARD + call GetTurnDuelistVariable + ld d, a + ld e, SECOND_ATTACK + call CopyAttackDataAndDamage_FromDeckIndex + ld a, ATTACK_FLAG2_ADDRESS | HEAL_USER_F + call CheckLoadedAttackFlag + jr c, .keep_second_attack + ld a, ATTACK_FLAG2_ADDRESS | NULLIFY_OR_WEAKEN_ATTACK_F + call CheckLoadedAttackFlag + jr c, .keep_second_attack +; switch to first attack + xor a + ld [wSelectedAttack], a + ret +.keep_second_attack + ld a, $01 + ld [wSelectedAttack], a + ret + +; returns carry if there are +; any basic Pokémon cards in deck. +CheckIfAnyBasicPokemonInDeck: ; 17057 (5:7057) + ld e, 0 +.loop + ld a, DUELVARS_CARD_LOCATIONS + add e + call GetTurnDuelistVariable + cp CARD_LOCATION_DECK + jr nz, .next + push de + ld a, e + call LoadCardDataToBuffer2_FromDeckIndex + pop de + ld a, [wLoadedCard2Type] + cp TYPE_ENERGY + jr nc, .next + ld a, [wLoadedCard2Stage] + or a + jr z, .set_carry +.next + inc e + ld a, DECK_SIZE + cp e + jr nz, .loop + or a + ret +.set_carry + scf + ret diff --git a/src/engine/duel/ai/trainer_cards.asm b/src/engine/duel/ai/trainer_cards.asm new file mode 100644 index 0000000..4bee001 --- /dev/null +++ b/src/engine/duel/ai/trainer_cards.asm @@ -0,0 +1,6073 @@ +INCLUDE "data/duel/ai_trainer_card_logic.asm" + +_AIProcessHandTrainerCards: ; 200e5 (8:40e5) + ld [wAITrainerCardPhase], a +; create hand list in wDuelTempList and wTempHandCardList. + call CreateHandCardList + ld hl, wDuelTempList + ld de, wTempHandCardList + call CopyBuffer + ld hl, wTempHandCardList + +.loop_hand + ld a, [hli] + ld [wAITrainerCardToPlay], a + cp $ff + ret z + + push hl + ld a, [wAITrainerCardPhase] + ld d, a + ld hl, AITrainerCardLogic +.loop_data + xor a + ld [wCurrentAIFlags], a + ld a, [hli] + cp $ff + jp z, .pop_hl + +; compare input to first byte in data and continue if equal. + cp d + jp nz, .inc_hl_by_5 + + ld a, [hli] + ld [wce17], a + ld a, [wAITrainerCardToPlay] + call LoadCardDataToBuffer1_FromDeckIndex + + cp SWITCH + jr nz, .skip_switch_check + + ld b, a + ld a, [wPreviousAIFlags] + and AI_FLAG_USED_SWITCH + jr nz, .inc_hl_by_4 + ld a, b + +.skip_switch_check +; compare hand card to second byte in data and continue if equal. + ld b, a + ld a, [wce17] + cp b + jr nz, .inc_hl_by_4 + +; found Trainer card + push hl + push de + ld a, [wAITrainerCardToPlay] + ldh [hTempCardIndex_ff9f], a + +; if Headache effects prevent playing card +; move on to the next item in list. + bank1call CheckCantUseTrainerDueToHeadache + jp c, .next_in_data + + call LoadNonPokemonCardEffectCommands + ld a, EFFECTCMDTYPE_INITIAL_EFFECT_1 + call TryExecuteEffectCommandFunction + jp c, .next_in_data + +; AI can randomly choose not to play card. + farcall AIChooseRandomlyNotToDoAction + jr c, .next_in_data + +; call routine to decide whether to play Trainer card + pop de + pop hl + push hl + call CallIndirect + pop hl + jr nc, .inc_hl_by_4 + +; routine returned carry, which means +; this card should be played. + inc hl + inc hl + ld [wAITrainerCardParameter], a + +; show Play Trainer Card screen + push de + push hl + ld a, [wAITrainerCardToPlay] + ldh [hTempCardIndex_ff9f], a + ld a, OPPACTION_PLAY_TRAINER + bank1call AIMakeDecision + pop hl + pop de + jr c, .inc_hl_by_2 + +; execute the effects of the Trainer card + push hl + call CallIndirect + pop hl + + inc hl + inc hl + ld a, [wPreviousAIFlags] + ld b, a + ld a, [wCurrentAIFlags] + or b + ld [wPreviousAIFlags], a + pop hl + and AI_FLAG_MODIFIED_HAND + jp z, .loop_hand + +; the hand was modified during the Trainer effect +; so it needs to be re-listed again and +; looped from the top. + call CreateHandCardList + ld hl, wDuelTempList + ld de, wTempHandCardList + call CopyBuffer + ld hl, wTempHandCardList +; clear the AI_FLAG_MODIFIED_HAND flag + ld a, [wPreviousAIFlags] + and ~AI_FLAG_MODIFIED_HAND + ld [wPreviousAIFlags], a + jp .loop_hand + +.inc_hl_by_5 + inc hl +.inc_hl_by_4 + inc hl + inc hl +.inc_hl_by_2 + inc hl + inc hl + jp .loop_data + +.next_in_data + pop de + pop hl + inc hl + inc hl + inc hl + inc hl + jp .loop_data + +.pop_hl + pop hl + jp .loop_hand + +; makes AI use Potion card. +AIPlay_Potion: ; 201b5 (8:41b5) + ld a, [wAITrainerCardToPlay] + ldh [hTempCardIndex_ff9f], a + ld a, [wAITrainerCardParameter] + ldh [hTemp_ffa0], a + ld e, a + call GetCardDamageAndMaxHP + cp 20 + jr c, .play_card + ld a, 20 +.play_card + ldh [hTempPlayAreaLocation_ffa1], a + ld a, OPPACTION_EXECUTE_TRAINER_EFFECTS + bank1call AIMakeDecision + ret + +; if AI doesn't decide to retreat this card, +; check if defending Pokémon can KO active card +; next turn after using Potion. +; if it cannot, return carry. +; also take into account whether attack is high recoil. +AIDecide_Potion1: ; 201d1 (8:41d1) + farcall AIDecideWhetherToRetreat + jr c, .no_carry + call AICheckIfAttackIsHighRecoil + jr c, .no_carry + xor a ; active card + ldh [hTempPlayAreaLocation_ff9d], a + farcall CheckIfDefendingPokemonCanKnockOut + jr nc, .no_carry + ld d, a + + ld a, DUELVARS_ARENA_CARD_HP + call GetTurnDuelistVariable + ld h, a + ld e, PLAY_AREA_ARENA + call GetCardDamageAndMaxHP + cp 20 + 1 ; if damage <= 20 + jr c, .calculate_hp + ld a, 20 ; amount of Potion HP healing + +; if damage done by defending Pokémon next turn will still +; KO this card after healing, return no carry. +.calculate_hp + ld l, a + ld a, h + add l + sub d + jr c, .no_carry + jr z, .no_carry + +; return carry. + xor a + scf + ret +.no_carry + or a + ret + +; finds a card in Play Area to use Potion on. +; output: +; a = card to use Potion on; +; carry set if Potion should be used. +AIDecide_Potion2: ; 20204 (8:4204) + xor a + ldh [hTempPlayAreaLocation_ff9d], a + farcall CheckIfDefendingPokemonCanKnockOut + jr nc, .start_from_active +; can KO + ld d, a + ld a, DUELVARS_ARENA_CARD_HP + call GetTurnDuelistVariable + ld h, a + ld e, PLAY_AREA_ARENA + call GetCardDamageAndMaxHP + cp 20 + 1 ; if damage <= 20 + jr c, .calculate_hp + ld a, 20 +; return if using healing prevents KO. +.calculate_hp + ld l, a + ld a, h + add l + sub d + jr c, .count_prizes + jr z, .count_prizes + or a + ret + +; using Potion on active card does not prevent a KO. +; if player is at last prize, start loop with active card. +; otherwise start loop at first bench Pokémon. +.count_prizes + call SwapTurn + call CountPrizes + call SwapTurn + dec a + jr z, .start_from_active + ld e, PLAY_AREA_BENCH_1 + jr .loop + +; find Play Area Pokémon with more than 10 damage. +; skip Pokémon if it has a BOOST_IF_TAKEN_DAMAGE attack. +.start_from_active + ld e, PLAY_AREA_ARENA +.loop + ld a, DUELVARS_ARENA_CARD + add e + call GetTurnDuelistVariable + cp $ff + ret z + call .check_boost_if_taken_damage + jr c, .has_boost_damage + call GetCardDamageAndMaxHP + cp 20 ; if damage >= 20 + jr nc, .found +.has_boost_damage + inc e + jr .loop + +; a card was found, now to check if it's active or benched. +.found + ld a, e + or a + jr z, .active_card + +; bench card + push de + call SwapTurn + call CountPrizes + call SwapTurn + dec a + or a + jr z, .check_random + ld a, 10 + call Random + cp 3 +; 7/10 chance of returning carry. +.check_random + pop de + jr c, .no_carry + ld a, e + scf + ret + +; return carry for active card if not High Recoil. +.active_card + push de + call AICheckIfAttackIsHighRecoil + pop de + jr c, .no_carry + ld a, e + scf + ret +.no_carry + or a + ret + +; return carry if either of the attacks are usable +; and have the BOOST_IF_TAKEN_DAMAGE effect. +.check_boost_if_taken_damage ; 2027e (8:427e) + push de + xor a ; FIRST_ATTACK_OR_PKMN_POWER + ld [wSelectedAttack], a + farcall CheckIfSelectedAttackIsUnusable + jr c, .second_attack + ld a, ATTACK_FLAG3_ADDRESS | BOOST_IF_TAKEN_DAMAGE_F + call CheckLoadedAttackFlag + jr c, .set_carry +.second_attack + ld a, SECOND_ATTACK + ld [wSelectedAttack], a + farcall CheckIfSelectedAttackIsUnusable + jr c, .false + ld a, ATTACK_FLAG3_ADDRESS | BOOST_IF_TAKEN_DAMAGE_F + call CheckLoadedAttackFlag + jr c, .set_carry +.false + pop de + or a + ret +.set_carry + pop de + scf + ret + +; makes AI use Super Potion card. +AIPlay_SuperPotion: ; 202a8 (8:42a8) + ld a, [wAITrainerCardToPlay] + ldh [hTempCardIndex_ff9f], a + ld a, [wAITrainerCardParameter] + ldh [hTempPlayAreaLocation_ffa1], a + call AIPickEnergyCardToDiscard + ldh [hTemp_ffa0], a + ld a, [wAITrainerCardParameter] + ld e, a + call GetCardDamageAndMaxHP + cp 40 + jr c, .play_card + ld a, 40 +.play_card + ldh [hTempRetreatCostCards], a + ld a, OPPACTION_EXECUTE_TRAINER_EFFECTS + bank1call AIMakeDecision + ret + +; if AI doesn't decide to retreat this card and card has +; any energy cards attached, check if defending Pokémon can KO +; active card next turn after using Super Potion. +; if it cannot, return carry. +; also take into account whether attack is high recoil. +AIDecide_SuperPotion1: ; 202cc (8:42cc) + farcall AIDecideWhetherToRetreat + jr c, .no_carry + call AICheckIfAttackIsHighRecoil + jr c, .no_carry + xor a + ldh [hTempPlayAreaLocation_ff9d], a + ld e, a + call .check_attached_energy + ret nc + farcall CheckIfDefendingPokemonCanKnockOut + jr nc, .no_carry + + ld d, a + ld d, a + ld a, DUELVARS_ARENA_CARD_HP + call GetTurnDuelistVariable + ld h, a + ld e, PLAY_AREA_ARENA + call GetCardDamageAndMaxHP + cp 40 + 1 ; if damage < 40 + jr c, .calculate_hp + ld a, 40 +.calculate_hp + ld l, a + ld a, h + add l + sub d + jr c, .no_carry + jr z, .no_carry + +; return carry + ld a, e + scf + ret +.no_carry + or a + ret + +; returns carry if card has energies attached. +.check_attached_energy ; 20305 (8:4305) + call GetPlayAreaCardAttachedEnergies + ld a, [wTotalAttachedEnergies] + or a + ret z + scf + ret + +; finds a card in Play Area to use Super Potion on. +; output: +; a = card to use Super Potion on; +; carry set if Super Potion should be used. +AIDecide_SuperPotion2: ; 2030f (8:430f) + xor a + ldh [hTempPlayAreaLocation_ff9d], a + farcall CheckIfDefendingPokemonCanKnockOut + jr nc, .start_from_active +; can KO + ld d, a + ld a, DUELVARS_ARENA_CARD_HP + call GetTurnDuelistVariable + ld h, a + ld e, PLAY_AREA_ARENA + call GetCardDamageAndMaxHP + cp 40 + 1 ; if damage < 40 + jr c, .calculate_hp + ld a, 40 +; return if using healing prevents KO. +.calculate_hp + ld l, a + ld a, h + add l + sub d + jr c, .count_prizes + jr z, .count_prizes + or a + ret + +; using Super Potion on active card does not prevent a KO. +; if player is at last prize, start loop with active card. +; otherwise start loop at first bench Pokémon. +.count_prizes + call SwapTurn + call CountPrizes + call SwapTurn + dec a + jr z, .start_from_active + ld e, PLAY_AREA_BENCH_1 + jr .loop + +; find Play Area Pokémon with more than 30 damage. +; skip Pokémon if it doesn't have any energy attached, +; has a BOOST_IF_TAKEN_DAMAGE attack, +; or if discarding makes any attack of its attacks unusable. +.start_from_active + ld e, PLAY_AREA_ARENA +.loop + ld a, DUELVARS_ARENA_CARD + add e + call GetTurnDuelistVariable + cp $ff + ret z + ld d, a + call .check_attached_energy + jr nc, .next + call .check_boost_if_taken_damage + jr c, .next + call .check_energy_cost + jr c, .next + call GetCardDamageAndMaxHP + cp 40 ; if damage >= 40 + jr nc, .found +.next + inc e + jr .loop + +; a card was found, now to check if it's active or benched. +.found + ld a, e + or a + jr z, .active_card + +; bench card + push de + call SwapTurn + call CountPrizes + call SwapTurn + dec a + or a + jr z, .check_random + ld a, 10 + call Random + cp 3 +; 7/10 chance of returning carry. +.check_random + pop de + jr c, .no_carry + ld a, e + scf + ret + +; return carry for active card if not Hgh Recoil. +.active_card + push de + call AICheckIfAttackIsHighRecoil + pop de + jr c, .no_carry + ld a, e + scf + ret +.no_carry + or a + ret + +; returns carry if card has energies attached. +.check_attached_energy ; 20394 (8:4394) + call GetPlayAreaCardAttachedEnergies + ld a, [wTotalAttachedEnergies] + or a + ret z + scf + ret + +; return carry if either of the attacks are usable +; and have the BOOST_IF_TAKEN_DAMAGE effect. +.check_boost_if_taken_damage ; 2039e (8:439e) + push de + xor a ; FIRST_ATTACK_OR_PKMN_POWER + ld [wSelectedAttack], a + farcall CheckIfSelectedAttackIsUnusable + jr c, .second_attack_1 + ld a, ATTACK_FLAG3_ADDRESS | BOOST_IF_TAKEN_DAMAGE_F + call CheckLoadedAttackFlag + jr c, .true_1 +.second_attack_1 + ld a, SECOND_ATTACK + ld [wSelectedAttack], a + farcall CheckIfSelectedAttackIsUnusable + jr c, .false_1 + ld a, ATTACK_FLAG3_ADDRESS | BOOST_IF_TAKEN_DAMAGE_F + call CheckLoadedAttackFlag + jr c, .true_1 +.false_1 + pop de + or a + ret +.true_1 + pop de + scf + ret + +; returns carry if discarding energy card renders any attack unusable, +; given that they have enough energy to be used before discarding. +.check_energy_cost ; 203c8 (8:43c8) + push de + xor a ; FIRST_ATTACK_OR_PKMN_POWER + ld [wSelectedAttack], a + ld a, e + ldh [hTempPlayAreaLocation_ff9d], a + farcall CheckEnergyNeededForAttack + jr c, .second_attack_2 + farcall CheckEnergyNeededForAttackAfterDiscard + jr c, .true_2 + +.second_attack_2 + pop de + push de + ld a, SECOND_ATTACK + ld [wSelectedAttack], a + ld a, e + ldh [hTempPlayAreaLocation_ff9d], a + farcall CheckEnergyNeededForAttack + jr c, .false_2 + farcall CheckEnergyNeededForAttackAfterDiscard + jr c, .true_2 + +.false_2 + pop de + or a + ret +.true_2 + pop de + scf + ret + +AIPlay_Defender: ; 203f8 (8:43f8) + ld a, [wAITrainerCardToPlay] + ldh [hTempCardIndex_ff9f], a + xor a + ldh [hTemp_ffa0], a + ld a, OPPACTION_EXECUTE_TRAINER_EFFECTS + bank1call AIMakeDecision + ret + +; returns carry if using Defender can prevent a KO +; by the defending Pokémon. +; this takes into account both attacks and whether they're useable. +AIDecide_Defender1: ; 20406 (8:4406) + xor a ; PLAY_AREA_ARENA + ldh [hTempPlayAreaLocation_ff9d], a + farcall CheckIfAnyAttackKnocksOutDefendingCard + jr nc, .cannot_ko + farcall CheckIfSelectedAttackIsUnusable + jr nc, .no_carry + farcall LookForEnergyNeededForAttackInHand + jr c, .no_carry + +.cannot_ko +; check if any of the defending Pokémon's attacks deal +; damage exactly equal to current HP, and if so, +; only continue if that attack is useable. + farcall CheckIfAnyDefendingPokemonAttackDealsSameDamageAsHP + jr nc, .no_carry + call SwapTurn + farcall CheckIfSelectedAttackIsUnusable + call SwapTurn + jr c, .no_carry + + ld a, [wSelectedAttack] + farcall EstimateDamage_FromDefendingPokemon + ld a, [wDamage] + ld [wce06], a + ld d, a + +; load in a the attack that was not selected, +; and check if it is useable. + ld a, [wSelectedAttack] + ld b, a + ld a, $01 + sub b + ld [wSelectedAttack], a + push de + call SwapTurn + farcall CheckIfSelectedAttackIsUnusable + call SwapTurn + pop de + jr c, .switch_back + +; the other attack is useable. +; compare its damage to the selected attack. + ld a, [wSelectedAttack] + push de + farcall EstimateDamage_FromDefendingPokemon + pop de + ld a, [wDamage] + cp d + jr nc, .subtract + +; in case the non-selected attack is useable +; and deals less damage than the selected attack, +; switch back to the other attack. +.switch_back + ld a, [wSelectedAttack] + ld b, a + ld a, $01 + sub b + ld [wSelectedAttack], a + ld a, [wce06] + ld [wDamage], a + +; now the selected attack is the one that deals +; the most damage of the two (and is useable). +; if subtracting damage by using Defender +; still prevents a KO, return carry. +.subtract + ld a, [wDamage] + sub 20 + ld d, a + ld a, DUELVARS_ARENA_CARD_HP + call GetTurnDuelistVariable + sub d + jr c, .no_carry + jr z, .no_carry + scf + ret +.no_carry + or a + ret + +; return carry if using Defender prevents Pokémon +; from being knocked out by an attack with recoil. +AIDecide_Defender2: ; 20486 (8:4486) + ld a, ATTACK_FLAG1_ADDRESS | HIGH_RECOIL_F + call CheckLoadedAttackFlag + jr c, .recoil + ld a, ATTACK_FLAG1_ADDRESS | LOW_RECOIL_F + call CheckLoadedAttackFlag + jr c, .recoil + or a + ret + +.recoil + ld a, DUELVARS_ARENA_CARD + call GetTurnDuelistVariable + call LoadCardDataToBuffer2_FromDeckIndex + ld a, [wSelectedAttack] + or a + jr nz, .second_attack +; first attack + ld a, [wLoadedCard2Atk1EffectParam] + jr .check_weak +.second_attack + ld a, [wLoadedCard2Atk2EffectParam] + +; double recoil damage if card is weak to its own color. +.check_weak + ld d, a + push de + call GetArenaCardColor + call TranslateColorToWR + ld b, a + call GetArenaCardWeakness + and b + pop de + jr z, .check_resist + sla d + +; subtract 30 from recoil damage if card resists its own color. +; if this yields a negative number, return no carry. +.check_resist + push de + call GetArenaCardColor + call TranslateColorToWR + ld b, a + call GetArenaCardResistance + and b + pop de + jr z, .subtract + ld a, d + sub 30 + jr c, .no_carry + ld d, a + +; subtract damage prevented by Defender. +; if damage still knocks out card, return no carry. +; if damage does not knock out, return carry. +.subtract + ld a, d + or a + jr z, .no_carry + sub 20 + ld d, a + ld a, DUELVARS_ARENA_CARD_HP + call GetTurnDuelistVariable + sub d + jr c, .no_carry + jr z, .no_carry + scf + ret +.no_carry + or a + ret + +AIPlay_Pluspower: ; 204e8 (8:44e8) + ld a, [wCurrentAIFlags] + or AI_FLAG_USED_PLUSPOWER + ld [wCurrentAIFlags], a + ld a, [wAITrainerCardParameter] + ld [wAIPluspowerAttack], a + ld a, [wAITrainerCardToPlay] + ldh [hTempCardIndex_ff9f], a + ld a, OPPACTION_EXECUTE_TRAINER_EFFECTS + bank1call AIMakeDecision + ret + +; returns carry if using a Pluspower can KO defending Pokémon +; if active card cannot KO without the boost. +; outputs in a the attack to use. +AIDecide_Pluspower1: ; 20501 (8:4501) +; this is mistakenly duplicated + xor a + ldh [hTempPlayAreaLocation_ff9d], a + xor a + ldh [hTempPlayAreaLocation_ff9d], a + +; continue if no attack can knock out. +; if there's an attack that can, only continue +; if it's unusable and there's no card in hand +; to fulfill its energy cost. + farcall CheckIfAnyAttackKnocksOutDefendingCard + jr nc, .cannot_ko + farcall CheckIfSelectedAttackIsUnusable + jr nc, .no_carry + farcall LookForEnergyNeededForAttackInHand + jr c, .no_carry + +; cannot use an attack that knocks out. +.cannot_ko +; get active Pokémon's info. + ld a, DUELVARS_ARENA_CARD + call GetTurnDuelistVariable + call GetCardIDFromDeckIndex + ld a, e + ld [wTempTurnDuelistCardID], a + +; get defending Pokémon's info and check +; its No Damage or Effect substatus. +; if substatus is active, return. + call SwapTurn + ld a, DUELVARS_ARENA_CARD + call GetTurnDuelistVariable + call GetCardIDFromDeckIndex + ld a, e + ld [wTempNonTurnDuelistCardID], a + bank1call HandleNoDamageOrEffectSubstatus + call SwapTurn + jr c, .no_carry + +; check both attacks and decide which one +; can KO with Pluspower boost. +; if neither can KO, return no carry. + xor a ; FIRST_ATTACK_OR_PKMN_POWER + ld [wSelectedAttack], a + call .check_ko_with_pluspower + jr c, .kos_with_pluspower_1 + ld a, SECOND_ATTACK + ld [wSelectedAttack], a + call .check_ko_with_pluspower + jr c, .kos_with_pluspower_2 + +.no_carry + or a + ret + +; first attack can KO with Pluspower. +.kos_with_pluspower_1 + call .check_mr_mime + jr nc, .no_carry + xor a ; FIRST_ATTACK_OR_PKMN_POWER + scf + ret +; second attack can KO with Pluspower. +.kos_with_pluspower_2 + call .check_mr_mime + jr nc, .no_carry + ld a, SECOND_ATTACK + scf + ret + +; return carry if attack is useable and KOs +; defending Pokémon with Pluspower boost. +.check_ko_with_pluspower ; 20562 (8:4562) + farcall CheckIfSelectedAttackIsUnusable + jr c, .unusable + ld a, [wSelectedAttack] + farcall EstimateDamage_VersusDefendingCard + ld a, DUELVARS_ARENA_CARD_HP + call GetNonTurnDuelistVariable + ld b, a + ld hl, wDamage + sub [hl] + jr c, .no_carry + jr z, .no_carry + ld a, [hl] + add 10 ; add Pluspower boost + ld c, a + ld a, b + sub c + ret c ; return carry if damage > HP left + ret nz ; does not KO + scf + ret ; KOs with Pluspower boost +.unusable + or a + ret + +; returns carry if Pluspower boost does +; not exceed 30 damage when facing Mr. Mime. +.check_mr_mime ; 20589 (8:4589) + ld a, [wDamage] + add 10 ; add Pluspower boost + cp 30 ; no danger in preventing damage + ret c + call SwapTurn + ld a, DUELVARS_ARENA_CARD + call GetTurnDuelistVariable + call GetCardIDFromDeckIndex + call SwapTurn + ld a, e + cp MR_MIME + ret z +; damage is >= 30 but not Mr. Mime + scf + ret + +; returns carry 7/10 of the time +; if selected attack is useable, can't KO without Pluspower boost +; can damage Mr. Mime even with Pluspower boost +; and has a minimum damage > 0. +; outputs in a the attack to use. +AIDecide_Pluspower2: ; 205a5 (8:45a5) + xor a + ldh [hTempPlayAreaLocation_ff9d], a + call .check_can_ko + jr nc, .no_carry + call .check_random + jr nc, .no_carry + call .check_mr_mime + jr nc, .no_carry + scf + ret +.no_carry + or a + ret + +; returns carry if Pluspower boost does +; not exceed 30 damage when facing Mr. Mime. +.check_mr_mime ; 205bb (8:45bb) + ld a, [wDamage] + add 10 ; add Pluspower boost + cp 30 ; no danger in preventing damage + ret c + call SwapTurn + ld a, DUELVARS_ARENA_CARD + call GetTurnDuelistVariable + call GetCardIDFromDeckIndex + call SwapTurn + ld a, e + cp MR_MIME + ret z +; damage is >= 30 but not Mr. Mime + scf + ret + +; return carry if attack is useable but cannot KO. +.check_can_ko ; 205d7 (8:45d7) + farcall CheckIfSelectedAttackIsUnusable + jr c, .unusable + ld a, [wSelectedAttack] + farcall EstimateDamage_VersusDefendingCard + ld a, DUELVARS_ARENA_CARD_HP + call GetNonTurnDuelistVariable + ld b, a + ld hl, wDamage + sub [hl] + jr c, .no_carry + jr z, .no_carry +; can't KO. + scf + ret +.unusable + or a + ret + +; return carry 7/10 of the time if +; attack is useable and minimum damage > 0. +.check_random ; 205f6 (8:45f6) + farcall CheckIfSelectedAttackIsUnusable + jr c, .unusable + ld a, [wSelectedAttack] + farcall EstimateDamage_VersusDefendingCard + ld a, [wAIMinDamage] + cp 10 + jr c, .unusable + ld a, 10 + call Random + cp 3 + ret + +AIPlay_Switch: ; 20612 (8:4612) + ld a, [wCurrentAIFlags] + or AI_FLAG_USED_SWITCH + ld [wCurrentAIFlags], a + ld a, [wAITrainerCardToPlay] + ldh [hTempCardIndex_ff9f], a + ld a, [wAITrainerCardParameter] + ldh [hTemp_ffa0], a + ld a, OPPACTION_EXECUTE_TRAINER_EFFECTS + bank1call AIMakeDecision + xor a + ld [wcdb4], a + ret + +; returns carry if the active card has less energy cards +; than the retreat cost and if AI can't play an energy +; card from the hand to fulfill the cost +AIDecide_Switch: ; 2062e (8:462e) +; check if AI can already play an energy card from hand to retreat + ld a, [wAIPlayEnergyCardForRetreat] + or a + jr z, .check_cost_amount + +; can't play energy card from hand to retreat +; compare number of energy cards attached to retreat cost + xor a ; PLAY_AREA_ARENA + ldh [hTempPlayAreaLocation_ff9d], a + call GetPlayAreaCardRetreatCost + push af + ld e, PLAY_AREA_ARENA + farcall CountNumberOfEnergyCardsAttached + ld b, a + pop af + sub b + ; jump if cards attached > retreat cost + jr c, .check_cost_amount + cp 2 + ; jump if retreat cost is 2 more energy cards + ; than the number of cards attached + jr nc, .switch + +.check_cost_amount + xor a ; PLAY_AREA_ARENA + ldh [hTempPlayAreaLocation_ff9d], a + call GetPlayAreaCardRetreatCost + cp 3 + ; jump if retreat cost >= 3 + jr nc, .switch + + push af + ld e, PLAY_AREA_ARENA + farcall CountNumberOfEnergyCardsAttached + pop bc + cp b + ; jump if energy cards attached < retreat cost + jr c, .switch + ret + +.switch + farcall AIDecideBenchPokemonToSwitchTo + ccf + ret + +AIPlay_GustOfWind: ; 20666 (8:4666) + ld a, [wCurrentAIFlags] + or AI_FLAG_USED_GUST_OF_WIND + ld [wCurrentAIFlags], a + ld a, [wAITrainerCardToPlay] + ldh [hTempCardIndex_ff9f], a + ld a, [wAITrainerCardParameter] + ldh [hTemp_ffa0], a + ld a, OPPACTION_EXECUTE_TRAINER_EFFECTS + bank1call AIMakeDecision + ret + +AIDecide_GustOfWind: ; 2067e (8:467e) + ld a, DUELVARS_NUMBER_OF_POKEMON_IN_PLAY_AREA + call GetNonTurnDuelistVariable + dec a + or a + ret z ; no bench cards + +; if used Gust Of Wind already, +; do not use it again. + ld a, [wPreviousAIFlags] + and AI_FLAG_USED_GUST_OF_WIND + ret nz + + farcall CheckIfActivePokemonCanUseAnyNonResidualAttack + ret nc ; no non-residual attack can be used + + xor a ; PLAY_AREA_ARENA + ldh [hTempPlayAreaLocation_ff9d], a + farcall CheckIfAnyAttackKnocksOutDefendingCard + jr nc, .check_id ; if can't KO + farcall CheckIfSelectedAttackIsUnusable + jr nc, .no_carry ; if KO attack is useable + farcall LookForEnergyNeededForAttackInHand + jr c, .no_carry ; if energy card is in hand + +.check_id + ; skip if current active card is MEW3 or MEWTWO1 + ld a, DUELVARS_ARENA_CARD + call GetTurnDuelistVariable + call GetCardIDFromDeckIndex + ld a, e + cp MEW3 + jr z, .no_carry + cp MEWTWO1 + jr z, .no_carry + + call .FindBenchCardToKnockOut + ret c + + xor a ; PLAY_AREA_ARENA + ldh [hTempPlayAreaLocation_ff9d], a + call .CheckIfNoAttackDealsDamage + jr c, .check_bench_energy + + ; skip if current arena card's color is + ; the defending card's weakness + call GetArenaCardColor + call TranslateColorToWR + ld b, a + call SwapTurn + call GetArenaCardWeakness + call SwapTurn + and b + jr nz, .no_carry + +; check weakness + call .FindBenchCardWithWeakness + ret nc ; no bench card weak to arena card + scf + ret ; found bench card weak to arena card + +.no_carry + or a + ret + +; being here means AI's arena card cannot damage player's arena card + +; first check if there is a card in player's bench that +; has no attached energy cards and that the AI can damage +.check_bench_energy + ; return carry if there's a bench card with weakness + call .FindBenchCardWithWeakness + ret c + + ld a, DUELVARS_NUMBER_OF_POKEMON_IN_PLAY_AREA + call GetNonTurnDuelistVariable + ld d, a + ld e, PLAY_AREA_ARENA +; loop through bench and check attached energy cards +.loop_1 + inc e + dec d + jr z, .check_bench_hp + call SwapTurn + call GetPlayAreaCardAttachedEnergies + call SwapTurn + ld a, [wTotalAttachedEnergies] + or a + jr nz, .loop_1 ; skip if has energy attached + call .CheckIfCanDamageBenchedCard + jr nc, .loop_1 + ld a, e + scf + ret + +.check_bench_hp + ld a, $ff + ld [wce06], a + xor a + ld [wce08], a + ld e, a + ld a, DUELVARS_NUMBER_OF_POKEMON_IN_PLAY_AREA + call GetNonTurnDuelistVariable + ld d, a + +; find bench card with least amount of available HP +.loop_2 + inc e + dec d + jr z, .check_found + ld a, e + add DUELVARS_ARENA_CARD_HP + call GetNonTurnDuelistVariable + ld b, a + ld a, [wce06] + inc b + cp b + jr c, .loop_2 + call .CheckIfCanDamageBenchedCard + jr nc, .loop_2 + dec b + ld a, b + ld [wce06], a + ld a, e + ld [wce08], a + jr .loop_2 + +.check_found + ld a, [wce08] + or a + jr z, .no_carry +; a card was found + +.set_carry + scf + ret + +.check_can_damage + push bc + push hl + xor a ; PLAY_AREA_ARENA + farcall CheckIfCanDamageDefendingPokemon + pop hl + pop bc + jr nc, .loop_3 + ld a, c + scf + ret + +; returns carry if any of the player's +; benched cards is weak to color in b +; and has a way to damage it +.FindBenchCardWithWeakness ; 2074d (8:474d) + ld a, DUELVARS_BENCH + call GetNonTurnDuelistVariable + ld c, PLAY_AREA_ARENA +.loop_3 + inc c + ld a, [hli] + cp $ff + jr z, .no_carry + call SwapTurn + call LoadCardDataToBuffer1_FromDeckIndex + call SwapTurn + ld a, [wLoadedCard1Weakness] + and b + jr nz, .check_can_damage + jr .loop_3 + +; returns carry if neither attack can deal damage +.CheckIfNoAttackDealsDamage ; 2076b (8:476b) + xor a ; FIRST_ATTACK_OR_PKMN_POWER + ld [wSelectedAttack], a + call .CheckIfAttackDealsNoDamage + jr c, .second_attack + ret +.second_attack + ld a, SECOND_ATTACK + ld [wSelectedAttack], a + call .CheckIfAttackDealsNoDamage + jr c, .true + ret +.true + scf + ret + +; returns carry if attack is Pokemon Power +; or otherwise doesn't deal any damage +.CheckIfAttackDealsNoDamage ; 20782 (8:4782) + ld a, [wSelectedAttack] + ld e, a + ld a, DUELVARS_ARENA_CARD + call GetTurnDuelistVariable + ld d, a + call CopyAttackDataAndDamage_FromDeckIndex + ld a, [wLoadedAttackCategory] + + ; skip if attack is a Power or has 0 damage + cp POKEMON_POWER + jr z, .no_damage + ld a, [wDamage] + or a + ret z + + ; check damage against defending card + ld a, [wSelectedAttack] + farcall EstimateDamage_VersusDefendingCard + ld a, [wAIMaxDamage] + or a + ret nz + +.no_damage + scf + ret + +; returns carry if there is a player's bench card that +; the opponent's current active card can KO +.FindBenchCardToKnockOut ; 207a9 (8:47a9) + ld a, DUELVARS_BENCH + call GetNonTurnDuelistVariable + ld e, PLAY_AREA_BENCH_1 + +.loop_4 + ld a, [hli] + cp $ff + ret z + +; overwrite the player's active card and its HP +; with the current bench card that is being checked + push hl + push de + ld b, a + ld a, DUELVARS_ARENA_CARD + call GetNonTurnDuelistVariable + push af + ld [hl], b + ld a, e + add DUELVARS_ARENA_CARD_HP + call GetNonTurnDuelistVariable + ld b, a + ld a, DUELVARS_ARENA_CARD_HP + call GetNonTurnDuelistVariable + push af + ld [hl], b + + xor a ; PLAY_AREA_ARENA + ldh [hTempPlayAreaLocation_ff9d], a + call .CheckIfAnyAttackKnocksOut + jr nc, .next + farcall CheckIfSelectedAttackIsUnusable + jr nc, .found + farcall LookForEnergyNeededForAttackInHand + jr c, .found + +; the following two local routines can be condensed into one +; since they both revert the player's arena card +.next + ld a, DUELVARS_ARENA_CARD_HP + call GetNonTurnDuelistVariable + pop af + ld [hl], a + ld a, DUELVARS_ARENA_CARD + call GetNonTurnDuelistVariable + pop af + ld [hl], a + pop de + inc e + pop hl + jr .loop_4 + +; revert player's arena card and set carry +.found + ld a, DUELVARS_ARENA_CARD_HP + call GetNonTurnDuelistVariable + pop af + ld [hl], a + ld a, DUELVARS_ARENA_CARD + call GetNonTurnDuelistVariable + pop af + ld [hl], a + pop de + ld a, e + pop hl + scf + ret + +; returns carry if any of arena card's attacks +; KOs player card in location stored in e +.CheckIfAnyAttackKnocksOut ; 20806 (8:4806) + xor a ; FIRST_ATTACK_OR_PKMN_POWER + call .CheckIfAttackKnocksOut + ret c + ld a, SECOND_ATTACK + +; returns carry if attack KOs player card +; in location stored in e +.CheckIfAttackKnocksOut + push de + farcall EstimateDamage_VersusDefendingCard + pop de + ld a, DUELVARS_ARENA_CARD_HP + add e + call GetNonTurnDuelistVariable + ld hl, wDamage + sub [hl] + ret c + ret nz + scf + ret + +; returns carry if opponent's arena card can damage +; this benched card if it were switched with +; the player's arena card +.CheckIfCanDamageBenchedCard ; 20821 (8:4821) + push bc + push de + push hl + + ; overwrite arena card data + ld a, e + add DUELVARS_ARENA_CARD + call GetNonTurnDuelistVariable + ld b, a + ld a, DUELVARS_ARENA_CARD + call GetNonTurnDuelistVariable + push af + ld [hl], b + + ; overwrite arena card HP + ld a, e + add DUELVARS_ARENA_CARD_HP + call GetNonTurnDuelistVariable + ld b, a + ld a, DUELVARS_ARENA_CARD_HP + call GetNonTurnDuelistVariable + push af + ld [hl], b + + xor a ; PLAY_AREA_ARENA + farcall CheckIfCanDamageDefendingPokemon + jr c, .can_damage + +; the following two local routines can be condensed into one +; since they both revert the player's arena card + +; can't damage + ld a, DUELVARS_ARENA_CARD_HP + call GetNonTurnDuelistVariable + pop af + ld [hl], a + ld a, DUELVARS_ARENA_CARD + call GetNonTurnDuelistVariable + pop af + ld [hl], a + pop hl + pop de + pop bc + or a + ret + +.can_damage + ld a, DUELVARS_ARENA_CARD_HP + call GetNonTurnDuelistVariable + pop af + ld [hl], a + ld a, DUELVARS_ARENA_CARD + call GetNonTurnDuelistVariable + pop af + ld [hl], a + pop hl + pop de + pop bc + scf + ret + +AIPlay_Bill: ; 2086d (8:486d) + ld a, [wAITrainerCardToPlay] + ldh [hTempCardIndex_ff9f], a + ld a, OPPACTION_EXECUTE_TRAINER_EFFECTS + bank1call AIMakeDecision + ret + +; return carry if cards in deck > 9 +AIDecide_Bill: ; 20878 (8:4878) + ld a, DUELVARS_NUMBER_OF_CARDS_NOT_IN_DECK + call GetTurnDuelistVariable + cp DECK_SIZE - 9 + ret + +AIPlay_EnergyRemoval: ; 20880 (8:4880) + ld a, [wAITrainerCardToPlay] + ldh [hTempCardIndex_ff9f], a + ld a, [wAITrainerCardParameter] + ldh [hTemp_ffa0], a + ld a, [wce1a] + ldh [hTempPlayAreaLocation_ffa1], a + ld a, OPPACTION_EXECUTE_TRAINER_EFFECTS + bank1call AIMakeDecision + ret + +; picks an energy card in the player's Play Area to remove +AIDecide_EnergyRemoval: ; 20895 (8:4895) +; check if the current active card can KO player's card +; if it's possible to KO, then do not consider the player's +; active card to remove its attached energy + xor a ; PLAY_AREA_ARENA + ldh [hTempPlayAreaLocation_ff9d], a + farcall CheckIfAnyAttackKnocksOutDefendingCard + jr nc, .cannot_ko + farcall CheckIfSelectedAttackIsUnusable + jr nc, .can_ko + farcall LookForEnergyNeededForAttackInHand + jr nc, .cannot_ko + +.can_ko + ; start checking from the bench + ld a, PLAY_AREA_BENCH_1 + ld [wce0f], a + jr .check_bench_energy +.cannot_ko + ; start checking from the arena card + xor a ; PLAY_AREA_ARENA + ld [wce0f], a + +; loop each card and check if it has enough energy to use any attack +; if it does, then proceed to pick an energy card to remove +.check_bench_energy + call SwapTurn + ld a, [wce0f] + ld e, a +.loop_1 + ld a, DUELVARS_ARENA_CARD + add e + call GetTurnDuelistVariable + cp $ff + jr z, .default + + ld d, a + call .CheckIfCardHasEnergyAttached + jr nc, .next_1 + call .CheckIfNotEnoughEnergyToAttack + jr nc, .pick_energy ; jump if enough energy to attack +.next_1 + inc e + jr .loop_1 + +.pick_energy +; a play area card was picked to remove energy +; store the picked energy card to remove in wce1a +; and set carry + ld a, e + push af + call PickAttachedEnergyCardToRemove + ld [wce1a], a + pop af + call SwapTurn + scf + ret + +; if no card in player's Play Area was found with enough energy +; to attack, just pick an energy card from player's active card +; (in case the AI cannot KO it this turn) +.default + ld a, [wce0f] + or a + jr nz, .check_bench_damage ; not active card + call .CheckIfCardHasEnergyAttached + jr c, .pick_energy + +; lastly, check what attack on player's Play Area is highest damaging +; and pick an energy card attached to that Pokemon to remove +.check_bench_damage + xor a + ld [wce06], a + ld [wce08], a + + ld e, PLAY_AREA_BENCH_1 +.loop_2 + ld a, DUELVARS_ARENA_CARD + add e + call GetTurnDuelistVariable + cp $ff + jr z, .found_damage + + ld d, a + call .CheckIfCardHasEnergyAttached + jr nc, .next_2 + call .FindHighestDamagingAttack +.next_2 + inc e + jr .loop_2 + +.found_damage + ld a, [wce08] + or a + jr z, .no_carry ; skip if none found + ld e, a + jr .pick_energy +.no_carry + call SwapTurn + or a + ret + +; returns carry if this card has any energy cards attached +.CheckIfCardHasEnergyAttached ; 2091a (8:491a) + call GetPlayAreaCardAttachedEnergies + ld a, [wTotalAttachedEnergies] + or a + ret z + scf + ret + +; returns carry if this card does not +; have enough energy for either of its attacks +.CheckIfNotEnoughEnergyToAttack ; 20924 (8:4924) + push de + xor a ; FIRST_ATTACK_OR_PKMN_POWER + ld [wSelectedAttack], a + ld a, e + ldh [hTempPlayAreaLocation_ff9d], a + farcall CheckEnergyNeededForAttack + jr nc, .enough_energy + pop de + + push de + ld a, SECOND_ATTACK + ld [wSelectedAttack], a + ld a, e + ldh [hTempPlayAreaLocation_ff9d], a + farcall CheckEnergyNeededForAttack + jr nc, .check_surplus + pop de + +; neither attack has enough energy + scf + ret + +.enough_energy + pop de + or a + ret + +; first attack doesn't have enough energy (or is just a Pokemon Power) +; but second attack has enough energy to be used +; check if there's surplus energy for attack and, if so, return carry +.check_surplus + farcall CheckIfNoSurplusEnergyForAttack + pop de + ccf + ret + +; stores in wce06 the highest damaging attack +; for the card in play area location in e +; and stores this card's location in wce08 +.FindHighestDamagingAttack ; 2094f (8:494f) + push de + ld a, e + ldh [hTempPlayAreaLocation_ff9d], a + + xor a ; FIRST_ATTACK_OR_PKMN_POWER + farcall EstimateDamage_VersusDefendingCard + ld a, [wDamage] + or a + jr z, .skip_1 + ld e, a + ld a, [wce06] + cp e + jr nc, .skip_1 + ld a, e + ld [wce06], a ; store this damage value + pop de + ld a, e + ld [wce08], a ; store this location + jr .second_attack + +.skip_1 + pop de + +.second_attack + push de + ld a, e + ldh [hTempPlayAreaLocation_ff9d], a + + ld a, SECOND_ATTACK + farcall EstimateDamage_VersusDefendingCard + ld a, [wDamage] + or a + jr z, .skip_2 + ld e, a + ld a, [wce06] + cp e + jr nc, .skip_2 + ld a, e + ld [wce06], a ; store this damage value + pop de + ld a, e + ld [wce08], a ; store this location + ret +.skip_2 + pop de + ret + +AIPlay_SuperEnergyRemoval: ; 20994 (8:4994) + ld a, [wAITrainerCardToPlay] + ldh [hTempCardIndex_ff9f], a + ld a, [wAITrainerCardParameter] + ldh [hTemp_ffa0], a + ld a, [wce1a] + ldh [hTempPlayAreaLocation_ffa1], a + ld a, [wce1b] + ldh [hTempRetreatCostCards], a + ld a, [wce1c] + ldh [hTempRetreatCostCards + 1], a + ld a, [wce1d] + ldh [hTempRetreatCostCards + 2], a + ld a, $ff + ldh [hTempRetreatCostCards + 3], a + ld a, OPPACTION_EXECUTE_TRAINER_EFFECTS + bank1call AIMakeDecision + ret + +; picks two energy cards in the player's Play Area to remove +AIDecide_SuperEnergyRemoval: ; 209bc (8:49bc) + ld e, PLAY_AREA_BENCH_1 +.loop_1 +; first find an Arena card with a color energy card +; to discard for card effect +; return immediately if no Arena cards + ld a, DUELVARS_ARENA_CARD + add e + call GetTurnDuelistVariable + cp $ff + jr z, .exit + + ld d, a + push de + call .LookForNonDoubleColorless + pop de + jr c, .not_double_colorless + inc e + jr .loop_1 + +; returns carry if an energy card other than double colorless +; is found attached to the card in play area location e +.LookForNonDoubleColorless + ld a, e + call CreateArenaOrBenchEnergyCardList + ld hl, wDuelTempList +.loop_2 + ld a, [hli] + cp $ff + ret z + call LoadCardDataToBuffer1_FromDeckIndex + cp DOUBLE_COLORLESS_ENERGY + ; any basic energy card + ; will set carry flag here + jr nc, .loop_2 + ret + +.exit + or a + ret + +; card in Play Area location e was found with +; a basic energy card +.not_double_colorless + ld a, e + ld [wce0f], a + +; check if the current active card can KO player's card +; if it's possible to KO, then do not consider the player's +; active card to remove its attached energy + xor a ; PLAY_AREA_ARENA + ldh [hTempPlayAreaLocation_ff9d], a + farcall CheckIfAnyAttackKnocksOutDefendingCard + jr nc, .cannot_ko + farcall CheckIfSelectedAttackIsUnusable + jr nc, .can_ko + farcall LookForEnergyNeededForAttackInHand + jr nc, .cannot_ko + +.can_ko + ; start checking from the bench + call SwapTurn + ld e, PLAY_AREA_BENCH_1 + jr .loop_3 +.cannot_ko + ; start checking from the arena card + call SwapTurn + ld e, PLAY_AREA_ARENA + +; loop each card and check if it has enough energy to use any attack +; if it does, then proceed to pick energy cards to remove +.loop_3 + ld a, DUELVARS_ARENA_CARD + add e + call GetTurnDuelistVariable + cp $ff + jr z, .no_carry + + ld d, a + call .CheckIfFewerThanTwoEnergyCards + jr c, .next_1 + call .CheckIfNotEnoughEnergyToAttack + jr nc, .found_card ; jump if enough energy to attack +.next_1 + inc e + jr .loop_3 + +.found_card +; a play area card was picked to remove energy +; if this is not the Arena Card, then check +; entire bench to pick the highest damage + ld a, e + or a + jr nz, .check_bench_damage + +; store the picked energy card to remove in wce1a +; and set carry +.pick_energy + ld [wce1b], a + call PickTwoAttachedEnergyCards + ld [wce1c], a + ld a, b + ld [wce1d], a + call SwapTurn + ld a, [wce0f] + push af + call AIPickEnergyCardToDiscard + ld [wce1a], a + pop af + scf + ret + +; check what attack on player's Play Area is highest damaging +; and pick an energy card attached to that Pokemon to remove +.check_bench_damage + xor a + ld [wce06], a + ld [wce08], a + + ld e, PLAY_AREA_BENCH_1 +.loop_4 + ld a, DUELVARS_ARENA_CARD + add e + call GetTurnDuelistVariable + cp $ff + jr z, .found_damage + + ld d, a + call .CheckIfFewerThanTwoEnergyCards + jr c, .next_2 + call .CheckIfNotEnoughEnergyToAttack + jr c, .next_2 + call .FindHighestDamagingAttack +.next_2 + inc e + jr .loop_4 + +.found_damage + ld a, [wce08] + or a + jr z, .no_carry + jr .pick_energy +.no_carry + call SwapTurn + or a + ret + +; returns carry if the number of energy cards attached +; is fewer than 2, or if all energy combined yields +; fewer than 2 energy +.CheckIfFewerThanTwoEnergyCards ; 20a77 (8:4a77) + call GetPlayAreaCardAttachedEnergies + ld a, [wTotalAttachedEnergies] + cp 2 + ret c ; return if fewer than 2 attached cards + +; count all energy attached +; i.e. colored energy card = 1 +; and double colorless energy card = 2 + xor a + ld b, NUM_COLORED_TYPES + ld hl, wAttachedEnergies +.loop_5 + add [hl] + inc hl + dec b + jr nz, .loop_5 + ld b, [hl] + srl b + add b + cp 2 + ret + +; returns carry if this card does not +; have enough energy for either of its attacks +.CheckIfNotEnoughEnergyToAttack ; 20a92 (8:4a92) + push de + xor a ; FIRST_ATTACK_OR_PKMN_POWER + ld [wSelectedAttack], a + ld a, e + ldh [hTempPlayAreaLocation_ff9d], a + farcall CheckEnergyNeededForAttack + jr nc, .enough_energy + pop de + + push de + ld a, SECOND_ATTACK + ld [wSelectedAttack], a + ld a, e + ldh [hTempPlayAreaLocation_ff9d], a + farcall CheckEnergyNeededForAttack + jr nc, .check_surplus + pop de + +; neither attack has enough energy + scf + ret + +.enough_energy + pop de + or a + ret + +; first attack doesn't have enough energy (or is just a Pokemon Power) +; but second attack has enough energy to be used +; check if there's surplus energy for attack and, if so, +; return carry if this surplus energy is at least 2 +.check_surplus + farcall CheckIfNoSurplusEnergyForAttack + cp 2 + jr c, .enough_energy + pop de + scf + ret + +; stores in wce06 the highest damaging attack +; for the card in play area location in e +; and stores this card's location in wce08 +.FindHighestDamagingAttack ; 20ac1 (8:4ac1) + push de + ld a, e + ldh [hTempPlayAreaLocation_ff9d], a + + xor a ; FIRST_ATTACK_OR_PKMN_POWER + farcall EstimateDamage_VersusDefendingCard + ld a, [wDamage] + or a + jr z, .skip_1 + ld e, a + ld a, [wce06] + cp e + jr nc, .skip_1 + ld a, e + ld [wce06], a ; store this damage value + pop de + ld a, e + ld [wce08], a ; store this location + jr .second_attack + +.skip_1 + pop de + +.second_attack + push de + ld a, e + ldh [hTempPlayAreaLocation_ff9d], a + + ld a, SECOND_ATTACK + farcall EstimateDamage_VersusDefendingCard + ld a, [wDamage] + or a + jr z, .skip_2 + ld e, a + ld a, [wce06] + cp e + jr nc, .skip_2 + ld a, e + ld [wce06], a ; store this damage value + pop de + ld a, e + ld [wce08], a ; store this location + ret +.skip_2 + pop de + ret + +AIPlay_PokemonBreeder: ; 20b06 (8:4b06) + ld a, [wAITrainerCardToPlay] + ldh [hTempCardIndex_ff9f], a + ld a, [wAITrainerCardParameter] + ldh [hTempPlayAreaLocation_ffa1], a + ld a, [wce1a] + ldh [hTemp_ffa0], a + ld a, OPPACTION_EXECUTE_TRAINER_EFFECTS + bank1call AIMakeDecision + ret + +AIDecide_PokemonBreeder: ; 20b1b (8:4b1b) + call IsPrehistoricPowerActive + jp c, .done + + ld a, 7 + ld hl, wce08 + call ClearMemory_Bank8 + + xor a + ld [wce06], a + call CreateHandCardList + ld hl, wDuelTempList + +.loop_hand_1 + ld a, [hli] + cp $ff + jr z, .not_found_in_hand + +; check if card in hand is any of the following +; stage 2 Pokemon cards + ld d, a + call LoadCardDataToBuffer1_FromDeckIndex + cp VENUSAUR1 + jr z, .found + cp VENUSAUR2 + jr z, .found + cp BLASTOISE + jr z, .found + cp VILEPLUME + jr z, .found + cp ALAKAZAM + jr z, .found + cp GENGAR + jr nz, .loop_hand_1 + +.found + ld a, DUELVARS_NUMBER_OF_POKEMON_IN_PLAY_AREA + push hl + call GetTurnDuelistVariable + pop hl + ld c, a + ld e, PLAY_AREA_ARENA + +; check Play Area for card that can evolve into +; the picked stage 2 Pokemon +.loop_play_area_1 + push hl + push bc + push de + call CheckIfCanEvolveInto_BasicToStage2 + pop de + call nc, .can_evolve + pop bc + pop hl + inc e + dec c + jr nz, .loop_play_area_1 + jr .loop_hand_1 + +.can_evolve + ld a, DUELVARS_ARENA_CARD_HP + add e + call GetTurnDuelistVariable + call ConvertHPToCounters + swap a + ld b, a + +; count number of energy cards attached and keep +; the lowest 4 bits (capped at $0f) + call GetPlayAreaCardAttachedEnergies + ld a, [wTotalAttachedEnergies] + cp $10 + jr c, .not_maxed_out + ld a, %00001111 +.not_maxed_out + or b + +; 4 high bits of a = HP counters Pokemon still has +; 4 low bits of a = number of energy cards attached + +; store this score in wce08 + PLAY_AREA* + ld hl, wce08 + ld c, e + ld b, $00 + add hl, bc + ld [hl], a + +; store the deck index of stage 2 Pokemon in wce0f + PLAY_AREA* + ld hl, wce0f + add hl, bc + ld [hl], d + +; increase wce06 by one + ld hl, wce06 + inc [hl] + ret + +.not_found_in_hand + ld a, [wce06] + or a + jr z, .check_evolution_and_dragonite + +; an evolution has been found before + xor a + ld [wce06], a + ld a, DUELVARS_NUMBER_OF_POKEMON_IN_PLAY_AREA + call GetTurnDuelistVariable + ld c, a + ld e, $00 + ld d, $00 + +; find highest score in wce08 +.loop_score_1 + ld hl, wce08 + add hl, de + ld a, [wce06] + cp [hl] + jr nc, .not_higher + +; store this score to wce06 + ld a, [hl] + ld [wce06], a +; store this PLay Area location to wce07 + ld a, e + ld [wce07], a + +.not_higher + inc e + dec c + jr nz, .loop_score_1 + +; store the deck index of the stage 2 card +; that has been decided in wce1a, +; return the Play Area location of card +; to evolve in a and return carry + ld a, [wce07] + ld e, a + ld hl, wce0f + add hl, de + ld a, [hl] + ld [wce1a], a + ld a, [wce07] + scf + ret + +.check_evolution_and_dragonite + ld a, 7 + ld hl, wce08 + call ClearMemory_Bank8 + + xor a + ld [wce06], a + call CreateHandCardList + ld hl, wDuelTempList + push hl + +.loop_hand_2 + pop hl + ld a, [hli] + cp $ff + jr z, .check_evolution_found + + push hl + ld d, a + ld a, DUELVARS_NUMBER_OF_POKEMON_IN_PLAY_AREA + call GetTurnDuelistVariable + ld c, a + ld e, PLAY_AREA_ARENA + +.loop_play_area_2 +; check if evolution is possible + push bc + push de + call CheckIfCanEvolveInto_BasicToStage2 + pop de + call nc, .HandleDragonite1Evolution + call nc, .can_evolve + +; not possible to evolve or returned carry +; when handling Dragonite1 evolution + pop bc + inc e + dec c + jr nz, .loop_play_area_2 + jr .loop_hand_2 + +.check_evolution_found + ld a, [wce06] + or a + jr nz, .evolution_was_found +; no evolution was found before + or a + ret + +.evolution_was_found + xor a + ld [wce06], a + ld a, $ff + ld [wce07], a + + ld a, DUELVARS_NUMBER_OF_POKEMON_IN_PLAY_AREA + call GetTurnDuelistVariable + ld c, a + ld e, $00 + ld d, $00 + +; find highest score in wce08 with at least +; 2 energy cards attached +.loop_score_2 + ld hl, wce08 + add hl, de + ld a, [wce06] + cp [hl] + jr nc, .next_score + +; take the lower 4 bits (total energy cards) +; and skip if less than 2 + ld a, [hl] + ld b, a + and %00001111 + cp 2 + jr c, .next_score + +; has at least 2 energy cards +; store the score in wce06 + ld a, b + ld [wce06], a +; store this PLay Area location to wce07 + ld a, e + ld [wce07], a + +.next_score + inc e + dec c + jr nz, .loop_score_2 + + ld a, [wce07] + cp $ff + jr z, .done + +; a card to evolve was found +; store the deck index of the stage 2 card +; that has been decided in wce1a, +; return the Play Area location of card +; to evolve in a and return carry + ld e, a + ld hl, wce0f + add hl, de + ld a, [hl] + ld [wce1a], a + ld a, [wce07] + scf + ret + +.done + or a + ret + +; return carry if card is evolving to Dragonite1 and if +; - the card that is evolving is not Arena card and +; number of damage counters in Play Area is under 8; +; - the card that is evolving is Arena card and has under 5 +; damage counters or has less than 3 energy cards attached. +.HandleDragonite1Evolution ; 20c5c (8:4c5c) + push af + push bc + push de + push hl + push de + +; check card ID + ld a, d + call GetCardIDFromDeckIndex + ld a, e + pop de + cp DRAGONITE1 + jr nz, .no_carry + +; check card Play Area location + ld a, e + or a + jr z, .active_card_dragonite + +; the card that is evolving is not active card + ld a, DUELVARS_NUMBER_OF_POKEMON_IN_PLAY_AREA + call GetTurnDuelistVariable + ld b, a + ld c, 0 + +; count damage counters in Play Area +.loop_play_area_damage + dec b + ld e, b + push bc + call GetCardDamageAndMaxHP + pop bc + call ConvertHPToCounters + add c + ld c, a + + ld a, b + or a + jr nz, .loop_play_area_damage + +; compare number of total damage counters +; with 7, if less or equal to that, set carry + ld a, 7 + cp c + jr c, .no_carry + jr .set_carry + +.active_card_dragonite +; the card that is evolving is active card +; compare number of this card's damage counters +; with 5, if less than that, set carry + ld e, PLAY_AREA_ARENA + call GetCardDamageAndMaxHP + cp 5 + jr c, .set_carry + +; compare number of this card's attached energy cards +; with 3, if less than that, set carry + ld e, PLAY_AREA_ARENA + farcall CountNumberOfEnergyCardsAttached + cp 3 + jr c, .set_carry + jr .no_carry + +.no_carry + pop hl + pop de + pop bc + pop af + ret + +.set_carry + pop hl + pop de + pop bc + pop af + scf + ret + +AIPlay_ProfessorOak: ; 20cae (8:4cae) + ld a, [wCurrentAIFlags] + or AI_FLAG_USED_PROFESSOR_OAK | AI_FLAG_MODIFIED_HAND + ld [wCurrentAIFlags], a + ld a, [wAITrainerCardToPlay] + ldh [hTempCardIndex_ff9f], a + ld a, OPPACTION_EXECUTE_TRAINER_EFFECTS + bank1call AIMakeDecision + ret + +; sets carry if AI determines a score of playing +; Professor Oak is over a certain threshold. +AIDecide_ProfessorOak: ; 20cc1 (8:4cc1) +; return if cards in deck <= 6 + ld a, DUELVARS_NUMBER_OF_CARDS_NOT_IN_DECK + call GetTurnDuelistVariable + cp DECK_SIZE - 6 + ret nc + + ld a, [wOpponentDeckID] + cp LEGENDARY_ARTICUNO_DECK_ID + jp z, .HandleLegendaryArticunoDeck + cp EXCAVATION_DECK_ID + jp z, .HandleExcavationDeck + cp WONDERS_OF_SCIENCE_DECK_ID + jp z, .HandleWondersOfScienceDeck + +; return if cards in deck <= 14 +.check_cards_deck + ld a, [hl] + cp DECK_SIZE - 14 + ret nc + +; initialize score + ld a, $1e + ld [wce06], a + +; check number of cards in hand +.check_cards_hand + ld a, DUELVARS_NUMBER_OF_CARDS_IN_HAND + call GetTurnDuelistVariable + cp 4 + jr nc, .more_than_3_cards + +; less than 4 cards in hand + ld a, [wce06] + add $32 + ld [wce06], a + jr .check_energy_cards + +.more_than_3_cards + cp 9 + jr c, .check_energy_cards + +; more than 8 cards + ld a, [wce06] + sub $1e + ld [wce06], a + +.check_energy_cards + farcall CreateEnergyCardListFromHand + jr nc, .handle_blastoise + +; no energy cards in hand + ld a, [wce06] + add $28 + ld [wce06], a + +.handle_blastoise + ld a, MUK + call CountPokemonIDInBothPlayAreas + jr c, .check_hand + +; no Muk in Play Area + ld a, BLASTOISE + call CountPokemonIDInPlayArea + jr nc, .check_hand + +; at least one Blastoise in AI Play Area + ld a, WATER_ENERGY + farcall LookForCardIDInHand + jr nc, .check_hand + +; no Water energy in hand + ld a, [wce06] + add $0a + ld [wce06], a + +; this part seems buggy +; the AI loops through all the cards in hand and checks +; if any of them is not a Pokemon card and has Basic stage. +; it seems like the intention was that if there was +; any Basic Pokemon still in hand, the AI would add to the score. +.check_hand + call CreateHandCardList + ld hl, wDuelTempList +.loop_hand + ld a, [hli] + cp $ff + jr z, .check_evolution + + call LoadCardDataToBuffer1_FromDeckIndex + ld a, [wLoadedCard1Type] + cp TYPE_ENERGY + jr c, .loop_hand ; bug, should be jr nc + + ld a, [wLoadedCard1Stage] + or a + jr nz, .loop_hand + + ld a, [wce06] + add $0a + ld [wce06], a + +.check_evolution + xor a + ld [wce0f], a + ld [wce0f + 1], a + + ld a, DUELVARS_NUMBER_OF_POKEMON_IN_PLAY_AREA + call GetTurnDuelistVariable + ld d, a + ld e, PLAY_AREA_ARENA + +.loop_play_area + push de + call .LookForEvolution + pop de + jr nc, .not_in_hand + +; there's a card in hand that can evolve + ld a, $01 + ld [wce0f], a + +.not_in_hand +; check if a card that can evolve was found at all +; if not, go to the next card in the Play Area + ld a, [wce08] + cp $01 + jr nz, .next_play_area + +; if it was found, set wce0f + 1 to $01 + ld a, $01 + ld [wce0f + 1], a + +.next_play_area + inc e + dec d + jr nz, .loop_play_area + +; if a card was found that evolves... + ld a, [wce0f + 1] + or a + jr z, .check_score + +; ...but that card is not in the hand... + ld a, [wce0f] + or a + jr nz, .check_score + +; ...add to the score + ld a, [wce06] + add $0a + ld [wce06], a + +; only return carry if score > $3c +.check_score + ld a, [wce06] + ld b, $3c + cp b + jr nc, .set_carry + or a + ret + +.set_carry + scf + ret + +; return carry if there's a card in the hand that +; can evolve the card in Play Area location in e. +; sets wce08 to $01 if any card is found that can +; evolve regardless of card location. +.LookForEvolution ; 20d9d (8:4d9d) + xor a + ld [wce08], a + ld d, 0 + +; loop through the whole deck to check if there's +; a card that can evolve this Pokemon. +.loop_deck_evolution + push de + call CheckIfCanEvolveInto + pop de + jr nc, .can_evolve +.evolution_not_in_hand + inc d + ld a, DECK_SIZE + cp d + jr nz, .loop_deck_evolution + + or a + ret + +; a card was found that can evolve, set wce08 to $01 +; and if the card is in the hand, return carry. +; otherwise resume looping through deck. +.can_evolve + ld a, $01 + ld [wce08], a + ld a, DUELVARS_CARD_LOCATIONS + add d + call GetTurnDuelistVariable + cp CARD_LOCATION_HAND + jr nz, .evolution_not_in_hand + + scf + ret + +; handles Legendary Articuno Deck AI logic. +.HandleLegendaryArticunoDeck ; 20dc3 (8:4dc3) + ld a, DUELVARS_NUMBER_OF_POKEMON_IN_PLAY_AREA + call GetTurnDuelistVariable + cp 3 + jr nc, .check_playable_cards + +; has less than 3 Pokemon in Play Area. + push af + call CreateHandCardList + pop af + ld d, a + ld e, PLAY_AREA_ARENA + +; if no cards in hand evolve cards in Play Area, +; returns carry. +.loop_play_area_articuno + ld a, DUELVARS_ARENA_CARD + add e + + push de + call GetTurnDuelistVariable + farcall CheckForEvolutionInList + pop de + jr c, .check_playable_cards + + inc e + ld a, d + cp e + jr nz, .loop_play_area_articuno + +.set_carry_articuno + scf + ret + +; if there are more than 3 energy cards in hand, +; return no carry, otherwise check for playable cards. +.check_playable_cards + call CountOppEnergyCardsInHand + cp 4 + jr nc, .no_carry_articuno + +; remove both Professor Oak cards from list +; before checking for playable cards + call CreateHandCardList + ld hl, wDuelTempList + ld e, PROFESSOR_OAK + farcall RemoveCardIDInList + ld e, PROFESSOR_OAK + farcall RemoveCardIDInList + +; look in hand for cards that can be played. +; if a card that cannot be played is found, return no carry. +; otherwise return carry. +.loop_hand_articuno + ld a, [hli] + cp $ff + jr z, .set_carry_articuno + push hl + farcall CheckIfCardCanBePlayed + pop hl + jr c, .loop_hand_articuno + +.no_carry_articuno + or a + ret + +; handles Excavation deck AI logic. +; sets score depending on whether there's no +; Mysterious Fossil in play and in hand. +.HandleExcavationDeck ; 20e11 (8:4e11) +; return no carry if cards in deck < 15 + ld a, [hl] + cp 46 + ret nc + +; look for Mysterious Fossil + ld a, MYSTERIOUS_FOSSIL + call LookForCardIDInHandAndPlayArea + jr c, .found_mysterious_fossil + ld a, $50 + ld [wce06], a + jp .check_cards_hand +.found_mysterious_fossil + ld a, $1e + ld [wce06], a + jp .check_cards_hand + +; handles Wonders of Science AI logic. +; if there's either Grimer or Muk in hand, +; do not play Professor Oak. +.HandleWondersOfScienceDeck ; 20e2c (8:4e2c) + ld a, GRIMER + call LookForCardIDInHandList_Bank8 + jr c, .found_grimer_or_muk + ld a, MUK + call LookForCardIDInHandList_Bank8 + jr c, .found_grimer_or_muk + + ld a, DUELVARS_NUMBER_OF_CARDS_NOT_IN_DECK + call GetTurnDuelistVariable + jp .check_cards_deck + +.found_grimer_or_muk + or a + ret + +AIPlay_EnergyRetrieval: ; 20e44 (8:4e44) + ld a, [wCurrentAIFlags] + or AI_FLAG_MODIFIED_HAND + ld [wCurrentAIFlags], a + ld a, [wAITrainerCardToPlay] + ldh [hTempCardIndex_ff9f], a + ld a, [wAITrainerCardParameter] + ldh [hTemp_ffa0], a + ld a, [wce1a] + ldh [hTempPlayAreaLocation_ffa1], a + ld a, [wce1b] + ldh [hTempRetreatCostCards], a + cp $ff + jr z, .asm_20e68 + ld a, $ff + ldh [$ffa3], a +.asm_20e68 + ld a, OPPACTION_EXECUTE_TRAINER_EFFECTS + bank1call AIMakeDecision + ret + +; checks whether AI can play Energy Retrieval and +; picks the energy cards from the discard pile, +; and duplicate cards in hand to discard. +AIDecide_EnergyRetrieval: ; 20e6e (8:4e6e) +; return no carry if no cards in hand + farcall CreateEnergyCardListFromHand + jp nc, .no_carry + +; handle Go Go Rain Dance deck +; return no carry if there's no Muk card in play and +; if there's no Blastoise card in Play Area +; if there's a Muk in play, continue as normal + ld a, [wOpponentDeckID] + cp GO_GO_RAIN_DANCE_DECK_ID + jr nz, .start + ld a, MUK + call CountPokemonIDInBothPlayAreas + jr c, .start + ld a, BLASTOISE + call CountPokemonIDInPlayArea + jp nc, .no_carry + +.start +; find duplicate cards in hand + call CreateHandCardList + ld hl, wDuelTempList + call FindDuplicateCards + jp c, .no_carry + + ld [wce06], a + ld a, CARD_LOCATION_DISCARD_PILE + call FindBasicEnergyCardsInLocation + jp c, .no_carry + +; some basic energy cards were found in Discard Pile + ld a, $ff + ld [wce1a], a + ld [wce1b], a + ld [wce1c], a + + ld a, DUELVARS_NUMBER_OF_POKEMON_IN_PLAY_AREA + call GetTurnDuelistVariable + ld d, a + ld e, PLAY_AREA_ARENA + +; first check if there are useful energy cards in the list +; and choose them for retrieval first +.loop_play_area + ld a, DUELVARS_ARENA_CARD + add e + push de + +; load this card's ID in wTempCardID +; and this card's Type in wTempCardType + call GetTurnDuelistVariable + call GetCardIDFromDeckIndex + ld a, e + ld [wTempCardID], a + call LoadCardDataToBuffer1_FromCardID + pop de + ld a, [wLoadedCard1Type] + or TYPE_ENERGY + ld [wTempCardType], a + +; loop the energy cards in the Discard Pile +; and check if they are useful for this Pokemon + ld hl, wDuelTempList +.loop_energy_cards_1 + ld a, [hli] + cp $ff + jr z, .next_play_area + + ld b, a + push hl + farcall CheckIfEnergyIsUseful + pop hl + jr nc, .loop_energy_cards_1 + + ld a, [wce1a] + cp $ff + jr nz, .second_energy_1 + +; check if there were already chosen cards, +; if this is the second chosen card, return carry + +; first energy card found + ld a, b + ld [wce1a], a + call RemoveCardFromList + jr .next_play_area +.second_energy_1 + ld a, b + ld [wce1b], a + jr .set_carry + +.next_play_area + inc e + dec d + jr nz, .loop_play_area + +; next, if there are still energy cards left to choose, +; loop through the energy cards again and select +; them in order. + ld hl, wDuelTempList +.loop_energy_cards_2 + ld a, [hli] + cp $ff + jr z, .check_chosen + ld b, a + ld a, [wce1a] + cp $ff + jr nz, .second_energy_2 + ld a, b + ld [wce1a], a + call RemoveCardFromList + jr .loop_energy_cards_2 + +.second_energy_2 + ld a, b + ld [wce1b], a + jr .set_carry + +; will set carry if at least one has been chosen +.check_chosen + ld a, [wce1a] + cp $ff + jr nz, .set_carry +.no_carry + or a + ret + +.set_carry + ld a, [wce06] + scf + ret + +; remove an element from the list +; and shortens it accordingly +; input: +; hl = pointer to element after the one to remove +RemoveCardFromList: ; 20f27 (8:4f27) + push de + ld d, h + ld e, l + dec hl + push hl +.loop_remove + ld a, [de] + ld [hli], a + cp $ff + jr z, .done_remove + inc de + jr .loop_remove +.done_remove + pop hl + pop de + ret + +; finds duplicates in card list in hl. +; if a duplicate of Pokemon cards are found, return in +; a the deck index of the second one. +; otherwise, if a duplicate of non-Pokemon cards are found +; return in a the deck index of the second one. +; if no duplicates found, return carry. +; input: +; hl = list to look in +; output: +; a = deck index of duplicate card +FindDuplicateCards: ; 20f38 (8:4f38) + ld a, $ff + ld [wce0f], a + ld [wce0f + 1], a + push hl + +.loop_outer +; get ID of current card + pop hl + ld a, [hli] + cp $ff + jr z, .check_found + call GetCardIDFromDeckIndex + ld b, e + push hl + +; loop the rest of the list to find +; another card with the same ID +.loop_inner + ld a, [hli] + cp $ff + jr z, .loop_outer + ld c, a + call GetCardIDFromDeckIndex + ld a, e + cp b + jr nz, .loop_inner + +; found two cards with same ID + push bc + call GetCardType + pop bc + cp TYPE_ENERGY + jr c, .not_energy + +; they are energy or trainer cards +; loads wce0f+1 with this card deck index + ld a, c + ld [wce0f + 1], a + jr .loop_outer + +.not_energy +; they are Pokemon cards +; loads wce0f with this card deck index + ld a, c + ld [wce0f], a + jr .loop_outer + +.check_found + ld a, [wce0f] + cp $ff + jr nz, .no_carry + ld a, [wce0f + 1] + cp $ff + jr nz, .no_carry + +; only set carry if duplicate cards were not found + scf + ret + +.no_carry +; two cards with the same ID were found +; of either Pokemon or Non-Pokemon cards + or a + ret + +AIPlay_SuperEnergyRetrieval: ; 20f80 (8:4f80) + ld a, [wCurrentAIFlags] + or AI_FLAG_MODIFIED_HAND + ld [wCurrentAIFlags], a + ld a, [wAITrainerCardToPlay] + ldh [hTempCardIndex_ff9f], a + ld a, [wAITrainerCardParameter] + ldh [hTemp_ffa0], a + ld a, [wce1a] + ldh [hTempPlayAreaLocation_ffa1], a + ld a, [wce1b] + ldh [hTempRetreatCostCards], a + ld a, [wce1c] + ldh [$ffa3], a + cp $ff + jr z, .asm_20fbb + ld a, [wce1d] + ldh [$ffa4], a + cp $ff + jr z, .asm_20fbb + ld a, [wce1e] + ldh [$ffa5], a + cp $ff + jr z, .asm_20fbb + ld a, $ff + ldh [$ffa6], a +.asm_20fbb + ld a, OPPACTION_EXECUTE_TRAINER_EFFECTS + bank1call AIMakeDecision + ret + +AIDecide_SuperEnergyRetrieval: ; 20fc1 (8:4fc1) +; return no carry if no cards in hand + farcall CreateEnergyCardListFromHand + jp nc, .no_carry + +; handle Go Go Rain Dance deck +; return no carry if there's no Muk card in play and +; if there's no Blastoise card in Play Area +; if there's a Muk in play, continue as normal + ld a, [wOpponentDeckID] + cp GO_GO_RAIN_DANCE_DECK_ID + jr nz, .start + ld a, MUK + call CountPokemonIDInBothPlayAreas + jr c, .start + ld a, BLASTOISE + call CountPokemonIDInPlayArea + jp nc, .no_carry + +.start +; find duplicate cards in hand + call CreateHandCardList + ld hl, wDuelTempList + call FindDuplicateCards + jp c, .no_carry + +; remove the duplicate card in hand +; and run the hand check again + ld [wce06], a + ld hl, wDuelTempList + call FindAndRemoveCardFromList + call FindDuplicateCards + jp c, .no_carry + + ld [wce08], a + ld a, CARD_LOCATION_DISCARD_PILE + call FindBasicEnergyCardsInLocation + jp c, .no_carry + +; some basic energy cards were found in Discard Pile + ld a, $ff + ld [wce1b], a + ld [wce1c], a + ld [wce1d], a + ld [wce1e], a + ld [wce1f], a + + ld a, DUELVARS_NUMBER_OF_POKEMON_IN_PLAY_AREA + call GetTurnDuelistVariable + ld d, a + ld e, PLAY_AREA_ARENA + +; first check if there are useful energy cards in the list +; and choose them for retrieval first +.loop_play_area + ld a, DUELVARS_ARENA_CARD + add e + push de + +; load this card's ID in wTempCardID +; and this card's Type in wTempCardType + call GetTurnDuelistVariable + call GetCardIDFromDeckIndex + ld a, e + ld [wTempCardID], a + call LoadCardDataToBuffer1_FromCardID + pop de + ld a, [wLoadedCard1Type] + or TYPE_ENERGY + ld [wTempCardType], a + +; loop the energy cards in the Discard Pile +; and check if they are useful for this Pokemon + ld hl, wDuelTempList +.loop_energy_cards_1 + ld a, [hli] + cp $ff + jr z, .next_play_area + + ld b, a + push hl + farcall CheckIfEnergyIsUseful + pop hl + jr nc, .loop_energy_cards_1 + +; first energy + ld a, [wce1b] + cp $ff + jr nz, .second_energy_1 + ld a, b + ld [wce1b], a + call RemoveCardFromList + jr .next_play_area + +.second_energy_1 + ld a, [wce1c] + cp $ff + jr nz, .third_energy_1 + ld a, b + ld [wce1c], a + call RemoveCardFromList + jr .next_play_area + +.third_energy_1 + ld a, [wce1d] + cp $ff + jr nz, .fourth_energy_1 + ld a, b + ld [wce1d], a + call RemoveCardFromList + jr .next_play_area + +.fourth_energy_1 + ld a, b + ld [wce1e], a + jr .set_carry + +.next_play_area + inc e + dec d + jr nz, .loop_play_area + +; next, if there are still energy cards left to choose, +; loop through the energy cards again and select +; them in order. + ld hl, wDuelTempList +.loop_energy_cards_2 + ld a, [hli] + cp $ff + jr z, .check_chosen + ld b, a + ld a, [wce1b] + cp $ff + jr nz, .second_energy_2 + ld a, b + +; first energy + ld [wce1b], a + call RemoveCardFromList + jr .loop_energy_cards_2 + +.second_energy_2 + ld a, [wce1c] + cp $ff + jr nz, .third_energy_2 + ld a, b + ld [wce1c], a + call RemoveCardFromList + jr .loop_energy_cards_2 + +.third_energy_2 + ld a, [wce1d] + cp $ff + jr nz, .fourth_energy + ld a, b + ld [wce1d], a + call RemoveCardFromList + jr .loop_energy_cards_2 + +.fourth_energy + ld a, b + ld [wce1e], a + jr .set_carry + +; will set carry if at least one has been chosen +.check_chosen + ld a, [wce1b] + cp $ff + jr nz, .set_carry + +.no_carry + or a + ret +.set_carry + ld a, [wce08] + ld [wce1a], a + ld a, [wce06] + scf + ret + +; finds the card with deck index a in list hl, +; and removes it from the list. +; the card HAS to exist in the list, since this +; routine does not check for the terminating byte $ff! +; input: +; a = card deck index to look +; hl = pointer to list of cards +FindAndRemoveCardFromList: ; 210d5 (8:50d5) + push hl + ld b, a +.loop_duplicate + ld a, [hli] + cp b + jr nz, .loop_duplicate + call RemoveCardFromList + pop hl + ret + +AIPlay_PokemonCenter: ; 210e0 (8:50e0) + ld a, [wAITrainerCardToPlay] + ldh [hTempCardIndex_ff9f], a + ld a, OPPACTION_EXECUTE_TRAINER_EFFECTS + bank1call AIMakeDecision + ret + +AIDecide_PokemonCenter: ; 210eb (8:50eb) + xor a + ldh [hTempPlayAreaLocation_ff9d], a + +; return if active Pokemon can KO player's card. + farcall CheckIfAnyAttackKnocksOutDefendingCard + jr nc, .start + farcall CheckIfSelectedAttackIsUnusable + jr nc, .no_carry + farcall LookForEnergyNeededForAttackInHand + jr c, .no_carry + +.start + xor a + ld [wce06], a + ld [wce08], a + ld [wce0f], a + + ld a, DUELVARS_NUMBER_OF_POKEMON_IN_PLAY_AREA + call GetTurnDuelistVariable + ld d, a + ld e, PLAY_AREA_ARENA + +.loop_play_area + ld a, DUELVARS_ARENA_CARD + add e + push de + call GetTurnDuelistVariable + call LoadCardDataToBuffer1_FromDeckIndex + ld a, e ; useless instruction + pop de + +; get this Pokemon's current HP in number of counters +; and add it to the total. + ld a, [wLoadedCard1HP] + call ConvertHPToCounters + ld b, a + ld a, [wce06] + add b + ld [wce06], a + +; get this Pokemon's current damage counters +; and add it to the total. + call GetCardDamageAndMaxHP + call ConvertHPToCounters + ld b, a + ld a, [wce08] + add b + ld [wce08], a + +; get this Pokemon's number of attached energy cards +; and add it to the total. +; if there's overflow, return no carry. + call GetPlayAreaCardAttachedEnergies + ld a, [wTotalAttachedEnergies] + ld b, a + ld a, [wce0f] + add b + jr c, .no_carry + ld [wce0f], a + + inc e + dec d + jr nz, .loop_play_area + +; if (number of damage counters / 2) < (total energy cards attached) +; return no carry. + ld a, [wce08] + srl a + ld hl, wce0f + cp [hl] + jr c, .no_carry + +; if (number of HP counters * 6 / 10) >= (number of damage counters) +; return no carry. + ld a, [wce06] + ld l, a + ld h, 6 + call HtimesL + call CalculateWordTensDigit + ld a, l + ld hl, wce08 + cp [hl] + jr nc, .no_carry + + scf + ret + +.no_carry + or a + ret + +AIPlay_ImposterProfessorOak: ; 21170 (8:5170) + ld a, [wAITrainerCardToPlay] + ldh [hTempCardIndex_ff9f], a + ld a, OPPACTION_EXECUTE_TRAINER_EFFECTS + bank1call AIMakeDecision + ret + +; sets carry depending on player's number of cards +; in deck in in hand. +AIDecide_ImposterProfessorOak: ; 2117b (8:517b) + ld a, DUELVARS_NUMBER_OF_CARDS_NOT_IN_DECK + call GetNonTurnDuelistVariable + cp DECK_SIZE - 14 + jr c, .more_than_14_cards + +; if player has less than 14 cards in deck, only +; set carry if number of cards in their hands < 6 + ld a, DUELVARS_NUMBER_OF_CARDS_IN_HAND + call GetNonTurnDuelistVariable + cp 6 + jr c, .set_carry +.no_carry + or a + ret + +; if player has more than 14 cards in deck, only +; set carry if number of cards in their hands >= 9 +.more_than_14_cards + ld a, DUELVARS_NUMBER_OF_CARDS_IN_HAND + call GetNonTurnDuelistVariable + cp 9 + jr c, .no_carry +.set_carry + scf + ret + +AIPlay_EnergySearch: ; 2119a (8:519a) + ld a, [wAITrainerCardToPlay] + ldh [hTempCardIndex_ff9f], a + ld a, [wAITrainerCardParameter] + ldh [hTemp_ffa0], a + ld a, OPPACTION_EXECUTE_TRAINER_EFFECTS + bank1call AIMakeDecision + ret + +; AI checks for playing Energy Search +AIDecide_EnergySearch: ; 211aa (8:51aa) + farcall CreateEnergyCardListFromHand + jr c, .start + call .CheckForUsefulEnergyCards + jr c, .start + +; there are energy cards in hand and at least +; one of them is useful to a card in Play Area +.no_carry + or a + ret + +.start + ld a, [wOpponentDeckID] + cp HEATED_BATTLE_DECK_ID + jr z, .heated_battle + cp WONDERS_OF_SCIENCE_DECK_ID + jr z, .wonders_of_science + +; if no energy cards in deck, return no carry + ld a, CARD_LOCATION_DECK + call FindBasicEnergyCardsInLocation + jr c, .no_carry + +; if any of the energy cards in deck is useful +; return carry right away... + call .CheckForUsefulEnergyCards + jr c, .no_useful + scf + ret + +; ...otherwise save the list in a before return carry. +.no_useful + ld a, [wDuelTempList] + scf + ret + +; Heated Battle deck only searches for Fire and Lightning +; if they are found to be useful to some card in Play Area +.heated_battle + ld a, CARD_LOCATION_DECK + call FindBasicEnergyCardsInLocation + jr c, .no_carry + call .CheckUsefulFireOrLightningEnergy + jr c, .no_carry + scf + ret + +; this subroutine has a bug. +; it was supposed to use the .CheckUsefulGrassEnergy subroutine +; but uses .CheckUsefulFireOrLightningEnergy instead. +.wonders_of_science + ld a, CARD_LOCATION_DECK + call FindBasicEnergyCardsInLocation + jr c, .no_carry + call .CheckUsefulFireOrLightningEnergy + jr c, .no_carry + scf + ret + +; return carry if cards in wDuelTempList are not +; useful to any of the Play Area Pokemon +.CheckForUsefulEnergyCards ; 211f1 (8:51f1) + ld a, DUELVARS_NUMBER_OF_POKEMON_IN_PLAY_AREA + call GetTurnDuelistVariable + ld d, a + ld e, PLAY_AREA_ARENA + +.loop_play_area_1 + ld a, DUELVARS_ARENA_CARD + add e + push de + call GetTurnDuelistVariable + +; store ID and type of card + call GetCardIDFromDeckIndex + ld a, e + ld [wTempCardID], a + call LoadCardDataToBuffer1_FromCardID + pop de + ld a, [wLoadedCard1Type] + or TYPE_ENERGY + ld [wTempCardType], a + +; look in list for a useful energy, +; is any is found return no carry. + ld hl, wDuelTempList +.loop_energy_1 + ld a, [hli] + cp $ff + jr z, .none_found + ld b, a + push hl + farcall CheckIfEnergyIsUseful + pop hl + jr nc, .loop_energy_1 + + ld a, b + or a + ret + +.none_found + inc e + ld a, e + cp d + jr nz, .loop_play_area_1 + + scf + ret + +; checks whether there are useful energies +; only for Fire and Lightning type Pokemon cards +; in Play Area. If none found, return carry. +.CheckUsefulFireOrLightningEnergy ; 2122e (8:522e) + ld a, DUELVARS_NUMBER_OF_POKEMON_IN_PLAY_AREA + call GetTurnDuelistVariable + ld d, a + ld e, PLAY_AREA_ARENA + +.loop_play_area_2 + ld a, DUELVARS_ARENA_CARD + add e + push de + call GetTurnDuelistVariable + +; get card's ID and Type + call GetCardIDFromDeckIndex + ld a, e + ld [wTempCardID], a + call LoadCardDataToBuffer1_FromCardID + pop de + ld a, [wLoadedCard1Type] + or TYPE_ENERGY + +; only do check if the Pokemon's type +; is either Fire or Lightning + cp TYPE_ENERGY_FIRE + jr z, .fire_or_lightning + cp TYPE_ENERGY_LIGHTNING + jr nz, .next_play_area + +; loop each energy card in list +.fire_or_lightning + ld [wTempCardType], a + ld hl, wDuelTempList +.loop_energy_2 + ld a, [hli] + cp $ff + jr z, .next_play_area + +; if this energy card is useful, +; return no carry. + ld b, a + push hl + farcall CheckIfEnergyIsUseful + pop hl + jr nc, .loop_energy_2 + + ld a, b + or a + ret + +.next_play_area + inc e + ld a, e + cp d + jr nz, .loop_play_area_2 + +; no card was found to be useful +; for Fire/Lightning type Pokemon card. + scf + ret + +; checks whether there are useful energies +; only for Grass type Pokemon cards +; in Play Area. If none found, return carry. +.CheckUsefulGrassEnergy ; 21273 (8:5273) +; unreferenced + ld a, DUELVARS_NUMBER_OF_POKEMON_IN_PLAY_AREA + call GetTurnDuelistVariable + ld d, a + ld e, PLAY_AREA_ARENA + +.loop_play_area_3 + ld a, DUELVARS_ARENA_CARD + add e + push de + call GetTurnDuelistVariable + +; get card's ID and Type + call GetCardIDFromDeckIndex + ld a, e + ld [wTempCardID], a + call LoadCardDataToBuffer1_FromCardID + pop de + ld a, [wLoadedCard1Type] + or TYPE_ENERGY + +; only do check if the Pokemon's type is Grass + cp TYPE_ENERGY_GRASS + jr nz, .next_play_area_3 + +; loop each energy card in list + ld [wTempCardType], a + ld hl, wDuelTempList +.loop_energy_3 + ld a, [hli] + cp $ff + jr z, .next_play_area_3 + +; if this energy card is useful, +; return no carry. + ld b, a + push hl + farcall CheckIfEnergyIsUseful + pop hl + jr nc, .loop_energy_3 + + ld a, b + or a + ret + +.next_play_area_3 + inc e + ld a, e + cp d + jr nz, .loop_play_area_3 + +; no card was found to be useful +; for Grass type Pokemon card. + scf + ret + +AIPlay_Pokedex: ; 212b4 (8:52b4) + ld a, [wAITrainerCardToPlay] + ldh [hTempCardIndex_ff9f], a + ld a, [wce1a] + ldh [hTemp_ffa0], a + ld a, [wce1b] + ldh [hTempPlayAreaLocation_ffa1], a + ld a, [wce1c] + ldh [hTempRetreatCostCards], a + ld a, [wce1d] + ldh [$ffa3], a + ld a, [wce1e] + ldh [$ffa4], a + ld a, $ff + ldh [$ffa5], a + ld a, OPPACTION_EXECUTE_TRAINER_EFFECTS + bank1call AIMakeDecision + ret + +AIDecide_Pokedex: ; 212dc (8:52dc) + ld a, [wAIPokedexCounter] + cp 5 + 1 + jr c, .no_carry ; return if counter hasn't reached 6 yet + +; return no carry if number of cards in deck <= 4 + ld a, DUELVARS_NUMBER_OF_CARDS_NOT_IN_DECK + call GetTurnDuelistVariable + cp DECK_SIZE - 4 + jr nc, .no_carry + +; has a 3 in 10 chance of actually playing card + ld a, 10 + call Random + cp 3 + jr c, .pick_cards + +.no_carry + or a + ret + +.pick_cards +; the following comparison is disregarded +; the Wonders of Science deck was probably intended +; to use PickPokedexCards_Unreferenced instead + ld a, [wOpponentDeckID] + cp WONDERS_OF_SCIENCE_DECK_ID + jp PickPokedexCards ; bug, should be jp nz + +; picks order of the cards in deck from the effects of Pokedex. +; prioritizes Pokemon cards, then Trainer cards, then energy cards. +; stores the resulting order in wce1a. +PickPokedexCards_Unreferenced: ; 212ff (8:52ff) +; unreferenced + xor a + ld [wAIPokedexCounter], a ; reset counter + + ld a, DUELVARS_NUMBER_OF_CARDS_NOT_IN_DECK + call GetTurnDuelistVariable + add DUELVARS_DECK_CARDS + ld l, a + lb de, $00, $00 + ld b, 5 + +; run through 5 of the remaining cards in deck +.next_card + ld a, [hli] + ld c, a + call .GetCardType + +; load this card's deck index and type in memory +; wce08 = card types +; wce0f = card deck indices + push hl + ld hl, wce08 + add hl, de + ld [hl], a + ld hl, wce0f + add hl, de + ld [hl], c + pop hl + + inc e + dec b + jr nz, .next_card + +; terminate the wce08 list + ld a, $ff + ld [wce08 + 5], a + + ld de, wce1a + +; find Pokemon + ld hl, wce08 + ld c, -1 + ld b, $00 + +; run through the stored cards +; and look for any Pokemon cards. +.loop_pokemon + inc c + ld a, [hli] + cp $ff + jr z, .find_trainers + cp TYPE_ENERGY + jr nc, .loop_pokemon +; found a Pokemon card +; store it in wce1a list + push hl + ld hl, wce0f + add hl, bc + ld a, [hl] + pop hl + ld [de], a + inc de + jr .loop_pokemon + +; run through the stored cards +; and look for any Trainer cards. +.find_trainers + ld hl, wce08 + ld c, -1 + ld b, $00 + +.loop_trainers + inc c + ld a, [hli] + cp $ff + jr z, .find_energy + cp TYPE_TRAINER + jr nz, .loop_trainers +; found a Trainer card +; store it in wce1a list + push hl + ld hl, wce0f + add hl, bc + ld a, [hl] + pop hl + ld [de], a + inc de + jr .loop_trainers + +.find_energy + ld hl, wce08 + ld c, -1 + ld b, $00 + +; run through the stored cards +; and look for any energy cards. +.loop_energy + inc c + ld a, [hli] + cp $ff + jr z, .done + and TYPE_ENERGY + jr z, .loop_energy +; found an energy card +; store it in wce1a list + push hl + ld hl, wce0f + add hl, bc + ld a, [hl] + pop hl + ld [de], a + inc de + jr .loop_energy + +.done + scf + ret + +.GetCardType ; 21383 (8:5383) + push bc + push de + call GetCardIDFromDeckIndex + call GetCardType + pop de + pop bc + ret + +; picks order of the cards in deck from the effects of Pokedex. +; prioritizes energy cards, then Pokemon cards, then Trainer cards. +; stores the resulting order in wce1a. +PickPokedexCards: ; 2138e (8:538e) + xor a + ld [wAIPokedexCounter], a ; reset counter ; reset counter + + ld a, DUELVARS_NUMBER_OF_CARDS_NOT_IN_DECK + call GetTurnDuelistVariable + add DUELVARS_DECK_CARDS + ld l, a + lb de, $00, $00 + ld b, 5 + +; run through 5 of the remaining cards in deck +.next_card + ld a, [hli] + ld c, a + call .GetCardType + +; load this card's deck index and type in memory +; wce08 = card types +; wce0f = card deck indices + push hl + ld hl, wce08 + add hl, de + ld [hl], a + ld hl, wce0f + add hl, de + ld [hl], c + pop hl + + inc e + dec b + jr nz, .next_card + +; terminate the wce08 list + ld a, $ff + ld [wce08 + 5], a + + ld de, wce1a + +; find energy + ld hl, wce08 + ld c, -1 + ld b, $00 + +; run through the stored cards +; and look for any energy cards. +.loop_energy + inc c + ld a, [hli] + cp $ff + jr z, .find_pokemon + and TYPE_ENERGY + jr z, .loop_energy +; found an energy card +; store it in wce1a list + push hl + ld hl, wce0f + add hl, bc + ld a, [hl] + pop hl + ld [de], a + inc de + jr .loop_energy + +.find_pokemon + ld hl, wce08 + ld c, -1 + ld b, $00 + +; run through the stored cards +; and look for any Pokemon cards. +.loop_pokemon + inc c + ld a, [hli] + cp $ff + jr z, .find_trainers + cp TYPE_ENERGY + jr nc, .loop_pokemon +; found a Pokemon card +; store it in wce1a list + push hl + ld hl, wce0f + add hl, bc + ld a, [hl] + pop hl + ld [de], a + inc de + jr .loop_pokemon + +; run through the stored cards +; and look for any Trainer cards. +.find_trainers + ld hl, wce08 + ld c, -1 + ld b, $00 + +.loop_trainers + inc c + ld a, [hli] + cp $ff + jr z, .done + cp TYPE_TRAINER + jr nz, .loop_trainers +; found a Trainer card +; store it in wce1a list + push hl + ld hl, wce0f + add hl, bc + ld a, [hl] + pop hl + ld [de], a + inc de + jr .loop_trainers + +.done + scf + ret + +.GetCardType ; 21412 (8:5412) + push bc + push de + call GetCardIDFromDeckIndex + call GetCardType + pop de + pop bc + ret + +AIPlay_FullHeal: ; 2141d (8:541d) + ld a, [wAITrainerCardToPlay] + ldh [hTempCardIndex_ff9f], a + ld a, OPPACTION_EXECUTE_TRAINER_EFFECTS + bank1call AIMakeDecision + ret + +AIDecide_FullHeal: ; 21428 (8:5428) + ld a, DUELVARS_ARENA_CARD_STATUS + call GetTurnDuelistVariable + +; skip if no status on arena card + or a ; NO_STATUS + jr z, .no_carry + + and CNF_SLP_PRZ + cp PARALYZED + jr z, .paralyzed + cp ASLEEP + jr z, .asleep + cp CONFUSED + jr z, .confused + ; if either PSN or DBLPSN, fallthrough + +.set_carry + scf + ret + +.asleep +; set carry if any of the following +; cards are in the Play Area. + ld a, GASTLY1 + ld b, PLAY_AREA_ARENA + call LookForCardIDInPlayArea_Bank8 + jr c, .set_carry + ld a, GASTLY2 + ld b, PLAY_AREA_ARENA + call LookForCardIDInPlayArea_Bank8 + jr c, .set_carry + ld a, HAUNTER2 + ld b, PLAY_AREA_ARENA + call LookForCardIDInPlayArea_Bank8 + jr c, .set_carry + +; otherwise fallthrough + +.paralyzed +; if Scoop Up is in hand and decided to be played, skip. + ld a, SCOOP_UP + call LookForCardIDInHandList_Bank8 + jr nc, .no_scoop_up_prz + call AIDecide_ScoopUp + jr c, .no_carry + +.no_scoop_up_prz +; if card can damage defending Pokemon... + xor a ; PLAY_AREA_ARENA + farcall CheckIfCanDamageDefendingPokemon + jr nc, .no_carry +; ...and can play an energy card to retreat, set carry. + ld a, [wAIPlayEnergyCardForRetreat] + or a + jr nz, .set_carry + +; if not, check whether it's a card it would rather retreat, +; and if it isn't, set carry. + farcall AIDecideWhetherToRetreat + jr nc, .set_carry + +.no_carry + or a + ret + +.confused +; if Scoop Up is in hand and decided to be played, skip. + ld a, SCOOP_UP + call LookForCardIDInHandList_Bank8 + jr nc, .no_scoop_up_cnf + call AIDecide_ScoopUp + jr c, .no_carry + +.no_scoop_up_cnf +; if card can damage defending Pokemon... + xor a ; PLAY_AREA_ARENA + farcall CheckIfCanDamageDefendingPokemon + jr nc, .no_carry +; ...and can play an energy card to retreat, set carry. + ld a, [wAIPlayEnergyCardForRetreat] + or a + jr nz, .set_carry +; if not, return no carry. + jr .no_carry + +AIPlay_MrFuji: ; 21497 (8:5497) + ld a, [wAITrainerCardToPlay] + ldh [hTempCardIndex_ff9f], a + ld a, [wAITrainerCardParameter] + ldh [hTemp_ffa0], a + ld a, OPPACTION_EXECUTE_TRAINER_EFFECTS + bank1call AIMakeDecision + ret + +; AI logic for playing Mr Fuji +AIDecide_MrFuji: ; 214a7 (8:54a7) + ld a, $ff + ld [wce06], a + ld [wce08], a + +; if just one Pokemon in Play Area, skip. + ld a, DUELVARS_NUMBER_OF_POKEMON_IN_PLAY_AREA + call GetTurnDuelistVariable + cp 1 + ret z + + dec a + ld d, a + ld e, PLAY_AREA_BENCH_1 + +; find a Pokemon in the bench that has damage counters. +.loop_bench + ld a, DUELVARS_ARENA_CARD + add e + push de + call GetTurnDuelistVariable + call LoadCardDataToBuffer1_FromDeckIndex + pop de + + ld a, [wLoadedCard1HP] + ld b, a + + ; skip if zero damage counters + call GetCardDamageAndMaxHP + call ConvertHPToCounters + or a + jr z, .next + +; a = damage counters +; b = hp left + call CalculateBDividedByA_Bank8 + cp 20 + jr nc, .next + +; here, HP left in counters is less than twice +; the number of damage counters, that is: +; HP < 1/3 max HP + +; if value is less than the one found before, store this one. + ld hl, wce08 + cp [hl] + jr nc, .next + ld [hl], a + ld a, e + ld [wce06], a +.next + inc e + dec d + jr nz, .loop_bench + + ld a, [wce06] + cp $ff + ret z + + scf + ret + +AIPlay_ScoopUp: ; 214f1 (8:54f1) + ld a, [wAITrainerCardToPlay] + ldh [hTempCardIndex_ff9f], a + ld a, [wAITrainerCardParameter] + ldh [hTemp_ffa0], a + ld a, [wce1a] + ldh [hTempPlayAreaLocation_ffa1], a + ld a, OPPACTION_EXECUTE_TRAINER_EFFECTS + bank1call AIMakeDecision + ret + +AIDecide_ScoopUp: ; 21506 (8:5506) + xor a + ldh [hTempPlayAreaLocation_ff9d], a + +; if only one Pokemon in Play Area, skip. + ld a, DUELVARS_NUMBER_OF_POKEMON_IN_PLAY_AREA + call GetTurnDuelistVariable + cp 2 + jr c, .no_carry + +; handle some decks differently + ld a, [wOpponentDeckID] + cp LEGENDARY_ARTICUNO_DECK_ID + jr z, .HandleLegendaryArticuno + cp LEGENDARY_RONALD_DECK_ID + jp z, .HandleLegendaryRonald + +; if can't KO defending Pokemon, check if defending Pokemon +; can KO this card. If so, then continue. +; If not, return no carry. + +; if it can KO the defending Pokemon this turn, +; return no carry. + farcall CheckIfAnyAttackKnocksOutDefendingCard + jr nc, .cannot_ko + farcall CheckIfSelectedAttackIsUnusable + jr nc, .no_carry + farcall LookForEnergyNeededForAttackInHand + jr c, .no_carry + +.cannot_ko + ld a, DUELVARS_ARENA_CARD_STATUS + call GetTurnDuelistVariable + and CNF_SLP_PRZ + cp PARALYZED + jr z, .cannot_retreat + cp ASLEEP + jr z, .cannot_retreat + +; doesn't have a status that prevents retreat. +; so check if it has enough energy to retreat. +; if not, return no carry. + xor a + ldh [hTempPlayAreaLocation_ff9d], a + call GetPlayAreaCardRetreatCost + ld b, a + ld e, PLAY_AREA_ARENA + farcall CountNumberOfEnergyCardsAttached + cp b + jr c, .cannot_retreat + +.no_carry + or a + ret + +.cannot_retreat +; store damage and total HP left + ld a, DUELVARS_ARENA_CARD + call GetTurnDuelistVariable + call LoadCardDataToBuffer1_FromDeckIndex + ld a, [wLoadedCard1HP] + call ConvertHPToCounters + ld d, a + +; skip if card has no damage counters. + ld e, PLAY_AREA_ARENA + call GetCardDamageAndMaxHP + or a + jr z, .no_carry + +; if (total damage / total HP counters) < 7 +; return carry. +; (this corresponds to damage counters +; being under 70% of the max HP) + ld b, a + ld a, d + call CalculateBDividedByA_Bank8 + cp 7 + jr c, .no_carry + +; store Pokemon to switch to in wce1a and set carry. +.decide_switch + farcall AIDecideBenchPokemonToSwitchTo + jr c, .no_carry + ld [wce1a], a + xor a + scf + ret + +; this deck will use Scoop Up on a benched Articuno2. +; it checks if the defending Pokemon is a Snorlax, +; but interestingly does not check for Muk in both Play Areas. +; will also use Scoop Up on +.HandleLegendaryArticuno +; if less than 3 Play Area Pokemon cards, skip. + ld a, DUELVARS_NUMBER_OF_POKEMON_IN_PLAY_AREA + call GetTurnDuelistVariable + cp 3 + jr c, .no_carry + +; look for Articuno2 in bench + ld a, ARTICUNO2 + ld b, PLAY_AREA_BENCH_1 + call LookForCardIDInPlayArea_Bank8 + jr c, .articuno_bench + +; check Arena card + ld a, DUELVARS_ARENA_CARD + call GetTurnDuelistVariable + call GetCardIDFromDeckIndex + ld a, e + cp ARTICUNO2 + jr z, .articuno_or_chansey + cp CHANSEY + jr nz, .no_carry + +; here either Articuno2 or Chansey +; is the Arena Card. +.articuno_or_chansey +; if can't KO defending Pokemon, check if defending Pokemon +; can KO this card. If so, then continue. +; If not, return no carry. + +; if it can KO the defending Pokemon this turn, +; return no carry. + farcall CheckIfAnyAttackKnocksOutDefendingCard + jr nc, .check_ko + farcall CheckIfSelectedAttackIsUnusable + jr nc, .no_carry + farcall LookForEnergyNeededForAttackInHand + jr c, .no_carry +.check_ko + farcall CheckIfDefendingPokemonCanKnockOut + jr nc, .no_carry + jr .decide_switch + +.articuno_bench +; skip if the defending card is Snorlax + push af + ld a, DUELVARS_ARENA_CARD + call GetNonTurnDuelistVariable + call SwapTurn + call GetCardIDFromDeckIndex + call SwapTurn + ld a, e + cp SNORLAX + pop bc + jr z, .no_carry + +; check attached energy cards. +; if it has any, return no carry. + ld a, b +.check_attached_energy + ld e, a + push af + farcall CountNumberOfEnergyCardsAttached + or a + pop bc + ld a, b + jr z, .no_energy + jp .no_carry + +.no_energy +; has decided to Scoop Up benched card, +; store $ff as the Pokemon card to switch to +; because there's no need to switch. + push af + ld a, $ff + ld [wce1a], a + pop af + scf + ret + +; this deck will use Scoop Up on a benched Articuno2, Zapdos3 or Moltres2. +; interestingly, does not check for Muk in both Play Areas. +.HandleLegendaryRonald ; 215e7 (8:55e7) +; if less than 3 Play Area Pokemon cards, skip. + ld a, DUELVARS_NUMBER_OF_POKEMON_IN_PLAY_AREA + call GetTurnDuelistVariable + cp 3 + jp c, .no_carry + + ld a, ARTICUNO2 + ld b, PLAY_AREA_BENCH_1 + call LookForCardIDInPlayArea_Bank8 + jr c, .articuno_bench + ld a, ZAPDOS3 + ld b, PLAY_AREA_BENCH_1 + call LookForCardIDInPlayArea_Bank8 + jr c, .check_attached_energy + ld a, MOLTRES2 + ld b, PLAY_AREA_BENCH_1 + call LookForCardIDInPlayArea_Bank8 + jr c, .check_attached_energy + jp .no_carry + +AIPlay_Maintenance: ; 2160f (8:560f) + ld a, [wCurrentAIFlags] + or AI_FLAG_MODIFIED_HAND + ld [wCurrentAIFlags], a + ld a, [wAITrainerCardToPlay] + ldh [hTempCardIndex_ff9f], a + ld a, [wce1a] + ldh [hTemp_ffa0], a + ld a, [wce1b] + ldh [hTempPlayAreaLocation_ffa1], a + ld a, OPPACTION_EXECUTE_TRAINER_EFFECTS + bank1call AIMakeDecision + ret + +; AI logic for playing Maintenance +AIDecide_Maintenance: ; 2162c (8:562c) +; Imakuni? has his own thing + ld a, [wOpponentDeckID] + cp IMAKUNI_DECK_ID + jr z, .imakuni + +; skip if number of cars in hand < 4. + ld a, DUELVARS_NUMBER_OF_CARDS_IN_HAND + call GetTurnDuelistVariable + cp 4 + jr c, .no_carry + +; list out all the hand cards and remove +; wAITrainerCardToPlay from list.Then find any duplicate cards. + call CreateHandCardList + ld hl, wDuelTempList + ld a, [wAITrainerCardToPlay] + call FindAndRemoveCardFromList +; if duplicates are not found, return no carry. + call FindDuplicateCards + jp c, .no_carry + +; store the first duplicate card and remove it from the list. +; run duplicate check again. + ld [wce1a], a + ld hl, wDuelTempList + call FindAndRemoveCardFromList +; if duplicates are not found, return no carry. + call FindDuplicateCards + jp c, .no_carry + +; store the second duplicate card and return carry. + ld [wce1b], a + scf + ret + +.no_carry + or a + ret + +.imakuni +; has a 2 in 10 chance of not skipping. + ld a, 10 + call Random + cp 2 + jr nc, .no_carry + +; skip if number of cards in hand < 3. + ld a, DUELVARS_NUMBER_OF_CARDS_IN_HAND + call GetTurnDuelistVariable + cp 3 + jr c, .no_carry + +; shuffle hand cards + call CreateHandCardList + ld hl, wDuelTempList + call CountCardsInDuelTempList + call ShuffleCards + +; go through each card and find +; cards that are different from wAITrainerCardToPlay. +; if found, add those cards to wce1a and wce1a+1. + ld a, [wAITrainerCardToPlay] + ld b, a + ld c, 2 + ld de, wce1a + +.loop + ld a, [hli] + cp $ff + jr z, .no_carry + cp b + jr z, .loop + ld [de], a + inc de + dec c + jr nz, .loop + +; two cards were found, return carry. + scf + ret + +AIPlay_Recycle: ; 2169a (8:569a) + ld a, [wAITrainerCardToPlay] + ldh [hTempCardIndex_ff9f], a + ldtx de, TrainerCardSuccessCheckText + bank1call TossCoin + jr nc, .asm_216ae + ld a, [wAITrainerCardParameter] + ldh [hTemp_ffa0], a + jr .asm_216b2 +.asm_216ae + ld a, $ff + ldh [hTemp_ffa0], a +.asm_216b2 + ld a, OPPACTION_EXECUTE_TRAINER_EFFECTS + bank1call AIMakeDecision + ret + +; lists cards to look for in the Discard Pile. +; has priorities for Ghost Deck, and a "default" priority list +; (which is the Fire Charge deck, since it's the only other +; deck that runs a Recycle card in it.) +AIDecide_Recycle: ; 216b8 (8:56b8) +; no use checking if no cards in Discard Pile + call CreateDiscardPileCardList + jr c, .no_carry + + ld a, $ff + ld [wce08], a + ld [wce08 + 1], a + ld [wce08 + 2], a + ld [wce08 + 3], a + ld [wce08 + 4], a + +; handle Ghost deck differently + ld hl, wDuelTempList + ld a, [wOpponentDeckID] + cp GHOST_DECK_ID + jr z, .loop_2 + +; priority list for Fire Charge deck +.loop_1 + ld a, [hli] + cp $ff + jr z, .done + + ld b, a + call LoadCardDataToBuffer1_FromDeckIndex + +; double colorless + cp DOUBLE_COLORLESS_ENERGY + jr nz, .chansey + ld a, b + ld [wce08], a + jr .loop_1 + +.chansey + cp CHANSEY + jr nz, .tauros + ld a, b + ld [wce08 + 1], a + jr .loop_1 + +.tauros + cp TAUROS + jr nz, .jigglypuff + ld a, b + ld [wce08 + 2], a + jr .loop_1 + +.jigglypuff + cp JIGGLYPUFF1 + jr nz, .loop_1 + ld a, b + ld [wce08 + 3], a + jr .loop_1 + +; loop through wce08 and set carry +; on the first that was found in Discard Pile. +; if none were found, return no carry. +.done + ld hl, wce08 + ld b, 5 +.loop_found + ld a, [hli] + cp $ff + jr nz, .set_carry + dec b + jr nz, .loop_found +.no_carry + or a + ret +.set_carry + scf + ret + +; priority list for Ghost deck +.loop_2 + ld a, [hli] + cp $ff + jr z, .done + + ld b, a + call LoadCardDataToBuffer1_FromDeckIndex + +; gastly2 + cp GASTLY2 + jr nz, .gastly1 + ld a, b + ld [wce08], a + jr .loop_2 + +.gastly1 + cp GASTLY1 + jr nz, .zubat + ld a, b + ld [wce08 + 1], a + jr .loop_2 + +.zubat + cp ZUBAT + jr nz, .ditto + ld a, b + ld [wce08 + 2], a + jr .loop_2 + +.ditto + cp DITTO + jr nz, .meowth + ld a, b + ld [wce08 + 3], a + jr .loop_2 + +.meowth + cp MEOWTH2 + jr nz, .loop_2 + ld a, b + ld [wce08 + 4], a + jr .loop_2 + +AIPlay_Lass: ; 21755 (8:5755) + ld a, [wCurrentAIFlags] + or AI_FLAG_MODIFIED_HAND + ld [wCurrentAIFlags], a + ld a, [wAITrainerCardToPlay] + ldh [hTempCardIndex_ff9f], a + ld a, OPPACTION_EXECUTE_TRAINER_EFFECTS + bank1call AIMakeDecision + ret + +AIDecide_Lass: ; 21768 (8:5768) +; skip if player has less than 7 cards in hand + ld a, DUELVARS_NUMBER_OF_CARDS_IN_HAND + call GetNonTurnDuelistVariable + cp 7 + jr c, .no_carry + +; look for Trainer cards in hand (except for Lass) +; if any is found, return no carry. +; otherwise, return carry. + call CreateHandCardList + ld hl, wDuelTempList +.loop + ld a, [hli] + cp $ff + jr z, .set_carry + ld b, a + call LoadCardDataToBuffer1_FromDeckIndex + cp LASS + jr z, .loop + ld a, [wLoadedCard1Type] + cp TYPE_TRAINER + jr nz, .loop +.no_carry + or a + ret +.set_carry + scf + ret + +AIPlay_ItemFinder: ; 2178f (8:578f) + ld a, [wCurrentAIFlags] + or AI_FLAG_MODIFIED_HAND + ld [wCurrentAIFlags], a + ld a, [wAITrainerCardToPlay] + ldh [hTempCardIndex_ff9f], a + ld a, [wce1a] + ldh [hTemp_ffa0], a + ld a, [wce1b] + ldh [hTempPlayAreaLocation_ffa1], a + ld a, [wAITrainerCardParameter] + ldh [hTempRetreatCostCards], a + ld a, OPPACTION_EXECUTE_TRAINER_EFFECTS + bank1call AIMakeDecision + ret + +; checks whether there's Energy Removal in Discard Pile. +; if so, find duplicate cards in hand to discard +; that are not Mr Mime and Pokemon Trader cards. +; this logic is suitable only for Strange Psyshock deck. +AIDecide_ItemFinder: ; 217b1 (8:57b1) +; skip if no Discard Pile. + call CreateDiscardPileCardList + jr c, .no_carry + +; look for Energy Removal in Discard Pile + ld hl, wDuelTempList +.loop_discard_pile + ld a, [hli] + cp $ff + jr z, .no_carry + ld b, a + call LoadCardDataToBuffer1_FromDeckIndex + cp ENERGY_REMOVAL + jr nz, .loop_discard_pile +; found, store this deck index + ld a, b + ld [wce06], a + +; before looking for cards to discard in hand, +; remove any Mr Mime and Pokemon Trader cards. +; this way these are guaranteed to not be discarded. + call CreateHandCardList + ld hl, wDuelTempList +.loop_hand + ld a, [hli] + cp $ff + jr z, .choose_discard + ld b, a + call LoadCardDataToBuffer1_FromDeckIndex + cp MR_MIME + jr nz, .pkmn_trader + call RemoveCardFromList + jr .loop_hand +.pkmn_trader + cp POKEMON_TRADER + jr nz, .loop_hand + call RemoveCardFromList + jr .loop_hand + +; choose cards to discard from hand. +.choose_discard + ld hl, wDuelTempList + +; do not discard wAITrainerCardToPlay + ld a, [wAITrainerCardToPlay] + call FindAndRemoveCardFromList +; find any duplicates, if not found, return no carry. + call FindDuplicateCards + jp c, .no_carry + +; store the duplicate found in wce1a and +; remove it from the hand list. + ld [wce1a], a + ld hl, wDuelTempList + call FindAndRemoveCardFromList +; find duplicates again, if not found, return no carry. + call FindDuplicateCards + jp c, .no_carry + +; store the duplicate found in wce1b. +; output the card to be recovered from the Discard Pile. + ld [wce1b], a + ld a, [wce06] + scf + ret + +.no_carry + or a + ret + +AIPlay_Imakuni: ; 21813 (8:5813) + ld a, [wAITrainerCardToPlay] + ldh [hTempCardIndex_ff9f], a + ld a, OPPACTION_EXECUTE_TRAINER_EFFECTS + bank1call AIMakeDecision + ret + +; only sets carry if Active card is not confused. +AIDecide_Imakuni: ; 2181e (8:581e) + ld a, DUELVARS_ARENA_CARD_STATUS + call GetTurnDuelistVariable + and CNF_SLP_PRZ + cp CONFUSED + jr z, .confused + scf + ret +.confused + or a + ret + +AIPlay_Gambler: ; 2182d (8:582d) + ld a, [wCurrentAIFlags] + or AI_FLAG_MODIFIED_HAND + ld [wCurrentAIFlags], a + ld a, [wOpponentDeckID] + cp IMAKUNI_DECK_ID + jr z, .asm_2186a + ld hl, wRNG1 + ld a, [hli] + ld [wce06], a + ld a, [hli] + ld [wce08], a + ld a, [hl] + ld [wce0f], a + ld a, $50 + ld [hld], a + ld [hld], a + ld [hl], a + ld a, [wAITrainerCardToPlay] + ldh [hTempCardIndex_ff9f], a + ld a, OPPACTION_EXECUTE_TRAINER_EFFECTS + bank1call AIMakeDecision + ld hl, wRNG1 + ld a, [wce06] + ld [hli], a + ld a, [wce08] + ld [hli], a + ld a, [wce0f] + ld [hl], a + ret +.asm_2186a + ld a, [wAITrainerCardToPlay] + ldh [hTempCardIndex_ff9f], a + ld a, OPPACTION_EXECUTE_TRAINER_EFFECTS + bank1call AIMakeDecision + ret + +; checks whether to play Gambler. +; aside from Imakuni?, all other opponents only +; play this card if Player is running Mewtwo1-only deck. +AIDecide_Gambler: ; 21875 (8:5875) +; Imakuni? has his own routine + ld a, [wOpponentDeckID] + cp IMAKUNI_DECK_ID + jr z, .imakuni + +; check if flag is set for Player using Mewtwo1 only deck + ld a, [wAIBarrierFlagCounter] + and AI_MEWTWO_MILL + jr z, .no_carry + +; set carry if number of cards in deck <= 4. +; this is done to counteract the deck out strategy +; of Mewtwo1 deck, by replenishing the deck with hand cards. + ld a, DUELVARS_NUMBER_OF_CARDS_NOT_IN_DECK + call GetTurnDuelistVariable + cp DECK_SIZE - 4 + jr nc, .set_carry +.no_carry + or a + ret + +.imakuni +; has a 2 in 10 chance of returning carry + ld a, 10 + call Random + cp 2 + jr nc, .no_carry +.set_carry + scf + ret + +AIPlay_Revive: ; 21899 (8:5899) + ld a, [wAITrainerCardToPlay] + ldh [hTempCardIndex_ff9f], a + ld a, [wAITrainerCardParameter] + ldh [hTemp_ffa0], a + ld a, OPPACTION_EXECUTE_TRAINER_EFFECTS + bank1call AIMakeDecision + ret + +; checks certain cards in Discard Pile to use Revive on. +; suitable for Muscle For Brains deck only. +AIDecide_Revive: ; 218a9 (8:58a9) +; skip if no cards in Discard Pile + call CreateDiscardPileCardList + jr c, .no_carry + +; skip if number of Pokemon cards in Play Area >= 4 + ld a, DUELVARS_NUMBER_OF_POKEMON_IN_PLAY_AREA + call GetTurnDuelistVariable + cp 4 + jr nc, .no_carry + +; look in Discard Pile for specific cards. + ld hl, wDuelTempList +.loop_discard_pile + ld a, [hli] + cp $ff + jr z, .no_carry + ld b, a + call LoadCardDataToBuffer1_FromDeckIndex + +; these checks have a bug. +; it works fine for Hitmonchan and Hitmonlee, +; but in case it's a Tauros card, the routine will fallthrough +; into the Kangaskhan check. since it will never be equal to Kangaskhan, +; it will fallthrough into the set carry branch. +; in case it's a Kangaskhan card, the check will fail in the Tauros check +; and jump back into the loop. so just by accident the Tauros check works, +; but Kangaskhan will never be correctly checked because of this. + cp HITMONCHAN + jr z, .set_carry + cp HITMONLEE + jr z, .set_carry + cp TAUROS + jr nz, .loop_discard_pile ; bug, these two lines should be swapped + cp KANGASKHAN + jr z, .set_carry ; bug, these two lines should be swapped + +.set_carry + ld a, b + scf + ret +.no_carry + or a + ret + +AIPlay_PokemonFlute: ; 218d8 (8:58d8) + ld a, [wAITrainerCardToPlay] + ldh [hTempCardIndex_ff9f], a + ld a, [wAITrainerCardParameter] + ldh [hTemp_ffa0], a + ld a, OPPACTION_EXECUTE_TRAINER_EFFECTS + bank1call AIMakeDecision + ret + +AIDecide_PokemonFlute: ; 218e8 (8:58e8) +; if player has no Discard Pile, skip. + call SwapTurn + call CreateDiscardPileCardList + call SwapTurn + jr c, .no_carry + +; if player's Play Area is already full, skip. + ld a, DUELVARS_NUMBER_OF_POKEMON_IN_PLAY_AREA + call GetNonTurnDuelistVariable + cp MAX_PLAY_AREA_POKEMON + jr nc, .no_carry + + ld a, [wOpponentDeckID] + cp IMAKUNI_DECK_ID + jr z, .imakuni + + ld a, $ff + ld [wce06], a + ld [wce08], a + +; find Basic stage Pokemon with lowest HP in Discard Pile + ld hl, wDuelTempList +.loop_1 + ld a, [hli] + cp $ff + jr z, .done + + ld b, a + call SwapTurn + call LoadCardDataToBuffer1_FromDeckIndex + call SwapTurn +; skip this card if it's not Pokemon card + ld a, [wLoadedCard1Type] + cp TYPE_ENERGY + jr nc, .loop_1 +; skip this card if it's not Basic Stage + ld a, [wLoadedCard1Stage] + or a ; BASIC + jr nz, .loop_1 + +; compare this HP with one stored + ld a, [wLoadedCard1HP] + push hl + ld hl, wce06 + cp [hl] + pop hl + jr nc, .loop_1 +; if lower, store this one + ld [wce06], a + ld a, b + ld [wce08], a + jr .loop_1 + +.done +; if lowest HP found >= 50, return no carry + ld a, [wce06] + cp 50 + jr nc, .no_carry +; otherwise output its deck index in a and set carry. + ld a, [wce08] + scf + ret +.no_carry + or a + ret + +.imakuni +; has 2 in 10 chance of not skipping + ld a, 10 + call Random + cp 2 + jr nc, .no_carry + +; look for any Basic Pokemon card + ld hl, wDuelTempList +.loop_2 + ld a, [hli] + cp $ff + jr z, .no_carry + ld b, a + call SwapTurn + call LoadCardDataToBuffer1_FromDeckIndex + call SwapTurn + ld a, [wLoadedCard1Type] + cp TYPE_ENERGY + jr nc, .loop_2 + ld a, [wLoadedCard1Stage] + or a ; BASIC + jr nz, .loop_2 + +; a Basic stage Pokemon was found, return carry + ld a, b + scf + ret + +AIPlay_ClefairyDollOrMysteriousFossil: ; 21977 (8:5977) + ld a, [wAITrainerCardToPlay] + ldh [hTempCardIndex_ff9f], a + ld a, OPPACTION_EXECUTE_TRAINER_EFFECTS + bank1call AIMakeDecision + ret + +; AI logic for playing Clefairy Doll +AIDecide_ClefairyDollOrMysteriousFossil: ; 21982 (8:5982) +; if has max number of Play Area Pokemon, skip + ld a, DUELVARS_NUMBER_OF_POKEMON_IN_PLAY_AREA + call GetTurnDuelistVariable + cp MAX_PLAY_AREA_POKEMON + jr nc, .no_carry + +; store number of Play Area Pokemon cards + ld [wce06], a + +; if the Arena card is Wigglytuff, return carry + ld a, DUELVARS_ARENA_CARD + call GetTurnDuelistVariable + call GetCardIDFromDeckIndex + ld a, e + cp WIGGLYTUFF + jr z, .set_carry + +; if number of Play Area Pokemon >= 4, return no carry + ld a, [wce06] + cp 4 + jr nc, .no_carry + +.set_carry + scf + ret +.no_carry + or a + ret + +AIPlay_Pokeball: ; 219a6 (8:59a6) + ld a, [wAITrainerCardToPlay] + ldh [hTempCardIndex_ff9f], a + ldtx de, TrainerCardSuccessCheckText + bank1call TossCoin + ldh [hTemp_ffa0], a + jr nc, .asm_219bc + ld a, [wAITrainerCardParameter] + ldh [hTempPlayAreaLocation_ffa1], a + jr .asm_219c0 +.asm_219bc + ld a, $ff + ldh [hTempPlayAreaLocation_ffa1], a +.asm_219c0 + ld a, OPPACTION_EXECUTE_TRAINER_EFFECTS + bank1call AIMakeDecision + ret + +AIDecide_Pokeball: ; 219c6 (8:59c6) +; go to the routines associated with deck ID + ld a, [wOpponentDeckID] + cp FIRE_CHARGE_DECK_ID + jr z, .fire_charge + cp HARD_POKEMON_DECK_ID + jr z, .hard_pokemon + cp PIKACHU_DECK_ID + jr z, .pikachu + cp ETCETERA_DECK_ID + jr z, .etcetera + cp LOVELY_NIDORAN_DECK_ID + jp z, .lovely_nidoran + or a + ret + +; this deck runs a deck check for specific +; card IDs in order of decreasing priority +.fire_charge + ld e, CHANSEY + ld a, CARD_LOCATION_DECK + call LookForCardIDInLocation + ret c + ld e, TAUROS + ld a, CARD_LOCATION_DECK + call LookForCardIDInLocation + ret c + ld e, JIGGLYPUFF1 + ld a, CARD_LOCATION_DECK + call LookForCardIDInLocation + ret c + ret + +; this deck runs a deck check for specific +; card IDs in order of decreasing priority +.hard_pokemon + ld e, RHYHORN + ld a, CARD_LOCATION_DECK + call LookForCardIDInLocation + ret c + ld e, RHYDON + ld a, CARD_LOCATION_DECK + call LookForCardIDInLocation + ret c + ld e, ONIX + ld a, CARD_LOCATION_DECK + call LookForCardIDInLocation + ret c + ret + +; this deck runs a deck check for specific +; card IDs in order of decreasing priority +.pikachu + ld e, PIKACHU2 + ld a, CARD_LOCATION_DECK + call LookForCardIDInLocation + ret c + ld e, PIKACHU3 + ld a, CARD_LOCATION_DECK + call LookForCardIDInLocation + ret c + ld e, PIKACHU4 + ld a, CARD_LOCATION_DECK + call LookForCardIDInLocation + ret c + ld e, PIKACHU1 + ld a, CARD_LOCATION_DECK + call LookForCardIDInLocation + ret c + ld e, FLYING_PIKACHU + ld a, CARD_LOCATION_DECK + call LookForCardIDInLocation + ret c + ret + +; this deck runs a deck check for specific +; card IDs in order of decreasing priority +; given a specific energy card in hand. +; also it avoids redundancy, so if it already +; has that card ID in the hand, it is skipped. +.etcetera +; fire + ld a, FIRE_ENERGY + call LookForCardIDInHandList_Bank8 + jr nc, .lightning + ld a, CHARMANDER + call LookForCardIDInHandList_Bank8 + jr c, .lightning + ld a, MAGMAR2 + call LookForCardIDInHandList_Bank8 + jr c, .lightning + ld e, CHARMANDER + ld a, CARD_LOCATION_DECK + call LookForCardIDInLocation + ret c + ld e, MAGMAR2 + ld a, CARD_LOCATION_DECK + call LookForCardIDInLocation + ret c + +.lightning + ld a, LIGHTNING_ENERGY + call LookForCardIDInHandList_Bank8 + jr nc, .fighting + ld a, PIKACHU1 + call LookForCardIDInHandList_Bank8 + jr c, .fighting + ld a, MAGNEMITE1 + call LookForCardIDInHandList_Bank8 + jr c, .fighting + ld e, PIKACHU1 + ld a, CARD_LOCATION_DECK + call LookForCardIDInLocation + ret c + ld e, MAGNEMITE1 + ld a, CARD_LOCATION_DECK + call LookForCardIDInLocation + ret c + +.fighting + ld a, FIGHTING_ENERGY + call LookForCardIDInHandList_Bank8 + jr nc, .psychic + ld a, DIGLETT + call LookForCardIDInHandList_Bank8 + jr c, .psychic + ld a, MACHOP + call LookForCardIDInHandList_Bank8 + jr c, .psychic + ld e, DIGLETT + ld a, CARD_LOCATION_DECK + call LookForCardIDInLocation + ret c + ld e, MACHOP + ld a, CARD_LOCATION_DECK + call LookForCardIDInLocation + ret c + +.psychic + ld a, PSYCHIC_ENERGY + call LookForCardIDInHandList_Bank8 + jr nc, .done_etcetera + ld a, GASTLY1 + call LookForCardIDInHandList_Bank8 + jr c, .done_etcetera + ld a, JYNX + call LookForCardIDInHandList_Bank8 + jr c, .done_etcetera + ld e, GASTLY1 + ld a, CARD_LOCATION_DECK + call LookForCardIDInLocation + ret c + ld e, JYNX + ld a, CARD_LOCATION_DECK + call LookForCardIDInLocation + ret c +.done_etcetera + or a + ret + +; this deck looks for card evolutions if +; its pre-evolution is in hand or in Play Area. +; if none of these are found, it looks for pre-evolutions +; of cards it has in hand. +; it does this for both the NidoranM (first) +; and NidoranF (second) families. +.lovely_nidoran + ld b, NIDORANM + ld a, NIDORINO + call LookForCardIDInDeck_GivenCardIDInHandAndPlayArea + ret c + ld b, NIDORINO + ld a, NIDOKING + call LookForCardIDInDeck_GivenCardIDInHandAndPlayArea + ret c + ld a, NIDORANM + ld b, NIDORINO + call LookForCardIDInDeck_GivenCardIDInHand + ret c + ld a, NIDORINO + ld b, NIDOKING + call LookForCardIDInDeck_GivenCardIDInHand + ret c + ld b, NIDORANF + ld a, NIDORINA + call LookForCardIDInDeck_GivenCardIDInHandAndPlayArea + ret c + ld b, NIDORINA + ld a, NIDOQUEEN + call LookForCardIDInDeck_GivenCardIDInHandAndPlayArea + ret c + ld a, NIDORANF + ld b, NIDORINA + call LookForCardIDInDeck_GivenCardIDInHand + ret c + ld a, NIDORINA + ld b, NIDOQUEEN + call LookForCardIDInDeck_GivenCardIDInHand + ret c + ret + +AIPlay_ComputerSearch: ; 21b12 (8:5b12) + ld a, [wCurrentAIFlags] + or AI_FLAG_MODIFIED_HAND + ld [wCurrentAIFlags], a + ld a, [wAITrainerCardToPlay] + ldh [hTempCardIndex_ff9f], a + ld a, [wAITrainerCardParameter] + ldh [hTempRetreatCostCards], a + ld a, [wce1a] + ldh [hTemp_ffa0], a + ld a, [wce1b] + ldh [hTempPlayAreaLocation_ffa1], a + ld a, OPPACTION_EXECUTE_TRAINER_EFFECTS + bank1call AIMakeDecision + ret + +; checks what Deck ID AI is playing and handle +; them in their own routine. +AIDecide_ComputerSearch: ; 21b34 (8:5b34) +; skip if number of cards in hand < 3 + ld a, DUELVARS_NUMBER_OF_CARDS_IN_HAND + call GetTurnDuelistVariable + cp 3 + jr c, .no_carry + + ld a, [wOpponentDeckID] + cp ROCK_CRUSHER_DECK_ID + jr z, AIDecide_ComputerSearch_RockCrusher + cp WONDERS_OF_SCIENCE_DECK_ID + jp z, AIDecide_ComputerSearch_WondersOfScience + cp FIRE_CHARGE_DECK_ID + jp z, AIDecide_ComputerSearch_FireCharge + cp ANGER_DECK_ID + jp z, AIDecide_ComputerSearch_Anger + +.no_carry + or a + ret + +AIDecide_ComputerSearch_RockCrusher: ; 21b55 (8:5b55) +; if number of cards in hand is equal to 3, +; target Professor Oak in deck + ld a, DUELVARS_NUMBER_OF_CARDS_IN_HAND + call GetTurnDuelistVariable + cp 3 + jr nz, .graveler + + ld e, PROFESSOR_OAK + ld a, CARD_LOCATION_DECK + call LookForCardIDInLocation + jr c, .find_discard_cards_1 + ; no Professor Oak in deck, fallthrough + +.no_carry + or a + ret + +.find_discard_cards_1 + ld [wce06], a + ld a, $ff + ld [wce1a], a + ld [wce1b], a + + call CreateHandCardList + ld hl, wDuelTempList + ld de, wce1a +.loop_hand_1 + ld a, [hli] + cp $ff + jr z, .check_discard_cards + + ld c, a + call LoadCardDataToBuffer1_FromDeckIndex + +; if any of the following cards are in the hand, +; return no carry. + cp PROFESSOR_OAK + jr z, .no_carry + cp FIGHTING_ENERGY + jr z, .no_carry + cp DOUBLE_COLORLESS_ENERGY + jr z, .no_carry + cp DIGLETT + jr z, .no_carry + cp GEODUDE + jr z, .no_carry + cp ONIX + jr z, .no_carry + cp RHYHORN + jr z, .no_carry + +; if it's same as wAITrainerCardToPlay, skip this card. + ld a, [wAITrainerCardToPlay] + ld b, a + ld a, c + cp b + jr z, .loop_hand_1 + +; store this card index in memory + ld [de], a + inc de + jr .loop_hand_1 + +.check_discard_cards +; check if two cards were found +; if so, output in a the deck index +; of Professor Oak card found in deck and set carry. + ld a, [wce1b] + cp $ff + jr z, .no_carry + ld a, [wce06] + scf + ret + +; more than 3 cards in hand, so look for +; specific evolution cards. + +; checks if there is a Graveler card in the deck to target. +; if so, check if there's Geodude in hand or Play Area, +; and if there's no Graveler card in hand, proceed. +; also removes Geodude from hand list so that it is not discarded. +.graveler + ld e, GRAVELER + ld a, CARD_LOCATION_DECK + call LookForCardIDInLocation + jr nc, .golem + ld [wce06], a + ld a, GEODUDE + call LookForCardIDInHandAndPlayArea + jr nc, .golem + ld a, GRAVELER + call LookForCardIDInHandList_Bank8 + jr c, .golem + call CreateHandCardList + ld hl, wDuelTempList + ld e, GEODUDE + farcall RemoveCardIDInList + jr .find_discard_cards_2 + +; checks if there is a Golem card in the deck to target. +; if so, check if there's Graveler in Play Area, +; and if there's no Golem card in hand, proceed. +.golem + ld e, GOLEM + ld a, CARD_LOCATION_DECK + call LookForCardIDInLocation + jr nc, .dugtrio + ld [wce06], a + ld a, GRAVELER + call LookForCardIDInPlayArea_Bank8 + jr nc, .dugtrio + ld a, GOLEM + call LookForCardIDInHandList_Bank8 + jr c, .dugtrio + call CreateHandCardList + ld hl, wDuelTempList + jr .find_discard_cards_2 + +; checks if there is a Dugtrio card in the deck to target. +; if so, check if there's Diglett in Play Area, +; and if there's no Dugtrio card in hand, proceed. +.dugtrio + ld e, DUGTRIO + ld a, CARD_LOCATION_DECK + call LookForCardIDInLocation + jp nc, .no_carry + ld [wce06], a + ld a, DIGLETT + call LookForCardIDInPlayArea_Bank8 + jp nc, .no_carry + ld a, DUGTRIO + call LookForCardIDInHandList_Bank8 + jp c, .no_carry + call CreateHandCardList + ld hl, wDuelTempList + jr .find_discard_cards_2 + +.find_discard_cards_2 + ld a, $ff + ld [wce1a], a + ld [wce1b], a + + ld bc, wce1a + ld d, $00 ; start considering Trainer cards only + +; stores wAITrainerCardToPlay in e so that +; all routines ignore it for the discard effects. + ld a, [wAITrainerCardToPlay] + ld e, a + +; this loop will store in wce1a cards to discard from hand. +; at the start it will only consider Trainer cards, +; then if there are still needed to discard, +; move on to Pokemon cards, and finally to Energy cards. +.loop_hand_2 + call RemoveFromListDifferentCardOfGivenType + jr c, .found + inc d ; move on to next type (Pokemon, then Energy) + ld a, $03 + cp d + jp z, .no_carry ; no more types to look + jr .loop_hand_2 +.found +; store this card in memory, +; and if there's still one more card to search for, +; jump back into the loop. + ld [bc], a + inc bc + ld a, [wce1b] + cp $ff + jr z, .loop_hand_2 + +; output in a Computer Search target and set carry. + ld a, [wce06] + scf + ret + +AIDecide_ComputerSearch_WondersOfScience: ; 21c56 (8:5c56) +; if number of cards in hand < 5, target Professor Oak in deck + ld a, DUELVARS_NUMBER_OF_CARDS_IN_HAND + call GetTurnDuelistVariable + cp 5 + jr nc, .look_in_hand + +; target Professor Oak for Computer Search + ld e, PROFESSOR_OAK + ld a, CARD_LOCATION_DECK + call LookForCardIDInLocation + jp nc, .look_in_hand ; can be a jr + ld [wce06], a + jr .find_discard_cards + +; Professor Oak not in deck, move on to +; look for other cards instead. +; if Grimer or Muk are not in hand, +; check whether to use Computer Search on them. +.look_in_hand + ld a, GRIMER + call LookForCardIDInHandList_Bank8 + jr nc, .target_grimer + ld a, MUK + call LookForCardIDInHandList_Bank8 + jr nc, .target_muk + +.no_carry + or a + ret + +; first check Grimer +; if in deck, check cards to discard. +.target_grimer + ld e, GRIMER + ld a, CARD_LOCATION_DECK + call LookForCardIDInLocation + jp nc, .no_carry ; can be a jr + ld [wce06], a + jr .find_discard_cards + +; first check Muk +; if in deck, check cards to discard. +.target_muk + ld e, MUK + ld a, CARD_LOCATION_DECK + call LookForCardIDInLocation + jp nc, .no_carry ; can be a jr + ld [wce06], a + +; only discard Trainer cards from hand. +; if there are less than 2 Trainer cards to discard, +; then return with no carry. +; else, store the cards to discard and the +; target card deck index, and return carry. +.find_discard_cards + call CreateHandCardList + ld hl, wDuelTempList + ld d, $00 ; first consider Trainer cards + +; ignore wAITrainerCardToPlay for the discard effects. + ld a, [wAITrainerCardToPlay] + ld e, a + call RemoveFromListDifferentCardOfGivenType + jr nc, .no_carry + ld [wce1a], a + call RemoveFromListDifferentCardOfGivenType + jr nc, .no_carry + ld [wce1b], a + ld a, [wce06] + scf + ret + +AIDecide_ComputerSearch_FireCharge: ; 21cbb (8:5cbb) +; pick target card in deck from highest to lowest priority. +; if not found in hand, go to corresponding branch. + ld a, CHANSEY + call LookForCardIDInHandList_Bank8 + jr nc, .chansey + ld a, TAUROS + call LookForCardIDInHandList_Bank8 + jr nc, .tauros + ld a, JIGGLYPUFF1 + call LookForCardIDInHandList_Bank8 + jr nc, .jigglypuff + ; fallthrough + +.no_carry + or a + ret + +; for each card targeted, check if it's in deck and, +; if not, then return no carry. +; else, look for cards to discard. +.chansey + ld e, CHANSEY + ld a, CARD_LOCATION_DECK + call LookForCardIDInLocation + jp nc, .no_carry + ld [wce06], a + jr .find_discard_cards +.tauros + ld e, TAUROS + ld a, CARD_LOCATION_DECK + call LookForCardIDInLocation + jp nc, .no_carry + ld [wce06], a + jr .find_discard_cards +.jigglypuff + ld e, JIGGLYPUFF1 + ld a, CARD_LOCATION_DECK + call LookForCardIDInLocation + jp nc, .no_carry + ld [wce06], a + +; only discard Trainer cards from hand. +; if there are less than 2 Trainer cards to discard, +; then return with no carry. +; else, store the cards to discard and the +; target card deck index, and return carry. +.find_discard_cards + call CreateHandCardList + ld hl, wDuelTempList + ld d, $00 ; first consider Trainer cards + +; ignore wAITrainerCardToPlay for the discard effects. + ld a, [wAITrainerCardToPlay] + ld e, a + call RemoveFromListDifferentCardOfGivenType + jr nc, .no_carry + ld [wce1a], a + call RemoveFromListDifferentCardOfGivenType + jr nc, .no_carry + ld [wce1b], a + ld a, [wce06] + scf + ret + +AIDecide_ComputerSearch_Anger: ; 21d1e (8:5d1e) +; for each of the following cards, +; first run a check if there's a pre-evolution in +; Play Area or in the hand. If there is, choose it as target. +; otherwise, check if the evolution card is in +; hand and if so, choose it as target instead. + ld b, RATTATA + ld a, RATICATE + call LookForCardIDInDeck_GivenCardIDInHandAndPlayArea + jr c, .find_discard_cards + ld a, RATTATA + ld b, RATICATE + call LookForCardIDInDeck_GivenCardIDInHand + jr c, .find_discard_cards + ld b, GROWLITHE + ld a, ARCANINE1 + call LookForCardIDInDeck_GivenCardIDInHandAndPlayArea + jr c, .find_discard_cards + ld a, GROWLITHE + ld b, ARCANINE1 + call LookForCardIDInDeck_GivenCardIDInHand + jr c, .find_discard_cards + ld b, DODUO + ld a, DODRIO + call LookForCardIDInDeck_GivenCardIDInHandAndPlayArea + jr c, .find_discard_cards + ld a, DODUO + ld b, DODRIO + call LookForCardIDInDeck_GivenCardIDInHand + jr c, .find_discard_cards + ; fallthrough + +.no_carry + or a + ret + +; only discard Trainer cards from hand. +; if there are less than 2 Trainer cards to discard, +; then return with no carry. +; else, store the cards to discard and the +; target card deck index, and return carry. +.find_discard_cards + ld [wce06], a + call CreateHandCardList + ld hl, wDuelTempList + ld d, $00 ; first consider Trainer cards + +; ignore wAITrainerCardToPlay for the discard effects. + ld a, [wAITrainerCardToPlay] + ld e, a + call RemoveFromListDifferentCardOfGivenType + jr nc, .no_carry + ld [wce1a], a + call RemoveFromListDifferentCardOfGivenType + jr nc, .no_carry + ld [wce1b], a + ld a, [wce06] + scf + ret + +AIPlay_PokemonTrader: ; 21d7a (8:5d7a) + ld a, [wAITrainerCardToPlay] + ldh [hTempCardIndex_ff9f], a + ld a, [wAITrainerCardParameter] + ldh [hTemp_ffa0], a + ld a, [wce1a] + ldh [hTempPlayAreaLocation_ffa1], a + ld a, OPPACTION_EXECUTE_TRAINER_EFFECTS + bank1call AIMakeDecision + ret + +AIDecide_PokemonTrader: ; 21d8f (8:5d8f) +; each deck has their own routine for picking +; what Pokemon to look for. + ld a, [wOpponentDeckID] + cp LEGENDARY_MOLTRES_DECK_ID + jr z, AIDecide_PokemonTrader_LegendaryMoltres + cp LEGENDARY_ARTICUNO_DECK_ID + jr z, AIDecide_PokemonTrader_LegendaryArticuno + cp LEGENDARY_DRAGONITE_DECK_ID + jp z, AIDecide_PokemonTrader_LegendaryDragonite + cp LEGENDARY_RONALD_DECK_ID + jp z, AIDecide_PokemonTrader_LegendaryRonald + cp BLISTERING_POKEMON_DECK_ID + jp z, AIDecide_PokemonTrader_BlisteringPokemon + cp SOUND_OF_THE_WAVES_DECK_ID + jp z, AIDecide_PokemonTrader_SoundOfTheWaves + cp POWER_GENERATOR_DECK_ID + jp z, AIDecide_PokemonTrader_PowerGenerator + cp FLOWER_GARDEN_DECK_ID + jp z, AIDecide_PokemonTrader_FlowerGarden + cp STRANGE_POWER_DECK_ID + jp z, AIDecide_PokemonTrader_StrangePower + cp FLAMETHROWER_DECK_ID + jp z, AIDecide_PokemonTrader_Flamethrower + or a + ret + +AIDecide_PokemonTrader_LegendaryMoltres: ; 21dc4 (8:5dc4) +; look for Moltres2 card in deck to trade with a +; card in hand different from Moltres1. + ld a, MOLTRES2 + ld e, MOLTRES1 + call LookForCardIDToTradeWithDifferentHandCard + jr nc, .no_carry +; success + ld [wce1a], a + ld a, e + scf + ret +.no_carry + or a + ret + +AIDecide_PokemonTrader_LegendaryArticuno: ; 21dd5 (8:5dd5) +; if has none of these cards in Hand or Play Area, proceed + ld a, ARTICUNO1 + call LookForCardIDInHandAndPlayArea + jr c, .no_carry + ld a, LAPRAS + call LookForCardIDInHandAndPlayArea + jr c, .no_carry + +; if doesn't have Seel in Hand or Play Area, +; look for it in the deck. +; otherwise, look for Dewgong instead. + ld a, SEEL + call LookForCardIDInHandAndPlayArea + jr c, .dewgong + + ld e, SEEL + ld a, CARD_LOCATION_DECK + call LookForCardIDInLocation + jr nc, .dewgong + ld [wce1a], a + jr .check_hand + +.dewgong + ld a, DEWGONG + call LookForCardIDInHandAndPlayArea + jr c, .no_carry + ld e, DEWGONG + ld a, CARD_LOCATION_DECK + call LookForCardIDInLocation + jr nc, .no_carry + ld [wce1a], a + +; a Seel or Dewgong was found in deck, +; check hand for card to trade for +.check_hand + ld a, CHANSEY + call CheckIfHasCardIDInHand + jr c, .set_carry + ld a, DITTO + call CheckIfHasCardIDInHand + jr c, .set_carry + ld a, ARTICUNO2 + call CheckIfHasCardIDInHand + jr c, .set_carry + ; doesn't have any of the cards in hand + +.no_carry + or a + ret + +.set_carry + scf + ret + +AIDecide_PokemonTrader_LegendaryDragonite: ; 21e24 (8:5e24) +; if has less than 5 cards of energy +; and of Pokemon in hand/Play Area, +; target a Kangaskhan in deck. + farcall CountOppEnergyCardsInHandAndAttached + cp 5 + jr c, .kangaskhan + call CountPokemonCardsInHandAndInPlayArea + cp 5 + jr c, .kangaskhan + ; total number of energy cards >= 5 + ; total number of Pokemon cards >= 5 + +; for each of the following cards, +; first run a check if there's a pre-evolution in +; Play Area or in the hand. If there is, choose it as target. +; otherwise, check if the evolution card is in +; hand and if so, choose it as target instead. + ld b, MAGIKARP + ld a, GYARADOS + call LookForCardIDInDeck_GivenCardIDInHandAndPlayArea + jr c, .choose_hand + ld a, MAGIKARP + ld b, GYARADOS + call LookForCardIDInDeck_GivenCardIDInHand + jr c, .choose_hand + ld b, DRATINI + ld a, DRAGONAIR + call LookForCardIDInDeck_GivenCardIDInHandAndPlayArea + jr c, .choose_hand + ld b, DRAGONAIR + ld a, DRAGONITE1 + call LookForCardIDInDeck_GivenCardIDInHandAndPlayArea + jr c, .choose_hand + ld a, DRATINI + ld b, DRAGONAIR + call LookForCardIDInDeck_GivenCardIDInHand + jr c, .choose_hand + ld a, DRAGONAIR + ld b, DRAGONITE1 + call LookForCardIDInDeck_GivenCardIDInHand + jr c, .choose_hand + ld b, CHARMANDER + ld a, CHARMELEON + call LookForCardIDInDeck_GivenCardIDInHandAndPlayArea + jr c, .choose_hand + ld b, CHARMELEON + ld a, CHARIZARD + call LookForCardIDInDeck_GivenCardIDInHandAndPlayArea + jr c, .choose_hand + ld a, CHARMANDER + ld b, CHARMELEON + call LookForCardIDInDeck_GivenCardIDInHand + jr c, .choose_hand + ld a, CHARMELEON + ld b, CHARIZARD + call LookForCardIDInDeck_GivenCardIDInHand + jr c, .choose_hand + jr .no_carry + +.kangaskhan + ld e, KANGASKHAN + ld a, CARD_LOCATION_DECK + call LookForCardIDInLocation + jr nc, .no_carry + +; card was found as target in deck, +; look for card in hand to trade with +.choose_hand + ld [wce1a], a + ld a, DRAGONAIR + call CheckIfHasCardIDInHand + jr c, .set_carry + ld a, CHARMELEON + call CheckIfHasCardIDInHand + jr c, .set_carry + ld a, GYARADOS + call CheckIfHasCardIDInHand + jr c, .set_carry + ld a, MAGIKARP + call CheckIfHasCardIDInHand + jr c, .set_carry + ld a, CHARMANDER + call CheckIfHasCardIDInHand + jr c, .set_carry + ld a, DRATINI + call CheckIfHasCardIDInHand + jr c, .set_carry + ; non found + +.no_carry + or a + ret +.set_carry + scf + ret + +AIDecide_PokemonTrader_LegendaryRonald: ; 21ec9 (8:5ec9) +; for each of the following cards, +; first run a check if there's a pre-evolution in +; Play Area or in the hand. If there is, choose it as target. +; otherwise, check if the evolution card is in +; hand and if so, choose it as target instead. + ld b, EEVEE + ld a, FLAREON1 + call LookForCardIDInDeck_GivenCardIDInHandAndPlayArea + jr c, .choose_hand + ld b, EEVEE + ld a, VAPOREON1 + call LookForCardIDInDeck_GivenCardIDInHandAndPlayArea + jr c, .choose_hand + ld b, EEVEE + ld a, JOLTEON1 + call LookForCardIDInDeck_GivenCardIDInHandAndPlayArea + jr c, .choose_hand + ld a, EEVEE + ld b, FLAREON1 + call LookForCardIDInDeck_GivenCardIDInHand + jr c, .choose_hand + ld a, EEVEE + ld b, VAPOREON1 + call LookForCardIDInDeck_GivenCardIDInHand + jr c, .choose_hand + ld a, EEVEE + ld b, JOLTEON1 + call LookForCardIDInDeck_GivenCardIDInHand + jr c, .choose_hand + ld b, DRATINI + ld a, DRAGONAIR + call LookForCardIDInDeck_GivenCardIDInHandAndPlayArea + jr c, .choose_hand + ld b, DRAGONAIR + ld a, DRAGONITE1 + call LookForCardIDInDeck_GivenCardIDInHandAndPlayArea + jr c, .choose_hand + ld a, DRATINI + ld b, DRAGONAIR + call LookForCardIDInDeck_GivenCardIDInHand + jr c, .choose_hand + ld a, DRAGONAIR + ld b, DRAGONITE1 + call LookForCardIDInDeck_GivenCardIDInHand + jr c, .choose_hand + jr .no_carry + +; card was found as target in deck, +; look for card in hand to trade with +.choose_hand + ld [wce1a], a + ld a, ZAPDOS3 + call LookForCardIDInHandList_Bank8 + jr c, .set_carry + ld a, ARTICUNO2 + call LookForCardIDInHandList_Bank8 + jr c, .set_carry + ld a, MOLTRES2 + call LookForCardIDInHandList_Bank8 + jr c, .set_carry + ; none found + +.no_carry + or a + ret +.set_carry + scf + ret + +AIDecide_PokemonTrader_BlisteringPokemon: ; 21f41 (8:5f41) +; for each of the following cards, +; first run a check if there's a pre-evolution in +; Play Area or in the hand. If there is, choose it as target. +; otherwise, check if the evolution card is in +; hand and if so, choose it as target instead. + ld b, RHYHORN + ld a, RHYDON + call LookForCardIDInDeck_GivenCardIDInHandAndPlayArea + jr c, .find_duplicates + ld a, RHYHORN + ld b, RHYDON + call LookForCardIDInDeck_GivenCardIDInHand + jr c, .find_duplicates + ld b, CUBONE + ld a, MAROWAK1 + call LookForCardIDInDeck_GivenCardIDInHandAndPlayArea + jr c, .find_duplicates + ld a, CUBONE + ld b, MAROWAK1 + call LookForCardIDInDeck_GivenCardIDInHand + jr c, .find_duplicates + ld b, PONYTA + ld a, RAPIDASH + call LookForCardIDInDeck_GivenCardIDInHandAndPlayArea + jr c, .find_duplicates + ld a, PONYTA + ld b, RAPIDASH + call LookForCardIDInDeck_GivenCardIDInHand + jr c, .find_duplicates + jr .no_carry + +; a card in deck was found to look for, +; check if there are duplicates in hand to trade with. +.find_duplicates + ld [wce1a], a + call FindDuplicatePokemonCards + jr c, .set_carry +.no_carry + or a + ret +.set_carry + scf + ret + +AIDecide_PokemonTrader_SoundOfTheWaves: ; 21f85 (8:5f85) +; for each of the following cards, +; first run a check if there's a pre-evolution in +; Play Area or in the hand. If there is, choose it as target. +; otherwise, check if the evolution card is in +; hand and if so, choose it as target instead. + ld b, SEEL + ld a, DEWGONG + call LookForCardIDInDeck_GivenCardIDInHandAndPlayArea + jr c, .choose_hand + ld a, SEEL + ld b, DEWGONG + call LookForCardIDInDeck_GivenCardIDInHand + jr c, .choose_hand + ld b, KRABBY + ld a, KINGLER + call LookForCardIDInDeck_GivenCardIDInHandAndPlayArea + jr c, .choose_hand + ld a, KRABBY + ld b, KINGLER + call LookForCardIDInDeck_GivenCardIDInHand + jr c, .choose_hand + ld b, SHELLDER + ld a, CLOYSTER + call LookForCardIDInDeck_GivenCardIDInHandAndPlayArea + jr c, .choose_hand + ld a, SHELLDER + ld b, CLOYSTER + call LookForCardIDInDeck_GivenCardIDInHand + jr c, .choose_hand + ld b, HORSEA + ld a, SEADRA + call LookForCardIDInDeck_GivenCardIDInHandAndPlayArea + jr c, .choose_hand + ld a, HORSEA + ld b, SEADRA + call LookForCardIDInDeck_GivenCardIDInHand + jr c, .choose_hand + ld b, TENTACOOL + ld a, TENTACRUEL + call LookForCardIDInDeck_GivenCardIDInHandAndPlayArea + jr c, .choose_hand + ld a, TENTACOOL + ld b, TENTACRUEL + call LookForCardIDInDeck_GivenCardIDInHand + jr c, .choose_hand + jr .no_carry + +; card was found as target in deck, +; look for card in hand to trade with +.choose_hand + ld [wce1a], a + ld a, SEEL + call CheckIfHasCardIDInHand + jr c, .set_carry + ld a, KRABBY + call CheckIfHasCardIDInHand + jr c, .set_carry + ld a, HORSEA + call CheckIfHasCardIDInHand + jr c, .set_carry + ld a, SHELLDER + call CheckIfHasCardIDInHand + jr c, .set_carry + ld a, TENTACOOL + call CheckIfHasCardIDInHand + jr c, .set_carry + ; none found + +.no_carry + or a + ret +.set_carry + scf + ret + +AIDecide_PokemonTrader_PowerGenerator: ; 2200b (8:600b) +; for each of the following cards, +; first run a check if there's a pre-evolution in +; Play Area or in the hand. If there is, choose it as target. +; otherwise, check if the evolution card is in +; hand and if so, choose it as target instead. + ld b, PIKACHU2 + ld a, RAICHU1 + call LookForCardIDInDeck_GivenCardIDInHandAndPlayArea + jp c, .find_duplicates + ld b, PIKACHU1 + ld a, RAICHU1 + call LookForCardIDInDeck_GivenCardIDInHandAndPlayArea + jr c, .find_duplicates + ld a, PIKACHU2 + ld b, RAICHU1 + call LookForCardIDInDeck_GivenCardIDInHand + jr c, .find_duplicates + ld a, PIKACHU1 + ld b, RAICHU1 + call LookForCardIDInDeck_GivenCardIDInHand + jr c, .find_duplicates + ld b, VOLTORB + ld a, ELECTRODE2 + call LookForCardIDInDeck_GivenCardIDInHandAndPlayArea + jr c, .find_duplicates + ld b, VOLTORB + ld a, ELECTRODE1 + call LookForCardIDInDeck_GivenCardIDInHandAndPlayArea + jr c, .find_duplicates + ld a, VOLTORB + ld b, ELECTRODE2 + call LookForCardIDInDeck_GivenCardIDInHand + jr c, .find_duplicates + ld a, VOLTORB + ld b, ELECTRODE1 + call LookForCardIDInDeck_GivenCardIDInHand + jr c, .find_duplicates + ld b, MAGNEMITE1 + ld a, MAGNETON2 + call LookForCardIDInDeck_GivenCardIDInHandAndPlayArea + jr c, .find_duplicates + ld b, MAGNEMITE2 + ld a, MAGNETON2 + call LookForCardIDInDeck_GivenCardIDInHandAndPlayArea + jr c, .find_duplicates + ld b, MAGNEMITE1 + ld a, MAGNETON1 + call LookForCardIDInDeck_GivenCardIDInHandAndPlayArea + jr c, .find_duplicates + ld b, MAGNEMITE2 + ld a, MAGNETON1 + call LookForCardIDInDeck_GivenCardIDInHandAndPlayArea + jr c, .find_duplicates + ld a, MAGNEMITE2 + ld b, MAGNETON2 + call LookForCardIDInDeck_GivenCardIDInHand + jr c, .find_duplicates + ld a, MAGNEMITE1 + ld b, MAGNETON2 + call LookForCardIDInDeck_GivenCardIDInHand + jr c, .find_duplicates + ld a, MAGNEMITE2 + ld b, MAGNETON1 + call LookForCardIDInDeck_GivenCardIDInHand + jr c, .find_duplicates + ld a, MAGNEMITE1 + ld b, MAGNETON1 + call LookForCardIDInDeck_GivenCardIDInHand + jr c, .find_duplicates + ; bug, missing jr .no_carry + +; since this last check falls through regardless of result, +; register a might hold an invalid deck index, +; which might lead to hilarious results like Brandon +; trading a Pikachu with a Grass Energy from the deck. +; however, since it's deep in a tower of conditionals, +; reaching here is extremely unlikely. + +; a card in deck was found to look for, +; check if there are duplicates in hand to trade with. +.find_duplicates + ld [wce1a], a + call FindDuplicatePokemonCards + jr c, .set_carry + or a + ret +.set_carry + scf + ret + +AIDecide_PokemonTrader_FlowerGarden: ; 220a8 (8:60a8) +; for each of the following cards, +; first run a check if there's a pre-evolution in +; Play Area or in the hand. If there is, choose it as target. +; otherwise, check if the evolution card is in +; hand and if so, choose it as target instead. + ld b, BULBASAUR + ld a, IVYSAUR + call LookForCardIDInDeck_GivenCardIDInHandAndPlayArea + jr c, .find_duplicates + ld b, IVYSAUR + ld a, VENUSAUR2 + call LookForCardIDInDeck_GivenCardIDInHandAndPlayArea + jr c, .find_duplicates + ld a, BULBASAUR + ld b, IVYSAUR + call LookForCardIDInDeck_GivenCardIDInHand + jr c, .find_duplicates + ld a, IVYSAUR + ld b, VENUSAUR2 + call LookForCardIDInDeck_GivenCardIDInHand + jr c, .find_duplicates + ld b, BELLSPROUT + ld a, WEEPINBELL + call LookForCardIDInDeck_GivenCardIDInHandAndPlayArea + jr c, .find_duplicates + ld b, WEEPINBELL + ld a, VICTREEBEL + call LookForCardIDInDeck_GivenCardIDInHandAndPlayArea + jr c, .find_duplicates + ld a, BELLSPROUT + ld b, WEEPINBELL + call LookForCardIDInDeck_GivenCardIDInHand + jr c, .find_duplicates + ld a, WEEPINBELL + ld b, VICTREEBEL + call LookForCardIDInDeck_GivenCardIDInHand + jr c, .find_duplicates + ld b, ODDISH + ld a, GLOOM + call LookForCardIDInDeck_GivenCardIDInHandAndPlayArea + jr c, .find_duplicates + ld b, GLOOM + ld a, VILEPLUME + call LookForCardIDInDeck_GivenCardIDInHandAndPlayArea + jr c, .find_duplicates + ld a, ODDISH + ld b, GLOOM + call LookForCardIDInDeck_GivenCardIDInHand + jr c, .find_duplicates + ld a, GLOOM + ld b, VILEPLUME + call LookForCardIDInDeck_GivenCardIDInHand + jr c, .find_duplicates + jr .no_carry + +; a card in deck was found to look for, +; check if there are duplicates in hand to trade with. +.find_duplicates + ld [wce1a], a + call FindDuplicatePokemonCards + jr c, .found +.no_carry + or a + ret +.found + scf + ret + +AIDecide_PokemonTrader_StrangePower: ; 22122 (8:6122) +; looks for a Pokemon in hand to trade with Mr Mime in deck. +; inputting Mr Mime in register e for the function is redundant +; since it already checks whether a Mr Mime exists in the hand. + ld a, MR_MIME + ld e, MR_MIME + call LookForCardIDToTradeWithDifferentHandCard + jr nc, .no_carry +; found + ld [wce1a], a + ld a, e + scf + ret +.no_carry + or a + ret + +AIDecide_PokemonTrader_Flamethrower: ; 22133 (8:6133) +; for each of the following cards, +; first run a check if there's a pre-evolution in +; Play Area or in the hand. If there is, choose it as target. +; otherwise, check if the evolution card is in +; hand and if so, choose it as target instead. + ld b, CHARMANDER + ld a, CHARMELEON + call LookForCardIDInDeck_GivenCardIDInHandAndPlayArea + jr c, .find_duplicates + ld b, CHARMELEON + ld a, CHARIZARD + call LookForCardIDInDeck_GivenCardIDInHandAndPlayArea + jr c, .find_duplicates + ld a, CHARMANDER + ld b, CHARMELEON + call LookForCardIDInDeck_GivenCardIDInHand + jr c, .find_duplicates + ld a, CHARMELEON + ld b, CHARIZARD + call LookForCardIDInDeck_GivenCardIDInHand + jr c, .find_duplicates + ld b, VULPIX + ld a, NINETALES1 + call LookForCardIDInDeck_GivenCardIDInHandAndPlayArea + jr c, .find_duplicates + ld a, VULPIX + ld b, NINETALES1 + call LookForCardIDInDeck_GivenCardIDInHand + jr c, .find_duplicates + ld b, GROWLITHE + ld a, ARCANINE2 + call LookForCardIDInDeck_GivenCardIDInHandAndPlayArea + jr c, .find_duplicates + ld a, GROWLITHE + ld b, ARCANINE2 + call LookForCardIDInDeck_GivenCardIDInHand + jr c, .find_duplicates + ld b, EEVEE + ld a, FLAREON2 + call LookForCardIDInDeck_GivenCardIDInHandAndPlayArea + jr c, .find_duplicates + ld a, EEVEE + ld b, FLAREON2 + call LookForCardIDInDeck_GivenCardIDInHand + jr c, .find_duplicates + jr .no_carry + +; a card in deck was found to look for, +; check if there are duplicates in hand to trade with. +.find_duplicates + ld [wce1a], a + call FindDuplicatePokemonCards + jr c, .set_carry +.no_carry + or a + ret +.set_carry + scf + ret diff --git a/src/engine/duel/effect_functions.asm b/src/engine/duel/effect_functions.asm new file mode 100644 index 0000000..ce3a517 --- /dev/null +++ b/src/engine/duel/effect_functions.asm @@ -0,0 +1,11194 @@ +Poison50PercentEffect: ; 2c000 (b:4000) + ldtx de, PoisonCheckText + call TossCoin_BankB + ret nc + +PoisonEffect: ; 2c007 (b:4007) + lb bc, CNF_SLP_PRZ, POISONED + jr ApplyStatusEffect + +DoublePoisonEffect: ; 2c00c (b:400c) + lb bc, CNF_SLP_PRZ, DOUBLE_POISONED + jr ApplyStatusEffect + +Paralysis50PercentEffect: ; 2c011 (b:4011) + ldtx de, ParalysisCheckText + call TossCoin_BankB + ret nc + +ParalysisEffect: ; 2c018 (b:4018) + lb bc, PSN_DBLPSN, PARALYZED + jr ApplyStatusEffect + +Confusion50PercentEffect: ; 2c01d (b:401d) + ldtx de, ConfusionCheckText + call TossCoin_BankB + ret nc + +ConfusionEffect: ; 2c024 (b:4024) + lb bc, PSN_DBLPSN, CONFUSED + jr ApplyStatusEffect + +Sleep50PercentEffect: ; 2c029 (b:4029) + ldtx de, SleepCheckText + call TossCoin_BankB + ret nc + +SleepEffect: ; 2c030 (b:4030) + lb bc, PSN_DBLPSN, ASLEEP + jr ApplyStatusEffect + +ApplyStatusEffect: ; 2c035 (b:4035) + ldh a, [hWhoseTurn] + ld hl, wWhoseTurn + cp [hl] + jr nz, .can_induce_status + ld a, [wTempNonTurnDuelistCardID] + cp CLEFAIRY_DOLL + jr z, .cant_induce_status + cp MYSTERIOUS_FOSSIL + jr z, .cant_induce_status + ; Snorlax's Thick Skinned prevents it from being statused... + cp SNORLAX + jr nz, .can_induce_status + call SwapTurn + xor a + ; ...unless already so, or if affected by Muk's Toxic Gas + call CheckCannotUseDueToStatus_OnlyToxicGasIfANon0 + call SwapTurn + jr c, .can_induce_status + +.cant_induce_status + ld a, c + ld [wNoEffectFromWhichStatus], a + call SetNoEffectFromStatus + or a + ret + +.can_induce_status + ld hl, wEffectFunctionsFeedbackIndex + push hl + ld e, [hl] + ld d, $0 + ld hl, wEffectFunctionsFeedback + add hl, de + call SwapTurn + ldh a, [hWhoseTurn] + ld [hli], a + call SwapTurn + ld [hl], b ; mask of status conditions not to discard on the target + inc hl + ld [hl], c ; status condition to inflict to the target + pop hl + ; advance wEffectFunctionsFeedbackIndex + inc [hl] + inc [hl] + inc [hl] + scf + ret + +TossCoin_BankB: ; 2c07e (b:407e) + call TossCoin + ret + +TossCoinATimes_BankB: ; 2c082 (b:4082) + call TossCoinATimes + ret + +CommentedOut_2c086: ; 2c086 (b:4086) + ret + +Func_2c087: ; 2c087 (b:4087) + xor a + jr Func_2c08c + +Func_2c08a: ; 2c08a (b:408a) + ld a, $1 + +Func_2c08c: ; 2c08c (b:408c) + push de + push af + ld a, OPPACTION_TOSS_COIN_A_TIMES + call SetOppAction_SerialSendDuelData + pop af + pop de + call SerialSend8Bytes + call TossCoinATimes + ret + +SetNoEffectFromStatus: ; 2c09c (b:409c) + ld a, EFFECT_FAILED_NO_EFFECT + ld [wEffectFailed], a + ret + +SetWasUnsuccessful: ; 2c0a2 (b:40a2) + ld a, EFFECT_FAILED_UNSUCCESSFUL + ld [wEffectFailed], a + ret + +Func_2c0a8: ; 2c0a8 (b:40a8) + ldh a, [hTemp_ffa0] + push af + ldh a, [hWhoseTurn] + ldh [hTemp_ffa0], a + ld a, OPPACTION_6B30 + call SetOppAction_SerialSendDuelData + bank1call Func_4f2d + ld c, a + pop af + ldh [hTemp_ffa0], a + ld a, c + ret + +Func_2c0bd: ; 2c0bd (b:40bd) + call ExchangeRNG + bank1call Func_4f2d + call ShuffleDeck + ret + +; return carry if Player is the Turn Duelist +IsPlayerTurn: ; 2c0c7 (b:40c7) + ld a, DUELVARS_DUELIST_TYPE + call GetTurnDuelistVariable + cp DUELIST_TYPE_PLAYER + jr z, .player + or a + ret +.player + scf + ret + +; Stores information about the attack damage for AI purposes +; taking into account poison damage between turns. +; if target poisoned +; [wAIMinDamage] <- [wDamage] +; [wAIMaxDamage] <- [wDamage] +; else +; [wAIMinDamage] <- [wDamage] + d +; [wAIMaxDamage] <- [wDamage] + e +; [wDamage] <- [wDamage] + a +UpdateExpectedAIDamage_AccountForPoison: ; 2c0d4 (b:40d4) + push af + ld a, DUELVARS_ARENA_CARD_STATUS + call GetNonTurnDuelistVariable + and POISONED | DOUBLE_POISONED + jr z, UpdateExpectedAIDamage.skip_push_af + pop af + ld a, [wDamage] + ld [wAIMinDamage], a + ld [wAIMaxDamage], a + ret + +; Sets some variables for AI use +; [wAIMinDamage] <- [wDamage] + d +; [wAIMaxDamage] <- [wDamage] + e +; [wDamage] <- [wDamage] + a +UpdateExpectedAIDamage: ; 2c0e9 (b:40e9) + push af + +.skip_push_af + ld hl, wDamage + ld a, [hl] + add d + ld [wAIMinDamage], a + ld a, [hl] + add e + ld [wAIMaxDamage], a + pop af + add [hl] + ld [hl], a + ret + +; Stores information about the attack damage for AI purposes +; [wDamage] <- a (average amount of damage) +; [wAIMinDamage] <- d (minimum) +; [wAIMaxDamage] <- e (maximum) +SetExpectedAIDamage: ; 2c0fb (b:40fb) + ld [wDamage], a + xor a + ld [wDamage + 1], a + ld a, d + ld [wAIMinDamage], a + ld a, e + ld [wAIMaxDamage], a + ret + +Func_2c10b: ; 2c10b (b:410b) + ldh [hTempPlayAreaLocation_ff9d], a + bank1call Func_61a1 + bank1call PrintPlayAreaCardList_EnableLCD + bank1call Func_6194 + ret + +; deal damage to all the turn holder's benched Pokemon +; input: a = amount of damage to deal to each Pokemon +DealDamageToAllBenchedPokemon: ; 2c117 (b:4117) + ld e, a + ld d, $00 + ld a, DUELVARS_NUMBER_OF_POKEMON_IN_PLAY_AREA + call GetTurnDuelistVariable + ld c, a + ld b, PLAY_AREA_ARENA + jr .skip_to_bench +.loop + push bc + call DealDamageToPlayAreaPokemon_RegularAnim + pop bc +.skip_to_bench + inc b + dec c + jr nz, .loop + ret + +Func_2c12e: ; 2c12e (b:412e) + ld [wLoadedAttackAnimation], a + ldh a, [hTempPlayAreaLocation_ff9d] + ld b, a + ld c, $0 ; neither WEAKNESS nor RESISTANCE + ldh a, [hWhoseTurn] + ld h, a + bank1call PlayAttackAnimation + bank1call WaitAttackAnimation + ret + +; apply a status condition of type 1 identified by register a to the target +ApplySubstatus1ToDefendingCard: ; 2c140 (b:4140) + push af + ld a, DUELVARS_ARENA_CARD_SUBSTATUS1 + call GetTurnDuelistVariable + pop af + ld [hli], a + ret + +; apply a status condition of type 2 identified by register a to the target, +; unless prevented by wNoDamageOrEffect +ApplySubstatus2ToDefendingCard: ; 2c149 (b:4149) + push af + call CheckNoDamageOrEffect + jr c, .no_damage_orEffect + ld a, DUELVARS_ARENA_CARD_SUBSTATUS2 + call GetNonTurnDuelistVariable + pop af + ld [hl], a + ld l, $f6 + ld [hl], a + ret + +.no_damage_orEffect + pop af + push hl + bank1call DrawDuelMainScene + pop hl + ld a, l + or h + call nz, DrawWideTextBox_PrintText + ret + +; overwrites in wDamage, wAIMinDamage and wAIMaxDamage +; with the value in a. +SetDefiniteDamage: ; 2c166 (b:4166) + ld [wDamage], a + ld [wAIMinDamage], a + ld [wAIMaxDamage], a + xor a + ld [wDamage + 1], a + ret + +; overwrites wAIMinDamage and wAIMaxDamage +; with value in wDamage. +SetDefiniteAIDamage: ; 2c174 (b:4174) + ld a, [wDamage] + ld [wAIMinDamage], a + ld [wAIMaxDamage], a + ret + +; returns in a some random occupied Play Area location +; in Turn Duelist's Play Area. +PickRandomPlayAreaCard: ; 2c17e (b:417e) + ld a, DUELVARS_NUMBER_OF_POKEMON_IN_PLAY_AREA + call GetTurnDuelistVariable + call Random + or a + ret + +; outputs in hl the next position +; in hTempList to place a new card, +; and increments hCurSelectionItem. +GetNextPositionInTempList: ; 2c188 (b:4188) + push de + ld hl, hCurSelectionItem + ld a, [hl] + inc [hl] + ld e, a + ld d, $00 + ld hl, hTempList + add hl, de + pop de + ret + +; creates in wDuelTempList list of attached Fire Energy cards +; that are attached to the Turn Duelist's Arena card. +CreateListOfFireEnergyAttachedToArena: ; 2c197 (b:4197) + ld a, TYPE_ENERGY_FIRE + ; fallthrough + +; creates in wDuelTempList a list of cards that +; are in the Arena of the same type as input a. +; this is called to list Energy cards of a specific type +; that are attached to the Arena Pokemon. +; input: +; a = TYPE_ENERGY_* constant +; output: +; a = number of cards in list; +; wDuelTempList filled with cards, terminated by $ff +CreateListOfEnergyAttachedToArena: ; 2c199 (b:4199) + ld b, a + ld c, 0 + ld de, wDuelTempList + ld a, DUELVARS_CARD_LOCATIONS + call GetTurnDuelistVariable +.loop + ld a, [hl] + cp CARD_LOCATION_ARENA + jr nz, .next + push de + ld a, l + call GetCardIDFromDeckIndex + call GetCardType + pop de + cp b + jr nz, .next ; is same as input type? + ld a, l + ld [de], a + inc de + inc c +.next + inc l + ld a, l + cp DECK_SIZE + jr c, .loop + + ld a, $ff + ld [de], a + ld a, c + ret + +; prints the text " devolved to !" with +; the proper card names and levels. +; input: +; d = deck index of the lower stage card +; e = deck index of card that was devolved +PrintDevolvedCardNameAndLevelText: ; 2c1c4 (b:41c4) + push de + ld a, e + call LoadCardDataToBuffer1_FromDeckIndex + ld bc, wTxRam2 + ld hl, wLoadedCard1Name + ld a, [hli] + ld [bc], a + inc bc + ld a, [hl] + ld [bc], a + + inc bc ; wTxRam2_b + xor a + ld [bc], a + inc bc + ld [bc], a + + ld a, d + call LoadCardDataToBuffer1_FromDeckIndex + ld a, 18 + call CopyCardNameAndLevel + ld [hl], $00 + ldtx hl, PokemonDevolvedToText + call DrawWideTextBox_WaitForInput + pop de + ret + +HandleSwitchDefendingPokemonEffect: ; 2c1ec (b:41ec) + ld e, a + cp $ff + ret z + +; check Defending Pokemon's HP + ld a, DUELVARS_ARENA_CARD_HP + call GetNonTurnDuelistVariable + or a + jr nz, .switch + +; if 0, handle Destiny Bond first + push de + bank1call HandleDestinyBondSubstatus + pop de + +.switch + call HandleNoDamageOrEffect + ret c + +; attack was successful, switch Defending Pokemon + call SwapTurn + call SwapArenaWithBenchPokemon + call SwapTurn + + xor a + ld [wccc5], a + ld [wDuelDisplayedScreen], a + inc a + ld [wccef], a + ret + +; returns carry if Defending has No Damage or Effect +; if so, print its appropriate text. +HandleNoDamageOrEffect: ; 2c216 (b:4216) + call CheckNoDamageOrEffect + ret nc + ld a, l + or h + call nz, DrawWideTextBox_PrintText + scf + ret + +; applies HP recovery on Pokemon after an attack +; with HP recovery effect, and handles its animation. +; input: +; d = damage effectiveness +; e = HP amount to recover +ApplyAndAnimateHPRecovery: ; 2c221 (b:4221) + push de + ld hl, wccbd + ld [hl], e + inc hl + ld [hl], d + +; get Arena card's damage + ld e, PLAY_AREA_ARENA + call GetCardDamageAndMaxHP + pop de + or a + ret z ; return if no damage + +; load correct animation + push de + ld a, ATK_ANIM_HEAL + ld [wLoadedAttackAnimation], a + ld bc, $01 ; arrow + bank1call PlayAttackAnimation + +; compare HP to be restored with max HP +; if HP to be restored would cause HP to +; be larger than max HP, cap it accordingly + ld e, PLAY_AREA_ARENA + call GetCardDamageAndMaxHP + ld b, $00 + pop de + ld a, DUELVARS_ARENA_CARD_HP + call GetTurnDuelistVariable + add e + ld e, a + ld a, 0 + adc d + ld d, a + ; de = damage dealt + current HP + ; bc = max HP of card + call CompareDEtoBC + jr c, .skip_cap + ; cap de to value in bc + ld e, c + ld d, b + +.skip_cap + ld [hl], e ; apply new HP to arena card + bank1call WaitAttackAnimation + ret + +; returns carry if Play Area has no damage counters. +CheckIfPlayAreaHasAnyDamage: ; 2c25b (b:425b) + ld a, DUELVARS_NUMBER_OF_POKEMON_IN_PLAY_AREA + call GetTurnDuelistVariable + ld d, a + ld e, PLAY_AREA_ARENA +.loop_play_area + call GetCardDamageAndMaxHP + or a + ret nz ; found damage + inc e + dec d + jr nz, .loop_play_area + ; no damage found + scf + ret + +; makes a list in wDuelTempList with the deck indices +; of Trainer cards found in Turn Duelist's Discard Pile. +; returns carry set if no Trainer cards found, and loads +; corresponding text to notify this. +CreateTrainerCardListFromDiscardPile: ; 2c26e (b:426e) +; get number of cards in Discard Pile +; and have hl point to the end of the +; Discard Pile list in wOpponentDeckCards. + ld a, DUELVARS_NUMBER_OF_CARDS_IN_DISCARD_PILE + call GetTurnDuelistVariable + ld b, a + add DUELVARS_DECK_CARDS + ld l, a + + ld de, wDuelTempList + inc b + jr .next_card + +.check_trainer + ld a, [hl] + call LoadCardDataToBuffer2_FromDeckIndex + ld a, [wLoadedCard2Type] + cp TYPE_TRAINER + jr nz, .next_card + + ld a, [hl] + ld [de], a + inc de + +.next_card + dec l + dec b + jr nz, .check_trainer + + ld a, $ff ; terminating byte + ld [de], a + ld a, [wDuelTempList] + cp $ff + jr z, .no_trainers + or a + ret +.no_trainers + ldtx hl, ThereAreNoTrainerCardsInDiscardPileText + scf + ret + +; makes a list in wDuelTempList with the deck indices +; of all basic energy cards found in Turn Duelist's Discard Pile. +CreateEnergyCardListFromDiscardPile_OnlyBasic: ; 2c2a0 (b:42a0) + ld c, $01 + jr CreateEnergyCardListFromDiscardPile + +; makes a list in wDuelTempList with the deck indices +; of all energy cards (including Double Colorless) +; found in Turn Duelist's Discard Pile. +CreateEnergyCardListFromDiscardPile_AllEnergy: ; 2c2a4 (b:42a4) + ld c, $00 +; fallthrough + +; makes a list in wDuelTempList with the deck indices +; of energy cards found in Turn Duelist's Discard Pile. +; if (c == 0), all energy cards are allowed; +; if (c != 0), double colorless energy cards are not included. +; returns carry if no energy cards were found. +CreateEnergyCardListFromDiscardPile: ; 2c2a6 (b:42a6) +; get number of cards in Discard Pile +; and have hl point to the end of the +; Discard Pile list in wOpponentDeckCards. + ld a, DUELVARS_NUMBER_OF_CARDS_IN_DISCARD_PILE + call GetTurnDuelistVariable + ld b, a + add DUELVARS_DECK_CARDS + ld l, a + + ld de, wDuelTempList + inc b + jr .next_card + +.check_energy + ld a, [hl] + call LoadCardDataToBuffer2_FromDeckIndex + ld a, [wLoadedCard2Type] + and TYPE_ENERGY + jr z, .next_card + +; if (c != $00), then we dismiss Double Colorless +; energy cards found. + ld a, c + or a + jr z, .copy + ld a, [wLoadedCard2Type] + cp TYPE_ENERGY_DOUBLE_COLORLESS + jr nc, .next_card + +.copy + ld a, [hl] + ld [de], a + inc de + +; goes through Discard Pile list +; in wOpponentDeckCards in descending order. +.next_card + dec l + dec b + jr nz, .check_energy + +; terminating byte on wDuelTempList + ld a, $ff + ld [de], a + +; check if any energy card was found +; by checking whether the first byte +; in wDuelTempList is $ff. +; if none were found, return carry. + ld a, [wDuelTempList] + cp $ff + jr z, .set_carry + or a + ret + +.set_carry + scf + ret + +; returns carry if Deck is empty +CheckIfDeckIsEmpty: ; 2c2e0 (b:42e0) + ld a, DUELVARS_NUMBER_OF_CARDS_NOT_IN_DECK + call GetTurnDuelistVariable + ldtx hl, NoCardsLeftInTheDeckText + cp DECK_SIZE + ccf + ret + +; searches through Deck in wDuelTempList looking for +; a certain card or cards, and prints text depending +; on whether at least one was found. +; if none were found, asks the Player whether to look +; in the Deck anyway, and returns carry if No is selected. +; uses SEARCHEFFECT_* as input which determines what to search for: +; SEARCHEFFECT_CARD_ID = search for card ID in e +; SEARCHEFFECT_NIDORAN = search for either NidoranM or NidoranF +; SEARCHEFFECT_BASIC_FIGHTING = search for any Basic Fighting Pokemon +; SEARCHEFFECT_BASIC_ENERGY = search for any Basic Energy +; SEARCHEFFECT_POKEMON = search for any Pokemon card +; input: +; d = SEARCHEFFECT_* constant +; e = (optional) card ID to search for in deck +; hl = text to print if Deck has card(s) +; output: +; carry set if refused to look at deck +LookForCardsInDeck: ; 2c2ec (b:42ec) + push hl + push bc + ld a, [wDuelTempList] + cp $ff + jr z, .none_in_deck + ld a, d + ld hl, .search_table + call JumpToFunctionInTable + jr c, .none_in_deck + pop bc + pop hl + call DrawWideTextBox_WaitForInput + or a + ret + +.none_in_deck + pop hl + call LoadTxRam2 + pop hl + ldtx hl, ThereIsNoInTheDeckText + call DrawWideTextBox_WaitForInput + ldtx hl, WouldYouLikeToCheckTheDeckText + call YesOrNoMenuWithText_SetCursorToYes + ret + +.search_table + dw .SearchDeckForCardID + dw .SearchDeckForNidoran + dw .SearchDeckForBasicFighting + dw .SearchDeckForBasicEnergy + dw .SearchDeckForPokemon + +.set_carry ; 2c321 (b:4321) + scf + ret + +; returns carry if no card with +; same card ID as e is found in Deck +.SearchDeckForCardID ; 2c323 (b:4323) + ld hl, wDuelTempList +.loop_deck_e + ld a, [hli] + cp $ff + jr z, .set_carry + push de + call GetCardIDFromDeckIndex + ld a, e + pop de + cp e + jr nz, .loop_deck_e + or a + ret + +; returns carry if no NidoranM or NidoranF card is found in Deck +.SearchDeckForNidoran ; 2c336 (b:4336) + ld hl, wDuelTempList +.loop_deck_nidoran + ld a, [hli] + cp $ff + jr z, .set_carry + call GetCardIDFromDeckIndex + ld a, e + cp NIDORANF + jr z, .found_nidoran + cp NIDORANM + jr nz, .loop_deck_nidoran +.found_nidoran + or a + ret + +; returns carry if no Basic Fighting Pokemon is found in Deck +.SearchDeckForBasicFighting ; 2c34c (b:434c) + ld hl, wDuelTempList +.loop_deck_fighting + ld a, [hli] + cp $ff + jr z, .set_carry + call LoadCardDataToBuffer2_FromDeckIndex + ld a, [wLoadedCard2Type] + cp TYPE_PKMN_FIGHTING + jr nz, .loop_deck_fighting + ld a, [wLoadedCard2Stage] + or a ; BASIC + jr nz, .loop_deck_fighting + ret + +; returns carry if no Basic Energy cards are found in Deck +.SearchDeckForBasicEnergy ; 2c365 (b:4365) + ld hl, wDuelTempList +.loop_deck_energy + ld a, [hli] + cp $ff + jr z, .set_carry + call GetCardIDFromDeckIndex + call GetCardType + cp TYPE_ENERGY_DOUBLE_COLORLESS + jr z, .loop_deck_energy + and TYPE_ENERGY + jr z, .loop_deck_energy + or a + ret + +; returns carry if no Pokemon cards are found in Deck +.SearchDeckForPokemon ; 2c37d (b:437d) + ld hl, wDuelTempList +.loop_deck_pkmn + ld a, [hli] + cp $ff + jr z, .set_carry + call GetCardIDFromDeckIndex + call GetCardType + cp TYPE_ENERGY + jr nc, .loop_deck_pkmn + or a + ret + +; handles the Player selection of attack +; to use, i.e. Amnesia or Metronome on. +; returns carry if none selected. +; outputs: +; d = card index of defending card +; e = attack index selected +HandleDefendingPokemonAttackSelection: ; 2c391 (b:4391) + bank1call DrawDuelMainScene + call SwapTurn + xor a + ldh [hCurSelectionItem], a + +.start + bank1call PrintAndLoadAttacksToDuelTempList + push af + ldh a, [hCurSelectionItem] + ld hl, .menu_parameters + call InitializeMenuParameters + pop af + + ld [wNumMenuItems], a + call EnableLCD +.loop_input + call DoFrame + ldh a, [hKeysPressed] + bit B_BUTTON_F, a + jr nz, .set_carry + and START + jr nz, .open_atk_page + call HandleMenuInput + jr nc, .loop_input + cp -1 + jr z, .loop_input + +; an attack was selected + ldh a, [hCurMenuItem] + add a + ld e, a + ld d, $00 + ld hl, wDuelTempList + add hl, de + ld d, [hl] + inc hl + ld e, [hl] + call SwapTurn + or a + ret + +.set_carry + call SwapTurn + scf + ret + +.open_atk_page + ldh a, [hCurMenuItem] + ldh [hCurSelectionItem], a + ld a, DUELVARS_ARENA_CARD + call GetTurnDuelistVariable + call LoadCardDataToBuffer1_FromDeckIndex + bank1call OpenAttackPage + call SwapTurn + bank1call DrawDuelMainScene + call SwapTurn + jr .start + +.menu_parameters + db 1, 13 ; cursor x, cursor y + db 2 ; y displacement between items + db 2 ; number of items + db SYM_CURSOR_R ; cursor tile number + db SYM_SPACE ; tile behind cursor + dw NULL ; function pointer if non-0 + +; loads in hl the pointer to attack's name. +; input: +; d = deck index of card +; e = attack index (0 = first attack, 1 = second attack) +GetAttackName: ; 2c3fc (b:43fc) + ld a, d + call LoadCardDataToBuffer1_FromDeckIndex + ld hl, wLoadedCard1Atk1Name + inc e + dec e + jr z, .load_name + ld hl, wLoadedCard1Atk2Name +.load_name + ld a, [hli] + ld h, [hl] + ld l, a + ret + +; returns carry if Defending Pokemon +; doesn't have an attack. +CheckIfDefendingPokemonHasAnyAttack: ; 2c40e (b:440e) + call SwapTurn + ld a, DUELVARS_ARENA_CARD + call GetTurnDuelistVariable + call LoadCardDataToBuffer2_FromDeckIndex + ld a, [wLoadedCard2Atk1Category] + cp POKEMON_POWER + jr nz, .has_attack + ld hl, wLoadedCard2Atk2Name + ld a, [hli] + or [hl] + jr nz, .has_attack + call SwapTurn + scf + ret +.has_attack + call SwapTurn + or a + ret + +; overwrites HP and Stage data of the card that was +; devolved in the Play Area to the values of new card. +; if the damage exceeds HP of pre-evolution, +; then HP is set to zero. +; input: +; a = card index of pre-evolved card +UpdateDevolvedCardHPAndStage: ; 2c431 (b:4431) + push bc + push de + push af + ldh a, [hTempPlayAreaLocation_ff9d] + ld e, a + call GetCardDamageAndMaxHP + ld b, a ; store damage + ld a, e + add DUELVARS_ARENA_CARD + call GetTurnDuelistVariable + pop af + + ld [hl], a + call LoadCardDataToBuffer2_FromDeckIndex + ld a, e + add DUELVARS_ARENA_CARD_HP + ld l, a + ld a, [wLoadedCard2HP] + sub b ; subtract damage from new HP + jr nc, .got_hp + ; damage exceeds HP + xor a ; 0 HP +.got_hp + ld [hl], a + ld a, e +; overwrite card stage + add DUELVARS_ARENA_CARD_STAGE + ld l, a + ld a, [wLoadedCard2Stage] + ld [hl], a + pop de + pop bc + ret + +; reset various status after devolving card. +ResetDevolvedCardStatus: ; 2c45d (b:445d) +; if it's Arena card, clear status conditions + ldh a, [hTempPlayAreaLocation_ff9d] + or a + jr nz, .skip_clear_status + call ClearAllStatusConditions +.skip_clear_status +; reset changed color status + ldh a, [hTempPlayAreaLocation_ff9d] + add DUELVARS_ARENA_CARD_CHANGED_TYPE + call GetTurnDuelistVariable + ld [hl], $00 +; reset C2 flags + ldh a, [hTempPlayAreaLocation_ff9d] + add DUELVARS_ARENA_CARD_FLAGS + ld l, a + ld [hl], $00 + ret + +; prompts the Player with a Yes/No question +; whether to quit the screen, even though +; they can select more cards from list. +; [hCurSelectionItem] holds number of cards +; that were already selected by the Player. +; input: +; - a = total number of cards that can be selected +; output: +; - carry set if "No" was selected +AskWhetherToQuitSelectingCards: ; 2c476 (b:4476) + ld hl, hCurSelectionItem + sub [hl] + ld l, a + ld h, $00 + call LoadTxRam3 + ldtx hl, YouCanSelectMoreCardsQuitText + call YesOrNoMenuWithText + ret + +; handles the selection of a forced switch by link/AI opponent or by the player. +; outputs the Play Area location of the chosen bench card in hTempPlayAreaLocation_ff9d. +DuelistSelectForcedSwitch: ; 2c487 (b:4487) + ld a, DUELVARS_DUELIST_TYPE + call GetNonTurnDuelistVariable + cp DUELIST_TYPE_LINK_OPP + jr z, .link_opp + + cp DUELIST_TYPE_PLAYER + jr z, .player + +; AI opponent + call SwapTurn + bank1call AIDoAction_ForcedSwitch + call SwapTurn + + ld a, [wPlayerAttackingAttackIndex] + ld e, a + ld a, [wPlayerAttackingCardIndex] + ld d, a + ld a, [wPlayerAttackingCardID] + call CopyAttackDataAndDamage_FromCardID + call Func_16f6 + ret + +.player + ldtx hl, SelectPkmnOnBenchToSwitchWithActiveText + call DrawWideTextBox_WaitForInput + call SwapTurn + bank1call HasAlivePokemonInBench + ld a, $01 + ld [wcbd4], a +.asm_2c4c0 + bank1call OpenPlayAreaScreenForSelection + jr c, .asm_2c4c0 + call SwapTurn + ret + +.link_opp +; get selection from link opponent + ld a, OPPACTION_FORCE_SWITCH_ACTIVE + call SetOppAction_SerialSendDuelData +.loop + call SerialRecvByte + jr nc, .received + halt + nop + jr .loop +.received + ldh [hTempPlayAreaLocation_ff9d], a + ret + +; returns in a the card index of energy card +; attached to Defending Pokemon +; that is to be discarded by the AI for an effect. +; outputs $ff is none was found. +; output: +; a = deck index of attached energy card chosen +AIPickEnergyCardToDiscardFromDefendingPokemon: ; 2c4da (b:44da) + call SwapTurn + ld e, PLAY_AREA_ARENA + call GetPlayAreaCardAttachedEnergies + + xor a + call CreateArenaOrBenchEnergyCardList + jr nc, .has_energy + ; no energy, return + ld a, $ff + jr .done + +.has_energy + ld a, DUELVARS_ARENA_CARD + call GetTurnDuelistVariable + call LoadCardDataToBuffer1_FromDeckIndex + ld e, COLORLESS + ld a, [wAttachedEnergies + COLORLESS] + or a + jr nz, .pick_color ; has colorless attached? + + ; no colorless energy attached. + ; if it's colorless Pokemon, just + ; pick any energy card at random... + ld a, [wLoadedCard1Type] + cp COLORLESS + jr nc, .choose_random + + ; ...if not, check if it has its + ; own color energy attached. + ; if it doesn't, pick at random. + ld e, a + ld d, $00 + ld hl, wAttachedEnergies + add hl, de + ld a, [hl] + or a + jr z, .choose_random + +; pick attached card with same color as e +.pick_color + ld hl, wDuelTempList +.loop_energy + ld a, [hli] + cp $ff + jr z, .choose_random + call LoadCardDataToBuffer2_FromDeckIndex + ld a, [wLoadedCard2Type] + and TYPE_PKMN + cp e + jr nz, .loop_energy + dec hl + +.done_chosen + ld a, [hl] +.done + call SwapTurn + ret + +.choose_random + call CountCardsInDuelTempList + ld hl, wDuelTempList + call ShuffleCards + jr .done_chosen + +; handles AI logic to pick attack for Amnesia +AIPickAttackForAmnesia: ; 2c532 (b:4532) +; load Defending Pokemon attacks + call SwapTurn + ld e, PLAY_AREA_ARENA + call GetPlayAreaCardAttachedEnergies + call HandleEnergyBurn + ld a, DUELVARS_ARENA_CARD + call GetTurnDuelistVariable + ld d, a + call LoadCardDataToBuffer2_FromDeckIndex +; if has no attack 1 name, return + ld hl, wLoadedCard2Atk1Name + ld a, [hli] + or [hl] + jr z, .chosen + +; if Defending Pokemon has enough energy for second attack, choose it + ld e, SECOND_ATTACK + bank1call _CheckIfEnoughEnergiesToAttack + jr nc, .chosen +; otherwise if first attack isn't a Pkmn Power, choose it instead. + ld e, FIRST_ATTACK_OR_PKMN_POWER + ld a, [wLoadedCard2Atk1Category] + cp POKEMON_POWER + jr nz, .chosen +; if it is a Pkmn Power, choose second attack. + ld e, SECOND_ATTACK +.chosen + ld a, e + call SwapTurn + ret + +; Return in a the PLAY_AREA_* of the non-turn holder's Pokemon card in bench with the lowest (remaining) HP. +; if multiple cards are tied for the lowest HP, the one with the highest PLAY_AREA_* is returned. +GetBenchPokemonWithLowestHP: ; 2c564 (b:4564) + call SwapTurn + ld a, DUELVARS_NUMBER_OF_POKEMON_IN_PLAY_AREA + call GetTurnDuelistVariable + ld c, a + lb de, PLAY_AREA_ARENA, $ff + ld b, d + ld a, DUELVARS_BENCH1_CARD_HP + call GetTurnDuelistVariable + jr .start +; find Play Area location with least amount of HP +.loop_bench + ld a, e + cp [hl] + jr c, .next ; skip if HP is higher + ld e, [hl] + ld d, b + +.next + inc hl +.start + inc b + dec c + jr nz, .loop_bench + + ld a, d + call SwapTurn + ret + +; handles drawing and selection of screen for +; choosing a color (excluding colorless), for use +; of Shift Pkmn Power and Conversion attacks. +; outputs in a the color that was selected or, +; if B was pressed, returns carry. +; input: +; a = Play Area location (PLAY_AREA_*), with: +; bit 7 not set if it's applying to opponent's card +; bit 7 set if it's applying to player's card +; hl = text to be printed in the bottom box +; output: +; a = color that was selected +HandleColorChangeScreen: ; 2c588 (b:4588) + or a + call z, SwapTurn + push af + call .DrawScreen + pop af + call z, SwapTurn + + ld hl, .menu_params + xor a + call InitializeMenuParameters + call EnableLCD + +.loop_input + call DoFrame + call HandleMenuInput + jr nc, .loop_input + cp -1 ; b pressed? + jr z, .set_carry + ld e, a + ld d, $00 + ld hl, ShiftListItemToColor + add hl, de + ld a, [hl] + or a + ret +.set_carry + scf + ret + +.menu_params + db 1, 1 ; cursor x, cursor y + db 2 ; y displacement between items + db MAX_PLAY_AREA_POKEMON ; number of items + db SYM_CURSOR_R ; cursor tile number + db SYM_SPACE ; tile behind cursor + dw NULL ; function pointer if non-0 + +.DrawScreen: ; 2c5be (b:45be) + push hl + push af + call EmptyScreen + call ZeroObjectPositions + call LoadDuelCardSymbolTiles + +; load card data + pop af + and $7f + ld [wTempPlayAreaLocation_cceb], a + add DUELVARS_ARENA_CARD + call GetTurnDuelistVariable + call LoadCardDataToBuffer1_FromDeckIndex + +; draw card gfx + ld de, v0Tiles1 + $20 tiles ; destination offset of loaded gfx + ld hl, wLoadedCard1Gfx + ld a, [hli] + ld h, [hl] + ld l, a + lb bc, $30, TILE_SIZE + call LoadCardGfx + bank1call SetBGP6OrSGB3ToCardPalette + bank1call FlushAllPalettesOrSendPal23Packet + ld a, $a0 + lb hl, 6, 1 + lb de, 9, 2 + lb bc, 8, 6 + call FillRectangle + bank1call ApplyBGP6OrSGB3ToCardImage + +; print card name and level at the top + ld a, 16 + call CopyCardNameAndLevel + ld [hl], $00 + lb de, 7, 0 + call InitTextPrinting + ld hl, wDefaultText + call ProcessText + +; list all the colors + ld hl, ShiftMenuData + call PlaceTextItems + +; print card's color, resistance and weakness + ld a, [wTempPlayAreaLocation_cceb] + call GetPlayAreaCardColor + inc a + lb bc, 15, 9 + call WriteByteToBGMap0 + ld a, [wTempPlayAreaLocation_cceb] + call GetPlayAreaCardWeakness + lb bc, 15, 10 + bank1call PrintCardPageWeaknessesOrResistances + ld a, [wTempPlayAreaLocation_cceb] + call GetPlayAreaCardResistance + lb bc, 15, 11 + bank1call PrintCardPageWeaknessesOrResistances + + call DrawWideTextBox + +; print list of color names on all list items + lb de, 4, 1 + ldtx hl, ColorListText + call InitTextPrinting_ProcessTextFromID + +; print input hl to text box + lb de, 1, 14 + pop hl + call InitTextPrinting_ProcessTextFromID + +; draw and apply palette to color icons + ld hl, ColorTileAndBGP + lb de, 2, 0 + ld c, NUM_COLORED_TYPES +.loop_colors + ld a, [hli] + push de + push bc + push hl + lb hl, 1, 2 + lb bc, 2, 2 + call FillRectangle + + ld a, [wConsole] + cp CONSOLE_CGB + jr nz, .skip_vram1 + pop hl + push hl + call BankswitchVRAM1 + ld a, [hl] + lb hl, 0, 0 + lb bc, 2, 2 + call FillRectangle + call BankswitchVRAM0 + +.skip_vram1 + pop hl + pop bc + pop de + inc hl + inc e + inc e + dec c + jr nz, .loop_colors + ret + +; loads wTxRam2 and wTxRam2_b: +; [wTxRam2] <- wLoadedCard1Name +; [wTxRam2_b] <- input color as text symbol +; input: +; a = type (color) constant +LoadCardNameAndInputColor: ; 2c686 (b:4686) + add a + ld e, a + ld d, $00 + ld hl, ColorToTextSymbol + add hl, de + +; load wTxRam2 with card's name + ld de, wTxRam2 + ld a, [wLoadedCard1Name] + ld [de], a + inc de + ld a, [wLoadedCard1Name + 1] + ld [de], a + +; load wTxRam2_b with ColorToTextSymbol + inc de + ld a, [hli] + ld [de], a + inc de + ld a, [hli] + ld [de], a + ret + +ShiftMenuData: ; 2c6a1 (b:46a1) + ; x, y, text id + textitem 10, 9, TypeText + textitem 10, 10, WeaknessText + textitem 10, 11, ResistanceText + db $ff + +ColorTileAndBGP: ; 2c6ae (b:46ae) + ; tile, BG + db $e4, $02 + db $e0, $01 + db $eC, $02 + db $e8, $01 + db $f0, $03 + db $f4, $03 + +ShiftListItemToColor: ; 2c6ba (b:46ba) + db GRASS + db FIRE + db WATER + db LIGHTNING + db FIGHTING + db PSYCHIC + +ColorToTextSymbol: ; 2c6c0 (b:46c0) + tx FireSymbolText + tx GrassSymbolText + tx LightningSymbolText + tx WaterSymbolText + tx FightingSymbolText + tx PsychicSymbolText + +DrawSymbolOnPlayAreaCursor: ; 2c6cc (b:46cc) + ld c, a + add a + add c + add 2 + ; a = 3*a + 2 + ld c, a + ld a, b + ld b, 0 + call WriteByteToBGMap0 + ret + +; possibly unreferenced +Func_2c6d9: ; 2c6d9 (b:46d9) + ldtx hl, IncompleteText + call DrawWideTextBox_WaitForInput + ret + +PlayAreaSelectionMenuParameters: ; 2c6e0 (b:46e0) + db 0, 0 ; cursor x, cursor y + db 3 ; y displacement between items + db MAX_PLAY_AREA_POKEMON ; number of items + db SYM_CURSOR_R ; cursor tile number + db SYM_SPACE ; tile behind cursor + dw NULL ; function pointer if non-0 + +BenchSelectionMenuParameters: ; 2c6e8 (b:46e8) + db 0, 3 ; cursor x, cursor y + db 3 ; y displacement between items + db MAX_PLAY_AREA_POKEMON ; number of items + db SYM_CURSOR_R ; cursor tile number + db SYM_SPACE ; tile behind cursor + dw NULL ; function pointer if non-0 + +SpitPoison_AIEffect: ; 2c6f0 (b:46f0) + ld a, 10 / 2 + lb de, 0, 10 + jp SetExpectedAIDamage + +; If heads, defending Pokemon becomes poisoned +SpitPoison_Poison50PercentEffect: ; 2c6f8 (b:46f8) + ldtx de, PoisonCheckText + call TossCoin_BankB + jp c, PoisonEffect + ld a, ATK_ANIM_SPIT_POISON_SUCCESS + ld [wLoadedAttackAnimation], a + call SetNoEffectFromStatus + ret + +; outputs in hTemp_ffa0 the result of the coin toss (0 = tails, 1 = heads). +; in case it was heads, stores in hTempPlayAreaLocation_ffa1 +; the PLAY_AREA_* location of the Bench Pokemon that was selected for switch. +TerrorStrike_50PercentSelectSwitchPokemon: ; 2c70a (b:470a) + xor a + ldh [hTemp_ffa0], a + +; return failure if no Pokemon to switch to + ld a, DUELVARS_NUMBER_OF_POKEMON_IN_PLAY_AREA + call GetNonTurnDuelistVariable + cp 2 + ret c + +; toss coin and store whether it was tails (0) or heads (1) in hTemp_ffa0. +; return if it was tails. + ldtx de, IfHeadsChangeOpponentsActivePokemonText + call Func_2c08a + ldh [hTemp_ffa0], a + ret nc + + call DuelistSelectForcedSwitch + ldh a, [hTempPlayAreaLocation_ff9d] + ldh [hTempPlayAreaLocation_ffa1], a + ret + +; if coin toss at hTemp_ffa0 was heads and it's possible, +; switch the Defending Pokemon +TerrorStrike_SwitchDefendingPokemon: ; 2c726 (b:4726) + ldh a, [hTemp_ffa0] + or a + ret z + ldh a, [hTempPlayAreaLocation_ffa1] + call HandleSwitchDefendingPokemonEffect + ret + +PoisonFang_AIEffect: ; 2c730 (b:4730) + ld a, 10 + lb de, 10, 10 + jp UpdateExpectedAIDamage_AccountForPoison + +WeepinbellPoisonPowder_AIEffect: ; 2c738 (b:4738) + ld a, 5 + lb de, 0, 10 + jp UpdateExpectedAIDamage_AccountForPoison + +; return carry if there are no Pokemon cards in the non-turn holder's bench +VictreebelLure_AssertPokemonInBench: ; 2c740 (b:4740) + ld a, DUELVARS_NUMBER_OF_POKEMON_IN_PLAY_AREA + call GetNonTurnDuelistVariable + ldtx hl, EffectNoPokemonOnTheBenchText + cp 2 + ret + +; return in hTempPlayAreaLocation_ffa1 the PLAY_AREA_* location +; of the Bench Pokemon that was selected for switch +VictreebelLure_SelectSwitchPokemon: ; 2c74b (b:474b) + ldtx hl, SelectPkmnOnBenchToSwitchWithActiveText + call DrawWideTextBox_WaitForInput + call SwapTurn + bank1call HasAlivePokemonInBench +.select_pokemon + bank1call OpenPlayAreaScreenForSelection + jr c, .select_pokemon + ldh a, [hTempPlayAreaLocation_ff9d] + ldh [hTemp_ffa0], a + call SwapTurn + ret + +; Return in hTemp_ffa0 the PLAY_AREA_* of the non-turn holder's Pokemon card in bench with the lowest (remaining) HP. +; if multiple cards are tied for the lowest HP, the one with the highest PLAY_AREA_* is returned. +VictreebelLure_GetBenchPokemonWithLowestHP: ; 2c764 (b:4764) + call GetBenchPokemonWithLowestHP + ldh [hTemp_ffa0], a + ret + +; Defending Pokemon is swapped out for the one with the PLAY_AREA_* at hTemp_ffa0 +; unless Mew's Neutralizing Shield or Haunter's Transparency prevents it. +VictreebelLure_SwitchDefendingPokemon: ; 2c76a (b:476a) + call SwapTurn + ldh a, [hTemp_ffa0] + ld e, a + call HandleNShieldAndTransparency + call nc, SwapArenaWithBenchPokemon + call SwapTurn + xor a + ld [wDuelDisplayedScreen], a + ret + +; If heads, defending Pokemon can't retreat next turn +AcidEffect: ; 2c77e (b:477e) + ldtx de, AcidCheckText + call TossCoin_BankB + ret nc + ld a, SUBSTATUS2_UNABLE_RETREAT + call ApplySubstatus2ToDefendingCard + ret + +GloomPoisonPowder_AIEffect: ; 2c78b (b:478b) + ld a, 10 + lb de, 10, 10 + jp UpdateExpectedAIDamage_AccountForPoison + +; Defending Pokemon and user become confused +FoulOdorEffect: ; 2c793 (b:4793) + call ConfusionEffect + call SwapTurn + call ConfusionEffect + call SwapTurn + ret + +; If heads, prevent all damage done to user next turn +KakunaStiffenEffect: ; 2c7a0 (b:47a0) + ldtx de, IfHeadsNoDamageNextTurnText + call TossCoin_BankB + jp nc, SetWasUnsuccessful + ld a, ATK_ANIM_PROTECT + ld [wLoadedAttackAnimation], a + ld a, SUBSTATUS1_NO_DAMAGE_STIFFEN + call ApplySubstatus1ToDefendingCard + ret + +KakunaPoisonPowder_AIEffect: ; 2c7b4 (b:47b4) + ld a, 5 + lb de, 0, 10 + jp UpdateExpectedAIDamage_AccountForPoison + +GolbatLeechLifeEffect: ; 2c7bc (b:47bc) + ld hl, wDealtDamage + ld e, [hl] + inc hl ; wDamageEffectiveness + ld d, [hl] + call ApplyAndAnimateHPRecovery + ret + +VenonatLeechLifeEffect: ; 2c7c6 (b:47c6) + ld hl, wDealtDamage + ld e, [hl] + inc hl ; wDamageEffectiveness + ld d, [hl] + call ApplyAndAnimateHPRecovery + ret + +; During your next turn, double damage +SwordsDanceEffect: ; 2c7d0 (b:47d0) + ld a, [wTempTurnDuelistCardID] + cp SCYTHER + ret nz + ld a, SUBSTATUS1_NEXT_TURN_DOUBLE_DAMAGE + call ApplySubstatus1ToDefendingCard + ret + +; If heads, defending Pokemon becomes confused +ZubatSupersonicEffect: ; 2c7dc (b:47dc) + call Confusion50PercentEffect + call nc, SetNoEffectFromStatus + ret + +ZubatLeechLifeEffect: ; 2c7e3 (b:47e3) + ld hl, wDealtDamage + ld e, [hl] + inc hl + ld d, [hl] + call ApplyAndAnimateHPRecovery + ret + +Twineedle_AIEffect: ; 2c7ed (b:47ed) + ld a, 60 / 2 + lb de, 0, 60 + jp SetExpectedAIDamage + +; Flip 2 coins; deal 30x number of heads +Twineedle_MultiplierEffect: ; 2c7f5 (b:47f5) + ld hl, 30 + call LoadTxRam3 + ldtx de, DamageCheckIfHeadsXDamageText + ld a, 2 + call TossCoinATimes_BankB + ld e, a + add a + add e + call ATimes10 + call SetDefiniteDamage + ret + +BeedrillPoisonSting_AIEffect: ; 2c80d (b:480d) + ld a, 5 + lb de, 0, 10 + jp UpdateExpectedAIDamage_AccountForPoison + +ExeggcuteLeechSeedEffect: ; 2c815 (b:4815) + ld hl, wDealtDamage + ld a, [hli] + or a + ret z ; return if no damage dealt + ld de, 10 + call ApplyAndAnimateHPRecovery + ret + +FoulGas_AIEffect: ; 2c822 (b:4822) + ld a, 5 + lb de, 0, 10 + jp UpdateExpectedAIDamage + +; If heads, defending Pokemon becomes poisoned. If tails, defending Pokemon becomes confused +FoulGas_PoisonOrConfusionEffect: ; 2c82a (b:482a) + ldtx de, PoisonedIfHeadsConfusedIfTailsText + call TossCoin_BankB + jp c, PoisonEffect + jp ConfusionEffect + +; an exact copy of KakunaStiffenEffect +; If heads, prevent all damage done to user next turn +MetapodStiffenEffect: ; 2c836 (b:4836) + ldtx de, IfHeadsNoDamageNextTurnText + call TossCoin_BankB + jp nc, SetWasUnsuccessful + ld a, ATK_ANIM_PROTECT + ld [wLoadedAttackAnimation], a + ld a, SUBSTATUS1_NO_DAMAGE_STIFFEN + call ApplySubstatus1ToDefendingCard + ret + +; returns carry if no cards in Deck or if +; Play Area is full already. +Sprout_CheckDeckAndPlayArea: ; 2c84a (b:484a) + call CheckIfDeckIsEmpty + ret c ; return if no cards in deck + ld a, DUELVARS_NUMBER_OF_POKEMON_IN_PLAY_AREA + call GetTurnDuelistVariable + ldtx hl, NoSpaceOnTheBenchText + cp MAX_PLAY_AREA_POKEMON + ccf + ret + +Sprout_PlayerSelectEffect: ; 2c85a (b:485a) + ld a, $ff + ldh [hTemp_ffa0], a + + call CreateDeckCardList + ldtx hl, ChooseAnOddishFromDeckText + ldtx bc, OddishText + lb de, SEARCHEFFECT_CARD_ID, ODDISH + call LookForCardsInDeck + ret c + +; draw Deck list interface and print text + bank1call Func_5591 + ldtx hl, ChooseAnOddishText + ldtx de, DuelistDeckText + bank1call SetCardListHeaderText + +.loop + bank1call DisplayCardList + jr c, .pressed_b + call GetCardIDFromDeckIndex + ld bc, ODDISH + call CompareDEtoBC + jr nz, .play_sfx + +; Oddish was selected + ldh a, [hTempCardIndex_ff98] + ldh [hTemp_ffa0], a + or a + ret + +.play_sfx + ; play SFX and loop back + call Func_3794 + jr .loop + +.pressed_b +; figure if Player can exit the screen without selecting, +; that is, if the Deck has no Oddish card. + ld a, DUELVARS_CARD_LOCATIONS + call GetTurnDuelistVariable +.loop_b_press + ld a, [hl] + cp CARD_LOCATION_DECK + jr nz, .next + ld a, l + call GetCardIDFromDeckIndex + ld bc, ODDISH + call CompareDEtoBC + jr z, .play_sfx ; found Oddish, go back to top loop +.next + inc l + ld a, l + cp DECK_SIZE + jr c, .loop_b_press + +; no Oddish in Deck, can safely exit screen + ld a, $ff + ldh [hTemp_ffa0], a + or a + ret + +Sprout_AISelectEffect: ; 2c8b7 (b:48b7) + call CreateDeckCardList + ld hl, wDuelTempList +.loop_deck + ld a, [hli] + ldh [hTemp_ffa0], a + cp $ff + ret z ; no Oddish + call GetCardIDFromDeckIndex + ld a, e + cp ODDISH + jr nz, .loop_deck + ret ; Oddish found + +Sprout_PutInPlayAreaEffect: ; 2c8cc (b:48cc) + ldh a, [hTemp_ffa0] + cp $ff + jr z, .shuffle + call SearchCardInDeckAndAddToHand + call AddCardToHand + call PutHandPokemonCardInPlayArea + call IsPlayerTurn + jr c, .shuffle + ; display card on screen + ldh a, [hTemp_ffa0] + ldtx hl, PlacedOnTheBenchText + bank1call DisplayCardDetailScreen +.shuffle + call Func_2c0bd + ret + +; returns carry if no Pokemon on Bench +Teleport_CheckBench: ; 2c8ec (b:48ec) + ld a, DUELVARS_NUMBER_OF_POKEMON_IN_PLAY_AREA + call GetTurnDuelistVariable + ldtx hl, ThereAreNoPokemonOnBenchText + cp 2 + ret + +Teleport_PlayerSelectEffect: ; 2c8f7 (b:48f7) + ldtx hl, SelectPkmnOnBenchToSwitchWithActiveText + call DrawWideTextBox_WaitForInput + bank1call HasAlivePokemonInBench + ld a, $01 + ld [wcbd4], a +.loop + bank1call OpenPlayAreaScreenForSelection + jr c, .loop + ldh a, [hTempPlayAreaLocation_ff9d] + ldh [hTemp_ffa0], a + ret + +Teleport_AISelectEffect: ; 2c90f (b:490f) + ld a, DUELVARS_NUMBER_OF_POKEMON_IN_PLAY_AREA + call GetTurnDuelistVariable + call Random + ldh [hTemp_ffa0], a + ret + +Teleport_SwitchEffect: ; 2c91a (b:491a) + ldh a, [hTemp_ffa0] + ld e, a + call SwapArenaWithBenchPokemon + xor a + ld [wDuelDisplayedScreen], a + ret + +BigEggsplosion_AIEffect: ; 2c925 (b:4925) + ldh a, [hTempPlayAreaLocation_ff9d] + ld e, a + call GetPlayAreaCardAttachedEnergies + ld a, [wTotalAttachedEnergies] + call SetDamageToATimes20 + inc h + jr nz, .capped + ld l, 255 +.capped + ld a, l + ld [wAIMaxDamage], a + srl a + ld [wDamage], a + xor a + ld [wAIMinDamage], a + ret + +; Flip coins equal to attached energies; deal 20x number of heads +BigEggsplosion_MultiplierEffect: ; 2c944 (b:4944) + ld e, PLAY_AREA_ARENA + call GetPlayAreaCardAttachedEnergies + ld hl, 20 + call LoadTxRam3 + ld a, [wTotalAttachedEnergies] + ldtx de, DamageCheckIfHeadsXDamageText + call TossCoinATimes_BankB +; fallthrough + +; set damage to 20*a. Also return result in hl +SetDamageToATimes20: ; 2c958 (b:4958) + ld l, a + ld h, $00 + ld e, l + ld d, h + add hl, hl + add hl, hl + add hl, de + add hl, hl + add hl, hl + ld a, l + ld [wDamage], a + ld a, h + ld [wDamage + 1], a + ret + +Thrash_AIEffect: ; 2c96b (b:496b) + ld a, (30 + 40) / 2 + lb de, 30, 40 + jp SetExpectedAIDamage + +; If heads 10 more damage; if tails, 10 damage to itself +Thrash_ModifierEffect: ; 2c973 (b:4973) + ldtx de, IfHeadPlus10IfTails10ToYourselfText + call TossCoin_BankB + ldh [hTemp_ffa0], a + ret nc + ld a, 10 + call AddToDamage + ret + +Thrash_RecoilEffect: ; 2c982 (b:4982) + ldh a, [hTemp_ffa0] + or a + ret nz + ld a, 10 + call DealRecoilDamageToSelf + ret + +Toxic_AIEffect: ; 2c98c (b:498c) + ld a, 20 + lb de, 20, 20 + jp UpdateExpectedAIDamage + +; Defending Pokémon becomes double poisoned (takes 20 damage per turn rather than 10) +Toxic_DoublePoisonEffect: ; 2c994 (b:4994) + call DoublePoisonEffect + ret + +BoyfriendsEffect: ; 2c998 (b:4998) + ld a, DUELVARS_ARENA_CARD + call GetTurnDuelistVariable + ld c, PLAY_AREA_ARENA +.loop + ld a, [hl] + cp $ff + jr z, .done + call GetCardIDFromDeckIndex + ld a, e + cp NIDOKING + jr nz, .next + ld a, d + cp $00 ; why check d? Card IDs are only 1 byte long + jr nz, .next + inc c +.next + inc hl + jr .loop +.done +; c holds number of Nidoking found in Play Area + ld a, c + add a + call ATimes10 + call AddToDamage ; adds 2 * 10 * c + ret + +NidoranFFurySwipes_AIEffect: ; 2c9be (b:49be) + ld a, 30 / 2 + lb de, 0, 30 + jp SetExpectedAIDamage + +NidoranFFurySwipes_MultiplierEffect: ; 2c9c6 (b:49c6) + ld hl, 10 + call LoadTxRam3 + ldtx de, DamageCheckIfHeadsXDamageText + ld a, 3 + call TossCoinATimes_BankB + call ATimes10 + call SetDefiniteDamage + ret + +NidoranFCallForFamily_CheckDeckAndPlayArea: ; 2c9db (b:49db) + call CheckIfDeckIsEmpty + ret c ; return if no cards in deck + ld a, DUELVARS_NUMBER_OF_POKEMON_IN_PLAY_AREA + call GetTurnDuelistVariable + ldtx hl, NoSpaceOnTheBenchText + cp MAX_PLAY_AREA_POKEMON + ccf + ret + +NidoranFCallForFamily_PlayerSelectEffect: ; 2c9eb (b:49eb) + ld a, $ff + ldh [hTemp_ffa0], a + + call CreateDeckCardList + ldtx hl, ChooseNidoranFromDeckText + ldtx bc, NidoranMNidoranFText + lb de, SEARCHEFFECT_NIDORAN, $00 + call LookForCardsInDeck + ret c + +; draw Deck list interface and print text + bank1call Func_5591 + ldtx hl, ChooseNidoranText + ldtx de, DuelistDeckText + bank1call SetCardListHeaderText + +.loop + bank1call DisplayCardList + jr c, .pressed_b + call GetCardIDFromDeckIndex + ld bc, NIDORANF + call CompareDEtoBC + jr z, .selected_nidoran + ld bc, NIDORANM + call CompareDEtoBC + jr nz, .loop ; .play_sfx would be more appropriate here + +.selected_nidoran + ldh a, [hTempCardIndex_ff98] + ldh [hTemp_ffa0], a + or a + ret + +.play_sfx + ; play SFX and loop back + call Func_3794 + jr .loop + +.pressed_b +; figure if Player can exit the screen without selecting, +; that is, if the Deck has no NidoranF or NidoranM card. + ld a, DUELVARS_CARD_LOCATIONS + call GetTurnDuelistVariable +.loop_b_press + ld a, [hl] + cp CARD_LOCATION_DECK + jr nz, .next + ld a, l + call GetCardIDFromDeckIndex + ld bc, NIDORANF + call CompareDEtoBC + jr z, .play_sfx ; found, go back to top loop + ld bc, NIDORANM + jr z, .play_sfx ; found, go back to top loop +.next + inc l + ld a, l + cp DECK_SIZE + jr c, .loop_b_press + +; no Nidoran in Deck, can safely exit screen + ld a, $ff + ldh [hTemp_ffa0], a + or a + ret + +NidoranFCallForFamily_AISelectEffect: ; 2ca55 (b:4a55) + call CreateDeckCardList + ld hl, wDuelTempList +.loop_deck + ld a, [hli] + ldh [hTemp_ffa0], a + cp $ff + ret z ; none found + call GetCardIDFromDeckIndex + ld a, e + cp NIDORANF + jr z, .found + cp NIDORANM + jr nz, .loop_deck +.found + ret + +NidoranFCallForFamily_PutInPlayAreaEffect: ; 2ca6e (b:4a6e) + ldh a, [hTemp_ffa0] + cp $ff + jr z, .shuffle + call SearchCardInDeckAndAddToHand + call AddCardToHand + call PutHandPokemonCardInPlayArea + call IsPlayerTurn + jr c, .shuffle + ; display card on screen + ldh a, [hTemp_ffa0] + ldtx hl, PlacedOnTheBenchText + bank1call DisplayCardDetailScreen +.shuffle + call Func_2c0bd + ret + +HornHazard_AIEffect: ; 2ca8e (b:4a8e) + ld a, 30 / 2 + lb de, 0, 30 + jp SetExpectedAIDamage + +HornHazard_NoDamage50PercentEffect: ; 2ca96 (b:4a96) + ldtx de, DamageCheckIfTailsNoDamageText + call TossCoin_BankB + jr c, .heads + xor a + call SetDefiniteDamage + call SetWasUnsuccessful + ret +.heads + ld a, ATK_ANIM_HIT + ld [wLoadedAttackAnimation], a + ret + +NidorinaSupersonicEffect: ; 2caac (b:4aac) + call Confusion50PercentEffect + call nc, SetNoEffectFromStatus + ret + +NidorinaDoubleKick_AIEffect: ; 2cab3 (b:4ab3) + ld a, 60 / 2 + lb de, 0, 60 + jp SetExpectedAIDamage + +NidorinaDoubleKick_MultiplierEffect: ; 2cabb (b:4abb) + ld hl, 30 + call LoadTxRam3 + ldtx de, DamageCheckIfHeadsXDamageText + ld a, 2 + call TossCoinATimes_BankB + ld e, a + add a + add e + call ATimes10 + call SetDefiniteDamage + ret + +NidorinoDoubleKick_AIEffect: ; 2cad3 (b:4ad3) + ld a, 60 / 2 + lb de, 0, 60 + jp SetExpectedAIDamage + +NidorinoDoubleKick_MultiplierEffect: ; 2cabb (b:4abb) + ld hl, 30 + call LoadTxRam3 + ldtx de, DamageCheckIfHeadsXDamageText + ld a, 2 + call TossCoinATimes_BankB + ld e, a + add a + add e + call ATimes10 + call SetDefiniteDamage + ret + +ButterfreeWhirlwind_CheckBench: ; 2caf3 (b:4af3) + ld a, DUELVARS_NUMBER_OF_POKEMON_IN_PLAY_AREA + call GetNonTurnDuelistVariable + cp 2 + jr nc, .has_bench + ; no bench, do not do effect + ld a, $ff + ldh [hTemp_ffa0], a + ret +.has_bench + call DuelistSelectForcedSwitch + ldh a, [hTempPlayAreaLocation_ff9d] + ldh [hTemp_ffa0], a + ret + +ButterfreeWhirlwind_SwitchEffect: ; 2cb09 (b:4b09) + ldh a, [hTemp_ffa0] + call HandleSwitchDefendingPokemonEffect + ret + +ButterfreeMegaDrainEffect: ; 2cb0f (b:4b0f) + ld hl, wDealtDamage + ld a, [hli] + ld h, [hl] + ld l, a + srl h + rr l + bit 0, l + jr z, .rounded + ; round up to nearest 10 + ld de, 10 / 2 + add hl, de +.rounded + ld e, l + ld d, h + call ApplyAndAnimateHPRecovery + ret + +WeedlePoisonSting_AIEffect: ; 2cb27 (b:4b27) + ld a, 5 + lb de, 0, 10 + jp UpdateExpectedAIDamage_AccountForPoison + +IvysaurPoisonPowder_AIEffect: ; 2cb2f (b:4b2f) + ld a, 10 + lb de, 10, 10 + jp UpdateExpectedAIDamage_AccountForPoison + +BulbasaurLeechSeedEffect: ; 2cb37 (b:4b37) + ld hl, wDealtDamage + ld a, [hli] + or [hl] + ret z ; return if no damage dealt + lb de, 0, 10 + call ApplyAndAnimateHPRecovery + ret + +; returns carry if no Grass Energy in Play Area +EnergyTrans_CheckPlayArea: ; 2cb44 (b:4b44) + ldh a, [hTempPlayAreaLocation_ff9d] + ldh [hTemp_ffa0], a + ldh a, [hTempPlayAreaLocation_ff9d] + call CheckCannotUseDueToStatus_OnlyToxicGasIfANon0 + ret c ; cannot use Pkmn Power + +; search in Play Area for at least 1 Grass Energy type + ld a, DUELVARS_CARD_LOCATIONS + call GetTurnDuelistVariable +.loop_deck + ld a, [hl] + and CARD_LOCATION_PLAY_AREA + jr z, .next + push hl + ld a, l + call GetCardIDFromDeckIndex + call GetCardType + pop hl + cp TYPE_ENERGY_GRASS + ret z +.next + inc l + ld a, l + cp DECK_SIZE + jr c, .loop_deck + +; none found + ldtx hl, NoGrassEnergyText + scf + ret + +EnergyTrans_PrintProcedure: ; 2cb6f (b:4b6f) + ldtx hl, ProcedureForEnergyTransferText + bank1call DrawWholeScreenTextBox + or a + ret + +EnergyTrans_TransferEffect: ; 2cb77 (b:4b77) + ld a, DUELVARS_DUELIST_TYPE + call GetTurnDuelistVariable + cp DUELIST_TYPE_PLAYER + jr z, .player +; not player + bank1call Func_61a1 + bank1call PrintPlayAreaCardList_EnableLCD + ret + +.player + xor a + ldh [hCurSelectionItem], a + bank1call Func_61a1 + +.draw_play_area + bank1call PrintPlayAreaCardList_EnableLCD + push af + ldh a, [hCurSelectionItem] + ld hl, PlayAreaSelectionMenuParameters + call InitializeMenuParameters + pop af + ld [wNumMenuItems], a + +; handle the action of taking a Grass Energy card +.loop_input_take + call DoFrame + call HandleMenuInput + jr nc, .loop_input_take + cp -1 ; b press? + ret z + +; a press + ldh [hAIPkmnPowerEffectParam], a + ldh [hCurSelectionItem], a + call CheckIfCardHasGrassEnergyAttached + jr c, .play_sfx ; no Grass attached + + ldh [hAIEnergyTransEnergyCard], a + ldh a, [hAIEnergyTransEnergyCard] ; useless + ; temporarily take card away to draw Play Area + call AddCardToHand + bank1call PrintPlayAreaCardList_EnableLCD + ldh a, [hAIPkmnPowerEffectParam] + ld e, a + ldh a, [hAIEnergyTransEnergyCard] + ; give card back + call PutHandCardInPlayArea + + ; draw Grass symbol near cursor + ldh a, [hAIPkmnPowerEffectParam] + ld b, SYM_GRASS + call DrawSymbolOnPlayAreaCursor + +; handle the action of placing a Grass Energy card +.loop_input_put + call DoFrame + call HandleMenuInput + jr nc, .loop_input_put + cp -1 ; b press? + jr z, .remove_symbol + +; a press + ldh [hCurSelectionItem], a + ldh [hAIEnergyTransPlayAreaLocation], a + ld a, OPPACTION_6B15 + call SetOppAction_SerialSendDuelData + ldh a, [hAIEnergyTransPlayAreaLocation] + ld e, a + ldh a, [hAIEnergyTransEnergyCard] + ; give card being held to this Pokemon + call AddCardToHand + call PutHandCardInPlayArea + +.remove_symbol + ldh a, [hAIPkmnPowerEffectParam] + ld b, SYM_SPACE + call DrawSymbolOnPlayAreaCursor + call EraseCursor + jr .draw_play_area + +.play_sfx + call Func_3794 + jr .loop_input_take + +EnergyTrans_AIEffect: ; 2cbfb (b:4bfb) + ldh a, [hAIEnergyTransPlayAreaLocation] + ld e, a + ldh a, [hAIEnergyTransEnergyCard] + call AddCardToHand + call PutHandCardInPlayArea + bank1call PrintPlayAreaCardList_EnableLCD + ret + +; returns carry if no Grass Energy cards +; attached to card in Play Area location of a. +; input: +; a = PLAY_AREA_* of location to check +CheckIfCardHasGrassEnergyAttached: ; 2cc0a (b:4c0a) + or CARD_LOCATION_PLAY_AREA + ld e, a + + ld a, DUELVARS_CARD_LOCATIONS + call GetTurnDuelistVariable +.loop + ld a, [hl] + cp e + jr nz, .next + push de + push hl + ld a, l + call GetCardIDFromDeckIndex + call GetCardType + pop hl + pop de + cp TYPE_ENERGY_GRASS + jr z, .no_carry +.next + inc l + ld a, l + cp DECK_SIZE + jr c, .loop + scf + ret +.no_carry + ld a, l + or a + ret + +GrimerMinimizeEffect: ; 2cc30 (b:4c30) + ld a, SUBSTATUS1_REDUCE_BY_20 + call ApplySubstatus1ToDefendingCard + ret + +ToxicGasEffect: ; 2cc36 (b:4c36) + scf + ret + +Sludge_AIEffect: ; 2cc38 (b:4c38) + ld a, 5 + lb de, 0, 10 + jp UpdateExpectedAIDamage_AccountForPoison + +; returns carry if no cards in Deck +; or if Play Area is full already. +BellsproutCallForFamily_CheckDeckAndPlayArea: ; 2cc40 (b:4c40) + call CheckIfDeckIsEmpty + ret c ; return if no cards in deck + ld a, DUELVARS_NUMBER_OF_POKEMON_IN_PLAY_AREA + call GetTurnDuelistVariable + ldtx hl, NoSpaceOnTheBenchText + cp MAX_PLAY_AREA_POKEMON + ccf + ret + +BellsproutCallForFamily_PlayerSelectEffect: ; 2cc50 (b:4c50) + ld a, $ff + ldh [hTemp_ffa0], a + + call CreateDeckCardList + ldtx hl, ChooseABellsproutFromDeckText + ldtx bc, BellsproutText + lb de, SEARCHEFFECT_CARD_ID, BELLSPROUT + call LookForCardsInDeck + ret c + +; draw Deck list interface and print text + bank1call Func_5591 + ldtx hl, ChooseABellsproutText + ldtx de, DuelistDeckText + bank1call SetCardListHeaderText + +.loop + bank1call DisplayCardList + jr c, .pressed_b + call GetCardIDFromDeckIndex + ld bc, BELLSPROUT + call CompareDEtoBC + jr nz, .play_sfx + +; Bellsprout was selected + ldh a, [hTempCardIndex_ff98] + ldh [hTemp_ffa0], a + or a + ret + +.play_sfx + ; play SFX and loop back + call Func_3794 + jr .loop + +.pressed_b +; figure if Player can exit the screen without selecting, +; that is, if the Deck has no Bellsprout card. + ld a, DUELVARS_CARD_LOCATIONS + call GetTurnDuelistVariable +.loop_b_press + ld a, [hl] + cp CARD_LOCATION_DECK + jr nz, .next + ld a, l + call GetCardIDFromDeckIndex + ld bc, BELLSPROUT + call CompareDEtoBC + jr z, .play_sfx ; found Bellsprout, go back to top loop +.next + inc l + ld a, l + cp DECK_SIZE + jr c, .loop_b_press + +; no Bellsprout in Deck, can safely exit screen + ld a, $ff + ldh [hTemp_ffa0], a + or a + ret + +BellsproutCallForFamily_AISelectEffect: ; 2ccad (b:4cad) + call CreateDeckCardList + ld hl, wDuelTempList +.loop_deck + ld a, [hli] + ldh [hTemp_ffa0], a + cp $ff + ret z ; no Bellsprout + call GetCardIDFromDeckIndex + ld a, e + cp BELLSPROUT + jr nz, .loop_deck + ret ; Bellsprout found + +BellsproutCallForFamily_PutInPlayAreaEffect: ; 2ccc2 (b:4cc2) + ldh a, [hTemp_ffa0] + cp $ff + jr z, .shuffle + call SearchCardInDeckAndAddToHand + call AddCardToHand + call PutHandPokemonCardInPlayArea + call IsPlayerTurn + jr c, .shuffle + ldh a, [hTemp_ffa0] + ldtx hl, PlacedOnTheBenchText + bank1call DisplayCardDetailScreen +.shuffle + call Func_2c0bd + ret + +WeezingSmog_AIEffect: ; 2cce2 (b:4ce2) + ld a, 5 + lb de, 0, 10 + jp UpdateExpectedAIDamage_AccountForPoison + +WeezingSelfdestructEffect: ; 2ccea (b:4cea) + ld a, 60 + call DealRecoilDamageToSelf + ld a, $01 + ld [wIsDamageToSelf], a + ld a, 10 + call DealDamageToAllBenchedPokemon + call SwapTurn + xor a + ld [wIsDamageToSelf], a + ld a, 10 + call DealDamageToAllBenchedPokemon + call SwapTurn + ret + +Shift_OncePerTurnCheck: ; 2cd09 (b:4d09) + ldh a, [hTempPlayAreaLocation_ff9d] + ldh [hTemp_ffa0], a + add DUELVARS_ARENA_CARD_FLAGS + call GetTurnDuelistVariable + and USED_PKMN_POWER_THIS_TURN + jr nz, .already_used + ldh a, [hTempPlayAreaLocation_ff9d] + call CheckCannotUseDueToStatus_OnlyToxicGasIfANon0 + ret +.already_used + ldtx hl, OnlyOncePerTurnText + scf + ret + +Shift_PlayerSelectEffect: ; 2cd21 (b:4d21) + ldtx hl, ChoosePokemonWishToColorChangeText + ldh a, [hTemp_ffa0] + or $80 + call HandleColorChangeScreen + ldh [hAIPkmnPowerEffectParam], a + ret c ; cancelled + +; check whether the color selected is valid + ; look in Turn Duelist's Play Area + call .CheckColorInPlayArea + ret nc + ; look in NonTurn Duelist's Play Area + call SwapTurn + call .CheckColorInPlayArea + call SwapTurn + ret nc + ; not found in either Duelist's Play Area + ldtx hl, UnableToSelectText + call DrawWideTextBox_WaitForInput + jr Shift_PlayerSelectEffect ; loop back to start + +; checks in input color in a exists in Turn Duelist's Play Area +; returns carry if not found. +.CheckColorInPlayArea: ; 2cd44 (b:4d44) + ld a, DUELVARS_NUMBER_OF_POKEMON_IN_PLAY_AREA + call GetTurnDuelistVariable + ld c, a + ld b, PLAY_AREA_ARENA +.loop_play_area + push bc + ld a, b + call GetPlayAreaCardColor + pop bc + ld hl, hAIPkmnPowerEffectParam + cp [hl] + ret z ; found + inc b + dec c + jr nz, .loop_play_area + ; not found + scf + ret + +Shift_ChangeColorEffect: ; 2cd5d (b:4d5d) + ldh a, [hTemp_ffa0] + add DUELVARS_ARENA_CARD + call GetTurnDuelistVariable + call LoadCardDataToBuffer1_FromDeckIndex + + ldh a, [hTemp_ffa0] + add DUELVARS_ARENA_CARD_FLAGS + call GetTurnDuelistVariable + set USED_PKMN_POWER_THIS_TURN_F, [hl] + + ldh a, [hTemp_ffa0] + add DUELVARS_ARENA_CARD_CHANGED_TYPE + ld l, a + ldh a, [hAIPkmnPowerEffectParam] + or HAS_CHANGED_COLOR + ld [hl], a + call LoadCardNameAndInputColor + ldtx hl, ChangedTheColorOfText + call DrawWideTextBox_WaitForInput + ret + +VenomPowder_AIEffect: ; 2cd84 (b:4d84) + ld a, 5 + lb de, 0, 10 + jp UpdateExpectedAIDamage + +VenomPowder_PoisonConfusion50PercentEffect: ; 2cd8c (b:4d8c) + ldtx de, VenomPowderCheckText + call TossCoin_BankB + ret nc ; return if tails + +; heads + call PoisonEffect + call ConfusionEffect + ret c + ld a, CONFUSED | POISONED + ld [wNoEffectFromWhichStatus], a + ret + +TangelaPoisonPowder_AIEffect: ; 2cda0 (b:4da0) + ld a, 5 + lb de, 0, 10 + jp UpdateExpectedAIDamage_AccountForPoison + +Heal_OncePerTurnCheck: ; 2cda8 (b:4da8) + ldh a, [hTempPlayAreaLocation_ff9d] + ldh [hTemp_ffa0], a + add DUELVARS_ARENA_CARD_FLAGS + call GetTurnDuelistVariable + and USED_PKMN_POWER_THIS_TURN + jr nz, .already_used + + call CheckIfPlayAreaHasAnyDamage + ldtx hl, NoPokemonWithDamageCountersText + ret c ; no damage counters to heal + + ldh a, [hTempPlayAreaLocation_ff9d] + call CheckCannotUseDueToStatus_OnlyToxicGasIfANon0 + ret + +.already_used + ldtx hl, OnlyOncePerTurnText + scf + ret + +Heal_RemoveDamageEffect: ; 2cdc7 (b:4dc7) + ldtx de, IfHeadsHealIsSuccessfulText + call TossCoin_BankB + ldh [hAIPkmnPowerEffectParam], a + jr nc, .done + + ld a, DUELVARS_DUELIST_TYPE + call GetTurnDuelistVariable + cp DUELIST_TYPE_LINK_OPP + jr z, .link_opp + and DUELIST_TYPE_AI_OPP + jr nz, .done + +; player + ldtx hl, ChoosePkmnToRemoveDamageCounterText + call DrawWideTextBox_WaitForInput + bank1call HasAlivePokemonInPlayArea +.loop_input + bank1call OpenPlayAreaScreenForSelection + jr c, .loop_input + ldh a, [hTempPlayAreaLocation_ff9d] + ldh [hPlayAreaEffectTarget], a + ld e, a + call GetCardDamageAndMaxHP + or a + jr z, .loop_input ; has no damage counters + ldh a, [hTempPlayAreaLocation_ff9d] + call SerialSend8Bytes + jr .done + +.link_opp + call SerialRecv8Bytes + ldh [hPlayAreaEffectTarget], a + ; fallthrough + +.done +; flag Pkmn Power as being used regardless of coin outcome + ldh a, [hTemp_ffa0] + add DUELVARS_ARENA_CARD_FLAGS + call GetTurnDuelistVariable + set USED_PKMN_POWER_THIS_TURN_F, [hl] + ldh a, [hAIPkmnPowerEffectParam] + or a + ret z ; return if coin was tails + + ldh a, [hPlayAreaEffectTarget] + add DUELVARS_ARENA_CARD_HP + call GetTurnDuelistVariable + add 10 ; remove 1 damage counter + ld [hl], a + ldh a, [hPlayAreaEffectTarget] + call Func_2c10b + call ExchangeRNG + ret + +PetalDance_AIEffect: ; 2ce23 (b:4e23) + ld a, 120 / 2 + lb de, 0, 120 + jp SetExpectedAIDamage + +PetalDance_MultiplierEffect: ; 2ce2b (b:4e2b) + ld hl, 40 + call LoadTxRam3 + ldtx de, DamageCheckIfHeadsXDamageText + ld a, 3 + call TossCoinATimes_BankB + add a + add a + call ATimes10 + ; a = 4 * 10 * heads + call SetDefiniteDamage + call SwapTurn + call ConfusionEffect + call SwapTurn + ret + +PoisonWhip_AIEffect: ; 2ce4b (b:4e4b) + ld a, 10 + lb de, 10, 10 + jp UpdateExpectedAIDamage_AccountForPoison + +SolarPower_CheckUse: ; 2ce53 (b:4e53) + ldh a, [hTempPlayAreaLocation_ff9d] + ldh [hTemp_ffa0], a + add DUELVARS_ARENA_CARD_FLAGS + call GetTurnDuelistVariable + and USED_PKMN_POWER_THIS_TURN + jr nz, .already_used + + ldh a, [hTempPlayAreaLocation_ff9d] + call CheckCannotUseDueToStatus_OnlyToxicGasIfANon0 + ret c ; can't use PKMN due to status or Toxic Gas + +; return carry if none of the Arena cards have status conditions + ld a, DUELVARS_ARENA_CARD_STATUS + call GetTurnDuelistVariable + or a + jr nz, .has_status + ld a, DUELVARS_ARENA_CARD_STATUS + call GetNonTurnDuelistVariable + or a + jr z, .no_status +.has_status + or a + ret +.already_used + ldtx hl, OnlyOncePerTurnText + scf + ret +.no_status + ldtx hl, NotAffectedByPoisonSleepParalysisOrConfusionText + scf + ret + +SolarPower_RemoveStatusEffect: ; 2ce82 (b:4e82) + ld a, ATK_ANIM_HEAL_BOTH_SIDES + ld [wLoadedAttackAnimation], a + bank1call Func_7415 + ldh a, [hTempPlayAreaLocation_ff9d] + ld b, a + ld c, $00 + ldh a, [hWhoseTurn] + ld h, a + bank1call PlayAttackAnimation + bank1call WaitAttackAnimation + + ldh a, [hTemp_ffa0] + add DUELVARS_ARENA_CARD_FLAGS + call GetTurnDuelistVariable + set USED_PKMN_POWER_THIS_TURN_F, [hl] + ld l, DUELVARS_ARENA_CARD_STATUS + ld [hl], NO_STATUS + + ld a, DUELVARS_ARENA_CARD_STATUS + call GetNonTurnDuelistVariable + ld [hl], NO_STATUS + bank1call DrawDuelHUDs + ret + +VenusaurMegaDrainEffect: ; 2ceb0 (b:4eb0) + ld hl, wDealtDamage + ld a, [hli] + ld h, [hl] + ld l, a + srl h + rr l + bit 0, l + jr z, .rounded + ; round up to nearest 10 + ld de, 10 / 2 + add hl, de +.rounded + ld e, l + ld d, h + call ApplyAndAnimateHPRecovery + ret + +; applies the damage bonus for attacks that get bonus +; from extra Water energy cards. +; this bonus is always 10 more damage for each extra Water energy +; and is always capped at a maximum of 20 damage. +; input: +; b = number of Water energy cards needed for paying Energy Cost +; c = number of colorless energy cards needed for paying Energy Cost +ApplyExtraWaterEnergyDamageBonus: ; 2cec8 (b:4ec8) + ld a, [wMetronomeEnergyCost] + or a + jr z, .not_metronome + ld c, a ; amount of colorless needed for Metronome + ld b, 0 ; no Water energy needed for Metronome + +.not_metronome + push bc + ldh a, [hTempPlayAreaLocation_ff9d] + ld e, a + call GetPlayAreaCardAttachedEnergies + pop bc + + ld hl, wAttachedEnergies + WATER + ld a, c + or a + jr z, .check_bonus ; is Energy cost all water energy? + + ; it's not, so we need to remove the + ; Water energy cards from calculations + ; if they pay for colorless instead. + ld a, [wTotalAttachedEnergies] + cp [hl] + jr nz, .check_bonus ; skip if at least 1 non-Water energy attached + + ; Water is the only energy color attached + ld a, c + add b + ld b, a + ; b += c + +.check_bonus + ld a, [hl] + sub b + jr c, .skip_bonus ; is water energy < b? + jr z, .skip_bonus ; is water energy == b? + +; a holds number of water energy not payed for energy cost + cp 3 + jr c, .less_than_3 + ld a, 2 ; cap this to 2 for bonus effect +.less_than_3 + call ATimes10 + call AddToDamage ; add 10 * a to damage + +.skip_bonus + ld a, [wDamage] + ld [wAIMinDamage], a + ld [wAIMaxDamage], a + ret + +OmastarWaterGunEffect: ; 2cf05 (b:4f05) + lb bc, 1, 1 + jr ApplyExtraWaterEnergyDamageBonus + +OmastarSpikeCannon_AIEffect: ; 2cf0a (b:4f0a) + ld a, 60 / 2 + lb de, 0, 60 + jp SetExpectedAIDamage + +OmastarSpikeCannon_MultiplierEffect: ; 2cf12 (b:4f12) + ld hl, 30 + call LoadTxRam3 + ld a, 2 + ldtx de, DamageCheckIfHeadsXDamageText + call TossCoinATimes_BankB + ld e, a + add a + add e + call ATimes10 + call SetDefiniteDamage ; 3 * 10 * heads + ret + +ClairvoyanceEffect: ; 2cf2a (b:4f2a) + scf + ret + +OmanyteWaterGunEffect: ; 2cf2c (b:4f2c) + lb bc, 1, 0 + jp ApplyExtraWaterEnergyDamageBonus + +WartortleWithdrawEffect: ; 2cf32 (b:4f32) + ldtx de, IfHeadsNoDamageNextTurnText + call TossCoin_BankB + jp nc, SetWasUnsuccessful + ld a, ATK_ANIM_PROTECT + ld [wLoadedAttackAnimation], a + ld a, SUBSTATUS1_NO_DAMAGE_10 + call ApplySubstatus1ToDefendingCard + ret + +RainDanceEffect: ; 2cf46 (b:4f46) + scf + ret + +HydroPumpEffect: ; 2cf48 (b:4f48) + lb bc, 3, 0 + jp ApplyExtraWaterEnergyDamageBonus + +KinglerFlail_AIEffect: ; 2cf4e (b:4f4e) + call KinglerFlail_HPCheck + jp SetDefiniteAIDamage + +KinglerFlail_HPCheck: ; 2cf54 (b:4f54) + ld e, PLAY_AREA_ARENA + call GetCardDamageAndMaxHP + call SetDefiniteDamage + ret + +; returns carry if no cards in Deck +; or if Play Area is full already. +KrabbyCallForFamily_CheckDeckAndPlayArea: ; 2cf5d (b:4f5d) + call CheckIfDeckIsEmpty + ret c ; return if no cards in deck + ld a, DUELVARS_NUMBER_OF_POKEMON_IN_PLAY_AREA + call GetTurnDuelistVariable + ldtx hl, NoSpaceOnTheBenchText + cp MAX_PLAY_AREA_POKEMON + ccf + ret + +KrabbyCallForFamily_PlayerSelectEffect: ; 2cf6d (b:4f6d) + ld a, $ff + ldh [hTemp_ffa0], a + + call CreateDeckCardList + ldtx hl, ChooseAKrabbyFromDeckText + ldtx bc, KrabbyText + lb de, SEARCHEFFECT_CARD_ID, KRABBY + call LookForCardsInDeck + ret c + +; draw Deck list interface and print text + bank1call Func_5591 + ldtx hl, ChooseAKrabbyText + ldtx de, DuelistDeckText + bank1call SetCardListHeaderText + +.loop + bank1call DisplayCardList + jr c, .pressed_b + call GetCardIDFromDeckIndex + ld bc, KRABBY + call CompareDEtoBC + jr nz, .play_sfx + +; Krabby was selected + ldh a, [hTempCardIndex_ff98] + ldh [hTemp_ffa0], a + or a + ret + +.play_sfx + ; play SFX and loop back + call Func_3794 + jr .loop + +.pressed_b +; figure if Player can exit the screen without selecting, +; that is, if the Deck has no Krabby card. + ld a, DUELVARS_CARD_LOCATIONS + call GetTurnDuelistVariable +.loop_b_press + ld a, [hl] + cp CARD_LOCATION_DECK + jr nz, .next + ld a, l + call GetCardIDFromDeckIndex + ld bc, KRABBY + call CompareDEtoBC + jr z, .play_sfx ; found Krabby, go back to top loop +.next + inc l + ld a, l + cp DECK_SIZE + jr c, .loop_b_press + +; no Krabby in Deck, can safely exit screen + ld a, $ff + ldh [hTemp_ffa0], a + or a + ret + +KrabbyCallForFamily_AISelectEffect: ; 2cfdf (b:4fdf) + call CreateDeckCardList + ld hl, wDuelTempList +.loop_deck + ld a, [hli] + ldh [hTemp_ffa0], a + cp $ff + ret z ; no Krabby + call GetCardIDFromDeckIndex + ld a, e + cp KRABBY + jr nz, .loop_deck + ret ; Krabby found + +KrabbyCallForFamily_PutInPlayAreaEffect: ; 2cfca (b:4fca) + ldh a, [hTemp_ffa0] + cp $ff + jr z, .shuffle + call SearchCardInDeckAndAddToHand + call AddCardToHand + call PutHandPokemonCardInPlayArea + call IsPlayerTurn + jr c, .shuffle + ldh a, [hTemp_ffa0] + ldtx hl, PlacedOnTheBenchText + bank1call DisplayCardDetailScreen +.shuffle + call Func_2c0bd + ret + +MagikarpFlail_AIEffect: ; 2cfff (b:4fff) + call MagikarpFlail_HPCheck + jp SetDefiniteAIDamage + +MagikarpFlail_HPCheck: ; 2d005 (b:5005) + ld e, PLAY_AREA_ARENA + call GetCardDamageAndMaxHP + call SetDefiniteDamage + ret + +HeadacheEffect: ; 2d00e (b:500e) + ld a, DUELVARS_ARENA_CARD_SUBSTATUS3 + call GetNonTurnDuelistVariable + set SUBSTATUS3_HEADACHE, [hl] + ret + +PsyduckFurySwipes_AIEffect: ; 2d016 (b:5016) + ld a, 30 / 2 + lb de, 0, 30 + jp SetExpectedAIDamage + +PsyduckFurySwipes_MultiplierEffect: ; 2d01e (b:501e) + ld hl, 10 + call LoadTxRam3 + ldtx de, DamageCheckIfHeadsXDamageText + ld a, 3 + call TossCoinATimes_BankB + call ATimes10 + call SetDefiniteDamage + ret + +GolduckHyperBeam_PlayerSelectEffect: ; 2d033 (b:5033) + call SwapTurn + ld e, PLAY_AREA_ARENA + call GetPlayAreaCardAttachedEnergies + ld a, [wTotalAttachedEnergies] + or a + jr z, .no_energy + +; draw Energy Card list screen + ldtx hl, ChooseDiscardEnergyCardFromOpponentText + call DrawWideTextBox_WaitForInput + xor a ; PLAY_AREA_ARENA + call CreateArenaOrBenchEnergyCardList + xor a ; PLAY_AREA_ARENA + bank1call DisplayEnergyDiscardScreen + +.loop_input + bank1call HandleEnergyDiscardMenuInput + jr c, .loop_input + + call SwapTurn + ldh a, [hTempCardIndex_ff98] + ldh [hTemp_ffa0], a ; store selected card to discard + ret + +.no_energy + call SwapTurn + ld a, $ff + ldh [hTemp_ffa0], a + or a + ret + +GolduckHyperBeam_AISelectEffect: ; 2d065 (b:5065) + call AIPickEnergyCardToDiscardFromDefendingPokemon + ldh [hTemp_ffa0], a + ret + +GolduckHyperBeam_DiscardEffect: ; 2d06b (b:506b) + call HandleNoDamageOrEffect + ret c ; return if attack had no effect + + ; check if energy card was chosen to discard + ldh a, [hTemp_ffa0] + cp $ff + ret z ; return if none selected + + ; discard Defending card's energy + call SwapTurn + call PutCardInDiscardPile + ld a, DUELVARS_ARENA_CARD_LAST_TURN_EFFECT + call GetTurnDuelistVariable + ld [hl], LAST_TURN_EFFECT_DISCARD_ENERGY + call SwapTurn + ret + +SeadraWaterGunEffect: ; 2d085 (b:5085) + lb bc, 1, 1 + jp ApplyExtraWaterEnergyDamageBonus + +SeadraAgilityEffect: ; 2d08b (b:508b) + ldtx de, IfHeadsDoNotReceiveDamageOrEffectText + call TossCoin_BankB + ret nc ; return if tails + ld a, ATK_ANIM_AGILITY_PROTECT + ld [wLoadedAttackAnimation], a + ld a, SUBSTATUS1_AGILITY + call ApplySubstatus1ToDefendingCard + ret + +ShellderSupersonicEffect: ; 2d09d (b:509d) + call Confusion50PercentEffect + call nc, SetNoEffectFromStatus + ret + +HideInShellEffect: ; 2d0a4 (b:50a4) + ldtx de, IfHeadsNoDamageNextTurnText + call TossCoin_BankB + jp nc, SetWasUnsuccessful + ld a, ATK_ANIM_PROTECT + ld [wLoadedAttackAnimation], a + ld a, SUBSTATUS1_NO_DAMAGE_11 + call ApplySubstatus1ToDefendingCard + ret + +VaporeonQuickAttack_AIEffect: ; 2d0b8 (b:50b8) + ld a, (10 + 30) / 2 + lb de, 10, 30 + jp SetExpectedAIDamage + +VaporeonQuickAttack_DamageBoostEffect: ; 2d0c0 (b:50c0) + ld hl, 20 + call LoadTxRam3 + ldtx de, DamageCheckIfHeadsPlusDamageText + call TossCoin_BankB + ret nc ; return if tails + ld a, 20 + call AddToDamage + ret + +VaporeonWaterGunEffect: ; 2d0d3 (b:50d3) + lb bc, 2, 1 + jp ApplyExtraWaterEnergyDamageBonus + +; returns carry if Arena card has no Water Energy attached +; or if it doesn't have any damage counters. +StarmieRecover_CheckEnergyHP: ; 2d0d9 (b:50d9) + ld e, PLAY_AREA_ARENA + call GetPlayAreaCardAttachedEnergies + ld a, [wAttachedEnergies + WATER] + ldtx hl, NotEnoughWaterEnergyText + cp 1 + ret c ; return if not enough energy + call GetCardDamageAndMaxHP + ldtx hl, NoDamageCountersText + cp 10 + ret ; return carry if no damage + +StarmieRecover_PlayerSelectEffect: ; 2d0f0 (b:50f0) + ld a, TYPE_ENERGY_WATER + call CreateListOfEnergyAttachedToArena + xor a ; PLAY_AREA_ARENA + bank1call DisplayEnergyDiscardScreen +.loop_input + bank1call HandleEnergyDiscardMenuInput + jr c, .loop_input + ldh a, [hTempCardIndex_ff98] + ldh [hTemp_ffa0], a ; store card chosen + ret + +StarmieRecover_AISelectEffect: ; 2d103 (b:5103) + ld a, TYPE_ENERGY_WATER + call CreateListOfEnergyAttachedToArena + ld a, [wDuelTempList] ; pick first card + ldh [hTemp_ffa0], a + ret + +StarmieRecover_DiscardEffect: ; 2d10e (b:510e) + ldh a, [hTemp_ffa0] + call PutCardInDiscardPile + ret + +StarmieRecover_HealEffect: ; 2d114 (b:5114) + ld e, PLAY_AREA_ARENA + call GetCardDamageAndMaxHP + ld e, a ; all damage for recovery + ld d, 0 + call ApplyAndAnimateHPRecovery + ret + +SquirtleWithdrawEffect: ; 2d120 (b:5120) + ldtx de, IfHeadsNoDamageNextTurnText + call TossCoin_BankB + jp nc, SetWasUnsuccessful + ld a, ATK_ANIM_PROTECT + ld [wLoadedAttackAnimation], a + ld a, SUBSTATUS1_NO_DAMAGE_10 + call ApplySubstatus1ToDefendingCard + ret + +HorseaSmokescreenEffect: ; 2d134 (b:5134) + ld a, SUBSTATUS2_SMOKESCREEN + call ApplySubstatus2ToDefendingCard + ret + +TentacruelSupersonicEffect: ; 2d13a (b:513a) + call Confusion50PercentEffect + call nc, SetNoEffectFromStatus + ret + +JellyfishSting_AIEffect: ; 2d141 (b:5141) + ld a, 10 + lb de, 10, 10 + jp UpdateExpectedAIDamage_AccountForPoison + +; returns carry if Defending Pokemon has no attacks +PoliwhirlAmnesia_CheckAttacks: ; 2d149 (b:5149) + call SwapTurn + ld a, DUELVARS_ARENA_CARD + call GetTurnDuelistVariable + call LoadCardDataToBuffer2_FromDeckIndex + ld a, [wLoadedCard2Atk1Category] + cp POKEMON_POWER + jr nz, .has_attack + ld hl, wLoadedCard2Atk2Name + ld a, [hli] + or [hl] + jr nz, .has_attack +; has no attack + call SwapTurn + ldtx hl, NoAttackMayBeChoosenText + scf + ret +.has_attack + call SwapTurn + or a + ret + +PoliwhirlAmnesia_PlayerSelectEffect: ; 2d16f (b:516f) + call PlayerPickAttackForAmnesia + ret + +PoliwhirlAmnesia_AISelectEffect: ; 2d173 (b:5173) + call AIPickAttackForAmnesia + ldh [hTemp_ffa0], a + ret + +PoliwhirlAmnesia_DisableEffect: ; 2d179 (b:5179) + call ApplyAmnesiaToAttack + ret + +PlayerPickAttackForAmnesia: ; 2d17d (b:517d) + ldtx hl, ChooseAttackOpponentWillNotBeAbleToUseText + call DrawWideTextBox_WaitForInput + call HandleDefendingPokemonAttackSelection + ld a, e + ldh [hTemp_ffa0], a + ret + +; applies the Amnesia effect on the defending Pokemon, +; for the attack index in hTemp_ffa0. +ApplyAmnesiaToAttack: ; 2d18a (b:518a) + ld a, SUBSTATUS2_AMNESIA + call ApplySubstatus2ToDefendingCard + ld a, [wNoDamageOrEffect] + or a + ret nz ; no effect + +; set selected attack as disabled + ld a, DUELVARS_ARENA_CARD_DISABLED_ATTACK_INDEX + call GetNonTurnDuelistVariable + ldh a, [hTemp_ffa0] + ld [hl], a + + ld l, DUELVARS_ARENA_CARD_LAST_TURN_EFFECT + ld [hl], LAST_TURN_EFFECT_AMNESIA + + call IsPlayerTurn + ret c ; return if Player + +; the rest of the routine if for Opponent +; to announce which attack was used for Amnesia. + call SwapTurn + ld a, DUELVARS_ARENA_CARD + call GetTurnDuelistVariable + ld d, a + ldh a, [hTemp_ffa0] + ld e, a + call GetAttackName + call LoadTxRam2 + ldtx hl, WasChosenForTheEffectOfAmnesiaText + call DrawWideTextBox_WaitForInput + call SwapTurn + ret + +PoliwhirlDoubleslap_AIEffect: ; 2d1c0 (b:51c0) + ld a, 60 / 2 + lb de, 0, 60 + jp SetExpectedAIDamage + +PoliwhirlDoubleslap_MultiplierEffect: ; 2d1c8 (b:51c8) + ld hl, 30 + call LoadTxRam3 + ldtx de, DamageCheckIfHeadsXDamageText + ld a, 2 + call TossCoinATimes_BankB + ld e, a + add a + add e + call ATimes10 + call SetDefiniteDamage + ret + +PoliwrathWaterGunEffect: ; 2d1e0 (b:51e0) + lb bc, 2, 1 + jp ApplyExtraWaterEnergyDamageBonus + +Whirlpool_PlayerSelectEffect: ; 2d1e6 (b:51e6) + call SwapTurn + xor a ; PLAY_AREA_ARENA + call CreateArenaOrBenchEnergyCardList + jr c, .no_energy + + ldtx hl, ChooseDiscardEnergyCardFromOpponentText + call DrawWideTextBox_WaitForInput + xor a ; PLAY_AREA_ARENA + bank1call DisplayEnergyDiscardScreen +.loop_input + bank1call HandleEnergyDiscardMenuInput + jr c, .loop_input + + call SwapTurn + ldh a, [hTempCardIndex_ff98] + ldh [hTemp_ffa0], a ; store selected card to discard + ret + +.no_energy + call SwapTurn + ld a, $ff + ldh [hTemp_ffa0], a + ret + +Whirlpool_AISelectEffect: ; 2d20e (b:520e) + call AIPickEnergyCardToDiscardFromDefendingPokemon + ldh [hTemp_ffa0], a + ret + +Whirlpool_DiscardEffect: ; 2d214 (b:5214) + call HandleNoDamageOrEffect + ret c ; return if attack had no effect + ldh a, [hTemp_ffa0] + cp $ff + ret z ; return if none selected + + ; discard Defending card's energy + ; this doesn't update DUELVARS_ARENA_CARD_LAST_TURN_EFFECT + call SwapTurn + call PutCardInDiscardPile + ; ld a, DUELVARS_ARENA_CARD_LAST_TURN_EFFECT + ; call GetTurnDuelistVariable + ; ld [hl], LAST_TURN_EFFECT_DISCARD_ENERGY + call SwapTurn + ret + +PoliwagWaterGunEffect: ; 2d227 (b:5227) + lb bc, 1, 0 + jp ApplyExtraWaterEnergyDamageBonus + +ClampEffect: ; 2d22d (b:522d) + ld a, ATK_ANIM_HIT_EFFECT + ld [wLoadedAttackAnimation], a + ldtx de, SuccessCheckIfHeadsAttackIsSuccessfulText + call TossCoin_BankB + jp c, ParalysisEffect +; unsuccessful + xor a ; ATK_ANIM_NONE + ld [wLoadedAttackAnimation], a + call SetDefiniteDamage + call SetWasUnsuccessful + ret + +CloysterSpikeCannon_AIEffect: ; 2d246 (b:5246) + ld a, 60 / 2 + lb de, 0, 60 + jp SetExpectedAIDamage + +CloysterSpikeCannon_MultiplierEffect: ; 2d24e (b:524e) + ld hl, 30 + call LoadTxRam3 + ldtx de, DamageCheckIfHeadsXDamageText + ld a, 2 + call TossCoinATimes_BankB + ld e, a + add a + add e + call ATimes10 + call SetDefiniteDamage + ret + +Blizzard_BenchDamage50PercentEffect: ; 2d266 (b:5266) + ldtx de, DamageToOppBenchIfHeadsDamageToYoursIfTailsText + call TossCoin_BankB + ldh [hTemp_ffa0], a ; store coin result + ret + +Blizzard_BenchDamageEffect: ; 2d26f (b:526f) + ldh a, [hTemp_ffa0] + or a + jr nz, .opp_bench + +; own bench + ld a, $01 + ld [wIsDamageToSelf], a + ld a, 10 + call DealDamageToAllBenchedPokemon + ret + +.opp_bench + call SwapTurn + ld a, 10 + call DealDamageToAllBenchedPokemon + call SwapTurn + ret + +; return carry if can use Cowardice +Cowardice_Check: ; 2d28b (b:528b) + ldh a, [hTempPlayAreaLocation_ff9d] + ldh [hTemp_ffa0], a + call CheckCannotUseDueToStatus_OnlyToxicGasIfANon0 + ret c ; return if cannot use + + ld a, DUELVARS_NUMBER_OF_POKEMON_IN_PLAY_AREA + call GetTurnDuelistVariable + ldtx hl, EffectNoPokemonOnTheBenchText + cp 2 + ret c ; return if no bench + + ldh a, [hTempPlayAreaLocation_ff9d] + add DUELVARS_ARENA_CARD_FLAGS + call GetTurnDuelistVariable + ldtx hl, CannotBeUsedInTurnWhichWasPlayedText + and CAN_EVOLVE_THIS_TURN + scf + ret z ; return if was played this turn + + or a + ret + +Cowardice_PlayerSelectEffect: ; 2d2ae (b:52ae) + ldh a, [hTemp_ffa0] + or a + ret nz ; return if not Arena card + ldtx hl, SelectPokemonToPlaceInTheArenaText + call DrawWideTextBox_WaitForInput + bank1call HasAlivePokemonInBench + bank1call OpenPlayAreaScreenForSelection + ldh a, [hTempPlayAreaLocation_ff9d] + ldh [hAIPkmnPowerEffectParam], a + ret + +Cowardice_RemoveFromPlayAreaEffect: ; 2d2c3 (b:52c3) + ldh a, [hTemp_ffa0] + add DUELVARS_ARENA_CARD + call GetTurnDuelistVariable + +; put card in Discard Pile temporarily, so that +; all cards attached are discarded as well. + push af + ldh a, [hTemp_ffa0] + ld e, a + call MovePlayAreaCardToDiscardPile + +; if card was in Arena, swap selected Bench +; Pokemon with Arena, otherwise skip. + ldh a, [hTemp_ffa0] + or a + jr nz, .skip_switch + ldh a, [hAIPkmnPowerEffectParam] + ld e, a + call SwapArenaWithBenchPokemon + +.skip_switch +; move card back to Hand from Discard Pile +; and adjust Play Area + pop af + call MoveDiscardPileCardToHand + call AddCardToHand + call ShiftAllPokemonToFirstPlayAreaSlots + + xor a + ld [wDuelDisplayedScreen], a + ret + +LaprasWaterGunEffect: ; 2d2eb (b:52eb) + lb bc, 1, 0 + jp ApplyExtraWaterEnergyDamageBonus + +Quickfreeze_InitialEffect: ; 2d2f1 (b:52f1) + scf + ret + +Quickfreeze_Paralysis50PercentEffect: ; 2d2f3 (b:52f3) + ldtx de, ParalysisCheckText + call TossCoin_BankB + jr c, .heads + +; tails + call SetWasUnsuccessful + bank1call DrawDuelMainScene + bank1call Func_1bca + call WaitForWideTextBoxInput + ret + +.heads + call ParalysisEffect + ldh a, [hTempPlayAreaLocation_ff9d] + ld b, a + ld c, $00 + ldh a, [hWhoseTurn] + ld h, a + bank1call PlayAttackAnimation + bank1call Func_741a + bank1call WaitAttackAnimation + bank1call Func_6df1 + bank1call DrawDuelHUDs + bank1call Func_1bca + call c, WaitForWideTextBoxInput + ret + +IceBreath_ZeroDamage: ; 2d329 (b:5329) + xor a + call SetDefiniteDamage + ret + +IceBreath_RandomPokemonDamageEffect: ; 2d32e (b:532e) + call SwapTurn + call PickRandomPlayAreaCard + ld b, a + ld de, 40 + call DealDamageToPlayAreaPokemon_RegularAnim + call SwapTurn + ret + +FocusEnergyEffect: ; 2d33f (b:533f) + ld a, [wTempTurnDuelistCardID] + cp VAPOREON1 + ret nz ; return if no Vaporeon1 + ld a, SUBSTATUS1_NEXT_TURN_DOUBLE_DAMAGE + call ApplySubstatus1ToDefendingCard + ret + +PlayerPickFireEnergyCardToDiscard: ; 2d34b (b:534b) + call CreateListOfFireEnergyAttachedToArena + xor a + bank1call DisplayEnergyDiscardScreen + bank1call HandleEnergyDiscardMenuInput + ldh a, [hTempCardIndex_ff98] + ldh [hTempList], a + ret + +AIPickFireEnergyCardToDiscard: ; 2d35a (b:535a) + call CreateListOfFireEnergyAttachedToArena + ld a, [wDuelTempList] + ldh [hTempList], a ; pick first in list + ret + +; returns carry if Arena card has no Fire Energy cards +ArcanineFlamethrower_CheckEnergy: ; 2d363 (b:5363) + ld e, PLAY_AREA_ARENA + call GetPlayAreaCardAttachedEnergies + ld a, [wAttachedEnergies] + ldtx hl, NotEnoughFireEnergyText + cp 1 + ret + +ArcanineFlamethrower_PlayerSelectEffect: ; 2d371 (b:5371) + call PlayerPickFireEnergyCardToDiscard + ret + +ArcanineFlamethrower_AISelectEffect: ; 2d375 (b:5375) + call AIPickFireEnergyCardToDiscard + ret + +ArcanineFlamethrower_DiscardEffect: ; 2d379 (b:5379) + ldh a, [hTempList] + call PutCardInDiscardPile + ret + +TakeDownEffect: ; 2d37f (b:537f) + ld a, 30 + call DealRecoilDamageToSelf + ret + +ArcanineQuickAttack_AIEffect: ; 2d385 (b:5385) + ld a, (10 + 30) / 2 + lb de, 10, 30 + jp SetExpectedAIDamage + +ArcanineQuickAttack_DamageBoostEffect: ; 2d38d (b:538d) + ld hl, 20 + call LoadTxRam3 + ldtx de, DamageCheckIfHeadsPlusDamageText + call TossCoin_BankB + ret nc ; return if tails + ld a, 20 + call AddToDamage + ret + +; return carry if has less than 2 Fire Energy cards +FlamesOfRage_CheckEnergy: ; 2d3a0 (b:53a0) + ld e, PLAY_AREA_ARENA + call GetPlayAreaCardAttachedEnergies + ld a, [wAttachedEnergies] + ldtx hl, NotEnoughFireEnergyText + cp 2 + ret + +FlamesOfRage_PlayerSelectEffect: ; 2d3ae (b:53ae) + ldtx hl, ChooseAndDiscard2FireEnergyCardsText + call DrawWideTextBox_WaitForInput + + xor a + ldh [hCurSelectionItem], a + call CreateListOfFireEnergyAttachedToArena + xor a + bank1call DisplayEnergyDiscardScreen +.loop_input + bank1call HandleEnergyDiscardMenuInput + ret c + call GetNextPositionInTempList + ldh a, [hTempCardIndex_ff98] + ld [hl], a + call RemoveCardFromDuelTempList + ldh a, [hCurSelectionItem] + cp 2 + ret nc ; return when 2 have been chosen + bank1call DisplayEnergyDiscardMenu + jr .loop_input + +FlamesOfRage_AISelectEffect: ; 2d3d5 (b:53d5) + call AIPickFireEnergyCardToDiscard + ld a, [wDuelTempList + 1] + ldh [hTempList + 1], a + ret + +FlamesOfRage_DiscardEffect: ; 2d3de (b:53de) + ldh a, [hTempList] + call PutCardInDiscardPile + ldh a, [hTempList + 1] + call PutCardInDiscardPile + ret + +FlamesOfRage_AIEffect: ; 2d3e9 (b:53e9) + call FlamesOfRage_DamageBoostEffect + jp SetDefiniteAIDamage + +FlamesOfRage_DamageBoostEffect: ; 2d3ef (b:53ef) + ld e, PLAY_AREA_ARENA + call GetCardDamageAndMaxHP + call AddToDamage + ret + +RapidashStomp_AIEffect: ; 2d3f8 (b:53f8) + ld a, (20 + 30) / 2 + lb de, 20, 30 + jp SetExpectedAIDamage + +RapidashStomp_DamageBoostEffect: ; 2d400 (b:5400) + ld hl, 10 + call LoadTxRam3 + ldtx de, DamageCheckIfHeadsPlusDamageText + call TossCoin_BankB + ret nc ; return if tails + ld a, 10 + call AddToDamage + ret + +RapidashAgilityEffect: ; 2d413 (b:5413) + ldtx de, IfHeadsDoNotReceiveDamageOrEffectText + call TossCoin_BankB + ret nc ; return if tails + ld a, ATK_ANIM_AGILITY_PROTECT + ld [wLoadedAttackAnimation], a + ld a, SUBSTATUS1_AGILITY + call ApplySubstatus1ToDefendingCard + ret + +; returns carry if Opponent has no Pokemon in bench +NinetalesLure_CheckBench: ; 2d425 (b:5425) + ld a, DUELVARS_NUMBER_OF_POKEMON_IN_PLAY_AREA + call GetNonTurnDuelistVariable + ldtx hl, EffectNoPokemonOnTheBenchText + cp 2 + ret + +NinetalesLure_PlayerSelectEffect: ; 2d430 (b:5430) + ldtx hl, SelectPkmnOnBenchToSwitchWithActiveText + call DrawWideTextBox_WaitForInput + call SwapTurn + bank1call HasAlivePokemonInBench +.loop_input + bank1call OpenPlayAreaScreenForSelection + jr c, .loop_input + ldh a, [hTempPlayAreaLocation_ff9d] + ldh [hTemp_ffa0], a + call SwapTurn + ret + +NinetalesLure_AISelectEffect: ; 2d449 (b:5449) + call GetBenchPokemonWithLowestHP + ldh [hTemp_ffa0], a + ret + +NinetalesLure_SwitchEffect: ; 2d44f (b:544f) + call SwapTurn + ldh a, [hTemp_ffa0] + ld e, a + call HandleNShieldAndTransparency + call nc, SwapArenaWithBenchPokemon + call SwapTurn + xor a + ld [wDuelDisplayedScreen], a + ret + +; return carry if no Fire energy cards +FireBlast_CheckEnergy: ; 2d463 (b:5463) + ld e, PLAY_AREA_ARENA + call GetPlayAreaCardAttachedEnergies + ldtx hl, NotEnoughFireEnergyText + ld a, [wAttachedEnergies] + cp 1 + ret + +FireBlast_PlayerSelectEffect: ; 2d471 (b:5471) + call PlayerPickFireEnergyCardToDiscard + ret + +FireBlast_AISelectEffect: ; 2d475 (b:5475) + call AIPickFireEnergyCardToDiscard + ret + +FireBlast_DiscardEffect: ; 2d479 (b:5479) + ldh a, [hTempList] + call PutCardInDiscardPile + ret + +; return carry if no Fire energy cards +Ember_CheckEnergy: ; 2d47f (b:547f) + ld e, PLAY_AREA_ARENA + call GetPlayAreaCardAttachedEnergies + ldtx hl, NotEnoughFireEnergyText + ld a, [wAttachedEnergies] + cp 1 + ret + +Ember_PlayerSelectEffect: ; 2d48d (b:548d) + call PlayerPickFireEnergyCardToDiscard + ret + +Ember_AISelectEffect: ; 2d491 (b:5491) + call AIPickFireEnergyCardToDiscard + ret + +Ember_DiscardEffect: ; 2d495 (b:5495) + ldh a, [hTempList] + call PutCardInDiscardPile + ret + +; return carry if no Fire energy cards +Wildfire_CheckEnergy: ; 2d49b (b:549b) + ld e, PLAY_AREA_ARENA + call GetPlayAreaCardAttachedEnergies + ldtx hl, NotEnoughFireEnergyText + ld a, [wAttachedEnergies] + cp 1 + ret + +Wildfire_PlayerSelectEffect: ; 2d4a9 (b:54a9) + ldtx hl, DiscardOppDeckAsManyFireEnergyCardsText + call DrawWideTextBox_WaitForInput + + xor a + ldh [hCurSelectionItem], a + call CreateListOfFireEnergyAttachedToArena + xor a + bank1call DisplayEnergyDiscardScreen + +; show list to Player and for each card selected to discard, +; just increase a counter and store it. +; this will be the output used by Wildfire_DiscardEnergyEffect. + xor a + ld [wEnergyDiscardMenuDenominator], a +.loop + ldh a, [hCurSelectionItem] + ld [wEnergyDiscardMenuNumerator], a + bank1call HandleEnergyDiscardMenuInput + jr c, .done + ld hl, hCurSelectionItem + inc [hl] + call RemoveCardFromDuelTempList + jr c, .done + bank1call DisplayEnergyDiscardMenu + jr .loop + +.done +; return carry if no cards were discarded +; output the result in hTemp_ffa0 + ldh a, [hCurSelectionItem] + ldh [hTemp_ffa0], a + or a + ret nz + scf + ret + +Wildfire_AISelectEffect: ; 2d4dd (b:54dd) +; AI always chooses 0 cards to discard + xor a + ldh [hTempList], a + ret + +Wildfire_DiscardEnergyEffect: ; 2d4e1 (b:54e1) + call CreateListOfFireEnergyAttachedToArena + ldh a, [hTemp_ffa0] + or a + ret z ; no cards to discard + +; discard cards from wDuelTempList equal to the number +; of cards that were input in hTemp_ffa0. +; these are all the Fire Energy cards attached to Arena card +; so it will discard the cards in order, regardless +; of the actual order that was selected by Player. + ld c, a + ld hl, wDuelTempList +.loop_discard + ld a, [hli] + call PutCardInDiscardPile + dec c + jr nz, .loop_discard + ret + +Wildfire_DiscardDeckEffect: ; 2d4f4 (b:54f4) + ldh a, [hTemp_ffa0] + ld c, a + ld b, $00 + call SwapTurn + ld a, DUELVARS_NUMBER_OF_CARDS_NOT_IN_DECK + call GetTurnDuelistVariable + ld a, DECK_SIZE + sub [hl] + cp c + jr nc, .start_discard + ; only discard number of cards that are left in deck + ld c, a + +.start_discard + push bc + inc c + jr .check_remaining + +.loop + ; discard top card from deck + call DrawCardFromDeck + call nc, PutCardInDiscardPile +.check_remaining + dec c + jr nz, .loop + + pop hl + call LoadTxRam3 + ldtx hl, DiscardedCardsFromDeckText + call DrawWideTextBox_PrintText + call SwapTurn + ret + +Moltres1DiveBomb_AIEffect: ; 2d523 (b:5523) + ld a, 80 / 2 + lb de, 0, 80 + jp SetExpectedAIDamage + +Moltres1DiveBomb_Success50PercentEffect: ; 2d52b (b:552b) + ldtx de, SuccessCheckIfHeadsAttackIsSuccessfulText + call TossCoin_BankB + jr c, .heads +; tails + xor a + call SetDefiniteDamage + call SetWasUnsuccessful + ret +.heads + ld a, ATK_ANIM_DIVE_BOMB + ld [wLoadedAttackAnimation], a + ret + +FlareonQuickAttack_AIEffect: ; 2d541 (b:5541) + ld a, (10 + 30) / 2 + lb de, 10, 30 + jp SetExpectedAIDamage + +FlareonQuickAttack_DamageBoostEffect: ; 2d549 (b:5549) + ld hl, 20 + call LoadTxRam3 + ldtx de, DamageCheckIfHeadsPlusDamageText + call TossCoin_BankB + ret nc ; return if tails + ld a, 20 + call AddToDamage + ret + +; return carry if no Fire Energy attached +FlareonFlamethrower_CheckEnergy: ; 2d55c (b:555c) + ld e, PLAY_AREA_ARENA + call GetPlayAreaCardAttachedEnergies + ldtx hl, NotEnoughFireEnergyText + ld a, [wAttachedEnergies] + cp 1 + ret + +FlareonFlamethrower_PlayerSelectEffect: ; 2d56a (b:556a) + call PlayerPickFireEnergyCardToDiscard + ret + +FlareonFlamethrower_AISelectEffect: ; 2d56e (b:556e) + call AIPickFireEnergyCardToDiscard + ret + +FlareonFlamethrower_DiscardEffect: ; 2d572 (b:5572) + ldh a, [hTempList] + call PutCardInDiscardPile + ret + +; return carry if no Fire Energy attached +MagmarFlamethrower_CheckEnergy: ; 2d578 (b:5578) + ld e, PLAY_AREA_ARENA + call GetPlayAreaCardAttachedEnergies + ldtx hl, NotEnoughFireEnergyText + ld a, [wAttachedEnergies] + cp 1 + ret + +MagmarFlamethrower_PlayerSelectEffect: ; 2d586 (b:5586) + call PlayerPickFireEnergyCardToDiscard + ret + +MagmarFlamethrower_AISelectEffect: ; 2d58a (b:558a) + call AIPickFireEnergyCardToDiscard + ret + +MagmarFlamethrower_DiscardEffect: ; 2d58e (b:558e) + ldh a, [hTempList] + call PutCardInDiscardPile + ret + +MagmarSmokescreenEffect: ; 2d594 (b:5594) + ld a, SUBSTATUS2_SMOKESCREEN + call ApplySubstatus2ToDefendingCard + ret + +MagmarSmog_AIEffect: ; 2d59a (b:559a) + ld a, 5 + lb de, 0, 10 + jp UpdateExpectedAIDamage_AccountForPoison + +; return carry if no Fire Energy attached +CharmeleonFlamethrower_CheckEnergy: ; 2d5a2 (b:55a2) + ld e, PLAY_AREA_ARENA + call GetPlayAreaCardAttachedEnergies + ldtx hl, NotEnoughFireEnergyText + ld a, [wAttachedEnergies] + cp 1 + ret + +CharmeleonFlamethrower_PlayerSelectEffect: ; 2d5b0 (b:55b0) + call PlayerPickFireEnergyCardToDiscard + ret + +CharmeleonFlamethrower_AISelectEffect: ; 2d5b4 (b:55b4) + call AIPickFireEnergyCardToDiscard + ret + +CharmeleonFlamethrower_DiscardEffect: ; 2d5b8 (b:55b8) + ldh a, [hTempList] + call PutCardInDiscardPile + ret + +EnergyBurnEffect: ; 2d5be (b:55be) + scf + ret + +; return carry if has less than 2 Fire Energy cards +FireSpin_CheckEnergy: ; 2d5c0 (b:55c0) + xor a ; PLAY_AREA_ARENA + call CreateArenaOrBenchEnergyCardList + call CountCardsInDuelTempList + ldtx hl, NotEnoughEnergyCardsText + cp 2 + ret + +FireSpin_PlayerSelectEffect: ; 2d5cd (b:55cd) + ldtx hl, ChooseAndDiscard2EnergyCardsText + call DrawWideTextBox_WaitForInput + + xor a + ldh [hCurSelectionItem], a + xor a + call CreateArenaOrBenchEnergyCardList + call SortCardsInDuelTempListByID + xor a + bank1call DisplayEnergyDiscardScreen + + ld a, 2 + ld [wEnergyDiscardMenuDenominator], a +.loop_input + bank1call HandleEnergyDiscardMenuInput + ret c + call GetNextPositionInTempList + ldh a, [hTempCardIndex_ff98] + ld [hl], a + ld hl, wEnergyDiscardMenuNumerator + inc [hl] + ldh a, [hCurSelectionItem] + cp 2 + jr nc, .done + ldh a, [hTempCardIndex_ff98] + call RemoveCardFromDuelTempList + bank1call DisplayEnergyDiscardMenu + jr .loop_input +.done +; return when 2 have been chosen + or a + ret + +FireSpin_AISelectEffect: ; 2d606 (b:5606) + xor a ; PLAY_AREA_ARENA + call CreateArenaOrBenchEnergyCardList + ld hl, wDuelTempList + ld a, [hli] + ldh [hTempList], a + ld a, [hl] + ldh [hTempList + 1], a + ret + +FireSpin_DiscardEffect: ; 2d614 (b:5614) + ld hl, hTempList + ld a, [hli] + call PutCardInDiscardPile + ld a, [hli] + call PutCardInDiscardPile + ret + +; returns carry if Pkmn Power cannot be used +; or if Arena card is not Charizard. +; this is unused. +EnergyBurnCheck_Unreferenced: ; 2d620 (b:5620) + xor a + bank1call CheckCannotUseDueToStatus_OnlyToxicGasIfANon0 + ret c + ld a, DUELVARS_ARENA_CARD + push de + call GetTurnDuelistVariable + call GetCardIDFromDeckIndex + ld a, e + pop de + cp CHARIZARD + jr nz, .not_charizard + or a + ret +.not_charizard + scf + ret + +FlareonRage_AIEffect: ; 2d638 (b:5638) + call FlareonRage_DamageBoostEffect + jp SetDefiniteAIDamage + +FlareonRage_DamageBoostEffect: ; 2d63e (b:563e) + ld e, PLAY_AREA_ARENA + call GetCardDamageAndMaxHP + call AddToDamage + ret + +MixUpEffect: ; 2d647 (b:5647) + call SwapTurn + call CreateHandCardList + call SortCardsInDuelTempListByID + +; first go through Hand to place +; all Pkmn cards in it in the Deck. + ld hl, wDuelTempList + ld c, 0 +.loop_hand + ld a, [hl] + cp $ff + jr z, .done_hand + call .CheckIfCardIsPkmnCard + jr nc, .next_hand + ; found Pkmn card, place in deck + inc c + ld a, [hl] + call RemoveCardFromHand + call ReturnCardToDeck +.next_hand + inc hl + jr .loop_hand + +.done_hand + ld a, c + ldh [hCurSelectionItem], a + push bc + ldtx hl, ThePkmnCardsInHandAndDeckWereShuffledText + call DrawWideTextBox_WaitForInput + + call Func_2c0bd + call CreateDeckCardList + pop bc + ldh a, [hCurSelectionItem] + or a + jr z, .done ; if no cards were removed from Hand, return + +; c holds the number of cards that were placed in the Deck. +; now pick Pkmn cards from the Deck to place in Hand. + ld hl, wDuelTempList +.loop_deck + ld a, [hl] + call .CheckIfCardIsPkmnCard + jr nc, .next_deck + dec c + ld a, [hl] + call SearchCardInDeckAndAddToHand + call AddCardToHand +.next_deck + inc hl + ld a, c + or a + jr nz, .loop_deck +.done + call SwapTurn + ret + +; returns carry if card index in a is Pkmn card +.CheckIfCardIsPkmnCard: ; 2d69a (b:569a) + call LoadCardDataToBuffer2_FromDeckIndex + ld a, [wLoadedCard2Type] + cp TYPE_ENERGY + ret + +DancingEmbers_AIEffect: ; 2d6a3 (b:56a3) + ld a, 80 / 2 + lb de, 0, 80 + jp SetExpectedAIDamage + +DancingEmbers_MultiplierEffect: ; 2d6ab (b:56ab) + ld hl, 10 + call LoadTxRam3 + ldtx de, DamageCheckIfHeadsXDamageText + ld a, 8 + call TossCoinATimes_BankB + call ATimes10 + call SetDefiniteDamage + ret + +Firegiver_InitialEffect: ; 2d6c0 (b:56c0) + scf + ret + +Firegiver_AddToHandEffect: ; 2d6c2 (b:56c2) +; fill wDuelTempList with all Fire Energy card +; deck indices that are in the Deck. + ld a, DUELVARS_CARD_LOCATIONS + call GetTurnDuelistVariable + ld de, wDuelTempList + ld c, 0 +.loop_cards + ld a, [hl] + cp CARD_LOCATION_DECK + jr nz, .next + push hl + push de + ld a, l + call GetCardIDFromDeckIndex + call GetCardType + pop de + pop hl + cp TYPE_ENERGY_FIRE + jr nz, .next + ld a, l + ld [de], a + inc de + inc c +.next + inc l + ld a, l + cp DECK_SIZE + jr c, .loop_cards + ld a, $ff + ld [de], a + +; check how many were found + ld a, c + or a + jr nz, .found + ; return if none found + ldtx hl, ThereWasNoFireEnergyText + call DrawWideTextBox_WaitForInput + call Func_2c0bd + ret + +.found +; pick a random number between 1 and 4, +; up to the maximum number of Fire Energy +; cards that were found. + ld a, 4 + call Random + inc a + cp c + jr c, .ok + ld a, c + +.ok + ldh [hCurSelectionItem], a +; load correct attack animation depending +; on what side the effect is from. + ld d, ATK_ANIM_FIREGIVER_PLAYER + ld a, [wDuelistType] + cp DUELIST_TYPE_PLAYER + jr z, .player_1 +; opponent + ld d, ATK_ANIM_FIREGIVER_OPP +.player_1 + ld a, d + ld [wLoadedAttackAnimation], a + +; start loop for adding Energy cards to hand + ldh a, [hCurSelectionItem] + ld c, a + ld hl, wDuelTempList +.loop_energy + push hl + push bc + ld bc, $0 + ldh a, [hWhoseTurn] + ld h, a + bank1call PlayAttackAnimation + bank1call WaitAttackAnimation + +; load correct coordinates to update the number of cards +; in hand and deck during animation. + lb bc, 18, 7 ; x, y for hand number + ld e, 3 ; y for deck number + ld a, [wLoadedAttackAnimation] + cp ATK_ANIM_FIREGIVER_PLAYER + jr z, .player_2 + lb bc, 4, 5 ; x, y for hand number + ld e, 10 ; y for deck number + +.player_2 +; update and print number of cards in hand + ld a, DUELVARS_NUMBER_OF_CARDS_IN_HAND + call GetTurnDuelistVariable + inc a + bank1call WriteTwoDigitNumberInTxSymbolFormat +; update and print number of cards in deck + ld a, DUELVARS_NUMBER_OF_CARDS_NOT_IN_DECK + call GetTurnDuelistVariable + ld a, DECK_SIZE - 1 + sub [hl] + ld c, e + bank1call WriteTwoDigitNumberInTxSymbolFormat + +; load Fire Energy card index and add to hand + pop bc + pop hl + ld a, [hli] + call SearchCardInDeckAndAddToHand + call AddCardToHand + dec c + jr nz, .loop_energy + +; load the number of cards added to hand and print text + ldh a, [hCurSelectionItem] + ld l, a + ld h, $00 + call LoadTxRam3 + ldtx hl, DrewFireEnergyFromTheHandText + call DrawWideTextBox_WaitForInput + call Func_2c0bd + ret + +Moltres2DiveBomb_AIEffect: ; 2d76e (b:576e) + ld a, 70 / 2 + lb de, 0, 70 + jp SetExpectedAIDamage + +Moltres2DiveBomb_Success50PercentEffect: ; 2d776 (b:5776) + ldtx de, SuccessCheckIfHeadsAttackIsSuccessfulText + call TossCoin_BankB + jr c, .heads +; tails + xor a + call SetDefiniteDamage + call SetWasUnsuccessful + ret +.heads + ld a, ATK_ANIM_DIVE_BOMB + ld [wLoadedAttackAnimation], a + ret + +; output in de the number of energy cards +; attached to the Defending Pokemon times 10. +; used for attacks that deal 10x number of energy +; cards attached to the Defending card. +GetEnergyAttachedMultiplierDamage: ; 2d78c (b:578c) + call SwapTurn + ld a, DUELVARS_CARD_LOCATIONS + call GetTurnDuelistVariable + + ld c, 0 +.loop + ld a, [hl] + cp CARD_LOCATION_ARENA + jr nz, .next + ; is in Arena + ld a, l + call GetCardIDFromDeckIndex + call GetCardType + and TYPE_ENERGY + jr z, .next + ; is Energy attached to Arena card + inc c +.next + inc l + ld a, l + cp DECK_SIZE + jr c, .loop + + call SwapTurn + ld l, c + ld h, $00 + ld b, $00 + add hl, hl ; hl = 2 * c + add hl, hl ; hl = 4 * c + add hl, bc ; hl = 5 * c + add hl, hl ; hl = 10 * c + ld e, l + ld d, h + ret + +; draws list of Energy Cards in Discard Pile +; for Player to select from. +; the Player can select up to 2 cards from the list. +; these cards are given in $ff-terminated list +; in hTempList. +HandleEnergyCardsInDiscardPileSelection: ; 2d7bc (b:57bc) + push hl + xor a + ldh [hCurSelectionItem], a + call CreateEnergyCardListFromDiscardPile_AllEnergy + pop hl + jr c, .finish + + call DrawWideTextBox_WaitForInput +.loop +; draws Discard Pile screen and textbox, +; and handles Player input + bank1call InitAndDrawCardListScreenLayout + ldtx hl, ChooseAnEnergyCardText + ldtx de, PlayerDiscardPileText + bank1call SetCardListHeaderText + bank1call DisplayCardList + jr nc, .selected + +; Player is trying to exit screen, +; but can select up to 2 cards total. +; prompt Player to confirm exiting screen. + ld a, 2 + call AskWhetherToQuitSelectingCards + jr c, .loop + jr .finish + +.selected +; a card was selected, so add it to list + call GetNextPositionInTempList + ldh a, [hTempCardIndex_ff98] + ld [hl], a + call RemoveCardFromDuelTempList + or a + jr z, .finish ; no more cards? + ldh a, [hCurSelectionItem] + cp 2 + jr c, .loop ; already selected 2 cards? + +.finish +; place terminating byte on list + call GetNextPositionInTempList + ld [hl], $ff + or a + ret + +; returns carry if Pkmn Power cannot be used, and +; sets the correct text in hl for failure. +Curse_CheckDamageAndBench: ; 2d7fc (b:57fc) + ldh a, [hTempPlayAreaLocation_ff9d] + ldh [hTemp_ffa0], a + +; fail if Pkmn Power has already been used + add DUELVARS_ARENA_CARD_FLAGS + call GetTurnDuelistVariable + ldtx hl, OnlyOncePerTurnText + and USED_PKMN_POWER_THIS_TURN + jr nz, .set_carry + +; fail if Opponent only has 1 Pokemon in Play Area + call SwapTurn + ld a, DUELVARS_NUMBER_OF_POKEMON_IN_PLAY_AREA + call GetTurnDuelistVariable + call SwapTurn + ldtx hl, CannotUseSinceTheresOnly1PkmnText + cp 2 + jr c, .set_carry + +; fail if Opponent has no damage counters + call SwapTurn + call CheckIfPlayAreaHasAnyDamage + call SwapTurn + ldtx hl, NoPokemonWithDamageCountersText + jr c, .set_carry + +; return carry if Pkmn Power cannot be used due +; to Toxic Gas or status. + ldh a, [hTempPlayAreaLocation_ff9d] + call CheckCannotUseDueToStatus_OnlyToxicGasIfANon0 + ret + +.set_carry + scf + ret + +Curse_PlayerSelectEffect: ; 2d834 (b:5834) + ldtx hl, ProcedureForCurseText + bank1call DrawWholeScreenTextBox + call SwapTurn + xor a + ldh [hCurSelectionItem], a + bank1call Func_61a1 +.start + bank1call PrintPlayAreaCardList_EnableLCD + push af + ldh a, [hCurSelectionItem] + ld hl, PlayAreaSelectionMenuParameters + call InitializeMenuParameters + pop af + ld [wNumMenuItems], a + +; first pick a target to take 1 damage counter from. +.loop_input_first + call DoFrame + call HandleMenuInput + jr nc, .loop_input_first + cp $ff + jr z, .cancel + ldh [hCurSelectionItem], a + ldh [hTempPlayAreaLocation_ffa1], a + call GetCardDamageAndMaxHP + or a + jr nz, .picked_first ; test if has damage + ; play sfx + call Func_3794 + jr .loop_input_first + +.picked_first +; give 10 HP to card selected, draw the scene, +; then immediately revert this. + ldh a, [hTempPlayAreaLocation_ffa1] + add DUELVARS_ARENA_CARD_HP + call GetTurnDuelistVariable + push af + push hl + add 10 + ld [hl], a + bank1call PrintPlayAreaCardList_EnableLCD + pop hl + pop af + ld [hl], a + +; draw damage counter on cursor + ldh a, [hTempPlayAreaLocation_ffa1] + ld b, SYM_HP_NOK + call DrawSymbolOnPlayAreaCursor + +; handle input to pick the target to receive the damage counter. +.loop_input_second + call DoFrame + call HandleMenuInput + jr nc, .loop_input_second + ldh [hPlayAreaEffectTarget], a + cp $ff + jr nz, .a_press ; was a pressed? + +; b press +; erase the damage counter symbol +; and loop back up again. + ldh a, [hTempPlayAreaLocation_ffa1] + ld b, SYM_SPACE + call DrawSymbolOnPlayAreaCursor + call EraseCursor + jr .start + +.a_press + ld hl, hTempPlayAreaLocation_ffa1 + cp [hl] + jr z, .loop_input_second ; same as first? +; a different Pokemon was picked, +; so store this Play Area location +; and erase the damage counter in the cursor. + ldh a, [hTempPlayAreaLocation_ffa1] + ld b, SYM_SPACE + call DrawSymbolOnPlayAreaCursor + call EraseCursor + call SwapTurn + or a + ret + +.cancel +; return carry if operation was cancelled. + call SwapTurn + scf + ret + +Curse_TransferDamageEffect: ; 2d8bb (b:58bb) +; set Pkmn Power as used + ldh a, [hTempList] + add DUELVARS_ARENA_CARD_FLAGS + call GetTurnDuelistVariable + set USED_PKMN_POWER_THIS_TURN_F, [hl] + +; figure out the type of duelist that used Curse. +; if it was the player, no need to draw the Play Area screen. + call SwapTurn + ld a, DUELVARS_DUELIST_TYPE + call GetNonTurnDuelistVariable + cp DUELIST_TYPE_PLAYER + jr z, .vs_player + +; vs. opponent + bank1call Func_61a1 +.vs_player +; transfer the damage counter to the targets that were selected. + ldh a, [hPlayAreaEffectTarget] + add DUELVARS_ARENA_CARD_HP + call GetTurnDuelistVariable + sub 10 + ld [hl], a + ldh a, [hTempPlayAreaLocation_ffa1] + add DUELVARS_ARENA_CARD_HP + ld l, a + ld a, 10 + add [hl] + ld [hl], a + + bank1call PrintPlayAreaCardList_EnableLCD + ld a, DUELVARS_DUELIST_TYPE + call GetNonTurnDuelistVariable + cp DUELIST_TYPE_PLAYER + jr z, .done +; vs. opponent + ldh a, [hPlayAreaEffectTarget] + ldh [hTempPlayAreaLocation_ff9d], a + bank1call Func_6194 + +.done + call SwapTurn + call ExchangeRNG + bank1call Func_6e49 + ret + +GengarDarkMind_PlayerSelectEffect: ; 2d903 (b:5903) + ld a, DUELVARS_NUMBER_OF_POKEMON_IN_PLAY_AREA + call GetNonTurnDuelistVariable + cp 2 + jr nc, .has_bench +; no bench Pokemon to damage. + ld a, $ff + ldh [hTemp_ffa0], a + ret + +.has_bench +; opens Play Area screen to select Bench Pokemon +; to damage, and store it before returning. + ldtx hl, ChoosePkmnInTheBenchToGiveDamageText + call DrawWideTextBox_WaitForInput + call SwapTurn + bank1call HasAlivePokemonInBench +.loop_input + bank1call OpenPlayAreaScreenForSelection + jr c, .loop_input + ldh a, [hTempPlayAreaLocation_ff9d] + ldh [hTemp_ffa0], a + call SwapTurn + ret + +GengarDarkMind_AISelectEffect: ; 2d92a (b:592a) + ld a, $ff + ldh [hTemp_ffa0], a + ld a, DUELVARS_NUMBER_OF_POKEMON_IN_PLAY_AREA + call GetNonTurnDuelistVariable + cp 2 + ret c ; return if no Bench Pokemon +; just pick Pokemon with lowest remaining HP. + call GetBenchPokemonWithLowestHP + ldh [hTemp_ffa0], a + ret + +GengarDarkMind_DamageBenchEffect: ; 2d93c (b:593c) + ldh a, [hTemp_ffa0] + cp $ff + ret z ; no target chosen + call SwapTurn + ld b, a + ld de, 10 + call DealDamageToPlayAreaPokemon_RegularAnim + call SwapTurn + ret + +SleepingGasEffect: ; 2d94f (b:594f) + call Sleep50PercentEffect + call nc, SetNoEffectFromStatus + ret + +DestinyBond_CheckEnergy: ; 2d956 (b:5956) + ld e, PLAY_AREA_ARENA + call GetPlayAreaCardAttachedEnergies + ld a, [wAttachedEnergies + PSYCHIC] + ldtx hl, NotEnoughPsychicEnergyText + cp 1 + ret + +DestinyBond_PlayerSelectEffect: ; 2d964 (b:5964) +; handle input and display of Energy card list + ld a, TYPE_ENERGY_PSYCHIC + call CreateListOfEnergyAttachedToArena + xor a + bank1call DisplayEnergyDiscardScreen + bank1call HandleEnergyDiscardMenuInput + ret c + ldh a, [hTempCardIndex_ff98] + ldh [hTempList], a + ret + +DestinyBond_AISelectEffect: ; 2d976 (b:5976) +; pick first card in list + ld a, TYPE_ENERGY_PSYCHIC + call CreateListOfEnergyAttachedToArena + ld a, [wDuelTempList] + ldh [hTempList], a + ret + +DestinyBond_DiscardEffect: ; 2d981 (b:5981) + ldh a, [hTempList] + call PutCardInDiscardPile + ret + +DestinyBond_DestinyBondEffect: ; 2d987 (b:5987) + ld a, SUBSTATUS1_DESTINY_BOND + call ApplySubstatus1ToDefendingCard + ret + +; returns carry if no Energy cards in Discard Pile. +EnergyConversion_CheckEnergy: ; 2d98d (b:598d) + call CreateEnergyCardListFromDiscardPile_AllEnergy + ldtx hl, ThereAreNoEnergyCardsInDiscardPileText + ret + +EnergyConversion_PlayerSelectEffect: ; 2d994 (b:5994) + ldtx hl, Choose2EnergyCardsFromDiscardPileForHandText + call HandleEnergyCardsInDiscardPileSelection + ret + +EnergyConversion_AISelectEffect: ; 2d99b (b:599b) + call CreateEnergyCardListFromDiscardPile_AllEnergy + ld hl, wDuelTempList + ld de, hTempList + ld c, 2 +; select the first two energy cards found in Discard Pile +.loop + ld a, [hli] + cp $ff + jr z, .done + ld [de], a + inc de + dec c + jr nz, .loop +.done + ld a, $ff + ld [de], a + ret + +EnergyConversion_AddToHandEffect: ; 2d9b4 (b:59b4) +; damage itself + ld a, 10 + call DealRecoilDamageToSelf + +; loop cards that were chosen +; until $ff is reached, +; and move them to the hand. + ld hl, hTempList + ld de, wDuelTempList +.loop_cards + ld a, [hli] + ld [de], a + inc de + cp $ff + jr z, .done + call MoveDiscardPileCardToHand + call AddCardToHand + jr .loop_cards + +.done + call IsPlayerTurn + ret c + bank1call Func_4b38 + ret + +; return carry if Defending Pokemon is not asleep +DreamEaterEffect: ; 2d9d6 (b:59d6) + ld a, DUELVARS_ARENA_CARD_STATUS + call GetNonTurnDuelistVariable + and CNF_SLP_PRZ + cp ASLEEP + ret z ; return if asleep +; not asleep, set carry and load text + ldtx hl, OpponentIsNotAsleepText + scf + ret + +TransparencyEffect: ; 2d9e5 (b:59e5) + scf + ret + +; returns carry if neither the Turn Duelist or +; the non-Turn Duelist have any deck cards. +Prophecy_CheckDeck: ; 2d9e7 (b:59e7) + ld a, DUELVARS_NUMBER_OF_CARDS_NOT_IN_DECK + call GetTurnDuelistVariable + cp DECK_SIZE + jr c, .no_carry + ld a, DUELVARS_NUMBER_OF_CARDS_NOT_IN_DECK + call GetNonTurnDuelistVariable + cp DECK_SIZE + jr c, .no_carry + ldtx hl, NoCardsLeftInTheDeckText + scf + ret +.no_carry + or a + ret + +Prophecy_PlayerSelectEffect: ; 2da00 (b:5a00) + ldtx hl, ProcedureForProphecyText + bank1call DrawWholeScreenTextBox +.select_deck + bank1call DrawDuelMainScene + ldtx hl, PleaseSelectTheDeckText + call TwoItemHorizontalMenu + ldh a, [hKeysHeld] + and B_BUTTON + jr nz, Prophecy_PlayerSelectEffect ; loop back to start + + ldh a, [hCurMenuItem] + ldh [hTempList], a ; store selection in first position in list + or a + jr z, .turn_duelist + +; non-turn duelist + ld a, DUELVARS_NUMBER_OF_CARDS_NOT_IN_DECK + call GetNonTurnDuelistVariable + cp DECK_SIZE + jr nc, .select_deck ; no cards, go back to deck selection + call SwapTurn + call HandleProphecyScreen + call SwapTurn + ret + +.turn_duelist + ld a, DUELVARS_NUMBER_OF_CARDS_NOT_IN_DECK + call GetTurnDuelistVariable + cp DECK_SIZE + jr nc, .select_deck ; no cards, go back to deck selection + call HandleProphecyScreen + ret + +Prophecy_AISelectEffect: ; 2da3c (b:5a3c) +; AI doesn't ever choose this attack +; so this it does no sorting. + ld a, $ff + ldh [hTemp_ffa0], a + ret + +Prophecy_ReorderDeckEffect: ; 2da41 (b:5a41) + ld hl, hTempList + ld a, [hli] + or a + jr z, .ReorderCards ; turn duelist's deck + cp $ff + ret z + + ; non-turn duelist's deck + call SwapTurn + call .ReorderCards + call SwapTurn + ret + +.ReorderCards + ld c, 0 +; add selected cards to hand in the specified order +.loop_add_hand + ld a, [hli] + cp $ff + jr z, .dec_hl + call SearchCardInDeckAndAddToHand + inc c + jr .loop_add_hand + +.dec_hl +; go to last card that was in the list + dec hl + dec hl + +.loop_return_deck +; return the cards to the top of the deck + ld a, [hld] + call ReturnCardToDeck + dec c + jr nz, .loop_return_deck + call IsPlayerTurn + ret c + ; print text in case it was the opponent + ldtx hl, ExchangedCardsInDuelistsHandText + call DrawWideTextBox_WaitForInput + ret + +; draw and handle Player selection for reordering +; the top 3 cards of Deck. +; the resulting list is output in order in hTempList. +HandleProphecyScreen: ; 2da76 (b:5a76) + ld a, DUELVARS_NUMBER_OF_CARDS_NOT_IN_DECK + call GetTurnDuelistVariable + ld b, a + ld a, DECK_SIZE + sub [hl] ; a = number of cards in deck + +; store in c the number of cards that will be reordered. +; this number is 3, unless the deck as fewer cards than +; that in which case it will be the number of cards remaining. + ld c, 3 + cp c + jr nc, .got_number_cards + ld c, a ; store number of remaining cards in c +.got_number_cards + ld a, c + inc a + ld [wNumberOfCardsToOrder], a + +; store in wDuelTempList the cards +; at top of Deck to be reordered. + ld a, b + add DUELVARS_DECK_CARDS + ld l, a + ld de, wDuelTempList +.loop_top_cards + ld a, [hli] + ld [de], a + inc de + dec c + jr nz, .loop_top_cards + ld a, $ff ; terminating byte + ld [de], a + +.start + call CountCardsInDuelTempList + ld b, a + ld a, 1 ; start at 1 + ldh [hCurSelectionItem], a + +; initialize buffer ahead in wDuelTempList. + ld hl, wDuelTempList + 10 + xor a +.loop_init_buffer + ld [hli], a + dec b + jr nz, .loop_init_buffer + ld [hl], $ff + + bank1call InitAndDrawCardListScreenLayout + ldtx hl, ChooseTheOrderOfTheCardsText + ldtx de, DuelistDeckText + bank1call SetCardListHeaderText + bank1call Func_5735 + +.loop_selection + bank1call DisplayCardList + jr c, .clear + +; first check if this card was already selected + ldh a, [hCurMenuItem] + ld e, a + ld d, $00 + ld hl, wDuelTempList + 10 + add hl, de + ld a, [hl] + or a + jr nz, .loop_selection ; already chosen + +; being here means card hasn't been selected yet, +; so add its order number to buffer and increment +; the sort number for the next card. + ldh a, [hCurSelectionItem] + ld [hl], a + inc a + ldh [hCurSelectionItem], a + bank1call Func_5744 + ldh a, [hCurSelectionItem] + ld hl, wNumberOfCardsToOrder + cp [hl] + jr c, .loop_selection ; still more cards + +; confirm that the ordering has been completed + call EraseCursor + ldtx hl, IsThisOKText + call YesOrNoMenuWithText_LeftAligned + jr c, .start ; if not, return back to beginning of selection + +; write in hTempList the card list +; in order that was selected. + ld hl, wDuelTempList + 10 + ld de, wDuelTempList + ld c, 0 +.loop_order + ld a, [hli] + cp $ff + jr z, .done + push hl + push bc + ld c, a + ld b, $00 + ld hl, hTempList + add hl, bc + ld a, [de] + ld [hl], a + pop bc + pop hl + inc de + inc c + jr .loop_order +; now hTempList has the list of card deck indices +; in the order selected to be place on top of the deck. + +.done + ld b, $00 + ld hl, hTempList + 1 + add hl, bc + ld [hl], $ff ; terminating byte + or a + ret + +.clear +; check if any reordering was done. + ld hl, hCurSelectionItem + ld a, [hl] + cp 1 + jr z, .loop_selection ; none done, go back +; clear the order that was selected thus far. + dec a + ld [hl], a + ld c, a + ld hl, wDuelTempList + 10 +.loop_clear + ld a, [hli] + cp c + jr nz, .loop_clear + ; clear this byte + dec hl + ld [hl], $00 + bank1call Func_5744 + jr .loop_selection + +HypnoDarkMind_PlayerSelectEffect: ; 2db2b (b:5b2b) + ld a, DUELVARS_NUMBER_OF_POKEMON_IN_PLAY_AREA + call GetNonTurnDuelistVariable + cp 2 + jr nc, .has_bench +; no bench Pokemon to damage. + ld a, $ff + ldh [hTemp_ffa0], a + ret + +.has_bench +; opens Play Area screen to select Bench Pokemon +; to damage, and store it before returning. + ldtx hl, ChoosePkmnInTheBenchToGiveDamageText + call DrawWideTextBox_WaitForInput + call SwapTurn + bank1call HasAlivePokemonInBench +.loop_input + bank1call OpenPlayAreaScreenForSelection + jr c, .loop_input + ldh a, [hTempPlayAreaLocation_ff9d] + ldh [hTemp_ffa0], a + call SwapTurn + ret + +HypnoDarkMind_AISelectEffect: ; 2db52 (b:5b52) + ld a, $ff + ldh [hTemp_ffa0], a + ld a, DUELVARS_NUMBER_OF_POKEMON_IN_PLAY_AREA + call GetNonTurnDuelistVariable + cp 2 + ret c ; return if no Bench Pokemon +; just pick Pokemon with lowest remaining HP. + call GetBenchPokemonWithLowestHP + ldh [hTemp_ffa0], a + ret + +HypnoDarkMind_DamageBenchEffect: ; 2db64 (b:5b64) + ldh a, [hTemp_ffa0] + cp $ff + ret z ; no target chosen + call SwapTurn + ld b, a + ld de, 10 + call DealDamageToPlayAreaPokemon_RegularAnim + call SwapTurn + ret + +InvisibleWallEffect: ; 2db77 (b:5b77) + scf + ret + +MrMimeMeditate_AIEffect: ; 2db79 (b:5b79) + call MrMimeMeditate_DamageBoostEffect + jp SetDefiniteAIDamage + +MrMimeMeditate_DamageBoostEffect: ; 2db7f (b:5b7f) +; add damage counters of Defending card to damage + call SwapTurn + ld e, PLAY_AREA_ARENA + call GetCardDamageAndMaxHP + call SwapTurn + call AddToDamage + ret + +; returns carry if Damage Swap cannot be used. +DamageSwap_CheckDamage: ; 2db8e (b:5b8e) + ldh a, [hTempPlayAreaLocation_ff9d] + ldh [hTemp_ffa0], a + call CheckIfPlayAreaHasAnyDamage + jr c, .no_damage + ldh a, [hTempPlayAreaLocation_ff9d] + call CheckCannotUseDueToStatus_OnlyToxicGasIfANon0 + ret +.no_damage + ldtx hl, NoPokemonWithDamageCountersText + scf + ret + +DamageSwap_SelectAndSwapEffect: ; 2dba2 (b:5ba2) + ld a, DUELVARS_DUELIST_TYPE + call GetTurnDuelistVariable + cp DUELIST_TYPE_PLAYER + jr z, .player +; non-player + bank1call Func_61a1 + bank1call PrintPlayAreaCardList_EnableLCD + ret + +.player + ldtx hl, ProcedureForDamageSwapText + bank1call DrawWholeScreenTextBox + xor a + ldh [hCurSelectionItem], a + bank1call Func_61a1 + +.start + bank1call PrintPlayAreaCardList_EnableLCD + push af + ldh a, [hCurSelectionItem] + ld hl, PlayAreaSelectionMenuParameters + call InitializeMenuParameters + pop af + ld [wNumMenuItems], a + +; handle selection of Pokemon to take damage from +.loop_input_first + call DoFrame + call HandleMenuInput + jr nc, .loop_input_first + cp $ff + ret z ; quit when B button is pressed + + ldh [hTempPlayAreaLocation_ffa1], a + ldh [hCurSelectionItem], a + +; if card has no damage, play sfx and return to start + call GetCardDamageAndMaxHP + or a + jr z, .no_damage + +; take damage away temporarily to draw UI. + ldh a, [hTempPlayAreaLocation_ffa1] + add DUELVARS_ARENA_CARD_HP + call GetTurnDuelistVariable + push af + push hl + add 10 + ld [hl], a + bank1call PrintPlayAreaCardList_EnableLCD + pop hl + pop af + ld [hl], a + +; draw damage counter in cursor + ldh a, [hTempPlayAreaLocation_ffa1] + ld b, SYM_HP_NOK + call DrawSymbolOnPlayAreaCursor + +; handle selection of Pokemon to give damage to +.loop_input_second + call DoFrame + call HandleMenuInput + jr nc, .loop_input_second + ; if B is pressed, return damage counter + ; to card that it was taken from + cp $ff + jr z, .update_ui + +; try to give the card selected the damage counter +; if it would KO, ignore it. + ldh [hPlayAreaEffectTarget], a + ldh [hCurSelectionItem], a + call TryGiveDamageCounter_DamageSwap + jr c, .loop_input_second + + ld a, OPPACTION_6B15 + call SetOppAction_SerialSendDuelData + +.update_ui + ldh a, [hTempPlayAreaLocation_ffa1] + ld b, SYM_SPACE + call DrawSymbolOnPlayAreaCursor + call EraseCursor + jr .start + +.no_damage + call Func_3794 + jr .loop_input_first + +; tries to give damage counter to hPlayAreaEffectTarget, +; and if successful updates UI screen. +DamageSwap_SwapEffect: ; 2dc27 (b:5c27) + call TryGiveDamageCounter_DamageSwap + ret c + bank1call PrintPlayAreaCardList_EnableLCD + or a + ret + +; tries to give the damage counter to the target +; chosen by the Player (hPlayAreaEffectTarget). +; if the damage counter would KO card, then do +; not give the damage counter and return carry. +TryGiveDamageCounter_DamageSwap: ; 2dc30 (b:5c30) + ldh a, [hPlayAreaEffectTarget] + add DUELVARS_ARENA_CARD_HP + call GetTurnDuelistVariable + sub 10 + jr z, .set_carry ; would bring HP to zero? +; has enough HP to receive a damage counter + ld [hl], a + ldh a, [hTempPlayAreaLocation_ffa1] + add DUELVARS_ARENA_CARD_HP + ld l, a + ld a, 10 + add [hl] + ld [hl], a + or a + ret +.set_carry + scf + ret + +PsywaveEffect: ; 2dc49 (b:5c49) + call GetEnergyAttachedMultiplierDamage + ld hl, wDamage + ld [hl], e + inc hl + ld [hl], d + ret + +; returns carry if neither Duelist has evolved Pokemon. +DevolutionBeam_CheckPlayArea: ; 2dc53 (b:5c53) + call CheckIfTurnDuelistHasEvolvedCards + ret nc + call SwapTurn + call CheckIfTurnDuelistHasEvolvedCards + call SwapTurn + ldtx hl, ThereAreNoStage1PokemonText + ret + +; returns carry of Player cancelled selection. +; otherwise, output in hTemp_ffa0 which Play Area +; was selected ($0 = own Play Area, $1 = opp. Play Area) +; and in hTempPlayAreaLocation_ffa1 selected card. +DevolutionBeam_PlayerSelectEffect: ; 2dc64 (b:5c64) + ldtx hl, ProcedureForDevolutionBeamText + bank1call DrawWholeScreenTextBox + +.start + bank1call DrawDuelMainScene + ldtx hl, PleaseSelectThePlayAreaText + call TwoItemHorizontalMenu + ldh a, [hKeysHeld] + and B_BUTTON + jr nz, .set_carry + +; a Play Area was selected + ldh a, [hCurMenuItem] + or a + jr nz, .opp_chosen + +; player chosen + call HandleEvolvedCardSelection + jr c, .start + + xor a +.store_selection + ld hl, hTemp_ffa0 + ld [hli], a ; store which Duelist Play Area selected + ldh a, [hTempPlayAreaLocation_ff9d] + ld [hl], a ; store which card selected + or a + ret + +.opp_chosen + call SwapTurn + call HandleEvolvedCardSelection + call SwapTurn + jr c, .start + ld a, $01 + jr .store_selection + +.set_carry + scf + ret + +DevolutionBeam_AISelectEffect: ; 2dc9e (b:5c9e) + ld a, $01 + ldh [hTemp_ffa0], a + call SwapTurn + call FindFirstNonBasicCardInPlayArea + call SwapTurn + jr c, .found + xor a + ldh [hTemp_ffa0], a + call FindFirstNonBasicCardInPlayArea +.found + ldh [hTempPlayAreaLocation_ffa1], a + ret + +DevolutionBeam_LoadAnimation: ; 2dcb6 (b:5cb6) + xor a ; ATK_ANIM_NONE + ld [wLoadedAttackAnimation], a + ret + +DevolutionBeam_DevolveEffect: ; 2dcbb (b:5cbb) + ldh a, [hTemp_ffa0] + or a + jr z, .DevolvePokemon + cp $ff + ret z + +; opponent's Play Area + call SwapTurn + ldh a, [hTempPlayAreaLocation_ffa1] + jr nz, .skip_handle_no_damage_effect + call HandleNoDamageOrEffect + jr c, .unaffected +.skip_handle_no_damage_effect + call .DevolvePokemon +.unaffected + call SwapTurn + ret + +.DevolvePokemon + ld a, ATK_ANIM_DEVOLUTION_BEAM + ld [wLoadedAttackAnimation], a + ldh a, [hTempPlayAreaLocation_ffa1] + ld b, a + ld c, $00 + ldh a, [hWhoseTurn] + ld h, a + bank1call PlayAttackAnimation + bank1call WaitAttackAnimation + +; load selected card's data + ldh a, [hTempPlayAreaLocation_ffa1] + ldh [hTempPlayAreaLocation_ff9d], a + ld [wTempPlayAreaLocation_cceb], a + add DUELVARS_ARENA_CARD + call GetTurnDuelistVariable + call LoadCardDataToBuffer1_FromDeckIndex + +; check if car is affected + ld a, [wLoadedCard1ID] + ld [wTempNonTurnDuelistCardID], a + ld de, $0 + ldh a, [hTempPlayAreaLocation_ff9d] + or a + jr nz, .skip_substatus_check + call HandleNoDamageOrEffectSubstatus + jr c, .check_no_damage_effect +.skip_substatus_check + call HandleDamageReductionOrNoDamageFromPkmnPowerEffects +.check_no_damage_effect + call CheckNoDamageOrEffect + jr nc, .devolve + call DrawWideTextBox_WaitForInput + ret + +.devolve + ldh a, [hTempPlayAreaLocation_ffa1] + ldh [hTempPlayAreaLocation_ff9d], a + add DUELVARS_ARENA_CARD + call GetTurnDuelistVariable + bank1call GetCardOneStageBelow + call PrintDevolvedCardNameAndLevelText + + ld a, d + call UpdateDevolvedCardHPAndStage + call ResetDevolvedCardStatus + +; add the evolved card to the hand + ld a, e + call AddCardToHand + +; check if this devolution KO's card + ldh a, [hTempPlayAreaLocation_ffa1] + call PrintPlayAreaCardKnockedOutIfNoHP + + xor a + ld [wDuelDisplayedScreen], a + ret + +; returns carry if Turn Duelist +; has no Stage1 or Stage2 cards in Play Area. +CheckIfTurnDuelistHasEvolvedCards: ; 2dd3b (b:5d3b) + ld a, DUELVARS_ARENA_CARD + call GetTurnDuelistVariable + ld d, h + ld e, DUELVARS_ARENA_CARD_STAGE +.loop + ld a, [hli] + cp $ff + jr z, .set_carry + ld a, [de] + inc de + or a + jr z, .loop ; is Basic Stage + ret +.set_carry + scf + ret + +; handles Player selection of an evolved card in Play Area. +; returns carry if Player cancelled operation. +HandleEvolvedCardSelection: ; 2dd50 (b:5d50) + bank1call HasAlivePokemonInPlayArea +.loop + bank1call OpenPlayAreaScreenForSelection + ret c + ldh a, [hTempPlayAreaLocation_ff9d] + add DUELVARS_ARENA_CARD_STAGE + call GetTurnDuelistVariable + or a + jr z, .loop ; if Basic, loop + ret + +; finds first occurrence in Play Area +; of Stage 1 or 2 card, and outputs its +; Play Area location in a, with carry set. +; if none found, don't return carry set. +FindFirstNonBasicCardInPlayArea: ; 2dd62 (b:5d62) + ld a, DUELVARS_NUMBER_OF_POKEMON_IN_PLAY_AREA + call GetTurnDuelistVariable + ld c, a + + ld b, PLAY_AREA_ARENA + ld l, DUELVARS_ARENA_CARD_STAGE +.loop + ld a, [hli] + or a + jr nz, .not_basic + inc b + dec c + jr nz, .loop + or a + ret +.not_basic + ld a, b + scf + ret + +NeutralizingShieldEffect: ; 2dd79 (b:5d79) + scf + ret + +Psychic_AIEffect: ; 2dd7b (b:5d7b) + call Psychic_DamageBoostEffect + jp SetDefiniteAIDamage + +Psychic_DamageBoostEffect: ; 2dd81 (b:5d81) + call GetEnergyAttachedMultiplierDamage + ld hl, wDamage + ld a, e + add [hl] + ld [hli], a + ld a, d + adc [hl] + ld [hl], a + ret + +; return carry if no Psychic Energy attached +Barrier_CheckEnergy: ; 2dd8e (b:5d8e) + ld e, PLAY_AREA_ARENA + call GetPlayAreaCardAttachedEnergies + ld a, [wAttachedEnergies + PSYCHIC] + ldtx hl, NotEnoughPsychicEnergyText + cp 1 + ret + +Barrier_PlayerSelectEffect: ; 2dd9c (b:5d9c) + ld a, TYPE_ENERGY_PSYCHIC + call CreateListOfEnergyAttachedToArena + xor a ; PLAY_AREA_ARENA + bank1call DisplayEnergyDiscardScreen + bank1call HandleEnergyDiscardMenuInput + ret c + ldh a, [hTempCardIndex_ff98] + ldh [hTemp_ffa0], a + ret + +Barrier_AISelectEffect: ; 2ddae (b:5dae) +; AI picks the first energy in list + ld a, TYPE_ENERGY_PSYCHIC + call CreateListOfEnergyAttachedToArena + ld a, [wDuelTempList] + ldh [hTemp_ffa0], a + ret + +Barrier_DiscardEffect: ; 2ddb9 (b:5db9) + ldh a, [hTemp_ffa0] + call PutCardInDiscardPile + ret + +Barrier_BarrierEffect: ; 2ddbf (b:5dbf) + ld a, SUBSTATUS1_BARRIER + call ApplySubstatus1ToDefendingCard + ret + +Mewtwo3EnergyAbsorption_CheckDiscardPile: ; 2ddc5 (b:5dc5) + call CreateEnergyCardListFromDiscardPile_AllEnergy + ldtx hl, ThereAreNoEnergyCardsInDiscardPileText + ret + +Mewtwo3EnergyAbsorption_PlayerSelectEffect: ; 2ddcc (b:5dcc) + ldtx hl, Choose2EnergyCardsFromDiscardPileToAttachText + call HandleEnergyCardsInDiscardPileSelection + ret + +Mewtwo3EnergyAbsorption_AISelectEffect: ; 2ddd3 (b:5dd3) +; AI picks first 2 energy cards + call CreateEnergyCardListFromDiscardPile_AllEnergy + ld hl, wDuelTempList + ld de, hTempList + ld c, 2 +.loop + ld a, [hli] + cp $ff + jr z, .done + ld [de], a + inc de + dec c + jr nz, .loop +.done + ld a, $ff ; terminating byte + ld [de], a + ret + +Mewtwo3EnergyAbsorption_AddToHandEffect: ; 2ddec (b:5dec) + ld hl, hTempList +.loop + ld a, [hli] + cp $ff + ret z + push hl + call MoveDiscardPileCardToHand + call GetTurnDuelistVariable + ld [hl], CARD_LOCATION_ARENA + pop hl + jr .loop + +Mewtwo2EnergyAbsorption_CheckDiscardPile: ; 2ddff (b:5dff) + call CreateEnergyCardListFromDiscardPile_AllEnergy + ldtx hl, ThereAreNoEnergyCardsInDiscardPileText + ret + +Mewtwo2EnergyAbsorption_PlayerSelectEffect: ; 2de06 (b:5e06) + ldtx hl, Choose2EnergyCardsFromDiscardPileToAttachText + call HandleEnergyCardsInDiscardPileSelection + ret + +Mewtwo2EnergyAbsorption_AISelectEffect: ; 2de0d (b:5e0d) +; AI picks first 2 energy cards + call CreateEnergyCardListFromDiscardPile_AllEnergy + ld hl, wDuelTempList + ld de, hTempList + ld c, 2 +.loop + ld a, [hli] + cp $ff + jr z, .done + ld [de], a + inc de + dec c + jr nz, .loop +.done + ld a, $ff ; terminating byte + ld [de], a + ret + +Mewtwo2EnergyAbsorption_AddToHandEffect: ; 2de26 (b:5e26) + ld hl, hTempList +.loop + ld a, [hli] + cp $ff + ret z + push hl + call MoveDiscardPileCardToHand + call GetTurnDuelistVariable + ld [hl], CARD_LOCATION_ARENA + pop hl + jr .loop + +; returns carry if Strange Behavior cannot be used. +StrangeBehavior_CheckDamage: ; 2de39 (b:5e39) +; does Play Area have any damage counters? + ldh a, [hTempPlayAreaLocation_ff9d] + ldh [hTemp_ffa0], a + call CheckIfPlayAreaHasAnyDamage + ldtx hl, NoPokemonWithDamageCountersText + jr c, .set_carry +; can Slowbro receive any damage counters without KO-ing? + ldh a, [hTempPlayAreaLocation_ff9d] + add DUELVARS_ARENA_CARD_HP + call GetTurnDuelistVariable + ldtx hl, CannotUseBecauseItWillBeKnockedOutText + cp 10 + 10 + jr c, .set_carry +; can Pkmn Power be used? + ldh a, [hTempPlayAreaLocation_ff9d] + call CheckCannotUseDueToStatus_OnlyToxicGasIfANon0 + ret + +.set_carry + scf + ret + +StrangeBehavior_SelectAndSwapEffect: ; 2de5b (b:5e5b) + ld a, DUELVARS_DUELIST_TYPE + call GetTurnDuelistVariable + cp DUELIST_TYPE_PLAYER + jr z, .player + +; not player + bank1call Func_61a1 + bank1call PrintPlayAreaCardList_EnableLCD + ret + +.player + ldtx hl, ProcedureForStrangeBehaviorText + bank1call DrawWholeScreenTextBox + + xor a + ldh [hCurSelectionItem], a + bank1call Func_61a1 +.start + bank1call PrintPlayAreaCardList_EnableLCD + push af + ldh a, [hCurSelectionItem] + ld hl, PlayAreaSelectionMenuParameters + call InitializeMenuParameters + pop af + + ld [wNumMenuItems], a +.loop_input + call DoFrame + call HandleMenuInput + jr nc, .loop_input + cp -1 + ret z ; return when B button is pressed + + ldh [hCurSelectionItem], a + ldh [hTempPlayAreaLocation_ffa1], a + ld hl, hTemp_ffa0 + cp [hl] + jr z, .play_sfx ; can't select Slowbro itself + + call GetCardDamageAndMaxHP + or a + jr z, .play_sfx ; can't select card without damage + + call TryGiveDamageCounter_StrangeBehavior + jr c, .play_sfx + ld a, OPPACTION_6B15 + call SetOppAction_SerialSendDuelData + jr .start + +.play_sfx + call Func_3794 + jr .loop_input + +StrangeBehavior_SwapEffect: ; 2deb3 (b:5eb3) + call TryGiveDamageCounter_StrangeBehavior + ret c + bank1call PrintPlayAreaCardList_EnableLCD + or a + ret + +; tries to give the damage counter to the target +; chosen by the Player (hTemp_ffa0). +; if the damage counter would KO card, then do +; not give the damage counter and return carry. +TryGiveDamageCounter_StrangeBehavior: ; 2debc (b:5ebc) + ldh a, [hTemp_ffa0] + add DUELVARS_ARENA_CARD_HP + call GetTurnDuelistVariable + sub 10 + jr z, .set_carry ; would bring HP to zero? +; has enough HP to receive a damage counter + ld [hl], a + ldh a, [hTempPlayAreaLocation_ffa1] + add DUELVARS_ARENA_CARD_HP + ld l, a + ld a, 10 + add [hl] + ld [hl], a + or a + ret +.set_carry + scf + ret + +; returns carry if has no damage counters. +SpacingOut_CheckDamage: ; 2ded5 (b:5ed5) + ld e, PLAY_AREA_ARENA + call GetCardDamageAndMaxHP + ldtx hl, NoDamageCountersText + cp 10 + ret + +SpacingOut_Success50PercentEffect: ; 2dee0 (b:5ee0) + ldtx de, SuccessCheckIfHeadsAttackIsSuccessfulText + call TossCoin_BankB + ldh [hTemp_ffa0], a + jp nc, SetWasUnsuccessful + ld a, ATK_ANIM_RECOVER + ld [wLoadedAttackAnimation], a + ret + +SpacingOut_HealEffect: ; 2def1 (b:5ef1) + ldh a, [hTemp_ffa0] + or a + ret z ; coin toss was tails + ld e, PLAY_AREA_ARENA + call GetCardDamageAndMaxHP + or a + ret z ; no damage counters + ld a, DUELVARS_ARENA_CARD_HP + call GetTurnDuelistVariable + add 10 + ld [hl], a + ret + +; sets carry if no Trainer cards in the Discard Pile. +Scavenge_CheckDiscardPile: ; 2df05 (b:5f05) + ld e, PLAY_AREA_ARENA + call GetPlayAreaCardAttachedEnergies + ld a, [wAttachedEnergies + PSYCHIC] + ldtx hl, NotEnoughPsychicEnergyText + cp 1 + ret c ; return if no Psychic energy attached + call CreateTrainerCardListFromDiscardPile + ldtx hl, ThereAreNoTrainerCardsInDiscardPileText ; this is redundant + ret + +Scavenge_PlayerSelectEnergyEffect: ; 2df1a (b:5f1a) + ld a, TYPE_ENERGY_PSYCHIC + call CreateListOfEnergyAttachedToArena + xor a ; PLAY_AREA_ARENA + bank1call DisplayEnergyDiscardScreen + bank1call HandleEnergyDiscardMenuInput + ret c + ldh a, [hTempCardIndex_ff98] + ldh [hTemp_ffa0], a + or a + ret + +Scavenge_AISelectEffect: ; 2df2d (b:5f2d) +; AI picks first Energy card in list + ld a, TYPE_ENERGY_PSYCHIC + call CreateListOfEnergyAttachedToArena + ld a, [wDuelTempList] + ldh [hTemp_ffa0], a +; AI picks first Trainer card in list + call CreateTrainerCardListFromDiscardPile + ld a, [wDuelTempList] + ldh [hTempPlayAreaLocation_ffa1], a + ret + +Scavenge_DiscardEffect: ; 2df40 (b:5f40) + ldh a, [hTemp_ffa0] + call PutCardInDiscardPile + ret + +Scavenge_PlayerSelectTrainerEffect: ; 2df46 (b:5f46) + call CreateTrainerCardListFromDiscardPile + bank1call Func_5591 + ldtx hl, PleaseSelectCardText + ldtx de, PlayerDiscardPileText + bank1call SetCardListHeaderText +.loop_input + bank1call DisplayCardList + jr c, .loop_input + ldh a, [hTempCardIndex_ff98] + ldh [hTempPlayAreaLocation_ffa1], a + ret + +Scavenge_AddToHandEffect: ; 2df5f (b:5f5f) + ldh a, [hTempPlayAreaLocation_ffa1] + call MoveDiscardPileCardToHand + call AddCardToHand + call IsPlayerTurn + ret c + ldh a, [hTempPlayAreaLocation_ffa1] + ldtx hl, WasPlacedInTheHandText + bank1call DisplayCardDetailScreen + ret + +; returns carry if Defending Pokemon has no attacks +SlowpokeAmnesia_CheckAttacks: ; 2df74 (b:5f74) + call CheckIfDefendingPokemonHasAnyAttack + ldtx hl, NoAttackMayBeChoosenText + ret + +SlowpokeAmnesia_PlayerSelectEffect: ; 2df7b (b:5f7b) + call PlayerPickAttackForAmnesia + ret + +SlowpokeAmnesia_AISelectEffect: ; 2df7f (b:5f7f) + call AIPickAttackForAmnesia + ldh [hTemp_ffa0], a + ret + +SlowpokeAmnesia_DisableEffect: ; 2df85 (b:5f85) + call ApplyAmnesiaToAttack + ret + +; returns carry if Arena card has no Psychic Energy attached +; or if it doesn't have any damage counters. +KadabraRecover_CheckEnergyHP: ; 2df89 (b:5f89) + ld e, PLAY_AREA_ARENA + call GetPlayAreaCardAttachedEnergies + ld a, [wAttachedEnergies + PSYCHIC] + ldtx hl, NotEnoughPsychicEnergyText + cp 1 + ret c ; return if not enough energy + call GetCardDamageAndMaxHP + ldtx hl, NoDamageCountersText + cp 10 + ret ; return carry if no damage + +KadabraRecover_PlayerSelectEffect: ; 2dfa0 (b:5fa0) + ld a, TYPE_ENERGY_PSYCHIC + call CreateListOfEnergyAttachedToArena + xor a ; PLAY_AREA_ARENA + bank1call DisplayEnergyDiscardScreen + bank1call HandleEnergyDiscardMenuInput + ret c + ldh a, [hTempCardIndex_ff98] + ldh [hTemp_ffa0], a ; store card chosen + ret + +KadabraRecover_AISelectEffect: ; 2dfb2 (b:5fb2) + ld a, TYPE_ENERGY_PSYCHIC + call CreateListOfEnergyAttachedToArena + ld a, [wDuelTempList] ; pick first card + ldh [hTemp_ffa0], a + ret + +KadabraRecover_DiscardEffect: ; 2dfbd (b:5fbd) + ldh a, [hTemp_ffa0] + call PutCardInDiscardPile + ret + +KadabraRecover_HealEffect: ; 2dfc3 (b:5fc3) + ld e, PLAY_AREA_ARENA + call GetCardDamageAndMaxHP + ld e, a ; all damage for recovery + ld d, 0 + call ApplyAndAnimateHPRecovery + ret + +JynxDoubleslap_AIEffect: ; 2dfd7 (b:5fd7) + ld a, 20 / 2 + lb de, 0, 20 + jp SetExpectedAIDamage + +JynxDoubleslap_MultiplierEffect: ; 2dfcf (b:5fcf) + ld hl, 10 + call LoadTxRam3 + ldtx de, DamageCheckIfHeadsXDamageText + ld a, 2 + call TossCoinATimes_BankB + call ATimes10 + call SetDefiniteDamage + ret + +JynxMeditate_AIEffect: ; 2dff2 (b:5ff2) + call JynxMeditate_DamageBoostEffect + jp SetDefiniteAIDamage + +JynxMeditate_DamageBoostEffect: ; 2dfec (b:5fec) +; add damage counters of Defending card to damage + call SwapTurn + ld e, PLAY_AREA_ARENA + call GetCardDamageAndMaxHP + call SwapTurn + call AddToDamage + ret + +MysteryAttack_AIEffect: ; 2e001 (b:6001) + ld a, 10 + lb de, 0, 20 + jp SetExpectedAIDamage + +MysteryAttack_RandomEffect: ; 2e009 (b:6009) + ld a, 10 + call SetDefiniteDamage + +; chooses a random effect from 8 possible options. + call UpdateRNGSources + and %111 + ldh [hTemp_ffa0], a + ld hl, .random_effect + jp JumpToFunctionInTable + +.random_effect + dw ParalysisEffect + dw PoisonEffect + dw SleepEffect + dw ConfusionEffect + dw .no_effect ; this will actually activate recovery effect afterwards + dw .no_effect + dw .more_damage + dw .no_damage + +.more_damage + ld a, 20 + call SetDefiniteDamage + ret + +.no_damage + ld a, ATK_ANIM_GLOW_EFFECT + ld [wLoadedAttackAnimation], a + xor a + call SetDefiniteDamage + call SetNoEffectFromStatus +.no_effect + ret + +MysteryAttack_RecoverEffect: ; 2e03e (b:603e) +; in case the 5th option was chosen for random effect, +; trigger recovery effect for 10 HP. + ldh a, [hTemp_ffa0] + cp 4 + ret nz + lb de, 0, 10 + call ApplyAndAnimateHPRecovery + ret + +StoneBarrage_AIEffect: ; 2e04a (b:604a) + ld a, 10 + lb de, 0, 100 + jp SetExpectedAIDamage + +StoneBarrage_MultiplierEffect: ; 2e052 (b:6052) + xor a + ldh [hTemp_ffa0], a +.loop_coin_toss + ldtx de, FlipUntilFailAppears10DamageForEachHeadsText + xor a + call TossCoinATimes_BankB + jr nc, .tails + ld hl, hTemp_ffa0 + inc [hl] ; increase heads count + jr .loop_coin_toss + +.tails +; store resulting damage + ldh a, [hTemp_ffa0] + ld l, a + ld h, 10 + call HtimesL + ld de, wDamage + ld a, l + ld [de], a + inc de + ld a, h + ld [de], a + ret + +OnixHardenEffect: ; 2e075 (b:6075) + ld a, SUBSTATUS1_HARDEN + call ApplySubstatus1ToDefendingCard + ret + +PrimeapeFurySwipes_AIEffect: ; 2e07b (b:607b) + ld a, 60 / 2 + lb de, 0, 60 + jp SetExpectedAIDamage + +PrimeapeFurySwipes_MultiplierEffect: ; 2e083 (b:6083) + ld hl, 20 + call LoadTxRam3 + ldtx de, DamageCheckIfHeadsXDamageText + ld a, 3 + call TossCoinATimes_BankB + add a + call ATimes10 + call SetDefiniteDamage + ret + +TantrumEffect: ; 2e099 (b:6099) + ldtx de, IfTailsYourPokemonBecomesConfusedText + call TossCoin_BankB + ret c ; return if heads +; confuse Pokemon + ld a, ATK_ANIM_MULTIPLE_SLASH + ld [wLoadedAttackAnimation], a + call SwapTurn + call ConfusionEffect + call SwapTurn + ret + +StrikesBackEffect: ; 2e0af (b:60af) + scf + ret + +KabutoArmorEffect: ; 2e0b1 (b:60b1) + scf + ret + +AbsorbEffect: ; 2e0b3 (b:60b3) + ld hl, wDealtDamage + ld a, [hli] + ld h, [hl] + ld l, a + srl h + rr l + bit 0, l + jr z, .rounded + ; round up to nearest 10 + ld de, 5 + add hl, de +.rounded + ld e, l + ld d, h + call ApplyAndAnimateHPRecovery + ret + +SnivelEffect: ; 2e0cb (b:60cb) + ld a, SUBSTATUS2_REDUCE_BY_20 + call ApplySubstatus2ToDefendingCard + ret + +CuboneRage_AIEffect: ; 2e0d1 (b:60d1) + call CuboneRage_DamageBoostEffect + jp SetDefiniteAIDamage + +CuboneRage_DamageBoostEffect: ; 2e0d7 (b:60d7) + ld e, PLAY_AREA_ARENA + call GetCardDamageAndMaxHP + call AddToDamage + ret + +Bonemerang_AIEffect: ; 2e0e0 (b:60e0) + ld a, 60 / 2 + lb de, 0, 60 + jp SetExpectedAIDamage + +Bonemerang_MultiplierEffect: ; 2e0e8 (b:60e8) + ld hl, 30 + call LoadTxRam3 + ldtx de, DamageCheckIfHeadsXDamageText + ld a, 2 + call TossCoinATimes_BankB + ld e, a + add a ; a = 2 * heads + add e ; a = 3 * heads + call ATimes10 + call SetDefiniteDamage + ret + +; returns carry if can't add Pokemon from deck +MarowakCallForFamily_CheckDeckAndPlayArea: ; 2e100 (b:6100) + call CheckIfDeckIsEmpty + ret c ; no cards in deck + ld a, DUELVARS_NUMBER_OF_POKEMON_IN_PLAY_AREA + call GetTurnDuelistVariable + ldtx hl, NoSpaceOnTheBenchText + cp MAX_PLAY_AREA_POKEMON + ccf + ret + +MarowakCallForFamily_PlayerSelectEffect: ; 2e110 (b:6110) + ld a, $ff + ldh [hTemp_ffa0], a + + call CreateDeckCardList + ldtx hl, ChooseBasicFightingPokemonFromDeckText + ldtx bc, FightingPokemonDeckText + lb de, SEARCHEFFECT_BASIC_FIGHTING, $00 + call LookForCardsInDeck + ret c + +; draw Deck list interface and print text + bank1call Func_5591 + ldtx hl, ChooseBasicFightingPokemonText + ldtx de, DuelistDeckText + bank1call SetCardListHeaderText + +.loop + bank1call DisplayCardList + jr c, .pressed_b + + call LoadCardDataToBuffer2_FromDeckIndex + ld a, [wLoadedCard2Type] + cp FIGHTING + jr nz, .play_sfx ; is Fighting? + ld a, [wLoadedCard2Stage] + or a + jr nz, .play_sfx ; is Basic? + ldh a, [hTempCardIndex_ff98] + ldh [hTemp_ffa0], a + or a + ret + +.play_sfx + ; play SFX and loop back + call Func_3794 + jr .loop + +.pressed_b +; figure if Player can exit the screen without selecting, +; that is, if the Deck has no Basic Fighting Pokemon. + ld a, DUELVARS_CARD_LOCATIONS + call GetTurnDuelistVariable +.loop_b_press + ld a, [hl] + cp CARD_LOCATION_DECK + jr nz, .next + ld a, l + call LoadCardDataToBuffer2_FromDeckIndex + ld a, [wLoadedCard1Type] + cp FIGHTING + jr nz, .next ; found, go back to top loop + ld a, [wLoadedCard1Stage] + or a + jr z, .play_sfx ; found, go back to top loop +.next + inc l + ld a, l + cp DECK_SIZE + jr c, .loop_b_press + +; no valid card in Deck, can safely exit screen + ld a, $ff + ldh [hTemp_ffa0], a + or a + ret + +MarowakCallForFamily_AISelectEffect: ; 2e177 (b:6177) + call CreateDeckCardList + ld hl, wDuelTempList +.loop_deck + ld a, [hli] + ldh [hTemp_ffa0], a + cp $ff + ret z ; none found + call LoadCardDataToBuffer2_FromDeckIndex + ld a, [wLoadedCard2Type] + cp FIGHTING + jr nz, .loop_deck + ld a, [wLoadedCard2Stage] + or a + jr nz, .loop_deck +; found + ret + +MarowakCallForFamily_PutInPlayAreaEffect: ; 2e194 (b:6194) + ldh a, [hTemp_ffa0] + cp $ff + jr z, .shuffle + call SearchCardInDeckAndAddToHand + call AddCardToHand + call PutHandPokemonCardInPlayArea + call IsPlayerTurn + jr c, .shuffle + ; display card on screen + ldh a, [hTemp_ffa0] + ldtx hl, PlacedOnTheBenchText + bank1call DisplayCardDetailScreen +.shuffle + call Func_2c0bd + ret + +KarateChop_AIEffect: ; 2e1b4 (b:61b4) + call KarateChop_DamageSubtractionEffect + jp SetDefiniteAIDamage + +KarateChop_DamageSubtractionEffect: ; 2e1ba (b:61ba) + ld e, PLAY_AREA_ARENA + call GetCardDamageAndMaxHP + ld e, a + ld hl, wDamage + ld a, [hl] + sub e + ld [hli], a + ld a, [hl] + sbc 0 + ld [hl], a + rla + ret nc +; cap it to 0 damage + xor a + call SetDefiniteDamage + ret + +SubmissionEffect: ; 2e1d1 (b:61d1) + ld a, 20 + call DealRecoilDamageToSelf + ret + +GolemSelfdestructEffect: ; 2e1d7 (b:61d7) + ld a, 100 + call DealRecoilDamageToSelf + ld a, $01 + ld [wIsDamageToSelf], a + ld a, 20 + call DealDamageToAllBenchedPokemon + call SwapTurn + xor a + ld [wIsDamageToSelf], a + ld a, 20 + call DealDamageToAllBenchedPokemon + call SwapTurn + ret + +GravelerHardenEffect: ; 2e1f6 (b:61f6) + ld a, SUBSTATUS1_HARDEN + call ApplySubstatus1ToDefendingCard + ret + +Ram_SelectSwitchEffect: ; 2e1fc (b:61fc) + ld a, DUELVARS_NUMBER_OF_POKEMON_IN_PLAY_AREA + call GetNonTurnDuelistVariable + cp 2 + jr c, .no_bench + call DuelistSelectForcedSwitch + ldh a, [hTempPlayAreaLocation_ff9d] + ldh [hTemp_ffa0], a + ret +.no_bench + ld a, $ff + ldh [hTemp_ffa0], a + ret + +Ram_RecoilSwitchEffect: ; 2e212 (b:6212) + ld a, 20 + call DealRecoilDamageToSelf + ldh a, [hTemp_ffa0] + call HandleSwitchDefendingPokemonEffect + ret + +LeerEffect: ; 2e21d (b:621d) + ldtx de, IfHeadsOpponentCannotAttackText + call TossCoin_BankB + jp nc, SetWasUnsuccessful + ld a, ATK_ANIM_LEER + ld [wLoadedAttackAnimation], a + ld a, SUBSTATUS2_LEER + call ApplySubstatus2ToDefendingCard + ret + +; return carry if opponent has no Bench Pokemon. +StretchKick_CheckBench: ; 2e231 (b:6231) + ld a, DUELVARS_NUMBER_OF_POKEMON_IN_PLAY_AREA + call GetNonTurnDuelistVariable + ldtx hl, EffectNoPokemonOnTheBenchText + cp 2 + ret + +StretchKick_PlayerSelectEffect: ; 2e23c (b:623c) + ldtx hl, ChoosePkmnInTheBenchToGiveDamageText + call DrawWideTextBox_WaitForInput + call SwapTurn + bank1call HasAlivePokemonInBench +.loop_input + bank1call OpenPlayAreaScreenForSelection + jr c, .loop_input + ldh a, [hTempPlayAreaLocation_ff9d] + ldh [hTemp_ffa0], a + call SwapTurn + ret + +StretchKick_AISelectEffect: ; 2e255 (b:6255) +; chooses Bench Pokemon with least amount of remaining HP + call GetBenchPokemonWithLowestHP + ldh [hTemp_ffa0], a + ret + +StretchKick_BenchDamageEffect: ; 2e25b (b:625b) + call SwapTurn + ldh a, [hTemp_ffa0] + ld b, a + ld de, 20 + call DealDamageToPlayAreaPokemon_RegularAnim + call SwapTurn + ret + +SandAttackEffect: ; 2e26b (b:626b) + ld a, SUBSTATUS2_SAND_ATTACK + call ApplySubstatus2ToDefendingCard + ret + +SandslashFurySwipes_AIEffect: ; 2e271 (b:6271) + ld a, 60 / 2 + lb de, 0, 60 + jp SetExpectedAIDamage + +SandslashFurySwipes_MultiplierEffect: ; 2e279 (b:6279) + ld hl, 20 + call LoadTxRam3 + ldtx de, DamageCheckIfHeadsXDamageText + ld a, 3 + call TossCoinATimes_BankB + add a + call ATimes10 + call SetDefiniteDamage + ret + +EarthquakeEffect: ; 2e28f (b:628f) + ld a, $01 + ld [wIsDamageToSelf], a + ld a, 10 + call DealDamageToAllBenchedPokemon + ret + +PrehistoricPowerEffect: ; 2e29a (b:629a) + scf + ret + +; returns carry if Pkmn Power can't be used. +Peek_OncePerTurnCheck: ; 2e29c (b:629c) + ldh a, [hTempPlayAreaLocation_ff9d] + ldh [hTemp_ffa0], a + add DUELVARS_ARENA_CARD_FLAGS + call GetTurnDuelistVariable + and USED_PKMN_POWER_THIS_TURN + jr nz, .already_used + ldh a, [hTempPlayAreaLocation_ff9d] + call CheckCannotUseDueToStatus_OnlyToxicGasIfANon0 + ret +.already_used + ldtx hl, OnlyOncePerTurnText + scf + ret + +Peek_SelectEffect: ; 2e2b4 (b:62b4) +; set Pkmn Power used flag + ldh a, [hTemp_ffa0] + add DUELVARS_ARENA_CARD_FLAGS + call GetTurnDuelistVariable + set USED_PKMN_POWER_THIS_TURN_F, [hl] + + ld a, DUELVARS_DUELIST_TYPE + call GetTurnDuelistVariable + cp DUELIST_TYPE_LINK_OPP + jr z, .link_opp + and DUELIST_TYPE_AI_OPP + jr nz, .ai_opp + +; player + call Func_3b31 + call HandlePeekSelection + ldh [hAIPkmnPowerEffectParam], a + call SerialSend8Bytes + ret + +.link_opp + call SerialRecv8Bytes + ldh [hAIPkmnPowerEffectParam], a + +.ai_opp + ldh a, [hAIPkmnPowerEffectParam] + bit AI_PEEK_TARGET_HAND_F, a + jr z, .prize_or_deck + and (~AI_PEEK_TARGET_HAND & $ff) ; unset bit to get deck index +; if masked value is higher than $40, then it means +; that AI chose to look at Player's deck. +; all deck indices will be smaller than $40. + cp $40 + jr c, .hand + ldh a, [hAIPkmnPowerEffectParam] + jr .prize_or_deck + +.hand +; AI chose to look at random card in hand, +; so display it to the Player on screen. + call SwapTurn + ldtx hl, PeekWasUsedToLookInYourHandText + bank1call DisplayCardDetailScreen + call SwapTurn + ret + +.prize_or_deck +; AI chose either a prize card or Player's top deck card, +; so show Play Area and draw cursor appropriately. + call Func_3b31 + call SwapTurn + ldh a, [hAIPkmnPowerEffectParam] + xor $80 + call DrawAIPeekScreen + call SwapTurn + ldtx hl, CardPeekWasUsedOnText + call DrawWideTextBox_WaitForInput + ret + +BoneAttackEffect: ; 2e30f (b:630f) + ldtx de, IfHeadsOpponentCannotAttackText + call TossCoin_BankB + ret nc + ld a, SUBSTATUS2_BONE_ATTACK + call ApplySubstatus2ToDefendingCard + ret + +; return carry if neither Play Area +; has room for more Bench Pokemon. +Wail_BenchCheck: ; 2e31c (b:631c) + ld a, DUELVARS_NUMBER_OF_POKEMON_IN_PLAY_AREA + call GetTurnDuelistVariable + cp MAX_PLAY_AREA_POKEMON + jr c, .no_carry + ld a, DUELVARS_NUMBER_OF_POKEMON_IN_PLAY_AREA + call GetNonTurnDuelistVariable + cp MAX_PLAY_AREA_POKEMON + jr c, .no_carry + ldtx hl, NoSpaceOnTheBenchText + scf + ret +.no_carry + or a + ret + +Wail_FillBenchEffect: ; 2e335 (b:6335) + call SwapTurn + call .FillBench + call SwapTurn + call .FillBench + +; display both Play Areas + ldtx hl, BasicPokemonWasPlacedOnEachBenchText + call DrawWideTextBox_WaitForInput + bank1call HasAlivePokemonInPlayArea + bank1call OpenPlayAreaScreenForSelection + call SwapTurn + bank1call HasAlivePokemonInPlayArea + bank1call OpenPlayAreaScreenForSelection + call SwapTurn + ret + +.FillBench ; 2e35a (b:635a) + call CreateDeckCardList + ret c + ld hl, wDuelTempList + call ShuffleCards + +; if no more space in the Bench, then return. +.check_bench + push hl + ld a, DUELVARS_NUMBER_OF_POKEMON_IN_PLAY_AREA + call GetTurnDuelistVariable + pop hl + cp MAX_PLAY_AREA_POKEMON + jr nc, .done + +; there's still space, so look for the next +; Basic Pokemon card to put in the Bench. +.loop + ld a, [hli] + ldh [hTempCardIndex_ff98], a + cp $ff + jr z, .done + call LoadCardDataToBuffer2_FromDeckIndex + ld a, [wLoadedCard2Type] + cp TYPE_ENERGY + jr nc, .loop ; is Pokemon card? + ld a, [wLoadedCard2Stage] + or a + jr nz, .loop ; is Basic? +; place card in Bench + push hl + ldh a, [hTempCardIndex_ff98] + call SearchCardInDeckAndAddToHand + call AddCardToHand + call PutHandPokemonCardInPlayArea + pop hl + jr .check_bench + +.done + call Func_2c0bd + ret + +Thunderpunch_AIEffect: ; 2e399 (b:6399) + ld a, (30 + 40) / 2 + lb de, 30, 40 + jp SetExpectedAIDamage + +Thunderpunch_ModifierEffect: ; 2e3a1 (b:63a1) + ldtx de, IfHeadPlus10IfTails10ToYourselfText + call TossCoin_BankB + ldh [hTemp_ffa0], a + ret nc ; return if got tails + ld a, 10 + call AddToDamage + ret + +Thunderpunch_RecoilEffect: ; 2e3b0 (b:63b0) + ldh a, [hTemp_ffa0] + or a + ret nz ; return if got heads + ld a, 10 + call DealRecoilDamageToSelf + ret + +LightScreenEffect: ; 2e3ba (b:63ba) + ld a, SUBSTATUS1_HALVE_DAMAGE + call ApplySubstatus1ToDefendingCard + ret + +ElectabuzzQuickAttack_AIEffect: ; 2e3c0 (b:63c0) + ld a, (10 + 30) / 2 + lb de, 10, 30 + jp SetExpectedAIDamage + +ElectabuzzQuickAttack_DamageBoostEffect: ; 2e3c8 (b:63c8) + ld hl, 20 + call LoadTxRam3 + ldtx de, DamageCheckIfHeadsPlusDamageText + call TossCoin_BankB + ret nc ; return if tails + ld a, 20 + call AddToDamage + ret + +MagnemiteSelfdestructEffect: ; 2e3db (b:63db) + ld a, 40 + call DealRecoilDamageToSelf + + ld a, $01 + ld [wIsDamageToSelf], a + ld a, 10 + call DealDamageToAllBenchedPokemon + call SwapTurn + + xor a + ld [wIsDamageToSelf], a + ld a, 10 + call DealDamageToAllBenchedPokemon + call SwapTurn + ret + +ZapdosThunder_Recoil50PercentEffect: ; 2e3fa (b:63fa) + ld hl, 30 + call LoadTxRam3 + ldtx de, IfTailsDamageToYourselfTooText + call TossCoin_BankB + ldh [hTemp_ffa0], a + ret + +ZapdosThunder_RecoilEffect: ; 2e409 (b:6409) + ld hl, 30 + call LoadTxRam3 + ldh a, [hTemp_ffa0] + or a + ret nz ; return if got heads + ld a, 30 + call DealRecoilDamageToSelf + ret + +ThunderboltEffect: ; 2e419 (b:6419) + xor a + call CreateArenaOrBenchEnergyCardList + ld hl, wDuelTempList +; put all energy cards in Discard Pile +.loop + ld a, [hli] + cp $ff + ret z + call PutCardInDiscardPile + jr .loop + +ThunderstormEffect: ; 2e429 (b:6429) + ld a, 1 + ldh [hCurSelectionItem], a + + call SwapTurn + ld a, DUELVARS_NUMBER_OF_POKEMON_IN_PLAY_AREA + call GetTurnDuelistVariable + ld c, a + ld b, 0 + ld e, b + jr .next_pkmn + +.check_damage + push de + push bc + call .DisplayText + ld de, $0 + call SwapTurn + call TossCoin_BankB + call SwapTurn + push af + call GetNextPositionInTempList + pop af + ld [hl], a ; store result in list + pop bc + pop de + jr c, .next_pkmn + inc b ; increase number of tails + +.next_pkmn + inc e + dec c + jr nz, .check_damage + +; all coins were tossed for each Benched Pokemon + call GetNextPositionInTempList + ld [hl], $ff + ld a, b + ldh [hTemp_ffa0], a + call Func_3b21 + call SwapTurn + +; tally recoil damage + ldh a, [hTemp_ffa0] + or a + jr z, .skip_recoil + ; deal number of tails times 10 to self + call ATimes10 + call DealRecoilDamageToSelf +.skip_recoil + +; deal damage for Bench Pokemon that got heads + call SwapTurn + ld hl, hTempPlayAreaLocation_ffa1 + ld b, PLAY_AREA_BENCH_1 +.loop_bench + ld a, [hli] + cp $ff + jr z, .done + or a + jr z, .skip_damage ; skip if tails + ld de, 20 + call DealDamageToPlayAreaPokemon_RegularAnim +.skip_damage + inc b + jr .loop_bench + +.done + call SwapTurn + ret + +; displays text for current Bench Pokemon, +; printing its Bench number and name. +.DisplayText ; 2e491 (b:6491) + ld b, e + ldtx hl, BenchText + ld de, wDefaultText + call CopyText + ld a, $30 ; 0 FW character + add b + ld [de], a + inc de + ld a, $20 ; space FW character + ld [de], a + inc de + + ld a, DUELVARS_ARENA_CARD + add b + call GetTurnDuelistVariable + call LoadCardDataToBuffer2_FromDeckIndex + ld hl, wLoadedCard2Name + ld a, [hli] + ld h, [hl] + ld l, a + call CopyText + + xor a + ld [wDuelDisplayedScreen], a + ret + +JolteonQuickAttack_AIEffect: ; 2e4bb (b:64bb) + ld a, (10 + 30) / 2 + lb de, 10, 30 + jp SetExpectedAIDamage + +JolteonQuickAttack_DamageBoostEffect: ; 2e4c3 (b:64c3) + ld hl, 20 + call LoadTxRam3 + ldtx de, DamageCheckIfHeadsPlusDamageText + call TossCoin_BankB + ret nc ; return if tails + ld a, 20 + call AddToDamage + ret + +PinMissile_AIEffect: ; 2e4d6 (b:64d6) + ld a, (20 * 4) / 2 + lb de, 0, 80 + jp SetExpectedAIDamage + +PinMissile_MultiplierEffect: ; 2e4de (b:64de) + ld hl, 20 + call LoadTxRam3 + ldtx de, DamageCheckIfHeadsXDamageText + ld a, 4 + call TossCoinATimes_BankB + add a ; a = 2 * heads + call ATimes10 + call SetDefiniteDamage + ret + +Fly_AIEffect: ; 2e4f4 (b:64f4) + ld a, 30 / 2 + lb de, 0, 30 + jp SetExpectedAIDamage + +Fly_Success50PercentEffect: ; 2e4fc (b:64fc) + ldtx de, SuccessCheckIfHeadsAttackIsSuccessfulText + call TossCoin_BankB + jr c, .heads + xor a ; ATK_ANIM_NONE + ld [wLoadedAttackAnimation], a + call SetDefiniteDamage + call SetWasUnsuccessful + ret +.heads + ld a, ATK_ANIM_AGILITY_PROTECT + ld [wLoadedAttackAnimation], a + ld a, SUBSTATUS1_FLY + call ApplySubstatus1ToDefendingCard + ret + +ThunderJolt_Recoil50PercentEffect: ; 2e51a (b:651a) + ld hl, 10 + call LoadTxRam3 + ldtx de, IfTailsDamageToYourselfTooText + call TossCoin_BankB + ldh [hTemp_ffa0], a + ret + +ThunderJolt_RecoilEffect: ; 2e529 (b:6529) + ld hl, 10 + call LoadTxRam3 + ldh a, [hTemp_ffa0] + or a + ret nz ; return if was heads + ld a, 10 + call DealRecoilDamageToSelf + ret + +Spark_PlayerSelectEffect: ; 2e539 (b:6539) + ld a, $ff + ldh [hTemp_ffa0], a + ld a, DUELVARS_NUMBER_OF_POKEMON_IN_PLAY_AREA + call GetNonTurnDuelistVariable + cp 2 + ret c ; has no Bench Pokemon + + ldtx hl, ChoosePkmnInTheBenchToGiveDamageText + call DrawWideTextBox_WaitForInput + call SwapTurn + bank1call HasAlivePokemonInBench + + ; the following two instructions can be removed + ; since Player selection will overwrite it. + ld a, PLAY_AREA_BENCH_1 + ldh [hTempPlayAreaLocation_ff9d], a + +.loop_input + bank1call OpenPlayAreaScreenForSelection + jr c, .loop_input + ldh a, [hTempPlayAreaLocation_ff9d] + ldh [hTemp_ffa0], a + call SwapTurn + ret + +Spark_AISelectEffect: ; 2e562 (b:6562) + ld a, $ff + ldh [hTemp_ffa0], a + ld a, DUELVARS_NUMBER_OF_POKEMON_IN_PLAY_AREA + call GetNonTurnDuelistVariable + cp 2 + ret c ; has no Bench Pokemon +; AI always picks Pokemon with lowest HP remaining + call GetBenchPokemonWithLowestHP + ldh [hTemp_ffa0], a + ret + +Spark_BenchDamageEffect: ; 2e574 (b:6574) + ldh a, [hTemp_ffa0] + cp $ff + ret z + call SwapTurn + ldh a, [hTemp_ffa0] + ld b, a + ld de, 10 + call DealDamageToPlayAreaPokemon_RegularAnim + call SwapTurn + ret + +Pikachu3GrowlEffect: ; 2e589 (b:6589) + ld a, SUBSTATUS2_GROWL + call ApplySubstatus2ToDefendingCard + ret + +Pikachu4GrowlEffect: ; 2e58f (b:658f) + ld a, SUBSTATUS2_GROWL + call ApplySubstatus2ToDefendingCard + ret + +ChainLightningEffect: ; 2e595 (b:6595) + ld a, 10 + call SetDefiniteDamage + call SwapTurn + call GetArenaCardColor + call SwapTurn + ldh [hCurSelectionItem], a + cp COLORLESS + ret z ; don't damage if colorless + +; opponent's Bench + call SwapTurn + call .DamageSameColorBench + call SwapTurn + +; own Bench + ld a, $01 + ld [wIsDamageToSelf], a + call .DamageSameColorBench + ret + +.DamageSameColorBench ; 2e5ba (b:65ba) + ld a, DUELVARS_NUMBER_OF_POKEMON_IN_PLAY_AREA + call GetTurnDuelistVariable + ld e, a + ld d, PLAY_AREA_ARENA + jr .next_bench + +.check_damage + ld a, d + call GetPlayAreaCardColor + ld c, a + ldh a, [hCurSelectionItem] + cp c + jr nz, .next_bench ; skip if not same color +; apply damage to this Bench card + push de + ld b, d + ld de, 10 + call DealDamageToPlayAreaPokemon_RegularAnim + pop de + +.next_bench + inc d + dec e + jr nz, .check_damage + ret + +RaichuAgilityEffect: ; 2e5dc (b:65dc) + ldtx de, IfHeadsDoNotReceiveDamageOrEffectText + call TossCoin_BankB + ret nc ; skip if got tails + ld a, ATK_ANIM_AGILITY_PROTECT + ld [wLoadedAttackAnimation], a + ld a, SUBSTATUS1_AGILITY + call ApplySubstatus1ToDefendingCard + ret + +RaichuThunder_Recoil50PercentEffect: ; 2e5ee (b:65ee) + ld hl, 30 + call LoadTxRam3 + ldtx de, IfTailsDamageToYourselfTooText + call TossCoin_BankB + ldh [hTemp_ffa0], a + ret + +RaichuThunder_RecoilEffect: ; 2e5fd (b:65fd) + ld hl, 30 + call LoadTxRam3 + ldh a, [hTemp_ffa0] + or a + ret nz ; return if got heads + ld a, 30 + call DealRecoilDamageToSelf + ret + +Gigashock_PlayerSelectEffect: ; 2e60d (b:660d) + call SwapTurn + ld a, DUELVARS_NUMBER_OF_POKEMON_IN_PLAY_AREA + call GetTurnDuelistVariable + cp 2 + jr nc, .has_bench + call SwapTurn + ld a, $ff + ldh [hTempList], a + ret + +.has_bench + ldtx hl, ChooseUpTo3PkmnOnBenchToGiveDamageText + call DrawWideTextBox_WaitForInput + +; init number of items in list and cursor position + xor a + ldh [hCurSelectionItem], a + ld [wce72], a + bank1call Func_61a1 +.start + bank1call PrintPlayAreaCardList_EnableLCD + push af + ld a, [wce72] + ld hl, BenchSelectionMenuParameters + call InitializeMenuParameters + pop af + +; exclude Arena Pokemon from number of items + dec a + ld [wNumMenuItems], a + +.loop_input + call DoFrame + call HandleMenuInput + jr nc, .loop_input + cp -1 + jr z, .try_cancel + + ld [wce72], a + call .CheckIfChosenAlready + jr nc, .not_chosen + ; play SFX + call Func_3794 + jr .loop_input + +.not_chosen +; mark this Play Area location + ldh a, [hCurMenuItem] + inc a + ld b, SYM_LIGHTNING + call DrawSymbolOnPlayAreaCursor +; store it in the list of chosen Bench Pokemon + call GetNextPositionInTempList + ldh a, [hCurMenuItem] + inc a + ld [hl], a + +; check if 3 were chosen already + ldh a, [hCurSelectionItem] + ld c, a + cp 3 + jr nc, .chosen ; check if already chose 3 + + ld a, DUELVARS_NUMBER_OF_POKEMON_IN_PLAY_AREA + call GetTurnDuelistVariable + dec a + cp c + jr nz, .start ; if sill more options available, loop back + ; fallthrough if no other options available to choose + +.chosen + ldh a, [hCurMenuItem] + inc a + call Func_2c10b + ldh a, [hKeysPressed] + and B_BUTTON + jr nz, .try_cancel + call SwapTurn + call GetNextPositionInTempList + ld [hl], $ff ; terminating byte + ret + +.try_cancel + ldh a, [hCurSelectionItem] + or a + jr z, .start ; none selected, can safely loop back to start + +; undo last selection made + dec a + ldh [hCurSelectionItem], a + ld e, a + ld d, $00 + ld hl, hTempList + add hl, de + ld a, [hl] + + push af + ld b, SYM_SPACE + call DrawSymbolOnPlayAreaCursor + call EraseCursor + pop af + + dec a + ld [wce72], a + jr .start + +; returns carry if Bench Pokemon +; in register a was already chosen. +.CheckIfChosenAlready: ; 2e6af (b:66af) + inc a + ld c, a + ldh a, [hCurSelectionItem] + ld b, a + ld hl, hTempList + inc b + jr .next_check +.check_chosen + ld a, [hli] + cp c + scf + ret z ; return if chosen already +.next_check + dec b + jr nz, .check_chosen + or a + ret + +Gigashock_AISelectEffect: ; 2e6c3 (b:66c3) +; if Bench has 3 Pokemon or less, no need for selection, +; since AI will choose them all. + ld a, DUELVARS_NUMBER_OF_POKEMON_IN_PLAY_AREA + call GetNonTurnDuelistVariable + cp MAX_PLAY_AREA_POKEMON - 1 + jr nc, .start_selection + +; select them all + ld hl, hTempList + ld b, PLAY_AREA_ARENA + jr .next_bench +.select_bench + ld [hl], b + inc hl +.next_bench + inc b + dec a + jr nz, .select_bench + ld [hl], $ff ; terminating byte + ret + +.start_selection +; has more than 3 Bench cards, proceed to sort them +; by lowest remaining HP to highest, and pick first 3. + call SwapTurn + dec a + ld c, a + ld b, PLAY_AREA_BENCH_1 + +; first select all of the Bench Pokemon and write to list + ld hl, hTempList +.loop_all + ld [hl], b + inc hl + inc b + dec c + jr nz, .loop_all + ld [hl], $00 ; end list with $00 + +; then check each of the Bench Pokemon HP +; sort them from lowest remaining HP to highest. + ld de, hTempList +.loop_outer + ld a, [de] + add DUELVARS_ARENA_CARD_HP + call GetTurnDuelistVariable + ld c, a + ld l, e + ld h, d + inc hl + +.loop_inner + ld a, [hli] + or a + jr z, .next ; reaching $00 means it's end of list + + push hl + add DUELVARS_ARENA_CARD_HP + call GetTurnDuelistVariable + pop hl + cp c + jr c, .loop_inner + ; a Bench Pokemon was found with less HP + ld c, a ; store its HP + +; switch the two + dec hl + ld b, [hl] + ld a, [de] + ld [hli], a + ld a, b + ld [de], a + jr .loop_inner + +.next + inc de + ld a, [de] + or a + jr nz, .loop_outer + +; done + ld a, $ff ; terminating byte + ldh [hTempList + 3], a + call SwapTurn + ret + +Gigashock_BenchDamageEffect: ; 2e71f (b:671f) + call SwapTurn + ld hl, hTempList +.loop_selection + ld a, [hli] + cp $ff + jr z, .done + push hl + ld b, a + ld de, 10 + call DealDamageToPlayAreaPokemon_RegularAnim + pop hl + jr .loop_selection +.done + call SwapTurn + ret + +Magneton1SelfdestructEffect: ; 2e739 (b:6739) + ld a, 80 + call DealRecoilDamageToSelf + +; own bench + ld a, $01 + ld [wIsDamageToSelf], a + ld a, 20 + call DealDamageToAllBenchedPokemon + +; opponent's bench + call SwapTurn + xor a + ld [wIsDamageToSelf], a + ld a, 20 + call DealDamageToAllBenchedPokemon + call SwapTurn + ret + +MagnetonSonicboom_UnaffectedByColorEffect: ; 2e758 (b:6758) + ld hl, wDamage + 1 + set UNAFFECTED_BY_WEAKNESS_RESISTANCE_F, [hl] + ret + +MagnetonSonicboom_NullEffect: ; 2e75e (b:675e) + ret + +Magneton2SelfdestructEffect: ; 2e75f (b:675f) + ld a, 100 + call DealRecoilDamageToSelf + +; own bench + ld a, $01 + ld [wIsDamageToSelf], a + ld a, 20 + call DealDamageToAllBenchedPokemon + +; opponent's bench + call SwapTurn + xor a + ld [wIsDamageToSelf], a + ld a, 20 + call DealDamageToAllBenchedPokemon + call SwapTurn + ret + +PealOfThunder_InitialEffect: ; 2e77e (b:677e) + scf + ret + +PealOfThunder_RandomlyDamageEffect: ; 2e780 (b:6780) + call ExchangeRNG + ld de, 30 ; damage to inflict + call RandomlyDamagePlayAreaPokemon + bank1call Func_6e49 + ret + +; randomly damages a Pokemon in play, except +; card that is in [hTempPlayAreaLocation_ff9d]. +; plays thunder animation when Play Area is shown. +; input: +; de = amount of damage to deal +RandomlyDamagePlayAreaPokemon: ; 2e78d (b:678d) + xor a + ld [wNoDamageOrEffect], a + +; choose randomly which Play Area to attack + call UpdateRNGSources + and 1 + jr nz, .opp_play_area + +; own Play Area + ld a, $01 + ld [wIsDamageToSelf], a + ld a, DUELVARS_NUMBER_OF_POKEMON_IN_PLAY_AREA + call GetTurnDuelistVariable + call Random + ld b, a + ; can't select Zapdos + ldh a, [hTempPlayAreaLocation_ff9d] + cp b + jr z, RandomlyDamagePlayAreaPokemon ; re-roll Pokemon to attack + +.damage + ld a, ATK_ANIM_THUNDER_PLAY_AREA + ld [wLoadedAttackAnimation], a + call DealDamageToPlayAreaPokemon + ret + +.opp_play_area + xor a + ld [wIsDamageToSelf], a + call SwapTurn + ld a, DUELVARS_NUMBER_OF_POKEMON_IN_PLAY_AREA + call GetTurnDuelistVariable + call Random + ld b, a + call .damage + call SwapTurn + ret + +BigThunderEffect: ; 2e7cb (b:67cb) + call ExchangeRNG + ld de, 70 ; damage to inflict + call RandomlyDamagePlayAreaPokemon + ret + +MagneticStormEffect: ; 2e7d5 (b:67d5) + ld a, DUELVARS_CARD_LOCATIONS + call GetTurnDuelistVariable + +; writes in wDuelTempList all deck indices +; of Energy cards attached to Pokemon +; in the Turn Duelist's Play Area. + ld de, wDuelTempList + ld c, 0 +.loop_card_locations + ld a, [hl] + and CARD_LOCATION_PLAY_AREA + jr z, .next_card_location + +; is a card that is in the Play Area + push hl + push de + push bc + ld a, l + call GetCardIDFromDeckIndex + call GetCardType + pop bc + pop de + pop hl + and TYPE_ENERGY + jr z, .next_card_location +; is an Energy card attached to Pokemon in Play Area + ld a, l + ld [de], a + inc de + inc c +.next_card_location + inc l + ld a, l + cp DECK_SIZE + jr c, .loop_card_locations + ld a, $ff ; terminating byte + ld [de], a + +; divide number of energy cards +; by number of Pokemon in Play Area + ld a, DUELVARS_NUMBER_OF_POKEMON_IN_PLAY_AREA + call GetTurnDuelistVariable + ld b, a + ld a, c + ld c, -1 +.loop_division + inc c + sub b + jr nc, .loop_division + ; c = floor(a / b) + +; evenly divides the Energy cards randomly +; to every Pokemon in the Play Area. + push bc + ld hl, wDuelTempList + call CountCardsInDuelTempList + call ShuffleCards + ld d, c + ld e, PLAY_AREA_ARENA +.start_attach + ld c, d + inc c + jr .check_done +.attach_energy + ld a, [hli] + push hl + push de + push bc + call AddCardToHand + call PutHandCardInPlayArea + pop bc + pop de + pop hl +.check_done + dec c + jr nz, .attach_energy +; go to next Pokemon in Play Area + inc e ; next in Play Area + dec b + jr nz, .start_attach + pop bc + + push hl + ld hl, hTempList + +; fill hTempList with PLAY_AREA_* locations +; that have Pokemon in them. + push hl + xor a +.loop_init + ld [hli], a + inc a + cp b + jr nz, .loop_init + pop hl + +; shuffle them and distribute +; the remaining cards in random order. + ld a, b + call ShuffleCards + pop hl + ld de, hTempList +.next_random_pokemon + ld a, [hl] + cp $ff + jr z, .done + push hl + push de + ld a, [de] + ld e, a + ld a, [hl] + call AddCardToHand + call PutHandCardInPlayArea + pop de + pop hl + inc hl + inc de + jr .next_random_pokemon + +.done + bank1call DrawDuelMainScene + bank1call DrawDuelHUDs + ldtx hl, TheEnergyCardFromPlayAreaWasMovedText + call DrawWideTextBox_WaitForInput + xor a + call Func_2c10b + ret + +ElectrodeSonicboom_UnaffectedByColorEffect: ; 2e870 (b:6870) + ld hl, wDamage + 1 + set UNAFFECTED_BY_WEAKNESS_RESISTANCE_F, [hl] + ret + +ElectrodeSonicboom_NullEffect: ; 2e876 (b:6876) + ret + +; return carry if no cards in Deck +EnergySpike_DeckCheck: ; 2e877 (b:6877) + call CheckIfDeckIsEmpty + ret + +EnergySpike_PlayerSelectEffect: ; 2e87b (b:687b) + ld a, $ff + ldh [hTemp_ffa0], a + +; search cards in Deck + call CreateDeckCardList + ldtx hl, Choose1BasicEnergyCardFromDeckText + ldtx bc, BasicEnergyText + lb de, SEARCHEFFECT_BASIC_ENERGY, 0 + call LookForCardsInDeck + ret c + + bank1call Func_5591 + ldtx hl, ChooseBasicEnergyCardText + ldtx de, DuelistDeckText + bank1call SetCardListHeaderText +.select_card + bank1call DisplayCardList + jr c, .try_cancel + call GetCardIDFromDeckIndex + call GetCardType + cp TYPE_ENERGY_DOUBLE_COLORLESS + jr nc, .select_card ; not a Basic Energy card + and TYPE_ENERGY + jr z, .select_card ; not a Basic Energy card + ; Energy card selected + + ldh a, [hTempCardIndex_ff98] + ldh [hTemp_ffa0], a + call EmptyScreen + ldtx hl, ChoosePokemonToAttachEnergyCardText + call DrawWideTextBox_WaitForInput + +; choose a Pokemon in Play Area to attach card + bank1call HasAlivePokemonInPlayArea +.loop_input + bank1call OpenPlayAreaScreenForSelection + jr c, .loop_input + ldh a, [hTempPlayAreaLocation_ff9d] + ldh [hTempPlayAreaLocation_ffa1], a + ret + +.play_sfx + call Func_3794 + jr .select_card + +.try_cancel +; Player tried exiting screen, if there are +; any Basic Energy cards, Player is forced to select them. +; otherwise, they can safely exit. + ld a, DUELVARS_CARD_LOCATIONS + call GetTurnDuelistVariable +.loop_deck + ld a, [hl] + cp CARD_LOCATION_DECK + jr nz, .next_card + ld a, l + call GetCardIDFromDeckIndex + call GetCardType + and TYPE_ENERGY + jr z, .next_card + cp TYPE_ENERGY_DOUBLE_COLORLESS + jr c, .play_sfx +.next_card + inc l + ld a, l + cp DECK_SIZE + jr c, .loop_deck + ; can exit + + ld a, $ff + ldh [hTemp_ffa0], a + ret + +EnergySpike_AISelectEffect: ; 2e8f1 (b:68f1) +; AI doesn't select any card + ld a, $ff + ldh [hTemp_ffa0], a + ret + +EnergySpike_AttachEnergyEffect: ; 2e8f6 (b:68f6) + ldh a, [hTemp_ffa0] + cp $ff + jr z, .done + +; add card to hand and attach it to the selected Pokemon + call SearchCardInDeckAndAddToHand + call AddCardToHand + ldh a, [hTempPlayAreaLocation_ffa1] + ld e, a + ldh a, [hTemp_ffa0] + call PutHandCardInPlayArea + call IsPlayerTurn + jr c, .done + +; not Player, so show detail screen +; and which Pokemon was chosen to attach Energy. + ldh a, [hTempPlayAreaLocation_ffa1] + add DUELVARS_ARENA_CARD + call GetTurnDuelistVariable + call LoadCardDataToBuffer1_FromDeckIndex + ld hl, wLoadedCard1Name + ld de, wTxRam2_b + ld a, [hli] + ld [de], a + inc de + ld a, [hli] + ld [de], a + ldh a, [hTemp_ffa0] + ldtx hl, AttachedEnergyToPokemonText + bank1call DisplayCardDetailScreen + +.done + call Func_2c0bd + ret + +JolteonDoubleKick_AIEffect: ; 2e930 (b:6930) + ld a, 40 / 2 + lb de, 0, 40 + jp SetExpectedAIDamage + +JolteonDoubleKick_MultiplierEffect: ; 2e938 (b:6938) + ld hl, 20 + call LoadTxRam3 + ldtx de, DamageCheckIfHeadsXDamageText + ld a, 2 + call TossCoinATimes_BankB + add a ; a = 2 * heads + call ATimes10 + call SetDefiniteDamage + ret + +TailWagEffect: ; 2e94e (b:694e) + ldtx de, IfHeadsOpponentCannotAttackText + call TossCoin_BankB + jp nc, SetWasUnsuccessful + ld a, ATK_ANIM_LURE + ld [wLoadedAttackAnimation], a + ld a, SUBSTATUS2_TAIL_WAG + call ApplySubstatus2ToDefendingCard + ret + +EeveeQuickAttack_AIEffect: ; 2e962 (b:5962) + ld a, (10 + 30) / 2 + lb de, 10, 30 + jp SetExpectedAIDamage + +EeveeQuickAttack_DamageBoostEffect: ; 2e96a (b:596a) + ld hl, 20 + call LoadTxRam3 + ldtx de, DamageCheckIfHeadsPlusDamageText + call TossCoin_BankB + ret nc ; return if tails + ld a, 20 + call AddToDamage + ret + +SpearowMirrorMove_AIEffect: ; 2e97d (b:697d) + jr MirrorMoveEffects.AIEffect + +SpearowMirrorMove_InitialEffect1: ; 2e97f (b:697f) + jr MirrorMoveEffects.InitialEffect1 + +SpearowMirrorMove_InitialEffect2: ; 2e981 (b:6981) + jr MirrorMoveEffects.InitialEffect2 + +SpearowMirrorMove_PlayerSelection: ; 2e983 (b:6983) + jr MirrorMoveEffects.PlayerSelection + +SpearowMirrorMove_AISelection: ; 2e985 (b:6985) + jr MirrorMoveEffects.AISelection + +SpearowMirrorMove_BeforeDamage: ; 2e987 (b:6987) + jr MirrorMoveEffects.BeforeDamage + +SpearowMirrorMove_AfterDamage: ; 2e989 (b:6989) + jp MirrorMoveEffects.AfterDamage + +; these are effect commands that Mirror Move uses +; in order to mimic last turn's attack. +; it covers all possible effect steps to perform its commands +; (i.e. selection for Amnesia and Energy discarding attacks, etc) +MirrorMoveEffects: ; 2e98c (b:698c) +.AIEffect + ld a, DUELVARS_ARENA_CARD_LAST_TURN_DAMAGE + call GetTurnDuelistVariable + ld a, [hl] + ld [wAIMinDamage], a + ld [wAIMaxDamage], a + ret + +.InitialEffect1 + ld a, DUELVARS_ARENA_CARD_LAST_TURN_DAMAGE + call GetTurnDuelistVariable + ld a, [hli] + or [hl] + inc hl + or [hl] + inc hl + ret nz ; return if has last turn damage + ld a, [hli] + or a + ret nz ; return if has last turn status + ; no attack received last turn + ldtx hl, YouDidNotReceiveAnAttackToMirrorMoveText + scf + ret + +.InitialEffect2 + ld a, $ff + ldh [hTemp_ffa0], a + ld a, DUELVARS_ARENA_CARD_LAST_TURN_EFFECT + call GetTurnDuelistVariable + or a + ret z ; no effect + cp LAST_TURN_EFFECT_AMNESIA + jp z, PlayerPickAttackForAmnesia + or a + ret + +.PlayerSelection + ld a, DUELVARS_ARENA_CARD_LAST_TURN_EFFECT + call GetTurnDuelistVariable + or a + ret z ; no effect +; handle Energy card discard effect + cp LAST_TURN_EFFECT_DISCARD_ENERGY + jp z, HandleEnergyDiscardEffectSelection + ret + +.AISelection + ld a, $ff + ldh [hTemp_ffa0], a + ld a, DUELVARS_ARENA_CARD_LAST_TURN_EFFECT + call GetTurnDuelistVariable + or a + ret z ; no effect + cp LAST_TURN_EFFECT_DISCARD_ENERGY + jr z, .discard_energy + cp LAST_TURN_EFFECT_AMNESIA + jr z, .pick_amnesia_attack + ret + +.discard_energy + call AIPickEnergyCardToDiscardFromDefendingPokemon + ldh [hTemp_ffa0], a + ret + +.pick_amnesia_attack + call AIPickAttackForAmnesia + ldh [hTemp_ffa0], a + ret + +.BeforeDamage +; if was attacked with Amnesia, apply it to the selected attack + ld a, DUELVARS_ARENA_CARD_LAST_TURN_EFFECT + call GetTurnDuelistVariable + cp LAST_TURN_EFFECT_AMNESIA + jr z, .apply_amnesia + +; otherwise, check if there was last turn damage, +; and write it to wDamage. + ld a, DUELVARS_ARENA_CARD_LAST_TURN_DAMAGE + call GetTurnDuelistVariable + ld de, wDamage + ld a, [hli] + ld [de], a + inc de + ld a, [hld] + ld [de], a + or [hl] + jr z, .no_damage + ld a, ATK_ANIM_HIT + ld [wLoadedAttackAnimation], a +.no_damage + inc hl + inc hl ; DUELVARS_ARENA_CARD_LAST_TURN_STATUS +; check if there was a status applied to Defending Pokemon +; from the attack it used. + push hl + ld a, DUELVARS_ARENA_CARD_STATUS + call GetNonTurnDuelistVariable + ld e, l + ld d, h + pop hl + ld a, [hli] + or a + jr z, .no_status + push hl + push de + call .ExecuteStatusEffect + pop de + pop hl +.no_status +; hl is at DUELVARS_ARENA_CARD_LAST_TURN_SUBSTATUS2 +; apply substatus2 to self + ld e, DUELVARS_ARENA_CARD_SUBSTATUS2 + ld a, [hli] + ld [de], a + ret + +.apply_amnesia + call ApplyAmnesiaToAttack + ret + +.AfterDamage: ; 2ea28 (b:6a28) + ld a, [wNoDamageOrEffect] + or a + ret nz ; is unaffected + ld a, DUELVARS_ARENA_CARD_LAST_TURN_EFFECT + call GetTurnDuelistVariable + cp LAST_TURN_EFFECT_DISCARD_ENERGY + jr nz, .change_weakness + +; execute Energy discard effect for card chosen + call SwapTurn + ldh a, [hTemp_ffa0] + call PutCardInDiscardPile + ld a, DUELVARS_ARENA_CARD_LAST_TURN_EFFECT + call GetTurnDuelistVariable + ld [hl], LAST_TURN_EFFECT_DISCARD_ENERGY + call SwapTurn + +.change_weakness + ld a, DUELVARS_ARENA_CARD_LAST_TURN_CHANGE_WEAK + call GetTurnDuelistVariable + ld a, [hl] + or a + ret z ; weakness wasn't changed last turn + + push hl + call SwapTurn + ld a, DUELVARS_ARENA_CARD + call GetTurnDuelistVariable + call LoadCardDataToBuffer2_FromDeckIndex + call SwapTurn + pop hl + + ld a, [wLoadedCard2Weakness] + or a + ret z ; defending Pokemon has no weakness to change + +; apply same color weakness to Defending Pokemon + ld a, [hl] + push af + ld a, DUELVARS_ARENA_CARD_CHANGED_WEAKNESS + call GetNonTurnDuelistVariable + pop af + ld [hl], a + +; print message of weakness color change + ld c, -1 +.loop_color + inc c + rla + jr nc, .loop_color + ld a, c + call SwapTurn + push af + ld a, DUELVARS_ARENA_CARD + call GetTurnDuelistVariable + call LoadCardDataToBuffer1_FromDeckIndex + pop af + call LoadCardNameAndInputColor + ldtx hl, ChangedTheWeaknessOfPokemonToColorText + call DrawWideTextBox_PrintText + call SwapTurn + ret + +.ExecuteStatusEffect: ; 2ea8f (b:6a8f) + ld c, a + and PSN_DBLPSN + jr z, .cnf_slp_prz + ld b, a + cp DOUBLE_POISONED + push bc + call z, DoublePoisonEffect + pop bc + ld a, b + cp POISONED + push bc + call z, PoisonEffect + pop bc +.cnf_slp_prz + ld a, c + and CNF_SLP_PRZ + ret z + cp CONFUSED + jp z, ConfusionEffect + cp ASLEEP + jp z, SleepEffect + cp PARALYZED + jp z, ParalysisEffect + ret + +FearowAgilityEffect: ; 2eab8 (b:6ab8) + ldtx de, IfHeadsDoNotReceiveDamageOrEffectText + call TossCoin_BankB + ret nc + ld a, ATK_ANIM_AGILITY_PROTECT + ld [wLoadedAttackAnimation], a + ld a, SUBSTATUS1_AGILITY + call ApplySubstatus1ToDefendingCard + ret + +; return carry if cannot use Step In +StepIn_BenchCheck: ; 2eaca (b:6aca) + ldh a, [hTempPlayAreaLocation_ff9d] + ldh [hTemp_ffa0], a + ldtx hl, CanOnlyBeUsedOnTheBenchText + or a + jr z, .set_carry + + add DUELVARS_ARENA_CARD_FLAGS + call GetTurnDuelistVariable + ldtx hl, OnlyOncePerTurnText + and USED_PKMN_POWER_THIS_TURN + jr nz, .set_carry + + ldh a, [hTempPlayAreaLocation_ff9d] + call CheckCannotUseDueToStatus_OnlyToxicGasIfANon0 + ret + +.set_carry + scf + ret + +StepIn_SwitchEffect: ; 2eae8 (b:6ae8) + ldh a, [hTemp_ffa0] + ld e, a + call SwapArenaWithBenchPokemon + ld a, DUELVARS_ARENA_CARD_FLAGS + call GetTurnDuelistVariable + set USED_PKMN_POWER_THIS_TURN_F, [hl] + ret + +Dragonite2Slam_AIEffect: ; 2eaf6 (b:6af6) + ld a, (40 * 2) / 2 + lb de, 0, 80 + jp SetExpectedAIDamage + +Dragonite2Slam_MultiplierEffect: ; 2eafe (b:6afe) + ld hl, 40 + call LoadTxRam3 + ldtx de, DamageCheckIfHeadsXDamageText + ld a, 2 + call TossCoinATimes_BankB + add a + add a + call ATimes10 + call SetDefiniteDamage + ret + +ThickSkinnedEffect: ; 2eb15 (b:6b15) + scf + ret + +LeekSlap_AIEffect: ; 2eb17 (b:6b17) + ld a, 30 / 2 + lb de, 0, 30 + jp SetExpectedAIDamage + +; return carry if already used attack in this duel +LeekSlap_OncePerDuelCheck: ; 2eb1f (b:6b1f) +; can only use attack if it was never used before this duel + ld a, DUELVARS_ARENA_CARD_FLAGS + call GetTurnDuelistVariable + and USED_LEEK_SLAP_THIS_DUEL + ret z + ldtx hl, ThisAttackCannotBeUsedTwiceText + scf + ret + +LeekSlap_SetUsedThisDuelFlag: ; 2eb2c (b:6b2c) + ld a, DUELVARS_ARENA_CARD_FLAGS + call GetTurnDuelistVariable + set USED_LEEK_SLAP_THIS_DUEL_F, [hl] + ret + +LeekSlap_NoDamage50PercentEffect: ; 2eb34 (b:6b34) + ldtx de, DamageCheckIfTailsNoDamageText + call TossCoin_BankB + ret c + xor a ; 0 damage + call SetDefiniteDamage + ret + +FetchEffect: ; 2eb40 (b:6b40) + ldtx hl, Draw1CardFromTheDeckText + call DrawWideTextBox_WaitForInput + bank1call DisplayDrawOneCardScreen + call DrawCardFromDeck + ret c ; return if deck is empty + call AddCardToHand + call LoadCardDataToBuffer1_FromDeckIndex + ld a, [wDuelistType] + cp DUELIST_TYPE_PLAYER + ret nz + ; show card on screen if it was Player + bank1call OpenCardPage_FromHand + ret + +CometPunch_AIEffect: ; 2eb5d (b:6b5d) + ld a, (20 * 4) / 2 + lb de, 0, 80 + jp SetExpectedAIDamage + +CometPunch_MultiplierEffect: ; 2eb65 (b:6b65) + ld hl, 20 + call LoadTxRam3 + ldtx de, DamageCheckIfHeadsXDamageText + ld a, 4 + call TossCoinATimes_BankB + add a + call ATimes10 + call SetDefiniteDamage + ret + +TaurosStomp_AIEffect: ; 2eb7b (b:6b7b) + ld a, (20 + 30) / 2 + lb de, 20, 30 + jp SetExpectedAIDamage + +TaurosStomp_DamageBoostEffect: ; 2eb83 (b:6b83) + ld hl, 10 + call LoadTxRam3 + ldtx de, DamageCheckIfHeadsPlusDamageText + call TossCoin_BankB + ret nc ; tails + ld a, 10 + call AddToDamage + ret + +Rampage_AIEffect: ; 2eb96 (b:6b96) + ld e, PLAY_AREA_ARENA + call GetCardDamageAndMaxHP + call AddToDamage + jp SetDefiniteAIDamage + +Rampage_Confusion50PercentEffect: ; 2eba1 (b:6ba1) + ld e, PLAY_AREA_ARENA + call GetCardDamageAndMaxHP + call AddToDamage + ldtx de, IfTailsYourPokemonBecomesConfusedText + call TossCoin_BankB + ret c ; heads + call SwapTurn + call ConfusionEffect + call SwapTurn + ret + +FuryAttack_AIEffect: ; 2ebba (b:6bba) + ld a, (10 * 2) / 2 + lb de, 0, 20 + jp SetExpectedAIDamage + +FuryAttack_MultiplierEffect: ; 2ebc2 (b:6bc2) + ld hl, 10 + call LoadTxRam3 + ld a, 2 + ldtx de, DamageCheckIfHeadsXDamageText + call TossCoinATimes_BankB + call ATimes10 + call SetDefiniteDamage + ret + +RetreatAidEffect: ; 2ebd7 (b:6bd7) + scf + ret + +DodrioRage_AIEffect: ; 2ebd9 (b:6bd9) + call DodrioRage_DamageBoostEffect + jp SetDefiniteAIDamage + +DodrioRage_DamageBoostEffect: ; 2ebdf (b:6bdf) + ld e, PLAY_AREA_ARENA + call GetCardDamageAndMaxHP + call AddToDamage + ret + +PayDayEffect: ; 2ebe8 (b:6be8) + ldtx de, IfHeadsDraw1CardFromDeckText + call TossCoin_BankB + ret nc ; tails + ldtx hl, Draw1CardFromTheDeckText + call DrawWideTextBox_WaitForInput + bank1call DisplayDrawOneCardScreen + call DrawCardFromDeck + ret c ; empty deck + call AddCardToHand + call LoadCardDataToBuffer1_FromDeckIndex + ld a, [wDuelistType] + cp DUELIST_TYPE_PLAYER + ret nz + ; show card on screen if it was Player + bank1call OpenCardPage_FromHand + ret + +DragonairSlam_AIEffect: ; 2ec0c (b:6c0c) + ld a, (30 * 2) / 2 + lb de, 0, 60 + jp SetExpectedAIDamage + +DragonairSlam_MultiplierEffect: ; 2ec14 (b:6c14) + ld hl, 30 + call LoadTxRam3 + ld a, 2 + ldtx de, DamageCheckIfHeadsXDamageText + call TossCoinATimes_BankB + ld e, a + add a + add e + call ATimes10 + call SetDefiniteDamage + ret + +DragonairHyperBeam_PlayerSelectEffect: ; 2ec2c (b:6c2c) + jp HandleEnergyDiscardEffectSelection + +DragonairHyperBeam_AISelectEffect: ; 2ec2f (b:6c2f) + call AIPickEnergyCardToDiscardFromDefendingPokemon + ldh [hTemp_ffa0], a + ret + +DragonairHyperBeam_DiscardEffect: ; 2ec35 (b:6c35) + call HandleNoDamageOrEffect + ret c ; is unaffected + ldh a, [hTemp_ffa0] + cp $ff + ret z ; no energy card chosen + call SwapTurn + call PutCardInDiscardPile + ld a, DUELVARS_ARENA_CARD_LAST_TURN_EFFECT + call GetTurnDuelistVariable + ld [hl], LAST_TURN_EFFECT_DISCARD_ENERGY + call SwapTurn + ret + +; handles screen for selecting an Energy card to discard +; that is attached to Defending Pokemon, +; and store the Player selection in [hTemp_ffa0]. +HandleEnergyDiscardEffectSelection: ; 2ec4f (b:6c4f) + call SwapTurn + xor a ; PLAY_AREA_ARENA + call CreateArenaOrBenchEnergyCardList + jr c, .no_energy + ldtx hl, ChooseDiscardEnergyCardFromOpponentText + call DrawWideTextBox_WaitForInput + xor a ; PLAY_AREA_ARENA + bank1call DisplayEnergyDiscardScreen + +.loop_input + bank1call HandleEnergyDiscardMenuInput + jr c, .loop_input + + call SwapTurn + ldh a, [hTempCardIndex_ff98] + ldh [hTemp_ffa0], a ; store selected card to discard + ret + +.no_energy + call SwapTurn + ld a, $ff + ldh [hTemp_ffa0], a + ret + +; return carry if Defending Pokemon has no attacks +ClefableMetronome_CheckAttacks: ; 2ec77 (b:6c77) + call CheckIfDefendingPokemonHasAnyAttack + ldtx hl, NoAttackMayBeChoosenText + ret + +ClefableMetronome_AISelectEffect: ; 2ec7e (b:6c7e) + call HandleAIMetronomeEffect + ret + +ClefableMetronome_UseAttackEffect: ; 2ec82 (b:6c82) + ld a, 1 ; energy cost of this attack + call HandlePlayerMetronomeEffect + ret + +ClefableMinimizeEffect: ; 2ec88 (b:6c88) + ld a, SUBSTATUS1_REDUCE_BY_20 + call ApplySubstatus1ToDefendingCard + ret + +HurricaneEffect: ; 2ec8e (b:6c8e) + call HandleNoDamageOrEffect + ret c ; is unaffected + + ld a, DUELVARS_ARENA_CARD_HP + call GetNonTurnDuelistVariable + or a + ret z ; return if Pokemon was KO'd + +; look at all the card locations and put all cards +; that are in the Arena in the hand. + call SwapTurn + ld a, DUELVARS_CARD_LOCATIONS + call GetTurnDuelistVariable +.loop_locations + ld a, [hl] + cp CARD_LOCATION_ARENA + jr nz, .next_card + ; card in Arena found, put in hand + ld a, l + call AddCardToHand +.next_card + inc l + ld a, l + cp DECK_SIZE + jr c, .loop_locations + +; empty the Arena card slot + ld l, DUELVARS_ARENA_CARD + ld a, [hl] + ld [hl], $ff + ld l, DUELVARS_ARENA_CARD_HP + ld [hl], 0 + call LoadCardDataToBuffer1_FromDeckIndex + ld hl, wLoadedCard1Name + ld a, [hli] + ld h, [hl] + ld l, a + call LoadTxRam2 + ldtx hl, PokemonAndAllAttachedCardsReturnedToHandText + call DrawWideTextBox_WaitForInput + xor a + ld [wDuelDisplayedScreen], a + call SwapTurn + ret + +PidgeottoWhirlwind_SelectEffect: ; 2ecd3 (b:6cd3) + ld a, DUELVARS_NUMBER_OF_POKEMON_IN_PLAY_AREA + call GetNonTurnDuelistVariable + cp 2 + jr nc, .switch + ; no Bench Pokemon + ld a, $ff + ldh [hTemp_ffa0], a + ret +.switch + call DuelistSelectForcedSwitch + ldh a, [hTempPlayAreaLocation_ff9d] + ldh [hTemp_ffa0], a + ret + +PidgeottoWhirlwind_SwitchEffect: ; 2ece9 (b:6ce9) + ldh a, [hTemp_ffa0] + call HandleSwitchDefendingPokemonEffect + ret + +PidgeottoMirrorMove_AIEffect: ; 2ecef (b:6cef) + jp MirrorMoveEffects.AIEffect + +PidgeottoMirrorMove_InitialEffect1: ; 2ecf2 (b:6cf2) + jp MirrorMoveEffects.InitialEffect1 + +PidgeottoMirrorMove_InitialEffect2: ; 2ecf5 (b:6cf5) + jp MirrorMoveEffects.InitialEffect2 + +PidgeottoMirrorMove_PlayerSelection: ; 2ecf8 (b:6cf8) + jp MirrorMoveEffects.PlayerSelection + +PidgeottoMirrorMove_AISelection: ; 2ecfb (b:6cfb) + jp MirrorMoveEffects.AISelection + +PidgeottoMirrorMove_BeforeDamage: ; 2ecfe (b:6cfe) + jp MirrorMoveEffects.BeforeDamage + +PidgeottoMirrorMove_AfterDamage: ; 2ed01 (b:6d01) + jp MirrorMoveEffects.AfterDamage + +SingEffect: ; 2ed04 (b:6d04) + call Sleep50PercentEffect + call nc, SetNoEffectFromStatus + ret + +; return carry if Defending Pokemon has no attacks +ClefairyMetronome_CheckAttacks: ; 2ed0b (b:6d0b) + call CheckIfDefendingPokemonHasAnyAttack + ldtx hl, NoAttackMayBeChoosenText + ret + +ClefairyMetronome_AISelectEffect: ; 2ed12 (b:6d12) + call HandleAIMetronomeEffect + ret + +ClefairyMetronome_UseAttackEffect: ; 2ed16 (b:6d16) + ld a, 3 ; energy cost of this attack +; fallthrough + +; handles Metronome selection, and validates +; whether it can use the selected attack. +; if unsuccessful, returns carry. +; input: +; a = amount of colorless energy needed for Metronome +HandlePlayerMetronomeEffect: ; 2ed18 (b:6d18) + ld [wMetronomeEnergyCost], a + ldtx hl, ChooseOppAttackToBeUsedWithMetronomeText + call DrawWideTextBox_WaitForInput + + call HandleDefendingPokemonAttackSelection + ret c ; return if operation cancelled + +; store this attack as selected attack to use + ld hl, wMetronomeSelectedAttack + ld [hl], d + inc hl + ld [hl], e + +; compare selected attack's name with +; the attack that is loaded, which is Metronome. +; if equal, then cannot select it. +; (i.e. cannot use Metronome with Metronome.) + ld hl, wLoadedAttackName + ld a, [hli] + ld h, [hl] + ld l, a + push hl + call SwapTurn + call CopyAttackDataAndDamage_FromDeckIndex + call SwapTurn + pop de + ld hl, wLoadedAttackName + ld a, e + cp [hl] + jr nz, .try_use + inc hl + ld a, d + cp [hl] + jr nz, .try_use + ; cannot select Metronome + ldtx hl, UnableToSelectText +.failed + call DrawWideTextBox_WaitForInput +.set_carry + scf + ret + +.try_use +; run the attack checks to determine +; whether it can be used. + ld a, EFFECTCMDTYPE_INITIAL_EFFECT_1 + call TryExecuteEffectCommandFunction + jr c, .failed + ld a, EFFECTCMDTYPE_INITIAL_EFFECT_2 + call TryExecuteEffectCommandFunction + jr c, .set_carry + ; successful + +; send data to link opponent + bank1call SendAttackDataToLinkOpponent + ld a, OPPACTION_USE_METRONOME_ATTACK + call SetOppAction_SerialSendDuelData + ld hl, wMetronomeSelectedAttack + ld d, [hl] + inc hl + ld e, [hl] + ld a, [wMetronomeEnergyCost] + ld c, a + call SerialSend8Bytes + + ldh a, [hTempCardIndex_ff9f] + ld [wPlayerAttackingCardIndex], a + ld a, [wSelectedAttack] + ld [wPlayerAttackingAttackIndex], a + ld a, [wTempCardID_ccc2] + ld [wPlayerAttackingCardID], a + or a + ret + +; does nothing for AI. +HandleAIMetronomeEffect: ; 2ed86 (b:6d86) + ret + +DoTheWaveEffect: ; 2ed87 (b:6d87) + ld a, DUELVARS_NUMBER_OF_POKEMON_IN_PLAY_AREA + call GetTurnDuelistVariable + dec a ; don't count arena card + call ATimes10 + call AddToDamage + ret + +; return carry if no damage counters +FirstAid_DamageCheck: ; 2ed94 (b:6d94) + ld e, PLAY_AREA_ARENA + call GetCardDamageAndMaxHP + ldtx hl, NoDamageCountersText + cp 10 + ret + +FirstAid_HealEffect: ; 2ed9f (b:6d9f) + lb de, 0, 10 + call ApplyAndAnimateHPRecovery + ret + +JigglypuffDoubleEdgeEffect: ; 2eda6 (b:6da6) + ld a, 20 + call DealRecoilDamageToSelf + ret + +PounceEffect: ; 2edac (b:6dac) + ld a, SUBSTATUS2_POUNCE + call ApplySubstatus2ToDefendingCard + ret + +LickitungSupersonicEffect: ; 2edb2 (b:6db2) + call Confusion50PercentEffect + call nc, SetNoEffectFromStatus + ret + +PidgeyWhirlwind_SelectEffect: ; 2edb9 (b:6db9) + ld a, DUELVARS_NUMBER_OF_POKEMON_IN_PLAY_AREA + call GetNonTurnDuelistVariable + cp 2 + jr nc, .switch + ; no Bench Pokemon + ld a, $ff + ldh [hTemp_ffa0], a + ret +.switch + call DuelistSelectForcedSwitch + ldh a, [hTempPlayAreaLocation_ff9d] + ldh [hTemp_ffa0], a + ret + +PidgeyWhirlwind_SwitchEffect: ; 2edcf (b:6dcf) + ldh a, [hTemp_ffa0] + call HandleSwitchDefendingPokemonEffect + ret + +; return carry if Defending card has no weakness +Conversion1_WeaknessCheck: ; 2edd5 (b:6dd5) + call SwapTurn + ld a, DUELVARS_ARENA_CARD + call GetTurnDuelistVariable + call LoadCardDataToBuffer2_FromDeckIndex + call SwapTurn + ld a, [wLoadedCard2Weakness] + or a + ret nz + ldtx hl, NoWeaknessText + scf + ret + +Conversion1_PlayerSelectEffect: ; 2eded (b:6ded) + ldtx hl, ChooseWeaknessYouWishToChangeText + xor a ; PLAY_AREA_ARENA + call HandleColorChangeScreen + ldh [hTemp_ffa0], a + ret + +Conversion1_AISelectEffect: ; 2edf7 (b:6df7) + call AISelectConversionColor + ret + +Conversion1_ChangeWeaknessEffect: ; 2edfb (b:6dfb) + call HandleNoDamageOrEffect + ret c ; is unaffected + +; apply changed weakness + ld a, DUELVARS_ARENA_CARD_CHANGED_WEAKNESS + call GetNonTurnDuelistVariable + ldh a, [hTemp_ffa0] + call TranslateColorToWR + ld [hl], a + ld l, DUELVARS_ARENA_CARD_LAST_TURN_CHANGE_WEAK + ld [hl], a + +; print text box + call SwapTurn + ldtx hl, ChangedTheWeaknessOfPokemonToColorText + call PrintArenaCardNameAndColorText + call SwapTurn + +; apply substatus + ld a, SUBSTATUS2_CONVERSION2 + call ApplySubstatus2ToDefendingCard + ret + +; returns carry if Active Pokemon has no Resistance. +Conversion2_ResistanceCheck: ; 2ee1f (b:6e1f) + ld a, DUELVARS_ARENA_CARD + call GetTurnDuelistVariable + call LoadCardDataToBuffer2_FromDeckIndex + ld a, [wLoadedCard2Resistance] + or a + ret nz + ldtx hl, NoResistanceText + scf + ret + +Conversion2_PlayerSelectEffect: ; 2ee31 (b:6e31) + ldtx hl, ChooseResistanceYouWishToChangeText + ld a, $80 + call HandleColorChangeScreen + ldh [hTemp_ffa0], a + ret + +Conversion2_AISelectEffect: ; 2ee3c (b:6e3c) +; AI will choose Defending Pokemon's color +; unless it is colorless. + call SwapTurn + ld a, DUELVARS_ARENA_CARD + call GetTurnDuelistVariable + call LoadCardDataToBuffer1_FromDeckIndex + call SwapTurn + ld a, [wLoadedCard1Type] + cp COLORLESS + jr z, .is_colorless + ldh [hTemp_ffa0], a + ret + +.is_colorless + call SwapTurn + call AISelectConversionColor + call SwapTurn + ret + +Conversion2_ChangeResistanceEffect: ; 2ee5e (b:6e5e) +; apply changed resistance + ld a, DUELVARS_ARENA_CARD_CHANGED_RESISTANCE + call GetTurnDuelistVariable + ldh a, [hTemp_ffa0] + call TranslateColorToWR + ld [hl], a + ldtx hl, ChangedTheResistanceOfPokemonToColorText +; fallthrough + +; prints text that requires card name and color, +; with the card name of the Turn Duelist's Arena Pokemon +; and color in [hTemp_ffa0]. +; input: +; hl = text to print +PrintArenaCardNameAndColorText: ; 2ee6c (b:6e6c) + push hl + ld a, DUELVARS_ARENA_CARD + call GetTurnDuelistVariable + call LoadCardDataToBuffer1_FromDeckIndex + ldh a, [hTemp_ffa0] + call LoadCardNameAndInputColor + pop hl + call DrawWideTextBox_PrintText + ret + +; handles AI logic for selecting a new color +; for weakness/resistance. +; - if within the context of Conversion1, looks +; in own Bench for a non-colorless card that can attack. +; - if within the context of Conversion2, looks +; in Player's Bench for a non-colorless card that can attack. +AISelectConversionColor: ; 2ee7f (b:6e7f) + ld a, DUELVARS_NUMBER_OF_POKEMON_IN_PLAY_AREA + call GetTurnDuelistVariable + ld d, a + ld e, PLAY_AREA_ARENA + jr .next_pkmn_atk + +; look for a non-colorless Bench Pokemon +; that has enough energy to use an attack. +.loop_atk + push de + call GetPlayAreaCardAttachedEnergies + ld a, e + add DUELVARS_ARENA_CARD + call GetTurnDuelistVariable + ld d, a + call LoadCardDataToBuffer1_FromDeckIndex + ld a, [wLoadedCard1Type] + cp COLORLESS + jr z, .skip_pkmn_atk ; skip colorless Pokemon + ld e, FIRST_ATTACK_OR_PKMN_POWER + bank1call _CheckIfEnoughEnergiesToAttack + jr nc, .found + ld e, SECOND_ATTACK + bank1call _CheckIfEnoughEnergiesToAttack + jr nc, .found +.skip_pkmn_atk + pop de +.next_pkmn_atk + inc e + dec d + jr nz, .loop_atk + +; none found in Bench. +; next, look for a non-colorless Bench Pokemon +; that has any Energy cards attached. + ld d, e ; number of Play Area Pokemon + ld e, PLAY_AREA_ARENA + jr .next_pkmn_energy + +.loop_energy + push de + call GetPlayAreaCardAttachedEnergies + ld a, [wTotalAttachedEnergies] + or a + jr z, .skip_pkmn_energy + ld a, e + add DUELVARS_ARENA_CARD + call GetTurnDuelistVariable + ld d, a + call LoadCardDataToBuffer1_FromDeckIndex + ld a, [wLoadedCard1Type] + cp COLORLESS + jr nz, .found +.skip_pkmn_energy + pop de +.next_pkmn_energy + inc e + dec d + jr nz, .loop_energy + +; otherwise, just select a random energy. + ld a, NUM_COLORED_TYPES + call Random + ldh [hTemp_ffa0], a + ret + +.found + pop de + ld a, [wLoadedCard1Type] + and TYPE_PKMN + ldh [hTemp_ffa0], a + ret + +ScrunchEffect: ; 2eee7 (b:6ee7) + ldtx de, IfHeadsNoDamageNextTurnText + call TossCoin_BankB + jp nc, SetWasUnsuccessful + ld a, ATK_ANIM_SCRUNCH + ld [wLoadedAttackAnimation], a + ld a, SUBSTATUS1_NO_DAMAGE_17 + call ApplySubstatus1ToDefendingCard + ret + +ChanseyDoubleEdgeEffect: ; 2eefb (b:6efb) + ld a, 80 + call DealRecoilDamageToSelf + ret + +SuperFang_AIEffect: ; 2ef01 (b:6f01) + call SuperFang_HalfHPEffect + jp SetDefiniteAIDamage + +SuperFang_HalfHPEffect: ; 2ef07 (b:6f07) + ld a, DUELVARS_ARENA_CARD_HP + call GetNonTurnDuelistVariable + srl a + bit 0, a + jr z, .rounded + ; round up + add 5 +.rounded + call SetDefiniteDamage + ret + +; return carry if no Pokemon in Bench +TrainerCardAsPokemon_BenchCheck: ; 2ef18 (b:6f18) + ldh a, [hTempPlayAreaLocation_ff9d] + ldh [hTemp_ffa0], a + ld a, DUELVARS_NUMBER_OF_POKEMON_IN_PLAY_AREA + call GetTurnDuelistVariable + ldtx hl, EffectNoPokemonOnTheBenchText + cp 2 + ret + +TrainerCardAsPokemon_PlayerSelectSwitch: ; 2ef27 (b:6f27) + ldh a, [hTemp_ffa0] + or a + ret nz ; no need to switch if it's not Arena card + + ldtx hl, SelectPokemonToPlaceInTheArenaText + call DrawWideTextBox_WaitForInput + bank1call HasAlivePokemonInBench + bank1call OpenPlayAreaScreenForSelection + ldh a, [hTempPlayAreaLocation_ff9d] + ldh [hTempPlayAreaLocation_ffa1], a + ret + +TrainerCardAsPokemon_DiscardEffect: ; 2ef3c (b:6f3c) + ldh a, [hTemp_ffa0] + ld e, a + call MovePlayAreaCardToDiscardPile + ldh a, [hTemp_ffa0] + or a + jr nz, .shift_cards + ldh a, [hTempPlayAreaLocation_ffa1] + ld e, a + call SwapArenaWithBenchPokemon +.shift_cards + call ShiftAllPokemonToFirstPlayAreaSlots + ret + +HealingWind_InitialEffect: ; 2ef51 (b:6f51) + scf + ret + +HealingWind_PlayAreaHealEffect: ; 2ef53 (b:6f53) +; play initial animation + ldh a, [hTempPlayAreaLocation_ff9d] + ld b, a + ld c, $00 + ldh a, [hWhoseTurn] + ld h, a + bank1call PlayAttackAnimation + bank1call WaitAttackAnimation + ld a, ATK_ANIM_HEALING_WIND_PLAY_AREA + ld [wLoadedAttackAnimation], a + + + ld a, DUELVARS_NUMBER_OF_POKEMON_IN_PLAY_AREA + call GetTurnDuelistVariable + ld d, a + ld e, PLAY_AREA_ARENA +.loop_play_area + push de + ld a, e + ldh [hTempPlayAreaLocation_ff9d], a + call GetCardDamageAndMaxHP + or a + jr z, .next_pkmn ; skip if no damage + +; if less than 20 damage, cap recovery at 10 damage + ld de, 20 + cp e + jr nc, .heal + ld e, a + +.heal +; add HP to this card + ldh a, [hTempPlayAreaLocation_ff9d] + add DUELVARS_ARENA_CARD_HP + call GetTurnDuelistVariable + add e + ld [hl], a + +; play heal animation + ldh a, [hTempPlayAreaLocation_ff9d] + ld b, a + ld c, $01 + ldh a, [hWhoseTurn] + ld h, a + bank1call PlayAttackAnimation + bank1call WaitAttackAnimation +.next_pkmn + pop de + inc e + dec d + jr nz, .loop_play_area + + ret + +Dragonite1Slam_AIEffect: ; 2ef9c (b:6f9c) + ld a, (30 * 2) / 2 + lb de, 0, 60 + jp SetExpectedAIDamage + +Dragonite1Slam_MultiplierEffect: ; 2efa4 (b:6fa4) + ld hl, 30 + call LoadTxRam3 + ldtx de, DamageCheckIfHeadsXDamageText + ld a, 2 + call TossCoinATimes_BankB + ld c, a + add a + add c + call ATimes10 + call SetDefiniteDamage + ret + +; possibly unreferenced +Func_2efbc: ; 2efbc (b:6fbc) + ld a, DUELVARS_NUMBER_OF_POKEMON_IN_PLAY_AREA + call GetTurnDuelistVariable + ld c, a + ld l, DUELVARS_ARENA_CARD_HP + ld de, wce76 +.asm_2efc7 + ld a, [hli] + ld [de], a + inc de + dec c + jr nz, .asm_2efc7 + ret + +; possibly unreferenced +Func_2efce: ; 2efce (b:6fce) + ld a, DUELVARS_NUMBER_OF_POKEMON_IN_PLAY_AREA + call GetTurnDuelistVariable + ld c, a + ld l, DUELVARS_ARENA_CARD_HP + ld de, wce76 +.asm_2efd9 + ld a, [de] + inc de + ld [hli], a + dec c + jr nz, .asm_2efd9 + ret + +CatPunchEffect: ; 2efe0 (b:6fe0) + call SwapTurn + call PickRandomPlayAreaCard + ld b, a + ld a, ATK_ANIM_CAT_PUNCH_PLAY_AREA + ld [wLoadedAttackAnimation], a + ld de, 20 + call DealDamageToPlayAreaPokemon + call SwapTurn + ret + +MorphEffect: ; 2eff6 (b:6ff6) + call ExchangeRNG + call .PickRandomBasicPokemonFromDeck + jr nc, .successful + ldtx hl, AttackUnsuccessfulText + call DrawWideTextBox_WaitForInput + ret + +.successful + ld a, DUELVARS_ARENA_CARD_STAGE + call GetTurnDuelistVariable + or a + jr z, .skip_discard_stage_below + +; if this is a stage 1 Pokemon (in case it's used +; by Clefable's Metronome attack) then first discard +; the lower stage card. + push hl + xor a + ldh [hTempPlayAreaLocation_ff9d], a + bank1call GetCardOneStageBelow + ld a, d + call PutCardInDiscardPile + pop hl + ld [hl], BASIC + +.skip_discard_stage_below +; overwrite card ID + ldh a, [hTempCardIndex_ff98] + call GetCardIDFromDeckIndex + ld a, DUELVARS_ARENA_CARD + call GetTurnDuelistVariable + ldh [hTempCardIndex_ff98], a + call _GetCardIDFromDeckIndex + ld [hl], e + +; overwrite HP to new card's maximum HP + ld e, PLAY_AREA_ARENA + call GetCardDamageAndMaxHP + ld a, DUELVARS_ARENA_CARD_HP + call GetTurnDuelistVariable + ld [hl], c + +; clear changed color and status + ld l, DUELVARS_ARENA_CARD_CHANGED_TYPE + ld [hl], $00 + call ClearAllStatusConditions + +; load both card's names for printing text + ld a, [wTempTurnDuelistCardID] + ld e, a + ld d, $00 + call LoadCardDataToBuffer2_FromCardID + ld hl, wLoadedCard2Name + ld de, wTxRam2 + ld a, [hli] + ld [de], a + inc de + ld a, [hl] + ld [de], a + inc de + ldh a, [hTempCardIndex_ff98] + call LoadCardDataToBuffer2_FromDeckIndex + ld hl, wLoadedCard2Name + ld a, [hli] + ld [de], a + inc de + ld a, [hl] + ld [de], a + ldtx hl, MetamorphsToText + call DrawWideTextBox_WaitForInput + + xor a + ld [wDuelDisplayedScreen], a + ret + +; picks a random Pokemon in the Deck to morph. +; needs to be a Basic Pokemon that doesn't have +; the same ID as the Arena card. +; returns carry if no Pokemon were found. +.PickRandomBasicPokemonFromDeck ; 2f06a (b:706a) + call CreateDeckCardList + ret c ; empty deck + ld hl, wDuelTempList + call ShuffleCards +.loop_deck + ld a, [hli] + ldh [hTempCardIndex_ff98], a + cp $ff + jr z, .set_carry + call LoadCardDataToBuffer2_FromDeckIndex + ld a, [wLoadedCard2Type] + cp TYPE_ENERGY + jr nc, .loop_deck ; skip non-Pokemon cards + ld a, [wLoadedCard2Stage] + or a + jr nz, .loop_deck ; skip non-Basic cards + ld a, [wLoadedCard2ID] + cp DUELVARS_ARENA_CARD + jr z, .loop_deck ; skip cards with same ID as Arena card + ldh a, [hTempCardIndex_ff98] + or a + ret +.set_carry + scf + ret + +; returns in a and [hTempCardIndex_ff98] the deck index +; of random Basic Pokemon card in deck. +; if none are found, return carry. +PickRandomBasicCardFromDeck: ; 2f098 (b:7098) + call CreateDeckCardList + ret c ; return if empty deck + ld hl, wDuelTempList + call ShuffleCards +.loop_deck + ld a, [hli] + ldh [hTempCardIndex_ff98], a + cp $ff + jr z, .set_carry + call LoadCardDataToBuffer2_FromDeckIndex + ld a, [wLoadedCard2Type] + cp TYPE_ENERGY + jr nc, .loop_deck ; skip if not Pokemon card + ld a, [wLoadedCard2Stage] + or a + jr nz, .loop_deck ; skip if not Basic stage + ldh a, [hTempCardIndex_ff98] + or a + ret +.set_carry + scf + ret + +SlicingWindEffect: ; 2f0bf (b:70bf) + call SwapTurn + call PickRandomPlayAreaCard + ld b, a + ld de, 30 + call DealDamageToPlayAreaPokemon_RegularAnim + call SwapTurn + ret + +Gale_LoadAnimation: ; 2f0d0 (b:70d0) + ld a, ATK_ANIM_GALE + ld [wLoadedAttackAnimation], a + ret + +Gale_SwitchEffect: ; 2f0d6 (b:70d6) +; if Defending card is unaffected by attack +; jump directly to switching this card only. + call HandleNoDamageOrEffect + jr c, .SwitchWithRandomBenchPokemon + +; handle switching Defending card + ld a, DUELVARS_ARENA_CARD_HP + call GetNonTurnDuelistVariable + or a + jr nz, .skip_destiny_bond + bank1call HandleDestinyBondSubstatus +.skip_destiny_bond + call SwapTurn + call .SwitchWithRandomBenchPokemon + jr c, .skip_clear_damage +; clear dealt damage because Pokemon was switched + xor a + ld hl, wDealtDamage + ld [hli], a + ld [hl], a +.skip_clear_damage + call SwapTurn +; fallthrough for attacking card switch + +.SwitchWithRandomBenchPokemon + ld a, DUELVARS_NUMBER_OF_POKEMON_IN_PLAY_AREA + call GetTurnDuelistVariable + cp 2 + ret c ; return if no Bench Pokemon + +; get random Bench location and swap + dec a + call Random + inc a + ld e, a + call SwapArenaWithBenchPokemon + + xor a + ld [wDuelDisplayedScreen], a + ret + +; return carry if Bench is full +FriendshipSong_BenchCheck: ; 2f10d (b:710d) + ld a, DUELVARS_NUMBER_OF_POKEMON_IN_PLAY_AREA + call GetTurnDuelistVariable + ldtx hl, NoSpaceOnTheBenchText + cp MAX_PLAY_AREA_POKEMON + ccf + ret + +FriendshipSong_AddToBench50PercentEffect: ; 2f119 (b:7119) + ldtx de, SuccessCheckIfHeadsAttackIsSuccessfulText + call TossCoin_BankB + jr c, .successful + +.none_came_text + ldtx hl, NoneCameText + call DrawWideTextBox_WaitForInput + ret + +.successful + call PickRandomBasicCardFromDeck + jr nc, .put_in_bench + ld a, ATK_ANIM_FRIENDSHIP_SONG + call Func_2c12e + call .none_came_text + call Func_2c0bd + ret + +.put_in_bench + call SearchCardInDeckAndAddToHand + call AddCardToHand + call PutHandPokemonCardInPlayArea + ld a, ATK_ANIM_FRIENDSHIP_SONG + call Func_2c12e + ldh a, [hTempCardIndex_ff98] + ldtx hl, CameToTheBenchText + bank1call DisplayCardDetailScreen + call Func_2c0bd + ret + +ExpandEffect: ; 2f153 (b:7153) + ld a, SUBSTATUS1_REDUCE_BY_10 + call ApplySubstatus1ToDefendingCard + ret + +; returns carry if either there are no damage counters +; or no Energy cards attached in the Play Area. +SuperPotion_DamageEnergyCheck: ; 2f159 (b:7159) + call CheckIfPlayAreaHasAnyDamage + ldtx hl, NoPokemonWithDamageCountersText + ret c ; no damage counters + call CheckIfThereAreAnyEnergyCardsAttached + ldtx hl, ThereIsNoEnergyCardAttachedText + ret + +SuperPotion_PlayerSelectEffect: ; 2f167 (b:7167) + ldtx hl, ChoosePokemonToRemoveDamageCounterFromText + call DrawWideTextBox_WaitForInput +.start + bank1call HasAlivePokemonInPlayArea +.read_input + bank1call OpenPlayAreaScreenForSelection + ret c ; exit if B is pressed + ld e, a + call GetCardDamageAndMaxHP + or a + jr z, .read_input ; Pokemon has no damage? + ldh a, [hCurMenuItem] + ld e, a + call GetPlayAreaCardAttachedEnergies + ld a, [wTotalAttachedEnergies] + or a + jr nz, .got_pkmn + ; no energy cards attached + ldtx hl, NoEnergyCardsText + call DrawWideTextBox_WaitForInput + jr .start + +.got_pkmn +; Pokemon has damage and Energy cards attached, +; prompt the Player for Energy selection to discard. + ldh a, [hCurMenuItem] + bank1call CreateArenaOrBenchEnergyCardList + ldh a, [hCurMenuItem] + bank1call DisplayEnergyDiscardScreen + bank1call HandleEnergyDiscardMenuInput + ret c ; exit if B was pressed + + ldh a, [hTempCardIndex_ff98] + ldh [hTemp_ffa0], a + ldh a, [hTempPlayAreaLocation_ff9d] + ldh [hTempPlayAreaLocation_ffa1], a + ld e, a + +; cap the healing damage if +; it would make it exceed max HP. + call GetCardDamageAndMaxHP + ld c, 40 + cp 40 + jr nc, .heal + ld c, a +.heal + ld a, c + ldh [hPlayAreaEffectTarget], a + or a + ret + +SuperPotion_HealEffect: ; 2f1b5 (b:71b5) + ldh a, [hTemp_ffa0] + call PutCardInDiscardPile + ldh a, [hTempPlayAreaLocation_ffa1] + ldh [hTempPlayAreaLocation_ff9d], a + ldh a, [hPlayAreaEffectTarget] + call HealPlayAreaCardHP + ret + +; checks if there is at least one Energy card +; attached to some card in the Turn Duelist's Play Area. +; return no carry if one is found, +; and returns carry set if none is found. +CheckIfThereAreAnyEnergyCardsAttached: ; 2f1c4 (b:71c4) + ld a, DUELVARS_CARD_LOCATIONS + call GetTurnDuelistVariable +.loop_deck + ld a, [hl] + bit CARD_LOCATION_PLAY_AREA_F, a + jr z, .next_card ; skip if not in Play Area + ld a, l + call LoadCardDataToBuffer2_FromDeckIndex + ld a, [wLoadedCard2Type] + cp TYPE_TRAINER + jr z, .next_card ; skip if it's a Trainer card + cp TYPE_ENERGY + jr nc, .found +.next_card + inc l + ld a, l + cp DECK_SIZE + jr c, .loop_deck + scf + ret +.found + or a + ret + +; handles Player selection for Pokemon in Play Area, +; then opens screen to choose one of the energy cards +; attached to that selected Pokemon. +; outputs the selection in: +; [hTemp_ffa0] = play area location +; [hTempPlayAreaLocation_ffa1] = index of energy card +HandlePokemonAndEnergySelectionScreen: ; 2f1e7 (b:71e7) + bank1call HasAlivePokemonInPlayArea + bank1call OpenPlayAreaScreenForSelection + ret c ; exit if B is pressed + ld e, a + call GetPlayAreaCardAttachedEnergies + ld a, [wTotalAttachedEnergies] + or a + jr nz, .has_energy + ldtx hl, NoEnergyCardsText + call DrawWideTextBox_WaitForInput + jr HandlePokemonAndEnergySelectionScreen ; loop back to start + +.has_energy + ldh a, [hCurMenuItem] + bank1call CreateArenaOrBenchEnergyCardList + ldh a, [hCurMenuItem] + bank1call DisplayEnergyDiscardScreen + bank1call HandleEnergyDiscardMenuInput + ldh a, [hTempPlayAreaLocation_ff9d] + ldh [hTemp_ffa0], a + ldh a, [hTempCardIndex_ff98] + ldh [hTempPlayAreaLocation_ffa1], a + ret + +ImakuniEffect: ; 2f216 (b:7216) + ld a, DUELVARS_ARENA_CARD + call GetTurnDuelistVariable + call LoadCardDataToBuffer1_FromDeckIndex + ld a, [wLoadedCard1ID] + +; cannot confuse Clefairy Doll and Mysterious Fossil + cp CLEFAIRY_DOLL + jr z, .failed + cp MYSTERIOUS_FOSSIL + jr z, .failed + +; cannot confuse Snorlax if its Pkmn Power is active + cp SNORLAX + jr nz, .success + xor a + call CheckCannotUseDueToStatus_OnlyToxicGasIfANon0 + jr c, .success + ; fallthrough if Thick Skinned is active + +.failed +; play confusion animation and print failure text + ld a, ATK_ANIM_IMAKUNI_CONFUSION + call Func_2fea9 + ldtx hl, ThereWasNoEffectText + call DrawWideTextBox_WaitForInput + ret + +.success +; play confusion animation and confuse card + ld a, ATK_ANIM_IMAKUNI_CONFUSION + call Func_2fea9 + ld a, DUELVARS_ARENA_CARD_STATUS + call GetTurnDuelistVariable + and PSN_DBLPSN + or CONFUSED + ld [hl], a + bank1call DrawDuelHUDs + ret + +; returns carry if opponent has no energy cards attached +EnergyRemoval_EnergyCheck: ; 2f252 (b:7252) + call SwapTurn + call CheckIfThereAreAnyEnergyCardsAttached + ldtx hl, NoEnergyAttachedToOpponentsActiveText + call SwapTurn + ret + +EnergyRemoval_PlayerSelection: ; 2f25f (b:725f) + ldtx hl, ChoosePokemonToRemoveEnergyFromText + call DrawWideTextBox_WaitForInput + call SwapTurn + call HandlePokemonAndEnergySelectionScreen + call SwapTurn + ret + +EnergyRemoval_AISelection: ; 2f26f (b:726f) + call AIPickEnergyCardToDiscardFromDefendingPokemon + ret + +EnergyRemoval_DiscardEffect: ; 2f273 (b:7273) + call SwapTurn + ldh a, [hTempPlayAreaLocation_ffa1] + call PutCardInDiscardPile + call SwapTurn + call IsPlayerTurn + ret c + +; show Player which Pokemon was affected + call SwapTurn + ldh a, [hTemp_ffa0] + call Func_2c10b + call SwapTurn + ret + +; return carry if no other card in hand to discard +; or if there are no Basic Energy cards in Discard Pile. +EnergyRetrieval_HandEnergyCheck: ; 2f28e (b:728e) + ld a, DUELVARS_NUMBER_OF_CARDS_IN_HAND + call GetTurnDuelistVariable + cp 2 + ldtx hl, NotEnoughCardsInHandText + ret c ; return if doesn't have another card to discard + call CreateEnergyCardListFromDiscardPile_OnlyBasic + ldtx hl, ThereAreNoBasicEnergyCardsInDiscardPileText + ret + +EnergyRetrieval_PlayerHandSelection: ; 2f2a0 (b:72a0) + ldtx hl, ChooseCardToDiscardFromHandText + call DrawWideTextBox_WaitForInput + call CreateHandCardList + ldh a, [hTempCardIndex_ff9f] + call RemoveCardFromDuelTempList + bank1call Func_5591 + bank1call DisplayCardList + ldh a, [hTempCardIndex_ff98] + ldh [hTempList], a + ret + +EnergyRetrieval_PlayerDiscardPileSelection: ; 2f2b9 (b:72b9) + ld a, 1 ; start at 1 due to card selected from hand + ldh [hCurSelectionItem], a + ldtx hl, Choose2BasicEnergyCardsFromDiscardPileText + call DrawWideTextBox_WaitForInput + call CreateEnergyCardListFromDiscardPile_OnlyBasic + +.select_card + bank1call InitAndDrawCardListScreenLayout + ldtx hl, PleaseSelectCardText + ldtx de, PlayerDiscardPileText + bank1call SetCardListHeaderText + bank1call DisplayCardList + jr nc, .selected + ; B was pressed + ld a, 2 + 1 ; includes the card selected from hand + call AskWhetherToQuitSelectingCards + jr c, .select_card ; player selected No + jr .done + +.selected + call GetNextPositionInTempList_TrainerEffects + ldh a, [hTempCardIndex_ff98] + ld [hl], a + call RemoveCardFromDuelTempList + jr c, .done + ldh a, [hCurSelectionItem] + cp 2 + 1 ; includes the card selected from hand + jr c, .select_card + +.done + call GetNextPositionInTempList_TrainerEffects + ld [hl], $ff ; terminating byte + or a + ret + +EnergyRetrieval_DiscardAndAddToHandEffect: ; 2f2f8 (b:72f8) + ld hl, hTempList + ld a, [hli] + call RemoveCardFromHand + call PutCardInDiscardPile + ld de, wDuelTempList +.loop + ld a, [hli] + ld [de], a + inc de + cp $ff + jr z, .done + call MoveDiscardPileCardToHand + call AddCardToHand + jr .loop +.done + call IsPlayerTurn + ret c + bank1call Func_4b38 + ret + +; return carry if no cards left in Deck. +EnergySearch_DeckCheck: ; 2f31c (b:731c) + ld a, DUELVARS_NUMBER_OF_CARDS_NOT_IN_DECK + call GetTurnDuelistVariable + cp DECK_SIZE + ccf + ldtx hl, NoCardsLeftInTheDeckText + ret + +EnergySearch_PlayerSelection: ; 2f328 (b:7328) + ld a, $ff + ldh [hTemp_ffa0], a + call CreateDeckCardList + ldtx hl, Choose1BasicEnergyCardFromDeckText + lb de, SEARCHEFFECT_BASIC_ENERGY, 0 + ldtx bc, BasicEnergyText + call LookForCardsInDeck + ret c ; skip showing deck + + bank1call Func_5591 + ldtx hl, ChooseBasicEnergyCardText + ldtx de, DuelistDeckText + bank1call SetCardListHeaderText +.read_input + bank1call DisplayCardList + jr c, .try_exit ; B pressed? + ldh a, [hTempCardIndex_ff98] + ldh [hTemp_ffa0], a + call CheckIfCardIsBasicEnergy + jr c, .play_sfx + or a + ret +.play_sfx + call Func_3794 + jr .read_input + +.try_exit +; check if Player can exit without selecting anything + ld hl, wDuelTempList +.next_card + ld a, [hli] + cp $ff + jr z, .exit + call CheckIfCardIsBasicEnergy + jr c, .next_card + jr .read_input ; no, has to select Energy card +.exit + ld a, $ff + ldh [hTemp_ffa0], a + or a + ret + +EnergySearch_AddToHandEffect: ; 2f372 (b:7372) + ldh a, [hTemp_ffa0] + cp $ff + jr z, .done +; add to hand + call SearchCardInDeckAndAddToHand + call AddCardToHand + call IsPlayerTurn + jr c, .done ; done if Player played card +; display card in screen + ldh a, [hTemp_ffa0] + ldtx hl, WasPlacedInTheHandText + bank1call DisplayCardDetailScreen +.done + call Func_2c0bd + ret + +; check if card index in a is a Basic Energy card. +; returns carry in case it's not. +CheckIfCardIsBasicEnergy: ; 2f38f (b:738f) + call LoadCardDataToBuffer2_FromDeckIndex + ld a, [wLoadedCard2Type] + cp TYPE_ENERGY + jr c, .not_basic_energy + cp TYPE_ENERGY_DOUBLE_COLORLESS + jr nc, .not_basic_energy +; is basic energy + or a + ret +.not_basic_energy + scf + ret + +ProfessorOakEffect: ; 2f3a1 (b:73a1) +; discard hand + call CreateHandCardList + call SortCardsInDuelTempListByID + ld hl, wDuelTempList +.discard_loop + ld a, [hli] + cp $ff + jr z, .draw_cards + call RemoveCardFromHand + call PutCardInDiscardPile + jr .discard_loop + +.draw_cards + ld a, 7 + bank1call DisplayDrawNCardsScreen + ld c, 7 +.draw_loop + call DrawCardFromDeck + jr c, .done + call AddCardToHand + dec c + jr nz, .draw_loop +.done + ret + +Potion_DamageCheck: ; 2f3ca (b:73ca) + call CheckIfPlayAreaHasAnyDamage + ldtx hl, NoPokemonWithDamageCountersText + ret + +Potion_PlayerSelection: ; 2f3d1 (b:73d1) + bank1call HasAlivePokemonInPlayArea +.read_input + bank1call OpenPlayAreaScreenForSelection + ret c ; exit is B was pressed + ldh a, [hTempPlayAreaLocation_ff9d] + ldh [hTemp_ffa0], a + ld e, a + call GetCardDamageAndMaxHP + or a + jr z, .read_input ; no damage, loop back to start +; cap damage + ld c, 20 + cp 20 + jr nc, .skip_cap + ld c, a +.skip_cap + ld a, c + ldh [hTempPlayAreaLocation_ffa1], a + or a + ret + +Potion_HealEffect: ; 2f3ef (b:73ef) + ldh a, [hTemp_ffa0] + ldh [hTempPlayAreaLocation_ff9d], a + ldh a, [hTempPlayAreaLocation_ffa1] + call HealPlayAreaCardHP + ret + +GamblerEffect: ; 2f3f9 (b:73f9) + ldtx de, CardCheckIfHeads8CardsIfTails1CardText + call TossCoin_BankB + ldh [hTemp_ffa0], a +; discard Gambler card from hand + ldh a, [hTempCardIndex_ff9f] + call RemoveCardFromHand + call PutCardInDiscardPile + +; shuffle cards into deck + call CreateHandCardList + call SortCardsInDuelTempListByID + ld hl, wDuelTempList +.loop_return_deck + ld a, [hli] + cp $ff + jr z, .check_coin_toss + call RemoveCardFromHand + call ReturnCardToDeck + jr .loop_return_deck + +.check_coin_toss + call Func_2c0bd + ld c, 8 + ldh a, [hTemp_ffa0] + or a + jr nz, .draw_cards ; coin toss was heads? + ; if tails, number of cards to draw is 1 + ld c, 1 + +; correct number of cards to draw is in c +.draw_cards + ld a, c + bank1call DisplayDrawNCardsScreen +.draw_loop + call DrawCardFromDeck + jr c, .done + call AddCardToHand + dec c + jr nz, .draw_loop +.done + ret + +; return carry if not enough cards in hand to discard +; or if there are no cards in the Discard Pile +ItemFinder_HandDiscardPileCheck: ; 2f43b (b:743b) + ld a, DUELVARS_NUMBER_OF_CARDS_IN_HAND + call GetTurnDuelistVariable + ldtx hl, NotEnoughCardsInHandText + cp 3 + ret c + call CreateTrainerCardListFromDiscardPile + ret + +ItemFinder_PlayerSelection: ; 2f44a (b:744a) + call HandlePlayerSelection2HandCardsToDiscard + ret c ; was operation cancelled? + +; cards were selected to discard from hand. +; now to choose a Trainer card from Discard Pile. + call CreateTrainerCardListFromDiscardPile + bank1call Func_5591 + ldtx hl, ChooseCardToPlaceInHandText + ldtx de, PlayerDiscardPileText + bank1call SetCardListHeaderText + bank1call DisplayCardList + ldh [hTempList + 2], a ; placed after the 2 cards selected to discard + ret + +ItemFinder_DiscardAddToHandEffect: ; 2f463 (b:7463) +; discard cards from hand + ld hl, hTempList + ld a, [hli] + call RemoveCardFromHand + call PutCardInDiscardPile + ld a, [hli] + call RemoveCardFromHand + call PutCardInDiscardPile + +; place card from Discard Pile to hand + ld a, [hl] + call MoveDiscardPileCardToHand + call AddCardToHand + call IsPlayerTurn + ret c +; display card in screen + ldh a, [hTempList + 2] + ldtx hl, WasPlacedInTheHandText + bank1call DisplayCardDetailScreen + ret + +Defender_PlayerSelection: ; 2f488 (b:7488) + ldtx hl, ChoosePokemonToAttachDefenderToText + call DrawWideTextBox_WaitForInput + bank1call HasAlivePokemonInPlayArea + bank1call OpenPlayAreaScreenForSelection + ldh a, [hTempPlayAreaLocation_ff9d] + ldh [hTemp_ffa0], a + ret + +Defender_AttachDefenderEffect: ; 2f499 (b:7499) +; attach Trainer card to Play Area Pokemon + ldh a, [hTemp_ffa0] + ld e, a + ldh a, [hTempCardIndex_ff9f] + call PutHandCardInPlayArea + +; increase number of Defender cards of this location by 1 + ldh a, [hTemp_ffa0] + add DUELVARS_ARENA_CARD_ATTACHED_DEFENDER + call GetTurnDuelistVariable + inc [hl] + call IsPlayerTurn + ret c + + ldh a, [hTemp_ffa0] + call Func_2c10b + ret + +; return carry if Bench is full. +MysteriousFossil_BenchCheck: ; 2f4b3 (b:74b3) + ld a, DUELVARS_NUMBER_OF_POKEMON_IN_PLAY_AREA + call GetTurnDuelistVariable + cp MAX_PLAY_AREA_POKEMON + ccf + ldtx hl, NoSpaceOnTheBenchText + ret + +MysteriousFossil_PlaceInPlayAreaEffect: ; 2f4bf (b:74bf) + ldh a, [hTempCardIndex_ff9f] + call PutHandPokemonCardInPlayArea + ret + +; return carry if Arena card has no status to heal. +FullHeal_StatusCheck: ; 2f4c5 (b:74c5) + ld a, DUELVARS_ARENA_CARD_STATUS + call GetTurnDuelistVariable + or a + ret nz + ldtx hl, NotAffectedByPoisonSleepParalysisOrConfusionText + scf + ret + +FullHeal_ClearStatusEffect: ; 2f4d1 (b:74d1) + ld a, ATK_ANIM_FULL_HEAL + call Func_2fea9 + ld a, DUELVARS_ARENA_CARD_STATUS + call GetTurnDuelistVariable + ld [hl], NO_STATUS + bank1call DrawDuelHUDs + ret + +ImposterProfessorOakEffect: ; 2f4e1 (b:74e1) + call SwapTurn + call CreateHandCardList + call SortCardsInDuelTempListByID + +; first return all cards in hand to the deck. + ld hl, wDuelTempList +.loop_return_deck + ld a, [hli] + cp $ff + jr z, .done_return + call RemoveCardFromHand + call ReturnCardToDeck + jr .loop_return_deck + +; then draw 7 cards from the deck. +.done_return + call Func_2c0bd + ld a, 7 + bank1call DisplayDrawNCardsScreen + ld c, 7 +.loop_draw + call DrawCardFromDeck + jr c, .done + call AddCardToHand + dec c + jr nz, .loop_draw +.done + call SwapTurn + ret + +; return carry if not enough cards in hand to discard +; or if there are no cards left in the deck. +ComputerSearch_HandDeckCheck: ; 2f513 (b:7513) + ld a, DUELVARS_NUMBER_OF_CARDS_IN_HAND + call GetTurnDuelistVariable + ldtx hl, NotEnoughCardsInHandText + cp 3 + ret c + ld a, DUELVARS_NUMBER_OF_CARDS_NOT_IN_DECK + call GetTurnDuelistVariable + ldtx hl, NoCardsLeftInTheDeckText + cp DECK_SIZE + ccf + ret + +ComputerSearch_PlayerDiscardHandSelection: ; 2f52a (b:752a) + call HandlePlayerSelection2HandCardsToDiscard + ret + +ComputerSearch_PlayerDeckSelection: ; 2f52e (b:752e) + call CreateDeckCardList + bank1call Func_5591 + ldtx hl, ChooseCardToPlaceInHandText + ldtx de, DuelistDeckText + bank1call SetCardListHeaderText +.loop_input + bank1call DisplayCardList + jr c, .loop_input ; can't exit with B button + ldh [hTempList + 2], a + ret + +ComputerSearch_DiscardAddToHandEffect: ; 2f545 (b:7545) +; discard cards from hand + ld hl, hTempList + ld a, [hli] + call RemoveCardFromHand + call PutCardInDiscardPile + ld a, [hli] + call RemoveCardFromHand + call PutCardInDiscardPile + +; add card from deck to hand + ld a, [hl] + call SearchCardInDeckAndAddToHand + call AddCardToHand + call Func_2c0bd + ret + +; return carry if Bench is full. +ClefairyDoll_BenchCheck: ; 2f561 (b:7561) + ld a, DUELVARS_NUMBER_OF_POKEMON_IN_PLAY_AREA + call GetTurnDuelistVariable + ldtx hl, NoSpaceOnTheBenchText + cp MAX_PLAY_AREA_POKEMON + ccf + ret + +ClefairyDoll_PlaceInPlayAreaEffect: ; 2f56d (b:756d) + ldh a, [hTempCardIndex_ff9f] + call PutHandPokemonCardInPlayArea + ret + +; return carry if no Pokemon in the Bench. +MrFuji_BenchCheck: ; 2f573 (b:7573) + ld a, DUELVARS_NUMBER_OF_POKEMON_IN_PLAY_AREA + call GetTurnDuelistVariable + ldtx hl, EffectNoPokemonOnTheBenchText + cp 2 + ret + +MrFuji_PlayerSelection: ; 2f57e (b:757e) + ldtx hl, ChoosePokemonToReturnToTheDeckText + call DrawWideTextBox_WaitForInput + bank1call HasAlivePokemonInBench + bank1call OpenPlayAreaScreenForSelection + ldh a, [hTempPlayAreaLocation_ff9d] + ldh [hTemp_ffa0], a + ret + +MrFuji_ReturnToDeckEffect: ; 2f58f (b:758f) +; get Play Area location's card index + ldh a, [hTemp_ffa0] + add DUELVARS_ARENA_CARD + call GetTurnDuelistVariable + ldh [hTempCardIndex_ff98], a + +; find all cards that are in the same location +; (previous evolutions and energy cards attached) +; and return them all to the deck. + ldh a, [hTemp_ffa0] + or CARD_LOCATION_PLAY_AREA + ld e, a + ld a, DUELVARS_CARD_LOCATIONS + call GetTurnDuelistVariable +.loop_cards + push de + push hl + ld a, [hl] + cp e + jr nz, .next_card + ld a, l + call ReturnCardToDeck +.next_card + pop hl + pop de + inc l + ld a, l + cp DECK_SIZE + jr c, .loop_cards + +; clear Play Area location of card + ldh a, [hTemp_ffa0] + ld e, a + call EmptyPlayAreaSlot + ld l, DUELVARS_NUMBER_OF_POKEMON_IN_PLAY_AREA + dec [hl] + call ShiftAllPokemonToFirstPlayAreaSlots + +; if Trainer card wasn't played by the Player, +; print the selected Pokemon's name and show card on screen. + call IsPlayerTurn + jr c, .done + ldh a, [hTempCardIndex_ff98] + call LoadCardDataToBuffer1_FromDeckIndex + ld hl, wLoadedCard1Name + ld a, [hli] + ld h, [hl] + ld l, a + call LoadTxRam2 + bank1call DrawLargePictureOfCard + ldtx hl, PokemonAndAllAttachedCardsWereReturnedToDeckText + call DrawWideTextBox_WaitForInput +.done + call Func_2c0bd + ret + +PlusPowerEffect: ; 2f5e0 (b:75e0) +; attach Trainer card to Arena Pokemon + ld e, PLAY_AREA_ARENA + ldh a, [hTempCardIndex_ff9f] + call PutHandCardInPlayArea + +; increase number of Defender cards of this location by 1 + ld a, DUELVARS_ARENA_CARD_ATTACHED_PLUSPOWER + call GetTurnDuelistVariable + inc [hl] + ret + +; return carry if no Pokemon in the Bench. +Switch_BenchCheck: ; 2f5ee (b:75ee) + ld a, DUELVARS_NUMBER_OF_POKEMON_IN_PLAY_AREA + call GetTurnDuelistVariable + ldtx hl, EffectNoPokemonOnTheBenchText + cp 2 + ret + +Switch_PlayerSelection: ; 2f5f9 (b:75f9) + ldtx hl, SelectPkmnOnBenchToSwitchWithActiveText + call DrawWideTextBox_WaitForInput + bank1call HasAlivePokemonInBench + bank1call OpenPlayAreaScreenForSelection + ldh a, [hTempPlayAreaLocation_ff9d] + ldh [hTemp_ffa0], a + ret + +Switch_SwitchEffect: ; 2f60a (b:760a) + ldh a, [hTemp_ffa0] + ld e, a + call SwapArenaWithBenchPokemon + ret + +PokemonCenter_DamageCheck: ; 2f611 (b:7611) + call CheckIfPlayAreaHasAnyDamage + ldtx hl, NoPokemonWithDamageCountersText + ret + +PokemonCenter_HealDiscardEnergyEffect: ; 2f618 (b:7618) + ld a, DUELVARS_NUMBER_OF_POKEMON_IN_PLAY_AREA + call GetTurnDuelistVariable + ld d, a + ld e, PLAY_AREA_ARENA + +; go through every Pokemon in the Play Area +; and heal all damage & discard their Energy cards. +.loop_play_area +; check its damage + ld a, e + ldh [hTempPlayAreaLocation_ff9d], a + call GetCardDamageAndMaxHP + or a + jr z, .next_pkmn ; if no damage, skip Pokemon + +; heal all its damage + push de + ld e, a + ld d, $00 + call HealPlayAreaCardHP + +; loop all cards in deck and for all the Energy cards +; that are attached to this Play Area location Pokemon, +; place them in the Discard Pile. + ldh a, [hTempPlayAreaLocation_ff9d] + or CARD_LOCATION_PLAY_AREA + ld e, a + ld a, $00 + call GetTurnDuelistVariable +.loop_deck + ld a, [hl] + cp e + jr nz, .next_card_deck ; not attached to card, skip + ld a, l + call LoadCardDataToBuffer2_FromDeckIndex + ld a, [wLoadedCard2Type] + and TYPE_ENERGY + jr z, .next_card_deck ; not Energy, skip + ld a, l + call PutCardInDiscardPile +.next_card_deck + inc l + ld a, l + cp DECK_SIZE + jr c, .loop_deck + + pop de +.next_pkmn + inc e + dec d + jr nz, .loop_play_area + ret + +; return carry if non-Turn Duelist has full Bench +; or if they have no Basic Pokemon cards in Discard Pile. +PokemonFlute_BenchCheck: ; 2f659 (b:7659) + ld a, DUELVARS_NUMBER_OF_POKEMON_IN_PLAY_AREA + call GetNonTurnDuelistVariable + ldtx hl, NoSpaceOnTheBenchText + cp MAX_PLAY_AREA_POKEMON + ccf + ret c ; not enough space in Bench + ; check Discard Pile + call SwapTurn + call CreateBasicPokemonCardListFromDiscardPile + ldtx hl, ThereAreNoPokemonInDiscardPileText + call SwapTurn + ret + +PokemonFlute_PlayerSelection: ; 2f672 (b:7672) +; create Discard Pile list + call SwapTurn + call CreateBasicPokemonCardListFromDiscardPile + +; display selection screen and store Player's selection + bank1call Func_5591 + ldtx hl, ChoosePokemonToPlaceInPlayText + ldtx de, PlayerDiscardPileText + bank1call SetCardListHeaderText + bank1call DisplayCardList + call SwapTurn + ldh a, [hTempCardIndex_ff98] + ldh [hTemp_ffa0], a + ret + +PokemonFlute_PlaceInPlayAreaText: ; 2f68f (b:768f) +; place selected card in non-Turn Duelist's Bench + call SwapTurn + ldh a, [hTemp_ffa0] + call MoveDiscardPileCardToHand + call AddCardToHand + call PutHandPokemonCardInPlayArea + call SwapTurn + +; unless it was the Player who played the card, +; display the Pokemon card on screen. + call IsPlayerTurn + ret c + call SwapTurn + ldh a, [hTemp_ffa0] + ldtx hl, CardWasChosenText + bank1call DisplayCardDetailScreen + call SwapTurn + ret + +PokemonBreeder_HandPlayAreaCheck: ; 2f6b3 (b:76b3) + call CreatePlayableStage2PokemonCardListFromHand + jr c, .cannot_evolve + bank1call IsPrehistoricPowerActive + ret +.cannot_evolve + ldtx hl, ConditionsForEvolvingToStage2NotFulfilledText + scf + ret + +PokemonBreeder_PlayerSelection: ; 2f6c1 (b:76c1) +; create hand list of playable Stage2 cards + call CreatePlayableStage2PokemonCardListFromHand + bank1call Func_5591 + +; handle Player selection of Stage2 card + ldtx hl, PleaseSelectCardText + ldtx de, DuelistHandText + bank1call SetCardListHeaderText + bank1call DisplayCardList + ret c ; exit if B was pressed + + ldh a, [hTempCardIndex_ff98] + ldh [hTemp_ffa0], a + ldtx hl, ChooseBasicPokemonToEvolveText + call DrawWideTextBox_WaitForInput + +; handle Player selection of Basic card to evolve + bank1call HasAlivePokemonInPlayArea +.read_input + bank1call OpenPlayAreaScreenForSelection + ret c ; exit if B was pressed + ldh a, [hTempPlayAreaLocation_ff9d] + ldh [hTempPlayAreaLocation_ffa1], a + ld e, a + ldh a, [hTemp_ffa0] + ld d, a + call CheckIfCanEvolveInto_BasicToStage2 + jr c, .read_input ; loop back if cannot evolve this card + or a + ret + +PokemonBreeder_EvolveEffect: ; 2f6f4 (b:76f4) + ldh a, [hTempCardIndex_ff9f] + push af + ld hl, hTemp_ffa0 + ld a, [hli] + ldh [hTempCardIndex_ff98], a + ld a, [hl] ; hTempPlayAreaLocation_ffa1 + ldh [hTempPlayAreaLocation_ff9d], a + +; load the Basic Pokemon card name to RAM + add DUELVARS_ARENA_CARD + call GetTurnDuelistVariable + call LoadCardDataToBuffer1_FromDeckIndex + ld hl, wLoadedCard1Name + ld a, [hli] + ld h, [hl] + ld l, a + call LoadTxRam2 + +; evolve card and overwrite its stage as STAGE2_WITHOUT_STAGE1 + ldh a, [hTempCardIndex_ff98] + call EvolvePokemonCard + ld [hl], STAGE2_WITHOUT_STAGE1 + +; load Stage2 Pokemon card name to RAM + ldh a, [hTempCardIndex_ff98] + call LoadCardDataToBuffer1_FromDeckIndex + ld a, 18 + call CopyCardNameAndLevel + xor a + ld [hl], a ; $0 character + ld hl, wTxRam2_b + ld [hli], a + ld [hl], a + +; display Pokemon picture and play sfx, +; print the corresponding card names. + bank1call DrawLargePictureOfCard + ld a, $5e + call PlaySFX + ldtx hl, PokemonEvolvedIntoPokemonText + call DrawWideTextBox_WaitForInput + bank1call Func_161e + pop af + ldh [hTempCardIndex_ff9f], a + ret + +; creates list in wDuelTempList of all Stage2 Pokemon cards +; in the hand that can evolve a Basic Pokemon card in Play Area +; through use of Pokemon Breeder. +; returns carry if that list is empty. +CreatePlayableStage2PokemonCardListFromHand: ; 2f73e (b:773e) + call CreateHandCardList + ret c ; return if no hand cards + +; check if hand Stage2 Pokemon cards can be made +; to evolve a Basic Pokemon in the Play Area and, if so, +; add it to the wDuelTempList. + ld hl, wDuelTempList + ld e, l + ld d, h +.loop_hand + ld a, [hl] + cp $ff + jr z, .done + call .CheckIfCanEvolveAnyPlayAreaBasicCard + jr c, .next_hand_card + ld a, [hl] + ld [de], a + inc de +.next_hand_card + inc hl + jr .loop_hand + +.done + ld a, $ff ; terminating byte + ld [de], a + ld a, [wDuelTempList] + cp $ff + scf + ret z ; return carry if empty + ; not empty + or a + ret + +; return carry if Stage2 card in a cannot evolve any +; of the Basic Pokemon in Play Area through Pokemon Breeder. +.CheckIfCanEvolveAnyPlayAreaBasicCard + push de + ld d, a + call LoadCardDataToBuffer2_FromDeckIndex + ld a, [wLoadedCard2Type] + cp TYPE_ENERGY + jr nc, .set_carry ; skip if not Pokemon card + ld a, [wLoadedCard2Stage] + cp STAGE2 + jr nz, .set_carry ; skip if not Stage2 + +; check if can evolve any Play Area cards + push hl + push bc + ld a, DUELVARS_NUMBER_OF_POKEMON_IN_PLAY_AREA + call GetTurnDuelistVariable + ld c, a + ld e, PLAY_AREA_ARENA +.loop_play_area + push bc + push de + call CheckIfCanEvolveInto_BasicToStage2 + pop de + pop bc + jr nc, .done_play_area + inc e + dec c + jr nz, .loop_play_area +; set carry + scf +.done_play_area + pop bc + pop hl + pop de + ret +.set_carry + pop de + scf + ret + +; return carry if no cards in the Bench. +ScoopUp_BenchCheck: ; 2f795 (b:7795) + ld a, DUELVARS_NUMBER_OF_POKEMON_IN_PLAY_AREA + call GetTurnDuelistVariable + ldtx hl, EffectNoPokemonOnTheBenchText + cp 2 + ret + +ScoopUp_PlayerSelection: ; 2f7a0 (b:77a0) +; print text box + ldtx hl, ChoosePokemonToScoopUpText + call DrawWideTextBox_WaitForInput + +; handle Player selection + bank1call HasAlivePokemonInPlayArea + bank1call OpenPlayAreaScreenForSelection + ret c ; exit if B was pressed + + ldh [hTemp_ffa0], a + or a + ret nz ; if it wasn't the Active Pokemon, we are done + +; handle switching to a Pokemon in Bench and store location selected. + call EmptyScreen + ldtx hl, SelectPokemonToPlaceInTheArenaText + call DrawWideTextBox_WaitForInput + bank1call HasAlivePokemonInBench + bank1call OpenPlayAreaScreenForSelection + ldh [hTempPlayAreaLocation_ffa1], a + ret + +ScoopUp_ReturnToHandEffect: ; 2f7c3 (b:77c3) +; store chosen card location to Scoop Up + ldh a, [hTemp_ffa0] + or CARD_LOCATION_PLAY_AREA + ld e, a + +; find Basic Pokemon card that is in the selected Play Area location +; and add it to the hand, discarding all cards attached. + ld a, DUELVARS_CARD_LOCATIONS + call GetTurnDuelistVariable +.loop + ld a, [hl] + cp e + jr nz, .next_card ; skip if not in selected location + ld a, l + call LoadCardDataToBuffer2_FromDeckIndex + ld a, [wLoadedCard2Type] + cp TYPE_ENERGY + jr nc, .next_card ; skip if not Pokemon card + ld a, [wLoadedCard2Stage] + or a + jr nz, .next_card ; skip if not Basic stage +; found + ld a, l + ldh [hTempCardIndex_ff98], a + call AddCardToHand + ; optimization: break loop here, since + ; no two Basic Pokemon cards may occupy + ; the same Play Area location. +.next_card + inc l + ld a, l + cp DECK_SIZE + jr c, .loop + +; since the card has been moved to hand, +; MovePlayAreaCardToDiscardPile will take care +; of discarding every higher stage cards and other cards attached. + ldh a, [hTemp_ffa0] + ld e, a + call MovePlayAreaCardToDiscardPile + +; if the Pokemon was in the Arena, clear status + ldh a, [hTemp_ffa0] + or a + jr nz, .skip_clear_status + call ClearAllStatusConditions +.skip_clear_status + +; if card was not played by Player, show detail screen +; and print corresponding text. + call IsPlayerTurn + jr c, .shift_or_switch + ldtx hl, PokemonWasReturnedFromArenaToHandText + ldh a, [hTemp_ffa0] + or a + jr z, .display_detail_screen + ldtx hl, PokemonWasReturnedFromBenchToHandText +.display_detail_screen + ldh a, [hTempCardIndex_ff98] + bank1call DisplayCardDetailScreen + +.shift_or_switch +; if card was in Bench, simply shift Pokemon slots... + ldh a, [hTemp_ffa0] + or a + jr z, .switch + call ShiftAllPokemonToFirstPlayAreaSlots + ret + +.switch +; ...if Pokemon was in Arena, then switch it with the selected Bench card. + ldh a, [hTempPlayAreaLocation_ffa1] + ld d, a + ld e, PLAY_AREA_ARENA + call SwapPlayAreaPokemon + call ShiftAllPokemonToFirstPlayAreaSlots + ret + +; return carry if no other cards in hand, +; or if there are no Pokemon cards in hand. +PokemonTrader_HandDeckCheck: ; 2f826 (b:7826) + ld a, DUELVARS_NUMBER_OF_CARDS_IN_HAND + call GetTurnDuelistVariable + ldtx hl, ThereAreNoCardsInHandThatYouCanChangeText + cp 2 + ret c ; return if no other cards in hand + call CreatePokemonCardListFromHand + ldtx hl, ThereAreNoCardsInHandThatYouCanChangeText + ret + +PokemonTrader_PlayerHandSelection: ; 2f838 (b:7838) +; print text box + ldtx hl, ChooseCardFromYourHandToSwitchText + call DrawWideTextBox_WaitForInput + +; create list with all Pokemon cards in hand + call CreatePokemonCardListFromHand + bank1call Func_5591 + +; handle Player selection + ldtx hl, ChooseCardToSwitchText + ldtx de, DuelistHandText + bank1call SetCardListHeaderText + bank1call DisplayCardList + ldh [hTemp_ffa0], a + ret + +PokemonTrader_PlayerDeckSelection: ; 2f853 (b:7853) +; temporarily place chosen hand card in deck +; so it can be potentially chosen to be traded. + ldh a, [hTemp_ffa0] + call RemoveCardFromHand + call ReturnCardToDeck + +; display deck card list screen + ldtx hl, ChooseBasicOrEvolutionPokemonCardFromDeckText + call DrawWideTextBox_WaitForInput + call CreateDeckCardList + bank1call Func_5591 + ldtx hl, ChoosePokemonCardText + ldtx de, DuelistDeckText + bank1call SetCardListHeaderText + +; handle Player selection +.read_input + bank1call DisplayCardList + jr c, .read_input ; pressing B loops back to selection + call LoadCardDataToBuffer2_FromDeckIndex + ld a, [wLoadedCard2Type] + cp TYPE_ENERGY + jr nc, .read_input ; can't select non-Pokemon cards + +; a valid card was selected, store its card index and +; place the selected hand card back to the hand. + ldh a, [hTempCardIndex_ff98] + ldh [hTempPlayAreaLocation_ffa1], a + ldh a, [hTemp_ffa0] + call SearchCardInDeckAndAddToHand + call AddCardToHand + or a + ret + +PokemonTrader_TradeCardsEffect: ; 2f88d (b:788d) +; place hand card in deck + ldh a, [hTemp_ffa0] + call RemoveCardFromHand + call ReturnCardToDeck + +; place deck card in hand + ldh a, [hTempPlayAreaLocation_ffa1] + call SearchCardInDeckAndAddToHand + call AddCardToHand + +; display cards if the Pokemon Trader wasn't played by Player + call IsPlayerTurn + jr c, .done + ldh a, [hTemp_ffa0] + ldtx hl, PokemonWasReturnedToDeckText + bank1call DisplayCardDetailScreen + ldh a, [hTempPlayAreaLocation_ffa1] + ldtx hl, WasPlacedInTheHandText + bank1call DisplayCardDetailScreen +.done + call Func_2c0bd + ret + +; makes list in wDuelTempList with all Pokemon cards +; that are in Turn Duelist's hand. +; if list turns out empty, return carry. +CreatePokemonCardListFromHand: ; 2f8b6 (b:78b6) + ld a, DUELVARS_NUMBER_OF_CARDS_IN_HAND + call GetTurnDuelistVariable + ld c, a + ld l, DUELVARS_HAND + ld de, wDuelTempList +.loop + ld a, [hl] + call LoadCardDataToBuffer2_FromDeckIndex + ld a, [wLoadedCard2Type] + cp TYPE_ENERGY + jr nc, .next_hand_card + ld a, [hl] + ld [de], a + inc de +.next_hand_card + inc l + dec c + jr nz, .loop + ld a, $ff ; terminating byte + ld [de], a + ld a, [wDuelTempList] + cp $ff + jr z, .set_carry + or a + ret +.set_carry + scf + ret + +; return carry if no cards in deck +Pokedex_DeckCheck: ; 2f8e1 (b:78e1) + ld a, DUELVARS_NUMBER_OF_CARDS_NOT_IN_DECK + call GetTurnDuelistVariable + ldtx hl, NoCardsLeftInTheDeckText + cp DECK_SIZE + ccf + ret + +Pokedex_PlayerSelection: ; 2f8ed (b:78ed) +; print text box + ldtx hl, RearrangeThe5CardsAtTopOfDeckText + call DrawWideTextBox_WaitForInput + +; cap the number of cards to reorder up to +; number of cards left in the deck (maximum of 5) + ld a, DUELVARS_NUMBER_OF_CARDS_NOT_IN_DECK + call GetTurnDuelistVariable + ld b, a + ld a, DECK_SIZE + sub [hl] + ld c, 5 + cp c + jr nc, .no_cap + ld c, a +.no_cap + +; fill wDuelTempList with cards that are going to be sorted + ld a, c + inc a + ld [wNumberOfCardsToOrder], a + ld a, b + add DUELVARS_DECK_CARDS + ld l, a + ld de, wDuelTempList +.loop_cards_to_order + ld a, [hli] + ld [de], a + inc de + dec c + jr nz, .loop_cards_to_order + ld a, $ff ; terminating byte + ld [de], a + +.clear_list +; wDuelTempList + 10 will be filled with numbers from 1 +; to 5 (or whatever the maximum order card is). +; so that the first item in that list corresponds to the first card +; the second item corresponds to the second card, etc. +; and the number in the list corresponds to the ordering number. + call CountCardsInDuelTempList + ld b, a + ld a, 1 +; fill order list with zeroes + ldh [hCurSelectionItem], a + ld hl, wDuelTempList + 10 + xor a +.loop_init + ld [hli], a + dec b + jr nz, .loop_init + ld [hl], $ff ; terminating byte + +; display card list to order + bank1call InitAndDrawCardListScreenLayout + ldtx hl, ChooseTheOrderOfTheCardsText + ldtx de, DuelistDeckText + bank1call SetCardListHeaderText + bank1call Func_5735 + +.read_input + bank1call DisplayCardList + jr c, .undo ; if B is pressed, undo last order selection + +; a card was selected, check if it's already been selected + ldh a, [hCurMenuItem] + ld e, a + ld d, $00 + ld hl, wDuelTempList + 10 + add hl, de + ld a, [hl] + or a + jr nz, .read_input ; already has an ordering number + +; hasn't been ordered yet, apply to it current ordering number +; and increase it by 1. + ldh a, [hCurSelectionItem] + ld [hl], a + inc a + ldh [hCurSelectionItem], a + +; refresh screen + push af + bank1call Func_5744 + pop af + +; check if we're done ordering + ldh a, [hCurSelectionItem] + ld hl, wNumberOfCardsToOrder + cp [hl] + jr c, .read_input ; if still more cards to select, loop back up + +; we're done selecting cards + call EraseCursor + ldtx hl, IsThisOKText + call YesOrNoMenuWithText_LeftAligned + jr c, .clear_list ; "No" was selected, start over + ; selection was confirmed + +; now wDuelTempList + 10 will be overwritten with the +; card indices in order of selection. + ld hl, wDuelTempList + 10 + ld de, wDuelTempList + ld c, 0 +.loop_write_indices + ld a, [hli] + cp $ff + jr z, .done_write_indices + push hl + push bc + ld c, a + ld b, $00 + ld hl, hTempCardIndex_ff9f + add hl, bc + ld a, [de] + ld [hl], a + pop bc + pop hl + inc de + inc c + jr .loop_write_indices + +.done_write_indices + ld b, $00 + ld hl, hTempList + add hl, bc + ld [hl], $ff ; terminating byte + or a + ret + +.undo +; undo last selection and get previous order number + ld hl, hCurSelectionItem + ld a, [hl] + cp 1 + jr z, .read_input ; already at first input, nothing to undo + dec a + ld [hl], a + ld c, a + ld hl, wDuelTempList + 10 +.asm_2f99e + ld a, [hli] + cp c + jr nz, .asm_2f99e + dec hl + ld [hl], $00 ; overwrite order number with 0 + bank1call Func_5744 + jr .read_input + +Pokedex_OrderDeckCardsEffect: ; 2f9aa (b:79aa) +; place cards in order to the hand. + ld hl, hTempList + ld c, 0 +.loop_place_hand + ld a, [hli] + cp $ff + jr z, .place_top_deck + call SearchCardInDeckAndAddToHand + inc c + jr .loop_place_hand + +.place_top_deck +; go to last card in list and iterate in decreasing order +; placing each card in top of deck. + dec hl + dec hl +.loop_place_deck + ld a, [hld] + call ReturnCardToDeck + dec c + jr nz, .loop_place_deck + ret + +BillEffect: ; 2f9c4 (b:79c4) + ld a, 2 + bank1call DisplayDrawNCardsScreen + ld c, 2 +.loop_draw + call DrawCardFromDeck + jr c, .done + ldh [hTempCardIndex_ff98], a + call AddCardToHand + call IsPlayerTurn + jr nc, .skip_display_screen + push bc + bank1call DisplayPlayerDrawCardScreen + pop bc +.skip_display_screen + dec c + jr nz, .loop_draw +.done + ret + +LassEffect: ; 2f9e3 (b:79e3) +; first discard Lass card that was used + ldh a, [hTempCardIndex_ff9f] + call RemoveCardFromHand + call PutCardInDiscardPile + + ldtx hl, PleaseCheckTheOpponentsHandText + call DrawWideTextBox_WaitForInput + + call .DisplayLinkOrCPUHand + ; do for non-Turn Duelist + call SwapTurn + call .ShuffleDuelistHandTrainerCardsInDeck + call SwapTurn + ; do for Turn Duelist +; fallthrough + +.ShuffleDuelistHandTrainerCardsInDeck + call CreateHandCardList + call SortCardsInDuelTempListByID + xor a + ldh [hCurSelectionItem], a + ld hl, wDuelTempList + +; go through all cards in hand +; and any Trainer card is returned to deck. +.loop_hand + ld a, [hli] + ldh [hTempCardIndex_ff98], a + cp $ff + jr z, .done + call GetCardIDFromDeckIndex + call GetCardType + cp TYPE_TRAINER + jr nz, .loop_hand + ldh a, [hTempCardIndex_ff98] + call RemoveCardFromHand + call ReturnCardToDeck + push hl + ld hl, hCurSelectionItem + inc [hl] + pop hl + jr .loop_hand +.done +; show card list + ldh a, [hCurSelectionItem] + or a + call nz, Func_2c0bd ; only show list if there were any Trainer cards + ret + +.DisplayLinkOrCPUHand ; 2fa31 (b:7a31) + ld a, [wDuelType] + or a + jr z, .cpu_opp + +; link duel + ldh a, [hWhoseTurn] + push af + ld a, OPPONENT_TURN + ldh [hWhoseTurn], a + call .DisplayOppHand + pop af + ldh [hWhoseTurn], a + ret + +.cpu_opp + call SwapTurn + call .DisplayOppHand + call SwapTurn + ret + +.DisplayOppHand ; 2fa4f (b:7a4f) + call CreateHandCardList + jr c, .no_cards + bank1call InitAndDrawCardListScreenLayout + ldtx hl, ChooseTheCardYouWishToExamineText + ldtx de, DuelistHandText + bank1call SetCardListHeaderText + ld a, A_BUTTON | START + ld [wNoItemSelectionMenuKeys], a + bank1call DisplayCardList + ret +.no_cards + ldtx hl, DuelistHasNoCardsInHandText + call DrawWideTextBox_WaitForInput + ret + +; return carry if not enough cards in hand for effect +Maintenance_HandCheck: ; 2fa70 (b:7a70) + ld a, DUELVARS_NUMBER_OF_CARDS_IN_HAND + call GetTurnDuelistVariable + ldtx hl, NotEnoughCardsInHandText + cp 3 + ret + +Maintenance_PlayerSelection: ; 2fa7b (b:7a7b) + ldtx hl, Choose2HandCardsFromHandToReturnToDeckText + ldtx de, ChooseTheCardToPutBackText + call HandlePlayerSelection2HandCards + ret + +Maintenance_ReturnToDeckAndDrawEffect: ; 2fa85 (b:7a85) +; return both selected cards to the deck + ldh a, [hTempList] + call RemoveCardFromHand + call ReturnCardToDeck + ldh a, [hTempList + 1] + call RemoveCardFromHand + call ReturnCardToDeck + call Func_2c0bd + +; draw one card + ld a, 1 + bank1call DisplayDrawNCardsScreen + call DrawCardFromDeck + ldh [hTempCardIndex_ff98], a + call AddCardToHand + call IsPlayerTurn + ret nc + ; show card on screen if played by Player + bank1call DisplayPlayerDrawCardScreen + ret + +; return carry if no cards in deck +PokeBall_DeckCheck: ; 2faad (b:7aad) + ld a, DUELVARS_NUMBER_OF_CARDS_NOT_IN_DECK + call GetTurnDuelistVariable + ldtx hl, NoCardsLeftInTheDeckText + cp DECK_SIZE + ccf + ret + +PokeBall_PlayerSelection: ; 2fab9 (b:7ab9) + ld de, DUELVARS_NUMBER_OF_POKEMON_IN_PLAY_AREA + call Func_2c08a + ldh [hTempList], a ; store coin result + ret nc + +; create list of all Pokemon cards in deck to search for + call CreateDeckCardList + ldtx hl, ChooseBasicOrEvolutionPokemonCardFromDeckText + ldtx bc, EvolutionCardText + lb de, SEARCHEFFECT_POKEMON, 0 + call LookForCardsInDeck + jr c, .no_pkmn ; return if Player chose not to check deck + +; handle input + bank1call Func_5591 + ldtx hl, ChoosePokemonCardText + ldtx de, DuelistDeckText + bank1call SetCardListHeaderText +.read_input + bank1call DisplayCardList + jr c, .try_exit ; B was pressed, check if Player can cancel operation + ldh a, [hTempCardIndex_ff98] + call LoadCardDataToBuffer2_FromDeckIndex + ld a, [wLoadedCard2Type] + cp TYPE_ENERGY + jr nc, .play_sfx ; can't select non-Pokemon card + ldh a, [hTempCardIndex_ff98] + ldh [hTempList + 1], a + or a + ret + +.no_pkmn + ld a, $ff + ldh [hTempList + 1], a + or a + ret + +.play_sfx + call Func_3794 + jr .read_input + +.try_exit +; Player can only exit screen if there are no cards to choose + ld hl, wDuelTempList +.loop + ld a, [hli] + cp $ff + jr z, .no_pkmn + call LoadCardDataToBuffer2_FromDeckIndex + ld a, [wLoadedCard2Type] + cp TYPE_ENERGY + jr nc, .loop + jr .read_input + +PokeBall_AddToHandEffect: ; 2fb15 (b:7b15) + ldh a, [hTempList] + or a + ret z ; return if coin toss was tails + + ldh a, [hTempList + 1] + cp $ff + jr z, .done ; skip if no Pokemon was chosen + +; add Pokemon card to hand and show in screen if +; it wasn't the Player who played the Trainer card. + call SearchCardInDeckAndAddToHand + call AddCardToHand + call IsPlayerTurn + jr c, .done + ldh a, [hTempList + 1] + ldtx hl, WasPlacedInTheHandText + bank1call DisplayCardDetailScreen +.done + call Func_2c0bd + ret + +; return carry if no cards in the Discard Pile +Recycle_DiscardPileCheck: ; 2fb36 (b:7b36) + ld a, DUELVARS_NUMBER_OF_CARDS_IN_DISCARD_PILE + call GetTurnDuelistVariable + ldtx hl, ThereAreNoCardsInTheDiscardPileText + cp 1 + ret + +Recycle_PlayerSelection: ; 2fb41 (b:7b41) + ld de, DUELVARS_NUMBER_OF_POKEMON_IN_PLAY_AREA + call Func_2c08a + jr nc, .tails + + call CreateDiscardPileCardList + bank1call Func_5591 + ldtx hl, PleaseSelectCardText + ldtx de, PlayerDiscardPileText + bank1call SetCardListHeaderText +.read_input + bank1call DisplayCardList + jr c, .read_input ; can't cancel with B button + +; Discard Pile card was chosen + ldh a, [hTempCardIndex_ff98] + ldh [hTempList], a + ret + +.tails + ld a, $ff + ldh [hTempList], a + or a + ret + +Recycle_AddToHandEffect: ; 2fb68 (b:7b68) + ldh a, [hTempList] + cp $ff + ret z ; return if no card was selected + +; add card to hand and show in screen if +; it wasn't the Player who played the Trainer card. + call MoveDiscardPileCardToHand + call ReturnCardToDeck + call IsPlayerTurn + ret c + ldh a, [hTempList] + ldtx hl, CardWasChosenText + bank1call DisplayCardDetailScreen + ret + +; return carry if Bench is full or +; if no Basic Pokemon cards in Discard Pile. +Revive_BenchCheck: ; 2fb80 (b:7b80) + ld a, DUELVARS_NUMBER_OF_POKEMON_IN_PLAY_AREA + call GetTurnDuelistVariable + ldtx hl, NoSpaceOnTheBenchText + cp MAX_PLAY_AREA_POKEMON + ccf + ret c + call CreateBasicPokemonCardListFromDiscardPile + ldtx hl, ThereAreNoPokemonInDiscardPileText + ret + +Revive_PlayerSelection: ; 2fb93 (b:7b93) +; create Basic Pokemon card list from Discard Pile + ldtx hl, ChooseBasicPokemonToPlaceOnBenchText + call DrawWideTextBox_WaitForInput + call CreateBasicPokemonCardListFromDiscardPile + bank1call Func_5591 + +; display screen to select Pokemon + ldtx hl, PleaseSelectCardText + ldtx de, PlayerDiscardPileText + bank1call SetCardListHeaderText + bank1call DisplayCardList + +; store selection + ldh a, [hTempCardIndex_ff98] + ldh [hTemp_ffa0], a + ret + +Revive_PlaceInPlayAreaEffect: ; 2fbb0 (b:7bb0) +; place selected Pokemon in the Bench + ldh a, [hTemp_ffa0] + call MoveDiscardPileCardToHand + call AddCardToHand + call PutHandPokemonCardInPlayArea + +; set HP to half, rounded up + add DUELVARS_ARENA_CARD_HP + call GetTurnDuelistVariable + srl a + bit 0, a + jr z, .rounded + add 5 ; round up HP to nearest 10 +.rounded + ld [hl], a + call IsPlayerTurn + ret c ; done if Player played Revive + +; display card + ldh a, [hTemp_ffa0] + ldtx hl, PlacedOnTheBenchText + bank1call DisplayCardDetailScreen + ret + +; makes list in wDuelTempList with all Basic Pokemon cards +; that are in Turn Duelist's Discard Pile. +; if list turns out empty, return carry. +CreateBasicPokemonCardListFromDiscardPile: ; 2fbd6 (b:7bd6) +; gets hl to point at end of Discard Pile cards +; and iterates the cards in reverse order. + ld a, DUELVARS_NUMBER_OF_CARDS_IN_DISCARD_PILE + call GetTurnDuelistVariable + ld b, a + add DUELVARS_DECK_CARDS + ld l, a + ld de, wDuelTempList + inc b + jr .next_discard_pile_card + +.check_card + ld a, [hl] + call LoadCardDataToBuffer2_FromDeckIndex + ld a, [wLoadedCard2Type] + cp TYPE_ENERGY + jr nc, .next_discard_pile_card ; if not Pokemon card, skip + ld a, [wLoadedCard2Stage] + or a + jr nz, .next_discard_pile_card ; if not Basic stage, skip + +; write this card's index to wDuelTempList + ld a, [hl] + ld [de], a + inc de +.next_discard_pile_card + dec l + dec b + jr nz, .check_card + +; done with the loop. + ld a, $ff ; terminating byte + ld [de], a + ld a, [wDuelTempList] + cp $ff + jr z, .set_carry + or a + ret +.set_carry + scf + ret + +; return carry if Turn Duelist has no Evolution cards in Play Area +DevolutionSpray_PlayAreaEvolutionCheck: ; 2fc0b (b:7c0b) + ld a, DUELVARS_NUMBER_OF_POKEMON_IN_PLAY_AREA + call GetTurnDuelistVariable + ld c, a + ld l, DUELVARS_ARENA_CARD +.loop + ld a, [hli] + call LoadCardDataToBuffer2_FromDeckIndex + ld a, [wLoadedCard2Stage] + or a + ret nz ; found an Evolution card + dec c + jr nz, .loop + + ldtx hl, ThereAreNoStage1PokemonText + scf + ret + +DevolutionSpray_PlayerSelection: ; 2fc24 (b:7c24) +; display textbox + ldtx hl, ChooseEvolutionCardAndPressAButtonToDevolveText + call DrawWideTextBox_WaitForInput + +; have Player select an Evolution card in Play Area + ld a, 1 + ldh [hCurSelectionItem], a + bank1call HasAlivePokemonInPlayArea +.read_input + bank1call OpenPlayAreaScreenForSelection + ret c ; exit if B was pressed + bank1call GetCardOneStageBelow + jr c, .read_input ; can't select Basic cards + +; get pre-evolution card data + ldh a, [hTempPlayAreaLocation_ff9d] + add DUELVARS_ARENA_CARD_HP + call GetTurnDuelistVariable + push hl + push af + ldh a, [hTempPlayAreaLocation_ff9d] + add DUELVARS_ARENA_CARD_STAGE + ld l, a + ld a, [hl] + push hl + push af + ldh a, [hTempPlayAreaLocation_ff9d] + add DUELVARS_ARENA_CARD + ld l, a + ld a, [hl] + push hl + push af + jr .update_data + +.repeat_devolution +; show Play Area screen with static cursor +; so that the Player either presses A to do one more devolution +; or presses B to finish selection. + bank1call Func_6194 + jr c, .done_selection ; if B pressed, end selection instead + ; do one more devolution + bank1call GetCardOneStageBelow + +.update_data +; overwrite the card data to new devolved stats + ld a, d + call UpdateDevolvedCardHPAndStage + call GetNextPositionInTempList_TrainerEffects + ld [hl], e + ld a, d + call LoadCardDataToBuffer2_FromDeckIndex + ld a, [wLoadedCard2Stage] + or a + jr nz, .repeat_devolution ; can do one more devolution + +.done_selection + call GetNextPositionInTempList_TrainerEffects + ld [hl], $ff ; terminating byte + +; store this Play Area location in first item of temp list + ldh a, [hTempPlayAreaLocation_ff9d] + ldh [hTempList], a + +; update Play Area location display of this Pokemon + call EmptyScreen + ldh a, [hTempPlayAreaLocation_ff9d] + ld hl, wHUDEnergyAndHPBarsX + ld [hli], a + ld [hl], $00 + bank1call PrintPlayAreaCardInformationAndLocation + call EnableLCD + pop bc + pop hl + +; rewrite all duelvars from before the selection was done +; this is so that if "No" is selected in confirmation menu, +; then the Pokemon isn't devolved and remains unchanged. + ld [hl], b + ldtx hl, IsThisOKText + call YesOrNoMenuWithText + pop bc + pop hl + + ld [hl], b + pop bc + pop hl + + ld [hl], b + ret + +DevolutionSpray_DevolutionEffect: ; 2fc99 (b:7c99) +; first byte in list is Play Area location chosen + ld hl, hTempList + ld a, [hli] + ldh [hTempPlayAreaLocation_ff9d], a + add DUELVARS_ARENA_CARD + call GetTurnDuelistVariable + push hl + push af + +; loop through devolutions selected + ld hl, hTempList + 1 +.loop_devolutions + ld a, [hl] + cp $ff + jr z, .check_ko ; list is over + ; devolve card to its stage below + push hl + bank1call GetCardOneStageBelow + ld a, d + call UpdateDevolvedCardHPAndStage + call ResetDevolvedCardStatus + pop hl + ld a, [hli] + call PutCardInDiscardPile + jr .loop_devolutions + +.check_ko + pop af + ld e, a + pop hl + ld d, [hl] + call PrintDevolvedCardNameAndLevelText + ldh a, [hTempList] + call PrintPlayAreaCardKnockedOutIfNoHP + bank1call Func_6e49 + ret + +; returns carry if neither duelist has any energy cards attached +SuperEnergyRemoval_EnergyCheck: ; 2fcd0 (b:7cd0) + call CheckIfThereAreAnyEnergyCardsAttached + ldtx hl, NoEnergyCardsAttachedToPokemonInYourPlayAreaText + ret c + call SwapTurn + call CheckIfThereAreAnyEnergyCardsAttached + ldtx hl, NoEnergyCardsAttachedToPokemonInOppPlayAreaText + call SwapTurn + ret + +SuperEnergyRemoval_PlayerSelection: ; 2fce4 (b:7ce4) +; handle selection of Energy to discard in own Play Area + ldtx hl, ChoosePokemonInYourAreaThenPokemonInYourOppText + call DrawWideTextBox_WaitForInput + call HandlePokemonAndEnergySelectionScreen + ret c ; return if operation was cancelled + + ldtx hl, ChoosePokemonToRemoveEnergyFromText + call DrawWideTextBox_WaitForInput + + call SwapTurn + ld a, 3 + ldh [hCurSelectionItem], a +.select_opp_pkmn + bank1call HasAlivePokemonInPlayArea + bank1call OpenPlayAreaScreenForSelection + jr nc, .opp_pkmn_selected + ; B was pressed + call SwapTurn + ret ; return if operation was cancelled +.opp_pkmn_selected + ld e, a + call GetPlayAreaCardAttachedEnergies + ld a, [wTotalAttachedEnergies] + or a + jr nz, .has_energy ; has any energy cards attached? + ; no energy, loop back + ldtx hl, NoEnergyCardsText + call DrawWideTextBox_WaitForInput + jr .select_opp_pkmn + +.has_energy +; store this Pokemon's Play Area location + ldh a, [hTempPlayAreaLocation_ff9d] + ldh [hPlayAreaEffectTarget], a +; store which energy card to discard from it + bank1call CreateArenaOrBenchEnergyCardList + ldh a, [hTempPlayAreaLocation_ff9d] + bank1call DisplayEnergyDiscardScreen + ld a, 2 + ld [wEnergyDiscardMenuDenominator], a + +.loop_discard_energy_selection + bank1call HandleEnergyDiscardMenuInput + jr nc, .energy_selected + ; B pressed + ld a, 5 + call AskWhetherToQuitSelectingCards + jr nc, .done ; finish operation + ; player selected to continue selection + ld a, [wEnergyDiscardMenuNumerator] + push af + ldh a, [hTempPlayAreaLocation_ff9d] + bank1call DisplayEnergyDiscardScreen + ld a, 2 + ld [wEnergyDiscardMenuDenominator], a + pop af + ld [wEnergyDiscardMenuNumerator], a + jr .loop_discard_energy_selection + +.energy_selected +; store energy cards to discard from opponent + call GetNextPositionInTempList_TrainerEffects + ldh a, [hTempCardIndex_ff98] + ld [hl], a + call RemoveCardFromDuelTempList + ld hl, wEnergyDiscardMenuNumerator + inc [hl] + ldh a, [hCurSelectionItem] + cp 5 + jr nc, .done ; no more energy cards to select + ld a, [wDuelTempList] + cp $ff + jr z, .done ; no more energy cards to select + bank1call DisplayEnergyDiscardMenu + jr .loop_discard_energy_selection + +.done + call GetNextPositionInTempList_TrainerEffects + ld [hl], $ff + call SwapTurn + or a + ret + +SuperEnergyRemoval_DiscardEffect: ; 2fd73 (b:7d73) + ld hl, hTempList + 1 + +; discard energy card of own Play Area + ld a, [hli] + call PutCardInDiscardPile + +; iterate and discard opponent's energy cards + inc hl + call SwapTurn +.loop + ld a, [hli] + cp $ff + jr z, .done_discard + call PutCardInDiscardPile + jr .loop + +.done_discard +; if it's Player's turn, return... + call SwapTurn + call IsPlayerTurn + ret c +; ...otherwise show Play Area of affected Pokemon +; in opponent's Play Area + ldh a, [hTemp_ffa0] + call Func_2c10b +; in player's Play Area + xor a + ld [wDuelDisplayedScreen], a + call SwapTurn + ldh a, [hPlayAreaEffectTarget] + call Func_2c10b + call SwapTurn + ret + +; return carry if not enough cards in hand to +; discard for Super Energy Retrieval effect +; or if the Discard Pile has no basic Energy cards +SuperEnergyRetrieval_HandEnergyCheck: ; 2fda4 (b:7da4) + ld a, DUELVARS_NUMBER_OF_CARDS_IN_HAND + call GetTurnDuelistVariable + ldtx hl, NotEnoughCardsInHandText + cp 3 + ret c + call CreateEnergyCardListFromDiscardPile_OnlyBasic + ldtx hl, ThereAreNoBasicEnergyCardsInDiscardPileText + ret + +SuperEnergyRetrieval_PlayerHandSelection: ; 2fdb6 (b:7db6) + call HandlePlayerSelection2HandCardsToDiscard + ret + +SuperEnergyRetrieval_PlayerDiscardPileSelection: ; 2fdba (b:7dba) + ldtx hl, ChooseUpTo4FromDiscardPileText + call DrawWideTextBox_WaitForInput + call CreateEnergyCardListFromDiscardPile_OnlyBasic + +.loop_discard_pile_selection + bank1call InitAndDrawCardListScreenLayout + ldtx hl, PleaseSelectCardText + ldtx de, PlayerDiscardPileText + bank1call SetCardListHeaderText + bank1call DisplayCardList + jr nc, .store_selected_card + ; B pressed + ld a, 6 + call AskWhetherToQuitSelectingCards + jr c, .loop_discard_pile_selection ; player selected to continue + jr .done + +.store_selected_card + ldh a, [hTempCardIndex_ff98] + call GetTurnDuelistVariable + call GetNextPositionInTempList_TrainerEffects + ldh a, [hTempCardIndex_ff98] + ld [hl], a ; store selected energy card + call RemoveCardFromDuelTempList + jr c, .done + ; this shouldn't happen + ldh a, [hCurSelectionItem] + cp 6 + jr c, .loop_discard_pile_selection + +.done +; insert terminating byte + call GetNextPositionInTempList_TrainerEffects + ld [hl], $ff + or a + ret + +SuperEnergyRetrieval_DiscardAndAddToHandEffect: ; 2fdfa (b:7dfa) +; discard 2 cards selected from the hand + ld hl, hTemp_ffa0 + ld a, [hli] + call RemoveCardFromHand + call PutCardInDiscardPile + ld a, [hli] + call RemoveCardFromHand + call PutCardInDiscardPile + +; put selected cards in hand + ld de, wDuelTempList +.loop + ld a, [hli] + ld [de], a + inc de + cp $ff + jr z, .done + call MoveDiscardPileCardToHand + call AddCardToHand + jr .loop + +.done +; if Player played the card, exit + call IsPlayerTurn + ret c +; if not, show card list selected by Opponent + bank1call Func_4b38 + ret + +; outputs in hl the next position +; in hTempList to place a new card, +; and increments hCurSelectionItem. +; identical to GetNextPositionInTempList. +GetNextPositionInTempList_TrainerEffects: ; 2fe25 (b:7e25) + push de + ld hl, hCurSelectionItem + ld a, [hl] + inc [hl] + ld e, a + ld d, $00 + ld hl, hTempList + add hl, de + pop de + ret + +; handles screen for Player to select 2 cards from the hand to discard. +; first prints text informing Player to choose cards to discard +; then runs HandlePlayerSelection2HandCards routine. +HandlePlayerSelection2HandCardsToDiscard: ; 2fe34 (b:7e34) + ldtx hl, Choose2CardsFromHandToDiscardText + ldtx de, ChooseTheCardToDiscardText +; fallthrough + +; handles screen for Player to select 2 cards from the hand +; to activate some Trainer card effect. +; assumes Trainer card index being used is in [hTempCardIndex_ff9f]. +; stores selection of cards in hTempList. +; returns carry if Player cancels operation. +; input: +; hl = text to print in text box; +; de = text to print in screen header. +HandlePlayerSelection2HandCards: ; 2fe3a (b:7e3a) + push de + call DrawWideTextBox_WaitForInput + +; remove the Trainer card being used from list +; of cards to select from hand. + call CreateHandCardList + ldh a, [hTempCardIndex_ff9f] + call RemoveCardFromDuelTempList + + xor a + ldh [hCurSelectionItem], a + pop hl +.loop + push hl + bank1call Func_5591 + pop hl + bank1call SetCardListInfoBoxText + push hl + bank1call DisplayCardList + pop hl + jr c, .set_carry ; was B pressed? + push hl + call GetNextPositionInTempList_TrainerEffects + ldh a, [hTempCardIndex_ff98] + ld [hl], a + call RemoveCardFromDuelTempList + pop hl + ldh a, [hCurSelectionItem] + cp 2 + jr c, .loop ; is selection over? + or a + ret +.set_carry + scf + ret + +; return carry if non-turn duelist has no benched Pokemon +GustOfWind_BenchCheck: ; 2fe6e (b:7e6e) + ld a, DUELVARS_NUMBER_OF_POKEMON_IN_PLAY_AREA + call GetNonTurnDuelistVariable + ldtx hl, EffectNoPokemonOnTheBenchText + cp 2 + ret + +GustOfWind_PlayerSelection: ; 2fe79 (b:7e79) + ldtx hl, ChooseAPokemonToSwitchWithActivePokemonText + call DrawWideTextBox_WaitForInput + call SwapTurn + bank1call HasAlivePokemonInBench + bank1call OpenPlayAreaScreenForSelection + ldh a, [hTempPlayAreaLocation_ff9d] + ldh [hTemp_ffa0], a + call SwapTurn + ret + +GustOfWind_SwitchEffect: ; 2fe90 (b:7e90) +; play whirlwind animation + ld a, ATK_ANIM_GUST_OF_WIND + call Func_2fea9 + +; switch Arena card + call SwapTurn + ldh a, [hTemp_ffa0] + ld e, a + call SwapArenaWithBenchPokemon + call SwapTurn + call ClearDamageReductionSubstatus2 + xor a + ld [wDuelDisplayedScreen], a + ret + +; input: +; a = attack animation to play +Func_2fea9: ; 2fea9 (b:7ea9) + ld [wLoadedAttackAnimation], a + bank1call Func_7415 + ld bc, $0 + ldh a, [hWhoseTurn] + ld h, a + bank1call PlayAttackAnimation + bank1call WaitAttackAnimation + ret + +; heals amount of damage in register e for card in +; Play Area location in [hTempPlayAreaLocation_ff9d]. +; plays healing animation and prints text with card's name. +; input: +; e = amount of HP to heal +; [hTempPlayAreaLocation_ff9d] = Play Area location of card to heal +HealPlayAreaCardHP: ; 2febc (b:7ebc) + ld e, a + ld d, $00 + +; play heal animation + push de + bank1call Func_7415 + ld a, ATK_ANIM_HEALING_WIND_PLAY_AREA + ld [wLoadedAttackAnimation], a + ldh a, [hTempPlayAreaLocation_ff9d] + ld b, a + ld c, $01 + ldh a, [hWhoseTurn] + ld h, a + bank1call PlayAttackAnimation + bank1call WaitAttackAnimation + pop hl + +; print Pokemon card name and damage healed + push hl + call LoadTxRam3 + ld hl, $0000 + call LoadTxRam2 + ldh a, [hTempPlayAreaLocation_ff9d] + add DUELVARS_ARENA_CARD + call GetTurnDuelistVariable + call LoadCardDataToBuffer1_FromDeckIndex + ld a, 18 + call CopyCardNameAndLevel + ld [hl], $00 ; terminating character on end of the name + ldtx hl, PokemonHealedDamageText + call DrawWideTextBox_WaitForInput + pop de + +; heal the target Pokemon + ldh a, [hTempPlayAreaLocation_ff9d] + add DUELVARS_ARENA_CARD_HP + call GetTurnDuelistVariable + add e + ld [hl], a + ret diff --git a/src/engine/effect_functions.asm b/src/engine/effect_functions.asm deleted file mode 100644 index ce3a517..0000000 --- a/src/engine/effect_functions.asm +++ /dev/null @@ -1,11194 +0,0 @@ -Poison50PercentEffect: ; 2c000 (b:4000) - ldtx de, PoisonCheckText - call TossCoin_BankB - ret nc - -PoisonEffect: ; 2c007 (b:4007) - lb bc, CNF_SLP_PRZ, POISONED - jr ApplyStatusEffect - -DoublePoisonEffect: ; 2c00c (b:400c) - lb bc, CNF_SLP_PRZ, DOUBLE_POISONED - jr ApplyStatusEffect - -Paralysis50PercentEffect: ; 2c011 (b:4011) - ldtx de, ParalysisCheckText - call TossCoin_BankB - ret nc - -ParalysisEffect: ; 2c018 (b:4018) - lb bc, PSN_DBLPSN, PARALYZED - jr ApplyStatusEffect - -Confusion50PercentEffect: ; 2c01d (b:401d) - ldtx de, ConfusionCheckText - call TossCoin_BankB - ret nc - -ConfusionEffect: ; 2c024 (b:4024) - lb bc, PSN_DBLPSN, CONFUSED - jr ApplyStatusEffect - -Sleep50PercentEffect: ; 2c029 (b:4029) - ldtx de, SleepCheckText - call TossCoin_BankB - ret nc - -SleepEffect: ; 2c030 (b:4030) - lb bc, PSN_DBLPSN, ASLEEP - jr ApplyStatusEffect - -ApplyStatusEffect: ; 2c035 (b:4035) - ldh a, [hWhoseTurn] - ld hl, wWhoseTurn - cp [hl] - jr nz, .can_induce_status - ld a, [wTempNonTurnDuelistCardID] - cp CLEFAIRY_DOLL - jr z, .cant_induce_status - cp MYSTERIOUS_FOSSIL - jr z, .cant_induce_status - ; Snorlax's Thick Skinned prevents it from being statused... - cp SNORLAX - jr nz, .can_induce_status - call SwapTurn - xor a - ; ...unless already so, or if affected by Muk's Toxic Gas - call CheckCannotUseDueToStatus_OnlyToxicGasIfANon0 - call SwapTurn - jr c, .can_induce_status - -.cant_induce_status - ld a, c - ld [wNoEffectFromWhichStatus], a - call SetNoEffectFromStatus - or a - ret - -.can_induce_status - ld hl, wEffectFunctionsFeedbackIndex - push hl - ld e, [hl] - ld d, $0 - ld hl, wEffectFunctionsFeedback - add hl, de - call SwapTurn - ldh a, [hWhoseTurn] - ld [hli], a - call SwapTurn - ld [hl], b ; mask of status conditions not to discard on the target - inc hl - ld [hl], c ; status condition to inflict to the target - pop hl - ; advance wEffectFunctionsFeedbackIndex - inc [hl] - inc [hl] - inc [hl] - scf - ret - -TossCoin_BankB: ; 2c07e (b:407e) - call TossCoin - ret - -TossCoinATimes_BankB: ; 2c082 (b:4082) - call TossCoinATimes - ret - -CommentedOut_2c086: ; 2c086 (b:4086) - ret - -Func_2c087: ; 2c087 (b:4087) - xor a - jr Func_2c08c - -Func_2c08a: ; 2c08a (b:408a) - ld a, $1 - -Func_2c08c: ; 2c08c (b:408c) - push de - push af - ld a, OPPACTION_TOSS_COIN_A_TIMES - call SetOppAction_SerialSendDuelData - pop af - pop de - call SerialSend8Bytes - call TossCoinATimes - ret - -SetNoEffectFromStatus: ; 2c09c (b:409c) - ld a, EFFECT_FAILED_NO_EFFECT - ld [wEffectFailed], a - ret - -SetWasUnsuccessful: ; 2c0a2 (b:40a2) - ld a, EFFECT_FAILED_UNSUCCESSFUL - ld [wEffectFailed], a - ret - -Func_2c0a8: ; 2c0a8 (b:40a8) - ldh a, [hTemp_ffa0] - push af - ldh a, [hWhoseTurn] - ldh [hTemp_ffa0], a - ld a, OPPACTION_6B30 - call SetOppAction_SerialSendDuelData - bank1call Func_4f2d - ld c, a - pop af - ldh [hTemp_ffa0], a - ld a, c - ret - -Func_2c0bd: ; 2c0bd (b:40bd) - call ExchangeRNG - bank1call Func_4f2d - call ShuffleDeck - ret - -; return carry if Player is the Turn Duelist -IsPlayerTurn: ; 2c0c7 (b:40c7) - ld a, DUELVARS_DUELIST_TYPE - call GetTurnDuelistVariable - cp DUELIST_TYPE_PLAYER - jr z, .player - or a - ret -.player - scf - ret - -; Stores information about the attack damage for AI purposes -; taking into account poison damage between turns. -; if target poisoned -; [wAIMinDamage] <- [wDamage] -; [wAIMaxDamage] <- [wDamage] -; else -; [wAIMinDamage] <- [wDamage] + d -; [wAIMaxDamage] <- [wDamage] + e -; [wDamage] <- [wDamage] + a -UpdateExpectedAIDamage_AccountForPoison: ; 2c0d4 (b:40d4) - push af - ld a, DUELVARS_ARENA_CARD_STATUS - call GetNonTurnDuelistVariable - and POISONED | DOUBLE_POISONED - jr z, UpdateExpectedAIDamage.skip_push_af - pop af - ld a, [wDamage] - ld [wAIMinDamage], a - ld [wAIMaxDamage], a - ret - -; Sets some variables for AI use -; [wAIMinDamage] <- [wDamage] + d -; [wAIMaxDamage] <- [wDamage] + e -; [wDamage] <- [wDamage] + a -UpdateExpectedAIDamage: ; 2c0e9 (b:40e9) - push af - -.skip_push_af - ld hl, wDamage - ld a, [hl] - add d - ld [wAIMinDamage], a - ld a, [hl] - add e - ld [wAIMaxDamage], a - pop af - add [hl] - ld [hl], a - ret - -; Stores information about the attack damage for AI purposes -; [wDamage] <- a (average amount of damage) -; [wAIMinDamage] <- d (minimum) -; [wAIMaxDamage] <- e (maximum) -SetExpectedAIDamage: ; 2c0fb (b:40fb) - ld [wDamage], a - xor a - ld [wDamage + 1], a - ld a, d - ld [wAIMinDamage], a - ld a, e - ld [wAIMaxDamage], a - ret - -Func_2c10b: ; 2c10b (b:410b) - ldh [hTempPlayAreaLocation_ff9d], a - bank1call Func_61a1 - bank1call PrintPlayAreaCardList_EnableLCD - bank1call Func_6194 - ret - -; deal damage to all the turn holder's benched Pokemon -; input: a = amount of damage to deal to each Pokemon -DealDamageToAllBenchedPokemon: ; 2c117 (b:4117) - ld e, a - ld d, $00 - ld a, DUELVARS_NUMBER_OF_POKEMON_IN_PLAY_AREA - call GetTurnDuelistVariable - ld c, a - ld b, PLAY_AREA_ARENA - jr .skip_to_bench -.loop - push bc - call DealDamageToPlayAreaPokemon_RegularAnim - pop bc -.skip_to_bench - inc b - dec c - jr nz, .loop - ret - -Func_2c12e: ; 2c12e (b:412e) - ld [wLoadedAttackAnimation], a - ldh a, [hTempPlayAreaLocation_ff9d] - ld b, a - ld c, $0 ; neither WEAKNESS nor RESISTANCE - ldh a, [hWhoseTurn] - ld h, a - bank1call PlayAttackAnimation - bank1call WaitAttackAnimation - ret - -; apply a status condition of type 1 identified by register a to the target -ApplySubstatus1ToDefendingCard: ; 2c140 (b:4140) - push af - ld a, DUELVARS_ARENA_CARD_SUBSTATUS1 - call GetTurnDuelistVariable - pop af - ld [hli], a - ret - -; apply a status condition of type 2 identified by register a to the target, -; unless prevented by wNoDamageOrEffect -ApplySubstatus2ToDefendingCard: ; 2c149 (b:4149) - push af - call CheckNoDamageOrEffect - jr c, .no_damage_orEffect - ld a, DUELVARS_ARENA_CARD_SUBSTATUS2 - call GetNonTurnDuelistVariable - pop af - ld [hl], a - ld l, $f6 - ld [hl], a - ret - -.no_damage_orEffect - pop af - push hl - bank1call DrawDuelMainScene - pop hl - ld a, l - or h - call nz, DrawWideTextBox_PrintText - ret - -; overwrites in wDamage, wAIMinDamage and wAIMaxDamage -; with the value in a. -SetDefiniteDamage: ; 2c166 (b:4166) - ld [wDamage], a - ld [wAIMinDamage], a - ld [wAIMaxDamage], a - xor a - ld [wDamage + 1], a - ret - -; overwrites wAIMinDamage and wAIMaxDamage -; with value in wDamage. -SetDefiniteAIDamage: ; 2c174 (b:4174) - ld a, [wDamage] - ld [wAIMinDamage], a - ld [wAIMaxDamage], a - ret - -; returns in a some random occupied Play Area location -; in Turn Duelist's Play Area. -PickRandomPlayAreaCard: ; 2c17e (b:417e) - ld a, DUELVARS_NUMBER_OF_POKEMON_IN_PLAY_AREA - call GetTurnDuelistVariable - call Random - or a - ret - -; outputs in hl the next position -; in hTempList to place a new card, -; and increments hCurSelectionItem. -GetNextPositionInTempList: ; 2c188 (b:4188) - push de - ld hl, hCurSelectionItem - ld a, [hl] - inc [hl] - ld e, a - ld d, $00 - ld hl, hTempList - add hl, de - pop de - ret - -; creates in wDuelTempList list of attached Fire Energy cards -; that are attached to the Turn Duelist's Arena card. -CreateListOfFireEnergyAttachedToArena: ; 2c197 (b:4197) - ld a, TYPE_ENERGY_FIRE - ; fallthrough - -; creates in wDuelTempList a list of cards that -; are in the Arena of the same type as input a. -; this is called to list Energy cards of a specific type -; that are attached to the Arena Pokemon. -; input: -; a = TYPE_ENERGY_* constant -; output: -; a = number of cards in list; -; wDuelTempList filled with cards, terminated by $ff -CreateListOfEnergyAttachedToArena: ; 2c199 (b:4199) - ld b, a - ld c, 0 - ld de, wDuelTempList - ld a, DUELVARS_CARD_LOCATIONS - call GetTurnDuelistVariable -.loop - ld a, [hl] - cp CARD_LOCATION_ARENA - jr nz, .next - push de - ld a, l - call GetCardIDFromDeckIndex - call GetCardType - pop de - cp b - jr nz, .next ; is same as input type? - ld a, l - ld [de], a - inc de - inc c -.next - inc l - ld a, l - cp DECK_SIZE - jr c, .loop - - ld a, $ff - ld [de], a - ld a, c - ret - -; prints the text " devolved to !" with -; the proper card names and levels. -; input: -; d = deck index of the lower stage card -; e = deck index of card that was devolved -PrintDevolvedCardNameAndLevelText: ; 2c1c4 (b:41c4) - push de - ld a, e - call LoadCardDataToBuffer1_FromDeckIndex - ld bc, wTxRam2 - ld hl, wLoadedCard1Name - ld a, [hli] - ld [bc], a - inc bc - ld a, [hl] - ld [bc], a - - inc bc ; wTxRam2_b - xor a - ld [bc], a - inc bc - ld [bc], a - - ld a, d - call LoadCardDataToBuffer1_FromDeckIndex - ld a, 18 - call CopyCardNameAndLevel - ld [hl], $00 - ldtx hl, PokemonDevolvedToText - call DrawWideTextBox_WaitForInput - pop de - ret - -HandleSwitchDefendingPokemonEffect: ; 2c1ec (b:41ec) - ld e, a - cp $ff - ret z - -; check Defending Pokemon's HP - ld a, DUELVARS_ARENA_CARD_HP - call GetNonTurnDuelistVariable - or a - jr nz, .switch - -; if 0, handle Destiny Bond first - push de - bank1call HandleDestinyBondSubstatus - pop de - -.switch - call HandleNoDamageOrEffect - ret c - -; attack was successful, switch Defending Pokemon - call SwapTurn - call SwapArenaWithBenchPokemon - call SwapTurn - - xor a - ld [wccc5], a - ld [wDuelDisplayedScreen], a - inc a - ld [wccef], a - ret - -; returns carry if Defending has No Damage or Effect -; if so, print its appropriate text. -HandleNoDamageOrEffect: ; 2c216 (b:4216) - call CheckNoDamageOrEffect - ret nc - ld a, l - or h - call nz, DrawWideTextBox_PrintText - scf - ret - -; applies HP recovery on Pokemon after an attack -; with HP recovery effect, and handles its animation. -; input: -; d = damage effectiveness -; e = HP amount to recover -ApplyAndAnimateHPRecovery: ; 2c221 (b:4221) - push de - ld hl, wccbd - ld [hl], e - inc hl - ld [hl], d - -; get Arena card's damage - ld e, PLAY_AREA_ARENA - call GetCardDamageAndMaxHP - pop de - or a - ret z ; return if no damage - -; load correct animation - push de - ld a, ATK_ANIM_HEAL - ld [wLoadedAttackAnimation], a - ld bc, $01 ; arrow - bank1call PlayAttackAnimation - -; compare HP to be restored with max HP -; if HP to be restored would cause HP to -; be larger than max HP, cap it accordingly - ld e, PLAY_AREA_ARENA - call GetCardDamageAndMaxHP - ld b, $00 - pop de - ld a, DUELVARS_ARENA_CARD_HP - call GetTurnDuelistVariable - add e - ld e, a - ld a, 0 - adc d - ld d, a - ; de = damage dealt + current HP - ; bc = max HP of card - call CompareDEtoBC - jr c, .skip_cap - ; cap de to value in bc - ld e, c - ld d, b - -.skip_cap - ld [hl], e ; apply new HP to arena card - bank1call WaitAttackAnimation - ret - -; returns carry if Play Area has no damage counters. -CheckIfPlayAreaHasAnyDamage: ; 2c25b (b:425b) - ld a, DUELVARS_NUMBER_OF_POKEMON_IN_PLAY_AREA - call GetTurnDuelistVariable - ld d, a - ld e, PLAY_AREA_ARENA -.loop_play_area - call GetCardDamageAndMaxHP - or a - ret nz ; found damage - inc e - dec d - jr nz, .loop_play_area - ; no damage found - scf - ret - -; makes a list in wDuelTempList with the deck indices -; of Trainer cards found in Turn Duelist's Discard Pile. -; returns carry set if no Trainer cards found, and loads -; corresponding text to notify this. -CreateTrainerCardListFromDiscardPile: ; 2c26e (b:426e) -; get number of cards in Discard Pile -; and have hl point to the end of the -; Discard Pile list in wOpponentDeckCards. - ld a, DUELVARS_NUMBER_OF_CARDS_IN_DISCARD_PILE - call GetTurnDuelistVariable - ld b, a - add DUELVARS_DECK_CARDS - ld l, a - - ld de, wDuelTempList - inc b - jr .next_card - -.check_trainer - ld a, [hl] - call LoadCardDataToBuffer2_FromDeckIndex - ld a, [wLoadedCard2Type] - cp TYPE_TRAINER - jr nz, .next_card - - ld a, [hl] - ld [de], a - inc de - -.next_card - dec l - dec b - jr nz, .check_trainer - - ld a, $ff ; terminating byte - ld [de], a - ld a, [wDuelTempList] - cp $ff - jr z, .no_trainers - or a - ret -.no_trainers - ldtx hl, ThereAreNoTrainerCardsInDiscardPileText - scf - ret - -; makes a list in wDuelTempList with the deck indices -; of all basic energy cards found in Turn Duelist's Discard Pile. -CreateEnergyCardListFromDiscardPile_OnlyBasic: ; 2c2a0 (b:42a0) - ld c, $01 - jr CreateEnergyCardListFromDiscardPile - -; makes a list in wDuelTempList with the deck indices -; of all energy cards (including Double Colorless) -; found in Turn Duelist's Discard Pile. -CreateEnergyCardListFromDiscardPile_AllEnergy: ; 2c2a4 (b:42a4) - ld c, $00 -; fallthrough - -; makes a list in wDuelTempList with the deck indices -; of energy cards found in Turn Duelist's Discard Pile. -; if (c == 0), all energy cards are allowed; -; if (c != 0), double colorless energy cards are not included. -; returns carry if no energy cards were found. -CreateEnergyCardListFromDiscardPile: ; 2c2a6 (b:42a6) -; get number of cards in Discard Pile -; and have hl point to the end of the -; Discard Pile list in wOpponentDeckCards. - ld a, DUELVARS_NUMBER_OF_CARDS_IN_DISCARD_PILE - call GetTurnDuelistVariable - ld b, a - add DUELVARS_DECK_CARDS - ld l, a - - ld de, wDuelTempList - inc b - jr .next_card - -.check_energy - ld a, [hl] - call LoadCardDataToBuffer2_FromDeckIndex - ld a, [wLoadedCard2Type] - and TYPE_ENERGY - jr z, .next_card - -; if (c != $00), then we dismiss Double Colorless -; energy cards found. - ld a, c - or a - jr z, .copy - ld a, [wLoadedCard2Type] - cp TYPE_ENERGY_DOUBLE_COLORLESS - jr nc, .next_card - -.copy - ld a, [hl] - ld [de], a - inc de - -; goes through Discard Pile list -; in wOpponentDeckCards in descending order. -.next_card - dec l - dec b - jr nz, .check_energy - -; terminating byte on wDuelTempList - ld a, $ff - ld [de], a - -; check if any energy card was found -; by checking whether the first byte -; in wDuelTempList is $ff. -; if none were found, return carry. - ld a, [wDuelTempList] - cp $ff - jr z, .set_carry - or a - ret - -.set_carry - scf - ret - -; returns carry if Deck is empty -CheckIfDeckIsEmpty: ; 2c2e0 (b:42e0) - ld a, DUELVARS_NUMBER_OF_CARDS_NOT_IN_DECK - call GetTurnDuelistVariable - ldtx hl, NoCardsLeftInTheDeckText - cp DECK_SIZE - ccf - ret - -; searches through Deck in wDuelTempList looking for -; a certain card or cards, and prints text depending -; on whether at least one was found. -; if none were found, asks the Player whether to look -; in the Deck anyway, and returns carry if No is selected. -; uses SEARCHEFFECT_* as input which determines what to search for: -; SEARCHEFFECT_CARD_ID = search for card ID in e -; SEARCHEFFECT_NIDORAN = search for either NidoranM or NidoranF -; SEARCHEFFECT_BASIC_FIGHTING = search for any Basic Fighting Pokemon -; SEARCHEFFECT_BASIC_ENERGY = search for any Basic Energy -; SEARCHEFFECT_POKEMON = search for any Pokemon card -; input: -; d = SEARCHEFFECT_* constant -; e = (optional) card ID to search for in deck -; hl = text to print if Deck has card(s) -; output: -; carry set if refused to look at deck -LookForCardsInDeck: ; 2c2ec (b:42ec) - push hl - push bc - ld a, [wDuelTempList] - cp $ff - jr z, .none_in_deck - ld a, d - ld hl, .search_table - call JumpToFunctionInTable - jr c, .none_in_deck - pop bc - pop hl - call DrawWideTextBox_WaitForInput - or a - ret - -.none_in_deck - pop hl - call LoadTxRam2 - pop hl - ldtx hl, ThereIsNoInTheDeckText - call DrawWideTextBox_WaitForInput - ldtx hl, WouldYouLikeToCheckTheDeckText - call YesOrNoMenuWithText_SetCursorToYes - ret - -.search_table - dw .SearchDeckForCardID - dw .SearchDeckForNidoran - dw .SearchDeckForBasicFighting - dw .SearchDeckForBasicEnergy - dw .SearchDeckForPokemon - -.set_carry ; 2c321 (b:4321) - scf - ret - -; returns carry if no card with -; same card ID as e is found in Deck -.SearchDeckForCardID ; 2c323 (b:4323) - ld hl, wDuelTempList -.loop_deck_e - ld a, [hli] - cp $ff - jr z, .set_carry - push de - call GetCardIDFromDeckIndex - ld a, e - pop de - cp e - jr nz, .loop_deck_e - or a - ret - -; returns carry if no NidoranM or NidoranF card is found in Deck -.SearchDeckForNidoran ; 2c336 (b:4336) - ld hl, wDuelTempList -.loop_deck_nidoran - ld a, [hli] - cp $ff - jr z, .set_carry - call GetCardIDFromDeckIndex - ld a, e - cp NIDORANF - jr z, .found_nidoran - cp NIDORANM - jr nz, .loop_deck_nidoran -.found_nidoran - or a - ret - -; returns carry if no Basic Fighting Pokemon is found in Deck -.SearchDeckForBasicFighting ; 2c34c (b:434c) - ld hl, wDuelTempList -.loop_deck_fighting - ld a, [hli] - cp $ff - jr z, .set_carry - call LoadCardDataToBuffer2_FromDeckIndex - ld a, [wLoadedCard2Type] - cp TYPE_PKMN_FIGHTING - jr nz, .loop_deck_fighting - ld a, [wLoadedCard2Stage] - or a ; BASIC - jr nz, .loop_deck_fighting - ret - -; returns carry if no Basic Energy cards are found in Deck -.SearchDeckForBasicEnergy ; 2c365 (b:4365) - ld hl, wDuelTempList -.loop_deck_energy - ld a, [hli] - cp $ff - jr z, .set_carry - call GetCardIDFromDeckIndex - call GetCardType - cp TYPE_ENERGY_DOUBLE_COLORLESS - jr z, .loop_deck_energy - and TYPE_ENERGY - jr z, .loop_deck_energy - or a - ret - -; returns carry if no Pokemon cards are found in Deck -.SearchDeckForPokemon ; 2c37d (b:437d) - ld hl, wDuelTempList -.loop_deck_pkmn - ld a, [hli] - cp $ff - jr z, .set_carry - call GetCardIDFromDeckIndex - call GetCardType - cp TYPE_ENERGY - jr nc, .loop_deck_pkmn - or a - ret - -; handles the Player selection of attack -; to use, i.e. Amnesia or Metronome on. -; returns carry if none selected. -; outputs: -; d = card index of defending card -; e = attack index selected -HandleDefendingPokemonAttackSelection: ; 2c391 (b:4391) - bank1call DrawDuelMainScene - call SwapTurn - xor a - ldh [hCurSelectionItem], a - -.start - bank1call PrintAndLoadAttacksToDuelTempList - push af - ldh a, [hCurSelectionItem] - ld hl, .menu_parameters - call InitializeMenuParameters - pop af - - ld [wNumMenuItems], a - call EnableLCD -.loop_input - call DoFrame - ldh a, [hKeysPressed] - bit B_BUTTON_F, a - jr nz, .set_carry - and START - jr nz, .open_atk_page - call HandleMenuInput - jr nc, .loop_input - cp -1 - jr z, .loop_input - -; an attack was selected - ldh a, [hCurMenuItem] - add a - ld e, a - ld d, $00 - ld hl, wDuelTempList - add hl, de - ld d, [hl] - inc hl - ld e, [hl] - call SwapTurn - or a - ret - -.set_carry - call SwapTurn - scf - ret - -.open_atk_page - ldh a, [hCurMenuItem] - ldh [hCurSelectionItem], a - ld a, DUELVARS_ARENA_CARD - call GetTurnDuelistVariable - call LoadCardDataToBuffer1_FromDeckIndex - bank1call OpenAttackPage - call SwapTurn - bank1call DrawDuelMainScene - call SwapTurn - jr .start - -.menu_parameters - db 1, 13 ; cursor x, cursor y - db 2 ; y displacement between items - db 2 ; number of items - db SYM_CURSOR_R ; cursor tile number - db SYM_SPACE ; tile behind cursor - dw NULL ; function pointer if non-0 - -; loads in hl the pointer to attack's name. -; input: -; d = deck index of card -; e = attack index (0 = first attack, 1 = second attack) -GetAttackName: ; 2c3fc (b:43fc) - ld a, d - call LoadCardDataToBuffer1_FromDeckIndex - ld hl, wLoadedCard1Atk1Name - inc e - dec e - jr z, .load_name - ld hl, wLoadedCard1Atk2Name -.load_name - ld a, [hli] - ld h, [hl] - ld l, a - ret - -; returns carry if Defending Pokemon -; doesn't have an attack. -CheckIfDefendingPokemonHasAnyAttack: ; 2c40e (b:440e) - call SwapTurn - ld a, DUELVARS_ARENA_CARD - call GetTurnDuelistVariable - call LoadCardDataToBuffer2_FromDeckIndex - ld a, [wLoadedCard2Atk1Category] - cp POKEMON_POWER - jr nz, .has_attack - ld hl, wLoadedCard2Atk2Name - ld a, [hli] - or [hl] - jr nz, .has_attack - call SwapTurn - scf - ret -.has_attack - call SwapTurn - or a - ret - -; overwrites HP and Stage data of the card that was -; devolved in the Play Area to the values of new card. -; if the damage exceeds HP of pre-evolution, -; then HP is set to zero. -; input: -; a = card index of pre-evolved card -UpdateDevolvedCardHPAndStage: ; 2c431 (b:4431) - push bc - push de - push af - ldh a, [hTempPlayAreaLocation_ff9d] - ld e, a - call GetCardDamageAndMaxHP - ld b, a ; store damage - ld a, e - add DUELVARS_ARENA_CARD - call GetTurnDuelistVariable - pop af - - ld [hl], a - call LoadCardDataToBuffer2_FromDeckIndex - ld a, e - add DUELVARS_ARENA_CARD_HP - ld l, a - ld a, [wLoadedCard2HP] - sub b ; subtract damage from new HP - jr nc, .got_hp - ; damage exceeds HP - xor a ; 0 HP -.got_hp - ld [hl], a - ld a, e -; overwrite card stage - add DUELVARS_ARENA_CARD_STAGE - ld l, a - ld a, [wLoadedCard2Stage] - ld [hl], a - pop de - pop bc - ret - -; reset various status after devolving card. -ResetDevolvedCardStatus: ; 2c45d (b:445d) -; if it's Arena card, clear status conditions - ldh a, [hTempPlayAreaLocation_ff9d] - or a - jr nz, .skip_clear_status - call ClearAllStatusConditions -.skip_clear_status -; reset changed color status - ldh a, [hTempPlayAreaLocation_ff9d] - add DUELVARS_ARENA_CARD_CHANGED_TYPE - call GetTurnDuelistVariable - ld [hl], $00 -; reset C2 flags - ldh a, [hTempPlayAreaLocation_ff9d] - add DUELVARS_ARENA_CARD_FLAGS - ld l, a - ld [hl], $00 - ret - -; prompts the Player with a Yes/No question -; whether to quit the screen, even though -; they can select more cards from list. -; [hCurSelectionItem] holds number of cards -; that were already selected by the Player. -; input: -; - a = total number of cards that can be selected -; output: -; - carry set if "No" was selected -AskWhetherToQuitSelectingCards: ; 2c476 (b:4476) - ld hl, hCurSelectionItem - sub [hl] - ld l, a - ld h, $00 - call LoadTxRam3 - ldtx hl, YouCanSelectMoreCardsQuitText - call YesOrNoMenuWithText - ret - -; handles the selection of a forced switch by link/AI opponent or by the player. -; outputs the Play Area location of the chosen bench card in hTempPlayAreaLocation_ff9d. -DuelistSelectForcedSwitch: ; 2c487 (b:4487) - ld a, DUELVARS_DUELIST_TYPE - call GetNonTurnDuelistVariable - cp DUELIST_TYPE_LINK_OPP - jr z, .link_opp - - cp DUELIST_TYPE_PLAYER - jr z, .player - -; AI opponent - call SwapTurn - bank1call AIDoAction_ForcedSwitch - call SwapTurn - - ld a, [wPlayerAttackingAttackIndex] - ld e, a - ld a, [wPlayerAttackingCardIndex] - ld d, a - ld a, [wPlayerAttackingCardID] - call CopyAttackDataAndDamage_FromCardID - call Func_16f6 - ret - -.player - ldtx hl, SelectPkmnOnBenchToSwitchWithActiveText - call DrawWideTextBox_WaitForInput - call SwapTurn - bank1call HasAlivePokemonInBench - ld a, $01 - ld [wcbd4], a -.asm_2c4c0 - bank1call OpenPlayAreaScreenForSelection - jr c, .asm_2c4c0 - call SwapTurn - ret - -.link_opp -; get selection from link opponent - ld a, OPPACTION_FORCE_SWITCH_ACTIVE - call SetOppAction_SerialSendDuelData -.loop - call SerialRecvByte - jr nc, .received - halt - nop - jr .loop -.received - ldh [hTempPlayAreaLocation_ff9d], a - ret - -; returns in a the card index of energy card -; attached to Defending Pokemon -; that is to be discarded by the AI for an effect. -; outputs $ff is none was found. -; output: -; a = deck index of attached energy card chosen -AIPickEnergyCardToDiscardFromDefendingPokemon: ; 2c4da (b:44da) - call SwapTurn - ld e, PLAY_AREA_ARENA - call GetPlayAreaCardAttachedEnergies - - xor a - call CreateArenaOrBenchEnergyCardList - jr nc, .has_energy - ; no energy, return - ld a, $ff - jr .done - -.has_energy - ld a, DUELVARS_ARENA_CARD - call GetTurnDuelistVariable - call LoadCardDataToBuffer1_FromDeckIndex - ld e, COLORLESS - ld a, [wAttachedEnergies + COLORLESS] - or a - jr nz, .pick_color ; has colorless attached? - - ; no colorless energy attached. - ; if it's colorless Pokemon, just - ; pick any energy card at random... - ld a, [wLoadedCard1Type] - cp COLORLESS - jr nc, .choose_random - - ; ...if not, check if it has its - ; own color energy attached. - ; if it doesn't, pick at random. - ld e, a - ld d, $00 - ld hl, wAttachedEnergies - add hl, de - ld a, [hl] - or a - jr z, .choose_random - -; pick attached card with same color as e -.pick_color - ld hl, wDuelTempList -.loop_energy - ld a, [hli] - cp $ff - jr z, .choose_random - call LoadCardDataToBuffer2_FromDeckIndex - ld a, [wLoadedCard2Type] - and TYPE_PKMN - cp e - jr nz, .loop_energy - dec hl - -.done_chosen - ld a, [hl] -.done - call SwapTurn - ret - -.choose_random - call CountCardsInDuelTempList - ld hl, wDuelTempList - call ShuffleCards - jr .done_chosen - -; handles AI logic to pick attack for Amnesia -AIPickAttackForAmnesia: ; 2c532 (b:4532) -; load Defending Pokemon attacks - call SwapTurn - ld e, PLAY_AREA_ARENA - call GetPlayAreaCardAttachedEnergies - call HandleEnergyBurn - ld a, DUELVARS_ARENA_CARD - call GetTurnDuelistVariable - ld d, a - call LoadCardDataToBuffer2_FromDeckIndex -; if has no attack 1 name, return - ld hl, wLoadedCard2Atk1Name - ld a, [hli] - or [hl] - jr z, .chosen - -; if Defending Pokemon has enough energy for second attack, choose it - ld e, SECOND_ATTACK - bank1call _CheckIfEnoughEnergiesToAttack - jr nc, .chosen -; otherwise if first attack isn't a Pkmn Power, choose it instead. - ld e, FIRST_ATTACK_OR_PKMN_POWER - ld a, [wLoadedCard2Atk1Category] - cp POKEMON_POWER - jr nz, .chosen -; if it is a Pkmn Power, choose second attack. - ld e, SECOND_ATTACK -.chosen - ld a, e - call SwapTurn - ret - -; Return in a the PLAY_AREA_* of the non-turn holder's Pokemon card in bench with the lowest (remaining) HP. -; if multiple cards are tied for the lowest HP, the one with the highest PLAY_AREA_* is returned. -GetBenchPokemonWithLowestHP: ; 2c564 (b:4564) - call SwapTurn - ld a, DUELVARS_NUMBER_OF_POKEMON_IN_PLAY_AREA - call GetTurnDuelistVariable - ld c, a - lb de, PLAY_AREA_ARENA, $ff - ld b, d - ld a, DUELVARS_BENCH1_CARD_HP - call GetTurnDuelistVariable - jr .start -; find Play Area location with least amount of HP -.loop_bench - ld a, e - cp [hl] - jr c, .next ; skip if HP is higher - ld e, [hl] - ld d, b - -.next - inc hl -.start - inc b - dec c - jr nz, .loop_bench - - ld a, d - call SwapTurn - ret - -; handles drawing and selection of screen for -; choosing a color (excluding colorless), for use -; of Shift Pkmn Power and Conversion attacks. -; outputs in a the color that was selected or, -; if B was pressed, returns carry. -; input: -; a = Play Area location (PLAY_AREA_*), with: -; bit 7 not set if it's applying to opponent's card -; bit 7 set if it's applying to player's card -; hl = text to be printed in the bottom box -; output: -; a = color that was selected -HandleColorChangeScreen: ; 2c588 (b:4588) - or a - call z, SwapTurn - push af - call .DrawScreen - pop af - call z, SwapTurn - - ld hl, .menu_params - xor a - call InitializeMenuParameters - call EnableLCD - -.loop_input - call DoFrame - call HandleMenuInput - jr nc, .loop_input - cp -1 ; b pressed? - jr z, .set_carry - ld e, a - ld d, $00 - ld hl, ShiftListItemToColor - add hl, de - ld a, [hl] - or a - ret -.set_carry - scf - ret - -.menu_params - db 1, 1 ; cursor x, cursor y - db 2 ; y displacement between items - db MAX_PLAY_AREA_POKEMON ; number of items - db SYM_CURSOR_R ; cursor tile number - db SYM_SPACE ; tile behind cursor - dw NULL ; function pointer if non-0 - -.DrawScreen: ; 2c5be (b:45be) - push hl - push af - call EmptyScreen - call ZeroObjectPositions - call LoadDuelCardSymbolTiles - -; load card data - pop af - and $7f - ld [wTempPlayAreaLocation_cceb], a - add DUELVARS_ARENA_CARD - call GetTurnDuelistVariable - call LoadCardDataToBuffer1_FromDeckIndex - -; draw card gfx - ld de, v0Tiles1 + $20 tiles ; destination offset of loaded gfx - ld hl, wLoadedCard1Gfx - ld a, [hli] - ld h, [hl] - ld l, a - lb bc, $30, TILE_SIZE - call LoadCardGfx - bank1call SetBGP6OrSGB3ToCardPalette - bank1call FlushAllPalettesOrSendPal23Packet - ld a, $a0 - lb hl, 6, 1 - lb de, 9, 2 - lb bc, 8, 6 - call FillRectangle - bank1call ApplyBGP6OrSGB3ToCardImage - -; print card name and level at the top - ld a, 16 - call CopyCardNameAndLevel - ld [hl], $00 - lb de, 7, 0 - call InitTextPrinting - ld hl, wDefaultText - call ProcessText - -; list all the colors - ld hl, ShiftMenuData - call PlaceTextItems - -; print card's color, resistance and weakness - ld a, [wTempPlayAreaLocation_cceb] - call GetPlayAreaCardColor - inc a - lb bc, 15, 9 - call WriteByteToBGMap0 - ld a, [wTempPlayAreaLocation_cceb] - call GetPlayAreaCardWeakness - lb bc, 15, 10 - bank1call PrintCardPageWeaknessesOrResistances - ld a, [wTempPlayAreaLocation_cceb] - call GetPlayAreaCardResistance - lb bc, 15, 11 - bank1call PrintCardPageWeaknessesOrResistances - - call DrawWideTextBox - -; print list of color names on all list items - lb de, 4, 1 - ldtx hl, ColorListText - call InitTextPrinting_ProcessTextFromID - -; print input hl to text box - lb de, 1, 14 - pop hl - call InitTextPrinting_ProcessTextFromID - -; draw and apply palette to color icons - ld hl, ColorTileAndBGP - lb de, 2, 0 - ld c, NUM_COLORED_TYPES -.loop_colors - ld a, [hli] - push de - push bc - push hl - lb hl, 1, 2 - lb bc, 2, 2 - call FillRectangle - - ld a, [wConsole] - cp CONSOLE_CGB - jr nz, .skip_vram1 - pop hl - push hl - call BankswitchVRAM1 - ld a, [hl] - lb hl, 0, 0 - lb bc, 2, 2 - call FillRectangle - call BankswitchVRAM0 - -.skip_vram1 - pop hl - pop bc - pop de - inc hl - inc e - inc e - dec c - jr nz, .loop_colors - ret - -; loads wTxRam2 and wTxRam2_b: -; [wTxRam2] <- wLoadedCard1Name -; [wTxRam2_b] <- input color as text symbol -; input: -; a = type (color) constant -LoadCardNameAndInputColor: ; 2c686 (b:4686) - add a - ld e, a - ld d, $00 - ld hl, ColorToTextSymbol - add hl, de - -; load wTxRam2 with card's name - ld de, wTxRam2 - ld a, [wLoadedCard1Name] - ld [de], a - inc de - ld a, [wLoadedCard1Name + 1] - ld [de], a - -; load wTxRam2_b with ColorToTextSymbol - inc de - ld a, [hli] - ld [de], a - inc de - ld a, [hli] - ld [de], a - ret - -ShiftMenuData: ; 2c6a1 (b:46a1) - ; x, y, text id - textitem 10, 9, TypeText - textitem 10, 10, WeaknessText - textitem 10, 11, ResistanceText - db $ff - -ColorTileAndBGP: ; 2c6ae (b:46ae) - ; tile, BG - db $e4, $02 - db $e0, $01 - db $eC, $02 - db $e8, $01 - db $f0, $03 - db $f4, $03 - -ShiftListItemToColor: ; 2c6ba (b:46ba) - db GRASS - db FIRE - db WATER - db LIGHTNING - db FIGHTING - db PSYCHIC - -ColorToTextSymbol: ; 2c6c0 (b:46c0) - tx FireSymbolText - tx GrassSymbolText - tx LightningSymbolText - tx WaterSymbolText - tx FightingSymbolText - tx PsychicSymbolText - -DrawSymbolOnPlayAreaCursor: ; 2c6cc (b:46cc) - ld c, a - add a - add c - add 2 - ; a = 3*a + 2 - ld c, a - ld a, b - ld b, 0 - call WriteByteToBGMap0 - ret - -; possibly unreferenced -Func_2c6d9: ; 2c6d9 (b:46d9) - ldtx hl, IncompleteText - call DrawWideTextBox_WaitForInput - ret - -PlayAreaSelectionMenuParameters: ; 2c6e0 (b:46e0) - db 0, 0 ; cursor x, cursor y - db 3 ; y displacement between items - db MAX_PLAY_AREA_POKEMON ; number of items - db SYM_CURSOR_R ; cursor tile number - db SYM_SPACE ; tile behind cursor - dw NULL ; function pointer if non-0 - -BenchSelectionMenuParameters: ; 2c6e8 (b:46e8) - db 0, 3 ; cursor x, cursor y - db 3 ; y displacement between items - db MAX_PLAY_AREA_POKEMON ; number of items - db SYM_CURSOR_R ; cursor tile number - db SYM_SPACE ; tile behind cursor - dw NULL ; function pointer if non-0 - -SpitPoison_AIEffect: ; 2c6f0 (b:46f0) - ld a, 10 / 2 - lb de, 0, 10 - jp SetExpectedAIDamage - -; If heads, defending Pokemon becomes poisoned -SpitPoison_Poison50PercentEffect: ; 2c6f8 (b:46f8) - ldtx de, PoisonCheckText - call TossCoin_BankB - jp c, PoisonEffect - ld a, ATK_ANIM_SPIT_POISON_SUCCESS - ld [wLoadedAttackAnimation], a - call SetNoEffectFromStatus - ret - -; outputs in hTemp_ffa0 the result of the coin toss (0 = tails, 1 = heads). -; in case it was heads, stores in hTempPlayAreaLocation_ffa1 -; the PLAY_AREA_* location of the Bench Pokemon that was selected for switch. -TerrorStrike_50PercentSelectSwitchPokemon: ; 2c70a (b:470a) - xor a - ldh [hTemp_ffa0], a - -; return failure if no Pokemon to switch to - ld a, DUELVARS_NUMBER_OF_POKEMON_IN_PLAY_AREA - call GetNonTurnDuelistVariable - cp 2 - ret c - -; toss coin and store whether it was tails (0) or heads (1) in hTemp_ffa0. -; return if it was tails. - ldtx de, IfHeadsChangeOpponentsActivePokemonText - call Func_2c08a - ldh [hTemp_ffa0], a - ret nc - - call DuelistSelectForcedSwitch - ldh a, [hTempPlayAreaLocation_ff9d] - ldh [hTempPlayAreaLocation_ffa1], a - ret - -; if coin toss at hTemp_ffa0 was heads and it's possible, -; switch the Defending Pokemon -TerrorStrike_SwitchDefendingPokemon: ; 2c726 (b:4726) - ldh a, [hTemp_ffa0] - or a - ret z - ldh a, [hTempPlayAreaLocation_ffa1] - call HandleSwitchDefendingPokemonEffect - ret - -PoisonFang_AIEffect: ; 2c730 (b:4730) - ld a, 10 - lb de, 10, 10 - jp UpdateExpectedAIDamage_AccountForPoison - -WeepinbellPoisonPowder_AIEffect: ; 2c738 (b:4738) - ld a, 5 - lb de, 0, 10 - jp UpdateExpectedAIDamage_AccountForPoison - -; return carry if there are no Pokemon cards in the non-turn holder's bench -VictreebelLure_AssertPokemonInBench: ; 2c740 (b:4740) - ld a, DUELVARS_NUMBER_OF_POKEMON_IN_PLAY_AREA - call GetNonTurnDuelistVariable - ldtx hl, EffectNoPokemonOnTheBenchText - cp 2 - ret - -; return in hTempPlayAreaLocation_ffa1 the PLAY_AREA_* location -; of the Bench Pokemon that was selected for switch -VictreebelLure_SelectSwitchPokemon: ; 2c74b (b:474b) - ldtx hl, SelectPkmnOnBenchToSwitchWithActiveText - call DrawWideTextBox_WaitForInput - call SwapTurn - bank1call HasAlivePokemonInBench -.select_pokemon - bank1call OpenPlayAreaScreenForSelection - jr c, .select_pokemon - ldh a, [hTempPlayAreaLocation_ff9d] - ldh [hTemp_ffa0], a - call SwapTurn - ret - -; Return in hTemp_ffa0 the PLAY_AREA_* of the non-turn holder's Pokemon card in bench with the lowest (remaining) HP. -; if multiple cards are tied for the lowest HP, the one with the highest PLAY_AREA_* is returned. -VictreebelLure_GetBenchPokemonWithLowestHP: ; 2c764 (b:4764) - call GetBenchPokemonWithLowestHP - ldh [hTemp_ffa0], a - ret - -; Defending Pokemon is swapped out for the one with the PLAY_AREA_* at hTemp_ffa0 -; unless Mew's Neutralizing Shield or Haunter's Transparency prevents it. -VictreebelLure_SwitchDefendingPokemon: ; 2c76a (b:476a) - call SwapTurn - ldh a, [hTemp_ffa0] - ld e, a - call HandleNShieldAndTransparency - call nc, SwapArenaWithBenchPokemon - call SwapTurn - xor a - ld [wDuelDisplayedScreen], a - ret - -; If heads, defending Pokemon can't retreat next turn -AcidEffect: ; 2c77e (b:477e) - ldtx de, AcidCheckText - call TossCoin_BankB - ret nc - ld a, SUBSTATUS2_UNABLE_RETREAT - call ApplySubstatus2ToDefendingCard - ret - -GloomPoisonPowder_AIEffect: ; 2c78b (b:478b) - ld a, 10 - lb de, 10, 10 - jp UpdateExpectedAIDamage_AccountForPoison - -; Defending Pokemon and user become confused -FoulOdorEffect: ; 2c793 (b:4793) - call ConfusionEffect - call SwapTurn - call ConfusionEffect - call SwapTurn - ret - -; If heads, prevent all damage done to user next turn -KakunaStiffenEffect: ; 2c7a0 (b:47a0) - ldtx de, IfHeadsNoDamageNextTurnText - call TossCoin_BankB - jp nc, SetWasUnsuccessful - ld a, ATK_ANIM_PROTECT - ld [wLoadedAttackAnimation], a - ld a, SUBSTATUS1_NO_DAMAGE_STIFFEN - call ApplySubstatus1ToDefendingCard - ret - -KakunaPoisonPowder_AIEffect: ; 2c7b4 (b:47b4) - ld a, 5 - lb de, 0, 10 - jp UpdateExpectedAIDamage_AccountForPoison - -GolbatLeechLifeEffect: ; 2c7bc (b:47bc) - ld hl, wDealtDamage - ld e, [hl] - inc hl ; wDamageEffectiveness - ld d, [hl] - call ApplyAndAnimateHPRecovery - ret - -VenonatLeechLifeEffect: ; 2c7c6 (b:47c6) - ld hl, wDealtDamage - ld e, [hl] - inc hl ; wDamageEffectiveness - ld d, [hl] - call ApplyAndAnimateHPRecovery - ret - -; During your next turn, double damage -SwordsDanceEffect: ; 2c7d0 (b:47d0) - ld a, [wTempTurnDuelistCardID] - cp SCYTHER - ret nz - ld a, SUBSTATUS1_NEXT_TURN_DOUBLE_DAMAGE - call ApplySubstatus1ToDefendingCard - ret - -; If heads, defending Pokemon becomes confused -ZubatSupersonicEffect: ; 2c7dc (b:47dc) - call Confusion50PercentEffect - call nc, SetNoEffectFromStatus - ret - -ZubatLeechLifeEffect: ; 2c7e3 (b:47e3) - ld hl, wDealtDamage - ld e, [hl] - inc hl - ld d, [hl] - call ApplyAndAnimateHPRecovery - ret - -Twineedle_AIEffect: ; 2c7ed (b:47ed) - ld a, 60 / 2 - lb de, 0, 60 - jp SetExpectedAIDamage - -; Flip 2 coins; deal 30x number of heads -Twineedle_MultiplierEffect: ; 2c7f5 (b:47f5) - ld hl, 30 - call LoadTxRam3 - ldtx de, DamageCheckIfHeadsXDamageText - ld a, 2 - call TossCoinATimes_BankB - ld e, a - add a - add e - call ATimes10 - call SetDefiniteDamage - ret - -BeedrillPoisonSting_AIEffect: ; 2c80d (b:480d) - ld a, 5 - lb de, 0, 10 - jp UpdateExpectedAIDamage_AccountForPoison - -ExeggcuteLeechSeedEffect: ; 2c815 (b:4815) - ld hl, wDealtDamage - ld a, [hli] - or a - ret z ; return if no damage dealt - ld de, 10 - call ApplyAndAnimateHPRecovery - ret - -FoulGas_AIEffect: ; 2c822 (b:4822) - ld a, 5 - lb de, 0, 10 - jp UpdateExpectedAIDamage - -; If heads, defending Pokemon becomes poisoned. If tails, defending Pokemon becomes confused -FoulGas_PoisonOrConfusionEffect: ; 2c82a (b:482a) - ldtx de, PoisonedIfHeadsConfusedIfTailsText - call TossCoin_BankB - jp c, PoisonEffect - jp ConfusionEffect - -; an exact copy of KakunaStiffenEffect -; If heads, prevent all damage done to user next turn -MetapodStiffenEffect: ; 2c836 (b:4836) - ldtx de, IfHeadsNoDamageNextTurnText - call TossCoin_BankB - jp nc, SetWasUnsuccessful - ld a, ATK_ANIM_PROTECT - ld [wLoadedAttackAnimation], a - ld a, SUBSTATUS1_NO_DAMAGE_STIFFEN - call ApplySubstatus1ToDefendingCard - ret - -; returns carry if no cards in Deck or if -; Play Area is full already. -Sprout_CheckDeckAndPlayArea: ; 2c84a (b:484a) - call CheckIfDeckIsEmpty - ret c ; return if no cards in deck - ld a, DUELVARS_NUMBER_OF_POKEMON_IN_PLAY_AREA - call GetTurnDuelistVariable - ldtx hl, NoSpaceOnTheBenchText - cp MAX_PLAY_AREA_POKEMON - ccf - ret - -Sprout_PlayerSelectEffect: ; 2c85a (b:485a) - ld a, $ff - ldh [hTemp_ffa0], a - - call CreateDeckCardList - ldtx hl, ChooseAnOddishFromDeckText - ldtx bc, OddishText - lb de, SEARCHEFFECT_CARD_ID, ODDISH - call LookForCardsInDeck - ret c - -; draw Deck list interface and print text - bank1call Func_5591 - ldtx hl, ChooseAnOddishText - ldtx de, DuelistDeckText - bank1call SetCardListHeaderText - -.loop - bank1call DisplayCardList - jr c, .pressed_b - call GetCardIDFromDeckIndex - ld bc, ODDISH - call CompareDEtoBC - jr nz, .play_sfx - -; Oddish was selected - ldh a, [hTempCardIndex_ff98] - ldh [hTemp_ffa0], a - or a - ret - -.play_sfx - ; play SFX and loop back - call Func_3794 - jr .loop - -.pressed_b -; figure if Player can exit the screen without selecting, -; that is, if the Deck has no Oddish card. - ld a, DUELVARS_CARD_LOCATIONS - call GetTurnDuelistVariable -.loop_b_press - ld a, [hl] - cp CARD_LOCATION_DECK - jr nz, .next - ld a, l - call GetCardIDFromDeckIndex - ld bc, ODDISH - call CompareDEtoBC - jr z, .play_sfx ; found Oddish, go back to top loop -.next - inc l - ld a, l - cp DECK_SIZE - jr c, .loop_b_press - -; no Oddish in Deck, can safely exit screen - ld a, $ff - ldh [hTemp_ffa0], a - or a - ret - -Sprout_AISelectEffect: ; 2c8b7 (b:48b7) - call CreateDeckCardList - ld hl, wDuelTempList -.loop_deck - ld a, [hli] - ldh [hTemp_ffa0], a - cp $ff - ret z ; no Oddish - call GetCardIDFromDeckIndex - ld a, e - cp ODDISH - jr nz, .loop_deck - ret ; Oddish found - -Sprout_PutInPlayAreaEffect: ; 2c8cc (b:48cc) - ldh a, [hTemp_ffa0] - cp $ff - jr z, .shuffle - call SearchCardInDeckAndAddToHand - call AddCardToHand - call PutHandPokemonCardInPlayArea - call IsPlayerTurn - jr c, .shuffle - ; display card on screen - ldh a, [hTemp_ffa0] - ldtx hl, PlacedOnTheBenchText - bank1call DisplayCardDetailScreen -.shuffle - call Func_2c0bd - ret - -; returns carry if no Pokemon on Bench -Teleport_CheckBench: ; 2c8ec (b:48ec) - ld a, DUELVARS_NUMBER_OF_POKEMON_IN_PLAY_AREA - call GetTurnDuelistVariable - ldtx hl, ThereAreNoPokemonOnBenchText - cp 2 - ret - -Teleport_PlayerSelectEffect: ; 2c8f7 (b:48f7) - ldtx hl, SelectPkmnOnBenchToSwitchWithActiveText - call DrawWideTextBox_WaitForInput - bank1call HasAlivePokemonInBench - ld a, $01 - ld [wcbd4], a -.loop - bank1call OpenPlayAreaScreenForSelection - jr c, .loop - ldh a, [hTempPlayAreaLocation_ff9d] - ldh [hTemp_ffa0], a - ret - -Teleport_AISelectEffect: ; 2c90f (b:490f) - ld a, DUELVARS_NUMBER_OF_POKEMON_IN_PLAY_AREA - call GetTurnDuelistVariable - call Random - ldh [hTemp_ffa0], a - ret - -Teleport_SwitchEffect: ; 2c91a (b:491a) - ldh a, [hTemp_ffa0] - ld e, a - call SwapArenaWithBenchPokemon - xor a - ld [wDuelDisplayedScreen], a - ret - -BigEggsplosion_AIEffect: ; 2c925 (b:4925) - ldh a, [hTempPlayAreaLocation_ff9d] - ld e, a - call GetPlayAreaCardAttachedEnergies - ld a, [wTotalAttachedEnergies] - call SetDamageToATimes20 - inc h - jr nz, .capped - ld l, 255 -.capped - ld a, l - ld [wAIMaxDamage], a - srl a - ld [wDamage], a - xor a - ld [wAIMinDamage], a - ret - -; Flip coins equal to attached energies; deal 20x number of heads -BigEggsplosion_MultiplierEffect: ; 2c944 (b:4944) - ld e, PLAY_AREA_ARENA - call GetPlayAreaCardAttachedEnergies - ld hl, 20 - call LoadTxRam3 - ld a, [wTotalAttachedEnergies] - ldtx de, DamageCheckIfHeadsXDamageText - call TossCoinATimes_BankB -; fallthrough - -; set damage to 20*a. Also return result in hl -SetDamageToATimes20: ; 2c958 (b:4958) - ld l, a - ld h, $00 - ld e, l - ld d, h - add hl, hl - add hl, hl - add hl, de - add hl, hl - add hl, hl - ld a, l - ld [wDamage], a - ld a, h - ld [wDamage + 1], a - ret - -Thrash_AIEffect: ; 2c96b (b:496b) - ld a, (30 + 40) / 2 - lb de, 30, 40 - jp SetExpectedAIDamage - -; If heads 10 more damage; if tails, 10 damage to itself -Thrash_ModifierEffect: ; 2c973 (b:4973) - ldtx de, IfHeadPlus10IfTails10ToYourselfText - call TossCoin_BankB - ldh [hTemp_ffa0], a - ret nc - ld a, 10 - call AddToDamage - ret - -Thrash_RecoilEffect: ; 2c982 (b:4982) - ldh a, [hTemp_ffa0] - or a - ret nz - ld a, 10 - call DealRecoilDamageToSelf - ret - -Toxic_AIEffect: ; 2c98c (b:498c) - ld a, 20 - lb de, 20, 20 - jp UpdateExpectedAIDamage - -; Defending Pokémon becomes double poisoned (takes 20 damage per turn rather than 10) -Toxic_DoublePoisonEffect: ; 2c994 (b:4994) - call DoublePoisonEffect - ret - -BoyfriendsEffect: ; 2c998 (b:4998) - ld a, DUELVARS_ARENA_CARD - call GetTurnDuelistVariable - ld c, PLAY_AREA_ARENA -.loop - ld a, [hl] - cp $ff - jr z, .done - call GetCardIDFromDeckIndex - ld a, e - cp NIDOKING - jr nz, .next - ld a, d - cp $00 ; why check d? Card IDs are only 1 byte long - jr nz, .next - inc c -.next - inc hl - jr .loop -.done -; c holds number of Nidoking found in Play Area - ld a, c - add a - call ATimes10 - call AddToDamage ; adds 2 * 10 * c - ret - -NidoranFFurySwipes_AIEffect: ; 2c9be (b:49be) - ld a, 30 / 2 - lb de, 0, 30 - jp SetExpectedAIDamage - -NidoranFFurySwipes_MultiplierEffect: ; 2c9c6 (b:49c6) - ld hl, 10 - call LoadTxRam3 - ldtx de, DamageCheckIfHeadsXDamageText - ld a, 3 - call TossCoinATimes_BankB - call ATimes10 - call SetDefiniteDamage - ret - -NidoranFCallForFamily_CheckDeckAndPlayArea: ; 2c9db (b:49db) - call CheckIfDeckIsEmpty - ret c ; return if no cards in deck - ld a, DUELVARS_NUMBER_OF_POKEMON_IN_PLAY_AREA - call GetTurnDuelistVariable - ldtx hl, NoSpaceOnTheBenchText - cp MAX_PLAY_AREA_POKEMON - ccf - ret - -NidoranFCallForFamily_PlayerSelectEffect: ; 2c9eb (b:49eb) - ld a, $ff - ldh [hTemp_ffa0], a - - call CreateDeckCardList - ldtx hl, ChooseNidoranFromDeckText - ldtx bc, NidoranMNidoranFText - lb de, SEARCHEFFECT_NIDORAN, $00 - call LookForCardsInDeck - ret c - -; draw Deck list interface and print text - bank1call Func_5591 - ldtx hl, ChooseNidoranText - ldtx de, DuelistDeckText - bank1call SetCardListHeaderText - -.loop - bank1call DisplayCardList - jr c, .pressed_b - call GetCardIDFromDeckIndex - ld bc, NIDORANF - call CompareDEtoBC - jr z, .selected_nidoran - ld bc, NIDORANM - call CompareDEtoBC - jr nz, .loop ; .play_sfx would be more appropriate here - -.selected_nidoran - ldh a, [hTempCardIndex_ff98] - ldh [hTemp_ffa0], a - or a - ret - -.play_sfx - ; play SFX and loop back - call Func_3794 - jr .loop - -.pressed_b -; figure if Player can exit the screen without selecting, -; that is, if the Deck has no NidoranF or NidoranM card. - ld a, DUELVARS_CARD_LOCATIONS - call GetTurnDuelistVariable -.loop_b_press - ld a, [hl] - cp CARD_LOCATION_DECK - jr nz, .next - ld a, l - call GetCardIDFromDeckIndex - ld bc, NIDORANF - call CompareDEtoBC - jr z, .play_sfx ; found, go back to top loop - ld bc, NIDORANM - jr z, .play_sfx ; found, go back to top loop -.next - inc l - ld a, l - cp DECK_SIZE - jr c, .loop_b_press - -; no Nidoran in Deck, can safely exit screen - ld a, $ff - ldh [hTemp_ffa0], a - or a - ret - -NidoranFCallForFamily_AISelectEffect: ; 2ca55 (b:4a55) - call CreateDeckCardList - ld hl, wDuelTempList -.loop_deck - ld a, [hli] - ldh [hTemp_ffa0], a - cp $ff - ret z ; none found - call GetCardIDFromDeckIndex - ld a, e - cp NIDORANF - jr z, .found - cp NIDORANM - jr nz, .loop_deck -.found - ret - -NidoranFCallForFamily_PutInPlayAreaEffect: ; 2ca6e (b:4a6e) - ldh a, [hTemp_ffa0] - cp $ff - jr z, .shuffle - call SearchCardInDeckAndAddToHand - call AddCardToHand - call PutHandPokemonCardInPlayArea - call IsPlayerTurn - jr c, .shuffle - ; display card on screen - ldh a, [hTemp_ffa0] - ldtx hl, PlacedOnTheBenchText - bank1call DisplayCardDetailScreen -.shuffle - call Func_2c0bd - ret - -HornHazard_AIEffect: ; 2ca8e (b:4a8e) - ld a, 30 / 2 - lb de, 0, 30 - jp SetExpectedAIDamage - -HornHazard_NoDamage50PercentEffect: ; 2ca96 (b:4a96) - ldtx de, DamageCheckIfTailsNoDamageText - call TossCoin_BankB - jr c, .heads - xor a - call SetDefiniteDamage - call SetWasUnsuccessful - ret -.heads - ld a, ATK_ANIM_HIT - ld [wLoadedAttackAnimation], a - ret - -NidorinaSupersonicEffect: ; 2caac (b:4aac) - call Confusion50PercentEffect - call nc, SetNoEffectFromStatus - ret - -NidorinaDoubleKick_AIEffect: ; 2cab3 (b:4ab3) - ld a, 60 / 2 - lb de, 0, 60 - jp SetExpectedAIDamage - -NidorinaDoubleKick_MultiplierEffect: ; 2cabb (b:4abb) - ld hl, 30 - call LoadTxRam3 - ldtx de, DamageCheckIfHeadsXDamageText - ld a, 2 - call TossCoinATimes_BankB - ld e, a - add a - add e - call ATimes10 - call SetDefiniteDamage - ret - -NidorinoDoubleKick_AIEffect: ; 2cad3 (b:4ad3) - ld a, 60 / 2 - lb de, 0, 60 - jp SetExpectedAIDamage - -NidorinoDoubleKick_MultiplierEffect: ; 2cabb (b:4abb) - ld hl, 30 - call LoadTxRam3 - ldtx de, DamageCheckIfHeadsXDamageText - ld a, 2 - call TossCoinATimes_BankB - ld e, a - add a - add e - call ATimes10 - call SetDefiniteDamage - ret - -ButterfreeWhirlwind_CheckBench: ; 2caf3 (b:4af3) - ld a, DUELVARS_NUMBER_OF_POKEMON_IN_PLAY_AREA - call GetNonTurnDuelistVariable - cp 2 - jr nc, .has_bench - ; no bench, do not do effect - ld a, $ff - ldh [hTemp_ffa0], a - ret -.has_bench - call DuelistSelectForcedSwitch - ldh a, [hTempPlayAreaLocation_ff9d] - ldh [hTemp_ffa0], a - ret - -ButterfreeWhirlwind_SwitchEffect: ; 2cb09 (b:4b09) - ldh a, [hTemp_ffa0] - call HandleSwitchDefendingPokemonEffect - ret - -ButterfreeMegaDrainEffect: ; 2cb0f (b:4b0f) - ld hl, wDealtDamage - ld a, [hli] - ld h, [hl] - ld l, a - srl h - rr l - bit 0, l - jr z, .rounded - ; round up to nearest 10 - ld de, 10 / 2 - add hl, de -.rounded - ld e, l - ld d, h - call ApplyAndAnimateHPRecovery - ret - -WeedlePoisonSting_AIEffect: ; 2cb27 (b:4b27) - ld a, 5 - lb de, 0, 10 - jp UpdateExpectedAIDamage_AccountForPoison - -IvysaurPoisonPowder_AIEffect: ; 2cb2f (b:4b2f) - ld a, 10 - lb de, 10, 10 - jp UpdateExpectedAIDamage_AccountForPoison - -BulbasaurLeechSeedEffect: ; 2cb37 (b:4b37) - ld hl, wDealtDamage - ld a, [hli] - or [hl] - ret z ; return if no damage dealt - lb de, 0, 10 - call ApplyAndAnimateHPRecovery - ret - -; returns carry if no Grass Energy in Play Area -EnergyTrans_CheckPlayArea: ; 2cb44 (b:4b44) - ldh a, [hTempPlayAreaLocation_ff9d] - ldh [hTemp_ffa0], a - ldh a, [hTempPlayAreaLocation_ff9d] - call CheckCannotUseDueToStatus_OnlyToxicGasIfANon0 - ret c ; cannot use Pkmn Power - -; search in Play Area for at least 1 Grass Energy type - ld a, DUELVARS_CARD_LOCATIONS - call GetTurnDuelistVariable -.loop_deck - ld a, [hl] - and CARD_LOCATION_PLAY_AREA - jr z, .next - push hl - ld a, l - call GetCardIDFromDeckIndex - call GetCardType - pop hl - cp TYPE_ENERGY_GRASS - ret z -.next - inc l - ld a, l - cp DECK_SIZE - jr c, .loop_deck - -; none found - ldtx hl, NoGrassEnergyText - scf - ret - -EnergyTrans_PrintProcedure: ; 2cb6f (b:4b6f) - ldtx hl, ProcedureForEnergyTransferText - bank1call DrawWholeScreenTextBox - or a - ret - -EnergyTrans_TransferEffect: ; 2cb77 (b:4b77) - ld a, DUELVARS_DUELIST_TYPE - call GetTurnDuelistVariable - cp DUELIST_TYPE_PLAYER - jr z, .player -; not player - bank1call Func_61a1 - bank1call PrintPlayAreaCardList_EnableLCD - ret - -.player - xor a - ldh [hCurSelectionItem], a - bank1call Func_61a1 - -.draw_play_area - bank1call PrintPlayAreaCardList_EnableLCD - push af - ldh a, [hCurSelectionItem] - ld hl, PlayAreaSelectionMenuParameters - call InitializeMenuParameters - pop af - ld [wNumMenuItems], a - -; handle the action of taking a Grass Energy card -.loop_input_take - call DoFrame - call HandleMenuInput - jr nc, .loop_input_take - cp -1 ; b press? - ret z - -; a press - ldh [hAIPkmnPowerEffectParam], a - ldh [hCurSelectionItem], a - call CheckIfCardHasGrassEnergyAttached - jr c, .play_sfx ; no Grass attached - - ldh [hAIEnergyTransEnergyCard], a - ldh a, [hAIEnergyTransEnergyCard] ; useless - ; temporarily take card away to draw Play Area - call AddCardToHand - bank1call PrintPlayAreaCardList_EnableLCD - ldh a, [hAIPkmnPowerEffectParam] - ld e, a - ldh a, [hAIEnergyTransEnergyCard] - ; give card back - call PutHandCardInPlayArea - - ; draw Grass symbol near cursor - ldh a, [hAIPkmnPowerEffectParam] - ld b, SYM_GRASS - call DrawSymbolOnPlayAreaCursor - -; handle the action of placing a Grass Energy card -.loop_input_put - call DoFrame - call HandleMenuInput - jr nc, .loop_input_put - cp -1 ; b press? - jr z, .remove_symbol - -; a press - ldh [hCurSelectionItem], a - ldh [hAIEnergyTransPlayAreaLocation], a - ld a, OPPACTION_6B15 - call SetOppAction_SerialSendDuelData - ldh a, [hAIEnergyTransPlayAreaLocation] - ld e, a - ldh a, [hAIEnergyTransEnergyCard] - ; give card being held to this Pokemon - call AddCardToHand - call PutHandCardInPlayArea - -.remove_symbol - ldh a, [hAIPkmnPowerEffectParam] - ld b, SYM_SPACE - call DrawSymbolOnPlayAreaCursor - call EraseCursor - jr .draw_play_area - -.play_sfx - call Func_3794 - jr .loop_input_take - -EnergyTrans_AIEffect: ; 2cbfb (b:4bfb) - ldh a, [hAIEnergyTransPlayAreaLocation] - ld e, a - ldh a, [hAIEnergyTransEnergyCard] - call AddCardToHand - call PutHandCardInPlayArea - bank1call PrintPlayAreaCardList_EnableLCD - ret - -; returns carry if no Grass Energy cards -; attached to card in Play Area location of a. -; input: -; a = PLAY_AREA_* of location to check -CheckIfCardHasGrassEnergyAttached: ; 2cc0a (b:4c0a) - or CARD_LOCATION_PLAY_AREA - ld e, a - - ld a, DUELVARS_CARD_LOCATIONS - call GetTurnDuelistVariable -.loop - ld a, [hl] - cp e - jr nz, .next - push de - push hl - ld a, l - call GetCardIDFromDeckIndex - call GetCardType - pop hl - pop de - cp TYPE_ENERGY_GRASS - jr z, .no_carry -.next - inc l - ld a, l - cp DECK_SIZE - jr c, .loop - scf - ret -.no_carry - ld a, l - or a - ret - -GrimerMinimizeEffect: ; 2cc30 (b:4c30) - ld a, SUBSTATUS1_REDUCE_BY_20 - call ApplySubstatus1ToDefendingCard - ret - -ToxicGasEffect: ; 2cc36 (b:4c36) - scf - ret - -Sludge_AIEffect: ; 2cc38 (b:4c38) - ld a, 5 - lb de, 0, 10 - jp UpdateExpectedAIDamage_AccountForPoison - -; returns carry if no cards in Deck -; or if Play Area is full already. -BellsproutCallForFamily_CheckDeckAndPlayArea: ; 2cc40 (b:4c40) - call CheckIfDeckIsEmpty - ret c ; return if no cards in deck - ld a, DUELVARS_NUMBER_OF_POKEMON_IN_PLAY_AREA - call GetTurnDuelistVariable - ldtx hl, NoSpaceOnTheBenchText - cp MAX_PLAY_AREA_POKEMON - ccf - ret - -BellsproutCallForFamily_PlayerSelectEffect: ; 2cc50 (b:4c50) - ld a, $ff - ldh [hTemp_ffa0], a - - call CreateDeckCardList - ldtx hl, ChooseABellsproutFromDeckText - ldtx bc, BellsproutText - lb de, SEARCHEFFECT_CARD_ID, BELLSPROUT - call LookForCardsInDeck - ret c - -; draw Deck list interface and print text - bank1call Func_5591 - ldtx hl, ChooseABellsproutText - ldtx de, DuelistDeckText - bank1call SetCardListHeaderText - -.loop - bank1call DisplayCardList - jr c, .pressed_b - call GetCardIDFromDeckIndex - ld bc, BELLSPROUT - call CompareDEtoBC - jr nz, .play_sfx - -; Bellsprout was selected - ldh a, [hTempCardIndex_ff98] - ldh [hTemp_ffa0], a - or a - ret - -.play_sfx - ; play SFX and loop back - call Func_3794 - jr .loop - -.pressed_b -; figure if Player can exit the screen without selecting, -; that is, if the Deck has no Bellsprout card. - ld a, DUELVARS_CARD_LOCATIONS - call GetTurnDuelistVariable -.loop_b_press - ld a, [hl] - cp CARD_LOCATION_DECK - jr nz, .next - ld a, l - call GetCardIDFromDeckIndex - ld bc, BELLSPROUT - call CompareDEtoBC - jr z, .play_sfx ; found Bellsprout, go back to top loop -.next - inc l - ld a, l - cp DECK_SIZE - jr c, .loop_b_press - -; no Bellsprout in Deck, can safely exit screen - ld a, $ff - ldh [hTemp_ffa0], a - or a - ret - -BellsproutCallForFamily_AISelectEffect: ; 2ccad (b:4cad) - call CreateDeckCardList - ld hl, wDuelTempList -.loop_deck - ld a, [hli] - ldh [hTemp_ffa0], a - cp $ff - ret z ; no Bellsprout - call GetCardIDFromDeckIndex - ld a, e - cp BELLSPROUT - jr nz, .loop_deck - ret ; Bellsprout found - -BellsproutCallForFamily_PutInPlayAreaEffect: ; 2ccc2 (b:4cc2) - ldh a, [hTemp_ffa0] - cp $ff - jr z, .shuffle - call SearchCardInDeckAndAddToHand - call AddCardToHand - call PutHandPokemonCardInPlayArea - call IsPlayerTurn - jr c, .shuffle - ldh a, [hTemp_ffa0] - ldtx hl, PlacedOnTheBenchText - bank1call DisplayCardDetailScreen -.shuffle - call Func_2c0bd - ret - -WeezingSmog_AIEffect: ; 2cce2 (b:4ce2) - ld a, 5 - lb de, 0, 10 - jp UpdateExpectedAIDamage_AccountForPoison - -WeezingSelfdestructEffect: ; 2ccea (b:4cea) - ld a, 60 - call DealRecoilDamageToSelf - ld a, $01 - ld [wIsDamageToSelf], a - ld a, 10 - call DealDamageToAllBenchedPokemon - call SwapTurn - xor a - ld [wIsDamageToSelf], a - ld a, 10 - call DealDamageToAllBenchedPokemon - call SwapTurn - ret - -Shift_OncePerTurnCheck: ; 2cd09 (b:4d09) - ldh a, [hTempPlayAreaLocation_ff9d] - ldh [hTemp_ffa0], a - add DUELVARS_ARENA_CARD_FLAGS - call GetTurnDuelistVariable - and USED_PKMN_POWER_THIS_TURN - jr nz, .already_used - ldh a, [hTempPlayAreaLocation_ff9d] - call CheckCannotUseDueToStatus_OnlyToxicGasIfANon0 - ret -.already_used - ldtx hl, OnlyOncePerTurnText - scf - ret - -Shift_PlayerSelectEffect: ; 2cd21 (b:4d21) - ldtx hl, ChoosePokemonWishToColorChangeText - ldh a, [hTemp_ffa0] - or $80 - call HandleColorChangeScreen - ldh [hAIPkmnPowerEffectParam], a - ret c ; cancelled - -; check whether the color selected is valid - ; look in Turn Duelist's Play Area - call .CheckColorInPlayArea - ret nc - ; look in NonTurn Duelist's Play Area - call SwapTurn - call .CheckColorInPlayArea - call SwapTurn - ret nc - ; not found in either Duelist's Play Area - ldtx hl, UnableToSelectText - call DrawWideTextBox_WaitForInput - jr Shift_PlayerSelectEffect ; loop back to start - -; checks in input color in a exists in Turn Duelist's Play Area -; returns carry if not found. -.CheckColorInPlayArea: ; 2cd44 (b:4d44) - ld a, DUELVARS_NUMBER_OF_POKEMON_IN_PLAY_AREA - call GetTurnDuelistVariable - ld c, a - ld b, PLAY_AREA_ARENA -.loop_play_area - push bc - ld a, b - call GetPlayAreaCardColor - pop bc - ld hl, hAIPkmnPowerEffectParam - cp [hl] - ret z ; found - inc b - dec c - jr nz, .loop_play_area - ; not found - scf - ret - -Shift_ChangeColorEffect: ; 2cd5d (b:4d5d) - ldh a, [hTemp_ffa0] - add DUELVARS_ARENA_CARD - call GetTurnDuelistVariable - call LoadCardDataToBuffer1_FromDeckIndex - - ldh a, [hTemp_ffa0] - add DUELVARS_ARENA_CARD_FLAGS - call GetTurnDuelistVariable - set USED_PKMN_POWER_THIS_TURN_F, [hl] - - ldh a, [hTemp_ffa0] - add DUELVARS_ARENA_CARD_CHANGED_TYPE - ld l, a - ldh a, [hAIPkmnPowerEffectParam] - or HAS_CHANGED_COLOR - ld [hl], a - call LoadCardNameAndInputColor - ldtx hl, ChangedTheColorOfText - call DrawWideTextBox_WaitForInput - ret - -VenomPowder_AIEffect: ; 2cd84 (b:4d84) - ld a, 5 - lb de, 0, 10 - jp UpdateExpectedAIDamage - -VenomPowder_PoisonConfusion50PercentEffect: ; 2cd8c (b:4d8c) - ldtx de, VenomPowderCheckText - call TossCoin_BankB - ret nc ; return if tails - -; heads - call PoisonEffect - call ConfusionEffect - ret c - ld a, CONFUSED | POISONED - ld [wNoEffectFromWhichStatus], a - ret - -TangelaPoisonPowder_AIEffect: ; 2cda0 (b:4da0) - ld a, 5 - lb de, 0, 10 - jp UpdateExpectedAIDamage_AccountForPoison - -Heal_OncePerTurnCheck: ; 2cda8 (b:4da8) - ldh a, [hTempPlayAreaLocation_ff9d] - ldh [hTemp_ffa0], a - add DUELVARS_ARENA_CARD_FLAGS - call GetTurnDuelistVariable - and USED_PKMN_POWER_THIS_TURN - jr nz, .already_used - - call CheckIfPlayAreaHasAnyDamage - ldtx hl, NoPokemonWithDamageCountersText - ret c ; no damage counters to heal - - ldh a, [hTempPlayAreaLocation_ff9d] - call CheckCannotUseDueToStatus_OnlyToxicGasIfANon0 - ret - -.already_used - ldtx hl, OnlyOncePerTurnText - scf - ret - -Heal_RemoveDamageEffect: ; 2cdc7 (b:4dc7) - ldtx de, IfHeadsHealIsSuccessfulText - call TossCoin_BankB - ldh [hAIPkmnPowerEffectParam], a - jr nc, .done - - ld a, DUELVARS_DUELIST_TYPE - call GetTurnDuelistVariable - cp DUELIST_TYPE_LINK_OPP - jr z, .link_opp - and DUELIST_TYPE_AI_OPP - jr nz, .done - -; player - ldtx hl, ChoosePkmnToRemoveDamageCounterText - call DrawWideTextBox_WaitForInput - bank1call HasAlivePokemonInPlayArea -.loop_input - bank1call OpenPlayAreaScreenForSelection - jr c, .loop_input - ldh a, [hTempPlayAreaLocation_ff9d] - ldh [hPlayAreaEffectTarget], a - ld e, a - call GetCardDamageAndMaxHP - or a - jr z, .loop_input ; has no damage counters - ldh a, [hTempPlayAreaLocation_ff9d] - call SerialSend8Bytes - jr .done - -.link_opp - call SerialRecv8Bytes - ldh [hPlayAreaEffectTarget], a - ; fallthrough - -.done -; flag Pkmn Power as being used regardless of coin outcome - ldh a, [hTemp_ffa0] - add DUELVARS_ARENA_CARD_FLAGS - call GetTurnDuelistVariable - set USED_PKMN_POWER_THIS_TURN_F, [hl] - ldh a, [hAIPkmnPowerEffectParam] - or a - ret z ; return if coin was tails - - ldh a, [hPlayAreaEffectTarget] - add DUELVARS_ARENA_CARD_HP - call GetTurnDuelistVariable - add 10 ; remove 1 damage counter - ld [hl], a - ldh a, [hPlayAreaEffectTarget] - call Func_2c10b - call ExchangeRNG - ret - -PetalDance_AIEffect: ; 2ce23 (b:4e23) - ld a, 120 / 2 - lb de, 0, 120 - jp SetExpectedAIDamage - -PetalDance_MultiplierEffect: ; 2ce2b (b:4e2b) - ld hl, 40 - call LoadTxRam3 - ldtx de, DamageCheckIfHeadsXDamageText - ld a, 3 - call TossCoinATimes_BankB - add a - add a - call ATimes10 - ; a = 4 * 10 * heads - call SetDefiniteDamage - call SwapTurn - call ConfusionEffect - call SwapTurn - ret - -PoisonWhip_AIEffect: ; 2ce4b (b:4e4b) - ld a, 10 - lb de, 10, 10 - jp UpdateExpectedAIDamage_AccountForPoison - -SolarPower_CheckUse: ; 2ce53 (b:4e53) - ldh a, [hTempPlayAreaLocation_ff9d] - ldh [hTemp_ffa0], a - add DUELVARS_ARENA_CARD_FLAGS - call GetTurnDuelistVariable - and USED_PKMN_POWER_THIS_TURN - jr nz, .already_used - - ldh a, [hTempPlayAreaLocation_ff9d] - call CheckCannotUseDueToStatus_OnlyToxicGasIfANon0 - ret c ; can't use PKMN due to status or Toxic Gas - -; return carry if none of the Arena cards have status conditions - ld a, DUELVARS_ARENA_CARD_STATUS - call GetTurnDuelistVariable - or a - jr nz, .has_status - ld a, DUELVARS_ARENA_CARD_STATUS - call GetNonTurnDuelistVariable - or a - jr z, .no_status -.has_status - or a - ret -.already_used - ldtx hl, OnlyOncePerTurnText - scf - ret -.no_status - ldtx hl, NotAffectedByPoisonSleepParalysisOrConfusionText - scf - ret - -SolarPower_RemoveStatusEffect: ; 2ce82 (b:4e82) - ld a, ATK_ANIM_HEAL_BOTH_SIDES - ld [wLoadedAttackAnimation], a - bank1call Func_7415 - ldh a, [hTempPlayAreaLocation_ff9d] - ld b, a - ld c, $00 - ldh a, [hWhoseTurn] - ld h, a - bank1call PlayAttackAnimation - bank1call WaitAttackAnimation - - ldh a, [hTemp_ffa0] - add DUELVARS_ARENA_CARD_FLAGS - call GetTurnDuelistVariable - set USED_PKMN_POWER_THIS_TURN_F, [hl] - ld l, DUELVARS_ARENA_CARD_STATUS - ld [hl], NO_STATUS - - ld a, DUELVARS_ARENA_CARD_STATUS - call GetNonTurnDuelistVariable - ld [hl], NO_STATUS - bank1call DrawDuelHUDs - ret - -VenusaurMegaDrainEffect: ; 2ceb0 (b:4eb0) - ld hl, wDealtDamage - ld a, [hli] - ld h, [hl] - ld l, a - srl h - rr l - bit 0, l - jr z, .rounded - ; round up to nearest 10 - ld de, 10 / 2 - add hl, de -.rounded - ld e, l - ld d, h - call ApplyAndAnimateHPRecovery - ret - -; applies the damage bonus for attacks that get bonus -; from extra Water energy cards. -; this bonus is always 10 more damage for each extra Water energy -; and is always capped at a maximum of 20 damage. -; input: -; b = number of Water energy cards needed for paying Energy Cost -; c = number of colorless energy cards needed for paying Energy Cost -ApplyExtraWaterEnergyDamageBonus: ; 2cec8 (b:4ec8) - ld a, [wMetronomeEnergyCost] - or a - jr z, .not_metronome - ld c, a ; amount of colorless needed for Metronome - ld b, 0 ; no Water energy needed for Metronome - -.not_metronome - push bc - ldh a, [hTempPlayAreaLocation_ff9d] - ld e, a - call GetPlayAreaCardAttachedEnergies - pop bc - - ld hl, wAttachedEnergies + WATER - ld a, c - or a - jr z, .check_bonus ; is Energy cost all water energy? - - ; it's not, so we need to remove the - ; Water energy cards from calculations - ; if they pay for colorless instead. - ld a, [wTotalAttachedEnergies] - cp [hl] - jr nz, .check_bonus ; skip if at least 1 non-Water energy attached - - ; Water is the only energy color attached - ld a, c - add b - ld b, a - ; b += c - -.check_bonus - ld a, [hl] - sub b - jr c, .skip_bonus ; is water energy < b? - jr z, .skip_bonus ; is water energy == b? - -; a holds number of water energy not payed for energy cost - cp 3 - jr c, .less_than_3 - ld a, 2 ; cap this to 2 for bonus effect -.less_than_3 - call ATimes10 - call AddToDamage ; add 10 * a to damage - -.skip_bonus - ld a, [wDamage] - ld [wAIMinDamage], a - ld [wAIMaxDamage], a - ret - -OmastarWaterGunEffect: ; 2cf05 (b:4f05) - lb bc, 1, 1 - jr ApplyExtraWaterEnergyDamageBonus - -OmastarSpikeCannon_AIEffect: ; 2cf0a (b:4f0a) - ld a, 60 / 2 - lb de, 0, 60 - jp SetExpectedAIDamage - -OmastarSpikeCannon_MultiplierEffect: ; 2cf12 (b:4f12) - ld hl, 30 - call LoadTxRam3 - ld a, 2 - ldtx de, DamageCheckIfHeadsXDamageText - call TossCoinATimes_BankB - ld e, a - add a - add e - call ATimes10 - call SetDefiniteDamage ; 3 * 10 * heads - ret - -ClairvoyanceEffect: ; 2cf2a (b:4f2a) - scf - ret - -OmanyteWaterGunEffect: ; 2cf2c (b:4f2c) - lb bc, 1, 0 - jp ApplyExtraWaterEnergyDamageBonus - -WartortleWithdrawEffect: ; 2cf32 (b:4f32) - ldtx de, IfHeadsNoDamageNextTurnText - call TossCoin_BankB - jp nc, SetWasUnsuccessful - ld a, ATK_ANIM_PROTECT - ld [wLoadedAttackAnimation], a - ld a, SUBSTATUS1_NO_DAMAGE_10 - call ApplySubstatus1ToDefendingCard - ret - -RainDanceEffect: ; 2cf46 (b:4f46) - scf - ret - -HydroPumpEffect: ; 2cf48 (b:4f48) - lb bc, 3, 0 - jp ApplyExtraWaterEnergyDamageBonus - -KinglerFlail_AIEffect: ; 2cf4e (b:4f4e) - call KinglerFlail_HPCheck - jp SetDefiniteAIDamage - -KinglerFlail_HPCheck: ; 2cf54 (b:4f54) - ld e, PLAY_AREA_ARENA - call GetCardDamageAndMaxHP - call SetDefiniteDamage - ret - -; returns carry if no cards in Deck -; or if Play Area is full already. -KrabbyCallForFamily_CheckDeckAndPlayArea: ; 2cf5d (b:4f5d) - call CheckIfDeckIsEmpty - ret c ; return if no cards in deck - ld a, DUELVARS_NUMBER_OF_POKEMON_IN_PLAY_AREA - call GetTurnDuelistVariable - ldtx hl, NoSpaceOnTheBenchText - cp MAX_PLAY_AREA_POKEMON - ccf - ret - -KrabbyCallForFamily_PlayerSelectEffect: ; 2cf6d (b:4f6d) - ld a, $ff - ldh [hTemp_ffa0], a - - call CreateDeckCardList - ldtx hl, ChooseAKrabbyFromDeckText - ldtx bc, KrabbyText - lb de, SEARCHEFFECT_CARD_ID, KRABBY - call LookForCardsInDeck - ret c - -; draw Deck list interface and print text - bank1call Func_5591 - ldtx hl, ChooseAKrabbyText - ldtx de, DuelistDeckText - bank1call SetCardListHeaderText - -.loop - bank1call DisplayCardList - jr c, .pressed_b - call GetCardIDFromDeckIndex - ld bc, KRABBY - call CompareDEtoBC - jr nz, .play_sfx - -; Krabby was selected - ldh a, [hTempCardIndex_ff98] - ldh [hTemp_ffa0], a - or a - ret - -.play_sfx - ; play SFX and loop back - call Func_3794 - jr .loop - -.pressed_b -; figure if Player can exit the screen without selecting, -; that is, if the Deck has no Krabby card. - ld a, DUELVARS_CARD_LOCATIONS - call GetTurnDuelistVariable -.loop_b_press - ld a, [hl] - cp CARD_LOCATION_DECK - jr nz, .next - ld a, l - call GetCardIDFromDeckIndex - ld bc, KRABBY - call CompareDEtoBC - jr z, .play_sfx ; found Krabby, go back to top loop -.next - inc l - ld a, l - cp DECK_SIZE - jr c, .loop_b_press - -; no Krabby in Deck, can safely exit screen - ld a, $ff - ldh [hTemp_ffa0], a - or a - ret - -KrabbyCallForFamily_AISelectEffect: ; 2cfdf (b:4fdf) - call CreateDeckCardList - ld hl, wDuelTempList -.loop_deck - ld a, [hli] - ldh [hTemp_ffa0], a - cp $ff - ret z ; no Krabby - call GetCardIDFromDeckIndex - ld a, e - cp KRABBY - jr nz, .loop_deck - ret ; Krabby found - -KrabbyCallForFamily_PutInPlayAreaEffect: ; 2cfca (b:4fca) - ldh a, [hTemp_ffa0] - cp $ff - jr z, .shuffle - call SearchCardInDeckAndAddToHand - call AddCardToHand - call PutHandPokemonCardInPlayArea - call IsPlayerTurn - jr c, .shuffle - ldh a, [hTemp_ffa0] - ldtx hl, PlacedOnTheBenchText - bank1call DisplayCardDetailScreen -.shuffle - call Func_2c0bd - ret - -MagikarpFlail_AIEffect: ; 2cfff (b:4fff) - call MagikarpFlail_HPCheck - jp SetDefiniteAIDamage - -MagikarpFlail_HPCheck: ; 2d005 (b:5005) - ld e, PLAY_AREA_ARENA - call GetCardDamageAndMaxHP - call SetDefiniteDamage - ret - -HeadacheEffect: ; 2d00e (b:500e) - ld a, DUELVARS_ARENA_CARD_SUBSTATUS3 - call GetNonTurnDuelistVariable - set SUBSTATUS3_HEADACHE, [hl] - ret - -PsyduckFurySwipes_AIEffect: ; 2d016 (b:5016) - ld a, 30 / 2 - lb de, 0, 30 - jp SetExpectedAIDamage - -PsyduckFurySwipes_MultiplierEffect: ; 2d01e (b:501e) - ld hl, 10 - call LoadTxRam3 - ldtx de, DamageCheckIfHeadsXDamageText - ld a, 3 - call TossCoinATimes_BankB - call ATimes10 - call SetDefiniteDamage - ret - -GolduckHyperBeam_PlayerSelectEffect: ; 2d033 (b:5033) - call SwapTurn - ld e, PLAY_AREA_ARENA - call GetPlayAreaCardAttachedEnergies - ld a, [wTotalAttachedEnergies] - or a - jr z, .no_energy - -; draw Energy Card list screen - ldtx hl, ChooseDiscardEnergyCardFromOpponentText - call DrawWideTextBox_WaitForInput - xor a ; PLAY_AREA_ARENA - call CreateArenaOrBenchEnergyCardList - xor a ; PLAY_AREA_ARENA - bank1call DisplayEnergyDiscardScreen - -.loop_input - bank1call HandleEnergyDiscardMenuInput - jr c, .loop_input - - call SwapTurn - ldh a, [hTempCardIndex_ff98] - ldh [hTemp_ffa0], a ; store selected card to discard - ret - -.no_energy - call SwapTurn - ld a, $ff - ldh [hTemp_ffa0], a - or a - ret - -GolduckHyperBeam_AISelectEffect: ; 2d065 (b:5065) - call AIPickEnergyCardToDiscardFromDefendingPokemon - ldh [hTemp_ffa0], a - ret - -GolduckHyperBeam_DiscardEffect: ; 2d06b (b:506b) - call HandleNoDamageOrEffect - ret c ; return if attack had no effect - - ; check if energy card was chosen to discard - ldh a, [hTemp_ffa0] - cp $ff - ret z ; return if none selected - - ; discard Defending card's energy - call SwapTurn - call PutCardInDiscardPile - ld a, DUELVARS_ARENA_CARD_LAST_TURN_EFFECT - call GetTurnDuelistVariable - ld [hl], LAST_TURN_EFFECT_DISCARD_ENERGY - call SwapTurn - ret - -SeadraWaterGunEffect: ; 2d085 (b:5085) - lb bc, 1, 1 - jp ApplyExtraWaterEnergyDamageBonus - -SeadraAgilityEffect: ; 2d08b (b:508b) - ldtx de, IfHeadsDoNotReceiveDamageOrEffectText - call TossCoin_BankB - ret nc ; return if tails - ld a, ATK_ANIM_AGILITY_PROTECT - ld [wLoadedAttackAnimation], a - ld a, SUBSTATUS1_AGILITY - call ApplySubstatus1ToDefendingCard - ret - -ShellderSupersonicEffect: ; 2d09d (b:509d) - call Confusion50PercentEffect - call nc, SetNoEffectFromStatus - ret - -HideInShellEffect: ; 2d0a4 (b:50a4) - ldtx de, IfHeadsNoDamageNextTurnText - call TossCoin_BankB - jp nc, SetWasUnsuccessful - ld a, ATK_ANIM_PROTECT - ld [wLoadedAttackAnimation], a - ld a, SUBSTATUS1_NO_DAMAGE_11 - call ApplySubstatus1ToDefendingCard - ret - -VaporeonQuickAttack_AIEffect: ; 2d0b8 (b:50b8) - ld a, (10 + 30) / 2 - lb de, 10, 30 - jp SetExpectedAIDamage - -VaporeonQuickAttack_DamageBoostEffect: ; 2d0c0 (b:50c0) - ld hl, 20 - call LoadTxRam3 - ldtx de, DamageCheckIfHeadsPlusDamageText - call TossCoin_BankB - ret nc ; return if tails - ld a, 20 - call AddToDamage - ret - -VaporeonWaterGunEffect: ; 2d0d3 (b:50d3) - lb bc, 2, 1 - jp ApplyExtraWaterEnergyDamageBonus - -; returns carry if Arena card has no Water Energy attached -; or if it doesn't have any damage counters. -StarmieRecover_CheckEnergyHP: ; 2d0d9 (b:50d9) - ld e, PLAY_AREA_ARENA - call GetPlayAreaCardAttachedEnergies - ld a, [wAttachedEnergies + WATER] - ldtx hl, NotEnoughWaterEnergyText - cp 1 - ret c ; return if not enough energy - call GetCardDamageAndMaxHP - ldtx hl, NoDamageCountersText - cp 10 - ret ; return carry if no damage - -StarmieRecover_PlayerSelectEffect: ; 2d0f0 (b:50f0) - ld a, TYPE_ENERGY_WATER - call CreateListOfEnergyAttachedToArena - xor a ; PLAY_AREA_ARENA - bank1call DisplayEnergyDiscardScreen -.loop_input - bank1call HandleEnergyDiscardMenuInput - jr c, .loop_input - ldh a, [hTempCardIndex_ff98] - ldh [hTemp_ffa0], a ; store card chosen - ret - -StarmieRecover_AISelectEffect: ; 2d103 (b:5103) - ld a, TYPE_ENERGY_WATER - call CreateListOfEnergyAttachedToArena - ld a, [wDuelTempList] ; pick first card - ldh [hTemp_ffa0], a - ret - -StarmieRecover_DiscardEffect: ; 2d10e (b:510e) - ldh a, [hTemp_ffa0] - call PutCardInDiscardPile - ret - -StarmieRecover_HealEffect: ; 2d114 (b:5114) - ld e, PLAY_AREA_ARENA - call GetCardDamageAndMaxHP - ld e, a ; all damage for recovery - ld d, 0 - call ApplyAndAnimateHPRecovery - ret - -SquirtleWithdrawEffect: ; 2d120 (b:5120) - ldtx de, IfHeadsNoDamageNextTurnText - call TossCoin_BankB - jp nc, SetWasUnsuccessful - ld a, ATK_ANIM_PROTECT - ld [wLoadedAttackAnimation], a - ld a, SUBSTATUS1_NO_DAMAGE_10 - call ApplySubstatus1ToDefendingCard - ret - -HorseaSmokescreenEffect: ; 2d134 (b:5134) - ld a, SUBSTATUS2_SMOKESCREEN - call ApplySubstatus2ToDefendingCard - ret - -TentacruelSupersonicEffect: ; 2d13a (b:513a) - call Confusion50PercentEffect - call nc, SetNoEffectFromStatus - ret - -JellyfishSting_AIEffect: ; 2d141 (b:5141) - ld a, 10 - lb de, 10, 10 - jp UpdateExpectedAIDamage_AccountForPoison - -; returns carry if Defending Pokemon has no attacks -PoliwhirlAmnesia_CheckAttacks: ; 2d149 (b:5149) - call SwapTurn - ld a, DUELVARS_ARENA_CARD - call GetTurnDuelistVariable - call LoadCardDataToBuffer2_FromDeckIndex - ld a, [wLoadedCard2Atk1Category] - cp POKEMON_POWER - jr nz, .has_attack - ld hl, wLoadedCard2Atk2Name - ld a, [hli] - or [hl] - jr nz, .has_attack -; has no attack - call SwapTurn - ldtx hl, NoAttackMayBeChoosenText - scf - ret -.has_attack - call SwapTurn - or a - ret - -PoliwhirlAmnesia_PlayerSelectEffect: ; 2d16f (b:516f) - call PlayerPickAttackForAmnesia - ret - -PoliwhirlAmnesia_AISelectEffect: ; 2d173 (b:5173) - call AIPickAttackForAmnesia - ldh [hTemp_ffa0], a - ret - -PoliwhirlAmnesia_DisableEffect: ; 2d179 (b:5179) - call ApplyAmnesiaToAttack - ret - -PlayerPickAttackForAmnesia: ; 2d17d (b:517d) - ldtx hl, ChooseAttackOpponentWillNotBeAbleToUseText - call DrawWideTextBox_WaitForInput - call HandleDefendingPokemonAttackSelection - ld a, e - ldh [hTemp_ffa0], a - ret - -; applies the Amnesia effect on the defending Pokemon, -; for the attack index in hTemp_ffa0. -ApplyAmnesiaToAttack: ; 2d18a (b:518a) - ld a, SUBSTATUS2_AMNESIA - call ApplySubstatus2ToDefendingCard - ld a, [wNoDamageOrEffect] - or a - ret nz ; no effect - -; set selected attack as disabled - ld a, DUELVARS_ARENA_CARD_DISABLED_ATTACK_INDEX - call GetNonTurnDuelistVariable - ldh a, [hTemp_ffa0] - ld [hl], a - - ld l, DUELVARS_ARENA_CARD_LAST_TURN_EFFECT - ld [hl], LAST_TURN_EFFECT_AMNESIA - - call IsPlayerTurn - ret c ; return if Player - -; the rest of the routine if for Opponent -; to announce which attack was used for Amnesia. - call SwapTurn - ld a, DUELVARS_ARENA_CARD - call GetTurnDuelistVariable - ld d, a - ldh a, [hTemp_ffa0] - ld e, a - call GetAttackName - call LoadTxRam2 - ldtx hl, WasChosenForTheEffectOfAmnesiaText - call DrawWideTextBox_WaitForInput - call SwapTurn - ret - -PoliwhirlDoubleslap_AIEffect: ; 2d1c0 (b:51c0) - ld a, 60 / 2 - lb de, 0, 60 - jp SetExpectedAIDamage - -PoliwhirlDoubleslap_MultiplierEffect: ; 2d1c8 (b:51c8) - ld hl, 30 - call LoadTxRam3 - ldtx de, DamageCheckIfHeadsXDamageText - ld a, 2 - call TossCoinATimes_BankB - ld e, a - add a - add e - call ATimes10 - call SetDefiniteDamage - ret - -PoliwrathWaterGunEffect: ; 2d1e0 (b:51e0) - lb bc, 2, 1 - jp ApplyExtraWaterEnergyDamageBonus - -Whirlpool_PlayerSelectEffect: ; 2d1e6 (b:51e6) - call SwapTurn - xor a ; PLAY_AREA_ARENA - call CreateArenaOrBenchEnergyCardList - jr c, .no_energy - - ldtx hl, ChooseDiscardEnergyCardFromOpponentText - call DrawWideTextBox_WaitForInput - xor a ; PLAY_AREA_ARENA - bank1call DisplayEnergyDiscardScreen -.loop_input - bank1call HandleEnergyDiscardMenuInput - jr c, .loop_input - - call SwapTurn - ldh a, [hTempCardIndex_ff98] - ldh [hTemp_ffa0], a ; store selected card to discard - ret - -.no_energy - call SwapTurn - ld a, $ff - ldh [hTemp_ffa0], a - ret - -Whirlpool_AISelectEffect: ; 2d20e (b:520e) - call AIPickEnergyCardToDiscardFromDefendingPokemon - ldh [hTemp_ffa0], a - ret - -Whirlpool_DiscardEffect: ; 2d214 (b:5214) - call HandleNoDamageOrEffect - ret c ; return if attack had no effect - ldh a, [hTemp_ffa0] - cp $ff - ret z ; return if none selected - - ; discard Defending card's energy - ; this doesn't update DUELVARS_ARENA_CARD_LAST_TURN_EFFECT - call SwapTurn - call PutCardInDiscardPile - ; ld a, DUELVARS_ARENA_CARD_LAST_TURN_EFFECT - ; call GetTurnDuelistVariable - ; ld [hl], LAST_TURN_EFFECT_DISCARD_ENERGY - call SwapTurn - ret - -PoliwagWaterGunEffect: ; 2d227 (b:5227) - lb bc, 1, 0 - jp ApplyExtraWaterEnergyDamageBonus - -ClampEffect: ; 2d22d (b:522d) - ld a, ATK_ANIM_HIT_EFFECT - ld [wLoadedAttackAnimation], a - ldtx de, SuccessCheckIfHeadsAttackIsSuccessfulText - call TossCoin_BankB - jp c, ParalysisEffect -; unsuccessful - xor a ; ATK_ANIM_NONE - ld [wLoadedAttackAnimation], a - call SetDefiniteDamage - call SetWasUnsuccessful - ret - -CloysterSpikeCannon_AIEffect: ; 2d246 (b:5246) - ld a, 60 / 2 - lb de, 0, 60 - jp SetExpectedAIDamage - -CloysterSpikeCannon_MultiplierEffect: ; 2d24e (b:524e) - ld hl, 30 - call LoadTxRam3 - ldtx de, DamageCheckIfHeadsXDamageText - ld a, 2 - call TossCoinATimes_BankB - ld e, a - add a - add e - call ATimes10 - call SetDefiniteDamage - ret - -Blizzard_BenchDamage50PercentEffect: ; 2d266 (b:5266) - ldtx de, DamageToOppBenchIfHeadsDamageToYoursIfTailsText - call TossCoin_BankB - ldh [hTemp_ffa0], a ; store coin result - ret - -Blizzard_BenchDamageEffect: ; 2d26f (b:526f) - ldh a, [hTemp_ffa0] - or a - jr nz, .opp_bench - -; own bench - ld a, $01 - ld [wIsDamageToSelf], a - ld a, 10 - call DealDamageToAllBenchedPokemon - ret - -.opp_bench - call SwapTurn - ld a, 10 - call DealDamageToAllBenchedPokemon - call SwapTurn - ret - -; return carry if can use Cowardice -Cowardice_Check: ; 2d28b (b:528b) - ldh a, [hTempPlayAreaLocation_ff9d] - ldh [hTemp_ffa0], a - call CheckCannotUseDueToStatus_OnlyToxicGasIfANon0 - ret c ; return if cannot use - - ld a, DUELVARS_NUMBER_OF_POKEMON_IN_PLAY_AREA - call GetTurnDuelistVariable - ldtx hl, EffectNoPokemonOnTheBenchText - cp 2 - ret c ; return if no bench - - ldh a, [hTempPlayAreaLocation_ff9d] - add DUELVARS_ARENA_CARD_FLAGS - call GetTurnDuelistVariable - ldtx hl, CannotBeUsedInTurnWhichWasPlayedText - and CAN_EVOLVE_THIS_TURN - scf - ret z ; return if was played this turn - - or a - ret - -Cowardice_PlayerSelectEffect: ; 2d2ae (b:52ae) - ldh a, [hTemp_ffa0] - or a - ret nz ; return if not Arena card - ldtx hl, SelectPokemonToPlaceInTheArenaText - call DrawWideTextBox_WaitForInput - bank1call HasAlivePokemonInBench - bank1call OpenPlayAreaScreenForSelection - ldh a, [hTempPlayAreaLocation_ff9d] - ldh [hAIPkmnPowerEffectParam], a - ret - -Cowardice_RemoveFromPlayAreaEffect: ; 2d2c3 (b:52c3) - ldh a, [hTemp_ffa0] - add DUELVARS_ARENA_CARD - call GetTurnDuelistVariable - -; put card in Discard Pile temporarily, so that -; all cards attached are discarded as well. - push af - ldh a, [hTemp_ffa0] - ld e, a - call MovePlayAreaCardToDiscardPile - -; if card was in Arena, swap selected Bench -; Pokemon with Arena, otherwise skip. - ldh a, [hTemp_ffa0] - or a - jr nz, .skip_switch - ldh a, [hAIPkmnPowerEffectParam] - ld e, a - call SwapArenaWithBenchPokemon - -.skip_switch -; move card back to Hand from Discard Pile -; and adjust Play Area - pop af - call MoveDiscardPileCardToHand - call AddCardToHand - call ShiftAllPokemonToFirstPlayAreaSlots - - xor a - ld [wDuelDisplayedScreen], a - ret - -LaprasWaterGunEffect: ; 2d2eb (b:52eb) - lb bc, 1, 0 - jp ApplyExtraWaterEnergyDamageBonus - -Quickfreeze_InitialEffect: ; 2d2f1 (b:52f1) - scf - ret - -Quickfreeze_Paralysis50PercentEffect: ; 2d2f3 (b:52f3) - ldtx de, ParalysisCheckText - call TossCoin_BankB - jr c, .heads - -; tails - call SetWasUnsuccessful - bank1call DrawDuelMainScene - bank1call Func_1bca - call WaitForWideTextBoxInput - ret - -.heads - call ParalysisEffect - ldh a, [hTempPlayAreaLocation_ff9d] - ld b, a - ld c, $00 - ldh a, [hWhoseTurn] - ld h, a - bank1call PlayAttackAnimation - bank1call Func_741a - bank1call WaitAttackAnimation - bank1call Func_6df1 - bank1call DrawDuelHUDs - bank1call Func_1bca - call c, WaitForWideTextBoxInput - ret - -IceBreath_ZeroDamage: ; 2d329 (b:5329) - xor a - call SetDefiniteDamage - ret - -IceBreath_RandomPokemonDamageEffect: ; 2d32e (b:532e) - call SwapTurn - call PickRandomPlayAreaCard - ld b, a - ld de, 40 - call DealDamageToPlayAreaPokemon_RegularAnim - call SwapTurn - ret - -FocusEnergyEffect: ; 2d33f (b:533f) - ld a, [wTempTurnDuelistCardID] - cp VAPOREON1 - ret nz ; return if no Vaporeon1 - ld a, SUBSTATUS1_NEXT_TURN_DOUBLE_DAMAGE - call ApplySubstatus1ToDefendingCard - ret - -PlayerPickFireEnergyCardToDiscard: ; 2d34b (b:534b) - call CreateListOfFireEnergyAttachedToArena - xor a - bank1call DisplayEnergyDiscardScreen - bank1call HandleEnergyDiscardMenuInput - ldh a, [hTempCardIndex_ff98] - ldh [hTempList], a - ret - -AIPickFireEnergyCardToDiscard: ; 2d35a (b:535a) - call CreateListOfFireEnergyAttachedToArena - ld a, [wDuelTempList] - ldh [hTempList], a ; pick first in list - ret - -; returns carry if Arena card has no Fire Energy cards -ArcanineFlamethrower_CheckEnergy: ; 2d363 (b:5363) - ld e, PLAY_AREA_ARENA - call GetPlayAreaCardAttachedEnergies - ld a, [wAttachedEnergies] - ldtx hl, NotEnoughFireEnergyText - cp 1 - ret - -ArcanineFlamethrower_PlayerSelectEffect: ; 2d371 (b:5371) - call PlayerPickFireEnergyCardToDiscard - ret - -ArcanineFlamethrower_AISelectEffect: ; 2d375 (b:5375) - call AIPickFireEnergyCardToDiscard - ret - -ArcanineFlamethrower_DiscardEffect: ; 2d379 (b:5379) - ldh a, [hTempList] - call PutCardInDiscardPile - ret - -TakeDownEffect: ; 2d37f (b:537f) - ld a, 30 - call DealRecoilDamageToSelf - ret - -ArcanineQuickAttack_AIEffect: ; 2d385 (b:5385) - ld a, (10 + 30) / 2 - lb de, 10, 30 - jp SetExpectedAIDamage - -ArcanineQuickAttack_DamageBoostEffect: ; 2d38d (b:538d) - ld hl, 20 - call LoadTxRam3 - ldtx de, DamageCheckIfHeadsPlusDamageText - call TossCoin_BankB - ret nc ; return if tails - ld a, 20 - call AddToDamage - ret - -; return carry if has less than 2 Fire Energy cards -FlamesOfRage_CheckEnergy: ; 2d3a0 (b:53a0) - ld e, PLAY_AREA_ARENA - call GetPlayAreaCardAttachedEnergies - ld a, [wAttachedEnergies] - ldtx hl, NotEnoughFireEnergyText - cp 2 - ret - -FlamesOfRage_PlayerSelectEffect: ; 2d3ae (b:53ae) - ldtx hl, ChooseAndDiscard2FireEnergyCardsText - call DrawWideTextBox_WaitForInput - - xor a - ldh [hCurSelectionItem], a - call CreateListOfFireEnergyAttachedToArena - xor a - bank1call DisplayEnergyDiscardScreen -.loop_input - bank1call HandleEnergyDiscardMenuInput - ret c - call GetNextPositionInTempList - ldh a, [hTempCardIndex_ff98] - ld [hl], a - call RemoveCardFromDuelTempList - ldh a, [hCurSelectionItem] - cp 2 - ret nc ; return when 2 have been chosen - bank1call DisplayEnergyDiscardMenu - jr .loop_input - -FlamesOfRage_AISelectEffect: ; 2d3d5 (b:53d5) - call AIPickFireEnergyCardToDiscard - ld a, [wDuelTempList + 1] - ldh [hTempList + 1], a - ret - -FlamesOfRage_DiscardEffect: ; 2d3de (b:53de) - ldh a, [hTempList] - call PutCardInDiscardPile - ldh a, [hTempList + 1] - call PutCardInDiscardPile - ret - -FlamesOfRage_AIEffect: ; 2d3e9 (b:53e9) - call FlamesOfRage_DamageBoostEffect - jp SetDefiniteAIDamage - -FlamesOfRage_DamageBoostEffect: ; 2d3ef (b:53ef) - ld e, PLAY_AREA_ARENA - call GetCardDamageAndMaxHP - call AddToDamage - ret - -RapidashStomp_AIEffect: ; 2d3f8 (b:53f8) - ld a, (20 + 30) / 2 - lb de, 20, 30 - jp SetExpectedAIDamage - -RapidashStomp_DamageBoostEffect: ; 2d400 (b:5400) - ld hl, 10 - call LoadTxRam3 - ldtx de, DamageCheckIfHeadsPlusDamageText - call TossCoin_BankB - ret nc ; return if tails - ld a, 10 - call AddToDamage - ret - -RapidashAgilityEffect: ; 2d413 (b:5413) - ldtx de, IfHeadsDoNotReceiveDamageOrEffectText - call TossCoin_BankB - ret nc ; return if tails - ld a, ATK_ANIM_AGILITY_PROTECT - ld [wLoadedAttackAnimation], a - ld a, SUBSTATUS1_AGILITY - call ApplySubstatus1ToDefendingCard - ret - -; returns carry if Opponent has no Pokemon in bench -NinetalesLure_CheckBench: ; 2d425 (b:5425) - ld a, DUELVARS_NUMBER_OF_POKEMON_IN_PLAY_AREA - call GetNonTurnDuelistVariable - ldtx hl, EffectNoPokemonOnTheBenchText - cp 2 - ret - -NinetalesLure_PlayerSelectEffect: ; 2d430 (b:5430) - ldtx hl, SelectPkmnOnBenchToSwitchWithActiveText - call DrawWideTextBox_WaitForInput - call SwapTurn - bank1call HasAlivePokemonInBench -.loop_input - bank1call OpenPlayAreaScreenForSelection - jr c, .loop_input - ldh a, [hTempPlayAreaLocation_ff9d] - ldh [hTemp_ffa0], a - call SwapTurn - ret - -NinetalesLure_AISelectEffect: ; 2d449 (b:5449) - call GetBenchPokemonWithLowestHP - ldh [hTemp_ffa0], a - ret - -NinetalesLure_SwitchEffect: ; 2d44f (b:544f) - call SwapTurn - ldh a, [hTemp_ffa0] - ld e, a - call HandleNShieldAndTransparency - call nc, SwapArenaWithBenchPokemon - call SwapTurn - xor a - ld [wDuelDisplayedScreen], a - ret - -; return carry if no Fire energy cards -FireBlast_CheckEnergy: ; 2d463 (b:5463) - ld e, PLAY_AREA_ARENA - call GetPlayAreaCardAttachedEnergies - ldtx hl, NotEnoughFireEnergyText - ld a, [wAttachedEnergies] - cp 1 - ret - -FireBlast_PlayerSelectEffect: ; 2d471 (b:5471) - call PlayerPickFireEnergyCardToDiscard - ret - -FireBlast_AISelectEffect: ; 2d475 (b:5475) - call AIPickFireEnergyCardToDiscard - ret - -FireBlast_DiscardEffect: ; 2d479 (b:5479) - ldh a, [hTempList] - call PutCardInDiscardPile - ret - -; return carry if no Fire energy cards -Ember_CheckEnergy: ; 2d47f (b:547f) - ld e, PLAY_AREA_ARENA - call GetPlayAreaCardAttachedEnergies - ldtx hl, NotEnoughFireEnergyText - ld a, [wAttachedEnergies] - cp 1 - ret - -Ember_PlayerSelectEffect: ; 2d48d (b:548d) - call PlayerPickFireEnergyCardToDiscard - ret - -Ember_AISelectEffect: ; 2d491 (b:5491) - call AIPickFireEnergyCardToDiscard - ret - -Ember_DiscardEffect: ; 2d495 (b:5495) - ldh a, [hTempList] - call PutCardInDiscardPile - ret - -; return carry if no Fire energy cards -Wildfire_CheckEnergy: ; 2d49b (b:549b) - ld e, PLAY_AREA_ARENA - call GetPlayAreaCardAttachedEnergies - ldtx hl, NotEnoughFireEnergyText - ld a, [wAttachedEnergies] - cp 1 - ret - -Wildfire_PlayerSelectEffect: ; 2d4a9 (b:54a9) - ldtx hl, DiscardOppDeckAsManyFireEnergyCardsText - call DrawWideTextBox_WaitForInput - - xor a - ldh [hCurSelectionItem], a - call CreateListOfFireEnergyAttachedToArena - xor a - bank1call DisplayEnergyDiscardScreen - -; show list to Player and for each card selected to discard, -; just increase a counter and store it. -; this will be the output used by Wildfire_DiscardEnergyEffect. - xor a - ld [wEnergyDiscardMenuDenominator], a -.loop - ldh a, [hCurSelectionItem] - ld [wEnergyDiscardMenuNumerator], a - bank1call HandleEnergyDiscardMenuInput - jr c, .done - ld hl, hCurSelectionItem - inc [hl] - call RemoveCardFromDuelTempList - jr c, .done - bank1call DisplayEnergyDiscardMenu - jr .loop - -.done -; return carry if no cards were discarded -; output the result in hTemp_ffa0 - ldh a, [hCurSelectionItem] - ldh [hTemp_ffa0], a - or a - ret nz - scf - ret - -Wildfire_AISelectEffect: ; 2d4dd (b:54dd) -; AI always chooses 0 cards to discard - xor a - ldh [hTempList], a - ret - -Wildfire_DiscardEnergyEffect: ; 2d4e1 (b:54e1) - call CreateListOfFireEnergyAttachedToArena - ldh a, [hTemp_ffa0] - or a - ret z ; no cards to discard - -; discard cards from wDuelTempList equal to the number -; of cards that were input in hTemp_ffa0. -; these are all the Fire Energy cards attached to Arena card -; so it will discard the cards in order, regardless -; of the actual order that was selected by Player. - ld c, a - ld hl, wDuelTempList -.loop_discard - ld a, [hli] - call PutCardInDiscardPile - dec c - jr nz, .loop_discard - ret - -Wildfire_DiscardDeckEffect: ; 2d4f4 (b:54f4) - ldh a, [hTemp_ffa0] - ld c, a - ld b, $00 - call SwapTurn - ld a, DUELVARS_NUMBER_OF_CARDS_NOT_IN_DECK - call GetTurnDuelistVariable - ld a, DECK_SIZE - sub [hl] - cp c - jr nc, .start_discard - ; only discard number of cards that are left in deck - ld c, a - -.start_discard - push bc - inc c - jr .check_remaining - -.loop - ; discard top card from deck - call DrawCardFromDeck - call nc, PutCardInDiscardPile -.check_remaining - dec c - jr nz, .loop - - pop hl - call LoadTxRam3 - ldtx hl, DiscardedCardsFromDeckText - call DrawWideTextBox_PrintText - call SwapTurn - ret - -Moltres1DiveBomb_AIEffect: ; 2d523 (b:5523) - ld a, 80 / 2 - lb de, 0, 80 - jp SetExpectedAIDamage - -Moltres1DiveBomb_Success50PercentEffect: ; 2d52b (b:552b) - ldtx de, SuccessCheckIfHeadsAttackIsSuccessfulText - call TossCoin_BankB - jr c, .heads -; tails - xor a - call SetDefiniteDamage - call SetWasUnsuccessful - ret -.heads - ld a, ATK_ANIM_DIVE_BOMB - ld [wLoadedAttackAnimation], a - ret - -FlareonQuickAttack_AIEffect: ; 2d541 (b:5541) - ld a, (10 + 30) / 2 - lb de, 10, 30 - jp SetExpectedAIDamage - -FlareonQuickAttack_DamageBoostEffect: ; 2d549 (b:5549) - ld hl, 20 - call LoadTxRam3 - ldtx de, DamageCheckIfHeadsPlusDamageText - call TossCoin_BankB - ret nc ; return if tails - ld a, 20 - call AddToDamage - ret - -; return carry if no Fire Energy attached -FlareonFlamethrower_CheckEnergy: ; 2d55c (b:555c) - ld e, PLAY_AREA_ARENA - call GetPlayAreaCardAttachedEnergies - ldtx hl, NotEnoughFireEnergyText - ld a, [wAttachedEnergies] - cp 1 - ret - -FlareonFlamethrower_PlayerSelectEffect: ; 2d56a (b:556a) - call PlayerPickFireEnergyCardToDiscard - ret - -FlareonFlamethrower_AISelectEffect: ; 2d56e (b:556e) - call AIPickFireEnergyCardToDiscard - ret - -FlareonFlamethrower_DiscardEffect: ; 2d572 (b:5572) - ldh a, [hTempList] - call PutCardInDiscardPile - ret - -; return carry if no Fire Energy attached -MagmarFlamethrower_CheckEnergy: ; 2d578 (b:5578) - ld e, PLAY_AREA_ARENA - call GetPlayAreaCardAttachedEnergies - ldtx hl, NotEnoughFireEnergyText - ld a, [wAttachedEnergies] - cp 1 - ret - -MagmarFlamethrower_PlayerSelectEffect: ; 2d586 (b:5586) - call PlayerPickFireEnergyCardToDiscard - ret - -MagmarFlamethrower_AISelectEffect: ; 2d58a (b:558a) - call AIPickFireEnergyCardToDiscard - ret - -MagmarFlamethrower_DiscardEffect: ; 2d58e (b:558e) - ldh a, [hTempList] - call PutCardInDiscardPile - ret - -MagmarSmokescreenEffect: ; 2d594 (b:5594) - ld a, SUBSTATUS2_SMOKESCREEN - call ApplySubstatus2ToDefendingCard - ret - -MagmarSmog_AIEffect: ; 2d59a (b:559a) - ld a, 5 - lb de, 0, 10 - jp UpdateExpectedAIDamage_AccountForPoison - -; return carry if no Fire Energy attached -CharmeleonFlamethrower_CheckEnergy: ; 2d5a2 (b:55a2) - ld e, PLAY_AREA_ARENA - call GetPlayAreaCardAttachedEnergies - ldtx hl, NotEnoughFireEnergyText - ld a, [wAttachedEnergies] - cp 1 - ret - -CharmeleonFlamethrower_PlayerSelectEffect: ; 2d5b0 (b:55b0) - call PlayerPickFireEnergyCardToDiscard - ret - -CharmeleonFlamethrower_AISelectEffect: ; 2d5b4 (b:55b4) - call AIPickFireEnergyCardToDiscard - ret - -CharmeleonFlamethrower_DiscardEffect: ; 2d5b8 (b:55b8) - ldh a, [hTempList] - call PutCardInDiscardPile - ret - -EnergyBurnEffect: ; 2d5be (b:55be) - scf - ret - -; return carry if has less than 2 Fire Energy cards -FireSpin_CheckEnergy: ; 2d5c0 (b:55c0) - xor a ; PLAY_AREA_ARENA - call CreateArenaOrBenchEnergyCardList - call CountCardsInDuelTempList - ldtx hl, NotEnoughEnergyCardsText - cp 2 - ret - -FireSpin_PlayerSelectEffect: ; 2d5cd (b:55cd) - ldtx hl, ChooseAndDiscard2EnergyCardsText - call DrawWideTextBox_WaitForInput - - xor a - ldh [hCurSelectionItem], a - xor a - call CreateArenaOrBenchEnergyCardList - call SortCardsInDuelTempListByID - xor a - bank1call DisplayEnergyDiscardScreen - - ld a, 2 - ld [wEnergyDiscardMenuDenominator], a -.loop_input - bank1call HandleEnergyDiscardMenuInput - ret c - call GetNextPositionInTempList - ldh a, [hTempCardIndex_ff98] - ld [hl], a - ld hl, wEnergyDiscardMenuNumerator - inc [hl] - ldh a, [hCurSelectionItem] - cp 2 - jr nc, .done - ldh a, [hTempCardIndex_ff98] - call RemoveCardFromDuelTempList - bank1call DisplayEnergyDiscardMenu - jr .loop_input -.done -; return when 2 have been chosen - or a - ret - -FireSpin_AISelectEffect: ; 2d606 (b:5606) - xor a ; PLAY_AREA_ARENA - call CreateArenaOrBenchEnergyCardList - ld hl, wDuelTempList - ld a, [hli] - ldh [hTempList], a - ld a, [hl] - ldh [hTempList + 1], a - ret - -FireSpin_DiscardEffect: ; 2d614 (b:5614) - ld hl, hTempList - ld a, [hli] - call PutCardInDiscardPile - ld a, [hli] - call PutCardInDiscardPile - ret - -; returns carry if Pkmn Power cannot be used -; or if Arena card is not Charizard. -; this is unused. -EnergyBurnCheck_Unreferenced: ; 2d620 (b:5620) - xor a - bank1call CheckCannotUseDueToStatus_OnlyToxicGasIfANon0 - ret c - ld a, DUELVARS_ARENA_CARD - push de - call GetTurnDuelistVariable - call GetCardIDFromDeckIndex - ld a, e - pop de - cp CHARIZARD - jr nz, .not_charizard - or a - ret -.not_charizard - scf - ret - -FlareonRage_AIEffect: ; 2d638 (b:5638) - call FlareonRage_DamageBoostEffect - jp SetDefiniteAIDamage - -FlareonRage_DamageBoostEffect: ; 2d63e (b:563e) - ld e, PLAY_AREA_ARENA - call GetCardDamageAndMaxHP - call AddToDamage - ret - -MixUpEffect: ; 2d647 (b:5647) - call SwapTurn - call CreateHandCardList - call SortCardsInDuelTempListByID - -; first go through Hand to place -; all Pkmn cards in it in the Deck. - ld hl, wDuelTempList - ld c, 0 -.loop_hand - ld a, [hl] - cp $ff - jr z, .done_hand - call .CheckIfCardIsPkmnCard - jr nc, .next_hand - ; found Pkmn card, place in deck - inc c - ld a, [hl] - call RemoveCardFromHand - call ReturnCardToDeck -.next_hand - inc hl - jr .loop_hand - -.done_hand - ld a, c - ldh [hCurSelectionItem], a - push bc - ldtx hl, ThePkmnCardsInHandAndDeckWereShuffledText - call DrawWideTextBox_WaitForInput - - call Func_2c0bd - call CreateDeckCardList - pop bc - ldh a, [hCurSelectionItem] - or a - jr z, .done ; if no cards were removed from Hand, return - -; c holds the number of cards that were placed in the Deck. -; now pick Pkmn cards from the Deck to place in Hand. - ld hl, wDuelTempList -.loop_deck - ld a, [hl] - call .CheckIfCardIsPkmnCard - jr nc, .next_deck - dec c - ld a, [hl] - call SearchCardInDeckAndAddToHand - call AddCardToHand -.next_deck - inc hl - ld a, c - or a - jr nz, .loop_deck -.done - call SwapTurn - ret - -; returns carry if card index in a is Pkmn card -.CheckIfCardIsPkmnCard: ; 2d69a (b:569a) - call LoadCardDataToBuffer2_FromDeckIndex - ld a, [wLoadedCard2Type] - cp TYPE_ENERGY - ret - -DancingEmbers_AIEffect: ; 2d6a3 (b:56a3) - ld a, 80 / 2 - lb de, 0, 80 - jp SetExpectedAIDamage - -DancingEmbers_MultiplierEffect: ; 2d6ab (b:56ab) - ld hl, 10 - call LoadTxRam3 - ldtx de, DamageCheckIfHeadsXDamageText - ld a, 8 - call TossCoinATimes_BankB - call ATimes10 - call SetDefiniteDamage - ret - -Firegiver_InitialEffect: ; 2d6c0 (b:56c0) - scf - ret - -Firegiver_AddToHandEffect: ; 2d6c2 (b:56c2) -; fill wDuelTempList with all Fire Energy card -; deck indices that are in the Deck. - ld a, DUELVARS_CARD_LOCATIONS - call GetTurnDuelistVariable - ld de, wDuelTempList - ld c, 0 -.loop_cards - ld a, [hl] - cp CARD_LOCATION_DECK - jr nz, .next - push hl - push de - ld a, l - call GetCardIDFromDeckIndex - call GetCardType - pop de - pop hl - cp TYPE_ENERGY_FIRE - jr nz, .next - ld a, l - ld [de], a - inc de - inc c -.next - inc l - ld a, l - cp DECK_SIZE - jr c, .loop_cards - ld a, $ff - ld [de], a - -; check how many were found - ld a, c - or a - jr nz, .found - ; return if none found - ldtx hl, ThereWasNoFireEnergyText - call DrawWideTextBox_WaitForInput - call Func_2c0bd - ret - -.found -; pick a random number between 1 and 4, -; up to the maximum number of Fire Energy -; cards that were found. - ld a, 4 - call Random - inc a - cp c - jr c, .ok - ld a, c - -.ok - ldh [hCurSelectionItem], a -; load correct attack animation depending -; on what side the effect is from. - ld d, ATK_ANIM_FIREGIVER_PLAYER - ld a, [wDuelistType] - cp DUELIST_TYPE_PLAYER - jr z, .player_1 -; opponent - ld d, ATK_ANIM_FIREGIVER_OPP -.player_1 - ld a, d - ld [wLoadedAttackAnimation], a - -; start loop for adding Energy cards to hand - ldh a, [hCurSelectionItem] - ld c, a - ld hl, wDuelTempList -.loop_energy - push hl - push bc - ld bc, $0 - ldh a, [hWhoseTurn] - ld h, a - bank1call PlayAttackAnimation - bank1call WaitAttackAnimation - -; load correct coordinates to update the number of cards -; in hand and deck during animation. - lb bc, 18, 7 ; x, y for hand number - ld e, 3 ; y for deck number - ld a, [wLoadedAttackAnimation] - cp ATK_ANIM_FIREGIVER_PLAYER - jr z, .player_2 - lb bc, 4, 5 ; x, y for hand number - ld e, 10 ; y for deck number - -.player_2 -; update and print number of cards in hand - ld a, DUELVARS_NUMBER_OF_CARDS_IN_HAND - call GetTurnDuelistVariable - inc a - bank1call WriteTwoDigitNumberInTxSymbolFormat -; update and print number of cards in deck - ld a, DUELVARS_NUMBER_OF_CARDS_NOT_IN_DECK - call GetTurnDuelistVariable - ld a, DECK_SIZE - 1 - sub [hl] - ld c, e - bank1call WriteTwoDigitNumberInTxSymbolFormat - -; load Fire Energy card index and add to hand - pop bc - pop hl - ld a, [hli] - call SearchCardInDeckAndAddToHand - call AddCardToHand - dec c - jr nz, .loop_energy - -; load the number of cards added to hand and print text - ldh a, [hCurSelectionItem] - ld l, a - ld h, $00 - call LoadTxRam3 - ldtx hl, DrewFireEnergyFromTheHandText - call DrawWideTextBox_WaitForInput - call Func_2c0bd - ret - -Moltres2DiveBomb_AIEffect: ; 2d76e (b:576e) - ld a, 70 / 2 - lb de, 0, 70 - jp SetExpectedAIDamage - -Moltres2DiveBomb_Success50PercentEffect: ; 2d776 (b:5776) - ldtx de, SuccessCheckIfHeadsAttackIsSuccessfulText - call TossCoin_BankB - jr c, .heads -; tails - xor a - call SetDefiniteDamage - call SetWasUnsuccessful - ret -.heads - ld a, ATK_ANIM_DIVE_BOMB - ld [wLoadedAttackAnimation], a - ret - -; output in de the number of energy cards -; attached to the Defending Pokemon times 10. -; used for attacks that deal 10x number of energy -; cards attached to the Defending card. -GetEnergyAttachedMultiplierDamage: ; 2d78c (b:578c) - call SwapTurn - ld a, DUELVARS_CARD_LOCATIONS - call GetTurnDuelistVariable - - ld c, 0 -.loop - ld a, [hl] - cp CARD_LOCATION_ARENA - jr nz, .next - ; is in Arena - ld a, l - call GetCardIDFromDeckIndex - call GetCardType - and TYPE_ENERGY - jr z, .next - ; is Energy attached to Arena card - inc c -.next - inc l - ld a, l - cp DECK_SIZE - jr c, .loop - - call SwapTurn - ld l, c - ld h, $00 - ld b, $00 - add hl, hl ; hl = 2 * c - add hl, hl ; hl = 4 * c - add hl, bc ; hl = 5 * c - add hl, hl ; hl = 10 * c - ld e, l - ld d, h - ret - -; draws list of Energy Cards in Discard Pile -; for Player to select from. -; the Player can select up to 2 cards from the list. -; these cards are given in $ff-terminated list -; in hTempList. -HandleEnergyCardsInDiscardPileSelection: ; 2d7bc (b:57bc) - push hl - xor a - ldh [hCurSelectionItem], a - call CreateEnergyCardListFromDiscardPile_AllEnergy - pop hl - jr c, .finish - - call DrawWideTextBox_WaitForInput -.loop -; draws Discard Pile screen and textbox, -; and handles Player input - bank1call InitAndDrawCardListScreenLayout - ldtx hl, ChooseAnEnergyCardText - ldtx de, PlayerDiscardPileText - bank1call SetCardListHeaderText - bank1call DisplayCardList - jr nc, .selected - -; Player is trying to exit screen, -; but can select up to 2 cards total. -; prompt Player to confirm exiting screen. - ld a, 2 - call AskWhetherToQuitSelectingCards - jr c, .loop - jr .finish - -.selected -; a card was selected, so add it to list - call GetNextPositionInTempList - ldh a, [hTempCardIndex_ff98] - ld [hl], a - call RemoveCardFromDuelTempList - or a - jr z, .finish ; no more cards? - ldh a, [hCurSelectionItem] - cp 2 - jr c, .loop ; already selected 2 cards? - -.finish -; place terminating byte on list - call GetNextPositionInTempList - ld [hl], $ff - or a - ret - -; returns carry if Pkmn Power cannot be used, and -; sets the correct text in hl for failure. -Curse_CheckDamageAndBench: ; 2d7fc (b:57fc) - ldh a, [hTempPlayAreaLocation_ff9d] - ldh [hTemp_ffa0], a - -; fail if Pkmn Power has already been used - add DUELVARS_ARENA_CARD_FLAGS - call GetTurnDuelistVariable - ldtx hl, OnlyOncePerTurnText - and USED_PKMN_POWER_THIS_TURN - jr nz, .set_carry - -; fail if Opponent only has 1 Pokemon in Play Area - call SwapTurn - ld a, DUELVARS_NUMBER_OF_POKEMON_IN_PLAY_AREA - call GetTurnDuelistVariable - call SwapTurn - ldtx hl, CannotUseSinceTheresOnly1PkmnText - cp 2 - jr c, .set_carry - -; fail if Opponent has no damage counters - call SwapTurn - call CheckIfPlayAreaHasAnyDamage - call SwapTurn - ldtx hl, NoPokemonWithDamageCountersText - jr c, .set_carry - -; return carry if Pkmn Power cannot be used due -; to Toxic Gas or status. - ldh a, [hTempPlayAreaLocation_ff9d] - call CheckCannotUseDueToStatus_OnlyToxicGasIfANon0 - ret - -.set_carry - scf - ret - -Curse_PlayerSelectEffect: ; 2d834 (b:5834) - ldtx hl, ProcedureForCurseText - bank1call DrawWholeScreenTextBox - call SwapTurn - xor a - ldh [hCurSelectionItem], a - bank1call Func_61a1 -.start - bank1call PrintPlayAreaCardList_EnableLCD - push af - ldh a, [hCurSelectionItem] - ld hl, PlayAreaSelectionMenuParameters - call InitializeMenuParameters - pop af - ld [wNumMenuItems], a - -; first pick a target to take 1 damage counter from. -.loop_input_first - call DoFrame - call HandleMenuInput - jr nc, .loop_input_first - cp $ff - jr z, .cancel - ldh [hCurSelectionItem], a - ldh [hTempPlayAreaLocation_ffa1], a - call GetCardDamageAndMaxHP - or a - jr nz, .picked_first ; test if has damage - ; play sfx - call Func_3794 - jr .loop_input_first - -.picked_first -; give 10 HP to card selected, draw the scene, -; then immediately revert this. - ldh a, [hTempPlayAreaLocation_ffa1] - add DUELVARS_ARENA_CARD_HP - call GetTurnDuelistVariable - push af - push hl - add 10 - ld [hl], a - bank1call PrintPlayAreaCardList_EnableLCD - pop hl - pop af - ld [hl], a - -; draw damage counter on cursor - ldh a, [hTempPlayAreaLocation_ffa1] - ld b, SYM_HP_NOK - call DrawSymbolOnPlayAreaCursor - -; handle input to pick the target to receive the damage counter. -.loop_input_second - call DoFrame - call HandleMenuInput - jr nc, .loop_input_second - ldh [hPlayAreaEffectTarget], a - cp $ff - jr nz, .a_press ; was a pressed? - -; b press -; erase the damage counter symbol -; and loop back up again. - ldh a, [hTempPlayAreaLocation_ffa1] - ld b, SYM_SPACE - call DrawSymbolOnPlayAreaCursor - call EraseCursor - jr .start - -.a_press - ld hl, hTempPlayAreaLocation_ffa1 - cp [hl] - jr z, .loop_input_second ; same as first? -; a different Pokemon was picked, -; so store this Play Area location -; and erase the damage counter in the cursor. - ldh a, [hTempPlayAreaLocation_ffa1] - ld b, SYM_SPACE - call DrawSymbolOnPlayAreaCursor - call EraseCursor - call SwapTurn - or a - ret - -.cancel -; return carry if operation was cancelled. - call SwapTurn - scf - ret - -Curse_TransferDamageEffect: ; 2d8bb (b:58bb) -; set Pkmn Power as used - ldh a, [hTempList] - add DUELVARS_ARENA_CARD_FLAGS - call GetTurnDuelistVariable - set USED_PKMN_POWER_THIS_TURN_F, [hl] - -; figure out the type of duelist that used Curse. -; if it was the player, no need to draw the Play Area screen. - call SwapTurn - ld a, DUELVARS_DUELIST_TYPE - call GetNonTurnDuelistVariable - cp DUELIST_TYPE_PLAYER - jr z, .vs_player - -; vs. opponent - bank1call Func_61a1 -.vs_player -; transfer the damage counter to the targets that were selected. - ldh a, [hPlayAreaEffectTarget] - add DUELVARS_ARENA_CARD_HP - call GetTurnDuelistVariable - sub 10 - ld [hl], a - ldh a, [hTempPlayAreaLocation_ffa1] - add DUELVARS_ARENA_CARD_HP - ld l, a - ld a, 10 - add [hl] - ld [hl], a - - bank1call PrintPlayAreaCardList_EnableLCD - ld a, DUELVARS_DUELIST_TYPE - call GetNonTurnDuelistVariable - cp DUELIST_TYPE_PLAYER - jr z, .done -; vs. opponent - ldh a, [hPlayAreaEffectTarget] - ldh [hTempPlayAreaLocation_ff9d], a - bank1call Func_6194 - -.done - call SwapTurn - call ExchangeRNG - bank1call Func_6e49 - ret - -GengarDarkMind_PlayerSelectEffect: ; 2d903 (b:5903) - ld a, DUELVARS_NUMBER_OF_POKEMON_IN_PLAY_AREA - call GetNonTurnDuelistVariable - cp 2 - jr nc, .has_bench -; no bench Pokemon to damage. - ld a, $ff - ldh [hTemp_ffa0], a - ret - -.has_bench -; opens Play Area screen to select Bench Pokemon -; to damage, and store it before returning. - ldtx hl, ChoosePkmnInTheBenchToGiveDamageText - call DrawWideTextBox_WaitForInput - call SwapTurn - bank1call HasAlivePokemonInBench -.loop_input - bank1call OpenPlayAreaScreenForSelection - jr c, .loop_input - ldh a, [hTempPlayAreaLocation_ff9d] - ldh [hTemp_ffa0], a - call SwapTurn - ret - -GengarDarkMind_AISelectEffect: ; 2d92a (b:592a) - ld a, $ff - ldh [hTemp_ffa0], a - ld a, DUELVARS_NUMBER_OF_POKEMON_IN_PLAY_AREA - call GetNonTurnDuelistVariable - cp 2 - ret c ; return if no Bench Pokemon -; just pick Pokemon with lowest remaining HP. - call GetBenchPokemonWithLowestHP - ldh [hTemp_ffa0], a - ret - -GengarDarkMind_DamageBenchEffect: ; 2d93c (b:593c) - ldh a, [hTemp_ffa0] - cp $ff - ret z ; no target chosen - call SwapTurn - ld b, a - ld de, 10 - call DealDamageToPlayAreaPokemon_RegularAnim - call SwapTurn - ret - -SleepingGasEffect: ; 2d94f (b:594f) - call Sleep50PercentEffect - call nc, SetNoEffectFromStatus - ret - -DestinyBond_CheckEnergy: ; 2d956 (b:5956) - ld e, PLAY_AREA_ARENA - call GetPlayAreaCardAttachedEnergies - ld a, [wAttachedEnergies + PSYCHIC] - ldtx hl, NotEnoughPsychicEnergyText - cp 1 - ret - -DestinyBond_PlayerSelectEffect: ; 2d964 (b:5964) -; handle input and display of Energy card list - ld a, TYPE_ENERGY_PSYCHIC - call CreateListOfEnergyAttachedToArena - xor a - bank1call DisplayEnergyDiscardScreen - bank1call HandleEnergyDiscardMenuInput - ret c - ldh a, [hTempCardIndex_ff98] - ldh [hTempList], a - ret - -DestinyBond_AISelectEffect: ; 2d976 (b:5976) -; pick first card in list - ld a, TYPE_ENERGY_PSYCHIC - call CreateListOfEnergyAttachedToArena - ld a, [wDuelTempList] - ldh [hTempList], a - ret - -DestinyBond_DiscardEffect: ; 2d981 (b:5981) - ldh a, [hTempList] - call PutCardInDiscardPile - ret - -DestinyBond_DestinyBondEffect: ; 2d987 (b:5987) - ld a, SUBSTATUS1_DESTINY_BOND - call ApplySubstatus1ToDefendingCard - ret - -; returns carry if no Energy cards in Discard Pile. -EnergyConversion_CheckEnergy: ; 2d98d (b:598d) - call CreateEnergyCardListFromDiscardPile_AllEnergy - ldtx hl, ThereAreNoEnergyCardsInDiscardPileText - ret - -EnergyConversion_PlayerSelectEffect: ; 2d994 (b:5994) - ldtx hl, Choose2EnergyCardsFromDiscardPileForHandText - call HandleEnergyCardsInDiscardPileSelection - ret - -EnergyConversion_AISelectEffect: ; 2d99b (b:599b) - call CreateEnergyCardListFromDiscardPile_AllEnergy - ld hl, wDuelTempList - ld de, hTempList - ld c, 2 -; select the first two energy cards found in Discard Pile -.loop - ld a, [hli] - cp $ff - jr z, .done - ld [de], a - inc de - dec c - jr nz, .loop -.done - ld a, $ff - ld [de], a - ret - -EnergyConversion_AddToHandEffect: ; 2d9b4 (b:59b4) -; damage itself - ld a, 10 - call DealRecoilDamageToSelf - -; loop cards that were chosen -; until $ff is reached, -; and move them to the hand. - ld hl, hTempList - ld de, wDuelTempList -.loop_cards - ld a, [hli] - ld [de], a - inc de - cp $ff - jr z, .done - call MoveDiscardPileCardToHand - call AddCardToHand - jr .loop_cards - -.done - call IsPlayerTurn - ret c - bank1call Func_4b38 - ret - -; return carry if Defending Pokemon is not asleep -DreamEaterEffect: ; 2d9d6 (b:59d6) - ld a, DUELVARS_ARENA_CARD_STATUS - call GetNonTurnDuelistVariable - and CNF_SLP_PRZ - cp ASLEEP - ret z ; return if asleep -; not asleep, set carry and load text - ldtx hl, OpponentIsNotAsleepText - scf - ret - -TransparencyEffect: ; 2d9e5 (b:59e5) - scf - ret - -; returns carry if neither the Turn Duelist or -; the non-Turn Duelist have any deck cards. -Prophecy_CheckDeck: ; 2d9e7 (b:59e7) - ld a, DUELVARS_NUMBER_OF_CARDS_NOT_IN_DECK - call GetTurnDuelistVariable - cp DECK_SIZE - jr c, .no_carry - ld a, DUELVARS_NUMBER_OF_CARDS_NOT_IN_DECK - call GetNonTurnDuelistVariable - cp DECK_SIZE - jr c, .no_carry - ldtx hl, NoCardsLeftInTheDeckText - scf - ret -.no_carry - or a - ret - -Prophecy_PlayerSelectEffect: ; 2da00 (b:5a00) - ldtx hl, ProcedureForProphecyText - bank1call DrawWholeScreenTextBox -.select_deck - bank1call DrawDuelMainScene - ldtx hl, PleaseSelectTheDeckText - call TwoItemHorizontalMenu - ldh a, [hKeysHeld] - and B_BUTTON - jr nz, Prophecy_PlayerSelectEffect ; loop back to start - - ldh a, [hCurMenuItem] - ldh [hTempList], a ; store selection in first position in list - or a - jr z, .turn_duelist - -; non-turn duelist - ld a, DUELVARS_NUMBER_OF_CARDS_NOT_IN_DECK - call GetNonTurnDuelistVariable - cp DECK_SIZE - jr nc, .select_deck ; no cards, go back to deck selection - call SwapTurn - call HandleProphecyScreen - call SwapTurn - ret - -.turn_duelist - ld a, DUELVARS_NUMBER_OF_CARDS_NOT_IN_DECK - call GetTurnDuelistVariable - cp DECK_SIZE - jr nc, .select_deck ; no cards, go back to deck selection - call HandleProphecyScreen - ret - -Prophecy_AISelectEffect: ; 2da3c (b:5a3c) -; AI doesn't ever choose this attack -; so this it does no sorting. - ld a, $ff - ldh [hTemp_ffa0], a - ret - -Prophecy_ReorderDeckEffect: ; 2da41 (b:5a41) - ld hl, hTempList - ld a, [hli] - or a - jr z, .ReorderCards ; turn duelist's deck - cp $ff - ret z - - ; non-turn duelist's deck - call SwapTurn - call .ReorderCards - call SwapTurn - ret - -.ReorderCards - ld c, 0 -; add selected cards to hand in the specified order -.loop_add_hand - ld a, [hli] - cp $ff - jr z, .dec_hl - call SearchCardInDeckAndAddToHand - inc c - jr .loop_add_hand - -.dec_hl -; go to last card that was in the list - dec hl - dec hl - -.loop_return_deck -; return the cards to the top of the deck - ld a, [hld] - call ReturnCardToDeck - dec c - jr nz, .loop_return_deck - call IsPlayerTurn - ret c - ; print text in case it was the opponent - ldtx hl, ExchangedCardsInDuelistsHandText - call DrawWideTextBox_WaitForInput - ret - -; draw and handle Player selection for reordering -; the top 3 cards of Deck. -; the resulting list is output in order in hTempList. -HandleProphecyScreen: ; 2da76 (b:5a76) - ld a, DUELVARS_NUMBER_OF_CARDS_NOT_IN_DECK - call GetTurnDuelistVariable - ld b, a - ld a, DECK_SIZE - sub [hl] ; a = number of cards in deck - -; store in c the number of cards that will be reordered. -; this number is 3, unless the deck as fewer cards than -; that in which case it will be the number of cards remaining. - ld c, 3 - cp c - jr nc, .got_number_cards - ld c, a ; store number of remaining cards in c -.got_number_cards - ld a, c - inc a - ld [wNumberOfCardsToOrder], a - -; store in wDuelTempList the cards -; at top of Deck to be reordered. - ld a, b - add DUELVARS_DECK_CARDS - ld l, a - ld de, wDuelTempList -.loop_top_cards - ld a, [hli] - ld [de], a - inc de - dec c - jr nz, .loop_top_cards - ld a, $ff ; terminating byte - ld [de], a - -.start - call CountCardsInDuelTempList - ld b, a - ld a, 1 ; start at 1 - ldh [hCurSelectionItem], a - -; initialize buffer ahead in wDuelTempList. - ld hl, wDuelTempList + 10 - xor a -.loop_init_buffer - ld [hli], a - dec b - jr nz, .loop_init_buffer - ld [hl], $ff - - bank1call InitAndDrawCardListScreenLayout - ldtx hl, ChooseTheOrderOfTheCardsText - ldtx de, DuelistDeckText - bank1call SetCardListHeaderText - bank1call Func_5735 - -.loop_selection - bank1call DisplayCardList - jr c, .clear - -; first check if this card was already selected - ldh a, [hCurMenuItem] - ld e, a - ld d, $00 - ld hl, wDuelTempList + 10 - add hl, de - ld a, [hl] - or a - jr nz, .loop_selection ; already chosen - -; being here means card hasn't been selected yet, -; so add its order number to buffer and increment -; the sort number for the next card. - ldh a, [hCurSelectionItem] - ld [hl], a - inc a - ldh [hCurSelectionItem], a - bank1call Func_5744 - ldh a, [hCurSelectionItem] - ld hl, wNumberOfCardsToOrder - cp [hl] - jr c, .loop_selection ; still more cards - -; confirm that the ordering has been completed - call EraseCursor - ldtx hl, IsThisOKText - call YesOrNoMenuWithText_LeftAligned - jr c, .start ; if not, return back to beginning of selection - -; write in hTempList the card list -; in order that was selected. - ld hl, wDuelTempList + 10 - ld de, wDuelTempList - ld c, 0 -.loop_order - ld a, [hli] - cp $ff - jr z, .done - push hl - push bc - ld c, a - ld b, $00 - ld hl, hTempList - add hl, bc - ld a, [de] - ld [hl], a - pop bc - pop hl - inc de - inc c - jr .loop_order -; now hTempList has the list of card deck indices -; in the order selected to be place on top of the deck. - -.done - ld b, $00 - ld hl, hTempList + 1 - add hl, bc - ld [hl], $ff ; terminating byte - or a - ret - -.clear -; check if any reordering was done. - ld hl, hCurSelectionItem - ld a, [hl] - cp 1 - jr z, .loop_selection ; none done, go back -; clear the order that was selected thus far. - dec a - ld [hl], a - ld c, a - ld hl, wDuelTempList + 10 -.loop_clear - ld a, [hli] - cp c - jr nz, .loop_clear - ; clear this byte - dec hl - ld [hl], $00 - bank1call Func_5744 - jr .loop_selection - -HypnoDarkMind_PlayerSelectEffect: ; 2db2b (b:5b2b) - ld a, DUELVARS_NUMBER_OF_POKEMON_IN_PLAY_AREA - call GetNonTurnDuelistVariable - cp 2 - jr nc, .has_bench -; no bench Pokemon to damage. - ld a, $ff - ldh [hTemp_ffa0], a - ret - -.has_bench -; opens Play Area screen to select Bench Pokemon -; to damage, and store it before returning. - ldtx hl, ChoosePkmnInTheBenchToGiveDamageText - call DrawWideTextBox_WaitForInput - call SwapTurn - bank1call HasAlivePokemonInBench -.loop_input - bank1call OpenPlayAreaScreenForSelection - jr c, .loop_input - ldh a, [hTempPlayAreaLocation_ff9d] - ldh [hTemp_ffa0], a - call SwapTurn - ret - -HypnoDarkMind_AISelectEffect: ; 2db52 (b:5b52) - ld a, $ff - ldh [hTemp_ffa0], a - ld a, DUELVARS_NUMBER_OF_POKEMON_IN_PLAY_AREA - call GetNonTurnDuelistVariable - cp 2 - ret c ; return if no Bench Pokemon -; just pick Pokemon with lowest remaining HP. - call GetBenchPokemonWithLowestHP - ldh [hTemp_ffa0], a - ret - -HypnoDarkMind_DamageBenchEffect: ; 2db64 (b:5b64) - ldh a, [hTemp_ffa0] - cp $ff - ret z ; no target chosen - call SwapTurn - ld b, a - ld de, 10 - call DealDamageToPlayAreaPokemon_RegularAnim - call SwapTurn - ret - -InvisibleWallEffect: ; 2db77 (b:5b77) - scf - ret - -MrMimeMeditate_AIEffect: ; 2db79 (b:5b79) - call MrMimeMeditate_DamageBoostEffect - jp SetDefiniteAIDamage - -MrMimeMeditate_DamageBoostEffect: ; 2db7f (b:5b7f) -; add damage counters of Defending card to damage - call SwapTurn - ld e, PLAY_AREA_ARENA - call GetCardDamageAndMaxHP - call SwapTurn - call AddToDamage - ret - -; returns carry if Damage Swap cannot be used. -DamageSwap_CheckDamage: ; 2db8e (b:5b8e) - ldh a, [hTempPlayAreaLocation_ff9d] - ldh [hTemp_ffa0], a - call CheckIfPlayAreaHasAnyDamage - jr c, .no_damage - ldh a, [hTempPlayAreaLocation_ff9d] - call CheckCannotUseDueToStatus_OnlyToxicGasIfANon0 - ret -.no_damage - ldtx hl, NoPokemonWithDamageCountersText - scf - ret - -DamageSwap_SelectAndSwapEffect: ; 2dba2 (b:5ba2) - ld a, DUELVARS_DUELIST_TYPE - call GetTurnDuelistVariable - cp DUELIST_TYPE_PLAYER - jr z, .player -; non-player - bank1call Func_61a1 - bank1call PrintPlayAreaCardList_EnableLCD - ret - -.player - ldtx hl, ProcedureForDamageSwapText - bank1call DrawWholeScreenTextBox - xor a - ldh [hCurSelectionItem], a - bank1call Func_61a1 - -.start - bank1call PrintPlayAreaCardList_EnableLCD - push af - ldh a, [hCurSelectionItem] - ld hl, PlayAreaSelectionMenuParameters - call InitializeMenuParameters - pop af - ld [wNumMenuItems], a - -; handle selection of Pokemon to take damage from -.loop_input_first - call DoFrame - call HandleMenuInput - jr nc, .loop_input_first - cp $ff - ret z ; quit when B button is pressed - - ldh [hTempPlayAreaLocation_ffa1], a - ldh [hCurSelectionItem], a - -; if card has no damage, play sfx and return to start - call GetCardDamageAndMaxHP - or a - jr z, .no_damage - -; take damage away temporarily to draw UI. - ldh a, [hTempPlayAreaLocation_ffa1] - add DUELVARS_ARENA_CARD_HP - call GetTurnDuelistVariable - push af - push hl - add 10 - ld [hl], a - bank1call PrintPlayAreaCardList_EnableLCD - pop hl - pop af - ld [hl], a - -; draw damage counter in cursor - ldh a, [hTempPlayAreaLocation_ffa1] - ld b, SYM_HP_NOK - call DrawSymbolOnPlayAreaCursor - -; handle selection of Pokemon to give damage to -.loop_input_second - call DoFrame - call HandleMenuInput - jr nc, .loop_input_second - ; if B is pressed, return damage counter - ; to card that it was taken from - cp $ff - jr z, .update_ui - -; try to give the card selected the damage counter -; if it would KO, ignore it. - ldh [hPlayAreaEffectTarget], a - ldh [hCurSelectionItem], a - call TryGiveDamageCounter_DamageSwap - jr c, .loop_input_second - - ld a, OPPACTION_6B15 - call SetOppAction_SerialSendDuelData - -.update_ui - ldh a, [hTempPlayAreaLocation_ffa1] - ld b, SYM_SPACE - call DrawSymbolOnPlayAreaCursor - call EraseCursor - jr .start - -.no_damage - call Func_3794 - jr .loop_input_first - -; tries to give damage counter to hPlayAreaEffectTarget, -; and if successful updates UI screen. -DamageSwap_SwapEffect: ; 2dc27 (b:5c27) - call TryGiveDamageCounter_DamageSwap - ret c - bank1call PrintPlayAreaCardList_EnableLCD - or a - ret - -; tries to give the damage counter to the target -; chosen by the Player (hPlayAreaEffectTarget). -; if the damage counter would KO card, then do -; not give the damage counter and return carry. -TryGiveDamageCounter_DamageSwap: ; 2dc30 (b:5c30) - ldh a, [hPlayAreaEffectTarget] - add DUELVARS_ARENA_CARD_HP - call GetTurnDuelistVariable - sub 10 - jr z, .set_carry ; would bring HP to zero? -; has enough HP to receive a damage counter - ld [hl], a - ldh a, [hTempPlayAreaLocation_ffa1] - add DUELVARS_ARENA_CARD_HP - ld l, a - ld a, 10 - add [hl] - ld [hl], a - or a - ret -.set_carry - scf - ret - -PsywaveEffect: ; 2dc49 (b:5c49) - call GetEnergyAttachedMultiplierDamage - ld hl, wDamage - ld [hl], e - inc hl - ld [hl], d - ret - -; returns carry if neither Duelist has evolved Pokemon. -DevolutionBeam_CheckPlayArea: ; 2dc53 (b:5c53) - call CheckIfTurnDuelistHasEvolvedCards - ret nc - call SwapTurn - call CheckIfTurnDuelistHasEvolvedCards - call SwapTurn - ldtx hl, ThereAreNoStage1PokemonText - ret - -; returns carry of Player cancelled selection. -; otherwise, output in hTemp_ffa0 which Play Area -; was selected ($0 = own Play Area, $1 = opp. Play Area) -; and in hTempPlayAreaLocation_ffa1 selected card. -DevolutionBeam_PlayerSelectEffect: ; 2dc64 (b:5c64) - ldtx hl, ProcedureForDevolutionBeamText - bank1call DrawWholeScreenTextBox - -.start - bank1call DrawDuelMainScene - ldtx hl, PleaseSelectThePlayAreaText - call TwoItemHorizontalMenu - ldh a, [hKeysHeld] - and B_BUTTON - jr nz, .set_carry - -; a Play Area was selected - ldh a, [hCurMenuItem] - or a - jr nz, .opp_chosen - -; player chosen - call HandleEvolvedCardSelection - jr c, .start - - xor a -.store_selection - ld hl, hTemp_ffa0 - ld [hli], a ; store which Duelist Play Area selected - ldh a, [hTempPlayAreaLocation_ff9d] - ld [hl], a ; store which card selected - or a - ret - -.opp_chosen - call SwapTurn - call HandleEvolvedCardSelection - call SwapTurn - jr c, .start - ld a, $01 - jr .store_selection - -.set_carry - scf - ret - -DevolutionBeam_AISelectEffect: ; 2dc9e (b:5c9e) - ld a, $01 - ldh [hTemp_ffa0], a - call SwapTurn - call FindFirstNonBasicCardInPlayArea - call SwapTurn - jr c, .found - xor a - ldh [hTemp_ffa0], a - call FindFirstNonBasicCardInPlayArea -.found - ldh [hTempPlayAreaLocation_ffa1], a - ret - -DevolutionBeam_LoadAnimation: ; 2dcb6 (b:5cb6) - xor a ; ATK_ANIM_NONE - ld [wLoadedAttackAnimation], a - ret - -DevolutionBeam_DevolveEffect: ; 2dcbb (b:5cbb) - ldh a, [hTemp_ffa0] - or a - jr z, .DevolvePokemon - cp $ff - ret z - -; opponent's Play Area - call SwapTurn - ldh a, [hTempPlayAreaLocation_ffa1] - jr nz, .skip_handle_no_damage_effect - call HandleNoDamageOrEffect - jr c, .unaffected -.skip_handle_no_damage_effect - call .DevolvePokemon -.unaffected - call SwapTurn - ret - -.DevolvePokemon - ld a, ATK_ANIM_DEVOLUTION_BEAM - ld [wLoadedAttackAnimation], a - ldh a, [hTempPlayAreaLocation_ffa1] - ld b, a - ld c, $00 - ldh a, [hWhoseTurn] - ld h, a - bank1call PlayAttackAnimation - bank1call WaitAttackAnimation - -; load selected card's data - ldh a, [hTempPlayAreaLocation_ffa1] - ldh [hTempPlayAreaLocation_ff9d], a - ld [wTempPlayAreaLocation_cceb], a - add DUELVARS_ARENA_CARD - call GetTurnDuelistVariable - call LoadCardDataToBuffer1_FromDeckIndex - -; check if car is affected - ld a, [wLoadedCard1ID] - ld [wTempNonTurnDuelistCardID], a - ld de, $0 - ldh a, [hTempPlayAreaLocation_ff9d] - or a - jr nz, .skip_substatus_check - call HandleNoDamageOrEffectSubstatus - jr c, .check_no_damage_effect -.skip_substatus_check - call HandleDamageReductionOrNoDamageFromPkmnPowerEffects -.check_no_damage_effect - call CheckNoDamageOrEffect - jr nc, .devolve - call DrawWideTextBox_WaitForInput - ret - -.devolve - ldh a, [hTempPlayAreaLocation_ffa1] - ldh [hTempPlayAreaLocation_ff9d], a - add DUELVARS_ARENA_CARD - call GetTurnDuelistVariable - bank1call GetCardOneStageBelow - call PrintDevolvedCardNameAndLevelText - - ld a, d - call UpdateDevolvedCardHPAndStage - call ResetDevolvedCardStatus - -; add the evolved card to the hand - ld a, e - call AddCardToHand - -; check if this devolution KO's card - ldh a, [hTempPlayAreaLocation_ffa1] - call PrintPlayAreaCardKnockedOutIfNoHP - - xor a - ld [wDuelDisplayedScreen], a - ret - -; returns carry if Turn Duelist -; has no Stage1 or Stage2 cards in Play Area. -CheckIfTurnDuelistHasEvolvedCards: ; 2dd3b (b:5d3b) - ld a, DUELVARS_ARENA_CARD - call GetTurnDuelistVariable - ld d, h - ld e, DUELVARS_ARENA_CARD_STAGE -.loop - ld a, [hli] - cp $ff - jr z, .set_carry - ld a, [de] - inc de - or a - jr z, .loop ; is Basic Stage - ret -.set_carry - scf - ret - -; handles Player selection of an evolved card in Play Area. -; returns carry if Player cancelled operation. -HandleEvolvedCardSelection: ; 2dd50 (b:5d50) - bank1call HasAlivePokemonInPlayArea -.loop - bank1call OpenPlayAreaScreenForSelection - ret c - ldh a, [hTempPlayAreaLocation_ff9d] - add DUELVARS_ARENA_CARD_STAGE - call GetTurnDuelistVariable - or a - jr z, .loop ; if Basic, loop - ret - -; finds first occurrence in Play Area -; of Stage 1 or 2 card, and outputs its -; Play Area location in a, with carry set. -; if none found, don't return carry set. -FindFirstNonBasicCardInPlayArea: ; 2dd62 (b:5d62) - ld a, DUELVARS_NUMBER_OF_POKEMON_IN_PLAY_AREA - call GetTurnDuelistVariable - ld c, a - - ld b, PLAY_AREA_ARENA - ld l, DUELVARS_ARENA_CARD_STAGE -.loop - ld a, [hli] - or a - jr nz, .not_basic - inc b - dec c - jr nz, .loop - or a - ret -.not_basic - ld a, b - scf - ret - -NeutralizingShieldEffect: ; 2dd79 (b:5d79) - scf - ret - -Psychic_AIEffect: ; 2dd7b (b:5d7b) - call Psychic_DamageBoostEffect - jp SetDefiniteAIDamage - -Psychic_DamageBoostEffect: ; 2dd81 (b:5d81) - call GetEnergyAttachedMultiplierDamage - ld hl, wDamage - ld a, e - add [hl] - ld [hli], a - ld a, d - adc [hl] - ld [hl], a - ret - -; return carry if no Psychic Energy attached -Barrier_CheckEnergy: ; 2dd8e (b:5d8e) - ld e, PLAY_AREA_ARENA - call GetPlayAreaCardAttachedEnergies - ld a, [wAttachedEnergies + PSYCHIC] - ldtx hl, NotEnoughPsychicEnergyText - cp 1 - ret - -Barrier_PlayerSelectEffect: ; 2dd9c (b:5d9c) - ld a, TYPE_ENERGY_PSYCHIC - call CreateListOfEnergyAttachedToArena - xor a ; PLAY_AREA_ARENA - bank1call DisplayEnergyDiscardScreen - bank1call HandleEnergyDiscardMenuInput - ret c - ldh a, [hTempCardIndex_ff98] - ldh [hTemp_ffa0], a - ret - -Barrier_AISelectEffect: ; 2ddae (b:5dae) -; AI picks the first energy in list - ld a, TYPE_ENERGY_PSYCHIC - call CreateListOfEnergyAttachedToArena - ld a, [wDuelTempList] - ldh [hTemp_ffa0], a - ret - -Barrier_DiscardEffect: ; 2ddb9 (b:5db9) - ldh a, [hTemp_ffa0] - call PutCardInDiscardPile - ret - -Barrier_BarrierEffect: ; 2ddbf (b:5dbf) - ld a, SUBSTATUS1_BARRIER - call ApplySubstatus1ToDefendingCard - ret - -Mewtwo3EnergyAbsorption_CheckDiscardPile: ; 2ddc5 (b:5dc5) - call CreateEnergyCardListFromDiscardPile_AllEnergy - ldtx hl, ThereAreNoEnergyCardsInDiscardPileText - ret - -Mewtwo3EnergyAbsorption_PlayerSelectEffect: ; 2ddcc (b:5dcc) - ldtx hl, Choose2EnergyCardsFromDiscardPileToAttachText - call HandleEnergyCardsInDiscardPileSelection - ret - -Mewtwo3EnergyAbsorption_AISelectEffect: ; 2ddd3 (b:5dd3) -; AI picks first 2 energy cards - call CreateEnergyCardListFromDiscardPile_AllEnergy - ld hl, wDuelTempList - ld de, hTempList - ld c, 2 -.loop - ld a, [hli] - cp $ff - jr z, .done - ld [de], a - inc de - dec c - jr nz, .loop -.done - ld a, $ff ; terminating byte - ld [de], a - ret - -Mewtwo3EnergyAbsorption_AddToHandEffect: ; 2ddec (b:5dec) - ld hl, hTempList -.loop - ld a, [hli] - cp $ff - ret z - push hl - call MoveDiscardPileCardToHand - call GetTurnDuelistVariable - ld [hl], CARD_LOCATION_ARENA - pop hl - jr .loop - -Mewtwo2EnergyAbsorption_CheckDiscardPile: ; 2ddff (b:5dff) - call CreateEnergyCardListFromDiscardPile_AllEnergy - ldtx hl, ThereAreNoEnergyCardsInDiscardPileText - ret - -Mewtwo2EnergyAbsorption_PlayerSelectEffect: ; 2de06 (b:5e06) - ldtx hl, Choose2EnergyCardsFromDiscardPileToAttachText - call HandleEnergyCardsInDiscardPileSelection - ret - -Mewtwo2EnergyAbsorption_AISelectEffect: ; 2de0d (b:5e0d) -; AI picks first 2 energy cards - call CreateEnergyCardListFromDiscardPile_AllEnergy - ld hl, wDuelTempList - ld de, hTempList - ld c, 2 -.loop - ld a, [hli] - cp $ff - jr z, .done - ld [de], a - inc de - dec c - jr nz, .loop -.done - ld a, $ff ; terminating byte - ld [de], a - ret - -Mewtwo2EnergyAbsorption_AddToHandEffect: ; 2de26 (b:5e26) - ld hl, hTempList -.loop - ld a, [hli] - cp $ff - ret z - push hl - call MoveDiscardPileCardToHand - call GetTurnDuelistVariable - ld [hl], CARD_LOCATION_ARENA - pop hl - jr .loop - -; returns carry if Strange Behavior cannot be used. -StrangeBehavior_CheckDamage: ; 2de39 (b:5e39) -; does Play Area have any damage counters? - ldh a, [hTempPlayAreaLocation_ff9d] - ldh [hTemp_ffa0], a - call CheckIfPlayAreaHasAnyDamage - ldtx hl, NoPokemonWithDamageCountersText - jr c, .set_carry -; can Slowbro receive any damage counters without KO-ing? - ldh a, [hTempPlayAreaLocation_ff9d] - add DUELVARS_ARENA_CARD_HP - call GetTurnDuelistVariable - ldtx hl, CannotUseBecauseItWillBeKnockedOutText - cp 10 + 10 - jr c, .set_carry -; can Pkmn Power be used? - ldh a, [hTempPlayAreaLocation_ff9d] - call CheckCannotUseDueToStatus_OnlyToxicGasIfANon0 - ret - -.set_carry - scf - ret - -StrangeBehavior_SelectAndSwapEffect: ; 2de5b (b:5e5b) - ld a, DUELVARS_DUELIST_TYPE - call GetTurnDuelistVariable - cp DUELIST_TYPE_PLAYER - jr z, .player - -; not player - bank1call Func_61a1 - bank1call PrintPlayAreaCardList_EnableLCD - ret - -.player - ldtx hl, ProcedureForStrangeBehaviorText - bank1call DrawWholeScreenTextBox - - xor a - ldh [hCurSelectionItem], a - bank1call Func_61a1 -.start - bank1call PrintPlayAreaCardList_EnableLCD - push af - ldh a, [hCurSelectionItem] - ld hl, PlayAreaSelectionMenuParameters - call InitializeMenuParameters - pop af - - ld [wNumMenuItems], a -.loop_input - call DoFrame - call HandleMenuInput - jr nc, .loop_input - cp -1 - ret z ; return when B button is pressed - - ldh [hCurSelectionItem], a - ldh [hTempPlayAreaLocation_ffa1], a - ld hl, hTemp_ffa0 - cp [hl] - jr z, .play_sfx ; can't select Slowbro itself - - call GetCardDamageAndMaxHP - or a - jr z, .play_sfx ; can't select card without damage - - call TryGiveDamageCounter_StrangeBehavior - jr c, .play_sfx - ld a, OPPACTION_6B15 - call SetOppAction_SerialSendDuelData - jr .start - -.play_sfx - call Func_3794 - jr .loop_input - -StrangeBehavior_SwapEffect: ; 2deb3 (b:5eb3) - call TryGiveDamageCounter_StrangeBehavior - ret c - bank1call PrintPlayAreaCardList_EnableLCD - or a - ret - -; tries to give the damage counter to the target -; chosen by the Player (hTemp_ffa0). -; if the damage counter would KO card, then do -; not give the damage counter and return carry. -TryGiveDamageCounter_StrangeBehavior: ; 2debc (b:5ebc) - ldh a, [hTemp_ffa0] - add DUELVARS_ARENA_CARD_HP - call GetTurnDuelistVariable - sub 10 - jr z, .set_carry ; would bring HP to zero? -; has enough HP to receive a damage counter - ld [hl], a - ldh a, [hTempPlayAreaLocation_ffa1] - add DUELVARS_ARENA_CARD_HP - ld l, a - ld a, 10 - add [hl] - ld [hl], a - or a - ret -.set_carry - scf - ret - -; returns carry if has no damage counters. -SpacingOut_CheckDamage: ; 2ded5 (b:5ed5) - ld e, PLAY_AREA_ARENA - call GetCardDamageAndMaxHP - ldtx hl, NoDamageCountersText - cp 10 - ret - -SpacingOut_Success50PercentEffect: ; 2dee0 (b:5ee0) - ldtx de, SuccessCheckIfHeadsAttackIsSuccessfulText - call TossCoin_BankB - ldh [hTemp_ffa0], a - jp nc, SetWasUnsuccessful - ld a, ATK_ANIM_RECOVER - ld [wLoadedAttackAnimation], a - ret - -SpacingOut_HealEffect: ; 2def1 (b:5ef1) - ldh a, [hTemp_ffa0] - or a - ret z ; coin toss was tails - ld e, PLAY_AREA_ARENA - call GetCardDamageAndMaxHP - or a - ret z ; no damage counters - ld a, DUELVARS_ARENA_CARD_HP - call GetTurnDuelistVariable - add 10 - ld [hl], a - ret - -; sets carry if no Trainer cards in the Discard Pile. -Scavenge_CheckDiscardPile: ; 2df05 (b:5f05) - ld e, PLAY_AREA_ARENA - call GetPlayAreaCardAttachedEnergies - ld a, [wAttachedEnergies + PSYCHIC] - ldtx hl, NotEnoughPsychicEnergyText - cp 1 - ret c ; return if no Psychic energy attached - call CreateTrainerCardListFromDiscardPile - ldtx hl, ThereAreNoTrainerCardsInDiscardPileText ; this is redundant - ret - -Scavenge_PlayerSelectEnergyEffect: ; 2df1a (b:5f1a) - ld a, TYPE_ENERGY_PSYCHIC - call CreateListOfEnergyAttachedToArena - xor a ; PLAY_AREA_ARENA - bank1call DisplayEnergyDiscardScreen - bank1call HandleEnergyDiscardMenuInput - ret c - ldh a, [hTempCardIndex_ff98] - ldh [hTemp_ffa0], a - or a - ret - -Scavenge_AISelectEffect: ; 2df2d (b:5f2d) -; AI picks first Energy card in list - ld a, TYPE_ENERGY_PSYCHIC - call CreateListOfEnergyAttachedToArena - ld a, [wDuelTempList] - ldh [hTemp_ffa0], a -; AI picks first Trainer card in list - call CreateTrainerCardListFromDiscardPile - ld a, [wDuelTempList] - ldh [hTempPlayAreaLocation_ffa1], a - ret - -Scavenge_DiscardEffect: ; 2df40 (b:5f40) - ldh a, [hTemp_ffa0] - call PutCardInDiscardPile - ret - -Scavenge_PlayerSelectTrainerEffect: ; 2df46 (b:5f46) - call CreateTrainerCardListFromDiscardPile - bank1call Func_5591 - ldtx hl, PleaseSelectCardText - ldtx de, PlayerDiscardPileText - bank1call SetCardListHeaderText -.loop_input - bank1call DisplayCardList - jr c, .loop_input - ldh a, [hTempCardIndex_ff98] - ldh [hTempPlayAreaLocation_ffa1], a - ret - -Scavenge_AddToHandEffect: ; 2df5f (b:5f5f) - ldh a, [hTempPlayAreaLocation_ffa1] - call MoveDiscardPileCardToHand - call AddCardToHand - call IsPlayerTurn - ret c - ldh a, [hTempPlayAreaLocation_ffa1] - ldtx hl, WasPlacedInTheHandText - bank1call DisplayCardDetailScreen - ret - -; returns carry if Defending Pokemon has no attacks -SlowpokeAmnesia_CheckAttacks: ; 2df74 (b:5f74) - call CheckIfDefendingPokemonHasAnyAttack - ldtx hl, NoAttackMayBeChoosenText - ret - -SlowpokeAmnesia_PlayerSelectEffect: ; 2df7b (b:5f7b) - call PlayerPickAttackForAmnesia - ret - -SlowpokeAmnesia_AISelectEffect: ; 2df7f (b:5f7f) - call AIPickAttackForAmnesia - ldh [hTemp_ffa0], a - ret - -SlowpokeAmnesia_DisableEffect: ; 2df85 (b:5f85) - call ApplyAmnesiaToAttack - ret - -; returns carry if Arena card has no Psychic Energy attached -; or if it doesn't have any damage counters. -KadabraRecover_CheckEnergyHP: ; 2df89 (b:5f89) - ld e, PLAY_AREA_ARENA - call GetPlayAreaCardAttachedEnergies - ld a, [wAttachedEnergies + PSYCHIC] - ldtx hl, NotEnoughPsychicEnergyText - cp 1 - ret c ; return if not enough energy - call GetCardDamageAndMaxHP - ldtx hl, NoDamageCountersText - cp 10 - ret ; return carry if no damage - -KadabraRecover_PlayerSelectEffect: ; 2dfa0 (b:5fa0) - ld a, TYPE_ENERGY_PSYCHIC - call CreateListOfEnergyAttachedToArena - xor a ; PLAY_AREA_ARENA - bank1call DisplayEnergyDiscardScreen - bank1call HandleEnergyDiscardMenuInput - ret c - ldh a, [hTempCardIndex_ff98] - ldh [hTemp_ffa0], a ; store card chosen - ret - -KadabraRecover_AISelectEffect: ; 2dfb2 (b:5fb2) - ld a, TYPE_ENERGY_PSYCHIC - call CreateListOfEnergyAttachedToArena - ld a, [wDuelTempList] ; pick first card - ldh [hTemp_ffa0], a - ret - -KadabraRecover_DiscardEffect: ; 2dfbd (b:5fbd) - ldh a, [hTemp_ffa0] - call PutCardInDiscardPile - ret - -KadabraRecover_HealEffect: ; 2dfc3 (b:5fc3) - ld e, PLAY_AREA_ARENA - call GetCardDamageAndMaxHP - ld e, a ; all damage for recovery - ld d, 0 - call ApplyAndAnimateHPRecovery - ret - -JynxDoubleslap_AIEffect: ; 2dfd7 (b:5fd7) - ld a, 20 / 2 - lb de, 0, 20 - jp SetExpectedAIDamage - -JynxDoubleslap_MultiplierEffect: ; 2dfcf (b:5fcf) - ld hl, 10 - call LoadTxRam3 - ldtx de, DamageCheckIfHeadsXDamageText - ld a, 2 - call TossCoinATimes_BankB - call ATimes10 - call SetDefiniteDamage - ret - -JynxMeditate_AIEffect: ; 2dff2 (b:5ff2) - call JynxMeditate_DamageBoostEffect - jp SetDefiniteAIDamage - -JynxMeditate_DamageBoostEffect: ; 2dfec (b:5fec) -; add damage counters of Defending card to damage - call SwapTurn - ld e, PLAY_AREA_ARENA - call GetCardDamageAndMaxHP - call SwapTurn - call AddToDamage - ret - -MysteryAttack_AIEffect: ; 2e001 (b:6001) - ld a, 10 - lb de, 0, 20 - jp SetExpectedAIDamage - -MysteryAttack_RandomEffect: ; 2e009 (b:6009) - ld a, 10 - call SetDefiniteDamage - -; chooses a random effect from 8 possible options. - call UpdateRNGSources - and %111 - ldh [hTemp_ffa0], a - ld hl, .random_effect - jp JumpToFunctionInTable - -.random_effect - dw ParalysisEffect - dw PoisonEffect - dw SleepEffect - dw ConfusionEffect - dw .no_effect ; this will actually activate recovery effect afterwards - dw .no_effect - dw .more_damage - dw .no_damage - -.more_damage - ld a, 20 - call SetDefiniteDamage - ret - -.no_damage - ld a, ATK_ANIM_GLOW_EFFECT - ld [wLoadedAttackAnimation], a - xor a - call SetDefiniteDamage - call SetNoEffectFromStatus -.no_effect - ret - -MysteryAttack_RecoverEffect: ; 2e03e (b:603e) -; in case the 5th option was chosen for random effect, -; trigger recovery effect for 10 HP. - ldh a, [hTemp_ffa0] - cp 4 - ret nz - lb de, 0, 10 - call ApplyAndAnimateHPRecovery - ret - -StoneBarrage_AIEffect: ; 2e04a (b:604a) - ld a, 10 - lb de, 0, 100 - jp SetExpectedAIDamage - -StoneBarrage_MultiplierEffect: ; 2e052 (b:6052) - xor a - ldh [hTemp_ffa0], a -.loop_coin_toss - ldtx de, FlipUntilFailAppears10DamageForEachHeadsText - xor a - call TossCoinATimes_BankB - jr nc, .tails - ld hl, hTemp_ffa0 - inc [hl] ; increase heads count - jr .loop_coin_toss - -.tails -; store resulting damage - ldh a, [hTemp_ffa0] - ld l, a - ld h, 10 - call HtimesL - ld de, wDamage - ld a, l - ld [de], a - inc de - ld a, h - ld [de], a - ret - -OnixHardenEffect: ; 2e075 (b:6075) - ld a, SUBSTATUS1_HARDEN - call ApplySubstatus1ToDefendingCard - ret - -PrimeapeFurySwipes_AIEffect: ; 2e07b (b:607b) - ld a, 60 / 2 - lb de, 0, 60 - jp SetExpectedAIDamage - -PrimeapeFurySwipes_MultiplierEffect: ; 2e083 (b:6083) - ld hl, 20 - call LoadTxRam3 - ldtx de, DamageCheckIfHeadsXDamageText - ld a, 3 - call TossCoinATimes_BankB - add a - call ATimes10 - call SetDefiniteDamage - ret - -TantrumEffect: ; 2e099 (b:6099) - ldtx de, IfTailsYourPokemonBecomesConfusedText - call TossCoin_BankB - ret c ; return if heads -; confuse Pokemon - ld a, ATK_ANIM_MULTIPLE_SLASH - ld [wLoadedAttackAnimation], a - call SwapTurn - call ConfusionEffect - call SwapTurn - ret - -StrikesBackEffect: ; 2e0af (b:60af) - scf - ret - -KabutoArmorEffect: ; 2e0b1 (b:60b1) - scf - ret - -AbsorbEffect: ; 2e0b3 (b:60b3) - ld hl, wDealtDamage - ld a, [hli] - ld h, [hl] - ld l, a - srl h - rr l - bit 0, l - jr z, .rounded - ; round up to nearest 10 - ld de, 5 - add hl, de -.rounded - ld e, l - ld d, h - call ApplyAndAnimateHPRecovery - ret - -SnivelEffect: ; 2e0cb (b:60cb) - ld a, SUBSTATUS2_REDUCE_BY_20 - call ApplySubstatus2ToDefendingCard - ret - -CuboneRage_AIEffect: ; 2e0d1 (b:60d1) - call CuboneRage_DamageBoostEffect - jp SetDefiniteAIDamage - -CuboneRage_DamageBoostEffect: ; 2e0d7 (b:60d7) - ld e, PLAY_AREA_ARENA - call GetCardDamageAndMaxHP - call AddToDamage - ret - -Bonemerang_AIEffect: ; 2e0e0 (b:60e0) - ld a, 60 / 2 - lb de, 0, 60 - jp SetExpectedAIDamage - -Bonemerang_MultiplierEffect: ; 2e0e8 (b:60e8) - ld hl, 30 - call LoadTxRam3 - ldtx de, DamageCheckIfHeadsXDamageText - ld a, 2 - call TossCoinATimes_BankB - ld e, a - add a ; a = 2 * heads - add e ; a = 3 * heads - call ATimes10 - call SetDefiniteDamage - ret - -; returns carry if can't add Pokemon from deck -MarowakCallForFamily_CheckDeckAndPlayArea: ; 2e100 (b:6100) - call CheckIfDeckIsEmpty - ret c ; no cards in deck - ld a, DUELVARS_NUMBER_OF_POKEMON_IN_PLAY_AREA - call GetTurnDuelistVariable - ldtx hl, NoSpaceOnTheBenchText - cp MAX_PLAY_AREA_POKEMON - ccf - ret - -MarowakCallForFamily_PlayerSelectEffect: ; 2e110 (b:6110) - ld a, $ff - ldh [hTemp_ffa0], a - - call CreateDeckCardList - ldtx hl, ChooseBasicFightingPokemonFromDeckText - ldtx bc, FightingPokemonDeckText - lb de, SEARCHEFFECT_BASIC_FIGHTING, $00 - call LookForCardsInDeck - ret c - -; draw Deck list interface and print text - bank1call Func_5591 - ldtx hl, ChooseBasicFightingPokemonText - ldtx de, DuelistDeckText - bank1call SetCardListHeaderText - -.loop - bank1call DisplayCardList - jr c, .pressed_b - - call LoadCardDataToBuffer2_FromDeckIndex - ld a, [wLoadedCard2Type] - cp FIGHTING - jr nz, .play_sfx ; is Fighting? - ld a, [wLoadedCard2Stage] - or a - jr nz, .play_sfx ; is Basic? - ldh a, [hTempCardIndex_ff98] - ldh [hTemp_ffa0], a - or a - ret - -.play_sfx - ; play SFX and loop back - call Func_3794 - jr .loop - -.pressed_b -; figure if Player can exit the screen without selecting, -; that is, if the Deck has no Basic Fighting Pokemon. - ld a, DUELVARS_CARD_LOCATIONS - call GetTurnDuelistVariable -.loop_b_press - ld a, [hl] - cp CARD_LOCATION_DECK - jr nz, .next - ld a, l - call LoadCardDataToBuffer2_FromDeckIndex - ld a, [wLoadedCard1Type] - cp FIGHTING - jr nz, .next ; found, go back to top loop - ld a, [wLoadedCard1Stage] - or a - jr z, .play_sfx ; found, go back to top loop -.next - inc l - ld a, l - cp DECK_SIZE - jr c, .loop_b_press - -; no valid card in Deck, can safely exit screen - ld a, $ff - ldh [hTemp_ffa0], a - or a - ret - -MarowakCallForFamily_AISelectEffect: ; 2e177 (b:6177) - call CreateDeckCardList - ld hl, wDuelTempList -.loop_deck - ld a, [hli] - ldh [hTemp_ffa0], a - cp $ff - ret z ; none found - call LoadCardDataToBuffer2_FromDeckIndex - ld a, [wLoadedCard2Type] - cp FIGHTING - jr nz, .loop_deck - ld a, [wLoadedCard2Stage] - or a - jr nz, .loop_deck -; found - ret - -MarowakCallForFamily_PutInPlayAreaEffect: ; 2e194 (b:6194) - ldh a, [hTemp_ffa0] - cp $ff - jr z, .shuffle - call SearchCardInDeckAndAddToHand - call AddCardToHand - call PutHandPokemonCardInPlayArea - call IsPlayerTurn - jr c, .shuffle - ; display card on screen - ldh a, [hTemp_ffa0] - ldtx hl, PlacedOnTheBenchText - bank1call DisplayCardDetailScreen -.shuffle - call Func_2c0bd - ret - -KarateChop_AIEffect: ; 2e1b4 (b:61b4) - call KarateChop_DamageSubtractionEffect - jp SetDefiniteAIDamage - -KarateChop_DamageSubtractionEffect: ; 2e1ba (b:61ba) - ld e, PLAY_AREA_ARENA - call GetCardDamageAndMaxHP - ld e, a - ld hl, wDamage - ld a, [hl] - sub e - ld [hli], a - ld a, [hl] - sbc 0 - ld [hl], a - rla - ret nc -; cap it to 0 damage - xor a - call SetDefiniteDamage - ret - -SubmissionEffect: ; 2e1d1 (b:61d1) - ld a, 20 - call DealRecoilDamageToSelf - ret - -GolemSelfdestructEffect: ; 2e1d7 (b:61d7) - ld a, 100 - call DealRecoilDamageToSelf - ld a, $01 - ld [wIsDamageToSelf], a - ld a, 20 - call DealDamageToAllBenchedPokemon - call SwapTurn - xor a - ld [wIsDamageToSelf], a - ld a, 20 - call DealDamageToAllBenchedPokemon - call SwapTurn - ret - -GravelerHardenEffect: ; 2e1f6 (b:61f6) - ld a, SUBSTATUS1_HARDEN - call ApplySubstatus1ToDefendingCard - ret - -Ram_SelectSwitchEffect: ; 2e1fc (b:61fc) - ld a, DUELVARS_NUMBER_OF_POKEMON_IN_PLAY_AREA - call GetNonTurnDuelistVariable - cp 2 - jr c, .no_bench - call DuelistSelectForcedSwitch - ldh a, [hTempPlayAreaLocation_ff9d] - ldh [hTemp_ffa0], a - ret -.no_bench - ld a, $ff - ldh [hTemp_ffa0], a - ret - -Ram_RecoilSwitchEffect: ; 2e212 (b:6212) - ld a, 20 - call DealRecoilDamageToSelf - ldh a, [hTemp_ffa0] - call HandleSwitchDefendingPokemonEffect - ret - -LeerEffect: ; 2e21d (b:621d) - ldtx de, IfHeadsOpponentCannotAttackText - call TossCoin_BankB - jp nc, SetWasUnsuccessful - ld a, ATK_ANIM_LEER - ld [wLoadedAttackAnimation], a - ld a, SUBSTATUS2_LEER - call ApplySubstatus2ToDefendingCard - ret - -; return carry if opponent has no Bench Pokemon. -StretchKick_CheckBench: ; 2e231 (b:6231) - ld a, DUELVARS_NUMBER_OF_POKEMON_IN_PLAY_AREA - call GetNonTurnDuelistVariable - ldtx hl, EffectNoPokemonOnTheBenchText - cp 2 - ret - -StretchKick_PlayerSelectEffect: ; 2e23c (b:623c) - ldtx hl, ChoosePkmnInTheBenchToGiveDamageText - call DrawWideTextBox_WaitForInput - call SwapTurn - bank1call HasAlivePokemonInBench -.loop_input - bank1call OpenPlayAreaScreenForSelection - jr c, .loop_input - ldh a, [hTempPlayAreaLocation_ff9d] - ldh [hTemp_ffa0], a - call SwapTurn - ret - -StretchKick_AISelectEffect: ; 2e255 (b:6255) -; chooses Bench Pokemon with least amount of remaining HP - call GetBenchPokemonWithLowestHP - ldh [hTemp_ffa0], a - ret - -StretchKick_BenchDamageEffect: ; 2e25b (b:625b) - call SwapTurn - ldh a, [hTemp_ffa0] - ld b, a - ld de, 20 - call DealDamageToPlayAreaPokemon_RegularAnim - call SwapTurn - ret - -SandAttackEffect: ; 2e26b (b:626b) - ld a, SUBSTATUS2_SAND_ATTACK - call ApplySubstatus2ToDefendingCard - ret - -SandslashFurySwipes_AIEffect: ; 2e271 (b:6271) - ld a, 60 / 2 - lb de, 0, 60 - jp SetExpectedAIDamage - -SandslashFurySwipes_MultiplierEffect: ; 2e279 (b:6279) - ld hl, 20 - call LoadTxRam3 - ldtx de, DamageCheckIfHeadsXDamageText - ld a, 3 - call TossCoinATimes_BankB - add a - call ATimes10 - call SetDefiniteDamage - ret - -EarthquakeEffect: ; 2e28f (b:628f) - ld a, $01 - ld [wIsDamageToSelf], a - ld a, 10 - call DealDamageToAllBenchedPokemon - ret - -PrehistoricPowerEffect: ; 2e29a (b:629a) - scf - ret - -; returns carry if Pkmn Power can't be used. -Peek_OncePerTurnCheck: ; 2e29c (b:629c) - ldh a, [hTempPlayAreaLocation_ff9d] - ldh [hTemp_ffa0], a - add DUELVARS_ARENA_CARD_FLAGS - call GetTurnDuelistVariable - and USED_PKMN_POWER_THIS_TURN - jr nz, .already_used - ldh a, [hTempPlayAreaLocation_ff9d] - call CheckCannotUseDueToStatus_OnlyToxicGasIfANon0 - ret -.already_used - ldtx hl, OnlyOncePerTurnText - scf - ret - -Peek_SelectEffect: ; 2e2b4 (b:62b4) -; set Pkmn Power used flag - ldh a, [hTemp_ffa0] - add DUELVARS_ARENA_CARD_FLAGS - call GetTurnDuelistVariable - set USED_PKMN_POWER_THIS_TURN_F, [hl] - - ld a, DUELVARS_DUELIST_TYPE - call GetTurnDuelistVariable - cp DUELIST_TYPE_LINK_OPP - jr z, .link_opp - and DUELIST_TYPE_AI_OPP - jr nz, .ai_opp - -; player - call Func_3b31 - call HandlePeekSelection - ldh [hAIPkmnPowerEffectParam], a - call SerialSend8Bytes - ret - -.link_opp - call SerialRecv8Bytes - ldh [hAIPkmnPowerEffectParam], a - -.ai_opp - ldh a, [hAIPkmnPowerEffectParam] - bit AI_PEEK_TARGET_HAND_F, a - jr z, .prize_or_deck - and (~AI_PEEK_TARGET_HAND & $ff) ; unset bit to get deck index -; if masked value is higher than $40, then it means -; that AI chose to look at Player's deck. -; all deck indices will be smaller than $40. - cp $40 - jr c, .hand - ldh a, [hAIPkmnPowerEffectParam] - jr .prize_or_deck - -.hand -; AI chose to look at random card in hand, -; so display it to the Player on screen. - call SwapTurn - ldtx hl, PeekWasUsedToLookInYourHandText - bank1call DisplayCardDetailScreen - call SwapTurn - ret - -.prize_or_deck -; AI chose either a prize card or Player's top deck card, -; so show Play Area and draw cursor appropriately. - call Func_3b31 - call SwapTurn - ldh a, [hAIPkmnPowerEffectParam] - xor $80 - call DrawAIPeekScreen - call SwapTurn - ldtx hl, CardPeekWasUsedOnText - call DrawWideTextBox_WaitForInput - ret - -BoneAttackEffect: ; 2e30f (b:630f) - ldtx de, IfHeadsOpponentCannotAttackText - call TossCoin_BankB - ret nc - ld a, SUBSTATUS2_BONE_ATTACK - call ApplySubstatus2ToDefendingCard - ret - -; return carry if neither Play Area -; has room for more Bench Pokemon. -Wail_BenchCheck: ; 2e31c (b:631c) - ld a, DUELVARS_NUMBER_OF_POKEMON_IN_PLAY_AREA - call GetTurnDuelistVariable - cp MAX_PLAY_AREA_POKEMON - jr c, .no_carry - ld a, DUELVARS_NUMBER_OF_POKEMON_IN_PLAY_AREA - call GetNonTurnDuelistVariable - cp MAX_PLAY_AREA_POKEMON - jr c, .no_carry - ldtx hl, NoSpaceOnTheBenchText - scf - ret -.no_carry - or a - ret - -Wail_FillBenchEffect: ; 2e335 (b:6335) - call SwapTurn - call .FillBench - call SwapTurn - call .FillBench - -; display both Play Areas - ldtx hl, BasicPokemonWasPlacedOnEachBenchText - call DrawWideTextBox_WaitForInput - bank1call HasAlivePokemonInPlayArea - bank1call OpenPlayAreaScreenForSelection - call SwapTurn - bank1call HasAlivePokemonInPlayArea - bank1call OpenPlayAreaScreenForSelection - call SwapTurn - ret - -.FillBench ; 2e35a (b:635a) - call CreateDeckCardList - ret c - ld hl, wDuelTempList - call ShuffleCards - -; if no more space in the Bench, then return. -.check_bench - push hl - ld a, DUELVARS_NUMBER_OF_POKEMON_IN_PLAY_AREA - call GetTurnDuelistVariable - pop hl - cp MAX_PLAY_AREA_POKEMON - jr nc, .done - -; there's still space, so look for the next -; Basic Pokemon card to put in the Bench. -.loop - ld a, [hli] - ldh [hTempCardIndex_ff98], a - cp $ff - jr z, .done - call LoadCardDataToBuffer2_FromDeckIndex - ld a, [wLoadedCard2Type] - cp TYPE_ENERGY - jr nc, .loop ; is Pokemon card? - ld a, [wLoadedCard2Stage] - or a - jr nz, .loop ; is Basic? -; place card in Bench - push hl - ldh a, [hTempCardIndex_ff98] - call SearchCardInDeckAndAddToHand - call AddCardToHand - call PutHandPokemonCardInPlayArea - pop hl - jr .check_bench - -.done - call Func_2c0bd - ret - -Thunderpunch_AIEffect: ; 2e399 (b:6399) - ld a, (30 + 40) / 2 - lb de, 30, 40 - jp SetExpectedAIDamage - -Thunderpunch_ModifierEffect: ; 2e3a1 (b:63a1) - ldtx de, IfHeadPlus10IfTails10ToYourselfText - call TossCoin_BankB - ldh [hTemp_ffa0], a - ret nc ; return if got tails - ld a, 10 - call AddToDamage - ret - -Thunderpunch_RecoilEffect: ; 2e3b0 (b:63b0) - ldh a, [hTemp_ffa0] - or a - ret nz ; return if got heads - ld a, 10 - call DealRecoilDamageToSelf - ret - -LightScreenEffect: ; 2e3ba (b:63ba) - ld a, SUBSTATUS1_HALVE_DAMAGE - call ApplySubstatus1ToDefendingCard - ret - -ElectabuzzQuickAttack_AIEffect: ; 2e3c0 (b:63c0) - ld a, (10 + 30) / 2 - lb de, 10, 30 - jp SetExpectedAIDamage - -ElectabuzzQuickAttack_DamageBoostEffect: ; 2e3c8 (b:63c8) - ld hl, 20 - call LoadTxRam3 - ldtx de, DamageCheckIfHeadsPlusDamageText - call TossCoin_BankB - ret nc ; return if tails - ld a, 20 - call AddToDamage - ret - -MagnemiteSelfdestructEffect: ; 2e3db (b:63db) - ld a, 40 - call DealRecoilDamageToSelf - - ld a, $01 - ld [wIsDamageToSelf], a - ld a, 10 - call DealDamageToAllBenchedPokemon - call SwapTurn - - xor a - ld [wIsDamageToSelf], a - ld a, 10 - call DealDamageToAllBenchedPokemon - call SwapTurn - ret - -ZapdosThunder_Recoil50PercentEffect: ; 2e3fa (b:63fa) - ld hl, 30 - call LoadTxRam3 - ldtx de, IfTailsDamageToYourselfTooText - call TossCoin_BankB - ldh [hTemp_ffa0], a - ret - -ZapdosThunder_RecoilEffect: ; 2e409 (b:6409) - ld hl, 30 - call LoadTxRam3 - ldh a, [hTemp_ffa0] - or a - ret nz ; return if got heads - ld a, 30 - call DealRecoilDamageToSelf - ret - -ThunderboltEffect: ; 2e419 (b:6419) - xor a - call CreateArenaOrBenchEnergyCardList - ld hl, wDuelTempList -; put all energy cards in Discard Pile -.loop - ld a, [hli] - cp $ff - ret z - call PutCardInDiscardPile - jr .loop - -ThunderstormEffect: ; 2e429 (b:6429) - ld a, 1 - ldh [hCurSelectionItem], a - - call SwapTurn - ld a, DUELVARS_NUMBER_OF_POKEMON_IN_PLAY_AREA - call GetTurnDuelistVariable - ld c, a - ld b, 0 - ld e, b - jr .next_pkmn - -.check_damage - push de - push bc - call .DisplayText - ld de, $0 - call SwapTurn - call TossCoin_BankB - call SwapTurn - push af - call GetNextPositionInTempList - pop af - ld [hl], a ; store result in list - pop bc - pop de - jr c, .next_pkmn - inc b ; increase number of tails - -.next_pkmn - inc e - dec c - jr nz, .check_damage - -; all coins were tossed for each Benched Pokemon - call GetNextPositionInTempList - ld [hl], $ff - ld a, b - ldh [hTemp_ffa0], a - call Func_3b21 - call SwapTurn - -; tally recoil damage - ldh a, [hTemp_ffa0] - or a - jr z, .skip_recoil - ; deal number of tails times 10 to self - call ATimes10 - call DealRecoilDamageToSelf -.skip_recoil - -; deal damage for Bench Pokemon that got heads - call SwapTurn - ld hl, hTempPlayAreaLocation_ffa1 - ld b, PLAY_AREA_BENCH_1 -.loop_bench - ld a, [hli] - cp $ff - jr z, .done - or a - jr z, .skip_damage ; skip if tails - ld de, 20 - call DealDamageToPlayAreaPokemon_RegularAnim -.skip_damage - inc b - jr .loop_bench - -.done - call SwapTurn - ret - -; displays text for current Bench Pokemon, -; printing its Bench number and name. -.DisplayText ; 2e491 (b:6491) - ld b, e - ldtx hl, BenchText - ld de, wDefaultText - call CopyText - ld a, $30 ; 0 FW character - add b - ld [de], a - inc de - ld a, $20 ; space FW character - ld [de], a - inc de - - ld a, DUELVARS_ARENA_CARD - add b - call GetTurnDuelistVariable - call LoadCardDataToBuffer2_FromDeckIndex - ld hl, wLoadedCard2Name - ld a, [hli] - ld h, [hl] - ld l, a - call CopyText - - xor a - ld [wDuelDisplayedScreen], a - ret - -JolteonQuickAttack_AIEffect: ; 2e4bb (b:64bb) - ld a, (10 + 30) / 2 - lb de, 10, 30 - jp SetExpectedAIDamage - -JolteonQuickAttack_DamageBoostEffect: ; 2e4c3 (b:64c3) - ld hl, 20 - call LoadTxRam3 - ldtx de, DamageCheckIfHeadsPlusDamageText - call TossCoin_BankB - ret nc ; return if tails - ld a, 20 - call AddToDamage - ret - -PinMissile_AIEffect: ; 2e4d6 (b:64d6) - ld a, (20 * 4) / 2 - lb de, 0, 80 - jp SetExpectedAIDamage - -PinMissile_MultiplierEffect: ; 2e4de (b:64de) - ld hl, 20 - call LoadTxRam3 - ldtx de, DamageCheckIfHeadsXDamageText - ld a, 4 - call TossCoinATimes_BankB - add a ; a = 2 * heads - call ATimes10 - call SetDefiniteDamage - ret - -Fly_AIEffect: ; 2e4f4 (b:64f4) - ld a, 30 / 2 - lb de, 0, 30 - jp SetExpectedAIDamage - -Fly_Success50PercentEffect: ; 2e4fc (b:64fc) - ldtx de, SuccessCheckIfHeadsAttackIsSuccessfulText - call TossCoin_BankB - jr c, .heads - xor a ; ATK_ANIM_NONE - ld [wLoadedAttackAnimation], a - call SetDefiniteDamage - call SetWasUnsuccessful - ret -.heads - ld a, ATK_ANIM_AGILITY_PROTECT - ld [wLoadedAttackAnimation], a - ld a, SUBSTATUS1_FLY - call ApplySubstatus1ToDefendingCard - ret - -ThunderJolt_Recoil50PercentEffect: ; 2e51a (b:651a) - ld hl, 10 - call LoadTxRam3 - ldtx de, IfTailsDamageToYourselfTooText - call TossCoin_BankB - ldh [hTemp_ffa0], a - ret - -ThunderJolt_RecoilEffect: ; 2e529 (b:6529) - ld hl, 10 - call LoadTxRam3 - ldh a, [hTemp_ffa0] - or a - ret nz ; return if was heads - ld a, 10 - call DealRecoilDamageToSelf - ret - -Spark_PlayerSelectEffect: ; 2e539 (b:6539) - ld a, $ff - ldh [hTemp_ffa0], a - ld a, DUELVARS_NUMBER_OF_POKEMON_IN_PLAY_AREA - call GetNonTurnDuelistVariable - cp 2 - ret c ; has no Bench Pokemon - - ldtx hl, ChoosePkmnInTheBenchToGiveDamageText - call DrawWideTextBox_WaitForInput - call SwapTurn - bank1call HasAlivePokemonInBench - - ; the following two instructions can be removed - ; since Player selection will overwrite it. - ld a, PLAY_AREA_BENCH_1 - ldh [hTempPlayAreaLocation_ff9d], a - -.loop_input - bank1call OpenPlayAreaScreenForSelection - jr c, .loop_input - ldh a, [hTempPlayAreaLocation_ff9d] - ldh [hTemp_ffa0], a - call SwapTurn - ret - -Spark_AISelectEffect: ; 2e562 (b:6562) - ld a, $ff - ldh [hTemp_ffa0], a - ld a, DUELVARS_NUMBER_OF_POKEMON_IN_PLAY_AREA - call GetNonTurnDuelistVariable - cp 2 - ret c ; has no Bench Pokemon -; AI always picks Pokemon with lowest HP remaining - call GetBenchPokemonWithLowestHP - ldh [hTemp_ffa0], a - ret - -Spark_BenchDamageEffect: ; 2e574 (b:6574) - ldh a, [hTemp_ffa0] - cp $ff - ret z - call SwapTurn - ldh a, [hTemp_ffa0] - ld b, a - ld de, 10 - call DealDamageToPlayAreaPokemon_RegularAnim - call SwapTurn - ret - -Pikachu3GrowlEffect: ; 2e589 (b:6589) - ld a, SUBSTATUS2_GROWL - call ApplySubstatus2ToDefendingCard - ret - -Pikachu4GrowlEffect: ; 2e58f (b:658f) - ld a, SUBSTATUS2_GROWL - call ApplySubstatus2ToDefendingCard - ret - -ChainLightningEffect: ; 2e595 (b:6595) - ld a, 10 - call SetDefiniteDamage - call SwapTurn - call GetArenaCardColor - call SwapTurn - ldh [hCurSelectionItem], a - cp COLORLESS - ret z ; don't damage if colorless - -; opponent's Bench - call SwapTurn - call .DamageSameColorBench - call SwapTurn - -; own Bench - ld a, $01 - ld [wIsDamageToSelf], a - call .DamageSameColorBench - ret - -.DamageSameColorBench ; 2e5ba (b:65ba) - ld a, DUELVARS_NUMBER_OF_POKEMON_IN_PLAY_AREA - call GetTurnDuelistVariable - ld e, a - ld d, PLAY_AREA_ARENA - jr .next_bench - -.check_damage - ld a, d - call GetPlayAreaCardColor - ld c, a - ldh a, [hCurSelectionItem] - cp c - jr nz, .next_bench ; skip if not same color -; apply damage to this Bench card - push de - ld b, d - ld de, 10 - call DealDamageToPlayAreaPokemon_RegularAnim - pop de - -.next_bench - inc d - dec e - jr nz, .check_damage - ret - -RaichuAgilityEffect: ; 2e5dc (b:65dc) - ldtx de, IfHeadsDoNotReceiveDamageOrEffectText - call TossCoin_BankB - ret nc ; skip if got tails - ld a, ATK_ANIM_AGILITY_PROTECT - ld [wLoadedAttackAnimation], a - ld a, SUBSTATUS1_AGILITY - call ApplySubstatus1ToDefendingCard - ret - -RaichuThunder_Recoil50PercentEffect: ; 2e5ee (b:65ee) - ld hl, 30 - call LoadTxRam3 - ldtx de, IfTailsDamageToYourselfTooText - call TossCoin_BankB - ldh [hTemp_ffa0], a - ret - -RaichuThunder_RecoilEffect: ; 2e5fd (b:65fd) - ld hl, 30 - call LoadTxRam3 - ldh a, [hTemp_ffa0] - or a - ret nz ; return if got heads - ld a, 30 - call DealRecoilDamageToSelf - ret - -Gigashock_PlayerSelectEffect: ; 2e60d (b:660d) - call SwapTurn - ld a, DUELVARS_NUMBER_OF_POKEMON_IN_PLAY_AREA - call GetTurnDuelistVariable - cp 2 - jr nc, .has_bench - call SwapTurn - ld a, $ff - ldh [hTempList], a - ret - -.has_bench - ldtx hl, ChooseUpTo3PkmnOnBenchToGiveDamageText - call DrawWideTextBox_WaitForInput - -; init number of items in list and cursor position - xor a - ldh [hCurSelectionItem], a - ld [wce72], a - bank1call Func_61a1 -.start - bank1call PrintPlayAreaCardList_EnableLCD - push af - ld a, [wce72] - ld hl, BenchSelectionMenuParameters - call InitializeMenuParameters - pop af - -; exclude Arena Pokemon from number of items - dec a - ld [wNumMenuItems], a - -.loop_input - call DoFrame - call HandleMenuInput - jr nc, .loop_input - cp -1 - jr z, .try_cancel - - ld [wce72], a - call .CheckIfChosenAlready - jr nc, .not_chosen - ; play SFX - call Func_3794 - jr .loop_input - -.not_chosen -; mark this Play Area location - ldh a, [hCurMenuItem] - inc a - ld b, SYM_LIGHTNING - call DrawSymbolOnPlayAreaCursor -; store it in the list of chosen Bench Pokemon - call GetNextPositionInTempList - ldh a, [hCurMenuItem] - inc a - ld [hl], a - -; check if 3 were chosen already - ldh a, [hCurSelectionItem] - ld c, a - cp 3 - jr nc, .chosen ; check if already chose 3 - - ld a, DUELVARS_NUMBER_OF_POKEMON_IN_PLAY_AREA - call GetTurnDuelistVariable - dec a - cp c - jr nz, .start ; if sill more options available, loop back - ; fallthrough if no other options available to choose - -.chosen - ldh a, [hCurMenuItem] - inc a - call Func_2c10b - ldh a, [hKeysPressed] - and B_BUTTON - jr nz, .try_cancel - call SwapTurn - call GetNextPositionInTempList - ld [hl], $ff ; terminating byte - ret - -.try_cancel - ldh a, [hCurSelectionItem] - or a - jr z, .start ; none selected, can safely loop back to start - -; undo last selection made - dec a - ldh [hCurSelectionItem], a - ld e, a - ld d, $00 - ld hl, hTempList - add hl, de - ld a, [hl] - - push af - ld b, SYM_SPACE - call DrawSymbolOnPlayAreaCursor - call EraseCursor - pop af - - dec a - ld [wce72], a - jr .start - -; returns carry if Bench Pokemon -; in register a was already chosen. -.CheckIfChosenAlready: ; 2e6af (b:66af) - inc a - ld c, a - ldh a, [hCurSelectionItem] - ld b, a - ld hl, hTempList - inc b - jr .next_check -.check_chosen - ld a, [hli] - cp c - scf - ret z ; return if chosen already -.next_check - dec b - jr nz, .check_chosen - or a - ret - -Gigashock_AISelectEffect: ; 2e6c3 (b:66c3) -; if Bench has 3 Pokemon or less, no need for selection, -; since AI will choose them all. - ld a, DUELVARS_NUMBER_OF_POKEMON_IN_PLAY_AREA - call GetNonTurnDuelistVariable - cp MAX_PLAY_AREA_POKEMON - 1 - jr nc, .start_selection - -; select them all - ld hl, hTempList - ld b, PLAY_AREA_ARENA - jr .next_bench -.select_bench - ld [hl], b - inc hl -.next_bench - inc b - dec a - jr nz, .select_bench - ld [hl], $ff ; terminating byte - ret - -.start_selection -; has more than 3 Bench cards, proceed to sort them -; by lowest remaining HP to highest, and pick first 3. - call SwapTurn - dec a - ld c, a - ld b, PLAY_AREA_BENCH_1 - -; first select all of the Bench Pokemon and write to list - ld hl, hTempList -.loop_all - ld [hl], b - inc hl - inc b - dec c - jr nz, .loop_all - ld [hl], $00 ; end list with $00 - -; then check each of the Bench Pokemon HP -; sort them from lowest remaining HP to highest. - ld de, hTempList -.loop_outer - ld a, [de] - add DUELVARS_ARENA_CARD_HP - call GetTurnDuelistVariable - ld c, a - ld l, e - ld h, d - inc hl - -.loop_inner - ld a, [hli] - or a - jr z, .next ; reaching $00 means it's end of list - - push hl - add DUELVARS_ARENA_CARD_HP - call GetTurnDuelistVariable - pop hl - cp c - jr c, .loop_inner - ; a Bench Pokemon was found with less HP - ld c, a ; store its HP - -; switch the two - dec hl - ld b, [hl] - ld a, [de] - ld [hli], a - ld a, b - ld [de], a - jr .loop_inner - -.next - inc de - ld a, [de] - or a - jr nz, .loop_outer - -; done - ld a, $ff ; terminating byte - ldh [hTempList + 3], a - call SwapTurn - ret - -Gigashock_BenchDamageEffect: ; 2e71f (b:671f) - call SwapTurn - ld hl, hTempList -.loop_selection - ld a, [hli] - cp $ff - jr z, .done - push hl - ld b, a - ld de, 10 - call DealDamageToPlayAreaPokemon_RegularAnim - pop hl - jr .loop_selection -.done - call SwapTurn - ret - -Magneton1SelfdestructEffect: ; 2e739 (b:6739) - ld a, 80 - call DealRecoilDamageToSelf - -; own bench - ld a, $01 - ld [wIsDamageToSelf], a - ld a, 20 - call DealDamageToAllBenchedPokemon - -; opponent's bench - call SwapTurn - xor a - ld [wIsDamageToSelf], a - ld a, 20 - call DealDamageToAllBenchedPokemon - call SwapTurn - ret - -MagnetonSonicboom_UnaffectedByColorEffect: ; 2e758 (b:6758) - ld hl, wDamage + 1 - set UNAFFECTED_BY_WEAKNESS_RESISTANCE_F, [hl] - ret - -MagnetonSonicboom_NullEffect: ; 2e75e (b:675e) - ret - -Magneton2SelfdestructEffect: ; 2e75f (b:675f) - ld a, 100 - call DealRecoilDamageToSelf - -; own bench - ld a, $01 - ld [wIsDamageToSelf], a - ld a, 20 - call DealDamageToAllBenchedPokemon - -; opponent's bench - call SwapTurn - xor a - ld [wIsDamageToSelf], a - ld a, 20 - call DealDamageToAllBenchedPokemon - call SwapTurn - ret - -PealOfThunder_InitialEffect: ; 2e77e (b:677e) - scf - ret - -PealOfThunder_RandomlyDamageEffect: ; 2e780 (b:6780) - call ExchangeRNG - ld de, 30 ; damage to inflict - call RandomlyDamagePlayAreaPokemon - bank1call Func_6e49 - ret - -; randomly damages a Pokemon in play, except -; card that is in [hTempPlayAreaLocation_ff9d]. -; plays thunder animation when Play Area is shown. -; input: -; de = amount of damage to deal -RandomlyDamagePlayAreaPokemon: ; 2e78d (b:678d) - xor a - ld [wNoDamageOrEffect], a - -; choose randomly which Play Area to attack - call UpdateRNGSources - and 1 - jr nz, .opp_play_area - -; own Play Area - ld a, $01 - ld [wIsDamageToSelf], a - ld a, DUELVARS_NUMBER_OF_POKEMON_IN_PLAY_AREA - call GetTurnDuelistVariable - call Random - ld b, a - ; can't select Zapdos - ldh a, [hTempPlayAreaLocation_ff9d] - cp b - jr z, RandomlyDamagePlayAreaPokemon ; re-roll Pokemon to attack - -.damage - ld a, ATK_ANIM_THUNDER_PLAY_AREA - ld [wLoadedAttackAnimation], a - call DealDamageToPlayAreaPokemon - ret - -.opp_play_area - xor a - ld [wIsDamageToSelf], a - call SwapTurn - ld a, DUELVARS_NUMBER_OF_POKEMON_IN_PLAY_AREA - call GetTurnDuelistVariable - call Random - ld b, a - call .damage - call SwapTurn - ret - -BigThunderEffect: ; 2e7cb (b:67cb) - call ExchangeRNG - ld de, 70 ; damage to inflict - call RandomlyDamagePlayAreaPokemon - ret - -MagneticStormEffect: ; 2e7d5 (b:67d5) - ld a, DUELVARS_CARD_LOCATIONS - call GetTurnDuelistVariable - -; writes in wDuelTempList all deck indices -; of Energy cards attached to Pokemon -; in the Turn Duelist's Play Area. - ld de, wDuelTempList - ld c, 0 -.loop_card_locations - ld a, [hl] - and CARD_LOCATION_PLAY_AREA - jr z, .next_card_location - -; is a card that is in the Play Area - push hl - push de - push bc - ld a, l - call GetCardIDFromDeckIndex - call GetCardType - pop bc - pop de - pop hl - and TYPE_ENERGY - jr z, .next_card_location -; is an Energy card attached to Pokemon in Play Area - ld a, l - ld [de], a - inc de - inc c -.next_card_location - inc l - ld a, l - cp DECK_SIZE - jr c, .loop_card_locations - ld a, $ff ; terminating byte - ld [de], a - -; divide number of energy cards -; by number of Pokemon in Play Area - ld a, DUELVARS_NUMBER_OF_POKEMON_IN_PLAY_AREA - call GetTurnDuelistVariable - ld b, a - ld a, c - ld c, -1 -.loop_division - inc c - sub b - jr nc, .loop_division - ; c = floor(a / b) - -; evenly divides the Energy cards randomly -; to every Pokemon in the Play Area. - push bc - ld hl, wDuelTempList - call CountCardsInDuelTempList - call ShuffleCards - ld d, c - ld e, PLAY_AREA_ARENA -.start_attach - ld c, d - inc c - jr .check_done -.attach_energy - ld a, [hli] - push hl - push de - push bc - call AddCardToHand - call PutHandCardInPlayArea - pop bc - pop de - pop hl -.check_done - dec c - jr nz, .attach_energy -; go to next Pokemon in Play Area - inc e ; next in Play Area - dec b - jr nz, .start_attach - pop bc - - push hl - ld hl, hTempList - -; fill hTempList with PLAY_AREA_* locations -; that have Pokemon in them. - push hl - xor a -.loop_init - ld [hli], a - inc a - cp b - jr nz, .loop_init - pop hl - -; shuffle them and distribute -; the remaining cards in random order. - ld a, b - call ShuffleCards - pop hl - ld de, hTempList -.next_random_pokemon - ld a, [hl] - cp $ff - jr z, .done - push hl - push de - ld a, [de] - ld e, a - ld a, [hl] - call AddCardToHand - call PutHandCardInPlayArea - pop de - pop hl - inc hl - inc de - jr .next_random_pokemon - -.done - bank1call DrawDuelMainScene - bank1call DrawDuelHUDs - ldtx hl, TheEnergyCardFromPlayAreaWasMovedText - call DrawWideTextBox_WaitForInput - xor a - call Func_2c10b - ret - -ElectrodeSonicboom_UnaffectedByColorEffect: ; 2e870 (b:6870) - ld hl, wDamage + 1 - set UNAFFECTED_BY_WEAKNESS_RESISTANCE_F, [hl] - ret - -ElectrodeSonicboom_NullEffect: ; 2e876 (b:6876) - ret - -; return carry if no cards in Deck -EnergySpike_DeckCheck: ; 2e877 (b:6877) - call CheckIfDeckIsEmpty - ret - -EnergySpike_PlayerSelectEffect: ; 2e87b (b:687b) - ld a, $ff - ldh [hTemp_ffa0], a - -; search cards in Deck - call CreateDeckCardList - ldtx hl, Choose1BasicEnergyCardFromDeckText - ldtx bc, BasicEnergyText - lb de, SEARCHEFFECT_BASIC_ENERGY, 0 - call LookForCardsInDeck - ret c - - bank1call Func_5591 - ldtx hl, ChooseBasicEnergyCardText - ldtx de, DuelistDeckText - bank1call SetCardListHeaderText -.select_card - bank1call DisplayCardList - jr c, .try_cancel - call GetCardIDFromDeckIndex - call GetCardType - cp TYPE_ENERGY_DOUBLE_COLORLESS - jr nc, .select_card ; not a Basic Energy card - and TYPE_ENERGY - jr z, .select_card ; not a Basic Energy card - ; Energy card selected - - ldh a, [hTempCardIndex_ff98] - ldh [hTemp_ffa0], a - call EmptyScreen - ldtx hl, ChoosePokemonToAttachEnergyCardText - call DrawWideTextBox_WaitForInput - -; choose a Pokemon in Play Area to attach card - bank1call HasAlivePokemonInPlayArea -.loop_input - bank1call OpenPlayAreaScreenForSelection - jr c, .loop_input - ldh a, [hTempPlayAreaLocation_ff9d] - ldh [hTempPlayAreaLocation_ffa1], a - ret - -.play_sfx - call Func_3794 - jr .select_card - -.try_cancel -; Player tried exiting screen, if there are -; any Basic Energy cards, Player is forced to select them. -; otherwise, they can safely exit. - ld a, DUELVARS_CARD_LOCATIONS - call GetTurnDuelistVariable -.loop_deck - ld a, [hl] - cp CARD_LOCATION_DECK - jr nz, .next_card - ld a, l - call GetCardIDFromDeckIndex - call GetCardType - and TYPE_ENERGY - jr z, .next_card - cp TYPE_ENERGY_DOUBLE_COLORLESS - jr c, .play_sfx -.next_card - inc l - ld a, l - cp DECK_SIZE - jr c, .loop_deck - ; can exit - - ld a, $ff - ldh [hTemp_ffa0], a - ret - -EnergySpike_AISelectEffect: ; 2e8f1 (b:68f1) -; AI doesn't select any card - ld a, $ff - ldh [hTemp_ffa0], a - ret - -EnergySpike_AttachEnergyEffect: ; 2e8f6 (b:68f6) - ldh a, [hTemp_ffa0] - cp $ff - jr z, .done - -; add card to hand and attach it to the selected Pokemon - call SearchCardInDeckAndAddToHand - call AddCardToHand - ldh a, [hTempPlayAreaLocation_ffa1] - ld e, a - ldh a, [hTemp_ffa0] - call PutHandCardInPlayArea - call IsPlayerTurn - jr c, .done - -; not Player, so show detail screen -; and which Pokemon was chosen to attach Energy. - ldh a, [hTempPlayAreaLocation_ffa1] - add DUELVARS_ARENA_CARD - call GetTurnDuelistVariable - call LoadCardDataToBuffer1_FromDeckIndex - ld hl, wLoadedCard1Name - ld de, wTxRam2_b - ld a, [hli] - ld [de], a - inc de - ld a, [hli] - ld [de], a - ldh a, [hTemp_ffa0] - ldtx hl, AttachedEnergyToPokemonText - bank1call DisplayCardDetailScreen - -.done - call Func_2c0bd - ret - -JolteonDoubleKick_AIEffect: ; 2e930 (b:6930) - ld a, 40 / 2 - lb de, 0, 40 - jp SetExpectedAIDamage - -JolteonDoubleKick_MultiplierEffect: ; 2e938 (b:6938) - ld hl, 20 - call LoadTxRam3 - ldtx de, DamageCheckIfHeadsXDamageText - ld a, 2 - call TossCoinATimes_BankB - add a ; a = 2 * heads - call ATimes10 - call SetDefiniteDamage - ret - -TailWagEffect: ; 2e94e (b:694e) - ldtx de, IfHeadsOpponentCannotAttackText - call TossCoin_BankB - jp nc, SetWasUnsuccessful - ld a, ATK_ANIM_LURE - ld [wLoadedAttackAnimation], a - ld a, SUBSTATUS2_TAIL_WAG - call ApplySubstatus2ToDefendingCard - ret - -EeveeQuickAttack_AIEffect: ; 2e962 (b:5962) - ld a, (10 + 30) / 2 - lb de, 10, 30 - jp SetExpectedAIDamage - -EeveeQuickAttack_DamageBoostEffect: ; 2e96a (b:596a) - ld hl, 20 - call LoadTxRam3 - ldtx de, DamageCheckIfHeadsPlusDamageText - call TossCoin_BankB - ret nc ; return if tails - ld a, 20 - call AddToDamage - ret - -SpearowMirrorMove_AIEffect: ; 2e97d (b:697d) - jr MirrorMoveEffects.AIEffect - -SpearowMirrorMove_InitialEffect1: ; 2e97f (b:697f) - jr MirrorMoveEffects.InitialEffect1 - -SpearowMirrorMove_InitialEffect2: ; 2e981 (b:6981) - jr MirrorMoveEffects.InitialEffect2 - -SpearowMirrorMove_PlayerSelection: ; 2e983 (b:6983) - jr MirrorMoveEffects.PlayerSelection - -SpearowMirrorMove_AISelection: ; 2e985 (b:6985) - jr MirrorMoveEffects.AISelection - -SpearowMirrorMove_BeforeDamage: ; 2e987 (b:6987) - jr MirrorMoveEffects.BeforeDamage - -SpearowMirrorMove_AfterDamage: ; 2e989 (b:6989) - jp MirrorMoveEffects.AfterDamage - -; these are effect commands that Mirror Move uses -; in order to mimic last turn's attack. -; it covers all possible effect steps to perform its commands -; (i.e. selection for Amnesia and Energy discarding attacks, etc) -MirrorMoveEffects: ; 2e98c (b:698c) -.AIEffect - ld a, DUELVARS_ARENA_CARD_LAST_TURN_DAMAGE - call GetTurnDuelistVariable - ld a, [hl] - ld [wAIMinDamage], a - ld [wAIMaxDamage], a - ret - -.InitialEffect1 - ld a, DUELVARS_ARENA_CARD_LAST_TURN_DAMAGE - call GetTurnDuelistVariable - ld a, [hli] - or [hl] - inc hl - or [hl] - inc hl - ret nz ; return if has last turn damage - ld a, [hli] - or a - ret nz ; return if has last turn status - ; no attack received last turn - ldtx hl, YouDidNotReceiveAnAttackToMirrorMoveText - scf - ret - -.InitialEffect2 - ld a, $ff - ldh [hTemp_ffa0], a - ld a, DUELVARS_ARENA_CARD_LAST_TURN_EFFECT - call GetTurnDuelistVariable - or a - ret z ; no effect - cp LAST_TURN_EFFECT_AMNESIA - jp z, PlayerPickAttackForAmnesia - or a - ret - -.PlayerSelection - ld a, DUELVARS_ARENA_CARD_LAST_TURN_EFFECT - call GetTurnDuelistVariable - or a - ret z ; no effect -; handle Energy card discard effect - cp LAST_TURN_EFFECT_DISCARD_ENERGY - jp z, HandleEnergyDiscardEffectSelection - ret - -.AISelection - ld a, $ff - ldh [hTemp_ffa0], a - ld a, DUELVARS_ARENA_CARD_LAST_TURN_EFFECT - call GetTurnDuelistVariable - or a - ret z ; no effect - cp LAST_TURN_EFFECT_DISCARD_ENERGY - jr z, .discard_energy - cp LAST_TURN_EFFECT_AMNESIA - jr z, .pick_amnesia_attack - ret - -.discard_energy - call AIPickEnergyCardToDiscardFromDefendingPokemon - ldh [hTemp_ffa0], a - ret - -.pick_amnesia_attack - call AIPickAttackForAmnesia - ldh [hTemp_ffa0], a - ret - -.BeforeDamage -; if was attacked with Amnesia, apply it to the selected attack - ld a, DUELVARS_ARENA_CARD_LAST_TURN_EFFECT - call GetTurnDuelistVariable - cp LAST_TURN_EFFECT_AMNESIA - jr z, .apply_amnesia - -; otherwise, check if there was last turn damage, -; and write it to wDamage. - ld a, DUELVARS_ARENA_CARD_LAST_TURN_DAMAGE - call GetTurnDuelistVariable - ld de, wDamage - ld a, [hli] - ld [de], a - inc de - ld a, [hld] - ld [de], a - or [hl] - jr z, .no_damage - ld a, ATK_ANIM_HIT - ld [wLoadedAttackAnimation], a -.no_damage - inc hl - inc hl ; DUELVARS_ARENA_CARD_LAST_TURN_STATUS -; check if there was a status applied to Defending Pokemon -; from the attack it used. - push hl - ld a, DUELVARS_ARENA_CARD_STATUS - call GetNonTurnDuelistVariable - ld e, l - ld d, h - pop hl - ld a, [hli] - or a - jr z, .no_status - push hl - push de - call .ExecuteStatusEffect - pop de - pop hl -.no_status -; hl is at DUELVARS_ARENA_CARD_LAST_TURN_SUBSTATUS2 -; apply substatus2 to self - ld e, DUELVARS_ARENA_CARD_SUBSTATUS2 - ld a, [hli] - ld [de], a - ret - -.apply_amnesia - call ApplyAmnesiaToAttack - ret - -.AfterDamage: ; 2ea28 (b:6a28) - ld a, [wNoDamageOrEffect] - or a - ret nz ; is unaffected - ld a, DUELVARS_ARENA_CARD_LAST_TURN_EFFECT - call GetTurnDuelistVariable - cp LAST_TURN_EFFECT_DISCARD_ENERGY - jr nz, .change_weakness - -; execute Energy discard effect for card chosen - call SwapTurn - ldh a, [hTemp_ffa0] - call PutCardInDiscardPile - ld a, DUELVARS_ARENA_CARD_LAST_TURN_EFFECT - call GetTurnDuelistVariable - ld [hl], LAST_TURN_EFFECT_DISCARD_ENERGY - call SwapTurn - -.change_weakness - ld a, DUELVARS_ARENA_CARD_LAST_TURN_CHANGE_WEAK - call GetTurnDuelistVariable - ld a, [hl] - or a - ret z ; weakness wasn't changed last turn - - push hl - call SwapTurn - ld a, DUELVARS_ARENA_CARD - call GetTurnDuelistVariable - call LoadCardDataToBuffer2_FromDeckIndex - call SwapTurn - pop hl - - ld a, [wLoadedCard2Weakness] - or a - ret z ; defending Pokemon has no weakness to change - -; apply same color weakness to Defending Pokemon - ld a, [hl] - push af - ld a, DUELVARS_ARENA_CARD_CHANGED_WEAKNESS - call GetNonTurnDuelistVariable - pop af - ld [hl], a - -; print message of weakness color change - ld c, -1 -.loop_color - inc c - rla - jr nc, .loop_color - ld a, c - call SwapTurn - push af - ld a, DUELVARS_ARENA_CARD - call GetTurnDuelistVariable - call LoadCardDataToBuffer1_FromDeckIndex - pop af - call LoadCardNameAndInputColor - ldtx hl, ChangedTheWeaknessOfPokemonToColorText - call DrawWideTextBox_PrintText - call SwapTurn - ret - -.ExecuteStatusEffect: ; 2ea8f (b:6a8f) - ld c, a - and PSN_DBLPSN - jr z, .cnf_slp_prz - ld b, a - cp DOUBLE_POISONED - push bc - call z, DoublePoisonEffect - pop bc - ld a, b - cp POISONED - push bc - call z, PoisonEffect - pop bc -.cnf_slp_prz - ld a, c - and CNF_SLP_PRZ - ret z - cp CONFUSED - jp z, ConfusionEffect - cp ASLEEP - jp z, SleepEffect - cp PARALYZED - jp z, ParalysisEffect - ret - -FearowAgilityEffect: ; 2eab8 (b:6ab8) - ldtx de, IfHeadsDoNotReceiveDamageOrEffectText - call TossCoin_BankB - ret nc - ld a, ATK_ANIM_AGILITY_PROTECT - ld [wLoadedAttackAnimation], a - ld a, SUBSTATUS1_AGILITY - call ApplySubstatus1ToDefendingCard - ret - -; return carry if cannot use Step In -StepIn_BenchCheck: ; 2eaca (b:6aca) - ldh a, [hTempPlayAreaLocation_ff9d] - ldh [hTemp_ffa0], a - ldtx hl, CanOnlyBeUsedOnTheBenchText - or a - jr z, .set_carry - - add DUELVARS_ARENA_CARD_FLAGS - call GetTurnDuelistVariable - ldtx hl, OnlyOncePerTurnText - and USED_PKMN_POWER_THIS_TURN - jr nz, .set_carry - - ldh a, [hTempPlayAreaLocation_ff9d] - call CheckCannotUseDueToStatus_OnlyToxicGasIfANon0 - ret - -.set_carry - scf - ret - -StepIn_SwitchEffect: ; 2eae8 (b:6ae8) - ldh a, [hTemp_ffa0] - ld e, a - call SwapArenaWithBenchPokemon - ld a, DUELVARS_ARENA_CARD_FLAGS - call GetTurnDuelistVariable - set USED_PKMN_POWER_THIS_TURN_F, [hl] - ret - -Dragonite2Slam_AIEffect: ; 2eaf6 (b:6af6) - ld a, (40 * 2) / 2 - lb de, 0, 80 - jp SetExpectedAIDamage - -Dragonite2Slam_MultiplierEffect: ; 2eafe (b:6afe) - ld hl, 40 - call LoadTxRam3 - ldtx de, DamageCheckIfHeadsXDamageText - ld a, 2 - call TossCoinATimes_BankB - add a - add a - call ATimes10 - call SetDefiniteDamage - ret - -ThickSkinnedEffect: ; 2eb15 (b:6b15) - scf - ret - -LeekSlap_AIEffect: ; 2eb17 (b:6b17) - ld a, 30 / 2 - lb de, 0, 30 - jp SetExpectedAIDamage - -; return carry if already used attack in this duel -LeekSlap_OncePerDuelCheck: ; 2eb1f (b:6b1f) -; can only use attack if it was never used before this duel - ld a, DUELVARS_ARENA_CARD_FLAGS - call GetTurnDuelistVariable - and USED_LEEK_SLAP_THIS_DUEL - ret z - ldtx hl, ThisAttackCannotBeUsedTwiceText - scf - ret - -LeekSlap_SetUsedThisDuelFlag: ; 2eb2c (b:6b2c) - ld a, DUELVARS_ARENA_CARD_FLAGS - call GetTurnDuelistVariable - set USED_LEEK_SLAP_THIS_DUEL_F, [hl] - ret - -LeekSlap_NoDamage50PercentEffect: ; 2eb34 (b:6b34) - ldtx de, DamageCheckIfTailsNoDamageText - call TossCoin_BankB - ret c - xor a ; 0 damage - call SetDefiniteDamage - ret - -FetchEffect: ; 2eb40 (b:6b40) - ldtx hl, Draw1CardFromTheDeckText - call DrawWideTextBox_WaitForInput - bank1call DisplayDrawOneCardScreen - call DrawCardFromDeck - ret c ; return if deck is empty - call AddCardToHand - call LoadCardDataToBuffer1_FromDeckIndex - ld a, [wDuelistType] - cp DUELIST_TYPE_PLAYER - ret nz - ; show card on screen if it was Player - bank1call OpenCardPage_FromHand - ret - -CometPunch_AIEffect: ; 2eb5d (b:6b5d) - ld a, (20 * 4) / 2 - lb de, 0, 80 - jp SetExpectedAIDamage - -CometPunch_MultiplierEffect: ; 2eb65 (b:6b65) - ld hl, 20 - call LoadTxRam3 - ldtx de, DamageCheckIfHeadsXDamageText - ld a, 4 - call TossCoinATimes_BankB - add a - call ATimes10 - call SetDefiniteDamage - ret - -TaurosStomp_AIEffect: ; 2eb7b (b:6b7b) - ld a, (20 + 30) / 2 - lb de, 20, 30 - jp SetExpectedAIDamage - -TaurosStomp_DamageBoostEffect: ; 2eb83 (b:6b83) - ld hl, 10 - call LoadTxRam3 - ldtx de, DamageCheckIfHeadsPlusDamageText - call TossCoin_BankB - ret nc ; tails - ld a, 10 - call AddToDamage - ret - -Rampage_AIEffect: ; 2eb96 (b:6b96) - ld e, PLAY_AREA_ARENA - call GetCardDamageAndMaxHP - call AddToDamage - jp SetDefiniteAIDamage - -Rampage_Confusion50PercentEffect: ; 2eba1 (b:6ba1) - ld e, PLAY_AREA_ARENA - call GetCardDamageAndMaxHP - call AddToDamage - ldtx de, IfTailsYourPokemonBecomesConfusedText - call TossCoin_BankB - ret c ; heads - call SwapTurn - call ConfusionEffect - call SwapTurn - ret - -FuryAttack_AIEffect: ; 2ebba (b:6bba) - ld a, (10 * 2) / 2 - lb de, 0, 20 - jp SetExpectedAIDamage - -FuryAttack_MultiplierEffect: ; 2ebc2 (b:6bc2) - ld hl, 10 - call LoadTxRam3 - ld a, 2 - ldtx de, DamageCheckIfHeadsXDamageText - call TossCoinATimes_BankB - call ATimes10 - call SetDefiniteDamage - ret - -RetreatAidEffect: ; 2ebd7 (b:6bd7) - scf - ret - -DodrioRage_AIEffect: ; 2ebd9 (b:6bd9) - call DodrioRage_DamageBoostEffect - jp SetDefiniteAIDamage - -DodrioRage_DamageBoostEffect: ; 2ebdf (b:6bdf) - ld e, PLAY_AREA_ARENA - call GetCardDamageAndMaxHP - call AddToDamage - ret - -PayDayEffect: ; 2ebe8 (b:6be8) - ldtx de, IfHeadsDraw1CardFromDeckText - call TossCoin_BankB - ret nc ; tails - ldtx hl, Draw1CardFromTheDeckText - call DrawWideTextBox_WaitForInput - bank1call DisplayDrawOneCardScreen - call DrawCardFromDeck - ret c ; empty deck - call AddCardToHand - call LoadCardDataToBuffer1_FromDeckIndex - ld a, [wDuelistType] - cp DUELIST_TYPE_PLAYER - ret nz - ; show card on screen if it was Player - bank1call OpenCardPage_FromHand - ret - -DragonairSlam_AIEffect: ; 2ec0c (b:6c0c) - ld a, (30 * 2) / 2 - lb de, 0, 60 - jp SetExpectedAIDamage - -DragonairSlam_MultiplierEffect: ; 2ec14 (b:6c14) - ld hl, 30 - call LoadTxRam3 - ld a, 2 - ldtx de, DamageCheckIfHeadsXDamageText - call TossCoinATimes_BankB - ld e, a - add a - add e - call ATimes10 - call SetDefiniteDamage - ret - -DragonairHyperBeam_PlayerSelectEffect: ; 2ec2c (b:6c2c) - jp HandleEnergyDiscardEffectSelection - -DragonairHyperBeam_AISelectEffect: ; 2ec2f (b:6c2f) - call AIPickEnergyCardToDiscardFromDefendingPokemon - ldh [hTemp_ffa0], a - ret - -DragonairHyperBeam_DiscardEffect: ; 2ec35 (b:6c35) - call HandleNoDamageOrEffect - ret c ; is unaffected - ldh a, [hTemp_ffa0] - cp $ff - ret z ; no energy card chosen - call SwapTurn - call PutCardInDiscardPile - ld a, DUELVARS_ARENA_CARD_LAST_TURN_EFFECT - call GetTurnDuelistVariable - ld [hl], LAST_TURN_EFFECT_DISCARD_ENERGY - call SwapTurn - ret - -; handles screen for selecting an Energy card to discard -; that is attached to Defending Pokemon, -; and store the Player selection in [hTemp_ffa0]. -HandleEnergyDiscardEffectSelection: ; 2ec4f (b:6c4f) - call SwapTurn - xor a ; PLAY_AREA_ARENA - call CreateArenaOrBenchEnergyCardList - jr c, .no_energy - ldtx hl, ChooseDiscardEnergyCardFromOpponentText - call DrawWideTextBox_WaitForInput - xor a ; PLAY_AREA_ARENA - bank1call DisplayEnergyDiscardScreen - -.loop_input - bank1call HandleEnergyDiscardMenuInput - jr c, .loop_input - - call SwapTurn - ldh a, [hTempCardIndex_ff98] - ldh [hTemp_ffa0], a ; store selected card to discard - ret - -.no_energy - call SwapTurn - ld a, $ff - ldh [hTemp_ffa0], a - ret - -; return carry if Defending Pokemon has no attacks -ClefableMetronome_CheckAttacks: ; 2ec77 (b:6c77) - call CheckIfDefendingPokemonHasAnyAttack - ldtx hl, NoAttackMayBeChoosenText - ret - -ClefableMetronome_AISelectEffect: ; 2ec7e (b:6c7e) - call HandleAIMetronomeEffect - ret - -ClefableMetronome_UseAttackEffect: ; 2ec82 (b:6c82) - ld a, 1 ; energy cost of this attack - call HandlePlayerMetronomeEffect - ret - -ClefableMinimizeEffect: ; 2ec88 (b:6c88) - ld a, SUBSTATUS1_REDUCE_BY_20 - call ApplySubstatus1ToDefendingCard - ret - -HurricaneEffect: ; 2ec8e (b:6c8e) - call HandleNoDamageOrEffect - ret c ; is unaffected - - ld a, DUELVARS_ARENA_CARD_HP - call GetNonTurnDuelistVariable - or a - ret z ; return if Pokemon was KO'd - -; look at all the card locations and put all cards -; that are in the Arena in the hand. - call SwapTurn - ld a, DUELVARS_CARD_LOCATIONS - call GetTurnDuelistVariable -.loop_locations - ld a, [hl] - cp CARD_LOCATION_ARENA - jr nz, .next_card - ; card in Arena found, put in hand - ld a, l - call AddCardToHand -.next_card - inc l - ld a, l - cp DECK_SIZE - jr c, .loop_locations - -; empty the Arena card slot - ld l, DUELVARS_ARENA_CARD - ld a, [hl] - ld [hl], $ff - ld l, DUELVARS_ARENA_CARD_HP - ld [hl], 0 - call LoadCardDataToBuffer1_FromDeckIndex - ld hl, wLoadedCard1Name - ld a, [hli] - ld h, [hl] - ld l, a - call LoadTxRam2 - ldtx hl, PokemonAndAllAttachedCardsReturnedToHandText - call DrawWideTextBox_WaitForInput - xor a - ld [wDuelDisplayedScreen], a - call SwapTurn - ret - -PidgeottoWhirlwind_SelectEffect: ; 2ecd3 (b:6cd3) - ld a, DUELVARS_NUMBER_OF_POKEMON_IN_PLAY_AREA - call GetNonTurnDuelistVariable - cp 2 - jr nc, .switch - ; no Bench Pokemon - ld a, $ff - ldh [hTemp_ffa0], a - ret -.switch - call DuelistSelectForcedSwitch - ldh a, [hTempPlayAreaLocation_ff9d] - ldh [hTemp_ffa0], a - ret - -PidgeottoWhirlwind_SwitchEffect: ; 2ece9 (b:6ce9) - ldh a, [hTemp_ffa0] - call HandleSwitchDefendingPokemonEffect - ret - -PidgeottoMirrorMove_AIEffect: ; 2ecef (b:6cef) - jp MirrorMoveEffects.AIEffect - -PidgeottoMirrorMove_InitialEffect1: ; 2ecf2 (b:6cf2) - jp MirrorMoveEffects.InitialEffect1 - -PidgeottoMirrorMove_InitialEffect2: ; 2ecf5 (b:6cf5) - jp MirrorMoveEffects.InitialEffect2 - -PidgeottoMirrorMove_PlayerSelection: ; 2ecf8 (b:6cf8) - jp MirrorMoveEffects.PlayerSelection - -PidgeottoMirrorMove_AISelection: ; 2ecfb (b:6cfb) - jp MirrorMoveEffects.AISelection - -PidgeottoMirrorMove_BeforeDamage: ; 2ecfe (b:6cfe) - jp MirrorMoveEffects.BeforeDamage - -PidgeottoMirrorMove_AfterDamage: ; 2ed01 (b:6d01) - jp MirrorMoveEffects.AfterDamage - -SingEffect: ; 2ed04 (b:6d04) - call Sleep50PercentEffect - call nc, SetNoEffectFromStatus - ret - -; return carry if Defending Pokemon has no attacks -ClefairyMetronome_CheckAttacks: ; 2ed0b (b:6d0b) - call CheckIfDefendingPokemonHasAnyAttack - ldtx hl, NoAttackMayBeChoosenText - ret - -ClefairyMetronome_AISelectEffect: ; 2ed12 (b:6d12) - call HandleAIMetronomeEffect - ret - -ClefairyMetronome_UseAttackEffect: ; 2ed16 (b:6d16) - ld a, 3 ; energy cost of this attack -; fallthrough - -; handles Metronome selection, and validates -; whether it can use the selected attack. -; if unsuccessful, returns carry. -; input: -; a = amount of colorless energy needed for Metronome -HandlePlayerMetronomeEffect: ; 2ed18 (b:6d18) - ld [wMetronomeEnergyCost], a - ldtx hl, ChooseOppAttackToBeUsedWithMetronomeText - call DrawWideTextBox_WaitForInput - - call HandleDefendingPokemonAttackSelection - ret c ; return if operation cancelled - -; store this attack as selected attack to use - ld hl, wMetronomeSelectedAttack - ld [hl], d - inc hl - ld [hl], e - -; compare selected attack's name with -; the attack that is loaded, which is Metronome. -; if equal, then cannot select it. -; (i.e. cannot use Metronome with Metronome.) - ld hl, wLoadedAttackName - ld a, [hli] - ld h, [hl] - ld l, a - push hl - call SwapTurn - call CopyAttackDataAndDamage_FromDeckIndex - call SwapTurn - pop de - ld hl, wLoadedAttackName - ld a, e - cp [hl] - jr nz, .try_use - inc hl - ld a, d - cp [hl] - jr nz, .try_use - ; cannot select Metronome - ldtx hl, UnableToSelectText -.failed - call DrawWideTextBox_WaitForInput -.set_carry - scf - ret - -.try_use -; run the attack checks to determine -; whether it can be used. - ld a, EFFECTCMDTYPE_INITIAL_EFFECT_1 - call TryExecuteEffectCommandFunction - jr c, .failed - ld a, EFFECTCMDTYPE_INITIAL_EFFECT_2 - call TryExecuteEffectCommandFunction - jr c, .set_carry - ; successful - -; send data to link opponent - bank1call SendAttackDataToLinkOpponent - ld a, OPPACTION_USE_METRONOME_ATTACK - call SetOppAction_SerialSendDuelData - ld hl, wMetronomeSelectedAttack - ld d, [hl] - inc hl - ld e, [hl] - ld a, [wMetronomeEnergyCost] - ld c, a - call SerialSend8Bytes - - ldh a, [hTempCardIndex_ff9f] - ld [wPlayerAttackingCardIndex], a - ld a, [wSelectedAttack] - ld [wPlayerAttackingAttackIndex], a - ld a, [wTempCardID_ccc2] - ld [wPlayerAttackingCardID], a - or a - ret - -; does nothing for AI. -HandleAIMetronomeEffect: ; 2ed86 (b:6d86) - ret - -DoTheWaveEffect: ; 2ed87 (b:6d87) - ld a, DUELVARS_NUMBER_OF_POKEMON_IN_PLAY_AREA - call GetTurnDuelistVariable - dec a ; don't count arena card - call ATimes10 - call AddToDamage - ret - -; return carry if no damage counters -FirstAid_DamageCheck: ; 2ed94 (b:6d94) - ld e, PLAY_AREA_ARENA - call GetCardDamageAndMaxHP - ldtx hl, NoDamageCountersText - cp 10 - ret - -FirstAid_HealEffect: ; 2ed9f (b:6d9f) - lb de, 0, 10 - call ApplyAndAnimateHPRecovery - ret - -JigglypuffDoubleEdgeEffect: ; 2eda6 (b:6da6) - ld a, 20 - call DealRecoilDamageToSelf - ret - -PounceEffect: ; 2edac (b:6dac) - ld a, SUBSTATUS2_POUNCE - call ApplySubstatus2ToDefendingCard - ret - -LickitungSupersonicEffect: ; 2edb2 (b:6db2) - call Confusion50PercentEffect - call nc, SetNoEffectFromStatus - ret - -PidgeyWhirlwind_SelectEffect: ; 2edb9 (b:6db9) - ld a, DUELVARS_NUMBER_OF_POKEMON_IN_PLAY_AREA - call GetNonTurnDuelistVariable - cp 2 - jr nc, .switch - ; no Bench Pokemon - ld a, $ff - ldh [hTemp_ffa0], a - ret -.switch - call DuelistSelectForcedSwitch - ldh a, [hTempPlayAreaLocation_ff9d] - ldh [hTemp_ffa0], a - ret - -PidgeyWhirlwind_SwitchEffect: ; 2edcf (b:6dcf) - ldh a, [hTemp_ffa0] - call HandleSwitchDefendingPokemonEffect - ret - -; return carry if Defending card has no weakness -Conversion1_WeaknessCheck: ; 2edd5 (b:6dd5) - call SwapTurn - ld a, DUELVARS_ARENA_CARD - call GetTurnDuelistVariable - call LoadCardDataToBuffer2_FromDeckIndex - call SwapTurn - ld a, [wLoadedCard2Weakness] - or a - ret nz - ldtx hl, NoWeaknessText - scf - ret - -Conversion1_PlayerSelectEffect: ; 2eded (b:6ded) - ldtx hl, ChooseWeaknessYouWishToChangeText - xor a ; PLAY_AREA_ARENA - call HandleColorChangeScreen - ldh [hTemp_ffa0], a - ret - -Conversion1_AISelectEffect: ; 2edf7 (b:6df7) - call AISelectConversionColor - ret - -Conversion1_ChangeWeaknessEffect: ; 2edfb (b:6dfb) - call HandleNoDamageOrEffect - ret c ; is unaffected - -; apply changed weakness - ld a, DUELVARS_ARENA_CARD_CHANGED_WEAKNESS - call GetNonTurnDuelistVariable - ldh a, [hTemp_ffa0] - call TranslateColorToWR - ld [hl], a - ld l, DUELVARS_ARENA_CARD_LAST_TURN_CHANGE_WEAK - ld [hl], a - -; print text box - call SwapTurn - ldtx hl, ChangedTheWeaknessOfPokemonToColorText - call PrintArenaCardNameAndColorText - call SwapTurn - -; apply substatus - ld a, SUBSTATUS2_CONVERSION2 - call ApplySubstatus2ToDefendingCard - ret - -; returns carry if Active Pokemon has no Resistance. -Conversion2_ResistanceCheck: ; 2ee1f (b:6e1f) - ld a, DUELVARS_ARENA_CARD - call GetTurnDuelistVariable - call LoadCardDataToBuffer2_FromDeckIndex - ld a, [wLoadedCard2Resistance] - or a - ret nz - ldtx hl, NoResistanceText - scf - ret - -Conversion2_PlayerSelectEffect: ; 2ee31 (b:6e31) - ldtx hl, ChooseResistanceYouWishToChangeText - ld a, $80 - call HandleColorChangeScreen - ldh [hTemp_ffa0], a - ret - -Conversion2_AISelectEffect: ; 2ee3c (b:6e3c) -; AI will choose Defending Pokemon's color -; unless it is colorless. - call SwapTurn - ld a, DUELVARS_ARENA_CARD - call GetTurnDuelistVariable - call LoadCardDataToBuffer1_FromDeckIndex - call SwapTurn - ld a, [wLoadedCard1Type] - cp COLORLESS - jr z, .is_colorless - ldh [hTemp_ffa0], a - ret - -.is_colorless - call SwapTurn - call AISelectConversionColor - call SwapTurn - ret - -Conversion2_ChangeResistanceEffect: ; 2ee5e (b:6e5e) -; apply changed resistance - ld a, DUELVARS_ARENA_CARD_CHANGED_RESISTANCE - call GetTurnDuelistVariable - ldh a, [hTemp_ffa0] - call TranslateColorToWR - ld [hl], a - ldtx hl, ChangedTheResistanceOfPokemonToColorText -; fallthrough - -; prints text that requires card name and color, -; with the card name of the Turn Duelist's Arena Pokemon -; and color in [hTemp_ffa0]. -; input: -; hl = text to print -PrintArenaCardNameAndColorText: ; 2ee6c (b:6e6c) - push hl - ld a, DUELVARS_ARENA_CARD - call GetTurnDuelistVariable - call LoadCardDataToBuffer1_FromDeckIndex - ldh a, [hTemp_ffa0] - call LoadCardNameAndInputColor - pop hl - call DrawWideTextBox_PrintText - ret - -; handles AI logic for selecting a new color -; for weakness/resistance. -; - if within the context of Conversion1, looks -; in own Bench for a non-colorless card that can attack. -; - if within the context of Conversion2, looks -; in Player's Bench for a non-colorless card that can attack. -AISelectConversionColor: ; 2ee7f (b:6e7f) - ld a, DUELVARS_NUMBER_OF_POKEMON_IN_PLAY_AREA - call GetTurnDuelistVariable - ld d, a - ld e, PLAY_AREA_ARENA - jr .next_pkmn_atk - -; look for a non-colorless Bench Pokemon -; that has enough energy to use an attack. -.loop_atk - push de - call GetPlayAreaCardAttachedEnergies - ld a, e - add DUELVARS_ARENA_CARD - call GetTurnDuelistVariable - ld d, a - call LoadCardDataToBuffer1_FromDeckIndex - ld a, [wLoadedCard1Type] - cp COLORLESS - jr z, .skip_pkmn_atk ; skip colorless Pokemon - ld e, FIRST_ATTACK_OR_PKMN_POWER - bank1call _CheckIfEnoughEnergiesToAttack - jr nc, .found - ld e, SECOND_ATTACK - bank1call _CheckIfEnoughEnergiesToAttack - jr nc, .found -.skip_pkmn_atk - pop de -.next_pkmn_atk - inc e - dec d - jr nz, .loop_atk - -; none found in Bench. -; next, look for a non-colorless Bench Pokemon -; that has any Energy cards attached. - ld d, e ; number of Play Area Pokemon - ld e, PLAY_AREA_ARENA - jr .next_pkmn_energy - -.loop_energy - push de - call GetPlayAreaCardAttachedEnergies - ld a, [wTotalAttachedEnergies] - or a - jr z, .skip_pkmn_energy - ld a, e - add DUELVARS_ARENA_CARD - call GetTurnDuelistVariable - ld d, a - call LoadCardDataToBuffer1_FromDeckIndex - ld a, [wLoadedCard1Type] - cp COLORLESS - jr nz, .found -.skip_pkmn_energy - pop de -.next_pkmn_energy - inc e - dec d - jr nz, .loop_energy - -; otherwise, just select a random energy. - ld a, NUM_COLORED_TYPES - call Random - ldh [hTemp_ffa0], a - ret - -.found - pop de - ld a, [wLoadedCard1Type] - and TYPE_PKMN - ldh [hTemp_ffa0], a - ret - -ScrunchEffect: ; 2eee7 (b:6ee7) - ldtx de, IfHeadsNoDamageNextTurnText - call TossCoin_BankB - jp nc, SetWasUnsuccessful - ld a, ATK_ANIM_SCRUNCH - ld [wLoadedAttackAnimation], a - ld a, SUBSTATUS1_NO_DAMAGE_17 - call ApplySubstatus1ToDefendingCard - ret - -ChanseyDoubleEdgeEffect: ; 2eefb (b:6efb) - ld a, 80 - call DealRecoilDamageToSelf - ret - -SuperFang_AIEffect: ; 2ef01 (b:6f01) - call SuperFang_HalfHPEffect - jp SetDefiniteAIDamage - -SuperFang_HalfHPEffect: ; 2ef07 (b:6f07) - ld a, DUELVARS_ARENA_CARD_HP - call GetNonTurnDuelistVariable - srl a - bit 0, a - jr z, .rounded - ; round up - add 5 -.rounded - call SetDefiniteDamage - ret - -; return carry if no Pokemon in Bench -TrainerCardAsPokemon_BenchCheck: ; 2ef18 (b:6f18) - ldh a, [hTempPlayAreaLocation_ff9d] - ldh [hTemp_ffa0], a - ld a, DUELVARS_NUMBER_OF_POKEMON_IN_PLAY_AREA - call GetTurnDuelistVariable - ldtx hl, EffectNoPokemonOnTheBenchText - cp 2 - ret - -TrainerCardAsPokemon_PlayerSelectSwitch: ; 2ef27 (b:6f27) - ldh a, [hTemp_ffa0] - or a - ret nz ; no need to switch if it's not Arena card - - ldtx hl, SelectPokemonToPlaceInTheArenaText - call DrawWideTextBox_WaitForInput - bank1call HasAlivePokemonInBench - bank1call OpenPlayAreaScreenForSelection - ldh a, [hTempPlayAreaLocation_ff9d] - ldh [hTempPlayAreaLocation_ffa1], a - ret - -TrainerCardAsPokemon_DiscardEffect: ; 2ef3c (b:6f3c) - ldh a, [hTemp_ffa0] - ld e, a - call MovePlayAreaCardToDiscardPile - ldh a, [hTemp_ffa0] - or a - jr nz, .shift_cards - ldh a, [hTempPlayAreaLocation_ffa1] - ld e, a - call SwapArenaWithBenchPokemon -.shift_cards - call ShiftAllPokemonToFirstPlayAreaSlots - ret - -HealingWind_InitialEffect: ; 2ef51 (b:6f51) - scf - ret - -HealingWind_PlayAreaHealEffect: ; 2ef53 (b:6f53) -; play initial animation - ldh a, [hTempPlayAreaLocation_ff9d] - ld b, a - ld c, $00 - ldh a, [hWhoseTurn] - ld h, a - bank1call PlayAttackAnimation - bank1call WaitAttackAnimation - ld a, ATK_ANIM_HEALING_WIND_PLAY_AREA - ld [wLoadedAttackAnimation], a - - - ld a, DUELVARS_NUMBER_OF_POKEMON_IN_PLAY_AREA - call GetTurnDuelistVariable - ld d, a - ld e, PLAY_AREA_ARENA -.loop_play_area - push de - ld a, e - ldh [hTempPlayAreaLocation_ff9d], a - call GetCardDamageAndMaxHP - or a - jr z, .next_pkmn ; skip if no damage - -; if less than 20 damage, cap recovery at 10 damage - ld de, 20 - cp e - jr nc, .heal - ld e, a - -.heal -; add HP to this card - ldh a, [hTempPlayAreaLocation_ff9d] - add DUELVARS_ARENA_CARD_HP - call GetTurnDuelistVariable - add e - ld [hl], a - -; play heal animation - ldh a, [hTempPlayAreaLocation_ff9d] - ld b, a - ld c, $01 - ldh a, [hWhoseTurn] - ld h, a - bank1call PlayAttackAnimation - bank1call WaitAttackAnimation -.next_pkmn - pop de - inc e - dec d - jr nz, .loop_play_area - - ret - -Dragonite1Slam_AIEffect: ; 2ef9c (b:6f9c) - ld a, (30 * 2) / 2 - lb de, 0, 60 - jp SetExpectedAIDamage - -Dragonite1Slam_MultiplierEffect: ; 2efa4 (b:6fa4) - ld hl, 30 - call LoadTxRam3 - ldtx de, DamageCheckIfHeadsXDamageText - ld a, 2 - call TossCoinATimes_BankB - ld c, a - add a - add c - call ATimes10 - call SetDefiniteDamage - ret - -; possibly unreferenced -Func_2efbc: ; 2efbc (b:6fbc) - ld a, DUELVARS_NUMBER_OF_POKEMON_IN_PLAY_AREA - call GetTurnDuelistVariable - ld c, a - ld l, DUELVARS_ARENA_CARD_HP - ld de, wce76 -.asm_2efc7 - ld a, [hli] - ld [de], a - inc de - dec c - jr nz, .asm_2efc7 - ret - -; possibly unreferenced -Func_2efce: ; 2efce (b:6fce) - ld a, DUELVARS_NUMBER_OF_POKEMON_IN_PLAY_AREA - call GetTurnDuelistVariable - ld c, a - ld l, DUELVARS_ARENA_CARD_HP - ld de, wce76 -.asm_2efd9 - ld a, [de] - inc de - ld [hli], a - dec c - jr nz, .asm_2efd9 - ret - -CatPunchEffect: ; 2efe0 (b:6fe0) - call SwapTurn - call PickRandomPlayAreaCard - ld b, a - ld a, ATK_ANIM_CAT_PUNCH_PLAY_AREA - ld [wLoadedAttackAnimation], a - ld de, 20 - call DealDamageToPlayAreaPokemon - call SwapTurn - ret - -MorphEffect: ; 2eff6 (b:6ff6) - call ExchangeRNG - call .PickRandomBasicPokemonFromDeck - jr nc, .successful - ldtx hl, AttackUnsuccessfulText - call DrawWideTextBox_WaitForInput - ret - -.successful - ld a, DUELVARS_ARENA_CARD_STAGE - call GetTurnDuelistVariable - or a - jr z, .skip_discard_stage_below - -; if this is a stage 1 Pokemon (in case it's used -; by Clefable's Metronome attack) then first discard -; the lower stage card. - push hl - xor a - ldh [hTempPlayAreaLocation_ff9d], a - bank1call GetCardOneStageBelow - ld a, d - call PutCardInDiscardPile - pop hl - ld [hl], BASIC - -.skip_discard_stage_below -; overwrite card ID - ldh a, [hTempCardIndex_ff98] - call GetCardIDFromDeckIndex - ld a, DUELVARS_ARENA_CARD - call GetTurnDuelistVariable - ldh [hTempCardIndex_ff98], a - call _GetCardIDFromDeckIndex - ld [hl], e - -; overwrite HP to new card's maximum HP - ld e, PLAY_AREA_ARENA - call GetCardDamageAndMaxHP - ld a, DUELVARS_ARENA_CARD_HP - call GetTurnDuelistVariable - ld [hl], c - -; clear changed color and status - ld l, DUELVARS_ARENA_CARD_CHANGED_TYPE - ld [hl], $00 - call ClearAllStatusConditions - -; load both card's names for printing text - ld a, [wTempTurnDuelistCardID] - ld e, a - ld d, $00 - call LoadCardDataToBuffer2_FromCardID - ld hl, wLoadedCard2Name - ld de, wTxRam2 - ld a, [hli] - ld [de], a - inc de - ld a, [hl] - ld [de], a - inc de - ldh a, [hTempCardIndex_ff98] - call LoadCardDataToBuffer2_FromDeckIndex - ld hl, wLoadedCard2Name - ld a, [hli] - ld [de], a - inc de - ld a, [hl] - ld [de], a - ldtx hl, MetamorphsToText - call DrawWideTextBox_WaitForInput - - xor a - ld [wDuelDisplayedScreen], a - ret - -; picks a random Pokemon in the Deck to morph. -; needs to be a Basic Pokemon that doesn't have -; the same ID as the Arena card. -; returns carry if no Pokemon were found. -.PickRandomBasicPokemonFromDeck ; 2f06a (b:706a) - call CreateDeckCardList - ret c ; empty deck - ld hl, wDuelTempList - call ShuffleCards -.loop_deck - ld a, [hli] - ldh [hTempCardIndex_ff98], a - cp $ff - jr z, .set_carry - call LoadCardDataToBuffer2_FromDeckIndex - ld a, [wLoadedCard2Type] - cp TYPE_ENERGY - jr nc, .loop_deck ; skip non-Pokemon cards - ld a, [wLoadedCard2Stage] - or a - jr nz, .loop_deck ; skip non-Basic cards - ld a, [wLoadedCard2ID] - cp DUELVARS_ARENA_CARD - jr z, .loop_deck ; skip cards with same ID as Arena card - ldh a, [hTempCardIndex_ff98] - or a - ret -.set_carry - scf - ret - -; returns in a and [hTempCardIndex_ff98] the deck index -; of random Basic Pokemon card in deck. -; if none are found, return carry. -PickRandomBasicCardFromDeck: ; 2f098 (b:7098) - call CreateDeckCardList - ret c ; return if empty deck - ld hl, wDuelTempList - call ShuffleCards -.loop_deck - ld a, [hli] - ldh [hTempCardIndex_ff98], a - cp $ff - jr z, .set_carry - call LoadCardDataToBuffer2_FromDeckIndex - ld a, [wLoadedCard2Type] - cp TYPE_ENERGY - jr nc, .loop_deck ; skip if not Pokemon card - ld a, [wLoadedCard2Stage] - or a - jr nz, .loop_deck ; skip if not Basic stage - ldh a, [hTempCardIndex_ff98] - or a - ret -.set_carry - scf - ret - -SlicingWindEffect: ; 2f0bf (b:70bf) - call SwapTurn - call PickRandomPlayAreaCard - ld b, a - ld de, 30 - call DealDamageToPlayAreaPokemon_RegularAnim - call SwapTurn - ret - -Gale_LoadAnimation: ; 2f0d0 (b:70d0) - ld a, ATK_ANIM_GALE - ld [wLoadedAttackAnimation], a - ret - -Gale_SwitchEffect: ; 2f0d6 (b:70d6) -; if Defending card is unaffected by attack -; jump directly to switching this card only. - call HandleNoDamageOrEffect - jr c, .SwitchWithRandomBenchPokemon - -; handle switching Defending card - ld a, DUELVARS_ARENA_CARD_HP - call GetNonTurnDuelistVariable - or a - jr nz, .skip_destiny_bond - bank1call HandleDestinyBondSubstatus -.skip_destiny_bond - call SwapTurn - call .SwitchWithRandomBenchPokemon - jr c, .skip_clear_damage -; clear dealt damage because Pokemon was switched - xor a - ld hl, wDealtDamage - ld [hli], a - ld [hl], a -.skip_clear_damage - call SwapTurn -; fallthrough for attacking card switch - -.SwitchWithRandomBenchPokemon - ld a, DUELVARS_NUMBER_OF_POKEMON_IN_PLAY_AREA - call GetTurnDuelistVariable - cp 2 - ret c ; return if no Bench Pokemon - -; get random Bench location and swap - dec a - call Random - inc a - ld e, a - call SwapArenaWithBenchPokemon - - xor a - ld [wDuelDisplayedScreen], a - ret - -; return carry if Bench is full -FriendshipSong_BenchCheck: ; 2f10d (b:710d) - ld a, DUELVARS_NUMBER_OF_POKEMON_IN_PLAY_AREA - call GetTurnDuelistVariable - ldtx hl, NoSpaceOnTheBenchText - cp MAX_PLAY_AREA_POKEMON - ccf - ret - -FriendshipSong_AddToBench50PercentEffect: ; 2f119 (b:7119) - ldtx de, SuccessCheckIfHeadsAttackIsSuccessfulText - call TossCoin_BankB - jr c, .successful - -.none_came_text - ldtx hl, NoneCameText - call DrawWideTextBox_WaitForInput - ret - -.successful - call PickRandomBasicCardFromDeck - jr nc, .put_in_bench - ld a, ATK_ANIM_FRIENDSHIP_SONG - call Func_2c12e - call .none_came_text - call Func_2c0bd - ret - -.put_in_bench - call SearchCardInDeckAndAddToHand - call AddCardToHand - call PutHandPokemonCardInPlayArea - ld a, ATK_ANIM_FRIENDSHIP_SONG - call Func_2c12e - ldh a, [hTempCardIndex_ff98] - ldtx hl, CameToTheBenchText - bank1call DisplayCardDetailScreen - call Func_2c0bd - ret - -ExpandEffect: ; 2f153 (b:7153) - ld a, SUBSTATUS1_REDUCE_BY_10 - call ApplySubstatus1ToDefendingCard - ret - -; returns carry if either there are no damage counters -; or no Energy cards attached in the Play Area. -SuperPotion_DamageEnergyCheck: ; 2f159 (b:7159) - call CheckIfPlayAreaHasAnyDamage - ldtx hl, NoPokemonWithDamageCountersText - ret c ; no damage counters - call CheckIfThereAreAnyEnergyCardsAttached - ldtx hl, ThereIsNoEnergyCardAttachedText - ret - -SuperPotion_PlayerSelectEffect: ; 2f167 (b:7167) - ldtx hl, ChoosePokemonToRemoveDamageCounterFromText - call DrawWideTextBox_WaitForInput -.start - bank1call HasAlivePokemonInPlayArea -.read_input - bank1call OpenPlayAreaScreenForSelection - ret c ; exit if B is pressed - ld e, a - call GetCardDamageAndMaxHP - or a - jr z, .read_input ; Pokemon has no damage? - ldh a, [hCurMenuItem] - ld e, a - call GetPlayAreaCardAttachedEnergies - ld a, [wTotalAttachedEnergies] - or a - jr nz, .got_pkmn - ; no energy cards attached - ldtx hl, NoEnergyCardsText - call DrawWideTextBox_WaitForInput - jr .start - -.got_pkmn -; Pokemon has damage and Energy cards attached, -; prompt the Player for Energy selection to discard. - ldh a, [hCurMenuItem] - bank1call CreateArenaOrBenchEnergyCardList - ldh a, [hCurMenuItem] - bank1call DisplayEnergyDiscardScreen - bank1call HandleEnergyDiscardMenuInput - ret c ; exit if B was pressed - - ldh a, [hTempCardIndex_ff98] - ldh [hTemp_ffa0], a - ldh a, [hTempPlayAreaLocation_ff9d] - ldh [hTempPlayAreaLocation_ffa1], a - ld e, a - -; cap the healing damage if -; it would make it exceed max HP. - call GetCardDamageAndMaxHP - ld c, 40 - cp 40 - jr nc, .heal - ld c, a -.heal - ld a, c - ldh [hPlayAreaEffectTarget], a - or a - ret - -SuperPotion_HealEffect: ; 2f1b5 (b:71b5) - ldh a, [hTemp_ffa0] - call PutCardInDiscardPile - ldh a, [hTempPlayAreaLocation_ffa1] - ldh [hTempPlayAreaLocation_ff9d], a - ldh a, [hPlayAreaEffectTarget] - call HealPlayAreaCardHP - ret - -; checks if there is at least one Energy card -; attached to some card in the Turn Duelist's Play Area. -; return no carry if one is found, -; and returns carry set if none is found. -CheckIfThereAreAnyEnergyCardsAttached: ; 2f1c4 (b:71c4) - ld a, DUELVARS_CARD_LOCATIONS - call GetTurnDuelistVariable -.loop_deck - ld a, [hl] - bit CARD_LOCATION_PLAY_AREA_F, a - jr z, .next_card ; skip if not in Play Area - ld a, l - call LoadCardDataToBuffer2_FromDeckIndex - ld a, [wLoadedCard2Type] - cp TYPE_TRAINER - jr z, .next_card ; skip if it's a Trainer card - cp TYPE_ENERGY - jr nc, .found -.next_card - inc l - ld a, l - cp DECK_SIZE - jr c, .loop_deck - scf - ret -.found - or a - ret - -; handles Player selection for Pokemon in Play Area, -; then opens screen to choose one of the energy cards -; attached to that selected Pokemon. -; outputs the selection in: -; [hTemp_ffa0] = play area location -; [hTempPlayAreaLocation_ffa1] = index of energy card -HandlePokemonAndEnergySelectionScreen: ; 2f1e7 (b:71e7) - bank1call HasAlivePokemonInPlayArea - bank1call OpenPlayAreaScreenForSelection - ret c ; exit if B is pressed - ld e, a - call GetPlayAreaCardAttachedEnergies - ld a, [wTotalAttachedEnergies] - or a - jr nz, .has_energy - ldtx hl, NoEnergyCardsText - call DrawWideTextBox_WaitForInput - jr HandlePokemonAndEnergySelectionScreen ; loop back to start - -.has_energy - ldh a, [hCurMenuItem] - bank1call CreateArenaOrBenchEnergyCardList - ldh a, [hCurMenuItem] - bank1call DisplayEnergyDiscardScreen - bank1call HandleEnergyDiscardMenuInput - ldh a, [hTempPlayAreaLocation_ff9d] - ldh [hTemp_ffa0], a - ldh a, [hTempCardIndex_ff98] - ldh [hTempPlayAreaLocation_ffa1], a - ret - -ImakuniEffect: ; 2f216 (b:7216) - ld a, DUELVARS_ARENA_CARD - call GetTurnDuelistVariable - call LoadCardDataToBuffer1_FromDeckIndex - ld a, [wLoadedCard1ID] - -; cannot confuse Clefairy Doll and Mysterious Fossil - cp CLEFAIRY_DOLL - jr z, .failed - cp MYSTERIOUS_FOSSIL - jr z, .failed - -; cannot confuse Snorlax if its Pkmn Power is active - cp SNORLAX - jr nz, .success - xor a - call CheckCannotUseDueToStatus_OnlyToxicGasIfANon0 - jr c, .success - ; fallthrough if Thick Skinned is active - -.failed -; play confusion animation and print failure text - ld a, ATK_ANIM_IMAKUNI_CONFUSION - call Func_2fea9 - ldtx hl, ThereWasNoEffectText - call DrawWideTextBox_WaitForInput - ret - -.success -; play confusion animation and confuse card - ld a, ATK_ANIM_IMAKUNI_CONFUSION - call Func_2fea9 - ld a, DUELVARS_ARENA_CARD_STATUS - call GetTurnDuelistVariable - and PSN_DBLPSN - or CONFUSED - ld [hl], a - bank1call DrawDuelHUDs - ret - -; returns carry if opponent has no energy cards attached -EnergyRemoval_EnergyCheck: ; 2f252 (b:7252) - call SwapTurn - call CheckIfThereAreAnyEnergyCardsAttached - ldtx hl, NoEnergyAttachedToOpponentsActiveText - call SwapTurn - ret - -EnergyRemoval_PlayerSelection: ; 2f25f (b:725f) - ldtx hl, ChoosePokemonToRemoveEnergyFromText - call DrawWideTextBox_WaitForInput - call SwapTurn - call HandlePokemonAndEnergySelectionScreen - call SwapTurn - ret - -EnergyRemoval_AISelection: ; 2f26f (b:726f) - call AIPickEnergyCardToDiscardFromDefendingPokemon - ret - -EnergyRemoval_DiscardEffect: ; 2f273 (b:7273) - call SwapTurn - ldh a, [hTempPlayAreaLocation_ffa1] - call PutCardInDiscardPile - call SwapTurn - call IsPlayerTurn - ret c - -; show Player which Pokemon was affected - call SwapTurn - ldh a, [hTemp_ffa0] - call Func_2c10b - call SwapTurn - ret - -; return carry if no other card in hand to discard -; or if there are no Basic Energy cards in Discard Pile. -EnergyRetrieval_HandEnergyCheck: ; 2f28e (b:728e) - ld a, DUELVARS_NUMBER_OF_CARDS_IN_HAND - call GetTurnDuelistVariable - cp 2 - ldtx hl, NotEnoughCardsInHandText - ret c ; return if doesn't have another card to discard - call CreateEnergyCardListFromDiscardPile_OnlyBasic - ldtx hl, ThereAreNoBasicEnergyCardsInDiscardPileText - ret - -EnergyRetrieval_PlayerHandSelection: ; 2f2a0 (b:72a0) - ldtx hl, ChooseCardToDiscardFromHandText - call DrawWideTextBox_WaitForInput - call CreateHandCardList - ldh a, [hTempCardIndex_ff9f] - call RemoveCardFromDuelTempList - bank1call Func_5591 - bank1call DisplayCardList - ldh a, [hTempCardIndex_ff98] - ldh [hTempList], a - ret - -EnergyRetrieval_PlayerDiscardPileSelection: ; 2f2b9 (b:72b9) - ld a, 1 ; start at 1 due to card selected from hand - ldh [hCurSelectionItem], a - ldtx hl, Choose2BasicEnergyCardsFromDiscardPileText - call DrawWideTextBox_WaitForInput - call CreateEnergyCardListFromDiscardPile_OnlyBasic - -.select_card - bank1call InitAndDrawCardListScreenLayout - ldtx hl, PleaseSelectCardText - ldtx de, PlayerDiscardPileText - bank1call SetCardListHeaderText - bank1call DisplayCardList - jr nc, .selected - ; B was pressed - ld a, 2 + 1 ; includes the card selected from hand - call AskWhetherToQuitSelectingCards - jr c, .select_card ; player selected No - jr .done - -.selected - call GetNextPositionInTempList_TrainerEffects - ldh a, [hTempCardIndex_ff98] - ld [hl], a - call RemoveCardFromDuelTempList - jr c, .done - ldh a, [hCurSelectionItem] - cp 2 + 1 ; includes the card selected from hand - jr c, .select_card - -.done - call GetNextPositionInTempList_TrainerEffects - ld [hl], $ff ; terminating byte - or a - ret - -EnergyRetrieval_DiscardAndAddToHandEffect: ; 2f2f8 (b:72f8) - ld hl, hTempList - ld a, [hli] - call RemoveCardFromHand - call PutCardInDiscardPile - ld de, wDuelTempList -.loop - ld a, [hli] - ld [de], a - inc de - cp $ff - jr z, .done - call MoveDiscardPileCardToHand - call AddCardToHand - jr .loop -.done - call IsPlayerTurn - ret c - bank1call Func_4b38 - ret - -; return carry if no cards left in Deck. -EnergySearch_DeckCheck: ; 2f31c (b:731c) - ld a, DUELVARS_NUMBER_OF_CARDS_NOT_IN_DECK - call GetTurnDuelistVariable - cp DECK_SIZE - ccf - ldtx hl, NoCardsLeftInTheDeckText - ret - -EnergySearch_PlayerSelection: ; 2f328 (b:7328) - ld a, $ff - ldh [hTemp_ffa0], a - call CreateDeckCardList - ldtx hl, Choose1BasicEnergyCardFromDeckText - lb de, SEARCHEFFECT_BASIC_ENERGY, 0 - ldtx bc, BasicEnergyText - call LookForCardsInDeck - ret c ; skip showing deck - - bank1call Func_5591 - ldtx hl, ChooseBasicEnergyCardText - ldtx de, DuelistDeckText - bank1call SetCardListHeaderText -.read_input - bank1call DisplayCardList - jr c, .try_exit ; B pressed? - ldh a, [hTempCardIndex_ff98] - ldh [hTemp_ffa0], a - call CheckIfCardIsBasicEnergy - jr c, .play_sfx - or a - ret -.play_sfx - call Func_3794 - jr .read_input - -.try_exit -; check if Player can exit without selecting anything - ld hl, wDuelTempList -.next_card - ld a, [hli] - cp $ff - jr z, .exit - call CheckIfCardIsBasicEnergy - jr c, .next_card - jr .read_input ; no, has to select Energy card -.exit - ld a, $ff - ldh [hTemp_ffa0], a - or a - ret - -EnergySearch_AddToHandEffect: ; 2f372 (b:7372) - ldh a, [hTemp_ffa0] - cp $ff - jr z, .done -; add to hand - call SearchCardInDeckAndAddToHand - call AddCardToHand - call IsPlayerTurn - jr c, .done ; done if Player played card -; display card in screen - ldh a, [hTemp_ffa0] - ldtx hl, WasPlacedInTheHandText - bank1call DisplayCardDetailScreen -.done - call Func_2c0bd - ret - -; check if card index in a is a Basic Energy card. -; returns carry in case it's not. -CheckIfCardIsBasicEnergy: ; 2f38f (b:738f) - call LoadCardDataToBuffer2_FromDeckIndex - ld a, [wLoadedCard2Type] - cp TYPE_ENERGY - jr c, .not_basic_energy - cp TYPE_ENERGY_DOUBLE_COLORLESS - jr nc, .not_basic_energy -; is basic energy - or a - ret -.not_basic_energy - scf - ret - -ProfessorOakEffect: ; 2f3a1 (b:73a1) -; discard hand - call CreateHandCardList - call SortCardsInDuelTempListByID - ld hl, wDuelTempList -.discard_loop - ld a, [hli] - cp $ff - jr z, .draw_cards - call RemoveCardFromHand - call PutCardInDiscardPile - jr .discard_loop - -.draw_cards - ld a, 7 - bank1call DisplayDrawNCardsScreen - ld c, 7 -.draw_loop - call DrawCardFromDeck - jr c, .done - call AddCardToHand - dec c - jr nz, .draw_loop -.done - ret - -Potion_DamageCheck: ; 2f3ca (b:73ca) - call CheckIfPlayAreaHasAnyDamage - ldtx hl, NoPokemonWithDamageCountersText - ret - -Potion_PlayerSelection: ; 2f3d1 (b:73d1) - bank1call HasAlivePokemonInPlayArea -.read_input - bank1call OpenPlayAreaScreenForSelection - ret c ; exit is B was pressed - ldh a, [hTempPlayAreaLocation_ff9d] - ldh [hTemp_ffa0], a - ld e, a - call GetCardDamageAndMaxHP - or a - jr z, .read_input ; no damage, loop back to start -; cap damage - ld c, 20 - cp 20 - jr nc, .skip_cap - ld c, a -.skip_cap - ld a, c - ldh [hTempPlayAreaLocation_ffa1], a - or a - ret - -Potion_HealEffect: ; 2f3ef (b:73ef) - ldh a, [hTemp_ffa0] - ldh [hTempPlayAreaLocation_ff9d], a - ldh a, [hTempPlayAreaLocation_ffa1] - call HealPlayAreaCardHP - ret - -GamblerEffect: ; 2f3f9 (b:73f9) - ldtx de, CardCheckIfHeads8CardsIfTails1CardText - call TossCoin_BankB - ldh [hTemp_ffa0], a -; discard Gambler card from hand - ldh a, [hTempCardIndex_ff9f] - call RemoveCardFromHand - call PutCardInDiscardPile - -; shuffle cards into deck - call CreateHandCardList - call SortCardsInDuelTempListByID - ld hl, wDuelTempList -.loop_return_deck - ld a, [hli] - cp $ff - jr z, .check_coin_toss - call RemoveCardFromHand - call ReturnCardToDeck - jr .loop_return_deck - -.check_coin_toss - call Func_2c0bd - ld c, 8 - ldh a, [hTemp_ffa0] - or a - jr nz, .draw_cards ; coin toss was heads? - ; if tails, number of cards to draw is 1 - ld c, 1 - -; correct number of cards to draw is in c -.draw_cards - ld a, c - bank1call DisplayDrawNCardsScreen -.draw_loop - call DrawCardFromDeck - jr c, .done - call AddCardToHand - dec c - jr nz, .draw_loop -.done - ret - -; return carry if not enough cards in hand to discard -; or if there are no cards in the Discard Pile -ItemFinder_HandDiscardPileCheck: ; 2f43b (b:743b) - ld a, DUELVARS_NUMBER_OF_CARDS_IN_HAND - call GetTurnDuelistVariable - ldtx hl, NotEnoughCardsInHandText - cp 3 - ret c - call CreateTrainerCardListFromDiscardPile - ret - -ItemFinder_PlayerSelection: ; 2f44a (b:744a) - call HandlePlayerSelection2HandCardsToDiscard - ret c ; was operation cancelled? - -; cards were selected to discard from hand. -; now to choose a Trainer card from Discard Pile. - call CreateTrainerCardListFromDiscardPile - bank1call Func_5591 - ldtx hl, ChooseCardToPlaceInHandText - ldtx de, PlayerDiscardPileText - bank1call SetCardListHeaderText - bank1call DisplayCardList - ldh [hTempList + 2], a ; placed after the 2 cards selected to discard - ret - -ItemFinder_DiscardAddToHandEffect: ; 2f463 (b:7463) -; discard cards from hand - ld hl, hTempList - ld a, [hli] - call RemoveCardFromHand - call PutCardInDiscardPile - ld a, [hli] - call RemoveCardFromHand - call PutCardInDiscardPile - -; place card from Discard Pile to hand - ld a, [hl] - call MoveDiscardPileCardToHand - call AddCardToHand - call IsPlayerTurn - ret c -; display card in screen - ldh a, [hTempList + 2] - ldtx hl, WasPlacedInTheHandText - bank1call DisplayCardDetailScreen - ret - -Defender_PlayerSelection: ; 2f488 (b:7488) - ldtx hl, ChoosePokemonToAttachDefenderToText - call DrawWideTextBox_WaitForInput - bank1call HasAlivePokemonInPlayArea - bank1call OpenPlayAreaScreenForSelection - ldh a, [hTempPlayAreaLocation_ff9d] - ldh [hTemp_ffa0], a - ret - -Defender_AttachDefenderEffect: ; 2f499 (b:7499) -; attach Trainer card to Play Area Pokemon - ldh a, [hTemp_ffa0] - ld e, a - ldh a, [hTempCardIndex_ff9f] - call PutHandCardInPlayArea - -; increase number of Defender cards of this location by 1 - ldh a, [hTemp_ffa0] - add DUELVARS_ARENA_CARD_ATTACHED_DEFENDER - call GetTurnDuelistVariable - inc [hl] - call IsPlayerTurn - ret c - - ldh a, [hTemp_ffa0] - call Func_2c10b - ret - -; return carry if Bench is full. -MysteriousFossil_BenchCheck: ; 2f4b3 (b:74b3) - ld a, DUELVARS_NUMBER_OF_POKEMON_IN_PLAY_AREA - call GetTurnDuelistVariable - cp MAX_PLAY_AREA_POKEMON - ccf - ldtx hl, NoSpaceOnTheBenchText - ret - -MysteriousFossil_PlaceInPlayAreaEffect: ; 2f4bf (b:74bf) - ldh a, [hTempCardIndex_ff9f] - call PutHandPokemonCardInPlayArea - ret - -; return carry if Arena card has no status to heal. -FullHeal_StatusCheck: ; 2f4c5 (b:74c5) - ld a, DUELVARS_ARENA_CARD_STATUS - call GetTurnDuelistVariable - or a - ret nz - ldtx hl, NotAffectedByPoisonSleepParalysisOrConfusionText - scf - ret - -FullHeal_ClearStatusEffect: ; 2f4d1 (b:74d1) - ld a, ATK_ANIM_FULL_HEAL - call Func_2fea9 - ld a, DUELVARS_ARENA_CARD_STATUS - call GetTurnDuelistVariable - ld [hl], NO_STATUS - bank1call DrawDuelHUDs - ret - -ImposterProfessorOakEffect: ; 2f4e1 (b:74e1) - call SwapTurn - call CreateHandCardList - call SortCardsInDuelTempListByID - -; first return all cards in hand to the deck. - ld hl, wDuelTempList -.loop_return_deck - ld a, [hli] - cp $ff - jr z, .done_return - call RemoveCardFromHand - call ReturnCardToDeck - jr .loop_return_deck - -; then draw 7 cards from the deck. -.done_return - call Func_2c0bd - ld a, 7 - bank1call DisplayDrawNCardsScreen - ld c, 7 -.loop_draw - call DrawCardFromDeck - jr c, .done - call AddCardToHand - dec c - jr nz, .loop_draw -.done - call SwapTurn - ret - -; return carry if not enough cards in hand to discard -; or if there are no cards left in the deck. -ComputerSearch_HandDeckCheck: ; 2f513 (b:7513) - ld a, DUELVARS_NUMBER_OF_CARDS_IN_HAND - call GetTurnDuelistVariable - ldtx hl, NotEnoughCardsInHandText - cp 3 - ret c - ld a, DUELVARS_NUMBER_OF_CARDS_NOT_IN_DECK - call GetTurnDuelistVariable - ldtx hl, NoCardsLeftInTheDeckText - cp DECK_SIZE - ccf - ret - -ComputerSearch_PlayerDiscardHandSelection: ; 2f52a (b:752a) - call HandlePlayerSelection2HandCardsToDiscard - ret - -ComputerSearch_PlayerDeckSelection: ; 2f52e (b:752e) - call CreateDeckCardList - bank1call Func_5591 - ldtx hl, ChooseCardToPlaceInHandText - ldtx de, DuelistDeckText - bank1call SetCardListHeaderText -.loop_input - bank1call DisplayCardList - jr c, .loop_input ; can't exit with B button - ldh [hTempList + 2], a - ret - -ComputerSearch_DiscardAddToHandEffect: ; 2f545 (b:7545) -; discard cards from hand - ld hl, hTempList - ld a, [hli] - call RemoveCardFromHand - call PutCardInDiscardPile - ld a, [hli] - call RemoveCardFromHand - call PutCardInDiscardPile - -; add card from deck to hand - ld a, [hl] - call SearchCardInDeckAndAddToHand - call AddCardToHand - call Func_2c0bd - ret - -; return carry if Bench is full. -ClefairyDoll_BenchCheck: ; 2f561 (b:7561) - ld a, DUELVARS_NUMBER_OF_POKEMON_IN_PLAY_AREA - call GetTurnDuelistVariable - ldtx hl, NoSpaceOnTheBenchText - cp MAX_PLAY_AREA_POKEMON - ccf - ret - -ClefairyDoll_PlaceInPlayAreaEffect: ; 2f56d (b:756d) - ldh a, [hTempCardIndex_ff9f] - call PutHandPokemonCardInPlayArea - ret - -; return carry if no Pokemon in the Bench. -MrFuji_BenchCheck: ; 2f573 (b:7573) - ld a, DUELVARS_NUMBER_OF_POKEMON_IN_PLAY_AREA - call GetTurnDuelistVariable - ldtx hl, EffectNoPokemonOnTheBenchText - cp 2 - ret - -MrFuji_PlayerSelection: ; 2f57e (b:757e) - ldtx hl, ChoosePokemonToReturnToTheDeckText - call DrawWideTextBox_WaitForInput - bank1call HasAlivePokemonInBench - bank1call OpenPlayAreaScreenForSelection - ldh a, [hTempPlayAreaLocation_ff9d] - ldh [hTemp_ffa0], a - ret - -MrFuji_ReturnToDeckEffect: ; 2f58f (b:758f) -; get Play Area location's card index - ldh a, [hTemp_ffa0] - add DUELVARS_ARENA_CARD - call GetTurnDuelistVariable - ldh [hTempCardIndex_ff98], a - -; find all cards that are in the same location -; (previous evolutions and energy cards attached) -; and return them all to the deck. - ldh a, [hTemp_ffa0] - or CARD_LOCATION_PLAY_AREA - ld e, a - ld a, DUELVARS_CARD_LOCATIONS - call GetTurnDuelistVariable -.loop_cards - push de - push hl - ld a, [hl] - cp e - jr nz, .next_card - ld a, l - call ReturnCardToDeck -.next_card - pop hl - pop de - inc l - ld a, l - cp DECK_SIZE - jr c, .loop_cards - -; clear Play Area location of card - ldh a, [hTemp_ffa0] - ld e, a - call EmptyPlayAreaSlot - ld l, DUELVARS_NUMBER_OF_POKEMON_IN_PLAY_AREA - dec [hl] - call ShiftAllPokemonToFirstPlayAreaSlots - -; if Trainer card wasn't played by the Player, -; print the selected Pokemon's name and show card on screen. - call IsPlayerTurn - jr c, .done - ldh a, [hTempCardIndex_ff98] - call LoadCardDataToBuffer1_FromDeckIndex - ld hl, wLoadedCard1Name - ld a, [hli] - ld h, [hl] - ld l, a - call LoadTxRam2 - bank1call DrawLargePictureOfCard - ldtx hl, PokemonAndAllAttachedCardsWereReturnedToDeckText - call DrawWideTextBox_WaitForInput -.done - call Func_2c0bd - ret - -PlusPowerEffect: ; 2f5e0 (b:75e0) -; attach Trainer card to Arena Pokemon - ld e, PLAY_AREA_ARENA - ldh a, [hTempCardIndex_ff9f] - call PutHandCardInPlayArea - -; increase number of Defender cards of this location by 1 - ld a, DUELVARS_ARENA_CARD_ATTACHED_PLUSPOWER - call GetTurnDuelistVariable - inc [hl] - ret - -; return carry if no Pokemon in the Bench. -Switch_BenchCheck: ; 2f5ee (b:75ee) - ld a, DUELVARS_NUMBER_OF_POKEMON_IN_PLAY_AREA - call GetTurnDuelistVariable - ldtx hl, EffectNoPokemonOnTheBenchText - cp 2 - ret - -Switch_PlayerSelection: ; 2f5f9 (b:75f9) - ldtx hl, SelectPkmnOnBenchToSwitchWithActiveText - call DrawWideTextBox_WaitForInput - bank1call HasAlivePokemonInBench - bank1call OpenPlayAreaScreenForSelection - ldh a, [hTempPlayAreaLocation_ff9d] - ldh [hTemp_ffa0], a - ret - -Switch_SwitchEffect: ; 2f60a (b:760a) - ldh a, [hTemp_ffa0] - ld e, a - call SwapArenaWithBenchPokemon - ret - -PokemonCenter_DamageCheck: ; 2f611 (b:7611) - call CheckIfPlayAreaHasAnyDamage - ldtx hl, NoPokemonWithDamageCountersText - ret - -PokemonCenter_HealDiscardEnergyEffect: ; 2f618 (b:7618) - ld a, DUELVARS_NUMBER_OF_POKEMON_IN_PLAY_AREA - call GetTurnDuelistVariable - ld d, a - ld e, PLAY_AREA_ARENA - -; go through every Pokemon in the Play Area -; and heal all damage & discard their Energy cards. -.loop_play_area -; check its damage - ld a, e - ldh [hTempPlayAreaLocation_ff9d], a - call GetCardDamageAndMaxHP - or a - jr z, .next_pkmn ; if no damage, skip Pokemon - -; heal all its damage - push de - ld e, a - ld d, $00 - call HealPlayAreaCardHP - -; loop all cards in deck and for all the Energy cards -; that are attached to this Play Area location Pokemon, -; place them in the Discard Pile. - ldh a, [hTempPlayAreaLocation_ff9d] - or CARD_LOCATION_PLAY_AREA - ld e, a - ld a, $00 - call GetTurnDuelistVariable -.loop_deck - ld a, [hl] - cp e - jr nz, .next_card_deck ; not attached to card, skip - ld a, l - call LoadCardDataToBuffer2_FromDeckIndex - ld a, [wLoadedCard2Type] - and TYPE_ENERGY - jr z, .next_card_deck ; not Energy, skip - ld a, l - call PutCardInDiscardPile -.next_card_deck - inc l - ld a, l - cp DECK_SIZE - jr c, .loop_deck - - pop de -.next_pkmn - inc e - dec d - jr nz, .loop_play_area - ret - -; return carry if non-Turn Duelist has full Bench -; or if they have no Basic Pokemon cards in Discard Pile. -PokemonFlute_BenchCheck: ; 2f659 (b:7659) - ld a, DUELVARS_NUMBER_OF_POKEMON_IN_PLAY_AREA - call GetNonTurnDuelistVariable - ldtx hl, NoSpaceOnTheBenchText - cp MAX_PLAY_AREA_POKEMON - ccf - ret c ; not enough space in Bench - ; check Discard Pile - call SwapTurn - call CreateBasicPokemonCardListFromDiscardPile - ldtx hl, ThereAreNoPokemonInDiscardPileText - call SwapTurn - ret - -PokemonFlute_PlayerSelection: ; 2f672 (b:7672) -; create Discard Pile list - call SwapTurn - call CreateBasicPokemonCardListFromDiscardPile - -; display selection screen and store Player's selection - bank1call Func_5591 - ldtx hl, ChoosePokemonToPlaceInPlayText - ldtx de, PlayerDiscardPileText - bank1call SetCardListHeaderText - bank1call DisplayCardList - call SwapTurn - ldh a, [hTempCardIndex_ff98] - ldh [hTemp_ffa0], a - ret - -PokemonFlute_PlaceInPlayAreaText: ; 2f68f (b:768f) -; place selected card in non-Turn Duelist's Bench - call SwapTurn - ldh a, [hTemp_ffa0] - call MoveDiscardPileCardToHand - call AddCardToHand - call PutHandPokemonCardInPlayArea - call SwapTurn - -; unless it was the Player who played the card, -; display the Pokemon card on screen. - call IsPlayerTurn - ret c - call SwapTurn - ldh a, [hTemp_ffa0] - ldtx hl, CardWasChosenText - bank1call DisplayCardDetailScreen - call SwapTurn - ret - -PokemonBreeder_HandPlayAreaCheck: ; 2f6b3 (b:76b3) - call CreatePlayableStage2PokemonCardListFromHand - jr c, .cannot_evolve - bank1call IsPrehistoricPowerActive - ret -.cannot_evolve - ldtx hl, ConditionsForEvolvingToStage2NotFulfilledText - scf - ret - -PokemonBreeder_PlayerSelection: ; 2f6c1 (b:76c1) -; create hand list of playable Stage2 cards - call CreatePlayableStage2PokemonCardListFromHand - bank1call Func_5591 - -; handle Player selection of Stage2 card - ldtx hl, PleaseSelectCardText - ldtx de, DuelistHandText - bank1call SetCardListHeaderText - bank1call DisplayCardList - ret c ; exit if B was pressed - - ldh a, [hTempCardIndex_ff98] - ldh [hTemp_ffa0], a - ldtx hl, ChooseBasicPokemonToEvolveText - call DrawWideTextBox_WaitForInput - -; handle Player selection of Basic card to evolve - bank1call HasAlivePokemonInPlayArea -.read_input - bank1call OpenPlayAreaScreenForSelection - ret c ; exit if B was pressed - ldh a, [hTempPlayAreaLocation_ff9d] - ldh [hTempPlayAreaLocation_ffa1], a - ld e, a - ldh a, [hTemp_ffa0] - ld d, a - call CheckIfCanEvolveInto_BasicToStage2 - jr c, .read_input ; loop back if cannot evolve this card - or a - ret - -PokemonBreeder_EvolveEffect: ; 2f6f4 (b:76f4) - ldh a, [hTempCardIndex_ff9f] - push af - ld hl, hTemp_ffa0 - ld a, [hli] - ldh [hTempCardIndex_ff98], a - ld a, [hl] ; hTempPlayAreaLocation_ffa1 - ldh [hTempPlayAreaLocation_ff9d], a - -; load the Basic Pokemon card name to RAM - add DUELVARS_ARENA_CARD - call GetTurnDuelistVariable - call LoadCardDataToBuffer1_FromDeckIndex - ld hl, wLoadedCard1Name - ld a, [hli] - ld h, [hl] - ld l, a - call LoadTxRam2 - -; evolve card and overwrite its stage as STAGE2_WITHOUT_STAGE1 - ldh a, [hTempCardIndex_ff98] - call EvolvePokemonCard - ld [hl], STAGE2_WITHOUT_STAGE1 - -; load Stage2 Pokemon card name to RAM - ldh a, [hTempCardIndex_ff98] - call LoadCardDataToBuffer1_FromDeckIndex - ld a, 18 - call CopyCardNameAndLevel - xor a - ld [hl], a ; $0 character - ld hl, wTxRam2_b - ld [hli], a - ld [hl], a - -; display Pokemon picture and play sfx, -; print the corresponding card names. - bank1call DrawLargePictureOfCard - ld a, $5e - call PlaySFX - ldtx hl, PokemonEvolvedIntoPokemonText - call DrawWideTextBox_WaitForInput - bank1call Func_161e - pop af - ldh [hTempCardIndex_ff9f], a - ret - -; creates list in wDuelTempList of all Stage2 Pokemon cards -; in the hand that can evolve a Basic Pokemon card in Play Area -; through use of Pokemon Breeder. -; returns carry if that list is empty. -CreatePlayableStage2PokemonCardListFromHand: ; 2f73e (b:773e) - call CreateHandCardList - ret c ; return if no hand cards - -; check if hand Stage2 Pokemon cards can be made -; to evolve a Basic Pokemon in the Play Area and, if so, -; add it to the wDuelTempList. - ld hl, wDuelTempList - ld e, l - ld d, h -.loop_hand - ld a, [hl] - cp $ff - jr z, .done - call .CheckIfCanEvolveAnyPlayAreaBasicCard - jr c, .next_hand_card - ld a, [hl] - ld [de], a - inc de -.next_hand_card - inc hl - jr .loop_hand - -.done - ld a, $ff ; terminating byte - ld [de], a - ld a, [wDuelTempList] - cp $ff - scf - ret z ; return carry if empty - ; not empty - or a - ret - -; return carry if Stage2 card in a cannot evolve any -; of the Basic Pokemon in Play Area through Pokemon Breeder. -.CheckIfCanEvolveAnyPlayAreaBasicCard - push de - ld d, a - call LoadCardDataToBuffer2_FromDeckIndex - ld a, [wLoadedCard2Type] - cp TYPE_ENERGY - jr nc, .set_carry ; skip if not Pokemon card - ld a, [wLoadedCard2Stage] - cp STAGE2 - jr nz, .set_carry ; skip if not Stage2 - -; check if can evolve any Play Area cards - push hl - push bc - ld a, DUELVARS_NUMBER_OF_POKEMON_IN_PLAY_AREA - call GetTurnDuelistVariable - ld c, a - ld e, PLAY_AREA_ARENA -.loop_play_area - push bc - push de - call CheckIfCanEvolveInto_BasicToStage2 - pop de - pop bc - jr nc, .done_play_area - inc e - dec c - jr nz, .loop_play_area -; set carry - scf -.done_play_area - pop bc - pop hl - pop de - ret -.set_carry - pop de - scf - ret - -; return carry if no cards in the Bench. -ScoopUp_BenchCheck: ; 2f795 (b:7795) - ld a, DUELVARS_NUMBER_OF_POKEMON_IN_PLAY_AREA - call GetTurnDuelistVariable - ldtx hl, EffectNoPokemonOnTheBenchText - cp 2 - ret - -ScoopUp_PlayerSelection: ; 2f7a0 (b:77a0) -; print text box - ldtx hl, ChoosePokemonToScoopUpText - call DrawWideTextBox_WaitForInput - -; handle Player selection - bank1call HasAlivePokemonInPlayArea - bank1call OpenPlayAreaScreenForSelection - ret c ; exit if B was pressed - - ldh [hTemp_ffa0], a - or a - ret nz ; if it wasn't the Active Pokemon, we are done - -; handle switching to a Pokemon in Bench and store location selected. - call EmptyScreen - ldtx hl, SelectPokemonToPlaceInTheArenaText - call DrawWideTextBox_WaitForInput - bank1call HasAlivePokemonInBench - bank1call OpenPlayAreaScreenForSelection - ldh [hTempPlayAreaLocation_ffa1], a - ret - -ScoopUp_ReturnToHandEffect: ; 2f7c3 (b:77c3) -; store chosen card location to Scoop Up - ldh a, [hTemp_ffa0] - or CARD_LOCATION_PLAY_AREA - ld e, a - -; find Basic Pokemon card that is in the selected Play Area location -; and add it to the hand, discarding all cards attached. - ld a, DUELVARS_CARD_LOCATIONS - call GetTurnDuelistVariable -.loop - ld a, [hl] - cp e - jr nz, .next_card ; skip if not in selected location - ld a, l - call LoadCardDataToBuffer2_FromDeckIndex - ld a, [wLoadedCard2Type] - cp TYPE_ENERGY - jr nc, .next_card ; skip if not Pokemon card - ld a, [wLoadedCard2Stage] - or a - jr nz, .next_card ; skip if not Basic stage -; found - ld a, l - ldh [hTempCardIndex_ff98], a - call AddCardToHand - ; optimization: break loop here, since - ; no two Basic Pokemon cards may occupy - ; the same Play Area location. -.next_card - inc l - ld a, l - cp DECK_SIZE - jr c, .loop - -; since the card has been moved to hand, -; MovePlayAreaCardToDiscardPile will take care -; of discarding every higher stage cards and other cards attached. - ldh a, [hTemp_ffa0] - ld e, a - call MovePlayAreaCardToDiscardPile - -; if the Pokemon was in the Arena, clear status - ldh a, [hTemp_ffa0] - or a - jr nz, .skip_clear_status - call ClearAllStatusConditions -.skip_clear_status - -; if card was not played by Player, show detail screen -; and print corresponding text. - call IsPlayerTurn - jr c, .shift_or_switch - ldtx hl, PokemonWasReturnedFromArenaToHandText - ldh a, [hTemp_ffa0] - or a - jr z, .display_detail_screen - ldtx hl, PokemonWasReturnedFromBenchToHandText -.display_detail_screen - ldh a, [hTempCardIndex_ff98] - bank1call DisplayCardDetailScreen - -.shift_or_switch -; if card was in Bench, simply shift Pokemon slots... - ldh a, [hTemp_ffa0] - or a - jr z, .switch - call ShiftAllPokemonToFirstPlayAreaSlots - ret - -.switch -; ...if Pokemon was in Arena, then switch it with the selected Bench card. - ldh a, [hTempPlayAreaLocation_ffa1] - ld d, a - ld e, PLAY_AREA_ARENA - call SwapPlayAreaPokemon - call ShiftAllPokemonToFirstPlayAreaSlots - ret - -; return carry if no other cards in hand, -; or if there are no Pokemon cards in hand. -PokemonTrader_HandDeckCheck: ; 2f826 (b:7826) - ld a, DUELVARS_NUMBER_OF_CARDS_IN_HAND - call GetTurnDuelistVariable - ldtx hl, ThereAreNoCardsInHandThatYouCanChangeText - cp 2 - ret c ; return if no other cards in hand - call CreatePokemonCardListFromHand - ldtx hl, ThereAreNoCardsInHandThatYouCanChangeText - ret - -PokemonTrader_PlayerHandSelection: ; 2f838 (b:7838) -; print text box - ldtx hl, ChooseCardFromYourHandToSwitchText - call DrawWideTextBox_WaitForInput - -; create list with all Pokemon cards in hand - call CreatePokemonCardListFromHand - bank1call Func_5591 - -; handle Player selection - ldtx hl, ChooseCardToSwitchText - ldtx de, DuelistHandText - bank1call SetCardListHeaderText - bank1call DisplayCardList - ldh [hTemp_ffa0], a - ret - -PokemonTrader_PlayerDeckSelection: ; 2f853 (b:7853) -; temporarily place chosen hand card in deck -; so it can be potentially chosen to be traded. - ldh a, [hTemp_ffa0] - call RemoveCardFromHand - call ReturnCardToDeck - -; display deck card list screen - ldtx hl, ChooseBasicOrEvolutionPokemonCardFromDeckText - call DrawWideTextBox_WaitForInput - call CreateDeckCardList - bank1call Func_5591 - ldtx hl, ChoosePokemonCardText - ldtx de, DuelistDeckText - bank1call SetCardListHeaderText - -; handle Player selection -.read_input - bank1call DisplayCardList - jr c, .read_input ; pressing B loops back to selection - call LoadCardDataToBuffer2_FromDeckIndex - ld a, [wLoadedCard2Type] - cp TYPE_ENERGY - jr nc, .read_input ; can't select non-Pokemon cards - -; a valid card was selected, store its card index and -; place the selected hand card back to the hand. - ldh a, [hTempCardIndex_ff98] - ldh [hTempPlayAreaLocation_ffa1], a - ldh a, [hTemp_ffa0] - call SearchCardInDeckAndAddToHand - call AddCardToHand - or a - ret - -PokemonTrader_TradeCardsEffect: ; 2f88d (b:788d) -; place hand card in deck - ldh a, [hTemp_ffa0] - call RemoveCardFromHand - call ReturnCardToDeck - -; place deck card in hand - ldh a, [hTempPlayAreaLocation_ffa1] - call SearchCardInDeckAndAddToHand - call AddCardToHand - -; display cards if the Pokemon Trader wasn't played by Player - call IsPlayerTurn - jr c, .done - ldh a, [hTemp_ffa0] - ldtx hl, PokemonWasReturnedToDeckText - bank1call DisplayCardDetailScreen - ldh a, [hTempPlayAreaLocation_ffa1] - ldtx hl, WasPlacedInTheHandText - bank1call DisplayCardDetailScreen -.done - call Func_2c0bd - ret - -; makes list in wDuelTempList with all Pokemon cards -; that are in Turn Duelist's hand. -; if list turns out empty, return carry. -CreatePokemonCardListFromHand: ; 2f8b6 (b:78b6) - ld a, DUELVARS_NUMBER_OF_CARDS_IN_HAND - call GetTurnDuelistVariable - ld c, a - ld l, DUELVARS_HAND - ld de, wDuelTempList -.loop - ld a, [hl] - call LoadCardDataToBuffer2_FromDeckIndex - ld a, [wLoadedCard2Type] - cp TYPE_ENERGY - jr nc, .next_hand_card - ld a, [hl] - ld [de], a - inc de -.next_hand_card - inc l - dec c - jr nz, .loop - ld a, $ff ; terminating byte - ld [de], a - ld a, [wDuelTempList] - cp $ff - jr z, .set_carry - or a - ret -.set_carry - scf - ret - -; return carry if no cards in deck -Pokedex_DeckCheck: ; 2f8e1 (b:78e1) - ld a, DUELVARS_NUMBER_OF_CARDS_NOT_IN_DECK - call GetTurnDuelistVariable - ldtx hl, NoCardsLeftInTheDeckText - cp DECK_SIZE - ccf - ret - -Pokedex_PlayerSelection: ; 2f8ed (b:78ed) -; print text box - ldtx hl, RearrangeThe5CardsAtTopOfDeckText - call DrawWideTextBox_WaitForInput - -; cap the number of cards to reorder up to -; number of cards left in the deck (maximum of 5) - ld a, DUELVARS_NUMBER_OF_CARDS_NOT_IN_DECK - call GetTurnDuelistVariable - ld b, a - ld a, DECK_SIZE - sub [hl] - ld c, 5 - cp c - jr nc, .no_cap - ld c, a -.no_cap - -; fill wDuelTempList with cards that are going to be sorted - ld a, c - inc a - ld [wNumberOfCardsToOrder], a - ld a, b - add DUELVARS_DECK_CARDS - ld l, a - ld de, wDuelTempList -.loop_cards_to_order - ld a, [hli] - ld [de], a - inc de - dec c - jr nz, .loop_cards_to_order - ld a, $ff ; terminating byte - ld [de], a - -.clear_list -; wDuelTempList + 10 will be filled with numbers from 1 -; to 5 (or whatever the maximum order card is). -; so that the first item in that list corresponds to the first card -; the second item corresponds to the second card, etc. -; and the number in the list corresponds to the ordering number. - call CountCardsInDuelTempList - ld b, a - ld a, 1 -; fill order list with zeroes - ldh [hCurSelectionItem], a - ld hl, wDuelTempList + 10 - xor a -.loop_init - ld [hli], a - dec b - jr nz, .loop_init - ld [hl], $ff ; terminating byte - -; display card list to order - bank1call InitAndDrawCardListScreenLayout - ldtx hl, ChooseTheOrderOfTheCardsText - ldtx de, DuelistDeckText - bank1call SetCardListHeaderText - bank1call Func_5735 - -.read_input - bank1call DisplayCardList - jr c, .undo ; if B is pressed, undo last order selection - -; a card was selected, check if it's already been selected - ldh a, [hCurMenuItem] - ld e, a - ld d, $00 - ld hl, wDuelTempList + 10 - add hl, de - ld a, [hl] - or a - jr nz, .read_input ; already has an ordering number - -; hasn't been ordered yet, apply to it current ordering number -; and increase it by 1. - ldh a, [hCurSelectionItem] - ld [hl], a - inc a - ldh [hCurSelectionItem], a - -; refresh screen - push af - bank1call Func_5744 - pop af - -; check if we're done ordering - ldh a, [hCurSelectionItem] - ld hl, wNumberOfCardsToOrder - cp [hl] - jr c, .read_input ; if still more cards to select, loop back up - -; we're done selecting cards - call EraseCursor - ldtx hl, IsThisOKText - call YesOrNoMenuWithText_LeftAligned - jr c, .clear_list ; "No" was selected, start over - ; selection was confirmed - -; now wDuelTempList + 10 will be overwritten with the -; card indices in order of selection. - ld hl, wDuelTempList + 10 - ld de, wDuelTempList - ld c, 0 -.loop_write_indices - ld a, [hli] - cp $ff - jr z, .done_write_indices - push hl - push bc - ld c, a - ld b, $00 - ld hl, hTempCardIndex_ff9f - add hl, bc - ld a, [de] - ld [hl], a - pop bc - pop hl - inc de - inc c - jr .loop_write_indices - -.done_write_indices - ld b, $00 - ld hl, hTempList - add hl, bc - ld [hl], $ff ; terminating byte - or a - ret - -.undo -; undo last selection and get previous order number - ld hl, hCurSelectionItem - ld a, [hl] - cp 1 - jr z, .read_input ; already at first input, nothing to undo - dec a - ld [hl], a - ld c, a - ld hl, wDuelTempList + 10 -.asm_2f99e - ld a, [hli] - cp c - jr nz, .asm_2f99e - dec hl - ld [hl], $00 ; overwrite order number with 0 - bank1call Func_5744 - jr .read_input - -Pokedex_OrderDeckCardsEffect: ; 2f9aa (b:79aa) -; place cards in order to the hand. - ld hl, hTempList - ld c, 0 -.loop_place_hand - ld a, [hli] - cp $ff - jr z, .place_top_deck - call SearchCardInDeckAndAddToHand - inc c - jr .loop_place_hand - -.place_top_deck -; go to last card in list and iterate in decreasing order -; placing each card in top of deck. - dec hl - dec hl -.loop_place_deck - ld a, [hld] - call ReturnCardToDeck - dec c - jr nz, .loop_place_deck - ret - -BillEffect: ; 2f9c4 (b:79c4) - ld a, 2 - bank1call DisplayDrawNCardsScreen - ld c, 2 -.loop_draw - call DrawCardFromDeck - jr c, .done - ldh [hTempCardIndex_ff98], a - call AddCardToHand - call IsPlayerTurn - jr nc, .skip_display_screen - push bc - bank1call DisplayPlayerDrawCardScreen - pop bc -.skip_display_screen - dec c - jr nz, .loop_draw -.done - ret - -LassEffect: ; 2f9e3 (b:79e3) -; first discard Lass card that was used - ldh a, [hTempCardIndex_ff9f] - call RemoveCardFromHand - call PutCardInDiscardPile - - ldtx hl, PleaseCheckTheOpponentsHandText - call DrawWideTextBox_WaitForInput - - call .DisplayLinkOrCPUHand - ; do for non-Turn Duelist - call SwapTurn - call .ShuffleDuelistHandTrainerCardsInDeck - call SwapTurn - ; do for Turn Duelist -; fallthrough - -.ShuffleDuelistHandTrainerCardsInDeck - call CreateHandCardList - call SortCardsInDuelTempListByID - xor a - ldh [hCurSelectionItem], a - ld hl, wDuelTempList - -; go through all cards in hand -; and any Trainer card is returned to deck. -.loop_hand - ld a, [hli] - ldh [hTempCardIndex_ff98], a - cp $ff - jr z, .done - call GetCardIDFromDeckIndex - call GetCardType - cp TYPE_TRAINER - jr nz, .loop_hand - ldh a, [hTempCardIndex_ff98] - call RemoveCardFromHand - call ReturnCardToDeck - push hl - ld hl, hCurSelectionItem - inc [hl] - pop hl - jr .loop_hand -.done -; show card list - ldh a, [hCurSelectionItem] - or a - call nz, Func_2c0bd ; only show list if there were any Trainer cards - ret - -.DisplayLinkOrCPUHand ; 2fa31 (b:7a31) - ld a, [wDuelType] - or a - jr z, .cpu_opp - -; link duel - ldh a, [hWhoseTurn] - push af - ld a, OPPONENT_TURN - ldh [hWhoseTurn], a - call .DisplayOppHand - pop af - ldh [hWhoseTurn], a - ret - -.cpu_opp - call SwapTurn - call .DisplayOppHand - call SwapTurn - ret - -.DisplayOppHand ; 2fa4f (b:7a4f) - call CreateHandCardList - jr c, .no_cards - bank1call InitAndDrawCardListScreenLayout - ldtx hl, ChooseTheCardYouWishToExamineText - ldtx de, DuelistHandText - bank1call SetCardListHeaderText - ld a, A_BUTTON | START - ld [wNoItemSelectionMenuKeys], a - bank1call DisplayCardList - ret -.no_cards - ldtx hl, DuelistHasNoCardsInHandText - call DrawWideTextBox_WaitForInput - ret - -; return carry if not enough cards in hand for effect -Maintenance_HandCheck: ; 2fa70 (b:7a70) - ld a, DUELVARS_NUMBER_OF_CARDS_IN_HAND - call GetTurnDuelistVariable - ldtx hl, NotEnoughCardsInHandText - cp 3 - ret - -Maintenance_PlayerSelection: ; 2fa7b (b:7a7b) - ldtx hl, Choose2HandCardsFromHandToReturnToDeckText - ldtx de, ChooseTheCardToPutBackText - call HandlePlayerSelection2HandCards - ret - -Maintenance_ReturnToDeckAndDrawEffect: ; 2fa85 (b:7a85) -; return both selected cards to the deck - ldh a, [hTempList] - call RemoveCardFromHand - call ReturnCardToDeck - ldh a, [hTempList + 1] - call RemoveCardFromHand - call ReturnCardToDeck - call Func_2c0bd - -; draw one card - ld a, 1 - bank1call DisplayDrawNCardsScreen - call DrawCardFromDeck - ldh [hTempCardIndex_ff98], a - call AddCardToHand - call IsPlayerTurn - ret nc - ; show card on screen if played by Player - bank1call DisplayPlayerDrawCardScreen - ret - -; return carry if no cards in deck -PokeBall_DeckCheck: ; 2faad (b:7aad) - ld a, DUELVARS_NUMBER_OF_CARDS_NOT_IN_DECK - call GetTurnDuelistVariable - ldtx hl, NoCardsLeftInTheDeckText - cp DECK_SIZE - ccf - ret - -PokeBall_PlayerSelection: ; 2fab9 (b:7ab9) - ld de, DUELVARS_NUMBER_OF_POKEMON_IN_PLAY_AREA - call Func_2c08a - ldh [hTempList], a ; store coin result - ret nc - -; create list of all Pokemon cards in deck to search for - call CreateDeckCardList - ldtx hl, ChooseBasicOrEvolutionPokemonCardFromDeckText - ldtx bc, EvolutionCardText - lb de, SEARCHEFFECT_POKEMON, 0 - call LookForCardsInDeck - jr c, .no_pkmn ; return if Player chose not to check deck - -; handle input - bank1call Func_5591 - ldtx hl, ChoosePokemonCardText - ldtx de, DuelistDeckText - bank1call SetCardListHeaderText -.read_input - bank1call DisplayCardList - jr c, .try_exit ; B was pressed, check if Player can cancel operation - ldh a, [hTempCardIndex_ff98] - call LoadCardDataToBuffer2_FromDeckIndex - ld a, [wLoadedCard2Type] - cp TYPE_ENERGY - jr nc, .play_sfx ; can't select non-Pokemon card - ldh a, [hTempCardIndex_ff98] - ldh [hTempList + 1], a - or a - ret - -.no_pkmn - ld a, $ff - ldh [hTempList + 1], a - or a - ret - -.play_sfx - call Func_3794 - jr .read_input - -.try_exit -; Player can only exit screen if there are no cards to choose - ld hl, wDuelTempList -.loop - ld a, [hli] - cp $ff - jr z, .no_pkmn - call LoadCardDataToBuffer2_FromDeckIndex - ld a, [wLoadedCard2Type] - cp TYPE_ENERGY - jr nc, .loop - jr .read_input - -PokeBall_AddToHandEffect: ; 2fb15 (b:7b15) - ldh a, [hTempList] - or a - ret z ; return if coin toss was tails - - ldh a, [hTempList + 1] - cp $ff - jr z, .done ; skip if no Pokemon was chosen - -; add Pokemon card to hand and show in screen if -; it wasn't the Player who played the Trainer card. - call SearchCardInDeckAndAddToHand - call AddCardToHand - call IsPlayerTurn - jr c, .done - ldh a, [hTempList + 1] - ldtx hl, WasPlacedInTheHandText - bank1call DisplayCardDetailScreen -.done - call Func_2c0bd - ret - -; return carry if no cards in the Discard Pile -Recycle_DiscardPileCheck: ; 2fb36 (b:7b36) - ld a, DUELVARS_NUMBER_OF_CARDS_IN_DISCARD_PILE - call GetTurnDuelistVariable - ldtx hl, ThereAreNoCardsInTheDiscardPileText - cp 1 - ret - -Recycle_PlayerSelection: ; 2fb41 (b:7b41) - ld de, DUELVARS_NUMBER_OF_POKEMON_IN_PLAY_AREA - call Func_2c08a - jr nc, .tails - - call CreateDiscardPileCardList - bank1call Func_5591 - ldtx hl, PleaseSelectCardText - ldtx de, PlayerDiscardPileText - bank1call SetCardListHeaderText -.read_input - bank1call DisplayCardList - jr c, .read_input ; can't cancel with B button - -; Discard Pile card was chosen - ldh a, [hTempCardIndex_ff98] - ldh [hTempList], a - ret - -.tails - ld a, $ff - ldh [hTempList], a - or a - ret - -Recycle_AddToHandEffect: ; 2fb68 (b:7b68) - ldh a, [hTempList] - cp $ff - ret z ; return if no card was selected - -; add card to hand and show in screen if -; it wasn't the Player who played the Trainer card. - call MoveDiscardPileCardToHand - call ReturnCardToDeck - call IsPlayerTurn - ret c - ldh a, [hTempList] - ldtx hl, CardWasChosenText - bank1call DisplayCardDetailScreen - ret - -; return carry if Bench is full or -; if no Basic Pokemon cards in Discard Pile. -Revive_BenchCheck: ; 2fb80 (b:7b80) - ld a, DUELVARS_NUMBER_OF_POKEMON_IN_PLAY_AREA - call GetTurnDuelistVariable - ldtx hl, NoSpaceOnTheBenchText - cp MAX_PLAY_AREA_POKEMON - ccf - ret c - call CreateBasicPokemonCardListFromDiscardPile - ldtx hl, ThereAreNoPokemonInDiscardPileText - ret - -Revive_PlayerSelection: ; 2fb93 (b:7b93) -; create Basic Pokemon card list from Discard Pile - ldtx hl, ChooseBasicPokemonToPlaceOnBenchText - call DrawWideTextBox_WaitForInput - call CreateBasicPokemonCardListFromDiscardPile - bank1call Func_5591 - -; display screen to select Pokemon - ldtx hl, PleaseSelectCardText - ldtx de, PlayerDiscardPileText - bank1call SetCardListHeaderText - bank1call DisplayCardList - -; store selection - ldh a, [hTempCardIndex_ff98] - ldh [hTemp_ffa0], a - ret - -Revive_PlaceInPlayAreaEffect: ; 2fbb0 (b:7bb0) -; place selected Pokemon in the Bench - ldh a, [hTemp_ffa0] - call MoveDiscardPileCardToHand - call AddCardToHand - call PutHandPokemonCardInPlayArea - -; set HP to half, rounded up - add DUELVARS_ARENA_CARD_HP - call GetTurnDuelistVariable - srl a - bit 0, a - jr z, .rounded - add 5 ; round up HP to nearest 10 -.rounded - ld [hl], a - call IsPlayerTurn - ret c ; done if Player played Revive - -; display card - ldh a, [hTemp_ffa0] - ldtx hl, PlacedOnTheBenchText - bank1call DisplayCardDetailScreen - ret - -; makes list in wDuelTempList with all Basic Pokemon cards -; that are in Turn Duelist's Discard Pile. -; if list turns out empty, return carry. -CreateBasicPokemonCardListFromDiscardPile: ; 2fbd6 (b:7bd6) -; gets hl to point at end of Discard Pile cards -; and iterates the cards in reverse order. - ld a, DUELVARS_NUMBER_OF_CARDS_IN_DISCARD_PILE - call GetTurnDuelistVariable - ld b, a - add DUELVARS_DECK_CARDS - ld l, a - ld de, wDuelTempList - inc b - jr .next_discard_pile_card - -.check_card - ld a, [hl] - call LoadCardDataToBuffer2_FromDeckIndex - ld a, [wLoadedCard2Type] - cp TYPE_ENERGY - jr nc, .next_discard_pile_card ; if not Pokemon card, skip - ld a, [wLoadedCard2Stage] - or a - jr nz, .next_discard_pile_card ; if not Basic stage, skip - -; write this card's index to wDuelTempList - ld a, [hl] - ld [de], a - inc de -.next_discard_pile_card - dec l - dec b - jr nz, .check_card - -; done with the loop. - ld a, $ff ; terminating byte - ld [de], a - ld a, [wDuelTempList] - cp $ff - jr z, .set_carry - or a - ret -.set_carry - scf - ret - -; return carry if Turn Duelist has no Evolution cards in Play Area -DevolutionSpray_PlayAreaEvolutionCheck: ; 2fc0b (b:7c0b) - ld a, DUELVARS_NUMBER_OF_POKEMON_IN_PLAY_AREA - call GetTurnDuelistVariable - ld c, a - ld l, DUELVARS_ARENA_CARD -.loop - ld a, [hli] - call LoadCardDataToBuffer2_FromDeckIndex - ld a, [wLoadedCard2Stage] - or a - ret nz ; found an Evolution card - dec c - jr nz, .loop - - ldtx hl, ThereAreNoStage1PokemonText - scf - ret - -DevolutionSpray_PlayerSelection: ; 2fc24 (b:7c24) -; display textbox - ldtx hl, ChooseEvolutionCardAndPressAButtonToDevolveText - call DrawWideTextBox_WaitForInput - -; have Player select an Evolution card in Play Area - ld a, 1 - ldh [hCurSelectionItem], a - bank1call HasAlivePokemonInPlayArea -.read_input - bank1call OpenPlayAreaScreenForSelection - ret c ; exit if B was pressed - bank1call GetCardOneStageBelow - jr c, .read_input ; can't select Basic cards - -; get pre-evolution card data - ldh a, [hTempPlayAreaLocation_ff9d] - add DUELVARS_ARENA_CARD_HP - call GetTurnDuelistVariable - push hl - push af - ldh a, [hTempPlayAreaLocation_ff9d] - add DUELVARS_ARENA_CARD_STAGE - ld l, a - ld a, [hl] - push hl - push af - ldh a, [hTempPlayAreaLocation_ff9d] - add DUELVARS_ARENA_CARD - ld l, a - ld a, [hl] - push hl - push af - jr .update_data - -.repeat_devolution -; show Play Area screen with static cursor -; so that the Player either presses A to do one more devolution -; or presses B to finish selection. - bank1call Func_6194 - jr c, .done_selection ; if B pressed, end selection instead - ; do one more devolution - bank1call GetCardOneStageBelow - -.update_data -; overwrite the card data to new devolved stats - ld a, d - call UpdateDevolvedCardHPAndStage - call GetNextPositionInTempList_TrainerEffects - ld [hl], e - ld a, d - call LoadCardDataToBuffer2_FromDeckIndex - ld a, [wLoadedCard2Stage] - or a - jr nz, .repeat_devolution ; can do one more devolution - -.done_selection - call GetNextPositionInTempList_TrainerEffects - ld [hl], $ff ; terminating byte - -; store this Play Area location in first item of temp list - ldh a, [hTempPlayAreaLocation_ff9d] - ldh [hTempList], a - -; update Play Area location display of this Pokemon - call EmptyScreen - ldh a, [hTempPlayAreaLocation_ff9d] - ld hl, wHUDEnergyAndHPBarsX - ld [hli], a - ld [hl], $00 - bank1call PrintPlayAreaCardInformationAndLocation - call EnableLCD - pop bc - pop hl - -; rewrite all duelvars from before the selection was done -; this is so that if "No" is selected in confirmation menu, -; then the Pokemon isn't devolved and remains unchanged. - ld [hl], b - ldtx hl, IsThisOKText - call YesOrNoMenuWithText - pop bc - pop hl - - ld [hl], b - pop bc - pop hl - - ld [hl], b - ret - -DevolutionSpray_DevolutionEffect: ; 2fc99 (b:7c99) -; first byte in list is Play Area location chosen - ld hl, hTempList - ld a, [hli] - ldh [hTempPlayAreaLocation_ff9d], a - add DUELVARS_ARENA_CARD - call GetTurnDuelistVariable - push hl - push af - -; loop through devolutions selected - ld hl, hTempList + 1 -.loop_devolutions - ld a, [hl] - cp $ff - jr z, .check_ko ; list is over - ; devolve card to its stage below - push hl - bank1call GetCardOneStageBelow - ld a, d - call UpdateDevolvedCardHPAndStage - call ResetDevolvedCardStatus - pop hl - ld a, [hli] - call PutCardInDiscardPile - jr .loop_devolutions - -.check_ko - pop af - ld e, a - pop hl - ld d, [hl] - call PrintDevolvedCardNameAndLevelText - ldh a, [hTempList] - call PrintPlayAreaCardKnockedOutIfNoHP - bank1call Func_6e49 - ret - -; returns carry if neither duelist has any energy cards attached -SuperEnergyRemoval_EnergyCheck: ; 2fcd0 (b:7cd0) - call CheckIfThereAreAnyEnergyCardsAttached - ldtx hl, NoEnergyCardsAttachedToPokemonInYourPlayAreaText - ret c - call SwapTurn - call CheckIfThereAreAnyEnergyCardsAttached - ldtx hl, NoEnergyCardsAttachedToPokemonInOppPlayAreaText - call SwapTurn - ret - -SuperEnergyRemoval_PlayerSelection: ; 2fce4 (b:7ce4) -; handle selection of Energy to discard in own Play Area - ldtx hl, ChoosePokemonInYourAreaThenPokemonInYourOppText - call DrawWideTextBox_WaitForInput - call HandlePokemonAndEnergySelectionScreen - ret c ; return if operation was cancelled - - ldtx hl, ChoosePokemonToRemoveEnergyFromText - call DrawWideTextBox_WaitForInput - - call SwapTurn - ld a, 3 - ldh [hCurSelectionItem], a -.select_opp_pkmn - bank1call HasAlivePokemonInPlayArea - bank1call OpenPlayAreaScreenForSelection - jr nc, .opp_pkmn_selected - ; B was pressed - call SwapTurn - ret ; return if operation was cancelled -.opp_pkmn_selected - ld e, a - call GetPlayAreaCardAttachedEnergies - ld a, [wTotalAttachedEnergies] - or a - jr nz, .has_energy ; has any energy cards attached? - ; no energy, loop back - ldtx hl, NoEnergyCardsText - call DrawWideTextBox_WaitForInput - jr .select_opp_pkmn - -.has_energy -; store this Pokemon's Play Area location - ldh a, [hTempPlayAreaLocation_ff9d] - ldh [hPlayAreaEffectTarget], a -; store which energy card to discard from it - bank1call CreateArenaOrBenchEnergyCardList - ldh a, [hTempPlayAreaLocation_ff9d] - bank1call DisplayEnergyDiscardScreen - ld a, 2 - ld [wEnergyDiscardMenuDenominator], a - -.loop_discard_energy_selection - bank1call HandleEnergyDiscardMenuInput - jr nc, .energy_selected - ; B pressed - ld a, 5 - call AskWhetherToQuitSelectingCards - jr nc, .done ; finish operation - ; player selected to continue selection - ld a, [wEnergyDiscardMenuNumerator] - push af - ldh a, [hTempPlayAreaLocation_ff9d] - bank1call DisplayEnergyDiscardScreen - ld a, 2 - ld [wEnergyDiscardMenuDenominator], a - pop af - ld [wEnergyDiscardMenuNumerator], a - jr .loop_discard_energy_selection - -.energy_selected -; store energy cards to discard from opponent - call GetNextPositionInTempList_TrainerEffects - ldh a, [hTempCardIndex_ff98] - ld [hl], a - call RemoveCardFromDuelTempList - ld hl, wEnergyDiscardMenuNumerator - inc [hl] - ldh a, [hCurSelectionItem] - cp 5 - jr nc, .done ; no more energy cards to select - ld a, [wDuelTempList] - cp $ff - jr z, .done ; no more energy cards to select - bank1call DisplayEnergyDiscardMenu - jr .loop_discard_energy_selection - -.done - call GetNextPositionInTempList_TrainerEffects - ld [hl], $ff - call SwapTurn - or a - ret - -SuperEnergyRemoval_DiscardEffect: ; 2fd73 (b:7d73) - ld hl, hTempList + 1 - -; discard energy card of own Play Area - ld a, [hli] - call PutCardInDiscardPile - -; iterate and discard opponent's energy cards - inc hl - call SwapTurn -.loop - ld a, [hli] - cp $ff - jr z, .done_discard - call PutCardInDiscardPile - jr .loop - -.done_discard -; if it's Player's turn, return... - call SwapTurn - call IsPlayerTurn - ret c -; ...otherwise show Play Area of affected Pokemon -; in opponent's Play Area - ldh a, [hTemp_ffa0] - call Func_2c10b -; in player's Play Area - xor a - ld [wDuelDisplayedScreen], a - call SwapTurn - ldh a, [hPlayAreaEffectTarget] - call Func_2c10b - call SwapTurn - ret - -; return carry if not enough cards in hand to -; discard for Super Energy Retrieval effect -; or if the Discard Pile has no basic Energy cards -SuperEnergyRetrieval_HandEnergyCheck: ; 2fda4 (b:7da4) - ld a, DUELVARS_NUMBER_OF_CARDS_IN_HAND - call GetTurnDuelistVariable - ldtx hl, NotEnoughCardsInHandText - cp 3 - ret c - call CreateEnergyCardListFromDiscardPile_OnlyBasic - ldtx hl, ThereAreNoBasicEnergyCardsInDiscardPileText - ret - -SuperEnergyRetrieval_PlayerHandSelection: ; 2fdb6 (b:7db6) - call HandlePlayerSelection2HandCardsToDiscard - ret - -SuperEnergyRetrieval_PlayerDiscardPileSelection: ; 2fdba (b:7dba) - ldtx hl, ChooseUpTo4FromDiscardPileText - call DrawWideTextBox_WaitForInput - call CreateEnergyCardListFromDiscardPile_OnlyBasic - -.loop_discard_pile_selection - bank1call InitAndDrawCardListScreenLayout - ldtx hl, PleaseSelectCardText - ldtx de, PlayerDiscardPileText - bank1call SetCardListHeaderText - bank1call DisplayCardList - jr nc, .store_selected_card - ; B pressed - ld a, 6 - call AskWhetherToQuitSelectingCards - jr c, .loop_discard_pile_selection ; player selected to continue - jr .done - -.store_selected_card - ldh a, [hTempCardIndex_ff98] - call GetTurnDuelistVariable - call GetNextPositionInTempList_TrainerEffects - ldh a, [hTempCardIndex_ff98] - ld [hl], a ; store selected energy card - call RemoveCardFromDuelTempList - jr c, .done - ; this shouldn't happen - ldh a, [hCurSelectionItem] - cp 6 - jr c, .loop_discard_pile_selection - -.done -; insert terminating byte - call GetNextPositionInTempList_TrainerEffects - ld [hl], $ff - or a - ret - -SuperEnergyRetrieval_DiscardAndAddToHandEffect: ; 2fdfa (b:7dfa) -; discard 2 cards selected from the hand - ld hl, hTemp_ffa0 - ld a, [hli] - call RemoveCardFromHand - call PutCardInDiscardPile - ld a, [hli] - call RemoveCardFromHand - call PutCardInDiscardPile - -; put selected cards in hand - ld de, wDuelTempList -.loop - ld a, [hli] - ld [de], a - inc de - cp $ff - jr z, .done - call MoveDiscardPileCardToHand - call AddCardToHand - jr .loop - -.done -; if Player played the card, exit - call IsPlayerTurn - ret c -; if not, show card list selected by Opponent - bank1call Func_4b38 - ret - -; outputs in hl the next position -; in hTempList to place a new card, -; and increments hCurSelectionItem. -; identical to GetNextPositionInTempList. -GetNextPositionInTempList_TrainerEffects: ; 2fe25 (b:7e25) - push de - ld hl, hCurSelectionItem - ld a, [hl] - inc [hl] - ld e, a - ld d, $00 - ld hl, hTempList - add hl, de - pop de - ret - -; handles screen for Player to select 2 cards from the hand to discard. -; first prints text informing Player to choose cards to discard -; then runs HandlePlayerSelection2HandCards routine. -HandlePlayerSelection2HandCardsToDiscard: ; 2fe34 (b:7e34) - ldtx hl, Choose2CardsFromHandToDiscardText - ldtx de, ChooseTheCardToDiscardText -; fallthrough - -; handles screen for Player to select 2 cards from the hand -; to activate some Trainer card effect. -; assumes Trainer card index being used is in [hTempCardIndex_ff9f]. -; stores selection of cards in hTempList. -; returns carry if Player cancels operation. -; input: -; hl = text to print in text box; -; de = text to print in screen header. -HandlePlayerSelection2HandCards: ; 2fe3a (b:7e3a) - push de - call DrawWideTextBox_WaitForInput - -; remove the Trainer card being used from list -; of cards to select from hand. - call CreateHandCardList - ldh a, [hTempCardIndex_ff9f] - call RemoveCardFromDuelTempList - - xor a - ldh [hCurSelectionItem], a - pop hl -.loop - push hl - bank1call Func_5591 - pop hl - bank1call SetCardListInfoBoxText - push hl - bank1call DisplayCardList - pop hl - jr c, .set_carry ; was B pressed? - push hl - call GetNextPositionInTempList_TrainerEffects - ldh a, [hTempCardIndex_ff98] - ld [hl], a - call RemoveCardFromDuelTempList - pop hl - ldh a, [hCurSelectionItem] - cp 2 - jr c, .loop ; is selection over? - or a - ret -.set_carry - scf - ret - -; return carry if non-turn duelist has no benched Pokemon -GustOfWind_BenchCheck: ; 2fe6e (b:7e6e) - ld a, DUELVARS_NUMBER_OF_POKEMON_IN_PLAY_AREA - call GetNonTurnDuelistVariable - ldtx hl, EffectNoPokemonOnTheBenchText - cp 2 - ret - -GustOfWind_PlayerSelection: ; 2fe79 (b:7e79) - ldtx hl, ChooseAPokemonToSwitchWithActivePokemonText - call DrawWideTextBox_WaitForInput - call SwapTurn - bank1call HasAlivePokemonInBench - bank1call OpenPlayAreaScreenForSelection - ldh a, [hTempPlayAreaLocation_ff9d] - ldh [hTemp_ffa0], a - call SwapTurn - ret - -GustOfWind_SwitchEffect: ; 2fe90 (b:7e90) -; play whirlwind animation - ld a, ATK_ANIM_GUST_OF_WIND - call Func_2fea9 - -; switch Arena card - call SwapTurn - ldh a, [hTemp_ffa0] - ld e, a - call SwapArenaWithBenchPokemon - call SwapTurn - call ClearDamageReductionSubstatus2 - xor a - ld [wDuelDisplayedScreen], a - ret - -; input: -; a = attack animation to play -Func_2fea9: ; 2fea9 (b:7ea9) - ld [wLoadedAttackAnimation], a - bank1call Func_7415 - ld bc, $0 - ldh a, [hWhoseTurn] - ld h, a - bank1call PlayAttackAnimation - bank1call WaitAttackAnimation - ret - -; heals amount of damage in register e for card in -; Play Area location in [hTempPlayAreaLocation_ff9d]. -; plays healing animation and prints text with card's name. -; input: -; e = amount of HP to heal -; [hTempPlayAreaLocation_ff9d] = Play Area location of card to heal -HealPlayAreaCardHP: ; 2febc (b:7ebc) - ld e, a - ld d, $00 - -; play heal animation - push de - bank1call Func_7415 - ld a, ATK_ANIM_HEALING_WIND_PLAY_AREA - ld [wLoadedAttackAnimation], a - ldh a, [hTempPlayAreaLocation_ff9d] - ld b, a - ld c, $01 - ldh a, [hWhoseTurn] - ld h, a - bank1call PlayAttackAnimation - bank1call WaitAttackAnimation - pop hl - -; print Pokemon card name and damage healed - push hl - call LoadTxRam3 - ld hl, $0000 - call LoadTxRam2 - ldh a, [hTempPlayAreaLocation_ff9d] - add DUELVARS_ARENA_CARD - call GetTurnDuelistVariable - call LoadCardDataToBuffer1_FromDeckIndex - ld a, 18 - call CopyCardNameAndLevel - ld [hl], $00 ; terminating character on end of the name - ldtx hl, PokemonHealedDamageText - call DrawWideTextBox_WaitForInput - pop de - -; heal the target Pokemon - ldh a, [hTempPlayAreaLocation_ff9d] - add DUELVARS_ARENA_CARD_HP - call GetTurnDuelistVariable - add e - ld [hl], a - ret diff --git a/src/engine/gfx/sprite_vblank.asm b/src/engine/gfx/sprite_vblank.asm new file mode 100644 index 0000000..5f099a1 --- /dev/null +++ b/src/engine/gfx/sprite_vblank.asm @@ -0,0 +1,39 @@ +; empties screen and replaces +; wVBlankFunctionTrampoline with HandleAllSpriteAnimations +SetSpriteAnimationsAsVBlankFunction: + call EmptyScreen + call Set_OBJ_8x8 + call Func_3ca4 + lb de, $38, $7f + call SetupText + ld hl, wVBlankFunctionTrampoline + 1 + ld de, wVBlankFunctionTrampolineBackup + call BackupVBlankFunctionTrampoline + di + ld [hl], LOW(HandleAllSpriteAnimations) + inc hl + ld [hl], HIGH(HandleAllSpriteAnimations) + ei + ret + +; sets backup VBlank function as wVBlankFunctionTrampoline +RestoreVBlankFunction: + ld hl, wVBlankFunctionTrampolineBackup + ld de, wVBlankFunctionTrampoline + 1 + call BackupVBlankFunctionTrampoline + call Func_3ca4 + bank1call ZeroObjectPositionsAndToggleOAMCopy + ret + +; copies 2 bytes from hl to de while interrupts are disabled +; used to load or store wVBlankFunctionTrampoline +; to wVBlankFunctionTrampolineBackup +BackupVBlankFunctionTrampoline: + di + ld a, [hli] + ld [de], a + inc de + ld a, [hld] + ld [de], a + ei + ret diff --git a/src/engine/sprite_vblank.asm b/src/engine/sprite_vblank.asm deleted file mode 100644 index 5f099a1..0000000 --- a/src/engine/sprite_vblank.asm +++ /dev/null @@ -1,39 +0,0 @@ -; empties screen and replaces -; wVBlankFunctionTrampoline with HandleAllSpriteAnimations -SetSpriteAnimationsAsVBlankFunction: - call EmptyScreen - call Set_OBJ_8x8 - call Func_3ca4 - lb de, $38, $7f - call SetupText - ld hl, wVBlankFunctionTrampoline + 1 - ld de, wVBlankFunctionTrampolineBackup - call BackupVBlankFunctionTrampoline - di - ld [hl], LOW(HandleAllSpriteAnimations) - inc hl - ld [hl], HIGH(HandleAllSpriteAnimations) - ei - ret - -; sets backup VBlank function as wVBlankFunctionTrampoline -RestoreVBlankFunction: - ld hl, wVBlankFunctionTrampolineBackup - ld de, wVBlankFunctionTrampoline + 1 - call BackupVBlankFunctionTrampoline - call Func_3ca4 - bank1call ZeroObjectPositionsAndToggleOAMCopy - ret - -; copies 2 bytes from hl to de while interrupts are disabled -; used to load or store wVBlankFunctionTrampoline -; to wVBlankFunctionTrampolineBackup -BackupVBlankFunctionTrampoline: - di - ld a, [hli] - ld [de], a - inc de - ld a, [hld] - ld [de], a - ei - ret diff --git a/src/home/ai.asm b/src/home/ai.asm index 270168f..b3834c1 100644 --- a/src/home/ai.asm +++ b/src/home/ai.asm @@ -70,7 +70,7 @@ AIDoAction_TakePrize: jr AIDoAction ; this line is not needed ; calls the appropriate AI routine to handle action, -; depending on the deck ID (see engine/ai/deck_ai.asm) +; depending on the deck ID (see engine/duel/ai/deck_ai.asm) ; input: ; - a = AIACTION_* constant AIDoAction: diff --git a/src/main.asm b/src/main.asm index e1d4c34..19da979 100644 --- a/src/main.asm +++ b/src/main.asm @@ -29,7 +29,7 @@ INCLUDE "engine/bank04.asm" SECTION "AI Logic 1", ROMX INCLUDE "data/deck_ai_pointers.asm" -INCLUDE "engine/ai/core.asm" +INCLUDE "engine/duel/ai/core.asm" SECTION "Menus 2", ROMX INCLUDE "engine/copy_card_name.asm" @@ -47,7 +47,7 @@ SECTION "IR Communications Core", ROMX INCLUDE "engine/link/ir_core.asm" SECTION "Sprite Animations VBlank", ROMX -INCLUDE "engine/sprite_vblank.asm" +INCLUDE "engine/gfx/sprite_vblank.asm" SECTION "Starter Deck", ROMX INCLUDE "engine/starter_deck.asm" @@ -100,12 +100,12 @@ SECTION "Booster Packs", ROMX INCLUDE "engine/booster_packs.asm" SECTION "AI Logic 2", ROMX -INCLUDE "engine/ai/trainer_cards.asm" -INCLUDE "engine/ai/pkmn_powers.asm" -INCLUDE "engine/ai/common.asm" +INCLUDE "engine/duel/ai/trainer_cards.asm" +INCLUDE "engine/duel/ai/pkmn_powers.asm" +INCLUDE "engine/duel/ai/common.asm" SECTION "Effect Functions", ROMX -INCLUDE "engine/effect_functions.asm" +INCLUDE "engine/duel/effect_functions.asm" SECTION "Decks", ROMX INCLUDE "data/decks.asm" -- cgit v1.2.3 From 50ac342dc60b4b1b4e61d70780c39caf9db97589 Mon Sep 17 00:00:00 2001 From: ElectroDeoxys Date: Wed, 29 Sep 2021 09:08:13 +0100 Subject: Fix Bugs and Glitches paths --- bugs_and_glitches.md | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/bugs_and_glitches.md b/bugs_and_glitches.md index a2d3ee8..59dcbc9 100644 --- a/bugs_and_glitches.md +++ b/bugs_and_glitches.md @@ -30,7 +30,7 @@ Fixes are written in the `diff` format. When the AI is scoring each Play Area Pokémon card to attach an Energy card, it first checks whether it's the Arena card and, if so, checks whether the attack can KO the Defending Pokémon. If it's true, then 20 is added to the score. Then it does the same Arena card check to increase the score further. The intention is probably to score the card with 20 if it can KO the opponent regardless of Play Area position, and additionally if it's the Arena card it should score 10 more points. -**Fix:** Edit `DetermineAIScoreOfAttackEnergyRequirement` in [src/engine/bank05.asm](https://github.com/pret/poketcg/blob/master/src/engine/bank05.asm): +**Fix:** Edit `DetermineAIScoreOfAttackEnergyRequirement` in [src/engine/duel/ai/energy.asm](https://github.com/pret/poketcg/blob/master/src/engine/duel/ai/energy.asm): ```diff DetermineAIScoreOfAttackEnergyRequirement: ; 16695 (5:6695) ... @@ -80,7 +80,7 @@ Each deck AI lists some card IDs that are not supposed to be placed as Prize car However, the routine to iterate these lists and look for these cards is buggy, which results in the game ignoring it completely. -**Fix:** Edit `SetUpBossStartingHandAndDeck` in [src/engine/bank05.asm](https://github.com/pret/poketcg/blob/master/src/engine/bank05.asm): +**Fix:** Edit `SetUpBossStartingHandAndDeck` in [src/engine/duel/ai/boss_deck_set_up.asm](https://github.com/pret/poketcg/blob/master/src/engine/duel/ai/boss_deck_set_up.asm): ```diff SetUpBossStartingHandAndDeck: ; 172af (5:72af) ... @@ -134,7 +134,7 @@ Each deck AI lists some Pokémon card IDs that have an associated score for retr However, the game never actually stores the pointer to these lists (a notable exception being the Legendary Moltres deck), so the AI cannot access these score modifiers. -**Fix:** Edit all applicable decks in `src/engine/ai/decks/`, uncommenting the following line: +**Fix:** Edit all applicable decks in `src/engine/duel/ai/decks/`, uncommenting the following line: ```diff .store_list_pointers store_list_pointer wAICardListAvoidPrize, .list_prize @@ -151,7 +151,7 @@ However, the game never actually stores the pointer to these lists (a notable ex When the AI is checking whether to play Professor Oak or not, it does a hand check to see if there are any Basic/Evolved Pokémon cards. One of these checks is supposed to add to the score if there are Basic Pokémon in hand, but as it is coded, it will never execute the score addition. -**Fix:** Edit `AIDecide_ProfessorOak` in [src/engine/bank08.asm](https://github.com/pret/poketcg/blob/master/src/engine/bank08.asm): +**Fix:** Edit `AIDecide_ProfessorOak` in [src/engine/duel/ai/trainer_cards.asm](https://github.com/pret/poketcg/blob/master/src/engine/duel/ai/trainer_cards.asm): ```diff AIDecide_ProfessorOak: ; 20cc1 (8:4cc1) ... @@ -180,7 +180,7 @@ AIDecide_ProfessorOak: ; 20cc1 (8:4cc1) The AI's decision to play Energy Search has two special cases: one for the Heated Battle deck and the other for the Wonders of Science deck. The former calls a subroutine to check only for Fire and Lightning energy cards in the deck, and the latter only checks for... Fire and Lightning energy cards. In a deck filled only with Grass and Psychic types. Needless is to say, poor Rick never finds any of those energy cards in the deck, so the Energy Search card is left forever unplayed. There's an unreferenced subroutine that looks for Grass energy that is supposed to be used instead. -**Fix:** Edit `AIDecide_EnergySearch` in [src/engine/bank08.asm](https://github.com/pret/poketcg/blob/master/src/engine/bank08.asm): +**Fix:** Edit `AIDecide_EnergySearch` in [src/engine/duel/ai/trainer_cards.asm](https://github.com/pret/poketcg/blob/master/src/engine/duel/ai/trainer_cards.asm): ```diff AIDecide_EnergySearch: ; 211aa (8:51aa) ... @@ -203,7 +203,7 @@ AIDecide_EnergySearch: ; 211aa (8:51aa) Seems Rick can't catch a break. When deciding which cards to prioritize in the Pokédex card effect, the AI checks if it's playing the Wonders of Science deck, then completely disregards the result and jumps unconditionally. Thus, Rick uses the generic algorithm for sorting the deck cards. -**Fix:** Edit `AIDecide_Pokedex` in [src/engine/bank08.asm](https://github.com/pret/poketcg/blob/master/src/engine/bank08.asm): +**Fix:** Edit `AIDecide_Pokedex` in [src/engine/duel/ai/trainer_cards.asm](https://github.com/pret/poketcg/blob/master/src/engine/duel/ai/trainer_cards.asm): ```diff AIDecide_Pokedex: ; 212dc (8:52dc) ... @@ -231,7 +231,7 @@ AIDecide_Pokedex: ; 212dc (8:52dc) Because of an error in the AI logic, Chris never considers using Revive on a Kangaskhan card in the Discard Pile, even though it is listed as one of the cards for the AI to check. -**Fix:** Edit `AIDecide_Revive` in [src/engine/bank08.asm](https://github.com/pret/poketcg/blob/master/src/engine/bank08.asm): +**Fix:** Edit `AIDecide_Revive` in [src/engine/duel/ai/trainer_cards.asm](https://github.com/pret/poketcg/blob/master/src/engine/duel/ai/trainer_cards.asm): ```diff AIDecide_Revive: ; 218a9 (8:58a9) ... @@ -260,7 +260,7 @@ AIDecide_Revive: ; 218a9 (8:58a9) A missing line in AI logic might result in strange behavior when executing the effect of Pokémon Trader for Power Generator deck. -**Fix:** Edit `AIDecide_PokemonTrader_PowerGenerator` in [src/engine/bank08.asm](https://github.com/pret/poketcg/blob/master/src/engine/bank08.asm): +**Fix:** Edit `AIDecide_PokemonTrader_PowerGenerator` in [src/engine/duel/ai/trainer_cards.asm](https://github.com/pret/poketcg/blob/master/src/engine/duel/ai/trainer_cards.asm): ```diff AIDecide_PokemonTrader_PowerGenerator: ; 2200b (8:600b) ... @@ -283,7 +283,7 @@ AIDecide_PokemonTrader_PowerGenerator: ; 2200b (8:600b) ## AI never uses Energy Trans in order to retreat Arena card -There is a mistake in the AI retreat logic, in [src/engine/ai/decks/general.asm](https://github.com/pret/poketcg/blob/master/src/engine/ai/decks/general.asm): +There is a mistake in the AI retreat logic, in [src/engine/duel/ai/decks/general.asm](https://github.com/pret/poketcg/blob/master/src/engine/duel/ai/decks/general.asm): ``` .used_switch ; if AI used switch, unset its AI flag @@ -308,7 +308,7 @@ There is a mistake in the AI retreat logic, in [src/engine/ai/decks/general.asm] ## Sam's practice deck does wrong card ID check -There is a mistake in the AI logic for deciding which Pokémon for Sam to switch to, in [src/engine/ai/decks/sams_practice.asm](https://github.com/pret/poketcg/blob/master/src/engine/ai/decks/sams_practice.asm): +There is a mistake in the AI logic for deciding which Pokémon for Sam to switch to, in [src/engine/duel/ai/decks/sams_practice.asm](https://github.com/pret/poketcg/blob/master/src/engine/duel/ai/decks/sams_practice.asm): ``` ; this is a bug, it's attempting to compare a card ID with a deck index. ; the intention was to change the card to switch to depending on whether @@ -328,7 +328,7 @@ There is a mistake in the AI logic for deciding which Pokémon for Sam to switch inc a ; PLAY_AREA_BENCH_2 ``` -**Fix:** Edit `AIDecide_PokemonTrader_PowerGenerator` in [src/engine/bank08.asm](https://github.com/pret/poketcg/blob/master/src/engine/bank08.asm): +**Fix:** Edit `AIDecide_PokemonTrader_PowerGenerator` in [src/engine/duel/ai/trainer_cards.asm](https://github.com/pret/poketcg/blob/master/src/engine/duel/ai/trainer_cards.asm): ```diff AIPerformScriptedTurn: ; 1483a (5:483a) ... @@ -360,7 +360,7 @@ AIPerformScriptedTurn: ; 1483a (5:483a) ## AI does not account for Mysterious Fossil or Clefairy Doll when using Shift Pkmn Power -**Fix:** Edit `HandleAIShift` in [src/engine/bank08.asm](https://github.com/pret/poketcg/blob/master/src/engine/bank08.asm): +**Fix:** Edit `HandleAIShift` in [src/engine/duel/ai/pkmn_powers.asm](https://github.com/pret/poketcg/blob/master/src/engine/duel/ai/pkmn_powers.asm): ```diff HandleAIShift: ; 22476 (8:6476) ... -- cgit v1.2.3 From 0d133430e5a6258b1b2496378243f29c62723b32 Mon Sep 17 00:00:00 2001 From: ElectroDeoxys Date: Wed, 29 Sep 2021 09:08:48 +0100 Subject: Fix typo in unused_save_validation.asm --- src/engine/unused_save_validation.asm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/engine/unused_save_validation.asm b/src/engine/unused_save_validation.asm index 9a30d38..f26bbcb 100644 --- a/src/engine/unused_save_validation.asm +++ b/src/engine/unused_save_validation.asm @@ -2,7 +2,7 @@ ; sUnusedSaveDataValidationByte would be used to store some validation byte ; and xor'd with $250 bytes in SRAM starting from sCardCollection ; if the result wasn't 0, then it would mean there was -; some save corrpution and an error message would pop up +; some save corruption and an error message would pop up StubbedUnusedSaveDataValidation: ret -- cgit v1.2.3 From 63a687d613e79cc2f8dd27198e45f9071a350ce6 Mon Sep 17 00:00:00 2001 From: ElectroDeoxys Date: Sun, 10 Oct 2021 21:42:24 +0100 Subject: WD -> Window --- src/engine/bank04.asm | 2 +- src/engine/bank07.asm | 14 +++++++------- src/engine/credits.asm | 4 ++-- src/engine/input_name.asm | 2 +- src/engine/sequences/credits_sequence_commands.asm | 4 ++-- src/home/lcd.asm | 4 ++-- 6 files changed, 15 insertions(+), 15 deletions(-) diff --git a/src/engine/bank04.asm b/src/engine/bank04.asm index 940df79..5fb216f 100644 --- a/src/engine/bank04.asm +++ b/src/engine/bank04.asm @@ -5337,7 +5337,7 @@ Func_12871: ; 12871 (4:6871) ldh [hSCY], a ldh [hWX], a ldh [hWY], a - call SetWDOff + call SetWindowOff ret Func_1288c: ; 1288c (4:688c) diff --git a/src/engine/bank07.asm b/src/engine/bank07.asm index 93ef008..cf9da13 100644 --- a/src/engine/bank07.asm +++ b/src/engine/bank07.asm @@ -1,5 +1,5 @@ -JumpSetWDOff: - jp SetWDOff +JumpSetWindowOff: + jp SetWindowOff ; debug function ; prints player's coordinates by pressing B @@ -7,18 +7,18 @@ JumpSetWDOff: Func_1c003: ; unreferenced ld a, [wCurMap] or a - jr z, JumpSetWDOff + jr z, JumpSetWindowOff ld a, [wOverworldMode] cp OWMODE_START_SCRIPT - jr nc, JumpSetWDOff + jr nc, JumpSetWindowOff ldh a, [hKeysHeld] ld b, a and A_BUTTON | B_BUTTON cp b - jr nz, JumpSetWDOff + jr nz, JumpSetWindowOff and B_BUTTON - jr z, JumpSetWDOff + jr z, JumpSetWindowOff ld bc, $20 ld a, [wPlayerXCoord] @@ -46,7 +46,7 @@ Func_1c003: ; unreferenced ld a, $68 ldh [hWY], a .set_wd_on - call Set_WD_on + call SetWindowOn ret Func_1c056: diff --git a/src/engine/credits.asm b/src/engine/credits.asm index d2da5d8..8ead323 100644 --- a/src/engine/credits.asm +++ b/src/engine/credits.asm @@ -23,7 +23,7 @@ PlayCreditsSequence: call PlaySong farcall Func_10ab4 call Func_3ca4 - call SetWDOff + call SetWindowOff call Func_1d758 call EnableLCD call DoFrameIfLCDEnabled @@ -52,7 +52,7 @@ Func_1d705: ld [wd648], a ld [wd64a], a call Func_1d765 - call Set_WD_on + call SetWindowOn call .Func_1d73a ; can be fallthrough ret diff --git a/src/engine/input_name.asm b/src/engine/input_name.asm index c9d222e..edcac4c 100644 --- a/src/engine/input_name.asm +++ b/src/engine/input_name.asm @@ -2,7 +2,7 @@ WhatIsYourNameData: textitem 1, 1, WhatIsYourNameText db $ff ; [Deck1Data ~ Deck4Data] -; These are directed from around (2:4f05), +; These are directed from InputCurDeckName, ; without any bank description. ; That is, the developers hard-coded it. -_-;; Deck1Data: diff --git a/src/engine/sequences/credits_sequence_commands.asm b/src/engine/sequences/credits_sequence_commands.asm index 263ecff..d80df8b 100644 --- a/src/engine/sequences/credits_sequence_commands.asm +++ b/src/engine/sequences/credits_sequence_commands.asm @@ -300,7 +300,7 @@ CreditsSequenceCmd_DisableLCD: ; 1d9db (7:59db) CreditsSequenceCmd_FadeIn: ; 1d9e1 (7:59e1) call DisableLCD - call Set_WD_on + call SetWindowOn farcall Func_10af9 jp AdvanceCreditsSequenceCmdPtrBy2 @@ -310,7 +310,7 @@ CreditsSequenceCmd_FadeOut: ; 1d9ee (7:59ee) call EnableLCD call DoFrameIfLCDEnabled call DisableLCD - call SetWDOff + call SetWindowOff jp AdvanceCreditsSequenceCmdPtrBy2 CreditsSequenceCmd_DrawRectangle: ; 1da04 (7:5a04) diff --git a/src/home/lcd.asm b/src/home/lcd.asm index 5d82c72..415aea0 100644 --- a/src/home/lcd.asm +++ b/src/home/lcd.asm @@ -69,14 +69,14 @@ Set_OBJ_8x16: ret ; set Window Display on -Set_WD_on: +SetWindowOn: ld a, [wLCDC] or LCDC_WINON ld [wLCDC], a ret ; set Window Display off -SetWDOff: +SetWindowOff: ld a, [wLCDC] and LCDC_WINOFF ld [wLCDC], a -- cgit v1.2.3