Compare commits

...

22 Commits

Author SHA1 Message Date
ImperialSympathizer
08e023e220
Merge pull request #4142 from ben-lear/mystery-encounters-feedback
Mystery encounters feedback
2024-09-09 17:22:03 -04:00
ImperialSympathizer
e950386987 pull latest from beta 2024-09-09 17:21:28 -04:00
ImperialSympathizer
d77ec7bcd4 PR suggestions and fixees 2024-09-09 17:20:09 -04:00
ImperialSympathizer
b25a0966ea
Merge pull request #4141 from ben-lear/mystery-encounters-feedback
dialogue updates and test fixes for MEs
2024-09-09 17:06:55 -04:00
ImperialSympathizer
bffa5cacdf dialogue updates and test fixes for MEs 2024-09-09 17:06:16 -04:00
ImperialSympathizer
8dd16f40f9 dialogue updates and test fixes for MEs 2024-09-09 17:04:28 -04:00
Adrian T.
e959595471
[Test] Fix throat chop and gorilla tactics tests (#4140) 2024-09-09 16:03:29 -04:00
Jannik Tappert
d9a8448c6e
[Enhancement] Added the ability to localize the tera type hover text (#4138) 2024-09-09 15:57:07 -04:00
Adrian T.
a82d64b5b5
[Ability] Implement Gorilla Tactics (#4051)
* fully implement gorilla tactics

* fix atk increase

* update oversight

* add showAbility param

* fix postmerge

* fix postmerge

* update tests
2024-09-09 15:41:53 -04:00
Brandon Bay
9317093044
[Enhancement] [UI/UX] Add ability and passive tooltips to starter select screen (#4023)
* Add ability and passive tooltips to starter select screen

* Remove explicit casts to BattleScene

* Increase tooltip size, reverse y when necessary, and always show passive tooltip

* Add ability name to tooltip title and persist tooltips between Pokemon

* Use vi function mocks
2024-09-09 15:07:00 -04:00
Adrian T.
9afab182e9
[Test] Remove obsolete splash_only (#4139) 2024-09-09 15:02:11 -04:00
Adrian T.
401568609b
[Dev] Add imports, Handle kebab-case fileName argument in test boilerplate script (#4072)
* add imports, handle kebab-case fileName argument

* fix spacing
2024-09-09 13:00:26 -04:00
Taylor Le Lievre
8df7422e8f
[Bug] Primal weather no longer persists if last mon dies to indirect damage (#3492)
* Fixed Delta Stream remaining active when last mon dies to indirect damage

* Rebasing changes

* Linting fix

* Combined if statements

* Changed params to optional

* Added unit test

* Apply suggestions from code review

* Fix test and remove `.js` from import

---------

Co-authored-by: NightKev <34855794+DayKev@users.noreply.github.com>
2024-09-09 12:59:58 -04:00
NightKev
11d912bad8
[Dev] Make OPP_MOVESET_OVERRIDE fully override the enemy's moveset (#4062)
* Make `OPP_MOVESET_OVERRIDE` fully override the enemy's moveset

* Update tests with new override behavior

* Fix tests

* Fix another test

* Move overrides no longer required to be arrays

* Remove `SPLASH_ONLY` test utility variable

* Update moveset override helper functions

* Missed some tests
2024-09-09 12:55:11 -04:00
Adrian T.
c59f6edf36
[Move] Implement Power Shift (#4083)
* fully implement power shift

* cleanup
2024-09-09 12:54:31 -04:00
Leo Kim
80e347840d
[Bug] Fix line spacing in level up stats and move info in Japanese (#4095) 2024-09-09 12:54:17 -04:00
NightKev
89b33466a9
[Test] Fix Safeguard test that relied on a now-fixed bug (#4098) 2024-09-09 12:53:45 -04:00
Lugiad
3d01e905df
[Localization][UI/UX] Clean up of unused localized images (#4110)
* Delete public/images/ui/legacy/summary_moves_effect_de.png

* Delete public/images/ui/legacy/summary_moves_effect_es.png

* Delete public/images/ui/legacy/summary_moves_effect_fr.png

* Delete public/images/ui/legacy/summary_moves_effect_it.png

* Delete public/images/ui/legacy/summary_moves_effect_pt-BR.png

* Delete public/images/ui/legacy/summary_moves_effect_zh-CN.png
2024-09-09 12:53:22 -04:00
Adrian T.
3c05237b2e
[Move] Fully implement Throat Chop (#4115)
* fully implement throat chop

* add linkcode in docs

* address comments

* update test
2024-09-09 12:52:20 -04:00
José Ricardo
a88b989939
[Localization] [pt_BR] Updated some translations (#4131) 2024-09-09 12:50:47 -04:00
Chapybara-jp
e84fe8c24e
[Localisation] [JA] Translated/updated spacing in several files, fixed Ice Face bug (#4129)
* Update ability-trigger.json

* Update ability.json

* Update arena-flyout.json

* Update arena-tag.json

* Update battle.json

* Update fight-ui-handler.json

* Update berry.json

* Update menu.json

* Update party-ui-handler.json

* Update starter-select-ui-handler.json

* Update tutorial.json

* Update move.json

* Update battle.json

* Update arena-flyout.json

* Update arena-flyout.json

* Update arena-tag.json

* Update party-ui-handler.json

* Update settings.json
2024-09-09 12:50:30 -04:00
Mumble
26eb63cf67
[Refactor] Cleaning up Learn move phase (#3672)
* Learn Move Phase rewrite

* Typedocs

* messages with confirm do not need an extra button press no more

* Added Documentation

* This does not work

* so sad

* Some updates

* Eslint issues + clean up

* Additions to handle learning during evolution + test fixes

* some more checks

* Update src/overrides.ts

Co-authored-by: NightKev <34855794+DayKev@users.noreply.github.com>

* Update src/test/phases/learn-move-phase.test.ts

Co-authored-by: NightKev <34855794+DayKev@users.noreply.github.com>

* Added new function and updated tests

* Fixed bracketing and added parameter types

* Added Sketch to the conditional

* Added some fixes. Weird stuff going on.

* Whoops

* async implementation done

* Update src/phases/learn-move-phase.ts

Co-authored-by: NightKev <34855794+DayKev@users.noreply.github.com>

* Apply suggestions from code review

Co-authored-by: NightKev <34855794+DayKev@users.noreply.github.com>

* Made showText=> summary a promise

* adapt learn-move-phase to `async-await`

* await add

---------

Co-authored-by: frutescens <info@laptop>
Co-authored-by: NightKev <34855794+DayKev@users.noreply.github.com>
Co-authored-by: flx-sta <50131232+flx-sta@users.noreply.github.com>
2024-09-09 09:11:46 -07:00
208 changed files with 2571 additions and 1913 deletions

View File

@ -20,54 +20,58 @@ const type = args[0]; // "move" or "ability"
let fileName = args[1]; // The file name
if (!type || !fileName) {
console.error('Please provide both a type ("move", "ability", or "item") and a file name.');
process.exit(1);
console.error('Please provide a type ("move", "ability", or "item") and a file name.');
process.exit(1);
}
// Convert fileName from to snake_case if camelCase is given
fileName = fileName.replace(/([a-z])([A-Z])/g, '$1_$2').toLowerCase();
// Convert fileName from kebab-case or camelCase to snake_case
fileName = fileName
.replace(/-+/g, '_') // Convert kebab-case (dashes) to underscores
.replace(/([a-z])([A-Z])/g, '$1_$2') // Convert camelCase to snake_case
.toLowerCase(); // Ensure all lowercase
// Format the description for the test case
const formattedName = fileName
.replace(/_/g, ' ')
.replace(/\b\w/g, char => char.toUpperCase());
.replace(/_/g, ' ')
.replace(/\b\w/g, char => char.toUpperCase());
// Determine the directory based on the type
let dir;
let description;
if (type === 'move') {
dir = path.join(__dirname, 'src', 'test', 'moves');
description = `Moves - ${formattedName}`;
dir = path.join(__dirname, 'src', 'test', 'moves');
description = `Moves - ${formattedName}`;
} else if (type === 'ability') {
dir = path.join(__dirname, 'src', 'test', 'abilities');
description = `Abilities - ${formattedName}`;
dir = path.join(__dirname, 'src', 'test', 'abilities');
description = `Abilities - ${formattedName}`;
} else if (type === "item") {
dir = path.join(__dirname, 'src', 'test', 'items');
description = `Items - ${formattedName}`;
dir = path.join(__dirname, 'src', 'test', 'items');
description = `Items - ${formattedName}`;
} else {
console.error('Invalid type. Please use "move", "ability", or "item".');
process.exit(1);
console.error('Invalid type. Please use "move", "ability", or "item".');
process.exit(1);
}
// Ensure the directory exists
if (!fs.existsSync(dir)) {
fs.mkdirSync(dir, { recursive: true });
fs.mkdirSync(dir, { recursive: true });
}
// Create the file with the given name
const filePath = path.join(dir, `${fileName}.test.ts`);
if (fs.existsSync(filePath)) {
console.error(`File "${fileName}.test.ts" already exists.`);
process.exit(1);
console.error(`File "${fileName}.test.ts" already exists.`);
process.exit(1);
}
// Define the content template
const content = `import { Abilities } from "#enums/abilities";
import { Moves } from "#enums/moves";
import { Species } from "#enums/species";
import GameManager from "#test/utils/gameManager";
import { SPLASH_ONLY } from "#test/utils/testUtils";
import Phaser from "phaser";
import { afterEach, beforeAll, beforeEach, describe, it } from "vitest";
import { afterEach, beforeAll, beforeEach, describe, it, expect } from "vitest";
describe("${description}", () => {
let phaserGame: Phaser.Game;
@ -87,14 +91,15 @@ describe("${description}", () => {
beforeEach(() => {
game = new GameManager(phaserGame);
game.override
.moveset([Moves.SPLASH])
.battleType("single")
.enemyAbility(Abilities.BALL_FETCH)
.enemyMoveset(SPLASH_ONLY);
.enemyMoveset(Moves.SPLASH);
});
it("test case", async () => {
// await game.classicMode.startBattle();
// game.move.select();
// await game.classicMode.startBattle([Species.MAGIKARP]);
// game.move.select(Moves.SPLASH);
}, TIMEOUT);
});
`;

Binary file not shown.

Before

Width:  |  Height:  |  Size: 799 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 807 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 799 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 800 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 799 B

View File

@ -95,7 +95,7 @@ import { ToggleDoublePositionPhase } from "./phases/toggle-double-position-phase
import { TurnInitPhase } from "./phases/turn-init-phase";
import { ShopCursorTarget } from "./enums/shop-cursor-target";
import MysteryEncounter from "./data/mystery-encounters/mystery-encounter";
import { allMysteryEncounters, ANTI_VARIANCE_WEIGHT_MODIFIER, AVERAGE_ENCOUNTERS_PER_RUN_TARGET, BASE_MYSTERY_ENCOUNTER_SPAWN_WEIGHT, mysteryEncountersByBiome, WEIGHT_INCREMENT_ON_SPAWN_MISS } from "./data/mystery-encounters/mystery-encounters";
import { allMysteryEncounters, ANTI_VARIANCE_WEIGHT_MODIFIER, AVERAGE_ENCOUNTERS_PER_RUN_TARGET, BASE_MYSTERY_ENCOUNTER_SPAWN_WEIGHT, MYSTERY_ENCOUNTER_SPAWN_MAX_WEIGHT, mysteryEncountersByBiome, WEIGHT_INCREMENT_ON_SPAWN_MISS } from "./data/mystery-encounters/mystery-encounters";
import { MysteryEncounterSaveData } from "#app/data/mystery-encounters/mystery-encounter-save-data";
import { MysteryEncounterType } from "#enums/mystery-encounter-type";
import { MysteryEncounterTier } from "#enums/mystery-encounter-tier";
@ -256,7 +256,7 @@ export default class BattleScene extends SceneBase {
public money: integer;
public pokemonInfoContainer: PokemonInfoContainer;
private party: PlayerPokemon[];
public mysteryEncounterSaveData: MysteryEncounterSaveData = new MysteryEncounterSaveData(null);
public mysteryEncounterSaveData: MysteryEncounterSaveData = new MysteryEncounterSaveData();
public lastMysteryEncounter?: MysteryEncounter;
/** Combined Biome and Wave count text */
private biomeWaveText: Phaser.GameObjects.Text;
@ -411,7 +411,6 @@ export default class BattleScene extends SceneBase {
this.fieldUI = fieldUI;
// @ts-ignore
const transition = this.make.rexTransitionImagePack({
x: 0,
y: 0,
@ -1171,10 +1170,10 @@ export default class BattleScene extends SceneBase {
// Check for mystery encounter
// Can only occur in place of a standard (non-boss) wild battle, waves 10-180
const highestMysteryEncounterWave = 180;
const lowestMysteryEncounterWave = 10;
const highestMysteryEncounterWave = this.gameMode.maxMysteryEncounterWave;
const lowestMysteryEncounterWave = this.gameMode.minMysteryEncounterWave;
if (this.gameMode.hasMysteryEncounters && newBattleType === BattleType.WILD && !this.gameMode.isBoss(newWaveIndex) && newWaveIndex < highestMysteryEncounterWave && newWaveIndex > lowestMysteryEncounterWave) {
const roll = Utils.randSeedInt(256);
const roll = Utils.randSeedInt(MYSTERY_ENCOUNTER_SPAWN_MAX_WEIGHT);
// Base spawn weight is BASE_MYSTERY_ENCOUNTER_SPAWN_WEIGHT/256, and increases by WEIGHT_INCREMENT_ON_SPAWN_MISS/256 for each missed attempt at spawning an encounter on a valid floor
const sessionEncounterRate = this.mysteryEncounterSaveData.encounterSpawnChance;
@ -1254,7 +1253,7 @@ export default class BattleScene extends SceneBase {
const isEndlessFifthWave = this.gameMode.hasShortBiomes && (lastBattle.waveIndex % 5) === 0;
const isWaveIndexMultipleOfFiftyMinusOne = (lastBattle.waveIndex % 50) === 49;
const isNewBiome = isWaveIndexMultipleOfTen || isEndlessFifthWave || (isEndlessOrDaily && isWaveIndexMultipleOfFiftyMinusOne);
const resetArenaState = isNewBiome || this.currentBattle.battleType === BattleType.TRAINER || this.currentBattle.battleType === BattleType.MYSTERY_ENCOUNTER || this.currentBattle.battleSpec === BattleSpec.FINAL_BOSS;
const resetArenaState = isNewBiome || [BattleType.TRAINER, BattleType.MYSTERY_ENCOUNTER].includes(this.currentBattle.battleType) || this.currentBattle.battleSpec === BattleSpec.FINAL_BOSS;
this.getEnemyParty().forEach(enemyPokemon => enemyPokemon.destroy());
this.trySpreadPokerus();
if (!isNewBiome && (newWaveIndex % 10) === 5) {

View File

@ -1595,8 +1595,8 @@ export class PostAttackAbAttr extends AbAttr {
private attackCondition: PokemonAttackCondition;
/** The default attackCondition requires that the selected move is a damaging move */
constructor(attackCondition: PokemonAttackCondition = (user, target, move) => (move.category !== MoveCategory.STATUS)) {
super();
constructor(attackCondition: PokemonAttackCondition = (user, target, move) => (move.category !== MoveCategory.STATUS), showAbility: boolean = true) {
super(showAbility);
this.attackCondition = attackCondition;
}
@ -1624,6 +1624,40 @@ export class PostAttackAbAttr extends AbAttr {
}
}
/**
* Ability attribute for Gorilla Tactics
* @extends PostAttackAbAttr
*/
export class GorillaTacticsAbAttr extends PostAttackAbAttr {
constructor() {
super((user, target, move) => true, false);
}
/**
*
* @param {Pokemon} pokemon the {@linkcode Pokemon} with this ability
* @param passive n/a
* @param simulated whether the ability is being simulated
* @param defender n/a
* @param move n/a
* @param hitResult n/a
* @param args n/a
* @returns `true` if the ability is applied
*/
applyPostAttackAfterMoveTypeCheck(pokemon: Pokemon, passive: boolean, simulated: boolean, defender: Pokemon, move: Move, hitResult: HitResult | null, args: any[]): boolean | Promise<boolean> {
if (simulated) {
return simulated;
}
if (pokemon.getTag(BattlerTagType.GORILLA_TACTICS)) {
return false;
}
pokemon.addTag(BattlerTagType.GORILLA_TACTICS);
return true;
}
}
export class PostAttackStealHeldItemAbAttr extends PostAttackAbAttr {
private stealCondition: PokemonAttackCondition | null;
@ -3923,7 +3957,7 @@ export class PostBattleLootAbAttr extends PostBattleAbAttr {
}
export class PostFaintAbAttr extends AbAttr {
applyPostFaint(pokemon: Pokemon, passive: boolean, simulated: boolean, attacker: Pokemon, move: Move, hitResult: HitResult, args: any[]): boolean {
applyPostFaint(pokemon: Pokemon, passive: boolean, simulated: boolean, attacker?: Pokemon, move?: Move, hitResult?: HitResult, ...args: any[]): boolean {
return false;
}
}
@ -3974,7 +4008,7 @@ export class PostFaintClearWeatherAbAttr extends PostFaintAbAttr {
* @param args N/A
* @returns {boolean} Returns true if the weather clears, otherwise false.
*/
applyPostFaint(pokemon: Pokemon, passive: boolean, simulated: boolean, attacker: Pokemon, move: Move, hitResult: HitResult, args: any[]): boolean {
applyPostFaint(pokemon: Pokemon, passive: boolean, simulated: boolean, attacker?: Pokemon, move?: Move, hitResult?: HitResult, ...args: any[]): boolean {
const weatherType = pokemon.scene.arena.weather?.weatherType;
let turnOffWeather = false;
@ -4022,8 +4056,8 @@ export class PostFaintContactDamageAbAttr extends PostFaintAbAttr {
this.damageRatio = damageRatio;
}
applyPostFaint(pokemon: Pokemon, passive: boolean, simulated: boolean, attacker: Pokemon, move: Move, hitResult: HitResult, args: any[]): boolean {
if (move.checkFlag(MoveFlags.MAKES_CONTACT, attacker, pokemon)) {
applyPostFaint(pokemon: Pokemon, passive: boolean, simulated: boolean, attacker?: Pokemon, move?: Move, hitResult?: HitResult, ...args: any[]): boolean {
if (move !== undefined && attacker !== undefined && move.checkFlag(MoveFlags.MAKES_CONTACT, attacker, pokemon)) { //If the mon didn't die to indirect damage
const cancelled = new Utils.BooleanHolder(false);
pokemon.scene.getField(true).map(p => applyAbAttrs(FieldPreventExplosiveMovesAbAttr, p, cancelled, simulated));
if (cancelled.value || attacker.hasAbilityWithAttr(BlockNonDirectDamageAbAttr)) {
@ -4052,8 +4086,8 @@ export class PostFaintHPDamageAbAttr extends PostFaintAbAttr {
super ();
}
applyPostFaint(pokemon: Pokemon, passive: boolean, simulated: boolean, attacker: Pokemon, move: Move, hitResult: HitResult, args: any[]): boolean {
if (!simulated) {
applyPostFaint(pokemon: Pokemon, passive: boolean, simulated: boolean, attacker?: Pokemon, move?: Move, hitResult?: HitResult, ...args: any[]): boolean {
if (move !== undefined && attacker !== undefined && !simulated) { //If the mon didn't die to indirect damage
const damage = pokemon.turnData.attacksReceived[0].damage;
attacker.damageAndUpdate((damage), HitResult.OTHER);
attacker.turnData.damageTaken += damage;
@ -4711,7 +4745,7 @@ export function applyPostBattleAbAttrs(attrType: Constructor<PostBattleAbAttr>,
}
export function applyPostFaintAbAttrs(attrType: Constructor<PostFaintAbAttr>,
pokemon: Pokemon, attacker: Pokemon, move: Move, hitResult: HitResult, simulated: boolean = false, ...args: any[]): Promise<void> {
pokemon: Pokemon, attacker?: Pokemon, move?: Move, hitResult?: HitResult, simulated: boolean = false, ...args: any[]): Promise<void> {
return applyAbAttrsInternal<PostFaintAbAttr>(attrType, pokemon, (attr, passive) => attr.applyPostFaint(pokemon, passive, simulated, attacker, move, hitResult, args), args, false, simulated);
}
@ -5597,7 +5631,7 @@ export function initAbilities() {
.bypassFaint()
.partial(),
new Ability(Abilities.GORILLA_TACTICS, 8)
.unimplemented(),
.attr(GorillaTacticsAbAttr),
new Ability(Abilities.NEUTRALIZING_GAS, 8)
.attr(SuppressFieldAbilitiesAbAttr)
.attr(UncopiableAbilityAbAttr)

View File

@ -107,8 +107,8 @@ export interface TerrainBattlerTag {
* to select restricted moves.
*/
export abstract class MoveRestrictionBattlerTag extends BattlerTag {
constructor(tagType: BattlerTagType, turnCount: integer, sourceMove?: Moves, sourceId?: integer) {
super(tagType, [ BattlerTagLapseType.PRE_MOVE, BattlerTagLapseType.TURN_END ], turnCount, sourceMove, sourceId);
constructor(tagType: BattlerTagType, lapseType: BattlerTagLapseType | BattlerTagLapseType[], turnCount: integer, sourceMove?: Moves, sourceId?: integer) {
super(tagType, lapseType, turnCount, sourceMove, sourceId);
}
/** @override */
@ -119,7 +119,9 @@ export abstract class MoveRestrictionBattlerTag extends BattlerTag {
const move = phase.move;
if (this.isMoveRestricted(move.moveId)) {
pokemon.scene.queueMessage(this.interruptedText(pokemon, move.moveId));
if (this.interruptedText(pokemon, move.moveId)) {
pokemon.scene.queueMessage(this.interruptedText(pokemon, move.moveId));
}
phase.cancel();
}
@ -155,7 +157,52 @@ export abstract class MoveRestrictionBattlerTag extends BattlerTag {
* @param {Moves} move {@linkcode Moves} ID of the move being interrupted
* @returns {string} text to display when the move is interrupted
*/
abstract interruptedText(pokemon: Pokemon, move: Moves): string;
interruptedText(pokemon: Pokemon, move: Moves): string {
return "";
}
}
/**
* Tag representing the "Throat Chop" effect. Pokemon with this tag cannot use sound-based moves.
* @see {@link https://bulbapedia.bulbagarden.net/wiki/Throat_Chop_(move) | Throat Chop}
* @extends MoveRestrictionBattlerTag
*/
export class ThroatChoppedTag extends MoveRestrictionBattlerTag {
constructor() {
super(BattlerTagType.THROAT_CHOPPED, [ BattlerTagLapseType.TURN_END, BattlerTagLapseType.PRE_MOVE ], 2, Moves.THROAT_CHOP);
}
/**
* Checks if a {@linkcode Moves | move} is restricted by Throat Chop.
* @override
* @param {Moves} move the {@linkcode Moves | move} to check for sound-based restriction
* @returns true if the move is sound-based
*/
override isMoveRestricted(move: Moves): boolean {
return allMoves[move].hasFlag(MoveFlags.SOUND_BASED);
}
/**
* Shows a message when the player attempts to select a move that is restricted by Throat Chop.
* @override
* @param {Pokemon} pokemon the {@linkcode Pokemon} that is attempting to select the restricted move
* @param {Moves} move the {@linkcode Moves | move} that is being restricted
* @returns the message to display when the player attempts to select the restricted move
*/
override selectionDeniedText(pokemon: Pokemon, move: Moves): string {
return i18next.t("battle:moveCannotBeSelected", { moveName: allMoves[move].name });
}
/**
* Shows a message when a move is interrupted by Throat Chop.
* @override
* @param {Pokemon} pokemon the interrupted {@linkcode Pokemon}
* @param {Moves} move the {@linkcode Moves | move} that was interrupted
* @returns the message to display when the move is interrupted
*/
override interruptedText(pokemon: Pokemon, move: Moves): string {
return i18next.t("battle:throatChopInterruptedMove", { pokemonName: getPokemonNameWithAffix(pokemon) });
}
}
/**
@ -167,7 +214,7 @@ export class DisabledTag extends MoveRestrictionBattlerTag {
private moveId: Moves = Moves.NONE;
constructor(sourceId: number) {
super(BattlerTagType.DISABLED, 4, Moves.DISABLE, sourceId);
super(BattlerTagType.DISABLED, [ BattlerTagLapseType.PRE_MOVE, BattlerTagLapseType.TURN_END ], 4, Moves.DISABLE, sourceId);
}
/** @override */
@ -178,7 +225,7 @@ export class DisabledTag extends MoveRestrictionBattlerTag {
/**
* @override
*
* Ensures that move history exists on `pokemon` and has a valid move. If so, sets the {@link moveId} and shows a message.
* Ensures that move history exists on `pokemon` and has a valid move. If so, sets the {@linkcode moveId} and shows a message.
* Otherwise the move ID will not get assigned and this tag will get removed next turn.
*/
override onAdd(pokemon: Pokemon): void {
@ -207,7 +254,12 @@ export class DisabledTag extends MoveRestrictionBattlerTag {
return i18next.t("battle:moveDisabled", { moveName: allMoves[move].name });
}
/** @override */
/**
* @override
* @param {Pokemon} pokemon {@linkcode Pokemon} attempting to use the restricted move
* @param {Moves} move {@linkcode Moves} ID of the move being interrupted
* @returns {string} text to display when the move is interrupted
*/
override interruptedText(pokemon: Pokemon, move: Moves): string {
return i18next.t("battle:disableInterruptedMove", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), moveName: allMoves[move].name });
}
@ -219,6 +271,72 @@ export class DisabledTag extends MoveRestrictionBattlerTag {
}
}
/**
* Tag used by Gorilla Tactics to restrict the user to using only one move.
* @extends MoveRestrictionBattlerTag
*/
export class GorillaTacticsTag extends MoveRestrictionBattlerTag {
private moveId = Moves.NONE;
constructor() {
super(BattlerTagType.GORILLA_TACTICS, BattlerTagLapseType.CUSTOM, 0);
}
/** @override */
override isMoveRestricted(move: Moves): boolean {
return move !== this.moveId;
}
/**
* @override
* @param {Pokemon} pokemon the {@linkcode Pokemon} to check if the tag can be added
* @returns `true` if the pokemon has a valid move and no existing {@linkcode GorillaTacticsTag}; `false` otherwise
*/
override canAdd(pokemon: Pokemon): boolean {
return (this.getLastValidMove(pokemon) !== undefined) && !pokemon.getTag(GorillaTacticsTag);
}
/**
* Ensures that move history exists on {@linkcode Pokemon} and has a valid move.
* If so, sets the {@linkcode moveId} and increases the user's Attack by 50%.
* @override
* @param {Pokemon} pokemon the {@linkcode Pokemon} to add the tag to
*/
override onAdd(pokemon: Pokemon): void {
const lastValidMove = this.getLastValidMove(pokemon);
if (!lastValidMove) {
return;
}
this.moveId = lastValidMove;
pokemon.setStat(Stat.ATK, pokemon.getStat(Stat.ATK, false) * 1.5, false);
}
/**
*
* @override
* @param {Pokemon} pokemon n/a
* @param {Moves} move {@linkcode Moves} ID of the move being denied
* @returns {string} text to display when the move is denied
*/
override selectionDeniedText(pokemon: Pokemon, move: Moves): string {
return i18next.t("battle:canOnlyUseMove", { moveName: allMoves[this.moveId].name, pokemonName: getPokemonNameWithAffix(pokemon) });
}
/**
* Gets the last valid move from the pokemon's move history.
* @param {Pokemon} pokemon {@linkcode Pokemon} to get the last valid move from
* @returns {Moves | undefined} the last valid move from the pokemon's move history
*/
getLastValidMove(pokemon: Pokemon): Moves | undefined {
const move = pokemon.getLastXMoves()
.find(m => m.move !== Moves.NONE && m.move !== Moves.STRUGGLE && !m.virtual);
return move?.move;
}
}
/**
* BattlerTag that represents the "recharge" effects of moves like Hyper Beam.
*/
@ -2194,6 +2312,10 @@ export function getBattlerTag(tagType: BattlerTagType, turnCount: number, source
return new GulpMissileTag(tagType, sourceMove);
case BattlerTagType.TAR_SHOT:
return new TarShotTag();
case BattlerTagType.THROAT_CHOPPED:
return new ThroatChoppedTag();
case BattlerTagType.GORILLA_TACTICS:
return new GorillaTacticsTag();
case BattlerTagType.MYSTERY_ENCOUNTER_POST_SUMMON:
return new MysteryEncounterPostSummonTag(sourceMove);
case BattlerTagType.NONE:

View File

@ -5987,9 +5987,8 @@ export class SwapStatAttr extends MoveEffectAttr {
}
/**
* Takes the average of the user's and target's corresponding current
* {@linkcode stat} values and sets that stat to the average for both
* temporarily.
* Swaps the user's and target's corresponding current
* {@linkcode EffectiveStat | stat} values
* @param user the {@linkcode Pokemon} that used the move
* @param target the {@linkcode Pokemon} that the move was used on
* @param move N/A
@ -6013,6 +6012,62 @@ export class SwapStatAttr extends MoveEffectAttr {
}
}
/**
* Attribute used to switch the user's own stats.
* Used by Power Shift.
* @extends MoveEffectAttr
*/
export class ShiftStatAttr extends MoveEffectAttr {
private statToSwitch: EffectiveStat;
private statToSwitchWith: EffectiveStat;
constructor(statToSwitch: EffectiveStat, statToSwitchWith: EffectiveStat) {
super();
this.statToSwitch = statToSwitch;
this.statToSwitchWith = statToSwitchWith;
}
/**
* Switches the user's stats based on the {@linkcode statToSwitch} and {@linkcode statToSwitchWith} attributes.
* @param {Pokemon} user the {@linkcode Pokemon} that used the move
* @param target n/a
* @param move n/a
* @param args n/a
* @returns whether the effect was applied
*/
override apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean {
if (!super.apply(user, target, move, args)) {
return false;
}
const firstStat = user.getStat(this.statToSwitch, false);
const secondStat = user.getStat(this.statToSwitchWith, false);
user.setStat(this.statToSwitch, secondStat, false);
user.setStat(this.statToSwitchWith, firstStat, false);
user.scene.queueMessage(i18next.t("moveTriggers:shiftedStats", {
pokemonName: getPokemonNameWithAffix(user),
statToSwitch: i18next.t(getStatKey(this.statToSwitch)),
statToSwitchWith: i18next.t(getStatKey(this.statToSwitchWith))
}));
return true;
}
/**
* Encourages the user to use the move if the stat to switch with is greater than the stat to switch.
* @param {Pokemon} user the {@linkcode Pokemon} that used the move
* @param target n/a
* @param move n/a
* @returns number of points to add to the user's benefit score
*/
override getUserBenefitScore(user: Pokemon, target: Pokemon, move: Move): integer {
return user.getStat(this.statToSwitchWith, false) > user.getStat(this.statToSwitch, false) ? 10 : 0;
}
}
/**
* Attribute used for status moves, namely Power Split and Guard Split,
* that take the average of a user's and target's corresponding
@ -8404,7 +8459,7 @@ export function initMoves() {
.target(MoveTarget.USER_AND_ALLIES)
.condition((user, target, move) => !![ user, user.getAlly() ].filter(p => p?.isActive()).find(p => !![ Abilities.PLUS, Abilities.MINUS].find(a => p.hasAbility(a, false)))),
new AttackMove(Moves.THROAT_CHOP, Type.DARK, MoveCategory.PHYSICAL, 80, 100, 15, 100, 0, 7)
.partial(),
.attr(AddBattlerTagAttr, BattlerTagType.THROAT_CHOPPED),
new AttackMove(Moves.POLLEN_PUFF, Type.BUG, MoveCategory.SPECIAL, 90, 100, 15, -1, 0, 7)
.attr(StatusCategoryOnAllyAttr)
.attr(HealOnAllyAttr, 0.5, true, false)
@ -8894,7 +8949,8 @@ export function initMoves() {
new AttackMove(Moves.PSYSHIELD_BASH, Type.PSYCHIC, MoveCategory.PHYSICAL, 70, 90, 10, 100, 0, 8)
.attr(StatStageChangeAttr, [ Stat.DEF ], 1, true),
new SelfStatusMove(Moves.POWER_SHIFT, Type.NORMAL, -1, 10, -1, 0, 8)
.unimplemented(),
.target(MoveTarget.USER)
.attr(ShiftStatAttr, Stat.ATK, Stat.DEF),
new AttackMove(Moves.STONE_AXE, Type.ROCK, MoveCategory.PHYSICAL, 65, 90, 15, 100, 0, 8)
.attr(AddArenaTrapTagHitAttr, ArenaTagType.STEALTH_ROCK)
.slicingMove(),

View File

@ -225,6 +225,7 @@ export const AbsoluteAvariceEncounter: MysteryEncounter =
};
encounter.enemyPartyConfigs = [config];
encounter.setDialogueToken("greedentName", getPokemonSpecies(Species.GREEDENT).getName());
return true;
})
@ -251,6 +252,9 @@ export const AbsoluteAvariceEncounter: MysteryEncounter =
party.forEach(p => {
if (revSeed) {
const seedModifier = revSeed.newModifier(p);
if (seedModifier) {
encounter.setDialogueToken("foodReward", seedModifier.type.name);
}
scene.addModifier(seedModifier, false, false, false, true);
}
});

View File

@ -83,6 +83,8 @@ export const AnOfferYouCantRefuseEncounter: MysteryEncounter =
}
}
encounter.setDialogueToken("liepardName", getPokemonSpecies(Species.LIEPARD).getName());
return true;
})
.withOption(

View File

@ -1,5 +1,5 @@
import {
EnemyPartyConfig,
EnemyPartyConfig, generateModifierType,
generateModifierTypeOption,
initBattleWithEnemyConfig,
leaveEncounterWithoutBattle,
@ -223,6 +223,15 @@ export const BugTypeSuperfanEncounter: MysteryEncounter =
},
];
const requiredItems = [
generateModifierType(scene, modifierTypes.QUICK_CLAW),
generateModifierType(scene, modifierTypes.GRIP_CLAW),
generateModifierType(scene, modifierTypes.ATTACK_TYPE_BOOSTER, [Type.BUG]),
];
const requiredItemString = requiredItems.map(m => m?.name ?? "unknown").join("/");
encounter.setDialogueToken("requiredBugItems", requiredItemString);
return true;
})
.withTitle(`${namespace}.title`)
@ -361,6 +370,7 @@ export const BugTypeSuperfanEncounter: MysteryEncounter =
})
.withPreOptionPhase(async (scene: BattleScene): Promise<boolean> => {
const encounter = scene.currentBattle.mysteryEncounter!;
const onPokemonSelected = (pokemon: PlayerPokemon) => {
// Get Pokemon held items and filter for valid ones
const validItems = pokemon.getHeldItems().filter(item => {

View File

@ -129,7 +129,7 @@ export const ClowningAroundEncounter: MysteryEncounter =
},
{ // Blacephalon has the random ability from pool, and 2 entirely random types to fit with the theme of the encounter
species: getPokemonSpecies(Species.BLACEPHALON),
mysteryEncounterPokemonData: new MysteryEncounterPokemonData(undefined, ability, undefined, [randSeedInt(18), randSeedInt(18)]),
mysteryEncounterPokemonData: new MysteryEncounterPokemonData({ ability: ability, types: [randSeedInt(18), randSeedInt(18)] }),
isBoss: true,
moveSet: [Moves.TRICK, Moves.HYPNOSIS, Moves.SHADOW_BALL, Moves.MIND_BLOWN]
},
@ -140,6 +140,8 @@ export const ClowningAroundEncounter: MysteryEncounter =
// Load animations/sfx for start of fight moves
loadCustomMovesForEncounter(scene, [Moves.ROLE_PLAY, Moves.TAUNT]);
encounter.setDialogueToken("blacephalonName", getPokemonSpecies(Species.BLACEPHALON).getName());
return true;
})
.withTitle(`${namespace}.title`)
@ -345,10 +347,9 @@ export const ClowningAroundEncounter: MysteryEncounter =
}
newTypes.push(secondType);
if (!pokemon.mysteryEncounterPokemonData) {
pokemon.mysteryEncounterPokemonData = new MysteryEncounterPokemonData(undefined, undefined, undefined, newTypes);
} else {
pokemon.mysteryEncounterPokemonData.types = newTypes;
pokemon.mysteryEncounterPokemonData = new MysteryEncounterPokemonData();
}
pokemon.mysteryEncounterPokemonData.types = newTypes;
}
})
.withOptionPhase(async (scene: BattleScene) => {
@ -410,11 +411,12 @@ function displayYesNoOptions(scene: BattleScene, resolve) {
function onYesAbilitySwap(scene: BattleScene, resolve) {
const onPokemonSelected = (pokemon: PlayerPokemon) => {
// Do ability swap
const encounter = scene.currentBattle.mysteryEncounter!;
if (!pokemon.mysteryEncounterPokemonData) {
pokemon.mysteryEncounterPokemonData = new MysteryEncounterPokemonData(undefined, Abilities.AERILATE);
pokemon.mysteryEncounterPokemonData = new MysteryEncounterPokemonData();
}
pokemon.mysteryEncounterPokemonData.ability = scene.currentBattle.mysteryEncounter!.misc.ability;
scene.currentBattle.mysteryEncounter!.setDialogueToken("chosenPokemon", pokemon.getNameToRender());
pokemon.mysteryEncounterPokemonData.ability = encounter.misc.ability;
encounter.setDialogueToken("chosenPokemon", pokemon.getNameToRender());
scene.ui.setMode(Mode.MESSAGE).then(() => resolve(true));
};

View File

@ -162,6 +162,8 @@ export const DancingLessonsEncounter: MysteryEncounter =
oricorioData
};
encounter.setDialogueToken("oricorioName", getPokemonSpecies(Species.ORICORIO).getName());
return true;
})
.withOption(

View File

@ -15,6 +15,7 @@ import { OptionSelectItem } from "#app/ui/abstact-option-select-ui-handler";
import { applyModifierTypeToPlayerPokemon } from "#app/data/mystery-encounters/utils/encounter-pokemon-utils";
import i18next from "#app/plugins/i18n";
import { ModifierRewardPhase } from "#app/phases/modifier-reward-phase";
import { getPokemonSpecies } from "#app/data/pokemon-species";
/** the i18n namespace for this encounter */
const namespace = "mysteryEncounter:delibirdy";
@ -88,6 +89,11 @@ export const DelibirdyEncounter: MysteryEncounter =
text: `${namespace}.outro`,
}
])
.withOnInit((scene: BattleScene) => {
const encounter = scene.currentBattle.mysteryEncounter!;
encounter.setDialogueToken("delibirdName", getPokemonSpecies(Species.DELIBIRD).getName());
return true;
})
.withOption(
MysteryEncounterOptionBuilder
.newOptionWithMode(MysteryEncounterOptionMode.DISABLED_OR_DEFAULT)

View File

@ -100,6 +100,8 @@ export const FieryFalloutEncounter: MysteryEncounter =
scene.arena.trySetWeather(WeatherType.SUNNY, true);
encounter.setDialogueToken("volcaronaName", getPokemonSpecies(Species.VOLCARONA).getName());
return true;
})
.withOnVisualsStart((scene: BattleScene) => {

View File

@ -77,7 +77,9 @@ export const FunAndGamesEncounter: MysteryEncounter =
.withDescription(`${namespace}.description`)
.withQuery(`${namespace}.query`)
.withOnInit((scene: BattleScene) => {
const encounter = scene.currentBattle.mysteryEncounter!;
scene.loadBgm("mystery_encounter_fun_and_games", "mystery_encounter_fun_and_games.mp3");
encounter.setDialogueToken("wobbuffetName", getPokemonSpecies(Species.WOBBUFFET).getName());
return true;
})
.withOnVisualsStart((scene: BattleScene) => {

View File

@ -215,7 +215,8 @@ export const GlobalTradeSystemEncounter: MysteryEncounter =
const encounter = scene.currentBattle.mysteryEncounter!;
const onPokemonSelected = (pokemon: PlayerPokemon) => {
// Randomly generate a Wonder Trade pokemon
const randomTradeOption = generateTradeOption(scene.getParty().map(p => p.species));
// const randomTradeOption = generateTradeOption(scene.getParty().map(p => p.species));
const randomTradeOption = getPokemonSpecies(Species.BURMY);
const tradePokemon = new EnemyPokemon(scene, randomTradeOption, pokemon.level, TrainerSlot.NONE, false);
// Extra shiny roll at 1/128 odds (boosted by events and charms)
if (!tradePokemon.shiny) {
@ -265,7 +266,7 @@ export const GlobalTradeSystemEncounter: MysteryEncounter =
receivedPokemonData.passive = tradedPokemon.passive;
receivedPokemonData.pokeball = randSeedInt(5);
const dataSource = new PokemonData(receivedPokemonData);
const newPlayerPokemon = scene.addPlayerPokemon(receivedPokemonData.species, receivedPokemonData.level, undefined, undefined, undefined, undefined, undefined, undefined, undefined, dataSource);
const newPlayerPokemon = scene.addPlayerPokemon(receivedPokemonData.species, receivedPokemonData.level, dataSource.abilityIndex, dataSource.formIndex, dataSource.gender, dataSource.shiny, dataSource.variant, dataSource.ivs, dataSource.nature, dataSource);
scene.getParty().push(newPlayerPokemon);
await newPlayerPokemon.loadAssets();

View File

@ -1,5 +1,5 @@
import { queueEncounterMessage, showEncounterText } from "#app/data/mystery-encounters/utils/encounter-dialogue-utils";
import { EnemyPartyConfig, initBattleWithEnemyConfig, leaveEncounterWithoutBattle, loadCustomMovesForEncounter, setEncounterRewards, transitionMysteryEncounterIntroVisuals } from "#app/data/mystery-encounters/utils/encounter-phase-utils";
import { EnemyPartyConfig, initBattleWithEnemyConfig, leaveEncounterWithoutBattle, setEncounterRewards, transitionMysteryEncounterIntroVisuals } from "#app/data/mystery-encounters/utils/encounter-phase-utils";
import { getHighestLevelPlayerPokemon, koPlayerPokemon } from "#app/data/mystery-encounters/utils/encounter-pokemon-utils";
import { ModifierTier } from "#app/modifier/modifier-tier";
import { randSeedInt } from "#app/utils.js";
@ -60,7 +60,7 @@ export const MysteriousChestEncounter: MysteryEncounter =
// Calculate boss mon
const config: EnemyPartyConfig = {
levelAdditiveMultiplier: 1,
levelAdditiveMultiplier: 0.5,
disableSwitch: true,
pokemonConfigs: [
{
@ -74,7 +74,7 @@ export const MysteriousChestEncounter: MysteryEncounter =
encounter.enemyPartyConfigs = [config];
loadCustomMovesForEncounter(scene, [Moves.CONFUSE_RAY, Moves.ASTONISH]);
encounter.setDialogueToken("gimmighoulName", getPokemonSpecies(Species.GIMMIGHOUL).getName());
return true;
})

View File

@ -68,6 +68,8 @@ export const SlumberingSnorlaxEncounter: MysteryEncounter =
// Load animations/sfx for Snorlax fight start moves
loadCustomMovesForEncounter(scene, [Moves.SNORE]);
encounter.setDialogueToken("snorlaxName", getPokemonSpecies(Species.SNORLAX).getName());
return true;
})
.withTitle(`${namespace}.title`)

View File

@ -76,7 +76,7 @@ export const TheStrongStuffEncounter: MysteryEncounter =
species: getPokemonSpecies(Species.SHUCKLE),
isBoss: true,
bossSegments: 5,
mysteryEncounterPokemonData: new MysteryEncounterPokemonData(1.25),
mysteryEncounterPokemonData: new MysteryEncounterPokemonData({ spriteScale: 1.25 }),
nature: Nature.BOLD,
moveSet: [Moves.INFESTATION, Moves.SALT_CURE, Moves.GASTRO_ACID, Moves.HEAL_ORDER],
modifierConfigs: [
@ -110,6 +110,8 @@ export const TheStrongStuffEncounter: MysteryEncounter =
loadCustomMovesForEncounter(scene, [Moves.GASTRO_ACID, Moves.STEALTH_ROCK]);
encounter.setDialogueToken("shuckleName", getPokemonSpecies(Species.SHUCKLE).getName());
return true;
})
.withTitle(`${namespace}.title`)

View File

@ -77,6 +77,7 @@ export const TrashToTreasureEncounter: MysteryEncounter =
scene.loadSe("PRSFX- Dig2", "battle_anims", "PRSFX- Dig2.wav");
scene.loadSe("PRSFX- Venom Drench", "battle_anims", "PRSFX- Venom Drench.wav");
return true;
})
.withOption(

View File

@ -174,16 +174,17 @@ export const UncommonBreedEncounter: MysteryEncounter =
// Remove 4 random berries from player's party
// Get all player berry items, remove from party, and store reference
let berryItems: BerryModifier[] = scene.findModifiers(m => m instanceof BerryModifier) as BerryModifier[];
const berryItems: BerryModifier[]= scene.findModifiers(m => m instanceof BerryModifier) as BerryModifier[];
for (let i = 0; i < 4; i++) {
berryItems = scene.findModifiers(m => m instanceof BerryModifier) as BerryModifier[];
const randBerry = berryItems[randSeedInt(berryItems.length)];
const index = randSeedInt(berryItems.length);
const randBerry = berryItems[index];
randBerry.stackCount--;
if (randBerry.stackCount === 0) {
scene.removeModifier(randBerry);
berryItems.splice(index, 1);
}
scene.updateModifiers(true, true);
}
scene.updateModifiers(true, true);
// Pokemon joins the team, with 2 egg moves
const encounter = scene.currentBattle.mysteryEncounter!;

View File

@ -370,10 +370,9 @@ async function doNewTeamPostProcess(scene: BattleScene, transformations: Pokemon
}
newTypes.push(newType);
if (!newPokemon.mysteryEncounterPokemonData) {
newPokemon.mysteryEncounterPokemonData = new MysteryEncounterPokemonData(undefined, undefined, undefined, newTypes);
} else {
newPokemon.mysteryEncounterPokemonData.types = newTypes;
newPokemon.mysteryEncounterPokemonData = new MysteryEncounterPokemonData();
}
newPokemon.mysteryEncounterPokemonData.types = newTypes;
for (const item of transformation.heldItems) {
item.pokemonId = newPokemon.id;

View File

@ -1,5 +1,6 @@
import { Abilities } from "#enums/abilities";
import { Type } from "#app/data/type";
import { isNullOrUndefined } from "#app/utils";
export class MysteryEncounterPokemonData {
public spriteScale: number;
@ -7,10 +8,14 @@ export class MysteryEncounterPokemonData {
public passive: Abilities | -1;
public types: Type[];
constructor(spriteScale?: number, ability?: Abilities, passive?: Abilities, types?: Type[]) {
this.spriteScale = spriteScale ?? -1;
this.ability = ability ?? -1;
this.passive = passive ?? -1;
this.types = types ?? [];
constructor(data?: MysteryEncounterPokemonData | Partial<MysteryEncounterPokemonData>) {
if (!isNullOrUndefined(data)) {
Object.assign(this, data);
}
this.spriteScale = this.spriteScale ?? -1;
this.ability = this.ability ?? -1;
this.passive = this.passive ?? -1;
this.types = this.types ?? [];
}
}

View File

@ -27,9 +27,12 @@ export class MysteryEncounterSaveData {
encounterSpawnChance: number = BASE_MYSTERY_ENCOUNTER_SPAWN_WEIGHT;
queuedEncounters: QueuedEncounter[] = [];
constructor(data: MysteryEncounterSaveData | null) {
constructor(data?: MysteryEncounterSaveData) {
if (!isNullOrUndefined(data)) {
Object.assign(this, data);
}
this.encounteredEvents = this.encounteredEvents ?? [];
this.queuedEncounters = this.queuedEncounters ?? [];
}
}

View File

@ -33,9 +33,14 @@ import { UncommonBreedEncounter } from "#app/data/mystery-encounters/encounters/
import { GlobalTradeSystemEncounter } from "#app/data/mystery-encounters/encounters/global-trade-system-encounter";
/**
* Spawn chance: (BASE_MYSTERY_ENCOUNTER_SPAWN_WEIGHT + WIGHT_INCREMENT_ON_SPAWN_MISS * <number of missed spawns>) / 256
* Spawn chance: (BASE_MYSTERY_ENCOUNTER_SPAWN_WEIGHT + WIGHT_INCREMENT_ON_SPAWN_MISS * <number of missed spawns>) / MYSTERY_ENCOUNTER_SPAWN_MAX_WEIGHT
*/
export const BASE_MYSTERY_ENCOUNTER_SPAWN_WEIGHT = 3;
/**
* The divisor for determining ME spawns, defines the "maximum" weight required for a spawn
* If spawn_weight === MYSTERY_ENCOUNTER_SPAWN_MAX_WEIGHT, 100% chance to spawn a ME
*/
export const MYSTERY_ENCOUNTER_SPAWN_MAX_WEIGHT = 256;
/**
* When an ME spawn roll fails, WEIGHT_INCREMENT_ON_SPAWN_MISS is added to future rolls for ME spawn checks.
* These values are cleared whenever the next ME spawns, and spawn weight returns to BASE_MYSTERY_ENCOUNTER_SPAWN_WEIGHT

View File

@ -73,6 +73,8 @@ export enum BattlerTagType {
SHELL_TRAP = "SHELL_TRAP",
DRAGON_CHEER = "DRAGON_CHEER",
NO_RETREAT = "NO_RETREAT",
GORILLA_TACTICS = "GORILLA_TACTICS",
THROAT_CHOPPED = "THROAT_CHOPPED",
TAR_SHOT = "TAR_SHOT",
MYSTERY_ENCOUNTER_POST_SUMMON = "MYSTERY_ENCOUNTER_POST_SUMMON",
}

View File

@ -5,7 +5,7 @@ import { variantData } from "#app/data/variant";
import BattleInfo, { PlayerBattleInfo, EnemyBattleInfo } from "../ui/battle-info";
import Move, { HighCritAttr, HitsTagAttr, applyMoveAttrs, FixedDamageAttr, VariableAtkAttr, allMoves, MoveCategory, TypelessAttr, CritOnlyAttr, getMoveTargets, OneHitKOAttr, VariableMoveTypeAttr, VariableDefAttr, AttackMove, ModifiedDamageAttr, VariableMoveTypeMultiplierAttr, IgnoreOpponentStatStagesAttr, SacrificialAttr, VariableMoveCategoryAttr, CounterDamageAttr, StatStageChangeAttr, RechargeAttr, ChargeAttr, IgnoreWeatherTypeDebuffAttr, BypassBurnDamageReductionAttr, SacrificialAttrOnHit, OneHitKOAccuracyAttr, RespectAttackTypeImmunityAttr } from "../data/move";
import { default as PokemonSpecies, PokemonSpeciesForm, SpeciesFormKey, getFusedSpeciesName, getPokemonSpecies, getPokemonSpeciesForm, getStarterValueFriendshipCap, speciesStarters, starterPassiveAbilities } from "../data/pokemon-species";
import { Constructor, randSeedInt } from "#app/utils";
import { Constructor, isNullOrUndefined, randSeedInt } from "#app/utils";
import * as Utils from "../utils";
import { Type, TypeDamageMultiplier, getTypeDamageMultiplier, getTypeRgb } from "../data/type";
import { getLevelTotalExp } from "../data/exp";
@ -202,7 +202,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
this.fusionGender = dataSource.fusionGender;
this.fusionLuck = dataSource.fusionLuck;
this.usedTMs = dataSource.usedTMs ?? [];
this.mysteryEncounterPokemonData = dataSource.mysteryEncounterPokemonData ?? new MysteryEncounterPokemonData();
this.mysteryEncounterPokemonData = new MysteryEncounterPokemonData(dataSource.mysteryEncounterPokemonData);
} else {
this.id = Utils.randSeedInt(4294967296);
this.ivs = ivs || Utils.getIvsFromId(this.id);
@ -548,10 +548,11 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
if (!ignoreOverride && this.summonData?.speciesForm) {
return this.summonData.speciesForm;
}
if (!this.species.forms?.length) {
return this.species;
if (this.species.forms && this.species.forms.length > 0) {
return this.species.forms[this.formIndex];
}
return this.species.forms[this.formIndex];
return this.species;
}
getFusionSpeciesForm(ignoreOverride?: boolean): PokemonSpeciesForm {
@ -1014,10 +1015,16 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
: this.moveset;
// Overrides moveset based on arrays specified in overrides.ts
const overrideArray: Array<Moves> = this.isPlayer() ? Overrides.MOVESET_OVERRIDE : Overrides.OPP_MOVESET_OVERRIDE;
let overrideArray: Moves | Array<Moves> = this.isPlayer() ? Overrides.MOVESET_OVERRIDE : Overrides.OPP_MOVESET_OVERRIDE;
if (!Array.isArray(overrideArray)) {
overrideArray = [overrideArray];
}
if (overrideArray.length > 0) {
if (!this.isPlayer()) {
this.moveset = [];
}
overrideArray.forEach((move: Moves, index: number) => {
const ppUsed = this.moveset[index]?.ppUsed || 0;
const ppUsed = this.moveset[index]?.ppUsed ?? 0;
this.moveset[index] = new PokemonMove(move, Math.min(ppUsed, allMoves[move].pp));
});
}
@ -1151,7 +1158,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
if (Overrides.OPP_ABILITY_OVERRIDE && !this.isPlayer()) {
return allAbilities[Overrides.OPP_ABILITY_OVERRIDE];
}
if (this.mysteryEncounterPokemonData.ability !== -1) {
if (!isNullOrUndefined(this.mysteryEncounterPokemonData.ability) && this.mysteryEncounterPokemonData.ability !== -1) {
return allAbilities[this.mysteryEncounterPokemonData.ability];
}
if (this.isFusion()) {
@ -1178,7 +1185,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
if (Overrides.OPP_PASSIVE_ABILITY_OVERRIDE && !this.isPlayer()) {
return allAbilities[Overrides.OPP_PASSIVE_ABILITY_OVERRIDE];
}
if (this.mysteryEncounterPokemonData.passive !== -1) {
if (!isNullOrUndefined(this.mysteryEncounterPokemonData.passive) && this.mysteryEncounterPokemonData.passive !== -1) {
return allAbilities[this.mysteryEncounterPokemonData.passive];
}

View File

@ -9,6 +9,7 @@ import * as Utils from "./utils";
import { Biome } from "#enums/biome";
import { Species } from "#enums/species";
import { Challenges } from "./enums/challenges";
import MAX_SAFE_INTEGER = Phaser.Math.MAX_SAFE_INTEGER;
export enum GameModes {
CLASSIC,
@ -30,6 +31,8 @@ interface GameModeConfig {
isSplicedOnly?: boolean;
isChallenge?: boolean;
hasMysteryEncounters?: boolean;
minMysteryEncounterWave?: number;
maxMysteryEncounterWave?: number;
}
export class GameMode implements GameModeConfig {
@ -47,6 +50,8 @@ export class GameMode implements GameModeConfig {
public challenges: Challenge[];
public battleConfig: FixedBattleConfigs;
public hasMysteryEncounters: boolean;
public minMysteryEncounterWave: number;
public maxMysteryEncounterWave: number;
constructor(modeId: GameModes, config: GameModeConfig, battleConfig?: FixedBattleConfigs) {
this.modeId = modeId;
@ -56,6 +61,8 @@ export class GameMode implements GameModeConfig {
this.challenges = allChallenges.map(c => copyChallenge(c));
}
this.battleConfig = battleConfig || {};
this.minMysteryEncounterWave = this.minMysteryEncounterWave ?? 0;
this.maxMysteryEncounterWave = this.maxMysteryEncounterWave ?? MAX_SAFE_INTEGER;
}
/**
@ -338,7 +345,7 @@ export class GameMode implements GameModeConfig {
export function getGameMode(gameMode: GameModes): GameMode {
switch (gameMode) {
case GameModes.CLASSIC:
return new GameMode(GameModes.CLASSIC, { isClassic: true, hasTrainers: true, hasMysteryEncounters: true }, classicFixedBattles);
return new GameMode(GameModes.CLASSIC, { isClassic: true, hasTrainers: true, hasMysteryEncounters: true, maxMysteryEncounterWave: 180, minMysteryEncounterWave: 10 }, classicFixedBattles);
case GameModes.ENDLESS:
return new GameMode(GameModes.ENDLESS, { isEndless: true, hasShortBiomes: true, hasRandomBosses: true });
case GameModes.SPLICED_ENDLESS:

View File

@ -3,5 +3,6 @@
"power": "Stärke",
"accuracy": "Genauigkeit",
"abilityFlyInText": "{{passive}}{{abilityName}} von {{pokemonName}} wirkt!",
"passive": "Passive Fähigkeit "
"passive": "Passive Fähigkeit ",
"teraHover": "Tera-Typ {{type}}"
}

View File

@ -48,7 +48,10 @@
"moveNotImplemented": "{{moveName}} is not yet implemented and cannot be selected.",
"moveNoPP": "There's no PP left for\nthis move!",
"moveDisabled": "{{moveName}} is disabled!",
"canOnlyUseMove": "{{pokemonName}} can only use {{moveName}}!",
"moveCannotBeSelected": "{{moveName}} cannot be selected!",
"disableInterruptedMove": "{{pokemonNameWithAffix}}'s {{moveName}}\nis disabled!",
"throatChopInterruptedMove": "The effects of Throat Chop prevent\n{{pokemonName}} from using certain moves!",
"noPokeballForce": "An unseen force\nprevents using Poké Balls.",
"noPokeballTrainer": "You can't catch\nanother trainer's Pokémon!",
"noPokeballMulti": "You can only throw a Poké Ball\nwhen there is one Pokémon remaining!",

View File

@ -3,5 +3,6 @@
"power": "Power",
"accuracy": "Accuracy",
"abilityFlyInText": " {{pokemonName}}'s {{passive}}{{abilityName}}",
"passive": "Passive "
"passive": "Passive ",
"teraHover": "{{type}} Terastallized"
}

View File

@ -7,6 +7,7 @@
"switchedStat": "{{pokemonName}} switched {{stat}} with its target!",
"sharedGuard": "{{pokemonName}} shared its guard with the target!",
"sharedPower": "{{pokemonName}} shared its power with the target!",
"shiftedStats": "{{pokemonName}} switched its {{statToSwitch}} and {{statToSwitchWith}}!",
"goingAllOutForAttack": "{{pokemonName}} is going all out for this attack!",
"regainedHealth": "{{pokemonName}} regained\nhealth!",
"keptGoingAndCrashed": "{{pokemonName}} kept going\nand crashed!",

View File

@ -1,25 +1,25 @@
{
"intro": "A Greedent ambushes you\nand steals your party's berries!",
"intro": "A {{greedentName}} ambushes you\nand steals your party's berries!",
"title": "Absolute Avarice",
"description": "The Greedent has caught you totally off guard now all your berries are gone!\n\nThe Greedent looks like it's about to eat them when it pauses to look at you, interested.",
"description": "The {{greedentName}} has caught you totally off guard now all your berries are gone!\n\nThe {{greedentName}} looks like it's about to eat them when it pauses to look at you, interested.",
"query": "What will you do?",
"option": {
"1": {
"label": "Battle It",
"tooltip": "(-) Tough Battle\n(+) Rewards from its Berry Hoard",
"selected": "The Greedent stuffs its cheeks\nand prepares for battle!",
"boss_enraged": "Greedent's fierce love for food has it incensed!",
"food_stash": "It looks like the Greedent was guarding an enormous stash of food!$@s{item_fanfare}Each Pokémon in your party gains 1x Reviver Seed!"
"selected": "The {{greedentName}} stuffs its cheeks\nand prepares for battle!",
"boss_enraged": "{{greedentName}}'s fierce love for food has it incensed!",
"food_stash": "It looks like the {{greedentName}} was guarding an enormous stash of food!$@s{item_fanfare}Each Pokémon in your party gains a {{foodReward}}!"
},
"2": {
"label": "Reason with It",
"tooltip": "(+) Regain Some Lost Berries",
"selected": "Your pleading strikes a chord with the Greedent.$It doesn't give all your berries back, but still tosses a few in your direction."
"selected": "Your pleading strikes a chord with the {{greedentName}}.$It doesn't give all your berries back, but still tosses a few in your direction."
},
"3": {
"label": "Let It Have the Food",
"tooltip": "(-) Lose All Berries\n(?) The Greedent Will Like You",
"selected": "The Greedent devours the entire\nstash of berries in a flash!$Patting its stomach,\nit looks at you appreciatively.$Perhaps you could feed it\nmore berries on your adventure...$@s{level_up_fanfare}The Greedent wants to join your party!"
"tooltip": "(-) Lose All Berries\n(?) The {{greedentName}} Will Like You",
"selected": "The {{greedentName}} devours the entire\nstash of berries in a flash!$Patting its stomach,\nit looks at you appreciatively.$Perhaps you could feed it\nmore berries on your adventure...$@s{level_up_fanfare}The {{greedentName}} wants to join your party!"
}
}
}

View File

@ -15,12 +15,12 @@
"label": "Extort the Kid",
"tooltip": "(+) {{option2PrimaryName}} uses {{moveOrAbility}}\n(+) Gain {{price, money}}",
"tooltip_disabled": "Your Pokémon need to have certain moves or abilities to choose this",
"selected": "My word, we're being robbed, Liepard!\n $You'll be hearing from my lawyers for this!"
"selected": "My word, we're being robbed, {{liepardName}}!$You'll be hearing from my lawyers for this!"
},
"3": {
"label": "Leave",
"tooltip": "(-) No Rewards",
"selected": "What a rotten day...$Ah, well. Let's return to the yacht club then, Liepard."
"selected": "What a rotten day...$Ah, well. Let's return to the yacht club then, {{liepardName}}."
}
}
}

View File

@ -1,7 +1,7 @@
{
"intro": "There's a huge berry bush\nnear that Pokémon!",
"title": "Berries Abound",
"description": "It looks like there's a strong Pokémon guarding a berry bush. Battling is the straightforward approach, but this Pokémon looks strong. Maybe a fast Pokémon would be able to grab some without getting caught?",
"description": "It looks like there's a strong Pokémon guarding a berry bush. Battling is the straightforward approach, but it looks strong. Perhaps a fast Pokémon could grab some berries without getting caught?",
"query": "What will you do?",
"berries": "Berries!",
"option": {

View File

@ -23,8 +23,8 @@
},
"3": {
"label": "Gift a Bug Item",
"tooltip": "(-) Give the trainer a Quick Claw, Grip Claw, or Silver Powder\n(+) Receive a Gift Item",
"disabled_tooltip": "You need to have a Quick Claw, Grip Claw, or Silver Powder to select this.",
"tooltip": "(-) Give the trainer a {{requiredBugItems}}\n(+) Receive a Gift Item",
"disabled_tooltip": "You need to have a {{requiredBugItems}} to select this.",
"select_prompt": "Select an item to give.",
"invalid_selection": "Pokémon doesn't have that kind of item.",
"selected": "You hand the trainer a {{selectedItem}}.",

View File

@ -3,7 +3,7 @@
"speaker": "Clown",
"intro_dialogue": "Bumbling buffoon, brace for a brilliant battle!\nYou'll be beaten by this brawling busker!",
"title": "Clowning Around",
"description": "Something is off about this encounter. The clown seems eager to goad you into a battle, but to what end?\n\nThe Blacephalon is especially strange, like it has @[TOOLTIP_TITLE]{weird types and ability.}",
"description": "Something is off about this encounter. The clown seems eager to goad you into a battle, but to what end?\n\nThe {{blacephalonName}} is especially strange, like it has @[TOOLTIP_TITLE]{weird types and ability.}",
"query": "What will you do?",
"option": {
"1": {
@ -19,14 +19,14 @@
"label": "Remain Unprovoked",
"tooltip": "(-) Upsets the Clown\n(?) Affects Pokémon Items",
"selected": "Dismal dodger, you deny a delightful duel?\nFeel my fury!",
"selected_2": "The clown's Blacephalon uses Trick!\nAll of your {{switchPokemon}}'s items were randomly swapped!",
"selected_2": "The clown's {{blacephalonName}} uses Trick!\nAll of your {{switchPokemon}}'s items were randomly swapped!",
"selected_3": "Flustered fool, fall for my flawless deception!"
},
"3": {
"label": "Return the Insults",
"tooltip": "(-) Upsets the Clown\n(?) Affects Pokémon Types",
"selected": "Dismal dodger, you deny a delightful duel?\nFeel my fury!",
"selected_2": "The clown's Blacephalon uses a strange move!\nAll of your team's types were randomly swapped!",
"selected_2": "The clown's {{blacephalonName}} uses a strange move!\nAll of your team's types were randomly swapped!",
"selected_3": "Flustered fool, fall for my flawless deception!"
}
},

View File

@ -1,26 +1,26 @@
{
"intro": "An Oricorio dances sadly alone, without a partner.",
"intro": "An {{oricorioName}} dances sadly alone, without a partner.",
"title": "Dancing Lessons",
"description": "The Oricorio doesn't seem aggressive, if anything it seems sad.\n\nMaybe it just wants someone to dance with...",
"description": "The {{oricorioName}} doesn't seem aggressive, if anything it seems sad.\n\nMaybe it just wants someone to dance with...",
"query": "What will you do?",
"option": {
"1": {
"label": "Battle It",
"tooltip": "(-) Tough Battle\n(+) Gain a Baton",
"selected": "The Oricorio is distraught and moves to defend itself!",
"boss_enraged": "The Oricorio's fear boosted its stats!"
"selected": "The {{oricorioName}} is distraught and moves to defend itself!",
"boss_enraged": "The {{oricorioName}}'s fear boosted its stats!"
},
"2": {
"label": "Learn Its Dance",
"tooltip": "(+) Teach a Pokémon Revelation Dance",
"selected": "You watch the Oricorio closely as it performs its dance...$@s{level_up_fanfare}Your {{selectedPokemon}} learned from the Oricorio!"
"selected": "You watch the {{oricorioName}} closely as it performs its dance...$@s{level_up_fanfare}Your {{selectedPokemon}} learned from the {{oricorioName}}!"
},
"3": {
"label": "Show It a Dance",
"tooltip": "(-) Teach the Oricorio a Dance Move\n(+) The Oricorio Will Like You",
"tooltip": "(-) Teach the {{oricorioName}} a Dance Move\n(+) The {{oricorioName}} Will Like You",
"disabled_tooltip": "Your Pokémon need to know a Dance move for this.",
"select_prompt": "Select a Dance type move to use.",
"selected": "The Oricorio watches in fascination as\n{{selectedPokemon}} shows off {{selectedMove}}!$It loves the display!$@s{level_up_fanfare}The Oricorio wants to join your party!"
"selected": "The {{oricorioName}} watches in fascination as\n{{selectedPokemon}} shows off {{selectedMove}}!$It loves the display!$@s{level_up_fanfare}The {{oricorioName}} wants to join your party!"
}
},
"invalid_selection": "This Pokémon doesn't know a Dance move"

View File

@ -1,29 +1,29 @@
{
"intro": "A pack of Delibird have appeared!",
"intro": "A pack of {{delibirdName}} have appeared!",
"title": "Delibir-dy",
"description": "The Delibirds are looking at you expectantly, as if they want something. Perhaps giving them an item or some money would satisfy them?",
"description": "The {{delibirdName}}s are looking at you expectantly, as if they want something. Perhaps giving them an item or some money would satisfy them?",
"query": "What will you give them?",
"invalid_selection": "Pokémon doesn't have that kind of item.",
"option": {
"1": {
"label": "Give Money",
"tooltip": "(-) Give the Delibirds {{money, money}}\n(+) Receive a Gift Item",
"selected": "You toss the money to the Delibirds,\nwho chatter amongst themselves excitedly.$They turn back to you and happily give you a present!"
"tooltip": "(-) Give the {{delibirdName}}s {{money, money}}\n(+) Receive a Gift Item",
"selected": "You toss the money to the {{delibirdName}}s,\nwho chatter amongst themselves excitedly.$They turn back to you and happily give you a present!"
},
"2": {
"label": "Give Food",
"tooltip": "(-) Give the Delibirds a Berry or Reviver Seed\n(+) Receive a Gift Item",
"tooltip": "(-) Give the {{delibirdName}}s a Berry or Reviver Seed\n(+) Receive a Gift Item",
"select_prompt": "Select an item to give.",
"selected": "You toss the {{chosenItem}} to the Delibirds,\nwho chatter amongst themselves excitedly.$They turn back to you and happily give you a present!"
"selected": "You toss the {{chosenItem}} to the {{delibirdName}}s,\nwho chatter amongst themselves excitedly.$They turn back to you and happily give you a present!"
},
"3": {
"label": "Give an Item",
"tooltip": "(-) Give the Delibirds a Held Item\n(+) Receive a Gift Item",
"tooltip": "(-) Give the {{delibirdName}}s a Held Item\n(+) Receive a Gift Item",
"select_prompt": "Select an item to give.",
"selected": "You toss the {{chosenItem}} to the Delibirds,\nwho chatter amongst themselves excitedly.$They turn back to you and happily give you a present!"
"selected": "You toss the {{chosenItem}} to the {{delibirdName}}s,\nwho chatter amongst themselves excitedly.$They turn back to you and happily give you a present!"
}
},
"outro": "The Delibird pack happily waddles off into the distance.$What a curious little exchange!"
"outro": "The {{delibirdName}} pack happily waddles off into the distance.$What a curious little exchange!"
}

View File

@ -7,7 +7,7 @@
"1": {
"label": "Find the Source",
"tooltip": "(?) Discover the source\n(-) Hard Battle",
"selected": "You push through the storm, and find two Volcarona in the middle of a mating dance!$They don't take kindly to the interruption and attack!"
"selected": "You push through the storm, and find two {{volcaronaName}}s in the middle of a mating dance!$They don't take kindly to the interruption and attack!"
},
"2": {
"label": "Hunker Down",
@ -19,7 +19,7 @@
"label": "Your Fire Types Help",
"tooltip": "(+) End the conditions\n(+) Gain a Charcoal",
"disabled_tooltip": "You need at least 2 Fire Type Pokémon to choose this",
"selected": "Your {{option3PrimaryName}} and {{option3SecondaryName}} guide you to where two Volcarona are in the middle of a mating dance!$Thankfully, your Pokémon are able to calm them,\nand they depart without issue."
"selected": "Your {{option3PrimaryName}} and {{option3SecondaryName}} guide you to where two {{volcaronaName}}s are in the middle of a mating dance!$Thankfully, your Pokémon are able to calm them,\nand they depart without issue."
}
},
"found_charcoal": "After the weather clears,\nyour {{leadPokemon}} spots something on the ground.$@s{item_fanfare}{{leadPokemon}} gained a Charcoal!"

View File

@ -1,7 +1,7 @@
{
"intro": "Something shiny is sparkling\non the ground near that Pokémon!",
"title": "Fight or Flight",
"description": "It looks like there's a strong Pokémon guarding an item. Battling is the straightforward approach, but this Pokémon looks strong. You could also try to sneak around, though the Pokémon might catch you.",
"description": "It looks like there's a strong Pokémon guarding an item. Battling is the straightforward approach, but it looks strong. Perhaps you could steal the item, if you have the right Pokémon for the job.",
"query": "What will you do?",
"option": {
"1": {

View File

@ -1,13 +1,13 @@
{
"intro_dialogue": "Step right up, folks! Try your luck\non the brand new Wobbuffet Whack-o-matic!",
"intro_dialogue": "Step right up, folks! Try your luck\non the brand new {{wobbuffetName}} Whack-o-matic!",
"speaker": "Showman",
"title": "Fun And Games!",
"description": "You've encountered a traveling show with a prize game! You will have @[TOOLTIP_TITLE]{3 turns} to bring the Wobbuffet as close to @[TOOLTIP_TITLE]{1 HP} as possible @[TOOLTIP_TITLE]{without KOing it} so it can wind up a huge Counter on the bell-ringing machine.\nBut be careful! If you KO the Wobbuffet, you'll have to pay for the cost of reviving it!",
"description": "You've encountered a traveling show with a prize game! You will have @[TOOLTIP_TITLE]{3 turns} to bring the {{wobbuffetName}} as close to @[TOOLTIP_TITLE]{1 HP} as possible @[TOOLTIP_TITLE]{without KOing it} so it can wind up a huge Counter on the bell-ringing machine.\nBut be careful! If you KO the {{wobbuffetName}}, you'll have to pay for the cost of reviving it!",
"query": "Would you like to play?",
"option": {
"1": {
"label": "Play the Game",
"tooltip": "(-) Pay {{option1Money, money}}\n(+) Play Wobbuffet Whack-o-matic",
"tooltip": "(-) Pay {{option1Money, money}}\n(+) Play {{wobbuffetName}} Whack-o-matic",
"selected": "Time to test your luck!"
},
"2": {
@ -16,15 +16,15 @@
"selected": "You hurry along your way,\nwith a slight feeling of regret."
}
},
"ko": "Oh no! The Wobbuffet fainted!$You lose the game and\nhave to pay for the revive cost...",
"ko": "Oh no! The {{wobbuffetName}} fainted!$You lose the game and\nhave to pay for the revive cost...",
"charging_continue": "The Wubboffet keeps charging its counter-swing!",
"turn_remaining_3": "Three turns remaining!",
"turn_remaining_2": "Two turns remaining!",
"turn_remaining_1": "One turn remaining!",
"end_game": "Time's up!$The Wobbuffet winds up to counter-swing and@d{16}.@d{16}.@d{16}.",
"best_result": "The Wobbuffet smacks the button so hard\nthe bell breaks off the top!$You win the grand prize!",
"great_result": "The Wobbuffet smacks the button, nearly hitting the bell!$So close!\nYou earn the second tier prize!",
"good_result": "The Wobbuffet hits the button hard enough to go midway up the scale!$You earn the third tier prize!",
"bad_result": "The Wobbuffet barely taps the button and nothing happens...$Oh no!\nYou don't win anything!",
"end_game": "Time's up!$The {{wobbuffetName}} winds up to counter-swing and@d{16}.@d{16}.@d{16}.",
"best_result": "The {{wobbuffetName}} smacks the button so hard\nthe bell breaks off the top!$You win the grand prize!",
"great_result": "The {{wobbuffetName}} smacks the button, nearly hitting the bell!$So close!\nYou earn the second tier prize!",
"good_result": "The {{wobbuffetName}} hits the button hard enough to go midway up the scale!$You earn the third tier prize!",
"bad_result": "The {{wobbuffetName}} barely taps the button and nothing happens...$Oh no!\nYou don't win anything!",
"outro": "That was a fun little game!"
}

View File

@ -12,7 +12,7 @@
"good": "Some pretty nice tools and items.",
"great": "A couple great tools and items!",
"amazing": "Whoa! An amazing item!",
"bad": "Oh no!@d{32}\nThe chest was actually a Gimmighoul in disguise!$Your {{pokeName}} jumps in front of you\nbut is KOed in the process!"
"bad": "Oh no!@d{32}\nThe chest was actually a {{gimmighoulName}} in disguise!$Your {{pokeName}} jumps in front of you\nbut is KOed in the process!"
},
"2": {
"label": "Too Risky, Leave",

View File

@ -1,25 +1,25 @@
{
"intro": "As you walk down a narrow pathway, you see a towering silhouette blocking your path.$You get closer to see a Snorlax sleeping peacefully.\nIt seems like there's no way around it.",
"title": "Slumbering Snorlax",
"intro": "As you walk down a narrow pathway, you see a towering silhouette blocking your path.$You get closer to see a {{snorlaxName}} sleeping peacefully.\nIt seems like there's no way around it.",
"title": "Slumbering {{snorlaxName}}",
"description": "You could attack it to try and get it to move, or simply wait for it to wake up. Who knows how long that could take, though...",
"query": "What will you do?",
"option": {
"1": {
"label": "Battle It",
"tooltip": "(-) Fight Sleeping Snorlax\n(+) Special Reward",
"tooltip": "(-) Fight Sleeping {{snorlaxName}}\n(+) Special Reward",
"selected": "You approach the\nPokémon without fear."
},
"2": {
"label": "Wait for It to Move",
"tooltip": "(-) Wait a Long Time\n(+) Recover Party",
"selected": ".@d{32}.@d{32}.@d{32}$You wait for a time, but the Snorlax's yawns make your party sleepy...",
"rest_result": "When you all awaken, the Snorlax is no where to be found -\nbut your Pokémon are all healed!"
"selected": ".@d{32}.@d{32}.@d{32}$You wait for a time, but the {{snorlaxName}}'s yawns make your party sleepy...",
"rest_result": "When you all awaken, the {{snorlaxName}} is no where to be found -\nbut your Pokémon are all healed!"
},
"3": {
"label": "Steal Its Item",
"tooltip": "(+) {{option3PrimaryName}} uses {{option3PrimaryMove}}\n(+) Special Reward",
"disabled_tooltip": "Your Pokémon need to know certain moves to choose this",
"selected": "Your {{option3PrimaryName}} uses {{option3PrimaryMove}}!$@s{item_fanfare}It steals Leftovers off the sleeping\nSnorlax and you make out like bandits!"
"selected": "Your {{option3PrimaryName}} uses {{option3PrimaryMove}}!$@s{item_fanfare}It steals Leftovers off the sleeping\n{{snorlaxName}} and you make out like bandits!"
}
}
}

View File

@ -1,20 +1,20 @@
{
"intro": "It's a massive Shuckle and what appears\nto be a large stash of... juice?",
"intro": "It's a massive {{shuckleName}} and what appears\nto be a large stash of... juice?",
"title": "The Strong Stuff",
"description": "The Shuckle that blocks your path looks incredibly strong. Meanwhile, the juice next to it is emanating power of some kind.\n\nThe Shuckle extends its feelers in your direction. It seems like it wants to do something...",
"description": "The {{shuckleName}} that blocks your path looks incredibly strong. Meanwhile, the juice next to it is emanating power of some kind.\n\nThe {{shuckleName}} extends its feelers in your direction. It seems like it wants to do something...",
"query": "What will you do?",
"option": {
"1": {
"label": "Approach the Shuckle",
"label": "Approach the {{shuckleName}}",
"tooltip": "(?) Something awful or amazing might happen",
"selected": "You black out.",
"selected_2": "@f{150}When you awaken, the Shuckle is gone\nand juice stash completely drained.${{highBstPokemon1}} and {{highBstPokemon2}}\nfeel a terrible lethargy come over them!$Their base stats were reduced by {{reductionValue}}!$Your remaining Pokémon feel an incredible vigor, though!\nTheir base stats are increased by {{increaseValue}}!"
"selected_2": "@f{150}When you awaken, the {{shuckleName}} is gone\nand juice stash completely drained.${{highBstPokemon1}} and {{highBstPokemon2}}\nfeel a terrible lethargy come over them!$Their base stats were reduced by {{reductionValue}}!$Your remaining Pokémon feel an incredible vigor, though!\nTheir base stats are increased by {{increaseValue}}!"
},
"2": {
"label": "Battle the Shuckle",
"label": "Battle the {{shuckleName}}",
"tooltip": "(-) Hard Battle\n(+) Special Rewards",
"selected": "Enraged, the Shuckle drinks some of its juice and attacks!",
"stat_boost": "The Shuckle's juice boosts its stats!"
"selected": "Enraged, the {{shuckleName}} drinks some of its juice and attacks!",
"stat_boost": "The {{shuckleName}}'s juice boosts its stats!"
}
},
"outro": "What a bizarre turn of events."

View File

@ -2,11 +2,11 @@
"blockRecoilDamage": "{{pokemonName}}は {{abilityName}}で 反動ダメージを 受けない!",
"badDreams": "{{pokemonName}}は ナイトメアに うなされている!",
"costar": "{{pokemonName}}は {{allyName}}の\n能力変化を コピーした",
"iceFaceAvoidedDamage": "{{pokemonName}}は\n{{abilityName}}で ダメージを 受けない!",
"iceFaceAvoidedDamage": "{{pokemonNameWithAffix}}は\n{{abilityName}}で ダメージを 受けない!",
"perishBody": "{{pokemonName}}の {{abilityName}}で\nおたがいは 3ターン後に ほろびいてしまう",
"poisonHeal": "{{pokemonName}}は {{abilityName}}で 回復した!",
"trace": "{{pokemonName}}は 相手の {{targetName}}の\n{{abilityName}}を トレースした!",
"windPowerCharged": "{{pokemonName}}は\n{{moveName}}を 受けて じゅうでんした!",
"windPowerCharged": "{{pokemonNameWithAffix}}は\n{{moveName}}を 受けて じゅうでんした!",
"quickDraw": "{{pokemonName}}は クイックドロウで\n行動が はやくなった",
"disguiseAvoidedDamage": "{{pokemonNameWithAffix}}の\nばけのかわが はがれた",
"blockItemTheft": "{{pokemonNameWithAffix}}の {{abilityName}}で\n道具を うばわれない",

File diff suppressed because it is too large Load Diff

View File

@ -39,5 +39,6 @@
"matBlock": "たたみがえし",
"craftyShield": "トリックガード",
"tailwind": "おいかぜ",
"happyHour": "ハッピータイム"
"happyHour": "ハッピータイム",
"safeguard": "しんぴなまもり"
}

View File

@ -47,5 +47,11 @@
"tailwindOnRemovePlayer": "味方の 追い風が 止んだ!",
"tailwindOnRemoveEnemy": "相手の 追い風が 止んだ!",
"happyHourOnAdd": "みんなが ハッピーな気分に\n包まれた",
"happyHourOnRemove": "みんなの 気分が 元に戻った"
}
"happyHourOnRemove": "みんなの 気分が 元に戻った",
"safeguardOnAdd": "場の全体は 神秘のベールに 包まれた!",
"safeguardOnAddPlayer": "味方は 神秘のベールに 包まれた!",
"safeguardOnAddEnemy": "相手は 神秘のベールに 包まれた!",
"safeguardOnRemove": "場の全体を 包んでいた\n神秘のベールが なくなった",
"safeguardOnRemovePlayer": "味方を 包んでいた\n神秘のベールが なくなった",
"safeguardOnRemoveEnemy": "相手を 包んでいた\n神秘のベールが なくなった"
}

View File

@ -4,7 +4,7 @@
"trainerAppearedDouble": "{{trainerName}}が\n勝負を しかけてきた!",
"trainerSendOut": "{{trainerName}}は\n{{pokemonName}}を 繰り出した!",
"singleWildAppeared": "あっ! 野生の {{pokemonName}}が 飛び出してきた!",
"multiWildAppeared": "あっ! 野生の {{pokemonName1}}と\n{{pokemonName2}}が 飛び出してきた!",
"multiWildAppeared": "あっ! 野生の {{pokemonName1}}と\n{{pokemonName2}}が 飛び出してきた!",
"playerComeBack": "{{pokemonName}}! 戻れ!",
"trainerComeBack": "{{trainerName}}は\n{{pokemonName}}を 引っ込めた!",
"playerGo": "ゆけっ! {{pokemonName}}",
@ -51,7 +51,7 @@
"noPokeballStrong": "相手の ポケモンが 強すぎて 捕まえられない!\nまずは 弱めよう",
"noEscapeForce": "見えない 力の せいで\n逃げることが できない!",
"noEscapeTrainer": "ダメだ! 勝負の最中に\n相手に 背中を 見せられない",
"noEscapePokemon": "{{pokemonName}}の {{moveName}}で {{escapeVerb}}!",
"noEscapePokemon": "{{pokemonName}}の {{moveName}}で\n{{escapeVerb}}!",
"runAwaySuccess": " うまく 逃げ切れた!",
"runAwayCannotEscape": "逃げることが できない!",
"escapeVerbSwitch": "入れ替えることが できない",
@ -62,6 +62,7 @@
"skipItemQuestion": "本当に アイテムを 取らずに 進みますか?",
"itemStackFull": "{{fullItemName}}の スタックが いっぱいです。\n代わりに {{itemName}}を 取得します。",
"eggHatching": "おや?",
"eggSkipPrompt": "タマゴは ふかします!\nタマゴまとめに 飛ばしますか",
"ivScannerUseQuestion": "{{pokemonName}}を\n個体値スキャナーで 操作しますか?",
"wildPokemonWithAffix": "野生の {{pokemonName}}",
"foePokemonWithAffix": "相手の {{pokemonName}}",

View File

@ -1,46 +1,46 @@
{
"SITRUS": {
"name": "オボンのみ",
"effect": "持たせると HPが 50以下になるとき HPを 25 回復する"
"effect": "持たせると HPが 50%以下に なるとき HPを 25% 回復する"
},
"LUM": {
"name": "ラムのみ",
"effect": "持たせると 状態異常や 混乱になるとき 回復する\n"
"effect": "持たせると 状態異常や 混乱に なるとき 回復する"
},
"ENIGMA": {
"name": "ナゾのみ",
"effect": "持たせると 効果バツグンの 技を 受けたとき HPを 回復する"
"effect": "持たせると 効果バツグンの 技を 受けたとき HPを 25%回復する"
},
"LIECHI": {
"name": "チイラのみ",
"effect": "持たせると HPが 25%以下に なるとき 攻撃が あがる"
"effect": "持たせると HPが 25%以下に なるとき 攻撃が あがる"
},
"GANLON": {
"name": "リュガのみ",
"effect": "持たせると HPが 25%以下に なるとき 防御が あがる\n"
"effect": "持たせると HPが 25%以下に なるとき 防御が あがる"
},
"PETAYA": {
"name": "ヤタピのみ",
"effect": "持たせると HPが 25%以下に なるとき 特攻が あがる\n"
"effect": "持たせると HPが 25%以下に なるとき 特攻が あがる"
},
"APICOT": {
"name": "ズアのみ",
"effect": "持たせると HPが 25%以下に なるとき 特防が あがる\n"
"effect": "持たせると HPが 25%以下に なるとき 特防が あがる"
},
"SALAC": {
"name": "カムラのみ",
"effect": "持たせると HPが 25%以下に なるとき 素早さが あがる"
"effect": "持たせると HPが 25%以下に なるとき 素早さが あがる"
},
"LANSAT": {
"name": "サンのみ",
"effect": "持たせると HPが 25%以下に なるとき 攻撃が 急所に 当たりやすくなる"
"effect": "持たせると HPが 25%以下に なるとき 攻撃が 急所に 当たりやすくなる"
},
"STARF": {
"name": "スターのみ",
"effect": "持たせると HPが 25%以下に なるとき どれか 1つの 能力が ぐーんと あがる"
"effect": "持たせると HPが 25%以下に なるとき どれか 1つの 能力が ぐーんと あがる"
},
"LEPPA": {
"name": "ヒメリのみ",
"effect": "持たせると PPが 0になる 技のPPを 10回復する"
"effect": "持たせると PPが0になる 技の PPを 10回復する"
}
}

View File

@ -2,6 +2,6 @@
"pp": "PP",
"power": "威力",
"accuracy": "命中",
"abilityFlyInText": " {{pokemonName}}の\n{{passive}}{{abilityName}}",
"abilityFlyInText": " {{pokemonName}}の\n{{passive}} {{abilityName}}",
"passive": "パッシブ "
}

View File

@ -22,7 +22,7 @@
"unmatchingPassword": "入力したパスワードが 一致しません",
"passwordNotMatchingConfirmPassword": "パスワードは パスワード確認と 一致する 必要があります",
"confirmPassword": "パスワード確認",
"registrationAgeWarning": "登録では 歳以上 であることを 確認します。",
"registrationAgeWarning": "登録では 13歳以上 であることを 確認します。",
"backToLogin": "ログインへ",
"failedToLoadSaveData": "セーブデータの 読み込みは 不可能でした。ページを 再読み込み してください。\n長い間に続く 場合は 管理者に 連絡してください。",
"sessionSuccess": "セッションが 正常に 読み込まれました。",
@ -46,10 +46,10 @@
"yes": "はい",
"no": "いいえ",
"disclaimer": "免責",
"disclaimerDescription": "このゲームは 未完成作品です。\nセーブデータの 損失を含める ゲーム性に関する 問題が 起きる可能性が あります。\nなお、ゲームは 予告なく変更される 可能性もあり、さらに更新され、完成されるとも 限りません。",
"disclaimerDescription": "このゲームは 未完成作品です。\nセーブデータの 損失を含める ゲーム性に関する 問題が 起きる可能性が あります。\nなお、ゲームは 予告なく変更される 可能性もあり、\nさらに更新され、完成されるとも 限りません。",
"choosePokemon": "ポケモンを選ぶ",
"renamePokemon": "ニックネームを変える",
"rename": "変える",
"rename": "名前を変える",
"nickname": "ニックネーム",
"errorServerDown": "おや!\nサーバーとの 接続中に 問題が 発生しました。\nゲームは 自動的に 再接続されます から\nウィンドウは 開いたままに しておいても よろしいです。",
"noSaves": "何の セーブファイルも ありません!",

File diff suppressed because it is too large Load Diff

View File

@ -4,5 +4,44 @@
"CANCEL": "やめる",
"RELEASE": "逃がす",
"APPLY": "使う",
"TEACH": "教える"
"TEACH": "教える",
"SPLICE": "吸収合体",
"UNSPLICE": "合体を分離",
"ACTIVATE": "有効にする",
"DEACTIVATE": "無効にする",
"TRANSFER": "アイテムを移動",
"ALL": "全部",
"PASS_BATON": "バトンタッチ",
"UNPAUSE_EVOLUTION": "進化を有効にする",
"REVIVE": "復活する",
"RENAME": "名前を変える",
"choosePokemon": "ポケモンを 選んで ください。",
"doWhatWithThisPokemon": "このポケモンを どうする?",
"noEnergy": "{{pokemonName}}は 戦うための\n元気が 残っていません",
"hasEnergy": "{{pokemonName}}は まだまだ 元気だ!",
"cantBeUsed": "{{pokemonName}}は このチャレンジで\n使えられません",
"tooManyItems": "{{pokemonName}}は このアイテムが\nこれ以上 持ちきれない",
"anyEffect": "使っても 効果がないよ",
"unpausedEvolutions": "{{pokemonName}}は また 進化できる。",
"unspliceConfirmation": "本当に {{pokemonName}}を {{fusionName}}から\n分離しますか {{fusionName}}は なくなる。",
"wasReverted": "{{fusionName}}は {{pokemonName}}に 回帰した。",
"releaseConfirmation": "本当に {{pokemonName}}を 逃がしますか?",
"releaseInBattle": "戦闘中の ポケモンを\n逃がすことは できません",
"selectAMove": "技を 選んでください。",
"changeQuantity": "移動する アイテムを 選んでください。\n< と > で 数量が 変えられる。",
"selectAnotherPokemonToSplice": "もう一つの ポケモンを 選んで 合体する。",
"cancel": "キャンセル",
"able": "可能",
"notAble": "不可能",
"learned": "覚えている",
"goodbye": "グッバイ {{pokemonName}}!",
"byebye": "ばいばい {{pokemonName}}!",
"farewell": "さようなら {{pokemonName}}!",
"soLong": "じゃあね {{pokemonName}}!",
"thisIsWhereWePart": "これでお別れだね {{pokemonName}}!",
"illMissYou": "恋しく思うよ {{pokemonName}}!",
"illNeverForgetYou": "一生忘れない {{pokemonName}}!",
"untilWeMeetAgain": "また出会える日まで、{{pokemonName}}!",
"sayonara": "さらば {{pokemonName}}!",
"smellYaLater": "そんじゃ あばよ {{pokemonName}}!"
}

View File

@ -20,7 +20,7 @@
"normal": "普通",
"fast": "早い",
"faster": "とても早い",
"skip": "スキップ",
"skip": "飛ばす",
"levelUpNotifications": "レベルアップ時のみ",
"on": "オン",
"off": "オフ",

View File

@ -23,7 +23,7 @@
"manageNature": "性格を変える",
"addToFavorites": "お気に入りにする",
"removeFromFavorites": "お気に入りから除く",
"useCandies": "を使う",
"useCandies": "アメを使う",
"selectNature": "性格を選んでください。",
"selectMoveSwapOut": "入れ替えたい技を選んでください。",
"selectMoveSwapWith": "他の技と交換してください。",

View File

@ -1,10 +1,10 @@
{
"intro": "PokéRogueへようこそ!ログライク要素が\n加わったバトル中心のポケモンファンゲームです。\n$このゲームは収益を上げず、Pokémonおよび使用される\n著作権資産に対する所有権を主張しません。\n$ゲームはまだ作業中ですが、完全にプレイすることができます。\nバグ報告はディスコードコミュニティをご利用ください。\n$ゲームが遅い場合は、ブラウザ設定で「ハードウェア\nアクセラレーション」がオンになっていることを確認してください",
"accessMenu": "メニューにアクセスするには、入力待ちの間にMキーまたはEscを押してください。\nメニューには設定やさまざまな機能が含まれています。",
"menu": "このメニューから設定にアクセスできます。\n$設定ではゲームスピード、ウィンドウスタイル、\nおよびその他のオプションを変更できます。\n$ここにはさまざまな他の機能もありますので、\nぜひ確認してみてください!",
"starterSelect": "この画面でZキーやSpaceを押してポケモンを選択できます。\n選んだポケモンは自分の最初のパーティーになります。\n$最大6匹のパーティーで始めることができますが\nポケモンによってポイントがあり、合計10を超えてはなりません。\n$捕まえたりふかさせたりすることで\n選択できる性別、特性、フォルムなどの幅を広げることができます。\n$個体値も徐々に累積して高くなるので、\n同じポケモンをたくさん捕まえてみてください!",
"pokerus": "毎日ランダムでスターターの\n3種類に紫色の枠が表示されます。\n$登録されたスターターの中にあれば、\nパーティに追加してつよさを確認してみましょう!",
"statChange": "ポケモンは交代しない限り、\n次のバトルでも能力変化が維持されます。\n$その代わりに、トレーナーバトルや新しいバイオームに\n入る直前に自動的にリセットされます。\n$CキーまたはShiftキーを押し続けると、\n現在のポケモンの能力変化を確認できます。\n$Vキーを押すと、\n相手が使用した技も確認できます。\n$ただし、今のバトルで相手ポケモンがすでに\n使った技のみが表示されます。",
"selectItem": "バトルが終わるたびに、\nランダムなアイテム3つの中から1つを選んで獲得します。\n$種類は消耗品、ポケモンの持ち物、\n永続的なパッシブアイテムなど様々です。\n$ほとんどの消耗しない道具は\n効果が累積されます。\n$進化用など一部のアイテムは\n使用できる場合にのみ登場します。\n$持ち物を渡す機能を使用して\nポケモン同士で道具を持たせることもできます。\n$持ち物があれば、アイテム選択画面の\n右下に渡す機能が表示されます。\n$お金で消耗品を購入することもでき、\nウェーブが進むにつれて購入可能な種類が増えます。\n$アイテムを選択すると次のウェーブに進むため、\nまず消耗品の購入を行ってください。",
"eggGacha": "この画面でポケモンのたまごクーポンを\nガチャができます。\n$卵は戦闘を繰り返すうちにふかします。\n珍しいほどもっと長くかかります。\n$ふかさせたポケモンはパーティーに追加されず、\nスターティングに登録されます。\n$卵からふかしたポケモンは一般的に\n野生で捕まえたポケモンよりも高い個体値を持ちます。\n$一部のポケモンは卵からしか手に入りません。\n$各ガチャマシンがそれぞれ異なるボーナスを持っているため、\n好きな方を使ってみてください!,"
}
"intro": "PokéRogueへ ようこそ! グライク要素が\n加わった バトル中心の ポケモンファンゲームです。\n$このゲームは 収益を上げず、Pokémonおよび 使用される\n著作権資産に 対する所有権を 主張しません。\n$ゲームは まだ開発中ですが、完全に プレイすることが できます。\nバグ報告は ディスコードコミュニティを ご利用ください。\n$ゲームが 遅い場合は、ブラウザ設定で「ハードウェア\nアクセラレーション」が オンになっている ことを 確認してください",
"accessMenu": "メニューを開くには 入力待ちの間に MキーEscを 押してください。\nメニューには 設定や 様々な機能が 含まれています。",
"menu": "このメニューから 設定が 開けます。\n$設定では、ゲームの速さや ウィンドウタイプなどの オプションを 変更できます。\n$ここには 様々な機能が ありますので、\nぜひ 確認してみてください!",
"starterSelect": "この画面で Zキー空白キーを押して ポケモンが 選択できます。\n選んだポケモンは 最初の手持ちに なります。\n$各ポケモンは ポイントが ある。最大6つを 選べますが\nポケモンのポイントが 合計10を超えては いけません。\n$ポケモンを 捕まえたり タマゴからふかしたり することで\n選択できる 性別、特性、フォルムなどの 幅を広げられます。\n$個体値も 徐々に 累積して 高くなるので、\n同じポケモンを たくさん 捕まえて みてください!",
"pokerus": "毎日、無作為に スターターの\n3種類には 紫色の枠が 表示されます。\n$登録された スターターの 中に いれば、\n手持ちに加えて 強さを 確認してみましょう!",
"statChange": "ポケモンを 入れ替えない限り、\n次のバトルでも 能力変化は なくなりません。\n$その代わりに、トレーナーバトルや 新しいバイオームに\n入る直前に 自動的に 能力変化は 元に戻ります。\n$CキーShiftキーを 押し続けると、\n場にいるポケモンの 能力変化を 確認できます。\n$Vキーを押すと、\n相手が出した技も 確認できます。\n$ただし、現在のバトルでの 相手ポケモンが\nすでに使った 技のみが 表示されます。",
"selectItem": "バトルが 終わるたびには、「ショップ」という\n画面で 3つのご褒美から 1つが選べます。\n$種類は 消耗品、ポケモンの持ち物や道具、\n永続的な パッシブアイテムなど 様々です。\n$ほとんどの 消耗しない 道具は\n効果が 累積されます。\n$例えば 進化アイテムなどの ご褒美は\n使用できる 場合にのみ 登場します。\n$持ち物や道具が\n手持ちポケモン間に 移動できる\n$持ち物や道具が あれば、ショップ画面の\n右下に「アイテム移行」が 表示されます。\n$ショップ画面で お金で 消耗品を 買えます。\nラウンドが 進むにつれて 買えるアイテムが 増えます。\n$ご褒美を 選択すると 次のラウンドに\n進むから、まず 消耗品を ってください。",
"eggGacha": "この画面では、「タマゴクーポン」で\nポケモンのタマゴを 取得できます。\n$タマゴは ラウンドが進めるうちに ふかします。\nタマゴのふかは レア度によって 時間が かかります。\n$ふかしたポケモンは 手持ちに 加えられず、\nスターターに 登録されます。\n$ふかしたポケモンは 一般的に\n野生ポケモンよりも 高い個体値があります。\n$あるポケモンは タマゴからしか 手に入りません。\n$各ガチャマシンは 個性的なボーナスが あるますから、\n好きな方から 引いてみてください,"
}

View File

@ -12,6 +12,7 @@
"blockItemTheft": "{{abilityName}} de {{pokemonNameWithAffix}}\nprevine o roubo de itens!",
"typeImmunityHeal": "{{abilityName}} de {{pokemonNameWithAffix}}\nrestaurou um pouco de PS!",
"nonSuperEffectiveImmunity": "{{pokemonNameWithAffix}} evitou dano\ncom {{abilityName}}!",
"fullHpResistType": "{{pokemonNameWithAffix}} fez seu casco brilhar!\nEstá distorcendo o confronte de tipos!",
"moveImmunity": "Isso não afeta {{pokemonNameWithAffix}}!",
"reverseDrain": "{{pokemonNameWithAffix}} absorveu a gosma líquida!",
"postDefendTypeChange": "{{abilityName}} de {{pokemonNameWithAffix}}\ntransformou-o no tipo {{typeName}}!",

View File

@ -44,6 +44,7 @@
"moveNotImplemented": "{{moveName}} ainda não foi implementado e não pode ser usado.",
"moveNoPP": "Não há mais PP\npara esse movimento!",
"moveDisabled": "Não se pode usar {{moveName}} porque foi desabilitado!",
"disableInterruptedMove": "{{moveName}} de {{pokemonNameWithAffix}}\nestá desabilitado!",
"noPokeballForce": "Uma força misteriosa\nte impede de usar Poké Bolas.",
"noPokeballTrainer": "Não se pode capturar\nPokémon dos outros!",
"noPokeballMulti": "Não se pode lançar Poké Bolas\nquando há mais de um Pokémon!",
@ -61,6 +62,7 @@
"skipItemQuestion": "Tem certeza de que não quer escolher um item?",
"itemStackFull": "O estoque de {{fullItemName}} está cheio.\nVocê receberá {{itemName}} no lugar.",
"eggHatching": "Opa?",
"eggSkipPrompt": "Pular para súmario de ovos?",
"ivScannerUseQuestion": "Quer usar o Scanner de IVs em {{pokemonName}}?",
"wildPokemonWithAffix": "{{pokemonName}} selvagem",
"foePokemonWithAffix": "{{pokemonName}} adversário",
@ -89,7 +91,7 @@
"statSeverelyFell_other": "{{stats}} de {{pokemonNameWithAffix}} diminuíram severamente!",
"statWontGoAnyLower_one": "{{stats}} de {{pokemonNameWithAffix}} não vai mais diminuir!",
"statWontGoAnyLower_other": "{{stats}} de {{pokemonNameWithAffix}} não vão mais diminuir!",
"transformedIntoType": "{{pokemonName}} transformed\ninto the {{type}} type!",
"transformedIntoType": "{{pokemonName}} se transformou\nno tipo {{type}}!",
"ppReduced": "O PP do movimento {{moveName}} de\n{{targetName}} foi reduzido em {{reduction}}!",
"retryBattle": "Você gostaria de tentar novamente desde o início da batalha?",
"unlockedSomething": "{{unlockedThing}}\nfoi desbloqueado.",

View File

@ -67,5 +67,7 @@
"saltCuredLapse": "{{pokemonNameWithAffix}} foi ferido pelo {{moveName}}!",
"cursedOnAdd": "{{pokemonNameWithAffix}} cortou seus PS pela metade e amaldiçoou {{pokemonName}}!",
"cursedLapse": "{{pokemonNameWithAffix}} foi ferido pelo Curse!",
"stockpilingOnAdd": "{{pokemonNameWithAffix}} estocou {{stockpiledCount}}!"
"stockpilingOnAdd": "{{pokemonNameWithAffix}} estocou {{stockpiledCount}}!",
"disabledOnAdd": "{{moveName}} de {{pokemonNameWithAffix}}\nfoi desabilitado!",
"disabledLapse": "{{moveName}} de {{pokemonNameWithAffix}}\nnão está mais desabilitado."
}

View File

@ -1,6 +1,7 @@
{
"title": "Desafios",
"illegalEvolution": "{{pokemon}} não pode ser escolhido\nnesse desafio!",
"noneSelected": "Nada Selecionado",
"singleGeneration": {
"name": "Geração Única",
"desc": "Você só pode user Pokémon da {{gen}} geração.",
@ -33,4 +34,4 @@
"value.0": "Desligado",
"value.1": "Ligado"
}
}
}

View File

@ -99,7 +99,7 @@ class DefaultOverrides {
readonly PASSIVE_ABILITY_OVERRIDE: Abilities = Abilities.NONE;
readonly STATUS_OVERRIDE: StatusEffect = StatusEffect.NONE;
readonly GENDER_OVERRIDE: Gender | null = null;
readonly MOVESET_OVERRIDE: Array<Moves> = [];
readonly MOVESET_OVERRIDE: Moves | Array<Moves> = [];
readonly SHINY_OVERRIDE: boolean = false;
readonly VARIANT_OVERRIDE: Variant = 0;
@ -112,7 +112,7 @@ class DefaultOverrides {
readonly OPP_PASSIVE_ABILITY_OVERRIDE: Abilities = Abilities.NONE;
readonly OPP_STATUS_OVERRIDE: StatusEffect = StatusEffect.NONE;
readonly OPP_GENDER_OVERRIDE: Gender | null = null;
readonly OPP_MOVESET_OVERRIDE: Array<Moves> = [];
readonly OPP_MOVESET_OVERRIDE: Moves | Array<Moves> = [];
readonly OPP_SHINY_OVERRIDE: boolean = false;
readonly OPP_VARIANT_OVERRIDE: Variant = 0;
readonly OPP_IVS_OVERRIDE: number | number[] = [];

View File

@ -1,5 +1,5 @@
import BattleScene from "#app/battle-scene";
import { BattleType, BattlerIndex } from "#app/battle";
import { BattlerIndex, BattleType } from "#app/battle";
import { applyAbAttrs, SyncEncounterNatureAbAttr } from "#app/data/ability";
import { getCharVariantFromDialogue } from "#app/data/dialogue";
import { TrainerSlot } from "#app/data/trainer-config";
@ -10,7 +10,7 @@ import { Species } from "#app/enums/species";
import { EncounterPhaseEvent } from "#app/events/battle-scene";
import Pokemon, { FieldPosition } from "#app/field/pokemon";
import { getPokemonNameWithAffix } from "#app/messages";
import { regenerateModifierPoolThresholds, ModifierPoolType } from "#app/modifier/modifier-type";
import { ModifierPoolType, regenerateModifierPoolThresholds } from "#app/modifier/modifier-type";
import { BoostBugSpawnModifier, IvScannerModifier, TurnHeldItemTransferModifier } from "#app/modifier/modifier";
import { achvs } from "#app/system/achv";
import { handleTutorial, Tutorial } from "#app/tutorial";
@ -18,6 +18,7 @@ import { Mode } from "#app/ui/ui";
import i18next from "i18next";
import { BattlePhase } from "./battle-phase";
import * as Utils from "#app/utils";
import { randSeedInt } from "#app/utils";
import { CheckSwitchPhase } from "./check-switch-phase";
import { GameOverPhase } from "./game-over-phase";
import { PostSummonPhase } from "./post-summon-phase";
@ -32,7 +33,6 @@ import { MysteryEncounterMode } from "#enums/mystery-encounter-mode";
import { doTrainerExclamation } from "#app/data/mystery-encounters/utils/encounter-phase-utils";
import { getEncounterText } from "#app/data/mystery-encounters/utils/encounter-dialogue-utils";
import { MysteryEncounterPhase } from "#app/phases/mystery-encounter-phases";
import { randSeedInt } from "#app/utils";
import { getGoldenBugNetSpecies } from "#app/data/mystery-encounters/utils/encounter-pokemon-utils";
export class EncounterPhase extends BattlePhase {
@ -153,8 +153,7 @@ export class EncounterPhase extends BattlePhase {
loadEnemyAssets.push(battle.trainer?.loadAssets().then(() => battle.trainer?.initSprite())!); // TODO: is this bang correct?
} else if (battle.battleType === BattleType.MYSTERY_ENCOUNTER) {
if (!battle.mysteryEncounter) {
const newEncounter = this.scene.getMysteryEncounter(mysteryEncounter);
battle.mysteryEncounter = newEncounter;
battle.mysteryEncounter = this.scene.getMysteryEncounter(mysteryEncounter?.encounterType);
}
if (battle.mysteryEncounter.introVisuals) {
loadEnemyAssets.push(battle.mysteryEncounter.introVisuals.loadAssets().then(() => battle.mysteryEncounter!.introVisuals!.initSprite()));

View File

@ -65,6 +65,8 @@ export class FaintPhase extends PokemonPhase {
if (pokemon.turnData?.attacksReceived?.length) {
const lastAttack = pokemon.turnData.attacksReceived[0];
applyPostFaintAbAttrs(PostFaintAbAttr, pokemon, this.scene.getPokemonById(lastAttack.sourceId)!, new PokemonMove(lastAttack.move).getMove(), lastAttack.result); // TODO: is this bang correct?
} else { //If killed by indirect damage, apply post-faint abilities without providing a last move
applyPostFaintAbAttrs(PostFaintAbAttr, pokemon);
}
const alivePlayField = this.scene.getField(true);

View File

@ -1,6 +1,6 @@
import BattleScene from "#app/battle-scene";
import { initMoveAnim, loadMoveAnimAssets } from "#app/data/battle-anims";
import { allMoves } from "#app/data/move";
import Move, { allMoves } from "#app/data/move";
import { SpeciesFormChangeMoveLearnedTrigger } from "#app/data/pokemon-forms";
import { Moves } from "#app/enums/moves";
import { getPokemonNameWithAffix } from "#app/messages";
@ -9,14 +9,15 @@ import { SummaryUiMode } from "#app/ui/summary-ui-handler";
import { Mode } from "#app/ui/ui";
import i18next from "i18next";
import { PlayerPartyMemberPokemonPhase } from "./player-party-member-pokemon-phase";
import Pokemon from "#app/field/pokemon";
export class LearnMovePhase extends PlayerPartyMemberPokemonPhase {
private moveId: Moves;
private messageMode: Mode;
private fromTM: boolean;
constructor(scene: BattleScene, partyMemberIndex: integer, moveId: Moves, fromTM?: boolean) {
super(scene, partyMemberIndex);
this.moveId = moveId;
this.fromTM = fromTM ?? false;
}
@ -26,87 +27,128 @@ export class LearnMovePhase extends PlayerPartyMemberPokemonPhase {
const pokemon = this.getPokemon();
const move = allMoves[this.moveId];
const currentMoveset = pokemon.getMoveset();
const existingMoveIndex = pokemon.getMoveset().findIndex(m => m?.moveId === move.id);
if (existingMoveIndex > -1) {
// The game first checks if the Pokemon already has the move and ends the phase if it does.
const hasMoveAlready = currentMoveset.some(m => m?.moveId === move.id) && this.moveId !== Moves.SKETCH;
if (hasMoveAlready) {
return this.end();
}
const emptyMoveIndex = pokemon.getMoveset().length < 4
? pokemon.getMoveset().length
: pokemon.getMoveset().findIndex(m => m === null);
const messageMode = this.scene.ui.getHandler() instanceof EvolutionSceneHandler
? Mode.EVOLUTION_SCENE
: Mode.MESSAGE;
if (emptyMoveIndex > -1) {
pokemon.setMove(emptyMoveIndex, this.moveId);
if (this.fromTM) {
pokemon.usedTMs.push(this.moveId);
}
initMoveAnim(this.scene, this.moveId).then(() => {
loadMoveAnimAssets(this.scene, [this.moveId], true)
.then(() => {
this.scene.ui.setMode(messageMode).then(() => {
// Sound loaded into game as is
this.scene.playSound("level_up_fanfare");
this.scene.ui.showText(i18next.t("battle:learnMove", { pokemonName: getPokemonNameWithAffix(pokemon), moveName: move.name }), null, () => {
this.scene.triggerPokemonFormChange(pokemon, SpeciesFormChangeMoveLearnedTrigger, true);
this.end();
}, messageMode === Mode.EVOLUTION_SCENE ? 1000 : null, true);
});
});
});
this.messageMode = this.scene.ui.getHandler() instanceof EvolutionSceneHandler ? Mode.EVOLUTION_SCENE : Mode.MESSAGE;
this.scene.ui.setMode(this.messageMode);
// If the Pokemon has less than 4 moves, the new move is added to the largest empty moveset index
// If it has 4 moves, the phase then checks if the player wants to replace the move itself.
if (currentMoveset.length < 4) {
this.learnMove(currentMoveset.length, move, pokemon);
} else {
this.scene.ui.setMode(messageMode).then(() => {
this.scene.ui.showText(i18next.t("battle:learnMovePrompt", { pokemonName: getPokemonNameWithAffix(pokemon), moveName: move.name }), null, () => {
this.scene.ui.showText(i18next.t("battle:learnMoveLimitReached", { pokemonName: getPokemonNameWithAffix(pokemon) }), null, () => {
this.scene.ui.showText(i18next.t("battle:learnMoveReplaceQuestion", { moveName: move.name }), null, () => {
const noHandler = () => {
this.scene.ui.setMode(messageMode).then(() => {
this.scene.ui.showText(i18next.t("battle:learnMoveStopTeaching", { moveName: move.name }), null, () => {
this.scene.ui.setModeWithoutClear(Mode.CONFIRM, () => {
this.scene.ui.setMode(messageMode);
this.scene.ui.showText(i18next.t("battle:learnMoveNotLearned", { pokemonName: getPokemonNameWithAffix(pokemon), moveName: move.name }), null, () => this.end(), null, true);
}, () => {
this.scene.ui.setMode(messageMode);
this.scene.unshiftPhase(new LearnMovePhase(this.scene, this.partyMemberIndex, this.moveId));
this.end();
});
});
});
};
this.scene.ui.setModeWithoutClear(Mode.CONFIRM, () => {
this.scene.ui.setMode(messageMode);
this.scene.ui.showText(i18next.t("battle:learnMoveForgetQuestion"), null, () => {
this.scene.ui.setModeWithoutClear(Mode.SUMMARY, this.getPokemon(), SummaryUiMode.LEARN_MOVE, move, (moveIndex: integer) => {
if (moveIndex === 4) {
noHandler();
return;
}
this.scene.ui.setMode(messageMode).then(() => {
this.scene.ui.showText(i18next.t("battle:countdownPoof"), null, () => {
this.scene.ui.showText(i18next.t("battle:learnMoveForgetSuccess", { pokemonName: getPokemonNameWithAffix(pokemon), moveName: pokemon.moveset[moveIndex]!.getName() }), null, () => { // TODO: is the bang correct?
this.scene.ui.showText(i18next.t("battle:learnMoveAnd"), null, () => {
if (this.fromTM) {
pokemon.usedTMs.push(this.moveId);
}
pokemon.setMove(moveIndex, Moves.NONE);
this.scene.unshiftPhase(new LearnMovePhase(this.scene, this.partyMemberIndex, this.moveId));
this.end();
}, null, true);
}, null, true);
}, null, true);
});
});
}, null, true);
}, noHandler);
});
}, null, true);
}, null, true);
});
this.replaceMoveCheck(move, pokemon);
}
}
/**
* This displays a chain of messages (listed below) and asks if the user wishes to forget a move.
*
* > [Pokemon] wants to learn the move [MoveName]
* > However, [Pokemon] already knows four moves.
* > Should a move be forgotten and replaced with [MoveName]? --> `Mode.CONFIRM` -> Yes: Go to `this.forgetMoveProcess()`, No: Go to `this.rejectMoveAndEnd()`
* @param move The Move to be learned
* @param Pokemon The Pokemon learning the move
*/
async replaceMoveCheck(move: Move, pokemon: Pokemon) {
const learnMovePrompt = i18next.t("battle:learnMovePrompt", { pokemonName: getPokemonNameWithAffix(pokemon), moveName: move.name });
const moveLimitReached = i18next.t("battle:learnMoveLimitReached", { pokemonName: getPokemonNameWithAffix(pokemon) });
const shouldReplaceQ = i18next.t("battle:learnMoveReplaceQuestion", { moveName: move.name });
const preQText = [learnMovePrompt, moveLimitReached].join("$");
await this.scene.ui.showTextPromise(preQText);
await this.scene.ui.showTextPromise(shouldReplaceQ, undefined, false);
await this.scene.ui.setModeWithoutClear(Mode.CONFIRM,
() => this.forgetMoveProcess(move, pokemon), // Yes
() => { // No
this.scene.ui.setMode(this.messageMode);
this.rejectMoveAndEnd(move, pokemon);
}
);
}
/**
* This facilitates the process in which an old move is chosen to be forgotten.
*
* > Which move should be forgotten?
*
* The game then goes `Mode.SUMMARY` to select a move to be forgotten.
* If a player does not select a move or chooses the new move (`moveIndex === 4`), the game goes to `this.rejectMoveAndEnd()`.
* If an old move is selected, the function then passes the `moveIndex` to `this.learnMove()`
* @param move The Move to be learned
* @param Pokemon The Pokemon learning the move
*/
async forgetMoveProcess(move: Move, pokemon: Pokemon) {
this.scene.ui.setMode(this.messageMode);
await this.scene.ui.showTextPromise(i18next.t("battle:learnMoveForgetQuestion"), undefined, true);
await this.scene.ui.setModeWithoutClear(Mode.SUMMARY, pokemon, SummaryUiMode.LEARN_MOVE, move, (moveIndex: integer) => {
if (moveIndex === 4) {
this.scene.ui.setMode(this.messageMode).then(() => this.rejectMoveAndEnd(move, pokemon));
return;
}
const forgetSuccessText = i18next.t("battle:learnMoveForgetSuccess", { pokemonName: getPokemonNameWithAffix(pokemon), moveName: pokemon.moveset[moveIndex]!.getName() });
const fullText = [i18next.t("battle:countdownPoof"), forgetSuccessText, i18next.t("battle:learnMoveAnd")].join("$");
this.scene.ui.setMode(this.messageMode).then(() => this.learnMove(moveIndex, move, pokemon, fullText));
});
}
/**
* This asks the player if they wish to end the current move learning process.
*
* > Stop trying to teach [MoveName]? --> `Mode.CONFIRM` --> Yes: > [Pokemon] did not learn the move [MoveName], No: `this.replaceMoveCheck()`
*
* If the player wishes to not teach the Pokemon the move, it displays a message and ends the phase.
* If the player reconsiders, it repeats the process for a Pokemon with a full moveset once again.
* @param move The Move to be learned
* @param Pokemon The Pokemon learning the move
*/
async rejectMoveAndEnd(move: Move, pokemon: Pokemon) {
await this.scene.ui.showTextPromise(i18next.t("battle:learnMoveStopTeaching", { moveName: move.name }), undefined, false);
this.scene.ui.setModeWithoutClear(Mode.CONFIRM,
() => {
this.scene.ui.setMode(this.messageMode);
this.scene.ui.showTextPromise(i18next.t("battle:learnMoveNotLearned", { pokemonName: getPokemonNameWithAffix(pokemon), moveName: move.name }), undefined, true).then(() => this.end());
},
() => {
this.scene.ui.setMode(this.messageMode);
this.replaceMoveCheck(move, pokemon);
}
);
}
/**
* This teaches the Pokemon the new move and ends the phase.
* When a Pokemon forgets a move and learns a new one, its 'Learn Move' message is significantly longer.
*
* Pokemon with a `moveset.length < 4`
* > [Pokemon] learned [MoveName]
*
* Pokemon with a `moveset.length > 4`
* > 1... 2... and 3... and Poof!
* > [Pokemon] forgot how to use [MoveName]
* > And...
* > [Pokemon] learned [MoveName]!
* @param move The Move to be learned
* @param Pokemon The Pokemon learning the move
*/
async learnMove(index: number, move: Move, pokemon: Pokemon, textMessage?: string) {
if (this.fromTM) {
pokemon.usedTMs.push(this.moveId);
}
pokemon.setMove(index, this.moveId);
initMoveAnim(this.scene, this.moveId).then(() => {
loadMoveAnimAssets(this.scene, [this.moveId], true);
this.scene.playSound("level_up_fanfare"); // Sound loaded into game as is
});
this.scene.ui.setMode(this.messageMode);
const learnMoveText = i18next.t("battle:learnMove", { pokemonName: getPokemonNameWithAffix(pokemon), moveName: move.name });
textMessage = textMessage ? textMessage+"$"+learnMoveText : learnMoveText;
await this.scene.ui.showTextPromise(textMessage, this.messageMode === Mode.EVOLUTION_SCENE ? 1000 : undefined, true);
this.scene.triggerPokemonFormChange(pokemon, SpeciesFormChangeMoveLearnedTrigger, true);
this.end();
}
}

View File

@ -952,7 +952,7 @@ export class GameData {
gameVersion: scene.game.config.gameVersion,
timestamp: new Date().getTime(),
challenges: scene.gameMode.challenges.map(c => new ChallengeData(c)),
mysteryEncounterType: scene.currentBattle.mysteryEncounter?.encounterType,
mysteryEncounterType: scene.currentBattle.mysteryEncounter?.encounterType ?? -1,
mysteryEncounterSaveData: scene.mysteryEncounterSaveData
} as SessionSaveData;
}
@ -1044,7 +1044,7 @@ export class GameData {
scene.score = sessionData.score;
scene.updateScoreText();
scene.mysteryEncounterSaveData = sessionData?.mysteryEncounterSaveData ?? new MysteryEncounterSaveData(null);
scene.mysteryEncounterSaveData = new MysteryEncounterSaveData(sessionData.mysteryEncounterSaveData);
scene.newArena(sessionData.arena.biome);

View File

@ -103,7 +103,7 @@ export default class PokemonData {
this.fusionLuck = source.fusionLuck !== undefined ? source.fusionLuck : (source.fusionShiny ? source.fusionVariant + 1 : 0);
this.usedTMs = source.usedTMs ?? [];
this.mysteryEncounterPokemonData = source.mysteryEncounterPokemonData ?? new MysteryEncounterPokemonData();
this.mysteryEncounterPokemonData = new MysteryEncounterPokemonData(source.mysteryEncounterPokemonData);
if (!forHistory) {
this.boss = (source instanceof EnemyPokemon && !!source.bossSegments) || (!this.player && !!source.boss);

View File

@ -3,7 +3,6 @@ import { Abilities } from "#enums/abilities";
import { Moves } from "#enums/moves";
import { Species } from "#enums/species";
import GameManager from "#test/utils/gameManager";
import { SPLASH_ONLY } from "#test/utils/testUtils";
import Phaser from "phaser";
import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest";
@ -27,7 +26,7 @@ describe("Abilities - Aura Break", () => {
game = new GameManager(phaserGame);
game.override.battleType("single");
game.override.moveset([Moves.MOONBLAST, Moves.DARK_PULSE, Moves.MOONBLAST, Moves.DARK_PULSE]);
game.override.enemyMoveset(SPLASH_ONLY);
game.override.enemyMoveset(Moves.SPLASH);
game.override.enemyAbility(Abilities.AURA_BREAK);
game.override.enemySpecies(Species.SHUCKLE);
});

View File

@ -5,7 +5,6 @@ import { TurnEndPhase } from "#app/phases/turn-end-phase";
import { Moves } from "#enums/moves";
import { Species } from "#enums/species";
import GameManager from "#test/utils/gameManager";
import { SPLASH_ONLY } from "#test/utils/testUtils";
import Phaser from "phaser";
import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest";
@ -31,7 +30,7 @@ describe("Abilities - Battery", () => {
game.override.enemySpecies(Species.SHUCKLE);
game.override.enemyAbility(Abilities.BALL_FETCH);
game.override.moveset([Moves.TACKLE, Moves.BREAKING_SWIPE, Moves.SPLASH, Moves.DAZZLING_GLEAM]);
game.override.enemyMoveset(SPLASH_ONLY);
game.override.enemyMoveset(Moves.SPLASH);
});
it("raises the power of allies' special moves by 30%", async () => {

View File

@ -4,7 +4,6 @@ import { Moves } from "#enums/moves";
import { Species } from "#enums/species";
import { Stat } from "#enums/stat";
import GameManager from "#test/utils/gameManager";
import { SPLASH_ONLY } from "#test/utils/testUtils";
import Phaser from "phaser";
import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest";
@ -31,7 +30,7 @@ describe("Abilities - Beast Boost", () => {
.ability(Abilities.BEAST_BOOST)
.startingLevel(2000)
.moveset([ Moves.FLAMETHROWER ])
.enemyMoveset(SPLASH_ONLY);
.enemyMoveset(Moves.SPLASH);
});
it("should prefer highest stat to boost its corresponding stat stage by 1 when winning a battle", async() => {
@ -51,7 +50,7 @@ describe("Abilities - Beast Boost", () => {
}, 20000);
it("should use in-battle overriden stats when determining the stat stage to raise by 1", async() => {
game.override.enemyMoveset(new Array(4).fill(Moves.GUARD_SPLIT));
game.override.enemyMoveset([Moves.GUARD_SPLIT]);
await game.classicMode.startBattle([Species.SLOWBRO]);

View File

@ -1,10 +1,10 @@
import { Stat } from "#enums/stat";
import GameManager from "#test/utils/gameManager";
import { Moves } from "#app/enums/moves";
import { Abilities } from "#enums/abilities";
import { Species } from "#enums/species";
import { Stat } from "#enums/stat";
import GameManager from "#test/utils/gameManager";
import Phaser from "phaser";
import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest";
import { SPLASH_ONLY } from "../utils/testUtils";
describe("Abilities - Contrary", () => {
let phaserGame: Phaser.Game;
@ -27,7 +27,7 @@ describe("Abilities - Contrary", () => {
.enemySpecies(Species.BULBASAUR)
.enemyAbility(Abilities.CONTRARY)
.ability(Abilities.INTIMIDATE)
.enemyMoveset(SPLASH_ONLY);
.enemyMoveset(Moves.SPLASH);
});
it("should invert stat changes when applied", async() => {

View File

@ -5,7 +5,6 @@ import { Species } from "#app/enums/species";
import { CommandPhase } from "#app/phases/command-phase";
import { MessagePhase } from "#app/phases/message-phase";
import GameManager from "#test/utils/gameManager";
import { SPLASH_ONLY } from "#test/utils/testUtils";
import Phaser from "phaser";
import { afterEach, beforeAll, beforeEach, describe, expect, test } from "vitest";
@ -30,7 +29,7 @@ describe("Abilities - COSTAR", () => {
game.override.battleType("double");
game.override.ability(Abilities.COSTAR);
game.override.moveset([Moves.SPLASH, Moves.NASTY_PLOT]);
game.override.enemyMoveset(SPLASH_ONLY);
game.override.enemyMoveset(Moves.SPLASH);
});

View File

@ -30,7 +30,7 @@ describe("Abilities - Dancer", () => {
.moveset([Moves.SWORDS_DANCE, Moves.SPLASH])
.enemySpecies(Species.MAGIKARP)
.enemyAbility(Abilities.DANCER)
.enemyMoveset(Array(4).fill(Moves.VICTORY_DANCE));
.enemyMoveset([Moves.VICTORY_DANCE]);
});
// Reference Link: https://bulbapedia.bulbagarden.net/wiki/Dancer_(Ability)

View File

@ -6,7 +6,6 @@ import { StatusEffect } from "#app/data/status-effect";
import { Stat } from "#enums/stat";
import GameManager from "#test/utils/gameManager";
import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest";
import { SPLASH_ONLY } from "../utils/testUtils";
const TIMEOUT = 20 * 1000;
@ -31,7 +30,7 @@ describe("Abilities - Disguise", () => {
game.override
.battleType("single")
.enemySpecies(Species.MIMIKYU)
.enemyMoveset(SPLASH_ONLY)
.enemyMoveset(Moves.SPLASH)
.starterSpecies(Species.REGIELEKI)
.moveset([Moves.SHADOW_SNEAK, Moves.VACUUM_WAVE, Moves.TOXIC_THREAD, Moves.SPLASH]);
}, TIMEOUT);
@ -108,7 +107,7 @@ describe("Abilities - Disguise", () => {
}, TIMEOUT);
it("persists form change when switched out", async () => {
game.override.enemyMoveset(Array(4).fill(Moves.SHADOW_SNEAK));
game.override.enemyMoveset([Moves.SHADOW_SNEAK]);
game.override.starterSpecies(0);
await game.classicMode.startBattle([ Species.MIMIKYU, Species.FURRET ]);
@ -194,7 +193,7 @@ describe("Abilities - Disguise", () => {
}, TIMEOUT);
it("doesn't faint twice when fainting due to Disguise break damage, nor prevent faint from Disguise break damage if using Endure", async () => {
game.override.enemyMoveset(Array(4).fill(Moves.ENDURE));
game.override.enemyMoveset([Moves.ENDURE]);
await game.classicMode.startBattle();
const mimikyu = game.scene.getEnemyPokemon()!;

View File

@ -1,9 +1,7 @@
import { Species } from "#app/enums/species";
import { TurnEndPhase } from "#app/phases/turn-end-phase";
import { Abilities } from "#enums/abilities";
import { Moves } from "#enums/moves";
import GameManager from "#test/utils/gameManager";
import { SPLASH_ONLY } from "#test/utils/testUtils";
import Phaser from "phaser";
import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest";
@ -23,63 +21,56 @@ describe("Abilities - Dry Skin", () => {
beforeEach(() => {
game = new GameManager(phaserGame);
game.override.battleType("single");
game.override.disableCrits();
game.override.enemyAbility(Abilities.DRY_SKIN);
game.override.enemyMoveset(SPLASH_ONLY);
game.override.enemySpecies(Species.CHARMANDER);
game.override.ability(Abilities.UNNERVE);
game.override.starterSpecies(Species.CHANDELURE);
game.override
.battleType("single")
.disableCrits()
.enemyAbility(Abilities.DRY_SKIN)
.enemyMoveset(Moves.SPLASH)
.enemySpecies(Species.CHARMANDER)
.ability(Abilities.BALL_FETCH)
.moveset([Moves.SUNNY_DAY, Moves.RAIN_DANCE, Moves.SPLASH, Moves.WATER_GUN])
.starterSpecies(Species.CHANDELURE);
});
it("during sunlight, lose 1/8 of maximum health at the end of each turn", async () => {
game.override.moveset([Moves.SUNNY_DAY, Moves.SPLASH]);
await game.startBattle();
await game.classicMode.startBattle();
const enemy = game.scene.getEnemyPokemon()!;
expect(enemy).not.toBe(undefined);
// first turn
let previousEnemyHp = enemy.hp;
game.move.select(Moves.SUNNY_DAY);
await game.phaseInterceptor.to(TurnEndPhase);
expect(enemy.hp).toBeLessThan(previousEnemyHp);
await game.phaseInterceptor.to("TurnEndPhase");
expect(enemy.hp).toBeLessThan(enemy.getMaxHp());
// second turn
previousEnemyHp = enemy.hp;
enemy.hp = enemy.getMaxHp();
game.move.select(Moves.SPLASH);
await game.phaseInterceptor.to(TurnEndPhase);
expect(enemy.hp).toBeLessThan(previousEnemyHp);
await game.phaseInterceptor.to("TurnEndPhase");
expect(enemy.hp).toBeLessThan(enemy.getMaxHp());
});
it("during rain, gain 1/8 of maximum health at the end of each turn", async () => {
game.override.moveset([Moves.RAIN_DANCE, Moves.SPLASH]);
await game.startBattle();
await game.classicMode.startBattle();
const enemy = game.scene.getEnemyPokemon()!;
expect(enemy).not.toBe(undefined);
enemy.hp = 1;
// first turn
let previousEnemyHp = enemy.hp;
game.move.select(Moves.RAIN_DANCE);
await game.phaseInterceptor.to(TurnEndPhase);
expect(enemy.hp).toBeGreaterThan(previousEnemyHp);
await game.phaseInterceptor.to("TurnEndPhase");
expect(enemy.hp).toBeGreaterThan(1);
// second turn
previousEnemyHp = enemy.hp;
enemy.hp = 1;
game.move.select(Moves.SPLASH);
await game.phaseInterceptor.to(TurnEndPhase);
expect(enemy.hp).toBeGreaterThan(previousEnemyHp);
await game.phaseInterceptor.to("TurnEndPhase");
expect(enemy.hp).toBeGreaterThan(1);
});
it("opposing fire attacks do 25% more damage", async () => {
game.override.moveset([Moves.FLAMETHROWER]);
await game.startBattle();
await game.classicMode.startBattle();
const enemy = game.scene.getEnemyPokemon()!;
const initialHP = 1000;
@ -87,72 +78,65 @@ describe("Abilities - Dry Skin", () => {
// first turn
game.move.select(Moves.FLAMETHROWER);
await game.phaseInterceptor.to(TurnEndPhase);
await game.phaseInterceptor.to("TurnEndPhase");
const fireDamageTakenWithDrySkin = initialHP - enemy.hp;
expect(enemy.hp > 0);
enemy.hp = initialHP;
game.override.enemyAbility(Abilities.NONE);
// second turn
game.move.select(Moves.FLAMETHROWER);
await game.phaseInterceptor.to(TurnEndPhase);
await game.phaseInterceptor.to("TurnEndPhase");
const fireDamageTakenWithoutDrySkin = initialHP - enemy.hp;
expect(fireDamageTakenWithDrySkin).toBeGreaterThan(fireDamageTakenWithoutDrySkin);
});
it("opposing water attacks heal 1/4 of maximum health and deal no damage", async () => {
game.override.moveset([Moves.WATER_GUN]);
await game.startBattle();
await game.classicMode.startBattle();
const enemy = game.scene.getEnemyPokemon()!;
expect(enemy).not.toBe(undefined);
enemy.hp = 1;
game.move.select(Moves.WATER_GUN);
await game.phaseInterceptor.to(TurnEndPhase);
await game.phaseInterceptor.to("TurnEndPhase");
expect(enemy.hp).toBeGreaterThan(1);
});
it("opposing water attacks do not heal if they were protected from", async () => {
game.override.moveset([Moves.WATER_GUN]);
game.override.enemyMoveset([Moves.PROTECT]);
await game.startBattle();
await game.classicMode.startBattle();
const enemy = game.scene.getEnemyPokemon()!;
expect(enemy).not.toBe(undefined);
enemy.hp = 1;
game.override.enemyMoveset([Moves.PROTECT, Moves.PROTECT, Moves.PROTECT, Moves.PROTECT]);
game.move.select(Moves.WATER_GUN);
await game.phaseInterceptor.to(TurnEndPhase);
await game.phaseInterceptor.to("TurnEndPhase");
expect(enemy.hp).toBe(1);
});
it("multi-strike water attacks only heal once", async () => {
game.override.moveset([Moves.WATER_GUN, Moves.WATER_SHURIKEN]);
await game.startBattle();
await game.classicMode.startBattle();
const enemy = game.scene.getEnemyPokemon()!;
expect(enemy).not.toBe(undefined);
enemy.hp = 1;
// first turn
game.move.select(Moves.WATER_SHURIKEN);
await game.phaseInterceptor.to(TurnEndPhase);
await game.phaseInterceptor.to("TurnEndPhase");
const healthGainedFromWaterShuriken = enemy.hp - 1;
enemy.hp = 1;
// second turn
game.move.select(Moves.WATER_GUN);
await game.phaseInterceptor.to(TurnEndPhase);
await game.phaseInterceptor.to("TurnEndPhase");
const healthGainedFromWaterGun = enemy.hp - 1;
expect(healthGainedFromWaterShuriken).toBe(healthGainedFromWaterGun);

View File

@ -7,7 +7,6 @@ import { TurnEndPhase } from "#app/phases/turn-end-phase";
import { Abilities } from "#enums/abilities";
import { Moves } from "#enums/moves";
import GameManager from "#test/utils/gameManager";
import { SPLASH_ONLY } from "#test/utils/testUtils";
import Phaser from "phaser";
import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest";
@ -38,7 +37,7 @@ describe("Abilities - Flash Fire", () => {
it("immune to Fire-type moves", async () => {
game.override.enemyMoveset(Array(4).fill(Moves.EMBER)).moveset(SPLASH_ONLY);
game.override.enemyMoveset([Moves.EMBER]).moveset(Moves.SPLASH);
await game.startBattle([Species.BLISSEY]);
const blissey = game.scene.getPlayerPokemon()!;
@ -49,7 +48,7 @@ describe("Abilities - Flash Fire", () => {
}, 20000);
it("not activate if the Pokémon is protected from the Fire-type move", async () => {
game.override.enemyMoveset(Array(4).fill(Moves.EMBER)).moveset([Moves.PROTECT]);
game.override.enemyMoveset([Moves.EMBER]).moveset([Moves.PROTECT]);
await game.startBattle([Species.BLISSEY]);
const blissey = game.scene.getPlayerPokemon()!;
@ -60,7 +59,7 @@ describe("Abilities - Flash Fire", () => {
}, 20000);
it("activated by Will-O-Wisp", async () => {
game.override.enemyMoveset(Array(4).fill(Moves.WILL_O_WISP)).moveset(SPLASH_ONLY);
game.override.enemyMoveset([Moves.WILL_O_WISP]).moveset(Moves.SPLASH);
await game.startBattle([Species.BLISSEY]);
const blissey = game.scene.getPlayerPokemon()!;
@ -75,7 +74,7 @@ describe("Abilities - Flash Fire", () => {
}, 20000);
it("activated after being frozen", async () => {
game.override.enemyMoveset(Array(4).fill(Moves.EMBER)).moveset(SPLASH_ONLY);
game.override.enemyMoveset([Moves.EMBER]).moveset(Moves.SPLASH);
game.override.statusEffect(StatusEffect.FREEZE);
await game.startBattle([Species.BLISSEY]);
@ -88,7 +87,7 @@ describe("Abilities - Flash Fire", () => {
}, 20000);
it("not passing with baton pass", async () => {
game.override.enemyMoveset(Array(4).fill(Moves.EMBER)).moveset([Moves.BATON_PASS]);
game.override.enemyMoveset([Moves.EMBER]).moveset([Moves.BATON_PASS]);
await game.startBattle([Species.BLISSEY, Species.CHANSEY]);
// ensure use baton pass after enemy moved
@ -104,7 +103,7 @@ describe("Abilities - Flash Fire", () => {
}, 20000);
it("boosts Fire-type move when the ability is activated", async () => {
game.override.enemyMoveset(Array(4).fill(Moves.FIRE_PLEDGE)).moveset([Moves.EMBER, Moves.SPLASH]);
game.override.enemyMoveset([Moves.FIRE_PLEDGE]).moveset([Moves.EMBER, Moves.SPLASH]);
game.override.enemyAbility(Abilities.FLASH_FIRE).ability(Abilities.NONE);
await game.startBattle([Species.BLISSEY]);
const blissey = game.scene.getPlayerPokemon()!;

View File

@ -5,7 +5,6 @@ import { WeatherType } from "#app/enums/weather-type";
import { Moves } from "#enums/moves";
import { Species } from "#enums/species";
import GameManager from "#test/utils/gameManager";
import { SPLASH_ONLY } from "#test/utils/testUtils";
import Phaser from "phaser";
import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest";
@ -44,7 +43,7 @@ describe("Abilities - Flower Gift", () => {
game.override
.moveset([Moves.SPLASH, Moves.RAIN_DANCE, Moves.SUNNY_DAY, Moves.SKILL_SWAP])
.enemySpecies(Species.MAGIKARP)
.enemyMoveset(SPLASH_ONLY)
.enemyMoveset(Moves.SPLASH)
.enemyAbility(Abilities.BALL_FETCH);
});
@ -92,7 +91,7 @@ describe("Abilities - Flower Gift", () => {
});
it("reverts to Overcast Form when the Pokémon loses Flower Gift, changes form under Harsh Sunlight/Sunny when it regains it", async () => {
game.override.enemyMoveset(Array(4).fill(Moves.SKILL_SWAP)).weather(WeatherType.HARSH_SUN);
game.override.enemyMoveset([Moves.SKILL_SWAP]).weather(WeatherType.HARSH_SUN);
await game.classicMode.startBattle([Species.CHERRIM]);
@ -111,7 +110,7 @@ describe("Abilities - Flower Gift", () => {
});
it("reverts to Overcast Form when the Flower Gift is suppressed, changes form under Harsh Sunlight/Sunny when it regains it", async () => {
game.override.enemyMoveset(Array(4).fill(Moves.GASTRO_ACID)).weather(WeatherType.HARSH_SUN);
game.override.enemyMoveset([Moves.GASTRO_ACID]).weather(WeatherType.HARSH_SUN);
await game.classicMode.startBattle([Species.CHERRIM, Species.MAGIKARP]);

View File

@ -10,7 +10,6 @@ import { TurnEndPhase } from "#app/phases/turn-end-phase";
import { Moves } from "#enums/moves";
import { Species } from "#enums/species";
import GameManager from "#test/utils/gameManager";
import { SPLASH_ONLY } from "#test/utils/testUtils";
import Phaser from "phaser";
import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest";
@ -67,7 +66,7 @@ describe("Abilities - Forecast", () => {
game.override
.moveset([Moves.SPLASH, Moves.RAIN_DANCE, Moves.SUNNY_DAY, Moves.TACKLE])
.enemySpecies(Species.MAGIKARP)
.enemyMoveset(SPLASH_ONLY)
.enemyMoveset(Moves.SPLASH)
.enemyAbility(Abilities.BALL_FETCH);
});
@ -229,7 +228,7 @@ describe("Abilities - Forecast", () => {
});
it("reverts to Normal Form when Forecast is suppressed, changes form to match the weather when it regains it", async () => {
game.override.enemyMoveset(Array(4).fill(Moves.GASTRO_ACID)).weather(WeatherType.RAIN);
game.override.enemyMoveset([Moves.GASTRO_ACID]).weather(WeatherType.RAIN);
await game.startBattle([Species.CASTFORM, Species.PIKACHU]);
const castform = game.scene.getPlayerPokemon()!;
@ -260,7 +259,7 @@ describe("Abilities - Forecast", () => {
});
it("does not change Castform's form until after Stealth Rock deals damage", async () => {
game.override.weather(WeatherType.RAIN).enemyMoveset(Array(4).fill(Moves.STEALTH_ROCK));
game.override.weather(WeatherType.RAIN).enemyMoveset([Moves.STEALTH_ROCK]);
await game.startBattle([Species.PIKACHU, Species.CASTFORM]);
// First turn - set up stealth rock

View File

@ -6,7 +6,6 @@ import { Moves } from "#app/enums/moves";
import { Species } from "#app/enums/species";
import { HitResult } from "#app/field/pokemon";
import GameManager from "#test/utils/gameManager";
import { SPLASH_ONLY } from "#test/utils/testUtils";
import Phaser from "phaser";
import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest";
@ -36,7 +35,7 @@ describe("Abilities - Galvanize", () => {
.moveset([Moves.TACKLE, Moves.REVELATION_DANCE, Moves.FURY_SWIPES])
.enemySpecies(Species.DUSCLOPS)
.enemyAbility(Abilities.BALL_FETCH)
.enemyMoveset(SPLASH_ONLY)
.enemyMoveset(Moves.SPLASH)
.enemyLevel(100);
});

View File

@ -0,0 +1,83 @@
import { BattlerIndex } from "#app/battle";
import { Moves } from "#app/enums/moves";
import { Species } from "#app/enums/species";
import { Stat } from "#app/enums/stat";
import { Abilities } from "#enums/abilities";
import GameManager from "#test/utils/gameManager";
import Phaser from "phaser";
import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest";
describe("Abilities - Gorilla Tactics", () => {
let phaserGame: Phaser.Game;
let game: GameManager;
const TIMEOUT = 20 * 1000;
beforeAll(() => {
phaserGame = new Phaser.Game({
type: Phaser.HEADLESS,
});
});
afterEach(() => {
game.phaseInterceptor.restoreOg();
});
beforeEach(() => {
game = new GameManager(phaserGame);
game.override
.battleType("single")
.enemyAbility(Abilities.BALL_FETCH)
.enemyMoveset([Moves.SPLASH, Moves.DISABLE])
.enemySpecies(Species.MAGIKARP)
.enemyLevel(30)
.moveset([Moves.SPLASH, Moves.TACKLE, Moves.GROWL])
.ability(Abilities.GORILLA_TACTICS);
});
it("boosts the Pokémon's Attack by 50%, but limits the Pokémon to using only one move", async () => {
await game.classicMode.startBattle([Species.GALAR_DARMANITAN]);
const darmanitan = game.scene.getPlayerPokemon()!;
const initialAtkStat = darmanitan.getStat(Stat.ATK);
game.move.select(Moves.SPLASH);
await game.forceEnemyMove(Moves.SPLASH);
await game.phaseInterceptor.to("TurnEndPhase");
expect(darmanitan.getStat(Stat.ATK, false)).toBeCloseTo(initialAtkStat * 1.5);
// Other moves should be restricted
expect(darmanitan.isMoveRestricted(Moves.TACKLE)).toBe(true);
expect(darmanitan.isMoveRestricted(Moves.SPLASH)).toBe(false);
}, TIMEOUT);
it("should struggle if the only usable move is disabled", async () => {
await game.classicMode.startBattle([Species.GALAR_DARMANITAN]);
const darmanitan = game.scene.getPlayerPokemon()!;
const enemy = game.scene.getEnemyPokemon()!;
// First turn, lock move to Growl
game.move.select(Moves.GROWL);
await game.forceEnemyMove(Moves.SPLASH);
// Second turn, Growl is interrupted by Disable
await game.toNextTurn();
game.move.select(Moves.GROWL);
await game.forceEnemyMove(Moves.DISABLE);
await game.setTurnOrder([BattlerIndex.ENEMY, BattlerIndex.PLAYER]);
await game.phaseInterceptor.to("TurnEndPhase");
expect(enemy.getStatStage(Stat.ATK)).toBe(-1); // Only the effect of the first Growl should be applied
// Third turn, Struggle is used
await game.toNextTurn();
game.move.select(Moves.TACKLE);
await game.setTurnOrder([BattlerIndex.PLAYER, BattlerIndex.ENEMY]);
await game.phaseInterceptor.to("MoveEndPhase");
expect(darmanitan.hp).toBeLessThan(darmanitan.getMaxHp());
}, TIMEOUT);
});

View File

@ -11,7 +11,6 @@ import { Moves } from "#enums/moves";
import { Species } from "#enums/species";
import Phaser from "phaser";
import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest";
import { SPLASH_ONLY } from "../utils/testUtils";
import { Stat } from "#enums/stat";
describe("Abilities - Gulp Missile", () => {
@ -49,7 +48,7 @@ describe("Abilities - Gulp Missile", () => {
.moveset([Moves.SURF, Moves.DIVE, Moves.SPLASH])
.enemySpecies(Species.SNORLAX)
.enemyAbility(Abilities.BALL_FETCH)
.enemyMoveset(SPLASH_ONLY)
.enemyMoveset(Moves.SPLASH)
.enemyLevel(5);
});
@ -108,7 +107,7 @@ describe("Abilities - Gulp Missile", () => {
});
it("deals 1/4 of the attacker's maximum HP when hit by a damaging attack", async () => {
game.override.enemyMoveset(Array(4).fill(Moves.TACKLE));
game.override.enemyMoveset([Moves.TACKLE]);
await game.startBattle([Species.CRAMORANT]);
const enemy = game.scene.getEnemyPokemon()!;
@ -121,7 +120,7 @@ describe("Abilities - Gulp Missile", () => {
});
it("does not have any effect when hit by non-damaging attack", async () => {
game.override.enemyMoveset(Array(4).fill(Moves.TAIL_WHIP));
game.override.enemyMoveset([Moves.TAIL_WHIP]);
await game.startBattle([Species.CRAMORANT]);
const cramorant = game.scene.getPlayerPokemon()!;
@ -140,7 +139,7 @@ describe("Abilities - Gulp Missile", () => {
});
it("lowers attacker's DEF stat stage by 1 when hit in Gulping form", async () => {
game.override.enemyMoveset(Array(4).fill(Moves.TACKLE));
game.override.enemyMoveset([Moves.TACKLE]);
await game.startBattle([Species.CRAMORANT]);
const cramorant = game.scene.getPlayerPokemon()!;
@ -164,7 +163,7 @@ describe("Abilities - Gulp Missile", () => {
});
it("paralyzes the enemy when hit in Gorging form", async () => {
game.override.enemyMoveset(Array(4).fill(Moves.TACKLE));
game.override.enemyMoveset([Moves.TACKLE]);
await game.startBattle([Species.CRAMORANT]);
const cramorant = game.scene.getPlayerPokemon()!;
@ -188,7 +187,7 @@ describe("Abilities - Gulp Missile", () => {
});
it("does not activate the ability when underwater", async () => {
game.override.enemyMoveset(Array(4).fill(Moves.SURF));
game.override.enemyMoveset([Moves.SURF]);
await game.startBattle([Species.CRAMORANT]);
const cramorant = game.scene.getPlayerPokemon()!;
@ -201,7 +200,7 @@ describe("Abilities - Gulp Missile", () => {
});
it("prevents effect damage but inflicts secondary effect on attacker with Magic Guard", async () => {
game.override.enemyMoveset(Array(4).fill(Moves.TACKLE)).enemyAbility(Abilities.MAGIC_GUARD);
game.override.enemyMoveset([Moves.TACKLE]).enemyAbility(Abilities.MAGIC_GUARD);
await game.startBattle([Species.CRAMORANT]);
const cramorant = game.scene.getPlayerPokemon()!;
@ -225,7 +224,7 @@ describe("Abilities - Gulp Missile", () => {
});
it("cannot be suppressed", async () => {
game.override.enemyMoveset(Array(4).fill(Moves.GASTRO_ACID));
game.override.enemyMoveset([Moves.GASTRO_ACID]);
await game.startBattle([Species.CRAMORANT]);
const cramorant = game.scene.getPlayerPokemon()!;
@ -245,7 +244,7 @@ describe("Abilities - Gulp Missile", () => {
});
it("cannot be swapped with another ability", async () => {
game.override.enemyMoveset(Array(4).fill(Moves.SKILL_SWAP));
game.override.enemyMoveset([Moves.SKILL_SWAP]);
await game.startBattle([Species.CRAMORANT]);
const cramorant = game.scene.getPlayerPokemon()!;

View File

@ -5,7 +5,6 @@ import { toDmgValue } from "#app/utils";
import { Abilities } from "#enums/abilities";
import { Moves } from "#enums/moves";
import GameManager from "#test/utils/gameManager";
import { SPLASH_ONLY } from "#test/utils/testUtils";
import Phaser from "phaser";
import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest";
@ -30,7 +29,7 @@ describe("Abilities - Heatproof", () => {
.disableCrits()
.enemySpecies(Species.CHARMANDER)
.enemyAbility(Abilities.HEATPROOF)
.enemyMoveset(SPLASH_ONLY)
.enemyMoveset(Moves.SPLASH)
.enemyLevel(100)
.starterSpecies(Species.CHANDELURE)
.ability(Abilities.BALL_FETCH)

View File

@ -4,7 +4,6 @@ import { Stat } from "#app/enums/stat";
import { Moves } from "#enums/moves";
import { Species } from "#enums/species";
import GameManager from "#test/utils/gameManager";
import { SPLASH_ONLY } from "#test/utils/testUtils";
import Phaser from "phaser";
import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest";
@ -29,7 +28,7 @@ describe("Abilities - Hustle", () => {
.moveset([ Moves.TACKLE, Moves.GIGA_DRAIN, Moves.FISSURE ])
.disableCrits()
.battleType("single")
.enemyMoveset(SPLASH_ONLY)
.enemyMoveset(Moves.SPLASH)
.enemySpecies(Species.SHUCKLE)
.enemyAbility(Abilities.BALL_FETCH);
});

View File

@ -3,7 +3,6 @@ import { Abilities } from "#enums/abilities";
import { Moves } from "#enums/moves";
import { Species } from "#enums/species";
import GameManager from "#test/utils/gameManager";
import { SPLASH_ONLY } from "#test/utils/testUtils";
import Phaser from "phaser";
import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest";
@ -29,7 +28,7 @@ describe("Abilities - Hyper Cutter", () => {
.ability(Abilities.BALL_FETCH)
.enemySpecies(Species.SHUCKLE)
.enemyAbility(Abilities.HYPER_CUTTER)
.enemyMoveset(SPLASH_ONLY);
.enemyMoveset(Moves.SPLASH);
});
// Reference Link: https://bulbapedia.bulbagarden.net/wiki/Hyper_Cutter_(Ability)

View File

@ -6,7 +6,6 @@ import { TurnEndPhase } from "#app/phases/turn-end-phase";
import { Moves } from "#enums/moves";
import { Stat, BATTLE_STATS, EFFECTIVE_STATS } from "#enums/stat";
import { Abilities } from "#enums/abilities";
import { SPLASH_ONLY } from "../utils/testUtils";
// TODO: Add more tests once Imposter is fully implemented
describe("Abilities - Imposter", () => {
@ -31,9 +30,9 @@ describe("Abilities - Imposter", () => {
.enemyLevel(200)
.enemyAbility(Abilities.BEAST_BOOST)
.enemyPassiveAbility(Abilities.BALL_FETCH)
.enemyMoveset(SPLASH_ONLY)
.enemyMoveset(Moves.SPLASH)
.ability(Abilities.IMPOSTER)
.moveset(SPLASH_ONLY);
.moveset(Moves.SPLASH);
});
it("should copy species, ability, gender, all stats except HP, all stat stages, moveset, and types of target", async () => {
@ -77,7 +76,7 @@ describe("Abilities - Imposter", () => {
}, 20000);
it("should copy in-battle overridden stats", async () => {
game.override.enemyMoveset(new Array(4).fill(Moves.POWER_SPLIT));
game.override.enemyMoveset([Moves.POWER_SPLIT]);
await game.startBattle([
Species.DITTO

View File

@ -7,7 +7,6 @@ import { getMovePosition } from "#test/utils/gameManagerUtils";
import { Abilities } from "#enums/abilities";
import { Moves } from "#enums/moves";
import { Species } from "#enums/species";
import { SPLASH_ONLY } from "#test/utils/testUtils";
describe("Abilities - Intimidate", () => {
let phaserGame: Phaser.Game;
@ -31,7 +30,7 @@ describe("Abilities - Intimidate", () => {
.enemyPassiveAbility(Abilities.HYDRATION)
.ability(Abilities.INTIMIDATE)
.startingWave(3)
.enemyMoveset(SPLASH_ONLY);
.enemyMoveset(Moves.SPLASH);
});
it("should lower ATK stat stage by 1 of enemy Pokemon on entry and player switch", async () => {
@ -108,7 +107,7 @@ describe("Abilities - Intimidate", () => {
it("should lower ATK stat stage by 1 for every switch", async () => {
game.override.moveset([Moves.SPLASH])
.enemyMoveset(new Array(4).fill(Moves.VOLT_SWITCH))
.enemyMoveset([Moves.VOLT_SWITCH])
.startingWave(5);
await game.classicMode.startBattle([ Species.MIGHTYENA, Species.POOCHYENA ]);

View File

@ -9,7 +9,6 @@ import { Biome } from "#enums/biome";
import { Moves } from "#enums/moves";
import { Species } from "#enums/species";
import GameManager from "#test/utils/gameManager";
import { SPLASH_ONLY } from "#test/utils/testUtils";
import Phaser from "phaser";
import { afterEach, beforeAll, beforeEach, describe, expect, test, vi } from "vitest";
@ -183,7 +182,7 @@ describe("Abilities - Libero", () => {
"ability applies correctly even if the pokemon's move misses",
async () => {
game.override.moveset([Moves.TACKLE]);
game.override.enemyMoveset(SPLASH_ONLY);
game.override.enemyMoveset(Moves.SPLASH);
await game.startBattle([Species.MAGIKARP]);

View File

@ -8,7 +8,6 @@ import { BattlerTagType } from "#enums/battler-tag-type";
import { Moves } from "#enums/moves";
import { Species } from "#enums/species";
import GameManager from "#test/utils/gameManager";
import { SPLASH_ONLY } from "#test/utils/testUtils";
import Phaser from "phaser";
import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest";
@ -39,7 +38,7 @@ describe("Abilities - Magic Guard", () => {
/** Enemy Pokemon overrides */
game.override.enemySpecies(Species.SNORLAX);
game.override.enemyAbility(Abilities.INSOMNIA);
game.override.enemyMoveset(SPLASH_ONLY);
game.override.enemyMoveset(Moves.SPLASH);
game.override.enemyLevel(100);
});

View File

@ -3,7 +3,6 @@ import { Abilities } from "#enums/abilities";
import { Moves } from "#enums/moves";
import { Species } from "#enums/species";
import GameManager from "#test/utils/gameManager";
import { SPLASH_ONLY } from "#test/utils/testUtils";
import Phaser from "phaser";
import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest";
@ -29,8 +28,8 @@ describe("Abilities - Moody", () => {
.enemySpecies(Species.RATTATA)
.enemyAbility(Abilities.BALL_FETCH)
.ability(Abilities.MOODY)
.enemyMoveset(SPLASH_ONLY)
.moveset(SPLASH_ONLY);
.enemyMoveset(Moves.SPLASH)
.moveset(Moves.SPLASH);
});
it("should increase one stat stage by 2 and decrease a different stat stage by 1",

View File

@ -5,7 +5,6 @@ import { Moves } from "#enums/moves";
import { Species } from "#enums/species";
import Phaser from "phaser";
import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest";
import { SPLASH_ONLY } from "../utils/testUtils";
import { BattlerIndex } from "#app/battle";
import { EnemyCommandPhase } from "#app/phases/enemy-command-phase";
import { VictoryPhase } from "#app/phases/victory-phase";
@ -34,7 +33,7 @@ describe("Abilities - Moxie", () => {
game.override.ability(Abilities.MOXIE);
game.override.startingLevel(2000);
game.override.moveset([ moveToUse ]);
game.override.enemyMoveset(SPLASH_ONLY);
game.override.enemyMoveset(Moves.SPLASH);
});
it("should raise ATK stat stage by 1 when winning a battle", async() => {

View File

@ -7,7 +7,6 @@ import { Abilities } from "#enums/abilities";
import { Moves } from "#enums/moves";
import { Species } from "#enums/species";
import GameManager from "#test/utils/gameManager";
import { SPLASH_ONLY } from "#test/utils/testUtils";
import Phaser from "phaser";
import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest";
@ -34,7 +33,7 @@ describe("Abilities - Parental Bond", () => {
game.override.ability(Abilities.PARENTAL_BOND);
game.override.enemySpecies(Species.SNORLAX);
game.override.enemyAbility(Abilities.FUR_COAT);
game.override.enemyMoveset(SPLASH_ONLY);
game.override.enemyMoveset(Moves.SPLASH);
game.override.startingLevel(100);
game.override.enemyLevel(100);
});
@ -175,7 +174,7 @@ describe("Abilities - Parental Bond", () => {
"should not apply multiplier to counter moves",
async () => {
game.override.moveset([Moves.COUNTER]);
game.override.enemyMoveset(Array(4).fill(Moves.TACKLE));
game.override.enemyMoveset([Moves.TACKLE]);
await game.classicMode.startBattle([Species.SHUCKLE]);
@ -465,7 +464,7 @@ describe("Abilities - Parental Bond", () => {
"should not cause user to hit into King's Shield more than once",
async () => {
game.override.moveset([Moves.TACKLE]);
game.override.enemyMoveset(Array(4).fill(Moves.KINGS_SHIELD));
game.override.enemyMoveset([Moves.KINGS_SHIELD]);
await game.classicMode.startBattle([Species.MAGIKARP]);

View File

@ -8,7 +8,6 @@ import { Species } from "#enums/species";
import GameManager from "#test/utils/gameManager";
import Phaser from "phaser";
import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest";
import { SPLASH_ONLY } from "../utils/testUtils";
describe("Abilities - Pastel Veil", () => {
let phaserGame: Phaser.Game;
@ -31,7 +30,7 @@ describe("Abilities - Pastel Veil", () => {
.moveset([Moves.TOXIC_THREAD, Moves.SPLASH])
.enemyAbility(Abilities.BALL_FETCH)
.enemySpecies(Species.SUNKERN)
.enemyMoveset(SPLASH_ONLY);
.enemyMoveset(Moves.SPLASH);
});
it("prevents the user and its allies from being afflicted by poison", async () => {

View File

@ -5,7 +5,6 @@ import { TurnEndPhase } from "#app/phases/turn-end-phase";
import { Moves } from "#enums/moves";
import { Species } from "#enums/species";
import GameManager from "#test/utils/gameManager";
import { SPLASH_ONLY } from "#test/utils/testUtils";
import Phaser from "phaser";
import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest";
@ -29,7 +28,7 @@ describe("Abilities - Power Spot", () => {
game = new GameManager(phaserGame);
game.override.battleType("double");
game.override.moveset([Moves.TACKLE, Moves.BREAKING_SWIPE, Moves.SPLASH, Moves.DAZZLING_GLEAM]);
game.override.enemyMoveset(SPLASH_ONLY);
game.override.enemyMoveset(Moves.SPLASH);
game.override.enemySpecies(Species.SHUCKLE);
game.override.enemyAbility(Abilities.BALL_FETCH);
});

View File

@ -9,7 +9,6 @@ import { Biome } from "#enums/biome";
import { Moves } from "#enums/moves";
import { Species } from "#enums/species";
import GameManager from "#test/utils/gameManager";
import { SPLASH_ONLY } from "#test/utils/testUtils";
import Phaser from "phaser";
import { afterEach, beforeAll, beforeEach, describe, expect, test, vi } from "vitest";
@ -183,7 +182,7 @@ describe("Abilities - Protean", () => {
"ability applies correctly even if the pokemon's move misses",
async () => {
game.override.moveset([Moves.TACKLE]);
game.override.enemyMoveset(SPLASH_ONLY);
game.override.enemyMoveset(Moves.SPLASH);
await game.startBattle([Species.MAGIKARP]);

View File

@ -32,7 +32,7 @@ describe("Abilities - Quick Draw", () => {
game.override.enemyLevel(100);
game.override.enemySpecies(Species.MAGIKARP);
game.override.enemyAbility(Abilities.BALL_FETCH);
game.override.enemyMoveset(Array(4).fill(Moves.TACKLE));
game.override.enemyMoveset([Moves.TACKLE]);
vi.spyOn(allAbilities[Abilities.QUICK_DRAW].getAttrs(BypassSpeedChanceAbAttr)[0], "chance", "get").mockReturnValue(100);
});
@ -76,7 +76,7 @@ describe("Abilities - Quick Draw", () => {
);
test("does not increase priority", async () => {
game.override.enemyMoveset(Array(4).fill(Moves.EXTREME_SPEED));
game.override.enemyMoveset([Moves.EXTREME_SPEED]);
await game.startBattle();

Some files were not shown because too many files have changed in this diff Show More