Port Reflect and Light Screen
BattleCommand_Screen and HandleScreens run natively. Each side owns independent five-turn counters that include the setup turn, do not refresh on repeated use and survive enemy send-outs. Screens expire in the original non-Link order with ROM messages; the existing damage path already doubles the right defense and keeps the critical/stage rule. This closes the only refusal found by the Route 30 stress run (Chikorita's Reflect). The NPC movement test now reads the ROM in beforeAll, so it skips cleanly without one. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
1 parent
e00dfab42e
commit
7c1c0cc45b
8 files changed
+282
-5
No files matched your search
@@ -61,6 +61,8 @@ class Side {
|
||||
wrapCount = 0;
|
||||
damageTaken = 0;
|
||||
screens = 0;
|
||||
lightScreenCount = 0;
|
||||
reflectCount = 0;
|
||||
turnsTaken = 0;
|
||||
charging = 0;
|
||||
lastMove = 0;
|
||||
@@ -1208,6 +1210,8 @@ export class Battle {
|
||||
return this.user.set(SUB4, C.SUBSTATUS_RAGE);
|
||||
case COMMAND.ragedamage:
|
||||
return this.rageDamage();
|
||||
case COMMAND.screen:
|
||||
return yield* this.screen();
|
||||
case COMMAND.curl:
|
||||
return this.user.set(SUB2, C.SUBSTATUS_CURLED);
|
||||
case COMMAND.doubleflyingdamage:
|
||||
@@ -1302,6 +1306,41 @@ export class Battle {
|
||||
if (this.random() < this.data.criticalChance(stage)) this.criticalHit = 1;
|
||||
}
|
||||
|
||||
/** BattleCommand_Screen: a repeated screen fails without renewing it. */
|
||||
private *screen(): Gen {
|
||||
const side = this.user;
|
||||
const light = side.move.effect === C.EFFECT_LIGHT_SCREEN;
|
||||
const bit = 1 << (light ? C.SCREENS_LIGHT_SCREEN : C.SCREENS_REFLECT);
|
||||
if (side.screens & bit) return yield* this.say(BATTLE.ButItFailedText);
|
||||
side.screens |= bit;
|
||||
if (light) side.lightScreenCount = 5;
|
||||
else side.reflectCount = 5;
|
||||
yield* this.say(light ? BATTLE.LightScreenEffectText : BATTLE.ReflectEffectText);
|
||||
}
|
||||
|
||||
/** HandleScreens, in non-Link order. Screens belong to the side and
|
||||
* survive a send-out; their setup turn is the first of five ticks. */
|
||||
private *handleScreens(): Gen {
|
||||
for (const [turn, side] of [this.player, this.enemy].entries()) {
|
||||
this.turn = turn;
|
||||
this.strings[RAM.stringBuffer1] = turn === 0 ? "Your" : "Enemy";
|
||||
if (side.screens & (1 << C.SCREENS_LIGHT_SCREEN)) {
|
||||
side.lightScreenCount = (side.lightScreenCount - 1) & 0xff;
|
||||
if (!side.lightScreenCount) {
|
||||
side.screens &= ~(1 << C.SCREENS_LIGHT_SCREEN);
|
||||
yield* this.say(BATTLE.BattleText_MonsLightScreenFell);
|
||||
}
|
||||
}
|
||||
if (side.screens & (1 << C.SCREENS_REFLECT)) {
|
||||
side.reflectCount = (side.reflectCount - 1) & 0xff;
|
||||
if (!side.reflectCount) {
|
||||
side.screens &= ~(1 << C.SCREENS_REFLECT);
|
||||
yield* this.say(BATTLE.BattleText_MonsReflectFaded);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private damageArgs = { power: 0, level: 0, attack: 0, defense: 0 };
|
||||
/** PlayerAttackDamage / EnemyAttackDamage. */
|
||||
private damageStats() {
|
||||
@@ -1968,7 +2007,8 @@ export class Battle {
|
||||
for (const side of [this.player, this.enemy]) {
|
||||
if (side.wrapCount || side.has(SUB1, C.SUBSTATUS_PERISH) || side.has(SUB5, C.SUBSTATUS_ENCORED))
|
||||
throw new UnsupportedBattle("Wrap, Perish Song and Encore are not ported yet.");
|
||||
if (side.screens) throw new UnsupportedBattle("Screens and Safeguard are not ported yet.");
|
||||
if (side.screens & ~((1 << C.SCREENS_LIGHT_SCREEN) | (1 << C.SCREENS_REFLECT)))
|
||||
throw new UnsupportedBattle("Other screen effects are not ported yet.");
|
||||
const effect = this.data.heldEffect(side.mon.item).effect;
|
||||
if (effect === C.HELD_RESTORE_PP || (effect >= C.HELD_ATTACK_UP && effect <= C.HELD_EVASION_UP))
|
||||
throw new UnsupportedBattle("Mystery Berry and stat-raising held items are not ported yet.");
|
||||
@@ -1995,6 +2035,7 @@ export class Battle {
|
||||
yield this.hud();
|
||||
yield* this.say(BATTLE.DefrostedOpponentText);
|
||||
}
|
||||
yield* this.handleScreens();
|
||||
// HandleHealingItems: turn 0 handles the enemy's items, then turn 1.
|
||||
for (const turn of [0, 1]) {
|
||||
this.turn = turn;
|
||||
|
||||
Reference in new issue
Block a user