summaryrefslogtreecommitdiff
path: root/engine
diff options
context:
space:
mode:
Diffstat (limited to 'engine')
-rw-r--r--engine/battle/ai/move.asm208
-rw-r--r--engine/battle/ai/redundant.asm198
-rw-r--r--engine/battle/ai/switch.asm658
-rw-r--r--engine/pokemon/learn.asm6
4 files changed, 1067 insertions, 3 deletions
diff --git a/engine/battle/ai/move.asm b/engine/battle/ai/move.asm
new file mode 100644
index 00000000..060761a0
--- /dev/null
+++ b/engine/battle/ai/move.asm
@@ -0,0 +1,208 @@
+AIChooseMove:
+; Score each move in wEnemyMonMoves starting from wBuffer1. Lower is better.
+; Pick the move with the lowest score.
+
+; Wildmons attack at random.
+ ld a, [wBattleMode]
+ dec a
+ ret z
+
+ ld a, [wLinkMode]
+ and a
+ ret nz
+
+; No use picking a move if there's no choice.
+ farcall CheckEnemyLockedIn
+ ret nz
+
+; The default score is 20. Unusable moves are given a score of 80.
+ ld a, 20
+ ld hl, wBuffer1
+ ld [hli], a
+ ld [hli], a
+ ld [hli], a
+ ld [hl], a
+
+; Don't pick disabled moves.
+ ld a, [wEnemyDisabledMove]
+ and a
+ jr z, .CheckPP
+
+ ld hl, wEnemyMonMoves
+ ld c, 0
+.CheckDisabledMove:
+ cp [hl]
+ jr z, .ScoreDisabledMove
+ inc c
+ inc hl
+ jr .CheckDisabledMove
+.ScoreDisabledMove:
+ ld hl, wBuffer1
+ ld b, 0
+ add hl, bc
+ ld [hl], 80
+
+; Don't pick moves with 0 PP.
+.CheckPP:
+ ld hl, wBuffer1 - 1
+ ld de, wEnemyMonPP
+ ld b, 0
+.CheckMovePP:
+ inc b
+ ld a, b
+ cp wEnemyMonMovesEnd - wEnemyMonMoves + 1
+ jr z, .ApplyLayers
+ inc hl
+ ld a, [de]
+ inc de
+ and PP_MASK
+ jr nz, .CheckMovePP
+ ld [hl], 80
+ jr .CheckMovePP
+
+; Apply AI scoring layers depending on the trainer class.
+.ApplyLayers:
+ ld hl, TrainerClassAttributes + TRNATTR_AI_MOVE_WEIGHTS
+ ld a, [wTrainerClass]
+ dec a
+ ld bc, 7 ; Trainer2AI - Trainer1AI
+ call AddNTimes
+ lb bc, CHECK_FLAG, 0
+ push bc
+ push hl
+
+.CheckLayer:
+ pop hl
+ pop bc
+
+ ld a, c
+ cp 16 ; up to 16 scoring layers
+ jr z, .DecrementScores
+
+ push bc
+ ld d, BANK(TrainerClassAttributes)
+ predef SmallFarFlagAction
+ ld d, c
+ pop bc
+
+ inc c
+ push bc
+ push hl
+
+ ld a, d
+ and a
+ jr z, .CheckLayer
+
+ ld hl, AIScoringPointers
+ dec c
+ ld b, 0
+ add hl, bc
+ add hl, bc
+ ld a, [hli]
+ ld h, [hl]
+ ld l, a
+ ld a, BANK(AIScoring)
+ call FarCall_hl
+
+ jr .CheckLayer
+
+; Decrement the scores of all moves one by one until one reaches 0.
+.DecrementScores:
+ ld hl, wBuffer1
+ ld de, wEnemyMonMoves
+ ld c, wEnemyMonMovesEnd - wEnemyMonMoves
+
+.DecrementNextScore:
+ ; If the enemy has no moves, this will infinite.
+ ld a, [de]
+ inc de
+ and a
+ jr z, .DecrementScores
+
+ ; We are done whenever a score reaches 0
+ dec [hl]
+ jr z, .PickLowestScoreMoves
+
+ ; If we just decremented the fourth move's score, go back to the first move
+ inc hl
+ dec c
+ jr z, .DecrementScores
+
+ jr .DecrementNextScore
+
+; In order to avoid bias towards the moves located first in memory, increment the scores
+; that were decremented one more time than the rest (in case there was a tie).
+; This means that the minimum score will be 1.
+.PickLowestScoreMoves:
+ ld a, c
+
+.move_loop
+ inc [hl]
+ dec hl
+ inc a
+ cp NUM_MOVES + 1
+ jr nz, .move_loop
+
+ ld hl, wBuffer1
+ ld de, wEnemyMonMoves
+ ld c, NUM_MOVES
+
+; Give a score of 0 to a blank move
+.loop2
+ ld a, [de]
+ and a
+ jr nz, .skip_load
+ ld [hl], a
+
+; Disregard the move if its score is not 1
+.skip_load
+ ld a, [hl]
+ dec a
+ jr z, .keep
+ xor a
+ ld [hli], a
+ jr .after_toss
+
+.keep
+ ld a, [de]
+ ld [hli], a
+.after_toss
+ inc de
+ dec c
+ jr nz, .loop2
+
+; Randomly choose one of the moves with a score of 1
+.ChooseMove:
+ ld hl, wBuffer1
+ call Random
+ maskbits NUM_MOVES
+ ld c, a
+ ld b, 0
+ add hl, bc
+ ld a, [hl]
+ and a
+ jr z, .ChooseMove
+
+ ld [wCurEnemyMove], a
+ ld a, c
+ ld [wCurEnemyMoveNum], a
+ ret
+
+AIScoringPointers:
+; entries correspond to AI_* constants
+ dw AI_Basic
+ dw AI_Setup
+ dw AI_Types
+ dw AI_Offensive
+ dw AI_Smart
+ dw AI_Opportunist
+ dw AI_Aggressive
+ dw AI_Cautious
+ dw AI_Status
+ dw AI_Risky
+ dw AI_None
+ dw AI_None
+ dw AI_None
+ dw AI_None
+ dw AI_None
+ dw AI_None
diff --git a/engine/battle/ai/redundant.asm b/engine/battle/ai/redundant.asm
new file mode 100644
index 00000000..d78fccb8
--- /dev/null
+++ b/engine/battle/ai/redundant.asm
@@ -0,0 +1,198 @@
+AI_Redundant:
+; Check if move effect c will fail because it's already been used.
+; Return z if the move is a good choice.
+; Return nz if the move is a bad choice.
+ ld a, c
+ ld de, 3
+ ld hl, .Moves
+ call IsInArray
+ jp nc, .NotRedundant
+ inc hl
+ ld a, [hli]
+ ld h, [hl]
+ ld l, a
+ jp hl
+
+.Moves:
+ dbw EFFECT_DREAM_EATER, .DreamEater
+ dbw EFFECT_HEAL, .Heal
+ dbw EFFECT_LIGHT_SCREEN, .LightScreen
+ dbw EFFECT_MIST, .Mist
+ dbw EFFECT_FOCUS_ENERGY, .FocusEnergy
+ dbw EFFECT_CONFUSE, .Confuse
+ dbw EFFECT_TRANSFORM, .Transform
+ dbw EFFECT_REFLECT, .Reflect
+ dbw EFFECT_SUBSTITUTE, .Substitute
+ dbw EFFECT_LEECH_SEED, .LeechSeed
+ dbw EFFECT_DISABLE, .Disable
+ dbw EFFECT_ENCORE, .Encore
+ dbw EFFECT_SNORE, .Snore
+ dbw EFFECT_SLEEP_TALK, .SleepTalk
+ dbw EFFECT_MEAN_LOOK, .MeanLook
+ dbw EFFECT_NIGHTMARE, .Nightmare
+ dbw EFFECT_SPIKES, .Spikes
+ dbw EFFECT_FORESIGHT, .Foresight
+ dbw EFFECT_PERISH_SONG, .PerishSong
+ dbw EFFECT_SANDSTORM, .Sandstorm
+ dbw EFFECT_ATTRACT, .Attract
+ dbw EFFECT_SAFEGUARD, .Safeguard
+ dbw EFFECT_RAIN_DANCE, .RainDance
+ dbw EFFECT_SUNNY_DAY, .SunnyDay
+ dbw EFFECT_TELEPORT, .Teleport
+ dbw EFFECT_MORNING_SUN, .MorningSun
+ dbw EFFECT_SYNTHESIS, .Synthesis
+ dbw EFFECT_MOONLIGHT, .Moonlight
+ dbw EFFECT_SWAGGER, .Swagger
+ dbw EFFECT_FUTURE_SIGHT, .FutureSight
+ db -1
+
+.LightScreen:
+ ld a, [wEnemyScreens]
+ bit SCREENS_LIGHT_SCREEN, a
+ ret
+
+.Mist:
+ ld a, [wEnemySubStatus4]
+ bit SUBSTATUS_MIST, a
+ ret
+
+.FocusEnergy:
+ ld a, [wEnemySubStatus4]
+ bit SUBSTATUS_FOCUS_ENERGY, a
+ ret
+
+.Confuse:
+ ld a, [wPlayerSubStatus3]
+ bit SUBSTATUS_CONFUSED, a
+ ret nz
+ ld a, [wPlayerScreens]
+ bit SCREENS_SAFEGUARD, a
+ ret
+
+.Transform:
+ ld a, [wEnemySubStatus5]
+ bit SUBSTATUS_TRANSFORMED, a
+ ret
+
+.Reflect:
+ ld a, [wEnemyScreens]
+ bit SCREENS_REFLECT, a
+ ret
+
+.Substitute:
+ ld a, [wEnemySubStatus4]
+ bit SUBSTATUS_SUBSTITUTE, a
+ ret
+
+.LeechSeed:
+ ld a, [wPlayerSubStatus4]
+ bit SUBSTATUS_LEECH_SEED, a
+ ret
+
+.Disable:
+ ld a, [wPlayerDisableCount]
+ and a
+ ret
+
+.Encore:
+ ld a, [wPlayerSubStatus5]
+ bit SUBSTATUS_ENCORED, a
+ ret
+
+.Snore:
+.SleepTalk:
+ ld a, [wEnemyMonStatus]
+ and SLP
+ jr z, .Redundant
+ jr .NotRedundant
+
+.MeanLook:
+ ld a, [wEnemySubStatus5]
+ bit SUBSTATUS_CANT_RUN, a
+ ret
+
+.Nightmare:
+ ld a, [wBattleMonStatus]
+ and a
+ jr z, .Redundant
+ ld a, [wPlayerSubStatus1]
+ bit SUBSTATUS_NIGHTMARE, a
+ ret
+
+.Spikes:
+ ld a, [wPlayerScreens]
+ bit SCREENS_SPIKES, a
+ ret
+
+.Foresight:
+ ld a, [wPlayerSubStatus1]
+ bit SUBSTATUS_IDENTIFIED, a
+ ret
+
+.PerishSong:
+ ld a, [wPlayerSubStatus1]
+ bit SUBSTATUS_PERISH, a
+ ret
+
+.Sandstorm:
+ ld a, [wBattleWeather]
+ cp WEATHER_SANDSTORM
+ jr z, .Redundant
+ jr .NotRedundant
+
+.Attract:
+ farcall CheckOppositeGender
+ jr c, .Redundant
+ ld a, [wPlayerSubStatus1]
+ bit SUBSTATUS_IN_LOVE, a
+ ret
+
+.Safeguard:
+ ld a, [wEnemyScreens]
+ bit SCREENS_SAFEGUARD, a
+ ret
+
+.RainDance:
+ ld a, [wBattleWeather]
+ cp WEATHER_RAIN
+ jr z, .Redundant
+ jr .NotRedundant
+
+.SunnyDay:
+ ld a, [wBattleWeather]
+ cp WEATHER_SUN
+ jr z, .Redundant
+ jr .NotRedundant
+
+.DreamEater:
+ ld a, [wBattleMonStatus]
+ and SLP
+ jr z, .Redundant
+ jr .NotRedundant
+
+.Swagger:
+ ld a, [wPlayerSubStatus3]
+ bit SUBSTATUS_CONFUSED, a
+ ret
+
+.FutureSight:
+ ld a, [wEnemyScreens]
+ bit 5, a
+ ret
+
+.Heal:
+.MorningSun:
+.Synthesis:
+.Moonlight:
+ farcall AICheckEnemyMaxHP
+ jr nc, .NotRedundant
+
+.Teleport:
+.Redundant:
+ ld a, 1
+ and a
+ ret
+
+.NotRedundant:
+ xor a
+ ret
diff --git a/engine/battle/ai/switch.asm b/engine/battle/ai/switch.asm
new file mode 100644
index 00000000..1998e7ec
--- /dev/null
+++ b/engine/battle/ai/switch.asm
@@ -0,0 +1,658 @@
+CheckPlayerMoveTypeMatchups:
+; Check how well the moves you've already used
+; fare against the enemy's Pokemon. Used to
+; score a potential switch.
+ push hl
+ push de
+ push bc
+ ld a, 10
+ ld [wEnemyAISwitchScore], a
+ ld hl, wPlayerUsedMoves
+ ld a, [hl]
+ and a
+ jr z, .unknown_moves
+
+ ld d, NUM_MOVES
+ ld e, 0
+.loop
+ ld a, [hli]
+ and a
+ jr z, .exit
+ push hl
+ dec a
+ ld hl, Moves + MOVE_POWER
+ call GetMoveAttr
+ and a
+ jr z, .next
+
+ inc hl
+ call GetMoveByte
+ ld hl, wEnemyMonType
+ call CheckTypeMatchup
+ ld a, [wTypeMatchup]
+ cp EFFECTIVE + 1 ; 1.0 + 0.1
+ jr nc, .super_effective
+ and a
+ jr z, .next
+ cp EFFECTIVE ; 1.0
+ jr nc, .neutral
+
+.not_very_effective
+ ld a, e
+ cp 1 ; 0.1
+ jr nc, .next
+ ld e, 1
+ jr .next
+
+.neutral
+ ld e, 2
+ jr .next
+
+.super_effective
+ call .DecreaseScore
+ pop hl
+ jr .done
+
+.next
+ pop hl
+ dec d
+ jr nz, .loop
+
+.exit
+ ld a, e
+ cp 2
+ jr z, .done
+ call .IncreaseScore
+ ld a, e
+ and a
+ jr nz, .done
+ call .IncreaseScore
+ jr .done
+
+.unknown_moves
+ ld a, [wBattleMonType1]
+ ld b, a
+ ld hl, wEnemyMonType1
+ call CheckTypeMatchup
+ ld a, [wTypeMatchup]
+ cp EFFECTIVE + 1 ; 1.0 + 0.1
+ jr c, .ok
+ call .DecreaseScore
+.ok
+ ld a, [wBattleMonType2]
+ cp b
+ jr z, .ok2
+ call CheckTypeMatchup
+ ld a, [wTypeMatchup]
+ cp EFFECTIVE + 1 ; 1.0 + 0.1
+ jr c, .ok2
+ call .DecreaseScore
+.ok2
+
+.done
+ call .CheckEnemyMoveMatchups
+ pop bc
+ pop de
+ pop hl
+ ret
+
+.CheckEnemyMoveMatchups:
+ ld de, wEnemyMonMoves
+ ld b, NUM_MOVES + 1
+ ld c, 0
+
+ ld a, [wTypeMatchup]
+ push af
+.loop2
+ dec b
+ jr z, .exit2
+
+ ld a, [de]
+ and a
+ jr z, .exit2
+
+ inc de
+ dec a
+ ld hl, Moves + MOVE_POWER
+ call GetMoveAttr
+ and a
+ jr z, .loop2
+
+ inc hl
+ call GetMoveByte
+ ld hl, wBattleMonType1
+ call CheckTypeMatchup
+
+ ld a, [wTypeMatchup]
+ ; immune
+ and a
+ jr z, .loop2
+
+ ; not very effective
+ inc c
+ cp EFFECTIVE
+ jr c, .loop2
+
+ ; neutral
+ inc c
+ inc c
+ inc c
+ inc c
+ inc c
+ cp EFFECTIVE
+ jr z, .loop2
+
+ ; super effective
+ ld c, 100
+ jr .loop2
+
+.exit2
+ pop af
+ ld [wTypeMatchup], a
+
+ ld a, c
+ and a
+ jr z, .doubledown ; double down
+ cp 5
+ jr c, .DecreaseScore ; down
+ cp 100
+ ret c
+ jr .IncreaseScore ; up
+
+.doubledown
+ call .DecreaseScore
+.DecreaseScore:
+ ld a, [wEnemyAISwitchScore]
+ dec a
+ ld [wEnemyAISwitchScore], a
+ ret
+
+.IncreaseScore:
+ ld a, [wEnemyAISwitchScore]
+ inc a
+ ld [wEnemyAISwitchScore], a
+ ret
+
+CheckAbleToSwitch:
+ xor a
+ ld [wEnemySwitchMonParam], a
+ call FindAliveEnemyMons
+ ret c
+
+ ld a, [wEnemySubStatus1]
+ bit SUBSTATUS_PERISH, a
+ jr z, .no_perish
+
+ ld a, [wEnemyPerishCount]
+ cp 1
+ jr nz, .no_perish
+
+ ; Perish count is 1
+
+ call FindAliveEnemyMons
+ call FindEnemyMonsWithAtLeastQuarterMaxHP
+ call FindEnemyMonsThatResistPlayer
+ call FindAliveEnemyMonsWithASuperEffectiveMove
+
+ ld a, e
+ cp 2
+ jr nz, .not_2
+
+ ld a, [wEnemyAISwitchScore]
+ add $30 ; maximum chance
+ ld [wEnemySwitchMonParam], a
+ ret
+
+.not_2
+ call FindAliveEnemyMons
+ sla c
+ sla c
+ ld b, $ff
+
+.loop1
+ inc b
+ sla c
+ jr nc, .loop1
+
+ ld a, b
+ add $30 ; maximum chance
+ ld [wEnemySwitchMonParam], a
+ ret
+
+.no_perish
+ call CheckPlayerMoveTypeMatchups
+ ld a, [wEnemyAISwitchScore]
+ cp 11
+ ret nc
+
+ ld a, [wLastPlayerCounterMove]
+ and a
+ jr z, .no_last_counter_move
+
+ call FindEnemyMonsImmuneToLastCounterMove
+ ld a, [wEnemyAISwitchScore]
+ and a
+ jr z, .no_last_counter_move
+
+ ld c, a
+ call FindEnemyMonsWithASuperEffectiveMove
+ ld a, [wEnemyAISwitchScore]
+ cp $ff
+ ret z
+
+ ld b, a
+ ld a, e
+ cp 2
+ jr z, .not_2_again
+
+ call CheckPlayerMoveTypeMatchups
+ ld a, [wEnemyAISwitchScore]
+ cp 10
+ ret nc
+
+ ld a, b
+ add $10
+ ld [wEnemySwitchMonParam], a
+ ret
+
+.not_2_again
+ ld c, $10
+ call CheckPlayerMoveTypeMatchups
+ ld a, [wEnemyAISwitchScore]
+ cp 10
+ jr nc, .okay
+ ld c, $20
+
+.okay
+ ld a, b
+ add c
+ ld [wEnemySwitchMonParam], a
+ ret
+
+.no_last_counter_move
+ call CheckPlayerMoveTypeMatchups
+ ld a, [wEnemyAISwitchScore]
+ cp 10
+ ret nc
+
+ call FindAliveEnemyMons
+ call FindEnemyMonsWithAtLeastQuarterMaxHP
+ call FindEnemyMonsThatResistPlayer
+ call FindAliveEnemyMonsWithASuperEffectiveMove
+
+ ld a, e
+ cp $2
+ ret nz
+
+ ld a, [wEnemyAISwitchScore]
+ add $10
+ ld [wEnemySwitchMonParam], a
+ ret
+
+FindAliveEnemyMons:
+ ld a, [wOTPartyCount]
+ cp 2
+ jr c, .only_one
+
+ ld d, a
+ ld e, 0
+ ld b, 1 << (PARTY_LENGTH - 1)
+ ld c, 0
+ ld hl, wOTPartyMon1HP
+
+.loop
+ ld a, [wCurOTMon]
+ cp e
+ jr z, .next
+
+ push bc
+ ld b, [hl]
+ inc hl
+ ld a, [hld]
+ or b
+ pop bc
+ jr z, .next
+
+ ld a, c
+ or b
+ ld c, a
+
+.next
+ srl b
+ push bc
+ ld bc, PARTYMON_STRUCT_LENGTH
+ add hl, bc
+ pop bc
+ inc e
+ dec d
+ jr nz, .loop
+
+ ld a, c
+ and a
+ jr nz, .more_than_one
+
+.only_one
+ scf
+ ret
+
+.more_than_one
+ and a
+ ret
+
+FindEnemyMonsImmuneToLastCounterMove:
+ ld hl, wOTPartyMon1
+ ld a, [wOTPartyCount]
+ ld b, a
+ ld c, 1 << (PARTY_LENGTH - 1)
+ ld d, 0
+ xor a
+ ld [wEnemyAISwitchScore], a
+
+.loop
+ ld a, [wCurOTMon]
+ cp d
+ push hl
+ jr z, .next
+
+ push hl
+ push bc
+
+ ; If the Pokemon has at least 1 HP...
+ ld bc, MON_HP
+ add hl, bc
+ pop bc
+ ld a, [hli]
+ or [hl]
+ pop hl
+ jr z, .next
+
+ ld a, [hl]
+ ld [wCurSpecies], a
+ call GetBaseData
+
+ ; the player's last move is damaging...
+ ld a, [wLastPlayerCounterMove]
+ dec a
+ ld hl, Moves + MOVE_POWER
+ call GetMoveAttr
+ and a
+ jr z, .next
+
+ ; and the Pokemon is immune to it...
+ inc hl
+ call GetMoveByte
+ ld hl, wBaseType
+ call CheckTypeMatchup
+ ld a, [wTypeMatchup]
+ and a
+ jr nz, .next
+
+ ; ... encourage that Pokemon.
+ ld a, [wEnemyAISwitchScore]
+ or c
+ ld [wEnemyAISwitchScore], a
+.next
+ pop hl
+ dec b
+ ret z
+
+ push bc
+ ld bc, PARTYMON_STRUCT_LENGTH
+ add hl, bc
+ pop bc
+
+ inc d
+ srl c
+ jr .loop
+
+FindAliveEnemyMonsWithASuperEffectiveMove:
+ push bc
+ ld a, [wOTPartyCount]
+ ld e, a
+ ld hl, wOTPartyMon1HP
+ ld b, 1 << (PARTY_LENGTH - 1)
+ ld c, 0
+.loop
+ ld a, [hli]
+ or [hl]
+ jr z, .next
+
+ ld a, b
+ or c
+ ld c, a
+
+.next
+ srl b
+ push bc
+ ld bc, wPartyMon2HP - (wPartyMon1HP + 1)
+ add hl, bc
+ pop bc
+ dec e
+ jr nz, .loop
+
+ ld a, c
+ pop bc
+
+ and c
+ ld c, a
+ ; fallthrough
+
+FindEnemyMonsWithASuperEffectiveMove:
+ ld a, -1
+ ld [wEnemyAISwitchScore], a
+ ld hl, wOTPartyMon1Moves
+ ld b, 1 << (PARTY_LENGTH - 1)
+ ld d, 0
+ ld e, 0
+.loop
+ ld a, b
+ and c
+ jr z, .next
+
+ push hl
+ push bc
+ ; for move on mon:
+ ld b, NUM_MOVES
+ ld c, 0
+.loop3
+ ; if move is None: break
+ ld a, [hli]
+ and a
+ push hl
+ jr z, .break3
+
+ ; if move has no power: continue
+ dec a
+ ld hl, Moves + MOVE_POWER
+ call GetMoveAttr
+ and a
+ jr z, .nope
+
+ ; check type matchups
+ inc hl
+ call GetMoveByte
+ ld hl, wBattleMonType1
+ call CheckTypeMatchup
+
+ ; if immune or not very effective: continue
+ ld a, [wTypeMatchup]
+ cp 10
+ jr c, .nope
+
+ ; if neutral: load 1 and continue
+ ld e, 1
+ cp EFFECTIVE + 1
+ jr c, .nope
+
+ ; if super-effective: load 2 and break
+ ld e, 2
+ jr .break3
+
+.nope
+ pop hl
+ dec b
+ jr nz, .loop3
+
+ jr .done
+
+.break3
+ pop hl
+.done
+ ld a, e
+ pop bc
+ pop hl
+ cp 2
+ jr z, .done2 ; at least one move is super-effective
+ cp 1
+ jr nz, .next ; no move does more than half damage
+
+ ; encourage this pokemon
+ ld a, d
+ or b
+ ld d, a
+ jr .next ; such a long jump
+
+.next
+ ; next pokemon?
+ push bc
+ ld bc, PARTYMON_STRUCT_LENGTH
+ add hl, bc
+ pop bc
+ srl b
+ jr nc, .loop
+
+ ; if no pokemon has a super-effective move: return
+ ld a, d
+ ld b, a
+ and a
+ ret z
+
+.done2
+ ; convert the bit flag to an int and return
+ push bc
+ sla b
+ sla b
+ ld c, $ff
+.loop2
+ inc c
+ sla b
+ jr nc, .loop2
+
+ ld a, c
+ ld [wEnemyAISwitchScore], a
+ pop bc
+ ret
+
+FindEnemyMonsThatResistPlayer:
+ push bc
+ ld hl, wOTPartyCount
+ ld b, 1 << (PARTY_LENGTH - 1)
+ ld c, 0
+
+.loop
+ ld a, [hli]
+ cp $ff
+ jr z, .done
+
+ push hl
+ ld [wCurSpecies], a
+ call GetBaseData
+ ld a, [wLastPlayerCounterMove]
+ and a
+ jr z, .skip_move
+
+ dec a
+ ld hl, Moves + MOVE_POWER
+ call GetMoveAttr
+ and a
+ jr z, .skip_move
+
+ inc hl
+ call GetMoveByte
+ jr .check_type
+
+.skip_move
+ ld a, [wBattleMonType1]
+ ld hl, wBaseType
+ call CheckTypeMatchup
+ ld a, [wTypeMatchup]
+ cp 10 + 1
+ jr nc, .dont_choose_mon
+ ld a, [wBattleMonType2]
+
+.check_type
+ ld hl, wBaseType
+ call CheckTypeMatchup
+ ld a, [wTypeMatchup]
+ cp EFFECTIVE + 1
+ jr nc, .dont_choose_mon
+
+ ld a, b
+ or c
+ ld c, a
+
+.dont_choose_mon
+ srl b
+ pop hl
+ jr .loop
+
+.done
+ ld a, c
+ pop bc
+ and c
+ ld c, a
+ ret
+
+FindEnemyMonsWithAtLeastQuarterMaxHP:
+ push bc
+ ld de, wOTPartyCount
+ ld b, 1 << (PARTY_LENGTH - 1)
+ ld c, 0
+ ld hl, wOTPartyMon1HP
+
+.loop
+ ld a, [de]
+ inc de
+ cp $ff
+ jr z, .done
+
+ push hl
+ push bc
+ ld b, [hl]
+ inc hl
+ ld c, [hl]
+ inc hl
+ inc hl
+; hl = MaxHP + 1
+; bc = [CurHP] * 4
+ srl c
+ rl b
+ srl c
+ rl b
+; if bc >= [hl], encourage
+ ld a, [hld]
+ cp c
+ ld a, [hl]
+ sbc b
+ pop bc
+ jr nc, .next
+
+ ld a, b
+ or c
+ ld c, a
+
+.next
+ srl b
+ pop hl
+ push bc
+ ld bc, PARTYMON_STRUCT_LENGTH
+ add hl, bc
+ pop bc
+ jr .loop
+
+.done
+ ld a, c
+ pop bc
+ and c
+ ld c, a
+ ret
diff --git a/engine/pokemon/learn.asm b/engine/pokemon/learn.asm
index b28f754b..65816783 100644
--- a/engine/pokemon/learn.asm
+++ b/engine/pokemon/learn.asm
@@ -33,12 +33,12 @@ LearnMove:
ld a, [wBattleMode]
and a
jr z, .asm_6638
- ld a, [wcbd3]
+ ld a, [wDisabledMove]
cp b
jr nz, .asm_6638
xor a
- ld [wcbd3], a
- ld [wcb53], a
+ ld [wDisabledMove], a
+ ld [wPlayerDisableCount], a
.asm_6638
call GetMoveName
ld hl, Text_1_2_and_Poof