Compare commits

..

No commits in common. "8d3c334e509db3b7bd80923aec8ef99ae4606052" and "b10102b52af9c72360a8dd17ba63668e2bd07be6" have entirely different histories.

13 changed files with 80 additions and 170 deletions

7
.eslintignore Normal file
View File

@ -0,0 +1,7 @@
dist/*
build/*
coverage/*
public/*
.github/*
node_modules/*
.vscode/*

35
.eslintrc Normal file
View File

@ -0,0 +1,35 @@
{
"parser": "@typescript-eslint/parser", // Specifies the ESLint parser for TypeScript
"plugins": ["@typescript-eslint", "import"], // Includes TypeScript and import plugins
"overrides": [
{
"files": ["src/**/*.{ts,tsx,js,jsx}"], // Applies these rules to all TypeScript and JavaScript files in the src directory
"rules": {
// General rules that apply to all files
"eqeqeq": ["error", "always"], // Enforces the use of === and !== instead of == and !=
"indent": ["error", 2], // Enforces a 2-space indentation
"quotes": ["error", "double"], // Enforces the use of double quotes for strings
"no-var": "error", // Disallows the use of var, enforcing let or const instead
"prefer-const": "error", // Prefers the use of const for variables that are never reassigned
"no-undef": "off", // Disables the rule that disallows the use of undeclared variables (TypeScript handles this)
"@typescript-eslint/no-unused-vars": [ "error", {
"args": "none", // Allows unused function parameters. Useful for functions with specific signatures where not all parameters are always used.
"ignoreRestSiblings": true // Allows unused variables that are part of a rest property in object destructuring. Useful for excluding certain properties from an object while using the rest.
}],
"eol-last": ["error", "always"], // Enforces at least one newline at the end of files
"@typescript-eslint/semi": ["error", "always"], // Requires semicolons for TypeScript-specific syntax
"semi": "off", // Disables the general semi rule for TypeScript files
"@typescript-eslint/no-extra-semi": ["error"], // Disallows unnecessary semicolons for TypeScript-specific syntax
"brace-style": "off", // Note: you must disable the base rule as it can report incorrect errors
"curly": ["error", "all"], // Enforces the use of curly braces for all control statements
"@typescript-eslint/brace-style": ["error", "1tbs"],
"no-trailing-spaces": ["error", { // Disallows trailing whitespace at the end of lines
"skipBlankLines": false, // Enforces the rule even on blank lines
"ignoreComments": false // Enforces the rule on lines containing comments
}],
"space-before-blocks": ["error", "always"], // Enforces a space before blocks
"keyword-spacing": ["error", { "before": true, "after": true }] // Enforces spacing before and after keywords
}
}
]
}

View File

@ -28,4 +28,4 @@ jobs:
run: npm ci # Use 'npm ci' to install dependencies run: npm ci # Use 'npm ci' to install dependencies
- name: eslint # Step to run linters - name: eslint # Step to run linters
run: npm run eslint-ci uses: icrawl/action-eslint@v1

View File

@ -1,42 +0,0 @@
import tseslint from '@typescript-eslint/eslint-plugin';
import parser from '@typescript-eslint/parser';
import imports from 'eslint-plugin-import';
export default [
{
files: ["src/**/*.{ts,tsx,js,jsx}"],
ignores: ["dist/*", "build/*", "coverage/*", "public/*", ".github/*", "node_modules/*", ".vscode/*"],
languageOptions: {
parser: parser
},
plugins: {
imports: imports.configs.recommended,
'@typescript-eslint': tseslint
},
rules: {
"eqeqeq": ["error", "always"], // Enforces the use of === and !== instead of == and !=
"indent": ["error", 2], // Enforces a 2-space indentation
"quotes": ["error", "double"], // Enforces the use of double quotes for strings
"no-var": "error", // Disallows the use of var, enforcing let or const instead
"prefer-const": "error", // Prefers the use of const for variables that are never reassigned
"no-undef": "off", // Disables the rule that disallows the use of undeclared variables (TypeScript handles this)
"@typescript-eslint/no-unused-vars": [ "error", {
"args": "none", // Allows unused function parameters. Useful for functions with specific signatures where not all parameters are always used.
"ignoreRestSiblings": true // Allows unused variables that are part of a rest property in object destructuring. Useful for excluding certain properties from an object while using the rest.
}],
"eol-last": ["error", "always"], // Enforces at least one newline at the end of files
"@typescript-eslint/semi": ["error", "always"], // Requires semicolons for TypeScript-specific syntax
"semi": "off", // Disables the general semi rule for TypeScript files
"no-extra-semi": ["error"], // Disallows unnecessary semicolons for TypeScript-specific syntax
"brace-style": "off", // Note: you must disable the base rule as it can report incorrect errors
"curly": ["error", "all"], // Enforces the use of curly braces for all control statements
"@typescript-eslint/brace-style": ["error", "1tbs"],
"no-trailing-spaces": ["error", { // Disallows trailing whitespace at the end of lines
"skipBlankLines": false, // Enforces the rule even on blank lines
"ignoreComments": false // Enforces the rule on lines containing comments
}],
"space-before-blocks": ["error", "always"], // Enforces a space before blocks
"keyword-spacing": ["error", { "before": true, "after": true }] // Enforces spacing before and after keywords
}
}
]

View File

@ -11,8 +11,7 @@
"test": "vitest run", "test": "vitest run",
"test:cov": "vitest run --coverage", "test:cov": "vitest run --coverage",
"test:watch": "vitest watch --coverage", "test:watch": "vitest watch --coverage",
"eslint": "eslint --fix .", "eslint": "eslint --fix ."
"eslint-ci": "eslint ."
}, },
"devDependencies": { "devDependencies": {
"@eslint/js": "^9.3.0", "@eslint/js": "^9.3.0",

View File

@ -63,8 +63,6 @@ export default class Battle {
private battleSeedState: string; private battleSeedState: string;
public moneyScattered: number; public moneyScattered: number;
public lastUsedPokeball: PokeballType; public lastUsedPokeball: PokeballType;
public playerFaints: number; // The amount of times pokemon on the players side have fainted
public enemyFaints: number; // The amount of times pokemon on the enemies side have fainted
private rngCounter: integer = 0; private rngCounter: integer = 0;
@ -91,8 +89,6 @@ export default class Battle {
this.battleSeedState = null; this.battleSeedState = null;
this.moneyScattered = 0; this.moneyScattered = 0;
this.lastUsedPokeball = null; this.lastUsedPokeball = null;
this.playerFaints = 0;
this.enemyFaints = 0;
} }
private initBattleSpec(): void { private initBattleSpec(): void {

View File

@ -1,28 +0,0 @@
/**
* Nintendo Pro Controller mapping
*/
const pad_procon = {
padID: "Pro Controller",
padType: "Nintendo",
gamepadMapping: {
RC_S: 1,
RC_E: 0,
RC_W: 3,
RC_N: 2,
START: 9, // +
SELECT: 8, // -
LB: 4,
RB: 5,
LT: 6,
RT: 7,
LS: 10,
RS: 11,
LC_N: 12,
LC_S: 13,
LC_W: 14,
LC_E: 15,
MENU: 16, // Home
},
};
export default pad_procon;

View File

@ -1083,37 +1083,6 @@ export class LowHpMoveTypePowerBoostAbAttr extends MoveTypePowerBoostAbAttr {
} }
} }
/**
* Abilities which cause a variable amount of power increase.
* @extends VariableMovePowerAbAttr
* @see {@link applyPreAttack}
*/
export class VariableMovePowerBoostAbAttr extends VariableMovePowerAbAttr {
private mult: (user: Pokemon, target: Pokemon, move: Move) => number;
/**
* @param mult A function which takes the user, target, and move, and returns the power multiplier. 1 means no multiplier.
* @param {boolean} showAbility Whether to show the ability when it activates.
*/
constructor(mult: (user: Pokemon, target: Pokemon, move: Move) => number, showAbility: boolean = true) {
super(showAbility);
this.mult = mult;
}
/**
* @override
*/
applyPreAttack(pokemon: Pokemon, passive: boolean, defender: Pokemon, move: PokemonMove, args: any[]): boolean {
const multiplier = this.mult(pokemon, defender, move.getMove());
if (multiplier !== 1) {
(args[0] as Utils.NumberHolder).value *= multiplier;
return true;
}
return false;
}
}
export class FieldVariableMovePowerAbAttr extends AbAttr { export class FieldVariableMovePowerAbAttr extends AbAttr {
applyPreAttack(pokemon: Pokemon, passive: boolean, defender: Pokemon, move: PokemonMove, args: any[]): boolean { applyPreAttack(pokemon: Pokemon, passive: boolean, defender: Pokemon, move: PokemonMove, args: any[]): boolean {
//const power = args[0] as Utils.NumberHolder; //const power = args[0] as Utils.NumberHolder;
@ -4282,8 +4251,7 @@ export function initAbilities() {
new Ability(Abilities.SHARPNESS, 9) new Ability(Abilities.SHARPNESS, 9)
.attr(MovePowerBoostAbAttr, (user, target, move) => move.hasFlag(MoveFlags.SLICING_MOVE), 1.5), .attr(MovePowerBoostAbAttr, (user, target, move) => move.hasFlag(MoveFlags.SLICING_MOVE), 1.5),
new Ability(Abilities.SUPREME_OVERLORD, 9) new Ability(Abilities.SUPREME_OVERLORD, 9)
.attr(VariableMovePowerBoostAbAttr, (user, target, move) => 1 + 0.1 * Math.min(user.isPlayer() ? user.scene.currentBattle.playerFaints : user.scene.currentBattle.enemyFaints, 5)) .unimplemented(),
.partial(),
new Ability(Abilities.COSTAR, 9) new Ability(Abilities.COSTAR, 9)
.unimplemented(), .unimplemented(),
new Ability(Abilities.TOXIC_DEBRIS, 9) new Ability(Abilities.TOXIC_DEBRIS, 9)

View File

@ -144,8 +144,8 @@ export default class Move implements Localizable {
localize(): void { localize(): void {
const i18nKey = Moves[this.id].split("_").filter(f => f).map((f, i) => i ? `${f[0]}${f.slice(1).toLowerCase()}` : f.toLowerCase()).join("") as unknown as string; const i18nKey = Moves[this.id].split("_").filter(f => f).map((f, i) => i ? `${f[0]}${f.slice(1).toLowerCase()}` : f.toLowerCase()).join("") as unknown as string;
this.name = this.id ? `${i18next.t(`move:${i18nKey}.name`)}${this.nameAppend}` : ""; this.name = this.id ? `${i18next.t(`move:${i18nKey}.name`).toString()}${this.nameAppend}` : "";
this.effect = this.id ? `${i18next.t(`move:${i18nKey}.effect`)}${this.nameAppend}` : ""; this.effect = this.id ? `${i18next.t(`move:${i18nKey}.effect`).toString()}${this.nameAppend}` : "";
} }
getAttrs(attrType: { new(...args: any[]): MoveAttr }): MoveAttr[] { getAttrs(attrType: { new(...args: any[]): MoveAttr }): MoveAttr[] {
@ -2011,10 +2011,10 @@ export class PostVictoryStatChangeAttr extends MoveAttr {
} }
applyPostVictory(user: Pokemon, target: Pokemon, move: Move): void { applyPostVictory(user: Pokemon, target: Pokemon, move: Move): void {
if (this.condition && !this.condition(user, target, move)) { if (this.condition && !this.condition(user, target, move)) {
return; return false;
} }
const statChangeAttr = new StatChangeAttr(this.stats, this.levels, this.showMessage); const statChangeAttr = new StatChangeAttr(this.stats, this.levels, this.showMessage);
statChangeAttr.apply(user, target, move, undefined); statChangeAttr.apply(user, target, move);
} }
} }
@ -7508,7 +7508,10 @@ export function initMoves() {
.attr(ConfuseAttr) .attr(ConfuseAttr)
.recklessMove(), .recklessMove(),
new AttackMove(Moves.LAST_RESPECTS, Type.GHOST, MoveCategory.PHYSICAL, 50, 100, 10, -1, 0, 9) new AttackMove(Moves.LAST_RESPECTS, Type.GHOST, MoveCategory.PHYSICAL, 50, 100, 10, -1, 0, 9)
.attr(MovePowerMultiplierAttr, (user, target, move) => 1 + Math.min(user.isPlayer() ? user.scene.currentBattle.playerFaints : user.scene.currentBattle.enemyFaints, 100)) .attr(MovePowerMultiplierAttr, (user, target, move) => {
return user.scene.getParty().reduce((acc, pokemonInParty) => acc + (pokemonInParty.status?.effect === StatusEffect.FAINT ? 1 : 0),
1,);
})
.makesContact(false), .makesContact(false),
new AttackMove(Moves.LUMINA_CRASH, Type.PSYCHIC, MoveCategory.SPECIAL, 80, 100, 10, 100, 0, 9) new AttackMove(Moves.LUMINA_CRASH, Type.PSYCHIC, MoveCategory.SPECIAL, 80, 100, 10, 100, 0, 9)
.attr(StatChangeAttr, BattleStat.SPDEF, -2), .attr(StatChangeAttr, BattleStat.SPDEF, -2),

View File

@ -5,7 +5,6 @@ import pad_generic from "./configs/pad_generic";
import pad_unlicensedSNES from "./configs/pad_unlicensedSNES"; import pad_unlicensedSNES from "./configs/pad_unlicensedSNES";
import pad_xbox360 from "./configs/pad_xbox360"; import pad_xbox360 from "./configs/pad_xbox360";
import pad_dualshock from "./configs/pad_dualshock"; import pad_dualshock from "./configs/pad_dualshock";
import pad_procon from "./configs/pad_procon";
import {Button} from "./enums/buttons"; import {Button} from "./enums/buttons";
import BattleScene from "./battle-scene"; import BattleScene from "./battle-scene";
@ -423,7 +422,6 @@ export class InputsController {
* - If the ID includes both '081f' and 'e401', it is identified as an unlicensed SNES gamepad. * - If the ID includes both '081f' and 'e401', it is identified as an unlicensed SNES gamepad.
* - If the ID contains 'xbox' and '360', it is identified as an Xbox 360 gamepad. * - If the ID contains 'xbox' and '360', it is identified as an Xbox 360 gamepad.
* - If the ID contains '054c', it is identified as a DualShock gamepad. * - If the ID contains '054c', it is identified as a DualShock gamepad.
* - If the ID includes both '057e' and '2009', it is identified as a Pro controller gamepad.
* If no specific identifiers are recognized, a generic gamepad configuration is returned. * If no specific identifiers are recognized, a generic gamepad configuration is returned.
*/ */
mapGamepad(id: string): GamepadConfig { mapGamepad(id: string): GamepadConfig {
@ -435,8 +433,6 @@ export class InputsController {
return pad_xbox360; return pad_xbox360;
} else if (id.includes("054c")) { } else if (id.includes("054c")) {
return pad_dualshock; return pad_dualshock;
} else if (id.includes("057e") && id.includes("2009")) {
return pad_procon;
} }
return pad_generic; return pad_generic;

View File

@ -3491,13 +3491,6 @@ export class FaintPhase extends PokemonPhase {
doFaint(): void { doFaint(): void {
const pokemon = this.getPokemon(); const pokemon = this.getPokemon();
// Track total times pokemon have been KO'd for supreme overlord/last respects
if (pokemon.isPlayer()) {
this.scene.currentBattle.playerFaints += 1;
} else {
this.scene.currentBattle.enemyFaints += 1;
}
this.scene.queueMessage(getPokemonMessage(pokemon, " fainted!"), null, true); this.scene.queueMessage(getPokemonMessage(pokemon, " fainted!"), null, true);
if (pokemon.turnData?.attacksReceived?.length) { if (pokemon.turnData?.attacksReceived?.length) {

View File

@ -6,16 +6,17 @@ import StarterSelectUiHandler from "./ui/starter-select-ui-handler";
import {Setting, settingOptions} from "./system/settings"; import {Setting, settingOptions} from "./system/settings";
import SettingsUiHandler from "./ui/settings-ui-handler"; import SettingsUiHandler from "./ui/settings-ui-handler";
import {Button} from "./enums/buttons"; import {Button} from "./enums/buttons";
import BattleScene from "./battle-scene";
type ActionKeys = Record<Button, () => void>; export interface ActionKeys {
[key in Button]: () => void;
}
export class UiInputs { export class UiInputs {
private scene: BattleScene; private scene: Phaser.Scene;
private events: Phaser.Events.EventEmitter; private events: Phaser.Events;
private inputsController: InputsController; private inputsController: InputsController;
constructor(scene: BattleScene, inputsController: InputsController) { constructor(scene: Phaser.Scene, inputsController: InputsController) {
this.scene = scene; this.scene = scene;
this.inputsController = inputsController; this.inputsController = inputsController;
this.init(); this.init();
@ -51,48 +52,30 @@ export class UiInputs {
} }
getActionsKeyDown(): ActionKeys { getActionsKeyDown(): ActionKeys {
const actions: ActionKeys = { const actions = {};
[Button.UP]: () => this.buttonDirection(Button.UP), actions[Button.UP] = () => this.buttonDirection(Button.UP);
[Button.DOWN]: () => this.buttonDirection(Button.DOWN), actions[Button.DOWN] = () => this.buttonDirection(Button.DOWN);
[Button.LEFT]: () => this.buttonDirection(Button.LEFT), actions[Button.LEFT] = () => this.buttonDirection(Button.LEFT);
[Button.RIGHT]: () => this.buttonDirection(Button.RIGHT), actions[Button.RIGHT] = () => this.buttonDirection(Button.RIGHT);
[Button.SUBMIT]: () => this.buttonTouch(), actions[Button.SUBMIT] = () => this.buttonTouch();
[Button.ACTION]: () => this.buttonAb(Button.ACTION), actions[Button.ACTION] = () => this.buttonAb(Button.ACTION);
[Button.CANCEL]: () => this.buttonAb(Button.CANCEL), actions[Button.CANCEL] = () => this.buttonAb(Button.CANCEL);
[Button.MENU]: () => this.buttonMenu(), actions[Button.MENU] = () => this.buttonMenu();
[Button.STATS]: () => this.buttonStats(true), actions[Button.STATS] = () => this.buttonStats(true);
[Button.CYCLE_SHINY]: () => this.buttonCycleOption(Button.CYCLE_SHINY), actions[Button.CYCLE_SHINY] = () => this.buttonCycleOption(Button.CYCLE_SHINY);
[Button.CYCLE_FORM]: () => this.buttonCycleOption(Button.CYCLE_FORM), actions[Button.CYCLE_FORM] = () => this.buttonCycleOption(Button.CYCLE_FORM);
[Button.CYCLE_GENDER]: () => this.buttonCycleOption(Button.CYCLE_GENDER), actions[Button.CYCLE_GENDER] = () => this.buttonCycleOption(Button.CYCLE_GENDER);
[Button.CYCLE_ABILITY]: () => this.buttonCycleOption(Button.CYCLE_ABILITY), actions[Button.CYCLE_ABILITY] = () => this.buttonCycleOption(Button.CYCLE_ABILITY);
[Button.CYCLE_NATURE]: () => this.buttonCycleOption(Button.CYCLE_NATURE), actions[Button.CYCLE_NATURE] = () => this.buttonCycleOption(Button.CYCLE_NATURE);
[Button.CYCLE_VARIANT]: () => this.buttonCycleOption(Button.CYCLE_VARIANT), actions[Button.CYCLE_VARIANT] = () => this.buttonCycleOption(Button.CYCLE_VARIANT);
[Button.SPEED_UP]: () => this.buttonSpeedChange(), actions[Button.SPEED_UP] = () => this.buttonSpeedChange();
[Button.SLOW_DOWN]: () => this.buttonSpeedChange(false), actions[Button.SLOW_DOWN] = () => this.buttonSpeedChange(false);
};
return actions; return actions;
} }
getActionsKeyUp(): ActionKeys { getActionsKeyUp(): ActionKeys {
const actions: ActionKeys = { const actions = {};
[Button.UP]: () => undefined, actions[Button.STATS] = () => this.buttonStats(false);
[Button.DOWN]: () => undefined,
[Button.LEFT]: () => undefined,
[Button.RIGHT]: () => undefined,
[Button.SUBMIT]: () => undefined,
[Button.ACTION]: () => undefined,
[Button.CANCEL]: () => undefined,
[Button.MENU]: () => undefined,
[Button.STATS]: () => this.buttonStats(false),
[Button.CYCLE_SHINY]: () => undefined,
[Button.CYCLE_FORM]: () => undefined,
[Button.CYCLE_GENDER]: () => undefined,
[Button.CYCLE_ABILITY]: () => undefined,
[Button.CYCLE_NATURE]: () => undefined,
[Button.CYCLE_VARIANT]: () => undefined,
[Button.SPEED_UP]: () => undefined,
[Button.SLOW_DOWN]: () => undefined,
};
return actions; return actions;
} }

View File

@ -897,7 +897,7 @@ export default class SummaryUiHandler extends UiHandler {
if (this.summaryUiMode === SummaryUiMode.LEARN_MOVE) { if (this.summaryUiMode === SummaryUiMode.LEARN_MOVE) {
this.extraMoveRowContainer.setVisible(true); this.extraMoveRowContainer.setVisible(true);
const newMoveTypeIcon = this.scene.add.sprite(0, 0, `types${Utils.verifyLang(i18next.language) ? `_${i18next.language}` : ""}`, Type[this.newMove.type].toLowerCase()); const newMoveTypeIcon = this.scene.add.sprite(0, 0, "types", Type[this.newMove.type].toLowerCase());
newMoveTypeIcon.setOrigin(0, 1); newMoveTypeIcon.setOrigin(0, 1);
this.extraMoveRowContainer.add(newMoveTypeIcon); this.extraMoveRowContainer.add(newMoveTypeIcon);