Merge branch 'beta' into pokemon-info-box-formName-enhancement
This commit is contained in:
commit
5f562ba4b9
|
@ -55,7 +55,7 @@ Check out [Github Issues](https://github.com/pagefaultgames/pokerogue/issues) to
|
|||
- Pokémon Sword/Shield
|
||||
- Pokémon Legends: Arceus
|
||||
- Pokémon Scarlet/Violet
|
||||
- Firel (Custom Laboratory, Metropolis, Seabed, and Space biome music)
|
||||
- Firel (Custom Ice Cave, Laboratory, Metropolis, Plains, Power Plant, Seabed, Space, and Volcano biome music)
|
||||
- Lmz (Custom Jungle biome music)
|
||||
- Andr06 (Custom Slum and Sea biome music)
|
||||
|
||||
|
|
28
index.css
28
index.css
|
@ -26,10 +26,36 @@ body {
|
|||
#app {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
#app > div:first-child {
|
||||
transform-origin: top !important;
|
||||
transform-origin: center !important;
|
||||
}
|
||||
|
||||
/*
|
||||
Supports automatic vertical centering as suggested in PR#1114, but only via CSS
|
||||
|
||||
Condition factorized to deduce CSS rules:
|
||||
true if (isLandscape && !isMobile() && !hasTouchscreen() || (hasTouchscreen() && !isTouchControlsEnabled))
|
||||
*/
|
||||
|
||||
/* isLandscape && !isMobile() && !hasTouchscreen() */
|
||||
@media (orientation: landscape) and (pointer: fine) {
|
||||
#app {
|
||||
align-items: center;
|
||||
}
|
||||
}
|
||||
|
||||
@media (pointer: coarse) {
|
||||
/* hasTouchscreen() && !isTouchControlsEnabled */
|
||||
body:has(> #touchControls[class=visible]) #app {
|
||||
align-items: start;
|
||||
}
|
||||
|
||||
body:has(> #touchControls[class=visible]) #app > div:first-child {
|
||||
transform-origin: top !important;
|
||||
}
|
||||
}
|
||||
|
||||
#layout:fullscreen #dpad, #layout:fullscreen {
|
||||
|
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
@ -2625,7 +2625,11 @@ export class PreStatStageChangeAbAttr extends AbAttr {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Protect one or all {@linkcode BattleStat} from reductions caused by other Pokémon's moves and Abilities
|
||||
*/
|
||||
export class ProtectStatAbAttr extends PreStatStageChangeAbAttr {
|
||||
/** {@linkcode BattleStat} to protect or `undefined` if **all** {@linkcode BattleStat} are protected */
|
||||
private protectedStat?: BattleStat;
|
||||
|
||||
constructor(protectedStat?: BattleStat) {
|
||||
|
@ -2634,7 +2638,17 @@ export class ProtectStatAbAttr extends PreStatStageChangeAbAttr {
|
|||
this.protectedStat = protectedStat;
|
||||
}
|
||||
|
||||
applyPreStatStageChange(_pokemon: Pokemon, _passive: boolean, simulated: boolean, stat: BattleStat, cancelled: Utils.BooleanHolder, _args: any[]): boolean {
|
||||
/**
|
||||
* Apply the {@linkcode ProtectedStatAbAttr} to an interaction
|
||||
* @param _pokemon
|
||||
* @param _passive
|
||||
* @param simulated
|
||||
* @param stat the {@linkcode BattleStat} being affected
|
||||
* @param cancelled The {@linkcode Utils.BooleanHolder} that will be set to true if the stat is protected
|
||||
* @param _args
|
||||
* @returns true if the stat is protected, false otherwise
|
||||
*/
|
||||
applyPreStatStageChange(_pokemon: Pokemon, _passive: boolean, _simulated: boolean, stat: BattleStat, cancelled: Utils.BooleanHolder, _args: any[]): boolean {
|
||||
if (Utils.isNullOrUndefined(this.protectedStat) || stat === this.protectedStat) {
|
||||
cancelled.value = true;
|
||||
return true;
|
||||
|
@ -3757,7 +3771,7 @@ export class StatStageChangeMultiplierAbAttr extends AbAttr {
|
|||
this.multiplier = multiplier;
|
||||
}
|
||||
|
||||
apply(pokemon: Pokemon, passive: boolean, simulated: boolean, cancelled: Utils.BooleanHolder, args: any[]): boolean {
|
||||
override apply(pokemon: Pokemon, passive: boolean, simulated: boolean, cancelled: Utils.BooleanHolder, args: any[]): boolean {
|
||||
(args[0] as Utils.IntegerHolder).value *= this.multiplier;
|
||||
|
||||
return true;
|
||||
|
|
|
@ -488,14 +488,14 @@ export function initMoveAnim(scene: BattleScene, move: Moves): Promise<void> {
|
|||
} else {
|
||||
moveAnims.set(move, null);
|
||||
const defaultMoveAnim = allMoves[move] instanceof AttackMove ? Moves.TACKLE : allMoves[move] instanceof SelfStatusMove ? Moves.FOCUS_ENERGY : Moves.TAIL_WHIP;
|
||||
const moveName = Moves[move].toLowerCase().replace(/\_/g, "-");
|
||||
|
||||
const fetchAnimAndResolve = (move: Moves) => {
|
||||
scene.cachedFetch(`./battle-anims/${moveName}.json`)
|
||||
scene.cachedFetch(`./battle-anims/${Utils.animationFileName(move)}.json`)
|
||||
.then(response => {
|
||||
const contentType = response.headers.get("content-type");
|
||||
if (!response.ok || contentType?.indexOf("application/json") === -1) {
|
||||
console.error(`Could not load animation file for move '${moveName}'`, response.status, response.statusText);
|
||||
populateMoveAnim(move, moveAnims.get(defaultMoveAnim));
|
||||
useDefaultAnim(move, defaultMoveAnim);
|
||||
logMissingMoveAnim(move, response.status, response.statusText);
|
||||
return resolve();
|
||||
}
|
||||
return response.json();
|
||||
|
@ -515,6 +515,11 @@ export function initMoveAnim(scene: BattleScene, move: Moves): Promise<void> {
|
|||
} else {
|
||||
resolve();
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
useDefaultAnim(move, defaultMoveAnim);
|
||||
logMissingMoveAnim(move, error);
|
||||
return resolve();
|
||||
});
|
||||
};
|
||||
fetchAnimAndResolve(move);
|
||||
|
@ -522,6 +527,29 @@ export function initMoveAnim(scene: BattleScene, move: Moves): Promise<void> {
|
|||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Populates the default animation for the given move.
|
||||
*
|
||||
* @param move the move to populate an animation for
|
||||
* @param defaultMoveAnim the move to use as the default animation
|
||||
*/
|
||||
function useDefaultAnim(move: Moves, defaultMoveAnim: Moves) {
|
||||
populateMoveAnim(move, moveAnims.get(defaultMoveAnim));
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper method for printing a warning to the console when a move animation is missing.
|
||||
*
|
||||
* @param move the move to populate an animation for
|
||||
* @param optionalParams parameters to add to the error logging
|
||||
*
|
||||
* @remarks use {@linkcode useDefaultAnim} to use a default animation
|
||||
*/
|
||||
function logMissingMoveAnim(move: Moves, ...optionalParams: any[]) {
|
||||
const moveName = Utils.animationFileName(move);
|
||||
console.warn(`Could not load animation file for move '${moveName}'`, ...optionalParams);
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetches animation configs to be used in a Mystery Encounter
|
||||
* @param scene
|
||||
|
|
|
@ -746,7 +746,7 @@ export class Arena {
|
|||
case Biome.TOWN:
|
||||
return 7.288;
|
||||
case Biome.PLAINS:
|
||||
return 7.693;
|
||||
return 17.485;
|
||||
case Biome.GRASS:
|
||||
return 1.995;
|
||||
case Biome.TALL_GRASS:
|
||||
|
@ -774,13 +774,13 @@ export class Arena {
|
|||
case Biome.DESERT:
|
||||
return 1.143;
|
||||
case Biome.ICE_CAVE:
|
||||
return 15.010;
|
||||
return 0.000;
|
||||
case Biome.MEADOW:
|
||||
return 3.891;
|
||||
case Biome.POWER_PLANT:
|
||||
return 2.810;
|
||||
return 9.447;
|
||||
case Biome.VOLCANO:
|
||||
return 5.116;
|
||||
return 17.637;
|
||||
case Biome.GRAVEYARD:
|
||||
return 3.232;
|
||||
case Biome.DOJO:
|
||||
|
|
|
@ -1273,13 +1273,13 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
|||
* @param attrType {@linkcode AbAttr} The ability attribute to check for.
|
||||
* @param canApply {@linkcode Boolean} If false, it doesn't check whether the ability is currently active
|
||||
* @param ignoreOverride {@linkcode Boolean} If true, it ignores ability changing effects
|
||||
* @returns {AbAttr[]} A list of all the ability attributes on this ability.
|
||||
* @returns A list of all the ability attributes on this ability.
|
||||
*/
|
||||
getAbilityAttrs(attrType: { new(...args: any[]): AbAttr }, canApply: boolean = true, ignoreOverride?: boolean): AbAttr[] {
|
||||
const abilityAttrs: AbAttr[] = [];
|
||||
getAbilityAttrs<T extends AbAttr = AbAttr>(attrType: { new(...args: any[]): T }, canApply: boolean = true, ignoreOverride?: boolean): T[] {
|
||||
const abilityAttrs: T[] = [];
|
||||
|
||||
if (!canApply || this.canApplyAbility()) {
|
||||
abilityAttrs.push(...this.getAbility(ignoreOverride).getAttrs(attrType));
|
||||
abilityAttrs.push(...this.getAbility(ignoreOverride).getAttrs<T>(attrType));
|
||||
}
|
||||
|
||||
if (!canApply || this.canApplyAbility(true)) {
|
||||
|
|
|
@ -108,7 +108,7 @@
|
|||
"forest": "PMD EoS Dusk Forest",
|
||||
"grass": "PMD EoS Apple Woods",
|
||||
"graveyard": "PMD EoS Mystifying Forest",
|
||||
"ice_cave": "PMD EoS Vast Ice Mountain",
|
||||
"ice_cave": "Firel - -60F",
|
||||
"island": "PMD EoS Craggy Coast",
|
||||
"jungle": "Lmz - Jungle",
|
||||
"laboratory": "Firel - Laboratory",
|
||||
|
@ -116,8 +116,8 @@
|
|||
"meadow": "PMD EoS Sky Peak Forest",
|
||||
"metropolis": "Firel - Metropolis",
|
||||
"mountain": "PMD EoS Mt. Horn",
|
||||
"plains": "PMD EoS Sky Peak Prairie",
|
||||
"power_plant": "PMD EoS Far Amp Plains",
|
||||
"plains": "Firel - Route 888",
|
||||
"power_plant": "Firel - The Klink",
|
||||
"ruins": "PMD EoS Deep Sealed Ruin",
|
||||
"sea": "Andr06 - Marine Mystique",
|
||||
"seabed": "Firel - Seabed",
|
||||
|
@ -128,7 +128,7 @@
|
|||
"tall_grass": "PMD EoS Foggy Forest",
|
||||
"temple": "PMD EoS Aegis Cave",
|
||||
"town": "PMD EoS Random Dungeon Theme 3",
|
||||
"volcano": "PMD EoS Steam Cave",
|
||||
"volcano": "Firel - Twisturn Volcano",
|
||||
"wasteland": "PMD EoS Hidden Highland",
|
||||
"encounter_ace_trainer": "BW Trainers' Eyes Meet (Ace Trainer)",
|
||||
"encounter_backpacker": "BW Trainers' Eyes Meet (Backpacker)",
|
||||
|
|
|
@ -13,8 +13,10 @@
|
|||
"ALL": "전부",
|
||||
"PASS_BATON": "배턴터치한다",
|
||||
"UNPAUSE_EVOLUTION": "진화 재개",
|
||||
"PAUSE_EVOLUTION": "진화 중지",
|
||||
"REVIVE": "되살린다",
|
||||
"RENAME": "닉네임 바꾸기",
|
||||
"SELECT": "선택한다",
|
||||
"choosePokemon": "포켓몬을 선택하세요.",
|
||||
"doWhatWithThisPokemon": "포켓몬을 어떻게 하겠습니까?",
|
||||
"noEnergy": "{{pokemonName}}[[는]] 싸울 수 있는\n기력이 남아 있지 않습니다!",
|
||||
|
@ -23,6 +25,7 @@
|
|||
"tooManyItems": "{{pokemonName}}[[는]] 지닌 도구의 수가\n너무 많습니다",
|
||||
"anyEffect": "써도 효과가 없다.",
|
||||
"unpausedEvolutions": "{{pokemonName}}의 진화가 재개되었다.",
|
||||
"pausedEvolutions": "{{pokemonName}}[[가]] 진화하지 않도록 했다.",
|
||||
"unspliceConfirmation": "{{pokemonName}}로부터 {{fusionName}}의 융합을 해제하시겠습니까?\n{{fusionName}}는 사라지게 됩니다.",
|
||||
"wasReverted": "{{fusionName}}은 {{pokemonName}}의 모습으로 돌아갔습니다!",
|
||||
"releaseConfirmation": "{{pokemonName}}[[를]]\n정말 놓아주겠습니까?",
|
||||
|
|
|
@ -11,6 +11,10 @@
|
|||
"expGainsSpeed": "경험치 획득 속도",
|
||||
"expPartyDisplay": "파티 경험치 표시",
|
||||
"skipSeenDialogues": "본 대화 생략",
|
||||
"eggSkip": "알 스킵",
|
||||
"never": "안 함",
|
||||
"always": "항상",
|
||||
"ask": "확인하기",
|
||||
"battleStyle": "시합 룰",
|
||||
"enableRetries": "재도전 허용",
|
||||
"hideIvs": "개체값탐지기 효과 끄기",
|
||||
|
|
|
@ -6,7 +6,7 @@ import Pokemon from "#app/field/pokemon";
|
|||
import { getPokemonNameWithAffix } from "#app/messages";
|
||||
import { ResetNegativeStatStageModifier } from "#app/modifier/modifier";
|
||||
import { handleTutorial, Tutorial } from "#app/tutorial";
|
||||
import * as Utils from "#app/utils";
|
||||
import { NumberHolder, BooleanHolder } from "#app/utils";
|
||||
import i18next from "i18next";
|
||||
import { PokemonPhase } from "./pokemon-phase";
|
||||
import { Stat, type BattleStat, getStatKey, getStatStageChangeDescriptionKey } from "#enums/stat";
|
||||
|
@ -42,17 +42,23 @@ export class StatStageChangePhase extends PokemonPhase {
|
|||
return this.end();
|
||||
}
|
||||
|
||||
const stages = new NumberHolder(this.stages);
|
||||
|
||||
if (!this.ignoreAbilities) {
|
||||
applyAbAttrs(StatStageChangeMultiplierAbAttr, pokemon, null, false, stages);
|
||||
}
|
||||
|
||||
let simulate = false;
|
||||
|
||||
const filteredStats = this.stats.filter(stat => {
|
||||
const cancelled = new Utils.BooleanHolder(false);
|
||||
const cancelled = new BooleanHolder(false);
|
||||
|
||||
if (!this.selfTarget && this.stages < 0) {
|
||||
if (!this.selfTarget && stages.value < 0) {
|
||||
// TODO: Include simulate boolean when tag applications can be simulated
|
||||
this.scene.arena.applyTagsForSide(MistTag, pokemon.isPlayer() ? ArenaTagSide.PLAYER : ArenaTagSide.ENEMY, cancelled);
|
||||
}
|
||||
|
||||
if (!cancelled.value && !this.selfTarget && this.stages < 0) {
|
||||
if (!cancelled.value && !this.selfTarget && stages.value < 0) {
|
||||
applyPreStatStageChangeAbAttrs(ProtectStatAbAttr, pokemon, stat, cancelled, simulate);
|
||||
}
|
||||
|
||||
|
@ -64,12 +70,6 @@ export class StatStageChangePhase extends PokemonPhase {
|
|||
return !cancelled.value;
|
||||
});
|
||||
|
||||
const stages = new Utils.IntegerHolder(this.stages);
|
||||
|
||||
if (!this.ignoreAbilities) {
|
||||
applyAbAttrs(StatStageChangeMultiplierAbAttr, pokemon, null, false, stages);
|
||||
}
|
||||
|
||||
const relLevels = filteredStats.map(s => (stages.value >= 1 ? Math.min(pokemon.getStatStage(s) + stages.value, 6) : Math.max(pokemon.getStatStage(s) + stages.value, -6)) - pokemon.getStatStage(s));
|
||||
|
||||
this.onChange && this.onChange(this.getPokemon(), filteredStats, relLevels);
|
||||
|
|
|
@ -31,7 +31,7 @@ describe("Abilities - Contrary", () => {
|
|||
});
|
||||
|
||||
it("should invert stat changes when applied", async() => {
|
||||
await game.startBattle([
|
||||
await game.classicMode.startBattle([
|
||||
Species.SLOWBRO
|
||||
]);
|
||||
|
||||
|
@ -39,4 +39,39 @@ describe("Abilities - Contrary", () => {
|
|||
|
||||
expect(enemyPokemon.getStatStage(Stat.ATK)).toBe(1);
|
||||
}, 20000);
|
||||
|
||||
describe("With Clear Body", () => {
|
||||
it("should apply positive effects", async () => {
|
||||
game.override
|
||||
.enemyPassiveAbility(Abilities.CLEAR_BODY)
|
||||
.moveset([Moves.TAIL_WHIP]);
|
||||
await game.classicMode.startBattle([Species.SLOWBRO]);
|
||||
|
||||
const enemyPokemon = game.scene.getEnemyPokemon()!;
|
||||
|
||||
expect(enemyPokemon.getStatStage(Stat.ATK)).toBe(1);
|
||||
|
||||
game.move.select(Moves.TAIL_WHIP);
|
||||
await game.phaseInterceptor.to("TurnEndPhase");
|
||||
|
||||
expect(enemyPokemon.getStatStage(Stat.DEF)).toBe(1);
|
||||
});
|
||||
|
||||
it("should block negative effects", async () => {
|
||||
game.override
|
||||
.enemyPassiveAbility(Abilities.CLEAR_BODY)
|
||||
.enemyMoveset([Moves.HOWL, Moves.HOWL, Moves.HOWL, Moves.HOWL])
|
||||
.moveset([Moves.SPLASH]);
|
||||
await game.classicMode.startBattle([Species.SLOWBRO]);
|
||||
|
||||
const enemyPokemon = game.scene.getEnemyPokemon()!;
|
||||
|
||||
expect(enemyPokemon.getStatStage(Stat.ATK)).toBe(1);
|
||||
|
||||
game.move.select(Moves.SPLASH);
|
||||
await game.phaseInterceptor.to("TurnEndPhase");
|
||||
|
||||
expect(enemyPokemon.getStatStage(Stat.ATK)).toBe(1);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -162,7 +162,7 @@ export default class BattleInfo extends Phaser.GameObjects.Container {
|
|||
this.splicedIcon.setInteractive(new Phaser.Geom.Rectangle(0, 0, 12, 15), Phaser.Geom.Rectangle.Contains);
|
||||
this.add(this.splicedIcon);
|
||||
|
||||
this.statusIndicator = this.scene.add.sprite(0, 0, `statuses_${i18next.resolvedLanguage}`);
|
||||
this.statusIndicator = this.scene.add.sprite(0, 0, Utils.getLocalizedSpriteKey("statuses"));
|
||||
this.statusIndicator.setName("icon_status");
|
||||
this.statusIndicator.setVisible(false);
|
||||
this.statusIndicator.setOrigin(0, 0);
|
||||
|
|
|
@ -91,7 +91,7 @@ export default class MoveInfoOverlay extends Phaser.GameObjects.Container implem
|
|||
valuesBg.setOrigin(0, 0);
|
||||
this.val.add(valuesBg);
|
||||
|
||||
this.typ = this.scene.add.sprite(25, EFF_HEIGHT - 35, `types${Utils.verifyLang(i18next.language) ? `_${i18next.language}` : ""}`, "unknown");
|
||||
this.typ = this.scene.add.sprite(25, EFF_HEIGHT - 35, Utils.getLocalizedSpriteKey("types"), "unknown");
|
||||
this.typ.setScale(0.8);
|
||||
this.val.add(this.typ);
|
||||
|
||||
|
@ -138,7 +138,7 @@ export default class MoveInfoOverlay extends Phaser.GameObjects.Container implem
|
|||
this.pow.setText(move.power >= 0 ? move.power.toString() : "---");
|
||||
this.acc.setText(move.accuracy >= 0 ? move.accuracy.toString() : "---");
|
||||
this.pp.setText(move.pp >= 0 ? move.pp.toString() : "---");
|
||||
this.typ.setTexture(`types${Utils.verifyLang(i18next.language) ? `_${i18next.language}` : ""}`, Type[move.type].toLowerCase());
|
||||
this.typ.setTexture(Utils.getLocalizedSpriteKey("types"), Type[move.type].toLowerCase());
|
||||
this.cat.setFrame(MoveCategory[move.category].toLowerCase());
|
||||
|
||||
this.desc.setText(move?.effect || "");
|
||||
|
|
|
@ -1272,7 +1272,7 @@ class PartySlot extends Phaser.GameObjects.Container {
|
|||
}
|
||||
|
||||
if (this.pokemon.status) {
|
||||
const statusIndicator = this.scene.add.sprite(0, 0, `statuses_${i18next.resolvedLanguage}`);
|
||||
const statusIndicator = this.scene.add.sprite(0, 0, Utils.getLocalizedSpriteKey("statuses"));
|
||||
statusIndicator.setFrame(StatusEffect[this.pokemon.status?.effect].toLowerCase());
|
||||
statusIndicator.setOrigin(0, 0);
|
||||
statusIndicator.setPositionRelative(slotLevelLabel, this.slotIndex >= battlerCount ? 43 : 55, 0);
|
||||
|
|
|
@ -214,7 +214,7 @@ export default class SummaryUiHandler extends UiHandler {
|
|||
|
||||
this.statusContainer.add(statusLabel);
|
||||
|
||||
this.status = this.scene.add.sprite(91, 4, `statuses_${i18next.resolvedLanguage}`);
|
||||
this.status = this.scene.add.sprite(91, 4, Utils.getLocalizedSpriteKey("statuses"));
|
||||
this.status.setOrigin(0.5, 0);
|
||||
|
||||
this.statusContainer.add(this.status);
|
||||
|
|
10
src/utils.ts
10
src/utils.ts
|
@ -1,4 +1,5 @@
|
|||
import { MoneyFormat } from "#enums/money-format";
|
||||
import { Moves } from "#enums/moves";
|
||||
import i18next from "i18next";
|
||||
|
||||
export const MissingTextureKey = "__MISSING";
|
||||
|
@ -628,3 +629,12 @@ export function getLocalizedSpriteKey(baseKey: string) {
|
|||
export function isBetween(num: number, min: number, max: number): boolean {
|
||||
return num >= min && num <= max;
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper method to return the animation filename for a given move
|
||||
*
|
||||
* @param move the move for which the animation filename is needed
|
||||
*/
|
||||
export function animationFileName(move: Moves): string {
|
||||
return Moves[move].toLowerCase().replace(/\_/g, "-");
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue