diff options
author | ElectroDeoxys <ElectroDeoxys@gmail.com> | 2021-06-07 16:33:15 +0100 |
---|---|---|
committer | ElectroDeoxys <ElectroDeoxys@gmail.com> | 2021-06-07 16:33:15 +0100 |
commit | 09239933ed98358ec3aff77d5eed90e5daba20a1 (patch) | |
tree | 6e2460c02c10248ab88de046cde8c730665ed60f | |
parent | 36ef80903a5cb821ddf4660276709144df67f8b6 (diff) |
Split bank 8
-rw-r--r-- | bugs_and_glitches.md | 6 | ||||
-rw-r--r-- | src/data/ai_trainer_card_logic.asm | 47 | ||||
-rw-r--r-- | src/engine/ai/common.asm | 970 | ||||
-rw-r--r-- | src/engine/ai/deck_ai.asm (renamed from src/engine/deck_ai/deck_ai.asm) | 38 | ||||
-rw-r--r-- | src/engine/ai/decks/fire_charge.asm (renamed from src/engine/deck_ai/decks/fire_charge.asm) | 0 | ||||
-rw-r--r-- | src/engine/ai/decks/first_strike.asm (renamed from src/engine/deck_ai/decks/first_strike.asm) | 0 | ||||
-rw-r--r-- | src/engine/ai/decks/flower_power.asm (renamed from src/engine/deck_ai/decks/flower_power.asm) | 0 | ||||
-rw-r--r-- | src/engine/ai/decks/general.asm (renamed from src/engine/deck_ai/decks/general.asm) | 0 | ||||
-rw-r--r-- | src/engine/ai/decks/general_no_retreat.asm (renamed from src/engine/deck_ai/decks/general_no_retreat.asm) | 0 | ||||
-rw-r--r-- | src/engine/ai/decks/go_go_rain_dance.asm (renamed from src/engine/deck_ai/decks/go_go_rain_dance.asm) | 0 | ||||
-rw-r--r-- | src/engine/ai/decks/im_ronald.asm (renamed from src/engine/deck_ai/decks/im_ronald.asm) | 0 | ||||
-rw-r--r-- | src/engine/ai/decks/invincible_ronald.asm (renamed from src/engine/deck_ai/decks/invincible_ronald.asm) | 0 | ||||
-rw-r--r-- | src/engine/ai/decks/legendary_articuno.asm (renamed from src/engine/deck_ai/decks/legendary_articuno.asm) | 0 | ||||
-rw-r--r-- | src/engine/ai/decks/legendary_dragonite.asm (renamed from src/engine/deck_ai/decks/legendary_dragonite.asm) | 0 | ||||
-rw-r--r-- | src/engine/ai/decks/legendary_moltres.asm (renamed from src/engine/deck_ai/decks/legendary_moltres.asm) | 0 | ||||
-rw-r--r-- | src/engine/ai/decks/legendary_ronald.asm (renamed from src/engine/deck_ai/decks/legendary_ronald.asm) | 0 | ||||
-rw-r--r-- | src/engine/ai/decks/legendary_zapdos.asm (renamed from src/engine/deck_ai/decks/legendary_zapdos.asm) | 0 | ||||
-rw-r--r-- | src/engine/ai/decks/powerful_ronald.asm (renamed from src/engine/deck_ai/decks/powerful_ronald.asm) | 0 | ||||
-rw-r--r-- | src/engine/ai/decks/rock_crusher.asm (renamed from src/engine/deck_ai/decks/rock_crusher.asm) | 0 | ||||
-rw-r--r-- | src/engine/ai/decks/sams_practice.asm (renamed from src/engine/deck_ai/decks/sams_practice.asm) | 0 | ||||
-rw-r--r-- | src/engine/ai/decks/strange_psyshock.asm (renamed from src/engine/deck_ai/decks/strange_psyshock.asm) | 0 | ||||
-rw-r--r-- | src/engine/ai/decks/wonders_of_science.asm (renamed from src/engine/deck_ai/decks/wonders_of_science.asm) | 0 | ||||
-rw-r--r-- | src/engine/ai/decks/zapping_selfdestruct.asm (renamed from src/engine/deck_ai/decks/zapping_selfdestruct.asm) | 0 | ||||
-rw-r--r-- | src/engine/ai/pkmn_powers.asm | 1228 | ||||
-rw-r--r-- | src/engine/ai/trainer_cards.asm (renamed from src/engine/bank08.asm) | 2260 | ||||
-rw-r--r-- | src/engine/bank05.asm | 2 | ||||
-rw-r--r-- | src/engine/home.asm | 2 | ||||
-rw-r--r-- | src/layout.link | 2 | ||||
-rw-r--r-- | src/main.asm | 6 |
29 files changed, 2281 insertions, 2280 deletions
diff --git a/bugs_and_glitches.md b/bugs_and_glitches.md index 96e3c40..f9704f8 100644 --- a/bugs_and_glitches.md +++ b/bugs_and_glitches.md @@ -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 expection being the Legendary Moltres deck), so the AI cannot access these score modifiers. -**Fix:** Edit all applicable decks in `src/engine/deck_ai/decks/`, uncommenting the following line: +**Fix:** Edit all applicable decks in `src/engine/ai/decks/`, uncommenting the following line: ```diff .store_list_pointers store_list_pointer wAICardListAvoidPrize, .list_prize @@ -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/deck_ai/decks/general.asm](https://github.com/pret/poketcg/blob/master/src/engine/deck_ai/decks/general.asm): +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): ``` .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/deck_ai/decks/general ## 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/deck_ai/decks/sams_practice.asm](https://github.com/pret/poketcg/blob/master/src/engine/deck_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/ai/decks/sams_practice.asm](https://github.com/pret/poketcg/blob/master/src/engine/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 diff --git a/src/data/ai_trainer_card_logic.asm b/src/data/ai_trainer_card_logic.asm new file mode 100644 index 0000000..57bf90f --- /dev/null +++ b/src/data/ai_trainer_card_logic.asm @@ -0,0 +1,47 @@ +ai_trainer_card_logic: MACRO + db \1 ; AI_TRAINER_CARD_PHASE_* constant + db \2 ; ID of trainer card + dw \3 ; function for AI decision to play card + dw \4 ; function for AI playing the card +ENDM + +AITrainerCardLogic: ; 20000 (8:4000) + ai_trainer_card_logic AI_TRAINER_CARD_PHASE_07, POTION, AIDecide_Potion1, AIPlay_Potion + ai_trainer_card_logic AI_TRAINER_CARD_PHASE_10, POTION, AIDecide_Potion2, AIPlay_Potion + ai_trainer_card_logic AI_TRAINER_CARD_PHASE_08, SUPER_POTION, AIDecide_SuperPotion1, AIPlay_SuperPotion + ai_trainer_card_logic AI_TRAINER_CARD_PHASE_11, SUPER_POTION, AIDecide_SuperPotion2, AIPlay_SuperPotion + ai_trainer_card_logic AI_TRAINER_CARD_PHASE_13, DEFENDER, AIDecide_Defender1, AIPlay_Defender + ai_trainer_card_logic AI_TRAINER_CARD_PHASE_14, DEFENDER, AIDecide_Defender2, AIPlay_Defender + ai_trainer_card_logic AI_TRAINER_CARD_PHASE_13, PLUSPOWER, AIDecide_Pluspower1, AIPlay_Pluspower + ai_trainer_card_logic AI_TRAINER_CARD_PHASE_14, PLUSPOWER, AIDecide_Pluspower2, AIPlay_Pluspower + ai_trainer_card_logic AI_TRAINER_CARD_PHASE_09, SWITCH, AIDecide_Switch, AIPlay_Switch + ai_trainer_card_logic AI_TRAINER_CARD_PHASE_07, GUST_OF_WIND, AIDecide_GustOfWind, AIPlay_GustOfWind + ai_trainer_card_logic AI_TRAINER_CARD_PHASE_10, GUST_OF_WIND, AIDecide_GustOfWind, AIPlay_GustOfWind + ai_trainer_card_logic AI_TRAINER_CARD_PHASE_04, BILL, AIDecide_Bill, AIPlay_Bill + ai_trainer_card_logic AI_TRAINER_CARD_PHASE_05, ENERGY_REMOVAL, AIDecide_EnergyRemoval, AIPlay_EnergyRemoval + ai_trainer_card_logic AI_TRAINER_CARD_PHASE_05, SUPER_ENERGY_REMOVAL, AIDecide_SuperEnergyRemoval, AIPlay_SuperEnergyRemoval + ai_trainer_card_logic AI_TRAINER_CARD_PHASE_07, POKEMON_BREEDER, AIDecide_PokemonBreeder, AIPlay_PokemonBreeder + ai_trainer_card_logic AI_TRAINER_CARD_PHASE_15, PROFESSOR_OAK, AIDecide_ProfessorOak, AIPlay_ProfessorOak + ai_trainer_card_logic AI_TRAINER_CARD_PHASE_10, ENERGY_RETRIEVAL, AIDecide_EnergyRetrieval, AIPlay_EnergyRetrieval + ai_trainer_card_logic AI_TRAINER_CARD_PHASE_11, SUPER_ENERGY_RETRIEVAL, AIDecide_SuperEnergyRetrieval, AIPlay_SuperEnergyRetrieval + ai_trainer_card_logic AI_TRAINER_CARD_PHASE_06, POKEMON_CENTER, AIDecide_PokemonCenter, AIPlay_PokemonCenter + ai_trainer_card_logic AI_TRAINER_CARD_PHASE_07, IMPOSTER_PROFESSOR_OAK, AIDecide_ImposterProfessorOak, AIPlay_ImposterProfessorOak + ai_trainer_card_logic AI_TRAINER_CARD_PHASE_12, ENERGY_SEARCH, AIDecide_EnergySearch, AIPlay_EnergySearch + ai_trainer_card_logic AI_TRAINER_CARD_PHASE_03, POKEDEX, AIDecide_Pokedex, AIPlay_Pokedex + ai_trainer_card_logic AI_TRAINER_CARD_PHASE_07, FULL_HEAL, AIDecide_FullHeal, AIPlay_FullHeal + ai_trainer_card_logic AI_TRAINER_CARD_PHASE_10, MR_FUJI, AIDecide_MrFuji, AIPlay_MrFuji + ai_trainer_card_logic AI_TRAINER_CARD_PHASE_10, SCOOP_UP, AIDecide_ScoopUp, AIPlay_ScoopUp + ai_trainer_card_logic AI_TRAINER_CARD_PHASE_02, MAINTENANCE, AIDecide_Maintenance, AIPlay_Maintenance + ai_trainer_card_logic AI_TRAINER_CARD_PHASE_03, RECYCLE, AIDecide_Recycle, AIPlay_Recycle + ai_trainer_card_logic AI_TRAINER_CARD_PHASE_13, LASS, AIDecide_Lass, AIPlay_Lass + ai_trainer_card_logic AI_TRAINER_CARD_PHASE_04, ITEM_FINDER, AIDecide_ItemFinder, AIPlay_ItemFinder + ai_trainer_card_logic AI_TRAINER_CARD_PHASE_01, IMAKUNI_CARD, AIDecide_Imakuni, AIPlay_Imakuni + ai_trainer_card_logic AI_TRAINER_CARD_PHASE_01, GAMBLER, AIDecide_Gambler, AIPlay_Gambler + ai_trainer_card_logic AI_TRAINER_CARD_PHASE_05, REVIVE, AIDecide_Revive, AIPlay_Revive + ai_trainer_card_logic AI_TRAINER_CARD_PHASE_13, POKEMON_FLUTE, AIDecide_PokemonFlute, AIPlay_PokemonFlute + ai_trainer_card_logic AI_TRAINER_CARD_PHASE_05, CLEFAIRY_DOLL, AIDecide_ClefairyDollOrMysteriousFossil, AIPlay_ClefairyDollOrMysteriousFossil + ai_trainer_card_logic AI_TRAINER_CARD_PHASE_05, MYSTERIOUS_FOSSIL, AIDecide_ClefairyDollOrMysteriousFossil, AIPlay_ClefairyDollOrMysteriousFossil + ai_trainer_card_logic AI_TRAINER_CARD_PHASE_02, POKE_BALL, AIDecide_Pokeball, AIPlay_Pokeball + ai_trainer_card_logic AI_TRAINER_CARD_PHASE_02, COMPUTER_SEARCH, AIDecide_ComputerSearch, AIPlay_ComputerSearch + ai_trainer_card_logic AI_TRAINER_CARD_PHASE_02, POKEMON_TRADER, AIDecide_PokemonTrader, AIPlay_PokemonTrader + db $ff diff --git a/src/engine/ai/common.asm b/src/engine/ai/common.asm new file mode 100644 index 0000000..d4f1da4 --- /dev/null +++ b/src/engine/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/deck_ai/deck_ai.asm b/src/engine/ai/deck_ai.asm index 97093c7..c330418 100644 --- a/src/engine/deck_ai/deck_ai.asm +++ b/src/engine/ai/deck_ai.asm @@ -61,22 +61,22 @@ ENDM ; wAICardListRetreatBonus : scores given to certain cards for retreat; ; wAICardListEnergyBonus : max number of energy cards and card scores. -INCLUDE "engine/deck_ai/decks/general.asm" -INCLUDE "engine/deck_ai/decks/sams_practice.asm" -INCLUDE "engine/deck_ai/decks/general_no_retreat.asm" -INCLUDE "engine/deck_ai/decks/legendary_moltres.asm" -INCLUDE "engine/deck_ai/decks/legendary_zapdos.asm" -INCLUDE "engine/deck_ai/decks/legendary_articuno.asm" -INCLUDE "engine/deck_ai/decks/legendary_dragonite.asm" -INCLUDE "engine/deck_ai/decks/first_strike.asm" -INCLUDE "engine/deck_ai/decks/rock_crusher.asm" -INCLUDE "engine/deck_ai/decks/go_go_rain_dance.asm" -INCLUDE "engine/deck_ai/decks/zapping_selfdestruct.asm" -INCLUDE "engine/deck_ai/decks/flower_power.asm" -INCLUDE "engine/deck_ai/decks/strange_psyshock.asm" -INCLUDE "engine/deck_ai/decks/wonders_of_science.asm" -INCLUDE "engine/deck_ai/decks/fire_charge.asm" -INCLUDE "engine/deck_ai/decks/im_ronald.asm" -INCLUDE "engine/deck_ai/decks/powerful_ronald.asm" -INCLUDE "engine/deck_ai/decks/invincible_ronald.asm" -INCLUDE "engine/deck_ai/decks/legendary_ronald.asm" +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/deck_ai/decks/fire_charge.asm b/src/engine/ai/decks/fire_charge.asm index f5b347b..f5b347b 100644 --- a/src/engine/deck_ai/decks/fire_charge.asm +++ b/src/engine/ai/decks/fire_charge.asm diff --git a/src/engine/deck_ai/decks/first_strike.asm b/src/engine/ai/decks/first_strike.asm index 2e636e1..2e636e1 100644 --- a/src/engine/deck_ai/decks/first_strike.asm +++ b/src/engine/ai/decks/first_strike.asm diff --git a/src/engine/deck_ai/decks/flower_power.asm b/src/engine/ai/decks/flower_power.asm index 4d423a3..4d423a3 100644 --- a/src/engine/deck_ai/decks/flower_power.asm +++ b/src/engine/ai/decks/flower_power.asm diff --git a/src/engine/deck_ai/decks/general.asm b/src/engine/ai/decks/general.asm index 039e101..039e101 100644 --- a/src/engine/deck_ai/decks/general.asm +++ b/src/engine/ai/decks/general.asm diff --git a/src/engine/deck_ai/decks/general_no_retreat.asm b/src/engine/ai/decks/general_no_retreat.asm index 20d84e3..20d84e3 100644 --- a/src/engine/deck_ai/decks/general_no_retreat.asm +++ b/src/engine/ai/decks/general_no_retreat.asm diff --git a/src/engine/deck_ai/decks/go_go_rain_dance.asm b/src/engine/ai/decks/go_go_rain_dance.asm index 23547e2..23547e2 100644 --- a/src/engine/deck_ai/decks/go_go_rain_dance.asm +++ b/src/engine/ai/decks/go_go_rain_dance.asm diff --git a/src/engine/deck_ai/decks/im_ronald.asm b/src/engine/ai/decks/im_ronald.asm index b002d83..b002d83 100644 --- a/src/engine/deck_ai/decks/im_ronald.asm +++ b/src/engine/ai/decks/im_ronald.asm diff --git a/src/engine/deck_ai/decks/invincible_ronald.asm b/src/engine/ai/decks/invincible_ronald.asm index 463560b..463560b 100644 --- a/src/engine/deck_ai/decks/invincible_ronald.asm +++ b/src/engine/ai/decks/invincible_ronald.asm diff --git a/src/engine/deck_ai/decks/legendary_articuno.asm b/src/engine/ai/decks/legendary_articuno.asm index 6409330..6409330 100644 --- a/src/engine/deck_ai/decks/legendary_articuno.asm +++ b/src/engine/ai/decks/legendary_articuno.asm diff --git a/src/engine/deck_ai/decks/legendary_dragonite.asm b/src/engine/ai/decks/legendary_dragonite.asm index 597f72c..597f72c 100644 --- a/src/engine/deck_ai/decks/legendary_dragonite.asm +++ b/src/engine/ai/decks/legendary_dragonite.asm diff --git a/src/engine/deck_ai/decks/legendary_moltres.asm b/src/engine/ai/decks/legendary_moltres.asm index d07afb9..d07afb9 100644 --- a/src/engine/deck_ai/decks/legendary_moltres.asm +++ b/src/engine/ai/decks/legendary_moltres.asm diff --git a/src/engine/deck_ai/decks/legendary_ronald.asm b/src/engine/ai/decks/legendary_ronald.asm index 3356838..3356838 100644 --- a/src/engine/deck_ai/decks/legendary_ronald.asm +++ b/src/engine/ai/decks/legendary_ronald.asm diff --git a/src/engine/deck_ai/decks/legendary_zapdos.asm b/src/engine/ai/decks/legendary_zapdos.asm index cc99f0c..cc99f0c 100644 --- a/src/engine/deck_ai/decks/legendary_zapdos.asm +++ b/src/engine/ai/decks/legendary_zapdos.asm diff --git a/src/engine/deck_ai/decks/powerful_ronald.asm b/src/engine/ai/decks/powerful_ronald.asm index 096fbea..096fbea 100644 --- a/src/engine/deck_ai/decks/powerful_ronald.asm +++ b/src/engine/ai/decks/powerful_ronald.asm diff --git a/src/engine/deck_ai/decks/rock_crusher.asm b/src/engine/ai/decks/rock_crusher.asm index 41a50fa..41a50fa 100644 --- a/src/engine/deck_ai/decks/rock_crusher.asm +++ b/src/engine/ai/decks/rock_crusher.asm diff --git a/src/engine/deck_ai/decks/sams_practice.asm b/src/engine/ai/decks/sams_practice.asm index dddce61..dddce61 100644 --- a/src/engine/deck_ai/decks/sams_practice.asm +++ b/src/engine/ai/decks/sams_practice.asm diff --git a/src/engine/deck_ai/decks/strange_psyshock.asm b/src/engine/ai/decks/strange_psyshock.asm index ef378b0..ef378b0 100644 --- a/src/engine/deck_ai/decks/strange_psyshock.asm +++ b/src/engine/ai/decks/strange_psyshock.asm diff --git a/src/engine/deck_ai/decks/wonders_of_science.asm b/src/engine/ai/decks/wonders_of_science.asm index 706a7e6..706a7e6 100644 --- a/src/engine/deck_ai/decks/wonders_of_science.asm +++ b/src/engine/ai/decks/wonders_of_science.asm diff --git a/src/engine/deck_ai/decks/zapping_selfdestruct.asm b/src/engine/ai/decks/zapping_selfdestruct.asm index da5e7c6..da5e7c6 100644 --- a/src/engine/deck_ai/decks/zapping_selfdestruct.asm +++ b/src/engine/ai/decks/zapping_selfdestruct.asm diff --git a/src/engine/ai/pkmn_powers.asm b/src/engine/ai/pkmn_powers.asm new file mode 100644 index 0000000..52a8036 --- /dev/null +++ b/src/engine/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, wcda5 + 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/bank08.asm b/src/engine/ai/trainer_cards.asm index 4b4444c..cd87a4b 100644 --- a/src/engine/bank08.asm +++ b/src/engine/ai/trainer_cards.asm @@ -1,50 +1,4 @@ -; unknown byte / card ID / function pointer 1 / function pointer 2 -unknown_data_20000: MACRO - db \1, \2 - dw \3 - dw \4 -ENDM - -Data_20000: ; 20000 (8:4000) - unknown_data_20000 AI_TRAINER_CARD_PHASE_07, POTION, AIDecide_Potion1, AIPlay_Potion - unknown_data_20000 AI_TRAINER_CARD_PHASE_10, POTION, AIDecide_Potion2, AIPlay_Potion - unknown_data_20000 AI_TRAINER_CARD_PHASE_08, SUPER_POTION, AIDecide_SuperPotion1, AIPlay_SuperPotion - unknown_data_20000 AI_TRAINER_CARD_PHASE_11, SUPER_POTION, AIDecide_SuperPotion2, AIPlay_SuperPotion - unknown_data_20000 AI_TRAINER_CARD_PHASE_13, DEFENDER, AIDecide_Defender1, AIPlay_Defender - unknown_data_20000 AI_TRAINER_CARD_PHASE_14, DEFENDER, AIDecide_Defender2, AIPlay_Defender - unknown_data_20000 AI_TRAINER_CARD_PHASE_13, PLUSPOWER, AIDecide_Pluspower1, AIPlay_Pluspower - unknown_data_20000 AI_TRAINER_CARD_PHASE_14, PLUSPOWER, AIDecide_Pluspower2, AIPlay_Pluspower - unknown_data_20000 AI_TRAINER_CARD_PHASE_09, SWITCH, AIDecide_Switch, AIPlay_Switch - unknown_data_20000 AI_TRAINER_CARD_PHASE_07, GUST_OF_WIND, AIDecide_GustOfWind, AIPlay_GustOfWind - unknown_data_20000 AI_TRAINER_CARD_PHASE_10, GUST_OF_WIND, AIDecide_GustOfWind, AIPlay_GustOfWind - unknown_data_20000 AI_TRAINER_CARD_PHASE_04, BILL, AIDecide_Bill, AIPlay_Bill - unknown_data_20000 AI_TRAINER_CARD_PHASE_05, ENERGY_REMOVAL, AIDecide_EnergyRemoval, AIPlay_EnergyRemoval - unknown_data_20000 AI_TRAINER_CARD_PHASE_05, SUPER_ENERGY_REMOVAL, AIDecide_SuperEnergyRemoval, AIPlay_SuperEnergyRemoval - unknown_data_20000 AI_TRAINER_CARD_PHASE_07, POKEMON_BREEDER, AIDecide_PokemonBreeder, AIPlay_PokemonBreeder - unknown_data_20000 AI_TRAINER_CARD_PHASE_15, PROFESSOR_OAK, AIDecide_ProfessorOak, AIPlay_ProfessorOak - unknown_data_20000 AI_TRAINER_CARD_PHASE_10, ENERGY_RETRIEVAL, AIDecide_EnergyRetrieval, AIPlay_EnergyRetrieval - unknown_data_20000 AI_TRAINER_CARD_PHASE_11, SUPER_ENERGY_RETRIEVAL, AIDecide_SuperEnergyRetrieval, AIPlay_SuperEnergyRetrieval - unknown_data_20000 AI_TRAINER_CARD_PHASE_06, POKEMON_CENTER, AIDecide_PokemonCenter, AIPlay_PokemonCenter - unknown_data_20000 AI_TRAINER_CARD_PHASE_07, IMPOSTER_PROFESSOR_OAK, AIDecide_ImposterProfessorOak, AIPlay_ImposterProfessorOak - unknown_data_20000 AI_TRAINER_CARD_PHASE_12, ENERGY_SEARCH, AIDecide_EnergySearch, AIPlay_EnergySearch - unknown_data_20000 AI_TRAINER_CARD_PHASE_03, POKEDEX, AIDecide_Pokedex, AIPlay_Pokedex - unknown_data_20000 AI_TRAINER_CARD_PHASE_07, FULL_HEAL, AIDecide_FullHeal, AIPlay_FullHeal - unknown_data_20000 AI_TRAINER_CARD_PHASE_10, MR_FUJI, AIDecide_MrFuji, AIPlay_MrFuji - unknown_data_20000 AI_TRAINER_CARD_PHASE_10, SCOOP_UP, AIDecide_ScoopUp, AIPlay_ScoopUp - unknown_data_20000 AI_TRAINER_CARD_PHASE_02, MAINTENANCE, AIDecide_Maintenance, AIPlay_Maintenance - unknown_data_20000 AI_TRAINER_CARD_PHASE_03, RECYCLE, AIDecide_Recycle, AIPlay_Recycle - unknown_data_20000 AI_TRAINER_CARD_PHASE_13, LASS, AIDecide_Lass, AIPlay_Lass - unknown_data_20000 AI_TRAINER_CARD_PHASE_04, ITEM_FINDER, AIDecide_ItemFinder, AIPlay_ItemFinder - unknown_data_20000 AI_TRAINER_CARD_PHASE_01, IMAKUNI_CARD, AIDecide_Imakuni, AIPlay_Imakuni - unknown_data_20000 AI_TRAINER_CARD_PHASE_01, GAMBLER, AIDecide_Gambler, AIPlay_Gambler - unknown_data_20000 AI_TRAINER_CARD_PHASE_05, REVIVE, AIDecide_Revive, AIPlay_Revive - unknown_data_20000 AI_TRAINER_CARD_PHASE_13, POKEMON_FLUTE, AIDecide_PokemonFlute, AIPlay_PokemonFlute - unknown_data_20000 AI_TRAINER_CARD_PHASE_05, CLEFAIRY_DOLL, AIDecide_ClefairyDollOrMysteriousFossil, AIPlay_ClefairyDollOrMysteriousFossil - unknown_data_20000 AI_TRAINER_CARD_PHASE_05, MYSTERIOUS_FOSSIL, AIDecide_ClefairyDollOrMysteriousFossil, AIPlay_ClefairyDollOrMysteriousFossil - unknown_data_20000 AI_TRAINER_CARD_PHASE_02, POKE_BALL, AIDecide_Pokeball, AIPlay_Pokeball - unknown_data_20000 AI_TRAINER_CARD_PHASE_02, COMPUTER_SEARCH, AIDecide_ComputerSearch, AIPlay_ComputerSearch - unknown_data_20000 AI_TRAINER_CARD_PHASE_02, POKEMON_TRADER, AIDecide_PokemonTrader, AIPlay_PokemonTrader - db $ff +INCLUDE "data/ai_trainer_card_logic.asm" _AIProcessHandTrainerCards: ; 200e5 (8:40e5) ld [wce18], a @@ -64,7 +18,7 @@ _AIProcessHandTrainerCards: ; 200e5 (8:40e5) push hl ld a, [wce18] ld d, a - ld hl, Data_20000 + ld hl, AITrainerCardLogic .loop_data xor a ld [wCurrentAIFlags], a @@ -220,7 +174,7 @@ AIPlay_Potion: ; 201b5 (8:41b5) AIDecide_Potion1: ; 201d1 (8:41d1) farcall AIDecideWhetherToRetreat jr c, .no_carry - call Func_22bad + call AICheckIfAttackIsHighRecoil jr c, .no_carry xor a ; active card ldh [hTempPlayAreaLocation_ff9d], a @@ -341,10 +295,10 @@ AIDecide_Potion2: ; 20204 (8:4204) scf ret -; return carry for active card if not Hgh Recoil. +; return carry for active card if not High Recoil. .active_card push de - call Func_22bad + call AICheckIfAttackIsHighRecoil pop de jr c, .no_carry ld a, e @@ -410,7 +364,7 @@ AIPlay_SuperPotion: ; 202a8 (8:42a8) AIDecide_SuperPotion1: ; 202cc (8:42cc) farcall AIDecideWhetherToRetreat jr c, .no_carry - call Func_22bad + call AICheckIfAttackIsHighRecoil jr c, .no_carry xor a ldh [hTempPlayAreaLocation_ff9d], a @@ -551,7 +505,7 @@ AIDecide_SuperPotion2: ; 2030f (8:430f) ; return carry for active card if not Hgh Recoil. .active_card push de - call Func_22bad + call AICheckIfAttackIsHighRecoil pop de jr c, .no_carry ld a, e @@ -6117,2203 +6071,3 @@ AIDecide_PokemonTrader_Flamethrower: ; 22133 (8:6133) .set_carry scf ret - -; 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, wcda5 - 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 - -; 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. -Func_22bad: ; 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/bank05.asm b/src/engine/bank05.asm index 75ca6b9..4845d40 100644 --- a/src/engine/bank05.asm +++ b/src/engine/bank05.asm @@ -1205,7 +1205,7 @@ AIProcessHandTrainerCards: ; 14663 (5:4663) farcall _AIProcessHandTrainerCards ret -INCLUDE "engine/deck_ai/deck_ai.asm" +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 diff --git a/src/engine/home.asm b/src/engine/home.asm index 87deaaa..670557f 100644 --- a/src/engine/home.asm +++ b/src/engine/home.asm @@ -8258,7 +8258,7 @@ AIDoAction_TakePrize: ; 2bd7 (0:2bd7) jr AIDoAction ; this line is not needed ; calls the appropriate AI routine to handle action, -; depending on the deck ID (see engine/deck_ai/deck_ai.asm) +; depending on the deck ID (see engine/ai/deck_ai.asm) ; input: ; - a = AIACTION_* constant AIDoAction: ; 2bdb (0:2bdb) diff --git a/src/layout.link b/src/layout.link index 413f9b8..7a0a42a 100644 --- a/src/layout.link +++ b/src/layout.link @@ -46,7 +46,7 @@ ROMX $07 "Credits Sequence" "Booster Packs" ROMX $08 - "Bank 8" + "AI Logic" ROMX $0b "Effect Functions" ROMX $0c diff --git a/src/main.asm b/src/main.asm index b331dc8..e16c9ab 100644 --- a/src/main.asm +++ b/src/main.asm @@ -31,8 +31,10 @@ INCLUDE "data/sequences/credits_sequence.asm" SECTION "Booster Packs", ROMX INCLUDE "engine/booster_packs.asm" -SECTION "Bank 8", ROMX -INCLUDE "engine/bank08.asm" +SECTION "AI Logic", ROMX +INCLUDE "engine/ai/trainer_cards.asm" +INCLUDE "engine/ai/pkmn_powers.asm" +INCLUDE "engine/ai/common.asm" SECTION "Effect Functions", ROMX INCLUDE "engine/effect_functions.asm" |