Add more move implementations
parent
355bfc76bf
commit
77999671b2
98
src/arena.ts
98
src/arena.ts
|
@ -9,11 +9,13 @@ import { CommonAnimPhase, MessagePhase } from "./battle-phases";
|
|||
import { CommonAnim } from "./data/battle-anims";
|
||||
import { Type } from "./data/type";
|
||||
import Move from "./data/move";
|
||||
import { ArenaTag, ArenaTagType, getArenaTag } from "./data/arena-tag";
|
||||
|
||||
export class Arena {
|
||||
private scene: BattleScene;
|
||||
public scene: BattleScene;
|
||||
public biomeType: Biome;
|
||||
public weather: Weather;
|
||||
public tags: ArenaTag[];
|
||||
private bgm: string;
|
||||
|
||||
private pokemonPool: BiomeTierPools;
|
||||
|
@ -21,6 +23,7 @@ export class Arena {
|
|||
constructor(scene: BattleScene, biome: Biome, bgm: string) {
|
||||
this.scene = scene;
|
||||
this.biomeType = biome;
|
||||
this.tags = [];
|
||||
this.bgm = bgm;
|
||||
this.pokemonPool = biomePools[biome];
|
||||
}
|
||||
|
@ -122,6 +125,52 @@ export class Arena {
|
|||
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 {
|
||||
if (this.weather?.weatherType === (weather || undefined))
|
||||
return false;
|
||||
|
@ -131,23 +180,23 @@ export class Arena {
|
|||
|
||||
if (this.weather) {
|
||||
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
|
||||
this.scene.unshiftPhase(new MessagePhase(this.scene, getWeatherClearMessage(oldWeatherType)));
|
||||
this.scene.queueMessage(getWeatherClearMessage(oldWeatherType));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
isMoveWeatherCancelled(move: Move) {
|
||||
return this.weather && this.weather.isMoveWeatherCancelled(move);
|
||||
}
|
||||
|
||||
getAttackTypeMultiplier(attackType: Type): number {
|
||||
if (!this.weather)
|
||||
return 1;
|
||||
return this.weather.getAttackTypeMultiplier(attackType);
|
||||
}
|
||||
|
||||
isMoveWeatherCancelled(move: Move) {
|
||||
return this.weather && this.weather.isMoveWeatherCancelled(move);
|
||||
}
|
||||
|
||||
isDaytime(): boolean {
|
||||
switch (this.biomeType) {
|
||||
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 {
|
||||
this.scene.loadBgm(this.bgm);
|
||||
}
|
||||
|
|
|
@ -18,7 +18,7 @@ import { BattleStat, getBattleStatLevelChangeDescription, getBattleStatName } fr
|
|||
import { Biome, biomeLinks } from "./data/biome";
|
||||
import { ModifierTypeOption, PokemonModifierType, PokemonMoveModifierType, getPlayerModifierTypeOptionsForWave, regenerateModifierPoolThresholds } from "./modifier/modifier-type";
|
||||
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 { Starter } from "./ui/starter-select-ui-handler";
|
||||
import { Gender } from "./data/gender";
|
||||
|
@ -416,7 +416,7 @@ export class CheckSwitchPhase extends BattlePhase {
|
|||
return;
|
||||
}
|
||||
|
||||
if (this.scene.getPlayerPokemon().getTag(BattleTagType.FRENZY)) {
|
||||
if (this.scene.getPlayerPokemon().getTag(BattlerTagType.FRENZY)) {
|
||||
super.end();
|
||||
return;
|
||||
}
|
||||
|
@ -596,6 +596,8 @@ export class TurnEndPhase extends BattlePhase {
|
|||
start() {
|
||||
super.start();
|
||||
|
||||
this.scene.currentBattle.incrementTurn();
|
||||
|
||||
const playerPokemon = this.scene.getPlayerPokemon();
|
||||
const enemyPokemon = this.scene.getEnemyPokemon();
|
||||
|
||||
|
@ -603,7 +605,7 @@ export class TurnEndPhase extends BattlePhase {
|
|||
if (!pokemon || !pokemon.hp)
|
||||
return;
|
||||
|
||||
pokemon.lapseTags(BattleTagLapseType.TURN_END);
|
||||
pokemon.lapseTags(BattlerTagLapseType.TURN_END);
|
||||
|
||||
const disabledMoves = pokemon.moveset.filter(m => m.isDisabled());
|
||||
for (let dm of disabledMoves) {
|
||||
|
@ -630,6 +632,8 @@ export class TurnEndPhase extends BattlePhase {
|
|||
handlePokemon(enemyPokemon);
|
||||
if (isDelayed)
|
||||
handlePokemon(playerPokemon);
|
||||
|
||||
this.scene.arena.lapseTags();
|
||||
|
||||
if (this.scene.arena.weather && !this.scene.arena.weather.lapse())
|
||||
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();
|
||||
|
||||
if (!this.followUp && this.canMove())
|
||||
this.pokemon.lapseTags(BattleTagLapseType.MOVE);
|
||||
this.pokemon.lapseTags(BattlerTagLapseType.MOVE);
|
||||
|
||||
const doMove = () => {
|
||||
const moveQueue = this.pokemon.getMoveQueue();
|
||||
|
@ -751,7 +755,7 @@ export abstract class MovePhase extends BattlePhase {
|
|||
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)
|
||||
this.move.ppUsed++;
|
||||
|
||||
|
@ -761,7 +765,7 @@ export abstract class MovePhase extends BattlePhase {
|
|||
failed.value = true;
|
||||
if (failed.value) {
|
||||
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
|
||||
this.scene.unshiftPhase(this.getEffectPhase());
|
||||
|
||||
|
@ -770,7 +774,7 @@ export abstract class MovePhase extends BattlePhase {
|
|||
|
||||
if (!this.canMove()) {
|
||||
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();
|
||||
return;
|
||||
}
|
||||
|
@ -790,7 +794,7 @@ export abstract class MovePhase extends BattlePhase {
|
|||
case StatusEffect.SLEEP:
|
||||
applyMoveAttrs(BypassSleepAttr, this.pokemon, target, this.move.getMove());
|
||||
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;
|
||||
break;
|
||||
case StatusEffect.FREEZE:
|
||||
|
@ -800,14 +804,12 @@ export abstract class MovePhase extends BattlePhase {
|
|||
break;
|
||||
}
|
||||
if (activated) {
|
||||
this.scene.unshiftPhase(new MessagePhase(this.scene,
|
||||
getPokemonMessage(this.pokemon, getStatusEffectActivationText(this.pokemon.status.effect))));
|
||||
this.scene.queueMessage(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)));
|
||||
doMove();
|
||||
} else {
|
||||
if (healed) {
|
||||
this.scene.unshiftPhase(new MessagePhase(this.scene,
|
||||
getPokemonMessage(this.pokemon, getStatusEffectHealText(this.pokemon.status.effect))));
|
||||
this.scene.queueMessage(getPokemonMessage(this.pokemon, getStatusEffectHealText(this.pokemon.status.effect)));
|
||||
this.pokemon.resetStatus();
|
||||
this.pokemon.updateInfo();
|
||||
}
|
||||
|
@ -869,7 +871,7 @@ abstract class MoveEffectPhase extends PokemonPhase {
|
|||
return;
|
||||
}
|
||||
|
||||
user.lapseTags(BattleTagLapseType.MOVE_EFFECT);
|
||||
user.lapseTags(BattlerTagLapseType.MOVE_EFFECT);
|
||||
|
||||
if (user.turnData.hitsLeft === undefined) {
|
||||
const hitCount = new Utils.IntegerHolder(1);
|
||||
|
@ -879,14 +881,14 @@ abstract class MoveEffectPhase extends PokemonPhase {
|
|||
}
|
||||
|
||||
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 });
|
||||
applyMoveAttrs(MissEffectAttr, user, target, this.move.getMove());
|
||||
this.end();
|
||||
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, () => {
|
||||
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());
|
||||
else {
|
||||
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);
|
||||
}
|
||||
|
||||
|
@ -1001,7 +1003,7 @@ export class MoveEndPhase extends PokemonPhase {
|
|||
start() {
|
||||
super.start();
|
||||
|
||||
this.getPokemon().lapseTags(BattleTagLapseType.AFTER_MOVE);
|
||||
this.getPokemon().lapseTags(BattlerTagLapseType.AFTER_MOVE);
|
||||
|
||||
this.end();
|
||||
}
|
||||
|
@ -1064,7 +1066,7 @@ export class StatChangePhase extends PokemonPhase {
|
|||
const end = () => {
|
||||
const messages = this.getStatChangeMessages(relLevels);
|
||||
for (let message of messages)
|
||||
this.scene.unshiftPhase(new MessagePhase(this.scene, message));
|
||||
this.scene.queueMessage(message);
|
||||
|
||||
for (let stat of this.stats)
|
||||
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() {
|
||||
if (this.weather.isDamaging()) {
|
||||
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));
|
||||
this.scene.unshiftPhase(new DamagePhase(this.scene, pokemon.isPlayer()));
|
||||
};
|
||||
|
@ -1179,7 +1181,7 @@ export class ObtainStatusEffectPhase extends PokemonPhase {
|
|||
pokemon.status.cureTurn = this.cureTurn;
|
||||
pokemon.updateInfo(true);
|
||||
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())
|
||||
this.scene.pushPhase(new PostTurnStatusEffectPhase(this.scene, this.player));
|
||||
this.end();
|
||||
|
@ -1187,7 +1189,7 @@ export class ObtainStatusEffectPhase extends PokemonPhase {
|
|||
return;
|
||||
}
|
||||
} 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();
|
||||
}
|
||||
}
|
||||
|
@ -1202,8 +1204,7 @@ export class PostTurnStatusEffectPhase extends PokemonPhase {
|
|||
if (pokemon?.hp && pokemon.status && pokemon.status.isPostTurn()) {
|
||||
pokemon.status.incrementTurn();
|
||||
new CommonBattleAnim(CommonAnim.POISON + (pokemon.status.effect - 1), pokemon).play(this.scene, () => {
|
||||
this.scene.unshiftPhase(new MessagePhase(this.scene,
|
||||
getPokemonMessage(pokemon, getStatusEffectActivationText(pokemon.status.effect))));
|
||||
this.scene.queueMessage(getPokemonMessage(pokemon, getStatusEffectActivationText(pokemon.status.effect)));
|
||||
switch (pokemon.status.effect) {
|
||||
case StatusEffect.POISON:
|
||||
case StatusEffect.BURN:
|
||||
|
@ -1288,7 +1289,7 @@ export class FaintPhase extends PokemonPhase {
|
|||
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)
|
||||
this.scene.unshiftPhase(new SwitchPhase(this.scene, true, false));
|
||||
|
@ -1297,7 +1298,7 @@ export class FaintPhase extends PokemonPhase {
|
|||
|
||||
const pokemon = this.getPokemon();
|
||||
|
||||
pokemon.lapseTags(BattleTagLapseType.FAINT);
|
||||
pokemon.lapseTags(BattlerTagLapseType.FAINT);
|
||||
|
||||
pokemon.faintCry(() => {
|
||||
pokemon.hideInfo();
|
||||
|
@ -1628,7 +1629,7 @@ export class PokemonHealPhase extends CommonAnimPhase {
|
|||
this.message = getPokemonMessage(pokemon, `'s\nHP is full!`);
|
||||
|
||||
if (this.message)
|
||||
this.scene.unshiftPhase(new MessagePhase(this.scene, this.message));
|
||||
this.scene.queueMessage(this.message);
|
||||
|
||||
if (fullHp)
|
||||
super.end();
|
||||
|
@ -1852,7 +1853,8 @@ export class SelectModifierPhase extends BattlePhase {
|
|||
} else
|
||||
this.scene.ui.setMode(Mode.MODIFIER_SELECT, typeOptions, modifierSelectCallback);
|
||||
});
|
||||
}
|
||||
} else
|
||||
this.scene.ui.setMode(Mode.MODIFIER_SELECT, typeOptions, modifierSelectCallback);
|
||||
}, PartyUiHandler.FilterItemMaxStacks);
|
||||
return;
|
||||
}
|
||||
|
|
|
@ -608,6 +608,10 @@ export default class BattleScene extends Phaser.Scene {
|
|||
this.currentPhase.start();
|
||||
}
|
||||
|
||||
queueMessage(message: string, callbackDelay?: integer, prompt?: boolean) {
|
||||
this.unshiftPhase(new MessagePhase(this, message, callbackDelay, prompt));
|
||||
}
|
||||
|
||||
populatePhaseQueue(): void {
|
||||
this.phaseQueue.push(new CommandPhase(this));
|
||||
}
|
||||
|
@ -622,7 +626,7 @@ export default class BattleScene extends Phaser.Scene {
|
|||
} else if (!virtual) {
|
||||
const defaultModifierType = getDefaultModifierTypeForTier(modifier.type.tier);
|
||||
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;
|
||||
}
|
||||
|
||||
|
|
|
@ -5,11 +5,13 @@ export class Battle {
|
|||
public waveIndex: integer;
|
||||
public enemyLevel: integer;
|
||||
public enemyPokemon: EnemyPokemon;
|
||||
public turn: integer;
|
||||
public playerParticipantIds: Set<integer> = new Set<integer>();
|
||||
|
||||
constructor(waveIndex: integer) {
|
||||
this.waveIndex = waveIndex;
|
||||
this.enemyLevel = this.getLevelForWave();
|
||||
this.turn = 1;
|
||||
}
|
||||
|
||||
private getLevelForWave(): number {
|
||||
|
@ -23,6 +25,10 @@ export class Battle {
|
|||
return Math.max(Math.round(baseLevel + Math.abs(Utils.randGauss(deviation))), 1);
|
||||
}
|
||||
|
||||
incrementTurn() {
|
||||
this.turn++;
|
||||
}
|
||||
|
||||
addParticipant(playerPokemon: PlayerPokemon): void {
|
||||
this.playerParticipantIds.add(playerPokemon.id);
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -7,7 +7,7 @@ import { StatusEffect } from "./status-effect";
|
|||
import * as Utils from "../utils";
|
||||
import { Moves } from "./move";
|
||||
|
||||
export enum BattleTagType {
|
||||
export enum BattlerTagType {
|
||||
NONE,
|
||||
RECHARGING,
|
||||
FLINCHED,
|
||||
|
@ -26,7 +26,7 @@ export enum BattleTagType {
|
|||
IGNORE_FLYING
|
||||
}
|
||||
|
||||
export enum BattleTagLapseType {
|
||||
export enum BattlerTagLapseType {
|
||||
FAINT,
|
||||
MOVE,
|
||||
AFTER_MOVE,
|
||||
|
@ -35,12 +35,12 @@ export enum BattleTagLapseType {
|
|||
CUSTOM
|
||||
}
|
||||
|
||||
export class BattleTag {
|
||||
public tagType: BattleTagType;
|
||||
public lapseType: BattleTagLapseType;
|
||||
export class BattlerTag {
|
||||
public tagType: BattlerTagType;
|
||||
public lapseType: BattlerTagLapseType;
|
||||
public turnCount: integer;
|
||||
|
||||
constructor(tagType: BattleTagType, lapseType: BattleTagLapseType, turnCount: integer) {
|
||||
constructor(tagType: BattlerTagType, lapseType: BattlerTagLapseType, turnCount: integer) {
|
||||
this.tagType = tagType;
|
||||
this.lapseType = lapseType;
|
||||
this.turnCount = turnCount;
|
||||
|
@ -52,14 +52,14 @@ export class BattleTag {
|
|||
|
||||
onOverlap(pokemon: Pokemon): void { }
|
||||
|
||||
lapse(pokemon: Pokemon, lapseType: BattleTagLapseType): boolean {
|
||||
lapse(pokemon: Pokemon, lapseType: BattlerTagLapseType): boolean {
|
||||
return --this.turnCount > 0;
|
||||
}
|
||||
}
|
||||
|
||||
export class RechargingTag extends BattleTag {
|
||||
export class RechargingTag extends BattlerTag {
|
||||
constructor() {
|
||||
super(BattleTagType.RECHARGING, BattleTagLapseType.MOVE, 1);
|
||||
super(BattlerTagType.RECHARGING, BattlerTagLapseType.MOVE, 1);
|
||||
}
|
||||
|
||||
onAdd(pokemon: Pokemon): void {
|
||||
|
@ -68,66 +68,66 @@ export class RechargingTag extends BattleTag {
|
|||
pokemon.getMoveQueue().push({ move: Moves.NONE })
|
||||
}
|
||||
|
||||
lapse(pokemon: Pokemon, lapseType: BattleTagLapseType): boolean {
|
||||
lapse(pokemon: Pokemon, lapseType: BattlerTagLapseType): boolean {
|
||||
super.lapse(pokemon, lapseType);
|
||||
|
||||
(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;
|
||||
}
|
||||
}
|
||||
|
||||
export class FlinchedTag extends BattleTag {
|
||||
export class FlinchedTag extends BattlerTag {
|
||||
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);
|
||||
|
||||
(pokemon.scene.getCurrentPhase() as MovePhase).cancel();
|
||||
pokemon.scene.unshiftPhase(new MessagePhase(pokemon.scene, getPokemonMessage(pokemon, ' flinched!')));
|
||||
pokemon.scene.queueMessage(getPokemonMessage(pokemon, ' flinched!'));
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
export class PseudoStatusTag extends BattleTag {
|
||||
constructor(tagType: BattleTagType, lapseType: BattleTagLapseType, turnCount: integer) {
|
||||
export class PseudoStatusTag extends BattlerTag {
|
||||
constructor(tagType: BattlerTagType, lapseType: BattlerTagLapseType, turnCount: integer) {
|
||||
super(tagType, lapseType, turnCount);
|
||||
}
|
||||
}
|
||||
|
||||
export class ConfusedTag extends PseudoStatusTag {
|
||||
constructor(turnCount: integer) {
|
||||
super(BattleTagType.CONFUSED, BattleTagLapseType.MOVE, turnCount);
|
||||
super(BattlerTagType.CONFUSED, BattlerTagLapseType.MOVE, turnCount);
|
||||
}
|
||||
|
||||
onAdd(pokemon: Pokemon): void {
|
||||
super.onAdd(pokemon);
|
||||
|
||||
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 {
|
||||
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 {
|
||||
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 {
|
||||
const ret = lapseType !== BattleTagLapseType.CUSTOM && super.lapse(pokemon, lapseType);
|
||||
lapse(pokemon: Pokemon, lapseType: BattlerTagLapseType): boolean {
|
||||
const ret = lapseType !== BattlerTagLapseType.CUSTOM && super.lapse(pokemon, lapseType);
|
||||
|
||||
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));
|
||||
|
||||
if (Utils.randInt(2)) {
|
||||
|
@ -135,7 +135,7 @@ export class ConfusedTag extends PseudoStatusTag {
|
|||
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));
|
||||
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.getCurrentPhase() as MovePhase).cancel();
|
||||
}
|
||||
|
@ -147,17 +147,17 @@ export class ConfusedTag extends PseudoStatusTag {
|
|||
|
||||
export class SeedTag extends PseudoStatusTag {
|
||||
constructor() {
|
||||
super(BattleTagType.SEEDED, BattleTagLapseType.AFTER_MOVE, 1);
|
||||
super(BattlerTagType.SEEDED, BattlerTagLapseType.AFTER_MOVE, 1);
|
||||
}
|
||||
|
||||
onAdd(pokemon: Pokemon): void {
|
||||
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 {
|
||||
const ret = lapseType !== BattleTagLapseType.CUSTOM || super.lapse(pokemon, lapseType);
|
||||
lapse(pokemon: Pokemon, lapseType: BattlerTagLapseType): boolean {
|
||||
const ret = lapseType !== BattlerTagLapseType.CUSTOM || super.lapse(pokemon, lapseType);
|
||||
|
||||
if (ret) {
|
||||
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 {
|
||||
constructor() {
|
||||
super(BattleTagType.NIGHTMARE, BattleTagLapseType.AFTER_MOVE, 1);
|
||||
super(BattlerTagType.NIGHTMARE, BattlerTagLapseType.AFTER_MOVE, 1);
|
||||
}
|
||||
|
||||
onAdd(pokemon: Pokemon): void {
|
||||
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 {
|
||||
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 {
|
||||
const ret = lapseType !== BattleTagLapseType.CUSTOM || super.lapse(pokemon, lapseType);
|
||||
lapse(pokemon: Pokemon, lapseType: BattlerTagLapseType): boolean {
|
||||
const ret = lapseType !== BattlerTagLapseType.CUSTOM || super.lapse(pokemon, lapseType);
|
||||
|
||||
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
|
||||
|
||||
const damage = Math.ceil(pokemon.getMaxHp() / 4);
|
||||
|
@ -207,17 +207,17 @@ export class NightmareTag extends PseudoStatusTag {
|
|||
|
||||
export class IngrainTag extends PseudoStatusTag {
|
||||
constructor() {
|
||||
super(BattleTagType.INGRAIN, BattleTagLapseType.TURN_END, 1);
|
||||
super(BattlerTagType.INGRAIN, BattlerTagLapseType.TURN_END, 1);
|
||||
}
|
||||
|
||||
onAdd(pokemon: Pokemon): void {
|
||||
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 {
|
||||
const ret = lapseType !== BattleTagLapseType.CUSTOM || super.lapse(pokemon, lapseType);
|
||||
lapse(pokemon: Pokemon, lapseType: BattlerTagLapseType): boolean {
|
||||
const ret = lapseType !== BattlerTagLapseType.CUSTOM || super.lapse(pokemon, lapseType);
|
||||
|
||||
if (ret)
|
||||
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 {
|
||||
constructor() {
|
||||
super(BattleTagType.AQUA_RING, BattleTagLapseType.TURN_END, 1);
|
||||
super(BattlerTagType.AQUA_RING, BattlerTagLapseType.TURN_END, 1);
|
||||
}
|
||||
|
||||
onAdd(pokemon: Pokemon): void {
|
||||
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 {
|
||||
const ret = lapseType !== BattleTagLapseType.CUSTOM || super.lapse(pokemon, lapseType);
|
||||
lapse(pokemon: Pokemon, lapseType: BattlerTagLapseType): boolean {
|
||||
const ret = lapseType !== BattlerTagLapseType.CUSTOM || super.lapse(pokemon, lapseType);
|
||||
|
||||
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));
|
||||
|
@ -248,18 +248,18 @@ export class AquaRingTag extends PseudoStatusTag {
|
|||
}
|
||||
}
|
||||
|
||||
export class DrowsyTag extends BattleTag {
|
||||
export class DrowsyTag extends BattlerTag {
|
||||
constructor() {
|
||||
super(BattleTagType.DROWSY, BattleTagLapseType.TURN_END, 2);
|
||||
super(BattlerTagType.DROWSY, BattlerTagLapseType.TURN_END, 2);
|
||||
}
|
||||
|
||||
onAdd(pokemon: Pokemon): void {
|
||||
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)) {
|
||||
pokemon.scene.unshiftPhase(new ObtainStatusEffectPhase(pokemon.scene, pokemon.isPlayer(), StatusEffect.SLEEP));
|
||||
return false;
|
||||
|
@ -269,21 +269,21 @@ export class DrowsyTag extends BattleTag {
|
|||
}
|
||||
}
|
||||
|
||||
export class ProtectedTag extends BattleTag {
|
||||
export class ProtectedTag extends BattlerTag {
|
||||
constructor() {
|
||||
super(BattleTagType.PROTECTED, BattleTagLapseType.CUSTOM, 0);
|
||||
super(BattlerTagType.PROTECTED, BattlerTagLapseType.CUSTOM, 0);
|
||||
}
|
||||
|
||||
onAdd(pokemon: Pokemon): void {
|
||||
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 {
|
||||
if (lapseType === BattleTagLapseType.CUSTOM) {
|
||||
lapse(pokemon: Pokemon, lapseType: BattlerTagLapseType): boolean {
|
||||
if (lapseType === BattlerTagLapseType.CUSTOM) {
|
||||
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;
|
||||
}
|
||||
|
||||
|
@ -291,9 +291,9 @@ export class ProtectedTag extends BattleTag {
|
|||
}
|
||||
}
|
||||
|
||||
export class HideSpriteTag extends BattleTag {
|
||||
constructor(tagType: BattleTagType, turnCount: integer) {
|
||||
super(tagType, BattleTagLapseType.MOVE_EFFECT, turnCount);
|
||||
export class HideSpriteTag extends BattlerTag {
|
||||
constructor(tagType: BattlerTagType, turnCount: integer) {
|
||||
super(tagType, BattlerTagLapseType.MOVE_EFFECT, turnCount);
|
||||
}
|
||||
|
||||
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) {
|
||||
case BattleTagType.RECHARGING:
|
||||
case BattlerTagType.RECHARGING:
|
||||
return new RechargingTag();
|
||||
case BattleTagType.FLINCHED:
|
||||
case BattlerTagType.FLINCHED:
|
||||
return new FlinchedTag();
|
||||
case BattleTagType.CONFUSED:
|
||||
case BattlerTagType.CONFUSED:
|
||||
return new ConfusedTag(turnCount);
|
||||
case BattleTagType.SEEDED:
|
||||
case BattlerTagType.SEEDED:
|
||||
return new SeedTag();
|
||||
case BattleTagType.NIGHTMARE:
|
||||
case BattlerTagType.NIGHTMARE:
|
||||
return new NightmareTag();
|
||||
case BattleTagType.AQUA_RING:
|
||||
case BattlerTagType.AQUA_RING:
|
||||
return new AquaRingTag();
|
||||
case BattleTagType.DROWSY:
|
||||
case BattlerTagType.DROWSY:
|
||||
return new DrowsyTag();
|
||||
case BattleTagType.PROTECTED:
|
||||
case BattlerTagType.PROTECTED:
|
||||
return new ProtectedTag();
|
||||
case BattleTagType.FLYING:
|
||||
case BattleTagType.UNDERGROUND:
|
||||
case BattlerTagType.FLYING:
|
||||
case BattlerTagType.UNDERGROUND:
|
||||
return new HideSpriteTag(tagType, turnCount);
|
||||
case BattleTagType.NO_CRIT:
|
||||
return new BattleTag(tagType, BattleTagLapseType.AFTER_MOVE, turnCount);
|
||||
case BattleTagType.BYPASS_SLEEP:
|
||||
return new BattleTag(BattleTagType.BYPASS_SLEEP, BattleTagLapseType.TURN_END, turnCount);
|
||||
case BattleTagType.IGNORE_FLYING:
|
||||
return new BattleTag(tagType, BattleTagLapseType.TURN_END, turnCount);
|
||||
case BattlerTagType.NO_CRIT:
|
||||
return new BattlerTag(tagType, BattlerTagLapseType.AFTER_MOVE, turnCount);
|
||||
case BattlerTagType.BYPASS_SLEEP:
|
||||
return new BattlerTag(BattlerTagType.BYPASS_SLEEP, BattlerTagLapseType.TURN_END, turnCount);
|
||||
case BattlerTagType.IGNORE_FLYING:
|
||||
return new BattlerTag(tagType, BattlerTagLapseType.TURN_END, turnCount);
|
||||
default:
|
||||
return new BattleTag(tagType, BattleTagLapseType.CUSTOM, turnCount);
|
||||
return new BattlerTag(tagType, BattlerTagLapseType.CUSTOM, turnCount);
|
||||
}
|
||||
}
|
|
@ -1,7 +1,7 @@
|
|||
import { MessagePhase, PokemonHealPhase } from "../battle-phases";
|
||||
import { getPokemonMessage } from "../messages";
|
||||
import Pokemon from "../pokemon";
|
||||
import { BattleTagType } from "./battle-tag";
|
||||
import { BattlerTagType } from "./battler-tag";
|
||||
import { getStatusEffectHealText } from "./status-effect";
|
||||
|
||||
export enum BerryType {
|
||||
|
@ -34,7 +34,7 @@ export function getBerryPredicate(berryType: BerryType): BerryPredicate {
|
|||
case BerryType.SITRUS:
|
||||
return (pokemon: Pokemon) => pokemon.getHpRatio() < 0.5;
|
||||
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:
|
||||
return (pokemon: Pokemon) => {
|
||||
if (pokemon.status) {
|
||||
pokemon.scene.unshiftPhase(new MessagePhase(pokemon.scene,
|
||||
getPokemonMessage(pokemon, getStatusEffectHealText(pokemon.status.effect))));
|
||||
pokemon.scene.queueMessage(getPokemonMessage(pokemon, getStatusEffectHealText(pokemon.status.effect)));
|
||||
pokemon.resetStatus();
|
||||
pokemon.updateInfo();
|
||||
} else if (pokemon.getTag(BattleTagType.CONFUSED))
|
||||
pokemon.lapseTag(BattleTagType.CONFUSED);
|
||||
} else if (pokemon.getTag(BattlerTagType.CONFUSED))
|
||||
pokemon.lapseTag(BattlerTagType.CONFUSED);
|
||||
};
|
||||
}
|
||||
}
|
298
src/data/move.ts
298
src/data/move.ts
|
@ -1,13 +1,14 @@
|
|||
import { ChargeAnim, MoveChargeAnim, initMoveAnim, loadMoveAnimAssets } from "./battle-anims";
|
||||
import { DamagePhase, EnemyMovePhase, MessagePhase, ObtainStatusEffectPhase, PlayerMovePhase, PokemonHealPhase, StatChangePhase } from "../battle-phases";
|
||||
import { BattleStat } from "./battle-stat";
|
||||
import { BattleTagType } from "./battle-tag";
|
||||
import { BattlerTagType } from "./battler-tag";
|
||||
import { getPokemonMessage } from "../messages";
|
||||
import Pokemon, { EnemyPokemon, MoveResult, PlayerPokemon, PokemonMove, TurnMove } from "../pokemon";
|
||||
import { StatusEffect, getStatusEffectDescriptor } from "./status-effect";
|
||||
import { Type } from "./type";
|
||||
import * as Utils from "../utils";
|
||||
import { WeatherType } from "./weather";
|
||||
import { ArenaTagType } from "./arena-tag";
|
||||
|
||||
export enum MoveCategory {
|
||||
PHYSICAL,
|
||||
|
@ -671,8 +672,8 @@ export class MoveEffectAttr extends MoveAttr {
|
|||
}
|
||||
|
||||
canApply(user: Pokemon, target: Pokemon, move: Move, args: any[]) {
|
||||
return !!(this.selfTarget ? user.hp && !user.getTag(BattleTagType.FRENZY) : target.hp)
|
||||
&& (this.selfTarget || !target.getTag(BattleTagType.PROTECTED) || move.hasFlag(MoveFlags.IGNORE_PROTECT));
|
||||
return !!(this.selfTarget ? user.hp && !user.getTag(BattlerTagType.FRENZY) : target.hp)
|
||||
&& (this.selfTarget || !target.getTag(BattlerTagType.PROTECTED) || move.hasFlag(MoveFlags.IGNORE_PROTECT));
|
||||
}
|
||||
|
||||
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);
|
||||
user.damage(recoilDamage);
|
||||
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;
|
||||
}
|
||||
|
@ -893,7 +894,7 @@ export class HealStatusEffectAttr extends MoveEffectAttr {
|
|||
|
||||
const pokemon = this.selfTarget ? user : target;
|
||||
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.updateInfo();
|
||||
}
|
||||
|
@ -905,7 +906,7 @@ export class HealStatusEffectAttr extends MoveEffectAttr {
|
|||
export class BypassSleepAttr extends MoveAttr {
|
||||
apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean {
|
||||
if (user.status?.effect === StatusEffect.SLEEP) {
|
||||
user.addTag(BattleTagType.BYPASS_SLEEP, 1);
|
||||
user.addTag(BattlerTagType.BYPASS_SLEEP, 1);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -947,7 +948,7 @@ export class ClearWeatherAttr extends MoveEffectAttr {
|
|||
export class OneHitKOAttr extends MoveHitEffectAttr {
|
||||
apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean {
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
@ -957,10 +958,10 @@ export class OverrideMoveEffectAttr extends MoveAttr { }
|
|||
export class ChargeAttr extends OverrideMoveEffectAttr {
|
||||
public chargeAnim: ChargeAnim;
|
||||
private chargeText: string;
|
||||
private tagType: BattleTagType;
|
||||
private tagType: BattlerTagType;
|
||||
public chargeEffect: boolean;
|
||||
|
||||
constructor(chargeAnim: ChargeAnim, chargeText: string, tagType?: BattleTagType, chargeEffect?: boolean) {
|
||||
constructor(chargeAnim: ChargeAnim, chargeText: string, tagType?: BattlerTagType, chargeEffect?: boolean) {
|
||||
super();
|
||||
|
||||
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) {
|
||||
(args[0] as Utils.BooleanHolder).value = true;
|
||||
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)
|
||||
user.addTag(this.tagType, 1);
|
||||
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 {
|
||||
apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean {
|
||||
const power = args[0] as Utils.NumberHolder;
|
||||
|
@ -1227,7 +1236,7 @@ export class DisableMoveAttr extends MoveEffectAttr {
|
|||
const disabledMove = target.moveset[moveIndex];
|
||||
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;
|
||||
}
|
||||
|
@ -1270,13 +1279,13 @@ export class FrenzyAttr extends MoveEffectAttr {
|
|||
return false;
|
||||
|
||||
if (!user.getMoveQueue().length) {
|
||||
if (!user.getTag(BattleTagType.FRENZY)) {
|
||||
if (!user.getTag(BattlerTagType.FRENZY)) {
|
||||
const turnCount = Utils.randInt(2) + 1;
|
||||
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 {
|
||||
applyMoveAttrs(AddTagAttr, user, target, move, args);
|
||||
user.lapseTag(BattleTagType.FRENZY);
|
||||
applyMoveAttrs(AddBattlerTagAttr, user, target, move, args);
|
||||
user.lapseTag(BattlerTagType.FRENZY);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
@ -1288,14 +1297,14 @@ export class FrenzyAttr extends MoveEffectAttr {
|
|||
export const frenzyMissFunc = (user: Pokemon, target: Pokemon, move: Move) => {
|
||||
while (user.getMoveQueue().length && user.getMoveQueue()[0].move === move.id)
|
||||
user.getMoveQueue().shift();
|
||||
user.lapseTag(BattleTagType.FRENZY)
|
||||
user.lapseTag(BattlerTagType.FRENZY)
|
||||
};
|
||||
|
||||
export class AddTagAttr extends MoveEffectAttr {
|
||||
public tagType: BattleTagType;
|
||||
export class AddBattlerTagAttr extends MoveEffectAttr {
|
||||
public tagType: BattlerTagType;
|
||||
public turnCount: integer;
|
||||
|
||||
constructor(tagType: BattleTagType, selfTarget?: boolean, turnCount?: integer) {
|
||||
constructor(tagType: BattlerTagType, selfTarget?: boolean, turnCount?: integer) {
|
||||
super(selfTarget);
|
||||
|
||||
this.tagType = tagType;
|
||||
|
@ -1315,29 +1324,29 @@ export class AddTagAttr extends MoveEffectAttr {
|
|||
}
|
||||
}
|
||||
|
||||
export class FlinchAttr extends AddTagAttr {
|
||||
export class FlinchAttr extends AddBattlerTagAttr {
|
||||
constructor() {
|
||||
super(BattleTagType.FLINCHED, false);
|
||||
super(BattlerTagType.FLINCHED, false);
|
||||
}
|
||||
}
|
||||
|
||||
export class ConfuseAttr extends AddTagAttr {
|
||||
export class ConfuseAttr extends AddBattlerTagAttr {
|
||||
constructor(selfTarget?: boolean) {
|
||||
super(BattleTagType.CONFUSED, selfTarget, Utils.randInt(4, 1));
|
||||
super(BattlerTagType.CONFUSED, selfTarget, Utils.randInt(4, 1));
|
||||
}
|
||||
}
|
||||
|
||||
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));
|
||||
}
|
||||
}
|
||||
|
||||
export class HitsTagAttr extends MoveAttr {
|
||||
public tagType: BattleTagType;
|
||||
public tagType: BattlerTagType;
|
||||
public doubleDamage: boolean;
|
||||
|
||||
constructor(tagType: BattleTagType, doubleDamage?: boolean) {
|
||||
constructor(tagType: BattlerTagType, doubleDamage?: boolean) {
|
||||
super();
|
||||
|
||||
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> {
|
||||
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);
|
||||
export class AddArenaTagAttr extends MoveEffectAttr {
|
||||
public tagType: ArenaTagType;
|
||||
public turnCount: integer;
|
||||
|
||||
constructor(tagType: ArenaTagType, turnCount?: integer) {
|
||||
super(true);
|
||||
|
||||
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 {
|
||||
|
@ -1379,7 +1435,9 @@ export class RandomMoveAttr extends OverrideMoveEffectAttr {
|
|||
const moveIds = Utils.getEnumValues(Moves).filter(m => m && m !== move.id);
|
||||
const moveId = moveIds[Utils.randInt(moveIds.length)];
|
||||
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(() => {
|
||||
loadMoveAnimAssets(user.scene, [ moveId ], 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 = [
|
||||
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),
|
||||
|
@ -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 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 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 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 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.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),
|
||||
|
@ -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.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.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.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()),
|
||||
|
@ -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.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 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 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,
|
||||
|
@ -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.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 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.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 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)),
|
||||
|
@ -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 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 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 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)),
|
||||
|
@ -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 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 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 MultiHitAttr(MultiHitType._3_INCR), new MissEffectAttr((user: Pokemon, target: Pokemon, move: Move) => {
|
||||
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 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 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.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()),
|
||||
|
@ -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 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 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 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),
|
||||
|
@ -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 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 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 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,
|
||||
|
@ -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.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 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.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)),
|
||||
|
@ -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.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 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 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),
|
||||
|
@ -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.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 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 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),
|
||||
|
@ -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.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 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.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.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,
|
||||
|
@ -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 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 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 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 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 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.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 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.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.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.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 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.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),
|
||||
|
@ -1753,11 +1904,11 @@ export const allMoves = [
|
|||
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 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.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.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.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)),
|
||||
|
@ -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 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.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 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 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.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.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 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 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()),
|
||||
|
@ -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 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 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 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),
|
||||
|
@ -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.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 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 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)),
|
||||
|
@ -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 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.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 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.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),
|
||||
|
@ -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.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 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 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)),
|
||||
|
@ -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.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 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 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),
|
||||
|
@ -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.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.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 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)),
|
||||
|
@ -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 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.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 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),
|
||||
|
@ -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 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 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 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 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.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 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.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(),
|
||||
|
|
|
@ -17,10 +17,11 @@ import { tmSpecies } from './data/tms';
|
|||
import { pokemonEvolutions, pokemonPrevolutions, SpeciesEvolution, SpeciesEvolutionCondition } from './data/pokemon-evolutions';
|
||||
import { DamagePhase, FaintPhase, MessagePhase } from './battle-phases';
|
||||
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 { WeatherType } from './data/weather';
|
||||
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 {
|
||||
public id: integer;
|
||||
|
@ -316,21 +317,28 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
|||
}
|
||||
|
||||
getTypes(): Type[] {
|
||||
const speciesForm = this.getSpeciesForm();
|
||||
const speciesTypes = [ speciesForm.type1 ];
|
||||
if (speciesForm.type2 !== null)
|
||||
speciesTypes.push(speciesForm.type1);
|
||||
const types = [];
|
||||
|
||||
if (this.getTag(BattleTagType.IGNORE_FLYING)) {
|
||||
const flyingIndex = speciesTypes.indexOf(Type.FLYING);
|
||||
if (flyingIndex > -1)
|
||||
speciesTypes.splice(flyingIndex, 1);
|
||||
if (this.summonData.types)
|
||||
this.summonData.types.forEach(t => types.push(t));
|
||||
else {
|
||||
const speciesForm = this.getSpeciesForm();
|
||||
|
||||
types.push(speciesForm.type1);
|
||||
if (speciesForm.type2 !== null)
|
||||
types.push(speciesForm.type1);
|
||||
}
|
||||
|
||||
if (!speciesTypes.length)
|
||||
speciesTypes.push(Type.NORMAL);
|
||||
if (this.getTag(BattlerTagType.IGNORE_FLYING)) {
|
||||
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 {
|
||||
|
@ -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 weatherTypeMultiplier = this.scene.arena.getAttackTypeMultiplier(move.type);
|
||||
applyMoveAttrs(VariablePowerAttr, source, this, move, power);
|
||||
this.scene.arena.applyTags(WeakenMoveTypeTag, move.type, power);
|
||||
this.scene.applyModifiers(AttackTypeBoosterModifier, source.isPlayer(), source, power);
|
||||
const critLevel = new Utils.IntegerHolder(0);
|
||||
applyMoveAttrs(HighCritAttr, source, this, move, critLevel);
|
||||
this.scene.applyModifiers(TempBattleStatBoosterModifier, source.isPlayer(), TempBattleStat.CRIT, critLevel);
|
||||
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 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;
|
||||
|
@ -510,18 +519,18 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
|||
source.turnData.damageDealt += damage;
|
||||
this.scene.unshiftPhase(new DamagePhase(this.scene, this.isPlayer(), result as DamageResult))
|
||||
if (isCritical)
|
||||
this.scene.unshiftPhase(new MessagePhase(this.scene, 'A critical hit!'));
|
||||
this.scene.queueMessage('A critical hit!');
|
||||
}
|
||||
|
||||
switch (result) {
|
||||
case MoveResult.SUPER_EFFECTIVE:
|
||||
this.scene.unshiftPhase(new MessagePhase(this.scene, 'It\'s super effective!'));
|
||||
this.scene.queueMessage('It\'s super effective!');
|
||||
break;
|
||||
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;
|
||||
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;
|
||||
|
@ -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);
|
||||
if (existingTag) {
|
||||
existingTag.onOverlap(this);
|
||||
return false;
|
||||
}
|
||||
|
||||
const newTag = getBattleTag(tagType, turnCount || 0);
|
||||
const newTag = getBattlerTag(tagType, turnCount || 0);
|
||||
this.summonData.tags.push(newTag);
|
||||
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'
|
||||
? this.summonData.tags.find(t => t.tagType === 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));
|
||||
}
|
||||
|
||||
getTags(tagType: BattleTagType | { new(...args: any[]): BattleTag }): BattleTag[] {
|
||||
getTags(tagType: BattlerTagType | { new(...args: any[]): BattlerTag }): BattlerTag[] {
|
||||
return typeof(tagType) === 'number'
|
||||
? this.summonData.tags.filter(t => t.tagType === 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));
|
||||
}
|
||||
|
||||
lapseTag(tagType: BattleTagType): boolean {
|
||||
lapseTag(tagType: BattlerTagType): boolean {
|
||||
const tags = this.summonData.tags;
|
||||
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);
|
||||
tags.splice(tags.indexOf(tag), 1);
|
||||
}
|
||||
return !!tag;
|
||||
}
|
||||
|
||||
lapseTags(lapseType: BattleTagLapseType): void {
|
||||
lapseTags(lapseType: BattlerTagLapseType): void {
|
||||
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);
|
||||
tags.splice(tags.indexOf(t), 1);
|
||||
});
|
||||
|
@ -699,8 +710,8 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
|||
const lastStatus = this.status.effect;
|
||||
this.status = undefined;
|
||||
if (lastStatus === StatusEffect.SLEEP) {
|
||||
if (this.getTag(BattleTagType.NIGHTMARE))
|
||||
this.lapseTag(BattleTagType.NIGHTMARE);
|
||||
if (this.getTag(BattlerTagType.NIGHTMARE))
|
||||
this.lapseTag(BattlerTagType.NIGHTMARE);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -711,8 +722,8 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
|||
|
||||
resetBattleSummonData(): void {
|
||||
this.battleSummonData = new PokemonBattleSummonData();
|
||||
if (this.getTag(BattleTagType.SEEDED))
|
||||
this.lapseTag(BattleTagType.SEEDED);
|
||||
if (this.getTag(BattlerTagType.SEEDED))
|
||||
this.lapseTag(BattlerTagType.SEEDED);
|
||||
}
|
||||
|
||||
resetTurnData(): void {
|
||||
|
@ -1001,7 +1012,8 @@ export class PokemonSummonData {
|
|||
public battleStats: integer[] = [ 0, 0, 0, 0, 0, 0, 0 ];
|
||||
public moveHistory: TurnMove[] = [];
|
||||
public moveQueue: QueuedMove[] = [];
|
||||
public tags: BattleTag[] = [];
|
||||
public tags: BattlerTag[] = [];
|
||||
public types: Type[];
|
||||
}
|
||||
|
||||
export class PokemonBattleSummonData {
|
||||
|
|
Loading…
Reference in New Issue