Add more abilities and move contact flag

This commit is contained in:
Flashfyre 2023-04-27 14:30:03 -04:00
parent 18679241e9
commit 005cc9b7d5
10 changed files with 2596 additions and 2077 deletions

View File

@ -194,12 +194,13 @@ export class Arena {
} }
isMoveWeatherCancelled(move: Move) { isMoveWeatherCancelled(move: Move) {
return this.weather && this.weather.isMoveWeatherCancelled(move); return this.weather && !this.weather.isEffectSuppressed(this.scene) && this.weather.isMoveWeatherCancelled(move);
} }
getAttackTypeMultiplier(attackType: Type): number { getAttackTypeMultiplier(attackType: Type): number {
if (!this.weather) if (!this.weather || this.weather.isEffectSuppressed(this.scene))
return 1; return 1;
return this.weather.getAttackTypeMultiplier(attackType); return this.weather.getAttackTypeMultiplier(attackType);
} }

View File

@ -25,7 +25,7 @@ import { Gender } from "./data/gender";
import { Weather, WeatherType, getRandomWeatherType, getWeatherDamageMessage, getWeatherLapseMessage } from "./data/weather"; import { Weather, WeatherType, getRandomWeatherType, getWeatherDamageMessage, getWeatherLapseMessage } from "./data/weather";
import { TempBattleStat } from "./data/temp-battle-stat"; import { TempBattleStat } from "./data/temp-battle-stat";
import { ArenaTrapTag, TrickRoomTag } from "./data/arena-tag"; import { ArenaTrapTag, TrickRoomTag } from "./data/arena-tag";
import { ProtectStatAttr, applyPreStatChangeAbilityAttrs } from "./data/ability"; import { PostWeatherLapseAbAttr, PreWeatherDamageAbAttr, ProtectStatAttr, SuppressWeatherEffectAbAttr, applyPostWeatherLapseAbAttrs, applyPreStatChangeAbAttrs, applyPreWeatherEffectAbAttrs } from "./data/ability";
export class SelectStarterPhase extends BattlePhase { export class SelectStarterPhase extends BattlePhase {
constructor(scene: BattleScene) { constructor(scene: BattleScene) {
@ -456,7 +456,36 @@ export class SummonMissingPhase extends SummonPhase {
} }
} }
export class CommandPhase extends BattlePhase { type PokemonFunc = (pokemon: Pokemon) => void;
export abstract class FieldPhase extends BattlePhase {
isPlayerDelayed(): boolean {
const playerPokemon = this.scene.getPlayerPokemon();
const enemyPokemon = this.scene.getEnemyPokemon();
const playerSpeed = playerPokemon?.getBattleStat(Stat.SPD) || 0;
const enemySpeed = enemyPokemon?.getBattleStat(Stat.SPD) || 0;
const speedDelayed = new Utils.BooleanHolder(playerSpeed < enemySpeed);
this.scene.arena.applyTags(TrickRoomTag, speedDelayed);
return speedDelayed.value || (playerSpeed === enemySpeed && Utils.randInt(2) === 1);
}
executeForBoth(func: PokemonFunc): void {
const playerPokemon = this.scene.getPlayerPokemon();
const enemyPokemon = this.scene.getEnemyPokemon();
const delayed = this.isPlayerDelayed();
if (!delayed && playerPokemon)
func(playerPokemon);
if (enemyPokemon)
func(enemyPokemon);
if (delayed && playerPokemon)
func(playerPokemon);
}
}
export class CommandPhase extends FieldPhase {
constructor(scene: BattleScene) { constructor(scene: BattleScene) {
super(scene) super(scene)
} }
@ -496,9 +525,6 @@ export class CommandPhase extends BattlePhase {
const enemyPokemon = this.scene.getEnemyPokemon(); const enemyPokemon = this.scene.getEnemyPokemon();
let success: boolean; let success: boolean;
const playerSpeed = playerPokemon.getBattleStat(Stat.SPD);
const enemySpeed = enemyPokemon.getBattleStat(Stat.SPD);
let isDelayed = (command: Command, playerMove: PokemonMove, enemyMove: PokemonMove) => { let isDelayed = (command: Command, playerMove: PokemonMove, enemyMove: PokemonMove) => {
switch (command) { switch (command) {
case Command.FIGHT: case Command.FIGHT:
@ -516,10 +542,7 @@ export class CommandPhase extends BattlePhase {
return true; return true;
} }
const speedDelayed = new Utils.BooleanHolder(playerSpeed < enemySpeed); return this.isPlayerDelayed();
this.scene.arena.applyTags(TrickRoomTag, speedDelayed);
return speedDelayed.value || (playerSpeed === enemySpeed && Utils.randInt(2) === 1);
}; };
let playerMove: PokemonMove; let playerMove: PokemonMove;
@ -573,7 +596,7 @@ export class CommandPhase extends BattlePhase {
if (success) { if (success) {
if (this.scene.arena.weather) if (this.scene.arena.weather)
this.scene.unshiftPhase(new WeatherEffectPhase(this.scene, this.scene.arena.weather, isDelayed(command, null, null))); this.scene.unshiftPhase(new WeatherEffectPhase(this.scene, this.scene.arena.weather));
const enemyMove = enemyPokemon.getNextMove(); const enemyMove = enemyPokemon.getNextMove();
const enemyPhase = new EnemyMovePhase(this.scene, enemyPokemon, enemyMove); const enemyPhase = new EnemyMovePhase(this.scene, enemyPokemon, enemyMove);
@ -608,7 +631,7 @@ export class CommandPhase extends BattlePhase {
} }
} }
export class TurnEndPhase extends BattlePhase { export class TurnEndPhase extends FieldPhase {
constructor(scene: BattleScene) { constructor(scene: BattleScene) {
super(scene); super(scene);
} }
@ -617,9 +640,6 @@ export class TurnEndPhase extends BattlePhase {
super.start(); super.start();
this.scene.currentBattle.incrementTurn(); this.scene.currentBattle.incrementTurn();
const playerPokemon = this.scene.getPlayerPokemon();
const enemyPokemon = this.scene.getEnemyPokemon();
const handlePokemon = (pokemon: Pokemon) => { const handlePokemon = (pokemon: Pokemon) => {
if (!pokemon || !pokemon.hp) if (!pokemon || !pokemon.hp)
@ -644,19 +664,7 @@ export class TurnEndPhase extends BattlePhase {
pokemon.battleSummonData.turnCount++; pokemon.battleSummonData.turnCount++;
}; };
const playerSpeed = playerPokemon?.getBattleStat(Stat.SPD) || 0; this.executeForBoth(handlePokemon);
const enemySpeed = enemyPokemon?.getBattleStat(Stat.SPD) || 0;
const speedDelayed = new Utils.BooleanHolder(playerSpeed < enemySpeed);
this.scene.arena.applyTags(TrickRoomTag, speedDelayed);
const isDelayed = speedDelayed.value || (playerSpeed === enemySpeed && Utils.randInt(2) === 1);
if (!isDelayed)
handlePokemon(playerPokemon);
handlePokemon(enemyPokemon);
if (isDelayed)
handlePokemon(playerPokemon);
this.scene.arena.lapseTags(); this.scene.arena.lapseTags();
@ -687,7 +695,7 @@ export class BattleEndPhase extends BattlePhase {
} }
} }
export abstract class PokemonPhase extends BattlePhase { export abstract class PokemonPhase extends FieldPhase {
protected player: boolean; protected player: boolean;
constructor(scene: BattleScene, player: boolean) { constructor(scene: BattleScene, player: boolean) {
@ -1079,6 +1087,18 @@ export class MoveAnimTestPhase extends BattlePhase {
} }
} }
export class ShowAbilityPhase extends PokemonPhase {
constructor(scene: BattleScene, player: boolean) {
super(scene, player);
}
start() {
this.scene.abilityBar.showAbility(this.getPokemon());
this.end();
}
}
export class StatChangePhase extends PokemonPhase { export class StatChangePhase extends PokemonPhase {
private stats: BattleStat[]; private stats: BattleStat[];
private selfTarget: boolean; private selfTarget: boolean;
@ -1088,6 +1108,7 @@ export class StatChangePhase extends PokemonPhase {
super(scene, player); super(scene, player);
const allStats = Utils.getEnumValues(BattleStat); const allStats = Utils.getEnumValues(BattleStat);
this.selfTarget = selfTarget;
this.stats = stats.map(s => s !== BattleStat.RAND ? s : allStats[Utils.randInt(BattleStat.SPD + 1)]); this.stats = stats.map(s => s !== BattleStat.RAND ? s : allStats[Utils.randInt(BattleStat.SPD + 1)]);
this.levels = levels; this.levels = levels;
} }
@ -1099,7 +1120,7 @@ export class StatChangePhase extends PokemonPhase {
const cancelled = new Utils.BooleanHolder(false); const cancelled = new Utils.BooleanHolder(false);
if (!this.selfTarget && this.levels < 0) if (!this.selfTarget && this.levels < 0)
applyPreStatChangeAbilityAttrs(ProtectStatAttr, this.getPokemon(), stat, cancelled); applyPreStatChangeAbAttrs(ProtectStatAttr, this.getPokemon(), stat, cancelled);
return !cancelled.value; return !cancelled.value;
}); });
@ -1170,37 +1191,46 @@ export class StatChangePhase extends PokemonPhase {
export class WeatherEffectPhase extends CommonAnimPhase { export class WeatherEffectPhase extends CommonAnimPhase {
private weather: Weather; private weather: Weather;
private playerDelayed: boolean;
constructor(scene: BattleScene, weather: Weather, playerDelayed: boolean) { constructor(scene: BattleScene, weather: Weather) {
super(scene, true, CommonAnim.SUNNY + (weather.weatherType - 1)); super(scene, true, CommonAnim.SUNNY + (weather.weatherType - 1));
this.weather = weather; this.weather = weather;
this.playerDelayed = playerDelayed;
} }
start() { start() {
if (this.weather.isDamaging()) { if (this.weather.isDamaging()) {
const inflictDamage = (pokemon: Pokemon) => {
this.scene.queueMessage(getWeatherDamageMessage(this.weather.weatherType, pokemon)); const cancelled = new Utils.BooleanHolder(false);
this.scene.unshiftPhase(new DamagePhase(this.scene, pokemon.isPlayer()));
pokemon.damage(Math.ceil(pokemon.getMaxHp() / 16));
};
const playerPokemon = this.scene.getPlayerPokemon(); this.executeForBoth((pokemon: Pokemon) => applyPreWeatherEffectAbAttrs(SuppressWeatherEffectAbAttr, pokemon, this.weather, cancelled));
const enemyPokemon = this.scene.getEnemyPokemon();
const playerImmune = !playerPokemon || !!playerPokemon.getTypes().filter(t => this.weather.isTypeDamageImmune(t)).length; if (!cancelled.value) {
const enemyImmune = !enemyPokemon || !!enemyPokemon.getTypes().filter(t => this.weather.isTypeDamageImmune(t)).length; const inflictDamage = (pokemon: Pokemon) => {
const cancelled = new Utils.BooleanHolder(false);
if (!this.playerDelayed && !playerImmune) applyPreWeatherEffectAbAttrs(PreWeatherDamageAbAttr, pokemon, this.weather, cancelled);
inflictDamage(playerPokemon);
if (!enemyImmune) if (cancelled.value)
inflictDamage(enemyPokemon); return;
if (this.playerDelayed && !playerImmune)
inflictDamage(playerPokemon); this.scene.queueMessage(getWeatherDamageMessage(this.weather.weatherType, pokemon));
this.scene.unshiftPhase(new DamagePhase(this.scene, pokemon.isPlayer()));
pokemon.damage(Math.ceil(pokemon.getMaxHp() / 16));
};
this.executeForBoth((pokemon: Pokemon) => {
const immune = !pokemon || !!pokemon.getTypes().filter(t => this.weather.isTypeDamageImmune(t)).length;
if (!immune)
inflictDamage(pokemon);
});
}
} }
this.scene.ui.showText(getWeatherLapseMessage(this.weather.weatherType), null, () => super.start()); this.scene.ui.showText(getWeatherLapseMessage(this.weather.weatherType), null, () => {
this.executeForBoth((pokemon: Pokemon) => applyPostWeatherLapseAbAttrs(PostWeatherLapseAbAttr, pokemon, this.weather));
super.start();
});
} }
} }

View File

@ -3,7 +3,7 @@ import { Biome } from './data/biome';
import UI from './ui/ui'; import UI from './ui/ui';
import { EncounterPhase, SummonPhase, CommandPhase, NextEncounterPhase, NewBiomeEncounterPhase, SelectBiomePhase, SelectStarterPhase, MessagePhase } from './battle-phases'; import { EncounterPhase, SummonPhase, CommandPhase, NextEncounterPhase, NewBiomeEncounterPhase, SelectBiomePhase, SelectStarterPhase, MessagePhase } from './battle-phases';
import Pokemon, { PlayerPokemon, EnemyPokemon } from './pokemon'; import Pokemon, { PlayerPokemon, EnemyPokemon } from './pokemon';
import PokemonSpecies, { allSpecies, getPokemonSpecies } from './data/pokemon-species'; import PokemonSpecies, { allSpecies, getPokemonSpecies, initSpecies } from './data/pokemon-species';
import * as Utils from './utils'; import * as Utils from './utils';
import { Modifier, ModifierBar, ConsumablePokemonModifier, ConsumableModifier, PartyShareModifier, PokemonHpRestoreModifier, HealingBoosterModifier, PersistentModifier, PokemonHeldItemModifier, ModifierPredicate } from './modifier/modifier'; import { Modifier, ModifierBar, ConsumablePokemonModifier, ConsumableModifier, PartyShareModifier, PokemonHpRestoreModifier, HealingBoosterModifier, PersistentModifier, PokemonHeldItemModifier, ModifierPredicate } from './modifier/modifier';
import { PokeballType } from './data/pokeball'; import { PokeballType } from './data/pokeball';
@ -17,9 +17,10 @@ import { Arena } from './arena';
import { GameData } from './system/game-data'; import { GameData } from './system/game-data';
import StarterSelectUiHandler from './ui/starter-select-ui-handler'; import StarterSelectUiHandler from './ui/starter-select-ui-handler';
import { TextStyle, addTextObject } from './ui/text'; import { TextStyle, addTextObject } from './ui/text';
import { Moves } from './data/move'; import { Moves, initMoves } from './data/move';
import { getDefaultModifierTypeForTier, getEnemyModifierTypesForWave } from './modifier/modifier-type'; import { getDefaultModifierTypeForTier, getEnemyModifierTypesForWave } from './modifier/modifier-type';
import AbilityBar from './ui/ability-bar'; import AbilityBar from './ui/ability-bar';
import { initAbilities } from './data/ability';
const enableAuto = true; const enableAuto = true;
const quickStart = false; const quickStart = false;
@ -58,6 +59,7 @@ export default class BattleScene extends Phaser.Scene {
private phaseQueue: BattlePhase[]; private phaseQueue: BattlePhase[];
private phaseQueuePrepend: BattlePhase[]; private phaseQueuePrepend: BattlePhase[];
private phaseQueuePrependSpliceIndex: integer;
private currentPhase: BattlePhase; private currentPhase: BattlePhase;
public field: Phaser.GameObjects.Container; public field: Phaser.GameObjects.Container;
public fieldUI: Phaser.GameObjects.Container; public fieldUI: Phaser.GameObjects.Container;
@ -94,10 +96,15 @@ export default class BattleScene extends Phaser.Scene {
constructor() { constructor() {
super('battle'); super('battle');
initSpecies();
initMoves();
initAbilities();
this.gameData = new GameData(this); this.gameData = new GameData(this);
this.phaseQueue = []; this.phaseQueue = [];
this.phaseQueuePrepend = []; this.phaseQueuePrepend = [];
this.phaseQueuePrependSpliceIndex = -1;
} }
loadImage(key: string, folder: string, filename?: string) { loadImage(key: string, folder: string, filename?: string) {
@ -265,7 +272,7 @@ export default class BattleScene extends Phaser.Scene {
this.loadBgm('evolution_fanfare'); this.loadBgm('evolution_fanfare');
//this.load.glsl('sprite', 'shaders/sprite.frag'); //this.load.glsl('sprite', 'shaders/sprite.frag');
populateAnims(); populateAnims();
} }
@ -643,14 +650,27 @@ export default class BattleScene extends Phaser.Scene {
} }
unshiftPhase(phase: BattlePhase): void { unshiftPhase(phase: BattlePhase): void {
this.phaseQueuePrepend.push(phase); if (this.phaseQueuePrependSpliceIndex === -1)
this.phaseQueuePrepend.push(phase);
else
this.phaseQueuePrepend.splice(this.phaseQueuePrependSpliceIndex, 0, phase);
} }
clearPhaseQueue(): void { clearPhaseQueue(): void {
this.phaseQueue.splice(0, this.phaseQueue.length); this.phaseQueue.splice(0, this.phaseQueue.length);
} }
setPhaseQueueSplice(): void {
this.phaseQueuePrependSpliceIndex = this.phaseQueuePrepend.length;
}
clearPhaseQueueSplice(): void {
this.phaseQueuePrependSpliceIndex = -1;
}
shiftPhase(): void { shiftPhase(): void {
if (this.phaseQueuePrependSpliceIndex > -1)
this.clearPhaseQueueSplice();
if (this.phaseQueuePrepend.length) { if (this.phaseQueuePrepend.length) {
while (this.phaseQueuePrepend.length) while (this.phaseQueuePrepend.length)
this.phaseQueue.unshift(this.phaseQueuePrepend.pop()); this.phaseQueue.unshift(this.phaseQueuePrepend.pop());

View File

@ -2,15 +2,20 @@ import Pokemon, { PokemonMove } from "../pokemon";
import { Type } from "./type"; import { Type } from "./type";
import * as Utils from "../utils"; import * as Utils from "../utils";
import { BattleStat, getBattleStatName } from "./battle-stat"; import { BattleStat, getBattleStatName } from "./battle-stat";
import { StatChangePhase } from "../battle-phases"; import { PokemonHealPhase, ShowAbilityPhase, StatChangePhase } from "../battle-phases";
import { getPokemonMessage } from "../messages"; import { getPokemonMessage } from "../messages";
import { Weather, WeatherType } from "./weather";
import { BattlerTagType } from "./battler-tag";
import { StatusEffect } from "./status-effect";
import { Moves, RecoilAttr } from "./move";
export class Ability { export class Ability {
public id: Abilities; public id: Abilities;
public name: string; public name: string;
public description: string; public description: string;
public generation: integer; public generation: integer;
public attrs: AbilityAttr[]; public attrs: AbAttr[];
public conditions: AbAttrCondition[];
constructor(id: Abilities, name: string, description: string, generation: integer) { constructor(id: Abilities, name: string, description: string, generation: integer) {
this.id = id; this.id = id;
@ -18,39 +23,70 @@ export class Ability {
this.description = description; this.description = description;
this.generation = generation; this.generation = generation;
this.attrs = []; this.attrs = [];
this.conditions = [];
} }
getAttrs(attrType: { new(...args: any[]): AbilityAttr }): AbilityAttr[] { getAttrs(attrType: { new(...args: any[]): AbAttr }): AbAttr[] {
return this.attrs.filter(a => a instanceof attrType); return this.attrs.filter(a => a instanceof attrType);
} }
attr<T extends new (...args: any[]) => AbilityAttr>(AttrType: T, ...args: ConstructorParameters<T>): Ability { attr<T extends new (...args: any[]) => AbAttr>(AttrType: T, ...args: ConstructorParameters<T>): Ability {
const attr = new AttrType(...args); const attr = new AttrType(...args);
this.attrs.push(attr); this.attrs.push(attr);
return this; return this;
} }
condition(condition: AbAttrCondition): Ability {
this.conditions.push(condition);
return this;
}
} }
export abstract class AbilityAttr { type AbAttrCondition = (pokemon: Pokemon) => boolean;
export abstract class AbAttr {
apply(pokemon: Pokemon, cancelled: Utils.BooleanHolder, args: any[]): boolean {
return false;
}
getTriggerMessage(pokemon: Pokemon, ...args: any[]) { getTriggerMessage(pokemon: Pokemon, ...args: any[]) {
return null; return null;
} }
getCondition(): AbAttrCondition {
return null;
}
} }
export class PreDefendAbilityAttr extends AbilityAttr { export class BlockRecoilDamageAttr extends AbAttr {
apply(pokemon: Pokemon, cancelled: Utils.BooleanHolder, args: any[]): boolean {
cancelled.value = true;
return true;
}
getTriggerMessage(pokemon: Pokemon, ...args: any[]) {
return getPokemonMessage(pokemon, `'s ${pokemon.getAbility().name}\nprotected it from recoil!`);
}
}
export class PreDefendAbAttr extends AbAttr {
applyPreDefend(pokemon: Pokemon, attacker: Pokemon, move: PokemonMove, cancelled: Utils.BooleanHolder, args: any[]): boolean { applyPreDefend(pokemon: Pokemon, attacker: Pokemon, move: PokemonMove, cancelled: Utils.BooleanHolder, args: any[]): boolean {
return false; return false;
} }
} }
export class TypeImmunityAttr extends PreDefendAbilityAttr { export class TypeImmunityAbAttr extends PreDefendAbAttr {
private immuneType: Type; private immuneType: Type;
private condition: AbAttrCondition;
constructor(immuneType: Type) { constructor(immuneType: Type, condition?: AbAttrCondition) {
super(); super();
this.immuneType = immuneType; this.immuneType = immuneType;
this.condition = condition;
} }
applyPreDefend(pokemon: Pokemon, attacker: Pokemon, move: PokemonMove, cancelled: Utils.BooleanHolder, args: any[]): boolean { applyPreDefend(pokemon: Pokemon, attacker: Pokemon, move: PokemonMove, cancelled: Utils.BooleanHolder, args: any[]): boolean {
@ -61,14 +97,18 @@ export class TypeImmunityAttr extends PreDefendAbilityAttr {
return false; return false;
} }
getCondition(): AbAttrCondition {
return this.condition;
}
} }
class TypeImmunityStatChangeAttr extends TypeImmunityAttr { class TypeImmunityStatChangeAbAttr extends TypeImmunityAbAttr {
private stat: BattleStat; private stat: BattleStat;
private levels: integer; private levels: integer;
constructor(immuneType: Type, stat: BattleStat, levels: integer) { constructor(immuneType: Type, stat: BattleStat, levels: integer, condition?: AbAttrCondition) {
super(immuneType); super(immuneType, condition);
this.stat = stat; this.stat = stat;
this.levels = levels; this.levels = levels;
@ -86,13 +126,85 @@ class TypeImmunityStatChangeAttr extends TypeImmunityAttr {
} }
} }
export class PreStatChangeAbilityAttr extends AbilityAttr { class TypeImmunityAddBattlerTagAbAttr extends TypeImmunityAbAttr {
private tagType: BattlerTagType;
private turnCount: integer;
constructor(immuneType: Type, tagType: BattlerTagType, turnCount: integer, condition?: AbAttrCondition) {
super(immuneType, condition);
this.tagType = tagType;
this.turnCount = turnCount;
}
applyPreDefend(pokemon: Pokemon, attacker: Pokemon, move: PokemonMove, cancelled: Utils.BooleanHolder, args: any[]): boolean {
const ret = super.applyPreDefend(pokemon, attacker, move, cancelled, args);
if (ret) {
cancelled.value = true;
pokemon.addTag(this.tagType, this.turnCount, undefined, pokemon.id);
}
return ret;
}
}
export class PreAttackAbAttr extends AbAttr {
applyPreAttack(pokemon: Pokemon, defender: Pokemon, move: PokemonMove, args: any[]): boolean {
return false;
}
}
export class VariableMovePowerAbAttr extends PreAttackAbAttr {
applyPreAttack(pokemon: Pokemon, defender: Pokemon, move: PokemonMove, args: any[]): boolean {
//const power = args[0] as Utils.NumberHolder;
return false;
}
}
export class LowHpMoveTypePowerBoostAbAttr extends VariableMovePowerAbAttr {
private boostedType: Type;
constructor(boostedType: Type) {
super();
this.boostedType = boostedType;
}
applyPreAttack(pokemon: Pokemon, defender: Pokemon, move: PokemonMove, args: any[]): boolean {
if (move.getMove().type === this.boostedType) {
(args[0] as Utils.NumberHolder).value *= 1.5;
return true;
}
return false;
}
getCondition(): AbAttrCondition {
return (pokemon) => pokemon.getHpRatio() <= 0.33;
}
}
export class RecoilMovePowerBoostAbAttr extends VariableMovePowerAbAttr {
applyPreAttack(pokemon: Pokemon, defender: Pokemon, move: PokemonMove, args: any[]): boolean {
if (move.getMove().getAttrs(RecoilAttr).length && move.moveId !== Moves.STRUGGLE) {
(args[0] as Utils.NumberHolder).value *= 1.2;
return true;
}
return false;
}
}
export class PreStatChangeAbAttr extends AbAttr {
applyPreStatChange(pokemon: Pokemon, stat: BattleStat, cancelled: Utils.BooleanHolder, args: any[]): boolean { applyPreStatChange(pokemon: Pokemon, stat: BattleStat, cancelled: Utils.BooleanHolder, args: any[]): boolean {
return false; return false;
} }
} }
export class ProtectStatAttr extends PreStatChangeAbilityAttr { export class ProtectStatAttr extends PreStatChangeAbAttr {
private protectedStat: BattleStat; private protectedStat: BattleStat;
constructor(protectedStat?: BattleStat) { constructor(protectedStat?: BattleStat) {
@ -115,32 +227,213 @@ export class ProtectStatAttr extends PreStatChangeAbilityAttr {
} }
} }
export function applyPreDefendAbilityAttrs(attrType: { new(...args: any[]): PreDefendAbilityAttr }, export class PreWeatherEffectAbAttr extends AbAttr {
pokemon: Pokemon, attacker: Pokemon, move: PokemonMove, cancelled: Utils.BooleanHolder, ...args: any[]): void { applyPreWeatherEffect(pokemon: Pokemon, weather: Weather, cancelled: Utils.BooleanHolder, args: any[]): boolean {
return false;
}
}
export class PreWeatherDamageAbAttr extends PreWeatherEffectAbAttr { }
export class BlockWeatherDamageAttr extends PreWeatherDamageAbAttr {
applyPreWeatherEffect(pokemon: Pokemon, weather: Weather, cancelled: Utils.BooleanHolder, args: any[]): boolean {
cancelled.value = true;
return true;
}
}
export class SuppressWeatherEffectAbAttr extends PreWeatherEffectAbAttr {
private affectsImmutable: boolean;
constructor(affectsImmutable?: boolean) {
super();
this.affectsImmutable = affectsImmutable;
}
applyPreWeatherEffect(pokemon: Pokemon, weather: Weather, cancelled: Utils.BooleanHolder, args: any[]): boolean {
if (this.affectsImmutable || weather.isImmutable()) {
cancelled.value = true;
return true;
}
return false;
}
}
export class PostWeatherLapseAbAttr extends AbAttr {
applyPostWeatherLapse(pokemon: Pokemon, weather: Weather, args: any[]): boolean {
return false;
}
}
export class WeatherHealAbAttr extends PostWeatherLapseAbAttr {
private weatherTypes: WeatherType[];
constructor(...weatherTypes: WeatherType[]) {
super();
this.weatherTypes = weatherTypes;
}
applyPostWeatherLapse(pokemon: Pokemon, weather: Weather, args: any[]): boolean {
if (this.weatherTypes.indexOf(weather.weatherType) > -1 && pokemon.getHpRatio() < 1) {
const scene = pokemon.scene;
scene.unshiftPhase(new PokemonHealPhase(scene, pokemon.isPlayer(), Math.max(Math.floor(pokemon.getMaxHp() / 16), 1), getPokemonMessage(pokemon, `'s ${pokemon.getAbility().name}\nrestored its HP a little!`), true));
return true;
}
return false;
}
}
export function applyAbAttrs(attrType: { new(...args: any[]): AbAttr }, pokemon: Pokemon, cancelled: Utils.BooleanHolder, ...args: any[]): void {
if (!pokemon.canApplyAbility())
return;
const ability = pokemon.getAbility(); const ability = pokemon.getAbility();
const attrs = ability.getAttrs(attrType) as PreDefendAbilityAttr[]; const attrs = ability.getAttrs(attrType) as AbAttr[];
console.log(attrs, ability);
for (let attr of attrs) { for (let attr of attrs) {
if (!canApplyAttr(pokemon, attr))
continue;
pokemon.scene.setPhaseQueueSplice();
if (attr.apply(pokemon, cancelled, args)) {
queueShowAbility(pokemon);
const message = attr.getTriggerMessage(pokemon);
if (message)
pokemon.scene.queueMessage(message);
}
}
pokemon.scene.clearPhaseQueueSplice();
}
export function applyPreDefendAbAttrs(attrType: { new(...args: any[]): PreDefendAbAttr },
pokemon: Pokemon, attacker: Pokemon, move: PokemonMove, cancelled: Utils.BooleanHolder, ...args: any[]): void {
if (!pokemon.canApplyAbility())
return;
const ability = pokemon.getAbility();
const attrs = ability.getAttrs(attrType) as PreDefendAbAttr[];
for (let attr of attrs) {
if (!canApplyAttr(pokemon, attr))
continue;
pokemon.scene.setPhaseQueueSplice();
if (attr.applyPreDefend(pokemon, attacker, move, cancelled, args)) { if (attr.applyPreDefend(pokemon, attacker, move, cancelled, args)) {
pokemon.scene.abilityBar.showAbility(pokemon); queueShowAbility(pokemon);
const message = attr.getTriggerMessage(pokemon, attacker, move); const message = attr.getTriggerMessage(pokemon, attacker, move);
if (message) if (message)
pokemon.scene.queueMessage(message); pokemon.scene.queueMessage(message);
} }
} }
pokemon.scene.clearPhaseQueueSplice();
} }
export function applyPreStatChangeAbilityAttrs(attrType: { new(...args: any[]): PreStatChangeAbilityAttr }, export function applyPreAttackAbAttrs(attrType: { new(...args: any[]): PreAttackAbAttr },
pokemon: Pokemon, stat: BattleStat, cancelled: Utils.BooleanHolder, ...args: any[]) { pokemon: Pokemon, defender: Pokemon, move: PokemonMove, ...args: any[]): void {
const ability = pokemon.getAbility(); if (!pokemon.canApplyAbility())
const attrs = ability.getAttrs(attrType) as PreStatChangeAbilityAttr[]; return;
for (let attr of attrs) {
if (attr.applyPreStatChange(pokemon, stat, cancelled, args)) { const ability = pokemon.getAbility();
const attrs = ability.getAttrs(attrType) as PreAttackAbAttr[];
for (let attr of attrs) {
if (!canApplyAttr(pokemon, attr))
continue;
pokemon.scene.setPhaseQueueSplice();
if (attr.applyPreAttack(pokemon, defender, move, args)) {
queueShowAbility(pokemon);
const message = attr.getTriggerMessage(pokemon, defender, move);
if (message)
pokemon.scene.queueMessage(message);
}
}
pokemon.scene.clearPhaseQueueSplice();
}
export function applyPreStatChangeAbAttrs(attrType: { new(...args: any[]): PreStatChangeAbAttr },
pokemon: Pokemon, stat: BattleStat, cancelled: Utils.BooleanHolder, ...args: any[]): void {
if (!pokemon.canApplyAbility())
return;
const ability = pokemon.getAbility();
const attrs = ability.getAttrs(attrType) as PreStatChangeAbAttr[];
for (let attr of attrs) {
if (!canApplyAttr(pokemon, attr))
continue;
pokemon.scene.setPhaseQueueSplice();
if (attr.applyPreStatChange(pokemon, stat, cancelled, args)) {
queueShowAbility(pokemon);
const message = attr.getTriggerMessage(pokemon, stat);
if (message)
pokemon.scene.queueMessage(message);
}
}
pokemon.scene.clearPhaseQueueSplice();
}
export function applyPreWeatherEffectAbAttrs(attrType: { new(...args: any[]): PreWeatherEffectAbAttr },
pokemon: Pokemon, weather: Weather, cancelled: Utils.BooleanHolder, silent?: boolean, ...args: any[]): void {
if (!pokemon.canApplyAbility())
return;
const ability = pokemon.getAbility();
const attrs = ability.getAttrs(attrType) as PreWeatherEffectAbAttr[];
for (let attr of attrs) {
if (!canApplyAttr(pokemon, attr))
continue;
pokemon.scene.setPhaseQueueSplice();
if (attr.applyPreWeatherEffect(pokemon, weather, cancelled, args)) {
if (!silent) {
pokemon.scene.abilityBar.showAbility(pokemon); pokemon.scene.abilityBar.showAbility(pokemon);
const message = attr.getTriggerMessage(pokemon, stat); const message = attr.getTriggerMessage(pokemon, weather);
if (message) if (message)
pokemon.scene.queueMessage(message); pokemon.scene.queueMessage(message);
} }
} }
}
pokemon.scene.clearPhaseQueueSplice();
}
export function applyPostWeatherLapseAbAttrs(attrType: { new(...args: any[]): PostWeatherLapseAbAttr },
pokemon: Pokemon, weather: Weather, ...args: any[]): void {
if (!pokemon.canApplyAbility())
return;
if (weather.isEffectSuppressed(pokemon.scene))
return;
const ability = pokemon.getAbility();
const attrs = ability.getAttrs(attrType) as PostWeatherLapseAbAttr[];
for (let attr of attrs) {
if (!canApplyAttr(pokemon, attr))
continue;
pokemon.scene.setPhaseQueueSplice();
if (attr.applyPostWeatherLapse(pokemon, weather, args)) {
queueShowAbility(pokemon);
const message = attr.getTriggerMessage(pokemon, weather);
if (message)
pokemon.scene.queueMessage(message);
}
}
pokemon.scene.clearPhaseQueueSplice();
}
function canApplyAttr(pokemon: Pokemon, attr: AbAttr): boolean {
const condition = attr.getCondition();
return !condition || condition(pokemon);
}
function queueShowAbility(pokemon: Pokemon): void {
pokemon.scene.unshiftPhase(new ShowAbilityPhase(pokemon.scene, pokemon.isPlayer()));
pokemon.scene.clearPhaseQueueSplice();
} }
export enum Abilities { export enum Abilities {
@ -317,184 +610,199 @@ export enum Abilities {
NEUTRALIZING_GAS NEUTRALIZING_GAS
} }
export const abilities = [ export const abilities = [ new Ability(Abilities.NONE, "-", "", 3) ];
new Ability(Abilities.NONE, "- (N)", "", 3),
new Ability(Abilities.AIR_LOCK, "Air Lock (N)", "Eliminates the effects of weather.", 3), export function initAbilities() {
new Ability(Abilities.ARENA_TRAP, "Arena Trap (N)", "Prevents the foe from fleeing.", 3), abilities.push(
new Ability(Abilities.BATTLE_ARMOR, "Battle Armor (N)", "The POKéMON is protected against critical hits.", 3), new Ability(Abilities.AIR_LOCK, "Air Lock", "Eliminates the effects of all weather.", 3)
new Ability(Abilities.BLAZE, "Blaze (N)", "Powers up FIRE-type moves in a pinch.", 3), .attr(SuppressWeatherEffectAbAttr, true),
new Ability(Abilities.CHLOROPHYLL, "Chlorophyll (N)", "Boosts the POKéMON's SPEED in sunshine.", 3), new Ability(Abilities.ARENA_TRAP, "Arena Trap (N)", "Prevents the foe from fleeing.", 3),
new Ability(Abilities.CLEAR_BODY, "Clear Body", "Prevents other POKéMON from lowering its stats.", 3) new Ability(Abilities.BATTLE_ARMOR, "Battle Armor (N)", "The POKéMON is protected against critical hits.", 3),
.attr(ProtectStatAttr), new Ability(Abilities.BLAZE, "Blaze", "Powers up FIRE-type moves in a pinch.", 3)
new Ability(Abilities.CLOUD_NINE, "Cloud Nine (N)", "Eliminates the effects of weather.", 3), .attr(LowHpMoveTypePowerBoostAbAttr, Type.FIRE),
new Ability(Abilities.COLOR_CHANGE, "Color Change (N)", "Changes the POKéMON's type to the foe's move.", 3), new Ability(Abilities.CHLOROPHYLL, "Chlorophyll (N)", "Boosts the POKéMON's SPEED in sunshine.", 3),
new Ability(Abilities.COMPOUND_EYES, "Compound Eyes (N)", "The POKéMON's accuracy is boosted.", 3), new Ability(Abilities.CLEAR_BODY, "Clear Body", "Prevents other POKéMON from lowering its stats.", 3)
new Ability(Abilities.CUTE_CHARM, "Cute Charm (N)", "Contact with the POKéMON may cause infatuation.", 3), .attr(ProtectStatAttr),
new Ability(Abilities.DAMP, "Damp (N)", "Prevents the use of self-destructing moves.", 3), new Ability(Abilities.CLOUD_NINE, "Cloud Nine", "Eliminates the effects of non-severe weather.", 3)
new Ability(Abilities.DRIZZLE, "Drizzle (N)", "The POKéMON makes it rain when it enters a battle.", 3), .attr(SuppressWeatherEffectAbAttr),
new Ability(Abilities.DROUGHT, "Drought (N)", "Turns the sunlight harsh when the POKéMON enters a battle.", 3), new Ability(Abilities.COLOR_CHANGE, "Color Change (N)", "Changes the POKéMON's type to the foe's move.", 3),
new Ability(Abilities.EARLY_BIRD, "Early Bird (N)", "The POKéMON awakens quickly from sleep.", 3), new Ability(Abilities.COMPOUND_EYES, "Compound Eyes (N)", "The POKéMON's accuracy is boosted.", 3),
new Ability(Abilities.EFFECT_SPORE, "Effect Spore (N)", "Contact may poison or cause paralysis or sleep.", 3), new Ability(Abilities.CUTE_CHARM, "Cute Charm (N)", "Contact with the POKéMON may cause infatuation.", 3),
new Ability(Abilities.FLAME_BODY, "Flame Body (N)", "Contact with the POKéMON may burn the attacker.", 3), new Ability(Abilities.DAMP, "Damp (N)", "Prevents the use of self-destructing moves.", 3),
new Ability(Abilities.FLASH_FIRE, "Flash Fire (N)", "It powers up FIRE-type moves if it's hit by one.", 3), new Ability(Abilities.DRIZZLE, "Drizzle (N)", "The POKéMON makes it rain when it enters a battle.", 3),
new Ability(Abilities.FORECAST, "Forecast (N)", "Castform transforms with the weather.", 3), new Ability(Abilities.DROUGHT, "Drought (N)", "Turns the sunlight harsh when the POKéMON enters a battle.", 3),
new Ability(Abilities.GUTS, "Guts (N)", "Boosts ATTACK if there is a status problem.", 3), new Ability(Abilities.EARLY_BIRD, "Early Bird (N)", "The POKéMON awakens quickly from sleep.", 3),
new Ability(Abilities.HUGE_POWER, "Huge Power (N)", "Raises the POKéMON's ATTACK stat.", 3), new Ability(Abilities.EFFECT_SPORE, "Effect Spore (N)", "Contact may poison or cause paralysis or sleep.", 3),
new Ability(Abilities.HUSTLE, "Hustle (N)", "Boosts the ATTACK stat, but lowers accuracy.", 3), new Ability(Abilities.FLAME_BODY, "Flame Body (N)", "Contact with the POKéMON may burn the attacker.", 3),
new Ability(Abilities.HYPER_CUTTER, "Hyper Cutter", "Prevents other POKéMON from lowering ATTACK stat.", 3) new Ability(Abilities.FLASH_FIRE, "Flash Fire", "It powers up FIRE-type moves if it's hit by one.", 3)
.attr(ProtectStatAttr, BattleStat.ATK), .attr(TypeImmunityAddBattlerTagAbAttr, Type.FIRE, 1, BattlerTagType.FIRE_BOOST, (pokemon: Pokemon) => !pokemon.status || pokemon.status.effect !== StatusEffect.FREEZE),
new Ability(Abilities.ILLUMINATE, "Illuminate (N)", "Raises the likelihood of meeting wild POKéMON.", 3), new Ability(Abilities.FORECAST, "Forecast (N)", "Castform transforms with the weather.", 3),
new Ability(Abilities.IMMUNITY, "Immunity (N)", "Prevents the POKéMON from getting poisoned.", 3), new Ability(Abilities.GUTS, "Guts (N)", "Boosts ATTACK if there is a status problem.", 3),
new Ability(Abilities.INNER_FOCUS, "Inner Focus (N)", "The POKéMON is protected from flinching.", 3), new Ability(Abilities.HUGE_POWER, "Huge Power (N)", "Raises the POKéMON's ATTACK stat.", 3),
new Ability(Abilities.INSOMNIA, "Insomnia (N)", "Prevents the POKéMON from falling asleep.", 3), new Ability(Abilities.HUSTLE, "Hustle (N)", "Boosts the ATTACK stat, but lowers accuracy.", 3),
new Ability(Abilities.INTIMIDATE, "Intimidate (N)", "Lowers the foe's ATTACK stat.", 3), new Ability(Abilities.HYPER_CUTTER, "Hyper Cutter", "Prevents other POKéMON from lowering ATTACK stat.", 3)
new Ability(Abilities.KEEN_EYE, "Keen Eye", "Prevents other POKéMON from lowering accuracy.", 3) .attr(ProtectStatAttr, BattleStat.ATK),
.attr(ProtectStatAttr, BattleStat.ACC), new Ability(Abilities.ILLUMINATE, "Illuminate (N)", "Raises the likelihood of meeting wild POKéMON.", 3),
new Ability(Abilities.LEVITATE, "Levitate", "Gives immunity to GROUND-type moves.", 3) new Ability(Abilities.IMMUNITY, "Immunity (N)", "Prevents the POKéMON from getting poisoned.", 3),
.attr(TypeImmunityAttr, Type.FLYING), new Ability(Abilities.INNER_FOCUS, "Inner Focus (N)", "The POKéMON is protected from flinching.", 3),
new Ability(Abilities.LIGHTNING_ROD, "Lightning Rod", "Draws in all ELECTRIC-type moves to up SP. ATK.", 3) new Ability(Abilities.INSOMNIA, "Insomnia (N)", "Prevents the POKéMON from falling asleep.", 3),
.attr(TypeImmunityStatChangeAttr, Type.ELECTRIC, BattleStat.SPATK, 1), new Ability(Abilities.INTIMIDATE, "Intimidate (N)", "Lowers the foe's ATTACK stat.", 3),
new Ability(Abilities.LIMBER, "Limber (N)", "The POKéMON is protected from paralysis.", 3), new Ability(Abilities.KEEN_EYE, "Keen Eye", "Prevents other POKéMON from lowering accuracy.", 3)
new Ability(Abilities.LIQUID_OOZE, "Liquid Ooze (N)", "Damages attackers using any draining move.", 3), .attr(ProtectStatAttr, BattleStat.ACC),
new Ability(Abilities.MAGMA_ARMOR, "Magma Armor (N)", "Prevents the POKéMON from becoming frozen.", 3), new Ability(Abilities.LEVITATE, "Levitate", "Gives immunity to GROUND-type moves.", 3)
new Ability(Abilities.MAGNET_PULL, "Magnet Pull (N)", "Prevents STEEL-type POKéMON from escaping.", 3), .attr(TypeImmunityAbAttr, Type.FLYING, (pokemon: Pokemon) => !pokemon.getTag(BattlerTagType.IGNORE_FLYING)),
new Ability(Abilities.MARVEL_SCALE, "Marvel Scale (N)", "Ups DEFENSE if there is a status problem.", 3), new Ability(Abilities.LIGHTNING_ROD, "Lightning Rod", "Draws in all ELECTRIC-type moves to up SP. ATK.", 3)
new Ability(Abilities.MINUS, "Minus (N)", "Ups SP. ATK if another POKéMON has PLUS or MINUS.", 3), .attr(TypeImmunityStatChangeAbAttr, Type.ELECTRIC, BattleStat.SPATK, 1),
new Ability(Abilities.NATURAL_CURE, "Natural Cure (N)", "All status problems heal when it switches out.", 3), new Ability(Abilities.LIMBER, "Limber (N)", "The POKéMON is protected from paralysis.", 3),
new Ability(Abilities.OBLIVIOUS, "Oblivious (N)", "Prevents it from becoming infatuated.", 3), new Ability(Abilities.LIQUID_OOZE, "Liquid Ooze (N)", "Damages attackers using any draining move.", 3),
new Ability(Abilities.OVERGROW, "Overgrow (N)", "Powers up GRASS-type moves in a pinch.", 3), new Ability(Abilities.MAGMA_ARMOR, "Magma Armor (N)", "Prevents the POKéMON from becoming frozen.", 3),
new Ability(Abilities.OWN_TEMPO, "Own Tempo (N)", "Prevents the POKéMON from becoming confused.", 3), new Ability(Abilities.MAGNET_PULL, "Magnet Pull (N)", "Prevents STEEL-type POKéMON from escaping.", 3),
new Ability(Abilities.PICKUP, "Pickup (N)", "The POKéMON may pick up items.", 3), new Ability(Abilities.MARVEL_SCALE, "Marvel Scale (N)", "Ups DEFENSE if there is a status problem.", 3),
new Ability(Abilities.PLUS, "Plus (N)", "Ups SP. ATK if another POKéMON has PLUS or MINUS.", 3), new Ability(Abilities.MINUS, "Minus (N)", "Ups SP. ATK if another POKéMON has PLUS or MINUS.", 3),
new Ability(Abilities.POISON_POINT, "Poison Point (N)", "Contact with the POKéMON may poison the attacker.", 3), new Ability(Abilities.NATURAL_CURE, "Natural Cure (N)", "All status problems heal when it switches out.", 3),
new Ability(Abilities.PRESSURE, "Pressure (N)", "The POKéMON raises the foe's PP usage.", 3), new Ability(Abilities.OBLIVIOUS, "Oblivious (N)", "Prevents it from becoming infatuated.", 3),
new Ability(Abilities.PURE_POWER, "Pure Power (N)", "Raises the POKéMON's ATTACK stat.", 3), new Ability(Abilities.OVERGROW, "Overgrow", "Powers up GRASS-type moves in a pinch.", 3)
new Ability(Abilities.RAIN_DISH, "Rain Dish (N)", "The POKéMON gradually regains HP in rain.", 3), .attr(LowHpMoveTypePowerBoostAbAttr, Type.GRASS),
new Ability(Abilities.ROCK_HEAD, "Rock Head (N)", "Protects the POKéMON from recoil damage.", 3), new Ability(Abilities.OWN_TEMPO, "Own Tempo (N)", "Prevents the POKéMON from becoming confused.", 3),
new Ability(Abilities.ROUGH_SKIN, "Rough Skin (N)", "Inflicts damage to the attacker on contact.", 3), new Ability(Abilities.PICKUP, "Pickup (N)", "The POKéMON may pick up items.", 3),
new Ability(Abilities.RUN_AWAY, "Run Away (N)", "Enables a sure getaway from wild POKéMON.", 3), new Ability(Abilities.PLUS, "Plus (N)", "Ups SP. ATK if another POKéMON has PLUS or MINUS.", 3),
new Ability(Abilities.SAND_STREAM, "Sand Stream (N)", "The POKéMON summons a sandstorm in battle.", 3), new Ability(Abilities.POISON_POINT, "Poison Point (N)", "Contact with the POKéMON may poison the attacker.", 3),
new Ability(Abilities.SAND_VEIL, "Sand Veil (N)", "Boosts the POKéMON's evasion in a sandstorm.", 3), new Ability(Abilities.PRESSURE, "Pressure (N)", "The POKéMON raises the foe's PP usage.", 3),
new Ability(Abilities.SERENE_GRACE, "Serene Grace (N)", "Boosts the likelihood of added effects appearing.", 3), new Ability(Abilities.PURE_POWER, "Pure Power (N)", "Raises the POKéMON's ATTACK stat.", 3),
new Ability(Abilities.SHADOW_TAG, "Shadow Tag (N)", "Prevents the foe from escaping.", 3), new Ability(Abilities.RAIN_DISH, "Rain Dish", "The POKéMON gradually regains HP in rain.", 3)
new Ability(Abilities.SHED_SKIN, "Shed Skin (N)", "The POKéMON may heal its own status problems.", 3), .attr(WeatherHealAbAttr, WeatherType.RAIN, WeatherType.HEAVY_RAIN),
new Ability(Abilities.SHELL_ARMOR, "Shell Armor (N)", "The POKéMON is protected against critical hits.", 3), new Ability(Abilities.ROCK_HEAD, "Rock Head", "Protects the POKéMON from recoil damage.", 3)
new Ability(Abilities.SHIELD_DUST, "Shield Dust (N)", "Blocks the added effects of attacks taken.", 3), .attr(BlockRecoilDamageAttr),
new Ability(Abilities.SOUNDPROOF, "Soundproof (N)", "Gives immunity to sound-based moves.", 3), new Ability(Abilities.ROUGH_SKIN, "Rough Skin (N)", "Inflicts damage to the attacker on contact.", 3),
new Ability(Abilities.SPEED_BOOST, "Speed Boost (N)", "Its SPEED stat is gradually boosted.", 3), new Ability(Abilities.RUN_AWAY, "Run Away (N)", "Enables a sure getaway from wild POKéMON.", 3),
new Ability(Abilities.STATIC, "Static (N)", "Contact with the POKéMON may cause paralysis.", 3), new Ability(Abilities.SAND_STREAM, "Sand Stream (N)", "The POKéMON summons a sandstorm in battle.", 3),
new Ability(Abilities.STENCH, "Stench (N)", "The stench may cause the target to flinch.", 3), new Ability(Abilities.SAND_VEIL, "Sand Veil (N)", "Boosts the POKéMON's evasion in a sandstorm.", 3),
new Ability(Abilities.STICKY_HOLD, "Sticky Hold (N)", "Protects the POKéMON from item theft.", 3), new Ability(Abilities.SERENE_GRACE, "Serene Grace (N)", "Boosts the likelihood of added effects appearing.", 3),
new Ability(Abilities.STURDY, "Sturdy (N)", "It cannot be knocked out with one hit.", 3), new Ability(Abilities.SHADOW_TAG, "Shadow Tag (N)", "Prevents the foe from escaping.", 3),
new Ability(Abilities.SUCTION_CUPS, "Suction Cups (N)", "Negates all moves that force switching out.", 3), new Ability(Abilities.SHED_SKIN, "Shed Skin (N)", "The POKéMON may heal its own status problems.", 3),
new Ability(Abilities.SWARM, "Swarm (N)", "Powers up BUG-type moves in a pinch.", 3), new Ability(Abilities.SHELL_ARMOR, "Shell Armor (N)", "The POKéMON is protected against critical hits.", 3),
new Ability(Abilities.SWIFT_SWIM, "Swift Swim (N)", "Boosts the POKéMON's SPEED in rain.", 3), new Ability(Abilities.SHIELD_DUST, "Shield Dust (N)", "Blocks the added effects of attacks taken.", 3),
new Ability(Abilities.SYNCHRONIZE, "Synchronize (N)", "Passes a burn, poison, or paralysis to the foe.", 3), new Ability(Abilities.SOUNDPROOF, "Soundproof (N)", "Gives immunity to sound-based moves.", 3),
new Ability(Abilities.THICK_FAT, "Thick Fat (N)", "Ups resistance to Fire- and ICE-type moves.", 3), new Ability(Abilities.SPEED_BOOST, "Speed Boost (N)", "Its SPEED stat is gradually boosted.", 3),
new Ability(Abilities.TORRENT, "Torrent (N)", "Powers up WATER-type moves in a pinch.", 3), new Ability(Abilities.STATIC, "Static (N)", "Contact with the POKéMON may cause paralysis.", 3),
new Ability(Abilities.TRACE, "Trace (N)", "The POKéMON copies a foe's Ability.", 3), new Ability(Abilities.STENCH, "Stench (N)", "The stench may cause the target to flinch.", 3),
new Ability(Abilities.TRUANT, "Truant (N)", "POKéMON can't attack on consecutive turns.", 3), new Ability(Abilities.STICKY_HOLD, "Sticky Hold (N)", "Protects the POKéMON from item theft.", 3),
new Ability(Abilities.VITAL_SPIRIT, "Vital Spirit (N)", "Prevents the POKéMON from falling asleep.", 3), new Ability(Abilities.STURDY, "Sturdy (N)", "It cannot be knocked out with one hit.", 3),
new Ability(Abilities.VOLT_ABSORB, "Volt Absorb (N)", "Restores HP if hit by an ELECTRIC-type move.", 3), new Ability(Abilities.SUCTION_CUPS, "Suction Cups (N)", "Negates all moves that force switching out.", 3),
new Ability(Abilities.WATER_ABSORB, "Water Absorb (N)", "Restores HP if hit by a WATER-type move.", 3), new Ability(Abilities.SWARM, "Swarm", "Powers up BUG-type moves in a pinch.", 3)
new Ability(Abilities.WATER_VEIL, "Water Veil (N)", "Prevents the POKéMON from getting a burn.", 3), .attr(LowHpMoveTypePowerBoostAbAttr, Type.BUG),
new Ability(Abilities.WHITE_SMOKE, "White Smoke", "Prevents other POKéMON from lowering its stats.", 3) new Ability(Abilities.SWIFT_SWIM, "Swift Swim (N)", "Boosts the POKéMON's SPEED in rain.", 3),
.attr(ProtectStatAttr), new Ability(Abilities.SYNCHRONIZE, "Synchronize (N)", "Passes a burn, poison, or paralysis to the foe.", 3),
new Ability(Abilities.WONDER_GUARD, "Wonder Guard (N)", "Only supereffective moves will hit.", 3), new Ability(Abilities.THICK_FAT, "Thick Fat (N)", "Ups resistance to Fire- and ICE-type moves.", 3),
new Ability(Abilities.ADAPTABILITY, "Adaptability (N)", "Powers up moves of the same type.", 4), new Ability(Abilities.TORRENT, "Torrent", "Powers up WATER-type moves in a pinch.", 3)
new Ability(Abilities.AFTERMATH, "Aftermath (N)", "Damages the attacker landing the finishing hit.", 4), .attr(LowHpMoveTypePowerBoostAbAttr, Type.WATER),
new Ability(Abilities.ANGER_POINT, "Anger Point (N)", "Maxes ATTACK after taking a critical hit.", 4), new Ability(Abilities.TRACE, "Trace (N)", "The POKéMON copies a foe's Ability.", 3),
new Ability(Abilities.ANTICIPATION, "Anticipation (N)", "Senses a foe's dangerous moves.", 4), new Ability(Abilities.TRUANT, "Truant (N)", "POKéMON can't attack on consecutive turns.", 3),
new Ability(Abilities.BAD_DREAMS, "Bad Dreams (N)", "Reduces a sleeping foe's HP.", 4), new Ability(Abilities.VITAL_SPIRIT, "Vital Spirit (N)", "Prevents the POKéMON from falling asleep.", 3),
new Ability(Abilities.DOWNLOAD, "Download (N)", "Adjusts power according to a foe's defenses.", 4), new Ability(Abilities.VOLT_ABSORB, "Volt Absorb (N)", "Restores HP if hit by an ELECTRIC-type move.", 3),
new Ability(Abilities.DRY_SKIN, "Dry Skin (N)", "Reduces HP if it is hot. Water restores HP.", 4), new Ability(Abilities.WATER_ABSORB, "Water Absorb (N)", "Restores HP if hit by a WATER-type move.", 3),
new Ability(Abilities.FILTER, "Filter (N)", "Reduces damage from super-effective attacks.", 4), new Ability(Abilities.WATER_VEIL, "Water Veil (N)", "Prevents the POKéMON from getting a burn.", 3),
new Ability(Abilities.FLOWER_GIFT, "Flower Gift (N)", "Powers up party POKéMON when it is sunny.", 4), new Ability(Abilities.WHITE_SMOKE, "White Smoke", "Prevents other POKéMON from lowering its stats.", 3)
new Ability(Abilities.FOREWARN, "Forewarn (N)", "Determines what moves a foe has.", 4), .attr(ProtectStatAttr),
new Ability(Abilities.FRISK, "Frisk (N)", "The POKéMON can check a foe's held item.", 4), new Ability(Abilities.WONDER_GUARD, "Wonder Guard (N)", "Only supereffective moves will hit.", 3),
new Ability(Abilities.GLUTTONY, "Gluttony (N)", "Encourages the early use of a held Berry.", 4), new Ability(Abilities.ADAPTABILITY, "Adaptability (N)", "Powers up moves of the same type.", 4),
new Ability(Abilities.HEATPROOF, "Heatproof (N)", "Weakens the power of FIRE-type moves.", 4), new Ability(Abilities.AFTERMATH, "Aftermath (N)", "Damages the attacker landing the finishing hit.", 4),
new Ability(Abilities.HONEY_GATHER, "Honey Gather (N)", "The POKéMON may gather Honey from somewhere.", 4), new Ability(Abilities.ANGER_POINT, "Anger Point (N)", "Maxes ATTACK after taking a critical hit.", 4),
new Ability(Abilities.HYDRATION, "Hydration (N)", "Heals status problems if it is raining.", 4), new Ability(Abilities.ANTICIPATION, "Anticipation (N)", "Senses a foe's dangerous moves.", 4),
new Ability(Abilities.ICE_BODY, "Ice Body (N)", "The POKéMON gradually regains HP in a hailstorm.", 4), new Ability(Abilities.BAD_DREAMS, "Bad Dreams (N)", "Reduces a sleeping foe's HP.", 4),
new Ability(Abilities.IRON_FIST, "Iron Fist (N)", "Boosts the power of punching moves.", 4), new Ability(Abilities.DOWNLOAD, "Download (N)", "Adjusts power according to a foe's defenses.", 4),
new Ability(Abilities.KLUTZ, "Klutz (N)", "The POKéMON can't use any held items.", 4), new Ability(Abilities.DRY_SKIN, "Dry Skin (N)", "Reduces HP if it is hot. Water restores HP.", 4),
new Ability(Abilities.LEAF_GUARD, "Leaf Guard (N)", "Prevents problems with status in sunny weather.", 4), new Ability(Abilities.FILTER, "Filter (N)", "Reduces damage from super-effective attacks.", 4),
new Ability(Abilities.MAGIC_GUARD, "Magic Guard (N)", "Protects the POKéMON from indirect damage.", 4), new Ability(Abilities.FLOWER_GIFT, "Flower Gift (N)", "Powers up party POKéMON when it is sunny.", 4),
new Ability(Abilities.MOLD_BREAKER, "Mold Breaker (N)", "Moves can be used regardless of Abilities.", 4), new Ability(Abilities.FOREWARN, "Forewarn (N)", "Determines what moves a foe has.", 4),
new Ability(Abilities.MOTOR_DRIVE, "Motor Drive (N)", "Raises SPEED if hit by an ELECTRIC-type move.", 4), new Ability(Abilities.FRISK, "Frisk (N)", "The POKéMON can check a foe's held item.", 4),
new Ability(Abilities.MULTITYPE, "Multitype (N)", "Changes type to match the held Plate.", 4), new Ability(Abilities.GLUTTONY, "Gluttony (N)", "Encourages the early use of a held Berry.", 4),
new Ability(Abilities.NO_GUARD, "No Guard (N)", "Ensures attacks by or against the POKéMON land.", 4), new Ability(Abilities.HEATPROOF, "Heatproof (N)", "Weakens the power of FIRE-type moves.", 4),
new Ability(Abilities.NORMALIZE, "Normalize (N)", "All the POKéMON's moves become the NORMAL type.", 4), new Ability(Abilities.HONEY_GATHER, "Honey Gather (N)", "The POKéMON may gather Honey from somewhere.", 4),
new Ability(Abilities.POISON_HEAL, "Poison Heal (N)", "Restores HP if the POKéMON is poisoned.", 4), new Ability(Abilities.HYDRATION, "Hydration (N)", "Heals status problems if it is raining.", 4),
new Ability(Abilities.QUICK_FEET, "Quick Feet (N)", "Boosts SPEED if there is a status problem.", 4), new Ability(Abilities.ICE_BODY, "Ice Body", "The POKéMON gradually regains HP in a hailstorm.", 4)
new Ability(Abilities.RECKLESS, "Reckless (N)", "Powers up moves that have recoil damage.", 4), .attr(WeatherHealAbAttr, WeatherType.HAIL),
new Ability(Abilities.RIVALRY, "Rivalry (N)", "Deals more damage to a POKéMON of same gender.", 4), new Ability(Abilities.IRON_FIST, "Iron Fist (N)", "Boosts the power of punching moves.", 4),
new Ability(Abilities.SCRAPPY, "Scrappy (N)", "Enables moves to hit GHOST-type POKéMON.", 4), new Ability(Abilities.KLUTZ, "Klutz (N)", "The POKéMON can't use any held items.", 4),
new Ability(Abilities.SIMPLE, "Simple (N)", "Doubles all stat changes.", 4), new Ability(Abilities.LEAF_GUARD, "Leaf Guard (N)", "Prevents problems with status in sunny weather.", 4),
new Ability(Abilities.SKILL_LINK, "Skill Link (N)", "Increases the frequency of multi-strike moves.", 4), new Ability(Abilities.MAGIC_GUARD, "Magic Guard (N)", "Protects the POKéMON from indirect damage.", 4),
new Ability(Abilities.SLOW_START, "Slow Start (N)", "Temporarily halves ATTACK and SPEED.", 4), new Ability(Abilities.MOLD_BREAKER, "Mold Breaker (N)", "Moves can be used regardless of Abilities.", 4),
new Ability(Abilities.SNIPER, "Sniper (N)", "Powers up moves if they become critical hits.", 4), new Ability(Abilities.MOTOR_DRIVE, "Motor Drive (N)", "Raises SPEED if hit by an ELECTRIC-type move.", 4),
new Ability(Abilities.SNOW_CLOAK, "Snow Cloak (N)", "Raises evasion in a hailstorm.", 4), new Ability(Abilities.MULTITYPE, "Multitype (N)", "Changes type to match the held Plate.", 4),
new Ability(Abilities.SNOW_WARNING, "Snow Warning (N)", "The POKéMON summons a hailstorm in battle.", 4), new Ability(Abilities.NO_GUARD, "No Guard (N)", "Ensures attacks by or against the POKéMON land.", 4),
new Ability(Abilities.SOLAR_POWER, "Solar Power (N)", "In sunshine, SP. ATK is boosted but HP decreases.", 4), new Ability(Abilities.NORMALIZE, "Normalize (N)", "All the POKéMON's moves become the NORMAL type.", 4),
new Ability(Abilities.SOLID_ROCK, "Solid Rock (N)", "Reduces damage from super-effective attacks.", 4), new Ability(Abilities.POISON_HEAL, "Poison Heal (N)", "Restores HP if the POKéMON is poisoned.", 4),
new Ability(Abilities.STALL, "Stall (N)", "The POKéMON moves after all other POKéMON do.", 4), new Ability(Abilities.QUICK_FEET, "Quick Feet (N)", "Boosts SPEED if there is a status problem.", 4),
new Ability(Abilities.STEADFAST, "Steadfast (N)", "Raises SPEED each time the POKéMON flinches.", 4), new Ability(Abilities.RECKLESS, "Reckless", "Powers up moves that have recoil damage.", 4)
new Ability(Abilities.STORM_DRAIN, "Storm Drain", "Draws in all WATER-type moves to up SP. ATK.", 4) .attr(RecoilMovePowerBoostAbAttr),
.attr(TypeImmunityStatChangeAttr, Type.WATER, BattleStat.SPATK, 1), new Ability(Abilities.RIVALRY, "Rivalry (N)", "Deals more damage to a POKéMON of same gender.", 4),
new Ability(Abilities.SUPER_LUCK, "Super Luck (N)", "Heightens the critical-hit ratios of moves.", 4), new Ability(Abilities.SCRAPPY, "Scrappy (N)", "Enables moves to hit GHOST-type POKéMON.", 4),
new Ability(Abilities.TANGLED_FEET, "Tangled Feet (N)", "Raises evasion if the POKéMON is confused.", 4), new Ability(Abilities.SIMPLE, "Simple (N)", "Doubles all stat changes.", 4),
new Ability(Abilities.TECHNICIAN, "Technician (N)", "Powers up the POKéMON's weaker moves.", 4), new Ability(Abilities.SKILL_LINK, "Skill Link (N)", "Increases the frequency of multi-strike moves.", 4),
new Ability(Abilities.TINTED_LENS, "Tinted Lens (N)", "Powers up “not very effective” moves.", 4), new Ability(Abilities.SLOW_START, "Slow Start (N)", "Temporarily halves ATTACK and SPEED.", 4),
new Ability(Abilities.UNAWARE, "Unaware (N)", "Ignores any stat changes in the POKéMON.", 4), new Ability(Abilities.SNIPER, "Sniper (N)", "Powers up moves if they become critical hits.", 4),
new Ability(Abilities.UNBURDEN, "Unburden (N)", "Raises SPEED if a held item is used.", 4), new Ability(Abilities.SNOW_CLOAK, "Snow Cloak (N)", "Raises evasion in a hailstorm.", 4),
new Ability(Abilities.ANALYTIC, "Analytic (N)", "Boosts move power when the POKéMON moves last.", 5), new Ability(Abilities.SNOW_WARNING, "Snow Warning (N)", "The POKéMON summons a hailstorm in battle.", 4),
new Ability(Abilities.BIG_PECKS, "Big Pecks", "Protects the POKéMON from DEFENSE-lowering attacks.", 5) new Ability(Abilities.SOLAR_POWER, "Solar Power (N)", "In sunshine, SP. ATK is boosted but HP decreases.", 4),
.attr(ProtectStatAttr, BattleStat.DEF), new Ability(Abilities.SOLID_ROCK, "Solid Rock (N)", "Reduces damage from super-effective attacks.", 4),
new Ability(Abilities.CONTRARY, "Contrary (N)", "Makes stat changes have an opposite effect.", 5), new Ability(Abilities.STALL, "Stall (N)", "The POKéMON moves after all other POKéMON do.", 4),
new Ability(Abilities.CURSED_BODY, "Cursed Body (N)", "May disable a move used on the POKéMON.", 5), new Ability(Abilities.STEADFAST, "Steadfast (N)", "Raises SPEED each time the POKéMON flinches.", 4),
new Ability(Abilities.DEFEATIST, "Defeatist (N)", "Lowers stats when HP drops below half.", 5), new Ability(Abilities.STORM_DRAIN, "Storm Drain", "Draws in all WATER-type moves to up SP. ATK.", 4)
new Ability(Abilities.DEFIANT, "Defiant (N)", "Sharply raises ATTACK when the POKéMON's stats are lowered.", 5), .attr(TypeImmunityStatChangeAbAttr, Type.WATER, BattleStat.SPATK, 1),
new Ability(Abilities.FLARE_BOOST, "Flare Boost (N)", "Powers up special attacks when burned.", 5), new Ability(Abilities.SUPER_LUCK, "Super Luck (N)", "Heightens the critical-hit ratios of moves.", 4),
new Ability(Abilities.FRIEND_GUARD, "Friend Guard (N)", "Reduces damage done to allies.", 5), new Ability(Abilities.TANGLED_FEET, "Tangled Feet (N)", "Raises evasion if the POKéMON is confused.", 4),
new Ability(Abilities.HARVEST, "Harvest (N)", "May create another Berry after one is used.", 5), new Ability(Abilities.TECHNICIAN, "Technician (N)", "Powers up the POKéMON's weaker moves.", 4),
new Ability(Abilities.HEALER, "Healer (N)", "May heal an ally's status conditions.", 5), new Ability(Abilities.TINTED_LENS, "Tinted Lens (N)", "Powers up “not very effective” moves.", 4),
new Ability(Abilities.HEAVY_METAL, "Heavy Metal (N)", "Doubles the POKéMON's weight.", 5), new Ability(Abilities.UNAWARE, "Unaware (N)", "Ignores any stat changes in the POKéMON.", 4),
new Ability(Abilities.ILLUSION, "Illusion (N)", "Enters battle disguised as the last POKéMON in the party.", 5), new Ability(Abilities.UNBURDEN, "Unburden (N)", "Raises SPEED if a held item is used.", 4),
new Ability(Abilities.IMPOSTER, "Imposter (N)", "It transforms itself into the POKéMON it is facing.", 5), new Ability(Abilities.ANALYTIC, "Analytic (N)", "Boosts move power when the POKéMON moves last.", 5),
new Ability(Abilities.INFILTRATOR, "Infiltrator (N)", "Passes through the foe's barrier and strikes.", 5), new Ability(Abilities.BIG_PECKS, "Big Pecks", "Protects the POKéMON from DEFENSE-lowering attacks.", 5)
new Ability(Abilities.IRON_BARBS, "Iron Barbs (N)", "Inflicts damage to the POKéMON on contact.", 5), .attr(ProtectStatAttr, BattleStat.DEF),
new Ability(Abilities.JUSTIFIED, "Justified (N)", "Raises ATTACK when hit by a DARK-type move.", 5), new Ability(Abilities.CONTRARY, "Contrary (N)", "Makes stat changes have an opposite effect.", 5),
new Ability(Abilities.LIGHT_METAL, "Light Metal (N)", "Halves the POKéMON's weight.", 5), new Ability(Abilities.CURSED_BODY, "Cursed Body (N)", "May disable a move used on the POKéMON.", 5),
new Ability(Abilities.MAGIC_BOUNCE, "Magic Bounce (N)", "Reflects status- changing moves.", 5), new Ability(Abilities.DEFEATIST, "Defeatist (N)", "Lowers stats when HP drops below half.", 5),
new Ability(Abilities.MOODY, "Moody (N)", "Raises one stat and lowers another.", 5), new Ability(Abilities.DEFIANT, "Defiant (N)", "Sharply raises ATTACK when the POKéMON's stats are lowered.", 5),
new Ability(Abilities.MOXIE, "Moxie (N)", "Boosts ATTACK after knocking out any POKéMON.", 5), new Ability(Abilities.FLARE_BOOST, "Flare Boost (N)", "Powers up special attacks when burned.", 5),
new Ability(Abilities.MULTISCALE, "Multiscale (N)", "Reduces damage when HP is full.", 5), new Ability(Abilities.FRIEND_GUARD, "Friend Guard (N)", "Reduces damage done to allies.", 5),
new Ability(Abilities.MUMMY, "Mummy (N)", "Contact with this POKéMON spreads this Ability.", 5), new Ability(Abilities.HARVEST, "Harvest (N)", "May create another Berry after one is used.", 5),
new Ability(Abilities.OVERCOAT, "Overcoat (N)", "Protects the POKéMON from weather damage.", 5), new Ability(Abilities.HEALER, "Healer (N)", "May heal an ally's status conditions.", 5),
new Ability(Abilities.PICKPOCKET, "Pickpocket (N)", "Steals an item when hit by another POKéMON.", 5), new Ability(Abilities.HEAVY_METAL, "Heavy Metal (N)", "Doubles the POKéMON's weight.", 5),
new Ability(Abilities.POISON_TOUCH, "Poison Touch (N)", "May poison targets when a POKéMON makes contact.", 5), new Ability(Abilities.ILLUSION, "Illusion (N)", "Enters battle disguised as the last POKéMON in the party.", 5),
new Ability(Abilities.PRANKSTER, "Prankster (N)", "Gives priority to a status move.", 5), new Ability(Abilities.IMPOSTER, "Imposter (N)", "It transforms itself into the POKéMON it is facing.", 5),
new Ability(Abilities.RATTLED, "Rattled (N)", "BUG, GHOST or DARK type moves scare it and boost its SPEED.", 5), new Ability(Abilities.INFILTRATOR, "Infiltrator (N)", "Passes through the foe's barrier and strikes.", 5),
new Ability(Abilities.REGENERATOR, "Regenerator (N)", "Restores a little HP when withdrawn from battle.", 5), new Ability(Abilities.IRON_BARBS, "Iron Barbs (N)", "Inflicts damage to the POKéMON on contact.", 5),
new Ability(Abilities.SAND_FORCE, "Sand Force (N)", "Boosts certain moves' power in a sandstorm.", 5), new Ability(Abilities.JUSTIFIED, "Justified (N)", "Raises ATTACK when hit by a DARK-type move.", 5),
new Ability(Abilities.SAND_RUSH, "Sand Rush (N)", "Boosts the POKéMON's SPEED in a sandstorm.", 5), new Ability(Abilities.LIGHT_METAL, "Light Metal (N)", "Halves the POKéMON's weight.", 5),
new Ability(Abilities.SAP_SIPPER, "Sap Sipper (N)", "Boosts ATTACK when hit by a GRASS-type move.", 5), new Ability(Abilities.MAGIC_BOUNCE, "Magic Bounce (N)", "Reflects status- changing moves.", 5),
new Ability(Abilities.SHEER_FORCE, "Sheer Force (N)", "Removes added effects to increase move damage.", 5), new Ability(Abilities.MOODY, "Moody (N)", "Raises one stat and lowers another.", 5),
new Ability(Abilities.TELEPATHY, "Telepathy (N)", "Anticipates an ally's ATTACK and dodges it.", 5), new Ability(Abilities.MOXIE, "Moxie (N)", "Boosts ATTACK after knocking out any POKéMON.", 5),
new Ability(Abilities.TERAVOLT, "Teravolt (N)", "Moves can be used regardless of Abilities.", 5), new Ability(Abilities.MULTISCALE, "Multiscale (N)", "Reduces damage when HP is full.", 5),
new Ability(Abilities.TOXIC_BOOST, "Toxic Boost (N)", "Powers up physical attacks when poisoned.", 5), new Ability(Abilities.MUMMY, "Mummy (N)", "Contact with this POKéMON spreads this Ability.", 5),
new Ability(Abilities.TURBOBLAZE, "Turboblaze (N)", "Moves can be used regardless of Abilities.", 5), new Ability(Abilities.OVERCOAT, "Overcoat", "Protects the POKéMON from weather damage.", 5)
new Ability(Abilities.UNNERVE, "Unnerve (N)", "Makes the foe nervous and unable to eat Berries.", 5), .attr(BlockWeatherDamageAttr),
new Ability(Abilities.VICTORY_STAR, "Victory Star (N)", "Boosts the accuracy of its allies and itself.", 5), new Ability(Abilities.PICKPOCKET, "Pickpocket (N)", "Once per battle, steals an item when hit by another POKéMON.", 5),
new Ability(Abilities.WEAK_ARMOR, "Weak Armor (N)", "Physical attacks lower DEFENSE and raise SPEED.", 5), new Ability(Abilities.POISON_TOUCH, "Poison Touch (N)", "May poison targets when a POKéMON makes contact.", 5),
new Ability(Abilities.WONDER_SKIN, "Wonder Skin (N)", "Makes status-changing moves more likely to miss.", 5), new Ability(Abilities.PRANKSTER, "Prankster (N)", "Gives priority to a status move.", 5),
new Ability(Abilities.ZEN_MODE, "Zen Mode (N)", "Changes form when HP drops below half.", 5), new Ability(Abilities.RATTLED, "Rattled (N)", "BUG, GHOST or DARK type moves scare it and boost its SPEED.", 5),
new Ability(Abilities.COMPETITIVE, "Competitive (N)", "Sharply raises SP. ATK when the POKéMON's stats are lowered.", 6), new Ability(Abilities.REGENERATOR, "Regenerator (N)", "Restores a little HP when withdrawn from battle.", 5),
new Ability(Abilities.DARK_AURA, "Dark Aura (N)", "Raises power of DARK type moves for all POKéMON in battle.", 6), new Ability(Abilities.SAND_FORCE, "Sand Force (N)", "Boosts certain moves' power in a sandstorm.", 5),
new Ability(Abilities.FAIRY_AURA, "Fairy Aura (N)", "Raises power of FAIRY type moves for all POKéMON in battle.", 6), new Ability(Abilities.SAND_RUSH, "Sand Rush (N)", "Boosts the POKéMON's SPEED in a sandstorm.", 5),
new Ability(Abilities.PROTEAN, "Protean (N)", "Changes the POKéMON's type to its last used move.", 6), new Ability(Abilities.SAP_SIPPER, "Sap Sipper (N)", "Boosts ATTACK when hit by a GRASS-type move.", 5),
new Ability(Abilities.SLUSH_RUSH, "Slush Rush (N)", "Boosts the POKéMON's SPEED stat in a hailstorm.", 7), new Ability(Abilities.SHEER_FORCE, "Sheer Force (N)", "Removes added effects to increase move damage.", 5),
new Ability(Abilities.NEUTRALIZING_GAS, "Neutralizing Gas (N)", "Neutralizes abilities of all POKéMON in battle.", 8) new Ability(Abilities.TELEPATHY, "Telepathy (N)", "Anticipates an ally's ATTACK and dodges it.", 5),
]; new Ability(Abilities.TERAVOLT, "Teravolt (N)", "Moves can be used regardless of Abilities.", 5),
new Ability(Abilities.TOXIC_BOOST, "Toxic Boost (N)", "Powers up physical attacks when poisoned.", 5),
new Ability(Abilities.TURBOBLAZE, "Turboblaze (N)", "Moves can be used regardless of Abilities.", 5),
new Ability(Abilities.UNNERVE, "Unnerve (N)", "Makes the foe nervous and unable to eat Berries.", 5),
new Ability(Abilities.VICTORY_STAR, "Victory Star (N)", "Boosts the accuracy of its allies and itself.", 5),
new Ability(Abilities.WEAK_ARMOR, "Weak Armor (N)", "Physical attacks lower DEFENSE and raise SPEED.", 5),
new Ability(Abilities.WONDER_SKIN, "Wonder Skin (N)", "Makes status-changing moves more likely to miss.", 5),
new Ability(Abilities.ZEN_MODE, "Zen Mode (N)", "Changes form when HP drops below half.", 5),
new Ability(Abilities.COMPETITIVE, "Competitive (N)", "Sharply raises SP. ATK when the POKéMON's stats are lowered.", 6),
new Ability(Abilities.DARK_AURA, "Dark Aura (N)", "Raises power of DARK type moves for all POKéMON in battle.", 6),
new Ability(Abilities.FAIRY_AURA, "Fairy Aura (N)", "Raises power of FAIRY type moves for all POKéMON in battle.", 6),
new Ability(Abilities.PROTEAN, "Protean (N)", "Changes the POKéMON's type to its last used move.", 6),
new Ability(Abilities.SLUSH_RUSH, "Slush Rush (N)", "Boosts the POKéMON's SPEED stat in a hailstorm.", 7),
new Ability(Abilities.NEUTRALIZING_GAS, "Neutralizing Gas (N)", "Neutralizes abilities of all POKéMON in battle.", 8)
);
}

View File

@ -31,6 +31,7 @@ export enum BattlerTagType {
FLYING, FLYING,
UNDERGROUND, UNDERGROUND,
HIDDEN, HIDDEN,
FIRE_BOOST,
CRIT_BOOST, CRIT_BOOST,
NO_CRIT, NO_CRIT,
BYPASS_SLEEP, BYPASS_SLEEP,
@ -464,6 +465,20 @@ export class HideSpriteTag extends BattlerTag {
} }
} }
export class TypeBoostTag extends BattlerTag {
public boostedType: Type;
constructor(tagType: BattlerTagType, sourceMove: Moves, boostedType: Type) {
super(tagType, BattlerTagLapseType.TURN_END, 1, sourceMove);
this.boostedType = boostedType;
}
lapse(pokemon: Pokemon, lapseType: BattlerTagLapseType): boolean {
return lapseType !== BattlerTagLapseType.CUSTOM || super.lapse(pokemon, lapseType);
}
}
export class CritBoostTag extends BattlerTag { export class CritBoostTag extends BattlerTag {
constructor(tagType: BattlerTagType, sourceMove: Moves) { constructor(tagType: BattlerTagType, sourceMove: Moves) {
super(tagType, BattlerTagLapseType.TURN_END, 1, sourceMove); super(tagType, BattlerTagLapseType.TURN_END, 1, sourceMove);
@ -526,6 +541,8 @@ export function getBattlerTag(tagType: BattlerTagType, turnCount: integer, sourc
case BattlerTagType.UNDERGROUND: case BattlerTagType.UNDERGROUND:
case BattlerTagType.HIDDEN: case BattlerTagType.HIDDEN:
return new HideSpriteTag(tagType, turnCount, sourceMove); return new HideSpriteTag(tagType, turnCount, sourceMove);
case BattlerTagType.FIRE_BOOST:
return new TypeBoostTag(tagType, sourceMove, Type.FIRE);
case BattlerTagType.CRIT_BOOST: case BattlerTagType.CRIT_BOOST:
return new CritBoostTag(tagType, sourceMove); return new CritBoostTag(tagType, sourceMove);
case BattlerTagType.NO_CRIT: case BattlerTagType.NO_CRIT:

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -4,6 +4,8 @@ import Pokemon from "../pokemon";
import { Type } from "./type"; import { Type } from "./type";
import Move, { AttackMove } from "./move"; import Move, { AttackMove } from "./move";
import * as Utils from "../utils"; import * as Utils from "../utils";
import BattleScene from "../battle-scene";
import { SuppressWeatherEffectAbAttr, applyPreWeatherEffectAbAttrs } from "./ability";
export enum WeatherType { export enum WeatherType {
NONE, NONE,
@ -96,6 +98,21 @@ export class Weather {
return false; return false;
} }
isEffectSuppressed(scene: BattleScene): boolean {
const playerPokemon = scene.getPlayerPokemon();
const enemyPokemon = scene.getEnemyPokemon();
const cancelled = new Utils.BooleanHolder(false);
if (playerPokemon)
applyPreWeatherEffectAbAttrs(SuppressWeatherEffectAbAttr, playerPokemon, this, cancelled, true);
if (enemyPokemon)
applyPreWeatherEffectAbAttrs(SuppressWeatherEffectAbAttr, enemyPokemon, this, cancelled, true);
return cancelled.value;
}
} }
export function getWeatherStartMessage(weatherType: WeatherType) { export function getWeatherStartMessage(weatherType: WeatherType) {

View File

@ -1,7 +1,7 @@
import Phaser from 'phaser'; import Phaser from 'phaser';
import BattleScene from './battle-scene'; import BattleScene from './battle-scene';
import BattleInfo, { PlayerBattleInfo, EnemyBattleInfo } from './ui/battle-info'; import BattleInfo, { PlayerBattleInfo, EnemyBattleInfo } from './ui/battle-info';
import Move, { StatChangeAttr, HighCritAttr, HitsTagAttr, applyMoveAttrs, FixedDamageAttr, VariablePowerAttr, Moves, allMoves, MoveCategory } from "./data/move"; import Move, { StatChangeAttr, HighCritAttr, HitsTagAttr, applyMoveAttrs, FixedDamageAttr, VariablePowerAttr, Moves, allMoves, MoveCategory, TypelessAttr } from "./data/move";
import { pokemonLevelMoves } from './data/pokemon-level-moves'; import { pokemonLevelMoves } from './data/pokemon-level-moves';
import { default as PokemonSpecies, PokemonSpeciesForm, getPokemonSpecies } from './data/pokemon-species'; import { default as PokemonSpecies, PokemonSpeciesForm, getPokemonSpecies } from './data/pokemon-species';
import * as Utils from './utils'; import * as Utils from './utils';
@ -17,13 +17,13 @@ import { tmSpecies } from './data/tms';
import { pokemonEvolutions, pokemonPrevolutions, SpeciesEvolution, SpeciesEvolutionCondition } from './data/pokemon-evolutions'; import { pokemonEvolutions, pokemonPrevolutions, SpeciesEvolution, SpeciesEvolutionCondition } from './data/pokemon-evolutions';
import { DamagePhase, FaintPhase } from './battle-phases'; import { DamagePhase, FaintPhase } from './battle-phases';
import { BattleStat } from './data/battle-stat'; import { BattleStat } from './data/battle-stat';
import { BattlerTag, BattlerTagLapseType, BattlerTagType, getBattlerTag } from './data/battler-tag'; import { BattlerTag, BattlerTagLapseType, BattlerTagType, TypeBoostTag, getBattlerTag } from './data/battler-tag';
import { Species } from './data/species'; import { Species } from './data/species';
import { WeatherType } from './data/weather'; import { WeatherType } from './data/weather';
import { TempBattleStat } from './data/temp-battle-stat'; import { TempBattleStat } from './data/temp-battle-stat';
import { WeakenMoveTypeTag } from './data/arena-tag'; import { WeakenMoveTypeTag } from './data/arena-tag';
import { Biome } from './data/biome'; import { Biome } from './data/biome';
import { Abilities, Ability, TypeImmunityAttr, abilities, applyPreDefendAbilityAttrs } from './data/ability'; import { Abilities, Ability, TypeImmunityAbAttr, VariableMovePowerAbAttr, abilities, applyPreAttackAbAttrs, applyPreDefendAbAttrs } from './data/ability';
export default abstract class Pokemon extends Phaser.GameObjects.Container { export default abstract class Pokemon extends Phaser.GameObjects.Container {
public id: integer; public id: integer;
@ -350,6 +350,10 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
return abilities[this.species.getAbility(this.abilityIndex)]; return abilities[this.species.getAbility(this.abilityIndex)];
} }
canApplyAbility(): boolean {
return !this.getAbility().conditions.find(condition => !condition(this));
}
getAttackMoveEffectiveness(moveType: Type): TypeDamageMultiplier { getAttackMoveEffectiveness(moveType: Type): TypeDamageMultiplier {
const types = this.getTypes(); const types = this.getTypes();
return getTypeDamageMultiplier(moveType, types[0]) * (types.length ? getTypeDamageMultiplier(moveType, types[1]) : 1) as TypeDamageMultiplier; return getTypeDamageMultiplier(moveType, types[0]) * (types.length ? getTypeDamageMultiplier(moveType, types[1]) : 1) as TypeDamageMultiplier;
@ -423,6 +427,9 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
console.log(allMoves[movePool[moveIndex]]); console.log(allMoves[movePool[moveIndex]]);
movePool.splice(moveIndex, 1); movePool.splice(moveIndex, 1);
} }
if (this.isPlayer())
this.moveset[1].moveId = Moves.TAKE_DOWN;
} }
trySelectMove(moveIndex: integer): boolean { trySelectMove(moveIndex: integer): boolean {
@ -484,18 +491,28 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
case MoveCategory.PHYSICAL: case MoveCategory.PHYSICAL:
case MoveCategory.SPECIAL: case MoveCategory.SPECIAL:
const isPhysical = moveCategory === MoveCategory.PHYSICAL; const isPhysical = moveCategory === MoveCategory.PHYSICAL;
const typeless = move.getAttrs(TypelessAttr).length
const cancelled = new Utils.BooleanHolder(false); const cancelled = new Utils.BooleanHolder(false);
const power = new Utils.NumberHolder(move.power); const power = new Utils.NumberHolder(move.power);
const typeMultiplier = new Utils.NumberHolder(getTypeDamageMultiplier(move.type, this.getSpeciesForm().type1) * (this.getSpeciesForm().type2 !== null ? getTypeDamageMultiplier(move.type, this.getSpeciesForm().type2) : 1)); const typeMultiplier = new Utils.NumberHolder(!typeless
const weatherTypeMultiplier = this.scene.arena.getAttackTypeMultiplier(move.type); ? getTypeDamageMultiplier(move.type, this.getSpeciesForm().type1) * (this.getSpeciesForm().type2 !== null ? getTypeDamageMultiplier(move.type, this.getSpeciesForm().type2) : 1)
applyPreDefendAbilityAttrs(TypeImmunityAttr, this, source, battlerMove, cancelled, typeMultiplier); : 1);
if (typeless)
typeMultiplier.value = 1;
applyPreAttackAbAttrs(VariableMovePowerAbAttr, source, this, battlerMove, power)
applyPreDefendAbAttrs(TypeImmunityAbAttr, this, source, battlerMove, cancelled, typeMultiplier);
if (cancelled.value) if (cancelled.value)
result = MoveResult.NO_EFFECT; result = MoveResult.NO_EFFECT;
else { else {
if (source.findTag(t => t instanceof TypeBoostTag && (t as TypeBoostTag).boostedType === move.type))
power.value *= 1.5;
const weatherTypeMultiplier = this.scene.arena.getAttackTypeMultiplier(move.type);
applyMoveAttrs(VariablePowerAttr, source, this, move, power); applyMoveAttrs(VariablePowerAttr, source, this, move, power);
this.scene.arena.applyTags(WeakenMoveTypeTag, move.type, power); if (!typeless) {
this.scene.applyModifiers(AttackTypeBoosterModifier, source.isPlayer(), source, power); this.scene.arena.applyTags(WeakenMoveTypeTag, move.type, power);
this.scene.applyModifiers(AttackTypeBoosterModifier, source.isPlayer(), source, power);
}
const critLevel = new Utils.IntegerHolder(0); const critLevel = new Utils.IntegerHolder(0);
applyMoveAttrs(HighCritAttr, source, this, move, critLevel); applyMoveAttrs(HighCritAttr, source, this, move, critLevel);
this.scene.applyModifiers(TempBattleStatBoosterModifier, source.isPlayer(), TempBattleStat.CRIT, critLevel); this.scene.applyModifiers(TempBattleStatBoosterModifier, source.isPlayer(), TempBattleStat.CRIT, critLevel);

View File

@ -2,6 +2,9 @@ import BattleScene from "../battle-scene";
import Pokemon from "../pokemon"; import Pokemon from "../pokemon";
import { TextStyle, addTextObject } from "./text"; import { TextStyle, addTextObject } from "./text";
const hiddenX = -91;
const shownX = 10;
export default class AbilityBar extends Phaser.GameObjects.Container { export default class AbilityBar extends Phaser.GameObjects.Container {
private bg: Phaser.GameObjects.Image; private bg: Phaser.GameObjects.Image;
private pokemonNameText: Phaser.GameObjects.Text; private pokemonNameText: Phaser.GameObjects.Text;
@ -12,7 +15,7 @@ export default class AbilityBar extends Phaser.GameObjects.Container {
public shown: boolean; public shown: boolean;
constructor(scene: BattleScene) { constructor(scene: BattleScene) {
super(scene, -91, (-scene.game.canvas.height / 6) + 64); super(scene, hiddenX, (-scene.game.canvas.height / 6) + 64);
} }
setup(): void { setup(): void {
@ -21,11 +24,11 @@ export default class AbilityBar extends Phaser.GameObjects.Container {
this.add(this.bg); this.add(this.bg);
this.pokemonNameText = addTextObject(this.scene, 5, 3, 'Pokemon', TextStyle.MESSAGE, { fontSize: '72px' }); this.pokemonNameText = addTextObject(this.scene, 5, 3, '', TextStyle.MESSAGE, { fontSize: '72px' });
this.pokemonNameText.setOrigin(0, 0); this.pokemonNameText.setOrigin(0, 0);
this.add(this.pokemonNameText); this.add(this.pokemonNameText);
this.abilityNameText = addTextObject(this.scene, 87, 16, 'Chlorophyll', TextStyle.WINDOW, { fontSize: '72px' }); this.abilityNameText = addTextObject(this.scene, 87, 16, '', TextStyle.WINDOW, { fontSize: '72px' });
this.abilityNameText.setOrigin(1, 0); this.abilityNameText.setOrigin(1, 0);
this.add(this.abilityNameText); this.add(this.abilityNameText);
@ -33,26 +36,32 @@ export default class AbilityBar extends Phaser.GameObjects.Container {
this.shown = false; this.shown = false;
} }
showAbility(pokemon: Pokemon) { showAbility(pokemon: Pokemon): void {
this.pokemonNameText.setText(`${pokemon.name}'s`); this.pokemonNameText.setText(`${pokemon.name}'s`);
this.abilityNameText.setText(pokemon.getAbility().name); this.abilityNameText.setText(pokemon.getAbility().name);
if (this.shown)
return;
if (this.tween) if (this.tween)
this.tween.stop(); this.tween.stop();
this.tween = this.scene.tweens.add({ this.tween = this.scene.tweens.add({
targets: this, targets: this,
x: 10, x: shownX,
duration: 500, duration: 500,
ease: 'Sine.easeOut', ease: 'Sine.easeOut',
onComplete: () => this.tween = null onComplete: () => this.tween = null
}); });
this.setVisible(true); this.setVisible(true);
this.shown = true; this.shown = true;
} }
hide() { hide(): void {
if (!this.shown)
return;
if (this.tween) if (this.tween)
this.tween.stop(); this.tween.stop();