diff --git a/battle/ai/scoring.asm b/battle/ai/scoring.asm index d495b9808..e0009210f 100644 --- a/battle/ai/scoring.asm +++ b/battle/ai/scoring.asm @@ -1,5 +1,5 @@ AIScoring_RedStatus: ; 38591 -; Don't use status-only moves if the player can't be statused. +; Handle the AI of status-only moves and moves with special effects ld hl, Buffer1 - 1 ld de, EnemyMonMoves @@ -19,15 +19,19 @@ AIScoring_RedStatus: ; 38591 ld a, [wEnemyMoveStruct + MOVE_EFFECT] ld c, a +; Dismiss moves with special effects if they are +; useless or not a good choice right now. +; For example, healing moves, weather moves, Dream Eater... push hl push de push bc - callba Function2c41a + callba AISpecialEffects pop bc pop de pop hl jr nz, .discourage +; Dismiss status-only moves if the player can't be statused. ld a, [wEnemyMoveStruct + MOVE_EFFECT] push hl push de @@ -45,6 +49,7 @@ AIScoring_RedStatus: ; 38591 and a jr nz, .discourage +; Dismiss Safeguard if it's already active ld a, [PlayerScreens] bit SCREENS_SAFEGUARD, a jr z, .checkmove @@ -65,7 +70,9 @@ AIScoring_RedStatus: ; 38591 AIScoring_RedStatMods: ; 385e0 -; Use stat-modifying moves on turn 1. +; 50% chance to greatly encourage stat-up moves during enemy's first turn +; 50% chance to greatly encourage stat-down moves during player's first turn +; Almost 90% chance to greatly discourage stat-modifying moves otherwise ld hl, Buffer1 - 1 ld de, EnemyMonMoves @@ -699,7 +706,7 @@ AIScoring_AlwaysHit: ; 38947 ; ...or player's evasion level has been rasied three or more stages ld a, [PlayerEvaLevel] cp $a - ret c + ret c .asm_38954 call Function39521 @@ -929,6 +936,7 @@ AIScoring_LeechSeed: ; 38a4e AIScoring_LightScreen: AIScoring_Reflect: ; 38a54 ; Over 90% chance to discourage this move unless enemy's HP is full + call AICheckEnemyMaxHP ret c call Random @@ -2956,7 +2964,9 @@ AIScoring_Opportunist: ; 39315 AIScoring_Aggressive: ; 39369 -; Use whatever does the most damage. +; Discourage all damaging moves but the one that does the most damage. +; If no damaging move deals damage to the player (immune), +; no move will be discouraged ; Figure out which attack does the most damage and put it in c. ld hl, EnemyMonMoves @@ -2984,6 +2994,7 @@ AIScoring_Aggressive: ; 39369 pop de pop hl +; Update current move if damage is highest so far ld a, [CurDamage + 1] cp e ld a, [CurDamage] @@ -3019,6 +3030,7 @@ AIScoring_Aggressive: ; 39369 cp EnemyMonMovesEnd - EnemyMonMoves + 1 jr z, .done +; Ignore this move if it is the highest damaging one cp c ld a, [de] inc de @@ -3027,15 +3039,17 @@ AIScoring_Aggressive: ; 39369 call AIGetEnemyMove +; Ignore this move if its power is 0 or 1 ld a, [wEnemyMoveStruct + MOVE_POWER] cp 2 jr c, .checkmove2 +; Ignore this move if it is reckless push hl push de push bc ld a, [wEnemyMoveStruct + MOVE_EFFECT] - ld hl, .aggressivemoves + ld hl, .recklessmoves ld de, 1 call IsInArray pop bc @@ -3043,13 +3057,14 @@ AIScoring_Aggressive: ; 39369 pop hl jr c, .checkmove2 +; If we made it this far, discourage this move inc [hl] jr .checkmove2 .done ret -.aggressivemoves +.recklessmoves db EFFECT_EXPLOSION db EFFECT_RAMPAGE db EFFECT_MULTI_HIT @@ -3293,6 +3308,8 @@ AIDiscourageMove: ; 39503 AIGetEnemyMove: ; 39508 +; Load attributes of move a into ram + push hl push de push bc diff --git a/main.asm b/main.asm index 846f75378..98db595d9 100644 --- a/main.asm +++ b/main.asm @@ -41681,11 +41681,13 @@ TrainerClassNames:: ; 2c1ef -Function2c41a: ; 2c41a (b:441a) -; More move AI. +AISpecialEffects: ; 2c41a (b:441a) +; Specific AI for certain move effects +; 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, Unknown_2c42c + ld hl, SpecialEffectMoves call IsInArray jp nc, Function2c545 inc hl @@ -41695,7 +41697,7 @@ Function2c41a: ; 2c41a (b:441a) jp [hl] ; 2c42c (b:442c) -Unknown_2c42c: ; 2c42c +SpecialEffectMoves: ; 2c42c dbw EFFECT_DREAM_EATER, Function2c524 dbw EFFECT_HEAL, Function2c539 dbw EFFECT_LIGHT_SCREEN, Function2c487 @@ -47148,7 +47150,7 @@ AIChooseMove: ; 440ce ld a, c cp 16 ; up to 16 scoring layers - jr z, .asm_4415e + jr z, .DecrementScores push bc ld d, $e ; BANK(TrainerAI) @@ -47177,27 +47179,36 @@ AIChooseMove: ; 440ce jr .CheckLayer -.asm_4415e +; Decrement the scores of all moves one by one until one reaches 0 +; If the Pokemon has no moves, the game will loop indefinitely +.DecrementScores ld hl, Buffer1 ld de, EnemyMonMoves ld c, EnemyMonMovesEnd - EnemyMonMoves -.asm_44166 + +.DecrementNextScore ld a, [de] inc de and a - jr z, .asm_4415e + jr z, .DecrementScores + ; We are done whenever a score reaches 0 dec [hl] - jr z, .asm_44174 + jr z, .PickLowestScoreMoves + ; If we just decremented the fourth move's score, go back to the first move inc hl dec c - jr z, .asm_4415e + jr z, .DecrementScores - jr .asm_44166 + jr .DecrementNextScore -.asm_44174 +; 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 + .asm_44175 inc [hl] dec hl @@ -47208,11 +47219,15 @@ AIChooseMove: ; 440ce ld hl, Buffer1 ld de, EnemyMonMoves ld c, NUM_MOVES + +; Give a score of 0 to a blank move .asm_44184 ld a, [de] and a jr nz, .asm_44189 - ld [hl], a + ld [hl], a + +; Disregard the move if its score is not 1 .asm_44189 ld a, [hl] dec a @@ -47220,6 +47235,7 @@ AIChooseMove: ; 440ce xor a ld [hli], a jr .asm_44193 + .asm_44191 ld a, [de] ld [hli], a @@ -47228,7 +47244,8 @@ AIChooseMove: ; 440ce dec c jr nz, .asm_44184 -.asm_44197 +; Randomly choose one of the moves with a score of 1 +.ChooseMove ld hl, Buffer1 call Random and 3 @@ -47237,7 +47254,7 @@ AIChooseMove: ; 440ce add hl, bc ld a, [hl] and a - jr z, .asm_44197 + jr z, .ChooseMove ld [CurEnemyMove], a ld a, c