Implement some abilities and attract move
parent
884c5cc40f
commit
d1d65708ce
|
@ -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 { ArenaTrapAbAttr, PostSummonAbAttr, PostTurnAbAttr, PostWeatherLapseAbAttr, PreWeatherDamageAbAttr, ProtectStatAttr, SuppressWeatherEffectAbAttr, applyPostSummonAbAttrs, applyPostTurnAbAttrs, applyPostWeatherLapseAbAttrs, applyPreStatChangeAbAttrs, applyPreWeatherEffectAbAttrs } from "./data/ability";
|
import { CheckTrappedAbAttr, PostDefendAbAttr, PostSummonAbAttr, PostTurnAbAttr, PostWeatherLapseAbAttr, PreWeatherDamageAbAttr, ProtectStatAttr, SuppressWeatherEffectAbAttr, applyCheckTrappedAbAttrs, applyPostDefendAbAttrs, applyPostSummonAbAttrs, applyPostTurnAbAttrs, applyPostWeatherLapseAbAttrs, applyPreStatChangeAbAttrs, applyPreWeatherEffectAbAttrs } from "./data/ability";
|
||||||
import { Unlockables, getUnlockableName } from "./system/unlockables";
|
import { Unlockables, getUnlockableName } from "./system/unlockables";
|
||||||
|
|
||||||
export class CheckLoadPhase extends BattlePhase {
|
export class CheckLoadPhase extends BattlePhase {
|
||||||
|
@ -678,13 +678,15 @@ export class CommandPhase extends FieldPhase {
|
||||||
break;
|
break;
|
||||||
case Command.POKEMON:
|
case Command.POKEMON:
|
||||||
const trapTag = playerPokemon.findTag(t => t instanceof TrappedTag) as TrappedTag;
|
const trapTag = playerPokemon.findTag(t => t instanceof TrappedTag) as TrappedTag;
|
||||||
const arenaTrapped = !!enemyPokemon.getAbility().hasAttr(ArenaTrapAbAttr);
|
const trapped = new Utils.BooleanHolder(false);
|
||||||
const batonPass = args[0] as boolean;
|
const batonPass = args[0] as boolean;
|
||||||
if (batonPass || (!trapTag && !arenaTrapped)) {
|
if (!batonPass)
|
||||||
|
applyCheckTrappedAbAttrs(CheckTrappedAbAttr, enemyPokemon, trapped);
|
||||||
|
if (batonPass || (!trapTag && !trapped.value)) {
|
||||||
this.scene.unshiftPhase(new SwitchSummonPhase(this.scene, cursor, true, args[0] as boolean));
|
this.scene.unshiftPhase(new SwitchSummonPhase(this.scene, cursor, true, args[0] as boolean));
|
||||||
success = true;
|
success = true;
|
||||||
} else
|
} else if (trapTag)
|
||||||
this.scene.ui.showText(`${this.scene.getPokemonById(trapTag.sourceId).name}'s ${trapTag?.getMoveName() || enemyPokemon.getAbility().name}\nprevents switching!`, null, () => {
|
this.scene.ui.showText(`${this.scene.getPokemonById(trapTag.sourceId).name}'s ${trapTag.getMoveName()}\nprevents switching!`, null, () => {
|
||||||
this.scene.ui.showText(null, 0);
|
this.scene.ui.showText(null, 0);
|
||||||
}, null, true);
|
}, null, true);
|
||||||
break;
|
break;
|
||||||
|
@ -877,7 +879,7 @@ export abstract class MovePhase extends BattlePhase {
|
||||||
|
|
||||||
console.log(Moves[this.move.moveId]);
|
console.log(Moves[this.move.moveId]);
|
||||||
|
|
||||||
const target = this.pokemon.isPlayer() ? this.scene.getEnemyPokemon() : this.scene.getPlayerPokemon();
|
const target = this.pokemon.getOpponent();
|
||||||
|
|
||||||
if (!this.followUp && this.canMove())
|
if (!this.followUp && this.canMove())
|
||||||
this.pokemon.lapseTags(BattlerTagLapseType.MOVE);
|
this.pokemon.lapseTags(BattlerTagLapseType.MOVE);
|
||||||
|
@ -1043,8 +1045,11 @@ abstract class MoveEffectPhase extends PokemonPhase {
|
||||||
target.addTag(BattlerTagType.FLINCHED, undefined, this.move.moveId, user.id);
|
target.addTag(BattlerTagType.FLINCHED, undefined, this.move.moveId, user.id);
|
||||||
}
|
}
|
||||||
// Charge attribute with charge effect takes all effect attributes and applies them to charge stage, so ignore them if this is present
|
// Charge attribute with charge effect takes all effect attributes and applies them to charge stage, so ignore them if this is present
|
||||||
if (!isProtected && !this.move.getMove().getAttrs(ChargeAttr).filter(ca => (ca as ChargeAttr).chargeEffect).length)
|
if (!isProtected && !this.move.getMove().getAttrs(ChargeAttr).filter(ca => (ca as ChargeAttr).chargeEffect).length) {
|
||||||
applyFilteredMoveAttrs((attr: MoveAttr) => attr instanceof MoveHitEffectAttr && (!!target.hp || (attr as MoveHitEffectAttr).selfTarget), user, target, this.move.getMove());
|
applyFilteredMoveAttrs((attr: MoveAttr) => attr instanceof MoveHitEffectAttr && (!!target.hp || (attr as MoveHitEffectAttr).selfTarget), user, target, this.move.getMove());
|
||||||
|
if (target.hp)
|
||||||
|
applyPostDefendAbAttrs(PostDefendAbAttr, user, target, this.move, result);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
this.end();
|
this.end();
|
||||||
});
|
});
|
||||||
|
|
|
@ -1,13 +1,13 @@
|
||||||
import Pokemon, { MoveResult, PokemonMove } from "../pokemon";
|
import Pokemon, { MoveResult, PokemonMove } from "../pokemon";
|
||||||
import { Type, getTypeDamageMultiplier } 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 { DamagePhase, PokemonHealPhase, ShowAbilityPhase, StatChangePhase } from "../battle-phases";
|
import { DamagePhase, PokemonHealPhase, ShowAbilityPhase, StatChangePhase } from "../battle-phases";
|
||||||
import { getPokemonMessage } from "../messages";
|
import { getPokemonMessage } from "../messages";
|
||||||
import { Weather, WeatherType } from "./weather";
|
import { Weather, WeatherType } from "./weather";
|
||||||
import { BattlerTagType } from "./battler-tag";
|
import { BattlerTag, BattlerTagType, TrappedTag } from "./battler-tag";
|
||||||
import { StatusEffect } from "./status-effect";
|
import { StatusEffect, getStatusEffectDescriptor } from "./status-effect";
|
||||||
import { Moves, RecoilAttr, WeatherHealAttr } from "./move";
|
import { MoveFlags, Moves, RecoilAttr } from "./move";
|
||||||
|
|
||||||
export class Ability {
|
export class Ability {
|
||||||
public id: Abilities;
|
public id: Abilities;
|
||||||
|
@ -212,6 +212,54 @@ export class NonSuperEffectiveImmunityAbAttr extends TypeImmunityAbAttr {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export class PostDefendAbAttr extends AbAttr {
|
||||||
|
applyPostDefend(pokemon: Pokemon, attacker: Pokemon, move: PokemonMove, moveResult: MoveResult, args: any[]): boolean {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class PostDefendContactApplyStatusEffectAbAttr extends PostDefendAbAttr {
|
||||||
|
private chance: integer;
|
||||||
|
private effects: StatusEffect[];
|
||||||
|
|
||||||
|
constructor(chance: integer, ...effects: StatusEffect[]) {
|
||||||
|
super();
|
||||||
|
|
||||||
|
this.chance = chance;
|
||||||
|
this.effects = effects;
|
||||||
|
}
|
||||||
|
|
||||||
|
applyPostDefend(pokemon: Pokemon, attacker: Pokemon, move: PokemonMove, moveResult: MoveResult, args: any[]): boolean {
|
||||||
|
if (move.getMove().hasFlag(MoveFlags.MAKES_CONTACT) && Utils.randInt(100) < this.chance) {
|
||||||
|
const effect = this.effects.length === 1 ? this.effects[0] : this.effects[Utils.randInt(this.effects.length)];
|
||||||
|
return attacker.trySetStatus(effect);
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class PostDefendContactApplyTagChanceAbAttr extends PostDefendAbAttr {
|
||||||
|
private chance: integer;
|
||||||
|
private tagType: BattlerTagType;
|
||||||
|
private turnCount: integer;
|
||||||
|
|
||||||
|
constructor(chance: integer, tagType: BattlerTagType, turnCount?: integer) {
|
||||||
|
super();
|
||||||
|
|
||||||
|
this.tagType = tagType;
|
||||||
|
this.chance = chance;
|
||||||
|
this.turnCount = turnCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
applyPostDefend(pokemon: Pokemon, attacker: Pokemon, move: PokemonMove, moveResult: MoveResult, args: any[]): boolean {
|
||||||
|
if (move.getMove().hasFlag(MoveFlags.MAKES_CONTACT) && Utils.randInt(100) < this.chance)
|
||||||
|
return attacker.addTag(this.tagType, this.turnCount, move.moveId, pokemon.id);
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export class PreAttackAbAttr extends AbAttr {
|
export class PreAttackAbAttr extends AbAttr {
|
||||||
applyPreAttack(pokemon: Pokemon, defender: Pokemon, move: PokemonMove, args: any[]): boolean {
|
applyPreAttack(pokemon: Pokemon, defender: Pokemon, move: PokemonMove, args: any[]): boolean {
|
||||||
return false;
|
return false;
|
||||||
|
@ -282,6 +330,29 @@ export class BattleStatMultiplierAbAttr extends AbAttr {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export class PostSummonAbAttr extends AbAttr {
|
||||||
|
applyPostSummon(pokemon: Pokemon, args: any[]) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class PostSummonWeatherChangeAbAttr extends PostSummonAbAttr {
|
||||||
|
private weatherType: WeatherType;
|
||||||
|
|
||||||
|
constructor(weatherType: WeatherType) {
|
||||||
|
super();
|
||||||
|
|
||||||
|
this.weatherType = weatherType;
|
||||||
|
}
|
||||||
|
|
||||||
|
applyPostSummon(pokemon: Pokemon, args: any[]): boolean {
|
||||||
|
if (!pokemon.scene.arena.weather?.isImmutable())
|
||||||
|
return pokemon.scene.arena.trySetWeather(this.weatherType, false);
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export class PreStatChangeAbAttr extends AbAttr {
|
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;
|
||||||
|
@ -311,9 +382,65 @@ export class ProtectStatAttr extends PreStatChangeAbAttr {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export class BlockCritAbAttr extends AbAttr { }
|
export class PreSetStatusAbAttr extends AbAttr {
|
||||||
|
applyPreSetStatus(pokemon: Pokemon, effect: StatusEffect, cancelled: Utils.BooleanHolder, args: any[]): boolean {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export class ArenaTrapAbAttr extends AbAttr { }
|
export class StatusEffectImmunityAbAttr extends PreSetStatusAbAttr {
|
||||||
|
private immuneEffects: StatusEffect[];
|
||||||
|
|
||||||
|
constructor(...immuneEffects: StatusEffect[]) {
|
||||||
|
super();
|
||||||
|
|
||||||
|
this.immuneEffects = immuneEffects;
|
||||||
|
}
|
||||||
|
|
||||||
|
applyPreSetStatus(pokemon: Pokemon, effect: StatusEffect, cancelled: Utils.BooleanHolder, args: any[]): boolean {
|
||||||
|
if (!this.immuneEffects.length || this.immuneEffects.indexOf(effect) > -1) {
|
||||||
|
cancelled.value = true;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
getTriggerMessage(pokemon: Pokemon, ...args: any[]): string {
|
||||||
|
return getPokemonMessage(pokemon, `'s ${pokemon.getAbility().name}\nprevents ${this.immuneEffects.length ? getStatusEffectDescriptor(args[0] as StatusEffect) : 'status problems'}!`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class PreApplyBattlerTagAbAttr extends AbAttr {
|
||||||
|
applyPreApplyBattlerTag(pokemon: Pokemon, tag: BattlerTag, cancelled: Utils.BooleanHolder, args: any[]): boolean {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class BattlerTagImmunityAbAttr extends PreApplyBattlerTagAbAttr {
|
||||||
|
private immuneTagType: BattlerTagType;
|
||||||
|
|
||||||
|
constructor(immuneTagType: BattlerTagType) {
|
||||||
|
super();
|
||||||
|
|
||||||
|
this.immuneTagType = immuneTagType;
|
||||||
|
}
|
||||||
|
|
||||||
|
applyPreApplyBattlerTag(pokemon: Pokemon, tag: BattlerTag, cancelled: Utils.BooleanHolder, args: any[]): boolean {
|
||||||
|
if (tag.tagType === this.immuneTagType) {
|
||||||
|
cancelled.value = true;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
getTriggerMessage(pokemon: Pokemon, ...args: any[]): string {
|
||||||
|
return getPokemonMessage(pokemon, `'s ${pokemon.getAbility().name}\nprevents ${(args[0] as BattlerTag).getDescriptor()}!`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class BlockCritAbAttr extends AbAttr { }
|
||||||
|
|
||||||
export class PreWeatherEffectAbAttr extends AbAttr {
|
export class PreWeatherEffectAbAttr extends AbAttr {
|
||||||
applyPreWeatherEffect(pokemon: Pokemon, weather: Weather, cancelled: Utils.BooleanHolder, args: any[]): boolean {
|
applyPreWeatherEffect(pokemon: Pokemon, weather: Weather, cancelled: Utils.BooleanHolder, args: any[]): boolean {
|
||||||
|
@ -453,6 +580,23 @@ export class PostWeatherLapseDamageAbAttr extends PostWeatherLapseAbAttr {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export class CheckTrappedAbAttr extends AbAttr {
|
||||||
|
applyCheckTrapped(pokemon: Pokemon, trapped: Utils.BooleanHolder, args: any[]): boolean {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class ArenaTrapAbAttr extends CheckTrappedAbAttr {
|
||||||
|
applyCheckTrapped(pokemon: Pokemon, trapped: Utils.BooleanHolder, args: any[]): boolean {
|
||||||
|
trapped.value = true;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
getTriggerMessage(pokemon: Pokemon, ...args: any[]): string {
|
||||||
|
return getPokemonMessage(pokemon, `\'s ${pokemon.getAbility().name}\nprevents switching!`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export function applyAbAttrs(attrType: { new(...args: any[]): AbAttr }, pokemon: Pokemon, cancelled: Utils.BooleanHolder, ...args: any[]): void {
|
export function applyAbAttrs(attrType: { new(...args: any[]): AbAttr }, pokemon: Pokemon, cancelled: Utils.BooleanHolder, ...args: any[]): void {
|
||||||
if (!pokemon.canApplyAbility())
|
if (!pokemon.canApplyAbility())
|
||||||
return;
|
return;
|
||||||
|
@ -497,6 +641,28 @@ export function applyPreDefendAbAttrs(attrType: { new(...args: any[]): PreDefend
|
||||||
pokemon.scene.clearPhaseQueueSplice();
|
pokemon.scene.clearPhaseQueueSplice();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function applyPostDefendAbAttrs(attrType: { new(...args: any[]): PostDefendAbAttr },
|
||||||
|
pokemon: Pokemon, attacker: Pokemon, move: PokemonMove, moveResult: MoveResult, ...args: any[]): void {
|
||||||
|
if (!pokemon.canApplyAbility())
|
||||||
|
return;
|
||||||
|
|
||||||
|
const ability = pokemon.getAbility();
|
||||||
|
const attrs = ability.getAttrs(attrType) as PostDefendAbAttr[];
|
||||||
|
for (let attr of attrs) {
|
||||||
|
if (!canApplyAttr(pokemon, attr))
|
||||||
|
continue;
|
||||||
|
pokemon.scene.setPhaseQueueSplice();
|
||||||
|
if (attr.applyPostDefend(pokemon, attacker, move, moveResult, args)) {
|
||||||
|
queueShowAbility(pokemon);
|
||||||
|
const message = attr.getTriggerMessage(pokemon, attacker, move);
|
||||||
|
if (message)
|
||||||
|
pokemon.scene.queueMessage(message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pokemon.scene.clearPhaseQueueSplice();
|
||||||
|
}
|
||||||
|
|
||||||
export function applyBattleStatMultiplierAbAttrs(attrType: { new(...args: any[]): BattleStatMultiplierAbAttr },
|
export function applyBattleStatMultiplierAbAttrs(attrType: { new(...args: any[]): BattleStatMultiplierAbAttr },
|
||||||
pokemon: Pokemon, battleStat: BattleStat, statValue: Utils.NumberHolder, ...args: any[]) {
|
pokemon: Pokemon, battleStat: BattleStat, statValue: Utils.NumberHolder, ...args: any[]) {
|
||||||
if (!pokemon.canApplyAbility())
|
if (!pokemon.canApplyAbility())
|
||||||
|
@ -542,6 +708,28 @@ export function applyPreAttackAbAttrs(attrType: { new(...args: any[]): PreAttack
|
||||||
pokemon.scene.clearPhaseQueueSplice();
|
pokemon.scene.clearPhaseQueueSplice();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function applyPostSummonAbAttrs(attrType: { new(...args: any[]): PostSummonAbAttr },
|
||||||
|
pokemon: Pokemon, ...args: any[]): void {
|
||||||
|
if (!pokemon.canApplyAbility())
|
||||||
|
return;
|
||||||
|
|
||||||
|
const ability = pokemon.getAbility();
|
||||||
|
const attrs = ability.getAttrs(attrType) as PostSummonAbAttr[];
|
||||||
|
for (let attr of attrs) {
|
||||||
|
if (!canApplyAttr(pokemon, attr))
|
||||||
|
continue;
|
||||||
|
pokemon.scene.setPhaseQueueSplice();
|
||||||
|
if (attr.applyPostSummon(pokemon, args)) {
|
||||||
|
queueShowAbility(pokemon);
|
||||||
|
const message = attr.getTriggerMessage(pokemon);
|
||||||
|
if (message)
|
||||||
|
pokemon.scene.queueMessage(message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pokemon.scene.clearPhaseQueueSplice();
|
||||||
|
}
|
||||||
|
|
||||||
export function applyPreStatChangeAbAttrs(attrType: { new(...args: any[]): PreStatChangeAbAttr },
|
export function applyPreStatChangeAbAttrs(attrType: { new(...args: any[]): PreStatChangeAbAttr },
|
||||||
pokemon: Pokemon, stat: BattleStat, cancelled: Utils.BooleanHolder, ...args: any[]): void {
|
pokemon: Pokemon, stat: BattleStat, cancelled: Utils.BooleanHolder, ...args: any[]): void {
|
||||||
if (!pokemon.canApplyAbility())
|
if (!pokemon.canApplyAbility())
|
||||||
|
@ -564,6 +752,50 @@ export function applyPreStatChangeAbAttrs(attrType: { new(...args: any[]): PreSt
|
||||||
pokemon.scene.clearPhaseQueueSplice();
|
pokemon.scene.clearPhaseQueueSplice();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function applyPreSetStatusAbAttrs(attrType: { new(...args: any[]): PreSetStatusAbAttr },
|
||||||
|
pokemon: Pokemon, effect: StatusEffect, cancelled: Utils.BooleanHolder, ...args: any[]): void {
|
||||||
|
if (!pokemon.canApplyAbility())
|
||||||
|
return;
|
||||||
|
|
||||||
|
const ability = pokemon.getAbility();
|
||||||
|
const attrs = ability.getAttrs(attrType) as PreSetStatusAbAttr[];
|
||||||
|
for (let attr of attrs) {
|
||||||
|
if (!canApplyAttr(pokemon, attr))
|
||||||
|
continue;
|
||||||
|
pokemon.scene.setPhaseQueueSplice();
|
||||||
|
if (attr.applyPreSetStatus(pokemon, effect, cancelled, args)) {
|
||||||
|
queueShowAbility(pokemon);
|
||||||
|
const message = attr.getTriggerMessage(pokemon, effect);
|
||||||
|
if (message)
|
||||||
|
pokemon.scene.queueMessage(message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pokemon.scene.clearPhaseQueueSplice();
|
||||||
|
}
|
||||||
|
|
||||||
|
export function applyPreApplyBattlerTagAbAttrs(attrType: { new(...args: any[]): PreApplyBattlerTagAbAttr },
|
||||||
|
pokemon: Pokemon, tag: BattlerTag, cancelled: Utils.BooleanHolder, ...args: any[]): void {
|
||||||
|
if (!pokemon.canApplyAbility())
|
||||||
|
return;
|
||||||
|
|
||||||
|
const ability = pokemon.getAbility();
|
||||||
|
const attrs = ability.getAttrs(attrType) as PreApplyBattlerTagAbAttr[];
|
||||||
|
for (let attr of attrs) {
|
||||||
|
if (!canApplyAttr(pokemon, attr))
|
||||||
|
continue;
|
||||||
|
pokemon.scene.setPhaseQueueSplice();
|
||||||
|
if (attr.applyPreApplyBattlerTag(pokemon, tag, cancelled, args)) {
|
||||||
|
queueShowAbility(pokemon);
|
||||||
|
const message = attr.getTriggerMessage(pokemon, tag);
|
||||||
|
if (message)
|
||||||
|
pokemon.scene.queueMessage(message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pokemon.scene.clearPhaseQueueSplice();
|
||||||
|
}
|
||||||
|
|
||||||
export function applyPreWeatherEffectAbAttrs(attrType: { new(...args: any[]): PreWeatherEffectAbAttr },
|
export function applyPreWeatherEffectAbAttrs(attrType: { new(...args: any[]): PreWeatherEffectAbAttr },
|
||||||
pokemon: Pokemon, weather: Weather, cancelled: Utils.BooleanHolder, ...args: any[]): void {
|
pokemon: Pokemon, weather: Weather, cancelled: Utils.BooleanHolder, ...args: any[]): void {
|
||||||
if (!pokemon.canApplyAbility())
|
if (!pokemon.canApplyAbility())
|
||||||
|
@ -635,6 +867,28 @@ export function applyPostWeatherLapseAbAttrs(attrType: { new(...args: any[]): Po
|
||||||
pokemon.scene.clearPhaseQueueSplice();
|
pokemon.scene.clearPhaseQueueSplice();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function applyCheckTrappedAbAttrs(attrType: { new(...args: any[]): CheckTrappedAbAttr },
|
||||||
|
pokemon: Pokemon, trapped: Utils.BooleanHolder, ...args: any[]): void {
|
||||||
|
if (!pokemon.canApplyAbility())
|
||||||
|
return;
|
||||||
|
|
||||||
|
const ability = pokemon.getAbility();
|
||||||
|
const attrs = ability.getAttrs(attrType) as CheckTrappedAbAttr[];
|
||||||
|
for (let attr of attrs) {
|
||||||
|
if (!canApplyAttr(pokemon, attr))
|
||||||
|
continue;
|
||||||
|
pokemon.scene.setPhaseQueueSplice();
|
||||||
|
if (attr.applyCheckTrapped(pokemon, trapped, args)) {
|
||||||
|
// Don't show ability bar because this call is asynchronous
|
||||||
|
const message = attr.getTriggerMessage(pokemon);
|
||||||
|
if (message)
|
||||||
|
pokemon.scene.ui.showText(message, null, () => pokemon.scene.ui.showText(null, 0), null, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pokemon.scene.clearPhaseQueueSplice();
|
||||||
|
}
|
||||||
|
|
||||||
function canApplyAttr(pokemon: Pokemon, attr: AbAttr): boolean {
|
function canApplyAttr(pokemon: Pokemon, attr: AbAttr): boolean {
|
||||||
const condition = attr.getCondition();
|
const condition = attr.getCondition();
|
||||||
return !condition || condition(pokemon);
|
return !condition || condition(pokemon);
|
||||||
|
@ -841,12 +1095,16 @@ export function initAbilities() {
|
||||||
new Ability(Abilities.COLOR_CHANGE, "Color Change (N)", "Changes the POKéMON's type to the foe's move.", 3),
|
new Ability(Abilities.COLOR_CHANGE, "Color Change (N)", "Changes the POKéMON's type to the foe's move.", 3),
|
||||||
new Ability(Abilities.COMPOUND_EYES, "Compound Eyes", "The POKéMON's accuracy is boosted.", 3)
|
new Ability(Abilities.COMPOUND_EYES, "Compound Eyes", "The POKéMON's accuracy is boosted.", 3)
|
||||||
.attr(BattleStatMultiplierAbAttr, BattleStat.ACC, 1.3),
|
.attr(BattleStatMultiplierAbAttr, BattleStat.ACC, 1.3),
|
||||||
new Ability(Abilities.CUTE_CHARM, "Cute Charm (N)", "Contact with the POKéMON may cause infatuation.", 3),
|
new Ability(Abilities.CUTE_CHARM, "Cute Charm", "Contact with the POKéMON may cause infatuation.", 3)
|
||||||
|
.attr(PostDefendContactApplyTagChanceAbAttr, 30, BattlerTagType.INFATUATED),
|
||||||
new Ability(Abilities.DAMP, "Damp (N)", "Prevents the use of self-destructing moves.", 3),
|
new Ability(Abilities.DAMP, "Damp (N)", "Prevents the use of self-destructing moves.", 3),
|
||||||
new Ability(Abilities.DRIZZLE, "Drizzle (N)", "The POKéMON makes it rain when it enters a battle.", 3),
|
new Ability(Abilities.DRIZZLE, "Drizzle", "The POKéMON makes it rain when it enters a battle.", 3)
|
||||||
new Ability(Abilities.DROUGHT, "Drought (N)", "Turns the sunlight harsh when the POKéMON enters a battle.", 3),
|
.attr(PostSummonWeatherChangeAbAttr, WeatherType.RAIN),
|
||||||
|
new Ability(Abilities.DROUGHT, "Drought", "Turns the sunlight harsh when the POKéMON enters a battle.", 3)
|
||||||
|
.attr(PostSummonWeatherChangeAbAttr, WeatherType.SUNNY),
|
||||||
new Ability(Abilities.EARLY_BIRD, "Early Bird (N)", "The POKéMON awakens quickly from sleep.", 3),
|
new Ability(Abilities.EARLY_BIRD, "Early Bird (N)", "The POKéMON awakens quickly from sleep.", 3),
|
||||||
new Ability(Abilities.EFFECT_SPORE, "Effect Spore (N)", "Contact may poison or cause paralysis or sleep.", 3),
|
new Ability(Abilities.EFFECT_SPORE, "Effect Spore", "Contact may poison or cause paralysis or sleep.", 3)
|
||||||
|
.attr(PostDefendContactApplyStatusEffectAbAttr, 10, StatusEffect.POISON, StatusEffect.PARALYSIS, StatusEffect.SLEEP),
|
||||||
new Ability(Abilities.FLAME_BODY, "Flame Body (N)", "Contact with the POKéMON may burn the attacker.", 3),
|
new Ability(Abilities.FLAME_BODY, "Flame Body (N)", "Contact with the POKéMON may burn the attacker.", 3),
|
||||||
new Ability(Abilities.FLASH_FIRE, "Flash Fire", "It powers up FIRE-type moves if it's hit by one.", 3)
|
new Ability(Abilities.FLASH_FIRE, "Flash Fire", "It powers up FIRE-type moves if it's hit by one.", 3)
|
||||||
.attr(TypeImmunityAddBattlerTagAbAttr, Type.FIRE, 1, BattlerTagType.FIRE_BOOST, (pokemon: Pokemon) => !pokemon.status || pokemon.status.effect !== StatusEffect.FREEZE),
|
.attr(TypeImmunityAddBattlerTagAbAttr, Type.FIRE, 1, BattlerTagType.FIRE_BOOST, (pokemon: Pokemon) => !pokemon.status || pokemon.status.effect !== StatusEffect.FREEZE),
|
||||||
|
@ -857,9 +1115,12 @@ export function initAbilities() {
|
||||||
new Ability(Abilities.HYPER_CUTTER, "Hyper Cutter", "Prevents other POKéMON from lowering ATTACK stat.", 3)
|
new Ability(Abilities.HYPER_CUTTER, "Hyper Cutter", "Prevents other POKéMON from lowering ATTACK stat.", 3)
|
||||||
.attr(ProtectStatAttr, BattleStat.ATK),
|
.attr(ProtectStatAttr, BattleStat.ATK),
|
||||||
new Ability(Abilities.ILLUMINATE, "Illuminate (N)", "Raises the likelihood of meeting wild POKéMON.", 3),
|
new Ability(Abilities.ILLUMINATE, "Illuminate (N)", "Raises the likelihood of meeting wild POKéMON.", 3),
|
||||||
new Ability(Abilities.IMMUNITY, "Immunity (N)", "Prevents the POKéMON from getting poisoned.", 3),
|
new Ability(Abilities.IMMUNITY, "Immunity", "Prevents the POKéMON from getting poisoned.", 3)
|
||||||
|
.attr(StatusEffectImmunityAbAttr, StatusEffect.POISON),
|
||||||
new Ability(Abilities.INNER_FOCUS, "Inner Focus (N)", "The POKéMON is protected from flinching.", 3),
|
new Ability(Abilities.INNER_FOCUS, "Inner Focus (N)", "The POKéMON is protected from flinching.", 3),
|
||||||
new Ability(Abilities.INSOMNIA, "Insomnia (N)", "Prevents the POKéMON from falling asleep.", 3),
|
new Ability(Abilities.INSOMNIA, "Insomnia", "Prevents the POKéMON from falling asleep.", 3)
|
||||||
|
.attr(StatusEffectImmunityAbAttr, StatusEffect.SLEEP)
|
||||||
|
.attr(BattlerTagImmunityAbAttr, BattlerTagType.DROWSY),
|
||||||
new Ability(Abilities.INTIMIDATE, "Intimidate (N)", "Lowers the foe's ATTACK stat.", 3),
|
new Ability(Abilities.INTIMIDATE, "Intimidate (N)", "Lowers the foe's ATTACK stat.", 3),
|
||||||
new Ability(Abilities.KEEN_EYE, "Keen Eye", "Prevents other POKéMON from lowering accuracy.", 3)
|
new Ability(Abilities.KEEN_EYE, "Keen Eye", "Prevents other POKéMON from lowering accuracy.", 3)
|
||||||
.attr(ProtectStatAttr, BattleStat.ACC),
|
.attr(ProtectStatAttr, BattleStat.ACC),
|
||||||
|
@ -869,15 +1130,20 @@ export function initAbilities() {
|
||||||
.attr(TypeImmunityStatChangeAbAttr, Type.ELECTRIC, BattleStat.SPATK, 1),
|
.attr(TypeImmunityStatChangeAbAttr, Type.ELECTRIC, BattleStat.SPATK, 1),
|
||||||
new Ability(Abilities.LIMBER, "Limber (N)", "The POKéMON is protected from paralysis.", 3),
|
new Ability(Abilities.LIMBER, "Limber (N)", "The POKéMON is protected from paralysis.", 3),
|
||||||
new Ability(Abilities.LIQUID_OOZE, "Liquid Ooze (N)", "Damages attackers using any draining move.", 3),
|
new Ability(Abilities.LIQUID_OOZE, "Liquid Ooze (N)", "Damages attackers using any draining move.", 3),
|
||||||
new Ability(Abilities.MAGMA_ARMOR, "Magma Armor (N)", "Prevents the POKéMON from becoming frozen.", 3),
|
new Ability(Abilities.MAGMA_ARMOR, "Magma Armor", "Prevents the POKéMON from becoming frozen.", 3)
|
||||||
new Ability(Abilities.MAGNET_PULL, "Magnet Pull (N)", "Prevents STEEL-type POKéMON from escaping.", 3),
|
.attr(StatusEffectImmunityAbAttr, StatusEffect.FREEZE),
|
||||||
|
new Ability(Abilities.MAGNET_PULL, "Magnet Pull", "Prevents STEEL-type POKéMON from escaping.", 3)
|
||||||
|
.attr(ArenaTrapAbAttr)
|
||||||
|
.condition((pokemon: Pokemon) => pokemon.getOpponent()?.isOfType(Type.STEEL)),
|
||||||
new Ability(Abilities.MARVEL_SCALE, "Marvel Scale (N)", "Ups DEFENSE if there is a status problem.", 3),
|
new Ability(Abilities.MARVEL_SCALE, "Marvel Scale (N)", "Ups DEFENSE if there is a status problem.", 3),
|
||||||
new Ability(Abilities.MINUS, "Minus (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.NATURAL_CURE, "Natural Cure (N)", "All status problems heal when it switches out.", 3),
|
new Ability(Abilities.NATURAL_CURE, "Natural Cure (N)", "All status problems heal when it switches out.", 3),
|
||||||
new Ability(Abilities.OBLIVIOUS, "Oblivious (N)", "Prevents it from becoming infatuated.", 3),
|
new Ability(Abilities.OBLIVIOUS, "Oblivious", "Prevents it from becoming infatuated.", 3)
|
||||||
|
.attr(BattlerTagImmunityAbAttr, BattlerTagType.INFATUATED),
|
||||||
new Ability(Abilities.OVERGROW, "Overgrow", "Powers up GRASS-type moves in a pinch.", 3)
|
new Ability(Abilities.OVERGROW, "Overgrow", "Powers up GRASS-type moves in a pinch.", 3)
|
||||||
.attr(LowHpMoveTypePowerBoostAbAttr, Type.GRASS),
|
.attr(LowHpMoveTypePowerBoostAbAttr, Type.GRASS),
|
||||||
new Ability(Abilities.OWN_TEMPO, "Own Tempo (N)", "Prevents the POKéMON from becoming confused.", 3),
|
new Ability(Abilities.OWN_TEMPO, "Own Tempo", "Prevents the POKéMON from becoming confused.", 3)
|
||||||
|
.attr(BattlerTagImmunityAbAttr, BattlerTagType.CONFUSED),
|
||||||
new Ability(Abilities.PICKUP, "Pickup (N)", "The POKéMON may pick up items.", 3),
|
new Ability(Abilities.PICKUP, "Pickup (N)", "The POKéMON may pick up items.", 3),
|
||||||
new Ability(Abilities.PLUS, "Plus (N)", "Ups SP. ATK if another POKéMON has PLUS or MINUS.", 3),
|
new Ability(Abilities.PLUS, "Plus (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.POISON_POINT, "Poison Point (N)", "Contact with the POKéMON may poison the attacker.", 3),
|
||||||
|
@ -889,7 +1155,8 @@ export function initAbilities() {
|
||||||
.attr(BlockRecoilDamageAttr),
|
.attr(BlockRecoilDamageAttr),
|
||||||
new Ability(Abilities.ROUGH_SKIN, "Rough Skin (N)", "Inflicts damage to the attacker on contact.", 3),
|
new Ability(Abilities.ROUGH_SKIN, "Rough Skin (N)", "Inflicts damage to the attacker on contact.", 3),
|
||||||
new Ability(Abilities.RUN_AWAY, "Run Away (N)", "Enables a sure getaway from wild POKéMON.", 3),
|
new Ability(Abilities.RUN_AWAY, "Run Away (N)", "Enables a sure getaway from wild POKéMON.", 3),
|
||||||
new Ability(Abilities.SAND_STREAM, "Sand Stream (N)", "The POKéMON summons a sandstorm in battle.", 3),
|
new Ability(Abilities.SAND_STREAM, "Sand Stream", "The POKéMON summons a sandstorm in battle.", 3)
|
||||||
|
.attr(PostSummonWeatherChangeAbAttr, WeatherType.SANDSTORM),
|
||||||
new Ability(Abilities.SAND_VEIL, "Sand Veil", "Boosts the POKéMON's evasion in a sandstorm.", 3)
|
new Ability(Abilities.SAND_VEIL, "Sand Veil", "Boosts the POKéMON's evasion in a sandstorm.", 3)
|
||||||
.attr(BattleStatMultiplierAbAttr, BattleStat.EVA, 1.2)
|
.attr(BattleStatMultiplierAbAttr, BattleStat.EVA, 1.2)
|
||||||
.attr(BlockWeatherDamageAttr, WeatherType.SANDSTORM)
|
.attr(BlockWeatherDamageAttr, WeatherType.SANDSTORM)
|
||||||
|
@ -920,12 +1187,15 @@ export function initAbilities() {
|
||||||
.attr(LowHpMoveTypePowerBoostAbAttr, Type.WATER),
|
.attr(LowHpMoveTypePowerBoostAbAttr, Type.WATER),
|
||||||
new Ability(Abilities.TRACE, "Trace (N)", "The POKéMON copies a foe's Ability.", 3),
|
new Ability(Abilities.TRACE, "Trace (N)", "The POKéMON copies a foe's Ability.", 3),
|
||||||
new Ability(Abilities.TRUANT, "Truant (N)", "POKéMON can't attack on consecutive turns.", 3),
|
new Ability(Abilities.TRUANT, "Truant (N)", "POKéMON can't attack on consecutive turns.", 3),
|
||||||
new Ability(Abilities.VITAL_SPIRIT, "Vital Spirit (N)", "Prevents the POKéMON from falling asleep.", 3),
|
new Ability(Abilities.VITAL_SPIRIT, "Vital Spirit", "Prevents the POKéMON from falling asleep.", 3)
|
||||||
|
.attr(StatusEffectImmunityAbAttr, StatusEffect.SLEEP)
|
||||||
|
.attr(BattlerTagImmunityAbAttr, BattlerTagType.DROWSY),
|
||||||
new Ability(Abilities.VOLT_ABSORB, "Volt Absorb", "Restores HP if hit by an ELECTRIC-type move.", 3)
|
new Ability(Abilities.VOLT_ABSORB, "Volt Absorb", "Restores HP if hit by an ELECTRIC-type move.", 3)
|
||||||
.attr(TypeImmunityHealAbAttr, Type.ELECTRIC),
|
.attr(TypeImmunityHealAbAttr, Type.ELECTRIC),
|
||||||
new Ability(Abilities.WATER_ABSORB, "Water Absorb", "Restores HP if hit by a WATER-type move.", 3)
|
new Ability(Abilities.WATER_ABSORB, "Water Absorb", "Restores HP if hit by a WATER-type move.", 3)
|
||||||
.attr(TypeImmunityHealAbAttr, Type.WATER),
|
.attr(TypeImmunityHealAbAttr, Type.WATER),
|
||||||
new Ability(Abilities.WATER_VEIL, "Water Veil (N)", "Prevents the POKéMON from getting a burn.", 3),
|
new Ability(Abilities.WATER_VEIL, "Water Veil", "Prevents the POKéMON from getting a burn.", 3)
|
||||||
|
.attr(StatusEffectImmunityAbAttr, StatusEffect.BURN),
|
||||||
new Ability(Abilities.WHITE_SMOKE, "White Smoke", "Prevents other POKéMON from lowering its stats.", 3)
|
new Ability(Abilities.WHITE_SMOKE, "White Smoke", "Prevents other POKéMON from lowering its stats.", 3)
|
||||||
.attr(ProtectStatAttr),
|
.attr(ProtectStatAttr),
|
||||||
new Ability(Abilities.WONDER_GUARD, "Wonder Guard", "Only super effective moves will hit.", 3)
|
new Ability(Abilities.WONDER_GUARD, "Wonder Guard", "Only super effective moves will hit.", 3)
|
||||||
|
@ -953,7 +1223,9 @@ export function initAbilities() {
|
||||||
.attr(PostWeatherLapseHealAbAttr, 1, WeatherType.HAIL),
|
.attr(PostWeatherLapseHealAbAttr, 1, WeatherType.HAIL),
|
||||||
new Ability(Abilities.IRON_FIST, "Iron Fist (N)", "Boosts the power of punching moves.", 4),
|
new Ability(Abilities.IRON_FIST, "Iron Fist (N)", "Boosts the power of punching moves.", 4),
|
||||||
new Ability(Abilities.KLUTZ, "Klutz (N)", "The POKéMON can't use any held items.", 4),
|
new Ability(Abilities.KLUTZ, "Klutz (N)", "The POKéMON can't use any held items.", 4),
|
||||||
new Ability(Abilities.LEAF_GUARD, "Leaf Guard (N)", "Prevents problems with status in sunny weather.", 4),
|
new Ability(Abilities.LEAF_GUARD, "Leaf Guard", "Prevents problems with status in sunny weather.", 4)
|
||||||
|
.attr(StatusEffectImmunityAbAttr)
|
||||||
|
.condition(getWeatherCondition(WeatherType.SUNNY, WeatherType.HARSH_SUN)),
|
||||||
new Ability(Abilities.MAGIC_GUARD, "Magic Guard (N)", "Protects the POKéMON from indirect damage.", 4),
|
new Ability(Abilities.MAGIC_GUARD, "Magic Guard (N)", "Protects the POKéMON from indirect damage.", 4),
|
||||||
new Ability(Abilities.MOLD_BREAKER, "Mold Breaker (N)", "Moves can be used regardless of Abilities.", 4),
|
new Ability(Abilities.MOLD_BREAKER, "Mold Breaker (N)", "Moves can be used regardless of Abilities.", 4),
|
||||||
new Ability(Abilities.MOTOR_DRIVE, "Motor Drive", "Raises SPEED if hit by an ELECTRIC-type move.", 4)
|
new Ability(Abilities.MOTOR_DRIVE, "Motor Drive", "Raises SPEED if hit by an ELECTRIC-type move.", 4)
|
||||||
|
@ -972,7 +1244,8 @@ export function initAbilities() {
|
||||||
new Ability(Abilities.SLOW_START, "Slow Start (N)", "Temporarily halves ATTACK and SPEED.", 4),
|
new Ability(Abilities.SLOW_START, "Slow Start (N)", "Temporarily halves ATTACK and SPEED.", 4),
|
||||||
new Ability(Abilities.SNIPER, "Sniper (N)", "Powers up moves if they become critical hits.", 4),
|
new Ability(Abilities.SNIPER, "Sniper (N)", "Powers up moves if they become critical hits.", 4),
|
||||||
new Ability(Abilities.SNOW_CLOAK, "Snow Cloak (N)", "Raises evasion in a hailstorm.", 4),
|
new Ability(Abilities.SNOW_CLOAK, "Snow Cloak (N)", "Raises evasion in a hailstorm.", 4),
|
||||||
new Ability(Abilities.SNOW_WARNING, "Snow Warning (N)", "The POKéMON summons a hailstorm in battle.", 4),
|
new Ability(Abilities.SNOW_WARNING, "Snow Warning", "The POKéMON summons a hailstorm in battle.", 4)
|
||||||
|
.attr(PostSummonWeatherChangeAbAttr, WeatherType.HAIL),
|
||||||
new Ability(Abilities.SOLAR_POWER, "Solar Power (N)", "In sunshine, SP. ATK is boosted but HP decreases.", 4),
|
new Ability(Abilities.SOLAR_POWER, "Solar Power (N)", "In sunshine, SP. ATK is boosted but HP decreases.", 4),
|
||||||
new Ability(Abilities.SOLID_ROCK, "Solid Rock (N)", "Reduces damage from super-effective attacks.", 4),
|
new Ability(Abilities.SOLID_ROCK, "Solid Rock (N)", "Reduces damage from super-effective attacks.", 4),
|
||||||
new Ability(Abilities.STALL, "Stall (N)", "The POKéMON moves after all other POKéMON do.", 4),
|
new Ability(Abilities.STALL, "Stall (N)", "The POKéMON moves after all other POKéMON do.", 4),
|
||||||
|
|
|
@ -134,7 +134,7 @@ class SpikesTag extends ArenaTrapTag {
|
||||||
super.onAdd(arena);
|
super.onAdd(arena);
|
||||||
|
|
||||||
const source = arena.scene.getPokemonById(this.sourceId);
|
const source = arena.scene.getPokemonById(this.sourceId);
|
||||||
const target = source.isPlayer() ? source.scene.getEnemyPokemon() : source.scene.getPlayerPokemon();
|
const target = source.getOpponent();
|
||||||
arena.scene.queueMessage(`${this.getMoveName()} were scattered\nall around ${target.name}'s feet!`);
|
arena.scene.queueMessage(`${this.getMoveName()} were scattered\nall around ${target.name}'s feet!`);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -161,7 +161,7 @@ class ToxicSpikesTag extends ArenaTrapTag {
|
||||||
super.onAdd(arena);
|
super.onAdd(arena);
|
||||||
|
|
||||||
const source = arena.scene.getPokemonById(this.sourceId);
|
const source = arena.scene.getPokemonById(this.sourceId);
|
||||||
const target = source.isPlayer() ? source.scene.getEnemyPokemon() : source.scene.getPlayerPokemon();
|
const target = source.getOpponent();
|
||||||
arena.scene.queueMessage(`${this.getMoveName()} were scattered\nall around ${target.name}'s feet!`);
|
arena.scene.queueMessage(`${this.getMoveName()} were scattered\nall around ${target.name}'s feet!`);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -187,7 +187,7 @@ class StealthRockTag extends ArenaTrapTag {
|
||||||
super.onAdd(arena);
|
super.onAdd(arena);
|
||||||
|
|
||||||
const source = arena.scene.getPokemonById(this.sourceId);
|
const source = arena.scene.getPokemonById(this.sourceId);
|
||||||
const target = source.isPlayer() ? source.scene.getEnemyPokemon() : source.scene.getPlayerPokemon();
|
const target = source.getOpponent();
|
||||||
arena.scene.queueMessage(`Pointed stones float in the air\naround ${target.name}!`);
|
arena.scene.queueMessage(`Pointed stones float in the air\naround ${target.name}!`);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -7,12 +7,14 @@ import { StatusEffect } from "./status-effect";
|
||||||
import * as Utils from "../utils";
|
import * as Utils from "../utils";
|
||||||
import { Moves, allMoves } from "./move";
|
import { Moves, allMoves } from "./move";
|
||||||
import { Type } from "./type";
|
import { Type } from "./type";
|
||||||
|
import { Gender } from "./gender";
|
||||||
|
|
||||||
export enum BattlerTagType {
|
export enum BattlerTagType {
|
||||||
NONE,
|
NONE,
|
||||||
RECHARGING,
|
RECHARGING,
|
||||||
FLINCHED,
|
FLINCHED,
|
||||||
CONFUSED,
|
CONFUSED,
|
||||||
|
INFATUATED,
|
||||||
SEEDED,
|
SEEDED,
|
||||||
NIGHTMARE,
|
NIGHTMARE,
|
||||||
FRENZY,
|
FRENZY,
|
||||||
|
@ -77,6 +79,10 @@ export class BattlerTag {
|
||||||
return --this.turnCount > 0;
|
return --this.turnCount > 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getDescriptor(): string {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
|
||||||
isSourceLinked(): boolean {
|
isSourceLinked(): boolean {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -130,6 +136,10 @@ export class TrappedTag extends BattlerTag {
|
||||||
pokemon.scene.queueMessage(getPokemonMessage(pokemon, ` was freed\nfrom ${this.getMoveName()}!`));
|
pokemon.scene.queueMessage(getPokemonMessage(pokemon, ` was freed\nfrom ${this.getMoveName()}!`));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getDescriptor(): string {
|
||||||
|
return 'trapping';
|
||||||
|
}
|
||||||
|
|
||||||
isSourceLinked(): boolean {
|
isSourceLinked(): boolean {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -152,6 +162,10 @@ export class FlinchedTag extends BattlerTag {
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getDescriptor(): string {
|
||||||
|
return 'flinching';
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export class ConfusedTag extends BattlerTag {
|
export class ConfusedTag extends BattlerTag {
|
||||||
|
@ -198,6 +212,58 @@ export class ConfusedTag extends BattlerTag {
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getDescriptor(): string {
|
||||||
|
return 'confusion';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class InfatuatedTag extends BattlerTag {
|
||||||
|
constructor(sourceMove: integer, sourceId: integer) {
|
||||||
|
super(BattlerTagType.INFATUATED, BattlerTagLapseType.MOVE, 1, sourceMove, sourceId);
|
||||||
|
}
|
||||||
|
|
||||||
|
canAdd(pokemon: Pokemon): boolean {
|
||||||
|
return pokemon.isOppositeGender(pokemon.scene.getPokemonById(this.sourceId));
|
||||||
|
}
|
||||||
|
|
||||||
|
onAdd(pokemon: Pokemon): void {
|
||||||
|
super.onAdd(pokemon);
|
||||||
|
|
||||||
|
pokemon.scene.queueMessage(getPokemonMessage(pokemon, ` fell in love\nwith ${pokemon.scene.getPokemonById(this.sourceId).name}!`));
|
||||||
|
}
|
||||||
|
|
||||||
|
onOverlap(pokemon: Pokemon): void {
|
||||||
|
super.onOverlap(pokemon);
|
||||||
|
|
||||||
|
pokemon.scene.queueMessage(getPokemonMessage(pokemon, ' is\nalready in love!'));
|
||||||
|
}
|
||||||
|
|
||||||
|
lapse(pokemon: Pokemon, lapseType: BattlerTagLapseType): boolean {
|
||||||
|
const ret = lapseType !== BattlerTagLapseType.CUSTOM || super.lapse(pokemon, lapseType);
|
||||||
|
|
||||||
|
if (ret) {
|
||||||
|
pokemon.scene.queueMessage(getPokemonMessage(pokemon, ` is in love\nwith ${pokemon.scene.getPokemonById(this.sourceId).name}!`));
|
||||||
|
pokemon.scene.unshiftPhase(new CommonAnimPhase(pokemon.scene, pokemon.isPlayer(), CommonAnim.ATTRACT));
|
||||||
|
|
||||||
|
if (Utils.randInt(2)) {
|
||||||
|
pokemon.scene.queueMessage(getPokemonMessage(pokemon, ' is\nimmobilized by love!'));
|
||||||
|
(pokemon.scene.getCurrentPhase() as MovePhase).cancel();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
onRemove(pokemon: Pokemon): void {
|
||||||
|
super.onRemove(pokemon);
|
||||||
|
|
||||||
|
pokemon.scene.queueMessage(getPokemonMessage(pokemon, ' got over\nits infatuation.'));
|
||||||
|
}
|
||||||
|
|
||||||
|
getDescriptor(): string {
|
||||||
|
return 'infatuation';
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export class SeedTag extends BattlerTag {
|
export class SeedTag extends BattlerTag {
|
||||||
|
@ -225,6 +291,10 @@ export class SeedTag extends BattlerTag {
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getDescriptor(): string {
|
||||||
|
return 'seeding';
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export class NightmareTag extends BattlerTag {
|
export class NightmareTag extends BattlerTag {
|
||||||
|
@ -258,6 +328,10 @@ export class NightmareTag extends BattlerTag {
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getDescriptor(): string {
|
||||||
|
return 'nightmares';
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export class IngrainTag extends TrappedTag {
|
export class IngrainTag extends TrappedTag {
|
||||||
|
@ -278,6 +352,10 @@ export class IngrainTag extends TrappedTag {
|
||||||
getTrapMessage(pokemon: Pokemon): string {
|
getTrapMessage(pokemon: Pokemon): string {
|
||||||
return getPokemonMessage(pokemon, ' planted its roots!');
|
return getPokemonMessage(pokemon, ' planted its roots!');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getDescriptor(): string {
|
||||||
|
return 'roots';
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export class AquaRingTag extends BattlerTag {
|
export class AquaRingTag extends BattlerTag {
|
||||||
|
@ -320,6 +398,10 @@ export class DrowsyTag extends BattlerTag {
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getDescriptor(): string {
|
||||||
|
return 'drowsiness';
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export abstract class DamagingTrapTag extends TrappedTag {
|
export abstract class DamagingTrapTag extends TrappedTag {
|
||||||
|
@ -357,7 +439,7 @@ export class BindTag extends DamagingTrapTag {
|
||||||
}
|
}
|
||||||
|
|
||||||
getTrapMessage(pokemon: Pokemon): string {
|
getTrapMessage(pokemon: Pokemon): string {
|
||||||
return getPokemonMessage(pokemon, ` was squeezed by\n${pokemon.scene.getPokemonById(this.sourceId)}'s ${this.getMoveName()}!`);
|
return getPokemonMessage(pokemon, ` was squeezed by\n${pokemon.scene.getPokemonById(this.sourceId).name}'s ${this.getMoveName()}!`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -367,7 +449,7 @@ export class WrapTag extends DamagingTrapTag {
|
||||||
}
|
}
|
||||||
|
|
||||||
getTrapMessage(pokemon: Pokemon): string {
|
getTrapMessage(pokemon: Pokemon): string {
|
||||||
return getPokemonMessage(pokemon, ` was WRAPPED\nby ${pokemon.scene.getPokemonById(this.sourceId)}!`);
|
return getPokemonMessage(pokemon, ` was WRAPPED\nby ${pokemon.scene.getPokemonById(this.sourceId).name}!`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -516,6 +598,8 @@ export function getBattlerTag(tagType: BattlerTagType, turnCount: integer, sourc
|
||||||
return new FlinchedTag(sourceMove);
|
return new FlinchedTag(sourceMove);
|
||||||
case BattlerTagType.CONFUSED:
|
case BattlerTagType.CONFUSED:
|
||||||
return new ConfusedTag(turnCount, sourceMove);
|
return new ConfusedTag(turnCount, sourceMove);
|
||||||
|
case BattlerTagType.INFATUATED:
|
||||||
|
return new InfatuatedTag(sourceMove, sourceId);
|
||||||
case BattlerTagType.SEEDED:
|
case BattlerTagType.SEEDED:
|
||||||
return new SeedTag(sourceId);
|
return new SeedTag(sourceId);
|
||||||
case BattlerTagType.NIGHTMARE:
|
case BattlerTagType.NIGHTMARE:
|
||||||
|
|
|
@ -55,7 +55,7 @@ export function getBerryPredicate(berryType: BerryType): BerryPredicate {
|
||||||
return (pokemon: Pokemon) => !!pokemon.status || !!pokemon.getTag(BattlerTagType.CONFUSED);
|
return (pokemon: Pokemon) => !!pokemon.status || !!pokemon.getTag(BattlerTagType.CONFUSED);
|
||||||
case BerryType.ENIGMA:
|
case BerryType.ENIGMA:
|
||||||
return (pokemon: Pokemon) => {
|
return (pokemon: Pokemon) => {
|
||||||
const opponent = pokemon.isPlayer() ? pokemon.scene.getEnemyPokemon() : pokemon.scene.getPlayerPokemon();
|
const opponent = pokemon.getOpponent();
|
||||||
const opponentLastMove = opponent ? opponent.getLastXMoves(1).find(() => true) : null; // TODO: Update so this works even if opponent has fainted
|
const opponentLastMove = opponent ? opponent.getLastXMoves(1).find(() => true) : null; // TODO: Update so this works even if opponent has fainted
|
||||||
|
|
||||||
return opponentLastMove && opponentLastMove.turn === pokemon.scene.currentBattle?.turn - 1 && opponentLastMove.result === MoveResult.SUPER_EFFECTIVE;
|
return opponentLastMove && opponentLastMove.turn === pokemon.scene.currentBattle?.turn - 1 && opponentLastMove.result === MoveResult.SUPER_EFFECTIVE;
|
||||||
|
|
|
@ -11,6 +11,7 @@ import { WeatherType } from "./weather";
|
||||||
import { ArenaTagType, ArenaTrapTag } from "./arena-tag";
|
import { ArenaTagType, ArenaTrapTag } from "./arena-tag";
|
||||||
import { BlockRecoilDamageAttr, applyAbAttrs } from "./ability";
|
import { BlockRecoilDamageAttr, applyAbAttrs } from "./ability";
|
||||||
import { PokemonHeldItemModifier } from "../modifier/modifier";
|
import { PokemonHeldItemModifier } from "../modifier/modifier";
|
||||||
|
import { Gender } from "./gender";
|
||||||
|
|
||||||
export enum MoveCategory {
|
export enum MoveCategory {
|
||||||
PHYSICAL,
|
PHYSICAL,
|
||||||
|
@ -1868,7 +1869,7 @@ export function applyFilteredMoveAttrs(attrFilter: MoveAttrFilter, user: Pokemon
|
||||||
export function getMoveTarget(user: Pokemon, move: Moves): Pokemon {
|
export function getMoveTarget(user: Pokemon, move: Moves): Pokemon {
|
||||||
const moveTarget = allMoves[move].moveTarget;
|
const moveTarget = allMoves[move].moveTarget;
|
||||||
|
|
||||||
const other = user.isPlayer() ? user.scene.getEnemyPokemon() : user.scene.getPlayerPokemon();
|
const other = user.getOpponent();
|
||||||
|
|
||||||
switch (moveTarget) {
|
switch (moveTarget) {
|
||||||
case MoveTarget.USER:
|
case MoveTarget.USER:
|
||||||
|
@ -2364,7 +2365,9 @@ export function initMoves() {
|
||||||
.attr(StatChangeAttr, BattleStat.DEF, 1, true),
|
.attr(StatChangeAttr, BattleStat.DEF, 1, true),
|
||||||
new StatusMove(Moves.MEAN_LOOK, "Mean Look", Type.NORMAL, -1, 5, -1, "Opponent cannot flee or switch.", -1, 0, 2)
|
new StatusMove(Moves.MEAN_LOOK, "Mean Look", Type.NORMAL, -1, 5, -1, "Opponent cannot flee or switch.", -1, 0, 2)
|
||||||
.attr(AddBattlerTagAttr, BattlerTagType.TRAPPED, false, 1, true),
|
.attr(AddBattlerTagAttr, BattlerTagType.TRAPPED, false, 1, true),
|
||||||
new StatusMove(Moves.ATTRACT, "Attract (N)", Type.NORMAL, 100, 15, -1, "If opponent is the opposite gender, it's less likely to attack.", -1, 0, 2),
|
new StatusMove(Moves.ATTRACT, "Attract", Type.NORMAL, 100, 15, -1, "If opponent is the opposite gender, it's less likely to attack.", -1, 0, 2)
|
||||||
|
.attr(AddBattlerTagAttr, BattlerTagType.INFATUATED)
|
||||||
|
.condition((user: Pokemon, target: Pokemon, move: Move) => user.isOppositeGender(target)),
|
||||||
new SelfStatusMove(Moves.SLEEP_TALK, "Sleep Talk", Type.NORMAL, -1, 10, 70, "User performs one of its own moves while sleeping.", -1, 0, 2)
|
new SelfStatusMove(Moves.SLEEP_TALK, "Sleep Talk", Type.NORMAL, -1, 10, 70, "User performs one of its own moves while sleeping.", -1, 0, 2)
|
||||||
.attr(BypassSleepAttr)
|
.attr(BypassSleepAttr)
|
||||||
.attr(RandomMovesetMoveAttr)
|
.attr(RandomMovesetMoveAttr)
|
||||||
|
|
|
@ -1009,7 +1009,7 @@ export class HeldItemTransferModifier extends PokemonHeldItemModifier {
|
||||||
|
|
||||||
apply(args: any[]): boolean {
|
apply(args: any[]): boolean {
|
||||||
const pokemon = args[0] as Pokemon;
|
const pokemon = args[0] as Pokemon;
|
||||||
const targetPokemon = pokemon.isPlayer() ? pokemon.scene.getEnemyPokemon() : pokemon.scene.getPlayerPokemon();
|
const targetPokemon = pokemon.getOpponent();
|
||||||
if (!targetPokemon)
|
if (!targetPokemon)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
|
|
|
@ -23,7 +23,7 @@ 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, BattleStatMultiplierAbAttr, BlockCritAbAttr, TypeImmunityAbAttr, VariableMovePowerAbAttr, abilities, applyBattleStatMultiplierAbAttrs, applyPreAttackAbAttrs, applyPreDefendAbAttrs } from './data/ability';
|
import { Abilities, Ability, BattleStatMultiplierAbAttr, BlockCritAbAttr, PreApplyBattlerTagAbAttr, StatusEffectImmunityAbAttr, TypeImmunityAbAttr, VariableMovePowerAbAttr, abilities, applyBattleStatMultiplierAbAttrs, applyPreApplyBattlerTagAbAttrs, applyPreAttackAbAttrs, applyPreDefendAbAttrs, applyPreSetStatusAbAttrs } from './data/ability';
|
||||||
import PokemonData from './system/pokemon-data';
|
import PokemonData from './system/pokemon-data';
|
||||||
|
|
||||||
export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
||||||
|
@ -483,6 +483,13 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
||||||
this.levelExp = this.exp - getLevelTotalExp(this.level, this.getSpeciesForm().growthRate);
|
this.levelExp = this.exp - getLevelTotalExp(this.level, this.getSpeciesForm().growthRate);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getOpponent(): Pokemon {
|
||||||
|
const ret = this.isPlayer() ? this.scene.getEnemyPokemon() : this.scene.getPlayerPokemon();
|
||||||
|
if (ret.summonData)
|
||||||
|
return ret;
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
apply(source: Pokemon, battlerMove: PokemonMove): MoveResult {
|
apply(source: Pokemon, battlerMove: PokemonMove): MoveResult {
|
||||||
let result: MoveResult;
|
let result: MoveResult;
|
||||||
const move = battlerMove.getMove();
|
const move = battlerMove.getMove();
|
||||||
|
@ -600,7 +607,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
||||||
if (!this.hp) {
|
if (!this.hp) {
|
||||||
this.scene.pushPhase(new FaintPhase(this.scene, this.isPlayer()));
|
this.scene.pushPhase(new FaintPhase(this.scene, this.isPlayer()));
|
||||||
this.resetSummonData();
|
this.resetSummonData();
|
||||||
(this.isPlayer() ? this.scene.getEnemyPokemon() : this.scene.getPlayerPokemon()).resetBattleSummonData();
|
this.getOpponent()?.resetBattleSummonData();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -613,7 +620,10 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
||||||
|
|
||||||
const newTag = getBattlerTag(tagType, turnCount || 0, sourceMove, sourceId);
|
const newTag = getBattlerTag(tagType, turnCount || 0, sourceMove, sourceId);
|
||||||
|
|
||||||
if (newTag.canAdd(this)) {
|
const cancelled = new Utils.BooleanHolder(false);
|
||||||
|
applyPreApplyBattlerTagAbAttrs(PreApplyBattlerTagAbAttr, this, newTag, cancelled);
|
||||||
|
|
||||||
|
if (!cancelled.value && newTag.canAdd(this)) {
|
||||||
this.summonData.tags.push(newTag);
|
this.summonData.tags.push(newTag);
|
||||||
newTag.onAdd(this);
|
newTag.onAdd(this);
|
||||||
|
|
||||||
|
@ -761,6 +771,10 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
isOppositeGender(pokemon: Pokemon): boolean {
|
||||||
|
return this.gender !== Gender.GENDERLESS && pokemon.gender === (this.gender === Gender.MALE ? Gender.FEMALE : Gender.MALE);
|
||||||
|
}
|
||||||
|
|
||||||
trySetStatus(effect: StatusEffect): boolean {
|
trySetStatus(effect: StatusEffect): boolean {
|
||||||
if (this.status)
|
if (this.status)
|
||||||
return false;
|
return false;
|
||||||
|
@ -779,6 +793,13 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
||||||
return false;
|
return false;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const cancelled = new Utils.BooleanHolder(false);
|
||||||
|
applyPreSetStatusAbAttrs(StatusEffectImmunityAbAttr, this, effect, cancelled);
|
||||||
|
|
||||||
|
if (cancelled.value)
|
||||||
|
return false;
|
||||||
|
|
||||||
this.status = new Status(effect);
|
this.status = new Status(effect);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue