Hotfix 1.9.4 to Main

Hotfix 1.9.4 to main
This commit is contained in:
damocleas 2025-05-18 10:17:32 -04:00 committed by GitHub
commit 10fced4f03
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
23 changed files with 652 additions and 275 deletions

4
package-lock.json generated
View File

@ -1,12 +1,12 @@
{ {
"name": "pokemon-rogue-battle", "name": "pokemon-rogue-battle",
"version": "1.9.3", "version": "1.9.4",
"lockfileVersion": 3, "lockfileVersion": 3,
"requires": true, "requires": true,
"packages": { "packages": {
"": { "": {
"name": "pokemon-rogue-battle", "name": "pokemon-rogue-battle",
"version": "1.9.3", "version": "1.9.4",
"hasInstallScript": true, "hasInstallScript": true,
"dependencies": { "dependencies": {
"@material/material-color-utilities": "^0.2.7", "@material/material-color-utilities": "^0.2.7",

View File

@ -1,7 +1,7 @@
{ {
"name": "pokemon-rogue-battle", "name": "pokemon-rogue-battle",
"private": true, "private": true,
"version": "1.9.3", "version": "1.9.4",
"type": "module", "type": "module",
"scripts": { "scripts": {
"start": "vite", "start": "vite",

@ -1 +1 @@
Subproject commit ee6bb371afefe4c6d872cc7765f0e0d26e630d4e Subproject commit a074ddb28c3f0c7e9bbd0560efa33e3ce5f71bcd

View File

@ -850,14 +850,14 @@ export class PostDefendContactApplyStatusEffectAbAttr extends PostDefendAbAttr {
} }
override canApplyPostDefend(pokemon: Pokemon, passive: boolean, simulated: boolean, attacker: Pokemon, move: Move, hitResult: HitResult | null, args: any[]): boolean { override canApplyPostDefend(pokemon: Pokemon, passive: boolean, simulated: boolean, attacker: Pokemon, move: Move, hitResult: HitResult | null, args: any[]): boolean {
const effect = this.effects.length === 1 ? this.effects[0] : this.effects[pokemon.randSeedInt(this.effects.length)]; const effect = this.effects.length === 1 ? this.effects[0] : this.effects[pokemon.randBattleSeedInt(this.effects.length)];
return move.doesFlagEffectApply({flag: MoveFlags.MAKES_CONTACT, user: attacker, target: pokemon}) && !attacker.status return move.doesFlagEffectApply({flag: MoveFlags.MAKES_CONTACT, user: attacker, target: pokemon}) && !attacker.status
&& (this.chance === -1 || pokemon.randSeedInt(100) < this.chance) && (this.chance === -1 || pokemon.randBattleSeedInt(100) < this.chance)
&& attacker.canSetStatus(effect, true, false, pokemon); && attacker.canSetStatus(effect, true, false, pokemon);
} }
override applyPostDefend(pokemon: Pokemon, _passive: boolean, simulated: boolean, attacker: Pokemon, move: Move, _hitResult: HitResult, _args: any[]): void { override applyPostDefend(pokemon: Pokemon, _passive: boolean, simulated: boolean, attacker: Pokemon, move: Move, _hitResult: HitResult, _args: any[]): void {
const effect = this.effects.length === 1 ? this.effects[0] : this.effects[pokemon.randSeedInt(this.effects.length)]; const effect = this.effects.length === 1 ? this.effects[0] : this.effects[pokemon.randBattleSeedInt(this.effects.length)];
attacker.trySetStatus(effect, true, pokemon); attacker.trySetStatus(effect, true, pokemon);
} }
} }
@ -891,7 +891,7 @@ export class PostDefendContactApplyTagChanceAbAttr extends PostDefendAbAttr {
} }
override canApplyPostDefend(pokemon: Pokemon, passive: boolean, simulated: boolean, attacker: Pokemon, move: Move, hitResult: HitResult | null, args: any[]): boolean { override canApplyPostDefend(pokemon: Pokemon, passive: boolean, simulated: boolean, attacker: Pokemon, move: Move, hitResult: HitResult | null, args: any[]): boolean {
return move.doesFlagEffectApply({flag: MoveFlags.MAKES_CONTACT, user: attacker, target: pokemon}) && pokemon.randSeedInt(100) < this.chance return move.doesFlagEffectApply({flag: MoveFlags.MAKES_CONTACT, user: attacker, target: pokemon}) && pokemon.randBattleSeedInt(100) < this.chance
&& attacker.canAddTag(this.tagType); && attacker.canAddTag(this.tagType);
} }
@ -1068,7 +1068,7 @@ export class PostDefendMoveDisableAbAttr extends PostDefendAbAttr {
override canApplyPostDefend(pokemon: Pokemon, passive: boolean, simulated: boolean, attacker: Pokemon, move: Move, hitResult: HitResult | null, args: any[]): boolean { override canApplyPostDefend(pokemon: Pokemon, passive: boolean, simulated: boolean, attacker: Pokemon, move: Move, hitResult: HitResult | null, args: any[]): boolean {
return attacker.getTag(BattlerTagType.DISABLED) === null return attacker.getTag(BattlerTagType.DISABLED) === null
&& move.doesFlagEffectApply({flag: MoveFlags.MAKES_CONTACT, user: attacker, target: pokemon}) && (this.chance === -1 || pokemon.randSeedInt(100) < this.chance); && move.doesFlagEffectApply({flag: MoveFlags.MAKES_CONTACT, user: attacker, target: pokemon}) && (this.chance === -1 || pokemon.randBattleSeedInt(100) < this.chance);
} }
override applyPostDefend(pokemon: Pokemon, _passive: boolean, simulated: boolean, attacker: Pokemon, move: Move, _hitResult: HitResult, _args: any[]): void { override applyPostDefend(pokemon: Pokemon, _passive: boolean, simulated: boolean, attacker: Pokemon, move: Move, _hitResult: HitResult, _args: any[]): void {
@ -1741,7 +1741,7 @@ export class PostAttackStealHeldItemAbAttr extends PostAttackAbAttr {
const heldItems = this.getTargetHeldItems(defender).filter((i) => i.isTransferable); const heldItems = this.getTargetHeldItems(defender).filter((i) => i.isTransferable);
if (heldItems.length) { if (heldItems.length) {
// Ensure that the stolen item in testing is the same as when the effect is applied // Ensure that the stolen item in testing is the same as when the effect is applied
this.stolenItem = heldItems[pokemon.randSeedInt(heldItems.length)]; this.stolenItem = heldItems[pokemon.randBattleSeedInt(heldItems.length)];
if (globalScene.canTransferHeldItemModifier(this.stolenItem, pokemon)) { if (globalScene.canTransferHeldItemModifier(this.stolenItem, pokemon)) {
return true; return true;
} }
@ -1762,7 +1762,7 @@ export class PostAttackStealHeldItemAbAttr extends PostAttackAbAttr {
): void { ): void {
const heldItems = this.getTargetHeldItems(defender).filter((i) => i.isTransferable); const heldItems = this.getTargetHeldItems(defender).filter((i) => i.isTransferable);
if (!this.stolenItem) { if (!this.stolenItem) {
this.stolenItem = heldItems[pokemon.randSeedInt(heldItems.length)]; this.stolenItem = heldItems[pokemon.randBattleSeedInt(heldItems.length)];
} }
if (globalScene.tryTransferHeldItemModifier(this.stolenItem, pokemon, false)) { if (globalScene.tryTransferHeldItemModifier(this.stolenItem, pokemon, false)) {
globalScene.queueMessage( globalScene.queueMessage(
@ -1799,9 +1799,9 @@ export class PostAttackApplyStatusEffectAbAttr extends PostAttackAbAttr {
if ( if (
super.canApplyPostAttack(pokemon, passive, simulated, attacker, move, hitResult, args) super.canApplyPostAttack(pokemon, passive, simulated, attacker, move, hitResult, args)
&& (simulated || !attacker.hasAbilityWithAttr(IgnoreMoveEffectsAbAttr) && pokemon !== attacker && (simulated || !attacker.hasAbilityWithAttr(IgnoreMoveEffectsAbAttr) && pokemon !== attacker
&& (!this.contactRequired || move.doesFlagEffectApply({flag: MoveFlags.MAKES_CONTACT, user: attacker, target: pokemon})) && pokemon.randSeedInt(100) < this.chance && !pokemon.status) && (!this.contactRequired || move.doesFlagEffectApply({flag: MoveFlags.MAKES_CONTACT, user: attacker, target: pokemon})) && pokemon.randBattleSeedInt(100) < this.chance && !pokemon.status)
) { ) {
const effect = this.effects.length === 1 ? this.effects[0] : this.effects[pokemon.randSeedInt(this.effects.length)]; const effect = this.effects.length === 1 ? this.effects[0] : this.effects[pokemon.randBattleSeedInt(this.effects.length)];
return simulated || attacker.canSetStatus(effect, true, false, pokemon); return simulated || attacker.canSetStatus(effect, true, false, pokemon);
} }
@ -1809,7 +1809,7 @@ export class PostAttackApplyStatusEffectAbAttr extends PostAttackAbAttr {
} }
applyPostAttack(pokemon: Pokemon, passive: boolean, simulated: boolean, attacker: Pokemon, move: Move, hitResult: HitResult, args: any[]): void { applyPostAttack(pokemon: Pokemon, passive: boolean, simulated: boolean, attacker: Pokemon, move: Move, hitResult: HitResult, args: any[]): void {
const effect = this.effects.length === 1 ? this.effects[0] : this.effects[pokemon.randSeedInt(this.effects.length)]; const effect = this.effects.length === 1 ? this.effects[0] : this.effects[pokemon.randBattleSeedInt(this.effects.length)];
attacker.trySetStatus(effect, true, pokemon); attacker.trySetStatus(effect, true, pokemon);
} }
} }
@ -1839,12 +1839,12 @@ export class PostAttackApplyBattlerTagAbAttr extends PostAttackAbAttr {
return super.canApplyPostAttack(pokemon, passive, simulated, attacker, move, hitResult, args) && return super.canApplyPostAttack(pokemon, passive, simulated, attacker, move, hitResult, args) &&
!attacker.hasAbilityWithAttr(IgnoreMoveEffectsAbAttr) && pokemon !== attacker && !attacker.hasAbilityWithAttr(IgnoreMoveEffectsAbAttr) && pokemon !== attacker &&
(!this.contactRequired || move.doesFlagEffectApply({flag: MoveFlags.MAKES_CONTACT, user: attacker, target: pokemon})) && (!this.contactRequired || move.doesFlagEffectApply({flag: MoveFlags.MAKES_CONTACT, user: attacker, target: pokemon})) &&
pokemon.randSeedInt(100) < this.chance(attacker, pokemon, move) && !pokemon.status; pokemon.randBattleSeedInt(100) < this.chance(attacker, pokemon, move) && !pokemon.status;
} }
override applyPostAttack(pokemon: Pokemon, passive: boolean, simulated: boolean, attacker: Pokemon, move: Move, hitResult: HitResult, args: any[]): void { override applyPostAttack(pokemon: Pokemon, passive: boolean, simulated: boolean, attacker: Pokemon, move: Move, hitResult: HitResult, args: any[]): void {
if (!simulated) { if (!simulated) {
const effect = this.effects.length === 1 ? this.effects[0] : this.effects[pokemon.randSeedInt(this.effects.length)]; const effect = this.effects.length === 1 ? this.effects[0] : this.effects[pokemon.randBattleSeedInt(this.effects.length)];
attacker.addTag(effect); attacker.addTag(effect);
} }
} }
@ -1868,7 +1868,7 @@ export class PostDefendStealHeldItemAbAttr extends PostDefendAbAttr {
) { ) {
const heldItems = this.getTargetHeldItems(attacker).filter((i) => i.isTransferable); const heldItems = this.getTargetHeldItems(attacker).filter((i) => i.isTransferable);
if (heldItems.length) { if (heldItems.length) {
this.stolenItem = heldItems[pokemon.randSeedInt(heldItems.length)]; this.stolenItem = heldItems[pokemon.randBattleSeedInt(heldItems.length)];
if (globalScene.canTransferHeldItemModifier(this.stolenItem, pokemon)) { if (globalScene.canTransferHeldItemModifier(this.stolenItem, pokemon)) {
return true; return true;
} }
@ -1889,7 +1889,7 @@ export class PostDefendStealHeldItemAbAttr extends PostDefendAbAttr {
const heldItems = this.getTargetHeldItems(attacker).filter((i) => i.isTransferable); const heldItems = this.getTargetHeldItems(attacker).filter((i) => i.isTransferable);
if (!this.stolenItem) { if (!this.stolenItem) {
this.stolenItem = heldItems[pokemon.randSeedInt(heldItems.length)]; this.stolenItem = heldItems[pokemon.randBattleSeedInt(heldItems.length)];
} }
if (globalScene.tryTransferHeldItemModifier(this.stolenItem, pokemon, false)) { if (globalScene.tryTransferHeldItemModifier(this.stolenItem, pokemon, false)) {
globalScene.queueMessage( globalScene.queueMessage(
@ -3171,7 +3171,7 @@ export class ConfusionOnStatusEffectAbAttr extends PostAttackAbAttr {
*/ */
override applyPostAttack(pokemon: Pokemon, passive: boolean, simulated: boolean, defender: Pokemon, move: Move, hitResult: HitResult, args: any[]): void { override applyPostAttack(pokemon: Pokemon, passive: boolean, simulated: boolean, defender: Pokemon, move: Move, hitResult: HitResult, args: any[]): void {
if (!simulated) { if (!simulated) {
defender.addTag(BattlerTagType.CONFUSED, pokemon.randSeedIntRange(2, 5), move.id, defender.id); defender.addTag(BattlerTagType.CONFUSED, pokemon.randBattleSeedIntRange(2, 5), move.id, defender.id);
} }
} }
} }
@ -4235,12 +4235,12 @@ export class MoodyAbAttr extends PostTurnAbAttr {
if (!simulated) { if (!simulated) {
if (canRaise.length > 0) { if (canRaise.length > 0) {
const raisedStat = canRaise[pokemon.randSeedInt(canRaise.length)]; const raisedStat = canRaise[pokemon.randBattleSeedInt(canRaise.length)];
canLower = canRaise.filter(s => s !== raisedStat); canLower = canRaise.filter(s => s !== raisedStat);
globalScene.unshiftPhase(new StatStageChangePhase(pokemon.getBattlerIndex(), true, [ raisedStat ], 2)); globalScene.unshiftPhase(new StatStageChangePhase(pokemon.getBattlerIndex(), true, [ raisedStat ], 2));
} }
if (canLower.length > 0) { if (canLower.length > 0) {
const loweredStat = canLower[pokemon.randSeedInt(canLower.length)]; const loweredStat = canLower[pokemon.randBattleSeedInt(canLower.length)];
globalScene.unshiftPhase(new StatStageChangePhase(pokemon.getBattlerIndex(), true, [ loweredStat ], -1)); globalScene.unshiftPhase(new StatStageChangePhase(pokemon.getBattlerIndex(), true, [ loweredStat ], -1));
} }
} }
@ -5349,7 +5349,7 @@ export class BypassSpeedChanceAbAttr extends AbAttr {
const isCommandFight = turnCommand?.command === Command.FIGHT; const isCommandFight = turnCommand?.command === Command.FIGHT;
const move = turnCommand?.move?.move ? allMoves[turnCommand.move.move] : null; const move = turnCommand?.move?.move ? allMoves[turnCommand.move.move] : null;
const isDamageMove = move?.category === MoveCategory.PHYSICAL || move?.category === MoveCategory.SPECIAL; const isDamageMove = move?.category === MoveCategory.PHYSICAL || move?.category === MoveCategory.SPECIAL;
return !simulated && !bypassSpeed.value && pokemon.randSeedInt(100) < this.chance && isCommandFight && isDamageMove; return !simulated && !bypassSpeed.value && pokemon.randBattleSeedInt(100) < this.chance && isCommandFight && isDamageMove;
} }
/** /**

View File

@ -766,11 +766,11 @@ export class ConfusedTag extends BattlerTag {
globalScene.unshiftPhase(new CommonAnimPhase(pokemon.getBattlerIndex(), undefined, CommonAnim.CONFUSION)); globalScene.unshiftPhase(new CommonAnimPhase(pokemon.getBattlerIndex(), undefined, CommonAnim.CONFUSION));
// 1/3 chance of hitting self with a 40 base power move // 1/3 chance of hitting self with a 40 base power move
if (pokemon.randSeedInt(3) === 0 || Overrides.CONFUSION_ACTIVATION_OVERRIDE === true) { if (pokemon.randBattleSeedInt(3) === 0 || Overrides.CONFUSION_ACTIVATION_OVERRIDE === true) {
const atk = pokemon.getEffectiveStat(Stat.ATK); const atk = pokemon.getEffectiveStat(Stat.ATK);
const def = pokemon.getEffectiveStat(Stat.DEF); const def = pokemon.getEffectiveStat(Stat.DEF);
const damage = toDmgValue( const damage = toDmgValue(
((((2 * pokemon.level) / 5 + 2) * 40 * atk) / def / 50 + 2) * (pokemon.randSeedIntRange(85, 100) / 100), ((((2 * pokemon.level) / 5 + 2) * 40 * atk) / def / 50 + 2) * (pokemon.randBattleSeedIntRange(85, 100) / 100),
); );
// Intentionally don't increment rage fist's hitCount // Intentionally don't increment rage fist's hitCount
globalScene.queueMessage(i18next.t("battlerTags:confusedLapseHurtItself")); globalScene.queueMessage(i18next.t("battlerTags:confusedLapseHurtItself"));
@ -890,7 +890,7 @@ export class InfatuatedTag extends BattlerTag {
); );
globalScene.unshiftPhase(new CommonAnimPhase(pokemon.getBattlerIndex(), undefined, CommonAnim.ATTRACT)); globalScene.unshiftPhase(new CommonAnimPhase(pokemon.getBattlerIndex(), undefined, CommonAnim.ATTRACT));
if (pokemon.randSeedInt(2)) { if (pokemon.randBattleSeedInt(2)) {
globalScene.queueMessage( globalScene.queueMessage(
i18next.t("battlerTags:infatuatedLapseImmobilize", { i18next.t("battlerTags:infatuatedLapseImmobilize", {
pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), pokemonNameWithAffix: getPokemonNameWithAffix(pokemon),
@ -1119,7 +1119,7 @@ export class FrenzyTag extends BattlerTag {
if (this.turnCount < 2) { if (this.turnCount < 2) {
// Only add CONFUSED tag if a disruption occurs on the final confusion-inducing turn of FRENZY // Only add CONFUSED tag if a disruption occurs on the final confusion-inducing turn of FRENZY
pokemon.addTag(BattlerTagType.CONFUSED, pokemon.randSeedIntRange(2, 4)); pokemon.addTag(BattlerTagType.CONFUSED, pokemon.randBattleSeedIntRange(2, 4));
} }
} }
} }

View File

@ -1591,7 +1591,7 @@ export class RandomLevelDamageAttr extends FixedDamageAttr {
} }
getDamage(user: Pokemon, target: Pokemon, move: Move): number { getDamage(user: Pokemon, target: Pokemon, move: Move): number {
return toDmgValue(user.level * (user.randSeedIntRange(50, 150) * 0.01)); return toDmgValue(user.level * (user.randBattleSeedIntRange(50, 150) * 0.01));
} }
} }
@ -2352,7 +2352,7 @@ export class MultiHitAttr extends MoveAttr {
switch (this.multiHitType) { switch (this.multiHitType) {
case MultiHitType._2_TO_5: case MultiHitType._2_TO_5:
{ {
const rand = user.randSeedInt(20); const rand = user.randBattleSeedInt(20);
const hitValue = new NumberHolder(rand); const hitValue = new NumberHolder(rand);
applyAbAttrs(MaxMultiHitAbAttr, user, null, false, hitValue); applyAbAttrs(MaxMultiHitAbAttr, user, null, false, hitValue);
if (hitValue.value >= 13) { if (hitValue.value >= 13) {
@ -2453,7 +2453,7 @@ export class StatusEffectAttr extends MoveEffectAttr {
apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean { apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean {
const moveChance = this.getMoveChance(user, target, move, this.selfTarget, true); const moveChance = this.getMoveChance(user, target, move, this.selfTarget, true);
const statusCheck = moveChance < 0 || moveChance === 100 || user.randSeedInt(100) < moveChance; const statusCheck = moveChance < 0 || moveChance === 100 || user.randBattleSeedInt(100) < moveChance;
const quiet = move.category !== MoveCategory.STATUS; const quiet = move.category !== MoveCategory.STATUS;
if (statusCheck) { if (statusCheck) {
const pokemon = this.selfTarget ? user : target; const pokemon = this.selfTarget ? user : target;
@ -2560,7 +2560,7 @@ export class StealHeldItemChanceAttr extends MoveEffectAttr {
const poolType = target.isPlayer() ? ModifierPoolType.PLAYER : target.hasTrainer() ? ModifierPoolType.TRAINER : ModifierPoolType.WILD; const poolType = target.isPlayer() ? ModifierPoolType.PLAYER : target.hasTrainer() ? ModifierPoolType.TRAINER : ModifierPoolType.WILD;
const highestItemTier = heldItems.map((m) => m.type.getOrInferTier(poolType)).reduce((highestTier, tier) => Math.max(tier!, highestTier), 0); // TODO: is the bang after tier correct? const highestItemTier = heldItems.map((m) => m.type.getOrInferTier(poolType)).reduce((highestTier, tier) => Math.max(tier!, highestTier), 0); // TODO: is the bang after tier correct?
const tierHeldItems = heldItems.filter((m) => m.type.getOrInferTier(poolType) === highestItemTier); const tierHeldItems = heldItems.filter((m) => m.type.getOrInferTier(poolType) === highestItemTier);
const stolenItem = tierHeldItems[user.randSeedInt(tierHeldItems.length)]; const stolenItem = tierHeldItems[user.randBattleSeedInt(tierHeldItems.length)];
if (!globalScene.tryTransferHeldItemModifier(stolenItem, user, false)) { if (!globalScene.tryTransferHeldItemModifier(stolenItem, user, false)) {
return false; return false;
} }
@ -2636,7 +2636,7 @@ export class RemoveHeldItemAttr extends MoveEffectAttr {
return false; return false;
} }
const removedItem = heldItems[user.randSeedInt(heldItems.length)]; const removedItem = heldItems[user.randBattleSeedInt(heldItems.length)];
// Decrease item amount and update icon // Decrease item amount and update icon
target.loseHeldItem(removedItem); target.loseHeldItem(removedItem);
@ -2697,7 +2697,7 @@ export class EatBerryAttr extends MoveEffectAttr {
} }
// pick a random berry to gobble and check if we preserve it // pick a random berry to gobble and check if we preserve it
this.chosenBerry = heldBerries[user.randSeedInt(heldBerries.length)]; this.chosenBerry = heldBerries[user.randBattleSeedInt(heldBerries.length)];
const preserve = new BooleanHolder(false); const preserve = new BooleanHolder(false);
// check for berry pouch preservation // check for berry pouch preservation
globalScene.applyModifiers(PreserveBerryModifier, pokemon.isPlayer(), pokemon, preserve); globalScene.applyModifiers(PreserveBerryModifier, pokemon.isPlayer(), pokemon, preserve);
@ -2774,7 +2774,7 @@ export class StealEatBerryAttr extends EatBerryAttr {
} }
// pick a random berry and eat it // pick a random berry and eat it
this.chosenBerry = heldBerries[user.randSeedInt(heldBerries.length)]; this.chosenBerry = heldBerries[user.randBattleSeedInt(heldBerries.length)];
applyPostItemLostAbAttrs(PostItemLostAbAttr, target, false); applyPostItemLostAbAttrs(PostItemLostAbAttr, target, false);
const message = i18next.t("battle:stealEatBerry", { pokemonName: user.name, targetName: target.name, berryName: this.chosenBerry.type.name }); const message = i18next.t("battle:stealEatBerry", { pokemonName: user.name, targetName: target.name, berryName: this.chosenBerry.type.name });
globalScene.queueMessage(message); globalScene.queueMessage(message);
@ -3205,7 +3205,7 @@ export class StatStageChangeAttr extends MoveEffectAttr {
} }
const moveChance = this.getMoveChance(user, target, move, this.selfTarget, true); const moveChance = this.getMoveChance(user, target, move, this.selfTarget, true);
if (moveChance < 0 || moveChance === 100 || user.randSeedInt(100) < moveChance) { if (moveChance < 0 || moveChance === 100 || user.randBattleSeedInt(100) < moveChance) {
const stages = this.getLevels(user); const stages = this.getLevels(user);
globalScene.unshiftPhase(new StatStageChangePhase((this.selfTarget ? user : target).getBattlerIndex(), this.selfTarget, this.stats, stages, this.showMessage)); globalScene.unshiftPhase(new StatStageChangePhase((this.selfTarget ? user : target).getBattlerIndex(), this.selfTarget, this.stats, stages, this.showMessage));
return true; return true;
@ -3431,7 +3431,7 @@ export class AcupressureStatStageChangeAttr extends MoveEffectAttr {
override apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean { override apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean {
const randStats = BATTLE_STATS.filter((s) => target.getStatStage(s) < 6); const randStats = BATTLE_STATS.filter((s) => target.getStatStage(s) < 6);
if (randStats.length > 0) { if (randStats.length > 0) {
const boostStat = [ randStats[user.randSeedInt(randStats.length)] ]; const boostStat = [ randStats[user.randBattleSeedInt(randStats.length)] ];
globalScene.unshiftPhase(new StatStageChangePhase(target.getBattlerIndex(), this.selfTarget, boostStat, 2)); globalScene.unshiftPhase(new StatStageChangePhase(target.getBattlerIndex(), this.selfTarget, boostStat, 2));
return true; return true;
} }
@ -4823,7 +4823,7 @@ export class ShellSideArmCategoryAttr extends VariableMoveCategoryAttr {
if (predictedPhysDmg > predictedSpecDmg) { if (predictedPhysDmg > predictedSpecDmg) {
category.value = MoveCategory.PHYSICAL; category.value = MoveCategory.PHYSICAL;
return true; return true;
} else if (predictedPhysDmg === predictedSpecDmg && user.randSeedInt(2) === 0) { } else if (predictedPhysDmg === predictedSpecDmg && user.randBattleSeedInt(2) === 0) {
category.value = MoveCategory.PHYSICAL; category.value = MoveCategory.PHYSICAL;
return true; return true;
} }
@ -5404,7 +5404,7 @@ export class FrenzyAttr extends MoveEffectAttr {
} }
if (!user.getTag(BattlerTagType.FRENZY) && !user.getMoveQueue().length) { if (!user.getTag(BattlerTagType.FRENZY) && !user.getMoveQueue().length) {
const turnCount = user.randSeedIntRange(1, 2); const turnCount = user.randBattleSeedIntRange(1, 2);
new Array(turnCount).fill(null).map(() => user.getMoveQueue().push({ move: move.id, targets: [ target.getBattlerIndex() ], ignorePP: true })); new Array(turnCount).fill(null).map(() => user.getMoveQueue().push({ move: move.id, targets: [ target.getBattlerIndex() ], ignorePP: true }));
user.addTag(BattlerTagType.FRENZY, turnCount, move.id, user.id); user.addTag(BattlerTagType.FRENZY, turnCount, move.id, user.id);
} else { } else {
@ -5485,8 +5485,8 @@ export class AddBattlerTagAttr extends MoveEffectAttr {
} }
const moveChance = this.getMoveChance(user, target, move, this.selfTarget, true); const moveChance = this.getMoveChance(user, target, move, this.selfTarget, true);
if (moveChance < 0 || moveChance === 100 || user.randSeedInt(100) < moveChance) { if (moveChance < 0 || moveChance === 100 || user.randBattleSeedInt(100) < moveChance) {
return (this.selfTarget ? user : target).addTag(this.tagType, user.randSeedIntRange(this.turnCountMin, this.turnCountMax), move.id, user.id); return (this.selfTarget ? user : target).addTag(this.tagType, user.randBattleSeedIntRange(this.turnCountMin, this.turnCountMax), move.id, user.id);
} }
return false; return false;
@ -5654,7 +5654,7 @@ export class JawLockAttr extends AddBattlerTagAttr {
} }
const moveChance = this.getMoveChance(user, target, move, this.selfTarget); const moveChance = this.getMoveChance(user, target, move, this.selfTarget);
if (moveChance < 0 || moveChance === 100 || user.randSeedInt(100) < moveChance) { if (moveChance < 0 || moveChance === 100 || user.randBattleSeedInt(100) < moveChance) {
/** /**
* Add the tag to both the user and the target. * Add the tag to both the user and the target.
* The target's tag source is considered to be the user and vice versa * The target's tag source is considered to be the user and vice versa
@ -5792,7 +5792,7 @@ export class ProtectAttr extends AddBattlerTagAttr {
timesUsed++; timesUsed++;
} }
if (timesUsed) { if (timesUsed) {
return !user.randSeedInt(Math.pow(3, timesUsed)); return !user.randBattleSeedInt(Math.pow(3, timesUsed));
} }
return true; return true;
}); });
@ -5916,7 +5916,7 @@ export class AddArenaTagAttr extends MoveEffectAttr {
return false; return false;
} }
if ((move.chance < 0 || move.chance === 100 || user.randSeedInt(100) < move.chance) && user.getLastXMoves(1)[0]?.result === MoveResult.SUCCESS) { if ((move.chance < 0 || move.chance === 100 || user.randBattleSeedInt(100) < move.chance) && user.getLastXMoves(1)[0]?.result === MoveResult.SUCCESS) {
const side = ((this.selfSideTarget ? user : target).isPlayer() !== (move.hasAttr(AddArenaTrapTagAttr) && target === user)) ? ArenaTagSide.PLAYER : ArenaTagSide.ENEMY; const side = ((this.selfSideTarget ? user : target).isPlayer() !== (move.hasAttr(AddArenaTrapTagAttr) && target === user)) ? ArenaTagSide.PLAYER : ArenaTagSide.ENEMY;
globalScene.arena.addTag(this.tagType, this.turnCount, move.id, user.id, side); globalScene.arena.addTag(this.tagType, this.turnCount, move.id, user.id, side);
return true; return true;
@ -5992,7 +5992,7 @@ export class AddArenaTrapTagHitAttr extends AddArenaTagAttr {
const moveChance = this.getMoveChance(user, target, move, this.selfTarget, true); const moveChance = this.getMoveChance(user, target, move, this.selfTarget, true);
const side = (this.selfSideTarget ? user : target).isPlayer() ? ArenaTagSide.PLAYER : ArenaTagSide.ENEMY; const side = (this.selfSideTarget ? user : target).isPlayer() ? ArenaTagSide.PLAYER : ArenaTagSide.ENEMY;
const tag = globalScene.arena.getTagOnSide(this.tagType, side) as ArenaTrapTag; const tag = globalScene.arena.getTagOnSide(this.tagType, side) as ArenaTrapTag;
if ((moveChance < 0 || moveChance === 100 || user.randSeedInt(100) < moveChance) && user.getLastXMoves(1)[0]?.result === MoveResult.SUCCESS) { if ((moveChance < 0 || moveChance === 100 || user.randBattleSeedInt(100) < moveChance) && user.getLastXMoves(1)[0]?.result === MoveResult.SUCCESS) {
globalScene.arena.addTag(this.tagType, 0, move.id, user.id, side); globalScene.arena.addTag(this.tagType, 0, move.id, user.id, side);
if (!tag) { if (!tag) {
return true; return true;
@ -6167,7 +6167,7 @@ export class RevivalBlessingAttr extends MoveEffectAttr {
// If used by an enemy trainer with at least one fainted non-boss Pokemon, this // If used by an enemy trainer with at least one fainted non-boss Pokemon, this
// revives one of said Pokemon selected at random. // revives one of said Pokemon selected at random.
const faintedPokemon = globalScene.getEnemyParty().filter((p) => p.isFainted() && !p.isBoss()); const faintedPokemon = globalScene.getEnemyParty().filter((p) => p.isFainted() && !p.isBoss());
const pokemon = faintedPokemon[user.randSeedInt(faintedPokemon.length)]; const pokemon = faintedPokemon[user.randBattleSeedInt(faintedPokemon.length)];
const slotIndex = globalScene.getEnemyParty().findIndex((p) => pokemon.id === p.id); const slotIndex = globalScene.getEnemyParty().findIndex((p) => pokemon.id === p.id);
pokemon.resetStatus(true, false, false, true); pokemon.resetStatus(true, false, false, true);
pokemon.heal(Math.min(toDmgValue(0.5 * pokemon.getMaxHp()), pokemon.getMaxHp())); pokemon.heal(Math.min(toDmgValue(0.5 * pokemon.getMaxHp()), pokemon.getMaxHp()));
@ -6263,7 +6263,7 @@ export class ForceSwitchOutAttr extends MoveEffectAttr {
if (switchOutTarget.hp > 0) { if (switchOutTarget.hp > 0) {
if (this.switchType === SwitchType.FORCE_SWITCH) { if (this.switchType === SwitchType.FORCE_SWITCH) {
switchOutTarget.leaveField(true); switchOutTarget.leaveField(true);
const slotIndex = eligibleNewIndices[user.randSeedInt(eligibleNewIndices.length)]; const slotIndex = eligibleNewIndices[user.randBattleSeedInt(eligibleNewIndices.length)];
globalScene.prependToPhase( globalScene.prependToPhase(
new SwitchSummonPhase( new SwitchSummonPhase(
this.switchType, this.switchType,
@ -6306,7 +6306,7 @@ export class ForceSwitchOutAttr extends MoveEffectAttr {
if (switchOutTarget.hp > 0) { if (switchOutTarget.hp > 0) {
if (this.switchType === SwitchType.FORCE_SWITCH) { if (this.switchType === SwitchType.FORCE_SWITCH) {
switchOutTarget.leaveField(true); switchOutTarget.leaveField(true);
const slotIndex = eligibleNewIndices[user.randSeedInt(eligibleNewIndices.length)]; const slotIndex = eligibleNewIndices[user.randBattleSeedInt(eligibleNewIndices.length)];
globalScene.prependToPhase( globalScene.prependToPhase(
new SwitchSummonPhase( new SwitchSummonPhase(
this.switchType, this.switchType,
@ -6725,7 +6725,7 @@ class CallMoveAttr extends OverrideMoveEffectAttr {
} }
const targets = moveTargets.multiple || moveTargets.targets.length === 1 const targets = moveTargets.multiple || moveTargets.targets.length === 1
? moveTargets.targets ? moveTargets.targets
: [ this.hasTarget ? target.getBattlerIndex() : moveTargets.targets[user.randSeedInt(moveTargets.targets.length)] ]; // account for Mirror Move having a target already : [ this.hasTarget ? target.getBattlerIndex() : moveTargets.targets[user.randBattleSeedInt(moveTargets.targets.length)] ]; // account for Mirror Move having a target already
user.getMoveQueue().push({ move: move.id, targets: targets, virtual: true, ignorePP: true }); user.getMoveQueue().push({ move: move.id, targets: targets, virtual: true, ignorePP: true });
globalScene.unshiftPhase(new LoadMoveAnimPhase(move.id)); globalScene.unshiftPhase(new LoadMoveAnimPhase(move.id));
globalScene.unshiftPhase(new MovePhase(user, targets, new PokemonMove(move.id, 0, 0, true), true, true)); globalScene.unshiftPhase(new MovePhase(user, targets, new PokemonMove(move.id, 0, 0, true), true, true));
@ -6765,7 +6765,7 @@ export class RandomMoveAttr extends CallMoveAttr {
const moveIds = getEnumValues(Moves).map(m => !this.invalidMoves.has(m) && !allMoves[m].name.endsWith(" (N)") ? m : Moves.NONE); const moveIds = getEnumValues(Moves).map(m => !this.invalidMoves.has(m) && !allMoves[m].name.endsWith(" (N)") ? m : Moves.NONE);
let moveId: Moves = Moves.NONE; let moveId: Moves = Moves.NONE;
do { do {
moveId = this.getMoveOverride() ?? moveIds[user.randSeedInt(moveIds.length)]; moveId = this.getMoveOverride() ?? moveIds[user.randBattleSeedInt(moveIds.length)];
} }
while (moveId === Moves.NONE); while (moveId === Moves.NONE);
return super.apply(user, target, allMoves[moveId], args); return super.apply(user, target, allMoves[moveId], args);
@ -6817,7 +6817,7 @@ export class RandomMovesetMoveAttr extends CallMoveAttr {
return false; return false;
} }
this.moveId = moves[user.randSeedInt(moves.length)]!.moveId; this.moveId = moves[user.randBattleSeedInt(moves.length)]!.moveId;
return true; return true;
}; };
} }
@ -7900,7 +7900,7 @@ const phaseForcedSlower = (phase: MovePhase, target: Pokemon, trickRoom: boolean
let slower: boolean; let slower: boolean;
// quashed pokemon still have speed ties // quashed pokemon still have speed ties
if (phase.pokemon.getEffectiveStat(Stat.SPD) === target.getEffectiveStat(Stat.SPD)) { if (phase.pokemon.getEffectiveStat(Stat.SPD) === target.getEffectiveStat(Stat.SPD)) {
slower = !!target.randSeedInt(2); slower = !!target.randBattleSeedInt(2);
} else { } else {
slower = !trickRoom ? phase.pokemon.getEffectiveStat(Stat.SPD) < target.getEffectiveStat(Stat.SPD) : phase.pokemon.getEffectiveStat(Stat.SPD) > target.getEffectiveStat(Stat.SPD); slower = !trickRoom ? phase.pokemon.getEffectiveStat(Stat.SPD) < target.getEffectiveStat(Stat.SPD) : phase.pokemon.getEffectiveStat(Stat.SPD) > target.getEffectiveStat(Stat.SPD);
} }
@ -8103,7 +8103,7 @@ export class ResistLastMoveTypeAttr extends MoveEffectAttr {
if (!validTypes.length) { if (!validTypes.length) {
return false; return false;
} }
const type = validTypes[user.randSeedInt(validTypes.length)]; const type = validTypes[user.randBattleSeedInt(validTypes.length)];
user.summonData.types = [ type ]; user.summonData.types = [ type ];
globalScene.queueMessage(i18next.t("battle:transformedIntoType", { pokemonName: getPokemonNameWithAffix(user), type: toReadableString(PokemonType[type]) })); globalScene.queueMessage(i18next.t("battle:transformedIntoType", { pokemonName: getPokemonNameWithAffix(user), type: toReadableString(PokemonType[type]) }));
user.updateInfo(); user.updateInfo();
@ -8218,7 +8218,7 @@ export function getMoveTargets(user: Pokemon, move: Moves, replaceTarget?: MoveT
multiple = moveTarget !== MoveTarget.NEAR_ENEMY; multiple = moveTarget !== MoveTarget.NEAR_ENEMY;
break; break;
case MoveTarget.RANDOM_NEAR_ENEMY: case MoveTarget.RANDOM_NEAR_ENEMY:
set = [ opponents[user.randSeedInt(opponents.length)] ]; set = [ opponents[user.randBattleSeedInt(opponents.length)] ];
break; break;
case MoveTarget.ATTACKER: case MoveTarget.ATTACKER:
return { targets: [ -1 as BattlerIndex ], multiple: false }; return { targets: [ -1 as BattlerIndex ], multiple: false };

File diff suppressed because it is too large Load Diff

View File

@ -54,7 +54,7 @@ import {
getStarterValueFriendshipCap, getStarterValueFriendshipCap,
speciesStarterCosts, speciesStarterCosts,
} from "#app/data/balance/starters"; } from "#app/data/balance/starters";
import { NumberHolder, randSeedInt, getIvsFromId, BooleanHolder, randSeedItem, isNullOrUndefined, getEnumValues, toDmgValue, fixedInt, rgbaToInt, rgbHexToRgba, rgbToHsv, deltaRgb, isBetween, type nil, type Constructor } from "#app/utils/common"; import { NumberHolder, randSeedInt, getIvsFromId, BooleanHolder, randSeedItem, isNullOrUndefined, getEnumValues, toDmgValue, fixedInt, rgbaToInt, rgbHexToRgba, rgbToHsv, deltaRgb, isBetween, type nil, type Constructor, randSeedIntRange } from "#app/utils/common";
import type { TypeDamageMultiplier } from "#app/data/type"; import type { TypeDamageMultiplier } from "#app/data/type";
import { getTypeDamageMultiplier, getTypeRgb } from "#app/data/type"; import { getTypeDamageMultiplier, getTypeRgb } from "#app/data/type";
import { PokemonType } from "#enums/pokemon-type"; import { PokemonType } from "#enums/pokemon-type";
@ -4485,7 +4485,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
*/ */
const randomMultiplier = simulated const randomMultiplier = simulated
? 1 ? 1
: this.randSeedIntRange(85, 100) / 100; : this.randBattleSeedIntRange(85, 100) / 100;
/** A damage multiplier for when the attack is of the attacker's type and/or Tera type. */ /** A damage multiplier for when the attack is of the attacker's type and/or Tera type. */
@ -5629,7 +5629,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
let sleepTurnsRemaining: NumberHolder; let sleepTurnsRemaining: NumberHolder;
if (effect === StatusEffect.SLEEP) { if (effect === StatusEffect.SLEEP) {
sleepTurnsRemaining = new NumberHolder(this.randSeedIntRange(2, 4)); sleepTurnsRemaining = new NumberHolder(this.randBattleSeedIntRange(2, 4));
this.setFrameRate(4); this.setFrameRate(4);
@ -5721,17 +5721,10 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
} }
/** /**
* Reset this Pokemon's {@linkcode PokemonSummonData | SummonData} and {@linkcode PokemonTempSummonData | TempSummonData} * Performs miscellaneous setup for when the Pokemon is summoned, like generating the substitute sprite
* in preparation for switching pokemon, as well as removing any relevant on-switch tags. * @param resetSummonData - Whether to additionally reset the Pokemon's summon data (default: `false`)
*/ */
resetSummonData(): void { public fieldSetup(resetSummonData?: boolean): void {
const illusion: IllusionData | null = this.summonData.illusion;
if (this.summonData.speciesForm) {
this.summonData.speciesForm = null;
this.updateFusionPalette();
}
this.summonData = new PokemonSummonData();
this.tempSummonData = new PokemonTempSummonData();
this.setSwitchOutStatus(false); this.setSwitchOutStatus(false);
if (globalScene) { if (globalScene) {
globalScene.triggerPokemonFormChange( globalScene.triggerPokemonFormChange(
@ -5740,7 +5733,6 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
true, true,
); );
} }
// If this Pokemon has a Substitute when loading in, play an animation to add its sprite // If this Pokemon has a Substitute when loading in, play an animation to add its sprite
if (this.getTag(SubstituteTag)) { if (this.getTag(SubstituteTag)) {
globalScene.triggerPokemonBattleAnim( globalScene.triggerPokemonBattleAnim(
@ -5758,6 +5750,24 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
) { ) {
this.setVisible(false); this.setVisible(false);
} }
if (resetSummonData) {
this.resetSummonData();
}
}
/**
* Reset this Pokemon's {@linkcode PokemonSummonData | SummonData} and {@linkcode PokemonTempSummonData | TempSummonData}
* in preparation for switching pokemon, as well as removing any relevant on-switch tags.
*/
resetSummonData(): void {
const illusion: IllusionData | null = this.summonData.illusion;
if (this.summonData.speciesForm) {
this.summonData.speciesForm = null;
this.updateFusionPalette();
}
this.summonData = new PokemonSummonData();
this.tempSummonData = new PokemonTempSummonData();
this.summonData.illusion = illusion this.summonData.illusion = illusion
this.updateInfo(); this.updateInfo();
} }
@ -6282,7 +6292,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
* @param min The minimum integer to pick, default `0` * @param min The minimum integer to pick, default `0`
* @returns A random integer between {@linkcode min} and ({@linkcode min} + {@linkcode range} - 1) * @returns A random integer between {@linkcode min} and ({@linkcode min} + {@linkcode range} - 1)
*/ */
randSeedInt(range: number, min = 0): number { randBattleSeedInt(range: number, min = 0): number {
return globalScene.currentBattle return globalScene.currentBattle
? globalScene.randBattleSeedInt(range, min) ? globalScene.randBattleSeedInt(range, min)
: randSeedInt(range, min); : randSeedInt(range, min);
@ -6294,8 +6304,10 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
* @param max The maximum integer to generate * @param max The maximum integer to generate
* @returns a random integer between {@linkcode min} and {@linkcode max} inclusive * @returns a random integer between {@linkcode min} and {@linkcode max} inclusive
*/ */
randSeedIntRange(min: number, max: number): number { randBattleSeedIntRange(min: number, max: number): number {
return this.randSeedInt(max - min + 1, min); return globalScene.currentBattle
? globalScene.randBattleSeedInt(max - min + 1, min)
: randSeedIntRange(min, max);
} }
/** /**
@ -7133,7 +7145,7 @@ export class EnemyPokemon extends Pokemon {
const { waveIndex } = globalScene.currentBattle; const { waveIndex } = globalScene.currentBattle;
const ivs: number[] = []; const ivs: number[] = [];
while (ivs.length < 6) { while (ivs.length < 6) {
ivs.push(this.randSeedIntRange(Math.floor(waveIndex / 10), 31)); ivs.push(randSeedIntRange(Math.floor(waveIndex / 10), 31));
} }
this.ivs = ivs; this.ivs = ivs;
} }

View File

@ -1548,7 +1548,7 @@ export class SurviveDamageModifier extends PokemonHeldItemModifier {
* @returns `true` if the survive damage has been applied * @returns `true` if the survive damage has been applied
*/ */
override apply(pokemon: Pokemon, surviveDamage: BooleanHolder): boolean { override apply(pokemon: Pokemon, surviveDamage: BooleanHolder): boolean {
if (!surviveDamage.value && pokemon.randSeedInt(10) < this.getStackCount()) { if (!surviveDamage.value && pokemon.randBattleSeedInt(10) < this.getStackCount()) {
surviveDamage.value = true; surviveDamage.value = true;
globalScene.queueMessage( globalScene.queueMessage(
@ -1594,7 +1594,7 @@ export class BypassSpeedChanceModifier extends PokemonHeldItemModifier {
* @returns `true` if {@linkcode BypassSpeedChanceModifier} has been applied * @returns `true` if {@linkcode BypassSpeedChanceModifier} has been applied
*/ */
override apply(pokemon: Pokemon, doBypassSpeed: BooleanHolder): boolean { override apply(pokemon: Pokemon, doBypassSpeed: BooleanHolder): boolean {
if (!doBypassSpeed.value && pokemon.randSeedInt(10) < this.getStackCount()) { if (!doBypassSpeed.value && pokemon.randBattleSeedInt(10) < this.getStackCount()) {
doBypassSpeed.value = true; doBypassSpeed.value = true;
const isCommandFight = const isCommandFight =
globalScene.currentBattle.turnCommands[pokemon.getBattlerIndex()]?.command === Command.FIGHT; globalScene.currentBattle.turnCommands[pokemon.getBattlerIndex()]?.command === Command.FIGHT;
@ -1658,7 +1658,7 @@ export class FlinchChanceModifier extends PokemonHeldItemModifier {
override apply(pokemon: Pokemon, flinched: BooleanHolder): boolean { override apply(pokemon: Pokemon, flinched: BooleanHolder): boolean {
// The check for pokemon.summonData is to ensure that a crash doesn't occur when a Pokemon with King's Rock procs a flinch // The check for pokemon.summonData is to ensure that a crash doesn't occur when a Pokemon with King's Rock procs a flinch
// TODO: Since summonData is always defined now, we can probably remove this // TODO: Since summonData is always defined now, we can probably remove this
if (pokemon.summonData && !flinched.value && pokemon.randSeedInt(100) < this.getStackCount() * this.chance) { if (pokemon.summonData && !flinched.value && pokemon.randBattleSeedInt(100) < this.getStackCount() * this.chance) {
flinched.value = true; flinched.value = true;
return true; return true;
} }
@ -1927,7 +1927,7 @@ export class PreserveBerryModifier extends PersistentModifier {
* @returns always `true` * @returns always `true`
*/ */
override apply(pokemon: Pokemon, doPreserve: BooleanHolder): boolean { override apply(pokemon: Pokemon, doPreserve: BooleanHolder): boolean {
doPreserve.value ||= pokemon.randSeedInt(10) < this.getStackCount() * 3; doPreserve.value ||= pokemon.randBattleSeedInt(10) < this.getStackCount() * 3;
return true; return true;
} }
@ -3240,7 +3240,7 @@ export abstract class HeldItemTransferModifier extends PokemonHeldItemModifier {
return false; return false;
} }
const targetPokemon = opponents[pokemon.randSeedInt(opponents.length)]; const targetPokemon = opponents[pokemon.randBattleSeedInt(opponents.length)];
const transferredItemCount = this.getTransferredItemCount(); const transferredItemCount = this.getTransferredItemCount();
if (!transferredItemCount) { if (!transferredItemCount) {
@ -3272,7 +3272,7 @@ export abstract class HeldItemTransferModifier extends PokemonHeldItemModifier {
break; break;
} }
} }
const randItemIndex = pokemon.randSeedInt(itemModifiers.length); const randItemIndex = pokemon.randBattleSeedInt(itemModifiers.length);
const randItem = itemModifiers[randItemIndex]; const randItem = itemModifiers[randItemIndex];
if (globalScene.tryTransferHeldItemModifier(randItem, pokemon, false)) { if (globalScene.tryTransferHeldItemModifier(randItem, pokemon, false)) {
transferredModifierTypes.push(randItem.type); transferredModifierTypes.push(randItem.type);
@ -3731,7 +3731,7 @@ export class EnemyEndureChanceModifier extends EnemyPersistentModifier {
* @returns `true` if {@linkcode Pokemon} endured * @returns `true` if {@linkcode Pokemon} endured
*/ */
override apply(target: Pokemon): boolean { override apply(target: Pokemon): boolean {
if (target.waveData.endured || target.randSeedInt(100) >= this.chance * this.getStackCount()) { if (target.waveData.endured || target.randBattleSeedInt(100) >= this.chance * this.getStackCount()) {
return false; return false;
} }

View File

@ -63,7 +63,7 @@ export class AttemptCapturePhase extends PokemonPhase {
const modifiedCatchRate = Math.round((((_3m - _2h) * catchRate * pokeballMultiplier) / _3m) * statusMultiplier); const modifiedCatchRate = Math.round((((_3m - _2h) * catchRate * pokeballMultiplier) / _3m) * statusMultiplier);
const shakeProbability = Math.round(65536 / Math.pow(255 / modifiedCatchRate, 0.1875)); // Formula taken from gen 6 const shakeProbability = Math.round(65536 / Math.pow(255 / modifiedCatchRate, 0.1875)); // Formula taken from gen 6
const criticalCaptureChance = getCriticalCaptureChance(modifiedCatchRate); const criticalCaptureChance = getCriticalCaptureChance(modifiedCatchRate);
const isCritical = pokemon.randSeedInt(256) < criticalCaptureChance; const isCritical = pokemon.randBattleSeedInt(256) < criticalCaptureChance;
const fpOffset = pokemon.getFieldPositionOffset(); const fpOffset = pokemon.getFieldPositionOffset();
const pokeballAtlasKey = getPokeballAtlasKey(this.pokeballType); const pokeballAtlasKey = getPokeballAtlasKey(this.pokeballType);
@ -135,14 +135,14 @@ export class AttemptCapturePhase extends PokemonPhase {
pokeballMultiplier === -1 || pokeballMultiplier === -1 ||
isCritical || isCritical ||
modifiedCatchRate >= 255 || modifiedCatchRate >= 255 ||
pokemon.randSeedInt(65536) < shakeProbability pokemon.randBattleSeedInt(65536) < shakeProbability
) { ) {
globalScene.playSound("se/pb_move"); globalScene.playSound("se/pb_move");
} else { } else {
shakeCounter.stop(); shakeCounter.stop();
this.failCatch(shakeCount); this.failCatch(shakeCount);
} }
} else if (isCritical && pokemon.randSeedInt(65536) >= shakeProbability) { } else if (isCritical && pokemon.randBattleSeedInt(65536) >= shakeProbability) {
// Above, perform the one shake check for critical captures after the ball shakes once // Above, perform the one shake check for critical captures after the ball shakes once
shakeCounter.stop(); shakeCounter.stop();
this.failCatch(shakeCount); this.failCatch(shakeCount);

View File

@ -34,7 +34,7 @@ export class AttemptRunPhase extends PokemonPhase {
applyAbAttrs(RunSuccessAbAttr, playerPokemon, null, false, escapeChance); applyAbAttrs(RunSuccessAbAttr, playerPokemon, null, false, escapeChance);
if (playerPokemon.randSeedInt(100) < escapeChance.value && !this.forceFailEscape) { if (playerPokemon.randBattleSeedInt(100) < escapeChance.value && !this.forceFailEscape) {
enemyField.forEach(enemyPokemon => applyPreLeaveFieldAbAttrs(PreLeaveFieldAbAttr, enemyPokemon)); enemyField.forEach(enemyPokemon => applyPreLeaveFieldAbAttrs(PreLeaveFieldAbAttr, enemyPokemon));
globalScene.playSound("se/flee"); globalScene.playSound("se/flee");

View File

@ -146,7 +146,7 @@ export class EncounterPhase extends BattlePhase {
const enemyPokemon = globalScene.getEnemyParty()[e]; const enemyPokemon = globalScene.getEnemyParty()[e];
if (e < (battle.double ? 2 : 1)) { if (e < (battle.double ? 2 : 1)) {
enemyPokemon.setX(-66 + enemyPokemon.getFieldPositionOffset()[0]); enemyPokemon.setX(-66 + enemyPokemon.getFieldPositionOffset()[0]);
enemyPokemon.resetSummonData(); enemyPokemon.fieldSetup(true);
} }
if (!this.loaded) { if (!this.loaded) {

View File

@ -35,20 +35,22 @@ export class MessagePhase extends Phase {
this.text = this.text.split(pokename[p]).join(repname[p]); this.text = this.text.split(pokename[p]).join(repname[p]);
} }
const pageIndex = this.text.indexOf("$"); const pageIndex = this.text.indexOf("$");
for (let p = 0; p < globalScene.getPlayerField().length; p++) {
this.text = this.text.split(repname[p]).join(pokename[p]);
}
if (pageIndex !== -1) { if (pageIndex !== -1) {
let page0 = this.text.slice(0, pageIndex);
let page1 = this.text.slice(pageIndex + 1);
// Pokemon names must be re-inserted _after_ the split, otherwise the index will be wrong
for (let p = 0; p < globalScene.getPlayerField().length; p++) {
page0 = page0.split(repname[p]).join(pokename[p]);
page1 = page1.split(repname[p]).join(pokename[p]);
}
globalScene.unshiftPhase( globalScene.unshiftPhase(
new MessagePhase( new MessagePhase(page1, this.callbackDelay, this.prompt, this.promptDelay, this.speaker),
this.text.slice(pageIndex + 1),
this.callbackDelay,
this.prompt,
this.promptDelay,
this.speaker,
),
); );
this.text = this.text.slice(0, pageIndex).trim(); this.text = page0.trim();
} else {
for (let p = 0; p < globalScene.getPlayerField().length; p++) {
this.text = this.text.split(repname[p]).join(pokename[p]);
}
} }
} }

View File

@ -354,8 +354,8 @@ export class MoveEffectPhase extends PokemonPhase {
move.id as Moves, move.id as Moves,
user, user,
firstTarget?.getBattlerIndex() ?? BattlerIndex.ATTACKER, firstTarget?.getBattlerIndex() ?? BattlerIndex.ATTACKER,
// Field moves and some moves used in mystery encounters should be played even on an empty field // Some moves used in mystery encounters should be played even on an empty field
fieldMove || (globalScene.currentBattle?.mysteryEncounter?.hasBattleAnimationsWithoutTargets ?? false), globalScene.currentBattle?.mysteryEncounter?.hasBattleAnimationsWithoutTargets ?? false,
).play(move.hitsSubstitute(user, firstTarget), () => this.postAnimCallback(user, targets)); ).play(move.hitsSubstitute(user, firstTarget), () => this.postAnimCallback(user, targets));
return; return;
@ -594,7 +594,7 @@ export class MoveEffectPhase extends PokemonPhase {
} }
const accuracyMultiplier = user.getAccuracyMultiplier(target, this.move); const accuracyMultiplier = user.getAccuracyMultiplier(target, this.move);
const rand = user.randSeedInt(100); const rand = user.randBattleSeedInt(100);
if (rand < moveAccuracy * accuracyMultiplier) { if (rand < moveAccuracy * accuracyMultiplier) {
return [HitCheckResult.HIT, effectiveness]; return [HitCheckResult.HIT, effectiveness];

View File

@ -232,7 +232,7 @@ export class MovePhase extends BattlePhase {
switch (this.pokemon.status.effect) { switch (this.pokemon.status.effect) {
case StatusEffect.PARALYSIS: case StatusEffect.PARALYSIS:
activated = activated =
(!this.pokemon.randSeedInt(4) || Overrides.STATUS_ACTIVATION_OVERRIDE === true) && (!this.pokemon.randBattleSeedInt(4) || Overrides.STATUS_ACTIVATION_OVERRIDE === true) &&
Overrides.STATUS_ACTIVATION_OVERRIDE !== false; Overrides.STATUS_ACTIVATION_OVERRIDE !== false;
break; break;
case StatusEffect.SLEEP: { case StatusEffect.SLEEP: {
@ -258,7 +258,7 @@ export class MovePhase extends BattlePhase {
.findAttr( .findAttr(
attr => attr instanceof HealStatusEffectAttr && attr.selfTarget && attr.isOfEffect(StatusEffect.FREEZE), attr => attr instanceof HealStatusEffectAttr && attr.selfTarget && attr.isOfEffect(StatusEffect.FREEZE),
) || ) ||
(!this.pokemon.randSeedInt(5) && Overrides.STATUS_ACTIVATION_OVERRIDE !== true) || (!this.pokemon.randBattleSeedInt(5) && Overrides.STATUS_ACTIVATION_OVERRIDE !== true) ||
Overrides.STATUS_ACTIVATION_OVERRIDE === false; Overrides.STATUS_ACTIVATION_OVERRIDE === false;
activated = !healed; activated = !healed;

View File

@ -196,6 +196,7 @@ export class SummonPhase extends PartyMemberPokemonPhase {
onComplete: () => { onComplete: () => {
pokemon.cry(pokemon.getHpRatio() > 0.25 ? undefined : { rate: 0.85 }); pokemon.cry(pokemon.getHpRatio() > 0.25 ? undefined : { rate: 0.85 });
pokemon.getSprite().clearTint(); pokemon.getSprite().clearTint();
pokemon.fieldSetup();
// necessary to stay transformed during wild waves // necessary to stay transformed during wild waves
if (pokemon.summonData.speciesForm) { if (pokemon.summonData.speciesForm) {
pokemon.loadAssets(false); pokemon.loadAssets(false);
@ -261,6 +262,7 @@ export class SummonPhase extends PartyMemberPokemonPhase {
onComplete: () => { onComplete: () => {
pokemon.cry(pokemon.getHpRatio() > 0.25 ? undefined : { rate: 0.85 }); pokemon.cry(pokemon.getHpRatio() > 0.25 ? undefined : { rate: 0.85 });
pokemon.getSprite().clearTint(); pokemon.getSprite().clearTint();
pokemon.fieldSetup();
globalScene.updateFieldScale(); globalScene.updateFieldScale();
globalScene.time.delayedCall(1000, () => this.end()); globalScene.time.delayedCall(1000, () => this.end());
}, },

View File

@ -193,7 +193,7 @@ export class SwitchSummonPhase extends SummonPhase {
switchedInPokemon.setAlpha(0.5); switchedInPokemon.setAlpha(0.5);
} }
} else { } else {
switchedInPokemon.resetSummonData(); switchedInPokemon.fieldSetup(true);
} }
this.summon(); this.summon();
}; };

View File

@ -99,6 +99,16 @@ export function randSeedInt(range: number, min = 0): number {
return Phaser.Math.RND.integerInRange(min, range - 1 + min); return Phaser.Math.RND.integerInRange(min, range - 1 + min);
} }
/**
* Generates a random number using the global seed
* @param min The minimum integer to generate
* @param max The maximum integer to generate
* @returns a random integer between {@linkcode min} and {@linkcode max} inclusive
*/
export function randSeedIntRange(min: number, max: number): number {
return randSeedInt(max - min + 1, min);
}
/** /**
* Returns a random integer between min and max (non-inclusive) * Returns a random integer between min and max (non-inclusive)
* @param min The lowest number * @param min The lowest number

View File

@ -111,7 +111,7 @@ describe("Abilities - Cud Chew", () => {
it("can store multiple berries across 2 turns with teatime", async () => { it("can store multiple berries across 2 turns with teatime", async () => {
// always eat first berry for stuff cheeks & company // always eat first berry for stuff cheeks & company
vi.spyOn(Pokemon.prototype, "randSeedInt").mockReturnValue(0); vi.spyOn(Pokemon.prototype, "randBattleSeedInt").mockReturnValue(0);
game.override game.override
.startingHeldItems([ .startingHeldItems([
{ name: "BERRY", type: BerryType.PETAYA, count: 3 }, { name: "BERRY", type: BerryType.PETAYA, count: 3 },

View File

@ -139,7 +139,7 @@ describe("Abilities - Desolate Land", () => {
await game.classicMode.startBattle([Species.MAGIKARP]); await game.classicMode.startBattle([Species.MAGIKARP]);
expect(game.scene.arena.weather?.weatherType).toBe(WeatherType.HARSH_SUN); expect(game.scene.arena.weather?.weatherType).toBe(WeatherType.HARSH_SUN);
vi.spyOn(game.scene.getPlayerPokemon()!, "randSeedInt").mockReturnValue(0); vi.spyOn(game.scene.getPlayerPokemon()!, "randBattleSeedInt").mockReturnValue(0);
const commandPhase = game.scene.getCurrentPhase() as CommandPhase; const commandPhase = game.scene.getCurrentPhase() as CommandPhase;
commandPhase.handleCommand(Command.RUN, 0); commandPhase.handleCommand(Command.RUN, 0);

View File

@ -164,7 +164,7 @@ describe("Abilities - Neutralizing Gas", () => {
await game.classicMode.startBattle([Species.MAGIKARP]); await game.classicMode.startBattle([Species.MAGIKARP]);
expect(game.scene.arena.getTag(ArenaTagType.NEUTRALIZING_GAS)).toBeDefined(); expect(game.scene.arena.getTag(ArenaTagType.NEUTRALIZING_GAS)).toBeDefined();
vi.spyOn(game.scene.getPlayerPokemon()!, "randSeedInt").mockReturnValue(0); vi.spyOn(game.scene.getPlayerPokemon()!, "randBattleSeedInt").mockReturnValue(0);
const commandPhase = game.scene.getCurrentPhase() as CommandPhase; const commandPhase = game.scene.getCurrentPhase() as CommandPhase;
commandPhase.handleCommand(Command.RUN, 0); commandPhase.handleCommand(Command.RUN, 0);

View File

@ -73,7 +73,7 @@ describe("Items - Reviver Seed", () => {
const reviverSeed = player.getHeldItems()[0] as PokemonInstantReviveModifier; const reviverSeed = player.getHeldItems()[0] as PokemonInstantReviveModifier;
vi.spyOn(reviverSeed, "apply"); vi.spyOn(reviverSeed, "apply");
vi.spyOn(player, "randSeedInt").mockReturnValue(0); // Force confusion self-hit vi.spyOn(player, "randBattleSeedInt").mockReturnValue(0); // Force confusion self-hit
game.move.select(Moves.TACKLE); game.move.select(Moves.TACKLE);
await game.phaseInterceptor.to("BerryPhase"); await game.phaseInterceptor.to("BerryPhase");

View File

@ -74,7 +74,7 @@ describe("Moves - U-turn", () => {
// arrange // arrange
game.override.enemyAbility(Abilities.POISON_POINT); game.override.enemyAbility(Abilities.POISON_POINT);
await game.classicMode.startBattle([Species.RAICHU, Species.SHUCKLE]); await game.classicMode.startBattle([Species.RAICHU, Species.SHUCKLE]);
vi.spyOn(game.scene.getEnemyPokemon()!, "randSeedInt").mockReturnValue(0); vi.spyOn(game.scene.getEnemyPokemon()!, "randBattleSeedInt").mockReturnValue(0);
// act // act
game.move.select(Moves.U_TURN); game.move.select(Moves.U_TURN);