Implement some abilities

pull/1/head
Flashfyre 2023-05-06 12:13:35 -04:00
parent 15cfd3bad4
commit c88828548c
3 changed files with 133 additions and 38 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 { ArenaTrapTag, TrickRoomTag } from "./data/arena-tag"; import { ArenaTrapTag, TrickRoomTag } from "./data/arena-tag";
import { CheckTrappedAbAttr, PostDefendAbAttr, PostSummonAbAttr, PostTurnAbAttr, PostWeatherLapseAbAttr, PreWeatherDamageAbAttr, ProtectStatAttr, SuppressWeatherEffectAbAttr, applyCheckTrappedAbAttrs, applyPostDefendAbAttrs, applyPostSummonAbAttrs, applyPostTurnAbAttrs, applyPostWeatherLapseAbAttrs, applyPreStatChangeAbAttrs, applyPreWeatherEffectAbAttrs } from "./data/ability"; import { CheckTrappedAbAttr, PostDefendAbAttr, PostSummonAbAttr, PostTurnAbAttr, PostWeatherLapseAbAttr, PreWeatherDamageAbAttr, ProtectStatAbAttr, 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 {
@ -1241,7 +1241,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)
applyPreStatChangeAbAttrs(ProtectStatAttr, this.getPokemon(), stat, cancelled); applyPreStatChangeAbAttrs(ProtectStatAbAttr, this.getPokemon(), stat, cancelled);
return !cancelled.value; return !cancelled.value;
}); });

View File

@ -51,6 +51,12 @@ export class Ability {
type AbAttrCondition = (pokemon: Pokemon) => boolean; type AbAttrCondition = (pokemon: Pokemon) => boolean;
export abstract class AbAttr { export abstract class AbAttr {
public showAbility: boolean;
constructor(showAbility?: boolean) {
this.showAbility = showAbility === undefined || showAbility;
}
apply(pokemon: Pokemon, cancelled: Utils.BooleanHolder, args: any[]): boolean { apply(pokemon: Pokemon, cancelled: Utils.BooleanHolder, args: any[]): boolean {
return false; return false;
} }
@ -94,19 +100,19 @@ export class BlockItemTheftAbAttr extends AbAttr {
} }
} }
export class TypeWeaknessAbAttr extends PreDefendAbAttr { export class ReceivedTypeDamageMultiplierAbAttr extends PreDefendAbAttr {
private weakType: Type; private moveType: Type;
private powerMultiplier: number; private powerMultiplier: number;
constructor(weakType: Type, powerMultiplier: number) { constructor(moveType: Type, powerMultiplier: number) {
super(); super();
this.weakType = weakType; this.moveType = moveType;
this.powerMultiplier = powerMultiplier; this.powerMultiplier = powerMultiplier;
} }
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 {
if (move.getMove().type === this.weakType) { if (move.getMove().type === this.moveType) {
(args[0] as Utils.NumberHolder).value *= this.powerMultiplier; (args[0] as Utils.NumberHolder).value *= this.powerMultiplier;
return true; return true;
} }
@ -367,6 +373,50 @@ export class PostSummonAbAttr extends AbAttr {
} }
} }
export class PostSummonAddBattlerTagAbAttr extends PostSummonAbAttr {
private tagType: BattlerTagType;
private turnCount: integer;
constructor(tagType: BattlerTagType, turnCount: integer) {
super(false);
this.tagType = tagType;
this.turnCount = turnCount;
}
applyPostSummon(pokemon: Pokemon, args: any[]): boolean {
return pokemon.addTag(this.tagType, this.turnCount);
}
}
export class PostSummonStatChangeAbAttr extends PostSummonAbAttr {
private stats: BattleStat[];
private levels: integer;
private selfTarget: boolean;
constructor(stats: BattleStat | BattleStat[], levels: integer, selfTarget?: boolean) {
super();
this.stats = typeof(stats) === 'number'
? [ stats as BattleStat ]
: stats as BattleStat[];
this.levels = levels;
this.selfTarget = !!selfTarget;
}
applyPostSummon(pokemon: Pokemon, args: any[]): boolean {
const statChangePhase = new StatChangePhase(pokemon.scene, pokemon.isPlayer() === this.selfTarget, this.selfTarget, this.stats, this.levels);
if (!this.selfTarget && !pokemon.getOpponent()?.summonData)
pokemon.scene.pushPhase(statChangePhase); // TODO: This causes the ability bar to be shown at the wrong time
else
pokemon.scene.unshiftPhase(statChangePhase);
return true;
}
}
export class PostSummonWeatherChangeAbAttr extends PostSummonAbAttr { export class PostSummonWeatherChangeAbAttr extends PostSummonAbAttr {
private weatherType: WeatherType; private weatherType: WeatherType;
@ -390,7 +440,7 @@ export class PreStatChangeAbAttr extends AbAttr {
} }
} }
export class ProtectStatAttr extends PreStatChangeAbAttr { export class ProtectStatAbAttr extends PreStatChangeAbAttr {
private protectedStat: BattleStat; private protectedStat: BattleStat;
constructor(protectedStat?: BattleStat) { constructor(protectedStat?: BattleStat) {
@ -640,7 +690,8 @@ export function applyAbAttrs(attrType: { new(...args: any[]): AbAttr }, pokemon:
continue; continue;
pokemon.scene.setPhaseQueueSplice(); pokemon.scene.setPhaseQueueSplice();
if (attr.apply(pokemon, cancelled, args)) { if (attr.apply(pokemon, cancelled, args)) {
queueShowAbility(pokemon); if (attr.showAbility)
queueShowAbility(pokemon);
const message = attr.getTriggerMessage(pokemon); const message = attr.getTriggerMessage(pokemon);
if (message) if (message)
pokemon.scene.queueMessage(message); pokemon.scene.queueMessage(message);
@ -662,7 +713,8 @@ export function applyPreDefendAbAttrs(attrType: { new(...args: any[]): PreDefend
continue; continue;
pokemon.scene.setPhaseQueueSplice(); pokemon.scene.setPhaseQueueSplice();
if (attr.applyPreDefend(pokemon, attacker, move, cancelled, args)) { if (attr.applyPreDefend(pokemon, attacker, move, cancelled, args)) {
queueShowAbility(pokemon); if (attr.showAbility)
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);
@ -684,7 +736,8 @@ export function applyPostDefendAbAttrs(attrType: { new(...args: any[]): PostDefe
continue; continue;
pokemon.scene.setPhaseQueueSplice(); pokemon.scene.setPhaseQueueSplice();
if (attr.applyPostDefend(pokemon, attacker, move, moveResult, args)) { if (attr.applyPostDefend(pokemon, attacker, move, moveResult, args)) {
queueShowAbility(pokemon); if (attr.showAbility)
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);
@ -708,7 +761,8 @@ export function applyBattleStatMultiplierAbAttrs(attrType: { new(...args: any[])
if (attr.applyBattleStat(pokemon, battleStat, statValue, args)) { if (attr.applyBattleStat(pokemon, battleStat, statValue, args)) {
const message = attr.getTriggerMessage(pokemon); const message = attr.getTriggerMessage(pokemon);
if (message) { if (message) {
queueShowAbility(pokemon); if (attr.showAbility)
queueShowAbility(pokemon);
pokemon.scene.queueMessage(message); pokemon.scene.queueMessage(message);
} }
} }
@ -729,7 +783,8 @@ export function applyPreAttackAbAttrs(attrType: { new(...args: any[]): PreAttack
continue; continue;
pokemon.scene.setPhaseQueueSplice(); pokemon.scene.setPhaseQueueSplice();
if (attr.applyPreAttack(pokemon, defender, move, args)) { if (attr.applyPreAttack(pokemon, defender, move, args)) {
queueShowAbility(pokemon); if (attr.showAbility)
queueShowAbility(pokemon);
const message = attr.getTriggerMessage(pokemon, defender, move); const message = attr.getTriggerMessage(pokemon, defender, move);
if (message) if (message)
pokemon.scene.queueMessage(message); pokemon.scene.queueMessage(message);
@ -751,7 +806,8 @@ export function applyPostSummonAbAttrs(attrType: { new(...args: any[]): PostSumm
continue; continue;
pokemon.scene.setPhaseQueueSplice(); pokemon.scene.setPhaseQueueSplice();
if (attr.applyPostSummon(pokemon, args)) { if (attr.applyPostSummon(pokemon, args)) {
queueShowAbility(pokemon); if (attr.showAbility)
queueShowAbility(pokemon);
const message = attr.getTriggerMessage(pokemon); const message = attr.getTriggerMessage(pokemon);
if (message) if (message)
pokemon.scene.queueMessage(message); pokemon.scene.queueMessage(message);
@ -773,7 +829,8 @@ export function applyPreStatChangeAbAttrs(attrType: { new(...args: any[]): PreSt
continue; continue;
pokemon.scene.setPhaseQueueSplice(); pokemon.scene.setPhaseQueueSplice();
if (attr.applyPreStatChange(pokemon, stat, cancelled, args)) { if (attr.applyPreStatChange(pokemon, stat, cancelled, args)) {
queueShowAbility(pokemon); if (attr.showAbility)
queueShowAbility(pokemon);
const message = attr.getTriggerMessage(pokemon, stat); const message = attr.getTriggerMessage(pokemon, stat);
if (message) if (message)
pokemon.scene.queueMessage(message); pokemon.scene.queueMessage(message);
@ -795,7 +852,8 @@ export function applyPreSetStatusAbAttrs(attrType: { new(...args: any[]): PreSet
continue; continue;
pokemon.scene.setPhaseQueueSplice(); pokemon.scene.setPhaseQueueSplice();
if (attr.applyPreSetStatus(pokemon, effect, cancelled, args)) { if (attr.applyPreSetStatus(pokemon, effect, cancelled, args)) {
queueShowAbility(pokemon); if (attr.showAbility)
queueShowAbility(pokemon);
const message = attr.getTriggerMessage(pokemon, effect); const message = attr.getTriggerMessage(pokemon, effect);
if (message) if (message)
pokemon.scene.queueMessage(message); pokemon.scene.queueMessage(message);
@ -817,7 +875,8 @@ export function applyPreApplyBattlerTagAbAttrs(attrType: { new(...args: any[]):
continue; continue;
pokemon.scene.setPhaseQueueSplice(); pokemon.scene.setPhaseQueueSplice();
if (attr.applyPreApplyBattlerTag(pokemon, tag, cancelled, args)) { if (attr.applyPreApplyBattlerTag(pokemon, tag, cancelled, args)) {
queueShowAbility(pokemon); if (attr.showAbility)
queueShowAbility(pokemon);
const message = attr.getTriggerMessage(pokemon, tag); const message = attr.getTriggerMessage(pokemon, tag);
if (message) if (message)
pokemon.scene.queueMessage(message); pokemon.scene.queueMessage(message);
@ -862,7 +921,8 @@ export function applyPostTurnAbAttrs(attrType: { new(...args: any[]): PostTurnAb
continue; continue;
pokemon.scene.setPhaseQueueSplice(); pokemon.scene.setPhaseQueueSplice();
if (attr.applyPostTurn(pokemon, args)) { if (attr.applyPostTurn(pokemon, args)) {
queueShowAbility(pokemon); if (attr.showAbility)
queueShowAbility(pokemon);
const message = attr.getTriggerMessage(pokemon); const message = attr.getTriggerMessage(pokemon);
if (message) if (message)
pokemon.scene.queueMessage(message); pokemon.scene.queueMessage(message);
@ -888,7 +948,8 @@ export function applyPostWeatherLapseAbAttrs(attrType: { new(...args: any[]): Po
continue; continue;
pokemon.scene.setPhaseQueueSplice(); pokemon.scene.setPhaseQueueSplice();
if (attr.applyPostWeatherLapse(pokemon, weather, args)) { if (attr.applyPostWeatherLapse(pokemon, weather, args)) {
queueShowAbility(pokemon); if (attr.showAbility)
queueShowAbility(pokemon);
const message = attr.getTriggerMessage(pokemon, weather); const message = attr.getTriggerMessage(pokemon, weather);
if (message) if (message)
pokemon.scene.queueMessage(message); pokemon.scene.queueMessage(message);
@ -1120,7 +1181,7 @@ export function initAbilities() {
.attr(BattleStatMultiplierAbAttr, BattleStat.SPD, 2) .attr(BattleStatMultiplierAbAttr, BattleStat.SPD, 2)
.condition(getWeatherCondition(WeatherType.SUNNY, WeatherType.HARSH_SUN)), // TODO: Show ability bar on weather change and summon .condition(getWeatherCondition(WeatherType.SUNNY, WeatherType.HARSH_SUN)), // TODO: Show ability bar on weather change and summon
new Ability(Abilities.CLEAR_BODY, "Clear Body", "Prevents other POKéMON from lowering its stats.", 3) new Ability(Abilities.CLEAR_BODY, "Clear Body", "Prevents other POKéMON from lowering its stats.", 3)
.attr(ProtectStatAttr), .attr(ProtectStatAbAttr),
new Ability(Abilities.CLOUD_NINE, "Cloud Nine", "Eliminates the effects of non-severe weather.", 3) new Ability(Abilities.CLOUD_NINE, "Cloud Nine", "Eliminates the effects of non-severe weather.", 3)
.attr(SuppressWeatherEffectAbAttr), .attr(SuppressWeatherEffectAbAttr),
new Ability(Abilities.COLOR_CHANGE, "Color Change", "Changes the POKéMON's type to the foe's move.", 3) new Ability(Abilities.COLOR_CHANGE, "Color Change", "Changes the POKéMON's type to the foe's move.", 3)
@ -1137,25 +1198,29 @@ export function initAbilities() {
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", "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), .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", "Contact with the POKéMON may burn the attacker.", 3)
.attr(PostDefendContactApplyStatusEffectAbAttr, 30, StatusEffect.BURN),
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),
new Ability(Abilities.FORECAST, "Forecast (N)", "Castform transforms with the weather.", 3), new Ability(Abilities.FORECAST, "Forecast (N)", "Castform transforms with the weather.", 3),
new Ability(Abilities.GUTS, "Guts (N)", "Boosts ATTACK if there is a status problem.", 3), new Ability(Abilities.GUTS, "Guts (N)", "Boosts ATTACK if there is a status problem.", 3),
new Ability(Abilities.HUGE_POWER, "Huge Power (N)", "Raises the POKéMON's ATTACK stat.", 3), new Ability(Abilities.HUGE_POWER, "Huge Power", "Raises the POKéMON's ATTACK stat.", 3)
.attr(PostSummonStatChangeAbAttr, BattleStat.ATK, 1, true),
new Ability(Abilities.HUSTLE, "Hustle (N)", "Boosts the ATTACK stat, but lowers accuracy.", 3), new Ability(Abilities.HUSTLE, "Hustle (N)", "Boosts the ATTACK stat, but lowers accuracy.", 3),
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(ProtectStatAbAttr, 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", "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), .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", "The POKéMON is protected from flinching.", 3)
.attr(BattlerTagImmunityAbAttr, BattlerTagType.FLINCHED),
new Ability(Abilities.INSOMNIA, "Insomnia", "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(StatusEffectImmunityAbAttr, StatusEffect.SLEEP)
.attr(BattlerTagImmunityAbAttr, BattlerTagType.DROWSY), .attr(BattlerTagImmunityAbAttr, BattlerTagType.DROWSY),
new Ability(Abilities.INTIMIDATE, "Intimidate (N)", "Lowers the foe's ATTACK stat.", 3), new Ability(Abilities.INTIMIDATE, "Intimidate", "Lowers the foe's ATTACK stat.", 3)
.attr(PostSummonStatChangeAbAttr, BattleStat.ATK, -1),
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(ProtectStatAbAttr, BattleStat.ACC),
new Ability(Abilities.LEVITATE, "Levitate", "Gives immunity to GROUND-type moves.", 3) new Ability(Abilities.LEVITATE, "Levitate", "Gives immunity to GROUND-type moves.", 3)
.attr(TypeImmunityAbAttr, Type.GROUND, (pokemon: Pokemon) => !pokemon.getTag(BattlerTagType.IGNORE_FLYING)), .attr(TypeImmunityAbAttr, Type.GROUND, (pokemon: Pokemon) => !pokemon.getTag(BattlerTagType.IGNORE_FLYING)),
new Ability(Abilities.LIGHTNING_ROD, "Lightning Rod", "Draws in all ELECTRIC-type moves to up SP. ATK.", 3) new Ability(Abilities.LIGHTNING_ROD, "Lightning Rod", "Draws in all ELECTRIC-type moves to up SP. ATK.", 3)
@ -1182,7 +1247,8 @@ export function initAbilities() {
new Ability(Abilities.POISON_POINT, "Poison Point", "Contact with the POKéMON may poison the attacker.", 3) new Ability(Abilities.POISON_POINT, "Poison Point", "Contact with the POKéMON may poison the attacker.", 3)
.attr(PostDefendContactApplyStatusEffectAbAttr, StatusEffect.POISON), .attr(PostDefendContactApplyStatusEffectAbAttr, StatusEffect.POISON),
new Ability(Abilities.PRESSURE, "Pressure (N)", "The POKéMON raises the foe's PP usage.", 3), new Ability(Abilities.PRESSURE, "Pressure (N)", "The POKéMON raises the foe's PP usage.", 3),
new Ability(Abilities.PURE_POWER, "Pure Power (N)", "Raises the POKéMON's ATTACK stat.", 3), new Ability(Abilities.PURE_POWER, "Pure Power", "Raises the POKéMON's ATTACK stat.", 3)
.attr(PostSummonStatChangeAbAttr, BattleStat.ATK, 1, true),
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.ROCK_HEAD, "Rock Head", "Protects the POKéMON from recoil damage.", 3) new Ability(Abilities.ROCK_HEAD, "Rock Head", "Protects the POKéMON from recoil damage.", 3)
@ -1218,11 +1284,14 @@ export function initAbilities() {
.attr(BattleStatMultiplierAbAttr, BattleStat.SPD, 2) .attr(BattleStatMultiplierAbAttr, BattleStat.SPD, 2)
.condition(getWeatherCondition(WeatherType.RAIN, WeatherType.HEAVY_RAIN)), // TODO: Show ability bar on weather change and summon .condition(getWeatherCondition(WeatherType.RAIN, WeatherType.HEAVY_RAIN)), // TODO: Show ability bar on weather change and summon
new Ability(Abilities.SYNCHRONIZE, "Synchronize (N)", "Passes a burn, poison, or paralysis to the foe.", 3), new Ability(Abilities.SYNCHRONIZE, "Synchronize (N)", "Passes a burn, poison, or paralysis to the foe.", 3),
new Ability(Abilities.THICK_FAT, "Thick Fat (N)", "Ups resistance to Fire- and ICE-type moves.", 3), new Ability(Abilities.THICK_FAT, "Thick Fat", "Ups resistance to Fire- and ICE-type moves.", 3)
.attr(ReceivedTypeDamageMultiplierAbAttr, Type.FIRE, 0.5)
.attr(ReceivedTypeDamageMultiplierAbAttr, Type.ICE, 0.5),
new Ability(Abilities.TORRENT, "Torrent", "Powers up WATER-type moves in a pinch.", 3) new Ability(Abilities.TORRENT, "Torrent", "Powers up WATER-type moves in a pinch.", 3)
.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", "POKéMON can't attack on consecutive turns.", 3)
.attr(PostSummonAddBattlerTagAbAttr, BattlerTagType.TRUANT, 1),
new Ability(Abilities.VITAL_SPIRIT, "Vital Spirit", "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(StatusEffectImmunityAbAttr, StatusEffect.SLEEP)
.attr(BattlerTagImmunityAbAttr, BattlerTagType.DROWSY), .attr(BattlerTagImmunityAbAttr, BattlerTagType.DROWSY),
@ -1233,7 +1302,7 @@ export function initAbilities() {
new Ability(Abilities.WATER_VEIL, "Water Veil", "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), .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(ProtectStatAbAttr),
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)
.attr(NonSuperEffectiveImmunityAbAttr), .attr(NonSuperEffectiveImmunityAbAttr),
new Ability(Abilities.ADAPTABILITY, "Adaptability (N)", "Powers up moves of the same type.", 4), new Ability(Abilities.ADAPTABILITY, "Adaptability (N)", "Powers up moves of the same type.", 4),
@ -1242,17 +1311,18 @@ export function initAbilities() {
new Ability(Abilities.ANTICIPATION, "Anticipation (N)", "Senses a foe's dangerous moves.", 4), new Ability(Abilities.ANTICIPATION, "Anticipation (N)", "Senses a foe's dangerous moves.", 4),
new Ability(Abilities.BAD_DREAMS, "Bad Dreams (N)", "Reduces a sleeping foe's HP.", 4), new Ability(Abilities.BAD_DREAMS, "Bad Dreams (N)", "Reduces a sleeping foe's HP.", 4),
new Ability(Abilities.DOWNLOAD, "Download (N)", "Adjusts power according to a foe's defenses.", 4), new Ability(Abilities.DOWNLOAD, "Download (N)", "Adjusts power according to a foe's defenses.", 4),
new Ability(Abilities.DRY_SKIN, "Dry Skin", "Reduces HP if it is hot. Water restores HP.", 4) // TODO new Ability(Abilities.DRY_SKIN, "Dry Skin", "Reduces HP if it is hot. Water restores HP.", 4)
.attr(PostWeatherLapseDamageAbAttr, 2, WeatherType.SUNNY, WeatherType.HARSH_SUN) .attr(PostWeatherLapseDamageAbAttr, 2, WeatherType.SUNNY, WeatherType.HARSH_SUN)
.attr(PostWeatherLapseHealAbAttr, 2, WeatherType.RAIN, WeatherType.HEAVY_RAIN) .attr(PostWeatherLapseHealAbAttr, 2, WeatherType.RAIN, WeatherType.HEAVY_RAIN)
.attr(TypeWeaknessAbAttr, Type.FIRE, 1.25) .attr(ReceivedTypeDamageMultiplierAbAttr, Type.FIRE, 1.25)
.attr(TypeImmunityHealAbAttr, Type.WATER), .attr(TypeImmunityHealAbAttr, Type.WATER),
new Ability(Abilities.FILTER, "Filter (N)", "Reduces damage from super-effective attacks.", 4), new Ability(Abilities.FILTER, "Filter (N)", "Reduces damage from super-effective attacks.", 4),
new Ability(Abilities.FLOWER_GIFT, "Flower Gift (N)", "Powers up party POKéMON when it is sunny.", 4), new Ability(Abilities.FLOWER_GIFT, "Flower Gift (N)", "Powers up party POKéMON when it is sunny.", 4),
new Ability(Abilities.FOREWARN, "Forewarn (N)", "Determines what moves a foe has.", 4), new Ability(Abilities.FOREWARN, "Forewarn (N)", "Determines what moves a foe has.", 4),
new Ability(Abilities.FRISK, "Frisk (N)", "The POKéMON can check a foe's held item.", 4), new Ability(Abilities.FRISK, "Frisk (N)", "The POKéMON can check a foe's held item.", 4),
new Ability(Abilities.GLUTTONY, "Gluttony (N)", "Encourages the early use of a held Berry.", 4), new Ability(Abilities.GLUTTONY, "Gluttony (N)", "Encourages the early use of a held Berry.", 4),
new Ability(Abilities.HEATPROOF, "Heatproof (N)", "Weakens the power of FIRE-type moves.", 4), new Ability(Abilities.HEATPROOF, "Heatproof", "Weakens the power of FIRE-type moves.", 4)
.attr(ReceivedTypeDamageMultiplierAbAttr, Type.FIRE, 0.5),
new Ability(Abilities.HONEY_GATHER, "Honey Gather (N)", "The POKéMON may gather Honey from somewhere.", 4), new Ability(Abilities.HONEY_GATHER, "Honey Gather (N)", "The POKéMON may gather Honey from somewhere.", 4),
new Ability(Abilities.HYDRATION, "Hydration (N)", "Heals status problems if it is raining.", 4), new Ability(Abilities.HYDRATION, "Hydration (N)", "Heals status problems if it is raining.", 4),
new Ability(Abilities.ICE_BODY, "Ice Body", "The POKéMON gradually regains HP in a hailstorm.", 4) new Ability(Abilities.ICE_BODY, "Ice Body", "The POKéMON gradually regains HP in a hailstorm.", 4)
@ -1298,7 +1368,7 @@ export function initAbilities() {
new Ability(Abilities.UNBURDEN, "Unburden (N)", "Raises SPEED if a held item is used.", 4), new Ability(Abilities.UNBURDEN, "Unburden (N)", "Raises SPEED if a held item is used.", 4),
new Ability(Abilities.ANALYTIC, "Analytic (N)", "Boosts move power when the POKéMON moves last.", 5), new Ability(Abilities.ANALYTIC, "Analytic (N)", "Boosts move power when the POKéMON moves last.", 5),
new Ability(Abilities.BIG_PECKS, "Big Pecks", "Protects the POKéMON from DEFENSE-lowering attacks.", 5) new Ability(Abilities.BIG_PECKS, "Big Pecks", "Protects the POKéMON from DEFENSE-lowering attacks.", 5)
.attr(ProtectStatAttr, BattleStat.DEF), .attr(ProtectStatAbAttr, BattleStat.DEF),
new Ability(Abilities.CONTRARY, "Contrary (N)", "Makes stat changes have an opposite effect.", 5), new Ability(Abilities.CONTRARY, "Contrary (N)", "Makes stat changes have an opposite effect.", 5),
new Ability(Abilities.CURSED_BODY, "Cursed Body (N)", "May disable a move used on the POKéMON.", 5), new Ability(Abilities.CURSED_BODY, "Cursed Body (N)", "May disable a move used on the POKéMON.", 5),
new Ability(Abilities.DEFEATIST, "Defeatist (N)", "Lowers stats when HP drops below half.", 5), new Ability(Abilities.DEFEATIST, "Defeatist (N)", "Lowers stats when HP drops below half.", 5),
@ -1322,7 +1392,8 @@ export function initAbilities() {
new Ability(Abilities.OVERCOAT, "Overcoat", "Protects the POKéMON from weather damage.", 5) new Ability(Abilities.OVERCOAT, "Overcoat", "Protects the POKéMON from weather damage.", 5)
.attr(BlockWeatherDamageAttr), .attr(BlockWeatherDamageAttr),
new Ability(Abilities.PICKPOCKET, "Pickpocket (N)", "Once per battle, steals an item when hit by another POKéMON.", 5), new Ability(Abilities.PICKPOCKET, "Pickpocket (N)", "Once per battle, steals an item when hit by another POKéMON.", 5),
new Ability(Abilities.POISON_TOUCH, "Poison Touch (N)", "May poison targets when a POKéMON makes contact.", 5), new Ability(Abilities.POISON_TOUCH, "Poison Touch", "May poison targets when a POKéMON makes contact.", 5)
.attr(PostDefendContactApplyStatusEffectAbAttr, 30, StatusEffect.POISON),
new Ability(Abilities.PRANKSTER, "Prankster (N)", "Gives priority to a status move.", 5), new Ability(Abilities.PRANKSTER, "Prankster (N)", "Gives priority to a status move.", 5),
new Ability(Abilities.RATTLED, "Rattled (N)", "BUG, GHOST or DARK type moves scare it and boost its SPEED.", 5), new Ability(Abilities.RATTLED, "Rattled (N)", "BUG, GHOST or DARK type moves scare it and boost its SPEED.", 5),
new Ability(Abilities.REGENERATOR, "Regenerator (N)", "Restores a little HP when withdrawn from battle.", 5), new Ability(Abilities.REGENERATOR, "Regenerator (N)", "Restores a little HP when withdrawn from battle.", 5),

View File

@ -1,13 +1,13 @@
import { CommonAnim, CommonBattleAnim } from "./battle-anims"; import { CommonAnim, CommonBattleAnim } from "./battle-anims";
import { CommonAnimPhase, DamagePhase, MovePhase, ObtainStatusEffectPhase, PokemonHealPhase } from "../battle-phases"; import { CommonAnimPhase, DamagePhase, MovePhase, ObtainStatusEffectPhase, PokemonHealPhase, ShowAbilityPhase } from "../battle-phases";
import { getPokemonMessage } from "../messages"; import { getPokemonMessage } from "../messages";
import Pokemon from "../pokemon"; import Pokemon, { MoveResult } from "../pokemon";
import { Stat } from "./pokemon-stat"; import { Stat } from "./pokemon-stat";
import { StatusEffect } from "./status-effect"; 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"; import { Abilities } from "./ability";
export enum BattlerTagType { export enum BattlerTagType {
NONE, NONE,
@ -30,6 +30,7 @@ export enum BattlerTagType {
SAND_TOMB, SAND_TOMB,
MAGMA_STORM, MAGMA_STORM,
PROTECTED, PROTECTED,
TRUANT,
FLYING, FLYING,
UNDERGROUND, UNDERGROUND,
HIDDEN, HIDDEN,
@ -527,6 +528,27 @@ export class ProtectedTag extends BattlerTag {
} }
} }
export class TruantTag extends BattlerTag {
constructor() {
super(BattlerTagType.TRUANT, BattlerTagLapseType.MOVE, 1, undefined);
}
lapse(pokemon: Pokemon, lapseType: BattlerTagLapseType): boolean {
if (pokemon.getAbility().id !== Abilities.TRUANT)
return super.lapse(pokemon, lapseType);
const lastMove = pokemon.getLastXMoves().find(() => true);
if (lastMove && lastMove.move !== Moves.NONE) {
(pokemon.scene.getCurrentPhase() as MovePhase).cancel();
pokemon.scene.unshiftPhase(new ShowAbilityPhase(pokemon.scene, pokemon.isPlayer()));
pokemon.scene.queueMessage(getPokemonMessage(pokemon, ' is\nloafing around!'));
}
return true;
}
}
export class HideSpriteTag extends BattlerTag { export class HideSpriteTag extends BattlerTag {
constructor(tagType: BattlerTagType, turnCount: integer, sourceMove: Moves) { constructor(tagType: BattlerTagType, turnCount: integer, sourceMove: Moves) {
super(tagType, BattlerTagLapseType.MOVE_EFFECT, turnCount, sourceMove); super(tagType, BattlerTagLapseType.MOVE_EFFECT, turnCount, sourceMove);
@ -628,6 +650,8 @@ export function getBattlerTag(tagType: BattlerTagType, turnCount: integer, sourc
return new MagmaStormTag(turnCount, sourceId); return new MagmaStormTag(turnCount, sourceId);
case BattlerTagType.PROTECTED: case BattlerTagType.PROTECTED:
return new ProtectedTag(sourceMove); return new ProtectedTag(sourceMove);
case BattlerTagType.TRUANT:
return new TruantTag();
case BattlerTagType.FLYING: case BattlerTagType.FLYING:
case BattlerTagType.UNDERGROUND: case BattlerTagType.UNDERGROUND:
case BattlerTagType.HIDDEN: case BattlerTagType.HIDDEN: