Port the Johto badge stat boosts (#31)
BadgeStatBoosts runs where the original calls it: when a Pokémon is sent out, after an in-battle level-up, and in CalcPlayerStats. It keeps the PlainBadge/MineralBadge swap and the Glacier Badge's Special Defense register bug. A transformed Pokémon that levels up now keeps its copied stats. The reference emulator compares all 256 badge bytes over 28 stat sets per browser against the unmodified routine. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
8fae1f0c0f
commit
ec3d1cd5c4
3 files changed
+112
-4
No files matched your search
@@ -908,7 +908,37 @@ export class Battle {
|
||||
};
|
||||
this.player.stats = mon.stats.slice(1);
|
||||
this.applyStatusEffects(this.player);
|
||||
// No badges exist yet, so BadgeStatBoosts changes nothing.
|
||||
this.badgeStatBoosts();
|
||||
}
|
||||
/** BadgeStatBoosts: Johto badges raise the battle Pokémon's Attack,
|
||||
* Defense, Speed and Special Attack by an eighth (to at most 999). The
|
||||
* PlainBadge and MineralBadge bits are swapped first. The Glacier Badge's
|
||||
* Special Defense boost tests the register that BoostStat leaves after
|
||||
* Special Attack, so it depends on that stat (a kept bug). */
|
||||
private badgeStatBoosts() {
|
||||
const badges = this.options.johtoBadges ?? 0;
|
||||
// ZEPHYR, HIVE, FOG, STORM, GLACIER and RISING stay; PLAIN (bit 2)
|
||||
// moves to bit 4 and MINERAL (bit 4) to bit 2.
|
||||
let b = (badges & 0xeb) | ((badges & 0x04) << 2) | ((badges & 0x10) >> 2);
|
||||
const stats = this.player.mon.stats;
|
||||
// BoostStat, returning the A register it leaves.
|
||||
const boost = (index: number) => {
|
||||
const value = stats[index] + (stats[index] >> 3);
|
||||
if (value >= C.MAX_STAT_VALUE) {
|
||||
stats[index] = C.MAX_STAT_VALUE;
|
||||
return C.MAX_STAT_VALUE & 0xff;
|
||||
}
|
||||
stats[index] = value;
|
||||
return ((value >> 8) - (C.MAX_STAT_VALUE >> 8) - ((value & 0xff) < (C.MAX_STAT_VALUE & 0xff) ? 1 : 0)) & 0xff;
|
||||
};
|
||||
let a = 0;
|
||||
for (let index = 0; index < 4; index++) {
|
||||
a = b;
|
||||
const carry = b & 1;
|
||||
b >>= 2;
|
||||
if (carry) a = boost(index);
|
||||
}
|
||||
if (a & 1) boost(4);
|
||||
}
|
||||
/** ApplyPrzEffectOnSpeed and ApplyBrnEffectOnAttack for one side. */
|
||||
private applyStatusEffects(side: Side) {
|
||||
@@ -1599,6 +1629,7 @@ export class Battle {
|
||||
const value = Math.floor((player.stats[stat] * numerator) / denominator);
|
||||
player.mon.stats[stat] = value === 0 ? 1 : Math.min(999, value);
|
||||
}
|
||||
this.badgeStatBoosts();
|
||||
this.applyStatusEffects(this.turn === 0 ? this.player : this.enemy);
|
||||
}
|
||||
/** PrintPartyMenuActionText with the Pokémon's nickname. */
|
||||
@@ -4985,13 +5016,15 @@ export class Battle {
|
||||
}
|
||||
this.failedMessage = 0;
|
||||
}
|
||||
/** CalcPlayerStats / CalcEnemyStats: stages, then status penalties. */
|
||||
/** CalcPlayerStats / CalcEnemyStats: stages, the player's badge
|
||||
* boosts, then status penalties. */
|
||||
private calcStats(side: Side) {
|
||||
for (let stat = 0; stat < 5; stat++) {
|
||||
const [numerator, denominator] = this.data.statMultiplier(side.levels[stat]);
|
||||
const value = Math.floor((side.stats[stat] * numerator) / denominator);
|
||||
side.mon.stats[stat] = value === 0 ? 1 : Math.min(999, value);
|
||||
}
|
||||
if (side === this.player) this.badgeStatBoosts();
|
||||
this.applyStatusEffects(side);
|
||||
}
|
||||
private statName() {
|
||||
@@ -6019,8 +6052,13 @@ export class Battle {
|
||||
side.mon.hp = mon.hp;
|
||||
side.mon.maxHp = mon.stats[0];
|
||||
side.mon.level = newLevel;
|
||||
side.stats = mon.stats.slice(1);
|
||||
this.calcStats(side);
|
||||
// A transformed Pokémon keeps its copied stats.
|
||||
if (!side.has(SUB5, C.SUBSTATUS_TRANSFORMED)) side.stats = mon.stats.slice(1);
|
||||
// ApplyStatLevelMultiplierOnAllStats, ApplyStatusEffectOnPlayerStats,
|
||||
// then BadgeStatBoosts.
|
||||
this.applyStatLevelMultipliers(side);
|
||||
this.applyStatusEffects(side);
|
||||
this.badgeStatBoosts();
|
||||
}
|
||||
// LevelUpHappinessMod: more where it was caught.
|
||||
this.changeHappiness(
|
||||
|
||||
@@ -249,6 +249,7 @@ export const C = {
|
||||
PP_MASK: 63,
|
||||
NUM_MOVES: 4,
|
||||
BASE_HAPPINESS: 70,
|
||||
MAX_STAT_VALUE: 999,
|
||||
START_MONEY: 3000,
|
||||
MAX_MONEY: 999999,
|
||||
MAX_COINS: 9999,
|
||||
|
||||
Reference in new issue
Block a user