Create utility function randSeedFloat

just` phaser.Math.RND.Frac` atm
This commit is contained in:
Bertie690 2025-05-04 09:12:44 -04:00
parent 1042b528ec
commit 37e98e3c7d
7 changed files with 59 additions and 35 deletions

View File

@ -8,6 +8,7 @@ import {
shiftCharCodes,
randSeedItem,
randInt,
randSeedFloat,
} from "#app/utils/common";
import Trainer, { TrainerVariant } from "./field/trainer";
import type { GameMode } from "./game-mode";
@ -150,7 +151,7 @@ export default class Battle {
randSeedGaussForLevel(value: number): number {
let rand = 0;
for (let i = value; i > 0; i--) {
rand += Phaser.Math.RND.realInRange(0, 1);
rand += randSeedFloat();
}
return rand / value;
}

View File

@ -1,5 +1,5 @@
import { HitResult, MoveResult, PlayerPokemon } from "#app/field/pokemon";
import { BooleanHolder, NumberHolder, toDmgValue, isNullOrUndefined, randSeedItem, randSeedInt, type Constructor } from "#app/utils/common";
import { BooleanHolder, NumberHolder, toDmgValue, isNullOrUndefined, randSeedItem, randSeedInt, type Constructor, randSeedFloat } from "#app/utils/common";
import { getPokemonNameWithAffix } from "#app/messages";
import { BattlerTagLapseType, GroundedTag } from "#app/data/battler-tags";
import { getNonVolatileStatusEffects, getStatusEffectDescriptor, getStatusEffectHealText } from "#app/data/status-effect";
@ -1247,7 +1247,7 @@ export class MoveTypeChangeAbAttr extends PreAttackAbAttr {
/**
* Determine if the move type change attribute can be applied
*
*
* Can be applied if:
* - The ability's condition is met, e.g. pixilate only boosts normal moves,
* - The move is not forbidden from having its type changed by an ability, e.g. {@linkcode Moves.MULTI_ATTACK}
@ -1263,7 +1263,7 @@ export class MoveTypeChangeAbAttr extends PreAttackAbAttr {
*/
override canApplyPreAttack(pokemon: Pokemon, _passive: boolean, _simulated: boolean, _defender: Pokemon | null, move: Move, _args: [NumberHolder?, NumberHolder?, ...any]): boolean {
return (!this.condition || this.condition(pokemon, _defender, move)) &&
!noAbilityTypeOverrideMoves.has(move.id) &&
!noAbilityTypeOverrideMoves.has(move.id) &&
(!pokemon.isTerastallized ||
(move.id !== Moves.TERA_BLAST &&
(move.id !== Moves.TERA_STARSTORM || pokemon.getTeraType() !== PokemonType.STELLAR || !pokemon.hasSpecies(Species.TERAPAGOS))));
@ -4104,8 +4104,8 @@ export class PostTurnRestoreBerryAbAttr extends PostTurnAbAttr {
}
// Clamp procChance to [0, 1]. Skip if didn't proc (less than pass)
const pass = Phaser.Math.RND.realInRange(0, 1);
return Phaser.Math.Clamp(this.procChance(pokemon), 0, 1) >= pass;
const pass = randSeedFloat();
return this.procChance(pokemon) >= pass;
}
override applyPostTurn(pokemon: Pokemon, passive: boolean, simulated: boolean, args: any[]): void {

View File

@ -29,7 +29,7 @@ import {
} from "../status-effect";
import { getTypeDamageMultiplier } from "../type";
import { PokemonType } from "#enums/pokemon-type";
import { BooleanHolder, NumberHolder, isNullOrUndefined, toDmgValue, randSeedItem, randSeedInt, getEnumValues, toReadableString, type Constructor } from "#app/utils/common";
import { BooleanHolder, NumberHolder, isNullOrUndefined, toDmgValue, randSeedItem, randSeedInt, getEnumValues, toReadableString, type Constructor, randSeedFloat } from "#app/utils/common";
import { WeatherType } from "#enums/weather-type";
import type { ArenaTrapTag } from "../arena-tag";
import { ArenaTagSide, WeakenMoveTypeTag } from "../arena-tag";
@ -2546,8 +2546,8 @@ export class StealHeldItemChanceAttr extends MoveEffectAttr {
}
apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean {
const rand = Phaser.Math.RND.realInRange(0, 1);
if (rand >= this.chance) {
const rand = randSeedFloat();
if (rand > this.chance) {
return false;
}

View File

@ -8,7 +8,14 @@ import type { AnySound } from "#app/battle-scene";
import { globalScene } from "#app/global-scene";
import type { GameMode } from "#app/game-mode";
import { DexAttr, type StarterMoveset } from "#app/system/game-data";
import { isNullOrUndefined, capitalizeString, randSeedInt, randSeedGauss, randSeedItem } from "#app/utils/common";
import {
isNullOrUndefined,
capitalizeString,
randSeedInt,
randSeedGauss,
randSeedItem,
randSeedFloat,
} from "#app/utils/common";
import { uncatchableSpecies } from "#app/data/balance/biomes";
import { speciesEggMoves } from "#app/data/balance/egg-moves";
import { GrowthRate } from "#app/data/exp";
@ -750,7 +757,7 @@ export abstract class PokemonSpeciesForm {
let paletteColors: Map<number, number> = new Map();
const originalRandom = Math.random;
Math.random = Phaser.Math.RND.frac;
Math.random = randSeedFloat;
globalScene.executeWithSeedOffset(
() => {
@ -773,6 +780,7 @@ export default class PokemonSpecies extends PokemonSpeciesForm implements Locali
readonly mythical: boolean;
readonly species: string;
readonly growthRate: GrowthRate;
/** The chance (as a decimal) for this Species to be male, or `null` for genderless species */
readonly malePercent: number | null;
readonly genderDiffs: boolean;
readonly canChangeForm: boolean;
@ -889,7 +897,7 @@ export default class PokemonSpecies extends PokemonSpeciesForm implements Locali
return Gender.GENDERLESS;
}
if (Phaser.Math.RND.realInRange(0, 1) <= this.malePercent) {
if (randSeedFloat() <= this.malePercent) {
return Gender.MALE;
}
return Gender.FEMALE;
@ -1138,7 +1146,7 @@ export default class PokemonSpecies extends PokemonSpeciesForm implements Locali
}
}
if (noEvolutionChance === 1 || Phaser.Math.RND.realInRange(0, 1) < noEvolutionChance) {
if (noEvolutionChance === 1 || randSeedFloat() <= noEvolutionChance) {
return this.speciesId;
}

View File

@ -54,7 +54,7 @@ import {
getStarterValueFriendshipCap,
speciesStarterCosts,
} from "#app/data/balance/starters";
import { NumberHolder, randSeedInt, getIvsFromId, BooleanHolder, randSeedItem, isNullOrUndefined, getEnumValues, toDmgValue, fixedInt, rgbaToInt, rgbHexToRgba, rgbToHsv, deltaRgb, isBetween, type nil, type Constructor } from "#app/utils/common";
import { NumberHolder, randSeedInt, getIvsFromId, BooleanHolder, randSeedItem, isNullOrUndefined, getEnumValues, toDmgValue, fixedInt, rgbaToInt, rgbHexToRgba, rgbToHsv, deltaRgb, isBetween, type nil, type Constructor, randSeedFloat } from "#app/utils/common";
import type { TypeDamageMultiplier } from "#app/data/type";
import { getTypeDamageMultiplier, getTypeRgb } from "#app/data/type";
import { PokemonType } from "#enums/pokemon-type";
@ -6084,7 +6084,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
let fusionPaletteColors: Map<number, number>;
const originalRandom = Math.random;
Math.random = () => Phaser.Math.RND.realInRange(0, 1);
Math.random = () => randSeedFloat();
globalScene.executeWithSeedOffset(
() => {

View File

@ -15,7 +15,7 @@ import { PokemonHealPhase } from "#app/phases/pokemon-heal-phase";
import type { VoucherType } from "#app/system/voucher";
import { Command } from "#app/ui/command-ui-handler";
import { addTextObject, TextStyle } from "#app/ui/text";
import { BooleanHolder, hslToHex, isNullOrUndefined, NumberHolder, toDmgValue } from "#app/utils/common";
import { BooleanHolder, hslToHex, isNullOrUndefined, NumberHolder, randSeedFloat, toDmgValue } from "#app/utils/common";
import { BattlerTagType } from "#enums/battler-tag-type";
import { BerryType } from "#enums/berry-type";
import type { Moves } from "#enums/moves";
@ -3368,7 +3368,7 @@ export class ContactHeldItemTransferChanceModifier extends HeldItemTransferModif
}
getTransferredItemCount(): number {
return Phaser.Math.RND.realInRange(0, 1) < this.chance * this.getStackCount() ? 1 : 0;
return randSeedFloat() <= this.chance * this.getStackCount() ? 1 : 0;
}
getTransferMessage(pokemon: Pokemon, targetPokemon: Pokemon, item: ModifierType): string {
@ -3646,7 +3646,7 @@ export class EnemyAttackStatusEffectChanceModifier extends EnemyPersistentModifi
* @returns `true` if the {@linkcode Pokemon} was affected
*/
override apply(enemyPokemon: Pokemon): boolean {
if (Phaser.Math.RND.realInRange(0, 1) < this.chance * this.getStackCount()) {
if (randSeedFloat() <= this.chance * this.getStackCount()) {
return enemyPokemon.trySetStatus(this.effect, true);
}
@ -3686,16 +3686,16 @@ export class EnemyStatusEffectHealChanceModifier extends EnemyPersistentModifier
* @returns `true` if the {@linkcode Pokemon} was healed
*/
override apply(enemyPokemon: Pokemon): boolean {
if (enemyPokemon.status && Phaser.Math.RND.realInRange(0, 1) < this.chance * this.getStackCount()) {
globalScene.queueMessage(
getStatusEffectHealText(enemyPokemon.status.effect, getPokemonNameWithAffix(enemyPokemon)),
);
enemyPokemon.resetStatus();
enemyPokemon.updateInfo();
return true;
if (!enemyPokemon.status || randSeedFloat() > this.chance * this.getStackCount()) {
return false;
}
return false;
globalScene.queueMessage(
getStatusEffectHealText(enemyPokemon.status.effect, getPokemonNameWithAffix(enemyPokemon)),
);
enemyPokemon.resetStatus();
enemyPokemon.updateInfo();
return true;
}
getMaxStackCount(): number {
@ -3774,7 +3774,7 @@ export class EnemyFusionChanceModifier extends EnemyPersistentModifier {
* @returns `true` if the {@linkcode EnemyPokemon} is a fusion
*/
override apply(isFusion: BooleanHolder): boolean {
if (Phaser.Math.RND.realInRange(0, 1) >= this.chance * this.getStackCount()) {
if (randSeedFloat() > this.chance * this.getStackCount()) {
return false;
}

View File

@ -57,8 +57,8 @@ export function randSeedGauss(stdev: number, mean = 0): number {
if (!stdev) {
return 0;
}
const u = 1 - Phaser.Math.RND.realInRange(0, 1);
const v = Phaser.Math.RND.realInRange(0, 1);
const u = 1 - randSeedFloat();
const v = randSeedFloat();
const z = Math.sqrt(-2.0 * Math.log(u)) * Math.cos(2.0 * Math.PI * v);
return z * stdev + mean;
}
@ -87,9 +87,9 @@ export function randInt(range: number, min = 0): number {
}
/**
* Generates a random number using the global seed, or the current battle's seed if called via `Battle.randSeedInt`
* @param range How large of a range of random numbers to choose from. If {@linkcode range} <= 1, returns {@linkcode min}
* @param min The minimum integer to pick, default `0`
* Generate a random integer using the global seed, or the current battle's seed if called via `Battle.randSeedInt`
* @param range - How large of a range of random numbers to choose from. If {@linkcode range} <= 1, returns {@linkcode min}
* @param min - The minimum integer to pick, default `0`
* @returns A random integer between {@linkcode min} and ({@linkcode min} + {@linkcode range} - 1)
*/
export function randSeedInt(range: number, min = 0): number {
@ -108,6 +108,14 @@ export function randIntRange(min: number, max: number): number {
return randInt(max - min, min);
}
/**
* Generate and return a random real number between `0` and `1` using the global seed.
* @returns A random floating-point number between `0` and `1`
*/
export function randSeedFloat(): number {
return Phaser.Math.RND.frac();
}
export function randItem<T>(items: T[]): T {
return items.length === 1 ? items[0] : items[randInt(items.length)];
}
@ -502,12 +510,19 @@ export function capitalizeString(str: string, sep: string, lowerFirstChar = true
return null;
}
export function isNullOrUndefined(object: any): object is null | undefined {
return object === null || object === undefined;
/**
* Report whether a given value is nullish (`null`/`undefined`).
* @param val - The value whose nullishness is being checked
* @returns `true` if `val` is either `null` or `undefined`
*/
export function isNullOrUndefined(val: any): val is null | undefined {
return val === null || val === undefined;
}
/**
* Capitalizes the first letter of a string
* Capitalize the first letter of a string.
* @param str - The string whose first letter is being capitalized
* @return The original string with its first letter capitalized
*/
export function capitalizeFirstLetter(str: string) {
return str.charAt(0).toUpperCase() + str.slice(1);