mirror of
https://github.com/pagefaultgames/pokerogue.git
synced 2025-08-24 16:29:27 +02:00
Merge branch 'beta' of https://github.com/pagefaultgames/pokerogue into balancerefactor
This commit is contained in:
commit
0d40d98de1
43
package-lock.json
generated
43
package-lock.json
generated
@ -37,7 +37,7 @@
|
|||||||
"typedoc": "^0.26.4",
|
"typedoc": "^0.26.4",
|
||||||
"typescript": "^5.5.3",
|
"typescript": "^5.5.3",
|
||||||
"typescript-eslint": "^8.0.0-alpha.54",
|
"typescript-eslint": "^8.0.0-alpha.54",
|
||||||
"vite": "^5.3.5",
|
"vite": "^5.4.8",
|
||||||
"vite-tsconfig-paths": "^4.3.2",
|
"vite-tsconfig-paths": "^4.3.2",
|
||||||
"vitest": "^2.0.4",
|
"vitest": "^2.0.4",
|
||||||
"vitest-canvas-mock": "^0.3.3"
|
"vitest-canvas-mock": "^0.3.3"
|
||||||
@ -3650,7 +3650,6 @@
|
|||||||
"integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==",
|
"integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"hasInstallScript": true,
|
"hasInstallScript": true,
|
||||||
"license": "MIT",
|
|
||||||
"optional": true,
|
"optional": true,
|
||||||
"os": [
|
"os": [
|
||||||
"darwin"
|
"darwin"
|
||||||
@ -5042,7 +5041,6 @@
|
|||||||
"url": "https://github.com/sponsors/ai"
|
"url": "https://github.com/sponsors/ai"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"license": "MIT",
|
|
||||||
"bin": {
|
"bin": {
|
||||||
"nanoid": "bin/nanoid.cjs"
|
"nanoid": "bin/nanoid.cjs"
|
||||||
},
|
},
|
||||||
@ -5441,9 +5439,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/postcss": {
|
"node_modules/postcss": {
|
||||||
"version": "8.4.39",
|
"version": "8.4.47",
|
||||||
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.39.tgz",
|
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.47.tgz",
|
||||||
"integrity": "sha512-0vzE+lAiG7hZl1/9I8yzKLx3aR9Xbof3fBHKunvMfOCYAtMhrsnccJY2iTURb9EZd5+pLuiNV9/c/GZJOHsgIw==",
|
"integrity": "sha512-56rxCq7G/XfB4EkXq9Egn5GCqugWvDFjafDOThIdMBsI15iqPqR5r15TfSr1YPYeEI19YeaXMCbY6u88Y76GLQ==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"funding": [
|
"funding": [
|
||||||
{
|
{
|
||||||
@ -5459,16 +5457,21 @@
|
|||||||
"url": "https://github.com/sponsors/ai"
|
"url": "https://github.com/sponsors/ai"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"license": "MIT",
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"nanoid": "^3.3.7",
|
"nanoid": "^3.3.7",
|
||||||
"picocolors": "^1.0.1",
|
"picocolors": "^1.1.0",
|
||||||
"source-map-js": "^1.2.0"
|
"source-map-js": "^1.2.1"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": "^10 || ^12 || >=14"
|
"node": "^10 || ^12 || >=14"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/postcss/node_modules/picocolors": {
|
||||||
|
"version": "1.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.0.tgz",
|
||||||
|
"integrity": "sha512-TQ92mBOW0l3LeMeyLV6mzy/kWr8lkd/hp3mTg7wYK7zJhuBStmGMBG0BdeDZS/dZx1IukaX6Bk11zcln25o1Aw==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
"node_modules/prelude-ls": {
|
"node_modules/prelude-ls": {
|
||||||
"version": "1.2.1",
|
"version": "1.2.1",
|
||||||
"resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz",
|
"resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz",
|
||||||
@ -5865,11 +5868,10 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/source-map-js": {
|
"node_modules/source-map-js": {
|
||||||
"version": "1.2.0",
|
"version": "1.2.1",
|
||||||
"resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz",
|
||||||
"integrity": "sha512-itJW8lvSA0TXEphiRoawsCksnlf8SyvmFzIhltqAHluXd88pkCd+cXJVHTDwdCr0IzwptSm035IHQktUu1QUMg==",
|
"integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "BSD-3-Clause",
|
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=0.10.0"
|
"node": ">=0.10.0"
|
||||||
}
|
}
|
||||||
@ -6469,15 +6471,14 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/vite": {
|
"node_modules/vite": {
|
||||||
"version": "5.3.5",
|
"version": "5.4.8",
|
||||||
"resolved": "https://registry.npmjs.org/vite/-/vite-5.3.5.tgz",
|
"resolved": "https://registry.npmjs.org/vite/-/vite-5.4.8.tgz",
|
||||||
"integrity": "sha512-MdjglKR6AQXQb9JGiS7Rc2wC6uMjcm7Go/NHNO63EwiJXfuk9PgqiP/n5IDJCziMkfw9n4Ubp7lttNwz+8ZVKA==",
|
"integrity": "sha512-FqrItQ4DT1NC4zCUqMB4c4AZORMKIa0m8/URVCZ77OZ/QSNeJ54bU1vrFADbDsuwfIPcgknRkmqakQcgnL4GiQ==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"esbuild": "^0.21.3",
|
"esbuild": "^0.21.3",
|
||||||
"postcss": "^8.4.39",
|
"postcss": "^8.4.43",
|
||||||
"rollup": "^4.13.0"
|
"rollup": "^4.20.0"
|
||||||
},
|
},
|
||||||
"bin": {
|
"bin": {
|
||||||
"vite": "bin/vite.js"
|
"vite": "bin/vite.js"
|
||||||
@ -6496,6 +6497,7 @@
|
|||||||
"less": "*",
|
"less": "*",
|
||||||
"lightningcss": "^1.21.0",
|
"lightningcss": "^1.21.0",
|
||||||
"sass": "*",
|
"sass": "*",
|
||||||
|
"sass-embedded": "*",
|
||||||
"stylus": "*",
|
"stylus": "*",
|
||||||
"sugarss": "*",
|
"sugarss": "*",
|
||||||
"terser": "^5.4.0"
|
"terser": "^5.4.0"
|
||||||
@ -6513,6 +6515,9 @@
|
|||||||
"sass": {
|
"sass": {
|
||||||
"optional": true
|
"optional": true
|
||||||
},
|
},
|
||||||
|
"sass-embedded": {
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
"stylus": {
|
"stylus": {
|
||||||
"optional": true
|
"optional": true
|
||||||
},
|
},
|
||||||
|
@ -41,7 +41,7 @@
|
|||||||
"typedoc": "^0.26.4",
|
"typedoc": "^0.26.4",
|
||||||
"typescript": "^5.5.3",
|
"typescript": "^5.5.3",
|
||||||
"typescript-eslint": "^8.0.0-alpha.54",
|
"typescript-eslint": "^8.0.0-alpha.54",
|
||||||
"vite": "^5.3.5",
|
"vite": "^5.4.8",
|
||||||
"vite-tsconfig-paths": "^4.3.2",
|
"vite-tsconfig-paths": "^4.3.2",
|
||||||
"vitest": "^2.0.4",
|
"vitest": "^2.0.4",
|
||||||
"vitest-canvas-mock": "^0.3.3"
|
"vitest-canvas-mock": "^0.3.3"
|
||||||
|
@ -1040,10 +1040,6 @@ export default class BattleScene extends SceneBase {
|
|||||||
|
|
||||||
this.gameMode = getGameMode(GameModes.CLASSIC);
|
this.gameMode = getGameMode(GameModes.CLASSIC);
|
||||||
|
|
||||||
this.setSeed(Overrides.SEED_OVERRIDE || Utils.randomString(24));
|
|
||||||
console.log("Seed:", this.seed);
|
|
||||||
this.resetSeed(); // Properly resets RNG after saving and quitting a session
|
|
||||||
|
|
||||||
this.disableMenu = false;
|
this.disableMenu = false;
|
||||||
|
|
||||||
this.score = 0;
|
this.score = 0;
|
||||||
@ -1078,6 +1074,12 @@ export default class BattleScene extends SceneBase {
|
|||||||
//@ts-ignore - allowing `null` for currentBattle causes a lot of trouble
|
//@ts-ignore - allowing `null` for currentBattle causes a lot of trouble
|
||||||
this.currentBattle = null; // TODO: resolve ts-ignore
|
this.currentBattle = null; // TODO: resolve ts-ignore
|
||||||
|
|
||||||
|
// Reset RNG after end of game or save & quit.
|
||||||
|
// This needs to happen after clearing this.currentBattle or the seed will be affected by the last wave played
|
||||||
|
this.setSeed(Overrides.SEED_OVERRIDE || Utils.randomString(24));
|
||||||
|
console.log("Seed:", this.seed);
|
||||||
|
this.resetSeed();
|
||||||
|
|
||||||
this.biomeWaveText.setText(startingWave.toString());
|
this.biomeWaveText.setText(startingWave.toString());
|
||||||
this.biomeWaveText.setVisible(false);
|
this.biomeWaveText.setVisible(false);
|
||||||
|
|
||||||
|
@ -497,7 +497,7 @@ function getRandomTrainerFunc(trainerPool: (TrainerType | TrainerType[])[], rand
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* 1/3 chance for evil team grunts to be double battles */
|
/* 1/3 chance for evil team grunts to be double battles */
|
||||||
const evilTeamGrunts = [TrainerType.ROCKET_GRUNT, TrainerType.MAGMA_GRUNT, TrainerType.AQUA_GRUNT, TrainerType.GALACTIC_GRUNT, TrainerType.PLASMA_GRUNT, TrainerType.FLARE_GRUNT];
|
const evilTeamGrunts = [TrainerType.ROCKET_GRUNT, TrainerType.MAGMA_GRUNT, TrainerType.AQUA_GRUNT, TrainerType.GALACTIC_GRUNT, TrainerType.PLASMA_GRUNT, TrainerType.FLARE_GRUNT, TrainerType.AETHER_GRUNT, TrainerType.SKULL_GRUNT, TrainerType.MACRO_GRUNT, TrainerType.STAR_GRUNT];
|
||||||
const isEvilTeamGrunt = evilTeamGrunts.includes(trainerTypes[rand]);
|
const isEvilTeamGrunt = evilTeamGrunts.includes(trainerTypes[rand]);
|
||||||
|
|
||||||
if (trainerConfigs[trainerTypes[rand]].hasDouble && isEvilTeamGrunt) {
|
if (trainerConfigs[trainerTypes[rand]].hasDouble && isEvilTeamGrunt) {
|
||||||
|
@ -4,7 +4,7 @@ import { Constructor } from "#app/utils";
|
|||||||
import * as Utils from "../utils";
|
import * as Utils from "../utils";
|
||||||
import { getPokemonNameWithAffix } from "../messages";
|
import { getPokemonNameWithAffix } from "../messages";
|
||||||
import { Weather, WeatherType } from "./weather";
|
import { Weather, WeatherType } from "./weather";
|
||||||
import { BattlerTag, GroundedTag, GulpMissileTag, SemiInvulnerableTag } from "./battler-tags";
|
import { BattlerTag, GroundedTag } from "./battler-tags";
|
||||||
import { StatusEffect, getNonVolatileStatusEffects, getStatusEffectDescriptor, getStatusEffectHealText } from "./status-effect";
|
import { StatusEffect, getNonVolatileStatusEffects, getStatusEffectDescriptor, getStatusEffectHealText } from "./status-effect";
|
||||||
import { Gender } from "./gender";
|
import { Gender } from "./gender";
|
||||||
import Move, { AttackMove, MoveCategory, MoveFlags, MoveTarget, FlinchAttr, OneHitKOAttr, HitHealAttr, allMoves, StatusMove, SelfStatusMove, VariablePowerAttr, applyMoveAttrs, IncrementMovePriorityAttr, VariableMoveTypeAttr, RandomMovesetMoveAttr, RandomMoveAttr, NaturePowerAttr, CopyMoveAttr, MoveAttr, MultiHitAttr, ChargeAttr, SacrificialAttr, SacrificialAttrOnHit, NeutralDamageAgainstFlyingTypeMultiplierAttr, FixedDamageAttr } from "./move";
|
import Move, { AttackMove, MoveCategory, MoveFlags, MoveTarget, FlinchAttr, OneHitKOAttr, HitHealAttr, allMoves, StatusMove, SelfStatusMove, VariablePowerAttr, applyMoveAttrs, IncrementMovePriorityAttr, VariableMoveTypeAttr, RandomMovesetMoveAttr, RandomMoveAttr, NaturePowerAttr, CopyMoveAttr, MoveAttr, MultiHitAttr, ChargeAttr, SacrificialAttr, SacrificialAttrOnHit, NeutralDamageAgainstFlyingTypeMultiplierAttr, FixedDamageAttr } from "./move";
|
||||||
@ -536,53 +536,6 @@ export class PostDefendAbAttr extends AbAttr {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Applies the effects of Gulp Missile when the user is hit by an attack.
|
|
||||||
* @extends PostDefendAbAttr
|
|
||||||
*/
|
|
||||||
export class PostDefendGulpMissileAbAttr extends PostDefendAbAttr {
|
|
||||||
constructor() {
|
|
||||||
super(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Damages the attacker and triggers the secondary effect based on the form or the BattlerTagType.
|
|
||||||
* @param {Pokemon} pokemon - The defending Pokemon.
|
|
||||||
* @param passive - n/a
|
|
||||||
* @param {Pokemon} attacker - The attacking Pokemon.
|
|
||||||
* @param {Move} move - The move being used.
|
|
||||||
* @param {HitResult} hitResult - n/a
|
|
||||||
* @param {any[]} args - n/a
|
|
||||||
* @returns Whether the effects of the ability are applied.
|
|
||||||
*/
|
|
||||||
applyPostDefend(pokemon: Pokemon, passive: boolean, simulated: boolean, attacker: Pokemon, move: Move, hitResult: HitResult, args: any[]): boolean | Promise<boolean> {
|
|
||||||
const battlerTag = pokemon.getTag(GulpMissileTag);
|
|
||||||
if (!battlerTag || move.category === MoveCategory.STATUS || pokemon.getTag(SemiInvulnerableTag)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (simulated) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
const cancelled = new Utils.BooleanHolder(false);
|
|
||||||
applyAbAttrs(BlockNonDirectDamageAbAttr, attacker, cancelled);
|
|
||||||
|
|
||||||
if (!cancelled.value) {
|
|
||||||
attacker.damageAndUpdate(Math.max(1, Math.floor(attacker.getMaxHp() / 4)), HitResult.OTHER);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (battlerTag.tagType === BattlerTagType.GULP_MISSILE_ARROKUDA) {
|
|
||||||
pokemon.scene.unshiftPhase(new StatStageChangePhase(pokemon.scene, attacker.getBattlerIndex(), false, [ Stat.DEF ], -1));
|
|
||||||
} else {
|
|
||||||
attacker.trySetStatus(StatusEffect.PARALYSIS, true, pokemon);
|
|
||||||
}
|
|
||||||
|
|
||||||
pokemon.removeTag(battlerTag.tagType);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export class FieldPriorityMoveImmunityAbAttr extends PreDefendAbAttr {
|
export class FieldPriorityMoveImmunityAbAttr extends PreDefendAbAttr {
|
||||||
applyPreDefend(pokemon: Pokemon, passive: boolean, simulated: boolean, attacker: Pokemon, move: Move, cancelled: Utils.BooleanHolder, args: any[]): boolean {
|
applyPreDefend(pokemon: Pokemon, passive: boolean, simulated: boolean, attacker: Pokemon, move: Move, cancelled: Utils.BooleanHolder, args: any[]): boolean {
|
||||||
const attackPriority = new Utils.IntegerHolder(move.priority);
|
const attackPriority = new Utils.IntegerHolder(move.priority);
|
||||||
@ -5210,8 +5163,7 @@ export function initAbilities() {
|
|||||||
.attr(PostSummonMessageAbAttr, (pokemon: Pokemon) => i18next.t("abilityTriggers:postSummonMoldBreaker", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon) }))
|
.attr(PostSummonMessageAbAttr, (pokemon: Pokemon) => i18next.t("abilityTriggers:postSummonMoldBreaker", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon) }))
|
||||||
.attr(MoveAbilityBypassAbAttr),
|
.attr(MoveAbilityBypassAbAttr),
|
||||||
new Ability(Abilities.SUPER_LUCK, 4)
|
new Ability(Abilities.SUPER_LUCK, 4)
|
||||||
.attr(BonusCritAbAttr)
|
.attr(BonusCritAbAttr),
|
||||||
.partial(),
|
|
||||||
new Ability(Abilities.AFTERMATH, 4)
|
new Ability(Abilities.AFTERMATH, 4)
|
||||||
.attr(PostFaintContactDamageAbAttr, 4)
|
.attr(PostFaintContactDamageAbAttr, 4)
|
||||||
.bypassFaint(),
|
.bypassFaint(),
|
||||||
@ -5669,13 +5621,19 @@ export function initAbilities() {
|
|||||||
new Ability(Abilities.MIRROR_ARMOR, 8)
|
new Ability(Abilities.MIRROR_ARMOR, 8)
|
||||||
.ignorable()
|
.ignorable()
|
||||||
.unimplemented(),
|
.unimplemented(),
|
||||||
|
/**
|
||||||
|
* Right now, the logic is attached to Surf and Dive moves. Ideally, the post-defend/hit should be an
|
||||||
|
* ability attribute but the current implementation of move effects for BattlerTag does not support this- in the case
|
||||||
|
* where Cramorant is fainted.
|
||||||
|
* @see {@linkcode GulpMissileTagAttr} and {@linkcode GulpMissileTag} for Gulp Missile implementation
|
||||||
|
*/
|
||||||
new Ability(Abilities.GULP_MISSILE, 8)
|
new Ability(Abilities.GULP_MISSILE, 8)
|
||||||
.attr(UnsuppressableAbilityAbAttr)
|
.attr(UnsuppressableAbilityAbAttr)
|
||||||
.attr(NoTransformAbilityAbAttr)
|
.attr(NoTransformAbilityAbAttr)
|
||||||
.attr(NoFusionAbilityAbAttr)
|
.attr(NoFusionAbilityAbAttr)
|
||||||
.attr(UncopiableAbilityAbAttr)
|
.attr(UncopiableAbilityAbAttr)
|
||||||
.attr(UnswappableAbilityAbAttr)
|
.attr(UnswappableAbilityAbAttr)
|
||||||
.attr(PostDefendGulpMissileAbAttr),
|
.bypassFaint(),
|
||||||
new Ability(Abilities.STALWART, 8)
|
new Ability(Abilities.STALWART, 8)
|
||||||
.attr(BlockRedirectAbAttr),
|
.attr(BlockRedirectAbAttr),
|
||||||
new Ability(Abilities.STEAM_ENGINE, 8)
|
new Ability(Abilities.STEAM_ENGINE, 8)
|
||||||
|
@ -511,6 +511,39 @@ class WaterSportTag extends WeakenMoveTypeTag {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Arena Tag class for the secondary effect of {@link https://bulbapedia.bulbagarden.net/wiki/Plasma_Fists_(move) | Plasma Fists}.
|
||||||
|
* Converts Normal-type moves to Electric type for the rest of the turn.
|
||||||
|
*/
|
||||||
|
export class PlasmaFistsTag extends ArenaTag {
|
||||||
|
constructor() {
|
||||||
|
super(ArenaTagType.PLASMA_FISTS, 1, Moves.PLASMA_FISTS);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Queues Plasma Fists' on-add message */
|
||||||
|
onAdd(arena: Arena): void {
|
||||||
|
arena.scene.queueMessage(i18next.t("arenaTag:plasmaFistsOnAdd"));
|
||||||
|
}
|
||||||
|
|
||||||
|
onRemove(arena: Arena): void { } // Removes default on-remove message
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Converts Normal-type moves to Electric type
|
||||||
|
* @param arena n/a
|
||||||
|
* @param args
|
||||||
|
* - `[0]` {@linkcode Utils.NumberHolder} A container with a move's {@linkcode Type}
|
||||||
|
* @returns `true` if the given move type changed; `false` otherwise.
|
||||||
|
*/
|
||||||
|
apply(arena: Arena, args: any[]): boolean {
|
||||||
|
const moveType = args[0];
|
||||||
|
if (moveType instanceof Utils.NumberHolder && moveType.value === Type.NORMAL) {
|
||||||
|
moveType.value = Type.ELECTRIC;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Abstract class to implement arena traps.
|
* Abstract class to implement arena traps.
|
||||||
*/
|
*/
|
||||||
@ -1010,6 +1043,8 @@ export function getArenaTag(tagType: ArenaTagType, turnCount: integer, sourceMov
|
|||||||
return new MudSportTag(turnCount, sourceId);
|
return new MudSportTag(turnCount, sourceId);
|
||||||
case ArenaTagType.WATER_SPORT:
|
case ArenaTagType.WATER_SPORT:
|
||||||
return new WaterSportTag(turnCount, sourceId);
|
return new WaterSportTag(turnCount, sourceId);
|
||||||
|
case ArenaTagType.PLASMA_FISTS:
|
||||||
|
return new PlasmaFistsTag();
|
||||||
case ArenaTagType.SPIKES:
|
case ArenaTagType.SPIKES:
|
||||||
return new SpikesTag(sourceId, side);
|
return new SpikesTag(sourceId, side);
|
||||||
case ArenaTagType.TOXIC_SPIKES:
|
case ArenaTagType.TOXIC_SPIKES:
|
||||||
|
@ -2123,7 +2123,36 @@ export class StockpilingTag extends BattlerTag {
|
|||||||
*/
|
*/
|
||||||
export class GulpMissileTag extends BattlerTag {
|
export class GulpMissileTag extends BattlerTag {
|
||||||
constructor(tagType: BattlerTagType, sourceMove: Moves) {
|
constructor(tagType: BattlerTagType, sourceMove: Moves) {
|
||||||
super(tagType, BattlerTagLapseType.CUSTOM, 0, sourceMove);
|
super(tagType, BattlerTagLapseType.HIT, 0, sourceMove);
|
||||||
|
}
|
||||||
|
|
||||||
|
override lapse(pokemon: Pokemon, lapseType: BattlerTagLapseType): boolean {
|
||||||
|
if (pokemon.getTag(BattlerTagType.UNDERWATER)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
const moveEffectPhase = pokemon.scene.getCurrentPhase();
|
||||||
|
if (moveEffectPhase instanceof MoveEffectPhase) {
|
||||||
|
const attacker = moveEffectPhase.getUserPokemon();
|
||||||
|
|
||||||
|
if (!attacker) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const cancelled = new Utils.BooleanHolder(false);
|
||||||
|
applyAbAttrs(BlockNonDirectDamageAbAttr, attacker, cancelled);
|
||||||
|
|
||||||
|
if (!cancelled.value) {
|
||||||
|
attacker.damageAndUpdate(Math.max(1, Math.floor(attacker.getMaxHp() / 4)), HitResult.OTHER);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.tagType === BattlerTagType.GULP_MISSILE_ARROKUDA) {
|
||||||
|
pokemon.scene.unshiftPhase(new StatStageChangePhase(pokemon.scene, attacker.getBattlerIndex(), false, [ Stat.DEF ], -1));
|
||||||
|
} else {
|
||||||
|
attacker.trySetStatus(StatusEffect.PARALYSIS, true, pokemon);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -2589,6 +2618,43 @@ export class ImprisonTag extends MoveRestrictionBattlerTag {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Battler Tag that applies the effects of Syrup Bomb to the target Pokemon.
|
||||||
|
* For three turns, starting from the turn of hit, at the end of each turn, the target Pokemon's speed will decrease by 1.
|
||||||
|
* The tag can also expire by taking the target Pokemon off the field.
|
||||||
|
*/
|
||||||
|
export class SyrupBombTag extends BattlerTag {
|
||||||
|
constructor() {
|
||||||
|
super(BattlerTagType.SYRUP_BOMB, BattlerTagLapseType.TURN_END, 3, Moves.SYRUP_BOMB);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds the Syrup Bomb battler tag to the target Pokemon.
|
||||||
|
* @param {Pokemon} pokemon the target Pokemon
|
||||||
|
*/
|
||||||
|
override onAdd(pokemon: Pokemon) {
|
||||||
|
super.onAdd(pokemon);
|
||||||
|
pokemon.scene.queueMessage(i18next.t("battlerTags:syrupBombOnAdd", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon) }));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Applies the single-stage speed down to the target Pokemon and decrements the tag's turn count
|
||||||
|
* @param {Pokemon} pokemon the target Pokemon
|
||||||
|
* @param {BattlerTagLapseType} _lapseType
|
||||||
|
* @returns `true` if the turnCount is still greater than 0 | `false` if the turnCount is 0 or the target Pokemon has been removed from the field
|
||||||
|
*/
|
||||||
|
override lapse(pokemon: Pokemon, _lapseType: BattlerTagLapseType): boolean {
|
||||||
|
if (!pokemon.isActive(true)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
pokemon.scene.queueMessage(i18next.t("battlerTags:syrupBombLapse", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon) })); // Custom message in lieu of an animation in mainline
|
||||||
|
pokemon.scene.unshiftPhase(new StatStageChangePhase(
|
||||||
|
pokemon.scene, pokemon.getBattlerIndex(), true,
|
||||||
|
[Stat.SPD], -1, true, false, true
|
||||||
|
));
|
||||||
|
return --this.turnCount > 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Retrieves a {@linkcode BattlerTag} based on the provided tag type, turn count, source move, and source ID.
|
* Retrieves a {@linkcode BattlerTag} based on the provided tag type, turn count, source move, and source ID.
|
||||||
@ -2763,6 +2829,8 @@ export function getBattlerTag(tagType: BattlerTagType, turnCount: number, source
|
|||||||
return new TauntTag();
|
return new TauntTag();
|
||||||
case BattlerTagType.IMPRISON:
|
case BattlerTagType.IMPRISON:
|
||||||
return new ImprisonTag(sourceId);
|
return new ImprisonTag(sourceId);
|
||||||
|
case BattlerTagType.SYRUP_BOMB:
|
||||||
|
return new SyrupBombTag();
|
||||||
case BattlerTagType.NONE:
|
case BattlerTagType.NONE:
|
||||||
default:
|
default:
|
||||||
return new BattlerTag(tagType, BattlerTagLapseType.CUSTOM, turnCount, sourceMove, sourceId);
|
return new BattlerTag(tagType, BattlerTagLapseType.CUSTOM, turnCount, sourceMove, sourceId);
|
||||||
|
@ -4544,6 +4544,7 @@ export class AddBattlerTagAttr extends MoveEffectAttr {
|
|||||||
case BattlerTagType.DROWSY:
|
case BattlerTagType.DROWSY:
|
||||||
case BattlerTagType.DISABLED:
|
case BattlerTagType.DISABLED:
|
||||||
case BattlerTagType.HEAL_BLOCK:
|
case BattlerTagType.HEAL_BLOCK:
|
||||||
|
case BattlerTagType.RECEIVE_DOUBLE_DAMAGE:
|
||||||
return -5;
|
return -5;
|
||||||
case BattlerTagType.SEEDED:
|
case BattlerTagType.SEEDED:
|
||||||
case BattlerTagType.SALT_CURED:
|
case BattlerTagType.SALT_CURED:
|
||||||
@ -4564,6 +4565,7 @@ export class AddBattlerTagAttr extends MoveEffectAttr {
|
|||||||
case BattlerTagType.ENCORE:
|
case BattlerTagType.ENCORE:
|
||||||
return -2;
|
return -2;
|
||||||
case BattlerTagType.MINIMIZED:
|
case BattlerTagType.MINIMIZED:
|
||||||
|
case BattlerTagType.ALWAYS_GET_HIT:
|
||||||
return 0;
|
return 0;
|
||||||
case BattlerTagType.INGRAIN:
|
case BattlerTagType.INGRAIN:
|
||||||
case BattlerTagType.IGNORE_ACCURACY:
|
case BattlerTagType.IGNORE_ACCURACY:
|
||||||
@ -6589,7 +6591,7 @@ export class MoveCondition {
|
|||||||
|
|
||||||
export class FirstMoveCondition extends MoveCondition {
|
export class FirstMoveCondition extends MoveCondition {
|
||||||
constructor() {
|
constructor() {
|
||||||
super((user, target, move) => user.battleSummonData?.turnCount === 1);
|
super((user, target, move) => user.battleSummonData?.waveTurnCount === 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
getUserBenefitScore(user: Pokemon, target: Pokemon, move: Move): integer {
|
getUserBenefitScore(user: Pokemon, target: Pokemon, move: Move): integer {
|
||||||
@ -8906,8 +8908,8 @@ export function initMoves() {
|
|||||||
.attr(HalfSacrificialAttr)
|
.attr(HalfSacrificialAttr)
|
||||||
.target(MoveTarget.ALL_NEAR_OTHERS),
|
.target(MoveTarget.ALL_NEAR_OTHERS),
|
||||||
new AttackMove(Moves.PLASMA_FISTS, Type.ELECTRIC, MoveCategory.PHYSICAL, 100, 100, 15, -1, 0, 7)
|
new AttackMove(Moves.PLASMA_FISTS, Type.ELECTRIC, MoveCategory.PHYSICAL, 100, 100, 15, -1, 0, 7)
|
||||||
.punchingMove()
|
.attr(AddArenaTagAttr, ArenaTagType.PLASMA_FISTS, 1)
|
||||||
.partial(),
|
.punchingMove(),
|
||||||
new AttackMove(Moves.PHOTON_GEYSER, Type.PSYCHIC, MoveCategory.SPECIAL, 100, 100, 5, -1, 0, 7)
|
new AttackMove(Moves.PHOTON_GEYSER, Type.PSYCHIC, MoveCategory.SPECIAL, 100, 100, 5, -1, 0, 7)
|
||||||
.attr(PhotonGeyserCategoryAttr)
|
.attr(PhotonGeyserCategoryAttr)
|
||||||
.ignoresAbilities()
|
.ignoresAbilities()
|
||||||
@ -8937,7 +8939,7 @@ export function initMoves() {
|
|||||||
.partial()
|
.partial()
|
||||||
.ignoresVirtual(),
|
.ignoresVirtual(),
|
||||||
/* End Unused */
|
/* End Unused */
|
||||||
new AttackMove(Moves.ZIPPY_ZAP, Type.ELECTRIC, MoveCategory.PHYSICAL, 50, 100, 15, 100, 2, 7) //LGPE Implementation
|
new AttackMove(Moves.ZIPPY_ZAP, Type.ELECTRIC, MoveCategory.PHYSICAL, 50, 100, 15, -1, 2, 7) //LGPE Implementation
|
||||||
.attr(CritOnlyAttr),
|
.attr(CritOnlyAttr),
|
||||||
new AttackMove(Moves.SPLISHY_SPLASH, Type.WATER, MoveCategory.SPECIAL, 90, 100, 15, 30, 0, 7)
|
new AttackMove(Moves.SPLISHY_SPLASH, Type.WATER, MoveCategory.SPECIAL, 90, 100, 15, 30, 0, 7)
|
||||||
.attr(StatusEffectAttr, StatusEffect.PARALYSIS)
|
.attr(StatusEffectAttr, StatusEffect.PARALYSIS)
|
||||||
@ -9622,9 +9624,8 @@ export function initMoves() {
|
|||||||
.target(MoveTarget.ALL_NEAR_ENEMIES)
|
.target(MoveTarget.ALL_NEAR_ENEMIES)
|
||||||
.triageMove(),
|
.triageMove(),
|
||||||
new AttackMove(Moves.SYRUP_BOMB, Type.GRASS, MoveCategory.SPECIAL, 60, 85, 10, -1, 0, 9)
|
new AttackMove(Moves.SYRUP_BOMB, Type.GRASS, MoveCategory.SPECIAL, 60, 85, 10, -1, 0, 9)
|
||||||
.attr(StatStageChangeAttr, [ Stat.SPD ], -1) //Temporary
|
.attr(AddBattlerTagAttr, BattlerTagType.SYRUP_BOMB, false, false, 3)
|
||||||
.ballBombMove()
|
.ballBombMove(),
|
||||||
.partial(),
|
|
||||||
new AttackMove(Moves.IVY_CUDGEL, Type.GRASS, MoveCategory.PHYSICAL, 100, 100, 10, -1, 0, 9)
|
new AttackMove(Moves.IVY_CUDGEL, Type.GRASS, MoveCategory.PHYSICAL, 100, 100, 10, -1, 0, 9)
|
||||||
.attr(IvyCudgelTypeAttr)
|
.attr(IvyCudgelTypeAttr)
|
||||||
.attr(HighCritAttr)
|
.attr(HighCritAttr)
|
||||||
|
@ -25,4 +25,5 @@ export enum ArenaTagType {
|
|||||||
SAFEGUARD = "SAFEGUARD",
|
SAFEGUARD = "SAFEGUARD",
|
||||||
NO_CRIT = "NO_CRIT",
|
NO_CRIT = "NO_CRIT",
|
||||||
IMPRISON = "IMPRISON",
|
IMPRISON = "IMPRISON",
|
||||||
|
PLASMA_FISTS = "PLASMA_FISTS",
|
||||||
}
|
}
|
||||||
|
@ -85,4 +85,5 @@ export enum BattlerTagType {
|
|||||||
TORMENT = "TORMENT",
|
TORMENT = "TORMENT",
|
||||||
TAUNT = "TAUNT",
|
TAUNT = "TAUNT",
|
||||||
IMPRISON = "IMPRISON",
|
IMPRISON = "IMPRISON",
|
||||||
|
SYRUP_BOMB = "SYRUP_BOMB",
|
||||||
}
|
}
|
||||||
|
@ -65,6 +65,12 @@ import { MysteryEncounterPokemonData } from "#app/data/mystery-encounters/myster
|
|||||||
import { SwitchType } from "#enums/switch-type";
|
import { SwitchType } from "#enums/switch-type";
|
||||||
import { BASE_HIDDEN_ABILITY_CHANCE, BASE_SHINY_CHANCE } from "#app/data/balance/rates";
|
import { BASE_HIDDEN_ABILITY_CHANCE, BASE_SHINY_CHANCE } from "#app/data/balance/rates";
|
||||||
|
|
||||||
|
/** `64/65536 -> 1/1024` */
|
||||||
|
const BASE_SHINY_CHANCE = 64;
|
||||||
|
|
||||||
|
/** `1/256` */
|
||||||
|
const BASE_HIDDEN_ABILITY_CHANCE = 256;
|
||||||
|
|
||||||
export enum FieldPosition {
|
export enum FieldPosition {
|
||||||
CENTER,
|
CENTER,
|
||||||
LEFT,
|
LEFT,
|
||||||
@ -1514,6 +1520,8 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
|||||||
applyMoveAttrs(VariableMoveTypeAttr, this, null, move, moveTypeHolder);
|
applyMoveAttrs(VariableMoveTypeAttr, this, null, move, moveTypeHolder);
|
||||||
applyPreAttackAbAttrs(MoveTypeChangeAbAttr, this, null, move, simulated, moveTypeHolder);
|
applyPreAttackAbAttrs(MoveTypeChangeAbAttr, this, null, move, simulated, moveTypeHolder);
|
||||||
|
|
||||||
|
this.scene.arena.applyTags(ArenaTagType.PLASMA_FISTS, moveTypeHolder);
|
||||||
|
|
||||||
return moveTypeHolder.value as Type;
|
return moveTypeHolder.value as Type;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -3977,7 +3985,8 @@ export class PlayerPokemon extends Pokemon {
|
|||||||
let compatible = false;
|
let compatible = false;
|
||||||
for (const p of tmSpecies[tm]) {
|
for (const p of tmSpecies[tm]) {
|
||||||
if (Array.isArray(p)) {
|
if (Array.isArray(p)) {
|
||||||
if (p[0] === this.species.speciesId || (this.fusionSpecies && p[0] === this.fusionSpecies.speciesId) && p.slice(1).indexOf(this.species.forms[this.formIndex]) > -1) {
|
const [pkm, form] = p;
|
||||||
|
if ((pkm === this.species.speciesId || this.fusionSpecies && pkm === this.fusionSpecies.speciesId) && form === this.getFormKey()) {
|
||||||
compatible = true;
|
compatible = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -4993,6 +5002,8 @@ export class PokemonBattleData {
|
|||||||
export class PokemonBattleSummonData {
|
export class PokemonBattleSummonData {
|
||||||
/** The number of turns the pokemon has passed since entering the battle */
|
/** The number of turns the pokemon has passed since entering the battle */
|
||||||
public turnCount: number = 1;
|
public turnCount: number = 1;
|
||||||
|
/** The number of turns the pokemon has passed since the start of the wave */
|
||||||
|
public waveTurnCount: number = 1;
|
||||||
/** The list of moves the pokemon has used since entering the battle */
|
/** The list of moves the pokemon has used since entering the battle */
|
||||||
public moveHistory: TurnMove[] = [];
|
public moveHistory: TurnMove[] = [];
|
||||||
}
|
}
|
||||||
|
@ -28,6 +28,7 @@
|
|||||||
"mudSportOnRemove": "Lehmsuhler hört auf zu wirken!",
|
"mudSportOnRemove": "Lehmsuhler hört auf zu wirken!",
|
||||||
"waterSportOnAdd": "Die Stärke aller Feuer-Attacken wurde reduziert!",
|
"waterSportOnAdd": "Die Stärke aller Feuer-Attacken wurde reduziert!",
|
||||||
"waterSportOnRemove": "Nassmacher hört auf zu wirken!",
|
"waterSportOnRemove": "Nassmacher hört auf zu wirken!",
|
||||||
|
"plasmaFistsOnAdd": "Ein elektrisch geladener Niederschlag regnet auf das Kampffeld herab!",
|
||||||
"spikesOnAdd": "Die {{opponentDesc}} sind von Stacheln umgeben!",
|
"spikesOnAdd": "Die {{opponentDesc}} sind von Stacheln umgeben!",
|
||||||
"spikesActivateTrap": "Die {{pokemonNameWithAffix}} wurde durch Stachler verletzt!!",
|
"spikesActivateTrap": "Die {{pokemonNameWithAffix}} wurde durch Stachler verletzt!!",
|
||||||
"toxicSpikesOnAdd": "Die {{opponentDesc}} sind überall von giftigen Stacheln umgeben",
|
"toxicSpikesOnAdd": "Die {{opponentDesc}} sind überall von giftigen Stacheln umgeben",
|
||||||
|
@ -28,6 +28,7 @@
|
|||||||
"mudSportOnRemove": "The effects of Mud Sport\nhave faded.",
|
"mudSportOnRemove": "The effects of Mud Sport\nhave faded.",
|
||||||
"waterSportOnAdd": "Fire's power was weakened!",
|
"waterSportOnAdd": "Fire's power was weakened!",
|
||||||
"waterSportOnRemove": "The effects of Water Sport\nhave faded.",
|
"waterSportOnRemove": "The effects of Water Sport\nhave faded.",
|
||||||
|
"plasmaFistsOnAdd": "A deluge of ions showers the battlefield!",
|
||||||
"spikesOnAdd": "{{moveName}} were scattered\nall around {{opponentDesc}}'s feet!",
|
"spikesOnAdd": "{{moveName}} were scattered\nall around {{opponentDesc}}'s feet!",
|
||||||
"spikesActivateTrap": "{{pokemonNameWithAffix}} is hurt\nby the spikes!",
|
"spikesActivateTrap": "{{pokemonNameWithAffix}} is hurt\nby the spikes!",
|
||||||
"toxicSpikesOnAdd": "{{moveName}} were scattered\nall around {{opponentDesc}}'s feet!",
|
"toxicSpikesOnAdd": "{{moveName}} were scattered\nall around {{opponentDesc}}'s feet!",
|
||||||
|
@ -78,5 +78,7 @@
|
|||||||
"tormentOnAdd": "{{pokemonNameWithAffix}} was subjected to torment!",
|
"tormentOnAdd": "{{pokemonNameWithAffix}} was subjected to torment!",
|
||||||
"tauntOnAdd": "{{pokemonNameWithAffix}} fell for the taunt!",
|
"tauntOnAdd": "{{pokemonNameWithAffix}} fell for the taunt!",
|
||||||
"imprisonOnAdd": "{{pokemonNameWithAffix}} sealed the opponents move(s)!",
|
"imprisonOnAdd": "{{pokemonNameWithAffix}} sealed the opponents move(s)!",
|
||||||
"autotomizeOnAdd": "{{pokemonNameWithAffix}} became nimble!"
|
"autotomizeOnAdd": "{{pokemonNameWithAffix}} became nimble!",
|
||||||
|
"syrupBombOnAdd": "{{pokemonNameWithAffix}} got covered in sticky, candy syrup!",
|
||||||
|
"syrupBombLapse": "The sticky syrup slowed down {{pokemonNameWithAffix}}!"
|
||||||
}
|
}
|
||||||
|
@ -28,6 +28,7 @@
|
|||||||
"mudSportOnRemove": "Chapoteo Lodo ha dejado de surtir efecto.",
|
"mudSportOnRemove": "Chapoteo Lodo ha dejado de surtir efecto.",
|
||||||
"waterSportOnAdd": "¡Se han debilitado los ataques\nde tipo Fuego!",
|
"waterSportOnAdd": "¡Se han debilitado los ataques\nde tipo Fuego!",
|
||||||
"waterSportOnRemove": "Hidrochorro ha dejado de surtir efecto.",
|
"waterSportOnRemove": "Hidrochorro ha dejado de surtir efecto.",
|
||||||
|
"plasmaFistsOnAdd": "¡Una lluvia de electrones cae sobre\nel terreno de combate!",
|
||||||
"spikesOnAdd": "¡El equipo de {{opponentDesc}} ha sido rodeado por {{moveName}}!",
|
"spikesOnAdd": "¡El equipo de {{opponentDesc}} ha sido rodeado por {{moveName}}!",
|
||||||
"spikesActivateTrap": "¡Las púas han herido a {{pokemonNameWithAffix}}!",
|
"spikesActivateTrap": "¡Las púas han herido a {{pokemonNameWithAffix}}!",
|
||||||
"toxicSpikesOnAdd": "¡El equipo de {{opponentDesc}} ha sido rodeado por {{moveName}}!",
|
"toxicSpikesOnAdd": "¡El equipo de {{opponentDesc}} ha sido rodeado por {{moveName}}!",
|
||||||
|
@ -28,6 +28,7 @@
|
|||||||
"mudSportOnRemove": "L’effet de Lance-Boue se dissipe !",
|
"mudSportOnRemove": "L’effet de Lance-Boue se dissipe !",
|
||||||
"waterSportOnAdd": "La puissance des capacités\nde type Feu diminue !",
|
"waterSportOnAdd": "La puissance des capacités\nde type Feu diminue !",
|
||||||
"waterSportOnRemove": "L’effet de Tourniquet se dissipe !",
|
"waterSportOnRemove": "L’effet de Tourniquet se dissipe !",
|
||||||
|
"plasmaFistsOnAdd": "Un déluge de plasma s’abat sur le terrain !",
|
||||||
"spikesOnAdd": "Des {{moveName}} s’éparpillent autour de {{opponentDesc}} !",
|
"spikesOnAdd": "Des {{moveName}} s’éparpillent autour de {{opponentDesc}} !",
|
||||||
"spikesActivateTrap": "{{pokemonNameWithAffix}} est blessé\npar les picots !",
|
"spikesActivateTrap": "{{pokemonNameWithAffix}} est blessé\npar les picots !",
|
||||||
"toxicSpikesOnAdd": "Des {{moveName}} s’éparpillent autour de {{opponentDesc}} !",
|
"toxicSpikesOnAdd": "Des {{moveName}} s’éparpillent autour de {{opponentDesc}} !",
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
{
|
{
|
||||||
|
"plasmaFistsOnAdd": "Una pioggia di elettroni si rovescia sui Pokémon!",
|
||||||
"safeguardOnAdd": "Un velo mistico ricopre il campo!",
|
"safeguardOnAdd": "Un velo mistico ricopre il campo!",
|
||||||
"safeguardOnAddPlayer": "Un velo mistico ricopre la tua squadra!",
|
"safeguardOnAddPlayer": "Un velo mistico ricopre la tua squadra!",
|
||||||
"safeguardOnAddEnemy": "Un velo mistico ricopre la squadra avversaria!",
|
"safeguardOnAddEnemy": "Un velo mistico ricopre la squadra avversaria!",
|
||||||
|
@ -2,11 +2,11 @@
|
|||||||
"blockRecoilDamage": "{{pokemonName}}は {{abilityName}}で 反動ダメージを 受けない!",
|
"blockRecoilDamage": "{{pokemonName}}は {{abilityName}}で 反動ダメージを 受けない!",
|
||||||
"badDreams": "{{pokemonName}}は ナイトメアに うなされている!",
|
"badDreams": "{{pokemonName}}は ナイトメアに うなされている!",
|
||||||
"costar": "{{pokemonName}}は {{allyName}}の\n能力変化を コピーした!",
|
"costar": "{{pokemonName}}は {{allyName}}の\n能力変化を コピーした!",
|
||||||
"iceFaceAvoidedDamage": "{{pokemonNameWithAffix}}は\n{{abilityName}}で ダメージを 受けない!",
|
"iceFaceAvoidedDamage": "{{pokemonNameWithAffix}} は\n{{abilityName}}で ダメージを 受けない!",
|
||||||
"perishBody": "{{pokemonName}}の {{abilityName}}で\nおたがいは 3ターン後に ほろびいてしまう!",
|
"perishBody": "{{pokemonName}}の {{abilityName}}で\nおたがいは 3ターン後に ほろびいてしまう!",
|
||||||
"poisonHeal": "{{pokemonName}}は {{abilityName}}で 回復した!",
|
"poisonHeal": "{{pokemonName}}は {{abilityName}}で 回復した!",
|
||||||
"trace": "{{pokemonName}}は 相手の {{targetName}}の\n{{abilityName}}を トレースした!",
|
"trace": "{{pokemonName}}は 相手の {{targetName}}の\n{{abilityName}}を トレースした!",
|
||||||
"windPowerCharged": "{{pokemonNameWithAffix}}は\n{{moveName}}を 受けて じゅうでんした!",
|
"windPowerCharged": "{{pokemonName}} は\n{{moveName}}を 受けて じゅうでんした!",
|
||||||
"quickDraw": "{{pokemonName}}は クイックドロウで\n行動が はやくなった!",
|
"quickDraw": "{{pokemonName}}は クイックドロウで\n行動が はやくなった!",
|
||||||
"disguiseAvoidedDamage": "{{pokemonNameWithAffix}}の\nばけのかわが はがれた!",
|
"disguiseAvoidedDamage": "{{pokemonNameWithAffix}}の\nばけのかわが はがれた!",
|
||||||
"blockItemTheft": "{{pokemonNameWithAffix}}の {{abilityName}}で\n道具を うばわれない!",
|
"blockItemTheft": "{{pokemonNameWithAffix}}の {{abilityName}}で\n道具を うばわれない!",
|
||||||
@ -48,8 +48,8 @@
|
|||||||
"weatherEffectDisappeared": "天候の影響が なくなった!",
|
"weatherEffectDisappeared": "天候の影響が なくなった!",
|
||||||
"postSummonMoldBreaker": "{{pokemonNameWithAffix}}は\nかたやぶりだ!",
|
"postSummonMoldBreaker": "{{pokemonNameWithAffix}}は\nかたやぶりだ!",
|
||||||
"postSummonAnticipation": "{{pokemonNameWithAffix}}は\nみぶるいした!",
|
"postSummonAnticipation": "{{pokemonNameWithAffix}}は\nみぶるいした!",
|
||||||
"postSummonTurboblaze": "{{pokemonNameWithAffix}}は\n燃え盛(もえさか)る オーラを 放っている!",
|
"postSummonTurboblaze": "{{pokemonNameWithAffix}}は\n燃え盛る オーラを 放っている!",
|
||||||
"postSummonTeravolt": "{{pokemonNameWithAffix}}は\n弾(はじ)ける オーラを 放っている!",
|
"postSummonTeravolt": "{{pokemonNameWithAffix}}は\n弾ける オーラを 放っている!",
|
||||||
"postSummonDarkAura": "{{pokemonNameWithAffix}}は\nダークオーラを 放っている!",
|
"postSummonDarkAura": "{{pokemonNameWithAffix}}は\nダークオーラを 放っている!",
|
||||||
"postSummonFairyAura": "{{pokemonNameWithAffix}}は\nフェアリーオーラを 放っている!",
|
"postSummonFairyAura": "{{pokemonNameWithAffix}}は\nフェアリーオーラを 放っている!",
|
||||||
"postSummonAuraBreak": "{{pokemonNameWithAffix}}は\nすべての オーラを 制圧する!",
|
"postSummonAuraBreak": "{{pokemonNameWithAffix}}は\nすべての オーラを 制圧する!",
|
||||||
|
@ -28,6 +28,7 @@
|
|||||||
"mudSportOnRemove": "どろあそびの 効果が なくなった!",
|
"mudSportOnRemove": "どろあそびの 効果が なくなった!",
|
||||||
"waterSportOnAdd": "炎の威力が 弱まった!",
|
"waterSportOnAdd": "炎の威力が 弱まった!",
|
||||||
"waterSportOnRemove": "みずあそびの 効果が なくなった!",
|
"waterSportOnRemove": "みずあそびの 効果が なくなった!",
|
||||||
|
"plasmaFistsOnAdd": "電子のシャワーが 降りそそいだ!",
|
||||||
"spikesOnAdd": "{{opponentDesc}}の 足下に\n{{moveName}}が 散らばった!",
|
"spikesOnAdd": "{{opponentDesc}}の 足下に\n{{moveName}}が 散らばった!",
|
||||||
"spikesActivateTrap": "{{pokemonNameWithAffix}}は\nまきびしの ダメージを 受けた!",
|
"spikesActivateTrap": "{{pokemonNameWithAffix}}は\nまきびしの ダメージを 受けた!",
|
||||||
"toxicSpikesOnAdd": "{{opponentDesc}}の 足下に\n{{moveName}}が 散らばった!",
|
"toxicSpikesOnAdd": "{{opponentDesc}}の 足下に\n{{moveName}}が 散らばった!",
|
||||||
|
@ -28,6 +28,7 @@
|
|||||||
"mudSportOnRemove": "흙놀이의 효과가\n없어졌다!",
|
"mudSportOnRemove": "흙놀이의 효과가\n없어졌다!",
|
||||||
"waterSportOnAdd": "불꽃의 위력이 약해졌다!",
|
"waterSportOnAdd": "불꽃의 위력이 약해졌다!",
|
||||||
"waterSportOnRemove": "물놀이의 효과가\n없어졌다!",
|
"waterSportOnRemove": "물놀이의 효과가\n없어졌다!",
|
||||||
|
"plasmaFistsOnAdd": "전기 입자가 쏟아졌다!",
|
||||||
"spikesOnAdd": "{{opponentDesc}}의 발밑에\n압정이 뿌려졌다!",
|
"spikesOnAdd": "{{opponentDesc}}의 발밑에\n압정이 뿌려졌다!",
|
||||||
"spikesActivateTrap": "{{pokemonNameWithAffix}}[[는]]\n압정뿌리기의 데미지를 입었다!",
|
"spikesActivateTrap": "{{pokemonNameWithAffix}}[[는]]\n압정뿌리기의 데미지를 입었다!",
|
||||||
"toxicSpikesOnAdd": "{{opponentDesc}}의 발밑에\n독압정이 뿌려졌다!",
|
"toxicSpikesOnAdd": "{{opponentDesc}}의 발밑에\n독압정이 뿌려졌다!",
|
||||||
|
@ -28,6 +28,7 @@
|
|||||||
"mudSportOnRemove": "Os efeitos de Mud Sport\nsumiram.",
|
"mudSportOnRemove": "Os efeitos de Mud Sport\nsumiram.",
|
||||||
"waterSportOnAdd": "O poder de movimentos de fogo foi enfraquecido!",
|
"waterSportOnAdd": "O poder de movimentos de fogo foi enfraquecido!",
|
||||||
"waterSportOnRemove": "Os efeitos de Water Sport\nsumiram.",
|
"waterSportOnRemove": "Os efeitos de Water Sport\nsumiram.",
|
||||||
|
"plasmaFistsOnAdd": "Um dilúvio de íons chove sobre o campo de batalha!",
|
||||||
"spikesOnAdd": "{{moveName}} foram espalhados\nno chão ao redor de {{opponentDesc}}!",
|
"spikesOnAdd": "{{moveName}} foram espalhados\nno chão ao redor de {{opponentDesc}}!",
|
||||||
"spikesActivateTrap": "{{pokemonNameWithAffix}} foi ferido\npelos espinhos!",
|
"spikesActivateTrap": "{{pokemonNameWithAffix}} foi ferido\npelos espinhos!",
|
||||||
"toxicSpikesOnAdd": "{{moveName}} foram espalhados\nno chão ao redor de {{opponentDesc}}!",
|
"toxicSpikesOnAdd": "{{moveName}} foram espalhados\nno chão ao redor de {{opponentDesc}}!",
|
||||||
|
@ -28,6 +28,7 @@
|
|||||||
"mudSportOnRemove": "玩泥巴的效果消失了!",
|
"mudSportOnRemove": "玩泥巴的效果消失了!",
|
||||||
"waterSportOnAdd": "火焰的威力减弱了!",
|
"waterSportOnAdd": "火焰的威力减弱了!",
|
||||||
"waterSportOnRemove": "玩水的效果消失了!",
|
"waterSportOnRemove": "玩水的效果消失了!",
|
||||||
|
"plasmaFistsOnAdd": "等离子雨倾盆而下!",
|
||||||
"spikesOnAdd": "{{opponentDesc}}脚下\n散落着{{moveName}}!",
|
"spikesOnAdd": "{{opponentDesc}}脚下\n散落着{{moveName}}!",
|
||||||
"spikesActivateTrap": "{{pokemonNameWithAffix}}\n受到了撒菱的伤害!",
|
"spikesActivateTrap": "{{pokemonNameWithAffix}}\n受到了撒菱的伤害!",
|
||||||
"toxicSpikesOnAdd": "{{opponentDesc}}脚下\n散落着{{moveName}}!",
|
"toxicSpikesOnAdd": "{{opponentDesc}}脚下\n散落着{{moveName}}!",
|
||||||
|
@ -28,6 +28,7 @@
|
|||||||
"mudSportOnRemove": "玩泥巴的效果消失了!",
|
"mudSportOnRemove": "玩泥巴的效果消失了!",
|
||||||
"waterSportOnAdd": "火焰的威力減弱了!",
|
"waterSportOnAdd": "火焰的威力減弱了!",
|
||||||
"waterSportOnRemove": "玩水的效果消失了!",
|
"waterSportOnRemove": "玩水的效果消失了!",
|
||||||
|
"plasmaFistsOnAdd": "等離子雨傾盆而下!",
|
||||||
"spikesOnAdd": "{{opponentDesc}}腳下\n散落著{{moveName}}!",
|
"spikesOnAdd": "{{opponentDesc}}腳下\n散落著{{moveName}}!",
|
||||||
"spikesActivateTrap": "{{pokemonNameWithAffix}}\n受到了撒菱的傷害!",
|
"spikesActivateTrap": "{{pokemonNameWithAffix}}\n受到了撒菱的傷害!",
|
||||||
"toxicSpikesOnAdd": "{{opponentDesc}}腳下\n散落著{{moveName}}!",
|
"toxicSpikesOnAdd": "{{opponentDesc}}腳下\n散落著{{moveName}}!",
|
||||||
|
@ -35,6 +35,12 @@ export class BattleEndPhase extends BattlePhase {
|
|||||||
this.scene.unshiftPhase(new GameOverPhase(this.scene, true));
|
this.scene.unshiftPhase(new GameOverPhase(this.scene, true));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for (const pokemon of this.scene.getField()) {
|
||||||
|
if (pokemon && pokemon.battleSummonData) {
|
||||||
|
pokemon.battleSummonData.waveTurnCount = 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
for (const pokemon of this.scene.getParty().filter(p => p.isAllowedInBattle())) {
|
for (const pokemon of this.scene.getParty().filter(p => p.isAllowedInBattle())) {
|
||||||
applyPostBattleAbAttrs(PostBattleAbAttr, pokemon);
|
applyPostBattleAbAttrs(PostBattleAbAttr, pokemon);
|
||||||
}
|
}
|
||||||
|
@ -173,6 +173,7 @@ export class SwitchSummonPhase extends SummonPhase {
|
|||||||
// Or compensate for force switch move if switched out pokemon is not fainted
|
// Or compensate for force switch move if switched out pokemon is not fainted
|
||||||
if (currentCommand === Command.POKEMON || lastPokemonIsForceSwitchedAndNotFainted) {
|
if (currentCommand === Command.POKEMON || lastPokemonIsForceSwitchedAndNotFainted) {
|
||||||
pokemon.battleSummonData.turnCount--;
|
pokemon.battleSummonData.turnCount--;
|
||||||
|
pokemon.battleSummonData.waveTurnCount--;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.switchType === SwitchType.BATON_PASS && pokemon) {
|
if (this.switchType === SwitchType.BATON_PASS && pokemon) {
|
||||||
|
@ -44,6 +44,7 @@ export class TurnEndPhase extends FieldPhase {
|
|||||||
this.scene.applyModifiers(TurnHeldItemTransferModifier, pokemon.isPlayer(), pokemon);
|
this.scene.applyModifiers(TurnHeldItemTransferModifier, pokemon.isPlayer(), pokemon);
|
||||||
|
|
||||||
pokemon.battleSummonData.turnCount++;
|
pokemon.battleSummonData.turnCount++;
|
||||||
|
pokemon.battleSummonData.waveTurnCount++;
|
||||||
};
|
};
|
||||||
|
|
||||||
this.executeForAll(handlePokemon);
|
this.executeForAll(handlePokemon);
|
||||||
|
@ -43,20 +43,17 @@ export class TurnStartPhase extends FieldPhase {
|
|||||||
orderedTargets = Utils.randSeedShuffle(orderedTargets);
|
orderedTargets = Utils.randSeedShuffle(orderedTargets);
|
||||||
}, this.scene.currentBattle.turn, this.scene.waveSeed);
|
}, this.scene.currentBattle.turn, this.scene.waveSeed);
|
||||||
|
|
||||||
orderedTargets.sort((a: Pokemon, b: Pokemon) => {
|
// Next, a check for Trick Room is applied to determine sort order.
|
||||||
const aSpeed = a?.getEffectiveStat(Stat.SPD) || 0;
|
|
||||||
const bSpeed = b?.getEffectiveStat(Stat.SPD) || 0;
|
|
||||||
|
|
||||||
return bSpeed - aSpeed;
|
|
||||||
});
|
|
||||||
|
|
||||||
// Next, a check for Trick Room is applied. If Trick Room is present, the order is reversed.
|
|
||||||
const speedReversed = new Utils.BooleanHolder(false);
|
const speedReversed = new Utils.BooleanHolder(false);
|
||||||
this.scene.arena.applyTags(TrickRoomTag, speedReversed);
|
this.scene.arena.applyTags(TrickRoomTag, speedReversed);
|
||||||
|
|
||||||
if (speedReversed.value) {
|
// Adjust the sort function based on whether Trick Room is active.
|
||||||
orderedTargets = orderedTargets.reverse();
|
orderedTargets.sort((a: Pokemon, b: Pokemon) => {
|
||||||
}
|
const aSpeed = a?.getEffectiveStat(Stat.SPD) ?? 0;
|
||||||
|
const bSpeed = b?.getEffectiveStat(Stat.SPD) ?? 0;
|
||||||
|
|
||||||
|
return speedReversed.value ? aSpeed - bSpeed : bSpeed - aSpeed;
|
||||||
|
});
|
||||||
|
|
||||||
return orderedTargets.map(t => t.getFieldIndex() + (!t.isPlayer() ? BattlerIndex.ENEMY : BattlerIndex.PLAYER));
|
return orderedTargets.map(t => t.getFieldIndex() + (!t.isPlayer() ? BattlerIndex.ENEMY : BattlerIndex.PLAYER));
|
||||||
}
|
}
|
||||||
|
@ -1,11 +1,7 @@
|
|||||||
import { BattlerTagType } from "#app/enums/battler-tag-type";
|
import { BattlerTagType } from "#enums/battler-tag-type";
|
||||||
import { StatusEffect } from "#app/enums/status-effect";
|
import { StatusEffect } from "#enums/status-effect";
|
||||||
import Pokemon from "#app/field/pokemon";
|
import Pokemon from "#app/field/pokemon";
|
||||||
import { BerryPhase } from "#app/phases/berry-phase";
|
import GameManager from "#test/utils/gameManager";
|
||||||
import { MoveEndPhase } from "#app/phases/move-end-phase";
|
|
||||||
import { TurnEndPhase } from "#app/phases/turn-end-phase";
|
|
||||||
import { TurnStartPhase } from "#app/phases/turn-start-phase";
|
|
||||||
import GameManager from "#app/test/utils/gameManager";
|
|
||||||
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";
|
||||||
@ -53,13 +49,13 @@ describe("Abilities - Gulp Missile", () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it("changes to Gulping Form if HP is over half when Surf or Dive is used", async () => {
|
it("changes to Gulping Form if HP is over half when Surf or Dive is used", async () => {
|
||||||
await game.startBattle([Species.CRAMORANT]);
|
await game.classicMode.startBattle([Species.CRAMORANT]);
|
||||||
const cramorant = game.scene.getPlayerPokemon()!;
|
const cramorant = game.scene.getPlayerPokemon()!;
|
||||||
|
|
||||||
game.move.select(Moves.DIVE);
|
game.move.select(Moves.DIVE);
|
||||||
await game.toNextTurn();
|
await game.toNextTurn();
|
||||||
game.move.select(Moves.DIVE);
|
game.move.select(Moves.DIVE);
|
||||||
await game.phaseInterceptor.to(MoveEndPhase);
|
await game.phaseInterceptor.to("MoveEndPhase");
|
||||||
|
|
||||||
expect(cramorant.getHpRatio()).toBeGreaterThanOrEqual(.5);
|
expect(cramorant.getHpRatio()).toBeGreaterThanOrEqual(.5);
|
||||||
expect(cramorant.getTag(BattlerTagType.GULP_MISSILE_ARROKUDA)).toBeDefined();
|
expect(cramorant.getTag(BattlerTagType.GULP_MISSILE_ARROKUDA)).toBeDefined();
|
||||||
@ -67,21 +63,21 @@ describe("Abilities - Gulp Missile", () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it("changes to Gorging Form if HP is under half when Surf or Dive is used", async () => {
|
it("changes to Gorging Form if HP is under half when Surf or Dive is used", async () => {
|
||||||
await game.startBattle([Species.CRAMORANT]);
|
await game.classicMode.startBattle([Species.CRAMORANT]);
|
||||||
const cramorant = game.scene.getPlayerPokemon()!;
|
const cramorant = game.scene.getPlayerPokemon()!;
|
||||||
|
|
||||||
vi.spyOn(cramorant, "getHpRatio").mockReturnValue(.49);
|
vi.spyOn(cramorant, "getHpRatio").mockReturnValue(.49);
|
||||||
expect(cramorant.getHpRatio()).toBe(.49);
|
expect(cramorant.getHpRatio()).toBe(.49);
|
||||||
|
|
||||||
game.move.select(Moves.SURF);
|
game.move.select(Moves.SURF);
|
||||||
await game.phaseInterceptor.to(MoveEndPhase);
|
await game.phaseInterceptor.to("MoveEndPhase");
|
||||||
|
|
||||||
expect(cramorant.getTag(BattlerTagType.GULP_MISSILE_PIKACHU)).toBeDefined();
|
expect(cramorant.getTag(BattlerTagType.GULP_MISSILE_PIKACHU)).toBeDefined();
|
||||||
expect(cramorant.formIndex).toBe(GORGING_FORM);
|
expect(cramorant.formIndex).toBe(GORGING_FORM);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("changes to base form when switched out after Surf or Dive is used", async () => {
|
it("changes to base form when switched out after Surf or Dive is used", async () => {
|
||||||
await game.startBattle([Species.CRAMORANT, Species.MAGIKARP]);
|
await game.classicMode.startBattle([Species.CRAMORANT, Species.MAGIKARP]);
|
||||||
const cramorant = game.scene.getPlayerPokemon()!;
|
const cramorant = game.scene.getPlayerPokemon()!;
|
||||||
|
|
||||||
game.move.select(Moves.SURF);
|
game.move.select(Moves.SURF);
|
||||||
@ -96,51 +92,51 @@ describe("Abilities - Gulp Missile", () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it("changes form during Dive's charge turn", async () => {
|
it("changes form during Dive's charge turn", async () => {
|
||||||
await game.startBattle([Species.CRAMORANT]);
|
await game.classicMode.startBattle([Species.CRAMORANT]);
|
||||||
const cramorant = game.scene.getPlayerPokemon()!;
|
const cramorant = game.scene.getPlayerPokemon()!;
|
||||||
|
|
||||||
game.move.select(Moves.DIVE);
|
game.move.select(Moves.DIVE);
|
||||||
await game.phaseInterceptor.to(MoveEndPhase);
|
await game.phaseInterceptor.to("MoveEndPhase");
|
||||||
|
|
||||||
expect(cramorant.getTag(BattlerTagType.GULP_MISSILE_ARROKUDA)).toBeDefined();
|
expect(cramorant.getTag(BattlerTagType.GULP_MISSILE_ARROKUDA)).toBeDefined();
|
||||||
expect(cramorant.formIndex).toBe(GULPING_FORM);
|
expect(cramorant.formIndex).toBe(GULPING_FORM);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("deals 1/4 of the attacker's maximum HP when hit by a damaging attack", async () => {
|
it("deals 1/4 of the attacker's maximum HP when hit by a damaging attack", async () => {
|
||||||
game.override.enemyMoveset([Moves.TACKLE]);
|
game.override.enemyMoveset(Moves.TACKLE);
|
||||||
await game.startBattle([Species.CRAMORANT]);
|
await game.classicMode.startBattle([Species.CRAMORANT]);
|
||||||
|
|
||||||
const enemy = game.scene.getEnemyPokemon()!;
|
const enemy = game.scene.getEnemyPokemon()!;
|
||||||
vi.spyOn(enemy, "damageAndUpdate");
|
vi.spyOn(enemy, "damageAndUpdate");
|
||||||
|
|
||||||
game.move.select(Moves.SURF);
|
game.move.select(Moves.SURF);
|
||||||
await game.phaseInterceptor.to(TurnEndPhase);
|
await game.phaseInterceptor.to("TurnEndPhase");
|
||||||
|
|
||||||
expect(enemy.damageAndUpdate).toHaveReturnedWith(getEffectDamage(enemy));
|
expect(enemy.damageAndUpdate).toHaveReturnedWith(getEffectDamage(enemy));
|
||||||
});
|
});
|
||||||
|
|
||||||
it("does not have any effect when hit by non-damaging attack", async () => {
|
it("does not have any effect when hit by non-damaging attack", async () => {
|
||||||
game.override.enemyMoveset([Moves.TAIL_WHIP]);
|
game.override.enemyMoveset(Moves.TAIL_WHIP);
|
||||||
await game.startBattle([Species.CRAMORANT]);
|
await game.classicMode.startBattle([Species.CRAMORANT]);
|
||||||
|
|
||||||
const cramorant = game.scene.getPlayerPokemon()!;
|
const cramorant = game.scene.getPlayerPokemon()!;
|
||||||
vi.spyOn(cramorant, "getHpRatio").mockReturnValue(.55);
|
vi.spyOn(cramorant, "getHpRatio").mockReturnValue(.55);
|
||||||
|
|
||||||
game.move.select(Moves.SURF);
|
game.move.select(Moves.SURF);
|
||||||
await game.phaseInterceptor.to(MoveEndPhase);
|
await game.phaseInterceptor.to("MoveEndPhase");
|
||||||
|
|
||||||
expect(cramorant.getTag(BattlerTagType.GULP_MISSILE_ARROKUDA)).toBeDefined();
|
expect(cramorant.getTag(BattlerTagType.GULP_MISSILE_ARROKUDA)).toBeDefined();
|
||||||
expect(cramorant.formIndex).toBe(GULPING_FORM);
|
expect(cramorant.formIndex).toBe(GULPING_FORM);
|
||||||
|
|
||||||
await game.phaseInterceptor.to(TurnEndPhase);
|
await game.phaseInterceptor.to("TurnEndPhase");
|
||||||
|
|
||||||
expect(cramorant.getTag(BattlerTagType.GULP_MISSILE_ARROKUDA)).toBeDefined();
|
expect(cramorant.getTag(BattlerTagType.GULP_MISSILE_ARROKUDA)).toBeDefined();
|
||||||
expect(cramorant.formIndex).toBe(GULPING_FORM);
|
expect(cramorant.formIndex).toBe(GULPING_FORM);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("lowers attacker's DEF stat stage by 1 when hit in Gulping form", async () => {
|
it("lowers attacker's DEF stat stage by 1 when hit in Gulping form", async () => {
|
||||||
game.override.enemyMoveset([Moves.TACKLE]);
|
game.override.enemyMoveset(Moves.TACKLE);
|
||||||
await game.startBattle([Species.CRAMORANT]);
|
await game.classicMode.startBattle([Species.CRAMORANT]);
|
||||||
|
|
||||||
const cramorant = game.scene.getPlayerPokemon()!;
|
const cramorant = game.scene.getPlayerPokemon()!;
|
||||||
const enemy = game.scene.getEnemyPokemon()!;
|
const enemy = game.scene.getEnemyPokemon()!;
|
||||||
@ -149,12 +145,12 @@ describe("Abilities - Gulp Missile", () => {
|
|||||||
vi.spyOn(cramorant, "getHpRatio").mockReturnValue(.55);
|
vi.spyOn(cramorant, "getHpRatio").mockReturnValue(.55);
|
||||||
|
|
||||||
game.move.select(Moves.SURF);
|
game.move.select(Moves.SURF);
|
||||||
await game.phaseInterceptor.to(MoveEndPhase);
|
await game.phaseInterceptor.to("MoveEndPhase");
|
||||||
|
|
||||||
expect(cramorant.getTag(BattlerTagType.GULP_MISSILE_ARROKUDA)).toBeDefined();
|
expect(cramorant.getTag(BattlerTagType.GULP_MISSILE_ARROKUDA)).toBeDefined();
|
||||||
expect(cramorant.formIndex).toBe(GULPING_FORM);
|
expect(cramorant.formIndex).toBe(GULPING_FORM);
|
||||||
|
|
||||||
await game.phaseInterceptor.to(TurnEndPhase);
|
await game.phaseInterceptor.to("TurnEndPhase");
|
||||||
|
|
||||||
expect(enemy.damageAndUpdate).toHaveReturnedWith(getEffectDamage(enemy));
|
expect(enemy.damageAndUpdate).toHaveReturnedWith(getEffectDamage(enemy));
|
||||||
expect(enemy.getStatStage(Stat.DEF)).toBe(-1);
|
expect(enemy.getStatStage(Stat.DEF)).toBe(-1);
|
||||||
@ -163,8 +159,8 @@ describe("Abilities - Gulp Missile", () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it("paralyzes the enemy when hit in Gorging form", async () => {
|
it("paralyzes the enemy when hit in Gorging form", async () => {
|
||||||
game.override.enemyMoveset([Moves.TACKLE]);
|
game.override.enemyMoveset(Moves.TACKLE);
|
||||||
await game.startBattle([Species.CRAMORANT]);
|
await game.classicMode.startBattle([Species.CRAMORANT]);
|
||||||
|
|
||||||
const cramorant = game.scene.getPlayerPokemon()!;
|
const cramorant = game.scene.getPlayerPokemon()!;
|
||||||
const enemy = game.scene.getEnemyPokemon()!;
|
const enemy = game.scene.getEnemyPokemon()!;
|
||||||
@ -173,12 +169,12 @@ describe("Abilities - Gulp Missile", () => {
|
|||||||
vi.spyOn(cramorant, "getHpRatio").mockReturnValue(.45);
|
vi.spyOn(cramorant, "getHpRatio").mockReturnValue(.45);
|
||||||
|
|
||||||
game.move.select(Moves.SURF);
|
game.move.select(Moves.SURF);
|
||||||
await game.phaseInterceptor.to(MoveEndPhase);
|
await game.phaseInterceptor.to("MoveEndPhase");
|
||||||
|
|
||||||
expect(cramorant.getTag(BattlerTagType.GULP_MISSILE_PIKACHU)).toBeDefined();
|
expect(cramorant.getTag(BattlerTagType.GULP_MISSILE_PIKACHU)).toBeDefined();
|
||||||
expect(cramorant.formIndex).toBe(GORGING_FORM);
|
expect(cramorant.formIndex).toBe(GORGING_FORM);
|
||||||
|
|
||||||
await game.phaseInterceptor.to(TurnEndPhase);
|
await game.phaseInterceptor.to("TurnEndPhase");
|
||||||
|
|
||||||
expect(enemy.damageAndUpdate).toHaveReturnedWith(getEffectDamage(enemy));
|
expect(enemy.damageAndUpdate).toHaveReturnedWith(getEffectDamage(enemy));
|
||||||
expect(enemy.status?.effect).toBe(StatusEffect.PARALYSIS);
|
expect(enemy.status?.effect).toBe(StatusEffect.PARALYSIS);
|
||||||
@ -187,21 +183,21 @@ describe("Abilities - Gulp Missile", () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it("does not activate the ability when underwater", async () => {
|
it("does not activate the ability when underwater", async () => {
|
||||||
game.override.enemyMoveset([Moves.SURF]);
|
game.override.enemyMoveset(Moves.SURF);
|
||||||
await game.startBattle([Species.CRAMORANT]);
|
await game.classicMode.startBattle([Species.CRAMORANT]);
|
||||||
|
|
||||||
const cramorant = game.scene.getPlayerPokemon()!;
|
const cramorant = game.scene.getPlayerPokemon()!;
|
||||||
|
|
||||||
game.move.select(Moves.DIVE);
|
game.move.select(Moves.DIVE);
|
||||||
await game.phaseInterceptor.to(BerryPhase, false);
|
await game.phaseInterceptor.to("BerryPhase", false);
|
||||||
|
|
||||||
expect(cramorant.getTag(BattlerTagType.GULP_MISSILE_ARROKUDA)).toBeDefined();
|
expect(cramorant.getTag(BattlerTagType.GULP_MISSILE_ARROKUDA)).toBeDefined();
|
||||||
expect(cramorant.formIndex).toBe(GULPING_FORM);
|
expect(cramorant.formIndex).toBe(GULPING_FORM);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("prevents effect damage but inflicts secondary effect on attacker with Magic Guard", async () => {
|
it("prevents effect damage but inflicts secondary effect on attacker with Magic Guard", async () => {
|
||||||
game.override.enemyMoveset([Moves.TACKLE]).enemyAbility(Abilities.MAGIC_GUARD);
|
game.override.enemyMoveset(Moves.TACKLE).enemyAbility(Abilities.MAGIC_GUARD);
|
||||||
await game.startBattle([Species.CRAMORANT]);
|
await game.classicMode.startBattle([Species.CRAMORANT]);
|
||||||
|
|
||||||
const cramorant = game.scene.getPlayerPokemon()!;
|
const cramorant = game.scene.getPlayerPokemon()!;
|
||||||
const enemy = game.scene.getEnemyPokemon()!;
|
const enemy = game.scene.getEnemyPokemon()!;
|
||||||
@ -209,13 +205,13 @@ describe("Abilities - Gulp Missile", () => {
|
|||||||
vi.spyOn(cramorant, "getHpRatio").mockReturnValue(.55);
|
vi.spyOn(cramorant, "getHpRatio").mockReturnValue(.55);
|
||||||
|
|
||||||
game.move.select(Moves.SURF);
|
game.move.select(Moves.SURF);
|
||||||
await game.phaseInterceptor.to(MoveEndPhase);
|
await game.phaseInterceptor.to("MoveEndPhase");
|
||||||
const enemyHpPreEffect = enemy.hp;
|
const enemyHpPreEffect = enemy.hp;
|
||||||
|
|
||||||
expect(cramorant.getTag(BattlerTagType.GULP_MISSILE_ARROKUDA)).toBeDefined();
|
expect(cramorant.getTag(BattlerTagType.GULP_MISSILE_ARROKUDA)).toBeDefined();
|
||||||
expect(cramorant.formIndex).toBe(GULPING_FORM);
|
expect(cramorant.formIndex).toBe(GULPING_FORM);
|
||||||
|
|
||||||
await game.phaseInterceptor.to(TurnEndPhase);
|
await game.phaseInterceptor.to("TurnEndPhase");
|
||||||
|
|
||||||
expect(enemy.hp).toBe(enemyHpPreEffect);
|
expect(enemy.hp).toBe(enemyHpPreEffect);
|
||||||
expect(enemy.getStatStage(Stat.DEF)).toBe(-1);
|
expect(enemy.getStatStage(Stat.DEF)).toBe(-1);
|
||||||
@ -223,20 +219,36 @@ describe("Abilities - Gulp Missile", () => {
|
|||||||
expect(cramorant.formIndex).toBe(NORMAL_FORM);
|
expect(cramorant.formIndex).toBe(NORMAL_FORM);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("activates on faint", async () => {
|
||||||
|
game.override.enemyMoveset(Moves.THUNDERBOLT);
|
||||||
|
await game.classicMode.startBattle([Species.CRAMORANT]);
|
||||||
|
|
||||||
|
const cramorant = game.scene.getPlayerPokemon()!;
|
||||||
|
|
||||||
|
game.move.select(Moves.SURF);
|
||||||
|
await game.phaseInterceptor.to("FaintPhase");
|
||||||
|
|
||||||
|
expect(cramorant.hp).toBe(0);
|
||||||
|
expect(cramorant.getTag(BattlerTagType.GULP_MISSILE_ARROKUDA)).toBeUndefined();
|
||||||
|
expect(cramorant.formIndex).toBe(NORMAL_FORM);
|
||||||
|
expect(game.scene.getEnemyPokemon()!.getStatStage(Stat.DEF)).toBe(-1);
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
it("cannot be suppressed", async () => {
|
it("cannot be suppressed", async () => {
|
||||||
game.override.enemyMoveset([Moves.GASTRO_ACID]);
|
game.override.enemyMoveset(Moves.GASTRO_ACID);
|
||||||
await game.startBattle([Species.CRAMORANT]);
|
await game.classicMode.startBattle([Species.CRAMORANT]);
|
||||||
|
|
||||||
const cramorant = game.scene.getPlayerPokemon()!;
|
const cramorant = game.scene.getPlayerPokemon()!;
|
||||||
vi.spyOn(cramorant, "getHpRatio").mockReturnValue(.55);
|
vi.spyOn(cramorant, "getHpRatio").mockReturnValue(.55);
|
||||||
|
|
||||||
game.move.select(Moves.SURF);
|
game.move.select(Moves.SURF);
|
||||||
await game.phaseInterceptor.to(MoveEndPhase);
|
await game.phaseInterceptor.to("MoveEndPhase");
|
||||||
|
|
||||||
expect(cramorant.getTag(BattlerTagType.GULP_MISSILE_ARROKUDA)).toBeDefined();
|
expect(cramorant.getTag(BattlerTagType.GULP_MISSILE_ARROKUDA)).toBeDefined();
|
||||||
expect(cramorant.formIndex).toBe(GULPING_FORM);
|
expect(cramorant.formIndex).toBe(GULPING_FORM);
|
||||||
|
|
||||||
await game.phaseInterceptor.to(TurnEndPhase);
|
await game.phaseInterceptor.to("TurnEndPhase");
|
||||||
|
|
||||||
expect(cramorant.hasAbility(Abilities.GULP_MISSILE)).toBe(true);
|
expect(cramorant.hasAbility(Abilities.GULP_MISSILE)).toBe(true);
|
||||||
expect(cramorant.getTag(BattlerTagType.GULP_MISSILE_ARROKUDA)).toBeDefined();
|
expect(cramorant.getTag(BattlerTagType.GULP_MISSILE_ARROKUDA)).toBeDefined();
|
||||||
@ -244,19 +256,19 @@ describe("Abilities - Gulp Missile", () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it("cannot be swapped with another ability", async () => {
|
it("cannot be swapped with another ability", async () => {
|
||||||
game.override.enemyMoveset([Moves.SKILL_SWAP]);
|
game.override.enemyMoveset(Moves.SKILL_SWAP);
|
||||||
await game.startBattle([Species.CRAMORANT]);
|
await game.classicMode.startBattle([Species.CRAMORANT]);
|
||||||
|
|
||||||
const cramorant = game.scene.getPlayerPokemon()!;
|
const cramorant = game.scene.getPlayerPokemon()!;
|
||||||
vi.spyOn(cramorant, "getHpRatio").mockReturnValue(.55);
|
vi.spyOn(cramorant, "getHpRatio").mockReturnValue(.55);
|
||||||
|
|
||||||
game.move.select(Moves.SURF);
|
game.move.select(Moves.SURF);
|
||||||
await game.phaseInterceptor.to(MoveEndPhase);
|
await game.phaseInterceptor.to("MoveEndPhase");
|
||||||
|
|
||||||
expect(cramorant.getTag(BattlerTagType.GULP_MISSILE_ARROKUDA)).toBeDefined();
|
expect(cramorant.getTag(BattlerTagType.GULP_MISSILE_ARROKUDA)).toBeDefined();
|
||||||
expect(cramorant.formIndex).toBe(GULPING_FORM);
|
expect(cramorant.formIndex).toBe(GULPING_FORM);
|
||||||
|
|
||||||
await game.phaseInterceptor.to(TurnEndPhase);
|
await game.phaseInterceptor.to("TurnEndPhase");
|
||||||
|
|
||||||
expect(cramorant.hasAbility(Abilities.GULP_MISSILE)).toBe(true);
|
expect(cramorant.hasAbility(Abilities.GULP_MISSILE)).toBe(true);
|
||||||
expect(cramorant.getTag(BattlerTagType.GULP_MISSILE_ARROKUDA)).toBeDefined();
|
expect(cramorant.getTag(BattlerTagType.GULP_MISSILE_ARROKUDA)).toBeDefined();
|
||||||
@ -266,9 +278,9 @@ describe("Abilities - Gulp Missile", () => {
|
|||||||
it("cannot be copied", async () => {
|
it("cannot be copied", async () => {
|
||||||
game.override.enemyAbility(Abilities.TRACE);
|
game.override.enemyAbility(Abilities.TRACE);
|
||||||
|
|
||||||
await game.startBattle([Species.CRAMORANT]);
|
await game.classicMode.startBattle([Species.CRAMORANT]);
|
||||||
game.move.select(Moves.SPLASH);
|
game.move.select(Moves.SPLASH);
|
||||||
await game.phaseInterceptor.to(TurnStartPhase);
|
await game.phaseInterceptor.to("TurnStartPhase");
|
||||||
|
|
||||||
expect(game.scene.getEnemyPokemon()?.hasAbility(Abilities.GULP_MISSILE)).toBe(false);
|
expect(game.scene.getEnemyPokemon()?.hasAbility(Abilities.GULP_MISSILE)).toBe(false);
|
||||||
});
|
});
|
||||||
|
@ -3,6 +3,7 @@ import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest";
|
|||||||
import GameManager from "../utils/gameManager";
|
import GameManager from "../utils/gameManager";
|
||||||
import { PokeballType } from "#app/enums/pokeball";
|
import { PokeballType } from "#app/enums/pokeball";
|
||||||
import BattleScene from "#app/battle-scene";
|
import BattleScene from "#app/battle-scene";
|
||||||
|
import { Moves } from "#app/enums/moves";
|
||||||
|
|
||||||
describe("Spec - Pokemon", () => {
|
describe("Spec - Pokemon", () => {
|
||||||
let phaserGame: Phaser.Game;
|
let phaserGame: Phaser.Game;
|
||||||
@ -63,4 +64,15 @@ describe("Spec - Pokemon", () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("should not share tms between different forms", async () => {
|
||||||
|
game.override.starterForms({ [Species.ROTOM]: 4 });
|
||||||
|
|
||||||
|
await game.classicMode.startBattle([Species.ROTOM]);
|
||||||
|
|
||||||
|
const fanRotom = game.scene.getPlayerPokemon()!;
|
||||||
|
|
||||||
|
expect(fanRotom.compatibleTms).not.toContain(Moves.BLIZZARD);
|
||||||
|
expect(fanRotom.compatibleTms).toContain(Moves.AIR_SLASH);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
@ -23,14 +23,15 @@ describe("Moves - Fake Out", () => {
|
|||||||
game.override
|
game.override
|
||||||
.battleType("single")
|
.battleType("single")
|
||||||
.enemySpecies(Species.CORVIKNIGHT)
|
.enemySpecies(Species.CORVIKNIGHT)
|
||||||
.starterSpecies(Species.FEEBAS)
|
|
||||||
.moveset([Moves.FAKE_OUT, Moves.SPLASH])
|
.moveset([Moves.FAKE_OUT, Moves.SPLASH])
|
||||||
.enemyMoveset(Moves.SPLASH)
|
.enemyMoveset(Moves.SPLASH)
|
||||||
|
.enemyLevel(10)
|
||||||
|
.startingLevel(10) // prevent LevelUpPhase from happening
|
||||||
.disableCrits();
|
.disableCrits();
|
||||||
});
|
});
|
||||||
|
|
||||||
it("can only be used on the first turn a pokemon is sent out", async() => {
|
it("can only be used on the first turn a pokemon is sent out in a battle", async() => {
|
||||||
await game.classicMode.startBattle();
|
await game.classicMode.startBattle([Species.FEEBAS]);
|
||||||
|
|
||||||
const enemy = game.scene.getEnemyPokemon()!;
|
const enemy = game.scene.getEnemyPokemon()!;
|
||||||
|
|
||||||
@ -44,22 +45,27 @@ describe("Moves - Fake Out", () => {
|
|||||||
await game.toNextTurn();
|
await game.toNextTurn();
|
||||||
|
|
||||||
expect(enemy.hp).toBe(postTurnOneHp);
|
expect(enemy.hp).toBe(postTurnOneHp);
|
||||||
|
}, 20000);
|
||||||
|
|
||||||
game.move.select(Moves.SPLASH);
|
// This is a PokeRogue buff to Fake Out
|
||||||
await game.doKillOpponents();
|
it("can be used at the start of every wave even if the pokemon wasn't recalled", async() => {
|
||||||
|
await game.classicMode.startBattle([Species.FEEBAS]);
|
||||||
|
|
||||||
|
const enemy = game.scene.getEnemyPokemon()!;
|
||||||
|
enemy.damageAndUpdate(enemy.getMaxHp() - 1);
|
||||||
|
|
||||||
|
game.move.select(Moves.FAKE_OUT);
|
||||||
await game.toNextWave();
|
await game.toNextWave();
|
||||||
|
|
||||||
const newEnemy = game.scene.getEnemyPokemon()!;
|
|
||||||
|
|
||||||
game.move.select(Moves.FAKE_OUT);
|
game.move.select(Moves.FAKE_OUT);
|
||||||
await game.toNextTurn();
|
await game.toNextTurn();
|
||||||
|
|
||||||
expect(newEnemy.hp).toBe(newEnemy.getMaxHp());
|
expect(game.scene.getEnemyPokemon()!.isFullHp()).toBe(false);
|
||||||
}, 20000);
|
}, 20000);
|
||||||
|
|
||||||
it("can be used again if recalled and sent back out", async() => {
|
it("can be used again if recalled and sent back out", async() => {
|
||||||
game.override.startingWave(4);
|
game.override.startingWave(4);
|
||||||
await game.classicMode.startBattle();
|
await game.classicMode.startBattle([Species.FEEBAS, Species.MAGIKARP]);
|
||||||
|
|
||||||
const enemy1 = game.scene.getEnemyPokemon()!;
|
const enemy1 = game.scene.getEnemyPokemon()!;
|
||||||
|
|
||||||
@ -76,6 +82,18 @@ describe("Moves - Fake Out", () => {
|
|||||||
|
|
||||||
const enemy2 = game.scene.getEnemyPokemon()!;
|
const enemy2 = game.scene.getEnemyPokemon()!;
|
||||||
|
|
||||||
|
expect(enemy2.hp).toBeLessThan(enemy2.getMaxHp());
|
||||||
|
enemy2.hp = enemy2.getMaxHp();
|
||||||
|
|
||||||
|
game.doSwitchPokemon(1);
|
||||||
|
await game.toNextTurn();
|
||||||
|
|
||||||
|
game.doSwitchPokemon(1);
|
||||||
|
await game.toNextTurn();
|
||||||
|
|
||||||
|
game.move.select(Moves.FAKE_OUT);
|
||||||
|
await game.toNextTurn();
|
||||||
|
|
||||||
expect(enemy2.hp).toBeLessThan(enemy2.getMaxHp());
|
expect(enemy2.hp).toBeLessThan(enemy2.getMaxHp());
|
||||||
}, 20000);
|
}, 20000);
|
||||||
});
|
});
|
||||||
|
98
src/test/moves/plasma_fists.test.ts
Normal file
98
src/test/moves/plasma_fists.test.ts
Normal file
@ -0,0 +1,98 @@
|
|||||||
|
import { BattlerIndex } from "#app/battle";
|
||||||
|
import { Type } from "#app/data/type";
|
||||||
|
import { Abilities } from "#enums/abilities";
|
||||||
|
import { Moves } from "#enums/moves";
|
||||||
|
import { Species } from "#enums/species";
|
||||||
|
import GameManager from "#test/utils/gameManager";
|
||||||
|
import Phaser from "phaser";
|
||||||
|
import { afterEach, beforeAll, beforeEach, describe, it, expect, vi } from "vitest";
|
||||||
|
|
||||||
|
describe("Moves - Plasma Fists", () => {
|
||||||
|
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.PLASMA_FISTS, Moves.TACKLE])
|
||||||
|
.battleType("double")
|
||||||
|
.startingLevel(100)
|
||||||
|
.enemySpecies(Species.DUSCLOPS)
|
||||||
|
.enemyAbility(Abilities.BALL_FETCH)
|
||||||
|
.enemyMoveset(Moves.TACKLE)
|
||||||
|
.enemyLevel(100);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should convert all subsequent Normal-type attacks to Electric-type", async () => {
|
||||||
|
await game.classicMode.startBattle([Species.DUSCLOPS, Species.BLASTOISE]);
|
||||||
|
|
||||||
|
const field = game.scene.getField(true);
|
||||||
|
field.forEach(p => vi.spyOn(p, "getMoveType"));
|
||||||
|
|
||||||
|
game.move.select(Moves.PLASMA_FISTS, 0, BattlerIndex.ENEMY);
|
||||||
|
game.move.select(Moves.TACKLE, 1, BattlerIndex.ENEMY_2);
|
||||||
|
|
||||||
|
await game.forceEnemyMove(Moves.TACKLE, BattlerIndex.PLAYER);
|
||||||
|
await game.forceEnemyMove(Moves.TACKLE, BattlerIndex.PLAYER_2);
|
||||||
|
|
||||||
|
await game.setTurnOrder([BattlerIndex.PLAYER, BattlerIndex.PLAYER_2, BattlerIndex.ENEMY, BattlerIndex.ENEMY_2]);
|
||||||
|
|
||||||
|
await game.phaseInterceptor.to("BerryPhase", false);
|
||||||
|
|
||||||
|
field.forEach(p => {
|
||||||
|
expect(p.getMoveType).toHaveLastReturnedWith(Type.ELECTRIC);
|
||||||
|
expect(p.hp).toBeLessThan(p.getMaxHp());
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should not affect Normal-type attacks boosted by Pixilate", async () => {
|
||||||
|
game.override
|
||||||
|
.battleType("single")
|
||||||
|
.enemyAbility(Abilities.PIXILATE);
|
||||||
|
|
||||||
|
await game.classicMode.startBattle([Species.ONIX]);
|
||||||
|
|
||||||
|
const playerPokemon = game.scene.getPlayerPokemon()!;
|
||||||
|
const enemyPokemon = game.scene.getEnemyPokemon()!;
|
||||||
|
vi.spyOn(enemyPokemon, "getMoveType");
|
||||||
|
|
||||||
|
game.move.select(Moves.PLASMA_FISTS);
|
||||||
|
|
||||||
|
await game.setTurnOrder([BattlerIndex.PLAYER, BattlerIndex.ENEMY]);
|
||||||
|
await game.phaseInterceptor.to("BerryPhase", false);
|
||||||
|
|
||||||
|
expect(enemyPokemon.getMoveType).toHaveLastReturnedWith(Type.FAIRY);
|
||||||
|
expect(playerPokemon.hp).toBeLessThan(playerPokemon.getMaxHp());
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should affect moves that become Normal type due to Normalize", async () => {
|
||||||
|
game.override
|
||||||
|
.battleType("single")
|
||||||
|
.enemyAbility(Abilities.NORMALIZE)
|
||||||
|
.enemyMoveset(Moves.WATER_GUN);
|
||||||
|
|
||||||
|
await game.classicMode.startBattle([Species.DUSCLOPS]);
|
||||||
|
|
||||||
|
const playerPokemon = game.scene.getPlayerPokemon()!;
|
||||||
|
const enemyPokemon = game.scene.getEnemyPokemon()!;
|
||||||
|
vi.spyOn(enemyPokemon, "getMoveType");
|
||||||
|
|
||||||
|
game.move.select(Moves.PLASMA_FISTS);
|
||||||
|
|
||||||
|
await game.setTurnOrder([BattlerIndex.PLAYER, BattlerIndex.ENEMY]);
|
||||||
|
await game.phaseInterceptor.to("BerryPhase", false);
|
||||||
|
|
||||||
|
expect(enemyPokemon.getMoveType).toHaveLastReturnedWith(Type.ELECTRIC);
|
||||||
|
expect(playerPokemon.hp).toBeLessThan(playerPokemon.getMaxHp());
|
||||||
|
});
|
||||||
|
});
|
82
src/test/moves/syrup_bomb.test.ts
Normal file
82
src/test/moves/syrup_bomb.test.ts
Normal file
@ -0,0 +1,82 @@
|
|||||||
|
import { allMoves } from "#app/data/move";
|
||||||
|
import { Moves } from "#enums/moves";
|
||||||
|
import { Species } from "#enums/species";
|
||||||
|
import { Abilities } from "#enums/abilities";
|
||||||
|
import { BattlerTagType } from "#enums/battler-tag-type";
|
||||||
|
import { Stat } from "#enums/stat";
|
||||||
|
import GameManager from "#test/utils/gameManager";
|
||||||
|
import Phaser from "phaser";
|
||||||
|
import { BattlerIndex } from "#app/battle";
|
||||||
|
import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest";
|
||||||
|
|
||||||
|
describe("Moves - SYRUP BOMB", () => {
|
||||||
|
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
|
||||||
|
.starterSpecies(Species.MAGIKARP)
|
||||||
|
.enemySpecies(Species.SNORLAX)
|
||||||
|
.startingLevel(30)
|
||||||
|
.enemyLevel(100)
|
||||||
|
.moveset([Moves.SYRUP_BOMB, Moves.SPLASH])
|
||||||
|
.enemyMoveset(Moves.SPLASH);
|
||||||
|
vi.spyOn(allMoves[Moves.SYRUP_BOMB], "accuracy", "get").mockReturnValue(100);
|
||||||
|
});
|
||||||
|
|
||||||
|
//Bulbapedia Reference: https://bulbapedia.bulbagarden.net/wiki/syrup_bomb_(move)
|
||||||
|
|
||||||
|
it("decreases the target Pokemon's speed stat once per turn for 3 turns",
|
||||||
|
async () => {
|
||||||
|
await game.startBattle([Species.MAGIKARP]);
|
||||||
|
|
||||||
|
const targetPokemon = game.scene.getEnemyPokemon()!;
|
||||||
|
expect(targetPokemon.getStatStage(Stat.SPD)).toBe(0);
|
||||||
|
|
||||||
|
game.move.select(Moves.SYRUP_BOMB);
|
||||||
|
await game.setTurnOrder([BattlerIndex.PLAYER, BattlerIndex.ENEMY]);
|
||||||
|
await game.move.forceHit();
|
||||||
|
await game.toNextTurn();
|
||||||
|
expect(targetPokemon.getTag(BattlerTagType.SYRUP_BOMB)).toBeDefined();
|
||||||
|
expect(targetPokemon.getStatStage(Stat.SPD)).toBe(-1);
|
||||||
|
|
||||||
|
game.move.select(Moves.SPLASH);
|
||||||
|
await game.toNextTurn();
|
||||||
|
expect(targetPokemon.getTag(BattlerTagType.SYRUP_BOMB)).toBeDefined();
|
||||||
|
expect(targetPokemon.getStatStage(Stat.SPD)).toBe(-2);
|
||||||
|
|
||||||
|
game.move.select(Moves.SPLASH);
|
||||||
|
await game.toNextTurn();
|
||||||
|
expect(targetPokemon.getTag(BattlerTagType.SYRUP_BOMB)).toBeUndefined();
|
||||||
|
expect(targetPokemon.getStatStage(Stat.SPD)).toBe(-3);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
it("does not affect Pokemon with the ability Bulletproof",
|
||||||
|
async () => {
|
||||||
|
game.override.enemyAbility(Abilities.BULLETPROOF);
|
||||||
|
await game.startBattle([Species.MAGIKARP]);
|
||||||
|
|
||||||
|
const targetPokemon = game.scene.getEnemyPokemon()!;
|
||||||
|
|
||||||
|
game.move.select(Moves.SYRUP_BOMB);
|
||||||
|
await game.setTurnOrder([BattlerIndex.PLAYER, BattlerIndex.ENEMY]);
|
||||||
|
await game.move.forceHit();
|
||||||
|
await game.toNextTurn();
|
||||||
|
expect(targetPokemon.isFullHp()).toBe(true);
|
||||||
|
expect(targetPokemon.getTag(BattlerTagType.SYRUP_BOMB)).toBeUndefined();
|
||||||
|
expect(targetPokemon.getStatStage(Stat.SPD)).toBe(0);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
});
|
@ -303,7 +303,7 @@ export default class GameManager {
|
|||||||
|
|
||||||
vi.spyOn(enemy, "getNextMove").mockReturnValueOnce({
|
vi.spyOn(enemy, "getNextMove").mockReturnValueOnce({
|
||||||
move: moveId,
|
move: moveId,
|
||||||
targets: (target && !legalTargets.multiple && legalTargets.targets.includes(target))
|
targets: (target !== undefined && !legalTargets.multiple && legalTargets.targets.includes(target))
|
||||||
? [target]
|
? [target]
|
||||||
: enemy.getNextTargets(moveId)
|
: enemy.getNextTargets(moveId)
|
||||||
});
|
});
|
||||||
|
@ -99,8 +99,9 @@ export default class EggSummaryUiHandler extends MessageUiHandler {
|
|||||||
|
|
||||||
clear() {
|
clear() {
|
||||||
super.clear();
|
super.clear();
|
||||||
this.cursor = -1;
|
|
||||||
this.scrollGridHandler.reset();
|
this.scrollGridHandler.reset();
|
||||||
|
this.cursor = -1;
|
||||||
|
|
||||||
this.summaryContainer.setVisible(false);
|
this.summaryContainer.setVisible(false);
|
||||||
this.pokemonIconsContainer.removeAll(true);
|
this.pokemonIconsContainer.removeAll(true);
|
||||||
this.pokemonContainers = [];
|
this.pokemonContainers = [];
|
||||||
@ -164,8 +165,8 @@ export default class EggSummaryUiHandler extends MessageUiHandler {
|
|||||||
|
|
||||||
this.scrollGridHandler.setTotalElements(this.eggHatchData.length);
|
this.scrollGridHandler.setTotalElements(this.eggHatchData.length);
|
||||||
this.updatePokemonIcons();
|
this.updatePokemonIcons();
|
||||||
|
|
||||||
this.setCursor(0);
|
this.setCursor(0);
|
||||||
|
|
||||||
this.scene.playSoundWithoutBgm("evolution_fanfare");
|
this.scene.playSoundWithoutBgm("evolution_fanfare");
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -6,6 +6,7 @@ import { TextStyle, addTextObject, getTextStyleOptions } from "./text";
|
|||||||
import { getSplashMessages } from "../data/splash-messages";
|
import { getSplashMessages } from "../data/splash-messages";
|
||||||
import i18next from "i18next";
|
import i18next from "i18next";
|
||||||
import { TimedEventDisplay } from "#app/timed-event-manager";
|
import { TimedEventDisplay } from "#app/timed-event-manager";
|
||||||
|
import { version } from "../../package.json";
|
||||||
|
|
||||||
export default class TitleUiHandler extends OptionSelectUiHandler {
|
export default class TitleUiHandler extends OptionSelectUiHandler {
|
||||||
/** If the stats can not be retrieved, use this fallback value */
|
/** If the stats can not be retrieved, use this fallback value */
|
||||||
@ -16,6 +17,7 @@ export default class TitleUiHandler extends OptionSelectUiHandler {
|
|||||||
private splashMessage: string;
|
private splashMessage: string;
|
||||||
private splashMessageText: Phaser.GameObjects.Text;
|
private splashMessageText: Phaser.GameObjects.Text;
|
||||||
private eventDisplay: TimedEventDisplay;
|
private eventDisplay: TimedEventDisplay;
|
||||||
|
private appVersionText: Phaser.GameObjects.Text;
|
||||||
|
|
||||||
private titleStatsTimer: NodeJS.Timeout | null;
|
private titleStatsTimer: NodeJS.Timeout | null;
|
||||||
|
|
||||||
@ -68,6 +70,11 @@ export default class TitleUiHandler extends OptionSelectUiHandler {
|
|||||||
loop: -1,
|
loop: -1,
|
||||||
yoyo: true,
|
yoyo: true,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
this.appVersionText = addTextObject(this.scene, logo.x - 60, logo.y + logo.displayHeight + 4, "", TextStyle.MONEY, { fontSize: "54px" });
|
||||||
|
this.appVersionText.setOrigin(0.5, 0.5);
|
||||||
|
this.appVersionText.setAngle(0);
|
||||||
|
this.titleContainer.add(this.appVersionText);
|
||||||
}
|
}
|
||||||
|
|
||||||
updateTitleStats(): void {
|
updateTitleStats(): void {
|
||||||
@ -91,6 +98,8 @@ export default class TitleUiHandler extends OptionSelectUiHandler {
|
|||||||
this.splashMessage = Utils.randItem(getSplashMessages());
|
this.splashMessage = Utils.randItem(getSplashMessages());
|
||||||
this.splashMessageText.setText(i18next.t(this.splashMessage, { count: TitleUiHandler.BATTLES_WON_FALLBACK }));
|
this.splashMessageText.setText(i18next.t(this.splashMessage, { count: TitleUiHandler.BATTLES_WON_FALLBACK }));
|
||||||
|
|
||||||
|
this.appVersionText.setText("v"+version);
|
||||||
|
|
||||||
const ui = this.getUi();
|
const ui = this.getUi();
|
||||||
|
|
||||||
if (this.scene.eventManager.isEventActive()) {
|
if (this.scene.eventManager.isEventActive()) {
|
||||||
|
Loading…
Reference in New Issue
Block a user