Implement some abilities

This commit is contained in:
Flashfyre 2023-12-22 01:16:56 -05:00
parent 4a575a45a9
commit 7deab1c545
5 changed files with 149 additions and 26 deletions

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 { ArenaTagType, ArenaTrapTag, TrickRoomTag } from "./data/arena-tag"; import { ArenaTagType, ArenaTrapTag, TrickRoomTag } from "./data/arena-tag";
import { Abilities, CheckTrappedAbAttr, IgnoreOpponentStatChangesAbAttr, PostDefendAbAttr, PostSummonAbAttr, PostTurnAbAttr, PostWeatherLapseAbAttr, PreWeatherDamageAbAttr, ProtectStatAbAttr, StatChangeMultiplierAbAttr, SuppressWeatherEffectAbAttr, applyAbAttrs, applyCheckTrappedAbAttrs, applyPostDefendAbAttrs, applyPostSummonAbAttrs, applyPostTurnAbAttrs, applyPostWeatherLapseAbAttrs, applyPreStatChangeAbAttrs, applyPreWeatherEffectAbAttrs } from "./data/ability"; import { Abilities, CheckTrappedAbAttr, IgnoreOpponentStatChangesAbAttr, PostAttackAbAttr, PostDefendAbAttr, PostSummonAbAttr, PostTurnAbAttr, PostWeatherLapseAbAttr, PreWeatherDamageAbAttr, ProtectStatAbAttr, StatChangeMultiplierAbAttr, SuppressWeatherEffectAbAttr, applyAbAttrs, applyCheckTrappedAbAttrs, applyPostAttackAbAttrs, applyPostDefendAbAttrs, applyPostSummonAbAttrs, applyPostTurnAbAttrs, applyPostWeatherLapseAbAttrs, applyPreStatChangeAbAttrs, applyPreWeatherEffectAbAttrs } from "./data/ability";
import { Unlockables, getUnlockableName } from "./system/unlockables"; import { Unlockables, getUnlockableName } from "./system/unlockables";
import { getBiomeKey } from "./arena"; import { getBiomeKey } from "./arena";
import { BattleType, BattlerIndex, TurnCommand } from "./battle"; import { BattleType, BattlerIndex, TurnCommand } from "./battle";
@ -1744,6 +1744,7 @@ export class MoveEffectPhase extends PokemonPhase {
if (!user.isPlayer() && this.move instanceof AttackMove) if (!user.isPlayer() && this.move instanceof AttackMove)
user.scene.applyModifiers(EnemyAttackStatusEffectChanceModifier, false, target); user.scene.applyModifiers(EnemyAttackStatusEffectChanceModifier, false, target);
} }
applyPostAttackAbAttrs(PostAttackAbAttr, user, target, this.move, hitResult);
if (this.move instanceof AttackMove) if (this.move instanceof AttackMove)
this.scene.applyModifiers(ContactHeldItemTransferChanceModifier, this.player, user, target.getFieldIndex()); this.scene.applyModifiers(ContactHeldItemTransferChanceModifier, this.player, user, target.getFieldIndex());
}) })

View File

@ -10,6 +10,7 @@ import { StatusEffect, getStatusEffectDescriptor } from "./status-effect";
import { MoveFlags, Moves, RecoilAttr } from "./move"; import { MoveFlags, Moves, RecoilAttr } from "./move";
import { ArenaTagType } from "./arena-tag"; import { ArenaTagType } from "./arena-tag";
import { Stat } from "./pokemon-stat"; import { Stat } from "./pokemon-stat";
import { PokemonHeldItemModifier } from "../modifier/modifier";
export class Ability { export class Ability {
public id: Abilities; public id: Abilities;
@ -96,6 +97,8 @@ export class DoubleBattleChanceAbAttr extends AbAttr {
} }
} }
type PreDefendAbAttrCondition = (pokemon: Pokemon, attacker: Pokemon, move: PokemonMove) => boolean;
export class PreDefendAbAttr extends AbAttr { 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;
@ -260,6 +263,29 @@ export class PostDefendAbAttr extends AbAttr {
} }
} }
export class MoveImmunityAbAttr extends PreDefendAbAttr {
private immuneCondition: PreDefendAbAttrCondition;
constructor(immuneCondition: PreDefendAbAttrCondition) {
super(true);
this.immuneCondition = immuneCondition;
}
applyPreDefend(pokemon: Pokemon, attacker: Pokemon, move: PokemonMove, cancelled: Utils.BooleanHolder, args: any[]): boolean {
if (this.immuneCondition(pokemon, attacker, move)) {
cancelled.value = true;
return true;
}
return false;
}
getTriggerMessage(pokemon: Pokemon, ...args: any[]): string {
return `It doesn\'t affect ${pokemon.name}!`;
}
}
export class PostDefendTypeChangeAbAttr extends PostDefendAbAttr { export class PostDefendTypeChangeAbAttr extends PostDefendAbAttr {
applyPostDefend(pokemon: Pokemon, attacker: Pokemon, move: PokemonMove, hitResult: HitResult, args: any[]): boolean { applyPostDefend(pokemon: Pokemon, attacker: Pokemon, move: PokemonMove, hitResult: HitResult, args: any[]): boolean {
if (hitResult < HitResult.NO_EFFECT) { if (hitResult < HitResult.NO_EFFECT) {
@ -433,6 +459,35 @@ export class BattleStatMultiplierAbAttr extends AbAttr {
} }
} }
export class PostAttackAbAttr extends AbAttr {
applyPostAttack(pokemon: Pokemon, defender: Pokemon, move: PokemonMove, hitResult: HitResult, args: any[]): boolean {
return false;
}
}
export class PostAttackStealHeldItemAbAttr extends PostAttackAbAttr {
applyPostAttack(pokemon: Pokemon, defender: Pokemon, move: PokemonMove, hitResult: HitResult, args: any[]): boolean {
if (hitResult < HitResult.NO_EFFECT) {
const heldItems = this.getTargetHeldItems(defender).filter(i => i.getTransferrable(false));
if (heldItems.length) {
const stolenItem = heldItems[Utils.randInt(heldItems.length)];
// TODO: Add support for promises
pokemon.scene.tryTransferHeldItemModifier(stolenItem, pokemon, false, false).then(success => {
if (success)
pokemon.scene.queueMessage(getPokemonMessage(pokemon, ` stole\n${defender.name}'s ${stolenItem.type.name}!`));
});
return true;
}
}
return false;
}
getTargetHeldItems(target: Pokemon): PokemonHeldItemModifier[] {
return target.scene.findModifiers(m => m instanceof PokemonHeldItemModifier
&& (m as PokemonHeldItemModifier).pokemonId === target.id, target.isPlayer()) as PokemonHeldItemModifier[];
}
}
export class IgnoreOpponentStatChangesAbAttr extends AbAttr { export class IgnoreOpponentStatChangesAbAttr extends AbAttr {
constructor() { constructor() {
super(false); super(false);
@ -779,9 +834,21 @@ export class PostTurnAbAttr extends AbAttr {
} }
} }
export class PostTurnSpeedBoostAbAttr extends PostTurnAbAttr { export class PostTurnStatChangeAbAttr extends PostTurnAbAttr {
private stats: BattleStat[];
private levels: integer;
constructor(stats: BattleStat | BattleStat[], levels: integer) {
super(true);
this.stats = Array.isArray(stats)
? stats
: [ stats ];
this.levels = levels;
}
applyPostTurn(pokemon: Pokemon, args: any[]): boolean { applyPostTurn(pokemon: Pokemon, args: any[]): boolean {
pokemon.scene.unshiftPhase(new StatChangePhase(pokemon.scene, pokemon.getBattlerIndex(), true, [ BattleStat.SPD ], 1)); pokemon.scene.unshiftPhase(new StatChangePhase(pokemon.scene, pokemon.getBattlerIndex(), true, this.stats, this.levels));
return true; return true;
} }
} }
@ -815,6 +882,14 @@ export class StatChangeMultiplierAbAttr extends AbAttr {
} }
} }
export class DoubleBerryEffectAbAttr extends AbAttr {
apply(pokemon: Pokemon, cancelled: Utils.BooleanHolder, args: any[]): boolean {
(args[0] as Utils.NumberHolder).value *= 2;
return true;
}
}
export class CheckTrappedAbAttr extends AbAttr { export class CheckTrappedAbAttr extends AbAttr {
applyCheckTrapped(pokemon: Pokemon, trapped: Utils.BooleanHolder, args: any[]): boolean { applyCheckTrapped(pokemon: Pokemon, trapped: Utils.BooleanHolder, args: any[]): boolean {
return false; return false;
@ -963,6 +1038,29 @@ export function applyPreAttackAbAttrs(attrType: { new(...args: any[]): PreAttack
pokemon.scene.clearPhaseQueueSplice(); pokemon.scene.clearPhaseQueueSplice();
} }
export function applyPostAttackAbAttrs(attrType: { new(...args: any[]): PostAttackAbAttr },
pokemon: Pokemon, defender: Pokemon, move: PokemonMove, hitResult: HitResult, ...args: any[]): void {
if (!pokemon.canApplyAbility())
return;
const ability = pokemon.getAbility();
const attrs = ability.getAttrs(attrType) as PostAttackAbAttr[];
for (let attr of attrs) {
if (!canApplyAttr(pokemon, attr))
continue;
pokemon.scene.setPhaseQueueSplice();
if (attr.applyPostAttack(pokemon, defender, move, hitResult, args)) {
if (attr.showAbility)
queueShowAbility(pokemon);
const message = attr.getTriggerMessage(pokemon, defender, move);
if (message)
pokemon.scene.queueMessage(message);
}
}
pokemon.scene.clearPhaseQueueSplice();
}
export function applyPostSummonAbAttrs(attrType: { new(...args: any[]): PostSummonAbAttr }, export function applyPostSummonAbAttrs(attrType: { new(...args: any[]): PostSummonAbAttr },
pokemon: Pokemon, ...args: any[]): void { pokemon: Pokemon, ...args: any[]): void {
if (!pokemon.canApplyAbility()) if (!pokemon.canApplyAbility())
@ -1479,7 +1577,7 @@ export function initAbilities() {
new Ability(Abilities.DRIZZLE, "Drizzle", "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)
.attr(PostSummonWeatherChangeAbAttr, WeatherType.RAIN), .attr(PostSummonWeatherChangeAbAttr, WeatherType.RAIN),
new Ability(Abilities.SPEED_BOOST, "Speed Boost", "Its Speed stat is boosted every turn.", 3) new Ability(Abilities.SPEED_BOOST, "Speed Boost", "Its Speed stat is boosted every turn.", 3)
.attr(PostTurnSpeedBoostAbAttr), .attr(PostTurnStatChangeAbAttr, BattleStat.SPD, 1),
new Ability(Abilities.BATTLE_ARMOR, "Battle Armor", "Hard armor protects the Pokémon from critical hits.", 3) new Ability(Abilities.BATTLE_ARMOR, "Battle Armor", "Hard armor protects the Pokémon from critical hits.", 3)
.attr(BlockCritAbAttr), .attr(BlockCritAbAttr),
new Ability(Abilities.STURDY, "Sturdy (N)", "It cannot be knocked out with one hit. One-hit KO moves cannot knock it out, either.", 3), new Ability(Abilities.STURDY, "Sturdy (N)", "It cannot be knocked out with one hit. One-hit KO moves cannot knock it out, either.", 3),
@ -1555,7 +1653,8 @@ export function initAbilities() {
new Ability(Abilities.MAGNET_PULL, "Magnet Pull", "Prevents Steel-type Pokémon from escaping using its magnetic force.", 3) new Ability(Abilities.MAGNET_PULL, "Magnet Pull", "Prevents Steel-type Pokémon from escaping using its magnetic force.", 3)
/*.attr(ArenaTrapAbAttr) /*.attr(ArenaTrapAbAttr)
.condition((pokemon: Pokemon) => pokemon.getOpponent()?.isOfType(Type.STEEL))*/, .condition((pokemon: Pokemon) => pokemon.getOpponent()?.isOfType(Type.STEEL))*/,
new Ability(Abilities.SOUNDPROOF, "Soundproof (N)", "Soundproofing gives the Pokémon full immunity to all sound-based moves.", 3), new Ability(Abilities.SOUNDPROOF, "Soundproof", "Soundproofing gives the Pokémon full immunity to all sound-based moves.", 3)
.attr(MoveImmunityAbAttr, (pokemon, attacker, move) => pokemon !== attacker && move.getMove().hasFlag(MoveFlags.SOUND_BASED)),
new Ability(Abilities.RAIN_DISH, "Rain Dish", "The Pokémon gradually regains HP in rain.", 3) new Ability(Abilities.RAIN_DISH, "Rain Dish", "The Pokémon gradually regains HP in rain.", 3)
.attr(PostWeatherLapseHealAbAttr, 1, WeatherType.RAIN, WeatherType.HEAVY_RAIN), .attr(PostWeatherLapseHealAbAttr, 1, WeatherType.RAIN, WeatherType.HEAVY_RAIN),
new Ability(Abilities.SAND_STREAM, "Sand Stream", "The Pokémon summons a sandstorm when it enters a battle.", 3) new Ability(Abilities.SAND_STREAM, "Sand Stream", "The Pokémon summons a sandstorm when it enters a battle.", 3)
@ -1703,7 +1802,9 @@ export function initAbilities() {
new Ability(Abilities.FLARE_BOOST, "Flare Boost (N)", "Powers up special attacks when the Pokémon is burned.", 5), new Ability(Abilities.FLARE_BOOST, "Flare Boost (N)", "Powers up special attacks when the Pokémon is burned.", 5),
new Ability(Abilities.HARVEST, "Harvest (N)", "May create another Berry after one is used.", 5), new Ability(Abilities.HARVEST, "Harvest (N)", "May create another Berry after one is used.", 5),
new Ability(Abilities.TELEPATHY, "Telepathy (N)", "Anticipates an ally's attack and dodges it.", 5), new Ability(Abilities.TELEPATHY, "Telepathy (N)", "Anticipates an ally's attack and dodges it.", 5),
new Ability(Abilities.MOODY, "Moody (N)", "Raises one stat sharply and lowers another every turn.", 5), new Ability(Abilities.MOODY, "Moody", "Raises one stat sharply and lowers another every turn.", 5)
.attr(PostTurnStatChangeAbAttr, BattleStat.RAND, 2)
.attr(PostTurnStatChangeAbAttr, BattleStat.RAND, -1),
new Ability(Abilities.OVERCOAT, "Overcoat", "Protects the Pokémon from things like sand, hail, and powder.", 5) new Ability(Abilities.OVERCOAT, "Overcoat", "Protects the Pokémon from things like sand, hail, and powder.", 5)
.attr(BlockWeatherDamageAttr), .attr(BlockWeatherDamageAttr),
new Ability(Abilities.POISON_TOUCH, "Poison Touch", "May poison a target when the Pokémon makes contact.", 5) new Ability(Abilities.POISON_TOUCH, "Poison Touch", "May poison a target when the Pokémon makes contact.", 5)
@ -1740,8 +1841,10 @@ export function initAbilities() {
new Ability(Abilities.CHEEK_POUCH, "Cheek Pouch (N)", "Restores HP as well when the Pokémon eats a Berry.", 6), new Ability(Abilities.CHEEK_POUCH, "Cheek Pouch (N)", "Restores HP as well when the Pokémon eats a Berry.", 6),
new Ability(Abilities.PROTEAN, "Protean (N)", "Changes the Pokémon's type to the type of the move it's about to use.", 6), new Ability(Abilities.PROTEAN, "Protean (N)", "Changes the Pokémon's type to the type of the move it's about to use.", 6),
new Ability(Abilities.FUR_COAT, "Fur Coat (N)", "Halves the damage from physical moves.", 6), new Ability(Abilities.FUR_COAT, "Fur Coat (N)", "Halves the damage from physical moves.", 6),
new Ability(Abilities.MAGICIAN, "Magician (N)", "The Pokémon steals the held item of a Pokémon it hits with a move.", 6), new Ability(Abilities.MAGICIAN, "Magician", "The Pokémon steals the held item of a Pokémon it hits with a move.", 6)
new Ability(Abilities.BULLETPROOF, "Bulletproof (N)", "Protects the Pokémon from some ball and bomb moves.", 6), .attr(PostAttackStealHeldItemAbAttr),
new Ability(Abilities.BULLETPROOF, "Bulletproof", "Protects the Pokémon from some ball and bomb moves.", 6)
.attr(MoveImmunityAbAttr, (pokemon, attacker, move) => pokemon !== attacker && move.getMove().hasFlag(MoveFlags.BALLBOMB_MOVE)),
new Ability(Abilities.COMPETITIVE, "Competitive (N)", "Boosts the Sp. Atk stat sharply when a stat is lowered.", 6), new Ability(Abilities.COMPETITIVE, "Competitive (N)", "Boosts the Sp. Atk stat sharply when a stat is lowered.", 6),
new Ability(Abilities.STRONG_JAW, "Strong Jaw (N)", "The Pokémon's strong jaw boosts the power of its biting moves.", 6), new Ability(Abilities.STRONG_JAW, "Strong Jaw (N)", "The Pokémon's strong jaw boosts the power of its biting moves.", 6),
new Ability(Abilities.REFRIGERATE, "Refrigerate (N)", "Normal-type moves become Ice-type moves. The power of those moves is boosted a little.", 6), new Ability(Abilities.REFRIGERATE, "Refrigerate (N)", "Normal-type moves become Ice-type moves. The power of those moves is boosted a little.", 6),
@ -1824,7 +1927,8 @@ export function initAbilities() {
new Ability(Abilities.PUNK_ROCK, "Punk Rock (N)", "Boosts the power of sound-based moves. The Pokémon also takes half the damage from these kinds of moves.", 8), new Ability(Abilities.PUNK_ROCK, "Punk Rock (N)", "Boosts the power of sound-based moves. The Pokémon also takes half the damage from these kinds of moves.", 8),
new Ability(Abilities.SAND_SPIT, "Sand Spit (N)", "The Pokémon creates a sandstorm when it's hit by an attack.", 8), new Ability(Abilities.SAND_SPIT, "Sand Spit (N)", "The Pokémon creates a sandstorm when it's hit by an attack.", 8),
new Ability(Abilities.ICE_SCALES, "Ice Scales (N)", "The Pokémon is protected by ice scales, which halve the damage taken from special moves.", 8), new Ability(Abilities.ICE_SCALES, "Ice Scales (N)", "The Pokémon is protected by ice scales, which halve the damage taken from special moves.", 8),
new Ability(Abilities.RIPEN, "Ripen (N)", "Ripens Berries and doubles their effect.", 8), new Ability(Abilities.RIPEN, "Ripen", "Ripens Berries and doubles their effect.", 8)
.attr(DoubleBerryEffectAbAttr),
new Ability(Abilities.ICE_FACE, "Ice Face (N)", "The Pokémon's ice head can take a physical attack as a substitute, but the attack also changes the Pokémon's appearance. The ice will be restored when it hails.", 8), new Ability(Abilities.ICE_FACE, "Ice Face (N)", "The Pokémon's ice head can take a physical attack as a substitute, but the attack also changes the Pokémon's appearance. The ice will be restored when it hails.", 8),
new Ability(Abilities.POWER_SPOT, "Power Spot (N)", "Just being next to the Pokémon powers up moves.", 8), new Ability(Abilities.POWER_SPOT, "Power Spot (N)", "Just being next to the Pokémon powers up moves.", 8),
new Ability(Abilities.MIMICRY, "Mimicry (N)", "Changes the Pokémon's type depending on the terrain.", 8), new Ability(Abilities.MIMICRY, "Mimicry (N)", "Changes the Pokémon's type depending on the terrain.", 8),
@ -1834,7 +1938,8 @@ export function initAbilities() {
new Ability(Abilities.WANDERING_SPIRIT, "Wandering Spirit (N)", "The Pokémon exchanges Abilities with a Pokémon that hits it with a move that makes direct contact.", 8), new Ability(Abilities.WANDERING_SPIRIT, "Wandering Spirit (N)", "The Pokémon exchanges Abilities with a Pokémon that hits it with a move that makes direct contact.", 8),
new Ability(Abilities.GORILLA_TACTICS, "Gorilla Tactics (N)", "Boosts the Pokémon's Attack stat but only allows the use of the first selected move.", 8), new Ability(Abilities.GORILLA_TACTICS, "Gorilla Tactics (N)", "Boosts the Pokémon's Attack stat but only allows the use of the first selected move.", 8),
new Ability(Abilities.NEUTRALIZING_GAS, "Neutralizing Gas (N)", "If the Pokémon with Neutralizing Gas is in the battle, the effects of all Pokémon's Abilities will be nullified or will not be triggered.", 8), new Ability(Abilities.NEUTRALIZING_GAS, "Neutralizing Gas (N)", "If the Pokémon with Neutralizing Gas is in the battle, the effects of all Pokémon's Abilities will be nullified or will not be triggered.", 8),
new Ability(Abilities.PASTEL_VEIL, "Pastel Veil (N)", "Protects the Pokémon and its ally Pokémon from being poisoned.", 8), new Ability(Abilities.PASTEL_VEIL, "Pastel Veil", "Protects the Pokémon and its ally Pokémon from being poisoned.", 8)
.attr(StatusEffectImmunityAbAttr, StatusEffect.POISON),
new Ability(Abilities.HUNGER_SWITCH, "Hunger Switch (N)", "The Pokémon changes its form, alternating between its Full Belly Mode and Hangry Mode after the end of each turn.", 8), new Ability(Abilities.HUNGER_SWITCH, "Hunger Switch (N)", "The Pokémon changes its form, alternating between its Full Belly Mode and Hangry Mode after the end of each turn.", 8),
new Ability(Abilities.QUICK_DRAW, "Quick Draw (N)", "Enables the Pokémon to move first occasionally.", 8), new Ability(Abilities.QUICK_DRAW, "Quick Draw (N)", "Enables the Pokémon to move first occasionally.", 8),
new Ability(Abilities.UNSEEN_FIST, "Unseen Fist (N)", "If the Pokémon uses moves that make direct contact, it can attack the target even if the target protects itself.", 8), new Ability(Abilities.UNSEEN_FIST, "Unseen Fist (N)", "If the Pokémon uses moves that make direct contact, it can attack the target even if the target protects itself.", 8),

View File

@ -6,6 +6,7 @@ import { BattleStat } from "./battle-stat";
import { BattlerTagType } from "./battler-tag"; import { BattlerTagType } from "./battler-tag";
import { getStatusEffectHealText } from "./status-effect"; import { getStatusEffectHealText } from "./status-effect";
import * as Utils from "../utils"; import * as Utils from "../utils";
import { DoubleBerryEffectAbAttr, applyAbAttrs } from "./ability";
export enum BerryType { export enum BerryType {
SITRUS, SITRUS,
@ -77,10 +78,12 @@ export type BerryEffectFunc = (pokemon: Pokemon) => void;
export function getBerryEffectFunc(berryType: BerryType): BerryEffectFunc { export function getBerryEffectFunc(berryType: BerryType): BerryEffectFunc {
switch (berryType) { switch (berryType) {
case BerryType.SITRUS: case BerryType.SITRUS:
case BerryType.ENIGMA: case BerryType.ENIGMA:
return (pokemon: Pokemon) => { return (pokemon: Pokemon) => {
const hpHealed = new Utils.NumberHolder(Math.floor(pokemon.getMaxHp() / 4));
applyAbAttrs(DoubleBerryEffectAbAttr, pokemon, null, hpHealed);
pokemon.scene.unshiftPhase(new PokemonHealPhase(pokemon.scene, pokemon.getBattlerIndex(), pokemon.scene.unshiftPhase(new PokemonHealPhase(pokemon.scene, pokemon.getBattlerIndex(),
Math.floor(pokemon.getMaxHp() / 4), getPokemonMessage(pokemon, `'s ${getBerryName(berryType)}\nrestored its HP!`), true)); hpHealed.value, getPokemonMessage(pokemon, `'s ${getBerryName(berryType)}\nrestored its HP!`), true));
}; };
case BerryType.LUM: case BerryType.LUM:
return (pokemon: Pokemon) => { return (pokemon: Pokemon) => {
@ -98,13 +101,19 @@ export function getBerryEffectFunc(berryType: BerryType): BerryEffectFunc {
case BerryType.SALAC: case BerryType.SALAC:
return (pokemon: Pokemon) => { return (pokemon: Pokemon) => {
const battleStat = (berryType - BerryType.LIECHI) as BattleStat; const battleStat = (berryType - BerryType.LIECHI) as BattleStat;
pokemon.scene.unshiftPhase(new StatChangePhase(pokemon.scene, pokemon.getBattlerIndex(), true, [ battleStat ], 1)); const statLevels = new Utils.NumberHolder(1);
applyAbAttrs(DoubleBerryEffectAbAttr, pokemon, null, statLevels);
pokemon.scene.unshiftPhase(new StatChangePhase(pokemon.scene, pokemon.getBattlerIndex(), true, [ battleStat ], statLevels.value));
}; };
case BerryType.LANSAT: case BerryType.LANSAT:
return (pokemon: Pokemon) => { return (pokemon: Pokemon) => {
pokemon.addTag(BattlerTagType.CRIT_BOOST); pokemon.addTag(BattlerTagType.CRIT_BOOST);
}; };
case BerryType.STARF: case BerryType.STARF:
return (pokemon: Pokemon) => pokemon.scene.unshiftPhase(new StatChangePhase(pokemon.scene, pokemon.getBattlerIndex(), true, [ BattleStat.RAND ], 2)); return (pokemon: Pokemon) => {
const statLevels = new Utils.NumberHolder(2);
applyAbAttrs(DoubleBerryEffectAbAttr, pokemon, null, statLevels);
return pokemon.scene.unshiftPhase(new StatChangePhase(pokemon.scene, pokemon.getBattlerIndex(), true, [ BattleStat.RAND ], statLevels.value));
};
} }
} }

View File

@ -1677,17 +1677,21 @@ export class StealHeldItemAttr extends MoveEffectAttr {
super(false, MoveEffectTrigger.HIT); super(false, MoveEffectTrigger.HIT);
} }
apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean { apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): Promise<boolean> {
const heldItems = this.getTargetHeldItems(target).filter(i => i.getTransferrable(false)); return new Promise<boolean>(resolve => {
if (heldItems.length) { const heldItems = this.getTargetHeldItems(target).filter(i => i.getTransferrable(false));
const stolenItem = heldItems[Utils.randInt(heldItems.length)]; if (heldItems.length) {
user.scene.tryTransferHeldItemModifier(stolenItem, user, false, false); const stolenItem = heldItems[Utils.randInt(heldItems.length)];
// Assumes the transfer was successful user.scene.tryTransferHeldItemModifier(stolenItem, user, false, false).then(success => {
user.scene.queueMessage(getPokemonMessage(user, ` stole\n${target.name}'s ${stolenItem.type.name}!`)); if (success)
return true; user.scene.queueMessage(getPokemonMessage(user, ` stole\n${target.name}'s ${stolenItem.type.name}!`));
} resolve(success);
});
return;
}
return false; resolve(false);
});
} }
getTargetHeldItems(target: Pokemon): PokemonHeldItemModifier[] { getTargetHeldItems(target: Pokemon): PokemonHeldItemModifier[] {

View File

@ -22,7 +22,7 @@ import { WeatherType } from './data/weather';
import { TempBattleStat } from './data/temp-battle-stat'; import { TempBattleStat } from './data/temp-battle-stat';
import { ArenaTagType, WeakenMoveTypeTag } from './data/arena-tag'; import { ArenaTagType, WeakenMoveTypeTag } from './data/arena-tag';
import { Biome } from './data/biome'; import { Biome } from './data/biome';
import { Abilities, Ability, BattleStatMultiplierAbAttr, BlockCritAbAttr, IgnoreOpponentStatChangesAbAttr, NonSuperEffectiveImmunityAbAttr, PreApplyBattlerTagAbAttr, StabBoostAbAttr, StatusEffectImmunityAbAttr, TypeImmunityAbAttr, VariableMovePowerAbAttr, WeightMultiplierAbAttr, allAbilities, applyAbAttrs, applyBattleStatMultiplierAbAttrs, applyPostDefendAbAttrs, applyPreApplyBattlerTagAbAttrs, applyPreAttackAbAttrs, applyPreDefendAbAttrs, applyPreSetStatusAbAttrs } from './data/ability'; import { Abilities, Ability, BattleStatMultiplierAbAttr, BlockCritAbAttr, IgnoreOpponentStatChangesAbAttr, MoveImmunityAbAttr, NonSuperEffectiveImmunityAbAttr, PreApplyBattlerTagAbAttr, StabBoostAbAttr, StatusEffectImmunityAbAttr, TypeImmunityAbAttr, VariableMovePowerAbAttr, WeightMultiplierAbAttr, allAbilities, applyAbAttrs, applyBattleStatMultiplierAbAttrs, applyPostDefendAbAttrs, applyPreApplyBattlerTagAbAttrs, applyPreAttackAbAttrs, applyPreDefendAbAttrs, applyPreSetStatusAbAttrs } from './data/ability';
import PokemonData from './system/pokemon-data'; import PokemonData from './system/pokemon-data';
import { BattlerIndex } from './battle'; import { BattlerIndex } from './battle';
import { Mode } from './ui/ui'; import { Mode } from './ui/ui';
@ -889,6 +889,8 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
if (!typeless) if (!typeless)
applyPreDefendAbAttrs(TypeImmunityAbAttr, this, source, battlerMove, cancelled, typeMultiplier); applyPreDefendAbAttrs(TypeImmunityAbAttr, this, source, battlerMove, cancelled, typeMultiplier);
if (!cancelled.value)
applyPreDefendAbAttrs(MoveImmunityAbAttr, this, source, battlerMove, cancelled, typeMultiplier);
if (cancelled.value) if (cancelled.value)
result = HitResult.NO_EFFECT; result = HitResult.NO_EFFECT;
@ -1009,6 +1011,8 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
case MoveCategory.STATUS: case MoveCategory.STATUS:
if (!typeless) if (!typeless)
applyPreDefendAbAttrs(TypeImmunityAbAttr, this, source, battlerMove, cancelled, typeMultiplier); applyPreDefendAbAttrs(TypeImmunityAbAttr, this, source, battlerMove, cancelled, typeMultiplier);
if (!cancelled.value)
applyPreDefendAbAttrs(MoveImmunityAbAttr, this, source, battlerMove, cancelled, typeMultiplier);
result = cancelled.value || !typeMultiplier.value ? HitResult.NO_EFFECT : HitResult.STATUS; result = cancelled.value || !typeMultiplier.value ? HitResult.NO_EFFECT : HitResult.STATUS;
break; break;
} }
@ -2109,8 +2113,8 @@ export enum HitResult {
EFFECTIVE = 1, EFFECTIVE = 1,
SUPER_EFFECTIVE, SUPER_EFFECTIVE,
NOT_VERY_EFFECTIVE, NOT_VERY_EFFECTIVE,
NO_EFFECT,
ONE_HIT_KO, ONE_HIT_KO,
NO_EFFECT,
STATUS, STATUS,
FAIL, FAIL,
MISS, MISS,