mirror of
https://github.com/pagefaultgames/pokerogue.git
synced 2025-07-06 08:22:16 +02:00
Fix off-by-one error in some random number calls
This commit is contained in:
parent
e39ebb68f2
commit
1fb6656c22
@ -49,8 +49,8 @@ import CandyBar from "./ui/candy-bar";
|
||||
import { Variant, variantData } from "./data/variant";
|
||||
import { Localizable } from "#app/interfaces/locales";
|
||||
import Overrides from "#app/overrides";
|
||||
import {InputsController} from "./inputs-controller";
|
||||
import {UiInputs} from "./ui-inputs";
|
||||
import { InputsController } from "./inputs-controller";
|
||||
import { UiInputs } from "./ui-inputs";
|
||||
import { NewArenaEvent } from "./events/battle-scene";
|
||||
import { ArenaFlyout } from "./ui/arena-flyout";
|
||||
import { EaseType } from "#enums/ease-type";
|
||||
@ -65,7 +65,7 @@ import { Species } from "#enums/species";
|
||||
import { UiTheme } from "#enums/ui-theme";
|
||||
import { TimedEventManager } from "#app/timed-event-manager.js";
|
||||
import i18next from "i18next";
|
||||
import {TrainerType} from "#enums/trainer-type";
|
||||
import { TrainerType } from "#enums/trainer-type";
|
||||
import { battleSpecDialogue } from "./data/dialogue";
|
||||
import { LoadingScene } from "./loading-scene";
|
||||
import { LevelCapPhase } from "./phases/level-cap-phase";
|
||||
@ -851,7 +851,7 @@ export default class BattleScene extends SceneBase {
|
||||
overrideModifiers(this, false);
|
||||
overrideHeldItems(this, pokemon, false);
|
||||
if (boss && !dataSource) {
|
||||
const secondaryIvs = Utils.getIvsFromId(Utils.randSeedInt(4294967295));
|
||||
const secondaryIvs = Utils.getIvsFromId(Utils.randSeedInt(4294967296));
|
||||
|
||||
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));
|
||||
@ -957,6 +957,7 @@ export default class BattleScene extends SceneBase {
|
||||
this.offsetGym = this.gameMode.isClassic && this.getGeneratedOffsetGym();
|
||||
}
|
||||
|
||||
// What is the purpose of this function? Why not just use Battle.randSeedInt() directly?
|
||||
randBattleSeedInt(range: integer, min: integer = 0): integer {
|
||||
return this.currentBattle?.randSeedInt(this, range, min);
|
||||
}
|
||||
@ -1107,7 +1108,8 @@ export default class BattleScene extends SceneBase {
|
||||
doubleTrainer = false;
|
||||
}
|
||||
}
|
||||
newTrainer = trainerData !== undefined ? trainerData.toTrainer(this) : new Trainer(this, trainerType, doubleTrainer ? TrainerVariant.DOUBLE : Utils.randSeedInt(2) ? TrainerVariant.FEMALE : TrainerVariant.DEFAULT);
|
||||
const variant = doubleTrainer ? TrainerVariant.DOUBLE : (Utils.randSeedInt(2) ? TrainerVariant.FEMALE : TrainerVariant.DEFAULT);
|
||||
newTrainer = trainerData !== undefined ? trainerData.toTrainer(this) : new Trainer(this, trainerType, variant);
|
||||
this.field.add(newTrainer);
|
||||
}
|
||||
}
|
||||
@ -2550,7 +2552,7 @@ export default class BattleScene extends SceneBase {
|
||||
if (mods.length < 1) {
|
||||
return mods;
|
||||
}
|
||||
const rand = Math.floor(Utils.randSeedInt(mods.length));
|
||||
const rand = Math.floor(Utils.randSeedInt(mods.length)); // Why is there a floor() call around a number that can only be an integer?
|
||||
return [mods[rand], ...shuffleModifiers(mods.filter((_, i) => i !== rand))];
|
||||
};
|
||||
modifiers = shuffleModifiers(modifiers);
|
||||
|
@ -6,7 +6,7 @@ import Trainer, { TrainerVariant } from "./field/trainer";
|
||||
import { GameMode } from "./game-mode";
|
||||
import { MoneyMultiplierModifier, PokemonHeldItemModifier } from "./modifier/modifier";
|
||||
import { PokeballType } from "./data/pokeball";
|
||||
import {trainerConfigs} from "#app/data/trainer-config";
|
||||
import { trainerConfigs } from "#app/data/trainer-config";
|
||||
import { ArenaTagType } from "#enums/arena-tag-type";
|
||||
import { BattleSpec } from "#enums/battle-spec";
|
||||
import { Moves } from "#enums/moves";
|
||||
|
@ -2452,7 +2452,7 @@ export class ConfusionOnStatusEffectAbAttr extends PostAttackAbAttr {
|
||||
*/
|
||||
applyPostAttackAfterMoveTypeCheck(pokemon: Pokemon, passive: boolean, defender: Pokemon, move: Move, hitResult: HitResult, args: any[]): boolean {
|
||||
if (this.effects.indexOf(args[0]) > -1 && !defender.isFainted()) {
|
||||
return defender.addTag(BattlerTagType.CONFUSED, pokemon.randSeedInt(3,2), move.id, defender.id);
|
||||
return defender.addTag(BattlerTagType.CONFUSED, pokemon.randSeedIntRange(2, 5), move.id, defender.id);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
@ -347,7 +347,7 @@ export class ConfusedTag extends BattlerTag {
|
||||
if (pokemon.randSeedInt(3) === 0) {
|
||||
const atk = pokemon.getBattleStat(Stat.ATK);
|
||||
const def = pokemon.getBattleStat(Stat.DEF);
|
||||
const damage = Math.ceil(((((2 * pokemon.level / 5 + 2) * 40 * atk / def) / 50) + 2) * (pokemon.randSeedInt(15, 85) / 100));
|
||||
const damage = Math.ceil(((((2 * pokemon.level / 5 + 2) * 40 * atk / def) / 50) + 2) * (pokemon.randSeedIntRange(85, 100) / 100));
|
||||
pokemon.scene.queueMessage(i18next.t("battlerTags:confusedLapseHurtItself"));
|
||||
pokemon.damageAndUpdate(damage);
|
||||
pokemon.battleData.hitCount++;
|
||||
|
@ -3,7 +3,7 @@ import { BattleStat, getBattleStatName } from "./battle-stat";
|
||||
import { EncoreTag, GulpMissileTag, HelpingHandTag, SemiInvulnerableTag, ShellTrapTag, StockpilingTag, TrappedTag, TypeBoostTag } from "./battler-tags";
|
||||
import { getPokemonNameWithAffix } from "../messages";
|
||||
import Pokemon, { AttackMoveResult, EnemyPokemon, HitResult, MoveResult, PlayerPokemon, PokemonMove, TurnMove } from "../field/pokemon";
|
||||
import { StatusEffect, getStatusEffectHealText, isNonVolatileStatusEffect, getNonVolatileStatusEffects} from "./status-effect";
|
||||
import { StatusEffect, getStatusEffectHealText, isNonVolatileStatusEffect, getNonVolatileStatusEffects } from "./status-effect";
|
||||
import { getTypeResistances, Type } from "./type";
|
||||
import { Constructor } from "#app/utils";
|
||||
import * as Utils from "../utils";
|
||||
@ -4334,7 +4334,7 @@ export class AddBattlerTagAttr extends MoveEffectAttr {
|
||||
|
||||
const moveChance = this.getMoveChance(user, target, move, this.selfTarget, true);
|
||||
if (moveChance < 0 || moveChance === 100 || user.randSeedInt(100) < moveChance) {
|
||||
return (this.selfTarget ? user : target).addTag(this.tagType, user.randSeedInt(this.turnCountMax - this.turnCountMin, this.turnCountMin), move.id, user.id);
|
||||
return (this.selfTarget ? user : target).addTag(this.tagType, user.randSeedIntRange(this.turnCountMin, this.turnCountMax), move.id, user.id);
|
||||
}
|
||||
|
||||
return false;
|
||||
|
@ -1573,7 +1573,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
||||
};
|
||||
|
||||
this.fusionSpecies = this.scene.randomSpecies(this.scene.currentBattle?.waveIndex || 0, this.level, false, filter, true);
|
||||
this.fusionAbilityIndex = (this.fusionSpecies.abilityHidden && hasHiddenAbility ? this.fusionSpecies.ability2 ? 2 : 1 : this.fusionSpecies.ability2 ? randAbilityIndex : 0);
|
||||
this.fusionAbilityIndex = (this.fusionSpecies.abilityHidden && hasHiddenAbility ? 2 : this.fusionSpecies.ability2 !== this.fusionSpecies.ability1 ? randAbilityIndex : 0);
|
||||
this.fusionShiny = this.shiny;
|
||||
this.fusionVariant = this.variant;
|
||||
|
||||
@ -2081,7 +2081,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
||||
|
||||
if (!isTypeImmune) {
|
||||
const levelMultiplier = (2 * source.level / 5 + 2);
|
||||
const randomMultiplier = ((this.scene.randBattleSeedInt(16) + 85) / 100);
|
||||
const randomMultiplier = (this.randSeedIntRange(85, 100) / 100);
|
||||
damage.value = Math.ceil((((levelMultiplier * power * sourceAtk.value / targetDef.value) / 50) + 2)
|
||||
* stabMultiplier.value
|
||||
* typeMultiplier.value
|
||||
|
@ -379,16 +379,16 @@ export class MoveEffectPhase extends PokemonPhase {
|
||||
return false;
|
||||
}
|
||||
|
||||
const moveAccuracy = this.move.getMove().calculateBattleAccuracy(user!, target); // TODO: is the bang correct here?
|
||||
const moveAccuracy = this.move.getMove().calculateBattleAccuracy(user, target);
|
||||
|
||||
if (moveAccuracy === -1) {
|
||||
return true;
|
||||
}
|
||||
|
||||
const accuracyMultiplier = user.getAccuracyMultiplier(target, this.move.getMove());
|
||||
const rand = user.randSeedInt(100, 1);
|
||||
const rand = user.randSeedInt(100);
|
||||
|
||||
return rand <= moveAccuracy * (accuracyMultiplier!); // TODO: is this bang correct?
|
||||
return rand < (moveAccuracy * accuracyMultiplier);
|
||||
}
|
||||
|
||||
/** Returns the {@linkcode Pokemon} using this phase's invoked move */
|
||||
|
56
src/rng.md
Normal file
56
src/rng.md
Normal file
@ -0,0 +1,56 @@
|
||||
`src/field/pokemon.ts -> Pokemon`
|
||||
```ts
|
||||
// This calls either `BattleScene:randBattleSeedInt()` in `src/battle-scene.ts` which calls `Battle:randSeedInt()` in `src/battle.ts` which calls `randSeedInt()` in `src/utils.ts`
|
||||
// or it directly calls `randSeedInt()` in `src/utils.ts`
|
||||
randSeedInt(range: integer, min: integer = 0): integer {
|
||||
return this.scene.currentBattle
|
||||
? this.scene.randBattleSeedInt(range, min)
|
||||
: Utils.randSeedInt(range, min);
|
||||
}
|
||||
```
|
||||
|
||||
`src/battle-scene.ts -> BattleScene`
|
||||
```ts
|
||||
// This calls `Battle:randSeedInt()` in `src/battle.ts` which calls `randSeedInt()` in `src/utils.ts`
|
||||
randBattleSeedInt(range: integer, min: integer = 0): integer {
|
||||
return this.currentBattle?.randSeedInt(this, range, min);
|
||||
}
|
||||
```
|
||||
|
||||
`src/battle.ts -> Battle`
|
||||
```ts
|
||||
// This calls `randSeedInt()` in `src/utils.ts`
|
||||
randSeedInt(scene: BattleScene, range: integer, min: integer = 0): integer {
|
||||
if (range <= 1) {
|
||||
return min;
|
||||
}
|
||||
const tempRngCounter = scene.rngCounter;
|
||||
const tempSeedOverride = scene.rngSeedOverride;
|
||||
const state = Phaser.Math.RND.state();
|
||||
if (this.battleSeedState) {
|
||||
Phaser.Math.RND.state(this.battleSeedState);
|
||||
} else {
|
||||
Phaser.Math.RND.sow([ Utils.shiftCharCodes(this.battleSeed, this.turn << 6) ]);
|
||||
console.log("Battle Seed:", this.battleSeed);
|
||||
}
|
||||
scene.rngCounter = this.rngCounter++;
|
||||
scene.rngSeedOverride = this.battleSeed;
|
||||
const ret = Utils.randSeedInt(range, min);
|
||||
this.battleSeedState = Phaser.Math.RND.state();
|
||||
Phaser.Math.RND.state(state);
|
||||
scene.rngCounter = tempRngCounter;
|
||||
scene.rngSeedOverride = tempSeedOverride;
|
||||
return ret;
|
||||
}
|
||||
```
|
||||
|
||||
`src/utils.ts`
|
||||
```ts
|
||||
// This is the eventual endpoint of every other RSI function
|
||||
export function randSeedInt(range: integer, min: integer = 0): integer {
|
||||
if (range <= 1) {
|
||||
return min;
|
||||
}
|
||||
return Phaser.Math.RND.integerInRange(min, (range - 1) + min);
|
||||
}
|
||||
```
|
@ -82,6 +82,7 @@ export function randInt(range: integer, min: integer = 0): integer {
|
||||
return Math.floor(Math.random() * range) + min;
|
||||
}
|
||||
|
||||
// Is this only seeded if called via `Battle:randSeedInt()`?
|
||||
export function randSeedInt(range: integer, min: integer = 0): integer {
|
||||
if (range <= 1) {
|
||||
return min;
|
||||
|
Loading…
Reference in New Issue
Block a user