Merge branch 'main' into misc3

This commit is contained in:
Temps Ray 2024-04-24 23:07:54 -04:00
commit f73dee9735
17 changed files with 4360 additions and 395 deletions

View File

@ -21,6 +21,7 @@ import { ModifierPoolType, getDefaultModifierTypeForTier, getEnemyModifierTypesF
import AbilityBar from './ui/ability-bar'; import AbilityBar from './ui/ability-bar';
import { BlockItemTheftAbAttr, DoubleBattleChanceAbAttr, IncrementMovePriorityAbAttr, applyAbAttrs, initAbilities } from './data/ability'; import { BlockItemTheftAbAttr, DoubleBattleChanceAbAttr, IncrementMovePriorityAbAttr, applyAbAttrs, initAbilities } from './data/ability';
import { Abilities } from "./data/enums/abilities"; import { Abilities } from "./data/enums/abilities";
import { allAbilities } from "./data/ability";
import Battle, { BattleType, FixedBattleConfig, fixedBattles } from './battle'; import Battle, { BattleType, FixedBattleConfig, fixedBattles } from './battle';
import { GameMode, GameModes, gameModes } from './game-mode'; import { GameMode, GameModes, gameModes } from './game-mode';
import FieldSpritePipeline from './pipelines/field-sprite'; import FieldSpritePipeline from './pipelines/field-sprite';
@ -808,6 +809,7 @@ export default class BattleScene extends SceneBase {
const localizable: Localizable[] = [ const localizable: Localizable[] = [
...allSpecies, ...allSpecies,
...allMoves, ...allMoves,
...allAbilities,
...Utils.getEnumValues(ModifierPoolType).map(mpt => getModifierPoolForType(mpt)).map(mp => Object.values(mp).flat().map(mt => mt.modifierType).filter(mt => 'localize' in mt).map(lpb => lpb as unknown as Localizable)).flat() ...Utils.getEnumValues(ModifierPoolType).map(mpt => getModifierPoolForType(mpt)).map(mp => Object.values(mp).flat().map(mt => mt.modifierType).filter(mt => 'localize' in mt).map(lpb => lpb as unknown as Localizable)).flat()
]; ];
for (let item of localizable) for (let item of localizable)

File diff suppressed because it is too large Load Diff

View File

@ -1,3 +1,5 @@
import i18next from '../plugins/i18n';
export enum Stat { export enum Stat {
HP = 0, HP = 0,
ATK, ATK,
@ -11,22 +13,22 @@ export function getStatName(stat: Stat, shorten: boolean = false) {
let ret: string; let ret: string;
switch (stat) { switch (stat) {
case Stat.HP: case Stat.HP:
ret = !shorten ? 'Max. HP' : 'MaxHP'; ret = !shorten ? i18next.t('pokemonStat:HP') : i18next.t('pokemonStat:HPshortened');
break; break;
case Stat.ATK: case Stat.ATK:
ret = !shorten ? 'Attack' : 'Atk'; ret = !shorten ? i18next.t('pokemonStat:ATK') : i18next.t('pokemonStat:ATKshortened');
break; break;
case Stat.DEF: case Stat.DEF:
ret = !shorten ? 'Defense' : 'Def'; ret = !shorten ? i18next.t('pokemonStat:DEF') : i18next.t('pokemonStat:DEFshortened');
break; break;
case Stat.SPATK: case Stat.SPATK:
ret = !shorten ? 'Sp. Atk' : 'SpAtk'; ret = !shorten ? i18next.t('pokemonStat:SPATK') : i18next.t('pokemonStat:SPATKshortened');
break; break;
case Stat.SPDEF: case Stat.SPDEF:
ret = !shorten ? 'Sp. Def' : 'SpDef'; ret = !shorten ? i18next.t('pokemonStat:SPDEF') : i18next.t('pokemonStat:SPDEFshortened');
break; break;
case Stat.SPD: case Stat.SPD:
ret = !shorten ? 'Speed' : 'Spd'; ret = !shorten ? i18next.t('pokemonStat:SPD') : i18next.t('pokemonStat:SPDshortened');
break; break;
} }
return ret; return ret;

1244
src/locales/en/ability.ts Normal file

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,16 @@
import { SimpleTranslationEntries } from "#app/plugins/i18n";
export const pokemonStat: SimpleTranslationEntries = {
"HP": "Max. HP",
"HPshortened": "MaxHP",
"ATK": "Attack",
"ATKshortened": "Atk",
"DEF": "Defense",
"DEFshortened": "Def",
"SPATK": "Sp. Atk",
"SPATKshortened": "SpAtk",
"SPDEF": "Sp. Def",
"SPDEFshortened": "SpDef",
"SPD": "Speed",
"SPDshortened": "Spd"
} as const;

1244
src/locales/es/ability.ts Normal file

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,16 @@
import { SimpleTranslationEntries } from "#app/plugins/i18n";
export const pokemonStat: SimpleTranslationEntries = {
"HP": "Max. HP",
"HPshortened": "MaxHP",
"ATK": "Attack",
"ATKshortened": "Atk",
"DEF": "Defense",
"DEFshortened": "Def",
"SPATK": "Sp. Atk",
"SPATKshortened": "SpAtk",
"SPDEF": "Sp. Def",
"SPDEFshortened": "SpDef",
"SPD": "Speed",
"SPDshortened": "Spd"
} as const;

1244
src/locales/fr/ability.ts Normal file

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,16 @@
import { SimpleTranslationEntries } from "#app/plugins/i18n";
export const pokemonStat: SimpleTranslationEntries = {
"HP": "PV",
"HPshortened": "PV",
"ATK": "Attaque",
"ATKshortened": "Atq",
"DEF": "Défense",
"DEFshortened": "Déf",
"SPATK": "Atq. Spé",
"SPATKshortened": "AtqSp",
"SPDEF": "Déf. Spé",
"SPDEFshortened": "DéfSp",
"SPD": "Vitesse",
"SPDshortened": "Vit"
} as const;

View File

@ -0,0 +1,16 @@
import { SimpleTranslationEntries } from "#app/plugins/i18n";
export const pokemonStat: SimpleTranslationEntries = {
"HP": "Max. HP",
"HPshortened": "MaxHP",
"ATK": "Attack",
"ATKshortened": "Atk",
"DEF": "Defense",
"DEFshortened": "Def",
"SPATK": "Sp. Atk",
"SPATKshortened": "SpAtk",
"SPDEF": "Sp. Def",
"SPDEFshortened": "SpDef",
"SPD": "Speed",
"SPDshortened": "Spd"
} as const;

View File

@ -8,6 +8,10 @@ import { move as enMove } from '../locales/en/move';
import { move as esMove } from '../locales/es/move'; import { move as esMove } from '../locales/es/move';
import { move as frMove } from '../locales/fr/move'; import { move as frMove } from '../locales/fr/move';
import { ability as enAbility } from '../locales/en/ability';
import { ability as esAbility } from '../locales/es/ability';
import { ability as frAbility } from '../locales/fr/ability';
import { pokeball as enPokeball } from '../locales/en/pokeball'; import { pokeball as enPokeball } from '../locales/en/pokeball';
import { pokeball as esPokeball } from '../locales/es/pokeball'; import { pokeball as esPokeball } from '../locales/es/pokeball';
import { pokeball as frPokeball } from '../locales/fr/pokeball'; import { pokeball as frPokeball } from '../locales/fr/pokeball';
@ -16,6 +20,11 @@ import { pokemon as enPokemon } from '../locales/en/pokemon';
import { pokemon as esPokemon } from '../locales/es/pokemon'; import { pokemon as esPokemon } from '../locales/es/pokemon';
import { pokemon as frPokemon } from '../locales/fr/pokemon'; import { pokemon as frPokemon } from '../locales/fr/pokemon';
import { pokemonStat as enPokemonStat } from '../locales/en/pokemon-stat';
import { pokemonStat as esPokemonStat } from '../locales/es/pokemon-stat';
import { pokemonStat as frPokemonStat } from '../locales/fr/pokemon-stat';
import { pokemonStat as itPokemonStat } from '../locales/it/pokemon-stat';
import { commandUiHandler as enCommandUiHandler } from '../locales/en/command-ui-handler'; import { commandUiHandler as enCommandUiHandler } from '../locales/en/command-ui-handler';
import { commandUiHandler as esCommandUiHandler } from '../locales/es/command-ui-handler'; import { commandUiHandler as esCommandUiHandler } from '../locales/es/command-ui-handler';
import { commandUiHandler as frCommandUiHandler } from '../locales/fr/command-ui-handler'; import { commandUiHandler as frCommandUiHandler } from '../locales/fr/command-ui-handler';
@ -34,6 +43,15 @@ export interface MoveTranslationEntries {
[key: string]: MoveTranslationEntry [key: string]: MoveTranslationEntry
} }
export interface AbilityTranslationEntry {
name: string,
description: string
}
export interface AbilityTranslationEntries {
[key: string]: AbilityTranslationEntry
}
export interface Localizable { export interface Localizable {
localize(): void; localize(): void;
} }
@ -69,26 +87,33 @@ export function initI18n(): void {
en: { en: {
menu: enMenu, menu: enMenu,
move: enMove, move: enMove,
ability: enAbility,
pokeball: enPokeball, pokeball: enPokeball,
pokemon: enPokemon, pokemon: enPokemon,
pokemonStat: enPokemonStat,
commandUiHandler: enCommandUiHandler, commandUiHandler: enCommandUiHandler,
}, },
es: { es: {
menu: esMenu, menu: esMenu,
move: esMove, move: esMove,
ability: esAbility,
pokeball: esPokeball, pokeball: esPokeball,
pokemon: esPokemon, pokemon: esPokemon,
pokemonStat: esPokemonStat,
commandUiHandler: esCommandUiHandler, commandUiHandler: esCommandUiHandler,
}, },
fr: { fr: {
menu: frMenu, menu: frMenu,
move: frMove, move: frMove,
ability: frAbility,
pokeball: frPokeball, pokeball: frPokeball,
pokemon: frPokemon, pokemon: frPokemon,
pokemonStat: frPokemonStat,
commandUiHandler: frCommandUiHandler, commandUiHandler: frCommandUiHandler,
}, },
it: { it: {
menu: itMenu, menu: itMenu,
pokemonStat: itPokemonStat,
}, },
}, },
}); });
@ -100,8 +125,10 @@ declare module 'i18next' {
resources: { resources: {
menu: typeof enMenu; menu: typeof enMenu;
move: typeof enMove; move: typeof enMove;
ability: typeof enAbility;
pokeball: typeof enPokeball; pokeball: typeof enPokeball;
pokemon: typeof enPokemon; pokemon: typeof enPokemon;
pokemonStat: typeof enPokemonStat;
commandUiHandler: typeof enCommandUiHandler; commandUiHandler: typeof enCommandUiHandler;
}; };
} }

View File

@ -272,7 +272,7 @@ export class GameData {
const systemData = JSON.stringify(data, (k: any, v: any) => typeof v === 'bigint' ? v <= maxIntAttrValue ? Number(v) : v.toString() : v); const systemData = JSON.stringify(data, (k: any, v: any) => typeof v === 'bigint' ? v <= maxIntAttrValue ? Number(v) : v.toString() : v);
if (!bypassLogin) { if (!bypassLogin) {
Utils.apiPost(`savedata/update?datatype=${GameDataType.SYSTEM}`, systemData) Utils.apiPost(`savedata/update?datatype=${GameDataType.SYSTEM}`, systemData, undefined, true)
.then(response => response.text()) .then(response => response.text())
.then(error => { .then(error => {
this.scene.ui.savingIcon.hide(); this.scene.ui.savingIcon.hide();
@ -551,7 +551,7 @@ export class GameData {
const sessionData = this.getSessionSaveData(scene); const sessionData = this.getSessionSaveData(scene);
if (!bypassLogin) { if (!bypassLogin) {
Utils.apiPost(`savedata/update?datatype=${GameDataType.SESSION}&slot=${scene.sessionSlotId}`, JSON.stringify(sessionData)) Utils.apiPost(`savedata/update?datatype=${GameDataType.SESSION}&slot=${scene.sessionSlotId}`, JSON.stringify(sessionData), undefined, true)
.then(response => response.text()) .then(response => response.text())
.then(error => { .then(error => {
if (error) { if (error) {
@ -752,7 +752,7 @@ export class GameData {
if (success !== null && !success) if (success !== null && !success)
return resolve([false, false]); return resolve([false, false]);
const sessionData = this.getSessionSaveData(scene); const sessionData = this.getSessionSaveData(scene);
Utils.apiPost(`savedata/clear?slot=${slotId}`, JSON.stringify(sessionData)).then(response => { Utils.apiPost(`savedata/clear?slot=${slotId}`, JSON.stringify(sessionData), undefined, true).then(response => {
if (response.ok) if (response.ok)
loggedInUser.lastSessionSlot = -1; loggedInUser.lastSessionSlot = -1;
return response.json(); return response.json();
@ -912,7 +912,7 @@ export class GameData {
updateUserInfo().then(success => { updateUserInfo().then(success => {
if (!success) if (!success)
return displayError(`Could not contact the server. Your ${dataName} data could not be imported.`); return displayError(`Could not contact the server. Your ${dataName} data could not be imported.`);
Utils.apiPost(`savedata/update?datatype=${dataType}${dataType === GameDataType.SESSION ? `&slot=${slotId}` : ''}`, dataStr) Utils.apiPost(`savedata/update?datatype=${dataType}${dataType === GameDataType.SESSION ? `&slot=${slotId}` : ''}`, dataStr, undefined, true)
.then(response => response.text()) .then(response => response.text())
.then(error => { .then(error => {
if (error) { if (error) {

View File

@ -6,12 +6,14 @@ import { Mode } from "./ui";
import UiHandler from "./ui-handler"; import UiHandler from "./ui-handler";
import * as Utils from "../utils"; import * as Utils from "../utils";
import { CommandPhase } from "../phases"; import { CommandPhase } from "../phases";
import { MoveCategory } from "#app/data/move.js";
export default class FightUiHandler extends UiHandler { export default class FightUiHandler extends UiHandler {
private movesContainer: Phaser.GameObjects.Container; private movesContainer: Phaser.GameObjects.Container;
private typeIcon: Phaser.GameObjects.Sprite; private typeIcon: Phaser.GameObjects.Sprite;
private ppText: Phaser.GameObjects.Text; private ppText: Phaser.GameObjects.Text;
private cursorObj: Phaser.GameObjects.Image; private cursorObj: Phaser.GameObjects.Image;
private moveCategoryIcon: Phaser.GameObjects.Sprite;
protected fieldIndex: integer = 0; protected fieldIndex: integer = 0;
protected cursor2: integer = 0; protected cursor2: integer = 0;
@ -26,10 +28,15 @@ export default class FightUiHandler extends UiHandler {
this.movesContainer = this.scene.add.container(18, -38.7); this.movesContainer = this.scene.add.container(18, -38.7);
ui.add(this.movesContainer); ui.add(this.movesContainer);
this.typeIcon = this.scene.add.sprite((this.scene.game.canvas.width / 6) - 33, -31, 'types', 'unknown'); this.typeIcon = this.scene.add.sprite((this.scene.game.canvas.width / 6) - 41, -31, 'types', 'unknown');
this.typeIcon.setVisible(false); this.typeIcon.setVisible(false);
ui.add(this.typeIcon); ui.add(this.typeIcon);
this.moveCategoryIcon = this.scene.add.sprite((this.scene.game.canvas.width / 6) - 19, -31, 'categories', 'physical');
this.moveCategoryIcon.setVisible(false);
ui.add(this.moveCategoryIcon);
this.ppText = addTextObject(this.scene, (this.scene.game.canvas.width / 6) - 18, -15.5, ' / ', TextStyle.WINDOW); this.ppText = addTextObject(this.scene, (this.scene.game.canvas.width / 6) - 18, -15.5, ' / ', TextStyle.WINDOW);
this.ppText.setOrigin(1, 0.5); this.ppText.setOrigin(1, 0.5);
this.ppText.setVisible(false); this.ppText.setVisible(false);
@ -120,16 +127,19 @@ export default class FightUiHandler extends UiHandler {
if (hasMove) { if (hasMove) {
const pokemonMove = moveset[cursor]; const pokemonMove = moveset[cursor];
this.typeIcon.setTexture('types', Type[pokemonMove.getMove().type].toLowerCase()); this.typeIcon.setTexture('types', Type[pokemonMove.getMove().type].toLowerCase()).setScale(0.65);
this.moveCategoryIcon.setTexture('categories', MoveCategory[pokemonMove.getMove().category].toLowerCase()).setScale(0.8);
const maxPP = pokemonMove.getMovePp(); const maxPP = pokemonMove.getMovePp();
const pp = maxPP - pokemonMove.ppUsed; const pp = maxPP - pokemonMove.ppUsed;
this.ppText.setText(`${Utils.padInt(pp, 2, ' ')}/${Utils.padInt(maxPP, 2, ' ')}`); this.ppText.setText(`${Utils.padInt(pp, 2, ' ')}/${Utils.padInt(maxPP, 2, ' ')}`);
} }
this.typeIcon.setVisible(hasMove); this.typeIcon.setVisible(hasMove);
this.ppText.setVisible(hasMove); this.ppText.setVisible(hasMove);
this.moveCategoryIcon.setVisible(hasMove);
this.cursorObj.setPosition(13 + (cursor % 2 === 1 ? 100 : 0), -31 + (cursor >= 2 ? 15 : 0)); this.cursorObj.setPosition(13 + (cursor % 2 === 1 ? 100 : 0), -31 + (cursor >= 2 ? 15 : 0));
@ -151,6 +161,7 @@ export default class FightUiHandler extends UiHandler {
this.clearMoves(); this.clearMoves();
this.typeIcon.setVisible(false); this.typeIcon.setVisible(false);
this.ppText.setVisible(false); this.ppText.setVisible(false);
this.moveCategoryIcon.setVisible(false);
this.eraseCursor(); this.eraseCursor();
} }

View File

@ -59,11 +59,7 @@ export default class LoginFormUiHandler extends FormModalUiHandler {
}; };
if (!this.inputs[0].text) if (!this.inputs[0].text)
return onFail(i18next.t('menu:emptyUsername')); return onFail(i18next.t('menu:emptyUsername'));
const contentType = 'application/x-www-form-urlencoded'; Utils.apiPost(`account/login`, `username=${encodeURIComponent(this.inputs[0].text)}&password=${encodeURIComponent(this.inputs[1].text)}`, 'application/x-www-form-urlencoded')
const headers = {
'Content-Type': contentType,
};
fetch(`${Utils.apiUrl}/account/login`, { method: 'POST', headers: headers, body: `username=${encodeURIComponent(this.inputs[0].text)}&password=${encodeURIComponent(this.inputs[1].text)}` })
.then(response => { .then(response => {
if (!response.ok) if (!response.ok)
return response.text(); return response.text();

View File

@ -307,7 +307,7 @@ export default class MenuUiHandler extends MessageUiHandler {
case MenuOptions.LOG_OUT: case MenuOptions.LOG_OUT:
success = true; success = true;
const doLogout = () => { const doLogout = () => {
Utils.apiPost('account/logout').then(res => { Utils.apiPost('account/logout', undefined, undefined, true).then(res => {
if (!res.ok) if (!res.ok)
console.error(`Log out failed (${res.status}: ${res.statusText})`); console.error(`Log out failed (${res.status}: ${res.statusText})`);
Utils.setCookie(Utils.sessionIdKey, ''); Utils.setCookie(Utils.sessionIdKey, '');

View File

@ -74,15 +74,11 @@ export default class RegistrationFormUiHandler extends FormModalUiHandler {
return onFail(this.getReadableErrorMessage('invalid password')); return onFail(this.getReadableErrorMessage('invalid password'));
if (this.inputs[1].text !== this.inputs[2].text) if (this.inputs[1].text !== this.inputs[2].text)
return onFail(i18next.t('menu:passwordNotMatchingConfirmPassword')); return onFail(i18next.t('menu:passwordNotMatchingConfirmPassword'));
const contentType = 'application/x-www-form-urlencoded'; Utils.apiPost(`account/register`, `username=${encodeURIComponent(this.inputs[0].text)}&password=${encodeURIComponent(this.inputs[1].text)}`, 'application/x-www-form-urlencoded')
const headers = {
'Content-Type': contentType,
};
fetch(`${Utils.apiUrl}/account/register`, { method: 'POST', headers: headers, body: `username=${encodeURIComponent(this.inputs[0].text)}&password=${encodeURIComponent(this.inputs[1].text)}` })
.then(response => response.text()) .then(response => response.text())
.then(response => { .then(response => {
if (!response) { if (!response) {
fetch(`${Utils.apiUrl}/account/login`, { method: 'POST', headers: headers, body: `username=${encodeURIComponent(this.inputs[0].text)}&password=${encodeURIComponent(this.inputs[1].text)}` }) Utils.apiPost(`account/login`, `username=${encodeURIComponent(this.inputs[0].text)}&password=${encodeURIComponent(this.inputs[1].text)}`, 'application/x-www-form-urlencoded')
.then(response => { .then(response => {
if (!response.ok) if (!response.ok)
return response.text(); return response.text();

View File

@ -212,7 +212,8 @@ export function executeIf<T>(condition: boolean, promiseFunc: () => Promise<T>):
export const sessionIdKey = 'pokerogue_sessionId'; export const sessionIdKey = 'pokerogue_sessionId';
export const isLocal = window.location.hostname === 'localhost'; export const isLocal = window.location.hostname === 'localhost';
export const serverUrl = isLocal ? 'http://localhost:8001' : ''; export const serverUrl = isLocal ? 'http://localhost:8001' : '';
export const apiUrl = isLocal ? serverUrl : 'api'; export const apiUrl = isLocal ? serverUrl : 'https://api.pokerogue.net';
export const fallbackApiUrl = isLocal ? serverUrl : 'api';
export function setCookie(cName: string, cValue: string): void { export function setCookie(cName: string, cValue: string): void {
const expiration = new Date(); const expiration = new Date();
@ -233,7 +234,7 @@ export function getCookie(cName: string): string {
return ''; return '';
} }
export function apiFetch(path: string, authed: boolean = false): Promise<Response> { export function apiFetch(path: string, authed: boolean = false, fallback: boolean = false): Promise<Response> {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
const request = {}; const request = {};
if (authed) { if (authed) {
@ -241,24 +242,40 @@ export function apiFetch(path: string, authed: boolean = false): Promise<Respons
if (sId) if (sId)
request['headers'] = { 'Authorization': sId }; request['headers'] = { 'Authorization': sId };
} }
fetch(`${apiUrl}/${path}`, request) fetch(`${!fallback ? apiUrl : fallbackApiUrl}/${path}`, request)
.then(response => resolve(response)) .then(response => {
.catch(err => reject(err)); if (!response.ok && response.status === 404 && !fallback)
return apiFetch(path, authed, true).then(res => resolve(res));
resolve(response);
})
.catch(err => {
if (fallback)
reject(err);
else
apiFetch(path, authed, true).then(res => resolve(res));
});
}); });
} }
export function apiPost(path: string, data?: any, contentType: string = 'application/json'): Promise<Response> { export function apiPost(path: string, data?: any, contentType: string = 'application/json', authed: boolean = false, fallback: boolean = false): Promise<Response> {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
const headers = { const headers = {
'Accept': contentType, 'Accept': contentType,
'Content-Type': contentType, 'Content-Type': contentType,
}; };
const sId = getCookie(sessionIdKey); if (authed) {
if (sId) const sId = getCookie(sessionIdKey);
headers['Authorization'] = sId; if (sId)
fetch(`${apiUrl}/${path}`, { method: 'POST', headers: headers, body: data }) headers['Authorization'] = sId;
}
fetch(`${!fallback ? apiUrl : fallbackApiUrl}/${path}`, { method: 'POST', headers: headers, body: data })
.then(response => resolve(response)) .then(response => resolve(response))
.catch(err => reject(err)); .catch(err => {
if (fallback)
reject(err);
else
apiPost(path, data, contentType, authed, true).then(res => resolve(res));
});
}); });
} }