RNG Logging extension

Copied over every file that uses any RNG function, adding more logging and hopefully fixing RNG discrepancies
This commit is contained in:
RedstonewolfX 2024-09-11 14:55:14 -04:00
parent f0ec8b4a87
commit dfcc5c9e87
31 changed files with 223 additions and 238 deletions

View File

@ -850,9 +850,9 @@ export default class BattleScene extends SceneBase {
return activeOnly ? this.infoToggles.filter(t => t?.isActive()) : this.infoToggles; return activeOnly ? this.infoToggles.filter(t => t?.isActive()) : this.infoToggles;
} }
getPokemonById(pokemonId: integer): Pokemon | undefined { getPokemonById(pokemonId: integer): Pokemon | null {
const findInParty = (party: Pokemon[]) => party.find(p => p.id === pokemonId); const findInParty = (party: Pokemon[]) => party.find(p => p.id === pokemonId);
return (findInParty(this.getParty()) || findInParty(this.getEnemyParty())) || undefined; return (findInParty(this.getParty()) || findInParty(this.getEnemyParty())) ?? null;
} }
addPlayerPokemon(species: PokemonSpecies, level: integer, abilityIndex?: integer, formIndex?: integer, gender?: Gender, shiny?: boolean, variant?: Variant, ivs?: integer[], nature?: Nature, dataSource?: Pokemon | PokemonData, postProcess?: (playerPokemon: PlayerPokemon) => void): PlayerPokemon { addPlayerPokemon(species: PokemonSpecies, level: integer, abilityIndex?: integer, formIndex?: integer, gender?: Gender, shiny?: boolean, variant?: Variant, ivs?: integer[], nature?: Nature, dataSource?: Pokemon | PokemonData, postProcess?: (playerPokemon: PlayerPokemon) => void): PlayerPokemon {
@ -879,7 +879,7 @@ export default class BattleScene extends SceneBase {
overrideModifiers(this, false); overrideModifiers(this, false);
overrideHeldItems(this, pokemon, false); overrideHeldItems(this, pokemon, false);
if (boss && !dataSource) { if (boss && !dataSource) {
const secondaryIvs = Utils.getIvsFromId(Utils.randSeedInt(4294967296)); const secondaryIvs = Utils.getIvsFromId(Utils.randSeedInt(4294967296, undefined, "IVs"));
for (let s = 0; s < pokemon.ivs.length; s++) { for (let s = 0; s < pokemon.ivs.length; s++) {
pokemon.ivs[s] = Math.round(Phaser.Math.Linear(Math.min(pokemon.ivs[s], secondaryIvs[s]), Math.max(pokemon.ivs[s], secondaryIvs[s]), 0.75)); pokemon.ivs[s] = Math.round(Phaser.Math.Linear(Math.min(pokemon.ivs[s], secondaryIvs[s]), Math.max(pokemon.ivs[s], secondaryIvs[s]), 0.75));
@ -1196,12 +1196,12 @@ export default class BattleScene extends SceneBase {
this.setScoreText(txt.join(" / ")) this.setScoreText(txt.join(" / "))
} }
newBattle(waveIndex?: integer, battleType?: BattleType, trainerData?: TrainerData, double?: boolean): Battle { newBattle(waveIndex?: integer, battleType?: BattleType, trainerData?: TrainerData, double?: boolean): Battle | null {
const _startingWave = Overrides.STARTING_WAVE_OVERRIDE || startingWave; const _startingWave = Overrides.STARTING_WAVE_OVERRIDE || startingWave;
const newWaveIndex = waveIndex || ((this.currentBattle?.waveIndex || (_startingWave - 1)) + 1); const newWaveIndex = waveIndex || ((this.currentBattle?.waveIndex || (_startingWave - 1)) + 1);
var newDouble; let newDouble: boolean | undefined;
var newBattleType; let newBattleType: BattleType;
var newTrainer; let newTrainer: Trainer | undefined;
let battleConfig: FixedBattleConfig | null = null; let battleConfig: FixedBattleConfig | null = null;
@ -1237,13 +1237,13 @@ export default class BattleScene extends SceneBase {
const doubleChance = new Utils.IntegerHolder(newWaveIndex % 10 === 0 ? 32 : 8); const doubleChance = new Utils.IntegerHolder(newWaveIndex % 10 === 0 ? 32 : 8);
this.applyModifiers(DoubleBattleChanceBoosterModifier, true, doubleChance); this.applyModifiers(DoubleBattleChanceBoosterModifier, true, doubleChance);
playerField.forEach(p => applyAbAttrs(DoubleBattleChanceAbAttr, p, null, false, doubleChance)); playerField.forEach(p => applyAbAttrs(DoubleBattleChanceAbAttr, p, null, false, doubleChance));
doubleTrainer = !Utils.randSeedInt(doubleChance.value); doubleTrainer = !Utils.randSeedInt(doubleChance.value, undefined, "Double battle roll");
// Add a check that special trainers can't be double except for tate and liza - they should use the normal double chance // Add a check that special trainers can't be double except for tate and liza - they should use the normal double chance
if (trainerConfigs[trainerType].trainerTypeDouble && ![ TrainerType.TATE, TrainerType.LIZA ].includes(trainerType)) { if (trainerConfigs[trainerType].trainerTypeDouble && ![ TrainerType.TATE, TrainerType.LIZA ].includes(trainerType)) {
doubleTrainer = false; doubleTrainer = false;
} }
} }
const variant = doubleTrainer ? TrainerVariant.DOUBLE : (Utils.randSeedInt(2) ? TrainerVariant.FEMALE : TrainerVariant.DEFAULT); const variant = doubleTrainer ? TrainerVariant.DOUBLE : (Utils.randSeedInt(2, undefined, "Trainer gender") ? TrainerVariant.FEMALE : TrainerVariant.DEFAULT);
newTrainer = trainerData !== undefined ? trainerData.toTrainer(this) : new Trainer(this, trainerType, variant); newTrainer = trainerData !== undefined ? trainerData.toTrainer(this) : new Trainer(this, trainerType, variant);
this.field.add(newTrainer); this.field.add(newTrainer);
} }
@ -1254,9 +1254,9 @@ export default class BattleScene extends SceneBase {
const doubleChance = new Utils.IntegerHolder(newWaveIndex % 10 === 0 ? 32 : 8); const doubleChance = new Utils.IntegerHolder(newWaveIndex % 10 === 0 ? 32 : 8);
this.applyModifiers(DoubleBattleChanceBoosterModifier, true, doubleChance); this.applyModifiers(DoubleBattleChanceBoosterModifier, true, doubleChance);
playerField.forEach(p => applyAbAttrs(DoubleBattleChanceAbAttr, p, null, false, doubleChance)); playerField.forEach(p => applyAbAttrs(DoubleBattleChanceAbAttr, p, null, false, doubleChance));
newDouble = !Utils.randSeedInt(doubleChance.value); newDouble = !Utils.randSeedInt(doubleChance.value, undefined, "Double battle roll");
} else if (newBattleType === BattleType.TRAINER) { } else if (newBattleType === BattleType.TRAINER) {
newDouble = newTrainer.variant === TrainerVariant.DOUBLE; newDouble = newTrainer?.variant === TrainerVariant.DOUBLE;
} }
} else if (!battleConfig) { } else if (!battleConfig) {
newDouble = !!double; newDouble = !!double;
@ -1408,19 +1408,19 @@ export default class BattleScene extends SceneBase {
case Species.TATSUGIRI: case Species.TATSUGIRI:
case Species.GIMMIGHOUL: case Species.GIMMIGHOUL:
case Species.PALDEA_TAUROS: case Species.PALDEA_TAUROS:
return Utils.randSeedInt(species.forms.length); return Utils.randSeedInt(species.forms.length, undefined, "General form selection");
case Species.PIKACHU: case Species.PIKACHU:
return Utils.randSeedInt(8); return Utils.randSeedInt(8, undefined, "Pikachu form selection");
case Species.EEVEE: case Species.EEVEE:
return Utils.randSeedInt(2); return Utils.randSeedInt(2, undefined, "Eevee form selection");
case Species.GRENINJA: case Species.GRENINJA:
return Utils.randSeedInt(2); return Utils.randSeedInt(2, undefined, "Greninja form selection");
case Species.ZYGARDE: case Species.ZYGARDE:
return Utils.randSeedInt(3); return Utils.randSeedInt(3, undefined, "Zygarde form selection");
case Species.MINIOR: case Species.MINIOR:
return Utils.randSeedInt(6); return Utils.randSeedInt(6, undefined, "Minior color selection");
case Species.ALCREMIE: case Species.ALCREMIE:
return Utils.randSeedInt(9); return Utils.randSeedInt(9, undefined, "Alcremie form selection");
case Species.MEOWSTIC: case Species.MEOWSTIC:
case Species.INDEEDEE: case Species.INDEEDEE:
case Species.BASCULEGION: case Species.BASCULEGION:
@ -1440,7 +1440,7 @@ export default class BattleScene extends SceneBase {
case Species.WORMADAM: case Species.WORMADAM:
case Species.ROTOM: case Species.ROTOM:
case Species.LYCANROC: case Species.LYCANROC:
return Utils.randSeedInt(species.forms.length); return Utils.randSeedInt(species.forms.length, undefined, "Non-area-specific form selection");
} }
return 0; return 0;
} }
@ -1451,7 +1451,7 @@ export default class BattleScene extends SceneBase {
private getGeneratedOffsetGym(): boolean { private getGeneratedOffsetGym(): boolean {
let ret = false; let ret = false;
this.executeWithSeedOffset(() => { this.executeWithSeedOffset(() => {
ret = !Utils.randSeedInt(2); ret = !Utils.randSeedInt(2, undefined, "Random gym offset");
}, 0, this.seed.toString()); }, 0, this.seed.toString());
return ret; return ret;
} }
@ -1459,7 +1459,7 @@ export default class BattleScene extends SceneBase {
private getGeneratedWaveCycleOffset(): integer { private getGeneratedWaveCycleOffset(): integer {
let ret = 0; let ret = 0;
this.executeWithSeedOffset(() => { this.executeWithSeedOffset(() => {
ret = Utils.randSeedInt(8) * 5; ret = Utils.randSeedInt(8, undefined, "Random day/night cycle offset 5 x") * 5;
}, 0, this.seed.toString()); }, 0, this.seed.toString());
return ret; return ret;
} }
@ -1481,7 +1481,7 @@ export default class BattleScene extends SceneBase {
isBoss = true; isBoss = true;
} else { } else {
this.executeWithSeedOffset(() => { this.executeWithSeedOffset(() => {
isBoss = waveIndex % 10 === 0 || (this.gameMode.hasRandomBosses && Utils.randSeedInt(100) < Math.min(Math.max(Math.ceil((waveIndex - 250) / 50), 0) * 2, 30)); isBoss = waveIndex % 10 === 0 || (this.gameMode.hasRandomBosses && Utils.randSeedInt(100, undefined, "Boss HP segments") < Math.min(Math.max(Math.ceil((waveIndex - 250) / 50), 0) * 2, 30));
}, waveIndex << 2); }, waveIndex << 2);
} }
if (!isBoss) { if (!isBoss) {
@ -1819,7 +1819,7 @@ export default class BattleScene extends SceneBase {
} }
return s; return s;
}))] : allSpecies.filter(s => s.isCatchable()); }))] : allSpecies.filter(s => s.isCatchable());
return filteredSpecies[Utils.randSeedInt(filteredSpecies.length)]; return filteredSpecies[Utils.randSeedInt(filteredSpecies.length, undefined, "Random Species")];
} }
generateRandomBiome(waveIndex: integer): Biome { generateRandomBiome(waveIndex: integer): Biome {
@ -1835,7 +1835,7 @@ export default class BattleScene extends SceneBase {
biomeThresholds.push(totalWeight); biomeThresholds.push(totalWeight);
} }
const randInt = Utils.randSeedInt(totalWeight); const randInt = Utils.randSeedInt(totalWeight, undefined, "Random biome");
for (const biome of biomes) { for (const biome of biomes) {
if (randInt < biomeThresholds[biome]) { if (randInt < biomeThresholds[biome]) {
@ -1843,7 +1843,7 @@ export default class BattleScene extends SceneBase {
} }
} }
return biomes[Utils.randSeedInt(biomes.length)]; return biomes[Utils.randSeedInt(biomes.length, undefined, "Random biome (initial roll failed)")];
} }
isBgmPlaying(): boolean { isBgmPlaying(): boolean {
@ -2665,7 +2665,7 @@ export default class BattleScene extends SceneBase {
pokemonModifierChance = Math.ceil(pokemonModifierChance * this.currentBattle.trainer.getPartyMemberModifierChanceMultiplier(i)); // eslint-disable-line pokemonModifierChance = Math.ceil(pokemonModifierChance * this.currentBattle.trainer.getPartyMemberModifierChanceMultiplier(i)); // eslint-disable-line
let count = 0; let count = 0;
for (let c = 0; c < chances; c++) { for (let c = 0; c < chances; c++) {
if (!Utils.randSeedInt(modifierChance)) { if (!Utils.randSeedInt(modifierChance, undefined, "Modifier roll")) {
count++; count++;
} }
} }
@ -2790,7 +2790,7 @@ export default class BattleScene extends SceneBase {
if (mods.length < 1) { if (mods.length < 1) {
return mods; return mods;
} }
const rand = Utils.randSeedInt(mods.length); const rand = Utils.randSeedInt(mods.length, undefined, "Apply shuffled modifiers");
return [mods[rand], ...shuffleModifiers(mods.filter((_, i) => i !== rand))]; return [mods[rand], ...shuffleModifiers(mods.filter((_, i) => i !== rand))];
}; };
modifiers = shuffleModifiers(modifiers); modifiers = shuffleModifiers(modifiers);

View File

@ -387,7 +387,7 @@ export default class Battle {
* @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(scene: BattleScene, range: number, min: number = 0, reason: string = "Unlabeled randSeedInt"): number { randSeedInt(scene: BattleScene, range: number, min: number = 0, reason?: string): number {
if (range <= 1) { if (range <= 1) {
return min; return min;
} }
@ -402,8 +402,7 @@ export default class Battle {
} }
scene.rngCounter = this.rngCounter++; scene.rngCounter = this.rngCounter++;
scene.rngSeedOverride = this.battleSeed; scene.rngSeedOverride = this.battleSeed;
const ret = Utils.randSeedInt(range, min); const ret = Utils.randSeedInt(range, min, reason);
console.log("[RNG] " + reason, ret)
this.battleSeedState = Phaser.Math.RND.state(); this.battleSeedState = Phaser.Math.RND.state();
Phaser.Math.RND.state(state); Phaser.Math.RND.state(state);
//scene.setScoreText("RNG: " + tempRngCounter + " (Last sim: " + this.rngCounter + ")") //scene.setScoreText("RNG: " + tempRngCounter + " (Last sim: " + this.rngCounter + ")")
@ -474,7 +473,7 @@ function getRandomTrainerFunc(trainerPool: (TrainerType | TrainerType[])[], rand
scene.executeWithSeedOffset(() => { scene.executeWithSeedOffset(() => {
for (const trainerPoolEntry of trainerPool) { for (const trainerPoolEntry of trainerPool) {
const trainerType = Array.isArray(trainerPoolEntry) const trainerType = Array.isArray(trainerPoolEntry)
? Utils.randSeedItem(trainerPoolEntry) ? Utils.randSeedItem(trainerPoolEntry, "Random trainer helper function")
: trainerPoolEntry; : trainerPoolEntry;
trainerTypes.push(trainerType); trainerTypes.push(trainerType);
} }
@ -482,7 +481,7 @@ function getRandomTrainerFunc(trainerPool: (TrainerType | TrainerType[])[], rand
let trainerGender = TrainerVariant.DEFAULT; let trainerGender = TrainerVariant.DEFAULT;
if (randomGender) { if (randomGender) {
trainerGender = (Utils.randInt(2) === 0) ? TrainerVariant.FEMALE : TrainerVariant.DEFAULT; trainerGender = (Utils.randInt(2, undefined, "Random trainer helper function") === 0) ? TrainerVariant.FEMALE : TrainerVariant.DEFAULT;
} }
/* 1/3 chance for evil team grunts to be double battles */ /* 1/3 chance for evil team grunts to be double battles */
@ -490,7 +489,7 @@ function getRandomTrainerFunc(trainerPool: (TrainerType | TrainerType[])[], rand
const isEvilTeamGrunt = evilTeamGrunts.includes(trainerTypes[rand]); const isEvilTeamGrunt = evilTeamGrunts.includes(trainerTypes[rand]);
if (trainerConfigs[trainerTypes[rand]].hasDouble && isEvilTeamGrunt) { if (trainerConfigs[trainerTypes[rand]].hasDouble && isEvilTeamGrunt) {
return new Trainer(scene, trainerTypes[rand], (Utils.randInt(3) === 0) ? TrainerVariant.DOUBLE : trainerGender); return new Trainer(scene, trainerTypes[rand], (Utils.randInt(3, undefined, "Evil grunt selection") === 0) ? TrainerVariant.DOUBLE : trainerGender);
} }
return new Trainer(scene, trainerTypes[rand], trainerGender); return new Trainer(scene, trainerTypes[rand], trainerGender);
@ -511,7 +510,7 @@ export interface FixedBattleConfigs {
*/ */
export const classicFixedBattles: FixedBattleConfigs = { export const classicFixedBattles: FixedBattleConfigs = {
[5]: new FixedBattleConfig().setBattleType(BattleType.TRAINER) [5]: new FixedBattleConfig().setBattleType(BattleType.TRAINER)
.setGetTrainerFunc(scene => new Trainer(scene, TrainerType.YOUNGSTER, Utils.randSeedInt(2) ? TrainerVariant.FEMALE : TrainerVariant.DEFAULT)), .setGetTrainerFunc(scene => new Trainer(scene, TrainerType.YOUNGSTER, Utils.randSeedInt(2, undefined, "Youngster gender") ? TrainerVariant.FEMALE : TrainerVariant.DEFAULT)),
[8]: new FixedBattleConfig().setBattleType(BattleType.TRAINER) [8]: new FixedBattleConfig().setBattleType(BattleType.TRAINER)
.setGetTrainerFunc(scene => new Trainer(scene, TrainerType.RIVAL, scene.gameData.gender === PlayerGender.MALE ? TrainerVariant.FEMALE : TrainerVariant.DEFAULT)), .setGetTrainerFunc(scene => new Trainer(scene, TrainerType.RIVAL, scene.gameData.gender === PlayerGender.MALE ? TrainerVariant.FEMALE : TrainerVariant.DEFAULT)),
[25]: new FixedBattleConfig().setBattleType(BattleType.TRAINER) [25]: new FixedBattleConfig().setBattleType(BattleType.TRAINER)

View File

@ -859,7 +859,7 @@ export class PostDefendContactApplyStatusEffectAbAttr extends PostDefendAbAttr {
applyPostDefend(pokemon: Pokemon, passive: boolean, simulated: boolean, attacker: Pokemon, move: Move, hitResult: HitResult, args: any[]): boolean { applyPostDefend(pokemon: Pokemon, passive: boolean, simulated: boolean, attacker: Pokemon, move: Move, hitResult: HitResult, args: any[]): boolean {
if (move.checkFlag(MoveFlags.MAKES_CONTACT, attacker, pokemon) && !attacker.status && (this.chance === -1 || pokemon.randSeedInt(100, undefined, "Random chance to apply effect after something makes contact") < this.chance)) { if (move.checkFlag(MoveFlags.MAKES_CONTACT, attacker, pokemon) && !attacker.status && (this.chance === -1 || pokemon.randSeedInt(100, undefined, "Random chance to apply effect after something makes contact") < this.chance)) {
const effect = this.effects.length === 1 ? this.effects[0] : this.effects[pokemon.randSeedInt(this.effects.length, undefined, "Selecting an effect to apply")]; const effect = this.effects.length === 1 ? this.effects[0] : this.effects[pokemon.randSeedInt(this.effects.length, undefined, "Choosing status to apply")];
if (simulated) { if (simulated) {
return attacker.canSetStatus(effect, true, false, pokemon); return attacker.canSetStatus(effect, true, false, pokemon);
} else { } else {
@ -1674,7 +1674,7 @@ export class PostAttackApplyStatusEffectAbAttr extends PostAttackAbAttr {
applyPostAttackAfterMoveTypeCheck(pokemon: Pokemon, passive: boolean, simulated: boolean, attacker: Pokemon, move: Move, hitResult: HitResult, args: any[]): boolean { applyPostAttackAfterMoveTypeCheck(pokemon: Pokemon, passive: boolean, simulated: boolean, attacker: Pokemon, move: Move, hitResult: HitResult, args: any[]): boolean {
/**Status inflicted by abilities post attacking are also considered additional effects.*/ /**Status inflicted by abilities post attacking are also considered additional effects.*/
if (!attacker.hasAbilityWithAttr(IgnoreMoveEffectsAbAttr) && !simulated && pokemon !== attacker && (!this.contactRequired || move.checkFlag(MoveFlags.MAKES_CONTACT, attacker, pokemon)) && pokemon.randSeedInt(100, undefined, "Chance to apply status after attacking") < this.chance && !pokemon.status) { if (!attacker.hasAbilityWithAttr(IgnoreMoveEffectsAbAttr) && !simulated && pokemon !== attacker && (!this.contactRequired || move.checkFlag(MoveFlags.MAKES_CONTACT, attacker, pokemon)) && pokemon.randSeedInt(100, undefined, "Chance to apply status after attacking") < this.chance && !pokemon.status) {
const effect = this.effects.length === 1 ? this.effects[0] : this.effects[pokemon.randSeedInt(this.effects.length, undefined, "Selecting a status to apply")]; const effect = this.effects.length === 1 ? this.effects[0] : this.effects[pokemon.randSeedInt(this.effects.length, undefined, "Choosing effect to apply")];
return attacker.trySetStatus(effect, true, pokemon); return attacker.trySetStatus(effect, true, pokemon);
} }
@ -1727,7 +1727,7 @@ export class PostDefendStealHeldItemAbAttr extends PostDefendAbAttr {
if (!simulated && hitResult < HitResult.NO_EFFECT && (!this.condition || this.condition(pokemon, attacker, move))) { if (!simulated && hitResult < HitResult.NO_EFFECT && (!this.condition || this.condition(pokemon, attacker, move))) {
const heldItems = this.getTargetHeldItems(attacker).filter(i => i.isTransferrable); const heldItems = this.getTargetHeldItems(attacker).filter(i => i.isTransferrable);
if (heldItems.length) { if (heldItems.length) {
const stolenItem = heldItems[pokemon.randSeedInt(heldItems.length, undefined, "Choosing an item to steal")]; const stolenItem = heldItems[pokemon.randSeedInt(heldItems.length, undefined, "Choosing item to steal (guaranteed steal)")];
pokemon.scene.tryTransferHeldItemModifier(stolenItem, pokemon, false).then(success => { pokemon.scene.tryTransferHeldItemModifier(stolenItem, pokemon, false).then(success => {
if (success) { if (success) {
pokemon.scene.queueMessage(i18next.t("abilityTriggers:postDefendStealHeldItem", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), attackerName: attacker.name, stolenItemType: stolenItem.type.name })); pokemon.scene.queueMessage(i18next.t("abilityTriggers:postDefendStealHeldItem", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), attackerName: attacker.name, stolenItemType: stolenItem.type.name }));
@ -2222,7 +2222,7 @@ export class PostSummonCopyAbilityAbAttr extends PostSummonAbAttr {
let target: Pokemon; let target: Pokemon;
if (targets.length > 1) { if (targets.length > 1) {
pokemon.scene.executeWithSeedOffset(() => target = Utils.randSeedItem(targets), pokemon.scene.currentBattle.waveIndex); pokemon.scene.executeWithSeedOffset(() => target = Utils.randSeedItem(targets, "Target to copy ability from"), pokemon.scene.currentBattle.waveIndex);
} else { } else {
target = targets[0]; target = targets[0];
} }
@ -2343,7 +2343,7 @@ export class PostSummonTransformAbAttr extends PostSummonAbAttr {
let target: Pokemon; let target: Pokemon;
if (targets.length > 1) { if (targets.length > 1) {
pokemon.scene.executeWithSeedOffset(() => target = Utils.randSeedItem(targets), pokemon.scene.currentBattle.waveIndex); pokemon.scene.executeWithSeedOffset(() => target = Utils.randSeedItem(targets, "Target to transform into"), pokemon.scene.currentBattle.waveIndex);
} else { } else {
target = targets[0]; target = targets[0];
} }
@ -2642,7 +2642,7 @@ export class ConfusionOnStatusEffectAbAttr extends PostAttackAbAttr {
if (simulated) { if (simulated) {
return defender.canAddTag(BattlerTagType.CONFUSED); return defender.canAddTag(BattlerTagType.CONFUSED);
} else { } else {
return defender.addTag(BattlerTagType.CONFUSED, pokemon.randSeedIntRange(2, 5, "Duration of Confusion effect"), move.id, defender.id); return defender.addTag(BattlerTagType.CONFUSED, pokemon.randSeedIntRange(2, 5, "Chance to apply effect after attacking"), move.id, defender.id);
} }
} }
return false; return false;
@ -3413,7 +3413,7 @@ export class PostTurnLootAbAttr extends PostTurnAbAttr {
return true; return true;
} }
const randomIdx = Utils.randSeedInt(berriesEaten.length); const randomIdx = Utils.randSeedInt(berriesEaten.length, undefined, "Randomly select a berry to regenerate");
const chosenBerryType = berriesEaten[randomIdx]; const chosenBerryType = berriesEaten[randomIdx];
const chosenBerry = new BerryModifierType(chosenBerryType); const chosenBerry = new BerryModifierType(chosenBerryType);
berriesEaten.splice(randomIdx); // Remove berry from memory berriesEaten.splice(randomIdx); // Remove berry from memory
@ -3466,12 +3466,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.randSeedInt(canRaise.length, undefined, "Choosing a random raisable stat to increase")];
canLower = canRaise.filter(s => s !== raisedStat); canLower = canRaise.filter(s => s !== raisedStat);
pokemon.scene.unshiftPhase(new StatStageChangePhase(pokemon.scene, pokemon.getBattlerIndex(), true, [ raisedStat ], 2)); pokemon.scene.unshiftPhase(new StatStageChangePhase(pokemon.scene, pokemon.getBattlerIndex(), true, [ raisedStat ], 2));
} }
if (canLower.length > 0) { if (canLower.length > 0) {
const loweredStat = canLower[pokemon.randSeedInt(canLower.length)]; const loweredStat = canLower[pokemon.randSeedInt(canLower.length, undefined, "Choosing a random lowerable stat to decrease")];
pokemon.scene.unshiftPhase(new StatStageChangePhase(pokemon.scene, pokemon.getBattlerIndex(), true, [ loweredStat ], -1)); pokemon.scene.unshiftPhase(new StatStageChangePhase(pokemon.scene, pokemon.getBattlerIndex(), true, [ loweredStat ], -1));
} }
} }
@ -3909,7 +3909,7 @@ export class PostBattleLootAbAttr extends PostBattleAbAttr {
applyPostBattle(pokemon: Pokemon, passive: boolean, simulated: boolean, args: any[]): boolean { applyPostBattle(pokemon: Pokemon, passive: boolean, simulated: boolean, args: any[]): boolean {
const postBattleLoot = pokemon.scene.currentBattle.postBattleLoot; const postBattleLoot = pokemon.scene.currentBattle.postBattleLoot;
if (!simulated && postBattleLoot.length) { if (!simulated && postBattleLoot.length) {
const randItem = Utils.randSeedItem(postBattleLoot); const randItem = Utils.randSeedItem(postBattleLoot, "Randomly selecting item to Pickup");
//@ts-ignore - TODO see below //@ts-ignore - TODO see below
if (pokemon.scene.tryTransferHeldItemModifier(randItem, pokemon, true, 1, true)) { // TODO: fix. This is a promise!? if (pokemon.scene.tryTransferHeldItemModifier(randItem, pokemon, true, 1, true)) { // TODO: fix. This is a promise!?
postBattleLoot.splice(postBattleLoot.indexOf(randItem), 1); postBattleLoot.splice(postBattleLoot.indexOf(randItem), 1);
@ -5032,7 +5032,7 @@ export function initAbilities() {
.bypassFaint() .bypassFaint()
.ignorable(), .ignorable(),
new Ability(Abilities.SHED_SKIN, 3) new Ability(Abilities.SHED_SKIN, 3)
.conditionalAttr(pokemon => !Utils.randSeedInt(3), PostTurnResetStatusAbAttr), .conditionalAttr(pokemon => !Utils.randSeedInt(3, undefined, "Random chance to activate Shed Skin"), PostTurnResetStatusAbAttr),
new Ability(Abilities.GUTS, 3) new Ability(Abilities.GUTS, 3)
.attr(BypassBurnDamageReductionAbAttr) .attr(BypassBurnDamageReductionAbAttr)
.conditionalAttr(pokemon => !!pokemon.status || pokemon.hasAbility(Abilities.COMATOSE), StatMultiplierAbAttr, Stat.ATK, 1.5), .conditionalAttr(pokemon => !!pokemon.status || pokemon.hasAbility(Abilities.COMATOSE), StatMultiplierAbAttr, Stat.ATK, 1.5),
@ -5247,7 +5247,7 @@ export function initAbilities() {
.attr(PostDefendMoveDisableAbAttr, 30) .attr(PostDefendMoveDisableAbAttr, 30)
.bypassFaint(), .bypassFaint(),
new Ability(Abilities.HEALER, 5) new Ability(Abilities.HEALER, 5)
.conditionalAttr(pokemon => pokemon.getAlly() && Utils.randSeedInt(10) < 3, PostTurnResetStatusAbAttr, true), .conditionalAttr(pokemon => pokemon.getAlly() && Utils.randSeedInt(10, undefined, "Random chance to apply Healer") < 3, PostTurnResetStatusAbAttr, true),
new Ability(Abilities.FRIEND_GUARD, 5) new Ability(Abilities.FRIEND_GUARD, 5)
.ignorable() .ignorable()
.unimplemented(), .unimplemented(),

View File

@ -483,7 +483,7 @@ export class ConfusedTag extends BattlerTag {
pokemon.scene.unshiftPhase(new CommonAnimPhase(pokemon.scene, pokemon.getBattlerIndex(), undefined, CommonAnim.CONFUSION)); pokemon.scene.unshiftPhase(new CommonAnimPhase(pokemon.scene, 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, undefined, "Self-hit confusion roll") === 0) { if (pokemon.randSeedInt(3, undefined, "Confusion chance") === 0) {
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 = Utils.toDmgValue(((((2 * pokemon.level / 5 + 2) * 40 * atk / def) / 50) + 2) * (pokemon.randSeedIntRange(85, 100, "Damage roll for Confusion") / 100)); const damage = Utils.toDmgValue(((((2 * pokemon.level / 5 + 2) * 40 * atk / def) / 50) + 2) * (pokemon.randSeedIntRange(85, 100, "Damage roll for Confusion") / 100));

View File

@ -114,7 +114,7 @@ export function getBerryEffectFunc(berryType: BerryType): BerryEffectFunc {
if (pokemon.battleData) { if (pokemon.battleData) {
pokemon.battleData.berriesEaten.push(berryType); pokemon.battleData.berriesEaten.push(berryType);
} }
const randStat = Utils.randSeedInt(Stat.SPD, Stat.ATK); const randStat = Utils.randSeedInt(Stat.SPD, Stat.ATK, "Randomly selecting a stat to raise");
const stages = new Utils.NumberHolder(2); const stages = new Utils.NumberHolder(2);
applyAbAttrs(DoubleBerryEffectAbAttr, pokemon, null, false, stages); applyAbAttrs(DoubleBerryEffectAbAttr, pokemon, null, false, stages);
pokemon.scene.unshiftPhase(new StatStageChangePhase(pokemon.scene, pokemon.getBattlerIndex(), true, [ randStat ], stages.value)); pokemon.scene.unshiftPhase(new StatStageChangePhase(pokemon.scene, pokemon.getBattlerIndex(), true, [ randStat ], stages.value));

View File

@ -7666,7 +7666,7 @@ export function initBiomes() {
if (biome === Biome.END) { if (biome === Biome.END) {
const biomeList = Object.keys(Biome).filter(key => !isNaN(Number(key))); const biomeList = Object.keys(Biome).filter(key => !isNaN(Number(key)));
biomeList.pop(); // Removes Biome.END from the list biomeList.pop(); // Removes Biome.END from the list
const randIndex = Utils.randInt(biomeList.length, 1); // Will never be Biome.TOWN const randIndex = Utils.randInt(biomeList.length, 1, "Traversing biomes"); // Will never be Biome.TOWN
biome = Biome[biomeList[randIndex]]; biome = Biome[biomeList[randIndex]];
} }
const linkedBiomes: (Biome | [ Biome, integer ])[] = Array.isArray(biomeLinks[biome]) const linkedBiomes: (Biome | [ Biome, integer ])[] = Array.isArray(biomeLinks[biome])

View File

@ -49,7 +49,7 @@ export function getDailyRunStarters(scene: BattleScene, seed: string): Starter[]
const costSpecies = Object.keys(speciesStarters) const costSpecies = Object.keys(speciesStarters)
.map(s => parseInt(s) as Species) .map(s => parseInt(s) as Species)
.filter(s => speciesStarters[s] === cost); .filter(s => speciesStarters[s] === cost);
const randPkmSpecies = getPokemonSpecies(Utils.randSeedItem(costSpecies)); const randPkmSpecies = getPokemonSpecies(Utils.randSeedItem(costSpecies, "Daily starters"));
const starterSpecies = getPokemonSpecies(randPkmSpecies.getTrainerSpeciesForLevel(startingLevel, true, PartyMemberStrength.STRONGER)); const starterSpecies = getPokemonSpecies(randPkmSpecies.getTrainerSpeciesForLevel(startingLevel, true, PartyMemberStrength.STRONGER));
starters.push(getDailyRunStarter(scene, starterSpecies, startingLevel)); starters.push(getDailyRunStarter(scene, starterSpecies, startingLevel));
} }

View File

@ -151,7 +151,7 @@ export class Egg {
this.checkForPityTierOverrides(eggOptions.scene!); // TODO: is this bang correct? this.checkForPityTierOverrides(eggOptions.scene!); // TODO: is this bang correct?
} }
this._id = eggOptions?.id ?? Utils.randInt(EGG_SEED, EGG_SEED * this._tier); this._id = eggOptions?.id ?? Utils.randInt(EGG_SEED, EGG_SEED * this._tier, "eg");
this._sourceType = eggOptions?.sourceType ?? undefined; this._sourceType = eggOptions?.sourceType ?? undefined;
this._hatchWaves = eggOptions?.hatchWaves ?? this.getEggTierDefaultHatchWaves(); this._hatchWaves = eggOptions?.hatchWaves ?? this.getEggTierDefaultHatchWaves();
@ -223,14 +223,14 @@ export class Egg {
let pokemonSpecies = getPokemonSpecies(this._species); let pokemonSpecies = getPokemonSpecies(this._species);
// Special condition to have Phione eggs also have a chance of generating Manaphy // Special condition to have Phione eggs also have a chance of generating Manaphy
if (this._species === Species.PHIONE) { if (this._species === Species.PHIONE) {
pokemonSpecies = getPokemonSpecies(Utils.randSeedInt(MANAPHY_EGG_MANAPHY_RATE) ? Species.PHIONE : Species.MANAPHY); pokemonSpecies = getPokemonSpecies(Utils.randSeedInt(MANAPHY_EGG_MANAPHY_RATE, undefined, "Chance of Manaphy Egg not scamming you") ? Species.PHIONE : Species.MANAPHY);
} }
// Sets the hidden ability if a hidden ability exists and // Sets the hidden ability if a hidden ability exists and
// the override is set or the egg hits the chance // the override is set or the egg hits the chance
let abilityIndex: number | undefined = undefined; let abilityIndex: number | undefined = undefined;
const sameSpeciesEggHACheck = (this._sourceType === EggSourceType.SAME_SPECIES_EGG && !Utils.randSeedInt(SAME_SPECIES_EGG_HA_RATE)); const sameSpeciesEggHACheck = (this._sourceType === EggSourceType.SAME_SPECIES_EGG && !Utils.randSeedInt(SAME_SPECIES_EGG_HA_RATE, undefined, "Hidden Ability chance (Candy egg)"));
const gachaEggHACheck = (!(this._sourceType === EggSourceType.SAME_SPECIES_EGG) && !Utils.randSeedInt(GACHA_EGG_HA_RATE)); const gachaEggHACheck = (!(this._sourceType === EggSourceType.SAME_SPECIES_EGG) && !Utils.randSeedInt(GACHA_EGG_HA_RATE, undefined, "Hidden Ability chance (Gacha)"));
if (pokemonSpecies.abilityHidden && (this._overrideHiddenAbility || sameSpeciesEggHACheck || gachaEggHACheck)) { if (pokemonSpecies.abilityHidden && (this._overrideHiddenAbility || sameSpeciesEggHACheck || gachaEggHACheck)) {
abilityIndex = 2; abilityIndex = 2;
} }
@ -240,7 +240,7 @@ export class Egg {
ret.shiny = this._isShiny; ret.shiny = this._isShiny;
ret.variant = this._variantTier; ret.variant = this._variantTier;
const secondaryIvs = Utils.getIvsFromId(Utils.randSeedInt(4294967295)); const secondaryIvs = Utils.getIvsFromId(Utils.randSeedInt(4294967295, undefined, "Egg IVs"));
for (let s = 0; s < ret.ivs.length; s++) { for (let s = 0; s < ret.ivs.length; s++) {
ret.ivs[s] = Math.max(ret.ivs[s], secondaryIvs[s]); ret.ivs[s] = Math.max(ret.ivs[s], secondaryIvs[s]);

View File

@ -1980,6 +1980,13 @@ export class StatusEffectAttr extends MoveEffectAttr {
return false; return false;
} }
} }
if (user !== target && target.scene.arena.getTagOnSide(ArenaTagType.SAFEGUARD, target.isPlayer() ? ArenaTagSide.PLAYER : ArenaTagSide.ENEMY)) {
if (move.category === MoveCategory.STATUS) {
user.scene.queueMessage(i18next.t("moveTriggers:safeguard", { targetName: getPokemonNameWithAffix(target)}));
}
return false;
}
if ((!pokemon.status || (pokemon.status.effect === this.effect && moveChance < 0)) if ((!pokemon.status || (pokemon.status.effect === this.effect && moveChance < 0))
&& pokemon.trySetStatus(this.effect, true, user, this.cureTurn)) { && pokemon.trySetStatus(this.effect, true, user, this.cureTurn)) {
applyPostAttackAbAttrs(ConfusionOnStatusEffectAbAttr, user, target, move, null, false, this.effect); applyPostAttackAbAttrs(ConfusionOnStatusEffectAbAttr, user, target, move, null, false, this.effect);
@ -2004,7 +2011,7 @@ export class MultiStatusEffectAttr extends StatusEffectAttr {
} }
apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean { apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean {
this.effect = Utils.randSeedItem(this.effects); this.effect = Utils.randSeedItem(this.effects, "Selecting status effect to apply");
const result = super.apply(user, target, move, args); const result = super.apply(user, target, move, args);
return result; return result;
} }
@ -2080,6 +2087,7 @@ export class StealHeldItemChanceAttr extends MoveEffectAttr {
} }
//*/ //*/
console.log("realInRange direct call @ StealHeldItemChanceAttr: " + rand)
if (rand >= this.chance) { if (rand >= this.chance) {
return resolve(false); return resolve(false);
} }
@ -2632,7 +2640,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, undefined, "Chance to apply status condition") < moveChance) { if (moveChance < 0 || moveChance === 100 || user.randSeedInt(100, undefined, "Random chance to raise stat") < moveChance) {
const stages = this.getLevels(user); const stages = this.getLevels(user);
user.scene.unshiftPhase(new StatStageChangePhase(user.scene, (this.selfTarget ? user : target).getBattlerIndex(), this.selfTarget, this.stats, stages, this.showMessage)); user.scene.unshiftPhase(new StatStageChangePhase(user.scene, (this.selfTarget ? user : target).getBattlerIndex(), this.selfTarget, this.stats, stages, this.showMessage));
return true; return true;
@ -2718,7 +2726,7 @@ export class AcupressureStatStageChangeAttr extends MoveEffectAttr {
apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean | Promise<boolean> { apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean | Promise<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.randSeedInt(randStats.length, undefined, "Choosing stat to raise")]];
user.scene.unshiftPhase(new StatStageChangePhase(user.scene, target.getBattlerIndex(), this.selfTarget, boostStat, 2)); user.scene.unshiftPhase(new StatStageChangePhase(user.scene, target.getBattlerIndex(), this.selfTarget, boostStat, 2));
return true; return true;
} }
@ -3051,7 +3059,7 @@ export class BeatUpAttr extends VariablePowerAttr {
const doublePowerChanceMessageFunc = (user: Pokemon, target: Pokemon, move: Move) => { const doublePowerChanceMessageFunc = (user: Pokemon, target: Pokemon, move: Move) => {
let message: string = ""; let message: string = "";
user.scene.executeWithSeedOffset(() => { user.scene.executeWithSeedOffset(() => {
const rand = Utils.randSeedInt(100); const rand = Utils.randSeedInt(100, undefined, "Doubled power chance (applying message)");
if (rand < move.chance) { if (rand < move.chance) {
message = i18next.t("moveTriggers:goingAllOutForAttack", {pokemonName: getPokemonNameWithAffix(user)}); message = i18next.t("moveTriggers:goingAllOutForAttack", {pokemonName: getPokemonNameWithAffix(user)});
} }
@ -3062,7 +3070,7 @@ const doublePowerChanceMessageFunc = (user: Pokemon, target: Pokemon, move: Move
export class DoublePowerChanceAttr extends VariablePowerAttr { export class DoublePowerChanceAttr extends VariablePowerAttr {
apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean { apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean {
let rand: integer; let rand: integer;
user.scene.executeWithSeedOffset(() => rand = Utils.randSeedInt(100), user.scene.currentBattle.turn << 6, user.scene.waveSeed); user.scene.executeWithSeedOffset(() => rand = Utils.randSeedInt(100, undefined, "Doubled power chance (applying move)"), user.scene.currentBattle.turn << 6, user.scene.waveSeed);
if (rand! < move.chance) { if (rand! < move.chance) {
const power = args[0] as Utils.NumberHolder; const power = args[0] as Utils.NumberHolder;
power.value *= 2; power.value *= 2;
@ -3323,7 +3331,7 @@ const magnitudeMessageFunc = (user: Pokemon, target: Pokemon, move: Move) => {
user.scene.executeWithSeedOffset(() => { user.scene.executeWithSeedOffset(() => {
const magnitudeThresholds = [ 5, 15, 35, 65, 75, 95 ]; const magnitudeThresholds = [ 5, 15, 35, 65, 75, 95 ];
const rand = Utils.randSeedInt(100); const rand = Utils.randSeedInt(100, undefined, "Magnitude selection (message)");
let m = 0; let m = 0;
for (; m < magnitudeThresholds.length; m++) { for (; m < magnitudeThresholds.length; m++) {
@ -3346,7 +3354,7 @@ export class MagnitudePowerAttr extends VariablePowerAttr {
let rand: integer; let rand: integer;
user.scene.executeWithSeedOffset(() => rand = Utils.randSeedInt(100), user.scene.currentBattle.turn << 6, user.scene.waveSeed); user.scene.executeWithSeedOffset(() => rand = Utils.randSeedInt(100, undefined, "Magnitude selection (move)"), user.scene.currentBattle.turn << 6, user.scene.waveSeed);
let m = 0; let m = 0;
for (; m < magnitudeThresholds.length; m++) { for (; m < magnitudeThresholds.length; m++) {
@ -3471,7 +3479,7 @@ export class PresentPowerAttr extends VariablePowerAttr {
*/ */
const firstHit = (user.turnData.hitCount === user.turnData.hitsLeft); const firstHit = (user.turnData.hitCount === user.turnData.hitsLeft);
const powerSeed = Utils.randSeedInt(firstHit ? 100 : 80); const powerSeed = Utils.randSeedInt(firstHit ? 100 : 80, undefined, "Present healing chance");
if (powerSeed <= 40) { if (powerSeed <= 40) {
(args[0] as Utils.NumberHolder).value = 40; (args[0] as Utils.NumberHolder).value = 40;
} else if (40 < powerSeed && powerSeed <= 70) { } else if (40 < powerSeed && powerSeed <= 70) {
@ -3919,7 +3927,7 @@ export class ShellSideArmCategoryAttr extends VariableMoveCategoryAttr {
} else if (atkRatio === specialRatio && args[1] == "SIM") { } else if (atkRatio === specialRatio && args[1] == "SIM") {
category.value = MoveCategory.PHYSICAL; category.value = MoveCategory.PHYSICAL;
return true; return true;
} else if (atkRatio === specialRatio && user.randSeedInt(2, undefined, "Randomly selecting an attack type for Shell Side Arm") === 0) { } else if (atkRatio === specialRatio && user.randSeedInt(2, undefined, "Random category for Shell Side Arm") === 0) {
category.value = MoveCategory.PHYSICAL; category.value = MoveCategory.PHYSICAL;
return true; return true;
} }
@ -4394,7 +4402,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, "Frenzy targeting"); const turnCount = user.randSeedIntRange(1, 2, "Frenzy duration");
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 {
@ -4446,8 +4454,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, undefined, "Chance to apply battler tag") < moveChance) { if (moveChance < 0 || moveChance === 100 || user.randSeedInt(100, undefined, "Chance to add Battler Tag") < moveChance) {
return (this.selfTarget ? user : target).addTag(this.tagType, user.randSeedIntRange(this.turnCountMin, this.turnCountMax, "Duration of effect"), move.id, user.id); return (this.selfTarget ? user : target).addTag(this.tagType, user.randSeedIntRange(this.turnCountMin, this.turnCountMax, "Battler Tag duration"), move.id, user.id);
} }
return false; return false;
@ -4572,7 +4580,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.randSeedInt(100, undefined, "Chance to apply Trap tag (Jaw Lock)") < 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
@ -4666,6 +4674,17 @@ export class ConfuseAttr extends AddBattlerTagAttr {
constructor(selfTarget?: boolean) { constructor(selfTarget?: boolean) {
super(BattlerTagType.CONFUSED, selfTarget, false, 2, 5); super(BattlerTagType.CONFUSED, selfTarget, false, 2, 5);
} }
apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean {
if (!this.selfTarget && target.scene.arena.getTagOnSide(ArenaTagType.SAFEGUARD, target.isPlayer() ? ArenaTagSide.PLAYER : ArenaTagSide.ENEMY)) {
if (move.category === MoveCategory.STATUS) {
user.scene.queueMessage(i18next.t("moveTriggers:safeguard", { targetName: getPokemonNameWithAffix(target)}));
}
return false;
}
return super.apply(user, target, move, args);
}
} }
export class RechargeAttr extends AddBattlerTagAttr { export class RechargeAttr extends AddBattlerTagAttr {
@ -4699,7 +4718,7 @@ export class ProtectAttr extends AddBattlerTagAttr {
timesUsed++; timesUsed++;
} }
if (timesUsed) { if (timesUsed) {
return !user.randSeedInt(Math.pow(3, timesUsed), undefined, "Chance for Protect-like move to fail"); return !user.randSeedInt(Math.pow(3, timesUsed), undefined, "Chance for Protection move to succeed");
} }
return true; return true;
}); });
@ -4855,7 +4874,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 = user.scene.arena.getTagOnSide(this.tagType, side) as ArenaTrapTag; const tag = user.scene.arena.getTagOnSide(this.tagType, side) as ArenaTrapTag;
if ((moveChance < 0 || moveChance === 100 || user.randSeedInt(100, undefined, "Chance to apply trap") < moveChance) && user.getLastXMoves(1)[0].result === MoveResult.SUCCESS) { if ((moveChance < 0 || moveChance === 100 || user.randSeedInt(100, undefined, "Chance to add arena tag on hit") < moveChance) && user.getLastXMoves(1)[0].result === MoveResult.SUCCESS) {
user.scene.arena.addTag(this.tagType, 0, move.id, user.id, side); user.scene.arena.addTag(this.tagType, 0, move.id, user.id, side);
if (!tag) { if (!tag) {
return true; return true;
@ -5009,7 +5028,7 @@ export class RevivalBlessingAttr extends MoveEffectAttr {
&& user.scene.getEnemyParty().findIndex(p => p.isFainted() && !p.isBoss()) > -1) { && user.scene.getEnemyParty().findIndex(p => p.isFainted() && !p.isBoss()) > -1) {
// Selects a random fainted pokemon // Selects a random fainted pokemon
const faintedPokemon = user.scene.getEnemyParty().filter(p => p.isFainted() && !p.isBoss()); const faintedPokemon = user.scene.getEnemyParty().filter(p => p.isFainted() && !p.isBoss());
const pokemon = faintedPokemon[user.randSeedInt(faintedPokemon.length, undefined, "Randomly selecting a Pokemon to revive")]; const pokemon = faintedPokemon[user.randSeedInt(faintedPokemon.length, undefined, "Choosing Pokemon to revive")];
const slotIndex = user.scene.getEnemyParty().findIndex(p => pokemon.id === p.id); const slotIndex = user.scene.getEnemyParty().findIndex(p => pokemon.id === p.id);
pokemon.resetStatus(); pokemon.resetStatus();
pokemon.heal(Math.min(Utils.toDmgValue(0.5 * pokemon.getMaxHp()), pokemon.getMaxHp())); pokemon.heal(Math.min(Utils.toDmgValue(0.5 * pokemon.getMaxHp()), pokemon.getMaxHp()));
@ -5319,7 +5338,7 @@ export class RandomMovesetMoveAttr extends OverrideMoveEffectAttr {
const moveset = (!this.enemyMoveset ? user : target).getMoveset(); const moveset = (!this.enemyMoveset ? user : target).getMoveset();
const moves = moveset.filter(m => !m?.getMove().hasFlag(MoveFlags.IGNORE_VIRTUAL)); const moves = moveset.filter(m => !m?.getMove().hasFlag(MoveFlags.IGNORE_VIRTUAL));
if (moves.length) { if (moves.length) {
const move = moves[user.randSeedInt(moves.length, undefined, "Randomly selecting a known move")]; const move = moves[user.randSeedInt(moves.length, undefined, "Choosing random move from moveset")];
const moveIndex = moveset.findIndex(m => m?.moveId === move?.moveId); const moveIndex = moveset.findIndex(m => m?.moveId === move?.moveId);
const moveTargets = getMoveTargets(user, move?.moveId!); // TODO: is this bang correct? const moveTargets = getMoveTargets(user, move?.moveId!); // TODO: is this bang correct?
if (!moveTargets.targets.length) { if (!moveTargets.targets.length) {
@ -6373,7 +6392,7 @@ export class ResistLastMoveTypeAttr extends MoveEffectAttr {
if (!validTypes.length) { if (!validTypes.length) {
return false; return false;
} }
const type = validTypes[user.randSeedInt(validTypes.length, undefined, "Randomly selecting a type for Conversion2 that resists Type." + Utils.getEnumKeys(Type)[moveData.type])]; const type = validTypes[user.randSeedInt(validTypes.length, undefined, "Choosing type to transform into (Conversion2)")];
user.summonData.types = [ type ]; user.summonData.types = [ type ];
user.scene.queueMessage(i18next.t("battle:transformedIntoType", {pokemonName: getPokemonNameWithAffix(user), type: Utils.toReadableString(Type[type])})); user.scene.queueMessage(i18next.t("battle:transformedIntoType", {pokemonName: getPokemonNameWithAffix(user), type: Utils.toReadableString(Type[type])}));
user.updateInfo(); user.updateInfo();
@ -7126,7 +7145,7 @@ export function initMoves() {
.attr(FriendshipPowerAttr, true), .attr(FriendshipPowerAttr, true),
new StatusMove(Moves.SAFEGUARD, Type.NORMAL, -1, 25, -1, 0, 2) new StatusMove(Moves.SAFEGUARD, Type.NORMAL, -1, 25, -1, 0, 2)
.target(MoveTarget.USER_SIDE) .target(MoveTarget.USER_SIDE)
.unimplemented(), .attr(AddArenaTagAttr, ArenaTagType.SAFEGUARD, 5, true, true),
new StatusMove(Moves.PAIN_SPLIT, Type.NORMAL, -1, 20, -1, 0, 2) new StatusMove(Moves.PAIN_SPLIT, Type.NORMAL, -1, 20, -1, 0, 2)
.attr(HpSplitAttr) .attr(HpSplitAttr)
.condition(failOnBossCondition), .condition(failOnBossCondition),
@ -7315,7 +7334,7 @@ export function initMoves() {
.attr(RemoveScreensAttr), .attr(RemoveScreensAttr),
new StatusMove(Moves.YAWN, Type.NORMAL, -1, 10, -1, 0, 3) new StatusMove(Moves.YAWN, Type.NORMAL, -1, 10, -1, 0, 3)
.attr(AddBattlerTagAttr, BattlerTagType.DROWSY, false, true) .attr(AddBattlerTagAttr, BattlerTagType.DROWSY, false, true)
.condition((user, target, move) => !target.status), .condition((user, target, move) => !target.status && !target.scene.arena.getTagOnSide(ArenaTagType.SAFEGUARD, target.isPlayer() ? ArenaTagSide.PLAYER : ArenaTagSide.ENEMY)),
new AttackMove(Moves.KNOCK_OFF, Type.DARK, MoveCategory.PHYSICAL, 65, 100, 20, -1, 0, 3) new AttackMove(Moves.KNOCK_OFF, Type.DARK, MoveCategory.PHYSICAL, 65, 100, 20, -1, 0, 3)
.attr(MovePowerMultiplierAttr, (user, target, move) => target.getHeldItems().filter(i => i.isTransferrable).length > 0 ? 1.5 : 1) .attr(MovePowerMultiplierAttr, (user, target, move) => target.getHeldItems().filter(i => i.isTransferrable).length > 0 ? 1.5 : 1)
.attr(RemoveHeldItemAttr, false), .attr(RemoveHeldItemAttr, false),
@ -8866,8 +8885,8 @@ export function initMoves() {
new AttackMove(Moves.SKITTER_SMACK, Type.BUG, MoveCategory.PHYSICAL, 70, 90, 10, 100, 0, 8) new AttackMove(Moves.SKITTER_SMACK, Type.BUG, MoveCategory.PHYSICAL, 70, 90, 10, 100, 0, 8)
.attr(StatStageChangeAttr, [ Stat.SPATK ], -1), .attr(StatStageChangeAttr, [ Stat.SPATK ], -1),
new AttackMove(Moves.BURNING_JEALOUSY, Type.FIRE, MoveCategory.SPECIAL, 70, 100, 5, 100, 0, 8) new AttackMove(Moves.BURNING_JEALOUSY, Type.FIRE, MoveCategory.SPECIAL, 70, 100, 5, 100, 0, 8)
.target(MoveTarget.ALL_NEAR_ENEMIES) .attr(StatusIfBoostedAttr, StatusEffect.BURN)
.partial(), .target(MoveTarget.ALL_NEAR_ENEMIES),
new AttackMove(Moves.LASH_OUT, Type.DARK, MoveCategory.PHYSICAL, 75, 100, 5, -1, 0, 8) new AttackMove(Moves.LASH_OUT, Type.DARK, MoveCategory.PHYSICAL, 75, 100, 5, -1, 0, 8)
.attr(MovePowerMultiplierAttr, (user, _target, _move) => user.turnData.statStagesDecreased ? 2 : 1), .attr(MovePowerMultiplierAttr, (user, _target, _move) => user.turnData.statStagesDecreased ? 2 : 1),
new AttackMove(Moves.POLTERGEIST, Type.GHOST, MoveCategory.PHYSICAL, 110, 90, 5, -1, 0, 8) new AttackMove(Moves.POLTERGEIST, Type.GHOST, MoveCategory.PHYSICAL, 110, 90, 5, -1, 0, 8)
@ -9316,12 +9335,11 @@ export function initMoves() {
new AttackMove(Moves.HARD_PRESS, Type.STEEL, MoveCategory.PHYSICAL, -1, 100, 10, -1, 0, 9) new AttackMove(Moves.HARD_PRESS, Type.STEEL, MoveCategory.PHYSICAL, -1, 100, 10, -1, 0, 9)
.attr(OpponentHighHpPowerAttr, 100), .attr(OpponentHighHpPowerAttr, 100),
new StatusMove(Moves.DRAGON_CHEER, Type.DRAGON, -1, 15, -1, 0, 9) new StatusMove(Moves.DRAGON_CHEER, Type.DRAGON, -1, 15, -1, 0, 9)
.attr(AddBattlerTagAttr, BattlerTagType.CRIT_BOOST, false, true) .attr(AddBattlerTagAttr, BattlerTagType.DRAGON_CHEER, false, true)
.target(MoveTarget.NEAR_ALLY) .target(MoveTarget.NEAR_ALLY),
.partial(),
new AttackMove(Moves.ALLURING_VOICE, Type.FAIRY, MoveCategory.SPECIAL, 80, 100, 10, -1, 0, 9) new AttackMove(Moves.ALLURING_VOICE, Type.FAIRY, MoveCategory.SPECIAL, 80, 100, 10, -1, 0, 9)
.soundBased() .attr(AddBattlerTagIfBoostedAttr, BattlerTagType.CONFUSED)
.partial(), .soundBased(),
new AttackMove(Moves.TEMPER_FLARE, Type.FIRE, MoveCategory.PHYSICAL, 75, 100, 10, -1, 0, 9) new AttackMove(Moves.TEMPER_FLARE, Type.FIRE, MoveCategory.PHYSICAL, 75, 100, 10, -1, 0, 9)
.attr(MovePowerMultiplierAttr, (user, target, move) => user.getLastXMoves(2)[1]?.result === MoveResult.MISS || user.getLastXMoves(2)[1]?.result === MoveResult.FAIL ? 2 : 1), .attr(MovePowerMultiplierAttr, (user, target, move) => user.getLastXMoves(2)[1]?.result === MoveResult.MISS || user.getLastXMoves(2)[1]?.result === MoveResult.FAIL ? 2 : 1),
new AttackMove(Moves.SUPERCELL_SLAM, Type.ELECTRIC, MoveCategory.PHYSICAL, 100, 95, 15, -1, 0, 9) new AttackMove(Moves.SUPERCELL_SLAM, Type.ELECTRIC, MoveCategory.PHYSICAL, 100, 95, 15, -1, 0, 9)
@ -9344,4 +9362,4 @@ export function initMoves() {
selfStatLowerMoves.push(m.id); selfStatLowerMoves.push(m.id);
} }
}); });
} }

View File

@ -1157,7 +1157,7 @@ export const pokemonEvolutions: PokemonEvolutions = {
[Species.TANDEMAUS]: [ [Species.TANDEMAUS]: [
new SpeciesFormEvolution(Species.MAUSHOLD, "", "three", 25, null, new SpeciesEvolutionCondition(p => { new SpeciesFormEvolution(Species.MAUSHOLD, "", "three", 25, null, new SpeciesEvolutionCondition(p => {
let ret = false; let ret = false;
p.scene.executeWithSeedOffset(() => ret = !Utils.randSeedInt(4), p.id); p.scene.executeWithSeedOffset(() => ret = !Utils.randSeedInt(4, undefined, "Tandemaus form selection"), p.id);
return ret; return ret;
})), })),
new SpeciesEvolution(Species.MAUSHOLD, 25, null, null) new SpeciesEvolution(Species.MAUSHOLD, 25, null, null)
@ -1325,7 +1325,7 @@ export const pokemonEvolutions: PokemonEvolutions = {
new SpeciesFormEvolution(Species.DUDUNSPARCE, "", "three-segment", 32, null, new SpeciesEvolutionCondition(p => { new SpeciesFormEvolution(Species.DUDUNSPARCE, "", "three-segment", 32, null, new SpeciesEvolutionCondition(p => {
let ret = false; let ret = false;
if (p.moveset.filter(m => m?.moveId === Moves.HYPER_DRILL).length > 0) { if (p.moveset.filter(m => m?.moveId === Moves.HYPER_DRILL).length > 0) {
p.scene.executeWithSeedOffset(() => ret = !Utils.randSeedInt(4), p.id); p.scene.executeWithSeedOffset(() => ret = !Utils.randSeedInt(4, undefined, "Dudunsparce form selection"), p.id);
} }
return ret; return ret;
}), SpeciesWildEvolutionDelay.LONG), }), SpeciesWildEvolutionDelay.LONG),

View File

@ -761,7 +761,7 @@ export default class PokemonSpecies extends PokemonSpeciesForm implements Locali
return this.speciesId; return this.speciesId;
} }
const randValue = evolutionPool.size === 1 ? 0 : Utils.randSeedInt(totalWeight); const randValue = evolutionPool.size === 1 ? 0 : Utils.randSeedInt(totalWeight, undefined, "Random levelled species");
for (const weight of evolutionPool.keys()) { for (const weight of evolutionPool.keys()) {
if (randValue < weight) { if (randValue < weight) {
@ -3340,7 +3340,7 @@ export function getPokerusStarters(scene: BattleScene): PokemonSpecies[] {
date.setUTCHours(0, 0, 0, 0); date.setUTCHours(0, 0, 0, 0);
scene.executeWithSeedOffset(() => { scene.executeWithSeedOffset(() => {
while (pokerusStarters.length < starterCount) { while (pokerusStarters.length < starterCount) {
const randomSpeciesId = parseInt(Utils.randSeedItem(Object.keys(speciesStarters)), 10); const randomSpeciesId = parseInt(Utils.randSeedItem(Object.keys(speciesStarters), "Get Pokerus starters"), 10);
const species = getPokemonSpecies(randomSpeciesId); const species = getPokemonSpecies(randomSpeciesId);
if (!pokerusStarters.includes(species)) { if (!pokerusStarters.includes(species)) {
pokerusStarters.push(species); pokerusStarters.push(species);

View File

@ -994,7 +994,7 @@ function getGymLeaderPartyTemplate(scene: BattleScene) {
function getRandomPartyMemberFunc(speciesPool: Species[], trainerSlot: TrainerSlot = TrainerSlot.TRAINER, ignoreEvolution: boolean = false, postProcess?: (enemyPokemon: EnemyPokemon) => void): PartyMemberFunc { function getRandomPartyMemberFunc(speciesPool: Species[], trainerSlot: TrainerSlot = TrainerSlot.TRAINER, ignoreEvolution: boolean = false, postProcess?: (enemyPokemon: EnemyPokemon) => void): PartyMemberFunc {
return (scene: BattleScene, level: integer, strength: PartyMemberStrength) => { return (scene: BattleScene, level: integer, strength: PartyMemberStrength) => {
let species = Utils.randSeedItem(speciesPool); let species = Utils.randSeedItem(speciesPool, "Get random party member");
if (!ignoreEvolution) { if (!ignoreEvolution) {
species = getPokemonSpecies(species).getTrainerSpeciesForLevel(level, true, strength, scene.currentBattle.waveIndex); species = getPokemonSpecies(species).getTrainerSpeciesForLevel(level, true, strength, scene.currentBattle.waveIndex);
} }
@ -1015,9 +1015,9 @@ function getRandomTeraModifiers(party: EnemyPokemon[], count: integer, types?: T
const ret: PersistentModifier[] = []; const ret: PersistentModifier[] = [];
const partyMemberIndexes = new Array(party.length).fill(null).map((_, i) => i); const partyMemberIndexes = new Array(party.length).fill(null).map((_, i) => i);
for (let t = 0; t < Math.min(count, party.length); t++) { for (let t = 0; t < Math.min(count, party.length); t++) {
const randomIndex = Utils.randSeedItem(partyMemberIndexes); const randomIndex = Utils.randSeedItem(partyMemberIndexes, "Get random tera modifiers");
partyMemberIndexes.splice(partyMemberIndexes.indexOf(randomIndex), 1); partyMemberIndexes.splice(partyMemberIndexes.indexOf(randomIndex), 1);
ret.push(modifierTypes.TERA_SHARD().generateType([], [Utils.randSeedItem(types ? types : party[randomIndex].getTypes())])!.withIdFromFunc(modifierTypes.TERA_SHARD).newModifier(party[randomIndex]) as PersistentModifier); // TODO: is the bang correct? ret.push(modifierTypes.TERA_SHARD().generateType([], [Utils.randSeedItem(types ? types : party[randomIndex].getTypes(), "Selecting Tera Type")])!.withIdFromFunc(modifierTypes.TERA_SHARD).newModifier(party[randomIndex]) as PersistentModifier); // TODO: is the bang correct?
} }
return ret; return ret;
} }
@ -1868,7 +1868,7 @@ export const trainerConfigs: TrainerConfigs = {
p.setBoss(true, 2); p.setBoss(true, 2);
p.generateAndPopulateMoveset(); p.generateAndPopulateMoveset();
p.pokeball = PokeballType.MASTER_BALL; p.pokeball = PokeballType.MASTER_BALL;
p.formIndex = Utils.randSeedInt(5); p.formIndex = Utils.randSeedInt(5, undefined, "Random form for Genesect");
})) }))
.setPartyMemberFunc(1, getRandomPartyMemberFunc([ Species.BASCULEGION, Species.JELLICENT ], TrainerSlot.TRAINER, true, p => { .setPartyMemberFunc(1, getRandomPartyMemberFunc([ Species.BASCULEGION, Species.JELLICENT ], TrainerSlot.TRAINER, true, p => {
p.generateAndPopulateMoveset(); p.generateAndPopulateMoveset();

View File

@ -378,7 +378,7 @@ export function getRandomWeatherType(arena: any /* Importing from arena causes a
let totalWeight = 0; let totalWeight = 0;
weatherPool.forEach(w => totalWeight += w.weight); weatherPool.forEach(w => totalWeight += w.weight);
const rand = Utils.randSeedInt(totalWeight); const rand = Utils.randSeedInt(totalWeight, undefined, "Weather selection");
let w = 0; let w = 0;
for (const weather of weatherPool) { for (const weather of weatherPool) {
w += weather.weight; w += weather.weight;

View File

@ -90,7 +90,7 @@ export class Arena {
if (typeof luckValue !== "undefined") { if (typeof luckValue !== "undefined") {
luckModifier = luckValue * (isBoss ? 0.5 : 2); luckModifier = luckValue * (isBoss ? 0.5 : 2);
} }
const tierValue = Utils.randSeedInt(randVal - luckModifier); const tierValue = Utils.randSeedInt(randVal - luckModifier, undefined, "Selecting rarity tier for encounter");
let tier = !isBoss let tier = !isBoss
? tierValue >= 156 ? BiomePoolTier.COMMON : tierValue >= 32 ? BiomePoolTier.UNCOMMON : tierValue >= 6 ? BiomePoolTier.RARE : tierValue >= 1 ? BiomePoolTier.SUPER_RARE : BiomePoolTier.ULTRA_RARE ? tierValue >= 156 ? BiomePoolTier.COMMON : tierValue >= 32 ? BiomePoolTier.UNCOMMON : tierValue >= 6 ? BiomePoolTier.RARE : tierValue >= 1 ? BiomePoolTier.SUPER_RARE : BiomePoolTier.ULTRA_RARE
: tierValue >= 20 ? BiomePoolTier.BOSS : tierValue >= 6 ? BiomePoolTier.BOSS_RARE : tierValue >= 1 ? BiomePoolTier.BOSS_SUPER_RARE : BiomePoolTier.BOSS_ULTRA_RARE; : tierValue >= 20 ? BiomePoolTier.BOSS : tierValue >= 6 ? BiomePoolTier.BOSS_RARE : tierValue >= 1 ? BiomePoolTier.BOSS_SUPER_RARE : BiomePoolTier.BOSS_ULTRA_RARE;
@ -118,7 +118,7 @@ export class Arena {
if (!tierPool.length) { if (!tierPool.length) {
ret = this.scene.randomSpecies(waveIndex, level); ret = this.scene.randomSpecies(waveIndex, level);
} else { } else {
const entry = tierPool[Utils.randSeedInt(tierPool.length)]; const entry = tierPool[Utils.randSeedInt(tierPool.length, undefined, "Selecting rarity tier but for real this time")];
let species: Species; let species: Species;
if (typeof entry === "number") { if (typeof entry === "number") {
species = entry as Species; species = entry as Species;
@ -129,7 +129,7 @@ export class Arena {
if (level >= levelThreshold) { if (level >= levelThreshold) {
const speciesIds = entry[levelThreshold]; const speciesIds = entry[levelThreshold];
if (speciesIds.length > 1) { if (speciesIds.length > 1) {
species = speciesIds[Utils.randSeedInt(speciesIds.length)]; species = speciesIds[Utils.randSeedInt(speciesIds.length, undefined, "Randomly selecting encounter species")];
} else { } else {
species = speciesIds[0]; species = speciesIds[0];
} }
@ -175,7 +175,7 @@ export class Arena {
const isBoss = !!this.trainerPool[BiomePoolTier.BOSS].length const isBoss = !!this.trainerPool[BiomePoolTier.BOSS].length
&& this.scene.gameMode.isTrainerBoss(waveIndex, this.biomeType, this.scene.offsetGym); && this.scene.gameMode.isTrainerBoss(waveIndex, this.biomeType, this.scene.offsetGym);
console.log(isBoss, this.trainerPool); console.log(isBoss, this.trainerPool);
const tierValue = Utils.randSeedInt(!isBoss ? 512 : 64); const tierValue = Utils.randSeedInt(!isBoss ? 512 : 64, undefined, "Selecting random trainer");
let tier = !isBoss let tier = !isBoss
? tierValue >= 156 ? BiomePoolTier.COMMON : tierValue >= 32 ? BiomePoolTier.UNCOMMON : tierValue >= 6 ? BiomePoolTier.RARE : tierValue >= 1 ? BiomePoolTier.SUPER_RARE : BiomePoolTier.ULTRA_RARE ? tierValue >= 156 ? BiomePoolTier.COMMON : tierValue >= 32 ? BiomePoolTier.UNCOMMON : tierValue >= 6 ? BiomePoolTier.RARE : tierValue >= 1 ? BiomePoolTier.SUPER_RARE : BiomePoolTier.ULTRA_RARE
: tierValue >= 20 ? BiomePoolTier.BOSS : tierValue >= 6 ? BiomePoolTier.BOSS_RARE : tierValue >= 1 ? BiomePoolTier.BOSS_SUPER_RARE : BiomePoolTier.BOSS_ULTRA_RARE; : tierValue >= 20 ? BiomePoolTier.BOSS : tierValue >= 6 ? BiomePoolTier.BOSS_RARE : tierValue >= 1 ? BiomePoolTier.BOSS_SUPER_RARE : BiomePoolTier.BOSS_ULTRA_RARE;
@ -185,7 +185,7 @@ export class Arena {
tier--; tier--;
} }
const tierPool = this.trainerPool[tier] || []; const tierPool = this.trainerPool[tier] || [];
return !tierPool.length ? TrainerType.BREEDER : tierPool[Utils.randSeedInt(tierPool.length)]; return !tierPool.length ? TrainerType.BREEDER : tierPool[Utils.randSeedInt(tierPool.length, undefined, "Selecting trainer type")];
} }
getSpeciesFormIndex(species: PokemonSpecies): integer { getSpeciesFormIndex(species: PokemonSpecies): integer {
@ -303,7 +303,7 @@ export class Arena {
/** /**
* Sets weather to the override specified in overrides.ts * Sets weather to the override specified in overrides.ts
* @param weather new {@linkcode WeatherType} to set * @param weather new weather to set of type WeatherType
* @returns true to force trySetWeather to return true * @returns true to force trySetWeather to return true
*/ */
trySetWeatherOverride(weather: WeatherType): boolean { trySetWeatherOverride(weather: WeatherType): boolean {
@ -315,8 +315,8 @@ export class Arena {
/** /**
* Attempts to set a new weather to the battle * Attempts to set a new weather to the battle
* @param weather {@linkcode WeatherType} new {@linkcode WeatherType} to set * @param weather new weather to set of type WeatherType
* @param hasPokemonSource boolean if the new weather is from a pokemon * @param hasPokemonSource is the new weather from a pokemon
* @returns true if new weather set, false if no weather provided or attempting to set the same weather as currently in use * @returns true if new weather set, false if no weather provided or attempting to set the same weather as currently in use
*/ */
trySetWeather(weather: WeatherType, hasPokemonSource: boolean): boolean { trySetWeather(weather: WeatherType, hasPokemonSource: boolean): boolean {
@ -587,12 +587,6 @@ export class Arena {
this.ignoreAbilities = ignoreAbilities; this.ignoreAbilities = ignoreAbilities;
} }
/**
* Applies each `ArenaTag` in this Arena, based on which side (self, enemy, or both) is passed in as a parameter
* @param tagType Either an {@linkcode ArenaTagType} string, or an actual {@linkcode ArenaTag} class to filter which ones to apply
* @param side {@linkcode ArenaTagSide} which side's arena tags to apply
* @param args array of parameters that the called upon tags may need
*/
applyTagsForSide(tagType: ArenaTagType | Constructor<ArenaTag>, side: ArenaTagSide, ...args: unknown[]): void { applyTagsForSide(tagType: ArenaTagType | Constructor<ArenaTag>, side: ArenaTagSide, ...args: unknown[]): void {
let tags = typeof tagType === "string" let tags = typeof tagType === "string"
? this.tags.filter(t => t.tagType === tagType) ? this.tags.filter(t => t.tagType === tagType)
@ -603,28 +597,11 @@ export class Arena {
tags.forEach(t => t.apply(this, args)); tags.forEach(t => t.apply(this, args));
} }
/**
* Applies the specified tag to both sides (ie: both user and trainer's tag that match the Tag specified)
* by calling {@linkcode applyTagsForSide()}
* @param tagType Either an {@linkcode ArenaTagType} string, or an actual {@linkcode ArenaTag} class to filter which ones to apply
* @param args array of parameters that the called upon tags may need
*/
applyTags(tagType: ArenaTagType | Constructor<ArenaTag>, ...args: unknown[]): void { applyTags(tagType: ArenaTagType | Constructor<ArenaTag>, ...args: unknown[]): void {
this.applyTagsForSide(tagType, ArenaTagSide.BOTH, ...args); this.applyTagsForSide(tagType, ArenaTagSide.BOTH, ...args);
} }
/** addTag(tagType: ArenaTagType, turnCount: integer, sourceMove: Moves | undefined, sourceId: integer, side: ArenaTagSide = ArenaTagSide.BOTH, quiet: boolean = false, targetIndex?: BattlerIndex): boolean {
* Adds a new tag to the arena
* @param tagType {@linkcode ArenaTagType} the tag being added
* @param turnCount How many turns the tag lasts
* @param sourceMove {@linkcode Moves} the move the tag came from, or `undefined` if not from a move
* @param sourceId The ID of the pokemon in play the tag came from (see {@linkcode BattleScene.getPokemonById})
* @param side {@linkcode ArenaTagSide} which side(s) the tag applies to
* @param quiet If a message should be queued on screen to announce the tag being added
* @param targetIndex The {@linkcode BattlerIndex} of the target pokemon
* @returns `false` if there already exists a tag of this type in the Arena
*/
addTag(tagType: ArenaTagType, turnCount: number, sourceMove: Moves | undefined, sourceId: number, side: ArenaTagSide = ArenaTagSide.BOTH, quiet: boolean = false, targetIndex?: BattlerIndex): boolean {
const existingTag = this.getTagOnSide(tagType, side); const existingTag = this.getTagOnSide(tagType, side);
if (existingTag) { if (existingTag) {
existingTag.onOverlap(this); existingTag.onOverlap(this);
@ -637,7 +614,6 @@ export class Arena {
return false; return false;
} }
// creates a new tag object
const newTag = getArenaTag(tagType, turnCount || 0, sourceMove, sourceId, targetIndex, side); const newTag = getArenaTag(tagType, turnCount || 0, sourceMove, sourceId, targetIndex, side);
if (newTag) { if (newTag) {
this.tags.push(newTag); this.tags.push(newTag);
@ -651,11 +627,6 @@ export class Arena {
return true; return true;
} }
/**
* Attempts to get a tag from the Arena via {@linkcode getTagOnSide} that applies to both sides
* @param tagType The {@linkcode ArenaTagType} or {@linkcode ArenaTag} to get
* @returns either the {@linkcode ArenaTag}, or `undefined` if it isn't there
*/
getTag(tagType: ArenaTagType | Constructor<ArenaTag>): ArenaTag | undefined { getTag(tagType: ArenaTagType | Constructor<ArenaTag>): ArenaTag | undefined {
return this.getTagOnSide(tagType, ArenaTagSide.BOTH); return this.getTagOnSide(tagType, ArenaTagSide.BOTH);
} }
@ -664,35 +635,16 @@ export class Arena {
return !!this.getTag(tagType); return !!this.getTag(tagType);
} }
/**
* Attempts to get a tag from the Arena from a specific side (the tag passed in has to either apply to both sides, or the specific side only)
*
* eg: `MIST` only applies to the user's side, while `MUD_SPORT` applies to both user and enemy side
* @param tagType The {@linkcode ArenaTagType} or {@linkcode ArenaTag} to get
* @param side The {@linkcode ArenaTagSide} to look at
* @returns either the {@linkcode ArenaTag}, or `undefined` if it isn't there
*/
getTagOnSide(tagType: ArenaTagType | Constructor<ArenaTag>, side: ArenaTagSide): ArenaTag | undefined { getTagOnSide(tagType: ArenaTagType | Constructor<ArenaTag>, side: ArenaTagSide): ArenaTag | undefined {
return typeof(tagType) === "string" return typeof(tagType) === "string"
? this.tags.find(t => t.tagType === tagType && (side === ArenaTagSide.BOTH || t.side === ArenaTagSide.BOTH || t.side === side)) ? this.tags.find(t => t.tagType === tagType && (side === ArenaTagSide.BOTH || t.side === ArenaTagSide.BOTH || t.side === side))
: this.tags.find(t => t instanceof tagType && (side === ArenaTagSide.BOTH || t.side === ArenaTagSide.BOTH || t.side === side)); : this.tags.find(t => t instanceof tagType && (side === ArenaTagSide.BOTH || t.side === ArenaTagSide.BOTH || t.side === side));
} }
/**
* Uses {@linkcode findTagsOnSide} to filter (using the parameter function) for specific tags that apply to both sides
* @param tagPredicate a function mapping {@linkcode ArenaTag}s to `boolean`s
* @returns array of {@linkcode ArenaTag}s from which the Arena's tags return true and apply to both sides
*/
findTags(tagPredicate: (t: ArenaTag) => boolean): ArenaTag[] { findTags(tagPredicate: (t: ArenaTag) => boolean): ArenaTag[] {
return this.findTagsOnSide(tagPredicate, ArenaTagSide.BOTH); return this.findTagsOnSide(tagPredicate, ArenaTagSide.BOTH);
} }
/**
* Returns specific tags from the arena that pass the `tagPredicate` function passed in as a parameter, and apply to the given side
* @param tagPredicate a function mapping {@linkcode ArenaTag}s to `boolean`s
* @param side The {@linkcode ArenaTagSide} to look at
* @returns array of {@linkcode ArenaTag}s from which the Arena's tags return `true` and apply to the given side
*/
findTagsOnSide(tagPredicate: (t: ArenaTag) => boolean, side: ArenaTagSide): ArenaTag[] { findTagsOnSide(tagPredicate: (t: ArenaTag) => boolean, side: ArenaTagSide): ArenaTag[] {
return this.tags.filter(t => tagPredicate(t) && (side === ArenaTagSide.BOTH || t.side === ArenaTagSide.BOTH || t.side === side)); return this.tags.filter(t => tagPredicate(t) && (side === ArenaTagSide.BOTH || t.side === ArenaTagSide.BOTH || t.side === side));
} }
@ -922,7 +874,7 @@ export class ArenaBase extends Phaser.GameObjects.Container {
if (!this.player) { if (!this.player) {
(this.scene as BattleScene).executeWithSeedOffset(() => { (this.scene as BattleScene).executeWithSeedOffset(() => {
this.propValue = propValue === undefined this.propValue = propValue === undefined
? hasProps ? Utils.randSeedInt(8) : 0 ? hasProps ? Utils.randSeedInt(8, undefined, "Selecting biome prop(?)") : 0
: propValue; : propValue;
this.props.forEach((prop, p) => { this.props.forEach((prop, p) => {
const propKey = `${biomeKey}_b${hasProps ? `_${p + 1}` : ""}`; const propKey = `${biomeKey}_b${hasProps ? `_${p + 1}` : ""}`;

View File

@ -31,7 +31,7 @@ export default class PokemonSpriteSparkleHandler {
const parent = (pokemon || s).parentContainer; const parent = (pokemon || s).parentContainer;
const texture = s.texture; const texture = s.texture;
const [ width, height ] = [ texture.source[0].width, texture.source[0].height ]; const [ width, height ] = [ texture.source[0].width, texture.source[0].height ];
const [ pixelX, pixelY ] = [ Utils.randInt(width), Utils.randInt(height) ]; const [ pixelX, pixelY ] = [ Utils.randInt(width, undefined, "Pixel X"), Utils.randInt(height, undefined, "Pixel Y") ];
const ratioX = s.width / width; const ratioX = s.width / width;
const ratioY = s.height / height; const ratioY = s.height / height;
const pixel = texture.manager.getPixel(pixelX, pixelY, texture.key, "__BASE"); const pixel = texture.manager.getPixel(pixelX, pixelY, texture.key, "__BASE");

View File

@ -156,8 +156,8 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
this.abilityIndex = abilityIndex; // Use the provided ability index if it is defined this.abilityIndex = abilityIndex; // Use the provided ability index if it is defined
} else { } else {
// If abilityIndex is not provided, determine it based on species and hidden ability // If abilityIndex is not provided, determine it based on species and hidden ability
const hasHiddenAbility = !Utils.randSeedInt(hiddenAbilityChance.value); const hasHiddenAbility = !Utils.randSeedInt(hiddenAbilityChance.value, undefined, "Hidden Ability chance");
const randAbilityIndex = Utils.randSeedInt(2); const randAbilityIndex = Utils.randSeedInt(2, undefined, "Selecting ability index");
if (species.abilityHidden && hasHiddenAbility) { if (species.abilityHidden && hasHiddenAbility) {
// If the species has a hidden ability and the hidden ability is present // If the species has a hidden ability and the hidden ability is present
this.abilityIndex = 2; this.abilityIndex = 2;
@ -210,7 +210,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
this.fusionLuck = dataSource.fusionLuck; this.fusionLuck = dataSource.fusionLuck;
this.usedTMs = dataSource.usedTMs ?? []; this.usedTMs = dataSource.usedTMs ?? [];
} else { } else {
this.id = Utils.randSeedInt(4294967296); this.id = Utils.randSeedInt(4294967296, undefined, "Generating a Pokemon ID to create Pokemon's IVs");
this.ivs = ivs || Utils.getIvsFromId(this.id); this.ivs = ivs || Utils.getIvsFromId(this.id);
if (this.gender === undefined) { if (this.gender === undefined) {
@ -923,7 +923,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
if (naturePool === undefined) { if (naturePool === undefined) {
naturePool = Utils.getEnumValues(Nature); naturePool = Utils.getEnumValues(Nature);
} }
const nature = naturePool[Utils.randSeedInt(naturePool.length)]; const nature = naturePool[Utils.randSeedInt(naturePool.length, undefined, "Random nature")];
this.setNature(nature); this.setNature(nature);
} }
@ -1451,26 +1451,22 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
} }
let multiplier = types.map(defType => { let multiplier = types.map(defType => {
const multiplier = new Utils.NumberHolder(getTypeDamageMultiplier(moveType, defType));
applyChallenges(this.scene.gameMode, ChallengeType.TYPE_EFFECTIVENESS, multiplier);
if (source) { if (source) {
const ignoreImmunity = new Utils.BooleanHolder(false); const ignoreImmunity = new Utils.BooleanHolder(false);
if (source.isActive(true) && source.hasAbilityWithAttr(IgnoreTypeImmunityAbAttr)) { if (source.isActive(true) && source.hasAbilityWithAttr(IgnoreTypeImmunityAbAttr)) {
applyAbAttrs(IgnoreTypeImmunityAbAttr, source, ignoreImmunity, simulated, moveType, defType); applyAbAttrs(IgnoreTypeImmunityAbAttr, source, ignoreImmunity, simulated, moveType, defType);
} }
if (ignoreImmunity.value) { if (ignoreImmunity.value) {
if (multiplier.value === 0) { return 1;
return 1;
}
} }
const exposedTags = this.findTags(tag => tag instanceof ExposedTag) as ExposedTag[]; const exposedTags = this.findTags(tag => tag instanceof ExposedTag) as ExposedTag[];
if (exposedTags.some(t => t.ignoreImmunity(defType, moveType))) { if (exposedTags.some(t => t.ignoreImmunity(defType, moveType))) {
if (multiplier.value === 0) { return 1;
return 1;
}
} }
} }
const multiplier = new Utils.NumberHolder(getTypeDamageMultiplier(moveType, defType));
applyChallenges(this.scene.gameMode, ChallengeType.TYPE_EFFECTIVENESS, multiplier);
return multiplier.value; return multiplier.value;
}).reduce((acc, cur) => acc * cur, 1) as TypeDamageMultiplier; }).reduce((acc, cur) => acc * cur, 1) as TypeDamageMultiplier;
@ -1717,7 +1713,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
if (!this.shiny || (!variantData.hasOwnProperty(variantDataIndex) && !variantData.hasOwnProperty(this.species.speciesId))) { if (!this.shiny || (!variantData.hasOwnProperty(variantDataIndex) && !variantData.hasOwnProperty(this.species.speciesId))) {
return 0; return 0;
} }
const rand = Utils.randSeedInt(10); const rand = Utils.randSeedInt(10, undefined, "Random variant selection");
if (rand >= 4) { if (rand >= 4) {
return 0; // 6/10 return 0; // 6/10
} else if (rand >= 1) { } else if (rand >= 1) {
@ -1733,8 +1729,8 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
this.scene.applyModifiers(HiddenAbilityRateBoosterModifier, true, hiddenAbilityChance); this.scene.applyModifiers(HiddenAbilityRateBoosterModifier, true, hiddenAbilityChance);
} }
const hasHiddenAbility = !Utils.randSeedInt(hiddenAbilityChance.value); const hasHiddenAbility = !Utils.randSeedInt(hiddenAbilityChance.value, undefined, "Whether the Pokemon has its HA or not");
const randAbilityIndex = Utils.randSeedInt(2); const randAbilityIndex = Utils.randSeedInt(2, undefined, "Ability slot (if no HA)");
const filter = !forStarter ? this.species.getCompatibleFusionSpeciesFilter() const filter = !forStarter ? this.species.getCompatibleFusionSpeciesFilter()
: species => { : species => {
@ -1907,7 +1903,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
if (stabMovePool.length) { if (stabMovePool.length) {
const totalWeight = stabMovePool.reduce((v, m) => v + m[1], 0); const totalWeight = stabMovePool.reduce((v, m) => v + m[1], 0);
let rand = Utils.randSeedInt(totalWeight); let rand = Utils.randSeedInt(totalWeight, undefined, "Selecting a STAB move to include");
let index = 0; let index = 0;
while (rand > stabMovePool[index][1]) { while (rand > stabMovePool[index][1]) {
rand -= stabMovePool[index++][1]; rand -= stabMovePool[index++][1];
@ -1918,7 +1914,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
const attackMovePool = baseWeights.filter(m => allMoves[m[0]].category !== MoveCategory.STATUS); const attackMovePool = baseWeights.filter(m => allMoves[m[0]].category !== MoveCategory.STATUS);
if (attackMovePool.length) { if (attackMovePool.length) {
const totalWeight = attackMovePool.reduce((v, m) => v + m[1], 0); const totalWeight = attackMovePool.reduce((v, m) => v + m[1], 0);
let rand = Utils.randSeedInt(totalWeight); let rand = Utils.randSeedInt(totalWeight, undefined, "Selecting a damage dealing move to include");
let index = 0; let index = 0;
while (rand > attackMovePool[index][1]) { while (rand > attackMovePool[index][1]) {
rand -= attackMovePool[index++][1]; rand -= attackMovePool[index++][1];
@ -1937,7 +1933,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
movePool = baseWeights.filter(m => !this.moveset.some(mo => m[0] === mo?.moveId)); movePool = baseWeights.filter(m => !this.moveset.some(mo => m[0] === mo?.moveId));
} }
const totalWeight = movePool.reduce((v, m) => v + m[1], 0); const totalWeight = movePool.reduce((v, m) => v + m[1], 0);
let rand = Utils.randSeedInt(totalWeight); let rand = Utils.randSeedInt(totalWeight, undefined, "Selecting moves");
let index = 0; let index = 0;
while (rand > movePool[index][1]) { while (rand > movePool[index][1]) {
rand -= movePool[index++][1]; rand -= movePool[index++][1];
@ -3646,7 +3642,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: integer, min: integer = 0, reason: string = "Pokémon randSeedInt"): integer { randSeedInt(range: integer, min: integer = 0, reason?: string): integer {
return this.scene.currentBattle return this.scene.currentBattle
? this.scene.randBattleSeedInt(range, min, reason) ? this.scene.randBattleSeedInt(range, min, reason)
: Utils.randSeedInt(range, min, reason); : Utils.randSeedInt(range, min, reason);
@ -3658,7 +3654,7 @@ 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: integer, max: integer, reason: string = "Pokémon randSeedInt"): integer { randSeedIntRange(min: integer, max: integer, reason?: string): integer {
return this.randSeedInt((max - min) + 1, min, reason); return this.randSeedInt((max - min) + 1, min, reason);
} }
@ -4269,6 +4265,7 @@ export class EnemyPokemon extends Pokemon {
* @returns this Pokemon's next move in the format {move, moveTargets} * @returns this Pokemon's next move in the format {move, moveTargets}
*/ */
getNextMove(): QueuedMove { getNextMove(): QueuedMove {
console.log("Starting getNextMove() for " + this.name)
// If this Pokemon has a move already queued, return it. // If this Pokemon has a move already queued, return it.
const queuedMove = this.getMoveQueue().length const queuedMove = this.getMoveQueue().length
? this.getMoveset().find(m => m?.moveId === this.getMoveQueue()[0].move) ? this.getMoveset().find(m => m?.moveId === this.getMoveQueue()[0].move)
@ -4281,9 +4278,11 @@ export class EnemyPokemon extends Pokemon {
this.flyout.setText(i) this.flyout.setText(i)
} }
} }
console.log(" Move was already selected")
return { move: queuedMove.moveId, targets: this.getMoveQueue()[0].targets, ignorePP: this.getMoveQueue()[0].ignorePP }; return { move: queuedMove.moveId, targets: this.getMoveQueue()[0].targets, ignorePP: this.getMoveQueue()[0].ignorePP };
} else { } else {
this.getMoveQueue().shift(); this.getMoveQueue().shift();
console.log(" Selected move cannot be used")
return this.getNextMove(); return this.getNextMove();
} }
} }
@ -4295,7 +4294,8 @@ export class EnemyPokemon extends Pokemon {
// If there's only 1 move in the move pool, use it. // If there's only 1 move in the move pool, use it.
if (movePool.length === 1) { if (movePool.length === 1) {
this.flyout.setText(this.getMoveset().indexOf(movePool[0])) this.flyout.setText(this.getMoveset().indexOf(movePool[0]))
return { move: movePool[0]!.moveId, targets: this.getNextTargets(movePool[0]!.moveId) }; console.log(" Only one move to select")
return { move: movePool[0]!.moveId, targets: this.getNextTargets(movePool[0]!.moveId) }; // TODO: are the bangs correct?
} }
// If a move is forced because of Encore, use it. // If a move is forced because of Encore, use it.
const encoreTag = this.getTag(EncoreTag) as EncoreTag; const encoreTag = this.getTag(EncoreTag) as EncoreTag;
@ -4303,6 +4303,7 @@ export class EnemyPokemon extends Pokemon {
const encoreMove = movePool.find(m => m?.moveId === encoreTag.moveId); const encoreMove = movePool.find(m => m?.moveId === encoreTag.moveId);
if (encoreMove) { if (encoreMove) {
this.flyout.setText(this.getMoveset().indexOf(encoreMove)) this.flyout.setText(this.getMoveset().indexOf(encoreMove))
console.log(" Locked into Encore")
return { move: encoreMove.moveId, targets: this.getNextTargets(encoreMove.moveId) }; return { move: encoreMove.moveId, targets: this.getNextTargets(encoreMove.moveId) };
} }
} }
@ -4311,6 +4312,7 @@ export class EnemyPokemon extends Pokemon {
var i = this.scene.randBattleSeedInt(movePool.length, undefined, "Move selection roll (RANDOM)") var i = this.scene.randBattleSeedInt(movePool.length, undefined, "Move selection roll (RANDOM)")
const moveId = movePool[i]!.moveId; const moveId = movePool[i]!.moveId;
this.flyout.setText(i) this.flyout.setText(i)
case AiType.RANDOM: // No enemy should spawn with this AI type in-game
return { move: moveId, targets: this.getNextTargets(moveId) }; return { move: moveId, targets: this.getNextTargets(moveId) };
case AiType.SMART_RANDOM: case AiType.SMART_RANDOM:
case AiType.SMART: case AiType.SMART:
@ -4392,22 +4394,25 @@ export class EnemyPokemon extends Pokemon {
}); });
let r = 0; let r = 0;
if (this.aiType === AiType.SMART_RANDOM) { if (this.aiType === AiType.SMART_RANDOM) {
while (r < sortedMovePool.length - 1 && this.scene.randBattleSeedInt(8, undefined, "Move selection roll (SMART_RANDOM)") >= 5) { // Has a 5/8 chance to select the best move, and a 3/8 chance to advance to the next best move (and repeat this roll)
while (r < sortedMovePool.length - 1 && this.scene.randBattleSeedInt(8, undefined, "Smart-Random AI Move Selection") >= 5) {
r++; r++;
} }
} else if (this.aiType === AiType.SMART) { } else if (this.aiType === AiType.SMART) {
// The chance to advance to the next best move increases when the compared moves' scores are closer to each other. // The chance to advance to the next best move increases when the compared moves' scores are closer to each other.
while (r < sortedMovePool.length - 1 && (moveScores[movePool.indexOf(sortedMovePool[r + 1])] / moveScores[movePool.indexOf(sortedMovePool[r])]) >= 0 while (r < sortedMovePool.length - 1 && (moveScores[movePool.indexOf(sortedMovePool[r + 1])] / moveScores[movePool.indexOf(sortedMovePool[r])]) >= 0
&& this.scene.randBattleSeedInt(100, undefined, "Move selection roll (SMART)") < Math.round((moveScores[movePool.indexOf(sortedMovePool[r + 1])] / moveScores[movePool.indexOf(sortedMovePool[r])]) * 50)) { && this.scene.randBattleSeedInt(100, undefined, "Smart AI Move Selection") < Math.round((moveScores[movePool.indexOf(sortedMovePool[r + 1])] / moveScores[movePool.indexOf(sortedMovePool[r])]) * 50)) {
r++; r++;
} }
} }
console.log(movePool.map(m => m!.getName()), moveScores, r, sortedMovePool.map(m => m!.getName())); console.log(movePool.map(m => m!.getName()), moveScores, r, sortedMovePool.map(m => m!.getName()));
this.flyout.setText(movePool.indexOf(sortedMovePool[r])) this.flyout.setText(movePool.indexOf(sortedMovePool[r]))
console.log(" Selected " + sortedMovePool[r]!.getName())
return { move: sortedMovePool[r]!.moveId, targets: moveTargets[sortedMovePool[r]!.moveId] }; return { move: sortedMovePool[r]!.moveId, targets: moveTargets[sortedMovePool[r]!.moveId] };
} }
} }
this.flyout.setText() this.flyout.setText()
console.log(" Selected Struggle")
return { move: Moves.STRUGGLE, targets: this.getNextTargets(Moves.STRUGGLE) }; return { move: Moves.STRUGGLE, targets: this.getNextTargets(Moves.STRUGGLE) };
} }
@ -4417,10 +4422,12 @@ export class EnemyPokemon extends Pokemon {
* @returns The indexes of the Pokemon the given move would target * @returns The indexes of the Pokemon the given move would target
*/ */
getNextTargets(moveId: Moves): BattlerIndex[] { getNextTargets(moveId: Moves): BattlerIndex[] {
console.log("Starting getNextTargets() for " + this.name + " with move " + Utils.getEnumKeys(Moves)[moveId])
const moveTargets = getMoveTargets(this, moveId); const moveTargets = getMoveTargets(this, moveId);
const targets = this.scene.getField(true).filter(p => moveTargets.targets.indexOf(p.getBattlerIndex()) > -1); const targets = this.scene.getField(true).filter(p => moveTargets.targets.indexOf(p.getBattlerIndex()) > -1);
// If the move is multi-target, return all targets' indexes // If the move is multi-target, return all targets' indexes
if (moveTargets.multiple) { if (moveTargets.multiple) {
console.log(" Multi-target move")
return targets.map(p => p.getBattlerIndex()); return targets.map(p => p.getBattlerIndex());
} }
@ -4444,9 +4451,10 @@ export class EnemyPokemon extends Pokemon {
// Set target to BattlerIndex.ATTACKER when using a counter move // Set target to BattlerIndex.ATTACKER when using a counter move
// This is the same as when the player does so // This is the same as when the player does so
if (move.hasAttr(CounterDamageAttr)) { if (move.hasAttr(CounterDamageAttr)) {
console.log(" Counter move")
return [BattlerIndex.ATTACKER]; return [BattlerIndex.ATTACKER];
} }
console.log(" No targets available")
return []; return [];
} }
@ -4480,7 +4488,7 @@ export class EnemyPokemon extends Pokemon {
* then select the first target whose cumulative weight (with all previous targets' weights) * then select the first target whose cumulative weight (with all previous targets' weights)
* is greater than that random number. * is greater than that random number.
*/ */
const randValue = this.scene.randBattleSeedInt(totalWeight, undefined, "Random target selection"); const randValue = this.scene.randBattleSeedInt(totalWeight, undefined, "Random move target");
let targetIndex: integer = 0; let targetIndex: integer = 0;
thresholds.every((t, i) => { thresholds.every((t, i) => {
@ -4491,6 +4499,8 @@ export class EnemyPokemon extends Pokemon {
targetIndex = i; targetIndex = i;
return false; return false;
}); });
console.log("Target selection thresholds", thresholds)
console.log(" Randomly selected position " + sortedBenefitScores[targetIndex][0] + " as target")
return [ sortedBenefitScores[targetIndex][0] ]; return [ sortedBenefitScores[targetIndex][0] ];
} }
@ -4600,7 +4610,7 @@ export class EnemyPokemon extends Pokemon {
} }
// Pick a random stat from the leftover stats to increase its stages // Pick a random stat from the leftover stats to increase its stages
const randInt = Utils.randSeedInt(totalWeight); const randInt = Utils.randSeedInt(totalWeight, undefined, "Random stat to raise from breaking a segment");
for (const i in statThresholds) { for (const i in statThresholds) {
if (randInt < statThresholds[i]) { if (randInt < statThresholds[i]) {
boostedStat = leftoverStats[i]; boostedStat = leftoverStats[i];

View File

@ -45,7 +45,7 @@ export default class Trainer extends Phaser.GameObjects.Container {
this.config.partyTemplates.length - 1); this.config.partyTemplates.length - 1);
if (trainerNamePools.hasOwnProperty(trainerType)) { if (trainerNamePools.hasOwnProperty(trainerType)) {
const namePool = trainerNamePools[trainerType]; const namePool = trainerNamePools[trainerType];
this.name = name || Utils.randSeedItem(Array.isArray(namePool[0]) ? namePool[variant === TrainerVariant.FEMALE ? 1 : 0] : namePool); this.name = name || Utils.randSeedItem(Array.isArray(namePool[0]) ? namePool[variant === TrainerVariant.FEMALE ? 1 : 0] : namePool, "Trainer name 1");
if (variant === TrainerVariant.DOUBLE) { if (variant === TrainerVariant.DOUBLE) {
if (this.config.doubleOnly) { if (this.config.doubleOnly) {
if (partnerName) { if (partnerName) {
@ -54,7 +54,7 @@ export default class Trainer extends Phaser.GameObjects.Container {
[this.name, this.partnerName] = this.name.split(" & "); [this.name, this.partnerName] = this.name.split(" & ");
} }
} else { } else {
this.partnerName = partnerName || Utils.randSeedItem(Array.isArray(namePool[0]) ? namePool[1] : namePool); this.partnerName = partnerName || Utils.randSeedItem(Array.isArray(namePool[0]) ? namePool[1] : namePool, "Trainer name 2");
} }
} }
} }
@ -487,7 +487,7 @@ export default class Trainer extends Phaser.GameObjects.Container {
let species: PokemonSpecies; let species: PokemonSpecies;
if (this.config.speciesPools) { if (this.config.speciesPools) {
const tierValue = Utils.randSeedInt(512); const tierValue = Utils.randSeedInt(512, undefined, "Randomly selecting species for trainer party");
let tier = tierValue >= 156 ? TrainerPoolTier.COMMON : tierValue >= 32 ? TrainerPoolTier.UNCOMMON : tierValue >= 6 ? TrainerPoolTier.RARE : tierValue >= 1 ? TrainerPoolTier.SUPER_RARE : TrainerPoolTier.ULTRA_RARE; let tier = tierValue >= 156 ? TrainerPoolTier.COMMON : tierValue >= 32 ? TrainerPoolTier.UNCOMMON : tierValue >= 6 ? TrainerPoolTier.RARE : tierValue >= 1 ? TrainerPoolTier.SUPER_RARE : TrainerPoolTier.ULTRA_RARE;
console.log(TrainerPoolTier[tier]); console.log(TrainerPoolTier[tier]);
while (!this.config.speciesPools.hasOwnProperty(tier) || !this.config.speciesPools[tier].length) { while (!this.config.speciesPools.hasOwnProperty(tier) || !this.config.speciesPools[tier].length) {
@ -495,7 +495,7 @@ export default class Trainer extends Phaser.GameObjects.Container {
tier--; tier--;
} }
const tierPool = this.config.speciesPools[tier]; const tierPool = this.config.speciesPools[tier];
species = getPokemonSpecies(Utils.randSeedItem(tierPool)); species = getPokemonSpecies(Utils.randSeedItem(tierPool, "Random party member species"));
} else { } else {
species = this.scene.randomSpecies(battle.waveIndex, level, false, this.config.speciesFilter); species = this.scene.randomSpecies(battle.waveIndex, level, false, this.config.speciesFilter);
} }
@ -587,7 +587,7 @@ export default class Trainer extends Phaser.GameObjects.Container {
if (maxScorePartyMemberIndexes.length > 1) { if (maxScorePartyMemberIndexes.length > 1) {
let rand: integer; let rand: integer;
this.scene.executeWithSeedOffset(() => rand = Utils.randSeedInt(maxScorePartyMemberIndexes.length), this.scene.currentBattle.turn << 2); this.scene.executeWithSeedOffset(() => rand = Utils.randSeedInt(maxScorePartyMemberIndexes.length, undefined, "Randomly selecting who to send out next"), this.scene.currentBattle.turn << 2);
return maxScorePartyMemberIndexes[rand!]; return maxScorePartyMemberIndexes[rand!];
} }

View File

@ -161,7 +161,7 @@ export class GameMode implements GameModeConfig {
} else if (w < waveIndex) { } else if (w < waveIndex) {
arena.scene.executeWithSeedOffset(() => { arena.scene.executeWithSeedOffset(() => {
const waveTrainerChance = arena.getTrainerChance(); const waveTrainerChance = arena.getTrainerChance();
if (!Utils.randSeedInt(waveTrainerChance)) { if (!Utils.randSeedInt(waveTrainerChance, undefined, "Random chance of wave being a Trainer Battle")) {
allowTrainerBattle = false; allowTrainerBattle = false;
} }
}, w); }, w);
@ -171,7 +171,7 @@ export class GameMode implements GameModeConfig {
} }
} }
} }
return Boolean(allowTrainerBattle && trainerChance && !Utils.randSeedInt(trainerChance)); return Boolean(allowTrainerBattle && trainerChance && !Utils.randSeedInt(trainerChance, undefined, "Random chance of wave being a Trainer Battle"));
} }
return false; return false;
} }
@ -189,7 +189,7 @@ export class GameMode implements GameModeConfig {
if (this.isDaily && this.isWaveFinal(waveIndex)) { if (this.isDaily && this.isWaveFinal(waveIndex)) {
const allFinalBossSpecies = allSpecies.filter(s => (s.subLegendary || s.legendary || s.mythical) const allFinalBossSpecies = allSpecies.filter(s => (s.subLegendary || s.legendary || s.mythical)
&& s.baseTotal >= 600 && s.speciesId !== Species.ETERNATUS && s.speciesId !== Species.ARCEUS); && s.baseTotal >= 600 && s.speciesId !== Species.ETERNATUS && s.speciesId !== Species.ARCEUS);
return Utils.randSeedItem(allFinalBossSpecies); return Utils.randSeedItem(allFinalBossSpecies, "Final Boss override");
} }
return null; return null;

View File

@ -1063,7 +1063,7 @@ class AttackTypeBoosterModifierTypeGenerator extends ModifierTypeGenerator {
let type: Type; let type: Type;
const randInt = Utils.randSeedInt(totalWeight); const randInt = Utils.randSeedInt(totalWeight, undefined, "Generating a move type booster");
let weight = 0; let weight = 0;
var fullweights: integer[] = [] var fullweights: integer[] = []
@ -1104,7 +1104,7 @@ class BaseStatBoosterModifierTypeGenerator extends ModifierTypeGenerator {
if (pregenArgs) { if (pregenArgs) {
return new BaseStatBoosterModifierType(pregenArgs[0]); return new BaseStatBoosterModifierType(pregenArgs[0]);
} }
const randStat: PermanentStat = Utils.randSeedInt(Stat.SPD + 1); const randStat: PermanentStat = Utils.randSeedInt(Stat.SPD + 1, undefined, "Randomly generating a Vitamin");
return new BaseStatBoosterModifierType(randStat); return new BaseStatBoosterModifierType(randStat);
}); });
} }
@ -1125,7 +1125,7 @@ class TempStatStageBoosterModifierTypeGenerator extends ModifierTypeGenerator {
if (pregenArgs && (pregenArgs.length === 1) && TEMP_BATTLE_STATS.includes(pregenArgs[0])) { if (pregenArgs && (pregenArgs.length === 1) && TEMP_BATTLE_STATS.includes(pregenArgs[0])) {
return new TempStatStageBoosterModifierType(pregenArgs[0]); return new TempStatStageBoosterModifierType(pregenArgs[0]);
} }
const randStat: TempBattleStat = Utils.randSeedInt(Stat.ACC, Stat.ATK); const randStat: TempBattleStat = Utils.randSeedInt(Stat.ACC, Stat.ATK, "Randomly choosing an X item");
return new TempStatStageBoosterModifierType(randStat); return new TempStatStageBoosterModifierType(randStat);
}); });
} }
@ -1190,7 +1190,7 @@ class SpeciesStatBoosterModifierTypeGenerator extends ModifierTypeGenerator {
} }
if (totalWeight !== 0) { if (totalWeight !== 0) {
const randInt = Utils.randSeedInt(totalWeight, 1); const randInt = Utils.randSeedInt(totalWeight, 1, "Randomly choosing a species booster");
let weight = 0; let weight = 0;
var fullweights: integer[] = [] var fullweights: integer[] = []
@ -1232,9 +1232,9 @@ class TmModifierTypeGenerator extends ModifierTypeGenerator {
if (!tierUniqueCompatibleTms.length) { if (!tierUniqueCompatibleTms.length) {
return null; return null;
} }
const randTmIndex = Utils.randSeedInt(tierUniqueCompatibleTms.length);
//console.log(tierUniqueCompatibleTms.map((v, i) => i == randTmIndex ? `> ${Utils.getEnumKeys(Moves)[v].toUpperCase() + Utils.getEnumKeys(Moves)[v].substring(1).toLowerCase()} <` : `${Utils.getEnumKeys(Moves)[v].toUpperCase() + Utils.getEnumKeys(Moves)[v].substring(1).toLowerCase()}`)) //console.log(tierUniqueCompatibleTms.map((v, i) => i == randTmIndex ? `> ${Utils.getEnumKeys(Moves)[v].toUpperCase() + Utils.getEnumKeys(Moves)[v].substring(1).toLowerCase()} <` : `${Utils.getEnumKeys(Moves)[v].toUpperCase() + Utils.getEnumKeys(Moves)[v].substring(1).toLowerCase()}`))
return new TmModifierType(tierUniqueCompatibleTms[randTmIndex], tier); return new TmModifierType(tierUniqueCompatibleTms[randTmIndex], tier);
const randTmIndex = Utils.randSeedInt(tierUniqueCompatibleTms.length, undefined, "Choosing a TM to give");
}); });
} }
} }
@ -1263,9 +1263,7 @@ class EvolutionItemModifierTypeGenerator extends ModifierTypeGenerator {
return null; return null;
} }
const idx = Utils.randSeedInt(evolutionItemPool.length) return new EvolutionItemModifierType(evolutionItemPool[Utils.randSeedInt(evolutionItemPool.length, undefined, "Choosing an evolution item")]!); // TODO: is the bang correct?
// console.log(evolutionItemPool.map((v, i) => i == idx ? `> ${Utils.getEnumKeys(EvolutionItem)[v!]} <` : Utils.getEnumKeys(EvolutionItem)[v!]))
return new EvolutionItemModifierType(evolutionItemPool[idx]!); // TODO: is the bang correct?
}); });
} }
} }
@ -1321,9 +1319,7 @@ class FormChangeItemModifierTypeGenerator extends ModifierTypeGenerator {
return null; return null;
} }
const idx = Utils.randSeedInt(formChangeItemPool.length) return new FormChangeItemModifierType(formChangeItemPool[Utils.randSeedInt(formChangeItemPool.length, undefined, "Choosing a form change item")]);
// console.log(formChangeItemPool.map((v, i) => i == idx ? `> ${Utils.getEnumKeys(FormChangeItem)[v!]} <` : Utils.getEnumKeys(FormChangeItem)[v!]))
return new FormChangeItemModifierType(formChangeItemPool[idx]!);
}); });
} }
} }
@ -1584,7 +1580,7 @@ export const modifierTypes = {
if (pregenArgs && (pregenArgs.length === 1) && (pregenArgs[0] in Nature)) { if (pregenArgs && (pregenArgs.length === 1) && (pregenArgs[0] in Nature)) {
return new PokemonNatureChangeModifierType(pregenArgs[0] as Nature); return new PokemonNatureChangeModifierType(pregenArgs[0] as Nature);
} }
return new PokemonNatureChangeModifierType(Utils.randSeedInt(Utils.getEnumValues(Nature).length) as Nature); return new PokemonNatureChangeModifierType(Utils.randSeedInt(Utils.getEnumValues(Nature).length, undefined, "Choosing a Mint") as Nature);
}), }),
TERA_SHARD: () => new ModifierTypeGenerator((party: Pokemon[], pregenArgs?: any[]) => { TERA_SHARD: () => new ModifierTypeGenerator((party: Pokemon[], pregenArgs?: any[]) => {
@ -1595,11 +1591,11 @@ export const modifierTypes = {
return null; return null;
} }
let type: Type; let type: Type;
if (!Utils.randSeedInt(3)) { if (!Utils.randSeedInt(3, undefined, "Choosing whether to give a type from your party")) {
const partyMemberTypes = party.map(p => p.getTypes(false, false, true)).flat(); const partyMemberTypes = party.map(p => p.getTypes(false, false, true)).flat();
type = Utils.randSeedItem(partyMemberTypes); type = Utils.randSeedItem(partyMemberTypes, "Choosing a Tera Shard type to give");
} else { } else {
type = Utils.randSeedInt(64) ? Utils.randSeedInt(18) as Type : Type.STELLAR; type = Utils.randSeedInt(64, undefined, "Choosing whether to give a Stellar Shard") ? Utils.randSeedInt(18, undefined, "Choosing a type (man I have no patience)") as Type : Type.STELLAR;
} }
return new TerastallizeModifierType(type); return new TerastallizeModifierType(type);
}), }),
@ -1610,7 +1606,7 @@ export const modifierTypes = {
} }
const berryTypes = Utils.getEnumValues(BerryType); const berryTypes = Utils.getEnumValues(BerryType);
let randBerryType: BerryType; let randBerryType: BerryType;
const rand = Utils.randSeedInt(12); const rand = Utils.randSeedInt(12, undefined, "Choosing a Berry");
if (rand < 2) { if (rand < 2) {
randBerryType = BerryType.SITRUS; randBerryType = BerryType.SITRUS;
} else if (rand < 4) { } else if (rand < 4) {
@ -1618,7 +1614,7 @@ export const modifierTypes = {
} else if (rand < 6) { } else if (rand < 6) {
randBerryType = BerryType.LEPPA; randBerryType = BerryType.LEPPA;
} else { } else {
randBerryType = berryTypes[Utils.randSeedInt(berryTypes.length - 3) + 2]; randBerryType = berryTypes[Utils.randSeedInt(berryTypes.length - 3, undefined, "Choosing a berry") + 2];
} }
return new BerryModifierType(randBerryType); return new BerryModifierType(randBerryType);
}), }),
@ -2720,7 +2716,7 @@ export function getDailyRunStarterModifiers(party: PlayerPokemon[], scene?: Batt
const ret: Modifiers.PokemonHeldItemModifier[] = []; const ret: Modifiers.PokemonHeldItemModifier[] = [];
for (const p of party) { for (const p of party) {
for (let m = 0; m < 3; m++) { for (let m = 0; m < 3; m++) {
const tierValue = Utils.randSeedInt(64); const tierValue = Utils.randSeedInt(64, undefined, "Choosing modifier tier for daily items");
let tier: ModifierTier; let tier: ModifierTier;
if (tierValue > 25) { if (tierValue > 25) {
@ -2808,7 +2804,7 @@ function getNewModifierTypeOption(party: Pokemon[], poolType: ModifierPoolType,
Phaser.Math.RND.state(state) Phaser.Math.RND.state(state)
} }
} }
const tierValue = Utils.randSeedInt(1024); const tierValue = Utils.randSeedInt(1024, undefined, "Choosing a modifier tier");
if (!upgradeCount) { if (!upgradeCount) {
upgradeCount = 0; upgradeCount = 0;
} }
@ -2822,7 +2818,7 @@ function getNewModifierTypeOption(party: Pokemon[], poolType: ModifierPoolType,
const upgradeOdds = Math.floor(128 / ((partyLuckValue + 4) / 4)); const upgradeOdds = Math.floor(128 / ((partyLuckValue + 4) / 4));
let upgraded = false; let upgraded = false;
do { do {
upgraded = Utils.randSeedInt(upgradeOdds) < 4; upgraded = Utils.randSeedInt(upgradeOdds, undefined, "Upgrade chance") < 4;
if (upgraded) { if (upgraded) {
upgradeCount++; upgradeCount++;
} }
@ -2880,7 +2876,7 @@ function getNewModifierTypeOption(party: Pokemon[], poolType: ModifierPoolType,
} }
const upgradeOdds = Math.floor(32 / ((partyShinyCount + 2) / 2)); const upgradeOdds = Math.floor(32 / ((partyShinyCount + 2) / 2));
while (modifierPool.hasOwnProperty(tier + upgradeCount + 1) && modifierPool[tier + upgradeCount + 1].length) { while (modifierPool.hasOwnProperty(tier + upgradeCount + 1) && modifierPool[tier + upgradeCount + 1].length) {
if (!Utils.randSeedInt(upgradeOdds)) { if (!Utils.randSeedInt(upgradeOdds, undefined, "Upgrade chance 2")) {
upgradeCount++; upgradeCount++;
} else { } else {
break; break;
@ -2907,9 +2903,9 @@ function getNewModifierTypeOption(party: Pokemon[], poolType: ModifierPoolType,
//console.log("Ignored items for this tier", ignoredPoolIndexes[tier].map((v, i) => [ignoredPoolNames[i], v]).flat()) //console.log("Ignored items for this tier", ignoredPoolIndexes[tier].map((v, i) => [ignoredPoolNames[i], v]).flat())
} }
} }
let modifierType: ModifierType = (pool[tier][index]).modifierType; let modifierType: ModifierType | null = (pool[tier][index]).modifierType;
if (modifierType instanceof ModifierTypeGenerator) { if (modifierType instanceof ModifierTypeGenerator) {
modifierType = (modifierType as ModifierTypeGenerator).generateType(party)!; modifierType = (modifierType as ModifierTypeGenerator).generateType(party);
if (modifierType === null) { if (modifierType === null) {
if (player) { if (player) {
if (!shutUpBro) console.log(ModifierTier[tier], upgradeCount); if (!shutUpBro) console.log(ModifierTier[tier], upgradeCount);

View File

@ -796,7 +796,7 @@ export class TerastallizeModifier extends LapsingPokemonHeldItemModifier {
/** /**
* Modifier used for held items, specifically vitamins like Carbos, Hp Up, etc., that * Modifier used for held items, specifically vitamins like Carbos, Hp Up, etc., that
* increase the value of a given {@linkcode PermanentStat}. * increase the value of a given {@linkcode PermanentStat}.
* @extends PokemonHeldItemModifier * @extends LapsingPersistentModifier
* @see {@linkcode apply} * @see {@linkcode apply}
*/ */
export class BaseStatModifier extends PokemonHeldItemModifier { export class BaseStatModifier extends PokemonHeldItemModifier {
@ -1494,7 +1494,7 @@ export class PreserveBerryModifier extends PersistentModifier {
apply(args: any[]): boolean { apply(args: any[]): boolean {
if (!(args[1] as Utils.BooleanHolder).value) { if (!(args[1] as Utils.BooleanHolder).value) {
(args[1] as Utils.BooleanHolder).value = (args[0] as Pokemon).randSeedInt(10, undefined, "Chance to save a berry") < this.getStackCount() * 3; (args[1] as Utils.BooleanHolder).value = (args[0] as Pokemon).randSeedInt(10, undefined, "Chance to preserve berry") < this.getStackCount() * 3;
} }
return true; return true;
@ -2428,7 +2428,7 @@ export abstract class HeldItemTransferModifier extends PokemonHeldItemModifier {
return false; return false;
} }
const targetPokemon = opponents[pokemon.randSeedInt(opponents.length, undefined, "Chance to steal/transfer an item")]; const targetPokemon = opponents[pokemon.randSeedInt(opponents.length, undefined, "Chance to steal item")];
const transferredItemCount = this.getTransferredItemCount(); const transferredItemCount = this.getTransferredItemCount();
if (!transferredItemCount) { if (!transferredItemCount) {

View File

@ -124,7 +124,7 @@ export class AttemptCapturePhase extends PokemonPhase {
shakeCounter.stop(); shakeCounter.stop();
this.failCatch(shakeCount); this.failCatch(shakeCount);
} else if (shakeCount++ < 3) { } else if (shakeCount++ < 3) {
if (pokeballMultiplier === -1 || this.roll(y) < y) { if (pokeballMultiplier === -1 || pokemon.randSeedInt(65536, undefined, "Capture roll") < y) {
this.scene.playSound("se/pb_move"); this.scene.playSound("se/pb_move");
} else { } else {
shakeCounter.stop(); shakeCounter.stop();

View File

@ -29,7 +29,7 @@ export class AttemptRunPhase extends PokemonPhase {
applyAbAttrs(RunSuccessAbAttr, playerPokemon, null, false, escapeChance); applyAbAttrs(RunSuccessAbAttr, playerPokemon, null, false, escapeChance);
if (playerPokemon.randSeedInt(100, undefined, "Run attempt") < escapeChance.value) { if (playerPokemon.randSeedInt(100, undefined, "Run away chance") < escapeChance.value) {
this.scene.playSound("se/flee"); this.scene.playSound("se/flee");
LoggerTools.logShop(this.scene, this.scene.currentBattle.waveIndex, "Fled") LoggerTools.logShop(this.scene, this.scene.currentBattle.waveIndex, "Fled")
this.scene.queueMessage(i18next.t("battle:runAwaySuccess"), null, true, 500); this.scene.queueMessage(i18next.t("battle:runAwaySuccess"), null, true, 500);

View File

@ -391,7 +391,7 @@ export class EggHatchPhase extends Phase {
repeat: intensity, repeat: intensity,
duration: Utils.getFrameMs(1), duration: Utils.getFrameMs(1),
onRepeat: () => { onRepeat: () => {
this.doSprayParticle(Utils.randInt(8), offsetY || 0); this.doSprayParticle(Utils.randInt(8, undefined, "%HIDE"), offsetY || 0);
} }
}); });
} }
@ -410,8 +410,8 @@ export class EggHatchPhase extends Phase {
let f = 0; let f = 0;
let yOffset = 0; let yOffset = 0;
const speed = 3 - Utils.randInt(8); const speed = 3 - Utils.randInt(8, undefined, "%HIDE");
const amp = 24 + Utils.randInt(32); const amp = 24 + Utils.randInt(32, undefined, "%HIDE");
const particleTimer = this.scene.tweens.addCounter({ const particleTimer = this.scene.tweens.addCounter({
repeat: -1, repeat: -1,

View File

@ -309,7 +309,7 @@ export class EncounterPhase extends BattlePhase {
doSummon(); doSummon();
} else { } else {
let message: string; let message: string;
this.scene.executeWithSeedOffset(() => message = Utils.randSeedItem(encounterMessages), this.scene.currentBattle.waveIndex); this.scene.executeWithSeedOffset(() => message = Utils.randSeedItem(encounterMessages, "Encounter message"), this.scene.currentBattle.waveIndex);
message = message!; // tell TS compiler it's defined now message = message!; // tell TS compiler it's defined now
const showDialogueAndSummon = () => { const showDialogueAndSummon = () => {
this.scene.ui.showDialogue(message, trainer?.getName(TrainerSlot.NONE, true), null, () => { this.scene.ui.showDialogue(message, trainer?.getName(TrainerSlot.NONE, true), null, () => {

View File

@ -251,7 +251,7 @@ export class MovePhase extends BattlePhase {
switch (this.pokemon.status.effect) { switch (this.pokemon.status.effect) {
case StatusEffect.PARALYSIS: case StatusEffect.PARALYSIS:
if (!this.pokemon.randSeedInt(4, undefined, "Paralysis chance")) { if (!this.pokemon.randSeedInt(4, undefined, "Chance to be immobilized by Paralysis")) {
activated = true; activated = true;
this.cancelled = true; this.cancelled = true;
} }

View File

@ -39,7 +39,7 @@ export class SelectBiomePhase extends BattlePhase {
let biomes: Biome[] = []; let biomes: Biome[] = [];
this.scene.executeWithSeedOffset(() => { this.scene.executeWithSeedOffset(() => {
biomes = (biomeLinks[currentBiome] as (Biome | [Biome, integer])[]) biomes = (biomeLinks[currentBiome] as (Biome | [Biome, integer])[])
.filter(b => !Array.isArray(b) || !Utils.randSeedInt(b[1])) .filter(b => !Array.isArray(b) || !Utils.randSeedInt(b[1], undefined, "Choosing next biome"))
.map(b => !Array.isArray(b) ? b : b[0]); .map(b => !Array.isArray(b) ? b : b[0]);
}, this.scene.currentBattle.waveIndex); }, this.scene.currentBattle.waveIndex);
if (biomes.length > 1 && this.scene.findModifier(m => m instanceof MapModifier)) { if (biomes.length > 1 && this.scene.findModifier(m => m instanceof MapModifier)) {
@ -48,7 +48,7 @@ export class SelectBiomePhase extends BattlePhase {
biomeChoices = (!Array.isArray(biomeLinks[currentBiome]) biomeChoices = (!Array.isArray(biomeLinks[currentBiome])
? [biomeLinks[currentBiome] as Biome] ? [biomeLinks[currentBiome] as Biome]
: biomeLinks[currentBiome] as (Biome | [Biome, integer])[]) : biomeLinks[currentBiome] as (Biome | [Biome, integer])[])
.filter((b, i) => !Array.isArray(b) || !Utils.randSeedInt(b[1])) .filter((b, i) => !Array.isArray(b) || !Utils.randSeedInt(b[1], undefined, "Choosing next biome for map"))
.map(b => Array.isArray(b) ? b[0] : b); .map(b => Array.isArray(b) ? b[0] : b);
}, this.scene.currentBattle.waveIndex); }, this.scene.currentBattle.waveIndex);
const biomeSelectItems = biomeChoices.map(b => { const biomeSelectItems = biomeChoices.map(b => {
@ -67,7 +67,7 @@ export class SelectBiomePhase extends BattlePhase {
delay: 1000 delay: 1000
}); });
} else { } else {
setNextBiome(biomes[Utils.randSeedInt(biomes.length)]); setNextBiome(biomes[Utils.randSeedInt(biomes.length, undefined, "Choosing biome randomly")]);
} }
} else if (biomeLinks.hasOwnProperty(currentBiome)) { } else if (biomeLinks.hasOwnProperty(currentBiome)) {
setNextBiome(biomeLinks[currentBiome] as Biome); setNextBiome(biomeLinks[currentBiome] as Biome);

View File

@ -38,7 +38,7 @@ export class TrainerVictoryPhase extends BattlePhase {
this.scene.ui.showText(i18next.t("battle:trainerDefeated", { trainerName: this.scene.currentBattle.trainer?.getName(TrainerSlot.NONE, true) }), null, () => { this.scene.ui.showText(i18next.t("battle:trainerDefeated", { trainerName: this.scene.currentBattle.trainer?.getName(TrainerSlot.NONE, true) }), null, () => {
const victoryMessages = this.scene.currentBattle.trainer?.getVictoryMessages()!; // TODO: is this bang correct? const victoryMessages = this.scene.currentBattle.trainer?.getVictoryMessages()!; // TODO: is this bang correct?
let message: string; let message: string;
this.scene.executeWithSeedOffset(() => message = Utils.randSeedItem(victoryMessages), this.scene.currentBattle.waveIndex); this.scene.executeWithSeedOffset(() => message = Utils.randSeedItem(victoryMessages, "Victory message"), this.scene.currentBattle.waveIndex);
message = message!; // tell TS compiler it's defined now message = message!; // tell TS compiler it's defined now
const showMessage = () => { const showMessage = () => {

View File

@ -31,8 +31,8 @@ import { Variant } from "#app/data/variant";
import {setSettingGamepad, SettingGamepad, settingGamepadDefaults} from "./settings/settings-gamepad"; import {setSettingGamepad, SettingGamepad, settingGamepadDefaults} from "./settings/settings-gamepad";
import {setSettingKeyboard, SettingKeyboard} from "#app/system/settings/settings-keyboard"; import {setSettingKeyboard, SettingKeyboard} from "#app/system/settings/settings-keyboard";
import { TerrainChangedEvent, WeatherChangedEvent } from "#app/events/arena"; import { TerrainChangedEvent, WeatherChangedEvent } from "#app/events/arena";
import * as Modifier from "../modifier/modifier";
import { StatusEffect } from "#app/data/status-effect"; import { StatusEffect } from "#app/data/status-effect";
import { EnemyAttackStatusEffectChanceModifier } from "../modifier/modifier";
import ChallengeData from "./challenge-data"; import ChallengeData from "./challenge-data";
import { Device } from "#enums/devices"; import { Device } from "#enums/devices";
import { GameDataType } from "#enums/game-data-type"; import { GameDataType } from "#enums/game-data-type";
@ -327,8 +327,8 @@ export class GameData {
this.loadSettings(); this.loadSettings();
this.loadGamepadSettings(); this.loadGamepadSettings();
this.loadMappingConfigs(); this.loadMappingConfigs();
this.trainerId = Utils.randInt(65536); this.trainerId = Utils.randInt(65536, undefined, "Trainer ID");
this.secretId = Utils.randInt(65536); this.secretId = Utils.randInt(65536, undefined, "Secret ID");
this.starterData = {}; this.starterData = {};
this.gameStats = new GameStats(); this.gameStats = new GameStats();
this.runHistory = {}; this.runHistory = {};
@ -1085,8 +1085,10 @@ export class GameData {
// TODO // TODO
//scene.arena.tags = sessionData.arena.tags; //scene.arena.tags = sessionData.arena.tags;
const modifiersModule = await import("../modifier/modifier");
for (const modifierData of sessionData.modifiers) { for (const modifierData of sessionData.modifiers) {
const modifier = modifierData.toModifier(scene, Modifier[modifierData.className]); const modifier = modifierData.toModifier(scene, modifiersModule[modifierData.className]);
if (modifier) { if (modifier) {
scene.addModifier(modifier, true); scene.addModifier(modifier, true);
} }
@ -1095,7 +1097,7 @@ export class GameData {
scene.updateModifiers(true); scene.updateModifiers(true);
for (const enemyModifierData of sessionData.enemyModifiers) { for (const enemyModifierData of sessionData.enemyModifiers) {
const modifier = enemyModifierData.toModifier(scene, Modifier[enemyModifierData.className]); const modifier = enemyModifierData.toModifier(scene, modifiersModule[enemyModifierData.className]);
if (modifier) { if (modifier) {
scene.addEnemyModifier(modifier, true); scene.addEnemyModifier(modifier, true);
} }
@ -1251,7 +1253,7 @@ export class GameData {
if (md?.className === "ExpBalanceModifier") { // Temporarily limit EXP Balance until it gets reworked if (md?.className === "ExpBalanceModifier") { // Temporarily limit EXP Balance until it gets reworked
md.stackCount = Math.min(md.stackCount, 4); md.stackCount = Math.min(md.stackCount, 4);
} }
if (md instanceof Modifier.EnemyAttackStatusEffectChanceModifier && md.effect === StatusEffect.FREEZE || md.effect === StatusEffect.SLEEP) { if (md instanceof EnemyAttackStatusEffectChanceModifier && md.effect === StatusEffect.FREEZE || md.effect === StatusEffect.SLEEP) {
continue; continue;
} }
ret.push(new PersistentModifierData(md, player)); ret.push(new PersistentModifierData(md, player));
@ -1507,7 +1509,7 @@ export class GameData {
this.scene.executeWithSeedOffset(() => { this.scene.executeWithSeedOffset(() => {
const neutralNatures = [ Nature.HARDY, Nature.DOCILE, Nature.SERIOUS, Nature.BASHFUL, Nature.QUIRKY ]; const neutralNatures = [ Nature.HARDY, Nature.DOCILE, Nature.SERIOUS, Nature.BASHFUL, Nature.QUIRKY ];
for (let s = 0; s < defaultStarterSpecies.length; s++) { for (let s = 0; s < defaultStarterSpecies.length; s++) {
defaultStarterNatures.push(Utils.randSeedItem(neutralNatures)); defaultStarterNatures.push(Utils.randSeedItem(neutralNatures, "Random neutral nature"));
} }
}, 0, "default"); }, 0, "default");
@ -1862,7 +1864,7 @@ export class GameData {
entry.hatchedCount = 0; entry.hatchedCount = 0;
} }
if (!entry.hasOwnProperty("natureAttr") || (entry.caughtAttr && !entry.natureAttr)) { if (!entry.hasOwnProperty("natureAttr") || (entry.caughtAttr && !entry.natureAttr)) {
entry.natureAttr = this.defaultDexData?.[k].natureAttr || (1 << Utils.randInt(25, 1)); entry.natureAttr = this.defaultDexData?.[k].natureAttr || (1 << Utils.randInt(25, 1, "Random bit shift"));
} }
} }
} }

View File

@ -1073,7 +1073,7 @@ export default class PartyUiHandler extends MessageUiHandler {
} }
getReleaseMessage(pokemonName: string): string { getReleaseMessage(pokemonName: string): string {
const rand = Utils.randInt(128); const rand = Utils.randInt(128, undefined, "Random release message");
if (rand < 20) { if (rand < 20) {
return i18next.t("partyUiHandler:goodbye", { pokemonName: pokemonName }); return i18next.t("partyUiHandler:goodbye", { pokemonName: pokemonName });
} else if (rand < 40) { } else if (rand < 40) {

View File

@ -85,7 +85,7 @@ export default class TitleUiHandler extends OptionSelectUiHandler {
const ret = super.show(args); const ret = super.show(args);
if (ret) { if (ret) {
this.splashMessage = Utils.randItem(getSplashMessages()); this.splashMessage = Utils.randItem(getSplashMessages(), "Splash Message selection");
this.splashMessageText.setText(this.splashMessage.replace("{COUNT}", "?")); this.splashMessageText.setText(this.splashMessage.replace("{COUNT}", "?"));
const ui = this.getUi(); const ui = this.getUi();

View File

@ -13,7 +13,7 @@ export function randomString(length: integer, seeded: boolean = false) {
let result = ""; let result = "";
for (let i = 0; i < length; i++) { for (let i = 0; i < length; i++) {
const randomIndex = seeded ? randSeedInt(characters.length) : Math.floor(Math.random() * characters.length); const randomIndex = seeded ? randSeedInt(characters.length, undefined, "%HIDE") : Math.floor(Math.random() * characters.length);
result += characters[randomIndex]; result += characters[randomIndex];
} }
@ -44,7 +44,7 @@ export function rangemap(value: integer, min: integer, max: integer, outMin: int
return ((value - min) / (max - min)) * (outMax - outMin) + outMin return ((value - min) / (max - min)) * (outMax - outMin) + outMin
} }
export function randGauss(stdev: number, mean: number = 0): number { export function randGauss(stdev: number, mean: number = 0, reason?: string): number {
if (!stdev) { if (!stdev) {
return 0; return 0;
} }
@ -54,7 +54,7 @@ export function randGauss(stdev: number, mean: number = 0): number {
return z * stdev + mean; return z * stdev + mean;
} }
export function randSeedGauss(stdev: number, mean: number = 0): number { export function randSeedGauss(stdev: number, mean: number = 0, reason?: string): number {
if (!stdev) { if (!stdev) {
return 0; return 0;
} }
@ -85,7 +85,9 @@ export function randInt(range: integer, min: integer = 0, reason?: string): inte
return min; return min;
} }
let V = Math.floor(Math.random() * range) + min; let V = Math.floor(Math.random() * range) + min;
//console.log(reason ? reason : "randInt", V) if (reason != "%HIDE") {
console.log("[unseeded] " + (reason ? reason : "randInt"), V)
}
return V; return V;
} }
@ -100,7 +102,13 @@ export function randSeedInt(range: integer, min: integer = 0, reason?: string):
return min; return min;
} }
let V = Phaser.Math.RND.integerInRange(min, (range - 1) + min); let V = Phaser.Math.RND.integerInRange(min, (range - 1) + min);
//console.log(reason ? reason : "randSeedInt", V) if (reason != "%HIDE") {
if (reason) {
console.log(reason, V)
} else {
console.error("unlabeled randSeedInt", V)
}
}
return V; return V;
} }
@ -122,7 +130,7 @@ export function randItem<T>(items: T[], reason?: string): T {
export function randSeedItem<T>(items: T[], reason?: string): T { export function randSeedItem<T>(items: T[], reason?: string): T {
function rpick() { function rpick() {
let V = Phaser.Math.RND.pick(items) let V = Phaser.Math.RND.pick(items)
//console.log(reason ? reason : "randSeedItem") console.log(reason ? reason : "randSeedItem")
return V; return V;
} }
return items.length === 1 return items.length === 1