Port the Smart AI effect handlers (#28)

Translate every AI_Smart_EffectHandlers entry, so trainers with the Smart layer are no longer refused. The AI's type checks keep the CheckTypeMatchup bug: they read the move type and Foresight flag through hBattleTurn, and Conversion2's write to the player's move type persists.

The reference emulator runs the unmodified AI_Smart routine over 6,024 states per browser, comparing scores, Random consumption, hBattleTurn and the player's move type. Validated with 579 unit tests, 151 browser tests, native/reference builds and reference/vendor/release checks. Kurt's balls, Park and Safari balls and special battle types remain on #28.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Sébastien QUEROLandClaude Opus 5.5 committed 2026-10-03 15:30:32 +02:00
1 parent 32d736ecae
commit 77704fe848
11 files changed
+682 -71

No files matched your search

+20 -6
View File
@@ -1036,11 +1036,23 @@ export class Battle {
player: this.player, enemy: this.enemy,
data: this.options.data, battleData: this.data, weather: this.weather,
random: () => this.random(),
turn: this.turn ? 1 : 0,
damage: (move) => this.enemyAIDamage(move),
playerUsedMoves: this.playerUsedMoves,
playerHasReserve: this.options.party.some((mon, i) => i !== this.curBattleMon && mon.hp > 0),
enemyHasReserve: this.options.trainer.party.some((mon, i) => i !== this.curOTMon && mon.hp > 0),
enemyParty: this.options.trainer.party,
switchScore: (type, identified) => this.checkPlayerMoveTypeMatchups(type, identified),
conversion2Type: this.reader.byte(SYMBOLS.moves + 255 * 7 + 3),
genders: [
this.options.data.gender(this.partyMon.species, this.partyMon.dvs),
this.options.data.gender(this.enemySpecies, this.enemy.has(SUB5, C.SUBSTATUS_TRANSFORMED) ? this.enemy.backupDvs : this.enemy.mon.dvs),
].map((gender) => gender === undefined ? -1 : gender === "male" ? 1 : 0) as [number, number],
});
this.enemy.moveNum = choice.index;
this.enemy.curMove = choice.move;
if (choice.enemyTurn) this.turn = 1;
else if (choice.playerTurn) this.turn = 0;
}
if (!this.checkPlayerLockedIn()) {
for (;;) {
@@ -5578,12 +5590,14 @@ export class Battle {
return e === 2 ? score + 0x10 : 0;
}
/** CheckPlayerMoveTypeMatchups: wEnemyAISwitchScore from BASE_AI_SWITCH_SCORE
* (10); lower means the enemy is worse off. Matchups use the enemy's move
* type (see checkAbleToSwitch). */
private checkPlayerMoveTypeMatchups(): number {
const data = this.options.data,
identified = this.player.has(SUB1, C.SUBSTATUS_IDENTIFIED),
type = this.enemy.move.type;
* (10); lower means the enemy is worse off. CheckTypeMatchup reads the move
* type and Foresight flag through hBattleTurn; switching runs on the
* enemy's turn (see checkAbleToSwitch), Smart AI on either. */
private checkPlayerMoveTypeMatchups(
type = this.enemy.move.type,
identified = this.player.has(SUB1, C.SUBSTATUS_IDENTIFIED),
): number {
const data = this.options.data;
const vsEnemy = () => this.typeMatchup(type, this.enemy.mon.types, identified),
vsPlayer = () => this.typeMatchup(type, this.player.mon.types, identified);
let score = 10;