diff --git a/src/battle-scene.ts b/src/battle-scene.ts index e659b588208c..d4bce3fa9c6e 100644 --- a/src/battle-scene.ts +++ b/src/battle-scene.ts @@ -1049,6 +1049,23 @@ export default class BattleScene extends SceneBase { return this.currentBattle?.randSeedInt(this, range, min); } + /** + * Shuffles an array based on the current battle's seed. + * @param {array} items a list of items + * @returns a new randomly shuffled array + */ + randBattleSeedShuffle(items: any[]): any[] { + if (items.length <= 1) { + return items; + } + const newArray = items.slice(0); + for (let i = items.length - 1; i > 0; i--) { + const j = this.currentBattle?.randSeedInt(this, i); + [ newArray[i], newArray[j] ] = [ newArray[j], newArray[i] ]; + } + return newArray; + } + reset(clearScene: boolean = false, clearData: boolean = false, reloadI18n: boolean = false): void { if (clearData) { this.gameData = new GameData(this); diff --git a/src/data/move.ts b/src/data/move.ts index 74ac61af8844..22fec87d3871 100644 --- a/src/data/move.ts +++ b/src/data/move.ts @@ -2363,7 +2363,7 @@ export class StealHeldItemChanceAttr extends MoveEffectAttr { if (move.hitsSubstitute(user, target)) { return resolve(false); } - const rand = Phaser.Math.RND.realInRange(0, 1); + const rand = user.randSeedInt(100); if (rand >= this.chance) { return resolve(false); } @@ -8208,7 +8208,7 @@ export function initMoves() { .attr(MultiHitPowerIncrementAttr, 3) .checkAllHits(), new AttackMove(Moves.THIEF, Type.DARK, MoveCategory.PHYSICAL, 60, 100, 25, -1, 0, 2) - .attr(StealHeldItemChanceAttr, 0.3), + .attr(StealHeldItemChanceAttr, 30), new StatusMove(Moves.SPIDER_WEB, Type.BUG, -1, 10, -1, 0, 2) .attr(AddBattlerTagAttr, BattlerTagType.TRAPPED, false, true, 1), new StatusMove(Moves.MIND_READER, Type.NORMAL, -1, 5, -1, 0, 2) @@ -8747,7 +8747,7 @@ export function initMoves() { .attr(HighCritAttr) .attr(StatusEffectAttr, StatusEffect.POISON), new AttackMove(Moves.COVET, Type.NORMAL, MoveCategory.PHYSICAL, 60, 100, 25, -1, 0, 3) - .attr(StealHeldItemChanceAttr, 0.3), + .attr(StealHeldItemChanceAttr, 30), new AttackMove(Moves.VOLT_TACKLE, Type.ELECTRIC, MoveCategory.PHYSICAL, 120, 100, 15, 10, 0, 3) .attr(RecoilAttr, false, 0.33) .attr(StatusEffectAttr, StatusEffect.PARALYSIS) diff --git a/src/data/mystery-encounters/encounters/absolute-avarice-encounter.ts b/src/data/mystery-encounters/encounters/absolute-avarice-encounter.ts index 9c00148fbace..6efb3b99419b 100644 --- a/src/data/mystery-encounters/encounters/absolute-avarice-encounter.ts +++ b/src/data/mystery-encounters/encounters/absolute-avarice-encounter.ts @@ -318,7 +318,7 @@ export const AbsoluteAvariceEncounter: MysteryEncounter = if (returnedBerryCount > 0) { for (let i = 0; i < returnedBerryCount; i++) { // Shuffle remaining berry types and pop - Phaser.Math.RND.shuffle(berryTypesAsArray); + pokemon.randSeedShuffle(berryTypesAsArray); const randBerryType = berryTypesAsArray.pop(); const berryModType = generateModifierType(scene, modifierTypes.BERRY, [ randBerryType ]) as BerryModifierType; diff --git a/src/data/mystery-encounters/utils/encounter-pokemon-utils.ts b/src/data/mystery-encounters/utils/encounter-pokemon-utils.ts index fc85754bdde3..148627efebbb 100644 --- a/src/data/mystery-encounters/utils/encounter-pokemon-utils.ts +++ b/src/data/mystery-encounters/utils/encounter-pokemon-utils.ts @@ -1,6 +1,6 @@ import BattleScene from "#app/battle-scene"; import i18next from "i18next"; -import { isNullOrUndefined, randSeedInt } from "#app/utils"; +import { isNullOrUndefined, randSeedInt, randSeedShuffle } from "#app/utils"; import { PokemonHeldItemModifier } from "#app/modifier/modifier"; import Pokemon, { EnemyPokemon, PlayerPokemon } from "#app/field/pokemon"; import { doPokeballBounceAnim, getPokeballAtlasKey, getPokeballCatchMultiplier, getPokeballTintColor } from "#app/data/pokeball"; @@ -241,7 +241,7 @@ export function getRandomSpeciesByStarterTier(starterTiers: number | [number, nu if (tryFilterStarterTiers.length > 0) { const index = randSeedInt(tryFilterStarterTiers.length); - return Phaser.Math.RND.shuffle(tryFilterStarterTiers)[index][0].speciesId; + return randSeedShuffle(tryFilterStarterTiers)[index][0].speciesId; } return Species.BULBASAUR; diff --git a/src/field/pokemon.ts b/src/field/pokemon.ts index 5d912f7d6e6a..b0580fafc22a 100644 --- a/src/field/pokemon.ts +++ b/src/field/pokemon.ts @@ -4044,6 +4044,15 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { return this.randSeedInt((max - min) + 1, min); } + /** + * Shuffles an array using the current battle's seed, or the global seed if `this.scene.currentBattle` is falsy. + * @param {array} items an array of items + * @returns {array} a new shuffled array of items + */ + randSeedShuffle(items: any[]): any[] { + return this.scene.currentBattle ? this.scene.randBattleSeedShuffle(items) : Utils.randSeedShuffle(items); + } + /** * Causes a Pokemon to leave the field (such as in preparation for a switch out/escape). * @param clearEffects Indicates if effects should be cleared (true) or passed diff --git a/src/modifier/modifier.ts b/src/modifier/modifier.ts index 7aa4b9308d10..b1b84603b1bf 100644 --- a/src/modifier/modifier.ts +++ b/src/modifier/modifier.ts @@ -3538,7 +3538,11 @@ export class EnemyTurnHealModifier extends EnemyPersistentModifier { return 10; } } - +/** + * Modifer Class for Burn, Poison, and Paralysis Tokens + * Burn/Poison Tokens -> 2.5% chance of status on hit + * Paralysis Token -> 5% chance of status on hit + */ export class EnemyAttackStatusEffectChanceModifier extends EnemyPersistentModifier { public effect: StatusEffect; public chance: number; @@ -3548,7 +3552,11 @@ export class EnemyAttackStatusEffectChanceModifier extends EnemyPersistentModifi this.effect = effect; //Hardcode temporarily - this.chance = .025 * ((this.effect === StatusEffect.BURN || this.effect === StatusEffect.POISON) ? 2 : 1); + if (this.effect === StatusEffect.BURN || this.effect === StatusEffect.POISON) { + this.chance = 2.5; + } else { + this.chance = 5; + } } match(modifier: Modifier): boolean { @@ -3556,11 +3564,11 @@ export class EnemyAttackStatusEffectChanceModifier extends EnemyPersistentModifi } clone(): EnemyAttackStatusEffectChanceModifier { - return new EnemyAttackStatusEffectChanceModifier(this.type, this.effect, this.chance * 100, this.stackCount); + return new EnemyAttackStatusEffectChanceModifier(this.type, this.effect, this.chance, this.stackCount); } getArgs(): any[] { - return [ this.effect, this.chance * 100 ]; + return [ this.effect, this.chance ]; } /** @@ -3569,7 +3577,7 @@ export class EnemyAttackStatusEffectChanceModifier extends EnemyPersistentModifi * @returns `true` if the {@linkcode Pokemon} was affected */ override apply(enemyPokemon: Pokemon): boolean { - if (Phaser.Math.RND.realInRange(0, 1) < (this.chance * this.getStackCount())) { + if (enemyPokemon.randSeedInt(100) < (this.chance * this.getStackCount())) { return enemyPokemon.trySetStatus(this.effect, true); } @@ -3581,6 +3589,9 @@ export class EnemyAttackStatusEffectChanceModifier extends EnemyPersistentModifi } } +/** + * Modifier Class for Full Heal Token + */ export class EnemyStatusEffectHealChanceModifier extends EnemyPersistentModifier { public chance: number; @@ -3588,7 +3599,7 @@ export class EnemyStatusEffectHealChanceModifier extends EnemyPersistentModifier super(type, stackCount); //Hardcode temporarily - this.chance = .025; + this.chance = 2.5; } match(modifier: Modifier): boolean { @@ -3596,11 +3607,11 @@ export class EnemyStatusEffectHealChanceModifier extends EnemyPersistentModifier } clone(): EnemyStatusEffectHealChanceModifier { - return new EnemyStatusEffectHealChanceModifier(this.type, this.chance * 100, this.stackCount); + return new EnemyStatusEffectHealChanceModifier(this.type, this.chance, this.stackCount); } getArgs(): any[] { - return [ this.chance * 100 ]; + return [ this.chance ]; } /** @@ -3609,7 +3620,7 @@ export class EnemyStatusEffectHealChanceModifier extends EnemyPersistentModifier * @returns `true` if the {@linkcode Pokemon} was healed */ override apply(enemyPokemon: Pokemon): boolean { - if (enemyPokemon.status && Phaser.Math.RND.realInRange(0, 1) < (this.chance * this.getStackCount())) { + if (enemyPokemon.status && enemyPokemon.randSeedInt(100) < (this.chance * this.getStackCount())) { enemyPokemon.scene.queueMessage(getStatusEffectHealText(enemyPokemon.status.effect, getPokemonNameWithAffix(enemyPokemon))); enemyPokemon.resetStatus(); enemyPokemon.updateInfo(); diff --git a/src/test/abilities/unburden.test.ts b/src/test/abilities/unburden.test.ts index ba14c7fdcd00..1640a461b68e 100644 --- a/src/test/abilities/unburden.test.ts +++ b/src/test/abilities/unburden.test.ts @@ -62,7 +62,7 @@ describe("Abilities - Unburden", () => { { name: "BERRY", type: BerryType.LUM, count: 1 }, ]); // For the various tests that use Thief, give it a 100% steal rate - vi.spyOn(allMoves[Moves.THIEF], "attrs", "get").mockReturnValue([ new StealHeldItemChanceAttr(1.0) ]); + vi.spyOn(allMoves[Moves.THIEF], "attrs", "get").mockReturnValue([ new StealHeldItemChanceAttr(100) ]); }); it("should activate when a berry is eaten", async () => {