From 2ac0a3645fc420c36b599638e593836e95c85f28 Mon Sep 17 00:00:00 2001 From: Flashfyre Date: Sat, 22 Apr 2023 22:14:53 -0400 Subject: [PATCH] Add switch out preventing to traps --- src/arena.ts | 2 +- src/battle-phases.ts | 19 +++- src/battle-scene.ts | 5 + src/data/battle-anims.ts | 2 +- src/data/battler-tag.ts | 194 +++++++++++++++++++--------------- src/data/move.ts | 12 ++- src/modifier/modifier-type.ts | 4 +- src/modifier/modifier.ts | 5 +- src/pokemon.ts | 36 +++++-- src/ui/party-ui-handler.ts | 33 +++--- 10 files changed, 190 insertions(+), 122 deletions(-) diff --git a/src/arena.ts b/src/arena.ts index 4f854fc57..b16986601 100644 --- a/src/arena.ts +++ b/src/arena.ts @@ -5,7 +5,7 @@ import * as Utils from "./utils"; import PokemonSpecies, { getPokemonSpecies } from "./data/pokemon-species"; import { Species } from "./data/species"; import { Weather, WeatherType, getWeatherClearMessage, getWeatherStartMessage } from "./data/weather"; -import { CommonAnimPhase, MessagePhase } from "./battle-phases"; +import { CommonAnimPhase } from "./battle-phases"; import { CommonAnim } from "./data/battle-anims"; import { Type } from "./data/type"; import Move from "./data/move"; diff --git a/src/battle-phases.ts b/src/battle-phases.ts index 9dc84f8ed..2fe2113b6 100644 --- a/src/battle-phases.ts +++ b/src/battle-phases.ts @@ -18,7 +18,7 @@ import { BattleStat, getBattleStatLevelChangeDescription, getBattleStatName } fr import { Biome, biomeLinks } from "./data/biome"; import { ModifierTypeOption, PokemonModifierType, PokemonMoveModifierType, getPlayerModifierTypeOptionsForWave, regenerateModifierPoolThresholds } from "./modifier/modifier-type"; import SoundFade from "phaser3-rex-plugins/plugins/soundfade"; -import { BattlerTagLapseType, BattlerTagType, HideSpriteTag as HiddenTag } from "./data/battler-tag"; +import { BattlerTagLapseType, BattlerTagType, HideSpriteTag as HiddenTag, DamagingTrapTag, TrappedTag as TrapTag } from "./data/battler-tag"; import { getPokemonMessage } from "./messages"; import { Starter } from "./ui/starter-select-ui-handler"; import { Gender } from "./data/gender"; @@ -370,6 +370,8 @@ export class SwitchSummonPhase extends SummonPhase { const playerPokemon = this.scene.getPlayerPokemon(); + this.scene.getEnemyPokemon()?.removeTagsBySourceId(playerPokemon.id); + this.scene.ui.showText(`Come back, ${this.scene.getPlayerPokemon().name}!`); this.scene.sound.play('pb_rel'); playerPokemon.hideInfo(); @@ -542,8 +544,14 @@ export class CommandPhase extends BattlePhase { } break; case Command.POKEMON: - this.scene.unshiftPhase(new SwitchSummonPhase(this.scene, cursor, true)); - success = true; + const trapTag = playerPokemon.findTag(t => t instanceof TrapTag) as TrapTag; + if (!trapTag) { + this.scene.unshiftPhase(new SwitchSummonPhase(this.scene, cursor, true)); + success = true; + } else + this.scene.ui.showText(`${this.scene.getPokemonById(trapTag.sourceId).name}'s ${trapTag.getMoveName()}\nprevents switching!`, null, () => { + this.scene.ui.showText(null, 0); + }, null, true); break; case Command.RUN: //this.scene.unshiftPhase(new MoveAnimTestPhase(this.scene, [ Moves.TELEPORT ])); @@ -1299,6 +1307,10 @@ export class FaintPhase extends PokemonPhase { const pokemon = this.getPokemon(); pokemon.lapseTags(BattlerTagLapseType.FAINT); + if (pokemon.isPlayer()) + this.scene.getEnemyPokemon()?.removeTagsBySourceId(pokemon.id); + else + this.scene.getPlayerPokemon()?.removeTagsBySourceId(pokemon.id); pokemon.faintCry(() => { pokemon.hideInfo(); @@ -1793,6 +1805,7 @@ export class AttemptCapturePhase extends BattlePhase { const newPokemon = pokemon.addToParty(); const modifiers = this.scene.findModifiers(m => m instanceof PokemonHeldItemModifier, false); Promise.all(modifiers.map(m => this.scene.addModifier(m))).then(() => { + this.scene.getPlayerPokemon().removeTagsBySourceId(pokemon.id); pokemon.hp = 0; this.scene.clearEnemyModifiers(); this.scene.field.remove(pokemon, true); diff --git a/src/battle-scene.ts b/src/battle-scene.ts index 833d53ae2..78c12e27d 100644 --- a/src/battle-scene.ts +++ b/src/battle-scene.ts @@ -433,6 +433,11 @@ export default class BattleScene extends Phaser.Scene { return this.currentBattle?.enemyPokemon; } + getPokemonById(pokemonId: integer): Pokemon { + const findInParty = (party: Pokemon[]) => party.find(p => p.id === pokemonId); + return findInParty(this.getParty()) || findInParty(this.getEnemyParty()); + } + reset(): void { this.pokeballCounts = Object.fromEntries(Utils.getEnumValues(PokeballType).filter(p => p <= PokeballType.MASTER_BALL).map(t => [ t, 0 ])); diff --git a/src/data/battle-anims.ts b/src/data/battle-anims.ts index 90f5a2d26..9b4300984 100644 --- a/src/data/battle-anims.ts +++ b/src/data/battle-anims.ts @@ -619,7 +619,7 @@ export abstract class BattleAnim { const setSpritePriority = (priority: integer) => { switch (priority) { case 0: - scene.field.moveBelow(moveSprite, scene.getEnemyPokemon()); + scene.field.moveBelow(moveSprite, scene.getEnemyPokemon() || scene.getPlayerPokemon()); break; case 1: scene.field.moveTo(moveSprite, scene.field.getAll().length - 1); diff --git a/src/data/battler-tag.ts b/src/data/battler-tag.ts index 1b6be9bcf..5dc4abf82 100644 --- a/src/data/battler-tag.ts +++ b/src/data/battler-tag.ts @@ -5,7 +5,8 @@ import Pokemon from "../pokemon"; import { Stat } from "./pokemon-stat"; import { StatusEffect } from "./status-effect"; import * as Utils from "../utils"; -import { Moves } from "./move"; +import { Moves, allMoves } from "./move"; +import { Type } from "./type"; export enum BattlerTagType { NONE, @@ -18,6 +19,7 @@ export enum BattlerTagType { INGRAIN, AQUA_RING, DROWSY, + TRAPPED, BIND, WRAP, FIRE_SPIN, @@ -46,11 +48,19 @@ export class BattlerTag { public tagType: BattlerTagType; public lapseType: BattlerTagLapseType; public turnCount: integer; + public sourceId: integer; + public sourceMove: Moves; - constructor(tagType: BattlerTagType, lapseType: BattlerTagLapseType, turnCount: integer) { + constructor(tagType: BattlerTagType, lapseType: BattlerTagLapseType, turnCount: integer, sourceId?: integer, sourceMove?: Moves) { this.tagType = tagType; this.lapseType = lapseType; this.turnCount = turnCount; + this.sourceId = sourceId; + this.sourceMove = sourceMove; + } + + canAdd(pokemon: Pokemon): boolean { + return true; } onAdd(pokemon: Pokemon): void { } @@ -62,6 +72,12 @@ export class BattlerTag { lapse(pokemon: Pokemon, lapseType: BattlerTagLapseType): boolean { return --this.turnCount > 0; } + + getMoveName(): string { + return this.sourceMove + ? allMoves[this.sourceMove].name + : null; + } } export class RechargingTag extends BattlerTag { @@ -85,6 +101,32 @@ export class RechargingTag extends BattlerTag { } } +export class TrappedTag extends BattlerTag { + constructor(tagType: BattlerTagType, lapseType: BattlerTagLapseType, turnCount: integer, sourceId: integer, sourceMove: Moves) { + super(tagType, lapseType, turnCount, sourceId, sourceMove); + } + + canAdd(pokemon: Pokemon): boolean { + return !pokemon.isOfType(Type.GHOST) && !pokemon.getTag(BattlerTagType.TRAPPED); + } + + onAdd(pokemon: Pokemon): void { + super.onAdd(pokemon); + + pokemon.scene.queueMessage(this.getTrapMessage(pokemon)); + } + + onRemove(pokemon: Pokemon): void { + super.onRemove(pokemon); + + pokemon.scene.queueMessage(getPokemonMessage(pokemon, ` was freed\nfrom ${this.getMoveName()}!`)); + } + + getTrapMessage(pokemon: Pokemon): string { + return getPokemonMessage(pokemon, ' can no\nlonger escape!'); + } +} + export class FlinchedTag extends BattlerTag { constructor() { super(BattlerTagType.FLINCHED, BattlerTagLapseType.MOVE, 0); @@ -100,13 +142,7 @@ export class FlinchedTag extends BattlerTag { } } -export class PseudoStatusTag extends BattlerTag { - constructor(tagType: BattlerTagType, lapseType: BattlerTagLapseType, turnCount: integer) { - super(tagType, lapseType, turnCount); - } -} - -export class ConfusedTag extends PseudoStatusTag { +export class ConfusedTag extends BattlerTag { constructor(turnCount: integer) { super(BattlerTagType.CONFUSED, BattlerTagLapseType.MOVE, turnCount); } @@ -152,7 +188,7 @@ export class ConfusedTag extends PseudoStatusTag { } } -export class SeedTag extends PseudoStatusTag { +export class SeedTag extends BattlerTag { constructor() { super(BattlerTagType.SEEDED, BattlerTagLapseType.AFTER_MOVE, 1); } @@ -179,7 +215,7 @@ export class SeedTag extends PseudoStatusTag { } } -export class NightmareTag extends PseudoStatusTag { +export class NightmareTag extends BattlerTag { constructor() { super(BattlerTagType.NIGHTMARE, BattlerTagLapseType.AFTER_MOVE, 1); } @@ -212,15 +248,9 @@ export class NightmareTag extends PseudoStatusTag { } } -export class IngrainTag extends PseudoStatusTag { - constructor() { - super(BattlerTagType.INGRAIN, BattlerTagLapseType.TURN_END, 1); - } - - onAdd(pokemon: Pokemon): void { - super.onAdd(pokemon); - - pokemon.scene.queueMessage(getPokemonMessage(pokemon, ' planted its roots!')); +export class IngrainTag extends TrappedTag { + constructor(sourceId: integer) { + super(BattlerTagType.INGRAIN, BattlerTagLapseType.TURN_END, 1, sourceId, Moves.INGRAIN); } lapse(pokemon: Pokemon, lapseType: BattlerTagLapseType): boolean { @@ -232,11 +262,15 @@ export class IngrainTag extends PseudoStatusTag { return ret; } + + getTrapMessage(pokemon: Pokemon): string { + return getPokemonMessage(pokemon, ' planted its roots!'); + } } -export class AquaRingTag extends PseudoStatusTag { +export class AquaRingTag extends BattlerTag { constructor() { - super(BattlerTagType.AQUA_RING, BattlerTagLapseType.TURN_END, 1); + super(BattlerTagType.AQUA_RING, BattlerTagLapseType.TURN_END, 1, undefined, Moves.AQUA_RING); } onAdd(pokemon: Pokemon): void { @@ -249,7 +283,7 @@ export class AquaRingTag extends PseudoStatusTag { const ret = lapseType !== BattlerTagLapseType.CUSTOM || super.lapse(pokemon, lapseType); if (ret) - pokemon.scene.unshiftPhase(new PokemonHealPhase(pokemon.scene, pokemon.isPlayer(), Math.floor(pokemon.getMaxHp() / 16), `AQUA RING restored\n${pokemon.name}\'s HP!`, true)); + pokemon.scene.unshiftPhase(new PokemonHealPhase(pokemon.scene, pokemon.isPlayer(), Math.floor(pokemon.getMaxHp() / 16), `${this.getMoveName()} restored\n${pokemon.name}\'s HP!`, true)); return ret; } @@ -276,30 +310,24 @@ export class DrowsyTag extends BattlerTag { } } -export abstract class TrapTag extends BattlerTag { +export abstract class DamagingTrapTag extends TrappedTag { private commonAnim: CommonAnim; - constructor(tagType: BattlerTagType, commonAnim: CommonAnim, turnCount: integer) { - super(tagType, BattlerTagLapseType.TURN_END, turnCount); + constructor(tagType: BattlerTagType, commonAnim: CommonAnim, turnCount: integer, sourceId: integer, sourceMove: Moves) { + super(tagType, BattlerTagLapseType.TURN_END, turnCount, sourceId, sourceMove); this.commonAnim = commonAnim; } - getTrapName(): string { - return BattlerTagType[this.tagType].toUpperCase().replace(/\_/g, ' '); - } - - onAdd(pokemon: Pokemon): void { - super.onAdd(pokemon); - - pokemon.scene.queueMessage(this.getTrapMessage(pokemon)); + canAdd(pokemon: Pokemon): boolean { + return !pokemon.isOfType(Type.GHOST) && !pokemon.findTag(t => t instanceof DamagingTrapTag); } lapse(pokemon: Pokemon, lapseType: BattlerTagLapseType): boolean { - const ret = lapseType !== BattlerTagLapseType.CUSTOM && super.lapse(pokemon, lapseType); + const ret = super.lapse(pokemon, lapseType); if (ret) { - pokemon.scene.queueMessage(getPokemonMessage(pokemon, ` is hurt\nby ${this.getTrapName()}!`)); + pokemon.scene.queueMessage(getPokemonMessage(pokemon, ` is hurt\nby ${this.getMoveName()}!`)); pokemon.scene.unshiftPhase(new CommonAnimPhase(pokemon.scene, pokemon.isPlayer(), this.commonAnim)); const damage = Math.ceil(pokemon.getMaxHp() / 16); @@ -309,39 +337,31 @@ export abstract class TrapTag extends BattlerTag { return ret; } - - onRemove(pokemon: Pokemon): void { - super.onRemove(pokemon); - - pokemon.scene.queueMessage(getPokemonMessage(pokemon, ` was freed\nfrom ${this.getTrapName()}!`)); - } - - abstract getTrapMessage(pokemon: Pokemon): string; } -export class BindTag extends TrapTag { - constructor(turnCount: integer) { - super(BattlerTagType.BIND, CommonAnim.BIND, turnCount); +export class BindTag extends DamagingTrapTag { + constructor(turnCount: integer, sourceId: integer) { + super(BattlerTagType.BIND, CommonAnim.BIND, turnCount, sourceId, Moves.BIND); } getTrapMessage(pokemon: Pokemon): string { - return getPokemonMessage(pokemon, ` was squeezed\nby ${this.getTrapName}!`); + return getPokemonMessage(pokemon, ` was squeezed by\n${pokemon.scene.getPokemonById(this.sourceId)}'s ${this.getMoveName()}!`); } } -export class WrapTag extends TrapTag { - constructor(turnCount: integer) { - super(BattlerTagType.WRAP, CommonAnim.WRAP, turnCount); +export class WrapTag extends DamagingTrapTag { + constructor(turnCount: integer, sourceId: integer) { + super(BattlerTagType.WRAP, CommonAnim.WRAP, turnCount, sourceId, Moves.WRAP); } getTrapMessage(pokemon: Pokemon): string { - return getPokemonMessage(pokemon, ' was WRAPPED!'); + return getPokemonMessage(pokemon, ` was WRAPPED\nby ${pokemon.scene.getPokemonById(this.sourceId)}!`); } } -export class FireSpinTag extends TrapTag { - constructor(turnCount: integer) { - super(BattlerTagType.FIRE_SPIN, CommonAnim.FIRE_SPIN, turnCount); +export abstract class VortexTrapTag extends DamagingTrapTag { + constructor(tagType: BattlerTagType, commonAnim: CommonAnim, turnCount: integer, sourceId: integer, sourceMove: Moves) { + super(tagType, commonAnim, turnCount, sourceId, sourceMove); } getTrapMessage(pokemon: Pokemon): string { @@ -349,39 +369,41 @@ export class FireSpinTag extends TrapTag { } } -export class WhirlpoolTag extends TrapTag { - constructor(turnCount: integer) { - super(BattlerTagType.WHIRLPOOL, CommonAnim.WHIRLPOOL, turnCount); - } - - getTrapMessage(pokemon: Pokemon): string { - return getPokemonMessage(pokemon, ' was trapped\nin the vortex!'); +export class FireSpinTag extends VortexTrapTag { + constructor(turnCount: integer, sourceId: integer) { + super(BattlerTagType.FIRE_SPIN, CommonAnim.FIRE_SPIN, turnCount, sourceId, Moves.FIRE_SPIN); } } -export class ClampTag extends TrapTag { - constructor(turnCount: integer) { - super(BattlerTagType.CLAMP, CommonAnim.CLAMP, turnCount); - } - - getTrapMessage(pokemon: Pokemon): string { - return getPokemonMessage(pokemon, ' was CLAMPED!'); +export class WhirlpoolTag extends VortexTrapTag { + constructor(turnCount: integer, sourceId: integer) { + super(BattlerTagType.WHIRLPOOL, CommonAnim.WHIRLPOOL, turnCount, sourceId, Moves.WHIRLPOOL); } } -export class SandTombTag extends TrapTag { - constructor(turnCount: integer) { - super(BattlerTagType.SAND_TOMB, CommonAnim.SAND_TOMB, turnCount); +export class ClampTag extends DamagingTrapTag { + constructor(turnCount: integer, sourceId: integer) { + super(BattlerTagType.CLAMP, CommonAnim.CLAMP, turnCount, sourceId, Moves.CLAMP); } getTrapMessage(pokemon: Pokemon): string { - return getPokemonMessage(pokemon, ` was trapped\nby ${this.getTrapName()}!`); + return getPokemonMessage(pokemon.scene.getPokemonById(this.sourceId), ` CLAMPED\n${pokemon.name}!`); } } -export class MagmaStormTag extends TrapTag { - constructor(turnCount: integer) { - super(BattlerTagType.MAGMA_STORM, CommonAnim.MAGMA_STORM, turnCount); +export class SandTombTag extends DamagingTrapTag { + constructor(turnCount: integer, sourceId: integer) { + super(BattlerTagType.SAND_TOMB, CommonAnim.SAND_TOMB, turnCount, sourceId, Moves.SAND_TOMB); + } + + getTrapMessage(pokemon: Pokemon): string { + return getPokemonMessage(pokemon.scene.getPokemonById(this.sourceId), ` became trapped\nby ${this.getMoveName()}!`); + } +} + +export class MagmaStormTag extends DamagingTrapTag { + constructor(turnCount: integer, sourceId: integer) { + super(BattlerTagType.MAGMA_STORM, CommonAnim.MAGMA_STORM, turnCount, sourceId, Moves.MAGMA_STORM); } getTrapMessage(pokemon: Pokemon): string { @@ -432,7 +454,7 @@ export class HideSpriteTag extends BattlerTag { } } -export function getBattlerTag(tagType: BattlerTagType, turnCount: integer): BattlerTag { +export function getBattlerTag(tagType: BattlerTagType, turnCount: integer, sourceId: integer, sourceMove: Moves): BattlerTag { switch (tagType) { case BattlerTagType.RECHARGING: return new RechargingTag(); @@ -445,25 +467,27 @@ export function getBattlerTag(tagType: BattlerTagType, turnCount: integer): Batt case BattlerTagType.NIGHTMARE: return new NightmareTag(); case BattlerTagType.INGRAIN: - return new IngrainTag(); + return new IngrainTag(sourceId); case BattlerTagType.AQUA_RING: return new AquaRingTag(); case BattlerTagType.DROWSY: return new DrowsyTag(); + case BattlerTagType.TRAPPED: + return new TrappedTag(tagType, BattlerTagLapseType.CUSTOM, turnCount, sourceId, sourceMove); case BattlerTagType.BIND: - return new BindTag(turnCount); + return new BindTag(turnCount, sourceId); case BattlerTagType.WRAP: - return new WrapTag(turnCount); + return new WrapTag(turnCount, sourceId); case BattlerTagType.FIRE_SPIN: - return new FireSpinTag(turnCount); + return new FireSpinTag(turnCount, sourceId); case BattlerTagType.WHIRLPOOL: - return new WhirlpoolTag(turnCount); + return new WhirlpoolTag(turnCount, sourceId); case BattlerTagType.CLAMP: - return new ClampTag(turnCount); + return new ClampTag(turnCount, sourceId); case BattlerTagType.SAND_TOMB: - return new SandTombTag(turnCount); + return new SandTombTag(turnCount, sourceId); case BattlerTagType.MAGMA_STORM: - return new MagmaStormTag(turnCount); + return new MagmaStormTag(turnCount, sourceId); case BattlerTagType.PROTECTED: return new ProtectedTag(); case BattlerTagType.FLYING: diff --git a/src/data/move.ts b/src/data/move.ts index 083716376..ab22a3458 100644 --- a/src/data/move.ts +++ b/src/data/move.ts @@ -1410,7 +1410,7 @@ export class AddBattlerTagAttr extends MoveEffectAttr { return false; if (move.chance < 0 || move.chance === 100 || Utils.randInt(100) < move.chance) { - (this.selfTarget ? user : target).addTag(this.tagType, this.turnCount); + (this.selfTarget ? user : target).addTag(this.tagType, this.turnCount, user.id, move.id); return true; } @@ -1738,7 +1738,7 @@ export const allMoves = [ new AttackMove(Moves.ABSORB, "Absorb", Type.GRASS, MoveCategory.SPECIAL, 20, 100, 25, -1, "User recovers half the HP inflicted on opponent.", -1, 0, 1, new HitHealAttr()), new AttackMove(Moves.MEGA_DRAIN, "Mega Drain", Type.GRASS, MoveCategory.SPECIAL, 40, 100, 15, -1, "User recovers half the HP inflicted on opponent.", -1, 0, 1, new HitHealAttr()), new StatusMove(Moves.LEECH_SEED, "Leech Seed", Type.GRASS, 90, 10, -1, "Drains HP from opponent each turn.", -1, 0, 1, - new ConditionalMoveAttr((user: Pokemon, target: Pokemon, move: Move) => !target.getTag(BattlerTagType.SEEDED) && !target.species.isOfType(Type.GRASS)), new AddBattlerTagAttr(BattlerTagType.SEEDED)), + new ConditionalMoveAttr((user: Pokemon, target: Pokemon, move: Move) => !target.getTag(BattlerTagType.SEEDED) && !target.isOfType(Type.GRASS)), new AddBattlerTagAttr(BattlerTagType.SEEDED)), new SelfStatusMove(Moves.GROWTH, "Growth", Type.NORMAL, -1, 20, -1, "Raises user's Attack and Special Attack.", -1, 0, 1, new GrowthStatChangeAttr()), new AttackMove(Moves.RAZOR_LEAF, "Razor Leaf", Type.GRASS, MoveCategory.PHYSICAL, 55, 95, 25, -1, "High critical hit ratio.", -1, 0, 1, new HighCritAttr()), new AttackMove(Moves.SOLAR_BEAM, "Solar Beam", Type.GRASS, MoveCategory.SPECIAL, 120, 100, 10, 168, "Charges on first turn, attacks on second.", -1, 0, 1, @@ -1896,7 +1896,8 @@ export const allMoves = [ new AttackMove(Moves.SPARK, "Spark", Type.ELECTRIC, MoveCategory.PHYSICAL, 65, 100, 20, -1, "May paralyze opponent.", 30, 0, 2, new StatusEffectAttr(StatusEffect.PARALYSIS)), new AttackMove(Moves.FURY_CUTTER, "Fury Cutter", Type.BUG, MoveCategory.PHYSICAL, 40, 95, 20, -1, "Power increases each turn.", -1, 0, 2, new ConsecutiveUseDoublePowerAttr(3, true)), new AttackMove(Moves.STEEL_WING, "Steel Wing", Type.STEEL, MoveCategory.PHYSICAL, 70, 90, 25, -1, "May raise user's Defense.", 10, 0, 2, new StatChangeAttr(BattleStat.DEF, 1, true)), - new StatusMove(Moves.MEAN_LOOK, "Mean Look (N)", 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, + new ConditionalMoveAttr((user: Pokemon, target: Pokemon, move: Move) => !target.getTag(BattlerTagType.TRAPPED)), new AddBattlerTagAttr(BattlerTagType.TRAPPED, false, 1)), 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 SelfStatusMove(Moves.SLEEP_TALK, "Sleep Talk", Type.NORMAL, -1, 10, 70, "User performs one of its own moves while sleeping.", -1, 0, 2, new BypassSleepAttr(), new ConditionalMoveAttr((user: Pokemon, target: Pokemon, move: Move) => user.status?.effect === StatusEffect.SLEEP), new RandomMovesetMoveAttr()), @@ -1967,7 +1968,7 @@ export const allMoves = [ new SelfStatusMove(Moves.WISH, "Wish (N)", Type.NORMAL, -1, 10, -1, "The user recovers HP in the following turn.", -1, 0, 3), new SelfStatusMove(Moves.ASSIST, "Assist (N)", Type.NORMAL, -1, 20, -1, "User performs a move known by its allies at random.", -1, 0, 3), new SelfStatusMove(Moves.INGRAIN, "Ingrain", Type.GRASS, -1, 20, -1, "User restores HP each turn. User cannot escape/switch.", -1, 0, 3, - new NoTagOverlapConditionalAttr(BattlerTagType.INGRAIN, true), new AddBattlerTagAttr(BattlerTagType.INGRAIN, true)), // TODO + new NoTagOverlapConditionalAttr(BattlerTagType.INGRAIN, true), new AddBattlerTagAttr(BattlerTagType.INGRAIN, true)), new AttackMove(Moves.SUPERPOWER, "Superpower", Type.FIGHTING, MoveCategory.PHYSICAL, 120, 100, 5, -1, "Lowers user's Attack and Defense.", 100, 0, 3, new StatChangeAttr([ BattleStat.ATK, BattleStat.DEF ], -1, true)), new SelfStatusMove(Moves.MAGIC_COAT, "Magic Coat (N)", Type.PSYCHIC, -1, 15, -1, "Reflects moves that cause status conditions back to the attacker.", -1, 4, 3), @@ -2035,7 +2036,8 @@ export const allMoves = [ new AttackMove(Moves.AERIAL_ACE, "Aerial Ace", Type.FLYING, MoveCategory.PHYSICAL, 60, -1, 20, 27, "Ignores Accuracy and Evasiveness.", -1, 0, 3), new AttackMove(Moves.ICICLE_SPEAR, "Icicle Spear", Type.ICE, MoveCategory.PHYSICAL, 25, 100, 30, -1, "Hits 2-5 times in one turn.", -1, 0, 3, new MultiHitAttr()), new SelfStatusMove(Moves.IRON_DEFENSE, "Iron Defense", Type.STEEL, -1, 15, 104, "Sharply raises user's Defense.", -1, 0, 3, new StatChangeAttr(BattleStat.DEF, 2, true)), - new StatusMove(Moves.BLOCK, "Block (N)", Type.NORMAL, -1, 5, -1, "Opponent cannot flee or switch.", -1, 0, 3), + new StatusMove(Moves.BLOCK, "Block", Type.NORMAL, -1, 5, -1, "Opponent cannot flee or switch.", -1, 0, 3, + new ConditionalMoveAttr((user: Pokemon, target: Pokemon, move: Move) => !target.getTag(BattlerTagType.TRAPPED)), new AddBattlerTagAttr(BattlerTagType.TRAPPED, false, 1)), new SelfStatusMove(Moves.HOWL, "Howl", Type.NORMAL, -1, 40, -1, "Raises Attack of allies.", -1, 0, 3, new StatChangeAttr(BattleStat.ATK, 1, true)), // TODO new AttackMove(Moves.DRAGON_CLAW, "Dragon Claw", Type.DRAGON, MoveCategory.PHYSICAL, 80, 100, 15, 78, "", -1, 0, 3), new AttackMove(Moves.FRENZY_PLANT, "Frenzy Plant", Type.GRASS, MoveCategory.SPECIAL, 150, 90, 5, 155, "User must recharge next turn.", -1, 0, 3, new AddBattlerTagAttr(BattlerTagType.RECHARGING, true)), diff --git a/src/modifier/modifier-type.ts b/src/modifier/modifier-type.ts index 943b03bf8..130f933b4 100644 --- a/src/modifier/modifier-type.ts +++ b/src/modifier/modifier-type.ts @@ -272,13 +272,13 @@ export class PokemonBaseStatBoosterModifierType extends PokemonHeldItemModifierT class AllPokemonFullHpRestoreModifierType extends ModifierType { constructor(name: string, description?: string, newModifierFunc?: NewModifierFunc, iconImage?: string) { - super(name, description || `Restore 100% HP for all POKéMON`, newModifierFunc || ((_type, _args) => new Modifiers.PokemonHpRestoreModifier(this, -1, 100, false)), iconImage); + super(name, description || `Restore 100% HP for all POKéMON`, newModifierFunc || ((_type, _args) => new Modifiers.PokemonHpRestoreModifier(this, -1, 100, true)), iconImage); } } class AllPokemonFullReviveModifierType extends AllPokemonFullHpRestoreModifierType { constructor(name: string, iconImage?: string) { - super(name, `Revives all fainted POKéMON, restoring 100% HP`, (_type, _args) => new Modifiers.PokemonHpRestoreModifier(this, -1, 100, true), iconImage); + super(name, `Revives all fainted POKéMON, restoring 100% HP`, (_type, _args) => new Modifiers.PokemonHpRestoreModifier(this, -1, 100, true, true), iconImage); } } diff --git a/src/modifier/modifier.ts b/src/modifier/modifier.ts index 0276b0001..3baf44bda 100644 --- a/src/modifier/modifier.ts +++ b/src/modifier/modifier.ts @@ -279,9 +279,8 @@ export abstract class PokemonHeldItemModifier extends PersistentModifier { return container; } - getPokemon(scene: BattleScene) { - const findInParty = (party: Pokemon[]) => party.find(p => p.id === this.pokemonId); - return findInParty(scene.getParty()) || findInParty(scene.getEnemyParty()); + getPokemon(scene: BattleScene): Pokemon { + return scene.getPokemonById(this.pokemonId); } } diff --git a/src/pokemon.ts b/src/pokemon.ts index 72304b7a9..2f42bff37 100644 --- a/src/pokemon.ts +++ b/src/pokemon.ts @@ -21,7 +21,7 @@ import { BattlerTag, BattlerTagLapseType, BattlerTagType, getBattlerTag } from ' import { Species } from './data/species'; import { WeatherType } from './data/weather'; import { TempBattleStat } from './data/temp-battle-stat'; -import { ArenaTagType, WeakenTypeTag as WeakenMoveTypeTag } from './data/arena-tag'; +import { WeakenTypeTag as WeakenMoveTypeTag } from './data/arena-tag'; export default abstract class Pokemon extends Phaser.GameObjects.Container { public id: integer; @@ -341,6 +341,10 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { return types; } + isOfType(type: Type) { + return this.getTypes().indexOf(type) > -1; + } + getEvolution(): SpeciesEvolution { if (!pokemonEvolutions.hasOwnProperty(this.species.speciesId)) return null; @@ -554,18 +558,23 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { } } - addTag(tagType: BattlerTagType, turnCount?: integer): boolean { + addTag(tagType: BattlerTagType, turnCount?: integer, sourceId?: integer, sourceMove?: Moves): boolean { const existingTag = this.getTag(tagType); if (existingTag) { existingTag.onOverlap(this); return false; } - const newTag = getBattlerTag(tagType, turnCount || 0); - this.summonData.tags.push(newTag); - newTag.onAdd(this); + const newTag = getBattlerTag(tagType, turnCount || 0, sourceId, sourceMove); - return true; + if (newTag.canAdd(this)) { + this.summonData.tags.push(newTag); + newTag.onAdd(this); + + return true; + } + + return false; } getTag(tagType: BattlerTagType | { new(...args: any[]): BattlerTag }): BattlerTag { @@ -606,6 +615,14 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { }); } + removeTagsBySourceId(sourceId: integer): void { + const tags = this.summonData.tags; + tags.filter(t => t.sourceId === sourceId).forEach(t => { + t.onRemove(this); + tags.splice(tags.indexOf(t), 1); + }); + } + getMoveHistory(): TurnMove[] { return this.summonData.moveHistory; } @@ -686,19 +703,18 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { trySetStatus(effect: StatusEffect): boolean { if (this.status) return false; - const speciesForm = this.getSpeciesForm(); switch (effect) { case StatusEffect.POISON: case StatusEffect.TOXIC: - if (speciesForm.isOfType(Type.POISON) || speciesForm.isOfType(Type.STEEL)) + if (this.isOfType(Type.POISON) || this.isOfType(Type.STEEL)) return false; break; case StatusEffect.FREEZE: - if (speciesForm.isOfType(Type.ICE)) + if (this.isOfType(Type.ICE)) return false; break; case StatusEffect.BURN: - if (speciesForm.isOfType(Type.FIRE)) + if (this.isOfType(Type.FIRE)) return false; break; } diff --git a/src/ui/party-ui-handler.ts b/src/ui/party-ui-handler.ts index d08ad2fab..54e4d9f76 100644 --- a/src/ui/party-ui-handler.ts +++ b/src/ui/party-ui-handler.ts @@ -218,13 +218,7 @@ export default class PartyUiHandler extends MessageUiHandler { return; } else { this.clearOptions(); - this.partyMessageBox.setTexture('party_message_large'); - this.message.y -= 15; - this.showText(filterResult as string, null, () => { - this.partyMessageBox.setTexture('party_message'); - this.message.setText(defaultMessage); - this.message.y += 15; - }, null, true); + this.showText(filterResult as string, null, () => this.showText(null, 0), null, true); } } else if (option === PartyOption.SUMMARY) { ui.playSelect(); @@ -239,11 +233,11 @@ export default class PartyUiHandler extends MessageUiHandler { this.doRelease(this.cursor); }, () => { ui.setMode(Mode.PARTY); - this.message.setText(defaultMessage); + this.showText(null, 0); }); }); } else - this.showText('You can\'t release a POKéMON that\'s in battle!', null, () => this.message.setText(defaultMessage), null, true); + this.showText('You can\'t release a POKéMON that\'s in battle!', null, () => this.showText(null, 0), null, true); } else if (option === PartyOption.CANCEL) this.processInput(Button.CANCEL); } else if (button === Button.CANCEL) { @@ -363,6 +357,21 @@ export default class PartyUiHandler extends MessageUiHandler { return changed; } + showText(text: string, delay?: integer, callback?: Function, callbackDelay?: integer, prompt?: boolean, promptDelay?: integer) { + if (text === null) + text = defaultMessage; + + if (text?.indexOf('\n') === -1) { + this.partyMessageBox.setTexture('party_message'); + this.message.setY(10); + } else { + this.partyMessageBox.setTexture('party_message_large'); + this.message.setY(-5); + } + + super.showText(text, delay, callback, callbackDelay, prompt, promptDelay); + } + showOptions() { if (this.cursor === 6) return; @@ -385,7 +394,7 @@ export default class PartyUiHandler extends MessageUiHandler { break; } - this.message.setText(optionsMessage); + this.showText(optionsMessage, 0); const optionsBottom = this.scene.add.image(0, 0, `party_options${wideOptions ? '_wide' : ''}_bottom`); optionsBottom.setOrigin(1, 1); @@ -500,7 +509,7 @@ export default class PartyUiHandler extends MessageUiHandler { this.selectCallback = null; selectCallback(this.cursor, PartyOption.RELEASE); } else - this.message.setText(defaultMessage); + this.showText(null, 0); }, null, true); } @@ -535,7 +544,7 @@ export default class PartyUiHandler extends MessageUiHandler { this.eraseOptionsCursor(); this.partyMessageBox.setTexture('party_message'); - this.message.setText(defaultMessage); + this.showText(null, 0); } eraseOptionsCursor() {