diff --git a/src/data/pokemon-species.ts b/src/data/pokemon-species.ts index 3b09f8f7698..bd13dd75473 100644 --- a/src/data/pokemon-species.ts +++ b/src/data/pokemon-species.ts @@ -435,7 +435,7 @@ export abstract class PokemonSpeciesForm { for (const moveId of moveset) { if (speciesEggMoves.hasOwnProperty(rootSpeciesId)) { const eggMoveIndex = speciesEggMoves[rootSpeciesId].findIndex(m => m === moveId); - if (eggMoveIndex > -1 && eggMoves & Math.pow(2, eggMoveIndex)) { + if (eggMoveIndex > -1 && (eggMoves & (1 << eggMoveIndex))) { continue; } } diff --git a/src/field/pokemon.ts b/src/field/pokemon.ts index 71e89d60cbd..2ed1b7e7355 100644 --- a/src/field/pokemon.ts +++ b/src/field/pokemon.ts @@ -1389,8 +1389,8 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { return false; } - const rand1 = Utils.binToDec(Utils.decToBin(this.id).substring(0, 16)); - const rand2 = Utils.binToDec(Utils.decToBin(this.id).substring(16, 32)); + const rand1 = (this.id & 0xFFFF0000) >>> 16; + const rand2 = (this.id & 0x0000FFFF); const E = this.scene.gameData.trainerId ^ this.scene.gameData.secretId; const F = rand1 ^ rand2; diff --git a/src/modifier/modifier.ts b/src/modifier/modifier.ts index 230ebc9251d..91041dc7564 100644 --- a/src/modifier/modifier.ts +++ b/src/modifier/modifier.ts @@ -1576,11 +1576,11 @@ export class PokemonNatureChangeModifier extends ConsumablePokemonModifier { const pokemon = args[0] as Pokemon; pokemon.natureOverride = this.nature; let speciesId = pokemon.species.speciesId; - pokemon.scene.gameData.dexData[speciesId].natureAttr |= Math.pow(2, this.nature + 1); + pokemon.scene.gameData.dexData[speciesId].natureAttr |= 1 << (this.nature + 1); while (pokemonPrevolutions.hasOwnProperty(speciesId)) { speciesId = pokemonPrevolutions[speciesId]; - pokemon.scene.gameData.dexData[speciesId].natureAttr |= Math.pow(2, this.nature + 1); + pokemon.scene.gameData.dexData[speciesId].natureAttr |= 1 << (this.nature + 1); } return true; diff --git a/src/system/game-data.ts b/src/system/game-data.ts index 6b8f183e94a..161b5a2c49b 100644 --- a/src/system/game-data.ts +++ b/src/system/game-data.ts @@ -349,7 +349,7 @@ export class GameData { this.scene.ui.savingIcon.show(); const data = this.getSystemSaveData(); - const maxIntAttrValue = Math.pow(2, 31); + const maxIntAttrValue = 0x80000000; const systemData = JSON.stringify(data, (k: any, v: any) => typeof v === "bigint" ? v <= maxIntAttrValue ? Number(v) : v.toString() : v); localStorage.setItem(`data_${loggedInUser.username}`, encrypt(systemData, bypassLogin)); @@ -1163,7 +1163,7 @@ export class GameData { } const sessionData = useCachedSession ? this.parseSessionData(decrypt(localStorage.getItem(`sessionData${scene.sessionSlotId ? scene.sessionSlotId : ""}_${loggedInUser.username}`), bypassLogin)) : this.getSessionSaveData(scene); - const maxIntAttrValue = Math.pow(2, 31); + const maxIntAttrValue = 0x80000000; const systemData = useCachedSystem ? this.parseSystemData(decrypt(localStorage.getItem(`data_${loggedInUser.username}`), bypassLogin)) : this.getSystemSaveData(); const request = { @@ -1368,7 +1368,7 @@ export class GameData { const entry = data[defaultStarterSpecies[ds]] as DexEntry; entry.seenAttr = defaultStarterAttr; entry.caughtAttr = defaultStarterAttr; - entry.natureAttr = Math.pow(2, defaultStarterNatures[ds] + 1); + entry.natureAttr = 1 << (defaultStarterNatures[ds] + 1); for (const i in entry.ivs) { entry.ivs[i] = 10; } @@ -1435,10 +1435,10 @@ export class GameData { dexEntry.caughtAttr |= dexAttr; if (speciesStarters.hasOwnProperty(species.speciesId)) { this.starterData[species.speciesId].abilityAttr |= pokemon.abilityIndex !== 1 || pokemon.species.ability2 - ? Math.pow(2, pokemon.abilityIndex) + ? 1 << pokemon.abilityIndex : AbilityAttr.ABILITY_HIDDEN; } - dexEntry.natureAttr |= Math.pow(2, pokemon.nature + 1); + dexEntry.natureAttr |= 1 << (pokemon.nature + 1); const hasPrevolution = pokemonPrevolutions.hasOwnProperty(species.speciesId); const newCatch = !caughtAttr; @@ -1474,7 +1474,7 @@ export class GameData { } if (!hasPrevolution && (!pokemon.scene.gameMode.isDaily || hasNewAttr || fromEgg)) { - this.addStarterCandy(species, (1 * (pokemon.isShiny() ? 5 * Math.pow(2, pokemon.variant || 0) : 1)) * (fromEgg || pokemon.isBoss() ? 2 : 1)); + this.addStarterCandy(species, (1 * (pokemon.isShiny() ? 5 * (1 << (pokemon.variant ?? 0)) : 1)) * (fromEgg || pokemon.isBoss() ? 2 : 1)); } } @@ -1545,7 +1545,7 @@ export class GameData { this.starterData[speciesId].eggMoves = 0; } - const value = Math.pow(2, eggMoveIndex); + const value = 1 << eggMoveIndex; if (this.starterData[speciesId].eggMoves & value) { resolve(false); @@ -1637,7 +1637,7 @@ export class GameData { getSpeciesDefaultNature(species: PokemonSpecies): Nature { const dexEntry = this.dexData[species.speciesId]; for (let n = 0; n < 25; n++) { - if (dexEntry.natureAttr & Math.pow(2, n + 1)) { + if (dexEntry.natureAttr & (1 << (n + 1))) { return n as Nature; } } @@ -1645,7 +1645,7 @@ export class GameData { } getSpeciesDefaultNatureAttr(species: PokemonSpecies): integer { - return Math.pow(2, this.getSpeciesDefaultNature(species)); + return 1 << (this.getSpeciesDefaultNature(species)); } getDexAttrLuck(dexAttr: bigint): integer { @@ -1655,7 +1655,7 @@ export class GameData { getNaturesForAttr(natureAttr: integer): Nature[] { const ret: Nature[] = []; for (let n = 0; n < 25; n++) { - if (natureAttr & Math.pow(2, n + 1)) { + if (natureAttr & (1 << (n + 1))) { ret.push(n); } } @@ -1697,7 +1697,7 @@ export class GameData { } getFormAttr(formIndex: integer): bigint { - return BigInt(Math.pow(2, 7 + formIndex)); + return BigInt(1 << (7 + formIndex)); } consolidateDexData(dexData: DexData): void { @@ -1707,7 +1707,7 @@ export class GameData { entry.hatchedCount = 0; } if (!entry.hasOwnProperty("natureAttr") || (entry.caughtAttr && !entry.natureAttr)) { - entry.natureAttr = this.defaultDexData[k].natureAttr || Math.pow(2, Utils.randInt(25, 1)); + entry.natureAttr = this.defaultDexData[k].natureAttr || (1 << Utils.randInt(25, 1)); } } } diff --git a/src/ui/pokemon-info-container.ts b/src/ui/pokemon-info-container.ts index 1e958ae53b7..b2ee5a9164a 100644 --- a/src/ui/pokemon-info-container.ts +++ b/src/ui/pokemon-info-container.ts @@ -216,7 +216,7 @@ export default class PokemonInfoContainer extends Phaser.GameObjects.Container { this.pokemonGenderText.setShadowColor(getGenderColor(pokemon.gender, true)); this.pokemonGenderText.setVisible(true); - const newGender = BigInt(Math.pow(2, pokemon.gender)) * DexAttr.MALE; + const newGender = BigInt(1 << pokemon.gender) * DexAttr.MALE; this.pokemonGenderNewText.setText("(+)"); this.pokemonGenderNewText.setColor(getTextColor(TextStyle.SUMMARY_BLUE, false, this.scene.uiTheme)); this.pokemonGenderNewText.setShadowColor(getTextColor(TextStyle.SUMMARY_BLUE, true, this.scene.uiTheme)); @@ -229,7 +229,7 @@ export default class PokemonInfoContainer extends Phaser.GameObjects.Container { if (pokemon.species.forms?.[pokemon.formIndex]?.formName) { this.pokemonFormLabelText.setVisible(true); this.pokemonFormText.setVisible(true); - const newForm = BigInt(Math.pow(2, pokemon.formIndex)) * DexAttr.DEFAULT_FORM; + const newForm = BigInt(1 << pokemon.formIndex) * DexAttr.DEFAULT_FORM; if ((newForm & caughtAttr) === BigInt(0)) { this.pokemonFormLabelText.setColor(getTextColor(TextStyle.SUMMARY_BLUE, false, this.scene.uiTheme)); @@ -266,7 +266,7 @@ export default class PokemonInfoContainer extends Phaser.GameObjects.Container { */ const opponentPokemonOneNormalAbility = (pokemon.species.getAbilityCount() === 2); const opponentPokemonAbilityIndex = (opponentPokemonOneNormalAbility && pokemon.abilityIndex === 1) ? 2 : pokemon.abilityIndex; - const opponentPokemonAbilityAttr = Math.pow(2, opponentPokemonAbilityIndex); + const opponentPokemonAbilityAttr = 1 << opponentPokemonAbilityIndex; const rootFormHasHiddenAbility = pokemon.scene.gameData.starterData[pokemon.species.getRootSpeciesId()].abilityAttr & opponentPokemonAbilityAttr; @@ -281,7 +281,7 @@ export default class PokemonInfoContainer extends Phaser.GameObjects.Container { this.pokemonNatureText.setText(getNatureName(pokemon.getNature(), true, false, false, this.scene.uiTheme)); const dexNatures = pokemon.scene.gameData.dexData[pokemon.species.speciesId].natureAttr; - const newNature = Math.pow(2, pokemon.nature + 1); + const newNature = 1 << (pokemon.nature + 1); if (!(dexNatures & newNature)) { this.pokemonNatureLabelText.setColor(getTextColor(TextStyle.SUMMARY_BLUE, false, this.scene.uiTheme)); @@ -305,8 +305,8 @@ export default class PokemonInfoContainer extends Phaser.GameObjects.Container { this.pokemonShinyIcon.on("pointerover", () => (this.scene as BattleScene).ui.showTooltip(null, `${i18next.t("common:shinyOnHover")}${shinyDescriptor ? ` (${shinyDescriptor})` : ""}`, true)); this.pokemonShinyIcon.on("pointerout", () => (this.scene as BattleScene).ui.hideTooltip()); - const newShiny = BigInt(Math.pow(2, (pokemon.shiny ? 1 : 0))); - const newVariant = BigInt(Math.pow(2, pokemon.variant + 4)); + const newShiny = BigInt(1 << (pokemon.shiny ? 1 : 0)); + const newVariant = BigInt(1 << (pokemon.variant + 4)); this.pokemonShinyNewIcon.setText("(+)"); this.pokemonShinyNewIcon.setColor(getTextColor(TextStyle.SUMMARY_BLUE, false, this.scene.uiTheme)); diff --git a/src/ui/starter-select-ui-handler.ts b/src/ui/starter-select-ui-handler.ts index 0b9210af646..08111908dda 100644 --- a/src/ui/starter-select-ui-handler.ts +++ b/src/ui/starter-select-ui-handler.ts @@ -2611,7 +2611,7 @@ export default class StarterSelectUiHandler extends MessageUiHandler { this.speciesStarterMoves.push(...levelMoves.filter(lm => lm[0] > 0 && lm[0] <= 5).map(lm => lm[1])); if (speciesEggMoves.hasOwnProperty(species.speciesId)) { for (let em = 0; em < 4; em++) { - if (this.scene.gameData.starterData[species.speciesId].eggMoves & Math.pow(2, em)) { + if (this.scene.gameData.starterData[species.speciesId].eggMoves & (1 << em)) { this.speciesStarterMoves.push(speciesEggMoves[species.speciesId][em]); } } @@ -2623,7 +2623,7 @@ export default class StarterSelectUiHandler extends MessageUiHandler { ? speciesMoveData as StarterMoveset : (speciesMoveData as StarterFormMoveData)[formIndex] : null; - const availableStarterMoves = this.speciesStarterMoves.concat(speciesEggMoves.hasOwnProperty(species.speciesId) ? speciesEggMoves[species.speciesId].filter((_, em: integer) => this.scene.gameData.starterData[species.speciesId].eggMoves & Math.pow(2, em)) : []); + const availableStarterMoves = this.speciesStarterMoves.concat(speciesEggMoves.hasOwnProperty(species.speciesId) ? speciesEggMoves[species.speciesId].filter((_, em: integer) => this.scene.gameData.starterData[species.speciesId].eggMoves & (1 << em)) : []); this.starterMoveset = (moveData || (this.speciesStarterMoves.slice(0, 4) as StarterMoveset)).filter(m => availableStarterMoves.find(sm => sm === m)) as StarterMoveset; // Consolidate move data if it contains an incompatible move if (this.starterMoveset.length < 4 && this.starterMoveset.length < availableStarterMoves.length) { @@ -2680,7 +2680,7 @@ export default class StarterSelectUiHandler extends MessageUiHandler { for (let em = 0; em < 4; em++) { const eggMove = hasEggMoves ? allMoves[speciesEggMoves[species.speciesId][em]] : null; - const eggMoveUnlocked = eggMove && this.scene.gameData.starterData[species.speciesId].eggMoves & Math.pow(2, em); + const eggMoveUnlocked = eggMove && this.scene.gameData.starterData[species.speciesId].eggMoves & (1 << em); this.pokemonEggMoveBgs[em].setFrame(Type[eggMove ? eggMove.type : Type.UNKNOWN].toString().toLowerCase()); this.pokemonEggMoveLabels[em].setText(eggMove && eggMoveUnlocked ? eggMove.name : "???"); } diff --git a/src/utils.ts b/src/utils.ts index 467d7601d38..ede0a4bd78a 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -165,40 +165,20 @@ export function getPlayTimeString(totalSeconds: integer): string { return `${days.padStart(2, "0")}:${hours.padStart(2, "0")}:${minutes.padStart(2, "0")}:${seconds.padStart(2, "0")}`; } -export function binToDec(input: string): integer { - const place: integer[] = []; - const binary: string[] = []; - - let decimalNum = 0; - - for (let i = 0; i < input.length; i++) { - binary.push(input[i]); - place.push(Math.pow(2, i)); - decimalNum += place[i] * parseInt(binary[i]); - } - - return decimalNum; -} - -export function decToBin(input: integer): string { - let bin = ""; - let intNum = input; - while (intNum > 0) { - bin = intNum % 2 ? `1${bin}` : `0${bin}`; - intNum = Math.floor(intNum * 0.5); - } - - return bin; -} - -export function getIvsFromId(id: integer): integer[] { +/** + * Generates IVs from a given {@linkcode id} by extracting 5 bits at a time + * starting from the least significant bit up to the 30th most significant bit. + * @param id 32-bit number + * @returns An array of six numbers corresponding to 5-bit chunks from {@linkcode id} + */ +export function getIvsFromId(id: number): number[] { return [ - binToDec(decToBin(id).substring(0, 5)), - binToDec(decToBin(id).substring(5, 10)), - binToDec(decToBin(id).substring(10, 15)), - binToDec(decToBin(id).substring(15, 20)), - binToDec(decToBin(id).substring(20, 25)), - binToDec(decToBin(id).substring(25, 30)) + (id & 0x3E000000) >>> 25, + (id & 0x01F00000) >>> 20, + (id & 0x000F8000) >>> 15, + (id & 0x00007C00) >>> 10, + (id & 0x000003E0) >>> 5, + (id & 0x0000001F) ]; }