Translate Opportunist, Aggressive and Risky trainer AI
Add the AI_Opportunist, AI_Aggressive and AI_Risky scoring layers with the ROM's stall, reckless and risky tables, HP thresholds, last-wins ties and strict knockout test. AIDamageCalc is ported as a pre-variation damage estimate, including a critical-hit flag left over from an earlier move, which the original also reads. Smart remains the only refused layer, so the rival stays gated. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
3e92936cb9
commit
0d1bedb3b1
10 files changed
+294
-11
No files matched your search
@@ -8,6 +8,7 @@ import {
|
||||
type BattleInput,
|
||||
type BattleOptions,
|
||||
} from "../src/game/battle/battle";
|
||||
import { C } from "../src/game/battle/constants";
|
||||
import { generatePartyMon } from "../src/game/party/pokemon";
|
||||
import { defaultTextContext } from "../src/game/scripts/text";
|
||||
|
||||
@@ -119,6 +120,40 @@ describe.skipIf(!existsSync(romPath))("single-Pokémon trainer battles", () => {
|
||||
run.next({ action: "fight", move: 0 });
|
||||
expect(battle.enemy.curMove).toBe(165);
|
||||
});
|
||||
it("uses the native damage estimate for Aggressive move scoring", () => {
|
||||
const { options } = setup();
|
||||
const enemy = options.trainer!.party[0];
|
||||
enemy.moves = [33, 98, 0, 0]; // Tackle, Quick Attack
|
||||
enemy.pp = enemy.moves.map((move) => move ? assets.pokemon.move(move).pp : 0);
|
||||
options.trainer!.aiFlags = 1 << 6;
|
||||
const battle = new Battle(options), run = battle.run();
|
||||
for (let guard = 0; guard < 20; guard++) {
|
||||
const next = run.next();
|
||||
if (!next.done && next.value.kind === "action") break;
|
||||
}
|
||||
expect(battle.enemy.curMove).toBe(98);
|
||||
});
|
||||
it("lets a leftover critical-hit flag alter the AI damage estimate", () => {
|
||||
const { options } = setup();
|
||||
const battle = new Battle(options), run = battle.run();
|
||||
for (let guard = 0; guard < 20; guard++) {
|
||||
const next = run.next();
|
||||
if (!next.done && next.value.kind === "action") break;
|
||||
}
|
||||
const internals = battle as unknown as {
|
||||
criticalHit: number;
|
||||
enemyAIDamage(move: number): number;
|
||||
};
|
||||
const plain = internals.enemyAIDamage(33); // Tackle
|
||||
battle.player.screens = 1 << C.SCREENS_REFLECT;
|
||||
const screened = internals.enemyAIDamage(33);
|
||||
// CheckDamageStatsCritical: equal stages select unboosted stats, which
|
||||
// ignore Reflect; DamageCalc then doubles the estimate.
|
||||
internals.criticalHit = 1;
|
||||
const critical = internals.enemyAIDamage(33);
|
||||
// Rattata L4 (Atk 10) Tackle into Cyndaquil (Def 18, 36 under Reflect).
|
||||
expect([plain, screened, critical]).toEqual([4, 3, 6]);
|
||||
});
|
||||
it("loses without receiving prize money", () => {
|
||||
const { mon, options } = setup(2);
|
||||
mon.hp = 1;
|
||||
|
||||
Reference in new issue
Block a user