Merge branch 'beta' into evilleaderchanges

This commit is contained in:
Blitzy 2025-07-25 20:51:05 -05:00 committed by GitHub
commit d179894018
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
535 changed files with 1312 additions and 977 deletions

View File

@ -41,4 +41,7 @@ jobs:
run: pnpm biome-ci
- name: Check dependencies with depcruise
run: pnpm depcruise
run: pnpm depcruise
- name: Lint with ls-lint
run: pnpm ls-lint

View File

@ -44,4 +44,4 @@ jobs:
run: pnpm i
- name: Run tests
run: pnpm exec vitest --project ${{ inputs.project }} --no-isolate --shard=${{ inputs.shard }}/${{ inputs.totalShards }} ${{ !runner.debug && '--silent' || '' }}
run: pnpm test:silent --shard=${{ inputs.shard }}/${{ inputs.totalShards }}

27
.ls-lint.yml Normal file
View File

@ -0,0 +1,27 @@
# Base settings to use
# Note that the `_cfg` key isn't part of ls-lint's configuration, it's just a YAML anchor for reuse.
_cfg: &cfg
.ps1: kebab-case
.ts: kebab-case
.js: kebab-case
.*.ts: kebab-case
.*.js: kebab-case
.dir: kebab-case
.py: snake_case # python files should always use snake_case
ls:
<<: *cfg
src:
<<: *cfg
.dir: kebab-case | regex:@types
.js: exists:0
src/system/version-migration/versions:
.ts: snake_case
<<: *cfg
ignore:
- node_modules
- .vscode
- .github
- .git
- public

View File

@ -104,7 +104,7 @@ Most non-trivial changes (*especially bug fixes*) should come along with new tes
- Test edge cases. A good strategy is to think of edge cases beforehand and create tests for them using `it.todo`. Once the edge case has been handled, you can remove the `todo` marker.
## 😈 Development Save File
> Some issues may require you to have unlocks on your save file which go beyond normal overrides. For this reason, the repository contains a [save file](../test/testUtils/saves/everything.psrv) with _everything_ unlocked (even ones not legitimately obtainable, like unimplemented variant shinies).
> Some issues may require you to have unlocks on your save file which go beyond normal overrides. For this reason, the repository contains a [save file](../test/test-utils/saves/everything.psrv) with _everything_ unlocked (even ones not legitimately obtainable, like unimplemented variant shinies).
1. Start the game up locally and navigate to `Menu -> Manage Data -> Import Data`
2. Select [everything.prsv](test/testUtils/saves/everything.prsv) (`test/testUtils/saves/everything.prsv`) and confirm.
2. Select [everything.prsv](test/test-utils/saves/everything.prsv) (`test/test-utils/saves/everything.prsv`) and confirm.

2
global.d.ts vendored
View File

@ -8,7 +8,7 @@ declare global {
* Can technically be undefined/null but for ease of use we are going to assume it is always defined.
* Used to load i18n files exclusively.
*
* To set up your own server in a test see `game_data.test.ts`
* To set up your own server in a test see `game-data.test.ts`
*/
var server: SetupServerApi;
}

View File

@ -1,11 +1,13 @@
pre-commit:
skip:
- merge
- rebase
commands:
biome-lint:
run: pnpm exec biome check --write --reporter=summary --staged --no-errors-on-unmatched
stage_fixed: true
skip:
- merge
- rebase
ls-lint:
run: pnpm exec ls-lint
post-merge:
commands:

View File

@ -12,7 +12,7 @@
"test": "vitest run --no-isolate",
"test:cov": "vitest run --coverage --no-isolate",
"test:watch": "vitest watch --coverage --no-isolate",
"test:silent": "vitest run --silent --no-isolate",
"test:silent": "vitest run --silent='passed-only' --no-isolate",
"test:create": "node scripts/create-test/create-test.js",
"typecheck": "tsc --noEmit",
"eslint": "eslint --fix .",
@ -28,6 +28,7 @@
},
"devDependencies": {
"@biomejs/biome": "2.0.0",
"@ls-lint/ls-lint": "2.3.1",
"@types/jsdom": "^21.1.7",
"@types/node": "^22.16.3",
"@vitest/coverage-istanbul": "^3.2.4",

View File

@ -45,6 +45,9 @@ importers:
'@biomejs/biome':
specifier: 2.0.0
version: 2.0.0
'@ls-lint/ls-lint':
specifier: 2.3.1
version: 2.3.1
'@types/jsdom':
specifier: ^21.1.7
version: 21.1.7
@ -565,6 +568,12 @@ packages:
'@jridgewell/trace-mapping@0.3.29':
resolution: {integrity: sha512-uw6guiW/gcAGPDhLmd77/6lW8QLeiV5RUTsAX46Db6oLhGaVj4lhnPwb184s1bkc8kdVg/+h988dro8GRDpmYQ==}
'@ls-lint/ls-lint@2.3.1':
resolution: {integrity: sha512-vPe6IDByQnQRTxcAYjTxrmga/tSIui50VBFTB5KIJWY3OOFmxE2VtymjeSEfQfiMbhZV/ZPAqYy2lt8pZFQ0Rw==}
cpu: [x64, arm64, s390x, ppc64le]
os: [darwin, linux, win32]
hasBin: true
'@material/material-color-utilities@0.2.7':
resolution: {integrity: sha512-0FCeqG6WvK4/Cc06F/xXMd/pv4FeisI0c1tUpBbfhA2n9Y8eZEv4Karjbmf2ZqQCPUWMrGp8A571tCjizxoTiQ==}
@ -2452,6 +2461,8 @@ snapshots:
'@jridgewell/resolve-uri': 3.1.2
'@jridgewell/sourcemap-codec': 1.5.4
'@ls-lint/ls-lint@2.3.1': {}
'@material/material-color-utilities@0.2.7': {}
'@mswjs/interceptors@0.39.2':

View File

@ -1,7 +1,7 @@
import { AbilityId } from "#enums/ability-id";
import { MoveId } from "#enums/move-id";
import { SpeciesId } from "#enums/species-id";
import { GameManager } from "#test/testUtils/gameManager";
import { GameManager } from "#test/test-utils/game-manager";
import Phaser from "phaser";
import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest";

View File

@ -2,13 +2,13 @@ import re
filenames = [['src/enums/moves.ts', 'move'], ['src/enums/abilities.ts', 'ability'], ['src/enums/species.ts', 'Pokémon']]
commentBlockStart = re.compile('\/\*[^\*].*') # Regex for the start of a comment block
commentBlockEnd = re.compile('.*,\*\/') # Regex for the end of a comment block
commentBlockStart = re.compile(r'\/\*[^\*].*') # Regex for the start of a comment block
commentBlockEnd = re.compile(r'.*,\*\/') # Regex for the end of a comment block
commentExp = re.compile('(?:\/\*\*.*\*\/)') # Regex for a url comment that already existed in the file
commentExp = re.compile(r'(?:\/\*\*.*\*\/)') # Regex for a url comment that already existed in the file
enumExp = re.compile('.*,') # Regex for a regular enum line
numberExp = re.compile(' +\= +\d+,')
numberExp = re.compile(r' +\= +\d+,')
replaceList = ['ALOLA', 'ETERNAL', 'GALAR', 'HISUI', 'PALDEA', 'BLOODMOON']

View File

@ -1,4 +1,4 @@
import type { UserInfo } from "#types/UserInfo";
import type { UserInfo } from "#types/user-info";
export interface AccountInfoResponse extends UserInfo {}

View File

@ -1,4 +1,4 @@
export class UpdateSessionSavedataRequest {
export interface UpdateSessionSavedataRequest {
slot: number;
trainerId: number;
secretId: number;

View File

@ -4,7 +4,7 @@ export interface GetSystemSavedataRequest {
clientSessionId: string;
}
export class UpdateSystemSavedataRequest {
export interface UpdateSystemSavedataRequest {
clientSessionId: string;
trainerId?: number;
secretId?: number;

View File

@ -1,6 +1,6 @@
import { pokerogueApi } from "#api/pokerogue-api";
import { bypassLogin } from "#app/global-vars/bypass-login";
import type { UserInfo } from "#types/UserInfo";
import type { UserInfo } from "#types/user-info";
import { randomString } from "#utils/common";
export let loggedInUser: UserInfo | null = null;

View File

@ -699,16 +699,16 @@ export class BattleScene extends SceneBase {
if (expSpriteKeys.size > 0) {
return;
}
this.cachedFetch("./exp-sprites.json")
.then(res => res.json())
.then(keys => {
if (Array.isArray(keys)) {
for (const key of keys) {
expSpriteKeys.add(key);
}
}
Promise.resolve();
});
const res = await this.cachedFetch("./exp-sprites.json");
const keys = await res.json();
if (!Array.isArray(keys)) {
throw new Error("EXP Sprites were not array when fetched!");
}
// TODO: Optimize this
for (const k of keys) {
expSpriteKeys.add(k);
}
}
/**
@ -1669,6 +1669,11 @@ export class BattleScene extends SceneBase {
case SpeciesId.MAUSHOLD:
case SpeciesId.DUDUNSPARCE:
return !randSeedInt(4) ? 1 : 0;
case SpeciesId.SINISTEA:
case SpeciesId.POLTEAGEIST:
case SpeciesId.POLTCHAGEIST:
case SpeciesId.SINISTCHA:
return !randSeedInt(16) ? 1 : 0;
case SpeciesId.PIKACHU:
if (this.currentBattle?.battleType === BattleType.TRAINER && this.currentBattle?.waveIndex < 30) {
return 0; // Ban Cosplay and Partner Pika from Trainers before wave 30

View File

@ -28,12 +28,12 @@ import { BattlerTagType } from "#enums/battler-tag-type";
import type { BerryType } from "#enums/berry-type";
import { Command } from "#enums/command";
import { HitResult } from "#enums/hit-result";
import { MoveCategory } from "#enums/MoveCategory";
import { MoveFlags } from "#enums/MoveFlags";
import { MoveTarget } from "#enums/MoveTarget";
import { CommonAnim } from "#enums/move-anims-common";
import { MoveCategory } from "#enums/move-category";
import { MoveFlags } from "#enums/move-flags";
import { MoveId } from "#enums/move-id";
import { MoveResult } from "#enums/move-result";
import { MoveTarget } from "#enums/move-target";
import { MoveUseMode } from "#enums/move-use-mode";
import { PokemonAnimType } from "#enums/pokemon-anim-type";
import { PokemonType } from "#enums/pokemon-type";

View File

@ -9,10 +9,10 @@ import { ArenaTagType } from "#enums/arena-tag-type";
import type { BattlerIndex } from "#enums/battler-index";
import { BattlerTagType } from "#enums/battler-tag-type";
import { HitResult } from "#enums/hit-result";
import { MoveCategory } from "#enums/MoveCategory";
import { MoveTarget } from "#enums/MoveTarget";
import { CommonAnim } from "#enums/move-anims-common";
import { MoveCategory } from "#enums/move-category";
import { MoveId } from "#enums/move-id";
import { MoveTarget } from "#enums/move-target";
import { MoveUseMode } from "#enums/move-use-mode";
import { PokemonType } from "#enums/pokemon-type";
import { Stat } from "#enums/stat";

View File

@ -86,7 +86,7 @@ export enum BiomePoolTier {
export const uncatchableSpecies: SpeciesId[] = [];
export interface SpeciesTree {
interface SpeciesTree {
[key: number]: SpeciesId[]
}
@ -94,11 +94,11 @@ export interface PokemonPools {
[key: number]: (SpeciesId | SpeciesTree)[]
}
export interface BiomeTierPokemonPools {
interface BiomeTierPokemonPools {
[key: number]: PokemonPools
}
export interface BiomePokemonPools {
interface BiomePokemonPools {
[key: number]: BiomeTierPokemonPools
}
@ -2022,7 +2022,6 @@ export const biomeTrainerPools: BiomeTrainerPools = {
}
};
// biome-ignore lint/complexity/noExcessiveCognitiveComplexity: init methods are expected to have many lines.
export function initBiomes() {
const pokemonBiomes = [
[ SpeciesId.BULBASAUR, PokemonType.GRASS, PokemonType.POISON, [

View File

@ -3,8 +3,8 @@ import { allMoves } from "#data/data-lists";
import type { BattlerIndex } from "#enums/battler-index";
import { BattlerTagType } from "#enums/battler-tag-type";
import { EncounterAnim } from "#enums/encounter-anims";
import { MoveFlags } from "#enums/MoveFlags";
import { AnimBlendType, AnimFocus, AnimFrameTarget, ChargeAnim, CommonAnim } from "#enums/move-anims-common";
import { MoveFlags } from "#enums/move-flags";
import { MoveId } from "#enums/move-id";
import type { Pokemon } from "#field/pokemon";
import { animationFileName, coerceArray, getFrameMs, isNullOrUndefined, type nil } from "#utils/common";

View File

@ -11,9 +11,9 @@ import { AbilityId } from "#enums/ability-id";
import { BattlerTagLapseType } from "#enums/battler-tag-lapse-type";
import { BattlerTagType } from "#enums/battler-tag-type";
import { HitResult } from "#enums/hit-result";
import { MoveCategory } from "#enums/MoveCategory";
import { MoveFlags } from "#enums/MoveFlags";
import { ChargeAnim, CommonAnim } from "#enums/move-anims-common";
import { MoveCategory } from "#enums/move-category";
import { MoveFlags } from "#enums/move-flags";
import { MoveId } from "#enums/move-id";
import { MoveResult } from "#enums/move-result";
import { MoveUseMode } from "#enums/move-use-mode";

View File

@ -1,8 +1,8 @@
import { allMoves } from "#data/data-lists";
import type { BattlerIndex } from "#enums/battler-index";
import { BattlerTagType } from "#enums/battler-tag-type";
import { MoveTarget } from "#enums/MoveTarget";
import type { MoveId } from "#enums/move-id";
import { MoveTarget } from "#enums/move-target";
import { PokemonType } from "#enums/pokemon-type";
import type { Pokemon } from "#field/pokemon";
import { applyMoveAttrs } from "#moves/apply-attrs";
@ -27,6 +27,28 @@ export function isFieldTargeted(move: Move): boolean {
return false;
}
/**
* Determine whether a move is a spread move.
*
* @param move - The {@linkcode Move} to check
* @returns Whether {@linkcode move} is spread-targeted.
* @remarks
* Examples include:
* - Moves targeting all adjacent Pokemon (like Surf)
* - Moves targeting all adjacent enemies (like Air Cutter)
*/
export function isSpreadMove(move: Move): boolean {
switch (move.moveTarget) {
case MoveTarget.ALL_ENEMIES:
case MoveTarget.ALL_NEAR_ENEMIES:
case MoveTarget.ALL_OTHERS:
case MoveTarget.ALL_NEAR_OTHERS:
return true;
}
return false;
}
export function getMoveTargets(user: Pokemon, move: MoveId, replaceTarget?: MoveTarget): MoveTargetSet {
const variableTarget = new NumberHolder(0);
user.getOpponents(false).forEach(p => applyMoveAttrs("VariableTargetAttr", user, p, allMoves[move], variableTarget));

View File

@ -48,11 +48,11 @@ import { ChargeAnim } from "#enums/move-anims-common";
import { MoveId } from "#enums/move-id";
import { MoveResult } from "#enums/move-result";
import { isVirtual, MoveUseMode } from "#enums/move-use-mode";
import { MoveCategory } from "#enums/MoveCategory";
import { MoveEffectTrigger } from "#enums/MoveEffectTrigger";
import { MoveFlags } from "#enums/MoveFlags";
import { MoveTarget } from "#enums/MoveTarget";
import { MultiHitType } from "#enums/MultiHitType";
import { MoveCategory } from "#enums/move-category";
import { MoveEffectTrigger } from "#enums/move-effect-trigger";
import { MoveFlags } from "#enums/move-flags";
import { MoveTarget } from "#enums/move-target";
import { MultiHitType } from "#enums/multi-hit-type";
import { PokemonType } from "#enums/pokemon-type";
import { SpeciesId } from "#enums/species-id";
import {
@ -808,16 +808,14 @@ export abstract class Move implements Localizable {
}
const power = new NumberHolder(this.power);
applyMoveAttrs("VariablePowerAttr", source, target, this, power);
const typeChangeMovePowerMultiplier = new NumberHolder(1);
const typeChangeHolder = new NumberHolder(this.type);
applyAbAttrs("MoveTypeChangeAbAttr", {pokemon: source, opponent: target, move: this, simulated: true, moveType: typeChangeHolder, power: typeChangeMovePowerMultiplier});
const sourceTeraType = source.getTeraType();
if (source.isTerastallized && sourceTeraType === this.type && power.value < 60 && this.priority <= 0 && !this.hasAttr("MultiHitAttr") && !globalScene.findModifier(m => m instanceof PokemonMultiHitModifier && m.pokemonId === source.id)) {
power.value = 60;
}
const abAttrParams: PreAttackModifyPowerAbAttrParams = {
pokemon: source,
opponent: target,
@ -832,6 +830,13 @@ export abstract class Move implements Localizable {
applyAbAttrs("AllyMoveCategoryPowerBoostAbAttr", {...abAttrParams, pokemon: ally});
}
// Non-priority, single-hit moves of the user's Tera Type are always a bare minimum of 60 power
const sourceTeraType = source.getTeraType();
if (source.isTerastallized && sourceTeraType === this.type && power.value < 60 && this.priority <= 0 && !this.hasAttr("MultiHitAttr") && !globalScene.findModifier(m => m instanceof PokemonMultiHitModifier && m.pokemonId === source.id)) {
power.value = 60;
}
const fieldAuras = new Set(
globalScene.getField(true)
.map((p) => p.getAbilityAttrs("FieldMoveTypePowerBoostAbAttr").filter(attr => {
@ -855,7 +860,6 @@ export abstract class Move implements Localizable {
power.value *= typeBoost.boostValue;
}
applyMoveAttrs("VariablePowerAttr", source, target, this, power);
if (!this.hasAttr("TypelessAttr")) {
globalScene.arena.applyTags(WeakenMoveTypeTag, simulated, typeChangeHolder.value, power);

View File

@ -8,9 +8,9 @@ import { BattlerIndex } from "#enums/battler-index";
import { BerryType } from "#enums/berry-type";
import { Challenges } from "#enums/challenges";
import { EncounterAnim } from "#enums/encounter-anims";
import { MoveCategory } from "#enums/MoveCategory";
import { ModifierPoolType } from "#enums/modifier-pool-type";
import { ModifierTier } from "#enums/modifier-tier";
import { MoveCategory } from "#enums/move-category";
import { MoveId } from "#enums/move-id";
import { MoveUseMode } from "#enums/move-use-mode";
import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode";

View File

@ -1,7 +1,7 @@
import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/constants";
import { globalScene } from "#app/global-scene";
import { modifierTypes } from "#data/data-lists";
import { MoveCategory } from "#enums/MoveCategory";
import { MoveCategory } from "#enums/move-category";
import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode";
import { MysteryEncounterTier } from "#enums/mystery-encounter-tier";
import { MysteryEncounterType } from "#enums/mystery-encounter-type";

View File

@ -18,7 +18,7 @@ import {
} from "#data/form-change-triggers";
import { AbilityId } from "#enums/ability-id";
import { FormChangeItem } from "#enums/form-change-item";
import { MoveCategory } from "#enums/MoveCategory";
import { MoveCategory } from "#enums/move-category";
import { MoveId } from "#enums/move-id";
import { SpeciesFormKey } from "#enums/species-form-key";
import { SpeciesId } from "#enums/species-id";

View File

@ -2851,11 +2851,11 @@ export function initSpecies() {
new PokemonSpecies(SpeciesId.GRAPPLOCT, 8, false, false, false, "Jujitsu Pokémon", PokemonType.FIGHTING, null, 1.6, 39, AbilityId.LIMBER, AbilityId.NONE, AbilityId.TECHNICIAN, 480, 80, 118, 90, 70, 80, 42, 45, 50, 168, GrowthRate.MEDIUM_SLOW, 50, false),
new PokemonSpecies(SpeciesId.SINISTEA, 8, false, false, false, "Black Tea Pokémon", PokemonType.GHOST, null, 0.1, 0.2, AbilityId.WEAK_ARMOR, AbilityId.NONE, AbilityId.CURSED_BODY, 308, 40, 45, 45, 74, 54, 50, 120, 50, 62, GrowthRate.MEDIUM_FAST, null, false, false,
new PokemonForm("Phony Form", "phony", PokemonType.GHOST, null, 0.1, 0.2, AbilityId.WEAK_ARMOR, AbilityId.NONE, AbilityId.CURSED_BODY, 308, 40, 45, 45, 74, 54, 50, 120, 50, 62, false, "", true),
new PokemonForm("Antique Form", "antique", PokemonType.GHOST, null, 0.1, 0.2, AbilityId.WEAK_ARMOR, AbilityId.NONE, AbilityId.CURSED_BODY, 308, 40, 45, 45, 74, 54, 50, 120, 50, 62, false, "", true, true),
new PokemonForm("Antique Form", "antique", PokemonType.GHOST, null, 0.1, 0.2, AbilityId.WEAK_ARMOR, AbilityId.NONE, AbilityId.CURSED_BODY, 308, 40, 45, 45, 74, 54, 50, 120, 50, 62, false, "", true),
),
new PokemonSpecies(SpeciesId.POLTEAGEIST, 8, false, false, false, "Black Tea Pokémon", PokemonType.GHOST, null, 0.2, 0.4, AbilityId.WEAK_ARMOR, AbilityId.NONE, AbilityId.CURSED_BODY, 508, 60, 65, 65, 134, 114, 70, 60, 50, 178, GrowthRate.MEDIUM_FAST, null, false, false,
new PokemonForm("Phony Form", "phony", PokemonType.GHOST, null, 0.2, 0.4, AbilityId.WEAK_ARMOR, AbilityId.NONE, AbilityId.CURSED_BODY, 508, 60, 65, 65, 134, 114, 70, 60, 50, 178, false, "", true),
new PokemonForm("Antique Form", "antique", PokemonType.GHOST, null, 0.2, 0.4, AbilityId.WEAK_ARMOR, AbilityId.NONE, AbilityId.CURSED_BODY, 508, 60, 65, 65, 134, 114, 70, 60, 50, 178, false, "", true, true),
new PokemonForm("Antique Form", "antique", PokemonType.GHOST, null, 0.2, 0.4, AbilityId.WEAK_ARMOR, AbilityId.NONE, AbilityId.CURSED_BODY, 508, 60, 65, 65, 134, 114, 70, 60, 50, 178, false, "", true),
),
new PokemonSpecies(SpeciesId.HATENNA, 8, false, false, false, "Calm Pokémon", PokemonType.PSYCHIC, null, 0.4, 3.4, AbilityId.HEALER, AbilityId.ANTICIPATION, AbilityId.MAGIC_BOUNCE, 265, 42, 30, 45, 56, 53, 39, 235, 50, 53, GrowthRate.SLOW, 0, false),
new PokemonSpecies(SpeciesId.HATTREM, 8, false, false, false, "Serene Pokémon", PokemonType.PSYCHIC, null, 0.6, 4.8, AbilityId.HEALER, AbilityId.ANTICIPATION, AbilityId.MAGIC_BOUNCE, 370, 57, 40, 65, 86, 73, 49, 120, 50, 130, GrowthRate.SLOW, 0, false),
@ -3109,11 +3109,11 @@ export function initSpecies() {
new PokemonSpecies(SpeciesId.DIPPLIN, 9, false, false, false, "Candy Apple Pokémon", PokemonType.GRASS, PokemonType.DRAGON, 0.4, 4.4, AbilityId.SUPERSWEET_SYRUP, AbilityId.GLUTTONY, AbilityId.STICKY_HOLD, 485, 80, 80, 110, 95, 80, 40, 45, 50, 170, GrowthRate.ERRATIC, 50, false),
new PokemonSpecies(SpeciesId.POLTCHAGEIST, 9, false, false, false, "Matcha Pokémon", PokemonType.GRASS, PokemonType.GHOST, 0.1, 1.1, AbilityId.HOSPITALITY, AbilityId.NONE, AbilityId.HEATPROOF, 308, 40, 45, 45, 74, 54, 50, 120, 50, 62, GrowthRate.SLOW, null, false, false,
new PokemonForm("Counterfeit Form", "counterfeit", PokemonType.GRASS, PokemonType.GHOST, 0.1, 1.1, AbilityId.HOSPITALITY, AbilityId.NONE, AbilityId.HEATPROOF, 308, 40, 45, 45, 74, 54, 50, 120, 50, 62, false, null, true),
new PokemonForm("Artisan Form", "artisan", PokemonType.GRASS, PokemonType.GHOST, 0.1, 1.1, AbilityId.HOSPITALITY, AbilityId.NONE, AbilityId.HEATPROOF, 308, 40, 45, 45, 74, 54, 50, 120, 50, 62, false, null, false, true),
new PokemonForm("Artisan Form", "artisan", PokemonType.GRASS, PokemonType.GHOST, 0.1, 1.1, AbilityId.HOSPITALITY, AbilityId.NONE, AbilityId.HEATPROOF, 308, 40, 45, 45, 74, 54, 50, 120, 50, 62, false, "counterfeit", true),
),
new PokemonSpecies(SpeciesId.SINISTCHA, 9, false, false, false, "Matcha Pokémon", PokemonType.GRASS, PokemonType.GHOST, 0.2, 2.2, AbilityId.HOSPITALITY, AbilityId.NONE, AbilityId.HEATPROOF, 508, 71, 60, 106, 121, 80, 70, 60, 50, 178, GrowthRate.SLOW, null, false, false,
new PokemonForm("Unremarkable Form", "unremarkable", PokemonType.GRASS, PokemonType.GHOST, 0.2, 2.2, AbilityId.HOSPITALITY, AbilityId.NONE, AbilityId.HEATPROOF, 508, 71, 60, 106, 121, 80, 70, 60, 50, 178),
new PokemonForm("Masterpiece Form", "masterpiece", PokemonType.GRASS, PokemonType.GHOST, 0.2, 2.2, AbilityId.HOSPITALITY, AbilityId.NONE, AbilityId.HEATPROOF, 508, 71, 60, 106, 121, 80, 70, 60, 50, 178, false, null, false, true),
new PokemonForm("Unremarkable Form", "unremarkable", PokemonType.GRASS, PokemonType.GHOST, 0.2, 2.2, AbilityId.HOSPITALITY, AbilityId.NONE, AbilityId.HEATPROOF, 508, 71, 60, 106, 121, 80, 70, 60, 50, 178, false, null, true),
new PokemonForm("Masterpiece Form", "masterpiece", PokemonType.GRASS, PokemonType.GHOST, 0.2, 2.2, AbilityId.HOSPITALITY, AbilityId.NONE, AbilityId.HEATPROOF, 508, 71, 60, 106, 121, 80, 70, 60, 50, 178, false, "unremarkable", true),
),
new PokemonSpecies(SpeciesId.OKIDOGI, 9, true, false, false, "Retainer Pokémon", PokemonType.POISON, PokemonType.FIGHTING, 1.8, 92.2, AbilityId.TOXIC_CHAIN, AbilityId.NONE, AbilityId.GUARD_DOG, 555, 88, 128, 115, 58, 86, 80, 3, 0, 276, GrowthRate.SLOW, 100, false),
new PokemonSpecies(SpeciesId.MUNKIDORI, 9, true, false, false, "Retainer Pokémon", PokemonType.POISON, PokemonType.PSYCHIC, 1, 12.2, AbilityId.TOXIC_CHAIN, AbilityId.NONE, AbilityId.FRISK, 555, 88, 75, 66, 130, 90, 106, 3, 0, 276, GrowthRate.SLOW, 100, false),

View File

@ -3,6 +3,7 @@ import type { BattlerIndex } from "#enums/battler-index";
import { PokemonType } from "#enums/pokemon-type";
import type { Pokemon } from "#field/pokemon";
import type { Move } from "#moves/move";
import { isFieldTargeted, isSpreadMove } from "#moves/move-utils";
import i18next from "i18next";
export enum TerrainType {
@ -60,13 +61,19 @@ export class Terrain {
isMoveTerrainCancelled(user: Pokemon, targets: BattlerIndex[], move: Move): boolean {
switch (this.terrainType) {
case TerrainType.PSYCHIC:
if (!move.hasAttr("ProtectAttr")) {
// Cancels move if the move has positive priority and targets a Pokemon grounded on the Psychic Terrain
return (
move.getPriority(user) > 0 &&
user.getOpponents(true).some(o => targets.includes(o.getBattlerIndex()) && o.isGrounded())
);
}
// Cf https://bulbapedia.bulbagarden.net/wiki/Psychic_Terrain_(move)#Generation_VII
// Psychic terrain will only cancel a move if it:
return (
// ... is neither spread nor field-targeted,
!isFieldTargeted(move) &&
!isSpreadMove(move) &&
// .. has positive final priority,
move.getPriority(user) > 0 &&
// ...and is targeting at least 1 grounded opponent
user
.getOpponents(true)
.some(o => targets.includes(o.getBattlerIndex()) && o.isGrounded())
);
}
return false;

View File

@ -82,11 +82,11 @@ import { DexAttr } from "#enums/dex-attr";
import { FieldPosition } from "#enums/field-position";
import { HitResult } from "#enums/hit-result";
import { LearnMoveSituation } from "#enums/learn-move-situation";
import { MoveCategory } from "#enums/MoveCategory";
import { MoveFlags } from "#enums/MoveFlags";
import { MoveTarget } from "#enums/MoveTarget";
import { ModifierTier } from "#enums/modifier-tier";
import { MoveCategory } from "#enums/move-category";
import { MoveFlags } from "#enums/move-flags";
import { MoveId } from "#enums/move-id";
import { MoveTarget } from "#enums/move-target";
import { isIgnorePP, isVirtual, MoveUseMode } from "#enums/move-use-mode";
import { Nature } from "#enums/nature";
import { PokeballType } from "#enums/pokeball";

35
src/init/init.ts Normal file
View File

@ -0,0 +1,35 @@
import { initAbilities } from "#abilities/ability";
import { initBiomes } from "#balance/biomes";
import { initEggMoves } from "#balance/egg-moves";
import { initPokemonPrevolutions, initPokemonStarters } from "#balance/pokemon-evolutions";
import { initChallenges } from "#data/challenge";
import { initTrainerTypeDialogue } from "#data/dialogue";
import { initPokemonForms } from "#data/pokemon-forms";
import { initSpecies } from "#data/pokemon-species";
import { initModifierPools } from "#modifiers/init-modifier-pools";
import { initModifierTypes } from "#modifiers/modifier-type";
import { initMoves } from "#moves/move";
import { initMysteryEncounters } from "#mystery-encounters/mystery-encounters";
import { initAchievements } from "#system/achv";
import { initVouchers } from "#system/voucher";
import { initStatsKeys } from "#ui/game-stats-ui-handler";
/** Initialize the game. */
export function initializeGame() {
initModifierTypes();
initModifierPools();
initAchievements();
initVouchers();
initStatsKeys();
initPokemonPrevolutions();
initPokemonStarters();
initBiomes();
initEggMoves();
initPokemonForms();
initTrainerTypeDialogue();
initSpecies();
initMoves();
initAbilities();
initChallenges();
initMysteryEncounters();
}

View File

@ -3,13 +3,13 @@ import { TouchControl } from "#app/touch-controls";
import { Button } from "#enums/buttons";
import { Device } from "#enums/devices";
import { UiMode } from "#enums/ui-mode";
import cfg_keyboard_qwerty from "#inputs/cfg_keyboard_qwerty";
import { assign, getButtonWithKeycode, getIconForLatestInput, swap } from "#inputs/configHandler";
import pad_dualshock from "#inputs/pad_dualshock";
import pad_generic from "#inputs/pad_generic";
import pad_procon from "#inputs/pad_procon";
import pad_unlicensedSNES from "#inputs/pad_unlicensedSNES";
import pad_xbox360 from "#inputs/pad_xbox360";
import cfg_keyboard_qwerty from "#inputs/cfg-keyboard-qwerty";
import { assign, getButtonWithKeycode, getIconForLatestInput, swap } from "#inputs/config-handler";
import pad_dualshock from "#inputs/pad-dualshock";
import pad_generic from "#inputs/pad-generic";
import pad_procon from "#inputs/pad-procon";
import pad_unlicensedSNES from "#inputs/pad-unlicensed-snes";
import pad_xbox360 from "#inputs/pad-xbox360";
import type { SettingGamepad } from "#system/settings-gamepad";
import type { SettingKeyboard } from "#system/settings-keyboard";
import { MoveTouchControlsHandler } from "#ui/move-touch-controls-handler";

View File

@ -1,29 +1,16 @@
import { initAbilities } from "#abilities/ability";
import { timedEventManager } from "#app/global-event-manager";
import { initializeGame } from "#app/init/init";
import { SceneBase } from "#app/scene-base";
import { isMobile } from "#app/touch-controls";
import { initBiomes } from "#balance/biomes";
import { initEggMoves } from "#balance/egg-moves";
import { initPokemonPrevolutions, initPokemonStarters } from "#balance/pokemon-evolutions";
import { initChallenges } from "#data/challenge";
import { initTrainerTypeDialogue } from "#data/dialogue";
import { initPokemonForms } from "#data/pokemon-forms";
import { initSpecies } from "#data/pokemon-species";
import { BiomeId } from "#enums/biome-id";
import { GachaType } from "#enums/gacha-types";
import { getBiomeHasProps } from "#field/arena";
import { initModifierPools } from "#modifiers/init-modifier-pools";
import { initModifierTypes } from "#modifiers/modifier-type";
import { initMoves } from "#moves/move";
import { initMysteryEncounters } from "#mystery-encounters/mystery-encounters";
import { CacheBustedLoaderPlugin } from "#plugins/cache-busted-loader-plugin";
import { initAchievements } from "#system/achv";
import { initVouchers } from "#system/voucher";
import { initStatsKeys } from "#ui/game-stats-ui-handler";
import { getWindowVariantSuffix, WindowVariant } from "#ui/ui-theme";
import { hasAllLocalizedSprites, localPing } from "#utils/common";
import { getEnumValues } from "#utils/enums";
import i18next from "i18next";
import type { GameObjects } from "phaser";
export class LoadingScene extends SceneBase {
public static readonly KEY = "loading";
@ -366,30 +353,12 @@ export class LoadingScene extends SceneBase {
this.loadLoadingScreen();
initModifierTypes();
initModifierPools();
initAchievements();
initVouchers();
initStatsKeys();
initPokemonPrevolutions();
initPokemonStarters();
initBiomes();
initEggMoves();
initPokemonForms();
initTrainerTypeDialogue();
initSpecies();
initMoves();
initAbilities();
initChallenges();
initMysteryEncounters();
initializeGame();
}
loadLoadingScreen() {
const mobile = isMobile();
const loadingGraphics: any[] = [];
const bg = this.add.image(0, 0, "");
bg.setOrigin(0, 0);
bg.setScale(6);
@ -460,6 +429,7 @@ export class LoadingScene extends SceneBase {
});
disclaimerDescriptionText.setOrigin(0.5, 0.5);
const loadingGraphics: (GameObjects.Image | GameObjects.Graphics | GameObjects.Text)[] = [];
loadingGraphics.push(
bg,
graphics,

View File

@ -279,6 +279,7 @@ export class AttemptCapturePhase extends PokemonPhase {
globalScene.updateModifiers(true);
removePokemon();
if (newPokemon) {
newPokemon.leaveField(true, true, false);
newPokemon.loadAssets().then(end);
} else {
end();

View File

@ -13,12 +13,12 @@ import { BattlerTagLapseType } from "#enums/battler-tag-lapse-type";
import { BattlerTagType } from "#enums/battler-tag-type";
import { HitCheckResult } from "#enums/hit-check-result";
import { HitResult } from "#enums/hit-result";
import { MoveCategory } from "#enums/MoveCategory";
import { MoveEffectTrigger } from "#enums/MoveEffectTrigger";
import { MoveFlags } from "#enums/MoveFlags";
import { MoveTarget } from "#enums/MoveTarget";
import { MoveCategory } from "#enums/move-category";
import { MoveEffectTrigger } from "#enums/move-effect-trigger";
import { MoveFlags } from "#enums/move-flags";
import { MoveId } from "#enums/move-id";
import { MoveResult } from "#enums/move-result";
import { MoveTarget } from "#enums/move-target";
import { isReflected, isVirtual, MoveUseMode } from "#enums/move-use-mode";
import { PokemonType } from "#enums/pokemon-type";
import type { Pokemon } from "#field/pokemon";

View File

@ -13,8 +13,8 @@ import { ArenaTagType } from "#enums/arena-tag-type";
import { BattlerIndex } from "#enums/battler-index";
import { BattlerTagLapseType } from "#enums/battler-tag-lapse-type";
import { BattlerTagType } from "#enums/battler-tag-type";
import { MoveFlags } from "#enums/MoveFlags";
import { CommonAnim } from "#enums/move-anims-common";
import { MoveFlags } from "#enums/move-flags";
import { MoveId } from "#enums/move-id";
import { MoveResult } from "#enums/move-result";
import { isIgnorePP, isIgnoreStatus, isReflected, isVirtual, MoveUseMode } from "#enums/move-use-mode";

View File

@ -2,8 +2,8 @@ import { globalScene } from "#app/global-scene";
import { getTerrainColor, TerrainType } from "#data/terrain";
import { getCurrentTime } from "#utils/common";
import Phaser from "phaser";
import fieldSpriteFragShader from "./glsl/fieldSpriteFragShader.frag?raw";
import spriteVertShader from "./glsl/spriteShader.vert?raw";
import fieldSpriteFragShader from "./glsl/field-sprite-frag-shader.frag?raw";
import spriteVertShader from "./glsl/sprite-shader.vert?raw";
export class FieldSpritePipeline extends Phaser.Renderer.WebGL.Pipelines.MultiPipeline {
constructor(game: Phaser.Game, config?: Phaser.Types.Renderer.WebGL.WebGLPipelineConfig) {

View File

@ -5,8 +5,8 @@ import { Pokemon } from "#field/pokemon";
import { Trainer } from "#field/trainer";
import { variantColorCache } from "#sprites/variant";
import { rgbHexToRgba } from "#utils/common";
import spriteFragShader from "./glsl/spriteFragShader.frag?raw";
import spriteVertShader from "./glsl/spriteShader.vert?raw";
import spriteFragShader from "./glsl/sprite-frag-shader.frag?raw";
import spriteVertShader from "./glsl/sprite-shader.vert?raw";
export class SpritePipeline extends FieldSpritePipeline {
private _tone: number[];

View File

@ -5,7 +5,7 @@ import type {
AccountLoginRequest,
AccountLoginResponse,
AccountRegisterRequest,
} from "#types/PokerogueAccountApi";
} from "#types/api/pokerogue-account-api";
import { removeCookie, setCookie } from "#utils/cookies";
/**

View File

@ -6,7 +6,7 @@ import type {
SearchAccountResponse,
UnlinkAccountFromDiscordIdRequest,
UnlinkAccountFromGoogledIdRequest,
} from "#types/PokerogueAdminApi";
} from "#types/api/pokerogue-admin-api";
export class PokerogueAdminApi extends ApiBase {
public readonly ERR_USERNAME_NOT_FOUND: string = "Username not found!";

View File

@ -3,7 +3,7 @@ import { PokerogueAccountApi } from "#api/pokerogue-account-api";
import { PokerogueAdminApi } from "#api/pokerogue-admin-api";
import { PokerogueDailyApi } from "#api/pokerogue-daily-api";
import { PokerogueSavedataApi } from "#api/pokerogue-savedata-api";
import type { TitleStatsResponse } from "#types/PokerogueApi";
import type { TitleStatsResponse } from "#types/api/pokerogue-api-types";
/**
* A wrapper for PokéRogue API requests.

View File

@ -1,5 +1,5 @@
import { ApiBase } from "#api/api-base";
import type { GetDailyRankingsPageCountRequest, GetDailyRankingsRequest } from "#types/PokerogueDailyApi";
import type { GetDailyRankingsPageCountRequest, GetDailyRankingsRequest } from "#types/api/pokerogue-daily-api";
import type { RankingEntry } from "#ui/daily-run-scoreboard";
/**

View File

@ -2,7 +2,7 @@ import { ApiBase } from "#api/api-base";
import { PokerogueSessionSavedataApi } from "#api/pokerogue-session-savedata-api";
import { PokerogueSystemSavedataApi } from "#api/pokerogue-system-savedata-api";
import { MAX_INT_ATTR_VALUE } from "#app/constants";
import type { UpdateAllSavedataRequest } from "#types/PokerogueSavedataApi";
import type { UpdateAllSavedataRequest } from "#types/api/pokerogue-save-data-api";
/**
* A wrapper for PokéRogue savedata API requests.

View File

@ -7,7 +7,7 @@ import type {
GetSessionSavedataRequest,
NewClearSessionSavedataRequest,
UpdateSessionSavedataRequest,
} from "#types/PokerogueSessionSavedataApi";
} from "#types/api/pokerogue-session-save-data-api";
/**
* A wrapper for PokéRogue session savedata API requests.

View File

@ -4,7 +4,7 @@ import type {
UpdateSystemSavedataRequest,
VerifySystemSavedataRequest,
VerifySystemSavedataResponse,
} from "#types/PokerogueSystemSavedataApi";
} from "#types/api/pokerogue-system-save-data-api";
/**
* A wrapper for PokéRogue system savedata API requests.

View File

@ -57,7 +57,7 @@ import {
applySessionVersionMigration,
applySettingsVersionMigration,
applySystemVersionMigration,
} from "#system/version_converter";
} from "#system/version-migration/version-converter";
import { VoucherType, vouchers } from "#system/voucher";
import { trainerConfigs } from "#trainers/trainer-config";
import type { DexData, DexEntry } from "#types/dex-data";

View File

@ -2,9 +2,9 @@
import { version } from "#package.json";
import type { SessionSaveData, SystemSaveData } from "#system/game-data";
import type { SessionSaveMigrator } from "#types/SessionSaveMigrator";
import type { SettingsSaveMigrator } from "#types/SettingsSaveMigrator";
import type { SystemSaveMigrator } from "#types/SystemSaveMigrator";
import type { SessionSaveMigrator } from "#types/session-save-migrator";
import type { SettingsSaveMigrator } from "#types/settings-save-migrator";
import type { SystemSaveMigrator } from "#types/system-save-migrator";
import { compareVersions } from "compare-versions";
/*
@ -49,11 +49,11 @@ export const settingsMigrators: readonly SettingsSaveMigrator[] = [settingsMigra
// Add migrator imports below:
// Example: import * as vA_B_C from "#system/vA_B_C";
import * as v1_0_4 from "#system/v1_0_4";
import * as v1_7_0 from "#system/v1_7_0";
import * as v1_8_3 from "#system/v1_8_3";
import * as v1_9_0 from "#system/v1_9_0";
import * as v1_10_0 from "#system/v1_10_0";
import * as v1_0_4 from "#system/version-migration/versions/v1_0_4";
import * as v1_7_0 from "#system/version-migration/versions/v1_7_0";
import * as v1_8_3 from "#system/version-migration/versions/v1_8_3";
import * as v1_9_0 from "#system/version-migration/versions/v1_9_0";
import * as v1_10_0 from "#system/version-migration/versions/v1_10_0";
/** Current game version */
const LATEST_VERSION = version;

View File

@ -5,9 +5,9 @@ import { AbilityAttr } from "#enums/ability-attr";
import { DexAttr } from "#enums/dex-attr";
import type { SessionSaveData, SystemSaveData } from "#system/game-data";
import { SettingKeys } from "#system/settings";
import type { SessionSaveMigrator } from "#types/SessionSaveMigrator";
import type { SettingsSaveMigrator } from "#types/SettingsSaveMigrator";
import type { SystemSaveMigrator } from "#types/SystemSaveMigrator";
import type { SessionSaveMigrator } from "#types/session-save-migrator";
import type { SettingsSaveMigrator } from "#types/settings-save-migrator";
import type { SystemSaveMigrator } from "#types/system-save-migrator";
import { isNullOrUndefined } from "#utils/common";
/**

View File

@ -3,7 +3,7 @@ import type { MoveId } from "#enums/move-id";
import type { MoveResult } from "#enums/move-result";
import { MoveUseMode } from "#enums/move-use-mode";
import type { SessionSaveData } from "#system/game-data";
import type { SessionSaveMigrator } from "#types/SessionSaveMigrator";
import type { SessionSaveMigrator } from "#types/session-save-migrator";
import type { TurnMove } from "#types/turn-move";
/** Prior signature of `TurnMove`; used to ensure parity */

View File

@ -2,8 +2,8 @@ import { globalScene } from "#app/global-scene";
import { getPokemonSpeciesForm } from "#data/pokemon-species";
import { DexAttr } from "#enums/dex-attr";
import type { SessionSaveData, SystemSaveData } from "#system/game-data";
import type { SessionSaveMigrator } from "#types/SessionSaveMigrator";
import type { SystemSaveMigrator } from "#types/SystemSaveMigrator";
import type { SessionSaveMigrator } from "#types/session-save-migrator";
import type { SystemSaveMigrator } from "#types/system-save-migrator";
import { isNullOrUndefined } from "#utils/common";
import { getPokemonSpecies } from "#utils/pokemon-utils";

View File

@ -1,7 +1,7 @@
import { DexAttr } from "#enums/dex-attr";
import { SpeciesId } from "#enums/species-id";
import type { SystemSaveData } from "#system/game-data";
import type { SystemSaveMigrator } from "#types/SystemSaveMigrator";
import type { SystemSaveMigrator } from "#types/system-save-migrator";
import { getPokemonSpecies } from "#utils/pokemon-utils";
/**

View File

@ -2,7 +2,7 @@ import { MoveId } from "#enums/move-id";
import { PokemonMove } from "#moves/pokemon-move";
import type { SessionSaveData } from "#system/game-data";
import type { PokemonData } from "#system/pokemon-data";
import type { SessionSaveMigrator } from "#types/SessionSaveMigrator";
import type { SessionSaveMigrator } from "#types/session-save-migrator";
/**
* Migrate all lingering rage fist data inside `CustomPokemonData`,

View File

@ -86,14 +86,14 @@ export class ArenaFlyout extends Phaser.GameObjects.Container {
private flyoutTextHeaderPlayer: Phaser.GameObjects.Text;
/** The {@linkcode Phaser.GameObjects.Text} header used to indicate the enemy's effects */
private flyoutTextHeaderEnemy: Phaser.GameObjects.Text;
/** The {@linkcode Phaser.GameObjects.Text} header used to indicate neutral effects */
/** The {@linkcode Phaser.GameObjects.Text} header used to indicate field effects */
private flyoutTextHeaderField: Phaser.GameObjects.Text;
/** The {@linkcode Phaser.GameObjects.Text} used to indicate the player's effects */
private flyoutTextPlayer: Phaser.GameObjects.Text;
/** The {@linkcode Phaser.GameObjects.Text} used to indicate the enemy's effects */
private flyoutTextEnemy: Phaser.GameObjects.Text;
/** The {@linkcode Phaser.GameObjects.Text} used to indicate neutral effects */
/** The {@linkcode Phaser.GameObjects.Text} used to indicate field effects */
private flyoutTextField: Phaser.GameObjects.Text;
/** Container for all field effects observed by this object */
@ -163,7 +163,7 @@ export class ArenaFlyout extends Phaser.GameObjects.Container {
this.flyoutTextHeaderField = addTextObject(
this.flyoutWidth / 2,
5,
i18next.t("arenaFlyout:neutral"),
i18next.t("arenaFlyout:field"),
TextStyle.SUMMARY_GREEN,
);
this.flyoutTextHeaderField.setFontSize(54);

View File

@ -4,7 +4,7 @@ import { getTypeDamageMultiplierColor } from "#data/type";
import { BattleType } from "#enums/battle-type";
import { Button } from "#enums/buttons";
import { Command } from "#enums/command";
import { MoveCategory } from "#enums/MoveCategory";
import { MoveCategory } from "#enums/move-category";
import { MoveUseMode } from "#enums/move-use-mode";
import { PokemonType } from "#enums/pokemon-type";
import { UiMode } from "#enums/ui-mode";

View File

@ -2,7 +2,6 @@ import { globalScene } from "#app/global-scene";
import { speciesStarterCosts } from "#balance/starters";
import { Button } from "#enums/buttons";
import { DexAttr } from "#enums/dex-attr";
import type { UiMode } from "#enums/ui-mode";
import { UiTheme } from "#enums/ui-theme";
import type { GameData } from "#system/game-data";
import { addTextObject, TextStyle } from "#ui/text";
@ -217,152 +216,207 @@ export class GameStatsUiHandler extends UiHandler {
private gameStatsContainer: Phaser.GameObjects.Container;
private statsContainer: Phaser.GameObjects.Container;
private statLabels: Phaser.GameObjects.Text[];
private statValues: Phaser.GameObjects.Text[];
/** The number of rows enabled per page. */
private static readonly ROWS_PER_PAGE = 9;
private statLabels: Phaser.GameObjects.Text[] = [];
private statValues: Phaser.GameObjects.Text[] = [];
private arrowUp: Phaser.GameObjects.Sprite;
private arrowDown: Phaser.GameObjects.Sprite;
constructor(mode: UiMode | null = null) {
super(mode);
this.statLabels = [];
this.statValues = [];
/** Whether the UI is single column mode */
private get singleCol(): boolean {
const resolvedLang = i18next.resolvedLanguage ?? "en";
// NOTE TO TRANSLATION TEAM: Add more languages that want to display
// in a single-column inside of the `[]` (e.g. `["ru", "fr"]`)
return ["fr", "es-ES", "es-MX", "it", "ja", "pt-BR", "ru"].includes(resolvedLang);
}
/** The number of columns used by this menu in the resolved language */
private get columnCount(): 1 | 2 {
return this.singleCol ? 1 : 2;
}
// #region Columnar-specific properties
/** The with of each column in the stats view */
private get colWidth(): number {
return (globalScene.scaledCanvas.width - 2) / this.columnCount;
}
/** THe width of a column's background window */
private get colBgWidth(): number {
return this.colWidth - 2;
}
/**
* Calculate the `x` position of the stat label based on its index.
*
* @remarks
* Should be used for stat labels (e.g. stat name, not its value). For stat value, use {@linkcode calcTextX}.
* @param index - The index of the stat label
* @returns The `x` position for the stat label
*/
private calcLabelX(index: number): number {
if (this.singleCol || !(index & 1)) {
return 8;
}
return 8 + (index & 1 ? this.colBgWidth : 0);
}
/**
* Calculate the `y` position of the stat label/text based on its index.
* @param index - The index of the stat label
* @returns The `y` position for the stat label
*/
private calcEntryY(index: number): number {
if (!this.singleCol) {
// Floor division by 2 as we want 1 to go to 0
index >>= 1;
}
return 28 + index * 16;
}
/**
* Calculate the `x` position of the stat value based on its index.
* @param index - The index of the stat value
* @returns The calculated `x` position
*/
private calcTextX(index: number): number {
if (this.singleCol || !(index & 1)) {
return this.colBgWidth - 8;
}
return this.colBgWidth * 2 - 8;
}
/** The number of stats on screen at one time (varies with column count) */
private get statsPerPage(): number {
return GameStatsUiHandler.ROWS_PER_PAGE * this.columnCount;
}
// #endregion Columnar-specific properties
setup() {
const ui = this.getUi();
this.gameStatsContainer = globalScene.add.container(1, -(globalScene.game.canvas.height / 6) + 1);
/** The scaled width of the global canvas */
const sWidth = globalScene.scaledCanvas.width;
/** The scaled height of the global canvas */
const sHeight = globalScene.scaledCanvas.height;
const gameStatsContainer = globalScene.add.container(1, -sHeight + 1);
this.gameStatsContainer = gameStatsContainer;
this.gameStatsContainer.setInteractive(
new Phaser.Geom.Rectangle(0, 0, globalScene.game.canvas.width / 6, globalScene.game.canvas.height / 6),
new Phaser.Geom.Rectangle(0, 0, sWidth, sHeight),
Phaser.Geom.Rectangle.Contains,
);
const headerBg = addWindow(0, 0, globalScene.game.canvas.width / 6 - 2, 24);
headerBg.setOrigin(0, 0);
const headerBg = addWindow(0, 0, sWidth - 2, 24).setOrigin(0);
const headerText = addTextObject(0, 0, i18next.t("gameStatsUiHandler:stats"), TextStyle.HEADER_LABEL);
headerText.setOrigin(0, 0);
headerText.setPositionRelative(headerBg, 8, 4);
const headerText = addTextObject(0, 0, i18next.t("gameStatsUiHandler:stats"), TextStyle.HEADER_LABEL)
.setOrigin(0)
.setPositionRelative(headerBg, 8, 4);
const statsBgWidth = (globalScene.game.canvas.width / 6 - 2) / 2;
const [statsBgLeft, statsBgRight] = new Array(2).fill(null).map((_, i) => {
const width = statsBgWidth + 2;
const height = Math.floor(globalScene.game.canvas.height / 6 - headerBg.height - 2);
const statsBg = addWindow(
(statsBgWidth - 2) * i,
headerBg.height,
width,
height,
false,
false,
i > 0 ? -3 : 0,
1,
);
statsBg.setOrigin(0, 0);
return statsBg;
});
this.gameStatsContainer.add([headerBg, headerText]);
this.statsContainer = globalScene.add.container(0, 0);
const colWidth = this.colWidth;
for (let i = 0; i < 18; i++) {
const statLabel = addTextObject(
8 + (i % 2 === 1 ? statsBgWidth : 0),
28 + Math.floor(i / 2) * 16,
"",
TextStyle.STATS_LABEL,
);
statLabel.setOrigin(0, 0);
this.statsContainer.add(statLabel);
this.statLabels.push(statLabel);
const statValue = addTextObject(statsBgWidth * ((i % 2) + 1) - 8, statLabel.y, "", TextStyle.STATS_VALUE);
statValue.setOrigin(1, 0);
this.statsContainer.add(statValue);
this.statValues.push(statValue);
{
const columnCount = this.columnCount;
const headerHeight = headerBg.height;
const statsBgHeight = Math.floor(globalScene.scaledCanvas.height - headerBg.height - 2);
const maskOffsetX = columnCount === 1 ? 0 : -3;
for (let i = 0; i < columnCount; i++) {
gameStatsContainer.add(
addWindow(i * this.colBgWidth, headerHeight, colWidth, statsBgHeight, false, false, maskOffsetX, 1, undefined) // formatting
.setOrigin(0),
);
}
}
this.gameStatsContainer.add(headerBg);
this.gameStatsContainer.add(headerText);
this.gameStatsContainer.add(statsBgLeft);
this.gameStatsContainer.add(statsBgRight);
const length = this.statsPerPage;
this.statLabels = Array.from({ length }, (_, i) =>
addTextObject(this.calcLabelX(i), this.calcEntryY(i), "", TextStyle.STATS_LABEL).setOrigin(0),
);
this.statValues = Array.from({ length }, (_, i) =>
addTextObject(this.calcTextX(i), this.calcEntryY(i), "", TextStyle.STATS_VALUE).setOrigin(1, 0),
);
this.statsContainer = globalScene.add.container(0, 0, [...this.statLabels, ...this.statValues]);
this.gameStatsContainer.add(this.statsContainer);
// arrows to show that we can scroll through the stats
const isLegacyTheme = globalScene.uiTheme === UiTheme.LEGACY;
this.arrowDown = globalScene.add.sprite(
statsBgWidth,
globalScene.game.canvas.height / 6 - (isLegacyTheme ? 9 : 5),
"prompt",
);
this.gameStatsContainer.add(this.arrowDown);
this.arrowUp = globalScene.add.sprite(statsBgWidth, headerBg.height + (isLegacyTheme ? 7 : 3), "prompt");
this.arrowUp.flipY = true;
this.gameStatsContainer.add(this.arrowUp);
const arrowX = this.singleCol ? colWidth / 2 : colWidth;
this.arrowDown = globalScene.add.sprite(arrowX, sHeight - (isLegacyTheme ? 9 : 5), "prompt");
this.arrowUp = globalScene.add
.sprite(arrowX, headerBg.height + (isLegacyTheme ? 7 : 3), "prompt") //
.setFlipY(true);
this.gameStatsContainer.add([this.arrowDown, this.arrowUp]);
ui.add(this.gameStatsContainer);
this.setCursor(0);
this.gameStatsContainer.setVisible(false);
}
show(args: any[]): boolean {
super.show(args);
this.gameStatsContainer.setActive(true).setVisible(true);
this.setCursor(0);
this.updateStats();
this.arrowUp.play("prompt");
this.arrowDown.play("prompt");
this.arrowUp.setActive(true).play("prompt").setVisible(false);
this.arrowDown.setActive(true).play("prompt");
/* `setCursor` handles updating stats if the position is different from before.
When opening this UI, we want to update stats regardless of the prior position. */
if (!this.setCursor(0)) {
this.updateStats();
}
if (globalScene.uiTheme === UiTheme.LEGACY) {
this.arrowUp.setTint(0x484848);
this.arrowDown.setTint(0x484848);
}
this.updateArrows();
this.gameStatsContainer.setVisible(true);
this.getUi().moveTo(this.gameStatsContainer, this.getUi().length - 1);
this.getUi().hideTooltip();
this.getUi()
.moveTo(this.gameStatsContainer, this.getUi().length - 1)
.hideTooltip();
return true;
}
updateStats(): void {
const statKeys = Object.keys(displayStats).slice(this.cursor * 2, this.cursor * 2 + 18);
/**
* Update the stat labels and values to reflect the current cursor position.
*
* @remarks
*
* Invokes each stat's {@linkcode DisplayStat.sourceFunc | sourceFunc} to obtain its value.
* Stat labels are shown as `???` if the stat is marked as hidden and its value is zero.
*/
private updateStats(): void {
const perPage = this.statsPerPage;
const columns = this.columnCount;
const statKeys = Object.keys(displayStats).slice(this.cursor * columns, this.cursor * columns + perPage);
statKeys.forEach((key, s) => {
const stat = displayStats[key] as DisplayStat;
const value = stat.sourceFunc!(globalScene.gameData); // TODO: is this bang correct?
const value = stat.sourceFunc?.(globalScene.gameData) ?? "-";
const valAsInt = Number.parseInt(value);
this.statLabels[s].setText(
!stat.hidden || Number.isNaN(Number.parseInt(value)) || Number.parseInt(value)
? i18next.t(`gameStatsUiHandler:${stat.label_key}`)
: "???",
!stat.hidden || Number.isNaN(value) || valAsInt ? i18next.t(`gameStatsUiHandler:${stat.label_key}`) : "???",
);
this.statValues[s].setText(value);
});
if (statKeys.length < 18) {
for (let s = statKeys.length; s < 18; s++) {
this.statLabels[s].setText("");
this.statValues[s].setText("");
}
for (let s = statKeys.length; s < perPage; s++) {
this.statLabels[s].setText("");
this.statValues[s].setText("");
}
}
/**
* Show arrows at the top / bottom of the page if it's possible to scroll in that direction
*/
updateArrows(): void {
const showUpArrow = this.cursor > 0;
this.arrowUp.setVisible(showUpArrow);
const showDownArrow = this.cursor < Math.ceil((Object.keys(displayStats).length - 18) / 2);
this.arrowDown.setVisible(showDownArrow);
/** The maximum cursor position */
private get maxCursorPos(): number {
return Math.ceil((Object.keys(displayStats).length - this.statsPerPage) / this.columnCount);
}
processInput(button: Button): boolean {
@ -370,45 +424,59 @@ export class GameStatsUiHandler extends UiHandler {
let success = false;
if (button === Button.CANCEL) {
success = true;
globalScene.ui.revertMode();
} else {
switch (button) {
case Button.UP:
if (this.cursor) {
success = this.setCursor(this.cursor - 1);
}
break;
case Button.DOWN:
if (this.cursor < Math.ceil((Object.keys(displayStats).length - 18) / 2)) {
success = this.setCursor(this.cursor + 1);
}
break;
}
/** The direction to move the cursor (up/down) */
let dir: 1 | -1 = 1;
switch (button) {
case Button.CANCEL:
success = true;
globalScene.ui.revertMode();
break;
// biome-ignore lint/suspicious/noFallthroughSwitchClause: intentional
case Button.UP:
dir = -1;
case Button.DOWN:
success = this.setCursor(this.cursor + dir);
}
if (success) {
ui.playSelect();
return true;
}
return success;
return false;
}
setCursor(cursor: number): boolean {
const ret = super.setCursor(cursor);
if (ret) {
this.updateStats();
this.updateArrows();
/**
* Set the cursor to the specified position, if able and update the stats display.
*
* @remarks
*
* If `newCursor` is not between `0` and {@linkcode maxCursorPos}, or if it is the same as {@linkcode newCursor}
* then no updates happen and `false` is returned.
*
* Otherwise, updates the up/down arrow visibility and calls {@linkcode updateStats}
*
* @param newCursor - The position to set the cursor to.
* @returns Whether the cursor successfully moved to a new position
*/
override setCursor(newCursor: number): boolean {
if (newCursor < 0 || newCursor > this.maxCursorPos || this.cursor === newCursor) {
return false;
}
return ret;
this.cursor = newCursor;
this.updateStats();
// NOTE: Do not toggle the arrows' "active" property here, as this would cause their animations to desync
this.arrowUp.setVisible(this.cursor > 0);
this.arrowDown.setVisible(this.cursor < this.maxCursorPos);
return true;
}
clear() {
super.clear();
this.gameStatsContainer.setVisible(false);
this.gameStatsContainer.setVisible(false).setActive(false);
}
}

View File

@ -273,12 +273,23 @@ export class ModifierSelectUiHandler extends AwaitableUiHandler {
// causing errors if reroll is selected
this.awaitingActionInput = false;
// TODO: Replace with `Promise.withResolvers` when possible.
let tweenResolve: () => void;
const tweenPromise = new Promise<void>(resolve => (tweenResolve = resolve));
const { promise: tweenPromise, resolve: tweenResolve } = Promise.withResolvers<void>();
let i = 0;
// TODO: Rework this bespoke logic for animating the modifier options.
// #region: animation
/** Holds promises that resolve once each reward's *upgrade animation* has finished playing */
const rewardAnimPromises: Promise<void>[] = [];
/** Holds promises that resolves once *all* animations for a reward have finished playing */
const rewardAnimAllSettledPromises: Promise<void>[] = [];
/*
* A counter here is used instead of a loop to "stagger" the apperance of each reward,
* using `sine.easeIn` to speed up the appearance of the rewards as each animation progresses.
*
* The `onComplete` callback for this tween is set to resolve once the upgrade animations
* for each reward has finished playing, allowing for the next set of animations to
* start to appear.
*/
globalScene.tweens.addCounter({
ease: "Sine.easeIn",
duration: 1250,
@ -288,30 +299,35 @@ export class ModifierSelectUiHandler extends AwaitableUiHandler {
const index = Math.floor(value * typeOptions.length);
if (index > i && index <= typeOptions.length) {
const option = this.options[i];
option?.show(
Math.floor((1 - value) * 1250) * 0.325 + 2000 * maxUpgradeCount,
-(maxUpgradeCount - typeOptions[i].upgradeCount),
);
if (option) {
rewardAnimPromises.push(
option.show(
Math.floor((1 - value) * 1250) * 0.325 + 2000 * maxUpgradeCount,
-(maxUpgradeCount - typeOptions[i].upgradeCount),
rewardAnimAllSettledPromises,
),
);
}
i++;
}
},
onComplete: () => {
tweenResolve();
Promise.allSettled(rewardAnimPromises).then(() => tweenResolve());
},
});
let shopResolve: () => void;
const shopPromise = new Promise<void>(resolve => (shopResolve = resolve));
tweenPromise.then(() => {
globalScene.time.delayedCall(1000, () => {
for (const shopOption of this.shopOptionsRows.flat()) {
shopOption.show(0, 0);
}
shopResolve();
});
/** Holds promises that resolve once each shop item has finished animating */
const shopAnimPromises: Promise<void>[] = [];
globalScene.time.delayedCall(1000 + maxUpgradeCount * 2000, () => {
for (const shopOption of this.shopOptionsRows.flat()) {
// It is safe to skip awaiting the `show` method here,
// as the promise it returns is also part of the promise appended to `shopAnimPromises`,
// which is awaited later on.
shopOption.show(0, 0, shopAnimPromises, false);
}
});
shopPromise.then(() => {
tweenPromise.then(() => {
globalScene.time.delayedCall(500, () => {
if (partyHasHeldItem) {
this.transferButtonContainer.setAlpha(0);
@ -344,31 +360,39 @@ export class ModifierSelectUiHandler extends AwaitableUiHandler {
duration: 250,
});
const updateCursorTarget = () => {
if (globalScene.shopCursorTarget === ShopCursorTarget.CHECK_TEAM) {
this.setRowCursor(0);
this.setCursor(2);
} else if (globalScene.shopCursorTarget === ShopCursorTarget.SHOP && globalScene.gameMode.hasNoShop) {
this.setRowCursor(ShopCursorTarget.REWARDS);
this.setCursor(0);
} else {
this.setRowCursor(globalScene.shopCursorTarget);
this.setCursor(0);
}
};
// Ensure that the reward animations have completed before allowing input to proceed.
// Required to ensure that the user cannot interact with the UI before the animations
// have completed, (which, among other things, would allow the GameObjects to be destroyed
// before the animations have completed, causing errors).
Promise.allSettled([...shopAnimPromises, ...rewardAnimAllSettledPromises]).then(() => {
const updateCursorTarget = () => {
if (globalScene.shopCursorTarget === ShopCursorTarget.CHECK_TEAM) {
this.setRowCursor(0);
this.setCursor(2);
} else if (globalScene.shopCursorTarget === ShopCursorTarget.SHOP && globalScene.gameMode.hasNoShop) {
this.setRowCursor(ShopCursorTarget.REWARDS);
this.setCursor(0);
} else {
this.setRowCursor(globalScene.shopCursorTarget);
this.setCursor(0);
}
};
updateCursorTarget();
updateCursorTarget();
handleTutorial(Tutorial.Select_Item).then(res => {
if (res) {
updateCursorTarget();
}
this.awaitingActionInput = true;
this.onActionInput = args[2];
handleTutorial(Tutorial.Select_Item).then(res => {
if (res) {
updateCursorTarget();
}
this.awaitingActionInput = true;
this.onActionInput = args[2];
});
});
});
});
// #endregion: animation
return true;
}
@ -820,14 +844,45 @@ class ModifierOption extends Phaser.GameObjects.Container {
}
}
show(remainingDuration: number, upgradeCountOffset: number) {
if (!this.modifierTypeOption.cost) {
/**
* Start the tweens responsible for animating the option's appearance
*
* @privateremarks
* This method is unusual. It "returns" (one via the actual return, one by via appending to the `promiseHolder`
* parameter) two promises. The promise returned by the method resolves once the option's appearance animations have
* completed, and is meant to allow callers to synchronize with the completion of the option's appearance animations.
* The promise appended to `promiseHolder` resolves once *all* animations started by this method have completed,
* and should be used by callers to ensure that all animations have completed before proceeding.
*
* @param remainingDuration - The duration in milliseconds that the animation can play for
* @param upgradeCountOffset - The offset to apply to the upgrade count for options whose rarity is being upgraded
* @param promiseHolder - A promise that resolves once all tweens started by this method have completed will be pushed to this array.
* @param isReward - Whether the option being shown is a reward, meaning it should show pokeball and upgrade animations.
* @returns A promise that resolves once the *option's apperance animations* have completed. This promise will resolve _before_ all
* promises that are initiated in this method complete. Instead, the `promiseHolder` array will contain a new promise
* that will resolve once all animations have completed.
*
*/
async show(
remainingDuration: number,
upgradeCountOffset: number,
promiseHolder: Promise<void>[],
isReward = true,
): Promise<void> {
/** Promises for the pokeball and upgrade animations */
const animPromises: Promise<void>[] = [];
if (isReward) {
const { promise: bouncePromise, resolve: resolveBounce } = Promise.withResolvers<void>();
globalScene.tweens.add({
targets: this.pb,
y: 0,
duration: 1250,
ease: "Bounce.Out",
onComplete: () => {
resolveBounce();
},
});
animPromises.push(bouncePromise);
let lastValue = 1;
let bounceCount = 0;
@ -857,7 +912,9 @@ class ModifierOption extends Phaser.GameObjects.Container {
// TODO: Figure out proper delay between chains and then convert this into a single tween chain
// rather than starting multiple tween chains.
for (let u = 0; u < this.modifierTypeOption.upgradeCount; u++) {
const { resolve, promise } = Promise.withResolvers<void>();
globalScene.tweens.chain({
tweens: [
{
@ -883,65 +940,99 @@ class ModifierOption extends Phaser.GameObjects.Container {
ease: "Sine.easeOut",
onComplete: () => {
this.pbTint.setVisible(false);
resolve();
},
},
],
});
animPromises.push(promise);
}
}
const finalPromises: Promise<void>[] = [];
globalScene.time.delayedCall(remainingDuration + 2000, () => {
if (!globalScene) {
return;
}
if (!this.modifierTypeOption.cost) {
if (isReward) {
this.pb.setTexture("pb", `${this.getPbAtlasKey(0)}_open`);
globalScene.playSound("se/pb_rel");
const { resolve: pbResolve, promise: pbPromise } = Promise.withResolvers<void>();
globalScene.tweens.add({
targets: this.pb,
duration: 500,
delay: 250,
ease: "Sine.easeIn",
alpha: 0,
onComplete: () => this.pb.destroy(),
onComplete: () => {
Promise.allSettled(animPromises).then(() => this.pb.destroy());
pbResolve();
},
});
finalPromises.push(pbPromise);
}
/** Delay for the rest of the tweens to ensure they show after the pokeball animation begins to appear */
const delay = isReward ? 250 : 0;
const { resolve: itemResolve, promise: itemPromise } = Promise.withResolvers<void>();
globalScene.tweens.add({
targets: this.itemContainer,
delay,
duration: 500,
ease: "Elastic.Out",
scale: 2,
alpha: 1,
onComplete: () => {
itemResolve();
},
});
if (!this.modifierTypeOption.cost) {
finalPromises.push(itemPromise);
if (isReward) {
const { resolve: itemTintResolve, promise: itemTintPromise } = Promise.withResolvers<void>();
globalScene.tweens.add({
targets: this.itemTint,
alpha: 0,
delay,
duration: 500,
ease: "Sine.easeIn",
onComplete: () => this.itemTint.destroy(),
onComplete: () => {
this.itemTint.destroy();
itemTintResolve();
},
});
finalPromises.push(itemTintPromise);
}
const { resolve: itemTextResolve, promise: itemTextPromise } = Promise.withResolvers<void>();
globalScene.tweens.add({
targets: this.itemText,
delay,
duration: 500,
alpha: 1,
y: 25,
ease: "Cubic.easeInOut",
onComplete: () => itemTextResolve(),
});
finalPromises.push(itemTextPromise);
if (this.itemCostText) {
const { resolve: itemCostResolve, promise: itemCostPromise } = Promise.withResolvers<void>();
globalScene.tweens.add({
targets: this.itemCostText,
delay,
duration: 500,
alpha: 1,
y: 35,
ease: "Cubic.easeInOut",
onComplete: () => itemCostResolve(),
});
finalPromises.push(itemCostPromise);
}
});
// The `.then` suppresses the return type for the Promise.allSettled so that it returns void.
promiseHolder.push(Promise.allSettled([...animPromises, ...finalPromises]).then());
await Promise.allSettled(animPromises);
}
getPbAtlasKey(tierOffset = 0) {

View File

@ -1,6 +1,6 @@
import type { InfoToggle } from "#app/battle-scene";
import { globalScene } from "#app/global-scene";
import { MoveCategory } from "#enums/MoveCategory";
import { MoveCategory } from "#enums/move-category";
import { PokemonType } from "#enums/pokemon-type";
import type { Move } from "#moves/move";
import { addTextObject, TextStyle } from "#ui/text";

View File

@ -1,7 +1,7 @@
import { globalScene } from "#app/global-scene";
import { Button } from "#enums/buttons";
import type { UiMode } from "#enums/ui-mode";
import { NavigationManager } from "#ui/navigationMenu";
import { NavigationManager } from "#ui/navigation-menu";
import { addTextObject, TextStyle } from "#ui/text";
import { UiHandler } from "#ui/ui-handler";
import { addWindow } from "#ui/ui-theme";

View File

@ -3,8 +3,8 @@ import type { InterfaceConfig } from "#app/inputs-controller";
import { Button } from "#enums/buttons";
import type { Device } from "#enums/devices";
import type { UiMode } from "#enums/ui-mode";
import { getIconWithSettingName } from "#inputs/configHandler";
import { NavigationManager, NavigationMenu } from "#ui/navigationMenu";
import { getIconWithSettingName } from "#inputs/config-handler";
import { NavigationManager, NavigationMenu } from "#ui/navigation-menu";
import { ScrollBar } from "#ui/scroll-bar";
import { addTextObject, TextStyle } from "#ui/text";
import { UiHandler } from "#ui/ui-handler";

View File

@ -5,7 +5,7 @@ import type { SettingType } from "#system/settings";
import { Setting, SettingKeys } from "#system/settings";
import type { InputsIcons } from "#ui/abstract-control-settings-ui-handler";
import { MessageUiHandler } from "#ui/message-ui-handler";
import { NavigationManager, NavigationMenu } from "#ui/navigationMenu";
import { NavigationManager, NavigationMenu } from "#ui/navigation-menu";
import { ScrollBar } from "#ui/scroll-bar";
import { addTextObject, TextStyle } from "#ui/text";
import { addWindow } from "#ui/ui-theme";

View File

@ -1,7 +1,7 @@
import { globalScene } from "#app/global-scene";
import { Device } from "#enums/devices";
import type { UiMode } from "#enums/ui-mode";
import { getIconWithSettingName, getKeyWithKeycode } from "#inputs/configHandler";
import { getIconWithSettingName, getKeyWithKeycode } from "#inputs/config-handler";
import { AbstractBindingUiHandler } from "#ui/abstract-binding-ui-handler";
import { addTextObject, TextStyle } from "#ui/text";
import i18next from "i18next";

View File

@ -1,7 +1,7 @@
import { globalScene } from "#app/global-scene";
import { Device } from "#enums/devices";
import type { UiMode } from "#enums/ui-mode";
import { getKeyWithKeycode } from "#inputs/configHandler";
import { getKeyWithKeycode } from "#inputs/config-handler";
import { AbstractBindingUiHandler } from "#ui/abstract-binding-ui-handler";
import { addTextObject, TextStyle } from "#ui/text";
import i18next from "i18next";

View File

@ -2,9 +2,9 @@ import { globalScene } from "#app/global-scene";
import type { InterfaceConfig } from "#app/inputs-controller";
import { Device } from "#enums/devices";
import type { UiMode } from "#enums/ui-mode";
import pad_dualshock from "#inputs/pad_dualshock";
import pad_unlicensedSNES from "#inputs/pad_unlicensedSNES";
import pad_xbox360 from "#inputs/pad_xbox360";
import pad_dualshock from "#inputs/pad-dualshock";
import pad_unlicensedSNES from "#inputs/pad-unlicensed-snes";
import pad_xbox360 from "#inputs/pad-xbox360";
import {
SettingGamepad,
setSettingGamepad,

View File

@ -2,8 +2,8 @@ import { globalScene } from "#app/global-scene";
import type { InterfaceConfig } from "#app/inputs-controller";
import { Device } from "#enums/devices";
import { UiMode } from "#enums/ui-mode";
import cfg_keyboard_qwerty from "#inputs/cfg_keyboard_qwerty";
import { deleteBind } from "#inputs/configHandler";
import cfg_keyboard_qwerty from "#inputs/cfg-keyboard-qwerty";
import { deleteBind } from "#inputs/config-handler";
import {
SettingKeyboard,
setSettingKeyboard,
@ -12,7 +12,7 @@ import {
settingKeyboardOptions,
} from "#system/settings-keyboard";
import { AbstractControlSettingsUiHandler } from "#ui/abstract-control-settings-ui-handler";
import { NavigationManager } from "#ui/navigationMenu";
import { NavigationManager } from "#ui/navigation-menu";
import { addTextObject, TextStyle } from "#ui/text";
import { reverseValueToKeySetting, truncateString } from "#utils/common";
import i18next from "i18next";

View File

@ -1,41 +0,0 @@
{
"textures": [
{
"image": "shiny_icons.png",
"format": "RGBA8888",
"size": {
"w": 45,
"h": 14
},
"scale": 1,
"frames": [
{
"filename": "0",
"rotated": false,
"trimmed": false,
"sourceSize": {
"w": 45,
"h": 14
},
"spriteSourceSize": {
"x": 0,
"y": 0,
"w": 15,
"h": 14
},
"frame": {
"x": 0,
"y": 0,
"w": 15,
"h": 14
}
}
]
}
],
"meta": {
"app": "https://www.codeandweb.com/texturepacker",
"version": "3.0",
"smartupdate": "$TexturePacker:SmartUpdate:a3275c7504a9b35b288265e191b1d14c:420725f3fb73c2cac0ab3bbc0a46f2e1:3a8b8ca0f0e4be067dd46c07b78ee013$"
}
}

View File

@ -10,7 +10,7 @@ import { getNatureName, getNatureStatMultiplier } from "#data/nature";
import { getPokeballAtlasKey } from "#data/pokeball";
import { getTypeRgb } from "#data/type";
import { Button } from "#enums/buttons";
import { MoveCategory } from "#enums/MoveCategory";
import { MoveCategory } from "#enums/move-category";
import { Nature } from "#enums/nature";
import { PlayerGender } from "#enums/player-gender";
import { PokemonType } from "#enums/pokemon-type";

View File

@ -29,7 +29,7 @@ import { MenuUiHandler } from "#ui/menu-ui-handler";
import { MessageUiHandler } from "#ui/message-ui-handler";
import { ModifierSelectUiHandler } from "#ui/modifier-select-ui-handler";
import { MysteryEncounterUiHandler } from "#ui/mystery-encounter-ui-handler";
import { NavigationManager } from "#ui/navigationMenu";
import { NavigationManager } from "#ui/navigation-menu";
import { OptionSelectUiHandler } from "#ui/option-select-ui-handler";
import { PartyUiHandler } from "#ui/party-ui-handler";
import { PokedexPageUiHandler } from "#ui/pokedex-page-ui-handler";

View File

@ -3,7 +3,7 @@ import { MoveId } from "#enums/move-id";
import { SpeciesId } from "#enums/species-id";
import { Stat } from "#enums/stat";
import { WeatherType } from "#enums/weather-type";
import { GameManager } from "#test/testUtils/gameManager";
import { GameManager } from "#test/test-utils/game-manager";
import Phaser from "phaser";
import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest";

View File

@ -2,7 +2,7 @@ import { AbilityId } from "#enums/ability-id";
import { MoveId } from "#enums/move-id";
import { SpeciesId } from "#enums/species-id";
import { Stat } from "#enums/stat";
import { GameManager } from "#test/testUtils/gameManager";
import { GameManager } from "#test/test-utils/game-manager";
import Phaser from "phaser";
import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest";

View File

@ -5,7 +5,7 @@ import { UiMode } from "#enums/ui-mode";
import { CommandPhase } from "#phases/command-phase";
import { TurnInitPhase } from "#phases/turn-init-phase";
import i18next from "#plugins/i18n";
import { GameManager } from "#test/testUtils/gameManager";
import { GameManager } from "#test/test-utils/game-manager";
import Phaser from "phaser";
import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest";

View File

@ -2,7 +2,7 @@ import { AbilityId } from "#enums/ability-id";
import { BattlerIndex } from "#enums/battler-index";
import { MoveId } from "#enums/move-id";
import { SpeciesId } from "#enums/species-id";
import { GameManager } from "#test/testUtils/gameManager";
import { GameManager } from "#test/test-utils/game-manager";
import { isBetween, toDmgValue } from "#utils/common";
import Phaser from "phaser";
import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest";

View File

@ -3,7 +3,7 @@ import { AbilityId } from "#enums/ability-id";
import { MoveId } from "#enums/move-id";
import { SpeciesId } from "#enums/species-id";
import { Stat } from "#enums/stat";
import { GameManager } from "#test/testUtils/gameManager";
import { GameManager } from "#test/test-utils/game-manager";
import Phaser from "phaser";
import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest";

View File

@ -2,7 +2,7 @@ import { allAbilities } from "#data/data-lists";
import { AbilityId } from "#enums/ability-id";
import { MoveId } from "#enums/move-id";
import { SpeciesId } from "#enums/species-id";
import { GameManager } from "#test/testUtils/gameManager";
import { GameManager } from "#test/test-utils/game-manager";
import Phaser from "phaser";
import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest";

View File

@ -5,7 +5,7 @@ import { BattlerTagType } from "#enums/battler-tag-type";
import { MoveId } from "#enums/move-id";
import { SpeciesId } from "#enums/species-id";
import type { PlayerPokemon } from "#field/pokemon";
import { GameManager } from "#test/testUtils/gameManager";
import { GameManager } from "#test/test-utils/game-manager";
import Phaser from "phaser";
import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest";

View File

@ -2,7 +2,7 @@ import { allMoves } from "#data/data-lists";
import { AbilityId } from "#enums/ability-id";
import { MoveId } from "#enums/move-id";
import { SpeciesId } from "#enums/species-id";
import { GameManager } from "#test/testUtils/gameManager";
import { GameManager } from "#test/test-utils/game-manager";
import Phaser from "phaser";
import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest";

View File

@ -4,7 +4,7 @@ import { MoveId } from "#enums/move-id";
import { SpeciesId } from "#enums/species-id";
import { MoveEffectPhase } from "#phases/move-effect-phase";
import { TurnEndPhase } from "#phases/turn-end-phase";
import { GameManager } from "#test/testUtils/gameManager";
import { GameManager } from "#test/test-utils/game-manager";
import Phaser from "phaser";
import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest";

Some files were not shown because too many files have changed in this diff Show More