pokerogue/src/modifier/modifier.ts

1215 lines
35 KiB
TypeScript
Raw Normal View History

2023-04-12 11:57:15 -04:00
import * as ModifierTypes from './modifier-type';
2023-04-20 19:44:56 -04:00
import { LearnMovePhase, LevelUpPhase, PokemonHealPhase } from "../battle-phases";
import BattleScene from "../battle-scene";
2023-04-20 15:46:05 -04:00
import { getLevelTotalExp } from "../data/exp";
import { PokeballType } from "../data/pokeball";
2023-04-23 18:40:21 -04:00
import Pokemon, { PlayerPokemon } from "../pokemon";
2023-04-20 15:46:05 -04:00
import { Stat } from "../data/pokemon-stat";
import { addTextObject, TextStyle } from "../ui/text";
import { Type } from '../data/type';
import { EvolutionPhase } from '../evolution-phase';
import { pokemonEvolutions } from '../data/pokemon-evolutions';
import { getPokemonMessage } from '../messages';
import * as Utils from "../utils";
import { TempBattleStat } from '../data/temp-battle-stat';
import { BerryType, getBerryEffectFunc, getBerryPredicate } from '../data/berry';
2023-04-26 16:07:29 -04:00
import { Species } from '../data/species';
2023-03-28 14:54:52 -04:00
2023-04-12 11:57:15 -04:00
type ModifierType = ModifierTypes.ModifierType;
2023-04-14 18:21:33 -04:00
export type ModifierPredicate = (modifier: Modifier) => boolean;
2023-04-12 11:57:15 -04:00
2023-03-28 14:54:52 -04:00
export class ModifierBar extends Phaser.GameObjects.Container {
2023-04-20 19:44:56 -04:00
private player: boolean;
2023-03-28 14:54:52 -04:00
2023-04-20 19:44:56 -04:00
constructor(scene: BattleScene, enemy?: boolean) {
super(scene, 1 + (enemy ? 302 : 0), 2);
this.player = !enemy;
2023-03-28 14:54:52 -04:00
this.setScale(0.5);
}
2023-04-09 19:15:21 -04:00
updateModifiers(modifiers: PersistentModifier[]) {
this.removeAll(true);
for (let modifier of modifiers) {
const icon = modifier.getIcon(this.scene as BattleScene);
this.add(icon);
this.setModifierIconPosition(icon, modifiers.length);
2023-03-28 14:54:52 -04:00
}
}
setModifierIconPosition(icon: Phaser.GameObjects.Container, modifierCount: integer) {
let rowIcons: integer = 12 + 6 * Math.max((Math.ceil(modifierCount / 12) - 2), 0);
const x = (this.getIndex(icon) % rowIcons) * 26 / (rowIcons / 12);
const y = Math.floor(this.getIndex(icon) / rowIcons) * 20;
2023-03-28 14:54:52 -04:00
2023-04-20 19:44:56 -04:00
icon.setPosition(this.player ? x : -x, y);
2023-03-28 14:54:52 -04:00
}
}
export abstract class Modifier {
public type: ModifierType;
constructor(type: ModifierType) {
this.type = type;
}
2023-04-09 19:15:21 -04:00
match(_modifier: Modifier): boolean {
return false;
2023-03-28 14:54:52 -04:00
}
shouldApply(_args: any[]): boolean {
return true;
}
abstract apply(args: any[]): boolean;
2023-04-09 19:15:21 -04:00
}
export abstract class PersistentModifier extends Modifier {
public stackCount: integer;
public virtualStackCount: integer;
2023-04-21 14:05:16 -04:00
constructor(type: ModifierType, stackCount: integer) {
2023-04-09 19:15:21 -04:00
super(type);
2023-04-21 14:05:16 -04:00
this.stackCount = stackCount === undefined ? 1 : stackCount;
2023-04-09 19:15:21 -04:00
this.virtualStackCount = 0;
}
add(modifiers: PersistentModifier[], virtual: boolean): boolean {
for (let modifier of modifiers) {
if (this.match(modifier))
return modifier.incrementStack(this.stackCount, virtual);
2023-04-09 19:15:21 -04:00
}
if (virtual) {
this.virtualStackCount += this.stackCount;
this.stackCount = 0;
}
modifiers.push(this);
return true;
}
abstract clone(): PersistentModifier;
2023-04-28 15:03:42 -04:00
getArgs(): any[] {
return [];
}
2023-04-21 14:05:16 -04:00
incrementStack(amount: integer, virtual: boolean): boolean {
if (this.getStackCount() + amount <= this.getMaxStackCount()) {
2023-04-09 19:15:21 -04:00
if (!virtual)
2023-04-21 14:05:16 -04:00
this.stackCount += amount;
2023-04-09 19:15:21 -04:00
else
2023-04-21 14:05:16 -04:00
this.virtualStackCount += amount;
return true;
2023-04-09 19:15:21 -04:00
}
return false;
2023-04-09 19:15:21 -04:00
}
2023-03-28 14:54:52 -04:00
2023-04-09 19:15:21 -04:00
getStackCount(): integer {
return this.stackCount + this.virtualStackCount;
2023-03-30 23:02:35 -04:00
}
getMaxStackCount(): integer {
return 99;
2023-03-28 14:54:52 -04:00
}
2023-04-23 18:40:21 -04:00
getIcon(scene: BattleScene, forSummary?: boolean): Phaser.GameObjects.Container {
2023-03-28 14:54:52 -04:00
const container = scene.add.container(0, 0);
const item = scene.add.sprite(0, 12, 'items');
item.setFrame(this.type.iconImage);
item.setOrigin(0, 0.5);
container.add(item);
const stackText = this.getIconStackText(scene);
if (stackText)
container.add(stackText);
2023-04-09 19:15:21 -04:00
const virtualStackText = this.getIconStackText(scene, true);
if (virtualStackText)
container.add(virtualStackText);
2023-03-28 14:54:52 -04:00
return container;
}
2023-04-09 19:15:21 -04:00
getIconStackText(scene: BattleScene, virtual?: boolean): Phaser.GameObjects.Text {
if (this.getMaxStackCount() === 1 || (virtual && !this.virtualStackCount))
2023-03-28 14:54:52 -04:00
return null;
2023-04-09 19:15:21 -04:00
const isStackMax = this.getStackCount() >= this.getMaxStackCount();
const maxColor = '#f89890';
const maxStrokeColor = '#984038';
if (virtual) {
const virtualText = addTextObject(scene, 27, 12, `+${this.virtualStackCount.toString()}`, TextStyle.PARTY, { fontSize: '66px', color: !isStackMax ? '#40c8f8' : maxColor });
2023-04-09 19:15:21 -04:00
virtualText.setShadow(0, 0, null);
virtualText.setStroke(!isStackMax ? '#006090' : maxStrokeColor, 16)
virtualText.setOrigin(1, 0);
return virtualText;
}
const text = addTextObject(scene, 8, 12, this.stackCount.toString(), TextStyle.PARTY, { fontSize: '66px', color: !isStackMax ? '#f8f8f8' : maxColor });
text.setShadow(0, 0, null);
2023-03-28 14:54:52 -04:00
text.setStroke('#424242', 16)
2023-04-09 19:15:21 -04:00
text.setOrigin(0, 0);
2023-03-28 14:54:52 -04:00
return text;
}
}
export abstract class ConsumableModifier extends Modifier {
constructor(type: ModifierType) {
super(type);
}
2023-04-09 19:15:21 -04:00
add(_modifiers: Modifier[]): boolean {
2023-03-28 14:54:52 -04:00
return true;
}
shouldApply(args: any[]): boolean {
2023-03-30 23:02:35 -04:00
return super.shouldApply(args) && args.length === 1 && args[0] instanceof BattleScene;
2023-03-28 14:54:52 -04:00
}
}
2023-04-12 11:57:15 -04:00
export class AddPokeballModifier extends ConsumableModifier {
2023-03-28 14:54:52 -04:00
private pokeballType: PokeballType;
private count: integer;
constructor(type: ModifierType, pokeballType: PokeballType, count: integer) {
super(type);
this.pokeballType = pokeballType;
this.count = count;
}
apply(args: any[]): boolean {
2023-04-01 22:59:07 -04:00
const pokeballCounts = (args[0] as BattleScene).pokeballCounts;
pokeballCounts[this.pokeballType] = Math.min(pokeballCounts[this.pokeballType] + this.count, 99);
2023-03-28 14:54:52 -04:00
return true;
}
}
2023-06-01 11:22:34 -04:00
export abstract class LapsingPersistentModifier extends PersistentModifier {
protected battlesLeft: integer;
constructor(type: ModifierTypes.ModifierType, battlesLeft?: integer, stackCount?: integer) {
super(type, stackCount);
this.battlesLeft = battlesLeft;
}
lapse(): boolean {
return !!--this.battlesLeft;
}
getIcon(scene: BattleScene): Phaser.GameObjects.Container {
const container = super.getIcon(scene);
const battleCountText = addTextObject(scene, 27, 0, this.battlesLeft.toString(), TextStyle.PARTY, { fontSize: '66px', color: '#f89890' });
battleCountText.setShadow(0, 0, null);
battleCountText.setStroke('#984038', 16)
battleCountText.setOrigin(1, 0);
container.add(battleCountText);
return container;
}
}
export class DoubleBattleChanceBoosterModifier extends LapsingPersistentModifier {
constructor(type: ModifierTypes.DoubleBattleChanceBoosterModifierType, battlesLeft: integer, stackCount?: integer) {
super(type, battlesLeft, stackCount);
}
2023-06-01 13:54:52 -04:00
match(modifier: Modifier): boolean {
if (modifier instanceof DoubleBattleChanceBoosterModifier)
return (modifier as DoubleBattleChanceBoosterModifier).battlesLeft === this.battlesLeft;
return false;
}
2023-06-01 11:22:34 -04:00
clone(): TempBattleStatBoosterModifier {
return new TempBattleStatBoosterModifier(this.type as ModifierTypes.TempBattleStatBoosterModifierType, this.battlesLeft, this.stackCount);
}
getArgs(): any[] {
return [ this.battlesLeft ];
}
apply(args: any[]): boolean {
const doubleBattleChance = args[0] as Utils.NumberHolder;
doubleBattleChance.value = Math.ceil(doubleBattleChance.value / 2);
return true;
}
}
export class TempBattleStatBoosterModifier extends LapsingPersistentModifier {
2023-04-20 15:46:05 -04:00
private tempBattleStat: TempBattleStat;
2023-04-28 15:03:42 -04:00
constructor(type: ModifierTypes.TempBattleStatBoosterModifierType, tempBattleStat: TempBattleStat, battlesLeft?: integer, stackCount?: integer) {
2023-06-01 11:22:34 -04:00
super(type, battlesLeft || 5, stackCount);
this.tempBattleStat = tempBattleStat;
}
2023-06-01 13:54:52 -04:00
match(modifier: Modifier): boolean {
if (modifier instanceof TempBattleStatBoosterModifier)
return (modifier as TempBattleStatBoosterModifier).tempBattleStat === this.tempBattleStat
&& (modifier as TempBattleStatBoosterModifier).battlesLeft === this.battlesLeft;
return false;
}
clone(): TempBattleStatBoosterModifier {
2023-06-01 11:22:34 -04:00
return new TempBattleStatBoosterModifier(this.type as ModifierTypes.TempBattleStatBoosterModifierType, this.tempBattleStat, this.battlesLeft, this.stackCount);
}
2023-04-28 15:03:42 -04:00
getArgs(): any[] {
return [ this.tempBattleStat, this.battlesLeft ];
}
apply(args: any[]): boolean {
2023-04-20 15:46:05 -04:00
const tempBattleStat = args[0] as TempBattleStat;
if (tempBattleStat === this.tempBattleStat) {
const statLevel = args[1] as Utils.IntegerHolder;
statLevel.value = Math.min(statLevel.value + 1, 6);
return true;
}
return false;
}
}
2023-04-26 19:19:39 -04:00
export class MapModifier extends PersistentModifier {
constructor(type: ModifierType, stackCount?: integer) {
super(type, stackCount);
}
clone(): MapModifier {
return new MapModifier(this.type, this.stackCount);
}
apply(args: any[]): boolean {
return true;
}
2023-05-19 16:13:11 -04:00
getMaxStackCount(): integer {
2023-04-26 19:19:39 -04:00
return 1;
}
}
2023-04-09 19:15:21 -04:00
export abstract class PokemonHeldItemModifier extends PersistentModifier {
2023-03-28 14:54:52 -04:00
public pokemonId: integer;
2023-04-21 14:05:16 -04:00
constructor(type: ModifierType, pokemonId: integer, stackCount: integer) {
super(type, stackCount);
2023-03-28 14:54:52 -04:00
this.pokemonId = pokemonId;
}
2023-04-21 14:05:16 -04:00
abstract matchType(_modifier: Modifier): boolean;
match(modifier: Modifier) {
return this.matchType(modifier) && (modifier as PokemonHeldItemModifier).pokemonId === this.pokemonId;
}
2023-04-28 15:03:42 -04:00
getArgs(): any[] {
return [ this.pokemonId ];
}
2023-03-28 14:54:52 -04:00
shouldApply(args: any[]): boolean {
2023-03-30 23:02:35 -04:00
return super.shouldApply(args) && args.length && args[0] instanceof Pokemon && (this.pokemonId === -1 || (args[0] as Pokemon).id === this.pokemonId);
2023-03-28 14:54:52 -04:00
}
getTransferrable(withinParty: boolean) {
return true;
}
2023-04-23 18:40:21 -04:00
getIcon(scene: BattleScene, forSummary?: boolean): Phaser.GameObjects.Container {
const container = !forSummary ? scene.add.container(0, 0) : super.getIcon(scene);
2023-03-28 14:54:52 -04:00
2023-04-23 18:40:21 -04:00
if (!forSummary) {
const pokemon = this.getPokemon(scene);
const isIconShown = pokemon instanceof PlayerPokemon || scene.currentBattle.seenEnemyPartyMemberIds.has(pokemon.id);
const iconAtlasKey = isIconShown ? pokemon.species.getIconAtlasKey() : 'pokemon_icons_0';
const pokemonIcon = scene.add.sprite(0, 8, iconAtlasKey);
if (pokemon.species.isObtainable()) {
const iconKey = isIconShown ? pokemon.getIconKey() : 'pkmn_icon__000';
pokemonIcon.play(iconKey).stop();
} else {
2023-04-26 16:07:29 -04:00
if (pokemon.species.speciesId === Species.ETERNATUS)
pokemonIcon.setScale(0.5, 0.5);
else
pokemonIcon.setPosition(-8, 0);
pokemonIcon.setFrame(pokemon.getIconId());
}
2023-04-23 18:40:21 -04:00
pokemonIcon.setOrigin(0, 0.5);
2023-03-28 14:54:52 -04:00
2023-04-23 18:40:21 -04:00
container.add(pokemonIcon);
2023-03-28 14:54:52 -04:00
2023-04-23 18:40:21 -04:00
const item = scene.add.sprite(16, this.virtualStackCount ? 8 : 16, 'items');
item.setScale(0.5);
item.setOrigin(0, 0.5);
item.setTexture('items', this.type.iconImage);
container.add(item);
2023-04-14 01:08:44 -04:00
2023-04-23 18:40:21 -04:00
const stackText = this.getIconStackText(scene);
if (stackText)
container.add(stackText);
2023-04-14 01:08:44 -04:00
2023-04-23 18:40:21 -04:00
const virtualStackText = this.getIconStackText(scene, true);
if (virtualStackText)
container.add(virtualStackText);
} else
container.setScale(0.5);
2023-04-14 01:08:44 -04:00
2023-03-28 14:54:52 -04:00
return container;
}
2023-04-22 22:14:53 -04:00
getPokemon(scene: BattleScene): Pokemon {
return scene.getPokemonById(this.pokemonId);
2023-03-28 14:54:52 -04:00
}
}
2023-04-09 19:15:21 -04:00
export class PokemonBaseStatModifier extends PokemonHeldItemModifier {
2023-03-28 14:54:52 -04:00
protected stat: Stat;
2023-04-21 14:05:16 -04:00
constructor(type: ModifierTypes.PokemonBaseStatBoosterModifierType, pokemonId: integer, stat: Stat, stackCount?: integer) {
super(type, pokemonId, stackCount);
2023-03-28 14:54:52 -04:00
this.stat = stat;
}
2023-04-21 14:05:16 -04:00
matchType(modifier: Modifier): boolean {
if (modifier instanceof PokemonBaseStatModifier)
return (modifier as PokemonBaseStatModifier).stat === this.stat;
2023-04-09 19:15:21 -04:00
return false;
}
2023-03-28 14:54:52 -04:00
2023-04-09 19:15:21 -04:00
clone(): PersistentModifier {
2023-04-21 14:05:16 -04:00
return new PokemonBaseStatModifier(this.type as ModifierTypes.PokemonBaseStatBoosterModifierType, this.pokemonId, this.stat, this.stackCount);
2023-03-28 14:54:52 -04:00
}
2023-04-28 15:03:42 -04:00
getArgs(): any[] {
return super.getArgs().concat(this.stat);
}
2023-03-28 14:54:52 -04:00
shouldApply(args: any[]): boolean {
return super.shouldApply(args) && args.length === 2 && args[1] instanceof Array;
2023-03-28 14:54:52 -04:00
}
apply(args: any[]): boolean {
2023-04-09 19:15:21 -04:00
args[1][this.stat] = Math.min(Math.floor(args[1][this.stat] * (1 + this.getStackCount() * 0.2)), 999999);
2023-03-28 14:54:52 -04:00
return true;
}
getTransferrable(_withinParty: boolean): boolean {
return false;
}
2023-04-14 01:08:44 -04:00
}
2023-03-28 14:54:52 -04:00
2023-04-14 18:21:33 -04:00
export class AttackTypeBoosterModifier extends PokemonHeldItemModifier {
private moveType: Type;
private boostMultiplier: number;
2023-04-21 14:05:16 -04:00
constructor(type: ModifierType, pokemonId: integer, moveType: Type, boostPercent: integer, stackCount?: integer) {
super(type, pokemonId, stackCount);
2023-04-14 18:21:33 -04:00
this.moveType = moveType;
this.boostMultiplier = boostPercent * 0.01;
}
2023-04-21 14:05:16 -04:00
matchType(modifier: Modifier) {
if (modifier instanceof AttackTypeBoosterModifier) {
const attackTypeBoosterModifier = modifier as AttackTypeBoosterModifier;
return attackTypeBoosterModifier.moveType === this.moveType && attackTypeBoosterModifier.boostMultiplier === this.boostMultiplier;
}
2023-04-14 18:21:33 -04:00
}
clone() {
2023-04-21 14:05:16 -04:00
return new AttackTypeBoosterModifier(this.type, this.pokemonId, this.moveType, this.boostMultiplier * 100, this.stackCount);
2023-04-14 18:21:33 -04:00
}
2023-04-28 15:03:42 -04:00
getArgs(): any[] {
return super.getArgs().concat([ this.moveType, this.boostMultiplier * 100 ]);
}
2023-04-14 18:21:33 -04:00
shouldApply(args: any[]): boolean {
return super.shouldApply(args) && args.length === 2 && args[1] instanceof Utils.NumberHolder;
}
apply(args: any[]): boolean {
(args[1] as Utils.NumberHolder).value = Math.floor((args[1] as Utils.NumberHolder).value * (1 + (this.getStackCount() * this.boostMultiplier)));
return true;
}
}
2023-04-23 21:31:06 -04:00
export class SurviveDamageModifier extends PokemonHeldItemModifier {
constructor(type: ModifierType, pokemonId: integer, stackCount?: integer) {
super(type, pokemonId, stackCount);
}
matchType(modifier: Modifier) {
return modifier instanceof SurviveDamageModifier;
}
clone() {
return new SurviveDamageModifier(this.type, this.pokemonId, this.stackCount);
}
shouldApply(args: any[]): boolean {
return super.shouldApply(args) && args.length === 2 && args[1] instanceof Utils.BooleanHolder;
}
apply(args: any[]): boolean {
const pokemon = args[0] as Pokemon;
const surviveDamage = args[1] as Utils.BooleanHolder;
2023-04-25 01:32:48 -04:00
if (!surviveDamage.value && Utils.randInt(10) < this.getStackCount()) {
2023-04-23 21:31:06 -04:00
surviveDamage.value = true;
pokemon.scene.queueMessage(getPokemonMessage(pokemon, ` hung on\nusing its ${this.type.name}!`));
}
return true;
}
2023-05-19 16:13:11 -04:00
getMaxStackCount(): integer {
2023-04-23 21:31:06 -04:00
return 5;
}
}
2023-04-25 01:32:48 -04:00
export class FlinchChanceModifier extends PokemonHeldItemModifier {
constructor(type: ModifierType, pokemonId: integer, stackCount?: integer) {
super(type, pokemonId, stackCount);
}
matchType(modifier: Modifier) {
return modifier instanceof FlinchChanceModifier;
}
clone() {
return new FlinchChanceModifier(this.type, this.pokemonId, this.stackCount);
}
shouldApply(args: any[]): boolean {
return super.shouldApply(args) && args.length === 2 && args[1] instanceof Utils.BooleanHolder;
}
apply(args: any[]): boolean {
const flinched = args[1] as Utils.BooleanHolder;
if (!flinched.value && Utils.randInt(10) < this.getStackCount())
flinched.value = true;
return true;
}
getMaxStackCount(): integer {
2023-04-25 01:32:48 -04:00
return 3;
}
}
2023-04-20 20:15:16 -04:00
export class TurnHealModifier extends PokemonHeldItemModifier {
2023-04-21 14:05:16 -04:00
constructor(type: ModifierType, pokemonId: integer, stackCount?: integer) {
super(type, pokemonId, stackCount);
2023-04-20 20:15:16 -04:00
}
2023-04-21 14:05:16 -04:00
matchType(modifier: Modifier) {
2023-04-20 20:15:16 -04:00
return modifier instanceof TurnHealModifier;
}
clone() {
2023-04-21 14:05:16 -04:00
return new TurnHealModifier(this.type, this.pokemonId, this.stackCount);
2023-04-20 20:15:16 -04:00
}
apply(args: any[]): boolean {
const pokemon = args[0] as Pokemon;
if (pokemon.getHpRatio() < 1) {
const scene = pokemon.scene;
scene.unshiftPhase(new PokemonHealPhase(scene, pokemon.getBattlerIndex(),
Math.max(Math.floor(pokemon.getMaxHp() / 16) * this.stackCount, 1), getPokemonMessage(pokemon, `'s ${this.type.name}\nrestored its HP a little!`), true));
2023-04-20 20:15:16 -04:00
}
return true;
}
getMaxStackCount(): integer {
2023-04-23 21:31:06 -04:00
return 4;
2023-04-20 20:15:16 -04:00
}
}
2023-04-14 01:08:44 -04:00
export class HitHealModifier extends PokemonHeldItemModifier {
2023-04-21 14:05:16 -04:00
constructor(type: ModifierType, pokemonId: integer, stackCount?: integer) {
super(type, pokemonId, stackCount);
2023-04-14 01:08:44 -04:00
}
2023-03-28 14:54:52 -04:00
2023-04-21 14:05:16 -04:00
matchType(modifier: Modifier) {
2023-04-14 01:08:44 -04:00
return modifier instanceof HitHealModifier;
}
2023-03-28 14:54:52 -04:00
2023-04-14 01:08:44 -04:00
clone() {
2023-04-21 14:05:16 -04:00
return new HitHealModifier(this.type, this.pokemonId, this.stackCount);
2023-04-14 01:08:44 -04:00
}
2023-03-28 14:54:52 -04:00
2023-04-14 01:08:44 -04:00
apply(args: any[]): boolean {
2023-04-20 19:44:56 -04:00
const pokemon = args[0] as Pokemon;
2023-04-09 19:15:21 -04:00
2023-04-14 01:08:44 -04:00
if (pokemon.turnData.damageDealt && pokemon.getHpRatio() < 1) {
2023-04-14 18:21:33 -04:00
const scene = pokemon.scene;
scene.unshiftPhase(new PokemonHealPhase(scene, pokemon.getBattlerIndex(),
Math.max(Math.floor(pokemon.turnData.damageDealt / 8) * this.stackCount, 1), getPokemonMessage(pokemon, `'s ${this.type.name}\nrestored its HP a little!`), true));
2023-04-14 01:08:44 -04:00
}
return true;
2023-03-28 14:54:52 -04:00
}
2023-04-20 20:15:16 -04:00
getMaxStackCount(): integer {
2023-04-23 21:31:06 -04:00
return 4;
2023-04-20 20:15:16 -04:00
}
2023-03-28 14:54:52 -04:00
}
2023-04-23 12:35:16 -04:00
export class LevelIncrementBoosterModifier extends PersistentModifier {
constructor(type: ModifierType, stackCount?: integer) {
super(type, stackCount);
}
match(modifier: Modifier) {
return modifier instanceof LevelIncrementBoosterModifier;
}
clone() {
return new LevelIncrementBoosterModifier(this.type, this.stackCount);
}
shouldApply(args: any[]): boolean {
return super.shouldApply(args) && args[0] instanceof Utils.IntegerHolder;
}
apply(args: any[]): boolean {
(args[0] as Utils.IntegerHolder).value += this.getStackCount();
return true;
}
}
2023-04-20 15:46:05 -04:00
export class BerryModifier extends PokemonHeldItemModifier {
public berryType: BerryType;
public consumed: boolean;
2023-04-21 14:05:16 -04:00
constructor(type: ModifierType, pokemonId: integer, berryType: BerryType, stackCount?: integer) {
super(type, pokemonId, stackCount);
2023-04-20 15:46:05 -04:00
this.berryType = berryType;
this.consumed = false;
}
2023-04-21 14:05:16 -04:00
matchType(modifier: Modifier) {
return modifier instanceof BerryModifier && (modifier as BerryModifier).berryType === this.berryType;
2023-04-20 15:46:05 -04:00
}
clone() {
2023-04-21 14:05:16 -04:00
return new BerryModifier(this.type, this.pokemonId, this.berryType, this.stackCount);
2023-04-20 15:46:05 -04:00
}
2023-04-28 15:03:42 -04:00
getArgs(): any[] {
return super.getArgs().concat(this.berryType);
}
2023-04-20 15:46:05 -04:00
shouldApply(args: any[]): boolean {
return !this.consumed && super.shouldApply(args) && getBerryPredicate(this.berryType)(args[0] as Pokemon);
}
apply(args: any[]): boolean {
const pokemon = args[0] as Pokemon;
const preserve = new Utils.BooleanHolder(false);
2023-04-20 19:44:56 -04:00
pokemon.scene.applyModifiers(PreserveBerryModifier, pokemon.isPlayer(), preserve);
2023-04-20 15:46:05 -04:00
getBerryEffectFunc(this.berryType)(pokemon);
if (!preserve.value)
this.consumed = true;
return true;
}
}
export class PreserveBerryModifier extends PersistentModifier {
2023-04-21 14:05:16 -04:00
constructor(type: ModifierType, stackCount?: integer) {
super(type, stackCount);
2023-04-20 15:46:05 -04:00
}
match(modifier: Modifier) {
return modifier instanceof PreserveBerryModifier;
}
clone() {
2023-04-21 14:05:16 -04:00
return new PreserveBerryModifier(this.type, this.stackCount);
2023-04-20 15:46:05 -04:00
}
shouldApply(args: any[]): boolean {
return super.shouldApply(args) && args[0] instanceof Utils.BooleanHolder;
}
apply(args: any[]): boolean {
if (!(args[0] as Utils.BooleanHolder).value)
(args[0] as Utils.BooleanHolder).value = this.getStackCount() === this.getMaxStackCount() || Utils.randInt(this.getMaxStackCount()) < this.getStackCount();
return true;
}
getMaxStackCount(): integer {
2023-04-20 15:46:05 -04:00
return 4;
}
}
2023-06-06 08:16:07 -04:00
export class PokemonInstantReviveModifier extends PokemonHeldItemModifier {
constructor(type: ModifierType, pokemonId: integer, stackCount?: integer) {
super(type, pokemonId, stackCount);
}
matchType(modifier: Modifier) {
return modifier instanceof PokemonInstantReviveModifier;
}
clone() {
return new PokemonInstantReviveModifier(this.type, this.pokemonId, this.stackCount);
}
apply(args: any[]): boolean {
const pokemon = args[0] as Pokemon;
2023-06-06 10:14:53 -04:00
pokemon.scene.unshiftPhase(new PokemonHealPhase(pokemon.scene, pokemon.getBattlerIndex(),
Math.max(Math.floor(pokemon.getMaxHp() / 2), 1), getPokemonMessage(pokemon, ` was revived\nby its ${this.type.name}!`), false, false, true));
pokemon.resetStatus();
2023-06-06 08:16:07 -04:00
return true;
}
getMaxStackCount(): integer {
return 10;
}
}
2023-04-09 19:15:21 -04:00
export abstract class ConsumablePokemonModifier extends ConsumableModifier {
public pokemonId: integer;
2023-03-28 14:54:52 -04:00
constructor(type: ModifierType, pokemonId: integer) {
2023-04-09 19:15:21 -04:00
super(type);
this.pokemonId = pokemonId;
2023-03-28 14:54:52 -04:00
}
2023-04-09 19:15:21 -04:00
shouldApply(args: any[]): boolean {
2023-04-20 19:44:56 -04:00
return args.length && args[0] instanceof PlayerPokemon && (this.pokemonId === -1 || (args[0] as PlayerPokemon).id === this.pokemonId);
2023-04-09 19:15:21 -04:00
}
getPokemon(scene: BattleScene) {
return scene.getParty().find(p => p.id === this.pokemonId);
2023-03-28 14:54:52 -04:00
}
}
export class PokemonHpRestoreModifier extends ConsumablePokemonModifier {
2023-04-09 19:15:21 -04:00
private restorePoints: integer;
private restorePercent: integer;
2023-04-20 15:46:05 -04:00
public fainted: boolean;
2023-03-28 14:54:52 -04:00
constructor(type: ModifierType, pokemonId: integer, restorePoints: integer, restorePercent: integer, fainted?: boolean) {
2023-03-28 14:54:52 -04:00
super(type, pokemonId);
2023-04-09 19:15:21 -04:00
this.restorePoints = restorePoints;
this.restorePercent = restorePercent;
this.fainted = !!fainted;
2023-03-28 14:54:52 -04:00
}
2023-04-09 19:15:21 -04:00
shouldApply(args: any[]): boolean {
return super.shouldApply(args) && (this.fainted || (args.length > 1 && typeof(args[1]) === 'number'));
}
2023-03-28 14:54:52 -04:00
apply(args: any[]): boolean {
const pokemon = args[0] as Pokemon;
2023-04-09 19:15:21 -04:00
if (!pokemon.hp === this.fainted) {
let restorePoints = this.restorePoints;
if (!this.fainted)
restorePoints = Math.floor(restorePoints * (args[1] as number));
2023-04-16 18:40:32 -04:00
else
pokemon.resetStatus();
pokemon.hp = Math.min(pokemon.hp + Math.ceil(Math.max(Math.floor((this.restorePercent * 0.01) * pokemon.getMaxHp()), restorePoints)), pokemon.getMaxHp());
2023-04-09 19:15:21 -04:00
}
2023-03-28 14:54:52 -04:00
return true;
}
}
2023-04-18 22:23:06 -04:00
export class PokemonStatusHealModifier extends ConsumablePokemonModifier {
constructor(type: ModifierType, pokemonId: integer) {
super(type, pokemonId);
}
apply(args: any[]): boolean {
const pokemon = args[0] as Pokemon;
pokemon.resetStatus();
return true;
}
}
2023-04-11 11:04:39 -04:00
export abstract class ConsumablePokemonMoveModifier extends ConsumablePokemonModifier {
public moveIndex: integer;
2023-04-04 21:10:11 -04:00
2023-04-11 11:04:39 -04:00
constructor(type: ModifierType, pokemonId: integer, moveIndex: integer) {
2023-04-04 21:10:11 -04:00
super(type, pokemonId);
2023-04-11 11:04:39 -04:00
this.moveIndex = moveIndex;
}
}
export class PokemonPpRestoreModifier extends ConsumablePokemonMoveModifier {
private restorePoints: integer;
constructor(type: ModifierType, pokemonId: integer, moveIndex: integer, restorePoints: integer) {
super(type, pokemonId, moveIndex);
2023-04-04 21:10:11 -04:00
this.restorePoints = restorePoints;
}
2023-04-11 11:04:39 -04:00
apply(args: any[]): boolean {
const pokemon = args[0] as Pokemon;
const move = pokemon.getMoveset()[this.moveIndex];
2023-04-11 11:04:39 -04:00
move.ppUsed = this.restorePoints >= -1 ? Math.max(move.ppUsed - this.restorePoints, 0) : 0;
return true;
}
}
export class PokemonAllMovePpRestoreModifier extends ConsumablePokemonModifier {
private restorePoints: integer;
constructor(type: ModifierType, pokemonId: integer, restorePoints: integer) {
super(type, pokemonId);
this.restorePoints = restorePoints;
2023-04-04 21:10:11 -04:00
}
apply(args: any[]): boolean {
const pokemon = args[0] as Pokemon;
for (let move of pokemon.getMoveset())
2023-04-11 11:04:39 -04:00
move.ppUsed = this.restorePoints >= -1 ? Math.max(move.ppUsed - this.restorePoints, 0) : 0;
2023-04-04 21:10:11 -04:00
return true;
}
}
2023-04-04 18:28:21 -04:00
export class PokemonLevelIncrementModifier extends ConsumablePokemonModifier {
constructor(type: ModifierType, pokemonId: integer) {
super(type, pokemonId);
}
apply(args: any[]): boolean {
const pokemon = args[0] as PlayerPokemon;
2023-04-23 12:35:16 -04:00
const levelCount = new Utils.IntegerHolder(1);
pokemon.scene.applyModifiers(LevelIncrementBoosterModifier, true, levelCount);
pokemon.level += levelCount.value;
if (pokemon.level <= pokemon.scene.getMaxExpLevel()) {
pokemon.exp = getLevelTotalExp(pokemon.level, pokemon.species.growthRate);
pokemon.levelExp = 0;
}
2023-04-04 18:28:21 -04:00
2023-04-14 18:21:33 -04:00
pokemon.scene.unshiftPhase(new LevelUpPhase(pokemon.scene, pokemon.scene.getParty().indexOf(pokemon), pokemon.level - 1, pokemon.level));
2023-04-04 18:28:21 -04:00
return true;
}
}
2023-04-08 00:21:44 -04:00
export class TmModifier extends ConsumablePokemonModifier {
2023-04-12 11:57:15 -04:00
constructor(type: ModifierTypes.TmModifierType, pokemonId: integer) {
2023-04-08 00:21:44 -04:00
super(type, pokemonId);
}
apply(args: any[]): boolean {
const pokemon = args[0] as PlayerPokemon;
2023-04-14 18:21:33 -04:00
pokemon.scene.unshiftPhase(new LearnMovePhase(pokemon.scene, pokemon.scene.getParty().indexOf(pokemon), (this.type as ModifierTypes.TmModifierType).moveId));
2023-04-08 00:21:44 -04:00
return true;
}
}
2023-04-14 18:21:33 -04:00
export class EvolutionItemModifier extends ConsumablePokemonModifier {
constructor(type: ModifierTypes.EvolutionItemModifierType, pokemonId: integer) {
super(type, pokemonId);
}
apply(args: any[]): boolean {
const pokemon = args[0] as PlayerPokemon;
const matchingEvolution = pokemonEvolutions[pokemon.species.speciesId].find(e => e.item === (this.type as ModifierTypes.EvolutionItemModifierType).evolutionItem
&& (!e.condition || e.condition.predicate(pokemon)));
if (matchingEvolution) {
pokemon.scene.unshiftPhase(new EvolutionPhase(pokemon.scene, pokemon.scene.getParty().indexOf(pokemon), matchingEvolution, pokemon.level - 1));
return true;
}
return false;
}
}
export class MultipleParticipantExpBonusModifier extends PersistentModifier {
2023-04-21 14:05:16 -04:00
constructor(type: ModifierType, stackCount?: integer) {
super(type, stackCount);
2023-04-09 19:15:21 -04:00
}
match(modifier: Modifier): boolean {
return modifier instanceof MultipleParticipantExpBonusModifier;
2023-04-09 19:15:21 -04:00
}
apply(_args: any[]): boolean {
return true;
2023-04-09 19:15:21 -04:00
}
clone(): MultipleParticipantExpBonusModifier {
return new MultipleParticipantExpBonusModifier(this.type, this.stackCount);
2023-04-09 19:15:21 -04:00
}
getMaxStackCount(): integer {
return 5;
2023-04-09 19:15:21 -04:00
}
}
export class HealingBoosterModifier extends PersistentModifier {
private multiplier: number;
2023-04-21 14:05:16 -04:00
constructor(type: ModifierType, multiplier: number, stackCount?: integer) {
super(type, stackCount);
2023-04-09 19:15:21 -04:00
this.multiplier = multiplier;
}
match(modifier: Modifier): boolean {
return modifier instanceof HealingBoosterModifier;
}
clone(): HealingBoosterModifier {
2023-04-21 14:05:16 -04:00
return new HealingBoosterModifier(this.type, this.multiplier, this.stackCount);
2023-04-09 19:15:21 -04:00
}
2023-04-28 15:03:42 -04:00
getArgs(): any[] {
return [ this.multiplier ];
}
2023-04-09 19:15:21 -04:00
apply(args: any[]): boolean {
const healingMultiplier = args[0] as Utils.IntegerHolder;
healingMultiplier.value = Math.floor(healingMultiplier.value * (this.multiplier + (this.getStackCount() - 1)));
2023-04-09 19:15:21 -04:00
return true;
}
2023-04-23 21:31:06 -04:00
getMaxStackCount(): integer {
return 3;
2023-04-23 21:31:06 -04:00
}
2023-04-09 19:15:21 -04:00
}
export class ExpBoosterModifier extends PersistentModifier {
private boostMultiplier: integer;
2023-04-21 14:05:16 -04:00
constructor(type: ModifierType, boostPercent: integer, stackCount?: integer) {
super(type, stackCount);
this.boostMultiplier = boostPercent * 0.01;
2023-03-28 14:54:52 -04:00
}
2023-04-09 19:15:21 -04:00
match(modifier: Modifier): boolean {
if (modifier instanceof ExpBoosterModifier) {
const expModifier = modifier as ExpBoosterModifier;
return expModifier.boostMultiplier === this.boostMultiplier;
2023-03-28 14:54:52 -04:00
}
2023-04-09 19:15:21 -04:00
return false;
}
2023-03-28 14:54:52 -04:00
2023-04-09 19:15:21 -04:00
clone(): ExpBoosterModifier {
2023-04-21 14:05:16 -04:00
return new ExpBoosterModifier(this.type, this.boostMultiplier * 100, this.stackCount);
2023-03-28 14:54:52 -04:00
}
2023-04-28 15:03:42 -04:00
getArgs(): any[] {
return [ this.boostMultiplier * 100 ];
}
2023-03-28 14:54:52 -04:00
apply(args: any[]): boolean {
2023-04-09 19:15:21 -04:00
(args[0] as Utils.NumberHolder).value = Math.floor((args[0] as Utils.NumberHolder).value * (1 + (this.getStackCount() * this.boostMultiplier)));
2023-03-30 23:02:35 -04:00
return true;
}
getStackCount(): integer {
return this.boostMultiplier < 1 ? super.getStackCount() : 10;
}
2023-03-30 23:02:35 -04:00
}
export class PokemonExpBoosterModifier extends PokemonHeldItemModifier {
private boostMultiplier: integer;
2023-04-21 14:05:16 -04:00
constructor(type: ModifierTypes.PokemonExpBoosterModifierType, pokemonId: integer, boostPercent: integer, stackCount?: integer) {
super(type, pokemonId, stackCount);
this.boostMultiplier = boostPercent * 0.01;
}
2023-04-21 14:05:16 -04:00
matchType(modifier: Modifier): boolean {
if (modifier instanceof PokemonExpBoosterModifier) {
const pokemonExpModifier = modifier as PokemonExpBoosterModifier;
2023-04-21 14:05:16 -04:00
return pokemonExpModifier.boostMultiplier === this.boostMultiplier;
}
return false;
}
clone(): PersistentModifier {
2023-04-21 14:05:16 -04:00
return new PokemonExpBoosterModifier(this.type as ModifierTypes.PokemonExpBoosterModifierType, this.pokemonId, this.boostMultiplier * 100, this.stackCount);
}
2023-04-28 15:03:42 -04:00
getArgs(): any[] {
return super.getArgs().concat(this.boostMultiplier * 100);
}
shouldApply(args: any[]): boolean {
return super.shouldApply(args) && args.length === 2 && args[1] instanceof Utils.NumberHolder;
}
apply(args: any[]): boolean {
(args[1] as Utils.NumberHolder).value = Math.floor((args[1] as Utils.NumberHolder).value * (1 + (this.getStackCount() * this.boostMultiplier)));
return true;
}
}
2023-04-09 19:15:21 -04:00
export class ExpShareModifier extends PersistentModifier {
2023-04-21 14:05:16 -04:00
constructor(type: ModifierType, stackCount?: integer) {
super(type, stackCount);
2023-03-30 23:02:35 -04:00
}
2023-03-28 14:54:52 -04:00
match(modifier: Modifier): boolean {
return modifier instanceof ExpShareModifier;
}
2023-03-30 23:02:35 -04:00
apply(_args: any[]): boolean {
2023-03-28 14:54:52 -04:00
return true;
}
2023-03-30 23:02:35 -04:00
2023-04-09 19:15:21 -04:00
clone(): ExpShareModifier {
2023-04-21 14:05:16 -04:00
return new ExpShareModifier(this.type, this.stackCount);
2023-04-09 19:15:21 -04:00
}
2023-03-30 23:02:35 -04:00
getMaxStackCount(): integer {
return 5;
}
2023-03-28 14:54:52 -04:00
}
2023-04-19 22:51:46 -04:00
export class ExpBalanceModifier extends PersistentModifier {
2023-04-21 14:05:16 -04:00
constructor(type: ModifierType, stackCount?: integer) {
super(type, stackCount);
2023-04-19 22:51:46 -04:00
}
match(modifier: Modifier): boolean {
return modifier instanceof ExpBalanceModifier;
}
apply(_args: any[]): boolean {
return true;
}
clone(): ExpBalanceModifier {
2023-04-21 14:05:16 -04:00
return new ExpBalanceModifier(this.type, this.stackCount);
2023-04-19 22:51:46 -04:00
}
getMaxStackCount(): integer {
return 1;
}
}
2023-04-09 19:15:21 -04:00
export class ShinyRateBoosterModifier extends PersistentModifier {
2023-04-21 14:05:16 -04:00
constructor(type: ModifierType, stackCount?: integer) {
super(type, stackCount);
2023-03-28 14:54:52 -04:00
}
2023-04-09 19:15:21 -04:00
match(modifier: Modifier): boolean {
return modifier instanceof ShinyRateBoosterModifier;
}
2023-03-28 14:54:52 -04:00
2023-04-09 19:15:21 -04:00
clone(): ShinyRateBoosterModifier {
2023-04-21 14:05:16 -04:00
return new ShinyRateBoosterModifier(this.type, this.stackCount);
2023-03-28 14:54:52 -04:00
}
apply(args: any[]): boolean {
2023-07-05 13:53:28 -04:00
(args[0] as Utils.IntegerHolder).value = Math.floor(Math.pow((args[0] as Utils.IntegerHolder).value * 0.5, Math.sqrt(this.getStackCount()) + 1));
2023-03-28 14:54:52 -04:00
return true;
}
2023-03-30 23:02:35 -04:00
getMaxStackCount(): integer {
2023-07-05 13:53:28 -04:00
return 5;
2023-03-30 23:02:35 -04:00
}
2023-03-28 14:54:52 -04:00
}
2023-04-28 19:26:41 -04:00
export class SwitchEffectTransferModifier extends PokemonHeldItemModifier {
constructor(type: ModifierType, pokemonId: integer, stackCount?: integer) {
super(type, pokemonId, stackCount);
}
matchType(modifier: Modifier): boolean {
return modifier instanceof SwitchEffectTransferModifier;
}
clone(): SwitchEffectTransferModifier {
return new SwitchEffectTransferModifier(this.type, this.pokemonId, this.stackCount);
}
apply(args: any[]): boolean {
return true;
}
getMaxStackCount(): integer {
return 1;
}
}
export abstract class HeldItemTransferModifier extends PokemonHeldItemModifier {
2023-04-23 10:24:22 -04:00
constructor(type: ModifierType, pokemonId: integer, stackCount?: integer) {
super(type, pokemonId, stackCount);
2023-04-21 15:45:48 -04:00
}
apply(args: any[]): boolean {
2023-04-23 10:24:22 -04:00
const pokemon = args[0] as Pokemon;
2023-05-19 16:13:11 -04:00
const opponents = pokemon.getOpponents();
if (!opponents.length)
2023-04-29 01:40:24 -04:00
return false;
2023-04-23 10:24:22 -04:00
2023-05-19 16:13:11 -04:00
const targetPokemon = opponents[Utils.randInt(opponents.length)];
const transferredItemCount = this.getTransferredItemCount();
if (!transferredItemCount)
return false;
const withinParty = pokemon.isPlayer() === targetPokemon.isPlayer();
2023-04-23 10:24:22 -04:00
const transferredModifierTypes: ModifierTypes.ModifierType[] = [];
const itemModifiers = pokemon.scene.findModifiers(m => m instanceof PokemonHeldItemModifier
&& (m as PokemonHeldItemModifier).pokemonId === targetPokemon.id && m.getTransferrable(withinParty), targetPokemon.isPlayer()) as PokemonHeldItemModifier[];
2023-04-23 10:24:22 -04:00
for (let i = 0; i < transferredItemCount; i++) {
2023-04-23 10:24:22 -04:00
if (!itemModifiers.length)
break;
const randItemIndex = Utils.randInt(itemModifiers.length);
const randItem = itemModifiers[randItemIndex];
if (pokemon.scene.tryTransferHeldItemModifier(randItem, pokemon, false, false)) {
transferredModifierTypes.push(randItem.type);
itemModifiers.splice(randItemIndex, 1);
}
}
2023-04-21 15:45:48 -04:00
2023-04-23 10:24:22 -04:00
for (let mt of transferredModifierTypes)
pokemon.scene.queueMessage(this.getTransferMessage(pokemon, targetPokemon, mt));
2023-04-21 15:45:48 -04:00
2023-04-23 10:24:22 -04:00
return !!transferredModifierTypes.length;
2023-04-21 15:45:48 -04:00
}
abstract getTransferredItemCount(): integer
abstract getTransferMessage(pokemon: Pokemon, targetPokemon: Pokemon, item: ModifierTypes.ModifierType): string
}
export class TurnHeldItemTransferModifier extends HeldItemTransferModifier {
constructor(type: ModifierType, pokemonId: integer, stackCount?: integer) {
super(type, pokemonId, stackCount);
}
matchType(modifier: Modifier): boolean {
return modifier instanceof TurnHeldItemTransferModifier;
}
clone(): TurnHeldItemTransferModifier {
return new TurnHeldItemTransferModifier(this.type, this.pokemonId, this.stackCount);
}
getTransferrable(withinParty: boolean) {
return withinParty;
}
getTransferredItemCount(): integer {
return this.getStackCount();
}
getTransferMessage(pokemon: Pokemon, targetPokemon: Pokemon, item: ModifierTypes.ModifierType): string {
return getPokemonMessage(targetPokemon, `'s ${item.name} was absorbed\nby ${pokemon.name}'s ${this.type.name}!`);
}
getMaxStackCount(): integer {
return 3;
}
}
export class ContactHeldItemTransferChanceModifier extends HeldItemTransferModifier {
private chance: number;
constructor(type: ModifierType, pokemonId: integer, chancePercent: integer, stackCount?: integer) {
super(type, pokemonId, stackCount);
this.chance = chancePercent / 100;
}
matchType(modifier: Modifier): boolean {
return modifier instanceof ContactHeldItemTransferChanceModifier;
}
clone(): ContactHeldItemTransferChanceModifier {
return new ContactHeldItemTransferChanceModifier(this.type, this.pokemonId, this.chance * 100, this.stackCount);
}
getArgs(): any[] {
return super.getArgs().concat(this.chance * 100);
}
getTransferredItemCount(): integer {
return Math.random() < (this.chance * this.getStackCount()) ? 1 : 0;
}
getTransferMessage(pokemon: Pokemon, targetPokemon: Pokemon, item: ModifierTypes.ModifierType): string {
return getPokemonMessage(targetPokemon, `'s ${item.name} was snatched\nby ${pokemon.name}'s ${this.type.name}!`);
}
getMaxStackCount(): integer {
return 5;
}
2023-04-21 15:45:48 -04:00
}
2023-04-09 19:15:21 -04:00
export class ExtraModifierModifier extends PersistentModifier {
2023-04-21 14:05:16 -04:00
constructor(type: ModifierType, stackCount?: integer) {
super(type, stackCount);
}
2023-04-20 01:07:39 -04:00
match(modifier: Modifier): boolean {
return modifier instanceof ExtraModifierModifier;
}
2023-04-09 19:15:21 -04:00
clone(): ExtraModifierModifier {
2023-04-21 14:05:16 -04:00
return new ExtraModifierModifier(this.type, this.stackCount);
2023-04-09 19:15:21 -04:00
}
apply(args: any[]): boolean {
2023-04-09 19:15:21 -04:00
(args[0] as Utils.IntegerHolder).value += this.getStackCount();
return true;
}
2023-04-28 19:26:41 -04:00
getMaxStackCount(): integer {
return 3;
}
2023-03-28 14:54:52 -04:00
}