Merge branch 'pagefaultgames:beta' into beta

This commit is contained in:
Chapybara-jp 2024-09-15 01:47:59 +02:00 committed by GitHub
commit fb11079ea7
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
17 changed files with 186 additions and 31 deletions

View File

@ -105,6 +105,7 @@ import HeldModifierConfig from "#app/interfaces/held-modifier-config";
import { ExpPhase } from "#app/phases/exp-phase"; import { ExpPhase } from "#app/phases/exp-phase";
import { ShowPartyExpBarPhase } from "#app/phases/show-party-exp-bar-phase"; import { ShowPartyExpBarPhase } from "#app/phases/show-party-exp-bar-phase";
import { MysteryEncounterMode } from "#enums/mystery-encounter-mode"; import { MysteryEncounterMode } from "#enums/mystery-encounter-mode";
import { ExpGainsSpeed } from "./enums/exp-gains-speed";
export const bypassLogin = import.meta.env.VITE_BYPASS_LOGIN === "1"; export const bypassLogin = import.meta.env.VITE_BYPASS_LOGIN === "1";
@ -180,7 +181,7 @@ export default class BattleScene extends SceneBase {
public experimentalSprites: boolean = false; public experimentalSprites: boolean = false;
public musicPreference: integer = 0; public musicPreference: integer = 0;
public moveAnimations: boolean = true; public moveAnimations: boolean = true;
public expGainsSpeed: integer = 0; public expGainsSpeed: ExpGainsSpeed = ExpGainsSpeed.DEFAULT;
public skipSeenDialogues: boolean = false; public skipSeenDialogues: boolean = false;
/** /**
* Determines if the egg hatching animation should be skipped * Determines if the egg hatching animation should be skipped

View File

@ -10,6 +10,7 @@ import { applyDamageToPokemon } from "#app/data/mystery-encounters/utils/encount
import { MysteryEncounterTier } from "#enums/mystery-encounter-tier"; import { MysteryEncounterTier } from "#enums/mystery-encounter-tier";
import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode"; import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode";
import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/game-mode"; import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/game-mode";
import {PokemonMove} from "#app/field/pokemon";
const OPTION_1_REQUIRED_MOVE = Moves.SURF; const OPTION_1_REQUIRED_MOVE = Moves.SURF;
const OPTION_2_REQUIRED_MOVE = Moves.FLY; const OPTION_2_REQUIRED_MOVE = Moves.FLY;
@ -44,8 +45,8 @@ export const LostAtSeaEncounter: MysteryEncounter = MysteryEncounterBuilder.with
const encounter = scene.currentBattle.mysteryEncounter!; const encounter = scene.currentBattle.mysteryEncounter!;
encounter.setDialogueToken("damagePercentage", String(DAMAGE_PERCENTAGE)); encounter.setDialogueToken("damagePercentage", String(DAMAGE_PERCENTAGE));
encounter.setDialogueToken("option1RequiredMove", Moves[OPTION_1_REQUIRED_MOVE]); encounter.setDialogueToken("option1RequiredMove", new PokemonMove(OPTION_1_REQUIRED_MOVE).getName());
encounter.setDialogueToken("option2RequiredMove", Moves[OPTION_2_REQUIRED_MOVE]); encounter.setDialogueToken("option2RequiredMove", new PokemonMove(OPTION_2_REQUIRED_MOVE).getName());
return true; return true;
}) })

View File

@ -87,6 +87,7 @@ export const MysteriousChallengersEncounter: MysteryEncounter =
); );
const e4Template = trainerPartyTemplates.ELITE_FOUR; const e4Template = trainerPartyTemplates.ELITE_FOUR;
const brutalConfig = trainerConfigs[brutalTrainerType].clone(); const brutalConfig = trainerConfigs[brutalTrainerType].clone();
brutalConfig.title = trainerConfigs[brutalTrainerType].title;
brutalConfig.setPartyTemplates(e4Template); brutalConfig.setPartyTemplates(e4Template);
// @ts-ignore // @ts-ignore
brutalConfig.partyTemplateFunc = null; // Overrides gym leader party template func brutalConfig.partyTemplateFunc = null; // Overrides gym leader party template func

View File

@ -255,7 +255,9 @@ export class TrainerConfig {
name = i18next.t("trainerNames:rival"); name = i18next.t("trainerNames:rival");
} }
} }
this.name = name; this.name = name;
return this; return this;
} }
@ -899,6 +901,20 @@ export class TrainerConfig {
return this; return this;
} }
/**
* Sets a localized name for the trainer. This should only be used for trainers that dont use a "initFor" function and are considered "named" trainers
* @param name - The name of the trainer.
* @returns {TrainerConfig} The updated TrainerConfig instance.
*/
setLocalizedName(name: string): TrainerConfig {
// Check if the internationalization (i18n) system is initialized.
if (!getIsInitialized()) {
initI18n();
}
this.name = i18next.t(`trainerNames:${name.toLowerCase()}`);
return this;
}
/** /**
* Retrieves the title for the trainer based on the provided trainer slot and variant. * Retrieves the title for the trainer based on the provided trainer slot and variant.
* @param {TrainerSlot} trainerSlot - The slot to determine which title to use. Defaults to TrainerSlot.NONE. * @param {TrainerSlot} trainerSlot - The slot to determine which title to use. Defaults to TrainerSlot.NONE.
@ -2270,21 +2286,22 @@ export const trainerConfigs: TrainerConfigs = {
} }
p.pokeball = PokeballType.MASTER_BALL; p.pokeball = PokeballType.MASTER_BALL;
})), })),
[TrainerType.VICTOR]: new TrainerConfig(++t).setName("Victor").setTitle("The Winstrates") [TrainerType.VICTOR]: new TrainerConfig(++t).setTitle("The Winstrates").setLocalizedName("Victor")
.setMoneyMultiplier(1) // The Winstrate trainers have total money multiplier of 6 .setMoneyMultiplier(1) // The Winstrate trainers have total money multiplier of 6
.setPartyTemplates(trainerPartyTemplates.ONE_AVG_ONE_STRONG), .setPartyTemplates(trainerPartyTemplates.ONE_AVG_ONE_STRONG),
[TrainerType.VICTORIA]: new TrainerConfig(++t).setName("Victoria").setTitle("The Winstrates") [TrainerType.VICTORIA]: new TrainerConfig(++t).setTitle("The Winstrates").setLocalizedName("Victoria")
.setMoneyMultiplier(1) .setMoneyMultiplier(1)
.setPartyTemplates(trainerPartyTemplates.ONE_AVG_ONE_STRONG), .setPartyTemplates(trainerPartyTemplates.ONE_AVG_ONE_STRONG),
[TrainerType.VIVI]: new TrainerConfig(++t).setName("Vivi").setTitle("The Winstrates") [TrainerType.VIVI]: new TrainerConfig(++t).setTitle("The Winstrates").setLocalizedName("Vivi")
.setMoneyMultiplier(1) .setMoneyMultiplier(1)
.setPartyTemplates(trainerPartyTemplates.TWO_AVG_ONE_STRONG), .setPartyTemplates(trainerPartyTemplates.TWO_AVG_ONE_STRONG),
[TrainerType.VICKY]: new TrainerConfig(++t).setName("Vicky").setTitle("The Winstrates") [TrainerType.VICKY]: new TrainerConfig(++t).setTitle("The Winstrates").setLocalizedName("Vicky")
.setMoneyMultiplier(1) .setMoneyMultiplier(1)
.setPartyTemplates(trainerPartyTemplates.ONE_AVG), .setPartyTemplates(trainerPartyTemplates.ONE_AVG),
[TrainerType.VITO]: new TrainerConfig(++t).setName("Vito").setTitle("The Winstrates") [TrainerType.VITO]: new TrainerConfig(++t).setTitle("The Winstrates").setLocalizedName("Vito")
.setMoneyMultiplier(2) .setMoneyMultiplier(2)
.setPartyTemplates(new TrainerPartyCompoundTemplate(new TrainerPartyTemplate(3, PartyMemberStrength.AVERAGE), new TrainerPartyTemplate(2, PartyMemberStrength.STRONG))), .setPartyTemplates(new TrainerPartyCompoundTemplate(new TrainerPartyTemplate(3, PartyMemberStrength.AVERAGE), new TrainerPartyTemplate(2, PartyMemberStrength.STRONG))),
[TrainerType.BUG_TYPE_SUPERFAN]: new TrainerConfig(++t).setMoneyMultiplier(2.25).setEncounterBgm(TrainerType.ACE_TRAINER) [TrainerType.BUG_TYPE_SUPERFAN]: new TrainerConfig(++t).setMoneyMultiplier(2.25).setEncounterBgm(TrainerType.ACE_TRAINER)
.setPartyTemplates(new TrainerPartyTemplate(2, PartyMemberStrength.AVERAGE)) .setPartyTemplates(new TrainerPartyTemplate(2, PartyMemberStrength.AVERAGE))
}; };

View File

@ -0,0 +1,22 @@
/**
* Defines the speed of gaining experience.
*
* @remarks
* The `expGainSpeed` can have several modes:
* - `0` - Default: The normal speed.
* - `1` - Fast: Fast speed.
* - `2` - Faster: Faster speed.
* - `3` - Skip: Skip gaining exp animation.
*
* @default 0 - Uses the default normal speed.
*/
export enum ExpGainsSpeed {
/** The normal speed. */
DEFAULT,
/** Fast speed. */
FAST,
/** Faster speed. */
FASTER,
/** Skip gaining exp animation. */
SKIP
}

View File

@ -592,8 +592,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
// Resetting properties should not be shown on the field // Resetting properties should not be shown on the field
this.setVisible(false); this.setVisible(false);
// Reset field position // Remove the offset from having a Substitute active
this.setFieldPosition(FieldPosition.CENTER);
if (this.isOffsetBySubstitute()) { if (this.isOffsetBySubstitute()) {
this.x -= this.getSubstituteOffset()[0]; this.x -= this.getSubstituteOffset()[0];
this.y -= this.getSubstituteOffset()[1]; this.y -= this.getSubstituteOffset()[1];
@ -2615,16 +2614,12 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
if (result === HitResult.IMMUNE) { if (result === HitResult.IMMUNE) {
this.scene.queueMessage(i18next.t("battle:hitResultImmune", { pokemonName: getPokemonNameWithAffix(this) })); this.scene.queueMessage(i18next.t("battle:hitResultImmune", { pokemonName: getPokemonNameWithAffix(this) }));
} else { } else {
this.scene.queueMessage(i18next.t("battle:hitResultNoEffect")); this.scene.queueMessage(i18next.t("battle:hitResultNoEffect", { pokemonName: getPokemonNameWithAffix(this) }));
} }
} }
return result; return result;
} }
if (isCritical) {
this.scene.queueMessage(i18next.t("battle:hitResultCriticalHit"));
}
// In case of fatal damage, this tag would have gotten cleared before we could lapse it. // In case of fatal damage, this tag would have gotten cleared before we could lapse it.
const destinyTag = this.getTag(BattlerTagType.DESTINY_BOND); const destinyTag = this.getTag(BattlerTagType.DESTINY_BOND);
@ -2667,6 +2662,10 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
} }
} }
if (isCritical) {
this.scene.queueMessage(i18next.t("battle:hitResultCriticalHit"));
}
// want to include is.Fainted() in case multi hit move ends early, still want to render message // want to include is.Fainted() in case multi hit move ends early, still want to render message
if (source.turnData.hitsLeft === 1 || this.isFainted()) { if (source.turnData.hitsLeft === 1 || this.isFainted()) {
switch (result) { switch (result) {
@ -4410,7 +4409,10 @@ export class EnemyPokemon extends Pokemon {
const isCritical = move.hasAttr(CritOnlyAttr) || !!this.getTag(BattlerTagType.ALWAYS_CRIT); const isCritical = move.hasAttr(CritOnlyAttr) || !!this.getTag(BattlerTagType.ALWAYS_CRIT);
return move.category !== MoveCategory.STATUS return move.category !== MoveCategory.STATUS
&& moveTargets.some(p => p.getAttackDamage(this, move, !p.battleData.abilityRevealed, false, isCritical).damage >= p.hp); && moveTargets.some(p => {
const doesNotFail = move.applyConditions(this, p, move) || [Moves.SUCKER_PUNCH, Moves.UPPER_HAND, Moves.THUNDERCLAP].includes(move.id);
return doesNotFail && p.getAttackDamage(this, move, !p.battleData.abilityRevealed, false, isCritical).damage >= p.hp;
});
}, this); }, this);
if (koMoves.length > 0) { if (koMoves.length > 0) {

View File

@ -51,7 +51,7 @@
"renamePokemon": "Renombrar Pokémon.", "renamePokemon": "Renombrar Pokémon.",
"rename": "Renombrar", "rename": "Renombrar",
"nickname": "Apodo", "nickname": "Apodo",
"errorServerDown": "¡Ups! Ha habido un problema al contactar con el servidor.\n\nPuedes mantener esta ventana abierta, el juego se reconectará automáticamente.", "errorServerDown": "¡Ups! Ha habido un problema al contactar con el servidor.\n\nPuedes mantener esta ventana abierta,\nel juego se reconectará automáticamente.",
"noSaves": "No tienes ninguna partida guardada registrada!", "noSaves": "No tienes ninguna partida guardada registrada!",
"tooManySaves": "¡Tienes demasiadas partidas guardadas registradas!" "tooManySaves": "¡Tienes demasiadas partidas guardadas registradas!"
} }

View File

@ -364,8 +364,8 @@
"effect": "Le lanceur creuse au premier tour et frappe au second." "effect": "Le lanceur creuse au premier tour et frappe au second."
}, },
"toxic": { "toxic": {
"name": "Fil Toxique", "name": "Toxik",
"effect": "Tisse un fil imprégné de venin. Empoisonne la cible et baisse sa Vitesse." "effect": "Le lanceur empoisonne gravement la cible. Les dégâts dus au poison augmentent à chaque tour."
}, },
"confusion": { "confusion": {
"name": "Choc Mental", "name": "Choc Mental",

View File

@ -11,6 +11,10 @@
"expGainsSpeed": "Vit. barre dExp", "expGainsSpeed": "Vit. barre dExp",
"expPartyDisplay": "Afficher Exp équipe", "expPartyDisplay": "Afficher Exp équipe",
"skipSeenDialogues": "Passer dialogues connus", "skipSeenDialogues": "Passer dialogues connus",
"eggSkip": "Animation déclosion",
"never": "Jamais",
"always": "Toujours",
"ask": "Demander",
"battleStyle": "Style de combat", "battleStyle": "Style de combat",
"enableRetries": "Activer les réessais", "enableRetries": "Activer les réessais",
"hideIvs": "Masquer Scanner dIV", "hideIvs": "Masquer Scanner dIV",

View File

@ -1,4 +1,5 @@
import BattleScene from "#app/battle-scene"; import BattleScene from "#app/battle-scene";
import { ExpGainsSpeed } from "#app/enums/exp-gains-speed";
import { ExpNotification } from "#app/enums/exp-notification"; import { ExpNotification } from "#app/enums/exp-notification";
import { ExpBoosterModifier } from "#app/modifier/modifier"; import { ExpBoosterModifier } from "#app/modifier/modifier";
import * as Utils from "#app/utils"; import * as Utils from "#app/utils";
@ -44,7 +45,7 @@ export class ShowPartyExpBarPhase extends PlayerPartyMemberPokemonPhase {
} else { } else {
this.end(); this.end();
} }
} else if (this.scene.expGainsSpeed < 3) { } else if (this.scene.expGainsSpeed < ExpGainsSpeed.SKIP) {
this.scene.partyExpBar.showPokemonExp(pokemon, exp.value, false, newLevel).then(() => { this.scene.partyExpBar.showPokemonExp(pokemon, exp.value, false, newLevel).then(() => {
setTimeout(() => this.end(), 500 / Math.pow(2, this.scene.expGainsSpeed)); setTimeout(() => this.end(), 500 / Math.pow(2, this.scene.expGainsSpeed));
}); });

View File

@ -6,7 +6,7 @@ import { AiType, EnemyPokemon } from "#app/field/pokemon";
import { randSeedInt } from "#app/utils"; import { randSeedInt } from "#app/utils";
import GameManager from "#test/utils/gameManager"; import GameManager from "#test/utils/gameManager";
import Phaser from "phaser"; import Phaser from "phaser";
import { afterEach, beforeAll, describe, expect, it, vi } from "vitest"; import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest";
const TIMEOUT = 20 * 1000; const TIMEOUT = 20 * 1000;
const NUM_TRIALS = 300; const NUM_TRIALS = 300;
@ -36,22 +36,26 @@ describe("Enemy Commands - Move Selection", () => {
phaserGame = new Phaser.Game({ phaserGame = new Phaser.Game({
type: Phaser.HEADLESS, type: Phaser.HEADLESS,
}); });
game = new GameManager(phaserGame);
game.override.ability(Abilities.BALL_FETCH);
}); });
afterEach(() => { afterEach(() => {
game.phaseInterceptor.restoreOg(); game.phaseInterceptor.restoreOg();
}); });
beforeEach(() => {
game = new GameManager(phaserGame);
game.override
.ability(Abilities.BALL_FETCH)
.enemyAbility(Abilities.BALL_FETCH);
});
it( it(
"should never use Status moves if an attack can KO", "should never use Status moves if an attack can KO",
async () => { async () => {
game.override game.override
.enemySpecies(Species.ETERNATUS) .enemySpecies(Species.ETERNATUS)
.enemyMoveset([Moves.ETERNABEAM, Moves.SLUDGE_BOMB, Moves.DRAGON_DANCE, Moves.COSMIC_POWER]) .enemyMoveset([Moves.ETERNABEAM, Moves.SLUDGE_BOMB, Moves.DRAGON_DANCE, Moves.COSMIC_POWER])
.enemyAbility(Abilities.BALL_FETCH)
.ability(Abilities.BALL_FETCH)
.startingLevel(1) .startingLevel(1)
.enemyLevel(100); .enemyLevel(100);
@ -72,4 +76,31 @@ describe("Enemy Commands - Move Selection", () => {
}); });
}, TIMEOUT }, TIMEOUT
); );
it(
"should not select Last Resort if it would fail, even if the move KOs otherwise",
async () => {
game.override
.enemySpecies(Species.KANGASKHAN)
.enemyMoveset([Moves.LAST_RESORT, Moves.GIGA_IMPACT, Moves.SPLASH, Moves.SWORDS_DANCE])
.startingLevel(1)
.enemyLevel(100);
await game.classicMode.startBattle([Species.MAGIKARP]);
const enemyPokemon = game.scene.getEnemyPokemon()!;
enemyPokemon.aiType = AiType.SMART_RANDOM;
const moveChoices: MoveChoiceSet = {};
const enemyMoveset = enemyPokemon.getMoveset();
enemyMoveset.forEach(mv => moveChoices[mv!.moveId] = 0);
getEnemyMoveChoices(enemyPokemon, moveChoices);
enemyMoveset.forEach(mv => {
if (mv?.getMove().category === MoveCategory.STATUS || mv?.moveId === Moves.LAST_RESORT) {
expect(moveChoices[mv.moveId]).toBe(0);
}
});
}, TIMEOUT
);
}); });

View File

@ -3,7 +3,6 @@ import * as MysteryEncounters from "#app/data/mystery-encounters/mystery-encount
import * as EncounterPhaseUtils from "#app/data/mystery-encounters/utils/encounter-phase-utils"; import * as EncounterPhaseUtils from "#app/data/mystery-encounters/utils/encounter-phase-utils";
import { getPokemonSpecies } from "#app/data/pokemon-species"; import { getPokemonSpecies } from "#app/data/pokemon-species";
import { Biome } from "#app/enums/biome"; import { Biome } from "#app/enums/biome";
import { Moves } from "#app/enums/moves";
import { MysteryEncounterType } from "#app/enums/mystery-encounter-type"; import { MysteryEncounterType } from "#app/enums/mystery-encounter-type";
import { Species } from "#app/enums/species"; import { Species } from "#app/enums/species";
import GameManager from "#app/test/utils/gameManager"; import GameManager from "#app/test/utils/gameManager";
@ -16,6 +15,7 @@ import BattleScene from "#app/battle-scene";
import { MysteryEncounterPhase } from "#app/phases/mystery-encounter-phases"; import { MysteryEncounterPhase } from "#app/phases/mystery-encounter-phases";
import { PartyExpPhase } from "#app/phases/party-exp-phase"; import { PartyExpPhase } from "#app/phases/party-exp-phase";
const namespace = "mysteryEncounter:lostAtSea"; const namespace = "mysteryEncounter:lostAtSea";
/** Blastoise for surf. Pidgeot for fly. Abra for none. */ /** Blastoise for surf. Pidgeot for fly. Abra for none. */
const defaultParty = [Species.BLASTOISE, Species.PIDGEOT, Species.ABRA]; const defaultParty = [Species.BLASTOISE, Species.PIDGEOT, Species.ABRA];
@ -102,8 +102,8 @@ describe("Lost at Sea - Mystery Encounter", () => {
const onInitResult = onInit!(scene); const onInitResult = onInit!(scene);
expect(LostAtSeaEncounter.dialogueTokens?.damagePercentage).toBe("25"); expect(LostAtSeaEncounter.dialogueTokens?.damagePercentage).toBe("25");
expect(LostAtSeaEncounter.dialogueTokens?.option1RequiredMove).toBe(Moves[Moves.SURF]); expect(LostAtSeaEncounter.dialogueTokens?.option1RequiredMove).toBe("Surf");
expect(LostAtSeaEncounter.dialogueTokens?.option2RequiredMove).toBe(Moves[Moves.FLY]); expect(LostAtSeaEncounter.dialogueTokens?.option2RequiredMove).toBe("Fly");
expect(onInitResult).toBe(true); expect(onInitResult).toBe(true);
}); });

View File

@ -0,0 +1,55 @@
import { ExpGainsSpeed } from "#app/enums/exp-gains-speed";
import { Species } from "#app/enums/species";
import { ExpPhase } from "#app/phases/exp-phase";
import { Abilities } from "#enums/abilities";
import { Moves } from "#enums/moves";
import GameManager from "#test/utils/gameManager";
import Phaser from "phaser";
import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest";
vi.mock("../data/exp", ({}) => {
return {
getLevelRelExp: vi.fn(() => 1), //consistent levelRelExp
};
});
describe("UI - Battle Info", () => {
let phaserGame: Phaser.Game;
let game: GameManager;
beforeAll(() => {
phaserGame = new Phaser.Game({
type: Phaser.HEADLESS,
});
});
afterEach(() => {
game.phaseInterceptor.restoreOg();
});
beforeEach(() => {
game = new GameManager(phaserGame);
game.override
.moveset([Moves.GUILLOTINE, Moves.SPLASH])
.battleType("single")
.enemyAbility(Abilities.BALL_FETCH)
.enemyMoveset(Moves.SPLASH)
.enemySpecies(Species.CATERPIE);
});
it.each([ExpGainsSpeed.FAST, ExpGainsSpeed.FASTER, ExpGainsSpeed.SKIP])(
"should increase exp gains animation by 2^%i",
async (expGainsSpeed) => {
game.settings.expGainsSpeed(expGainsSpeed);
vi.spyOn(Math, "pow");
await game.classicMode.startBattle([Species.CHARIZARD]);
game.move.select(Moves.SPLASH);
await game.doKillOpponents();
await game.phaseInterceptor.to(ExpPhase, true);
expect(Math.pow).not.toHaveBeenCalledWith(2, expGainsSpeed);
}
);
});

View File

@ -54,6 +54,7 @@ import { MysteryEncounterPhase } from "#app/phases/mystery-encounter-phases";
import { expect } from "vitest"; import { expect } from "vitest";
import { MysteryEncounterType } from "#enums/mystery-encounter-type"; import { MysteryEncounterType } from "#enums/mystery-encounter-type";
import { isNullOrUndefined } from "#app/utils"; import { isNullOrUndefined } from "#app/utils";
import { ExpGainsSpeed } from "#app/enums/exp-gains-speed";
/** /**
* Class to manage the game state and transitions between phases. * Class to manage the game state and transitions between phases.
@ -148,7 +149,7 @@ export default class GameManager {
this.scene.gameSpeed = 5; this.scene.gameSpeed = 5;
this.scene.moveAnimations = false; this.scene.moveAnimations = false;
this.scene.showLevelUpStats = false; this.scene.showLevelUpStats = false;
this.scene.expGainsSpeed = 3; this.scene.expGainsSpeed = ExpGainsSpeed.SKIP;
this.scene.expParty = ExpNotification.SKIP; this.scene.expParty = ExpNotification.SKIP;
this.scene.hpBarSpeed = 3; this.scene.hpBarSpeed = 3;
this.scene.enableTutorials = false; this.scene.enableTutorials = false;

View File

@ -1,6 +1,7 @@
import { PlayerGender } from "#app/enums/player-gender"; import { PlayerGender } from "#app/enums/player-gender";
import { BattleStyle } from "#app/enums/battle-style"; import { BattleStyle } from "#app/enums/battle-style";
import { GameManagerHelper } from "./gameManagerHelper"; import { GameManagerHelper } from "./gameManagerHelper";
import { ExpGainsSpeed } from "#app/enums/exp-gains-speed";
/** /**
* Helper to handle settings for tests * Helper to handle settings for tests
@ -38,6 +39,15 @@ export class SettingsHelper extends GameManagerHelper {
this.log(`Gender set to: ${PlayerGender[gender]} (=${gender})` ); this.log(`Gender set to: ${PlayerGender[gender]} (=${gender})` );
} }
/**
* Change the exp gains speed
* @param speed the {@linkcode ExpGainsSpeed} to set
*/
expGainsSpeed(speed: ExpGainsSpeed) {
this.game.scene.expGainsSpeed = speed;
this.log(`Exp Gains Speed set to: ${ExpGainsSpeed[speed]} (=${speed})` );
}
private log(...params: any[]) { private log(...params: any[]) {
console.log("Settings:", ...params); console.log("Settings:", ...params);
} }

View File

@ -60,6 +60,7 @@ export interface PromptHandler {
expireFn?: () => void; expireFn?: () => void;
awaitingActionInput?: boolean; awaitingActionInput?: boolean;
} }
import { ExpPhase } from "#app/phases/exp-phase";
export default class PhaseInterceptor { export default class PhaseInterceptor {
public scene; public scene;
@ -127,7 +128,8 @@ export default class PhaseInterceptor {
[MysteryEncounterRewardsPhase, this.startPhase], [MysteryEncounterRewardsPhase, this.startPhase],
[PostMysteryEncounterPhase, this.startPhase], [PostMysteryEncounterPhase, this.startPhase],
[ModifierRewardPhase, this.startPhase], [ModifierRewardPhase, this.startPhase],
[PartyExpPhase, this.startPhase] [PartyExpPhase, this.startPhase],
[ExpPhase, this.startPhase],
]; ];
private endBySetMode = [ private endBySetMode = [

View File

@ -11,8 +11,11 @@ import { Stat } from "#enums/stat";
import BattleFlyout from "./battle-flyout"; import BattleFlyout from "./battle-flyout";
import { WindowVariant, addWindow } from "./ui-theme"; import { WindowVariant, addWindow } from "./ui-theme";
import i18next from "i18next"; import i18next from "i18next";
import { ExpGainsSpeed } from "#app/enums/exp-gains-speed";
export default class BattleInfo extends Phaser.GameObjects.Container { export default class BattleInfo extends Phaser.GameObjects.Container {
public static readonly EXP_GAINS_DURATION_BASE = 1650;
private baseY: number; private baseY: number;
private player: boolean; private player: boolean;
@ -702,7 +705,11 @@ export default class BattleInfo extends Phaser.GameObjects.Container {
instant = true; instant = true;
} }
const durationMultiplier = Phaser.Tweens.Builders.GetEaseFunction("Sine.easeIn")(1 - (Math.max(this.lastLevel - 100, 0) / 150)); const durationMultiplier = Phaser.Tweens.Builders.GetEaseFunction("Sine.easeIn")(1 - (Math.max(this.lastLevel - 100, 0) / 150));
const duration = this.visible && !instant ? (((levelExp - this.lastLevelExp) / relLevelExp) * 1650) * durationMultiplier * levelDurationMultiplier : 0; let duration = this.visible && !instant ? (((levelExp - this.lastLevelExp) / relLevelExp) * BattleInfo.EXP_GAINS_DURATION_BASE) * durationMultiplier * levelDurationMultiplier : 0;
const speed = (this.scene as BattleScene).expGainsSpeed;
if (speed && speed >= ExpGainsSpeed.DEFAULT) {
duration = speed >= ExpGainsSpeed.SKIP ? ExpGainsSpeed.DEFAULT : duration / Math.pow(2, speed);
}
if (ratio === 1) { if (ratio === 1) {
this.lastLevelExp = 0; this.lastLevelExp = 0;
this.lastLevel++; this.lastLevel++;