Compare commits

...

18 Commits

Author SHA1 Message Date
Bertie690
919a009e22
Merge d685386e17 into da7903ab92 2025-08-15 10:55:49 -05:00
fabske0
da7903ab92
[i18n] rename cancel to cancelButton (#6267)
rename cancel to cancelButton
2025-08-15 11:34:54 -04:00
Bertie690
70e7f8b4d4
[Misc] Removed populateAnims script (#6229)
Removed `populateAnims`

Co-authored-by: Wlowscha <54003515+Wlowscha@users.noreply.github.com>
2025-08-15 17:11:37 +02:00
Bertie690
d685386e17
Update CONTRIBUTING.md
Co-authored-by: NightKev <34855794+DayKev@users.noreply.github.com>
2025-08-14 17:12:57 -04:00
Bertie690
963efe2f2d Added mention of alternate dev setup to CONTRIBUTING.md 2025-08-07 11:44:08 -04:00
Bertie690
b1434c1457 Removed non-extensions.json files; added default config directly to devcontainer 2025-08-07 11:21:38 -04:00
Bertie690
77f9a80cf8
Update .devcontainer/devcontainer.json
Co-authored-by: NightKev <34855794+DayKev@users.noreply.github.com>
2025-08-07 11:00:20 -04:00
Bertie690
890cd6bcc5
Update settings.json
Co-authored-by: NightKev <34855794+DayKev@users.noreply.github.com>
2025-08-07 10:35:31 -04:00
NightKev
0e4d924433 Re-add .vscode/ to Biome ignore list 2025-08-02 01:40:32 -07:00
Bertie690
53c88192f5
Update settings.json 2025-08-01 18:07:59 -04:00
Bertie690
c88af5d058
Update settings.json
Co-authored-by: NightKev <34855794+DayKev@users.noreply.github.com>
2025-08-01 18:05:53 -04:00
Bertie690
c9ea813b01
Update settings.json
Co-authored-by: NightKev <34855794+DayKev@users.noreply.github.com>
2025-08-01 18:05:43 -04:00
Bertie690
e59dc87bf1
Update extensions.json
Co-authored-by: NightKev <34855794+DayKev@users.noreply.github.com>
2025-08-01 18:02:45 -04:00
Bertie690
1b8c2cfd0b
Merge branch 'beta' into settings-json 2025-08-01 18:02:26 -04:00
Bertie690
b1468c17ef
Update settings.json 2025-08-01 13:28:09 -04:00
Bertie690
11ca012270
Removed "don't lint vscode json files" setting from biome.jsonc 2025-08-01 11:56:46 -04:00
Bertie690
cfef679967
Update settings.json 2025-08-01 11:55:01 -04:00
Bertie690
31efc1939b [Dev] Added devcontainer.json and VS code config files 2025-08-01 11:00:53 -04:00
9 changed files with 127 additions and 317 deletions

View File

@ -0,0 +1,61 @@
// For format details, see https://aka.ms/devcontainer.json. For config options, see the
// README at: https://github.com/devcontainers/templates/tree/main/src/typescript-node
{
"name": "Node.js & TypeScript",
// Or use a Dockerfile or Docker Compose file. More info: https://containers.dev/guide/dockerfile
"image": "mcr.microsoft.com/devcontainers/typescript-node:1-22-bookworm",
"features": {
"ghcr.io/devcontainers/features/github-cli:1": {
"installDirectlyFromGitHubRelease": true,
"version": "latest"
},
"ghcr.io/devcontainers-extra/features/pnpm:2": {
"version": "latest"
}
},
"customizations": {
"vscode": {
"settings": {
// # Formatter configs
"editor.defaultFormatter": "biomejs.biome",
"editor.tabSize": 2,
"editor.insertSpaces": true,
"editor.codeActionsOnSave": {
"source.addMissingImports.ts": "always",
"source.removeUnusedImports": "always",
"source.fixAll.biome": "always",
"source.organizeImports.biome": "always"
},
"biome.suggestInstallingGlobally": false,
// # JS/TS setting overrides
"javascript.preferences.importModuleSpecifier": "non-relative",
"javascript.preferences.importModuleSpecifierEnding": "index",
"javascript.preferGoToSourceDefinition": true,
"javascript.updateImportsOnFileMove.enabled": "always",
"typescript.preferences.importModuleSpecifier": "non-relative",
"typescript.preferences.importModuleSpecifierEnding": "index",
"typescript.preferGoToSourceDefinition": true,
"typescript.updateImportsOnFileMove.enabled": "always",
"typescript.tsserver.experimental.enableProjectDiagnostics": true,
// # Miscellaneous
"npm.packageManager": "pnpm",
"npm.scriptRunner": "pnpm",
"vitest.cliArguments": "--no-isolate"
},
"extensions": [
"biomejs.biome",
"YoavBls.pretty-ts-errors",
"vitest.explorer",
"adpyke.codesnap", // Bind to a hotkey (ctrl+\, etc) for best results
"aaron-bond.better-comments",
"MuTsunTsai.jsdoc-link"
]
}
},
"postCreateCommand": "pnpm install",
"forwardPorts": [8000]
}

5
.gitignore vendored
View File

@ -12,9 +12,10 @@ dist
dist-ssr dist-ssr
*.local *.local
# Editor directories and files # Editor directories and files (excluding `extensions.json` for devcontainer)
.vscode
*.code-workspace *.code-workspace
.vscode/*
!.vscode/extensions.json
.idea .idea
.DS_Store .DS_Store
*.suo *.suo

View File

@ -26,3 +26,4 @@ ignore:
- .git - .git
- public - public
- dist - dist
- .devcontainer

13
.vscode/extensions.json vendored Normal file
View File

@ -0,0 +1,13 @@
{
"recommendations": [
"biomejs.biome",
"YoavBls.pretty-ts-errors",
"vitest.explorer",
// This stuff isn't mandatory - it's just nice to have :)
"adpyke.codesnap", // Bind to a hotkey (ctrl+\, etc) for best results
"aaron-bond.better-comments",
"MuTsunTsai.jsdoc-link"
]
}

View File

@ -2,7 +2,7 @@
Thank you for taking the time to contribute, every little bit helps. This project is entirely open-source and unmonetized - community contributions are what keep it alive! Thank you for taking the time to contribute, every little bit helps. This project is entirely open-source and unmonetized - community contributions are what keep it alive!
Please make sure you understand everything relevant to your changes from the [Table of Contents](#-table-of-contents), and absolutely *feel free to reach out in the **#dev-corner** channel on [Discord](https://discord.gg/pokerogue)*. Please make sure you understand everything relevant to your changes from the [Table of Contents](#-table-of-contents), and absolutely *feel free to reach out in the **#dev-corner** channel on [Discord](https://discord.gg/pokerogue)*.
We are here to help and the better you understand what you're working on, the easier it will be for it to find its way into the game. We are here to help and the better you understand what you're working on, the easier it will be for it to find its way into the game.
## 📄 Table of Contents ## 📄 Table of Contents
@ -16,19 +16,36 @@ We are here to help and the better you understand what you're working on, the ea
## 🛠️ Development Basics ## 🛠️ Development Basics
PokéRogue is built with [Typescript](https://www.typescriptlang.org/docs/handbook/intro.html), using the [Phaser](https://github.com/phaserjs/phaser) game framework. PokéRogue is built with [Typescript](https://www.typescriptlang.org/docs/handbook/intro.html), using the [Phaser](https://github.com/phaserjs/phaser) game framework.
If you have the motivation and experience with Typescript/Javascript (or are willing to learn) you can contribute by forking the repository and making pull requests with contributions. If you have the motivation and experience with Typescript/Javascript (or are willing to learn), you can contribute by forking the repository and making pull requests with contributions.
## 💻 Environment Setup ## 💻 Environment Setup
### Prerequisites ### Codespaces/Devcontainer Environment
- node: >=22.14.0 - [manage with pnpm](https://pnpm.io/cli/env) | [manage with fnm](https://github.com/Schniz/fnm) | [manage with nvm](https://github.com/nvm-sh/nvm) Arguably the easiest way to get started is by using the prepared development environment.
We have a `.devcontainer/devcontainer.json` file, meaning we are compatible with:
- [![Open in GitHub Codespaces][codespaces-badge]][codespaces-link], or
- the [Visual Studio Code Remote - Containers][devcontainer-ext] extension.
This Linux environment comes with all required dependencies needed to start working on the project.
[codespaces-badge]: <https://github.com/codespaces/badge.svg>
[codespaces-link]: <https://github.com/codespaces/new?hide_repo_select=true&repo=620476224&ref=beta>
[devcontainer-ext]: <https://marketplace.visualstudio.com/items?itemName=ms-vscode-remote.remote-containers>
### Local Development
#### Prerequisites
- node: >=22.14.0 - [manage with pnpm](https://pnpm.io/cli/env) | [manage with fnm](https://github.com/Schniz/fnm) | [manage with nvm](https://github.com/nvm-sh/nvm) | [manage with volta.sh](https://volta.sh/)
- pnpm: 10.x - [how to install](https://pnpm.io/installation) (not recommended to install via `npm` on Windows native) | [alternate method - volta.sh](https://volta.sh/) - pnpm: 10.x - [how to install](https://pnpm.io/installation) (not recommended to install via `npm` on Windows native) | [alternate method - volta.sh](https://volta.sh/)
- The repository [forked](https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/working-with-forks/fork-a-repo) and [cloned](https://docs.github.com/en/repositories/creating-and-managing-repositories/cloning-a-repository) locally on your device - The repository [forked](https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/working-with-forks/fork-a-repo) and [cloned](https://docs.github.com/en/repositories/creating-and-managing-repositories/cloning-a-repository) locally on your device
### Running Locally #### Running Locally
1. Run `pnpm install` from the repository root 1. Run `pnpm install` from the repository root
- *if you run into any errors, reach out in the **#dev-corner** channel on Discord* - *if you run into any errors, reach out in the **#dev-corner** channel on Discord*
@ -36,7 +53,7 @@ If you have the motivation and experience with Typescript/Javascript (or are wil
## 🚀 Getting Started ## 🚀 Getting Started
A great way to develop an understanding of how the project works is to look at test cases (located in [the `test` folder](./test/)). A great way to develop an understanding of how the project works is to look at test cases (located in [the `test` folder](./test/)).
Tests show you both how things are supposed to work and the expected "flow" to get from point A to point B in battles. Tests show you both how things are supposed to work and the expected "flow" to get from point A to point B in battles.
*This is a big project and you will be confused at times - never be afraid to reach out and ask questions in **#dev-corner***! *This is a big project and you will be confused at times - never be afraid to reach out and ask questions in **#dev-corner***!
@ -50,7 +67,7 @@ Most issues are bugs and are labeled with their area, such as `Move`, `Ability`,
- `P2`: Minor - Incorrect (but non-crashing) move/ability/interaction - `P2`: Minor - Incorrect (but non-crashing) move/ability/interaction
- `P3`: No gameplay impact - typo, minor graphical error, etc. - `P3`: No gameplay impact - typo, minor graphical error, etc.
Also under issues, you can take a look at the [List of Partial / Unimplemented Moves and Abilities](https://github.com/pagefaultgames/pokerogue/issues/3503) and the [Bug Board](https://github.com/orgs/pagefaultgames/projects/3) (the latter is essentially the same as the issues page but easier to work with). Also under issues, you can take a look at the [List of Partial / Unimplemented Moves and Abilities](https://github.com/pagefaultgames/pokerogue/issues/3503) and the [Bug Board](https://github.com/orgs/pagefaultgames/projects/3). The latter is essentially the same as the issues page, so take your pick.
You are free to comment on any issue so that you may be assigned to it and we can avoid multiple people working on the same thing. You are free to comment on any issue so that you may be assigned to it and we can avoid multiple people working on the same thing.
@ -58,7 +75,7 @@ You are free to comment on any issue so that you may be assigned to it and we ca
You can find the auto-generated documentation [here](https://pagefaultgames.github.io/pokerogue/main/index.html). You can find the auto-generated documentation [here](https://pagefaultgames.github.io/pokerogue/main/index.html).
Additionally, the [docs folder](./docs) contains a variety of in-depth documents and guides useful for aspiring contributors. Additionally, the [docs folder](./docs) contains a variety of in-depth documents and guides useful for aspiring contributors. \
Notable topics include: Notable topics include:
- [Commenting your code](./docs/comments.md) - [Commenting your code](./docs/comments.md)
- [Linting & Formatting](./docs/linting.md) - [Linting & Formatting](./docs/linting.md)
@ -86,17 +103,17 @@ const overrides = {
``` ```
Read through `src/overrides.ts` file to find the override that fits your needs - there are a lot of them! Read through `src/overrides.ts` file to find the override that fits your needs - there are a lot of them!
If the situation you're trying to test can't be created using existing overrides (or with the [Dev Save](#-development-save-file)), reach out in **#dev-corner**. If the situation you're trying to test can't be created using existing overrides (or with the [Dev Save](#-development-save-file)), reach out in **#dev-corner**.
You can get help testing your specific changes, and you might have found a new override that needs to be created! You can get help testing your specific changes, and you might have found a new override that needs to be created!
### 2 - Automatic Testing ### 2 - Automatic Testing
> PokéRogue uses [Vitest](https://vitest.dev/) for automatic testing. Checking out the existing tests in the [test](./test/) folder is a great way to understand how this works, and to get familiar with the project as a whole. > PokéRogue uses [Vitest](https://vitest.dev/) for automatic testing. Checking out the existing tests in the [test](./test/) folder is a great way to understand how this works, and to get familiar with the project as a whole.
To make sure your changes didn't break any existing test cases, run `pnpm test:silent` in your terminal. You can also provide an argument to the command: to run only the Dancer (ability) tests, you could write `pnpm test:silent dancer`. To make sure your changes didn't break any existing test cases, run `pnpm test:silent` in your terminal. You can also provide an argument to the command: to run only the Dancer (ability) tests, you could write `pnpm test:silent dancer`.
- __Note that passing all test cases does *not* guarantee that everything is working properly__. The project does not have complete regression testing. - __Note that passing all test cases does *not* guarantee that everything is working properly__. The project does not have complete regression testing.
Most non-trivial changes (*especially bug fixes*) should come along with new test cases. Most non-trivial changes (*especially bug fixes*) should come along with new test cases.
- To make a new test file, run `pnpm test:create` and follow the prompts. If the move/ability/etc. you're modifying already has tests, simply add new cases to the end of the file. As mentioned before, the easiest way to get familiar with the system and understand how to write your own tests is simply to read the existing tests, particularly ones similar to the tests you intend to write. - To make a new test file, run `pnpm test:create` and follow the prompts. If the move/ability/etc. you're modifying already has tests, simply add new cases to the end of the file. As mentioned before, the easiest way to get familiar with the system and understand how to write your own tests is simply to read the existing tests, particularly ones similar to the tests you intend to write.
- Ensure that new tests: - Ensure that new tests:
- Are deterministic. In other words, the test should never pass or fail when it shouldn't due to randomness. This involves primarily ensuring that abilities and moves are never randomly selected. - Are deterministic. In other words, the test should never pass or fail when it shouldn't due to randomness. This involves primarily ensuring that abilities and moves are never randomly selected.
@ -107,4 +124,4 @@ Most non-trivial changes (*especially bug fixes*) should come along with new tes
> 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). > 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` 1. Start the game up locally and navigate to `Menu -> Manage Data -> Import Data`
2. Select [everything.prsv](test/test-utils/saves/everything.prsv) (`test/test-utils/saves/everything.prsv`) and confirm. 2. Select [everything.prsv](test/test-utils/saves/everything.prsv) (`test/test-utils/saves/everything.prsv`) and confirm.

View File

@ -36,7 +36,6 @@
"!**/src/data/balance/tms.ts" "!**/src/data/balance/tms.ts"
] ]
}, },
"assist": { "assist": {
"actions": { "actions": {
"source": { "source": {

View File

@ -27,13 +27,7 @@ import { UiInputs } from "#app/ui-inputs";
import { biomeDepths, getBiomeName } from "#balance/biomes"; import { biomeDepths, getBiomeName } from "#balance/biomes";
import { pokemonPrevolutions } from "#balance/pokemon-evolutions"; import { pokemonPrevolutions } from "#balance/pokemon-evolutions";
import { FRIENDSHIP_GAIN_FROM_BATTLE } from "#balance/starters"; import { FRIENDSHIP_GAIN_FROM_BATTLE } from "#balance/starters";
import { import { initCommonAnims, initMoveAnim, loadCommonAnimAssets, loadMoveAnimAssets } from "#data/battle-anims";
initCommonAnims,
initMoveAnim,
loadCommonAnimAssets,
loadMoveAnimAssets,
populateAnims,
} from "#data/battle-anims";
import { allAbilities, allMoves, allSpecies, modifierTypes } from "#data/data-lists"; import { allAbilities, allMoves, allSpecies, modifierTypes } from "#data/data-lists";
import { battleSpecDialogue } from "#data/dialogue"; import { battleSpecDialogue } from "#data/dialogue";
import type { SpeciesFormChangeTrigger } from "#data/form-change-triggers"; import type { SpeciesFormChangeTrigger } from "#data/form-change-triggers";
@ -388,7 +382,6 @@ export class BattleScene extends SceneBase {
const defaultMoves = [MoveId.TACKLE, MoveId.TAIL_WHIP, MoveId.FOCUS_ENERGY, MoveId.STRUGGLE]; const defaultMoves = [MoveId.TACKLE, MoveId.TAIL_WHIP, MoveId.FOCUS_ENERGY, MoveId.STRUGGLE];
await Promise.all([ await Promise.all([
populateAnims(),
this.initVariantData(), this.initVariantData(),
initCommonAnims().then(() => loadCommonAnimAssets(true)), initCommonAnims().then(() => loadCommonAnimAssets(true)),
Promise.all(defaultMoves.map(m => initMoveAnim(m))).then(() => loadMoveAnimAssets(defaultMoves, true)), Promise.all(defaultMoves.map(m => initMoveAnim(m))).then(() => loadMoveAnimAssets(defaultMoves, true)),

View File

@ -404,22 +404,18 @@ export const chargeAnims = new Map<ChargeAnim, AnimConfig | [AnimConfig, AnimCon
export const commonAnims = new Map<CommonAnim, AnimConfig>(); export const commonAnims = new Map<CommonAnim, AnimConfig>();
export const encounterAnims = new Map<EncounterAnim, AnimConfig>(); export const encounterAnims = new Map<EncounterAnim, AnimConfig>();
export function initCommonAnims(): Promise<void> { export async function initCommonAnims(): Promise<void> {
return new Promise(resolve => { const commonAnimFetches: Promise<Map<CommonAnim, AnimConfig>>[] = [];
const commonAnimNames = getEnumKeys(CommonAnim); for (const commonAnimName of getEnumKeys(CommonAnim)) {
const commonAnimIds = getEnumValues(CommonAnim); const commonAnimId = CommonAnim[commonAnimName];
const commonAnimFetches: Promise<Map<CommonAnim, AnimConfig>>[] = []; commonAnimFetches.push(
for (let ca = 0; ca < commonAnimIds.length; ca++) { globalScene
const commonAnimId = commonAnimIds[ca]; .cachedFetch(`./battle-anims/common-${toKebabCase(commonAnimName)}.json`)
commonAnimFetches.push( .then(response => response.json())
globalScene .then(cas => commonAnims.set(commonAnimId, new AnimConfig(cas))),
.cachedFetch(`./battle-anims/common-${toKebabCase(commonAnimNames[ca])}.json`) );
.then(response => response.json()) }
.then(cas => commonAnims.set(commonAnimId, new AnimConfig(cas))), await Promise.allSettled(commonAnimFetches);
);
}
Promise.allSettled(commonAnimFetches).then(() => resolve());
});
} }
export function initMoveAnim(move: MoveId): Promise<void> { export function initMoveAnim(move: MoveId): Promise<void> {
@ -1396,279 +1392,3 @@ export class EncounterBattleAnim extends BattleAnim {
return this.oppAnim; return this.oppAnim;
} }
} }
export async function populateAnims() {
const commonAnimNames = getEnumKeys(CommonAnim).map(k => k.toLowerCase());
const commonAnimMatchNames = commonAnimNames.map(k => k.replace(/_/g, ""));
const commonAnimIds = getEnumValues(CommonAnim);
const chargeAnimNames = getEnumKeys(ChargeAnim).map(k => k.toLowerCase());
const chargeAnimMatchNames = chargeAnimNames.map(k => k.replace(/_/g, " "));
const chargeAnimIds = getEnumValues(ChargeAnim);
const commonNamePattern = /name: (?:Common:)?(Opp )?(.*)/;
const moveNameToId = {};
// Exclude MoveId.NONE;
for (const move of getEnumValues(MoveId).slice(1)) {
// KARATE_CHOP => KARATECHOP
const moveName = MoveId[move].toUpperCase().replace(/_/g, "");
moveNameToId[moveName] = move;
}
const seNames: string[] = []; //(await fs.readdir('./public/audio/se/battle_anims/')).map(se => se.toString());
const animsData: any[] = []; //battleAnimRawData.split('!ruby/array:PBAnimation').slice(1); // TODO: add a proper type
for (let a = 0; a < animsData.length; a++) {
const fields = animsData[a].split("@").slice(1);
const nameField = fields.find(f => f.startsWith("name: "));
let isOppMove: boolean | undefined;
let commonAnimId: CommonAnim | undefined;
let chargeAnimId: ChargeAnim | undefined;
if (!nameField.startsWith("name: Move:") && !(isOppMove = nameField.startsWith("name: OppMove:"))) {
const nameMatch = commonNamePattern.exec(nameField)!; // TODO: is this bang correct?
const name = nameMatch[2].toLowerCase();
if (commonAnimMatchNames.indexOf(name) > -1) {
commonAnimId = commonAnimIds[commonAnimMatchNames.indexOf(name)];
} else if (chargeAnimMatchNames.indexOf(name) > -1) {
isOppMove = nameField.startsWith("name: Opp ");
chargeAnimId = chargeAnimIds[chargeAnimMatchNames.indexOf(name)];
}
}
const nameIndex = nameField.indexOf(":", 5) + 1;
const animName = nameField.slice(nameIndex, nameField.indexOf("\n", nameIndex));
if (!moveNameToId.hasOwnProperty(animName) && !commonAnimId && !chargeAnimId) {
continue;
}
const anim = commonAnimId || chargeAnimId ? new AnimConfig() : new AnimConfig();
if (anim instanceof AnimConfig) {
(anim as AnimConfig).id = moveNameToId[animName];
}
if (commonAnimId) {
commonAnims.set(commonAnimId, anim);
} else if (chargeAnimId) {
chargeAnims.set(chargeAnimId, !isOppMove ? anim : [chargeAnims.get(chargeAnimId) as AnimConfig, anim]);
} else {
moveAnims.set(
moveNameToId[animName],
!isOppMove ? (anim as AnimConfig) : [moveAnims.get(moveNameToId[animName]) as AnimConfig, anim as AnimConfig],
);
}
for (let f = 0; f < fields.length; f++) {
const field = fields[f];
const fieldName = field.slice(0, field.indexOf(":"));
const fieldData = field.slice(fieldName.length + 1, field.lastIndexOf("\n")).trim();
switch (fieldName) {
case "array": {
const framesData = fieldData.split(" - - - ").slice(1);
for (let fd = 0; fd < framesData.length; fd++) {
anim.frames.push([]);
const frameData = framesData[fd];
const focusFramesData = frameData.split(" - - ");
for (let tf = 0; tf < focusFramesData.length; tf++) {
const values = focusFramesData[tf].replace(/ {6}- /g, "").split("\n");
const targetFrame = new AnimFrame(
Number.parseFloat(values[0]),
Number.parseFloat(values[1]),
Number.parseFloat(values[2]),
Number.parseFloat(values[11]),
Number.parseFloat(values[3]),
Number.parseInt(values[4]) === 1,
Number.parseInt(values[6]) === 1,
Number.parseInt(values[5]),
Number.parseInt(values[7]),
Number.parseInt(values[8]),
Number.parseInt(values[12]),
Number.parseInt(values[13]),
Number.parseInt(values[14]),
Number.parseInt(values[15]),
Number.parseInt(values[16]),
Number.parseInt(values[17]),
Number.parseInt(values[18]),
Number.parseInt(values[19]),
Number.parseInt(values[21]),
Number.parseInt(values[22]),
Number.parseInt(values[23]),
Number.parseInt(values[24]),
Number.parseInt(values[20]) === 1,
Number.parseInt(values[25]),
Number.parseInt(values[26]) as AnimFocus,
);
anim.frames[fd].push(targetFrame);
}
}
break;
}
case "graphic": {
const graphic = fieldData !== "''" ? fieldData : "";
anim.graphic = graphic.indexOf(".") > -1 ? graphic.slice(0, fieldData.indexOf(".")) : graphic;
break;
}
case "timing": {
const timingEntries = fieldData.split("- !ruby/object:PBAnimTiming ").slice(1);
for (let t = 0; t < timingEntries.length; t++) {
const timingData = timingEntries[t]
.replace(/\n/g, " ")
.replace(/[ ]{2,}/g, " ")
.replace(/[a-z]+: ! '', /gi, "")
.replace(/name: (.*?),/, 'name: "$1",')
.replace(
/flashColor: !ruby\/object:Color { alpha: ([\d.]+), blue: ([\d.]+), green: ([\d.]+), red: ([\d.]+)}/,
"flashRed: $4, flashGreen: $3, flashBlue: $2, flashAlpha: $1",
);
const frameIndex = Number.parseInt(/frame: (\d+)/.exec(timingData)![1]); // TODO: is the bang correct?
let resourceName = /name: "(.*?)"/.exec(timingData)![1].replace("''", ""); // TODO: is the bang correct?
const timingType = Number.parseInt(/timingType: (\d)/.exec(timingData)![1]); // TODO: is the bang correct?
let timedEvent: AnimTimedEvent | undefined;
switch (timingType) {
case 0:
if (resourceName && resourceName.indexOf(".") === -1) {
let ext: string | undefined;
["wav", "mp3", "m4a"].every(e => {
if (seNames.indexOf(`${resourceName}.${e}`) > -1) {
ext = e;
return false;
}
return true;
});
if (!ext) {
ext = ".wav";
}
resourceName += `.${ext}`;
}
timedEvent = new AnimTimedSoundEvent(frameIndex, resourceName);
break;
case 1:
timedEvent = new AnimTimedAddBgEvent(frameIndex, resourceName.slice(0, resourceName.indexOf(".")));
break;
case 2:
timedEvent = new AnimTimedUpdateBgEvent(frameIndex, resourceName.slice(0, resourceName.indexOf(".")));
break;
}
if (!timedEvent) {
continue;
}
const propPattern = /([a-z]+): (.*?)(?:,|\})/gi;
let propMatch: RegExpExecArray;
while ((propMatch = propPattern.exec(timingData)!)) {
// TODO: is this bang correct?
const prop = propMatch[1];
let value: any = propMatch[2];
switch (prop) {
case "bgX":
case "bgY":
value = Number.parseFloat(value);
break;
case "volume":
case "pitch":
case "opacity":
case "colorRed":
case "colorGreen":
case "colorBlue":
case "colorAlpha":
case "duration":
case "flashScope":
case "flashRed":
case "flashGreen":
case "flashBlue":
case "flashAlpha":
case "flashDuration":
value = Number.parseInt(value);
break;
}
if (timedEvent.hasOwnProperty(prop)) {
timedEvent[prop] = value;
}
}
if (!anim.frameTimedEvents.has(frameIndex)) {
anim.frameTimedEvents.set(frameIndex, []);
}
anim.frameTimedEvents.get(frameIndex)!.push(timedEvent); // TODO: is this bang correct?
}
break;
}
case "position":
anim.position = Number.parseInt(fieldData);
break;
case "hue":
anim.hue = Number.parseInt(fieldData);
break;
}
}
}
// biome-ignore lint/correctness/noUnusedVariables: used in commented code
const animReplacer = (k, v) => {
if (k === "id" && !v) {
return undefined;
}
if (v instanceof Map) {
return Object.fromEntries(v);
}
if (v instanceof AnimTimedEvent) {
v["eventType"] = v.getEventType();
}
return v;
};
const animConfigProps = ["id", "graphic", "frames", "frameTimedEvents", "position", "hue"];
const animFrameProps = [
"x",
"y",
"zoomX",
"zoomY",
"angle",
"mirror",
"visible",
"blendType",
"target",
"graphicFrame",
"opacity",
"color",
"tone",
"flash",
"locked",
"priority",
"focus",
];
const propSets = [animConfigProps, animFrameProps];
// biome-ignore lint/correctness/noUnusedVariables: used in commented code
const animComparator = (a: Element, b: Element) => {
let props: string[];
for (let p = 0; p < propSets.length; p++) {
props = propSets[p];
// @ts-expect-error TODO
const ai = props.indexOf(a.key);
if (ai === -1) {
continue;
}
// @ts-expect-error TODO
const bi = props.indexOf(b.key);
return ai < bi ? -1 : ai > bi ? 1 : 0;
}
return 0;
};
/*for (let ma of moveAnims.keys()) {
const data = moveAnims.get(ma);
(async () => {
await fs.writeFile(`../public/battle-anims/${Moves[ma].toLowerCase().replace(/\_/g, '-')}.json`, stringify(data, { replacer: animReplacer, cmp: animComparator, space: ' ' }));
})();
}
for (let ca of chargeAnims.keys()) {
const data = chargeAnims.get(ca);
(async () => {
await fs.writeFile(`../public/battle-anims/${chargeAnimNames[chargeAnimIds.indexOf(ca)].replace(/\_/g, '-')}.json`, stringify(data, { replacer: animReplacer, cmp: animComparator, space: ' ' }));
})();
}
for (let cma of commonAnims.keys()) {
const data = commonAnims.get(cma);
(async () => {
await fs.writeFile(`../public/battle-anims/common-${commonAnimNames[commonAnimIds.indexOf(cma)].replace(/\_/g, '-')}.json`, stringify(data, { replacer: animReplacer, cmp: animComparator, space: ' ' }));
})();
}*/
}

View File

@ -2142,7 +2142,12 @@ class PartyCancelButton extends Phaser.GameObjects.Container {
this.partyCancelPb = partyCancelPb; this.partyCancelPb = partyCancelPb;
const partyCancelText = addTextObject(-10, -7, i18next.t("partyUiHandler:cancel"), TextStyle.PARTY_CANCEL_BUTTON); const partyCancelText = addTextObject(
-10,
-7,
i18next.t("partyUiHandler:cancelButton"),
TextStyle.PARTY_CANCEL_BUTTON,
);
this.add(partyCancelText); this.add(partyCancelText);
} }