Add more move implementations

pull/1/head
Flashfyre 2023-04-21 19:30:04 -04:00
parent 355bfc76bf
commit 77999671b2
9 changed files with 568 additions and 220 deletions

View File

@ -9,11 +9,13 @@ import { CommonAnimPhase, MessagePhase } from "./battle-phases";
import { CommonAnim } from "./data/battle-anims"; import { CommonAnim } from "./data/battle-anims";
import { Type } from "./data/type"; import { Type } from "./data/type";
import Move from "./data/move"; import Move from "./data/move";
import { ArenaTag, ArenaTagType, getArenaTag } from "./data/arena-tag";
export class Arena { export class Arena {
private scene: BattleScene; public scene: BattleScene;
public biomeType: Biome; public biomeType: Biome;
public weather: Weather; public weather: Weather;
public tags: ArenaTag[];
private bgm: string; private bgm: string;
private pokemonPool: BiomeTierPools; private pokemonPool: BiomeTierPools;
@ -21,6 +23,7 @@ export class Arena {
constructor(scene: BattleScene, biome: Biome, bgm: string) { constructor(scene: BattleScene, biome: Biome, bgm: string) {
this.scene = scene; this.scene = scene;
this.biomeType = biome; this.biomeType = biome;
this.tags = [];
this.bgm = bgm; this.bgm = bgm;
this.pokemonPool = biomePools[biome]; this.pokemonPool = biomePools[biome];
} }
@ -122,6 +125,52 @@ export class Arena {
return 0; return 0;
} }
getTypeForBiome() {
switch (this.biomeType) {
case Biome.PLAINS:
return Type.NORMAL;
case Biome.GRASS:
case Biome.TALL_GRASS:
case Biome.FOREST:
return Type.GRASS;
case Biome.CITY:
case Biome.SWAMP:
return Type.POISON;
case Biome.SEA:
case Biome.BEACH:
case Biome.LAKE:
case Biome.SEABED:
return Type.WATER;
case Biome.MOUNTAIN:
return Type.FLYING;
case Biome.LAND:
return Type.GROUND;
case Biome.CAVE:
case Biome.DESERT:
return Type.ROCK;
case Biome.ICE_CAVE:
return Type.ICE;
case Biome.MEADOW:
return Type.FAIRY;
case Biome.POWER_PLANT:
return Type.ELECTRIC;
case Biome.VOLCANO:
return Type.FIRE;
case Biome.GRAVEYARD:
return Type.GHOST;
case Biome.DOJO:
return Type.FIGHTING;
case Biome.RUINS:
return Type.PSYCHIC;
case Biome.WASTELAND:
return Type.DRAGON;
case Biome.ABYSS:
return Type.DARK;
case Biome.SPACE:
return Type.STEEL;
}
}
trySetWeather(weather: WeatherType, viaMove: boolean): boolean { trySetWeather(weather: WeatherType, viaMove: boolean): boolean {
if (this.weather?.weatherType === (weather || undefined)) if (this.weather?.weatherType === (weather || undefined))
return false; return false;
@ -131,23 +180,23 @@ export class Arena {
if (this.weather) { if (this.weather) {
this.scene.unshiftPhase(new CommonAnimPhase(this.scene, true, CommonAnim.SUNNY + (weather - 1))); this.scene.unshiftPhase(new CommonAnimPhase(this.scene, true, CommonAnim.SUNNY + (weather - 1)));
this.scene.unshiftPhase(new MessagePhase(this.scene, getWeatherStartMessage(weather))); this.scene.queueMessage(getWeatherStartMessage(weather));
} else } else
this.scene.unshiftPhase(new MessagePhase(this.scene, getWeatherClearMessage(oldWeatherType))); this.scene.queueMessage(getWeatherClearMessage(oldWeatherType));
return true; return true;
} }
isMoveWeatherCancelled(move: Move) {
return this.weather && this.weather.isMoveWeatherCancelled(move);
}
getAttackTypeMultiplier(attackType: Type): number { getAttackTypeMultiplier(attackType: Type): number {
if (!this.weather) if (!this.weather)
return 1; return 1;
return this.weather.getAttackTypeMultiplier(attackType); return this.weather.getAttackTypeMultiplier(attackType);
} }
isMoveWeatherCancelled(move: Move) {
return this.weather && this.weather.isMoveWeatherCancelled(move);
}
isDaytime(): boolean { isDaytime(): boolean {
switch (this.biomeType) { switch (this.biomeType) {
case Biome.PLAINS: case Biome.PLAINS:
@ -164,6 +213,41 @@ export class Arena {
} }
} }
applyTags(tagType: ArenaTagType | { new(...args: any[]): ArenaTag }, ...args: any[]): void {
const tags = typeof tagType === 'number'
? this.tags.filter(t => t.tagType === tagType)
: this.tags.filter(t => t instanceof tagType);
tags.forEach(t => t.apply(args));
}
addTag(tagType: ArenaTagType, turnCount: integer): boolean {
const existingTag = this.getTag(tagType);
if (existingTag) {
existingTag.onOverlap(this);
return false;
}
const newTag = getArenaTag(tagType, turnCount || 0);
this.tags.push(newTag);
newTag.onAdd(this);
return true;
}
getTag(tagType: ArenaTagType | { new(...args: any[]): ArenaTag }): ArenaTag {
return typeof(tagType) === 'number'
? this.tags.find(t => t.tagType === tagType)
: this.tags.find(t => t instanceof tagType);
}
lapseTags(): void {
const tags = this.tags;
tags.filter(t => !(t.lapse(this))).forEach(t => {
t.onRemove(this);
tags.splice(tags.indexOf(t), 1);
});
}
preloadBgm(): void { preloadBgm(): void {
this.scene.loadBgm(this.bgm); this.scene.loadBgm(this.bgm);
} }

View File

@ -18,7 +18,7 @@ import { BattleStat, getBattleStatLevelChangeDescription, getBattleStatName } fr
import { Biome, biomeLinks } from "./data/biome"; import { Biome, biomeLinks } from "./data/biome";
import { ModifierTypeOption, PokemonModifierType, PokemonMoveModifierType, getPlayerModifierTypeOptionsForWave, regenerateModifierPoolThresholds } from "./modifier/modifier-type"; import { ModifierTypeOption, PokemonModifierType, PokemonMoveModifierType, getPlayerModifierTypeOptionsForWave, regenerateModifierPoolThresholds } from "./modifier/modifier-type";
import SoundFade from "phaser3-rex-plugins/plugins/soundfade"; import SoundFade from "phaser3-rex-plugins/plugins/soundfade";
import { BattleTagLapseType, BattleTagType, HideSpriteTag as HiddenTag } from "./data/battle-tag"; import { BattlerTagLapseType, BattlerTagType, HideSpriteTag as HiddenTag } from "./data/battler-tag";
import { getPokemonMessage } from "./messages"; import { getPokemonMessage } from "./messages";
import { Starter } from "./ui/starter-select-ui-handler"; import { Starter } from "./ui/starter-select-ui-handler";
import { Gender } from "./data/gender"; import { Gender } from "./data/gender";
@ -416,7 +416,7 @@ export class CheckSwitchPhase extends BattlePhase {
return; return;
} }
if (this.scene.getPlayerPokemon().getTag(BattleTagType.FRENZY)) { if (this.scene.getPlayerPokemon().getTag(BattlerTagType.FRENZY)) {
super.end(); super.end();
return; return;
} }
@ -596,6 +596,8 @@ export class TurnEndPhase extends BattlePhase {
start() { start() {
super.start(); super.start();
this.scene.currentBattle.incrementTurn();
const playerPokemon = this.scene.getPlayerPokemon(); const playerPokemon = this.scene.getPlayerPokemon();
const enemyPokemon = this.scene.getEnemyPokemon(); const enemyPokemon = this.scene.getEnemyPokemon();
@ -603,7 +605,7 @@ export class TurnEndPhase extends BattlePhase {
if (!pokemon || !pokemon.hp) if (!pokemon || !pokemon.hp)
return; return;
pokemon.lapseTags(BattleTagLapseType.TURN_END); pokemon.lapseTags(BattlerTagLapseType.TURN_END);
const disabledMoves = pokemon.moveset.filter(m => m.isDisabled()); const disabledMoves = pokemon.moveset.filter(m => m.isDisabled());
for (let dm of disabledMoves) { for (let dm of disabledMoves) {
@ -630,6 +632,8 @@ export class TurnEndPhase extends BattlePhase {
handlePokemon(enemyPokemon); handlePokemon(enemyPokemon);
if (isDelayed) if (isDelayed)
handlePokemon(playerPokemon); handlePokemon(playerPokemon);
this.scene.arena.lapseTags();
if (this.scene.arena.weather && !this.scene.arena.weather.lapse()) if (this.scene.arena.weather && !this.scene.arena.weather.lapse())
this.scene.arena.trySetWeather(WeatherType.NONE, false); this.scene.arena.trySetWeather(WeatherType.NONE, false);
@ -735,7 +739,7 @@ export abstract class MovePhase extends BattlePhase {
const target = this.pokemon.isPlayer() ? this.scene.getEnemyPokemon() : this.scene.getPlayerPokemon(); const target = this.pokemon.isPlayer() ? this.scene.getEnemyPokemon() : this.scene.getPlayerPokemon();
if (!this.followUp && this.canMove()) if (!this.followUp && this.canMove())
this.pokemon.lapseTags(BattleTagLapseType.MOVE); this.pokemon.lapseTags(BattlerTagLapseType.MOVE);
const doMove = () => { const doMove = () => {
const moveQueue = this.pokemon.getMoveQueue(); const moveQueue = this.pokemon.getMoveQueue();
@ -751,7 +755,7 @@ export abstract class MovePhase extends BattlePhase {
return; return;
} }
this.scene.unshiftPhase(new MessagePhase(this.scene, getPokemonMessage(this.pokemon, ` used\n${this.move.getName()}!`), 500)); this.scene.queueMessage(getPokemonMessage(this.pokemon, ` used\n${this.move.getName()}!`), 500);
if (!moveQueue.length || !moveQueue.shift().ignorePP) if (!moveQueue.length || !moveQueue.shift().ignorePP)
this.move.ppUsed++; this.move.ppUsed++;
@ -761,7 +765,7 @@ export abstract class MovePhase extends BattlePhase {
failed.value = true; failed.value = true;
if (failed.value) { if (failed.value) {
this.pokemon.getMoveHistory().push({ move: this.move.moveId, result: MoveResult.FAILED, virtual: this.move.virtual }); this.pokemon.getMoveHistory().push({ move: this.move.moveId, result: MoveResult.FAILED, virtual: this.move.virtual });
this.scene.unshiftPhase(new MessagePhase(this.scene, 'But it failed!')); this.scene.queueMessage('But it failed!');
} else } else
this.scene.unshiftPhase(this.getEffectPhase()); this.scene.unshiftPhase(this.getEffectPhase());
@ -770,7 +774,7 @@ export abstract class MovePhase extends BattlePhase {
if (!this.canMove()) { if (!this.canMove()) {
if (this.move.isDisabled()) if (this.move.isDisabled())
this.scene.unshiftPhase(new MessagePhase(this.scene, `${this.move.getName()} is disabled!`)); this.scene.queueMessage(`${this.move.getName()} is disabled!`);
this.end(); this.end();
return; return;
} }
@ -790,7 +794,7 @@ export abstract class MovePhase extends BattlePhase {
case StatusEffect.SLEEP: case StatusEffect.SLEEP:
applyMoveAttrs(BypassSleepAttr, this.pokemon, target, this.move.getMove()); applyMoveAttrs(BypassSleepAttr, this.pokemon, target, this.move.getMove());
healed = this.pokemon.status.turnCount === this.pokemon.status.cureTurn; healed = this.pokemon.status.turnCount === this.pokemon.status.cureTurn;
activated = !healed && !this.pokemon.getTag(BattleTagType.BYPASS_SLEEP); activated = !healed && !this.pokemon.getTag(BattlerTagType.BYPASS_SLEEP);
this.cancelled = activated; this.cancelled = activated;
break; break;
case StatusEffect.FREEZE: case StatusEffect.FREEZE:
@ -800,14 +804,12 @@ export abstract class MovePhase extends BattlePhase {
break; break;
} }
if (activated) { if (activated) {
this.scene.unshiftPhase(new MessagePhase(this.scene, this.scene.queueMessage(getPokemonMessage(this.pokemon, getStatusEffectActivationText(this.pokemon.status.effect)));
getPokemonMessage(this.pokemon, getStatusEffectActivationText(this.pokemon.status.effect))));
this.scene.unshiftPhase(new CommonAnimPhase(this.scene, this.pokemon.isPlayer(), CommonAnim.POISON + (this.pokemon.status.effect - 1))); this.scene.unshiftPhase(new CommonAnimPhase(this.scene, this.pokemon.isPlayer(), CommonAnim.POISON + (this.pokemon.status.effect - 1)));
doMove(); doMove();
} else { } else {
if (healed) { if (healed) {
this.scene.unshiftPhase(new MessagePhase(this.scene, this.scene.queueMessage(getPokemonMessage(this.pokemon, getStatusEffectHealText(this.pokemon.status.effect)));
getPokemonMessage(this.pokemon, getStatusEffectHealText(this.pokemon.status.effect))));
this.pokemon.resetStatus(); this.pokemon.resetStatus();
this.pokemon.updateInfo(); this.pokemon.updateInfo();
} }
@ -869,7 +871,7 @@ abstract class MoveEffectPhase extends PokemonPhase {
return; return;
} }
user.lapseTags(BattleTagLapseType.MOVE_EFFECT); user.lapseTags(BattlerTagLapseType.MOVE_EFFECT);
if (user.turnData.hitsLeft === undefined) { if (user.turnData.hitsLeft === undefined) {
const hitCount = new Utils.IntegerHolder(1); const hitCount = new Utils.IntegerHolder(1);
@ -879,14 +881,14 @@ abstract class MoveEffectPhase extends PokemonPhase {
} }
if (!this.hitCheck()) { if (!this.hitCheck()) {
this.scene.unshiftPhase(new MessagePhase(this.scene, getPokemonMessage(user, '\'s\nattack missed!'))); this.scene.queueMessage(getPokemonMessage(user, '\'s\nattack missed!'));
user.getMoveHistory().push({ move: this.move.moveId, result: MoveResult.MISSED, virtual: this.move.virtual }); user.getMoveHistory().push({ move: this.move.moveId, result: MoveResult.MISSED, virtual: this.move.virtual });
applyMoveAttrs(MissEffectAttr, user, target, this.move.getMove()); applyMoveAttrs(MissEffectAttr, user, target, this.move.getMove());
this.end(); this.end();
return; return;
} }
const isProtected = !this.move.getMove().hasFlag(MoveFlags.IGNORE_PROTECT) && target.lapseTag(BattleTagType.PROTECTED); const isProtected = !this.move.getMove().hasFlag(MoveFlags.IGNORE_PROTECT) && target.lapseTag(BattlerTagType.PROTECTED);
new MoveAnim(this.move.getMove().id as Moves, user, target).play(this.scene, () => { new MoveAnim(this.move.getMove().id as Moves, user, target).play(this.scene, () => {
const result = !isProtected ? target.apply(user, this.move) : MoveResult.NO_EFFECT; const result = !isProtected ? target.apply(user, this.move) : MoveResult.NO_EFFECT;
@ -907,7 +909,7 @@ abstract class MoveEffectPhase extends PokemonPhase {
this.scene.unshiftPhase(this.getNewHitPhase()); this.scene.unshiftPhase(this.getNewHitPhase());
else { else {
if (user.turnData.hitsTotal > 1) if (user.turnData.hitsTotal > 1)
this.scene.unshiftPhase(new MessagePhase(this.scene, `Hit ${user.turnData.hitCount} time(s)!`)); this.scene.queueMessage(`Hit ${user.turnData.hitCount} time(s)!`);
this.scene.applyModifiers(HitHealModifier, this.player, user); this.scene.applyModifiers(HitHealModifier, this.player, user);
} }
@ -1001,7 +1003,7 @@ export class MoveEndPhase extends PokemonPhase {
start() { start() {
super.start(); super.start();
this.getPokemon().lapseTags(BattleTagLapseType.AFTER_MOVE); this.getPokemon().lapseTags(BattlerTagLapseType.AFTER_MOVE);
this.end(); this.end();
} }
@ -1064,7 +1066,7 @@ export class StatChangePhase extends PokemonPhase {
const end = () => { const end = () => {
const messages = this.getStatChangeMessages(relLevels); const messages = this.getStatChangeMessages(relLevels);
for (let message of messages) for (let message of messages)
this.scene.unshiftPhase(new MessagePhase(this.scene, message)); this.scene.queueMessage(message);
for (let stat of this.stats) for (let stat of this.stats)
pokemon.summonData.battleStats[stat] = Math.max(Math.min(pokemon.summonData.battleStats[stat] + this.levels, 6), -6); pokemon.summonData.battleStats[stat] = Math.max(Math.min(pokemon.summonData.battleStats[stat] + this.levels, 6), -6);
@ -1137,7 +1139,7 @@ export class WeatherEffectPhase extends CommonAnimPhase {
start() { start() {
if (this.weather.isDamaging()) { if (this.weather.isDamaging()) {
const inflictDamage = (pokemon: Pokemon) => { const inflictDamage = (pokemon: Pokemon) => {
this.scene.unshiftPhase(new MessagePhase(this.scene, getWeatherDamageMessage(this.weather.weatherType, pokemon))); this.scene.queueMessage(getWeatherDamageMessage(this.weather.weatherType, pokemon));
pokemon.damage(Math.ceil(pokemon.getMaxHp() / 16)); pokemon.damage(Math.ceil(pokemon.getMaxHp() / 16));
this.scene.unshiftPhase(new DamagePhase(this.scene, pokemon.isPlayer())); this.scene.unshiftPhase(new DamagePhase(this.scene, pokemon.isPlayer()));
}; };
@ -1179,7 +1181,7 @@ export class ObtainStatusEffectPhase extends PokemonPhase {
pokemon.status.cureTurn = this.cureTurn; pokemon.status.cureTurn = this.cureTurn;
pokemon.updateInfo(true); pokemon.updateInfo(true);
new CommonBattleAnim(CommonAnim.POISON + (this.statusEffect - 1), pokemon).play(this.scene, () => { new CommonBattleAnim(CommonAnim.POISON + (this.statusEffect - 1), pokemon).play(this.scene, () => {
this.scene.unshiftPhase(new MessagePhase(this.scene, getPokemonMessage(pokemon, getStatusEffectObtainText(this.statusEffect)))); this.scene.queueMessage(getPokemonMessage(pokemon, getStatusEffectObtainText(this.statusEffect)));
if (pokemon.status.isPostTurn()) if (pokemon.status.isPostTurn())
this.scene.pushPhase(new PostTurnStatusEffectPhase(this.scene, this.player)); this.scene.pushPhase(new PostTurnStatusEffectPhase(this.scene, this.player));
this.end(); this.end();
@ -1187,7 +1189,7 @@ export class ObtainStatusEffectPhase extends PokemonPhase {
return; return;
} }
} else if (pokemon.status.effect === this.statusEffect) } else if (pokemon.status.effect === this.statusEffect)
this.scene.unshiftPhase(new MessagePhase(this.scene, getPokemonMessage(pokemon, getStatusEffectOverlapText(this.statusEffect)))); this.scene.queueMessage(getPokemonMessage(pokemon, getStatusEffectOverlapText(this.statusEffect)));
this.end(); this.end();
} }
} }
@ -1202,8 +1204,7 @@ export class PostTurnStatusEffectPhase extends PokemonPhase {
if (pokemon?.hp && pokemon.status && pokemon.status.isPostTurn()) { if (pokemon?.hp && pokemon.status && pokemon.status.isPostTurn()) {
pokemon.status.incrementTurn(); pokemon.status.incrementTurn();
new CommonBattleAnim(CommonAnim.POISON + (pokemon.status.effect - 1), pokemon).play(this.scene, () => { new CommonBattleAnim(CommonAnim.POISON + (pokemon.status.effect - 1), pokemon).play(this.scene, () => {
this.scene.unshiftPhase(new MessagePhase(this.scene, this.scene.queueMessage(getPokemonMessage(pokemon, getStatusEffectActivationText(pokemon.status.effect)));
getPokemonMessage(pokemon, getStatusEffectActivationText(pokemon.status.effect))));
switch (pokemon.status.effect) { switch (pokemon.status.effect) {
case StatusEffect.POISON: case StatusEffect.POISON:
case StatusEffect.BURN: case StatusEffect.BURN:
@ -1288,7 +1289,7 @@ export class FaintPhase extends PokemonPhase {
start() { start() {
super.start(); super.start();
this.scene.unshiftPhase(new MessagePhase(this.scene, getPokemonMessage(this.getPokemon(), ' fainted!'), null, true)); this.scene.queueMessage(getPokemonMessage(this.getPokemon(), ' fainted!'), null, true);
if (this.player) if (this.player)
this.scene.unshiftPhase(new SwitchPhase(this.scene, true, false)); this.scene.unshiftPhase(new SwitchPhase(this.scene, true, false));
@ -1297,7 +1298,7 @@ export class FaintPhase extends PokemonPhase {
const pokemon = this.getPokemon(); const pokemon = this.getPokemon();
pokemon.lapseTags(BattleTagLapseType.FAINT); pokemon.lapseTags(BattlerTagLapseType.FAINT);
pokemon.faintCry(() => { pokemon.faintCry(() => {
pokemon.hideInfo(); pokemon.hideInfo();
@ -1628,7 +1629,7 @@ export class PokemonHealPhase extends CommonAnimPhase {
this.message = getPokemonMessage(pokemon, `'s\nHP is full!`); this.message = getPokemonMessage(pokemon, `'s\nHP is full!`);
if (this.message) if (this.message)
this.scene.unshiftPhase(new MessagePhase(this.scene, this.message)); this.scene.queueMessage(this.message);
if (fullHp) if (fullHp)
super.end(); super.end();
@ -1852,7 +1853,8 @@ export class SelectModifierPhase extends BattlePhase {
} else } else
this.scene.ui.setMode(Mode.MODIFIER_SELECT, typeOptions, modifierSelectCallback); this.scene.ui.setMode(Mode.MODIFIER_SELECT, typeOptions, modifierSelectCallback);
}); });
} } else
this.scene.ui.setMode(Mode.MODIFIER_SELECT, typeOptions, modifierSelectCallback);
}, PartyUiHandler.FilterItemMaxStacks); }, PartyUiHandler.FilterItemMaxStacks);
return; return;
} }

View File

@ -608,6 +608,10 @@ export default class BattleScene extends Phaser.Scene {
this.currentPhase.start(); this.currentPhase.start();
} }
queueMessage(message: string, callbackDelay?: integer, prompt?: boolean) {
this.unshiftPhase(new MessagePhase(this, message, callbackDelay, prompt));
}
populatePhaseQueue(): void { populatePhaseQueue(): void {
this.phaseQueue.push(new CommandPhase(this)); this.phaseQueue.push(new CommandPhase(this));
} }
@ -622,7 +626,7 @@ export default class BattleScene extends Phaser.Scene {
} else if (!virtual) { } else if (!virtual) {
const defaultModifierType = getDefaultModifierTypeForTier(modifier.type.tier); const defaultModifierType = getDefaultModifierTypeForTier(modifier.type.tier);
this.addModifier(defaultModifierType.newModifier(), playSound).then(() => resolve()); this.addModifier(defaultModifierType.newModifier(), playSound).then(() => resolve());
this.unshiftPhase(new MessagePhase(this, `The stack for this item is full.\n You will receive ${defaultModifierType.name} instead.`, null, true)); this.queueMessage(`The stack for this item is full.\n You will receive ${defaultModifierType.name} instead.`, null, true);
return; return;
} }

View File

@ -5,11 +5,13 @@ export class Battle {
public waveIndex: integer; public waveIndex: integer;
public enemyLevel: integer; public enemyLevel: integer;
public enemyPokemon: EnemyPokemon; public enemyPokemon: EnemyPokemon;
public turn: integer;
public playerParticipantIds: Set<integer> = new Set<integer>(); public playerParticipantIds: Set<integer> = new Set<integer>();
constructor(waveIndex: integer) { constructor(waveIndex: integer) {
this.waveIndex = waveIndex; this.waveIndex = waveIndex;
this.enemyLevel = this.getLevelForWave(); this.enemyLevel = this.getLevelForWave();
this.turn = 1;
} }
private getLevelForWave(): number { private getLevelForWave(): number {
@ -23,6 +25,10 @@ export class Battle {
return Math.max(Math.round(baseLevel + Math.abs(Utils.randGauss(deviation))), 1); return Math.max(Math.round(baseLevel + Math.abs(Utils.randGauss(deviation))), 1);
} }
incrementTurn() {
this.turn++;
}
addParticipant(playerPokemon: PlayerPokemon): void { addParticipant(playerPokemon: PlayerPokemon): void {
this.playerParticipantIds.add(playerPokemon.id); this.playerParticipantIds.add(playerPokemon.id);
} }

89
src/data/arena-tag.ts Normal file
View File

@ -0,0 +1,89 @@
import { Arena } from "../arena";
import { Type } from "./type";
import * as Utils from "../utils";
export enum ArenaTagType {
NONE,
MUD_SPORT,
WATER_SPORT
}
export class ArenaTag {
public tagType: ArenaTagType;
public turnCount: integer;
constructor(tagType: ArenaTagType, turnCount: integer) {
this.tagType = tagType;
this.turnCount = turnCount;
}
apply(args: any[]): boolean {
return true;
}
onAdd(arena: Arena): void { }
onRemove(arena: Arena): void { }
onOverlap(arena: Arena): void { }
lapse(arena: Arena): boolean {
return --this.turnCount > 0;
}
}
export class WeakenTypeTag extends ArenaTag {
private weakenedType: Type;
constructor(tagType: ArenaTagType, turnCount: integer, type: Type) {
super(tagType, turnCount);
this.weakenedType = type;
}
apply(args: any[]): boolean {
if ((args[0] as Type) === this.weakenedType) {
(args[1] as Utils.NumberHolder).value *= 0.33;
return true;
}
return false;
}
}
class MudSportTag extends WeakenTypeTag {
constructor(turnCount: integer) {
super(ArenaTagType.MUD_SPORT, turnCount, Type.ELECTRIC);
}
onAdd(arena: Arena): void {
arena.scene.queueMessage('Electricity\'s power was weakened!');
}
onRemove(arena: Arena): void {
arena.scene.queueMessage('MUD SPORT\'s effect wore off.');
}
}
class WaterSportTag extends WeakenTypeTag {
constructor(turnCount: integer) {
super(ArenaTagType.WATER_SPORT, turnCount, Type.FIRE);
}
onAdd(arena: Arena): void {
arena.scene.queueMessage('Fire\'s power was weakened!');
}
onRemove(arena: Arena): void {
arena.scene.queueMessage('WATER SPORT\'s effect wore off.');
}
}
export function getArenaTag(tagType: ArenaTagType, turnCount: integer): ArenaTag {
switch (tagType) {
case ArenaTagType.MUD_SPORT:
return new MudSportTag(turnCount);
case ArenaTagType.WATER_SPORT:
return new WaterSportTag(turnCount);
}
}

View File

@ -7,7 +7,7 @@ import { StatusEffect } from "./status-effect";
import * as Utils from "../utils"; import * as Utils from "../utils";
import { Moves } from "./move"; import { Moves } from "./move";
export enum BattleTagType { export enum BattlerTagType {
NONE, NONE,
RECHARGING, RECHARGING,
FLINCHED, FLINCHED,
@ -26,7 +26,7 @@ export enum BattleTagType {
IGNORE_FLYING IGNORE_FLYING
} }
export enum BattleTagLapseType { export enum BattlerTagLapseType {
FAINT, FAINT,
MOVE, MOVE,
AFTER_MOVE, AFTER_MOVE,
@ -35,12 +35,12 @@ export enum BattleTagLapseType {
CUSTOM CUSTOM
} }
export class BattleTag { export class BattlerTag {
public tagType: BattleTagType; public tagType: BattlerTagType;
public lapseType: BattleTagLapseType; public lapseType: BattlerTagLapseType;
public turnCount: integer; public turnCount: integer;
constructor(tagType: BattleTagType, lapseType: BattleTagLapseType, turnCount: integer) { constructor(tagType: BattlerTagType, lapseType: BattlerTagLapseType, turnCount: integer) {
this.tagType = tagType; this.tagType = tagType;
this.lapseType = lapseType; this.lapseType = lapseType;
this.turnCount = turnCount; this.turnCount = turnCount;
@ -52,14 +52,14 @@ export class BattleTag {
onOverlap(pokemon: Pokemon): void { } onOverlap(pokemon: Pokemon): void { }
lapse(pokemon: Pokemon, lapseType: BattleTagLapseType): boolean { lapse(pokemon: Pokemon, lapseType: BattlerTagLapseType): boolean {
return --this.turnCount > 0; return --this.turnCount > 0;
} }
} }
export class RechargingTag extends BattleTag { export class RechargingTag extends BattlerTag {
constructor() { constructor() {
super(BattleTagType.RECHARGING, BattleTagLapseType.MOVE, 1); super(BattlerTagType.RECHARGING, BattlerTagLapseType.MOVE, 1);
} }
onAdd(pokemon: Pokemon): void { onAdd(pokemon: Pokemon): void {
@ -68,66 +68,66 @@ export class RechargingTag extends BattleTag {
pokemon.getMoveQueue().push({ move: Moves.NONE }) pokemon.getMoveQueue().push({ move: Moves.NONE })
} }
lapse(pokemon: Pokemon, lapseType: BattleTagLapseType): boolean { lapse(pokemon: Pokemon, lapseType: BattlerTagLapseType): boolean {
super.lapse(pokemon, lapseType); super.lapse(pokemon, lapseType);
(pokemon.scene.getCurrentPhase() as MovePhase).cancel(); (pokemon.scene.getCurrentPhase() as MovePhase).cancel();
pokemon.scene.unshiftPhase(new MessagePhase(pokemon.scene, getPokemonMessage(pokemon, ' must\nrecharge!'))); pokemon.scene.queueMessage(getPokemonMessage(pokemon, ' must\nrecharge!'));
return true; return true;
} }
} }
export class FlinchedTag extends BattleTag { export class FlinchedTag extends BattlerTag {
constructor() { constructor() {
super(BattleTagType.FLINCHED, BattleTagLapseType.MOVE, 0); super(BattlerTagType.FLINCHED, BattlerTagLapseType.MOVE, 0);
} }
lapse(pokemon: Pokemon, lapseType: BattleTagLapseType): boolean { lapse(pokemon: Pokemon, lapseType: BattlerTagLapseType): boolean {
super.lapse(pokemon, lapseType); super.lapse(pokemon, lapseType);
(pokemon.scene.getCurrentPhase() as MovePhase).cancel(); (pokemon.scene.getCurrentPhase() as MovePhase).cancel();
pokemon.scene.unshiftPhase(new MessagePhase(pokemon.scene, getPokemonMessage(pokemon, ' flinched!'))); pokemon.scene.queueMessage(getPokemonMessage(pokemon, ' flinched!'));
return true; return true;
} }
} }
export class PseudoStatusTag extends BattleTag { export class PseudoStatusTag extends BattlerTag {
constructor(tagType: BattleTagType, lapseType: BattleTagLapseType, turnCount: integer) { constructor(tagType: BattlerTagType, lapseType: BattlerTagLapseType, turnCount: integer) {
super(tagType, lapseType, turnCount); super(tagType, lapseType, turnCount);
} }
} }
export class ConfusedTag extends PseudoStatusTag { export class ConfusedTag extends PseudoStatusTag {
constructor(turnCount: integer) { constructor(turnCount: integer) {
super(BattleTagType.CONFUSED, BattleTagLapseType.MOVE, turnCount); super(BattlerTagType.CONFUSED, BattlerTagLapseType.MOVE, turnCount);
} }
onAdd(pokemon: Pokemon): void { onAdd(pokemon: Pokemon): void {
super.onAdd(pokemon); super.onAdd(pokemon);
pokemon.scene.unshiftPhase(new CommonAnimPhase(pokemon.scene, pokemon.isPlayer(), CommonAnim.CONFUSION)); pokemon.scene.unshiftPhase(new CommonAnimPhase(pokemon.scene, pokemon.isPlayer(), CommonAnim.CONFUSION));
pokemon.scene.unshiftPhase(new MessagePhase(pokemon.scene, getPokemonMessage(pokemon, ' became\nconfused!'))); pokemon.scene.queueMessage(getPokemonMessage(pokemon, ' became\nconfused!'));
} }
onRemove(pokemon: Pokemon): void { onRemove(pokemon: Pokemon): void {
super.onRemove(pokemon); super.onRemove(pokemon);
pokemon.scene.unshiftPhase(new MessagePhase(pokemon.scene, getPokemonMessage(pokemon, ' snapped\nout of confusion!'))); pokemon.scene.queueMessage(getPokemonMessage(pokemon, ' snapped\nout of confusion!'));
} }
onOverlap(pokemon: Pokemon): void { onOverlap(pokemon: Pokemon): void {
super.onOverlap(pokemon); super.onOverlap(pokemon);
pokemon.scene.unshiftPhase(new MessagePhase(pokemon.scene, getPokemonMessage(pokemon, ' is\nalready confused!'))); pokemon.scene.queueMessage(getPokemonMessage(pokemon, ' is\nalready confused!'));
} }
lapse(pokemon: Pokemon, lapseType: BattleTagLapseType): boolean { lapse(pokemon: Pokemon, lapseType: BattlerTagLapseType): boolean {
const ret = lapseType !== BattleTagLapseType.CUSTOM && super.lapse(pokemon, lapseType); const ret = lapseType !== BattlerTagLapseType.CUSTOM && super.lapse(pokemon, lapseType);
if (ret) { if (ret) {
pokemon.scene.unshiftPhase(new MessagePhase(pokemon.scene, getPokemonMessage(pokemon, ' is\nconfused!'))); pokemon.scene.queueMessage(getPokemonMessage(pokemon, ' is\nconfused!'));
pokemon.scene.unshiftPhase(new CommonAnimPhase(pokemon.scene, pokemon.isPlayer(), CommonAnim.CONFUSION)); pokemon.scene.unshiftPhase(new CommonAnimPhase(pokemon.scene, pokemon.isPlayer(), CommonAnim.CONFUSION));
if (Utils.randInt(2)) { if (Utils.randInt(2)) {
@ -135,7 +135,7 @@ export class ConfusedTag extends PseudoStatusTag {
const def = pokemon.getBattleStat(Stat.DEF); const def = pokemon.getBattleStat(Stat.DEF);
const damage = Math.ceil(((((2 * pokemon.level / 5 + 2) * 40 * atk / def) / 50) + 2) * ((Utils.randInt(15) + 85) / 100)); const damage = Math.ceil(((((2 * pokemon.level / 5 + 2) * 40 * atk / def) / 50) + 2) * ((Utils.randInt(15) + 85) / 100));
pokemon.damage(damage); pokemon.damage(damage);
pokemon.scene.unshiftPhase(new MessagePhase(pokemon.scene, 'It hurt itself in its\nconfusion!')); pokemon.scene.queueMessage('It hurt itself in its\nconfusion!');
pokemon.scene.unshiftPhase(new DamagePhase(pokemon.scene, pokemon.isPlayer())); pokemon.scene.unshiftPhase(new DamagePhase(pokemon.scene, pokemon.isPlayer()));
(pokemon.scene.getCurrentPhase() as MovePhase).cancel(); (pokemon.scene.getCurrentPhase() as MovePhase).cancel();
} }
@ -147,17 +147,17 @@ export class ConfusedTag extends PseudoStatusTag {
export class SeedTag extends PseudoStatusTag { export class SeedTag extends PseudoStatusTag {
constructor() { constructor() {
super(BattleTagType.SEEDED, BattleTagLapseType.AFTER_MOVE, 1); super(BattlerTagType.SEEDED, BattlerTagLapseType.AFTER_MOVE, 1);
} }
onAdd(pokemon: Pokemon): void { onAdd(pokemon: Pokemon): void {
super.onAdd(pokemon); super.onAdd(pokemon);
pokemon.scene.unshiftPhase(new MessagePhase(pokemon.scene, getPokemonMessage(pokemon, ' was seeded!'))); pokemon.scene.queueMessage(getPokemonMessage(pokemon, ' was seeded!'));
} }
lapse(pokemon: Pokemon, lapseType: BattleTagLapseType): boolean { lapse(pokemon: Pokemon, lapseType: BattlerTagLapseType): boolean {
const ret = lapseType !== BattleTagLapseType.CUSTOM || super.lapse(pokemon, lapseType); const ret = lapseType !== BattlerTagLapseType.CUSTOM || super.lapse(pokemon, lapseType);
if (ret) { if (ret) {
pokemon.scene.unshiftPhase(new CommonAnimPhase(pokemon.scene, !pokemon.isPlayer(), CommonAnim.LEECH_SEED)); pokemon.scene.unshiftPhase(new CommonAnimPhase(pokemon.scene, !pokemon.isPlayer(), CommonAnim.LEECH_SEED));
@ -174,26 +174,26 @@ export class SeedTag extends PseudoStatusTag {
export class NightmareTag extends PseudoStatusTag { export class NightmareTag extends PseudoStatusTag {
constructor() { constructor() {
super(BattleTagType.NIGHTMARE, BattleTagLapseType.AFTER_MOVE, 1); super(BattlerTagType.NIGHTMARE, BattlerTagLapseType.AFTER_MOVE, 1);
} }
onAdd(pokemon: Pokemon): void { onAdd(pokemon: Pokemon): void {
super.onAdd(pokemon); super.onAdd(pokemon);
pokemon.scene.unshiftPhase(new MessagePhase(pokemon.scene, getPokemonMessage(pokemon, ' began\nhaving a NIGHTMARE!'))); pokemon.scene.queueMessage(getPokemonMessage(pokemon, ' began\nhaving a NIGHTMARE!'));
} }
onOverlap(pokemon: Pokemon): void { onOverlap(pokemon: Pokemon): void {
super.onOverlap(pokemon); super.onOverlap(pokemon);
pokemon.scene.unshiftPhase(new MessagePhase(pokemon.scene, getPokemonMessage(pokemon, ' is\nalready locked in a NIGHTMARE!'))); pokemon.scene.queueMessage(getPokemonMessage(pokemon, ' is\nalready locked in a NIGHTMARE!'));
} }
lapse(pokemon: Pokemon, lapseType: BattleTagLapseType): boolean { lapse(pokemon: Pokemon, lapseType: BattlerTagLapseType): boolean {
const ret = lapseType !== BattleTagLapseType.CUSTOM || super.lapse(pokemon, lapseType); const ret = lapseType !== BattlerTagLapseType.CUSTOM || super.lapse(pokemon, lapseType);
if (ret) { if (ret) {
pokemon.scene.unshiftPhase(new MessagePhase(pokemon.scene, getPokemonMessage(pokemon, ' is locked\nin a NIGHTMARE!'))); pokemon.scene.queueMessage(getPokemonMessage(pokemon, ' is locked\nin a NIGHTMARE!'));
pokemon.scene.unshiftPhase(new CommonAnimPhase(pokemon.scene, pokemon.isPlayer(), CommonAnim.CURSE)); // TODO: Update animation type pokemon.scene.unshiftPhase(new CommonAnimPhase(pokemon.scene, pokemon.isPlayer(), CommonAnim.CURSE)); // TODO: Update animation type
const damage = Math.ceil(pokemon.getMaxHp() / 4); const damage = Math.ceil(pokemon.getMaxHp() / 4);
@ -207,17 +207,17 @@ export class NightmareTag extends PseudoStatusTag {
export class IngrainTag extends PseudoStatusTag { export class IngrainTag extends PseudoStatusTag {
constructor() { constructor() {
super(BattleTagType.INGRAIN, BattleTagLapseType.TURN_END, 1); super(BattlerTagType.INGRAIN, BattlerTagLapseType.TURN_END, 1);
} }
onAdd(pokemon: Pokemon): void { onAdd(pokemon: Pokemon): void {
super.onAdd(pokemon); super.onAdd(pokemon);
pokemon.scene.unshiftPhase(new MessagePhase(pokemon.scene, getPokemonMessage(pokemon, ' planted its roots!'))); pokemon.scene.queueMessage(getPokemonMessage(pokemon, ' planted its roots!'));
} }
lapse(pokemon: Pokemon, lapseType: BattleTagLapseType): boolean { lapse(pokemon: Pokemon, lapseType: BattlerTagLapseType): boolean {
const ret = lapseType !== BattleTagLapseType.CUSTOM || super.lapse(pokemon, lapseType); const ret = lapseType !== BattlerTagLapseType.CUSTOM || super.lapse(pokemon, lapseType);
if (ret) if (ret)
pokemon.scene.unshiftPhase(new PokemonHealPhase(pokemon.scene, pokemon.isPlayer(), Math.floor(pokemon.getMaxHp() / 16), pokemon.scene.unshiftPhase(new PokemonHealPhase(pokemon.scene, pokemon.isPlayer(), Math.floor(pokemon.getMaxHp() / 16),
@ -229,17 +229,17 @@ export class IngrainTag extends PseudoStatusTag {
export class AquaRingTag extends PseudoStatusTag { export class AquaRingTag extends PseudoStatusTag {
constructor() { constructor() {
super(BattleTagType.AQUA_RING, BattleTagLapseType.TURN_END, 1); super(BattlerTagType.AQUA_RING, BattlerTagLapseType.TURN_END, 1);
} }
onAdd(pokemon: Pokemon): void { onAdd(pokemon: Pokemon): void {
super.onAdd(pokemon); super.onAdd(pokemon);
pokemon.scene.unshiftPhase(new MessagePhase(pokemon.scene, getPokemonMessage(pokemon, ' surrounded\nitself with a veil of water!'))); pokemon.scene.queueMessage(getPokemonMessage(pokemon, ' surrounded\nitself with a veil of water!'));
} }
lapse(pokemon: Pokemon, lapseType: BattleTagLapseType): boolean { lapse(pokemon: Pokemon, lapseType: BattlerTagLapseType): boolean {
const ret = lapseType !== BattleTagLapseType.CUSTOM || super.lapse(pokemon, lapseType); const ret = lapseType !== BattlerTagLapseType.CUSTOM || super.lapse(pokemon, lapseType);
if (ret) if (ret)
pokemon.scene.unshiftPhase(new PokemonHealPhase(pokemon.scene, pokemon.isPlayer(), Math.floor(pokemon.getMaxHp() / 16), `AQUA RING restored\n${pokemon.name}\'s HP!`, true)); pokemon.scene.unshiftPhase(new PokemonHealPhase(pokemon.scene, pokemon.isPlayer(), Math.floor(pokemon.getMaxHp() / 16), `AQUA RING restored\n${pokemon.name}\'s HP!`, true));
@ -248,18 +248,18 @@ export class AquaRingTag extends PseudoStatusTag {
} }
} }
export class DrowsyTag extends BattleTag { export class DrowsyTag extends BattlerTag {
constructor() { constructor() {
super(BattleTagType.DROWSY, BattleTagLapseType.TURN_END, 2); super(BattlerTagType.DROWSY, BattlerTagLapseType.TURN_END, 2);
} }
onAdd(pokemon: Pokemon): void { onAdd(pokemon: Pokemon): void {
super.onAdd(pokemon); super.onAdd(pokemon);
pokemon.scene.unshiftPhase(new MessagePhase(pokemon.scene, getPokemonMessage(pokemon, ' grew drowsy!'))); pokemon.scene.queueMessage(getPokemonMessage(pokemon, ' grew drowsy!'));
} }
lapse(pokemon: Pokemon, lapseType: BattleTagLapseType): boolean { lapse(pokemon: Pokemon, lapseType: BattlerTagLapseType): boolean {
if (!super.lapse(pokemon, lapseType)) { if (!super.lapse(pokemon, lapseType)) {
pokemon.scene.unshiftPhase(new ObtainStatusEffectPhase(pokemon.scene, pokemon.isPlayer(), StatusEffect.SLEEP)); pokemon.scene.unshiftPhase(new ObtainStatusEffectPhase(pokemon.scene, pokemon.isPlayer(), StatusEffect.SLEEP));
return false; return false;
@ -269,21 +269,21 @@ export class DrowsyTag extends BattleTag {
} }
} }
export class ProtectedTag extends BattleTag { export class ProtectedTag extends BattlerTag {
constructor() { constructor() {
super(BattleTagType.PROTECTED, BattleTagLapseType.CUSTOM, 0); super(BattlerTagType.PROTECTED, BattlerTagLapseType.CUSTOM, 0);
} }
onAdd(pokemon: Pokemon): void { onAdd(pokemon: Pokemon): void {
super.onAdd(pokemon); super.onAdd(pokemon);
pokemon.scene.unshiftPhase(new MessagePhase(pokemon.scene, getPokemonMessage(pokemon, '\nprotected itself!'))); pokemon.scene.queueMessage(getPokemonMessage(pokemon, '\nprotected itself!'));
} }
lapse(pokemon: Pokemon, lapseType: BattleTagLapseType): boolean { lapse(pokemon: Pokemon, lapseType: BattlerTagLapseType): boolean {
if (lapseType === BattleTagLapseType.CUSTOM) { if (lapseType === BattlerTagLapseType.CUSTOM) {
new CommonBattleAnim(CommonAnim.PROTECT, pokemon).play(pokemon.scene); new CommonBattleAnim(CommonAnim.PROTECT, pokemon).play(pokemon.scene);
pokemon.scene.unshiftPhase(new MessagePhase(pokemon.scene, getPokemonMessage(pokemon, '\nprotected itself!'))); pokemon.scene.queueMessage(getPokemonMessage(pokemon, '\nprotected itself!'));
return true; return true;
} }
@ -291,9 +291,9 @@ export class ProtectedTag extends BattleTag {
} }
} }
export class HideSpriteTag extends BattleTag { export class HideSpriteTag extends BattlerTag {
constructor(tagType: BattleTagType, turnCount: integer) { constructor(tagType: BattlerTagType, turnCount: integer) {
super(tagType, BattleTagLapseType.MOVE_EFFECT, turnCount); super(tagType, BattlerTagLapseType.MOVE_EFFECT, turnCount);
} }
onAdd(pokemon: Pokemon): void { onAdd(pokemon: Pokemon): void {
@ -312,34 +312,34 @@ export class HideSpriteTag extends BattleTag {
} }
} }
export function getBattleTag(tagType: BattleTagType, turnCount: integer): BattleTag { export function getBattlerTag(tagType: BattlerTagType, turnCount: integer): BattlerTag {
switch (tagType) { switch (tagType) {
case BattleTagType.RECHARGING: case BattlerTagType.RECHARGING:
return new RechargingTag(); return new RechargingTag();
case BattleTagType.FLINCHED: case BattlerTagType.FLINCHED:
return new FlinchedTag(); return new FlinchedTag();
case BattleTagType.CONFUSED: case BattlerTagType.CONFUSED:
return new ConfusedTag(turnCount); return new ConfusedTag(turnCount);
case BattleTagType.SEEDED: case BattlerTagType.SEEDED:
return new SeedTag(); return new SeedTag();
case BattleTagType.NIGHTMARE: case BattlerTagType.NIGHTMARE:
return new NightmareTag(); return new NightmareTag();
case BattleTagType.AQUA_RING: case BattlerTagType.AQUA_RING:
return new AquaRingTag(); return new AquaRingTag();
case BattleTagType.DROWSY: case BattlerTagType.DROWSY:
return new DrowsyTag(); return new DrowsyTag();
case BattleTagType.PROTECTED: case BattlerTagType.PROTECTED:
return new ProtectedTag(); return new ProtectedTag();
case BattleTagType.FLYING: case BattlerTagType.FLYING:
case BattleTagType.UNDERGROUND: case BattlerTagType.UNDERGROUND:
return new HideSpriteTag(tagType, turnCount); return new HideSpriteTag(tagType, turnCount);
case BattleTagType.NO_CRIT: case BattlerTagType.NO_CRIT:
return new BattleTag(tagType, BattleTagLapseType.AFTER_MOVE, turnCount); return new BattlerTag(tagType, BattlerTagLapseType.AFTER_MOVE, turnCount);
case BattleTagType.BYPASS_SLEEP: case BattlerTagType.BYPASS_SLEEP:
return new BattleTag(BattleTagType.BYPASS_SLEEP, BattleTagLapseType.TURN_END, turnCount); return new BattlerTag(BattlerTagType.BYPASS_SLEEP, BattlerTagLapseType.TURN_END, turnCount);
case BattleTagType.IGNORE_FLYING: case BattlerTagType.IGNORE_FLYING:
return new BattleTag(tagType, BattleTagLapseType.TURN_END, turnCount); return new BattlerTag(tagType, BattlerTagLapseType.TURN_END, turnCount);
default: default:
return new BattleTag(tagType, BattleTagLapseType.CUSTOM, turnCount); return new BattlerTag(tagType, BattlerTagLapseType.CUSTOM, turnCount);
} }
} }

View File

@ -1,7 +1,7 @@
import { MessagePhase, PokemonHealPhase } from "../battle-phases"; import { MessagePhase, PokemonHealPhase } from "../battle-phases";
import { getPokemonMessage } from "../messages"; import { getPokemonMessage } from "../messages";
import Pokemon from "../pokemon"; import Pokemon from "../pokemon";
import { BattleTagType } from "./battle-tag"; import { BattlerTagType } from "./battler-tag";
import { getStatusEffectHealText } from "./status-effect"; import { getStatusEffectHealText } from "./status-effect";
export enum BerryType { export enum BerryType {
@ -34,7 +34,7 @@ export function getBerryPredicate(berryType: BerryType): BerryPredicate {
case BerryType.SITRUS: case BerryType.SITRUS:
return (pokemon: Pokemon) => pokemon.getHpRatio() < 0.5; return (pokemon: Pokemon) => pokemon.getHpRatio() < 0.5;
case BerryType.LUM: case BerryType.LUM:
return (pokemon: Pokemon) => !!pokemon.status || !!pokemon.getTag(BattleTagType.CONFUSED); return (pokemon: Pokemon) => !!pokemon.status || !!pokemon.getTag(BattlerTagType.CONFUSED);
} }
} }
@ -49,12 +49,11 @@ export function getBerryEffectFunc(berryType: BerryType): BerryEffectFunc {
case BerryType.LUM: case BerryType.LUM:
return (pokemon: Pokemon) => { return (pokemon: Pokemon) => {
if (pokemon.status) { if (pokemon.status) {
pokemon.scene.unshiftPhase(new MessagePhase(pokemon.scene, pokemon.scene.queueMessage(getPokemonMessage(pokemon, getStatusEffectHealText(pokemon.status.effect)));
getPokemonMessage(pokemon, getStatusEffectHealText(pokemon.status.effect))));
pokemon.resetStatus(); pokemon.resetStatus();
pokemon.updateInfo(); pokemon.updateInfo();
} else if (pokemon.getTag(BattleTagType.CONFUSED)) } else if (pokemon.getTag(BattlerTagType.CONFUSED))
pokemon.lapseTag(BattleTagType.CONFUSED); pokemon.lapseTag(BattlerTagType.CONFUSED);
}; };
} }
} }

View File

@ -1,13 +1,14 @@
import { ChargeAnim, MoveChargeAnim, initMoveAnim, loadMoveAnimAssets } from "./battle-anims"; import { ChargeAnim, MoveChargeAnim, initMoveAnim, loadMoveAnimAssets } from "./battle-anims";
import { DamagePhase, EnemyMovePhase, MessagePhase, ObtainStatusEffectPhase, PlayerMovePhase, PokemonHealPhase, StatChangePhase } from "../battle-phases"; import { DamagePhase, EnemyMovePhase, MessagePhase, ObtainStatusEffectPhase, PlayerMovePhase, PokemonHealPhase, StatChangePhase } from "../battle-phases";
import { BattleStat } from "./battle-stat"; import { BattleStat } from "./battle-stat";
import { BattleTagType } from "./battle-tag"; import { BattlerTagType } from "./battler-tag";
import { getPokemonMessage } from "../messages"; import { getPokemonMessage } from "../messages";
import Pokemon, { EnemyPokemon, MoveResult, PlayerPokemon, PokemonMove, TurnMove } from "../pokemon"; import Pokemon, { EnemyPokemon, MoveResult, PlayerPokemon, PokemonMove, TurnMove } from "../pokemon";
import { StatusEffect, getStatusEffectDescriptor } from "./status-effect"; import { StatusEffect, getStatusEffectDescriptor } from "./status-effect";
import { Type } from "./type"; import { Type } from "./type";
import * as Utils from "../utils"; import * as Utils from "../utils";
import { WeatherType } from "./weather"; import { WeatherType } from "./weather";
import { ArenaTagType } from "./arena-tag";
export enum MoveCategory { export enum MoveCategory {
PHYSICAL, PHYSICAL,
@ -671,8 +672,8 @@ export class MoveEffectAttr extends MoveAttr {
} }
canApply(user: Pokemon, target: Pokemon, move: Move, args: any[]) { canApply(user: Pokemon, target: Pokemon, move: Move, args: any[]) {
return !!(this.selfTarget ? user.hp && !user.getTag(BattleTagType.FRENZY) : target.hp) return !!(this.selfTarget ? user.hp && !user.getTag(BattlerTagType.FRENZY) : target.hp)
&& (this.selfTarget || !target.getTag(BattleTagType.PROTECTED) || move.hasFlag(MoveFlags.IGNORE_PROTECT)); && (this.selfTarget || !target.getTag(BattlerTagType.PROTECTED) || move.hasFlag(MoveFlags.IGNORE_PROTECT));
} }
apply(user: Pokemon, target: Pokemon, move: Move, args: any[]) { apply(user: Pokemon, target: Pokemon, move: Move, args: any[]) {
@ -735,7 +736,7 @@ export class RecoilAttr extends MoveEffectAttr {
const recoilDamage = Math.max(Math.floor(user.turnData.damageDealt / 4), 1); const recoilDamage = Math.max(Math.floor(user.turnData.damageDealt / 4), 1);
user.damage(recoilDamage); user.damage(recoilDamage);
user.scene.unshiftPhase(new DamagePhase(user.scene, user.isPlayer(), MoveResult.OTHER)); user.scene.unshiftPhase(new DamagePhase(user.scene, user.isPlayer(), MoveResult.OTHER));
user.scene.unshiftPhase(new MessagePhase(user.scene, getPokemonMessage(user, ' is hit\nwith recoil!'))); user.scene.queueMessage(getPokemonMessage(user, ' is hit\nwith recoil!'));
return true; return true;
} }
@ -893,7 +894,7 @@ export class HealStatusEffectAttr extends MoveEffectAttr {
const pokemon = this.selfTarget ? user : target; const pokemon = this.selfTarget ? user : target;
if (pokemon.status && this.effects.indexOf(pokemon.status.effect) > -1) { if (pokemon.status && this.effects.indexOf(pokemon.status.effect) > -1) {
pokemon.scene.unshiftPhase(new MessagePhase(pokemon.scene, getPokemonMessage(pokemon, ` was cured of its\n${getStatusEffectDescriptor(pokemon.status.effect)}!`))); pokemon.scene.queueMessage(getPokemonMessage(pokemon, ` was cured of its\n${getStatusEffectDescriptor(pokemon.status.effect)}!`));
pokemon.resetStatus(); pokemon.resetStatus();
pokemon.updateInfo(); pokemon.updateInfo();
} }
@ -905,7 +906,7 @@ export class HealStatusEffectAttr extends MoveEffectAttr {
export class BypassSleepAttr extends MoveAttr { export class BypassSleepAttr extends MoveAttr {
apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean { apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean {
if (user.status?.effect === StatusEffect.SLEEP) { if (user.status?.effect === StatusEffect.SLEEP) {
user.addTag(BattleTagType.BYPASS_SLEEP, 1); user.addTag(BattlerTagType.BYPASS_SLEEP, 1);
return true; return true;
} }
@ -947,7 +948,7 @@ export class ClearWeatherAttr extends MoveEffectAttr {
export class OneHitKOAttr extends MoveHitEffectAttr { export class OneHitKOAttr extends MoveHitEffectAttr {
apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean { apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean {
target.hp = 0; target.hp = 0;
user.scene.unshiftPhase(new MessagePhase(user.scene, 'It\'s a one-hit KO!')); user.scene.queueMessage('It\'s a one-hit KO!');
return true; return true;
} }
} }
@ -957,10 +958,10 @@ export class OverrideMoveEffectAttr extends MoveAttr { }
export class ChargeAttr extends OverrideMoveEffectAttr { export class ChargeAttr extends OverrideMoveEffectAttr {
public chargeAnim: ChargeAnim; public chargeAnim: ChargeAnim;
private chargeText: string; private chargeText: string;
private tagType: BattleTagType; private tagType: BattlerTagType;
public chargeEffect: boolean; public chargeEffect: boolean;
constructor(chargeAnim: ChargeAnim, chargeText: string, tagType?: BattleTagType, chargeEffect?: boolean) { constructor(chargeAnim: ChargeAnim, chargeText: string, tagType?: BattlerTagType, chargeEffect?: boolean) {
super(); super();
this.chargeAnim = chargeAnim; this.chargeAnim = chargeAnim;
@ -975,7 +976,7 @@ export class ChargeAttr extends OverrideMoveEffectAttr {
if (!lastMove.length || lastMove[0].move !== move.id || lastMove[0].result !== MoveResult.OTHER) { if (!lastMove.length || lastMove[0].move !== move.id || lastMove[0].result !== MoveResult.OTHER) {
(args[0] as Utils.BooleanHolder).value = true; (args[0] as Utils.BooleanHolder).value = true;
new MoveChargeAnim(this.chargeAnim, move.id, user, target).play(user.scene, () => { new MoveChargeAnim(this.chargeAnim, move.id, user, target).play(user.scene, () => {
user.scene.unshiftPhase(new MessagePhase(user.scene, getPokemonMessage(user, ` ${this.chargeText.replace('{TARGET}', target.name)}`))); user.scene.queueMessage(getPokemonMessage(user, ` ${this.chargeText.replace('{TARGET}', target.name)}`));
if (this.tagType) if (this.tagType)
user.addTag(this.tagType, 1); user.addTag(this.tagType, 1);
if (this.chargeEffect) if (this.chargeEffect)
@ -1092,6 +1093,14 @@ export class WeightPowerAttr extends VariablePowerAttr {
} }
} }
export class HpPowerAttr extends VariablePowerAttr {
apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean {
(args[0] as Utils.NumberHolder).value = Math.max(Math.floor(150 * user.getHpRatio()), 1);
return true;
}
}
export class SolarBeamPowerAttr extends VariablePowerAttr { export class SolarBeamPowerAttr extends VariablePowerAttr {
apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean { apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean {
const power = args[0] as Utils.NumberHolder; const power = args[0] as Utils.NumberHolder;
@ -1227,7 +1236,7 @@ export class DisableMoveAttr extends MoveEffectAttr {
const disabledMove = target.moveset[moveIndex]; const disabledMove = target.moveset[moveIndex];
disabledMove.disableTurns = 4; disabledMove.disableTurns = 4;
user.scene.unshiftPhase(new MessagePhase(user.scene, getPokemonMessage(target, `'s ${disabledMove.getName()}\nwas disabled!`))) user.scene.queueMessage(getPokemonMessage(target, `'s ${disabledMove.getName()}\nwas disabled!`));
return true; return true;
} }
@ -1270,13 +1279,13 @@ export class FrenzyAttr extends MoveEffectAttr {
return false; return false;
if (!user.getMoveQueue().length) { if (!user.getMoveQueue().length) {
if (!user.getTag(BattleTagType.FRENZY)) { if (!user.getTag(BattlerTagType.FRENZY)) {
const turnCount = Utils.randInt(2) + 1; const turnCount = Utils.randInt(2) + 1;
new Array(turnCount).fill(null).map(() => user.getMoveQueue().push({ move: move.id, ignorePP: true })); new Array(turnCount).fill(null).map(() => user.getMoveQueue().push({ move: move.id, ignorePP: true }));
user.addTag(BattleTagType.FRENZY, 1); user.addTag(BattlerTagType.FRENZY, 1);
} else { } else {
applyMoveAttrs(AddTagAttr, user, target, move, args); applyMoveAttrs(AddBattlerTagAttr, user, target, move, args);
user.lapseTag(BattleTagType.FRENZY); user.lapseTag(BattlerTagType.FRENZY);
} }
return true; return true;
} }
@ -1288,14 +1297,14 @@ export class FrenzyAttr extends MoveEffectAttr {
export const frenzyMissFunc = (user: Pokemon, target: Pokemon, move: Move) => { export const frenzyMissFunc = (user: Pokemon, target: Pokemon, move: Move) => {
while (user.getMoveQueue().length && user.getMoveQueue()[0].move === move.id) while (user.getMoveQueue().length && user.getMoveQueue()[0].move === move.id)
user.getMoveQueue().shift(); user.getMoveQueue().shift();
user.lapseTag(BattleTagType.FRENZY) user.lapseTag(BattlerTagType.FRENZY)
}; };
export class AddTagAttr extends MoveEffectAttr { export class AddBattlerTagAttr extends MoveEffectAttr {
public tagType: BattleTagType; public tagType: BattlerTagType;
public turnCount: integer; public turnCount: integer;
constructor(tagType: BattleTagType, selfTarget?: boolean, turnCount?: integer) { constructor(tagType: BattlerTagType, selfTarget?: boolean, turnCount?: integer) {
super(selfTarget); super(selfTarget);
this.tagType = tagType; this.tagType = tagType;
@ -1315,29 +1324,29 @@ export class AddTagAttr extends MoveEffectAttr {
} }
} }
export class FlinchAttr extends AddTagAttr { export class FlinchAttr extends AddBattlerTagAttr {
constructor() { constructor() {
super(BattleTagType.FLINCHED, false); super(BattlerTagType.FLINCHED, false);
} }
} }
export class ConfuseAttr extends AddTagAttr { export class ConfuseAttr extends AddBattlerTagAttr {
constructor(selfTarget?: boolean) { constructor(selfTarget?: boolean) {
super(BattleTagType.CONFUSED, selfTarget, Utils.randInt(4, 1)); super(BattlerTagType.CONFUSED, selfTarget, Utils.randInt(4, 1));
} }
} }
export class NoTagOverlapConditionalAttr extends ConditionalMoveAttr { export class NoTagOverlapConditionalAttr extends ConditionalMoveAttr {
constructor(tagType: BattleTagType, selfTarget?: boolean) { constructor(tagType: BattlerTagType, selfTarget?: boolean) {
super((user: Pokemon, target: Pokemon, move: Move) => !(selfTarget ? user : target).getTag(tagType)); super((user: Pokemon, target: Pokemon, move: Move) => !(selfTarget ? user : target).getTag(tagType));
} }
} }
export class HitsTagAttr extends MoveAttr { export class HitsTagAttr extends MoveAttr {
public tagType: BattleTagType; public tagType: BattlerTagType;
public doubleDamage: boolean; public doubleDamage: boolean;
constructor(tagType: BattleTagType, doubleDamage?: boolean) { constructor(tagType: BattlerTagType, doubleDamage?: boolean) {
super(); super();
this.tagType = tagType; this.tagType = tagType;
@ -1345,17 +1354,64 @@ export class HitsTagAttr extends MoveAttr {
} }
} }
export function applyMoveAttrs(attrType: { new(...args: any[]): MoveAttr }, user: Pokemon, target: Pokemon, move: Move, ...args: any[]): Promise<void> { export class AddArenaTagAttr extends MoveEffectAttr {
return new Promise(resolve => { public tagType: ArenaTagType;
const attrPromises: Promise<boolean>[] = []; public turnCount: integer;
const moveAttrs = move.attrs.filter(a => a instanceof attrType);
for (let attr of moveAttrs) { constructor(tagType: ArenaTagType, turnCount?: integer) {
const result = attr.apply(user, target, move, args); super(true);
if (result instanceof Promise<boolean>)
attrPromises.push(result); this.tagType = tagType;
this.turnCount = turnCount;
}
apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean {
if (!super.apply(user, target, move, args))
return false;
if (move.chance < 0 || move.chance === 100 || Utils.randInt(100) < move.chance) {
user.scene.arena.addTag(this.tagType, this.turnCount);
return true;
} }
Promise.allSettled(attrPromises).then(() => resolve());
}); return false;
}
}
export class CopyTypeAttr extends MoveEffectAttr {
constructor() {
super(true);
}
apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean {
if (!super.apply(user, target, move, args))
return false;
user.summonData.types = target.getTypes();
user.scene.queueMessage(getPokemonMessage(user, `'s type\nchanged to match ${target.name}'s!`));
return true;
}
}
export class CopyBiomeTypeAttr extends MoveEffectAttr {
constructor() {
super(true);
}
apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean {
if (!super.apply(user, target, move, args))
return false;
const biomeType = user.scene.arena.getTypeForBiome();
user.summonData.types = [ biomeType ];
user.scene.queueMessage(getPokemonMessage(user, ` transformed\ninto the ${Type[biomeType].toUpperCase()} type!`));
return true;
}
} }
export class RandomMovesetMoveAttr extends OverrideMoveEffectAttr { export class RandomMovesetMoveAttr extends OverrideMoveEffectAttr {
@ -1379,7 +1435,9 @@ export class RandomMoveAttr extends OverrideMoveEffectAttr {
const moveIds = Utils.getEnumValues(Moves).filter(m => m && m !== move.id); const moveIds = Utils.getEnumValues(Moves).filter(m => m && m !== move.id);
const moveId = moveIds[Utils.randInt(moveIds.length)]; const moveId = moveIds[Utils.randInt(moveIds.length)];
user.getMoveQueue().push({ move: moveId, ignorePP: true }); user.getMoveQueue().push({ move: moveId, ignorePP: true });
user.scene.unshiftPhase(user.isPlayer() ? new PlayerMovePhase(user.scene, user as PlayerPokemon, new PokemonMove(moveId, 0, 0, true), true) : new EnemyMovePhase(user.scene, user as EnemyPokemon, new PokemonMove(moveId, 0, 0, true), true)); user.scene.unshiftPhase(user.isPlayer()
? new PlayerMovePhase(user.scene, user as PlayerPokemon, new PokemonMove(moveId, 0, 0, true), true)
: new EnemyMovePhase(user.scene, user as EnemyPokemon, new PokemonMove(moveId, 0, 0, true), true));
initMoveAnim(moveId).then(() => { initMoveAnim(moveId).then(() => {
loadMoveAnimAssets(user.scene, [ moveId ], true) loadMoveAnimAssets(user.scene, [ moveId ], true)
.then(() => resolve(true)); .then(() => resolve(true));
@ -1388,6 +1446,98 @@ export class RandomMoveAttr extends OverrideMoveEffectAttr {
} }
} }
const targetMoveCopiableCondition = (user: Pokemon, target: Pokemon, move: Move) => {
const targetMoves = target.getMoveHistory().filter(m => !m.virtual);
if (!targetMoves.length)
return false;
const copiableMove = targetMoves[0];
if (!copiableMove.move)
return false;
if (allMoves[copiableMove.move].getAttrs(ChargeAttr) && copiableMove.result === MoveResult.OTHER)
return false;
// TODO: Add last turn of Bide
return true;
};
export class CopyMoveAttr extends OverrideMoveEffectAttr {
apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean {
const targetMoves = target.getMoveHistory().filter(m => !m.virtual);
if (!targetMoves.length)
return false;
const copiedMove = targetMoves[0];
user.getMoveQueue().push({ move: copiedMove.move, ignorePP: true });
user.scene.unshiftPhase(user.isPlayer()
? new PlayerMovePhase(user.scene, user as PlayerPokemon, new PokemonMove(copiedMove.move, 0, 0, true), true)
: new EnemyMovePhase(user.scene, user as EnemyPokemon, new PokemonMove(copiedMove.move, 0, 0, true), true));
return true;
}
}
const targetMoveSketchableCondition = (user: Pokemon, target: Pokemon, move: Move) => {
if (!targetMoveCopiableCondition(user, target, move))
return false;
const targetMoves = target.getMoveHistory().filter(m => !m.virtual);
if (!targetMoves.length)
return false;
const sketchableMove = targetMoves[0];
if (user.moveset.find(m => m.moveId === sketchableMove.move))
return false;
return true;
};
export class SketchAttr extends MoveEffectAttr {
constructor() {
super(true);
}
apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean {
if (!super.apply(user, target, move, args))
return false;
const targetMoves = target.getMoveHistory().filter(m => !m.virtual);
if (!targetMoves.length)
return false;
const sketchedMove = allMoves[targetMoves[0].move];
const sketchIndex = user.moveset.findIndex(m => m.moveId === move.id);
if (sketchIndex === -1)
return false;
user.moveset[sketchIndex] = new PokemonMove(sketchedMove.id, 0, 0);
user.scene.queueMessage(getPokemonMessage(user, ` sketched\n${sketchedMove.name}!`));
return true;
}
}
export function applyMoveAttrs(attrType: { new(...args: any[]): MoveAttr }, user: Pokemon, target: Pokemon, move: Move, ...args: any[]): Promise<void> {
return new Promise(resolve => {
const attrPromises: Promise<boolean>[] = [];
const moveAttrs = move.attrs.filter(a => a instanceof attrType);
for (let attr of moveAttrs) {
const result = attr.apply(user, target, move, args);
if (result instanceof Promise<boolean>)
attrPromises.push(result);
}
Promise.allSettled(attrPromises).then(() => resolve());
});
}
export const allMoves = [ export const allMoves = [
new StatusMove(Moves.NONE, "-", Type.NORMAL, MoveCategory.STATUS, -1, -1, "", -1, 0, 1), new StatusMove(Moves.NONE, "-", Type.NORMAL, MoveCategory.STATUS, -1, -1, "", -1, 0, 1),
new AttackMove(Moves.POUND, "Pound", Type.NORMAL, MoveCategory.PHYSICAL, 40, 100, 35, -1, "", -1, 0, 1), new AttackMove(Moves.POUND, "Pound", Type.NORMAL, MoveCategory.PHYSICAL, 40, 100, 35, -1, "", -1, 0, 1),
@ -1407,11 +1557,11 @@ export const allMoves = [
new SelfStatusMove(Moves.SWORDS_DANCE, "Swords Dance", Type.NORMAL, -1, 20, 88, "Sharply raises user's Attack.", -1, 0, 1, new StatChangeAttr(BattleStat.ATK, 2, true)), new SelfStatusMove(Moves.SWORDS_DANCE, "Swords Dance", Type.NORMAL, -1, 20, 88, "Sharply raises user's Attack.", -1, 0, 1, new StatChangeAttr(BattleStat.ATK, 2, true)),
new AttackMove(Moves.CUT, "Cut", Type.NORMAL, MoveCategory.PHYSICAL, 50, 95, 30, -1, "", -1, 0, 1), new AttackMove(Moves.CUT, "Cut", Type.NORMAL, MoveCategory.PHYSICAL, 50, 95, 30, -1, "", -1, 0, 1),
new AttackMove(Moves.GUST, "Gust", Type.FLYING, MoveCategory.SPECIAL, 40, 100, 35, -1, "Hits Pokémon using Fly/Bounce/Sky Drop with double power.", -1, 0, 1, new AttackMove(Moves.GUST, "Gust", Type.FLYING, MoveCategory.SPECIAL, 40, 100, 35, -1, "Hits Pokémon using Fly/Bounce/Sky Drop with double power.", -1, 0, 1,
new HitsTagAttr(BattleTagType.FLYING, true)), new HitsTagAttr(BattlerTagType.FLYING, true)),
new AttackMove(Moves.WING_ATTACK, "Wing Attack", Type.FLYING, MoveCategory.PHYSICAL, 60, 100, 35, -1, "", -1, 0, 1), new AttackMove(Moves.WING_ATTACK, "Wing Attack", Type.FLYING, MoveCategory.PHYSICAL, 60, 100, 35, -1, "", -1, 0, 1),
new StatusMove(Moves.WHIRLWIND, "Whirlwind", Type.NORMAL, -1, 20, -1, "In battles, the opponent switches. In the wild, the Pokémon runs.", -1, -6, 1), // TODO new StatusMove(Moves.WHIRLWIND, "Whirlwind", Type.NORMAL, -1, 20, -1, "In battles, the opponent switches. In the wild, the Pokémon runs.", -1, -6, 1), // TODO
new AttackMove(Moves.FLY, "Fly", Type.FLYING, MoveCategory.PHYSICAL, 90, 95, 15, 97, "Flies up on first turn, attacks on second turn.", -1, 0, 1, new AttackMove(Moves.FLY, "Fly", Type.FLYING, MoveCategory.PHYSICAL, 90, 95, 15, 97, "Flies up on first turn, attacks on second turn.", -1, 0, 1,
new ChargeAttr(ChargeAnim.FLY_CHARGING, 'flew\nup high!', BattleTagType.FLYING)), new ChargeAttr(ChargeAnim.FLY_CHARGING, 'flew\nup high!', BattlerTagType.FLYING)),
new AttackMove(Moves.BIND, "Bind", Type.NORMAL, MoveCategory.PHYSICAL, 15, 85, 20, -1, "Traps opponent, damaging them for 4-5 turns.", 100, 0, 1), // TODO new AttackMove(Moves.BIND, "Bind", Type.NORMAL, MoveCategory.PHYSICAL, 15, 85, 20, -1, "Traps opponent, damaging them for 4-5 turns.", 100, 0, 1), // TODO
new AttackMove(Moves.SLAM, "Slam", Type.NORMAL, MoveCategory.PHYSICAL, 80, 75, 20, -1, "", -1, 0, 1), new AttackMove(Moves.SLAM, "Slam", Type.NORMAL, MoveCategory.PHYSICAL, 80, 75, 20, -1, "", -1, 0, 1),
new AttackMove(Moves.VINE_WHIP, "Vine Whip", Type.GRASS, MoveCategory.PHYSICAL, 45, 100, 25, -1, "", -1, 0, 1), new AttackMove(Moves.VINE_WHIP, "Vine Whip", Type.GRASS, MoveCategory.PHYSICAL, 45, 100, 25, -1, "", -1, 0, 1),
@ -1456,7 +1606,7 @@ export const allMoves = [
new AttackMove(Moves.PSYBEAM, "Psybeam", Type.PSYCHIC, MoveCategory.SPECIAL, 65, 100, 20, 16, "May confuse opponent.", 10, 0, 1, new ConfuseAttr()), new AttackMove(Moves.PSYBEAM, "Psybeam", Type.PSYCHIC, MoveCategory.SPECIAL, 65, 100, 20, 16, "May confuse opponent.", 10, 0, 1, new ConfuseAttr()),
new AttackMove(Moves.BUBBLE_BEAM, "Bubble Beam", Type.WATER, MoveCategory.SPECIAL, 65, 100, 20, -1, "May lower opponent's Speed.", 10, 0, 1, new StatChangeAttr(BattleStat.SPD, -1)), new AttackMove(Moves.BUBBLE_BEAM, "Bubble Beam", Type.WATER, MoveCategory.SPECIAL, 65, 100, 20, -1, "May lower opponent's Speed.", 10, 0, 1, new StatChangeAttr(BattleStat.SPD, -1)),
new AttackMove(Moves.AURORA_BEAM, "Aurora Beam", Type.ICE, MoveCategory.SPECIAL, 65, 100, 20, -1, "May lower opponent's Attack.", 10, 0, 1, new StatChangeAttr(BattleStat.ATK, -1)), new AttackMove(Moves.AURORA_BEAM, "Aurora Beam", Type.ICE, MoveCategory.SPECIAL, 65, 100, 20, -1, "May lower opponent's Attack.", 10, 0, 1, new StatChangeAttr(BattleStat.ATK, -1)),
new AttackMove(Moves.HYPER_BEAM, "Hyper Beam", Type.NORMAL, MoveCategory.SPECIAL, 150, 90, 5, 163, "User must recharge next turn.", -1, 0, 1, new AddTagAttr(BattleTagType.RECHARGING, true)), new AttackMove(Moves.HYPER_BEAM, "Hyper Beam", Type.NORMAL, MoveCategory.SPECIAL, 150, 90, 5, 163, "User must recharge next turn.", -1, 0, 1, new AddBattlerTagAttr(BattlerTagType.RECHARGING, true)),
new AttackMove(Moves.PECK, "Peck", Type.FLYING, MoveCategory.PHYSICAL, 35, 100, 35, -1, "", -1, 0, 1), new AttackMove(Moves.PECK, "Peck", Type.FLYING, MoveCategory.PHYSICAL, 35, 100, 35, -1, "", -1, 0, 1),
new AttackMove(Moves.DRILL_PECK, "Drill Peck", Type.FLYING, MoveCategory.PHYSICAL, 80, 100, 20, -1, "", -1, 0, 1), new AttackMove(Moves.DRILL_PECK, "Drill Peck", Type.FLYING, MoveCategory.PHYSICAL, 80, 100, 20, -1, "", -1, 0, 1),
new AttackMove(Moves.SUBMISSION, "Submission", Type.FIGHTING, MoveCategory.PHYSICAL, 80, 80, 20, -1, "User receives recoil damage.", -1, 0, 1, new RecoilAttr()), new AttackMove(Moves.SUBMISSION, "Submission", Type.FIGHTING, MoveCategory.PHYSICAL, 80, 80, 20, -1, "User receives recoil damage.", -1, 0, 1, new RecoilAttr()),
@ -1467,7 +1617,7 @@ export const allMoves = [
new AttackMove(Moves.ABSORB, "Absorb", Type.GRASS, MoveCategory.SPECIAL, 20, 100, 25, -1, "User recovers half the HP inflicted on opponent.", -1, 0, 1, new HitHealAttr()), new AttackMove(Moves.ABSORB, "Absorb", Type.GRASS, MoveCategory.SPECIAL, 20, 100, 25, -1, "User recovers half the HP inflicted on opponent.", -1, 0, 1, new HitHealAttr()),
new AttackMove(Moves.MEGA_DRAIN, "Mega Drain", Type.GRASS, MoveCategory.SPECIAL, 40, 100, 15, -1, "User recovers half the HP inflicted on opponent.", -1, 0, 1, new HitHealAttr()), new AttackMove(Moves.MEGA_DRAIN, "Mega Drain", Type.GRASS, MoveCategory.SPECIAL, 40, 100, 15, -1, "User recovers half the HP inflicted on opponent.", -1, 0, 1, new HitHealAttr()),
new StatusMove(Moves.LEECH_SEED, "Leech Seed", Type.GRASS, 90, 10, -1, "Drains HP from opponent each turn.", -1, 0, 1, new StatusMove(Moves.LEECH_SEED, "Leech Seed", Type.GRASS, 90, 10, -1, "Drains HP from opponent each turn.", -1, 0, 1,
new ConditionalMoveAttr((user: Pokemon, target: Pokemon, move: Move) => !target.getTag(BattleTagType.SEEDED) && !target.species.isOfType(Type.GRASS)), new AddTagAttr(BattleTagType.SEEDED)), new ConditionalMoveAttr((user: Pokemon, target: Pokemon, move: Move) => !target.getTag(BattlerTagType.SEEDED) && !target.species.isOfType(Type.GRASS)), new AddBattlerTagAttr(BattlerTagType.SEEDED)),
new SelfStatusMove(Moves.GROWTH, "Growth", Type.NORMAL, -1, 20, -1, "Raises user's Attack and Special Attack.", -1, 0, 1, new GrowthStatChangeAttr()), new SelfStatusMove(Moves.GROWTH, "Growth", Type.NORMAL, -1, 20, -1, "Raises user's Attack and Special Attack.", -1, 0, 1, new GrowthStatChangeAttr()),
new AttackMove(Moves.RAZOR_LEAF, "Razor Leaf", Type.GRASS, MoveCategory.PHYSICAL, 55, 95, 25, -1, "High critical hit ratio.", -1, 0, 1, new HighCritAttr()), new AttackMove(Moves.RAZOR_LEAF, "Razor Leaf", Type.GRASS, MoveCategory.PHYSICAL, 55, 95, 25, -1, "High critical hit ratio.", -1, 0, 1, new HighCritAttr()),
new AttackMove(Moves.SOLAR_BEAM, "Solar Beam", Type.GRASS, MoveCategory.SPECIAL, 120, 100, 10, 168, "Charges on first turn, attacks on second.", -1, 0, 1, new AttackMove(Moves.SOLAR_BEAM, "Solar Beam", Type.GRASS, MoveCategory.SPECIAL, 120, 100, 10, 168, "Charges on first turn, attacks on second.", -1, 0, 1,
@ -1486,10 +1636,10 @@ export const allMoves = [
new AttackMove(Moves.THUNDER, "Thunder", Type.ELECTRIC, MoveCategory.SPECIAL, 110, 70, 10, 166, "May paralyze opponent.", 30, 0, 1, new StatusEffectAttr(StatusEffect.PARALYSIS)), new AttackMove(Moves.THUNDER, "Thunder", Type.ELECTRIC, MoveCategory.SPECIAL, 110, 70, 10, 166, "May paralyze opponent.", 30, 0, 1, new StatusEffectAttr(StatusEffect.PARALYSIS)),
new AttackMove(Moves.ROCK_THROW, "Rock Throw", Type.ROCK, MoveCategory.PHYSICAL, 50, 90, 15, -1, "", -1, 0, 1), new AttackMove(Moves.ROCK_THROW, "Rock Throw", Type.ROCK, MoveCategory.PHYSICAL, 50, 90, 15, -1, "", -1, 0, 1),
new AttackMove(Moves.EARTHQUAKE, "Earthquake", Type.GROUND, MoveCategory.PHYSICAL, 100, 100, 10, 149, "Power is doubled if opponent is underground from using Dig.", -1, 0, 1, new AttackMove(Moves.EARTHQUAKE, "Earthquake", Type.GROUND, MoveCategory.PHYSICAL, 100, 100, 10, 149, "Power is doubled if opponent is underground from using Dig.", -1, 0, 1,
new HitsTagAttr(BattleTagType.UNDERGROUND, true)), new HitsTagAttr(BattlerTagType.UNDERGROUND, true)),
new AttackMove(Moves.FISSURE, "Fissure", Type.GROUND, MoveCategory.PHYSICAL, -1, 30, 5, -1, "One-Hit-KO, if it hits.", -1, 0, 1, new OneHitKOAttr()), new AttackMove(Moves.FISSURE, "Fissure", Type.GROUND, MoveCategory.PHYSICAL, -1, 30, 5, -1, "One-Hit-KO, if it hits.", -1, 0, 1, new OneHitKOAttr()),
new AttackMove(Moves.DIG, "Dig", Type.GROUND, MoveCategory.PHYSICAL, 80, 100, 10, 55, "Digs underground on first turn, attacks on second. Can also escape from caves.", -1, 0, 1, new AttackMove(Moves.DIG, "Dig", Type.GROUND, MoveCategory.PHYSICAL, 80, 100, 10, 55, "Digs underground on first turn, attacks on second. Can also escape from caves.", -1, 0, 1,
new ChargeAttr(ChargeAnim.DIG_CHARGING, 'dug a hole!', BattleTagType.UNDERGROUND)), new ChargeAttr(ChargeAnim.DIG_CHARGING, 'dug a hole!', BattlerTagType.UNDERGROUND)),
new StatusMove(Moves.TOXIC, "Toxic", Type.POISON, 90, 10, -1, "Badly poisons opponent.", -1, 0, 1, new StatusEffectAttr(StatusEffect.TOXIC)), new StatusMove(Moves.TOXIC, "Toxic", Type.POISON, 90, 10, -1, "Badly poisons opponent.", -1, 0, 1, new StatusEffectAttr(StatusEffect.TOXIC)),
new AttackMove(Moves.CONFUSION, "Confusion", Type.PSYCHIC, MoveCategory.SPECIAL, 50, 100, 25, -1, "May confuse opponent.", 10, 0, 1, new ConfuseAttr()), new AttackMove(Moves.CONFUSION, "Confusion", Type.PSYCHIC, MoveCategory.SPECIAL, 50, 100, 25, -1, "May confuse opponent.", 10, 0, 1, new ConfuseAttr()),
new AttackMove(Moves.PSYCHIC, "Psychic", Type.PSYCHIC, MoveCategory.SPECIAL, 90, 100, 10, 120, "May lower opponent's Special Defense.", 10, 0, 1, new StatChangeAttr(BattleStat.SPDEF, -1)), new AttackMove(Moves.PSYCHIC, "Psychic", Type.PSYCHIC, MoveCategory.SPECIAL, 90, 100, 10, 120, "May lower opponent's Special Defense.", 10, 0, 1, new StatChangeAttr(BattleStat.SPDEF, -1)),
@ -1500,7 +1650,7 @@ export const allMoves = [
new AttackMove(Moves.RAGE, "Rage", Type.NORMAL, MoveCategory.PHYSICAL, 20, 100, 20, -1, "Raises user's Attack when hit.", -1, 0, 1), // TODO new AttackMove(Moves.RAGE, "Rage", Type.NORMAL, MoveCategory.PHYSICAL, 20, 100, 20, -1, "Raises user's Attack when hit.", -1, 0, 1), // TODO
new SelfStatusMove(Moves.TELEPORT, "Teleport", Type.PSYCHIC, -1, 20, -1, "Allows user to flee wild battles.", -1, 0, 1), new SelfStatusMove(Moves.TELEPORT, "Teleport", Type.PSYCHIC, -1, 20, -1, "Allows user to flee wild battles.", -1, 0, 1),
new AttackMove(Moves.NIGHT_SHADE, "Night Shade", Type.GHOST, MoveCategory.SPECIAL, -1, 100, 15, 42, "Inflicts damage equal to user's level.", -1, 0, 1), new AttackMove(Moves.NIGHT_SHADE, "Night Shade", Type.GHOST, MoveCategory.SPECIAL, -1, 100, 15, 42, "Inflicts damage equal to user's level.", -1, 0, 1),
new StatusMove(Moves.MIMIC, "Mimic", Type.NORMAL, -1, 10, -1, "Copies the opponent's last move.", -1, 0, 1), new StatusMove(Moves.MIMIC, "Mimic", Type.NORMAL, -1, 10, -1, "Copies the opponent's last move.", -1, 0, 1, new ConditionalMoveAttr(targetMoveCopiableCondition), new CopyMoveAttr()),
new StatusMove(Moves.SCREECH, "Screech", Type.NORMAL, 85, 40, -1, "Sharply lowers opponent's Defense.", -1, 0, 1, new StatChangeAttr(BattleStat.DEF, -2)), new StatusMove(Moves.SCREECH, "Screech", Type.NORMAL, 85, 40, -1, "Sharply lowers opponent's Defense.", -1, 0, 1, new StatChangeAttr(BattleStat.DEF, -2)),
new SelfStatusMove(Moves.DOUBLE_TEAM, "Double Team", Type.NORMAL, -1, 15, -1, "Raises user's Evasiveness.", -1, 0, 1, new StatChangeAttr(BattleStat.EVA, 1, true)), new SelfStatusMove(Moves.DOUBLE_TEAM, "Double Team", Type.NORMAL, -1, 15, -1, "Raises user's Evasiveness.", -1, 0, 1, new StatChangeAttr(BattleStat.EVA, 1, true)),
new SelfStatusMove(Moves.RECOVER, "Recover", Type.NORMAL, -1, 5, -1, "User recovers half its max HP.", -1, 0, 1, new HealAttr(0.5)), new SelfStatusMove(Moves.RECOVER, "Recover", Type.NORMAL, -1, 5, -1, "User recovers half its max HP.", -1, 0, 1, new HealAttr(0.5)),
@ -1572,7 +1722,8 @@ export const allMoves = [
new AttackMove(Moves.SLASH, "Slash", Type.NORMAL, MoveCategory.PHYSICAL, 70, 100, 20, -1, "High critical hit ratio.", -1, 0, 1, new HighCritAttr()), new AttackMove(Moves.SLASH, "Slash", Type.NORMAL, MoveCategory.PHYSICAL, 70, 100, 20, -1, "High critical hit ratio.", -1, 0, 1, new HighCritAttr()),
new SelfStatusMove(Moves.SUBSTITUTE, "Substitute", Type.NORMAL, -1, 10, 103, "Uses HP to creates a decoy that takes hits.", -1, 0, 1), new SelfStatusMove(Moves.SUBSTITUTE, "Substitute", Type.NORMAL, -1, 10, 103, "Uses HP to creates a decoy that takes hits.", -1, 0, 1),
new AttackMove(Moves.STRUGGLE, "Struggle", Type.NORMAL, MoveCategory.PHYSICAL, 50, -1, -1, -1, "Only usable when all PP are gone. Hurts the user.", -1, 0, 1), new AttackMove(Moves.STRUGGLE, "Struggle", Type.NORMAL, MoveCategory.PHYSICAL, 50, -1, -1, -1, "Only usable when all PP are gone. Hurts the user.", -1, 0, 1),
new SelfStatusMove(Moves.SKETCH, "Sketch", Type.NORMAL, -1, 1, -1, "Permanently copies the opponent's last move.", -1, 0, 2), new SelfStatusMove(Moves.SKETCH, "Sketch", Type.NORMAL, -1, 1, -1, "Permanently copies the opponent's last move.", -1, 0, 2,
new ConditionalMoveAttr(targetMoveSketchableCondition), new SketchAttr()),
new AttackMove(Moves.TRIPLE_KICK, "Triple Kick", Type.FIGHTING, MoveCategory.PHYSICAL, 10, 90, 10, -1, "Hits thrice in one turn at increasing power.", -1, 0, 2, new AttackMove(Moves.TRIPLE_KICK, "Triple Kick", Type.FIGHTING, MoveCategory.PHYSICAL, 10, 90, 10, -1, "Hits thrice in one turn at increasing power.", -1, 0, 2,
new MultiHitAttr(MultiHitType._3_INCR), new MissEffectAttr((user: Pokemon, target: Pokemon, move: Move) => { new MultiHitAttr(MultiHitType._3_INCR), new MissEffectAttr((user: Pokemon, target: Pokemon, move: Move) => {
user.turnData.hitsLeft = 0; user.turnData.hitsLeft = 0;
@ -1582,7 +1733,7 @@ export const allMoves = [
new StatusMove(Moves.SPIDER_WEB, "Spider Web", Type.BUG, -1, 10, -1, "Opponent cannot escape/switch.", -1, 0, 2), new StatusMove(Moves.SPIDER_WEB, "Spider Web", Type.BUG, -1, 10, -1, "Opponent cannot escape/switch.", -1, 0, 2),
new SelfStatusMove(Moves.MIND_READER, "Mind Reader", Type.NORMAL, -1, 5, -1, "User's next attack is guaranteed to hit.", -1, 0, 2), new SelfStatusMove(Moves.MIND_READER, "Mind Reader", Type.NORMAL, -1, 5, -1, "User's next attack is guaranteed to hit.", -1, 0, 2),
new StatusMove(Moves.NIGHTMARE, "Nightmare", Type.GHOST, 100, 15, -1, "The sleeping opponent loses 25% of its max HP each turn.", -1, 0, 2, new StatusMove(Moves.NIGHTMARE, "Nightmare", Type.GHOST, 100, 15, -1, "The sleeping opponent loses 25% of its max HP each turn.", -1, 0, 2,
new ConditionalMoveAttr((user: Pokemon, target: Pokemon, move: Move) => target.status?.effect === StatusEffect.SLEEP), new AddTagAttr(BattleTagType.NIGHTMARE)), new ConditionalMoveAttr((user: Pokemon, target: Pokemon, move: Move) => target.status?.effect === StatusEffect.SLEEP), new AddBattlerTagAttr(BattlerTagType.NIGHTMARE)),
new AttackMove(Moves.FLAME_WHEEL, "Flame Wheel", Type.FIRE, MoveCategory.PHYSICAL, 60, 100, 25, -1, "May burn opponent.", 10, 0, 2, new StatusEffectAttr(StatusEffect.BURN)), new AttackMove(Moves.FLAME_WHEEL, "Flame Wheel", Type.FIRE, MoveCategory.PHYSICAL, 60, 100, 25, -1, "May burn opponent.", 10, 0, 2, new StatusEffectAttr(StatusEffect.BURN)),
new AttackMove(Moves.SNORE, "Snore", Type.NORMAL, MoveCategory.SPECIAL, 50, 100, 15, -1, "Can only be used if asleep. May cause flinching.", 30, 0, 2, new AttackMove(Moves.SNORE, "Snore", Type.NORMAL, MoveCategory.SPECIAL, 50, 100, 15, -1, "Can only be used if asleep. May cause flinching.", 30, 0, 2,
new BypassSleepAttr(), new ConditionalMoveAttr((user: Pokemon, target: Pokemon, move: Move) => user.status?.effect === StatusEffect.SLEEP), new FlinchAttr()), new BypassSleepAttr(), new ConditionalMoveAttr((user: Pokemon, target: Pokemon, move: Move) => user.status?.effect === StatusEffect.SLEEP), new FlinchAttr()),
@ -1595,7 +1746,7 @@ export const allMoves = [
new StatusMove(Moves.SPITE, "Spite", Type.GHOST, 100, 10, -1, "The opponent's last move loses 2-5 PP.", -1, 0, 2), new StatusMove(Moves.SPITE, "Spite", Type.GHOST, 100, 10, -1, "The opponent's last move loses 2-5 PP.", -1, 0, 2),
new AttackMove(Moves.POWDER_SNOW, "Powder Snow", Type.ICE, MoveCategory.SPECIAL, 40, 100, 25, -1, "May freeze opponent.", 10, 0, 2, new StatusEffectAttr(StatusEffect.FREEZE)), new AttackMove(Moves.POWDER_SNOW, "Powder Snow", Type.ICE, MoveCategory.SPECIAL, 40, 100, 25, -1, "May freeze opponent.", 10, 0, 2, new StatusEffectAttr(StatusEffect.FREEZE)),
new SelfStatusMove(Moves.PROTECT, "Protect", Type.NORMAL, -1, 10, 7, "Protects the user, but may fail if used consecutively.", -1, 4, 2, new SelfStatusMove(Moves.PROTECT, "Protect", Type.NORMAL, -1, 10, 7, "Protects the user, but may fail if used consecutively.", -1, 4, 2,
new WaningConsecutiveUseConditionalMoveAttr(), new AddTagAttr(BattleTagType.PROTECTED, true)), new WaningConsecutiveUseConditionalMoveAttr(), new AddBattlerTagAttr(BattlerTagType.PROTECTED, true)),
new AttackMove(Moves.MACH_PUNCH, "Mach Punch", Type.FIGHTING, MoveCategory.PHYSICAL, 40, 100, 30, -1, "User attacks first.", -1, 1, 2), new AttackMove(Moves.MACH_PUNCH, "Mach Punch", Type.FIGHTING, MoveCategory.PHYSICAL, 40, 100, 30, -1, "User attacks first.", -1, 1, 2),
new StatusMove(Moves.SCARY_FACE, "Scary Face", Type.NORMAL, 100, 10, 6, "Sharply lowers opponent's Speed.", -1, 0, 2, new StatChangeAttr(BattleStat.SPD, -2)), new StatusMove(Moves.SCARY_FACE, "Scary Face", Type.NORMAL, 100, 10, 6, "Sharply lowers opponent's Speed.", -1, 0, 2, new StatChangeAttr(BattleStat.SPD, -2)),
new AttackMove(Moves.FEINT_ATTACK, "Feint Attack", Type.DARK, MoveCategory.PHYSICAL, 60, -1, 20, -1, "Ignores Accuracy and Evasiveness.", -1, 0, 2), new AttackMove(Moves.FEINT_ATTACK, "Feint Attack", Type.DARK, MoveCategory.PHYSICAL, 60, -1, 20, -1, "Ignores Accuracy and Evasiveness.", -1, 0, 2),
@ -1611,7 +1762,7 @@ export const allMoves = [
new StatusMove(Moves.PERISH_SONG, "Perish Song", Type.NORMAL, -1, 5, -1, "Any Pokémon in play when this attack is used faints in 3 turns.", -1, 0, 2).ignoreProtect(), new StatusMove(Moves.PERISH_SONG, "Perish Song", Type.NORMAL, -1, 5, -1, "Any Pokémon in play when this attack is used faints in 3 turns.", -1, 0, 2).ignoreProtect(),
new AttackMove(Moves.ICY_WIND, "Icy Wind", Type.ICE, MoveCategory.SPECIAL, 55, 95, 15, 34, "Lowers opponent's Speed.", 100, 0, 2, new StatChangeAttr(BattleStat.SPD, -1)), new AttackMove(Moves.ICY_WIND, "Icy Wind", Type.ICE, MoveCategory.SPECIAL, 55, 95, 15, 34, "Lowers opponent's Speed.", 100, 0, 2, new StatChangeAttr(BattleStat.SPD, -1)),
new SelfStatusMove(Moves.DETECT, "Detect", Type.FIGHTING, -1, 5, -1, "Protects the user, but may fail if used consecutively.", -1, 4, 2, new SelfStatusMove(Moves.DETECT, "Detect", Type.FIGHTING, -1, 5, -1, "Protects the user, but may fail if used consecutively.", -1, 4, 2,
new WaningConsecutiveUseConditionalMoveAttr(), new AddTagAttr(BattleTagType.PROTECTED, true)), new WaningConsecutiveUseConditionalMoveAttr(), new AddBattlerTagAttr(BattlerTagType.PROTECTED, true)),
new AttackMove(Moves.BONE_RUSH, "Bone Rush", Type.GROUND, MoveCategory.PHYSICAL, 25, 90, 10, -1, "Hits 2-5 times in one turn.", -1, 0, 2, new MultiHitAttr()), new AttackMove(Moves.BONE_RUSH, "Bone Rush", Type.GROUND, MoveCategory.PHYSICAL, 25, 90, 10, -1, "Hits 2-5 times in one turn.", -1, 0, 2, new MultiHitAttr()),
new SelfStatusMove(Moves.LOCK_ON, "Lock-On", Type.NORMAL, -1, 5, -1, "User's next attack is guaranteed to hit.", -1, 0, 2), new SelfStatusMove(Moves.LOCK_ON, "Lock-On", Type.NORMAL, -1, 5, -1, "User's next attack is guaranteed to hit.", -1, 0, 2),
new AttackMove(Moves.OUTRAGE, "Outrage", Type.DRAGON, MoveCategory.PHYSICAL, 120, 100, 10, 156, "User attacks for 2-3 turns but then becomes confused.", -1, 0, 2, new AttackMove(Moves.OUTRAGE, "Outrage", Type.DRAGON, MoveCategory.PHYSICAL, 120, 100, 10, 156, "User attacks for 2-3 turns but then becomes confused.", -1, 0, 2,
@ -1656,7 +1807,7 @@ export const allMoves = [
new AttackMove(Moves.HIDDEN_POWER, "Hidden Power", Type.NORMAL, MoveCategory.SPECIAL, 60, 100, 15, -1, "Type and power depends on user's IVs.", -1, 0, 2), new AttackMove(Moves.HIDDEN_POWER, "Hidden Power", Type.NORMAL, MoveCategory.SPECIAL, 60, 100, 15, -1, "Type and power depends on user's IVs.", -1, 0, 2),
new AttackMove(Moves.CROSS_CHOP, "Cross Chop", Type.FIGHTING, MoveCategory.PHYSICAL, 100, 80, 5, -1, "High critical hit ratio.", -1, 0, 2, new HighCritAttr()), new AttackMove(Moves.CROSS_CHOP, "Cross Chop", Type.FIGHTING, MoveCategory.PHYSICAL, 100, 80, 5, -1, "High critical hit ratio.", -1, 0, 2, new HighCritAttr()),
new AttackMove(Moves.TWISTER, "Twister", Type.DRAGON, MoveCategory.SPECIAL, 40, 100, 20, -1, "May cause flinching. Hits Pokémon using Fly/Bounce with double power.", 20, 0, 2, new AttackMove(Moves.TWISTER, "Twister", Type.DRAGON, MoveCategory.SPECIAL, 40, 100, 20, -1, "May cause flinching. Hits Pokémon using Fly/Bounce with double power.", 20, 0, 2,
new HitsTagAttr(BattleTagType.FLYING, true), new FlinchAttr()), // TODO new HitsTagAttr(BattlerTagType.FLYING, true), new FlinchAttr()), // TODO
new SelfStatusMove(Moves.RAIN_DANCE, "Rain Dance", Type.WATER, -1, 5, 50, "Makes it rain for 5 turns.", -1, 0, 2, new WeatherConditionalMoveAttr(WeatherType.RAIN), new WeatherChangeAttr(WeatherType.RAIN)), new SelfStatusMove(Moves.RAIN_DANCE, "Rain Dance", Type.WATER, -1, 5, 50, "Makes it rain for 5 turns.", -1, 0, 2, new WeatherConditionalMoveAttr(WeatherType.RAIN), new WeatherChangeAttr(WeatherType.RAIN)),
new SelfStatusMove(Moves.SUNNY_DAY, "Sunny Day", Type.FIRE, -1, 5, 49, "Makes it sunny for 5 turns.", -1, 0, 2, new WeatherConditionalMoveAttr(WeatherType.SUNNY), new WeatherChangeAttr(WeatherType.SUNNY)), new SelfStatusMove(Moves.SUNNY_DAY, "Sunny Day", Type.FIRE, -1, 5, 49, "Makes it sunny for 5 turns.", -1, 0, 2, new WeatherConditionalMoveAttr(WeatherType.SUNNY), new WeatherChangeAttr(WeatherType.SUNNY)),
new AttackMove(Moves.CRUNCH, "Crunch", Type.DARK, MoveCategory.PHYSICAL, 80, 100, 15, 108, "May lower opponent's Defense.", 20, 0, 2, new StatChangeAttr(BattleStat.DEF, -1)), new AttackMove(Moves.CRUNCH, "Crunch", Type.DARK, MoveCategory.PHYSICAL, 80, 100, 15, 108, "May lower opponent's Defense.", 20, 0, 2, new StatChangeAttr(BattleStat.DEF, -1)),
@ -1671,7 +1822,7 @@ export const allMoves = [
new AttackMove(Moves.WHIRLPOOL, "Whirlpool", Type.WATER, MoveCategory.SPECIAL, 35, 85, 15, -1, "Traps opponent, damaging them for 4-5 turns.", 100, 0, 2), new AttackMove(Moves.WHIRLPOOL, "Whirlpool", Type.WATER, MoveCategory.SPECIAL, 35, 85, 15, -1, "Traps opponent, damaging them for 4-5 turns.", 100, 0, 2),
new AttackMove(Moves.BEAT_UP, "Beat Up", Type.DARK, MoveCategory.PHYSICAL, -1, 100, 10, -1, "Each Pokémon in user's party attacks.", -1, 0, 2), new AttackMove(Moves.BEAT_UP, "Beat Up", Type.DARK, MoveCategory.PHYSICAL, -1, 100, 10, -1, "Each Pokémon in user's party attacks.", -1, 0, 2),
new AttackMove(Moves.FAKE_OUT, "Fake Out", Type.NORMAL, MoveCategory.PHYSICAL, 40, 100, 10, -1, "User attacks first, foe flinches. Only usable on first turn.", 100, 3, 3, new AttackMove(Moves.FAKE_OUT, "Fake Out", Type.NORMAL, MoveCategory.PHYSICAL, 40, 100, 10, -1, "User attacks first, foe flinches. Only usable on first turn.", 100, 3, 3,
new ConditionalMoveAttr((user: Pokemon, target: Pokemon, move: Move) => user.battleSummonData.turnCount === 1), new FlinchAttr()), new ConditionalMoveAttr((user: Pokemon, target: Pokemon, move: Move) => user.scene.currentBattle.turn === 1), new FlinchAttr()),
new AttackMove(Moves.UPROAR, "Uproar", Type.NORMAL, MoveCategory.SPECIAL, 90, 100, 10, -1, "User attacks for 3 turns and prevents sleep.", -1, 0, 3), // TODO new AttackMove(Moves.UPROAR, "Uproar", Type.NORMAL, MoveCategory.SPECIAL, 90, 100, 10, -1, "User attacks for 3 turns and prevents sleep.", -1, 0, 3), // TODO
new SelfStatusMove(Moves.STOCKPILE, "Stockpile", Type.NORMAL, -1, 20, -1, "Stores energy for use with Spit Up and Swallow.", -1, 0, 3), new SelfStatusMove(Moves.STOCKPILE, "Stockpile", Type.NORMAL, -1, 20, -1, "Stores energy for use with Spit Up and Swallow.", -1, 0, 3),
new AttackMove(Moves.SPIT_UP, "Spit Up", Type.NORMAL, MoveCategory.SPECIAL, -1, 100, 10, -1, "Power depends on how many times the user performed Stockpile.", -1, 0, 3), new AttackMove(Moves.SPIT_UP, "Spit Up", Type.NORMAL, MoveCategory.SPECIAL, -1, 100, 10, -1, "Power depends on how many times the user performed Stockpile.", -1, 0, 3),
@ -1697,7 +1848,7 @@ export const allMoves = [
new SelfStatusMove(Moves.WISH, "Wish", Type.NORMAL, -1, 10, -1, "The user recovers HP in the following turn.", -1, 0, 3), new SelfStatusMove(Moves.WISH, "Wish", Type.NORMAL, -1, 10, -1, "The user recovers HP in the following turn.", -1, 0, 3),
new SelfStatusMove(Moves.ASSIST, "Assist", Type.NORMAL, -1, 20, -1, "User performs a move known by its allies at random.", -1, 0, 3), new SelfStatusMove(Moves.ASSIST, "Assist", Type.NORMAL, -1, 20, -1, "User performs a move known by its allies at random.", -1, 0, 3),
new SelfStatusMove(Moves.INGRAIN, "Ingrain", Type.GRASS, -1, 20, -1, "User restores HP each turn. User cannot escape/switch.", -1, 0, 3, new SelfStatusMove(Moves.INGRAIN, "Ingrain", Type.GRASS, -1, 20, -1, "User restores HP each turn. User cannot escape/switch.", -1, 0, 3,
new NoTagOverlapConditionalAttr(BattleTagType.INGRAIN, true), new AddTagAttr(BattleTagType.INGRAIN, true)), new NoTagOverlapConditionalAttr(BattlerTagType.INGRAIN, true), new AddBattlerTagAttr(BattlerTagType.INGRAIN, true)),
new AttackMove(Moves.SUPERPOWER, "Superpower", Type.FIGHTING, MoveCategory.PHYSICAL, 120, 100, 5, -1, "Lowers user's Attack and Defense.", 100, 0, 3, new AttackMove(Moves.SUPERPOWER, "Superpower", Type.FIGHTING, MoveCategory.PHYSICAL, 120, 100, 5, -1, "Lowers user's Attack and Defense.", 100, 0, 3,
new StatChangeAttr([ BattleStat.ATK, BattleStat.DEF ], -1, true)), new StatChangeAttr([ BattleStat.ATK, BattleStat.DEF ], -1, true)),
new SelfStatusMove(Moves.MAGIC_COAT, "Magic Coat", Type.PSYCHIC, -1, 15, -1, "Reflects moves that cause status conditions back to the attacker.", -1, 4, 3), new SelfStatusMove(Moves.MAGIC_COAT, "Magic Coat", Type.PSYCHIC, -1, 15, -1, "Reflects moves that cause status conditions back to the attacker.", -1, 4, 3),
@ -1705,10 +1856,10 @@ export const allMoves = [
new AttackMove(Moves.REVENGE, "Revenge", Type.FIGHTING, MoveCategory.PHYSICAL, 60, 100, 10, -1, "Power increases if user was hit first.", -1, -4, 3), new AttackMove(Moves.REVENGE, "Revenge", Type.FIGHTING, MoveCategory.PHYSICAL, 60, 100, 10, -1, "Power increases if user was hit first.", -1, -4, 3),
new AttackMove(Moves.BRICK_BREAK, "Brick Break", Type.FIGHTING, MoveCategory.PHYSICAL, 75, 100, 15, 58, "Breaks through Reflect and Light Screen barriers.", -1, 0, 3), new AttackMove(Moves.BRICK_BREAK, "Brick Break", Type.FIGHTING, MoveCategory.PHYSICAL, 75, 100, 15, 58, "Breaks through Reflect and Light Screen barriers.", -1, 0, 3),
new StatusMove(Moves.YAWN, "Yawn", Type.NORMAL, -1, 10, -1, "Puts opponent to sleep in the next turn.", -1, 0, 3, new StatusMove(Moves.YAWN, "Yawn", Type.NORMAL, -1, 10, -1, "Puts opponent to sleep in the next turn.", -1, 0, 3,
new ConditionalMoveAttr((user: Pokemon, target: Pokemon, move: Move) => !target.status), new NoTagOverlapConditionalAttr(BattleTagType.DROWSY), new AddTagAttr(BattleTagType.DROWSY)), new ConditionalMoveAttr((user: Pokemon, target: Pokemon, move: Move) => !target.status), new NoTagOverlapConditionalAttr(BattlerTagType.DROWSY), new AddBattlerTagAttr(BattlerTagType.DROWSY)),
new AttackMove(Moves.KNOCK_OFF, "Knock Off", Type.DARK, MoveCategory.PHYSICAL, 65, 100, 20, -1, "Removes opponent's held item for the rest of the battle.", -1, 0, 3), new AttackMove(Moves.KNOCK_OFF, "Knock Off", Type.DARK, MoveCategory.PHYSICAL, 65, 100, 20, -1, "Removes opponent's held item for the rest of the battle.", -1, 0, 3),
new AttackMove(Moves.ENDEAVOR, "Endeavor", Type.NORMAL, MoveCategory.PHYSICAL, -1, 100, 5, -1, "Reduces opponent's HP to same as user's.", -1, 0, 3), new AttackMove(Moves.ENDEAVOR, "Endeavor", Type.NORMAL, MoveCategory.PHYSICAL, -1, 100, 5, -1, "Reduces opponent's HP to same as user's.", -1, 0, 3),
new AttackMove(Moves.ERUPTION, "Eruption", Type.FIRE, MoveCategory.SPECIAL, 150, 100, 5, -1, "Stronger when the user's HP is higher.", -1, 0, 3), new AttackMove(Moves.ERUPTION, "Eruption", Type.FIRE, MoveCategory.SPECIAL, 150, 100, 5, -1, "Stronger when the user's HP is higher.", -1, 0, 3, new HpPowerAttr()),
new StatusMove(Moves.SKILL_SWAP, "Skill Swap", Type.PSYCHIC, -1, 10, 98, "The user swaps Abilities with the opponent.", -1, 0, 3), new StatusMove(Moves.SKILL_SWAP, "Skill Swap", Type.PSYCHIC, -1, 10, 98, "The user swaps Abilities with the opponent.", -1, 0, 3),
new StatusMove(Moves.IMPRISON, "Imprison", Type.PSYCHIC, -1, 10, 92, "Opponent is unable to use moves that the user also knows.", -1, 0, 3), new StatusMove(Moves.IMPRISON, "Imprison", Type.PSYCHIC, -1, 10, 92, "Opponent is unable to use moves that the user also knows.", -1, 0, 3),
new SelfStatusMove(Moves.REFRESH, "Refresh", Type.NORMAL, -1, 20, -1, "Cures paralysis, poison, and burns.", -1, 0, 3, new SelfStatusMove(Moves.REFRESH, "Refresh", Type.NORMAL, -1, 20, -1, "Cures paralysis, poison, and burns.", -1, 0, 3,
@ -1720,22 +1871,22 @@ export const allMoves = [
new AttackMove(Moves.DIVE, "Dive", Type.WATER, MoveCategory.PHYSICAL, 80, 100, 10, -1, "Dives underwater on first turn, attacks on second turn.", -1, 0, 3, new AttackMove(Moves.DIVE, "Dive", Type.WATER, MoveCategory.PHYSICAL, 80, 100, 10, -1, "Dives underwater on first turn, attacks on second turn.", -1, 0, 3,
new ChargeAttr(ChargeAnim.DIVE_CHARGING, 'hid\nunderwater!')), new ChargeAttr(ChargeAnim.DIVE_CHARGING, 'hid\nunderwater!')),
new AttackMove(Moves.ARM_THRUST, "Arm Thrust", Type.FIGHTING, MoveCategory.PHYSICAL, 15, 100, 20, -1, "Hits 2-5 times in one turn.", -1, 0, 3, new MultiHitAttr()), new AttackMove(Moves.ARM_THRUST, "Arm Thrust", Type.FIGHTING, MoveCategory.PHYSICAL, 15, 100, 20, -1, "Hits 2-5 times in one turn.", -1, 0, 3, new MultiHitAttr()),
new SelfStatusMove(Moves.CAMOUFLAGE, "Camouflage", Type.NORMAL, -1, 20, -1, "Changes user's type according to the location.", -1, 0, 3), new SelfStatusMove(Moves.CAMOUFLAGE, "Camouflage", Type.NORMAL, -1, 20, -1, "Changes user's type according to the location.", -1, 0, 3, new CopyBiomeTypeAttr()),
new SelfStatusMove(Moves.TAIL_GLOW, "Tail Glow", Type.BUG, -1, 20, -1, "Drastically raises user's Special Attack.", -1, 0, 3, new StatChangeAttr(BattleStat.SPATK, 3, true)), new SelfStatusMove(Moves.TAIL_GLOW, "Tail Glow", Type.BUG, -1, 20, -1, "Drastically raises user's Special Attack.", -1, 0, 3, new StatChangeAttr(BattleStat.SPATK, 3, true)),
new AttackMove(Moves.LUSTER_PURGE, "Luster Purge", Type.PSYCHIC, MoveCategory.SPECIAL, 70, 100, 5, -1, "May lower opponent's Special Defense.", 50, 0, 3, new StatChangeAttr(BattleStat.SPDEF, -1)), new AttackMove(Moves.LUSTER_PURGE, "Luster Purge", Type.PSYCHIC, MoveCategory.SPECIAL, 70, 100, 5, -1, "May lower opponent's Special Defense.", 50, 0, 3, new StatChangeAttr(BattleStat.SPDEF, -1)),
new AttackMove(Moves.MIST_BALL, "Mist Ball", Type.PSYCHIC, MoveCategory.SPECIAL, 70, 100, 5, -1, "May lower opponent's Special Attack.", 50, 0, 3, new StatChangeAttr(BattleStat.SPATK, -1)), new AttackMove(Moves.MIST_BALL, "Mist Ball", Type.PSYCHIC, MoveCategory.SPECIAL, 70, 100, 5, -1, "May lower opponent's Special Attack.", 50, 0, 3, new StatChangeAttr(BattleStat.SPATK, -1)),
new StatusMove(Moves.FEATHER_DANCE, "Feather Dance", Type.FLYING, 100, 15, -1, "Sharply lowers opponent's Attack.", -1, 0, 3, new StatChangeAttr(BattleStat.ATK, -2)), new StatusMove(Moves.FEATHER_DANCE, "Feather Dance", Type.FLYING, 100, 15, -1, "Sharply lowers opponent's Attack.", -1, 0, 3, new StatChangeAttr(BattleStat.ATK, -2)),
new StatusMove(Moves.TEETER_DANCE, "Teeter Dance", Type.NORMAL, 100, 20, -1, "Confuses all Pokémon.", -1, 0, 3, new ConfuseAttr(true), new ConfuseAttr()), new StatusMove(Moves.TEETER_DANCE, "Teeter Dance", Type.NORMAL, 100, 20, -1, "Confuses all Pokémon.", -1, 0, 3, new ConfuseAttr(true), new ConfuseAttr()),
new AttackMove(Moves.BLAZE_KICK, "Blaze Kick", Type.FIRE, MoveCategory.PHYSICAL, 85, 90, 10, -1, "High critical hit ratio. May burn opponent.", 10, 0, 3, new HighCritAttr(), new StatusEffectAttr(StatusEffect.BURN)), new AttackMove(Moves.BLAZE_KICK, "Blaze Kick", Type.FIRE, MoveCategory.PHYSICAL, 85, 90, 10, -1, "High critical hit ratio. May burn opponent.", 10, 0, 3, new HighCritAttr(), new StatusEffectAttr(StatusEffect.BURN)),
new SelfStatusMove(Moves.MUD_SPORT, "Mud Sport", Type.GROUND, -1, 15, -1, "Weakens the power of Electric-type moves.", -1, 0, 3), new SelfStatusMove(Moves.MUD_SPORT, "Mud Sport", Type.GROUND, -1, 15, -1, "Weakens the power of Electric-type moves.", -1, 0, 3, new AddArenaTagAttr(ArenaTagType.MUD_SPORT, 5)),
new AttackMove(Moves.ICE_BALL, "Ice Ball", Type.ICE, MoveCategory.PHYSICAL, 30, 90, 20, -1, "Doubles in power each turn for 5 turns.", -1, 0, 3), new AttackMove(Moves.ICE_BALL, "Ice Ball", Type.ICE, MoveCategory.PHYSICAL, 30, 90, 20, -1, "Doubles in power each turn for 5 turns.", -1, 0, 3),
new AttackMove(Moves.NEEDLE_ARM, "Needle Arm", Type.GRASS, MoveCategory.PHYSICAL, 60, 100, 15, -1, "May cause flinching.", 30, 0, 3, new FlinchAttr()), new AttackMove(Moves.NEEDLE_ARM, "Needle Arm", Type.GRASS, MoveCategory.PHYSICAL, 60, 100, 15, -1, "May cause flinching.", 30, 0, 3, new FlinchAttr()),
new SelfStatusMove(Moves.SLACK_OFF, "Slack Off", Type.NORMAL, -1, 5, -1, "User recovers half its max HP.", -1, 0, 3, new HealAttr()), new SelfStatusMove(Moves.SLACK_OFF, "Slack Off", Type.NORMAL, -1, 5, -1, "User recovers half its max HP.", -1, 0, 3, new HealAttr()),
new AttackMove(Moves.HYPER_VOICE, "Hyper Voice", Type.NORMAL, MoveCategory.SPECIAL, 90, 100, 10, 117, "", -1, 0, 3), new AttackMove(Moves.HYPER_VOICE, "Hyper Voice", Type.NORMAL, MoveCategory.SPECIAL, 90, 100, 10, 117, "", -1, 0, 3),
new AttackMove(Moves.POISON_FANG, "Poison Fang", Type.POISON, MoveCategory.PHYSICAL, 50, 100, 15, -1, "May badly poison opponent.", 50, 0, 3, new StatusEffectAttr(StatusEffect.TOXIC)), new AttackMove(Moves.POISON_FANG, "Poison Fang", Type.POISON, MoveCategory.PHYSICAL, 50, 100, 15, -1, "May badly poison opponent.", 50, 0, 3, new StatusEffectAttr(StatusEffect.TOXIC)),
new AttackMove(Moves.CRUSH_CLAW, "Crush Claw", Type.NORMAL, MoveCategory.PHYSICAL, 75, 95, 10, -1, "May lower opponent's Defense.", 50, 0, 3, new StatChangeAttr(BattleStat.DEF, -1)), new AttackMove(Moves.CRUSH_CLAW, "Crush Claw", Type.NORMAL, MoveCategory.PHYSICAL, 75, 95, 10, -1, "May lower opponent's Defense.", 50, 0, 3, new StatChangeAttr(BattleStat.DEF, -1)),
new AttackMove(Moves.BLAST_BURN, "Blast Burn", Type.FIRE, MoveCategory.SPECIAL, 150, 90, 5, 153, "User must recharge next turn.", -1, 0, 3, new AddTagAttr(BattleTagType.RECHARGING, true)), new AttackMove(Moves.BLAST_BURN, "Blast Burn", Type.FIRE, MoveCategory.SPECIAL, 150, 90, 5, 153, "User must recharge next turn.", -1, 0, 3, new AddBattlerTagAttr(BattlerTagType.RECHARGING, true)),
new AttackMove(Moves.HYDRO_CANNON, "Hydro Cannon", Type.WATER, MoveCategory.SPECIAL, 150, 90, 5, 154, "User must recharge next turn.", -1, 0, 3, new AddTagAttr(BattleTagType.RECHARGING, true)), new AttackMove(Moves.HYDRO_CANNON, "Hydro Cannon", Type.WATER, MoveCategory.SPECIAL, 150, 90, 5, 154, "User must recharge next turn.", -1, 0, 3, new AddBattlerTagAttr(BattlerTagType.RECHARGING, true)),
new AttackMove(Moves.METEOR_MASH, "Meteor Mash", Type.STEEL, MoveCategory.PHYSICAL, 90, 90, 10, -1, "May raise user's Attack.", 20, 0, 3, new StatChangeAttr(BattleStat.ATK, 1, true)), new AttackMove(Moves.METEOR_MASH, "Meteor Mash", Type.STEEL, MoveCategory.PHYSICAL, 90, 90, 10, -1, "May raise user's Attack.", 20, 0, 3, new StatChangeAttr(BattleStat.ATK, 1, true)),
new AttackMove(Moves.ASTONISH, "Astonish", Type.GHOST, MoveCategory.PHYSICAL, 30, 100, 15, -1, "May cause flinching.", 30, 0, 3, new FlinchAttr()), new AttackMove(Moves.ASTONISH, "Astonish", Type.GHOST, MoveCategory.PHYSICAL, 30, 100, 15, -1, "May cause flinching.", 30, 0, 3, new FlinchAttr()),
new AttackMove(Moves.WEATHER_BALL, "Weather Ball", Type.NORMAL, MoveCategory.SPECIAL, 50, 100, 10, -1, "Move's power and type changes with the weather.", -1, 0, 3), new AttackMove(Moves.WEATHER_BALL, "Weather Ball", Type.NORMAL, MoveCategory.SPECIAL, 50, 100, 10, -1, "Move's power and type changes with the weather.", -1, 0, 3),
@ -1753,11 +1904,11 @@ export const allMoves = [
new StatChangeAttr(BattleStat.ATK, -1), new StatChangeAttr(BattleStat.DEF, -1)), new StatChangeAttr(BattleStat.ATK, -1), new StatChangeAttr(BattleStat.DEF, -1)),
new SelfStatusMove(Moves.COSMIC_POWER, "Cosmic Power", Type.PSYCHIC, -1, 20, -1, "Raises user's Defense and Special Defense.", -1, 0, 3, new SelfStatusMove(Moves.COSMIC_POWER, "Cosmic Power", Type.PSYCHIC, -1, 20, -1, "Raises user's Defense and Special Defense.", -1, 0, 3,
new StatChangeAttr([ BattleStat.DEF, BattleStat.SPDEF ], 1, true)), new StatChangeAttr([ BattleStat.DEF, BattleStat.SPDEF ], 1, true)),
new AttackMove(Moves.WATER_SPOUT, "Water Spout", Type.WATER, MoveCategory.SPECIAL, 150, 100, 5, -1, "The higher the user's HP, the higher the damage caused.", -1, 0, 3), new AttackMove(Moves.WATER_SPOUT, "Water Spout", Type.WATER, MoveCategory.SPECIAL, 150, 100, 5, -1, "The higher the user's HP, the higher the damage caused.", -1, 0, 3, new HpPowerAttr()),
new AttackMove(Moves.SIGNAL_BEAM, "Signal Beam", Type.BUG, MoveCategory.SPECIAL, 75, 100, 15, -1, "May confuse opponent.", 10, 0, 3, new ConfuseAttr()), new AttackMove(Moves.SIGNAL_BEAM, "Signal Beam", Type.BUG, MoveCategory.SPECIAL, 75, 100, 15, -1, "May confuse opponent.", 10, 0, 3, new ConfuseAttr()),
new AttackMove(Moves.SHADOW_PUNCH, "Shadow Punch", Type.GHOST, MoveCategory.PHYSICAL, 60, -1, 20, -1, "Ignores Accuracy and Evasiveness.", -1, 0, 3), new AttackMove(Moves.SHADOW_PUNCH, "Shadow Punch", Type.GHOST, MoveCategory.PHYSICAL, 60, -1, 20, -1, "Ignores Accuracy and Evasiveness.", -1, 0, 3),
new AttackMove(Moves.EXTRASENSORY, "Extrasensory", Type.PSYCHIC, MoveCategory.SPECIAL, 80, 100, 20, -1, "May cause flinching.", 10, 0, 3, new FlinchAttr()), new AttackMove(Moves.EXTRASENSORY, "Extrasensory", Type.PSYCHIC, MoveCategory.SPECIAL, 80, 100, 20, -1, "May cause flinching.", 10, 0, 3, new FlinchAttr()),
new AttackMove(Moves.SKY_UPPERCUT, "Sky Uppercut", Type.FIGHTING, MoveCategory.PHYSICAL, 85, 90, 15, -1, "Hits the opponent, even during Fly.", -1, 0, 3, new HitsTagAttr(BattleTagType.FLYING)), new AttackMove(Moves.SKY_UPPERCUT, "Sky Uppercut", Type.FIGHTING, MoveCategory.PHYSICAL, 85, 90, 15, -1, "Hits the opponent, even during Fly.", -1, 0, 3, new HitsTagAttr(BattlerTagType.FLYING)),
new AttackMove(Moves.SAND_TOMB, "Sand Tomb", Type.GROUND, MoveCategory.PHYSICAL, 35, 85, 15, -1, "Traps opponent, damaging them for 4-5 turns.", 100, 0, 3), new AttackMove(Moves.SAND_TOMB, "Sand Tomb", Type.GROUND, MoveCategory.PHYSICAL, 35, 85, 15, -1, "Traps opponent, damaging them for 4-5 turns.", 100, 0, 3),
new AttackMove(Moves.SHEER_COLD, "Sheer Cold", Type.ICE, MoveCategory.SPECIAL, -1, 30, 5, -1, "One-Hit-KO, if it hits.", -1, 0, 3), new AttackMove(Moves.SHEER_COLD, "Sheer Cold", Type.ICE, MoveCategory.SPECIAL, -1, 30, 5, -1, "One-Hit-KO, if it hits.", -1, 0, 3),
new AttackMove(Moves.MUDDY_WATER, "Muddy Water", Type.WATER, MoveCategory.SPECIAL, 90, 85, 10, -1, "May lower opponent's Accuracy.", 30, 0, 3, new StatChangeAttr(BattleStat.ACC, -1)), new AttackMove(Moves.MUDDY_WATER, "Muddy Water", Type.WATER, MoveCategory.SPECIAL, 90, 85, 10, -1, "May lower opponent's Accuracy.", 30, 0, 3, new StatChangeAttr(BattleStat.ACC, -1)),
@ -1768,18 +1919,18 @@ export const allMoves = [
new StatusMove(Moves.BLOCK, "Block", Type.NORMAL, -1, 5, -1, "Opponent cannot flee or switch.", -1, 0, 3), new StatusMove(Moves.BLOCK, "Block", Type.NORMAL, -1, 5, -1, "Opponent cannot flee or switch.", -1, 0, 3),
new SelfStatusMove(Moves.HOWL, "Howl", Type.NORMAL, -1, 40, -1, "Raises Attack of allies.", -1, 0, 3, new StatChangeAttr(BattleStat.ATK, 1, true)), // TODO new SelfStatusMove(Moves.HOWL, "Howl", Type.NORMAL, -1, 40, -1, "Raises Attack of allies.", -1, 0, 3, new StatChangeAttr(BattleStat.ATK, 1, true)), // TODO
new AttackMove(Moves.DRAGON_CLAW, "Dragon Claw", Type.DRAGON, MoveCategory.PHYSICAL, 80, 100, 15, 78, "", -1, 0, 3), new AttackMove(Moves.DRAGON_CLAW, "Dragon Claw", Type.DRAGON, MoveCategory.PHYSICAL, 80, 100, 15, 78, "", -1, 0, 3),
new AttackMove(Moves.FRENZY_PLANT, "Frenzy Plant", Type.GRASS, MoveCategory.SPECIAL, 150, 90, 5, 155, "User must recharge next turn.", -1, 0, 3, new AddTagAttr(BattleTagType.RECHARGING, true)), new AttackMove(Moves.FRENZY_PLANT, "Frenzy Plant", Type.GRASS, MoveCategory.SPECIAL, 150, 90, 5, 155, "User must recharge next turn.", -1, 0, 3, new AddBattlerTagAttr(BattlerTagType.RECHARGING, true)),
new SelfStatusMove(Moves.BULK_UP, "Bulk Up", Type.FIGHTING, -1, 20, 64, "Raises user's Attack and Defense.", -1, 0, 3, new SelfStatusMove(Moves.BULK_UP, "Bulk Up", Type.FIGHTING, -1, 20, 64, "Raises user's Attack and Defense.", -1, 0, 3,
new StatChangeAttr([ BattleStat.ATK, BattleStat.DEF ], 1, true)), new StatChangeAttr([ BattleStat.ATK, BattleStat.DEF ], 1, true)),
new AttackMove(Moves.BOUNCE, "Bounce", Type.FLYING, MoveCategory.PHYSICAL, 85, 85, 5, -1, "Springs up on first turn, attacks on second. May paralyze opponent.", 30, 0, 3, new AttackMove(Moves.BOUNCE, "Bounce", Type.FLYING, MoveCategory.PHYSICAL, 85, 85, 5, -1, "Springs up on first turn, attacks on second. May paralyze opponent.", 30, 0, 3,
new ChargeAttr(ChargeAnim.BOUNCE_CHARGING, 'sprang up!', BattleTagType.FLYING), new StatusEffectAttr(StatusEffect.PARALYSIS)), new ChargeAttr(ChargeAnim.BOUNCE_CHARGING, 'sprang up!', BattlerTagType.FLYING), new StatusEffectAttr(StatusEffect.PARALYSIS)),
new AttackMove(Moves.MUD_SHOT, "Mud Shot", Type.GROUND, MoveCategory.SPECIAL, 55, 95, 15, 35, "Lowers opponent's Speed.", 100, 0, 3, new StatChangeAttr(BattleStat.SPD, -1)), new AttackMove(Moves.MUD_SHOT, "Mud Shot", Type.GROUND, MoveCategory.SPECIAL, 55, 95, 15, 35, "Lowers opponent's Speed.", 100, 0, 3, new StatChangeAttr(BattleStat.SPD, -1)),
new AttackMove(Moves.POISON_TAIL, "Poison Tail", Type.POISON, MoveCategory.PHYSICAL, 50, 100, 25, 26, "High critical hit ratio. May poison opponent.", 10, 0, 3, new HighCritAttr(), new StatusEffectAttr(StatusEffect.POISON)), new AttackMove(Moves.POISON_TAIL, "Poison Tail", Type.POISON, MoveCategory.PHYSICAL, 50, 100, 25, 26, "High critical hit ratio. May poison opponent.", 10, 0, 3, new HighCritAttr(), new StatusEffectAttr(StatusEffect.POISON)),
new AttackMove(Moves.COVET, "Covet", Type.NORMAL, MoveCategory.PHYSICAL, 60, 100, 25, -1, "Opponent's item is stolen by the user.", -1, 0, 3), new AttackMove(Moves.COVET, "Covet", Type.NORMAL, MoveCategory.PHYSICAL, 60, 100, 25, -1, "Opponent's item is stolen by the user.", -1, 0, 3),
new AttackMove(Moves.VOLT_TACKLE, "Volt Tackle", Type.ELECTRIC, MoveCategory.PHYSICAL, 120, 100, 15, -1, "User receives recoil damage. May paralyze opponent.", 10, 0, 3, new AttackMove(Moves.VOLT_TACKLE, "Volt Tackle", Type.ELECTRIC, MoveCategory.PHYSICAL, 120, 100, 15, -1, "User receives recoil damage. May paralyze opponent.", 10, 0, 3,
new RecoilAttr(), new StatusEffectAttr(StatusEffect.PARALYSIS)), new RecoilAttr(), new StatusEffectAttr(StatusEffect.PARALYSIS)),
new AttackMove(Moves.MAGICAL_LEAF, "Magical Leaf", Type.GRASS, MoveCategory.SPECIAL, 60, -1, 20, 33, "Ignores Accuracy and Evasiveness.", -1, 0, 3), new AttackMove(Moves.MAGICAL_LEAF, "Magical Leaf", Type.GRASS, MoveCategory.SPECIAL, 60, -1, 20, 33, "Ignores Accuracy and Evasiveness.", -1, 0, 3),
new SelfStatusMove(Moves.WATER_SPORT, "Water Sport", Type.WATER, -1, 15, -1, "Weakens the power of Fire-type moves.", -1, 0, 3), new SelfStatusMove(Moves.WATER_SPORT, "Water Sport", Type.WATER, -1, 15, -1, "Weakens the power of Fire-type moves.", -1, 0, 3, new AddArenaTagAttr(ArenaTagType.WATER_SPORT, 5)),
new SelfStatusMove(Moves.CALM_MIND, "Calm Mind", Type.PSYCHIC, -1, 20, 129, "Raises user's Special Attack and Special Defense.", -1, 0, 3, new SelfStatusMove(Moves.CALM_MIND, "Calm Mind", Type.PSYCHIC, -1, 20, 129, "Raises user's Special Attack and Special Defense.", -1, 0, 3,
new StatChangeAttr([ BattleStat.SPATK, BattleStat.SPDEF ], 1, true)), new StatChangeAttr([ BattleStat.SPATK, BattleStat.SPDEF ], 1, true)),
new AttackMove(Moves.LEAF_BLADE, "Leaf Blade", Type.GRASS, MoveCategory.PHYSICAL, 90, 100, 15, -1, "High critical hit ratio.", -1, 0, 3, new HighCritAttr()), new AttackMove(Moves.LEAF_BLADE, "Leaf Blade", Type.GRASS, MoveCategory.PHYSICAL, 90, 100, 15, -1, "High critical hit ratio.", -1, 0, 3, new HighCritAttr()),
@ -1791,7 +1942,7 @@ export const allMoves = [
new AttackMove(Moves.DOOM_DESIRE, "Doom Desire", Type.STEEL, MoveCategory.SPECIAL, 140, 100, 5, -1, "Damage occurs 2 turns later.", -1, 0, 3, new AttackMove(Moves.DOOM_DESIRE, "Doom Desire", Type.STEEL, MoveCategory.SPECIAL, 140, 100, 5, -1, "Damage occurs 2 turns later.", -1, 0, 3,
new ChargeAttr(ChargeAnim.DOOM_DESIRE_CHARGING, 'chose\nDOOM DESIRE as its destiny!')), // Fix this move to work properly new ChargeAttr(ChargeAnim.DOOM_DESIRE_CHARGING, 'chose\nDOOM DESIRE as its destiny!')), // Fix this move to work properly
new AttackMove(Moves.PSYCHO_BOOST, "Psycho Boost", Type.PSYCHIC, MoveCategory.SPECIAL, 140, 90, 5, -1, "Sharply lowers user's Special Attack.", 100, 0, 3, new StatChangeAttr(BattleStat.SPATK, -2, true)), new AttackMove(Moves.PSYCHO_BOOST, "Psycho Boost", Type.PSYCHIC, MoveCategory.SPECIAL, 140, 90, 5, -1, "Sharply lowers user's Special Attack.", 100, 0, 3, new StatChangeAttr(BattleStat.SPATK, -2, true)),
new SelfStatusMove(Moves.ROOST, "Roost", Type.FLYING, -1, 5, -1, "User recovers half of its max HP and loses the Flying type temporarily.", -1, 0, 4, new HitHealAttr(), new AddTagAttr(BattleTagType.IGNORE_FLYING, true, 1)), new SelfStatusMove(Moves.ROOST, "Roost", Type.FLYING, -1, 5, -1, "User recovers half of its max HP and loses the Flying type temporarily.", -1, 0, 4, new HitHealAttr(), new AddBattlerTagAttr(BattlerTagType.IGNORE_FLYING, true, 1)),
new SelfStatusMove(Moves.GRAVITY, "Gravity", Type.PSYCHIC, -1, 5, -1, "Prevents moves like Fly and Bounce and the Ability Levitate for 5 turns.", -1, 0, 4), new SelfStatusMove(Moves.GRAVITY, "Gravity", Type.PSYCHIC, -1, 5, -1, "Prevents moves like Fly and Bounce and the Ability Levitate for 5 turns.", -1, 0, 4),
new StatusMove(Moves.MIRACLE_EYE, "Miracle Eye", Type.PSYCHIC, -1, 40, -1, "Resets opponent's Evasiveness, removes Dark's Psychic immunity.", -1, 0, 4), new StatusMove(Moves.MIRACLE_EYE, "Miracle Eye", Type.PSYCHIC, -1, 40, -1, "Resets opponent's Evasiveness, removes Dark's Psychic immunity.", -1, 0, 4),
new AttackMove(Moves.WAKE_UP_SLAP, "Wake-Up Slap", Type.FIGHTING, MoveCategory.PHYSICAL, 70, 100, 10, -1, "Power doubles if opponent is asleep, but wakes it up.", -1, 0, 4), new AttackMove(Moves.WAKE_UP_SLAP, "Wake-Up Slap", Type.FIGHTING, MoveCategory.PHYSICAL, 70, 100, 10, -1, "Power doubles if opponent is asleep, but wakes it up.", -1, 0, 4),
@ -1801,7 +1952,7 @@ export const allMoves = [
new AttackMove(Moves.BRINE, "Brine", Type.WATER, MoveCategory.SPECIAL, 65, 100, 10, -1, "Power doubles if opponent's HP is less than 50%.", -1, 0, 4, new MovePowerMultiplierAttr((user: Pokemon, target: Pokemon, move: Move) => target.getHpRatio() < 0.5 ? 2 : 1)), new AttackMove(Moves.BRINE, "Brine", Type.WATER, MoveCategory.SPECIAL, 65, 100, 10, -1, "Power doubles if opponent's HP is less than 50%.", -1, 0, 4, new MovePowerMultiplierAttr((user: Pokemon, target: Pokemon, move: Move) => target.getHpRatio() < 0.5 ? 2 : 1)),
new AttackMove(Moves.NATURAL_GIFT, "Natural Gift", Type.NORMAL, MoveCategory.PHYSICAL, -1, 100, 15, -1, "Power and type depend on the user's held berry.", -1, 0, 4), new AttackMove(Moves.NATURAL_GIFT, "Natural Gift", Type.NORMAL, MoveCategory.PHYSICAL, -1, 100, 15, -1, "Power and type depend on the user's held berry.", -1, 0, 4),
new AttackMove(Moves.FEINT, "Feint", Type.NORMAL, MoveCategory.PHYSICAL, 30, 100, 10, -1, "Only hits if opponent uses Protect or Detect in the same turn.", -1, 2, 4, new AttackMove(Moves.FEINT, "Feint", Type.NORMAL, MoveCategory.PHYSICAL, 30, 100, 10, -1, "Only hits if opponent uses Protect or Detect in the same turn.", -1, 2, 4,
new ConditionalMoveAttr((user: Pokemon, target: Pokemon, move: Move) => !!target.getTag(BattleTagType.PROTECTED))).ignoreProtect(), new ConditionalMoveAttr((user: Pokemon, target: Pokemon, move: Move) => !!target.getTag(BattlerTagType.PROTECTED))).ignoreProtect(),
new AttackMove(Moves.PLUCK, "Pluck", Type.FLYING, MoveCategory.PHYSICAL, 60, 100, 20, -1, "If the opponent is holding a berry, its effect is stolen by user.", -1, 0, 4), new AttackMove(Moves.PLUCK, "Pluck", Type.FLYING, MoveCategory.PHYSICAL, 60, 100, 20, -1, "If the opponent is holding a berry, its effect is stolen by user.", -1, 0, 4),
new SelfStatusMove(Moves.TAILWIND, "Tailwind", Type.FLYING, -1, 15, 113, "Doubles Speed for 4 turns.", -1, 0, 4), new SelfStatusMove(Moves.TAILWIND, "Tailwind", Type.FLYING, -1, 15, 113, "Doubles Speed for 4 turns.", -1, 0, 4),
new SelfStatusMove(Moves.ACUPRESSURE, "Acupressure", Type.NORMAL, -1, 30, -1, "Sharply raises a random stat.", -1, 0, 4, new StatChangeAttr(BattleStat.RAND, 2, true)), new SelfStatusMove(Moves.ACUPRESSURE, "Acupressure", Type.NORMAL, -1, 30, -1, "Sharply raises a random stat.", -1, 0, 4, new StatChangeAttr(BattleStat.RAND, 2, true)),
@ -1819,9 +1970,9 @@ export const allMoves = [
new AttackMove(Moves.WRING_OUT, "Wring Out", Type.NORMAL, MoveCategory.SPECIAL, -1, 100, 5, -1, "The higher the opponent's HP, the higher the damage.", -1, 0, 4), new AttackMove(Moves.WRING_OUT, "Wring Out", Type.NORMAL, MoveCategory.SPECIAL, -1, 100, 5, -1, "The higher the opponent's HP, the higher the damage.", -1, 0, 4),
new SelfStatusMove(Moves.POWER_TRICK, "Power Trick", Type.PSYCHIC, -1, 10, -1, "User's own Attack and Defense switch.", -1, 0, 4), new SelfStatusMove(Moves.POWER_TRICK, "Power Trick", Type.PSYCHIC, -1, 10, -1, "User's own Attack and Defense switch.", -1, 0, 4),
new StatusMove(Moves.GASTRO_ACID, "Gastro Acid", Type.POISON, 100, 10, -1, "Cancels out the effect of the opponent's Ability.", -1, 0, 4), new StatusMove(Moves.GASTRO_ACID, "Gastro Acid", Type.POISON, 100, 10, -1, "Cancels out the effect of the opponent's Ability.", -1, 0, 4),
new StatusMove(Moves.LUCKY_CHANT, "Lucky Chant", Type.NORMAL, -1, 30, -1, "Opponent cannot land critical hits for 5 turns.", -1, 0, 4, new AddTagAttr(BattleTagType.NO_CRIT, false, 5)), new StatusMove(Moves.LUCKY_CHANT, "Lucky Chant", Type.NORMAL, -1, 30, -1, "Opponent cannot land critical hits for 5 turns.", -1, 0, 4, new AddBattlerTagAttr(BattlerTagType.NO_CRIT, false, 5)),
new StatusMove(Moves.ME_FIRST, "Me First", Type.NORMAL, -1, 20, -1, "User copies the opponent's attack with 1.5× power.", -1, 0, 4), new StatusMove(Moves.ME_FIRST, "Me First", Type.NORMAL, -1, 20, -1, "User copies the opponent's attack with 1.5× power.", -1, 0, 4),
new SelfStatusMove(Moves.COPYCAT, "Copycat", Type.NORMAL, -1, 20, -1, "Copies opponent's last move.", -1, 0, 4), new SelfStatusMove(Moves.COPYCAT, "Copycat", Type.NORMAL, -1, 20, -1, "Copies opponent's last move.", -1, 0, 4, new ConditionalMoveAttr(targetMoveCopiableCondition), new CopyMoveAttr()),
new StatusMove(Moves.POWER_SWAP, "Power Swap", Type.PSYCHIC, -1, 10, -1, "User and opponent swap Attack and Special Attack.", -1, 0, 4), new StatusMove(Moves.POWER_SWAP, "Power Swap", Type.PSYCHIC, -1, 10, -1, "User and opponent swap Attack and Special Attack.", -1, 0, 4),
new StatusMove(Moves.GUARD_SWAP, "Guard Swap", Type.PSYCHIC, -1, 10, -1, "User and opponent swap Defense and Special Defense.", -1, 0, 4), new StatusMove(Moves.GUARD_SWAP, "Guard Swap", Type.PSYCHIC, -1, 10, -1, "User and opponent swap Defense and Special Defense.", -1, 0, 4),
new AttackMove(Moves.PUNISHMENT, "Punishment", Type.DARK, MoveCategory.PHYSICAL, -1, 100, 5, -1, "Power increases when opponent's stats have been raised.", -1, 0, 4), new AttackMove(Moves.PUNISHMENT, "Punishment", Type.DARK, MoveCategory.PHYSICAL, -1, 100, 5, -1, "Power increases when opponent's stats have been raised.", -1, 0, 4),
@ -1831,7 +1982,7 @@ export const allMoves = [
new StatusMove(Moves.TOXIC_SPIKES, "Toxic Spikes", Type.POISON, -1, 20, 91, "Poisons opponents when they switch into battle.", -1, 0, 4), new StatusMove(Moves.TOXIC_SPIKES, "Toxic Spikes", Type.POISON, -1, 20, 91, "Poisons opponents when they switch into battle.", -1, 0, 4),
new StatusMove(Moves.HEART_SWAP, "Heart Swap", Type.PSYCHIC, -1, 10, -1, "Stat changes are swapped with the opponent.", -1, 0, 4), new StatusMove(Moves.HEART_SWAP, "Heart Swap", Type.PSYCHIC, -1, 10, -1, "Stat changes are swapped with the opponent.", -1, 0, 4),
new SelfStatusMove(Moves.AQUA_RING, "Aqua Ring", Type.WATER, -1, 20, -1, "Restores a little HP each turn.", -1, 0, 4, new SelfStatusMove(Moves.AQUA_RING, "Aqua Ring", Type.WATER, -1, 20, -1, "Restores a little HP each turn.", -1, 0, 4,
new NoTagOverlapConditionalAttr(BattleTagType.INGRAIN, true), new AddTagAttr(BattleTagType.AQUA_RING, true)), new NoTagOverlapConditionalAttr(BattlerTagType.INGRAIN, true), new AddBattlerTagAttr(BattlerTagType.AQUA_RING, true)),
new SelfStatusMove(Moves.MAGNET_RISE, "Magnet Rise", Type.ELECTRIC, -1, 10, -1, "User becomes immune to Ground-type moves for 5 turns.", -1, 0, 4), new SelfStatusMove(Moves.MAGNET_RISE, "Magnet Rise", Type.ELECTRIC, -1, 10, -1, "User becomes immune to Ground-type moves for 5 turns.", -1, 0, 4),
new AttackMove(Moves.FLARE_BLITZ, "Flare Blitz", Type.FIRE, MoveCategory.PHYSICAL, 120, 100, 15, 165, "User receives recoil damage. May burn opponent.", 10, 0, 4, new AttackMove(Moves.FLARE_BLITZ, "Flare Blitz", Type.FIRE, MoveCategory.PHYSICAL, 120, 100, 15, 165, "User receives recoil damage. May burn opponent.", 10, 0, 4,
new RecoilAttr(), new StatusEffectAttr(StatusEffect.BURN)), new RecoilAttr(), new StatusEffectAttr(StatusEffect.BURN)),
@ -1856,7 +2007,7 @@ export const allMoves = [
new AttackMove(Moves.BRAVE_BIRD, "Brave Bird", Type.FLYING, MoveCategory.PHYSICAL, 120, 100, 15, 164, "User receives recoil damage.", -1, 0, 4, new RecoilAttr()), new AttackMove(Moves.BRAVE_BIRD, "Brave Bird", Type.FLYING, MoveCategory.PHYSICAL, 120, 100, 15, 164, "User receives recoil damage.", -1, 0, 4, new RecoilAttr()),
new AttackMove(Moves.EARTH_POWER, "Earth Power", Type.GROUND, MoveCategory.SPECIAL, 90, 100, 10, 133, "May lower opponent's Special Defense.", 10, 0, 4, new StatChangeAttr(BattleStat.SPDEF, -1)), new AttackMove(Moves.EARTH_POWER, "Earth Power", Type.GROUND, MoveCategory.SPECIAL, 90, 100, 10, 133, "May lower opponent's Special Defense.", 10, 0, 4, new StatChangeAttr(BattleStat.SPDEF, -1)),
new StatusMove(Moves.SWITCHEROO, "Switcheroo", Type.DARK, 100, 10, -1, "Swaps held items with the opponent.", -1, 0, 4), new StatusMove(Moves.SWITCHEROO, "Switcheroo", Type.DARK, 100, 10, -1, "Swaps held items with the opponent.", -1, 0, 4),
new AttackMove(Moves.GIGA_IMPACT, "Giga Impact", Type.NORMAL, MoveCategory.PHYSICAL, 150, 90, 5, 152, "User must recharge next turn.", -1, 0, 4, new AddTagAttr(BattleTagType.RECHARGING, true)), new AttackMove(Moves.GIGA_IMPACT, "Giga Impact", Type.NORMAL, MoveCategory.PHYSICAL, 150, 90, 5, 152, "User must recharge next turn.", -1, 0, 4, new AddBattlerTagAttr(BattlerTagType.RECHARGING, true)),
new SelfStatusMove(Moves.NASTY_PLOT, "Nasty Plot", Type.DARK, -1, 20, 140, "Sharply raises user's Special Attack.", -1, 0, 4, new StatChangeAttr(BattleStat.SPATK, 2, true)), new SelfStatusMove(Moves.NASTY_PLOT, "Nasty Plot", Type.DARK, -1, 20, 140, "Sharply raises user's Special Attack.", -1, 0, 4, new StatChangeAttr(BattleStat.SPATK, 2, true)),
new AttackMove(Moves.BULLET_PUNCH, "Bullet Punch", Type.STEEL, MoveCategory.PHYSICAL, 40, 100, 30, -1, "User attacks first.", -1, 1, 4), new AttackMove(Moves.BULLET_PUNCH, "Bullet Punch", Type.STEEL, MoveCategory.PHYSICAL, 40, 100, 30, -1, "User attacks first.", -1, 1, 4),
new AttackMove(Moves.AVALANCHE, "Avalanche", Type.ICE, MoveCategory.PHYSICAL, 60, 100, 10, 46, "Power doubles if user took damage first.", -1, -4, 4), new AttackMove(Moves.AVALANCHE, "Avalanche", Type.ICE, MoveCategory.PHYSICAL, 60, 100, 10, 46, "Power doubles if user took damage first.", -1, -4, 4),
@ -1879,7 +2030,7 @@ export const allMoves = [
new AttackMove(Moves.LAVA_PLUME, "Lava Plume", Type.FIRE, MoveCategory.SPECIAL, 80, 100, 15, -1, "May burn opponent.", 30, 0, 4, new StatusEffectAttr(StatusEffect.BURN)), new AttackMove(Moves.LAVA_PLUME, "Lava Plume", Type.FIRE, MoveCategory.SPECIAL, 80, 100, 15, -1, "May burn opponent.", 30, 0, 4, new StatusEffectAttr(StatusEffect.BURN)),
new AttackMove(Moves.LEAF_STORM, "Leaf Storm", Type.GRASS, MoveCategory.SPECIAL, 130, 90, 5, 159, "Sharply lowers user's Special Attack.", 100, 0, 4, new StatChangeAttr(BattleStat.SPATK, -2, true)), new AttackMove(Moves.LEAF_STORM, "Leaf Storm", Type.GRASS, MoveCategory.SPECIAL, 130, 90, 5, 159, "Sharply lowers user's Special Attack.", 100, 0, 4, new StatChangeAttr(BattleStat.SPATK, -2, true)),
new AttackMove(Moves.POWER_WHIP, "Power Whip", Type.GRASS, MoveCategory.PHYSICAL, 120, 85, 10, -1, "", -1, 0, 4), new AttackMove(Moves.POWER_WHIP, "Power Whip", Type.GRASS, MoveCategory.PHYSICAL, 120, 85, 10, -1, "", -1, 0, 4),
new AttackMove(Moves.ROCK_WRECKER, "Rock Wrecker", Type.ROCK, MoveCategory.PHYSICAL, 150, 90, 5, -1, "User must recharge next turn.", -1, 0, 4, new AddTagAttr(BattleTagType.RECHARGING, true)), new AttackMove(Moves.ROCK_WRECKER, "Rock Wrecker", Type.ROCK, MoveCategory.PHYSICAL, 150, 90, 5, -1, "User must recharge next turn.", -1, 0, 4, new AddBattlerTagAttr(BattlerTagType.RECHARGING, true)),
new AttackMove(Moves.CROSS_POISON, "Cross Poison", Type.POISON, MoveCategory.PHYSICAL, 70, 100, 20, -1, "High critical hit ratio. May poison opponent.", 10, 0, 4, new AttackMove(Moves.CROSS_POISON, "Cross Poison", Type.POISON, MoveCategory.PHYSICAL, 70, 100, 20, -1, "High critical hit ratio. May poison opponent.", 10, 0, 4,
new HighCritAttr(), new StatusEffectAttr(StatusEffect.POISON)), new HighCritAttr(), new StatusEffectAttr(StatusEffect.POISON)),
new AttackMove(Moves.GUNK_SHOT, "Gunk Shot", Type.POISON, MoveCategory.PHYSICAL, 120, 80, 5, 102, "May poison opponent.", 30, 0, 4, new StatusEffectAttr(StatusEffect.POISON)), new AttackMove(Moves.GUNK_SHOT, "Gunk Shot", Type.POISON, MoveCategory.PHYSICAL, 120, 80, 5, 102, "May poison opponent.", 30, 0, 4, new StatusEffectAttr(StatusEffect.POISON)),
@ -1901,7 +2052,7 @@ export const allMoves = [
new SelfStatusMove(Moves.HEAL_ORDER, "Heal Order", Type.BUG, -1, 10, -1, "User recovers half its max HP.", -1, 0, 4, new HealAttr(0.5)), new SelfStatusMove(Moves.HEAL_ORDER, "Heal Order", Type.BUG, -1, 10, -1, "User recovers half its max HP.", -1, 0, 4, new HealAttr(0.5)),
new AttackMove(Moves.HEAD_SMASH, "Head Smash", Type.ROCK, MoveCategory.PHYSICAL, 150, 80, 5, -1, "User receives recoil damage.", -1, 0, 4, new RecoilAttr()), new AttackMove(Moves.HEAD_SMASH, "Head Smash", Type.ROCK, MoveCategory.PHYSICAL, 150, 80, 5, -1, "User receives recoil damage.", -1, 0, 4, new RecoilAttr()),
new AttackMove(Moves.DOUBLE_HIT, "Double Hit", Type.NORMAL, MoveCategory.PHYSICAL, 35, 90, 10, -1, "Hits twice in one turn.", -1, 0, 4, new MultiHitAttr(MultiHitType._2)), new AttackMove(Moves.DOUBLE_HIT, "Double Hit", Type.NORMAL, MoveCategory.PHYSICAL, 35, 90, 10, -1, "Hits twice in one turn.", -1, 0, 4, new MultiHitAttr(MultiHitType._2)),
new AttackMove(Moves.ROAR_OF_TIME, "Roar of Time", Type.DRAGON, MoveCategory.SPECIAL, 150, 90, 5, -1, "User must recharge next turn.", -1, 0, 4, new AddTagAttr(BattleTagType.RECHARGING, true)), new AttackMove(Moves.ROAR_OF_TIME, "Roar of Time", Type.DRAGON, MoveCategory.SPECIAL, 150, 90, 5, -1, "User must recharge next turn.", -1, 0, 4, new AddBattlerTagAttr(BattlerTagType.RECHARGING, true)),
new AttackMove(Moves.SPACIAL_REND, "Spacial Rend", Type.DRAGON, MoveCategory.SPECIAL, 100, 95, 5, -1, "High critical hit ratio.", -1, 0, 4, new HighCritAttr()), new AttackMove(Moves.SPACIAL_REND, "Spacial Rend", Type.DRAGON, MoveCategory.SPECIAL, 100, 95, 5, -1, "High critical hit ratio.", -1, 0, 4, new HighCritAttr()),
new SelfStatusMove(Moves.LUNAR_DANCE, "Lunar Dance", Type.PSYCHIC, -1, 10, -1, "The user faints but the next Pokémon released is fully healed.", -1, 0, 4, new SacrificialAttr()), // TODO new SelfStatusMove(Moves.LUNAR_DANCE, "Lunar Dance", Type.PSYCHIC, -1, 10, -1, "The user faints but the next Pokémon released is fully healed.", -1, 0, 4, new SacrificialAttr()), // TODO
new AttackMove(Moves.CRUSH_GRIP, "Crush Grip", Type.NORMAL, MoveCategory.PHYSICAL, -1, 100, 5, -1, "More powerful when opponent has higher HP.", -1, 0, 4), new AttackMove(Moves.CRUSH_GRIP, "Crush Grip", Type.NORMAL, MoveCategory.PHYSICAL, -1, 100, 5, -1, "More powerful when opponent has higher HP.", -1, 0, 4),
@ -1954,16 +2105,17 @@ export const allMoves = [
new SelfStatusMove(Moves.SHELL_SMASH, "Shell Smash", Type.NORMAL, -1, 15, -1, "Sharply raises user's Attack, Special Attack and Speed but lowers Defense and Special Defense.", -1, 0, 5, new SelfStatusMove(Moves.SHELL_SMASH, "Shell Smash", Type.NORMAL, -1, 15, -1, "Sharply raises user's Attack, Special Attack and Speed but lowers Defense and Special Defense.", -1, 0, 5,
new StatChangeAttr([ BattleStat.ATK, BattleStat.SPATK ], 2, true), new StatChangeAttr([ BattleStat.DEF, BattleStat.SPDEF ], -1, true)), new StatChangeAttr([ BattleStat.ATK, BattleStat.SPATK ], 2, true), new StatChangeAttr([ BattleStat.DEF, BattleStat.SPDEF ], -1, true)),
new StatusMove(Moves.HEAL_PULSE, "Heal Pulse", Type.PSYCHIC, -1, 10, -1, "Restores half the target's max HP.", -1, 0, 5, new HealAttr(0.5, false, false)), new StatusMove(Moves.HEAL_PULSE, "Heal Pulse", Type.PSYCHIC, -1, 10, -1, "Restores half the target's max HP.", -1, 0, 5, new HealAttr(0.5, false, false)),
new AttackMove(Moves.HEX, "Hex", Type.GHOST, MoveCategory.SPECIAL, 65, 100, 10, 29, "Inflicts more damage if the target has a status condition.", -1, 0, 5), new AttackMove(Moves.HEX, "Hex", Type.GHOST, MoveCategory.SPECIAL, 65, 100, 10, 29, "Inflicts more damage if the target has a status condition.", -1, 0, 5,
new MovePowerMultiplierAttr((user: Pokemon, target: Pokemon, move: Move) => target.status ? 2 : 1)),
new AttackMove(Moves.SKY_DROP, "Sky Drop", Type.FLYING, MoveCategory.PHYSICAL, 60, 100, 10, -1, "Takes opponent into the air on first turn, drops them on second turn.", -1, 0, 5, new AttackMove(Moves.SKY_DROP, "Sky Drop", Type.FLYING, MoveCategory.PHYSICAL, 60, 100, 10, -1, "Takes opponent into the air on first turn, drops them on second turn.", -1, 0, 5,
new ChargeAttr(ChargeAnim.SKY_DROP_CHARGING, 'took {TARGET}\ninto the sky!', BattleTagType.FLYING)), // TODO: Add 2nd turn message new ChargeAttr(ChargeAnim.SKY_DROP_CHARGING, 'took {TARGET}\ninto the sky!', BattlerTagType.FLYING)), // TODO: Add 2nd turn message
new SelfStatusMove(Moves.SHIFT_GEAR, "Shift Gear", Type.STEEL, -1, 10, -1, "Raises user's Attack and sharply raises Speed.", -1, 0, 5, new SelfStatusMove(Moves.SHIFT_GEAR, "Shift Gear", Type.STEEL, -1, 10, -1, "Raises user's Attack and sharply raises Speed.", -1, 0, 5,
new StatChangeAttr(BattleStat.ATK, 1, true), new StatChangeAttr(BattleStat.SPD, 2, true)), new StatChangeAttr(BattleStat.ATK, 1, true), new StatChangeAttr(BattleStat.SPD, 2, true)),
new AttackMove(Moves.CIRCLE_THROW, "Circle Throw", Type.FIGHTING, MoveCategory.PHYSICAL, 60, 90, 10, -1, "In battles, the opponent switches. In the wild, the Pokémon runs.", -1, -6, 5), new AttackMove(Moves.CIRCLE_THROW, "Circle Throw", Type.FIGHTING, MoveCategory.PHYSICAL, 60, 90, 10, -1, "In battles, the opponent switches. In the wild, the Pokémon runs.", -1, -6, 5),
new AttackMove(Moves.INCINERATE, "Incinerate", Type.FIRE, MoveCategory.SPECIAL, 60, 100, 15, -1, "Destroys the target's held berry.", -1, 0, 5), new AttackMove(Moves.INCINERATE, "Incinerate", Type.FIRE, MoveCategory.SPECIAL, 60, 100, 15, -1, "Destroys the target's held berry.", -1, 0, 5),
new StatusMove(Moves.QUASH, "Quash", Type.DARK, 100, 15, -1, "Makes the target act last this turn.", -1, 0, 5), new StatusMove(Moves.QUASH, "Quash", Type.DARK, 100, 15, -1, "Makes the target act last this turn.", -1, 0, 5),
new AttackMove(Moves.ACROBATICS, "Acrobatics", Type.FLYING, MoveCategory.PHYSICAL, 55, 100, 15, 14, "Stronger when the user does not have a held item.", -1, 0, 5), new AttackMove(Moves.ACROBATICS, "Acrobatics", Type.FLYING, MoveCategory.PHYSICAL, 55, 100, 15, 14, "Stronger when the user does not have a held item.", -1, 0, 5),
new StatusMove(Moves.REFLECT_TYPE, "Reflect Type", Type.NORMAL, -1, 15, -1, "User becomes the target's type.", -1, 0, 5), new StatusMove(Moves.REFLECT_TYPE, "Reflect Type", Type.NORMAL, -1, 15, -1, "User becomes the target's type.", -1, 0, 5, new CopyTypeAttr()),
new AttackMove(Moves.RETALIATE, "Retaliate", Type.NORMAL, MoveCategory.PHYSICAL, 70, 100, 5, -1, "Inflicts double damage if a teammate fainted on the last turn.", -1, 0, 5), new AttackMove(Moves.RETALIATE, "Retaliate", Type.NORMAL, MoveCategory.PHYSICAL, 70, 100, 5, -1, "Inflicts double damage if a teammate fainted on the last turn.", -1, 0, 5),
new AttackMove(Moves.FINAL_GAMBIT, "Final Gambit", Type.FIGHTING, MoveCategory.SPECIAL, -1, 100, 5, -1, "Inflicts damage equal to the user's remaining HP. User faints.", -1, 0, 5, new UserHpDamageAttr(), new SacrificialAttr()), new AttackMove(Moves.FINAL_GAMBIT, "Final Gambit", Type.FIGHTING, MoveCategory.SPECIAL, -1, 100, 5, -1, "Inflicts damage equal to the user's remaining HP. User faints.", -1, 0, 5, new UserHpDamageAttr(), new SacrificialAttr()),
new StatusMove(Moves.BESTOW, "Bestow", Type.NORMAL, -1, 15, -1, "Gives the user's held item to the target.", -1, 0, 5).ignoreProtect(), new StatusMove(Moves.BESTOW, "Bestow", Type.NORMAL, -1, 15, -1, "Gives the user's held item to the target.", -1, 0, 5).ignoreProtect(),

View File

@ -17,10 +17,11 @@ import { tmSpecies } from './data/tms';
import { pokemonEvolutions, pokemonPrevolutions, SpeciesEvolution, SpeciesEvolutionCondition } from './data/pokemon-evolutions'; import { pokemonEvolutions, pokemonPrevolutions, SpeciesEvolution, SpeciesEvolutionCondition } from './data/pokemon-evolutions';
import { DamagePhase, FaintPhase, MessagePhase } from './battle-phases'; import { DamagePhase, FaintPhase, MessagePhase } from './battle-phases';
import { BattleStat } from './data/battle-stat'; import { BattleStat } from './data/battle-stat';
import { BattleTag, BattleTagLapseType, BattleTagType, getBattleTag } from './data/battle-tag'; import { BattlerTag, BattlerTagLapseType, BattlerTagType, getBattlerTag } from './data/battler-tag';
import { Species } from './data/species'; import { Species } from './data/species';
import { WeatherType } from './data/weather'; import { WeatherType } from './data/weather';
import { TempBattleStat } from './data/temp-battle-stat'; import { TempBattleStat } from './data/temp-battle-stat';
import { ArenaTagType, WeakenTypeTag as WeakenMoveTypeTag } from './data/arena-tag';
export default abstract class Pokemon extends Phaser.GameObjects.Container { export default abstract class Pokemon extends Phaser.GameObjects.Container {
public id: integer; public id: integer;
@ -316,21 +317,28 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
} }
getTypes(): Type[] { getTypes(): Type[] {
const speciesForm = this.getSpeciesForm(); const types = [];
const speciesTypes = [ speciesForm.type1 ];
if (speciesForm.type2 !== null)
speciesTypes.push(speciesForm.type1);
if (this.getTag(BattleTagType.IGNORE_FLYING)) { if (this.summonData.types)
const flyingIndex = speciesTypes.indexOf(Type.FLYING); this.summonData.types.forEach(t => types.push(t));
if (flyingIndex > -1) else {
speciesTypes.splice(flyingIndex, 1); const speciesForm = this.getSpeciesForm();
types.push(speciesForm.type1);
if (speciesForm.type2 !== null)
types.push(speciesForm.type1);
} }
if (!speciesTypes.length) if (this.getTag(BattlerTagType.IGNORE_FLYING)) {
speciesTypes.push(Type.NORMAL); const flyingIndex = types.indexOf(Type.FLYING);
if (flyingIndex > -1)
types.splice(flyingIndex, 1);
}
return speciesTypes; if (!types.length)
types.push(Type.NORMAL);
return types;
} }
getEvolution(): SpeciesEvolution { getEvolution(): SpeciesEvolution {
@ -466,12 +474,13 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
const typeMultiplier = getTypeDamageMultiplier(move.type, this.getSpeciesForm().type1) * (this.getSpeciesForm().type2 !== null ? getTypeDamageMultiplier(move.type, this.getSpeciesForm().type2) : 1); const typeMultiplier = getTypeDamageMultiplier(move.type, this.getSpeciesForm().type1) * (this.getSpeciesForm().type2 !== null ? getTypeDamageMultiplier(move.type, this.getSpeciesForm().type2) : 1);
const weatherTypeMultiplier = this.scene.arena.getAttackTypeMultiplier(move.type); const weatherTypeMultiplier = this.scene.arena.getAttackTypeMultiplier(move.type);
applyMoveAttrs(VariablePowerAttr, source, this, move, power); applyMoveAttrs(VariablePowerAttr, source, this, move, power);
this.scene.arena.applyTags(WeakenMoveTypeTag, move.type, power);
this.scene.applyModifiers(AttackTypeBoosterModifier, source.isPlayer(), source, power); this.scene.applyModifiers(AttackTypeBoosterModifier, source.isPlayer(), source, power);
const critLevel = new Utils.IntegerHolder(0); const critLevel = new Utils.IntegerHolder(0);
applyMoveAttrs(HighCritAttr, source, this, move, critLevel); applyMoveAttrs(HighCritAttr, source, this, move, critLevel);
this.scene.applyModifiers(TempBattleStatBoosterModifier, source.isPlayer(), TempBattleStat.CRIT, critLevel); this.scene.applyModifiers(TempBattleStatBoosterModifier, source.isPlayer(), TempBattleStat.CRIT, critLevel);
const critChance = Math.ceil(16 / Math.pow(2, critLevel.value)); const critChance = Math.ceil(16 / Math.pow(2, critLevel.value));
let isCritical = !source.getTag(BattleTagType.NO_CRIT) && (critChance === 1 || !Utils.randInt(critChance)); let isCritical = !source.getTag(BattlerTagType.NO_CRIT) && (critChance === 1 || !Utils.randInt(critChance));
const sourceAtk = source.getBattleStat(isPhysical ? Stat.ATK : Stat.SPATK); const sourceAtk = source.getBattleStat(isPhysical ? Stat.ATK : Stat.SPATK);
const targetDef = this.getBattleStat(isPhysical ? Stat.DEF : Stat.SPDEF); const targetDef = this.getBattleStat(isPhysical ? Stat.DEF : Stat.SPDEF);
const stabMultiplier = source.species.type1 === move.type || (source.species.type2 !== null && source.species.type2 === move.type) ? 1.5 : 1; const stabMultiplier = source.species.type1 === move.type || (source.species.type2 !== null && source.species.type2 === move.type) ? 1.5 : 1;
@ -510,18 +519,18 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
source.turnData.damageDealt += damage; source.turnData.damageDealt += damage;
this.scene.unshiftPhase(new DamagePhase(this.scene, this.isPlayer(), result as DamageResult)) this.scene.unshiftPhase(new DamagePhase(this.scene, this.isPlayer(), result as DamageResult))
if (isCritical) if (isCritical)
this.scene.unshiftPhase(new MessagePhase(this.scene, 'A critical hit!')); this.scene.queueMessage('A critical hit!');
} }
switch (result) { switch (result) {
case MoveResult.SUPER_EFFECTIVE: case MoveResult.SUPER_EFFECTIVE:
this.scene.unshiftPhase(new MessagePhase(this.scene, 'It\'s super effective!')); this.scene.queueMessage('It\'s super effective!');
break; break;
case MoveResult.NOT_VERY_EFFECTIVE: case MoveResult.NOT_VERY_EFFECTIVE:
this.scene.unshiftPhase(new MessagePhase(this.scene, 'It\'s not very effective!')); this.scene.queueMessage('It\'s not very effective!');
break; break;
case MoveResult.NO_EFFECT: case MoveResult.NO_EFFECT:
this.scene.unshiftPhase(new MessagePhase(this.scene, `It doesn\'t affect ${this.name}!`)); this.scene.queueMessage(`It doesn\'t affect ${this.name}!`);
break; break;
} }
break; break;
@ -545,51 +554,53 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
} }
} }
addTag(tagType: BattleTagType, turnCount?: integer): boolean { addTag(tagType: BattlerTagType, turnCount?: integer): boolean {
const existingTag = this.getTag(tagType); const existingTag = this.getTag(tagType);
if (existingTag) { if (existingTag) {
existingTag.onOverlap(this); existingTag.onOverlap(this);
return false; return false;
} }
const newTag = getBattleTag(tagType, turnCount || 0); const newTag = getBattlerTag(tagType, turnCount || 0);
this.summonData.tags.push(newTag); this.summonData.tags.push(newTag);
newTag.onAdd(this); newTag.onAdd(this);
return true;
} }
getTag(tagType: BattleTagType | { new(...args: any[]): BattleTag }): BattleTag { getTag(tagType: BattlerTagType | { new(...args: any[]): BattlerTag }): BattlerTag {
return typeof(tagType) === 'number' return typeof(tagType) === 'number'
? this.summonData.tags.find(t => t.tagType === tagType) ? this.summonData.tags.find(t => t.tagType === tagType)
: this.summonData.tags.find(t => t instanceof tagType); : this.summonData.tags.find(t => t instanceof tagType);
} }
findTag(tagFilter: ((tag: BattleTag) => boolean)) { findTag(tagFilter: ((tag: BattlerTag) => boolean)) {
return this.summonData.tags.find(t => tagFilter(t)); return this.summonData.tags.find(t => tagFilter(t));
} }
getTags(tagType: BattleTagType | { new(...args: any[]): BattleTag }): BattleTag[] { getTags(tagType: BattlerTagType | { new(...args: any[]): BattlerTag }): BattlerTag[] {
return typeof(tagType) === 'number' return typeof(tagType) === 'number'
? this.summonData.tags.filter(t => t.tagType === tagType) ? this.summonData.tags.filter(t => t.tagType === tagType)
: this.summonData.tags.filter(t => t instanceof tagType); : this.summonData.tags.filter(t => t instanceof tagType);
} }
findTags(tagFilter: ((tag: BattleTag) => boolean)) { findTags(tagFilter: ((tag: BattlerTag) => boolean)) {
return this.summonData.tags.filter(t => tagFilter(t)); return this.summonData.tags.filter(t => tagFilter(t));
} }
lapseTag(tagType: BattleTagType): boolean { lapseTag(tagType: BattlerTagType): boolean {
const tags = this.summonData.tags; const tags = this.summonData.tags;
const tag = tags.find(t => t.tagType === tagType); const tag = tags.find(t => t.tagType === tagType);
if (tag && !(tag.lapse(this, BattleTagLapseType.CUSTOM))) { if (tag && !(tag.lapse(this, BattlerTagLapseType.CUSTOM))) {
tag.onRemove(this); tag.onRemove(this);
tags.splice(tags.indexOf(tag), 1); tags.splice(tags.indexOf(tag), 1);
} }
return !!tag; return !!tag;
} }
lapseTags(lapseType: BattleTagLapseType): void { lapseTags(lapseType: BattlerTagLapseType): void {
const tags = this.summonData.tags; const tags = this.summonData.tags;
tags.filter(t => lapseType === BattleTagLapseType.FAINT || ((t.lapseType === lapseType) && !(t.lapse(this, lapseType))) || (lapseType === BattleTagLapseType.TURN_END && t.turnCount < 1)).forEach(t => { tags.filter(t => lapseType === BattlerTagLapseType.FAINT || ((t.lapseType === lapseType) && !(t.lapse(this, lapseType))) || (lapseType === BattlerTagLapseType.TURN_END && t.turnCount < 1)).forEach(t => {
t.onRemove(this); t.onRemove(this);
tags.splice(tags.indexOf(t), 1); tags.splice(tags.indexOf(t), 1);
}); });
@ -699,8 +710,8 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
const lastStatus = this.status.effect; const lastStatus = this.status.effect;
this.status = undefined; this.status = undefined;
if (lastStatus === StatusEffect.SLEEP) { if (lastStatus === StatusEffect.SLEEP) {
if (this.getTag(BattleTagType.NIGHTMARE)) if (this.getTag(BattlerTagType.NIGHTMARE))
this.lapseTag(BattleTagType.NIGHTMARE); this.lapseTag(BattlerTagType.NIGHTMARE);
} }
} }
@ -711,8 +722,8 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
resetBattleSummonData(): void { resetBattleSummonData(): void {
this.battleSummonData = new PokemonBattleSummonData(); this.battleSummonData = new PokemonBattleSummonData();
if (this.getTag(BattleTagType.SEEDED)) if (this.getTag(BattlerTagType.SEEDED))
this.lapseTag(BattleTagType.SEEDED); this.lapseTag(BattlerTagType.SEEDED);
} }
resetTurnData(): void { resetTurnData(): void {
@ -1001,7 +1012,8 @@ export class PokemonSummonData {
public battleStats: integer[] = [ 0, 0, 0, 0, 0, 0, 0 ]; public battleStats: integer[] = [ 0, 0, 0, 0, 0, 0, 0 ];
public moveHistory: TurnMove[] = []; public moveHistory: TurnMove[] = [];
public moveQueue: QueuedMove[] = []; public moveQueue: QueuedMove[] = [];
public tags: BattleTag[] = []; public tags: BattlerTag[] = [];
public types: Type[];
} }
export class PokemonBattleSummonData { export class PokemonBattleSummonData {