Merge remote-tracking branch 'upstream/beta' into rest

This commit is contained in:
Bertie690 2025-07-25 19:23:59 -04:00
commit a1c718322f
526 changed files with 1041 additions and 837 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

@ -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

@ -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

@ -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

@ -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";

View File

@ -1,11 +1,11 @@
import { allMoves } from "#data/data-lists";
import { Status } from "#data/status-effect";
import { AbilityId } from "#enums/ability-id";
import { MultiHitType } from "#enums/MultiHitType";
import { MoveId } from "#enums/move-id";
import { MultiHitType } from "#enums/multi-hit-type";
import { SpeciesId } from "#enums/species-id";
import { StatusEffect } from "#enums/status-effect";
import { GameManager } from "#test/testUtils/gameManager";
import { GameManager } from "#test/test-utils/game-manager";
import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest";
describe("Abilities - BATTLE BOND", () => {

View File

@ -3,7 +3,7 @@ import { BattlerIndex } from "#enums/battler-index";
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

@ -9,7 +9,7 @@ import type { EffectiveStat } from "#enums/stat";
import { Stat } from "#enums/stat";
import { StatusEffect } from "#enums/status-effect";
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, vi } from "vitest";

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