Merge branch 'beta' into multi-lens-rework

This commit is contained in:
innerthunder 2024-11-11 11:56:22 -08:00
commit 6ad2420a45
4 changed files with 55 additions and 80 deletions

View File

@ -8516,7 +8516,8 @@ export function initMoves() {
new StatusMove(Moves.HELPING_HAND, Type.NORMAL, -1, 20, -1, 5, 3) new StatusMove(Moves.HELPING_HAND, Type.NORMAL, -1, 20, -1, 5, 3)
.attr(AddBattlerTagAttr, BattlerTagType.HELPING_HAND) .attr(AddBattlerTagAttr, BattlerTagType.HELPING_HAND)
.ignoresSubstitute() .ignoresSubstitute()
.target(MoveTarget.NEAR_ALLY), .target(MoveTarget.NEAR_ALLY)
.condition(failIfSingleBattle),
new StatusMove(Moves.TRICK, Type.PSYCHIC, 100, 10, -1, 0, 3) new StatusMove(Moves.TRICK, Type.PSYCHIC, 100, 10, -1, 0, 3)
.unimplemented(), .unimplemented(),
new StatusMove(Moves.ROLE_PLAY, Type.PSYCHIC, -1, 10, -1, 0, 3) new StatusMove(Moves.ROLE_PLAY, Type.PSYCHIC, -1, 10, -1, 0, 3)
@ -9208,6 +9209,7 @@ export function initMoves() {
.target(MoveTarget.ALL_NEAR_ENEMIES) .target(MoveTarget.ALL_NEAR_ENEMIES)
.attr(RemoveHeldItemAttr, true), .attr(RemoveHeldItemAttr, true),
new StatusMove(Moves.QUASH, Type.DARK, 100, 15, -1, 0, 5) new StatusMove(Moves.QUASH, Type.DARK, 100, 15, -1, 0, 5)
.condition(failIfSingleBattle)
.unimplemented(), .unimplemented(),
new AttackMove(Moves.ACROBATICS, Type.FLYING, MoveCategory.PHYSICAL, 55, 100, 15, -1, 0, 5) new AttackMove(Moves.ACROBATICS, Type.FLYING, MoveCategory.PHYSICAL, 55, 100, 15, -1, 0, 5)
.attr(MovePowerMultiplierAttr, (user, target, move) => Math.max(1, 2 - 0.2 * user.getHeldItems().filter(i => i.isTransferable).reduce((v, m) => v + m.stackCount, 0))), .attr(MovePowerMultiplierAttr, (user, target, move) => Math.max(1, 2 - 0.2 * user.getHeldItems().filter(i => i.isTransferable).reduce((v, m) => v + m.stackCount, 0))),
@ -9495,6 +9497,7 @@ export function initMoves() {
new StatusMove(Moves.AROMATIC_MIST, Type.FAIRY, -1, 20, -1, 0, 6) new StatusMove(Moves.AROMATIC_MIST, Type.FAIRY, -1, 20, -1, 0, 6)
.attr(StatStageChangeAttr, [ Stat.SPDEF ], 1) .attr(StatStageChangeAttr, [ Stat.SPDEF ], 1)
.ignoresSubstitute() .ignoresSubstitute()
.condition(failIfSingleBattle)
.target(MoveTarget.NEAR_ALLY), .target(MoveTarget.NEAR_ALLY),
new StatusMove(Moves.EERIE_IMPULSE, Type.ELECTRIC, 100, 15, -1, 0, 6) new StatusMove(Moves.EERIE_IMPULSE, Type.ELECTRIC, 100, 15, -1, 0, 6)
.attr(StatStageChangeAttr, [ Stat.SPATK ], -2), .attr(StatStageChangeAttr, [ Stat.SPATK ], -2),
@ -9723,7 +9726,8 @@ export function initMoves() {
new AttackMove(Moves.LEAFAGE, Type.GRASS, MoveCategory.PHYSICAL, 40, 100, 40, -1, 0, 7) new AttackMove(Moves.LEAFAGE, Type.GRASS, MoveCategory.PHYSICAL, 40, 100, 40, -1, 0, 7)
.makesContact(false), .makesContact(false),
new StatusMove(Moves.SPOTLIGHT, Type.NORMAL, -1, 15, -1, 3, 7) new StatusMove(Moves.SPOTLIGHT, Type.NORMAL, -1, 15, -1, 3, 7)
.attr(AddBattlerTagAttr, BattlerTagType.CENTER_OF_ATTENTION, false), .attr(AddBattlerTagAttr, BattlerTagType.CENTER_OF_ATTENTION, false)
.condition(failIfSingleBattle),
new StatusMove(Moves.TOXIC_THREAD, Type.POISON, 100, 20, -1, 0, 7) new StatusMove(Moves.TOXIC_THREAD, Type.POISON, 100, 20, -1, 0, 7)
.attr(StatusEffectAttr, StatusEffect.POISON) .attr(StatusEffectAttr, StatusEffect.POISON)
.attr(StatStageChangeAttr, [ Stat.SPD ], -1), .attr(StatStageChangeAttr, [ Stat.SPD ], -1),
@ -10180,7 +10184,8 @@ export function initMoves() {
.unimplemented(), .unimplemented(),
new StatusMove(Moves.COACHING, Type.FIGHTING, -1, 10, -1, 0, 8) new StatusMove(Moves.COACHING, Type.FIGHTING, -1, 10, -1, 0, 8)
.attr(StatStageChangeAttr, [ Stat.ATK, Stat.DEF ], 1) .attr(StatStageChangeAttr, [ Stat.ATK, Stat.DEF ], 1)
.target(MoveTarget.NEAR_ALLY), .target(MoveTarget.NEAR_ALLY)
.condition(failIfSingleBattle),
new AttackMove(Moves.FLIP_TURN, Type.WATER, MoveCategory.PHYSICAL, 60, 100, 20, -1, 0, 8) new AttackMove(Moves.FLIP_TURN, Type.WATER, MoveCategory.PHYSICAL, 60, 100, 20, -1, 0, 8)
.attr(ForceSwitchOutAttr, true), .attr(ForceSwitchOutAttr, true),
new AttackMove(Moves.TRIPLE_AXEL, Type.ICE, MoveCategory.PHYSICAL, 20, 90, 10, -1, 0, 8) new AttackMove(Moves.TRIPLE_AXEL, Type.ICE, MoveCategory.PHYSICAL, 20, 90, 10, -1, 0, 8)

View File

@ -442,7 +442,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
}; };
if (this.shiny) { if (this.shiny) {
const populateVariantColors = (isBackSprite: boolean = false): Promise<void> => { const populateVariantColors = (isBackSprite: boolean = false): Promise<void> => {
return new Promise(resolve => { return new Promise(async resolve => {
const battleSpritePath = this.getBattleSpriteAtlasPath(isBackSprite, ignoreOverride).replace("variant/", "").replace(/_[1-3]$/, ""); const battleSpritePath = this.getBattleSpriteAtlasPath(isBackSprite, ignoreOverride).replace("variant/", "").replace(/_[1-3]$/, "");
let config = variantData; let config = variantData;
const useExpSprite = this.scene.experimentalSprites && this.scene.hasExpSprite(this.getBattleSpriteKey(isBackSprite, ignoreOverride)); const useExpSprite = this.scene.experimentalSprites && this.scene.hasExpSprite(this.getBattleSpriteKey(isBackSprite, ignoreOverride));
@ -451,7 +451,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
if (variantSet && variantSet[this.variant] === 1) { if (variantSet && variantSet[this.variant] === 1) {
const cacheKey = this.getBattleSpriteKey(isBackSprite); const cacheKey = this.getBattleSpriteKey(isBackSprite);
if (!variantColorCache.hasOwnProperty(cacheKey)) { if (!variantColorCache.hasOwnProperty(cacheKey)) {
this.populateVariantColorCache(cacheKey, useExpSprite, battleSpritePath); await this.populateVariantColorCache(cacheKey, useExpSprite, battleSpritePath);
} }
} }
resolve(); resolve();
@ -483,10 +483,10 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
* @param battleSpritePath the filename of the sprite * @param battleSpritePath the filename of the sprite
* @param optionalParams any additional params to log * @param optionalParams any additional params to log
*/ */
fallbackVariantColor(cacheKey: string, attemptedSpritePath: string, useExpSprite: boolean, battleSpritePath: string, ...optionalParams: any[]) { async fallbackVariantColor(cacheKey: string, attemptedSpritePath: string, useExpSprite: boolean, battleSpritePath: string, ...optionalParams: any[]) {
console.warn(`Could not load ${attemptedSpritePath}!`, ...optionalParams); console.warn(`Could not load ${attemptedSpritePath}!`, ...optionalParams);
if (useExpSprite) { if (useExpSprite) {
this.populateVariantColorCache(cacheKey, false, battleSpritePath); await this.populateVariantColorCache(cacheKey, false, battleSpritePath);
} }
} }
@ -497,18 +497,20 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
* @param useExpSprite should the experimental sprite be used * @param useExpSprite should the experimental sprite be used
* @param battleSpritePath the filename of the sprite * @param battleSpritePath the filename of the sprite
*/ */
populateVariantColorCache(cacheKey: string, useExpSprite: boolean, battleSpritePath: string) { async populateVariantColorCache(cacheKey: string, useExpSprite: boolean, battleSpritePath: string) {
const spritePath = `./images/pokemon/variant/${useExpSprite ? "exp/" : ""}${battleSpritePath}.json`; const spritePath = `./images/pokemon/variant/${useExpSprite ? "exp/" : ""}${battleSpritePath}.json`;
this.scene.cachedFetch(spritePath).then(res => { return this.scene.cachedFetch(spritePath).then(res => {
// Prevent the JSON from processing if it failed to load // Prevent the JSON from processing if it failed to load
if (!res.ok) { if (!res.ok) {
return this.fallbackVariantColor(cacheKey, res.url, useExpSprite, battleSpritePath, res.status, res.statusText); return this.fallbackVariantColor(cacheKey, res.url, useExpSprite, battleSpritePath, res.status, res.statusText);
} }
return res.json(); return res.json();
}).catch(error => { }).catch(error => {
this.fallbackVariantColor(cacheKey, spritePath, useExpSprite, battleSpritePath, error); return this.fallbackVariantColor(cacheKey, spritePath, useExpSprite, battleSpritePath, error);
}).then(c => { }).then(c => {
variantColorCache[cacheKey] = c; if (!isNullOrUndefined(c)) {
variantColorCache[cacheKey] = c;
}
}); });
} }

View File

@ -120,13 +120,10 @@ export class MovePhase extends BattlePhase {
console.log(Moves[this.move.moveId]); console.log(Moves[this.move.moveId]);
// Check if move is unusable (e.g. because it's out of PP due to a mid-turn Spite). // Check if move is unusable (e.g. because it's out of PP due to a mid-turn Spite).
if (!this.canMove(true)) { if (!this.canMove(true) && (this.pokemon.isActive(true) || this.move.ppUsed >= this.move.getMovePp())) {
if (this.pokemon.isActive(true) && this.move.ppUsed >= this.move.getMovePp()) { this.fail();
this.fail(); this.showMoveText();
this.showMoveText(); this.showFailedText();
this.showFailedText();
}
return this.end(); return this.end();
} }
@ -378,16 +375,12 @@ export class MovePhase extends BattlePhase {
} else { } else {
this.pokemon.pushMoveHistory({ move: this.move.moveId, targets: this.targets, result: MoveResult.FAIL, virtual: this.move.virtual }); this.pokemon.pushMoveHistory({ move: this.move.moveId, targets: this.targets, result: MoveResult.FAIL, virtual: this.move.virtual });
let failedText: string | undefined;
const failureMessage = move.getFailedText(this.pokemon, targets[0], move, new BooleanHolder(false)); const failureMessage = move.getFailedText(this.pokemon, targets[0], move, new BooleanHolder(false));
if (failureMessage) { if (failureMessage) {
failedText = failureMessage; this.showMoveText();
this.showFailedText(failureMessage);
} }
this.showMoveText();
this.showFailedText(failedText);
// Remove the user from its semi-invulnerable state (if applicable) // Remove the user from its semi-invulnerable state (if applicable)
this.pokemon.lapseTags(BattlerTagLapseType.MOVE_EFFECT); this.pokemon.lapseTags(BattlerTagLapseType.MOVE_EFFECT);
} }

View File

@ -1,14 +1,8 @@
import { BattlerIndex } from "#app/battle";
import { Status } from "#app/data/status-effect"; import { Status } from "#app/data/status-effect";
import { DamagePhase } from "#app/phases/damage-phase";
import { SwitchSummonPhase } from "#app/phases/switch-summon-phase";
import { Mode } from "#app/ui/ui";
import { Abilities } from "#enums/abilities"; import { Abilities } from "#enums/abilities";
import { Moves } from "#enums/moves"; import { Moves } from "#enums/moves";
import { Species } from "#enums/species"; import { Species } from "#enums/species";
import { Stat } from "#enums/stat";
import { StatusEffect } from "#enums/status-effect"; import { StatusEffect } from "#enums/status-effect";
import { SwitchType } from "#enums/switch-type";
import GameManager from "#test/utils/gameManager"; import GameManager from "#test/utils/gameManager";
import Phaser from "phaser"; import Phaser from "phaser";
import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest";
@ -34,78 +28,60 @@ describe("Abilities - ZEN MODE", () => {
game = new GameManager(phaserGame); game = new GameManager(phaserGame);
game.override game.override
.battleType("single") .battleType("single")
.enemySpecies(Species.RATTATA) .disableCrits()
.enemyAbility(Abilities.HYDRATION) .enemySpecies(Species.MAGIKARP)
.enemyAbility(Abilities.BALL_FETCH)
.enemyLevel(5)
.ability(Abilities.ZEN_MODE) .ability(Abilities.ZEN_MODE)
.startingLevel(100)
.moveset(Moves.SPLASH) .moveset(Moves.SPLASH)
.enemyMoveset(Moves.TACKLE); .enemyMoveset(Moves.SEISMIC_TOSS);
}); });
it("shouldn't change form when taking damage if not dropping below 50% HP", async () => { it("shouldn't change form when taking damage if not dropping below 50% HP", async () => {
await game.classicMode.startBattle([ Species.DARMANITAN ]); await game.classicMode.startBattle([ Species.DARMANITAN ]);
const player = game.scene.getPlayerPokemon()!; const darmanitan = game.scene.getPlayerPokemon()!;
player.stats[Stat.HP] = 100; expect(darmanitan.formIndex).toBe(baseForm);
player.hp = 100;
expect(player.formIndex).toBe(baseForm);
game.move.select(Moves.SPLASH); game.move.select(Moves.SPLASH);
await game.setTurnOrder([ BattlerIndex.ENEMY, BattlerIndex.PLAYER ]); await game.toNextTurn();
await game.phaseInterceptor.to("BerryPhase");
expect(player.hp).toBeLessThan(100); expect(darmanitan.getHpRatio()).toBeLessThan(1);
expect(player.formIndex).toBe(baseForm); expect(darmanitan.getHpRatio()).toBeGreaterThan(0.5);
expect(darmanitan.formIndex).toBe(baseForm);
}); });
it("should change form when falling below 50% HP", async () => { it("should change form when falling below 50% HP", async () => {
await game.classicMode.startBattle([ Species.DARMANITAN ]); await game.classicMode.startBattle([ Species.DARMANITAN ]);
const player = game.scene.getPlayerPokemon()!; const darmanitan = game.scene.getPlayerPokemon()!;
player.stats[Stat.HP] = 1000; darmanitan.hp = (darmanitan.getMaxHp() / 2) + 1;
player.hp = 100; expect(darmanitan.formIndex).toBe(baseForm);
expect(player.formIndex).toBe(baseForm);
game.move.select(Moves.SPLASH); game.move.select(Moves.SPLASH);
await game.toNextTurn();
await game.setTurnOrder([ BattlerIndex.ENEMY, BattlerIndex.PLAYER ]); expect(darmanitan.getHpRatio()).toBeLessThan(0.5);
await game.phaseInterceptor.to("QuietFormChangePhase"); expect(darmanitan.formIndex).toBe(zenForm);
await game.phaseInterceptor.to("TurnInitPhase", false);
expect(player.hp).not.toBe(100);
expect(player.formIndex).toBe(zenForm);
}); });
it("should stay zen mode when fainted", async () => { it("should stay zen mode when fainted", async () => {
await game.classicMode.startBattle([ Species.DARMANITAN, Species.CHARIZARD ]); await game.classicMode.startBattle([ Species.DARMANITAN, Species.CHARIZARD ]);
const player = game.scene.getPlayerPokemon()!; const darmanitan = game.scene.getPlayerPokemon()!;
player.stats[Stat.HP] = 1000; darmanitan.hp = (darmanitan.getMaxHp() / 2) + 1;
player.hp = 100; expect(darmanitan.formIndex).toBe(baseForm);
expect(player.formIndex).toBe(baseForm);
game.move.select(Moves.SPLASH); game.move.select(Moves.SPLASH);
await game.toNextTurn();
await game.setTurnOrder([ BattlerIndex.ENEMY, BattlerIndex.PLAYER ]); expect(darmanitan.getHpRatio()).toBeLessThan(0.5);
await game.phaseInterceptor.to(DamagePhase, false); expect(darmanitan.formIndex).toBe(zenForm);
const damagePhase = game.scene.getCurrentPhase() as DamagePhase;
damagePhase.updateAmount(80);
await game.phaseInterceptor.to("QuietFormChangePhase");
expect(player.hp).not.toBe(100); game.move.select(Moves.SPLASH);
expect(player.formIndex).toBe(zenForm); await game.killPokemon(darmanitan);
game.doSelectPartyPokemon(1);
await game.killPokemon(player); await game.toNextTurn();
expect(player.isFainted()).toBe(true);
await game.phaseInterceptor.to("TurnStartPhase");
game.onNextPrompt("SwitchPhase", Mode.PARTY, () => {
game.scene.unshiftPhase(new SwitchSummonPhase(game.scene, SwitchType.SWITCH, 0, 1, false));
game.scene.ui.setMode(Mode.MESSAGE);
});
game.onNextPrompt("SwitchPhase", Mode.MESSAGE, () => {
game.endPhase();
});
await game.phaseInterceptor.to("PostSummonPhase");
expect(darmanitan.isFainted()).toBe(true);
expect(game.scene.getPlayerParty()[1].formIndex).toBe(zenForm); expect(game.scene.getPlayerParty()[1].formIndex).toBe(zenForm);
}); });
@ -117,7 +93,8 @@ describe("Abilities - ZEN MODE", () => {
await game.classicMode.startBattle([ Species.MAGIKARP, Species.DARMANITAN ]); await game.classicMode.startBattle([ Species.MAGIKARP, Species.DARMANITAN ]);
const darmanitan = game.scene.getPlayerParty().find((p) => p.species.speciesId === Species.DARMANITAN)!; const darmanitan = game.scene.getPlayerParty()[1];
darmanitan.hp = 1;
expect(darmanitan.formIndex).toBe(zenForm); expect(darmanitan.formIndex).toBe(zenForm);
darmanitan.hp = 0; darmanitan.hp = 0;
@ -126,9 +103,7 @@ describe("Abilities - ZEN MODE", () => {
game.move.select(Moves.SPLASH); game.move.select(Moves.SPLASH);
await game.doKillOpponents(); await game.doKillOpponents();
await game.phaseInterceptor.to("TurnEndPhase"); await game.toNextWave();
game.doSelectModifier();
await game.phaseInterceptor.to("QuietFormChangePhase");
expect(darmanitan.formIndex).toBe(baseForm); expect(darmanitan.formIndex).toBe(baseForm);
}); });